[gradle] 02/81: Import upstream 2.4

Kai-Chung Yan seamlik-guest at moszumanska.debian.org
Wed Jun 17 13:33:38 UTC 2015


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

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

commit 9a78ec2dd297d11c25d2353bd051545c567e597e
Author: Kai-Chung Yan <seamlikok at gmail.com>
Date:   Thu May 21 15:43:03 2015 +0800

    Import upstream 2.4
---
 build.gradle                                       |   58 +-
 buildSrc/build.gradle                              |    7 +-
 .../main/groovy/org/gradle/build/BuildTypes.groovy |    7 +-
 .../main/groovy/org/gradle/build/JarJarJar.groovy  |    8 +
 .../org/gradle/build/ReleasedVersions.groovy       |    4 +
 .../org/gradle/build/TestReportAggregator.groovy   |   51 -
 ...csTask.groovy => AssembleSamplesDocTask.groovy} |    0
 .../{Docbook2XHtml.groovy => Docbook2Xhtml.groovy} |    0
 .../gradle/build/docs/ExtractSnippetsTask.groovy   |    2 +-
 .../build/docs/SampleElementLocationHandler.groovy |    2 +-
 .../build/docs/UserGuideTransformTask.groovy       |    8 +-
 .../docs/dsl/docbook/AssembleDslDocTask.groovy     |    2 +-
 .../build/docs/dsl/docbook/BasicJavadocLexer.java  |    2 +-
 .../build/docs/dsl/docbook/JavadocConverter.java   |    3 +-
 .../docs/dsl/docbook/JavadocLinkConverter.java     |   18 +-
 .../build/docs/dsl/links/ClassLinkMetaData.java    |   11 +-
 .../build/docs/dsl/source/model/TypeMetaData.java  |    4 +-
 .../build/docs/UserGuideTransformTaskTest.groovy   |   72 +
 .../docs/dsl/docbook/BasicJavadocLexerTest.groovy  |   13 +-
 .../docs/dsl/docbook/JavadocConverterTest.groovy   |   20 +
 .../dsl/docbook/JavadocLinkConverterTest.groovy    |   31 +-
 .../dsl/source/ExtractDslMetaDataTaskTest.groovy   |    3 +-
 .../dsl/source/model/MethodMetaDataTest.groovy     |   12 +
 .../docs/dsl/source/model/TypeMetaDataTest.groovy  |   11 +-
 config/checkstyle/checkstyle-api.xml               |    2 +-
 config/checkstyle/suppressions.xml                 |    5 +
 config/codenarc.xml                                |    1 +
 gradle/buildReceipt.gradle                         |   12 +-
 gradle/classycle.gradle                            |   14 +-
 gradle/codeQuality.gradle                          |   11 +-
 gradle/compile.gradle                              |    2 +-
 gradle/dependencies.gradle                         |   85 +-
 gradle/fix-GRADLE-2492.gradle                      |   29 +
 gradle/groovyProject.gradle                        |    8 +-
 gradle/idea.gradle                                 |   45 +-
 gradle/integTest.gradle                            |  127 +-
 gradle/providedConfiguration.gradle                |    2 +-
 gradle/strictCompile.gradle                        |   23 +
 gradle/taskOrdering.gradle                         |   36 +
 gradle/testFixtures.gradle                         |    3 +-
 gradle/wrapper/gradle-wrapper.jar                  |  Bin 51365 -> 52165 bytes
 gradle/wrapper/gradle-wrapper.properties           |    4 +-
 settings.gradle                                    |   22 +-
 .../announce/AnnouncePluginIntegrationTest.groovy  |   25 +
 .../BuildAnnouncementsPluginIntegrationTest.groovy |   20 +-
 .../announce/BuildAnnouncementsPlugin.groovy       |    2 +-
 .../internal/DefaultAnnouncerFactory.groovy        |    9 +-
 ...e.properties => org.gradle.announce.properties} |    0
 ...s => org.gradle.build-announcements.properties} |    0
 subprojects/antlr/antlr.gradle                     |    9 +-
 .../antlr/AbstractAntlrIntegrationTest.groovy      |   49 +
 .../antlr/Antlr2PluginIntegrationTest.groovy       |  150 +
 .../antlr/Antlr3PluginIntegrationTest.groovy       |   98 +
 .../antlr/Antlr4PluginIntegrationTest.groovy       |   70 +
 .../antlr/AntlrPluginIntegrationTest.groovy        |   64 +
 .../IncrementalAntlrTaskIntegrationTest.groovy     |  180 +
 .../samples/SamplesAntlrIntegrationTest.groovy     |   40 +
 .../org/gradle/api/plugins/antlr/AntlrPlugin.java  |   46 +-
 .../org/gradle/api/plugins/antlr/AntlrTask.java    |  132 +-
 .../api/plugins/antlr/internal/AntlrExecuter.java  |  119 +
 .../api/plugins/antlr/internal/AntlrResult.java    |   41 +
 .../internal/AntlrSourceGenerationException.java   |   27 +
 .../internal/AntlrSourceVirtualDirectoryImpl.java  |    1 +
 .../api/plugins/antlr/internal/AntlrSpec.java      |   75 +
 .../plugins/antlr/internal/AntlrSpecFactory.java   |   46 +
 .../plugins/antlr/internal/AntlrWorkerClient.java  |   30 +
 .../antlr/internal/AntlrWorkerClientProtocol.java  |   21 +
 .../plugins/antlr/internal/AntlrWorkerManager.java |   58 +
 .../plugins/antlr/internal/AntlrWorkerServer.java  |   53 +
 .../api/plugins/antlr/internal/GenerationPlan.java |   77 -
 .../antlr/internal/GenerationPlanBuilder.java      |  129 -
 .../plugins/antlr/internal/GrammarDelegate.java    |  148 -
 .../antlr/internal/GrammarFileMetadata.java        |   59 -
 .../plugins/antlr/internal/GrammarMetadata.java    |   92 -
 .../plugins/antlr/internal/MetadataExtracter.java  |   84 -
 .../gradle/api/plugins/antlr/internal/XRef.java    |   95 -
 .../antlr/internal/antlr2/GenerationPlan.java      |   77 +
 .../internal/antlr2/GenerationPlanBuilder.java     |  129 +
 .../antlr/internal/antlr2/GrammarDelegate.java     |  148 +
 .../antlr/internal/antlr2/GrammarFileMetadata.java |   59 +
 .../antlr/internal/antlr2/GrammarMetadata.java     |   92 +
 .../antlr/internal/antlr2/MetadataExtracter.java   |   84 +
 .../api/plugins/antlr/internal/antlr2/XRef.java    |   93 +
 ...ntlr.properties => org.gradle.antlr.properties} |    0
 .../api/plugins/antlr/AntlrPluginTest.groovy       |    7 +-
 .../antlr/internal/AntlrSpecFactoryTest.groovy     |  102 +
 .../gradle/api/internal/ClosureBackedAction.java   |  109 +
 .../main/groovy/org/gradle/api/specs/Specs.java    |   81 +
 .../org/gradle/groovy/scripts/Transformer.java     |   23 +
 .../internal/AbstractScriptTransformer.java        |    0
 .../gradle/groovy/scripts/internal/AstUtils.java   |  251 +
 .../scripts/internal/RestrictiveCodeVisitor.java   |  251 +
 .../groovy/scripts/internal/ScriptBlock.java       |   37 +
 .../ScriptSourceDescriptionTransformer.java        |   41 +
 .../scripts/internal/StatementTransformer.java     |   29 +
 .../main/groovy/org/gradle/util/Configurable.java  |    0
 .../groovy/org/gradle/api/specs/OrSpecTest.java    |    6 +-
 .../groovy/org/gradle/api/specs/SpecsTest.groovy   |    0
 subprojects/base-services/base-services.gradle     |    1 +
 .../SystemPropertiesIntegrationTest.groovy         |   49 +
 .../src/main/java/org/gradle/api/JavaVersion.java  |    8 +
 .../src/main/java/org/gradle/api/Named.java        |    8 +-
 .../src/main/java/org/gradle/api/Transformer.java  |   12 +-
 .../src/main/java/org/gradle/internal/Actions.java |   20 +-
 .../main/java/org/gradle/internal/BiAction.java    |   23 +
 .../main/java/org/gradle/internal/BiActions.java   |   34 +
 .../src/main/java/org/gradle/internal/Cast.java    |    4 +
 .../main/java/org/gradle/internal/Factories.java   |    4 +
 .../src/main/java/org/gradle/internal/Pair.java    |  123 +
 .../main/java/org/gradle/internal/Supplier.java    |   25 -
 .../main/java/org/gradle/internal/Suppliers.java   |   64 -
 .../java/org/gradle/internal/SystemProperties.java |   54 +-
 .../java/org/gradle/internal/Transformers.java     |   25 +-
 .../main/java/org/gradle/internal/TriAction.java   |   23 +
 .../org/gradle/internal/UncheckedException.java    |    6 +
 .../internal/classloader/CachingClassLoader.java   |   18 +
 .../internal/classloader/ClassLoaderFactory.java   |    8 +-
 .../gradle/internal/classloader/ClasspathUtil.java |   19 +-
 .../classloader/DefaultClassLoaderFactory.java     |    7 +-
 .../internal/classloader/FilteringClassLoader.java |   40 +-
 .../classloader/MultiParentClassLoader.java        |   33 +-
 .../classloader/MutableURLClassLoader.java         |    5 +
 .../org/gradle/internal/classpath/ClassPath.java   |    7 +-
 .../internal/classpath/DefaultClassPath.java       |   30 +-
 .../internal/concurrent/CompositeStoppable.java    |   44 +-
 .../concurrent/DefaultExecutorFactory.java         |   93 +-
 .../internal/concurrent/ExecutorFactory.java       |   10 +
 .../gradle/internal/concurrent/ExecutorPolicy.java |   83 +
 .../internal/concurrent/StoppableExecutor.java     |    4 +-
 .../internal/concurrent/StoppableExecutorImpl.java |   91 +
 .../internal/concurrent/ThreadFactoryImpl.java     |   43 +
 .../org/gradle/internal/exceptions/Contextual.java |    0
 .../exceptions/DefaultMultiCauseException.java     |  111 +
 .../internal/exceptions/DiagnosticsVisitor.java    |   29 +
 .../exceptions/FormattingDiagnosticsVisitor.java   |   71 +
 .../internal/exceptions/MultiCauseException.java   |    0
 .../java/org/gradle/internal/hash/HashUtil.java    |    3 +
 .../java/org/gradle/internal/hash/HashValue.java   |    8 +-
 .../main/java/org/gradle/internal/io/IoUtils.java  |   35 +
 .../java/org/gradle/internal/jvm/JavaInfo.java     |    9 +-
 .../java/org/gradle/internal/jvm/JdkTools.java     |   67 +
 .../src/main/java/org/gradle/internal/jvm/Jvm.java |    9 +-
 .../jvm/UnsupportedJavaRuntimeException.java       |   41 +
 .../gradle/internal/operations/BuildOperation.java |   24 +
 .../internal/operations/BuildOperationFailure.java |   42 +
 .../operations/BuildOperationProcessor.java        |   34 +
 .../internal/operations/BuildOperationQueue.java   |   46 +
 .../internal/operations/BuildOperationWorker.java  |   26 +
 .../operations/DefaultBuildOperationProcessor.java |   39 +
 .../operations/DefaultBuildOperationQueue.java     |  117 +
 .../operations/MultipleBuildOperationFailures.java |   50 +
 .../org/gradle/internal/os/OperatingSystem.java    |   13 +-
 .../org/gradle/internal/reflect/ClassDetails.java  |   50 +
 .../gradle/internal/reflect/ClassInspector.java    |  233 +
 .../internal/reflect/DirectInstantiator.java       |   10 +
 .../org/gradle/internal/reflect/JavaMethod.java    |   22 +-
 .../internal/reflect/JavaReflectionUtil.java       |  137 +-
 .../gradle/internal/reflect/MethodDescription.java |  104 +
 .../reflect/MethodSignatureEquivalence.java        |   48 +
 .../reflect/ObjectInstantiationException.java      |    3 +
 .../gradle/internal/reflect/PropertyAccessor.java  |    6 +-
 .../gradle/internal/reflect/PropertyDetails.java   |   28 +
 .../internal/service/DefaultServiceRegistry.java   |  306 +-
 .../gradle/internal/service/ServiceLocator.java    |    4 +-
 .../gradle/internal/service/ServiceRegistry.java   |   10 -
 .../java/org/gradle/internal/util/BiFunction.java  |   23 +
 .../java/org/gradle/internal/util/NumberUtil.java  |   74 +
 .../main/java/org/gradle/util/CollectionUtils.java |  120 +-
 .../main/java/org/gradle/util/GradleVersion.java   |  311 ++
 .../src/main/java/org/gradle/util/TextUtil.java    |  126 +
 .../groovy/org/gradle/api/JavaVersionSpec.groovy   |    9 +
 .../groovy/org/gradle/internal/PairTest.groovy     |   56 +
 .../org/gradle/internal/SuppliersTest.groovy       |   82 -
 .../gradle/internal/SystemPropertiesTest.groovy    |    4 +-
 .../org/gradle/internal/TransformersTest.groovy    |   19 +-
 .../classloader/CachingClassLoaderTest.groovy      |   12 +
 .../classloader/FilteringClassLoaderTest.groovy    |   34 +
 .../classloader/MultiParentClassLoaderTest.groovy  |   11 +
 .../internal/classpath/DefaultClassPathTest.groovy |   27 +-
 .../concurrent/CompositeStoppableTest.groovy       |   34 -
 .../concurrent/DefaultExecutorFactoryTest.groovy   |    2 +-
 .../DefaultMultiCauseExceptionTest.groovy          |  125 +
 .../FormattingDiagnosticsVisitorTest.groovy        |   45 +
 .../org/gradle/internal/hash/HashUtilTest.groovy   |  109 +
 .../org/gradle/internal/hash/HashValueTest.groovy  |   25 +-
 .../org/gradle/internal/jvm/JdkToolsTest.groovy    |   55 +
 .../DefaultBuildOperationProcessorTest.groovy      |  160 +
 .../DefaultBuildOperationQueueTest.groovy          |  155 +
 .../MultipleBuildOperationFailuresTest.groovy      |  129 +
 .../gradle/internal/os/OperatingSystemTest.groovy  |   17 +-
 .../internal/reflect/ClassInspectorTest.groovy     |  357 ++
 .../internal/reflect/DirectInstantiatorTest.groovy |    2 +-
 .../internal/reflect/JavaReflectionUtilTest.groovy |   72 +-
 .../gradle/internal/reflect/JavaTestSubject.java   |    6 +-
 .../internal/reflect/MethodDescriptionTest.groovy  |   33 +
 .../service/DefaultServiceRegistryTest.groovy      |   96 +-
 .../org/gradle/internal/util/NumberUtilTest.groovy |   97 +
 .../org/gradle/util/CollectionUtilsTest.groovy     |   55 +-
 .../org/gradle/util/GradleVersionTest.groovy       |  251 +
 .../BuildComparisonHtmlReportFixture.groovy        |   68 +
 .../gradle/BuildComparisonIntegrationSpec.groovy   |  104 +-
 ...Pre12CompareGradleBuildsCrossVersionSpec.groovy |   47 +-
 .../source/build.gradle                            |    0
 .../source/settings.gradle                         |    0
 .../source/src/main/resources/dir1/sameSub.zip     |  Bin 0 -> 688 bytes
 .../src/main/resources/dir2/differentSub.zip       |  Bin 0 -> 688 bytes
 .../source/src/main/resources/sourceSub.zip        |  Bin 0 -> 688 bytes
 .../target/build.gradle                            |    0
 .../target/settings.gradle                         |    0
 .../target/src/main/resources/dir1/sameSub.zip     |  Bin 0 -> 688 bytes
 .../src/main/resources/dir2/differentSub.zip       |  Bin 0 -> 689 bytes
 .../target/src/main/resources/targetSub.zip        |  Bin 0 -> 688 bytes
 .../source/build.gradle                            |    0
 .../source/settings.gradle                         |    0
 .../source/src/main/java/org/gradle/Changed.java   |    0
 .../src/main/java/org/gradle/DifferentCrc.java     |    0
 .../src/main/java/org/gradle/SourceBuildOnly.java  |    0
 .../source/src/main/java/org/gradle/Unchanged.java |    0
 .../source/src/main/resources/dir1/different.txt   |    1 +
 .../source/src/main/resources/similar.txt          |    1 +
 .../src/main/resources/someSource.properties       |    1 +
 .../target/build.gradle                            |    0
 .../target}/settings.gradle                        |    0
 .../target/src/main/java/org/gradle/Changed.java   |    0
 .../src/main/java/org/gradle/DifferentCrc.java     |    0
 .../src/main/java/org/gradle/TargetBuildOnly.java  |    0
 .../target/src/main/java/org/gradle/Unchanged.java |    0
 .../target/src/main/resources/dir1/different.txt   |    1 +
 .../target/src/main/resources/similar.txt          |    1 +
 .../src/main/resources/someTarget.properties       |    1 +
 .../gradle/CompareGradleBuilds.java                |   36 +-
 .../gradle/CompareGradleBuildsPlugin.groovy        |    2 +-
 .../gradle/internal/GradleBuildComparison.java     |    2 +-
 .../internal/GradleBuildOutcomeSetInferrer.java    |    2 +-
 .../internal/GradleBuildOutcomeSetTransformer.java |   10 +-
 .../archive/GeneratedArchiveBuildOutcome.java      |    1 +
 .../GeneratedArchiveBuildOutcomeComparator.java    |   27 +-
 ...neratedArchiveBuildOutcomeComparisonResult.java |    2 +-
 ...BuildOutcomeComparisonResultHtmlRenderer.groovy |   32 +-
 .../internal/archive/entry/ArchiveEntry.java       |  213 +-
 .../archive/entry/ArchiveEntryComparison.java      |   16 +-
 .../entry/FileToArchiveEntrySetTransformer.java    |   54 +-
 .../entry/ZipEntryToArchiveEntryTransformer.java   |   33 -
 ...blishArtifactToFileBuildOutcomeTransformer.java |    8 +-
 .../GradleBuildComparisonResultHtmlRenderer.groovy |   55 +-
 ...=> org.gradle.compare-gradle-builds.properties} |    0
 ...DefaultBuildOutcomeComparatorFactoryTest.groovy |    2 +-
 .../GradleBuildOutcomeSetInferrerTest.groovy       |    4 +-
 .../GradleBuildOutcomeSetTransformerTest.groovy    |    8 +-
 ...neratedArchiveBuildOutcomeComparatorTest.groovy |  103 +-
 .../entry/ArchiveEntryComparisonTest.groovy        |   14 +-
 .../internal/archive/entry/ArchiveEntryTest.groovy |  101 +-
 .../FileToArchiveEntrySetTransformerTest.groovy    |   74 +-
 .../ZipEntryToArchiveEntryTransformerTest.groovy   |   63 -
 ...rtifactToFileBuildOutcomeTransformerTest.groovy |   10 +-
 ...dleBuildComparisonResultHtmlRendererTest.groovy |   67 +-
 .../fixtures/MutableProjectOutcomes.groovy         |    4 +-
 .../StringBuildOutcomeComparisonResult.groovy      |    2 +-
 subprojects/build-init/build-init.gradle           |    7 +-
 .../plugins/BuildInitPluginIntegrationTest.groovy  |    5 -
 .../GroovyLibraryInitIntegrationTest.groovy        |    1 -
 .../plugins/MavenConversionIntegrationTest.groovy  |    6 +-
 .../plugins/ScalaLibraryInitIntegrationTest.groovy |    3 -
 .../org/gradle/api/tasks/wrapper/Wrapper.java      |   11 +-
 .../buildinit/plugins/BuildInitPlugin.groovy       |   62 -
 .../gradle/buildinit/plugins/BuildInitPlugin.java  |   34 +
 .../gradle/buildinit/plugins/WrapperPlugin.groovy  |   34 -
 .../gradle/buildinit/plugins/WrapperPlugin.java    |   34 +
 .../BasicTemplateBasedProjectInitDescriptor.java   |    4 +-
 .../plugins/internal/BuildInitAutoApplyAction.java |   33 -
 .../plugins/internal/BuildInitServices.java        |    3 +
 .../GroovyLibraryProjectInitDescriptor.java        |    1 +
 .../internal/JavaLibraryProjectInitDescriptor.java |    1 +
 .../ProjectLayoutSetupRegistryFactory.groovy       |    4 +-
 .../internal/WrapperPluginAutoApplyAction.groovy   |   32 -
 .../internal/action/BuildInitAutoApplyAction.java  |   29 +
 .../action/WrapperPluginAutoApplyAction.groovy     |   29 +
 .../internal/maven/MavenProjectsCreator.java       |    2 +-
 .../org/gradle/buildinit/plugins/package-info.java |   20 +
 .../tasks/internal/TaskConfiguration.java          |  126 +
 ...properties => org.gradle.build-init.properties} |    0
 ...er.properties => org.gradle.wrapper.properties} |    0
 ...le.configuration.project.ProjectConfigureAction |    4 +-
 .../tasks/templates/build.gradle.template          |    8 +-
 .../templates/groovylibrary/build.gradle.template  |    8 +-
 .../templates/javalibrary/build.gradle.template    |    6 +-
 .../templates/scalalibrary/build.gradle.template   |    4 +-
 .../buildinit/plugins/BuildInitPluginSpec.groovy   |    8 +-
 .../buildinit/plugins/WrapperPluginSpec.groovy     |    6 +-
 .../internal/BuildInitAutoApplyActionSpec.groovy   |   72 -
 .../action/BuildInitAutoApplyActionSpec.groovy     |   71 +
 .../gradle/cli/AbstractCommandLineConverter.java   |   12 -
 .../AbstractPropertiesCommandLineConverter.java    |    5 -
 .../java/org/gradle/cli/CommandLineConverter.java  |    4 -
 .../java/org/gradle/cli/CommandLineParser.java     |    2 +-
 .../java/org/gradle/cli/ParsedCommandLine.java     |   22 +-
 ...stractPropertiesCommandLineConverterTest.groovy |    2 +-
 .../org/gradle/cli/CommandLineParserTest.groovy    |    2 +-
 .../org/gradle/cli/ParsedCommandLineTest.groovy    |   18 +
 ...rojectPropertiesCommandLineConverterTest.groovy |    2 +-
 ...SystemPropertiesCommandLineConverterTest.groovy |    2 +-
 subprojects/code-quality/code-quality.gradle       |    6 +-
 .../AbstractFindBugsPluginIntegrationTest.groovy   |  469 ++
 .../quality/CheckstylePluginIntegrationTest.groovy |   16 +-
 .../quality/CodeNarcPluginIntegrationTest.groovy   |   12 +-
 .../CodeQualityPluginIntegrationTest.groovy        |  207 -
 ...ndBugsClasspathValidationIntegrationTest.groovy |   58 +
 .../FindBugsForOldJavaIntegrationTest.groovy       |   32 +
 .../plugins/quality/FindBugsIntegrationTest.groovy |   22 +
 .../quality/FindBugsPluginIntegrationTest.groovy   |  381 +-
 .../quality/JDependPluginIntegrationTest.groovy    |   22 +
 .../quality/PmdPluginIntegrationTest.groovy        |   44 +-
 .../quality/PmdPluginVersionIntegrationTest.groovy |    2 +-
 .../gradle/api/plugins/quality/Checkstyle.groovy   |   78 +-
 .../api/plugins/quality/CheckstyleExtension.groovy |   31 +-
 .../api/plugins/quality/CheckstylePlugin.groovy    |    9 +-
 .../org/gradle/api/plugins/quality/CodeNarc.groovy |   76 +-
 .../api/plugins/quality/CodeNarcExtension.groovy   |   30 +-
 .../api/plugins/quality/CodeNarcPlugin.groovy      |   19 +-
 .../api/plugins/quality/CodeQualityPlugin.groovy   |   71 -
 .../org/gradle/api/plugins/quality/FindBugs.groovy |  100 +-
 .../api/plugins/quality/FindBugsExtension.groovy   |   71 +-
 .../api/plugins/quality/FindBugsPlugin.groovy      |   12 +-
 .../GroovyCodeQualityPluginConvention.groovy       |   60 -
 .../org/gradle/api/plugins/quality/JDepend.groovy  |   14 +-
 .../api/plugins/quality/JDependPlugin.groovy       |   21 +-
 .../quality/JavaCodeQualityPluginConvention.groovy |   60 -
 .../org/gradle/api/plugins/quality/Pmd.groovy      |   92 +-
 .../gradle/api/plugins/quality/PmdExtension.groovy |   24 +
 .../gradle/api/plugins/quality/PmdPlugin.groovy    |   17 +-
 .../internal/AbstractCodeQualityPlugin.groovy      |    6 +-
 .../findbugs/FindBugsClasspathValidator.java       |   70 +
 .../quality/internal/findbugs/FindBugsResult.java  |    6 +-
 .../internal/findbugs/FindBugsSpecBuilder.java     |   16 +
 .../internal/findbugs/FindBugsWorkerServer.java    |   13 +-
 .../gradle-plugins/code-quality.properties         |    1 -
 ...properties => org.gradle.checkstyle.properties} |    0
 ...c.properties => org.gradle.codenarc.properties} |    0
 ...s.properties => org.gradle.findbugs.properties} |    0
 ...nd.properties => org.gradle.jdepend.properties} |    0
 .../{pmd.properties => org.gradle.pmd.properties}  |    0
 .../plugins/quality/CheckstylePluginTest.groovy    |   51 +-
 .../api/plugins/quality/CheckstyleTest.groovy      |   15 +-
 .../api/plugins/quality/CodeNarcPluginTest.groovy  |   55 +-
 .../plugins/quality/CodeQualityPluginTest.groovy   |  159 -
 .../gradle/api/plugins/quality/CodenarcTest.groovy |   33 +
 .../api/plugins/quality/FindBugsPluginTest.groovy  |   80 +-
 .../gradle/api/plugins/quality/FindBugsTest.groovy |   37 +-
 .../api/plugins/quality/JDependPluginTest.groovy   |   15 +-
 .../api/plugins/quality/PmdPluginTest.groovy       |   25 +-
 .../findbugs/FindBugsClasspathValidatorTest.groovy |   63 +
 .../findbugs/FindBugsWorkerServerTest.groovy       |   40 +
 subprojects/core-impl/core-impl.gradle             |   77 -
 .../ArtifactDependenciesIntegrationTest.groovy     |  691 ---
 .../resolve/CacheResolveIntegrationTest.groovy     |  115 -
 ...ModuleDependenciesResolveIntegrationTest.groovy |   67 -
 ...adataRulesChangingModulesIntegrationTest.groovy |  170 -
 .../ComponentMetadataRulesIntegrationTest.groovy   |  118 -
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   45 -
 .../DependencyNotationIntegrationSpec.groovy       |  186 -
 ...ependencyResolutionEventsIntegrationTest.groovy |   51 -
 .../DependencyResolveRulesIntegrationTest.groovy   |  797 ---
 ...LibraryArtifactResolutionIntegrationTest.groovy |  105 -
 .../JvmLibraryArtifactResolveTestFixture.groovy    |  214 -
 .../ProjectDependenciesIntegrationTest.groovy      |   92 -
 .../ProjectDependencyResolveIntegrationTest.groovy |  422 --
 ...ResolutionStrategySamplesIntegrationTest.groovy |   58 -
 .../ResolveCrossVersionIntegrationTest.groovy      |   60 -
 .../integtests/resolve/ResolveTestFixture.groovy   |  333 --
 .../ResolvedConfigurationIntegrationTest.groovy    |  106 -
 ...VersionConflictResolutionIntegrationTest.groovy |  733 ---
 ...AliasedArtifactResolutionIntegrationTest.groovy |  202 -
 .../CacheReuseCrossVersionIntegrationTest.groovy   |  173 -
 .../M3CacheReuseCrossVersionIntegrationTest.groovy |   75 -
 .../MavenM2CacheReuseIntegrationTest.groovy        |   54 -
 .../ResolutionOverrideIntegrationTest.groovy       |  221 -
 ...ameCacheUsageCrossVersionIntegrationTest.groovy |   77 -
 .../CachedChangingModulesIntegrationTest.groovy    |  232 -
 ...achedDependencyResolutionIntegrationTest.groovy |  239 -
 .../CachedMissingModulesIntegrationTest.groovy     |  295 --
 ...ependencyMetadataInMemoryIntegrationTest.groovy |  197 -
 ...coverFromBrokenResolutionIntegrationTest.groovy |  357 --
 .../FileSystemResolverIntegrationTest.groovy       |   76 -
 .../custom/IvySFtpResolverIntegrationTest.groovy   |   84 -
 .../custom/IvyUrlResolverIntegrationTest.groovy    |  251 -
 .../AbstractHttpsRepoResolveIntegrationTest.groovy |  129 -
 ...ationDependencyResolutionIntegrationTest.groovy |  221 -
 ...odingDependencyResolutionIntegrationTest.groovy |   49 -
 .../http/HttpProxyResolveIntegrationTest.groovy    |  154 -
 .../http/HttpRedirectResolveIntegrationTest.groovy |   87 -
 .../ivy/IvyBrokenDescriptorIntegrationTest.groovy  |  119 -
 .../IvyBrokenRemoteResolveIntegrationTest.groovy   |  181 -
 ...angingModuleRemoteResolveIntegrationTest.groovy |  418 --
 ...adataRulesChangingModulesIntegrationTest.groovy |   41 -
 ...IvyComponentMetadataRulesIntegrationTest.groovy |   42 -
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   98 -
 ...CustomStatusLatestVersionIntegrationTest.groovy |  107 -
 .../ivy/IvyDescriptorResolveIntegrationTest.groovy |  240 -
 .../IvyDescriptorValidationIntegrationTest.groovy  |   54 -
 ...amicRevisionRemoteResolveIntegrationTest.groovy |  835 ----
 ...IvyDynamicRevisionResolveIntegrationTest.groovy |  430 --
 .../ivy/IvyHttpRepoResolveIntegrationTest.groovy   |  345 --
 .../ivy/IvyHttpsRepoResolveIntegrationTest.groovy  |   28 -
 ...LibraryArtifactResolutionIntegrationTest.groovy |  325 --
 .../ivy/IvyModuleResolveIntegrationTest.groovy     |  223 -
 .../resolve/ivy/IvyResolveIntegrationTest.groovy   |  197 -
 .../maven/BadPomFileResolveIntegrationTest.groovy  |  186 -
 .../LegacyMavenRepoResolveIntegrationTest.groovy   |  225 -
 .../MavenBrokenRemoteResolveIntegrationTest.groovy |  101 -
 ...adataRulesChangingModulesIntegrationTest.groovy |   70 -
 ...venComponentMetadataRulesIntegrationTest.groovy |   42 -
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   65 -
 ...venCustomPackagingResolveIntegrationTest.groovy |   68 -
 .../MavenDependencyResolveIntegrationTest.groovy   |  212 -
 .../MavenDynamicResolveIntegrationTest.groovy      |  228 -
 .../MavenHttpRepoResolveIntegrationTest.groovy     |  297 --
 ...LibraryArtifactResolutionIntegrationTest.groovy |  321 --
 .../maven/MavenLatestResolveIntegrationTest.groovy |   98 -
 .../MavenLocalRepoResolveIntegrationTest.groovy    |  320 --
 .../MavenParentPomResolveIntegrationTest.groovy    |  449 --
 .../MavenPomPackagingResolveIntegrationTest.groovy |  378 --
 .../maven/MavenPomResolveIntegrationTest.groovy    |   65 -
 .../MavenProfileResolveIntegrationTest.groovy      |  218 -
 .../MavenSnapshotResolveIntegrationTest.groovy     |  679 ---
 .../projectWithConfigurationHierarchy.gradle       |   58 -
 .../projectWithUnknownDependency.gradle            |   14 -
 .../artifacts/ArtifactDependencyResolver.java      |   29 -
 .../artifacts/DefaultArtifactIdentifier.java       |  107 -
 .../DefaultDependencyManagementServices.java       |  188 -
 .../artifacts/DefaultModuleIdentifier.java         |   67 -
 .../artifacts/DefaultModuleVersionIdentifier.java  |  101 -
 .../artifacts/DefaultResolvedArtifact.java         |  111 -
 .../DependencyManagementBuildScopeServices.java    |  284 --
 .../DependencyManagementGlobalScopeServices.java   |   86 -
 .../api/internal/artifacts/DependencyServices.java |   33 -
 .../artifacts/ModuleMetadataProcessor.java         |   22 -
 .../ModuleVersionIdentifierSerializer.java         |   39 -
 .../internal/artifacts/ModuleVersionPublisher.java |   28 -
 .../artifacts/ModuleVersionSelectorSerializer.java |   41 -
 .../internal/artifacts/PlexusLoggerAdapter.java    |   98 -
 .../ResolvedConfigurationIdentifierSerializer.java |   39 -
 .../api/internal/artifacts/ResolverResults.java    |   64 -
 .../DefaultComponentIdentifierFactory.java         |   32 -
 .../DefaultModuleComponentIdentifier.java          |   98 -
 .../component/DefaultModuleComponentSelector.java  |  107 -
 .../DefaultProjectComponentIdentifier.java         |   69 -
 .../component/DefaultProjectComponentSelector.java |   82 -
 .../artifacts/configurations/Configurations.java   |   56 -
 .../configurations/DefaultConfiguration.java       |  572 ---
 .../DefaultConfigurationContainer.java             |  123 -
 .../artifacts/dsl/DefaultArtifactHandler.groovy    |   67 -
 .../dsl/DefaultComponentMetadataHandler.java       |   47 -
 .../dsl/ModuleVersionSelectorParsers.java          |   90 -
 .../artifacts/dsl/ParsedModuleStringNotation.java  |   65 -
 .../dsl/PublishArtifactNotationParserFactory.java  |   98 -
 .../ivyservice/ArtifactResolveContext.java         |   21 -
 .../ivyservice/ArtifactResolveResult.java          |   35 -
 .../artifacts/ivyservice/ArtifactResolver.java     |   33 -
 .../ivyservice/ArtifactSetResolveResult.java       |   32 -
 .../ivyservice/ArtifactTypeResolveContext.java     |   38 -
 .../ivyservice/BuildableArtifactResolveResult.java |   39 -
 .../BuildableArtifactSetResolveResult.java         |   29 -
 .../BuildableComponentResolveResult.java           |   42 -
 .../internal/artifacts/ivyservice/CacheLayout.java |   59 -
 .../CacheLockingArtifactDependencyResolver.java    |   46 -
 .../artifacts/ivyservice/CacheLockingManager.java  |   53 -
 .../ivyservice/ComponentResolveResult.java         |   42 -
 .../ivyservice/ConfigurationResolveContext.java    |   41 -
 .../ivyservice/ContextualArtifactResolver.java     |   58 -
 .../DefaultBuildableArtifactResolveResult.java     |   63 -
 .../DefaultBuildableArtifactSetResolveResult.java  |   64 -
 .../DefaultBuildableComponentResolveResult.java    |   73 -
 .../ivyservice/DefaultCacheLockingManager.java     |   88 -
 .../ivyservice/DefaultConfigurationResolver.java   |   49 -
 .../DefaultDependencyResolveDetails.java           |   72 -
 .../ivyservice/DefaultIvyDependencyPublisher.java  |   71 -
 .../ivyservice/DefaultLenientConfiguration.java    |  169 -
 .../ivyservice/DefaultUnresolvedDependency.java    |   47 -
 .../DependencyToModuleVersionIdResolver.java       |   28 -
 .../DependencyToModuleVersionResolver.java         |   28 -
 .../ErrorHandlingArtifactDependencyResolver.java   |  256 -
 .../ivyservice/IvyBackedArtifactPublisher.java     |   84 -
 .../ivyservice/IvyDependencyPublisher.java         |   26 -
 .../IvyResolverBackedModuleVersionPublisher.java   |   67 -
 .../api/internal/artifacts/ivyservice/IvyUtil.java |   77 -
 .../ivyservice/IvyXmlModuleDescriptorWriter.java   |  402 --
 .../ivyservice/LocalComponentFactory.java          |   26 -
 .../ivyservice/ModuleToModuleVersionResolver.java  |   29 -
 .../ivyservice/ModuleVersionIdResolveResult.java   |   46 -
 .../ivyservice/ModuleVersionNotFoundException.java |   34 -
 .../ivyservice/ModuleVersionResolveException.java  |  110 -
 .../SelfResolvingDependencyResolver.java           |  113 -
 ...cuitEmptyConfigsArtifactDependencyResolver.java |  104 -
 .../SubstitutedModuleVersionIdResolveResult.java   |   47 -
 .../VersionForcingDependencyToModuleResolver.java  |   75 -
 .../clientmodule/ClientModuleResolver.java         |   47 -
 .../DefaultCachedModuleVersionList.java            |   37 -
 .../dynamicversions/ModuleVersionsCache.java       |   33 -
 .../dynamicversions/ModuleVersionsCacheEntry.java  |   28 -
 .../SingleFileBackedModuleVersionsCache.java       |  140 -
 .../ivyresolve/ArtifactNotFoundException.java      |   24 -
 .../ivyresolve/ArtifactResolveException.java       |   69 -
 ...uildableModuleVersionMetaDataResolveResult.java |   72 -
 ...ildableModuleVersionSelectionResolveResult.java |   60 -
 .../CacheLockingModuleVersionRepository.java       |   78 -
 .../ivyresolve/CachingModuleVersionRepository.java |  308 --
 .../ivyresolve/ChangingModuleDetector.java         |   55 -
 .../ConfiguredModuleVersionRepository.java         |   23 -
 ...uildableModuleVersionMetaDataResolveResult.java |   95 -
 ...ildableModuleVersionSelectionResolveResult.java |   58 -
 .../ivyresolve/DefaultModuleVersionListing.java    |   94 -
 .../ivyresolve/DependencyResolverIdentifier.java   |   66 -
 .../ivyresolve/ErrorHandlingArtifactResolver.java  |   47 -
 .../IvyAwareModuleVersionRepository.java           |   26 -
 .../IvyDynamicResolveModuleVersionRepository.java  |   81 -
 .../ivyresolve/LazyDependencyToModuleResolver.java |  155 -
 .../LocalArtifactsModuleVersionRepository.java     |   31 -
 .../LocalAwareModuleVersionRepository.java         |   42 -
 .../ivyresolve/LocalModuleVersionRepository.java   |   69 -
 .../ivyresolve/LoopbackDependencyResolver.java     |  186 -
 .../ivyservice/ivyresolve/ModuleSource.java        |   28 -
 .../ivyresolve/ModuleVersionListing.java           |   30 -
 .../ivyresolve/ModuleVersionRepository.java        |   41 -
 .../ivyresolve/NoOpRepositoryCacheManager.java     |   76 -
 .../ivyservice/ivyresolve/RepositoryChain.java     |   24 -
 .../RepositoryChainArtifactResolver.java           |   64 -
 .../RepositoryChainDependencyResolver.java         |  297 --
 .../RepositoryChainModuleResolution.java           |   38 -
 .../ivyresolve/RepositoryChainModuleSource.java    |   34 -
 .../ivyservice/ivyresolve/ResolveIvyFactory.java   |  202 -
 .../StartParameterResolutionOverride.java          |  123 -
 .../ivyservice/ivyresolve/UserResolverChain.java   |   53 -
 .../memcache/CachedModuleVersionResult.java        |   55 -
 .../ivyresolve/memcache/CachedRepository.java      |   90 -
 .../memcache/DependencyMetadataCache.java          |  123 -
 .../memcache/DependencyMetadataCacheStats.java     |   29 -
 .../memcache/InMemoryDependencyMetadataCache.java  |   62 -
 .../parser/AbstractModuleDescriptorParser.java     |   55 -
 .../ivyresolve/parser/DescriptorParseContext.java  |   27 -
 .../parser/DisconnectedDescriptorParseContext.java |   37 -
 .../DisconnectedIvyXmlModuleDescriptorParser.java  |   65 -
 .../parser/GradlePomModuleDescriptorBuilder.java   |  377 --
 .../parser/GradlePomModuleDescriptorParser.java    |  216 -
 .../parser/IvyXmlModuleDescriptorParser.java       | 1226 -----
 .../ivyresolve/parser/MetaDataParseException.java  |   32 -
 .../ivyresolve/parser/MetaDataParser.java          |   29 -
 .../ivyservice/ivyresolve/parser/PomReader.java    |  776 ---
 .../ivyresolve/strategy/ChainVersionMatcher.java   |   64 -
 .../ivyresolve/strategy/ExactVersionMatcher.java   |  113 -
 .../ivyresolve/strategy/LatestStrategy.java        |   42 -
 .../ivyresolve/strategy/LatestVersionMatcher.java  |   47 -
 .../ivyresolve/strategy/LatestVersionStrategy.java |   65 -
 .../ivyresolve/strategy/ResolverStrategy.java      |   53 -
 .../ivyresolve/strategy/SubVersionMatcher.java     |   59 -
 .../ivyresolve/strategy/VersionMatcher.java        |   67 -
 .../ivyresolve/strategy/VersionRangeMatcher.java   |  180 -
 .../CachedModuleDescriptorParseContext.java        |   38 -
 .../modulecache/DefaultCachedMetaData.java         |   71 -
 .../modulecache/DefaultModuleArtifactsCache.java   |  182 -
 .../modulecache/DefaultModuleMetaDataCache.java    |  176 -
 .../modulecache/ModuleArtifactsCache.java          |   37 -
 .../modulecache/ModuleDescriptorCacheEntry.java    |   38 -
 .../modulecache/ModuleDescriptorStore.java         |   79 -
 .../modulecache/ModuleMetaDataCache.java           |   47 -
 .../ConfigurationsToArtifactsConverter.java        |   23 -
 .../ConfigurationsToModuleDescriptorConverter.java |   23 -
 .../DefaultConfigurationsToArtifactsConverter.java |   60 -
 ...tConfigurationsToModuleDescriptorConverter.java |   42 -
 .../DefaultModuleDescriptorFactory.java            |   26 -
 .../moduleconverter/ModuleDescriptorFactory.java   |   23 -
 .../PublishLocalComponentFactory.java              |   47 -
 .../ResolveLocalComponentFactory.java              |   55 -
 .../AbstractIvyDependencyDescriptorFactory.java    |   77 -
 .../ClientModuleDependencyDescriptor.java          |   34 -
 ...ClientModuleIvyDependencyDescriptorFactory.java |   63 -
 .../dependencies/ClientModuleMetaDataFactory.java  |   26 -
 .../DefaultClientModuleMetaDataFactory.java        |   54 -
 ...ultDependenciesToModuleDescriptorConverter.java |   59 -
 .../DefaultDependencyDescriptorFactory.java        |   48 -
 .../DependenciesToModuleDescriptorConverter.java   |   25 -
 .../dependencies/DependencyDescriptorFactory.java  |   23 -
 .../dependencies/EnhancedDependencyDescriptor.java |   34 -
 ...ternalModuleIvyDependencyDescriptorFactory.java |   54 -
 .../IvyDependencyDescriptorFactory.java            |   25 -
 .../dependencies/ProjectDependencyDescriptor.java  |   31 -
 .../ProjectIvyDependencyDescriptorFactory.java     |   51 -
 .../DefaultProjectComponentRegistry.java           |   36 -
 .../projectmodule/DefaultProjectPublication.java   |   40 -
 .../projectmodule/ProjectArtifactResolver.java     |   65 -
 .../projectmodule/ProjectComponentRegistry.java    |   22 -
 .../projectmodule/ProjectDependencyResolver.java   |   57 -
 .../resolutionstrategy/DefaultCachePolicy.java     |  254 -
 .../DefaultResolutionStrategy.java                 |  127 -
 .../ModuleForcingResolveRule.java                  |   54 -
 .../resolveengine/DefaultDependencyResolver.java   |  130 -
 .../DefaultDependencyToConfigurationResolver.java  |   81 -
 .../resolveengine/DependencyGraphBuilder.java      |  985 ----
 .../DependencyToConfigurationResolver.java         |   30 -
 .../LatestModuleConflictResolver.java              |   32 -
 .../resolveengine/ModuleConflictResolver.java      |   22 -
 .../resolveengine/ModuleRevisionResolveState.java  |   27 -
 .../resolveengine/ModuleVersionSpec.java           |  556 ---
 .../resolveengine/StrictConflictResolver.java      |   30 -
 .../VersionSelectionReasonResolver.java            |   36 -
 .../DefaultResolvedConfigurationBuilder.java       |  151 -
 .../DefaultTransientConfigurationResults.java      |   45 -
 .../oldresult/ResolvedConfigurationBuilder.java    |   44 -
 .../oldresult/TransientConfigurationResults.java   |   32 -
 .../TransientConfigurationResultsBuilder.java      |  186 -
 .../result/CachingDependencyResultFactory.java     |   56 -
 .../result/ComponentIdentifierSerializer.java      |   76 -
 .../result/ComponentSelectionReasonSerializer.java |   59 -
 .../result/ComponentSelectorSerializer.java        |   76 -
 .../result/DefaultInternalDependencyResult.java    |   59 -
 .../result/DefaultModuleVersionSelection.java      |   45 -
 .../result/DefaultResolutionResultBuilder.java     |   85 -
 .../result/InternalDependencyResult.java           |   40 -
 .../result/InternalDependencyResultSerializer.java |   62 -
 .../result/ModuleVersionSelection.java             |   30 -
 .../result/ModuleVersionSelectionSerializer.java   |   47 -
 .../result/StreamingResolutionResultBuilder.java   |  200 -
 .../result/VersionSelectionReasons.java            |   81 -
 .../resolveengine/store/CachedStoreFactory.java    |  102 -
 .../resolveengine/store/DefaultBinaryStore.java    |  147 -
 .../AbstractModuleDescriptorBackedMetaData.java    |  233 -
 .../BuildableModuleVersionPublishMetaData.java     |   27 -
 .../metadata/ComponentArtifactIdentifier.java      |   34 -
 .../metadata/ComponentArtifactMetaData.java        |   39 -
 .../artifacts/metadata/ComponentMetaData.java      |   85 -
 .../artifacts/metadata/ConfigurationMetaData.java  |   41 -
 .../metadata/DefaultDependencyMetaData.java        |  122 -
 .../artifacts/metadata/DefaultIvyArtifactName.java |   97 -
 .../metadata/DefaultLocalArtifactIdentifier.java   |   69 -
 .../metadata/DefaultLocalComponentMetaData.java    |  157 -
 .../DefaultModuleVersionArtifactIdentifier.java    |   79 -
 .../DefaultModuleVersionArtifactMetaData.java      |   63 -
 .../DefaultModuleVersionPublishMetaData.java       |   84 -
 .../artifacts/metadata/DependencyMetaData.java     |   65 -
 .../artifacts/metadata/IvyArtifactName.java        |   37 -
 .../artifacts/metadata/LocalArtifactMetaData.java  |   23 -
 .../artifacts/metadata/LocalComponentMetaData.java |   40 -
 .../metadata/ModuleDescriptorAdapter.java          |  112 -
 .../metadata/ModuleVersionArtifactIdentifier.java  |   29 -
 .../ModuleVersionArtifactIdentifierSerializer.java |   51 -
 .../metadata/ModuleVersionArtifactMetaData.java    |   38 -
 .../ModuleVersionArtifactPublishMetaData.java      |   35 -
 .../artifacts/metadata/ModuleVersionMetaData.java  |   41 -
 .../metadata/ModuleVersionPublishMetaData.java     |   28 -
 .../metadata/MutableLocalComponentMetaData.java    |   28 -
 .../metadata/MutableModuleVersionMetaData.java     |   34 -
 .../DefaultLocalMavenRepositoryLocator.java        |   74 -
 .../mvnsettings/DefaultMavenFileLocations.java     |   55 -
 .../mvnsettings/DefaultMavenSettingsProvider.java  |   40 -
 .../repositories/AbstractArtifactRepository.java   |   42 -
 .../AbstractAuthenticationSupportedRepository.java |   37 -
 .../repositories/DefaultBaseRepositoryFactory.java |  134 -
 .../DefaultFlatDirArtifactRepository.java          |   98 -
 .../repositories/DefaultIvyArtifactRepository.java |  201 -
 .../DefaultMavenArtifactRepository.java            |  123 -
 .../DefaultMavenLocalArtifactRepository.java       |   50 -
 .../repositories/LegacyDependencyResolver.java     |  294 --
 .../repositories/LegacyMavenResolver.java          |   61 -
 .../repositories/ResolutionAwareRepository.java    |   26 -
 .../DownloadingRepositoryArtifactCache.java        |   73 -
 .../LocalFileRepositoryArtifactCache.java          |   43 -
 .../cachemanager/RepositoryArtifactCache.java      |   45 -
 .../layout/GradleRepositoryLayout.java             |   42 -
 .../repositories/layout/MavenRepositoryLayout.java |   45 -
 .../layout/PatternRepositoryLayout.java            |   85 -
 .../repositories/layout/RepositoryLayout.java      |   47 -
 .../legacy/AbstractRepositoryCacheManager.java     |   85 -
 .../legacy/CustomIvyResolverRepositoryFactory.java |   41 -
 .../legacy/CustomResolverArtifactRepository.java   |   72 -
 .../legacy/DownloadingRepositoryCacheManager.java  |  161 -
 .../legacy/EnhancedArtifactDownloadReport.java     |   38 -
 .../legacy/FixedResolverArtifactRepository.java    |   60 -
 .../legacy/IvyDependencyResolverAdapter.java       |  202 -
 .../LegacyDependencyResolverRepositoryFactory.java |   24 -
 .../legacy/LegacyResolverParserSettings.java       |   98 -
 .../legacy/LocalFileRepositoryCacheManager.java    |   88 -
 .../repositories/resolver/AbstractVersionList.java |   37 -
 .../resolver/ChainedVersionLister.java             |   79 -
 .../resolver/ComponentMetadataDetailsAdapter.java  |   58 -
 .../repositories/resolver/DefaultVersionList.java  |   37 -
 .../resolver/ExternalResourceResolver.java         |  599 ---
 ...rnalResourceResolverDescriptorParseContext.java |   72 -
 .../repositories/resolver/IvyResolver.java         |   92 -
 .../repositories/resolver/IvyResourcePattern.java  |   84 -
 .../repositories/resolver/M2ResourcePattern.java   |   79 -
 .../repositories/resolver/MavenLocalResolver.java  |   57 -
 .../repositories/resolver/MavenMetadataLoader.java |   89 -
 .../repositories/resolver/MavenResolver.java       |  275 --
 .../repositories/resolver/MavenVersionLister.java  |   50 -
 .../resolver/PatternBasedResolver.java             |   31 -
 .../repositories/resolver/ResourcePattern.java     |   43 -
 .../resolver/ResourceVersionLister.java            |  165 -
 .../VerifyingExternalResourceDownloader.java       |   86 -
 .../repositories/resolver/VersionList.java         |   74 -
 .../repositories/resolver/VersionLister.java       |   26 -
 .../transport/ProgressLoggingTransferListener.java |   58 -
 .../transport/RepositoryTransport.java             |   29 -
 .../transport/RepositoryTransportFactory.java      |   56 -
 .../resolution/AbstractSoftwareArtifact.java       |   51 -
 .../resolution/AbstractSoftwareComponent.java      |   49 -
 .../resolution/ComponentMetaDataArtifact.java      |   21 -
 .../resolution/DefaultArtifactResolutionQuery.java |  156 -
 .../DefaultArtifactResolutionQueryFactory.java     |   46 -
 .../DefaultArtifactResolutionQueryResult.java      |   46 -
 .../artifacts/resolution/DefaultJvmLibrary.java    |   45 -
 .../DefaultJvmLibraryJavadocArtifact.java          |   31 -
 .../DefaultJvmLibrarySourcesArtifact.java          |   31 -
 .../DefaultUnresolvedSoftwareComponent.java        |   37 -
 .../resolution/IvyDescriptorArtifact.java          |   21 -
 .../artifacts/resolution/MavenPomArtifact.java     |   22 -
 .../result/DefaultUnresolvedDependencyResult.java  |   56 -
 .../externalresource/AbstractExternalResource.java |   68 -
 .../DefaultLocallyAvailableExternalResource.java   |   53 -
 .../externalresource/ExternalResource.java         |   88 -
 .../LocalFileStandInExternalResource.java          |   92 -
 .../LocallyAvailableExternalResource.java          |   27 -
 .../MetaDataOnlyExternalResource.java              |   73 -
 .../externalresource/MissingExternalResource.java  |   68 -
 .../externalresource/UrlExternalResource.java      |   65 -
 .../cached/ByUrlCachedExternalResourceIndex.java   |   28 -
 .../externalresource/cached/CachedArtifact.java    |   23 -
 .../cached/CachedArtifactIndex.java                |   63 -
 .../cached/CachedExternalResource.java             |   56 -
 .../cached/CachedExternalResourceAdapter.java      |   75 -
 .../cached/CachedExternalResourceIndex.java        |   71 -
 .../externalresource/cached/CachedItem.java        |   49 -
 .../cached/DefaultCachedArtifact.java              |   55 -
 .../cached/DefaultCachedExternalResource.java      |   72 -
 .../cached/DefaultCachedExternalResourceIndex.java |   51 -
 .../externalresource/ivy/AbstractCachedIndex.java  |  104 -
 .../ArtifactAtRepositoryCachedArtifactIndex.java   |   99 -
 .../ivy/ArtifactAtRepositoryKey.java               |   56 -
 .../AbstractLocallyAvailableResourceFinder.java    |   37 -
 .../CompositeLocallyAvailableResourceFinder.java   |   70 -
 .../LazyLocallyAvailableResourceCandidates.java    |   60 -
 .../local/LocallyAvailableResourceCandidates.java  |   31 -
 .../local/LocallyAvailableResourceFinder.java      |   32 -
 ...leResourceFinderSearchableFileStoreAdapter.java |   54 -
 .../ivy/LocallyAvailableResourceFinderFactory.java |  158 -
 ...PatternBasedLocallyAvailableResourceFinder.java |   63 -
 .../metadata/DefaultExternalResourceMetaData.java  |   70 -
 .../metadata/ExternalResourceMetaData.java         |   54 -
 .../metadata/ExternalResourceMetaDataCompare.java  |   66 -
 .../transfer/AbstractProgressLoggingHandler.java   |   41 -
 .../CacheAwareExternalResourceAccessor.java        |   29 -
 .../DefaultCacheAwareExternalResourceAccessor.java |  118 -
 .../transfer/ExternalResourceAccessor.java         |   75 -
 .../transfer/ExternalResourceLister.java           |   26 -
 .../transfer/ExternalResourceUploader.java         |   27 -
 .../ProgressLoggingExternalResourceAccessor.java   |  157 -
 .../ProgressLoggingExternalResourceUploader.java   |   81 -
 .../transfer/ResourceOperation.java                |   70 -
 .../DefaultExternalResourceRepository.java         |  125 -
 .../transport/ExternalResourceRepository.java      |   70 -
 .../transport/file/FileResourceConnector.java      |   99 -
 .../transport/file/FileTransport.java              |   79 -
 .../http/ApacheDirectoryListingParser.java         |  131 -
 .../transport/http/DefaultHttpSettings.java        |   35 -
 .../transport/http/HttpClientConfigurer.java       |  128 -
 .../transport/http/HttpClientHelper.java           |  129 -
 .../transport/http/HttpProxySettings.java          |   42 -
 .../transport/http/HttpRequestException.java       |   31 -
 .../transport/http/HttpResourceAccessor.java       |  108 -
 .../transport/http/HttpResourceLister.java         |   73 -
 .../transport/http/HttpResourceUploader.java       |   49 -
 .../transport/http/HttpResponseResource.java       |  141 -
 .../transport/http/HttpSettings.java               |   24 -
 .../transport/http/HttpTransport.java              |   81 -
 .../JavaSystemPropertiesHttpProxySettings.java     |  104 -
 .../http/RepeatableInputStreamEntity.java          |   60 -
 .../transport/http/ntlm/NTLMCredentials.java       |   92 -
 .../transport/http/ntlm/NTLMSchemeFactory.java     |   64 -
 .../filestore/ivy/ArtifactIdentifierFileStore.java |   44 -
 .../ClientModuleNotationParserFactory.java         |   41 -
 .../DependencyClassPathNotationParser.java         |   56 -
 .../notations/DependencyFilesNotationParser.java   |   44 -
 .../notations/DependencyMapNotationParser.java     |   53 -
 .../notations/DependencyNotationParser.java        |   55 -
 .../notations/DependencyProjectNotationParser.java |   43 -
 .../notations/DependencyStringNotationParser.java  |   74 -
 .../notations/ProjectDependencyFactory.java        |   58 -
 .../DefaultDependencyManagementServicesTest.groovy |   92 -
 .../artifacts/DefaultResolvedArtifactTest.groovy   |   48 -
 .../artifacts/DefaultResolvedDependencySpec.groovy |   92 -
 .../artifacts/DefaultResolvedDependencyTest.java   |  215 -
 .../ModuleVersionSelectorSerializerTest.groovy     |   33 -
 ...vedConfigurationIdentifierSerializerTest.groovy |   37 -
 .../internal/artifacts/ResolverResultsSpec.groovy  |   52 -
 .../DefaultComponentIdentifierFactoryTest.groovy   |   53 -
 .../DefaultModuleComponentIdentifierTest.groovy    |   81 -
 .../DefaultModuleComponentSelectorTest.groovy      |  117 -
 .../DefaultProjectComponentIdentifierTest.groovy   |   58 -
 .../DefaultProjectComponentSelectorTest.groovy     |   91 -
 .../DefaultConfigurationContainerSpec.groovy       |  100 -
 .../DefaultConfigurationContainerTest.groovy       |  117 -
 .../configurations/DefaultConfigurationSpec.groovy |  320 --
 .../configurations/DefaultConfigurationTest.java   |  965 ----
 .../dsl/DefaultComponentMetadataHandlerTest.groovy |   42 -
 .../dsl/ModuleVersionSelectorParsersTest.groovy    |  152 -
 ...PublishArtifactNotationParserFactoryTest.groovy |  122 -
 .../artifacts/ivyservice/CacheLayoutTest.groovy    |   54 -
 ...cheLockingArtifactDependencyResolverTest.groovy |   45 -
 ...efaultBuildableArtifactResolveResultTest.groovy |   64 -
 ...ultBuildableArtifactSetResolveResultTest.groovy |   75 -
 ...faultBuildableComponentResolveResultTest.groovy |  109 -
 .../DefaultDependencyResolveDetailsSpec.groovy     |  152 -
 .../DefaultUnresolvedDependencySpec.groovy         |   35 -
 ...orHandlingArtifactDependencyResolverTest.groovy |  156 -
 .../artifacts/ivyservice/IvyUtilTest.groovy        |   32 -
 .../IvyXmlModuleDescriptorWriterTest.groovy        |  148 -
 .../ModuleVersionNotFoundExceptionTest.groovy      |   49 -
 .../ModuleVersionResolveExceptionTest.groovy       |   60 -
 .../SelfResolvingDependencyResolverTest.groovy     |  135 -
 ...ptyConfigsArtifactDependencyResolverSpec.groovy |   77 -
 ...ionForcingDependencyToModuleResolverSpec.groovy |  107 -
 .../clientmodule/ClientModuleResolverTest.groovy   |   95 -
 .../CachingModuleVersionRepositoryTest.groovy      |  101 -
 ...leModuleVersionMetaDataResolveResultTest.groovy |  174 -
 .../DependencyResolverIdentifierTest.groovy        |  123 -
 .../ErrorHandlingArtifactResolverTest.groovy       |   82 -
 ...ynamicResolveModuleVersionRepositoryTest.groovy |   69 -
 .../LazyDependencyToModuleResolverTest.groovy      |  244 -
 .../RepositoryChainDependencyResolverTest.groovy   |  639 ---
 .../memcache/CachedModuleVersionResultTest.groovy  |   99 -
 .../memcache/CachedRepositoryTest.groovy           |  178 -
 .../memcache/DependencyMetadataCacheTest.groovy    |  225 -
 .../InMemoryDependencyMetadataCacheTest.groovy     |   76 -
 ...tractGradlePomModuleDescriptorParserTest.groovy |   76 -
 .../ivyresolve/parser/AbstractPomReaderTest.groovy |   71 -
 ...onnectedIvyXmlModuleDescriptorParserTest.groovy |   80 -
 ...adlePomModuleDescriptorParserProfileTest.groovy |  827 ----
 .../GradlePomModuleDescriptorParserTest.groovy     | 2087 --------
 .../parser/IvyXmlModuleDescriptorParserTest.groovy |  761 ---
 .../ivyresolve/parser/PomReaderProfileTest.groovy  | 1566 ------
 .../ivyresolve/parser/PomReaderTest.groovy         |  818 ----
 .../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   |   83 -
 ...ltConfigurationsToArtifactsConverterTest.groovy |  103 -
 ...figurationsToModuleDescriptorConverterTest.java |   91 -
 .../DefaultModuleDescriptorFactoryTest.groovy      |   40 -
 .../PublishLocalComponentFactoryTest.groovy        |   56 -
 .../ResolveLocalComponentFactoryTest.groovy        |   91 -
 ...actDependencyDescriptorFactoryInternalTest.java |  135 -
 ...ntModuleIvyDependencyDescriptorFactoryTest.java |   89 -
 .../DefaultClientModuleMetaDataFactoryTest.java    |   89 -
 ...ependenciesToModuleDescriptorConverterTest.java |  129 -
 .../DefaultDependencyDescriptorFactoryTest.groovy  |   61 -
 ...ernalModuleDependencyDescriptorFactoryTest.java |   65 -
 .../ProjectDependencyDescriptorFactoryTest.groovy  |   63 -
 .../ProjectDependencyResolverTest.groovy           |   75 -
 .../DefaultCachePolicySpec.groovy                  |  276 --
 .../DefaultResolutionStrategySpec.groovy           |  193 -
 .../ModuleForcingResolveRuleSpec.groovy            |   89 -
 .../DependencyGraphBuilderTest.groovy              |  957 ----
 .../resolveengine/ModuleVersionSpecTest.groovy     |  368 --
 .../VersionSelectionReasonResolverTest.groovy      |   43 -
 .../CachingDependencyResultFactoryTest.groovy      |   76 -
 .../ComponentIdentifierSerializerTest.groovy       |   60 -
 .../ComponentSelectionReasonSerializerTest.groovy  |   54 -
 .../result/ComponentSelectorSerializerTest.groovy  |   60 -
 .../DefaultResolutionResultBuilderSpec.groovy      |  284 --
 .../resolveengine/result/DummyBinaryStore.groovy   |   48 -
 .../InternalDependencyResultSerializerTest.groovy  |   80 -
 .../ModuleVersionSelectionSerializerTest.groovy    |   40 -
 .../StreamingResolutionResultBuilderTest.groovy    |  139 -
 .../result/VersionSelectionReasonsTest.groovy      |   41 -
 .../metadata/DefaultDependencyMetaDataTest.groovy  |  162 -
 .../metadata/DefaultIvyArtifactNameTest.groovy     |   53 -
 .../DefaultLocalArtifactIdentifierTest.groovy      |   67 -
 .../DefaultLocalComponentMetaDataTest.groovy       |  146 -
 ...faultModuleVersionArtifactIdentifierTest.groovy |   67 -
 ...DefaultModuleVersionArtifactMetaDataTest.groovy |   71 -
 .../DefaultModuleVersionPublishMetaDataTest.groovy |   42 -
 .../metadata/ModuleDescriptorAdapterTest.groovy    |  296 --
 .../DefaultLocalMavenRepositoryLocatorTest.groovy  |  148 -
 .../DefaultBaseRepositoryFactoryTest.groovy        |  162 -
 .../DefaultFlatDirArtifactRepositoryTest.groovy    |   82 -
 .../DefaultIvyArtifactRepositoryTest.groovy        |  320 --
 .../DefaultMavenArtifactRepositoryTest.groovy      |  150 -
 .../DefaultMavenLocalRepositoryTest.groovy         |   69 -
 .../DownloadingRepositoryCacheManagerTest.groovy   |   67 -
 .../resolver/ChainedVersionListerTest.groovy       |  125 -
 .../resolver/ExternalResourceResolverTest.groovy   |  122 -
 .../resolver/IvyResourcePatternTest.groovy         |   50 -
 .../resolver/M2ResourcePatternTest.groovy          |   84 -
 .../repositories/resolver/MavenResolverTest.groovy |   53 -
 .../resolver/MavenVersionListerTest.groovy         |  195 -
 .../resolver/ResourceVersionListerTest.groovy      |  199 -
 .../ProgressLoggingTransferListenerTest.groovy     |   78 -
 .../result/DefaultResolutionResultTest.groovy      |  113 -
 .../DefaultResolvedModuleVersionResultSpec.groovy  |   63 -
 .../CachedExternalResourceAdapterTest.groovy       |   78 -
 .../DefaultArtifactResolutionCacheTest.groovy      |   84 -
 ...ifactAtRepositoryCachedArtifactIndexTest.groovy |  128 -
 ...positeLocallyAvailableResourceFinderTest.groovy |   75 -
 ...zyLocallyAvailableResourceCandidatesTest.groovy |   57 -
 .../DefaultExternalResourceMetaDataTest.groovy     |   33 -
 .../ExternalResourceMetaDataCompareTest.groovy     |  172 -
 ...ltCacheAwareExternalResourceAccessorTest.groovy |   63 -
 ...gressLoggingExternalResourceAccessorTest.groovy |  111 -
 ...gressLoggingExternalResourceUploaderTest.groovy |   67 -
 .../transfer/ResourceOperationTest.groovy          |   95 -
 .../http/ApacheDirectoryListingParserTest.groovy   |  169 -
 .../transport/http/HttpClientConfigurerTest.groovy |  101 -
 .../transport/http/HttpClientHelperTest.groovy     |   62 -
 .../transport/http/HttpResourceListerTest.groovy   |   45 -
 .../transport/http/HttpResponseResourceTest.groovy |   72 -
 ...avaSystemPropertiesHttpProxySettingsTest.groovy |   88 -
 .../transport/http/ntlm/NTLMCredentialsTest.groovy |   96 -
 .../DependencyClassPathNotationParserTest.groovy   |   55 -
 .../DependencyMapNotationParserTest.groovy         |  137 -
 .../notations/DependencyNotationParserTest.groovy  |   54 -
 .../DependencyStringNotationParserTest.groovy      |  171 -
 .../notations/ProjectDependencyFactoryTest.groovy  |   64 -
 .../ivyservice/ivyresolve/parser/test-full.xml     |  106 -
 .../result/ResolutionResultDataBuilder.groovy      |   48 -
 subprojects/core/core.gradle                       |   42 +-
 .../DeprecationHandlingIntegrationTest.groovy      |    9 -
 .../org/gradle/api/ApplyPluginIntegSpec.groovy     |   18 +-
 .../api/BuildScriptExecutionIntegrationSpec.groovy |   40 +
 .../api/BuildScriptExecutionIntegrationTest.groovy |   91 +
 .../ConfigurationOnDemandIntegrationTest.groovy    |  175 +-
 ...rredConfigurableExtensionIntegrationTest.groovy |    2 +-
 .../ExternalScriptExecutionIntegrationSpec.groovy  |   72 +
 .../ExternalScriptExecutionIntegrationTest.groovy  |  196 +
 .../gradle/api/FinalizerTaskIntegrationTest.groovy |   11 +-
 .../api/InitScriptExecutionIntegrationTest.groovy  |  158 +
 .../PluginApplicationErrorIntegrationTest.groovy   |   83 +
 .../SettingsScriptExecutionIntegrationTest.groovy  |   78 +
 .../api/dsl/ConcurrentClassDecorationSpec.groovy   |    2 +-
 .../api/dsl/DynamicObjectIntegrationTest.groovy    |   14 +-
 .../api/dsl/PluginDetectionIntegrationTest.groovy  |  229 +
 .../api/file/FileResolutionIntegrationTest.groovy  |   56 +
 .../ClassLoadersCachingIntegrationTest.groovy      |  559 +++
 .../resource/TextResourceIntegrationTest.groovy    |   91 +
 .../gradle/api/tasks/ArchiveIntegrationTest.groovy |    5 +-
 .../api/tasks/CopyErrorIntegrationTest.groovy      |   24 +
 .../tasks/CopyPermissionsIntegrationTest.groovy    |    4 +
 .../api/tasks/CopyTaskIntegrationSpec.groovy       |   36 +-
 .../api/tasks/CopyTaskIntegrationTest.groovy       |  223 +-
 ...tionTimeTaskConfigurationIntegrationTest.groovy |  192 +-
 .../FailingIncrementalTasksIntegrationTest.groovy  |   47 +
 .../tasks/IncrementalBuildIntegrationTest.groovy   |  376 ++
 .../tasks/IncrementalTaskIntegrationTest.groovy    |   47 -
 .../tasks/IncrementalTasksIntegrationTest.groovy   |  343 ++
 ...kCommandLineConfigurationIntegrationSpec.groovy |    1 -
 .../TaskInputPropertiesIntegrationTest.groovy      |   60 +
 .../api/tasks/TaskRemovalIntegrationTest.groovy    |   44 +-
 .../api/tasks/TaskSelectionIntegrationTest.groovy  |  117 +
 .../RuleBasedTaskExecutionIntegrationTest.groovy   |  234 +
 .../scripts/StatementLabelsIntegrationTest.groovy  |   46 +-
 ...tiveBuildScriptEvaluationIntegrationTest.groovy |   49 +
 .../plugin/PluginHandlerScriptIntegTest.groovy     |  554 ---
 .../ScriptPluginClassLoadingIntegrationTest.groovy |  226 +-
 .../BintrayPluginResolutionIntegTest.groovy        |  115 -
 .../internal/PathLimitationIntegTest.groovy        |   19 +-
 .../internal/WorkerProcessIntegrationTest.java     |   13 +-
 .../ProjectBuilderIntegrationTest.groovy           |    2 +-
 .../shared/build.gradle                            |   53 +
 .../buildSrc/src/main/java/DirTransformerTask.java |    0
 .../buildSrc/src/main/java/GeneratorTask.java      |    0
 .../buildSrc/src/main/java/TransformerTask.java    |    0
 .../groovy/org/gradle/BuildExceptionReporter.java  |   37 +-
 .../src/main/groovy/org/gradle/BuildLogger.java    |    2 +-
 .../src/main/groovy/org/gradle/BuildResult.java    |    2 +-
 .../src/main/groovy/org/gradle/CacheUsage.java     |   36 -
 .../src/main/groovy/org/gradle/GradleLauncher.java |  187 -
 .../src/main/groovy/org/gradle/RefreshOptions.java |   85 -
 .../src/main/groovy/org/gradle/StartParameter.java |  258 +-
 .../groovy/org/gradle/TaskExecutionLogger.java     |    2 +-
 .../groovy/org/gradle/TaskExecutionRequest.java    |   44 +
 .../src/main/groovy/org/gradle/api/AntBuilder.java |   14 +
 .../org/gradle/api/BuildCancelledException.java    |   37 +
 .../org/gradle/api/BuildableModelElement.java      |   43 +
 .../main/groovy/org/gradle/api/DefaultTask.java    |    2 +
 .../IllegalOperationAtExecutionTimeException.java  |   29 -
 .../gradle/api/NamedDomainObjectCollection.java    |    2 +
 .../api/PolymorphicDomainObjectContainer.java      |   12 +
 .../src/main/groovy/org/gradle/api/Project.java    |  257 +-
 .../src/main/groovy/org/gradle/api/Script.java     |   44 +-
 .../core/src/main/groovy/org/gradle/api/Task.java  |   41 +-
 .../api/artifacts/ArtifactRepositoryContainer.java |  167 +-
 .../org/gradle/api/artifacts/ClientModule.java     |    2 +-
 .../gradle/api/artifacts/ComponentMetadata.java    |   60 +
 .../api/artifacts/ComponentMetadataDetails.java    |   33 +-
 .../api/artifacts/ComponentModuleMetadata.java     |   40 +
 .../artifacts/ComponentModuleMetadataDetails.java  |   41 +
 .../gradle/api/artifacts/ComponentSelection.java   |   43 +
 .../api/artifacts/ComponentSelectionRules.java     |  143 +
 .../org/gradle/api/artifacts/Configuration.java    |    4 +-
 .../api/artifacts/ConfigurationContainer.java      |   25 -
 .../api/artifacts/DependencyResolveDetails.java    |    2 +-
 .../api/artifacts/DependencySubstitution.java      |   52 +
 .../api/artifacts/DependencySubstitutions.java     |  167 +
 .../org/gradle/api/artifacts/ExcludeRule.java      |   15 -
 .../artifacts/ModuleDependencySubstitution.java    |   42 +
 .../artifacts/ProjectDependencySubstitution.java   |   31 +
 .../org/gradle/api/artifacts/PublishException.java |    4 +
 .../gradle/api/artifacts/ResolutionStrategy.java   |   49 +-
 .../org/gradle/api/artifacts/ResolveException.java |    4 +-
 .../org/gradle/api/artifacts/ResolvedArtifact.java |    9 -
 .../gradle/api/artifacts/UnresolvedDependency.java |    9 -
 .../artifacts/dsl/ComponentMetadataHandler.java    |   94 +-
 .../dsl/ComponentModuleMetadataHandler.java        |   60 +
 .../api/artifacts/dsl/DependencyHandler.java       |   38 +-
 .../api/artifacts/dsl/RepositoryHandler.java       |   90 +-
 .../org/gradle/api/artifacts/ivy/IvyExtraInfo.java |   60 +
 .../api/artifacts/ivy/IvyModuleDescriptor.java     |   52 +
 .../org/gradle/api/artifacts/ivy/package-info.java |   20 +
 .../artifacts/query/ArtifactResolutionQuery.java   |   75 +
 .../gradle/api/artifacts/query/package-info.java   |   20 +
 .../repositories/AuthenticationSupported.java      |   63 +-
 .../repositories/IvyArtifactRepository.java        |   62 +-
 .../repositories/IvyPatternRepositoryLayout.java   |   55 +
 .../repositories/MavenArtifactRepository.java      |    2 +
 .../repositories/PasswordCredentials.java          |    5 +-
 .../artifacts/repositories/RepositoryLayout.java   |   25 +
 .../resolution/ArtifactResolutionQuery.java        |   32 -
 .../resolution/ArtifactResolutionQueryResult.java  |   32 -
 .../api/artifacts/resolution/JvmLibrary.java       |   31 -
 .../artifacts/resolution/JvmLibraryArtifact.java   |   27 -
 .../resolution/JvmLibraryJavadocArtifact.java      |   27 -
 .../resolution/JvmLibrarySourcesArtifact.java      |   27 -
 .../api/artifacts/resolution/SoftwareArtifact.java |   45 -
 .../artifacts/resolution/SoftwareComponent.java    |   32 -
 .../resolution/UnresolvedSoftwareComponent.java    |   41 -
 .../api/artifacts/resolution/package-info.java     |   20 -
 .../artifacts/result/ArtifactResolutionResult.java |   50 +
 .../api/artifacts/result/ArtifactResult.java       |   29 +
 .../artifacts/result/ComponentArtifactsResult.java |   44 +
 .../api/artifacts/result/ComponentResult.java      |   32 +
 .../artifacts/result/ComponentSelectionReason.java |    7 +-
 .../artifacts/result/ResolvedArtifactResult.java   |   33 +
 .../artifacts/result/ResolvedComponentResult.java  |    1 +
 .../artifacts/result/UnresolvedArtifactResult.java |   31 +
 .../result/UnresolvedComponentResult.java          |   31 +
 .../groovy/org/gradle/api/component/Artifact.java  |   27 +
 .../groovy/org/gradle/api/component/Component.java |   27 +
 .../org/gradle/api/credentials/AwsCredentials.java |   47 +
 .../org/gradle/api/credentials/Credentials.java    |   29 +
 .../org/gradle/api/credentials/package-info.java   |   24 +
 .../main/groovy/org/gradle/api/file/CopySpec.java  |   46 +-
 .../org/gradle/api/file/FileCopyDetails.java       |   37 +-
 .../org/gradle/api/initialization/Settings.java    |    2 +-
 .../internal/AbstractBuildableModelElement.java    |   58 +
 .../api/internal/AbstractClassGenerator.java       |  379 +-
 .../gradle/api/internal/AbstractDynamicObject.java |    2 +-
 .../AbstractPolymorphicDomainObjectContainer.java  |   18 +-
 .../org/gradle/api/internal/AbstractTask.java      |  270 +-
 .../api/internal/AsmBackedClassGenerator.java      |  529 +-
 .../org/gradle/api/internal/BeanDynamicObject.java |   33 +-
 .../gradle/api/internal/ClosureBackedAction.java   |   99 -
 .../api/internal/CompositeDomainObjectSet.java     |   57 +-
 .../api/internal/CompositeDynamicObject.java       |    7 +-
 .../org/gradle/api/internal/ConfigureDelegate.java |   23 +-
 .../api/internal/DefaultClassPathProvider.java     |    3 +
 .../internal/DefaultDomainObjectCollection.java    |   14 +-
 .../DefaultNamedDomainObjectCollection.java        |  170 +-
 .../api/internal/DefaultNamedDomainObjectList.java |   17 +-
 .../api/internal/DefaultNamedDomainObjectSet.java  |    4 +-
 .../DefaultPolymorphicDomainObjectContainer.java   |   41 +-
 .../api/internal/DelegatingDomainObjectSet.java    |   11 +-
 .../api/internal/DependencyClassPathProvider.java  |   16 +-
 .../internal/DependencyInjectingInstantiator.java  |   98 +-
 .../gradle/api/internal/DocumentationRegistry.java |    4 +
 .../internal/DynamicModulesClassPathProvider.java  |   16 +-
 .../api/internal/ExtensibleDynamicObject.java      |   12 +-
 .../org/gradle/api/internal/GradleInternal.java    |    2 +-
 .../PolymorphicDomainObjectContainerInternal.java  |   29 +
 .../org/gradle/api/internal/ProcessOperations.java |   10 +-
 .../ReflectiveNamedDomainObjectFactory.java        |    2 +-
 .../org/gradle/api/internal/SettingsInternal.java  |   13 +-
 .../org/gradle/api/internal/TaskInternal.java      |   14 +-
 .../api/internal/ThreadGlobalInstantiator.java     |    2 +-
 .../TypedDomainObjectContainerWrapper.java         |  221 +
 .../internal/artifacts/BaseRepositoryFactory.java  |   14 +-
 .../DefaultArtifactRepositoryContainer.java        |  119 +-
 .../api/internal/artifacts/DefaultExcludeRule.java |   12 -
 .../artifacts/DefaultExcludeRuleContainer.java     |    8 +-
 .../artifacts/DependencyResolutionServices.java    |    5 +-
 .../DependencyResolveDetailsInternal.java          |    3 +
 .../artifacts/DependencySubstitutionInternal.java  |   35 +
 .../artifacts/ExcludeRuleNotationConverter.java    |   49 +
 .../artifacts/ExcludeRuleNotationParser.java       |   44 -
 .../ModuleDependencySubstitutionInternal.java      |   25 +
 .../ProjectDependencySubstitutionInternal.java     |   23 +
 .../configurations/ConfigurationInternal.java      |   27 -
 .../configurations/ResolutionStrategyInternal.java |   56 -
 .../configurations/dynamicversion/CachePolicy.java |    8 +-
 .../dependencies/AbstractExternalDependency.java   |   42 -
 .../AbstractExternalModuleDependency.java          |   86 +
 .../dependencies/DefaultClientModule.java          |   71 +-
 .../DefaultExternalModuleDependency.java           |   62 +-
 .../artifacts/dsl/DefaultRepositoryHandler.java    |   42 +-
 .../ArtifactResolutionQueryFactory.java            |   22 -
 .../dsl/dependencies/DefaultDependencyHandler.java |   22 +-
 ...elegate.groovy => ModuleFactoryDelegate.groovy} |    0
 .../query/ArtifactResolutionQueryFactory.java      |   22 +
 .../repositories/ArtifactRepositoryInternal.java   |    6 -
 .../org/gradle/api/internal/cache/BinaryStore.java |    4 +-
 .../org/gradle/api/internal/cache/Cache.java       |    2 +-
 .../api/internal/cache/CacheAccessSerializer.java  |    2 +-
 .../gradle/api/internal/cache/CacheSupport.java    |    2 +-
 .../org/gradle/api/internal/cache/Loader.java      |   26 +
 .../api/internal/cache/MinimalPersistentCache.java |   88 +
 .../cache/SingleOperationPersistentStore.java      |   88 +
 .../org/gradle/api/internal/cache/Stash.java       |   26 +
 .../changes/ChangesOnlyIncrementalTaskInputs.java  |    4 +-
 .../DefaultTaskArtifactStateRepository.java        |    4 +-
 .../changes/IncrementalTaskInputsInternal.java     |   24 +
 .../changes/RebuildIncrementalTaskInputs.java      |    4 +-
 .../ShortCircuitTaskArtifactStateRepository.java   |    3 +-
 .../changes/StatefulIncrementalTaskInputs.java     |   13 +-
 .../rules/InputFilesStateChangeRule.java           |    7 +-
 .../changedetection/rules/TaskUpToDateState.java   |   29 +-
 .../state/CacheBackedFileSnapshotRepository.java   |    2 +-
 .../state/CacheBackedTaskHistoryRepository.java    |   17 +-
 .../state/CachingFileSnapshotter.java              |   11 +-
 .../state/DefaultFileCollectionSnapshotter.java    |   73 +-
 .../state/DefaultFileSnapshotterSerializer.java    |   18 +-
 .../state/DefaultTaskArtifactStateCacheAccess.java |    6 +-
 .../state/FileCollectionSnapshot.java              |    2 +
 .../state/FileCollectionSnapshotter.java           |    2 +-
 .../changedetection/state/FileSnapshot.java        |   21 +
 .../changedetection/state/FileSnapshotter.java     |   10 -
 .../changedetection/state/FilesSnapshotSet.java    |   37 +
 .../state/InputPropertiesSerializer.java           |   45 +
 .../state/OutputFilesCollectionSnapshotter.java    |   10 +-
 .../state/OutputFilesSnapshotSerializer.java       |    6 +-
 .../internal/classpath/DefaultModuleRegistry.java  |   23 +-
 .../internal/coerce/PropertySetTransformer.java    |   25 +
 .../internal/coerce/StringToEnumTransformer.java   |   86 +
 .../TypeCoercingMethodArgumentsTransformer.java    |   91 -
 .../api/internal/collections/CollectionFilter.java |    5 +
 .../api/internal/component/ArtifactType.java       |   26 +
 .../internal/component/BuildableJavaComponent.java |   36 +
 .../api/internal/component/ComponentRegistry.java  |   32 +
 .../component/ComponentTypeRegistration.java       |   25 +
 .../internal/component/ComponentTypeRegistry.java  |   25 +
 .../component/DefaultComponentTypeRegistry.java    |   69 +
 .../api/internal/file/AbstractFileResolver.java    |   23 +-
 .../api/internal/file/AbstractFileTreeElement.java |    4 +-
 .../api/internal/file/BaseDirFileResolver.java     |    2 +-
 .../api/internal/file/BasicFileResolver.java       |   63 +
 .../api/internal/file/DefaultFileLookup.java       |    2 +-
 .../api/internal/file/DefaultFileOperations.java   |   57 +-
 .../api/internal/file/DefaultFileTreeElement.java  |   12 +-
 .../api/internal/file/DefaultFileVisitDetails.java |    4 +-
 .../org/gradle/api/internal/file/FileLookup.java   |    2 +-
 .../gradle/api/internal/file/FileOperations.java   |   12 +-
 .../internal/file/FileOrUriNotationConverter.java  |  120 +
 .../api/internal/file/FileOrUriNotationParser.java |  111 -
 .../api/internal/file/IdentityFileResolver.java    |    4 +-
 .../org/gradle/api/internal/file/RelativeFile.java |   12 +
 .../api/internal/file/TemporaryFileProvider.java   |    2 +-
 .../internal/file/TmpDirTemporaryFileProvider.java |    2 +-
 .../api/internal/file/archive/TarFileTree.java     |   12 +-
 .../api/internal/file/archive/ZipFileTree.java     |   10 +-
 .../file/collections/DelegatingFileTree.java       |   53 +
 .../file/collections/DirectoryFileTree.java        |    4 +-
 .../collections/LazilyInitializedFileTree.java     |   35 +
 .../api/internal/file/collections/MapFileTree.java |   31 +-
 .../collections/SingleIncludePatternFileTree.java  |    8 +-
 .../file/collections/SingletonFileTree.java        |    4 +-
 .../api/internal/file/copy/CopyActionExecuter.java |    2 +-
 .../internal/file/copy/CopyFileVisitorImpl.java    |   12 +-
 .../api/internal/file/copy/CopySpecActionImpl.java |   10 +-
 .../CopySpecBackedCopyActionProcessingStream.java  |    2 +-
 .../api/internal/file/copy/CopySpecInternal.java   |   23 +-
 .../api/internal/file/copy/CopySpecResolver.java   |   54 +
 .../api/internal/file/copy/DefaultCopySpec.java    |  363 +-
 .../internal/file/copy/DefaultFileCopyDetails.java |   39 +-
 .../file/copy/DelegatingCopySpecInternal.java      |   34 +-
 .../file/copy/DestinationRootCopySpec.java         |    1 +
 .../gradle/api/internal/file/copy/LineFilter.java  |    2 +-
 .../api/internal/file/copy/MatchingCopyAction.java |    2 +-
 .../file/copy/NormalizingCopyActionDecorator.java  |   14 +-
 .../internal/file/copy/PathNotationConverter.java  |   74 +
 .../api/internal/file/copy/PathNotationParser.java |   63 -
 .../internal/file/copy/RelativizedCopySpec.java    |   51 -
 .../internal/file/copy/SingleParentCopySpec.java   |   63 +
 .../filestore/GroupedAndNamedUniqueFileStore.java  |   83 -
 .../api/internal/filestore/PathKeyFileStore.java   |  202 -
 .../filestore/PathNormalisingKeyFileStore.java     |   66 -
 .../internal/filestore/UniquePathKeyFileStore.java |   52 -
 .../gradle/api/internal/html/SimpleHtmlWriter.java |   41 -
 .../initialization/BasicDomainObjectContext.java   |   25 +
 .../internal/initialization/ClassLoaderCache.java  |   27 -
 .../internal/initialization/ClassLoaderIds.java    |   80 +
 .../internal/initialization/ClassLoaderScope.java  |   64 +-
 .../initialization/ClassLoaderScopeIdentifier.java |  118 +
 .../initialization/DefaultClassLoaderCache.java    |  102 -
 .../initialization/DefaultClassLoaderScope.java    |  176 +-
 .../DefaultScriptHandlerFactory.java               |    5 -
 .../initialization/RootClassLoaderScope.java       |   45 +-
 .../ScriptHandlerClassLoaderFactory.java           |    2 +-
 .../loadercache/ClassLoaderCache.java              |   31 +
 .../initialization/loadercache/ClassLoaderId.java  |   25 +
 .../loadercache/ClassPathSnapshot.java             |   25 +
 .../loadercache/ClassPathSnapshotter.java          |   26 +
 .../loadercache/DefaultClassLoaderCache.java       |  184 +
 .../loadercache/FileClassPathSnapshotter.java      |   50 +
 .../loadercache/HashClassPathSnapshotter.java      |  100 +
 .../api/internal/plugins/DefaultAppliedPlugin.java |   42 +
 .../api/internal/plugins/DefaultConvention.java    |    6 -
 .../plugins/DefaultObjectConfigurationAction.java  |   49 +-
 .../internal/plugins/DefaultPluginContainer.java   |  105 +-
 .../api/internal/plugins/DefaultPluginManager.java |  212 +
 .../internal/plugins/DefaultPluginRegistry.java    |  243 +-
 .../plugins/DefaultPotentialPluginWithId.java      |   67 +
 .../internal/plugins/EmbeddableJavaProject.java    |   31 -
 .../ExtraPropertiesDynamicObjectAdapter.java       |   25 +-
 .../plugins/ImperativeOnlyPluginApplicator.java    |   47 +
 .../plugins/PluginApplicationException.java        |   28 +
 .../api/internal/plugins/PluginApplicator.java     |   32 +
 .../api/internal/plugins/PluginAwareInternal.java  |   23 +
 .../api/internal/plugins/PluginDescriptor.java     |    4 +
 .../api/internal/plugins/PluginImplementation.java |   32 +
 .../api/internal/plugins/PluginInspector.java      |  156 +
 .../internal/plugins/PluginManagerInternal.java    |   71 +
 .../api/internal/plugins/PluginRegistry.java       |   26 +-
 .../api/internal/plugins/PotentialPlugin.java      |   43 +
 .../plugins/RuleBasedPluginApplicator.java         |   72 +
 .../api/internal/project/AbstractPluginAware.java  |   37 +-
 .../api/internal/project/AbstractProject.java      |  501 +-
 .../api/internal/project/DefaultAntBuilder.java    |   66 +-
 .../internal/project/DefaultAntBuilderFactory.java |    4 +-
 .../project/DefaultIsolatedAntBuilder.groovy       |  127 +-
 .../api/internal/project/DefaultProject.java       |    4 +-
 .../project/DefaultProjectAccessListener.java      |   11 +-
 .../internal/project/DefaultProjectTaskLister.java |   32 +
 .../project/DeferredProjectConfiguration.java      |   65 +
 .../api/internal/project/IProjectFactory.java      |    2 +-
 .../api/internal/project/IsolatedAntBuilder.java   |   10 -
 .../api/internal/project/ProjectFactory.java       |   16 +-
 .../api/internal/project/ProjectInternal.java      |   25 +-
 .../api/internal/project/ProjectTaskLister.java    |   30 +
 .../api/internal/project/ant/BasicAntBuilder.java  |    8 +-
 .../AnnotationProcessingTaskFactory.java           |   21 +-
 .../taskfactory/DependencyAutoWireTaskFactory.java |   10 +-
 .../internal/project/taskfactory/ITaskFactory.java |    3 +-
 .../InputDirectoryPropertyAnnotationHandler.java   |    4 +-
 .../InputFilePropertyAnnotationHandler.java        |    4 +-
 .../InputFilesPropertyAnnotationHandler.java       |    4 +-
 .../InputPropertyAnnotationHandler.java            |    4 +-
 .../NestedBeanPropertyAnnotationHandler.java       |    4 +-
 .../OutputDirectoryPropertyAnnotationHandler.java  |    5 +-
 .../OutputFilePropertyAnnotationHandler.java       |    5 +-
 .../internal/project/taskfactory/TaskFactory.java  |   27 +-
 .../internal/project/taskfactory/UpdateAction.java |    4 +-
 .../api/internal/resource/CachingResource.java     |   45 -
 .../api/internal/resource/DelegatingResource.java  |   52 -
 .../org/gradle/api/internal/resource/Resource.java |   64 -
 .../api/internal/resource/ResourceException.java   |   31 -
 .../resource/ResourceNotFoundException.java        |   30 -
 .../api/internal/resource/StringResource.java      |   50 -
 .../gradle/api/internal/resource/UriResource.java  |  117 -
 .../resources/CharSourceBackedTextResource.java    |   87 +
 .../internal/resources/DefaultResourceHandler.java |   22 +-
 .../resources/DefaultTextResourceFactory.java      |   55 +
 .../FileCollectionBackedArchiveTextResource.java   |   52 +
 .../FileCollectionBackedTextResource.java          |   87 +
 .../resources/StringBackedTextResource.java        |   74 +
 .../api/internal/tasks/DefaultTaskCollection.java  |   17 +-
 .../api/internal/tasks/DefaultTaskContainer.java   |  165 +-
 .../tasks/DefaultTaskContainerFactory.java         |   50 +-
 .../api/internal/tasks/DefaultTaskDependency.java  |   22 +-
 .../api/internal/tasks/DefaultTaskInputs.java      |   93 +-
 .../api/internal/tasks/DefaultTaskOutputs.java     |   49 +-
 .../internal/tasks/PublicTaskSpecification.java    |   38 +
 .../api/internal/tasks/TaskContainerInternal.java  |   37 +-
 .../org/gradle/api/internal/tasks/TaskMutator.java |   95 +
 .../gradle/api/internal/tasks/TaskResolver.java    |    2 +-
 .../api/internal/tasks/TaskStateInternal.java      |    1 +
 .../api/internal/tasks/TaskStatusNagger.java       |  105 -
 .../tasks/options/OptionNotationParserFactory.java |   23 +-
 .../api/internal/xml/SimpleMarkupWriter.java       |  421 --
 .../gradle/api/internal/xml/SimpleXmlWriter.java   |   45 -
 .../gradle/api/internal/xml/XmlTransformer.java    |  359 --
 .../groovy/org/gradle/api/logging/LogLevel.java    |   92 +-
 .../groovy/org/gradle/api/logging/Logging.java     |  316 +-
 .../org/gradle/api/logging/LoggingManager.java     |    3 +
 .../org/gradle/api/logging/LoggingOutput.java      |    3 +
 .../org/gradle/api/plugins/AppliedPlugin.java      |   70 +
 .../groovy/org/gradle/api/plugins/Convention.java  |    2 +-
 .../org/gradle/api/plugins/ExtensionContainer.java |   11 -
 .../api/plugins/ExtraPropertiesExtension.java      |   11 +-
 .../gradle/api/plugins/InvalidPluginException.java |   33 +
 .../api/plugins/ObjectConfigurationAction.java     |   11 +
 .../groovy/org/gradle/api/plugins/PluginAware.java |   72 +-
 .../org/gradle/api/plugins/PluginContainer.java    |   18 +-
 .../org/gradle/api/plugins/PluginManager.java      |  111 +
 .../org/gradle/api/resources/ResourceHandler.java  |   13 +
 .../org/gradle/api/resources/TextResource.java     |   87 +
 .../gradle/api/resources/TextResourceFactory.java  |   88 +
 .../main/groovy/org/gradle/api/specs/Specs.java    |   92 -
 .../org/gradle/api/tasks/AbstractCopyTask.java     |   64 +-
 .../org/gradle/api/tasks/AbstractExecTask.java     |  279 ++
 .../org/gradle/api/tasks/ConventionValue.java      |    5 +-
 .../src/main/groovy/org/gradle/api/tasks/Copy.java |   16 +-
 .../groovy/org/gradle/api/tasks/Directory.groovy   |   42 -
 .../src/main/groovy/org/gradle/api/tasks/Exec.java |  248 +-
 .../groovy/org/gradle/api/tasks/GradleBuild.java   |  240 +-
 .../org/gradle/api/tasks/InputDirectory.java       |    3 +-
 .../main/groovy/org/gradle/api/tasks/JavaExec.java |    9 +-
 .../org/gradle/api/tasks/ParallelizableTask.java   |   73 +
 .../groovy/org/gradle/api/tasks/SourceTask.java    |   25 +-
 .../src/main/groovy/org/gradle/api/tasks/Sync.java |    7 +-
 .../groovy/org/gradle/api/tasks/TaskContainer.java |  109 +-
 .../groovy/org/gradle/api/tasks/TaskState.java     |    2 +-
 .../gradle/api/tasks/TaskValidationException.java  |    4 +-
 .../groovy/org/gradle/api/tasks/ant/AntTarget.java |   30 +-
 .../api/tasks/bundling/AbstractArchiveTask.java    |    2 +-
 .../org/gradle/api/tasks/bundling/Compression.java |   11 -
 .../tasks/incremental/IncrementalTaskInputs.java   |    3 +-
 .../org/gradle/api/tasks/util/PatternSet.java      |   28 +-
 .../cache/PersistentIndexedCacheParameters.java    |    8 +-
 .../groovy/org/gradle/cache/PersistentStore.java   |    4 +-
 .../org/gradle/cache/internal/CacheFactory.java    |    3 +-
 .../gradle/cache/internal/DefaultCacheAccess.java  |    9 +-
 .../gradle/cache/internal/DefaultCacheFactory.java |   24 +-
 .../cache/internal/DefaultCacheRepository.java     |    7 +-
 .../cache/internal/DefaultFileLockManager.java     |    3 +-
 .../internal/DefaultPersistentDirectoryCache.java  |    9 +-
 .../internal/DefaultPersistentDirectoryStore.java  |    2 +-
 .../internal/DefaultProcessMetaDataProvider.java   |    2 +-
 .../cache/internal/FileLockCommunicator.java       |    2 +-
 .../cache/internal/NonThreadsafeInMemoryStore.java |   74 +
 .../gradle/cache/internal/SimpleStateCache.java    |    6 +-
 .../btree/BTreePersistentIndexedCache.java         |    6 +-
 .../DefaultFileLockContentionHandler.java          |    7 +-
 .../configuration/DefaultBuildConfigurer.java      |   19 +-
 .../gradle/configuration/DefaultImportsReader.java |   63 +
 .../configuration/DefaultInitScriptProcessor.java  |   14 +-
 .../configuration/DefaultScriptPluginFactory.java  |  146 +-
 .../configuration/ImplicitTasksConfigurer.java     |   26 -
 .../org/gradle/configuration/ImportsReader.java    |   46 +-
 .../gradle/configuration/ImportsScriptSource.java  |   53 -
 .../gradle/configuration/ScriptPluginFactory.java  |    2 +-
 .../project/BuildScriptProcessor.java              |    3 +-
 .../project/LifecycleProjectEvaluator.java         |    2 +-
 .../project/PluginsProjectConfigureActions.java    |    7 +-
 .../project/ProjectDependencies2TaskResolver.java  |   41 -
 .../TaskModelRealizingConfigurationAction.java     |   31 -
 .../org/gradle/execution/DefaultBuildExecuter.java |    2 +
 .../DefaultTasksBuildExecutionAction.java          |   21 +-
 ...ludedTaskFilteringBuildConfigurationAction.java |   17 +-
 .../gradle/execution/MultipleBuildFailures.java    |    4 +-
 .../org/gradle/execution/ProjectConfigurer.java    |   31 +
 .../gradle/execution/ProjectEvaluatingAction.java  |   54 -
 .../execution/SelectedTaskExecutionAction.java     |   21 +
 .../org/gradle/execution/TaskNameResolver.java     |  191 +-
 .../TaskNameResolvingBuildConfigurationAction.java |   25 +-
 .../gradle/execution/TaskPathProjectEvaluator.java |   37 +-
 .../gradle/execution/TaskSelectionException.java   |    4 +-
 .../org/gradle/execution/TaskSelectionResult.java  |    4 +-
 .../groovy/org/gradle/execution/TaskSelector.java  |  104 +-
 .../commandline/CommandLineTaskParser.java         |   20 +-
 .../commandline/TaskConfigurationException.java    |    4 +-
 .../taskgraph/DefaultTaskExecutionPlan.java        |  349 +-
 .../taskgraph/DefaultTaskGraphExecuter.java        |   10 +-
 .../taskgraph/ParallelTaskPlanExecutor.java        |   21 +-
 .../taskgraph/TaskDependencyGraphEdge.groovy       |   36 -
 .../org/gradle/execution/taskgraph/TaskInfo.java   |    2 +-
 .../execution/taskpath/ResolvedTaskPath.java       |    9 +
 .../groovy/scripts/AbstractUriScriptSource.java    |   58 +
 .../gradle/groovy/scripts/CachingScriptSource.java |    4 +-
 .../org/gradle/groovy/scripts/DefaultScript.java   |   43 +-
 .../scripts/DefaultScriptCompilerFactory.java      |   32 +-
 .../groovy/scripts/DelegatingScriptSource.java     |    2 +-
 .../scripts/NonExistentFileScriptSource.java       |   64 +
 .../org/gradle/groovy/scripts/ScriptCompiler.java  |   20 +-
 .../org/gradle/groovy/scripts/ScriptRunner.java    |    5 +-
 .../org/gradle/groovy/scripts/ScriptSource.java    |    2 +-
 .../gradle/groovy/scripts/StringScriptSource.java  |    4 +-
 .../org/gradle/groovy/scripts/Transformer.java     |   28 -
 .../org/gradle/groovy/scripts/UriScriptSource.java |   48 +-
 .../gradle/groovy/scripts/internal/AstUtils.java   |   91 -
 .../scripts/internal/BuildScriptTransformer.java   |   40 +-
 .../internal/CachingScriptClassCompiler.java       |   66 +-
 .../internal/ClassCachingCompiledScript.java       |   42 +
 .../groovy/scripts/internal/CompileOperation.java  |   47 +
 .../groovy/scripts/internal/CompiledScript.java    |   26 +
 .../internal/DefaultScriptCompilationHandler.java  |  211 +-
 .../internal/DefaultScriptRunnerFactory.java       |   36 +-
 .../internal/FactoryBackedCompileOperation.java    |   56 +
 .../FileCacheBackedScriptClassCompiler.java        |   44 +-
 .../scripts/internal/FilteredTransformer.java      |   28 -
 .../internal/FilteringScriptTransformer.java       |   49 +
 .../internal/FilteringStatementTransformer.java    |   43 -
 .../scripts/internal/FixMainScriptTransformer.java |    4 -
 .../ImperativeStatementDetectingTransformer.java   |  334 ++
 .../internal/InitialPassStatementTransformer.java  |  103 +
 .../internal/PluginsAndBuildscriptTransformer.java |  127 -
 .../groovy/scripts/internal/ScriptBlock.java       |   37 -
 ...riptBlockToServiceConfigurationTransformer.java |   54 -
 .../scripts/internal/ScriptClassCompiler.java      |    8 +-
 .../scripts/internal/ScriptCompilationHandler.java |   13 +-
 .../scripts/internal/ScriptRunnerFactory.java      |    3 +-
 .../internal/ShortCircuitEmptyScriptCompiler.java  |   29 +-
 .../StatementExtractingScriptTransformer.java      |  145 -
 .../internal/StatementLabelsDeprecationLogger.java |   34 -
 .../internal/StatementLabelsScriptTransformer.java |   25 +-
 .../scripts/internal/StatementTransformer.java     |   30 -
 .../scripts/internal/SubsetScriptTransformer.java  |  109 +
 .../internal/TaskDefinitionScriptTransformer.java  |    4 -
 .../gradle/initialization/AbstractProjectSpec.java |   25 +-
 .../org/gradle/initialization/BaseSettings.java    |   94 +-
 .../org/gradle/initialization/BuildAction.java     |   31 -
 .../initialization/BuildCancellationToken.java     |   36 +
 .../initialization/BuildCompletionListener.java    |   24 +
 .../org/gradle/initialization/BuildController.java |   45 -
 .../gradle/initialization/BuildEventConsumer.java  |   25 +
 .../initialization/BuildFileProjectSpec.java       |   31 +-
 .../initialization/BuildLayoutParameters.java      |   25 +-
 .../org/gradle/initialization/BuildLoader.java     |    2 +-
 .../gradle/initialization/BuildRequestContext.java |   32 +
 .../gradle/initialization/ClassLoaderRegistry.java |    8 +-
 .../initialization/ClassLoaderScopeRegistry.java   |   27 +
 .../DefaultBuildCancellationToken.java             |   79 +
 .../initialization/DefaultBuildRequestContext.java |   51 +
 .../initialization/DefaultClassLoaderRegistry.java |   40 +-
 .../DefaultClassLoaderScopeRegistry.java           |   40 +
 .../DefaultCommandLineConverter.java               |   96 +-
 .../initialization/DefaultExceptionAnalyser.java   |    6 +-
 .../initialization/DefaultGradleLauncher.java      |   63 +-
 .../DefaultGradleLauncherFactory.java              |   80 +-
 .../gradle/initialization/DefaultProjectSpec.java  |   32 +-
 .../org/gradle/initialization/DefaultSettings.java |    6 +-
 .../initialization/DependencyResolutionLogger.java |    1 -
 .../FixedBuildCancellationToken.java               |   33 +
 .../org/gradle/initialization/GradleLauncher.java  |   66 +
 .../initialization/GradleLauncherFactory.java      |   17 +-
 .../initialization/InstantiatingBuildLoader.java   |   33 +-
 .../initialization/LayoutCommandLineConverter.java |   22 +-
 .../initialization/NoOpBuildEventConsumer.java     |   23 +
 .../ProjectDirectoryProjectSpec.java               |   29 +-
 .../initialization/ProjectPathProjectSpec.java     |   66 -
 .../ProjectPropertySettingBuildLoader.java         |   15 +-
 .../org/gradle/initialization/ProjectSpec.java     |   17 +-
 .../org/gradle/initialization/ProjectSpecs.java    |   17 +-
 .../PropertiesLoadingSettingsProcessor.java        |    4 +-
 .../ScriptEvaluatingSettingsProcessor.java         |   10 +-
 .../org/gradle/initialization/SettingsFactory.java |    4 +-
 .../org/gradle/initialization/SettingsHandler.java |   50 +-
 .../gradle/initialization/SettingsLocation.java    |    2 +-
 .../gradle/initialization/SettingsProcessor.java   |    2 +-
 .../StackTraceSanitizingExceptionAnalyser.java     |   32 +
 .../buildsrc/BuildSourceBuilder.java               |   20 +-
 .../buildsrc/BuildSrcBuildListenerFactory.java     |    5 +-
 .../buildsrc/BuildSrcUpdateFactory.java            |    2 +-
 .../gradle/initialization/layout/BuildLayout.java  |    2 +
 .../initialization/layout/BuildLayoutFactory.java  |   16 +-
 .../internal/DefaultTaskExecutionRequest.java      |   82 +
 .../AuthenticationSupportedInternal.java           |   31 +
 .../credentials/DefaultAwsCredentials.java         |   41 +
 .../exceptions/AbstractMultiCauseException.java    |  111 -
 .../exceptions/LocationAwareException.java         |   21 +-
 .../LoggingDeprecatedFeatureHandler.java           |    4 +-
 .../org/gradle/internal/html/SimpleHtmlWriter.java |   41 +
 .../gradle/internal/invocation/BuildAction.java    |   28 +
 .../internal/invocation/BuildActionRunner.java     |   27 +
 .../internal/invocation/BuildController.java       |   60 +
 .../operations/logging/BuildOperationLogInfo.java  |   47 +
 .../operations/logging/BuildOperationLogger.java   |   25 +
 .../logging/BuildOperationLoggerFactory.java       |   23 +
 .../logging/DefaultBuildOperationLogger.java       |   96 +
 .../DefaultBuildOperationLoggerFactory.java        |   73 +
 .../local/GroupedAndNamedUniqueFileStore.java      |   80 +
 .../internal/resource/local/PathKeyFileStore.java  |  198 +
 .../local/PathNormalisingKeyFileStore.java         |   63 +
 .../resource/local/UniquePathKeyFileStore.java     |   51 +
 .../scopes/BuildScopeServiceRegistryFactory.java   |    4 +-
 .../service/scopes/BuildScopeServices.java         |  132 +-
 .../service/scopes/GlobalScopeServices.java        |   87 +-
 .../service/scopes/GradleScopeServices.java        |   51 +-
 .../service/scopes/PluginServiceRegistry.java      |    6 +
 .../service/scopes/ProjectScopeServices.java       |   48 +-
 .../service/scopes/SettingsScopeServices.java      |   14 +-
 .../service/scopes/TaskExecutionServices.java      |   12 +-
 .../internal/service/scopes/TaskScopeServices.java |   10 +-
 .../CharSequenceNotationConverter.java             |   39 +
 .../typeconversion/CharSequenceNotationParser.java |   12 +-
 .../ClosureToSpecNotationConverter.java            |   40 +
 .../ClosureToSpecNotationParser.java               |   36 -
 .../typeconversion/CompositeNotationConverter.java |   43 +
 .../typeconversion/CompositeNotationParser.java    |    7 +-
 .../EnumFromCharSequenceNotationParser.java        |   12 +-
 .../ErrorHandlingNotationParser.java               |   44 +-
 .../typeconversion/FlatteningNotationParser.java   |    8 +-
 .../typeconversion/JustReturningConverter.java     |   39 +
 .../typeconversion/JustReturningParser.java        |   38 -
 .../typeconversion/MapNotationConverter.java       |  137 +
 .../internal/typeconversion/MapNotationParser.java |  131 -
 .../typeconversion/NotationConvertResult.java      |   26 +
 .../internal/typeconversion/NotationConverter.java |   38 +
 .../NotationConverterToNotationParserAdapter.java  |   55 +
 .../internal/typeconversion/NotationParser.java    |   10 +-
 .../typeconversion/NotationParserBuilder.java      |   97 +-
 .../TypeFilteringNotationConverter.java            |   40 +
 .../typeconversion/TypedNotationConverter.java     |   48 +
 .../typeconversion/TypedNotationParser.java        |   47 -
 .../UnsupportedNotationException.java              |   24 +
 .../gradle/internal/xml/SimpleMarkupWriter.java    |  306 ++
 .../org/gradle/internal/xml/SimpleXmlWriter.java   |   45 +
 .../org/gradle/internal/xml/XmlTransformer.java    |  359 ++
 .../org/gradle/internal/xml/XmlValidation.java     |  146 +
 .../org/gradle/invocation/DefaultGradle.java       |   85 +-
 .../org/gradle/listener/BroadcastDispatch.java     |  113 -
 .../gradle/listener/ContextClassLoaderProxy.java   |   42 -
 .../gradle/listener/DefaultListenerManager.java    |  184 -
 .../org/gradle/listener/LazyCreationProxy.java     |   55 -
 .../org/gradle/listener/ListenerBroadcast.java     |  115 +-
 .../org/gradle/listener/ListenerManager.java       |   83 +-
 .../listener/ListenerNotificationException.java    |   29 -
 .../groovy/org/gradle/logging/ConsoleOutput.java   |   39 +
 .../org/gradle/logging/LoggingConfiguration.java   |   17 +-
 .../org/gradle/logging/LoggingServiceRegistry.java |   57 +-
 .../AbstractLineChoppingStyledTextOutput.java      |    2 +-
 .../logging/internal/AbstractStyledTextOutput.java |    2 +-
 .../org/gradle/logging/internal/AnsiConsole.java   |   12 +-
 .../logging/internal/ConsoleConfigureAction.java   |   39 +-
 .../gradle/logging/internal/DefaultColorMap.java   |   14 +-
 .../logging/internal/DefaultLoggingManager.java    |   33 +-
 .../internal/DefaultStatusBarFormatter.java        |    2 +-
 .../internal/LoggingCommandLineConverter.java      |   31 +-
 .../logging/internal/LoggingOutputInternal.java    |   45 +-
 .../logging/internal/OutputEventRenderer.java      |  105 +-
 .../internal/ProgressLogEventGenerator.java        |    2 +-
 .../internal/logback/LogLevelConverter.java        |   70 -
 .../internal/logback/LogbackLoggingConfigurer.java |  138 -
 .../slf4j/OutputEventListenerBackedLogger.java     |  472 ++
 .../OutputEventListenerBackedLoggerContext.java    |  417 ++
 .../internal/slf4j/Slf4jLoggingConfigurer.java     |   51 +
 .../groovy/org/gradle/model/ModelFinalizer.java    |   30 -
 .../main/groovy/org/gradle/model/ModelPath.java    |   90 -
 .../main/groovy/org/gradle/model/ModelRule.java    |   40 -
 .../main/groovy/org/gradle/model/ModelRules.java   |   57 -
 .../collection/internal/BridgedCollections.java    |  213 +
 .../DomainObjectContainerModelProjection.java      |  137 +
 ...cTypesDomainObjectContainerModelProjection.java |   45 +
 ...icTypeDomainObjectContainerModelProjection.java |   64 +
 .../main/groovy/org/gradle/model/dsl/ModelDsl.java |   26 -
 .../gradle/model/dsl/internal/GroovyModelDsl.java  |   84 -
 .../org/gradle/model/internal/DefaultInputs.java   |   42 -
 .../model/internal/DefaultModelRegistry.java       |  253 -
 .../groovy/org/gradle/model/internal/Inputs.java   |   25 -
 .../model/internal/ModelCreationListener.java      |   25 -
 .../org/gradle/model/internal/ModelCreator.java    |   25 -
 .../org/gradle/model/internal/ModelMutation.java   |   39 -
 .../org/gradle/model/internal/ModelMutator.java    |   25 -
 .../org/gradle/model/internal/ModelRegistry.java   |   38 -
 .../internal/ModelRegistryBackedModelRules.java    |  129 -
 .../persist/AlwaysNewModelRegistryStore.java       |   36 +
 .../model/internal/persist/ModelRegistryStore.java |   26 +
 .../persist/ReusingModelRegistryStore.java         |   60 +
 .../model/internal/rules/ReflectiveRule.java       |  207 -
 .../groovy/org/gradle/plugin/PluginHandler.java    |   31 -
 .../plugin/internal/DefaultPluginHandler.java      |   48 -
 .../plugin/internal/InvalidPluginIdException.java  |   33 +
 .../internal/NonPluggableTargetPluginHandler.java  |   38 -
 .../org/gradle/plugin/internal/PluginId.java       |  124 +
 .../plugin/internal/PluginRequestApplicator.java   |   50 -
 .../internal/PluginResolutionApplicator.java       |   39 -
 .../plugin/internal/PluginResolverFactory.java     |   95 -
 .../gradle/plugin/internal/PluginResolvers.java    |   59 -
 .../internal/ClassPathPluginResolution.java        |   46 -
 .../resolve/internal/CompositePluginResolver.java  |   48 -
 .../resolve/internal/DefaultPluginRequest.java     |   78 -
 .../DependencyResolvingClasspathProvider.java      |   46 -
 .../internal/InvalidPluginRequestException.java    |   25 -
 .../resolve/internal/JCenterPluginMapper.java      |  113 -
 .../internal/JCenterRepositoryConfigurer.java      |   26 -
 .../internal/ModuleMappingPluginResolver.java      |   68 -
 .../NotInPluginRegistryPluginResolverCheck.java    |   61 -
 .../internal/PluginRegistryPluginResolver.java     |   56 -
 .../plugin/resolve/internal/PluginRequest.java     |   33 -
 .../resolve/internal/PluginRequestSerializer.java  |   34 -
 .../plugin/resolve/internal/PluginResolution.java  |   31 -
 .../plugin/resolve/internal/PluginResolver.java    |   33 -
 .../resolve/internal/SimplePluginResolution.java   |   34 -
 .../gradle/plugin/use/PluginDependenciesSpec.java  |  110 +
 .../gradle/plugin/use/PluginDependencySpec.java    |   52 +
 .../plugin/use/internal/DefaultPluginRequest.java  |  106 +
 .../plugin/use/internal/DefaultPluginRequests.java |   44 +
 .../internal/InvalidPluginRequestException.java    |   32 +
 .../gradle/plugin/use/internal/PluginRequest.java  |   34 +
 .../use/internal/PluginRequestApplicator.java      |   26 +
 .../use/internal/PluginRequestCollector.java       |  101 +
 .../gradle/plugin/use/internal/PluginRequests.java |   24 +
 .../use/internal/PluginRequestsSerializer.java     |   56 +
 .../PluginUseScriptBlockMetadataExtractor.java     |  164 +
 .../groovy/org/gradle/plugin/use/package-info.java |   20 +
 .../process/internal/DefaultExecActionFactory.java |   32 +
 .../gradle/process/internal/DefaultExecHandle.java |    4 +-
 .../internal/DefaultWorkerProcessFactory.java      |   21 +-
 .../gradle/process/internal/ExecActionFactory.java |    4 +
 .../org/gradle/process/internal/JvmOptions.java    |   20 +-
 .../process/internal/WorkerProcessBuilder.java     |    9 +
 .../internal/child/ActionExecutionWorker.java      |    9 +-
 ...nClassesInIsolatedClassLoaderWorkerFactory.java |   39 +-
 ...ionClassesInSystemClassLoaderWorkerFactory.java |   79 +-
 .../internal/child/BootstrapSecurityManager.java   |   11 +-
 .../child/ImplementationClassLoaderWorker.java     |   12 +-
 .../child/SystemApplicationClassLoaderWorker.java  |   22 +-
 .../process/internal/child/WorkerFactory.java      |    7 -
 .../child/WorkerProcessClassPathProvider.java      |   24 +-
 .../launcher/BootstrapClassLoaderWorker.java       |   48 -
 .../internal/launcher/GradleWorkerMain.java        |   47 +-
 .../launcher/IsolatedGradleWorkerMain.java         |   59 +
 .../groovy/org/gradle/profile/BuildProfile.java    |   22 +-
 .../org/gradle/profile/ProfileEventAdapter.java    |   12 +-
 .../org/gradle/profile/ProfileReportRenderer.java  |   19 +-
 .../org/gradle/reporting/CodePanelRenderer.java    |    2 +-
 .../org/gradle/reporting/HtmlPageBuilder.java      |   33 +
 .../org/gradle/reporting/HtmlReportBuilder.java    |   30 +
 .../org/gradle/reporting/HtmlReportRenderer.java   |  168 +-
 .../org/gradle/reporting/TabbedPageRenderer.java   |   58 +-
 .../groovy/org/gradle/reporting/TabsRenderer.java  |    2 +-
 .../org/gradle/reporting/TextReportRenderer.java   |   41 -
 .../internal/InMemoryCacheFactory.java             |   44 +-
 .../internal/InMemoryIndexedCache.java             |    6 +-
 .../testfixtures/internal/NoOpLoggingManager.java  |   19 +-
 .../testfixtures/internal/ProjectBuilderImpl.java  |   40 +-
 .../internal/TestBuildScopeServices.java           |    6 +
 .../internal/TestGlobalScopeServices.java          |    4 +-
 .../src/main/groovy/org/gradle/util/Clock.java     |    2 +-
 .../main/groovy/org/gradle/util/GFileUtils.java    |   23 +-
 .../main/groovy/org/gradle/util/GradleVersion.java |  334 --
 .../core/src/main/groovy/org/gradle/util/Jvm.java  |   93 -
 .../org/gradle/util/LineBufferingOutputStream.java |    2 +-
 .../org/gradle/util/SingleMessageLogger.java       |   30 +-
 .../src/main/groovy/org/gradle/util/TextUtil.java  |  118 -
 .../main/groovy/org/gradle/util/VersionNumber.java |  231 +-
 .../src/main/groovy/org/gradle/util/WrapUtil.java  |   13 +
 .../groovy/org/slf4j/impl/StaticLoggerBinder.java  |   51 +
 .../resources/org/gradle/reporting/base-style.css  |   19 +-
 .../main/resources/org/gradle/reporting/report.js  |  275 +-
 .../org/gradle/BuildExceptionReporterTest.groovy   |   25 +-
 .../groovy/org/gradle/StartParameterTest.groovy    |   70 +-
 .../api/file/MicroBenchmarkPerformanceTest.groovy  |   48 +
 .../AbstractClassGeneratorTestGroovy.groovy        |   13 +
 .../AbstractNamedDomainObjectContainerTest.groovy  |   31 +-
 .../gradle/api/internal/AbstractTaskSpec.groovy    |   67 -
 .../gradle/api/internal/AbstractTaskTest.groovy    |   84 +
 .../AsmBackedClassGeneratorGroovyTest.groovy       |  174 +-
 .../api/internal/AsmBackedClassGeneratorTest.java  |  263 +-
 .../api/internal/ClosureBackedActionTest.groovy    |    2 -
 .../internal/CompositeDomainObjectSetTest.groovy   |    7 +-
 .../DefaultNamedDomainObjectListTest.groovy        |    2 +-
 .../internal/DefaultNamedDomainObjectSetTest.java  |   33 +-
 ...PolymorphicDomainObjectContainerBaseTest.groovy |   11 +
 ...aultPolymorphicDomainObjectContainerTest.groovy |   24 +-
 .../org/gradle/api/internal/DefaultTaskTest.groovy |    8 +-
 .../DependencyClassPathProviderTest.groovy         |    6 +-
 .../DependencyInjectingInstantiatorTest.groovy     |  119 +-
 .../api/internal/DocumentationRegistryTest.groovy  |   10 +
 .../api/internal/ExtensibleDynamicObjectTest.java  |   57 +-
 .../ExtensibleDynamicObjectTestHelper.groovy       |   12 +-
 ...AutoCreateNamedDomainObjectContainerSpec.groovy |    6 +-
 .../gradle/api/internal/PackageScopedClass.java    |   20 +
 .../org/gradle/api/internal/TestJavaObject.java    |   23 +
 .../TypedDomainObjectContainerWrapperTest.groovy   |  157 +
 .../DefaultArtifactRepositoryContainerTest.groovy  |  158 +-
 .../ExcludeRuleNotationConverterTest.groovy        |   83 +
 .../artifacts/ExcludeRuleNotationParserTest.groovy |   82 -
 .../AbstractModuleDependencySpec.groovy            |   40 +-
 .../dependencies/AbstractModuleDependencyTest.java |  110 -
 .../dependencies/ClientModuleDependencySpec.groovy |   41 +
 .../dependencies/DefaultClientModuleTest.java      |   94 -
 .../DefaultExternalModuleDependencySpec.groovy     |   26 +
 .../DefaultExternalModuleDependencyTest.java       |   99 -
 .../dsl/DefaultRepositoryHandlerTest.groovy        |   96 +-
 .../DefaultDependencyHandlerTest.groovy            |    4 +-
 .../publish/AbstractPublishArtifactTest.java       |    9 -
 .../publish/DefaultPublishArtifactTest.java        |    4 -
 ...meAfterContainerInclusionDeprecationTest.groovy |   67 -
 .../DefaultTaskArtifactStateRepositoryTest.groovy  |    9 +-
 ...rtCircuitTaskArtifactStateRepositoryTest.groovy |    2 +-
 .../rules/InputFilesStateChangeRuleTest.groovy     |   17 +-
 .../rules/TaskUpToDateStateTest.groovy             |   99 +
 .../CacheBackedFileSnapshotRepositoryTest.groovy   |    2 +-
 .../DefaultFileCollectionSnapshotterTest.groovy    |    2 +-
 .../DefaultFileSnapshotterSerializerTest.groovy    |    2 +-
 .../state/InputPropertiesSerializerTest.groovy     |   74 +
 .../state/OutputFilesSnapshotSerializerTest.groovy |    4 +-
 .../classpath/DefaultModuleRegistryTest.groovy     |   15 +
 .../coerce/StringToEnumTransformerTest.groovy      |   68 +
 ...peCoercingMethodArgumentsTransformerTest.groovy |   68 -
 .../DefaultComponentTypeRegistryTest.groovy        |   83 +
 .../internal/file/AbstractFileCollectionTest.java  |    7 +
 .../internal/file/AbstractFileTreeElementTest.java |    4 +-
 .../internal/file/BaseDirFileResolverSpec.groovy   |   25 +-
 .../internal/file/BaseDirFileResolverTest.groovy   |    5 +-
 .../api/internal/file/BasicFileResolverTest.groovy |   58 +
 .../internal/file/CompositeFileCollectionTest.java |    7 +
 .../api/internal/file/CompositeFileTreeTest.java   |    8 +
 .../internal/file/DefaultFileOperationsTest.groovy |   63 +-
 .../file/DefaultFileTreeElementTest.groovy         |    4 +-
 .../file/DefaultSourceDirectorySetTest.groovy      |    2 +
 .../file/FileOrUriNotationConverterTest.groovy     |  117 +
 .../file/FileOrUriNotationParserTest.groovy        |  107 -
 .../api/internal/file/RelativeFileTest.groovy      |   43 +
 .../api/internal/file/UnionFileCollectionTest.java |    7 +
 .../api/internal/file/UnionFileTreeTest.java       |    7 +
 .../api/internal/file/archive/TarFileTreeTest.java |   16 +-
 .../api/internal/file/archive/ZipFileTreeTest.java |   11 +-
 ...ciesOnlyFileCollectionResolveContextTest.groovy |    2 +
 .../DefaultConfigurableFileCollectionTest.java     |   11 +-
 .../DefaultConfigurableFileTreeTest.groovy         |    2 +
 .../DefaultFileCollectionResolveContextTest.groovy |    3 +
 .../file/collections/DirectoryFileTreeTest.java    |    2 +
 .../file/collections/FileTreeAdapterTest.groovy    |    2 +
 .../internal/file/collections/MapFileTreeTest.java |   32 +-
 .../SingleIncludePatternFileTreeSpec.groovy        |    2 +
 .../file/collections/SingletonFileTreeTest.groovy  |    2 +
 .../file/copy/CopyActionExecuterTest.groovy        |    4 +-
 .../file/copy/CopyFileVisitorImplTest.groovy       |   24 +-
 .../file/copy/CopySpecActionImplTest.groovy        |    8 +-
 ...SpecBackedCopyActionProcessingStreamTest.groovy |    2 +-
 .../internal/file/copy/CopySpecMatchingTest.groovy |   21 +-
 .../file/copy/DefaultCopySpecResolutionTest.groovy |  323 ++
 .../internal/file/copy/DefaultCopySpecTest.groovy  |  314 +-
 ...DuplicateHandlingCopyActionDecoratorTest.groovy |  196 -
 .../DuplicateHandlingCopyActionExecutorTest.groovy |  197 +
 .../api/internal/file/copy/LineFilterTest.groovy   |    2 +-
 .../file/copy/PathNotationConverterTest.groovy     |  108 +
 .../file/copy/PathNotationParserTest.groovy        |   90 -
 .../file/copy/SyncCopyActionDecoratorTest.groovy   |    2 +-
 .../internal/filestore/PathKeyFileStoreTest.groovy |  198 -
 .../PathNormalisingKeyFileStoreTest.groovy         |   94 -
 .../filestore/UniquePathKeyFileStoreTest.groovy    |  112 -
 .../api/internal/html/SimpleHtmlWriterTest.groovy  |   40 -
 .../initialization/ClassLoaderIdsTest.groovy       |   39 +
 .../ClassLoaderScopeIdentifierTest.groovy          |   60 +
 .../DefaultClassLoaderCacheTest.groovy             |   80 -
 .../DefaultClassLoaderScopeTest.groovy             |  315 +-
 .../DefaultScriptHandlerFactoryTest.groovy         |   10 +-
 .../initialization/DefaultScriptHandlerTest.groovy |    2 +-
 .../api/internal/initialization/TestClass1.java    |   20 +
 .../api/internal/initialization/TestClass2.java    |   20 +
 .../loadercache/DefaultClassLoaderCacheTest.groovy |  168 +
 .../loadercache/DummyClassLoaderCache.java         |   39 +
 .../HashClassPathSnapshotterTest.groovy            |  115 +
 .../DefaultObjectConfigurationActionTest.groovy    |   13 +-
 .../plugins/DefaultPluginContainerTest.groovy      |  290 ++
 .../plugins/DefaultPluginContainerTest.java        |  128 -
 .../plugins/DefaultPluginManagerTest.groovy        |  581 +++
 .../plugins/DefaultPluginRegistryTest.groovy       |  302 +-
 .../ExtraPropertiesDynamicObjectAdapterTest.groovy |   15 +-
 .../plugins/RuleSourceApplicationTest.groovy       |  103 +
 .../internal/project/DefaultAntBuilderTest.groovy  |   45 +-
 .../project/DefaultIsolatedAntBuilderTest.groovy   |  106 +-
 .../api/internal/project/DefaultProjectTest.groovy |  364 +-
 .../DeferredProjectConfigurationTest.groovy        |   89 +
 .../internal/project/NewDefaultProjectTest.groovy  |  101 +-
 .../api/internal/project/ProjectFactoryTest.groovy |   20 +-
 .../gradle/api/internal/project/TestAntTask.java   |   28 +
 .../gradle/api/internal/project/TestPlugin2.groovy |   25 -
 .../api/internal/project/TestRuleSource.groovy     |   22 +
 .../AnnotationProcessingTaskFactoryTest.java       |   67 +-
 .../project/taskfactory/TaskFactoryTest.groovy     |   14 +-
 .../internal/resource/CachingResourceTest.groovy   |   65 -
 .../internal/resource/StringResourceTest.groovy    |   47 -
 .../api/internal/resource/UriResourceTest.groovy   |  183 -
 .../resources/AbstractTextResourceTest.groovy      |   47 +
 .../CharSourceBackedTextResourceTest.groovy        |   32 +
 ...llectionBackedTarArchiveTextResourceTest.groovy |   35 +
 .../FileCollectionBackedTextResourceTest.groovy    |   30 +
 ...llectionBackedZipArchiveTextResourceTest.groovy |   36 +
 .../resources/StringBackedTextResourceTest.groovy  |   27 +
 .../internal/tasks/DefaultTaskContainerTest.groovy |   96 +-
 .../tasks/DefaultTaskDependencyTest.groovy         |   47 +-
 .../internal/tasks/DefaultTaskInputsTest.groovy    |   72 +-
 .../internal/tasks/DefaultTaskOutputsTest.groovy   |   28 +-
 .../tasks/PublicTaskSpecificationTest.groovy       |   62 +
 .../api/internal/tasks/TaskMutatorTest.groovy      |   77 +
 .../options/OptionNotationParserFactorySpec.groovy |    1 -
 .../tasks/util/DefaultJavaForkOptionsTest.groovy   |   34 +-
 .../api/internal/xml/SimpleXmlWriterSpec.groovy    |  414 --
 .../api/internal/xml/XmlTransformerTest.groovy     |  358 --
 .../groovy/org/gradle/api/logging/LoggingTest.java |   66 +-
 .../gradle/api/tasks/AbstractCopyTaskTest.groovy   |   28 +-
 .../org/gradle/api/tasks/DirectoryTest.groovy      |   78 -
 .../org/gradle/api/tasks/GradleBuildTest.groovy    |  113 +-
 .../org/gradle/api/tasks/ant/AntTargetTest.java    |   16 -
 .../gradle/api/tasks/util/PatternSetTest.groovy    |  188 +-
 .../cache/internal/DefaultCacheAccessTest.groovy   |    2 +-
 .../cache/internal/DefaultCacheFactoryTest.groovy  |   62 +-
 .../internal/DefaultCacheRepositoryTest.groovy     |   21 +-
 .../DefaultPersistentDirectoryCacheSpec.groovy     |    3 +-
 .../DefaultPersistentDirectoryCacheTest.java       |   30 +-
 ...tPersistentDirectoryStoreConcurrencyTest.groovy |   58 +
 .../DefaultProcessMetaDataProviderTest.groovy      |    2 +-
 .../cache/internal/SimpleStateCacheTest.groovy     |    4 +-
 .../btree/BTreePersistentIndexedCacheTest.java     |    4 +-
 .../DefaultBuildConfigurerTest.groovy              |   17 +-
 .../configuration/DefaultImportsReaderTest.groovy  |   32 +
 .../DefaultInitScriptProcessorTest.groovy          |   20 +-
 .../DefaultScriptPluginFactoryTest.groovy          |   60 +-
 .../gradle/configuration/ImportsReaderTest.groovy  |   44 -
 .../configuration/ImportsScriptSourceTest.java     |  101 -
 .../project/BuildScriptProcessorTest.groovy        |    8 +-
 .../project/LifecycleProjectEvaluatorTest.groovy   |   11 +-
 .../PluginsProjectConfigureActionsTest.groovy      |    9 +-
 .../ProjectDependencies2TaskResolverTest.groovy    |   37 -
 .../DefaultTasksBuildExecutionActionTest.groovy    |   22 +-
 ...askFilteringBuildConfigurationActionTest.groovy |   15 +-
 .../execution/ProjectEvaluatingActionTest.groovy   |   66 -
 .../gradle/execution/TaskNameResolverTest.groovy   |  333 +-
 ...ameResolvingBuildConfigurationActionSpec.groovy |   90 +
 ...kNameResolvingBuildConfigurationActionTest.java |  423 --
 .../execution/TaskPathProjectEvaluatorTest.groovy  |   49 +-
 .../org/gradle/execution/TaskSelectorTest.groovy   |  145 +
 .../CommandLineTaskConfigurerSpec.groovy           |    7 +-
 .../commandline/CommandLineTaskParserSpec.groovy   |   99 +-
 ...askExecutionPlanParallelTaskHandlingTest.groovy |  349 ++
 .../taskgraph/DefaultTaskExecutionPlanTest.groovy  |  109 +-
 .../taskgraph/DefaultTaskGraphExecuterSpec.groovy  |  104 +
 .../taskgraph/DefaultTaskGraphExecuterTest.java    |   46 +-
 .../groovy/scripts/CachingScriptSourceTest.java    |    4 +-
 .../DefaultScriptCompilerFactoryTest.groovy        |   37 +-
 .../gradle/groovy/scripts/DefaultScriptTest.groovy |    7 +-
 .../groovy/scripts/StringScriptSourceTest.java     |    2 +-
 .../gradle/groovy/scripts/UriScriptSourceTest.java |    2 +-
 .../internal/BuildScriptTransformerSpec.groovy     |   96 +
 .../internal/CachingScriptClassCompilerTest.groovy |   70 +-
 .../DefaultScriptCompilationHandlerTest.java       |  123 +-
 .../internal/DefaultScriptRunnerFactoryTest.java   |   22 +-
 .../FileCacheBackedScriptClassCompilerTest.groovy  |   37 +-
 .../ShortCircuitEmptyScriptCompilerTest.groovy     |   36 +-
 .../BuildLayoutParametersTest.groovy               |    6 +-
 .../DefaultBuildCancellationTokenSpec.groovy       |  145 +
 .../DefaultCommandLineConverterTest.groovy         |  445 ++
 .../DefaultCommandLineConverterTest.java           |  413 --
 .../DefaultExceptionAnalyserTest.java              |   18 +-
 .../DefaultGradleLauncherFactoryTest.groovy        |   82 +-
 .../initialization/DefaultGradleLauncherTest.java  |   84 +-
 .../initialization/DefaultSettingsTest.groovy      |   49 +-
 .../InstantiatingBuildLoaderTest.groovy            |   36 +-
 .../LayoutCommandLineConverterTest.groovy          |   13 +-
 .../initialization/ProjectPathProjectSpecTest.java |  117 -
 .../ProjectPropertySettingBuildLoaderTest.groovy   |   29 +-
 .../gradle/initialization/ProjectSpecsTest.groovy  |   19 +-
 .../PropertiesLoadingSettingsProcessorTest.java    |   64 -
 .../initialization/SettingsFactoryTest.groovy      |    7 +-
 .../initialization/SettingsHandlerTest.groovy      |    4 +-
 .../buildsrc/BuildSourceBuilderTest.groovy         |    3 +
 .../BuildSrcBuildListenerFactoryTest.groovy        |   23 +-
 .../buildsrc/BuildSrcUpdateFactoryTest.groovy      |    2 +-
 .../layout/BuildLayoutFactoryTest.groovy           |    9 +-
 .../AbstractMultiCauseExceptionTest.groovy         |  125 -
 .../exceptions/LocationAwareExceptionTest.groovy   |    4 +-
 .../LoggingDeprecatedFeatureHandlerTest.groovy     |   10 +-
 .../internal/html/SimpleHtmlWriterTest.groovy      |   40 +
 .../DefaultBuildOperationLoggerFactoryTest.groovy  |   55 +
 .../logging/DefaultBuildOperationLoggerTest.groovy |  153 +
 .../resource/local/PathKeyFileStoreTest.groovy     |  199 +
 .../local/PathNormalisingKeyFileStoreTest.groovy   |   96 +
 .../local/UniquePathKeyFileStoreTest.groovy        |  114 +
 .../service/scopes/BuildScopeServicesTest.groovy   |   37 +-
 .../service/scopes/GlobalScopeServicesTest.java    |   81 +-
 .../service/scopes/GradleScopeServicesTest.groovy  |   51 +-
 .../service/scopes/ProjectScopeServicesTest.groovy |   42 +-
 .../scopes/SettingsScopeServicesTest.groovy        |   29 +-
 .../scopes/TaskExecutionServicesTest.groovy        |   15 +-
 .../service/scopes/TaskScopeServicesTest.java      |    6 +-
 .../CharSequenceNotationConverterTest.groovy       |   49 +
 .../CharSequenceNotationParserTest.groovy          |   47 -
 .../ClosureToSpecNotationConverterTest.groovy      |   38 +
 .../ClosureToSpecNotationParserTest.groovy         |   36 -
 .../EnumFromCharSequenceNotationParserSpec.groovy  |    1 +
 .../ErrorHandlingNotationParserTest.groovy         |   18 +-
 .../typeconversion/MapNotationConverterTest.groovy |  111 +
 .../typeconversion/MapNotationParserTest.groovy    |  109 -
 ...tionConverterToNotationParserAdapterTest.groovy |   51 +
 .../NotationParserBuilderSpec.groovy               |  101 +-
 .../TypeFilteringNotationConverterTest.groovy      |   41 +
 .../TypedNotationConverterTest.groovy              |   49 +
 .../typeconversion/TypedNotationParserTest.groovy  |   49 -
 .../gradle/internal/xml/SimpleXmlWriterSpec.groovy |  413 ++
 .../gradle/internal/xml/XmlTransformerTest.groovy  |  359 ++
 .../gradle/internal/xml/XmlValidationTest.groovy   |  111 +
 .../org/gradle/invocation/DefaultGradleTest.java   |   39 +-
 .../listener/DefaultListenerManagerTest.java       |  225 -
 .../gradle/listener/LazyCreationProxyTest.groovy   |   90 -
 .../org/gradle/listener/ListenerBroadcastTest.java |  268 -
 .../org/gradle/logging/ConfigureLogging.groovy     |   64 -
 .../logging/LoggingServiceRegistryTest.groovy      |   83 +-
 .../groovy/org/gradle/logging/TestAppender.groovy  |   40 -
 ...AbstractLineChoppingStyledTextOutputTest.groovy |    2 +-
 .../internal/AbstractStyledTextOutputTest.groovy   |    2 +-
 .../gradle/logging/internal/AnsiConsoleTest.groovy |    2 +-
 .../ConsoleBackedProgressRendererTest.groovy       |    2 +-
 .../DefaultStandardOutputRedirectorTest.groovy     |   28 +-
 .../internal/DefaultStatusBarFormatterTest.groovy  |    2 +-
 .../internal/JavaUtilLoggingConfigurerTest.groovy  |    8 +-
 .../LoggingCommandLineConverterTest.groovy         |   29 +-
 .../internal/OutputEventRendererTest.groovy        |   18 +-
 .../logback/LogbackLoggingConfigurerTest.groovy    |  249 -
 ...tputEventListenerBackedLoggerContextTest.groovy |   64 +
 ...enerBackedLoggerDefaultConfigurationTest.groovy |   99 +
 .../OutputEventListenerBackedLoggerTest.groovy     |  907 ++++
 .../slf4j/Slf4jLoggingConfigurerTest.groovy        |  178 +
 .../model/dsl/internal/GroovyModelDslTest.groovy   |  101 -
 .../ModelRegistryBackedModelRulesTest.groovy       |  102 -
 .../plugin/bintray/JCenterPluginMapperSpec.groovy  |   80 -
 .../internal/DefaultPluginHandlerTest.groovy       |   62 -
 .../org/gradle/plugin/internal/PluginIdTest.groovy |   76 +
 .../use/internal/PluginRequestCollectorTest.groovy |   79 +
 .../internal/PluginRequestsSerializerTest.groovy   |   48 +
 .../process/internal/DefaultExecHandleSpec.groovy  |   23 +-
 .../process/internal/ExecHandleBuilderTest.groovy  |    2 +
 .../internal/JavaExecHandleBuilderTest.groovy      |   14 +-
 .../gradle/process/internal/JvmOptionsTest.groovy  |   27 +-
 .../org/gradle/process/internal/TestApp.java       |   26 +
 .../internal/child/ActionExecutionWorkerTest.java  |    7 +-
 .../child/BootstrapSecurityManagerTest.groovy      |    6 +-
 .../child/ImplementationClassLoaderWorkerTest.java |    5 +-
 .../profile/ProfileReportRendererTest.groovy       |    5 +-
 .../gradle/reporting/HtmlReportRendererTest.groovy |  135 +-
 .../org/gradle/reporting/TabsRendererTest.groovy   |    2 +-
 .../gradle/reporting/TextReportRendererTest.groovy |   41 -
 .../gradle/testfixtures/ProjectBuilderTest.groovy  |   85 +-
 .../groovy/org/gradle/util/GFileUtilsTest.groovy   |   13 +-
 .../org/gradle/util/GradleVersionTest.groovy       |  276 --
 .../gradle/util/LineBufferingOutputStreamTest.java |    2 +-
 .../org/gradle/util/SingleMessageLoggerTest.groovy |   22 +-
 .../groovy/org/gradle/util/TextUtilTest.groovy     |    9 +
 .../org/gradle/util/VersionNumberTest.groovy       |   83 +-
 ...perties => org.gradle.custom-plugin.properties} |    0
 .../org.gradle.custom-rule-source.properties       |    1 +
 .../resources/org/gradle/api/file/symlinks/symlink |    0
 .../org/gradle/api/internal/file/TestFiles.java    |    6 +-
 .../gradle/api/tasks/AbstractSpockTaskTest.groovy  |   18 +-
 .../org/gradle/api/tasks/AbstractTaskTest.java     |   20 +-
 .../org/gradle/logging/ConfigureLogging.groovy     |   63 +
 .../gradle/logging/TestOutputEventListener.groovy  |   44 +
 .../org/gradle/logging/TestStyledTextOutput.groovy |    2 +-
 .../gradle/test/fixtures/ConcurrentTestUtil.groovy |   40 +-
 .../groovy/org/gradle/util/TestUtil.groovy         |    2 +-
 subprojects/cpp/cpp.gradle                         |   30 -
 ...alStudioFileCustomizationIntegrationTest.groovy |  207 -
 .../VisualStudioMultiProjectIntegrationTest.groovy |  316 --
 ...VisualStudioSingleProjectIntegrationTest.groovy |  840 ----
 .../VisualStudioPluginIntegrationTest.groovy       |   26 -
 .../CppAutoTestedSamplesIntegrationTest.groovy     |   31 -
 .../plugins/AssemblerPluginIntegrationTest.groovy  |   22 -
 .../c/plugins/CPluginIntegrationTest.groovy        |   22 -
 ...tLanguageIncrementalBuildIntegrationTest.groovy |  513 --
 ...anguageIncrementalCompileIntegrationTest.groovy |  529 --
 .../cpp/AbstractLanguageIntegrationTest.groovy     |  198 -
 ...yLanguageIncrementalBuildIntegrationTest.groovy |  136 -
 .../cpp/AssemblyLanguageIntegrationTest.groovy     |  110 -
 .../cpp/BinaryBuildTypesIntegrationTest.groovy     |  206 -
 .../cpp/BinaryFlavorsIntegrationTest.groovy        |  227 -
 .../cpp/BinaryPlatformIntegrationTest.groovy       |  349 --
 ...llingMixedCAndCppLanguageIntegrationTest.groovy |   25 -
 ...CLanguageIncrementalBuildIntegrationTest.groovy |   27 -
 ...anguageIncrementalCompileIntegrationTest.groovy |   26 -
 .../language/cpp/CLanguageIntegrationTest.groovy   |  201 -
 .../language/cpp/CUnitIntegrationTest.groovy       |  387 --
 .../language/cpp/CppBinariesIntegrationTest.groovy |  371 --
 .../cpp/CppCallingCLanguageIntegrationTest.groovy  |   25 -
 ...pLanguageIncrementalBuildIntegrationTest.groovy |   25 -
 ...anguageIncrementalCompileIntegrationTest.groovy |   26 -
 .../language/cpp/CppLanguageIntegrationTest.groovy |  114 -
 .../language/cpp/CppPluginGoodBehaviourTest.groovy |   25 -
 .../cpp/DuplicateBaseNamesIntegrationTest.groovy   |  118 -
 ...GccToolChainCustomisationIntegrationTest.groovy |  193 -
 .../GccToolChainDiscoveryIntegrationTest.groovy    |  124 -
 .../cpp/GeneratedSourcesIntegrationTest.groovy     |  383 --
 .../LibraryApiDependenciesIntegrationTest.groovy   |  237 -
 .../cpp/LibraryBinariesIntegrationTest.groovy      |  298 --
 .../cpp/LibraryDependenciesIntegrationTest.groovy  |  350 --
 .../cpp/MixedLanguageIntegrationTest.groovy        |  168 -
 .../cpp/MultipleToolChainIntegrationTest.groovy    |  115 -
 .../cpp/NativeBinariesPluginIntegrationTest.groovy |  226 -
 .../cpp/NativeSamplesIntegrationTest.groovy        |  409 --
 .../cpp/PrebuiltLibrariesIntegrationTest.groovy    |  349 --
 .../cpp/SharedLibrarySoNameIntegrationTest.groovy  |   80 -
 .../SourceSetDependenciesIntegrationTest.groovy    |  153 -
 .../cpp/ToolChainDiscoveryIntegrationTest.groovy   |   89 -
 ...ResourcesIncrementalBuildIntegrationTest.groovy |  162 -
 .../cpp/WindowsResourcesIntegrationTest.groovy     |  143 -
 ...ndowsResourcesUnsupportedIntegrationTest.groovy |   55 -
 ...bstractInstalledToolChainIntegrationSpec.groovy |   81 -
 .../cpp/fixtures/SingleToolChainTestRunner.java    |   77 -
 .../cpp/plugins/CppPluginIntegrationTest.groovy    |   22 -
 .../MixedObjectiveCIntegrationTest.groovy          |   33 -
 ...CLanguageIncrementalBuildIntegrationTest.groovy |   86 -
 ...anguageIncrementalCompileIntegrationTest.groovy |  120 -
 .../ObjectiveCLanguageIntegrationTest.groovy       |   32 -
 .../ObjectiveCUnsupportedIntegrationTest.groovy    |   53 -
 .../plugins/ObjectiveCPluginIntegrationTest.groovy |   26 -
 ...pLanguageIncrementalBuildIntegrationTest.groovy |   31 -
 ...anguageIncrementalCompileIntegrationTest.groovy |   31 -
 .../ObjectiveCppLanguageIntegrationTest.groovy     |   32 -
 .../ObjectiveCppUnsupportedIntegrationTest.groovy  |   54 -
 .../ObjectiveCppPluginIntegrationTest.groovy       |   26 -
 .../WindowsResourcesPluginIntegrationTest.groovy   |   26 -
 .../NativeBinariesPluginIntegrationTest.groovy     |   26 -
 .../plugins/CUnitPluginIntegrationTest.groovy      |   22 -
 .../shared/libs/cunit/2.1-2/lib/linux/libcunit.a   |  Bin 117622 -> 0 bytes
 .../shared/libs/cunit/2.1-2/lib/osx/libcunit.a     |  Bin 85768 -> 0 bytes
 .../groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy  |   79 -
 .../gradle/ide/cdt/model/CprojectDescriptor.groovy |  117 -
 .../gradle/ide/cdt/model/CprojectSettings.groovy   |  103 -
 .../gradle/ide/cdt/model/ProjectDescriptor.groovy  |   48 -
 .../ide/visualstudio/VisualStudioExtension.java    |   36 -
 .../ide/visualstudio/VisualStudioProject.java      |   62 -
 .../ide/visualstudio/VisualStudioSolution.java     |   64 -
 .../internal/DefaultVisualStudioExtension.java     |   52 -
 .../internal/DefaultVisualStudioProject.groovy     |  154 -
 .../internal/DefaultVisualStudioSolution.groovy    |  114 -
 ...ecutableVisualStudioProjectConfiguration.groovy |   46 -
 .../VisualStudioProjectConfiguration.groovy        |  109 -
 .../internal/VisualStudioProjectMapper.java        |   90 -
 .../internal/VisualStudioProjectRegistry.java      |   65 -
 .../internal/VisualStudioProjectResolver.java      |   44 -
 .../internal/rules/CreateVisualStudioModel.java    |   39 -
 .../internal/rules/CreateVisualStudioTasks.java    |   90 -
 .../visualstudio/plugins/VisualStudioPlugin.groovy |   73 -
 .../tasks/internal/VisualStudioFiltersFile.groovy  |   58 -
 .../tasks/internal/VisualStudioProjectFile.groovy  |   98 -
 .../org/gradle/language/DependentSourceSet.java    |   57 -
 .../gradle/language/HeaderExportingSourceSet.java  |   45 -
 .../language/assembler/AssemblerSourceSet.java     |   42 -
 .../internal/DefaultAssemblerSourceSet.java        |   29 -
 .../assembler/plugins/AssemblerLangPlugin.groovy   |   62 -
 .../groovy/org/gradle/language/c/CSourceSet.java   |   49 -
 .../language/c/internal/DefaultCSourceSet.java     |   32 -
 .../gradle/language/c/plugins/CLangPlugin.groovy   |   63 -
 .../org/gradle/language/cpp/CppSourceSet.java      |   49 -
 .../language/cpp/internal/DefaultCppSourceSet.java |   29 -
 .../language/cpp/plugins/CppLangPlugin.groovy      |   62 -
 .../AbstractHeaderExportingDependentSourceSet.java |   64 -
 .../internal/AbstractHeaderExportingSourceSet.java |   53 -
 .../ConfigurationBasedNativeDependencySet.groovy   |   90 -
 .../language/objectivec/ObjectiveCSourceSet.java   |   52 -
 .../internal/DefaultObjectiveCSourceSet.java       |   32 -
 .../objectivec/plugins/ObjectiveCLangPlugin.groovy |   62 -
 .../objectivecpp/ObjectiveCppSourceSet.java        |   51 -
 .../internal/DefaultObjectiveCppSourceSet.java     |   32 -
 .../plugins/ObjectiveCppLangPlugin.groovy          |   62 -
 .../org/gradle/language/rc/WindowsResourceSet.java |   48 -
 .../rc/internal/DefaultWindowsResourceSet.java     |   28 -
 .../rc/plugins/WindowsResourceScriptPlugin.groovy  |   63 -
 .../org/gradle/nativebinaries/BuildType.java       |   30 -
 .../gradle/nativebinaries/BuildTypeContainer.java  |   27 -
 .../org/gradle/nativebinaries/Executable.java      |   26 -
 .../gradle/nativebinaries/ExecutableBinary.java    |   36 -
 .../gradle/nativebinaries/ExecutableContainer.java |   27 -
 .../groovy/org/gradle/nativebinaries/Flavor.java   |   31 -
 .../org/gradle/nativebinaries/FlavorContainer.java |   30 -
 .../groovy/org/gradle/nativebinaries/Library.java  |   39 -
 .../org/gradle/nativebinaries/LibraryBinary.java   |   26 -
 .../gradle/nativebinaries/LibraryContainer.java    |   27 -
 .../org/gradle/nativebinaries/NativeBinary.java    |   42 -
 .../gradle/nativebinaries/NativeBinaryTasks.java   |   43 -
 .../gradle/nativebinaries/NativeDependencySet.java |   40 -
 .../nativebinaries/NativeLibraryRequirement.java   |   40 -
 .../gradle/nativebinaries/PrebuiltLibraries.java   |   29 -
 .../org/gradle/nativebinaries/PrebuiltLibrary.java |   38 -
 .../gradle/nativebinaries/ProjectNativeBinary.java |  106 -
 .../nativebinaries/ProjectNativeComponent.java     |   66 -
 .../org/gradle/nativebinaries/Repositories.java    |   28 -
 .../gradle/nativebinaries/SharedLibraryBinary.java |   47 -
 .../gradle/nativebinaries/StaticLibraryBinary.java |   40 -
 .../nativebinaries/TargetedNativeComponent.java    |   42 -
 .../groovy/org/gradle/nativebinaries/Tool.java     |   39 -
 .../internal/AbstractBinaryToolSpec.java           |   59 -
 .../internal/AbstractProjectLibraryBinary.java     |  104 -
 .../internal/AbstractProjectNativeBinary.java      |  161 -
 .../internal/AbstractProjectNativeComponent.java   |   73 -
 .../AbstractTargetedProjectNativeComponent.java    |   82 -
 .../nativebinaries/internal/BinaryToolSpec.java    |   39 -
 .../nativebinaries/internal/DefaultBuildType.java  |   39 -
 .../internal/DefaultBuildTypeContainer.java        |   33 -
 .../nativebinaries/internal/DefaultExecutable.java |   28 -
 .../internal/DefaultExecutableContainer.java       |   38 -
 .../nativebinaries/internal/DefaultFlavor.java     |   41 -
 .../internal/DefaultFlavorContainer.java           |   33 -
 .../nativebinaries/internal/DefaultLibrary.java    |   42 -
 .../internal/DefaultLibraryContainer.java          |   38 -
 .../nativebinaries/internal/DefaultLinkerSpec.java |   68 -
 .../internal/DefaultNativeBinaryTasks.java         |   56 -
 .../internal/DefaultStaticLibraryArchiverSpec.java |   45 -
 .../nativebinaries/internal/DefaultTool.java       |   37 -
 .../internal/LibraryBinaryInternal.java            |   29 -
 .../gradle/nativebinaries/internal/LinkerSpec.java |   44 -
 .../internal/NativeBinaryServices.java             |   37 -
 .../internal/NativeProjectComponentIdentifier.java |   64 -
 .../internal/ProjectExecutableBinary.java          |   49 -
 .../internal/ProjectNativeBinaryInternal.java      |   36 -
 .../internal/ProjectNativeComponentInternal.java   |   24 -
 .../internal/ProjectNativeLibraryRequirement.java  |   48 -
 .../internal/ProjectSharedLibraryBinary.java       |  120 -
 .../internal/ProjectStaticLibraryBinary.java       |   86 -
 .../internal/SharedLibraryLinkerSpec.java          |   23 -
 .../internal/SourceSetNotationParser.java          |   73 -
 .../internal/StaticLibraryArchiverSpec.java        |   31 -
 .../internal/StaticLibraryBinaryInternal.java      |   26 -
 .../internal/TargetedNativeComponentInternal.java  |   29 -
 .../configure/ApplySourceSetConventions.java       |   50 -
 .../configure/ConfigureGeneratedSourceSets.java    |   54 -
 .../configure/CreateDefaultBuildTypes.java         |   30 -
 .../internal/configure/CreateDefaultFlavors.java   |   32 -
 .../internal/configure/CreateDefaultPlatform.java  |   29 -
 .../internal/configure/CreateNativeBinaries.java   |   79 -
 .../configure/DefaultNativeBinariesFactory.java    |   60 -
 .../internal/configure/NativeBinariesFactory.java  |   27 -
 .../configure/ProjectNativeBinaryInitializer.java  |   52 -
 .../ProjectNativeComponentInitializer.java         |   90 -
 .../internal/configure/RepositoriesFactory.java    |   69 -
 .../prebuilt/AbstractPrebuiltLibraryBinary.java    |  107 -
 .../prebuilt/DefaultPrebuiltLibraries.java         |   59 -
 .../internal/prebuilt/DefaultPrebuiltLibrary.java  |   50 -
 .../DefaultPrebuiltSharedLibraryBinary.java        |   66 -
 .../DefaultPrebuiltStaticLibraryBinary.java        |   55 -
 .../prebuilt/PrebuiltLibraryBinaryLocator.java     |   58 -
 .../prebuilt/PrebuiltLibraryInitializer.java       |   77 -
 .../prebuilt/PrebuiltLibraryResolveException.java  |   27 -
 .../ApiRequirementNativeDependencyResolver.java    |  104 -
 .../resolve/ChainedLibraryBinaryLocator.java       |   51 -
 .../internal/resolve/DefaultLibraryResolver.java   |  107 -
 .../resolve/DefaultNativeDependencySet.java        |   41 -
 .../internal/resolve/DefaultProjectLocator.java    |   41 -
 .../InputHandlingNativeDependencyResolver.java     |   36 -
 .../internal/resolve/LibraryBinaryLocator.java     |   25 -
 .../resolve/LibraryNativeDependencyResolver.java   |   36 -
 .../internal/resolve/LibraryResolveException.java  |   32 -
 .../NativeBinaryRequirementResolveResult.java      |   65 -
 .../resolve/NativeBinaryResolveResult.java         |   74 -
 .../resolve/NativeDependencyNotationParser.java    |   59 -
 .../internal/resolve/NativeDependencyResolver.java |   20 -
 .../resolve/NativeDependencyResolverServices.java  |   47 -
 .../resolve/ProjectLibraryBinaryLocator.java       |   44 -
 .../internal/resolve/ProjectLocator.java           |   22 -
 ...RequirementParsingNativeDependencyResolver.java |   38 -
 .../resolve/SourceSetNativeDependencyResolver.java |   97 -
 .../nativebinaries/language/PreprocessingTool.java |   45 -
 .../language/assembler/internal/AssembleSpec.java  |   35 -
 .../assembler/internal/DefaultAssembleSpec.java    |   47 -
 .../language/assembler/package-info.java           |   20 -
 .../plugins/AssemblerNativeBinariesPlugin.groovy   |   88 -
 .../assembler/plugins/AssemblerPlugin.groovy       |   35 -
 .../language/assembler/tasks/Assemble.groovy       |   95 -
 .../language/c/internal/CCompileSpec.java          |   23 -
 .../language/c/internal/DefaultCCompileSpec.java   |   22 -
 .../AbstractIncrementalNativeCompiler.java         |   86 -
 .../incremental/CleanCompilingNativeCompiler.java  |   57 -
 .../internal/incremental/CompilationFileState.java |   50 -
 .../c/internal/incremental/CompilationState.java   |   41 -
 .../incremental/CompilationStateSerializer.java    |  127 -
 .../incremental/DefaultIncrementalCompilation.java |   37 -
 .../incremental/DefaultSourceIncludes.java         |   79 -
 .../incremental/DefaultSourceIncludesParser.java   |   43 -
 .../incremental/DefaultSourceIncludesResolver.java |   67 -
 .../incremental/IncrementalCompilation.java        |   25 -
 .../incremental/IncrementalCompileProcessor.java   |  148 -
 .../incremental/IncrementalCompilerBuilder.java    |   75 -
 .../incremental/IncrementalNativeCompiler.java     |   45 -
 .../c/internal/incremental/ResolvedInclude.java    |   67 -
 .../c/internal/incremental/SourceIncludes.java     |   24 -
 .../internal/incremental/SourceIncludesParser.java |   24 -
 .../incremental/SourceIncludesResolver.java        |   23 -
 .../incremental/sourceparser/CSourceParser.java    |   33 -
 .../sourceparser/PreprocessingReader.java          |  145 -
 .../sourceparser/RegexBackedCSourceParser.java     |   82 -
 .../nativebinaries/language/c/package-info.java    |   20 -
 .../c/plugins/CNativeBinariesPlugin.groovy         |   91 -
 .../language/c/plugins/CPlugin.groovy              |   35 -
 .../c/tasks/AbstractNativeCompileTask.groovy       |  145 -
 .../language/c/tasks/CCompile.groovy               |   36 -
 .../language/cpp/internal/CppCompileSpec.java      |   23 -
 .../cpp/internal/DefaultCppCompileSpec.java        |   22 -
 .../nativebinaries/language/cpp/package-info.java  |   20 -
 .../cpp/plugins/CppNativeBinariesPlugin.groovy     |   89 -
 .../language/cpp/plugins/CppPlugin.groovy          |   35 -
 .../language/cpp/plugins/package-info.java         |   20 -
 .../language/cpp/tasks/CppCompile.groovy           |   38 -
 .../language/cpp/tasks/package-info.java           |   20 -
 .../internal/AbstractNativeCompileSpec.java        |  109 -
 .../internal/DefaultPreprocessingTool.java         |   39 -
 .../internal/DefaultObjectiveCCompileSpec.java     |   23 -
 .../objectivec/internal/ObjectiveCCompileSpec.java |   23 -
 .../language/objectivec/package-info.java          |   20 -
 .../plugins/ObjectiveCNativeBinariesPlugin.groovy  |   89 -
 .../objectivec/plugins/ObjectiveCPlugin.groovy     |   36 -
 .../language/objectivec/plugins/package-info.java  |   20 -
 .../objectivec/tasks/ObjectiveCCompile.groovy      |   38 -
 .../language/objectivec/tasks/package-info.java    |   20 -
 .../internal/DefaultObjectiveCppCompileSpec.java   |   23 -
 .../internal/ObjectiveCppCompileSpec.java          |   23 -
 .../language/objectivecpp/package-info.java        |   20 -
 .../ObjectiveCppNativeBinariesPlugin.groovy        |   90 -
 .../objectivecpp/plugins/ObjectiveCppPlugin.groovy |   36 -
 .../objectivecpp/plugins/package-info.java         |   20 -
 .../objectivecpp/tasks/ObjectiveCppCompile.groovy  |   38 -
 .../language/objectivecpp/tasks/package-info.java  |   20 -
 .../nativebinaries/language/package-info.java      |   20 -
 .../DefaultWindowsResourceCompileSpec.java         |   21 -
 .../rc/internal/WindowsResourceCompileSpec.java    |   21 -
 .../WindowsResourcesNativeBinariesPlugin.groovy    |  103 -
 .../rc/plugins/WindowsResourcesPlugin.groovy       |   36 -
 .../rc/tasks/WindowsResourceCompile.groovy         |  131 -
 .../org/gradle/nativebinaries/package-info.java    |   20 -
 .../nativebinaries/platform/Architecture.java      |   32 -
 .../nativebinaries/platform/OperatingSystem.java   |   60 -
 .../gradle/nativebinaries/platform/Platform.java   |  125 -
 .../nativebinaries/platform/PlatformContainer.java |   27 -
 .../platform/internal/ArchitectureInternal.java    |   38 -
 .../internal/ArchitectureNotationParser.java       |   91 -
 .../platform/internal/DefaultArchitecture.java     |  100 -
 .../platform/internal/DefaultOperatingSystem.java  |   68 -
 .../platform/internal/DefaultPlatform.java         |   74 -
 .../internal/DefaultPlatformContainer.java         |   39 -
 .../internal/OperatingSystemNotationParser.java    |   80 -
 .../platform/internal/PlatformInternal.java        |   22 -
 .../nativebinaries/platform/package-info.java      |   20 -
 .../plugins/NativeBinariesModelPlugin.java         |  142 -
 .../plugins/NativeBinariesPlugin.groovy            |  138 -
 .../nativebinaries/plugins/package-info.java       |   20 -
 .../nativebinaries/tasks/AbstractLinkTask.groovy   |  120 -
 .../nativebinaries/tasks/BuildBinaryTask.java      |   31 -
 .../tasks/CreateStaticLibrary.groovy               |   96 -
 .../nativebinaries/tasks/InstallExecutable.groovy  |  156 -
 .../nativebinaries/tasks/LinkExecutable.groovy     |   31 -
 .../nativebinaries/tasks/LinkSharedLibrary.groovy  |   43 -
 .../gradle/nativebinaries/tasks/package-info.java  |   20 -
 .../test/ProjectComponentTestSuite.java            |   31 -
 .../org/gradle/nativebinaries/test/TestSuite.java  |   26 -
 .../nativebinaries/test/TestSuiteContainer.java    |   27 -
 .../test/TestSuiteExecutableBinary.java            |   26 -
 .../nativebinaries/test/cunit/CUnitTestSuite.java  |   26 -
 .../cunit/internal/ConfigureCUnitTestSources.java  |   66 -
 .../test/cunit/internal/CreateCUnitBinaries.java   |   88 -
 .../test/cunit/internal/DefaultCUnitTestSuite.java |   39 -
 .../nativebinaries/test/cunit/package-info.java    |   20 -
 .../test/cunit/plugins/CUnitPlugin.groovy          |   72 -
 .../test/cunit/plugins/package-info.java           |   20 -
 .../test/cunit/tasks/GenerateCUnitLauncher.groovy  |   40 -
 .../test/cunit/tasks/package-info.java             |   20 -
 .../test/internal/DefaultTestSuiteContainer.java   |   30 -
 .../internal/DefaultTestSuiteExecutableBinary.java |   48 -
 .../gradle/nativebinaries/test/package-info.java   |   20 -
 .../test/plugins/NativeBinariesTestPlugin.groovy   |   75 -
 .../nativebinaries/test/plugins/package-info.java  |   20 -
 .../test/tasks/RunTestExecutable.groovy            |   79 -
 .../nativebinaries/test/tasks/package-info.java    |   20 -
 .../org/gradle/nativebinaries/toolchain/Clang.java |   41 -
 .../org/gradle/nativebinaries/toolchain/Gcc.java   |   41 -
 .../gradle/nativebinaries/toolchain/GccTool.java   |   42 -
 .../toolchain/PlatformConfigurableToolChain.java   |   55 -
 .../toolchain/TargetPlatformConfiguration.java     |   67 -
 .../gradle/nativebinaries/toolchain/ToolChain.java |   35 -
 .../toolchain/ToolChainRegistry.java               |   28 -
 .../gradle/nativebinaries/toolchain/VisualCpp.java |   47 -
 .../toolchain/internal/AbstractToolChain.java      |   73 -
 .../toolchain/internal/ArgsTransformer.java        |   25 -
 .../toolchain/internal/CommandLineTool.java        |  113 -
 .../CompileSpecToArgsTransformerChain.java         |   44 -
 .../internal/DefaultToolChainRegistry.java         |  149 -
 .../toolchain/internal/MacroArgsConverter.java     |   35 -
 .../toolchain/internal/NativeCompileSpec.java      |   63 -
 .../internal/OptionsFileArgsTransformer.java       |   64 -
 .../toolchain/internal/OutputCleaningCompiler.java |   67 -
 .../toolchain/internal/PlatformToolChain.java      |   40 -
 .../SingleSourceCompileArgTransformer.java         |   66 -
 .../toolchain/internal/ToolChainAvailability.java  |   68 -
 .../toolchain/internal/ToolChainInternal.java      |   41 -
 .../internal/ToolChainRegistryInternal.java        |   34 -
 .../toolchain/internal/ToolSearchResult.java       |   28 -
 .../toolchain/internal/ToolType.java               |   44 -
 .../internal/UnavailablePlatformToolChain.java     |   79 -
 .../toolchain/internal/clang/ClangToolChain.java   |   47 -
 .../gcc/AbstractGccCompatibleToolChain.java        |  267 -
 .../internal/gcc/ArStaticLibraryArchiver.java      |   73 -
 .../toolchain/internal/gcc/Assembler.java          |   80 -
 .../toolchain/internal/gcc/CCompiler.java          |   36 -
 .../internal/gcc/CommandLineToolSearchResult.java  |   25 -
 .../toolchain/internal/gcc/CppCompiler.java        |   37 -
 .../internal/gcc/GccCompilerArgsTransformer.java   |   58 -
 .../toolchain/internal/gcc/GccLinker.java          |   94 -
 .../internal/gcc/GccOptionsFileArgTransformer.java |   52 -
 .../internal/gcc/GccPlatformToolChain.java         |  114 -
 .../toolchain/internal/gcc/GccToolChain.java       |   90 -
 .../toolchain/internal/gcc/GccToolSearchPath.java  |   49 -
 .../toolchain/internal/gcc/NativeCompiler.java     |   65 -
 .../toolchain/internal/gcc/ObjectiveCCompiler.java |   38 -
 .../internal/gcc/ObjectiveCppCompiler.java         |   37 -
 .../gcc/PostTransformActionArgsTransformer.java    |   39 -
 .../internal/gcc/ShortCircuitArgsTransformer.java  |   41 -
 .../internal/gcc/version/GccVersionDeterminer.java |  142 -
 .../internal/gcc/version/GccVersionResult.java     |   23 -
 .../toolchain/internal/msvcpp/Assembler.java       |   80 -
 .../toolchain/internal/msvcpp/CCompiler.java       |   33 -
 .../toolchain/internal/msvcpp/CppCompiler.java     |   33 -
 .../msvcpp/DefaultVisualStudioLocator.java         |  369 --
 .../internal/msvcpp/DefaultWindowsSdkLocator.java  |  275 --
 .../toolchain/internal/msvcpp/EscapeUserArgs.java  |   40 -
 .../internal/msvcpp/InstallationSearchResult.java  |   27 -
 .../msvcpp/LibExeStaticLibraryArchiver.java        |   58 -
 .../toolchain/internal/msvcpp/LinkExeLinker.java   |   70 -
 .../toolchain/internal/msvcpp/NativeCompiler.java  |   59 -
 .../msvcpp/VisualCppCompilerArgsTransformer.java   |   50 -
 .../internal/msvcpp/VisualCppInstall.java          |  116 -
 .../internal/msvcpp/VisualCppToolChain.java        |  245 -
 .../internal/msvcpp/VisualStudioInstall.java       |   48 -
 .../internal/msvcpp/VisualStudioLocator.java       |   29 -
 .../internal/msvcpp/WindowsResourceCompiler.java   |   90 -
 .../toolchain/internal/msvcpp/WindowsSdk.java      |  138 -
 .../internal/msvcpp/WindowsSdkLocator.java         |   29 -
 .../internal/plugins/StandardToolChainsPlugin.java |   34 -
 .../toolchain/internal/tools/DefaultTool.java      |   58 -
 .../internal/tools/DefaultToolRegistry.java        |   35 -
 .../toolchain/internal/tools/GccToolInternal.java  |   29 -
 .../toolchain/internal/tools/PlatformGccTool.java  |   58 -
 .../internal/tools/PlatformToolRegistry.java       |   48 -
 .../toolchain/internal/tools/ToolRegistry.java     |   22 -
 .../toolchain/internal/tools/ToolSearchPath.java   |  135 -
 .../nativebinaries/toolchain/package-info.java     |   20 -
 .../toolchain/plugins/ClangCompilerPlugin.groovy   |   62 -
 .../toolchain/plugins/GccCompilerPlugin.groovy     |   63 -
 .../plugins/MicrosoftVisualCppPlugin.groovy        |   76 -
 .../toolchain/plugins/package-info.java            |   20 -
 .../META-INF/gradle-plugins/assembler.properties   |    1 -
 .../resources/META-INF/gradle-plugins/c.properties |    1 -
 .../META-INF/gradle-plugins/cpp.properties         |    1 -
 .../META-INF/gradle-plugins/cunit.properties       |   17 -
 .../gradle-plugins/native-binaries.properties      |    1 -
 .../META-INF/gradle-plugins/objective-c.properties |    1 -
 .../gradle-plugins/objective-cpp.properties        |    1 -
 .../gradle-plugins/windows-resources.properties    |    1 -
 ...e.internal.service.scopes.PluginServiceRegistry |    1 -
 .../ide/cdt/model/CprojectSettingsSpec.groovy      |   58 -
 .../internal/DefaultVisualStudioProjectTest.groovy |  115 -
 .../VisualStudioProjectConfigurationTest.groovy    |  196 -
 .../internal/VisualStudioProjectMapperTest.groovy  |  142 -
 .../VisualStudioProjectRegistryTest.groovy         |  109 -
 .../internal/VisualStudioFiltersFileTest.groovy    |   97 -
 .../internal/VisualStudioProjectFileTest.groovy    |  118 -
 .../internal/VisualStudioSolutionFileTest.groovy   |  190 -
 .../plugins/AssemblerLangPluginTest.groovy         |   42 -
 .../language/c/plugins/CLangPluginTest.groovy      |   42 -
 .../language/cpp/plugins/CppLangPluginTest.groovy  |   42 -
 .../internal/DefaultBuildTypeTest.groovy           |   29 -
 .../internal/DefaultExecutableBinaryTest.groovy    |   39 -
 .../internal/DefaultExecutableTest.groovy          |   29 -
 .../internal/DefaultFlavorTest.groovy              |   29 -
 .../internal/DefaultLibraryTest.groovy             |   59 -
 .../internal/DefaultNativeBinaryTasksTest.groovy   |   55 -
 .../internal/DefaultNativeComponentTest.groovy     |   77 -
 .../internal/ProjectNativeBinaryTest.groovy        |  210 -
 .../internal/ProjectSharedLibraryBinaryTest.groovy |  123 -
 .../internal/ProjectStaticLibraryBinaryTest.groovy |  125 -
 .../internal/SourceSetNotationParserTest.groovy    |   53 -
 .../configure/CreateDefaultBuildTypesTest.groovy   |   44 -
 .../configure/CreateDefaultFlavorsTest.groovy      |   67 -
 .../configure/CreateDefaultPlatformTest.groovy     |   44 -
 .../DefaultNativeBinariesFactoryTest.groovy        |   93 -
 .../ProjectNativeBinaryInitializerTest.groovy      |   99 -
 .../ProjectNativeComponentInitializerTest.groovy   |  145 -
 .../DefaultPrebuiltSharedLibraryBinaryTest.groovy  |   74 -
 .../DefaultPrebuiltStaticLibraryBinaryTest.groovy  |   56 -
 .../NativeDependencyNotationParserTest.groovy      |   72 -
 .../resolve/ProjectLibraryBinaryLocatorTest.groovy |  136 -
 .../AbstractNativeBinariesPluginTest.groovy        |  160 -
 .../AssemblerNativeBinariesPluginTest.groovy       |  183 -
 .../language/assembler/tasks/AssemblerTest.groovy  |   65 -
 .../CleanCompilingNativeCompilerTest.groovy        |   68 -
 .../CompilationStateSerializerTest.groovy          |   90 -
 .../DefaultSourceIncludesParserTest.groovy         |   68 -
 .../DefaultSourceIncludesResolverTest.groovy       |  154 -
 .../IncrementalCompileProcessorTest.groovy         |  422 --
 .../IncrementalNativeCompilerTest.groovy           |   54 -
 .../sourceparser/PreprocessingReaderTest.groovy    |   72 -
 .../RegexBackedCSourceParserTest.groovy            |  386 --
 .../c/plugins/CNativeBinariesPluginTest.groovy     |   48 -
 .../language/c/tasks/CCompileTest.groovy           |   68 -
 .../binaryinfo/ReadelfBinaryInfoTest.groovy        |   56 -
 .../cpp/internal/DefaultCppSourceSetTest.groovy    |   66 -
 .../cpp/plugins/CppNativeBinariesPluginTest.groovy |  206 -
 .../language/cpp/tasks/CppCompileTest.groovy       |   67 -
 .../ObjectiveCNativeBinariesPluginTest.groovy      |   46 -
 .../ObjectiveCppNativeBinariesPluginTest.groovy    |   45 -
 .../internal/ArchitectureNotationParserTest.groovy |  115 -
 .../internal/DefaultArchitectureTest.groovy        |   29 -
 .../internal/DefaultOperatingSystemTest.groovy     |   30 -
 .../platform/internal/DefaultPlatformTest.groovy   |   61 -
 .../OperatingSystemNotationParserTest.groovy       |  100 -
 .../plugins/NativeBinariesModelPluginTest.groovy   |  238 -
 .../plugins/NativeBinariesPluginTest.groovy        |  126 -
 .../internal/DefaultToolChainRegistryTest.groovy   |  182 -
 .../internal/OutputCleaningCompilerTest.groovy     |  115 -
 .../internal/ToolChainAvailabilityTest.groovy      |   67 -
 .../UnavailablePlatformToolChainTest.groovy        |   48 -
 .../gcc/AbstractGccCompatibleToolChainTest.groovy  |  237 -
 .../toolchain/internal/gcc/AssemblerTest.groovy    |   83 -
 .../toolchain/internal/gcc/CCompilerTest.groovy    |   98 -
 .../internal/gcc/ClangToolChainTest.groovy         |   48 -
 .../toolchain/internal/gcc/GccLinkerTest.groovy    |   86 -
 .../toolchain/internal/gcc/GccToolChainTest.groovy |   68 -
 .../gcc/ShortCircuitArgsTransformerTest.groovy     |   64 -
 .../gcc/version/GccVersionDeterminerTest.groovy    |  139 -
 .../msvcpp/DefaultVisualStudioLocatorTest.groovy   |  202 -
 .../msvcpp/DefaultWindowsSdkLocatorTest.groovy     |  256 -
 .../internal/msvcpp/VisualCppToolChainTest.groovy  |  206 -
 .../internal/tools/ToolSearchPathTest.groovy       |  125 -
 .../plugins/ClangCompilerPluginTest.groovy         |   57 -
 .../toolchain/plugins/GccCompilerPluginTest.groovy |   59 -
 .../plugins/MicrosoftVisualCppPluginTest.groovy    |   64 -
 .../toolchain/plugins/ToolchainPluginTest.groovy   |   70 -
 .../ide/visualstudio/fixtures/ProjectFile.groovy   |  128 -
 .../language/cpp/fixtures/AvailableToolChains.java |  503 --
 .../language/cpp/fixtures/ExecutableFixture.groovy |   35 -
 .../cpp/fixtures/NativeBinaryFixture.groovy        |   89 -
 .../cpp/fixtures/NativeInstallationFixture.groovy  |   73 -
 .../cpp/fixtures/RequiresInstalledToolChain.groovy |   30 -
 .../RequiresInstalledToolChainExtension.groovy     |   39 -
 .../cpp/fixtures/SharedLibraryFixture.groovy       |   47 -
 .../cpp/fixtures/StaticLibraryFixture.groovy       |   29 -
 .../cpp/fixtures/ToolChainRequirement.java         |   30 -
 .../app/CCallingMixedCAndCppHelloWorldApp.groovy   |   83 -
 .../fixtures/app/CCompilerDetectingTestApp.groovy  |   80 -
 .../cpp/fixtures/app/CHelloWorldApp.groovy         |  167 -
 .../fixtures/app/CppCallingCHelloWorldApp.groovy   |   75 -
 .../app/CppCompilerDetectingTestApp.groovy         |   80 -
 .../cpp/fixtures/app/CppHelloWorldApp.groovy       |  116 -
 .../app/DuplicateAssemblerBaseNamesTestApp.groovy  |   88 -
 .../fixtures/app/DuplicateCBaseNamesTestApp.groovy |   61 -
 .../app/DuplicateCppBaseNamesTestApp.groovy        |   64 -
 .../app/DuplicateMixedSameBaseNamesTestApp.groovy  |  140 -
 .../app/DuplicateObjectiveCBaseNamesTestApp.groovy |   80 -
 .../DuplicateObjectiveCppBaseNamesTestApp.groovy   |   80 -
 ...uplicateWindowsResourcesBaseNamesTestApp.groovy |   91 -
 .../ExeWithDiamondDependencyHelloWorldApp.groovy   |   56 -
 .../ExeWithLibraryUsingLibraryHelloWorldApp.groovy |  132 -
 .../language/cpp/fixtures/app/HelloWorldApp.java   |  117 -
 .../cpp/fixtures/app/IncrementalHelloWorldApp.java |   49 -
 .../fixtures/app/MixedLanguageHelloWorldApp.groovy |  143 -
 .../app/MixedObjectiveCHelloWorldApp.groovy        |  120 -
 .../fixtures/app/ObjectiveCHelloWorldApp.groovy    |  139 -
 .../fixtures/app/ObjectiveCppHelloWorldApp.groovy  |  146 -
 .../fixtures/app/PlatformDetectingTestApp.groovy   |   85 -
 .../language/cpp/fixtures/app/SourceFile.java      |   61 -
 .../language/cpp/fixtures/app/TestApp.java         |   72 -
 .../language/cpp/fixtures/app/TestComponent.groovy |   39 -
 .../app/WindowsResourceHelloWorldApp.groovy        |  129 -
 .../cpp/fixtures/binaryinfo/BinaryInfo.java        |   28 -
 .../fixtures/binaryinfo/DumpbinBinaryInfo.groovy   |   99 -
 .../cpp/fixtures/binaryinfo/OtoolBinaryInfo.groovy |   59 -
 .../fixtures/binaryinfo/ReadelfBinaryInfo.groovy   |   82 -
 .../test/cunit/CUnitTestResults.groovy             |  104 -
 .../dependency-management.gradle                   |   88 +
 .../ArtifactDeclarationIntegrationTest.groovy      |    0
 .../ArtifactDependenciesIntegrationTest.groovy     |  691 +++
 .../resolve/CacheResolveIntegrationTest.groovy     |  112 +
 ...ModuleDependenciesResolveIntegrationTest.groovy |  110 +
 ...adataRulesChangingModulesIntegrationTest.groovy |  169 +
 ...etadataRulesErrorHandlingIntegrationTest.groovy |  175 +
 .../ComponentMetadataRulesIntegrationTest.groovy   |  223 +
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   43 +
 .../ComponentReplacementIntegrationTest.groovy     |  350 ++
 .../resolve/CredentialsDslIntegrationTest.groovy   |   39 +
 .../DependencyExcludeResolveIntegrationTest.groovy |   89 +
 .../DependencyNotationIntegrationSpec.groovy       |  211 +
 ...ependencyResolutionEventsIntegrationTest.groovy |   51 +
 .../DependencyResolveRulesIntegrationTest.groovy   |  840 ++++
 ...pendencySubstitutionRulesIntegrationTest.groovy | 1417 ++++++
 .../DetachedConfigurationsIntegrationTest.groovy   |    0
 .../ExtendingConfigurationsIntegrationTest.groovy  |    0
 ...LibraryArtifactResolutionIntegrationTest.groovy |  112 +
 .../resolve/FlatDirResolveIntegrationTest.groovy   |    0
 .../resolve/ForcedModulesIntegrationTest.groovy    |    0
 .../JvmLibraryArtifactResolveTestFixture.groovy    |  213 +
 .../MetadataArtifactResolveTestFixture.groovy      |  194 +
 .../ProjectDependenciesIntegrationTest.groovy      |   89 +
 .../ProjectDependencyResolveIntegrationTest.groovy |  500 ++
 .../ResolutionResultApiIntegrationTest.groovy      |    0
 ...ResolutionStrategySamplesIntegrationTest.groovy |   58 +
 .../ResolveCrossVersionIntegrationTest.groovy      |  131 +
 .../integtests/resolve/ResolveTestFixture.groovy   |  352 ++
 .../ResolvedConfigurationIntegrationTest.groovy    |  100 +
 .../ScriptDependencyResolveIntegrationTest.groovy  |   59 +
 .../UnsupportedConfigurationMutationTest.groovy    |  227 +
 ...VersionConflictResolutionIntegrationTest.groovy |  727 +++
 ...actCacheReuseCrossVersionIntegrationTest.groovy |    0
 ...AliasedArtifactResolutionIntegrationTest.groovy |  200 +
 .../CacheReuseCrossVersionIntegrationTest.groovy   |  178 +
 .../M3CacheReuseCrossVersionIntegrationTest.groovy |   83 +
 .../MavenM2CacheReuseIntegrationTest.groovy        |   88 +
 .../ResolutionOverrideIntegrationTest.groovy       |  212 +
 ...ameCacheUsageCrossVersionIntegrationTest.groovy |   82 +
 .../CachedChangingModulesIntegrationTest.groovy    |  229 +
 ...achedDependencyResolutionIntegrationTest.groovy |  238 +
 .../CachedMissingModulesIntegrationTest.groovy     |  500 ++
 ...ependencyMetadataInMemoryIntegrationTest.groovy |  195 +
 ...coverFromBrokenResolutionIntegrationTest.groovy |  354 ++
 .../AbstractHttpsRepoResolveIntegrationTest.groovy |  111 +
 ...ationDependencyResolutionIntegrationTest.groovy |  215 +
 ...odingDependencyResolutionIntegrationTest.groovy |   46 +
 .../http/HttpProxyResolveIntegrationTest.groovy    |  151 +
 .../http/HttpRedirectResolveIntegrationTest.groovy |   85 +
 ...ctComponentSelectionRulesIntegrationTest.groovy |  104 +
 ...yDescriptorExcludeResolveIntegrationTest.groovy |   54 +
 ...SelectionRulesDependencyResolveIntegTest.groovy |  428 ++
 ...nentSelectionRulesErrorHandlingIntegTest.groovy |  284 ++
 ...mponentSelectionRulesProcessingIntegTest.groovy |  451 ++
 .../ivy/IvyBrokenDescriptorIntegrationTest.groovy  |  148 +
 .../IvyBrokenRemoteResolveIntegrationTest.groovy   |  364 ++
 ...angingModuleRemoteResolveIntegrationTest.groovy |  406 ++
 ...adataRulesChangingModulesIntegrationTest.groovy |   41 +
 ...IvyComponentMetadataRulesIntegrationTest.groovy |  315 ++
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   98 +
 ...CustomStatusLatestVersionIntegrationTest.groovy |  238 +
 ...rDependencyExcludeResolveIntegrationTest.groovy |  386 ++
 ...iptorModuleExcludeResolveIntegrationTest.groovy |  322 ++
 .../ivy/IvyDescriptorResolveIntegrationTest.groovy |  239 +
 .../IvyDescriptorValidationIntegrationTest.groovy  |   54 +
 ...amicRevisionRemoteResolveIntegrationTest.groovy | 1233 +++++
 ...IvyDynamicRevisionResolveIntegrationTest.groovy |  449 ++
 .../ivy/IvyFileRepoResolveIntegrationTest.groovy   |    0
 .../ivy/IvyHttpRepoResolveIntegrationTest.groovy   |   63 +
 .../ivy/IvyHttpsRepoResolveIntegrationTest.groovy  |   28 +
 ...LibraryArtifactResolutionIntegrationTest.groovy |  435 ++
 ...yModuleArtifactResolutionIntegrationTest.groovy |  172 +
 .../ivy/IvyModuleResolveIntegrationTest.groovy     |  258 +
 .../resolve/ivy/IvyResolveIntegrationTest.groovy   |  276 ++
 .../maven/BadPomFileResolveIntegrationTest.groovy  |  182 +
 .../MavenBrokenRemoteResolveIntegrationTest.groovy |   97 +
 ...adataRulesChangingModulesIntegrationTest.groovy |   70 +
 ...venComponentMetadataRulesIntegrationTest.groovy |   75 +
 ...ponentMetadataRulesStatusIntegrationTest.groovy |   67 +
 ...venCustomPackagingResolveIntegrationTest.groovy |   66 +
 .../MavenDependencyResolveIntegrationTest.groovy   |  212 +
 .../MavenDynamicResolveIntegrationTest.groovy      |  401 ++
 .../MavenFileRepoResolveIntegrationTest.groovy     |    0
 .../MavenHttpRepoResolveIntegrationTest.groovy     |  391 ++
 .../MavenHttpsRepoResolveIntegrationTest.groovy    |    0
 ...nJcenterDependencyResolveIntegrationTest.groovy |    0
 ...LibraryArtifactResolutionIntegrationTest.groovy |  334 ++
 .../maven/MavenLatestResolveIntegrationTest.groovy |  120 +
 .../MavenLocalRepoResolveIntegrationTest.groovy    |  362 ++
 ...nModuleArtifactResolutionIntegrationTest.groovy |  170 +
 .../MavenParentPomResolveIntegrationTest.groovy    |  433 ++
 .../MavenPomExcludeResolveIntegrationTest.groovy   |   76 +
 .../MavenPomPackagingResolveIntegrationTest.groovy |  360 ++
 .../maven/MavenPomResolveIntegrationTest.groovy    |   64 +
 .../MavenProfileResolveIntegrationTest.groovy      |  485 ++
 .../MavenSnapshotResolveIntegrationTest.groovy     |  997 ++++
 .../resolve/support/RepositoryDslSupport.groovy    |   76 +
 .../projectA-1.2-ivy.xml                           |    0
 .../projectB-1.5-ivy.xml                           |    0
 .../projectWithConfigurationHierarchy.gradle       |   57 +
 .../projectA-1.2-ivy.xml                           |    0
 .../projectB-1.5-ivy.xml                           |    0
 .../projectWithCyclesInDependencyGraph.gradle      |    0
 .../canNestModules/projectWithNestedModules.gradle |    0
 .../canUseDynamicVersions/projectA-1.2-ivy.xml     |    0
 .../canUseDynamicVersions/projectB-1.5-ivy.xml     |    0
 .../projectWithDynamicVersions.gradle              |    0
 .../projectA-1.2-ivy.xml                           |    0
 .../projectA-2.0-ivy.xml                           |    0
 .../projectB-1.5-ivy.xml                           |    0
 .../projectB-2.1.5-ivy.xml                         |    0
 .../projectWithConflicts.gradle                    |    0
 .../dependencyReportWithConflicts/settings.gradle  |    0
 .../artifacts/ArtifactDependencyResolver.java      |   29 +
 .../artifacts/ArtifactPublicationServices.java     |    0
 .../api/internal/artifacts/ArtifactPublisher.java  |    0
 .../artifacts/ComponentMetadataProcessor.java      |   22 +
 .../ComponentModuleMetadataProcessor.java          |   23 +
 .../artifacts/ComponentSelectionInternal.java      |   25 +
 .../artifacts/ComponentSelectionRulesInternal.java |   27 +
 .../internal/artifacts/ConfigurationResolver.java  |    0
 .../artifacts/DefaultArtifactIdentifier.java       |  107 +
 .../artifacts/DefaultComponentSelection.java       |   46 +
 .../artifacts/DefaultDependencyFactory.java        |    0
 .../DefaultDependencyManagementServices.java       |  203 +
 .../DefaultGlobalDependencyResolutionRules.java    |   35 +
 .../artifacts/DefaultModuleIdentifier.java         |   71 +
 .../artifacts/DefaultModuleVersionIdentifier.java  |  109 +
 .../artifacts/DefaultModuleVersionSelector.java    |    0
 .../artifacts/DefaultProjectDependencyFactory.java |    0
 .../artifacts/DefaultResolvedArtifact.java         |   98 +
 .../artifacts/DefaultResolvedDependency.java       |    0
 .../DependencyManagementBuildScopeServices.java    |  236 +
 .../DependencyManagementGlobalScopeServices.java   |   68 +
 .../api/internal/artifacts/DependencyServices.java |   36 +
 .../artifacts/GlobalDependencyResolutionRules.java |   21 +
 .../ModuleVersionIdentifierSerializer.java         |   39 +
 .../internal/artifacts/ModuleVersionPublisher.java |   25 +
 .../artifacts/ModuleVersionSelectorSerializer.java |   41 +
 .../artifacts/ResolvedConfigurationIdentifier.java |    0
 .../ResolvedConfigurationIdentifierSerializer.java |   39 +
 .../api/internal/artifacts/ResolverResults.java    |   75 +
 .../component/ComponentIdentifierFactory.java      |    0
 .../DefaultComponentIdentifierFactory.java         |   34 +
 .../ConfigurationContainerInternal.java            |    0
 .../configurations/ConfigurationInternal.java      |   26 +
 .../artifacts/configurations/Configurations.java   |   40 +
 .../configurations/ConfigurationsProvider.java     |    0
 .../configurations/DefaultConfiguration.java       |  569 +++
 .../DefaultConfigurationContainer.java             |  113 +
 .../DetachedConfigurationsProvider.java            |    0
 .../configurations/MutationValidator.java          |   47 +
 .../configurations/ResolutionStrategyInternal.java |   71 +
 .../configurations/RunnableMutationValidator.java  |   34 +
 .../configurations/TasksFromDependentProjects.java |   80 +
 .../TasksFromProjectDependencies.java              |   58 +
 .../api/internal/artifacts/dsl/ArtifactFile.java   |    0
 .../dsl/ComponentModuleMetadataContainer.java      |   93 +
 .../artifacts/dsl/ComponentSelectorParsers.java    |   99 +
 .../artifacts/dsl/DefaultArtifactHandler.java      |   72 +
 .../dsl/DefaultComponentMetadataHandler.java       |  178 +
 .../dsl/DefaultComponentModuleMetadataHandler.java |   33 +
 .../artifacts/dsl/ModuleReplacementsData.java      |   24 +
 .../dsl/ModuleVersionSelectorParsers.java          |   83 +
 .../artifacts/dsl/ParsedModuleStringNotation.java  |   65 +
 .../dsl/PublishArtifactNotationParserFactory.java  |   94 +
 .../ivyservice/AbstractDependencySubstitution.java |   73 +
 .../ivyservice/ArtifactCacheMetaData.java          |    0
 .../internal/artifacts/ivyservice/CacheLayout.java |   62 +
 .../CacheLockingArtifactDependencyResolver.java    |   46 +
 .../artifacts/ivyservice/CacheLockingManager.java  |   53 +
 .../ivyservice/ContextualArtifactResolver.java     |   73 +
 .../ivyservice/DefaultCacheLockingManager.java     |   87 +
 .../ivyservice/DefaultConfigurationResolver.java   |   49 +
 .../DefaultDependencyResolveDetails.java           |   95 +
 .../ivyservice/DefaultIvyContextManager.java       |    0
 .../ivyservice/DefaultIvyDependencyPublisher.java  |   73 +
 .../artifacts/ivyservice/DefaultIvyExtraInfo.java  |   69 +
 .../ivyservice/DefaultIvyModuleDescriptor.java     |   45 +
 .../ivyservice/DefaultLenientConfiguration.java    |  169 +
 .../DefaultModuleDependencySubstitution.java       |   50 +
 .../DefaultProjectDependencySubstitution.java      |   28 +
 .../ivyservice/DefaultResolvedConfiguration.java   |    0
 .../ivyservice/DefaultUnresolvedDependency.java    |   41 +
 .../ivyservice/DependencySubstitutionResolver.java |   61 +
 .../ErrorHandlingArtifactDependencyResolver.java   |  256 +
 .../ivyservice/IvyBackedArtifactPublisher.java     |   83 +
 .../artifacts/ivyservice/IvyContextManager.java    |    0
 .../ivyservice/IvyDependencyPublisher.java         |   26 +
 .../artifacts/ivyservice/IvyLoggingAdaper.java     |    0
 .../ivyservice/IvyModuleDescriptorWriter.java      |    0
 .../api/internal/artifacts/ivyservice/IvyUtil.java |   90 +
 .../ivyservice/IvyXmlModuleDescriptorWriter.java   |  402 ++
 .../ivyservice/LocalComponentFactory.java          |   26 +
 .../internal/artifacts/ivyservice/NamespaceId.java |  100 +
 .../SelfResolvingDependencyResolver.java           |  113 +
 ...cuitEmptyConfigsArtifactDependencyResolver.java |  107 +
 .../clientmodule/ClientModuleResolver.java         |   77 +
 .../DefaultCachedModuleVersionList.java            |   38 +
 .../DefaultResolvedModuleVersion.java              |    0
 .../dynamicversions/ModuleVersionsCache.java       |   34 +
 .../dynamicversions/ModuleVersionsCacheEntry.java  |   28 +
 .../SingleFileBackedModuleVersionsCache.java       |  138 +
 .../ivyresolve/BaseModuleComponentRepository.java  |   55 +
 .../BaseModuleComponentRepositoryAccess.java       |   56 +
 ...cheLockReleasingModuleComponentsRepository.java |  100 +
 .../CachingModuleComponentRepository.java          |  402 ++
 .../ivyresolve/ComponentMetaDataResolveState.java  |   81 +
 .../ComponentSelectionRulesProcessor.java          |  118 +
 .../ConfiguredModuleComponentRepository.java       |   23 +
 .../DefaultVersionedComponentChooser.java          |  153 +
 .../ivyresolve/DependencyResolverIdentifier.java   |   46 +
 .../ivyresolve/DynamicVersionResolver.java         |  365 ++
 .../ivyresolve/ErrorHandlingArtifactResolver.java  |   58 +
 .../ErrorHandlingModuleComponentRepository.java    |  122 +
 .../ivyservice/ivyresolve/IvyContextualiser.java   |    0
 ...amicResolveModuleComponentRepositoryAccess.java |   68 +
 .../ivyresolve/LocalModuleComponentRepository.java |  114 +
 .../ivyservice/ivyresolve/MetadataProvider.java    |   76 +
 .../ivyresolve/ModuleComponentRepository.java      |   33 +
 .../ModuleComponentRepositoryAccess.java           |   55 +
 .../ivyresolve/ModuleComponentResolveState.java    |   26 +
 .../ivyresolve/NoRepositoriesResolver.java         |   71 +
 .../ivyservice/ivyresolve/RepositoryChain.java     |   31 +
 .../ivyresolve/RepositoryChainAdapter.java         |   71 +
 .../RepositoryChainArtifactResolver.java           |   88 +
 .../RepositoryChainDependencyResolver.java         |  143 +
 .../RepositoryChainModuleResolution.java           |   32 +
 .../ivyresolve/RepositoryChainModuleSource.java    |   36 +
 .../ivyservice/ivyresolve/ResolveIvyFactory.java   |  188 +
 .../StartParameterResolutionOverride.java          |  126 +
 .../ivyservice/ivyresolve/UserResolverChain.java   |   78 +
 .../ivyservice/ivyresolve/VersionInfo.java         |    0
 .../artifacts/ivyservice/ivyresolve/Versioned.java |    0
 .../ivyresolve/VersionedComponentChooser.java      |   31 +
 .../memcache/CachedModuleVersionResult.java        |   59 +
 .../memcache/InMemoryArtifactsCache.java           |   49 +
 .../ivyresolve/memcache/InMemoryCacheStats.java    |   29 +
 .../InMemoryCachedModuleComponentRepository.java   |   87 +
 .../memcache/InMemoryCachedRepositoryFactory.java  |   62 +
 .../ivyresolve/memcache/InMemoryMetaDataCache.java |   70 +
 .../InMemoryModuleComponentRepositoryCaches.java   |   42 +
 .../parser/AbstractModuleDescriptorParser.java     |   55 +
 .../ivyresolve/parser/BuildableIvyArtifact.java    |   47 +
 .../ivyresolve/parser/DescriptorParseContext.java  |   24 +
 .../parser/DisconnectedDescriptorParseContext.java |   33 +
 .../DisconnectedIvyXmlModuleDescriptorParser.java  |   65 +
 .../DownloadedIvyModuleDescriptorParser.java       |    0
 .../parser/GradlePomModuleDescriptorBuilder.java   |  402 ++
 .../parser/GradlePomModuleDescriptorParser.java    |  217 +
 .../parser/IvyXmlModuleDescriptorParser.java       | 1281 +++++
 .../ivyresolve/parser/MetaDataParseException.java  |   32 +
 .../ivyresolve/parser/MetaDataParser.java          |   29 +
 .../ivyservice/ivyresolve/parser/PomDomParser.java |  165 +
 .../ivyservice/ivyresolve/parser/PomParent.java    |    0
 .../ivyservice/ivyresolve/parser/PomReader.java    |  675 +++
 .../ivyresolve/parser/RootPomParent.java           |    0
 .../UnresolvedDependencyVersionException.java      |    0
 .../ivyresolve/parser/data/MavenDependencyKey.java |    0
 .../ivyresolve/parser/data/PomDependencyMgt.java   |    0
 .../ivyresolve/parser/data/PomProfile.java         |    0
 .../strategy/AbstractVersionSelector.java          |   35 +
 .../strategy/DefaultVersionComparator.java         |   47 +
 .../strategy/DefaultVersionSelectorScheme.java     |   45 +
 .../ivyresolve/strategy/ExactVersionSelector.java  |   41 +
 .../ivyresolve/strategy/LatestVersionSelector.java |   49 +
 .../strategy/MavenVersionSelectorScheme.java       |   62 +
 .../ivyresolve/strategy/ResolverStrategy.java      |   41 +
 .../strategy/StaticVersionComparator.java          |   81 +
 .../ivyresolve/strategy/SubVersionSelector.java    |   44 +
 .../ivyservice/ivyresolve/strategy/Version.java    |   40 +
 .../ivyresolve/strategy/VersionComparator.java     |   31 +
 .../ivyresolve/strategy/VersionParser.java         |  122 +
 .../ivyresolve/strategy/VersionRangeSelector.java  |  167 +
 .../ivyresolve/strategy/VersionSelector.java       |   55 +
 .../ivyresolve/strategy/VersionSelectorScheme.java |   36 +
 .../CachedModuleDescriptorParseContext.java        |   32 +
 .../modulecache/DefaultCachedMetaData.java         |   62 +
 .../modulecache/DefaultModuleArtifactsCache.java   |  182 +
 .../modulecache/DefaultModuleMetaDataCache.java    |  143 +
 .../modulecache/IvyModuleCacheEntry.java           |   35 +
 .../modulecache/MavenModuleCacheEntry.java         |   41 +
 .../modulecache/MissingModuleCacheEntry.java       |   25 +
 .../modulecache/ModuleArtifactsCache.java          |   37 +
 .../modulecache/ModuleDescriptorCacheEntry.java    |   77 +
 .../ModuleDescriptorCacheEntrySerializer.java      |   84 +
 .../modulecache/ModuleDescriptorStore.java         |   74 +
 .../modulecache/ModuleMetaDataCache.java           |   47 +
 .../ConfigurationsToArtifactsConverter.java        |   23 +
 .../ConfigurationsToModuleDescriptorConverter.java |   23 +
 .../DefaultConfigurationsToArtifactsConverter.java |   53 +
 ...tConfigurationsToModuleDescriptorConverter.java |   36 +
 .../DefaultExcludeRuleConverter.java               |    0
 .../moduleconverter/ExcludeRuleConverter.java      |    0
 .../ResolveLocalComponentFactory.java              |   62 +
 .../AbstractIvyDependencyDescriptorFactory.java    |   77 +
 ...ultDependenciesToModuleDescriptorConverter.java |   59 +
 .../DefaultDependencyDescriptorFactory.java        |   47 +
 .../DependenciesToModuleDescriptorConverter.java   |   25 +
 .../dependencies/DependencyDescriptorFactory.java  |   24 +
 ...ternalModuleIvyDependencyDescriptorFactory.java |   59 +
 .../IvyDependencyDescriptorFactory.java            |   26 +
 .../ProjectIvyDependencyDescriptorFactory.java     |   56 +
 .../ReflectiveDependencyDescriptorFactory.java     |    0
 .../DefaultProjectComponentRegistry.java           |   36 +
 .../projectmodule/DefaultProjectPublication.java   |   42 +
 .../DefaultProjectPublicationRegistry.java         |    0
 .../projectmodule/ProjectArtifactResolver.java     |   72 +
 .../projectmodule/ProjectComponentRegistry.java    |   22 +
 .../projectmodule/ProjectDependencyResolver.java   |   56 +
 .../projectmodule/ProjectPublication.java          |    0
 .../projectmodule/ProjectPublicationRegistry.java  |    0
 .../resolutionstrategy/DefaultCachePolicy.java     |  270 +
 .../DefaultComponentSelectionRules.java            |  143 +
 .../DefaultDependencySubstitutions.java            |  267 +
 .../DefaultExternalResourceCachePolicy.java        |    0
 .../DefaultResolutionStrategy.java                 |  160 +
 .../DependencySubstitutionsInternal.java           |   34 +
 .../ExternalResourceCachePolicy.java               |    0
 .../LatestConflictResolution.java                  |    0
 .../ModuleForcingResolveRule.java                  |   57 +
 .../StrictConflictResolution.java                  |    0
 .../resolveengine/ComponentResolutionState.java    |   38 +
 .../resolveengine/DefaultDependencyResolver.java   |  139 +
 .../DefaultDependencyToConfigurationResolver.java  |   81 +
 .../DefaultModuleResolutionFilter.java             |  764 +++
 .../DependencyToConfigurationResolver.java         |   30 +
 .../LatestModuleConflictResolver.java              |   69 +
 .../resolveengine/ModuleConflictResolver.java      |   30 +
 .../resolveengine/ModuleResolutionFilter.java      |   54 +
 .../resolveengine/StrictConflictResolver.java      |   30 +
 .../VersionSelectionReasonResolver.java            |   36 +
 .../graph/CompositeDependencyGraphVisitor.java     |   52 +
 .../graph/DependencyGraphBuilder.java              |  920 ++++
 .../graph/DependencyGraphVisitor.java              |   24 +
 .../ResolutionResultDependencyGraphVisitor.java    |   43 +
 ...esolvedConfigurationDependencyGraphVisitor.java |  178 +
 ...lvedProjectConfigurationResultGraphVisitor.java |   50 +
 .../graph/conflicts/CandidateModule.java           |   37 +
 .../graph/conflicts/CompositeConflictResolver.java |   44 +
 .../graph/conflicts/ConflictContainer.java         |  130 +
 .../graph/conflicts/ConflictHandler.java           |   43 +
 .../graph/conflicts/ConflictResolutionResult.java  |   32 +
 .../graph/conflicts/DefaultConflictHandler.java    |   74 +
 .../conflicts/DefaultConflictResolutionResult.java |   37 +
 .../graph/conflicts/PotentialConflict.java         |   33 +
 .../graph/conflicts/PotentialConflictFactory.java  |   39 +
 .../DefaultResolvedConfigurationBuilder.java       |  133 +
 .../DefaultTransientConfigurationResults.java      |   38 +
 .../oldresult/ResolvedConfigurationBuilder.java    |   44 +
 .../oldresult/ResolvedConfigurationResults.java    |    0
 .../oldresult/ResolvedContentsMapping.java         |    0
 .../oldresult/TransientConfigurationResults.java   |   29 +
 .../TransientConfigurationResultsBuilder.java      |  189 +
 .../DefaultResolvedProjectConfigurationResult.java |   74 +
 ...tResolvedProjectConfigurationResultBuilder.java |   56 +
 ...DefaultResolvedProjectConfigurationResults.java |   32 +
 .../ResolvedProjectConfigurationResult.java        |   27 +
 .../ResolvedProjectConfigurationResultBuilder.java |   25 +
 .../ResolvedProjectConfigurationResults.java       |   23 +
 .../result/CachingDependencyResultFactory.java     |   56 +
 .../result/ComponentIdentifierSerializer.java      |   76 +
 .../result/ComponentSelectionReasonSerializer.java |   59 +
 .../result/ComponentSelectorSerializer.java        |   76 +
 .../result/DefaultInternalDependencyResult.java    |   59 +
 .../result/DefaultModuleVersionSelection.java      |   45 +
 .../result/DefaultResolutionResultBuilder.java     |   85 +
 .../result/InternalDependencyResult.java           |   40 +
 .../result/InternalDependencyResultSerializer.java |   62 +
 .../result/ModuleVersionSelection.java             |   30 +
 .../result/ModuleVersionSelectionSerializer.java   |   47 +
 .../result/ResolutionResultBuilder.java            |    0
 .../result/StreamingResolutionResultBuilder.java   |  200 +
 .../result/VersionSelectionReasons.java            |   83 +
 .../resolveengine/store/CachedStoreFactory.java    |  103 +
 .../resolveengine/store/DefaultBinaryStore.java    |  144 +
 .../store/ResolutionResultsStoreFactory.java       |    0
 .../ivyservice/resolveengine/store/StoreSet.java   |    0
 .../ModuleVersionArtifactIdentifierSerializer.java |   54 +
 .../CannotLocateLocalMavenRepositoryException.java |    0
 .../DefaultLocalMavenRepositoryLocator.java        |  105 +
 .../mvnsettings/DefaultMavenFileLocations.java     |   50 +
 .../mvnsettings/DefaultMavenSettingsProvider.java  |   39 +
 .../mvnsettings/LocalMavenRepositoryLocator.java   |    0
 .../artifacts/mvnsettings/MavenFileLocations.java  |    0
 .../mvnsettings/MavenSettingsProvider.java         |    0
 .../query/DefaultArtifactResolutionQuery.java      |  163 +
 .../DefaultArtifactResolutionQueryFactory.java     |   48 +
 .../repositories/AbstractArtifactRepository.java   |   41 +
 .../AbstractAuthenticationSupportedRepository.java |  105 +
 .../repositories/DefaultBaseRepositoryFactory.java |   99 +
 .../DefaultFlatDirArtifactRepository.java          |   95 +
 .../repositories/DefaultIvyArtifactRepository.java |  195 +
 .../DefaultMavenArtifactRepository.java            |  128 +
 .../DefaultMavenLocalArtifactRepository.java       |   52 +
 .../repositories/DefaultPasswordCredentials.java   |    0
 .../repositories/PublicationAwareRepository.java   |    0
 .../repositories/ResolutionAwareRepository.java    |   26 +
 .../layout/AbstractRepositoryLayout.java           |   48 +
 .../layout/DefaultIvyPatternRepositoryLayout.java  |   80 +
 .../layout/GradleRepositoryLayout.java             |   42 +
 .../repositories/layout/IvyRepositoryLayout.java   |   40 +
 .../repositories/layout/MavenRepositoryLayout.java |   45 +
 .../repositories/layout/ResolvedPattern.java       |    0
 .../resolver/AbstractResourcePattern.java          |   87 +
 .../resolver/ChainedVersionLister.java             |   62 +
 .../resolver/ComponentMetadataDetailsAdapter.java  |   58 +
 .../DefaultExternalResourceArtifactResolver.java   |  104 +
 .../resolver/ExternalResourceArtifactResolver.java |   31 +
 .../resolver/ExternalResourceResolver.java         |  444 ++
 ...rnalResourceResolverDescriptorParseContext.java |   65 +
 .../repositories/resolver/IvyResolver.java         |  154 +
 .../repositories/resolver/IvyResourcePattern.java  |   60 +
 .../repositories/resolver/M2ResourcePattern.java   |   89 +
 .../repositories/resolver/MavenLocalResolver.java  |   79 +
 .../repositories/resolver/MavenMetadata.java       |    0
 .../repositories/resolver/MavenMetadataLoader.java |   90 +
 .../repositories/resolver/MavenPattern.java        |    0
 .../repositories/resolver/MavenResolver.java       |  280 ++
 .../MavenUniqueSnapshotComponentIdentifier.java    |   67 +
 ...ueSnapshotExternalResourceArtifactResolver.java |   49 +
 .../resolver/MavenUniqueSnapshotModuleSource.java  |   30 +
 .../repositories/resolver/MavenVersionLister.java  |   54 +
 .../resolver/PatternBasedResolver.java             |   29 +
 .../repositories/resolver/ResourcePattern.java     |   51 +
 .../resolver/ResourceVersionLister.java            |  149 +
 .../repositories/resolver/VersionLister.java       |   32 +
 .../resolver/VersionPatternVisitor.java            |   31 +
 .../transport/RepositoryTransport.java             |   27 +
 .../transport/RepositoryTransportFactory.java      |  145 +
 .../artifacts/result/AbstractDependencyResult.java |    0
 .../result/DefaultArtifactResolutionResult.java    |   45 +
 .../result/DefaultComponentArtifactsResult.java    |   51 +
 .../artifacts/result/DefaultResolutionResult.java  |    0
 .../result/DefaultResolvedArtifactResult.java      |   39 +
 .../result/DefaultResolvedComponentResult.java     |    0
 .../result/DefaultResolvedDependencyResult.java    |    0
 .../result/DefaultUnresolvedArtifactResult.java    |   37 +
 .../result/DefaultUnresolvedComponentResult.java   |   37 +
 .../result/DefaultUnresolvedDependencyResult.java  |   52 +
 .../artifacts/result/jvm/AbstractArtifact.java     |   51 +
 .../filestore/ivy/ArtifactIdentifierFileStore.java |   44 +
 .../ClientModuleNotationParserFactory.java         |   40 +
 .../DependencyClassPathNotationConverter.java      |   81 +
 .../DependencyFilesNotationConverter.java          |   42 +
 .../notations/DependencyMapNotationConverter.java  |   52 +
 .../notations/DependencyNotationParser.java        |   43 +
 .../DependencyProjectNotationConverter.java        |   43 +
 .../DependencyStringNotationConverter.java         |   74 +
 .../ModuleIdentiferNotationConverter.java          |   64 +
 .../notations/ProjectDependencyFactory.java        |   60 +
 .../AbstractModuleComponentResolveMetaData.java    |  138 +
 .../model/BuildableIvyModulePublishMetaData.java   |   27 +
 .../model/BuildableIvyModuleResolveMetaData.java   |   76 +
 .../model/DefaultIvyModulePublishMetaData.java     |   84 +
 .../model/DefaultIvyModuleResolveMetaData.java     |   65 +
 .../model/DefaultMavenModuleResolveMetaData.java   |   92 +
 .../DefaultModuleComponentArtifactIdentifier.java  |   88 +
 .../DefaultModuleComponentArtifactMetaData.java    |   68 +
 .../model/DefaultModuleComponentIdentifier.java    |  102 +
 .../model/DefaultModuleComponentSelector.java      |  116 +
 .../model/IvyModuleArtifactPublishMetaData.java    |   36 +
 .../external/model/IvyModulePublishMetaData.java   |   27 +
 .../external/model/IvyModuleResolveMetaData.java   |   39 +
 .../external/model/MavenModuleResolveMetaData.java |   28 +
 .../model/ModuleComponentArtifactIdentifier.java   |   30 +
 .../model/ModuleComponentArtifactMetaData.java     |   39 +
 .../model/ModuleComponentResolveMetaData.java      |   40 +
 .../MutableModuleComponentResolveMetaData.java     |   50 +
 .../model/DefaultLocalArtifactIdentifier.java      |   72 +
 .../local/model/DefaultLocalComponentMetaData.java |  182 +
 .../model/DefaultProjectComponentIdentifier.java   |   69 +
 .../model/DefaultProjectComponentSelector.java     |   80 +
 .../model/DefaultProjectDependencyMetaData.java    |   35 +
 .../local/model/DslOriginDependencyMetaData.java   |   30 +
 .../model/DslOriginDependencyMetaDataWrapper.java  |   79 +
 .../local/model/LocalArtifactMetaData.java         |   25 +
 .../local/model/LocalComponentMetaData.java        |   40 +
 .../local/model/MutableLocalComponentMetaData.java |   36 +
 .../local/model/OpaqueComponentIdentifier.java     |   60 +
 .../AbstractModuleDescriptorBackedMetaData.java    |  265 +
 .../model/ComponentArtifactIdentifier.java         |   34 +
 .../component/model/ComponentArtifactMetaData.java |   39 +
 .../component/model/ComponentResolveMetaData.java  |   90 +
 .../internal/component/model/ComponentUsage.java   |   20 +
 .../component/model/ConfigurationMetaData.java     |   41 +
 .../component/model/DefaultComponentUsage.java     |   33 +
 .../component/model/DefaultDependencyMetaData.java |  154 +
 .../component/model/DefaultIvyArtifactName.java    |  118 +
 .../component/model/DependencyMetaData.java        |   74 +
 .../internal/component/model/IvyArtifactName.java  |   37 +
 .../internal/component/model/ModuleSource.java     |   28 +
 .../resolve/ArtifactNotFoundException.java         |   38 +
 .../internal/resolve/ArtifactResolveException.java |   71 +
 .../resolve/ModuleVersionNotFoundException.java    |   92 +
 .../resolve/ModuleVersionResolveException.java     |  127 +
 .../resolve/resolver/ArtifactResolver.java         |   41 +
 .../resolver/ComponentMetaDataResolver.java        |   28 +
 .../resolver/DependencyToComponentIdResolver.java  |   29 +
 .../resolver/DependencyToComponentResolver.java    |   29 +
 .../resolver/ModuleToComponentResolver.java        |   30 +
 .../resolve/result/ArtifactResolveResult.java      |   37 +
 .../resolve/result/ArtifactSetResolveResult.java   |   35 +
 .../result/BuildableArtifactResolveResult.java     |   39 +
 .../result/BuildableArtifactSetResolveResult.java  |   27 +
 .../result/BuildableComponentIdResolveResult.java  |   33 +
 .../result/BuildableComponentResolveResult.java    |   43 +
 .../result/BuildableComponentSelectionResult.java  |   73 +
 ...ldableModuleComponentMetaDataResolveResult.java |   72 +
 ...BuildableModuleVersionListingResolveResult.java |   65 +
 .../resolve/result/ComponentIdResolveResult.java   |   45 +
 .../resolve/result/ComponentResolveResult.java     |   48 +
 .../DefaultBuildableArtifactResolveResult.java     |   67 +
 .../DefaultBuildableArtifactSetResolveResult.java  |   64 +
 .../DefaultBuildableComponentIdResolveResult.java  |   96 +
 .../DefaultBuildableComponentResolveResult.java    |   88 +
 .../DefaultBuildableComponentSelectionResult.java  |   96 +
 ...ldableModuleComponentMetaDataResolveResult.java |   97 +
 ...BuildableModuleVersionListingResolveResult.java |   82 +
 .../result/DefaultResourceAwareResolveResult.java  |   44 +
 .../internal/resolve/result/ResolveResult.java     |   32 +
 .../resolve/result/ResourceAwareResolveResult.java |   40 +
 .../cached/ByUrlCachedExternalResourceIndex.java   |   28 +
 .../internal/resource/cached/CachedArtifact.java   |   26 +
 .../resource/cached/CachedArtifactIndex.java       |   63 +
 .../resource/cached/CachedExternalResource.java    |   46 +
 .../cached/CachedExternalResourceIndex.java        |   71 +
 .../internal/resource/cached/CachedItem.java       |   49 +
 .../resource/cached/DefaultCachedArtifact.java     |   64 +
 .../cached/DefaultCachedExternalResource.java      |   67 +
 .../cached/DefaultCachedExternalResourceIndex.java |   51 +
 .../resource/cached/ivy/AbstractCachedIndex.java   |  104 +
 .../ArtifactAtRepositoryCachedArtifactIndex.java   |  110 +
 .../cached/ivy/ArtifactAtRepositoryKey.java        |   56 +
 .../ivy/LocallyAvailableResourceFinderFactory.java |  155 +
 ...PatternBasedLocallyAvailableResourceFinder.java |   63 +
 .../transfer/AbstractProgressLoggingHandler.java   |   80 +
 .../CacheAwareExternalResourceAccessor.java        |   38 +
 .../DefaultCacheAwareExternalResourceAccessor.java |  237 +
 .../ProgressLoggingExternalResourceAccessor.java   |   84 +
 .../ProgressLoggingExternalResourceUploader.java   |   62 +
 .../resource/transfer/ResourceOperation.java       |   70 +
 .../transport/AbstractRepositoryTransport.java     |   27 +
 .../DefaultExternalResourceRepository.java         |   84 +
 .../transport/ExternalResourceRepository.java      |   72 +
 .../ResourceConnectorRepositoryTransport.java      |   55 +
 .../transport/file/FileResourceConnector.java      |   90 +
 .../resource/transport/file/FileTransport.java     |   61 +
 .../internal/rules/ClosureBackedRuleAction.java    |   95 +
 .../internal/rules/DefaultRuleActionAdapter.java   |   60 +
 .../internal/rules/DefaultRuleActionValidator.java |   65 +
 .../gradle/internal/rules/NoInputsRuleAction.java  |   56 +
 .../java/org/gradle/internal/rules/RuleAction.java |   32 +
 .../gradle/internal/rules/RuleActionAdapter.java   |   28 +
 .../rules/RuleActionValidationException.java       |   34 +
 .../gradle/internal/rules/RuleActionValidator.java |   21 +
 .../internal/rules/RuleSourceBackedRuleAction.java |  102 +
 .../org/gradle/internal/rules/SpecRuleAction.java  |   40 +
 ...internal.artifacts.DependencyManagementServices |    0
 ...e.internal.service.scopes.PluginServiceRegistry |    0
 .../artifacts/DefaultArtifactIdentifierTest.groovy |    0
 .../artifacts/DefaultComponentSelectionTest.groovy |   53 +
 .../DefaultDependencyManagementServicesTest.groovy |   92 +
 .../artifacts/DefaultModuleIdentifierSpec.groovy   |    0
 .../DefaultModuleVersionIdentifierSpec.groovy      |    0
 .../DefaultModuleVersionSelectorTest.groovy        |    0
 .../artifacts/DefaultResolvedArtifactTest.groovy   |   48 +
 .../artifacts/DefaultResolvedDependencySpec.groovy |   92 +
 .../artifacts/DefaultResolvedDependencyTest.java   |  215 +
 ...pendencyManagementBuildScopeServicesTest.groovy |    0
 ...endencyManagementGlobalScopeServicesTest.groovy |    0
 .../ModuleVersionSelectorSerializerTest.groovy     |   33 +
 ...vedConfigurationIdentifierSerializerTest.groovy |   37 +
 .../ResolvedConfigurationIdentifierSpec.groovy     |    0
 .../internal/artifacts/ResolverResultsSpec.groovy  |   55 +
 .../DefaultComponentIdentifierFactoryTest.groovy   |   55 +
 .../configurations/ConfigurationsTest.java         |    0
 .../DefaultConfigurationContainerSpec.groovy       |  102 +
 .../DefaultConfigurationContainerTest.groovy       |  119 +
 .../configurations/DefaultConfigurationSpec.groovy |  359 ++
 .../configurations/DefaultConfigurationTest.java   |  906 ++++
 .../TasksFromDependentProjectsTest.groovy          |   73 +
 .../TasksFromProjectDependenciesTest.groovy        |   64 +
 .../internal/artifacts/dsl/ArtifactFileTest.groovy |    0
 .../ComponentModuleMetadataContainerTest.groovy    |   79 +
 .../dsl/ComponentSelectorParsersTest.groovy        |  174 +
 .../dsl/DefaultArtifactHandlerTest.groovy          |    0
 .../dsl/DefaultComponentMetadataHandlerTest.groovy |  412 ++
 .../dsl/ModuleVersionSelectorParsersTest.groovy    |  153 +
 ...lishArtifactNotationConverterFactoryTest.groovy |  122 +
 .../artifacts/ivyservice/ArtifactTypeTest.groovy   |   30 +
 .../artifacts/ivyservice/CacheLayoutTest.groovy    |   54 +
 ...cheLockingArtifactDependencyResolverTest.groovy |   45 +
 .../DefaultCacheLockingManagerTest.groovy          |    0
 .../DefaultDependencyResolveDetailsSpec.groovy     |  154 +
 .../ivyservice/DefaultIvyContextManagerTest.groovy |    0
 .../ivyservice/DefaultIvyExtraInfoTest.groovy      |   89 +
 .../DefaultIvyModuleDescriptorTest.groovy          |   50 +
 .../DefaultModuleDependencySubstitutionTest.groovy |  192 +
 ...DefaultProjectDependencySubstitutionTest.groovy |  102 +
 .../DefaultUnresolvedDependencySpec.groovy         |   34 +
 .../DependencySubstitutionResolverSpec.groovy      |   84 +
 ...orHandlingArtifactDependencyResolverTest.groovy |  158 +
 .../artifacts/ivyservice/IvyUtilTest.groovy        |   45 +
 .../IvyXmlModuleDescriptorWriterTest.groovy        |  146 +
 .../artifacts/ivyservice/NamespaceIdTest.groovy    |   64 +
 .../SelfResolvingDependencyResolverTest.groovy     |  136 +
 ...ptyConfigsArtifactDependencyResolverSpec.groovy |   77 +
 .../clientmodule/ClientModuleResolverTest.groovy   |   96 +
 .../BaseModuleComponentRepositoryTest.groovy       |   61 +
 .../CachingModuleComponentRepositoryTest.groovy    |  152 +
 .../ComponentSelectionRulesProcessorTest.groovy    |  323 ++
 .../DefaultVersionedComponentChooserTest.groovy    |  345 ++
 .../DependencyResolverIdentifierTest.groovy        |   63 +
 .../ErrorHandlingArtifactResolverTest.groovy       |   98 +
 ...solveModuleComponentRepositoryAccessTest.groovy |   72 +
 .../ivyresolve/MetadataProviderTest.groovy         |  121 +
 .../ivyresolve/RepositoryChainAdapterTest.groovy   |   72 +
 .../RepositoryChainArtifactResolverTest.groovy     |  128 +
 .../RepositoryChainDependencyResolverTest.groovy   |  582 +++
 .../ivyresolve/ResolveIvyFactoryTest.groovy        |  139 +
 .../memcache/CachedModuleVersionResultTest.groovy  |  118 +
 .../memcache/InMemoryArtifactsCacheTest.groovy     |   76 +
 ...emoryCachedModuleComponentRepositoryTest.groovy |  233 +
 .../InMemoryCachedRepositoryFactoryTest.groovy     |   77 +
 .../memcache/InMemoryMetaDataCacheTest.groovy      |  142 +
 ...tractGradlePomModuleDescriptorParserTest.groovy |   70 +
 .../ivyresolve/parser/AbstractPomReaderTest.groovy |   71 +
 ...onnectedIvyXmlModuleDescriptorParserTest.groovy |   80 +
 .../DownloadedIvyModuleDescriptorParserTest.groovy |    0
 ...adlePomModuleDescriptorParserProfileTest.groovy | 1735 +++++++
 .../GradlePomModuleDescriptorParserTest.groovy     | 2250 +++++++++
 .../parser/IvyXmlModuleDescriptorParserTest.groovy |  939 ++++
 .../ivyresolve/parser/PomReaderProfileTest.groovy  | 2364 +++++++++
 .../ivyresolve/parser/PomReaderTest.groovy         |  851 ++++
 .../parser/data/MavenDependencyKeyTest.groovy      |    0
 .../strategy/AbstractVersionSelectorTest.groovy    |   40 +
 .../strategy/DefaultVersionComparatorTest.groovy   |  201 +
 .../DefaultVersionSelectorSchemeTest.groovy        |   79 +
 .../strategy/ExactVersionSelectorTest.groovy       |   85 +
 .../strategy/LatestVersionSelectorTest.groovy      |   71 +
 .../strategy/MavenVersionSelectorSchemeTest.groovy |   66 +
 .../strategy/SubVersionSelectorTest.groovy         |   64 +
 .../ivyresolve/strategy/VersionParserTest.groovy   |  103 +
 .../strategy/VersionRangeSelectorTest.groovy       |  155 +
 .../modulecache/ModuleDescriptorStoreTest.groovy   |   77 +
 ...ltConfigurationsToArtifactsConverterTest.groovy |  100 +
 ...figurationsToModuleDescriptorConverterTest.java |   94 +
 .../DefaultExcludeRuleConverterTest.java           |    0
 .../moduleconverter/IvyConverterTestUtil.java      |    0
 .../ResolveLocalComponentFactoryTest.groovy        |   58 +
 ...actDependencyDescriptorFactoryInternalTest.java |  135 +
 ...endenciesToModuleDescriptorConverterTest.groovy |   87 +
 .../DefaultDependencyDescriptorFactoryTest.groovy  |   65 +
 ...ernalModuleDependencyDescriptorFactoryTest.java |   67 +
 .../ProjectDependencyDescriptorFactoryTest.groovy  |   63 +
 ...eflectiveDependencyDescriptorFactoryTest.groovy |    0
 .../ProjectDependencyResolverTest.groovy           |   68 +
 .../DefaultCachePolicySpec.groovy                  |  329 ++
 .../DefaultComponentSelectionRulesTest.groovy      |  301 ++
 .../DefaultDependencySubstitutionsSpec.groovy      |  328 ++
 .../DefaultResolutionStrategySpec.groovy           |  245 +
 .../ModuleForcingResolveRuleSpec.groovy            |   89 +
 .../DefaultModuleResolutionFilterTest.groovy       |  623 +++
 .../DependencyGraphBuilderTest.groovy              | 1095 +++++
 .../VersionSelectionReasonResolverTest.groovy      |   43 +
 .../graph/conflicts/ConflictContainerTest.groovy   |  149 +
 .../conflicts/DefaultConflictHandlerTest.groovy    |  101 +
 .../CachingDependencyResultFactoryTest.groovy      |   76 +
 .../ComponentIdentifierSerializerTest.groovy       |   60 +
 .../ComponentSelectionReasonSerializerTest.groovy  |   54 +
 .../result/ComponentSelectorSerializerTest.groovy  |   60 +
 .../DefaultResolutionResultBuilderSpec.groovy      |  284 ++
 .../resolveengine/result/DummyBinaryStore.groovy   |   48 +
 .../resolveengine/result/DummyStore.groovy         |    0
 .../InternalDependencyResultSerializerTest.groovy  |   80 +
 .../ModuleVersionSelectionSerializerTest.groovy    |   40 +
 .../result/ResolutionResultPrinter.groovy          |    0
 .../StreamingResolutionResultBuilderTest.groovy    |  139 +
 .../result/VersionSelectionReasonsTest.groovy      |   42 +
 .../store/CachedStoreFactoryTest.groovy            |    0
 .../store/DefaultBinaryStoreTest.groovy            |    0
 .../store/ResolutionResultsStoreFactoryTest.groovy |    0
 .../DefaultLocalMavenRepositoryLocatorTest.groovy  |  188 +
 .../DefaultArtifactResolutionQueryTest.groovy      |  150 +
 ...itoryChangingNameAfterContainerInclusion.groovy |   41 +
 ...actAuthenticationSupportedRepositoryTest.groovy |  201 +
 .../DefaultBaseRepositoryFactoryTest.groovy        |  104 +
 .../DefaultFlatDirArtifactRepositoryTest.groovy    |   82 +
 .../DefaultIvyArtifactRepositoryTest.groovy        |  280 ++
 .../DefaultMavenArtifactRepositoryTest.groovy      |  137 +
 .../DefaultMavenLocalRepositoryTest.groovy         |   66 +
 .../resolver/ChainedVersionListerTest.groovy       |  141 +
 .../resolver/ExternalResourceResolverTest.groovy   |  123 +
 .../repositories/resolver/IvyResolverTest.groovy   |   31 +
 .../resolver/IvyResourcePatternTest.groovy         |   89 +
 .../resolver/M2ResourcePatternTest.groovy          |  127 +
 .../repositories/resolver/MavenResolverTest.groovy |   31 +
 ...venUniqueSnapshotComponentIdentifierTest.groovy |   79 +
 ...shotExternalResourceArtifactResolverTest.groovy |   69 +
 .../resolver/MavenVersionListerTest.groovy         |  212 +
 .../resolver/ResourceVersionListerTest.groovy      |  198 +
 .../RepositoryTransportFactoryTest.groovy          |   76 +
 .../DefaultArtifactResolutionResultTest.groovy     |   59 +
 .../DefaultComponentArtifactsResultTest.groovy     |   43 +
 .../result/DefaultResolutionResultTest.groovy      |  113 +
 .../DefaultResolvedComponentResultTest.groovy      |   63 +
 ...DependencyClassPathNotationConverterTest.groovy |   69 +
 .../DependencyMapNotationConverterTest.groovy      |  139 +
 .../DependencyStringNotationConverterTest.groovy   |  176 +
 .../ModuleIdentiferNotationConverterTest.groovy    |   52 +
 .../notations/ProjectDependencyFactoryTest.groovy  |   64 +
 ...stractModuleComponentResolveMetaDataTest.groovy |  275 ++
 .../BuildableIvyModuleResolveMetaDataTest.groovy   |   92 +
 .../DefaultIvyModulePublishMetaDataTest.groovy     |   42 +
 .../DefaultIvyModuleResolveMetaDataTest.groovy     |   66 +
 .../DefaultMavenModuleResolveMetaDataTest.groovy   |   74 +
 ...ultModuleComponentArtifactIdentifierTest.groovy |   66 +
 ...faultModuleComponentArtifactMetaDataTest.groovy |   71 +
 .../DefaultModuleComponentIdentifierTest.groovy    |   81 +
 .../DefaultModuleComponentSelectorTest.groovy      |  118 +
 .../DefaultLocalArtifactIdentifierTest.groovy      |   67 +
 .../model/DefaultLocalComponentMetaDataTest.groovy |  221 +
 .../DefaultProjectComponentIdentifierTest.groovy   |   58 +
 .../DefaultProjectComponentSelectorTest.groovy     |   92 +
 .../model/OpaqueComponentIdentifierTest.groovy     |   60 +
 .../model/DefaultDependencyMetaDataTest.groovy     |  203 +
 .../model/DefaultIvyArtifactNameTest.groovy        |   65 +
 .../resolve/ArtifactNotFoundExceptionTest.groovy   |   47 +
 .../ModuleVersionNotFoundExceptionTest.groovy      |  135 +
 .../ModuleVersionResolveExceptionTest.groovy       |   56 +
 ...efaultBuildableArtifactResolveResultTest.groovy |  106 +
 ...ultBuildableArtifactSetResolveResultTest.groovy |   75 +
 ...ultBuildableComponentIdResolveResultTest.groovy |   96 +
 ...faultBuildableComponentResolveResultTest.groovy |  154 +
 ...ultBuildableComponentSelectionResultTest.groovy |   94 +
 ...ModuleComponentMetaDataResolveResultTest.groovy |  108 +
 ...bleModuleVersionListingResolveResultTest.groovy |   80 +
 .../DefaultResourceAwareResolveResultTest.groovy   |   34 +
 .../DefaultArtifactResolutionCacheTest.groovy      |   80 +
 ...positeLocallyAvailableResourceFinderTest.groovy |   74 +
 ...zyLocallyAvailableResourceCandidatesTest.groovy |   57 +
 ...ltCacheAwareExternalResourceAccessorTest.groovy |  335 ++
 ...gressLoggingExternalResourceAccessorTest.groovy |  147 +
 ...gressLoggingExternalResourceUploaderTest.groovy |   73 +
 .../resource/transfer/ResourceOperationTest.groovy |   95 +
 .../rules/ClosureBackedRuleActionTest.groovy       |  149 +
 .../rules/DefaultRuleActionAdapterTest.groovy      |  135 +
 .../rules/DefaultRuleActionValidatorTest.groovy    |   56 +
 .../internal/rules/NoInputsRuleActionTest.groovy   |   60 +
 .../rules/RuleSourceBackedRuleActionTest.groovy    |  167 +
 .../ivyservice/ivyresolve/parser/test-full.xml     |  107 +
 ...ifactAtRepositoryCachedArtifactIndexTest.groovy |  130 +
 .../result/ResolutionResultDataBuilder.groovy      |   48 +
 ...tractIvyRemoteRepoResolveIntegrationTest.groovy |  428 ++
 subprojects/diagnostics/diagnostics.gradle         |   11 +-
 .../plugins/HelpTasksPluginIntegrationTest.groovy  |   43 +
 .../ComponentReportIntegrationTest.groovy          |  154 +
 .../HtmlDependencyReportTaskIntegrationTest.groovy |   71 +-
 .../model/ModelReportIntegrationTest.groovy        |   57 +
 ...pendencyInsightReportTaskIntegrationTest.groovy |   51 +-
 .../DependencyReportTaskIntegrationTest.groovy     |  211 +-
 .../diagnostics/HelpTaskIntegrationTest.groovy     |  122 +-
 .../TaskReportTaskIntegrationTest.groovy           |   78 +-
 .../org/gradle/api/plugins/HelpTasksPlugin.groovy  |   68 -
 .../org/gradle/api/plugins/HelpTasksPlugin.java    |  191 +
 .../gradle/api/plugins/ProjectReportsPlugin.java   |   12 +-
 .../plugins/internal/HelpTasksAutoApplyAction.java |    2 +-
 .../api/reporting/components/ComponentReport.java  |  101 +
 .../internal/AbstractBinaryRenderer.java           |   71 +
 .../components/internal/BinaryRenderer.java        |   26 +
 .../components/internal/ComponentRenderer.java     |   50 +
 .../internal/ComponentReportRenderer.java          |  108 +
 .../components/internal/DiagnosticsServices.java   |   45 +
 .../components/internal/SourceSetRenderer.java     |   53 +
 .../internal/TypeAwareBinaryRenderer.java          |   57 +
 .../api/reporting/components/package-info.java     |   20 +
 .../dependencies/HtmlDependencyReportTask.java     |   34 +-
 .../internal/HtmlDependencyReporter.groovy         |   99 +-
 .../JsonDependencyReportIndexRenderer.groovy       |   79 -
 .../internal/JsonProjectDependencyRenderer.groovy  |   13 +-
 .../dependencies/internal/ProjectPageRenderer.java |   88 +
 .../internal/ProjectsPageRenderer.java             |   85 +
 .../gradle/api/reporting/model/ModelReport.java    |   64 +
 .../model/internal/ModelReportRenderer.java        |   56 +
 .../gradle/api/reporting/model/package-info.java   |   20 +
 .../api/tasks/diagnostics/AbstractReportTask.java  |   17 +-
 .../diagnostics/DependencyInsightReportTask.groovy |   39 +-
 .../api/tasks/diagnostics/ProjectReportTask.java   |   10 +-
 .../api/tasks/diagnostics/TaskReportTask.java      |   20 +-
 .../tasks/diagnostics/internal/ReportRenderer.java |   12 +-
 .../diagnostics/internal/TaskReportRenderer.java   |   23 +-
 .../diagnostics/internal/TextReportRenderer.java   |   56 +-
 .../AsciiDependencyReportRenderer.java             |    2 +-
 .../dsl/DependencyResultSpecNotationConverter.java |   46 +
 .../dsl/DependencyResultSpecNotationParser.java    |   54 -
 .../internal/graph/DependencyGraphRenderer.groovy  |    4 +-
 .../graph/nodes/UnresolvedDependencyEdge.java      |    3 +-
 .../insight/DependencyInsightReporter.groovy       |   10 +-
 .../internal/insight/DependencyResultSorter.java   |   31 +-
 .../internal/text/DefaultTextReportBuilder.java    |  114 +
 .../internal/text/TextReportBuilder.java           |   43 +
 .../main/groovy/org/gradle/configuration/Help.java |   38 +-
 .../gradle/configuration/TaskDetailPrinter.java    |   50 +-
 ...properties => org.gradle.help-tasks.properties} |    0
 ...erties => org.gradle.project-report.properties} |    0
 ...rties => org.gradle.project-reports.properties} |    0
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../diagnostics/htmldependencyreport/index.html    |   48 -
 .../htmldependencyreport/jquery-1.10.1.min.js      |    6 -
 .../diagnostics/htmldependencyreport/script.js     |   27 +-
 .../diagnostics/htmldependencyreport/style.css     |    5 +-
 .../diagnostics/htmldependencyreport/template.html |   41 -
 .../diagnostics/htmldependencyreport/tree.css      |   10 +-
 .../gradle/api/plugins/HelpTasksPluginSpec.groovy  |   46 +-
 .../internal/ComponentRendererTest.groovy          |  106 +
 .../internal/ComponentReportRendererTest.groovy    |  142 +
 .../internal/SourceSetRendererTest.groovy          |   79 +
 .../tasks/diagnostics/AbstractReportTaskTest.java  |    8 +-
 .../DependencyInsightReportTaskSpec.groovy         |    3 +-
 .../api/tasks/diagnostics/TaskReportTaskTest.java  |    6 +-
 .../DependencyResultSpecNotationParserSpec.groovy  |   23 +-
 .../internal/dsl/DependencyResultSpecTest.groovy   |    2 +-
 .../AbstractRenderableDependencyResultSpec.groovy  |    8 +-
 .../nodes/RenderableDependencyResultTest.groovy    |    2 +-
 ...RenderableUnresolvedDependencyResultTest.groovy |    2 +-
 .../internal/graph/nodes/SimpleDependency.java     |    2 +-
 .../insight/DependencyInsightReporterSpec.groovy   |   17 +-
 .../insight/DependencyResultSorterSpec.groovy      |   40 +-
 .../text/DefaultTextReportBuilderTest.groovy       |  152 +
 .../AbstractComponentReportIntegrationTest.groovy  |   51 +
 .../ComponentReportOutputFormatter.groovy          |   46 +
 subprojects/distributions/distributions.gradle     |    3 +
 .../gradle/AllDistributionIntegrationSpec.groovy   |    5 +
 .../gradle/BinDistributionIntegrationSpec.groovy   |    5 +
 .../org/gradle/DistributionIntegrationSpec.groovy  |   55 +-
 .../gradle/SrcDistributionIntegrationSpec.groovy   |    5 +
 subprojects/distributions/src/toplevel/LICENSE     |   18 +-
 subprojects/distributions/src/toplevel/NOTICE      |    1 -
 subprojects/docs/docs.gradle                       |   83 +-
 subprojects/docs/src/docs/css/base.css             |    2 +-
 subprojects/docs/src/docs/css/dsl.css              |    6 +-
 subprojects/docs/src/docs/css/release-notes.css    |    6 +-
 subprojects/docs/src/docs/css/userguide.css        |    4 -
 subprojects/docs/src/docs/dsl/dsl.xml              |  128 +-
 .../org.gradle.api.NamedDomainObjectCollection.xml |   44 +
 .../org.gradle.api.NamedDomainObjectContainer.xml  |   44 +
 ...ml => org.gradle.api.NamedDomainObjectList.xml} |    0
 ...xml => org.gradle.api.NamedDomainObjectSet.xml} |    0
 .../docs/src/docs/dsl/org.gradle.api.Project.xml   |   18 -
 ...org.gradle.api.artifacts.ComponentSelection.xml |   47 +
 ...radle.api.artifacts.ComponentSelectionRules.xml |   44 +
 ...gradle.api.artifacts.ConfigurationContainer.xml |    3 -
 ...org.gradle.api.artifacts.ResolutionStrategy.xml |    6 +
 ....api.artifacts.dsl.ComponentMetadataHandler.xml |   44 +
 ...rtifacts.dsl.ComponentModuleMetadataHandler.xml |   41 +
 ....gradle.api.artifacts.dsl.DependencyHandler.xml |    9 +
 ....gradle.api.artifacts.dsl.RepositoryHandler.xml |    3 -
 ...api.artifacts.query.ArtifactResolutionQuery.xml |   47 +
 ...i.artifacts.repositories.ArtifactRepository.xml |   41 +
 ...ifacts.repositories.AuthenticationSupported.xml |   44 +
 .../dsl/org.gradle.api.plugins.PluginAware.xml     |   31 +
 .../dsl/org.gradle.api.plugins.PluginManager.xml   |   34 +
 .../dsl/org.gradle.api.plugins.antlr.AntlrTask.xml |    1 +
 .../org.gradle.api.plugins.quality.Checkstyle.xml  |    4 +
 ...dle.api.plugins.quality.CheckstyleExtension.xml |   12 +-
 .../org.gradle.api.plugins.quality.CodeNarc.xml    |    4 +
 ...radle.api.plugins.quality.CodeNarcExtension.xml |   14 +-
 .../org.gradle.api.plugins.quality.FindBugs.xml    |    8 +
 ...radle.api.plugins.quality.FindBugsExtension.xml |   10 +-
 ...s.quality.GroovyCodeQualityPluginConvention.xml |   39 -
 ...ins.quality.JavaCodeQualityPluginConvention.xml |   43 -
 .../dsl/org.gradle.api.plugins.quality.Pmd.xml     |    8 +
 ...org.gradle.api.plugins.quality.PmdExtension.xml |   10 +-
 ....gradle.api.publish.ivy.IvyModuleDescriptor.xml |   25 -
 ...dle.api.publish.ivy.IvyModuleDescriptorSpec.xml |   37 +
 .../dsl/org.gradle.api.publish.maven.MavenPom.xml  |    3 +
 ....publish.maven.tasks.AbstractPublishToMaven.xml |   25 +
 ...ublish.maven.tasks.PublishToMavenRepository.xml |    1 -
 ...le.api.reporting.components.ComponentReport.xml |   22 +
 ...org.gradle.api.reporting.model.ModelReport.xml} |    0
 .../org.gradle.api.resources.ResourceHandler.xml   |   47 +
 .../dsl/org.gradle.api.resources.TextResource.xml  |   47 +
 ...rg.gradle.api.resources.TextResourceFactory.xml |   47 +
 .../dsl/org.gradle.api.tasks.AbstractExecTask.xml  |   67 +
 .../src/docs/dsl/org.gradle.api.tasks.Exec.xml     |   47 +-
 ...le.api.tasks.application.CreateStartScripts.xml |   24 -
 .../docs/dsl/org.gradle.api.tasks.bundling.Jar.xml |   19 -
 ...org.gradle.api.tasks.compile.CompileOptions.xml |   16 +-
 ...adle.api.tasks.compile.GroovyCompileOptions.xml |   16 +-
 .../org.gradle.api.tasks.compile.JavaCompile.xml   |    4 +
 .../dsl/org.gradle.api.tasks.javadoc.Javadoc.xml   |    4 +
 .../org.gradle.api.tasks.scala.ScalaCompile.xml    |   14 +-
 ....gradle.api.tasks.scala.ScalaCompileOptions.xml |    4 -
 .../docs/dsl/org.gradle.api.tasks.testing.Test.xml |   12 -
 ...le.jvm.application.tasks.CreateStartScripts.xml |   26 +
 .../docs/src/docs/dsl/org.gradle.jvm.tasks.Jar.xml |   33 +
 .../dsl/org.gradle.jvm.toolchain.JavaToolChain.xml |   22 +
 .../dsl/org.gradle.language.DependentSourceSet.xml |    3 -
 ...l => org.gradle.language.PreprocessingTool.xml} |    0
 ...g.gradle.language.assembler.tasks.Assemble.xml} |    0
 ...ml => org.gradle.language.c.tasks.CCompile.xml} |    0
 ... org.gradle.language.cpp.plugins.CppPlugin.xml} |    0
 ...> org.gradle.language.cpp.tasks.CppCompile.xml} |    0
 ...veplatform.tasks.AbstractNativeCompileTask.xml} |    0
 ...anguage.objectivec.tasks.ObjectiveCCompile.xml} |    0
 ...age.objectivecpp.tasks.ObjectiveCppCompile.xml} |    0
 ...e.language.rc.tasks.WindowsResourceCompile.xml} |    0
 ...e.language.scala.tasks.AbstractScalaCompile.xml |   51 +
 ...g.gradle.nativebinaries.ProjectNativeBinary.xml |   68 -
 ...radle.nativebinaries.ProjectNativeComponent.xml |   53 -
 ...adle.nativebinaries.TargetedNativeComponent.xml |   48 -
 ...org.gradle.nativebinaries.platform.Platform.xml |   50 -
 ...tivebinaries.test.ProjectComponentTestSuite.xml |   41 -
 .../org.gradle.nativebinaries.toolchain.Gcc.xml    |   40 -
 ...ies.toolchain.PlatformConfigurableToolChain.xml |   44 -
 ...xml => org.gradle.nativeplatform.BuildType.xml} |    0
 ...g.gradle.nativeplatform.BuildTypeContainer.xml} |    0
 ...er.xml => org.gradle.nativeplatform.Flavor.xml} |    0
 ... org.gradle.nativeplatform.FlavorContainer.xml} |    0
 ... => org.gradle.nativeplatform.NativeBinary.xml} |    0
 .../org.gradle.nativeplatform.NativeBinarySpec.xml |   56 +
 ...dle.nativeplatform.NativeComponentExtension.xml |   50 +
 ...g.gradle.nativeplatform.NativeComponentSpec.xml |   41 +
 ...org.gradle.nativeplatform.NativeExecutable.xml} |    0
 ...adle.nativeplatform.NativeExecutableBinary.xml} |    0
 ....nativeplatform.NativeExecutableBinarySpec.xml} |    0
 ...gradle.nativeplatform.NativeExecutableSpec.xml} |    0
 ...=> org.gradle.nativeplatform.NativeLibrary.xml} |    0
 ....gradle.nativeplatform.NativeLibraryBinary.xml} |    0
 ...rg.gradle.nativeplatform.NativeLibrarySpec.xml} |    0
 ... org.gradle.nativeplatform.PrebuiltLibrary.xml} |    0
 ...nativeplatform.PrebuiltSharedLibraryBinary.xml} |    0
 ...nativeplatform.PrebuiltStaticLibraryBinary.xml} |    0
 ....gradle.nativeplatform.SharedLibraryBinary.xml} |    0
 ...adle.nativeplatform.SharedLibraryBinarySpec.xml |   44 +
 ....gradle.nativeplatform.StaticLibraryBinary.xml} |    0
 ...dle.nativeplatform.StaticLibraryBinarySpec.xml} |    0
 ...adle.nativeplatform.TargetedNativeComponent.xml |   45 +
 ...Tool.xml => org.gradle.nativeplatform.Tool.xml} |    0
 ....nativeplatform.platform.PlatformContainer.xml} |    0
 ...adle.nativeplatform.tasks.AbstractLinkTask.xml} |    0
 ...e.nativeplatform.tasks.CreateStaticLibrary.xml} |    0
 ...dle.nativeplatform.tasks.InstallExecutable.xml} |    0
 ...gradle.nativeplatform.tasks.LinkExecutable.xml} |    0
 ...dle.nativeplatform.tasks.LinkSharedLibrary.xml} |    0
 ...iveplatform.test.NativeTestSuiteBinarySpec.xml} |    0
 ...le.nativeplatform.test.NativeTestSuiteSpec.xml} |    0
 ...dle.nativeplatform.test.TestSuiteContainer.xml} |    0
 ...tiveplatform.test.cunit.CUnitTestSuiteSpec.xml} |    0
 ...orm.test.googletest.GoogleTestTestSuiteSpec.xml |   38 +
 ...nativeplatform.test.tasks.RunTestExecutable.xml |   22 +
 ... org.gradle.nativeplatform.toolchain.Clang.xml} |    0
 ...iveplatform.toolchain.ConfigurableToolChain.xml |   45 +
 ...=> org.gradle.nativeplatform.toolchain.Gcc.xml} |    0
 ...eplatform.toolchain.GccCompatibleToolChain.xml} |    0
 ...orm.toolchain.PlatformConfigurableToolChain.xml |   39 +
 ...nativeplatform.toolchain.ToolChainRegistry.xml} |    0
 ....gradle.nativeplatform.toolchain.VisualCpp.xml} |    0
 ...atform.toolchain.plugins.GppCompilerPlugin.xml} |    0
 ...toolchain.plugins.MicrosoftVisualCppPlugin.xml} |    0
 ...ml => org.gradle.platform.base.Application.xml} |    0
 ...ner.xml => org.gradle.platform.base.Binary.xml} |    0
 ...> org.gradle.platform.base.BinaryContainer.xml} |    0
 .../dsl/org.gradle.platform.base.BinarySpec.xml    |   50 +
 .../dsl/org.gradle.platform.base.ComponentSpec.xml |   50 +
 ...radle.platform.base.ComponentSpecContainer.xml} |    0
 ...er.xml => org.gradle.platform.base.Library.xml} |    0
 ...ml => org.gradle.platform.base.LibrarySpec.xml} |    0
 ...le.platform.base.PlatformAwareComponentSpec.xml |   41 +
 ...rg.gradle.plugin.use.PluginDependenciesSpec.xml |   41 +
 .../org.gradle.plugin.use.PluginDependencySpec.xml |   41 +
 ...lugins.ide.eclipse.GenerateEclipseClasspath.xml |   40 +-
 ...adle.plugins.ide.eclipse.GenerateEclipseJdt.xml |    4 +
 ....plugins.ide.eclipse.GenerateEclipseProject.xml |    6 +-
 ...ins.ide.eclipse.GenerateEclipseWtpComponent.xml |   53 +-
 ...plugins.ide.eclipse.GenerateEclipseWtpFacet.xml |    4 +-
 ....gradle.plugins.ide.idea.GenerateIdeaModule.xml |    5 +
 ...gradle.plugins.ide.idea.GenerateIdeaProject.xml |    5 +
 ...adle.plugins.ide.idea.GenerateIdeaWorkspace.xml |    4 +
 ...g.gradle.plugins.ide.idea.model.IdeaProject.xml |    5 +
 .../org.gradle.sonar.runner.SonarProperties.xml    |   48 +
 ...rg.gradle.sonar.runner.SonarRunnerExtension.xml |   46 +
 ...radle.sonar.runner.SonarRunnerRootExtension.xml |   50 +
 .../org.gradle.sonar.runner.tasks.SonarRunner.xml  |   47 +
 ...esting.jacoco.plugins.JacocoPluginExtension.xml |    2 +-
 subprojects/docs/src/docs/dsl/plugins.xml          |   36 +-
 .../docs/src/docs/release/content/script.js        |    7 +-
 subprojects/docs/src/docs/release/notes-next.md    |  113 +
 .../docs/src/docs/release/notes-template.md        |    6 +-
 subprojects/docs/src/docs/release/notes.md         |  544 ++-
 .../docs/src/docs/userguide/announcePlugin.xml     |    4 +-
 subprojects/docs/src/docs/userguide/ant.xml        |   17 +-
 .../docs/src/docs/userguide/antlrPlugin.xml        |   28 +-
 .../docs/src/docs/userguide/applicationPlugin.xml  |  150 +-
 .../userguide/artifactDependenciesTutorial.xml     |   14 +-
 .../docs/src/docs/userguide/artifactMngmt.xml      |   12 +-
 .../docs/userguide/buildAnnouncementsPlugin.xml    |    2 +-
 .../src/docs/userguide/buildDashboardPlugin.xml    |    6 +-
 .../docs/src/docs/userguide/buildEnvironment.xml   |  115 +-
 .../docs/src/docs/userguide/buildInitPlugin.xml    |   54 +-
 .../docs/src/docs/userguide/buildLifecycle.xml     |   81 +-
 .../src/docs/userguide/buildScriptsTutorial.xml    |   31 +-
 .../docs/src/docs/userguide/checkstylePlugin.xml   |    4 +-
 .../docs/src/docs/userguide/codeNarcPlugin.xml     |    4 +-
 .../docs/src/docs/userguide/commandLine.xml        |   84 +-
 .../src/docs/userguide/commandLineTutorial.xml     |   19 +-
 .../docs/src/docs/userguide/comparingBuilds.xml    |   12 +-
 .../docs/src/docs/userguide/customPlugins.xml      |   96 +-
 .../docs/src/docs/userguide/customTasks.xml        |   14 +-
 subprojects/docs/src/docs/userguide/depMngmt.xml   |  556 ++-
 .../docs/src/docs/userguide/distributionPlugin.xml |  100 +-
 subprojects/docs/src/docs/userguide/earPlugin.xml  |   18 +-
 .../docs/src/docs/userguide/eclipsePlugin.xml      |  142 +-
 subprojects/docs/src/docs/userguide/embedding.xml  |   38 +-
 .../docs/src/docs/userguide/featureLifecycle.xml   |   12 +-
 .../docs/src/docs/userguide/findBugsPlugin.xml     |    4 +-
 .../docs/src/docs/userguide/gradleDaemon.xml       |  311 +-
 .../docs/src/docs/userguide/gradleWrapper.xml      |   32 +-
 .../docs/src/docs/userguide/groovyPlugin.xml       |   21 +-
 .../docs/src/docs/userguide/groovyTutorial.xml     |    8 +-
 .../docs/src/docs/userguide/guiTutorial.xml        |   16 +-
 subprojects/docs/src/docs/userguide/ideaPlugin.xml |   42 +-
 .../userguide/img/codeQualityPluginTasks.graphml   |  135 -
 .../docs/userguide/img/codeQualityPluginTasks.png  |  Bin 9278 -> 0 bytes
 .../userguide/img/commandLineTutorialTasks.graphml |  245 +-
 .../userguide/img/commandLineTutorialTasks.png     |  Bin 5469 -> 4220 bytes
 .../docs/userguide/img/groovyPluginTasks.graphml   |  439 +-
 .../src/docs/userguide/img/groovyPluginTasks.png   |  Bin 17912 -> 13717 bytes
 .../src/docs/userguide/img/javaPluginTasks.graphml |  630 ++-
 .../src/docs/userguide/img/javaPluginTasks.png     |  Bin 25445 -> 21111 bytes
 .../docs/userguide/img/jettyPluginTasks.graphml    |  204 +-
 .../src/docs/userguide/img/jettyPluginTasks.png    |  Bin 5898 -> 5692 bytes
 .../docs/userguide/img/scalaPluginTasks.graphml    |  439 +-
 .../src/docs/userguide/img/scalaPluginTasks.png    |  Bin 17262 -> 13331 bytes
 .../src/docs/userguide/img/warPluginTasks.graphml  |  139 +-
 .../docs/src/docs/userguide/img/warPluginTasks.png |  Bin 2113 -> 1782 bytes
 .../docs/src/docs/userguide/initscripts.xml        |   15 +-
 .../docs/src/docs/userguide/installation.xml       |   20 +-
 .../docs/src/docs/userguide/introduction.xml       |    3 +
 .../docs/src/docs/userguide/jacocoPlugin.xml       |    4 +-
 .../docs/src/docs/userguide/javaGradlePlugin.xml   |   55 +
 .../userguide/javaLibraryDistributionPlugin.xml    |   10 +-
 subprojects/docs/src/docs/userguide/javaPlugin.xml |  136 +-
 .../docs/userguide/javaProjectGenericLayout.xml    |    2 +
 .../src/docs/userguide/javaProjectMainLayout.xml   |    2 +
 .../src/docs/userguide/javaProjectTestLayout.xml   |    2 +
 .../docs/src/docs/userguide/javaTutorial.xml       |   33 +-
 .../docs/src/docs/userguide/jdependPlugin.xml      |    4 +-
 .../docs/src/docs/userguide/jettyPlugin.xml        |    2 +-
 subprojects/docs/src/docs/userguide/logging.xml    |    2 +-
 .../docs/src/docs/userguide/mavenPlugin.xml        |   38 +-
 .../docs/src/docs/userguide/multiproject.xml       |  284 +-
 .../docs/src/docs/userguide/nativeBinaries.xml     |  436 +-
 subprojects/docs/src/docs/userguide/newModel.xml   |  370 ++
 .../docs/src/docs/userguide/organizeBuildLogic.xml |   34 +-
 subprojects/docs/src/docs/userguide/osgi.xml       |    7 +-
 subprojects/docs/src/docs/userguide/overview.xml   |   29 +-
 subprojects/docs/src/docs/userguide/plugins.xml    |  289 +-
 subprojects/docs/src/docs/userguide/pmdPlugin.xml  |    4 +-
 .../docs/src/docs/userguide/potentialTraps.xml     |    2 +-
 .../docs/src/docs/userguide/projectReports.xml     |    9 +-
 .../docs/src/docs/userguide/publishingIvy.xml      |   66 +-
 .../docs/src/docs/userguide/publishingMaven.xml    |   32 +-
 .../docs/src/docs/userguide/scalaPlugin.xml        |   27 +-
 .../docs/src/docs/userguide/signingPlugin.xml      |   18 +-
 .../docs/src/docs/userguide/sonarRunnerPlugin.xml  |   96 +-
 .../docs/src/docs/userguide/standardPlugins.xml    |   15 +-
 subprojects/docs/src/docs/userguide/tasks.xml      |   99 +-
 .../docs/src/docs/userguide/thisAndThat.xml        |  132 -
 .../docs/src/docs/userguide/troubleshooting.xml    |    4 +-
 subprojects/docs/src/docs/userguide/userguide.xml  |    5 +-
 subprojects/docs/src/docs/userguide/warPlugin.xml  |   14 +-
 .../docs/src/docs/userguide/webTutorial.xml        |    9 +-
 .../docs/src/docs/userguide/workingWithFiles.xml   |   16 +-
 .../docs/src/docs/userguide/wrapperPlugin.xml      |    6 +-
 .../src/docs/userguide/writingBuildScripts.xml     |   48 +-
 subprojects/docs/src/samples/antlr/build.gradle    |   11 +-
 .../antlr/src/main/antlr/org/gradle/Calculator.g   |   23 +-
 .../src/test/java/org/gradle/GrammarTest.java      |    9 +-
 .../docs/src/samples/application/build.gradle      |   14 +-
 .../docs/src/samples/buildDashboard/build.gradle   |    2 +-
 .../samples/clientModuleDependencies/build.gradle  |    2 +-
 .../docs/src/samples/codeQuality/build.gradle      |    2 +-
 .../samples/componentMetadataRules/build.gradle    |   98 +
 .../repo/org.sample/api/1.9/api-1.9.jar}           |    0
 .../repo/org.sample/api/1.9/ivy-1.9.xml            |    9 +
 .../repo/org.sample/api/2.0/api-2.0.jar}           |    0
 .../repo/org.sample/api/2.0/ivy-2.0.xml            |    9 +
 .../repo/org.sample/client/1.3/client-1.3.jar}     |    0
 .../repo/org.sample/client/1.3/ivy-1.3.xml         |    9 +
 .../repo/org.sample/client/1.4/client-1.4.jar}     |    0
 .../repo/org.sample/client/1.4/ivy-1.4.xml         |    9 +
 .../repo/org.sample/client/1.5/client-1.5.jar}     |    0
 .../repo/org.sample/client/1.5/ivy-1.5.xml         |    9 +
 .../repo/org.sample/lib/1.9/ivy-1.9.xml            |   25 +
 .../repo/org.sample/lib/1.9/lib-1.9.jar}           |    0
 .../repo/org.sample/lib/2.0/ivy-2.0.xml            |   27 +
 .../repo/org.sample/lib/2.0/lib-2.0.jar}           |    0
 .../samples/componentSelectionRules/build.gradle   |  173 +
 .../repo/org.sample/api/1.3.0/api-1.3.0.jar}       |    0
 .../repo/org.sample/api/1.3.0/ivy-1.3.0.xml        |   25 +
 .../repo/org.sample/api/1.4/api-1.4.jar}           |    0
 .../repo/org.sample/api/1.4/ivy-1.4.xml            |   25 +
 .../repo/org.sample/api/1.5/api-1.5.jar}           |    0
 .../repo/org.sample/api/1.5/ivy-1.5.xml            |   27 +
 .../repo/org.sample/lib/1.9/ivy-1.9.xml            |   26 +
 .../repo/org.sample/lib/1.9/lib-1.9.jar}           |    0
 .../repo/org.sample/lib/2.0/ivy-2.0.xml            |   26 +
 .../repo/org.sample/lib/2.0/lib-2.0.jar}           |    0
 .../samples/customModel/componentType/build.gradle |  103 +
 .../samples/customModel/languageType/build.gradle  |   32 +
 .../customModel/languageType/buildSrc/build.gradle |   27 +
 .../DefaultDocumentationBinary.groovy              |   21 +
 .../DefaultDocumentationComponent.groovy           |   21 +
 .../documentation/DocumentationBinary.groovy       |   21 +
 .../documentation/DocumentationComponent.groovy    |   21 +
 .../documentation/DocumentationPlugin.groovy       |   59 +
 .../documentation/DocumentationSourceSet.groovy    |   29 +
 .../markdown/DefaultMarkdownSourceSet.groovy       |   24 +
 .../sample/markdown/MarkdownHtmlCompile.groovy     |   60 +
 .../groovy/sample/markdown/MarkdownPlugin.groovy   |   47 +
 .../sample/markdown/MarkdownSourceSet.groovy       |   29 +
 .../languageType/src/docs/userguide/chapter1.md    |    9 +
 .../languageType/src/docs/userguide/chapter2.md    |    4 +
 .../src/samples/customPlugin/consumer/build.gradle |    5 +-
 .../src/samples/customPlugin/plugin/build.gradle   |    2 +-
 ....properties => org.samples.greeting.properties} |    0
 .../groovy/org/gradle/GreetingPluginTest.groovy    |    2 +-
 .../src/samples/ear/earCustomized/ear/build.gradle |   19 +-
 subprojects/docs/src/samples/eclipse/build.gradle  |    7 +-
 .../samples/groovy/customizedLayout/build.gradle   |    4 +-
 .../samples/groovy/mixedJavaAndGroovy/build.gradle |    4 +-
 .../groovy/multiproject/buildSrc/build.gradle      |    2 +-
 .../multiproject/groovycDetector/build.gradle      |    2 +-
 .../groovy/multiproject/testproject/build.gradle   |    4 +-
 .../src/test/groovy/org/gradle/VersionTest.groovy  |    6 +-
 .../src/samples/groovy/quickstart/build.gradle     |    4 +-
 .../src/test/groovy/org/gradle/PersonTest.groovy   |    2 +-
 subprojects/docs/src/samples/idea/build.gradle     |    4 +-
 .../descriptor-customization/build.gradle          |    6 +-
 .../ivy-publish/java-multi-project/build.gradle    |    5 +-
 .../ivy-publish/java-multi-project/output-ivy.xml  |    2 +-
 .../ivy-publish/multiple-publications/build.gradle |    4 +-
 .../multiple-publications/output/project1.ivy.xml  |    6 +-
 .../samples/ivy-publish/quickstart/build.gradle    |    3 +-
 .../docs/src/samples/ivypublish/build.gradle       |    4 +-
 .../docs/src/samples/java/apiAndImpl/build.gradle  |    2 +-
 .../docs/src/samples/java/base/test/build.gradle   |    2 +-
 .../src/samples/java/customizedLayout/build.gradle |    2 +-
 .../src/samples/java/multiproject/build.gradle     |    2 +-
 .../java/multiproject/buildSrc/build.gradle        |    2 +-
 .../docs/src/samples/java/onlyif/build.gradle      |    2 +-
 .../docs/src/samples/java/quickstart/build.gradle  |    3 +-
 .../src/samples/java/testListener/build.gradle     |    6 +-
 .../src/test/java/org/gradle/DoNothingTest.java    |    2 +
 .../samples/java/withIntegrationTests/build.gradle |    4 +-
 .../docs/src/samples/javaGradlePlugin/build.gradle |   19 +
 .../docs/src/samples/javaGradlePlugin/readme.xml   |    7 +
 .../main/java/org/gradle/sample/SimplePlugin.java  |   26 +
 .../gradle-plugins/simple-plugin.properties        |    1 +
 .../src/samples/jvmComponents/java/build.gradle    |   26 +
 .../main/java/org/gradle/samples/HelloWorld.java   |    7 +
 .../src/samples/jvmComponents/scala/build.gradle   |   29 +
 .../main/scala/org/gradle/samples/Greeter.scala    |   22 +
 .../main/scala/org/gradle/samples/HelloWorld.scala |   22 +
 .../src/main/scala/org/gradle/samples/Person.java  |   15 +
 .../multiple-publications/build.gradle             |    2 +-
 .../multiple-publications/output/project1.pom.xml  |    2 +-
 .../maven-publish/pomCustomization/build.gradle    |    6 +-
 .../samples/maven-publish/quickstart/build.gradle  |    9 +-
 .../src/samples/maven/pomGeneration/build.gradle   |   16 +-
 .../modelRules/basicRuleSourcePlugin/build.gradle  |   43 +
 .../src/samples/modelRules/modelDsl/build.gradle   |   28 +
 .../samples/native-binaries/assembler/build.gradle |   46 +-
 .../src/samples/native-binaries/c/build.gradle     |   25 +-
 .../native-binaries/c/src/hello/headers/hello.h    |    2 +-
 .../samples/native-binaries/cpp-exe/build.gradle   |   20 +-
 .../samples/native-binaries/cpp-lib/build.gradle   |   12 +-
 .../native-binaries/cpp-lib/src/main/cpp/hello.cpp |   10 +-
 .../cpp-lib/src/main/headers/hello.h               |    4 +-
 .../src/samples/native-binaries/cpp/build.gradle   |   29 +-
 .../native-binaries/cpp/src/hello/cpp/hello.cpp    |    4 +-
 .../native-binaries/cpp/src/hello/headers/hello.h  |    7 +-
 .../native-binaries/cpp/src/main/cpp/main.cpp      |    5 +-
 .../src/samples/native-binaries/cunit/build.gradle |   22 +-
 .../lib/cunit/2.1-2/include/CUnit/Automated.h      |   90 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/Basic.h    |  113 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/CUError.h  |  199 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/CUnit.h    |  383 --
 .../lib/cunit/2.1-2/include/CUnit/CUnit_intl.h     |   62 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/Console.h  |   60 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/MyMem.h    |  104 -
 .../cunit/lib/cunit/2.1-2/include/CUnit/TestDB.h   |  914 ----
 .../cunit/lib/cunit/2.1-2/include/CUnit/TestRun.h  |  444 --
 .../cunit/lib/cunit/2.1-2/include/CUnit/Util.h     |  158 -
 .../cunit/lib/cunit/2.1-2/lib/cygwin/cunit.lib     |  Bin 81868 -> 0 bytes
 .../cunit/lib/cunit/2.1-2/lib/linux/libcunit.a     |  Bin 117622 -> 0 bytes
 .../cunit/lib/cunit/2.1-2/lib/mingw/cunit.lib      |  Bin 75800 -> 0 bytes
 .../cunit/lib/cunit/2.1-2/lib/osx/libcunit.a       |  Bin 85768 -> 0 bytes
 .../cunit/lib/cunit/2.1-2/lib/vs2010/cunit.lib     |  Bin 117792 -> 0 bytes
 .../cunit/lib/cunit/2.1-2/lib/vs2013/cunit.lib     |  Bin 116230 -> 0 bytes
 .../libs/cunit/2.1-2/include/CUnit/Automated.h     |    0
 .../cunit}/libs/cunit/2.1-2/include/CUnit/Basic.h  |    0
 .../libs/cunit/2.1-2/include/CUnit/CUError.h       |    0
 .../cunit}/libs/cunit/2.1-2/include/CUnit/CUnit.h  |    0
 .../libs/cunit/2.1-2/include/CUnit/CUnit_intl.h    |    0
 .../libs/cunit/2.1-2/include/CUnit/Console.h       |    0
 .../cunit}/libs/cunit/2.1-2/include/CUnit/MyMem.h  |    0
 .../cunit}/libs/cunit/2.1-2/include/CUnit/TestDB.h |    0
 .../libs/cunit/2.1-2/include/CUnit/TestRun.h       |    0
 .../cunit}/libs/cunit/2.1-2/include/CUnit/Util.h   |    0
 .../cunit}/libs/cunit/2.1-2/lib/cygwin/cunit.lib   |  Bin
 .../cunit/libs/cunit/2.1-2/lib/linux/libcunit.a    |  Bin 0 -> 80426 bytes
 .../cunit}/libs/cunit/2.1-2/lib/mingw/cunit.lib    |  Bin
 .../cunit/libs/cunit/2.1-2/lib/osx/libcunit.a      |  Bin 0 -> 97232 bytes
 .../cunit}/libs/cunit/2.1-2/lib/vs2010/cunit.lib   |  Bin
 .../cunit}/libs/cunit/2.1-2/lib/vs2013/cunit.lib   |  Bin
 .../operatorsTest/{cunit => c}/suite_operators.c   |    0
 .../src/operatorsTest/{cunit => c}/test_minus.c    |    0
 .../src/operatorsTest/{cunit => c}/test_plus.c     |    0
 .../src/operatorsTest/headers/test_operators.h     |    4 +-
 .../native-binaries/custom-layout/build.gradle     |   62 +-
 .../custom-layout/src/include/hello.h              |    2 +-
 .../samples/native-binaries/flavors/build.gradle   |   33 +-
 .../flavors/src/{lib => hello}/cpp/hello.cpp       |    0
 .../flavors/src/hello/headers/hello.h              |    7 +
 .../flavors/src/lib/headers/hello.h                |   10 -
 .../flavors/src/{exe => main}/cpp/main.cpp         |    0
 .../samples/native-binaries/google-test/README.md  |    7 +
 .../native-binaries/google-test/build.gradle       |   59 +
 .../1.7.0/include/gtest/gtest-death-test.h         |  294 ++
 .../googleTest/1.7.0/include/gtest/gtest-message.h |  250 +
 .../1.7.0/include/gtest/gtest-param-test.h         | 1421 ++++++
 .../1.7.0/include/gtest/gtest-param-test.h.pump    |  487 ++
 .../1.7.0/include/gtest/gtest-printers.h           |  855 ++++
 .../googleTest/1.7.0/include/gtest/gtest-spi.h     |  232 +
 .../1.7.0/include/gtest/gtest-test-part.h          |  179 +
 .../1.7.0/include/gtest/gtest-typed-test.h         |  259 +
 .../libs/googleTest/1.7.0/include/gtest/gtest.h    | 2291 +++++++++
 .../1.7.0/include/gtest/gtest_pred_impl.h          |  358 ++
 .../googleTest/1.7.0/include/gtest/gtest_prod.h    |   58 +
 .../gtest/internal/gtest-death-test-internal.h     |  319 ++
 .../1.7.0/include/gtest/internal/gtest-filepath.h  |  206 +
 .../1.7.0/include/gtest/internal/gtest-internal.h  | 1158 +++++
 .../include/gtest/internal/gtest-linked_ptr.h      |  233 +
 .../gtest/internal/gtest-param-util-generated.h    | 5143 ++++++++++++++++++++
 .../internal/gtest-param-util-generated.h.pump     |  301 ++
 .../include/gtest/internal/gtest-param-util.h      |  619 +++
 .../1.7.0/include/gtest/internal/gtest-port.h      | 1947 ++++++++
 .../1.7.0/include/gtest/internal/gtest-string.h    |  167 +
 .../1.7.0/include/gtest/internal/gtest-tuple.h     | 1012 ++++
 .../include/gtest/internal/gtest-tuple.h.pump      |  339 ++
 .../1.7.0/include/gtest/internal/gtest-type-util.h | 3331 +++++++++++++
 .../include/gtest/internal/gtest-type-util.h.pump  |  297 ++
 .../libs/googleTest/1.7.0/lib/cygwin/gtest.lib     |  Bin 0 -> 1143180 bytes
 .../libs/googleTest/1.7.0/lib/linux/libgtest.a     |  Bin 0 -> 932906 bytes
 .../libs/googleTest/1.7.0/lib/mingw/gtest.lib      |  Bin 0 -> 1108082 bytes
 .../libs/googleTest/1.7.0/lib/osx/libgtest.a       |  Bin 0 -> 801248 bytes
 .../libs/googleTest/1.7.0/lib/vs2010/gtest.lib     |  Bin 0 -> 1316096 bytes
 .../libs/googleTest/1.7.0/lib/vs2013/gtest.lib     |  Bin 0 -> 1356916 bytes
 .../google-test/src/operators/cpp/minus.cpp        |    5 +
 .../google-test/src/operators/cpp/plus.cpp         |    9 +
 .../google-test/src/operators/headers/operators.h  |    2 +
 .../src/operatorsTest/cpp/test_main.cpp            |    8 +
 .../src/operatorsTest/cpp/test_minus.cpp           |   10 +
 .../src/operatorsTest/cpp/test_plus.cpp            |   10 +
 .../src/samples/native-binaries/idl/build.gradle   |   18 +-
 .../native-binaries/multi-project/build.gradle     |   29 +-
 .../multi-project/lib/src/main/cpp/hello.cpp       |    6 +-
 .../multi-project/lib/src/main/headers/hello.h     |    4 +-
 .../native-binaries/objective-c/build.gradle       |   16 +-
 .../objective-c/src/main/headers/greeter.h         |    5 +
 .../objective-c/src/main/objc/greeter.m            |   10 +
 .../objective-c/src/main/objc/main.m               |   14 +-
 .../native-binaries/objective-cpp/build.gradle     |   16 +-
 .../objective-cpp/src/main/headers/greeter.h       |    7 +
 .../objective-cpp/src/main/objcpp/greeter.mm       |   11 +
 .../objective-cpp/src/main/objcpp/main.mm          |   16 +-
 .../prebuilt/3rd-party-lib/util/build.gradle       |   25 +-
 .../samples/native-binaries/prebuilt/build.gradle  |   17 +-
 .../native-binaries/target-platforms/build.gradle  |   49 +
 .../target-platforms/src/main/cpp/main.cpp         |   18 +
 .../native-binaries/tool-chains/build.gradle       |   38 +-
 .../samples/native-binaries/variants/build.gradle  |   29 +-
 .../variants/src/hello/headers/hello.h             |    7 +-
 .../native-binaries/visual-studio/build.gradle     |   17 +-
 .../visual-studio/src/hello/headers/hello.h        |    2 +-
 .../build-resource-only-dll.gradle                 |   38 +-
 .../native-binaries/windows-resources/build.gradle |   23 +-
 .../windows-resources/src/hello/cpp/hello.cpp      |    2 +-
 .../windows-resources/src/hello/headers/hello.h    |    8 +-
 subprojects/docs/src/samples/osgi/build.gradle     |    2 +-
 .../app/assets/coffeescript/console.coffee         |    1 +
 .../app/assets/coffeescript/squareit.coffee        |    7 +
 .../play/advanced/app/assets/javascripts/sample.js |    9 +
 .../advanced/app/controllers/Application.scala     |   16 +
 .../app/controllers/QuestionsThreeController.scala |   34 +
 .../app/controllers/hello/HelloController.java     |   30 +
 .../samples/play/advanced/app/models/Person.scala  |    7 +
 .../play/advanced/app/views/fields.scala.html      |    9 +
 .../play/advanced/app/views/index.scala.html       |    7 +
 .../play/advanced/app/views/main.scala.html        |   16 +
 .../play/advanced/app/views/pass.scala.html        |   13 +
 .../play/advanced/app/views/person.scala.html      |   29 +
 .../play/advanced/app/views/square.scala.html      |   23 +
 .../docs/src/samples/play/advanced/build.gradle    |   28 +
 .../samples/play/advanced/conf/application.conf    |   62 +
 .../src/samples/play/advanced/conf/hello.routes    |    1 +
 .../docs/src/samples/play/advanced/conf/routes     |   19 +
 .../play/advanced/public/images/favicon.png        |  Bin 0 -> 687 bytes
 .../play/advanced/public/javascripts/hello.js      |    3 +
 .../public/javascripts/jquery-2.1.3.min.js         |    4 +
 .../advanced/public/stylesheets/bootstrap.min.css  |    9 +
 .../play/advanced/public/stylesheets/main.css}     |    0
 .../play/advanced/test/ApplicationSpec.scala       |   30 +
 .../play/advanced/test/IntegrationSpec.scala       |   24 +
 .../play/basic/app/controllers/Application.scala   |   14 +
 .../samples/play/basic/app/views/index.scala.html  |    7 +
 .../samples/play/basic/app/views/main.scala.html   |   15 +
 .../docs/src/samples/play/basic/build.gradle       |   16 +
 .../src/samples/play/basic/conf/application.conf   |   62 +
 .../docs/src/samples/play/basic/conf/routes        |    9 +
 .../samples/play/basic/public/images/favicon.png   |  Bin 0 -> 687 bytes
 .../samples/play/basic/public/javascripts/hello.js |    3 +
 .../play/basic/public/stylesheets/main.css}        |    0
 .../samples/play/basic/test/ApplicationSpec.scala  |   41 +
 .../samples/play/basic/test/IntegrationSpec.scala  |   24 +
 .../multiproject/app/controllers/Application.scala |   12 +
 .../play/multiproject/app/views/index.scala.html   |   19 +
 .../src/samples/play/multiproject/build.gradle     |   19 +
 .../play/multiproject/conf/application.conf        |   28 +
 .../docs/src/samples/play/multiproject/conf/routes |   11 +
 .../admin/app/controllers/admin/Application.scala  |   12 +
 .../admin/app/controllers/admin/Assets.scala       |    4 +
 .../modules/admin/app/views/admin/index.scala.html |   14 +
 .../play/multiproject/modules/admin/build.gradle   |    7 +
 .../multiproject/modules/admin/conf/admin.routes   |    2 +
 .../modules/admin/conf/application.conf            |    3 +
 .../modules/admin/public/javascript/admin.js       |    4 +
 .../user/app/controllers/user/Application.scala    |   12 +
 .../modules/user/app/views/user/index.scala.html   |   12 +
 .../play/multiproject/modules/user/build.gradle    |    7 +
 .../modules/user/conf/application.conf             |    3 +
 .../multiproject/modules/user/conf/user.routes     |    1 +
 .../play/multiproject/modules/util/build.gradle    |    3 +
 .../src/main/java/org/sample/util/BuiltBy.java     |    7 +
 .../play/multiproject/public/images/gradle.ico     |  Bin 0 -> 25051 bytes
 .../multiproject/public/javascript/timestamp.js    |    4 +
 .../src/samples/play/multiproject/settings.gradle  |    5 +
 .../src/samples/plugins/buildscript/build.gradle   |   30 +
 .../docs/src/samples/plugins/dsl/build.gradle      |   29 +
 .../samples/scala/customizedLayout/build.gradle    |    4 +-
 .../docs/src/samples/scala/fsc/build.gradle        |    4 +-
 .../samples/scala/mixedJavaAndScala/build.gradle   |    4 +-
 .../docs/src/samples/scala/quickstart/build.gradle |    4 +-
 .../docs/src/samples/scala/zinc/build.gradle       |    4 +-
 .../src/samples/sonarRunner/advanced/build.gradle  |    8 +
 .../advanced/src/main/java/org/gradle/Person.java  |   32 +
 .../src/test/java/org/gradle/PersonTest.java       |   28 +
 .../samples/sonarRunner/quickstart/build.gradle    |    6 +
 .../src/samples/testing/filtering/build.gradle     |    2 +-
 .../samples/testing/jacoco/quickstart/build.gradle |    2 +-
 .../samples/testing/junit/categories/build.gradle  |    2 +-
 .../src/samples/testing/testReport/build.gradle    |    2 +-
 .../toolingApi/customModel/tooling/build.gradle    |    4 +-
 .../src/samples/toolingApi/eclipse/build.gradle    |    4 +-
 .../docs/src/samples/toolingApi/idea/build.gradle  |    4 +-
 .../docs/src/samples/toolingApi/model/build.gradle |    4 +-
 .../src/samples/toolingApi/runBuild/build.gradle   |    4 +-
 .../src/main/java/org/gradle/sample/Main.java      |    6 +-
 .../samples/userguide/ant/renameTask/build.gradle  |    3 +
 .../src/samples/userguide/ant/renameTask/build.xml |    5 +
 .../ant/useExternalAntTaskWithConfig/build.gradle  |    8 +-
 .../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/componentModuleMetadata/build.gradle |   11 +
 .../artifacts/configurationHandling/build.gradle   |   25 +-
 .../repo/air.birds/albatross-1.0.jar}              |    0
 .../artifacts/defineRepository/build.gradle        |   60 +-
 .../artifacts/externalDependencies/build.gradle    |   23 +-
 .../artifacts/resolutionStrategy/build.gradle      |   20 +-
 .../userguide/buildlifecycle/basic/build.gradle    |   10 +
 .../projectEvaluateEvents/build.gradle             |    2 +-
 .../buildlifecycle/taskCreationEvents/build.gradle |    2 +-
 .../samples/userguide/distribution/build.gradle    |   13 +
 .../src/samples/userguide/files/copy/build.gradle  |    8 +-
 .../userguide/files/fileCollections/build.gradle   |    4 +-
 .../userguide/groovy/groovyDependency/build.gradle |    2 +-
 .../userguide/initScripts/plugins/init.gradle      |    7 +-
 .../samples/userguide/java/sourceSets/build.gradle |    2 +-
 .../multiproject/dependencies/java/build.gradle    |    2 +-
 .../dependencies/javaWithCustomConf/build.gradle   |    2 +-
 .../messages/consumer/build.gradle                 |    2 +-
 .../messages/consumer/build.gradle                 |    2 +-
 .../multiproject/dependencies/webDist/build.gradle |   13 +-
 .../multiproject/partialTasks/water/build.gradle   |    4 +-
 .../partialTasks/water/krill/build.gradle          |    4 +-
 .../spreadSpecifics/water/bluewhale/build.gradle   |    4 +-
 .../spreadSpecifics/water/krill/build.gradle       |    2 +-
 .../water/bluewhale/build.gradle                   |    4 +-
 .../userguide/organizeBuildLogic/build.gradle      |    2 +-
 .../organizeBuildLogic/inherited/build.gradle      |    4 +-
 .../organizeBuildLogic/injected/build.gradle       |   12 +-
 .../userguide/scala/scalaDependency/build.gradle   |    2 +-
 .../tasks/customTaskWithFileProperty/build.gradle  |    2 +-
 .../userguide/tasks/incrementalTask/build.gradle   |    3 +-
 .../tutorial/configureObject/build.gradle          |    2 +-
 .../configureObjectUsingScript/build.gradle        |    2 +-
 .../configureObjectUsingScript/other.gradle        |    5 +-
 .../samples/userguide/tutorial/groovy/build.gradle |   14 +-
 .../tutorial/groovyWithFlatDir/build.gradle        |    7 +-
 .../userguide/tutorial/makeDirectory/build.gradle  |   11 -
 .../userguide/tutorial/manifest/build.gradle       |    9 +-
 .../userguide/tutorial/mkdirTrap/build.gradle      |    2 +-
 .../userguide/tutorial/pluginIntro/build.gradle    |    5 +-
 .../userguide/tutorial/projectReports/build.gradle |    4 +-
 .../tutorial/properties/gradle.properties          |    2 +-
 .../src/samples/userguide/tutorial/scope.groovy    |   12 +-
 .../tutorial/stopExecutionException/build.gradle   |    3 +-
 .../samples/userguide/wrapper/simple/build.gradle  |    2 +-
 .../userguideOutput/basicRuleSourcePlugin-all.out  |    6 +
 .../basicRuleSourcePlugin-model-task.out           |   10 +
 .../src/samples/userguideOutput/buildlifecycle.out |    4 +
 .../userguideOutput/completeCUnitExample.out       |    5 +
 .../userguideOutput/completeGoogleTestExample.out  |   22 +
 .../userguideOutput/configurationHandlingCopy.out  |    4 +-
 .../configurationHandlingDependencies.out          |    6 +-
 .../samples/userguideOutput/customStatusScheme.out |    1 -
 .../userguideOutput/dependencyInsightReport.out    |    2 +-
 .../userguideOutput/dependencyListReport.out       |    6 +-
 .../dependencyListReportFiltered.out               |    2 +-
 .../src/samples/userguideOutput/latestSelector.out |    4 +-
 .../src/samples/userguideOutput/makeDirectory.out  |    1 -
 .../src/samples/userguideOutput/modelDslCreate.out |    6 +
 .../userguideOutput/nativeComponentReport.out      |   51 +
 .../samples/userguideOutput/propertyListReport.out |    3 +-
 .../samples/userguideOutput/renameAntDelegate.out  |    6 +
 .../docs/src/samples/userguideOutput/taskHelp.out  |    2 +
 .../samples/userguideOutput/taskListAllReport.out  |   20 +-
 .../src/samples/userguideOutput/taskListReport.out |    8 +-
 .../userguideOutput/usePluginsInInitScripts.out    |    2 +-
 .../samples/userguideOutput/wrapperCommandLine.out |    5 +
 .../docs/src/samples/water/bluewhale/build.gradle  |    7 -
 subprojects/docs/src/samples/water/build.gradle    |   16 -
 .../docs/src/samples/water/krill/build.gradle      |    7 -
 .../src/samples/water/phytoplankton/build.gradle   |    5 -
 subprojects/docs/src/samples/water/settings.gradle |    1 -
 .../samples/webApplication/customized/build.gradle |    2 +-
 .../releasenotes/FunctionalReleaseNotesTest.groovy |   26 +-
 .../docs/src/transforms/release-notes.gradle       |    2 +-
 .../plugins/ear/EarPluginIntegrationTest.groovy    |  198 +-
 .../org/gradle/plugins/ear/application_1_3.dtd     |  251 +
 .../main/groovy/org/gradle/plugins/ear/Ear.groovy  |   22 +-
 .../groovy/org/gradle/plugins/ear/EarPlugin.java   |   12 +-
 .../gradle/plugins/ear/EarPluginConvention.groovy  |   19 +-
 .../ear/descriptor/DeploymentDescriptor.java       |    8 +
 .../internal/DefaultDeploymentDescriptor.groovy    |   52 +-
 .../internal/DefaultEarSecurityRole.groovy         |    3 +
 .../{ear.properties => org.gradle.ear.properties}  |    0
 .../org/gradle/plugins/ear/EarPluginTest.groovy    |   50 +-
 .../groovy/org/gradle/plugins/ear/EarTest.groovy   |   29 +-
 .../DefaultDeploymentDescriptorTest.groovy         |   18 +-
 subprojects/ide-native/ide-native.gradle           |   28 +
 ...utoTestedSamplesIdeNativeIntegrationTest.groovy |   31 +
 .../NativeIdeSamplesIntegrationTest.groovy         |   51 +
 ...alStudioFileCustomizationIntegrationTest.groovy |  204 +
 .../VisualStudioMultiProjectIntegrationTest.groovy |  363 ++
 ...VisualStudioSingleProjectIntegrationTest.groovy |  924 ++++
 .../VisualStudioPluginIntegrationTest.groovy       |   22 +
 .../groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy  |   80 +
 .../gradle/ide/cdt/model/CprojectDescriptor.groovy |  117 +
 .../gradle/ide/cdt/model/CprojectSettings.groovy   |  102 +
 .../gradle/ide/cdt/model/ProjectDescriptor.groovy  |   48 +
 .../gradle/ide/cdt/model/ProjectSettings.groovy    |    0
 .../ide/cdt/tasks/GenerateMetadataFileTask.groovy  |    0
 .../org/gradle/ide/visualstudio/ConfigFile.java    |    0
 .../gradle/ide/visualstudio/TextConfigFile.java    |    0
 .../org/gradle/ide/visualstudio/TextProvider.java  |    0
 .../ide/visualstudio/VisualStudioExtension.java    |   36 +
 .../ide/visualstudio/VisualStudioProject.java      |   62 +
 .../ide/visualstudio/VisualStudioSolution.java     |   64 +
 .../org/gradle/ide/visualstudio/XmlConfigFile.java |    0
 .../internal/DefaultVisualStudioExtension.java     |   51 +
 .../internal/DefaultVisualStudioProject.groovy     |  153 +
 .../internal/DefaultVisualStudioSolution.groovy    |  114 +
 ...ecutableVisualStudioProjectConfiguration.groovy |   47 +
 .../internal/VisualStudioExtensionInternal.java    |   25 +
 .../VisualStudioProjectConfiguration.groovy        |  108 +
 .../internal/VisualStudioProjectMapper.java        |   90 +
 .../internal/VisualStudioProjectRegistry.java      |   65 +
 .../internal/VisualStudioProjectResolver.java      |   46 +
 .../internal/VisualStudioSolutionRegistry.java     |    0
 .../org/gradle/ide/visualstudio/package-info.java  |    0
 .../visualstudio/plugins/VisualStudioPlugin.java   |  149 +
 .../ide/visualstudio/plugins/package-info.java     |   20 +
 .../tasks/GenerateFiltersFileTask.groovy           |    0
 .../tasks/GenerateProjectFileTask.groovy           |    0
 .../tasks/GenerateSolutionFileTask.groovy          |    0
 .../internal/AbsoluteFileNameTransformer.java      |    0
 .../internal/RelativeFileNameTransformer.java      |    0
 .../tasks/internal/VisualStudioFiltersFile.groovy  |   58 +
 .../tasks/internal/VisualStudioProjectFile.groovy  |   98 +
 .../tasks/internal/VisualStudioSolutionFile.groovy |    0
 .../org.gradle.eclipse-cdt.properties}             |    0
 .../org.gradle.visual-studio.properties}           |    0
 .../gradle/ide/cdt/model/defaultCproject-linux.xml |    0
 .../gradle/ide/cdt/model/defaultCproject-macos.xml |    0
 .../org/gradle/ide/cdt/model/defaultProject.xml    |    0
 .../ide/visualstudio/tasks/internal/default.sln    |    0
 .../visualstudio/tasks/internal/default.vcxproj    |    0
 .../tasks/internal/default.vcxproj.filters         |    0
 .../ide/cdt/model/CprojectSettingsSpec.groovy      |   58 +
 .../ide/cdt/model/ProjectDescriptorSpec.groovy     |    0
 .../internal/DefaultVisualStudioProjectTest.groovy |  115 +
 .../VisualStudioProjectConfigurationTest.groovy    |  199 +
 .../internal/VisualStudioProjectMapperTest.groovy  |  156 +
 .../VisualStudioProjectRegistryTest.groovy         |  106 +
 .../RelativeFileNameTransformerTest.groovy         |    0
 .../internal/VisualStudioFiltersFileTest.groovy    |   97 +
 .../internal/VisualStudioProjectFileTest.groovy    |  118 +
 .../internal/VisualStudioSolutionFileTest.groovy   |  190 +
 subprojects/ide/ide.gradle                         |    2 +-
 .../plugins/ide/AbstractIdeIntegrationSpec.groovy  |    5 +
 .../plugins/ide/AbstractIdeIntegrationTest.groovy  |    5 +
 ...ractSourcesAndJavadocJarsIntegrationTest.groovy |  115 +-
 .../eclipse/AbstractEclipseIntegrationSpec.groovy  |   53 +
 .../eclipse/AbstractEclipseIntegrationTest.groovy  |   12 +
 .../ide/eclipse/EclipseClasspathFixture.groovy     |   39 +-
 .../eclipse/EclipseClasspathIntegrationTest.groovy |   61 +-
 ...pseDependencySubstitutionIntegrationTest.groovy |  117 +
 .../ide/eclipse/EclipseProjectFixture.groovy       |   59 +
 ...ipseSourcesAndJavadocJarsIntegrationTest.groovy |   16 +-
 .../ide/eclipse/EclipseWtpComponentFixture.groovy  |   98 +
 ...WtpEarAndWebAndEjbProjectIntegrationTest.groovy |  102 +
 .../EclipseWtpEarProjectIntegrationTest.groovy     |   66 +
 .../EclipseWtpEmptyProjectIntegrationTest.groovy   |   48 +
 .../ide/eclipse/EclipseWtpFacetsFixture.groovy     |   53 +
 .../ide/eclipse/EclipseWtpIntegrationTest.groovy   |  109 -
 ...seWtpJavaEarSingleProjectIntegrationTest.groovy |   74 +
 .../EclipseWtpJavaProjectIntegrationTest.groovy    |   72 +
 .../eclipse/EclipseWtpModelIntegrationTest.groovy  |   86 +-
 ...lipseWtpWebAndJavaProjectIntegrationTest.groovy |  113 +
 .../EclipseWtpWebProjectIntegrationTest.groovy     |   79 +
 ...deaDependencySubstitutionIntegrationTest.groovy |  122 +
 .../plugins/ide/idea/IdeaIntegrationTest.groovy    |   24 +-
 .../plugins/ide/idea/IdeaModuleFixture.groovy      |  144 +
 .../ide/idea/IdeaModuleIntegrationTest.groovy      |  114 +-
 .../ide/idea/IdeaMultiModuleIntegrationTest.groovy |  118 +-
 .../ide/idea/IdeaProjectIntegrationTest.groovy     |   21 +
 ...IdeaSourcesAndJavadocJarsIntegrationTest.groovy |   26 +-
 .../canCreateAndDeleteMetaData/common/build.gradle |   25 +
 .../src/main/java/org/gradle/api/PersonList.java   |    5 +
 .../src/main/resources/someprops.properties}       |    0
 .../test/java/org/gradle/shared/PersonTest.java    |    9 +
 .../expectedFiles/apiClasspath.xml                 |    6 +-
 .../expectedFiles/apiWtpComponent.xml              |    6 +
 .../expectedFiles/apiWtpFacet.xml                  |    5 +
 .../expectedFiles/commonClasspath.xml              |   30 +
 .../expectedFiles/commonJdt.properties             |   11 +
 .../expectedFiles/commonProject.xml                |   26 +
 .../expectedFiles/commonWtpComponent.xml           |    9 +
 .../expectedFiles/commonWtpFacet.xml               |    5 +
 .../canCreateAndDeleteMetaData/master/build.gradle |    6 +-
 .../master/settings.gradle                         |    2 +-
 .../org/gradle/plugins/ide/api/GeneratorTask.java  |    7 +
 .../plugins/ide/api/XmlFileContentMerger.groovy    |    2 +-
 .../gradle/plugins/ide/api/XmlGeneratorTask.java   |    2 +-
 .../plugins/ide/eclipse/EclipsePlugin.groovy       |   19 +-
 .../plugins/ide/eclipse/EclipseWtpPlugin.groovy    |  277 +-
 .../ide/eclipse/GenerateEclipseClasspath.groovy    |  165 +-
 .../plugins/ide/eclipse/GenerateEclipseJdt.groovy  |    5 +-
 .../ide/eclipse/GenerateEclipseProject.groovy      |    5 +-
 .../ide/eclipse/GenerateEclipseWtpComponent.groovy |  166 +-
 .../ide/eclipse/GenerateEclipseWtpFacet.groovy     |    7 +-
 .../plugins/ide/eclipse/model/Classpath.groovy     |    2 +-
 .../ide/eclipse/model/EclipseClasspath.groovy      |    6 +-
 .../plugins/ide/eclipse/model/EclipseWtp.groovy    |    6 -
 .../ide/eclipse/model/EclipseWtpComponent.groovy   |    8 +-
 .../ide/eclipse/model/EclipseWtpFacet.groovy       |    3 +-
 .../gradle/plugins/ide/eclipse/model/Facet.groovy  |    6 +
 .../plugins/ide/eclipse/model/Project.groovy       |    2 +-
 .../plugins/ide/eclipse/model/WtpComponent.groovy  |    2 +-
 .../plugins/ide/eclipse/model/WtpFacet.groovy      |    2 +-
 .../model/internal/FileReferenceFactory.groovy     |    4 +-
 .../model/internal/WtpComponentFactory.groovy      |   13 +-
 .../plugins/ide/idea/GenerateIdeaModule.groovy     |    2 +-
 .../plugins/ide/idea/GenerateIdeaProject.groovy    |    2 +-
 .../plugins/ide/idea/GenerateIdeaWorkspace.groovy  |    4 +-
 .../plugins/ide/idea/model/IdeaModule.groovy       |   35 +-
 .../plugins/ide/idea/model/IdeaModuleIml.groovy    |    2 +-
 .../plugins/ide/idea/model/IdeaProject.groovy      |   19 +-
 .../gradle/plugins/ide/idea/model/Module.groovy    |   43 +-
 .../plugins/ide/idea/model/ModuleLibrary.groovy    |    4 +-
 .../plugins/ide/idea/model/PathFactory.groovy      |    6 +-
 .../gradle/plugins/ide/idea/model/Project.groovy   |   60 +-
 .../ide/idea/model/SingleEntryModuleLibrary.groovy |   23 +-
 .../gradle/plugins/ide/idea/model/Workspace.groovy |    2 +-
 .../model/internal/IdeaDependenciesProvider.java   |   16 +-
 .../ide/internal/IdeDependenciesExtractor.groovy   |  170 -
 .../ide/internal/IdeDependenciesExtractor.java     |  189 +
 .../XmlPersistableConfigurationObject.groovy       |    2 +-
 .../internal/tooling/BuildInvocationsBuilder.java  |   98 +-
 .../ide/internal/tooling/EclipseModelBuilder.java  |   20 +-
 .../ide/internal/tooling/GradleProjectBuilder.java |   37 +-
 .../ide/internal/tooling/IdeaModelBuilder.java     |   17 +-
 .../tooling/ToolingRegistrationAction.java         |    4 +-
 .../eclipse/DefaultEclipseLinkedResource.java      |    4 +-
 .../tooling/eclipse/DefaultEclipseProject.java     |   50 +-
 .../eclipse/DefaultEclipseProjectDependency.java   |   11 +-
 .../eclipse/DefaultEclipseSourceDirectory.java     |    4 +-
 .../tooling/eclipse/DefaultEclipseTask.java        |   11 +-
 .../tooling/idea/DefaultIdeaContentRoot.java       |   41 +-
 .../internal/tooling/idea/DefaultIdeaModule.java   |    7 +-
 .../internal/tooling/idea/DefaultIdeaProject.java  |   15 +-
 .../tooling/idea/DefaultIdeaSourceDirectory.java   |   16 +-
 .../resolver/DefaultIdeDependencyResolver.java     |   35 +-
 .../internal/resolver/model/IdeDependencyKey.java  |   21 +-
 .../model/IdeExtendedRepoFileDependency.java       |   35 +-
 ...roperties => org.gradle.eclipse-wtp.properties} |    0
 ...se.properties => org.gradle.eclipse.properties} |    0
 ...{idea.properties => org.gradle.idea.properties} |    0
 .../plugins/ide/eclipse/EclipsePluginTest.groovy   |   26 +-
 .../ide/eclipse/EclipseWtpPluginTest.groovy        |  285 +-
 .../eclipse/GenerateEclipseClasspathTest.groovy    |   18 -
 .../eclipse/GenerateEclipseWtpComponentTest.groovy |   29 -
 .../ide/eclipse/GenerateEclipseWtpFacetTest.groovy |   20 +
 .../plugins/ide/eclipse/model/ClasspathTest.groovy |    2 +-
 .../plugins/ide/eclipse/model/ProjectTest.groovy   |    2 +-
 .../ide/eclipse/model/WtpComponentTest.groovy      |    2 +-
 .../plugins/ide/eclipse/model/WtpFacetTest.groovy  |    2 +-
 ...leTest.groovy => GenerateIdeaModuleTest.groovy} |    0
 .../gradle/plugins/ide/idea/IdeaPluginTest.groovy  |    4 +-
 .../plugins/ide/idea/model/ModuleTest.groovy       |   20 +-
 .../plugins/ide/idea/model/ProjectTest.groovy      |   17 +-
 .../internal/IdeaDependenciesProviderTest.groovy   |    8 +-
 .../XmlPersistableConfigurationObjectTest.groovy   |    2 +-
 .../tooling/BuildInvocationsBuilderTest.groovy     |  126 +-
 .../tooling/GradleProjectBuilderTest.groovy        |   29 +-
 .../ide/internal/tooling/TasksFactoryTest.groovy   |    2 +-
 .../tooling/idea/DefaultIdeaContentRootTest.groovy |   47 +
 .../gradle/plugins/ide/idea/model/customModule.xml |    2 +
 subprojects/integ-test/integ-test.gradle           |   10 +-
 .../org/gradle/debug/GradleRunConfiguration.groovy |    2 +-
 .../integtests/AntProjectIntegrationTest.groovy    |   44 +
 .../integtests/AntlrPluginGoodBehaviourTest.groovy |   25 -
 .../integtests/ApplicationIntegrationSpec.groovy   |  370 +-
 .../integtests/ApplicationIntegrationTest.groovy   |  350 --
 .../BuildAggregationIntegrationTest.groovy         |   31 +-
 .../BuildScriptExecutionIntegrationTest.groovy     |   91 -
 .../BuildSourceBuilderIntegrationTest.groovy       |    2 +-
 .../integtests/CacheProjectIntegrationTest.groovy  |   15 +-
 .../integtests/CharacterEncodingIntegTest.groovy   |  103 -
 .../integtests/CustomPluginIntegrationTest.groovy  |    4 +-
 .../DistributionLocatorIntegrationTest.groovy      |    3 +-
 .../ExternalScriptExecutionIntegrationTest.groovy  |  196 -
 .../IncrementalBuildIntegrationTest.groovy         |  380 --
 .../IncrementalTasksIntegrationTest.groovy         |  343 --
 .../InitScriptExecutionIntegrationTest.groovy      |  158 -
 .../integtests/MavenPluginGoodBehaviourTest.groovy |    4 -
 ...gacyAndComponentJvmPluginIntegrationTest.groovy |   91 +
 .../MixedNativeAndJvmProjectIntegrationTest.groovy |  126 +-
 .../ParallelProjectExecutionIntegrationTest.groovy |   15 +-
 .../ParallelTaskExecutionIntegrationTest.groovy    |  273 ++
 .../integtests/ProjectLayoutIntegrationTest.groovy |   23 +-
 .../integtests/ProjectLoadingIntegrationTest.java  |   83 +-
 .../SettingsScriptExecutionIntegrationTest.groovy  |   78 -
 .../integtests/TaskExecutionIntegrationTest.groovy |  126 +-
 ...ssingBinaryCompatibilityCrossVersionSpec.groovy |   70 +-
 .../integtests/WaterProjectIntegrationTest.groovy  |   77 -
 .../BuildEnvironmentIntegrationTest.groovy         |    8 +-
 .../logging/LoggingIntegrationTest.groovy          |   10 +-
 .../ivy/IvySFtpPublishIntegrationTest.groovy       |  101 -
 .../samples/JUnitSamplesIntegrationTest.groovy     |    2 +-
 .../samples/SamplesAntlrIntegrationTest.groovy     |   40 -
 .../SamplesApplicationIntegrationTest.groovy       |    2 +-
 .../SamplesCodeQualityIntegrationTest.groovy       |   16 +-
 ...lesComponentMetadataRulesIntegrationTest.groovy |   75 +
 ...esComponentSelectionRulesIntegrationTest.groovy |   89 +
 .../SamplesJavaTestListenerIntegrationTest.groovy  |   46 +
 .../SamplesWebQuickstartIntegrationTest.groovy     |   27 +-
 .../samples/UserGuideSamplesIntegrationTest.groovy |    5 +-
 .../samples/UserGuideSamplesRunner.groovy          |  298 ++
 .../canUseANonStandardBuildDir/build.gradle        |    2 +-
 .../internal-integ-testing.gradle                  |    6 +-
 .../AbstractDependencyResolutionTest.groovy        |   31 -
 .../AbstractHttpDependencyResolutionTest.groovy    |   57 +
 .../fixtures/AbstractIntegrationSpec.groovy        |   63 +-
 .../fixtures/AbstractIntegrationTest.java          |   29 +-
 .../fixtures/AutoTestedSamplesUtil.groovy          |    3 +-
 .../integtests/fixtures/AvailableJavaHomes.java    |  222 +-
 .../gradle/integtests/fixtures/ClassFile.groovy    |   69 -
 .../fixtures/CompilationOutputsFixture.groovy      |   84 +
 .../fixtures/CrossVersionIntegrationSpec.groovy    |   15 +-
 .../gradle/integtests/fixtures/EnableModelDsl.java |   32 +
 .../ForkScalaCompileInDaemonModeFixture.groovy     |   57 +
 .../fixtures/MultiVersionIntegrationSpec.groovy    |    4 +-
 .../fixtures/MultiVersionSpecRunner.groovy         |   18 +-
 .../org/gradle/integtests/fixtures/Sample.java     |   20 +-
 .../org/gradle/integtests/fixtures/SourceFile.java |   61 +
 .../integtests/fixtures/TestDependency.groovy      |   80 +
 .../fixtures/TestNGExecutionResult.groovy          |    7 +-
 .../fixtures/UserGuideSamplesRunner.groovy         |  275 --
 .../fixtures/WellBehavedPluginTest.groovy          |   28 +-
 .../executer/AbstractDelegatingGradleExecuter.java |    5 -
 .../fixtures/executer/AbstractGradleExecuter.java  |   52 +-
 .../fixtures/executer/AnyOrderOutputMatcher.groovy |    2 +-
 .../fixtures/executer/DaemonGradleExecuter.java    |   17 +-
 .../executer/DefaultGradleDistribution.java        |   26 +-
 .../executer/DownloadableGradleDistribution.groovy |    7 +-
 .../executer/EmbeddedDaemonGradleExecuter.java     |  113 -
 .../fixtures/executer/ExecutionFailure.java        |    5 +-
 .../fixtures/executer/ForkingGradleExecuter.java   |   18 +-
 .../executer/GradleContextualExecuter.java         |    7 +-
 .../fixtures/executer/GradleDistribution.java      |   15 +-
 .../fixtures/executer/GradleExecuter.java          |   34 +-
 .../integtests/fixtures/executer/GradleHandle.java |   20 +
 .../fixtures/executer/InProcessGradleExecuter.java |   89 +-
 .../executer/InitScriptExecuterFixture.groovy      |   12 +-
 .../executer/OutputScrapingExecutionFailure.java   |   73 +-
 .../executer/OutputScrapingExecutionResult.java    |   12 +
 .../executer/ParallelForkingGradleExecuter.java    |   10 +-
 .../executer/ProgressLoggingFixture.groovy         |    2 +-
 .../executer/ProjectLifecycleFixture.groovy        |    1 +
 .../fixtures/executer/RedirectMavenCentral.groovy  |   46 -
 .../executer/SequentialOutputMatcher.groovy        |    9 +-
 .../UnderDevelopmentGradleDistribution.java        |    6 +
 .../fixtures/jvm/InstalledJvmLocator.java          |   82 +
 .../integtests/fixtures/jvm/JvmInstallation.java   |   65 +
 .../fixtures/jvm/OsXInstalledJvmLocator.java       |   47 +
 .../integtests/fixtures/jvm/OsXJavaHomeParser.java |   68 +
 .../integtests/fixtures/jvm/UbuntuJvmLocator.java  |   75 +
 .../fixtures/jvm/WindowsOracleJvmLocator.java      |   68 +
 .../fixtures/versions/ClasspathVersionSource.java  |    2 +-
 .../org/gradle/test/fixtures/AbstractModule.groovy |    1 +
 .../org/gradle/test/fixtures/HttpModule.groovy     |    2 +-
 .../gradle/test/fixtures/bintray/BintrayApi.groovy |   83 -
 .../test/fixtures/bintray/BintrayTestServer.groovy |   62 -
 .../gradle/test/fixtures/ivy/IvyDescriptor.groovy  |   10 +
 .../gradle/test/fixtures/ivy/IvyFileModule.groovy  |  162 +-
 .../test/fixtures/ivy/IvyFileRepository.groovy     |   34 +-
 .../gradle/test/fixtures/ivy/IvyHttpModule.groovy  |  161 -
 .../test/fixtures/ivy/IvyHttpRepository.groovy     |   67 -
 .../org/gradle/test/fixtures/ivy/IvyModule.java    |   12 +
 .../gradle/test/fixtures/ivy/IvyRepository.groovy  |    2 -
 .../ivy/M2CompatibleIvyPatternHelper.groovy        |   40 +
 .../test/fixtures/ivy/RemoteIvyModule.groovy       |   24 +
 .../test/fixtures/ivy/RemoteIvyRepository.groovy   |   32 +
 .../test/fixtures/keystore/TestKeyStore.groovy     |   77 +
 .../test/fixtures/maven/AbstractMavenModule.groovy |  126 +-
 .../test/fixtures/maven/BasicHttpResource.groovy   |   41 -
 .../test/fixtures/maven/DelegatingMavenModule.java |  141 +
 .../gradle/test/fixtures/maven/HttpArtifact.groovy |   66 -
 .../gradle/test/fixtures/maven/HttpResource.groovy |   74 -
 .../test/fixtures/maven/MavenDependency.groovy     |    1 +
 .../fixtures/maven/MavenDependencyExclusion.groovy |   22 +
 .../test/fixtures/maven/MavenHttpArtifact.groovy   |   45 -
 .../test/fixtures/maven/MavenHttpModule.groovy     |  143 -
 .../test/fixtures/maven/MavenHttpRepository.groovy |   60 -
 .../test/fixtures/maven/MavenLocalModule.groovy    |    6 +
 .../gradle/test/fixtures/maven/MavenModule.groovy  |   17 +-
 .../org/gradle/test/fixtures/maven/MavenPom.groovy |   14 +-
 .../test/fixtures/maven/MavenRepository.groovy     |    2 -
 .../test/fixtures/maven/MetaDataArtifact.groovy    |   48 -
 .../test/fixtures/maven/PomHttpArtifact.groovy     |   57 -
 .../test/fixtures/plugin/PluginBuilder.groovy      |   57 +-
 .../test/fixtures/resource/RemoteArtifact.java     |   23 +
 .../test/fixtures/resource/RemoteResource.groovy   |   48 +
 .../gradle/test/fixtures/server/ExpectOne.groovy   |   29 +
 .../test/fixtures/server/RepositoryServer.groovy   |   34 +
 .../test/fixtures/server/ServerExpectation.groovy  |   21 +
 .../fixtures/server/ServerWithExpectations.groovy  |   56 +
 .../server/http/AbstractHttpResource.groovy        |  101 +
 .../fixtures/server/http/BasicHttpResource.groovy  |   40 +
 .../fixtures/server/http/BlockingHttpServer.groovy |   52 +-
 .../server/http/CyclicBarrierHttpServer.java       |  222 +
 .../test/fixtures/server/http/HttpArtifact.groovy  |   62 +
 .../server/http/HttpDirectoryResource.groovy       |   75 +
 .../test/fixtures/server/http/HttpResource.groovy  |   81 +
 .../server/http/HttpResourceInteraction.java       |   21 +
 .../test/fixtures/server/http/HttpServer.groovy    |  322 +-
 .../test/fixtures/server/http/IvyHttpModule.groovy |  209 +
 .../fixtures/server/http/IvyHttpRepository.groovy  |   67 +
 .../fixtures/server/http/MavenHttpArtifact.groovy  |   45 +
 .../fixtures/server/http/MavenHttpModule.groovy    |  102 +
 .../server/http/MavenHttpRepository.groovy         |   61 +
 .../fixtures/server/http/MetaDataArtifact.groovy   |   49 +
 .../fixtures/server/http/PomHttpArtifact.groovy    |   44 +
 .../server/http/RepositoryHttpServer.groovy        |   56 +
 .../fixtures/server/http/TestProxyServer.groovy    |   10 +-
 .../test/fixtures/server/sftp/IvySftpModule.groovy |  135 +
 .../fixtures/server/sftp/IvySftpRepository.groovy  |   69 +
 .../fixtures/server/sftp/MavenSftpModule.groovy    |   48 +
 .../server/sftp/MavenSftpRepository.groovy         |   40 +
 .../test/fixtures/server/sftp/SFTPServer.groovy    |  469 +-
 .../test/fixtures/server/sftp/SftpArtifact.java    |   37 +
 .../server/sftp/SftpDirectoryResource.groovy       |   85 +
 .../test/fixtures/server/sftp/SftpResource.groovy  |  141 +
 .../src/main/resources/logback.xml                 |   14 -
 .../src/main/resources/test-key-store/keyStore}    |  Bin
 .../src/main/resources/test-key-store/trustStore}  |  Bin
 .../fixtures/jvm/OsXJavaHomeParserTest.groovy      |  100 +
 .../fixtures/jvm/UbuntuJvmLocatorTest.groovy       |  113 +
 .../test/fixtures/maven/MavenFileModuleTest.groovy |   29 +-
 .../fixtures/maven/MavenLocalModuleTest.groovy     |   33 +-
 .../internal-testing/internal-testing.gradle       |    1 +
 .../fixtures/DefaultTestExecutionResult.groovy     |    7 +-
 .../fixtures/HtmlTestExecutionResult.groovy        |   17 +-
 .../fixtures/JUnitTestClassExecutionResult.groovy  |   13 +
 .../fixtures/JUnitXmlTestExecutionResult.groovy    |   21 +-
 .../fixtures/TestClassExecutionResult.java         |    2 +
 .../gradle/integtests/fixtures/UrlValidator.groovy |   79 +
 .../fixtures/archive/ArchiveTestFixture.groovy     |   21 +-
 .../test/fixtures/archive/JarTestFixture.groovy    |   25 +-
 .../fixtures/concurrent/TestExecutorFactory.groovy |    5 +
 .../concurrent/TestStoppableExecutor.groovy        |   23 +-
 .../gradle/test/fixtures/encoding/Identifier.java  |   54 +-
 .../file/AbstractTestDirectoryProvider.java        |  118 +
 .../org/gradle/test/fixtures/file/ClassFile.groovy |   73 +
 .../file/TestDistributionDirectoryProvider.java    |   44 +
 .../org/gradle/test/fixtures/file/TestFile.java    |   46 +-
 .../file/TestNameTestDirectoryProvider.java        |   87 +-
 .../test/fixtures/file/TestWorkspaceBuilder.groovy |    2 +-
 .../internal/NativeServicesTestFixture.java        |   48 +
 .../main/groovy/org/gradle/util/Assertions.groovy  |   42 -
 .../src/main/groovy/org/gradle/util/Matchers.java  |   16 +
 .../main/groovy/org/gradle/util/Requires.groovy    |    3 +-
 .../groovy/org/gradle/util/TestPrecondition.groovy |   28 +-
 .../org/gradle/util/UsesNativeServices.groovy      |   31 +
 .../gradle/util/UsesNativeServicesExtension.groovy |   38 +
 .../groovy/org/gradle/util/AssertionsTest.groovy   |   58 -
 subprojects/ivy/ivy.gradle                         |    6 +-
 .../publish/ivy/AbstractIvyPublishIntegTest.groovy |   67 -
 .../IvyHttpsLegacyPublishIntegrationTest.groovy    |   47 +
 ...IvyPublishArtifactCustomizationIntegTest.groovy |   42 +
 .../publish/ivy/IvyPublishBasicIntegTest.groovy    |    2 +-
 .../IvyPublishCrossVersionIntegrationTest.groovy   |   37 +-
 ...yPublishDescriptorCustomizationIntegTest.groovy |   79 +-
 .../api/publish/ivy/IvyPublishHttpIntegTest.groovy |   11 +-
 .../publish/ivy/IvyPublishHttpsIntegTest.groovy    |  140 +
 .../IvyPublishIdentifierValidationIntegTest.groovy |  160 -
 .../api/publish/ivy/IvyPublishJavaIntegTest.groovy |    2 +-
 .../ivy/IvyPublishValidationIntegTest.groovy       |  196 +
 .../api/publish/ivy/IvyPublishWarIntegTest.groovy  |    2 +-
 .../ivy/SamplesIvyPublishIntegrationTest.groovy    |    2 +-
 .../ivy/plugins/IvyPublishPluginIntegTest.groovy   |    6 -
 .../ivy/IvyHttpLegacyPublishIntegrationTest.groovy |   25 +
 .../ivy/IvyHttpPublishIntegrationTest.groovy       |   70 +-
 .../IvyUrlResolverPublishIntegrationTest.groovy    |   75 -
 .../gradle/api/publish/ivy/IvyExtraInfoSpec.java   |   37 +
 .../api/publish/ivy/IvyModuleDescriptor.java       |   82 -
 .../api/publish/ivy/IvyModuleDescriptorSpec.java   |  104 +
 .../org/gradle/api/publish/ivy/IvyPublication.java |    8 +-
 .../ivy/internal/IvyPublicationTasksModelRule.java |   82 -
 .../publish/ivy/internal/IvyPublishServices.java   |   62 +
 .../artifact/IvyArtifactNotationParserFactory.java |   71 +-
 .../publication/DefaultIvyExtraInfoSpec.java       |   36 +
 .../publication/DefaultIvyModuleDescriptor.java    |   72 -
 .../DefaultIvyModuleDescriptorSpec.java            |   99 +
 .../publication/DefaultIvyPublication.java         |   10 +-
 .../publication/IvyModuleDescriptorInternal.java   |   40 -
 .../IvyModuleDescriptorSpecInternal.java           |   40 +
 .../publication/IvyPublicationInternal.java        |    2 +-
 .../publisher/DependencyResolverIvyPublisher.java  |    4 +-
 .../publisher/IvyDescriptorFileGenerator.java      |   41 +-
 .../internal/publisher/ValidatingIvyPublisher.java |   26 +-
 .../api/publish/ivy/plugins/IvyPublishPlugin.java  |   65 +-
 .../publish/ivy/tasks/GenerateIvyDescriptor.java   |   41 +-
 .../publish/ivy/tasks/PublishToIvyRepository.java  |   18 +-
 .../java/org/gradle/ivy/IvyDescriptorArtifact.java |   27 +
 .../src/main/java/org/gradle/ivy/IvyModule.java    |   27 +
 .../src/main/java/org/gradle/ivy/package-info.java |   23 +
 ...roperties => org.gradle.ivy-publish.properties} |    0
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../IvyArtifactNotationParserFactoryTest.groovy    |    2 +-
 .../publication/DefaultIvyExtraInfoSpecTest.groovy |   32 +
 .../DefaultIvyModuleDescriptorSpecTest.groovy      |   59 +
 .../publication/DefaultIvyPublicationTest.groovy   |    5 +-
 .../IvyDescriptorFileGeneratorTest.groovy          |   37 +-
 .../publisher/ValidatingIvyPublisherTest.groovy    |  142 +-
 .../ivy/plugins/IvyPublishPluginTest.groovy        |   19 +-
 .../publish/ivy/AbstractIvyPublishIntegTest.groovy |   78 +
 ...actIvyRemoteLegacyPublishIntegrationTest.groovy |  122 +
 .../plugins/JacocoPluginIntegrationTest.groovy     |   71 +-
 .../jacoco/plugins/JacocoVersionIntegTest.groovy   |   30 +-
 .../testing/jacoco/plugins/JacocoPlugin.groovy     |    7 +-
 .../jacoco/plugins/JacocoPluginExtension.groovy    |    3 +-
 .../gradle/testing/jacoco/tasks/JacocoMerge.groovy |   16 +-
 .../testing/jacoco/tasks/JacocoReport.groovy       |   55 +-
 .../jacoco/tasks/JacocoReportsContainer.java       |    2 +-
 ...oco.properties => org.gradle.jacoco.properties} |    0
 .../jacoco/plugins/JacocoReportFixture.groovy      |   47 +
 subprojects/javascript/javascript.gradle           |    2 +-
 .../JavaScriptBasePluginIntegrationTest.groovy     |    2 +-
 .../CoffeeScriptBasePluginIntegrationTest.groovy   |    5 +-
 .../envjs/EnvJsPluginIntegrationTest.groovy        |    4 +
 .../jshint/JsHintPluginIntegrationTest.groovy      |    7 +
 .../javascript/base/JavaScriptBasePlugin.groovy    |    2 +-
 .../base/JavaScriptRepositoriesExtension.java      |   10 +-
 .../base/SourceTransformationException.java        |   30 +
 .../coffeescript/CoffeeScriptBasePlugin.groovy     |    3 +-
 .../coffeescript/CoffeeScriptCompile.java          |   25 +-
 .../internal/rhino/CoffeeScriptCompilerWorker.java |    8 +-
 .../plugins/javascript/envjs/EnvJsPlugin.groovy    |    4 +-
 .../gradle/plugins/javascript/jshint/JsHint.java   |    7 +-
 .../plugins/javascript/jshint/JsHintPlugin.groovy  |    4 +-
 .../plugins/javascript/rhino/RhinoPlugin.groovy    |    3 +-
 .../javascript/rhino/worker/RhinoWorkerUtils.java  |    4 +-
 ...ies => org.gradle.coffeescript-base.properties} |    0
 ...nvjs.properties => org.gradle.envjs.properties} |    0
 ...rties => org.gradle.javascript-base.properties} |    0
 ...int.properties => org.gradle.jshint.properties} |    0
 ...hino.properties => org.gradle.rhino.properties} |    0
 .../base/JavaScriptBasePluginTest.groovy           |    4 +-
 .../javascript/rhino/RhinoPluginTest.groovy        |    7 +-
 .../org/gradle/api/plugins/jetty/JettyPlugin.java  |    2 +-
 .../plugins/jetty/internal/JettyConfiguration.java |    2 +-
 ...etty.properties => org.gradle.jetty.properties} |    0
 subprojects/language-base/language-base.gradle     |    7 -
 .../groovy/org/gradle/language/base/Binary.java    |   33 -
 .../org/gradle/language/base/BinaryContainer.java  |   25 -
 .../language/base/BuildableModelElement.java       |   41 -
 .../gradle/language/base/FunctionalSourceSet.java  |   27 -
 .../gradle/language/base/LanguageSourceSet.java    |   47 -
 .../org/gradle/language/base/ProjectSourceSet.java |   26 -
 .../internal/AbstractBuildableModelElement.java    |   55 -
 .../base/internal/AbstractLanguageSourceSet.java   |   81 -
 .../language/base/internal/BinaryInternal.java     |   23 -
 .../language/base/internal/BinaryNamingScheme.java |   35 -
 .../base/internal/BinaryNamingSchemeBuilder.java   |   26 -
 .../base/internal/DefaultBinaryContainer.java      |   27 -
 .../base/internal/DefaultBinaryNamingScheme.java   |  112 -
 .../internal/DefaultBinaryNamingSchemeBuilder.java |   64 -
 .../base/internal/DefaultFunctionalSourceSet.java  |   39 -
 .../base/internal/DefaultProjectSourceSet.java     |   32 -
 .../base/internal/LanguageSourceSetInternal.java   |   37 -
 .../language/base/plugins/LanguageBasePlugin.java  |   67 -
 .../base/internal/BuildableModelElementTest.groovy |   54 -
 .../internal/DefaultBinaryNamingSchemeTest.groovy  |   79 -
 .../internal/DefaultFunctionalSourceSetTest.groovy |   29 -
 subprojects/language-groovy/language-groovy.gradle |   13 +
 .../gradle/groovy/GroovyDocIntegrationTest.groovy  |   61 +
 .../internal/tasks/compile/ApiGroovyCompiler.java  |  242 +
 .../tasks/compile/CleaningGroovyCompiler.java      |   41 +
 .../compile/DefaultGroovyJavaJointCompileSpec.java |    0
 .../DefaultGroovyJavaJointCompileSpecFactory.java  |   46 +
 .../internal/tasks/compile/GroovyCompileSpec.java  |    0
 .../GroovyCompileTransformingClassLoader.java      |  124 +
 .../tasks/compile/GroovyCompilerFactory.java       |   56 +
 .../tasks/compile/GroovyJavaJointCompileSpec.java  |    0
 .../tasks/compile/GroovyJavaJointCompiler.java     |   26 +
 .../tasks/compile/NormalizingGroovyCompiler.java   |  123 +
 .../tasks/compile/daemon/DaemonGroovyCompiler.java |   60 +
 .../gradle/api/tasks/compile/GroovyCompile.java    |  138 +
 .../api/tasks/compile/GroovyCompileOptions.java    |  308 ++
 .../api/tasks/compile/GroovyForkOptions.java       |    0
 .../org/gradle/api/tasks/compile/package-info.java |   20 +
 .../org/gradle/api/tasks/javadoc/AntGroovydoc.java |  113 +
 .../org/gradle/api/tasks/javadoc/Groovydoc.java    |  366 ++
 ...ultGroovyJavaJointCompileSpecFactoryTest.groovy |   44 +
 ...GroovyCompileTransformingClassLoaderTest.groovy |   96 +
 .../compile/NormalizingGroovyCompilerTest.groovy   |   66 +
 .../tasks/compile/GroovyCompileOptionsTest.groovy  |   97 +
 .../api/tasks/compile/GroovyForkOptionsTest.groovy |    0
 .../gradle/api/tasks/javadoc/GroovydocTest.java    |    0
 subprojects/language-java/language-java.gradle     |   17 +
 .../compile/JavaCompileIntegrationTest.groovy      |   91 +
 .../JavaCompileParallelIntegrationTest.groovy      |  102 +
 ...ncrementalJavaCompilationIntegrationTest.groovy |  407 ++
 ...ncrementalJavaCompilationIntegrationTest.groovy |  342 ++
 .../gradle/javadoc/JavadocIntegrationTest.groovy   |  100 +
 ...aLanguageIncrementalBuildIntegrationTest.groovy |   41 +
 .../java/JavaLanguageIntegrationTest.groovy        |  179 +
 .../java/SampleJavaLanguageIntegrationTest.groovy  |   41 +
 .../JavaLanguagePluginGoodBehaviourTest.groovy     |   25 +
 .../JavaLanguagePluginIntegrationTest.groovy       |   23 +
 .../build.gradle                                   |    0
 .../src/main/java/Person.java                      |    0
 .../src/taglet/java/LocaleAwareTaglet.java         |    0
 .../handlesTagsAndTaglets/build.gradle             |    0
 .../src/main/java/Person.java                      |    0
 .../src/taglet/java/CustomTaglet.java              |    0
 .../api/internal/tasks/DefaultJavaToolChain.java   |  119 +
 .../compile/AbstractJavaCompileSpecFactory.java    |   47 +
 .../api/internal/tasks/compile/AntDepend.java      |    0
 .../tasks/compile/AntDependsStaleClassCleaner.java |   62 +
 .../tasks/compile/CleaningJavaCompiler.java        |   53 +
 .../tasks/compile/CleaningJavaCompilerSupport.java |   40 +
 .../tasks/compile/CommandLineJavaCompileSpec.java  |   20 +
 .../tasks/compile/CommandLineJavaCompiler.java     |   64 +
 .../CommandLineJavaCompilerArgumentsGenerator.java |   78 +
 .../internal/tasks/compile/DaemonJavaCompiler.java |   39 +
 .../tasks/compile/DefaultJavaCompileSpec.java      |   42 +
 .../compile/DefaultJavaCompileSpecFactory.java     |   43 +
 .../tasks/compile/DefaultJavaCompilerFactory.java  |   62 +
 .../tasks/compile/ForkingJavaCompileSpec.java      |   20 +
 .../internal/tasks/compile/JavaCompileSpec.java    |   31 +
 .../compile/JavaCompilerArgumentsBuilder.java      |  212 +
 .../tasks/compile/JavaCompilerFactory.java         |   28 +
 .../compile/JavaHomeBasedJavaCompilerFactory.java  |   80 +
 .../internal/tasks/compile/JdkJavaCompiler.java    |   61 +
 .../tasks/compile/NormalizingJavaCompiler.java     |  110 +
 .../incremental/ClassSetAnalysisUpdater.java       |   53 +
 .../compile/incremental/CompilationSourceDirs.java |   53 +
 .../IncrementalCompilationFinalizer.java           |   50 +
 .../IncrementalCompilationInitializer.java         |   69 +
 .../incremental/IncrementalCompilerDecorator.java  |   79 +
 .../incremental/IncrementalCompilerFactory.java    |   61 +
 .../compile/incremental/JavaChangeProcessor.java   |   44 +
 .../incremental/RecompilationNotNecessary.java     |   26 +
 .../incremental/RecompilationSpecProvider.java     |   78 +
 .../compile/incremental/SelectiveCompiler.java     |   73 +
 .../compile/incremental/SourceToNameConverter.java |   48 +
 .../analyzer/CachingClassDependenciesAnalyzer.java |   44 +
 .../incremental/analyzer/ClassAnalysis.java        |   38 +
 .../incremental/analyzer/ClassAnalysisCache.java   |   22 +
 .../analyzer/ClassAnalysisSerializer.java          |   42 +
 .../analyzer/ClassDependenciesAnalyzer.java        |   23 +
 .../analyzer/ClassDependenciesVisitor.java         |   58 +
 .../incremental/analyzer/ClassFilesAnalyzer.java   |   61 +
 .../incremental/analyzer/ClassRelevancyFilter.java |   30 +
 .../analyzer/DefaultClassAnalysisCache.java        |   33 +
 .../analyzer/DefaultClassDependenciesAnalyzer.java |   77 +
 .../compile/incremental/cache/CompileCaches.java   |   25 +
 .../cache/DefaultGeneralCompileCaches.java         |   39 +
 .../incremental/cache/GeneralCompileCaches.java    |   25 +
 .../compile/incremental/deps/AffectedClasses.java  |   38 +
 .../deps/ClassDependentsAccumulator.java           |   57 +
 .../compile/incremental/deps/ClassSetAnalysis.java |   79 +
 .../incremental/deps/ClassSetAnalysisData.java     |   84 +
 .../incremental/deps/DefaultDependentsSet.java     |   65 +
 .../compile/incremental/deps/DependencyToAll.java  |   44 +
 .../compile/incremental/deps/DependentsSet.java    |   27 +
 .../deps/LocalClassSetAnalysisStore.java           |   41 +
 .../incremental/deps/OutputToNameConverter.java    |   38 +
 .../incremental/jar/CachingJarSnapshotter.java     |   55 +
 .../incremental/jar/ClasspathJarFinder.java        |   41 +
 .../incremental/jar/DefaultJarSnapshotCache.java   |   64 +
 .../incremental/jar/DefaultJarSnapshotter.java     |   58 +
 .../tasks/compile/incremental/jar/JarArchive.java  |   30 +
 .../incremental/jar/JarChangeDependentsFinder.java |   83 +
 .../incremental/jar/JarChangeProcessor.java        |   46 +
 .../incremental/jar/JarClasspathSnapshot.java      |   51 +
 .../incremental/jar/JarClasspathSnapshotData.java  |   40 +
 .../jar/JarClasspathSnapshotDataSerializer.java    |   42 +
 .../jar/JarClasspathSnapshotFactory.java           |   52 +
 .../incremental/jar/JarClasspathSnapshotMaker.java |   66 +
 .../jar/JarClasspathSnapshotProvider.java          |   23 +
 .../jar/JarClasspathSnapshotWriter.java            |   23 +
 .../tasks/compile/incremental/jar/JarSnapshot.java |   96 +
 .../compile/incremental/jar/JarSnapshotCache.java  |   27 +
 .../compile/incremental/jar/JarSnapshotData.java   |   43 +
 .../incremental/jar/JarSnapshotDataSerializer.java |   52 +
 .../compile/incremental/jar/JarSnapshotter.java    |   21 +
 .../jar/LocalJarClasspathSnapshotStore.java        |   39 +
 .../incremental/jar/PreviousCompilation.java       |   54 +
 .../incremental/recomp/RecompilationSpec.java      |   43 +
 .../gradle/api/tasks/compile/CompileOptions.java   |  429 ++
 .../org/gradle/api/tasks/compile/DebugOptions.java |    0
 .../gradle/api/tasks/compile/DependOptions.java    |    0
 .../org/gradle/api/tasks/compile/ForkOptions.java  |    0
 .../org/gradle/api/tasks/compile/JavaCompile.java  |  193 +
 .../org/gradle/api/tasks/compile/package-info.java |   20 +
 .../java/org/gradle/api/tasks/javadoc/Javadoc.java |  318 ++
 .../tasks/javadoc/internal/JavadocGenerator.java   |   62 +
 .../api/tasks/javadoc/internal/JavadocSpec.java    |   70 +
 .../org/gradle/api/tasks/javadoc/package-info.java |    0
 .../external/javadoc/CoreJavadocOptions.java       |    0
 .../external/javadoc/JavadocMemberLevel.java       |    0
 .../external/javadoc/JavadocOfflineLink.java       |   44 +
 .../external/javadoc/JavadocOptionFileOption.java  |    0
 .../external/javadoc/JavadocOutputLevel.java       |    0
 .../external/javadoc/MinimalJavadocOptions.java    |    0
 .../javadoc/OptionLessJavadocOptionFileOption.java |    0
 .../javadoc/StandardJavadocDocletOptions.java      |    0
 .../internal/AbstractJavadocOptionFileOption.java  |    0
 .../AbstractListJavadocOptionFileOption.java       |   59 +
 .../internal/BooleanJavadocOptionFileOption.java   |    0
 .../internal/EnumJavadocOptionFileOption.java      |    0
 .../internal/FileJavadocOptionFileOption.java      |    0
 .../internal/GroupsJavadocOptionFileOption.java    |    0
 .../javadoc/internal/JavadocExecHandleBuilder.java |    0
 .../javadoc/internal/JavadocOptionFile.java        |  115 +
 .../javadoc/internal/JavadocOptionFileWriter.java  |   59 +
 .../internal/JavadocOptionFileWriterContext.java   |  109 +
 .../LinksOfflineJavadocOptionFileOption.java       |    0
 .../MultilineStringsJavadocOptionFileOption.java   |    0
 .../OptionLessStringsJavadocOptionFileOption.java  |    0
 .../internal/PathJavadocOptionFileOption.java      |    0
 .../internal/StringJavadocOptionFileOption.java    |    0
 .../internal/StringsJavadocOptionFileOption.java   |    0
 .../org/gradle/external/javadoc/package-info.java  |    0
 .../org/gradle/language/java/JavaSourceSet.java    |   28 +
 .../language/java/artifact/JavadocArtifact.java    |   28 +
 .../language/java/artifact/package-info.java       |   20 +
 .../internal/DefaultJavaLanguageSourceSet.java     |   34 +
 .../JavaLanguagePluginServiceRegistry.java         |   48 +
 .../internal/JavaToolChainServiceRegistry.java     |   71 +
 .../org/gradle/language/java/package-info.java     |    0
 .../language/java/plugins/JavaLanguagePlugin.java  |  118 +
 .../gradle/language/java/plugins/package-info.java |   23 +
 .../language/java/tasks/PlatformJavaCompile.java   |   37 +
 .../gradle/language/java/tasks/package-info.java   |   23 +
 .../gradle-plugins/org.gradle.java-lang.properties |    1 +
 ...e.internal.service.scopes.PluginServiceRegistry |    2 +
 .../internal/tasks/DefaultJavaToolChainTest.groovy |   97 +
 .../tasks/compile/CleaningJavaCompilerTest.groovy  |   61 +
 ...ndLineJavaCompilerArgumentsGeneratorTest.groovy |   81 +
 .../DefaultJavaCompileSpecFactoryTest.groovy       |   43 +
 .../compile/DefaultJavaCompilerFactoryTest.groovy  |   73 +
 .../JavaCompilerArgumentsBuilderTest.groovy        |  333 ++
 .../JavaHomeBasedJavaCompilerFactoryTest.groovy    |   81 +
 .../compile/NormalizingJavaCompilerTest.groovy     |  125 +
 .../incremental/ClassSetAnalysisUpdaterTest.groovy |   42 +
 .../IncrementalCompilationFinalizerTest.groovy     |   55 +
 .../IncrementalCompilationInitializerTest.groovy   |   63 +
 .../incremental/SourceToNameConverterTest.groovy   |   43 +
 .../DefaultClassDependenciesAnalyzerTest.groovy    |   77 +
 .../analyzer/annotations/SomeClassAnnotation.java  |    0
 .../annotations/SomeRuntimeAnnotation.java         |    0
 .../analyzer/annotations/SomeSourceAnnotation.java |    0
 .../annotations/UsesAnnotationInField.java         |    0
 .../analyzer/annotations/UsesClassAnnotation.java  |    0
 .../annotations/UsesRuntimeAnnotation.java         |    0
 .../analyzer/annotations/UsesSourceAnnotation.java |    0
 .../deps/ClassDependentsAccumulatorTest.groovy     |  106 +
 .../incremental/deps/ClassFilesAnalyzerTest.groovy |   62 +
 .../deps/ClassSetAnalysisDataSerializerTest.groovy |   50 +
 .../incremental/deps/ClassSetAnalysisTest.groovy   |  179 +
 .../deps/OutputToNameConverterTest.groovy          |   44 +
 .../jar/DefaultJarSnapshotterTest.groovy           |   67 +
 .../jar/JarClasspathSnapshotFactoryTest.groovy     |   65 +
 .../jar/JarClasspathSnapshotMakerTest.groovy       |   72 +
 .../compile/incremental/jar/JarSnapshotTest.groovy |  114 +
 .../incremental/test/AccessedFromPrivateClass.java |   24 +
 .../incremental/test/HasNonPrivateConstants.java   |   21 +
 .../incremental/test/HasPrivateConstants.java      |   22 +
 .../incremental/test/HasPublicConstants.java       |   21 +
 .../tasks/compile/incremental/test/SomeClass.java  |   35 +
 .../compile/incremental/test/SomeOtherClass.java   |   24 +
 .../test/UsedByNonPrivateConstantsClass.java       |   20 +
 .../compile/incremental/test/YetAnotherClass.java  |   20 +
 .../api/tasks/compile/CompileOptionsTest.groovy    |  165 +
 .../api/tasks/compile/DebugOptionsTest.groovy      |    0
 .../api/tasks/compile/ForkOptionsTest.groovy       |    0
 .../api/tasks/compile/JavaCompileTest.groovy       |   50 +
 .../gradle/api/tasks/javadoc/JavadocTest.groovy    |  103 +
 .../javadoc/StandardJavadocDocletOptionsTest.java  |  523 ++
 .../BooleanJavadocOptionFileOptionTest.java        |    0
 .../internal/EnumJavadocOptionFileOptionTest.java  |    0
 .../internal/FileJavadocOptionFileOptionTest.java  |    0
 .../GroupsJavadocOptionFileOptionTest.java         |    0
 .../internal/JavadocExecHandleBuilderTest.groovy   |    0
 .../javadoc/internal/JavadocOptionFileTest.java    |    0
 .../JavadocOptionFileWriterContextTest.groovy      |   57 +
 .../internal/JavadocOptionFileWriterTest.groovy    |    0
 .../LinksOfflineJavadocOptionFileOptionTest.java   |    0
 ...ultilineStringsJavadocOptionFileOptionTest.java |    0
 ...tionLessStringsJavadocOptionFileOptionTest.java |    0
 .../internal/PathJavadocOptionFileOptionTest.java  |    0
 .../StringJavadocOptionFileOptionTest.java         |    0
 .../StringsJavadocOptionFileOptionTest.java        |    0
 .../java/plugins/JavaLanguagePluginTest.groovy     |   40 +
 .../language/fixtures/BadJavaComponent.groovy      |   47 +
 .../language/fixtures/TestJavaComponent.groovy     |   72 +
 subprojects/language-jvm/language-jvm.gradle       |   15 +-
 .../jvm/JvmResourcesPluginIntegrationTest.groovy   |   22 +
 .../ResourceOnlyJvmLibraryIntegrationTest.groovy   |  111 +
 .../org/gradle/language/java/JavaSourceSet.java    |   28 -
 .../java/internal/DefaultJavaSourceSet.java        |   52 -
 .../gradle/language/jvm/ClassDirectoryBinary.java  |   40 -
 .../groovy/org/gradle/language/jvm/Classpath.java  |   28 -
 .../org/gradle/language/jvm/ResourceSet.java       |   25 -
 .../internal/ClassDirectoryBinaryNamingScheme.java |   70 -
 .../jvm/internal/DefaultClassDirectoryBinary.java  |   83 -
 .../language/jvm/internal/DefaultClasspath.java    |   39 -
 .../language/jvm/internal/DefaultResourceSet.java  |   28 -
 .../jvm/internal/SimpleStaleClassCleaner.java      |   43 -
 .../language/jvm/internal/StaleClassCleaner.java   |   43 -
 .../language/jvm/plugins/JvmLanguagePlugin.java    |  107 -
 .../gradle/language/jvm/plugins/package-info.java  |   23 -
 .../language/jvm/tasks/ProcessResources.java       |   35 -
 .../tasks/compile/CompilationFailedException.java  |    0
 .../tasks/compile/CompileSpecToArguments.java      |   23 +
 .../compile/DefaultJvmLanguageCompileSpec.java     |   87 +
 .../tasks/compile/ExecSpecBackedArgCollector.java  |   38 +
 .../tasks/compile/JvmLanguageCompileSpec.java      |   52 +
 .../tasks/compile/NoOpStaleClassCleaner.java       |   26 +
 .../compile/daemon/AbstractDaemonCompiler.java     |   51 +
 .../tasks/compile/daemon/CompileResult.java        |    0
 .../compile/daemon/CompilerClientsManager.java     |    0
 .../tasks/compile/daemon/CompilerDaemon.java       |   26 +
 .../tasks/compile/daemon/CompilerDaemonClient.java |   66 +
 .../daemon/CompilerDaemonClientProtocol.java       |    0
 .../compile/daemon/CompilerDaemonFactory.java      |    0
 .../compile/daemon/CompilerDaemonManager.java      |   55 +
 .../tasks/compile/daemon/CompilerDaemonServer.java |   65 +
 .../daemon/CompilerDaemonServerProtocol.java       |   27 +
 .../compile/daemon/CompilerDaemonStarter.java      |   63 +
 .../tasks/compile/daemon/DaemonForkOptions.java    |    0
 .../daemon/InProcessCompilerDaemonFactory.java     |   94 +
 .../gradle/api/tasks/compile/AbstractCompile.java  |    0
 .../gradle/api/tasks/compile/AbstractOptions.java  |   85 +
 .../gradle/api/tasks/compile/BaseForkOptions.java  |    0
 .../org/gradle/api/tasks/compile/package-info.java |    0
 .../org/gradle/language/jvm/JvmResourceSet.java    |   25 +
 .../DefaultJvmResourceLanguageSourceSet.java       |   26 +
 .../language/jvm/internal/EmptyClasspath.java      |   34 +
 .../jvm/internal/JvmPluginServiceRegistry.java     |   48 +
 .../org/gradle/language/jvm/package-info.java      |    0
 .../language/jvm/plugins/JvmResourcesPlugin.java   |  101 +
 .../gradle/language/jvm/plugins/package-info.java  |   23 +
 .../language/jvm/tasks/ProcessResources.java       |   35 +
 .../gradle/language/jvm/tasks/package-info.java    |    0
 .../org.gradle.jvm-resources.properties            |    1 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../daemon/CompilerClientsManagerTest.groovy       |    0
 .../daemon/CompilerDaemonManagerTest.groovy        |  100 +
 .../daemon/DaemonForkOptionsMergeTest.groovy       |    0
 .../compile/daemon/DaemonForkOptionsTest.groovy    |    0
 .../api/tasks/compile/AbstractOptionsTest.groovy   |    0
 .../java/internal/DefaultJavaSourceSetTest.groovy  |   33 -
 .../ClassDirectoryBinaryNamingSchemeTest.groovy    |   55 -
 .../DefaultClassDirectoryBinaryTest.groovy         |   79 -
 .../jvm/internal/DefaultResourceSetTest.groovy     |   32 -
 .../jvm/IncrementalTestJvmComponent.groovy         |   26 +
 .../integtests/fixtures/jvm/JvmSourceFile.java     |   69 +
 .../fixtures/jvm/TestJvmComponent.groovy           |   48 +
 ...mLanguageIncrementalBuildIntegrationTest.groovy |  189 +
 .../AbstractJvmLanguageIntegrationTest.groovy      |  211 +
 ...AbstractJvmPluginLanguageIntegrationTest.groovy |  174 +
 subprojects/language-native/language-native.gradle |   32 +
 ...eLanguageIncrementalBuildIntegrationTest.groovy |  594 +++
 ...anguageIncrementalCompileIntegrationTest.groovy |  520 ++
 .../AbstractNativeLanguageIntegrationTest.groovy   |  212 +
 ...ctNativePreCompiledHeaderIntegrationTest.groovy |  386 ++
 ...stedSamplesLanguageNativeIntegrationTest.groovy |   33 +
 .../DuplicateBaseNamesIntegrationTest.groovy       |  175 +
 ...yLanguageIncrementalBuildIntegrationTest.groovy |  145 +
 .../AssemblyLanguageIntegrationTest.groovy         |  118 +
 .../AssemblerLangPluginIntegrationTest.groovy      |   22 +
 .../plugins/AssemblerPluginIntegrationTest.groovy  |   22 +
 ...llingMixedCAndCppLanguageIntegrationTest.groovy |   26 +
 ...CLanguageIncrementalBuildIntegrationTest.groovy |   27 +
 ...anguageIncrementalCompileIntegrationTest.groovy |   50 +
 .../language/c/CLanguageIntegrationTest.groovy     |  244 +
 ...CPreCompiledHeaderSourcesIntegrationTest.groovy |   28 +
 .../c/CppCallingCLanguageIntegrationTest.groovy    |   26 +
 .../language/c/MixedLanguageIntegrationTest.groovy |  171 +
 .../c/plugins/CLangPluginIntegrationTest.groovy    |   22 +
 .../c/plugins/CPluginIntegrationTest.groovy        |   22 +
 ...pLanguageIncrementalBuildIntegrationTest.groovy |   27 +
 ...anguageIncrementalCompileIntegrationTest.groovy |   27 +
 .../language/cpp/CppLanguageIntegrationTest.groovy |  123 +
 ...pPreCompiledHeaderSourcesIntegrationTest.groovy |   28 +
 .../plugins/CppLangPluginIntegrationTest.groovy    |   22 +
 .../cpp/plugins/CppPluginIntegrationTest.groovy    |   22 +
 .../app/DuplicateAssemblerBaseNamesTestApp.groovy  |   90 +
 .../fixtures/app/DuplicateCBaseNamesTestApp.groovy |   64 +
 .../app/DuplicateCppBaseNamesTestApp.groovy        |   67 +
 .../app/DuplicateMixedSameBaseNamesTestApp.groovy  |  144 +
 .../app/DuplicateObjectiveCBaseNamesTestApp.groovy |   77 +
 .../DuplicateObjectiveCppBaseNamesTestApp.groovy   |   84 +
 ...uplicateWindowsResourcesBaseNamesTestApp.groovy |   86 +
 .../NativeLanguageSamplesIntegrationTest.groovy    |  176 +
 .../ParallelNativePluginsIntegrationTest.groovy    |  138 +
 .../MixedObjectiveCIntegrationTest.groovy          |   33 +
 ...CLanguageIncrementalBuildIntegrationTest.groovy |   59 +
 ...anguageIncrementalCompileIntegrationTest.groovy |  119 +
 .../ObjectiveCLanguageIntegrationTest.groovy       |   32 +
 ...CPreCompiledHeaderSourcesIntegrationTest.groovy |   31 +
 .../ObjectiveCUnsupportedIntegrationTest.groovy    |   55 +
 .../ObjectiveCLangPluginIntegrationTest.groovy     |   22 +
 .../plugins/ObjectiveCPluginIntegrationTest.groovy |   22 +
 ...pLanguageIncrementalBuildIntegrationTest.groovy |   31 +
 ...anguageIncrementalCompileIntegrationTest.groovy |   31 +
 .../ObjectiveCppLanguageIntegrationTest.groovy     |   32 +
 ...pPreCompiledHeaderSourcesIntegrationTest.groovy |   31 +
 .../ObjectiveCppUnsupportedIntegrationTest.groovy  |   56 +
 .../ObjectiveCppLangPluginIntegrationTest.groovy   |   22 +
 .../ObjectiveCppPluginIntegrationTest.groovy       |   22 +
 ...ResourcesIncrementalBuildIntegrationTest.groovy |  178 +
 .../rc/WindowsResourcesIntegrationTest.groovy      |  153 +
 ...ndowsResourcesUnsupportedIntegrationTest.groovy |   94 +
 ...ndowsResourceScriptPluginIntegrationTest.groovy |   22 +
 .../WindowsResourcesPluginIntegrationTest.groovy   |   22 +
 .../org/gradle/language/PreprocessingTool.java     |   45 +
 .../language/assembler/AssemblerSourceSet.java     |   45 +
 .../assembler/internal/DefaultAssembleSpec.java    |   24 +
 .../internal/DefaultAssemblerSourceSet.java        |   27 +
 .../gradle/language/assembler/package-info.java    |    0
 .../assembler/plugins/AssemblerLangPlugin.java     |   77 +
 .../assembler/plugins/AssemblerPlugin.java         |   38 +
 .../plugins/internal/AssembleTaskConfig.java       |   60 +
 .../language/assembler/plugins/package-info.java   |   20 +
 .../gradle/language/assembler/tasks/Assemble.java  |  146 +
 .../language/assembler/tasks/package-info.java     |   20 +
 .../java/org/gradle/language/c/CSourceSet.java     |   52 +
 .../language/c/internal/DefaultCCompileSpec.java   |   23 +
 .../c/internal/DefaultCPCHCompileSpec.java         |   23 +
 .../language/c/internal/DefaultCSourceSet.java     |   27 +
 .../java}/org/gradle/language/c/package-info.java  |    0
 .../gradle/language/c/plugins/CLangPCHPlugin.java  |   58 +
 .../org/gradle/language/c/plugins/CLangPlugin.java |   78 +
 .../org/gradle/language/c/plugins/CPlugin.java     |   39 +
 .../gradle/language/c/plugins/package-info.java    |   20 +
 .../java/org/gradle/language/c/tasks/CCompile.java |   34 +
 .../c/tasks/CPreCompiledHeaderCompile.java         |   31 +
 .../org/gradle/language/c/tasks/package-info.java  |   20 +
 .../java/org/gradle/language/cpp/CppSourceSet.java |   52 +
 .../cpp/internal/DefaultCppCompileSpec.java        |   23 +
 .../cpp/internal/DefaultCppPCHCompileSpec.java     |   23 +
 .../language/cpp/internal/DefaultCppSourceSet.java |   27 +
 .../org/gradle/language/cpp/package-info.java      |    0
 .../language/cpp/plugins/CppLangPCHPlugin.java     |   59 +
 .../gradle/language/cpp/plugins/CppLangPlugin.java |   77 +
 .../org/gradle/language/cpp/plugins/CppPlugin.java |   38 +
 .../gradle/language/cpp/plugins/package-info.java  |   20 +
 .../org/gradle/language/cpp/tasks/CppCompile.java  |   35 +
 .../cpp/tasks/CppPreCompiledHeaderCompile.java     |   31 +
 .../gradle/language/cpp/tasks/package-info.java    |   20 +
 .../AbstractHeaderExportingDependentSourceSet.java |   72 +
 .../internal/AbstractHeaderExportingSourceSet.java |   50 +
 .../internal/AbstractNativeCompileSpec.java        |  171 +
 .../nativeplatform/internal/CompileTaskConfig.java |  136 +
 .../internal/DefaultPreprocessingTool.java         |   39 +
 .../internal/NativeLanguageTransform.java          |   35 +
 .../internal/PCHCompileTaskConfig.java             |   58 +
 .../internal/incremental/CompilationFileState.java |   52 +
 .../internal/incremental/CompilationState.java     |   41 +
 .../incremental/CompilationStateCacheFactory.java  |   23 +
 .../incremental/CompilationStateSerializer.java    |  128 +
 .../DefaultCompilationStateCacheFactory.java       |   56 +
 .../incremental/DefaultIncrementalCompilation.java |   52 +
 .../incremental/DefaultSourceIncludes.java         |   81 +
 .../incremental/DefaultSourceIncludesParser.java   |   44 +
 .../incremental/DefaultSourceIncludesResolver.java |   67 +
 .../incremental/IncrementalCompilation.java        |   32 +
 .../incremental/IncrementalCompileProcessor.java   |  155 +
 .../incremental/IncrementalCompilerBuilder.java    |   39 +
 .../incremental/IncrementalNativeCompiler.java     |  119 +
 .../internal/incremental/ResolvedInclude.java      |   67 +
 .../internal/incremental/SourceIncludesParser.java |   26 +
 .../incremental/SourceIncludesResolver.java        |   25 +
 .../incremental/sourceparser/CSourceParser.java    |   33 +
 .../sourceparser/PreprocessingReader.java          |  147 +
 .../sourceparser/RegexBackedCSourceParser.java     |   81 +
 .../internal/registry/NativeLanguageServices.java  |   38 +
 .../tasks/AbstractNativeCompileTask.java           |  253 +
 .../tasks/AbstractNativePCHCompileTask.java        |   23 +
 .../nativeplatform/tasks/package-info.java         |   20 +
 .../language/objectivec/ObjectiveCSourceSet.java   |   55 +
 .../internal/DefaultObjectiveCCompileSpec.java     |   24 +
 .../internal/DefaultObjectiveCPCHCompileSpec.java  |   23 +
 .../internal/DefaultObjectiveCSourceSet.java       |   27 +
 .../gradle/language/objectivec/package-info.java   |    0
 .../plugins/ObjectiveCLangPCHPlugin.java           |   58 +
 .../objectivec/plugins/ObjectiveCLangPlugin.java   |   77 +
 .../objectivec/plugins/ObjectiveCPlugin.java       |   39 +
 .../language/objectivec/plugins/package-info.java  |   20 +
 .../objectivec/tasks/ObjectiveCCompile.java        |   35 +
 .../tasks/ObjectiveCPreCompiledHeaderCompile.java  |   31 +
 .../language/objectivec/tasks/package-info.java    |   20 +
 .../objectivecpp/ObjectiveCppSourceSet.java        |   54 +
 .../internal/DefaultObjectiveCppCompileSpec.java   |   24 +
 .../DefaultObjectiveCppPCHCompileSpec.java         |   23 +
 .../internal/DefaultObjectiveCppSourceSet.java     |   27 +
 .../gradle/language/objectivecpp/package-info.java |    0
 .../plugins/ObjectiveCppLangPCHPlugin.java         |   58 +
 .../plugins/ObjectiveCppLangPlugin.java            |   78 +
 .../objectivecpp/plugins/ObjectiveCppPlugin.java   |   39 +
 .../objectivecpp/plugins/package-info.java         |   20 +
 .../objectivecpp/tasks/ObjectiveCppCompile.java    |   35 +
 .../ObjectiveCppPreCompiledHeaderCompile.java      |   31 +
 .../language/objectivecpp/tasks/package-info.java  |   20 +
 .../java}/org/gradle/language/package-info.java    |    0
 .../org/gradle/language/rc/WindowsResourceSet.java |   52 +
 .../DefaultWindowsResourceCompileSpec.java         |   22 +
 .../rc/internal/DefaultWindowsResourceSet.java     |   26 +
 .../java}/org/gradle/language/rc/package-info.java |    0
 .../rc/plugins/WindowsResourceScriptPlugin.java    |   88 +
 .../rc/plugins/WindowsResourcesPlugin.java         |   38 +
 .../WindowsResourcesCompileTaskConfig.java         |   78 +
 .../gradle/language/rc/plugins/package-info.java   |   20 +
 .../language/rc/tasks/WindowsResourceCompile.java  |  197 +
 .../org/gradle/language/rc/tasks/package-info.java |   20 +
 .../org.gradle.assembler-lang.properties           |    1 +
 .../gradle-plugins/org.gradle.assembler.properties |    1 +
 .../gradle-plugins/org.gradle.c-lang.properties    |    1 +
 .../gradle-plugins/org.gradle.c.properties         |    1 +
 .../gradle-plugins/org.gradle.cpp-lang.properties  |    1 +
 .../gradle-plugins/org.gradle.cpp.properties       |    1 +
 .../org.gradle.objective-c-lang.properties         |    1 +
 .../org.gradle.objective-c.properties              |    1 +
 .../org.gradle.objective-cpp-lang.properties       |    1 +
 .../org.gradle.objective-cpp.properties            |    1 +
 .../org.gradle.windows-resource-script.properties  |    1 +
 .../org.gradle.windows-resources.properties        |    1 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../AbstractNativeComponentPluginTest.groovy       |  180 +
 .../plugins/AssemblerLangPluginTest.groovy         |   36 +
 .../assembler/plugins/AssemblerPluginTest.groovy   |  172 +
 .../language/assembler/tasks/AssemblerTest.groovy  |   68 +
 .../language/c/plugins/CLangPluginTest.groovy      |   36 +
 .../gradle/language/c/plugins/CPluginTest.groovy   |   49 +
 .../gradle/language/c/tasks/CCompileTest.groovy    |   71 +
 .../cpp/internal/DefaultCppSourceSetTest.groovy    |   66 +
 .../language/cpp/plugins/CppLangPluginTest.groovy  |   38 +
 .../language/cpp/plugins/CppPluginTest.groovy      |   48 +
 .../language/cpp/tasks/CppCompileTest.groovy       |   71 +
 .../CompilationStateSerializerTest.groovy          |   90 +
 .../DefaultSourceIncludesParserTest.groovy         |   68 +
 .../DefaultSourceIncludesResolverTest.groovy       |  155 +
 .../IncrementalCompileProcessorTest.groovy         |  426 ++
 .../IncrementalNativeCompilerTest.groovy           |  105 +
 .../sourceparser/PreprocessingReaderTest.groovy    |   88 +
 .../RegexBackedCSourceParserTest.groovy            |  446 ++
 .../plugins/ObjectiveCLangPluginTest.groovy        |   38 +
 .../objectivec/plugins/ObjectiveCPluginTest.groovy |   47 +
 .../plugins/ObjectiveCppLangPluginTest.groovy      |   37 +
 .../plugins/ObjectiveCppPluginTest.groovy          |   46 +
 subprojects/language-scala/language-scala.gradle   |   17 +
 .../scala/JointScalaLangIntegrationTest.groovy     |   25 +
 .../SampleScalaLanguageIntegrationTest.groovy      |   44 +
 ...aLanguageIncrementalBuildIntegrationTest.groovy |   25 +
 .../scala/ScalaLanguageIntegrationTest.groovy      |   48 +
 ...aToolProviderNotAvailableIntegrationTest.groovy |   50 +
 .../ScalaLanguagePluginGoodBehaviourTest.groovy    |   26 +
 .../ScalaLanguagePluginIntegrationTest.groovy      |   24 +
 .../internal/tasks/scala/DaemonScalaCompiler.java  |   71 +
 .../scala/DefaultScalaJavaJointCompileSpec.java    |   62 +
 .../DefaultScalaJavaJointCompileSpecFactory.java   |   49 +
 .../tasks/scala/NormalizingScalaCompiler.java      |  110 +
 .../api/internal/tasks/scala/ScalaCompileSpec.java |   31 +
 .../tasks/scala/ScalaJavaJointCompileSpec.java     |    0
 .../internal/tasks/scala/ZincScalaCompiler.java    |  124 +
 .../scala/ZincScalaCompilerArgumentsGenerator.java |   66 +
 .../api/tasks/scala/IncrementalCompileOptions.java |   71 +
 .../gradle/api/tasks/scala/ScalaForkOptions.java   |    0
 .../org/gradle/api/tasks/scala/package-info.java   |   21 +
 .../language/scala/ScalaLanguageSourceSet.java     |   29 +
 .../org/gradle/language/scala/ScalaPlatform.java   |   28 +
 .../internal/DefaultScalaLanguageSourceSet.java    |   36 +
 .../scala/internal/DefaultScalaPlatform.java       |   50 +
 .../toolchain/DefaultScalaToolProvider.java        |   70 +
 .../toolchain/DownloadingScalaToolChain.java       |   75 +
 .../toolchain/NotFoundScalaToolProvider.java       |   60 +
 .../internal/toolchain/ScalaToolChainInternal.java |   24 +
 .../toolchain/ScalaToolChainServiceRegistry.java   |   47 +
 .../org/gradle/language/scala/package-info.java    |   20 +
 .../scala/plugins/ScalaLanguagePlugin.java         |  131 +
 .../language/scala/plugins/package-info.java       |   23 +
 .../language/scala/tasks/AbstractScalaCompile.java |  135 +
 .../scala/tasks/BaseScalaCompileOptions.java       |  218 +
 .../language/scala/tasks/PlatformScalaCompile.java |   58 +
 .../gradle/language/scala/tasks/package-info.java  |   23 +
 .../language/scala/toolchain/ScalaToolChain.java   |   25 +
 .../language/scala/toolchain/package-info.java     |   24 +
 .../org.gradle.scala-lang.properties               |   17 +
 ...e.internal.service.scopes.PluginServiceRegistry |   16 +
 ...aultScalaJavaJointCompileSpecFactoryTest.groovy |   45 +
 .../scala/NormalizingScalaCompilerTest.groovy      |  116 +
 .../ZincScalaCompilerArgumentsGeneratorTest.groovy |  110 +
 .../toolchain/DefaultScalaToolProviderTest.groovy  |   46 +
 .../toolchain/DownloadingScalaToolChainTest.groovy |  103 +
 .../scala/plugins/ScalaLanguagePluginTest.groovy   |   40 +
 .../language/scala/fixtures/BadScalaLibrary.groovy |   43 +
 .../fixtures/TestJointCompiledComponent.groovy     |   50 +
 .../scala/fixtures/TestScalaComponent.groovy       |   60 +
 subprojects/launcher/launcher.gradle               |   10 +
 .../BuildEnvironmentIntegrationTest.groovy         |  198 +
 .../launcher/CommandLineIntegrationSpec.groovy     |   75 +
 ...EnablingParallelExecutionIntegrationTest.groovy |   16 +-
 .../GradleConfigurabilityIntegrationSpec.groovy    |   49 +-
 .../launcher/GradleNativeIntegrationTest.groovy    |   36 +
 .../daemon/DaemonFeedbackIntegrationSpec.groovy    |   41 +-
 .../DaemonHealthLoggingIntegrationTest.groovy      |   30 +
 .../DaemonInitScriptHandlingIntegrationTest.groovy |    2 +-
 ...itialCommunicationFailureIntegrationSpec.groovy |   23 +-
 .../launcher/daemon/DaemonIntegrationSpec.groovy   |   19 +-
 .../launcher/daemon/DaemonLifecycleSpec.groovy     |  181 +-
 .../DaemonNativeServicesIntegrationTest.groovy     |   34 +
 .../DaemonOutputToggleIntegrationTest.groovy       |   39 +
 ...emonPerformanceMonitoringIntegrationTest.groovy |   77 +
 .../daemon/DaemonReuseIntegrationTest.groovy       |   35 +
 .../DaemonStartupMessageIntegrationTest.groovy     |   61 +
 .../DaemonUsageSuggestionIntegrationTest.groovy    |   68 +
 .../launcher/daemon/EmbeddedDaemonSmokeTest.groovy |   71 -
 .../gradle/launcher/daemon/ExecuteBuildAction.java |   28 -
 .../launcher/daemon/IsolatedDaemonSpec.groovy      |   37 +
 .../LocaleSupportDaemonIntegrationTest.groovy      |   95 +
 .../ProcessCrashHandlingIntegrationTest.groovy     |   80 +
 .../daemon/SingleUseDaemonIntegrationTest.groovy   |  107 +-
 .../daemon/StoppingDaemonIntegrationSpec.groovy    |   57 +-
 .../daemon/testing/DaemonContextParser.java        |   48 -
 .../daemon/testing/DaemonLogsAnalyzer.groovy       |   55 -
 .../daemon/testing/DaemonsEventSequence.groovy     |    2 +-
 .../launcher/daemon/testing/TestableDaemon.groovy  |  116 -
 .../src/main/java/org/gradle/launcher/Main.java    |   17 +-
 .../org/gradle/launcher/bootstrap/EntryPoint.java  |    8 +-
 .../launcher/bootstrap/ProcessBootstrap.java       |    8 +-
 .../gradle/launcher/cli/BuildActionsFactory.java   |   86 +-
 .../org/gradle/launcher/cli/CommandLineAction.java |    4 +-
 .../launcher/cli/CommandLineActionFactory.java     |   58 +-
 .../gradle/launcher/cli/ExecuteBuildAction.java    |   12 +-
 .../org/gradle/launcher/cli/GuiActionsFactory.java |    7 +-
 .../launcher/cli/JavaRuntimeValidationAction.java  |   37 +
 .../org/gradle/launcher/cli/RunBuildAction.java    |   25 +-
 .../org/gradle/launcher/cli/StopDaemonAction.java  |    6 +-
 .../cli/converter/DaemonCommandLineConverter.java  |    5 -
 .../cli/converter/LayoutToPropertiesConverter.java |   12 +-
 .../PropertiesToDaemonParametersConverter.java     |    5 +-
 .../PropertiesToStartParameterConverter.java       |   20 +-
 .../launcher/daemon/bootstrap/DaemonGreeter.java   |   20 +-
 .../launcher/daemon/bootstrap/DaemonMain.java      |   87 +-
 .../bootstrap/DaemonStartupCommunication.java      |   82 +-
 .../daemon/bootstrap/ForegroundDaemonAction.java   |   53 +
 .../daemon/bootstrap/ForegroundDaemonMain.java     |   49 -
 .../daemon/client/DaemonCancelForwarder.java       |   50 +
 .../launcher/daemon/client/DaemonClient.java       |   91 +-
 .../daemon/client/DaemonClientConnection.java      |   34 +-
 .../daemon/client/DaemonClientFactory.java         |   60 +
 .../daemon/client/DaemonClientGlobalServices.java  |   28 +
 .../daemon/client/DaemonClientInputForwarder.java  |   54 +-
 .../client/DaemonClientInterruptedException.java   |   30 +
 .../daemon/client/DaemonClientServices.java        |   48 +-
 .../daemon/client/DaemonClientServicesSupport.java |   23 +-
 .../launcher/daemon/client/DaemonConnector.java    |    9 +
 .../daemon/client/DaemonDisappearedException.java  |    2 +-
 .../daemon/client/DaemonStartListener.java         |   26 +
 .../launcher/daemon/client/DaemonStopClient.java   |  116 +
 .../daemon/client/DefaultDaemonConnector.java      |   60 +-
 .../daemon/client/DefaultDaemonStarter.java        |   18 +-
 .../client/EmbeddedDaemonClientServices.java       |   27 +-
 .../daemon/client/EmbeddedDaemonStarter.java       |    2 +-
 .../launcher/daemon/client/InputForwarder.java     |    2 +-
 .../daemon/client/JvmVersionValidator.java         |   66 +
 .../daemon/client/SingleUseDaemonClient.java       |    9 +-
 .../daemon/client/StopDaemonClientServices.java    |   46 -
 .../launcher/daemon/client/StopDispatcher.java     |   12 +-
 .../daemon/client/StubDaemonHealthServices.java    |   34 +
 .../daemon/configuration/CurrentProcess.java       |   21 +-
 .../daemon/configuration/DaemonParameters.java     |   44 +-
 .../launcher/daemon/configuration/DaemonUsage.java |   39 +
 .../daemon/configuration/GradleProperties.java     |    3 +-
 .../daemon/context/DaemonCompatibilitySpec.java    |    2 +-
 .../launcher/daemon/context/DaemonContext.java     |    2 +-
 .../daemon/context/DaemonContextBuilder.java       |   12 +-
 .../daemon/context/DaemonInstanceDetails.java      |   30 +
 .../daemon/diagnostics/DaemonStartupInfo.java      |   25 +-
 .../launcher/daemon/logging/DaemonMessages.java    |    1 +
 .../org/gradle/launcher/daemon/protocol/Build.java |   19 +-
 .../launcher/daemon/protocol/BuildAndStop.java     |    7 +-
 .../launcher/daemon/protocol/BuildEvent.java       |   37 +
 .../launcher/daemon/protocol/BuildStarted.java     |    4 +-
 .../gradle/launcher/daemon/protocol/Cancel.java    |   23 +
 .../gradle/launcher/daemon/protocol/Command.java   |    4 +-
 .../daemon/protocol/DaemonUnavailable.java         |    4 +-
 .../gradle/launcher/daemon/protocol/Finished.java  |    8 +-
 .../gradle/launcher/daemon/protocol/Message.java   |   25 +
 .../gradle/launcher/daemon/protocol/Result.java    |    8 +-
 .../launcher/daemon/protocol/StopWhenIdle.java     |   22 +
 .../launcher/daemon/registry/DaemonInfo.java       |   15 +-
 .../launcher/daemon/registry/DaemonRegistry.java   |    4 +
 .../daemon/registry/PersistentDaemonRegistry.java  |    2 +-
 .../org/gradle/launcher/daemon/server/Daemon.java  |   10 +-
 .../launcher/daemon/server/DaemonServices.java     |   36 +-
 .../daemon/server/DaemonStateCoordinator.java      |  189 +-
 .../daemon/server/DaemonStoppedException.java      |   30 -
 .../daemon/server/DaemonTcpServerConnector.java    |    8 +-
 .../daemon/server/DefaultDaemonConnection.java     |  127 +-
 .../server/DefaultIncomingConnectionHandler.java   |   23 +-
 .../daemon/server/api/DaemonCommandAction.java     |   35 +
 .../daemon/server/api/DaemonCommandExecution.java  |  136 +
 .../daemon/server/api/DaemonConnection.java        |  109 +
 .../daemon/server/api/DaemonStateControl.java      |   65 +
 .../daemon/server/api/DaemonStoppedException.java  |   28 +
 .../server/api/DaemonUnavailableException.java     |   27 +
 .../launcher/daemon/server/api/StdinHandler.java   |   25 +
 .../daemon/server/exec/BuildCommandOnly.java       |    2 +
 .../server/exec/CatchAndForwardDaemonFailure.java  |   38 -
 .../daemon/server/exec/DaemonCommandAction.java    |   35 -
 .../daemon/server/exec/DaemonCommandExecuter.java  |    4 +-
 .../daemon/server/exec/DaemonCommandExecution.java |  134 -
 .../daemon/server/exec/DaemonConnection.java       |   90 -
 .../daemon/server/exec/DaemonHygieneAction.java    |   50 -
 .../daemon/server/exec/DaemonStateControl.java     |   46 -
 .../server/exec/DaemonUnavailableException.java    |   27 -
 .../server/exec/DefaultDaemonCommandExecuter.java  |   56 +-
 .../server/exec/EstablishBuildEnvironment.java     |   20 +-
 .../launcher/daemon/server/exec/ExecuteBuild.java  |   33 +-
 .../daemon/server/exec/ForwardClientInput.java     |    3 +
 .../launcher/daemon/server/exec/HandleCancel.java  |   42 +
 .../launcher/daemon/server/exec/HandleStop.java    |   39 -
 .../launcher/daemon/server/exec/LogToClient.java   |   16 +-
 .../server/exec/NoOpDaemonCommandAction.java       |    3 +
 .../server/exec/RequestStopIfSingleUsedDaemon.java |   36 +
 .../daemon/server/exec/ResetDeprecationLogger.java |    2 +
 .../launcher/daemon/server/exec/ReturnResult.java  |    2 +
 .../server/exec/StartBuildOrRespondWithBusy.java   |   13 +-
 .../server/exec/StartStopIfBuildAndStop.java       |   35 -
 .../launcher/daemon/server/exec/StdinHandler.java  |   25 -
 .../server/exec/StopHandlingCommandExecuter.java   |   45 +
 .../daemon/server/exec/WatchForDisconnection.java  |    9 +-
 .../daemon/server/health/DaemonHealthServices.java |   32 +
 .../daemon/server/health/DaemonHealthTracker.java  |   56 +
 .../launcher/daemon/server/health/DaemonStats.java |   93 +
 .../daemon/server/health/DaemonStatus.java         |   48 +
 .../server/health/DefaultDaemonHealthServices.java |   42 +
 .../daemon/server/health/HealthLogger.java         |   34 +
 .../daemon/server/health/HintGCAfterBuild.java     |   52 +
 .../launcher/daemon/server/health/MemoryInfo.java  |   61 +
 .../gradle/launcher/exec/BuildActionExecuter.java  |    6 +-
 .../launcher/exec/BuildActionParameters.java       |    9 +-
 .../launcher/exec/ChainingBuildActionRunner.java   |   42 +
 .../DaemonUsageSuggestingBuildActionExecuter.java  |   65 +
 .../exec/DefaultBuildActionParameters.java         |   23 +-
 .../exec/InProcessBuildActionExecuter.java         |   84 +-
 .../internal/impl/DefaultBuildInvocations.java     |   45 +
 .../internal/impl/LaunchableGradleTask.java        |   23 +-
 .../impl/LaunchableGradleTaskSelector.java         |   20 +-
 .../internal/impl/LaunchableImplementation.java    |   31 -
 .../internal/provider/BuildModelAction.java        |   91 +-
 .../internal/provider/ClassLoaderCache.java        |   72 +
 .../internal/provider/ClasspathInferer.java        |    2 +-
 .../provider/ClientProvidedBuildAction.java        |   62 +-
 .../ClientSidePayloadClassLoaderFactory.java       |  121 +
 .../internal/provider/ConfiguringBuildAction.java  |  152 -
 .../internal/provider/ConnectionScopeServices.java |   27 +-
 .../provider/DaemonBuildActionExecuter.java        |   27 +-
 .../DaemonSidePayloadClassLoaderFactory.java       |   91 +
 .../internal/provider/DefaultBuildController.java  |   70 -
 .../internal/provider/DefaultConnection.java       |  148 +-
 .../DefaultPayloadClassLoaderRegistry.java         |  115 +-
 .../provider/ExecuteBuildActionRunner.java         |   32 +
 .../provider/InternalCancellationTokenAdapter.java |   40 +
 .../gradle/tooling/internal/provider/JarCache.java |  102 +
 .../internal/provider/LauncherServices.java        |   82 +
 .../LoggingBridgingBuildActionExecuter.java        |   28 +-
 .../internal/provider/ModelClassLoaderFactory.java |   99 +-
 .../provider/PayloadClassLoaderFactory.java        |   30 +
 .../internal/provider/PayloadSerializer.java       |   23 +-
 .../internal/provider/ProviderConnection.java      |   98 +-
 .../provider/ProviderStartParameterConverter.java  |   99 +
 .../internal/provider/ReflectionClassLookup.java   |   44 -
 .../internal/provider/ShutdownCoordinator.java     |   51 +
 .../provider/ToolingGlobalScopeServices.java       |   25 -
 .../tooling/internal/provider/ToolingServices.java |   32 -
 .../connection/AdaptedOperationParameters.java     |  129 -
 .../provider/connection/BuildLogLevelMixIn.java    |    2 +-
 .../connection/ProviderConnectionParameters.java   |    7 +
 .../connection/ProviderOperationParameters.java    |   10 +-
 .../provider/events/AbstractTestProgressEvent.java |   39 +
 .../provider/events/AbstractTestResult.java        |   48 +
 .../internal/provider/events/DefaultFailure.java   |   60 +
 .../provider/events/DefaultTestDescriptor.java     |   85 +
 .../provider/events/DefaultTestFailureResult.java  |   40 +
 .../events/DefaultTestFinishedProgressEvent.java   |   38 +
 .../provider/events/DefaultTestSkippedResult.java  |   30 +
 .../events/DefaultTestStartedProgressEvent.java    |   30 +
 .../provider/events/DefaultTestSuccessResult.java  |   30 +
 ...e.internal.service.scopes.PluginServiceRegistry |    2 +-
 .../groovy/org/gradle/launcher/MainTest.groovy     |    2 +-
 .../launcher/bootstrap/EntryPointTest.groovy       |    2 +-
 .../launcher/cli/BuildActionsFactoryTest.groovy    |   49 +-
 .../cli/CommandLineActionFactoryTest.groovy        |   36 +-
 .../launcher/cli/GuiActionsFactoryTest.groovy      |    2 +-
 .../gradle/launcher/cli/RunBuildActionTest.groovy  |   21 +-
 .../launcher/cli/StopDaemonActionTest.groovy       |    4 +-
 .../DaemonCommandLineConverterTest.groovy          |   27 +-
 .../LayoutToPropertiesConverterTest.groovy         |   12 +
 ...ropertiesToDaemonParametersConverterTest.groovy |   46 +-
 .../PropertiesToStartParameterConverterTest.groovy |   13 +-
 .../daemon/DaemonExecHandleBuilderSpec.groovy      |    3 +-
 .../daemon/bootstrap/DaemonGreeterTest.groovy      |   23 +-
 .../DaemonStartupCommunicationSpec.groovy          |   42 +-
 .../daemon/client/DaemonCancelForwarderTest.groovy |   70 +
 .../client/DaemonClientConnectionTest.groovy       |   16 +-
 .../client/DaemonClientInputForwarderTest.groovy   |    2 +-
 .../daemon/client/DaemonClientServicesTest.groovy  |   21 +-
 .../launcher/daemon/client/DaemonClientTest.groovy |  128 +-
 .../daemon/client/DaemonStopClientTest.groovy      |  118 +
 .../client/DefaultDaemonConnectorTest.groovy       |    6 +-
 .../daemon/client/InputForwarderTest.groovy        |    2 +
 .../daemon/client/JvmVersionValidatorTest.groovy   |   65 +
 .../daemon/client/StopDispatcherTest.groovy        |   12 +-
 .../daemon/configuration/CurrentProcessTest.groovy |   40 +-
 .../configuration/DaemonParametersTest.groovy      |   61 +-
 .../context/DaemonCompatibilitySpecSpec.groovy     |    6 +-
 .../registry/DaemonRegistryServicesTest.groovy     |    1 -
 .../registry/PersistentDaemonRegistryTest.groovy   |    2 +-
 .../DaemonServerExceptionHandlingTest.groovy       |   45 +-
 .../daemon/server/DaemonServicesTest.groovy        |    4 +-
 .../server/DaemonStateCoordinatorTest.groovy       |  265 +-
 .../server/DefaultDaemonConnectionTest.groovy      |    3 +-
 .../server/exec/DaemonHygieneActionTest.groovy     |   52 -
 .../server/health/DaemonHealthTrackerTest.groovy   |   69 +
 .../daemon/server/health/DaemonStatsTest.groovy    |   61 +
 .../daemon/server/health/DaemonStatusTest.groovy   |   76 +
 .../daemon/server/health/HealthLoggerTest.groovy   |   44 +
 .../server/health/HintGCAfterBuildTest.groovy      |   52 +
 .../exec/ChainingBuildActionRunnerTest.groovy      |   44 +
 ...onUsageSuggestingBuildActionExecuterTest.groovy |  111 +
 .../exec/DefaultBuildActionParametersTest.groovy   |    5 +-
 .../exec/InProcessBuildActionExecuterTest.groovy   |  141 +-
 .../provider/AbstractClassGraphSpec.groovy         |   24 +-
 .../internal/provider/ClasspathInfererTest.groovy  |   54 +
 .../ClientSidePayloadClassLoaderFactoryTest.groovy |   36 +
 .../provider/ConfiguringBuildActionTest.groovy     |  122 -
 .../provider/DaemonBuildActionExecuterTest.groovy  |   10 +-
 .../DaemonSidePayloadClassLoaderFactoryTest.groovy |   76 +
 .../provider/DefaultBuildControllerTest.groovy     |  129 -
 .../tooling/internal/provider/JarCacheTest.groovy  |  130 +
 .../LoggingBridgingBuildActionExecuterTest.groovy  |   16 +-
 .../provider/ModelClassLoaderFactoryTest.groovy    |   36 -
 .../internal/provider/PayloadSerializerTest.groovy |    5 +-
 .../ProviderStartParameterConverterTest.groovy     |  122 +
 .../provider/ToolingGlobalScopeServicesTest.groovy |   29 -
 .../AdaptedOperationParametersTest.groovy          |   68 -
 .../daemon/testing/AbstractDaemonFixture.groovy    |  102 +
 .../daemon/testing/DaemonContextParser.java        |   77 +
 .../launcher/daemon/testing/DaemonFixture.java     |   54 +
 .../daemon/testing/DaemonLogFileStateProbe.groovy  |   84 +
 .../daemon/testing/DaemonLogsAnalyzer.groovy       |   81 +
 .../daemon/testing/DaemonRegistryStateProbe.groovy |   40 +
 .../launcher/daemon/testing/DaemonStateProbe.java  |   21 +
 .../launcher/daemon/testing/DaemonsFixture.java    |   41 +
 .../launcher/daemon/testing/LegacyDaemon.groovy    |   57 +
 .../launcher/daemon/testing/TestableDaemon.groovy  |   61 +
 subprojects/maven/maven.gradle                     |    9 +-
 .../maven/AbstractMavenPublishIntegTest.groovy     |   98 -
 .../MavenPublicationVersionRangeIntegTest.groovy   |   64 +
 ...venPublishArtifactCustomizationIntegTest.groovy |    4 +-
 .../maven/MavenPublishBasicIntegTest.groovy        |    6 +-
 .../maven/MavenPublishCoordinatesIntegTest.groovy  |    1 +
 .../MavenPublishCrossVersionIntegrationTest.groovy |   11 +-
 .../maven/MavenPublishDependenciesIntegTest.groovy |  111 +
 .../publish/maven/MavenPublishEarIntegTest.groovy  |    2 +
 .../publish/maven/MavenPublishHttpIntegTest.groovy |   18 +-
 .../maven/MavenPublishHttpsIntegTest.groovy        |  152 +
 ...avenPublishIdentifierValidationIntegTest.groovy |   20 +-
 .../maven/MavenPublishIssuesIntegTest.groovy       |   72 +-
 .../publish/maven/MavenPublishJavaIntegTest.groovy |    4 +-
 .../maven/MavenPublishMultiProjectIntegTest.groovy |    4 +-
 .../MavenPublishPomCustomizationIntegTest.groovy   |    4 +-
 .../maven/MavenPublishPomPackagingIntegTest.groovy |  179 +
 .../maven/MavenPublishWarProjectIntegTest.groovy   |    4 +-
 .../plugins/MavenPublishPluginIntegTest.groovy     |    5 -
 .../maven/MavenPomGenerationIntegrationTest.groovy |    5 +
 .../MavenPublishIgnoresMavenSettingsTest.groovy    |    3 -
 .../maven/MavenPublishIntegrationTest.groovy       |   66 +-
 .../MavenPublishVersionRangeIntegrationTest.groovy |   97 +
 ...SamplesMavenPomGenerationIntegrationTest.groovy |    2 +-
 .../SamplesMavenQuickstartIntegrationTest.groovy   |    2 +-
 .../apache/maven/artifact/ant/Authentication.java  |   44 +
 .../org/apache/maven/artifact/ant/Proxy.java       |   45 +
 .../maven/artifact/ant/RemoteRepository.java       |   97 +
 .../org/apache/maven/artifact/ant/Repository.java  |   85 +
 .../maven/artifact/ant/RepositoryPolicy.java       |   69 +
 .../apache/maven/artifact/ant/package-info.java    |   25 +
 .../maven/Conf2ScopeMappingContainer.java          |    2 +-
 .../gradle/api/artifacts/maven/MavenDeployer.java  |    2 +-
 .../gradle/api/artifacts/maven/MavenResolver.java  |    2 +-
 .../groovy/org/gradle/api/plugins/MavenPlugin.java |   22 +-
 .../maven/internal/CustomModelBuilder.java         |   82 -
 .../DefaultConf2ScopeMappingContainer.java         |  112 -
 .../maven/internal/DefaultDeployerFactory.java     |   19 +-
 .../maven/internal/DefaultMavenFactory.java        |   45 -
 .../maven/internal/DefaultMavenPom.java            |  236 -
 .../maven/internal/DefaultMavenPomFactory.java     |   43 -
 .../maven/internal/ExcludeRuleConverter.java       |   23 -
 .../maven/internal/MavenVersionRangeMapper.java    |   37 +
 .../maven/internal/PomDependenciesConverter.java   |   26 -
 .../maven/internal/VersionRangeMapper.java         |   21 +
 .../action/AbstractMavenPublishAction.java         |  200 +
 .../action/LoggingMavenTransferListener.java       |   65 +
 .../maven/internal/action/MavenDeployAction.java   |   97 +
 .../maven/internal/action/MavenInstallAction.java  |   40 +
 .../maven/internal/action/MavenPublishAction.java  |   26 +
 .../internal/action/MavenWagonDeployAction.java    |   38 +
 .../maven/internal/action/ParsedMavenPom.java      |   64 +
 .../maven/internal/ant/AbstractMavenResolver.java  |  314 --
 .../maven/internal/ant/BaseMavenDeployer.java      |  119 -
 .../maven/internal/ant/BaseMavenInstaller.java     |   40 -
 .../maven/internal/ant/CustomDeployTask.java       |   45 -
 .../ant/CustomInstallDeployTaskSupport.java        |   27 -
 .../maven/internal/ant/CustomInstallTask.java      |   38 -
 .../internal/ant/DefaultExcludeRuleConverter.java  |   37 -
 .../internal/ant/DefaultGroovyMavenDeployer.groovy |   47 -
 .../ant/DefaultPomDependenciesConverter.java       |  153 -
 .../internal/ant/EmptyMavenSettingsSupplier.java   |   49 -
 .../maven/internal/ant/LoggingHelper.java          |   43 -
 .../maven/internal/ant/MavenSettingsSupplier.java  |   24 -
 .../ant/MaybeUserMavenSettingsSupplier.java        |   44 -
 .../ProjectDependencyArtifactIdExtractorHack.java  |  109 -
 .../maven/internal/ant/RepositoryBuilder.java      |   32 -
 .../maven/internal/ant/RepositoryFactory.java      |   51 -
 .../internal/deployer/AbstractMavenResolver.java   |  196 +
 .../maven/internal/deployer/BaseMavenDeployer.java |  101 +
 .../internal/deployer/BaseMavenInstaller.java      |   39 +
 .../deployer/DefaultGroovyMavenDeployer.groovy     |   49 +
 .../maven/internal/deployer/RepositoryBuilder.java |   32 +
 .../maven/internal/deployer/RepositoryFactory.java |   51 +
 .../maven/internal/pom/CustomModelBuilder.java     |   82 +
 .../pom/DefaultConf2ScopeMappingContainer.java     |  112 +
 .../internal/pom/DefaultExcludeRuleConverter.java  |   35 +
 .../maven/internal/pom/DefaultMavenFactory.java    |   51 +
 .../maven/internal/pom/DefaultMavenPom.java        |  236 +
 .../maven/internal/pom/DefaultMavenPomFactory.java |   43 +
 .../pom/DefaultPomDependenciesConverter.java       |  158 +
 .../maven/internal/pom/ExcludeRuleConverter.java   |   23 +
 .../maven/internal/pom/PlexusLoggerAdapter.java    |   98 +
 .../internal/pom/PomDependenciesConverter.java     |   26 +
 .../ProjectDependencyArtifactIdExtractorHack.java  |  111 +
 .../wagon/RepositoryTransportDeployWagon.java      |  304 ++
 .../wagon/RepositoryTransportWagonAdapter.java     |   61 +
 .../maven/internal/wagon/WagonRegistry.java        |   45 +
 .../gradle/api/publish/maven/MavenPublication.java |   14 +-
 .../maven/internal/MavenPublishServices.java       |   61 +
 .../maven/internal/MavenPublishTaskModelRule.java  |  109 -
 .../MavenArtifactNotationParserFactory.java        |   92 +-
 .../dependencies/DefaultMavenDependency.java       |   11 +
 .../dependencies/MavenDependencyInternal.java      |    2 +
 .../internal/publication/DefaultMavenPom.java      |    6 +-
 .../publication/DefaultMavenPublication.java       |   51 +-
 .../publication/MavenPublicationInternal.java      |    2 +-
 .../AbstractAntTaskBackedMavenPublisher.java       |  116 -
 .../internal/publisher/AbstractMavenPublisher.java |   76 +
 .../AntTaskBackedMavenLocalPublisher.java          |   64 -
 .../publisher/AntTaskBackedMavenPublisher.java     |   68 -
 .../internal/publisher/MavenLocalPublisher.java    |   38 +
 .../publisher/MavenNormalizedPublication.java      |    8 +-
 .../internal/publisher/MavenRemotePublisher.java   |  103 +
 .../publisher/MavenRemoteRepositoryFactory.java    |   50 -
 .../internal/tasks/MavenPomFileGenerator.java      |   23 +-
 .../publish/maven/plugins/MavenPublishPlugin.java  |   92 +-
 .../maven/tasks/AbstractPublishToMaven.java        |  107 +
 .../api/publish/maven/tasks/GenerateMavenPom.java  |   21 +-
 .../publish/maven/tasks/PublishToMavenLocal.java   |   46 +-
 .../maven/tasks/PublishToMavenRepository.java      |   90 +-
 .../main/java/org/gradle/maven/MavenModule.java    |   27 +
 .../java/org/gradle/maven/MavenPomArtifact.java    |   27 +
 .../main/java/org/gradle/maven/package-info.java   |   23 +
 ...perties => org.gradle.maven-publish.properties} |    0
 ...aven.properties => org.gradle.maven.properties} |    0
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../api/plugins/MavenPluginConventionTest.groovy   |    2 +-
 .../org/gradle/api/plugins/MavenPluginTest.java    |   24 +-
 .../maven/internal/DefaultArtifactPomTest.java     |    2 +
 .../DefaultConf2ScopeMappingContainerTest.java     |  125 -
 .../internal/DefaultMavenPomFactoryTest.groovy     |   40 -
 .../maven/internal/DefaultMavenPomTest.groovy      |  186 -
 .../internal/MavenVersionRangeMapperTest.groovy    |   26 +
 .../ant/DefaultExcludeRuleConverterTest.java       |   54 -
 .../ant/DefaultGroovyMavenDeployerTest.groovy      |  110 -
 .../ant/DefaultGroovyPomFilterContainerTest.groovy |  120 -
 .../ant/DefaultPomDependenciesConverterTest.java   |  250 -
 .../ant/EmptyMavenSettingsSupplierTest.groovy      |   52 -
 .../ant/MaybeUserMavenSettingsSupplierTest.groovy  |   61 -
 ...ectDependencyArtifactIdExtractorHackTest.groovy |   90 -
 .../deployer/DefaultGroovyMavenDeployerTest.groovy |  114 +
 .../pom/DefaultConf2ScopeMappingContainerTest.java |  125 +
 .../pom/DefaultExcludeRuleConverterTest.java       |   54 +
 .../pom/DefaultGroovyPomFilterContainerTest.groovy |  120 +
 .../internal/pom/DefaultMavenPomFactoryTest.groovy |   40 +
 .../maven/internal/pom/DefaultMavenPomTest.groovy  |  186 +
 .../pom/DefaultPomDependenciesConverterTest.groovy |  260 +
 ...ectDependencyArtifactIdExtractorHackTest.groovy |   90 +
 .../RepositoryTransportDeployWagonTest.groovy      |  309 ++
 .../RepositoryTransportWagonAdapterTest.groovy     |   91 +
 .../MavenArtifactNotationParserFactoryTest.groovy  |    2 +-
 .../publication/DefaultMavenPublicationTest.groovy |   48 +-
 .../publisher/ValidatingMavenPublisherTest.groovy  |   36 +-
 .../tasks/MavenPomFileGeneratorTest.groovy         |   82 +-
 .../maven/plugins/MavenPublishPluginTest.groovy    |   19 +-
 .../maven/AbstractMavenPublishIntegTest.groovy     |   95 +
 .../gradle/internal/event/BroadcastDispatch.java   |  117 +
 .../internal/event/DefaultListenerManager.java     |  162 +
 .../gradle/internal/event/ListenerBroadcast.java   |  140 +
 .../org/gradle/internal/event/ListenerManager.java |   88 +
 .../event/ListenerNotificationException.java       |   29 +
 .../serialize/AbstractCollectionSerializer.java    |   40 +
 .../gradle/internal/serialize/AbstractDecoder.java |  105 +
 .../gradle/internal/serialize/AbstractEncoder.java |   80 +
 .../internal/serialize/BaseSerializerFactory.java  |  140 +
 .../org/gradle/internal/serialize/Decoder.java     |  118 +
 .../internal/serialize/DefaultSerializer.java      |   56 +
 .../serialize/DefaultSerializerRegistry.java       |   85 +
 .../org/gradle/internal/serialize/Encoder.java     |   91 +
 .../internal/serialize/FlushableEncoder.java       |   24 +
 .../serialize/InputStreamBackedDecoder.java        |   65 +
 .../gradle/internal/serialize/ListSerializer.java  |   37 +
 .../gradle/internal/serialize/LongSerializer.java  |   29 +
 .../gradle/internal/serialize/MapSerializer.java   |   74 +
 .../serialize/NullSafeStringSerializer.java        |   26 +
 .../gradle/internal/serialize/ObjectReader.java    |   21 +
 .../gradle/internal/serialize/ObjectWriter.java    |   21 +
 .../serialize/OutputStreamBackedEncoder.java       |   65 +
 .../org/gradle/internal/serialize/Serializer.java  |   29 +
 .../internal/serialize/SerializerRegistry.java     |   23 +
 .../gradle/internal/serialize/SetSerializer.java   |   46 +
 .../internal/serialize/kryo/JavaSerializer.java    |   65 +
 .../internal/serialize/kryo/KryoBackedDecoder.java |  156 +
 .../internal/serialize/kryo/KryoBackedEncoder.java |   91 +
 .../serialize/kryo/StatefulSerializer.java         |   28 +
 .../serialize/kryo/TypeSafeSerializer.java         |   50 +
 .../dispatch/ContextClassLoaderProxy.java          |   37 +
 .../messaging/dispatch/MethodInvocation.java       |    4 +-
 .../org/gradle/messaging/dispatch/Receive.java     |    3 +
 .../messaging/remote/ObjectConnectionBuilder.java  |    2 +-
 .../remote/internal/ConnectCompletion.java         |    4 +-
 .../remote/internal/DefaultMessageSerializer.java  |    4 +-
 .../remote/internal/EagerReceiveBuffer.java        |    2 +-
 .../remote/internal/MessageSerializer.java         |    4 +-
 .../remote/internal/MessagingServices.java         |    4 +-
 .../remote/internal/RemoteConnection.java          |   40 +
 .../internal/hub/InterHubMessageSerializer.java    |   14 +-
 .../messaging/remote/internal/hub/MessageHub.java  |    9 +-
 .../hub/MessageHubBackedObjectConnection.java      |    6 +-
 .../internal/hub/MethodInvocationSerializer.java   |   10 +-
 .../remote/internal/inet/InetAddressFactory.java   |  194 +-
 .../remote/internal/inet/MulticastConnection.java  |   35 +-
 .../internal/inet/SocketConnectCompletion.java     |    9 +-
 .../remote/internal/inet/SocketConnection.java     |   25 +-
 .../remote/internal/inet/TcpIncomingConnector.java |    2 +-
 .../protocol/DiscoveryProtocolSerializer.java      |    4 +-
 .../serialize/AbstractCollectionSerializer.java    |   40 -
 .../messaging/serialize/AbstractDecoder.java       |  105 -
 .../messaging/serialize/AbstractEncoder.java       |   80 -
 .../messaging/serialize/BaseSerializerFactory.java |   70 -
 .../org/gradle/messaging/serialize/Decoder.java    |  118 -
 .../messaging/serialize/DefaultSerializer.java     |   56 -
 .../serialize/DefaultSerializerRegistry.java       |   85 -
 .../org/gradle/messaging/serialize/Encoder.java    |   91 -
 .../messaging/serialize/FlushableEncoder.java      |   24 -
 .../serialize/InputStreamBackedDecoder.java        |   65 -
 .../gradle/messaging/serialize/ListSerializer.java |   37 -
 .../gradle/messaging/serialize/LongSerializer.java |   29 -
 .../gradle/messaging/serialize/MapSerializer.java  |   48 -
 .../serialize/NullSafeStringSerializer.java        |   26 -
 .../gradle/messaging/serialize/ObjectReader.java   |   21 -
 .../gradle/messaging/serialize/ObjectWriter.java   |   21 -
 .../serialize/OutputStreamBackedEncoder.java       |   65 -
 .../org/gradle/messaging/serialize/Serializer.java |   29 -
 .../messaging/serialize/SerializerRegistry.java    |   23 -
 .../gradle/messaging/serialize/SetSerializer.java  |   38 -
 .../messaging/serialize/kryo/JavaSerializer.java   |   65 -
 .../serialize/kryo/KryoBackedDecoder.java          |  156 -
 .../serialize/kryo/KryoBackedEncoder.java          |   91 -
 .../serialize/kryo/StatefulSerializer.java         |   28 -
 .../serialize/kryo/TypeSafeSerializer.java         |   50 -
 .../internal/event/DefaultListenerManagerTest.java |  226 +
 .../internal/event/ListenerBroadcastTest.java      |  268 +
 .../internal/serialize/AbstractCodecTest.groovy    |  522 ++
 .../serialize/BaseSerializerFactoryTest.groovy     |  125 +
 .../serialize/DefaultSerializerRegistryTest.groovy |   92 +
 .../serialize/DefaultSerializerTest.groovy         |   32 +
 .../internal/serialize/ListSerializerTest.groovy   |   45 +
 .../internal/serialize/LongSerializerTest.groovy   |   34 +
 .../internal/serialize/MapSerializerTest.groovy    |   80 +
 .../internal/serialize/SetSerializerTest.groovy    |   54 +
 .../serialize/StreamBackedCodecTest.groovy         |   31 +
 .../serialize/kryo/KryoBackedCodecTest.groovy      |   98 +
 .../messaging/dispatch/MethodInvocationTest.groovy |   45 +
 .../messaging/dispatch/MethodInvocationTest.java   |   35 -
 .../messaging/remote/internal/MessageTest.groovy   |    6 +-
 .../remote/internal/ProtocolStackTest.groovy       |    6 +-
 .../hub/InterHubMessageSerializerTest.groovy       |    2 +-
 .../internal/hub/MessageHubBackedClientTest.groovy |    4 +-
 .../internal/hub/MessageHubBackedServerTest.groovy |    4 +-
 .../hub/MethodInvocationSerializerTest.groovy      |    6 +-
 .../internal/inet/InetAddressFactoryTest.groovy    |    8 +
 .../internal/inet/MulticastConnectionTest.groovy   |   87 +
 .../messaging/serialize/AbstractCodecTest.groovy   |  524 --
 .../serialize/BaseSerializerFactoryTest.groovy     |   73 -
 .../serialize/DefaultSerializerRegistryTest.groovy |   92 -
 .../serialize/DefaultSerializerTest.groovy         |   32 -
 .../messaging/serialize/ListSerializerTest.groovy  |   45 -
 .../messaging/serialize/LongSerializerTest.groovy  |   34 -
 .../messaging/serialize/MapSerializerTest.groovy   |   47 -
 .../messaging/serialize/SetSerializerTest.groovy   |   47 -
 .../serialize/StreamBackedCodecTest.groovy         |   31 -
 .../serialize/kryo/KryoBackedCodecTest.groovy      |   98 -
 .../internal/serialize/SerializerSpec.groovy       |   41 +
 .../messaging/serialize/SerializerSpec.groovy      |   39 -
 subprojects/model-core/model-core.gradle           |   43 +
 .../model/ConfigurationCycleIntegrationTest.groovy |   73 +
 .../gradle/model/ModelReuseIntegrationTest.groovy  |  205 +
 .../ModelRuleBindingFailureIntegrationTest.groovy  |  242 +
 ...odelRuleBindingValidationIntegrationTest.groovy |   72 +
 .../model/ModelRuleCachingIntegrationTest.groovy   |   59 +
 .../model/ModelRuleSamplesIntegrationTest.groovy   |   37 +
 .../ModelRuleValidationIntegrationTest.groovy      |   76 +
 ...ationRuleApplicationOrderIntegrationTest.groovy |  153 +
 .../model/PluginRuleSourceIntegrationTest.groovy   |  434 ++
 .../model/ScopedRuleSourceIntegrationTest.groovy   |  166 +
 .../model/TaskCreationIntegrationTest.groovy       |  675 +++
 ...actClassBackedManagedTypeIntegrationTest.groovy |  308 ++
 .../ComplexManagedTypeIntegrationTest.groovy       |  228 +
 .../CyclicalManagedTypeIntegrationTest.groovy      |  121 +
 .../EnumsInManagedModelIntegrationTest.groovy      |   75 +
 ...nterfaceBackedManagedTypeIntegrationTest.groovy |  346 ++
 ...validManagedModelMutationIntegrationTest.groovy |  272 ++
 .../InvalidManagedModelRuleIntegrationTest.groovy  |  191 +
 ...odelPropertyTargetingRuleIntegrationTest.groovy |  267 +
 .../model/managed/ManagedSetIntegrationTest.groovy |  582 +++
 ...anagedTypeImplementationClassCachingSpec.groovy |   63 +
 ...peWithUnmanagedPropertiesIntegrationTest.groovy |  128 +
 .../PolymorphicManagedTypeIntegrationTest.groovy   |  251 +
 .../PrimitivesInManagedModelIntegrationTest.groovy |  207 +
 .../gradle/model/ConfigurationCycleException.java  |   30 +
 .../src/main/java/org/gradle/model/Defaults.java   |   38 +
 .../src/main/java/org/gradle/model/Finalize.java   |   38 +
 .../InvalidModelRuleDeclarationException.java      |   54 +
 .../gradle/model/InvalidModelRuleException.java    |   54 +
 .../src/main/java/org/gradle/model/Managed.java    |  100 +
 .../src/main/java/org/gradle/model/Model.java      |   73 +
 .../gradle/model/ModelRuleBindingException.java    |   34 +
 .../org/gradle/model/ModelViewClosedException.java |   39 +
 .../src/main/java/org/gradle/model/Mutate.java     |   38 +
 .../src/main/java}/org/gradle/model/Path.java      |    0
 .../src/main/java/org/gradle/model/RuleSource.java |   78 +
 .../src/main/java/org/gradle/model/Unmanaged.java  |   37 +
 .../src/main/java/org/gradle/model/Validate.java   |   38 +
 .../gradle/model/WriteOnlyModelViewException.java  |   35 +
 .../gradle/model/collection/CollectionBuilder.java |  229 +
 .../org/gradle/model/collection/ManagedSet.java    |   62 +
 .../org/gradle/model/collection/package-info.java  |   20 +
 .../internal/core/ActionBackedModelAction.java     |   59 +
 .../internal/core/BiActionBackedModelAction.java   |   70 +
 .../internal/core/ChainingModelProjection.java     |  117 +
 .../internal/core/CollectionBuilderModelView.java  |  283 ++
 .../internal/core/DefaultCollectionBuilder.java    |  314 ++
 .../internal/core/DelegatingCollectionBuilder.java |  157 +
 .../core/DependencyOnlyExtractedModelRule.java     |   52 +
 .../model/internal/core/DirectNodeModelAction.java |   70 +
 .../internal/core/DuplicateModelException.java     |   28 +
 .../model/internal/core/EmptyModelProjection.java  |   64 +
 .../model/internal/core/ExtractedModelAction.java  |   63 +
 .../model/internal/core/ExtractedModelCreator.java |   55 +
 .../model/internal/core/ExtractedModelRule.java    |   42 +
 .../model/internal/core/InstanceModelView.java     |   55 +
 .../gradle/model/internal/core/ModelAction.java    |   33 +
 .../model/internal/core/ModelActionRole.java       |   30 +
 .../gradle/model/internal/core/ModelAdapter.java   |   36 +
 .../gradle/model/internal/core/ModelCreator.java   |   37 +
 .../model/internal/core/ModelCreatorFactory.java   |   41 +
 .../gradle/model/internal/core/ModelCreators.java  |  112 +
 .../org/gradle/model/internal/core/ModelNode.java  |   74 +
 .../org/gradle/model/internal/core/ModelPath.java  |  257 +
 .../model/internal/core/ModelProjection.java       |   20 +
 .../gradle/model/internal/core/ModelPromise.java   |   37 +
 .../gradle/model/internal/core/ModelReference.java |  148 +
 .../gradle/model/internal/core/ModelRegistrar.java |   29 +
 .../org/gradle/model/internal/core/ModelRule.java  |   23 +
 .../internal/core/ModelRuleExecutionException.java |   49 +
 .../org/gradle/model/internal/core/ModelView.java  |   31 +
 .../org/gradle/model/internal/core/ModelViews.java |   49 +
 .../model/internal/core/MutableModelNode.java      |  102 +
 .../internal/core/NamedEntityInstantiator.java     |   21 +
 .../core/ProjectionBackedModelCreator.java         |   83 +
 .../TypeCompatibilityModelProjectionSupport.java   |  110 +
 .../internal/core/UnmanagedModelProjection.java    |   40 +
 .../rule/describe/AbstractModelRuleDescriptor.java |   31 +
 .../rule/describe/MethodModelRuleDescriptor.java   |  107 +
 .../core/rule/describe/ModelRuleDescriptor.java    |   30 +
 .../rule/describe/NestedModelRuleDescriptor.java   |   52 +
 .../rule/describe/SimpleModelRuleDescriptor.java   |   41 +
 ...AbstractAnnotationDrivenModelRuleExtractor.java |   47 +
 .../AbstractModelCreationRuleExtractor.java        |   39 +
 .../AbstractMutationModelRuleExtractor.java        |   43 +
 .../inspect/DefaultMethodRuleDefinition.java       |  128 +
 .../inspect/DefaultModelCreatorFactory.java        |   82 +
 .../internal/inspect/DefaultModelRuleInvoker.java  |   56 +
 .../inspect/DefaultsModelRuleExtractor.java        |   29 +
 .../inspect/FinalizeModelRuleExtractor.java        |   29 +
 .../inspect/ManagedModelCreationRuleExtractor.java |   94 +
 .../internal/inspect/ManagedModelInitializer.java  |   77 +
 .../internal/inspect/ManagedSetInitializer.java    |   40 +
 .../internal/inspect/MethodBackedModelAction.java  |   65 +
 .../internal/inspect/MethodModelRuleExtractor.java |   30 +
 .../inspect/MethodModelRuleExtractors.java         |   39 +
 .../internal/inspect/MethodRuleDefinition.java     |   45 +
 .../model/internal/inspect/ModelRuleExtractor.java |  229 +
 .../model/internal/inspect/ModelRuleInvoker.java   |   21 +
 .../internal/inspect/ModelRuleSourceDetector.java  |  105 +
 .../internal/inspect/MutateModelRuleExtractor.java |   29 +
 .../inspect/RuleMethodBackedMutationAction.java    |   39 +
 .../UnmanagedModelCreationRuleExtractor.java       |   92 +
 .../inspect/ValidateModelRuleExtractor.java        |   29 +
 .../internal/manage/instance/ManagedInstance.java  |   29 +
 .../manage/instance/ManagedProxyFactory.java       |   42 +
 .../manage/instance/ModelElementState.java         |   29 +
 .../manage/projection/ManagedModelProjection.java  |  172 +
 .../projection/ManagedSetModelProjection.java      |  244 +
 .../manage/schema/ModelCollectionSchema.java       |   32 +
 .../internal/manage/schema/ModelProperty.java      |   88 +
 .../model/internal/manage/schema/ModelSchema.java  |   84 +
 .../internal/manage/schema/ModelSchemaStore.java   |   29 +
 .../internal/manage/schema/ModelStructSchema.java  |   45 +
 .../manage/schema/cache/ModelSchemaCache.java      |  109 +
 .../manage/schema/cache/MultiWeakClassSet.java     |   81 +
 .../manage/schema/cache/SingleWeakClassSet.java    |   59 +
 .../internal/manage/schema/cache/WeakClassSet.java |   36 +
 .../schema/extract/DefaultModelSchemaStore.java    |   53 +
 .../manage/schema/extract/EnumStrategy.java        |   40 +
 .../InvalidManagedModelElementTypeException.java   |   79 +
 .../schema/extract/JdkValueTypeStrategy.java       |   73 +
 .../schema/extract/ManagedProxyClassGenerator.java |  403 ++
 .../manage/schema/extract/ManagedSetStrategy.java  |   85 +
 .../extract/ModelSchemaExtractionContext.java      |   86 +
 .../extract/ModelSchemaExtractionResult.java       |   47 +
 .../extract/ModelSchemaExtractionStrategy.java     |   29 +
 .../schema/extract/ModelSchemaExtractor.java       |  117 +
 .../manage/schema/extract/PrimitiveStrategy.java   |   55 +
 .../manage/schema/extract/StructStrategy.java      |  417 ++
 .../UnmanagedModelElementTypeException.java        |   23 +
 .../manage/schema/extract/UnmanagedStrategy.java   |   32 +
 .../method/WeaklyTypeReferencingMethod.java        |  165 +
 .../internal/registry/BinderCreationListener.java  |   42 +
 .../model/internal/registry/CreatorRuleBinder.java |   35 +
 .../internal/registry/DefaultModelRegistry.java    | 1017 ++++
 .../model/internal/registry/ModelBinding.java      |   71 +
 .../internal/registry/ModelCreationListener.java   |   64 +
 .../gradle/model/internal/registry/ModelGraph.java |  186 +
 .../model/internal/registry/ModelNodeInternal.java |  239 +
 .../registry/ModelPathSuggestionProvider.java      |   90 +
 .../model/internal/registry/ModelRegistry.java     |  141 +
 .../internal/registry/ModelRegistryScope.java      |   23 +
 .../model/internal/registry/MutatorRuleBinder.java |   67 +
 .../registry/OneOfTypeBinderCreationListener.java  |   72 +
 .../registry/PathBinderCreationListener.java       |   57 +
 .../gradle/model/internal/registry/RuleBinder.java |  106 +
 .../registry/UnboundModelRulesException.java       |   47 +
 .../internal/registry/UnboundRulesProcessor.java   |   89 +
 .../internal/report/AmbiguousBindingReporter.java  |  108 +
 .../report/IncompatibleTypeReferenceReporter.java  |   92 +
 .../model/internal/report/unbound/UnboundRule.java |   85 +
 .../internal/report/unbound/UnboundRuleInput.java  |  134 +
 .../report/unbound/UnboundRulesReporter.java       |   90 +
 .../model/internal/type/ClassTypeWrapper.java      |   37 +
 .../org/gradle/model/internal/type/ModelType.java  |  381 ++
 .../org/gradle/model/internal/type/ModelTypes.java |   29 +
 .../model/internal/type/NullTypeWrapper.java       |   33 +
 .../internal/type/ParameterizedTypeWrapper.java    |  135 +
 .../gradle/model/internal/type/TypeWrapper.java    |   25 +
 .../model/internal/type/WildcardTypeWrapper.java   |  105 +
 .../main/java/org/gradle/model/package-info.java   |   20 +
 .../internal/CollectionBuilderModelViewTest.groovy |   51 +
 .../internal/DefaultCollectionBuilderTest.groovy   |  842 ++++
 .../collection/internal/HasDependencies.groovy     |   27 +
 .../gradle/model/collection/internal/Special.java  |   23 +
 .../core/InstanceBackedModelCreatorTest.groovy     |   56 +
 .../model/internal/core/ModelPathTest.groovy       |  119 +
 .../internal/core/ModelPathValidationTest.groovy   |  123 +
 .../model/internal/core/ModelTypeJavaTest.java     |   43 +
 .../model/internal/core/ModelTypeTest.groovy       |  122 +
 .../describe/MethodModelRuleDescriptorTest.groovy  |   46 +
 ...herManagedWithPropertyOfInvalidManagedType.java |   24 +
 .../ClassModelRuleSourceValidationTest.groovy      |   66 +
 .../gradle/model/internal/inspect/HasStrings.java  |   23 +
 .../internal/inspect/ManagedAnnotatedClass.java    |   23 +
 ...agedWithNestedPropertyOfInvalidManagedType.java |   24 +
 ...gedWithNestedReferenceOfInvalidManagedType.java |   25 +
 .../inspect/ManagedWithNonManageableParents.java   |   23 +
 .../ManagedWithPropertyOfInvalidManagedType.java   |   24 +
 .../ManagedWithReferenceOfInvalidManagedType.java  |   25 +
 .../internal/inspect/ModelRuleBindingTest.groovy   |  186 +
 .../internal/inspect/ModelRuleExtractorTest.groovy |  532 ++
 .../inspect/ModelRuleSourceDetectorTest.groovy     |  114 +
 .../inspect/MutationRuleExecutionOrderTest.groovy  |  126 +
 .../gradle/model/internal/inspect/NonManaged.java  |   20 +
 .../gradle/model/internal/inspect/OuterClass.java  |   72 +
 .../internal/inspect/ParametrizedManaged.java      |   23 +
 .../manage/instance/ManagedProxyTest.groovy        |   45 +
 .../ManagedSetModelProjectionTest.groovy           |  170 +
 .../extract/DefaultModelSchemaStoreTest.groovy     |  144 +
 .../extract/ManagedProxyClassGeneratorTest.groovy  |  136 +
 .../schema/extract/ModelSchemaExtractorTest.groovy |  814 ++++
 .../manage/schema/extract/SimpleManagedType.java   |   28 +
 .../manage/schema/extract/SpecialManagedSet.java   |   21 +
 .../registry/DefaultModelRegistryTest.groovy       |  826 ++++
 .../model/internal/registry/ModelGraphTest.groovy  |  362 ++
 .../registry/ModelNodeReplacementTest.groovy       |   57 +
 .../ModelPathSuggestionProviderTest.groovy         |   55 +
 .../registry/ModelRegistryEphemeralNodeTest.groovy |  147 +
 .../model/internal/registry/ScopedRuleTest.groovy  |  209 +
 .../registry/UnboundRulesProcessorTest.groovy      |  396 ++
 .../report/unbound/UnboundRulesReporterTest.groovy |   50 +
 .../internal/fixture/ModelRegistryHelper.java      |  568 +++
 .../unbound/UnboundRulesReportMatchers.groovy      |   38 +
 subprojects/model-groovy/model-groovy.gradle       |   33 +
 .../dsl/ModelDslCreationIntegrationTest.groovy     |  121 +
 .../model/dsl/ModelDslIntegrationTest.groovy       |  234 +
 .../ModelDslRuleDetectionIntegrationSpec.groovy    |  151 +
 ...odelDslRuleInputDetectionIntegrationSpec.groovy |  251 +
 .../NestedModelDslUsageIntegrationSpec.groovy      |  178 +
 .../internal/NonTransformedModelDslBacking.java    |  130 +
 .../dsl/internal/TransformedModelDslBacking.java   |  137 +
 .../model/dsl/internal/inputs/RuleInputAccess.java |   23 +
 .../internal/inputs/RuleInputAccessBacking.java    |   58 +
 .../ClosureCreationInterceptingVerifier.java       |   39 +
 .../internal/transform/ModelBlockTransformer.java  |  101 +
 .../model/dsl/internal/transform/RuleMetadata.java |   32 +
 .../model/dsl/internal/transform/RuleVisitor.java  |  168 +
 .../model/dsl/internal/transform/RulesBlock.java   |   30 +
 .../model/dsl/internal/transform/RulesVisitor.java |  188 +
 .../dsl/internal/transform/SourceLocation.java     |   55 +
 .../NonTransformedModelDslBackingTest.groovy       |  178 +
 .../internal/TransformedModelDslBackingTest.groovy |  125 +
 subprojects/native/native.gradle                   |    3 -
 .../NativeIntegrationException.java                |   26 +
 .../NativeIntegrationUnavailableException.java     |   25 +
 .../nativeintegration/ProcessEnvironment.java      |  103 +
 .../nativeintegration/ReflectiveEnvironment.java   |   76 +
 .../nativeintegration/console/ConsoleDetector.java |   29 +
 .../nativeintegration/console/ConsoleMetaData.java |   36 +
 .../console/FallbackConsoleMetaData.java           |   31 +
 .../console/NativePlatformConsoleDetector.java     |   48 +
 .../console/NativePlatformConsoleMetaData.java     |   43 +
 .../console/NoOpConsoleDetector.java               |   23 +
 .../console/UnixConsoleMetaData.java               |   51 +
 .../console/WindowsConsoleDetector.java            |   35 +
 .../nativeintegration/filesystem/Chmod.java        |   31 +
 .../filesystem/FileCanonicalizer.java              |   23 +
 .../filesystem/FileException.java                  |   23 +
 .../filesystem/FileModeAccessor.java               |   23 +
 .../filesystem/FileModeMutator.java                |   23 +
 .../nativeintegration/filesystem/FileSystem.java   |   57 +
 .../nativeintegration/filesystem/Stat.java         |   23 +
 .../nativeintegration/filesystem/Symlink.java      |   25 +
 .../filesystem/jdk7/Jdk7FileCanonicalizer.java     |   33 +
 .../jdk7/PosixFilePermissionConverter.java         |   96 +
 .../jdk7/PosixJdk7FilePermissionHandler.java       |   42 +
 .../filesystem/services/EmptyChmod.java            |   30 +
 .../services/FallbackFileCanonicalizer.java        |   33 +
 .../filesystem/services/FallbackStat.java          |   36 +
 .../filesystem/services/FileSystemServices.java    |   84 +
 .../filesystem/services/GenericFileSystem.java     |  126 +
 .../services/NativePlatformBackedChmod.java        |   34 +
 .../services/NativePlatformBackedStat.java         |   35 +
 .../services/NativePlatformBackedSymlink.java      |   40 +
 .../filesystem/services/UnavailablePosixFiles.java |   22 +
 .../services/UnsupportedFilePermissions.java       |   49 +
 .../filesystem/services/UnsupportedSymlink.java    |   32 +
 .../filesystem/services/WindowsSymlink.java        |   32 +
 .../jna/JnaBootPathConfigurer.java                 |   66 +
 .../jna/UnsupportedEnvironment.java                |   70 +
 .../AbstractProcessEnvironment.java                |   92 +
 .../NativePlatformBackedProcessEnvironment.java    |   51 +
 .../nativeintegration/services/FileSystems.java    |   24 +
 .../nativeintegration/services/NativeServices.java |  209 +
 .../nativeplatform/NativeIntegrationException.java |   26 -
 .../NativeIntegrationUnavailableException.java     |   25 -
 .../nativeplatform/ProcessEnvironment.java         |  103 -
 .../nativeplatform/ReflectiveEnvironment.java      |   76 -
 .../nativeplatform/console/ConsoleDetector.java    |   29 -
 .../nativeplatform/console/ConsoleMetaData.java    |   36 -
 .../console/FallbackConsoleMetaData.java           |   31 -
 .../console/NativePlatformConsoleDetector.java     |   48 -
 .../console/NativePlatformConsoleMetaData.java     |   43 -
 .../console/NoOpConsoleDetector.java               |   23 -
 .../console/UnixConsoleMetaData.java               |   51 -
 .../console/WindowsConsoleDetector.java            |   35 -
 .../internal/nativeplatform/filesystem/Chmod.java  |   33 -
 .../filesystem/DefaultFilePathEncoder.java         |   42 -
 .../nativeplatform/filesystem/EmptyChmod.java      |   25 -
 .../nativeplatform/filesystem/FallbackStat.java    |   30 -
 .../nativeplatform/filesystem/FallbackSymlink.java |   26 -
 .../nativeplatform/filesystem/FilePathEncoder.java |   23 -
 .../nativeplatform/filesystem/FileSystem.java      |   62 +-
 .../filesystem/FileSystemServices.java             |  116 -
 .../filesystem/GenericFileSystem.java              |  144 -
 .../nativeplatform/filesystem/LibCStat.java        |   55 -
 .../nativeplatform/filesystem/LibcChmod.java       |   42 -
 .../nativeplatform/filesystem/LibcSymlink.java     |   40 -
 .../filesystem/NativePlatformBackedChmod.java      |   34 -
 .../filesystem/NativePlatformBackedStat.java       |   34 -
 .../filesystem/NativePlatformBackedSymlink.java    |   35 -
 .../nativeplatform/filesystem/PosixStat.java       |   34 -
 .../nativeplatform/filesystem/PosixUtil.java       |   75 -
 .../internal/nativeplatform/filesystem/Stat.java   |   24 -
 .../nativeplatform/filesystem/Symlink.java         |   24 -
 .../jdk7/PosixFilePermissionConverter.java         |   96 -
 .../jdk7/PosixJdk7FilePermissionHandler.java       |   47 -
 .../nativeplatform/jna/JnaBootPathConfigurer.java  |   66 -
 .../gradle/internal/nativeplatform/jna/LibC.java   |   36 -
 .../jna/LibCBackedConsoleDetector.java             |   64 -
 .../jna/LibCBackedProcessEnvironment.java          |   69 -
 .../nativeplatform/jna/UnsupportedEnvironment.java |   70 -
 .../AbstractProcessEnvironment.java                |   92 -
 .../NativePlatformBackedProcessEnvironment.java    |   51 -
 .../nativeplatform/services/FileSystems.java       |   24 -
 .../nativeplatform/services/NativeServices.java    |  197 -
 .../NativePlatformConsoleDetectorTest.groovy       |   67 +
 .../filesystem/CommonFileSystemTest.groovy         |  119 +
 .../filesystem/LinuxFileSystemTest.groovy          |   36 +
 .../filesystem/MacOsFileSystemTest.groovy          |   36 +
 .../filesystem/WindowsFileSystemTest.groovy        |   37 +
 .../jdk7/PosixFilePermissionConverterTest.groovy   |   59 +
 .../jdk7/PosixJdk7FilePermissionHandlerTest.groovy |   41 +
 .../services/GenericFileSystemTest.groovy          |   75 +
 .../services/UnsupportedFilePermissionsTest.groovy |   57 +
 .../ProcessEnvironmentTest.groovy                  |   79 +
 .../NativeServicesInitializationTest.groovy        |   47 +
 .../services/NativeServicesTest.groovy             |   80 +
 .../NativePlatformConsoleDetectorTest.groovy       |   67 -
 .../filesystem/CommonFileSystemTest.groovy         |  113 -
 .../nativeplatform/filesystem/LibcStatTest.groovy  |   51 -
 .../filesystem/LinuxFileSystemTest.groovy          |   36 -
 .../filesystem/MacOsFileSystemTest.groovy          |   36 -
 .../filesystem/WindowsFileSystemTest.groovy        |   37 -
 .../jdk7/PosixFilePermissionConverterTest.groovy   |   59 -
 .../jdk7/PosixJdk7FilePermissionHandlerTest.groovy |   41 -
 .../jna/LibCBackedProcessEnvironmentTest.groovy    |   39 -
 .../ProcessEnvironmentTest.groovy                  |   79 -
 .../services/NativeServicesTest.groovy             |   68 -
 subprojects/open-api/open-api.gradle               |    3 +-
 .../openapi/BlockingRequestObserver.java           |  136 -
 .../integtests/openapi/CrossVersionBuilder.java    |   99 -
 ...CrossVersionCompatibilityIntegrationTest.groovy |   89 +-
 .../ExtraTestCommandLineOptionsListener.java       |   32 -
 .../integtests/openapi/GradleRunnerTest.groovy     |  278 --
 .../gradle/integtests/openapi/OpenApiFixture.java  |  154 -
 .../gradle/integtests/openapi/OpenApiUiTest.groovy |  971 ----
 .../integtests/openapi/OutputUILordTest.groovy     |  127 -
 .../TestAlternateUIInteractionVersion1.java        |   52 -
 .../openapi/TestSettingsNodeVersion1.java          |  245 -
 .../TestSingleDualPaneUIInteractionVersion1.java   |   44 -
 .../shared/build.gradle                            |    3 -
 .../shared/settings.gradle                         |    1 -
 .../integtests/openapi/testproject/build.gradle    |    1 -
 .../integtests/openapi/testproject/settings.gradle |    1 -
 .../org/gradle/foundation/BootstrapLoader.java     |  187 -
 .../gradle/foundation/ParentLastClassLoader.java   |   72 -
 .../gradle/openapi/external/ExternalUtility.java   |  166 -
 .../external/runner/GradleRunnerFactory.java       |  133 -
 .../external/runner/GradleRunnerVersion1.java      |   35 -
 .../openapi/external/ui/DualPaneUIVersion1.java    |   59 -
 .../org/gradle/openapi/external/ui/UIFactory.java  |  238 -
 .../openapi/external/ExternalUtilityTest.groovy    |   70 -
 .../plugins/osgi/OsgiPluginIntegrationSpec.groovy  |    7 +-
 .../org/gradle/api/plugins/osgi/OsgiPlugin.groovy  |    3 +-
 ...{osgi.properties => org.gradle.osgi.properties} |    0
 .../plugins/osgi/OsgiPluginConventionTest.groovy   |    2 +-
 .../gradle/api/plugins/osgi/OsgiPluginTest.groovy  |    1 -
 subprojects/performance/performance.gradle         |  256 +-
 .../performance/src/configPlugin/ConfigPlugin.java |    4 +-
 subprojects/performance/src/generator.groovy       |   70 +-
 .../performance/CleanBuildPerformanceTest.groovy   |    5 +-
 .../ConfigurationPerformanceTest.groovy            |    5 +-
 .../performance/DaemonPerformanceTest.groovy       |   48 +
 .../DependencyReportPerformanceTest.groovy         |    5 +-
 .../DependencyResolutionStressTest.groovy          |    2 +
 .../performance/FirstBuildPerformanceTest.groovy   |    5 +-
 .../IdeIntegrationPerformanceTest.groovy           |   19 +-
 .../ManyEmptyProjectsHelpPerformanceTest.groovy    |   40 +
 .../NativeParallelPerformanceTest.groovy           |   44 +
 .../performance/NativePerformanceTest.groovy       |   62 +
 .../NativeScenarioPerformanceTest.groovy           |   77 +
 .../OldJavaPluginBigProjectPerformanceTest.groovy  |   50 +
 .../OldVsNewJavaPluginPerformanceTest.groovy       |   85 +
 .../ParallelBuildPerformanceTest.groovy            |   40 +
 .../ProjectDependenciesPerformanceTest.groovy      |   38 +
 .../TestExecutionPerformanceTest.groovy            |    5 +-
 .../UpToDateBuildPerformanceTest.groovy            |    5 +-
 .../performance/VariantsPerformanceTest.groovy     |  130 +
 .../templates/build-event-timestamps/build.gradle  |   20 +
 .../src/templates/config-inject/build.gradle       |    8 +-
 .../src/templates/heap-capture/build.gradle        |    8 +-
 subprojects/performance/src/templates/init.gradle  |    5 +
 .../src/templates/java-source/Production.java      |   24 +
 .../src/templates/native-component/build.gradle    |    9 +
 .../src/templates/native-scenario/build.gradle     |   70 +
 .../performance/src/templates/native-source/lib.c  |    8 +
 .../src/templates/new-java-plugin/build.gradle     |  112 +
 .../src/templates/old-java-plugin/build.gradle     |   76 +
 .../src/templates/project-with-source/build.gradle |   25 +-
 .../src/templates/project-with-source/pom.xml      |   12 +-
 .../performance/src/templates/root-project/pom.xml |    1 +
 .../performance/src/templates/settings.gradle      |    8 +-
 .../src/templates/variants-new-model/build.gradle  |  194 +
 .../src/templates/variants-old-model/build.gradle  |  167 +
 .../src/templates/with-testng/build.gradle         |    5 +-
 .../src/templates/with-verbose-testng/build.gradle |    5 +-
 .../gradle/performance/ResultSpecification.groovy  |   28 +-
 .../BuildEventTimestampCollectorTest.groovy        |   97 +
 .../CrossVersionPerformanceResultsTest.groovy      |  287 ++
 .../CrossVersionPerformanceTestRunnerTest.groovy   |  110 +
 .../performance/fixture/GCEventParserTest.groovy   |   64 +
 .../fixture/GCLoggingCollectorTest.groovy          |   29 +-
 .../fixture/PerformanceResultsTest.groovy          |  287 --
 .../fixture/PerformanceTestRunnerTest.groovy       |  105 -
 .../performance/fixture/WaitingReaderTest.groovy   |   93 +
 .../performance/measure/DataSeriesTest.groovy      |    4 +-
 .../results/CompositeResultsStoreTest.groovy       |   46 +
 .../results/CrossBuildResultsStoreTest.groovy      |  155 +
 .../results/CrossVersionResultsStoreTest.groovy    |  234 +
 .../performance/results/ReportGeneratorTest.groovy |    4 +-
 .../performance/results/ResultsStoreTest.groovy    |  248 -
 .../org/gradle/performance/fixture/gc-1.txt        |   18 +-
 .../org/gradle/performance/fixture/gc-2.txt        |   14 +-
 .../org/gradle/performance/fixture/gc-3.txt        |   14 +-
 .../org/gradle/performance/fixture/gc-4.txt        |   19 +
 .../gradle/performance/fixture/mac-jdk8.0.25.txt   |   24 +
 .../org/gradle/performance/fixture/win-1.txt       |   20 +
 .../AbstractCrossBuildPerformanceTest.groovy       |   64 +
 .../AbstractCrossVersionPerformanceTest.groovy     |   56 +
 .../groovy/org/gradle/performance/Experiment.java  |   20 +
 .../org/gradle/performance/PerformanceTest.java    |   20 +
 .../fixture/AbstractPerformanceTest.groovy         |   52 -
 .../performance/fixture/BaselineVersion.groovy     |   10 +-
 .../performance/fixture/BuildDisplayInfo.groovy    |   39 +
 .../fixture/BuildEventTimestampCollector.java      |   95 +
 .../performance/fixture/BuildExperimentRunner.java |   84 +
 .../performance/fixture/BuildExperimentSpec.groovy |   90 +
 .../fixture/CompositeDataCollector.java            |   18 +-
 .../performance/fixture/CompositeDataReporter.java |   10 +-
 .../fixture/CrossBuildPerformanceResults.groovy    |   60 +
 .../fixture/CrossBuildPerformanceTestRunner.groovy |   97 +
 .../fixture/CrossVersionPerformanceResults.groovy  |  123 +
 .../CrossVersionPerformanceTestRunner.groovy       |  118 +
 .../gradle/performance/fixture/DataCollector.java  |    6 +-
 .../gradle/performance/fixture/DataReporter.groovy |    8 +-
 .../gradle/performance/fixture/GCEventParser.java  |   74 +
 .../performance/fixture/GCLoggingCollector.java    |  133 +-
 .../fixture/GradleExecuterBackedSession.groovy     |   78 +
 .../fixture/GradleInvocationSpec.groovy            |  136 +
 .../gradle/performance/fixture/GradleSession.java  |   29 +
 .../performance/fixture/GradleSessionProvider.java |   38 +
 .../fixture/MeasuredOperationList.groovy           |   10 +-
 .../performance/fixture/MemoryInfoCollector.groovy |   10 +-
 .../gradle/performance/fixture/OperationTimer.java |   15 +-
 .../performance/fixture/PerformanceResults.groovy  |  129 -
 .../performance/fixture/PerformanceTestResult.java |   74 +
 .../fixture/PerformanceTestRunner.groovy           |  138 -
 .../performance/fixture/PerformanceTestSpec.java   |   47 +
 .../fixture/TextFileDataReporter.groovy            |    4 +-
 .../fixture/ToolingApiBackedGradleSession.groovy   |   72 +
 .../gradle/performance/fixture/WaitingReader.java  |   73 +
 .../org/gradle/performance/measure/DataSeries.java |   22 +
 .../performance/measure/MeasuredOperation.groovy   |    6 +
 .../performance/results/AllResultsStore.java       |   48 +
 .../performance/results/CompositeResultsStore.java |   68 +
 .../performance/results/ConnectionAction.java      |   23 +
 .../results/CrossBuildResultsStore.java            |  264 +
 .../results/CrossBuildTestExecutionHistory.java    |  128 +
 .../results/CrossVersionResultsStore.java          |  261 +
 .../results/CrossVersionTestExecutionHistory.java  |  150 +
 .../org/gradle/performance/results/H2FileDb.java   |   61 +
 .../performance/results/HtmlPageGenerator.java     |   70 +-
 .../performance/results/IndexPageGenerator.java    |   59 +-
 .../performance/results/PerformanceResults.java    |   39 +
 .../performance/results/ReportGenerator.java       |    5 +-
 .../gradle/performance/results/ResultsStore.java   |  291 +-
 .../performance/results/TestDataGenerator.java     |   18 +-
 .../performance/results/TestExecutionHistory.java  |   64 +-
 .../performance/results/TestPageGenerator.java     |  139 +-
 subprojects/platform-base/platform-base.gradle     |   10 +
 .../base/AssembleTaskIntegrationTest.groovy        |  151 +
 ...oTestedSamplePlatformBaseIntegrationTest.groovy |   27 +
 .../base/ComponentTypeSampleIntegTest.groovy       |   59 +
 .../base/CustomBinaryIntegrationTest.groovy        |  302 ++
 .../base/CustomBinaryTasksIntegrationTest.groovy   |  278 ++
 .../CustomComponentBinariesIntegrationTest.groovy  |  244 +
 .../CustomComponentPluginIntegrationTest.groovy    |  318 ++
 .../base/LanguageTypeIntegrationTest.groovy        |  112 +
 .../base/LanguageTypeSampleIntegrationTest.groovy  |   67 +
 .../LanguageBasePluginIntegrationTest.groovy       |   21 +
 .../LifecycleBasePluginIntegrationTest.groovy      |   92 +
 .../api/internal/tasks/compile/Compiler.java       |   26 +
 .../gradle/language/base/FunctionalSourceSet.java  |   30 +
 .../gradle/language/base/LanguageSourceSet.java    |   49 +
 .../org/gradle/language/base/ProjectSourceSet.java |   25 +
 .../language/base/artifact/SourcesArtifact.java    |   28 +
 .../language/base/artifact/package-info.java       |   20 +
 .../base/internal/DefaultFunctionalSourceSet.java  |   66 +
 .../base/internal/DefaultProjectSourceSet.java     |   26 +
 .../base/internal/LanguageSourceSetContainer.java  |   51 +
 .../base/internal/LanguageSourceSetInternal.java   |   34 +
 .../base/internal/SourceSetNotationParser.java     |   70 +
 .../base/internal/SourceTransformTaskConfig.java   |   28 +
 .../base/internal/compile/CompileSpec.java         |   20 +
 .../language/base/internal/compile/Compiler.java   |   22 +
 .../base/internal/compile/CompilerFactory.java     |   21 +
 .../base/internal/compile/CompilerUtil.java        |   24 +
 .../language/base/internal/plugins/CleanRule.java  |   61 +
 .../internal/registry/DefaultLanguageRegistry.java |   32 +
 .../DefaultLanguageTransformContainer.java         |   32 +
 .../internal/registry/LanguageRegistration.java    |   38 +
 .../base/internal/registry/LanguageRegistry.java   |   25 +
 .../base/internal/registry/LanguageTransform.java  |   52 +
 .../registry/LanguageTransformContainer.java       |   25 +
 .../registry/RuleBasedLanguageRegistration.java    |   59 +
 .../internal/tasks/SimpleStaleClassCleaner.java    |   43 +
 .../base/internal/tasks/StaleClassCleaner.java     |   43 +
 .../org/gradle/language/base/package-info.java     |    0
 .../base/plugins/ComponentModelBasePlugin.java     |  215 +
 .../language/base/plugins/LanguageBasePlugin.java  |  195 +
 .../language/base/plugins/LifecycleBasePlugin.java |  116 +
 .../gradle/language/base/plugins/package-info.java |    0
 .../base/sources/BaseLanguageSourceSet.java        |  143 +
 .../gradle/language/base/sources/package-info.java |   24 +
 .../java/org/gradle/platform/base/Application.java |   27 +
 .../platform/base/ApplicationBinarySpec.java       |   30 +
 .../org/gradle/platform/base/ApplicationSpec.java  |   26 +
 .../main/java/org/gradle/platform/base/Binary.java |   30 +
 .../org/gradle/platform/base/BinaryContainer.java  |   25 +
 .../java/org/gradle/platform/base/BinarySpec.java  |   71 +
 .../java/org/gradle/platform/base/BinaryTasks.java |   68 +
 .../platform/base/BinaryTasksCollection.java       |   34 +
 .../java/org/gradle/platform/base/BinaryType.java  |   54 +
 .../gradle/platform/base/BinaryTypeBuilder.java    |   26 +
 .../gradle/platform/base/ComponentBinaries.java    |   60 +
 .../org/gradle/platform/base/ComponentSpec.java    |   58 +
 .../platform/base/ComponentSpecContainer.java      |   28 +
 .../platform/base/ComponentSpecIdentifier.java     |   28 +
 .../org/gradle/platform/base/ComponentType.java    |   59 +
 .../gradle/platform/base/ComponentTypeBuilder.java |   24 +
 .../platform/base/InvalidModelException.java       |   32 +
 .../org/gradle/platform/base/LanguageType.java     |   52 +
 .../gradle/platform/base/LanguageTypeBuilder.java  |   29 +
 .../java/org/gradle/platform/base/Library.java     |   27 +
 .../gradle/platform/base/LibraryBinarySpec.java    |   30 +
 .../java/org/gradle/platform/base/LibrarySpec.java |   27 +
 .../platform/base/ModelInstantiationException.java |   32 +
 .../java/org/gradle/platform/base/Platform.java    |   35 +
 .../platform/base/PlatformAwareComponentSpec.java  |   31 +
 .../gradle/platform/base/PlatformContainer.java    |   27 +
 .../java/org/gradle/platform/base/ToolChain.java   |   35 +
 .../gradle/platform/base/ToolChainRegistry.java    |   29 +
 .../platform/base/TransformationFileType.java      |   26 +
 .../java/org/gradle/platform/base/TypeBuilder.java |   35 +
 .../platform/base/binary/BaseBinarySpec.java       |  173 +
 .../gradle/platform/base/binary/package-info.java  |   23 +
 .../platform/base/component/BaseComponentSpec.java |  136 +
 .../platform/base/component/package-info.java      |   23 +
 .../platform/base/internal/BinaryBuildAbility.java |   24 +
 .../platform/base/internal/BinaryNamingScheme.java |   42 +
 .../base/internal/BinaryNamingSchemeBuilder.java   |   26 +
 .../platform/base/internal/BinarySpecInternal.java |   32 +
 .../internal/BinaryTasksCollectionWrapper.java     |  193 +
 .../base/internal/ComponentSpecInternal.java       |   30 +
 .../base/internal/DefaultBinaryContainer.java      |   27 +
 .../base/internal/DefaultBinaryNamingScheme.java   |  116 +
 .../internal/DefaultBinaryNamingSchemeBuilder.java |   64 +
 .../internal/DefaultBinaryTasksCollection.java     |   61 +
 .../internal/DefaultComponentSpecContainer.java    |   29 +
 .../internal/DefaultComponentSpecIdentifier.java   |   60 +
 .../base/internal/DefaultPlatformContainer.java    |   30 +
 .../base/internal/DefaultPlatformRequirement.java  |   53 +
 .../base/internal/DefaultPlatformResolvers.java    |   70 +
 .../platform/base/internal/FixedBuildAbility.java  |   39 +
 .../PlatformAwareComponentSpecInternal.java        |   35 +
 .../base/internal/PlatformRequirement.java         |   21 +
 .../platform/base/internal/PlatformResolver.java   |   24 +
 .../platform/base/internal/PlatformResolvers.java  |   24 +
 .../base/internal/ToolSearchBuildAbility.java      |   38 +
 .../builder/LanguageTypeBuilderInternal.java       |   24 +
 .../base/internal/builder/TypeBuilderInternal.java |   23 +
 ...nnotationDrivenComponentModelRuleExtractor.java |   99 +
 .../internal/registry/AbstractTypeBuilder.java     |   41 +
 .../registry/BinaryTasksModelRuleExtractor.java    |  113 +
 .../registry/BinaryTypeModelRuleExtractor.java     |  109 +
 .../registry/CollectionBuilderBasedRule.java       |   97 +
 .../ComponentBinariesModelRuleExtractor.java       |  105 +
 .../ComponentModelBaseServiceRegistry.java         |   76 +
 .../registry/ComponentTypeModelRuleExtractor.java  |  118 +
 .../registry/LanguageTypeModelRuleExtractor.java   |  118 +
 .../internal/registry/TypeModelRuleExtractor.java  |  121 +
 .../RuleAwarePolymorphicDomainObjectContainer.java |   52 +
 .../internal/test/DefaultTestSuiteContainer.java   |   28 +
 .../base/internal/toolchain/ArgCollector.java      |   25 +
 .../base/internal/toolchain/ArgWriter.java         |   89 +
 .../toolchain/DefaultResolvedCompiler.java         |   45 +
 .../internal/toolchain/DefaultResolvedTool.java    |   43 +
 .../internal/toolchain/DefaultToolResolver.java    |  178 +
 .../base/internal/toolchain/ResolvedTool.java      |   21 +
 .../internal/toolchain/ToolChainAvailability.java  |   68 +
 .../base/internal/toolchain/ToolChainInternal.java |   27 +
 .../base/internal/toolchain/ToolProvider.java      |   26 +
 .../base/internal/toolchain/ToolResolver.java      |   29 +
 .../base/internal/toolchain/ToolSearchResult.java  |   28 +
 .../internal/util/ImplementationTypeDetermer.java  |   61 +
 .../org/gradle/platform/base/package-info.java     |   23 +
 .../platform/base/test/TestSuiteBinarySpec.java    |   31 +
 .../platform/base/test/TestSuiteContainer.java     |   27 +
 .../gradle/platform/base/test/TestSuiteSpec.java   |   31 +
 .../gradle/platform/base/test/package-info.java    |   23 +
 .../org.gradle.language-base.properties}           |    0
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../ComponentTypeModelRuleExtractorTest.groovy     |  182 +
 .../internal/DefaultFunctionalSourceSetTest.groovy |   30 +
 .../internal/SourceSetNotationParserTest.groovy    |   54 +
 .../LanguageTypeModelRuleExtractorTest.groovy      |  184 +
 .../tasks/SimpleStaleClassCleanerTest.groovy       |   75 +
 .../plugins/ComponentModelBasePluginTest.groovy    |  283 ++
 .../base/plugins/LanguageBasePluginTest.groovy     |   73 +
 .../base/plugins/LifecycleBasePluginTest.groovy    |   88 +
 .../platform/base/binary/BaseBinarySpecTest.groovy |   70 +
 .../base/component/BaseComponentSpecTest.groovy    |  105 +
 .../base/internal/BuildableModelElementTest.groovy |   54 +
 .../internal/DefaultBinaryNamingSchemeTest.groovy  |   79 +
 .../DefaultBinaryTasksCollectionTest.groovy        |   82 +
 .../base/internal/FixedBuildAbilityTest.groovy     |   49 +
 .../internal/ToolSearchBuildAbilityTest.groovy     |   56 +
 ...AbstractAnnotationModelRuleExtractorTest.groovy |   67 +
 .../BinaryTasksModelRuleExtractorTest.groovy       |  108 +
 .../BinaryTypeModelRuleExtractorTest.groovy        |  179 +
 .../ComponentBinariesModelRuleExtractorTest.groovy |  134 +
 ...warePolymorphicDomainObjectContainerTest.groovy |   66 +
 .../base/internal/toolchain/ArgWriterTest.groovy   |   78 +
 .../toolchain/DefaultToolResolverTest.groovy       |  128 +
 .../toolchain/ToolChainAvailabilityTest.groovy     |   67 +
 .../plugin/AbstractLanguagePluginSpec.groovy       |   53 +
 subprojects/platform-jvm/platform-jvm.gradle       |   17 +
 .../jvm/ComponentReportIntegrationTest.groovy      |  186 +
 .../gradle/jvm/JarBinariesIntegrationTest.groovy   |   88 +
 ...toTestedSamplePlatformJvmIntegrationTest.groovy |   27 +
 .../JvmComponentPluginGoodBehaviourTest.groovy     |   21 +
 .../JvmComponentPluginIntegrationTest.groovy       |  246 +
 .../org/gradle/api/java/archives/Attributes.java   |    0
 .../org/gradle/api/java/archives/Manifest.java     |  112 +
 .../api/java/archives/ManifestException.java       |    0
 .../api/java/archives/ManifestMergeDetails.java    |    0
 .../api/java/archives/ManifestMergeSpec.java       |   57 +
 .../java/archives/internal/DefaultAttributes.java  |   97 +
 .../java/archives/internal/DefaultManifest.java    |  241 +
 .../internal/DefaultManifestMergeDetails.java      |    0
 .../internal/DefaultManifestMergeSpec.java         |  122 +
 .../org/gradle/api/java/archives/package-info.java |    0
 .../src/main/java/org/gradle/jvm/Classpath.java    |   28 +
 .../main/java/org/gradle/jvm/JarBinarySpec.java    |   38 +
 .../main/java/org/gradle/jvm/JvmBinarySpec.java    |   84 +
 .../main/java/org/gradle/jvm/JvmBinaryTasks.java   |   32 +
 .../src/main/java/org/gradle/jvm/JvmByteCode.java  |   27 +
 .../java/org/gradle/jvm/JvmComponentExtension.java |   33 +
 .../main/java/org/gradle/jvm/JvmComponentSpec.java |   25 +
 .../src/main/java/org/gradle/jvm/JvmLibrary.java   |   27 +
 .../main/java/org/gradle/jvm/JvmLibrarySpec.java   |   29 +
 .../src/main/java/org/gradle/jvm/JvmResources.java |   28 +
 .../jvm/internal/AbstractJvmBinaryRenderer.java    |   29 +
 .../org/gradle/jvm/internal/DefaultClasspath.java  |   39 +
 .../gradle/jvm/internal/DefaultJarBinarySpec.java  |  108 +
 .../gradle/jvm/internal/DefaultJvmBinaryTasks.java |   33 +
 .../gradle/jvm/internal/DefaultJvmLibrarySpec.java |   54 +
 .../org/gradle/jvm/internal/JarBinaryRenderer.java |   32 +
 .../gradle/jvm/internal/JarBinarySpecInternal.java |   26 +
 .../gradle/jvm/internal/JavaPlatformResolver.java  |   51 +
 .../jvm/internal/JvmLibrarySpecInternal.java       |   26 +
 .../gradle/jvm/internal/PlatformJvmServices.java   |   35 +
 .../plugins/DefaultJvmComponentExtension.java      |   34 +
 .../internal/toolchain/JavaToolChainInternal.java  |   24 +
 .../src/main/java/org/gradle/jvm/package-info.java |   23 +
 .../java/org/gradle/jvm/platform/JavaPlatform.java |   45 +
 .../jvm/platform/internal/DefaultJavaPlatform.java |   64 +
 .../java/org/gradle/jvm/platform/package-info.java |   20 +
 .../org/gradle/jvm/plugins/JvmComponentPlugin.java |  181 +
 .../java/org/gradle/jvm/plugins/package-info.java  |   23 +
 .../src/main/java/org/gradle/jvm/tasks/Jar.java    |  130 +
 .../java/org/gradle/jvm/tasks/package-info.java    |   23 +
 .../org/gradle/jvm/toolchain/JavaToolChain.java    |   39 +
 .../jvm/toolchain/JavaToolChainRegistry.java       |   29 +
 .../internal/DefaultJavaToolChainRegistry.java     |   34 +
 .../org/gradle/jvm/toolchain/package-info.java     |   20 +
 .../org.gradle.jvm-component.properties            |   17 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../archives/internal/DefaultAttributesTest.groovy |    0
 .../internal/DefaultManifestMergeSpecTest.groovy   |    0
 .../archives/internal/DefaultManifestTest.groovy   |    0
 .../jvm/internal/DefaultJarBinarySpecTest.groovy   |   65 +
 .../jvm/internal/DefaultJvmBinaryTasksTest.groovy  |   60 +
 .../jvm/internal/DefaultJvmLibrarySpecTest.groovy  |   71 +
 .../internal/plugins/CreateJvmBinariesTest.groovy  |   98 +
 .../internal/DefaultJavaPlatformTest.groovy        |   51 +
 .../groovy/org/gradle/jvm/tasks/JarTest.groovy     |   61 +
 subprojects/platform-native/platform-native.gradle |   42 +
 ...estedSamplesRuntimeNativeIntegrationTest.groovy |   31 +
 .../BinaryBuildTypesIntegrationTest.groovy         |  210 +
 .../BinaryConfigurationIntegrationTest.groovy      |  384 ++
 .../BinaryFlavorsIntegrationTest.groovy            |  175 +
 .../ComponentReportIntegrationTest.groovy          |  256 +
 .../LibraryApiDependenciesIntegrationTest.groovy   |  264 +
 .../LibraryBinariesIntegrationTest.groovy          |  317 ++
 .../LibraryDependenciesIntegrationTest.groovy      |  399 ++
 .../NativeBinariesIntegrationTest.groovy           |  360 ++
 .../NativePlatformSamplesIntegrationTest.groovy    |  240 +
 .../PrebuiltLibrariesIntegrationTest.groovy        |  370 ++
 .../SharedLibrarySoNameIntegrationTest.groovy      |   82 +
 .../BinaryNativePlatformIntegrationTest.groovy     |  416 ++
 .../NativeComponentPluginIntegrationTest.groovy    |   22 +
 .../GeneratedSourcesIntegrationTest.groovy         |  461 ++
 ...rceSetCompileDependenciesIntegrationTest.groovy |  147 +
 .../SourceSetDependenciesIntegrationTest.groovy    |  169 +
 ...SourceSetLinkDependenciesIntegrationTest.groovy |  154 +
 .../CommonToolchainCustomizationIntegTest.groovy   |   61 +
 ...ToolChainCrossCompilationIntegrationTest.groovy |   92 +
 ...GccToolChainCustomisationIntegrationTest.groovy |  215 +
 .../GccToolChainDiscoveryIntegrationTest.groovy    |  162 +
 .../MultipleNativeToolChainIntegrationTest.groovy  |   91 +
 .../NativeToolChainDiscoveryIntegrationTest.groovy |   91 +
 ...sualCppToolChainDiscoveryIntegrationTest.groovy |   84 +
 .../nativeplatform/DependentSourceSet.java         |   71 +
 .../nativeplatform/HeaderExportingSourceSet.java   |   43 +
 .../language/nativeplatform/NativeResourceSet.java |   26 +
 .../nativeplatform/internal/SourceIncludes.java    |   24 +
 .../language/nativeplatform/package-info.java      |   20 +
 .../org/gradle/nativeplatform/BuildType.java       |   30 +
 .../gradle/nativeplatform/BuildTypeContainer.java  |   27 +
 .../groovy/org/gradle/nativeplatform/Flavor.java   |   31 +
 .../org/gradle/nativeplatform/FlavorContainer.java |   30 +
 .../org/gradle/nativeplatform/NativeBinary.java    |   42 +
 .../gradle/nativeplatform/NativeBinarySpec.java    |   92 +
 .../nativeplatform/NativeComponentExtension.java   |   47 +
 .../gradle/nativeplatform/NativeComponentSpec.java |   35 +
 .../gradle/nativeplatform/NativeDependencySet.java |   40 +
 .../gradle/nativeplatform/NativeExecutable.java    |   27 +
 .../nativeplatform/NativeExecutableBinary.java     |   31 +
 .../nativeplatform/NativeExecutableBinarySpec.java |   73 +
 .../nativeplatform/NativeExecutableSpec.java       |   27 +
 .../org/gradle/nativeplatform/NativeLibrary.java   |   26 +
 .../gradle/nativeplatform/NativeLibraryBinary.java |   33 +
 .../nativeplatform/NativeLibraryBinarySpec.java    |   36 +
 .../nativeplatform/NativeLibraryRequirement.java   |   39 +
 .../gradle/nativeplatform/NativeLibrarySpec.java   |   40 +
 .../org/gradle/nativeplatform/ObjectFile.java      |   27 +
 .../gradle/nativeplatform/PrebuiltLibraries.java   |   29 +
 .../org/gradle/nativeplatform/PrebuiltLibrary.java |   38 +
 .../PrebuiltSharedLibraryBinary.java               |   37 +
 .../PrebuiltStaticLibraryBinary.java               |   32 +
 .../org/gradle/nativeplatform/Repositories.java    |   28 +
 .../gradle/nativeplatform/SharedLibraryBinary.java |   37 +
 .../nativeplatform/SharedLibraryBinarySpec.java    |   65 +
 .../gradle/nativeplatform/StaticLibraryBinary.java |   34 +
 .../nativeplatform/StaticLibraryBinarySpec.java    |   61 +
 .../nativeplatform/TargetedNativeComponent.java    |   39 +
 .../groovy/org/gradle/nativeplatform/Tool.java     |   36 +
 .../internal/AbstractBinaryToolSpec.java           |   80 +
 .../internal/AbstractNativeBinaryRenderer.java     |   31 +
 .../internal/AbstractNativeBinarySpec.java         |  166 +
 .../internal/AbstractNativeComponentSpec.java      |   44 +
 .../internal/AbstractNativeLibraryBinarySpec.java  |  103 +
 .../AbstractTargetedNativeComponentSpec.java       |   77 +
 .../nativeplatform/internal/BinaryToolSpec.java    |   49 +
 .../BuildOperationLoggingCompilerDecorator.java    |   43 +
 .../internal/CompilerOutputFileNamingScheme.java   |   44 +
 .../nativeplatform/internal/DefaultBuildType.java  |   39 +
 .../internal/DefaultBuildTypeContainer.java        |   34 +
 .../nativeplatform/internal/DefaultFlavor.java     |   41 +
 .../internal/DefaultFlavorContainer.java           |   34 +
 .../nativeplatform/internal/DefaultLinkerSpec.java |   68 +
 .../DefaultNativeExecutableBinarySpec.java         |   79 +
 .../internal/DefaultNativeExecutableSpec.java      |   25 +
 .../internal/DefaultNativeLibrarySpec.java         |   39 +
 .../internal/DefaultSharedLibraryBinarySpec.java   |  132 +
 .../internal/DefaultStaticLibraryArchiverSpec.java |   45 +
 .../internal/DefaultStaticLibraryBinarySpec.java   |  100 +
 .../nativeplatform/internal/DefaultTool.java       |   37 +
 .../gradle/nativeplatform/internal/LinkerSpec.java |   43 +
 .../internal/NativeBinarySpecInternal.java         |   63 +
 .../internal/NativeExecutableBinaryRenderer.java   |   37 +
 .../NativeExecutableBinarySpecInternal.java        |   22 +
 .../internal/NativePlatformResolver.java           |   49 +
 .../internal/ProjectNativeLibraryRequirement.java  |   48 +
 .../internal/SharedLibraryBinaryRenderer.java      |   32 +
 .../internal/SharedLibraryBinarySpecInternal.java  |   22 +
 .../internal/SharedLibraryLinkerSpec.java          |   23 +
 .../internal/StaticLibraryArchiverSpec.java        |   31 +
 .../internal/StaticLibraryBinaryRenderer.java      |   32 +
 .../internal/StaticLibraryBinarySpecInternal.java  |   22 +
 .../internal/TargetedNativeComponentInternal.java  |   28 +
 .../configure/DefaultNativeBinariesFactory.java    |   77 +
 .../configure/DefaultNativeComponentExtension.java |   49 +
 .../internal/configure/NativeBinariesFactory.java  |   28 +
 .../configure/NativeBinarySpecInitializer.java     |   51 +
 .../configure/NativeComponentSpecInitializer.java  |  109 +
 .../ToolSettingNativeBinaryInitializer.java        |   43 +
 ...DefaultPreCompiledHeaderTransformContainer.java |   33 +
 .../pch/PreCompiledHeaderTransformContainer.java   |   23 +
 .../prebuilt/AbstractPrebuiltLibraryBinary.java    |  107 +
 .../prebuilt/DefaultPrebuiltLibraries.java         |   59 +
 .../internal/prebuilt/DefaultPrebuiltLibrary.java  |   50 +
 .../DefaultPrebuiltSharedLibraryBinary.java        |   66 +
 .../DefaultPrebuiltStaticLibraryBinary.java        |   55 +
 .../prebuilt/PrebuiltLibraryBinaryLocator.java     |   60 +
 .../prebuilt/PrebuiltLibraryInitializer.java       |   80 +
 .../prebuilt/PrebuiltLibraryResolveException.java  |   27 +
 .../ApiRequirementNativeDependencyResolver.java    |  104 +
 .../resolve/ChainedLibraryBinaryLocator.java       |   51 +
 .../internal/resolve/DefaultLibraryResolver.java   |  109 +
 .../resolve/DefaultNativeDependencySet.java        |   41 +
 .../internal/resolve/DefaultProjectLocator.java    |   42 +
 .../InputHandlingNativeDependencyResolver.java     |   36 +
 .../internal/resolve/LibraryBinaryLocator.java     |   25 +
 .../resolve/LibraryNativeDependencyResolver.java   |   39 +
 .../internal/resolve/LibraryResolveException.java  |   32 +
 .../NativeBinaryRequirementResolveResult.java      |   64 +
 .../resolve/NativeBinaryResolveResult.java         |   74 +
 .../resolve/NativeDependencyNotationParser.java    |   59 +
 .../internal/resolve/NativeDependencyResolver.java |   20 +
 .../resolve/NativeDependencyResolverServices.java  |   48 +
 .../resolve/ProjectLibraryBinaryLocator.java       |   54 +
 .../internal/resolve/ProjectLocator.java           |   22 +
 ...RequirementParsingNativeDependencyResolver.java |   38 +
 .../resolve/SourceSetNativeDependencyResolver.java |   97 +
 .../internal/services/NativeBinaryServices.java    |   50 +
 .../org/gradle/nativeplatform/package-info.java    |   20 +
 .../nativeplatform/platform/Architecture.java      |   32 +
 .../nativeplatform/platform/NativePlatform.java    |  119 +
 .../nativeplatform/platform/OperatingSystem.java   |   60 +
 .../platform/internal/ArchitectureInternal.java    |   30 +
 .../platform/internal/Architectures.java           |   68 +
 .../platform/internal/DefaultArchitecture.java     |   70 +
 .../platform/internal/DefaultNativePlatform.java   |   85 +
 .../platform/internal/DefaultOperatingSystem.java  |   92 +
 .../platform/internal/NativePlatformInternal.java  |   24 +
 .../platform/internal/NativePlatforms.java         |  112 +
 .../platform/internal/OperatingSystemInternal.java |   23 +
 .../nativeplatform/platform/package-info.java      |   20 +
 .../plugins/NativeComponentModelPlugin.java        |  319 ++
 .../plugins/NativeComponentPlugin.groovy           |  132 +
 .../nativeplatform/plugins/package-info.java       |   20 +
 .../nativeplatform/tasks/AbstractLinkTask.groovy   |  130 +
 .../tasks/CreateStaticLibrary.groovy               |  111 +
 .../nativeplatform/tasks/InstallExecutable.groovy  |  169 +
 .../nativeplatform/tasks/LinkExecutable.groovy     |   33 +
 .../nativeplatform/tasks/LinkSharedLibrary.groovy  |   46 +
 .../nativeplatform/tasks/ObjectFilesToBinary.java  |   32 +
 .../tasks/PrefixHeaderFileGenerateTask.java        |   58 +
 .../gradle/nativeplatform/tasks/package-info.java  |   20 +
 .../test/NativeTestSuiteBinarySpec.java            |   84 +
 .../nativeplatform/test/NativeTestSuiteSpec.java   |   33 +
 .../internal/DefaultNativeTestSuiteBinarySpec.java |   93 +
 .../internal/NativeTestSuiteBinaryRenderer.java    |   38 +
 .../NativeTestSuiteBinarySpecInternal.java         |   24 +
 .../gradle/nativeplatform/test/package-info.java   |   20 +
 .../test/plugins/NativeBinariesTestPlugin.java     |  102 +
 .../nativeplatform/test/plugins/package-info.java  |   20 +
 .../test/tasks/RunTestExecutable.groovy            |   72 +
 .../nativeplatform/test/tasks/package-info.java    |   20 +
 .../org/gradle/nativeplatform/toolchain/Clang.java |   26 +
 .../toolchain/CommandLineToolConfiguration.java    |   33 +
 .../org/gradle/nativeplatform/toolchain/Gcc.java   |   26 +
 .../toolchain/GccCommandLineToolConfiguration.java |   38 +
 .../toolchain/GccCompatibleToolChain.java          |   56 +
 .../toolchain/GccPlatformToolChain.java            |   60 +
 .../toolchain/NativePlatformToolChain.java         |   31 +
 .../nativeplatform/toolchain/NativeToolChain.java  |   29 +
 .../toolchain/NativeToolChainRegistry.java         |   30 +
 .../gradle/nativeplatform/toolchain/VisualCpp.java |   53 +
 .../toolchain/VisualCppPlatformToolChain.java      |   55 +
 .../internal/AbstractPlatformToolProvider.java     |  161 +
 .../toolchain/internal/ArgsTransformer.java        |   25 +
 .../toolchain/internal/CommandLineToolContext.java |   36 +
 .../internal/CommandLineToolInvocation.java        |   36 +
 .../internal/CommandLineToolInvocationFailure.java |   26 +
 .../internal/CommandLineToolInvocationWorker.java  |   26 +
 .../internal/DefaultCommandLineToolInvocation.java |   63 +
 .../DefaultCommandLineToolInvocationWorker.java    |   90 +
 .../DefaultMutableCommandLineToolContext.java      |   70 +
 .../internal/DefaultNativeToolChainRegistry.java   |  134 +
 .../toolchain/internal/ExtendableToolChain.java    |   69 +
 .../toolchain/internal/MacroArgsConverter.java     |   35 +
 .../internal/MutableCommandLineToolContext.java    |   34 +
 .../toolchain/internal/NativeCompileSpec.java      |   85 +
 .../toolchain/internal/NativeCompiler.java         |  155 +
 .../internal/NativeToolChainInternal.java          |   39 +
 .../internal/NativeToolChainRegistryInternal.java  |   31 +
 .../toolchain/internal/OptionsFileArgsWriter.java  |   66 +
 .../toolchain/internal/OutputCleaningCompiler.java |   67 +
 .../internal/PCHObjectDirectoryGeneratorUtil.java  |   39 +
 .../toolchain/internal/PlatformToolProvider.java   |   33 +
 .../internal/PrefixHeaderFileGeneratorUtil.java    |   49 +
 .../toolchain/internal/ToolType.java               |   45 +
 .../internal/UnavailablePlatformToolProvider.java  |   86 +
 .../toolchain/internal/clang/ClangToolChain.java   |   51 +
 .../internal/compilespec/AssembleSpec.java         |   26 +
 .../internal/compilespec/CCompileSpec.java         |   23 +
 .../internal/compilespec/CPCHCompileSpec.java      |   22 +
 .../internal/compilespec/CppCompileSpec.java       |   23 +
 .../internal/compilespec/CppPCHCompileSpec.java    |   22 +
 .../compilespec/ObjectiveCCompileSpec.java         |   23 +
 .../compilespec/ObjectiveCPCHCompileSpec.java      |   22 +
 .../compilespec/ObjectiveCppCompileSpec.java       |   23 +
 .../compilespec/ObjectiveCppPCHCompileSpec.java    |   22 +
 .../compilespec/WindowsResourceCompileSpec.java    |   21 +
 .../gcc/AbstractGccCompatibleToolChain.java        |  254 +
 .../internal/gcc/ArStaticLibraryArchiver.java      |   88 +
 .../toolchain/internal/gcc/Assembler.java          |   36 +
 .../toolchain/internal/gcc/CCompiler.java          |   36 +
 .../toolchain/internal/gcc/CPCHCompiler.java       |   35 +
 .../toolchain/internal/gcc/CppCompiler.java        |   36 +
 .../toolchain/internal/gcc/CppPCHCompiler.java     |   35 +
 .../internal/gcc/DefaultGccPlatformToolChain.java  |  100 +
 .../internal/gcc/GccCompatibleNativeCompiler.java  |   55 +
 .../internal/gcc/GccCompilerArgsTransformer.java   |   70 +
 .../toolchain/internal/gcc/GccLinker.java          |  111 +
 .../internal/gcc/GccOptionsFileArgsWriter.java     |   53 +
 .../internal/gcc/GccPlatformToolProvider.java      |  139 +
 .../toolchain/internal/gcc/GccToolChain.java       |   47 +
 .../toolchain/internal/gcc/ObjectiveCCompiler.java |   36 +
 .../internal/gcc/ObjectiveCPCHCompiler.java        |   35 +
 .../internal/gcc/ObjectiveCppCompiler.java         |   36 +
 .../internal/gcc/ObjectiveCppPCHCompiler.java      |   35 +
 .../internal/gcc/TargetPlatformConfiguration.java  |   31 +
 .../gcc/version/CompilerMetaDataProvider.java      |   24 +
 .../version/CompilerMetaDataProviderFactory.java   |   90 +
 .../internal/gcc/version/GccVersionDeterminer.java |  218 +
 .../internal/gcc/version/GccVersionResult.java     |   32 +
 .../toolchain/internal/msvcpp/Assembler.java       |   44 +
 .../toolchain/internal/msvcpp/CCompiler.java       |   36 +
 .../toolchain/internal/msvcpp/CPCHCompiler.java    |   38 +
 .../toolchain/internal/msvcpp/CppCompiler.java     |   36 +
 .../toolchain/internal/msvcpp/CppPCHCompiler.java  |   38 +
 .../msvcpp/DefaultVisualCppPlatformToolChain.java  |   72 +
 .../msvcpp/DefaultVisualStudioLocator.java         |  368 ++
 .../internal/msvcpp/DefaultWindowsSdkLocator.java  |  275 ++
 .../toolchain/internal/msvcpp/EscapeUserArgs.java  |   40 +
 .../msvcpp/LibExeStaticLibraryArchiver.java        |   78 +
 .../toolchain/internal/msvcpp/LinkExeLinker.java   |   85 +
 .../msvcpp/VisualCppCompilerArgsTransformer.java   |   69 +
 .../internal/msvcpp/VisualCppInstall.java          |  112 +
 .../internal/msvcpp/VisualCppNativeCompiler.java   |   61 +
 .../msvcpp/VisualCppOptionsFileArgsWriter.java     |   31 +
 .../VisualCppPCHCompilerArgsTransformer.java       |   26 +
 .../VisualCppPCHSourceFileGeneratorUtil.java       |   62 +
 .../msvcpp/VisualCppPCHSourceFileTransformer.java  |   36 +
 .../msvcpp/VisualCppPlatformToolProvider.java      |  201 +
 .../internal/msvcpp/VisualCppToolChain.java        |  139 +
 .../internal/msvcpp/VisualStudioInstall.java       |   48 +
 .../internal/msvcpp/VisualStudioLocator.java       |   29 +
 .../internal/msvcpp/WindowsResourceCompiler.java   |   48 +
 .../toolchain/internal/msvcpp/WindowsSdk.java      |  138 +
 .../internal/msvcpp/WindowsSdkLocator.java         |   29 +
 .../internal/plugins/StandardToolChainsPlugin.java |   34 +
 .../CommandLineToolConfigurationInternal.java      |   27 +
 .../tools/CommandLineToolSearchResult.java         |   25 +
 .../tools/DefaultCommandLineToolConfiguration.java |   45 +
 .../DefaultGccCommandLineToolConfiguration.java    |   35 +
 .../GccCommandLineToolConfigurationInternal.java   |   29 +
 .../toolchain/internal/tools/ToolRegistry.java     |   22 +
 .../toolchain/internal/tools/ToolSearchPath.java   |  184 +
 .../nativeplatform/toolchain/package-info.java     |   20 +
 .../toolchain/plugins/ClangCompilerPlugin.java     |   65 +
 .../toolchain/plugins/GccCompilerPlugin.java       |   67 +
 .../plugins/MicrosoftVisualCppPlugin.java          |   68 +
 .../toolchain/plugins/package-info.java            |   20 +
 .../org.gradle.native-component.properties         |    1 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../internal/DefaultBuildTypeTest.groovy           |   29 +
 .../internal/DefaultFlavorTest.groovy              |   29 +
 .../internal/DefaultNativeComponentTest.groovy     |   59 +
 .../DefaultNativeExecutableBinarySpecTest.groovy   |   77 +
 .../DefaultNativeExecutableSpecTest.groovy         |   35 +
 .../internal/DefaultNativeLibrarySpecTest.groovy   |   66 +
 .../DefaultSharedLibraryBinarySpecTest.groovy      |  157 +
 .../DefaultStaticLibraryBinarySpecTest.groovy      |  154 +
 .../internal/NativeBinarySpecTest.groovy           |  221 +
 .../configure/CreateDefaultBuildTypesTest.groovy   |   45 +
 .../configure/CreateDefaultFlavorsTest.groovy      |   68 +
 .../DefaultNativeBinariesFactoryTest.groovy        |  103 +
 .../NativeBinarySpecInitializerTest.groovy         |   99 +
 .../NativeComponentSpecInitializerTest.groovy      |  180 +
 .../ToolSettingNativeBinaryInitializerTest.groovy  |   71 +
 .../DefaultPrebuiltSharedLibraryBinaryTest.groovy  |   74 +
 .../DefaultPrebuiltStaticLibraryBinaryTest.groovy  |   56 +
 .../NativeDependencyNotationParserTest.groovy      |   72 +
 .../resolve/ProjectLibraryBinaryLocatorTest.groovy |  148 +
 .../platform/internal/ArchitecturesTest.groovy     |   36 +
 .../internal/DefaultArchitectureTest.groovy        |   56 +
 .../internal/DefaultNativePlatformTest.groovy      |   54 +
 .../internal/DefaultOperatingSystemTest.groovy     |   58 +
 .../platform/internal/ReadelfBinaryInfoTest.groovy |  242 +
 .../plugins/NativeComponentModelPluginTest.groovy  |  230 +
 .../plugins/NativeComponentPluginTest.groovy       |  102 +
 .../DefaultNativeTestSuiteBinarySpecTest.groovy    |   68 +
 .../plugins/NativeBinariesTestPluginTest.groovy    |   70 +
 ...faultCommandLineToolInvocationWorkerTest.groovy |   49 +
 .../DefaultNativeToolChainRegistryTest.groovy      |  183 +
 .../toolchain/internal/NativeCompilerTest.groovy   |  199 +
 .../internal/OptionsFileArgsWriterTest.groovy      |   41 +
 .../internal/OutputCleaningCompilerTest.groovy     |  117 +
 .../PrefixHeaderFileGeneratorUtilTest.groovy       |   46 +
 ...navailableNativePlatformToolProviderTest.groovy |   49 +
 .../gcc/AbstractGccCompatibleToolChainTest.groovy  |  356 ++
 .../toolchain/internal/gcc/AssemblerTest.groovy    |   38 +
 .../toolchain/internal/gcc/CCompilerTest.groovy    |   38 +
 .../internal/gcc/ClangToolChainTest.groovy         |   58 +
 .../toolchain/internal/gcc/CppCompilerTest.groovy  |   39 +
 .../gcc/GccCompatibleNativeCompilerTest.groovy     |   41 +
 .../toolchain/internal/gcc/GccLinkerTest.groovy    |  216 +
 .../gcc/GccOptionsFileArgsWriterTest.groovy        |   51 +
 .../toolchain/internal/gcc/GccToolChainTest.groovy |   74 +
 .../gcc/version/GccVersionDeterminerTest.groovy    |  197 +
 .../toolchain/internal/msvcpp/AssemblerTest.groovy |   50 +
 .../toolchain/internal/msvcpp/CCompilerTest.groovy |   39 +
 .../internal/msvcpp/CppCompilerTest.groovy         |   40 +
 .../msvcpp/DefaultVisualStudioLocatorTest.groovy   |  202 +
 .../msvcpp/DefaultWindowsSdkLocatorTest.groovy     |  256 +
 .../msvcpp/VisualCppNativeCompilerTest.groovy      |   43 +
 .../VisualCppOptionsFileArgsWriterTest.groovy      |   30 +
 .../VisualCppPCHSourceFileGeneratorUtilTest.groovy |   52 +
 .../VisualCppPCHSourceFileTransformerTest.groovy   |   49 +
 .../VisualCppPlatformToolProviderTest.groovy       |   40 +
 .../internal/msvcpp/VisualCppToolChainTest.groovy  |  232 +
 .../msvcpp/WindowsResourceCompilerTest.groovy      |   51 +
 .../internal/tools/ToolSearchPathTest.groovy       |  200 +
 .../plugins/ClangCompilerPluginTest.groovy         |   57 +
 .../toolchain/plugins/GccCompilerPluginTest.groovy |   59 +
 .../plugins/MicrosoftVisualCppPluginTest.groovy    |   64 +
 .../plugins/NativeToolChainPluginTest.groovy       |   73 +
 .../ide/visualstudio/fixtures/FiltersFile.groovy   |    0
 .../ide/visualstudio/fixtures/ProjectFile.groovy   |  128 +
 .../ide/visualstudio/fixtures/SolutionFile.groovy  |    0
 ...bstractInstalledToolChainIntegrationSpec.groovy |   85 +
 .../fixtures/AvailableToolChains.java              |  503 ++
 .../fixtures/ExecutableFixture.groovy              |   35 +
 .../fixtures/NativeBinaryFixture.groovy            |   89 +
 .../fixtures/NativeInstallationFixture.groovy      |   73 +
 .../fixtures/NativePlatformsTestFixture.java       |   37 +
 .../fixtures/RequiresInstalledToolChain.groovy     |   30 +
 .../RequiresInstalledToolChainExtension.groovy     |   39 +
 .../fixtures/SharedLibraryFixture.groovy           |   47 +
 .../fixtures/SingleToolChainTestRunner.java        |   89 +
 .../fixtures/StaticLibraryFixture.groovy           |   29 +
 .../fixtures/ToolChainRequirement.java             |   32 +
 .../app/CCallingMixedCAndCppHelloWorldApp.groovy   |   85 +
 .../fixtures/app/CCompilerDetectingTestApp.groovy  |   81 +
 .../fixtures/app/CHelloWorldApp.groovy             |  173 +
 .../fixtures/app/CPCHHelloWorldApp.groovy          |  186 +
 .../fixtures/app/CppCallingCHelloWorldApp.groovy   |   77 +
 .../app/CppCompilerDetectingTestApp.groovy         |   81 +
 .../fixtures/app/CppHelloWorldApp.groovy           |  169 +
 .../fixtures/app/CppPCHHelloWorldApp.groovy        |  187 +
 .../ExeWithDiamondDependencyHelloWorldApp.groovy   |   58 +
 .../ExeWithLibraryUsingLibraryHelloWorldApp.groovy |  133 +
 .../nativeplatform/fixtures/app/HelloWorldApp.java |  121 +
 .../fixtures/app/IncrementalHelloWorldApp.java     |   53 +
 .../fixtures/app/MixedLanguageHelloWorldApp.groovy |  146 +
 .../app/MixedObjectiveCHelloWorldApp.groovy        |  122 +
 .../fixtures/app/ObjectiveCHelloWorldApp.groovy    |  147 +
 .../fixtures/app/ObjectiveCPCHHelloWorldApp.groovy |  190 +
 .../fixtures/app/ObjectiveCppHelloWorldApp.groovy  |  153 +
 .../app/ObjectiveCppPCHHelloWorldApp.groovy        |  187 +
 .../fixtures/app/PCHHelloWorldApp.groovy           |   31 +
 .../fixtures/app/PlatformDetectingTestApp.groovy   |   87 +
 .../nativeplatform/fixtures/app/TestApp.java       |   74 +
 .../fixtures/app/TestNativeComponent.groovy        |   40 +
 .../app/WindowsResourceHelloWorldApp.groovy        |  131 +
 .../fixtures/binaryinfo/BinaryInfo.java            |   28 +
 .../fixtures/binaryinfo/DumpbinBinaryInfo.groovy   |   97 +
 .../fixtures/binaryinfo/OtoolBinaryInfo.groovy     |   58 +
 .../fixtures/binaryinfo/ReadelfBinaryInfo.groovy   |   89 +
 subprojects/platform-play/platform-play.gradle     |   18 +
 ...xedPlayAndJavaLangProjectIntegrationTest.groovy |   71 +
 ...edPlayAndScalaLangProjectIntegrationTest.groovy |   70 +
 .../PlayAppWithFailingTestsIntegrationTest.groovy  |   66 +
 .../PlayApplicationBinariesIntegrationTest.groovy  |   52 +
 .../PlayBinaryApplicationIntegrationTest.groovy    |   94 +
 ...ayDistributionApplicationIntegrationTest.groovy |  121 +
 ...ayMultiProjectApplicationIntegrationTest.groovy |  179 +
 .../integtest/PlayPlatformIntegrationTest.groovy   |  136 +
 .../PlayTestApplicationIntegrationTest.groovy      |   69 +
 .../advanced/AdvancedAppContentVerifier.groovy     |   41 +
 .../PlayBinaryAdvancedAppIntegrationTest.groovy    |   56 +
 ...ayDistributionAdvancedAppIntegrationTest.groovy |   71 +
 .../basic/PlayBinaryBasicAppIntegrationTest.groovy |   28 +
 .../PlayDistributionBasicAppIntegrationTest.groovy |   28 +
 .../basic/PlayTestBasicAppIntegrationTest.groovy   |   36 +
 ...BinaryAppWithDependenciesIntegrationTest.groovy |   28 +
 ...butionAppWithDependenciesIntegrationTest.groovy |   28 +
 ...ayTestAppWithDependenciesIntegrationTest.groovy |   36 +
 .../DistributionTestExecHandleBuilder.groovy       |   76 +
 .../play/integtest/fixtures/PlayCoverage.groovy    |   21 +
 ...ayMultiVersionApplicationIntegrationTest.groovy |   50 +
 .../PlayMultiVersionIntegrationTest.groovy         |   28 +
 ...ultiVersionRunApplicationIntegrationTest.groovy |   47 +
 .../integtest/fixtures/app/AdvancedPlayApp.groovy  |   20 +
 .../integtest/fixtures/app/BasicPlayApp.groovy     |   20 +
 .../play/integtest/fixtures/app/PlayApp.groovy     |   94 +
 .../fixtures/app/PlayAppWithDependencies.groovy    |   20 +
 .../integtest/fixtures/app/PlayMultiProject.groovy |   26 +
 .../fixtures/app/WithFailingTestsApp.groovy        |   38 +
 .../AbstractPlaySampleIntegrationTest.groovy       |  100 +
 .../AdvancedPlaySampleIntegrationTest.groovy       |   49 +
 .../samples/BasicPlaySampleIntegrationTest.groovy  |   29 +
 .../MultiprojectPlaySampleIntegrationTest.groovy   |   78 +
 ...cationPluginGoodBehaviourIntegrationTest.groovy |   27 +
 .../PlayApplicationPluginIntegrationTest.groovy    |  193 +
 ...ScriptPluginGoodBehaviourIntegrationTest.groovy |   27 +
 .../PlayCoffeeScriptPluginIntegrationTest.groovy   |   95 +
 .../PlayDistributionPluginIntegrationTest.groovy   |  115 +
 ...ScriptPluginGoodBehaviourIntegrationTest.groovy |   28 +
 .../PlayJavaScriptPluginIntegrationTest.groovy     |   89 +
 ...stractCoffeeScriptCompileIntegrationTest.groovy |   94 +
 .../AbstractJavaScriptMinifyIntegrationTest.groovy |  149 +
 .../CoffeeScriptCompileIntegrationTest.groovy      |  234 +
 ...offeeScriptImplementationIntegrationTest.groovy |  104 +
 .../tasks/DistributionZipIntegrationTest.groovy    |   81 +
 .../tasks/JavaScriptMinifyIntegrationTest.groovy   |  230 +
 .../play/tasks/PlayAssetsJarIntegrationTest.groovy |   78 +
 .../play/tasks/RoutesCompileIntegrationTest.groovy |  236 +
 .../play/tasks/TwirlCompileIntegrationTest.groovy  |  213 +
 .../play/tasks/TwirlVersionIntegrationTest.groovy  |  108 +
 .../src/integTest/resources/coffee-script.min.js   |   12 +
 .../app/assets/javascripts/sample.js               |   47 +
 .../app/assets/javascripts/test.coffee             |   28 +
 .../app/controllers/Application.scala              |   35 +
 .../app/controllers/jva/PureJava.java              |   28 +
 .../app/controllers/scala/MixedJava.java           |   30 +
 .../app/advancedplayapp/app/models/DataType.java   |   32 +
 .../advancedplayapp/app/models/ScalaClass.scala    |    3 +
 .../app/special/strangename/Application.scala      |   24 +
 .../app/views/awesome/index.scala.html             |    7 +
 .../app/advancedplayapp/app/views/index.scala.html |    9 +
 .../app/advancedplayapp/app/views/main.scala.html  |   13 +
 .../fixtures/app/advancedplayapp/build.gradle      |   16 +
 .../fixtures/app/advancedplayapp/conf/jva.routes   |    1 +
 .../fixtures/app/advancedplayapp/conf/routes       |   10 +
 .../fixtures/app/advancedplayapp/conf/scala.routes |    2 +
 .../basicplayapp/app/controllers/Application.scala |   32 +
 .../app/basicplayapp/app/views/index.scala.html    |    7 +
 .../app/basicplayapp/app/views/main.scala.html     |   13 +
 .../fixtures/app/basicplayapp/build.gradle         |   11 +
 .../fixtures/app/basicplayapp/conf/routes          |    9 +
 .../app/basicplayapp/test/ApplicationSpec.scala    |   35 +
 .../app/basicplayapp/test/IntegrationSpec.scala    |   34 +
 .../app/controllers/Application.scala              |   33 +
 .../app/views/index.scala.html                     |    7 +
 .../app/views/main.scala.html                      |   13 +
 .../app/playappwithdependencies/build.gradle       |   16 +
 .../app/playappwithdependencies/conf/routes        |    9 +
 .../test/ApplicationSpec.scala                     |   36 +
 .../test/IntegrationSpec.scala                     |   38 +
 .../fixtures/app/playmultiproject/build.gradle     |    9 +
 .../app/playmultiproject/javalibrary/build.gradle  |    3 +
 .../javalibrary/src/main/java/org/test/Util.java   |    7 +
 .../primary/app/controllers/Application.scala      |   18 +
 .../app/playmultiproject/primary/build.gradle      |    8 +
 .../playmultiproject/primary/conf/application.conf |   11 +
 .../app/playmultiproject/primary/conf/routes       |    5 +
 .../playmultiproject/primary/public/primary.txt    |    1 +
 .../fixtures/app/playmultiproject/settings.gradle  |    1 +
 .../app/controllers/submodule/Application.scala    |   12 +
 .../app/playmultiproject/submodule/build.gradle    |    3 +
 .../submodule/public/submodule.txt                 |    1 +
 .../play/integtest/fixtures/app/shared/README      |    1 +
 .../fixtures/app/shared/conf/application.conf      |   11 +
 .../fixtures/app/shared/public/images/favicon.svg  |   10 +
 .../app/shared/public/javascripts/hello.js         |    3 +
 .../app/shared/public/stylesheets/main.css}        |    0
 .../test/FailingApplicationSpec.scala              |   36 +
 .../test/FailingIntegrationSpec.scala              |   36 +
 .../coffeescript/CoffeeScriptSourceSet.java        |   27 +
 .../internal/DefaultCoffeeScriptSourceSet.java     |   30 +
 .../gradle/language/coffeescript/package-info.java |   20 +
 .../language/javascript/JavaScriptSourceSet.java   |   27 +
 .../internal/DefaultJavaScriptSourceSet.java       |   30 +
 .../gradle/language/javascript/package-info.java   |   20 +
 .../gradle/language/routes/RoutesSourceSet.java    |   27 +
 .../routes/internal/DefaultRoutesSourceSet.java    |   30 +
 .../org/gradle/language/routes/package-info.java   |   20 +
 .../org/gradle/language/twirl/TwirlSourceSet.java  |   27 +
 .../twirl/internal/DefaultTwirlSourceSet.java      |   30 +
 .../org/gradle/language/twirl/package-info.java    |   20 +
 .../src/main/java/org/gradle/play/JvmClasses.java  |   50 +
 .../org/gradle/play/PlayApplicationBinarySpec.java |   51 +
 .../java/org/gradle/play/PlayApplicationSpec.java  |   30 +
 .../main/java/org/gradle/play/PublicAssets.java    |   39 +
 .../gradle/play/distribution/PlayDistribution.java |   29 +
 .../distribution/PlayDistributionContainer.java    |   28 +
 .../org/gradle/play/distribution/package-info.java |   20 +
 .../play/internal/CleaningPlayToolCompiler.java    |   40 +
 .../internal/DefaultPlayApplicationBinarySpec.java |  165 +
 .../play/internal/DefaultPlayApplicationSpec.java  |   47 +
 .../gradle/play/internal/DefaultPlayPlatform.java  |   64 +
 .../PlayApplicationBinarySpecInternal.java         |   46 +
 .../play/internal/PlayApplicationSpecInternal.java |   23 +
 .../play/internal/PlayPlatformNotationParser.java  |   65 +
 .../play/internal/PlayPlatformRequirement.java     |   63 +
 .../gradle/play/internal/PlayPlatformResolver.java |   86 +
 .../distribution/DefaultPlayDistribution.java      |   35 +
 .../DefaultPlayDistributionContainer.java          |   28 +
 .../javascript/DefaultJavaScriptCompileSpec.java   |   52 +
 .../internal/javascript/GoogleClosureCompiler.java |  150 +
 .../JavaScriptCompileDestinationCalculator.java    |   46 +
 .../internal/javascript/JavaScriptCompileSpec.java |   24 +
 .../play/internal/platform/PlayMajorVersion.java   |   67 +
 .../internal/platform/PlayPlatformInternal.java    |   23 +
 .../internal/routes/DefaultRoutesCompileSpec.java  |   51 +
 .../DefaultVersionedRoutesCompilerAdapter.java     |   50 +
 .../play/internal/routes/RoutesCompileSpec.java    |   28 +
 .../play/internal/routes/RoutesCompiler.java       |   88 +
 .../internal/routes/RoutesCompilerAdapterV22X.java |   59 +
 .../internal/routes/RoutesCompilerAdapterV23X.java |   63 +
 .../internal/routes/RoutesCompilerFactory.java     |   38 +
 .../routes/VersionedRoutesCompilerAdapter.java     |   34 +
 .../play/internal/run/DefaultPlayRunSpec.java      |   54 +
 .../run/DefaultVersionedPlayRunAdapter.java        |   96 +
 .../play/internal/run/PlayAppLifecycleUpdate.java  |   62 +
 .../play/internal/run/PlayApplicationRunner.java   |   65 +
 .../internal/run/PlayApplicationRunnerToken.java   |   33 +
 .../play/internal/run/PlayRunAdapterV22X.java      |   34 +
 .../play/internal/run/PlayRunAdapterV23X.java      |   35 +
 .../org/gradle/play/internal/run/PlayRunSpec.java  |   33 +
 .../internal/run/PlayRunWorkerClientProtocol.java  |   21 +
 .../internal/run/PlayRunWorkerServerProtocol.java  |   22 +
 .../gradle/play/internal/run/PlayWorkerClient.java |   56 +
 .../gradle/play/internal/run/PlayWorkerServer.java |   83 +
 .../play/internal/run/VersionedPlayRunAdapter.java |   30 +
 .../spec/PlayApplicationBinaryRenderer.java        |   34 +
 .../gradle/play/internal/spec/PlayCompileSpec.java |   28 +
 .../internal/toolchain/DaemonPlayCompiler.java     |   45 +
 .../internal/toolchain/DefaultPlayToolChain.java   |  123 +
 .../toolchain/DefaultPlayToolProvider.java         |  125 +
 .../internal/toolchain/PlayToolChainInternal.java  |   26 +
 .../toolchain/PlayToolChainServiceRegistry.java    |   50 +
 .../play/internal/toolchain/PlayToolProvider.java  |   22 +
 .../internal/twirl/DefaultTwirlCompileSpec.java    |   52 +
 .../play/internal/twirl/TwirlCompileSpec.java      |   28 +
 .../gradle/play/internal/twirl/TwirlCompiler.java  |   71 +
 .../internal/twirl/TwirlCompilerAdapterV10X.java   |  105 +
 .../internal/twirl/TwirlCompilerAdapterV22X.java   |   91 +
 .../play/internal/twirl/TwirlCompilerFactory.java  |   39 +
 .../twirl/VersionedTwirlCompilerAdapter.java       |   34 +
 .../main/java/org/gradle/play/package-info.java    |   20 +
 .../org/gradle/play/platform/PlayPlatform.java     |   35 +
 .../org/gradle/play/platform/package-info.java     |   20 +
 .../gradle/play/plugins/PlayApplicationPlugin.java |  431 ++
 .../play/plugins/PlayCoffeeScriptPlugin.java       |  132 +
 .../play/plugins/PlayDistributionPlugin.java       |  224 +
 .../gradle/play/plugins/PlayJavaScriptPlugin.java  |  103 +
 .../java/org/gradle/play/plugins/PlayPlugin.java   |   35 +
 .../play/plugins/PlayPluginConfigurations.java     |   93 +
 .../org/gradle/play/plugins/PlayTestPlugin.java    |  117 +
 .../java/org/gradle/play/plugins/package-info.java |   22 +
 .../org/gradle/play/tasks/JavaScriptMinify.java    |  150 +
 .../gradle/play/tasks/PlayCoffeeScriptCompile.java |   56 +
 .../main/java/org/gradle/play/tasks/PlayRun.java   |  136 +
 .../java/org/gradle/play/tasks/RoutesCompile.java  |  124 +
 .../java/org/gradle/play/tasks/TwirlCompile.java   |  171 +
 .../java/org/gradle/play/tasks/package-info.java   |   20 +
 .../org/gradle/play/toolchain/PlayToolChain.java   |   29 +
 .../org/gradle/play/toolchain/package-info.java    |   19 +
 .../scala/internal/reflect/ScalaCodecMapper.java   |   30 +
 .../scala/internal/reflect/ScalaListBuffer.java    |   38 +
 .../gradle/scala/internal/reflect/ScalaMethod.java |   80 +
 .../reflect/ScalaOptionInvocationWrapper.java      |   48 +
 .../internal/reflect/ScalaReflectionUtil.java      |   23 +
 .../scala/internal/reflect/package-info.java       |   23 +
 .../org.gradle.play-application.properties         |   17 +
 .../org.gradle.play-coffeescript.properties        |   17 +
 .../org.gradle.play-javascript.properties          |   17 +
 .../gradle-plugins/org.gradle.play.properties      |   17 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../DefaultCoffeeScriptSourceSetTest.groovy        |   32 +
 .../internal/DefaultJavaScriptSourceSetTest.groovy |   33 +
 .../DefaultPlayApplicationBinarySpecTest.groovy    |   52 +
 .../play/internal/DefaultPlayToolChainTest.groovy  |  142 +
 .../play/internal/PlayPlatformResolverTest.groovy  |  137 +
 ...vaScriptCompileDestinationCalculatorTest.groovy |   42 +
 .../toolchain/DaemonPlayCompilerTest.groovy        |   63 +
 .../toolchain/DefaultPlayToolProviderTest.groovy   |  103 +
 .../play/plugins/PlayCoffeeScriptPluginTest.groovy |   54 +
 .../play/plugins/PlayDistributionPluginTest.groovy |  235 +
 .../play/plugins/PlayJavaScriptPluginTest.groovy   |   54 +
 .../gradle/play/plugins/PlayTestPluginTest.groovy  |   78 +
 .../org/gradle/play/tasks/PlayRunTest.groovy       |   86 +
 .../org/gradle/play/tasks/TwirlCompileTest.groovy  |   82 +
 .../plugin-development/plugin-development.gradle   |   26 +
 .../JavaGradlePluginPluginIntegrationTest.groovy   |  167 +
 .../devel/plugins/JavaGradlePluginPlugin.java      |  154 +
 .../gradle/plugin/devel/plugins/package-info.java  |   20 +
 .../org.gradle.java-gradle-plugin.properties       |   17 +
 .../plugins/JavaGradlePluginPluginTest.groovy      |  202 +
 subprojects/plugin-use/plugin-use.gradle           |   29 +
 .../plugin/use/CorePluginUseIntegrationSpec.groovy |  195 +
 .../use/DeployedPortalIntegrationSpec.groovy       |   64 +
 ...readyOnClasspathDetectionIntegrationSpec.groovy |  106 +
 .../use/NonCorePluginUseIntegrationSpec.groovy     |   41 +
 .../NonDeclarativePluginUseIntegrationSpec.groovy  |  305 ++
 .../PluginUseClassLoadingIntegrationSpec.groovy    |  171 +
 .../plugin/use/PluginUseDslIntegrationSpec.groovy  |  238 +
 ...tPluginResolutionFailuresIntegrationSpec.groovy |  105 +
 .../use/RuleSourcePluginUseIntegrationSpec.groovy  |   60 +
 ...lutionCachingCrossVersionIntegrationTest.groovy |   70 +
 .../PluginResolutionCachingIntegrationTest.groovy  |  192 +
 ...esolutionDeprecatedClientIntegrationTest.groovy |  330 ++
 ...ginResolutionServiceCommsIntegrationTest.groovy |  366 ++
 .../PluginResolutionServiceIntegrationSpec.groovy  |  103 +
 .../internal/DefaultPluginRequestApplicator.java   |  248 +
 .../plugin/use/internal/PluginResolverFactory.java |   59 +
 .../internal/PluginUsePluginServiceRegistry.java   |  108 +
 .../internal/ClassPathPluginResolution.java        |   60 +
 .../resolve/internal/CompositePluginResolver.java  |   40 +
 .../use/resolve/internal/CorePluginResolver.java   |   61 +
 .../use/resolve/internal/NoopPluginResolver.java   |   48 +
 ...onCorePluginOnClasspathCheckPluginResolver.java |   56 +
 .../use/resolve/internal/PluginResolution.java     |   27 +
 .../resolve/internal/PluginResolutionResult.java   |   55 +
 .../use/resolve/internal/PluginResolveContext.java |   26 +
 .../use/resolve/internal/PluginResolver.java       |   31 +
 .../resolve/internal/SimplePluginResolution.java   |   37 +
 .../use/resolve/service/internal/ClientStatus.java |   67 +
 ...tionListeningPluginResolutionServiceClient.java |   89 +
 .../resolve/service/internal/ErrorResponse.java    |   44 +
 .../HttpPluginResolutionServiceClient.java         |  174 +
 ...MemoryCachingPluginResolutionServiceClient.java |  169 +
 .../OfflinePluginResolutionServiceClient.java      |   36 +
 ...istentCachingPluginResolutionServiceClient.java |  248 +
 .../internal/PluginResolutionServiceClient.java    |  160 +
 .../internal/PluginResolutionServiceResolver.java  |  159 +
 .../service/internal/PluginUseMetaData.java        |  105 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../resolve/internal/CorePluginResolverTest.groovy |   94 +
 ...steningPluginResolutionServiceClientTest.groovy |   97 +
 .../HttpPluginResolutionServiceClientTest.groovy   |  144 +
 ...CachingPluginResolutionServiceClientTest.groovy |  135 +
 ...CachingPluginResolutionServiceClientTest.groovy |  155 +
 .../PluginResolutionServiceTestServer.groovy       |  239 +
 subprojects/plugins/plugins.gradle                 |   15 +-
 .../ApplicationPluginIntegrationTest.groovy        |  223 +
 .../api/plugins/BasePluginIntegrationTest.groovy   |   26 +-
 .../DistributionPluginIntegrationTest.groovy       |   93 +-
 .../JavaLibraryDistributionIntegrationTest.groovy  |    2 +-
 .../api/plugins/ParallelJavaPluginTest.groovy      |   46 +
 .../api/tasks/JavaExecIntegrationTest.groovy       |    6 +-
 .../groovy/GroovyBasePluginIntegrationTest.groovy  |   42 +-
 .../groovy/GroovyPluginIntegrationTest.groovy      |   52 -
 .../AntForkingGroovyCompilerIntegrationTest.groovy |   46 -
 ...ntInProcessGroovyCompilerIntegrationTest.groovy |   42 -
 .../BasicGroovyCompilerIntegrationSpec.groovy      |  443 +-
 .../compile/GroovyCompilerIntegrationSpec.groovy   |   17 +-
 .../InProcessGroovyCompilerIntegrationTest.groovy  |    1 -
 .../IncrementalGroovyCompileIntegrationTest.groovy |   18 +
 .../compile/InvokeDynamicGroovyCompilerSpec.groovy |    1 -
 .../JreJavaHomeGroovyIntegrationTest.groovy        |   19 +-
 .../java/ComponentReportIntegrationTest.groovy     |   67 +
 .../JavaCrossCompilationIntegrationTest.groovy     |  112 +
 .../gradle/java/JavaPluginGoodBehaviourTest.groovy |   47 -
 .../AntForkingJavaCompilerIntegrationTest.groovy   |   35 -
 .../AntInProcessJavaCompilerIntegrationTest.groovy |   35 -
 .../BasicJavaCompilerIntegrationSpec.groovy        |  155 +-
 .../CommandLineJavaCompilerIntegrationTest.groovy  |    1 -
 .../InProcessJavaCompilerIntegrationTest.groovy    |    1 -
 ...ncrementalJavaCompilationIntegrationTest.groovy |  219 -
 .../compile/JavaCompilerIntegrationSpec.groovy     |   20 +-
 .../JreJavaHomeJavaIntegrationTest.groovy          |   20 +-
 .../gradle/javadoc/JavadocIntegrationTest.groovy   |   48 -
 .../testing/IncrementalTestIntegrationTest.groovy  |   10 +-
 .../testing/SuiteTimestampIntegrationTest.groovy   |    2 +-
 .../testing/TestEnvironmentIntegrationTest.groovy  |    1 +
 .../TestOutputListenerIntegrationTest.groovy       |   14 +-
 .../testing/TestReportIntegrationTest.groovy       |   40 +-
 .../gradle/testing/TestingIntegrationTest.groovy   |   81 +-
 .../CucumberJVMReportIntegrationTest.groovy        |    8 +-
 .../AbstractTestFilteringIntegrationTest.groovy    |    3 +
 .../gradle/testing/fixture/JUnitCoverage.groovy    |    3 +-
 .../gradle/testing/fixture/TestNGCoverage.groovy   |   14 +-
 .../junit/JUnitAssumptionsIntegrationTest.groovy   |    2 +-
 .../junit/JUnitCategoriesIntegrationSpec.groovy    |    2 +-
 .../JUnitConsoleLoggingIntegrationTest.groovy      |  121 +
 .../JUnitFilteringSupportIntegrationTest.groovy    |    4 +-
 .../testing/junit/JUnitIntegrationTest.groovy      |  309 +-
 .../testing/junit/JUnitJnaIntegrationTest.groovy   |   41 +
 .../junit/JUnitLoggingIntegrationTest.groovy       |  121 -
 ...JUnitLoggingOutputCaptureIntegrationTest.groovy |  296 ++
 .../TestNGConsoleLoggingIntegrationTest.groovy     |  172 +
 .../TestNGFailurePolicyIntegrationTest.groovy      |   85 +
 .../testing/testng/TestNGIntegrationTest.groovy    |   41 +-
 .../testng/TestNGLoggingIntegrationTest.groovy     |  170 -
 ...estNGLoggingOutputCaptureIntegrationTest.groovy |  173 +
 .../TestNGParallelSuiteIntegrationTest.groovy      |   73 +
 .../TestNGProducesOldReportsIntegrationTest.groovy |   10 +-
 .../TestNGStaticLoggingIntegrationTest.groovy      |  147 +
 .../ParallelJavaPluginTest/shared/build.gradle     |   60 +
 .../shared/src/custom/java/SomeClass.java          |   18 +
 .../shared/src/custom/resources/resource.txt}      |    0
 .../shared/src/customTest/java/SomeTest.java       |   24 +
 .../src/customTest/resources/testResource.txt}     |    0
 .../shared/src/main/java/SomeClass.java            |   18 +
 .../shared/src/main/resources/resource.txt}        |    0
 .../shared/src/test/java/SomeTest.java             |   24 +
 .../shared/src/test/resources/testResource.txt}    |    0
 .../canUseCustomFileExtensions/build.gradle        |    2 +-
 .../build.gradle                                   |    2 +-
 .../compileJavaFx8Code/build.gradle                |    5 +
 .../src/main/groovy/FxApp.groovy                   |    9 +
 .../configurationScriptNotSupported/build.gradle   |    7 +
 .../groovycompilerconfig.groovy                    |   23 +
 .../src/main/groovy/BrokenClass.groovy             |    5 +
 .../failsBecauseOfInvalidConfigFile/build.gradle   |    7 +
 .../groovycompilerconfig.groovy                    |    3 +
 .../src/main/groovy/BrokenClass.groovy             |    5 +
 .../failsBecauseOfMissingConfigFile/build.gradle   |    7 +
 .../src/main/groovy/BrokenClass.groovy             |    5 +
 .../groovyToolClassesAreNotVisible/build.gradle    |    5 +
 .../src/main/groovy/Thing.groovy                   |    3 +
 .../useConfigurationScript/build.gradle            |    7 +
 .../groovycompilerconfig.groovy                    |    5 +
 .../src/main/groovy/BrokenClass.groovy             |    5 +
 .../build.gradle                                   |    3 +-
 .../src/test/groovy/TestCaseTransformTest.groovy   |   17 +-
 .../canUseAstTransformWrittenInGroovy/build.gradle |    2 +-
 .../canUseBuiltInAstTransform/build.gradle         |    2 +-
 .../canUseThirdPartyAstTransform/build.gradle      |    2 +-
 .../gradle3235/build.gradle                        |   10 +
 .../src/main/groovy/com/example/Country.groovy     |   11 +
 .../org.codehaus.groovy.runtime.ExtensionModule    |    3 +
 .../src/test/groovy/DummyFileForCompilation.groovy |   22 +
 .../build.gradle                                   |    7 +
 .../groovycompilerconfig.groovy                    |    4 +
 .../newbuild.gradle                                |    5 +
 .../newgroovycompilerconfig.groovy                 |    5 +
 .../src/main/groovy/BrokenClass.groovy             |    5 +
 .../build.gradle                                   |    2 +-
 .../shared/build.gradle                            |    2 +-
 .../build.gradle                                   |    2 +-
 .../build.gradle                                   |    2 +-
 .../test/java/org/gradle/MySystemClassLoader.java  |    2 +-
 .../build.gradle                                   |    2 +-
 .../main/java/org/gradle/MySystemClassLoader.java  |    2 +-
 .../build.gradle                                   |    2 +-
 .../build.gradle                                   |    2 +-
 .../reportsUnloadableCategories/build.gradle       |    2 +-
 .../shared/build.gradle                            |   23 +
 .../src/test/groovy/org/gradle/JUnit4Test.groovy   |    0
 .../standardOutputLogging/build.gradle             |   34 +
 .../org/gradle/JUnit4StandardOutputTest.groovy     |    0
 .../build.gradle                                   |   11 -
 .../test/groovy/org/gradle/SystemErrTest.groovy    |   32 -
 .../build.gradle                                   |   11 -
 .../test/groovy/org/gradle/SystemOutTest.groovy    |   32 -
 .../canHaveMultipleTestTaskInstances/build.gradle  |    2 +-
 .../canRunSingleTests/build.gradle                 |    2 +-
 .../createsRunnerBeforeTests/build.gradle          |    8 +
 .../src/test/java/org/gradle/CustomRunner.java     |   57 +
 .../test/java/org/gradle/ExecutionOrderTest.java   |   20 +
 .../detectsTestClasses/build.gradle                |    2 +-
 .../executesTestsInCorrectEnvironment/build.gradle |    2 +-
 .../src/test/java/org/gradle/OkTest.java           |   41 +-
 .../src/test/java/org/gradle/OtherTest.java        |   15 -
 .../build.gradle                                   |    2 +-
 .../src/test/java/org/gradle/Unloadable.java       |    4 +
 .../suitesOutputIsVisible/build.gradle             |    2 +-
 .../supportsTestCategories/build.gradle            |    2 +-
 .../build.gradle                                   |    2 +-
 .../canRunTestsUsingJna/build.gradle               |   10 +
 .../canRunTestsUsingJna/src/test/java/OkTest.java  |    9 +
 .../shared/build.gradle                            |   23 -
 .../standardOutputLogging/build.gradle             |   34 -
 .../shared/build.gradle                            |   27 +
 .../TestWithFailureInConfigMethod.java             |   31 +
 .../src/test/java/org/gradle/OkTest.java           |    7 -
 .../groovyJdk15Failing/build.gradle                |    2 +-
 .../groovyJdk15Passing/build.gradle                |    2 +-
 .../distribution/plugins/DistributionPlugin.groovy |   74 +-
 .../internal/java/AbstractLanguageSourceSet.java   |   86 +
 .../api/internal/java/DefaultJavaSourceSet.java    |   50 +
 .../api/internal/java/DefaultJvmResourceSet.java   |   25 +
 .../jvm/ClassDirectoryBinaryNamingScheme.java      |   74 +
 .../internal/jvm/ClassDirectoryBinaryRenderer.java |   34 +
 .../jvm/ClassDirectoryBinarySpecInternal.java      |   25 +
 .../jvm/DefaultClassDirectoryBinarySpec.java       |  171 +
 .../org/gradle/api/internal/plugins/CleanRule.java |   56 -
 .../api/internal/plugins/ProcessResources.java     |   25 -
 .../internal/plugins/StartScriptGenerator.groovy   |  162 -
 .../api/internal/plugins/StartScriptGenerator.java |  141 +
 .../gradle/api/internal/plugins/UploadRule.java    |    8 +-
 .../gradle/api/internal/tasks/CompileServices.java |   36 +-
 .../internal/tasks/DefaultSourceSetContainer.java  |   21 +-
 .../api/internal/tasks/DefaultSourceSetOutput.java |    8 -
 .../internal/tasks/SourceSetCompileClasspath.java  |    2 +-
 .../compile/AntDependsStaleClassCleaner.groovy     |   48 -
 .../tasks/compile/AntGroovyCompiler.groovy         |   94 -
 .../internal/tasks/compile/AntJavaCompiler.groovy  |   75 -
 .../internal/tasks/compile/ApiGroovyCompiler.java  |  140 -
 .../api/internal/tasks/compile/ArgCollector.java   |   25 -
 .../api/internal/tasks/compile/ArgWriter.java      |   89 -
 .../tasks/compile/CleaningGroovyCompiler.java      |   40 -
 .../tasks/compile/CleaningJavaCompiler.java        |   50 -
 .../tasks/compile/CleaningJavaCompilerSupport.java |   39 -
 .../tasks/compile/CommandLineJavaCompiler.java     |   71 -
 .../CommandLineJavaCompilerArgumentsGenerator.java |   81 -
 .../api/internal/tasks/compile/CompileSpec.java    |   20 -
 .../tasks/compile/CompileSpecToArguments.java      |   21 -
 .../api/internal/tasks/compile/Compiler.java       |   22 -
 .../tasks/compile/DefaultJavaCompileSpec.java      |   51 -
 .../tasks/compile/DefaultJavaCompilerFactory.java  |  108 -
 .../compile/DefaultJvmLanguageCompileSpec.java     |   61 -
 .../tasks/compile/DelegatingGroovyCompiler.java    |   31 -
 .../tasks/compile/DelegatingJavaCompiler.java      |   31 -
 .../tasks/compile/ExecSpecBackedArgCollector.java  |   37 -
 .../GroovyCompileTransformingClassLoader.java      |  124 -
 .../tasks/compile/GroovyCompilerFactory.java       |   82 -
 .../compile/InProcessJavaCompilerFactory.java      |   52 -
 .../internal/tasks/compile/JavaCompileSpec.java    |   37 -
 .../compile/JavaCompilerArgumentsBuilder.java      |  168 -
 .../tasks/compile/JavaCompilerFactory.java         |   25 -
 .../tasks/compile/JvmLanguageCompileSpec.java      |   39 -
 .../tasks/compile/NoOpStaleClassCleaner.java       |   26 -
 .../tasks/compile/NormalizingGroovyCompiler.java   |  109 -
 .../tasks/compile/NormalizingJavaCompiler.java     |  105 -
 .../internal/tasks/compile/SunJavaCompiler.java    |   46 -
 .../tasks/compile/daemon/CompilerDaemon.java       |   26 -
 .../tasks/compile/daemon/CompilerDaemonClient.java |   66 -
 .../compile/daemon/CompilerDaemonManager.java      |   54 -
 .../tasks/compile/daemon/CompilerDaemonServer.java |   65 -
 .../daemon/CompilerDaemonServerProtocol.java       |   27 -
 .../compile/daemon/CompilerDaemonStarter.java      |   68 -
 .../tasks/compile/daemon/DaemonGroovyCompiler.java |   75 -
 .../tasks/compile/daemon/DaemonJavaCompiler.java   |   51 -
 .../daemon/InProcessCompilerDaemonFactory.java     |   79 -
 .../compile/incremental/AllFromJarRebuildInfo.java |   41 -
 .../tasks/compile/incremental/ClassDependents.java |   44 -
 .../compile/incremental/ClassNameProvider.java     |   38 -
 .../compile/incremental/DefaultRebuildInfo.java    |   53 -
 .../tasks/compile/incremental/DummySerializer.java |   51 -
 .../incremental/IncrementalCompilationSupport.java |   51 -
 .../compile/incremental/InputOutputMapper.java     |   60 -
 .../tasks/compile/incremental/JarArchive.java      |   30 -
 .../compile/incremental/JarChangeProcessor.java    |   56 -
 .../tasks/compile/incremental/JarDelta.java        |   23 -
 .../tasks/compile/incremental/JarSnapshot.java     |   43 -
 .../compile/incremental/JarSnapshotCache.java      |   53 -
 .../compile/incremental/JarSnapshotFeeder.java     |   57 -
 .../tasks/compile/incremental/JarSnapshotter.java  |   46 -
 .../tasks/compile/incremental/JavaSourceClass.java |   41 -
 .../compile/incremental/OutputClassMapper.java     |   32 -
 .../tasks/compile/incremental/RebuildInfo.java     |   25 -
 .../compile/incremental/SelectiveCompilation.java  |  141 -
 .../compile/incremental/SelectiveJavaCompiler.java |   66 -
 .../incremental/SpecificClassesRebuildInfo.java    |   23 -
 .../incremental/analyzer/ClassAnalysis.java        |   37 -
 .../analyzer/ClassDependenciesAnalyzer.java        |   74 -
 .../analyzer/ClassDependenciesVisitor.java         |   58 -
 .../incremental/analyzer/ClassRelevancyFilter.java |   30 -
 .../incremental/graph/ClassDependencyInfo.java     |   59 -
 .../graph/ClassDependencyInfoExtractor.java        |   74 -
 .../graph/ClassDependencyInfoSerializer.java       |   47 -
 .../tasks/compile/jdk6/Jdk6JavaCompiler.java       |   79 -
 .../tasks/testing/DecoratingTestDescriptor.java    |    4 +-
 .../tasks/testing/DefaultTestClassDescriptor.java  |    2 +-
 .../tasks/testing/DefaultTestDescriptor.java       |    2 +-
 .../tasks/testing/DefaultTestMethodDescriptor.java |    2 +-
 .../tasks/testing/DefaultTestSuiteDescriptor.java  |    2 +-
 .../tasks/testing/SuiteTestClassProcessor.java     |    4 +-
 .../tasks/testing/TestClassLoaderFactory.java      |   41 +
 .../tasks/testing/TestDescriptorInternal.java      |    5 +
 .../testing/detection/DefaultTestExecuter.java     |    2 +-
 .../tasks/testing/detection/TestClassVisitor.java  |    2 +-
 .../internal/tasks/testing/junit/JUnitSpec.java    |    9 +-
 .../testing/junit/JUnitTestClassExecuter.java      |    2 +-
 .../testing/junit/JUnitTestClassProcessor.java     |   13 +-
 .../tasks/testing/junit/JUnitTestEventAdapter.java |    5 +-
 .../tasks/testing/junit/JUnitTestFramework.java    |   23 +-
 .../testing/junit/JUnitTestMethodDetecter.java     |    2 +-
 .../testing/junit/report/ClassPageRenderer.java    |    9 +-
 .../testing/junit/report/DefaultTestReport.java    |   38 +-
 .../testing/junit/report/OverviewPageRenderer.java |    2 +-
 .../testing/junit/report/PackagePageRenderer.java  |    2 +-
 .../tasks/testing/junit/report/PageRenderer.java   |   10 +-
 .../testing/junit/result/JUnitXmlResultWriter.java |    2 +-
 .../testing/junit/result/TestOutputStore.java      |    4 +-
 .../junit/result/TestReportDataCollector.java      |    4 +
 .../testing/junit/result/TestResultSerializer.java |   10 +-
 .../tasks/testing/logging/DefaultTestLogging.java  |    8 +-
 .../CaptureTestOutputTestResultProcessor.java      |   82 +-
 .../tasks/testing/processors/TestMainAction.java   |   17 +-
 .../testing/processors/TestOutputRedirector.java   |   78 +
 .../results/StateTrackingTestResultProcessor.java  |   33 +-
 .../tasks/testing/results/TestListenerAdapter.java |   18 +-
 .../testing/results/TestListenerInternal.java      |   31 +
 .../testing/results/UnknownTestDescriptor.java     |    3 +-
 .../testng/TestNGListenerAdapterFactory.java       |    3 +-
 .../internal/tasks/testing/testng/TestNGSpec.java  |    6 +
 .../testing/testng/TestNGTestClassProcessor.java   |   56 +-
 .../tasks/testing/testng/TestNGTestFramework.java  |   34 +-
 .../testing/testng/TestNGTestMethodDetecter.java   |    2 +-
 .../testng/TestNGTestResultProcessorAdapter.java   |   79 +-
 .../tasks/testing/worker/TestEventSerializer.java  |   27 +-
 .../internal/tasks/testing/worker/TestWorker.java  |   13 +-
 .../testing/worker/WorkerTestClassProcessor.java   |    2 +-
 .../org/gradle/api/java/archives/Manifest.java     |  112 -
 .../api/java/archives/ManifestMergeSpec.java       |   57 -
 .../java/archives/internal/DefaultAttributes.java  |   97 -
 .../java/archives/internal/DefaultManifest.java    |  241 -
 .../internal/DefaultManifestMergeSpec.java         |  122 -
 .../gradle/api/plugins/ApplicationPlugin.groovy    |   74 +-
 .../groovy/org/gradle/api/plugins/BasePlugin.java  |   65 +-
 .../org/gradle/api/plugins/GroovyBasePlugin.java   |   41 +-
 .../org/gradle/api/plugins/GroovyPlugin.java       |   11 +-
 .../org/gradle/api/plugins/JavaBasePlugin.java     |   63 +-
 .../org/gradle/api/plugins/JavaLanguagePlugin.java |  113 -
 .../plugins/JavaLibraryDistributionPlugin.groovy   |    9 +-
 .../groovy/org/gradle/api/plugins/JavaPlugin.java  |   25 +-
 .../gradle/api/plugins/JavaPluginConvention.groovy |   16 +-
 .../api/plugins/LegacyJavaComponentPlugin.java     |  159 +
 .../groovy/org/gradle/api/plugins/WarPlugin.java   |    2 +-
 .../groovy/org/gradle/api/tasks/GroovyRuntime.java |   19 +-
 .../groovy/org/gradle/api/tasks/SourceSet.java     |    2 +-
 .../org/gradle/api/tasks/SourceSetContainer.java   |   25 -
 .../org/gradle/api/tasks/SourceSetOutput.java      |    2 +-
 .../main/groovy/org/gradle/api/tasks/Upload.java   |   16 +-
 .../tasks/application/CreateStartScripts.groovy    |   97 +-
 .../org/gradle/api/tasks/bundling/Jar.groovy       |   92 +-
 .../gradle/api/tasks/compile/AbstractOptions.java  |   89 -
 .../org/gradle/api/tasks/compile/Compile.java      |  215 -
 .../gradle/api/tasks/compile/CompileOptions.java   |  539 --
 .../gradle/api/tasks/compile/GroovyCompile.java    |  139 -
 .../api/tasks/compile/GroovyCompileOptions.java    |  330 --
 .../org/gradle/api/tasks/compile/JavaCompile.java  |   31 -
 .../gradle/api/tasks/javadoc/AntGroovydoc.groovy   |   70 -
 .../org/gradle/api/tasks/javadoc/AntJavadoc.groovy |   51 -
 .../org/gradle/api/tasks/javadoc/Groovydoc.java    |  355 --
 .../org/gradle/api/tasks/javadoc/Javadoc.java      |  306 --
 .../groovy/org/gradle/api/tasks/testing/Test.java  |  211 +-
 .../gradle/api/tasks/testing/TestDescriptor.java   |    3 +
 .../org/gradle/api/tasks/testing/TestLogging.java  |   38 -
 .../org/gradle/api/tasks/testing/TestReport.java   |    1 +
 .../org/gradle/api/tasks/testing/TestResult.java   |    5 +-
 .../api/tasks/testing/logging/TestLogging.java     |    2 +-
 .../testing/logging/TestLoggingContainer.java      |    5 +
 .../api/tasks/testing/testng/TestNGOptions.groovy  |    7 +
 .../external/javadoc/JavadocOfflineLink.java       |   44 -
 .../AbstractListJavadocOptionFileOption.java       |   59 -
 .../javadoc/internal/JavadocOptionFile.java        |  115 -
 .../javadoc/internal/JavadocOptionFileWriter.java  |   59 -
 .../internal/JavadocOptionFileWriterContext.java   |  108 -
 .../org/gradle/jvm/ClassDirectoryBinarySpec.java   |   31 +
 .../jvm/application/tasks/CreateStartScripts.java  |  278 ++
 .../gradle/jvm/application/tasks/package-info.java |   20 +
 .../main/groovy/org/gradle/jvm/package-info.java   |   20 +
 ...DefaultJavaAppStartScriptGenerationDetails.java |  130 +
 .../DefaultTemplateBasedStartScriptGenerator.java  |   95 +
 .../plugins/StartScriptTemplateBindingFactory.java |  184 +
 .../internal/plugins/UnixStartScriptGenerator.java |   31 +
 .../plugins/WindowsStartScriptGenerator.java       |   29 +
 .../JavaAppStartScriptGenerationDetails.java       |   63 +
 .../jvm/application/scripts/ScriptGenerator.java   |   42 +
 .../scripts/TemplateBasedScriptGenerator.java      |   42 +
 .../jvm/application/scripts/package-info.java      |   20 +
 .../gradle-plugins/github-dependencies.properties  |    1 -
 .../META-INF/gradle-plugins/java-lang.properties   |    1 -
 .../META-INF/gradle-plugins/jvm-lang.properties    |    1 -
 .../META-INF/gradle-plugins/lang-base.properties   |    1 -
 ...roperties => org.gradle.application.properties} |    0
 ...{base.properties => org.gradle.base.properties} |    0
 ...operties => org.gradle.distribution.properties} |    0
 ...roperties => org.gradle.groovy-base.properties} |    0
 ...ovy.properties => org.gradle.groovy.properties} |    0
 ....properties => org.gradle.java-base.properties} |    0
 ...rg.gradle.java-library-distribution.properties} |    0
 ...{java.properties => org.gradle.java.properties} |    0
 .../{war.properties => org.gradle.war.properties}  |    0
 .../internal/tasks/testing/junit/report/style.css  |    1 -
 .../plugins/DistributionPluginTest.groovy          |   35 +-
 .../internal/java/DefaultJavaSourceSetTest.groovy  |   29 +
 .../internal/java/DefaultJvmResourceSetTest.groovy |   28 +
 .../ClassDirectoryBinaryNamingSchemeTest.groovy    |   53 +
 .../jvm/DefaultClassDirectoryBinarySpecTest.groovy |   91 +
 .../api/internal/plugins/GroovyJarFileTest.groovy  |    7 +-
 .../plugins/StartScriptGeneratorTest.groovy        |  177 +-
 .../plugins/UnixStartScriptGeneratorTest.groovy    |  141 +
 .../plugins/WindowsStartScriptGeneratorTest.groovy |  117 +
 .../tasks/DefaultGroovySourceSetTest.groovy        |    7 +-
 .../tasks/DefaultSourceSetContainerTest.java       |    2 +-
 .../api/internal/tasks/DefaultSourceSetTest.groovy |    7 +
 .../internal/tasks/compile/ArgWriterTest.groovy    |   77 -
 .../tasks/compile/CleaningJavaCompilerTest.groovy  |   61 -
 ...ndLineJavaCompilerArgumentsGeneratorTest.groovy |   76 -
 .../compile/DefaultJavaCompilerFactoryTest.groovy  |   84 -
 .../compile/DelegatingJavaCompilerTest.groovy      |   33 -
 ...GroovyCompileTransformingClassLoaderTest.groovy |   96 -
 .../InProcessJavaCompilerFactoryTest.groovy        |   39 -
 .../JavaCompilerArgumentsBuilderTest.groovy        |  276 --
 .../compile/NormalizingGroovyCompilerTest.groovy   |   67 -
 .../compile/NormalizingJavaCompilerTest.groovy     |  125 -
 .../compile/SimpleStaleClassCleanerTest.groovy     |   77 -
 .../daemon/CompilerDaemonManagerTest.groovy        |  100 -
 .../incremental/AllFromJarRebuildInfoTest.groovy   |   44 -
 .../incremental/ClassNameProviderTest.groovy       |   42 -
 .../IncrementalCompilationSupportTest.groovy       |   52 -
 .../incremental/InputOutputMapperTest.groovy       |   53 -
 .../incremental/JarSnapshotCacheTest.groovy        |   50 -
 .../incremental/JarSnapshotFeederTest.groovy       |   72 -
 .../compile/incremental/JarSnapshotTest.groovy     |   49 -
 .../compile/incremental/JarSnapshotterTest.groovy  |   52 -
 .../compile/incremental/JavaSourceClassTest.groovy |   38 -
 .../incremental/OutputClassMapperTest.groovy       |   28 -
 .../analyzer/AccessedFromPrivateClass.java         |   24 -
 .../analyzer/ClassDependenciesAnalyzerTest.groovy  |   74 -
 .../analyzer/HasNonPrivateConstants.java           |   21 -
 .../incremental/analyzer/HasPrivateConstants.java  |   22 -
 .../incremental/analyzer/HasPublicConstants.java   |   21 -
 .../compile/incremental/analyzer/SomeClass.java    |   35 -
 .../incremental/analyzer/SomeOtherClass.java       |   24 -
 .../analyzer/UsedByNonPrivateConstantsClass.java   |   20 -
 .../incremental/analyzer/YetAnotherClass.java      |   20 -
 .../graph/ClassDependencyInfoExtractorTest.groovy  |   37 -
 .../graph/ClassDependencyInfoSerializerTest.groovy |   38 -
 .../testing/DefaultTestClassDescriptorTest.groovy  |    2 +-
 .../testing/DefaultTestSuiteDescriptorTest.groovy  |    2 +-
 .../detection/DefaultTestExecuterTest.groovy       |    1 +
 .../junit/JUnitTestClassProcessorData.groovy       |    2 +
 .../junit/JUnitTestClassProcessorTest.groovy       |   17 +-
 .../junit/report/CompositeTestResultsTest.groovy   |   20 +
 .../junit/report/DefaultTestReportTest.groovy      |  195 +-
 .../Binary2JUnitXmlReportGeneratorSpec.groovy      |    1 -
 .../junit/result/JUnitXmlResultWriterSpec.groovy   |    2 +-
 .../logging/DefaultTestLoggingContainerTest.groovy |    2 +-
 .../testing/logging/DefaultTestLoggingTest.groovy  |   19 +-
 .../testing/logging/SimpleTestDescriptor.groovy    |    6 +-
 ...CaptureTestOutputTestResultProcessorTest.groovy |  123 +-
 .../testing/processors/TestMainActionTest.groovy   |   25 +-
 .../processors/TestOutputRedirectorTest.groovy     |   82 +
 .../StateTrackingTestResultProcessorTest.groovy    |  347 ++
 .../testing/results/TestListenerAdapterTest.groovy |  324 --
 .../testng/TestNGListenerAdapterFactorySpec.groovy |   18 +-
 .../testng/TestNGTestClassProcessorTest.groovy     |  212 +-
 .../testing/testng/TestNGTestFrameworkTest.groovy  |    5 +-
 .../testing/worker/TestEventSerializerTest.groovy  |    4 +-
 .../org/gradle/api/plugins/BasePluginTest.groovy   |   26 +-
 .../gradle/api/plugins/GroovyBasePluginTest.groovy |   20 +-
 .../org/gradle/api/plugins/GroovyPluginTest.groovy |   26 +-
 .../gradle/api/plugins/JavaBasePluginTest.groovy   |   80 +-
 .../api/plugins/JavaLanguagePluginTest.groovy      |   50 -
 .../JavaLibraryDistributionPluginTest.groovy       |    4 +-
 .../api/plugins/JavaPluginConventionTest.groovy    |    7 +-
 .../org/gradle/api/plugins/JavaPluginTest.groovy   |   26 +-
 .../api/plugins/JvmLanguagePluginTest.groovy       |   89 -
 .../api/plugins/LanguageBasePluginTest.groovy      |   56 -
 .../plugins/LegacyJavaComponentPluginTest.groovy   |   55 +
 .../org/gradle/api/tasks/GroovyRuntimeTest.groovy  |   38 +-
 .../application/CreateStartScriptsTest.groovy      |   10 +-
 .../org/gradle/api/tasks/bundling/JarTest.groovy   |   59 -
 .../api/tasks/compile/CompileOptionsTest.groovy    |  172 -
 .../tasks/compile/GroovyCompileOptionsTest.groovy  |  101 -
 .../api/tasks/compile/GroovyCompileTest.java       |    2 +-
 .../gradle/api/tasks/compile/JavaCompileTest.java  |   86 -
 .../org/gradle/api/tasks/javadoc/JavadocTest.java  |  153 -
 .../gradle/api/tasks/testing/TestTaskSpec.groovy   |  215 +-
 .../org/gradle/api/tasks/testing/TestTest.java     |  109 +-
 .../tasks/testing/testng/TestNGOptionsTest.groovy  |    2 +
 .../javadoc/StandardJavadocDocletOptionsTest.java  |  523 --
 .../JavadocOptionFileWriterContextTest.java        |   93 -
 subprojects/publish/publish.gradle                 |    2 +-
 .../internal/PublicationFieldValidator.java        |   10 +-
 .../api/publish/internal/PublishOperation.java     |    7 +-
 .../api/publish/internal/PublishServices.java      |    3 +
 .../api/publish/plugins/PublishingPlugin.java      |   64 +-
 ...properties => org.gradle.publishing.properties} |    0
 ...ProjectDependencyPublicationResolverTest.groovy |    2 +-
 .../publish/plugins/PublishingPluginTest.groovy    |    6 +-
 subprojects/reporting/reporting.gradle             |   20 +-
 .../internal/TaskReportContainerIntegTest.groovy   |    7 +-
 .../BuildDashboardPluginIntegrationTest.groovy     |   11 +-
 .../gradle/api/plugins/ReportingBasePlugin.java    |   10 +-
 .../api/plugins/ReportingBasePluginConvention.java |  101 -
 .../api/reporting/BuildDashboardReports.java       |    2 +-
 .../api/reporting/GenerateBuildDashboard.java      |   14 +-
 .../internal/BuildDashboardGenerator.java          |   64 +-
 .../reporting/internal/DefaultReportContainer.java |    1 +
 .../reporting/plugins/BuildDashboardPlugin.java    |   11 +-
 ...rties => org.gradle.build-dashboard.properties} |    0
 .../ReportingBasePluginConventionTest.groovy       |   64 -
 .../api/plugins/ReportingBasePluginTest.groovy     |   11 +-
 .../internal/BuildDashboardGeneratorSpec.groovy    |   57 +-
 .../internal/DefaultReportContainerTest.groovy     |    2 +-
 subprojects/resources-http/resources-http.gradle   |   30 +
 .../http/ApacheDirectoryListingParser.java         |  145 +
 .../transport/http/DefaultHttpSettings.java        |   36 +
 .../transport/http/HttpClientConfigurer.java       |  130 +
 .../resource/transport/http/HttpClientHelper.java  |  129 +
 .../transport/http/HttpConnectorFactory.java       |   42 +
 .../resource/transport/http/HttpProxySettings.java |   42 +
 .../transport/http/HttpRequestException.java       |   31 +
 .../transport/http/HttpResourceAccessor.java       |  116 +
 .../transport/http/HttpResourceLister.java         |   57 +
 .../transport/http/HttpResourceUploader.java       |   49 +
 .../http/HttpResourcesPluginServiceRegistry.java   |   42 +
 .../transport/http/HttpResponseResource.java       |  147 +
 .../resource/transport/http/HttpSettings.java      |   25 +
 .../JavaSystemPropertiesHttpProxySettings.java     |   25 +
 .../http/JavaSystemPropertiesProxySettings.java    |  120 +
 ...avaSystemPropertiesSecureHttpProxySettings.java |   26 +
 .../http/RepeatableInputStreamEntity.java          |   58 +
 .../transport/http/ntlm/NTLMCredentials.java       |   93 +
 .../transport/http/ntlm/NTLMSchemeFactory.java     |   64 +
 ...e.internal.service.scopes.PluginServiceRegistry |    2 +
 .../http/ApacheDirectoryListingParserTest.groovy   |  172 +
 .../transport/http/HttpClientConfigurerTest.groovy |  101 +
 .../transport/http/HttpClientHelperTest.groovy     |   62 +
 .../transport/http/HttpResourceListerTest.groovy   |   46 +
 .../transport/http/HttpResponseResourceTest.groovy |   88 +
 ...avaSystemPropertiesHttpProxySettingsTest.groovy |   29 +
 .../JavaSystemPropertiesProxySettingsTest.groovy   |   95 +
 ...temPropertiesSecureHttpProxySettingsTest.groovy |   29 +
 .../transport/http/ntlm/NTLMCredentialsTest.groovy |   96 +
 .../transport/http/artifactory_dirlisting.html     |    0
 .../transport/http/mavencentral_dirlisting.html    |    0
 .../resource}/transport/http/nexus_dirlisting.html |    0
 subprojects/resources-s3/resources-s3.gradle       |   15 +
 .../s3/AbstractS3DependencyResolutionTest.groovy   |   70 +
 .../resource/s3/S3ClientIntegrationTest.groovy     |  188 +
 .../resource/s3/fixtures/IvyS3Module.groovy        |  157 +
 .../resource/s3/fixtures/IvyS3Repository.groovy    |   72 +
 .../resource/s3/fixtures/MavenS3Module.groovy      |   53 +
 .../resource/s3/fixtures/MavenS3Repository.groovy  |   44 +
 .../resource/s3/fixtures/S3Artifact.groovy         |   36 +
 .../s3/fixtures/S3DirectoryResource.groovy         |  102 +
 .../resource/s3/fixtures/S3Resource.groovy         |  103 +
 .../resource/s3/fixtures/S3Server.groovy           |  496 ++
 .../resource/s3/fixtures/stub/HttpMessage.groovy   |   25 +
 .../resource/s3/fixtures/stub/HttpStub.groovy      |   49 +
 .../resource/s3/fixtures/stub/StubRequest.groovy   |   26 +
 .../resource/s3/fixtures/stub/StubResponse.groovy  |   24 +
 .../s3/ivy/IvyPublishS3IntegrationTest.groovy      |   73 +
 .../s3/ivy/IvyS3RepoResolveIntegrationTest.groovy  |   39 +
 .../ivy/IvyS3UploadArchivesIntegrationTest.groovy  |   36 +
 .../MavenPublishS3ErrorsIntegrationTest.groovy     |   85 +
 .../s3/maven/MavenPublishS3IntegrationTest.groovy  |   80 +
 .../maven/MavenS3ProxiedRepoIntegrationTest.groovy |  124 +
 .../maven/MavenS3RepoErrorsIntegrationTest.groovy  |  123 +
 .../maven/MavenS3RepoResolveIntegrationTest.groovy |  115 +
 .../MavenS3SnapshotRepoIntegrationTest.groovy      |  137 +
 .../resource/transport/aws/s3/S3Client.java        |  199 +
 .../transport/aws/s3/S3ConnectionProperties.java   |  105 +
 .../transport/aws/s3/S3ConnectorFactory.java       |   41 +
 .../transport/aws/s3/S3RegionalResource.java       |   85 +
 .../resource/transport/aws/s3/S3Resource.java      |   71 +
 .../transport/aws/s3/S3ResourceConnector.java      |   82 +
 .../aws/s3/S3ResourcesPluginServiceRegistry.java   |   42 +
 ...e.internal.service.scopes.PluginServiceRegistry |    2 +
 .../resource/transport/aws/s3/S3ClientTest.groovy  |  210 +
 .../aws/s3/S3ConnectionPropertiesTest.groovy       |  105 +
 .../transport/aws/s3/S3ConnectorFactoryTest.groovy |   38 +
 .../transport/aws/s3/S3RegionalResourceTest.groovy |   48 +
 .../aws/s3/S3ResourceConnectorTest.groovy          |   48 +
 subprojects/resources-sftp/resources-sftp.gradle   |   29 +
 .../ivy/IvyPublishSftpIntegrationTest.groovy       |  209 +
 .../ivy/IvySftpLegacyPublishIntegrationTest.groovy |   25 +
 .../maven/MavenPublishSftpIntegrationTest.groovy   |   92 +
 .../AbstractSftpDependencyResolutionTest.groovy    |   43 +
 ...vySftpRepoDynamicRevisionIntegrationTest.groovy |  120 +
 .../ivy/IvySftpRepoErrorsIntegrationTest.groovy    |  217 +
 .../ivy/IvySftpRepoResolveIntegrationTest.groovy   |   34 +
 .../MavenSftpRepoResolveIntegrationTest.groovy     |   59 +
 .../transport/sftp/LockableSftpClient.java         |   25 +
 .../resource/transport/sftp/SftpClientFactory.java |  170 +
 .../transport/sftp/SftpConnectorFactory.java       |   48 +
 .../internal/resource/transport/sftp/SftpHost.java |   92 +
 .../resource/transport/sftp/SftpResource.java      |   68 +
 .../transport/sftp/SftpResourceAccessor.java       |   73 +
 .../transport/sftp/SftpResourceLister.java         |   58 +
 .../transport/sftp/SftpResourceUploader.java       |   82 +
 .../sftp/SftpResourcesPluginServiceRegistry.java   |   47 +
 ...e.internal.service.scopes.PluginServiceRegistry |    2 +
 .../transport/sftp/SftpClientFactoryTest.groovy    |  259 +
 subprojects/resources/resources.gradle             |    4 +
 .../org/gradle/internal/filestore/FileStore.java   |   32 -
 .../internal/filestore/FileStoreSearcher.java      |   27 -
 .../resource/AbstractExternalResource.java         |  112 +
 .../gradle/internal/resource/CachingResource.java  |   45 +
 .../org/gradle/internal/resource/CharsetUtil.java  |   25 +
 .../internal/resource/DelegatingResource.java      |   52 +
 .../gradle/internal/resource/ExternalResource.java |   96 +
 .../internal/resource/ExternalResourceName.java    |  202 +
 .../resource/LocalFileStandInExternalResource.java |   70 +
 .../internal/resource/PasswordCredentials.java     |   41 +
 .../org/gradle/internal/resource/Resource.java     |   69 +
 .../internal/resource/ResourceException.java       |   67 +
 .../resource/ResourceNotFoundException.java        |   32 +
 .../gradle/internal/resource/StringResource.java   |   50 +
 .../org/gradle/internal/resource/UriResource.java  |  204 +
 .../connector/ResourceConnectorFactory.java        |   27 +
 .../connector/ResourceConnectorSpecification.java  |   22 +
 .../AbstractLocallyAvailableResourceFinder.java    |   37 +
 .../resource/local/ByteArrayLocalResource.java     |   38 +
 .../CompositeLocallyAvailableResourceFinder.java   |   69 +
 .../DefaultLocallyAvailableExternalResource.java   |   50 +
 .../internal/resource/local/FileLocalResource.java |   45 +
 .../gradle/internal/resource/local/FileStore.java  |   31 +
 .../internal/resource/local/FileStoreSearcher.java |   25 +
 .../LazyLocallyAvailableResourceCandidates.java    |   58 +
 .../internal/resource/local/LocalResource.java     |   30 +
 .../local/LocallyAvailableExternalResource.java    |   27 +
 .../local/LocallyAvailableResourceCandidates.java  |   30 +
 .../local/LocallyAvailableResourceFinder.java      |   32 +
 ...leResourceFinderSearchableFileStoreAdapter.java |   52 +
 .../metadata/DefaultExternalResourceMetaData.java  |   78 +
 .../metadata/ExternalResourceMetaData.java         |   61 +
 .../metadata/ExternalResourceMetaDataCompare.java  |   66 +
 .../resource/transfer/DefaultExternalResource.java |   64 +
 .../transfer/DefaultExternalResourceConnector.java |   60 +
 .../transfer/ExternalResourceAccessor.java         |   57 +
 .../transfer/ExternalResourceConnector.java        |   20 +
 .../resource/transfer/ExternalResourceLister.java  |   29 +
 .../transfer/ExternalResourceReadResponse.java     |   34 +
 .../transfer/ExternalResourceUploader.java         |   26 +
 .../resource/transfer/UrlExternalResource.java     |   79 +
 .../resource/AbstractExternalResourceTest.groovy   |  164 +
 .../internal/resource/CachingResourceTest.groovy   |   67 +
 .../resource/ExternalResourceNameTest.groovy       |  164 +
 .../internal/resource/ResourceExceptionTest.groovy |   54 +
 .../internal/resource/StringResourceTest.groovy    |   49 +
 .../internal/resource/UriResourceTest.groovy       |  207 +
 .../DefaultExternalResourceMetaDataTest.groovy     |   33 +
 .../ExternalResourceMetaDataCompareTest.groovy     |  172 +
 subprojects/scala/scala.gradle                     |    7 +-
 .../integtests/ScalaProjectIntegrationTest.java    |    5 +
 .../integtests/fixtures/ScalaCoverage.groovy       |   32 +
 .../SamplesMixedJavaAndScalaIntegrationTest.groovy |    8 +
 ...plesScalaCustomizedLayoutIntegrationTest.groovy |    2 +
 .../SamplesScalaQuickstartIntegrationTest.groovy   |    8 +
 .../samples/SamplesScalaZincIntegrationTest.groovy |    3 -
 .../scala/ScalaBasePluginIntegrationTest.groovy    |   52 +-
 .../gradle/scala/ScalaPluginIntegrationTest.groovy |   36 +
 ...ctAntForkingScalaCompilerIntegrationTest.groovy |   41 +
 ...AntInProcessScalaCompilerIntegrationTest.groovy |   43 +
 ...ForkingOlderScalaCompilerIntegrationTest.groovy |   26 +
 .../AntForkingScalaCompilerIntegrationTest.groovy  |   29 +-
 ...ProcessOlderScalaCompilerIntegrationTest.groovy |   26 +
 ...AntInProcessScalaCompilerIntegrationTest.groovy |   27 +-
 .../BasicScalaCompilerIntegrationTest.groovy       |    9 +-
 .../IncrementalScalaCompileIntegrationTest.groovy  |    4 +-
 .../ZincScalaCompilerIntegrationTest.groovy        |   93 +-
 ...tForkingScalaCompilerJdk6IntegrationTest.groovy |   45 -
 ...nProcessScalaCompilerJdk6IntegrationTest.groovy |   43 -
 .../ZincScalaCompilerJdk6IntegrationTest.groovy    |   96 -
 .../JreJavaHomeScalaIntegrationTest.groovy         |   18 +-
 .../scala/test/ScalaTestIntegrationTest.groovy     |   10 +-
 .../recompilesDependentClasses/build.gradle        |    2 +-
 .../build.gradle                                   |    2 +-
 .../build.gradle                                   |    0
 .../prj1/src/main/scala/Person.scala               |    0
 .../prj2/src/main/scala/House.scala                |    0
 .../prj2/src/main/scala/Other.scala                |    0
 .../settings.gradle                                |    0
 .../compilesJavaCodeIncrementally/build.gradle     |    0
 .../src/main/scala/House.java                      |   11 +
 .../src/main/scala/Other.java                      |    0
 .../src/main/scala/Person.java                     |    0
 .../compilesScalaCodeIncrementally/build.gradle    |    0
 .../src/main/scala/House.scala                     |    0
 .../src/main/scala/Other.scala                     |    0
 .../src/main/scala/Person.scala                    |    0
 .../src/main/scala/House.java                      |   13 -
 .../internal/tasks/scala/AntScalaCompiler.groovy   |   44 +-
 .../tasks/scala/CleaningScalaCompiler.java         |   19 +-
 .../internal/tasks/scala/DaemonScalaCompiler.java  |   86 -
 .../tasks/scala/DefaultScalaCompileSpec.java       |   58 -
 .../scala/DefaultScalaJavaJointCompileSpec.java    |   62 -
 .../tasks/scala/DefaultScalaJavaJointCompiler.java |    2 +-
 .../tasks/scala/DelegatingScalaCompiler.java       |   33 -
 .../tasks/scala/NormalizingScalaCompiler.java      |  107 -
 .../api/internal/tasks/scala/ScalaCompileSpec.java |   39 -
 .../scala/ScalaCompilerArgumentsGenerator.java     |   66 -
 .../internal/tasks/scala/ScalaCompilerFactory.java |   58 +-
 .../tasks/scala/ScalaJavaJointCompiler.java        |   26 +
 .../tasks/scala/jdk6/ZincScalaCompiler.java        |  124 -
 .../api/plugins/scala/ScalaBasePlugin.groovy       |   39 +-
 .../gradle/api/plugins/scala/ScalaPlugin.groovy    |    4 +-
 .../org/gradle/api/tasks/ScalaRuntime.groovy       |  159 -
 .../groovy/org/gradle/api/tasks/ScalaRuntime.java  |  151 +
 .../api/tasks/scala/IncrementalCompileOptions.java |   72 -
 .../org/gradle/api/tasks/scala/ScalaCompile.java   |  152 +-
 .../api/tasks/scala/ScalaCompileOptions.java       |  279 +-
 .../org/gradle/api/tasks/scala/ScalaDoc.java       |   16 +-
 .../gradle/api/tasks/scala/ScalaDocOptions.groovy  |  112 -
 .../gradle/api/tasks/scala/ScalaDocOptions.java    |  220 +
 ...properties => org.gradle.scala-base.properties} |    0
 ...cala.properties => org.gradle.scala.properties} |    0
 .../tasks/DefaultScalaSourceSetTest.groovy         |    5 +
 .../scala/DefaultScalaJavaJointCompilerTest.groovy |    7 +-
 .../scala/NormalizingScalaCompilerTest.groovy      |  127 -
 .../ScalaCompilerArgumentsGeneratorTest.groovy     |  105 -
 .../api/plugins/scala/ScalaBasePluginTest.groovy   |   52 +-
 .../org/gradle/api/tasks/ScalaRuntimeTest.groovy   |   15 +-
 .../gradle/api/tasks/scala/ScalaCompileTest.java   |   48 +-
 .../org/gradle/api/tasks/scala/ScalaDocTest.java   |   53 +-
 subprojects/signing/signing.gradle                 |    6 +-
 .../NoSigningCredentialsIntegrationSpec.groovy     |    4 +
 .../plugins/signing/SigningSamplesSpec.groovy      |    5 +-
 .../signing/SigningTasksIntegrationSpec.groovy     |   12 +-
 .../groovy/org/gradle/plugins/signing/Sign.groovy  |    2 +-
 .../gradle/plugins/signing/SignOperation.groovy    |    2 +-
 .../gradle/plugins/signing/SigningExtension.groovy |    2 +-
 .../gradle/plugins/signing/SigningPlugin.groovy    |   10 +-
 .../plugins/signing/SigningPluginConvention.groovy |   78 -
 .../signing/signatory/pgp/PgpSignatory.groovy      |   14 +-
 .../signatory/pgp/PgpSignatoryProvider.groovy      |    2 +-
 ...ng.properties => org.gradle.signing.properties} |    0
 .../plugins/signing/SignOperationSpec.groovy       |    8 +-
 .../signing/SignatoriesConfigurationSpec.groovy    |    6 +-
 .../signing/SigningConfigurationsSpec.groovy       |    2 +-
 .../plugins/signing/SigningProjectSpec.groovy      |    2 +-
 subprojects/sonar/sonar.gradle                     |   12 +-
 .../plugins/sonar/SonarSmokeIntegrationTest.groovy |    8 +-
 .../runner/SonarRunnerSmokeIntegrationTest.groovy  |   82 -
 .../runner/SonarRunnerSmokeIntegrationTest.groovy  |   82 +
 .../org/gradle/sonar/runner/SonarTestServer.groovy |  138 +
 .../SonarSmokeIntegrationTest/shared/build.gradle  |    2 +-
 .../shared/build.gradle                            |   20 -
 .../shared/groovyProject/build.gradle              |   14 -
 .../shared/settings.gradle                         |    3 -
 .../shared/build.gradle                            |   20 +
 .../shared/customizedProject/build.gradle          |    0
 .../gradle/test/customizedProject/Production1.java |    0
 .../org/gradle/test/customizedProject/Test1.java   |    0
 .../shared/emptyJavaProject/build.gradle           |   17 +
 .../shared/gradle.properties                       |    0
 .../shared/groovyProject/build.gradle              |   14 +
 .../test/groovyProject/ProductionGroovy1.groovy    |    0
 .../test/groovyProject/ProductionGroovy10.groovy   |    0
 .../test/groovyProject/ProductionGroovy2.groovy    |    0
 .../test/groovyProject/ProductionGroovy3.groovy    |    0
 .../test/groovyProject/ProductionGroovy4.groovy    |    0
 .../test/groovyProject/ProductionGroovy5.groovy    |    0
 .../test/groovyProject/ProductionGroovy6.groovy    |    0
 .../test/groovyProject/ProductionGroovy7.groovy    |    0
 .../test/groovyProject/ProductionGroovy8.groovy    |    0
 .../test/groovyProject/ProductionGroovy9.groovy    |    0
 .../gradle/test/groovyProject/TestGroovy1.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy10.groovy  |    0
 .../gradle/test/groovyProject/TestGroovy2.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy3.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy4.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy5.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy6.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy7.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy8.groovy   |    0
 .../gradle/test/groovyProject/TestGroovy9.groovy   |    0
 .../shared/javaProjectWithJacoco/build.gradle      |    0
 .../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
 .../javaProjectWithSkippedTestTask/build.gradle    |   21 +
 .../src/test/java/Test1.java                       |   25 +
 .../javaProjectWithoutTestClasses/build.gradle     |    1 +
 .../src/main/java/Production1.java                 |   12 +
 .../nested/nested2/nestedProject/build.gradle      |    0
 .../org/gradle/test/nestedProject/Production1.java |    0
 .../java/org/gradle/test/nestedProject/Test1.java  |    0
 .../shared/settings.gradle                         |    3 +
 .../shared/skippedProject/build.gradle             |    0
 .../gradle/test/skippedProject/Production1.java    |    0
 .../java/org/gradle/test/skippedProject/Test1.java |    0
 .../gradle/api/plugins/sonar/SonarAnalyze.groovy   |   47 +-
 .../gradle/api/sonar/runner/SonarProperties.groovy |   56 -
 .../org/gradle/api/sonar/runner/SonarRunner.groovy |   54 -
 .../api/sonar/runner/SonarRunnerExtension.groovy   |   78 -
 .../api/sonar/runner/SonarRunnerPlugin.groovy      |  243 -
 .../org/gradle/sonar/runner/SonarProperties.java   |   69 +
 .../gradle/sonar/runner/SonarRunnerExtension.java  |  100 +
 .../sonar/runner/SonarRunnerRootExtension.java     |   91 +
 .../org/gradle/sonar/runner/package-info.java      |   22 +
 .../sonar/runner/plugins/SonarRunnerPlugin.java    |  350 ++
 .../gradle/sonar/runner/plugins/package-info.java  |   22 +
 .../org/gradle/sonar/runner/tasks/SonarRunner.java |  126 +
 .../gradle/sonar/runner/tasks/package-info.java    |   22 +
 .../org.gradle.sonar-runner.properties             |    1 +
 ...onar.properties => org.gradle.sonar.properties} |    0
 .../gradle-plugins/sonar-runner.properties         |    1 -
 subprojects/sonar/src/main/resources/logback.xml   |   28 +
 .../api/plugins/sonar/SonarPluginTest.groovy       |   10 +-
 .../api/sonar/runner/SonarPropertiesTest.groovy    |   48 -
 .../sonar/runner/SonarRunnerExtensionTest.groovy   |   43 -
 .../api/sonar/runner/SonarRunnerPluginTest.groovy  |  377 --
 .../gradle/sonar/runner/SonarPropertiesTest.groovy |   48 +
 .../sonar/runner/SonarRunnerExtensionTest.groovy   |   46 +
 .../runner/plugins/SonarRunnerPluginTest.groovy    |  445 ++
 .../test/cunit/CUnitIntegrationTest.groovy         |  419 ++
 .../test/cunit/CUnitSamplesIntegrationTest.groovy  |   95 +
 .../test/cunit/CUnitTestResults.groovy             |  105 +
 .../cunit/ComponentReportIntegrationTest.groovy    |   83 +
 .../plugins/CUnitPluginIntegrationTest.groovy      |   26 +
 .../ComponentReportIntegrationTest.groovy          |   81 +
 .../googletest/GoogleTestIntegrationTest.groovy    |  409 ++
 .../GoogleTestSamplesIntegrationTest.groovy        |   79 +
 .../test/googletest/GoogleTestTestResults.groovy   |   75 +
 .../plugins/GoogleTestPluginIntegrationTest.groovy |   26 +
 .../test/cunit/CUnitTestSuiteBinarySpec.java       |   38 +
 .../test/cunit/CUnitTestSuiteSpec.java             |   26 +
 .../internal/DefaultCUnitTestSuiteBinary.java      |   50 +
 .../cunit/internal/DefaultCUnitTestSuiteSpec.java  |   37 +
 .../nativeplatform/test/cunit/package-info.java    |   20 +
 .../test/cunit/plugins/CUnitPlugin.java            |  185 +
 .../test/cunit/plugins/package-info.java           |   20 +
 .../test/cunit/tasks/GenerateCUnitLauncher.java    |   61 +
 .../test/cunit/tasks/package-info.java             |   20 +
 .../googletest/GoogleTestTestSuiteBinarySpec.java  |   38 +
 .../test/googletest/GoogleTestTestSuiteSpec.java   |   27 +
 .../internal/DefaultGoogleTestTestSuiteBinary.java |   50 +
 .../internal/DefaultGoogleTestTestSuiteSpec.java   |   37 +
 .../test/googletest/package-info.java              |   20 +
 .../test/googletest/plugins/GoogleTestPlugin.java  |  159 +
 .../test/googletest/plugins/package-info.java      |   20 +
 .../gradle-plugins/org.gradle.cunit.properties     |   17 +
 .../org.gradle.google-test.properties              |   17 +
 .../test/cunit/tasks/gradle_cunit_main.c           |    0
 .../test/cunit/tasks/gradle_cunit_register.h       |    0
 .../nativeplatform/test/cunit/CUnitTest.groovy     |   44 +
 .../test/googletest/GoogleTestTest.groovy          |   44 +
 subprojects/testing-native/testing-native.gradle   |   32 +
 .../provider/runner/BuildModelActionRunner.java    |   90 +
 .../runner/ClientForwardingTestListener.java       |  107 +
 .../runner/ClientProvidedBuildActionRunner.java    |   68 +
 .../provider/runner/DefaultBuildController.java    |   76 +
 .../provider/runner/ToolingBuilderServices.java    |   46 +
 ...e.internal.service.scopes.PluginServiceRegistry |    1 +
 .../ClientProvidedBuildActionRunnerTest.groovy     |  114 +
 .../runner/DefaultBuildControllerTest.groovy       |  148 +
 .../tooling-api-builders.gradle                    |    8 +
 .../tooling/AutoTestedSamplesToolingApiTest.groovy |    6 +-
 .../ConcurrentToolingApiIntegrationSpec.groovy     |   32 +-
 .../SamplesToolingApiIntegrationTest.groovy        |    1 +
 .../ToolingApiClasspathIntegrationTest.groovy      |    2 +-
 .../tooling/ToolingApiIntegrationTest.groovy       |   20 +-
 .../tooling/ToolingApiRemoteIntegrationTest.groovy |  102 +-
 .../fixture/ExternalToolingApiDistribution.groovy  |    3 +-
 .../tooling/fixture/TargetGradleVersion.java       |    2 +-
 .../integtests/tooling/fixture/TextUtil.java       |    2 +-
 .../integtests/tooling/fixture/ToolingApi.groovy   |  100 +-
 .../ToolingApiCompatibilitySuiteRunner.groovy      |    3 +
 .../fixture/ToolingApiDistributionResolver.groovy  |    6 +-
 .../tooling/fixture/ToolingApiSpecification.groovy |  117 +-
 .../tooling/fixture/ToolingApiVersion.java         |    2 +-
 .../ToolingApiEclipseModelCrossVersionSpec.groovy  |    7 +-
 .../m3/ToolingApiLoggingCrossVersionSpec.groovy    |   78 +-
 ...piEclipseLinkedResourcesCrossVersionSpec.groovy |    4 -
 ...ngApiEclipseMinimalModelCrossVersionSpec.groovy |    2 -
 ...EclipseModelWithFlatRepoCrossVersionSpec.groovy |    2 -
 ...ToolingApiBuildExecutionCrossVersionSpec.groovy |    7 +-
 ...ildableEclipseModelFixesCrossVersionSpec.groovy |    8 +-
 .../ToolingApiEclipseModelCrossVersionSpec.groovy  |    2 -
 .../ToolingApiGradleProjectCrossVersionSpec.groovy |    2 -
 ...orsProjectCustomizationsCrossVersionSpec.groovy |    9 +-
 .../m5/ToolingApiIdeaModelCrossVersionSpec.groovy  |    4 -
 .../m5/ToolingApiModelCrossVersionSpec.groovy      |    4 -
 ...ReceivingStandardStreamsCrossVersionSpec.groovy |    6 +-
 ...UnsupportedModelFeedbackCrossVersionSpec.groovy |   38 -
 .../BuildEnvironmentModelCrossVersionSpec.groovy   |   33 +-
 .../ConsumingStandardInputCrossVersionSpec.groovy  |    6 +-
 .../m8/JavaConfigurabilityCrossVersionSpec.groovy  |    6 +-
 ...rictLongRunningOperationCrossVersionSpec.groovy |   96 -
 .../ToolingApiEclipseModelCrossVersionSpec.groovy  |    4 -
 .../m8/ToolingApiLoggingCrossVersionSpec.groovy    |   32 +-
 ...sionOnlyBuildEnvironmentCrossVersionSpec.groovy |   53 -
 .../m9/DaemonErrorFeedbackCrossVersionSpec.groovy  |    4 +-
 ...adlePropertiesToolingApiCrossVersionSpec.groovy |    8 +-
 .../M9JavaConfigurabilityCrossVersionSpec.groovy   |   26 +-
 ...singCommandLineArgumentsCrossVersionSpec.groovy |    4 +-
 .../r112/BuildInvocationsCrossVersionSpec.groovy   |  135 +-
 .../r112/PublicationsCrossVersionSpec.groovy       |   22 +-
 .../r112/TaskDisplayNameCrossVersionSpec.groovy    |    2 -
 .../r112/TestFilteringCrossVersionSpec.groovy      |    4 +-
 .../ToolingApiDeprecationsCrossVersionSpec.groovy  |   94 +-
 .../r112/UserHomeDirCrossVersionSpec.groovy        |    1 -
 .../DependencyMetaDataCrossVersionSpec.groovy      |    2 -
 .../r12rc1/BuildModelCrossVersionSpec.groovy       |    2 -
 .../ProjectOutcomesModuleCrossVersionSpec.groovy   |    2 -
 ...pportedOperationFeedbackCrossVersionSpec.groovy |    4 +-
 ...ApiInitScriptCrossVersionIntegrationTest.groovy |   12 +-
 ...ningCommandLineArgumentsCrossVersionSpec.groovy |    2 -
 ...ApiConfigurationOnDemandCrossVersionSpec.groovy |    2 -
 ...knownCustomModelFeedbackCrossVersionSpec.groovy |    2 +-
 .../tooling/r18/BuildActionCrossVersionSpec.groovy |   36 +-
 .../r18/BuildScriptModelCrossVersionSpec.groovy    |   23 +-
 .../integtests/tooling/r18/CounterAction.java      |   28 +
 .../r18/GradleBuildModelCrossVersionSpec.groovy    |    2 +-
 .../integtests/tooling/r20/BrokenAction.java       |   26 +
 .../r20/GradleProjectModelCrossVersionSpec.groovy  |   42 +
 ...ngApiUnsupportedBuildJvmCrossVersionSpec.groovy |   73 +
 ...gApiUnsupportedClientJvmCrossVersionSpec.groovy |  109 +
 .../r21/CancellationCrossVersionSpec.groovy        |  131 +
 .../integtests/tooling/r21/HangingBuildAction.java |   28 +
 .../r21/PreCancellationCrossVersionSpec.groovy     |   65 +
 .../r21/R21CancellationCrossVersionSpec.groovy     |  264 +
 .../r21/TaskVisibilityCrossVersionSpec.groovy      |  100 +
 .../tooling/r22/BuildActionCrossVersionSpec.groovy |  100 +
 .../r22/CancellationCrossVersionSpec.groovy        |  386 ++
 .../r22/ClientShutdownCrossVersionSpec.groovy      |  137 +
 .../tooling/r22/Idea13ModelCrossVersionSpec.groovy |   83 +
 .../r23/GradleProjectCrossVersionSpec.groovy       |   50 +
 .../ImplicitTasksToolingApiCrossVersionSpec.groovy |   38 +
 .../r23/ModelBuilderCrossVersionSpec.groovy        |   78 +
 .../r23/StandardStreamsCrossVersionSpec.groovy     |  174 +
 .../r24/BuildEnvironmentCrossVersionSpec.groovy    |   46 +
 .../r24/BuildInvocationsCrossVersionSpec.groovy    |   67 +
 .../DaemonUsageSuggestionCrossVersionTest.groovy   |   47 +
 .../r24/GradleProjectCrossVersionSpec.groovy       |   44 +
 .../ModelTasksToolingApiCrossVersionTest.groovy    |   60 +
 .../r24/TestProgressCrossVersionSpec.groovy        |  744 +++
 ...TestProgressDaemonErrorsCrossVersionSpec.groovy |   75 +
 .../org/gradle/tooling/BuildActionExecuter.java    |   78 +-
 .../gradle/tooling/BuildCancelledException.java    |   35 +
 .../java/org/gradle/tooling/BuildLauncher.java     |  116 +-
 .../java/org/gradle/tooling/CancellationToken.java |   35 +
 .../gradle/tooling/CancellationTokenSource.java    |   57 +
 .../src/main/java/org/gradle/tooling/Failure.java  |   55 +
 .../java/org/gradle/tooling/GradleConnector.java   |   32 +-
 .../org/gradle/tooling/LongRunningOperation.java   |   37 +-
 .../main/java/org/gradle/tooling/ModelBuilder.java |   36 +-
 .../java/org/gradle/tooling/ResultHandler.java     |    6 +-
 .../org/gradle/tooling/events/FailureResult.java   |   39 +
 .../org/gradle/tooling/events/FinishEvent.java     |   36 +
 .../gradle/tooling/events/OperationDescriptor.java |   53 +
 .../org/gradle/tooling/events/OperationResult.java |   43 +
 .../org/gradle/tooling/events/ProgressEvent.java   |   52 +
 .../org/gradle/tooling/events/SkippedResult.java   |   28 +
 .../java/org/gradle/tooling/events/StartEvent.java |   28 +
 .../org/gradle/tooling/events/SuccessResult.java   |   28 +
 .../tooling/events/internal/BaseFinishEvent.java   |   40 +
 .../tooling/events/internal/BaseProgressEvent.java |   57 +
 .../tooling/events/internal/BaseStartEvent.java    |   31 +
 .../org/gradle/tooling/events/package-info.java    |   20 +
 .../gradle/tooling/events/test/JvmTestKind.java    |   47 +
 .../events/test/JvmTestOperationDescriptor.java    |   62 +
 .../tooling/events/test/TestFailureResult.java     |   41 +
 .../tooling/events/test/TestFinishEvent.java       |   46 +
 .../events/test/TestOperationDescriptor.java       |   29 +
 .../tooling/events/test/TestOperationResult.java   |   29 +
 .../tooling/events/test/TestProgressEvent.java     |   38 +
 .../tooling/events/test/TestProgressListener.java  |   46 +
 .../tooling/events/test/TestSkippedResult.java     |   29 +
 .../gradle/tooling/events/test/TestStartEvent.java |   29 +
 .../tooling/events/test/TestSuccessResult.java     |   29 +
 .../test/internal/DefaultTestFailureResult.java    |   54 +
 .../test/internal/DefaultTestFinishEvent.java      |   43 +
 .../test/internal/DefaultTestSkippedResult.java    |   44 +
 .../test/internal/DefaultTestStartEvent.java       |   37 +
 .../test/internal/DefaultTestSuccessResult.java    |   44 +
 .../gradle/tooling/events/test/package-info.java   |   20 +
 ...UnsupportedOperationConfigurationException.java |    2 +-
 .../internal/adapter/ProtocolToModelAdapter.java   |    7 +-
 .../internal/build/DefaultBuildEnvironment.java    |   25 +-
 .../build/VersionOnlyBuildEnvironment.java         |   18 +-
 .../consumer/AbstractLongRunningOperation.java     |   22 +-
 .../consumer/CancellationTokenInternal.java        |   23 +
 .../internal/consumer/ConnectionFactory.java       |   18 +-
 .../internal/consumer/ConnectionParameters.java    |    5 +
 .../internal/consumer/ConnectorServices.java       |   62 +-
 .../consumer/DefaultBuildActionExecuter.java       |    5 +-
 .../internal/consumer/DefaultBuildLauncher.java    |   47 +-
 .../consumer/DefaultCancellationTokenSource.java   |   54 +
 .../consumer/DefaultConnectionParameters.java      |   16 +-
 .../consumer/DefaultExecutorServiceFactory.java    |   26 +
 .../tooling/internal/consumer/DefaultFailure.java  |   50 +
 .../internal/consumer/DefaultGradleConnector.java  |   30 +-
 .../internal/consumer/DefaultModelBuilder.java     |    7 +-
 .../tooling/internal/consumer/Distribution.java    |    4 +-
 .../internal/consumer/DistributionFactory.java     |   82 +-
 .../internal/consumer/ExecutorServiceFactory.java  |   25 +
 .../tooling/internal/consumer/LoggingProvider.java |    2 +-
 .../internal/consumer/ResultHandlerAdapter.java    |    4 +
 .../internal/consumer/SynchronizedLogging.java     |   60 +-
 .../connection/AbstractConsumerConnection.java     |   14 +-
 .../consumer/connection/AbstractModelProducer.java |   33 -
 .../AbstractPre12ConsumerConnection.java           |   57 -
 .../connection/ActionAwareConsumerConnection.java  |   95 +-
 .../internal/consumer/connection/ActionRunner.java |   24 +
 .../BuildActionRunnerBackedConsumerConnection.java |   32 +-
 .../BuildInvocationsAdapterProducer.java           |   17 +-
 .../connection/CancellableConsumerConnection.java  |  115 +
 ...CancellableModelBuilderBackedModelProducer.java |   66 +
 ...ConnectionVersion4BackedConsumerConnection.java |  162 +-
 .../connection/ConsumerActionExecutor.java         |    8 +-
 .../consumer/connection/ConsumerConnection.java    |   13 +-
 .../connection/GradleBuildAdapterProducer.java     |   11 +-
 .../connection/InternalBuildActionAdapter.java     |   42 +
 ...InternalConnectionBackedConsumerConnection.java |   65 +-
 .../connection/LazyConsumerActionExecutor.java     |   17 +-
 .../LoggingInitializerConsumerActionExecutor.java  |   46 -
 .../ModelBuilderBackedConsumerConnection.java      |   72 +-
 .../ModelBuilderBackedModelProducer.java           |    9 +-
 .../connection/NoToolingApiConnection.java         |    5 +
 .../NonCancellableConsumerConnectionAdapter.java   |   73 +
 .../ProgressLoggingConsumerActionExecutor.java     |    2 +-
 .../ShutdownAwareConsumerConnection.java           |   37 +
 .../connection/UnsupportedActionRunner.java        |   34 +
 .../converters/BuildInvocationsConverter.java      |   64 +-
 .../ConsumerProvidedBuildInvocations.java          |   43 +
 .../converters/GradleProjectConverter.java         |   63 -
 .../converters/GradleProjectMixInHandler.java      |   30 -
 .../converters/PropertyHandlerFactory.java         |   44 -
 .../loader/CachingToolingImplementationLoader.java |   19 +-
 .../loader/DefaultToolingImplementationLoader.java |   20 +-
 .../SynchronizedToolingImplementationLoader.java   |   23 +-
 .../loader/ToolingImplementationLoader.java        |    4 +-
 .../parameters/BuildCancellationTokenAdapter.java  |   40 +
 .../parameters/BuildProgressListenerAdapter.java   |  242 +
 .../parameters/ConsumerOperationParameters.java    |  108 +-
 .../parameters/ProgressListenerAdapter.java        |   11 +-
 .../consumer/versioning/VersionDetails.java        |    4 +-
 .../internal/gradle/BasicGradleTaskSelector.java   |   76 -
 .../internal/gradle/ConsumerProvidedTask.java      |   84 +
 .../gradle/ConsumerProvidedTaskSelector.java       |   75 +
 .../internal/gradle/DefaultBuildInvocations.java   |   46 -
 .../gradle/DefaultConvertedGradleProject.java      |   58 -
 .../internal/gradle/DefaultGradleProject.java      |   26 +-
 .../internal/gradle/DefaultGradleProjectTask.java  |   30 -
 .../tooling/internal/gradle/DefaultGradleTask.java |   81 -
 .../internal/gradle/PartialGradleProject.java      |    3 +
 .../internal/protocol/BuildActionRunner.java       |    6 +-
 .../protocol/BuildableProjectVersion1.java         |    2 +-
 .../internal/protocol/ConnectionVersion4.java      |   18 +-
 .../protocol/HierarchicalProjectVersion1.java      |    2 +-
 .../protocol/InternalBasicIdeaProject.java         |    2 +-
 .../protocol/InternalBuildActionExecutor.java      |    8 +-
 .../protocol/InternalBuildCancelledException.java  |   30 +
 .../protocol/InternalBuildProgressListener.java    |   50 +
 .../protocol/InternalCancellableConnection.java    |   78 +
 .../protocol/InternalCancellationToken.java        |   41 +
 .../internal/protocol/InternalConnection.java      |   10 +-
 .../tooling/internal/protocol/InternalFailure.java |   48 +
 .../internal/protocol/InternalGradleProject.java   |    2 +-
 .../internal/protocol/InternalIdeaProject.java     |    2 +-
 .../tooling/internal/protocol/ModelBuilder.java    |    8 +-
 .../internal/protocol/ShutdownParameters.java      |   27 +
 .../internal/protocol/StoppableConnection.java     |   39 +
 .../protocol/eclipse/EclipseProjectVersion3.java   |    3 +-
 .../protocol/events/InternalJvmTestDescriptor.java |   56 +
 .../protocol/events/InternalTestDescriptor.java    |   54 +
 .../protocol/events/InternalTestFailureResult.java |   25 +
 .../events/InternalTestFinishedProgressEvent.java  |   31 +
 .../protocol/events/InternalTestProgressEvent.java |   44 +
 .../protocol/events/InternalTestResult.java        |   51 +
 .../protocol/events/InternalTestSkippedResult.java |   27 +
 .../events/InternalTestStartedProgressEvent.java   |   23 +
 .../protocol/events/InternalTestSuccessResult.java |   25 +
 .../org/gradle/tooling/model/GradleProject.java    |   21 +
 .../java/org/gradle/tooling/model/Launchable.java  |    9 +
 .../main/java/org/gradle/tooling/model/Task.java   |    5 +-
 .../tooling/model/build/GradleEnvironment.java     |   15 +-
 .../gradle/tooling/model/eclipse/EclipseTask.java  |   32 -
 .../model/eclipse/HierarchicalEclipseProject.java  |    6 +-
 .../tooling/model/gradle/BasicGradleProject.java   |    1 -
 .../tooling/model/gradle/BuildInvocations.java     |    8 +-
 .../gradle/tooling/model/gradle/GradleBuild.java   |    3 +-
 .../tooling/model/gradle/ProjectPublications.java  |    3 +-
 .../gradle/tooling/model/idea/IdeaContentRoot.java |   25 +-
 .../tooling/model/idea/IdeaSourceDirectory.java    |   12 +-
 .../tooling/fixture/GradleVersionSpecTest.groovy   |   18 +
 .../adapter/ProtocolToModelAdapterTest.groovy      |   12 +
 .../internal/consumer/ConnectionFactoryTest.groovy |   48 -
 .../internal/consumer/ConnectorServicesTest.groovy |   13 +-
 .../consumer/DefaultBuildActionExecuterTest.groovy |    4 +-
 .../consumer/DefaultBuildLauncherTest.groovy       |   36 +-
 .../DefaultCancellationTokenSourceTest.groovy      |   48 +
 .../consumer/DefaultModelBuilderTest.groovy        |    1 +
 .../consumer/DistributionFactoryTest.groovy        |   43 +-
 .../consumer/SynchronizedLoggingTest.groovy        |   45 +-
 .../ActionAwareConsumerConnectionTest.groovy       |   74 +-
 ...ActionRunnerBackedConsumerConnectionTest.groovy |    6 +-
 .../CancellableConsumerConnectionTest.groovy       |  168 +
 ...lableModelBuilderBackedModelProducerTest.groovy |   75 +
 ...tionVersion4BackedConsumerConnectionTest.groovy |  258 +-
 .../GradleBuildAdapterProducerTest.groovy          |   18 +-
 ...alConnectionBackedConsumerConnectionTest.groovy |    5 +-
 .../LazyConsumerActionExecutorTest.groovy          |   47 +-
 ...ModelBuilderBackedConsumerConnectionTest.groovy |   66 +-
 ...CancellableConsumerConnectionAdapterTest.groovy |   90 +
 ...rogressLoggingConsumerActionExecutorTest.groovy |    2 +-
 .../BuildInvocationsConverterTest.groovy           |   15 +-
 .../CachingToolingImplementationLoaderTest.groovy  |   74 +-
 .../DefaultToolingImplementationLoaderTest.groovy  |  115 +-
 ...chronizedToolingImplementationLoaderTest.groovy |   78 +-
 .../BuildProgressListenerAdapterTest.groovy        |  533 ++
 .../ConsumerOperationParametersTest.groovy         |   59 +-
 .../parameters/ProgressListenerAdapterTest.groovy  |    7 +-
 .../tooling/fixture/GradleVersionSpec.java         |    7 +
 .../tooling/fixture/TestOutputStream.groovy        |   35 +
 .../tooling/fixture/TestResultHandler.groovy       |   47 +
 subprojects/tooling-api/tooling-api.gradle         |   22 +-
 .../gradle/integtests/BlockingRequestObserver.java |  136 +
 ...ExtraTestCommandLineOptionsListenerWrapper.java |   32 +
 .../integtests/FavoritesIntegrationTest.java       |    9 +-
 .../integtests/LiveOutputIntegrationTest.groovy    |  105 +-
 .../ModelTasksGradleUIIntegrationTest.groovy       |   70 +
 ...projectProjectAndTaskListIntegrationTest.groovy |   10 +-
 .../org/gradle/integtests/OpenApiFixture.java      |  137 +
 .../org/gradle/integtests/OpenApiUiTest.groovy     |  933 ++++
 .../org/gradle/integtests/OutputUILordTest.groovy  |  127 +
 .../TestAlternateUIInteractionVersion1.java        |   52 +
 .../integtests/TestExecutionInteraction.groovy     |   92 +
 .../integtests/TestSettingsNodeVersion1.java       |  245 +
 .../TestSingleDualPaneUIInteractionVersion1.java   |   43 +
 .../org/gradle/foundation/ProjectConverter.java    |   56 +-
 .../java/org/gradle/foundation/ProjectView.java    |   17 -
 .../gradle/foundation/ipc/basic/ClientProcess.java |   27 +-
 .../ipc/gradle/AbstractGradleServerProtocol.java   |    2 +-
 .../gradle/ExecuteGradleCommandServerProtocol.java |    2 +-
 .../gradle/foundation/ipc/gradle/GradleClient.java |   28 +-
 .../ipc/gradle/KillGradleServerProtocol.java       |    4 -
 .../ipc/gradle/TaskListClientProtocol.java         |    4 +-
 .../ipc/gradle/TaskListServerProtocol.java         |    2 +-
 .../gradle/foundation/queue/ExecutionQueue.java    |    5 +-
 .../gradleplugin/foundation/GradlePluginLord.java  |   13 +-
 .../foundation/request/ExecutionRequest.java       |    2 +-
 .../foundation/runner/GradleRunner.java            |   73 -
 .../swing/common/PreferencesAssistant.java         |    2 +-
 .../swing/generic/DualPaneUIInstance.java          |   57 -
 .../userinterface/swing/generic/OutputPanel.java   |    2 +-
 .../generic/SwingEditFavoriteInteraction.java      |    2 +-
 .../userinterface/swing/generic/tabs/SetupTab.java |    4 +-
 .../swing/standalone/Application.java              |   21 +-
 .../swing/standalone/BlockingApplication.java      |   56 +-
 .../wrappers/NoLongerSupportedException.java       |   23 +
 .../openapi/wrappers/RunnerWrapperFactory.java     |   59 -
 .../gradle/openapi/wrappers/UIWrapperFactory.java  |   57 -
 .../wrappers/foundation/ProjectWrapper.java        |    3 +-
 .../runner/GradleRunnerInteractionWrapper.java     |  123 -
 .../wrappers/runner/GradleRunnerWrapper.java       |   32 +-
 .../openapi/wrappers/ui/DualPaneUIWrapper.java     |   41 +-
 .../openapi/wrappers/ui/SinglePaneUIWrapper.java   |   17 +
 .../org/gradle/foundation/BuildInformation.java    |    5 +-
 .../org/gradle/foundation/FavoritesTest.java       |   11 +-
 .../org/gradle/foundation/FileLinkTests.java       |    2 +-
 .../groovy/org/gradle/foundation/FilterTest.java   |    9 +-
 .../gradle/foundation/ProjectConverterTest.groovy  |    7 +-
 .../groovy/org/gradle/foundation/TestUtility.java  |   48 +-
 .../WrapperConcurrentDownloadTest.groovy           |    2 +-
 .../WrapperCrossVersionIntegrationTest.groovy      |   40 +-
 .../WrapperGenerationIntegrationTest.groovy        |   28 +-
 .../WrapperLoggingIntegrationTest.groovy           |   51 +
 .../WrapperProjectIntegrationTest.groovy           |    2 +-
 .../WrapperUserHomeIntegrationTest.groovy          |    2 +-
 .../src/main/java/org/gradle/wrapper/Download.java |   12 +-
 .../gradle/wrapper/ExclusiveFileAccessManager.java |    8 +-
 .../java/org/gradle/wrapper/GradleWrapperMain.java |   18 +-
 .../src/main/java/org/gradle/wrapper/Install.java  |   16 +-
 .../src/main/java/org/gradle/wrapper/Logger.java   |   52 +
 .../java/org/gradle/wrapper/PathAssembler.java     |   16 +-
 .../groovy/org/gradle/wrapper/DownloadTest.groovy  |    2 +-
 .../groovy/org/gradle/wrapper/InstallTest.groovy   |    2 +-
 .../org/gradle/wrapper/PathAssemblerTest.java      |    9 +-
 version.txt                                        |    2 +-
 7926 files changed, 360164 insertions(+), 189602 deletions(-)

diff --git a/build.gradle b/build.gradle
index 4de6d0f..e27d571 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,6 @@
 
 import org.gradle.build.Install
 import org.gradle.build.BuildTypes
-import org.gradle.build.TestReportAggregator
 
 defaultTasks 'assemble'
 apply plugin: 'java-base'
@@ -28,23 +27,23 @@ buildTypes {
     sanityCheck "classes", "doc:checkstyleApi", "codeQuality", "docs:check"
 
     // The minimum to be run before check-in
-    preCommitBuild "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test"
-    quickCheck "doc:checkstyleApi", "docs:check", "codeQuality", "classes", "test"
+    preCommitBuild "doc:checkstyleApi", "docs:check", "codeQuality", "test"
+    quickCheck "doc:checkstyleApi", "docs:check", "codeQuality", "test"
 
     // For testing pull requests
-    pullRequestValidation "doc:checkstyleApi", "docs:check", "codeQuality", "classes"
+    pullRequestValidation "doc:checkstyleApi", "docs:check", "codeQuality"
 
     // A full (in-process) test
     developerBuild "check"
 
-    // Used by the first phase of the build pipeline
-    quickTest "runtimeTests", "runtimeIntegTests"
+    // Used by the first phase of the build pipeline, running only last version on multiversion - tests
+    quickTest "runtimeTests", "runtimeIntegTests", "performance:test"
 
     // Used for builds to run all tests, but not necessarily on all platforms
-    fullTest "runtimeTests", "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "forking"
+    fullTest "runtimeTests", "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "forking", testAllVersions: true
 
     // Used for builds to test the code on certain platforms
-    platformTest "runtimeTests", "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "forking", testAllPlatforms: true
+    platformTest "runtimeTests", "runtimeIntegTests", "performance:test", useIncomingDistributions: true, defaultIntegTestExecuter: "forking", testAllVersions: true, testAllPlatforms: true
 
     // Tests using the daemon mode
     daemonTest "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "daemon"
@@ -53,10 +52,10 @@ buildTypes {
     parallelTest "runtimeIntegTests", useIncomingDistributions: true, defaultIntegTestExecuter: "parallel"
 
     // Run the performance tests
-    performanceTest "performance:integTest", useIncomingDistributions: true
+    performanceTest "performance:performanceTest", useIncomingDistributions: true
 
-    // Run the performance tests
-    localPerformanceTest "performance:integTest"
+    // Run the performance experiments
+    performanceExperiments "performance:performanceExperiments", useIncomingDistributions: true
 
     // Used for cross version tests on CI
     crossVersionTest "crossVersionIntegTest", useIncomingDistributions: true
@@ -86,17 +85,29 @@ ext {
         ext.useIncomingDistributions = false
     }
 
-    internalProjects = subprojects.findAll { it.name.startsWith("internal") || it.name in ["integTest", "distributions"] }
+    internalProjects = subprojects.findAll { it.name.startsWith("internal") || it.name in ["integTest", "distributions", "performance"] }
     groovyProjects = subprojects
     publicGroovyProjects = groovyProjects - internalProjects
-    publishedProjects = [project(':core'), project(':toolingApi'), project(':wrapper'), project(':baseServices'), project(':messaging')]
     pluginProjects = [
         'plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven',
-        'ide', 'announce', 'scala', 'sonar', 'signing', 'cpp', 'ear', 'javascript', 'buildComparison',
-        'diagnostics', 'reporting', 'publish', 'ivy', 'jacoco', 'buildInit', 'languageJvm', 'languageBase'
+        'ide', 'announce', 'scala', 'sonar', 'signing', 'ear', 'javascript', 'buildComparison',
+        'diagnostics', 'reporting', 'publish', 'ivy', 'jacoco', 'buildInit', 'platformBase',
+        'platformJvm', 'languageJvm', 'languageJava', 'languageGroovy', 'languageScala',
+        'platformNative', 'platformPlay', 'languageNative', 'ideNative', 'testingNative',
+        'pluginDevelopment', 'pluginUse', 'resourcesHttp', 'resourcesSftp', 'resourcesS3',
+        'toolingApiBuilders'
     ].collect {
         project(it)
     }
+    publishedProjects = [
+            project(':core'),
+            project(':toolingApi'),
+            project(':wrapper'),
+            project(':baseServices'),
+            project(':baseServicesGroovy'),
+            project(':messaging'),
+            project(':resources')
+    ]
 }
 
 apply from: "gradle/buildReceipt.gradle"
@@ -107,15 +118,18 @@ apply from: "gradle/wrapper.gradle"
 apply from: "gradle/idea.gradle"
 apply from: "gradle/eclipse.gradle"
 apply from: "gradle/classycle.gradle"
+apply from: "gradle/strictCompile.gradle"
 apply from: "gradle/noDependencyResolutionDuringConfiguration.gradle"
 apply from: "gradle/testSetup.gradle"
 apply from: "gradle/testGroupings.gradle"
+apply from: "gradle/taskOrdering.gradle"
+apply from: "gradle/fix-GRADLE-2492.gradle"
 
 allprojects {
     group = 'org.gradle'
 
     repositories {
-        maven { url 'http://repo.gradle.org/gradle/libs' }
+        maven { url 'https://repo.gradle.org/gradle/libs' }
     }
 }
 
@@ -141,6 +155,9 @@ subprojects {
 }
 
 configurations {
+    sonar {
+        visible = false
+    }
     runtime {
         visible = false
     }
@@ -156,8 +173,9 @@ configurations {
 dependencies {
     runtime project(':launcher')
     runtime project(':wrapper')
+    sonar libraries.logback_classic
     gradlePlugins pluginProjects
-    gradlePlugins project(':coreImpl')
+    gradlePlugins project(':dependencyManagement')
 }
 
 task verifyIsProductionBuildEnvironment << {
@@ -179,12 +197,6 @@ task waitForDaemonsToDie {
     }
 }
 
-task aggregateTestReports(type: TestReportAggregator) {
-    testReportDir = reporting.file("tests")
-    testResultsDir = file("${buildDir}/test-results")
-    projects = subprojects
-}
-
 evaluationDependsOn ":distributions"
 
 task install(type: Install) {
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 538676d..a3cb553 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -21,7 +21,7 @@ apply plugin: 'idea'
 apply plugin: 'eclipse'
 
 repositories {
-    maven { url 'http://repo.gradle.org/gradle/libs' }
+    maven { url 'https://repo.gradle.org/gradle/libs' }
     mavenCentral()
 }
 
@@ -30,8 +30,8 @@ dependencies {
     compile 'com.google.guava:guava-jdk5:14.0.1 at jar'
     compile 'commons-lang:commons-lang:2.6 at jar'
     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'
+    testCompile 'junit:junit:4.12 at jar'
+    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0 at jar', 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2'
 
     compile "org.pegdown:pegdown:1.1.0"
     compile "org.jsoup:jsoup:1.6.3"
@@ -42,5 +42,6 @@ dependencies {
     compile "org.gradle.jarjar:jarjar:1.2.1"
 }
 apply from: '../gradle/compile.gradle'
+apply from: '../gradle/taskOrdering.gradle'
 apply from: '../gradle/codeQuality.gradle'
 apply from: '../gradle/classycle.gradle'
diff --git a/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy b/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
index e582417..1c1f25a 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/BuildTypes.groovy
@@ -53,13 +53,14 @@ class BuildTypes {
             def taskNames = project.gradle.startParameter.taskNames
 
             def usedName = taskNames.find { it in [name, abbreviation] }
-            if (usedName) {
+            def index = taskNames.indexOf(usedName)
+            if (usedName && !((taskNames[index - 1] == '--task') && (taskNames[index - 2] ==~ /h(e(lp?)?)?/))) {
                 activeNames << name
-                def index = taskNames.indexOf(usedName)
                 taskNames.remove((int)index)
                 tasks.reverse().each {
                     taskNames.add(index, it)
                 }
+                project.gradle.startParameter.taskNames = taskNames
                 projectProperties.each { k, v ->
                     if (!project.hasProperty(k)) {
                         project.ext."$k" = null
@@ -74,4 +75,4 @@ class BuildTypes {
          }
     }
 
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy b/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
index 9b9e789..e4f0b2f 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/JarJarJar.groovy
@@ -26,6 +26,7 @@ import org.gradle.api.tasks.Input
 class JarJarJar extends Jar {
     @Input def rules = [:]
     @Input def keeps = []
+    @Input def zaps = []
 
     public JarJarJar() {
         doLast {
@@ -64,11 +65,18 @@ class JarJarJar extends Jar {
         keeps << pattern
     }
 
+    void zap(String pattern) {
+        zaps << pattern
+    }
+
     private void writeRuleFile(File ruleFile) {
         ruleFile.withPrintWriter { writer ->
             rules.each {pattern, result ->
                 writer.println("rule ${pattern} ${result}")
             }
+            zaps.each {pattern ->
+                writer.println("zap ${pattern}")
+            }
             keeps.each {pattern ->
                 writer.println("keep ${pattern}")
             }
diff --git a/buildSrc/src/main/groovy/org/gradle/build/ReleasedVersions.groovy b/buildSrc/src/main/groovy/org/gradle/build/ReleasedVersions.groovy
index 96e21ab..f289a90 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/ReleasedVersions.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/ReleasedVersions.groovy
@@ -39,6 +39,10 @@ class ReleasedVersions {
 
     private void download() {
         if (offline) {
+            if (!destFile.isFile()) {
+                throw new RuntimeException("The versions info file (${destFile.name}) does not exist from a previous build and cannot be downloaded (due to --offline switch).\n"
+                                           + "After running 'clean', build must be executed once online before going offline")
+            }
             LOGGER.warn("Versions information will not be downloaded because --offline switch is used.\n"
                     + "Without the version information certain integration tests may fail or use outdated version details.")
             return
diff --git a/buildSrc/src/main/groovy/org/gradle/build/TestReportAggregator.groovy b/buildSrc/src/main/groovy/org/gradle/build/TestReportAggregator.groovy
deleted file mode 100644
index 2c5ed5f..0000000
--- a/buildSrc/src/main/groovy/org/gradle/build/TestReportAggregator.groovy
+++ /dev/null
@@ -1,51 +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.build
-
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.testing.Test;
-
-class TestReportAggregator extends Copy {
-    def projects
-
-    File testResultsDir
-
-    @OutputDirectory
-    File testReportDir
-
-    def TestReportAggregator() {
-        dependsOn { testTasks }
-        from { inputTestResultDirs }
-        into { testResultsDir }
-    }
-
-    @TaskAction
-    def aggregate() {
-        def report = new org.gradle.api.internal.tasks.testing.junit.report.DefaultTestReport(testReportDir: testReportDir, testResultsDir: testResultsDir)
-        report.generateReport()
-    }
-
-    def getTestTasks() {
-        projects.collect { it.tasks.withType(Test) }.flatten()
-    }
-
-    def getInputTestResultDirs() {
-        testTasks*.testResultsDir
-    }
-
-}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSamplesDocTask.groovy
similarity index 100%
rename from buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy
rename to buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSamplesDocTask.groovy
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/Docbook2XHtml.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/Docbook2Xhtml.groovy
similarity index 100%
rename from buildSrc/src/main/groovy/org/gradle/build/docs/Docbook2XHtml.groovy
rename to buildSrc/src/main/groovy/org/gradle/build/docs/Docbook2Xhtml.groovy
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/ExtractSnippetsTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/ExtractSnippetsTask.groovy
index 812b563..cb8b2e1 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/ExtractSnippetsTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/ExtractSnippetsTask.groovy
@@ -47,7 +47,7 @@ public class ExtractSnippetsTask extends SourceTask {
 
                 destFile.parentFile.mkdirs()
 
-                if (['.jar', '.zip', '.gpg'].find{ name.endsWith(it) }) {
+                if (['.war', '.jar', '.zip', '.gpg'].find{ name.endsWith(it) }) {
                     destFile.withOutputStream { it.write(srcFile.readBytes()) }
                     return
                 }
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementLocationHandler.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementLocationHandler.groovy
index 90b03c0..c3abbf9 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementLocationHandler.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/SampleElementLocationHandler.groovy
@@ -51,7 +51,7 @@ class SampleElementLocationHandler {
             textElement.appendChild(doc.createTextNode(' The code for this example can be found at '))
             Element filenameElement = doc.createElement('filename')
             textElement.appendChild(filenameElement)
-            textElement.appendChild(doc.createTextNode(' which is in both the binary and source distributions of Gradle.'))
+            textElement.appendChild(doc.createTextNode(' in the ‘-all’ distribution of Gradle.'))
             filenameElement.appendChild(doc.createTextNode("samples/$srcDir"))
 
             parentElement.appendChild(tipElement)
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 b85b563..3e278db 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
@@ -80,11 +80,11 @@ public class UserGuideTransformTask extends DefaultTask {
     def transform() {
         XIncludeAwareXmlProvider provider = new XIncludeAwareXmlProvider()
         provider.parse(sourceFile)
-        transform(provider.document)
+        transformImpl(provider.document)
         provider.write(destFile)
     }
 
-    private def transform(Document doc) {
+    private def transformImpl(Document doc) {
         use(DOMCategory) {
             use(BuildableDOMCategory) {
                 addVersionInfo(doc)
@@ -111,8 +111,8 @@ public class UserGuideTransformTask extends DefaultTask {
         }
     }
 
-    String normalise(String content) {
-        return content.trim().replace('\t', '    ').replace('\r\n', '\n')
+    static String normalise(String content) {
+        return content.replace('\t', '    ').stripIndent().replace('\r\n', '\n')
     }
 
     def transformApiLinks(Document doc) {
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/AssembleDslDocTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/AssembleDslDocTask.groovy
index 78d39ea..357f374 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/AssembleDslDocTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/AssembleDslDocTask.groovy
@@ -114,7 +114,7 @@ class AssembleDslDocTask extends DefaultTask {
             plugin.extends.each { Element e ->
                 def targetClass = e.'@targetClass'
                 if (!targetClass) {
-                    throw new RuntimeException("No targetClass specified for extention provided by plugin '$pluginId'.")
+                    throw new RuntimeException("No targetClass specified for extension provided by plugin '$pluginId'.")
                 }
                 def extension = extensions[targetClass]
                 if (!extension) {
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexer.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexer.java
index aae62ba..d29bb62 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexer.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexer.java
@@ -23,7 +23,7 @@ import java.util.regex.Pattern;
  * Converts the main description of a javadoc comment into a stream of tokens.
  */
 class BasicJavadocLexer implements JavadocLexer {
-    private static final Pattern HTML_ELEMENT = Pattern.compile("(?s)<\\\\?.+?>");
+    private static final Pattern HTML_ELEMENT = Pattern.compile("(?s)<\\\\?[^<]+?>");
     private static final Pattern ELEMENT_ATTRIBUTE = Pattern.compile("(?s)\\w+(\\s*=\\s*('.*?')|(\".*?\"))?");
     private static final Pattern END_ATTRIBUTE_NAME = Pattern.compile("=|(\\s)|(/>)|>");
     private static final Pattern ATTRIBUTE_SEPARATOR = Pattern.compile("\\s*=\\s*");
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 64f27b6..6449f6b 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
@@ -330,6 +330,7 @@ public class JavadocConverter {
             elementToElementMap.put("ol", "orderedlist");
             elementToElementMap.put("li", "listitem");
             elementToElementMap.put("em", "emphasis");
+            elementToElementMap.put("strong", "emphasis");
             elementToElementMap.put("i", "emphasis");
             elementToElementMap.put("b", "emphasis");
             elementToElementMap.put("code", "literal");
@@ -612,7 +613,7 @@ public class JavadocConverter {
             }
             if (elementName.equals("dd")) {
                 if (currentItem == null) {
-                    throw new IllegalStateException("No <dt> element preceeding <dd> element.");
+                    throw new IllegalStateException("No <dt> element preceding <dd> element.");
                 }
                 nodes.push(document.createElement("listitem"));
                 return true;
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverter.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverter.java
index 38c99b3..0cfabf9 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverter.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverter.java
@@ -33,7 +33,9 @@ import java.util.regex.Pattern;
  * Converts a javadoc link into docbook.
  */
 public class JavadocLinkConverter {
-    private final Pattern linkPattern = Pattern.compile("(?s)\\s*([\\w\\.]*)(#(\\w+)(\\((.*)\\))?)?.*");
+    private static final Pattern LINK_PATTERN = Pattern.compile("(?s)\\s*([\\w\\.]*)(#(\\w+)(\\((.*)\\))?)?.*");
+    private static final Pattern TYPE_PATTERN = Pattern.compile("(\\w+)\\s*(.*?)\\s*");
+    private static final Pattern PARAM_DELIMITER = Pattern.compile(",\\s*");
     private final Document document;
     private final TypeNameResolver typeNameResolver;
     private final LinkRenderer linkRenderer;
@@ -63,7 +65,7 @@ public class JavadocLinkConverter {
     }
 
     private Node doResolve(String link, ClassMetaData classMetaData, GenerationListener listener) {
-        Matcher matcher = linkPattern.matcher(link);
+        Matcher matcher = LINK_PATTERN.matcher(link);
         if (!matcher.matches()) {
             return null;
         }
@@ -95,10 +97,10 @@ public class JavadocLinkConverter {
             signature.append(methodSignature);
             signature.append("(");
             if (matcher.group(5).length() > 0) {
-                String[] types = matcher.group(5).split(",\\s*");
+                String[] types = PARAM_DELIMITER.split(matcher.group(5));
                 for (int i = 0; i < types.length; i++) {
                     String type = types[i];
-                    Matcher typeMatcher = Pattern.compile("(\\w+)(.*)").matcher(type);
+                    Matcher typeMatcher = TYPE_PATTERN.matcher(type);
                     if (!typeMatcher.matches()) {
                         return null;
                     }
@@ -106,7 +108,11 @@ public class JavadocLinkConverter {
                         signature.append(", ");
                     }
                     signature.append(typeNameResolver.resolve(typeMatcher.group(1), classMetaData));
-                    signature.append(typeMatcher.group(2));
+                    String suffix = typeMatcher.group(2);
+                    if (suffix.equals("...")) {
+                        suffix = "[]";
+                    }
+                    signature.append(suffix);
                 }
             }
             signature.append(")");
@@ -152,7 +158,7 @@ public class JavadocLinkConverter {
             String targetClassName = typeNameResolver.resolve(parts[0], classMetaData);
             targetClass = repository.find(targetClassName);
             if (targetClass == null) {
-                listener.warning(String.format("Could not local target class '%s' for field value link '%s'", targetClass, fieldName));
+                listener.warning(String.format("Could not locate target class '%s' for field value link '%s'", targetClass, fieldName));
                 Element element = document.createElement("UNHANDLED-VALUE");
                 element.appendChild(document.createTextNode(targetClassName + ":" + parts[1]));
                 return element;
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/links/ClassLinkMetaData.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/links/ClassLinkMetaData.java
index f310ff5..54ee61c 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/links/ClassLinkMetaData.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/links/ClassLinkMetaData.java
@@ -65,6 +65,11 @@ public class ClassLinkMetaData implements Serializable, Attachable<ClassLinkMeta
     }
 
     private MethodLinkMetaData findMethod(String method) {
+        if (method.endsWith("...)")) {
+            // Should reuse the link parsing stuff from JavadocLinkConverter instead
+            method = method.substring(0, method.length() - 4) + "[])";
+        }
+
         MethodLinkMetaData metaData = methods.get(method);
         if (metaData != null) {
             return metaData;
@@ -79,15 +84,15 @@ public class ClassLinkMetaData implements Serializable, Attachable<ClassLinkMeta
         if (candidates.isEmpty()) {
             String message = String.format("No method '%s' found for class '%s'.", method, className);
             message += "\nThis problem may happen when some apilink from docbook template xmls refers to unknown method."
-                    +  "\nExample: <apilink class=\"org.gradle.api.Project\" method=\"someMethodThatDoesNotExist\"/>";
+                    + "\nExample: <apilink class=\"org.gradle.api.Project\" method=\"someMethodThatDoesNotExist\"/>";
             throw new RuntimeException(message);
         }
         if (candidates.size() != 1) {
             String message = String.format("Found multiple methods called '%s' in class '%s'. Candidates: %s",
                     method, className, CollectionUtils.join(", ", candidates));
             message += "\nThis problem may happen when some apilink from docbook template xmls is incorrect. Example:"
-                    +  "\nIncorrect: <apilink class=\"org.gradle.api.Project\" method=\"tarTree\"/>"
-                    +  "\nCorrect:   <apilink class=\"org.gradle.api.Project\" method=\"tarTree(Object)\"/>";
+                    + "\nIncorrect: <apilink class=\"org.gradle.api.Project\" method=\"tarTree\"/>"
+                    + "\nCorrect:   <apilink class=\"org.gradle.api.Project\" method=\"tarTree(Object)\"/>";
             throw new RuntimeException(message);
         }
         return candidates.get(0);
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaData.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaData.java
index 76a64b7..32695ce 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaData.java
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaData.java
@@ -82,7 +82,9 @@ public class TypeMetaData implements Serializable, TypeContainer {
         }
         TypeMetaData rawType = new TypeMetaData(name);
         rawType.arrayDimensions = arrayDimensions;
-        rawType.varargs = varargs;
+        if (varargs) {
+            rawType.arrayDimensions++;
+        }
         return rawType;
     }
 
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/UserGuideTransformTaskTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/UserGuideTransformTaskTest.groovy
new file mode 100644
index 0000000..2eb6757
--- /dev/null
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/UserGuideTransformTaskTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class UserGuideTransformTaskTest extends Specification {
+
+    def replacesTabsWith4Spaces() {
+        given:
+        String content = "test\ttest\ttest"
+        when:
+        def actual = UserGuideTransformTask.normalise(content)
+
+        then:
+        actual == "test    test    test"
+    }
+
+    def usesUnixLineEndings() {
+        given:
+        String content = "test\r\ntest\r\ntest"
+        when:
+        def actual = UserGuideTransformTask.normalise(content)
+
+        then:
+        actual == "test\ntest\ntest"
+    }
+
+    def stripsLeadingWhitespaceOnSingleLine() {
+        given:
+        String content = "     test"
+        when:
+        def actual = UserGuideTransformTask.normalise(content)
+
+        then:
+        actual == "test"
+    }
+
+    def preservesFormattingWithIndentedMultiline() {
+        given:
+        String content = TextUtil.normaliseLineSeparators(
+"""            sources {
+                cpp {
+                    lib library: "hello"
+                }
+            }""")
+        when:
+        def actual = UserGuideTransformTask.normalise(content)
+
+        then:
+        actual == 
+"""sources {
+    cpp {
+        lib library: "hello"
+    }
+}"""
+    }
+}
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexerTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexerTest.groovy
index 216c81b..94cf78c 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexerTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/BasicJavadocLexerTest.groovy
@@ -19,7 +19,7 @@ import spock.lang.Specification
 
 class BasicJavadocLexerTest extends Specification {
     final BasicJavadocLexer lexer = new BasicJavadocLexer(new JavadocScanner(""))
-    final JavadocLexer.TokenVisitor visitor = Mock()
+    final visitor = Mock(JavadocLexer.TokenVisitor)
 
     def parsesHtmlElements() {
         when:
@@ -164,6 +164,17 @@ class BasicJavadocLexerTest extends Specification {
         0 * visitor._
     }
 
+    def ignoresBadlyFormedHtmlElement() {
+        when:
+        lexer.pushText("a << b")
+        lexer.visit(visitor)
+
+        then:
+        1 * visitor.onText('a << b')
+        1 * visitor.onEnd()
+        0 * visitor._
+    }
+
     def javadocTagCanContainEOLChars() {
         when:
         lexer.pushText(" * {@link #Something(Object,\n * String\n * }")
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 3124e3b..caa7e89 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
@@ -185,6 +185,16 @@ line 2</para>'''
 literal code</programlisting>'''
     }
 
+    def preElementCanContainReservedCharacters() {
+        _ * classMetaData.rawCommentText >> ''' * <pre>a << b</pre>'''
+
+        when:
+        def result = parser.parse(classMetaData, listener)
+
+        then:
+        format(result.docbook) == '''<programlisting language="java">a << b</programlisting>'''
+    }
+
     def implicitlyEndsCurrentParagraphAtNextBlockElement() {
         _ * classMetaData.rawCommentText >> ''' * for example: <pre>this is some
  * literal code</pre> does something.
@@ -287,6 +297,16 @@ literal code</programlisting><para> does something.
         format(result.docbook) == '''<para><emphasis>text</emphasis></para>'''
     }
 
+    def convertsAStrongElementToAnEmphasisElement() {
+        _ * classMetaData.rawCommentText >> '<strong>text</strong>'
+
+        when:
+        def result = parser.parse(classMetaData, listener)
+
+        then:
+        format(result.docbook) == '''<para><emphasis>text</emphasis></para>'''
+    }
+
     def convertsBAndIElementToAnEmphasisElement() {
         _ * classMetaData.rawCommentText >> '<i>text</i> <b>other</b>'
 
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverterTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverterTest.groovy
index bbdc46a..a9485f2 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverterTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/docbook/JavadocLinkConverterTest.groovy
@@ -93,10 +93,18 @@ class JavadocLinkConverterTest extends XmlSpecification {
         _ * linkRenderer.link(method, listener) >> parse('<someLinkElement/>')
 
         when:
-        def link = converter.resolve('#someName(SomeClass, Object)', classMetaData, listener)
+        def link = converter.resolve(input, classMetaData, listener)
 
         then:
         format(link) == '<someLinkElement/>'
+
+        where:
+        input << [
+                '#someName(SomeClass,Object)',
+                '#someName(SomeClass, Object)',
+                '#someName(SomeClass,\tObject)',
+                '#someName  (  \t SomeClass ,\tObject\t ) '
+        ]
     }
 
     def convertsMethodSignatureWithNoParamsToLink() {
@@ -125,6 +133,27 @@ class JavadocLinkConverterTest extends XmlSpecification {
         format(link) == '<someLinkElement/>'
     }
 
+    def convertsMethodSignatureWithVarargsTypeToLink() {
+        MethodMetaData method = method('someName', signature: 'someName(org.gradle.SomeClass[])')
+        _ * nameResolver.resolve('SomeClass', classMetaData) >>'org.gradle.SomeClass'
+        _ * classMetaData.declaredMethods >> ([method] as Set)
+        _ * linkRenderer.link(method, listener) >> parse('<someLinkElement/>')
+
+        when:
+        def link = converter.resolve(input, classMetaData, listener)
+
+        then:
+        format(link) == '<someLinkElement/>'
+
+        where:
+        input << [
+                '#someName(SomeClass[])',
+                '#someName(SomeClass [] )',
+                '#someName(SomeClass...)',
+                '#someName(SomeClass ... )'
+        ]
+    }
+
     def convertsMethodNameWithLabelToLink() {
         MethodMetaData method = method('someName')
         _ * classMetaData.declaredMethods >> ([method] as Set)
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTaskTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTaskTest.groovy
index 0d92bb6..d8f1351 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTaskTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/ExtractDslMetaDataTaskTest.groovy
@@ -22,7 +22,7 @@ import spock.lang.Specification
 
 class ExtractDslMetaDataTaskTest extends Specification {
     final Project project = new ProjectBuilder().build()
-    final ExtractDslMetaDataTask task = project.tasks.add('dsl', ExtractDslMetaDataTask.class)
+    final ExtractDslMetaDataTask task = project.tasks.create('dsl', ExtractDslMetaDataTask.class)
     final SimpleClassMetaDataRepository<ClassMetaData> repository = new SimpleClassMetaDataRepository<ClassMetaData>()
 
     def setup() {
@@ -305,6 +305,7 @@ class ExtractDslMetaDataTaskTest extends Specification {
         arrayMethod.parameters[0].name == 'strings'
         arrayMethod.parameters[0].type.signature == 'java.lang.String[]...'
         arrayMethod.parameters[0].type.arrayDimensions == 2
+        arrayMethod.parameters[0].type.varargs
 
         javaClass.declaredPropertyNames == ['intProp'] as Set
     }
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/MethodMetaDataTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/MethodMetaDataTest.groovy
index 0cbf728..c72371f 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/MethodMetaDataTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/MethodMetaDataTest.groovy
@@ -28,6 +28,7 @@ class MethodMetaDataTest extends Specification {
 
         expect:
         method.signature == 'ReturnType method(ParamType param1, ParamType2 param2)'
+        method.overrideSignature == 'method(ParamType, ParamType2)'
     }
 
     def formatsOverrideSignatureUsingRawParameterTypes() {
@@ -36,9 +37,20 @@ class MethodMetaDataTest extends Specification {
         method.addParameter('param2', new TypeMetaData('ParamType2'))
 
         expect:
+        method.signature == 'ReturnType method(ParamType<Type1> param, ParamType2 param2)'
         method.overrideSignature == 'method(ParamType, ParamType2)'
     }
 
+    def formatsOverrideSignatureForVarargsParameter() {
+        method.returnType = new TypeMetaData('ReturnType')
+        method.addParameter('param', new TypeMetaData('ParamType'))
+        method.addParameter('param2', new TypeMetaData('ParamType2').setVarargs())
+
+        expect:
+        method.signature == 'ReturnType method(ParamType param, ParamType2... param2)'
+        method.overrideSignature == 'method(ParamType, ParamType2[])'
+    }
+
     def locatesOverriddenMethodInSuperClass() {
         ClassMetaData superClassMetaData = Mock()
         MethodMetaData overriddenMethod = Mock()
diff --git a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaDataTest.groovy b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaDataTest.groovy
index ca803b9..85e7308 100644
--- a/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaDataTest.groovy
+++ b/buildSrc/src/test/groovy/org/gradle/build/docs/dsl/source/model/TypeMetaDataTest.groovy
@@ -34,10 +34,17 @@ class TypeMetaDataTest extends Specification {
     }
 
     def rawTypeForVarargsType() {
+        when:
         type.setVarargs()
 
-        expect:
-        type.rawType.signature == 'org.gradle.SomeType...'
+        then:
+        type.rawType.signature == 'org.gradle.SomeType[]'
+
+        when:
+        type.addArrayDimension()
+
+        then:
+        type.rawType.signature == 'org.gradle.SomeType[][]'
     }
 
     def rawTypeForParameterizedArrayType() {
diff --git a/config/checkstyle/checkstyle-api.xml b/config/checkstyle/checkstyle-api.xml
index c646fdd..3952530 100644
--- a/config/checkstyle/checkstyle-api.xml
+++ b/config/checkstyle/checkstyle-api.xml
@@ -29,7 +29,7 @@
         </module>
 
         <module name="JavadocType">
-            <property name="scope" value="package"/>
+            <property name="scope" value="protected"/>
         </module>
 
         <!-- TODO - switch this on -->
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
index 1951392..f84a243 100644
--- a/config/checkstyle/suppressions.xml
+++ b/config/checkstyle/suppressions.xml
@@ -24,6 +24,8 @@
               files=".*[/\\]subprojects[/\\]base-services-groovy[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\][^/\\]+"/>
     <suppress checks="JavadocPackage"
               files=".*[/\\]subprojects[/\\]base-services-groovy[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]specs[/\\][^/\\]+"/>
+    <suppress checks="JavadocPackage"
+              files=".*[/\\]subprojects[/\\]language-groovy[/\\]src[/\\]main[/\\]java[/\\]org[/\\]gradle[/\\]api[/\\]tasks[/\\]javadoc[/\\][^/\\]+"/>
     <!-- Don't require api docs for projects only used internally -->
     <suppress checks="Javadoc.*"
               files=".*[/\\]subprojects[/\\]internal-.+[/\\]src[/\\]main[/\\].+"/>
@@ -32,5 +34,8 @@
     <suppress checks="Javadoc.*"
               files=".*[/\\]subprojects[/\\]javascript[/\\].+"/>
 
+    <suppress checks="Javadoc.*"
+              files=".*[/\\]subprojects[/\\]core[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]logging[/\\]LogLevel.java"/>
+
 
 </suppressions>
\ No newline at end of file
diff --git a/config/codenarc.xml b/config/codenarc.xml
index 1daadd3..3487e92 100644
--- a/config/codenarc.xml
+++ b/config/codenarc.xml
@@ -25,6 +25,7 @@
     <ruleset-ref path='rulesets/imports.xml'>
         <exclude name="ImportFromSunPackages"/>
         <exclude name="MisorderedStaticImports"/>
+        <exclude name="NoWildcardImports"/>
     </ruleset-ref>
     <ruleset-ref path='rulesets/naming.xml'>
         <rule-config name='ClassName'>
diff --git a/gradle/buildReceipt.gradle b/gradle/buildReceipt.gradle
index eb0209e..16882d1 100644
--- a/gradle/buildReceipt.gradle
+++ b/gradle/buildReceipt.gradle
@@ -109,7 +109,17 @@ task createBuildReceipt(dependsOn: determineCommitId) {
         try {
             hostName = InetAddress.localHost.hostName
         } catch (UnknownHostException e) {
-            hostName = "unknown"
+            def baos = new ByteArrayOutputStream()
+            def execResult = exec {
+                ignoreExitValue = true
+                commandLine = ["hostname"]
+                standardOutput = baos
+            }
+            if (execResult.exitValue == 0) {
+                hostName = new String(baos.toByteArray(), "utf8").trim()
+            } else {
+                hostName = "unknown"
+            }
         }
         def data = [
                 commitId: determineCommitId.commitId,
diff --git a/gradle/classycle.gradle b/gradle/classycle.gradle
index 16685d2..15dd94b 100644
--- a/gradle/classycle.gradle
+++ b/gradle/classycle.gradle
@@ -1,5 +1,7 @@
 allprojects {
-    ext.useClassycle = {
+    ext.useClassycle = { params = [:] ->
+        def excludePatterns = params.exclude ?: []
+
         configurations {
             classycle
         }
@@ -12,8 +14,8 @@ allprojects {
 
         sourceSets.all { sourceSet ->
             def taskName = sourceSet.getTaskName('classycle', null)
-            task(taskName){
-                def reportFile = reporting.file("classcycle/${sourceSet.name}.xml")
+            task(taskName) {
+                def reportFile = reporting.file("classcycle/${sourceSet.name}.txt")
                 inputs.files sourceSet.output
                 outputs.file reportFile
                 doLast {
@@ -29,7 +31,11 @@ allprojects {
                         check absenceOfPackageCycles > 1 in org.gradle.*
                     """
                         ) {
-                            fileset(dir: sourceSet.output.classesDir)
+                            fileset(dir: sourceSet.output.classesDir) {
+                                excludePatterns.each { excludePattern ->
+                                    exclude(name: excludePattern)
+                                }
+                            }
                         }
                     } catch(Exception e) {
                         throw new RuntimeException("Classycle check failed: $e.message. See report at ${new org.gradle.logging.ConsoleRenderer().asClickableFileUrl(reportFile)}", e)
diff --git a/gradle/codeQuality.gradle b/gradle/codeQuality.gradle
index af0254d..ea941ce 100644
--- a/gradle/codeQuality.gradle
+++ b/gradle/codeQuality.gradle
@@ -1,5 +1,6 @@
 apply plugin: 'checkstyle'
 apply plugin: 'codenarc'
+apply from: new File(buildscript.sourceFile.parentFile, 'dependencies.gradle')
 
 def configDir = new File(buildscript.sourceFile.parentFile.parentFile, 'config')
 
@@ -14,6 +15,8 @@ codenarc {
     configFile = new File(configDir, "codenarc.xml")
 }
 
+configurations.codenarc.resolutionStrategy.force(libraries.groovy)
+
 plugins.withType(GroovyBasePlugin) {
     sourceSets.all { sourceSet ->
         task "${sourceSet.getTaskName('checkstyle', 'groovy')}"(type: Checkstyle) {
@@ -25,8 +28,10 @@ plugins.withType(GroovyBasePlugin) {
     }
 }
 
+def codeQualityTasks = tasks.matching { task ->
+    [org.gradle.api.plugins.quality.CodeNarc, org.gradle.api.plugins.quality.Checkstyle].any { it.isInstance(task) }
+}
+
 task codeQuality {
-    dependsOn tasks.matching { task ->
-        [org.gradle.api.plugins.quality.CodeNarc, org.gradle.api.plugins.quality.Checkstyle].any { it.isInstance(task) }
-    }
+    dependsOn codeQualityTasks
 }
\ No newline at end of file
diff --git a/gradle/compile.gradle b/gradle/compile.gradle
index 766abd9..c491d5a 100644
--- a/gradle/compile.gradle
+++ b/gradle/compile.gradle
@@ -1,4 +1,4 @@
-tasks.withType(Compile) {
+tasks.withType(JavaCompile) {
     options.encoding = 'utf-8'
     options.compilerArgs = ['-Xlint:-options']
 }
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index e8ff446..0cee389 100755
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -21,30 +21,35 @@ ext {
 
 versions.commons_io = 'commons-io:commons-io:1.4'
 
-libraries.ant = dependencies.module('org.apache.ant:ant:1.9.3') {
-    dependency 'org.apache.ant:ant-launcher:1.9.3 at jar'
+versions.groovy = "2.3.10"
+
+versions.bouncycastle = "1.51"
+
+libraries.ant = dependencies.module('org.apache.ant:ant:1.9.4') {
+    dependency 'org.apache.ant:ant-launcher:1.9.4 at jar'
 }
 
-libraries.asm =  'org.ow2.asm:asm-all:5.0_BETA at jar'
+libraries.asm =  'org.ow2.asm:asm-all:5.0.3'
 libraries.commons_cli = 'commons-cli:commons-cli:1.2 at jar'
 libraries.commons_io = dependencies.module(versions.commons_io)
 libraries.commons_lang = 'commons-lang:commons-lang:2.6 at jar'
 libraries.commons_collections = 'commons-collections:commons-collections:3.2.1 at jar'
+libraries.jsch = "com.jcraft:jsch:0.1.51"
 libraries.ivy = dependencies.module('org.apache.ivy:ivy:2.2.0'){
-    dependency "com.jcraft:jsch:0.1.51"
+    dependency libraries.jsch
 }
 libraries.jcip = "net.jcip:jcip-annotations:1.0 at jar"
 libraries.inject = dependencies.module('javax.inject:javax.inject:1')
 
 // Logging
-libraries.slf4j_api = dependencies.module('org.slf4j:slf4j-api:1.7.5')
-libraries.jcl_to_slf4j = dependencies.module('org.slf4j:jcl-over-slf4j:1.7.5') {
+libraries.slf4j_api = dependencies.module('org.slf4j:slf4j-api:1.7.10')
+libraries.jcl_to_slf4j = dependencies.module('org.slf4j:jcl-over-slf4j:1.7.10') {
     dependency libraries.slf4j_api
 }
-libraries.jul_to_slf4j = dependencies.module('org.slf4j:jul-to-slf4j:1.7.5') {
+libraries.jul_to_slf4j = dependencies.module('org.slf4j:jul-to-slf4j:1.7.10') {
     dependency libraries.slf4j_api
 }
-libraries.log4j_to_slf4j = dependencies.module('org.slf4j:log4j-over-slf4j:1.7.5') {
+libraries.log4j_to_slf4j = dependencies.module('org.slf4j:log4j-over-slf4j:1.7.10') {
     dependency libraries.slf4j_api
 }
 libraries.logback_core = dependencies.module('ch.qos.logback:logback-core:1.0.13')
@@ -71,21 +76,31 @@ libraries.commons_httpclient = dependencies.module('org.apache.httpcomponents:ht
     dependency "org.samba.jcifs:jcifs:1.3.17 at jar"
 }
 
-libraries.maven_ant_tasks = dependencies.module("org.apache.maven:maven-ant-tasks:2.1.3") {
-    libraries.ant
-}
-
+libraries.maven_publish = [
+    'classworlds:classworlds:1.1-alpha-2 at jar',
+    'org.codehaus.plexus:plexus-container-default:1.0-alpha-9-stable-1 at jar',
+    'org.codehaus.plexus:plexus-utils:1.5.15 at jar',
+    'org.codehaus.plexus:plexus-interpolation:1.11 at jar',
+    'org.apache.maven:maven-artifact:2.2.1 at jar',
+    'org.apache.maven:maven-artifact-manager:2.2.1 at jar',
+    'org.apache.maven:maven-repository-metadata:2.2.1 at jar',
+    'org.apache.maven:maven-model:2.2.1 at jar',
+    'org.apache.maven:maven-project:2.2.1 at jar',
+    'org.apache.maven:maven-error-diagnostics:2.2.1 at jar',
+    'org.apache.maven:maven-settings:2.2.1 at jar',
+    'org.apache.maven.wagon:wagon-file:1.0-beta-6 at jar',
+    'org.apache.maven.wagon:wagon-http-lightweight:1.0-beta-6 at jar',
+    'org.apache.maven.wagon:wagon-provider-api:1.0-beta-6 at jar',
+]
 libraries += [
-        ant_antlr: 'org.apache.ant:ant-antlr:1.9.3 at jar',
-        antlr: 'antlr:antlr:2.7.7 at jar',
         dom4j: 'dom4j:dom4j:1.6.1 at jar',
-        guava: 'com.google.guava:guava-jdk5:14.0.1 at jar',
+        guava: 'com.google.guava:guava-jdk5:17.0 at jar',
         jsr305: 'com.google.code.findbugs:jsr305:1.3.9 at jar',
-        groovy: 'org.codehaus.groovy:groovy-all:1.8.6 at jar',
+        groovy: "org.codehaus.groovy:groovy-all:${versions.groovy}",
         jaxen: 'jaxen:jaxen:1.1 at jar',
         jcip: "net.jcip:jcip-annotations:1.0 at jar",
         jna: 'net.java.dev.jna:jna:3.2.7 at jar',
-        junit: 'junit:junit:4.11 at jar',
+        junit: 'junit:junit:4.12 at jar',
         xmlunit: 'xmlunit:xmlunit:1.3',
         nekohtml: 'net.sourceforge.nekohtml:nekohtml:1.9.14',
         xbean: 'org.apache.xbean:xbean-reflect:3.4 at jar', //required by maven3 classes
@@ -135,7 +150,7 @@ libraries.maven3 = dependencies.module("org.apache.maven:maven-core:3.0.4") {
 }
 
 libraries.spock = [
-    'org.spockframework:spock-core:0.7-groovy-1.8 at jar',
+    'org.spockframework:spock-core:0.7-groovy-2.0 at jar',
     libraries.groovy,
     libraries.objenesis,
     'cglib:cglib-nodep:2.2.2'
@@ -149,4 +164,36 @@ libraries.jmock = [
     libraries.objenesis,
     'cglib:cglib-nodep:2.2'
 ]
-libraries.gson = "com.google.code.gson:gson:2.2.4"
\ No newline at end of file
+libraries.gson = "com.google.code.gson:gson:2.2.4"
+libraries.sshd = dependencies.module("org.apache.sshd:sshd-core:0.13.0") {
+    dependency libraries.slf4j_api
+    dependency "org.apache.mina:mina-core:2.0.8"
+}
+
+libraries.bouncycastle_provider = "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}@jar"
+libraries.bouncycastle_pgp = dependencies.module("org.bouncycastle:bcpg-jdk15on:${versions.bouncycastle}") {
+    dependency libraries.bouncycastle_provider
+}
+
+libraries.joda = 'joda-time:joda-time:2.7 at jar'
+
+libraries.awsS3 = [
+        'com.amazonaws:aws-java-sdk-s3:1.9.19 at jar',
+        'com.amazonaws:aws-java-sdk-kms:1.9.19 at jar',
+        'com.amazonaws:aws-java-sdk-core:1.9.19 at jar',
+        'com.fasterxml.jackson.core:jackson-core:2.3.2 at jar',
+        'com.fasterxml.jackson.core:jackson-annotations:2.3.2 at jar',
+        'com.fasterxml.jackson.core:jackson-databind:2.3.2 at jar'
+] + libraries.commons_httpclient + libraries.joda
+
+allprojects {
+    configurations.all {
+        resolutionStrategy.eachDependency { details ->
+            if (details.requested.group == 'org.ow2.asm') {
+                details.useTarget(libraries.asm)
+            } else if (details.requested.group == 'org.codehaus.groovy') {
+                details.useTarget(libraries.groovy)
+            }
+        }
+    }
+}
diff --git a/gradle/fix-GRADLE-2492.gradle b/gradle/fix-GRADLE-2492.gradle
new file mode 100644
index 0000000..6132fa9
--- /dev/null
+++ b/gradle/fix-GRADLE-2492.gradle
@@ -0,0 +1,29 @@
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.locks.ReentrantLock
+
+// Maven POM generation is not thread safe, so serialize all the Upload tasks we can use `--parallel`.
+// https://issues.gradle.org/browse/GRADLE-2492
+// When we start building with 2.3 and later we should remove this and just add a common output dir for all tasks and let Gradle serialize them
+def lock = new ReentrantLock()
+def available = lock.newCondition()
+def busy = new AtomicBoolean()
+def serializedTasks = []
+allprojects {
+    tasks.matching { it.name == "generatePom" || it instanceof Upload }.all {
+        serializedTasks << it
+        doFirst {
+            lock.lock()
+            while (busy.get()) {
+                available.await()
+            }
+            busy.set(true)
+        }
+    }
+}
+gradle.taskGraph.afterTask {
+    if (it in serializedTasks && lock.heldByCurrentThread) {
+        busy.set(false)
+        available.signal()
+        lock.unlock()
+    }
+}
\ No newline at end of file
diff --git a/gradle/groovyProject.gradle b/gradle/groovyProject.gradle
index a1bcd7e..a17b6a8 100644
--- a/gradle/groovyProject.gradle
+++ b/gradle/groovyProject.gradle
@@ -8,7 +8,7 @@ sourceCompatibility = 1.5
 targetCompatibility = 1.5
 
 ext {
-    compileTasks = tasks.matching { it instanceof Compile || it instanceof GroovyCompile }
+    compileTasks = tasks.matching { it instanceof JavaCompile || it instanceof GroovyCompile }
     testTasks = tasks.withType(Test)
     generatedResourcesDir = file("$buildDir/generated-resources/main")
     generatedTestResourcesDir = file("$buildDir/generated-resources/test")
@@ -42,7 +42,9 @@ if (!javaVersion.java7) {
 }
 
 testTasks.all { task ->
-    maxParallelForks = rootProject.maxParallelForks
+    if (isCiServer) {
+        maxParallelForks = rootProject.maxParallelForks
+    }
     if (isCiServer) {
         doFirst {
             println "maxParallelForks for '$task.path' is $task.maxParallelForks"
@@ -118,4 +120,4 @@ if (buildTypes.isActive("pullRequestValidation")) {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/gradle/idea.gradle b/gradle/idea.gradle
index dbfd2a6..9dbe8e1 100644
--- a/gradle/idea.gradle
+++ b/gradle/idea.gradle
@@ -25,8 +25,8 @@ idea {
     project {
         wildcards += ['?*.gradle']
 
-        languageLevel = '1.5'
-
+        languageLevel = '1.6'
+        vcs = 'Git'
         ipr {
             withXml { provider ->
                 // Exclude resource directories from compilation and add them back in as classpath resources
@@ -64,10 +64,6 @@ idea {
                     }
                 }
 
-                // Use git
-                def vcsConfig = node.component.find { it.'@name' == 'VcsDirectoryMappings' }
-                vcsConfig.mapping[0].'@vcs' = 'Git'
-
                 // Set gradle home
                 def gradleSettings = node.appendNode('component', [name: 'GradleSettings'])
                 gradleSettings.appendNode('option', [name: 'SDK_HOME', value: gradle.gradleHomeDir.absolutePath])
@@ -76,20 +72,44 @@ idea {
                 def javacSettings = node.appendNode('component', [name: 'JavacSettings'])
                 javacSettings.appendNode('option', [name: 'MAXIMUM_HEAP_SIZE', value: "256"])
 
+                // Nullability annotations
+                def nullableManager = node.component.find { it.'@name' == 'NullableNotNullManager' }
+                if (nullableManager) {
+                    nullableManager.parent().remove(nullableManager)
+                }
+
+                node.append(new XmlParser().parseText("""
+          <component name="NullableNotNullManager">
+            <option name="myDefaultNullable" value="org.gradle.api.Nullable" />
+            <option name="myDefaultNotNull" value="" />
+            <option name="myNullables">
+              <value>
+                <list size="1">
+                  <item index="0" class="java.lang.String" itemvalue="org.gradle.api.Nullable" />
+                </list>
+              </value>
+            </option>
+            <option name="myNotNulls">
+              <value>
+                <list size="0" />
+              </value>
+            </option>
+          </component>
+        """))
                 // license header
                 def copyrightManager = node.component.find { it.'@name' == 'CopyrightManager' }
                 copyrightManager. at default = "ASL2"
                 def aslCopyright = copyrightManager.copyright.find { it.option.find { it. at name == "myName" }?. at value == "ASL2" }
                 if (aslCopyright == null) {
-                  copyrightManager.append(new XmlParser().parseText('''
+                  copyrightManager.append(new XmlParser().parseText("""
                       <copyright>
-                          <option name="notice" value="Copyright 2013 the original author or authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot [...]
+                          <option name="notice" value="Copyright \${today.year} the original author or authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed o [...]
                           <option name="keyword" value="Copyright" />
                           <option name="allowReplaceKeyword" value="" />
                           <option name="myName" value="ASL2" />
                           <option name="myLocal" value="true" />
                       </copyright>
-                '''))
+                """))
                 }
 
                 // Code formatting options
@@ -202,6 +222,7 @@ idea {
                       <excludes>
                         <file url="file://$PROJECT_DIR$/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy" />
                       </excludes>
+                      <option name="heapSize" value="2000" />
                     </component>
                 '''))
             }
@@ -237,6 +258,12 @@ idea {
 
         vmParameters.'@value' = defaultTestVmParams.collect { it.contains(" ") ? "\"$it\"" : it }.join(" ")
 
+        // Add int test environment variables
+        def lang = System.getenv('LANG') ?: 'en_US.UTF-8'
+        def envVars = runConfig.envs.first()
+        envVars.children.each { child -> envVars.remove(child) }
+        envVars.appendNode('env', [name: 'LANG', value: lang])
+
         // Add an application configuration
         runManagerConfig.'@selected' = 'Application.Gradle'
         def appConfig =
diff --git a/gradle/integTest.gradle b/gradle/integTest.gradle
index 244ed40..473ba0e 100644
--- a/gradle/integTest.gradle
+++ b/gradle/integTest.gradle
@@ -20,10 +20,29 @@ dependencies {
     //above can be removed when we implement the auto-apply plugins
 }
 
-ext.integTestTasks = tasks.withType(Test).matching { it.name.toLowerCase().endsWith('integtest') }
+ext.integTestTasks = tasks.withType(Test).matching { it.name != 'test' }
 
 import org.gradle.util.GradleVersion
 
+import java.util.regex.Pattern
+
+def removeDodgyCacheFiles(File dir) {
+    if (dir.directory) {
+        for (File cacheDir: dir.listFiles()) {
+            if (!cacheDir.name.matches("\\d+\\.\\d+(\\.\\d+)?(-\\w+)*(-\\d{14}[+-]\\d{4})?")) {
+                continue
+            }
+            for (String name: ["fileHashes", "outputFileStates", "fileSnapshots"]) {
+                def stateDir = new File(cacheDir, name)
+                if (stateDir.directory) {
+                    println "Removing old cache directory : ${stateDir}"
+                    delete(stateDir)
+                }
+            }
+        }
+    }
+}
+
 def removeOldVersionsFromDir(File dir, def shouldDelete, def dirPrefix = "", def dirSuffix = "") {
     if (dir.directory) {
 
@@ -52,34 +71,98 @@ def removeOldVersionsFromDir(File dir, def shouldDelete, def dirPrefix = "", def
     }
 }
 
-task cleanUpCaches {
-    doLast {
-        def executingVersion = GradleVersion.version(gradle.gradleVersion)
+project(":") {
+    if (tasks.findByName('cleanUpCaches')) {
+        return
+    }
 
-        def versionProps = readBuildReceipt("${rootProject.buildDir}/${buildReceiptFileName}")
-        def testedVersion = GradleVersion.version(versionProps.versionNumber)
+    task cleanUpCaches {
+        dependsOn ':createBuildReceipt'
+        doLast {
+            def executingVersion = GradleVersion.version(gradle.gradleVersion)
 
-        // Expire .gradle cache where major version is older than executing version
-        def expireTaskCache = { def candidateVersion ->
-            return candidateVersion.baseVersion < executingVersion.baseVersion
-        }
+            def versionProps = readBuildReceipt("${buildDir}/${buildReceiptFileName}")
+            def testedVersion = GradleVersion.version(versionProps.versionNumber)
 
-        // Expire intTestImage cache snapshots that are older than the tested version
-        def expireIntegTestCache = { def candidateVersion ->
-            return candidateVersion.snapshot && candidateVersion < testedVersion
+            // Expire .gradle cache where major version is older than executing version
+            def expireTaskCache = { def candidateVersion ->
+                return candidateVersion.baseVersion < executingVersion.baseVersion
+            }
+
+            // Expire intTestImage cache snapshots that are older than the tested version
+            def expireIntegTestCache = { def candidateVersion ->
+                return candidateVersion.snapshot && candidateVersion < testedVersion
+            }
+
+            // Remove state for old versions of Gradle that we're unlikely to ever require again
+            removeOldVersionsFromDir(file("buildSrc/.gradle"), expireTaskCache)
+            removeOldVersionsFromDir(file(".gradle"), expireTaskCache)
+            removeOldVersionsFromDir(file("intTestHomeDir/worker-1/caches"), expireIntegTestCache)
+            removeOldVersionsFromDir(file("intTestHomeDir/worker-1/daemon"), expireIntegTestCache)
+
+            // Remove old distributions used by wrapper that we're unlikely to ever require again
+            removeOldVersionsFromDir(file("intTestHomeDir/worker-1/wrapper/dists"), expireIntegTestCache, "gradle-", "-bin")
+            delete(file("intTestHomeDir/worker-1/wrapper/dists/dist"))
+
+            // Remove caches that weren't multi-process safe and may be corrupt
+            removeDodgyCacheFiles(file("intTestHomeDir/worker-1/caches"))
         }
+    }
+}
 
-        removeOldVersionsFromDir(rootProject.file("buildSrc/.gradle"), expireTaskCache)
-        removeOldVersionsFromDir(rootProject.file(".gradle"), expireTaskCache)
-        removeOldVersionsFromDir(rootProject.file("intTestHomeDir/worker-1/caches"), expireIntegTestCache)
-        removeOldVersionsFromDir(rootProject.file("intTestHomeDir/worker-1/daemon"), expireIntegTestCache)
-        removeOldVersionsFromDir(rootProject.file("intTestHomeDir/worker-1/wrapper/dists"), expireIntegTestCache, "gradle-", "-bin")
-        delete(rootProject.file("intTestHomeDir/worker-1/wrapper/dists/dist"))
+project(":") {
+    if (tasks.findByName('cleanUpDaemons')) {
+        return
+    }
+    task cleanUpDaemons {
+        onlyIf { !org.gradle.internal.jvm.Jvm.current().ibmJvm }
+        doLast {
+            def output = new ByteArrayOutputStream()
+            exec {
+                commandLine(org.gradle.internal.jvm.Jvm.current().getExecutable("jps").absolutePath, "-m")
+                standardOutput = output
+            }
+            def pattern = Pattern.compile("(\\d+)\\s+GradleDaemon\\s+(\\S+)\\s+(.+)")
+            output.toString().readLines().each { String line ->
+                def matcher = pattern.matcher(line)
+                if (!matcher.matches()) {
+                    return
+                }
+                def remainder = matcher.group(3)
+                if (!remainder.startsWith("${project.projectDir}${File.separator}")) {
+                    return
+                }
+                def pid = matcher.group(1)
+                println "killing daemon with pid $pid"
+                def killOutput = new ByteArrayOutputStream()
+                def result = exec {
+                    if (org.gradle.internal.os.OperatingSystem.current().windows) {
+                        commandLine = ["taskkill.exe", "/F", "/T", "/PID", pid]
+                    } else {
+                        commandLine = ["kill", pid]
+                    }
+                    standardOutput = killOutput
+                    errorOutput = killOutput
+                    ignoreExitValue = true
+                }
+                if (result.exitValue != 0) {
+                    String out = killOutput.toString()
+                    if (!out.contains('No such process')) {
+                        throw new GradleException("""Failed to kill daemon process $pid.
+Output: ${killOutput}
+""")
+                    }
+                }
+            }
+        }
     }
 }
 
 integTestTasks.all { Test task ->
-    dependsOn ':intTestImage', 'cleanUpCaches'
+    dependsOn ':intTestImage', ':cleanUpCaches'
+    finalizedBy ':cleanUpDaemons'
+    shouldRunAfter 'test'
+    
     testClassesDir = sourceSets.integTest.output.classesDir
     classpath = sourceSets.integTest.runtimeClasspath
     testSrcDirs = []
@@ -90,7 +173,9 @@ integTestTasks.all { Test task ->
     // use -PtestVersions=all or -PtestVersions=1.2,1.3…
     systemProperties['org.gradle.integtest.versions'] = project.hasProperty('testVersions') ? project.testVersions : 'latest'
 
-    systemProperties['org.gradle.integtest.cpp.toolChains'] = project.hasProperty("testAllPlatforms") ? 'all' : 'default';
+    systemProperties['org.gradle.integtest.cpp.toolChains'] = project.hasProperty("testAllPlatforms") ? 'all' : 'default'
+
+    systemProperties['org.gradle.integtest.multiversion'] = project.hasProperty("multiVersion") ? 'all' : 'default'
 
     dependsOn project.task("configure${task.name.capitalize()}") << {
         configure(task) {
diff --git a/gradle/providedConfiguration.gradle b/gradle/providedConfiguration.gradle
index 496fe08..923d207 100644
--- a/gradle/providedConfiguration.gradle
+++ b/gradle/providedConfiguration.gradle
@@ -16,5 +16,5 @@ sourceSets.main {
 }
 
 plugins.withType(IdeaPlugin) {
-    idea.module.scopes.PROVIDED.plus += configurations.provided
+    idea.module.scopes.PROVIDED.plus = [ configurations.provided ]
 }
diff --git a/gradle/strictCompile.gradle b/gradle/strictCompile.gradle
new file mode 100644
index 0000000..d076a2b
--- /dev/null
+++ b/gradle/strictCompile.gradle
@@ -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.
+ */
+
+allprojects {
+    ext.strictCompile = {
+        tasks.withType(AbstractCompile) {
+            options.compilerArgs << "-Werror" << "-Xlint:all" << "-Xlint:-options" << "-Xlint:-serial"
+        }
+    }
+}
\ No newline at end of file
diff --git a/gradle/taskOrdering.gradle b/gradle/taskOrdering.gradle
new file mode 100644
index 0000000..127f594
--- /dev/null
+++ b/gradle/taskOrdering.gradle
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+gradle.projectsEvaluated {
+    def groups = [:].withDefault { [] }
+
+    def codeQualityPrefixes = ["codenarc", "checkstyle", "classycle", "codeQuality"]
+    def taskContainers = subprojects*.tasks
+    taskContainers.each { tasks ->
+        tasks.each { task ->
+            if (codeQualityPrefixes.any { task.name.startsWith(it) }) {
+                groups.codeQuality << task
+            } else if (task instanceof Test && task.name.startsWith("test")) {
+                groups.unitTest << task
+            } else if (task instanceof Test && task.name.contains("integTest")) {
+                groups.integTest << task
+            }
+        }
+    }
+
+    groups.unitTest*.shouldRunAfter groups.codeQuality
+    groups.integTest*.shouldRunAfter groups.codeQuality, groups.unitTest
+}
\ No newline at end of file
diff --git a/gradle/testFixtures.gradle b/gradle/testFixtures.gradle
index ffc5fc5..4a7515f 100644
--- a/gradle/testFixtures.gradle
+++ b/gradle/testFixtures.gradle
@@ -31,7 +31,8 @@ sourceSets {
 }
 
 dependencies {
-    outputDirs sourceSets.testFixtures.output, sourceSets.main.output
+    outputDirs sourceSets.testFixtures.output
+    testFixturesUsageCompile project(project.path)
     testFixturesCompile libraries.junit, libraries.jmock, libraries.spock
 }
 
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 1350737..4fa5d19 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 07e3d3d..f21bba9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Mar 13 18:36:09 CET 2014
+#Tue Apr 07 09:01:34 AEST 2015
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions-snapshots/gradle-1.12-20140312230025+0000-bin.zip
+distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-2.4-20150417030942+0000-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 50074a8..eced905 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,15 +17,19 @@ include 'distributions'
 include 'baseServices'
 include 'baseServicesGroovy'
 include 'core'
-include 'coreImpl'
+include 'dependencyManagement'
 include 'wrapper'
 include 'cli'
 include 'launcher'
 include 'messaging'
 include 'resources'
+include 'resourcesHttp'
+include 'resourcesS3'
+include 'resourcesSftp'
 include 'plugins'
 include 'scala'
 include 'ide'
+include 'ideNative'
 include 'osgi'
 include 'maven'
 include 'announce'
@@ -35,11 +39,11 @@ include 'antlr'
 include 'ui'
 include 'openApi'
 include 'toolingApi'
+include 'toolingApiBuilders'
 include 'docs'
 include 'integTest'
 include 'sonar'
 include 'signing'
-include 'cpp'
 include 'ear'
 include 'native'
 include 'internalTesting'
@@ -53,8 +57,20 @@ include 'publish'
 include 'ivy'
 include 'jacoco'
 include 'buildInit'
-include 'languageBase'
+include 'platformBase'
+include 'platformNative'
+include 'platformJvm'
 include 'languageJvm'
+include 'languageJava'
+include 'languageGroovy'
+include 'languageNative'
+include 'languageScala'
+include 'pluginUse'
+include 'pluginDevelopment'
+include 'modelCore'
+include 'modelGroovy'
+include 'testingNative'
+include 'platformPlay'
 
 rootProject.name = 'gradle'
 rootProject.children.each {project ->
diff --git a/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/AnnouncePluginIntegrationTest.groovy b/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/AnnouncePluginIntegrationTest.groovy
new file mode 100644
index 0000000..52bf12a
--- /dev/null
+++ b/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/AnnouncePluginIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class AnnouncePluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getMainTask() {
+        return "tasks"
+    }
+}
diff --git a/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginIntegrationTest.groovy b/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginIntegrationTest.groovy
index b60b666..935ae88 100644
--- a/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginIntegrationTest.groovy
+++ b/subprojects/announce/src/integTest/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPluginIntegrationTest.groovy
@@ -19,23 +19,29 @@ import org.gradle.integtests.fixtures.WellBehavedPluginTest
 
 class BuildAnnouncementsPluginIntegrationTest extends WellBehavedPluginTest {
     @Override
-    String getPluginId() {
-        return "build-announcements"
-    }
-
-    @Override
     String getMainTask() {
         return "tasks"
     }
 
     def "does not blow up when a local notification mechanism is not available"() {
         buildFile << """
-apply plugin: 'java'
 apply plugin: 'build-announcements'
 """
 
         expect:
-        succeeds 'assemble'
+        succeeds 'tasks'
+    }
+
+    def "does not blow up in headless mode when a local notification mechanism is not available"() {
+        buildFile << """
+apply plugin: 'build-announcements'
+"""
+
+        given:
+        executer.withArgument("-Djava.awt.headless=false")
+
+        expect:
+        succeeds 'tasks'
     }
 
     def "can use custom announcer to receive announcements"() {
diff --git a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPlugin.groovy b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPlugin.groovy
index 0616880..2837239 100644
--- a/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPlugin.groovy
+++ b/subprojects/announce/src/main/groovy/org/gradle/api/plugins/announce/BuildAnnouncementsPlugin.groovy
@@ -25,7 +25,7 @@ import org.gradle.api.plugins.announce.internal.AnnouncingBuildListener
  */
 class BuildAnnouncementsPlugin implements Plugin<Project> {
     void apply(Project project) {
-        project.plugins.apply(AnnouncePlugin)
+        project.pluginManager.apply(AnnouncePlugin)
         AnnouncePluginExtension extension = project.extensions.findByType(AnnouncePluginExtension)
         def listener = new AnnouncingBuildListener(extension.local)
         project.gradle.addBuildListener(listener)
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 d78f09a..dd08a5c 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
@@ -16,10 +16,10 @@
 package org.gradle.api.plugins.announce.internal
 
 import org.gradle.api.InvalidUserDataException
-import org.gradle.api.JavaVersion
 import org.gradle.api.internal.ProcessOperations
 import org.gradle.api.plugins.announce.AnnouncePluginExtension
 import org.gradle.api.plugins.announce.Announcer
+import org.gradle.api.plugins.announce.internal.jdk6.AppleScriptBackedGrowlAnnouncer
 import org.gradle.internal.os.OperatingSystem
 
 class DefaultAnnouncerFactory implements AnnouncerFactory {
@@ -57,16 +57,13 @@ class DefaultAnnouncerFactory implements AnnouncerFactory {
             case "snarl":
                 return new Snarl(iconProvider)
             case "growl":
-                if (JavaVersion.current().java6Compatible) {
+                if (!java.awt.GraphicsEnvironment.isHeadless()) {
                     try {
-                        return getClass().getClassLoader().loadClass("org.gradle.api.plugins.announce.internal.jdk6.AppleScriptBackedGrowlAnnouncer").newInstance(iconProvider)
+                        return new AppleScriptBackedGrowlAnnouncer(iconProvider)
                     }
                     catch (AnnouncerUnavailableException e) {
                         // Ignore and fall back to growl notify
                     }
-                    catch (ClassNotFoundException e) {
-                        // Ignore and fall back to growl notify
-                    }
                 }
                 return new GrowlNotifyBackedAnnouncer(processOperations, iconProvider)
             default:
diff --git a/subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties b/subprojects/announce/src/main/resources/META-INF/gradle-plugins/org.gradle.announce.properties
similarity index 100%
rename from subprojects/announce/src/main/resources/META-INF/gradle-plugins/announce.properties
rename to subprojects/announce/src/main/resources/META-INF/gradle-plugins/org.gradle.announce.properties
diff --git a/subprojects/announce/src/main/resources/META-INF/gradle-plugins/build-announcements.properties b/subprojects/announce/src/main/resources/META-INF/gradle-plugins/org.gradle.build-announcements.properties
similarity index 100%
rename from subprojects/announce/src/main/resources/META-INF/gradle-plugins/build-announcements.properties
rename to subprojects/announce/src/main/resources/META-INF/gradle-plugins/org.gradle.build-announcements.properties
diff --git a/subprojects/antlr/antlr.gradle b/subprojects/antlr/antlr.gradle
index 9a1eca4..e4b1205 100644
--- a/subprojects/antlr/antlr.gradle
+++ b/subprojects/antlr/antlr.gradle
@@ -13,16 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-dependencies {
-    compile libraries.groovy
+apply from: "$rootDir/gradle/providedConfiguration.gradle"
 
+dependencies {
     compile project(':core')
     compile project(':plugins')
 
+    provided "antlr:antlr:2.7.7 at jar"
+
     compile libraries.slf4j_api
-    compile libraries.ant
-    compile libraries.ant_antlr
-    compile libraries.antlr
 }
 
 useTestFixtures()
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AbstractAntlrIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AbstractAntlrIntegrationTest.groovy
new file mode 100644
index 0000000..d528409
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AbstractAntlrIntegrationTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+abstract class AbstractAntlrIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer.withArgument("-i")
+        writeBuildFile()
+    }
+
+    abstract String getAntlrDependency()
+
+    void assertAntlrVersion(int version) {
+        assert output.contains("Processing with ANTLR $version")
+    }
+
+    private void writeBuildFile() {
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "antlr"
+
+            repositories() {
+                jcenter()
+            }
+
+            dependencies {
+                antlr '$antlrDependency'
+            }
+        """
+    }
+
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr2PluginIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr2PluginIntegrationTest.groovy
new file mode 100644
index 0000000..e9cae24
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr2PluginIntegrationTest.groovy
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr
+
+import org.gradle.util.TextUtil
+
+class Antlr2PluginIntegrationTest extends AbstractAntlrIntegrationTest {
+
+    String antlrDependency = "antlr:antlr:2.7.7"
+
+    def "analyze and build good grammar"() {
+        goodGrammar()
+        goodProgram()
+
+        expect:
+        succeeds("generateGrammarSource")
+        assertAntlrVersion(2)
+        assertGrammarSourceGenerated("TestGrammar")
+        assertGrammarSourceGenerated("AnotherGrammar")
+        succeeds("build")
+    }
+
+    def "analyze bad grammar"() {
+        when:
+        badGrammar()
+        then:
+        fails("generateGrammarSource")
+        output.contains("TestGrammar.g:7:24: unexpected token: extra")
+        output.contains("TestGrammar.g:9:13: unexpected token: mexpr")
+        output.contains("TestGrammar.g:7:24: rule classDef trapped:")
+        output.contains("TestGrammar.g:7:24: unexpected token: extra")
+        assertAntlrVersion(2)
+        errorOutput.contains(TextUtil.toPlatformLineSeparators("""
+* What went wrong:
+Execution failed for task ':generateGrammarSource'.
+> There was 1 error during grammar generation
+   > ANTLR Panic: Exiting due to errors.
+"""))
+    }
+
+    def "uses antlr v2 if no explicit dependency is set"() {
+        buildFile.text = """
+            apply plugin: "java"
+            apply plugin: "antlr"
+
+            repositories() {
+                jcenter()
+            }"""
+
+        goodGrammar()
+        goodProgram()
+
+        expect:
+        succeeds("generateGrammarSource")
+        assertAntlrVersion(2)
+        assertGrammarSourceGenerated("TestGrammar")
+        succeeds("build")
+    }
+
+    private goodGrammar() {
+        file("src/main/antlr/TestGrammar.g") << """class TestGrammar extends Parser;
+            options {
+                buildAST = true;
+            }
+
+            expr:   mexpr (PLUS^ mexpr)* SEMI!
+                ;
+
+            mexpr
+                :   atom (STAR^ atom)*
+                ;
+
+            atom:   INT
+                ;"""
+
+        file("src/main/antlr/AnotherGrammar.g") << """class AnotherGrammar extends Parser;
+            options {
+                buildAST = true;
+                importVocab = TestGrammar;
+            }
+
+            expr:   mexpr (PLUS^ mexpr)* SEMI!
+                ;
+
+            mexpr
+                :   atom (STAR^ atom)*
+                ;
+
+            atom:   INT
+                ;"""
+
+    }
+
+    private goodProgram() {
+        file("src/main/java/com/example/test/Test.java") << """
+            import antlr.Token;
+            import antlr.TokenStream;
+            import antlr.TokenStreamException;
+
+            public class Test {
+                public static void main(String[] args) {
+                    TestGrammar parser = new TestGrammar(new DummyTokenStream());
+                }
+
+                private static class DummyTokenStream implements TokenStream {
+                    public Token nextToken() throws TokenStreamException {
+                        return null;
+                    }
+                }
+            }
+        """
+    }
+
+    private badGrammar() {
+        file("src/main/antlr/TestGrammar.g") << """class TestGrammar extends Parser;
+            options {
+                buildAST = true;
+            }
+
+            expr:   mexpr (PLUS^ mexpr)* SEMI!
+                ; some extra stuff
+
+            mexpr
+                :   atom (STAR^ atom)*
+                ;
+
+            atom:   INT
+                ;"""
+    }
+
+    private void assertGrammarSourceGenerated(String grammarName) {
+        assert file("build/generated-src/antlr/main/${grammarName}.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}.smap").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}TokenTypes.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}TokenTypes.txt").exists()
+    }
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr3PluginIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr3PluginIntegrationTest.groovy
new file mode 100644
index 0000000..05ed4b4
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr3PluginIntegrationTest.groovy
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr
+
+class Antlr3PluginIntegrationTest extends AbstractAntlrIntegrationTest {
+
+    String antlrDependency = "org.antlr:antlr:3.5.2"
+
+    def "analyze good grammar"() {
+        goodGrammar()
+
+        expect:
+        succeeds("generateGrammarSource")
+
+        assertGrammarSourceGenerated("Test")
+        assertGrammarSourceGenerated("AnotherGrammar")
+        assertAntlrVersion(3)
+    }
+
+    private void assertGrammarSourceGenerated(String grammarName) {
+        assert file("build/generated-src/antlr/main/${grammarName}.tokens").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Lexer.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Parser.java").exists()
+    }
+
+    def "analyze bad grammar"() {
+        badGrammar()
+
+        expect:
+        fails("generateGrammarSource")
+        assertAntlrVersion(3)
+    }
+
+    private goodGrammar() {
+        file("src/main/antlr/Test.g") << """grammar Test;
+            list    :   item (item)*
+                    ;
+
+            item    :   
+                ID
+                | INT
+                ;
+
+            ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
+                ;
+
+            INT :   '0'..'9'+
+                ;
+        """
+
+        file("src/main/antlr/AnotherGrammar.g") << """grammar AnotherGrammar;
+            list    :   item (item)*
+                    ;
+
+            item    :
+                ID
+                | INT
+                ;
+
+            ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
+                ;
+
+            INT :   '0'..'9'+
+                ;
+        """
+    }
+
+    private badGrammar() {
+        file("src/main/antlr/Test.g") << """grammar Test;
+            list    :   item (item)*
+                    ; some extra stuff
+
+            item    :   
+                ID
+                | INT
+                ;
+
+            ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
+                ;
+
+            INT :   '0'..'9'+
+                ;
+        """
+    }
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr4PluginIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr4PluginIntegrationTest.groovy
new file mode 100644
index 0000000..5aa1904
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/Antlr4PluginIntegrationTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr
+
+class Antlr4PluginIntegrationTest extends AbstractAntlrIntegrationTest {
+
+    String antlrDependency = "org.antlr:antlr4:4.3"
+
+    def "analyze good grammar"() {
+        goodGrammar()
+
+        expect:
+        succeeds("generateGrammarSource")
+        assertGrammarSourceGenerated("Test")
+        assertGrammarSourceGenerated("Another")
+        assertAntlrVersion(4)
+    }
+
+    private void assertGrammarSourceGenerated(String grammarName) {
+        assert file("build/generated-src/antlr/main/${grammarName}.tokens").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}BaseListener.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Lexer.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Lexer.tokens").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Listener.java").exists()
+        assert file("build/generated-src/antlr/main/${grammarName}Parser.java").exists()
+    }
+
+    def "analyze bad grammar"() {
+        badGrammar()
+
+        expect:
+        fails("generateGrammarSource")
+        assertAntlrVersion(4)
+    }
+
+    private goodGrammar() {
+        file("src/main/antlr/Test.g4") << """grammar Test;
+            r  : 'hello' ID ;        
+            ID : [a-z]+ ;  
+            WS : [ \\t\\r\\n]+ -> skip ;
+        """
+
+        file("src/main/antlr/Another.g4") << """grammar Another;
+            r  : 'hello' ID ;
+            ID : [a-z]+ ;
+            WS : [ \\t\\r\\n]+ -> skip ;
+        """
+    }
+
+    private badGrammar() {
+        file("src/main/antlr/Test.g4") << """grammar Test;
+            r  : 'hello' ID ;    extrastuff
+            ID : [a-z]+ ;            
+            WS : [ \\t\\r\\n]+ -> skip ;
+        """
+    }
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AntlrPluginIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AntlrPluginIntegrationTest.groovy
new file mode 100644
index 0000000..3dfb749
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/AntlrPluginIntegrationTest.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.api.plugins.antlr
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class AntlrPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getMainTask() {
+        return "build"
+    }
+
+    def "can handle grammar in nested folders"(){
+        given:
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "antlr"
+
+            repositories() {
+                jcenter()
+            }
+        """
+        and:
+
+        file("src/main/antlr/org/acme/TestGrammar.g") << """ class TestGrammar extends Parser;
+        options {
+            buildAST = true;
+        }
+
+        expr:   mexpr (PLUS^ mexpr)* SEMI!
+        ;
+
+        mexpr
+        :   atom (STAR^ atom)*
+        ;
+
+        atom:   INT
+        ;"""
+
+        when:
+        succeeds("generateGrammarSource")
+
+        then:
+        file("build/generated-src/antlr/main/TestGrammar.java").exists()
+        file("build/generated-src/antlr/main/TestGrammar.smap").exists()
+        file("build/generated-src/antlr/main/TestGrammarTokenTypes.java").exists()
+        file("build/generated-src/antlr/main/TestGrammarTokenTypes.txt").exists()
+
+    }
+
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/IncrementalAntlrTaskIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/IncrementalAntlrTaskIntegrationTest.groovy
new file mode 100644
index 0000000..7e5fd0e
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/api/plugins/antlr/IncrementalAntlrTaskIntegrationTest.groovy
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr
+
+class IncrementalAntlrTaskIntegrationTest extends AbstractAntlrIntegrationTest {
+    String antlrDependency = "org.antlr:antlr:3.5.2"
+
+    def test1TokenFile = file("build/generated-src/antlr/main/Test1.tokens")
+    def test1LexerFile = file("build/generated-src/antlr/main/Test1Lexer.java")
+    def test1ParserFile = file("build/generated-src/antlr/main/Test1Parser.java")
+
+    def test2TokenFile = file("build/generated-src/antlr/main/Test2.tokens")
+    def test2LexerFile = file("build/generated-src/antlr/main/Test2Lexer.java")
+    def test2ParserFile = file("build/generated-src/antlr/main/Test2Parser.java")
+
+    def "changed task inputs handled incrementally"() {
+        when:
+        grammar("Test1", "Test2")
+        then:
+        succeeds("generateGrammarSource")
+
+        when:
+        def test1TokensFileSnapshot = test1TokenFile.snapshot()
+        def test1LexerFileSnapshot = test1LexerFile.snapshot()
+        def test1ParserFileSnapshot = test1ParserFile.snapshot()
+
+        def test2TokensFileSnapshot = test2TokenFile.snapshot()
+        def test2LexerFileSnapshot = test2LexerFile.snapshot()
+        def test2ParserFileSnapshot = test2ParserFile.snapshot()
+
+        changedGrammar("Test2")
+
+        then:
+        succeeds("generateGrammarSource")
+        test1TokenFile.assertHasNotChangedSince(test1TokensFileSnapshot);
+        test1LexerFile.assertHasNotChangedSince(test1LexerFileSnapshot);
+        test1ParserFile.assertHasNotChangedSince(test1ParserFileSnapshot);
+
+        test2TokenFile.assertHasChangedSince(test2TokensFileSnapshot);
+        test2LexerFile.assertHasChangedSince(test2LexerFileSnapshot);
+        test2ParserFile.assertHasChangedSince(test2ParserFileSnapshot);
+    }
+
+    def "added grammar is handled incrementally"() {
+        when:
+        grammar("Test1")
+        then:
+        succeeds("generateGrammarSource")
+
+        when:
+        def test1TokensFileSnapshot = test1TokenFile.snapshot()
+        def test1LexerFileSnapshot = test1LexerFile.snapshot()
+        def test1ParserFileSnapshot = test1ParserFile.snapshot()
+
+        !test2TokenFile.exists()
+        !test2LexerFile.exists()
+        !test2ParserFile.exists()
+
+        grammar("Test2")
+
+        then:
+        succeeds("generateGrammarSource")
+        test1TokenFile.assertHasNotChangedSince(test1TokensFileSnapshot);
+        test1LexerFile.assertHasNotChangedSince(test1LexerFileSnapshot);
+        test1ParserFile.assertHasNotChangedSince(test1ParserFileSnapshot);
+
+        test2TokenFile.exists()
+        test2LexerFile.exists()
+        test2ParserFile.exists()
+
+    }
+
+    def "rerun when arguments changed"() {
+        when:
+        grammar("Test1")
+        then:
+        succeeds("generateGrammarSource")
+
+        when:
+        def test1TokensFileSnapshot = test1TokenFile.snapshot()
+        def test1LexerFileSnapshot = test1LexerFile.snapshot()
+        def test1ParserFileSnapshot = test1ParserFile.snapshot()
+
+        buildFile << """
+        generateGrammarSource {
+            arguments << '-dfa'
+        }
+        """
+
+        then:
+        succeeds("generateGrammarSource")
+        test1TokenFile.assertHasChangedSince(test1TokensFileSnapshot);
+        test1LexerFile.assertHasChangedSince(test1LexerFileSnapshot);
+        test1ParserFile.assertHasChangedSince(test1ParserFileSnapshot);
+    }
+
+    def "output for removed grammar file is not handled correctly"() {
+        when:
+        grammar("Test1", "Test2")
+        then:
+        succeeds("generateGrammarSource")
+
+        test1TokenFile.exists()
+        test1LexerFile.exists()
+        test1ParserFile.exists()
+
+        test2TokenFile.exists()
+        test2LexerFile.exists()
+        test2ParserFile.exists()
+
+        when:
+        removedGrammar("Test1")
+
+        then:
+        succeeds("generateGrammarSource")
+        !test1TokenFile.exists();
+        !test1LexerFile.exists();
+        !test1ParserFile.exists();
+    }
+
+    def grammar(String... ids) {
+        ids.each { id ->
+            file("src/main/antlr/${id}.g") << """grammar ${id};
+            list    :   item (item)*
+                    ;
+
+            item    :
+                ID
+                | INT
+                ;
+
+            ID  :   ('a'..'z'|'_') ('a'..'z'|'0'..'9'|'_')*
+                ;
+
+            INT :   '0'..'9'+
+                ;
+        """
+        }
+    }
+
+    def changedGrammar(String... ids) {
+        ids.each { id ->
+            file("src/main/antlr/${id}.g").text = """grammar ${id};
+             list    :   item (item)*
+                    ;
+
+            item    :
+                ID
+                | INT
+                ;
+
+            ID  :   ('A'..'Z'|'_') ('A'..'Z'|'0'..'9'|'_')*
+                ;
+
+            INT :   '0'..'9'+
+                ;
+        """
+        }
+    }
+
+    def removedGrammar(String... ids) {
+        ids.each { id ->
+            file("src/main/antlr/${id}.g").delete()
+        }
+    }
+}
diff --git a/subprojects/antlr/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy b/subprojects/antlr/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy
new file mode 100644
index 0000000..404ddcc
--- /dev/null
+++ b/subprojects/antlr/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.samples
+
+import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Rule
+import org.junit.Test
+
+class SamplesAntlrIntegrationTest extends AbstractIntegrationTest {
+
+    @Rule public final Sample sample = new Sample(testDirectoryProvider, 'antlr')
+
+    @Test
+    public void canBuild() {
+        TestFile projectDir = sample.dir
+
+        // Build and test projects
+        executer.inDirectory(projectDir).withTasks('clean', 'build').run()
+
+        // Check tests have run
+        def result = new DefaultTestExecutionResult(projectDir)
+        result.assertTestClassesExecuted('org.gradle.GrammarTest')
+    }
+}
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 56dfe62..3dc1a61 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
@@ -18,10 +18,12 @@ package org.gradle.api.plugins.antlr;
 
 import org.gradle.api.Action;
 import org.gradle.api.Plugin;
+import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.DependencySet;
+import org.gradle.api.artifacts.ResolvableDependencies;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultSourceSet;
 import org.gradle.api.plugins.JavaPlugin;
 import org.gradle.api.plugins.JavaPluginConvention;
@@ -37,7 +39,7 @@ import static org.gradle.api.plugins.JavaPlugin.COMPILE_CONFIGURATION_NAME;
 /**
  * A plugin for adding Antlr support to {@link JavaPlugin java projects}.
  */
-public class AntlrPlugin implements Plugin<ProjectInternal> {
+public class AntlrPlugin implements Plugin<Project> {
     public static final String ANTLR_CONFIGURATION_NAME = "antlr";
     private final FileResolver fileResolver;
 
@@ -46,15 +48,37 @@ public class AntlrPlugin implements Plugin<ProjectInternal> {
         this.fileResolver = fileResolver;
     }
 
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(JavaPlugin.class);
+    public void apply(final Project project) {
+        project.getPluginManager().apply(JavaPlugin.class);
 
         // set up a configuration named 'antlr' for the user to specify the antlr libs to use in case
         // they want a specific version etc.
-        Configuration antlrConfiguration = project.getConfigurations().create(ANTLR_CONFIGURATION_NAME).setVisible(false)
-                .setTransitive(false).setDescription("The Antlr libraries to be used for this project.");
+        final Configuration antlrConfiguration = project.getConfigurations().create(ANTLR_CONFIGURATION_NAME)
+                .setVisible(false)
+                .setDescription("The Antlr libraries to be used for this project.");
+
+        antlrConfiguration.getIncoming().beforeResolve(new Action<ResolvableDependencies>() {
+            public void execute(ResolvableDependencies resolvableDependencies) {
+                DependencySet dependencies = antlrConfiguration.getDependencies();
+                if (dependencies.isEmpty()) {
+                    dependencies.add(project.getDependencies().create("antlr:antlr:2.7.7 at jar"));
+                }
+            }
+        });
+
         project.getConfigurations().getByName(COMPILE_CONFIGURATION_NAME).extendsFrom(antlrConfiguration);
 
+        // Wire the antlr configuration into all antlr tasks
+        project.getTasks().withType(AntlrTask.class, new Action<AntlrTask>() {
+            public void execute(AntlrTask antlrTask) {
+                antlrTask.getConventionMapping().map("antlrClasspath", new Callable<Object>() {
+                    public Object call() throws Exception {
+                        return project.getConfigurations().getByName(ANTLR_CONFIGURATION_NAME);
+                    }
+                });
+            }
+        });
+
         project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().all(
                 new Action<SourceSet>() {
                     public void execute(SourceSet sourceSet) {
@@ -78,15 +102,7 @@ public class AntlrPlugin implements Plugin<ProjectInternal> {
                         // 3) set up convention mapping for default sources (allows user to not have to specify)
                         antlrTask.setSource(antlrDirectoryDelegate.getAntlr());
 
-                        // 4) set up convention mapping for handling the 'antlr' dependency configuration
-                        antlrTask.getConventionMapping().map("antlrClasspath", new Callable<Object>() {
-                            public Object call() throws Exception {
-                                return project.getConfigurations().getByName(ANTLR_CONFIGURATION_NAME).copy()
-                                        .setTransitive(true);
-                            }
-                        });
-
-                        // 5) Set up the Antlr output directory (adding to javac inputs!)
+                        // 4) Set up the Antlr output directory (adding to javac inputs!)
                         final String outputDirectoryName = String.format("%s/generated-src/antlr/%s",
                                 project.getBuildDir(), sourceSet.getName());
                         final File outputDirectory = new File(outputDirectoryName);
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 44a2696..88eca06 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,44 +16,39 @@
 
 package org.gradle.api.plugins.antlr;
 
-import org.apache.tools.ant.taskdefs.optional.ANTLR;
-import org.apache.tools.ant.types.Path;
+import org.gradle.api.Action;
 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.*;
+import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+import org.gradle.internal.Factory;
+import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.GFileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+import javax.inject.Inject;
 import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * <p>Generates parsers from Antlr grammars.</p>
- *
- * <p>Most properties here are self-evident, but I wanted to highlight one in particular: {@link #setAntlrClasspath} is
- * used to define the classpath that should be passed along to the Ant {@link ANTLR} task as its classpath.  That is the
- * classpath it uses to perform generation execution.  This <b>should</b> really only require the antlr jar.  In {@link
- * AntlrPlugin} usage, this would happen simply by adding your antlr jar into the 'antlr' dependency configuration
- * created and exposed by the {@link AntlrPlugin} itself.</p>
+ * Generates parsers from Antlr grammars.
  */
 public class AntlrTask extends SourceTask {
-    private static final Logger LOGGER = LoggerFactory.getLogger(AntlrTask.class);
 
     private boolean trace;
     private boolean traceLexer;
     private boolean traceParser;
     private boolean traceTreeWalker;
+    private List<String> arguments = new ArrayList<String>();
 
     private FileCollection antlrClasspath;
 
     private File outputDirectory;
+    private String maxHeapSize;
 
     /**
      * Specifies that all rules call {@code traceIn}/{@code traceOut}.
@@ -100,6 +95,28 @@ public class AntlrTask extends SourceTask {
     }
 
     /**
+     * The maximum heap size for the forked antlr process (ex: '1g').
+     */
+    public String getMaxHeapSize() {
+        return maxHeapSize;
+    }
+
+    public void setMaxHeapSize(String maxHeapSize) {
+        this.maxHeapSize = maxHeapSize;
+    }
+
+    public void setArguments(List<String> arguments) {
+        if (arguments != null) {
+            this.arguments = arguments;
+        }
+    }
+
+    @Input
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
      * Returns the directory to generate the parser source files into.
      *
      * @return The output directory.
@@ -133,41 +150,60 @@ public class AntlrTask extends SourceTask {
      *
      * @param antlrClasspath The Ant task implementation classpath. Must not be null.
      */
-    public void setAntlrClasspath(FileCollection antlrClasspath) {
+    protected void setAntlrClasspath(FileCollection antlrClasspath) {
         this.antlrClasspath = antlrClasspath;
     }
 
+    @Inject
+    protected Factory<WorkerProcessBuilder> getWorkerProcessBuilderFactory() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
-    public void generate() {
-        // Determine the grammar files and the proper ordering amongst them
-        XRef xref = new MetadataExtracter().extractMetadata(getSource());
-        List<GenerationPlan> generationPlans = new GenerationPlanBuilder(outputDirectory).buildGenerationPlans(xref);
-
-        for (GenerationPlan generationPlan : generationPlans) {
-            if (!generationPlan.isOutOfDate()) {
-                LOGGER.info("grammar [" + generationPlan.getId() + "] was up-to-date; skipping");
-                continue;
+    public void execute(IncrementalTaskInputs inputs) {
+        final Set<File> grammarFiles = new HashSet<File>();
+        final Set<File> sourceFiles = getSource().getFiles();
+        final AtomicBoolean cleanRebuild = new AtomicBoolean();
+        inputs.outOfDate(
+                new Action<InputFileDetails>() {
+                    public void execute(InputFileDetails details) {
+                        File input = details.getFile();
+                        if (sourceFiles.contains(input)) {
+                            grammarFiles.add(input);
+                        } else {
+                            // classpath change?
+                            cleanRebuild.set(true);
+                        }
+                    }
+                }
+        );
+        inputs.removed(new Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails details) {
+                if (details.isRemoved()) {
+                    cleanRebuild.set(true);
+                }
             }
+        });
+        if (cleanRebuild.get()) {
+            GFileUtils.cleanDirectory(outputDirectory);
+            grammarFiles.addAll(sourceFiles);
+        }
 
-            LOGGER.info("performing grammar generation [" + generationPlan.getId() + "]");
-
-            //noinspection ResultOfMethodCallIgnored
-            GFileUtils.mkdirs(generationPlan.getGenerationDirectory());
+        AntlrWorkerManager manager = new AntlrWorkerManager();
+        AntlrSpec spec = new AntlrSpecFactory().create(this, grammarFiles);
+        AntlrResult result = manager.runWorker(getProject().getProjectDir(), getWorkerProcessBuilderFactory(), getAntlrClasspath(), spec);
+        evaluate(result);
+    }
 
-            ANTLR antlr = new ANTLR();
-            antlr.setProject(getAnt().getAntProject());
-            Path antlrTaskClasspath = antlr.createClasspath();
-            for (File dep : getAntlrClasspath()) {
-                antlrTaskClasspath.createPathElement().setLocation(dep);
-            }
-            antlr.setTrace(trace);
-            antlr.setTraceLexer(traceLexer);
-            antlr.setTraceParser(traceParser);
-            antlr.setTraceTreeWalker(traceTreeWalker);
-            antlr.setOutputdirectory(generationPlan.getGenerationDirectory());
-            antlr.setTarget(generationPlan.getSource());
-
-            antlr.execute();
+    private void evaluate(AntlrResult result) {
+        int errorCount = result.getErrorCount();
+        if (errorCount == 1) {
+            throw new AntlrSourceGenerationException("There was 1 error during grammar generation", result.getException());
+        } else if (errorCount > 1) {
+            throw new AntlrSourceGenerationException("There were "
+                    + errorCount
+                    + " errors during grammar generation", result.getException());
         }
     }
 }
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrExecuter.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrExecuter.java
new file mode 100644
index 0000000..3f474a9
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrExecuter.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.plugins.antlr.internal.antlr2.GenerationPlan;
+import org.gradle.api.plugins.antlr.internal.antlr2.GenerationPlanBuilder;
+import org.gradle.api.plugins.antlr.internal.antlr2.MetadataExtracter;
+import org.gradle.api.plugins.antlr.internal.antlr2.XRef;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+
+public class AntlrExecuter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AntlrExecuter.class);
+
+    AntlrResult runAntlr(AntlrSpec spec) throws IOException, InterruptedException {
+        String[] commandLine = toArray(spec.asCommandLineWithFiles());
+
+        // Try ANTLR 4
+        try {
+            Object toolObj = loadTool("org.antlr.v4.Tool", commandLine);
+            LOGGER.info("Processing with ANTLR 4");
+            return processV4(toolObj);
+        } catch (ClassNotFoundException e) {
+            LOGGER.debug("ANTLR 4 not found on classpath");
+        }
+
+        // Try ANTLR 3
+        try {
+            Object toolObj = loadTool("org.antlr.Tool", commandLine);
+            LOGGER.info("Processing with ANTLR 3");
+            return processV3(toolObj);
+        } catch (ClassNotFoundException e) {
+            LOGGER.debug("ANTLR 3 not found on classpath");
+        }
+
+        // Try ANTLR 2
+        try {
+            Object toolObj = loadTool("antlr.Tool", null);
+            LOGGER.info("Processing with ANTLR 2");
+            return processV2(toolObj, spec.asCommandLineWithoutFiles(), spec.getGrammarFiles(), spec.getOutputDirectory());
+        } catch (ClassNotFoundException e) {
+            LOGGER.debug("ANTLR 2 not found on classpath");
+        }
+
+        throw new IllegalStateException("No Antlr implementation available");
+    }
+
+    private String[] toArray(List<String> strings) {
+        return strings.toArray(new String[strings.size()]);
+    }
+
+    /**
+     * Utility method to create an instance of the Tool class.
+     * @throws ClassNotFoundException if class was not on the runtime classpath.
+     */
+    Object loadTool(String className, String[] args) throws ClassNotFoundException {
+        try {
+            Class<?> toolClass = Class.forName(className); // ok to use caller classloader
+            if (args == null) {
+                return toolClass.newInstance();
+            } else {
+                Constructor<?> constructor = toolClass.getConstructor(String[].class);
+                return constructor.newInstance(new Object[]{args});
+            }
+        } catch (ClassNotFoundException e) {
+            throw e;
+        } catch (InvocationTargetException e) {
+            throw new GradleException("Failed to load ANTLR", e.getCause());
+        } catch (Exception e) {
+            throw new GradleException("Failed to load ANTLR", e);
+        }
+    }
+
+    AntlrResult processV2(Object tool, List<String> arguments, Set<File> grammarFiles, File outputDirectory) {
+        XRef xref = new MetadataExtracter().extractMetadata(grammarFiles);
+        List<GenerationPlan> generationPlans = new GenerationPlanBuilder(outputDirectory).buildGenerationPlans(xref);
+        for (GenerationPlan generationPlan : generationPlans) {
+            String[] argArr = arguments.toArray(new String[arguments.size() + 1]);
+            argArr[arguments.size()] = generationPlan.getSource().getAbsolutePath();
+            JavaReflectionUtil.method(tool, Integer.class, "doEverything", String[].class).invoke(tool, new Object[]{argArr});
+        }
+        return new AntlrResult(0);  // ANTLR 2 always returning 0
+    }
+
+    AntlrResult processV3(Object tool) {
+        JavaReflectionUtil.method(tool, Void.class, "process").invoke(tool);
+        return new AntlrResult(JavaReflectionUtil.method(tool, Integer.class, "getNumErrors").invoke(tool));
+    }
+
+    AntlrResult processV4(Object tool) {
+        JavaReflectionUtil.method(tool, Void.class, "processGrammarsOnCommandLine").invoke(tool);
+        return new AntlrResult(JavaReflectionUtil.method(tool, Integer.class, "getNumErrors").invoke(tool));
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrResult.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrResult.java
new file mode 100644
index 0000000..7731834
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrResult.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import java.io.Serializable;
+
+public class AntlrResult implements Serializable {
+
+    private final int errorCount;
+    private final Exception exception;
+
+    public AntlrResult(int errorCount) {
+        this(errorCount, null);
+    }
+    public AntlrResult(int errorCount, Exception exception) {
+        this.errorCount = errorCount;
+        this.exception = exception;
+    }
+
+    public int getErrorCount() {
+        return errorCount;
+    }
+
+    public Exception getException() {
+        return exception;
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceGenerationException.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceGenerationException.java
new file mode 100644
index 0000000..2f07dc3
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceGenerationException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+
+ at Contextual
+public class AntlrSourceGenerationException extends GradleException {
+    public AntlrSourceGenerationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
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 006320d..a66d352 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
@@ -32,6 +32,7 @@ public class AntlrSourceVirtualDirectoryImpl implements AntlrSourceVirtualDirect
         final String displayName = String.format("%s Antlr source", parentDisplayName);
         antlr = new DefaultSourceDirectorySet(displayName, fileResolver);
         antlr.getFilter().include("**/*.g");
+        antlr.getFilter().include("**/*.g4");  
     }
 
     public SourceDirectorySet getAntlr() {
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpec.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpec.java
new file mode 100644
index 0000000..c745dc4
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpec.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+public class AntlrSpec implements Serializable {
+
+    private List<String> arguments;
+    private Set<File> grammarFiles;
+    private String maxHeapSize;
+    private File outputDirectory;
+
+    public AntlrSpec(List<String> arguments, Set<File> grammarFiles, File outputDirectory, String maxHeapSize) {
+        this.arguments = arguments;
+        this.grammarFiles = grammarFiles;
+        this.outputDirectory = outputDirectory;
+        this.maxHeapSize = maxHeapSize;
+    }
+
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    public Set<File> getGrammarFiles() {
+        return grammarFiles;
+    }
+
+    public String getMaxHeapSize() {
+        return maxHeapSize;
+    }
+
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    public List<String> asCommandLineWithoutFiles() {
+        List<String> commandLine = Lists.newLinkedList(arguments);
+
+        commandLine.add("-o");
+        commandLine.add(getOutputDirectory().getAbsolutePath());
+
+        return commandLine;
+    }
+
+    public List<String> asCommandLineWithFiles() {
+        List<String> commandLine = Lists.newLinkedList(asCommandLineWithoutFiles());
+
+        for (File file : getGrammarFiles()) {
+            commandLine.add(file.getAbsolutePath());
+        }
+
+        return commandLine;
+    }
+
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactory.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactory.java
new file mode 100644
index 0000000..d2ce8d2
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.plugins.antlr.AntlrTask;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+public class AntlrSpecFactory {
+
+    public AntlrSpec create(AntlrTask antlrTask, Set<File> grammarFiles) {
+        List<String> arguments = Lists.newLinkedList(antlrTask.getArguments());
+
+        if (antlrTask.isTrace() && !arguments.contains("-trace")) {
+            arguments.add("-trace");
+        }
+        if (antlrTask.isTraceLexer() && !arguments.contains("-traceLexer")) {
+            arguments.add("-traceLexer");
+        }
+        if (antlrTask.isTraceParser() && !arguments.contains("-traceParser")) {
+            arguments.add("-traceParser");
+        }
+        if (antlrTask.isTraceTreeWalker() && !arguments.contains("-traceTreeWalker")) {
+            arguments.add("-traceTreeWalker");
+        }
+
+        return new AntlrSpec(arguments, grammarFiles, antlrTask.getOutputDirectory(), antlrTask.getMaxHeapSize());
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClient.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClient.java
new file mode 100644
index 0000000..d6d433f
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClient.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+public class AntlrWorkerClient implements AntlrWorkerClientProtocol {
+
+    private AntlrResult result;
+
+    public void executed(AntlrResult result) {
+        this.result = result;
+    }
+
+    public AntlrResult getResult() {
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClientProtocol.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClientProtocol.java
new file mode 100644
index 0000000..5c79bc0
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerClientProtocol.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+public interface AntlrWorkerClientProtocol {
+    void executed(AntlrResult result);
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerManager.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerManager.java
new file mode 100644
index 0000000..ea4ec09
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.internal.Factory;
+import org.gradle.process.internal.JavaExecHandleBuilder;
+import org.gradle.process.internal.WorkerProcess;
+import org.gradle.process.internal.WorkerProcessBuilder;
+
+import java.io.File;
+
+public class AntlrWorkerManager {
+
+    public AntlrResult runWorker(File workingDir, Factory<WorkerProcessBuilder> workerFactory, FileCollection antlrClasspath, AntlrSpec spec) {
+
+        WorkerProcess process = createWorkerProcess(workingDir, workerFactory, antlrClasspath, spec);
+        process.start();
+
+        AntlrWorkerClient clientCallBack = new AntlrWorkerClient();
+        process.getConnection().addIncoming(AntlrWorkerClientProtocol.class, clientCallBack);
+        process.getConnection().connect();
+
+        process.waitForStop();
+
+        return clientCallBack.getResult();
+    }
+
+    private WorkerProcess createWorkerProcess(File workingDir, Factory<WorkerProcessBuilder> workerFactory, FileCollection antlrClasspath, AntlrSpec spec) {
+        WorkerProcessBuilder builder = workerFactory.create();
+        builder.setBaseName("Gradle ANTLR Worker");
+
+        if (antlrClasspath != null) {
+            builder.applicationClasspath(antlrClasspath);
+        }
+        builder.sharedPackages(new String[] {"antlr", "org.antlr"});
+        JavaExecHandleBuilder javaCommand = builder.getJavaCommand();
+        javaCommand.setWorkingDir(workingDir);
+        javaCommand.setMaxHeapSize(spec.getMaxHeapSize());
+        javaCommand.systemProperty("ANTLR_DO_NOT_EXIT", "true");
+        javaCommand.redirectErrorStream();
+        return builder.worker(new AntlrWorkerServer(spec)).build();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerServer.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerServer.java
new file mode 100644
index 0000000..07ccef9
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrWorkerServer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal;
+
+import org.gradle.api.Action;
+import org.gradle.process.internal.WorkerProcessContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+
+public class AntlrWorkerServer implements Action<WorkerProcessContext>, Serializable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AntlrWorkerServer.class);
+
+    private AntlrSpec spec;
+
+    public AntlrWorkerServer(AntlrSpec spec) {
+        this.spec = spec;
+    }
+
+    public void execute(WorkerProcessContext context) {
+        final AntlrResult result = execute();
+        final AntlrWorkerClientProtocol clientProtocol = context.getServerConnection().addOutgoing(AntlrWorkerClientProtocol.class);
+        context.getServerConnection().connect();
+        clientProtocol.executed(result);
+    }
+
+    public AntlrResult execute() {
+        LOGGER.debug("Executing ANTLR worker");
+        try {
+            AntlrExecuter antlrExecuter = new AntlrExecuter();
+            return antlrExecuter.runAntlr(spec);
+        } catch (Exception e) {
+            return new AntlrResult(1, e);
+        }
+    }
+}
\ No newline at end of file
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
deleted file mode 100644
index 97d1247..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.internal;
-
-import java.io.File;
-
-/**
- * Models information relevant to generation of a particular Antlr grammar file.
- */
-public class GenerationPlan {
-    private final File source;
-    private final File generationDirectory;
-
-    private File importVocabTokenTypesDirectory;
-    private boolean outOfDate;
-
-    /**
-     * Instantiates a generation plan.
-     *
-     * @param source The grammar file.
-     * @param generationDirectory The directory into which generated lexers and parsers should be written, accounting for
-     * declared package.
-     */
-    GenerationPlan(File source, File generationDirectory) {
-        this.source = source;
-        this.generationDirectory = generationDirectory;
-    }
-
-    public String getId() {
-        return getSource().getPath();
-    }
-
-    public File getSource() {
-        return source;
-    }
-
-    public File getGenerationDirectory() {
-        return generationDirectory;
-    }
-
-    public File getImportVocabTokenTypesDirectory() {
-        return importVocabTokenTypesDirectory;
-    }
-
-    void setImportVocabTokenTypesDirectory(File importVocabTokenTypesDirectory) {
-        this.importVocabTokenTypesDirectory = importVocabTokenTypesDirectory;
-    }
-
-    /**
-     * Is the grammar file modeled by this plan out of considered out of date?
-     *
-     * @return True if the grammar generation is out of date (needs regen); false otherwise.
-     */
-    public boolean isOutOfDate() {
-        return outOfDate;
-    }
-
-    /**
-     * Marks the plan as out of date.
-     */
-    void markOutOfDate() {
-        this.outOfDate = true;
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index 2d82713..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.internal;
-
-import org.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;
-
-/**
- * Builder for the properly order list of {@link GenerationPlan generation plans}.
- *
- * <p>IMPL NOTE : Uses recursive calls to achieve ordering.</p>
- */
-public class GenerationPlanBuilder {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GenerationPlanBuilder.class);
-
-    private final LinkedHashMap<String, GenerationPlan> generationPlans = new LinkedHashMap<String, GenerationPlan>();
-    private final File outputDirectory;
-
-    private XRef metadataXRef;
-
-    public GenerationPlanBuilder(File outputDirectory) {
-        this.outputDirectory = outputDirectory;
-    }
-
-    public synchronized List<GenerationPlan> buildGenerationPlans(XRef metadataXRef) {
-        this.metadataXRef = metadataXRef;
-
-        Iterator<GrammarFileMetadata> grammarFiles = metadataXRef.iterateGrammarFiles();
-        while (grammarFiles.hasNext()) {
-            final GrammarFileMetadata grammarFileMetadata = grammarFiles.next();
-            // NOTE : loacteOrBuildGenerationPlan populates the generationPlans map
-            loacteOrBuildGenerationPlan(grammarFileMetadata);
-        }
-
-        return new ArrayList<GenerationPlan>(generationPlans.values());
-    }
-
-    private GenerationPlan loacteOrBuildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
-        GenerationPlan generationPlan = generationPlans.get(grammarFileMetadata.getFilePath().getPath());
-        if (generationPlan == null) {
-            generationPlan = buildGenerationPlan(grammarFileMetadata);
-        }
-        return generationPlan;
-    }
-
-    private GenerationPlan buildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
-        File generationDirectory = isEmpty(grammarFileMetadata.getPackageName()) ? outputDirectory : new File(
-                outputDirectory, grammarFileMetadata.getPackageName().replace('.', File.separatorChar));
-
-        GenerationPlan generationPlan = new GenerationPlan(grammarFileMetadata.getFilePath(), generationDirectory);
-
-        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
-            final File generatedParserFile = new File(outputDirectory, grammarMetadata.determineGeneratedParserPath());
-
-            if (!generatedParserFile.exists()) {
-                generationPlan.markOutOfDate();
-            } else if (generatedParserFile.lastModified() < generationPlan.getSource().lastModified()) {
-                generationPlan.markOutOfDate();
-            }
-
-            // see if the grammar if out-of-date by way of its super-grammar(s) as gleaned from parsing the grammar file
-            if (!grammarMetadata.extendsStandardGrammar()) {
-                final GrammarFileMetadata superGrammarGrammarFileMetadata = grammarMetadata.getSuperGrammarDelegate()
-                        .getAssociatedGrammarMetadata().getGrammarFile();
-                if (superGrammarGrammarFileMetadata != null) {
-                    final GenerationPlan superGrammarGenerationPlan = loacteOrBuildGenerationPlan(
-                            superGrammarGrammarFileMetadata);
-                    if (superGrammarGenerationPlan.isOutOfDate()) {
-                        generationPlan.markOutOfDate();
-                    } else if (superGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
-                            .lastModified()) {
-                        generationPlan.markOutOfDate();
-                    }
-                }
-            }
-
-            // see if the grammar if out-of-date by way of its importVocab
-            if (isNotEmpty(grammarMetadata.getImportVocab())) {
-                final GrammarFileMetadata importVocabGrammarFileMetadata = metadataXRef.getGrammarFileByExportVocab(
-                        grammarMetadata.getImportVocab());
-                if (importVocabGrammarFileMetadata == null) {
-                    LOGGER.warn("unable to locate grammar exporting specifcied import vocab ["
-                            + grammarMetadata.getImportVocab() + "]");
-                } else if (!importVocabGrammarFileMetadata.getFilePath().equals(grammarFileMetadata.getFilePath())) {
-                    final GenerationPlan importVocabGrammarGenerationPlan = loacteOrBuildGenerationPlan(
-                            importVocabGrammarFileMetadata);
-                    generationPlan.setImportVocabTokenTypesDirectory(
-                            importVocabGrammarGenerationPlan.getGenerationDirectory());
-                    if (importVocabGrammarGenerationPlan.isOutOfDate()) {
-                        generationPlan.markOutOfDate();
-                    } else if (importVocabGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
-                            .lastModified()) {
-                        generationPlan.markOutOfDate();
-                    }
-                }
-            }
-        }
-
-        generationPlans.put(generationPlan.getId(), generationPlan);
-        return generationPlan;
-    }
-
-    private boolean isEmpty(String string) {
-        return string == null || string.trim().length() == 0;
-    }
-
-    private boolean isNotEmpty(String string) {
-        return !isEmpty(string);
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index 497eb88..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.internal;
-
-import antlr.collections.impl.IndexedVector;
-import antlr.preprocessor.GrammarFile;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-
-/**
- * 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.
- */
-public class GrammarDelegate {
-    public static List<GrammarDelegate> extractGrammarDelegates(GrammarFile antlrGrammarFile) {
-        List<GrammarDelegate> grammarDelegates = new ArrayList<GrammarDelegate>();
-        Enumeration grammarFileGramars = antlrGrammarFile.getGrammars().elements();
-        while (grammarFileGramars.hasMoreElements()) {
-            grammarDelegates.add(new GrammarDelegate(grammarFileGramars.nextElement()));
-        }
-        return grammarDelegates;
-    }
-
-    private final String className;
-    private final String importVocab;
-    private final String exportVocab;
-    private final GrammarDelegate superGrammarDelegate;
-
-    public GrammarDelegate(Object antlrGrammarMetadata) {
-        try {
-            final Method getNameMethod = ANTLR_GRAMMAR_CLASS.getDeclaredMethod("getName", NO_ARG_SIGNATURE);
-            getNameMethod.setAccessible(true);
-            this.className = (String) getNameMethod.invoke(antlrGrammarMetadata, NO_ARGS);
-
-            final Method getSuperGrammarMethod = ANTLR_GRAMMAR_CLASS.getMethod("getSuperGrammar", NO_ARG_SIGNATURE);
-            getSuperGrammarMethod.setAccessible(true);
-            final Object antlrSuperGrammarGrammarMetadata = getSuperGrammarMethod.invoke(antlrGrammarMetadata, NO_ARGS);
-            this.superGrammarDelegate = antlrSuperGrammarGrammarMetadata == null ? null : new GrammarDelegate(antlrSuperGrammarGrammarMetadata);
-
-            Method getOptionsMethod = ANTLR_GRAMMAR_CLASS.getMethod("getOptions", NO_ARG_SIGNATURE);
-            getOptionsMethod.setAccessible(true);
-            IndexedVector options = (IndexedVector) getOptionsMethod.invoke(antlrGrammarMetadata, NO_ARGS);
-
-            Method getRHSMethod = ANTLR_OPTION_CLASS.getMethod("getRHS", NO_ARG_SIGNATURE);
-            getRHSMethod.setAccessible(true);
-
-            final Object importVocabOption = options == null ? null : options.getElement("importVocab");
-            this.importVocab = importVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(importVocabOption, NO_ARGS));
-
-            final Object exportVocabOption = options == null ? null : options.getElement("exportVocab");
-            this.exportVocab = exportVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(exportVocabOption, NO_ARGS));
-        } catch (Throwable t) {
-            throw new IllegalStateException("Error accessing  Antlr grammar metadata", t);
-        }
-    }
-
-    /**
-     * Retrieves the unqualified name of the lexer/parser class.
-     *
-     * @return The unqualified lexer/parser class name.
-     */
-    public String getClassName() {
-        return className;
-    }
-
-    /**
-     * Retrieves the name of this vocabulary imported by this grammar.
-     *
-     * @return The gammar's imported vocabulary name.
-     */
-    public String getImportVocab() {
-        return importVocab;
-    }
-
-    /**
-     * Retrieves the name of this vocabulary exported by this grammar.
-     *
-     * @return The gammar's exported vocabulary name.
-     */
-    public String getExportVocab() {
-        return exportVocab;
-    }
-
-    /**
-     * Retrieves the grammar delegate associated with this grammars super grammar deduced during preprocessing from its extends clause.
-     *
-     * @return The super-grammar grammar delegate
-     */
-    public GrammarDelegate getSuperGrammarDelegate() {
-        return superGrammarDelegate;
-    }
-
-    private GrammarMetadata associatedGrammarMetadata;
-
-    public void associateWith(GrammarMetadata associatedGrammarMetadata) {
-        this.associatedGrammarMetadata = associatedGrammarMetadata;
-    }
-
-    public GrammarMetadata getAssociatedGrammarMetadata() {
-        return associatedGrammarMetadata;
-    }
-
-    private String vocabName(String vocabName) {
-        if (vocabName == null) {
-            return null;
-        }
-        vocabName = vocabName.trim();
-        if (vocabName.endsWith(";")) {
-            vocabName = vocabName.substring(0, vocabName.length() - 1);
-        }
-        return vocabName;
-    }
-
-    private static final Class ANTLR_GRAMMAR_CLASS;
-    private static final Class ANTLR_OPTION_CLASS;
-
-    static {
-        ANTLR_GRAMMAR_CLASS = loadAntlrClass("antlr.preprocessor.Grammar");
-        ANTLR_OPTION_CLASS = loadAntlrClass("antlr.preprocessor.Option");
-    }
-
-    public static final Class[] NO_ARG_SIGNATURE = new Class[0];
-    public static final Object[] NO_ARGS = new Object[0];
-
-    private static Class loadAntlrClass(String className) {
-        try {
-            return Class.forName(className, true, GrammarDelegate.class.getClassLoader());
-        } catch (ClassNotFoundException e) {
-            throw new IllegalStateException("Unable to locate Antlr class [" + className + "]", e);
-        }
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index 7d3daa5..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.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.plugins.antlr.internal;
-
-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.
- */
-public class GrammarFileMetadata {
-    private final File filePath;
-    private final antlr.preprocessor.GrammarFile antlrGrammarFile;
-    private final String packageName;
-    private List<GrammarMetadata> grammarMetadatas = new ArrayList<GrammarMetadata>();
-
-    public GrammarFileMetadata(File filePath, antlr.preprocessor.GrammarFile antlrGrammarFile, String packageName) {
-        this.filePath = filePath;
-        this.antlrGrammarFile = antlrGrammarFile;
-        this.packageName = packageName;
-
-        List<GrammarDelegate> antlrGrammarDelegates = GrammarDelegate.extractGrammarDelegates(antlrGrammarFile);
-        for (GrammarDelegate antlrGrammarDelegate : antlrGrammarDelegates) {
-            GrammarMetadata grammarMetadata = new GrammarMetadata(this, antlrGrammarDelegate);
-            grammarMetadatas.add(grammarMetadata);
-        }
-    }
-
-    public File getFilePath() {
-        return filePath;
-    }
-
-    public antlr.preprocessor.GrammarFile getAntlrGrammarFile() {
-        return antlrGrammarFile;
-    }
-
-    public String getPackageName() {
-        return packageName;
-    }
-
-    public List<GrammarMetadata> getGrammars() {
-        return grammarMetadatas;
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index 371715e..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.internal;
-
-import antlr.Parser;
-import antlr.TreeParser;
-
-import java.io.File;
-
-/**
- * Models a grammar defined within an Antlr grammar file.
- */
-public class GrammarMetadata {
-    private final GrammarFileMetadata grammarFileMetadata;
-    private final GrammarDelegate grammarDelegate;
-
-    public GrammarMetadata(GrammarFileMetadata grammarFileMetadata, GrammarDelegate grammarDelegate) {
-        this.grammarFileMetadata = grammarFileMetadata;
-        this.grammarDelegate = grammarDelegate;
-        grammarDelegate.associateWith(this);
-    }
-
-    public GrammarFileMetadata getGrammarFile() {
-        return grammarFileMetadata;
-    }
-
-    public String getClassName() {
-        return grammarDelegate.getClassName();
-    }
-
-    public String getQualifiedClassName() {
-        if (isEmpty(getPackageName())) {
-            return getClassName();
-        } else {
-            return getPackageName() + '.' + getClassName();
-        }
-    }
-
-    public GrammarDelegate getSuperGrammarDelegate() {
-        return grammarDelegate.getSuperGrammarDelegate();
-    }
-
-    public boolean extendsStandardGrammar() {
-        final String superGrammarClassName = getSuperGrammarDelegate().getClassName();
-        return Parser.class.getName().equals(superGrammarClassName) || Parser.class.getSimpleName().equals(
-                superGrammarClassName) || TreeParser.class.getName().equals(superGrammarClassName)
-                || TreeParser.class.getSimpleName().equals(superGrammarClassName) || "Lexer".equals(
-                superGrammarClassName);
-    }
-
-    public String getImportVocab() {
-        return grammarDelegate.getImportVocab();
-    }
-
-    public String getExportVocab() {
-        return grammarDelegate.getExportVocab();
-    }
-
-    public String getPackageName() {
-        return getGrammarFile().getPackageName();
-    }
-
-    /**
-     * Determine the relative path of the generated parser java file.
-     *
-     * @return The relative generated parser file path.
-     */
-    public String determineGeneratedParserPath() {
-        if (isEmpty(getPackageName())) {
-            return getClassName() + ".java";
-        } else {
-            return getPackageName().replace('.', File.separatorChar) + File.separatorChar + getClassName() + ".java";
-        }
-    }
-
-    private boolean isEmpty(String packageName) {
-        return packageName == null || packageName.trim().length() == 0;
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index fee4fd8..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.internal;
-
-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.
- */
-public class MetadataExtracter {
-
-    public XRef extractMetadata(FileTree source) {
-        antlr.Tool tool = new antlr.Tool();
-        antlr.preprocessor.Hierarchy hierarchy = new antlr.preprocessor.Hierarchy(tool);
-
-        // first let antlr preprocess the grammars...
-        for (File grammarFileFile : source.getFiles()) {
-            final String grammarFilePath = grammarFileFile.getPath();
-
-            try {
-                hierarchy.readGrammarFile(grammarFilePath);
-            } catch (FileNotFoundException e) {
-                // should never happen here
-                throw new IllegalStateException("Received FileNotFoundException on already read file", e);
-            }
-        }
-
-        // now, do our processing using the antlr preprocessor results whenever possible.
-        XRef xref = new XRef(hierarchy);
-        for (File grammarFileFile : source.getFiles()) {
-
-            // determine the package name :(
-            String grammarPackageName = null;
-            try {
-                BufferedReader in = new BufferedReader(new FileReader(grammarFileFile));
-                try {
-                    String line;
-                    while ((line = in.readLine()) != null) {
-                        line = line.trim();
-                        if (line.startsWith("package") && line.endsWith(";")) {
-                            grammarPackageName = line.substring(8, line.length() - 1);
-                            break;
-                        }
-                    }
-                } finally {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        throw new UncheckedIOException(e);
-                    }
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-
-            final String grammarFilePath = grammarFileFile.getPath();
-            antlr.preprocessor.GrammarFile antlrGrammarFile = hierarchy.getFile(grammarFilePath);
-
-            GrammarFileMetadata grammarFileMetadata = new GrammarFileMetadata(grammarFileFile, antlrGrammarFile,
-                    grammarPackageName);
-
-            xref.addGrammarFile(grammarFileMetadata);
-        }
-
-        return xref;
-    }
-}
diff --git a/subprojects/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
deleted file mode 100644
index 976fee9..0000000
--- a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
+++ /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.plugins.antlr.internal;
-
-import antlr.preprocessor.Hierarchy;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-
-/**
- * Models cross-reference (x-ref) info about {@link GrammarFileMetadata grammar files} such as {@link #filesByPath},
- * {@link #filesByExportVocab} and {@link #filesByClassName}.
- */
-public class XRef {
-    private final Hierarchy antlrHierarchy;
-
-    private LinkedHashMap<String, GrammarFileMetadata> filesByPath = new LinkedHashMap<String, GrammarFileMetadata>();
-    private HashMap<String, GrammarFileMetadata> filesByExportVocab = new HashMap<String, GrammarFileMetadata>();
-    private HashMap<String, GrammarFileMetadata> filesByClassName = new HashMap<String, GrammarFileMetadata>();
-
-    public XRef(Hierarchy antlrHierarchy) {
-        this.antlrHierarchy = antlrHierarchy;
-    }
-
-    public Object getAntlrHierarchy() {
-        return antlrHierarchy;
-    }
-
-    /**
-     * Adds a grammar file to this cross-reference.
-     *
-     * @param grammarFileMetadata The grammar file to add (and to be cross referenced).
-     */
-    void addGrammarFile(GrammarFileMetadata grammarFileMetadata) {
-        filesByPath.put(grammarFileMetadata.getFilePath().getPath(), grammarFileMetadata);
-        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
-            filesByClassName.put(grammarMetadata.getClassName(), grammarFileMetadata);
-            if (grammarMetadata.getExportVocab() != null) {
-                GrammarFileMetadata old = filesByExportVocab.put(grammarMetadata.getExportVocab(), grammarFileMetadata);
-                if (old != null && old != grammarFileMetadata) {
-                    System.out.println("[WARNING] : multiple grammars defined the same exportVocab : " + grammarMetadata
-                            .getExportVocab());
-                }
-            }
-        }
-    }
-
-    public Iterator<GrammarFileMetadata> iterateGrammarFiles() {
-        return filesByPath.values().iterator();
-    }
-
-    /**
-     * Locate the grammar file metadata by grammar file path.
-     *
-     * @param path The grammar file path.
-     * @return The grammar file metadata.  May be null if none found.
-     */
-    public GrammarFileMetadata getGrammarFileByPath(String path) {
-        return filesByPath.get(path);
-    }
-
-    /**
-     * Locate the grammar file metadata by the name of a class generated from one of its included grammars.
-     *
-     * @param className The generated class name.
-     * @return The grammar file metadata.  May be null if none found.
-     */
-    public GrammarFileMetadata getGrammarFileByClassName(String className) {
-        return filesByClassName.get(className);
-    }
-
-    /**
-     * Locate the grammar file metadata by the name of a vocabulary exported from one of its included grammars.
-     *
-     * @param vocabName The vocabulary name
-     * @return The grammar file metadata.  May be null if none found.
-     */
-    public GrammarFileMetadata getGrammarFileByExportVocab(String vocabName) {
-        return filesByExportVocab.get(vocabName);
-    }
-}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlan.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlan.java
new file mode 100644
index 0000000..7ee9797
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlan.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+import java.io.File;
+
+/**
+ * Models information relevant to generation of a particular Antlr grammar file.
+ */
+public class GenerationPlan {
+    private final File source;
+    private final File generationDirectory;
+
+    private File importVocabTokenTypesDirectory;
+    private boolean outOfDate;
+
+    /**
+     * Instantiates a generation plan.
+     *
+     * @param source The grammar file.
+     * @param generationDirectory The directory into which generated lexers and parsers should be written, accounting for
+     * declared package.
+     */
+    GenerationPlan(File source, File generationDirectory) {
+        this.source = source;
+        this.generationDirectory = generationDirectory;
+    }
+
+    public String getId() {
+        return getSource().getPath();
+    }
+
+    public File getSource() {
+        return source;
+    }
+
+    public File getGenerationDirectory() {
+        return generationDirectory;
+    }
+
+    public File getImportVocabTokenTypesDirectory() {
+        return importVocabTokenTypesDirectory;
+    }
+
+    void setImportVocabTokenTypesDirectory(File importVocabTokenTypesDirectory) {
+        this.importVocabTokenTypesDirectory = importVocabTokenTypesDirectory;
+    }
+
+    /**
+     * Is the grammar file modeled by this plan out of considered out of date?
+     *
+     * @return True if the grammar generation is out of date (needs regen); false otherwise.
+     */
+    public boolean isOutOfDate() {
+        return outOfDate;
+    }
+
+    /**
+     * Marks the plan as out of date.
+     */
+    void markOutOfDate() {
+        this.outOfDate = true;
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlanBuilder.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlanBuilder.java
new file mode 100644
index 0000000..d045f13
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GenerationPlanBuilder.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+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;
+
+/**
+ * Builder for the properly order list of {@link GenerationPlan generation plans}.
+ *
+ * <p>IMPL NOTE : Uses recursive calls to achieve ordering.</p>
+ */
+public class GenerationPlanBuilder {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GenerationPlanBuilder.class);
+
+    private final LinkedHashMap<String, GenerationPlan> generationPlans = new LinkedHashMap<String, GenerationPlan>();
+    private final File outputDirectory;
+
+    private XRef metadataXRef;
+
+    public GenerationPlanBuilder(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    public synchronized List<GenerationPlan> buildGenerationPlans(XRef metadataXRef) {
+        this.metadataXRef = metadataXRef;
+
+        Iterator<GrammarFileMetadata> grammarFiles = metadataXRef.iterateGrammarFiles();
+        while (grammarFiles.hasNext()) {
+            final GrammarFileMetadata grammarFileMetadata = grammarFiles.next();
+            // NOTE : locateOrBuildGenerationPlan populates the generationPlans map
+            locateOrBuildGenerationPlan(grammarFileMetadata);
+        }
+
+        return new ArrayList<GenerationPlan>(generationPlans.values());
+    }
+
+    private GenerationPlan locateOrBuildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
+        GenerationPlan generationPlan = generationPlans.get(grammarFileMetadata.getFilePath().getPath());
+        if (generationPlan == null) {
+            generationPlan = buildGenerationPlan(grammarFileMetadata);
+        }
+        return generationPlan;
+    }
+
+    private GenerationPlan buildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
+        File generationDirectory = isEmpty(grammarFileMetadata.getPackageName()) ? outputDirectory : new File(
+                outputDirectory, grammarFileMetadata.getPackageName().replace('.', File.separatorChar));
+
+        GenerationPlan generationPlan = new GenerationPlan(grammarFileMetadata.getFilePath(), generationDirectory);
+
+        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
+            final File generatedParserFile = new File(outputDirectory, grammarMetadata.determineGeneratedParserPath());
+
+            if (!generatedParserFile.exists()) {
+                generationPlan.markOutOfDate();
+            } else if (generatedParserFile.lastModified() < generationPlan.getSource().lastModified()) {
+                generationPlan.markOutOfDate();
+            }
+
+            // see if the grammar if out-of-date by way of its super-grammar(s) as gleaned from parsing the grammar file
+            if (!grammarMetadata.extendsStandardGrammar()) {
+                final GrammarFileMetadata superGrammarGrammarFileMetadata = grammarMetadata.getSuperGrammarDelegate()
+                        .getAssociatedGrammarMetadata().getGrammarFile();
+                if (superGrammarGrammarFileMetadata != null) {
+                    final GenerationPlan superGrammarGenerationPlan = locateOrBuildGenerationPlan(
+                            superGrammarGrammarFileMetadata);
+                    if (superGrammarGenerationPlan.isOutOfDate()) {
+                        generationPlan.markOutOfDate();
+                    } else if (superGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
+                            .lastModified()) {
+                        generationPlan.markOutOfDate();
+                    }
+                }
+            }
+
+            // see if the grammar if out-of-date by way of its importVocab
+            if (isNotEmpty(grammarMetadata.getImportVocab())) {
+                final GrammarFileMetadata importVocabGrammarFileMetadata = metadataXRef.getGrammarFileByExportVocab(
+                        grammarMetadata.getImportVocab());
+                if (importVocabGrammarFileMetadata == null) {
+                    LOGGER.warn("unable to locate grammar exporting specified import vocab ["
+                            + grammarMetadata.getImportVocab() + "]");
+                } else if (!importVocabGrammarFileMetadata.getFilePath().equals(grammarFileMetadata.getFilePath())) {
+                    final GenerationPlan importVocabGrammarGenerationPlan = locateOrBuildGenerationPlan(
+                            importVocabGrammarFileMetadata);
+                    generationPlan.setImportVocabTokenTypesDirectory(
+                            importVocabGrammarGenerationPlan.getGenerationDirectory());
+                    if (importVocabGrammarGenerationPlan.isOutOfDate()) {
+                        generationPlan.markOutOfDate();
+                    } else if (importVocabGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
+                            .lastModified()) {
+                        generationPlan.markOutOfDate();
+                    }
+                }
+            }
+        }
+
+        generationPlans.put(generationPlan.getId(), generationPlan);
+        return generationPlan;
+    }
+
+    private boolean isEmpty(String string) {
+        return string == null || string.trim().length() == 0;
+    }
+
+    private boolean isNotEmpty(String string) {
+        return !isEmpty(string);
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarDelegate.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarDelegate.java
new file mode 100644
index 0000000..0814682
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarDelegate.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+import antlr.collections.impl.IndexedVector;
+import antlr.preprocessor.GrammarFile;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * 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.
+ */
+public class GrammarDelegate {
+    public static List<GrammarDelegate> extractGrammarDelegates(GrammarFile antlrGrammarFile) {
+        List<GrammarDelegate> grammarDelegates = new ArrayList<GrammarDelegate>();
+        Enumeration grammarFileGramars = antlrGrammarFile.getGrammars().elements();
+        while (grammarFileGramars.hasMoreElements()) {
+            grammarDelegates.add(new GrammarDelegate(grammarFileGramars.nextElement()));
+        }
+        return grammarDelegates;
+    }
+
+    private final String className;
+    private final String importVocab;
+    private final String exportVocab;
+    private final GrammarDelegate superGrammarDelegate;
+
+    public GrammarDelegate(Object antlrGrammarMetadata) {
+        try {
+            final Method getNameMethod = ANTLR_GRAMMAR_CLASS.getDeclaredMethod("getName", NO_ARG_SIGNATURE);
+            getNameMethod.setAccessible(true);
+            this.className = (String) getNameMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+
+            final Method getSuperGrammarMethod = ANTLR_GRAMMAR_CLASS.getMethod("getSuperGrammar", NO_ARG_SIGNATURE);
+            getSuperGrammarMethod.setAccessible(true);
+            final Object antlrSuperGrammarGrammarMetadata = getSuperGrammarMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+            this.superGrammarDelegate = antlrSuperGrammarGrammarMetadata == null ? null : new GrammarDelegate(antlrSuperGrammarGrammarMetadata);
+
+            Method getOptionsMethod = ANTLR_GRAMMAR_CLASS.getMethod("getOptions", NO_ARG_SIGNATURE);
+            getOptionsMethod.setAccessible(true);
+            IndexedVector options = (IndexedVector) getOptionsMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+
+            Method getRHSMethod = ANTLR_OPTION_CLASS.getMethod("getRHS", NO_ARG_SIGNATURE);
+            getRHSMethod.setAccessible(true);
+
+            final Object importVocabOption = options == null ? null : options.getElement("importVocab");
+            this.importVocab = importVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(importVocabOption, NO_ARGS));
+
+            final Object exportVocabOption = options == null ? null : options.getElement("exportVocab");
+            this.exportVocab = exportVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(exportVocabOption, NO_ARGS));
+        } catch (Throwable t) {
+            throw new IllegalStateException("Error accessing  Antlr grammar metadata", t);
+        }
+    }
+
+    /**
+     * Retrieves the unqualified name of the lexer/parser class.
+     *
+     * @return The unqualified lexer/parser class name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Retrieves the name of this vocabulary imported by this grammar.
+     *
+     * @return The gammar's imported vocabulary name.
+     */
+    public String getImportVocab() {
+        return importVocab;
+    }
+
+    /**
+     * Retrieves the name of this vocabulary exported by this grammar.
+     *
+     * @return The gammar's exported vocabulary name.
+     */
+    public String getExportVocab() {
+        return exportVocab;
+    }
+
+    /**
+     * Retrieves the grammar delegate associated with this grammars super grammar deduced during preprocessing from its extends clause.
+     *
+     * @return The super-grammar grammar delegate
+     */
+    public GrammarDelegate getSuperGrammarDelegate() {
+        return superGrammarDelegate;
+    }
+
+    private GrammarMetadata associatedGrammarMetadata;
+
+    public void associateWith(GrammarMetadata associatedGrammarMetadata) {
+        this.associatedGrammarMetadata = associatedGrammarMetadata;
+    }
+
+    public GrammarMetadata getAssociatedGrammarMetadata() {
+        return associatedGrammarMetadata;
+    }
+
+    private String vocabName(String vocabName) {
+        if (vocabName == null) {
+            return null;
+        }
+        vocabName = vocabName.trim();
+        if (vocabName.endsWith(";")) {
+            vocabName = vocabName.substring(0, vocabName.length() - 1);
+        }
+        return vocabName;
+    }
+
+    private static final Class ANTLR_GRAMMAR_CLASS;
+    private static final Class ANTLR_OPTION_CLASS;
+
+    static {
+        ANTLR_GRAMMAR_CLASS = loadAntlrClass("antlr.preprocessor.Grammar");
+        ANTLR_OPTION_CLASS = loadAntlrClass("antlr.preprocessor.Option");
+    }
+
+    public static final Class[] NO_ARG_SIGNATURE = new Class[0];
+    public static final Object[] NO_ARGS = new Object[0];
+
+    private static Class loadAntlrClass(String className) {
+        try {
+            return Class.forName(className, true, GrammarDelegate.class.getClassLoader());
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("Unable to locate Antlr class [" + className + "]", e);
+        }
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarFileMetadata.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarFileMetadata.java
new file mode 100644
index 0000000..ef6ed4f
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarFileMetadata.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+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.
+ */
+public class GrammarFileMetadata {
+    private final File filePath;
+    private final antlr.preprocessor.GrammarFile antlrGrammarFile;
+    private final String packageName;
+    private List<GrammarMetadata> grammarMetadatas = new ArrayList<GrammarMetadata>();
+
+    public GrammarFileMetadata(File filePath, antlr.preprocessor.GrammarFile antlrGrammarFile, String packageName) {
+        this.filePath = filePath;
+        this.antlrGrammarFile = antlrGrammarFile;
+        this.packageName = packageName;
+
+        List<GrammarDelegate> antlrGrammarDelegates = GrammarDelegate.extractGrammarDelegates(antlrGrammarFile);
+        for (GrammarDelegate antlrGrammarDelegate : antlrGrammarDelegates) {
+            GrammarMetadata grammarMetadata = new GrammarMetadata(this, antlrGrammarDelegate);
+            grammarMetadatas.add(grammarMetadata);
+        }
+    }
+
+    public File getFilePath() {
+        return filePath;
+    }
+
+    public antlr.preprocessor.GrammarFile getAntlrGrammarFile() {
+        return antlrGrammarFile;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public List<GrammarMetadata> getGrammars() {
+        return grammarMetadatas;
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarMetadata.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarMetadata.java
new file mode 100644
index 0000000..1f03971
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/GrammarMetadata.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+import antlr.Parser;
+import antlr.TreeParser;
+
+import java.io.File;
+
+/**
+ * Models a grammar defined within an Antlr grammar file.
+ */
+public class GrammarMetadata {
+    private final GrammarFileMetadata grammarFileMetadata;
+    private final GrammarDelegate grammarDelegate;
+
+    public GrammarMetadata(GrammarFileMetadata grammarFileMetadata, GrammarDelegate grammarDelegate) {
+        this.grammarFileMetadata = grammarFileMetadata;
+        this.grammarDelegate = grammarDelegate;
+        grammarDelegate.associateWith(this);
+    }
+
+    public GrammarFileMetadata getGrammarFile() {
+        return grammarFileMetadata;
+    }
+
+    public String getClassName() {
+        return grammarDelegate.getClassName();
+    }
+
+    public String getQualifiedClassName() {
+        if (isEmpty(getPackageName())) {
+            return getClassName();
+        } else {
+            return getPackageName() + '.' + getClassName();
+        }
+    }
+
+    public GrammarDelegate getSuperGrammarDelegate() {
+        return grammarDelegate.getSuperGrammarDelegate();
+    }
+
+    public boolean extendsStandardGrammar() {
+        final String superGrammarClassName = getSuperGrammarDelegate().getClassName();
+        return Parser.class.getName().equals(superGrammarClassName) || Parser.class.getSimpleName().equals(
+                superGrammarClassName) || TreeParser.class.getName().equals(superGrammarClassName)
+                || TreeParser.class.getSimpleName().equals(superGrammarClassName) || "Lexer".equals(
+                superGrammarClassName);
+    }
+
+    public String getImportVocab() {
+        return grammarDelegate.getImportVocab();
+    }
+
+    public String getExportVocab() {
+        return grammarDelegate.getExportVocab();
+    }
+
+    public String getPackageName() {
+        return getGrammarFile().getPackageName();
+    }
+
+    /**
+     * Determine the relative path of the generated parser java file.
+     *
+     * @return The relative generated parser file path.
+     */
+    public String determineGeneratedParserPath() {
+        if (isEmpty(getPackageName())) {
+            return getClassName() + ".java";
+        } else {
+            return getPackageName().replace('.', File.separatorChar) + File.separatorChar + getClassName() + ".java";
+        }
+    }
+
+    private boolean isEmpty(String packageName) {
+        return packageName == null || packageName.trim().length() == 0;
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/MetadataExtracter.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/MetadataExtracter.java
new file mode 100644
index 0000000..56c6925
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/MetadataExtracter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+import org.gradle.api.UncheckedIOException;
+
+import java.io.*;
+import java.util.Set;
+
+/**
+ * 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.
+ */
+public class MetadataExtracter {
+
+    public XRef extractMetadata(Set<File> sources) {
+        antlr.Tool tool = new antlr.Tool();
+        antlr.preprocessor.Hierarchy hierarchy = new antlr.preprocessor.Hierarchy(tool);
+
+        // first let antlr preprocess the grammars...
+        for (File grammarFileFile : sources) {
+            final String grammarFilePath = grammarFileFile.getPath();
+
+            try {
+                hierarchy.readGrammarFile(grammarFilePath);
+            } catch (FileNotFoundException e) {
+                // should never happen here
+                throw new IllegalStateException("Received FileNotFoundException on already read file", e);
+            }
+        }
+
+        // now, do our processing using the antlr preprocessor results whenever possible.
+        XRef xref = new XRef(hierarchy);
+        for (File grammarFileFile : sources) {
+
+            // determine the package name :(
+            String grammarPackageName = null;
+            try {
+                BufferedReader in = new BufferedReader(new FileReader(grammarFileFile));
+                try {
+                    String line;
+                    while ((line = in.readLine()) != null) {
+                        line = line.trim();
+                        if (line.startsWith("package") && line.endsWith(";")) {
+                            grammarPackageName = line.substring(8, line.length() - 1);
+                            break;
+                        }
+                    }
+                } finally {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            final String grammarFilePath = grammarFileFile.getPath();
+            antlr.preprocessor.GrammarFile antlrGrammarFile = hierarchy.getFile(grammarFilePath);
+
+            GrammarFileMetadata grammarFileMetadata = new GrammarFileMetadata(grammarFileFile, antlrGrammarFile,
+                    grammarPackageName);
+
+            xref.addGrammarFile(grammarFileMetadata);
+        }
+
+        return xref;
+    }
+}
diff --git a/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/XRef.java b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/XRef.java
new file mode 100644
index 0000000..403c9ec
--- /dev/null
+++ b/subprojects/antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/antlr2/XRef.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal.antlr2;
+
+import antlr.preprocessor.Hierarchy;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * Models cross-reference (x-ref) info about {@link GrammarFileMetadata grammar files} such as {@link #filesByPath}, {@link #filesByExportVocab} and {@link #filesByClassName}.
+ */
+public class XRef {
+    private final Hierarchy antlrHierarchy;
+
+    private LinkedHashMap<String, GrammarFileMetadata> filesByPath = new LinkedHashMap<String, GrammarFileMetadata>();
+    private HashMap<String, GrammarFileMetadata> filesByExportVocab = new HashMap<String, GrammarFileMetadata>();
+    private HashMap<String, GrammarFileMetadata> filesByClassName = new HashMap<String, GrammarFileMetadata>();
+
+    public XRef(Hierarchy antlrHierarchy) {
+        this.antlrHierarchy = antlrHierarchy;
+    }
+
+    public Object getAntlrHierarchy() {
+        return antlrHierarchy;
+    }
+
+    /**
+     * Adds a grammar file to this cross-reference.
+     *
+     * @param grammarFileMetadata The grammar file to add (and to be cross referenced).
+     */
+    void addGrammarFile(GrammarFileMetadata grammarFileMetadata) {
+        filesByPath.put(grammarFileMetadata.getFilePath().getPath(), grammarFileMetadata);
+        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
+            filesByClassName.put(grammarMetadata.getClassName(), grammarFileMetadata);
+            String exportVocabName = grammarMetadata.getExportVocab() != null ? grammarMetadata.getExportVocab() : grammarMetadata.getClassName();
+            GrammarFileMetadata old = filesByExportVocab.put(exportVocabName, grammarFileMetadata);
+            if (old != null && old != grammarFileMetadata) {
+                System.out.println("[WARNING] : multiple grammars defined the same exportVocab : " + exportVocabName);
+            }
+
+        }
+    }
+
+    public Iterator<GrammarFileMetadata> iterateGrammarFiles() {
+        return filesByPath.values().iterator();
+    }
+
+    /**
+     * Locate the grammar file metadata by grammar file path.
+     *
+     * @param path The grammar file path.
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByPath(String path) {
+        return filesByPath.get(path);
+    }
+
+    /**
+     * Locate the grammar file metadata by the name of a class generated from one of its included grammars.
+     *
+     * @param className The generated class name.
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByClassName(String className) {
+        return filesByClassName.get(className);
+    }
+
+    /**
+     * Locate the grammar file metadata by the name of a vocabulary exported from one of its included grammars.
+     *
+     * @param vocabName The vocabulary name
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByExportVocab(String vocabName) {
+        return filesByExportVocab.get(vocabName);
+    }
+}
diff --git a/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties b/subprojects/antlr/src/main/resources/META-INF/gradle-plugins/org.gradle.antlr.properties
similarity index 100%
rename from subprojects/antlr/src/main/resources/META-INF/gradle-plugins/antlr.properties
rename to subprojects/antlr/src/main/resources/META-INF/gradle-plugins/org.gradle.antlr.properties
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 42fccb2..10e13df 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
@@ -25,7 +25,7 @@ class AntlrPluginTest extends Specification {
 
     def addsAntlrPropertiesToEachSourceSet() {
         when:
-        project.apply plugin: AntlrPlugin
+        project.pluginManager.apply(AntlrPlugin)
 
         then:
         def main = project.sourceSets.main
@@ -41,10 +41,10 @@ class AntlrPluginTest extends Specification {
         def custom = project.sourceSets.custom
         custom.antlr.srcDirs == [project.file('src/custom/antlr')] as Set
     }
-    
+
     def addsTaskForEachSourceSet() {
         when:
-        project.apply plugin: AntlrPlugin
+        project.pluginManager.apply(AntlrPlugin)
 
         then:
         def main = project.tasks.generateGrammarSource
@@ -63,4 +63,5 @@ class AntlrPluginTest extends Specification {
         custom instanceof AntlrTask
         project.tasks.compileCustomJava.taskDependencies.getDependencies(null).contains(custom)
     }
+
 }
diff --git a/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactoryTest.groovy b/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactoryTest.groovy
new file mode 100644
index 0000000..101db8a
--- /dev/null
+++ b/subprojects/antlr/src/test/groovy/org/gradle/api/plugins/antlr/internal/AntlrSpecFactoryTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.antlr.internal
+
+import org.gradle.api.plugins.antlr.AntlrTask
+import spock.lang.Specification
+
+class AntlrSpecFactoryTest extends Specification {
+
+    AntlrExecuter executer = new AntlrExecuter()
+    private AntlrSpecFactory factory = new AntlrSpecFactory()
+
+    def tracePropertiesAddedToArgumentList() {
+        when:
+        AntlrTask task = Mock()
+
+        _ * task.outputDirectory >> destFile()
+        _ * task.getArguments() >> []
+        _ * task.isTrace() >> true
+        _ * task.isTraceLexer() >> true
+        _ * task.isTraceParser() >> true
+        _ * task.isTraceTreeWalker() >> true
+
+        def spec = factory.create(task, [] as Set)
+
+        then:
+        spec.arguments.contains("-trace")
+        spec.arguments.contains("-traceLexer")
+        spec.arguments.contains("-traceParser")
+        spec.arguments.contains("-traceTreeWalker")
+    }
+
+    def customTraceArgumentsOverrideProperties() {
+        when:
+        AntlrTask task = Mock()
+        _ * task.outputDirectory >> destFile()
+        _ * task.getArguments() >> ["-trace", "-traceLexer", "-traceParser", "-traceTreeWalker"]
+
+        def spec = factory.create(task, [] as Set)
+
+        then:
+        spec.arguments.contains("-trace")
+        spec.arguments.contains("-traceLexer")
+        spec.arguments.contains("-traceParser")
+        spec.arguments.contains("-traceTreeWalker")
+    }
+
+    def traceArgumentsDoNotDuplicateTrueTraceProperties() {
+        when:
+        AntlrTask task = Mock()
+        _ * task.outputDirectory >> destFile()
+        _ * task.getArguments() >> ["-trace", "-traceLexer", "-traceParser", "-traceTreeWalker"]
+        _ * task.isTrace() >> true
+        _ * task.isTraceLexer() >> true
+        _ * task.isTraceParser() >> true
+        _ * task.isTraceTreeWalker() >> true
+
+        def spec = factory.create(task, [] as Set)
+
+        then:
+        spec.arguments.count { it == "-trace" } == 1
+        spec.arguments.count { it == "-traceLexer" } == 1
+        spec.arguments.count { it == "-traceParser" } == 1
+        spec.arguments.count { it == "-traceTreeWalker" } == 1
+    }
+
+    def buildCommonArgumentsAddsAllParameters() {
+        when:
+        AntlrTask task = Mock()
+        _ * task.outputDirectory >> destFile()
+        _ * task.getArguments() >> ["-test"]
+        _ * task.isTrace() >> true
+        _ * task.isTraceLexer() >> true
+        _ * task.isTraceParser() >> true
+        _ * task.isTraceTreeWalker() >> true
+
+        def spec = factory.create(task, [] as Set)
+
+        then:
+        spec.asCommandLineWithoutFiles() == ["-test", "-trace", "-traceLexer", "-traceParser", "-traceTreeWalker", "-o", "/output"]
+    }
+
+    def destFile() {
+        File dest = Mock()
+        dest.getAbsolutePath() >> "/output"
+        dest
+    }
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java
new file mode 100644
index 0000000..91572c3
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import com.google.common.base.Objects;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.InvalidActionClosureException;
+import org.gradle.util.Configurable;
+
+public class ClosureBackedAction<T> implements Action<T> {
+    private final Closure closure;
+    private final boolean configureableAware;
+    private final int resolveStrategy;
+
+    public static <T> ClosureBackedAction<T> of(Closure<?> closure) {
+        return new ClosureBackedAction<T>(closure);
+    }
+
+    public ClosureBackedAction(Closure closure) {
+        this(closure, Closure.DELEGATE_FIRST, true);
+    }
+
+    public ClosureBackedAction(Closure closure, int resolveStrategy) {
+        this(closure, resolveStrategy, false);
+    }
+
+    public ClosureBackedAction(Closure closure, int resolveStrategy, boolean configureableAware) {
+        this.closure = closure;
+        this.configureableAware = configureableAware;
+        this.resolveStrategy = resolveStrategy;
+    }
+
+    public static <T> void execute(T delegate, Closure<?> closure) {
+        new ClosureBackedAction<T>(closure).execute(delegate);
+    }
+
+    public void execute(T delegate) {
+        if (closure == null) {
+            return;
+        }
+
+        try {
+            if (configureableAware && delegate instanceof Configurable) {
+                ((Configurable) delegate).configure(closure);
+            } else {
+                Closure copy = (Closure) closure.clone();
+                copy.setResolveStrategy(resolveStrategy);
+                copy.setDelegate(delegate);
+                if (copy.getMaximumNumberOfParameters() == 0) {
+                    copy.call();
+                } else {
+                    copy.call(delegate);
+                }
+            }
+        } catch (groovy.lang.MissingMethodException e) {
+            if (Objects.equal(e.getType(), closure.getClass()) && Objects.equal(e.getMethod(), "doCall")) {
+                throw new InvalidActionClosureException(closure, delegate);
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ClosureBackedAction that = (ClosureBackedAction) o;
+
+        if (configureableAware != that.configureableAware) {
+            return false;
+        }
+        if (resolveStrategy != that.resolveStrategy) {
+            return false;
+        }
+        if (!closure.equals(that.closure)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = closure.hashCode();
+        result = 31 * result + (configureableAware ? 1 : 0);
+        result = 31 * result + resolveStrategy;
+        return result;
+    }
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/Specs.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/Specs.java
new file mode 100644
index 0000000..16b60b4
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/api/specs/Specs.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.specs;
+
+import groovy.lang.Closure;
+import org.gradle.api.specs.internal.ClosureSpec;
+import org.gradle.internal.Cast;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Provides a number of {@link org.gradle.api.specs.Spec} implementations.
+ */
+public class Specs {
+
+    public static final Spec<Object> SATISFIES_ALL = new Spec<Object>() {
+        public boolean isSatisfiedBy(Object element) {
+            return true;
+        }
+    };
+
+    public static <T> Spec<T> satisfyAll() {
+        return Cast.uncheckedCast(SATISFIES_ALL);
+    }
+
+    public static final Spec<Object> SATISFIES_NONE = new Spec<Object>() {
+        public boolean isSatisfiedBy(Object element) {
+            return false;
+        }
+    };
+
+    public static <T> Spec<T> satisfyNone() {
+        return Cast.uncheckedCast(SATISFIES_NONE);
+    }
+
+    //TODO SF rename for consistency with Actions.toAction
+    public static <T> Spec<T> convertClosureToSpec(final Closure closure) {
+        return new ClosureSpec<T>(closure);
+    }
+
+    public static <T> AndSpec<T> and(Spec<? super T>... specs) {
+        return new AndSpec<T>(specs);
+    }
+
+    public static <T> AndSpec<T> and(Collection<? extends Spec<? super T>> specs) {
+        return new AndSpec<T>(specs);
+    }
+
+    public static <T> OrSpec<T> or(Spec<? super T>... specs) {
+        return new OrSpec<T>(specs);
+    }
+
+    public static <T> OrSpec<T> or(Collection<? extends Spec<? super T>> specs) {
+        return new OrSpec<T>(specs);
+    }
+
+    public static <T> NotSpec<T> not(Spec<? super T> spec) {
+        return new NotSpec<T>(spec);
+    }
+
+    public static <T> Spec<T> or(boolean defaultWhenNoSpecs, List<? extends Spec<? super T>> specs) {
+        if (specs.isEmpty()) {
+            return defaultWhenNoSpecs ? Specs.<T>satisfyAll() : Specs.<T>satisfyNone();
+        }
+        return new OrSpec<T>(specs);
+    }
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/Transformer.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/Transformer.java
new file mode 100644
index 0000000..3288c1d
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/Transformer.java
@@ -0,0 +1,23 @@
+/*
+ * 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.groovy.scripts;
+
+import org.codehaus.groovy.control.CompilationUnit;
+
+public interface Transformer {
+
+    void register(CompilationUnit compilationUnit);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AbstractScriptTransformer.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/AbstractScriptTransformer.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AbstractScriptTransformer.java
rename to subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/AbstractScriptTransformer.java
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java
new file mode 100644
index 0000000..1297061
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import com.google.common.base.Predicate;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Pair;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Self contained utility functions for dealing with AST.
+ */
+public abstract class AstUtils {
+
+    private AstUtils() {
+    }
+
+    public static boolean isMethodOnThis(MethodCallExpression call, String name) {
+        boolean hasName = call.getMethod() instanceof ConstantExpression && call.getMethod().getText().equals(name);
+        return hasName && targetIsThis(call);
+    }
+
+    public static boolean targetIsThis(MethodCallExpression call) {
+        Expression target = call.getObjectExpression();
+        return target instanceof VariableExpression && target.getText().equals("this");
+    }
+
+    public static void visitScriptCode(SourceUnit source, GroovyCodeVisitor transformer) {
+        source.getAST().getStatementBlock().visit(transformer);
+        for (Object method : source.getAST().getMethods()) {
+            MethodNode methodNode = (MethodNode) method;
+            methodNode.getCode().visit(transformer);
+        }
+    }
+
+    public static ClassNode getScriptClass(SourceUnit source) {
+        if (source.getAST().getStatementBlock().getStatements().isEmpty() && source.getAST().getMethods().isEmpty()) {
+            // There is no script class when there are no statements or methods declared in the script
+            return null;
+        }
+        return source.getAST().getClasses().get(0);
+    }
+
+    public static void removeMethod(ClassNode declaringClass, MethodNode methodNode) {
+        declaringClass.getMethods().remove(methodNode);
+        declaringClass.getDeclaredMethods(methodNode.getName()).clear();
+    }
+
+    public static void filterAndTransformStatements(SourceUnit source, StatementTransformer transformer) {
+        ListIterator<Statement> statementIterator = source.getAST().getStatementBlock().getStatements().listIterator();
+        while (statementIterator.hasNext()) {
+            Statement originalStatement = statementIterator.next();
+            Statement transformedStatement = transformer.transform(source, originalStatement);
+            if (transformedStatement == null) {
+                statementIterator.remove();
+            } else if (transformedStatement != originalStatement) {
+                statementIterator.set(transformedStatement);
+            }
+        }
+    }
+
+    public static boolean isVisible(SourceUnit source, String className) {
+        try {
+            source.getClassLoader().loadClass(className);
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    @Nullable
+    public static MethodCallExpression extractBareMethodCall(Statement statement) {
+        if (!(statement instanceof ExpressionStatement)) {
+            return null;
+        }
+
+        ExpressionStatement expressionStatement = (ExpressionStatement) statement;
+        if (!(expressionStatement.getExpression() instanceof MethodCallExpression)) {
+            return null;
+        }
+
+        MethodCallExpression methodCall = (MethodCallExpression) expressionStatement.getExpression();
+        if (!targetIsThis(methodCall)) {
+            return null;
+        }
+
+        return methodCall;
+    }
+
+    @Nullable
+    public static String extractConstantMethodName(MethodCallExpression methodCall) {
+        if (!(methodCall.getMethod() instanceof ConstantExpression)) {
+            return null;
+        }
+
+        return methodCall.getMethod().getText();
+    }
+
+    @Nullable
+    public static ScriptBlock detectScriptBlock(Statement statement) {
+        MethodCallExpression methodCall = extractBareMethodCall(statement);
+        if (methodCall == null) {
+            return null;
+        }
+
+        String methodName = extractConstantMethodName(methodCall);
+        if (methodName == null) {
+            return null;
+        }
+
+        ClosureExpression closureExpression = getSingleClosureArg(methodCall);
+        return closureExpression == null ? null : new ScriptBlock(methodName, closureExpression);
+    }
+
+    public static Pair<ClassExpression, ClosureExpression> getClassAndClosureArgs(MethodCall methodCall) {
+        if (!(methodCall.getArguments() instanceof ArgumentListExpression)) {
+            return null;
+        }
+
+        ArgumentListExpression args = (ArgumentListExpression) methodCall.getArguments();
+        if (args.getExpressions().size() == 2 && args.getExpression(0) instanceof ClassExpression && args.getExpression(1) instanceof ClosureExpression) {
+            return Pair.of((ClassExpression) args.getExpression(0), (ClosureExpression) args.getExpression(1));
+        } else {
+            return null;
+        }
+    }
+
+    public static ClassExpression getClassArg(MethodCall methodCall) {
+        if (!(methodCall.getArguments() instanceof ArgumentListExpression)) {
+            return null;
+        }
+
+        ArgumentListExpression args = (ArgumentListExpression) methodCall.getArguments();
+        if (args.getExpressions().size() == 1 && args.getExpression(0) instanceof ClassExpression) {
+            return (ClassExpression) args.getExpression(0);
+        } else {
+            return null;
+        }
+    }
+
+    public static ClosureExpression getSingleClosureArg(MethodCall methodCall) {
+        if (!(methodCall.getArguments() instanceof ArgumentListExpression)) {
+            return null;
+        }
+
+        ArgumentListExpression args = (ArgumentListExpression) methodCall.getArguments();
+        if (args.getExpressions().size() == 1 && args.getExpression(0) instanceof ClosureExpression) {
+            return (ClosureExpression) args.getExpression(0);
+        } else {
+            return null;
+        }
+    }
+
+    @Nullable
+    public static ScriptBlock detectScriptBlock(Statement statement, Predicate<? super ScriptBlock> predicate) {
+        ScriptBlock scriptBlock = detectScriptBlock(statement);
+        if (scriptBlock != null && predicate.apply(scriptBlock)) {
+            return scriptBlock;
+        } else {
+            return null;
+        }
+    }
+
+    @Nullable
+    public static ScriptBlock detectScriptBlock(Statement statement, final Collection<String> names) {
+        return detectScriptBlock(statement, new Predicate<ScriptBlock>() {
+            public boolean apply(ScriptBlock input) {
+                return names.contains(input.getName());
+            }
+        });
+    }
+
+    public static boolean isString(ConstantExpression constantExpression) {
+        return constantExpression.getType().getName().equals(String.class.getName());
+    }
+
+    @Nullable
+    public static ConstantExpression hasSingleConstantStringArg(MethodCallExpression call) {
+        ArgumentListExpression argumentList = (ArgumentListExpression) call.getArguments();
+        if (argumentList.getExpressions().size() == 1) {
+            Expression argumentExpression = argumentList.getExpressions().get(0);
+            if (argumentExpression instanceof ConstantExpression) {
+                ConstantExpression constantArgumentExpression = (ConstantExpression) argumentExpression;
+                if (isString(constantArgumentExpression)) {
+                    return constantArgumentExpression;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static Iterable<? extends Statement> unpack(Statement statement) {
+        if (statement instanceof BlockStatement) {
+            return ((BlockStatement) statement).getStatements();
+        } else {
+            return Collections.singleton(statement);
+        }
+    }
+
+    public static MethodNode getGeneratedClosureImplMethod(ClassNode classNode) {
+        if (!classNode.implementsInterface(ClassHelper.GENERATED_CLOSURE_Type)) {
+            throw new IllegalArgumentException("expecting generated closure class node");
+        }
+
+        List<MethodNode> doCallMethods = classNode.getDeclaredMethods("doCall");
+        return doCallMethods.get(0);
+    }
+
+    public static boolean isReturnNullStatement(Statement statement) {
+        if (statement instanceof ReturnStatement) {
+            ReturnStatement returnStatement = (ReturnStatement) statement;
+            if (returnStatement.getExpression() instanceof ConstantExpression) {
+                ConstantExpression constantExpression = (ConstantExpression) returnStatement.getExpression();
+                if (constantExpression.getValue() == null) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/RestrictiveCodeVisitor.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/RestrictiveCodeVisitor.java
new file mode 100644
index 0000000..a943b70
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/RestrictiveCodeVisitor.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+public class RestrictiveCodeVisitor extends CodeVisitorSupport {
+
+    private final SourceUnit sourceUnit;
+    private final String message;
+
+    public RestrictiveCodeVisitor(SourceUnit sourceUnit, String message) {
+        this.sourceUnit = sourceUnit;
+        this.message = message;
+    }
+
+    protected void restrict(ASTNode astNode) {
+        restrict(astNode, message);
+    }
+
+    protected void restrict(ASTNode astNode, String message) {
+        sourceUnit.getErrorCollector().addError(
+                new SyntaxException(message, astNode.getLineNumber(), astNode.getColumnNumber()),
+                sourceUnit
+        );
+    }
+
+    public void visitBlockStatement(BlockStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitForLoop(ForStatement forLoop) {
+        restrict(forLoop);
+    }
+
+    public void visitWhileLoop(WhileStatement loop) {
+        restrict(loop);
+    }
+
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        restrict(loop);
+    }
+
+    public void visitIfElse(IfStatement ifElse) {
+        restrict(ifElse);
+    }
+
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitReturnStatement(ReturnStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitAssertStatement(AssertStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitTryCatchFinally(TryCatchStatement finally1) {
+        restrict(finally1);
+    }
+
+    public void visitSwitch(SwitchStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitCaseStatement(CaseStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitBreakStatement(BreakStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitContinueStatement(ContinueStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitThrowStatement(ThrowStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitSynchronizedStatement(SynchronizedStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitCatchStatement(CatchStatement statement) {
+        restrict(statement);
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        restrict(call);
+    }
+
+    public void visitStaticMethodCallExpression(StaticMethodCallExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitTernaryExpression(TernaryExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitShortTernaryExpression(ElvisOperatorExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitPrefixExpression(PrefixExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitPostfixExpression(PostfixExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitBooleanExpression(BooleanExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitClosureExpression(ClosureExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitTupleExpression(TupleExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitMapExpression(MapExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitMapEntryExpression(MapEntryExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitListExpression(ListExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitRangeExpression(RangeExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitPropertyExpression(PropertyExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitAttributeExpression(AttributeExpression attributeExpression) {
+        restrict(attributeExpression);
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitMethodPointerExpression(MethodPointerExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitConstantExpression(ConstantExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitClassExpression(ClassExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitVariableExpression(VariableExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitGStringExpression(GStringExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitArrayExpression(ArrayExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitSpreadExpression(SpreadExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitSpreadMapExpression(SpreadMapExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitNotExpression(NotExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitCastExpression(CastExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitArgumentlistExpression(ArgumentListExpression expression) {
+        restrict(expression);
+    }
+
+    public void visitClosureListExpression(ClosureListExpression closureListExpression) {
+        restrict(closureListExpression);
+    }
+
+    public void visitBytecodeExpression(BytecodeExpression expression) {
+        restrict(expression);
+    }
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.java
new file mode 100644
index 0000000..e480796
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.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.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+
+public class ScriptBlock {
+    private final String name;
+    private final ClosureExpression closureExpression;
+
+    public ScriptBlock(String name, ClosureExpression closureExpression) {
+        this.name = name;
+        this.closureExpression = closureExpression;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ClosureExpression getClosureExpression() {
+        return closureExpression;
+    }
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptSourceDescriptionTransformer.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptSourceDescriptionTransformer.java
new file mode 100644
index 0000000..437b0ab
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptSourceDescriptionTransformer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+
+public class ScriptSourceDescriptionTransformer extends AbstractScriptTransformer {
+    public final static String AST_NODE_METADATA_KEY = ScriptSourceDescriptionTransformer.class.getName();
+
+    private final String scriptSourceDescription;
+
+    ScriptSourceDescriptionTransformer(String scriptSourceDescription) {
+        this.scriptSourceDescription = scriptSourceDescription;
+    }
+
+    @Override
+    protected int getPhase() {
+        return Phases.CONVERSION;
+    }
+
+    public void call(SourceUnit source) throws CompilationFailedException {
+        source.getAST().setNodeMetaData(AST_NODE_METADATA_KEY, scriptSourceDescription);
+    }
+
+}
diff --git a/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.java
new file mode 100644
index 0000000..5c1d8db
--- /dev/null
+++ b/subprojects/base-services-groovy/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.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.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * A transformer of individual statements.
+ */
+public interface StatementTransformer {
+
+    // Return null to remove the statement
+    Statement transform(SourceUnit sourceUnit, Statement statement);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/Configurable.java b/subprojects/base-services-groovy/src/main/groovy/org/gradle/util/Configurable.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/util/Configurable.java
rename to subprojects/base-services-groovy/src/main/groovy/org/gradle/util/Configurable.java
diff --git a/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/OrSpecTest.java b/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/OrSpecTest.java
index 29bd8a8..56e53c7 100644
--- a/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/OrSpecTest.java
+++ b/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/OrSpecTest.java
@@ -26,12 +26,12 @@ public class OrSpecTest extends AbstractCompositeSpecTest {
     private JUnit4Mockery context = new JUnit4Mockery();
 
     public org.gradle.api.specs.CompositeSpec createCompositeSpec(Spec... specs) {
-        return new org.gradle.api.specs.OrSpec(specs);
+        return new OrSpec(specs);
     }
 
     @Test
     public void isSatisfiedWhenNoSpecs() {
-        assertTrue(new org.gradle.api.specs.OrSpec().isSatisfiedBy(new Object()));
+        assertTrue(new OrSpec().isSatisfiedBy(new Object()));
     }
     
     @Test
@@ -41,6 +41,6 @@ public class OrSpecTest extends AbstractCompositeSpecTest {
 
     @Test
     public void isSatisfiedByWithAllFalse() {
-        assertFalse(new AndSpec(createAtomicElements(false, false, false)).isSatisfiedBy(context.mock(Dependency.class)));
+        assertFalse(new OrSpec(createAtomicElements(false, false, false)).isSatisfiedBy(context.mock(Dependency.class)));
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/specs/SpecsTest.groovy b/subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/SpecsTest.groovy
similarity index 100%
rename from subprojects/core/src/test/groovy/org/gradle/api/specs/SpecsTest.groovy
rename to subprojects/base-services-groovy/src/test/groovy/org/gradle/api/specs/SpecsTest.groovy
diff --git a/subprojects/base-services/base-services.gradle b/subprojects/base-services/base-services.gradle
index 2a2dcff..3233875 100644
--- a/subprojects/base-services/base-services.gradle
+++ b/subprojects/base-services/base-services.gradle
@@ -7,6 +7,7 @@
 dependencies {
     publishCompile libraries.slf4j_api
     compile libraries.guava
+    compile libraries.commons_lang
     testCompile libraries.groovy
 }
 
diff --git a/subprojects/base-services/src/integTest/groovy/org/gradle/internal/SystemPropertiesIntegrationTest.groovy b/subprojects/base-services/src/integTest/groovy/org/gradle/internal/SystemPropertiesIntegrationTest.groovy
new file mode 100644
index 0000000..175c463
--- /dev/null
+++ b/subprojects/base-services/src/integTest/groovy/org/gradle/internal/SystemPropertiesIntegrationTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.test.fixtures.concurrent.ConcurrentSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+
+class SystemPropertiesIntegrationTest extends ConcurrentSpec {
+    @Rule TestNameTestDirectoryProvider temporaryFolder
+
+    def "creates Java compiler for mismatching Java home directory for multiple threads concurrently"() {
+        final int threadCount = 100
+        Factory<String> factory = Mock()
+        String factoryCreationResult = 'test'
+        File originalJavaHomeDir = SystemProperties.instance.javaHomeDir
+        File providedJavaHomeDir = temporaryFolder.file('my/test/java/home/toolprovider')
+
+        when:
+        async {
+            threadCount.times {
+                start {
+                    String expectedString = SystemProperties.instance.withJavaHome(providedJavaHomeDir, factory)
+                    assert factoryCreationResult == expectedString
+                }
+            }
+        }
+
+        then:
+        threadCount * factory.create() >> {
+            assert SystemProperties.instance.javaHomeDir == providedJavaHomeDir
+            factoryCreationResult
+        }
+        assert SystemProperties.instance.javaHomeDir == originalJavaHomeDir
+    }
+}
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 c6dff4e..d8d54df 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
@@ -72,6 +72,14 @@ public enum JavaVersion {
         return toVersion(System.getProperty("java.version"));
     }
 
+    public static JavaVersion forClassVersion(int classVersion) {
+        int index = classVersion - 45; //class file versions: 1.1 == 45, 1.2 == 46...
+        if (index > 0 && index < values().length && values()[index].hasMajorVersion) {
+            return values()[index];
+        }
+        throw new IllegalArgumentException(String.format("Could not determine java version from '%d'.", classVersion));
+    }
+
     public boolean isJava5() {
         return this == VERSION_1_5;
     }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/Named.java b/subprojects/base-services/src/main/java/org/gradle/api/Named.java
index f1a9820..67cc4dc 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/Named.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/Named.java
@@ -38,13 +38,17 @@ public interface Named {
      * An implementation of the namer interface for objects implementing the named interface.
      */
     public static class Namer implements org.gradle.api.Namer<Named> {
+
+        public static final org.gradle.api.Namer<Named> INSTANCE = new Namer();
+
         public String determineName(Named object) {
             return object.getName();
         }
-        
+
+        @SuppressWarnings("unchecked")
         public static <T> org.gradle.api.Namer<? super T> forType(Class<? extends T> type) {
             if (Named.class.isAssignableFrom(type)) {
-                return (org.gradle.api.Namer<T>)new Namer();
+                return (org.gradle.api.Namer<T>) INSTANCE;
             } else {
                 throw new IllegalArgumentException(String.format("The '%s' cannot be used with FactoryNamedDomainObjectContainer without specifying a Namer as it does not implement the Named interface.", type));
             }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/Transformer.java b/subprojects/base-services/src/main/java/org/gradle/api/Transformer.java
index aadb125..43cf474 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/Transformer.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/Transformer.java
@@ -17,18 +17,18 @@ package org.gradle.api;
 
 /**
  * <p>A {@code Transformer} transforms objects of type.</p>
- * 
+ *
  * <p>Implementations are free to return new objects or mutate the incoming value.</p>
  *
- * @param <R> The type the value is transformed to.
- * @param <I> The type of the value to be transformed.
+ * @param <OUT> The type the value is transformed to.
+ * @param <IN> The type of the value to be transformed.
  */
-public interface Transformer<R, I> {
+public interface Transformer<OUT, IN> {
     /**
      * Transforms the given object, and returns the transformed value.
      *
-     * @param original The object to transform.
+     * @param in The object to transform.
      * @return The transformed object.
      */
-    R transform(I original);
+    OUT transform(IN in);
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Actions.java b/subprojects/base-services/src/main/java/org/gradle/internal/Actions.java
index 0fe20f7..e1d5704 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Actions.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/Actions.java
@@ -21,9 +21,7 @@ import org.gradle.api.Transformer;
 import org.gradle.api.specs.Spec;
 
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.Arrays;
 
 public abstract class Actions {
 
@@ -39,7 +37,8 @@ public abstract class Actions {
     }
 
     private static class NullAction<T> implements Action<T>, Serializable {
-        public void execute(T t) {}
+        public void execute(T t) {
+        }
     }
 
     /**
@@ -49,7 +48,7 @@ public abstract class Actions {
      * @param <T> The type of the object that action is for
      * @return The composite action.
      */
-    public static <T> Action<T> composite(Iterable<Action<? super T>> actions) {
+    public static <T> Action<T> composite(Iterable<? extends Action<? super T>> actions) {
         return new CompositeAction<T>(actions);
     }
 
@@ -61,15 +60,13 @@ public abstract class Actions {
      * @return The composite action.
      */
     public static <T> Action<T> composite(Action<? super T>... actions) {
-        final List<Action<? super T>> actionsCopy = new ArrayList<Action<? super T>>(actions.length);
-        Collections.addAll(actionsCopy, actions);
-        return composite(actionsCopy);
-   }
+        return composite(Arrays.asList(actions));
+    }
 
     private static class CompositeAction<T> implements Action<T> {
-        private final Iterable<Action<? super T>> actions;
+        private final Iterable<? extends Action<? super T>> actions;
 
-        private CompositeAction(Iterable<Action<? super T>> actions) {
+        private CompositeAction(Iterable<? extends Action<? super T>> actions) {
             this.actions = actions;
         }
 
@@ -153,6 +150,7 @@ public abstract class Actions {
      * @return An action that runs the given runnable, ignoring the argument.
      */
     public static <T> Action<T> toAction(Runnable runnable) {
+        //TODO SF this method accepts Closure instance as parameter but does not work correctly for it
         if (runnable == null) {
             return Actions.doNothing();
         } else {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/BiAction.java b/subprojects/base-services/src/main/java/org/gradle/internal/BiAction.java
new file mode 100644
index 0000000..ada7641
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/BiAction.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public interface BiAction<A, B> {
+
+    void execute(A a, B b);
+
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/BiActions.java b/subprojects/base-services/src/main/java/org/gradle/internal/BiActions.java
new file mode 100644
index 0000000..b84a582
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/BiActions.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public abstract class BiActions {
+
+    private static final BiAction<Object, Object> NOOP = new BiAction<Object, Object>() {
+        public void execute(Object o, Object o2) {
+
+        }
+    };
+
+    private BiActions() {
+    }
+
+    public static BiAction<Object, Object> doNothing() {
+        return NOOP;
+    }
+
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Cast.java b/subprojects/base-services/src/main/java/org/gradle/internal/Cast.java
index 39ded66..47b38c2 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Cast.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/Cast.java
@@ -43,4 +43,8 @@ public abstract class Cast {
         }
     }
 
+    @SuppressWarnings("unchecked")
+    public static <T> T uncheckedCast(Object object) {
+        return (T) object;
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Factories.java b/subprojects/base-services/src/main/java/org/gradle/internal/Factories.java
index 1085221..c082234 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Factories.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/Factories.java
@@ -32,4 +32,8 @@ public abstract class Factories {
             }
         };
     }
+
+    public static <T> Factory<T> constantNull() {
+        return constant(null);
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Pair.java b/subprojects/base-services/src/main/java/org/gradle/internal/Pair.java
new file mode 100644
index 0000000..bb8a402
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/Pair.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 com.google.common.base.Function;
+
+public final class Pair<L, R> {
+
+    public final L left;
+    public final R right;
+
+    private Pair(L left, R right) {
+        this.left = left;
+        this.right = right;
+    }
+
+    public L getLeft() {
+        return left;
+    }
+
+    public R getRight() {
+        return right;
+    }
+
+    public L left() {
+        return left;
+    }
+
+    public R right() {
+        return right;
+    }
+
+    public static <L, R> Pair<L, R> of(L left, R right) {
+        return new Pair<L, R>(left, right);
+    }
+
+    public <T> Pair<T, Pair<L, R>> pushLeft(T t) {
+        return of(t, this);
+    }
+
+    public <T> Pair<Pair<L, R>, T> pushRight(T t) {
+        return of(this, t);
+    }
+
+    public <T> Pair<Pair<T, L>, R> nestLeft(T t) {
+        return of(of(t, left), right);
+    }
+
+    public <T> Pair<L, Pair<T, R>> nestRight(T t) {
+        return of(left, of(t, right));
+    }
+
+    public <T> Pair<T, R> mapLeft(Function<? super L, ? extends T> function) throws Exception {
+        return of(function.apply(left), right);
+    }
+
+    public <T> Pair<L, T> mapRight(Function<? super R, ? extends T> function) throws Exception {
+        return of(left, function.apply(right));
+    }
+
+    public <T> T map(Function<? super Pair<L, R>, ? extends T> function) throws Exception {
+        return function.apply(this);
+    }
+
+    public static <L, T extends Pair<L, ?>> Function<T, L> unpackLeft() {
+        return new Function<T, L>() {
+            @Override
+            public L apply(T tuple) {
+                return tuple.left;
+            }
+        };
+    }
+
+    public static <R, T extends Pair<?, R>> Function<T, R> unpackRight() {
+        return new Function<T, R>() {
+            @Override
+            public R apply(T tuple) {
+                return tuple.right;
+            }
+        };
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Pair<?, ?> pair = (Pair<?, ?>) o;
+
+        return !(left != null ? !left.equals(pair.left) : pair.left != null) && !(right != null ? !right.equals(pair.right) : pair.right != null);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = left != null ? left.hashCode() : 0;
+        result = 31 * result + (right != null ? right.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "Pair[" + left + "," + right + ']';
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Supplier.java b/subprojects/base-services/src/main/java/org/gradle/internal/Supplier.java
deleted file mode 100644
index 7f64a15..0000000
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Supplier.java
+++ /dev/null
@@ -1,25 +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.internal;
-
-import org.gradle.api.Transformer;
-
-public interface Supplier<I> {
-
-    <R> R supplyTo(Transformer<R, ? super I> transformer);
-
-}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Suppliers.java b/subprojects/base-services/src/main/java/org/gradle/internal/Suppliers.java
deleted file mode 100644
index d57f4ed..0000000
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Suppliers.java
+++ /dev/null
@@ -1,64 +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.internal;
-
-import org.gradle.api.Transformer;
-
-import java.io.Closeable;
-
-public abstract class Suppliers {
-
-    public static <I extends Closeable> Supplier<I> ofQuietlyClosed(final Factory<I> factory) {
-        return new Supplier<I>() {
-            public <R> R supplyTo(Transformer<R, ? super I> transformer) {
-                I thing = factory.create();
-                try {
-                    return transformer.transform(thing);
-                } finally {
-                    try {
-                        thing.close();
-                    } catch (Exception ignore) {
-                        // ignore
-                    }
-                }
-            }
-        };
-    }
-
-    public static <I> Supplier<I> of(final Factory<? extends I> factory) {
-        return new Supplier<I>() {
-            public <R> R supplyTo(Transformer<R, ? super I> transformer) {
-                I value = factory.create();
-                return transformer.transform(value);
-            }
-        };
-    }
-
-    public static <I, T> Supplier<T> wrap(final Supplier<? extends I> supplier, final Transformer<T, ? super I> suppliedTransformer) {
-        return new Supplier<T>() {
-            public <R> R supplyTo(final Transformer<R, ? super T> transformer) {
-                return supplier.supplyTo(new Transformer<R, I>() {
-                    public R transform(I original) {
-                        T transformedSupplied = suppliedTransformer.transform(original);
-                        return transformer.transform(transformedSupplied);
-                    }
-                });
-            }
-        };
-    }
-
-}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/SystemProperties.java b/subprojects/base-services/src/main/java/org/gradle/internal/SystemProperties.java
index b161335..bfc11a0 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/SystemProperties.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/SystemProperties.java
@@ -18,6 +18,8 @@ package org.gradle.internal;
 import java.io.File;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import static com.google.common.collect.ImmutableSet.of;
 
@@ -60,36 +62,72 @@ public class SystemProperties {
             "java.runtime.version"
     );
 
+    private static final SystemProperties INSTANCE = new SystemProperties();
+    private final Lock lock = new ReentrantLock();
+
+    public static SystemProperties getInstance() {
+        return INSTANCE;
+    }
+
     @SuppressWarnings("unchecked")
-    public static Map<String, String> asMap() {
+    public Map<String, String> asMap() {
         return (Map) System.getProperties();
     }
 
-    public static String getLineSeparator() {
+    public String getLineSeparator() {
         return System.getProperty("line.separator");
     }
 
-    public static String getJavaIoTmpDir() {
+    public String getJavaIoTmpDir() {
         return System.getProperty("java.io.tmpdir");
     }
 
-    public static String getUserHome() {
+    public String getUserHome() {
         return System.getProperty("user.home");
     }
 
-    public static String getJavaVersion() {
+    public String getJavaVersion() {
         return System.getProperty("java.version");
     }
 
-    public static File getCurrentDir() {
+    public File getCurrentDir() {
         return new File(System.getProperty("user.dir"));
     }
 
+    public File getJavaHomeDir() {
+        return new File(System.getProperty("java.home"));
+    }
+
+    public void setJavaHomeDir(File javaHomeDir) {
+        System.setProperty("java.home", javaHomeDir.getAbsolutePath());
+    }
+
+    /**
+     * Creates instance for Factory implementation with the provided Java home directory. Setting the "java.home" system property is thread-safe
+     * and is set back to the original value of "java.home" after the operation.
+     *
+     * @param javaHomeDir Java home directory
+     * @param factory Factory
+     * @return Instance created by Factory implementation
+     */
+    public <T> T withJavaHome(File javaHomeDir, Factory<T> factory) {
+        lock.lock();
+        File originalJavaHomeDir = getJavaHomeDir();
+        setJavaHomeDir(javaHomeDir);
+
+        try {
+            return factory.create();
+        } finally {
+            setJavaHomeDir(originalJavaHomeDir);
+            lock.unlock();
+        }
+    }
+
     /**
      * Returns the keys that are guaranteed to be contained in System.getProperties() by default,
      * as specified in the Javadoc for that method.
      */
-    public static Set<String> getStandardProperties() {
+    public Set<String> getStandardProperties() {
         return STANDARD_PROPERTIES;
     }
 
@@ -100,7 +138,7 @@ public class SystemProperties {
      * @return the set of keys of {@code System.getProperties()} which should not be adjusted
      *   by client code. This method never returns {@code null}.
      */
-    public static Set<String> getNonStandardImportantProperties() {
+    public Set<String> getNonStandardImportantProperties() {
         return IMPORTANT_NON_STANDARD_PROPERTIES;
     }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/Transformers.java b/subprojects/base-services/src/main/java/org/gradle/internal/Transformers.java
index edde1a0..3f4c4d4 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/Transformers.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/Transformers.java
@@ -81,11 +81,12 @@ public abstract class Transformers {
      * @return The naming transformer.
      */
     public static Transformer<String, Named> name() {
-        return name(new Named.Namer());
+        return name(Named.Namer.INSTANCE);
     }
 
     /**
      * Returns a transformer that names objects with the given {@link Namer}
+     *
      * @param namer The namer to name the objects with
      * @param <T> The type of objects to be named
      * @return The naming transformer.
@@ -122,9 +123,14 @@ public abstract class Transformers {
         };
     }
 
-    /**
-     * Converts an {@link Action} to a {@link Transformer} that runs the action against the input value and returns {@code null}.
-     */
+    public static <R> Transformer<R, Object> toTransformer(final Factory<R> factory) {
+        return new Transformer<R, Object>() {
+            public R transform(Object original) {
+                return factory.create();
+            }
+        };
+    }
+
     public static <R, I> Transformer<R, I> toTransformer(final Action<? super I> action) {
         return new Transformer<R, I>() {
             public R transform(I original) {
@@ -148,4 +154,15 @@ public abstract class Transformers {
             }
         };
     }
+
+    /**
+     * Always returns the given argument.
+     */
+    public static <T, I> Transformer<T, I> constant(final T t) {
+        return new Transformer<T, I>() {
+            public T transform(I original) {
+                return t;
+            }
+        };
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/TriAction.java b/subprojects/base-services/src/main/java/org/gradle/internal/TriAction.java
new file mode 100644
index 0000000..ef83492
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/TriAction.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public interface TriAction<A, B, C> {
+
+    void execute(A a, B b, C c);
+
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/UncheckedException.java b/subprojects/base-services/src/main/java/org/gradle/internal/UncheckedException.java
index 8e6119a..7ad53b4 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/UncheckedException.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/UncheckedException.java
@@ -16,6 +16,9 @@
 
 package org.gradle.internal;
 
+import org.gradle.api.UncheckedIOException;
+
+import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 
 /**
@@ -36,6 +39,9 @@ public final class UncheckedException extends RuntimeException {
         if (t instanceof Error) {
             throw (Error) t;
         }
+        if (t instanceof IOException) {
+            throw new UncheckedIOException(t);
+        }
         throw new UncheckedException(t);
     }
 
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
index 7bad876..07c5299 100644
--- 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
@@ -23,9 +23,11 @@ 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();
+    private final ClassLoader parent;
 
     public CachingClassLoader(ClassLoader parent) {
         super(parent);
+        this.parent = parent;
     }
 
     @Override
@@ -63,4 +65,20 @@ public class CachingClassLoader extends ClassLoader implements ClassLoaderHierar
             return getClass().getName().hashCode();
         }
     }
+
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CachingClassLoader)) {
+            return false;
+        }
+
+        CachingClassLoader that = (CachingClassLoader) o;
+        return parent.equals(that.parent);
+    }
+
+    public int hashCode() {
+        return parent.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
index 90d37a8..fe2d1b0 100644
--- 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
@@ -22,6 +22,11 @@ import java.util.List;
 
 public interface ClassLoaderFactory {
     /**
+     * Returns the ClassLoader that will be used as the parent for all isolated ClassLoaders.
+     */
+    ClassLoader getIsolatedSystemClassLoader();
+
+    /**
      * Creates a ClassLoader implementation which has only the classes from the specified URIs and the Java API visible.
      */
     ClassLoader createIsolatedClassLoader(ClassPath classPath);
@@ -32,8 +37,7 @@ public interface ClassLoaderFactory {
     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.
+     * 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
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
index eaa5af1..7190f49 100644
--- 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
@@ -26,6 +26,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.security.CodeSource;
 import java.util.*;
 
 public class ClasspathUtil {
@@ -60,14 +61,22 @@ public class ClasspathUtil {
     public static File getClasspathForClass(Class<?> targetClass) {
         URI location;
         try {
-            location = targetClass.getProtectionDomain().getCodeSource().getLocation().toURI();
+            CodeSource codeSource = targetClass.getProtectionDomain().getCodeSource();
+            if (codeSource != null && codeSource.getLocation() != null) {
+                location = codeSource.getLocation().toURI();
+                if (location.getScheme().equals("file")) {
+                    return new File(location);
+                }
+            }
+            String resourceName = targetClass.getName().replace(".", "/") + ".class";
+            URL resource = targetClass.getClassLoader().getResource(resourceName);
+            if (resource != null) {
+                return getClasspathForResource(resource, resourceName);
+            }
+            throw new GradleException(String.format("Cannot determine classpath for class %s.", targetClass.getName()));
         } 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) {
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
index 4e99d57..1a8177e 100644
--- 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
@@ -32,6 +32,11 @@ import java.util.Collection;
 import java.util.List;
 
 public class DefaultClassLoaderFactory implements ClassLoaderFactory {
+    @Override
+    public ClassLoader getIsolatedSystemClassLoader() {
+        return ClassLoader.getSystemClassLoader().getParent();
+    }
+
     public ClassLoader createIsolatedClassLoader(Iterable<URI> uris) {
         return doCreateIsolatedClassLoader(CollectionUtils.collect(uris, Transformers.toURL()));
     }
@@ -63,7 +68,7 @@ public class DefaultClassLoaderFactory implements ClassLoaderFactory {
             }
         }
 
-        return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), ClassLoader.getSystemClassLoader().getParent());
+        return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), getIsolatedSystemClassLoader());
     }
 
     public FilteringClassLoader createFilteringClassLoader(ClassLoader parent) {
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
index 95a1573..d1e5144 100644
--- 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
@@ -36,6 +36,7 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
     private final Set<String> resourceNames = new HashSet<String>();
     private final Set<String> classNames = new HashSet<String>();
     private final Set<String> disallowedClassNames = new HashSet<String>();
+    private final Set<String> disallowedPackagePrefixes = new HashSet<String>();
 
     static {
         EXT_CLASS_LOADER = ClassLoader.getSystemClassLoader().getParent();
@@ -57,11 +58,12 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
         resourceNames.addAll(spec.resourceNames);
         resourcePrefixes.addAll(spec.resourcePrefixes);
         classNames.addAll(spec.classNames);
-        disallowedClassNames.addAll(spec.classNames);
+        disallowedClassNames.addAll(spec.disallowedClassNames);
+        disallowedPackagePrefixes.addAll(spec.disallowedPackagePrefixes);
     }
 
     public void visit(ClassLoaderVisitor visitor) {
-        visitor.visitSpec(new Spec(classNames, packageNames, packagePrefixes, resourcePrefixes, resourceNames, disallowedClassNames));
+        visitor.visitSpec(new Spec(classNames, packageNames, packagePrefixes, resourcePrefixes, resourceNames, disallowedClassNames, disallowedPackagePrefixes));
         visitor.visitParent(getParent());
     }
 
@@ -74,7 +76,7 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
         }
 
         if (!classAllowed(name)) {
-            throw new ClassNotFoundException(String.format("%s not found.", name));
+            throw new ClassNotFoundException(name + " not found.");
         }
 
         Class<?> cl = super.loadClass(name, false);
@@ -134,6 +136,12 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
     }
 
     private boolean allowed(Package pkg) {
+        for (String packagePrefix : disallowedPackagePrefixes) {
+            if (pkg.getName().startsWith(packagePrefix)) {
+                return false;
+            }
+        }
+
         if (SYSTEM_PACKAGES.contains(pkg.getName())) {
             return true;
         }
@@ -152,6 +160,12 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
         if (disallowedClassNames.contains(className)) {
             return false;
         }
+        for (String packagePrefix : disallowedPackagePrefixes) {
+            if (className.startsWith(packagePrefix)) {
+                return false;
+            }
+        }
+
         if (classNames.contains(className)) {
             return true;
         }
@@ -193,6 +207,15 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
     }
 
     /**
+     * Marks a package and all its sub-packages as not visible. Does not affect resources in those packages.
+     *
+     * @param packagePrefix the package prefix
+     */
+    public void disallowPackage(String packagePrefix) {
+        disallowedPackagePrefixes.add(packagePrefix + ".");
+    }
+
+    /**
      * Marks all resources with the given prefix as visible.
      *
      * @param resourcePrefix the resource prefix
@@ -218,14 +241,17 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
         final Set<String> resourceNames;
         final Set<String> classNames;
         final Set<String> disallowedClassNames;
+        final Set<String> disallowedPackagePrefixes;
+
 
-        public Spec(Collection<String> classNames, Collection<String> packageNames, Collection<String> packagePrefixes, Collection<String> resourcePrefixes, Collection<String> resourceNames, Collection<String> disallowedClassNames) {
+        public Spec(Collection<String> classNames, Collection<String> packageNames, Collection<String> packagePrefixes, Collection<String> resourcePrefixes, Collection<String> resourceNames, Collection<String> disallowedClassNames, Collection<String> disallowedPackagePrefixes) {
             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);
+            this.disallowedPackagePrefixes = new HashSet<String>(disallowedPackagePrefixes);
         }
 
         @Override
@@ -242,7 +268,8 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
                     && other.resourceNames.equals(resourceNames)
                     && other.resourcePrefixes.equals(resourcePrefixes)
                     && other.classNames.equals(classNames)
-                    && other.disallowedClassNames.equals(disallowedClassNames);
+                    && other.disallowedClassNames.equals(disallowedClassNames)
+                    && other.disallowedPackagePrefixes.equals(disallowedPackagePrefixes);
         }
 
         @Override
@@ -252,7 +279,8 @@ public class FilteringClassLoader extends ClassLoader implements ClassLoaderHier
                     ^ resourceNames.hashCode()
                     ^ resourcePrefixes.hashCode()
                     ^ classNames.hashCode()
-                    ^ disallowedClassNames.hashCode();
+                    ^ disallowedClassNames.hashCode()
+                    ^ disallowedPackagePrefixes.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
index 9031999..4857c30 100644
--- 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
@@ -15,8 +15,8 @@
  */
 package org.gradle.internal.classloader;
 
-import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.gradle.internal.reflect.JavaMethod;
+import org.gradle.internal.reflect.JavaReflectionUtil;
 
 import java.io.IOException;
 import java.net.URL;
@@ -30,9 +30,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
  * 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 static final JavaMethod<ClassLoader, Package[]> GET_PACKAGES_METHOD = JavaReflectionUtil.method(ClassLoader.class, Package[].class, "getPackages");
+    private static final JavaMethod<ClassLoader, Package> GET_PACKAGE_METHOD = JavaReflectionUtil.method(ClassLoader.class, Package.class, "getPackage", String.class);
+
     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));
@@ -41,8 +43,6 @@ public class MultiParentClassLoader extends ClassLoader implements ClassLoaderHi
     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) {
@@ -71,7 +71,7 @@ public class MultiParentClassLoader extends ClassLoader implements ClassLoaderHi
     @Override
     protected Package getPackage(String name) {
         for (ClassLoader parent : parents) {
-            Package p = getPackageMethod.invoke(parent, name);
+            Package p = GET_PACKAGE_METHOD.invoke(parent, name);
             if (p != null) {
                 return p;
             }
@@ -83,7 +83,7 @@ public class MultiParentClassLoader extends ClassLoader implements ClassLoaderHi
     protected Package[] getPackages() {
         Set<Package> packages = new LinkedHashSet<Package>();
         for (ClassLoader parent : parents) {
-            Package[] parentPackages = getPackagesMethod.invoke(parent);
+            Package[] parentPackages = GET_PACKAGES_METHOD.invoke(parent);
             packages.addAll(Arrays.asList(parentPackages));
         }
         return packages.toArray(new Package[packages.size()]);
@@ -123,4 +123,23 @@ public class MultiParentClassLoader extends ClassLoader implements ClassLoaderHi
             return getClass().getName().hashCode();
         }
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof MultiParentClassLoader)) {
+            return false;
+        }
+
+        MultiParentClassLoader that = (MultiParentClassLoader) o;
+
+        return parents.equals(that.parents);
+    }
+
+    @Override
+    public int hashCode() {
+        return parents.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
index 4f5f6ea..d90f365 100755
--- 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
@@ -70,6 +70,11 @@ public class MutableURLClassLoader extends URLClassLoader implements ClassLoader
         }
 
         @Override
+        public String toString() {
+            return String.format("[%s classpath:%s]", getClass().getSimpleName(), classpath);
+        }
+
+        @Override
         public boolean equals(Object obj) {
             if (obj == this) {
                 return true;
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classpath/ClassPath.java b/subprojects/base-services/src/main/java/org/gradle/internal/classpath/ClassPath.java
index ab65707..7b5dcfb 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/classpath/ClassPath.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classpath/ClassPath.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.net.URI;
 import java.net.URL;
 import java.util.Collection;
+import java.util.List;
 
 /**
  * An immutable classpath.
@@ -27,11 +28,11 @@ import java.util.Collection;
 public interface ClassPath {
     boolean isEmpty();
 
-    Collection<URI> getAsURIs();
+    List<URI> getAsURIs();
 
-    Collection<File> getAsFiles();
+    List<File> getAsFiles();
 
-    Collection<URL> getAsURLs();
+    List<URL> getAsURLs();
 
     URL[] getAsURLArray();
 
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classpath/DefaultClassPath.java b/subprojects/base-services/src/main/java/org/gradle/internal/classpath/DefaultClassPath.java
index 6198267..cc6debb 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/classpath/DefaultClassPath.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classpath/DefaultClassPath.java
@@ -23,10 +23,7 @@ import java.io.Serializable;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 /**
  * An immutable classpath.
@@ -35,14 +32,21 @@ public class DefaultClassPath implements ClassPath, Serializable {
     private final List<File> files;
 
     public DefaultClassPath(Iterable<File> files) {
-        this.files = new ArrayList<File>();
+        Set<File> noDuplicates = new LinkedHashSet<File>();
         for (File file : files) {
-            this.files.add(file);
+            noDuplicates.add(file);
         }
+        this.files = new ArrayList<File>(noDuplicates);
     }
-    
+
+    private DefaultClassPath(Set<File> files) {
+        this.files = new ArrayList<File>(files);
+    }
+
     public DefaultClassPath(File... files) {
-        this(Arrays.asList(files));
+        Set<File> noDuplicates = new LinkedHashSet<File>();
+        Collections.addAll(noDuplicates, files);
+        this.files = new ArrayList<File>(noDuplicates);
     }
 
     @Override
@@ -54,7 +58,7 @@ public class DefaultClassPath implements ClassPath, Serializable {
         return files.isEmpty();
     }
 
-    public Collection<URI> getAsURIs() {
+    public List<URI> getAsURIs() {
         List<URI> urls = new ArrayList<URI>();
         for (File file : files) {
             urls.add(file.toURI());
@@ -62,7 +66,7 @@ public class DefaultClassPath implements ClassPath, Serializable {
         return urls;
     }
 
-    public Collection<File> getAsFiles() {
+    public List<File> getAsFiles() {
         return files;
     }
 
@@ -71,7 +75,7 @@ public class DefaultClassPath implements ClassPath, Serializable {
         return result.toArray(new URL[result.size()]);
     }
 
-    public Collection<URL> getAsURLs() {
+    public List<URL> getAsURLs() {
         List<URL> urls = new ArrayList<URL>();
         for (File file : files) {
             try {
@@ -100,8 +104,8 @@ public class DefaultClassPath implements ClassPath, Serializable {
         return new DefaultClassPath(concat(files, other));
     }
 
-    private Iterable<File> concat(List<File> files1, Collection<File> files2) {
-        List<File> result = new ArrayList<File>();
+    private Set<File> concat(Collection<File> files1, Collection<File> files2) {
+        Set<File> result = new LinkedHashSet<File>();
         result.addAll(files1);
         result.addAll(files2);
         return result;
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/CompositeStoppable.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/CompositeStoppable.java
index 37e05f5..f94423f 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/CompositeStoppable.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/CompositeStoppable.java
@@ -17,18 +17,28 @@
 package org.gradle.internal.concurrent;
 
 import org.gradle.internal.UncheckedException;
-import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+/**
+ * A {@link org.gradle.internal.concurrent.Stoppable} that stops a collection of things. If an element implements
+ * {@link java.io.Closeable} or {@link org.gradle.internal.concurrent.Stoppable} then the appropriate close/stop
+ * method is called on that object, otherwise the element is ignored. Elements may be {@code null}, in which case they
+ * are ignored.
+ *
+ * <p>Attempts to stop as many elements as possible in the presence of failures.</p>
+ */
 public class CompositeStoppable implements Stoppable {
     private static final Logger LOGGER = LoggerFactory.getLogger(CompositeStoppable.class);
+    public static final Stoppable NO_OP_STOPPABLE = new Stoppable() {
+        public void stop() {
+        }
+    };
     private final List<Stoppable> elements = new CopyOnWriteArrayList<Stoppable>();
 
     public CompositeStoppable() {
@@ -56,17 +66,7 @@ public class CompositeStoppable implements Stoppable {
         return this;
     }
 
-    private static void invoke(Method method, Object target, Object... args) {
-        JavaReflectionUtil.method(target, Object.class, method).invoke(target, args);
-    }
-
     private static Stoppable toStoppable(final Object object) {
-        if (object == null) {
-            return new Stoppable() {
-                public void stop() {
-                }
-            };
-        }
         if (object instanceof Stoppable) {
             return (Stoppable) object;
         }
@@ -87,25 +87,7 @@ public class CompositeStoppable implements Stoppable {
                 }
             };
         }
-        return new Stoppable() {
-            @Override
-            public String toString() {
-                return object.toString();
-            }
-
-            public void stop() {
-                try {
-                    invoke(object.getClass().getMethod("stop"), object);
-                } catch (NoSuchMethodException e) {
-                    // ignore
-                }
-                try {
-                    invoke(object.getClass().getMethod("close"), object);
-                } catch (NoSuchMethodException e) {
-                    // ignore
-                }
-            }
-        };
+        return NO_OP_STOPPABLE;
     }
 
     public void stop() {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
index a9069c6..bf34718 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
@@ -16,18 +16,14 @@
 
 package org.gradle.internal.concurrent;
 
-import org.gradle.internal.UncheckedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.util.Set;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 public class DefaultExecutorFactory implements ExecutorFactory, Stoppable {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExecutorFactory.class);
-    private final Set<StoppableExecutorImpl> executors = new CopyOnWriteArraySet<StoppableExecutorImpl>();
+    private final Set<StoppableExecutor> executors = new CopyOnWriteArraySet<StoppableExecutor>();
 
     public void stop() {
         try {
@@ -38,7 +34,7 @@ public class DefaultExecutorFactory implements ExecutorFactory, Stoppable {
     }
 
     public StoppableExecutor create(String displayName) {
-        StoppableExecutorImpl executor = new StoppableExecutorImpl(createExecutor(displayName));
+        StoppableExecutor executor = new TrackedStoppableExecutor(createExecutor(displayName), new ExecutorPolicy.CatchAndRecordFailures());
         executors.add(executor);
         return executor;
     }
@@ -47,80 +43,27 @@ public class DefaultExecutorFactory implements ExecutorFactory, Stoppable {
         return Executors.newCachedThreadPool(new ThreadFactoryImpl(displayName));
     }
 
-    private class StoppableExecutorImpl implements StoppableExecutor {
-        private final ExecutorService executor;
-        private final ThreadLocal<Runnable> executing = new ThreadLocal<Runnable>();
-        private final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
-
-        public StoppableExecutorImpl(ExecutorService executor) {
-            this.executor = executor;
-        }
-
-        public void execute(final Runnable command) {
-            executor.execute(new Runnable() {
-                public void run() {
-                    executing.set(command);
-                    try {
-                        command.run();
-                    } catch (Throwable throwable) {
-                        if (!failure.compareAndSet(null, throwable)) {
-                            LOGGER.error(String.format("Failed to execute %s.", command), throwable);
-                        }
-                    } finally {
-                        executing.set(null);
-                    }
-                }
-            });
-        }
+    public StoppableExecutor create(String displayName, int fixedSize) {
+        StoppableExecutor executor = new TrackedStoppableExecutor(createExecutor(displayName, fixedSize), new ExecutorPolicy.PropagateFailures());
+        executors.add(executor);
+        return executor;
+    }
 
-        public void requestStop() {
-            executor.shutdown();
-        }
+    protected ExecutorService createExecutor(String displayName, int fixedSize) {
+        return Executors.newFixedThreadPool(fixedSize, new ThreadFactoryImpl(displayName));
+    }
 
-        public void stop() {
-            stop(Integer.MAX_VALUE, TimeUnit.SECONDS);
+    private class TrackedStoppableExecutor extends StoppableExecutorImpl {
+        public TrackedStoppableExecutor(ExecutorService executor, ExecutorPolicy executorPolicy) {
+            super(executor, executorPolicy);
         }
 
         public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
-            requestStop();
-            if (executing.get() != null) {
-                throw new IllegalStateException("Cannot stop this executor from an executor thread.");
-            }
             try {
-                try {
-                    if (!executor.awaitTermination(timeoutValue, timeoutUnits)) {
-                        executor.shutdownNow();
-                        throw new IllegalStateException("Timeout waiting for concurrent jobs to complete.");
-                    }
-                } catch (InterruptedException e) {
-                    throw new UncheckedException(e);
-                }
-                if (failure.get() != null) {
-                    throw UncheckedException.throwAsUncheckedException(failure.get());
-                }
+                super.stop(timeoutValue, timeoutUnits);
             } finally {
                 executors.remove(this);
             }
         }
     }
-
-    private static class ThreadFactoryImpl implements ThreadFactory {
-        private final AtomicLong counter = new AtomicLong();
-        private final String displayName;
-
-        public ThreadFactoryImpl(String displayName) {
-            this.displayName = displayName;
-        }
-
-        public Thread newThread(Runnable r) {
-            Thread thread = new Thread(r);
-            long count = counter.incrementAndGet();
-            if (count == 1) {
-                thread.setName(displayName);
-            } else {
-                thread.setName(String.format("%s Thread %s", displayName, count));
-            }
-            return thread;
-        }
-    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
index 32ddb9e..8bdb027 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
@@ -24,4 +24,14 @@ public interface ExecutorFactory {
      * @return The executor.
      */
     StoppableExecutor create(String displayName);
+
+    /**
+     * Creates an executor which can run multiple tasks concurrently. It is the caller's responsibility to stop the executor.
+     *
+     * @param displayName The display name for the this executor. Used for thread names, logging and error message.
+     * @param fixedSize The maximum number of threads allowed
+     *
+     * @return The executor.
+     */
+    StoppableExecutor create(String displayName, int fixedSize);
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorPolicy.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorPolicy.java
new file mode 100644
index 0000000..3305508
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorPolicy.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Controls the behavior of an executor when a task is executed and an executor is stopped.
+ */
+interface ExecutorPolicy {
+    /**
+     * Special behavior when a task is executed.
+     *
+     * The Runnable's run() needs to be called from this method.
+     */
+    void onExecute(Runnable command);
+
+    /**
+     * Special behavior after an executor is stopped.
+     *
+     * This is called after the underlying Executor has been stopped.
+     */
+    void onStop();
+
+    /**
+     * Runs the Runnable, catches all Throwables and logs them.
+     *
+     * The first exception caught during onExecute(), will be rethrown in onStop().
+     */
+    static class CatchAndRecordFailures implements ExecutorPolicy {
+        private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExecutorFactory.class);
+        private final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
+
+        public void onExecute(Runnable command) {
+            try {
+                command.run();
+            } catch (Throwable throwable) {
+                // Capture and log all failures
+                if (!failure.compareAndSet(null, throwable)) {
+                    LOGGER.error(String.format("Failed to execute %s.", command), throwable);
+                }
+            }
+        }
+
+        public void onStop() {
+            // Rethrow the first failure
+            if (failure.get() != null) {
+                throw UncheckedException.throwAsUncheckedException(failure.get());
+            }
+        }
+    }
+
+    /**
+     * Just runs the Runnable and lets any exceptions propagate as usual.
+     */
+    static class PropagateFailures implements ExecutorPolicy {
+        public void onExecute(Runnable command) {
+            command.run();
+        }
+
+        public void onStop() {
+            // Do nothing
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutor.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutor.java
index d8a6815..18f0df0 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutor.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutor.java
@@ -16,10 +16,10 @@
 
 package org.gradle.internal.concurrent;
 
-import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 
-public interface StoppableExecutor extends AsyncStoppable, Executor {
+public interface StoppableExecutor extends AsyncStoppable, ExecutorService {
     /**
      * Stops accepting new jobs and blocks until all currently executing jobs have been completed.
      */
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutorImpl.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutorImpl.java
new file mode 100644
index 0000000..b248cbd
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/StoppableExecutorImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+class StoppableExecutorImpl extends AbstractExecutorService implements StoppableExecutor {
+    private final ExecutorService executor;
+    private final ThreadLocal<Runnable> executing = new ThreadLocal<Runnable>();
+    private final ExecutorPolicy executorPolicy;
+    StoppableExecutorImpl(ExecutorService executor, ExecutorPolicy executorPolicy) {
+        this.executor = executor;
+        this.executorPolicy = executorPolicy;
+    }
+
+    public void execute(final Runnable command) {
+        executor.execute(new Runnable() {
+            public void run() {
+            executing.set(command);
+            try {
+                executorPolicy.onExecute(command);
+            } finally {
+                executing.set(null);
+            }
+            }
+        });
+    }
+
+    public void requestStop() {
+        executor.shutdown();
+    }
+
+    public void stop() {
+        stop(Integer.MAX_VALUE, TimeUnit.SECONDS);
+    }
+
+    public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
+        requestStop();
+        if (executing.get() != null) {
+            throw new IllegalStateException("Cannot stop this executor from an executor thread.");
+        }
+        try {
+            if (!executor.awaitTermination(timeoutValue, timeoutUnits)) {
+                executor.shutdownNow();
+                throw new IllegalStateException("Timeout waiting for concurrent jobs to complete.");
+            }
+        } catch (InterruptedException e) {
+            throw new UncheckedException(e);
+        }
+        executorPolicy.onStop();
+    }
+
+    public void shutdown() {
+        executor.shutdown();
+    }
+
+    public List<Runnable> shutdownNow() {
+        return executor.shutdownNow();
+    }
+
+    public boolean isShutdown() {
+        return executor.isShutdown();
+    }
+
+    public boolean isTerminated() {
+        return executor.isTerminated();
+    }
+
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        return executor.awaitTermination(timeout, unit);
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ThreadFactoryImpl.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ThreadFactoryImpl.java
new file mode 100644
index 0000000..372c4ee
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ThreadFactoryImpl.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+* Provides meaningful names to threads created in a thread pool
+*/
+public class ThreadFactoryImpl implements ThreadFactory {
+    private final AtomicLong counter = new AtomicLong();
+    private final String displayName;
+
+    public ThreadFactoryImpl(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public Thread newThread(Runnable r) {
+        Thread thread = new Thread(r);
+        long count = counter.incrementAndGet();
+        if (count == 1) {
+            thread.setName(displayName);
+        } else {
+            thread.setName(String.format("%s Thread %s", displayName, count));
+        }
+        return thread;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/Contextual.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/Contextual.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/internal/exceptions/Contextual.java
rename to subprojects/base-services/src/main/java/org/gradle/internal/exceptions/Contextual.java
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java
new file mode 100644
index 0000000..45a0367
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.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.exceptions;
+
+import org.gradle.api.GradleException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class DefaultMultiCauseException extends GradleException implements MultiCauseException {
+    private final List<Throwable> causes = new CopyOnWriteArrayList<Throwable>();
+    private transient ThreadLocal<Boolean> hideCause = threadLocal();
+
+    public DefaultMultiCauseException(String message) {
+        super(message);
+    }
+
+    public DefaultMultiCauseException(String message, Throwable... causes) {
+        super(message);
+        this.causes.addAll(Arrays.asList(causes));
+    }
+
+    public DefaultMultiCauseException(String message, Iterable<? extends Throwable> causes) {
+        super(message);
+        initCauses(causes);
+    }
+
+    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
+        inputStream.defaultReadObject();
+        hideCause = threadLocal();
+    }
+
+    private ThreadLocal<Boolean> threadLocal() {
+        return new ThreadLocal<Boolean>() {
+            @Override
+            protected Boolean initialValue() {
+                return false;
+            }
+        };
+    }
+
+    public List<? extends Throwable> getCauses() {
+        return causes;
+    }
+
+    @Override
+    public Throwable initCause(Throwable throwable) {
+        causes.clear();
+        causes.add(throwable);
+        return null;
+    }
+
+    public void initCauses(Iterable<? extends Throwable> causes) {
+        this.causes.clear();
+        for (Throwable cause : causes) {
+            this.causes.add(cause);
+        }
+    }
+
+    @Override
+    public Throwable getCause() {
+        if (hideCause.get()) {
+            return null;
+        }
+        return causes.isEmpty() ? null : causes.get(0);
+    }
+
+    @Override
+    public void printStackTrace(PrintStream printStream) {
+        PrintWriter writer = new PrintWriter(printStream);
+        printStackTrace(writer);
+        writer.flush();
+    }
+
+    @Override
+    public void printStackTrace(PrintWriter printWriter) {
+        if (causes.size() <= 1) {
+            super.printStackTrace(printWriter);
+            return;
+        }
+
+        hideCause.set(true);
+        try {
+            super.printStackTrace(printWriter);
+            for (int i = 0; i < causes.size(); i++) {
+                Throwable cause = causes.get(i);
+                printWriter.format("Cause %s: ", i + 1);
+                cause.printStackTrace(printWriter);
+            }
+        } finally {
+            hideCause.set(false);
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DiagnosticsVisitor.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DiagnosticsVisitor.java
new file mode 100644
index 0000000..255cb28
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DiagnosticsVisitor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.exceptions;
+
+public interface DiagnosticsVisitor {
+    /**
+     * Adds the description of some candidate.
+     */
+    DiagnosticsVisitor candidate(String displayName);
+
+    /**
+     * Adds an example for the previous candidate.
+     */
+    DiagnosticsVisitor example(String example);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/FormattingDiagnosticsVisitor.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/FormattingDiagnosticsVisitor.java
new file mode 100644
index 0000000..d148b0c
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/FormattingDiagnosticsVisitor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.exceptions;
+
+import com.google.common.base.Joiner;
+
+import java.util.*;
+
+/**
+ * Formats candidates as a list.
+ */
+public class FormattingDiagnosticsVisitor implements DiagnosticsVisitor {
+    private final Map<String, Candidate> candidates = new LinkedHashMap<String, Candidate>();
+    private Candidate current;
+
+    public Collection<String> getCandidates() {
+        return format(candidates);
+    }
+
+    private Collection<String> format(Map<String, Candidate> candidates) {
+        List<String> formatted = new ArrayList<String>();
+        for (Candidate candidate : candidates.values()) {
+            if (candidate.examples.isEmpty()) {
+                formatted.add(candidate.description);
+            } else {
+                formatted.add(String.format("%s, for example %s.", candidate.description, Joiner.on(", ").join(candidate.examples)));
+            }
+        }
+        return formatted;
+    }
+
+    @Override
+    public DiagnosticsVisitor candidate(String displayName) {
+        Candidate candidate = candidates.get(displayName);
+        if (candidate == null) {
+            candidate = new Candidate(displayName);
+            candidates.put(displayName, candidate);
+        }
+        current = candidate;
+        return this;
+    }
+
+    @Override
+    public DiagnosticsVisitor example(String example) {
+        current.examples.add(example);
+        return this;
+    }
+
+    private static class Candidate {
+        final String description;
+        final List<String> examples = new ArrayList<String>();
+
+        public Candidate(String description) {
+            this.description = description;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/MultiCauseException.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/MultiCauseException.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/internal/exceptions/MultiCauseException.java
rename to subprojects/base-services/src/main/java/org/gradle/internal/exceptions/MultiCauseException.java
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashUtil.java
index a13a6cc..b9b22fc 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashUtil.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashUtil.java
@@ -32,6 +32,9 @@ public class HashUtil {
     public static HashValue createHash(File file, String algorithm) {
         try {
             return createHash(new FileInputStream(file), algorithm);
+        } catch (UncheckedIOException e) {
+            // Catch any unchecked io exceptions and add the file path for troubleshooting
+            throw new UncheckedIOException(String.format("Failed to create %s hash for file %s.", algorithm, file.getAbsolutePath()), e.getCause());
         } catch (FileNotFoundException e) {
             throw new UncheckedIOException(e);
         }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashValue.java b/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashValue.java
index 35cbf3c..f123a86 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashValue.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/hash/HashValue.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.internal.hash;
 
+import com.google.common.base.Strings;
+
 import java.math.BigInteger;
 
 public class HashValue {
@@ -55,7 +57,7 @@ public class HashValue {
     }
 
     public String asCompactString() {
-        return digest.toString(32);
+        return digest.toString(36);
     }
 
     public String asHexString() {
@@ -70,6 +72,10 @@ public class HashValue {
         return digest;
     }
 
+    public String asZeroPaddedHexString(int expectedLength) {
+        return Strings.padStart(asHexString(), expectedLength, '0');
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/io/IoUtils.java b/subprojects/base-services/src/main/java/org/gradle/internal/io/IoUtils.java
new file mode 100644
index 0000000..bd3bb1f
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/io/IoUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.io;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.concurrent.CompositeStoppable;
+
+import java.io.Closeable;
+
+public abstract class IoUtils {
+
+    // TODO merge in IoActions
+
+    public static <T, C extends Closeable> T get(C resource, Transformer<T, ? super C> transformer) {
+        try {
+            return transformer.transform(resource);
+        } finally {
+            CompositeStoppable.stoppable(resource).stop();
+        }
+    }
+}
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 0967b36..66ecec9 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
@@ -17,7 +17,6 @@
 package org.gradle.internal.jvm;
 
 import java.io.File;
-import java.util.Map;
 
 public interface JavaInfo {
     /**
@@ -30,6 +29,12 @@ public interface JavaInfo {
      * @return the executable
      * @throws JavaHomeException when executable cannot be found
      */
+    File getJavacExecutable() throws JavaHomeException;
+
+    /**
+     * @return the executable
+     * @throws JavaHomeException when executable cannot be found
+     */
     File getJavadocExecutable() throws JavaHomeException;
 
     /**
@@ -56,6 +61,4 @@ public interface JavaInfo {
      * with custom jre location or if jdk is not installed.
      */
     File getToolsJar();
-
-    Map<String, ?> getInheritableEnvironmentVariables(Map<String, ?> envVars);
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JdkTools.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JdkTools.java
new file mode 100644
index 0000000..985a3fb
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/JdkTools.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.internal.classloader.DefaultClassLoaderFactory;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.reflect.DirectInstantiator;
+
+import javax.tools.JavaCompiler;
+import java.io.File;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Subset replacement for {@link javax.tools.ToolProvider} that avoids the application class loader.
+ */
+public class JdkTools {
+
+    // Copied from ToolProvider.defaultJavaCompilerName
+    private static final String DEFAULT_COMPILER_IMPL_NAME = "com.sun.tools.javac.api.JavacTool";
+    private static final AtomicReference<JdkTools> INSTANCE = new AtomicReference<JdkTools>();
+
+    private final ClassLoader isolatedToolsLoader;
+
+    public static JdkTools current() {
+        JdkTools jdkTools = INSTANCE.get();
+        if (jdkTools == null) {
+            INSTANCE.compareAndSet(null, new JdkTools(Jvm.current()));
+            jdkTools = INSTANCE.get();
+        }
+        return jdkTools;
+    }
+
+    JdkTools(JavaInfo javaInfo) {
+        File toolsJar = javaInfo.getToolsJar();
+        if (toolsJar == null) {
+            throw new IllegalStateException("Could not find tools.jar");
+        }
+
+        isolatedToolsLoader = new DefaultClassLoaderFactory().createIsolatedClassLoader(new DefaultClassPath(toolsJar));
+    }
+
+    public JavaCompiler getSystemJavaCompiler() {
+        Class<?> compilerImplClass;
+        try {
+            compilerImplClass = isolatedToolsLoader.loadClass(DEFAULT_COMPILER_IMPL_NAME);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("Could not load class '" + DEFAULT_COMPILER_IMPL_NAME + "' from " + Jvm.current().getToolsJar());
+        }
+
+        return DirectInstantiator.instantiate(compilerImplClass.asSubclass(JavaCompiler.class));
+    }
+
+}
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 bbca272..18d68bd 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
@@ -116,7 +116,7 @@ public class Jvm implements JavaInfo {
         if (userSupplied) {
             return "User-supplied java: " + javaBase;
         }
-        return String.format("%s (%s %s)", SystemProperties.getJavaVersion(), System.getProperty("java.vm.vendor"), System.getProperty("java.vm.version"));
+        return String.format("%s (%s %s)", SystemProperties.getInstance().getJavaVersion(), System.getProperty("java.vm.vendor"), System.getProperty("java.vm.version"));
     }
 
     private File findExecutable(String command) {
@@ -151,6 +151,9 @@ public class Jvm implements JavaInfo {
         return findExecutable("java");
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public File getJavacExecutable() throws JavaHomeException {
         return findExecutable("javac");
     }
@@ -221,7 +224,7 @@ public class Jvm implements JavaInfo {
         if (os.isWindows()) {
             File jreDir;
             if (javaVersion.isJava5()) {
-                jreDir = new File(javaHome.getParentFile(), String.format("jre%s", SystemProperties.getJavaVersion()));
+                jreDir = new File(javaHome.getParentFile(), String.format("jre%s", SystemProperties.getInstance().getJavaVersion()));
             } else {
                 jreDir = new File(javaHome.getParentFile(), String.format("jre%s", javaVersion.getMajorVersion()));
             }
@@ -260,7 +263,7 @@ public class Jvm implements JavaInfo {
         }
 
         if (os.isWindows()) {
-            String version = SystemProperties.getJavaVersion();
+            String version = SystemProperties.getInstance().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");
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/jvm/UnsupportedJavaRuntimeException.java b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/UnsupportedJavaRuntimeException.java
new file mode 100644
index 0000000..a222d53
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/jvm/UnsupportedJavaRuntimeException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.GradleException;
+import org.gradle.api.JavaVersion;
+import org.gradle.util.GradleVersion;
+
+public class UnsupportedJavaRuntimeException extends GradleException {
+    public UnsupportedJavaRuntimeException(String message) {
+        super(message);
+    }
+
+    public UnsupportedJavaRuntimeException(JavaVersion javaVersion) {
+        super(String.format("Gradle %s requires Java 6 or later to run. Your build is currently configured to use Java %s.", GradleVersion.current().getVersion(), javaVersion.getMajorVersion()));
+    }
+
+    public static UnsupportedJavaRuntimeException usingUnsupportedVersion(String component, JavaVersion minVersion) {
+        return new UnsupportedJavaRuntimeException(String.format("%s %s requires Java %s or later to run. You are currently using Java %s.", component, GradleVersion.current().getVersion(),
+                minVersion.getMajorVersion(), JavaVersion.current().getMajorVersion()));
+    }
+
+    public static UnsupportedJavaRuntimeException configuredWithUnsupportedVersion(String component, JavaVersion minVersion, JavaVersion configuredVersion) {
+        return new UnsupportedJavaRuntimeException(String.format("%s %s requires Java %s or later to run. Your build is currently configured to use Java %s.", component, GradleVersion.current().getVersion(),
+                minVersion.getMajorVersion(), configuredVersion.getMajorVersion()));
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperation.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperation.java
new file mode 100644
index 0000000..81ab699
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperation.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+public interface BuildOperation {
+    /**
+     * Returns a human consumable name for this operation.
+     */
+    String getDescription();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationFailure.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationFailure.java
new file mode 100644
index 0000000..2517866
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationFailure.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+import org.gradle.api.GradleException;
+
+public class BuildOperationFailure extends GradleException {
+    private final BuildOperation operation;
+
+    protected BuildOperationFailure(BuildOperation operation) {
+        super();
+        this.operation = operation;
+    }
+
+    protected BuildOperationFailure(BuildOperation operation, String message) {
+        super(message);
+        this.operation = operation;
+    }
+
+    protected BuildOperationFailure(BuildOperation operation, String message, Throwable cause) {
+        super(message, cause);
+        this.operation = operation;
+    }
+
+    public BuildOperation getOperation() {
+        return operation;
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationProcessor.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationProcessor.java
new file mode 100644
index 0000000..8bbb49e
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationProcessor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+
+import org.gradle.api.Nullable;
+
+/**
+ * A processor for executing build operations.
+ */
+public interface BuildOperationProcessor {
+    /**
+     * Creates a new queue for holding operations to be executed.
+     *
+     * @param worker The action to be executed for each operation.
+     * @param <T> The type of operations the worker uses.
+     * @return A queue to add operations to and wait for their completion.
+     */
+    <T extends BuildOperation> BuildOperationQueue<T> newQueue(BuildOperationWorker<T> worker, @Nullable String logLocation);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationQueue.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationQueue.java
new file mode 100644
index 0000000..e1b45c2
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationQueue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+/**
+ * An individual active, single use, queue of build operations.
+ * <p>
+ * The queue is active in that operations are potentially executed as soon as they are added.
+ * The queue is single use in that no further work can be added once {@link #waitForCompletion()} is called.
+ * <p>
+ * A queue instance is not threadsafe and MUST only be used from a single thread.
+ *
+ * @param <T> type of build operations to hold
+ */
+public interface BuildOperationQueue<T extends BuildOperation> {
+
+    /**
+     * Adds an operation to be executed, potentially executing it instantly.
+     *
+     * @param operation operation to execute
+     */
+    public void add(T operation);
+
+    /**
+     * Waits for all previously added operations to complete.
+     * <p>
+     * On failure, some effort is made to cancel any operations that have not started.
+     *
+     * @throws MultipleBuildOperationFailures if <em>any</em> operation failed
+     */
+    public void waitForCompletion() throws MultipleBuildOperationFailures;
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationWorker.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationWorker.java
new file mode 100644
index 0000000..e251803
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationWorker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+import org.gradle.api.Action;
+
+public interface BuildOperationWorker<T extends BuildOperation> extends Action<T> {
+    /**
+     * Returns a human consumable name for this tool.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationProcessor.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationProcessor.java
new file mode 100644
index 0000000..0e2510e
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationProcessor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.concurrent.StoppableExecutor;
+
+public class DefaultBuildOperationProcessor implements BuildOperationProcessor, Stoppable {
+
+    private final StoppableExecutor fixedSizePool;
+
+    public DefaultBuildOperationProcessor(ExecutorFactory executorFactory, int maxWorkerCount) {
+        this.fixedSizePool = executorFactory.create("build operations", maxWorkerCount);
+    }
+
+    public <T extends BuildOperation> BuildOperationQueue<T> newQueue(BuildOperationWorker<T> worker, @Nullable String logLocation) {
+        return new DefaultBuildOperationQueue<T>(fixedSizePool, worker, logLocation);
+    }
+
+    public void stop() {
+        fixedSizePool.stop();
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationQueue.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationQueue.java
new file mode 100644
index 0000000..da165ab
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/DefaultBuildOperationQueue.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Queues;
+import com.google.common.util.concurrent.*;
+import org.gradle.internal.UncheckedException;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+
+class DefaultBuildOperationQueue<T extends BuildOperation> implements BuildOperationQueue<T> {
+    private final ListeningExecutorService executor;
+    private final BuildOperationWorker<T> worker;
+
+    private final List<ListenableFuture> operations;
+    private final String logLocation;
+
+    private boolean waitingForCompletion;
+
+    DefaultBuildOperationQueue(ExecutorService executor, BuildOperationWorker<T> worker, String logLocation) {
+        this.logLocation = logLocation;
+        this.executor = MoreExecutors.listeningDecorator(executor);
+        this.worker = worker;
+        this.operations = Lists.newLinkedList();
+    }
+
+    public void add(final T operation) {
+        if (waitingForCompletion) {
+            throw new IllegalStateException("BuildOperationQueue cannot be reused once it has started completion.");
+        }
+        ListenableFuture<?> future = executor.submit(new OperationHolder(operation));
+        operations.add(future);
+    }
+
+    public void waitForCompletion() throws MultipleBuildOperationFailures {
+        waitingForCompletion = true;
+
+        CountDownLatch finished = new CountDownLatch(operations.size());
+        Queue<Throwable> failures = Queues.newConcurrentLinkedQueue();
+
+        for (ListenableFuture operation : operations) {
+            Futures.addCallback(operation, new CompletionCallback(finished, failures));
+        }
+
+        try {
+            finished.await();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+
+        // all operations are complete, check for errors
+        if (!failures.isEmpty()) {
+            throw new MultipleBuildOperationFailures(getFailureMessage(failures), failures, logLocation);
+        }
+    }
+
+    private String getFailureMessage(Collection<Throwable> failures) {
+        if (failures.size() == 1) {
+            return "A build operation failed.";
+        }
+        return "Multiple build operations failed.";
+    }
+
+    private static class CompletionCallback implements FutureCallback {
+        private final CountDownLatch finished;
+        private final Collection<Throwable> failures;
+
+        private CompletionCallback(CountDownLatch finished, Collection<Throwable> failures) {
+            this.finished = finished;
+            this.failures = failures;
+        }
+
+        public void onSuccess(Object result) {
+            finished.countDown();
+        }
+
+        public void onFailure(Throwable t) {
+            failures.add(t);
+            finished.countDown();
+        }
+    }
+
+    private class OperationHolder implements Runnable {
+        private final T operation;
+
+        OperationHolder(T operation) {
+            this.operation = operation;
+        }
+
+        public void run() {
+            worker.execute(operation);
+        }
+
+        public String toString() {
+            return "Worker ".concat(worker.getDisplayName()).concat(" for operation ").concat(operation.getDescription());
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/MultipleBuildOperationFailures.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/MultipleBuildOperationFailures.java
new file mode 100644
index 0000000..d380cae
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/MultipleBuildOperationFailures.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
+
+public class MultipleBuildOperationFailures extends DefaultMultiCauseException {
+    private static final int MAX_CAUSES = 10;
+
+    public MultipleBuildOperationFailures(String message, Iterable<? extends Throwable> causes, @Nullable String logLocation) {
+        super(format(message, causes, logLocation), causes);
+    }
+
+    private static String format(String message, Iterable<? extends Throwable> causes, String logLocation) {
+        StringBuilder sb = new StringBuilder(message);
+        int count = 0;
+        for (Throwable cause : causes) {
+            if (count++ < MAX_CAUSES) {
+                sb.append(String.format("%n    %s", cause.getMessage()));
+            }
+        }
+
+        int suppressedFailureCount = count - MAX_CAUSES;
+        if (suppressedFailureCount == 1) {
+            sb.append(String.format("%n    ...and %d more failure.", suppressedFailureCount));
+        } else if (suppressedFailureCount > 1) {
+            sb.append(String.format("%n    ...and %d more failures.", suppressedFailureCount));
+        }
+
+        if (logLocation != null) {
+            sb.append(String.format("%nSee the complete log at: ")).append(logLocation);
+        }
+        return sb.toString();
+    }
+}
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 7fcafaf..83d4fca 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
@@ -33,12 +33,16 @@ public abstract class OperatingSystem {
     public static final Unix UNIX = new Unix();
 
     public static OperatingSystem current() {
-        String osName = System.getProperty("os.name").toLowerCase();
+        return forName(System.getProperty("os.name"));
+    }
+
+    public static OperatingSystem forName(String os) {
+        String osName = os.toLowerCase();
         if (osName.contains("windows")) {
             return WINDOWS;
-        } else if (osName.contains("mac os x") || osName.contains("darwin")) {
+        } else if (osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx")) {
             return MAC_OS;
-        } else if (osName.contains("sunos")) {
+        } else if (osName.contains("sunos") || osName.contains("solaris")) {
             return SOLARIS;
         } else if (osName.contains("linux")) {
             return LINUX;
@@ -127,7 +131,7 @@ public abstract class OperatingSystem {
         return all;
     }
     
-    List<File> getPath() {
+    public List<File> getPath() {
         String path = System.getenv(getPathVar());
         if (path == null) {
             return Collections.emptyList();
@@ -289,7 +293,6 @@ public abstract class OperatingSystem {
         @Override
         public boolean isMacOsX() {
             return true;
-
         }
 
         @Override
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassDetails.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassDetails.java
new file mode 100644
index 0000000..c51d3dc
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassDetails.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public interface ClassDetails {
+    /**
+     * Returns the non-private properties of this class. Includes inherited properties
+     */
+    Set<String> getPropertyNames();
+
+    /**
+     * Returns the non-private properties of this class. Includes inherited properties
+     */
+    Collection<? extends PropertyDetails> getProperties();
+
+    /**
+     * Returns the details of a non-private property of this class.
+     */
+    PropertyDetails getProperty(String name) throws NoSuchPropertyException;
+
+    /**
+     * Returns all methods of this class, including all inherited, private and static methods.
+     */
+    List<Method> getAllMethods();
+
+    /**
+     * Returns the non-private instance methods of this class that are not property getter or setter methods.
+     * Includes inherited methods.
+     */
+    List<Method> getInstanceMethods();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassInspector.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassInspector.java
new file mode 100644
index 0000000..6af4cf7
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ClassInspector.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+public class ClassInspector {
+    /**
+     * Extracts a view of the given class.
+     */
+    public static ClassDetails inspect(Class<?> type) {
+        DefaultClassDetails classDetails = new DefaultClassDetails(type);
+        visitGraph(type, classDetails);
+        return classDetails;
+    }
+
+    private static void visitGraph(Class<?> type, DefaultClassDetails classDetails) {
+        Set<Class<?>> seen = new HashSet<Class<?>>();
+        List<Class<?>> queue = new ArrayList<Class<?>>();
+        queue.add(type);
+        while (!queue.isEmpty()) {
+            Class<?> current = queue.remove(0);
+            if (!seen.add(current)) {
+                continue;
+            }
+            inspectClass(current, classDetails);
+            for (Class<?> c : current.getInterfaces()) {
+                queue.add(0, c);
+            }
+            if (current.getSuperclass() != null && current.getSuperclass() != Object.class) {
+                queue.add(0, current.getSuperclass());
+            }
+        }
+    }
+
+    private static void inspectClass(Class<?> type, DefaultClassDetails classDetails) {
+        for (Method method : type.getDeclaredMethods()) {
+            classDetails.method(method);
+
+            if (Modifier.isPrivate(method.getModifiers()) || Modifier.isStatic(method.getModifiers()) || method.isBridge()) {
+                continue;
+            }
+
+            Class<?>[] parameterTypes = method.getParameterTypes();
+            String methodName = method.getName();
+            if (methodName.startsWith("get")
+                    && methodName.length() > 3
+                    && !method.getReturnType().equals(Void.TYPE)
+                    && parameterTypes.length == 0) {
+                String propertyName = propertyName(methodName, 3);
+                classDetails.property(propertyName).addGetter(method);
+            } else if (methodName.startsWith("is")
+                    && methodName.length() > 2
+                    && (method.getReturnType().equals(Boolean.class) || method.getReturnType().equals(Boolean.TYPE))
+                    && parameterTypes.length == 0) {
+                String propertyName = propertyName(methodName, 2);
+                classDetails.property(propertyName).addGetter(method);
+            } else if (methodName.startsWith("set")
+                    && methodName.length() > 3
+                    && parameterTypes.length == 1) {
+                String propertyName = propertyName(methodName, 3);
+                classDetails.property(propertyName).addSetter(method);
+            } else {
+                classDetails.instanceMethod(method);
+            }
+        }
+    }
+
+    private static String propertyName(String methodName, int beginIndex) {
+        String propertyName = methodName.substring(beginIndex);
+        if (Character.isUpperCase(propertyName.charAt(0)) && propertyName.length() > 1 && Character.isUpperCase(propertyName.charAt(1))) {
+            // First 2 chars are upper-case -> leave name unmodified
+            return propertyName;
+        }
+        return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+    }
+
+    private static class MethodSignature {
+        final String name;
+        final Class<?>[] params;
+
+        private MethodSignature(String name, Class<?>[] params) {
+            this.name = name;
+            this.params = params;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            MethodSignature other = (MethodSignature) obj;
+            return other.name.equals(name) && Arrays.equals(params, other.params);
+        }
+
+        @Override
+        public int hashCode() {
+            return name.hashCode() ^ Arrays.hashCode(params);
+        }
+    }
+
+    private static class MethodSet implements Iterable<Method> {
+        private final Set<MethodSignature> signatures = new HashSet<MethodSignature>();
+        private final List<Method> methods = new ArrayList<Method>();
+
+        /**
+         * @return true if the method was added, false if not
+         */
+        public boolean add(Method method) {
+            MethodSignature methodSignature = new MethodSignature(method.getName(), method.getParameterTypes());
+            if (signatures.add(methodSignature)) {
+                methods.add(method);
+                return true;
+            }
+            return false;
+        }
+
+        public Iterator<Method> iterator() {
+            return methods.iterator();
+        }
+
+        public List<Method> getValues() {
+            return methods;
+        }
+
+        public boolean isEmpty() {
+            return methods.isEmpty();
+        }
+    }
+
+    private static class DefaultClassDetails implements ClassDetails {
+        private final Class<?> type;
+        private final MethodSet instanceMethods = new MethodSet();
+        private final Map<String, DefaultPropertyDetails> properties = new TreeMap<String, DefaultPropertyDetails>();
+        private final List<Method> methods = new ArrayList<Method>();
+
+        private DefaultClassDetails(Class<?> type) {
+            this.type = type;
+        }
+
+        @Override
+        public List<Method> getAllMethods() {
+            return methods;
+        }
+
+        @Override
+        public List<Method> getInstanceMethods() {
+            return instanceMethods.getValues();
+        }
+
+        @Override
+        public Set<String> getPropertyNames() {
+            return properties.keySet();
+        }
+
+        @Override
+        public Collection<? extends PropertyDetails> getProperties() {
+            return properties.values();
+        }
+
+        @Override
+        public PropertyDetails getProperty(String name) throws NoSuchPropertyException {
+            DefaultPropertyDetails property = properties.get(name);
+            if (property == null) {
+                throw new NoSuchPropertyException(String.format("No property '%s' found on %s.", name, type));
+            }
+            return property;
+        }
+
+        public void method(Method method) {
+            methods.add(method);
+        }
+
+        public void instanceMethod(Method method) {
+            instanceMethods.add(method);
+        }
+
+        public DefaultPropertyDetails property(String propertyName) {
+            DefaultPropertyDetails property = properties.get(propertyName);
+            if (property == null) {
+                property = new DefaultPropertyDetails(propertyName);
+                properties.put(propertyName, property);
+            }
+            return property;
+        }
+    }
+
+    private static class DefaultPropertyDetails implements PropertyDetails {
+        private final String name;
+        private final MethodSet getters = new MethodSet();
+        private final MethodSet setters = new MethodSet();
+
+        private DefaultPropertyDetails(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public List<Method> getGetters() {
+            return getters.getValues();
+        }
+
+        @Override
+        public List<Method> getSetters() {
+            return setters.getValues();
+        }
+
+        public void addGetter(Method method) {
+            getters.add(method);
+        }
+
+        public void addSetter(Method method) {
+            setters.add(method);
+        }
+    }
+}
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 cd7f87f..100515d 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,6 +22,16 @@ import java.util.Arrays;
 import java.util.List;
 
 public class DirectInstantiator implements Instantiator {
+
+    public static final Instantiator INSTANCE = new DirectInstantiator();
+
+    public static <T> T instantiate(Class<? extends T> type, Object... params) {
+        return INSTANCE.newInstance(type, params);
+    }
+
+    private DirectInstantiator() {
+    }
+
     public <T> T newInstance(Class<? extends T> type, Object... params) {
         try {
             List<Constructor<?>> matches = new ArrayList<Constructor<?>>();
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaMethod.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaMethod.java
index 6f40a89..f37b4e9 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaMethod.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaMethod.java
@@ -28,21 +28,25 @@ 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) {
+    public JavaMethod(Class<T> target, Class<R> returnType, String name, boolean allowStatic, Class<?>... paramTypes) {
         this.returnType = returnType;
-        method = findMethod(target, target, name, paramTypes);
+        method = findMethod(target, target, name, allowStatic, paramTypes);
         method.setAccessible(true);
     }
 
+    public JavaMethod(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) {
+        this(target, returnType, name, false, paramTypes);
+    }
+
     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) {
+    private Method findMethod(Class origTarget, Class target, String name, boolean allowStatic, Class<?>[] paramTypes) {
         for (Method method : target.getDeclaredMethods()) {
-            if (Modifier.isStatic(method.getModifiers())) {
+            if (!allowStatic && Modifier.isStatic(method.getModifiers())) {
                 continue;
             }
             if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), paramTypes)) {
@@ -54,10 +58,18 @@ public class JavaMethod<T, R> {
         if (parent == null) {
             throw new NoSuchMethodException(String.format("Could not find method %s(%s) on %s.", name, Joiner.on(", ").join(paramTypes), origTarget.getSimpleName()));
         } else {
-            return findMethod(origTarget, parent, name, paramTypes);
+            return findMethod(origTarget, parent, name, allowStatic, paramTypes);
         }
     }
 
+    public boolean isStatic() {
+        return Modifier.isStatic(method.getModifiers());
+    }
+
+    public R invokeStatic(Object... args) {
+        return invoke(null, args);
+    }
+
     public R invoke(T target, Object... args) {
         try {
             Object result = method.invoke(target, args);
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 a922a8a..dc41efd 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
@@ -18,11 +18,13 @@ package org.gradle.internal.reflect;
 
 import org.gradle.api.Transformer;
 import org.gradle.api.specs.Spec;
+import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.util.CollectionUtils;
 
 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;
@@ -32,17 +34,17 @@ public class JavaReflectionUtil {
     /**
      * Locates the readable properties of the given type. Searches only public properties.
      */
-    public static Map<String, PropertyAccessor> readableProperties(Class<?> target) {
+    public static <T> Map<String, PropertyAccessor> readableProperties(Class<T> 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));
+                properties.put(propertyName, new GetterMethodBackedPropertyAccessor<T, Object>(propertyName, Object.class, 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));
+                properties.put(propertyName, new GetterMethodBackedPropertyAccessor<T, Object>(propertyName, Object.class, method));
             }
         }
         return properties;
@@ -53,12 +55,50 @@ public class JavaReflectionUtil {
      *
      * @throws NoSuchPropertyException when the given property does not exist.
      */
-    public static PropertyAccessor readableProperty(Class<?> target, String property) throws NoSuchPropertyException {
+    public static <T, F> PropertyAccessor<T, F> readableProperty(Class<T> target, Class<F> returnType, String property) throws NoSuchPropertyException {
         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);
+        return new GetterMethodBackedPropertyAccessor<T, F>(property, returnType, getterMethod);
+    }
+
+    /**
+     * 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 <T, F> PropertyAccessor<T, F> readableProperty(T target, Class<F> returnType, String property) throws NoSuchPropertyException {
+        @SuppressWarnings("unchecked")
+        Class<T> targetClass = (Class<T>) target.getClass();
+        return readableProperty(targetClass, returnType, property);
+    }
+
+    /**
+     * Locates the field with the given name as a readable property.  Searches only public fields.
+     *
+     * @throws NoSuchPropertyException
+     */
+    public static <T, F> PropertyAccessor<T, F> readableField(Class<T> target, Class<F> fieldType, String fieldName) throws NoSuchPropertyException {
+        Field field;
+        try {
+            field = target.getField(fieldName);
+        } catch (java.lang.NoSuchFieldException e) {
+            throw new NoSuchPropertyException(String.format("Could not find field '%s' on class %s.", fieldName, target.getSimpleName()));
+        }
+
+        return new FieldBackedPropertyAccessor<T, F>(fieldName, fieldType, field);
+    }
+
+    /**
+     * Locates the field with the given name as a readable property.  Searches only public fields.
+     *
+     * @throws NoSuchPropertyException
+     */
+    public static <T, F> PropertyAccessor<T, F> readableField(T target, Class<F> fieldType, String fieldName) throws NoSuchPropertyException {
+        @SuppressWarnings("unchecked")
+        Class<T> targetClass = (Class<T>) target.getClass();
+        return readableField(targetClass, fieldType, fieldName);
     }
 
     private static Method findGetterMethod(Class<?> target, String property) {
@@ -117,7 +157,9 @@ public class JavaReflectionUtil {
     }
 
     public static Class<?> getWrapperTypeForPrimitiveType(Class<?> type) {
-        if (type == Boolean.TYPE) {
+        if (type == Character.TYPE) {
+            return Character.class;
+        } else if (type == Boolean.TYPE) {
             return Boolean.class;
         } else if (type == Long.TYPE) {
             return Long.class;
@@ -132,7 +174,7 @@ public class JavaReflectionUtil {
         } else if (type == Double.TYPE) {
             return Double.class;
         }
-        throw new IllegalArgumentException(String.format("Don't know how wrapper type for primitive type %s.", type));
+        throw new IllegalArgumentException(String.format("Don't know the wrapper type for primitive type %s.", type));
     }
 
     /**
@@ -143,6 +185,13 @@ public class JavaReflectionUtil {
     }
 
     /**
+     * Locates the given static method. Searches all methods, including private methods.
+     */
+    public static <T, R> JavaMethod<T, R> staticMethod(Class<T> target, Class<R> returnType, String name, Class<?>... paramTypes) throws NoSuchMethodException {
+        return new JavaMethod<T, R>(target, returnType, name, true, paramTypes);
+    }
+
+    /**
      * Locates the given method. Searches all methods, including private methods.
      */
     public static <T, R> JavaMethod<T, R> method(T target, Class<R> returnType, String name, Class<?>... paramTypes) throws NoSuchMethodException {
@@ -185,6 +234,10 @@ public class JavaReflectionUtil {
         return methods.isEmpty() ? null : methods.get(0);
     }
 
+    public static List<Method> findAllMethods(Class<?> target, Spec<Method> predicate) {
+        return findAllMethodsInternal(target, predicate, new MultiMap<String, Method>(), new ArrayList<Method>(), false);
+    }
+
     // Not hasProperty() because that's awkward with Groovy objects implementing it
     public static boolean propertyExists(Object target, String propertyName) {
         Class<?> targetType = target.getClass();
@@ -274,22 +327,19 @@ public class JavaReflectionUtil {
         }
     }
 
-    public static boolean isClassAvailable(String className) {
-        try {
-            JavaReflectionUtil.class.getClassLoader().loadClass(className);
-            return true;
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
+    public static <T> Factory<T> factory(final Instantiator instantiator, final Class<? extends T> type, final Object... args) {
+        return new InstantiatingFactory<T>(instantiator, type, args);
     }
 
-    private static class GetterMethodBackedPropertyAccessor implements PropertyAccessor {
+    private static class GetterMethodBackedPropertyAccessor<T, F> implements PropertyAccessor<T, F> {
         private final String property;
         private final Method method;
+        private final Class<F> returnType;
 
-        public GetterMethodBackedPropertyAccessor(String property, Method method) {
+        public GetterMethodBackedPropertyAccessor(String property, Class<F> returnType, Method method) {
             this.property = property;
             this.method = method;
+            this.returnType = returnType;
         }
 
         @Override
@@ -301,13 +351,13 @@ public class JavaReflectionUtil {
             return property;
         }
 
-        public Class<?> getType() {
-            return method.getClass();
+        public Class<F> getType() {
+            return returnType;
         }
 
-        public Object getValue(Object target) {
+        public F getValue(T target) {
             try {
-                return method.invoke(target);
+                return returnType.cast(method.invoke(target));
             } catch (InvocationTargetException e) {
                 throw UncheckedException.unwrapAndRethrow(e);
             } catch (Exception e) {
@@ -316,6 +366,37 @@ public class JavaReflectionUtil {
         }
     }
 
+    private static class FieldBackedPropertyAccessor<T, F> implements PropertyAccessor<T, F> {
+        private final String property;
+        private final Field field;
+        private final Class<F> fieldType;
+
+        public FieldBackedPropertyAccessor(String property, Class<F> fieldType, Field field) {
+            this.property = property;
+            this.field = field;
+            this.fieldType = fieldType;
+        }
+
+        @Override
+        public String getName() {
+            return property;
+        }
+
+        @Override
+        public Class<F> getType() {
+            return fieldType;
+        }
+
+        @Override
+        public F getValue(T target) {
+            try {
+                return fieldType.cast(field.get(target));
+            } catch (IllegalAccessException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+    }
+
     private static class MethodBackedPropertyMutator implements PropertyMutator {
         private final String property;
         private final Method method;
@@ -348,4 +429,20 @@ public class JavaReflectionUtil {
             }
         }
     }
+
+    private static class InstantiatingFactory<T> implements Factory<T> {
+        private final Instantiator instantiator;
+        private final Class<? extends T> type;
+        private final Object[] args;
+
+        public InstantiatingFactory(Instantiator instantiator, Class<? extends T> type, Object... args) {
+            this.instantiator = instantiator;
+            this.type = type;
+            this.args = args;
+        }
+
+        public T create() {
+            return instantiator.newInstance(type, args);
+        }
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodDescription.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodDescription.java
new file mode 100644
index 0000000..da7a190
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodDescription.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import org.gradle.internal.Cast;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+public class MethodDescription {
+
+    private String owner;
+    private String name;
+    private String returnType;
+    private Iterable<String> parameterTypes;
+
+    public MethodDescription(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (returnType != null) {
+            sb.append(returnType).append(" ");
+        }
+        if (owner != null) {
+            sb.append(owner).append("#");
+        }
+        sb.append(name);
+
+        if (parameterTypes != null) {
+            sb.append("(");
+            Joiner.on(", ").appendTo(sb, parameterTypes);
+            sb.append(")");
+        }
+
+        return sb.toString();
+    }
+
+    public static MethodDescription name(String name) {
+        return new MethodDescription(name);
+    }
+
+    public static MethodDescription of(Method method) {
+        return name(method.getName())
+                .owner(method.getDeclaringClass())
+                .returns(method.getGenericReturnType())
+                .takes(method.getGenericParameterTypes());
+    }
+
+    public static MethodDescription of(Constructor<?> constructor) {
+        return name("<init>")
+                .owner(constructor.getDeclaringClass())
+                .takes(constructor.getGenericParameterTypes());
+    }
+
+    private String typeName(Type type) {
+        if (type == null) {
+            return null;
+        }
+        return type instanceof Class ? Cast.cast(Class.class, type).getName() : type.toString();
+    }
+
+    public MethodDescription returns(Type returnType) {
+        this.returnType = typeName(returnType);
+        return this;
+    }
+
+    public MethodDescription owner(Class<?> owner) {
+        this.owner = typeName(owner);
+        return this;
+    }
+
+    public MethodDescription takes(Type[] parameterTypes) {
+        this.parameterTypes = Iterables.transform(Arrays.asList(parameterTypes), new Function<Type, String>() {
+            @Override
+            public String apply(Type input) {
+                return typeName(input);
+            }
+        });
+        return this;
+    }
+
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodSignatureEquivalence.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodSignatureEquivalence.java
new file mode 100644
index 0000000..4515077
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/MethodSignatureEquivalence.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import com.google.common.base.Equivalence;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import java.lang.reflect.Method;
+
+public class MethodSignatureEquivalence extends Equivalence<Method> {
+
+    @Override
+    protected boolean doEquivalent(Method a, Method b) {
+        boolean equals = new EqualsBuilder()
+                .append(a.getName(), b.getName())
+                .append(a.getGenericParameterTypes(), b.getGenericParameterTypes())
+                .isEquals();
+        if (equals) {
+            equals = a.getReturnType().equals(b.getReturnType())
+                    || a.getReturnType().isAssignableFrom(b.getReturnType())
+                    || b.getReturnType().isAssignableFrom(a.getReturnType());
+        }
+        return equals;
+    }
+
+    @Override
+    protected int doHash(Method method) {
+        return new HashCodeBuilder()
+                .append(method.getName())
+                .append(method.getGenericParameterTypes())
+                .toHashCode();
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ObjectInstantiationException.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ObjectInstantiationException.java
index 801321e..d06e9a8 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ObjectInstantiationException.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/ObjectInstantiationException.java
@@ -16,6 +16,9 @@
 
 package org.gradle.internal.reflect;
 
+/**
+ * Thrown when an object cannot be instantiated.
+ */
 public class ObjectInstantiationException extends RuntimeException {
     public ObjectInstantiationException(Class<?> targetType, Throwable throwable) {
         super(String.format("Could not create an instance of type %s.", targetType.getName()), throwable);
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
index 3d02e6c..5efc51f 100644
--- 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
@@ -16,10 +16,10 @@
 
 package org.gradle.internal.reflect;
 
-public interface PropertyAccessor {
+public interface PropertyAccessor<T, F> {
     String getName();
 
-    Class<?> getType();
+    Class<F> getType();
 
-    Object getValue(Object target);
+    F getValue(T target);
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyDetails.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyDetails.java
new file mode 100644
index 0000000..4f09c7d
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/PropertyDetails.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+public interface PropertyDetails {
+    String getName();
+
+    List<Method> getGetters();
+
+    List<Method> getSetters();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/service/DefaultServiceRegistry.java b/subprojects/base-services/src/main/java/org/gradle/internal/service/DefaultServiceRegistry.java
index 5cc1ad0..8ffab1f 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/service/DefaultServiceRegistry.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/service/DefaultServiceRegistry.java
@@ -22,9 +22,13 @@ import org.gradle.internal.Factory;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.util.BiFunction;
 
+import java.io.Closeable;
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * A hierarchical {@link ServiceRegistry} implementation.
@@ -48,15 +52,25 @@ import java.util.*;
  *
  * <p>Service instances are created on demand. {@link #getFactory(Class)} looks for a service instance which implements {@code Factory<T>} where {@code T} is the expected type.</p>
  *
+ * <p>Service instances and factories are closed when the registry that created them is closed using {@link #close()}. If a service instance or factory implements
+ * {@link java.io.Closeable} or {@link org.gradle.internal.concurrent.Stoppable} then the appropriate close() or stop() method is called. Instances are closed in
+ * reverse dependency order.</p>
+ *
  * <p>Service registries are arranged in a hierarchy. If a service of a given type cannot be located, the registry uses its parent registry, if any, to locate the service.</p>
  */
-public class DefaultServiceRegistry implements ServiceRegistry {
+public class DefaultServiceRegistry implements ServiceRegistry, Closeable {
+
+    private static final ConcurrentMap<Class<?>, RelevantMethods> METHODS_CACHE = new ConcurrentHashMap<Class<?>, RelevantMethods>();
+    private static final ConcurrentMap<Type, BiFunction<ServiceProvider, LookupContext, Provider>> SERVICE_TYPE_PROVIDER_CACHE = new ConcurrentHashMap<Type, BiFunction<ServiceProvider, LookupContext, Provider>>();
+    private final Map<Type, ServiceProvider> providerCache = new HashMap<Type, ServiceProvider>();
+
     private final Object lock = new Object();
     private final CompositeProvider allServices = new CompositeProvider();
     private final OwnServices ownServices;
     private final CompositeProvider parentServices;
     private final String displayName;
     private boolean closed;
+    private boolean mutable = true; // access under lock
 
     public DefaultServiceRegistry() {
         this(null, Collections.<ServiceRegistry>emptyList());
@@ -105,81 +119,152 @@ public class DefaultServiceRegistry implements ServiceRegistry {
         return displayName;
     }
 
+    static class RelevantMethods {
+        final List<Method> decorators;
+        final List<Method> factories;
+        final List<Method> configurers;
+
+        public RelevantMethods(List<Method> decorators, List<Method> factories, List<Method> configurers) {
+            this.decorators = decorators;
+            this.factories = factories;
+            this.configurers = configurers;
+        }
+    }
+
+    static class RelevantMethodsBuilder {
+        final List<Method> remainingMethods;
+        final Class<?> type;
+        final LinkedList<Method> decorators = new LinkedList<Method>();
+        final LinkedList<Method> factories = new LinkedList<Method>();
+        final LinkedList<Method> configurers = new LinkedList<Method>();
+        final Set<String> seen = new HashSet<String>();
+
+        public RelevantMethodsBuilder(Class<?> type) {
+            this.type = type;
+            this.remainingMethods = new LinkedList<Method>();
+
+            for (Class<?> clazz = type; clazz != Object.class && clazz != DefaultServiceRegistry.class; clazz = clazz.getSuperclass()) {
+                remainingMethods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
+            }
+        }
+
+        void add(Iterator<Method> iterator, List<Method> builder, Method method) {
+            if (seen.add(method.getName())) {
+                builder.add(method);
+            }
+            iterator.remove();
+        }
+
+        RelevantMethods build() {
+            return new RelevantMethods(decorators, factories, configurers);
+        }
+    }
+
+
     private void findProviderMethods(Object target) {
-        Set<String> methods = new HashSet<String>();
-        for (Class<?> type = target.getClass(); type != Object.class; type = type.getSuperclass()) {
-            findDecoratorMethods(target, type, methods, ownServices);
-            findFactoryMethods(target, type, methods, ownServices);
+        Class<?> type = target.getClass();
+        RelevantMethods methods = getMethods(type);
+        for (Method method : methods.decorators) {
+            if (parentServices == null) {
+                throw new ServiceLookupException(String.format("Cannot use decorator method %s.%s() when no parent registry is provided.", type.getSimpleName(), method.getName()));
+            }
+            ownServices.add(new DecoratorMethodService(target, method));
         }
-        findConfigureMethod(target);
+        for (Method method : methods.factories) {
+            ownServices.add(new FactoryMethodService(target, method));
+        }
+        for (Method method : methods.configurers) {
+            applyConfigureMethod(method, target);
+        }
+    }
+
+    private RelevantMethods getMethods(Class<?> type) {
+        RelevantMethods relevantMethods = METHODS_CACHE.get(type);
+        if (relevantMethods == null) {
+            relevantMethods = buildRelevantMethods(type);
+            METHODS_CACHE.putIfAbsent(type, relevantMethods);
+        }
+
+        return relevantMethods;
+    }
+
+    private RelevantMethods buildRelevantMethods(Class<?> type) {
+        RelevantMethods relevantMethods;
+        RelevantMethodsBuilder builder = new RelevantMethodsBuilder(type);
+        addDecoratorMethods(builder);
+        addFactoryMethods(builder);
+        addConfigureMethods(builder);
+        relevantMethods = builder.build();
+        return relevantMethods;
     }
 
-    private void findConfigureMethod(Object target) {
-        for (Class<?> type = target.getClass(); type != Object.class; type = type.getSuperclass()) {
-            for (Method method : type.getDeclaredMethods()) {
-                if (!method.getName().equals("configure")) {
-                    continue;
+    private void applyConfigureMethod(Method method, Object target) {
+        Object[] params = new Object[method.getGenericParameterTypes().length];
+        DefaultLookupContext context = new DefaultLookupContext();
+        for (int i = 0; i < method.getGenericParameterTypes().length; i++) {
+            Type paramType = method.getGenericParameterTypes()[i];
+            if (paramType.equals(ServiceRegistration.class)) {
+                params[i] = newRegistration();
+            } else {
+                ServiceProvider paramProvider = context.find(paramType, allServices);
+                if (paramProvider == null) {
+                    throw new ServiceLookupException(String.format("Cannot configure services using %s.%s() as required service of type %s is not available.",
+                            method.getDeclaringClass().getSimpleName(),
+                            method.getName(),
+                            format(paramType)));
                 }
+                params[i] = paramProvider.get();
+            }
+        }
+        try {
+            invoke(method, target, params);
+        } catch (Exception e) {
+            throw new ServiceLookupException(String.format("Could not configure services using %s.%s().",
+                    method.getDeclaringClass().getSimpleName(),
+                    method.getName()), e);
+        }
+    }
+
+    private static void addConfigureMethods(RelevantMethodsBuilder builder) {
+        Class<?> type = builder.type;
+        Iterator<Method> iterator = builder.remainingMethods.iterator();
+        while (iterator.hasNext()) {
+            Method method = iterator.next();
+            if (method.getName().equals("configure")) {
                 if (!method.getReturnType().equals(Void.TYPE)) {
                     throw new ServiceLookupException(String.format("Method %s.%s() must return void.", type.getSimpleName(), method.getName()));
                 }
-                Object[] params = new Object[method.getGenericParameterTypes().length];
-                DefaultLookupContext context = new DefaultLookupContext();
-                for (int i = 0; i < method.getGenericParameterTypes().length; i++) {
-                    Type paramType = method.getGenericParameterTypes()[i];
-                    if (paramType.equals(ServiceRegistration.class)) {
-                        params[i] = newRegistration();
-                    } else {
-                        ServiceProvider paramProvider = context.find(paramType, allServices);
-                        if (paramProvider == null) {
-                            throw new ServiceLookupException(String.format("Cannot configure services using %s.%s() as required service of type %s is not available.",
-                                    method.getDeclaringClass().getSimpleName(),
-                                    method.getName(),
-                                    format(paramType)));
-                        }
-                        params[i] = paramProvider.get();
-                    }
-                }
-                try {
-                    invoke(method, target, params);
-                } catch (Exception e) {
-                    throw new ServiceLookupException(String.format("Could not configure services using %s.%s().",
-                            method.getDeclaringClass().getSimpleName(),
-                            method.getName()), e);
-                }
-                return;
+                builder.add(iterator, builder.configurers, method);
             }
         }
     }
 
-    private void findFactoryMethods(Object target, Class<?> type, Set<String> factoryMethods, OwnServices ownServices) {
-        for (Method method : type.getDeclaredMethods()) {
-            if (method.getName().startsWith("create")
-                    && !Modifier.isStatic(method.getModifiers())) {
+    private static void addFactoryMethods(RelevantMethodsBuilder builder) {
+        Class<?> type = builder.type;
+        Iterator<Method> iterator = builder.remainingMethods.iterator();
+        while (iterator.hasNext()) {
+            Method method = iterator.next();
+            if (method.getName().startsWith("create") && !Modifier.isStatic(method.getModifiers())) {
                 if (method.getReturnType().equals(Void.TYPE)) {
                     throw new ServiceLookupException(String.format("Method %s.%s() must not return void.", type.getSimpleName(), method.getName()));
                 }
-                if (factoryMethods.add(method.getName())) {
-                    ownServices.add(new FactoryMethodService(target, method));
-                }
+
+                builder.add(iterator, builder.factories, method);
             }
         }
     }
 
-    private void findDecoratorMethods(Object target, Class<?> type, Set<String> decoratorMethods, OwnServices ownServices) {
-        for (Method method : type.getDeclaredMethods()) {
-            if (method.getName().startsWith("create")
-                    && method.getParameterTypes().length == 1
-                    && method.getParameterTypes()[0].equals(method.getReturnType())) {
-                if (parentServices == null) {
-                    throw new ServiceLookupException(String.format("Cannot use decorator method %s.%s() when no parent registry is provided.", type.getSimpleName(), method.getName()));
-                }
+    private static void addDecoratorMethods(RelevantMethodsBuilder builder) {
+        Class<?> type = builder.type;
+        Iterator<Method> iterator = builder.remainingMethods.iterator();
+        while (iterator.hasNext()) {
+            Method method = iterator.next();
+            if (method.getName().startsWith("create") && method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(method.getReturnType())) {
                 if (method.getReturnType().equals(Void.TYPE)) {
                     throw new ServiceLookupException(String.format("Method %s.%s() must not return void.", type.getSimpleName(), method.getName()));
                 }
-                if (decoratorMethods.add(method.getName())) {
-                    ownServices.add(new DecoratorMethodService(target, method));
-                }
+
+                builder.add(iterator, builder.decorators, method);
             }
         }
     }
@@ -188,11 +273,18 @@ public class DefaultServiceRegistry implements ServiceRegistry {
      * Adds services to this container using the given action.
      */
     public void register(Action<? super ServiceRegistration> action) {
+        assertMutable();
         action.execute(newRegistration());
     }
 
+    private void assertMutable() {
+        if (!mutable) {
+            throw new IllegalStateException("Cannot add provide to service registry " + this + " as it is no longer mutable");
+        }
+    }
+
     private ServiceRegistration newRegistration() {
-        return new ServiceRegistration(){
+        return new ServiceRegistration() {
             public <T> void add(Class<T> serviceType, T serviceInstance) {
                 DefaultServiceRegistry.this.add(serviceType, serviceInstance);
             }
@@ -211,6 +303,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
      * Adds a service to this registry. The given object is closed when this registry is closed.
      */
     public <T> DefaultServiceRegistry add(Class<T> serviceType, final T serviceInstance) {
+        assertMutable();
         ownServices.add(new FixedInstanceService<T>(serviceType, serviceInstance));
         return this;
     }
@@ -219,6 +312,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
      * Adds a service provider bean to this registry. This provider may define factory and decorator methods.
      */
     public DefaultServiceRegistry addProvider(Object provider) {
+        assertMutable();
         findProviderMethods(provider);
         return this;
     }
@@ -265,6 +359,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
 
     public <T> List<T> getAll(Class<T> serviceType) throws ServiceLookupException {
         synchronized (lock) {
+            mutable = false;
             if (closed) {
                 throw new IllegalStateException(String.format("Cannot locate service of type %s, as %s has been closed.", format(serviceType), displayName));
             }
@@ -283,20 +378,28 @@ public class DefaultServiceRegistry implements ServiceRegistry {
         return doGet(serviceType);
     }
 
+
     private Object doGet(Type serviceType) throws IllegalArgumentException {
         synchronized (lock) {
+            mutable = false;
             if (closed) {
                 throw new IllegalStateException(String.format("Cannot locate service of type %s, as %s has been closed.", format(serviceType), displayName));
             }
-
-            DefaultLookupContext context = new DefaultLookupContext();
-            ServiceProvider provider = context.find(serviceType, allServices);
-            if (provider != null) {
-                return provider.get();
+            ServiceProvider provider = providerCache.get(serviceType);
+            if (provider == null) {
+                provider = getServiceProvider(serviceType);
+                providerCache.put(serviceType, provider);
             }
+            return provider.get();
+        }
+    }
 
+    private ServiceProvider getServiceProvider(Type serviceType) {
+        ServiceProvider provider = new DefaultLookupContext().find(serviceType, allServices);
+        if (provider == null) {
             throw new UnknownServiceException(serviceType, String.format("No service of type %s available in %s.", format(serviceType), displayName));
         }
+        return provider;
     }
 
     public <T> Factory<T> getFactory(Class<T> type) {
@@ -580,7 +683,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                             format(serviceType),
                             getFactory().getDeclaringClass().getSimpleName(),
                             getFactory().getName(),
-                            i+1,
+                            i + 1,
                             format(paramType)), e);
                 }
             }
@@ -906,7 +1009,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                 if (parameterizedType.getRawType() instanceof Class) {
                     return type.isAssignableFrom((Class) parameterizedType.getRawType());
                 }
-            } else if(element instanceof Class) {
+            } else if (element instanceof Class) {
                 Class<?> other = (Class<?>) element;
                 return type.isAssignableFrom(other);
             }
@@ -947,6 +1050,7 @@ public class DefaultServiceRegistry implements ServiceRegistry {
         }
     }
 
+
     private static class DefaultLookupContext implements LookupContext {
         private final Set<Type> visiting = new HashSet<Type>();
 
@@ -955,36 +1059,70 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                 throw new ServiceValidationException(String.format("Cycle in dependencies of service of type %s.", format(serviceType)));
             }
             try {
-                if (serviceType instanceof ParameterizedType) {
-                    ParameterizedType parameterizedType = (ParameterizedType) serviceType;
-                    if (parameterizedType.getRawType().equals(Factory.class)) {
-                        Type typeArg = parameterizedType.getActualTypeArguments()[0];
-                        if (typeArg instanceof Class) {
-                            return provider.getFactory(this, (Class) typeArg);
-                        }
-                        if (typeArg instanceof WildcardType) {
-                            WildcardType wildcardType = (WildcardType) typeArg;
-                            if (wildcardType.getLowerBounds().length == 1 && wildcardType.getUpperBounds().length == 1) {
-                                if (wildcardType.getLowerBounds()[0] instanceof Class && wildcardType.getUpperBounds()[0].equals(Object.class)) {
-                                    return provider.getFactory(this, (Class<Object>) wildcardType.getLowerBounds()[0]);
-                                }
+                return getServiceProvider(serviceType, provider);
+            } finally {
+                visiting.remove(serviceType);
+            }
+        }
+
+        public ServiceProvider getServiceProvider(Type serviceType, Provider provider) {
+            BiFunction<ServiceProvider, LookupContext, Provider> function = SERVICE_TYPE_PROVIDER_CACHE.get(serviceType);
+            if (function == null) {
+                function = createServiceProviderFactory(serviceType);
+                SERVICE_TYPE_PROVIDER_CACHE.putIfAbsent(serviceType, function);
+            }
+            return function.apply(this, provider);
+        }
+
+        private static BiFunction<ServiceProvider, LookupContext, Provider> createServiceProviderFactory(final Type serviceType) {
+            if (serviceType instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType) serviceType;
+                if (parameterizedType.getRawType().equals(Factory.class)) {
+                    final Type typeArg = parameterizedType.getActualTypeArguments()[0];
+                    if (typeArg instanceof Class) {
+                        return new BiFunction<ServiceProvider, LookupContext, Provider>() {
+                            @Override
+                            public ServiceProvider apply(LookupContext lookupContext, Provider provider) {
+                                return provider.getFactory(lookupContext, (Class) typeArg);
                             }
-                            if (wildcardType.getLowerBounds().length == 0 && wildcardType.getUpperBounds().length == 1) {
-                                if (wildcardType.getUpperBounds()[0] instanceof Class) {
-                                    return provider.getFactory(this, (Class<Object>) wildcardType.getUpperBounds()[0]);
-                                }
+                        };
+                    }
+                    if (typeArg instanceof WildcardType) {
+                        final WildcardType wildcardType = (WildcardType) typeArg;
+                        if (wildcardType.getLowerBounds().length == 1 && wildcardType.getUpperBounds().length == 1) {
+                            if (wildcardType.getLowerBounds()[0] instanceof Class && wildcardType.getUpperBounds()[0].equals(Object.class)) {
+                                return new BiFunction<ServiceProvider, LookupContext, Provider>() {
+                                    @Override
+                                    public ServiceProvider apply(LookupContext lookupContext, Provider provider) {
+                                        return provider.getFactory(lookupContext, (Class<?>) wildcardType.getLowerBounds()[0]);
+                                    }
+                                };
+                            }
+                        }
+                        if (wildcardType.getLowerBounds().length == 0 && wildcardType.getUpperBounds().length == 1) {
+                            if (wildcardType.getUpperBounds()[0] instanceof Class) {
+                                return new BiFunction<ServiceProvider, LookupContext, Provider>() {
+                                    @Override
+                                    public ServiceProvider apply(LookupContext lookupContext, Provider provider) {
+                                        return provider.getFactory(lookupContext, (Class<?>) wildcardType.getUpperBounds()[0]);
+                                    }
+                                };
                             }
                         }
                     }
                 }
-
-                return provider.getService(this, toSpec(serviceType));
-            } finally {
-                visiting.remove(serviceType);
             }
+
+            final TypeSpec serviceTypeSpec = toSpec(serviceType);
+            return new BiFunction<ServiceProvider, LookupContext, Provider>() {
+                @Override
+                public ServiceProvider apply(LookupContext lookupContext, Provider provider) {
+                    return provider.getService(lookupContext, serviceTypeSpec);
+                }
+            };
         }
 
-        TypeSpec toSpec(Type serviceType) {
+        static TypeSpec toSpec(Type serviceType) {
             if (serviceType instanceof ParameterizedType) {
                 ParameterizedType parameterizedType = (ParameterizedType) serviceType;
                 List<TypeSpec> paramSpecs = new ArrayList<TypeSpec>();
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 e52fbcb..7c63713 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
@@ -17,7 +17,6 @@ package org.gradle.internal.service;
 
 import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.reflect.ObjectInstantiationException;
 
 import java.io.BufferedReader;
@@ -157,9 +156,8 @@ public class ServiceLocator {
         }
 
         public T newInstance(Object... params) {
-            Instantiator instantiator = new DirectInstantiator();
             try {
-                return instantiator.newInstance(implementationClass, params);
+                return DirectInstantiator.instantiate(implementationClass, params);
             } catch (ObjectInstantiationException t) {
                 throw new RuntimeException(String.format("Could not create an implementation of service '%s'.", serviceType.getName()), t);
             }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceRegistry.java b/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceRegistry.java
index b0bd768..ea55354 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceRegistry.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/service/ServiceRegistry.java
@@ -75,14 +75,4 @@ public interface ServiceRegistry {
      * @throws ServiceLookupException On failure to lookup the specified service factory.
      */
     <T> T newInstance(Class<T> type) throws UnknownServiceException, ServiceLookupException;
-
-    /**
-     * Closes all services for this registry.
-     *
-     * For each service, if the service has a public void close() or stop() method, that method is called to close the service.
-     */
-    void close();
-
-    boolean isClosed();
-
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/util/BiFunction.java b/subprojects/base-services/src/main/java/org/gradle/internal/util/BiFunction.java
new file mode 100644
index 0000000..6f10ab9
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/util/BiFunction.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util;
+
+public interface BiFunction<O, A, B> {
+
+    O apply(A a, B b);
+
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/util/NumberUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/util/NumberUtil.java
new file mode 100644
index 0000000..6098831
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/util/NumberUtil.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util;
+
+import static java.lang.String.format;
+
+/**
+ * Utility methods for working with numbers
+ */
+public class NumberUtil {
+
+    /**
+     * Percentage (0-...) of given input.
+     *
+     * @param fraction the fraction of total, must be >= 0. if 0, the result will be 100.
+     * @param total the total, must be >= 0, if 0, the result will be 0.
+     */
+    public static int percentOf(long fraction, long total) {
+        if (total < 0 || fraction < 0) {
+            throw new IllegalArgumentException("Unable to calculate percentage: " + fraction + " of " + total
+                    + ". All inputs must be >= 0");
+        }
+        if (total == 0) {
+            return 0;
+        }
+        float out = fraction * 100.0f / total;
+        return (int) out;
+    }
+
+    /**
+     * Formats bytes, e.g. 1000 -> 1kB, -2500 -> -2.5 kB
+     */
+    public static String formatBytes(long bytes) {
+        if (bytes < 0) {
+            return "-".concat(formatBytes(-bytes));
+        }
+        int unit = 1000;
+        if (bytes < unit) {
+            return bytes + " B";
+        }
+        int exp = (int) (Math.log(bytes) / Math.log(unit));
+        char pre = "kMGTPE".charAt(exp - 1);
+        return format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+    }
+
+    /**
+     * gets ordinal String representation of given value (e.g. 1 -> 1st, 12 -> 12th, 22 -> 22nd, etc.)
+     */
+    public static String ordinal(int value) {
+        String[] sufixes = new String[]{"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"};
+        switch (value % 100) {
+            case 11:
+            case 12:
+            case 13:
+                return value + "th";
+            default:
+                return value + sufixes[value % 10];
+        }
+    }
+}
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 e990673..0a55df0 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
@@ -15,9 +15,18 @@
  */
 package org.gradle.util;
 
+import com.google.common.base.Equivalence;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import org.gradle.api.Action;
+import org.gradle.api.Nullable;
 import org.gradle.api.Transformer;
 import org.gradle.api.specs.Spec;
+import org.gradle.internal.Factory;
+import org.gradle.internal.Pair;
 import org.gradle.internal.Transformers;
 
 import java.lang.reflect.Array;
@@ -63,6 +72,11 @@ public abstract class CollectionUtils {
         return filter(list, new LinkedList<T>(), filter);
     }
 
+    public static <T> List<T> filter(T[] array, Spec<? super T> filter) {
+        return filter(Arrays.asList(array), new LinkedList<T>(), filter);
+    }
+
+
     /**
      * Returns a sorted copy of the provided collection of things. Uses the provided comparator to sort.
      */
@@ -186,7 +200,7 @@ public abstract class CollectionUtils {
                 Object[] thingArray = (Object[]) thing;
                 List<T> list = new ArrayList<T>(thingArray.length);
                 for (Object thingThing : thingArray) {
-                    list.addAll(flattenCollections((Class) type, thingThing));
+                    list.addAll(flattenCollections(type, thingThing));
                 }
                 return list;
             }
@@ -195,7 +209,7 @@ public abstract class CollectionUtils {
                 Collection<?> collection = (Collection<?>) thing;
                 List<T> list = new ArrayList<T>();
                 for (Object element : collection) {
-                    list.addAll(flattenCollections((Class) type, element));
+                    list.addAll(flattenCollections(type, element));
                 }
                 return list;
             }
@@ -204,26 +218,24 @@ public abstract class CollectionUtils {
         } else {
             List<T> list = new ArrayList<T>();
             for (Object thing : things) {
-                list.addAll(flattenCollections((Class) type, thing));
+                list.addAll(flattenCollections(type, thing));
             }
             return list;
         }
     }
 
     public static <T> List<T> toList(Iterable<? extends T> things) {
-        if (things == null) {
-            return new ArrayList<T>(0);
-        }
         if (things instanceof List) {
             @SuppressWarnings("unchecked") List<T> castThings = (List<T>) things;
             return castThings;
         }
-        if (things instanceof Collection) {
-            return new ArrayList<T>((Collection) things);
-        }
-        List<T> list = new ArrayList<T>();
-        for (T thing : things) {
-            list.add(thing);
+        return toMutableList(things);
+    }
+
+    public static <T> List<T> toList(Enumeration<? extends T> things) {
+        AbstractList<T> list = new ArrayList<T>();
+        while (things.hasMoreElements()) {
+            list.add(things.nextElement());
         }
         return list;
     }
@@ -261,9 +273,7 @@ public abstract class CollectionUtils {
         }
 
         List<T> list = new ArrayList<T>(things.length);
-        for (T thing : things) {
-            list.add(thing);
-        }
+        Collections.addAll(list, things);
         return list;
     }
 
@@ -310,7 +320,7 @@ public abstract class CollectionUtils {
         return collect(source, destination, Transformers.asString());
     }
 
-    public static List<String> stringize(List<?> source) {
+    public static List<String> stringize(Collection<?> source) {
         return stringize(source, new ArrayList<String>(source.size()));
     }
 
@@ -373,9 +383,7 @@ public abstract class CollectionUtils {
      * @return t1
      */
     public static <T> Collection<T> addAll(Collection<T> t1, T... t2) {
-        for (T t : t2) {
-            t1.add(t);
-        }
+        Collections.addAll(t1, t2);
         return t1;
     }
 
@@ -386,13 +394,8 @@ public abstract class CollectionUtils {
      * @see CollectionUtils#diffSetsBy(java.util.Set, java.util.Set, org.gradle.api.Transformer)
      */
     public static class SetDiff<T> {
-        public static class Pair<T> {
-            public T left;
-            public T right;
-        }
-
         public Set<T> leftOnly = new HashSet<T>();
-        public Set<Pair<T>> common = new HashSet<Pair<T>>();
+        public Set<Pair<T, T>> common = new HashSet<Pair<T, T>>();
         public Set<T> rightOnly = new HashSet<T>();
     }
 
@@ -427,9 +430,7 @@ public abstract class CollectionUtils {
             if (rightValue == null) {
                 setDiff.leftOnly.add(leftEntry.getValue());
             } else {
-                SetDiff.Pair<T> pair = new SetDiff.Pair<T>();
-                pair.left = leftEntry.getValue();
-                pair.right = rightValue;
+                Pair<T, T> pair = Pair.of(leftEntry.getValue(), rightValue);
                 setDiff.common.add(pair);
             }
         }
@@ -485,14 +486,14 @@ public abstract class CollectionUtils {
             throw new NullPointerException("The 'objects' cannot be null");
         }
 
-        boolean first = true;
         StringBuilder string = new StringBuilder();
-        for (Object object : objects) {
-            if (!first) {
+        Iterator<?> iterator = objects.iterator();
+        if (iterator.hasNext()) {
+            string.append(iterator.next().toString());
+            while (iterator.hasNext()) {
                 string.append(separator);
+                string.append(iterator.next().toString());
             }
-            string.append(object.toString());
-            first = false;
         }
         return string.toString();
     }
@@ -532,4 +533,57 @@ public abstract class CollectionUtils {
         return target;
     }
 
+    public static <K, V> ImmutableListMultimap<K, V> groupBy(Iterable<? extends V> iterable, Transformer<? extends K, V> grouper) {
+        ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder();
+
+        for (V element : iterable) {
+            K key = grouper.transform(element);
+            builder.put(key, element);
+        }
+
+        return builder.build();
+    }
+
+    public static <T> Iterable<? extends T> unpack(final Iterable<? extends Factory<? extends T>> factories) {
+        return new Iterable<T>() {
+            private final Iterator<? extends Factory<? extends T>> delegate = factories.iterator();
+
+            public Iterator<T> iterator() {
+                return new Iterator<T>() {
+                    public boolean hasNext() {
+                        return delegate.hasNext();
+                    }
+
+                    public T next() {
+                        return delegate.next().create();
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    @Nullable
+    public static <T> List<T> nonEmptyOrNull(Iterable<T> iterable) {
+        ImmutableList<T> list = ImmutableList.copyOf(iterable);
+        return list.isEmpty() ? null : list;
+    }
+
+    public static <T> List<T> dedup(Iterable<T> source, final Equivalence<T> equivalence) {
+        Iterable<Equivalence.Wrapper<T>> wrappers = Iterables.transform(source, new Function<T, Equivalence.Wrapper<T>>() {
+            public Equivalence.Wrapper<T> apply(@Nullable T input) {
+                return equivalence.wrap(input);
+            }
+        });
+        Set<Equivalence.Wrapper<T>> deduped = ImmutableSet.copyOf(wrappers);
+        return ImmutableList.copyOf(Iterables.transform(deduped, new Function<Equivalence.Wrapper<T>, T>() {
+            public T apply(Equivalence.Wrapper<T> input) {
+                return input.get();
+            }
+        }));
+    }
+
 }
\ No newline at end of file
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/GradleVersion.java b/subprojects/base-services/src/main/java/org/gradle/util/GradleVersion.java
new file mode 100644
index 0000000..b024ea7
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/util/GradleVersion.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.UncheckedIOException;
+import org.gradle.internal.UncheckedException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GradleVersion implements Comparable<GradleVersion> {
+    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;
+    private static final GradleVersion CURRENT;
+
+    public static final String RESOURCE_NAME = "/org/gradle/build-receipt.properties";
+
+    static {
+        URL resource = GradleVersion.class.getResource(RESOURCE_NAME);
+
+        InputStream inputStream = null;
+        try {
+            URLConnection connection = resource.openConnection();
+            inputStream = connection.getInputStream();
+            Properties properties = new Properties();
+            properties.load(inputStream);
+
+            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, buildNumber, commitId);
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not load version details from resource '%s'.", resource), e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        }
+    }
+
+    public static GradleVersion current() {
+        return CURRENT;
+    }
+
+    /**
+     * 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, 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()) {
+            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(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(5).equals("rc")) {
+                stageNumber = 3;
+            } else {
+                stageNumber = 1;
+            }
+            String stageString = matcher.group(6);
+            stage = new Stage(stageNumber, stageString);
+        } else {
+            stage = null;
+        }
+
+        if (matcher.group(8) != null) {
+            try {
+                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(8)).getTime();
+                }
+            } catch (ParseException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        } else {
+            snapshot = null;
+        }
+    }
+
+    private String formatBuildTime(Date buildTime) {
+        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
+        format.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return format.format(buildTime);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Gradle %s", version);
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getBuildTime() {
+        return buildTime;
+    }
+
+    public String getBuildNumber() {
+        return buildNumber;
+    }
+
+    public String getRevision() {
+        return commitId;
+    }
+
+    public boolean isSnapshot() {
+        return snapshot != null;
+    }
+
+    /**
+     * 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
+     */
+    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 GradleVersion getNextMajor() {
+        if (stage != null && stage.stage == STAGE_MILESTONE) {
+            return version(majorPart + ".0");
+        }
+        return version((majorPart + 1) + ".0");
+    }
+
+    public int compareTo(GradleVersion gradleVersion) {
+        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]);
+
+            if (part > otherPart) {
+                return 1;
+            }
+            if (otherPart > part) {
+                return -1;
+            }
+        }
+        if (majorVersionParts.length > otherMajorVersionParts.length) {
+            return 1;
+        }
+        if (majorVersionParts.length < otherMajorVersionParts.length) {
+            return -1;
+        }
+
+        if (stage != null && gradleVersion.stage != null) {
+            int diff = stage.compareTo(gradleVersion.stage);
+            if (diff != 0) {
+                return diff;
+            }
+        }
+        if (stage == null && gradleVersion.stage != null) {
+            return 1;
+        }
+        if (stage != null && gradleVersion.stage == null) {
+            return -1;
+        }
+
+        if (snapshot != null && gradleVersion.snapshot != null) {
+            return snapshot.compareTo(gradleVersion.snapshot);
+        }
+        if (snapshot == null && gradleVersion.snapshot != null) {
+            return 1;
+        }
+        if (snapshot != null && gradleVersion.snapshot == null) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || o.getClass() != getClass()) {
+            return false;
+        }
+        GradleVersion other = (GradleVersion) o;
+        return version.equals(other.version);
+    }
+
+    @Override
+    public int hashCode() {
+        return version.hashCode();
+    }
+
+    public boolean isValid() {
+        return versionPart != null;
+    }
+
+    static final class Stage implements Comparable<Stage> {
+        final int stage;
+        final int number;
+        final Character patchNo;
+
+        Stage(int stage, String number) {
+            this.stage = stage;
+            Matcher m = Pattern.compile("(\\d+)([a-z])?").matcher(number);
+            try {
+                m.matches();
+                this.number = Integer.parseInt(m.group(1));
+            } catch (Exception e) {
+                throw new RuntimeException("Invalid stage small number: " + number, e);
+            }
+
+            if (m.groupCount() == 2 && m.group(2) != null) {
+                this.patchNo = m.group(2).charAt(0);
+            } else {
+                this.patchNo = '_';
+            }
+        }
+
+        public int compareTo(Stage other) {
+            if (stage > other.stage) {
+                return 1;
+            }
+            if (stage < other.stage) {
+                return -1;
+            }
+            if (number > other.number) {
+                return 1;
+            }
+            if (number < other.number) {
+                return -1;
+            }
+            if (patchNo > other.patchNo) {
+                return 1;
+            }
+            if (patchNo < other.patchNo) {
+                return -1;
+            }
+            return 0;
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/TextUtil.java b/subprojects/base-services/src/main/java/org/gradle/util/TextUtil.java
new file mode 100644
index 0000000..233344a
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/util/TextUtil.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.util;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.gradle.internal.SystemProperties;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+public class TextUtil {
+    private static final Pattern WHITESPACE = Pattern.compile("\\s*");
+
+    /**
+     * Returns the line separator for Windows.
+     */
+    public static String getWindowsLineSeparator() {
+        return "\r\n";
+    }
+
+    /**
+     * Returns the line separator for Unix.
+     */
+    public static String getUnixLineSeparator() {
+        return "\n";
+    }
+
+    /**
+     * Returns the line separator for this platform.
+     */
+    public static String getPlatformLineSeparator() {
+        return SystemProperties.getInstance().getLineSeparator();
+    }
+
+    /**
+     * Converts all line separators in the specified string to the specified line separator.
+     */
+    public static String convertLineSeparators(String str, String sep) {
+        return str == null ? null : str.replaceAll("\r\n|\r|\n", sep);
+    }
+
+    /**
+     * Converts all line separators in the specified string to the platform's line separator.
+     */
+    public static String toPlatformLineSeparators(String str) {
+        return str == null ? null : convertLineSeparators(str, getPlatformLineSeparator());
+    }
+
+    /**
+     * 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) {
+        return path.replaceAll(Pattern.quote(File.separator), "/");
+    }
+
+    /**
+     * Escapes the toString() representation of {@code obj} for use in a literal string.
+     * This is useful for interpolating variables into script strings, as well as in other situations.
+     */
+    public static String escapeString(Object obj) {
+        return obj == null ? null : StringEscapeUtils.escapeJava(obj.toString());
+    }
+
+    /**
+     * Tells whether the specified string contains any whitespace characters.
+     */
+    public static boolean containsWhitespace(String str) {
+        for (int i = 0; i < str.length(); i++) {
+            if (Character.isWhitespace(str.charAt(i))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Indents every line of {@code text} by {@code indent}. Empty lines
+     * and lines that only contain whitespace are not indented.
+     */
+    public static String indent(String text, String indent) {
+        StringBuilder builder = new StringBuilder();
+        String[] lines = text.split("\n");
+
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i];
+            if (!WHITESPACE.matcher(line).matches()) {
+                builder.append(indent);
+            }
+            builder.append(line);
+            if (i < lines.length - 1) {
+                builder.append('\n');
+            }
+        }
+
+        return builder.toString();
+    }
+
+    public static String shorterOf(String s1, String s2) {
+        if (s2.length() >= s1.length()) {
+            return s1;
+        } else {
+            return s2;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/api/JavaVersionSpec.groovy b/subprojects/base-services/src/test/groovy/org/gradle/api/JavaVersionSpec.groovy
index bd14af4..d583568 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/api/JavaVersionSpec.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/api/JavaVersionSpec.groovy
@@ -51,6 +51,15 @@ public class JavaVersionSpec extends Specification {
         JavaVersion.toVersion("1.9.0-internal") == JavaVersion.VERSION_1_9
     }
 
+    def convertClassVersionToJavaVersion() {
+        expect:
+        JavaVersion.forClassVersion(49) == JavaVersion.VERSION_1_5
+        JavaVersion.forClassVersion(50) == JavaVersion.VERSION_1_6
+        JavaVersion.forClassVersion(51) == JavaVersion.VERSION_1_7
+        JavaVersion.forClassVersion(52) == JavaVersion.VERSION_1_8
+        JavaVersion.forClassVersion(53) == JavaVersion.VERSION_1_9
+    }
+
     def failsToConvertStringToVersionForUnknownVersion() {
         expect:
         conversionFails("1");
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/PairTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/PairTest.groovy
new file mode 100644
index 0000000..efebaea
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/PairTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import static Pair.unpackLeft
+import static Pair.unpackRight
+
+class PairTest extends Specification {
+
+  def "can create and transform pair"() {
+    given:
+    def t = Pair.of(1, "a")
+
+    expect:
+    t.nestLeft(2).left == Pair.of(2, 1)
+    t.nestLeft(2).right == "a"
+    t.nestRight(2).left == 1
+    t.nestRight(2).right == Pair.of(2, "a")
+
+    t.pushLeft(2).left == 2
+    t.pushLeft(2).right == t
+    t.pushRight(2).left == t
+    t.pushRight(2).right == 2
+
+    t.mapLeft { it + 1 }.left == 2
+    t.mapLeft { it + 1 }.right == "a"
+    t.mapRight { it * 2 }.left == 1
+    t.mapRight { it * 2 }.right == "aa"
+
+    t.nestLeft(2).mapLeft(unpackLeft()).left == 2
+    t.nestLeft(2).mapLeft(unpackRight()).left == 1
+    t.nestLeft(2).mapLeft(unpackLeft()).right == "a"
+    t.nestLeft(2).mapLeft(unpackRight()).right == "a"
+    t.nestRight(2).mapRight(unpackLeft()).right == 2
+    t.nestRight(2).mapRight(unpackRight()).right == "a"
+    t.nestRight(2).mapRight(unpackLeft()).left == 1
+    t.nestRight(2).mapRight(unpackRight()).left == 1
+  }
+
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/SuppliersTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/SuppliersTest.groovy
deleted file mode 100644
index b852d03..0000000
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/SuppliersTest.groovy
+++ /dev/null
@@ -1,82 +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.internal
-
-import org.gradle.api.Transformer
-import spock.lang.Specification
-
-class SuppliersTest extends Specification {
-
-    def "closeable is closed on success"() {
-        given:
-        def closeable = Mock(Closeable)
-        def supplier = Suppliers.ofQuietlyClosed(Factories.constant(closeable))
-
-        when:
-        def result = supplier.supplyTo({ 1 } as Transformer)
-
-        then:
-        result == 1
-        1 * closeable.close()
-    }
-
-    def "closeable is closed on failure"() {
-        given:
-        def closeable = Mock(Closeable)
-        def supplier = Suppliers.ofQuietlyClosed(Factories.constant(closeable))
-
-        when:
-        supplier.supplyTo({ throw new IllegalStateException("!!") } as Transformer)
-
-        then:
-        def e = thrown IllegalStateException
-        e.message == "!!"
-        1 * closeable.close()
-    }
-
-    def "exceptions thrown during close are ignored"() {
-        given:
-        def closeable = Mock(Closeable)
-        def supplier = Suppliers.ofQuietlyClosed(Factories.constant(closeable))
-        1 * closeable.close() >> { throw new IllegalStateException("on close") }
-
-        when:
-        supplier.supplyTo({ throw new IllegalStateException("!!") } as Transformer)
-
-        then:
-        def e = thrown IllegalStateException
-        e.message == "!!"
-    }
-
-    def "suppliers can transformed"() {
-        when:
-        def original = Suppliers.of(Factories.constant(1))
-        def wrapped = Suppliers.wrap(original, new Transformer<Integer, Integer>() {
-            Integer transform(Integer integer) {
-                integer * 2
-            }
-        })
-
-        then:
-        4 == wrapped.supplyTo(new Transformer<Integer, Integer>() {
-            Integer transform(Integer integer) {
-                integer * 2
-            }
-        })
-    }
-
-}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/SystemPropertiesTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/SystemPropertiesTest.groovy
index c1a1102..df38605 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/SystemPropertiesTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/SystemPropertiesTest.groovy
@@ -21,7 +21,7 @@ import spock.lang.Specification
 class SystemPropertiesTest extends Specification {
     def "can be queried for standard system properties"() {
         expect:
-        SystemProperties.standardProperties.contains("os.name")
-        !SystemProperties.standardProperties.contains("foo.bar")
+        SystemProperties.instance.standardProperties.contains("os.name")
+        !SystemProperties.instance.standardProperties.contains("foo.bar")
     }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/TransformersTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/TransformersTest.groovy
index 4fafb05..d3349d1 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/TransformersTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/TransformersTest.groovy
@@ -21,7 +21,7 @@ import org.gradle.api.Named
 import org.gradle.api.Namer
 import spock.lang.Specification
 
-import static Transformers.*
+import static org.gradle.internal.Transformers.*
 
 class TransformersTest extends Specification {
 
@@ -88,6 +88,23 @@ class TransformersTest extends Specification {
         result == null
     }
 
+    def "factory as transformer"() {
+        def factory = Mock(Factory)
+
+        when:
+        def result = toTransformer(factory).transform("original")
+
+        then:
+        1 * factory.create() >> "transformed"
+        result == "transformed"
+    }
+
+    def constants() {
+        def foo = "foo"
+        expect:
+        constant(foo).transform(null).is(foo)
+    }
+
     Named named(String name) {
         new Named() {
             String getName() {
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
index 53991c6..441a7a7 100644
--- 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
@@ -75,4 +75,16 @@ class CachingClassLoaderTest extends Specification {
         1 * visitor.visitParent(parent)
         0 * visitor._
     }
+
+    def "equals and hashcode"() {
+        def c1 = new URLClassLoader()
+        def c2 = new URLClassLoader()
+
+        expect:
+        new CachingClassLoader(c1) == new CachingClassLoader(c1)
+        new CachingClassLoader(c1).hashCode() == new CachingClassLoader(c1).hashCode()
+
+        new CachingClassLoader(c1) != new CachingClassLoader(c2)
+        new CachingClassLoader(c1).hashCode() != new CachingClassLoader(c2).hashCode()
+    }
 }
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
index 92fa31b..ff0d152 100644
--- 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
@@ -169,6 +169,25 @@ class FilteringClassLoaderTest extends Specification {
         canSeeResource('org/gradle/util/ClassLoaderTest.txt')
     }
 
+    void "can disallow packages"() {
+        given:
+        classLoader.disallowPackage("org.junit")
+
+        expect:
+        cannotLoadClass(Test)
+        cannotSeePackage("org.junit")
+        cannotSeePackage("org.junit.subpackage")
+    }
+
+    void "disallow wins over allow packages"() {
+        given:
+        classLoader.disallowPackage("org.junit")
+        classLoader.allowPackage("org.junit")
+
+        expect:
+        cannotLoadClass(Test)
+    }
+
     void "visits self and parent"() {
         def visitor = Mock(ClassLoaderVisitor)
         given:
@@ -258,4 +277,19 @@ class FilteringClassLoaderTest extends Specification {
         and:
         0 * parent._
     }
+
+    void "spec is copied correctly"() {
+        given:
+        def parent = Mock(ClassLoader, useObjenesis: false)
+        def spec = new FilteringClassLoader.Spec([ 'allow.ClassName' ], [ 'allowPackage' ], [ 'allowPackagePrefix' ], [ 'allowPackageResource' ], [ 'allowResource' ], [ 'disallow.ClassName' ], [ 'disallowPackage' ])
+        def filteringClassLoader = new FilteringClassLoader(parent, spec)
+        def visitor = Mock(ClassLoaderVisitor)
+
+        when:
+        filteringClassLoader.visit(visitor)
+
+        then:
+        1 * visitor.visitSpec(spec)
+        1 * visitor.visitParent(parent)
+    }
 }
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
index 0faa891..210b992 100644
--- 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
@@ -120,4 +120,15 @@ class MultiParentClassLoaderTest extends Specification {
         1 * visitor.visitParent(parent2)
         0 * visitor._
     }
+
+    def "equals and hashcode"() {
+        def c1 = new URLClassLoader()
+        def c2 = new CachingClassLoader(c1)
+        expect:
+        new MultiParentClassLoader(c1, c2) == new MultiParentClassLoader(c1, c2)
+        new MultiParentClassLoader(c1, c2).hashCode() == new MultiParentClassLoader(c1, c2).hashCode()
+
+        new MultiParentClassLoader(c1, c2) != new MultiParentClassLoader(c1, c2, new URLClassLoader())
+        new MultiParentClassLoader(c1, c2).hashCode() != new MultiParentClassLoader(c1, c2, new URLClassLoader()).hashCode()
+    }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classpath/DefaultClassPathTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classpath/DefaultClassPathTest.groovy
index 5d44518..2e90bd6 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/classpath/DefaultClassPathTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classpath/DefaultClassPathTest.groovy
@@ -19,6 +19,17 @@ import spock.lang.Specification
 import org.gradle.util.Matchers
 
 class DefaultClassPathTest extends Specification {
+    def "removes duplicates when constructed"() {
+        def file1 = new File("a.jar")
+        def file2 = new File("b.jar")
+        def cp1 = new DefaultClassPath(file1, file2, file1, file2)
+        def cp2 = new DefaultClassPath([file1, file1, file2, file1])
+
+        expect:
+        cp1.asFiles == [file1, file2]
+        cp2.asFiles == [file1, file2]
+    }
+
     def "can add classpaths together"() {
         def file1 = new File("a.jar")
         def file2 = new File("b.jar")
@@ -30,6 +41,18 @@ class DefaultClassPathTest extends Specification {
         cp3.asFiles == [file1, file2]
     }
 
+    def "removes duplicates when added together"() {
+        def file1 = new File("a.jar")
+        def file2 = new File("b.jar")
+        def file3 = new File("c.jar")
+        def cp1 = new DefaultClassPath(file1, file2)
+        def cp2 = new DefaultClassPath(file3, file2, file1)
+
+        expect:
+        def cp3 = cp1 + cp2
+        cp3.asFiles == [file1, file2, file3]
+    }
+
     def "add returns lhs when rhs is empty"() {
         def cp1 = new DefaultClassPath(new File("a.jar"))
         def cp2 = new DefaultClassPath()
@@ -48,7 +71,7 @@ class DefaultClassPathTest extends Specification {
 
     def "can add collection of files to classpath"() {
         def file1 = new File("a.jar")
-        def file2 = new File("a.jar")
+        def file2 = new File("b.jar")
         def cp = new DefaultClassPath(file1)
 
         expect:
@@ -62,6 +85,7 @@ class DefaultClassPathTest extends Specification {
         def file3 = new File("c.jar")
         def cp = new DefaultClassPath(file1, file2)
         def same = new DefaultClassPath(file1, file2)
+        def sameWithDuplicates = new DefaultClassPath(file1, file2, file2, file1)
         def differentOrder = new DefaultClassPath(file2, file1)
         def missing = new DefaultClassPath(file2)
         def extra = new DefaultClassPath(file1, file2, file3)
@@ -69,6 +93,7 @@ class DefaultClassPathTest extends Specification {
 
         expect:
         cp Matchers.strictlyEqual(same)
+        cp Matchers.strictlyEqual(sameWithDuplicates)
         cp != differentOrder
         cp != missing
         cp != extra
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/CompositeStoppableTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/CompositeStoppableTest.groovy
index 54e2b5a..f8da7e8 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/CompositeStoppableTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/CompositeStoppableTest.groovy
@@ -85,30 +85,6 @@ class CompositeStoppableTest extends Specification {
         1 * b.stop()
     }
 
-    def callsACloseMethodOnArbitraryObject() {
-        ClassWithCloseMethod a = Mock()
-
-        stoppable.add(a)
-
-        when:
-        stoppable.stop()
-
-        then:
-        1 * a.close()
-    }
-
-    def callsAStopMethodOnArbitraryObject() {
-        ClassWithStopMethod a = Mock()
-
-        stoppable.add(a)
-
-        when:
-        stoppable.stop()
-
-        then:
-        1 * a.stop()
-    }
-
     def doesNothingWhenArbitraryObjectDoesNotHaveStopOrCloseMethod() {
         Runnable r = Mock()
 
@@ -132,14 +108,4 @@ class CompositeStoppableTest extends Specification {
         then:
         1 * a.stop()
     }
-
-    static class ClassWithCloseMethod {
-        void close() {
-        }
-    }
-
-    static class ClassWithStopMethod {
-        void stop() {
-        }
-    }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/DefaultExecutorFactoryTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/DefaultExecutorFactoryTest.groovy
index 0482418..a45265b 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/DefaultExecutorFactoryTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/concurrent/DefaultExecutorFactoryTest.groovy
@@ -120,7 +120,6 @@ class DefaultExecutorFactoryTest extends ConcurrentSpec {
 
         def failure2 = new RuntimeException()
         def runnable2 = {
-            thread.blockUntil.broken1
             instant.broken2
             throw failure2
         }
@@ -128,6 +127,7 @@ class DefaultExecutorFactoryTest extends ConcurrentSpec {
         when:
         def executor = factory.create('test')
         executor.execute(runnable1)
+        thread.blockUntil.broken1
         executor.execute(runnable2)
         thread.blockUntil.broken2
 
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/DefaultMultiCauseExceptionTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/DefaultMultiCauseExceptionTest.groovy
new file mode 100644
index 0000000..6a7eb60
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/DefaultMultiCauseExceptionTest.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.internal.exceptions
+
+import org.gradle.util.GUtil
+import spock.lang.Specification
+
+
+class DefaultMultiCauseExceptionTest extends Specification {
+    def getCauseReturnsTheFirstCause() {
+        def cause1 = new RuntimeException()
+        def cause2 = new RuntimeException()
+        def failure = new TestMultiCauseException('message', [cause1, cause2])
+
+        expect:
+        failure.cause == cause1
+        failure.causes == [cause1, cause2]
+    }
+
+    def getCauseReturnsNullWhenThereAreNoCauses() {
+        def failure = new TestMultiCauseException('message', [])
+
+        expect:
+        failure.cause == null
+        failure.causes == []
+    }
+
+    def canUseInitCauseToProvideCause() {
+        def cause1 = new RuntimeException()
+        def failure = new TestMultiCauseException('message', [])
+        failure.initCause(cause1)
+
+        expect:
+        failure.cause == cause1
+        failure.causes == [cause1]
+    }
+
+    def canUseInitCausesToProvideMultipleCause() {
+        def cause1 = new RuntimeException()
+        def cause2 = new RuntimeException()
+        def failure = new TestMultiCauseException('message', [])
+        failure.initCauses([cause1, cause2])
+
+        expect:
+        failure.cause == cause1
+        failure.causes == [cause1, cause2]
+    }
+
+    def printStackTraceWithMultipleCauses() {
+        RuntimeException cause1 = new RuntimeException('cause1')
+        RuntimeException cause2 = new RuntimeException('cause2')
+        def failure = new TestMultiCauseException('message', [cause1, cause2])
+        def outstr = new StringWriter()
+
+        when:
+        outstr.withPrintWriter { writer ->
+            failure.printStackTrace(writer)
+        }
+
+        then:
+        outstr.toString().contains("${TestMultiCauseException.name}: message")
+        outstr.toString().contains("Cause 1: ${RuntimeException.name}: cause1")
+        outstr.toString().contains("Cause 2: ${RuntimeException.name}: cause2")
+    }
+
+    def printStackTraceWithSingleCause() {
+        RuntimeException cause1 = new RuntimeException('cause1')
+        def failure = new TestMultiCauseException('message', [cause1])
+        def outstr = new StringWriter()
+
+        when:
+        outstr.withPrintWriter { writer ->
+            failure.printStackTrace(writer)
+        }
+
+        then:
+        outstr.toString().contains("${TestMultiCauseException.name}: message")
+        outstr.toString().contains("Caused by: ${RuntimeException.name}: cause1")
+    }
+
+    def canSerializeAndDeserializeException() {
+        def cause1 = new RuntimeException("cause1")
+        def cause2 = new RuntimeException("cause2")
+        def failure = new TestMultiCauseException("message", [cause1, cause2])
+
+        when:
+        def bytes = GUtil.serialize(failure)
+        def result = new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject()
+
+        then:
+        result instanceof TestMultiCauseException
+        result.message == "message"
+        result.causes.size() == 2
+        result.causes*.message == ["cause1", "cause2"]
+
+        when:
+        def outstr = new StringWriter()
+        outstr.withPrintWriter { result.printStackTrace(it) }
+
+        then:
+        outstr.toString().contains("${TestMultiCauseException.name}: message")
+        outstr.toString().contains("Cause 1: ${RuntimeException.name}: cause1")
+        outstr.toString().contains("Cause 2: ${RuntimeException.name}: cause2")
+    }
+}
+
+class TestMultiCauseException extends DefaultMultiCauseException {
+    TestMultiCauseException(String message, Iterable<? extends Throwable> causes) {
+        super(message, causes)
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/FormattingDiagnosticsVisitorTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/FormattingDiagnosticsVisitorTest.groovy
new file mode 100644
index 0000000..d713829
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/exceptions/FormattingDiagnosticsVisitorTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.exceptions
+
+import spock.lang.Specification
+
+class FormattingDiagnosticsVisitorTest extends Specification {
+    def "formats candidates with examples"() {
+        def visitor = new FormattingDiagnosticsVisitor()
+
+        given:
+        visitor.candidate("thing 1")
+        visitor.candidate("thing 2").example("a")
+        visitor.candidate("thing 3").example("a").example("b")
+
+        expect:
+        visitor.candidates == ["thing 1", "thing 2, for example a.", "thing 3, for example a, b."]
+    }
+
+    def "merges duplicate candidates"() {
+        def visitor = new FormattingDiagnosticsVisitor()
+
+        given:
+        visitor.candidate("thing 1")
+        visitor.candidate("thing 1").example("a")
+        visitor.candidate("thing 1").example("b")
+
+        expect:
+        visitor.candidates == ["thing 1, for example a, b."]
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashUtilTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashUtilTest.groovy
new file mode 100644
index 0000000..7549391
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashUtilTest.groovy
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.hash
+
+import org.gradle.api.UncheckedIOException
+import spock.lang.Issue
+import spock.lang.Specification
+
+class HashUtilTest extends Specification {
+    String stringToHash = "a test string"
+    String md5HashString = "b1a4cf30d3f4095f0a7d2a6676bcae77"
+    String sha1HashString = "2da75da5c85478df42df0f917700241ed282f599"
+
+    def "createHash from String returns MD5 hash" () {
+        expect:
+        HashUtil.createHash(stringToHash, "MD5").asHexString() == md5HashString
+    }
+
+    def "createHash from File returns MD5 hash" () {
+        setup:
+        File file = File.createTempFile("HashUtilTest", null)
+        file << stringToHash
+
+        expect:
+        HashUtil.createHash(file, "MD5").asHexString() == md5HashString
+
+        cleanup:
+        file.delete()
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2967")
+    def "createHash from File adds filename to UncheckedIOException" () {
+        setup:
+        String filename = "/some/path/to/file"
+        File file = Spy(File, constructorArgs: [ filename ]) {
+            1 * getPath() >> { throw new UncheckedIOException(new IOException("thrown from spy class")) }
+            1 * getAbsolutePath() >> filename
+        }
+
+        when:
+        HashUtil.createHash(file, "MD5")
+
+        then:
+        UncheckedIOException e = thrown()
+        e.message.contains(filename)
+        e.message.contains("MD5")
+    }
+
+    def "createHash from InputStream returns MD5 hash" () {
+        expect:
+        HashUtil.createHash(new ByteArrayInputStream(stringToHash.bytes), "MD5").asHexString() == md5HashString
+    }
+
+    def "createHash from InputStream wraps IOException in UncheckedIOException" () {
+        setup:
+        IOException ioe = new IOException("thrown from stub class")
+        InputStream stubInputStream = Stub(InputStream) {
+            _ * read(_ as byte[]) >> { throw ioe }
+        }
+
+        when:
+        HashUtil.createHash(stubInputStream, "MD5")
+
+        then:
+        UncheckedIOException e = thrown()
+        e.cause == ioe
+    }
+
+    def "createCompactMD5 returns correct String" () {
+        expect:
+        HashUtil.createCompactMD5(stringToHash) == new BigInteger(md5HashString, 16).toString(36)
+    }
+
+    def "sha1 from byteArray returns SHA1 hash" () {
+        expect:
+        HashUtil.sha1(stringToHash.bytes).asHexString() == sha1HashString
+    }
+
+    def "sha1 from InputStream returns SHA1 hash" () {
+        expect:
+        HashUtil.sha1(new ByteArrayInputStream(stringToHash.bytes)).asHexString() == sha1HashString
+    }
+
+    def "sha1 from File returns SHA1 hash" () {
+        setup:
+        File file = File.createTempFile("HashUtilTest", null)
+        file << stringToHash
+
+        expect:
+        HashUtil.sha1(file).asHexString() == sha1HashString
+
+        cleanup:
+        file.delete()
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashValueTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashValueTest.groovy
index 2f05f02..3314be7 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashValueTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/hash/HashValueTest.groovy
@@ -41,24 +41,31 @@ class HashValueTest extends Specification {
 
         where:
         hexString                          | compactString
-        "1234"                             | "4hk"
-        "abc123"                           | "ang93"
-        "d41d8cd98f00b204e9800998ecf8427e" | "6k3m6dj3o0m82ej009j3mfggju"
-        "FFF"                              | "3vv"
+        "1234"                             | "3lg"
+        "abc123"                           | "6p99f"
+        "d41d8cd98f00b204e9800998ecf8427e" | "ck2u8j60r58fu0sgyxrigm3cu"
+        "FFF"                              | "35r"
     }
 
     def "can roundtrip compact sha1 representation"() {
         given:
         def hash = new HashValue("1234")
-        
+
         expect:
         hash.equals(new HashValue(hash.asHexString()))
     }
-    
+
     def "creates short MD5 for string input"() {
         expect:
-        HashUtil.createCompactMD5("") == "6k3m6dj3o0m82ej009j3mfggju"
-        HashUtil.createCompactMD5("a") == "co5qrjg7hmqk33gsps9kne9j1"
-        HashUtil.createCompactMD5("i") == "46bg60milgs1hubil371u1l1q1"
+        HashUtil.createCompactMD5("") == "ck2u8j60r58fu0sgyxrigm3cu"
+        HashUtil.createCompactMD5("a") == "r6p51cluyxfm1x21kf967yw1"
+        HashUtil.createCompactMD5("i") == "7ycx034q3zbhupl01mv32dx6p"
+    }
+
+    def "creates a zero padded hex string"() {
+        def md5 = new HashValue("19981ffd09dbfad8cb0d33ee3f72d6a")
+        expect:
+        md5.asHexString() == '19981ffd09dbfad8cb0d33ee3f72d6a'
+        md5.asZeroPaddedHexString(32) == '019981ffd09dbfad8cb0d33ee3f72d6a'
     }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JdkToolsTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JdkToolsTest.groovy
new file mode 100644
index 0000000..ab5609d
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/jvm/JdkToolsTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 spock.lang.IgnoreIf
+import spock.lang.Specification
+
+import javax.tools.JavaCompiler
+
+class JdkToolsTest extends Specification {
+
+    @IgnoreIf({ Jvm.current().toolsJar == null })
+    def "can get java compiler"() {
+        def compiler = JdkTools.current().systemJavaCompiler
+
+        expect:
+        compiler instanceof JavaCompiler
+        compiler.class == JdkTools.current().systemJavaCompiler.class
+    }
+
+    def "throws when no tools"() {
+        when:
+        new JdkTools(Mock(JavaInfo) {
+            getToolsJar() >> null
+        })
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "throws when tools doesn't contain compiler"() {
+        when:
+        new JdkTools(Mock(JavaInfo) {
+            getToolsJar() >> new File("/nothing")
+        }).systemJavaCompiler
+
+        then:
+        thrown IllegalStateException
+    }
+
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationProcessorTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationProcessorTest.groovy
new file mode 100644
index 0000000..4824e91
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationProcessorTest.groovy
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations
+
+import org.gradle.api.GradleException
+import org.gradle.internal.concurrent.DefaultExecutorFactory
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.util.concurrent.CountDownLatch
+
+class DefaultBuildOperationProcessorTest extends Specification {
+
+
+    public static final String LOG_LOCATION = "<log location>"
+
+    @Unroll
+    def "all #operations operations run to completion when using #maxThreads threads"() {
+        given:
+        def buildOperationProcessor = new DefaultBuildOperationProcessor(new DefaultExecutorFactory(), maxThreads)
+        def operation = Mock(DefaultBuildOperationQueueTest.TestBuildOperation)
+        def worker = new DefaultBuildOperationQueueTest.SimpleWorker()
+
+        when:
+        def queue = buildOperationProcessor.newQueue(worker, LOG_LOCATION)
+        operations.times { queue.add(operation) }
+        and:
+        queue.waitForCompletion()
+
+        then:
+        operations * operation.run()
+
+        where:
+        // Where operations < maxThreads
+        // operations = maxThreads
+        // operations >> maxThreads
+        operations | maxThreads
+        0          | 1
+        1          | 1
+        20         | 1
+        1          | 4
+        4          | 4
+        20         | 4
+    }
+
+    @Unroll
+    def "all work run to completion for multiple queues when using multiple threads #maxThreads"() {
+        given:
+        def amountOfWork = 10
+        def worker = new DefaultBuildOperationQueueTest.SimpleWorker()
+        def buildOperationProcessor = new DefaultBuildOperationProcessor(new DefaultExecutorFactory(), maxThreads)
+        def queues = [
+                buildOperationProcessor.newQueue(worker, LOG_LOCATION),
+                buildOperationProcessor.newQueue(worker, LOG_LOCATION),
+                buildOperationProcessor.newQueue(worker, LOG_LOCATION),
+                buildOperationProcessor.newQueue(worker, LOG_LOCATION),
+                buildOperationProcessor.newQueue(worker, LOG_LOCATION),
+        ]
+        def operations = [
+                Mock(DefaultBuildOperationQueueTest.TestBuildOperation),
+                Mock(DefaultBuildOperationQueueTest.TestBuildOperation),
+                Mock(DefaultBuildOperationQueueTest.TestBuildOperation),
+                Mock(DefaultBuildOperationQueueTest.TestBuildOperation),
+                Mock(DefaultBuildOperationQueueTest.TestBuildOperation),
+        ]
+
+        when:
+        queues.eachWithIndex { queue, i ->
+            amountOfWork.times {
+                queue.add(operations[i])
+            }
+        }
+
+        and:
+        queues.each { queue ->
+            queue.waitForCompletion()
+        }
+
+        then:
+        operations.each { operation ->
+            amountOfWork * operation.run()
+        }
+
+        where:
+        maxThreads | _
+        1          | _
+        4          | _
+        10         | _
+    }
+
+    def "failures in one queue do not cause failures in other queues"() {
+        given:
+        def amountOfWork = 10
+        def maxThreads = 4
+        def buildOperationProcessor = new DefaultBuildOperationProcessor(new DefaultExecutorFactory(), maxThreads)
+        def success = Stub(DefaultBuildOperationQueueTest.TestBuildOperation)
+        def failure = Stub(DefaultBuildOperationQueueTest.TestBuildOperation) {
+            run() >> { throw new Exception() }
+        }
+        def worker = new DefaultBuildOperationQueueTest.SimpleWorker()
+        def successfulQueue = buildOperationProcessor.newQueue(worker, LOG_LOCATION)
+        def failedQueue = buildOperationProcessor.newQueue(worker, LOG_LOCATION)
+
+        amountOfWork.times {
+            successfulQueue.add(success)
+            failedQueue.add(failure)
+        }
+
+        when:
+        successfulQueue.waitForCompletion()
+
+        then:
+        noExceptionThrown()
+
+        when:
+        failedQueue.waitForCompletion()
+
+        then:
+        thrown MultipleBuildOperationFailures
+    }
+
+    def "multiple failures get reported"() {
+        given:
+        def threadCount = 4
+        def buildOperationProcessor = new DefaultBuildOperationProcessor(new DefaultExecutorFactory(), threadCount)
+        def worker = new DefaultBuildOperationQueueTest.SimpleWorker()
+        def queue = buildOperationProcessor.newQueue(worker, LOG_LOCATION)
+        def startLatch = new CountDownLatch(1)
+        def operation = Stub(DefaultBuildOperationQueueTest.TestBuildOperation) {
+            run() >> {
+                startLatch.await()
+                throw new GradleException("always fails")
+            }
+        }
+        when:
+        threadCount.times { queue.add(operation) }
+        startLatch.countDown() // cause all operations to fail
+        and:
+        queue.waitForCompletion()
+
+        then:
+        def e = thrown(MultipleBuildOperationFailures)
+        e instanceof MultipleBuildOperationFailures
+        ((MultipleBuildOperationFailures) e).getCauses().size() == 4
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationQueueTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationQueueTest.groovy
new file mode 100644
index 0000000..7657149
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/DefaultBuildOperationQueueTest.groovy
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations
+
+import com.google.common.util.concurrent.ListeningExecutorService
+import com.google.common.util.concurrent.MoreExecutors
+import org.gradle.api.GradleException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.util.concurrent.Executors
+
+class DefaultBuildOperationQueueTest extends Specification {
+
+    public static final String LOG_LOCATION = "<log location>"
+    abstract static class TestBuildOperation implements BuildOperation, Runnable {
+        public String getDescription() { return toString() }
+        public String toString() { return getClass().simpleName }
+    }
+
+    static class Success extends TestBuildOperation {
+        void run() {
+            // do nothing
+        }
+    }
+
+    static class Failure extends TestBuildOperation {
+        void run() {
+            throw new BuildOperationFailure(this, "always fails")
+        }
+    }
+
+    static class SimpleWorker implements BuildOperationWorker<TestBuildOperation> {
+        public void execute(TestBuildOperation run) {
+            run.run();
+        }
+
+        String getDisplayName() {
+            return getClass().simpleName
+        }
+    }
+
+    BuildOperationQueue operationQueue
+
+    void setupQueue(int threads) {
+        ListeningExecutorService sameThreadExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(threads))
+        operationQueue = new DefaultBuildOperationQueue(sameThreadExecutor, new SimpleWorker(), LOG_LOCATION)
+    }
+
+    @Unroll
+    def "executes all #runs operations in #threads threads"() {
+        given:
+        setupQueue(threads)
+        def success = Mock(TestBuildOperation)
+
+        when:
+        runs.times { operationQueue.add(success) }
+
+        and:
+        operationQueue.waitForCompletion()
+
+        then:
+        runs * success.run()
+
+        where:
+        runs | threads
+        0    | 1
+        0    | 4
+        0    | 10
+        1    | 1
+        1    | 4
+        1    | 10
+        5    | 1
+        5    | 4
+        5    | 10
+    }
+    
+    def "cannot use operation queue once it has completed"() {
+        given:
+        setupQueue(1)
+        operationQueue.waitForCompletion()
+
+        when:
+        operationQueue.add(Mock(TestBuildOperation))
+
+        then:
+        thrown IllegalStateException
+    }
+
+    @Unroll
+    def "failures propagate to caller regardless of when it failed #operations with #threads threads"() {
+        given:
+        setupQueue(threads)
+        operations.each { operation ->
+            operationQueue.add(operation)
+        }
+        def failureCount = operations.findAll({it instanceof Failure}).size()
+
+        when:
+        operationQueue.waitForCompletion()
+
+        then:
+        // assumes we don't fail early
+        MultipleBuildOperationFailures e = thrown()
+        e.getCauses().every({ it instanceof GradleException })
+        e.getCauses().size() == failureCount
+
+        where:
+        [operations, threads] << [
+                [[new Success(), new Success(), new Failure()],
+                 [new Success(), new Failure(), new Success()],
+                 [new Failure(), new Success(), new Success()],
+                 [new Failure(), new Failure(), new Failure()],
+                 [new Failure(), new Failure(), new Success()],
+                 [new Failure(), new Success(), new Failure()],
+                 [new Success(), new Failure(), new Failure()]],
+                [1, 4, 10]].combinations()
+    }
+
+    def "all failures reported in order for a single threaded executor"() {
+        given:
+        setupQueue(1)
+        operationQueue.add(Stub(TestBuildOperation) {
+            run() >> { throw new RuntimeException("first") }
+        })
+        operationQueue.add(Stub(TestBuildOperation) {
+            run() >> { throw new RuntimeException("second") }
+        })
+        operationQueue.add(Stub(TestBuildOperation) {
+            run() >> { throw new RuntimeException("third") }
+        })
+
+        when:
+        operationQueue.waitForCompletion()
+
+        then:
+        // assumes we don't fail early
+        MultipleBuildOperationFailures e = thrown()
+        e.getCauses()*.message == [ 'first', 'second', 'third' ]
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/MultipleBuildOperationFailuresTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/MultipleBuildOperationFailuresTest.groovy
new file mode 100644
index 0000000..221dfac
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/operations/MultipleBuildOperationFailuresTest.groovy
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations
+
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+
+class MultipleBuildOperationFailuresTest extends Specification {
+
+    public static final String LOG_LOCATION = "<log location>"
+    static class TestException extends BuildOperationFailure {
+        protected TestException(int count) {
+            super(null, "<test message $count>")
+        }
+    }
+
+    def "format for a single failure"() {
+        given:
+        def failures = [ new TestException(0) ]
+        def exception = new MultipleBuildOperationFailures("<message>", failures, LOG_LOCATION)
+        when:
+        def message = exception.getMessage()
+        then:
+        TextUtil.normaliseLineSeparators(message) == """<message>
+    <test message 0>
+See the complete log at: $LOG_LOCATION"""
+    }
+
+    def "format for multiple failures at limit"() {
+        given:
+        def failures = (0..9).collect { new TestException(it) }
+        def exception = new MultipleBuildOperationFailures("<message>", failures, LOG_LOCATION)
+        when:
+        def message = exception.getMessage()
+        then:
+        TextUtil.normaliseLineSeparators(message) == """<message>
+    <test message 0>
+    <test message 1>
+    <test message 2>
+    <test message 3>
+    <test message 4>
+    <test message 5>
+    <test message 6>
+    <test message 7>
+    <test message 8>
+    <test message 9>
+See the complete log at: $LOG_LOCATION"""
+    }
+
+    def "format for multiple failures just over limit"() {
+        given:
+        def failures = (0..10).collect { new TestException(it) }
+        def exception = new MultipleBuildOperationFailures("<message>", failures, LOG_LOCATION)
+        when:
+        def message = exception.getMessage()
+        then:
+        TextUtil.normaliseLineSeparators(message) == """<message>
+    <test message 0>
+    <test message 1>
+    <test message 2>
+    <test message 3>
+    <test message 4>
+    <test message 5>
+    <test message 6>
+    <test message 7>
+    <test message 8>
+    <test message 9>
+    ...and 1 more failure.
+See the complete log at: $LOG_LOCATION"""
+    }
+
+    def "format for multiple failures beyond limit"() {
+        given:
+        def failures = (0..14).collect { new TestException(it) }
+        def exception = new MultipleBuildOperationFailures("<message>", failures, LOG_LOCATION)
+        when:
+        def message = exception.getMessage()
+        then:
+        TextUtil.normaliseLineSeparators(message) == """<message>
+    <test message 0>
+    <test message 1>
+    <test message 2>
+    <test message 3>
+    <test message 4>
+    <test message 5>
+    <test message 6>
+    <test message 7>
+    <test message 8>
+    <test message 9>
+    ...and 5 more failures.
+See the complete log at: $LOG_LOCATION"""
+    }
+
+    def "omits location when null"() {
+        given:
+        def failures = (0..14).collect { new TestException(it) }
+        def exception = new MultipleBuildOperationFailures("<message>", failures, null)
+        when:
+        def message = exception.getMessage()
+        then:
+        TextUtil.normaliseLineSeparators(message) == """<message>
+    <test message 0>
+    <test message 1>
+    <test message 2>
+    <test message 3>
+    <test message 4>
+    <test message 5>
+    <test message 6>
+    <test message 7>
+    <test message 8>
+    <test message 9>
+    ...and 5 more failures."""
+    }
+}
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 ddc6450..4df3b33 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
@@ -130,6 +130,12 @@ class OperatingSystemTest extends Specification {
 
         then:
         OperatingSystem.current() instanceof OperatingSystem.MacOs
+
+        when:
+        System.properties['os.name'] = 'osx'
+
+        then:
+        OperatingSystem.current() instanceof OperatingSystem.MacOs
     }
 
     def "Mac OS X identifies itself correctly"() {
@@ -141,10 +147,17 @@ class OperatingSystemTest extends Specification {
         os.macOsX
     }
 
-    def "uses os.name property to determine if solaris"() {
+    def "uses os.name property to determine if sunos"() {
+        when:
         System.properties['os.name'] = 'SunOS'
 
-        expect:
+        then:
+        OperatingSystem.current() instanceof OperatingSystem.Solaris
+
+        when:
+        System.properties['os.name'] = 'solaris'
+
+        then:
         OperatingSystem.current() instanceof OperatingSystem.Solaris
     }
 
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/ClassInspectorTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/ClassInspectorTest.groovy
new file mode 100644
index 0000000..5b79594
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/ClassInspectorTest.groovy
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import spock.lang.Specification
+
+class ClassInspectorTest extends Specification {
+    def "extracts properties of a Groovy class"() {
+        expect:
+        def details = ClassInspector.inspect(SomeClass)
+
+        details.propertyNames == ['metaClass', 'prop', 'readOnly', 'writeOnly'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 0
+        writeOnly.setters.size() == 1
+    }
+
+    def "extracts property names"() {
+        expect:
+        def details = ClassInspector.inspect(PropNames)
+
+        details.propertyNames == ['metaClass', 'a', 'b', 'URL', 'url', '_A'] as Set
+    }
+
+    def "extracts properties of a Groovy interface"() {
+        expect:
+        def details = ClassInspector.inspect(SomeInterface)
+
+        details.propertyNames == ['prop', 'readOnly', 'writeOnly'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 0
+        writeOnly.setters.size() == 1
+    }
+
+    def "extracts boolean properties"() {
+        expect:
+        def details = ClassInspector.inspect(BooleanProps)
+
+        details.propertyNames == ['metaClass', 'prop', 'someProp', 'readOnly'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 2
+        prop.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def someProp = details.getProperty('someProp')
+        someProp.getters.size() == 1
+        someProp.setters.size() == 1
+    }
+
+    def "extracts properties with overloaded getters and setters"() {
+        expect:
+        def details = ClassInspector.inspect(Overloads)
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 2
+        prop.setters.size() == 3
+    }
+
+    def "extracts properties from super class"() {
+        expect:
+        def details = ClassInspector.inspect(SubClass)
+
+        details.propertyNames == ['metaClass', 'prop', 'readOnly', 'writeOnly', 'other'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 1
+
+        def other = details.getProperty('other')
+        other.getters.size() == 1
+        other.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 0
+        writeOnly.setters.size() == 1
+    }
+
+    def "extracts properties from super interface"() {
+        expect:
+        def details = ClassInspector.inspect(SubInterface)
+
+        details.propertyNames == ['prop', 'readOnly', 'writeOnly', 'other'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 1
+
+        def other = details.getProperty('other')
+        other.getters.size() == 1
+        other.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 0
+        writeOnly.setters.size() == 1
+    }
+
+    def "extracts properties from super type graph"() {
+        expect:
+        def details = ClassInspector.inspect(AbstractSubClass)
+
+        details.propertyNames == ['metaClass', 'prop', 'readOnly', 'writeOnly', 'other'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 1
+
+        def other = details.getProperty('other')
+        other.getters.size() == 1
+        other.setters.size() == 1
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 0
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 0
+        writeOnly.setters.size() == 1
+    }
+
+    def "subtype can specialize a property type"() {
+        expect:
+        def details = ClassInspector.inspect(SpecializingType)
+
+        details.propertyNames == ['metaClass', 'prop', 'readOnly', 'writeOnly'] as Set
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.setters.size() == 2
+
+        def readOnly = details.getProperty('readOnly')
+        readOnly.getters.size() == 1
+        readOnly.setters.size() == 1
+
+        def writeOnly = details.getProperty('writeOnly')
+        writeOnly.getters.size() == 1
+        writeOnly.setters.size() == 1
+    }
+
+    def "collects instance methods that are not getters or setters"() {
+        expect:
+        def details = ClassInspector.inspect(SomeClass)
+
+        details.instanceMethods.find { it.name == 'prop' }
+        !details.instanceMethods.contains(details.getProperty('prop').getters[0])
+    }
+
+    def "ignores overridden property getters and setters"() {
+        expect:
+        def details = ClassInspector.inspect(Overrides)
+
+        def prop = details.getProperty('prop')
+        prop.getters.size() == 1
+        prop.getters[0].declaringClass == Overrides
+        prop.setters.size() == 1
+        prop.setters[0].declaringClass == Overrides
+    }
+
+    def "ignores overridden instance methods"() {
+        expect:
+        def details = ClassInspector.inspect(Overrides)
+
+        def methods = details.instanceMethods.findAll { it.name == 'prop' }
+        methods.size() == 1
+        methods[0].declaringClass == Overrides
+    }
+
+    class SomeClass {
+        Number prop
+
+        String getReadOnly() {
+            return prop
+        }
+
+        void setWriteOnly(String value) {
+        }
+
+        void get() {
+        }
+
+        void set(String value) {
+        }
+
+        void getProp(String value) {
+        }
+
+        void setProp() {
+        }
+
+        protected String prop() {
+            return prop
+        }
+
+        static String ignoredStatic
+        private String ignoredPrivate
+
+        private Number getPrivate() {
+            return 12
+        }
+
+        static Long getLong() {
+            return 12L
+        }
+    }
+
+    interface SomeInterface {
+        Number getProp()
+        void setProp(Number value)
+
+        String getReadOnly()
+
+        void setWriteOnly(String value)
+
+        void get()
+
+        void set(String value)
+
+        void getProp(String value)
+
+        void setProp()
+    }
+
+    class PropNames {
+        String getA() { null }
+        String getB() { null }
+        String getURL() { null }
+        String getUrl() { null }
+        String get_A() { null }
+    }
+
+    class BooleanProps {
+        boolean prop
+
+        boolean isSomeProp() {
+            return prop
+        }
+
+        void setSomeProp(boolean value) {
+        }
+
+        Boolean isReadOnly() {
+            return prop
+        }
+    }
+
+    class Overloads {
+        String getProp() {
+            return null
+        }
+
+        boolean isProp() {
+            return false
+        }
+
+        void setProp(String value) {
+        }
+
+        void setProp(int value) {
+        }
+
+        void setProp(Object value) {
+        }
+    }
+
+    class SubClass extends SomeClass {
+        Long other
+    }
+
+    interface SubInterface extends SomeInterface {
+        Long getOther()
+        void setOther(Long l)
+    }
+
+    abstract class AbstractSuperClass implements SomeInterface {
+        Number prop
+    }
+
+    abstract class AbstractSubClass extends AbstractSuperClass implements SubInterface {
+        Long other
+    }
+
+    class Overrides extends SomeClass {
+        @Override
+        Number getProp() {
+            return super.getProp()
+        }
+
+        @Override
+        void setProp(Number prop) {
+            super.setProp(prop)
+        }
+
+        @Override
+        protected String prop() {
+            return "123"
+        }
+    }
+
+    class SpecializingType extends SomeClass {
+        Long getProp() {
+            return 12L
+        }
+
+        void setProp(Long l) {
+        }
+
+        void setReadOnly(String s) {
+        }
+
+        String getWriteOnly() {
+            return ""
+        }
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/DirectInstantiatorTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/DirectInstantiatorTest.groovy
index 4798d0b..070945c 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/DirectInstantiatorTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/DirectInstantiatorTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.internal.reflect
 import spock.lang.Specification
 
 class DirectInstantiatorTest extends Specification {
-    final DirectInstantiator instantiator = new DirectInstantiator()
+    final DirectInstantiator instantiator = DirectInstantiator.INSTANCE
 
     def "creates instance with constructor parameters"() {
         CharSequence param = Mock()
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 f05e1df..ba69424 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
@@ -63,7 +63,22 @@ class JavaReflectionUtilTest extends Specification {
 
     def "read property"() {
         expect:
-        readableProperty(JavaTestSubject, "myProperty").getValue(myProperties) == "myValue"
+        readableProperty(JavaTestSubject, String, "myProperty").getValue(myProperties) == "myValue"
+    }
+
+    def "read property using instance"() {
+        expect:
+        readableProperty(myProperties, String, "myProperty").getValue(myProperties) == "myValue"
+    }
+
+    def "read field" () {
+        expect:
+        readableField(JavaTestSubject, String, "myField").getValue(myProperties) == "myFieldValue"
+    }
+
+    def "read field using instance" () {
+        expect:
+        readableField(myProperties, String, "myField").getValue(myProperties) == "myFieldValue"
     }
 
     def "write property"() {
@@ -71,12 +86,17 @@ class JavaReflectionUtilTest extends Specification {
         writeableProperty(JavaTestSubject, "myProperty").setValue(myProperties, "otherValue")
 
         then:
-        readableProperty(JavaTestSubject, "myProperty").getValue(myProperties) == "otherValue"
+        readableProperty(JavaTestSubject, String, "myProperty").getValue(myProperties) == "otherValue"
     }
 
     def "read boolean property"() {
         expect:
-        readableProperty(JavaTestSubject, "myBooleanProperty").getValue(myProperties) == true
+        readableProperty(JavaTestSubject, Boolean, "myBooleanProperty").getValue(myProperties) == true
+    }
+
+    def "read boolean field" () {
+        expect:
+        readableField(JavaTestSubject, Boolean, "myBooleanField").getValue(myProperties) == true
     }
 
     def "write boolean property"() {
@@ -84,12 +104,12 @@ class JavaReflectionUtilTest extends Specification {
         writeableProperty(JavaTestSubject, "myBooleanProperty").setValue(myProperties, false)
 
         then:
-        readableProperty(JavaTestSubject, "myBooleanProperty").getValue(myProperties) == false
+        readableProperty(JavaTestSubject, Boolean, "myBooleanProperty").getValue(myProperties) == false
     }
 
     def "cannot read property that doesn't have a well formed getter"() {
         when:
-        readableProperty(JavaTestSubject, property)
+        readableProperty(JavaTestSubject, String, property)
 
         then:
         NoSuchPropertyException e = thrown()
@@ -107,7 +127,7 @@ class JavaReflectionUtilTest extends Specification {
 
     def "cannot read property that is not public"() {
         when:
-        readableProperty(JavaTestSubject, property)
+        readableProperty(JavaTestSubject, String, property)
 
         then:
         NoSuchPropertyException e = thrown()
@@ -161,6 +181,21 @@ class JavaReflectionUtilTest extends Specification {
         method(myProperties.class, String, "getMyProperty").invoke(myProperties) == "foo"
     }
 
+    def "call static methods successfully reflectively" () {
+        when:
+        staticMethod(myProperties.class, Void, "setStaticProperty", String.class).invokeStatic("foo")
+
+        then:
+        staticMethod(myProperties.class, String, "getStaticProperty").invokeStatic() == "foo"
+    }
+
+    def "static methods are identifiable" () {
+        expect:
+        staticMethod(myProperties.class, Void, "setStaticProperty", String.class).isStatic()
+        staticMethod(myProperties.class, String, "getStaticProperty").isStatic()
+        method(myProperties.class, String, "getMyProperty").isStatic() == false
+    }
+
     def "call failing methods reflectively"() {
         when:
         method(myProperties.class, Void, "throwsException").invoke(myProperties)
@@ -225,6 +260,27 @@ class JavaReflectionUtilTest extends Specification {
         getAnnotation(InheritsInterface, NotInheritedAnnotation) == null
     }
 
+    static class Thing {
+        final String name
+
+        Thing(String name) {
+            this.name = name
+        }
+
+        Thing() {
+            this(null)
+        }
+    }
+
+    def "new instance"() {
+        def instantiator = DirectInstantiator.INSTANCE
+
+        expect:
+        factory(instantiator, Thing).create().name == null
+        factory(instantiator, Thing, "foo").create().name == "foo"
+        !factory(instantiator, Thing).create().is(factory(instantiator, Thing).create())
+    }
+
 }
 
 @Retention(RetentionPolicy.RUNTIME)
@@ -251,14 +307,18 @@ interface RootInterface {}
 interface SubInterface extends RootInterface {}
 
 class ImplementsRootInterface implements RootInterface {}
+
 class ImplementsSubInterface implements SubInterface {}
+
 class ImplementsBoth implements RootInterface, SubInterface {}
 
 @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
index aa3e8dd..d3f3bbe 100644
--- 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
@@ -22,6 +22,9 @@ public class JavaTestSubject {
     final IllegalStateException failure = new IllegalStateException();
     private String myProp = "myValue";
     private boolean myBooleanProp = true;
+    public String myField = "myFieldValue";
+    public boolean myBooleanField = true;
+    private static String myStaticProperty;
 
     public int publicField;
 
@@ -54,10 +57,11 @@ public class JavaTestSubject {
     }
 
     public static String getStaticProperty() {
-        return null;
+        return myStaticProperty;
     }
 
     public static void setStaticProperty(String value) {
+        myStaticProperty = value;
     }
 
     public void getVoidProperty() {
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/MethodDescriptionTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/MethodDescriptionTest.groovy
new file mode 100644
index 0000000..bba3972
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/reflect/MethodDescriptionTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import spock.lang.Specification
+
+class MethodDescriptionTest extends Specification {
+
+    def "string"() {
+        expect:
+        MethodDescription.name("a").toString() == "a"
+        MethodDescription.name("a").returns(String).toString() == "java.lang.String a"
+        MethodDescription.name("a").returns(String).takes().toString() == "java.lang.String a()"
+        MethodDescription.name("a").returns(String).owner(String).takes().toString() == "java.lang.String java.lang.String#a()"
+        MethodDescription.name("a").returns(String).owner(String).takes(String).toString() == "java.lang.String java.lang.String#a(java.lang.String)"
+        MethodDescription.name("a").returns(String).owner(String).takes(String, String).toString() == "java.lang.String java.lang.String#a(java.lang.String, java.lang.String)"
+        MethodDescription.name("a").owner(String).takes(String, String).toString() == "java.lang.String#a(java.lang.String, java.lang.String)"
+    }
+}
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/DefaultServiceRegistryTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/DefaultServiceRegistryTest.groovy
index b72d4f6..0625c27 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/service/DefaultServiceRegistryTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/service/DefaultServiceRegistryTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.internal.service
 
 import org.gradle.api.Action
 import org.gradle.internal.Factory
+import org.gradle.internal.concurrent.Stoppable
 import org.gradle.util.TextUtil
 import spock.lang.Specification
 
@@ -220,6 +221,7 @@ class DefaultServiceRegistryTest extends Specification {
             Callable<String> createStringCallable() {
                 return { "hello" }
             }
+
             Factory<String> createStringFactory() {
                 return { "world" } as Factory
             }
@@ -593,7 +595,7 @@ class DefaultServiceRegistryTest extends Specification {
 
     def failsWhenCannotCreateServiceInstanceFromImplementationClass() {
         given:
-        registry.register({ registration -> registration.add(ClassWithBrokenConstructor)} as Action)
+        registry.register({ registration -> registration.add(ClassWithBrokenConstructor) } as Action)
 
         when:
         registry.get(ClassWithBrokenConstructor)
@@ -605,7 +607,7 @@ class DefaultServiceRegistryTest extends Specification {
     }
 
     def canGetAllServicesOfAGivenType() {
-        registry.addProvider(new Object(){
+        registry.addProvider(new Object() {
             String createOtherString() {
                 return "hi"
             }
@@ -618,13 +620,15 @@ class DefaultServiceRegistryTest extends Specification {
 
     def canGetAllServicesOfARawType() {
         def registry = new DefaultServiceRegistry()
-        registry.addProvider(new Object(){
+        registry.addProvider(new Object() {
             String createString() {
                 return "hi"
             }
+
             Factory<String> createFactory() {
                 return {} as Factory
             }
+
             Callable<String> createCallable() {
                 return {}
             }
@@ -809,7 +813,7 @@ class DefaultServiceRegistryTest extends Specification {
 
     def closeInvokesCloseMethodOnEachServiceCreatedFromImplementationClass() {
         given:
-        registry.register({ registration -> registration.add(ClosableService)} as Action)
+        registry.register({ registration -> registration.add(ClosableService) } as Action)
         def service = registry.get(ClosableService)
 
         when:
@@ -840,20 +844,21 @@ class DefaultServiceRegistryTest extends Specification {
     def closeClosesServicesInDependencyOrder() {
         def service1 = Mock(TestCloseService)
         def service2 = Mock(TestStopService)
-        def service3 = Mock(Closeable)
+        def service3 = Mock(ClosableService)
         def registry = new DefaultServiceRegistry()
 
         given:
         registry.addProvider(new Object() {
-            TestStopService createService2(Closeable b) {
+            TestStopService createService2(ClosableService b) {
                 return service2
             }
-            Closeable createService3() {
+
+            ClosableService createService3() {
                 return service3
             }
         })
         registry.addProvider(new Object() {
-            TestCloseService createService1(TestStopService a, Closeable b) {
+            TestCloseService createService1(TestStopService a, ClosableService b) {
                 return service1
             }
         })
@@ -876,19 +881,21 @@ class DefaultServiceRegistryTest extends Specification {
     def closeContinuesToCloseServicesAfterFailingToStopSomeService() {
         def service1 = Mock(TestCloseService)
         def service2 = Mock(TestStopService)
-        def service3 = Mock(Closeable)
+        def service3 = Mock(ClosableService)
         def failure = new RuntimeException()
         def registry = new DefaultServiceRegistry()
 
         given:
         registry.addProvider(new Object() {
-            TestStopService createService2(Closeable b) {
+            TestStopService createService2(ClosableService b) {
                 return service2
             }
-            TestCloseService createService1(TestStopService a, Closeable b) {
+
+            TestCloseService createService1(TestStopService a) {
                 return service1
             }
-            Closeable createService3() {
+
+            ClosableService createService3() {
                 return service3
             }
         })
@@ -978,6 +985,60 @@ class DefaultServiceRegistryTest extends Specification {
         e.message == "Cannot locate factory for objects of type BigDecimal, as TestRegistry has been closed."
     }
 
+    def "cannot add provider after getting a service via class"() {
+        when:
+        registry.get(Integer)
+        registry.addProvider(new TestProvider())
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot add provider after getting a service via type"() {
+        when:
+        registry.get(Integer as Type)
+        registry.addProvider(new TestProvider())
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot add provider after getting all services"() {
+        when:
+        registry.getAll(Integer)
+        registry.addProvider(new TestProvider())
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot add instance after getting a service via class"() {
+        when:
+        registry.get(Integer)
+        registry.add(String, "foo")
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot add instance after getting a service via type"() {
+        when:
+        registry.get(Integer as Type)
+        registry.add(String, "foo")
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot add instance after getting all services"() {
+        when:
+        registry.getAll(Integer)
+        registry.add(String, "foo")
+
+        then:
+        thrown IllegalStateException
+    }
+
     private Factory<Number> numberFactory
     private Factory<String> stringFactory
     private Factory<? super BigDecimal> superBigDecimalFactory
@@ -1006,6 +1067,7 @@ class DefaultServiceRegistryTest extends Specification {
 
     private static class TestFactory implements Factory<BigDecimal> {
         int value;
+
         public BigDecimal create() {
             return BigDecimal.valueOf(value++)
         }
@@ -1209,18 +1271,14 @@ class DefaultServiceRegistryTest extends Specification {
         }
     }
 
-    public interface TestCloseService {
+    public interface TestCloseService extends Closeable {
         void close()
     }
 
-    public interface TestStopService {
+    public interface TestStopService extends Stoppable {
         void stop()
     }
 
-    public interface ClosableServiceRegistry extends ServiceRegistry {
-        void close()
-    }
-
     static class ClassWithBrokenConstructor {
         static def failure = new RuntimeException("broken")
 
@@ -1229,7 +1287,7 @@ class DefaultServiceRegistryTest extends Specification {
         }
     }
 
-    static class ClosableService {
+    static class ClosableService implements Closeable {
         boolean closed
 
         void close() {
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/util/NumberUtilTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/util/NumberUtilTest.groovy
new file mode 100644
index 0000000..142a700
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/util/NumberUtilTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util
+
+import spock.lang.Specification
+
+import static org.gradle.internal.util.NumberUtil.formatBytes
+import static org.gradle.internal.util.NumberUtil.percentOf
+
+class NumberUtilTest extends Specification {
+
+    def "knows percentage"() {
+        expect:
+        percentOf(0, 100) == 0
+        percentOf(1, 100) == 1
+        percentOf(99, 100) == 99
+        percentOf(100, 100) == 100
+        percentOf(101, 100) == 101
+        percentOf(50, 200) == 25
+        percentOf(50, 200) == 25
+        percentOf(17, 301) == 5
+        percentOf(50, 0) == 0
+    }
+
+    def "percentage does not allow negative values"() {
+        when: percentOf(50, -10) == 0
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Unable to calculate percentage: 50 of -10. All inputs must be >= 0"
+
+        when: percentOf(-1, 100) == 0
+        then:
+        ex = thrown(IllegalArgumentException)
+        ex.message == "Unable to calculate percentage: -1 of 100. All inputs must be >= 0"
+    }
+
+    def "formats bytes"() {
+        expect:
+        formatBytes(-1) == "-1 B"
+        formatBytes(0) == "0 B"
+        formatBytes(1) == "1 B"
+        formatBytes(999) == "999 B"
+        formatBytes(1000) == String.format("%.1f kB", 1.0)
+        formatBytes(1001) == String.format("%.1f kB", 1.0)
+        formatBytes(1501) == String.format("%.1f kB", 1.5)
+        formatBytes(1999) == String.format("%.1f kB", 2.0)
+        formatBytes(-1999) == String.format("%.1f kB", -2.0)
+        formatBytes(1000000) == String.format("%.1f MB", 1.0)
+        formatBytes(1000000000) == String.format("%.1f GB", 1.0)
+        formatBytes(1000000000000) == String.format("%.1f TB", 1.0)
+        formatBytes(1000000000000000) == String.format("%.1f PB", 1.0)
+        formatBytes(1000000000000000000) == String.format("%.1f EB", 1.0)
+    }
+
+    def "knows ordinal"() {
+        expect:
+        ordinal == NumberUtil.ordinal(input)
+
+        where:
+        input | ordinal
+        0     | "0th"
+        1     | "1st"
+        2     | "2nd"
+        3     | "3rd"
+        4     | "4th"
+        10    | "10th"
+        11    | "11th"
+        12    | "12th"
+        13    | "13th"
+        14    | "14th"
+        20    | "20th"
+        21    | "21st"
+        22    | "22nd"
+        23    | "23rd"
+        24    | "24th"
+        100   | "100th"
+        1001  | "1001st"
+        10012 | "10012th"
+        10013 | "10013th"
+        10014 | "10014th"
+    }
+}
+
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 ae8f0c5..891354c 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
@@ -15,6 +15,7 @@
  */
 package org.gradle.util
 
+import com.google.common.base.Equivalence
 import org.gradle.api.Action
 import org.gradle.api.Transformer
 import org.gradle.api.internal.ClosureBackedAction
@@ -40,6 +41,18 @@ class CollectionUtilsTest extends Specification {
         filter(4, 5, 6) == [4]
     }
 
+    def "array filtering"() {
+        given:
+        def spec = Specs.convertClosureToSpec { it < 5 }
+        def filter = { Integer[] nums -> filter(nums, spec) }
+
+        expect:
+        filter(1, 2, 3) == [1, 2, 3]
+        filter(7, 8, 9) == []
+        filter() == []
+        filter(4, 5, 6) == [4]
+    }
+
     def "list collecting"() {
         def transformer = new Transformer() {
             def transform(i) { i * 2 }
@@ -172,10 +185,10 @@ class CollectionUtilsTest extends Specification {
         expect:
         intersection([collA, collB]) == collC
         where:
-        collA              | collB            | collC
-        []                 | ["a", "b", "c"]  | []
-        ['a', 'b', 'c']    | ["a", "b", "c"]  | ['a', 'b', 'c']
-        ['a', 'b', 'c']    | ["b", "c"]       | ['b', 'c']
+        collA           | collB           | collC
+        []              | ["a", "b", "c"] | []
+        ['a', 'b', 'c'] | ["a", "b", "c"] | ['a', 'b', 'c']
+        ['a', 'b', 'c'] | ["b", "c"]      | ['b', 'c']
     }
 
     def "flattenToList"() {
@@ -289,9 +302,16 @@ class CollectionUtilsTest extends Specification {
         toSet([]).empty
     }
 
+    def "to list"() {
+        expect:
+        toList([1, 2, 3] as Set) == [1, 2, 3]
+        toList([]).empty
+        toList(([1, 2, 3] as Vector).elements()) == [1, 2, 3]
+    }
+
     def "sorting with comparator"() {
         given:
-        def naturalComparator = { a, b -> a<=>b } as Comparator
+        def naturalComparator = { a, b -> a <=> b } as Comparator
 
         expect:
         def l = [1, 2, 3]
@@ -316,6 +336,31 @@ class CollectionUtilsTest extends Specification {
         sort([] as Set) == []
     }
 
+    def "grouping"() {
+        expect:
+        groupBy([1, 2, 3], transformer { "a" }).asMap() == ["a": [1, 2, 3]]
+        groupBy(["a", "b", "c"], transformer { it.toUpperCase() }).asMap() == ["A": ["a"], "B": ["b"], "C": ["c"]]
+        groupBy([], transformer { throw new AssertionError("shouldn't be called") }).isEmpty()
+    }
+
+    def "dedup"() {
+        expect:
+        dedup([1, 2, 3, 2], Equivalence.equals()) == [1, 2, 3]
+        dedup([], Equivalence.equals()) == []
+    }
+
+    def unpack() {
+        expect:
+        unpack([{ 1 } as org.gradle.internal.Factory, { 2 } as org.gradle.internal.Factory, { 3 } as org.gradle.internal.Factory]).toList() == [1, 2, 3]
+        unpack([]).toList().isEmpty()
+    }
+
+    def nonEmptyOrNull() {
+        expect:
+        nonEmptyOrNull([1, 2, 3]) == [1, 2, 3]
+        nonEmptyOrNull([]) == null
+    }
+
     Spec<?> spec(Closure c) {
         Specs.convertClosureToSpec(c)
     }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/util/GradleVersionTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
new file mode 100644
index 0000000..ea45748
--- /dev/null
+++ b/subprojects/base-services/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.util
+
+import spock.lang.Issue
+import spock.lang.Specification
+
+class GradleVersionTest extends Specification {
+    final GradleVersion version = GradleVersion.current()
+
+    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 "current version has non-null parts"() {
+        expect:
+        version.version
+        version.buildTime
+        version.nextMajor
+        version.baseVersion
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-1892")
+    def "build time should always print in UTC"() {
+        expect:
+        version.buildTime.endsWith("UTC")
+    }
+
+    def equalsAndHashCode() {
+        expect:
+        Matchers.strictlyEquals(GradleVersion.version('0.9'), GradleVersion.version('0.9'))
+        GradleVersion.version('0.9') != GradleVersion.version('1.0')
+    }
+
+    def canConstructVersionFromString(String version) {
+        expect:
+        def gradleVersion = GradleVersion.version(version)
+        gradleVersion.version == version
+        gradleVersion.toString() == "Gradle ${version}"
+
+        where:
+        version << [
+                '1.0',
+                '12.4.5.67',
+                '1.0-milestone-5',
+                '1.0-milestone-5a',
+                '3.2-rc-2',
+        ]
+    }
+
+    def versionsWithTimestampAreConsideredSnapshots(String version) {
+        expect:
+        def gradleVersion = GradleVersion.version(version)
+        gradleVersion.version == version
+        gradleVersion.snapshot
+
+        where:
+        version << [
+                '0.9-20101220110000+1100',
+                '0.9-20101220110000-0800',
+                '1.2-20120501110000'
+        ]
+    }
+
+    def versionsWithoutTimestampAreNotConsideredSnapshots(String version) {
+        expect:
+        !GradleVersion.version(version).snapshot
+
+        where:
+        version << [
+                '0.9-milestone-5',
+                '2.1-rc-1',
+                '1.2',
+                '1.2.1']
+    }
+
+    def canCompareMajorVersions() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a      | b
+        '0.9'  | '0.8'
+        '1.0'  | '0.10'
+        '10.0' | '2.1'
+        '2.5'  | '2.4'
+    }
+
+    def canComparePointVersions() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a                   | b
+        '0.9.2'             | '0.9.1'
+        '0.10.1'            | '0.9.2'
+        '1.2.3.40'          | '1.2.3.8'
+        '1.2.3.1'           | '1.2.3'
+        '1.2.3.1.4.12.9023' | '1.2.3'
+    }
+
+    def canComparePointVersionAndMajorVersions() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a       | b
+        '0.9.1' | '0.9'
+        '0.10'  | '0.9.1'
+    }
+
+    def canComparePreviewsMilestonesAndRCVersions() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a                 | b
+        '1.0-milestone-2' | '1.0-milestone-1'
+        '1.0-preview-2'   | '1.0-preview-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'
+    }
+
+    def canComparePatchVersion() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a                  | b
+        '1.0-milestone-2a' | '1.0-milestone-2'
+        '1.0-milestone-2b' | '1.0-milestone-2a'
+        '1.0-milestone-3'  | '1.0-milestone-2b'
+        '1.0'              | '1.0-milestone-2b'
+    }
+
+    def canCompareSnapshotVersions() {
+        expect:
+        GradleVersion.version(a) > GradleVersion.version(b)
+        GradleVersion.version(b) < GradleVersion.version(a)
+        GradleVersion.version(a) == GradleVersion.version(a)
+        GradleVersion.version(b) == GradleVersion.version(b)
+
+        where:
+        a                         | b
+        '0.9-20101220110000+1100' | '0.9-20101220100000+1100'
+        '0.9-20101220110000+1000' | '0.9-20101220100000+1100'
+        '0.9-20101220110000-0100' | '0.9-20101220100000+0000'
+        '0.9-20101220110000'      | '0.9-20101220100000'
+        '0.9-20101220110000'      | '0.9-20101220110000+0100'
+        '0.9-20101220110000-0100' | '0.9-20101220110000'
+        '0.9'                     | '0.9-20101220100000+1000'
+        '0.9'                     | '0.9-20101220100000'
+    }
+
+    def "can get version base"() {
+        expect:
+        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
+
+        where:
+        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 "milestones are treated as base versions"() {
+        expect:
+        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
+
+        where:
+        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
+    }
+}
diff --git a/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/fixtures/BuildComparisonHtmlReportFixture.groovy b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/fixtures/BuildComparisonHtmlReportFixture.groovy
new file mode 100644
index 0000000..b666248
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/fixtures/BuildComparisonHtmlReportFixture.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.fixtures
+
+import org.jsoup.nodes.Document
+
+class BuildComparisonHtmlReportFixture {
+    Document document
+
+    public BuildComparisonHtmlReportFixture(Document document) {
+        this.document = document
+    }
+
+    String getOutcomeName() {
+        document.select("h3").text()
+    }
+
+    def getEntries() {
+        document.select("table")[2].select("tr").tail().collectEntries { [it.select("td")[0].text(), it.select("td")[1].text()] }
+    }
+
+    boolean isIdentical() {
+        document.select("p").last().text() == "The archives are completely identical."
+    }
+
+    def select(def selector) {
+        document.select(selector)
+    }
+
+    def getResult(String id) {
+        def outcomeComparision = document.select("div.build-outcome-comparison").find { it.id() == id }
+        outcomeComparision.select(".comparison-result-msg").text()
+    }
+
+    String getSourceBuildVersion() {
+        document.body().select("table")[0].select("tr")[2].select("td")[0].text()
+    }
+
+    String getTargetBuildVersion() {
+        document.body().select("table")[0].select("tr")[2].select("td")[1].text()
+    }
+
+    def sourceWasInferred(){
+        hasInferredHtmlWarning("source")
+    }
+
+    def targetWasInferred() {
+        hasInferredHtmlWarning("target")
+    }
+
+    private void hasInferredHtmlWarning(String buildName) {
+        assert document.body().select(".warning.inferred-outcomes").text().contains("Build outcomes were not able to be determined for the $buildName build")
+    }
+}
diff --git a/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec.groovy b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec.groovy
index 1d50e30..ecc674a 100644
--- a/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec.groovy
+++ b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec.groovy
@@ -16,20 +16,20 @@
 
 package org.gradle.api.plugins.buildcomparison.gradle
 
+import org.gradle.api.plugins.buildcomparison.fixtures.BuildComparisonHtmlReportFixture
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
 import org.gradle.test.fixtures.file.TestFile
 import org.jsoup.Jsoup
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
 import org.junit.Rule
 
 class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
     private static final String NOT_IDENTICAL_MESSAGE_PREFIX = "The build outcomes were not found to be identical. See the report at: file:///"
-    @Rule TestResources testResources = new TestResources(temporaryFolder)
+    @Rule
+    TestResources testResources = new TestResources(temporaryFolder)
 
     @Override
-    String getPluginId() {
+    String getPluginName() {
         "compare-gradle-builds"
     }
 
@@ -43,33 +43,53 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         applyPlugin()
     }
 
-    def compareArchives() {
-        given:
-        buildFile << """
-            compareGradleBuilds {
-                sourceBuild.projectDir "source"
-                targetBuild { projectDir "target" }
-            }
-        """
-
+    def compareSimpleArchives() {
         when:
-        fails "compareGradleBuilds"
+        buildsCompared("source", "target")
 
         then:
         failedBecauseNotIdentical()
 
-        def html = html()
-
-        // Name of outcome
-        html.select("h3").text() == ":jar"
+        def report = report()
+        report.outcomeName == ":jar"
 
-        // Entry comparisons
-        def rows = html.select("table")[2].select("tr").tail().collectEntries { [it.select("td")[0].text(), it.select("td")[1].text()] }
-        rows.size() == 4
+        def rows = report.entries
+        rows.size() == 7
         rows["org/gradle/Changed.class"] == "entry in the source build is 394 bytes - in the target build it is 471 bytes (+77)"
         rows["org/gradle/DifferentCrc.class"] == "entries are of identical size but have different content"
         rows["org/gradle/SourceBuildOnly.class"] == "entry does not exist in target build archive"
         rows["org/gradle/TargetBuildOnly.class"] == "entry does not exist in source build archive"
+        rows["someSource.properties"] == "entry does not exist in target build archive"
+        rows["someTarget.properties"] == "entry does not exist in source build archive"
+        rows["dir1/different.txt"] == "entries are of identical size but have different content"
+
+        and:
+        storedFile("source").exists()
+        storedFile("source/_jar").list().toList() == ["testBuild.jar"]
+        storedFile("target/_jar").list().toList() == ["testBuild.jar"]
+
+        and: // old filestore not around
+        !testDirectory.list().any { it.startsWith(CompareGradleBuilds.TMP_FILESTORAGE_PREFIX) }
+    }
+
+    def compareNestedArchives() {
+        when:
+        buildsCompared("source", "target")
+
+        then:
+        failedBecauseNotIdentical()
+
+        def report = report()
+        report.outcomeName == ":jar"
+
+        // Entry comparisons
+        def rows = report.entries
+
+        rows.size() == 4
+        rows["sourceSub.zip"] == "entry does not exist in target build archive"
+        rows["targetSub.zip"] == "entry does not exist in source build archive"
+        rows["dir2/differentSub.zip"] == "entry in the source build is 688 bytes - in the target build it is 689 bytes (+1)"
+        rows["dir2/differentSub.zip!/a.txt"] == "entry in the source build is 0 bytes - in the target build it is 1 bytes (+1)"
 
         and:
         storedFile("source").exists()
@@ -80,6 +100,17 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         !testDirectory.list().any { it.startsWith(CompareGradleBuilds.TMP_FILESTORAGE_PREFIX) }
     }
 
+
+    def buildsCompared(String source, String target) {
+        buildFile << """
+            compareGradleBuilds {
+                sourceBuild.projectDir "$source"
+                targetBuild { projectDir "$target" }
+            }
+        """
+        fails "compareGradleBuilds"
+    }
+
     void failedBecauseNotIdentical() {
         failure.assertHasCause(NOT_IDENTICAL_MESSAGE_PREFIX)
     }
@@ -96,7 +127,7 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         run "compareGradleBuilds"
 
         then:
-        html().select("p").last().text() == "The archives are completely identical."
+        report().identical
         output.contains("The source build and target build are identical")
     }
 
@@ -125,7 +156,7 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         then:
         failedBecauseNotIdentical()
 
-        html().select("h3")[0].nextSibling().nextSibling().text() == "This version of Gradle does not understand this kind of build outcome."
+        report().select("h3")[0].nextSibling().nextSibling().text() == "This version of Gradle does not understand this kind of build outcome."
     }
 
     def "cannot compare when both sides are old"() {
@@ -221,7 +252,7 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         fails "compareGradleBuilds"
 
         and:
-        comparisonResultMsg(html(), ":jar") == "The archive was only produced by the target build."
+        report().getResult(":jar") == "The archive was only produced by the target build."
     }
 
     def "can handle artifact not existing on target side"() {
@@ -241,7 +272,7 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         fails "compareGradleBuilds"
 
         and:
-        comparisonResultMsg(html(), ":jar") == "The archive was only produced by the source build."
+        report().getResult(":jar") == "The archive was only produced by the source build."
     }
 
     def "can handle uncompared outcomes"() {
@@ -281,27 +312,20 @@ class BuildComparisonIntegrationSpec extends WellBehavedPluginTest {
         then:
         fails "compareGradleBuilds"
 
-        def html = html()
-        html.select("h2").find { it.text() == "Uncompared source outcomes" }
-        html.select("h2").find { it.text() == "Uncompared target outcomes" }
+        def report = report()
+        report.select("h2").find { it.text() == "Uncompared source outcomes" }
+        report.select("h2").find { it.text() == "Uncompared target outcomes" }
 
-        html.select(".build-outcome.source h3").text() == ":javadocJar"
-        html.select(".build-outcome.target h3").text() == ":sourceJar"
+        report.select(".build-outcome.source h3").text() == ":javadocJar"
+        report.select(".build-outcome.target h3").text() == ":sourceJar"
     }
 
-    Document html(path = "build/reports/compareGradleBuilds/index.html") {
-        Jsoup.parse(file(path), null)
+    BuildComparisonHtmlReportFixture report(path = "build/reports/compareGradleBuilds/index.html") {
+        new BuildComparisonHtmlReportFixture(Jsoup.parse(file(path), null))
     }
 
     TestFile storedFile(String path, String base = "build/reports/compareGradleBuilds/files") {
         file("$base/$path")
     }
-
-    String comparisonResultMsg(Document html, String id) {
-        outcomeComparison(html, id).select(".comparison-result-msg").text()
-    }
-
-    Element outcomeComparison(Document html, String id) {
-        html.select("div.build-outcome-comparison").find { it.id() == id }
-    }
 }
+
diff --git a/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/Pre12CompareGradleBuildsCrossVersionSpec.groovy b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/Pre12CompareGradleBuildsCrossVersionSpec.groovy
index 8ee6117..709997c 100644
--- a/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/Pre12CompareGradleBuildsCrossVersionSpec.groovy
+++ b/subprojects/build-comparison/src/integTest/groovy/org/gradle/api/plugins/buildcomparison/gradle/Pre12CompareGradleBuildsCrossVersionSpec.groovy
@@ -16,13 +16,13 @@
 
 package org.gradle.api.plugins.buildcomparison.gradle
 
+import org.gradle.api.plugins.buildcomparison.fixtures.BuildComparisonHtmlReportFixture
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetVersions
 import org.gradle.integtests.fixtures.executer.ExecutionResult
 import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.jsoup.Jsoup
-import org.jsoup.nodes.Document
 
 @TargetVersions(["1.0", "1.1"])
 class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSpec {
@@ -51,8 +51,9 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         sourceWasInferred()
 
         then:
-        sourceBuildVersion == previous.version.version
-        targetBuildVersion == current.version.version
+        def report = report();
+        report.sourceBuildVersion == previous.version.version
+        report.targetBuildVersion == current.version.version
     }
 
     def "can compare identical builds with target pre 1.2"() {
@@ -73,8 +74,9 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         targetWasInferred()
 
         then:
-        sourceBuildVersion == current.version.version
-        targetBuildVersion == previous.version.version
+        def report = report();
+        report.sourceBuildVersion == current.version.version
+        report.targetBuildVersion == previous.version.version
     }
 
     def "can compare different builds with source pre 1.2"() {
@@ -97,8 +99,9 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         sourceWasInferred()
 
         then:
-        sourceBuildVersion == previous.version.version
-        targetBuildVersion == current.version.version
+        def report = report();
+        report.sourceBuildVersion == previous.version.version
+        report.targetBuildVersion == current.version.version
     }
 
     def "can compare different builds with target pre 1.2"() {
@@ -121,8 +124,9 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         targetWasInferred()
 
         then:
-        sourceBuildVersion == current.version.version
-        targetBuildVersion == previous.version.version
+        def report = report();
+        report.sourceBuildVersion == current.version.version
+        report.targetBuildVersion == previous.version.version
     }
 
     protected versionGuard(TestFile file = buildFile, Closure string) {
@@ -140,16 +144,8 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         current.executer(temporaryFolder).requireGradleHome().withTasks("compareGradleBuilds")
     }
 
-    Document html(path = "build/reports/compareGradleBuilds/index.html") {
-        Jsoup.parse(file(path), null)
-    }
-
-    String getSourceBuildVersion(Document html = this.html()) {
-        html.body().select("table")[0].select("tr")[2].select("td")[0].text()
-    }
-
-    String getTargetBuildVersion(Document html = this.html()) {
-        html.body().select("table")[0].select("tr")[2].select("td")[1].text()
+    BuildComparisonHtmlReportFixture report(path = "build/reports/compareGradleBuilds/index.html") {
+        new BuildComparisonHtmlReportFixture(Jsoup.parse(file(path), null))
     }
 
     void failBecauseNotIdentical() {
@@ -157,22 +153,19 @@ class Pre12CompareGradleBuildsCrossVersionSpec extends CrossVersionIntegrationSp
         result.assertHasCause("The build outcomes were not found to be identical. See the report at: file:///")
     }
 
-    void sourceWasInferred(Document html = this.html()) {
+    void sourceWasInferred(def html = this.report()) {
+        html.sourceWasInferred()
         hasInferredLogWarning("source")
-        hasInferredHtmlWarning("source", html)
     }
 
-    void targetWasInferred(Document html = this.html()) {
+    void targetWasInferred(def html = this.report()) {
+        html.targetWasInferred()
         hasInferredLogWarning("target")
-        hasInferredHtmlWarning("target", html)
     }
 
-    void hasInferredLogWarning(String buildName) {
+    private void hasInferredLogWarning(String buildName) {
         assert result.output.contains("The build outcomes for the $buildName build will be inferred from the")
     }
 
-    void hasInferredHtmlWarning(String buildName, Document html) {
-        assert html.body().select(".warning.inferred-outcomes").text().contains("Build outcomes were not able to be determined for the $buildName build")
-    }
 
 }
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/build.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/build.gradle
similarity index 100%
copy from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/build.gradle
copy to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/build.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/settings.gradle
similarity index 100%
copy from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle
copy to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/settings.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir1/sameSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir1/sameSub.zip
new file mode 100644
index 0000000..f4aa9e2
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir1/sameSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir2/differentSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir2/differentSub.zip
new file mode 100644
index 0000000..f4aa9e2
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/dir2/differentSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/sourceSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/sourceSub.zip
new file mode 100644
index 0000000..f4aa9e2
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/source/src/main/resources/sourceSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/build.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/build.gradle
similarity index 100%
copy from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/build.gradle
copy to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/build.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/settings.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/settings.gradle
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/settings.gradle
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/settings.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir1/sameSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir1/sameSub.zip
new file mode 100644
index 0000000..f4aa9e2
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir1/sameSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir2/differentSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir2/differentSub.zip
new file mode 100644
index 0000000..c8c9399
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/dir2/differentSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/targetSub.zip b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/targetSub.zip
new file mode 100644
index 0000000..f4aa9e2
Binary files /dev/null and b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareNestedArchives/target/src/main/resources/targetSub.zip differ
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/build.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/build.gradle
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/build.gradle
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/build.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/settings.gradle
similarity index 100%
copy from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle
copy to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/settings.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/Changed.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/Changed.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/Changed.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/Changed.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/DifferentCrc.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/DifferentCrc.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/DifferentCrc.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/DifferentCrc.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/SourceBuildOnly.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/SourceBuildOnly.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/SourceBuildOnly.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/SourceBuildOnly.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/Unchanged.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/Unchanged.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/src/main/java/org/gradle/Unchanged.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/java/org/gradle/Unchanged.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/dir1/different.txt b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/dir1/different.txt
new file mode 100644
index 0000000..7084f23
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/dir1/different.txt
@@ -0,0 +1 @@
+this file differs in source and target dir
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/similar.txt b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/similar.txt
new file mode 100644
index 0000000..05b431d
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/similar.txt
@@ -0,0 +1 @@
+Some similar content in target and source
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/someSource.properties b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/someSource.properties
new file mode 100644
index 0000000..df105a2
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/source/src/main/resources/someSource.properties
@@ -0,0 +1 @@
+testProp=source
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/build.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/build.gradle
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/build.gradle
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/build.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/settings.gradle
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/source/settings.gradle
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/settings.gradle
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/Changed.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/Changed.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/Changed.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/Changed.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/DifferentCrc.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/DifferentCrc.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/DifferentCrc.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/DifferentCrc.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/TargetBuildOnly.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/TargetBuildOnly.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/TargetBuildOnly.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/TargetBuildOnly.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/Unchanged.java b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/Unchanged.java
similarity index 100%
rename from subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareArchives/target/src/main/java/org/gradle/Unchanged.java
rename to subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/java/org/gradle/Unchanged.java
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/dir1/different.txt b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/dir1/different.txt
new file mode 100644
index 0000000..4c75564
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/dir1/different.txt
@@ -0,0 +1 @@
+this file differs in target and target dir
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/similar.txt b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/similar.txt
new file mode 100644
index 0000000..05b431d
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/similar.txt
@@ -0,0 +1 @@
+Some similar content in target and source
\ No newline at end of file
diff --git a/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/someTarget.properties b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/someTarget.properties
new file mode 100644
index 0000000..4c86809
--- /dev/null
+++ b/subprojects/build-comparison/src/integTest/resources/org/gradle/api/plugins/buildcomparison/gradle/BuildComparisonIntegrationSpec/compareSimpleArchives/target/src/main/resources/someTarget.properties
@@ -0,0 +1 @@
+testProp=target
\ No newline at end of file
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 ede1389..e4c7932 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,8 +18,7 @@ package org.gradle.api.plugins.buildcomparison.gradle;
 
 import org.gradle.api.*;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.filestore.FileStore;
-import org.gradle.api.internal.filestore.PathNormalisingKeyFileStore;
+import org.gradle.internal.resource.local.PathNormalisingKeyFileStore;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.plugins.buildcomparison.compare.internal.BuildComparisonResult;
 import org.gradle.api.plugins.buildcomparison.gradle.internal.ComparableGradleBuildExecuter;
@@ -37,6 +36,7 @@ import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.OutputDirectory;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.api.tasks.VerificationTask;
+import org.gradle.internal.resource.local.FileStore;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.logging.ConsoleRenderer;
 import org.gradle.logging.ProgressLogger;
@@ -65,14 +65,9 @@ public class CompareGradleBuilds extends DefaultTask implements VerificationTask
     private boolean ignoreFailures;
     private Object reportDir;
 
-    private final FileResolver fileResolver;
-    private final ProgressLoggerFactory progressLoggerFactory;
-
-    @Inject
-    public CompareGradleBuilds(FileResolver fileResolver, ProgressLoggerFactory progressLoggerFactory, Instantiator instantiator) {
-        this.fileResolver = fileResolver;
-        this.progressLoggerFactory = progressLoggerFactory;
-
+    public CompareGradleBuilds() {
+        FileResolver fileResolver = getFileResolver();
+        Instantiator instantiator = getInstantiator();
         sourceBuild = instantiator.newInstance(DefaultGradleBuildInvocationSpec.class, fileResolver, getProject().getRootDir());
         sourceBuild.setTasks(DEFAULT_TASKS);
         targetBuild = instantiator.newInstance(DefaultGradleBuildInvocationSpec.class, fileResolver, getProject().getRootDir());
@@ -86,6 +81,21 @@ public class CompareGradleBuilds extends DefaultTask implements VerificationTask
         });
     }
 
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ProgressLoggerFactory getProgressLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * The specification of how to invoke the source build.
      *
@@ -173,7 +183,7 @@ public class CompareGradleBuilds extends DefaultTask implements VerificationTask
      */
     @OutputDirectory
     public File getReportDir() {
-        return reportDir == null ? null : fileResolver.resolve(reportDir);
+        return reportDir == null ? null : getFileResolver().resolve(reportDir);
     }
 
     /**
@@ -209,7 +219,7 @@ public class CompareGradleBuilds extends DefaultTask implements VerificationTask
         ComparableGradleBuildExecuter targetBuildExecuter = new ComparableGradleBuildExecuter(targetBuild);
 
         Logger logger = getLogger();
-        ProgressLogger progressLogger = progressLoggerFactory.newOperation(getClass());
+        ProgressLogger progressLogger = getProgressLoggerFactory().newOperation(getClass());
         progressLogger.setDescription("Gradle Build Comparison");
         progressLogger.setShortDescription(getName());
 
@@ -233,7 +243,7 @@ public class CompareGradleBuilds extends DefaultTask implements VerificationTask
                 new UnknownBuildOutcomeHtmlRenderer()
         );
 
-        File fileStoreTmpBase = fileResolver.resolve(String.format(TMP_FILESTORAGE_PREFIX + "-%s-%s", getName(), System.currentTimeMillis()));
+        File fileStoreTmpBase = getFileResolver().resolve(String.format(TMP_FILESTORAGE_PREFIX + "-%s-%s", getName(), System.currentTimeMillis()));
         FileStore<String> fileStore = new PathNormalisingKeyFileStore(fileStoreTmpBase);
 
         Map<String, String> hostAttributes = new LinkedHashMap<String, String>(4);
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuildsPlugin.groovy b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuildsPlugin.groovy
index 104621b..bcf6d02 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuildsPlugin.groovy
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/gradle/CompareGradleBuildsPlugin.groovy
@@ -29,7 +29,7 @@ import org.gradle.api.reporting.ReportingExtension
 class CompareGradleBuildsPlugin implements Plugin<Project> {
 
     void apply(Project project) {
-        project.apply(plugin: ReportingBasePlugin)
+        project.pluginManager.apply(ReportingBasePlugin)
         ReportingExtension reportingExtension = project.extensions.findByType(ReportingExtension)
 
         project.tasks.withType(CompareGradleBuilds) { CompareGradleBuilds task ->
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 9f85e9e..5ca8c4f 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.internal.IoActions;
-import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.resource.local.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 98b320a..cd423ba 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,7 +17,7 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal;
 
 import org.gradle.api.Transformer;
-import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.resource.local.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;
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 20765cc..119f6ca 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,7 +17,7 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal;
 
 import org.gradle.api.Transformer;
-import org.gradle.internal.filestore.FileStore;
+import org.gradle.internal.resource.local.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;
@@ -34,6 +34,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import static org.apache.commons.lang.StringUtils.isEmpty;
+
 /**
  * Transforms from the Gradle specific build outcomes into source agnostic outcomes.
  */
@@ -88,7 +90,11 @@ public class GradleBuildOutcomeSetTransformer implements Transformer<Set<BuildOu
             BuildOutcome buildOutcome = new GeneratedArchiveBuildOutcome(outcome.getTaskPath(), outcome.getDescription(), resource, relativePath);
             translatedOutcomes.add(buildOutcome);
         } else {
-            translatedOutcomes.add(new UnknownBuildOutcome(outcome.getTaskPath(), outcome.getDescription()));
+            String outcomeName = outcome.getTaskPath();
+            if (isEmpty(outcomeName)) {
+                outcomeName = GFileUtils.relativePath(rootProject.getProjectDirectory(), outcome.getFile());
+            }
+            translatedOutcomes.add(new UnknownBuildOutcome(outcomeName, outcome.getDescription()));
         }
     }
 
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 2b6fc4b..ae0e1cf 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
@@ -51,4 +51,5 @@ public class GeneratedArchiveBuildOutcome extends BuildOutcomeSupport {
     public String getRootRelativePath() {
         return rootRelativePath;
     }
+
 }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparator.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparator.java
index 38e9d9c..838d02b 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparator.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparator.java
@@ -20,20 +20,23 @@ import org.gradle.api.Transformer;
 import org.gradle.api.plugins.buildcomparison.compare.internal.BuildOutcomeComparator;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.BuildOutcomeAssociation;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ArchiveEntry;
-import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ZipEntryToArchiveEntryTransformer;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ArchiveEntryComparison;
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.FileToArchiveEntrySetTransformer;
+import org.gradle.internal.Pair;
 import org.gradle.util.CollectionUtils;
 
 import java.io.File;
-import java.util.*;
+import java.util.Collections;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 public class GeneratedArchiveBuildOutcomeComparator implements BuildOutcomeComparator<GeneratedArchiveBuildOutcome, GeneratedArchiveBuildOutcomeComparisonResult> {
 
     private final Transformer<Set<ArchiveEntry>, File> archiveToEntriesTransformer;
 
     public GeneratedArchiveBuildOutcomeComparator() {
-        this(new FileToArchiveEntrySetTransformer(new ZipEntryToArchiveEntryTransformer()));
+        this(new FileToArchiveEntrySetTransformer());
     }
 
     GeneratedArchiveBuildOutcomeComparator(Transformer<Set<ArchiveEntry>, File> archiveToEntriesTransformer) {
@@ -62,28 +65,24 @@ public class GeneratedArchiveBuildOutcomeComparator implements BuildOutcomeCompa
             targetEntries = Collections.emptySet();
         }
 
-        CollectionUtils.SetDiff<ArchiveEntry> diff = CollectionUtils.diffSetsBy(sourceEntries, targetEntries, new Transformer<String, ArchiveEntry>() {
-            public String transform(ArchiveEntry entry) {
+        CollectionUtils.SetDiff<ArchiveEntry> diff = CollectionUtils.diffSetsBy(sourceEntries, targetEntries, new Transformer<ArchiveEntry.Path, ArchiveEntry>() {
+            public ArchiveEntry.Path transform(ArchiveEntry entry) {
                 return entry.getPath();
             }
         });
 
-        SortedSet<ArchiveEntryComparison> entryComparisons = new TreeSet<ArchiveEntryComparison>(new Comparator<ArchiveEntryComparison>() {
-            public int compare(ArchiveEntryComparison o1, ArchiveEntryComparison o2) {
-                return o1.getPath().compareTo(o2.getPath());
-            }
-        });
+        SortedSet<ArchiveEntryComparison> entryComparisons = new TreeSet<ArchiveEntryComparison>();
 
         for (ArchiveEntry sourceOnly : diff.leftOnly) {
-            entryComparisons.add(new ArchiveEntryComparison(sourceOnly.getPath(), sourceOnly, null));
+            entryComparisons.add(new ArchiveEntryComparison(sourceOnly, null));
         }
 
-        for (CollectionUtils.SetDiff.Pair<ArchiveEntry> pair : diff.common) {
-            entryComparisons.add(new ArchiveEntryComparison(pair.left.getPath(), pair.left, pair.right));
+        for (Pair<ArchiveEntry, ArchiveEntry> pair : diff.common) {
+            entryComparisons.add(new ArchiveEntryComparison(pair.left, pair.right));
         }
 
         for (ArchiveEntry targetOnly : diff.rightOnly) {
-            entryComparisons.add(new ArchiveEntryComparison(targetOnly.getPath(), null, targetOnly));
+            entryComparisons.add(new ArchiveEntryComparison(null, targetOnly));
         }
 
         return new GeneratedArchiveBuildOutcomeComparisonResult(association, entryComparisons);
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResult.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResult.java
index 2a8592b..1edbf7a 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResult.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResult.java
@@ -62,6 +62,6 @@ public class GeneratedArchiveBuildOutcomeComparisonResult extends BuildOutcomeCo
     }
 
     public boolean isOutcomesAreIdentical() {
-        return getComparisonResultType().equals(ComparisonResultType.EQUAL);
+        return getComparisonResultType().equals(ComparisonResultType.EQUAL) || getComparisonResultType().equals(ComparisonResultType.NON_EXISTENT);
     }
 }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer.groovy b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer.groovy
index 28de496..0f84c04 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer.groovy
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer.groovy
@@ -69,15 +69,15 @@ class GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer extends BuildOutc
         }
 
         if (result.comparisonResultType == NON_EXISTENT) {
-            resultMsg "Neither side produced the archive.", false, context
+            resultMsg "Neither side produced the archive.", result.outcomesAreIdentical, context
         } else if (result.comparisonResultType == SOURCE_ONLY) {
-            resultMsg "The archive was only produced by the source build.", false, context
+            resultMsg "The archive was only produced by the source build.", result.outcomesAreIdentical, context
         } else if (result.comparisonResultType == TARGET_ONLY) {
-            resultMsg "The archive was only produced by the target build.", false, context
+            resultMsg "The archive was only produced by the target build.", result.outcomesAreIdentical, context
         } else if (result.comparisonResultType == EQUAL) {
-            resultMsg "The archives are completely identical.", true, context
+            resultMsg "The archives are completely identical.", result.outcomesAreIdentical, context
         } else if (result.comparisonResultType == UNEQUAL) {
-            resultMsg "There are differences within the archive.", false, context
+            resultMsg "There are differences within the archive.", result.outcomesAreIdentical, context
             renderUnequal(context, result.entryComparisons)
         } else {
             result.comparisonResultType.throwUnsupported()
@@ -99,11 +99,23 @@ class GeneratedArchiveBuildOutcomeComparisonResultHtmlRenderer extends BuildOutc
                 }
 
                 entryComparisons.each { entryComparison ->
-                    if (entryComparison.comparisonResultType != EQUAL) {
-                        tr {
-                            td entryComparison.path
-                            td toDifferenceDescription(entryComparison)
-                        }
+                    if (entryComparison.comparisonResultType == EQUAL) {
+                        return
+                    }
+
+                    if ((entryComparison.comparisonResultType == SOURCE_ONLY)
+                            && entryComparisons.find { (it.comparisonResultType == SOURCE_ONLY) && (it.source.subEntries.contains(entryComparison.source)) }) {
+                        return
+                    }
+
+                    if ((entryComparison.comparisonResultType == TARGET_ONLY)
+                            && entryComparisons.find { (it.comparisonResultType == TARGET_ONLY) && (it.target.subEntries.contains(entryComparison.target)) }) {
+                        return
+                    }
+
+                    tr {
+                        td entryComparison.path
+                        td toDifferenceDescription(entryComparison)
                     }
                 }
             }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntry.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntry.java
index 917b406..add451a 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntry.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntry.java
@@ -16,43 +16,111 @@
 
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry;
 
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.gradle.util.ConfigureUtil;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
 public class ArchiveEntry {
 
-    private String path;
-    private boolean directory;
-    private long size = -1;
-    private long crc = -1;
+    public static class Path implements Comparable<Path> {
+        private final String fullPath;
+        private final ImmutableList<String> components;
 
-    public String getPath() {
-        return path;
+        public Path(ImmutableList<String> components) {
+            this.fullPath = Joiner.on("!/").join(components);
+            this.components = components;
+        }
+
+        public int compareTo(Path o) {
+            for (int i = 0; i < Math.max(components.size(), o.components.size()); ++i) {
+                boolean hasLeft = components.size() > i;
+                boolean hasRight = o.components.size() > i;
+
+                if (!hasLeft && hasRight) {
+                    return -1;
+                } else if (hasLeft && !hasRight) {
+                    return 1;
+                } else {
+                    int result = components.get(i).compareTo(o.components.get(i));
+                    if (result != 0) {
+                        return result;
+                    }
+                }
+            }
+
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Path path = (Path) o;
+
+            if (!fullPath.equals(path.fullPath)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return fullPath.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return fullPath;
+        }
     }
 
-    public void setPath(String path) {
+    private final Path path;
+    private final boolean directory;
+    private final long size;
+    private final long crc;
+    private final ImmutableSet<ArchiveEntry> subEntries;
+
+    private ArchiveEntry(Path path, boolean directory, long size, long crc, ImmutableSet<ArchiveEntry> subEntries) {
+        this.directory = directory;
+        this.size = size;
+        this.crc = crc;
+        this.subEntries = subEntries;
         this.path = path;
     }
 
-    public boolean isDirectory() {
-        return directory;
+    public static ArchiveEntry of(Map<String, ?> map) {
+        return ConfigureUtil.configureByMap(map, new Builder()).build();
     }
 
-    public void setDirectory(boolean directory) {
-        this.directory = directory;
+    public Path getPath() {
+        return path;
     }
 
-    public long getSize() {
-        return size;
+    public boolean isDirectory() {
+        return directory;
     }
 
-    public void setSize(long size) {
-        this.size = size;
+    public long getSize() {
+        return size;
     }
 
     public long getCrc() {
         return crc;
     }
 
-    public void setCrc(long crc) {
-        this.crc = crc;
+    public ImmutableSet<ArchiveEntry> getSubEntries() {
+        return subEntries;
     }
 
     @Override
@@ -66,18 +134,27 @@ public class ArchiveEntry {
 
         ArchiveEntry that = (ArchiveEntry) o;
 
-        if (crc != that.crc) {
+        if (!path.equals(that.path)) {
             return false;
         }
+
         if (directory != that.directory) {
             return false;
         }
-        if (size != that.size) {
+
+        if (!subEntries.equals(that.subEntries)) {
             return false;
         }
-        //noinspection RedundantIfStatement
-        if (path != null ? !path.equals(that.path) : that.path != null) {
-            return false;
+
+        if (subEntries.isEmpty()) {
+            if (crc != that.crc) {
+                return false;
+            }
+            if (size != that.size) {
+                return false;
+            }
+        } else {
+            return subEntries.equals(that.subEntries);
         }
 
         return true;
@@ -87,8 +164,96 @@ public class ArchiveEntry {
     public int hashCode() {
         int result = path != null ? path.hashCode() : 0;
         result = 31 * result + (directory ? 1 : 0);
-        result = 31 * result + (int) (size ^ (size >>> 32));
-        result = 31 * result + (int) (crc ^ (crc >>> 32));
+        if (subEntries.isEmpty()) {
+            result = 31 * result + (int) (size ^ (size >>> 32));
+            result = 31 * result + (int) (crc ^ (crc >>> 32));
+        }
+        result = 31 * result + subEntries.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "ArchiveEntry{"
+                + ", path='" + path + '\''
+                + ", directory=" + directory
+                + ", size=" + size
+                + ", crc=" + crc
+                + ", subEntries=" + subEntries
+                + '}';
+    }
+
+    public static class Builder {
+
+        private String path;
+        private boolean directory;
+        private long size;
+        private long crc;
+        private ImmutableSet<ArchiveEntry> subEntries = ImmutableSet.of();
+        private List<String> parentPaths = ImmutableList.of();
+
+        public String getPath() {
+            return path;
+        }
+
+        public boolean isDirectory() {
+            return directory;
+        }
+
+        public long getSize() {
+            return size;
+        }
+
+        public long getCrc() {
+            return crc;
+        }
+
+        public Collection<ArchiveEntry> getSubEntries() {
+            return subEntries;
+        }
+
+        public Builder setPath(String path) {
+            this.path = path;
+            return this;
+        }
+
+        public Builder setDirectory(boolean directory) {
+            this.directory = directory;
+            return this;
+        }
+
+        public Builder setSize(long size) {
+            this.size = size;
+            return this;
+        }
+
+        public Builder setCrc(long crc) {
+            this.crc = crc;
+            return this;
+        }
+
+        public Builder setSubEntries(ImmutableSet<ArchiveEntry> subEntries) {
+            this.subEntries = subEntries;
+            return this;
+        }
+
+        public void setParentPaths(List<String> parentPaths) {
+            this.parentPaths = parentPaths;
+        }
+
+        public ArchiveEntry build() {
+            if (path == null) {
+                throw new IllegalStateException("'path' is required");
+            }
+            if (subEntries == null) {
+                throw new IllegalStateException("'subEntries' is required");
+            }
+            if (directory && !subEntries.isEmpty()) {
+                throw new IllegalStateException("directory entry cannot have sub entries");
+            }
+
+            ImmutableList<String> parts = parentPaths.isEmpty() ? ImmutableList.of(path) : ImmutableList.<String>builder().addAll(parentPaths).add(path).build();
+            return new ArchiveEntry(new Path(parts), directory, size, crc, subEntries);
+        }
+    }
 }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparison.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparison.java
index dba6dbb..eb7af9c 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparison.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparison.java
@@ -17,19 +17,20 @@
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry;
 
 import org.gradle.api.plugins.buildcomparison.compare.internal.ComparisonResultType;
+import org.gradle.util.GUtil;
 
-public class ArchiveEntryComparison {
+public class ArchiveEntryComparison implements Comparable<ArchiveEntryComparison> {
 
-    private final String path;
+    private final ArchiveEntry.Path path;
     private final ArchiveEntry source;
     private final ArchiveEntry target;
 
-    public ArchiveEntryComparison(String path, ArchiveEntry source, ArchiveEntry target) {
+    public ArchiveEntryComparison(ArchiveEntry source, ArchiveEntry target) {
         if (source == null && target == null) {
             throw new IllegalArgumentException("Both 'from' and 'to' cannot be null");
         }
 
-        this.path = path;
+        this.path = GUtil.elvis(source, target).getPath();
         this.source = source;
         this.target = target;
     }
@@ -45,7 +46,7 @@ public class ArchiveEntryComparison {
         }
     }
 
-    public String getPath() {
+    public ArchiveEntry.Path getPath() {
         return path;
     }
 
@@ -56,4 +57,9 @@ public class ArchiveEntryComparison {
     public ArchiveEntry getTarget() {
         return target;
     }
+
+    @SuppressWarnings("NullableProblems")
+    public int compareTo(ArchiveEntryComparison o) {
+        return path.compareTo(o.path);
+    }
 }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformer.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformer.java
index dd49b0e..b1019b4 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformer.java
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformer.java
@@ -16,30 +16,20 @@
 
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import org.apache.commons.io.IOUtils;
 import org.gradle.api.Transformer;
 import org.gradle.api.UncheckedIOException;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashSet;
+import java.io.*;
 import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
 public class FileToArchiveEntrySetTransformer implements Transformer<Set<ArchiveEntry>, File> {
 
-    private final Transformer<ArchiveEntry, ZipEntry> entryTransformer;
-
-    public FileToArchiveEntrySetTransformer(Transformer<ArchiveEntry, ZipEntry> entryTransformer) {
-        this.entryTransformer = entryTransformer;
-    }
-
     public Set<ArchiveEntry> transform(File archiveFile) {
-        Set<ArchiveEntry> entries = new HashSet<ArchiveEntry>();
-
         FileInputStream fileInputStream;
         try {
             fileInputStream = new FileInputStream(archiveFile);
@@ -47,12 +37,44 @@ public class FileToArchiveEntrySetTransformer implements Transformer<Set<Archive
             throw new UncheckedIOException(e);
         }
 
-        ZipInputStream zipStream = new ZipInputStream(fileInputStream);
+        ImmutableSet.Builder<ArchiveEntry> allEntries = ImmutableSet.builder();
+        walk(fileInputStream, allEntries, ImmutableList.<String>of());
+        return allEntries.build();
+    }
+
+    private ImmutableSet<ArchiveEntry> walk(InputStream archiveInputStream, ImmutableSet.Builder<ArchiveEntry> allEntries, ImmutableList<String> parentPaths) {
+        ImmutableSet.Builder<ArchiveEntry> entries = ImmutableSet.builder();
+        ZipInputStream zipStream = new ZipInputStream(archiveInputStream);
 
         try {
             ZipEntry entry = zipStream.getNextEntry();
             while (entry != null) {
-                entries.add(entryTransformer.transform(entry));
+                ArchiveEntry.Builder builder = new ArchiveEntry.Builder();
+                builder.setParentPaths(parentPaths);
+                builder.setPath(entry.getName());
+                builder.setCrc(entry.getCrc());
+                builder.setDirectory(entry.isDirectory());
+                builder.setSize(entry.getSize());
+                if (!builder.isDirectory() && (zipStream.available() == 1)) {
+                    boolean zipEntry;
+                    final BufferedInputStream bis = new BufferedInputStream(zipStream) {
+                        @Override
+                        public void close() throws IOException {
+                        }
+                    };
+                    bis.mark(Integer.MAX_VALUE);
+                    zipEntry = new ZipInputStream(bis).getNextEntry() != null;
+                    bis.reset();
+                    if (zipEntry) {
+                        ImmutableList<String> nextParentPaths = ImmutableList.<String>builder().addAll(parentPaths).add(entry.getName()).build();
+                        ImmutableSet<ArchiveEntry> subEntries = walk(bis, allEntries, nextParentPaths);
+                        builder.setSubEntries(subEntries);
+                    }
+                }
+
+                ArchiveEntry archiveEntry = builder.build();
+                entries.add(archiveEntry);
+                allEntries.add(archiveEntry);
                 zipStream.closeEntry();
                 entry = zipStream.getNextEntry();
             }
@@ -62,7 +84,7 @@ public class FileToArchiveEntrySetTransformer implements Transformer<Set<Archive
             IOUtils.closeQuietly(zipStream);
         }
 
-        return entries;
+        return entries.build();
     }
 
 }
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformer.java b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformer.java
deleted file mode 100644
index 0dae5be..0000000
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformer.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.api.plugins.buildcomparison.outcome.internal.archive.entry;
-
-import org.gradle.api.Transformer;
-
-import java.util.zip.ZipEntry;
-
-public class ZipEntryToArchiveEntryTransformer implements Transformer<ArchiveEntry, ZipEntry> {
-
-    public ArchiveEntry transform(ZipEntry zipEntry) {
-        ArchiveEntry archiveEntry = new ArchiveEntry();
-        archiveEntry.setPath(zipEntry.getName());
-        archiveEntry.setCrc(zipEntry.getCrc());
-        archiveEntry.setDirectory(zipEntry.isDirectory());
-        archiveEntry.setSize(zipEntry.getSize());
-        return archiveEntry;
-    }
-}
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
index b853a6e..0ebf9e1 100644
--- 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
@@ -20,7 +20,11 @@ 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.api.tasks.bundling.AbstractArchiveTask;
+import org.gradle.api.tasks.bundling.Tar;
+import org.gradle.api.tasks.bundling.War;
+import org.gradle.api.tasks.bundling.Zip;
+import org.gradle.jvm.tasks.Jar;
 import org.gradle.plugins.ear.Ear;
 import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome;
 
@@ -49,7 +53,7 @@ public class PublishArtifactToFileBuildOutcomeTransformer {
     }
 
     private String getDescription(PublishArtifact artifact) {
-        return String.format("Publish artifact '%s'", artifact.toString());
+        return "Publish artifact '".concat(artifact.toString()).concat("'");
     }
 
     private String getTypeIdentifier(PublishArtifact artifact) {
diff --git a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRenderer.groovy b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRenderer.groovy
index 9611558..4593863 100644
--- a/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRenderer.groovy
+++ b/subprojects/build-comparison/src/main/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRenderer.groovy
@@ -67,8 +67,8 @@ class GradleBuildComparisonResultHtmlRenderer implements BuildComparisonResultRe
             body {
                 div("class": "text-container") {
                     renderHeading(result, context)
-                    renderOutcomeComparisons(result, context)
                     renderUncomparedOutcomes(result, context)
+                    renderOutcomeComparisons(result, context)
                 }
             }
         }
@@ -88,15 +88,16 @@ class GradleBuildComparisonResultHtmlRenderer implements BuildComparisonResultRe
                 h2 "Uncompared ${side} outcomes"
                 p "Uncompared ${side} build outcomes are outcomes that were not matched with a ${other} build outcome."
 
+                def sortedUncompareds = uncompareds.sort { it.name }
                 ol {
-                    for (uncompared in uncompareds) {
+                    for (uncompared in sortedUncompareds) {
                         li {
-                            a(href: "#${uncompared.name}", uncompared.name)
+                            a("class": context.diffClass(false), href: "#${uncompared.name}", uncompared.name)
                         }
                     }
                 }
 
-                for (uncompared in uncompareds) {
+                for (uncompared in sortedUncompareds) {
                     BuildOutcomeRenderer renderer = outcomeRenderers.getRenderer(uncompared.getClass())
 
                     if (renderer == null) {
@@ -116,24 +117,50 @@ class GradleBuildComparisonResultHtmlRenderer implements BuildComparisonResultRe
             h2 "Compared build outcomes"
             p "Compared build outcomes are outcomes that have been identified as being intended to be the same between the target and source build."
 
+            def comparisons = result.comparisons.sort { name it }
             ol {
-                for (comparison in result.comparisons) {
-                    li {
-                        // TODO: assuming that the names are unique and that they are always the same on both sides which they are in 1.2
-                        a("class": context.diffClass(comparison.outcomesAreIdentical), href: "#${name(comparison)}", name(comparison))
+                for (comparison in comparisons) {
+                    if (!comparison.outcomesAreIdentical) {
+                        li {
+                            // TODO: assuming that the names are unique and that they are always the same on both sides which they are in 1.2
+                            a("class": context.diffClass(comparison.outcomesAreIdentical), href: "#${name(comparison)}", name(comparison))
+                        }
+                    }
+                }
+                for (comparison in comparisons) {
+                    if (comparison.outcomesAreIdentical) {
+                        li {
+                            // TODO: assuming that the names are unique and that they are always the same on both sides which they are in 1.2
+                            a("class": context.diffClass(true), href: "#${name(comparison)}", name(comparison))
+                        }
                     }
                 }
             }
 
-            for (BuildOutcomeComparisonResult comparison in result.comparisons) {
-                BuildOutcomeComparisonResultRenderer renderer = comparisonRenderers.getRenderer(comparison.getClass())
+            for (BuildOutcomeComparisonResult comparison in comparisons) {
+                if (!comparison.outcomesAreIdentical) {
+                    BuildOutcomeComparisonResultRenderer renderer = comparisonRenderers.getRenderer(comparison.getClass())
 
-                if (renderer == null) {
-                    throw new IllegalArgumentException(String.format("Cannot find renderer for build outcome comparison result type: %s", comparison.getClass()))
+                    if (renderer == null) {
+                        throw new IllegalArgumentException(String.format("Cannot find renderer for build outcome comparison result type: %s", comparison.getClass()))
+                    }
+
+                    div("class": "build-outcome-comparison text-container", id: name(comparison)) {
+                        renderer.render(comparison, context)
+                    }
                 }
+            }
+            for (BuildOutcomeComparisonResult comparison in comparisons) {
+                if (comparison.outcomesAreIdentical) {
+                    BuildOutcomeComparisonResultRenderer renderer = comparisonRenderers.getRenderer(comparison.getClass())
 
-                div("class": "build-outcome-comparison text-container", id: name(comparison)) {
-                    renderer.render(comparison, context)
+                    if (renderer == null) {
+                        throw new IllegalArgumentException(String.format("Cannot find renderer for build outcome comparison result type: %s", comparison.getClass()))
+                    }
+
+                    div("class": "build-outcome-comparison text-container", id: name(comparison)) {
+                        renderer.render(comparison, context)
+                    }
                 }
             }
         }
diff --git a/subprojects/build-comparison/src/main/resources/META-INF/gradle-plugins/compare-gradle-builds.properties b/subprojects/build-comparison/src/main/resources/META-INF/gradle-plugins/org.gradle.compare-gradle-builds.properties
similarity index 100%
rename from subprojects/build-comparison/src/main/resources/META-INF/gradle-plugins/compare-gradle-builds.properties
rename to subprojects/build-comparison/src/main/resources/META-INF/gradle-plugins/org.gradle.compare-gradle-builds.properties
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/compare/internal/DefaultBuildOutcomeComparatorFactoryTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/compare/internal/DefaultBuildOutcomeComparatorFactoryTest.groovy
index 22b519b..6c34163 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/compare/internal/DefaultBuildOutcomeComparatorFactoryTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/compare/internal/DefaultBuildOutcomeComparatorFactoryTest.groovy
@@ -77,7 +77,7 @@ class DefaultBuildOutcomeComparatorFactoryTest extends Specification {
     }
 
     private static BuildOutcomeComparator toBuildOutcomeComparator(BuildOutcome buildOutcome) {
-        new BuildOutcomeComparator<>() {
+        new BuildOutcomeComparator() {
             String toString() {
                 "comparator for $buildOutcome"
             }
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 665f6ec..061bcbb 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
@@ -16,7 +16,7 @@
 
 package org.gradle.api.plugins.buildcomparison.gradle.internal
 
-import org.gradle.api.internal.filestore.PathNormalisingKeyFileStore
+import org.gradle.internal.resource.local.PathNormalisingKeyFileStore
 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
@@ -25,11 +25,13 @@ import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.model.internal.outcomes.GradleBuildOutcome
 import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome
 import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
 import static org.gradle.api.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier.*
 
+ at UsesNativeServices
 class GradleBuildOutcomeSetInferrerTest extends Specification {
 
     @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
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 b01fb24..9719668 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,7 +17,7 @@
 package org.gradle.api.plugins.buildcomparison.gradle.internal
 
 import org.gradle.api.Action
-import org.gradle.internal.filestore.FileStore
+import org.gradle.internal.resource.local.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
@@ -60,6 +60,7 @@ class GradleBuildOutcomeSetTransformerTest extends Specification {
         given:
         ProjectOutcomesBuilder builder = new ProjectOutcomesBuilder()
         ProjectOutcomes projectOutput = builder.build(dir.testDirectory) {
+            addFile "preBuilt.jar", UNKNOWN_ARTIFACT.typeIdentifier, ""
             createChild("a") {
                 addFile "a1", JAR_ARTIFACT.typeIdentifier
             }
@@ -83,8 +84,8 @@ class GradleBuildOutcomeSetTransformerTest extends Specification {
         def outcomes = transformer.transform(projectOutput).collectEntries { [it.name, it] }
 
         then:
-        outcomes.size() == 5
-        outcomes.keySet().toList().sort() == [":a:a1", ":b:b1", ":b:b2", ":c:a:ca1", ":c:a:ca2"]
+        outcomes.size() == 6
+        outcomes.keySet().toList().sort() == [":a:a1", ":b:b1", ":b:b2", ":c:a:ca1", ":c:a:ca2", "preBuilt.jar"]
         outcomes[":a:a1"] instanceof GeneratedArchiveBuildOutcome
         outcomes[":a:a1"].archiveFile.name == "a1"
         outcomes[":b:b1"] instanceof GeneratedArchiveBuildOutcome
@@ -92,6 +93,7 @@ class GradleBuildOutcomeSetTransformerTest extends Specification {
         outcomes[":b:b2"] instanceof GeneratedArchiveBuildOutcome
         outcomes[":c:a:ca1"] instanceof GeneratedArchiveBuildOutcome
         outcomes[":c:a:ca2"] instanceof UnknownBuildOutcome
+        outcomes["preBuilt.jar"] instanceof UnknownBuildOutcome
     }
 
     List<GradleBuildOutcome> allBuildOutcomes(ProjectOutcomes outcomes, List<GradleBuildOutcome> collector = []) {
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 0eaa388..e4d030c 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
@@ -16,6 +16,8 @@
 
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive
 
+import com.google.common.collect.ImmutableSet
+import com.google.common.collect.Sets
 import org.gradle.api.Transformer
 import org.gradle.api.plugins.buildcomparison.outcome.internal.DefaultBuildOutcomeAssociation
 import org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry.ArchiveEntry
@@ -29,7 +31,8 @@ import static org.gradle.api.plugins.buildcomparison.compare.internal.Comparison
 
 class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
 
-    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
 
     def transformer = Mock(Transformer)
     def comparator = new GeneratedArchiveBuildOutcomeComparator(transformer)
@@ -42,12 +45,19 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
     }
 
     ArchiveEntry entry(Map attrs) {
-        new ArchiveEntry(attrs)
+        ArchiveEntry.of(attrs)
+    }
+
+    Set<ArchiveEntry> flatten(Set<ArchiveEntry> archiveEntries) {
+        archiveEntries.collect {
+            [it] + flatten(it.subEntries)
+        }.flatten()
     }
 
     void mockEntries(File archive, ArchiveEntry... entries) {
+        def flattened = flatten(Sets.newHashSet(entries))
         interaction {
-            _ * transformer.transform(archive) >>> [entries as Set]
+            _ * transformer.transform(archive) >> flattened
         }
     }
 
@@ -59,7 +69,22 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
                 entry(path: "d1/f1", size: 10), // diff
                 entry(path: "d1/f2"), // only in from
                 entry(path: "d2/"), // only in from
-                entry(path: "f2") // only in from
+                entry(path: "f2"), // only in from
+                entry(path: "sourceSub.zip", subEntries: ImmutableSet.of( // only in from
+                        entry(parentPaths: ["sourceSub.zip"], path: "a.txt"), // only in from
+                        entry(parentPaths: ["sourceSub.zip"], path: "b/"), // only in from
+                        entry(parentPaths: ["sourceSub.zip"], path: "b/c.txt") // only in from
+                )),
+                entry(path: "sameSub.zip", subEntries: ImmutableSet.of(
+                        entry(parentPaths: ["sameSub.zip"], path: "a.txt"),
+                        entry(parentPaths: ["sameSub.zip"], path: "b/"),
+                        entry(parentPaths: ["sameSub.zip"], path: "b/c.txt")
+                )),
+                entry(path: "differentSub.zip", subEntries: ImmutableSet.of(
+                        entry(parentPaths: ["differentSub.zip"], path: "a.txt", size: 0),
+                        entry(parentPaths: ["differentSub.zip"], path: "b/"),
+                        entry(parentPaths: ["differentSub.zip"], path: "b/c.txt")
+                ))
         )
 
         and:
@@ -69,13 +94,55 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
                 entry(path: "d1/f1", size: 20),
                 entry(path: "d1/f3"), // only in to
                 entry(path: "d3/"), // only in to
-                entry(path: "f3") // only in to
+                entry(path: "f3"), // only in to
+                entry(path: "targetSub.zip", subEntries: ImmutableSet.of( // only in from
+                        entry(parentPaths: ["targetSub.zip"], path: "a.txt"), // only in from
+                        entry(parentPaths: ["targetSub.zip"], path: "b/"), // only in from
+                        entry(parentPaths: ["targetSub.zip"], path: "b/c.txt") // only in from
+                )),
+                entry(path: "sameSub.zip", subEntries: ImmutableSet.of(
+                        entry(parentPaths: ["sameSub.zip"], path: "a.txt"),
+                        entry(parentPaths: ["sameSub.zip"], path: "b/"),
+                        entry(parentPaths: ["sameSub.zip"], path: "b/c.txt")
+                )),
+                entry(path: "differentSub.zip", subEntries: ImmutableSet.of(
+                        entry(parentPaths: ["differentSub.zip"], path: "a.txt", size: 1),
+                        entry(parentPaths: ["differentSub.zip"], path: "b/"),
+                        entry(parentPaths: ["differentSub.zip"], path: "b/c.txt")
+                ))
         )
 
         then:
         def result = compare(existingFrom, existingTo)
-        result.entryComparisons*.path == ["d1/", "d1/f1", "d1/f2", "d1/f3", "d2/", "d3/", "f1", "f2", "f3"]
-        Map<String, ArchiveEntryComparison> indexed = result.entryComparisons.collectEntries { [it.path, it] }
+        result.entryComparisons*.path*.toString() == [
+                "d1/",
+                "d1/f1",
+                "d1/f2",
+                "d1/f3",
+                "d2/",
+                "d3/",
+                "differentSub.zip",
+                "differentSub.zip!/a.txt",
+                "differentSub.zip!/b/",
+                "differentSub.zip!/b/c.txt",
+                "f1",
+                "f2",
+                "f3",
+                "sameSub.zip",
+                "sameSub.zip!/a.txt",
+                "sameSub.zip!/b/",
+                "sameSub.zip!/b/c.txt",
+                "sourceSub.zip",
+                "sourceSub.zip!/a.txt",
+                "sourceSub.zip!/b/",
+                "sourceSub.zip!/b/c.txt",
+                "targetSub.zip",
+                "targetSub.zip!/a.txt",
+                "targetSub.zip!/b/",
+                "targetSub.zip!/b/c.txt"
+        ]
+
+        Map<String, ArchiveEntryComparison> indexed = result.entryComparisons.collectEntries { [it.path.toString(), it] }
 
         indexed["f1"].comparisonResultType == EQUAL
         indexed["f2"].comparisonResultType == SOURCE_ONLY
@@ -88,6 +155,26 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
 
         indexed["d2/"].comparisonResultType == SOURCE_ONLY
         indexed["d3/"].comparisonResultType == TARGET_ONLY
+
+        indexed["sourceSub.zip"].comparisonResultType == SOURCE_ONLY
+        indexed["sourceSub.zip!/a.txt"].comparisonResultType == SOURCE_ONLY
+        indexed["sourceSub.zip!/b/"].comparisonResultType == SOURCE_ONLY
+        indexed["sourceSub.zip!/b/c.txt"].comparisonResultType == SOURCE_ONLY
+
+        indexed["targetSub.zip"].comparisonResultType == TARGET_ONLY
+        indexed["targetSub.zip!/a.txt"].comparisonResultType == TARGET_ONLY
+        indexed["targetSub.zip!/b/"].comparisonResultType == TARGET_ONLY
+        indexed["targetSub.zip!/b/c.txt"].comparisonResultType == TARGET_ONLY
+
+        indexed["sameSub.zip"].comparisonResultType == EQUAL
+        indexed["sameSub.zip!/a.txt"].comparisonResultType == EQUAL
+        indexed["sameSub.zip!/b/"].comparisonResultType == EQUAL
+        indexed["sameSub.zip!/b/c.txt"].comparisonResultType == EQUAL
+
+        indexed["differentSub.zip"].comparisonResultType == UNEQUAL
+        indexed["differentSub.zip!/a.txt"].comparisonResultType == UNEQUAL
+        indexed["differentSub.zip!/b/"].comparisonResultType == EQUAL
+        indexed["differentSub.zip!/b/c.txt"].comparisonResultType == EQUAL
     }
 
 
@@ -113,7 +200,7 @@ class GeneratedArchiveBuildOutcomeComparatorTest extends Specification {
         !compare(notExistingFrom, existingTo).outcomesAreIdentical
         !compare(existingFrom, notExistingTo).outcomesAreIdentical
         !compare(existingFrom, unequal).outcomesAreIdentical
-        !compare(notExistingFrom, notExistingTo).outcomesAreIdentical
+        compare(notExistingFrom, notExistingTo).outcomesAreIdentical
     }
 
     protected GeneratedArchiveBuildOutcomeComparisonResult compare(from, to) {
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparisonTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparisonTest.groovy
index a2e5dc3..3201563 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparisonTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryComparisonTest.groovy
@@ -22,24 +22,23 @@ import spock.lang.Specification
 class ArchiveEntryComparisonTest extends Specification {
 
     ArchiveEntry entry(Map attrs) {
-        new ArchiveEntry(attrs)
+        ArchiveEntry.of(attrs)
     }
 
-    def path
+    String path = "path"
     def from = entry(path: path, size: 10)
     def to = entry(path: path, size: 10)
 
-    ArchiveEntryComparison comparison(String path = path, ArchiveEntry from = from, ArchiveEntry to = to) {
-        new ArchiveEntryComparison(path, from, to)
+    ArchiveEntryComparison comparison() {
+        new ArchiveEntryComparison(from, to)
     }
 
-
     def "comparisons"() {
         expect:
         comparison().comparisonResultType == ComparisonResultType.EQUAL
 
         when:
-        from.size += 1
+        from = entry(path: path, size: 11)
 
         then:
         comparison().comparisonResultType == ComparisonResultType.UNEQUAL
@@ -58,7 +57,7 @@ class ArchiveEntryComparisonTest extends Specification {
         comparison().comparisonResultType == ComparisonResultType.SOURCE_ONLY
     }
 
-    def "from or to must be null"() {
+    def "from or to must be non null"() {
         given:
         from = null
         to = null
@@ -69,4 +68,5 @@ class ArchiveEntryComparisonTest extends Specification {
         then:
         thrown IllegalArgumentException
     }
+
 }
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryTest.groovy
index 74b5717..ae2b251 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ArchiveEntryTest.groovy
@@ -16,24 +16,23 @@
 
 package org.gradle.api.plugins.buildcomparison.outcome.internal.archive.entry
 
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableSet
 import spock.lang.Specification
 
 class ArchiveEntryTest extends Specification {
 
     def "equals and hash code"() {
         when:
-        def a1 = new ArchiveEntry(
-                path: "foo",
-                size: 10,
-                crc: 10,
-                directory: true
-        )
-        def a2 = new ArchiveEntry(
-                path: "foo",
-                size: 10,
-                crc: 10,
-                directory: true
-        )
+        def props = [
+                path     : "foo",
+                size     : 10,
+                crc      : 10,
+                directory: false
+        ]
+
+        def a1 = ArchiveEntry.of(props)
+        def a2 = ArchiveEntry.of(props)
 
         then:
         a1 == a2
@@ -41,11 +40,87 @@ class ArchiveEntryTest extends Specification {
         a1.hashCode() == a2.hashCode()
 
         when:
-        a2.size = 20
+        a2 = ArchiveEntry.of(props + [size: 20])
+
+        then:
+        a1 != a2
+        a2 != a1
+        a1.hashCode() != a2.hashCode()
+
+        when:
+        def a3 = ArchiveEntry.of(props)
+        a1 = ArchiveEntry.of(props + [subEntries: ImmutableSet.of(a3)])
+
+        then:
+        a1 != a2
+        a2 != a1
+        a1.hashCode() != a2.hashCode()
+
+        when:
+        a2 = ArchiveEntry.of(props + [subEntries: ImmutableSet.of(a3)])
+
+        then:
+        a1 == a2
+        a2 == a1
+        a1.hashCode() == a2.hashCode()
+
+        when:
+        def a4 = ArchiveEntry.of(props + [size: 20])
+        a1 = ArchiveEntry.of(props + [subEntries: ImmutableSet.of(a4)])
+
+        then:
+        a1 != a2
+        a2 != a1
+        a1.hashCode() != a2.hashCode()
+
+        when:
+        a1 = ArchiveEntry.of(props)
 
         then:
         a1 != a2
         a2 != a1
         a1.hashCode() != a2.hashCode()
+
+        when:
+        a1 = ArchiveEntry.of(props + [crc: 20])
+
+        then:
+        a1 != a2
+        a2 != a1
+        a1.hashCode() != a2.hashCode()
+    }
+
+    def "path ordering"() {
+        expect:
+        path("a") == path("a")
+        path("a") < path("a", "a")
+        path("z") > path("a", "a")
+        path("a", "a") < path("a", "b")
+    }
+
+    def "paths are case sensitive"() {
+        when:
+        def a1Props = [
+                path     : "foo",
+                size     : 10,
+                crc      : 10,
+                directory: false
+        ]
+        def a2Props = [
+                path     : "Foo",
+                size     : 10,
+                crc      : 10,
+                directory: false
+        ]
+
+        def a1 = ArchiveEntry.of(a1Props)
+        def a2 = ArchiveEntry.of(a2Props)
+
+        then:
+        a1.path != a2.path
+    }
+
+    static ArchiveEntry.Path path(String... components) {
+        new ArchiveEntry.Path(ImmutableList.copyOf(components))
     }
 }
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformerTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformerTest.groovy
index e34830e..bc987b6 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformerTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/FileToArchiveEntrySetTransformerTest.groovy
@@ -23,20 +23,33 @@ import spock.lang.Specification
 
 class FileToArchiveEntrySetTransformerTest extends Specification {
 
-    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
     Set<ArchiveEntry> entrySet
 
-    def transformer = new FileToArchiveEntrySetTransformer(new ZipEntryToArchiveEntryTransformer())
+    def transformer = new FileToArchiveEntrySetTransformer()
 
     TestFile contents
     TestFile zip
+    TestFile subZipContents
+    TestFile subZip
+    TestFile subSubZipContents
+    TestFile subSubZip
 
     def setup() {
         contents = dir.createDir("contents")
         zip = dir.file("zip.zip")
+        subZipContents = dir.createDir("subZipContents")
+        subZip = dir.file("contents/sub.zip")
+        subSubZipContents = dir.createDir("subSubZipContents")
+        subSubZip = dir.file("subZipContents/subSub.zip")
     }
 
-    def createZip(Closure c) {
+    def createZip(TestFile contents, TestFile zip) {
+        createZip(contents, zip) {}
+    }
+
+    def createZip(TestFile contents, TestFile zip, @DelegatesTo(TestFile) Closure c) {
         contents.with(c)
         contents.zipTo(zip)
         entrySet = transformer.transform(zip)
@@ -44,7 +57,7 @@ class FileToArchiveEntrySetTransformerTest extends Specification {
 
     def "can handle zip file"() {
         when:
-        createZip {
+        createZip(contents, zip) {
             createFile("f1.txt") << "f1"
             createFile("dir1/f2.txt") << "f2"
             createFile("dir1/f3.txt") << "f3"
@@ -53,14 +66,61 @@ class FileToArchiveEntrySetTransformerTest extends Specification {
 
         then:
         entrySet.size() == 6
-        entrySet*.path.sort() == ["dir1/", "dir1/f2.txt", "dir1/f3.txt", "dir2/", "dir2/f4.txt", "f1.txt"]
+        entrySet*.path.sort()*.toString() == ["dir1/", "dir1/f2.txt", "dir1/f3.txt", "dir2/", "dir2/f4.txt", "f1.txt"]
     }
 
-    def "can handle empty zip"() {
+    def "can handle empty zip file"() {
         when:
-        createZip { }
+        createZip(contents, zip)
 
         then:
         entrySet.empty
     }
+
+    def "can handle recursive zip file"() {
+        when:
+        createZip(contents, zip) {
+            createFile("f1.txt") << "f1"
+            createFile("dir1/f2.txt") << "f2"
+            createFile("dir1/f3.txt") << "f3"
+            createFile("dir2/f4.txt") << "f3"
+            createZip(subZipContents, subZip) {
+                createFile("g1.txt") << "g1"
+                createFile("dir1/g2.txt") << "g2"
+                createFile("dir1/g3.txt") << "g3"
+                createFile("dir2/g4.txt") << "g3"
+                createZip(subSubZipContents, subSubZip) {
+                    createFile("h1.txt") << "h1"
+                    createFile("dir1/h2.txt") << "h2"
+                    createFile("dir1/h3.txt") << "h3"
+                    createFile("dir2/h4.txt") << "h3"
+                }
+            }
+        }
+
+        then:
+        entrySet.size() == 20
+        entrySet*.path.sort()*.toString() == [
+                "dir1/",
+                "dir1/f2.txt",
+                "dir1/f3.txt",
+                "dir2/",
+                "dir2/f4.txt",
+                "f1.txt",
+                "sub.zip",
+                "sub.zip!/dir1/",
+                "sub.zip!/dir1/g2.txt",
+                "sub.zip!/dir1/g3.txt",
+                "sub.zip!/dir2/",
+                "sub.zip!/dir2/g4.txt",
+                "sub.zip!/g1.txt",
+                "sub.zip!/subSub.zip",
+                "sub.zip!/subSub.zip!/dir1/",
+                "sub.zip!/subSub.zip!/dir1/h2.txt",
+                "sub.zip!/subSub.zip!/dir1/h3.txt",
+                "sub.zip!/subSub.zip!/dir2/",
+                "sub.zip!/subSub.zip!/dir2/h4.txt",
+                "sub.zip!/subSub.zip!/h1.txt"
+        ]
+    }
 }
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformerTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformerTest.groovy
deleted file mode 100644
index d63b62e..0000000
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/outcome/internal/archive/entry/ZipEntryToArchiveEntryTransformerTest.groovy
+++ /dev/null
@@ -1,63 +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.plugins.buildcomparison.outcome.internal.archive.entry
-
-import spock.lang.Specification
-
-import java.util.zip.ZipEntry
-
-class ZipEntryToArchiveEntryTransformerTest extends Specification {
-
-    def transformer = new ZipEntryToArchiveEntryTransformer()
-
-    def "transforms file"() {
-        given:
-        def zip = zipEntry("abc") {
-            size = 10
-            crc = 10
-        }
-
-        when:
-        def archiveEntry = transformer.transform(zip)
-
-        then:
-        archiveEntry.path == "abc"
-        archiveEntry.crc == 10
-        !archiveEntry.directory
-        archiveEntry.size == 10
-    }
-
-    def "transforms directory"() {
-        given:
-        def zip = zipEntry("abc/") { }
-
-        when:
-        def archiveEntry = transformer.transform(zip)
-
-        then:
-        archiveEntry.path == "abc/"
-        archiveEntry.crc == -1
-        archiveEntry.directory
-        archiveEntry.size == -1
-    }
-
-    ZipEntry zipEntry(String name, Closure c) {
-        def ze = new ZipEntry(name)
-        ze.with(c)
-        ze
-    }
-}
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
index ad74474..37ade29 100644
--- 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
@@ -16,20 +16,24 @@
 
 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.plugins.buildcomparison.outcome.internal.FileOutcomeIdentifier
 import org.gradle.api.tasks.TaskDependency
+import org.gradle.api.tasks.bundling.AbstractArchiveTask
+import org.gradle.api.tasks.bundling.Tar
+import org.gradle.api.tasks.bundling.War
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.jvm.tasks.Jar
 import org.gradle.plugins.ear.Ear
 import org.gradle.tooling.model.internal.outcomes.GradleFileBuildOutcome
+import org.gradle.util.TestUtil
 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 {
 
diff --git a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRendererTest.groovy b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRendererTest.groovy
index e0b2180..adefc7e 100644
--- a/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRendererTest.groovy
+++ b/subprojects/build-comparison/src/test/groovy/org/gradle/api/plugins/buildcomparison/render/internal/html/GradleBuildComparisonResultHtmlRendererTest.groovy
@@ -119,9 +119,74 @@ class GradleBuildComparisonResultHtmlRendererTest extends Specification {
         tables.size() == 3
         tables[0].select("th").text() == "Source Target Distance"
         tables[0].select("td")[0].text() == "a"
-        tables[2].select("td")[2].text() == comparisons.last.distance.toString()
+        tables[1].select("td")[2].text() == comparisons.last.distance.toString()
         html.select(".build-outcome.source").find { it.id() == "foo" }
         html.select(".build-outcome.target").find { it.id() == "bar" }
     }
 
+    def "sort buildcomparison report by name"() {
+        given:
+        comparisonRenderers.registerRenderer(new StringBuildOutcomeComparisonResultHtmlRenderer())
+        outcomeRenderers.registerRenderer(new StringBuildOutcomeHtmlRenderer())
+
+        and:
+        comparisons << strcmp("a", "a")
+        comparisons << strcmp("c", "a")
+        comparisons << strcmp("b", "a")
+
+        and:
+        unassociatedFrom << str("ufa")
+        unassociatedFrom << str("ufc")
+        unassociatedFrom << str("ufb")
+
+        and:
+        unassociatedTo << str("uta")
+        unassociatedTo << str("utc")
+        unassociatedTo << str("utb")
+
+        when:
+        def html = render()
+
+        then:
+        def tables = html.select(".build-outcome-comparison table")
+        tables[0].select("td")[0].text() == "b"
+        tables[1].select("td")[0].text() == "c"
+        tables[2].select("td")[0].text() == "a"
+
+        and:
+        def uncomparedFroms = html.select(".build-outcome.source p")
+        uncomparedFroms[0].text() == "ufa"
+        uncomparedFroms[1].text() == "ufb"
+        uncomparedFroms[2].text() == "ufc"
+
+        and:
+        def uncomparedTos = html.select(".build-outcome.target p")
+        uncomparedTos[0].text() == "uta"
+        uncomparedTos[1].text() == "utb"
+        uncomparedTos[2].text() == "utc"
+    }
+
+    def "show differences first in the buildcomparison report"() {
+        given:
+        comparisonRenderers.registerRenderer(new StringBuildOutcomeComparisonResultHtmlRenderer())
+        outcomeRenderers.registerRenderer(new StringBuildOutcomeHtmlRenderer())
+
+        and:
+        comparisons << strcmp("a", "a")
+        comparisons << strcmp("c", "a")
+        comparisons << strcmp("b", "a")
+        comparisons << strcmp("e", "e")
+        comparisons << strcmp("d", "d")
+
+        when:
+        def html = render()
+
+        then:
+        def tables = html.select(".build-outcome-comparison table")
+        tables[0].select("td")[0].text() == "b"
+        tables[1].select("td")[0].text() == "c"
+        tables[2].select("td")[0].text() == "a"
+        tables[3].select("td")[0].text() == "d"
+        tables[4].select("td")[0].text() == "e"
+    }
 }
diff --git a/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/fixtures/MutableProjectOutcomes.groovy b/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/fixtures/MutableProjectOutcomes.groovy
index c1b35bf..c1fe2a2 100644
--- a/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/fixtures/MutableProjectOutcomes.groovy
+++ b/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/fixtures/MutableProjectOutcomes.groovy
@@ -59,7 +59,7 @@ class MutableProjectOutcomes implements ProjectOutcomes {
             }
 
             String getTaskPath() {
-                "$path:$taskName"
+                taskName ? "$path:$taskName" : ''
             }
 
             String getTypeIdentifier() {
@@ -69,4 +69,4 @@ class MutableProjectOutcomes implements ProjectOutcomes {
         outcomes << outcome
         outcome
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/outcome/string/StringBuildOutcomeComparisonResult.groovy b/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/outcome/string/StringBuildOutcomeComparisonResult.groovy
index 7b93d79..ecfff42 100644
--- a/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/outcome/string/StringBuildOutcomeComparisonResult.groovy
+++ b/subprojects/build-comparison/src/testFixtures/groovy/org/gradle/api/plugins/buildcomparison/outcome/string/StringBuildOutcomeComparisonResult.groovy
@@ -32,6 +32,6 @@ class StringBuildOutcomeComparisonResult extends BuildOutcomeComparisonResultSup
     }
 
     boolean isOutcomesAreIdentical() {
-        distance != 0
+        distance == 0
     }
 }
diff --git a/subprojects/build-init/build-init.gradle b/subprojects/build-init/build-init.gradle
index 045f462..76885b8 100644
--- a/subprojects/build-init/build-init.gradle
+++ b/subprojects/build-init/build-init.gradle
@@ -24,11 +24,11 @@ dependencies {
 
 dependencies {
     components {
-        eachComponent { ComponentMetadataDetails details ->
+        all { ComponentMetadataDetails details ->
             def version = details.id.version
             if(version.matches("(\\d\\.?)+")){
                 details.status = "release"
-            }else{
+            } else {
                 details.status = "integration"
             }
             details.statusScheme = ["integration", "release"]
@@ -66,6 +66,9 @@ class GenerateVersionProperties extends DefaultTask {
         findLatest('scala-xml', "org.scala-lang.modules:scala-xml_${versionProperties.scala}:latest.release", versionProperties)
         findLatest('groovy', 'org.codehaus.groovy:groovy:latest.release', versionProperties)
         findLatest('junit', 'junit:junit:latest.release', versionProperties)
+        findLatest('slf4j', 'org.slf4j:slf4j-api:latest.release', versionProperties)
+        def groovyVersion = VersionNumber.parse(versionProperties['groovy'])
+        versionProperties.put('spock', "1.0-groovy-${groovyVersion.major}.${groovyVersion.minor}" as String)
 
         outputFile.withOutputStream { outputStream ->
             versionProperties.store(outputStream, "Version values used in build-init templates")
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
index e0b253f..e80dc14 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
@@ -34,11 +34,6 @@ class BuildInitPluginIntegrationTest extends WellBehavedPluginTest {
         return "init"
     }
 
-    @Override
-    String getPluginId() {
-        "build-init"
-    }
-
     def "init shows up on tasks overview "() {
         when:
         run 'tasks'
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
index ced4e04..d641607 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
@@ -21,7 +21,6 @@ import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TestExecutionResult
 
-
 class GroovyLibraryInitIntegrationTest extends AbstractIntegrationSpec {
 
     public static final String SAMPLE_LIBRARY_CLASS = "src/main/groovy/Library.groovy"
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
index 6d2561a..8b42209 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
@@ -22,9 +22,9 @@ import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.maven.M2Installation
-import org.gradle.test.fixtures.maven.MavenHttpModule
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.maven.PomHttpArtifact
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.PomHttpArtifact
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.gradle.util.SetSystemProperties
 import org.junit.Rule
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
index 6a00911..7b8d430 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
@@ -20,10 +20,7 @@ import org.gradle.buildinit.plugins.fixtures.WrapperTestFixture
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TestExecutionResult
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
- at Requires(TestPrecondition.JDK6_OR_LATER)
 class ScalaLibraryInitIntegrationTest extends AbstractIntegrationSpec {
 
     public static final String SAMPLE_LIBRARY_CLASS = "src/main/scala/Library.scala"
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java b/subprojects/build-init/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java
index 6340677..a37dffa 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/api/tasks/wrapper/Wrapper.java
@@ -21,6 +21,7 @@ import org.gradle.api.GradleException;
 import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.plugins.StartScriptGenerator;
+import org.gradle.api.internal.tasks.options.Option;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
@@ -29,6 +30,7 @@ import org.gradle.wrapper.GradleWrapperMain;
 import org.gradle.wrapper.Install;
 import org.gradle.wrapper.WrapperExecutor;
 
+import javax.inject.Inject;
 import java.io.File;
 import java.net.URL;
 import java.util.Properties;
@@ -84,11 +86,16 @@ public class Wrapper extends DefaultTask {
         gradleVersion = GradleVersion.current();
     }
 
+    @Inject
+    protected FileLookup getFileLookup() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     void generate() {
         File jarFileDestination = getJarFile();
         File unixScript = getScriptFile();
-        FileResolver resolver = getServices().get(FileLookup.class).getFileResolver(unixScript.getParentFile());
+        FileResolver resolver = getFileLookup().getFileResolver(unixScript.getParentFile());
         String jarFileRelativePath = resolver.resolveAsRelativePath(jarFileDestination);
 
         writeProperties(getPropertiesFile());
@@ -197,6 +204,7 @@ public class Wrapper extends DefaultTask {
      * The version of the gradle distribution required by the wrapper. This is usually the same version of Gradle you
      * use for building your project.
      */
+    @Option(option = "gradle-version", description = "The version of the Gradle distribution required by the wrapper.")
     public void setGradleVersion(String gradleVersion) {
         this.gradleVersion = GradleVersion.version(gradleVersion);
     }
@@ -224,6 +232,7 @@ public class Wrapper extends DefaultTask {
         }
     }
 
+    @Option(option = "gradle-distribution-url", description = "The URL to download the gradle distribution from.")
     public void setDistributionUrl(String url) {
         this.distributionUrl = url;
     }
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.groovy b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.groovy
deleted file mode 100644
index 0120c8e..0000000
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.groovy
+++ /dev/null
@@ -1,62 +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.buildinit.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.buildinit.tasks.InitBuild
-
- at Incubating
-class BuildInitPlugin implements Plugin<Project> {
-    public static final String INIT_BUILD_TASK_NAME = "init"
-    public static final String GROUP = 'Build Setup'
-
-    void apply(Project project) {
-        Task init = project.tasks.create(INIT_BUILD_TASK_NAME, InitBuild)
-        init.group = GROUP
-        init.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
-        }
-        init.onlyIf {
-            def skippedMsg = setupCanBeSkipped()
-            if (skippedMsg) {
-                project.logger.warn skippedMsg
-                return false
-            }
-            return true
-        }
-
-        if (!setupCanBeSkipped()) {
-            init.dependsOn("wrapper")
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.java
new file mode 100644
index 0000000..1cb5299
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/BuildInitPlugin.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildinit.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.buildinit.tasks.internal.TaskConfiguration;
+
+/**
+ * The build init plugin.
+ */
+ at Incubating
+public class BuildInitPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        TaskConfiguration.createInitTask(project);
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.groovy b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.groovy
deleted file mode 100644
index 46e6ea7..0000000
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.groovy
+++ /dev/null
@@ -1,34 +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.buildinit.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 = BuildInitPlugin.GROUP
-        wrapper.description = "Generates Gradle wrapper files. [incubating]"
-    }
-}
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.java
new file mode 100644
index 0000000..04ae639
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/WrapperPlugin.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildinit.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.buildinit.tasks.internal.TaskConfiguration;
+
+/**
+ * The wrapper plugin.
+ */
+ at Incubating
+public class WrapperPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        TaskConfiguration.createWrapperTask(project);
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BasicTemplateBasedProjectInitDescriptor.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BasicTemplateBasedProjectInitDescriptor.java
index 50405d4..3ff2be8 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BasicTemplateBasedProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BasicTemplateBasedProjectInitDescriptor.java
@@ -19,12 +19,14 @@ package org.gradle.buildinit.plugins.internal;
 import org.gradle.util.GUtil;
 
 public class BasicTemplateBasedProjectInitDescriptor extends TemplateBasedProjectInitDescriptor{
-    public BasicTemplateBasedProjectInitDescriptor(TemplateOperationFactory templateOperationFactory , TemplateOperation settingsTemplateOperation) {
+    public BasicTemplateBasedProjectInitDescriptor(TemplateOperationFactory templateOperationFactory, TemplateLibraryVersionProvider libraryVersionProvider, TemplateOperation settingsTemplateOperation) {
         register(settingsTemplateOperation);
         register(templateOperationFactory.newTemplateOperation()
                         .withTemplate("build.gradle.template")
                         .withTarget("build.gradle")
                         .withDocumentationBindings(GUtil.map("ref_userguide_java_tutorial", "tutorial_java_projects"))
+                        .withBindings(GUtil.map("slf4jVersion", libraryVersionProvider.getVersion("slf4j")))
+                        .withBindings(GUtil.map("junitVersion", libraryVersionProvider.getVersion("junit")))
                         .create()
         );
     }
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyAction.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyAction.java
deleted file mode 100644
index b4eada8..0000000
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyAction.java
+++ /dev/null
@@ -1,33 +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.buildinit.plugins.internal;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.configuration.project.ProjectConfigureAction;
-
-public class BuildInitAutoApplyAction implements ProjectConfigureAction {
-
-    public void execute(final ProjectInternal projectInternal) {
-        if (projectInternal.getParent() == null) {
-            projectInternal.getTasks().addPlaceholderAction("init", new Runnable() {
-                public void run() {
-                    projectInternal.getPlugins().apply("build-init");
-                }
-            });
-        }
-    }
-}
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitServices.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitServices.java
index 03d1db9..e646b1a 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitServices.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/BuildInitServices.java
@@ -32,6 +32,9 @@ public class BuildInitServices implements PluginServiceRegistry {
     public void registerBuildServices(ServiceRegistration registration) {
     }
 
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
     public void registerProjectServices(ServiceRegistration registration) {
         registration.addProvider(new ProjectScopeBuildInitServices());
     }
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/GroovyLibraryProjectInitDescriptor.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/GroovyLibraryProjectInitDescriptor.java
index 67ff13d..5edc7d5 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/GroovyLibraryProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/GroovyLibraryProjectInitDescriptor.java
@@ -32,6 +32,7 @@ public class GroovyLibraryProjectInitDescriptor extends LanguageLibraryProjectIn
                 .withDocumentationBindings(GUtil.map("ref_userguide_groovy_tutorial", "tutorial_groovy_projects"))
                 .withBindings(GUtil.map("groovyVersion", libraryVersionProvider.getVersion("groovy")))
                 .withBindings(GUtil.map("junitVersion", libraryVersionProvider.getVersion("junit")))
+                .withBindings(GUtil.map("spockVersion", libraryVersionProvider.getVersion("spock")))
                 .create()
         );
 
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/JavaLibraryProjectInitDescriptor.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/JavaLibraryProjectInitDescriptor.java
index 1206244..8ea497c 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/JavaLibraryProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/JavaLibraryProjectInitDescriptor.java
@@ -34,6 +34,7 @@ public class JavaLibraryProjectInitDescriptor extends LanguageLibraryProjectInit
                 .withTarget("build.gradle")
                 .withDocumentationBindings(GUtil.map("ref_userguide_java_tutorial", "tutorial_java_projects"))
                 .withBindings(GUtil.map("junitVersion", libraryVersionProvider.getVersion("junit")))
+                .withBindings(GUtil.map("slf4jVersion", libraryVersionProvider.getVersion("slf4j")))
                 .create()
         );
 
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy
index f310ed9..ee78315 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/ProjectLayoutSetupRegistryFactory.groovy
@@ -45,9 +45,7 @@ class ProjectLayoutSetupRegistryFactory {
         registry.add(BuildInitTypeIds.GROOVY_LIBRARY,
                         new GroovyLibraryProjectInitDescriptor(templateOperationBuilder, fileResolver, libraryVersionProvider, simpleGlobalFilesBuildSettingsDescriptor));
 
-        TemplateOperation basicBuildFile  =
-
-        registry.add(BuildInitTypeIds.BASIC, new BasicTemplateBasedProjectInitDescriptor(templateOperationBuilder, simpleGlobalFilesBuildSettingsDescriptor));
+        registry.add(BuildInitTypeIds.BASIC, new BasicTemplateBasedProjectInitDescriptor(templateOperationBuilder, libraryVersionProvider, simpleGlobalFilesBuildSettingsDescriptor));
         registry.add(BuildInitTypeIds.POM, new PomProjectInitDescriptor(fileResolver, mavenSettingsProvider))
         return registry
     }
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyAction.groovy b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyAction.groovy
deleted file mode 100644
index 818124a..0000000
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyAction.groovy
+++ /dev/null
@@ -1,32 +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.buildinit.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-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyAction.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyAction.java
new file mode 100644
index 0000000..12f00d1
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyAction.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.buildinit.plugins.internal.action;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.buildinit.tasks.internal.TaskConfiguration;
+import org.gradle.configuration.project.ProjectConfigureAction;
+
+public class BuildInitAutoApplyAction implements ProjectConfigureAction {
+
+    public void execute(final ProjectInternal projectInternal) {
+        TaskConfiguration.addInitPlaceholder(projectInternal);
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/WrapperPluginAutoApplyAction.groovy b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/WrapperPluginAutoApplyAction.groovy
new file mode 100644
index 0000000..5b84601
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/action/WrapperPluginAutoApplyAction.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildinit.plugins.internal.action
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.buildinit.tasks.internal.TaskConfiguration
+import org.gradle.configuration.project.ProjectConfigureAction
+
+public class WrapperPluginAutoApplyAction implements ProjectConfigureAction {
+
+    public void execute(final ProjectInternal projectInternal) {
+        TaskConfiguration.addWrapperPlaceholder(projectInternal);
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreator.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreator.java
index ddd4924..842cbdf 100644
--- a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreator.java
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreator.java
@@ -64,7 +64,7 @@ public class MavenProjectsCreator {
         ProjectBuilder builder = container.lookup(ProjectBuilder.class);
         MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest();
         final Properties properties = new Properties();
-        properties.putAll(SystemProperties.asMap());
+        properties.putAll(SystemProperties.getInstance().asMap());
         executionRequest.setSystemProperties(properties);
         MavenExecutionRequestPopulator populator = container.lookup(MavenExecutionRequestPopulator.class);
         populator.populateFromSettings(executionRequest, settings);
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/package-info.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/package-info.java
new file mode 100644
index 0000000..522e1c7
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Build init plugins.
+ */
+package org.gradle.buildinit.plugins;
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/groovy/org/gradle/buildinit/tasks/internal/TaskConfiguration.java b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/tasks/internal/TaskConfiguration.java
new file mode 100644
index 0000000..662ab07
--- /dev/null
+++ b/subprojects/build-init/src/main/groovy/org/gradle/buildinit/tasks/internal/TaskConfiguration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.buildinit.tasks.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.wrapper.Wrapper;
+import org.gradle.buildinit.tasks.InitBuild;
+
+import java.io.File;
+import java.util.concurrent.Callable;
+
+public class TaskConfiguration {
+
+    public static final String INIT_BUILD_TASK_NAME = "init";
+    public static final String GROUP = "Build Setup";
+
+    public static void configureInit(final InitBuild init) {
+        init.setGroup(GROUP);
+        init.setDescription("Initializes a new Gradle build. [incubating]");
+        final Transformer<String, Project> setupCanBeSkipped = new Transformer<String, Project>() {
+
+            @Override
+            public String transform(Project project) {
+                if (project.file("build.gradle").exists()) {
+                    return "The build file 'build.gradle' already exists. Skipping build initialization.";
+                }
+
+                File buildFile = project.getBuildFile();
+                if (buildFile != null && buildFile.exists()) {
+                    return "The build file \'" + buildFile.getName() + "\' already exists. Skipping build initialization.";
+                }
+
+                if (project.file("settings.gradle").exists()) {
+                    return "The settings file 'settings.gradle' already exists. Skipping build initialization.";
+                }
+
+                if (project.getSubprojects().size() > 0) {
+                    return "This Gradle project appears to be part of an existing multi-project Gradle build. Skipping build initialization.";
+                }
+
+                return null;
+            }
+        };
+        init.onlyIf(new Spec<Task>() {
+            @Override
+            public boolean isSatisfiedBy(Task element) {
+                Object skippedMsg = setupCanBeSkipped.transform(element.getProject());
+                if (skippedMsg != null) {
+                    element.getProject().getLogger().warn((String) skippedMsg);
+                    return false;
+                }
+
+                return true;
+            }
+        });
+
+        init.dependsOn(new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                if (setupCanBeSkipped.transform(init.getProject()) == null) {
+                    return "wrapper";
+                } else {
+                    return null;
+                }
+            }
+        });
+    }
+
+    public static void configureWrapper(Wrapper wrapper) {
+        wrapper.setGroup(GROUP);
+        wrapper.setDescription("Generates Gradle wrapper files. [incubating]");
+    }
+
+    public static void createInitTask(Project project) {
+        configureInit(project.getTasks().create(INIT_BUILD_TASK_NAME, InitBuild.class));
+    }
+
+    public static void createWrapperTask(Project project) {
+        configureWrapper(project.getTasks().create("wrapper", Wrapper.class));
+    }
+
+    public static void addInitPlaceholder(final ProjectInternal projectInternal) {
+        if (projectInternal.getParent() == null) {
+            projectInternal.getTasks().addPlaceholderAction("init", InitBuild.class, new InitBuildAction());
+        }
+    }
+
+    public static void addWrapperPlaceholder(ProjectInternal projectInternal) {
+        if (projectInternal.getParent() == null) {
+            projectInternal.getTasks().addPlaceholderAction("wrapper", Wrapper.class, new WrapperAction());
+        }
+    }
+
+    private static class InitBuildAction implements Action<InitBuild> {
+        @Override
+        public void execute(InitBuild initBuild) {
+            configureInit(initBuild);
+        }
+    }
+
+    private static class WrapperAction implements Action<Wrapper> {
+        @Override
+        public void execute(Wrapper wrapper) {
+            configureWrapper(wrapper);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/build-init/src/main/resources/META-INF/gradle-plugins/build-init.properties b/subprojects/build-init/src/main/resources/META-INF/gradle-plugins/org.gradle.build-init.properties
similarity index 100%
rename from subprojects/build-init/src/main/resources/META-INF/gradle-plugins/build-init.properties
rename to subprojects/build-init/src/main/resources/META-INF/gradle-plugins/org.gradle.build-init.properties
diff --git a/subprojects/build-init/src/main/resources/META-INF/gradle-plugins/wrapper.properties b/subprojects/build-init/src/main/resources/META-INF/gradle-plugins/org.gradle.wrapper.properties
similarity index 100%
rename from subprojects/build-init/src/main/resources/META-INF/gradle-plugins/wrapper.properties
rename to subprojects/build-init/src/main/resources/META-INF/gradle-plugins/org.gradle.wrapper.properties
diff --git a/subprojects/build-init/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction b/subprojects/build-init/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
index 2ebf5cd..bc2637e 100644
--- a/subprojects/build-init/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
+++ b/subprojects/build-init/src/main/resources/META-INF/services/org.gradle.configuration.project.ProjectConfigureAction
@@ -1,2 +1,2 @@
-org.gradle.buildinit.plugins.internal.BuildInitAutoApplyAction
-org.gradle.buildinit.plugins.internal.WrapperPluginAutoApplyAction
+org.gradle.buildinit.plugins.internal.action.BuildInitAutoApplyAction
+org.gradle.buildinit.plugins.internal.action.WrapperPluginAutoApplyAction
diff --git a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/build.gradle.template b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/build.gradle.template
index bd238ce..18aef15 100644
--- a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/build.gradle.template
+++ b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/build.gradle.template
@@ -13,20 +13,20 @@ apply plugin: 'java'
 
 // In this section you declare where to find the dependencies of your project
 repositories {
-    // Use 'maven central' for resolving your dependencies.
+    // Use 'jcenter' for resolving your dependencies.
     // You can declare any Maven/Ivy/file repository here.
-    mavenCentral()
+    jcenter()
 }
 
 // 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'
+    compile 'org.slf4j:slf4j-api:${slf4jVersion.groovyString}'
 
     // 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"
+    testCompile 'junit:junit:${junitVersion.groovyString}'
 }
 */
diff --git a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/groovylibrary/build.gradle.template b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/groovylibrary/build.gradle.template
index 863b94b..1c943bc 100644
--- a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/groovylibrary/build.gradle.template
+++ b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/groovylibrary/build.gradle.template
@@ -12,17 +12,17 @@ apply plugin: 'groovy'
 
 // In this section you declare where to find the dependencies of your project
 repositories {
-    // Use 'maven central' for resolving your dependencies.
+    // Use 'jcenter' for resolving your dependencies.
     // You can declare any Maven/Ivy/file repository here.
-    mavenCentral()
+    jcenter()
 }
 
 // In this section you declare the dependencies for your production and test code
 dependencies {
     // We use the latest groovy 2.x version for building this library
-    compile 'org.codehaus.groovy:groovy:${groovyVersion.groovyString}'
+    compile 'org.codehaus.groovy:groovy-all:${groovyVersion.groovyString}'
 
     // We use the awesome Spock testing and specification framework
-    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
+    testCompile 'org.spockframework:spock-core:${spockVersion.groovyString}'
     testCompile 'junit:junit:${junitVersion.groovyString}'
 }
diff --git a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/javalibrary/build.gradle.template b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/javalibrary/build.gradle.template
index 35b3e22..0d98795 100644
--- a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/javalibrary/build.gradle.template
+++ b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/javalibrary/build.gradle.template
@@ -12,15 +12,15 @@ apply plugin: 'java'
 
 // In this section you declare where to find the dependencies of your project
 repositories {
-    // Use 'maven central' for resolving your dependencies.
+    // Use 'jcenter' for resolving your dependencies.
     // You can declare any Maven/Ivy/file repository here.
-    mavenCentral()
+    jcenter()
 }
 
 // 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'
+    compile 'org.slf4j:slf4j-api:${slf4jVersion.groovyComment}'
 
     // 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
diff --git a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/scalalibrary/build.gradle.template b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/scalalibrary/build.gradle.template
index 4b61ee7..90756ec 100644
--- a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/scalalibrary/build.gradle.template
+++ b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/scalalibrary/build.gradle.template
@@ -12,9 +12,9 @@ apply plugin: 'scala'
 
 // In this section you declare where to find the dependencies of your project
 repositories {
-    // Use 'maven central' for resolving your dependencies.
+    // Use 'jcenter' for resolving your dependencies.
     // You can declare any Maven/Ivy/file repository here.
-    mavenCentral()
+    jcenter()
 }
 
 // In this section you declare the dependencies for your production and test code
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/BuildInitPluginSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/BuildInitPluginSpec.groovy
index 9e05699..6ec2262 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/BuildInitPluginSpec.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/BuildInitPluginSpec.groovy
@@ -28,7 +28,7 @@ class BuildInitPluginSpec extends Specification {
 
     def "applies plugin"() {
         when:
-        project.plugins.apply BuildInitPlugin
+        project.pluginManager.apply BuildInitPlugin
         and:
         project.evaluate()
         then:
@@ -44,7 +44,7 @@ class BuildInitPluginSpec extends Specification {
         buildFile << '// an empty build'
         project = TestUtil.builder().withProjectDir(projectDir).build()
         when:
-        project.plugins.apply BuildInitPlugin
+        project.pluginManager.apply(BuildInitPlugin)
 
         then:
         project.init != null
@@ -56,7 +56,7 @@ class BuildInitPluginSpec extends Specification {
         project.file("settings.gradle") << '// an empty file'
 
         when:
-        project.plugins.apply BuildInitPlugin
+        project.pluginManager.apply BuildInitPlugin
 
         then:
         project.init != null
@@ -68,7 +68,7 @@ class BuildInitPluginSpec extends Specification {
         TestUtil.createChildProject(project, 'child')
 
         when:
-        project.plugins.apply BuildInitPlugin
+        project.pluginManager.apply BuildInitPlugin
 
         then:
         project.init != null
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/WrapperPluginSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/WrapperPluginSpec.groovy
index 222f62f..f0bc4f6 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/WrapperPluginSpec.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/WrapperPluginSpec.groovy
@@ -15,10 +15,10 @@
  */
 
 
-
 package org.gradle.buildinit.plugins
 
 import org.gradle.api.tasks.wrapper.Wrapper
+import org.gradle.buildinit.tasks.internal.TaskConfiguration
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
@@ -27,10 +27,10 @@ class WrapperPluginSpec extends Specification {
 
     def "adds 'wrapper' task"() {
         when:
-        project.plugins.apply WrapperPlugin
+        project.pluginManager.apply WrapperPlugin
 
         then:
         project.tasks.wrapper instanceof Wrapper
-        project.tasks.wrapper.group == BuildInitPlugin.GROUP
+        project.tasks.wrapper.group == TaskConfiguration.GROUP
     }
 }
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyActionSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyActionSpec.groovy
deleted file mode 100644
index 2d0a7db..0000000
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/BuildInitAutoApplyActionSpec.groovy
+++ /dev/null
@@ -1,72 +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.buildinit.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 BuildInitAutoApplyActionSpec 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 init on taskcontainer"() {
-        when:
-        new BuildInitAutoApplyAction().execute(projectInternal)
-        then:
-        1 * taskContainerInternal.addPlaceholderAction("init", _) >> {args -> args[1].run()}
-        1 * projectInternal.getParent() >> null
-        1 * projectInternal.getPlugins() >> pluginContainer
-        1 * pluginContainer.apply("build-init")
-    }
-
-    def "is not applied on non rootprojects"() {
-        given:
-        isNotRootProject()
-        when:
-        new BuildInitAutoApplyAction().execute(projectInternal)
-        then:
-        0 * taskContainerInternal.addPlaceholderAction("init", _)
-        0 * projectInternal.getPlugins() >> pluginContainer
-        0 * pluginContainer.apply("build-init")
-    }
-
-    def isNotRootProject() {
-        projectInternal.getParent() >> Mock(ProjectInternal)
-    }
-
-    void noChildprojects() {
-        projectInternal.subprojects >> []
-    }
-}
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyActionSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyActionSpec.groovy
new file mode 100644
index 0000000..0bedfe9
--- /dev/null
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/action/BuildInitAutoApplyActionSpec.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.buildinit.plugins.internal.action
+
+import org.gradle.api.internal.plugins.DefaultPluginManager
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.tasks.TaskContainerInternal
+import org.gradle.buildinit.tasks.InitBuild
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildInitAutoApplyActionSpec extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    TestFile buildFile
+    ProjectInternal projectInternal
+    DefaultPluginManager pluginManager
+    TaskContainerInternal taskContainerInternal
+
+    public void setup() {
+        projectInternal = Mock(ProjectInternal)
+        taskContainerInternal = Mock(TaskContainerInternal)
+        pluginManager = Mock(DefaultPluginManager)
+        _ * projectInternal.getTasks() >> taskContainerInternal
+
+    }
+
+    def "applies placeholder action for init on taskcontainer"() {
+        when:
+        new BuildInitAutoApplyAction().execute(projectInternal)
+
+        then:
+        1 * taskContainerInternal.addPlaceholderAction("init", InitBuild.class, _)
+        1 * projectInternal.getParent() >> null
+    }
+
+    def "is not applied on non rootprojects"() {
+        given:
+        isNotRootProject()
+        when:
+        new BuildInitAutoApplyAction().execute(projectInternal)
+
+        then:
+        0 * taskContainerInternal.addPlaceholderAction(*_)
+    }
+
+    def isNotRootProject() {
+        projectInternal.getParent() >> Mock(ProjectInternal)
+    }
+
+    void noChildprojects() {
+        projectInternal.subprojects >> []
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/AbstractCommandLineConverter.java b/subprojects/cli/src/main/java/org/gradle/cli/AbstractCommandLineConverter.java
index 07081fe..f2d3d01 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/AbstractCommandLineConverter.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/AbstractCommandLineConverter.java
@@ -16,21 +16,9 @@
 package org.gradle.cli;
 
 public abstract class AbstractCommandLineConverter<T> implements CommandLineConverter<T> {
-    public T convert(Iterable<String> args) throws CommandLineArgumentException {
-        CommandLineParser parser = new CommandLineParser();
-        configure(parser);
-        return convert(parser.parse(args));
-    }
-
-    public T convert(ParsedCommandLine args) throws CommandLineArgumentException {
-        return convert(args, newInstance());
-    }
-
     public T convert(Iterable<String> args, T target) throws CommandLineArgumentException {
         CommandLineParser parser = new CommandLineParser();
         configure(parser);
         return convert(parser.parse(args), target);
     }
-
-    protected abstract T newInstance();
 }
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/AbstractPropertiesCommandLineConverter.java b/subprojects/cli/src/main/java/org/gradle/cli/AbstractPropertiesCommandLineConverter.java
index b66d3f6..e68c2d2 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/AbstractPropertiesCommandLineConverter.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/AbstractPropertiesCommandLineConverter.java
@@ -16,7 +16,6 @@
 
 package org.gradle.cli;
 
-import java.util.HashMap;
 import java.util.Map;
 
 public abstract class AbstractPropertiesCommandLineConverter extends AbstractCommandLineConverter<Map<String, String>> {
@@ -30,10 +29,6 @@ public abstract class AbstractPropertiesCommandLineConverter extends AbstractCom
         option.hasDescription(getPropertyOptionDescription());
     }
 
-    protected Map<String, String> newInstance() {
-        return new HashMap<String, String>();
-    }
-
     public Map<String, String> convert(ParsedCommandLine options, Map<String, String> properties) throws CommandLineArgumentException {
         for (String keyValueExpression : options.option(getPropertyOption()).getValues()) {
             int pos = keyValueExpression.indexOf("=");
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 bfc2627..f0c7a38 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineConverter.java
@@ -16,12 +16,8 @@
 package org.gradle.cli;
 
 public interface CommandLineConverter<T> {
-    T convert(Iterable<String> args) throws CommandLineArgumentException;
-
     T convert(Iterable<String> args, T target) throws CommandLineArgumentException;
 
-    T convert(ParsedCommandLine args) throws CommandLineArgumentException;
-
     T convert(ParsedCommandLine args, T target) throws CommandLineArgumentException;
 
     void configure(CommandLineParser parser);
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 3996137..b124fc3 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/CommandLineParser.java
@@ -64,7 +64,7 @@ public class CommandLineParser {
     }
 
     public CommandLineParser(Writer deprecationPrinter) {
-        this.deprecationPrinter = new PrintWriter(deprecationPrinter);
+        this.deprecationPrinter = new PrintWriter(deprecationPrinter, true);
     }
 
     /**
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 3134649..104950c 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/ParsedCommandLine.java
@@ -20,6 +20,7 @@ import java.util.*;
 public class ParsedCommandLine {
     private final Map<String, ParsedCommandLineOption> optionsByString = new HashMap<String, ParsedCommandLineOption>();
     private final Set<String> presentOptions = new HashSet<String>();
+    private final Set<String> removedOptions = new HashSet<String>();
     private final List<String> extraArguments = new ArrayList<String>();
 
     ParsedCommandLine(Iterable<CommandLineOption> options) {
@@ -33,7 +34,7 @@ public class ParsedCommandLine {
 
     @Override
     public String toString() {
-        return String.format("options: %s, extraArguments: %s", quoteAndJoin(presentOptions), quoteAndJoin(extraArguments));
+        return String.format("options: %s, extraArguments: %s, removedOptions: %s", quoteAndJoin(presentOptions), quoteAndJoin(extraArguments), quoteAndJoin(removedOptions));
     }
 
     private String quoteAndJoin(Iterable<String> strings) {
@@ -63,6 +64,18 @@ public class ParsedCommandLine {
     }
 
     /**
+     * Returns true if the given option was present in this command-line,
+     * but was removed because another option appeared later that replaces it.
+     *
+     * @param option The option, without the '-' or '--' prefix.
+     * @return true if the option was present.
+     */
+    public boolean hadOptionRemoved(String option) {
+        option(option);
+        return removedOptions.contains(option);
+    }
+
+    /**
      * See also {@link #hasOption}.
      *
      * @param logLevelOptions the options to check
@@ -106,6 +119,11 @@ public class ParsedCommandLine {
     }
 
     void removeOption(CommandLineOption option) {
-        presentOptions.removeAll(option.getOptions());
+        for (String optionStr : option.getOptions()) {
+            if (presentOptions.remove(optionStr)) {
+                // Only keep track of removed options that were present in the command line
+                removedOptions.add(optionStr);
+            }
+        }
     }
 }
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
index 0a3832f..f66d0e7 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
@@ -38,7 +38,7 @@ class AbstractPropertiesCommandLineConverterTest extends Specification {
     }
 
     def convert(String... args) {
-        converter.convert(Arrays.asList(args)).sort()
+        converter.convert(Arrays.asList(args), new HashMap<String, String>()).sort()
     }
 
     def "configures property based options"() {
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 87e6f2c..c9424db 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/CommandLineParserTest.groovy
@@ -685,7 +685,7 @@ class CommandLineParserTest extends Specification {
         result.extraArguments == ["-b", "-C", "-d"]
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-1871")
+    @Issue("https://issues.gradle.org/browse/GRADLE-1871")
     def "unknown options containing known arguments in their value are allowed"() {
         given:
         parser.option("a")
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 22851fe..f459561 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/ParsedCommandLineTest.groovy
@@ -36,4 +36,22 @@ class ParsedCommandLineTest extends Specification {
     private CommandLineOption cmdOption(String opt) {
         new CommandLineOption([opt])
     }
+
+    def "keeps track of removed options"() {
+        when:
+        def line = new ParsedCommandLine([cmdOption("a"), cmdOption("b"), cmdOption("c")]);
+        line.addOption("a", cmdOption("a"))
+        line.addOption("b", cmdOption("b"))
+        // allowOneOf(a, b)
+        line.removeOption(cmdOption("a"))
+
+        then:
+        !line.hasOption("a")
+        line.hasOption("b")
+
+        line.hadOptionRemoved("a")
+
+        !line.hasAnyOption(["a", "c"])
+        line.hasAnyOption(["b", "c"])
+    }
 }
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/ProjectPropertiesCommandLineConverterTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/ProjectPropertiesCommandLineConverterTest.groovy
index 00623ba..69d2943 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/ProjectPropertiesCommandLineConverterTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/ProjectPropertiesCommandLineConverterTest.groovy
@@ -22,7 +22,7 @@ class ProjectPropertiesCommandLineConverterTest extends Specification{
   def converter = new ProjectPropertiesCommandLineConverter();
 
   def convert(String... args) {
-    converter.convert(Arrays.asList(args)).sort()
+    converter.convert(Arrays.asList(args), new HashMap<String, String>()).sort()
   }
 
   def "parses project properties args"() {
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
index 595e168..44491a1 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
@@ -22,7 +22,7 @@ class SystemPropertiesCommandLineConverterTest extends Specification {
     def converter = new SystemPropertiesCommandLineConverter();
 
     def convert(String... args) {
-        converter.convert(Arrays.asList(args)).sort()
+        converter.convert(Arrays.asList(args), new HashMap<String, String>()).sort()
     }
 
     def "parses system properties args"() {
diff --git a/subprojects/code-quality/code-quality.gradle b/subprojects/code-quality/code-quality.gradle
index 0ecaff2..90bbe3f 100644
--- a/subprojects/code-quality/code-quality.gradle
+++ b/subprojects/code-quality/code-quality.gradle
@@ -27,9 +27,9 @@ dependencies {
     // minimal dependencies to make our code compile
     // we don't ship these dependencies because findbugs plugin will download them (and more) at runtime
     provided "com.google.code.findbugs:findbugs:2.0.1 at jar"
-    provided "com.google.code.findbugs:bcel:2.0.1 at jar"
-    provided "dom4j:dom4j:1.6.1 at jar"
-    provided "jaxen:jaxen:1.1.1 at jar"
+    provided libraries.dom4j
+    testRuntime "com.google.code.findbugs:bcel:2.0.1 at jar"
+    testRuntime libraries.jaxen
 }
 
 useTestFixtures()
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AbstractFindBugsPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AbstractFindBugsPluginIntegrationTest.groovy
new file mode 100644
index 0000000..29054ea
--- /dev/null
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AbstractFindBugsPluginIntegrationTest.groovy
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.quality
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.hamcrest.Matcher
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+
+import static org.gradle.util.Matchers.containsLine
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.startsWith
+
+abstract class AbstractFindBugsPluginIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        writeBuildFile()
+    }
+
+    def "default findbugs version can be inspected"() {
+        expect:
+        succeeds("dependencies", "--configuration", "findbugs")
+        output.contains "com.google.code.findbugs:findbugs:"
+    }
+
+    def "analyze good code"() {
+        goodCode()
+        expect:
+        succeeds("check")
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
+        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
+    }
+
+    void "analyze bad code"() {
+        badCode()
+
+        expect:
+        fails("check")
+        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"))
+    }
+
+    void "can ignore failures"() {
+        badCode()
+        buildFile << """
+            findbugs {
+                ignoreFailures = true
+            }
+        """
+
+        expect:
+        succeeds("check")
+        output.contains("FindBugs rule violations were found. See the report at:")
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.BadClass"))
+        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.BadClassTest"))
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "is incremental"() {
+        given:
+        goodCode()
+
+        expect:
+        succeeds("findbugsMain") && ":findbugsMain" in nonSkippedTasks
+        succeeds(":findbugsMain") && ":findbugsMain" in skippedTasks
+
+        when:
+        file("build/reports/findbugs/main.xml").delete()
+
+        then:
+        succeeds("findbugsMain") && ":findbugsMain" in nonSkippedTasks
+    }
+
+    def "cannot generate multiple reports"() {
+        given:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled true
+                html.enabled true
+            }
+        """
+
+        and:
+        goodCode()
+
+        expect:
+        fails "findbugsMain"
+
+        failure.assertHasCause "FindBugs tasks can only have one report enabled"
+    }
+
+    def "can use optional arguments"() {
+        given:
+        buildFile << """
+            findbugs {
+                effort 'max'
+                reportLevel 'high'
+                includeFilterConfig resources.text.fromFile('include.xml')
+                excludeFilter file('exclude.xml')
+                visitors = ['FindDeadLocalStores', 'UnreadFields']
+                omitVisitors = ['WaitInLoop', 'UnnecessaryMath']
+            }
+            findbugsMain.reports {
+                xml.enabled true
+            }
+        """
+
+        and:
+        goodCode()
+        badCode()
+
+        and:
+        writeFilterFile('include.xml', '.*')
+        writeFilterFile('exclude.xml', 'org\\.gradle\\.Bad.*')
+
+        expect:
+        succeeds("check")
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
+        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
+    }
+
+    void "excludes baseline bug for matching bug instance"() {
+        given:
+        String baselineExcludeFilename = 'baselineExclude.xml'
+
+        buildFile << """
+            findbugs {
+                excludeBugsFilterConfig resources.text.fromFile('${baselineExcludeFilename}')
+            }
+            findbugsMain.reports {
+                xml.enabled true
+            }
+        """
+
+        and:
+        badCode()
+
+        when:
+        writeBaselineBugsFilterFile(baselineExcludeFilename, new JavaClass('org.gradle', 'BadClass'))
+
+        then:
+        succeeds("check")
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.BadClass"))
+
+        when:
+        writeBaselineBugsFilterFile(baselineExcludeFilename, new JavaClass('org.gradle', 'SomeOtherClass'))
+
+        then:
+        fails("check")
+        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"))
+    }
+
+    def "can generate html reports"() {
+        given:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled false
+                html.enabled true
+            }
+        """
+
+        and:
+        goodCode()
+
+        when:
+        run "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.html").exists()
+    }
+    
+    def "can generate xml with messages reports"() {
+        given:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled true
+                xml.withMessages true
+                html.enabled false
+            }
+            findbugsMain.ignoreFailures true
+        """
+
+        and:
+        badCode()
+
+        when:
+        run "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        containsXmlMessages(file("build/reports/findbugs/main.xml"))
+    }
+
+    def "can generate no reports"() {
+        given:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled false
+                html.enabled false
+            }
+        """
+
+        and:
+        goodCode()
+
+        expect:
+        succeeds "findbugsMain"
+
+        and:
+        !file("build/reports/findbugs/main.html").exists()
+        !file("build/reports/findbugs/main.xml").exists()
+    }
+
+    def "can analyze a lot of classes"() {
+        goodCode(800)
+        expect:
+        succeeds("check")
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
+        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class800"))
+        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
+        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class800Test"))
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "is incremental for reporting settings"() {
+        given:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled true
+            }
+        """
+
+        and:
+        goodCode()
+
+        when:
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        ":findbugsMain" in nonSkippedTasks
+        !(":findbugsMain" in skippedTasks)
+
+        when:
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        !(":findbugsMain" in nonSkippedTasks)
+        ":findbugsMain" in skippedTasks
+
+        when:
+        buildFile << """
+            findbugsMain.reports {
+                xml.enabled false
+            }
+        """
+
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        ":findbugsMain" in nonSkippedTasks
+        !(":findbugsMain" in skippedTasks)
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "is incremental for withMessage"() {
+        given:
+        buildFile << """
+            findbugsMain {
+                reports {
+                    xml.enabled true
+                    xml.withMessages true
+                }
+
+                ignoreFailures true
+            }
+        """
+
+        and:
+        badCode()
+
+        when:
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        containsXmlMessages(file("build/reports/findbugs/main.xml"))
+        ":findbugsMain" in nonSkippedTasks
+        !(":findbugsMain" in skippedTasks)
+
+        when:
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        containsXmlMessages(file("build/reports/findbugs/main.xml"))
+        !(":findbugsMain" in nonSkippedTasks)
+        ":findbugsMain" in skippedTasks
+
+        when:
+        buildFile << """
+            findbugsMain {
+                reports {
+                    xml.enabled true
+                    xml.withMessages false
+                }
+
+                ignoreFailures true
+            }
+        """
+
+        succeeds "findbugsMain"
+
+        then:
+        file("build/reports/findbugs/main.xml").exists()
+        !containsXmlMessages(file("build/reports/findbugs/main.xml"))
+        ":findbugsMain" in nonSkippedTasks
+        !(":findbugsMain" in skippedTasks)
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "is withMessage ignored for non-XML report setting"() {
+        given:
+        buildFile << """
+            findbugsMain {
+                reports {
+                    xml.enabled false
+                    xml.withMessages true
+                    html.enabled true
+                }
+            }
+        """
+
+        and:
+        goodCode()
+
+        when:
+        succeeds "findbugsMain"
+
+        then:
+        !file("build/reports/findbugs/main.xml").exists()
+        file("build/reports/findbugs/main.html").exists()
+
+        when:
+        buildFile << """
+            findbugsMain.reports {
+                xml.withMessages false
+            }
+        """
+
+        and:
+        succeeds "findbugsMain"
+
+        then:
+        !file("build/reports/findbugs/main.xml").exists()
+        file("build/reports/findbugs/main.html").exists()
+        !(":findbugsMain" in nonSkippedTasks)
+        ":findbugsMain" in skippedTasks
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3214")
+    def "Thrown java.lang.Error due to missing transitive dependency is handled and fails the build"() {
+        given:
+        buildFile << 'configurations.findbugs.transitive = false'
+
+        and:
+        goodCode()
+
+        expect:
+        fails("check")
+        failure.assertHasCause 'FindBugs encountered an error.'
+        failure.assertHasDescription "Execution failed for task ':findbugsMain'."
+        errorOutput.contains 'Caused by: java.lang.NoClassDefFoundError'
+    }
+
+    private static boolean containsXmlMessages(File xmlReportFile) {
+        new XmlSlurper().parseText(xmlReportFile.text).BugInstance.children().collect { it.name() }.containsAll(['ShortMessage', 'LongMessage'])
+    }
+
+    private goodCode(int numberOfClasses = 1) {
+        1.upto(numberOfClasses) {
+            file("src/main/java/org/gradle/Class${it}.java") << "package org.gradle; public class Class${it} { public boolean isFoo(Object arg) { return true; } }"
+            file("src/test/java/org/gradle/Class${it}Test.java") << "package org.gradle; public class Class${it}Test { public boolean isFoo(Object arg) { return true; } }"
+        }
+    }
+
+    private badCode() {
+        // Has DM_EXIT
+        file('src/main/java/org/gradle/BadClass.java') << 'package org.gradle; public class BadClass { public boolean isFoo(Object arg) { System.exit(1); return true; } }'
+        // Has ES_COMPARING_PARAMETER_STRING_WITH_EQ
+        file('src/test/java/org/gradle/BadClassTest.java') << 'package org.gradle; public class BadClassTest { public boolean isFoo(Object arg) { return "true" == "false"; } }'
+    }
+
+    private Matcher<String> containsClass(String className) {
+        containsLine(containsString(className.replace(".", File.separator)))
+    }
+
+    private void writeBuildFile() {
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "findbugs"
+
+            repositories {
+                mavenCentral()
+            }
+        """
+    }
+
+    private void writeFilterFile(String filename, String className) {
+        file(filename) << """
+            <FindBugsFilter>
+            <Match>
+                <Class name="${className}" />
+            </Match>
+            </FindBugsFilter>
+        """
+    }
+
+    private void writeBaselineBugsFilterFile(String filename, JavaClass javaClass) {
+        file(filename) << """
+            <BugCollection>
+                <BugInstance type="DM_EXIT" priority="2" rank="16" abbrev="Dm" category="BAD_PRACTICE">
+                    <Class classname="${javaClass.fullyQualifiedClassName}">
+                        <SourceLine classname="${javaClass.fullyQualifiedClassName}" start="1" end="1" sourcefile="${javaClass.classFilename}" sourcepath="${javaClass.fullyQualifiedClassFilename}"/>
+                    </Class>
+                    <Method classname="${javaClass.fullyQualifiedClassName}" name="isFoo" signature="(Ljava/lang/Object;)Z" isStatic="false">
+                        <SourceLine classname="${javaClass.fullyQualifiedClassName}" start="1" end="1" startBytecode="0" endBytecode="57" sourcefile="${javaClass.classFilename}" sourcepath="${javaClass.fullyQualifiedClassFilename}"/>
+                    </Method>
+                    <SourceLine classname="${javaClass.fullyQualifiedClassName}" start="1" end="1" startBytecode="1" endBytecode="1" sourcefile="${javaClass.classFilename}" sourcepath="${javaClass.fullyQualifiedClassFilename}"/>
+                </BugInstance>
+            </BugCollection>
+        """
+    }
+
+    private class JavaClass {
+        String pkg
+        String className
+        String fullyQualifiedClassName
+        String classFilename
+        String fullyQualifiedClassFilename
+
+        JavaClass(String pkg, String className) {
+            this.pkg = pkg
+            this.className = className
+            fullyQualifiedClassName = "${pkg}.${className}"
+            classFilename = "${className}.java"
+            fullyQualifiedClassFilename = "${pkg.replaceAll('\\.', '/')}/${classFilename}"
+        }
+    }
+}
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 6081732..e26d904 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
@@ -16,7 +16,9 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.hamcrest.Matcher
+import spock.lang.IgnoreIf
 
 import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.Matchers.containsString
@@ -63,6 +65,7 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
     }
 
     def "analyze bad code"() {
+        defaultLanguage('en')
         badCode()
 
         expect:
@@ -76,6 +79,7 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
 
     def "can suppress console output"() {
         given:
+        defaultLanguage('en')
         badCode()
 
         when:
@@ -105,13 +109,15 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
         file("build/reports/checkstyle/main.xml").assertContents(containsClass("org.gradle.class2"))
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "is incremental"() {
         given:
         goodCode()
 
         expect:
         succeeds("checkstyleMain") && ":checkstyleMain" in nonSkippedTasks
-        succeeds(":checkstyleMain") && ":checkstyleMain" in skippedTasks
+        executer.withArgument("-i")
+        succeeds("checkstyleMain") && ":checkstyleMain" in skippedTasks
 
         when:
         file("build/reports/checkstyle/main.xml").delete()
@@ -137,8 +143,8 @@ class CheckstylePluginIntegrationTest extends WellBehavedPluginTest {
     private goodCode() {
         file('src/main/java/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
         file('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
-        file('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class1 { }'
-        file('src/test/groovy/org/gradle/TestClass2.java') << 'package org.gradle; class TestClass1 { }'
+        file('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class2 { }'
+        file('src/test/groovy/org/gradle/TestClass2.java') << 'package org.gradle; class TestClass2 { }'
     }
 
     private badCode() {
@@ -179,4 +185,8 @@ dependencies {
 </module>
         """
     }
+
+    private void defaultLanguage(String defaultLanguage) {
+        executer.withDefaultLocale(new Locale(defaultLanguage))
+    }
 }
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 eade780..6de1fea 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
@@ -16,10 +16,18 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
 import static org.hamcrest.Matchers.startsWith
 
 class CodeNarcPluginIntegrationTest extends WellBehavedPluginTest {
     @Override
+    String getPluginName() {
+        return "codenarc"
+    }
+
+    @Override
     String getMainTask() {
         return "check"
     }
@@ -56,6 +64,7 @@ class CodeNarcPluginIntegrationTest extends WellBehavedPluginTest {
         file("build/reports/codenarc/test.html").exists()
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "is incremental"() {
         given:
         goodCode()
@@ -70,7 +79,8 @@ class CodeNarcPluginIntegrationTest extends WellBehavedPluginTest {
         then:
         succeeds("codenarcMain") && ":codenarcMain" in nonSkippedTasks
     }
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "can generate multiple reports"() {
         given:
         buildFile << """
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
deleted file mode 100644
index 265fd0f..0000000
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/CodeQualityPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.quality
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.executer.ExecutionFailure
-import org.gradle.test.fixtures.file.TestFile
-import org.hamcrest.Matcher
-import org.junit.Test
-
-import static org.gradle.util.Matchers.containsLine
-import static org.hamcrest.Matchers.*
-
-class CodeQualityPluginIntegrationTest extends AbstractIntegrationTest {
-    {
-        // code-quality plugin is deprecated
-        executer.withDeprecationChecksDisabled()
-    }
-
-    @Test
-    public void handlesEmptyProjects() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'java'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-        inTestDirectory().withTasks('check').run()
-    }
-
-    @Test
-    public void generatesReportForJavaSource() {
-        testFile('build.gradle') << '''
-apply plugin: 'java'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/java/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
-        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
-    }
-
-    @Test
-    public void generatesReportForJavaSourceInGroovySourceDirs() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/groovy/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/checkstyle/main.xml').assertContents(containsClass('org.gradle.Class1'))
-        testFile('build/checkstyle/test.xml').assertContents(containsClass('org.gradle.TestClass1'))
-    }
-
-    private Matcher<String> containsClass(String classname) {
-        return containsLine(containsString(classname.replace('.', File.separator) + '.java'))
-    }
-
-    @Test
-    public void checkstyleOnlyChecksJavaSource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/groovy/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        testFile('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class2 { }'
-        testFile('src/main/groovy/org/gradle/class3.groovy') << 'package org.gradle; class class3 { }'
-
-        inTestDirectory().withTasks('checkstyleMain').run()
-
-        testFile('build/checkstyle/main.xml').assertExists()
-        testFile('build/checkstyle/main.xml').assertContents(not(containsClass('org.gradle.class3')))
-    }
-
-    @Test
-    public void checkstyleViolationBreaksBuild() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-        writeCheckstyleConfig()
-
-        testFile('src/main/java/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
-        testFile('src/main/groovy/org/gradle/class2.java') << 'package org.gradle; class class2 { }'
-
-        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
-        failure.assertHasDescription('Execution failed for task \':checkstyleMain\'.')
-        failure.assertThatCause(startsWith('Checkstyle rule violations were found. See the report at'))
-
-        testFile('build/checkstyle/main.xml').assertExists()
-    }
-
-    @Test
-    public void generatesReportForGroovySource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/Class1.groovy') << 'package org.gradle; class Class1 { }'
-        testFile('src/test/groovy/org/gradle/TestClass1.groovy') << 'package org.gradle; class TestClass1 { }'
-
-        inTestDirectory().withTasks('check').run()
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-        testFile('build/reports/codenarc/test.html').assertExists()
-    }
-
-    @Test
-    public void codeNarcOnlyChecksGroovySource() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/class1.java') << 'package org.gradle; class class1 { }'
-        testFile('src/main/groovy/org/gradle/Class2.groovy') << 'package org.gradle; class Class2 { }'
-
-        inTestDirectory().withTasks('codenarcMain').run()
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-    }
-
-    @Test
-    public void codeNarcViolationBreaksBuild() {
-        testFile('build.gradle') << '''
-apply plugin: 'groovy'
-apply plugin: 'code-quality'
-repositories { mavenCentral() }
-dependencies { compile localGroovy() }
-'''
-
-        writeCodeNarcConfigFile()
-
-        testFile('src/main/groovy/org/gradle/class1.groovy') << 'package org.gradle; class class1 { }'
-
-        ExecutionFailure failure = inTestDirectory().withTasks('check').runWithFailure()
-        failure.assertHasDescription('Execution failed for task \':codenarcMain\'.')
-        failure.assertThatCause(startsWith('CodeNarc rule violations were found. See the report at:'))
-
-        testFile('build/reports/codenarc/main.html').assertExists()
-    }
-
-    private TestFile writeCheckstyleConfig() {
-        return testFile('config/checkstyle/checkstyle.xml') << '''
-<!DOCTYPE module PUBLIC
-        "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
-        "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
-<module name="Checker">
-    <module name="TreeWalker">
-        <module name="TypeName"/>
-    </module>
-</module>'''
-    }
-
-    private TestFile writeCodeNarcConfigFile() {
-        return testFile('config/codenarc/codenarc.xml') << '''
-<ruleset xmlns="http://codenarc.org/ruleset/1.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xsi:schemaLocation="http://codenarc.org/ruleset/1.0 http://codenarc.org/ruleset-schema.xsd"
-        xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd">
-    <ruleset-ref path='rulesets/naming.xml'/>
-</ruleset>
-'''
-    }
-
-}
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsClasspathValidationIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsClasspathValidationIntegrationTest.groovy
new file mode 100644
index 0000000..251455e
--- /dev/null
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsClasspathValidationIntegrationTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.api.plugins.quality
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.hamcrest.CoreMatchers
+
+class FindBugsClasspathValidationIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "findbugs"
+
+            repositories {
+                mavenCentral()
+            }
+        """
+        file('src/main/java/org/gradle/BadClass.java') << 'package org.gradle; public class BadClass { public boolean isFoo(Object arg) { System.exit(1); return true; } }'
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "informs that FindBugs version is too low"() {
+        buildFile << """
+            dependencies {
+                //downgrade version:
+                findbugs "com.google.code.findbugs:findbugs:2.0.3"
+            }
+        """
+        expect:
+        fails("findbugsMain")
+        failure.assertThatCause(CoreMatchers.containsString("too low to work with currently used Java"))
+    }
+
+    @Requires(TestPrecondition.JDK6)
+    def "informs that FindBugs version is too high"() {
+        expect:
+        fails("findbugsMain")
+        failure.assertThatCause(CoreMatchers.containsString("too high to work with currently used Java"))
+    }
+}
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsForOldJavaIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsForOldJavaIntegrationTest.groovy
new file mode 100644
index 0000000..651480e
--- /dev/null
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsForOldJavaIntegrationTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.quality
+
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.JDK6)
+class FindBugsForOldJavaIntegrationTest extends AbstractFindBugsPluginIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            dependencies {
+                //downgrade version:
+                findbugs "com.google.code.findbugs:findbugs:2.0.3"
+            }
+        """
+    }
+}
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsIntegrationTest.groovy
new file mode 100644
index 0000000..48101fe
--- /dev/null
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/FindBugsIntegrationTest.groovy
@@ -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.plugins.quality
+
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+class FindBugsIntegrationTest extends AbstractFindBugsPluginIntegrationTest {}
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 b9f7db8..e0bbbff 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 the original author or authors.
+ * Copyright 2014 the original author 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,386 +13,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+
 package org.gradle.api.plugins.quality
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
-import org.hamcrest.Matcher
-
-import static org.gradle.util.Matchers.containsLine
-import static org.hamcrest.Matchers.containsString
-import static org.hamcrest.Matchers.startsWith
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 
+ at Requires(TestPrecondition.JDK7_OR_LATER)
 class FindBugsPluginIntegrationTest extends WellBehavedPluginTest {
     @Override
+    String getPluginName() {
+        return "findbugs"
+    }
+
+    @Override
     String getMainTask() {
         return "check"
     }
 
     def setup() {
-        writeBuildFile()
-    }
-
-    def "allows configuring tool dependencies explicitly"() {
-        expect: //defaults exist and can be inspected
-        succeeds("dependencies", "--configuration", "findbugs")
-        output.contains "com.google.code.findbugs:findbugs:"
-
-        when:
-        buildFile << """
-            dependencies {
-                //downgrade version:
-                findbugs "com.google.code.findbugs:findbugs:2.0.0"
-            }
-        """
-
-        then:
-        succeeds("dependencies", "--configuration", "findbugs")
-        output.contains "com.google.code.findbugs:findbugs:2.0.0"
-    }
-
-    def "analyze good code"() {
-        goodCode()
-        expect:
-        succeeds("check")
-        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
-        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
-    }
-
-    void "analyze bad code"() {
-        badCode()
-
-        expect:
-        fails("check")
-        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"))
-    }
-
-    void "can ignore failures"() {
-        badCode()
-        buildFile << """
-            findbugs {
-                ignoreFailures = true
-            }
-        """
-
-        expect:
-        succeeds("check")
-        output.contains("FindBugs rule violations were found. See the report at:")
-        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.BadClass"))
-        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.BadClassTest"))
-    }
-
-    def "is incremental"() {
-        given:
-        goodCode()
-
-        expect:
-        succeeds("findbugsMain") && ":findbugsMain" in nonSkippedTasks
-        succeeds(":findbugsMain") && ":findbugsMain" in skippedTasks
-
-        when:
-        file("build/reports/findbugs/main.xml").delete()
-
-        then:
-        succeeds("findbugsMain") && ":findbugsMain" in nonSkippedTasks
-    }
-
-    def "cannot generate multiple reports"() {
-        given:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled true
-                html.enabled true
-            }
-        """
-
-        and:
-        goodCode()
-
-        expect:
-        fails "findbugsMain"
-
-        failure.assertHasCause "FindBugs tasks can only have one report enabled"
-    }
-
-    def "can use optional arguments"() {
-        given:
-        buildFile << """
-            findbugs {
-                effort 'max'
-                reportLevel 'high'
-                includeFilter file('include.xml')
-                excludeFilter file('exclude.xml')
-                visitors = ['FindDeadLocalStores', 'UnreadFields']
-                omitVisitors = ['WaitInLoop', 'UnnecessaryMath']
-            }
-            findbugsMain.reports {
-                xml.enabled true
-            }
-        """
-
-        and:
-        goodCode()
-        badCode()
-
-        and:
-        writeFilterFile('include.xml', '.*')
-        writeFilterFile('exclude.xml', 'org\\.gradle\\.Bad.*')
-
-        expect:
-        succeeds("check")
-        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
-        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
-    }
-
-    def "can generate html reports"() {
-        given:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled false
-                html.enabled true
-            }
-        """
-
-        and:
-        goodCode()
-
-        when:
-        run "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.html").exists()
-    }
-    
-    def "can generate xml with messages reports"() {
-        given:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled true
-                xml.withMessages true
-                html.enabled false
-            }
-            findbugsMain.ignoreFailures true
-        """
-
-        and:
-        badCode()
-
-        when:
-        run "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        containsXmlMessages(file("build/reports/findbugs/main.xml"))
-    }
-
-    def "can generate no reports"() {
-        given:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled false
-                html.enabled false
-            }
-        """
-
-        and:
-        goodCode()
-
-        expect:
-        succeeds "findbugsMain"
-
-        and:
-        !file("build/reports/findbugs/main.html").exists()
-        !file("build/reports/findbugs/main.xml").exists()
-    }
-
-    def "can analyze a lot of classes"() {
-        goodCode(800)
-        expect:
-        succeeds("check")
-        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class1"))
-        file("build/reports/findbugs/main.xml").assertContents(containsClass("org.gradle.Class800"))
-        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
-        file("build/reports/findbugs/test.xml").assertContents(containsClass("org.gradle.Class800Test"))
-    }
-
-    def "is incremental for reporting settings"() {
-        given:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled true
-            }
-        """
-
-        and:
-        goodCode()
-
-        when:
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        ":findbugsMain" in nonSkippedTasks
-        !(":findbugsMain" in skippedTasks)
-
-        when:
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        !(":findbugsMain" in nonSkippedTasks)
-        ":findbugsMain" in skippedTasks
-
-        when:
-        buildFile << """
-            findbugsMain.reports {
-                xml.enabled false
-            }
-        """
-
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        ":findbugsMain" in nonSkippedTasks
-        !(":findbugsMain" in skippedTasks)
-    }
-
-    def "is incremental for withMessage"() {
-        given:
         buildFile << """
-            findbugsMain {
-                reports {
-                    xml.enabled true
-                    xml.withMessages true
-                }
-
-                ignoreFailures true
-            }
-        """
-
-        and:
-        badCode()
-
-        when:
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        containsXmlMessages(file("build/reports/findbugs/main.xml"))
-        ":findbugsMain" in nonSkippedTasks
-        !(":findbugsMain" in skippedTasks)
-
-        when:
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        containsXmlMessages(file("build/reports/findbugs/main.xml"))
-        !(":findbugsMain" in nonSkippedTasks)
-        ":findbugsMain" in skippedTasks
-
-        when:
-        buildFile << """
-            findbugsMain {
-                reports {
-                    xml.enabled true
-                    xml.withMessages false
-                }
-
-                ignoreFailures true
-            }
-        """
-
-        succeeds "findbugsMain"
-
-        then:
-        file("build/reports/findbugs/main.xml").exists()
-        !containsXmlMessages(file("build/reports/findbugs/main.xml"))
-        ":findbugsMain" in nonSkippedTasks
-        !(":findbugsMain" in skippedTasks)
-    }
-
-    def "is withMessage ignored for non-XML report setting"() {
-        given:
-        buildFile << """
-            findbugsMain {
-                reports {
-                    xml.enabled false
-                    xml.withMessages true
-                    html.enabled true
-                }
-            }
-        """
-
-        and:
-        goodCode()
-
-        when:
-        succeeds "findbugsMain"
-
-        then:
-        !file("build/reports/findbugs/main.xml").exists()
-        file("build/reports/findbugs/main.html").exists()
-
-        when:
-        buildFile << """
-            findbugsMain.reports {
-                xml.withMessages false
-            }
-        """
-
-        and:
-        succeeds "findbugsMain"
-
-        then:
-        !file("build/reports/findbugs/main.xml").exists()
-        file("build/reports/findbugs/main.html").exists()
-        !(":findbugsMain" in nonSkippedTasks)
-        ":findbugsMain" in skippedTasks
-    }
-
-    private boolean containsXmlMessages(File xmlReportFile) {
-        new XmlSlurper().parseText(xmlReportFile.text).BugInstance.children().collect { it.name() }.containsAll(['ShortMessage', 'LongMessage'])
-    }
-
-    private goodCode(int numberOfClasses = 1) {
-        1.upto(numberOfClasses) {
-            file("src/main/java/org/gradle/Class${it}.java") << "package org.gradle; public class Class${it} { public boolean isFoo(Object arg) { return true; } }"
-            file("src/test/java/org/gradle/Class${it}Test.java") << "package org.gradle; public class Class${it}Test { public boolean isFoo(Object arg) { return true; } }"
-        }
-    }
-
-    private badCode() {
-        // Has DM_EXIT
-        file('src/main/java/org/gradle/BadClass.java') << 'package org.gradle; public class BadClass { public boolean isFoo(Object arg) { System.exit(1); return true; } }'
-        // Has ES_COMPARING_PARAMETER_STRING_WITH_EQ
-        file('src/test/java/org/gradle/BadClassTest.java') << 'package org.gradle; public class BadClassTest { public boolean isFoo(Object arg) { return "true" == "false"; } }'
-    }
-
-    private Matcher<String> containsClass(String className) {
-        containsLine(containsString(className.replace(".", File.separator)))
-    }
-
-    private void writeBuildFile() {
-        file("build.gradle") << """
-            apply plugin: "java"
-            apply plugin: "findbugs"
-
-            repositories {
-                mavenCentral()
-            }
-        """
-    }
-
-    private void writeFilterFile(String filename, String className) {
-        file(filename) << """
-            <FindBugsFilter>
-            <Match>
-                <Class name="${className}" />
-            </Match>
-            </FindBugsFilter>
+            apply plugin: 'java'
         """
     }
 }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/JDependPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/JDependPluginIntegrationTest.groovy
index 8f42125..03dfc5a 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/JDependPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/JDependPluginIntegrationTest.groovy
@@ -16,6 +16,9 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
 import static org.hamcrest.Matchers.containsString
 
 class JDependPluginIntegrationTest extends WellBehavedPluginTest {
@@ -24,6 +27,11 @@ class JDependPluginIntegrationTest extends WellBehavedPluginTest {
     }
 
     @Override
+    String getPluginName() {
+        return "jdepend"
+    }
+
+    @Override
     String getMainTask() {
         return "check"
     }
@@ -37,6 +45,7 @@ class JDependPluginIntegrationTest extends WellBehavedPluginTest {
         file("build/reports/jdepend/test.xml").assertContents(containsString("org.gradle.Class1Test"))
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "is incremental"() {
         given:
         goodCode()
@@ -108,6 +117,19 @@ class JDependPluginIntegrationTest extends WellBehavedPluginTest {
         failure.assertHasCause "JDepend tasks must have one report enabled"
     }
 
+    def "does not fail if configuration is resolved before task execution"() {
+        when:
+        goodCode()
+
+        and:
+        buildFile << """
+            configurations.jdepend.files
+        """
+
+        then:
+        succeeds "jdependMain"
+    }
+
     private goodCode() {
         file("src/main/java/org/gradle/Class1.java") <<
                 "package org.gradle; class Class1 { public boolean is() { return true; } }"
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 04f2350..05f2a47 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
@@ -35,7 +35,7 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
     def "allows configuring tool dependencies explicitly"() {
         expect: //defaults exist and can be inspected
         succeeds("dependencies", "--configuration", "pmd")
-        output.contains "pmd:pmd:"
+        output.contains "pmd:pmd-java:"
 
         when:
         buildFile << """
@@ -136,6 +136,40 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
         file("build/reports/pmd/main.xml").assertContents(containsClass("org.gradle.Class2"))
     }
 
+    def "use custom rule set"() {
+        customCode()
+
+        buildFile << """
+            pmd {
+                ruleSets = []
+                ruleSetConfig = resources.text.fromString('''${customRuleSetText()}''')
+            }
+        """
+
+        expect:
+        fails("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"))
+
+    }
+
+    def "can enable console output"() {
+        buildFile << """
+            pmd {
+                consoleOutput = true
+            }
+        """
+        badCode()
+
+        expect:
+        fails("check")
+        failure.assertHasDescription("Execution failed for task ':pmdTest'.")
+        failure.assertThatCause(containsString("2 PMD rule violations were found. See the report at:"))
+        output.contains "Class1Test.java:1:\tEmpty initializer was found"
+    }
+
     private void writeBuildFile() {
         file("build.gradle") << """
             apply plugin: "java"
@@ -175,7 +209,11 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
     }
 
     private customRuleSet() {
-        file ("customRuleSet.xml") << """
+        file("customRuleSet.xml") << customRuleSetText()
+    }
+
+    private customRuleSetText() {
+        """
             <ruleset name="custom"
                 xmlns="http://pmd.sf.net/ruleset/1.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -184,7 +222,7 @@ class PmdPluginIntegrationTest extends WellBehavedPluginTest {
 
                 <description>Custom rule set</description>
 
-                <rule ref="rulesets/braces.xml"/>
+                <rule ref="rulesets/java/braces.xml"/>
             </ruleset>
         """
     }
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 784bebd..ddf3696 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.4'])
+ at TargetVersions(['4.3', '5.0.4', '5.1.1', '5.2.3'])
 class PmdPluginVersionIntegrationTest extends MultiVersionIntegrationSpec {
     def "can use different PMD versions"() {
         given:
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.groovy
index d385a93..288eacc 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.groovy
@@ -16,14 +16,15 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.api.GradleException
+import org.gradle.api.Incubating
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.plugins.quality.internal.CheckstyleReportsImpl
 import org.gradle.api.reporting.Reporting
+import org.gradle.api.resources.TextResource
 import org.gradle.api.tasks.*
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.logging.ConsoleRenderer
-import org.gradle.util.DeprecationLogger
 
 import javax.inject.Inject
 
@@ -44,52 +45,51 @@ class Checkstyle extends SourceTask implements VerificationTask, Reporting<Check
     FileCollection classpath
 
     /**
-     * The Checkstyle configuration file to use.
+     * The Checkstyle configuration to use. Replaces the {@code configFile} property.
+     *
+     * @since 2.2
      */
-    @InputFile
-    File configFile
+    @Incubating
+    @Nested
+    TextResource config
 
     /**
-     * The properties available for use in the configuration file. These are substituted into the configuration
-     * file.
+     * The Checkstyle configuration file to use.
      */
-    @Input
-    @Optional
-    Map<String, Object> configProperties = [:]
+    File getConfigFile() {
+        getConfig()?.asFile()
+    }
 
     /**
-     * The properties available for use in the configuration file. These are substituted into the configuration
-     * file.
-     *
-     * @deprecated renamed to <tt>configProperties</tt>
+     * The Checkstyle configuration file to use.
      */
-    @Deprecated
-    Map<String, Object> getProperties() {
-        DeprecationLogger.nagUserOfReplacedProperty("Checkstyle.properties", "configProperties")
-        getConfigProperties()
+    void setConfigFile(File configFile) {
+        setConfig(project.resources.text.fromFile(configFile))
     }
 
     /**
      * The properties available for use in the configuration file. These are substituted into the configuration
      * file.
-     *
-     * @deprecated renamed to <tt>configProperties</tt>
      */
-    @Deprecated
-    void setProperties(Map<String, Object> properties) {
-        DeprecationLogger.nagUserOfReplacedProperty("Checkstyle.properties", "configProperties")
-        setConfigProperties(properties)
-    }
+    @Input
+    @Optional
+    Map<String, Object> configProperties = [:]
 
     @Nested
     private final CheckstyleReportsImpl reports
 
-    private final IsolatedAntBuilder antBuilder
+    Checkstyle() {
+        reports = instantiator.newInstance(CheckstyleReportsImpl, this)
+    }
 
     @Inject
-    Checkstyle(Instantiator instantiator, IsolatedAntBuilder antBuilder) {
-        this.antBuilder = antBuilder
-        reports = instantiator.newInstance(CheckstyleReportsImpl, this)
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -124,26 +124,6 @@ class Checkstyle extends SourceTask implements VerificationTask, Reporting<Check
     }
 
     /**
-     * Returns the destination file for the XML report.
-     *
-     * @deprecated Use {@code reports.xml.destination} instead.
-     */
-    @Deprecated
-    File getResultFile() {
-        DeprecationLogger.nagUserOfReplacedProperty("Checkstyle.resultFile", "reports.xml.destination")
-        return reports.xml.destination
-    }
-
-    /**
-     * @deprecated Use {@code reports.xml.destination} instead.
-     */
-    @Deprecated
-    void setResultFile(File file) {
-        DeprecationLogger.nagUserOfReplacedProperty("Checkstyle.resultFile", "reports.xml.destination")
-        reports.xml.destination = file
-    }
-
-    /**
      * Whether or not this task will ignore failures and continue running the build.
      */
     boolean ignoreFailures
@@ -159,7 +139,7 @@ class Checkstyle extends SourceTask implements VerificationTask, Reporting<Check
         antBuilder.withClasspath(getCheckstyleClasspath()).execute {
             ant.taskdef(name: 'checkstyle', classname: 'com.puppycrawl.tools.checkstyle.CheckStyleTask')
 
-            ant.checkstyle(config: getConfigFile(), failOnViolation: false, failureProperty: propertyName) {
+            ant.checkstyle(config: getConfig().asFile(), failOnViolation: false, failureProperty: propertyName) {
                 getSource().addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
                 getClasspath().addToAntBuilder(ant, 'classpath')
                 if (showViolations) {
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleExtension.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleExtension.groovy
index d3be0b7..35978d7 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleExtension.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstyleExtension.groovy
@@ -15,12 +15,39 @@
  */
 package org.gradle.api.plugins.quality
 
+import org.gradle.api.Incubating
+import org.gradle.api.Project
+import org.gradle.api.resources.TextResource
+
 class CheckstyleExtension extends CodeQualityExtension {
+    private final Project prj
+
+    CheckstyleExtension(Project project) {
+        prj = project
+    }
+
+    /**
+     * The Checkstyle configuration to use. Replaces the {@code configFile} property.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    TextResource config
+
+    /**
+     * The Checkstyle configuration file to use.
+     */
+    File getConfigFile() {
+        getConfig().asFile()
+    }
+
     /**
      * The Checkstyle configuration file to use.
      */
-    File configFile
-    
+    void setConfigFile(File configFile) {
+        setConfig(prj.resources.text.fromFile(configFile))
+    }
+
     /**
      * The properties available for use in the configuration file. These are substituted into the configuration
      * file.
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstylePlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstylePlugin.groovy
index aaaf457..6924d3d 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstylePlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CheckstylePlugin.groovy
@@ -19,6 +19,7 @@ import org.gradle.api.plugins.quality.internal.AbstractCodeQualityPlugin
 import org.gradle.api.tasks.SourceSet
 
 class CheckstylePlugin extends AbstractCodeQualityPlugin<Checkstyle> {
+    public static final String DEFAULT_CHECKSTYLE_VERSION = "5.9"
     private CheckstyleExtension extension
 
     @Override
@@ -33,11 +34,11 @@ class CheckstylePlugin extends AbstractCodeQualityPlugin<Checkstyle> {
 
     @Override
     protected CodeQualityExtension createExtension() {
-        extension = project.extensions.create("checkstyle", CheckstyleExtension)
+        extension = project.extensions.create("checkstyle", CheckstyleExtension, project)
 
         extension.with {
-            toolVersion = "5.6"
-            configFile = project.file("config/checkstyle/checkstyle.xml")
+            toolVersion = DEFAULT_CHECKSTYLE_VERSION
+            config = project.resources.text.fromFile("config/checkstyle/checkstyle.xml")
         }
 
         return extension
@@ -54,7 +55,7 @@ class CheckstylePlugin extends AbstractCodeQualityPlugin<Checkstyle> {
 
         task.conventionMapping.with {
             checkstyleClasspath = { conf }
-            configFile = { extension.configFile }
+            config = { extension.config }
             configProperties = { extension.configProperties }
             ignoreFailures = { extension.ignoreFailures }
             showViolations = { extension.showViolations }
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 60d26e0..6d9ea3c 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
@@ -16,16 +16,18 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.api.GradleException
+import org.gradle.api.Incubating
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.plugins.quality.internal.CodeNarcReportsImpl
 import org.gradle.api.reporting.Report
 import org.gradle.api.reporting.Reporting
+import org.gradle.api.resources.TextResource
 import org.gradle.api.tasks.*
+import org.gradle.internal.classpath.DefaultClassPath
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.logging.ConsoleRenderer
-import org.gradle.util.DeprecationLogger
 
 import javax.inject.Inject
 
@@ -40,10 +42,13 @@ class CodeNarc extends SourceTask implements VerificationTask, Reporting<CodeNar
     FileCollection codenarcClasspath
 
     /**
-     * The CodeNarc configuration file to use.
+     * The CodeNarc configuration to use. Replaces the {@code configFile} property.
+     *
+     * @since 2.2
      */
-    @InputFile
-    File configFile
+    @Incubating
+    @Nested
+    TextResource config
 
     /**
      * The maximum number of priority 1 violations allowed before failing the build.
@@ -63,68 +68,47 @@ class CodeNarc extends SourceTask implements VerificationTask, Reporting<CodeNar
     @Input
     int maxPriority3Violations
 
-    /**
-     * The format type of the CodeNarc report.
-     *
-     * @deprecated Use {@code reports.<report-type>.enabled} instead.
-     */
-    @Deprecated
-    String getReportFormat() {
-        DeprecationLogger.nagUserOfReplacedProperty("CodeNarc.reportFormat", "reports.<report-type>.enabled")
-        reports.firstEnabled?.name
-    }
+    @Nested
+    private final CodeNarcReportsImpl reports
 
     /**
-     * @deprecated Use {@code reports.<report-type>.enabled} instead.
+     * Whether or not the build should break when the verifications performed by this task fail.
      */
-    @Deprecated
-    void setReportFormat(String reportFormat) {
-        DeprecationLogger.nagUserOfReplacedProperty("CodeNarc.reportFormat", "reports.<report-type>.enabled")
-        reports.each {
-            it.enabled == it.name == reportFormat
-        }
+    boolean ignoreFailures
+
+    CodeNarc() {
+        reports = instantiator.newInstance(CodeNarcReportsImpl, this)
     }
 
     /**
-     * The file to write the report to.
-     *
-     * @deprecated Use {@code reports.<report-type>.destination} instead.
+     * The CodeNarc configuration file to use.
      */
-    @Deprecated
-    File getReportFile() {
-        DeprecationLogger.nagUserOfReplacedProperty("CodeNarc.reportFile", "reports.<report-type>.destination")
-        reports.firstEnabled?.destination
+    File getConfigFile() {
+        getConfig()?.asFile()
     }
 
     /**
-     * @deprecated Use {@code reports.<report-type>.destination} instead.
+     * The CodeNarc configuration file to use.
      */
-    @Deprecated
-    void setReportFile(File reportFile) {
-        DeprecationLogger.nagUserOfReplacedProperty("CodeNarc.reportFile", "reports.<report-type>.destination")
-        reports.firstEnabled?.destination = reportFile
+    void setConfigFile(File configFile) {
+        setConfig(project.resources.text.fromFile(configFile))
     }
 
-    @Nested
-    private final CodeNarcReportsImpl reports
-
-    private final IsolatedAntBuilder antBuilder
-
-    /**
-     * Whether or not the build should break when the verifications performed by this task fail.
-     */
-    boolean ignoreFailures
+    @Inject
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
 
     @Inject
-    CodeNarc(Instantiator instantiator, IsolatedAntBuilder antBuilder) {
-        reports = instantiator.newInstance(CodeNarcReportsImpl, this)
-        this.antBuilder = antBuilder
+    IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     @TaskAction
     void run() {
         logging.captureStandardOutput(LogLevel.INFO)
-        antBuilder.withClasspath(getCodenarcClasspath()).execute {
+        def classpath = new DefaultClassPath(getCodenarcClasspath())
+        antBuilder.withClasspath(classpath.asFiles).execute {
             ant.taskdef(name: 'codenarc', classname: 'org.codenarc.ant.CodeNarcTask')
             try {
                 ant.codenarc(ruleSetFiles: "file:${getConfigFile()}", maxPriority1Violations: getMaxPriority1Violations(), maxPriority2Violations: getMaxPriority2Violations(), maxPriority3Violations: getMaxPriority3Violations()) {
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 f0af5e5..7c8003f 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
@@ -15,13 +15,25 @@
  */
 package org.gradle.api.plugins.quality
 
+import org.gradle.api.Incubating
 import org.gradle.api.InvalidUserDataException
+import org.gradle.api.Project
+import org.gradle.api.resources.TextResource
 
 class CodeNarcExtension extends CodeQualityExtension {
+    private final Project prj
+
+    CodeNarcExtension(Project project) {
+        prj = project
+    }
+
     /**
-     * The CodeNarc configuration file to use.
+     * The CodeNarc configuration to use. Replaces the {@code configFile} property.
+     *
+     * @since 2.2
      */
-    File configFile
+    @Incubating
+    TextResource config
 
     /**
      * The maximum number of priority 1 violations allowed before failing the build.
@@ -43,6 +55,20 @@ class CodeNarcExtension extends CodeQualityExtension {
      */
     String reportFormat
 
+    /**
+     * The CodeNarc configuration file to use.
+     */
+    File getConfigFile() {
+        getConfig()?.asFile()
+    }
+
+    /**
+     * The CodeNarc configuration file to use.
+     */
+    void setConfigFile(File file) {
+        setConfig(prj.resources.text.fromFile(file))
+    }
+
     void setReportFormat(String reportFormat) {
         if (reportFormat in ["xml", "html", "console", "text"]) {
             this.reportFormat = 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 36ceb27..59a2f6f 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
@@ -20,6 +20,7 @@ import org.gradle.api.plugins.quality.internal.AbstractCodeQualityPlugin
 import org.gradle.api.tasks.SourceSet
 
 class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
+    public static final String DEFAULT_CODENARC_VERSION = "0.23"
     private CodeNarcExtension extension
 
     @Override
@@ -39,10 +40,10 @@ class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
 
     @Override
     protected CodeQualityExtension createExtension() {
-        extension = project.extensions.create("codenarc", CodeNarcExtension)
+        extension = project.extensions.create("codenarc", CodeNarcExtension, project)
         extension.with {
-            toolVersion = "0.18"
-            configFile = project.rootProject.file("config/codenarc/codenarc.xml")
+            toolVersion = DEFAULT_CODENARC_VERSION
+            config = project.resources.text.fromFile(project.rootProject.file("config/codenarc/codenarc.xml"))
             maxPriority1Violations = 0
             maxPriority2Violations = 0
             maxPriority3Violations = 0
@@ -53,15 +54,15 @@ class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
 
     @Override
     protected void configureTaskDefaults(CodeNarc task, String baseName) {
-        def config = project.configurations['codenarc']
-        config.incoming.beforeResolve {
-            if (config.dependencies.empty) {
-                config.dependencies.add(project.dependencies.create("org.codenarc:CodeNarc:$extension.toolVersion"))
+        def codenarcConfiguration = project.configurations['codenarc']
+        codenarcConfiguration.incoming.beforeResolve {
+            if (codenarcConfiguration.dependencies.empty) {
+                codenarcConfiguration.dependencies.add(project.dependencies.create("org.codenarc:CodeNarc:$extension.toolVersion"))
             }
         }
         task.conventionMapping.with {
-            codenarcClasspath = { config }
-            configFile = { extension.configFile }
+            codenarcClasspath = { codenarcConfiguration }
+            config = { extension.config }
             maxPriority1Violations = { extension.maxPriority1Violations }
             maxPriority2Violations = { extension.maxPriority2Violations }
             maxPriority3Violations = { extension.maxPriority3Violations }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy
deleted file mode 100644
index 1e7cb80..0000000
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeQualityPlugin.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.plugins.quality
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.plugins.ReportingBasePlugin
-import org.gradle.util.DeprecationLogger
-
-/**
- * A plugin which measures and enforces code quality for Java and Groovy projects.
- *
- * @deprecated use {@link CheckstylePlugin} and {@link CodeNarcPlugin} instead
- */
- at Deprecated
-public class CodeQualityPlugin implements Plugin<Project> {
-    private Project project
-
-    static final String CHECKSTYLE_MAIN_TASK = "checkstyleMain"
-    static final String CHECKSTYLE_TEST_TASK = "checkstyleTest"
-    static final String CODE_NARC_MAIN_TASK = "codenarcMain"
-    static final String CODE_NARC_TEST_TASK = "codenarcTest"
-
-    void apply(Project project) {
-        DeprecationLogger.nagUserOfReplacedPlugin('code-quality', 'checkstyle or codenarc')
-
-        this.project = project
-
-        project.plugins.apply(ReportingBasePlugin)
-        configureCheckstyle()
-        configureCodeNarc()
-    }
-
-    private void configureCheckstyle() {
-        def javaPluginConvention = new JavaCodeQualityPluginConvention(project)
-        project.convention.plugins.javaCodeQuality = javaPluginConvention
-
-        project.plugins.apply(CheckstylePlugin)
-        project.checkstyle.conventionMapping.with {
-            configFile = { javaPluginConvention.checkstyleConfigFile }
-            configProperties = { javaPluginConvention.checkstyleProperties }
-            reportsDir = { javaPluginConvention.checkstyleResultsDir }
-        }
-    }
-
-    private void configureCodeNarc() {
-        def groovyPluginConvention = new GroovyCodeQualityPluginConvention(project)
-        project.convention.plugins.groovyCodeQuality = groovyPluginConvention
-
-        project.plugins.apply(CodeNarcPlugin)
-        project.codenarc.conventionMapping.with {
-            configFile = { groovyPluginConvention.codeNarcConfigFile }
-            reportFormat = { groovyPluginConvention.codeNarcReportsFormat }
-            reportsDir = { groovyPluginConvention.codeNarcReportsDir }
-        }
-    }
-}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugs.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugs.groovy
index 4e7083e..5adf484 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugs.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugs.groovy
@@ -15,23 +15,22 @@
  */
 package org.gradle.api.plugins.quality
 
+import groovy.transform.PackageScope
 import org.gradle.api.GradleException
+import org.gradle.api.Incubating
+import org.gradle.api.JavaVersion
 import org.gradle.api.file.FileCollection
+import org.gradle.api.logging.LogLevel
 import org.gradle.api.plugins.quality.internal.FindBugsReportsImpl
-import org.gradle.api.plugins.quality.internal.findbugs.FindBugsWorkerManager
-import org.gradle.api.plugins.quality.internal.findbugs.FindBugsResult
-import org.gradle.api.plugins.quality.internal.findbugs.FindBugsSpec
-import org.gradle.api.plugins.quality.internal.findbugs.FindBugsSpecBuilder
+import org.gradle.api.plugins.quality.internal.findbugs.*
 import org.gradle.api.reporting.Reporting
+import org.gradle.api.resources.TextResource
 import org.gradle.api.tasks.*
-import org.gradle.api.logging.LogLevel
 import org.gradle.internal.Factory
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.logging.ConsoleRenderer
 import org.gradle.process.internal.WorkerProcessBuilder
 
-import groovy.transform.PackageScope
-
 import javax.inject.Inject
 
 /**
@@ -113,28 +112,48 @@ class FindBugs extends SourceTask implements VerificationTask, Reporting<FindBug
     Collection<String> omitVisitors = []
 
     /**
-     * The filename of a filter specifying which bugs are reported.
+     * A filter specifying which bugs are reported. Replaces the {@code includeFilter} property.
+     *
+     * @since 2.2
      */
-    @InputFile
+    @Incubating
+    @Nested
     @Optional
-    File includeFilter
+    TextResource includeFilterConfig
 
     /**
-     * The filename of a filter specifying bugs to exclude from being reported.
+     * A filter specifying bugs to exclude from being reported. Replaces the {@code excludeFilter} property.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    @Nested
+    @Optional
+    TextResource excludeFilterConfig
+
+    /**
+     * A filter specifying baseline bugs to exclude from being reported.
      */
-    @InputFile
+    @Incubating
+    @Nested
     @Optional
-    File excludeFilter
+    TextResource excludeBugsFilterConfig
 
     @Nested
     private final FindBugsReportsImpl reports
 
-    private final Factory<WorkerProcessBuilder> workerFactory
+    FindBugs() {
+        reports = instantiator.newInstance(FindBugsReportsImpl, this)
+    }
 
     @Inject
-    FindBugs(Instantiator instantiator, Factory<WorkerProcessBuilder> workerFactory) {
-        reports = instantiator.newInstance(FindBugsReportsImpl, this)
-        this.workerFactory = workerFactory
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    Factory<WorkerProcessBuilder> getWorkerProcessBuilderFactory() {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -168,15 +187,59 @@ class FindBugs extends SourceTask implements VerificationTask, Reporting<FindBug
         reports.configure(closure)
     }
 
+    /**
+     * The filename of a filter specifying which bugs are reported.
+     */
+    File getIncludeFilter() {
+        getIncludeFilterConfig()?.asFile()
+    }
+
+    /**
+     * The filename of a filter specifying which bugs are reported.
+     */
+    void setIncludeFilter(File filter) {
+        setIncludeFilterConfig(project.resources.text.fromFile(filter))
+    }
+
+    /**
+     * The filename of a filter specifying bugs to exclude from being reported.
+     */
+    File getExcludeFilter() {
+        getExcludeFilterConfig()?.asFile()
+    }
+
+    /**
+     * The filename of a filter specifying bugs to exclude from being reported.
+     */
+    void setExcludeFilter(File filter) {
+        setExcludeFilterConfig(project.resources.text.fromFile(filter))
+    }
+
+    /**
+     * The filename of a filter specifying baseline bugs to exclude from being reported.
+     */
+    File getExcludeBugsFilter() {
+        getExcludeBugsFilterConfig()?.asFile()
+    }
+
+    /**
+     * The filename of a filter specifying baseline bugs to exclude from being reported.
+     */
+    void setExcludeBugsFilter(File filter) {
+        setExcludeBugsFilterConfig(project.resources.text.fromFile(filter))
+    }
+
     @TaskAction
     void run() {
+        new FindBugsClasspathValidator(JavaVersion.current()).validateClasspath(getFindbugsClasspath().files*.name)
+
         FindBugsSpec spec = generateSpec()
         FindBugsWorkerManager manager = new FindBugsWorkerManager()
 
         logging.captureStandardOutput(LogLevel.DEBUG)
         logging.captureStandardError(LogLevel.DEBUG)
 
-        FindBugsResult result = manager.runWorker(getProject().getProjectDir(), workerFactory, getFindbugsClasspath(), spec)
+        FindBugsResult result = manager.runWorker(getProject().getProjectDir(), workerProcessBuilderFactory, getFindbugsClasspath(), spec)
         evaluateResult(result);
     }
 
@@ -197,6 +260,7 @@ class FindBugs extends SourceTask implements VerificationTask, Reporting<FindBug
             .withOmitVisitors(getOmitVisitors())
             .withExcludeFilter(getExcludeFilter())
             .withIncludeFilter(getIncludeFilter())
+            .withExcludeBugsFilter(getExcludeBugsFilter())
             .configureReports(getReports())
 
         return specBuilder.build()
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsExtension.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsExtension.groovy
index 2355bb9..a186abd 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsExtension.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/FindBugsExtension.groovy
@@ -15,6 +15,10 @@
  */
 package org.gradle.api.plugins.quality
 
+import org.gradle.api.Incubating
+import org.gradle.api.Project
+import org.gradle.api.resources.TextResource
+
 /**
  * Configuration options for the FindBugs plugin. All options have sensible defaults.
  * See the <a href="http://findbugs.sourceforge.net/manual/">FindBugs Manual</a> for additional information
@@ -38,12 +42,19 @@ package org.gradle.api.plugins.quality
  *         omitVisitors = ["FindNonShortCircuit"]
  *         includeFilter = file("$rootProject.projectDir/config/findbugs/includeFilter.xml")
  *         excludeFilter = file("$rootProject.projectDir/config/findbugs/excludeFilter.xml")
+ *         excludeBugsFilter = file("$rootProject.projectDir/config/findbugs/excludeBugsFilter.xml")
  *     }
  * </pre>
  *
  * @see FindBugsPlugin
  */
 class FindBugsExtension extends CodeQualityExtension {
+    private final Project prj
+
+    FindBugsExtension(Project project) {
+        prj = project
+    }
+
     /**
      * The analysis effort level. The value specified should be one of {@code min}, {@code default}, or {@code max}.
      * Higher levels increase precision and find more bugs at the expense of running time and memory consumption.
@@ -70,12 +81,68 @@ class FindBugsExtension extends CodeQualityExtension {
     Collection<String> omitVisitors
 
     /**
+     * A filter specifying which bugs are reported. Replaces the {@code includeFilter} property.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    TextResource includeFilterConfig
+
+    /**
+     * A filter specifying bugs to exclude from being reported. Replaces the {@code excludeFilter} property.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    TextResource excludeFilterConfig
+
+    /**
+     * A filter specifying baseline bugs to exclude from being reported.
+     *
+     * @since 2.4
+     */
+    @Incubating
+    TextResource excludeBugsFilterConfig
+
+    /**
+     * The filename of a filter specifying which bugs are reported.
+     */
+    File getIncludeFilter() {
+        getIncludeFilterConfig()?.asFile()
+    }
+
+    /**
      * The filename of a filter specifying which bugs are reported.
      */
-    File includeFilter
+    void setIncludeFilter(File filter) {
+        setIncludeFilterConfig(prj.resources.text.fromFile(filter))
+    }
+
+    /**
+     * The filename of a filter specifying bugs to exclude from being reported.
+     */
+    File getExcludeFilter() {
+        getExcludeFilterConfig()?.asFile()
+    }
 
     /**
      * The filename of a filter specifying bugs to exclude from being reported.
      */
-    File excludeFilter
+    void setExcludeFilter(File filter) {
+        setExcludeFilterConfig(prj.resources.text.fromFile(filter))
+    }
+
+    /**
+     * The filename of a filter specifying baseline bugs to exclude from being reported.
+     */
+    File getExcludeBugsFilter() {
+        getExcludeBugsFilterConfig()?.asFile()
+    }
+
+    /**
+     * The filename of a filter specifying baseline bugs to exclude from being reported.
+     */
+    void setExcludeBugsFilter(File filter) {
+        setExcludeBugsFilterConfig(prj.resources.text.fromFile(filter))
+    }
 }
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 b76152f..f6eb0a2 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
@@ -34,6 +34,7 @@ import org.gradle.api.tasks.SourceSet
  * @see FindBugsExtension
  */
 class FindBugsPlugin extends AbstractCodeQualityPlugin<FindBugs> {
+    public static final String DEFAULT_FINDBUGS_VERSION = "3.0.1"
     private FindBugsExtension extension
 
     @Override
@@ -61,10 +62,8 @@ class FindBugsPlugin extends AbstractCodeQualityPlugin<FindBugs> {
 
     @Override
     protected CodeQualityExtension createExtension() {
-        extension = project.extensions.create("findbugs", FindBugsExtension)
-        extension.with {
-            toolVersion = "2.0.1"
-        }
+        extension = project.extensions.create("findbugs", FindBugsExtension, project)
+        extension.toolVersion = DEFAULT_FINDBUGS_VERSION
         return extension
     }
 
@@ -86,8 +85,9 @@ class FindBugsPlugin extends AbstractCodeQualityPlugin<FindBugs> {
             reportLevel = { extension.reportLevel }
             visitors = { extension.visitors }
             omitVisitors = { extension.omitVisitors }
-            excludeFilter = { extension.excludeFilter }
-            includeFilter = { extension.includeFilter }
+            excludeFilterConfig = { extension.excludeFilterConfig }
+            includeFilterConfig = { extension.includeFilterConfig }
+            excludeBugsFilterConfig = { extension.excludeBugsFilterConfig }
  
         }
         task.reports.all { Report report ->
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy
deleted file mode 100644
index ddc705e..0000000
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/GroovyCodeQualityPluginConvention.groovy
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.quality
-
-import org.gradle.api.Project
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.reporting.ReportingExtension
-
-class GroovyCodeQualityPluginConvention {
-    /**
-     * The name of the CodeNarc configuration file, relative to the project directory.
-     */
-    String codeNarcConfigFileName
-
-    /**
-     * The output format of the generated CodeNarc report.
-     */
-    String codeNarcReportsFormat
-
-    /**
-     * The name of the directory to write CodeNarc reports into.
-     */
-    String codeNarcReportsDirName
-
-    private final ProjectInternal project
-
-    def GroovyCodeQualityPluginConvention(Project project) {
-        this.project = project
-        codeNarcConfigFileName = 'config/codenarc/codenarc.xml'
-        codeNarcReportsFormat = "html"
-        codeNarcReportsDirName = 'codenarc'
-    }
-
-    /**
-     * The CodeNarc configuration file.
-     */
-    File getCodeNarcConfigFile() {
-        project.file(codeNarcConfigFileName)
-    }
-
-    /**
-     * The directory to write CodeNarc reports into.
-     */
-    File getCodeNarcReportsDir() {
-        project.extensions.getByType(ReportingExtension).file(codeNarcReportsDirName)
-    }
-}
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 b493c44..bc6ccb4 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
@@ -54,12 +54,18 @@ class JDepend extends DefaultTask implements Reporting<JDependReports> {
     @Nested
     private final JDependReportsImpl reports
 
-    private final IsolatedAntBuilder antBuilder
+    JDepend() {
+        reports = instantiator.newInstance(JDependReportsImpl, this)
+    }
 
     @Inject
-    JDepend(Instantiator instantiator, IsolatedAntBuilder antBuilder) {
-        this.antBuilder = antBuilder
-        reports = instantiator.newInstance(JDependReportsImpl, this)
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     /**
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependPlugin.groovy
index e2863f1..a43fd30 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JDependPlugin.groovy
@@ -35,6 +35,7 @@ import org.gradle.api.tasks.SourceSet
  * @see JDepend
  */
 class JDependPlugin extends AbstractCodeQualityPlugin<JDepend> {
+    public static final String DEFAULT_JDEPEND_VERSION = "2.9.1"
     private JDependExtension extension
 
     @Override
@@ -51,25 +52,25 @@ class JDependPlugin extends AbstractCodeQualityPlugin<JDepend> {
     protected CodeQualityExtension createExtension() {
         extension = project.extensions.create("jdepend", JDependExtension)
         extension.with {
-            toolVersion = "2.9.1"
+            toolVersion = DEFAULT_JDEPEND_VERSION
         }
         return extension
     }
 
     @Override
     protected void configureTaskDefaults(JDepend task, String baseName) {
-        task.conventionMapping.with {
-            jdependClasspath = {
-                def config = project.configurations['jdepend']
-                if (config.dependencies.empty) {
-                    project.dependencies {
-                        jdepend "jdepend:jdepend:$extension.toolVersion"
-                        jdepend("org.apache.ant:ant-jdepend:1.8.2")
-                    }
+        def config = project.configurations['jdepend']
+        config.incoming.beforeResolve {
+            if (config.dependencies.empty) {
+                project.dependencies {
+                    jdepend "jdepend:jdepend:$extension.toolVersion"
+                    jdepend("org.apache.ant:ant-jdepend:1.9.4")
                 }
-                config
             }
         }
+        task.conventionMapping.with {
+            jdependClasspath = { config }
+        }
         task.reports.all { Report report ->
             report.conventionMapping.with {
                 enabled = { report.name == "xml" }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JavaCodeQualityPluginConvention.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JavaCodeQualityPluginConvention.groovy
deleted file mode 100644
index 2bc63d9..0000000
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/JavaCodeQualityPluginConvention.groovy
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.quality
-
-import org.gradle.api.Project
-import org.gradle.api.internal.file.FileLookup
-import org.gradle.api.internal.project.ProjectInternal
-
-class JavaCodeQualityPluginConvention {
-
-    /**
-     * The name of the Checkstyle configuration file, relative to the project directory.
-     */
-    String checkstyleConfigFileName
-
-    /**
-     * The name of the directory to write Checkstyle results to, relative to the build directory.
-     */
-    String checkstyleResultsDirName
-
-    /**
-     * The set of properties to substitute into the Checkstyle configuration file.
-     */
-    Map<String, Object> checkstyleProperties = [:]
-
-    private ProjectInternal project
-
-    def JavaCodeQualityPluginConvention(Project project) {
-        this.project = project
-        checkstyleConfigFileName = 'config/checkstyle/checkstyle.xml'
-        checkstyleResultsDirName = 'checkstyle'
-    }
-
-    /**
-     * The Checkstyle configuration file.
-     */
-    File getCheckstyleConfigFile() {
-        project.file(checkstyleConfigFileName)
-    }
-
-    /**
-     * The directory to write the Checkstyle results into.
-     */
-    File getCheckstyleResultsDir() {
-        project.services.get(FileLookup).getFileResolver(project.buildDir).resolve(checkstyleResultsDirName)
-    }
-}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.groovy
index 02e545c..761c445 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.groovy
@@ -16,11 +16,16 @@
 package org.gradle.api.plugins.quality
 
 import org.gradle.api.GradleException
+import org.gradle.api.Incubating
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.plugins.quality.internal.PmdReportsImpl
 import org.gradle.api.reporting.Reporting
+import org.gradle.api.resources.TextResource
 import org.gradle.api.tasks.*
+import org.gradle.internal.nativeintegration.console.ConsoleDetector
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData
+import org.gradle.internal.nativeintegration.services.NativeServices
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.logging.ConsoleRenderer
 
@@ -54,6 +59,23 @@ class Pmd extends SourceTask implements VerificationTask, Reporting<PmdReports>
     TargetJdk targetJdk
 
     /**
+     * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that
+     * it does not currently support multiple rule sets.
+     *
+     * See the
+     * <a href="http://pmd.sourceforge.net/howtomakearuleset.html">official documentation</a>
+     * for how to author a rule set.
+     *
+     * Example: ruleSetConfig = resources.text.fromFile(resources.file("config/pmd/myRuleSets.xml"))
+     *
+     * @since 2.2
+     */
+    @Incubating
+    @Nested
+    @Optional
+    TextResource ruleSetConfig
+
+    /**
      * The custom rule set files to be used. See the <a href="http://pmd.sourceforge.net/howtomakearuleset.html">official documentation</a> for
      * how to author a rule set file.
      *
@@ -65,8 +87,6 @@ class Pmd extends SourceTask implements VerificationTask, Reporting<PmdReports>
     @Nested
     private final PmdReportsImpl reports
 
-    private final IsolatedAntBuilder antBuilder
-
     /**
      * Whether or not to allow the build to continue if there are warnings.
      *
@@ -74,9 +94,24 @@ class Pmd extends SourceTask implements VerificationTask, Reporting<PmdReports>
      */
     boolean ignoreFailures
 
-    @Inject Pmd(Instantiator instantiator, IsolatedAntBuilder antBuilder) {
+    /**
+     * Whether or not to write PMD results to {@code System.out}.
+     */
+    @Incubating
+    boolean consoleOutput
+
+    Pmd() {
         reports = instantiator.newInstance(PmdReportsImpl, this)
-        this.antBuilder = antBuilder
+    }
+
+    @Inject
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     @TaskAction
@@ -97,25 +132,38 @@ class Pmd extends SourceTask implements VerificationTask, Reporting<PmdReports>
             }
         }
 
-        antBuilder.withClasspath(getPmdClasspath()).execute {
+        antBuilder.withClasspath(getPmdClasspath()).execute { a ->
             ant.taskdef(name: 'pmd', classname: 'net.sourceforge.pmd.ant.PMDTask')
-                ant.pmd(antPmdArgs) {
-                    getSource().addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
-                    getRuleSets().each {
-                        ruleset(it)
-                    }
-                    getRuleSetFiles().each {
-                        ruleset(it)
-                    }
+            ant.pmd(antPmdArgs) {
+                getSource().addToAntBuilder(ant, 'fileset', FileCollection.AntType.FileSet)
+                getRuleSets().each {
+                    ruleset(it)
+                }
+                getRuleSetFiles().each {
+                    ruleset(it)
+                }
+                def ruleSetConfig = getRuleSetConfig()
+                if (ruleSetConfig != null) {
+                    ruleset(ruleSetConfig.asFile())
+                }
 
-                    if (reports.html.enabled) {
-                        assert reports.html.destination.parentFile.exists()
-                        formatter(type: prePmd5 ? "betterhtml" : "html", toFile: reports.html.destination)
-                    }
-                    if (reports.xml.enabled) {
-                        formatter(type: 'xml', toFile: reports.xml.destination)
+                if (reports.html.enabled) {
+                    assert reports.html.destination.parentFile.exists()
+                    formatter(type: prePmd5 ? "betterhtml" : "html", toFile: reports.html.destination)
+                }
+                if (reports.xml.enabled) {
+                    formatter(type: 'xml', toFile: reports.xml.destination)
+                }
+
+                if (getConsoleOutput()) {
+                    def consoleOutputType = 'text'
+                    if (stdOutIsAttachedToTerminal()) {
+                        consoleOutputType = 'textcolor'
                     }
+                    a.builder.saveStreams = false
+                    formatter(type: consoleOutputType, toConsole: true)
                 }
+            }
             def failureCount = ant.project.properties["pmdFailureCount"]
             if (failureCount) {
                 def message = "$failureCount PMD rule violations were found."
@@ -133,6 +181,12 @@ class Pmd extends SourceTask implements VerificationTask, Reporting<PmdReports>
         }
     }
 
+    boolean stdOutIsAttachedToTerminal() {
+        ConsoleDetector consoleDetector = NativeServices.getInstance().get(ConsoleDetector.class)
+        ConsoleMetaData consoleMetaData = consoleDetector.getConsole()
+        consoleMetaData?.stdOut
+    }
+
     /**
      * Configures the reports to be generated by this task.
      */
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 d36d82e..afdd3eb 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
@@ -15,8 +15,10 @@
  */
 package org.gradle.api.plugins.quality
 
+import org.gradle.api.Incubating
 import org.gradle.api.Project
 import org.gradle.api.file.FileCollection
+import org.gradle.api.resources.TextResource
 
 /**
  * Configuration options for the PMD plugin.
@@ -64,6 +66,21 @@ class PmdExtension extends CodeQualityExtension {
     }
 
     /**
+     * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that
+     * it does not currently support multiple rule sets.
+     *
+     * See the
+     * <a href="http://pmd.sourceforge.net/howtomakearuleset.html">official documentation</a>
+     * for how to author a rule set.
+     *
+     * Example: ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")
+     *
+     * @since 2.2
+     */
+    @Incubating
+    TextResource ruleSetConfig
+
+    /**
      * The custom rule set files to be used. See the <a href="http://pmd.sourceforge.net/howtomakearuleset.html">official documentation</a> for
      * how to author a rule set file.
      *
@@ -82,4 +99,11 @@ class PmdExtension extends CodeQualityExtension {
     void ruleSetFiles(Object... ruleSetFiles) {
         this.ruleSetFiles.add(project.files(ruleSetFiles))
     }
+
+    /**
+     * Whether or not to write PMD results to {@code System.out}.
+     */
+    @Incubating
+    boolean consoleOutput
+
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.groovy
index da23b35..0c84ea9 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.groovy
@@ -33,6 +33,7 @@ import org.gradle.util.VersionNumber
  * @see Pmd
  */
 class PmdPlugin extends AbstractCodeQualityPlugin<Pmd> {
+    public static final String DEFAULT_PMD_VERSION = "5.2.3"
     private PmdExtension extension
 
     @Override
@@ -49,7 +50,7 @@ class PmdPlugin extends AbstractCodeQualityPlugin<Pmd> {
     protected CodeQualityExtension createExtension() {
         extension = project.extensions.create("pmd", PmdExtension, project)
         extension.with {
-            toolVersion = "4.3"
+            toolVersion = DEFAULT_PMD_VERSION
             // NOTE: should change default rule set to java-basic once we bump default version to 5.0+
             // this will also require a change to Pmd.run() (convert java-basic to basic for old versions,
             // instead of basic to java-basic for new versions)
@@ -78,16 +79,17 @@ class PmdPlugin extends AbstractCodeQualityPlugin<Pmd> {
         config.incoming.beforeResolve {
             if (config.dependencies.empty) {
                 VersionNumber version = VersionNumber.parse(extension.toolVersion)
-                String dependency = (version < VersionNumber.parse("5.0.0"))?
-                    "pmd:pmd:$extension.toolVersion" : "net.sourceforge.pmd:pmd:$extension.toolVersion"
+                String dependency = calculateDefaultDependencyNotation(version)
                 config.dependencies.add(project.dependencies.create(dependency))
             }
         }
         task.conventionMapping.with {
             pmdClasspath = { config }
             ruleSets = { extension.ruleSets }
+            ruleSetConfig = { extension.ruleSetConfig }
             ruleSetFiles = { extension.ruleSetFiles }
             ignoreFailures = { extension.ignoreFailures }
+            consoleOutput = { extension.consoleOutput }
             targetJdk = { extension.targetJdk }
             task.reports.all { report ->
                 report.conventionMapping.with {
@@ -98,6 +100,15 @@ class PmdPlugin extends AbstractCodeQualityPlugin<Pmd> {
         }
     }
 
+    private String calculateDefaultDependencyNotation(VersionNumber toolVersion) {
+        if (toolVersion < VersionNumber.version(5)) {
+            return "pmd:pmd:$extension.toolVersion"
+        } else if (toolVersion < VersionNumber.parse("5.2.0")) {
+            return "net.sourceforge.pmd:pmd:$extension.toolVersion"
+        }
+        return "net.sourceforge.pmd:pmd-java:$extension.toolVersion"
+    }
+
     @Override
     protected void configureForSourceSet(SourceSet sourceSet, Pmd task) {
         task.with {
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 7a7b4f8..6a376c9 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
@@ -31,7 +31,7 @@ abstract class AbstractCodeQualityPlugin<T> implements Plugin<ProjectInternal> {
         this.project = project
 
         beforeApply()
-        project.plugins.apply(ReportingBasePlugin)
+        project.pluginManager.apply(ReportingBasePlugin)
         createConfigurations()
         extension = createExtension()
         configureExtensionRule()
@@ -72,8 +72,6 @@ abstract class AbstractCodeQualityPlugin<T> implements Plugin<ProjectInternal> {
             exclude group: 'ant', module: 'ant'
             exclude group: 'org.apache.ant', module: 'ant'
             exclude group: 'org.apache.ant', module: 'ant-launcher'
-            exclude group: 'org.codehaus.groovy', module: 'groovy'
-            exclude group: 'org.codehaus.groovy', module: 'groovy-all'
             exclude group: 'org.slf4j', module: 'slf4j-api'
             exclude group: 'org.slf4j', module: 'jcl-over-slf4j'
             exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
@@ -120,7 +118,7 @@ abstract class AbstractCodeQualityPlugin<T> implements Plugin<ProjectInternal> {
 
     private void configureCheckTask() {
         project.plugins.withType(basePlugin) {
-            project.tasks['check'].dependsOn { extension.sourceSets.collect { it.getTaskName(taskBaseName, null) }}
+            project.tasks['check'].dependsOn { extension.sourceSets.collect { it.getTaskName(taskBaseName, null) } }
         }
     }
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidator.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidator.java
new file mode 100644
index 0000000..87d3f68
--- /dev/null
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.quality.internal.findbugs;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.JavaVersion;
+import org.gradle.util.VersionNumber;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FindBugsClasspathValidator {
+
+    private final JavaVersion javaVersion;
+
+    public FindBugsClasspathValidator(JavaVersion javaVersion) {
+        this.javaVersion = javaVersion;
+    }
+
+    public void validateClasspath(Iterable<String> fileNamesOnClasspath) {
+        VersionNumber v = getFindbugsVersion(fileNamesOnClasspath);
+        boolean java6orLess = javaVersion.compareTo(JavaVersion.VERSION_1_7) < 0;
+        boolean findbugs3orMore = v.getMajor() > 2;
+        if (java6orLess && findbugs3orMore) {
+            throw new FindBugsVersionTooHighException("The version of FindBugs (" + v + ") inferred from FindBugs classpath is too high to work with currently used Java version (" + javaVersion + ")."
+                    + " Please use lower version of FindBugs or use newer version of Java. Inspected FindBugs classpath: " + fileNamesOnClasspath);
+        }
+        boolean java8orMore = javaVersion.compareTo(JavaVersion.VERSION_1_7) > 0;
+        boolean findbugs2orLess = v.getMajor() < 3;
+        if (java8orMore && findbugs2orLess) {
+            throw new FindBugsVersionTooLowException("The version of FindBugs (" + v + ") inferred from FindBugs classpath is too low to work with currently used Java version (" + javaVersion + ")."
+                    + " Please use higher version of FindBugs. Inspected FindBugs classpath: " + fileNamesOnClasspath);
+        }
+    }
+
+    static class FindBugsVersionTooLowException extends GradleException {
+        FindBugsVersionTooLowException(String message) {
+            super(message);
+        }
+    }
+    static class FindBugsVersionTooHighException extends GradleException {
+        FindBugsVersionTooHighException(String message) {
+            super(message);
+        }
+    }
+
+    private VersionNumber getFindbugsVersion(Iterable<String> classpath) {
+        for (String f: classpath) {
+            Matcher m = Pattern.compile("findbugs-(.*)\\.jar").matcher(f);
+            if (m.matches()) {
+                return VersionNumber.parse(m.group(1));
+            }
+        }
+        throw new GradleException("Unable to infer the version of FindBugs from currently specified FindBugs classpath: " + classpath);
+    }
+}
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsResult.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsResult.java
index 5032b15..9db3b72 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsResult.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsResult.java
@@ -23,13 +23,13 @@ public class FindBugsResult implements Serializable {
     private final int bugCount;
     private final int missingClassCount;
     private final int errorCount;
-    private final Exception exception;
+    private final Throwable exception;
 
     public FindBugsResult(int bugCount, int missingClassCount, int errorCount) {
         this(bugCount, missingClassCount, errorCount, null);
     }
 
-    public FindBugsResult(int bugCount, int missingClassCount, int errorCount, Exception exception) {
+    public FindBugsResult(int bugCount, int missingClassCount, int errorCount, Throwable exception) {
         this.bugCount = bugCount;
         this.missingClassCount = missingClassCount;
         this.errorCount = errorCount;
@@ -48,7 +48,7 @@ public class FindBugsResult implements Serializable {
         return errorCount;
     }
 
-    public Exception getException() {
+    public Throwable getException() {
         return exception;
     }
 }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsSpecBuilder.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsSpecBuilder.java
index 47daf3d..c7946a8 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsSpecBuilder.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsSpecBuilder.java
@@ -47,6 +47,7 @@ public class FindBugsSpecBuilder {
     private Collection<String> omitVisitors;
     private File excludeFilter;
     private File includeFilter;
+    private File excludeBugsFilter;
     private boolean debugEnabled;
 
     public FindBugsSpecBuilder(FileCollection classes) {
@@ -128,6 +129,16 @@ public class FindBugsSpecBuilder {
         return this;
     }
 
+    public FindBugsSpecBuilder withExcludeBugsFilter(File excludeBugsFilter) {
+        if (excludeBugsFilter != null && !excludeBugsFilter.canRead()) {
+            String errorStr = String.format("Cannot read file specified for FindBugs 'excludeBugsFilter' property: %s", excludeBugsFilter);
+            throw new InvalidUserDataException(errorStr);
+        }
+
+        this.excludeBugsFilter = excludeBugsFilter;
+        return this;
+    }
+
     public FindBugsSpecBuilder withDebugging(boolean debugEnabled){
         this.debugEnabled = debugEnabled;
         return this;
@@ -203,6 +214,11 @@ public class FindBugsSpecBuilder {
             args.add(includeFilter.getPath());
         }
 
+        if (has(excludeBugsFilter)) {
+            args.add("-excludeBugs");
+            args.add(excludeBugsFilter.getPath());
+        }
+
         for (File classFile : classes.getFiles()) {
             args.add(classFile.getAbsolutePath());
         }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServer.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServer.java
index 0c8f9e1..236d8ba 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServer.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServer.java
@@ -32,20 +32,19 @@ public class FindBugsWorkerServer implements Action<WorkerProcessContext>, Seria
     }
 
     public void execute(WorkerProcessContext context) {
-        final FindBugsResult result = execute();
+        final FindBugsResult result = execute(new FindBugsExecuter());
         final FindBugsWorkerClientProtocol clientProtocol = context.getServerConnection().addOutgoing(FindBugsWorkerClientProtocol.class);
         context.getServerConnection().connect();
         clientProtocol.executed(result);
     }
 
-    public FindBugsResult execute() {
+    FindBugsResult execute(FindBugsExecuter executer) {
         LOGGER.debug("Executing FindBugs worker.");
         try {
-            FindBugsExecuter findBugsExecuter = new FindBugsExecuter();
-            return findBugsExecuter.runFindbugs(spec);
-        } catch (Exception e) {
-            LOGGER.warn("Exception occurred while running FindBugs.", e);
-            return new FindBugsResult(0, 0, 1, e); //mark result with error count 1
+            return executer.runFindbugs(spec);
+        } catch (Throwable t) {
+            LOGGER.warn("Exception occurred while running FindBugs.", t);
+            return new FindBugsResult(0, 0, 1, t); //mark result with error count 1
         }
     }
 }
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties
deleted file mode 100755
index d23cb61..0000000
--- a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/code-quality.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.api.plugins.quality.CodeQualityPlugin
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/checkstyle.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.checkstyle.properties
similarity index 100%
rename from subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/checkstyle.properties
rename to subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.checkstyle.properties
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/codenarc.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.codenarc.properties
similarity index 100%
rename from subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/codenarc.properties
rename to subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.codenarc.properties
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/findbugs.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.findbugs.properties
similarity index 100%
rename from subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/findbugs.properties
rename to subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.findbugs.properties
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/jdepend.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.jdepend.properties
similarity index 100%
rename from subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/jdepend.properties
rename to subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.jdepend.properties
diff --git a/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/pmd.properties b/subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.pmd.properties
similarity index 100%
rename from subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/pmd.properties
rename to subprojects/code-quality/src/main/resources/META-INF/gradle-plugins/org.gradle.pmd.properties
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 d3afb34..613b5bf 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
@@ -15,12 +15,12 @@
  */
 package org.gradle.api.plugins.quality
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
 import org.gradle.util.TestUtil
-import org.gradle.api.plugins.JavaBasePlugin
-
 import spock.lang.Specification
 
 import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
@@ -28,10 +28,10 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class CheckstylePluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(CheckstylePlugin)
+        project.pluginManager.apply(CheckstylePlugin)
     }
 
     def "applies reporting-base plugin"() {
@@ -53,13 +53,14 @@ class CheckstylePluginTest extends Specification {
         expect:
         CheckstyleExtension extension = project.extensions.checkstyle
         extension.configFile == project.file("config/checkstyle/checkstyle.xml")
+        extension.config.inputFiles.singleFile == project.file("config/checkstyle/checkstyle.xml")
         extension.configProperties == [:]
         extension.reportsDir == project.file("build/reports/checkstyle")
         !extension.ignoreFailures
     }
 
     def "configures checkstyle task for each source set"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -80,8 +81,9 @@ class CheckstylePluginTest extends Specification {
             assert checkstyleClasspath == project.configurations["checkstyle"]
             assert classpath == sourceSet.output
             assert configFile == project.file("config/checkstyle/checkstyle.xml")
+            assert config.inputFiles.singleFile == project.file("config/checkstyle/checkstyle.xml")
             assert configProperties == [:]
-            assert resultFile == project.file("build/reports/checkstyle/${sourceSet.name}.xml")
+            assert reports.xml.destination == project.file("build/reports/checkstyle/${sourceSet.name}.xml")
             assert !ignoreFailures
             assert showViolations
         }
@@ -95,13 +97,14 @@ class CheckstylePluginTest extends Specification {
         task.source.isEmpty()
         task.checkstyleClasspath == project.configurations.checkstyle
         task.configFile == project.file("config/checkstyle/checkstyle.xml")
+        task.config.inputFiles.singleFile == project.file("config/checkstyle/checkstyle.xml")
         task.configProperties == [:]
-        task.resultFile == project.file("build/reports/checkstyle/custom.xml")
+        task.reports.xml.destination == project.file("build/reports/checkstyle/custom.xml")
         !task.ignoreFailures
     }
 
     def "adds checkstyle tasks to check lifecycle task"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -113,7 +116,7 @@ class CheckstylePluginTest extends Specification {
     }
     
     def "can customize settings via extension"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -122,11 +125,11 @@ class CheckstylePluginTest extends Specification {
         
         project.checkstyle {
             sourceSets = [project.sourceSets.main]
-            configFile = project.file("checkstyle-config")
+            config = project.resources.text.fromFile("checkstyle-config")
             configProperties = [foo: "foo"]
             reportsDir = project.file("checkstyle-reports")
             ignoreFailures = true
-            displayViolations = true
+            showViolations = true
         }
         
         expect:
@@ -145,17 +148,18 @@ class CheckstylePluginTest extends Specification {
             assert source as List == sourceSet.allJava as List
             assert checkstyleClasspath == project.configurations["checkstyle"]
             assert configFile == project.file("checkstyle-config")
+            assert config.inputFiles.singleFile == project.file("checkstyle-config")
             assert configProperties == [foo: "foo"]
-            assert resultFile == project.file("checkstyle-reports/${sourceSet.name}.xml")
+            assert reports.xml.destination == project.file("checkstyle-reports/${sourceSet.name}.xml")
             assert ignoreFailures
             assert showViolations
         }
     }
-    
+
     def "can customize any additional checkstyle tasks via extension"() {
         def task = project.tasks.create("checkstyleCustom", Checkstyle)
         project.checkstyle {
-            configFile = project.file("checkstyle-config")
+            config = project.resources.text.fromFile("checkstyle-config")
             configProperties = [foo: "foo"]
             reportsDir = project.file("checkstyle-reports")
             ignoreFailures = true
@@ -166,9 +170,22 @@ class CheckstylePluginTest extends Specification {
         task.source.isEmpty()
         task.checkstyleClasspath == project.configurations.checkstyle
         task.configFile == project.file("checkstyle-config")
+        task.config.inputFiles.singleFile == project.file("checkstyle-config")
         task.configProperties == [foo: "foo"]
-        task.resultFile == project.file("checkstyle-reports/custom.xml")
+        task.reports.xml.destination == project.file("checkstyle-reports/custom.xml")
         task.ignoreFailures
     }
-    
+
+    def "can use legacy configFile extension property"() {
+        project.pluginManager.apply(JavaPlugin)
+
+        project.checkstyle {
+            configFile = project.file("checkstyle-config")
+        }
+
+        expect:
+        project.checkstyle.configFile == project.file("checkstyle-config") // computed property
+        project.tasks.checkstyleMain.configFile == project.file("checkstyle-config")
+        project.tasks.checkstyleTest.configFile == project.file("checkstyle-config")
+    }
 }
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 c53f5e8..5781a36 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
@@ -21,15 +21,16 @@ import org.gradle.testfixtures.ProjectBuilder
 import spock.lang.Specification
 
 class CheckstyleTest extends Specification {
-    def "default configuration"() {
-        def project = ProjectBuilder.builder().build()
-        def checkstyle = project.tasks.create("checkstyle", Checkstyle)
+    def project = ProjectBuilder.builder().build()
+    def checkstyle = project.tasks.create("checkstyle", Checkstyle)
 
+    def "default configuration"() {
         expect:
         with(checkstyle) {
             checkstyleClasspath == null
             classpath == null
             configFile == null
+            config == null
             configProperties == [:]
             !reports.xml.enabled
             reports.xml.destination == null
@@ -38,4 +39,12 @@ class CheckstyleTest extends Specification {
             showViolations
         }
     }
+
+    def "can use legacy configFile property"() {
+        checkstyle.configFile = project.file("config/file.txt")
+
+        expect:
+        checkstyle.configFile == project.file("config/file.txt")
+        checkstyle.config.inputFiles.singleFile == project.file("config/file.txt")
+    }
 }
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 181a965..b6dfd2b 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
@@ -15,21 +15,23 @@
  */
 package org.gradle.api.plugins.quality
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.plugins.GroovyBasePlugin
+import org.gradle.api.plugins.GroovyPlugin
+import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
 import org.gradle.util.TestUtil
 import spock.lang.Specification
+
 import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
 import static org.hamcrest.Matchers.hasItems
 import static spock.util.matcher.HamcrestSupport.that
-import org.gradle.api.plugins.ReportingBasePlugin
 
 class CodeNarcPluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(CodeNarcPlugin)
+        project.pluginManager.apply(CodeNarcPlugin)
     }
 
     def "applies reporting-base plugin"() {
@@ -50,6 +52,7 @@ class CodeNarcPluginTest extends Specification {
     def "adds codenarc extension"() {
         expect:
         CodeNarcExtension codenarc = project.extensions.codenarc
+        codenarc.config.inputFiles.singleFile == project.file("config/codenarc/codenarc.xml")
         codenarc.configFile == project.file("config/codenarc/codenarc.xml")
         codenarc.maxPriority1Violations == 0
         codenarc.maxPriority2Violations == 0
@@ -61,7 +64,7 @@ class CodeNarcPluginTest extends Specification {
     }
 
     def "adds codenarc task for each source set"() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
         project.sourceSets {
             main
             test
@@ -81,18 +84,19 @@ class CodeNarcPluginTest extends Specification {
             assert description == "Run CodeNarc analysis for ${sourceSet.name} classes"
             assert source as List == sourceSet.allGroovy  as List
             assert codenarcClasspath == project.configurations.codenarc
+            assert config.inputFiles.singleFile == project.file("config/codenarc/codenarc.xml")
             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 reports.enabled*.name == ["html"]
+            assert reports.html.destination == project.file("build/reports/codenarc/${sourceSet.name}.html")
             assert ignoreFailures == false
         }
     }
 
     def "can customize per-source-set tasks via extension"() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
         project.sourceSets {
             main
             test
@@ -100,7 +104,6 @@ class CodeNarcPluginTest extends Specification {
         }
 
         project.codenarc {
-            checkTasks = ["codenarcMain"]
             configFile = project.file("codenarc-config")
             maxPriority1Violations = 10
             maxPriority2Violations = 50
@@ -123,12 +126,13 @@ class CodeNarcPluginTest extends Specification {
             assert description == "Run CodeNarc analysis for ${sourceSet.name} classes"
             assert source as List == sourceSet.allGroovy as List
             assert codenarcClasspath == project.configurations.codenarc
+            assert config.inputFiles.singleFile == project.file("codenarc-config")
             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 reports.enabled*.name == ["xml"]
+            assert reports.xml.destination == project.file("codenarc-reports/${sourceSet.name}.xml")
             assert ignoreFailures == true
         }
     }
@@ -140,12 +144,13 @@ class CodeNarcPluginTest extends Specification {
         task.description == null
         task.source.isEmpty()
         task.codenarcClasspath == project.configurations.codenarc
+        task.config.inputFiles.singleFile == project.file("config/codenarc/codenarc.xml")
         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.reports.enabled*.name == ["html"]
+        task.reports.html.destination == project.file("build/reports/codenarc/custom.html")
         task.ignoreFailures == false
     }
 
@@ -153,7 +158,7 @@ class CodeNarcPluginTest extends Specification {
         def task = project.tasks.create("codenarcCustom", CodeNarc)
 
         project.codenarc {
-            configFile = project.file("codenarc-config")
+            config = project.resources.text.fromFile("codenarc-config")
             maxPriority1Violations = 10
             maxPriority2Violations = 50
             maxPriority3Violations = 200
@@ -166,17 +171,18 @@ class CodeNarcPluginTest extends Specification {
         task.description == null
         task.source.isEmpty()
         task.codenarcClasspath == project.configurations.codenarc
+        task.config.inputFiles.singleFile == project.file("codenarc-config")
         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.reports.enabled*.name == ["xml"]
+        task.reports.xml.destination == project.file("codenarc-reports/custom.xml")
         task.ignoreFailures == true
     }
     
     def "adds codenarc tasks from each source sets to check lifecycle task"() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
         project.sourceSets {
             main
             test
@@ -190,7 +196,7 @@ class CodeNarcPluginTest extends Specification {
     }
 
     def "can customize which tasks are added to check lifecycle task"() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
         project.sourceSets {
             main
             test
@@ -221,4 +227,17 @@ class CodeNarcPluginTest extends Specification {
             assert xml.destination == project.file("build/foo.xml")
         }
     }
+
+    def "can use legacy configFile extension property"() {
+        project.pluginManager.apply(GroovyPlugin)
+
+        project.codenarc {
+            configFile = project.file("codenarc-config")
+        }
+
+        expect:
+        project.codenarc.configFile == project.file("codenarc-config") // computed property
+        project.tasks.codenarcMain.configFile == project.file("codenarc-config")
+        project.tasks.codenarcTest.configFile == project.file("codenarc-config")
+    }
 }
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
deleted file mode 100644
index 274d893..0000000
--- a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodeQualityPluginTest.groovy
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.quality
-
-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.api.tasks.TaskDependencyMatchers
-import org.gradle.util.TestUtil
-import org.junit.Test
-import static org.gradle.util.Matchers.*
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.GroovyBasePlugin
-
-class CodeQualityPluginTest {
-    private final Project project = TestUtil.createRootProject()
-    private final CodeQualityPlugin plugin = new CodeQualityPlugin()
-
-    @Test public void appliesCheckstyleAndCodeNarcPlugins() {
-        plugin.apply(project)
-
-        assertTrue(project.plugins.hasPlugin(ReportingBasePlugin))
-        assertTrue(project.plugins.hasPlugin(CheckstylePlugin))
-        assertTrue(project.plugins.hasPlugin(CodeNarcPlugin))
-        assertFalse(project.plugins.hasPlugin(JavaBasePlugin))
-        assertFalse(project.plugins.hasPlugin(GroovyBasePlugin))
-    }
-
-    @Test public void addsConventionObjectsToProject() {
-        plugin.apply(project)
-
-        assertThat(project.convention.plugins.javaCodeQuality, instanceOf(JavaCodeQualityPluginConvention))
-        assertThat(project.checkstyleProperties, equalTo([:]))
-        assertThat(project.checkstyleConfigFile, equalTo(project.file("config/checkstyle/checkstyle.xml")))
-        assertThat(project.file("build/checkstyle"), equalTo(project.checkstyleResultsDir))
-
-        assertThat(project.convention.plugins.groovyCodeQuality, instanceOf(GroovyCodeQualityPluginConvention))
-        assertThat(project.codeNarcConfigFile, equalTo(project.file("config/codenarc/codenarc.xml")))
-        assertThat(project.codeNarcReportsDir, equalTo(project.file("build/reports/codenarc")))
-    }
-
-    @Test public void createsTasksAndAppliesMappingsForEachJavaSourceSet() {
-        plugin.apply(project)
-
-        project.plugins.apply(JavaPlugin)
-        project.checkstyleProperties.someProp = 'someValue'
-
-        def task = project.tasks[CodeQualityPlugin.CHECKSTYLE_MAIN_TASK]
-        assertThat(task, instanceOf(Checkstyle))
-        assertThat(task.source as List, equalTo(project.sourceSets.main.allJava as List))
-        assertThat(task.checkstyleClasspath, equalTo(project.configurations.checkstyle))
-        assertThat(task.configFile, equalTo(project.checkstyleConfigFile))
-        assertThat(task.resultFile, equalTo(project.file("build/checkstyle/main.xml")))
-        assertThat(task.configProperties, equalTo(project.checkstyleProperties))
-        assertThat(task, TaskDependencyMatchers.dependsOn(JavaPlugin.CLASSES_TASK_NAME))
-
-        task = project.tasks[CodeQualityPlugin.CHECKSTYLE_TEST_TASK]
-        assertThat(task, instanceOf(Checkstyle))
-        assertThat(task.source as List, equalTo(project.sourceSets.test.allJava as List))
-        assertThat(task.checkstyleClasspath, equalTo(project.configurations.checkstyle))
-        assertThat(task.configFile, equalTo(project.checkstyleConfigFile))
-        assertThat(task.resultFile, equalTo(project.file("build/checkstyle/test.xml")))
-        assertThat(task.properties, equalTo(project.checkstyleProperties))
-        assertThat(task, TaskDependencyMatchers.dependsOn(JavaPlugin.TEST_CLASSES_TASK_NAME))
-
-        project.sourceSets.create('custom')
-        task = project.tasks['checkstyleCustom']
-        assertThat(task, instanceOf(Checkstyle))
-        assertThat(task.source as List, equalTo(project.sourceSets.custom.allJava as List))
-        assertThat(task.checkstyleClasspath, equalTo(project.configurations.checkstyle))
-        assertThat(task.configFile, equalTo(project.checkstyleConfigFile))
-        assertThat(task.resultFile, equalTo(project.file("build/checkstyle/custom.xml")))
-        assertThat(task.properties, equalTo(project.checkstyleProperties))
-        assertThat(task, TaskDependencyMatchers.dependsOn("customClasses"))
-
-        task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
-        assertThat(task, TaskDependencyMatchers.dependsOn(hasItems(CodeQualityPlugin.CHECKSTYLE_MAIN_TASK, CodeQualityPlugin.CHECKSTYLE_TEST_TASK, 'checkstyleCustom')))
-    }
-
-    @Test public void createsTasksAndAppliesMappingsForEachGroovySourceSet() {
-        plugin.apply(project)
-
-        project.plugins.apply(GroovyPlugin)
-
-        def task = project.tasks[CodeQualityPlugin.CODE_NARC_MAIN_TASK]
-        assertThat(task, instanceOf(CodeNarc))
-        assertThat(task.source as List, equalTo(project.sourceSets.main.allGroovy as List))
-        assertThat(task.codenarcClasspath, equalTo(project.configurations.codenarc))
-        assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
-        assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/main.html")))
-        assertThat(task, TaskDependencyMatchers.dependsOn())
-
-        task = project.tasks[CodeQualityPlugin.CODE_NARC_TEST_TASK]
-        assertThat(task, instanceOf(CodeNarc))
-        assertThat(task.source as List, equalTo(project.sourceSets.test.allGroovy as List))
-        assertThat(task.codenarcClasspath, equalTo(project.configurations.codenarc))
-        assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
-        assertThat(task.reportFormat, equalTo(project.codeNarcReportsFormat))
-        assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/test.html")))
-        assertThat(task, TaskDependencyMatchers.dependsOn())
-
-        project.sourceSets.create('custom')
-        task = project.tasks['codenarcCustom']
-        assertThat(task, instanceOf(CodeNarc))
-        assertThat(task.source as List, equalTo(project.sourceSets.custom.allGroovy as List))
-        assertThat(task.codenarcClasspath, equalTo(project.configurations.codenarc))
-        assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
-        assertThat(task.reportFormat, equalTo(project.codeNarcReportsFormat))
-        assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/custom.html")))
-        assertThat(task, TaskDependencyMatchers.dependsOn())
-
-        task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
-        assertThat(task, TaskDependencyMatchers.dependsOn(hasItem(CodeQualityPlugin.CODE_NARC_MAIN_TASK)))
-        assertThat(task, TaskDependencyMatchers.dependsOn(hasItem(CodeQualityPlugin.CODE_NARC_TEST_TASK)))
-        assertThat(task, TaskDependencyMatchers.dependsOn(hasItem('codenarcCustom')))
-    }
-
-    @Test public void appliesMappingsForCheckstyleTasksWithoutRequiringTheJavaBasePluginToBeApplied() {
-        plugin.apply(project)
-
-        project.checkstyleProperties.someProp = 'someValue'
-
-        def task = project.tasks.create('checkstyleApi', Checkstyle)
-        assertThat(task.source, isEmpty())
-        assertThat(task.checkstyleClasspath, equalTo(project.configurations.checkstyle))
-        assertThat(task.configFile, equalTo(project.checkstyleConfigFile))
-        assertThat(task.resultFile, equalTo(project.file("build/checkstyle/api.xml")))
-        assertThat(task.configProperties, equalTo(project.checkstyleProperties))
-    }
-
-    @Test public void appliesMappingsForCodeNarcTasksWithoutRequiringTheGroovyBasePluginToBeApplied() {
-        plugin.apply(project)
-
-        project.checkstyleProperties.someProp = 'someValue'
-
-        def task = project.tasks.create('codenarcApi', CodeNarc)
-        assertThat(task.source, isEmpty())
-        assertThat(task.codenarcClasspath, equalTo(project.configurations.codenarc))
-        assertThat(task.configFile, equalTo(project.codeNarcConfigFile))
-        assertThat(task.reportFile, equalTo(project.file("build/reports/codenarc/api.html")))
-    }
-
-}
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodenarcTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodenarcTest.groovy
new file mode 100644
index 0000000..e2ffea5
--- /dev/null
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/CodenarcTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.quality
+
+import org.gradle.testfixtures.ProjectBuilder
+import spock.lang.Specification
+
+class CodenarcTest extends Specification {
+    def project = ProjectBuilder.builder().build()
+    def codenarc = project.tasks.create("codenarc", CodeNarc)
+
+    def "can use legacy configFile property"() {
+        codenarc.configFile = project.file("config/file.txt")
+
+        expect:
+        codenarc.configFile == project.file("config/file.txt")
+        codenarc.config.inputFiles.singleFile == project.file("config/file.txt")
+    }
+}
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 ac2ebb9..7990629 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
@@ -15,8 +15,9 @@
  */
 package org.gradle.api.plugins.quality
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
 import org.gradle.util.TestUtil
@@ -27,10 +28,10 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class FindBugsPluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(FindBugsPlugin)
+        project.pluginManager.apply(FindBugsPlugin)
     }
 
     def "applies reporting-base plugin"() {
@@ -57,12 +58,16 @@ class FindBugsPluginTest extends Specification {
         extension.reportLevel == null
         extension.visitors == null
         extension.omitVisitors == null
+        extension.includeFilterConfig == null
+        extension.excludeFilterConfig == null
+        extension.excludeBugsFilterConfig == null
         extension.includeFilter == null
         extension.excludeFilter == null
+        extension.excludeBugsFilter == null
     }
 
     def "configures FindBugs task for each source set"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -89,8 +94,12 @@ class FindBugsPluginTest extends Specification {
             reportLevel == null
             visitors == null
             omitVisitors == null
+            excludeFilterConfig == null
+            includeFilterConfig == null
+            excludeBugsFilterConfig == null
             excludeFilter == null
             includeFilter == null
+            excludeBugsFilter == null
         }
     }
 
@@ -111,13 +120,17 @@ class FindBugsPluginTest extends Specification {
             reportLevel == null
             visitors == null
             omitVisitors == null
+            excludeFilterConfig == null
+            includeFilterConfig == null
+            excludeBugsFilterConfig == null
             excludeFilter == null
             includeFilter == null
+            excludeBugsFilter == null
         }
     }
 
     def "adds FindBugs tasks to check lifecycle task"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -129,7 +142,7 @@ class FindBugsPluginTest extends Specification {
     }
 
     def "can customize settings via extension"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -146,6 +159,7 @@ class FindBugsPluginTest extends Specification {
             omitVisitors = ['org.gradle.Interface']
             includeFilter = new File("include.txt")
             excludeFilter = new File("exclude.txt")
+            excludeBugsFilter = new File("baselineBugs.txt")
         }
 
         expect:
@@ -169,8 +183,12 @@ class FindBugsPluginTest extends Specification {
             reportLevel == 'high'
             visitors == ['org.gradle.Class']
             omitVisitors == ['org.gradle.Interface']
-            includeFilter == new File("include.txt")
-            excludeFilter == new File("exclude.txt")
+            includeFilterConfig.inputFiles.singleFile == project.file("include.txt")
+            excludeFilterConfig.inputFiles.singleFile == project.file("exclude.txt")
+            excludeBugsFilterConfig.inputFiles.singleFile == project.file("baselineBugs.txt")
+            includeFilter == project.file("include.txt")
+            excludeFilter == project.file("exclude.txt")
+            excludeBugsFilter == project.file("baselineBugs.txt")
         }
     }
     
@@ -183,8 +201,9 @@ class FindBugsPluginTest extends Specification {
             reportLevel = 'high'
             visitors = ['org.gradle.Class']
             omitVisitors = ['org.gradle.Interface']
-            includeFilter = new File("include.txt")
-            excludeFilter = new File("exclude.txt")
+            includeFilterConfig = project.resources.text.fromFile("include.txt")
+            excludeFilterConfig = project.resources.text.fromFile("exclude.txt")
+            excludeBugsFilterConfig = project.resources.text.fromFile("baselineBugs.txt")
         }
 
         expect:
@@ -201,14 +220,18 @@ class FindBugsPluginTest extends Specification {
             reportLevel == 'high'
             visitors == ['org.gradle.Class']
             omitVisitors == ['org.gradle.Interface']
-            includeFilter == new File("include.txt")
-            excludeFilter == new File("exclude.txt")
+            includeFilterConfig.inputFiles.singleFile == project.file("include.txt")
+            excludeFilterConfig.inputFiles.singleFile == project.file("exclude.txt")
+            excludeBugsFilterConfig.inputFiles.singleFile == project.file("baselineBugs.txt")
+            includeFilter == project.file("include.txt")
+            excludeFilter == project.file("exclude.txt")
+            excludeBugsFilter == project.file("baselineBugs.txt")
         }
     }
 
     def "can configure reporting"() {
         given:
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
         }
@@ -224,4 +247,35 @@ class FindBugsPluginTest extends Specification {
         then:
         noExceptionThrown()
     }
+
+    def "can use legacy includeFilter extension property"() {
+        project.pluginManager.apply(JavaPlugin)
+
+        project.findbugs.includeFilter = project.file("filter.txt")
+
+
+        expect:
+        project.findbugs.includeFilter == project.file("filter.txt")
+        project.findbugs.includeFilterConfig.inputFiles.singleFile == project.file("filter.txt")
+    }
+
+    def "can use legacy excludeFilter extension property"() {
+        project.pluginManager.apply(JavaPlugin)
+
+        project.findbugs.excludeFilter = project.file("filter.txt")
+
+        expect:
+        project.findbugs.excludeFilter == project.file("filter.txt")
+        project.findbugs.excludeFilterConfig.inputFiles.singleFile == project.file("filter.txt")
+    }
+
+    def "can use legacy excludeBugsFilter extension property"() {
+        project.pluginManager.apply(JavaPlugin)
+
+        project.findbugs.excludeBugsFilter = project.file("filter.txt")
+
+        expect:
+        project.findbugs.excludeBugsFilter == project.file("filter.txt")
+        project.findbugs.excludeBugsFilterConfig.inputFiles.singleFile == project.file("filter.txt")
+    }
 }
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 3a9d1d9..06bd2c2 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
@@ -16,19 +16,14 @@
 
 package org.gradle.api.plugins.quality
 
-import spock.lang.Specification
-
+import org.gradle.api.GradleException
 import org.gradle.api.plugins.quality.internal.findbugs.FindBugsResult
 import org.gradle.testfixtures.ProjectBuilder
-import org.gradle.api.GradleException
+import spock.lang.Specification
 
 class FindBugsTest extends Specification {
-    private FindBugs findbugs
-
-    def setup() {
-        def project = ProjectBuilder.builder().build()
-        findbugs = project.tasks.create("findbugs", FindBugs)
-    }
+    def project = ProjectBuilder.builder().build()
+    FindBugs findbugs = project.tasks.create("findbugs", FindBugs)
 
     def "fails when errorCount greater than zero"() {
         def result = Mock(FindBugsResult)
@@ -107,4 +102,28 @@ class FindBugsTest extends Specification {
         then:
         noExceptionThrown()
     }
+
+    def "can use legacy includeFilter property"() {
+        findbugs.includeFilter = project.file("config/file.txt")
+
+        expect:
+        findbugs.includeFilter == project.file("config/file.txt")
+        findbugs.includeFilterConfig.inputFiles.singleFile == project.file("config/file.txt")
+    }
+
+    def "can use legacy excludeFilter property"() {
+        findbugs.excludeFilter = project.file("config/file.txt")
+
+        expect:
+        findbugs.excludeFilter == project.file("config/file.txt")
+        findbugs.excludeFilterConfig.inputFiles.singleFile == project.file("config/file.txt")
+    }
+
+    def "can use legacy excludeBugsFilter property"() {
+        findbugs.excludeBugsFilter = project.file("config/file.txt")
+
+        expect:
+        findbugs.excludeBugsFilter == project.file("config/file.txt")
+        findbugs.excludeBugsFilterConfig.inputFiles.singleFile == project.file("config/file.txt")
+    }
 }
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 d28dd7c..5b5c81c 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
@@ -15,21 +15,22 @@
  */
 package org.gradle.api.plugins.quality
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
 import org.gradle.util.TestUtil
 import spock.lang.Specification
+
 import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
 import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class JDependPluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(JDependPlugin)
+        project.pluginManager.apply(JDependPlugin)
     }
 
     def "applies reporting-base plugin"() {
@@ -55,7 +56,7 @@ class JDependPluginTest extends Specification {
     }
 
     def "configures jdepend task for each source set"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -90,7 +91,7 @@ class JDependPluginTest extends Specification {
     }
 
     def "adds jdepend tasks to check lifecycle task"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -102,7 +103,7 @@ class JDependPluginTest extends Specification {
     }
 
     def "can customize settings via extension"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -138,7 +139,7 @@ class JDependPluginTest extends Specification {
 
     def "can configure reporting"() {
         given:
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
         }
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 2513e92..4bc9bec 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
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.plugins.quality
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.tasks.SourceSet
@@ -27,10 +27,10 @@ import static org.hamcrest.Matchers.*
 import static spock.util.matcher.HamcrestSupport.that
 
 class PmdPluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(PmdPlugin)
+        project.pluginManager.apply(PmdPlugin)
     }
 
     def "applies reporting-base plugin"() {
@@ -52,13 +52,14 @@ class PmdPluginTest extends Specification {
         expect:
         PmdExtension extension = project.extensions.pmd
         extension.ruleSets == ["basic"]
+        extension.ruleSetConfig == null
         extension.ruleSetFiles.empty
         extension.reportsDir == project.file("build/reports/pmd")
         !extension.ignoreFailures
     }
 
     def "configures pmd task for each source set"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -72,7 +73,7 @@ class PmdPluginTest extends Specification {
     }
 
     def "configures pmd targetjdk based on sourcecompatibilityLevel"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         when:
         project.setSourceCompatibility(sourceCompatibility)
         project.sourceSets {
@@ -102,6 +103,7 @@ class PmdPluginTest extends Specification {
             source as List == sourceSet.allJava as List
             assert pmdClasspath == project.configurations.pmd
             assert ruleSets == ["basic"]
+            assert ruleSetConfig == null
             assert ruleSetFiles.empty
             assert reports.xml.destination == project.file("build/reports/pmd/${sourceSet.name}.xml")
             assert reports.html.destination == project.file("build/reports/pmd/${sourceSet.name}.html")
@@ -117,6 +119,7 @@ class PmdPluginTest extends Specification {
         task.source.empty
         task.pmdClasspath == project.configurations.pmd
         task.ruleSets == ["basic"]
+        task.ruleSetConfig == null
         task.ruleSetFiles.empty
         task.reports.xml.destination == project.file("build/reports/pmd/custom.xml")
         task.reports.html.destination == project.file("build/reports/pmd/custom.html")
@@ -124,7 +127,7 @@ class PmdPluginTest extends Specification {
     }
 
     def "adds pmd tasks to check lifecycle task"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -136,7 +139,7 @@ class PmdPluginTest extends Specification {
     }
 
     def "can customize settings via extension"() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets {
             main
             test
@@ -146,6 +149,7 @@ class PmdPluginTest extends Specification {
         project.pmd {
             sourceSets = [project.sourceSets.main]
             ruleSets = ["braces", "unusedcode"]
+            ruleSetConfig = project.resources.text.fromString("ruleset contents")
             ruleSetFiles = project.files("my-ruleset.xml")
             reportsDir = project.file("pmd-reports")
             ignoreFailures = true
@@ -167,7 +171,8 @@ class PmdPluginTest extends Specification {
             source as List == sourceSet.allJava as List
             assert pmdClasspath == project.configurations.pmd
             assert ruleSets == ["braces", "unusedcode"]
-            assert ruleSetFiles.files == project.files("my-ruleset.xml").files
+            assert ruleSetConfig.asString() == "ruleset contents"
+            assert ruleSetFiles.singleFile == project.file("my-ruleset.xml")
             assert reports.xml.destination == project.file("pmd-reports/${sourceSet.name}.xml")
             assert reports.html.destination == project.file("pmd-reports/${sourceSet.name}.html")
             assert ignoreFailures == true
@@ -178,6 +183,7 @@ class PmdPluginTest extends Specification {
         def task = project.tasks.create("pmdCustom", Pmd)
         project.pmd {
             ruleSets = ["braces", "unusedcode"]
+            ruleSetConfig = project.resources.text.fromString("ruleset contents")
             ruleSetFiles = project.files("my-ruleset.xml")
             reportsDir = project.file("pmd-reports")
             ignoreFailures = true
@@ -188,7 +194,8 @@ class PmdPluginTest extends Specification {
         task.source.empty
         task.pmdClasspath == project.configurations.pmd
         task.ruleSets == ["braces", "unusedcode"]
-        task.ruleSetFiles.files == project.files("my-ruleset.xml").files
+        task.ruleSetConfig.asString() == "ruleset contents"
+        task.ruleSetFiles.singleFile == project.file("my-ruleset.xml")
         task.reports.xml.destination == project.file("pmd-reports/custom.xml")
         task.reports.html.destination == project.file("pmd-reports/custom.html")
         task.outputs.files.files == task.reports.enabled*.destination as Set
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidatorTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidatorTest.groovy
new file mode 100644
index 0000000..c2028bb
--- /dev/null
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsClasspathValidatorTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.quality.internal.findbugs
+
+import org.gradle.api.GradleException
+import org.gradle.api.JavaVersion
+import spock.lang.Specification
+
+import static org.gradle.api.plugins.quality.internal.findbugs.FindBugsClasspathValidator.FindBugsVersionTooHighException
+import static org.gradle.api.plugins.quality.internal.findbugs.FindBugsClasspathValidator.FindBugsVersionTooLowException
+
+class FindBugsClasspathValidatorTest extends Specification {
+
+    def newVer = ['foo.jar', 'findbugs-3.0.0.jar']
+    def oldVer = ['foo.jar', 'findbugs-2.0.3.jar']
+
+    def "validates"() {
+        expect:
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_5).validateClasspath(oldVer)
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_6).validateClasspath(oldVer)
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_7).validateClasspath(oldVer)
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_7).validateClasspath(newVer)
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_8).validateClasspath(newVer)
+        new FindBugsClasspathValidator(JavaVersion.VERSION_1_9).validateClasspath(newVer)
+    }
+
+    def "reports older version of findbugs required"() {
+        when: new FindBugsClasspathValidator(JavaVersion.VERSION_1_5).validateClasspath(newVer)
+        then: thrown(FindBugsVersionTooHighException)
+
+        when: new FindBugsClasspathValidator(JavaVersion.VERSION_1_6).validateClasspath(newVer)
+        then: thrown(FindBugsVersionTooHighException)
+    }
+
+    def "reports newer version of findbugs required"() {
+        when: new FindBugsClasspathValidator(JavaVersion.VERSION_1_8).validateClasspath(oldVer)
+        then: thrown(FindBugsVersionTooLowException)
+
+        when: new FindBugsClasspathValidator(JavaVersion.VERSION_1_9).validateClasspath(oldVer)
+        then: thrown(FindBugsVersionTooLowException)
+    }
+
+    def "requires findbugs jar in classpath"() {
+        when:
+        new FindBugsClasspathValidator(JavaVersion.current()).validateClasspath(['foo.jar'])
+        then:
+        def ex = thrown(GradleException)
+        ex.message.contains('foo.jar')
+    }
+}
diff --git a/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServerTest.groovy b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServerTest.groovy
new file mode 100644
index 0000000..fe31aa1
--- /dev/null
+++ b/subprojects/code-quality/src/test/groovy/org/gradle/api/plugins/quality/internal/findbugs/FindBugsWorkerServerTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.quality.internal.findbugs
+
+import spock.lang.Specification
+import spock.lang.Subject
+
+class FindBugsWorkerServerTest extends Specification {
+
+    def executer = Mock(FindBugsExecuter)
+    def spec = Stub(FindBugsSpec)
+    @Subject server = new FindBugsWorkerServer(spec)
+
+    def "fatal crash provides result"() {
+        def error = new Error("Ka-boom!")
+
+        when:
+        def r = server.execute(executer)
+
+        then:
+        1 * executer.runFindbugs(spec) >> { throw error }
+
+        and:
+        r.exception == error
+    }
+}
diff --git a/subprojects/core-impl/core-impl.gradle b/subprojects/core-impl/core-impl.gradle
deleted file mode 100644
index e66ee48..0000000
--- a/subprojects/core-impl/core-impl.gradle
+++ /dev/null
@@ -1,77 +0,0 @@
-apply plugin: "groovy"
-
-import org.gradle.build.*
-
-configurations {
-    mvn3Input
-}
-
-dependencies {
-    compile project(":core")
-
-    compile libraries.commons_httpclient
-    compile libraries.commons_lang
-    compile libraries.commons_io
-    compile libraries.ivy
-    compile libraries.slf4j_api
-    compile libraries.maven_ant_tasks
-    compile libraries.nekohtml
-    runtime libraries.xbean //maven3 classes dependency
-
-    compile fileTree("$buildDir/libs/jarjar") {
-        builtBy 'jarJarMaven3'
-    }
-
-    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"
-}
-
-task jarJarMaven3(type: JarJar) {
-    inputJars = configurations.mvn3Input
-    outputDir = file("$buildDir/libs/jarjar")
-
-    //unfortunately, all those need to be jarjarred.
-    // Even if some library (like aether) is not included in maven-ant-tasks it has
-    // META-INF/plexus/components.xml that to jarjarred components.
-    rule('org.apache.maven.**', 'org.gradle.mvn3.org.apache.maven. at 1')
-    rule('org.codehaus.**', 'org.gradle.mvn3.org.codehaus. at 1')
-    rule('org.sonatype.**', 'org.gradle.mvn3.org.sonatype. at 1')
-
-    avoidConflictingPlexusComponents(it)
-}
-
-if(isWindows && javaVersion.java5){
-    compileTestGroovy.options.fork (memoryMaximumSize: '512m')
-}
-
-classpathManifest.dependsOn jarJarMaven3 //see GRADLE-2521
-
-//adding explicit task dependencies due to http://issues.gradle.org/browse/GRADLE-2481
-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).
-    //The implementation is the same but the 'hint' is different and this makes plexus fail to start.
-    //I'm removing the components.xml file from the sec-dispatcher jar.
-    //This file contains only single component so I think we can remove it.
-    task.doLast {
-        def plexusSec = "$outputDir/jarjar-plexus-sec-dispatcher-1.3.jar"
-        def plexusSecNoComps = "$plexusSec-noComps"
-        ant {
-            zip(destfile: plexusSecNoComps, update: true) {
-                zipfileset(src: plexusSec) {
-                    exclude(name: 'META-INF/plexus/components.xml')
-                }
-            }
-            move(file: plexusSecNoComps, tofile: plexusSec)
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index cf87638..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,691 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.ExecutionFailure
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import spock.lang.Issue
-
-import static org.hamcrest.Matchers.containsString
-
-class ArtifactDependenciesIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final TestResources testResources = new TestResources(testDirectoryProvider)
-
-    @Before
-    public void setup() {
-        executer.requireOwnGradleUserHomeDir()
-    }
-
-    @Test
-    public void canHaveConfigurationHierarchy() {
-        File buildFile = testFile("projectWithConfigurationHierarchy.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void dependencyReportWithConflicts() {
-        File buildFile = testFile("projectWithConflicts.gradle");
-        usingBuildFile(buildFile).run();
-        usingBuildFile(buildFile).withDependencyList().run();
-    }
-
-    @Test
-    public void canNestModules() throws IOException {
-        File buildFile = testFile("projectWithNestedModules.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void canHaveCycleInDependencyGraph() throws IOException {
-        File buildFile = testFile("projectWithCyclesInDependencyGraph.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void canUseDynamicVersions() throws IOException {
-        File buildFile = testFile("projectWithDynamicVersions.gradle");
-        usingBuildFile(buildFile).run();
-    }
-
-    @Test
-    public void resolutionFailsWhenProjectHasNoRepositoriesEvenWhenArtifactIsCachedLocally() {
-        testFile('settings.gradle') << 'include "a", "b"'
-        testFile('build.gradle') << """
-subprojects {
-    configurations {
-        compile
-    }
-    task listDeps << { configurations.compile.each { } }
-}
-project(':a') {
-    repositories {
-        maven { url '${repo.uri}' }
-    }
-    dependencies {
-        compile 'org.gradle.test:external1:1.0'
-    }
-}
-project(':b') {
-    dependencies {
-        compile 'org.gradle.test:external1:1.0'
-    }
-}
-"""
-        repo.module('org.gradle.test', 'external1', '1.0').publish()
-
-        inTestDirectory().withTasks('a:listDeps').run()
-        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
-        result.assertThatCause(containsString('Could not find org.gradle.test:external1:1.0.'))
-    }
-
-    @Test
-    public void resolutionFailsForMissingArtifact() {
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    compile; missingExt; missingClassifier
-}
-dependencies {
-    compile "org.gradle.test:lib:1.0"
-    missingExt "org.gradle.test:lib:1.0 at zip"
-    missingClassifier "org.gradle.test:lib:1.0:classifier1"
-}
-task listJar << { configurations.compile.each { } }
-task listMissingExt << { configurations.missingExt.each { } }
-task listMissingClassifier << { configurations.missingClassifier.each { } }
-"""
-        repo.module('org.gradle.test', 'lib', '1.0').publish()
-
-        inTestDirectory().withTasks('listJar').run()
-
-        def result = inTestDirectory().withTasks('listMissingExt').runWithFailure()
-        result.assertThatCause(containsString("Artifact 'org.gradle.test:lib:1.0:lib.zip' not found"))
-
-        result = inTestDirectory().withTasks('listMissingClassifier').runWithFailure()
-        result.assertThatCause(containsString("Artifact 'org.gradle.test:lib:1.0:lib-classifier1.jar' not found"))
-    }
-
-    @Test
-    @Issue("GRADLE-1342")
-    public void resolutionDoesNotUseCachedArtifactFromDifferentRepository() {
-        def repo1 = maven('repo1')
-        repo1.module('org.gradle.test', 'external1', '1.0').publish()
-        def repo2 = maven('repo2')
-
-        testFile('settings.gradle') << 'include "a", "b"'
-        testFile('build.gradle') << """
-subprojects {
-    configurations {
-        compile
-    }
-    task listDeps << { configurations.compile.each { } }
-}
-project(':a') {
-    repositories {
-        maven { url '${repo1.uri}' }
-    }
-    dependencies {
-        compile 'org.gradle.test:external1:1.0'
-    }
-}
-project(':b') {
-    repositories {
-        maven { url '${repo2.uri}' }
-    }
-    dependencies {
-        compile 'org.gradle.test:external1:1.0'
-    }
-}
-"""
-
-        inTestDirectory().withTasks('a:listDeps').run()
-        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
-        result.assertThatCause(containsString('Could not find org.gradle.test:external1:1.0.'))
-    }
-
-    @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')
-        module.artifact(classifier: 'classifier')
-        module.publish()
-        repo.module('org.gradle.test', 'dist', '1.0').hasType('zip').publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    compile
-}
-dependencies {
-    compile "org.gradle.test:lib:1.0"
-    compile "org.gradle.test:lib:1.0:classifier"
-    compile "org.gradle.test:lib:1.0 at zip"
-    compile "org.gradle.test:dist:1.0"
-}
-task test << {
-    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'
-    assert artifacts[0].type == 'jar'
-    assert artifacts[0].extension == 'jar'
-    assert artifacts[0].classifier == null
-    assert artifacts[1].name == 'lib'
-    assert artifacts[1].type == 'jar'
-    assert artifacts[1].extension == 'jar'
-    assert artifacts[1].classifier == 'classifier'
-    assert artifacts[2].name == 'lib'
-    assert artifacts[2].type == 'zip'
-    assert artifacts[2].extension == 'zip'
-    assert artifacts[2].classifier == null
-    assert artifacts[3].name == 'dist'
-    assert artifacts[3].type == 'zip'
-    assert artifacts[3].extension == 'zip'
-    assert artifacts[3].classifier == null
-}
-"""
-
-        executer.withDeprecationChecksDisabled()
-        def result = inTestDirectory().withTasks('test').run()
-        assert result.output.contains('Relying on packaging to define the extension of the main artifact has been deprecated')
-    }
-
-    @Test
-    @Issue("GRADLE-1567")
-    public void resolutionDifferentiatesBetweenArtifactsThatDifferOnlyInClassifier() {
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(classifier: 'classifier1')
-        module.artifact(classifier: 'classifier2')
-        module.publish()
-
-        testFile('settings.gradle') << 'include "a", "b", "c"'
-        testFile('build.gradle') << """
-subprojects {
-    repositories {
-        maven { url '${repo.uri}' }
-    }
-    configurations {
-        compile
-    }
-}
-project(':a') {
-    dependencies {
-        compile 'org.gradle.test:external1:1.0:classifier1'
-    }
-    task test(dependsOn: configurations.compile) << {
-        assert configurations.compile.collect { it.name } == ['external1-1.0-classifier1.jar']
-        assert configurations.compile.resolvedConfiguration.resolvedArtifacts.collect { "\${it.name}-\${it.classifier}" } == ['external1-classifier1']
-    }
-}
-project(':b') {
-    dependencies {
-        compile 'org.gradle.test:external1:1.0:classifier2'
-    }
-    task test(dependsOn: configurations.compile) << {
-        assert configurations.compile.collect { it.name } == ['external1-1.0-classifier2.jar']
-        assert configurations.compile.resolvedConfiguration.resolvedArtifacts.collect { "\${it.name}-\${it.classifier}" } == ['external1-classifier2']
-    }
-}
-"""
-
-        inTestDirectory().withTasks('a:test').run()
-        inTestDirectory().withTasks('b:test').run()
-    }
-
-    @Test
-    @Issue("GRADLE-739")
-    public void singleConfigurationCanContainMultipleArtifactsThatOnlyDifferByClassifier() {
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(classifier: 'baseClassifier')
-        module.artifact(classifier: 'extendedClassifier')
-        module.publish()
-        repo.module('org.gradle.test', 'other', '1.0').publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    base
-    extendedWithClassifier.extendsFrom base
-    extendedWithOther.extendsFrom base
-    justDefault
-    justClassifier
-    rawBase
-    rawExtended.extendsFrom rawBase
-    cBase
-    cExtended.extendsFrom cBase
-}
-dependencies {
-    base 'org.gradle.test:external1:1.0'
-    base 'org.gradle.test:external1:1.0:baseClassifier'
-    extendedWithClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
-    extendedWithOther 'org.gradle.test:other:1.0'
-    justDefault 'org.gradle.test:external1:1.0'
-    justClassifier 'org.gradle.test:external1:1.0:baseClassifier'
-    justClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
-    rawBase 'org.gradle.test:external1:1.0'
-    rawExtended 'org.gradle.test:external1:1.0:extendedClassifier'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar']
-    checkDeps configurations.extendedWithOther, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
-    checkDeps configurations.extendedWithClassifier, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-    checkDeps configurations.justDefault, ['external1-1.0.jar']
-    checkDeps configurations.justClassifier, ['external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-    checkDeps configurations.rawBase, ['external1-1.0.jar']
-    checkDeps configurations.rawExtended, ['external1-1.0.jar', 'external1-1.0-extendedClassifier.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    @Issue("GRADLE-739")
-    public void canUseClassifiersCombinedWithArtifactWithNonStandardPackaging() {
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(type: 'txt')
-        module.artifact(classifier: 'baseClassifier', type: 'jar')
-        module.artifact(classifier: 'extendedClassifier', type: 'jar')
-        module.hasType('zip')
-        module.publish()
-        repo.module('org.gradle.test', 'other', '1.0').publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    base
-    extended.extendsFrom base
-    extendedWithClassifier.extendsFrom base
-    extendedWithType.extendsFrom base
-}
-dependencies {
-    base 'org.gradle.test:external1:1.0'
-    base 'org.gradle.test:external1:1.0:baseClassifier'
-    extended 'org.gradle.test:other:1.0'
-    extendedWithClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
-    extendedWithType 'org.gradle.test:external1:1.0 at txt'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.base, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar']
-    checkDeps configurations.extended, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
-    checkDeps configurations.extendedWithClassifier, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-    checkDeps configurations.extendedWithType, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0.txt']
-}
-"""
-        executer.withDeprecationChecksDisabled()
-        def result = inTestDirectory().withTasks('test').run()
-        assert result.output.contains('Relying on packaging to define the extension of the main artifact has been deprecated')
-    }
-
-    @Test
-    @Issue("GRADLE-739")
-    public void configurationCanContainMultipleArtifactsThatOnlyDifferByType() {
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(type: 'zip')
-        module.artifact(classifier: 'classifier')
-        module.artifact(classifier: 'classifier', type: 'bin')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    base
-    extended.extendsFrom base
-    extended2.extendsFrom base
-}
-dependencies {
-    base 'org.gradle.test:external1:1.0'
-    base 'org.gradle.test:external1:1.0 at zip'
-    extended 'org.gradle.test:external1:1.0:classifier'
-    extended2 'org.gradle.test:external1:1.0:classifier at bin'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0.zip']
-    checkDeps configurations.extended, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.jar']
-    checkDeps configurations.extended2, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.bin']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void "dependencies that are excluded by a dependency are not retrieved"() {
-        repo.module('org.gradle.test', 'one', '1.0').publish()
-        repo.module('org.gradle.test', 'two', '1.0').publish()
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.dependsOn('org.gradle.test', 'one', '1.0')
-        module.artifact(classifier: 'classifier')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    reference
-    excluded
-    extendedExcluded.extendsFrom excluded
-    excludedWithClassifier
-}
-dependencies {
-    reference 'org.gradle.test:external1:1.0'
-    excluded 'org.gradle.test:external1:1.0', { exclude module: 'one' }
-    extendedExcluded 'org.gradle.test:two:1.0'
-    excludedWithClassifier 'org.gradle.test:external1:1.0', { exclude module: 'one' }
-    excludedWithClassifier 'org.gradle.test:external1:1.0:classifier', { exclude module: 'one' }
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config*.name as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.reference, ['external1-1.0.jar', 'one-1.0.jar']
-    checkDeps configurations.excluded, ['external1-1.0.jar']
-    checkDeps configurations.extendedExcluded, ['external1-1.0.jar', 'two-1.0.jar']
-    checkDeps configurations.excludedWithClassifier, ['external1-1.0.jar', 'external1-1.0-classifier.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void "dependencies that are globally excluded are not retrieved"() {
-        repo.module('org.gradle.test', 'direct', '1.0').publish()
-        repo.module('org.gradle.test', 'transitive', '1.0').publish()
-        def module = repo.module('org.gradle.test', 'external', '1.0')
-        module.dependsOn('org.gradle.test', 'transitive', '1.0')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    excluded {
-        exclude module: 'direct'
-        exclude module: 'transitive'
-    }
-    extendedExcluded.extendsFrom excluded
-}
-dependencies {
-    excluded 'org.gradle.test:external:1.0'
-    excluded 'org.gradle.test:direct:1.0'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config*.name as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.excluded, ['external-1.0.jar']
-    checkDeps configurations.extendedExcluded, ['external-1.0.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void "does not attempt to resolve an excluded dependency"() {
-        def module = repo.module('org.gradle.test', 'external', '1.0')
-        module.dependsOn('org.gradle.test', 'unknown1', '1.0')
-        module.dependsOn('org.gradle.test', 'unknown2', '1.0')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    excluded {
-        exclude module: 'unknown2'
-    }
-}
-dependencies {
-    excluded 'org.gradle.test:external:1.0', { exclude module: 'unknown1' }
-    excluded 'org.gradle.test:unknown2:1.0'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config*.name as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.excluded, ['external-1.0.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void nonTransitiveDependenciesAreNotRetrieved() {
-        repo.module('org.gradle.test', 'one', '1.0').publish()
-        repo.module('org.gradle.test', 'two', '1.0').publish()
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.dependsOn('org.gradle.test', 'one', '1.0')
-        module.artifact(classifier: 'classifier')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    transitive
-    nonTransitive
-    extendedNonTransitive.extendsFrom nonTransitive
-    extendedBoth.extendsFrom transitive, nonTransitive
-    mergedNonTransitive
-}
-dependencies {
-    transitive 'org.gradle.test:external1:1.0'
-    nonTransitive 'org.gradle.test:external1:1.0', { transitive = false }
-    extendedNonTransitive 'org.gradle.test:two:1.0'
-    mergedNonTransitive 'org.gradle.test:external1:1.0', {transitive = false }
-    mergedNonTransitive 'org.gradle.test:external1:1.0:classifier', { transitive = false }
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.transitive, ['external1-1.0.jar', 'one-1.0.jar']
-    checkDeps configurations.nonTransitive, ['external1-1.0.jar']
-    checkDeps configurations.extendedNonTransitive, ['external1-1.0.jar', 'two-1.0.jar']
-    checkDeps configurations.extendedBoth, ['external1-1.0.jar', 'one-1.0.jar']
-    checkDeps configurations.mergedNonTransitive, ['external1-1.0.jar', 'external1-1.0-classifier.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void "configuration transitive = false overrides dependency transitive flag"() {
-        repo.module('org.gradle.test', 'one', '1.0').publish()
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.dependsOn('org.gradle.test', 'one', '1.0')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    override { transitive = false }
-}
-dependencies {
-    override 'org.gradle.test:external1:1.0'
-}
-
-task test << {
-    assert configurations.override.collect { it.name } == ['external1-1.0.jar']
-}
-"""
-
-        inTestDirectory().withTasks('test').run()
-    }
-
-    /*
-     * Originally, we were aliasing dependency descriptors that were identical. This caused alias errors when we subsequently modified one of these descriptors.
-     */
-
-    @Test
-    public void addingClassifierToDuplicateDependencyDoesNotAffectOriginal() {
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(classifier: 'withClassifier')
-        module.publish()
-
-        testFile('build.gradle') << """
-repositories {
-    maven { url '${repo.uri}' }
-}
-configurations {
-    a
-    b
-}
-dependencies {
-    a 'org.gradle.test:external1:1.0'
-    b 'org.gradle.test:external1:1.0', 'org.gradle.test:external1:1.0:withClassifier'
-}
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test << {
-    checkDeps configurations.a, ['external1-1.0.jar']
-    checkDeps configurations.b, ['external1-1.0-withClassifier.jar', 'external1-1.0.jar']
-}
-"""
-        inTestDirectory().withTasks('test').run()
-    }
-
-    @Test
-    public void reportsUnknownDependencyError() {
-        File buildFile = testFile("projectWithUnknownDependency.gradle");
-        ExecutionFailure failure = usingBuildFile(buildFile).runWithFailure();
-
-        failure
-                .assertHasFileName("Build file '" + buildFile.getPath() + "'")
-                .assertHasDescription("Execution failed for task ':listJars'.");
-
-        failure.assertResolutionFailure(':compile')
-                .assertHasCause("Could not find test:unknownProjectA:1.2.")
-                .assertHasCause("Could not find test:unknownProjectB:2.1.5.")
-    }
-
-    @Test
-    public void projectCanDependOnItself() {
-        TestFile buildFile = testFile("build.gradle");
-        buildFile << '''
-            configurations { compile; create('default') }
-            dependencies { compile project(':') }
-            task jar1(type: Jar) { destinationDir = buildDir; baseName = '1' }
-            task jar2(type: Jar) { destinationDir = buildDir; baseName = '2' }
-            artifacts { compile jar1; 'default' jar2 }
-            task listJars << {
-                assert configurations.compile.collect { it.name } == ['2.jar']
-            }
-'''
-
-        inTestDirectory().withTasks("listJars").run()
-    }
-
-    @Test
-    public void canSpecifyProducerTasksForFileDependency() {
-        testFile("settings.gradle").write("include 'sub'");
-        testFile("build.gradle") << '''
-            configurations { compile }
-            dependencies { compile project(path: ':sub', configuration: 'compile') }
-            task test(dependsOn: configurations.compile) << {
-                assert file('sub/sub.jar').isFile()
-            }
-'''
-        testFile("sub/build.gradle") << '''
-            configurations { compile }
-            dependencies { compile files('sub.jar') { builtBy 'jar' } }
-            task jar << { file('sub.jar').text = 'content' }
-'''
-
-        inTestDirectory().withTasks("test").run().assertTasksExecuted(":sub:jar", ":test");
-    }
-
-    def getRepo() {
-        return maven(testFile('repo'))
-    }
-}
-
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
deleted file mode 100644
index 5a6f026..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-public class CacheResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    public void "cache handles manual deletion of cached artifacts"() {
-        server.start()
-
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        def cacheDir = executer.gradleUserHomeDir.file('caches').toURI()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-task deleteCacheFiles(type: Delete) {
-    delete fileTree(dir: '${cacheDir}', includes: ['**/projectA/**'])
-}
-"""
-
-        and:
-        module.allowAll()
-
-        and:
-        succeeds('listJars')
-        succeeds('deleteCacheFiles')
-        
-        when:
-        server.resetExpectations()
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        then:
-        succeeds('listJars')
-    }
-
-    public void "cache entries are segregated between different repositories"() {
-        server.start()
-        given:
-        def repo1 = ivyHttpRepo('ivy-repo-a')
-        def module1 = repo1.module('org.gradle', 'testproject', '1.0').publish()
-        def repo2 = ivyHttpRepo('ivy-repo-b')
-        def module2 = repo2.module('org.gradle', 'testproject', '1.0').publishWithChangedContent()
-
-        and:
-        settingsFile << "include 'a','b'"
-        buildFile << """
-subprojects {
-    configurations {
-        test
-    }
-    dependencies {
-        test "org.gradle:testproject:1.0"
-    }
-    task retrieve(type: Sync) {
-        into 'build'
-        from configurations.test
-    }
-}
-project('a') {
-    repositories {
-        ivy { url "${repo1.uri}" }
-    }
-}
-project('b') {
-    repositories {
-        ivy { url "${repo2.uri}" }
-    }
-    retrieve.dependsOn(':a:retrieve')
-}
-"""
-
-        when:
-        module1.ivy.expectGet()
-        module1.jar.expectGet()
-
-        module2.ivy.expectHead()
-        module2.ivy.sha1.expectGet()
-        module2.ivy.expectGet()
-        module2.jar.expectHead()
-        module2.jar.sha1.expectGet()
-        module2.jar.expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('a/build/testproject-1.0.jar').assertIsCopyOf(module1.jarFile)
-        file('b/build/testproject-1.0.jar').assertIsCopyOf(module2.jarFile)
-    }
-}
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
deleted file mode 100644
index f54052b..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.junit.Test
-
-public class ClientModuleDependenciesResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Test
-    public void "uses metadata from Client Module and looks up artifact in declared repositories"() {
-        given:
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-        def projectAInRepo1 = repo1.module('group', 'projectA', '1.2')
-        def projectAInRepo2 = repo2.module('group', 'projectA', '1.2').publish()
-        def projectB = repo1.module('group', 'projectB', '1.3').publish()
-
-        server.start()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${repo1.uri}" }
-    ivy { url "${repo2.uri}" }
-}
-configurations { compile }
-dependencies {
-    compile module("group:projectA:1.2") {
-       dependency("group:projectB:1.3")
-    }
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-1.3.jar']
-}
-"""
-
-        when:
-        projectB.ivy.expectGet()
-        projectB.jar.expectGet()
-        projectAInRepo1.ivy.expectGetMissing()
-        projectAInRepo1.jar.expectHeadMissing()
-        projectAInRepo2.ivy.expectGet()
-        projectAInRepo2.jar.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-
-        then:
-        succeeds('listJars')
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy
deleted file mode 100644
index b319e82..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.test.fixtures.HttpRepository
-
-abstract class ComponentMetadataRulesChangingModulesIntegrationTest extends AbstractDependencyResolutionTest {
-    abstract HttpRepository getRepo()
-    abstract String getRepoDeclaration()
-
-    def moduleA = getRepo().module('org.test', 'moduleA', '1.0')
-
-    def setup() {
-        server.start()
-        moduleA.publish()
-        moduleA.allowAll()
-    }
-
-    def "changing dependency doesn't affect changing flag"() {
-        buildFile <<
-"""
-$repoDeclaration
-configurations {
-    modules
-}
-dependencies {
-    modules("org.test:moduleA:1.0") { changing = true }
-    components {
-        eachComponent { details ->
-            assert !details.changing
-        }
-    }
-}
-task resolve << {
-    configurations.modules.resolve()
-}
-"""
-
-        expect:
-        succeeds("resolve")
-    }
-
-    def "static and dynamic dependencies have changing flag initialized to false"() {
-        buildFile <<
-"""
-$repoDeclaration
-configurations {
-    modules
-}
-dependencies {
-    modules "org.test:moduleA:$version"
-    components {
-        eachComponent { details ->
-            assert !details.changing
-        }
-    }
-}
-task resolve << {
-    configurations.modules.resolve()
-}
-"""
-
-        expect:
-        succeeds("resolve")
-
-        where:
-        version << ["1.0", "[1.0,2.0]"]
-    }
-
-
-    def "rule can make a component changing"() {
-        buildFile <<
-"""
-$repoDeclaration
-configurations {
-    modules {
-        resolutionStrategy.cacheChangingModulesFor 0, "seconds"
-    }
-}
-dependencies {
-    modules("org.test:moduleA:1.0")
-    components {
-        eachComponent { it.changing = true }
-    }
-}
-task resolve << {
-    copy {
-        from configurations.modules
-        into "modules"
-    }
-}
-"""
-
-        when:
-        run("resolve")
-        def artifact = file("modules/moduleA-1.0.jar")
-        def snapshot = artifact.snapshot()
-
-        and:
-        moduleA.publishWithChangedContent()
-        run("resolve")
-
-        then:
-        artifact.assertContentsHaveChangedSince(snapshot)
-
-        when:
-        buildFile << """
-dependencies.components.eachComponent { it.changing = false }
-"""
-        snapshot = artifact.snapshot()
-        server.resetExpectations()
-
-        and:
-        run("resolve")
-
-        then:
-        artifact.assertContentsHaveNotChangedSince(snapshot)
-    }
-
-    def "rule cannot make a dependency non-changing"() {
-        buildFile <<
-"""
-$repoDeclaration
-configurations {
-    modules {
-        resolutionStrategy.cacheChangingModulesFor 0, "seconds"
-    }
-}
-dependencies {
-    modules("org.test:moduleA:1.0") { changing = true }
-    components {
-        eachComponent { it.changing = false }
-    }
-}
-task resolve << {
-    copy {
-        from configurations.modules
-        into "modules"
-    }
-}
-"""
-
-        when:
-        run("resolve")
-        def artifact = file("modules/moduleA-1.0.jar")
-        def snapshot = artifact.snapshot()
-
-        and:
-        moduleA.publishWithChangedContent()
-        run("resolve")
-
-        then:
-        artifact.assertContentsHaveChangedSince(snapshot)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy
deleted file mode 100644
index ce5aabd..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.test.fixtures.HttpRepository
-
-abstract class ComponentMetadataRulesIntegrationTest extends AbstractDependencyResolutionTest {
-    abstract HttpRepository getRepo()
-    abstract String getRepoDeclaration()
-    abstract String getDefaultStatus()
-
-    def setup() {
-        server.start()
-
-        buildFile <<
-"""
-$repoDeclaration
-
-configurations { compile }
-
-dependencies {
-    compile 'org.test:projectA:1.0'
-}
-
-task resolve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-    }
-
-
-    def "rule receives correct metadata"() {
-        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
-        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 == "$defaultStatus"
-            assert details.statusScheme == ["integration", "milestone", "release"]
-            assert !details.changing
-        }
-    }
-}
-"""
-
-        expect:
-        succeeds 'resolve'
-    }
-
-    def "changes made by a rule are visible to subsequent rules"() {
-        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
-
-        buildFile <<
-                """
-dependencies {
-    components {
-        eachComponent { details ->
-            details.status "integration.changed" // verify that 'details' is enhanced
-            details.statusScheme = ["integration.changed", "milestone.changed", "release.changed"]
-            details.changing = true
-        }
-        eachComponent { details ->
-            assert details.status == "integration.changed"
-            assert details.statusScheme == ["integration.changed", "milestone.changed", "release.changed"]
-            assert details.changing
-        }
-    }
-}
-"""
-
-        expect:
-        succeeds 'resolve'
-    }
-
-    def "changes made by a rule are not cached"() {
-        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
-
-        buildFile <<
-                """
-dependencies {
-    components {
-        eachComponent { details ->
-            assert !details.changing
-            assert details.status == "$defaultStatus"
-            assert details.statusScheme == ["integration", "milestone", "release"]
-
-            details.changing = true
-            details.status = "release.changed"
-            details.statusScheme = ["integration.changed", "milestone.changed", "release.changed"]
-        }
-    }
-}
-"""
-
-        expect:
-        succeeds 'resolve'
-        succeeds 'resolve'
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy
deleted file mode 100644
index faa5b49..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.test.fixtures.HttpRepository
-
-abstract class ComponentMetadataRulesStatusIntegrationTest extends AbstractDependencyResolutionTest {
-    abstract HttpRepository getRepo()
-
-    abstract String getRepoDeclaration()
-
-    def setup() {
-        server.start()
-
-        buildFile <<
-"""
-$repoDeclaration
-
-configurations { compile }
-
-dependencies {
-    compile 'org.test:projectA:1.0'
-}
-
-task resolve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 770a261..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
+++ /dev/null
@@ -1,186 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.hamcrest.Matchers
-
-class DependencyNotationIntegrationSpec extends AbstractIntegrationSpec {
-
-    def "understands dependency notations"() {
-        when:
-        buildFile <<  """
-import org.gradle.api.internal.artifacts.dependencies.*
-configurations {
-    conf
-    gradleStuff
-    allowsCollections
-}
-
-def someDependency = new DefaultSelfResolvingDependency(files('foo.txt'))
-dependencies {
-    conf someDependency
-    conf "org.mockito:mockito-core:1.8"
-    conf group: 'org.spockframework', name: 'spock-core', version: '1.0'
-    conf module('org.foo:moduleOne:1.0'), module('org.foo:moduleTwo:1.0')
-
-    gradleStuff gradleApi()
-
-    allowsCollections "org.mockito:mockito-core:1.8", someDependency
-}
-
-task checkDeps << {
-    def deps = configurations.conf.incoming.dependencies
-    assert deps.contains(someDependency)
-    assert deps.find { it instanceof ExternalDependency && it.group == 'org.mockito' && it.name == 'mockito-core' && it.version == '1.8'  }
-    assert deps.find { it instanceof ExternalDependency && it.group == 'org.spockframework' && it.name == 'spock-core' && it.version == '1.0'  }
-    assert deps.find { it instanceof ClientModule && it.name == 'moduleOne' && it.group == 'org.foo' }
-    assert deps.find { it instanceof ClientModule && it.name == 'moduleTwo' && it.version == '1.0' }
-
-    deps = configurations.gradleStuff.dependencies
-    assert deps.findAll { it instanceof SelfResolvingDependency }.size() > 0 : "should include gradle api jars"
-
-    deps = configurations.allowsCollections.dependencies
-    assert deps.size() == 2
-    assert deps.find { it instanceof ExternalDependency && it.group == 'org.mockito' }
-    assert deps.contains(someDependency)
-}
-"""
-        then:
-        succeeds 'checkDeps'
-    }
-
-    def "understands project notations"() {
-        when:
-        settingsFile << "include 'otherProject'"
-
-        buildFile <<  """
-configurations {
-    conf
-    confTwo
-}
-
-project(':otherProject') {
-    configurations {
-        otherConf
-    }
-}
-
-dependencies {
-    conf project(':otherProject')
-    confTwo project(path: ':otherProject', configuration: 'otherConf')
-}
-
-task checkDeps << {
-    def deps = configurations.conf.incoming.dependencies
-    assert deps.size() == 1
-    assert deps.find { it.dependencyProject.path == ':otherProject' }
-
-    deps = configurations.confTwo.incoming.dependencies
-    assert deps.size() == 1
-    assert deps.find { it.dependencyProject.path == ':otherProject' && it.projectConfiguration.name == 'otherConf' }
-}
-"""
-        then:
-        succeeds 'checkDeps'
-    }
-
-    def "understands client module notation with dependencies"() {
-        when:
-        buildFile <<  """
-configurations {
-    conf
-}
-
-dependencies {
-    conf module('org.foo:moduleOne:1.0') {
-        dependency 'org.foo:bar:1.0'
-        dependencies ('org.foo:one:1', 'org.foo:two:1')
-        dependency ('high:five:5') { transitive = false }
-    }
-}
-
-task checkDeps << {
-    def deps = configurations.conf.incoming.dependencies
-    assert deps.size() == 1
-    def dep = deps.find { it instanceof ClientModule && it.name == 'moduleOne' }
-    assert dep
-    assert dep.dependencies.size() == 4
-    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'bar' && it.version == '1.0' && it.transitive == true }
-    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'one' && it.version == '1' }
-    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'two' && it.version == '1' }
-    assert dep.dependencies.find { it.group == 'high' && it.name == 'five' && it.version == '5' && it.transitive == false }
-}
-"""
-        then:
-        succeeds 'checkDeps'
-    }
-
-    def "fails gracefully for invalid notations"() {
-        when:
-        buildFile <<  """
-configurations {
-    conf
-}
-
-dependencies {
-    conf 100
-}
-
-task checkDeps
-"""
-        then:
-        fails 'checkDeps'
-        failure.assertThatCause(Matchers.startsWith("Cannot convert the provided notation to an object of type Dependency: 100."))
-    }
-
-    def "fails gracefully for single null notation"() {
-        when:
-        buildFile <<  """
-configurations {
-    conf
-}
-
-dependencies {
-    conf null
-}
-
-task checkDeps
-"""
-        then:
-        fails 'checkDeps'
-        failure.assertThatCause(Matchers.startsWith("Cannot convert a null value to an object of type Dependency"))
-    }
-
-    def "fails gracefully for null notation in list"() {
-        when:
-        buildFile <<  """
-configurations {
-    conf
-}
-
-dependencies {
-    conf "a:b:c", null, "d:e:f"
-}
-
-task checkDeps
-"""
-        then:
-        fails 'checkDeps'
-        failure.assertThatCause(Matchers.startsWith("Cannot convert a null value to an object of type Dependency"))
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy
deleted file mode 100644
index fc7881e..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy
+++ /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.integtests.resolve
-
-import org.gradle.integtests.fixtures.*
-import spock.lang.Issue
-
-class DependencyResolutionEventsIntegrationTest extends AbstractIntegrationSpec {
-
-    @Issue("http://issues.gradle.org/browse/GRADLE-2047")
-    def "can access resolved files from afterResolve hook"() {
-        given:
-        file("thing.txt") << "stuff"
-        buildFile << """
-            configurations {
-                things.incoming.afterResolve { incoming ->
-                    incoming.files.files
-                    println "accessed files"
-                }
-            }
-            dependencies {
-                things files("thing.txt")
-            }
-
-            task resolveIt(type: Copy, dependsOn: configurations.things) {
-                from configurations.things
-                into buildDir
-            }
-        """
-
-        when:
-        run "resolveIt"
-
-        then:
-        output.contains "accessed files"
-    }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index db1cb3c..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
+++ /dev/null
@@ -1,797 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-
-class DependencyResolveRulesIntegrationTest extends AbstractIntegrationSpec {
-
-    void "forces multiple modules by rule"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5') publish()
-        mavenRepo.module("org.utils", "optional-lib", '5.0').publish()
-
-        //above models the scenario where org.utils:api and org.utils:impl are libraries that must be resolved with the same version
-        //however due to the conflict resolution, org.utils:api:1.5 and org.utils.impl:1.3 are resolved.
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.stuff:foo:2.0', 'org.utils:impl:1.3', 'org.utils:optional-lib:5.0'
-            }
-
-            configurations.conf.resolutionStrategy {
-	            eachDependency {
-                    if (it.requested.group == 'org.utils' && it.requested.name != 'optional-lib') {
-                        it.useVersion '1.5'
-                    }
-	            }
-	            failOnVersionConflict()
-	        }
-"""
-
-        when:
-        run("resolveConf")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "module forced by rule has correct selection reason"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'impl', '1.3') publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.stuff:foo:2.0'
-            }
-
-            configurations.conf.resolutionStrategy {
-	            eachDependency {
-                    if (it.requested.group == 'org.utils') {
-                        it.useVersion '1.5'
-                    }
-	            }
-	        }
-
-	        task check << {
-	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
-	            assert deps*.selected.id.module == ['foo', 'impl', 'api']
-	            assert deps*.selected.id.version == ['2.0', '1.5', '1.5']
-	            assert deps*.selected.selectionReason.forced         == [false, false, false]
-	            assert deps*.selected.selectionReason.selectedByRule == [false, true, true]
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "all rules are executed orderly and last one wins"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy {
-	            eachDependency {
-	                assert it.target == it.requested
-                    it.useVersion '1.4'
-	            }
-	            eachDependency {
-	                assert it.target.version == '1.4'
-	                assert it.target.name == it.requested.name
-	                assert it.target.group == it.requested.group
-                    it.useVersion '1.5'
-	            }
-	            eachDependency {
-	                assert it.target.version == '1.5'
-	                //don't change the version
-	            }
-	        }
-
-	        task check << {
-	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                assert deps.size() == 2
-                deps.each {
-	                assert it.selected.id.version == '1.5'
-	                assert it.selected.selectionReason.selectedByRule
-	                assert it.selected.selectionReason.description == 'selected by rule'
-	            }
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "can unforce the version"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy {
-                force("org.utils:impl:1.5", "org.utils:api:1.5")
-
-	            eachDependency {
-                    it.useVersion it.requested.version
-	            }
-	        }
-
-	        task check << {
-	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                assert deps.size() == 2
-                deps.each {
-	                assert it.selected.id.version == '1.3'
-                    def reason = it.selected.selectionReason
-                    assert !reason.forced
-                    assert reason.selectedByRule
-	            }
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "rule are applied after forced modules"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy {
-                force("org.utils:impl:1.5", "org.utils:api:1.5")
-
-	            eachDependency {
-                    assert it.target.version == '1.5'
-                    it.useVersion '1.3'
-	            }
-	        }
-
-	        task check << {
-	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                assert deps.size() == 2
-                deps.each {
-	                assert it.selected.id.version == '1.3'
-                    def reason = it.selected.selectionReason
-                    assert !reason.forced
-                    assert reason.selectedByRule
-	            }
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "forced modules and rules coexist"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
-
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy {
-                force("org.utils:impl:1.5")
-
-	            eachDependency {
-                    if (it.requested.name == 'api') {
-                        assert it.target == it.requested
-                        it.useVersion '1.5'
-                    }
-	            }
-	        }
-
-	        task check << {
-                def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                assert deps.find {
-                    it.selected.id.module == 'impl' &&
-                    it.selected.id.version == '1.5' &&
-                    it.selected.selectionReason.forced &&
-                    !it.selected.selectionReason.selectedByRule
-                }
-
-                assert deps.find {
-	                it.selected.id.module == 'api' &&
-                    it.selected.id.version == '1.5' &&
-                    !it.selected.selectionReason.forced &&
-                    it.selected.selectionReason.selectedByRule
-	            }
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "rule selects a dynamic version"()
-    {
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.4').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:api:1.3'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                it.useVersion '1.+'
-	        }
-
-	        task check << {
-                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                assert deps.size() == 1
-                assert deps[0].requested.version == '1.3'
-                assert deps[0].selected.id.version == '1.5'
-                assert !deps[0].selected.selectionReason.forced
-                assert deps[0].selected.selectionReason.selectedByRule
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "can blacklist a version"()
-    {
-        mavenRepo.module("org.utils", "a",  '1.4').publish()
-        mavenRepo.module("org.utils", "a",  '1.3').publish()
-        mavenRepo.module("org.utils", "a",  '1.2').publish()
-        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                // a:1.2 is blacklisted, 1.4 should be used instead:
-                if (it.requested.name == 'a' && it.requested.version == '1.2') {
-                    it.useVersion '1.4'
-                }
-	        }
-
-	        task check << {
-                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                def a = modules.find { it.id.module == 'a' }
-                assert a.id.version == '1.4'
-                assert a.selectionReason.conflictResolution
-                assert a.selectionReason.selectedByRule
-                assert !a.selectionReason.forced
-                assert a.selectionReason.description == 'selected by rule and conflict resolution'
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "can blacklist a version that is not used"()
-    {
-        mavenRepo.module("org.utils", "a",  '1.3').publish()
-        mavenRepo.module("org.utils", "a",  '1.2').publish()
-        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                // a:1.2 is blacklisted, 1.2.1 should be used instead:
-                if (it.requested.name == 'a' && it.requested.version == '1.2') {
-                    it.useVersion '1.2.1'
-                }
-	        }
-
-	        task check << {
-                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                def a = modules.find { it.id.module == 'a' }
-                assert a.id.version == '1.3'
-                assert a.selectionReason.conflictResolution
-                assert !a.selectionReason.selectedByRule
-                assert !a.selectionReason.forced
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    def "can use custom versioning scheme"()
-    {
-        mavenRepo.module("org.utils", "api",  '1.3').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:api:default'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                if (it.requested.version == 'default') {
-                    it.useVersion '1.3'
-                }
-	        }
-
-	        task check << {
-                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                assert deps.size() == 1
-                deps[0].requested.version == 'default'
-                deps[0].selected.id.version == '1.3'
-                deps[0].selected.selectionReason.selectedByRule
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    def "can use custom versioning scheme for transitive dependencies"()
-    {
-        mavenRepo.module("org.utils", "api",  '1.3').publish()
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', 'default').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                if (it.requested.version == 'default') {
-                    it.useVersion '1.3'
-                }
-	        }
-
-	        task check << {
-                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                assert deps.size() == 2
-                def api = deps.find { it.requested.module == 'api' }
-                api.requested.version == 'default'
-                api.selected.id.version == '1.3'
-                api.selected.selectionReason.selectedByRule
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "rule selects unavailable version"()
-    {
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:api:1.3'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                it.useVersion '1.123.15' //does not exist
-	        }
-
-	        task check << {
-                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                assert deps.size() == 1
-                assert deps[0].attempted.group == 'org.utils'
-                assert deps[0].attempted.module == 'api'
-                assert deps[0].attempted.version == '1.123.15'
-                assert deps[0].attemptedReason.selectedByRule
-                assert deps[0].failure.message.contains('1.123.15')
-                assert deps[0].requested.version == '1.3'
-	        }
-"""
-
-        when:
-        def failure = runAndFail("check", "resolveConf")
-
-        then:
-        failure.assertResolutionFailure(":conf")
-            .assertHasCause("Could not find org.utils:api:1.123.15")
-    }
-
-    void "rules triggered exactly once per the same dependency"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.5').publish()
-
-        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5').publish()
-        mavenRepo.module("org.stuff", "bar", '2.0').dependsOn('org.utils', 'impl', '1.3').publish()
-
-        /*
-        dependencies:
-
-        impl:1.3->api:1.3
-        foo->api:1.5
-        bar->impl:1.3(*)->api:1.3(*)
-
-        * - should be excluded as it was already visited
-        */
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3', 'org.stuff:foo:2.0', 'org.stuff:bar:2.0'
-            }
-
-            List requested = []
-
-            configurations.conf.resolutionStrategy {
-	            eachDependency {
-                    requested << "\$it.requested.name:\$it.requested.version"
-	            }
-	        }
-
-	        task check << {
-                configurations.conf.resolve()
-                assert requested == ['impl:1.3', 'foo:2.0', 'bar:2.0', 'api:1.3', 'api:1.5']
-	        }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
-    }
-
-    void "runtime exception when evaluating rule yields decent exception"()
-    {
-        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
-        mavenRepo.module("org.utils", "api", '1.3').publish()
-
-        settingsFile << "rootProject.name = 'root'"
-        buildFile << """
-            version = 1.0
-
-            $common
-
-            dependencies {
-                conf 'org.utils:impl:1.3'
-            }
-
-            configurations.conf.resolutionStrategy {
-	            eachDependency {
-                    it.useVersion '1.3' //happy
-	            }
-                eachDependency {
-                    throw new RuntimeException("Unhappy :(")
-	            }
-	        }
-"""
-
-        when:
-        def failure = runAndFail("resolveConf")
-
-        then:
-        failure.assertResolutionFailure(":conf")
-                .assertHasCause("Could not resolve org.utils:impl:1.3.")
-                .assertHasCause("Unhappy :(")
-                .assertFailedDependencyRequiredBy(":root:1.0")
-    }
-
-    void "can substitute module name and resolve conflict"()
-    {
-        mavenRepo.module("org.utils", "a",  '1.2').publish()
-        mavenRepo.module("org.utils", "b",  '2.0').publish()
-        mavenRepo.module("org.utils", "b",  '2.1').publish()
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org.utils:a:1.2', 'org.utils:b:2.0'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                if (it.requested.name == 'a') {
-                    it.useTarget(it.requested.group + ':b:2.1')
-                }
-	        }
-
-	        task check << {
-                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                assert !modules.find { it.id.module == 'a' }
-                def b = modules.find { it.id.module == 'b' }
-                assert b.id.version == '2.1'
-                assert b.selectionReason.conflictResolution
-                assert b.selectionReason.selectedByRule
-                assert !b.selectionReason.forced
-                assert b.selectionReason.description == 'selected by rule and conflict resolution'
-	        }
-"""
-
-        when:
-        run("check", "dependencies")
-
-        then:
-        output.contains(toPlatformLineSeparators("""conf
-+--- org.utils:a:1.2 -> org.utils:b:2.1
-\\--- org.utils:b:2.0 -> 2.1"""))
-    }
-
-    def "can substitute module group"()
-    {
-        mavenRepo.module("org", "a", "1.0").publish()
-        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
-        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
-        mavenRepo.module("org", "c").publish()
-        //a1
-        //b->a2->c
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org:a:1.0', 'foo:b:1.0'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                if (it.requested.group == 'foo') {
-                    it.useTarget('org:' + it.requested.name + ':' + it.requested.version)
-                }
-	        }
-"""
-
-        when:
-        run("dependencies")
-
-        then:
-        output.contains(toPlatformLineSeparators("""
-+--- org:a:1.0 -> 2.0
-|    \\--- org:c:1.0
-\\--- foo:b:1.0 -> org:b:1.0
-     \\--- org:a:2.0 (*)"""))
-    }
-
-    def "can substitute module group, name and version"()
-    {
-        mavenRepo.module("org", "a", "1.0").publish()
-        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
-        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
-        mavenRepo.module("org", "c").publish()
-        //a1
-        //b->a2->c
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org:a:1.0', 'foo:bar:baz'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                if (it.requested.group == 'foo') {
-                    it.useTarget group: 'org', name: 'b', version: '1.0'
-                }
-	        }
-"""
-
-        when:
-        run("dependencies")
-
-        then:
-        output.contains(toPlatformLineSeparators("""
-+--- org:a:1.0 -> 2.0
-|    \\--- org:c:1.0
-\\--- foo:bar:baz -> org:b:1.0
-     \\--- org:a:2.0 (*)"""))
-    }
-
-    def "provides decent feedback when target module incorrectly specified"()
-    {
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org:a:1.0', 'foo:bar:baz'
-            }
-
-            configurations.conf.resolutionStrategy.eachDependency {
-                it.useTarget "foobar"
-	        }
-"""
-
-        when:
-        runAndFail("dependencies")
-
-        then:
-        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()
-        mavenRepo.module("org", "a", "2.0").dependsOn("org", "b", "2.5").publish()
-        mavenRepo.module("org", "b", "3.0").publish()
-        mavenRepo.module("org", "b", "4.0").publish()
-
-        /*
-        I agree this dependency set is awkward but it is the simplest reproducible scenario
-        a:1.0
-        a:2.0 -> b:2.5
-        b:3.0
-        b:4.0
-
-        the conflict resolution of b:
-        1st pass: b:3 vs b:4(wins)
-        2nd pass: b:2.5 vs b:4(wins *again*)
-        */
-
-        buildFile << """
-            $common
-
-            dependencies {
-                conf 'org:b:3.0', 'org:b:4.0', 'org:a:1.0', 'org:a:2.0'
-            }
-
-            task check << {
-                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                assert modules.find { it.id.module == 'b' && it.id.version == '4.0' && it.selectionReason.conflictResolution }
-            }
-"""
-
-        expect:
-        run("check")
-    }
-
-    String getCommon() {
-        """configurations { conf }
-        repositories {
-            maven { url "${mavenRepo.uri}" }
-        }
-        task resolveConf << { configurations.conf.files }
-
-        //resolving the configuration at the end:
-        gradle.startParameter.taskNames += 'resolveConf'
-        """
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
deleted file mode 100644
index e4fe71a..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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
-
-class FlatDirJvmLibraryArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    JvmLibraryArtifactResolveTestFixture fixture
-
-    def setup() {
-        buildFile << """
-repositories {
-    flatDir { dir 'repo' }
-}
-configurations { compile }
-dependencies {
-    compile "some.group:some-artifact:1.0"
-}
-"""
-        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
-    }
-
-    def "resolves and does not cache source and javadoc artifacts"() {
-        publishModule()
-        fixture.requestingTypes()
-                .expectSourceArtifact("sources")
-                .expectJavadocArtifact("javadoc")
-                .prepare()
-
-        when:
-        succeeds("verify")
-
-        and:
-        def snapshot = file("sources/some-artifact-1.0-sources.jar").snapshot()
-
-        and:
-        publishChanged()
-
-        then:
-        succeeds("verify")
-        file("sources/some-artifact-1.0-sources.jar").assertHasChangedSince(snapshot)
-    }
-
-    def "resolves artifacts of non-existing component"() {
-        fixture.expectComponentNotFound().prepare()
-
-        expect:
-        succeeds("verify")
-    }
-
-    def "resolve missing source and javadoc artifacts"() {
-        file("repo/some-artifact-1.0.jar").createFile()
-
-        fixture.requestingTypes().prepare()
-
-        expect:
-        succeeds("verify")
-    }
-
-    def "resolve partially missing artifacts"() {
-        file("repo/some-artifact-1.0.jar").createFile()
-        file("repo/some-artifact-1.0-sources.jar").createFile()
-
-        fixture.requestingTypes()
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        expect:
-        succeeds("verify")
-    }
-
-    def "can only resolve component if main artifact exists"() {
-        file("repo/some-artifact-1.0-sources.jar").createFile()
-        file("repo/some-artifact-1.0-javadoc.jar").createFile()
-
-        fixture.expectComponentNotFound().prepare()
-
-        expect:
-        succeeds("verify")
-    }
-
-    private publishModule() {
-        file("repo/some-artifact-1.0.jar").createFile()
-        file("repo/some-artifact-1.0-sources.jar").createFile()
-        file("repo/some-artifact-1.0-javadoc.jar").createFile()
-    }
-
-    private publishChanged() {
-        file("repo/some-artifact-1.0.jar") << "more"
-        file("repo/some-artifact-1.0-sources.jar") << "more"
-        file("repo/some-artifact-1.0-javadoc.jar") << "more"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy
deleted file mode 100644
index cab646c..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy
+++ /dev/null
@@ -1,214 +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.integtests.resolve
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import org.gradle.api.artifacts.resolution.JvmLibraryArtifact
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionNotFoundException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactNotFoundException
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.test.fixtures.file.TestFile
-/**
- * A test fixture that injects a task into a build that uses the Artifact Query API to download some artifacts, validating the results.
- */
-class JvmLibraryArtifactResolveTestFixture {
-    private final TestFile buildFile
-    private final String config
-    private ModuleComponentIdentifier id = DefaultModuleComponentIdentifier.newId("some.group", "some-artifact", "1.0")
-    private artifactTypes = []
-    private expectedSources = []
-    Throwable expectedSourceFailure
-    private expectedJavadoc = []
-    Throwable expectedJavadocFailure
-    private Throwable unresolvedComponentFailure
-
-    JvmLibraryArtifactResolveTestFixture(TestFile buildFile, String config = "compile") {
-        this.buildFile = buildFile
-        this.config = config
-    }
-
-    JvmLibraryArtifactResolveTestFixture withComponentVersion(String group, String module, String version) {
-        this.id = DefaultModuleComponentIdentifier.newId(group, module, version)
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture requestingTypes(Class<? extends JvmLibraryArtifact>... artifactTypes) {
-        this.artifactTypes = artifactTypes as List
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture clearExpectations() {
-        this.unresolvedComponentFailure = null
-        this.expectedSources = []
-        this.expectedJavadoc = []
-        this.expectedSourceFailure = null
-        this.expectedJavadocFailure = null
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectComponentNotFound() {
-        this.unresolvedComponentFailure = new ModuleVersionNotFoundException(new DefaultModuleVersionSelector(id.group, id.module, id.version))
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectComponentResolutionFailure(Throwable failure) {
-        unresolvedComponentFailure = failure
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectSourceArtifact(String classifier) {
-        expectedSources << "${id.module}-${id.version}-${classifier}.jar"
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectSourceArtifactNotFound(String artifactClassifier) {
-        expectedSourceFailure = new ArtifactNotFoundException(new DefaultModuleVersionArtifactIdentifier(id, id.module, "jar", "jar", [classifier: artifactClassifier]))
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectSourceArtifactFailure(Throwable failure) {
-        expectedSourceFailure = failure
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectJavadocArtifact(String classifier) {
-        expectedJavadoc << "${id.module}-${id.version}-${classifier}.jar"
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectJavadocArtifactNotFound(String artifactClassifier) {
-        expectedJavadocFailure = new ArtifactNotFoundException(new DefaultModuleVersionArtifactIdentifier(id, id.module, "jar", "jar", [classifier: artifactClassifier]))
-        this
-    }
-
-    JvmLibraryArtifactResolveTestFixture expectJavadocArtifactFailure(Throwable failure) {
-        expectedJavadocFailure = failure
-        this
-    }
-
-    /**
-     * Injects the appropriate stuff into the build script.
-     */
-    void prepare() {
-        if (unresolvedComponentFailure != null) {
-            prepareComponentNotFound()
-        } else {
-            createVerifyTask("verify")
-        }
-    }
-
-    void createVerifyTask(def taskName) {
-        buildFile << """
-task $taskName << {
-    def deps = configurations.compile.incoming.resolutionResult.allDependencies as List
-    assert deps.size() == 1
-    def componentId = deps[0].selected.id
-
-    def result = dependencies.createArtifactResolutionQuery()
-        .forComponents(deps[0].selected.id)
-        .withArtifacts(JvmLibrary$artifactTypesString)
-        .execute()
-
-    assert result.components.size() == 1
-    def jvmLibrary = result.components.iterator().next()
-    assert jvmLibrary.id.group == "${id.group}"
-    assert jvmLibrary.id.module == "${id.module}"
-    assert jvmLibrary.id.version == "${id.version}"
-    assert jvmLibrary instanceof JvmLibrary
-
-    def sourceArtifactFiles = []
-    jvmLibrary.sourcesArtifacts.each { artifact ->
-        assert artifact instanceof JvmLibrarySourcesArtifact
-        if (artifact.failure != null) {
-            ${checkException("artifact.failure", expectedSourceFailure)}
-        } else {
-            copy {
-                from artifact.file
-                into "sources"
-            }
-            sourceArtifactFiles << artifact.file.name
-        }
-    }
-    assert sourceArtifactFiles as Set == ${toQuotedList(expectedSources)} as Set
-
-    def javadocArtifactFiles = []
-    jvmLibrary.javadocArtifacts.each { artifact ->
-        assert artifact instanceof JvmLibraryJavadocArtifact
-        if (artifact.failure != null) {
-            ${checkException("artifact.failure", expectedJavadocFailure)}
-        } else {
-            copy {
-                from artifact.file
-                into "javadoc"
-            }
-            javadocArtifactFiles << artifact.file.name
-        }
-    }
-    assert javadocArtifactFiles as Set == ${toQuotedList(expectedJavadoc)} as Set
-
-    assert result.unresolvedComponents.empty
-}
-"""
-    }
-
-    private static String checkException(String reference, Throwable expected) {
-        if (expected == null) {
-            return "throw $reference"
-        }
-        return """
-    assert ${reference} instanceof ${expected.class.name}
-    assert ${reference}.message == "${expected.message}"
-"""
-    }
-
-    private static String toQuotedList(def values) {
-        return values.collect({"\"$it\""}).toListString()
-    }
-
-    private void prepareComponentNotFound() {
-        buildFile << """
-task verify << {
-    def unknownComponentId = [getGroup: {'${id.group}'}, getModule: {'${id.module}'}, getVersion: {'${id.version}'}, getDisplayName: {'unknown'}] as ModuleComponentIdentifier
-    def result = dependencies.createArtifactResolutionQuery()
-        .forComponents(unknownComponentId)
-        .withArtifacts(JvmLibrary$artifactTypesString)
-        .execute()
-
-    assert result.components.empty
-    assert result.unresolvedComponents.size() == 1
-    for (component in result.unresolvedComponents) {
-        assert component.id.group == "${id.group}"
-        assert component.id.module == "${id.module}"
-        assert component.id.version == "${id.version}"
-        assert component.id.displayName == 'unknown'
-        ${checkException("component.failure", unresolvedComponentFailure)}
-    }
-}
-"""
-    }
-
-    private String getArtifactTypesString() {
-        def artifactTypesString = ""
-        for (Class<? extends JvmLibraryArtifact> type : artifactTypes) {
-            artifactTypesString += ", ${type.simpleName}"
-        }
-        return artifactTypesString
-    }
-}
-
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
deleted file mode 100644
index 7c231ce..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,92 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Issue
-
-class ProjectDependenciesIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Issue("GRADLE-2477") //this is a feature on its own but also covers one of the reported issues
-    def "resolving project dependency triggers configuration of the target project"() {
-        settingsFile << "include 'impl'"
-        buildFile << """
-            apply plugin: 'java'
-            dependencies {
-                compile project(":impl")
-            }
-            repositories {
-                //resolving project must declare the repo
-                maven { url '${mavenRepo.uri}' }
-            }
-            println "Resolved at configuration time: " + configurations.compile.files*.name
-        """
-
-        mavenRepo.module("org", "foo").publish()
-        file("impl/build.gradle") << """
-            apply plugin: 'java'
-            dependencies {
-                compile "org:foo:1.0"
-            }
-        """
-
-        when:
-        run()
-
-        then:
-        result.output.contains "Resolved at configuration time: [impl.jar, foo-1.0.jar]"
-    }
-
-    def "configuring project dependencies by map is validated"() {
-        settingsFile << "include 'impl'"
-        buildFile << """
-            allprojects { configurations.create('conf') }
-            task extraKey << {
-                def dep = dependencies.project(path: ":impl", configuration: ":conf", foo: "bar")
-                assert dep.foo == "bar"
-            }
-            task missingPath << {
-                dependencies.project(paths: ":impl", configuration: ":conf")
-            }
-            task missingConfiguration << {
-                dependencies.project(path: ":impl", configurations: ":conf")
-            }
-        """
-
-        when:
-        executer.withDeprecationChecksDisabled()
-        run("extraKey")
-
-        then:
-        noExceptionThrown()
-
-        when:
-        executer.withDeprecationChecksDisabled()
-        run("missingConfiguration")
-
-        then:
-        noExceptionThrown()
-
-        when:
-        runAndFail("missingPath")
-
-        then:
-        failureHasCause("Required keys [path] are missing from map")
-    }
-}
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
deleted file mode 100644
index b12c551..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,422 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import spock.lang.Issue
-
-class ProjectDependencyResolveIntegrationTest extends AbstractIntegrationSpec {
-    public void "project dependency includes artifacts and transitive dependencies of default configuration in target project"() {
-        given:
-        mavenRepo.module("org.other", "externalA", 1.2).publish()
-        mavenRepo.module("org.other", "externalB", 2.1).publish()
-
-        and:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        buildFile << """
-allprojects {
-    repositories { maven { url '$mavenRepo.uri' } }
-}
-project(":a") {
-    configurations {
-        api
-        'default' { extendsFrom api }
-    }
-    dependencies {
-        api "org.other:externalA:1.2"
-        'default' "org.other:externalB:2.1"
-    }
-    task jar(type: Jar) { baseName = 'a' }
-    artifacts { api jar }
-}
-project(":b") {
-    group = 'org.gradle'
-    version = '1.0'
-
-    configurations {
-        compile
-    }
-    dependencies {
-        compile project(':a')
-    }
-
-    task check(dependsOn: configurations.compile) << {
-        assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar', 'externalB-2.1.jar']
-        def result = configurations.compile.incoming.resolutionResult
-
-         // Check root component
-        def rootId = result.root.id
-        assert rootId instanceof ProjectComponentIdentifier
-        def rootPublishedAs = result.root.moduleVersion
-        assert rootPublishedAs.group == 'org.gradle'
-        assert rootPublishedAs.name == 'b'
-        assert rootPublishedAs.version == '1.0'
-
-        // Check project components
-        def projectComponents = result.root.dependencies.selected.findAll { it.id instanceof ProjectComponentIdentifier }
-        assert projectComponents.size() == 1
-        def projectA = projectComponents[0]
-        assert projectA.id.projectPath == ':a'
-        assert projectA.moduleVersion.group != null
-        assert projectA.moduleVersion.name == 'a'
-        assert projectA.moduleVersion.version == 'unspecified'
-
-        // Check project dependencies
-        def projectDependencies = result.root.dependencies.requested.findAll { it instanceof ProjectComponentSelector }
-        assert projectDependencies.size() == 1
-        def projectDependency = projectDependencies[0]
-        assert projectDependency.projectPath == ':a'
-
-        // Check external module components
-        def externalComponents = result.allDependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
-        assert externalComponents.size() == 2
-        def externalA = externalComponents[0]
-        assert externalA.id.group == 'org.other'
-        assert externalA.id.module == 'externalA'
-        assert externalA.id.version == '1.2'
-        assert externalA.moduleVersion.group == 'org.other'
-        assert externalA.moduleVersion.name == 'externalA'
-        assert externalA.moduleVersion.version == '1.2'
-        def externalB = externalComponents[1]
-        assert externalB.id.group == 'org.other'
-        assert externalB.id.module == 'externalB'
-        assert externalB.id.version == '2.1'
-        assert externalB.moduleVersion.group == 'org.other'
-        assert externalB.moduleVersion.name == 'externalB'
-        assert externalB.moduleVersion.version == '2.1'
-    }
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    public void "project dependency that specifies a target configuration includes artifacts and transitive dependencies of selected configuration"() {
-        given:
-        mavenRepo.module("org.other", "externalA", 1.2).publish()
-
-        and:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        buildFile << """
-allprojects {
-    repositories { maven { url '$mavenRepo.uri' } }
-}
-project(":a") {
-    configurations {
-        api
-        runtime { extendsFrom api }
-    }
-    dependencies {
-        api "org.other:externalA:1.2"
-    }
-    task jar(type: Jar) { baseName = 'a' }
-    artifacts { api jar }
-}
-project(":b") {
-    configurations {
-        compile
-    }
-    dependencies {
-        compile project(path: ':a', configuration: 'runtime')
-    }
-    task check(dependsOn: configurations.compile) << {
-        assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar']
-    }
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    @Issue("GRADLE-2899")
-    public void "consuming project can refer to multiple configurations of target project"() {
-        given:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        buildFile << """
-project(':a') {
-    configurations {
-        configA1
-        configA2
-    }
-    task A1jar(type: Jar) {
-        archiveName = 'A1.jar'
-    }
-    task A2jar(type: Jar) {
-        archiveName = 'A2.jar'
-    }
-    artifacts {
-        configA1 A1jar
-        configA2 A2jar
-    }
-}
-
-project(':b') {
-    configurations {
-        configB1
-        configB2
-    }
-    dependencies {
-        configB1 project(path:':a', configuration:'configA1')
-        configB2 project(path:':a', configuration:'configA2')
-    }
-    task check << {
-        assert configurations.configB1.collect { it.name } == ['A1.jar']
-        assert configurations.configB2.collect { it.name } == ['A2.jar']
-    }
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    public void "resolved project artifacts contain project version in their names"() {
-        given:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        file('a/build.gradle') << '''
-            apply plugin: 'base'
-            configurations { compile }
-            task aJar(type: Jar) { }
-            artifacts { compile aJar }
-'''
-        file('b/build.gradle') << '''
-            apply plugin: 'base'
-            version = 'early'
-            configurations { compile }
-            task bJar(type: Jar) { }
-            gradle.taskGraph.whenReady { project.version = 'late' }
-            artifacts { compile bJar }
-'''
-        file('build.gradle') << '''
-            configurations { compile }
-            dependencies { compile project(path: ':a', configuration: 'compile'), project(path: ':b', configuration: 'compile') }
-            task test(dependsOn: configurations.compile) << {
-                assert configurations.compile.collect { it.name } == ['a.jar', 'b-late.jar']
-            }
-'''
-
-        expect:
-        succeeds "test"
-    }
-
-    public void "project dependency that references an artifact includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
-        given:
-        mavenRepo.module("group", "externalA", 1.5).publish()
-
-        and:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        buildFile << """
-allprojects {
-    apply plugin: 'base'
-    repositories { maven { url '${mavenRepo.uri}' } }
-}
-
-project(":a") {
-    configurations { 'default' {} }
-    dependencies { 'default' 'group:externalA:1.5' }
-    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 = 'b'; type = 'jar' } } }
-    task test {
-        inputs.files configurations.compile
-        doFirst {
-            assert configurations.compile.files.collect { it.name } == ['b.jar', 'externalA-1.5.jar']
-        }
-    }
-}
-"""
-
-        expect:
-        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:b.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()
-
-        and:
-        file('settings.gradle') << "include 'a', 'b'"
-
-        and:
-        buildFile << '''
-allprojects {
-    apply plugin: 'java'
-    repositories { maven { url rootProject.uri('repo') } }
-}
-project(':a') {
-    dependencies {
-        compile 'group:externalA:1.5'
-        compile files('libs/externalB.jar')
-    }
-}
-project(':b') {
-    dependencies {
-        compile project(':a'), { transitive = false }
-    }
-    task listJars << {
-        assert configurations.compile.collect { it.name } == ['a.jar']
-    }
-}
-'''
-
-        expect:
-        succeeds "listJars"
-    }
-
-    public void "can have cycle in project dependencies"() {
-        given:
-        file('settings.gradle') << "include 'a', 'b', 'c'"
-
-        and:
-        buildFile << """
-
-subprojects {
-    apply plugin: 'base'
-    configurations {
-        'default'
-        other
-    }
-    task jar(type: Jar)
-    artifacts {
-        'default' jar
-    }
-}
-
-project('a') {
-    dependencies {
-        'default' project(':b')
-        other project(':b')
-    }
-    task listJars {
-        dependsOn configurations.default
-        dependsOn configurations.other
-        doFirst {
-            def jars = configurations.default.collect { it.name } as Set
-            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
-
-            jars = configurations.other.collect { it.name } as Set
-            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
-        }
-    }
-}
-
-project('b') {
-    dependencies {
-        'default' project(':c')
-    }
-}
-
-project('c') {
-    dependencies {
-        'default' project(':a')
-    }
-}
-"""
-
-        expect:
-        succeeds "listJars"
-    }
-
-    // this test is largely covered by other tests, but does ensure that there is nothing special about
-    // project dependencies that are “built” by built in plugins like the Java plugin's created jars
-    def "can use zip files as project dependencies"() {
-        given:
-        file("settings.gradle") << "include 'a'; include 'b'"
-        file("a/some.txt") << "foo"
-        file("a/build.gradle") << """
-            group = "g"
-            version = 1.0
-            
-            apply plugin: 'base'
-            task zip(type: Zip) {
-                from "some.txt"
-            }
-
-            artifacts {
-                delegate.default zip
-            }
-        """
-        file("b/build.gradle") << """
-            configurations { conf }
-            dependencies {
-                conf project(":a")
-            }
-            
-            task copyZip(type: Copy) {
-                from configurations.conf
-                into "\$buildDir/copied"
-            }
-        """
-        
-        when:
-        succeeds ":b:copyZip"
-        
-        then:
-        ":b:copyZip" in nonSkippedTasks
-        
-        and:
-        file("b/build/copied/a-1.0.zip").exists()
-    }
-}
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
deleted file mode 100644
index 50903c5..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
+++ /dev/null
@@ -1,58 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.Sample
-import org.junit.Rule
-
-public class ResolutionStrategySamplesIntegrationTest extends AbstractIntegrationSpec {
-
-    @Rule public final Sample sample = new Sample(temporaryFolder, 'userguide/artifacts/resolutionStrategy')
-
-    void "can resolve dependencies"()
-    {
-        mavenRepo.module("org", "foo").publish()
-        mavenRepo.module("org", "bar").publish()
-        mavenRepo.module("org.gradle", "gradle-core", "1.4").publish()
-        mavenRepo.module("org.software", "some-library", "1.2.1").publish()
-        mavenRepo.module("org.codehaus", "groovy", "1.7").publish()
-        mavenRepo.module("org.slf4j", "log4j-over-slf4j", "1.7.5").publish()
-
-        sample.dir.file("build.gradle") << """
-            configurations { conf }
-            repositories { maven { url "${mavenRepo.uri}" } }
-            dependencies {
-                conf "org:foo:1.0"
-                conf "org.gradle:gradle-core:1.0"
-                conf "org:bar:default"
-                conf "org.software:some-library:1.2"
-                conf "org.codehaus:groovy-all:1.7"
-                conf "log4j:log4j:1.2"
-            }
-            task resolveConf << { configurations.conf.files }
-        """
-
-        when:
-        inDirectory(sample.dir)
-        //smoke testing if dependency resolution works fine
-        run("resolveConf")
-
-        then:
-        noExceptionThrown()
-    }
-}
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
deleted file mode 100644
index 81d178b..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
+++ /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.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 {
-    mavenRepo urls: "http://localhost:${server.port}/repo"
-}
-
-configurations {
-    compile
-}
-
-dependencies {
-    compile 'test:io:1.4'
-    compile 'test:lang:2.+'
-}
-
-task check << {
-    assert configurations.compile*.name as Set == ['io-1.4.jar', 'lang-2.6.jar'] as Set
-}
-"""
-
-        expect:
-        version previous withTasks 'check' run()
-        version current withTasks 'check' run()
-        version previous withTasks 'check' run()
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy
deleted file mode 100644
index ff4cc62..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy
+++ /dev/null
@@ -1,333 +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.integtests.resolve
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.artifacts.result.ResolvedComponentResult
-import org.gradle.api.tasks.TaskAction
-import org.gradle.internal.classloader.ClasspathUtil
-import org.gradle.test.fixtures.file.TestFile
-
-/**
- * A test fixture that injects a task into a build that resolves a dependency configuration and does some validation of the resulting graph, to
- * ensure that the old and new dependency graphs plus the artifacts and files are as expected and well-formed.
- */
-class ResolveTestFixture {
-    private final TestFile buildFile
-    private final String config
-
-    ResolveTestFixture(TestFile buildFile, String config = "compile") {
-        this.config = config
-        this.buildFile = buildFile
-    }
-
-    /**
-     * Injects the appropriate stuff into the build script.
-     */
-    void prepare() {
-        buildFile << """
-buildscript {
-    dependencies.classpath files("${ClasspathUtil.getClasspathForClass(GenerateGraphTask).toURI()}")
-}
-
-allprojects {
-    tasks.addPlaceholderAction("checkDeps") {
-        tasks.create(name: "checkDeps", dependsOn: configurations.${config}, type: ${GenerateGraphTask.name}) {
-            outputFile = rootProject.file("\${rootProject.buildDir}/${config}.txt")
-            configuration = configurations.$config
-        }
-    }
-}
-"""
-    }
-
-    /**
-     * Verifies the result of executing the task injected by {@link #prepare()}. The closure delegates to a {@link GraphBuilder} instance.
-     */
-    void expectGraph(Closure closure) {
-        def graph = new GraphBuilder()
-        closure.resolveStrategy = Closure.DELEGATE_ONLY
-        closure.delegate = graph
-        closure.call()
-
-        if (graph.root == null) {
-            throw new IllegalArgumentException("No root node defined")
-        }
-
-        def configDetailsFile = buildFile.parentFile.file("build/${config}.txt")
-        def configDetails = configDetailsFile.text.readLines()
-
-        def actualArtifacts = configDetails.findAll { it.startsWith('artifact:') }.collect { it.substring(9) }
-        def expectedArtifacts = graph.artifactNodes.collect { "[${it.moduleVersionId}][${it.module}.jar]" }
-        assert actualArtifacts == expectedArtifacts
-
-        def actualFiles = configDetails.findAll { it.startsWith('file:') }.collect { it.substring(5) }
-        def expectedFiles = graph.artifactNodes.collect { it.fileName }
-        assert actualFiles == expectedFiles
-
-        def actualFirstLevel = configDetails.findAll { it.startsWith('first-level:') }.collect { it.substring(12) }
-        def expectedFirstLevel = graph.root.deps.collect { "[${it.selected.moduleVersionId}:default]" }
-        assert actualFirstLevel == expectedFirstLevel
-
-        def actualRoot = configDetails.find { it.startsWith('root:') }.substring(5)
-        def expectedRoot = "[id:${graph.root.id}][mv:${graph.root.moduleVersionId}][reason:${graph.root.reason}]"
-        assert actualRoot == expectedRoot
-
-        def actualNodes = configDetails.findAll { it.startsWith('component:') }.collect { it.substring(10) }
-        def expectedNodes = graph.nodes.values().collect { "[id:${it.id}][mv:${it.moduleVersionId}][reason:${it.reason}]" }
-        assert actualNodes == expectedNodes
-
-        def actualEdges = configDetails.findAll { it.startsWith('dependency:') }.collect { it.substring(11) }
-        def expectedEdges = graph.edges.collect { "[from:${it.from.id}][${it.requested}->${it.selected.id}]" }
-        assert actualEdges == expectedEdges
-    }
-
-    public static class GraphBuilder {
-        final Map<String, NodeBuilder> nodes = new LinkedHashMap<>()
-        NodeBuilder root
-
-        private getArtifactNodes() {
-            Set<NodeBuilder> result = new LinkedHashSet()
-            visitNodes(root, result)
-            return result
-        }
-
-        private void visitNodes(NodeBuilder node, Set<NodeBuilder> result) {
-            Set<NodeBuilder> nodesToVisit = []
-            for (EdgeBuilder edge: node.deps) {
-                if (result.add(edge.selected)) {
-                    nodesToVisit << edge.selected
-                }
-            }
-            for(NodeBuilder child: nodesToVisit) {
-                visitNodes(child, result)
-            }
-        }
-
-        private getEdges() {
-            Set<EdgeBuilder> result = new LinkedHashSet<>()
-            Set<NodeBuilder> seen = []
-            visitEdges(root, seen, result)
-            return result
-        }
-
-        private visitEdges(NodeBuilder node, Set<NodeBuilder> seenNodes, Set<EdgeBuilder> edges) {
-            for (EdgeBuilder edge: node.deps) {
-                edges.add(edge)
-                if (seenNodes.add(edge.selected)) {
-                    visitEdges(edge.selected, seenNodes, edges)
-                }
-            }
-        }
-
-        /**
-         * Defines the root node of the graph. The closure delegates to a {@link NodeBuilder} instance that represents the root node.
-         */
-        def root(String value, Closure cl) {
-            if (root != null) {
-                throw new IllegalStateException("Root node is already defined")
-            }
-            root = node(value, value)
-            cl.resolveStrategy = Closure.DELEGATE_ONLY
-            cl.delegate = root
-            cl.call()
-            return root
-        }
-
-        def root(String path, String value, Closure cl) {
-            if (root != null) {
-                throw new IllegalStateException("Root node is already defined")
-            }
-            root = node("project $path", value)
-            cl.resolveStrategy = Closure.DELEGATE_ONLY
-            cl.delegate = root
-            cl.call()
-            return root
-        }
-
-        def node(String id, String moduleVersion) {
-            def node = nodes[moduleVersion]
-            if (!node) {
-                node = new NodeBuilder(id, moduleVersion, this)
-                nodes[moduleVersion] = node
-            }
-            return node
-        }
-    }
-
-    public static class EdgeBuilder {
-        final String requested
-        final NodeBuilder from
-        final NodeBuilder selected
-
-        EdgeBuilder(NodeBuilder from, String requested, NodeBuilder selected) {
-            this.from = from
-            this.selected = selected
-            this.requested = requested
-        }
-    }
-
-    public static class NodeBuilder {
-        final List<EdgeBuilder> deps = []
-        private final GraphBuilder graph
-        final String id
-        final String moduleVersionId
-        final String group
-        final String module
-        final String version
-        private String reason
-
-        NodeBuilder(String id, String moduleVersionId, GraphBuilder graph) {
-            this.graph = graph
-            if (moduleVersionId.matches(':\\w+:')) {
-                def parts = moduleVersionId.split(':')
-                this.group = null
-                this.module = parts[1]
-                this.version = null
-                this.moduleVersionId = ":${module}:unspecified"
-            } else if (moduleVersionId.matches('\\w+:\\w+:')) {
-                def parts = moduleVersionId.split(':')
-                this.group = parts[0]
-                this.module = parts[1]
-                this.version = null
-                this.moduleVersionId = "${group}:${module}:unspecified"
-            } else {
-                def parts = moduleVersionId.split(':')
-                assert parts.length == 3
-                this.group = parts[0]
-                this.module = parts[1]
-                this.version = parts[2]
-                this.moduleVersionId = moduleVersionId
-            }
-            if (id == moduleVersionId) {
-                this.id = this.moduleVersionId
-            } else {
-                this.id = id
-            }
-        }
-
-        private def getReason() {
-            reason ?: (this == graph.root ? 'root:' : 'requested:')
-        }
-
-        private def getFileName() {
-            "$module${version ? '-' + version : ''}.jar"
-        }
-
-        private def addNode(String id, String moduleVersionId = id) {
-            def node = graph.node(id, moduleVersionId)
-            deps << new EdgeBuilder(this, node.id, node)
-            return node
-        }
-
-        /**
-         * Defines a dependency on the given external module.
-         */
-        def module(String moduleVersionId) {
-            return addNode(moduleVersionId)
-        }
-
-        /**
-         * Defines a dependency on the given external module. The closure delegates to a {@link NodeBuilder} instance that represents the target node.
-         */
-        def module(String moduleVersionId, Closure cl) {
-            def node = module(moduleVersionId)
-            cl.resolveStrategy = Closure.DELEGATE_ONLY
-            cl.delegate = node
-            cl.call()
-            return node
-        }
-
-        /**
-         * Defines a dependency on the given project. The closure delegates to a {@link NodeBuilder} instance that represents the target node.
-         */
-        def project(String path, String value, Closure cl) {
-            def node = addNode("project $path", value)
-            cl.resolveStrategy = Closure.DELEGATE_ONLY
-            cl.delegate = node
-            cl.call()
-            return node
-        }
-
-        /**
-         * Defines a dependency on the given node.
-         */
-        def edge(String requested, String selectedModuleVersionId) {
-            def node = graph.node(selectedModuleVersionId, selectedModuleVersionId)
-            deps << new EdgeBuilder(this, requested, node)
-            return node
-        }
-
-        /**
-         * Marks that this node was selected due to conflict resolution.
-         */
-        def byConflictResolution() {
-            reason = 'conflict resolution:conflict'
-            this
-        }
-    }
-}
-
-public class GenerateGraphTask extends DefaultTask {
-    File outputFile
-    Configuration configuration
-
-    @TaskAction
-    def generateOutput() {
-        outputFile.parentFile.mkdirs()
-
-        outputFile.withPrintWriter { writer ->
-            configuration.resolvedConfiguration.firstLevelModuleDependencies.each {
-                writer.println("first-level:[${it.moduleGroup}:${it.moduleName}:${it.moduleVersion}:${it.configuration}]")
-            }
-            configuration.resolvedConfiguration.resolvedArtifacts.each {
-                writer.println("artifact:[${it.moduleVersion.id}][${it.name}${it.classifier ? "-" + it.classifier : ""}.${it.extension}]")
-            }
-            def root = configuration.incoming.resolutionResult.root
-            writer.println("root:${formatComponent(root)}")
-            configuration.incoming.resolutionResult.allComponents.each {
-                writer.println("component:${formatComponent(it)}")
-            }
-            configuration.incoming.resolutionResult.allDependencies.each {
-                writer.println("dependency:[from:${it.from.id}][${it.requested}->${it.selected.id}]")
-            }
-            configuration.files.each {
-                writer.println("file:${it.name}")
-            }
-        }
-    }
-
-    def formatComponent(ResolvedComponentResult result) {
-        return "[id:${result.id}][mv:${result.moduleVersion}][reason:${formatReason(result.selectionReason)}]"
-    }
-
-    def formatReason(ComponentSelectionReason reason) {
-        def reasons = []
-        if (reason.conflictResolution) {
-            reasons << "conflict"
-        }
-        if (reason.forced) {
-            reasons << "forced"
-        }
-        if (reason.selectedByRule) {
-            reasons << "selectedByRule"
-        }
-        return "${reason.description}:${reasons.join(',')}"
-    }
-}
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
deleted file mode 100644
index 4fbb47a..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve
-
-import org.gradle.api.Project
-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.TestUtil
-import org.junit.Before
-import org.junit.Test
-
-public class ResolvedConfigurationIntegrationTest extends AbstractIntegrationTest {
-    def DefaultProject project = TestUtil.createRootProject()
-    def Project childProject = TestUtil.createChildProject(project, "child", new File("."))
-
-    @Before
-    public void boringSetup() {
-        project.allprojects { apply plugin: 'java' }
-
-        project.repositories {
-            maven { url mavenRepo.uri }
-        }
-    }
-
-    @Test
-    public void "resolves leniently"() {
-        mavenRepo.module('org.foo', 'hiphop').publish()
-        mavenRepo.module('org.foo', 'rock').dependsOn("some unresolved dependency").publish()
-
-        project.dependencies {
-            compile 'org.foo:hiphop:1.0'
-            compile 'unresolved.org:hiphopxx:3.0' //does not exist
-            compile childProject
-
-            compile 'org.foo:rock:1.0' //contains unresolved transitive dependency
-        }
-
-        LenientConfiguration compile = project.configurations.compile.resolvedConfiguration.lenientConfiguration
-
-        def unresolved = compile.getUnresolvedModuleDependencies()
-        def resolved = compile.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
-
-        assert resolved.size() == 3
-        assert resolved.find { it.moduleName == 'hiphop' }
-        assert resolved.find { it.moduleName == 'rock' }
-        assert resolved.find { it.moduleName == 'child' }
-
-        assert unresolved.size() == 2
-        assert unresolved.find { it.selector.group == 'unresolved.org' && it.selector.name == 'hiphopxx' && it.selector.version == '3.0' }
-        assert unresolved.find { it.selector.name == 'some unresolved dependency' }
-    }
-
-    @Test
-    public void "resolves leniently from mixed confs"() {
-        mavenRepo.module('org.foo', 'hiphop').publish()
-        mavenRepo.module('org.foo', 'rock').dependsOn("some unresolved dependency").publish()
-
-        project.allprojects { apply plugin: 'java' }
-
-        project.repositories {
-            maven { url mavenRepo }
-        }
-
-        project.configurations {
-            someConf
-        }
-
-        project.dependencies {
-            compile 'org.foo:hiphop:1.0'
-            someConf 'org.foo:hiphopxx:1.0' //does not exist
-        }
-
-        LenientConfiguration compile = project.configurations.compile.resolvedConfiguration.lenientConfiguration
-
-        def unresolved = compile.getUnresolvedModuleDependencies()
-        def resolved = compile.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
-
-        assert resolved.size() == 1
-        assert resolved.find { it.moduleName == 'hiphop' }
-        assert unresolved.size() == 0
-
-        LenientConfiguration someConf = project.configurations.someConf.resolvedConfiguration.lenientConfiguration
-
-        unresolved = someConf.getUnresolvedModuleDependencies()
-        resolved = someConf.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
-
-        assert resolved.size() == 0
-        assert unresolved.size() == 1
-        assert unresolved.find { it.selector.name == 'hiphopxx' }
-    }
-}
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
deleted file mode 100644
index 29e0938..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,733 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import spock.lang.Issue
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-import static org.hamcrest.Matchers.containsString
-
-public class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
-
-    void "strict conflict resolution should fail due to conflict"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.4.4')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		compile project(':api')
-		compile project(':impl')
-	}
-
-	configurations.compile.resolutionStrategy.failOnVersionConflict()
-}
-"""
-
-        expect:
-        runAndFail("tool:dependencies")
-        failure.assertThatCause(containsString('A conflict was found between the following modules:'))
-    }
-
-    void "strict conflict resolution should pass when no conflicts"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		compile project(':api')
-		compile project(':impl')
-	}
-
-	configurations.all { resolutionStrategy.failOnVersionConflict() }
-}
-"""
-
-        expect:
-        run("tool:dependencies")
-    }
-
-    void "resolves to the latest version by default"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-
-        settingsFile << """
-rootProject.name = 'test'
-include 'api', 'impl', 'tool'
-"""
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		compile (group: 'org', name: 'foo', version:'1.4.4')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		compile project(':api')
-		compile project(':impl')
-	}
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        run("tool:checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":tool", "test:tool:") {
-                project(":api", "test:api:") {
-                    edge("org:foo:1.3.3", "org:foo:1.4.4")
-                }
-                project(":impl", "test:impl:") {
-                    module("org:foo:1.4.4").byConflictResolution()
-                }
-            }
-        }
-    }
-
-    void "does not attempt to resolve an evicted dependency"() {
-        mavenRepo.module("org", "external", "1.2").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.0").publish()
-
-        settingsFile << "rootProject.name = 'test'"
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:external:1.2").byConflictResolution()
-                module("org:dep:2.2") {
-                    edge("org:external:1.0", "org:external:1.2")
-                }
-            }
-        }
-    }
-
-    @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.allComponents*.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()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.+").publish()
-
-        def buildFile = file("build.gradle")
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-
-task checkDeps << {
-    assert configurations.compile*.name == ['dep-2.2.jar', 'external-1.4.jar']
-}
-"""
-
-        expect:
-        run("checkDeps")
-    }
-
-    void "fails when version selected by conflict resolution does not exist"() {
-        mavenRepo.module("org", "external", "1.2").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-
-task checkDeps << {
-    assert configurations.compile*.name == ['external-1.2.jar', 'dep-2.2.jar']
-}
-"""
-
-        expect:
-        runAndFail("checkDeps")
-        failure.assertHasCause("Could not find org:external:1.4.")
-    }
-
-    void "does not fail when evicted version does not exist"() {
-        mavenRepo.module("org", "external", "1.4").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-
-task checkDeps << {
-    assert configurations.compile*.name == ['dep-2.2.jar', 'external-1.4.jar']
-}
-"""
-
-        expect:
-        run("checkDeps")
-    }
-
-    void "takes newest dynamic version when dynamic version forced"() {
-        mavenRepo.module("org", "foo", '1.3.0').publish()
-
-        mavenRepo.module("org", "foo", '1.4.1').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-        mavenRepo.module("org", "foo", '1.4.9').publish()
-
-        mavenRepo.module("org", "foo", '1.6.0').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		compile 'org:foo:1.4.4'
-	}
-}
-
-project(':impl') {
-	dependencies {
-		compile 'org:foo:1.4.1'
-	}
-}
-
-project(':tool') {
-
-	dependencies {
-		compile project(':api'), project(':impl'), 'org:foo:1.3.0'
-	}
-
-	configurations.all {
-	    resolutionStrategy {
-	        force 'org:foo:1.4+'
-	        failOnVersionConflict()
-	    }
-	}
-
-	task checkDeps << {
-        assert configurations.compile*.name.contains('foo-1.4.9.jar')
-    }
-}
-
-"""
-
-        expect:
-        run("tool:checkDeps")
-    }
-
-    void "parent pom does not participate in forcing mechanism"() {
-        mavenRepo.module("org", "foo", '1.3.0').publish()
-        mavenRepo.module("org", "foo", '2.4.0').publish()
-
-        def parent = mavenRepo.module("org", "someParent", "1.0")
-        parent.type = 'pom'
-        parent.dependsOn("org", "foo", "1.3.0")
-        parent.publish()
-
-        def otherParent = mavenRepo.module("org", "someParent", "2.0")
-        otherParent.type = 'pom'
-        otherParent.dependsOn("org", "foo", "2.4.0")
-        otherParent.publish()
-
-        def module = mavenRepo.module("org", "someArtifact", '1.0')
-        module.parentPomSection = """
-<parent>
-  <groupId>org</groupId>
-  <artifactId>someParent</artifactId>
-  <version>1.0</version>
-</parent>
-"""
-        module.publish()
-
-        buildFile << """
-apply plugin: 'java'
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-dependencies {
-    compile 'org:someArtifact:1.0'
-}
-
-configurations.all {
-    resolutionStrategy {
-        force 'org:someParent:2.0'
-        failOnVersionConflict()
-    }
-}
-
-task checkDeps << {
-    def deps = configurations.compile*.name
-    assert deps.contains('someArtifact-1.0.jar')
-    assert deps.contains('foo-1.3.0.jar')
-    assert deps.size() == 2
-}
-"""
-
-        expect:
-        run("checkDeps")
-    }
-
-    void "previously evicted nodes should contain correct target version"() {
-        /*
-        a1->b1
-        a2->b2->a1
-
-        resolution process:
-
-        1. stop resolution, resolve conflict a1 vs a2
-        2. select a2, restart resolution
-        3. stop, resolve b1 vs b2
-        4. select b2, restart
-        5. resolve b2 dependencies, a1 has been evicted previously but it should show correctly on the report
-           ('dependencies' report pre 1.2 would not show the a1 dependency leaf for this scenario)
-        */
-
-        ivyRepo.module("org", "b", '1.0').publish()
-        ivyRepo.module("org", "a", '1.0').dependsOn("org", "b", '1.0').publish()
-        ivyRepo.module("org", "b", '2.0').dependsOn("org", "a", "1.0").publish()
-        ivyRepo.module("org", "a", '2.0').dependsOn("org", "b", '2.0').publish()
-
-        buildFile << """
-            repositories {
-                ivy { url "${ivyRepo.uri}" }
-            }
-
-            configurations {
-                conf
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:a:2.0'
-            }
-            task checkDeps << {
-                assert configurations.conf*.name == ['a-2.0.jar', 'b-2.0.jar']
-                def result = configurations.conf.incoming.resolutionResult
-                assert result.allComponents.size() == 3
-                def root = result.root
-                assert root.dependencies*.toString() == ['org:a:1.0 -> org:a:2.0', 'org:a:2.0']
-                def a = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
-                assert a.dependencies*.toString() == ['org:b:2.0']
-                def b = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b' }
-                assert b.dependencies*.toString() == ['org:a:1.0 -> org:a:2.0']
-            }
-        """
-
-        expect:
-        run("checkDeps")
-    }
-
-    @Issue("GRADLE-2555")
-    void "can deal with transitive with parent in conflict"() {
-        /*
-            Graph looks like…
-
-            \--- org:a:1.0
-                 \--- org:in-conflict:1.0 -> 2.0
-                      \--- org:target:1.0
-                           \--- org:target-child:1.0
-            \--- org:b:1.0
-                 \--- org:b-child:1.0
-                      \--- org:in-conflict:2.0 (*)
-
-            This is the simplest structure I could boil it down to that produces the error.
-            - target *must* have a child
-            - Having "b" depend directly on "in-conflict" does not produce the error, needs to go through "b-child"
-         */
-
-        mavenRepo.module("org", "target-child", "1.0").publish()
-        mavenRepo.module("org", "target", "1.0").dependsOn("org", "target-child", "1.0").publish()
-        mavenRepo.module("org", "in-conflict", "1.0").dependsOn("org", "target", "1.0").publish()
-        mavenRepo.module("org", "in-conflict", "2.0").dependsOn("org", "target", "1.0").publish()
-
-        mavenRepo.module("org", "a", '1.0').dependsOn("org", "in-conflict", "1.0").publish()
-
-        mavenRepo.module("org", "b-child", '1.0').dependsOn("org", "in-conflict", "2.0").publish()
-
-        mavenRepo.module("org", "b", '1.0').dependsOn("org", "b-child", "1.0").publish()
-
-        buildFile << """
-            repositories { maven { url "${mavenRepo.uri}" } }
-
-            configurations { conf }
-
-            dependencies {
-                conf "org:a:1.0", "org:b:1.0"
-            }
-
-        task checkDeps << {
-            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.allComponents.size() == 7
-            def a = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
-            assert a.dependencies*.toString() == ['org:in-conflict:1.0 -> org:in-conflict:2.0']
-            def bChild = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b-child' }
-            assert bChild.dependencies*.toString() == ['org:in-conflict:2.0']
-            def target = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'target' }
-            assert target.dependents*.from*.toString() == ['org:in-conflict:2.0']
-        }
-        """
-
-        expect:
-        run("checkDeps")
-    }
-
-    @Issue("GRADLE-2555")
-    void "batched up conflicts with conflicted parent and child"() {
-        /*
-        Dependency tree:
-
-        a->c1
-        b->c2->x1
-        d->x1
-        f->x2
-
-        Everything is resolvable but not x2
-
-        Scenario:
-         - We have batched up conflicts
-         - parent of one conflicted version is also conflicted
-         - conflicted parent is positioned on the conflicts queue after the conflicted child (the order of declaring dependencies matters)
-         - winning parent depends on a child that previously was evicted
-         - finally, the winning child is an unresolved dependency
-        */
-        mavenRepo.module("org", "c", '1.0').publish()
-        mavenRepo.module("org", "x", '1.0').publish()
-        mavenRepo.module("org", "c", '2.0').dependsOn("org", "x", '1.0').publish()
-        mavenRepo.module("org", "a").dependsOn("org", "c", "1.0").publish()
-        mavenRepo.module("org", "b").dependsOn("org", "c", "2.0").publish()
-        mavenRepo.module("org", "d").dependsOn("org", "x", "1.0").publish()
-        mavenRepo.module("org", "f").dependsOn("org", "x", "2.0").publish()
-
-        buildFile << """
-            repositories { maven { url "${mavenRepo.uri}" } }
-            configurations {
-                childFirst
-                parentFirst
-            }
-            dependencies {
-                //conflicted child is resolved first
-                childFirst 'org:d:1.0', 'org:f:1.0', 'org:a:1.0', 'org:b:1.0'
-                //conflicted parent is resolved first
-                parentFirst 'org:a:1.0', 'org:b:1.0', 'org:d:1.0', 'org:f:1.0'
-            }
-        """
-
-        when:
-        run("dependencies")
-
-        then:
-        output.contains(toPlatformLineSeparators("""
-childFirst
-+--- org:d:1.0
-|    \\--- org:x:1.0 -> 2.0 FAILED
-+--- org:f:1.0
-|    \\--- org:x:2.0 FAILED
-+--- org:a:1.0
-|    \\--- org:c:1.0 -> 2.0
-|         \\--- org:x:1.0 -> 2.0 FAILED
-\\--- org:b:1.0
-     \\--- org:c:2.0 (*)
-
-parentFirst
-+--- org:a:1.0
-|    \\--- org:c:1.0 -> 2.0
-|         \\--- org:x:1.0 -> 2.0 FAILED
-+--- org:b:1.0
-|    \\--- org:c:2.0 (*)
-+--- org:d:1.0
-|    \\--- org:x:1.0 -> 2.0 FAILED
-\\--- org:f:1.0
-     \\--- org:x:2.0 FAILED"""))
-    }
-
-    @Issue("GRADLE-2752")
-    void "selects root module when earlier version of module 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"
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", "org:test:1.3") {
-                module("org:other:1.7") {
-                    edge("org:test:1.2", "org:test:1.3")
-                }
-            }
-        }
-    }
-
-    @Issue("GRADLE-2920")
-    void "selects later version of root module when requested"() {
-        mavenRepo.module("org", "test", "2.1").publish()
-        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "2.1").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"
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", "org:test:1.3") {
-                module("org:other:1.7") {
-                    module("org:test:2.1").byConflictResolution()
-                }
-            }
-        }
-    }
-
-    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.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == '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/AliasedArtifactResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
deleted file mode 100644
index dea3a73..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.artifactreuse
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Ignore
-
-class AliasedArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    def mavenRepo1 = mavenHttpRepo("maven1")
-    def mavenRepo2 = mavenHttpRepo("maven2")
-    def ivyRepo1 = ivyHttpRepo("ivy1")
-    def ivyRepo2 = ivyHttpRepo("ivy2")
-
-    def setup() {
-        server.start()
-
-        buildFile << """
-            repositories {
-                if (project.hasProperty('mavenRepository1')) {
-                    maven { url '${mavenRepo1.uri}' }
-                } else if (project.hasProperty('mavenRepository2')) {
-                    maven { url '${mavenRepo2.uri}' }
-                } else if (project.hasProperty('ivyRepository1')) {
-                    ivy { url '${ivyRepo1.uri}' }
-                } else if (project.hasProperty('ivyRepository2')) {
-                    ivy { url '${ivyRepo2.uri}' }
-                } else if (project.hasProperty('fileRepository')) {
-                    maven { url '${mavenRepo.uri}' }
-                }
-            }
-            configurations { compile }
-            dependencies {
-                compile 'org.name:projectB:1.0'
-            }
-
-            task retrieve(type: Sync) {
-                into 'libs'
-                from configurations.compile
-            }
-        """
-    }
-
-    def "does not re-download maven artifact downloaded from a different maven repository when sha1 matches"() {
-        when:
-        def projectBModuleRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBModuleRepo1.pom.expectGet()
-        projectBModuleRepo1.artifact.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository1'
-
-        when:
-        def projectBModuleRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
-        projectBModuleRepo2.pom.expectHead()
-        projectBModuleRepo2.pom.sha1.expectGet()
-        projectBModuleRepo2.artifact.expectHead()
-        projectBModuleRepo2.artifact.sha1.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository2'
-    }
-
-    def "does not re-download ivy artifact downloaded from a different ivy repository when sha1 matches"() {
-        when:
-        def projectBRepo1 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo1.ivy.expectGet()
-        projectBRepo1.jar.expectGet()
-
-        then:
-        succeedsWith 'ivyRepository1'
-
-        when:
-        def projectBRepo2 = ivyRepo2.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo2.ivy.expectHead()
-        projectBRepo2.ivy.sha1.expectGet()
-        projectBRepo2.jar.expectHead()
-        projectBRepo2.jar.sha1.expectGet()
-
-        then:
-        succeedsWith 'ivyRepository2'
-    }
-
-    def "does not re-download ivy artifact downloaded from a maven repository when sha1 matches"() {
-        when:
-        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo1.pom.expectGet()
-        projectBRepo1.artifact.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository1'
-
-        when:
-        def projectBRepo2 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo2.ivy.expectGet()
-        projectBRepo2.jar.expectHead()
-        projectBRepo2.jar.sha1.expectGet()
-
-        then:
-        succeedsWith 'ivyRepository1'
-    }
-
-    def "does not re-download maven artifact downloaded from a ivy repository when sha1 matches"() {
-        when:
-        def projectBRepo1 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo1.ivy.expectGet()
-        projectBRepo1.jar.expectGet()
-
-        then:
-        succeedsWith 'ivyRepository1'
-
-        when:
-        def projectBRepo2 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo2.pom.expectGet()
-        projectBRepo2.artifact.expectHead()
-        projectBRepo2.artifact.sha1.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository1'
-    }
-
-    @Ignore("File repository does not cache artifacts locally, so they are not used to prevent download")
-    def "does not download artifact previously accessed from a file uri when sha1 matches"() {
-        given:
-        succeedsWith 'fileRepository'
-
-        when:
-        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo2.pom.expectHead()
-        projectBRepo2.pom.sha1.expectGet()
-        projectBRepo2.artifact.expectHead()
-        projectBRepo2.artifact.sha1.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository2'
-    }
-
-    def "does re-download maven artifact downloaded from a different URI when sha1 not found"() {
-        when:
-        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo1.pom.expectGet()
-        projectBRepo1.artifact.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository1'
-
-        when:
-        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo2.pom.expectHead()
-        projectBRepo2.pom.sha1.expectGetMissing()
-        projectBRepo2.pom.expectGet()
-        projectBRepo2.artifact.expectHead()
-        projectBRepo2.artifact.sha1.expectGetMissing()
-        projectBRepo2.getArtifact().expectGet()
-
-        then:
-        succeedsWith 'mavenRepository2'
-    }
-
-    def "does re-download maven artifact downloaded from a different URI when sha1 does not match"() {
-        when:
-        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
-        projectBRepo1.pom.expectGet()
-        projectBRepo1.artifact.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository1'
-
-        when:
-        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publishWithChangedContent()
-        projectBRepo2.pom.expectHead()
-        projectBRepo2.pom.sha1.expectGet()
-        projectBRepo2.pom.expectGet()
-
-        projectBRepo2.artifact.expectHead()
-        projectBRepo2.artifact.sha1.expectGet()
-        projectBRepo2.artifact.expectGet()
-
-        then:
-        succeedsWith 'mavenRepository2'
-    }
-
-    def succeedsWith(repository) {
-        executer.withArguments('-i', "-P${repository}")
-        def result = succeeds 'retrieve'
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        server.resetExpectations()
-        return result
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy
deleted file mode 100644
index e33cf9d..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.artifactreuse
-
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.junit.Rule
-
-import org.gradle.integtests.fixtures.IgnoreVersions
-import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
-
- at TargetVersions('1.0-milestone-6+')
- at IgnoreVersions({ it.artifactCacheLayoutVersion == DefaultCacheLockingManager.CACHE_LAYOUT_VERSION || it.version.version == "1.9-rc-1" })
-class CacheReuseCrossVersionIntegrationTest extends AbstractCacheReuseCrossVersionIntegrationTest {
-    @Rule public final HttpServer server = new HttpServer()
-    final MavenHttpRepository httpRepo = new MavenHttpRepository(server, new MavenFileRepository(file("maven-repo")))
-
-    def "uses cached artifacts from previous Gradle version when no sha1 header"() {
-        given:
-        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
-        server.sendSha1Header = false
-        server.start()
-        buildFile << """
-repositories {
-    maven { url '${httpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectB:1.0'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        def userHome = file('user-home')
-
-        when:
-        projectB.allowAll()
-
-        and:
-        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        def snapshot = file('libs/projectB-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        projectB.pom.expectHead()
-        projectB.pom.sha1.expectGet()
-        projectB.artifact.expectHead()
-        projectB.artifact.sha1.expectGet()
-
-        and:
-        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
-    }
-
-    def "uses cached artifacts from previous Gradle version with sha1 header"() {
-        given:
-        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
-        server.sendSha1Header = true
-        server.start()
-        buildFile << """
-repositories {
-    maven { url '${httpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectB:1.0'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        def userHome = file('user-home')
-
-        when:
-        projectB.allowAll()
-
-        and:
-        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        def snapshot = file('libs/projectB-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        projectB.pom.expectHead()
-        projectB.artifact.expectHead()
-
-        and:
-        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
-    }
-
-    def "uses cached artifacts from previous Gradle version that match dynamic version"() {
-        given:
-        def projectB = httpRepo.module('org.name', 'projectB', '1.1').publish()
-        server.start()
-
-        buildFile << """
-repositories {
-    maven { url '${httpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectB:[1.0,2.0]'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        def userHome = file('user-home')
-
-        when:
-        httpRepo.getModuleMetaData("org.name", "projectB").expectGet()
-        projectB.allowAll()
-
-        and:
-        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.1.jar')
-        def snapshot = file('libs/projectB-1.1.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        httpRepo.getModuleMetaData("org.name", "projectB").expectGet()
-        projectB.pom.expectHead()
-        projectB.pom.sha1.expectGet()
-        projectB.artifact.expectHead()
-        projectB.artifact.sha1.expectGet()
-
-        and:
-        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.1.jar')
-        file('libs/projectB-1.1.jar').assertContentsHaveNotChangedSince(snapshot)
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy
deleted file mode 100644
index 65c1318..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.artifactreuse
-
-import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.junit.Rule
-
-// Special case for milestone-3 since that version (and earlier versions) do not include POM reuse.
- at TargetVersions('1.0-milestone-3')
-class M3CacheReuseCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
-    @Rule public final HttpServer server = new HttpServer()
-    final MavenHttpRepository remoteRepo = new MavenHttpRepository(server, mavenRepo)
-
-    def "uses cached artifacts from previous Gradle version"() {
-        given:
-        def projectB = remoteRepo.module('org.name', 'projectB').publish()
-
-        server.start()
-        buildFile << """
-repositories {
-    mavenRepo(urls: ['${remoteRepo.uri}'])
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectB:1.0'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        def userHome = file('user-home')
-
-        when:
-        projectB.allowAll()
-
-        and:
-        version previous withGradleUserHomeDir userHome withTasks 'retrieve' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        def snapshot = file('libs/projectB-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        projectB.pom.expectGet()
-        projectB.artifact.expectHead()
-        projectB.artifact.sha1.expectGet()
-
-        and:
-        version current withGradleUserHomeDir userHome withTasks 'retrieve' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.groovy
deleted file mode 100644
index 291946f..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.groovy
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.artifactreuse
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class MavenM2CacheReuseIntegrationTest extends AbstractDependencyResolutionTest {
-    def "uses cached artifacts from maven local cache"() {
-        given:
-        def module1 = mavenHttpRepo.module('gradletest.maven.local.cache.test', "foo", "1.0").publish()
-        m2Installation.generateGlobalSettingsFile()
-        def module2 = m2Installation.mavenRepo().module('gradletest.maven.local.cache.test', "foo", "1.0").publish()
-        server.start()
-
-        buildFile.text = """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations { compile }
-dependencies {
-    compile 'gradletest.maven.local.cache.test:foo:1.0'
-}
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'build'
-}
-"""
-        and:
-        module1.pom.expectHead()
-        module1.pom.sha1.expectGet()
-        module1.artifact.expectHead()
-        module1.artifact.sha1.expectGet()
-
-        when:
-        using m2Installation
-        run 'retrieve'
-
-        then:
-        file('build/foo-1.0.jar').assertIsCopyOf(module2.artifactFile)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
deleted file mode 100644
index 1c79efa..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.artifactreuse
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class ResolutionOverrideIntegrationTest extends AbstractDependencyResolutionTest {
-    public void "will refresh non-changing module when run with --refresh-dependencies"() {
-        given:
-        server.start()
-        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
-
-        and:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations { compile }
-dependencies { compile 'org.name:projectA:1.2' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        module.allowAll()
-
-        when:
-        succeeds 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar')
-        def snapshot = file('libs/projectA-1.2.jar').snapshot()
-
-        when:
-        module.publishWithChangedContent()
-
-        and:
-        server.resetExpectations()
-        module.pom.expectHead()
-        module.pom.sha1.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectHead()
-        module.artifact.sha1.expectGet()
-        module.artifact.expectGet()
-
-        and:
-        executer.withArguments('--refresh-dependencies')
-        succeeds 'retrieve'
-
-        then:
-        file('libs/projectA-1.2.jar').assertIsCopyOf(module.artifactFile).assertHasChangedSince(snapshot)
-    }
-
-    public void "will recover from missing module when run with --refresh-dependencies"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
-        def artifact = module.artifact
-
-        buildFile << """
-repositories {
-    maven {
-        url "${mavenHttpRepo.uri}"
-    }
-}
-configurations { missing }
-dependencies {
-    missing 'org.name:projectA:1.2'
-}
-task showMissing << { println configurations.missing.files }
-"""
-
-        when:
-        module.pom.expectGetMissing()
-        artifact.expectHeadMissing()
-
-        then:
-        fails("showMissing")
-
-        when:
-        server.resetExpectations()
-        module.pom.expectGet()
-        module.getArtifact().expectGet()
-
-        then:
-        executer.withArguments("--refresh-dependencies")
-        succeeds('showMissing')
-    }
-
-    public void "will recover from missing artifact when run with --refresh-dependencies"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    maven {
-        url "${mavenHttpRepo.uri}"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectA:1.2'
-}
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        and:
-        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
-        def artifact = module.artifact
-
-        when:
-        module.pom.expectGet()
-        artifact.expectGetMissing()
-
-        then:
-        fails "retrieve"
-
-        when:
-        server.resetExpectations()
-        module.pom.expectHead()
-        artifact.expectGet()
-
-        then:
-        executer.withArguments("--refresh-dependencies")
-        succeeds 'retrieve'
-        file('libs').assertHasDescendants('projectA-1.2.jar')
-    }
-
-    public void "will not expire cache entries when run with offline flag"() {
-
-        given:
-        server.start()
-        def module = mavenHttpRepo.module("org.name", "unique", "1.0-SNAPSHOT").publish()
-
-        and:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations { compile }
-configurations.all {
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-dependencies {
-    compile "org.name:unique:1.0-SNAPSHOT"
-}
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when: "Server handles requests"
-        module.allowAll()
-
-        and: "We resolve dependencies"
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar')
-        def snapshot = file('libs/unique-1.0-SNAPSHOT.jar').snapshot()
-
-        when:
-        module.publishWithChangedContent()
-
-        and: "We resolve again, offline"
-        server.resetExpectations()
-        executer.withArguments('--offline')
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar')
-        file('libs/unique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshot)
-    }
-
-    public void "does not attempt to contact server when run with offline flag"() {
-        given:
-        server.start()
-
-        and:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations { compile }
-dependencies { compile 'org.name:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        executer.withArguments("--offline")
-
-        then:
-        fails 'listJars'
-
-        and:
-        failure.assertHasDescription('Execution failed for task \':listJars\'.')
-        failure.assertResolutionFailure(":compile")
-            .assertHasCause('No cached version of org.name:projectA:1.2 available for offline mode')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy
deleted file mode 100644
index 3daa366..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy
+++ /dev/null
@@ -1,77 +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.integtests.resolve.artifactreuse
-
-import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
-import org.gradle.integtests.fixtures.IgnoreVersions
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.junit.Rule
-
- at TargetVersions('1.0-milestone-6+')
- at IgnoreVersions({ it.artifactCacheLayoutVersion != DefaultCacheLockingManager.CACHE_LAYOUT_VERSION })
-class SameCacheUsageCrossVersionIntegrationTest extends AbstractCacheReuseCrossVersionIntegrationTest {
-    @Rule public final HttpServer server = new HttpServer()
-    final MavenHttpRepository httpRepo = new MavenHttpRepository(server, new MavenFileRepository(file("maven-repo")))
-
-    def "incurs zero remote requests when cache version not upgraded"() {
-        given:
-        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
-        server.sendSha1Header = false
-        server.start()
-        buildFile << """
-repositories {
-    maven { url '${httpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name:projectB:1.0'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-        and:
-        def userHome = file('user-home')
-
-        when:
-        projectB.allowAll()
-
-        and:
-        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        def snapshot = file('libs/projectB-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        //expect no http requests
-
-        and:
-        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
-
-        then:
-        file('libs').assertHasDescendants('projectB-1.0.jar')
-        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy
deleted file mode 100644
index 3031cb9..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy
+++ /dev/null
@@ -1,232 +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.integtests.resolve.caching
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-public class CachedChangingModulesIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def "can cache and refresh unique versioned maven artifacts with a classifier"() {
-        given:
-        server.start()
-        def repo = mavenHttpRepo("repo")
-        def module = repo.module("group", "projectA", "1.0-SNAPSHOT")
-        def sourceArtifact = module.artifact(classifier: "source")
-
-        module.publish()
-        buildFile << """
-        repositories {
-            maven {
-                name 'repo'
-                url '${repo.uri}'
-            }
-        }
-        configurations {
-            compile
-        }
-
-        configurations.all {
-            resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-        }
-
-        dependencies {
-            compile 'group:projectA:1.0-SNAPSHOT:source'
-        }
-
-        task retrieve(type: Sync) {
-            into 'libs'
-            from configurations.compile
-        }
-        """
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectGet()
-        module.metaData.expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        module.metaData.expectGet()
-        sourceArtifact.expectHead()
-        module.pom.expectHead()
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-
-        module.metaData.expectGet()
-        module.pom.sha1.expectGet()
-        module.pom.expectHead()
-        module.pom.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-        sourceArtifact.sha1.expectGet()
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-    }
-
-    def "can cache and refresh non unique versioned maven artifacts with a classifier"() {
-        given:
-        server.start()
-        def repo = mavenHttpRepo("repo")
-        def module = repo.module("group", "projectA", "1.0-SNAPSHOT").withNonUniqueSnapshots()
-        def sourceArtifact = module.artifact(classifier: "source")
-
-        module.publish()
-        buildFile << """
-        repositories {
-            maven {
-                name 'repo'
-                url '${repo.uri}'
-            }
-        }
-        configurations {
-            compile
-        }
-
-        configurations.all {
-            resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-        }
-
-        dependencies {
-            compile 'group:projectA:1.0-SNAPSHOT:source'
-        }
-
-        task retrieve(type: Sync) {
-            into 'libs'
-            from configurations.compile
-        }
-        """
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectGet()
-        module.metaData.expectGetMissing()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        module.metaData.expectGetMissing()
-        sourceArtifact.expectHead()
-        module.pom.expectHead()
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-
-        module.metaData.expectGetMissing()
-        module.pom.sha1.expectGet()
-        module.pom.expectHead()
-        module.pom.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-        sourceArtifact.sha1.expectGet()
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-    }
-
-    def "can cache and refresh ivy changing artifacts with a classifier"() {
-        given:
-        server.start()
-        def repo = ivyHttpRepo("repo")
-        def module = repo.module("group", "projectA", "1.0")
-        module.artifact(classifier: "source")
-
-        module.publish()
-        buildFile << """
-          repositories {
-              ivy {
-                  name 'repo'
-                  url '${repo.uri}'
-              }
-          }
-          configurations {
-              compile
-          }
-
-          configurations.all {
-              resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-          }
-
-          dependencies {
-                compile group: "group", name: "projectA", version: "1.0", classifier: "source", changing: true
-          }
-
-          task retrieve(type: Sync) {
-              into 'libs'
-              from configurations.compile
-          }
-          """
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "source").expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.getArtifact(classifier: 'source').expectHead()
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.getArtifact(classifier: 'source').expectHead()
-
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.getArtifact(classifier: 'source').expectGet()
-        module.getArtifact(classifier: 'source').sha1.expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy
deleted file mode 100644
index dfaaa68..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,239 +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.integtests.resolve.caching
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import org.gradle.test.fixtures.server.http.HttpServer
-import spock.lang.Issue
-
-/**
- * We are using Ivy here, but the strategy is the same for any kind of repository.
- */
-class CachedDependencyResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    IvyHttpModule module
-
-    TestFile downloaded
-    TestFile.Snapshot lastState
-
-    def setup() {
-        server.start()
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-configurations.all {
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1.1", changing: true
-}
-
-task retrieve(type: Sync) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        downloaded = file('build/projectA-1.1.jar')
-    }
-
-    void initialResolve() {
-        module.ivy.expectGet()
-        module.jar.expectGet()
-        resolve()
-    }
-
-    void resolve() {
-        if (downloaded.exists()) {
-            lastState = downloaded.snapshot()
-        }
-
-        succeeds ":retrieve"
-    }
-
-    void headOnlyRequests() {
-        module.ivy.expectHead()
-        module.jar.expectHead()
-    }
-
-    void headSha1ThenGetRequests() {
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-    }
-
-    void sha1OnlyRequests() {
-        module.ivy.sha1.expectGet()
-        module.jar.sha1.expectGet()
-    }
-
-    void sha1ThenGetRequests() {
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-    }
-
-    void headThenSha1Requests() {
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-    }
-
-    void headThenGetRequests() {
-        module.ivy.expectHead()
-        module.ivy.expectGet()
-
-        module.jar.expectHead()
-        module.jar.expectGet()
-    }
-
-    void unchangedResolve() {
-        resolve()
-        downloaded.assertHasNotChangedSince(lastState)
-    }
-
-    void changedResolve() {
-        resolve()
-        downloaded.assertHasChangedSince(lastState)
-    }
-
-    void change() {
-        module.publishWithChangedContent()
-    }
-
-    def "etags are used to determine changed"() {
-        given:
-        server.etags = HttpServer.EtagStrategy.RAW_SHA1_HEX
-        server.sendLastModified = false
-        initialResolve()
-
-        expect:
-        headOnlyRequests()
-        unchangedResolve()
-
-        when:
-        change()
-
-        then:
-        headSha1ThenGetRequests()
-        changedResolve()
-    }
-
-    def "last modified and content length are used to determine changed"() {
-        given:
-        server.etags = null
-        initialResolve()
-
-        expect:
-        headOnlyRequests()
-        unchangedResolve()
-
-        when:
-        change()
-
-        then:
-        headSha1ThenGetRequests()
-        changedResolve()
-    }
-
-    def "checksum is used when last modified and content length can't be used"() {
-        given:
-        server.etags = null
-        server.sendLastModified = false
-        initialResolve()
-
-        expect:
-        headThenSha1Requests()
-        unchangedResolve()
-
-        when:
-        change()
-
-        then:
-        headSha1ThenGetRequests()
-        changedResolve()
-    }
-
-    def "no need for sha1 request if we get it in the metadata"() {
-        given:
-        server.sendSha1Header = true
-        initialResolve()
-
-        expect:
-        headOnlyRequests()
-        unchangedResolve()
-
-        when:
-        change()
-
-        then:
-        headThenGetRequests()
-        changedResolve()
-    }
-
-    def "no need for sha1 request if we know the etag is sha1"() {
-        given:
-        server.etags = HttpServer.EtagStrategy.NEXUS_ENCODED_SHA1
-        initialResolve()
-
-        expect:
-        headOnlyRequests()
-        unchangedResolve()
-
-        when:
-        change()
-
-        then:
-        headThenGetRequests()
-        changedResolve()
-    }
-
-    @Issue("GRADLE-2781")
-    def "no leading zeros in sha1 checksums supported"() {
-        given:
-        server.etags = null
-        server.sendLastModified = false
-        byte[] jarBytes = [0, 0, 0, 5]
-        module.jarFile.bytes = jarBytes
-        initialResolve()
-        expect:
-        headThenSha1Requests()
-        trimLeadingZerosFromSHA1()
-        unchangedResolve()
-    }
-
-    def trimLeadingZerosFromSHA1() {
-        //remove leading zeros from sha1 checksum
-        new File("${module.jarFile.absolutePath}.sha1").text = "e14c6ef59816760e2c9b5a57157e8ac9de4012"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
deleted file mode 100644
index 883cd01..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
+++ /dev/null
@@ -1,295 +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.integtests.resolve.caching
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import spock.lang.IgnoreIf
-
-class CachedMissingModulesIntegrationTest extends AbstractDependencyResolutionTest {
-
-    public void "caches missing module when module found in another repository"() {
-        server.start()
-
-        given:
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-        def moduleInRepo1 = repo1.module("group", "projectA", "1.2")
-        def moduleInRepo2 = repo2.module('group', 'projectA', '1.2').publish()
-
-        buildFile << """
-repositories {
-    ivy { url "${repo1.uri}"}
-    ivy { url "${repo2.uri}"}
-}
-configurations { missing }
-dependencies {
-    missing 'group:projectA:1.2'
-}
-task showMissing << { println configurations.missing.files }
-"""
-
-        when:
-        moduleInRepo1.ivy.expectGetMissing()
-        moduleInRepo1.jar.expectHeadMissing()
-        moduleInRepo2.ivy.expectGet()
-        moduleInRepo2.jar.expectGet()
-
-        then:
-        succeeds("showMissing")
-
-        when:
-        server.resetExpectations() // Missing status in repo1 is cached
-
-        then:
-        succeeds('showMissing')
-    }
-
-    def "cached not-found information for dynamic version is ignored if module is not available in any repo"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        repo1.module("group", "projectA", "1.0")
-        def repo2 = mavenHttpRepo("repo2")
-        def repo2Module = repo2.module("group", "projectA", "1.0")
-
-        buildFile << """
-            repositories {
-                maven {
-                    name 'repo1'
-                    url '${repo1.uri}'
-                }
-                maven {
-                    name 'repo2'
-                    url '${repo2.uri}'
-                }
-            }
-            configurations { compile }
-            dependencies {
-                compile 'group:projectA:latest.integration'
-            }
-
-            task retrieve(type: Sync) {
-                into 'libs'
-                from configurations.compile
-            }
-            """
-
-        when:
-        repo1.getModuleMetaData("group", "projectA").expectGetMissing()
-        repo1.expectDirectoryListGet("group", "projectA")
-        repo2.getModuleMetaData("group", "projectA").expectGetMissing()
-        repo2.expectDirectoryListGet("group", "projectA")
-
-        then:
-        runAndFail 'retrieve'
-
-        when:
-        server.resetExpectations()
-        repo1.getModuleMetaData("group", "projectA").expectGetMissing()
-        repo1.expectDirectoryListGet("group", "projectA")
-        repo2Module.publish()
-        repo2.getModuleMetaData("group", "projectA").expectGet()
-        repo2Module.pom.expectGet()
-        repo2Module.getArtifact().expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-
-        then:
-        run 'retrieve'
-    }
-
-    def "cached not-found information for fixed version is ignored if module is not available in any repo"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo1Module = repo1.module("group", "projectA", "1.0")
-        def repo1Artifact = repo1Module.artifact
-
-        def repo2 = mavenHttpRepo("repo2")
-        def repo2Module = repo2.module("group", "projectA", "1.0")
-        def repo2Artifact = repo2Module.artifact
-
-        buildFile << """
-    repositories {
-        maven {
-            name 'repo1'
-            url '${repo1.uri}'
-        }
-        maven {
-            name 'repo2'
-            url '${repo2.uri}'
-        }
-    }
-    configurations { compile }
-    dependencies {
-        compile 'group:projectA:1.0'
-    }
-
-    task retrieve(type: Sync) {
-        into 'libs'
-        from configurations.compile
-    }
-    """
-
-        when:
-        repo1Module.pom.expectGetMissing()
-        repo1Artifact.expectHeadMissing()
-        repo2Module.pom.expectGetMissing()
-        repo2Artifact.expectHeadMissing()
-
-        then:
-        runAndFail 'retrieve'
-
-        when:
-        server.resetExpectations()
-        repo1Module.pom.expectGetMissing()
-        repo1Artifact.expectHeadMissing()
-        repo2Module.publish()
-        repo2Module.pom.expectGet()
-        repo2Artifact.expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-
-        then:
-        run 'retrieve'
-    }
-
-    @IgnoreIf({ GradleContextualExecuter.isParallel() })
-    def "hit each remote repo only once per build and missing module"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo1Module = repo1.module("group", "projectA", "1.0")
-        def repo1Artifact = repo1Module.artifact
-        def repo2 = mavenHttpRepo("repo2")
-        def repo2Module = repo2.module("group", "projectA", "1.0")
-        def repo2Artifact = repo2Module.artifact
-
-        settingsFile << "include 'subproject'"
-        buildFile << """
-            allprojects{
-                repositories {
-                    maven {
-                        name 'repo1'
-                        url '${repo1.uri}'
-                    }
-                    maven {
-                        name 'repo2'
-                        url '${repo2.uri}'
-                    }
-                }
-            }
-            configurations {
-                config1
-            }
-            dependencies {
-                config1 'group:projectA:1.0'
-            }
-
-            task resolveConfig1 << {
-                   configurations.config1.incoming.resolutionResult.allDependencies{
-                        it instanceof UnresolvedDependencyResult
-                   }
-            }
-
-            project(":subproject"){
-                configurations{
-                    config2
-                }
-                dependencies{
-                    config2 'group:projectA:1.0'
-                }
-                task resolveConfig2 << {
-                    configurations.config2.incoming.resolutionResult.allDependencies{
-                        it instanceof UnresolvedDependencyResult
-                    }
-                }
-            }
-        """
-        when:
-        repo1Module.pom.expectGetMissing()
-        repo1Artifact.expectHeadMissing()
-        repo2Module.pom.expectGetMissing()
-        repo2Artifact.expectHeadMissing()
-
-        then:
-        run('resolveConfig1')
-
-        when:
-        server.resetExpectations()
-        repo1Module.pom.expectGetMissing()
-        repo1Artifact.expectHeadMissing()
-        repo2Module.pom.expectGetMissing()
-        repo2Artifact.expectHeadMissing()
-
-        then:
-        run "resolveConfig1", "resolveConfig2"
-    }
-
-    def "does not hit remote repositories if version is available in local repo"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo1Module = repo1.module("group", "projectA", "1.0")
-        def repo2 = mavenRepo("repo2")
-        def repo2Module = repo2.module("group", "projectA", "1.0")
-
-        buildFile << """
-        repositories {
-           maven {
-               name 'repo1'
-               url '${repo1.uri}'
-           }
-           maven {
-               name 'repo2'
-               url '${repo2.uri}'
-           }
-       }
-       configurations { compile }
-       dependencies {
-           compile 'group:projectA:1.0'
-       }
-
-       task retrieve(type: Sync) {
-           into 'libs'
-           from configurations.compile
-       }
-       """
-
-        when:
-        repo2Module.publish()
-        repo1Module.pom.expectGetMissing()
-        repo1Module.artifact.expectHeadMissing()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        then:
-        run 'retrieve'
-    }
-}
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
deleted file mode 100644
index 0723800..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
+++ /dev/null
@@ -1,197 +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.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 "version list, 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.+'
-                two 'org:lib:1.+'
-            }
-            //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/caching/RecoverFromBrokenResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/RecoverFromBrokenResolutionIntegrationTest.groovy
deleted file mode 100644
index 4c829dd..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/caching/RecoverFromBrokenResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,357 +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.integtests.resolve.caching
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.maven.MavenHttpModule
-import org.hamcrest.Matchers
-
-class RecoverFromBrokenResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def repo
-    def module
-
-    def setup() {
-        server.start()
-    }
-
-    private void buildFileWithSnapshotDependency() {
-        buildFile << """
-            configurations {
-                compile
-            }
-
-            configurations.all {
-                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-            }
-
-            dependencies {
-                compile 'group:projectA:1.0-SNAPSHOT'
-            }
-
-            task retrieve(type: Sync) {
-                into 'libs'
-                from configurations.compile
-            }
-            """
-    }
-
-    def "can run offline mode after hitting broken repo url"() {
-        given:
-        buildFileWithSnapshotDependency()
-        and:
-        noAuthorizationRepo()
-
-        publishedMavenModule()
-        when:
-        moduleAvailableViaHttp()
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        server.addBroken("/")
-        then:
-        fails 'retrieve'
-
-        and:
-        //TODO should expose the failed task in the error message like
-        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
-        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertThatCause(Matchers.containsString("Received status code 500 from server: broken"))
-
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
-    }
-
-    def "can run offline mode after connection problem with repo url using unique snapshot version"() {
-        given:
-        buildFileWithSnapshotDependency()
-        noAuthorizationRepo()
-        publishedMavenModule()
-
-        when:
-        moduleAvailableViaHttp()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        int port = server.port
-        server.stop()
-        then:
-        fails 'retrieve'
-
-        and:
-        //TODO should expose the failed task in the error message like
-        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
-        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
-    }
-
-    def "can run offline mode after connection problem with repo url using non unique snapshot version"() {
-        given:
-        buildFileWithSnapshotDependency()
-        noAuthorizationRepo()
-        publishedMavenModule(true)
-
-        when:
-        moduleAvailableViaHttpWithoutMetaData()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        int port = server.port
-        server.stop()
-        then:
-        fails 'retrieve'
-
-        and:
-        //TODO should expose the failed task in the error message like
-        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
-        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
-    }
-
-    def "can run offline mode after authentication fails on remote repo"() {
-        given:
-        buildFileWithSnapshotDependency()
-        and:
-        authorizationRepo()
-        when:
-        publishedMavenModule()
-        server.resetExpectations()
-        server.expectGet('/repo/group/projectA/1.0-SNAPSHOT/maven-metadata.xml', 'username', 'password', module.metaDataFile)
-        server.expectGet("/repo/group/projectA/1.0-SNAPSHOT/${module.pomFile.name}", 'username', 'password', module.pomFile)
-        server.expectGet("/repo/group/projectA/1.0-SNAPSHOT/${module.artifactFile.name}", 'username', 'password', module.artifactFile)
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        server.allowGetOrHead('/repo/group/projectA/1.0-SNAPSHOT/maven-metadata.xml', 'bad_username', 'password', module.metaDataFile)
-
-        then:
-        fails 'retrieve'
-
-        and:
-        //TODO should expose the failed task in the error message like
-        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
-        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertThatCause(Matchers.containsString("Received status code 401 from server: Unauthorized"))
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
-    }
-
-    def "can run offline mode after connection problem with repo when using ivy changing modules"() {
-        given:
-        def ivyRepo = ivyHttpRepo("ivyRepo")
-        def ivyModule = ivyRepo.module("group", "projectA", "1.0")
-        ivyModule.publish()
-        and:
-        buildFile.text = """
-                  repositories {
-                       ivy {
-                           name 'repo'
-                           url '${ivyRepo.uri}'
-                       }
-                  }
-                  configurations {
-                      compile
-                  }
-
-                  configurations.all {
-                      resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-                  }
-
-                  dependencies {
-                      compile group:'group', name:'projectA', version:'1.0', changing:true
-                  }
-
-                  task retrieve(type: Sync) {
-                      into 'libs'
-                      from configurations.compile
-                  }"""
-        when:
-        ivyModule.ivy.expectGet()
-        ivyModule.jar.expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        int port = server.port
-        server.stop()
-        then:
-        fails 'retrieve'
-
-        and:
-        //TODO should expose the failed task in the error message like
-        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
-        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.0.jar').assertIsCopyOf(ivyModule.jarFile)
-    }
-
-    def "can run offline mode after connection problem with repo when using ivy dynamic version"() {
-        given:
-        def ivyRepo = ivyHttpRepo("ivyRepo")
-        def ivyModule = ivyRepo.module("group", "projectA", "1.1")
-        ivyModule.publish()
-        and:
-        buildFile << """
-                  repositories {
-                       ivy { url '${ivyRepo.uri}' }
-                  }
-                  configurations {
-                      compile
-                  }
-                  configurations.all {
-                      resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
-                  }
-                  dependencies {
-                      compile group:'group', name:'projectA', version:'1.+'
-                  }
-
-                  task retrieve(type: Sync) {
-                      into 'libs'
-                      from configurations.compile
-                  }"""
-        when:
-        ivyModule.repository.expectDirectoryListGet('group', 'projectA')
-        ivyModule.ivy.expectGet()
-        ivyModule.jar.expectGet()
-
-        then:
-        run 'retrieve'
-
-        when:
-        server.resetExpectations()
-        int port = server.port
-        server.stop()
-        then:
-        fails 'retrieve'
-
-        and:
-        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
-        failure.assertHasCause("Could not list versions using Ivy pattern 'http://localhost:${port}/ivyRepo/[organisation]/[module]/[revision]/ivy-[revision].xml")
-        failure.assertHasCause("Connection to http://localhost:${port} refused")
-
-        when:
-        server.resetExpectations()
-        then:
-        executer.withArgument("--offline")
-        run 'retrieve'
-        and:
-        file('libs/projectA-1.1.jar').assertIsCopyOf(ivyModule.jarFile)
-    }
-
-    def moduleAvailableViaHttp() {
-        module.metaData.expectGet()
-        module.pom.expectGet()
-        module.getArtifact().expectGet()
-    }
-
-    def moduleAvailableViaHttpWithoutMetaData() {
-        module.metaData.expectGetMissing()
-        module.pom.expectGet()
-        module.getArtifact().expectGet()
-    }
-
-
-    private MavenHttpModule publishedMavenModule(withNonUniqueVersion = false) {
-        module = repo.module("group", "projectA", "1.0-SNAPSHOT")
-        if (withNonUniqueVersion) {
-            module.withNonUniqueSnapshots()
-        }
-        module.publish()
-        module
-    }
-
-    private noAuthorizationRepo() {
-        repo = mavenHttpRepo("repo")
-
-        buildFile << """
-        repositories {
-            maven {
-                name 'repo'
-                url '${repo.uri}'
-            }
-        } """
-    }
-
-    private authorizationRepo() {
-        repo = mavenHttpRepo("repo")
-        buildFile << """
-        repositories {
-            maven {
-                url '${repo.uri}'
-                credentials {
-                    password 'password'
-                    username 'username'
-                }
-            }
-        }
-        """
-    }
-
-}
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
deleted file mode 100644
index 652edfe..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/FileSystemResolverIntegrationTest.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.integtests.resolve.custom
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class FileSystemResolverIntegrationTest extends AbstractIntegrationSpec {
-    def "file system resolvers use item at source by default"() {
-        given:
-        def module = ivyRepo.module("group", "projectA", "1.2")
-        module.publish()
-        def jar = module.jarFile
-        jar.text = "1"
-        
-        buildFile << """
-            def repoDir = file('${ivyRepo.uri}')
-            repositories {
-                add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
-                    name = "repo"
-                    addIvyPattern(repoDir.absolutePath + "/[organization]/[module]/[revision]/ivy-[module]-[revision].xml")
-                    addArtifactPattern(repoDir.absolutePath + "/[organization]/[module]/[revision]/[module]-[revision].[ext]")
-                }
-            }
-            configurations { compile }
-            dependencies { compile 'group:projectA:1.2' }
-            task echoContent << {
-                def dep = configurations.compile.singleFile
-                println "content: " + dep.text
-                println "path: " + dep.canonicalPath
-            }
-        """
-
-        when:
-        executer.withDeprecationChecksDisabled()
-        run 'echoContent'
-
-        then:
-        scrapeValue("content") == "1"
-        scrapeValue("path") == jar.canonicalPath
-
-        when:
-        jar.text = "2"
-
-        executer.withDeprecationChecksDisabled()
-        run 'echoContent'
-
-        then:
-        scrapeValue("content") == "2"
-        scrapeValue("path") == jar.canonicalPath
-    }
-
-    protected scrapeValue(label) {
-        def fullLabel = "$label: "
-        for (line in output.readLines()) {
-            if (line.startsWith(fullLabel))  {
-                return line - fullLabel
-            }
-        }
-
-        null
-    }
-}
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
deleted file mode 100644
index 2aed311..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvySFtpResolverIntegrationTest.groovy
+++ /dev/null
@@ -1,84 +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.integtests.resolve.custom
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.gradle.test.fixtures.ivy.IvyRepository
-import org.gradle.test.fixtures.server.sftp.SFTPServer
-import org.junit.Rule
-
-class IvySFtpResolverIntegrationTest extends AbstractIntegrationSpec {
-
-    @Rule
-    public final SFTPServer server = new SFTPServer(this)
-
-    @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    def setup() {
-        requireOwnGradleUserHomeDir()
-    }
-
-    public void "can resolve and cache dependencies from an SFTP Ivy repository"() {
-        given:
-        def repo = ivyRepo()
-        def module = repo.module('group', 'projectA', '1.2')
-        module.publish();
-
-        and:
-        buildFile << """
-repositories {
-    add(new org.apache.ivy.plugins.resolver.SFTPResolver()) {
-        name = "sftprepo"
-        host = "${server.hostAddress}"
-        port = ${server.port}
-        user = "simple"
-        userPassword = "simple"
-        addIvyPattern "repos/libs/[organization]/[module]/[revision]/ivy-[revision].xml"
-        addArtifactPattern "repos/libs/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
-    }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-        when:
-        executer.withDeprecationChecksDisabled()
-        run 'listJars'
-
-        then:
-        server.fileRequests == ["repos/libs/group/projectA/1.2/ivy-1.2.xml",
-                "repos/libs/group/projectA/1.2/projectA-1.2.jar"
-        ] as Set
-
-        progressLogging.downloadProgressLogged("repos/libs/group/projectA/1.2/ivy-1.2.xml")
-        progressLogging.downloadProgressLogged("repos/libs/group/projectA/1.2/projectA-1.2.jar")
-
-        when:
-        server.clearRequests()
-        executer.withDeprecationChecksDisabled()
-        run 'listJars'
-
-        then:
-        server.fileRequests.empty
-    }
-
-    IvyRepository ivyRepo() {
-        return ivy(server.file("repos/libs/"))
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 11d5efb..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/custom/IvyUrlResolverIntegrationTest.groovy
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.custom
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-import org.junit.Rule
-
-class IvyUrlResolverIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    def setup() {
-        server.expectUserAgent(null) // custom resolver uses apache/ivy as user agent strings
-    }
-
-    public void "can resolve and cache dependencies from an HTTP Ivy repository"() {
-        server.start()
-        given:
-        final IvyHttpRepository repo = ivyHttpRepo
-        def projectA = repo.module('group', 'projectA', '1.2').publish()
-        repo.module('group', 'projectB').publish()
-        def projectB11 = repo.module('group', 'projectB', '1.1').publish()
-        def projectB20 = repo.module('group', 'projectB', '2.0').publish()
-
-        and:
-        buildFile << """
-repositories {
-    add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-        name = "repo"
-        addIvyPattern("${repo.ivyPattern}")
-        addArtifactPattern("${repo.artifactPattern}")
-    }
-}
-configurations {
-    simple
-    dynamic
-    dynamic2
-    dynamicMissing
-}
-dependencies {
-    simple 'group:projectA:1.2'
-    dynamic 'group:projectB:1.+'
-    dynamic2 'group:projectB:2.+'
-    dynamicMissing 'group:projectC:1.+'
-}
-task retrieveSimple(type: Sync) {
-  from configurations.simple
-  into 'simple'
-}
-task retrieveDynamic(type: Sync) {
-  from configurations.dynamic
-  into 'dynamic'
-}
-task retrieveDynamic2(type: Sync) {
-  from configurations.dynamic2
-  into 'dynamic2'
-}
-task retrieveDynamicMissing(type: Sync) {
-  from configurations.dynamicMissing
-  into 'dynamicMissing'
-}
-"""
-        when:
-        projectA.ivy.expectHead()
-        projectA.ivy.expectGet()
-        projectA.jar.expectHead()
-        projectA.jar.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        and:
-        succeeds 'retrieveSimple'
-
-        then:
-        file('simple').assertHasDescendants('projectA-1.2.jar')
-
-        and:
-        progressLogging.downloadProgressLogged(projectA.ivy.uri)
-        progressLogging.downloadProgressLogged(projectA.jar.uri)
-
-        when:
-        server.resetExpectations()
-
-        // No extra calls for cached dependencies
-        and:
-        executer.withDeprecationChecksDisabled()
-        succeeds 'retrieveSimple'
-
-        then:
-        file('simple').assertHasDescendants('projectA-1.2.jar')
-
-        when:
-        repo.expectDirectoryListGet('group', 'projectB')
-        projectB11.ivy.expectHead()
-        projectB11.ivy.expectGet()
-        projectB11.jar.expectHead()
-        projectB11.jar.expectGet()
-
-        projectB11.ivy.expectGet()
-
-        and:
-        executer.withDeprecationChecksDisabled()
-        succeeds 'retrieveDynamic'
-
-        then:
-        file('dynamic').assertHasDescendants('projectB-1.1.jar')
-
-        and:
-        progressLogging.downloadProgressLogged(projectB11.ivy.uri)
-        progressLogging.downloadProgressLogged(projectB11.jar.uri)
-
-        when:
-        server.resetExpectations()
-
-        // No extra calls for cached dependencies
-        and:
-        executer.withDeprecationChecksDisabled()
-        succeeds 'retrieveDynamic'
-
-        then:
-        file('dynamic').assertHasDescendants('projectB-1.1.jar')
-
-        when:
-        repo.expectDirectoryListGet('group', 'projectB')
-        projectB20.ivy.expectHead()
-        projectB20.ivy.expectGet()
-        projectB20.jar.expectHead()
-        projectB20.jar.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        projectB20.ivy.expectGet()
-
-        and:
-        succeeds 'retrieveDynamic2'
-
-        then:
-        file('dynamic2').assertHasDescendants('projectB-2.0.jar')
-    }
-
-    public void "reports module missing when directory listing for module is empty or broken"() {
-        given:
-        server.start()
-        def repo = ivyHttpRepo
-
-        and:
-        buildFile << """
-repositories {
-    add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-        name = "repo"
-        addIvyPattern("${repo.ivyPattern}")
-        addArtifactPattern("${repo.artifactPattern}")
-    }
-}
-configurations { compile }
-dependencies { compile 'org.gradle:projectA:1.+' }
-task testResolve(type: Sync) {
-  println configurations.compile.files
-}
-"""
-        when:
-        server.resetExpectations()
-        2.times {repo.expectDirectoryListGetBroken("org.gradle", "projectA") }
-        executer.withDeprecationChecksDisabled()
-
-        then:
-        fails 'testResolve'
-
-        and:
-        failure.assertHasCause "Could not find any version that matches org.gradle:projectA:1.+"
-
-        when:
-        2.times { repo.expectDirectoryListGetMissing("org.gradle", "projectA") }
-        executer.withDeprecationChecksDisabled()
-
-        then:
-        fails 'testResolve'
-
-        and:
-        failure.assertHasCause "Could not find any version that matches org.gradle:projectA:1.+"
-    }
-
-    public void "honours changing patterns from custom resolver"() {
-        server.start()
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2-SNAPSHOT').publish()
-
-        and:
-        buildFile << """
-repositories {
-    add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-        name = "repo"
-        addIvyPattern("${ivyHttpRepo.ivyPattern}")
-        addArtifactPattern("${ivyHttpRepo.artifactPattern}")
-        changingMatcher = 'regexp'
-        changingPattern = '.*SNAPSHOT.*'
-    }
-}
-configurations { compile }
-configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-dependencies { compile 'group:projectA:1.2-SNAPSHOT' }
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-        when:
-        module.ivy.expectHead()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        run 'retrieve'
-
-        then:
-        def jarFile = file('libs/projectA-1.2-SNAPSHOT.jar')
-        jarFile.assertIsCopyOf(module.jarFile)
-        def snapshot = jarFile.snapshot()
-
-        when:
-        module.publishWithChangedContent()
-
-        server.resetExpectations()
-        // Server will be hit to get updated versions
-        module.ivy.expectHead()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        run 'retrieve'
-
-        then:
-        def changedJarFile = file('libs/projectA-1.2-SNAPSHOT.jar')
-        changedJarFile.assertHasChangedSince(snapshot)
-        changedJarFile.assertIsCopyOf(module.jarFile)
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy
deleted file mode 100644
index d64a2e1..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,129 +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.integtests.resolve.http
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-
-import static org.gradle.util.Matchers.containsText
-
-abstract class AbstractHttpsRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-    File clientStore // contains the client's public and private keys
-    File serverStore // contains the server's public and private keys
-
-    abstract protected String setupRepo()
-
-    def "resolve with server certificate"() {
-        setupCertStores()
-        server.enableSsl(serverStore.path, "asdfgh")
-        server.start()
-
-        def repoType = setupRepo()
-        setupBuildFile(repoType)
-
-        when:
-        executer.withArgument("-Djavax.net.ssl.trustStore=$serverStore.path")
-                .withArgument("-Djavax.net.ssl.trustStorePassword=asdfgh")
-                .withTasks('libs').run()
-
-        then:
-        file('libs').assertHasDescendants('my-module-1.0.jar')
-    }
-
-    def "resolve with server and client certificate"() {
-        setupCertStores()
-        server.enableSsl(serverStore.path, "asdfgh", clientStore.path, "asdfgh")
-        server.start()
-
-        def repoType = setupRepo()
-        setupBuildFile(repoType)
-
-        when:
-        executer.withArgument("-Djavax.net.ssl.trustStore=$serverStore.path")
-                .withArgument("-Djavax.net.ssl.trustStorePassword=asdfgh")
-                .withArgument("-Djavax.net.ssl.keyStore=$clientStore.path")
-                .withArgument("-Djavax.net.ssl.keyStorePassword=asdfgh")
-                .withTasks('libs').run()
-
-        then:
-        file('libs').assertHasDescendants('my-module-1.0.jar')
-    }
-
-    def "decent error message when client can't authenticate server"() {
-        setupCertStores()
-        server.enableSsl(serverStore.path, "asdfgh")
-        server.start()
-
-        def repoType = setupRepo()
-        setupBuildFile(repoType)
-
-        when:
-        def failure = executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
-                .withArgument("-Djavax.net.ssl.trustStore=$clientStore.path") // intentionally use wrong trust store for client
-                .withArgument("-Djavax.net.ssl.trustStorePassword=asdfgh")
-                .withTasks('libs').runWithFailure()
-
-        then:
-        failure.assertThatCause(containsText("Could not GET 'https://localhost:(\\d*)/repo1/my-group/my-module/1.0/"))
-        failure.assertHasCause("peer not authenticated")
-    }
-
-    def "decent error message when server can't authenticate client"() {
-        setupCertStores()
-        server.enableSsl(serverStore.path, "asdfgh", serverStore.path, "asdfgh") // intentionally use wrong trust store for server
-        server.start()
-
-        def repoType = setupRepo()
-        setupBuildFile(repoType)
-
-        when:
-        def failure = executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
-                .withArgument("-Djavax.net.ssl.trustStore=$serverStore.path")
-                .withArgument("-Djavax.net.ssl.trustStorePassword=asdfgh")
-                .withArgument("-Djavax.net.ssl.keyStore=$clientStore.path")
-                .withArgument("-Djavax.net.ssl.keyStorePassword=asdfgh")
-                .withTasks('libs').runWithFailure()
-
-        then:
-        failure.assertThatCause(containsText("Could not GET 'https://localhost:(\\d*)/repo1/my-group/my-module/1.0/"))
-        failure.assertHasCause("peer not authenticated")
-    }
-
-    def setupCertStores() {
-        clientStore = resources.dir.file("clientStore")
-        serverStore = resources.dir.file("serverStore")
-    }
-
-    private void setupBuildFile(String repoType) {
-        buildFile << """
-repositories {
-    $repoType { url 'https://localhost:${server.sslPort}/repo1' }
-}
-configurations { compile }
-dependencies {
-    compile 'my-group:my-module:1.0'
-}
-task libs(type: Copy) {
-    into 'libs'
-    from configurations.compile
-}
-        """
-    }
-}
-
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy
deleted file mode 100644
index cce1527..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,221 +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.integtests.resolve.http
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.hamcrest.Matchers
-import spock.lang.Unroll
-
-class HttpAuthenticationDependencyResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    static String badCredentials = "credentials{username 'testuser'; password 'bad'}"
-
-    @Unroll
-    public void "can resolve dependencies from #authScheme authenticated HTTP ivy repository"() {
-        server.start()
-        given:
-        def moduleA = ivyRepo().module('group', 'projectA', '1.2').publish()
-        ivyRepo().module('group', 'projectB', '2.1').publish()
-        ivyRepo().module('group', 'projectB', '2.2').publish()
-        def moduleB = ivyRepo().module('group', 'projectB', '2.3').publish()
-        ivyRepo().module('group', 'projectB', '3.0').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy {
-        url "http://localhost:${server.port}/repo"
-
-        credentials {
-            password 'password'
-            username 'username'
-        }
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.2'
-    compile 'group:projectB:2.+'
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar','projectB-2.3.jar']
-}
-"""
-
-        when:
-        server.authenticationScheme = authScheme
-
-        and:
-        server.expectGet('/repo/group/projectA/1.2/ivy-1.2.xml', 'username', 'password', moduleA.ivyFile)
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', 'username', 'password', moduleA.jarFile)
-        server.expectGetDirectoryListing("/repo/group/projectB/", 'username', 'password', moduleB.moduleDir.parentFile)
-        server.expectGet("/repo/group/projectB/2.3/ivy-2.3.xml", 'username', 'password', moduleB.ivyFile)
-        server.expectGet("/repo/group/projectB/2.3/projectB-2.3.jar", 'username', 'password', moduleB.jarFile)
-
-        then:
-        succeeds('listJars')
-
-        where:
-        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
-    }
-
-    @Unroll
-    public void "can resolve dependencies from #authScheme authenticated HTTP maven repository"() {
-        server.start()
-        given:
-        def moduleA = mavenRepo().module('group', 'projectA', '1.2').publish()
-        mavenRepo().module('group', 'projectB', '2.0').publish()
-        mavenRepo().module('group', 'projectB', '2.2').publish()
-        def moduleB = mavenRepo().module('group', 'projectB', '2.3').publish()
-        mavenRepo().module('group', 'projectB', '3.0').publish()
-        def moduleC = mavenRepo().module('group', 'projectC', '3.1-SNAPSHOT').publish()
-        def moduleD = mavenRepo().module('group', 'projectD', '4-SNAPSHOT').withNonUniqueSnapshots().publish()
-        and:
-        buildFile << """
-repositories {
-    maven {
-        url "http://localhost:${server.port}/repo"
-
-        credentials {
-            password 'password'
-            username 'username'
-        }
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.2'
-    compile 'group:projectB:2.+'
-    compile 'group:projectC:3.1-SNAPSHOT'
-    compile 'group:projectD:4-SNAPSHOT'
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-2.3.jar', 'projectC-3.1-SNAPSHOT.jar', 'projectD-4-SNAPSHOT.jar']
-}
-"""
-
-        when:
-        server.authenticationScheme = authScheme
-
-        and:
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.pom', 'username', 'password', moduleA.pomFile)
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', 'username', 'password', moduleA.artifactFile)
-        server.expectGet('/repo/group/projectB/maven-metadata.xml', 'username', 'password', moduleB.rootMetaDataFile)
-        server.expectGet('/repo/group/projectB/2.3/projectB-2.3.pom', 'username', 'password', moduleB.pomFile)
-        server.expectGet('/repo/group/projectB/2.3/projectB-2.3.jar', 'username', 'password', moduleB.artifactFile)
-
-        server.expectGet('/repo/group/projectC/3.1-SNAPSHOT/maven-metadata.xml', 'username', 'password', moduleC.metaDataFile)
-        server.expectGet("/repo/group/projectC/3.1-SNAPSHOT/projectC-${moduleC.getPublishArtifactVersion()}.pom", 'username', 'password', moduleC.pomFile)
-        server.expectGet("/repo/group/projectC/3.1-SNAPSHOT/projectC-${moduleC.getPublishArtifactVersion()}.jar", 'username', 'password', moduleC.artifactFile)
-
-        server.expectGet('/repo/group/projectD/4-SNAPSHOT/maven-metadata.xml', 'username', 'password', moduleD.metaDataFile)
-        server.expectGet("/repo/group/projectD/4-SNAPSHOT/projectD-4-SNAPSHOT.pom", 'username', 'password', moduleD.pomFile)
-        server.expectGet("/repo/group/projectD/4-SNAPSHOT/projectD-4-SNAPSHOT.jar", 'username', 'password', moduleD.artifactFile)
-
-        then:
-        succeeds('listJars')
-
-        where:
-        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
-    }
-
-    @Unroll
-    def "reports failure resolving with #credsName credentials from #authScheme authenticated HTTP ivy repository"() {
-        server.start()
-        given:
-        def module = ivyRepo().module('group', 'projectA', '1.2').publish()
-        when:
-        settingsFile << 'rootProject.name = "publish"'
-        buildFile << """
-   repositories {
-       ivy {
-            url "http://localhost:${server.port}/repo"
-            $creds
-        }
-   }
-   configurations { compile }
-   dependencies {
-       compile 'group:projectA:1.2'
-   }
-   task listJars << {
-       assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-   }
-   """
-
-        and:
-        server.authenticationScheme = authScheme
-        server.allowGetOrHead('/repo/group/projectA/1.2/ivy-1.2.xml', 'username', 'password', module.ivyFile)
-
-        then:
-        fails 'listJars'
-
-        and:
-        failure
-            .assertHasDescription('Execution failed for task \':listJars\'.')
-            .assertResolutionFailure(':compile')
-            .assertThatCause(Matchers.containsString('Received status code 401 from server: Unauthorized'))
-
-        where:
-        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST, HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
-        credsName << ['empty', 'empty', 'bad', 'bad']
-        creds << ['', '', badCredentials, badCredentials]
-    }
-
-    @Unroll
-    def "reports failure resolving with #credsName credentials from #authScheme authenticated HTTP maven repository"() {
-        given:
-        server.start()
-
-        and:
-        def module = mavenRepo().module('group', 'projectA', '1.2').publish()
-
-        when:
-        settingsFile << 'rootProject.name = "publish"'
-        buildFile << """
- repositories {
-     maven {
-         url "http://localhost:${server.port}/repo"
-        $creds
-     }
- }
- configurations { compile }
- dependencies {
-     compile 'group:projectA:1.2'
- }
- task listJars << {
-     assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
- }
- """
-
-        and:
-        server.authenticationScheme = authScheme
-        server.allowGetOrHead('/repo/group/projectA/1.2/projectA-1.2.pom', 'username', 'password', module.pomFile)
-
-        then:
-        fails 'listJars'
-
-        and:
-        failure
-            .assertHasDescription('Execution failed for task \':listJars\'.')
-            .assertResolutionFailure(':compile')
-            .assertThatCause(Matchers.containsString('Received status code 401 from server: Unauthorized'))
-
-        where:
-        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST, HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
-        credsName << ['empty', 'empty', 'bad', 'bad']
-        creds << ['', '', badCredentials, badCredentials]
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.groovy
deleted file mode 100644
index 2d31405..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.http
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest;
-
-
-public class HttpEncodingDependencyResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    public void "handles gzip encoded content"() {
-        server.start()
-
-        given:
-        def repo = ivyRepo()
-        def module = repo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "http://localhost:${server.port}/repo" }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        server.expectGetGZipped('/repo/group/projectA/1.2/ivy-1.2.xml', module.ivyFile)
-        server.expectGetGZipped('/repo/group/projectA/1.2/projectA-1.2.jar', module.jarFile)
-
-        then:
-        succeeds('listJars')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy
deleted file mode 100644
index 8fc1a64..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.http
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.gradle.test.fixtures.server.http.TestProxyServer
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
-import spock.lang.Unroll
-
-class HttpProxyResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Rule TestProxyServer proxyServer = new TestProxyServer(server)
-    @Rule SetSystemProperties systemProperties = new SetSystemProperties()
-
-    public void "uses configured proxy to access remote HTTP repository"() {
-        server.start()
-        proxyServer.start()
-
-        given:
-        def repo = ivyRepo()
-        def module = repo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "http://not.a.real.domain/repo" }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}")
-
-        and:
-        server.expectGet('/repo/group/projectA/1.2/ivy-1.2.xml', module.ivyFile)
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', module.jarFile)
-
-        then:
-        succeeds('listJars')
-
-        and:
-        proxyServer.requestCount == 2
-    }
-
-    public void "uses authenticated proxy to access remote HTTP repository"() {
-        server.start()
-        proxyServer.start()
-
-        given:
-        def repo = ivyRepo()
-        def module = repo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy {
-        url "http://not.a.real.domain/repo"
-    }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}", "-Dhttp.nonProxyHosts=foo",
-                               "-Dhttp.proxyUser=proxyUser", "-Dhttp.proxyPassword=proxyPassword")
-
-        and:
-        proxyServer.requireAuthentication('proxyUser', 'proxyPassword')
-
-        and:
-        server.expectGet('/repo/group/projectA/1.2/ivy-1.2.xml', module.ivyFile)
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', module.jarFile)
-
-        then:
-        succeeds('listJars')
-
-        and:
-        proxyServer.requestCount == 2
-    }
-
-    @Unroll
-    public void "passes target credentials to #authScheme authenticated server via proxy"() {
-        server.start()
-        proxyServer.start()
-
-        given:
-        def repo = ivyRepo()
-        def module = repo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy {
-        url "http://not.a.real.domain/repo"
-        credentials {
-            username 'targetUser'
-            password 'targetPassword'
-        }
-    }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        server.authenticationScheme = authScheme
-        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}", "-Dhttp.proxyUser=proxyUser", "-Dhttp.proxyPassword=proxyPassword")
-
-        and:
-        proxyServer.requireAuthentication('proxyUser', 'proxyPassword')
-
-        and:
-        server.expectGet('/repo/group/projectA/1.2/ivy-1.2.xml', 'targetUser', 'targetPassword', module.ivyFile)
-        server.expectGet('/repo/group/projectA/1.2/projectA-1.2.jar', 'targetUser', 'targetPassword', module.jarFile)
-
-        then:
-        succeeds('listJars')
-
-        and:
-        // 1 extra request for authentication
-        proxyServer.requestCount == 3
-
-        where:
-        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy
deleted file mode 100644
index 0e94c08..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,87 +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.integtests.resolve.http
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
-import spock.lang.Issue
-
-class HttpRedirectResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Rule SetSystemProperties systemProperties = new SetSystemProperties()
-    @Rule public final HttpServer server2 = new HttpServer()
-
-    public void "resolves module artifacts via HTTP redirect"() {
-        server.start()
-        server2.start()
-
-        given:
-        def module = ivyRepo().module('group', 'projectA').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "http://localhost:${server.port}/repo" }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.0' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar']
-}
-"""
-
-        when:
-        server.expectGetRedirected('/repo/group/projectA/1.0/ivy-1.0.xml', "http://localhost:${server2.port}/redirected/group/projectA/1.0/ivy-1.0.xml")
-        server2.expectGet('/redirected/group/projectA/1.0/ivy-1.0.xml', module.ivyFile)
-        server.expectGetRedirected('/repo/group/projectA/1.0/projectA-1.0.jar', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.jar")
-        server2.expectGet('/redirected/group/projectA/1.0/projectA-1.0.jar', module.jarFile)
-
-        then:
-        succeeds('listJars')
-    }
-
-    @Issue('GRADLE-2196')
-    public void "resolves artifact-only module via HTTP redirect"() {
-        server.start()
-        server2.start()
-
-        given:
-        def module = ivyRepo().module('group', 'projectA').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "http://localhost:${server.port}/repo" }
-}
-configurations { compile }
-dependencies { compile 'group:projectA:1.0 at zip' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.0.zip']
-}
-"""
-
-        when:
-        server.expectGetMissing('/repo/group/projectA/1.0/ivy-1.0.xml')
-        server.expectHeadRedirected('/repo/group/projectA/1.0/projectA-1.0.zip', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.zip")
-        server2.expectHead('/redirected/group/projectA/1.0/projectA-1.0.zip', module.jarFile)
-        server.expectGetRedirected('/repo/group/projectA/1.0/projectA-1.0.zip', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.zip")
-        server2.expectGet('/redirected/group/projectA/1.0/projectA-1.0.zip', module.jarFile)
-
-        then:
-        succeeds('listJars')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy
deleted file mode 100644
index ba878f3..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy
+++ /dev/null
@@ -1,119 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class IvyBrokenDescriptorIntegrationTest extends AbstractDependencyResolutionTest {
-    def "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.ivy.expectGet()
-
-        then:
-        fails "showBroken"
-        failure
-            .assertResolutionFailure(":compile")
-            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
-            .assertHasCause("invalid version null")
-    }
-
-    def "reports missing parent descriptor"() {
-        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 parent = ivyHttpRepo.module('group', 'parent', 'a')
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').extendsFrom(organisation: 'group', module: 'parent', revision: 'a').publish()
-
-        when:
-        module.ivy.expectGet()
-        parent.ivy.expectGetMissing()
-        parent.jar.expectHeadMissing()
-
-        then:
-        fails "showBroken"
-        failure
-            .assertResolutionFailure(":compile")
-            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
-            .assertHasCause("Could not find any version that matches group:parent:a.")
-    }
-
-    def "reports parent 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 parent = ivyHttpRepo.module('group', 'parent', 'a').publish()
-        parent.ivyFile.text = '<ivy-module/>'
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').extendsFrom(organisation: 'group', module: 'parent', revision: 'a').publish()
-
-        when:
-        module.ivy.expectGet()
-        parent.ivy.expectGet()
-
-        then:
-        fails "showBroken"
-        failure
-            .assertResolutionFailure(":compile")
-            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
-            .assertHasCause("Could not parse Ivy file ${parent.ivy.uri}")
-            .assertHasCause("invalid version null")
-    }
-}
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
deleted file mode 100644
index bfb3295..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-import static org.hamcrest.Matchers.containsString
-
-class IvyBrokenRemoteResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    public void "reports and recovers from missing module"() {
-        server.start()
-
-        given:
-        def repo = ivyHttpRepo("repo1")
-        def module = repo.module("group", "projectA", "1.2").publish()
-
-        buildFile << """
-repositories {
-    ivy { url "${repo.uri}"}
-}
-configurations { missing }
-dependencies {
-    missing 'group:projectA:1.2'
-}
-task showMissing << { println configurations.missing.files }
-"""
-
-        when:
-        module.ivy.expectGetMissing()
-        module.jar.expectHeadMissing()
-
-        then:
-        fails("showMissing")
-        failure
-            .assertHasDescription('Execution failed for task \':showMissing\'.')
-            .assertHasCause('Could not resolve all dependencies for configuration \':missing\'.')
-            .assertHasCause('Could not find group:projectA:1.2.')
-
-        when:
-        server.resetExpectations()
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        then:
-        succeeds('showMissing')
-    }
-
-    public void "reports and recovers from failed Ivy descriptor download"() {
-        server.start()
-
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.3').publish()
-
-        buildFile << """
-repositories {
-    ivy {
-        url "${ivyHttpRepo.uri}"
-    }
-}
-configurations { broken }
-dependencies {
-    broken 'group:projectA:1.3'
-}
-task showBroken << { println configurations.broken.files }
-"""
-
-        when:
-        module.ivy.expectGetBroken()
-        fails("showBroken")
-
-        then:
-        failure
-            .assertHasDescription('Execution failed for task \':showBroken\'.')
-            .assertResolutionFailure(':broken')
-            .assertHasCause('Could not resolve group:projectA:1.3.')
-            .assertHasCause("Could not GET '${module.ivy.uri}'. Received status code 500 from server: broken")
-
-        when:
-        server.resetExpectations()
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        then:
-        succeeds("showBroken")
-    }
-
-    public void "reports and caches missing artifacts"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy {
-        url "${ivyHttpRepo.uri}"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.2'
-}
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        and:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGetMissing()
-
-        then:
-        fails "retrieve"
-
-        failure.assertThatCause(containsString("Artifact 'group:projectA:1.2:projectA.jar' not found"))
-
-        when:
-        server.resetExpectations()
-
-        then:
-        fails "retrieve"
-        failure.assertThatCause(containsString("Artifact 'group:projectA:1.2:projectA.jar' not found"))
-    }
-
-    public void "reports and recovers from failed artifact download"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy {
-        url "${ivyHttpRepo.uri}"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.2'
-}
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        and:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGetBroken()
-
-        then:
-        fails "retrieve"
-        failure.assertHasCause("Could not download artifact 'group:projectA:1.2:projectA.jar'")
-        failure.assertHasCause("Could not GET '${module.jar.uri}'. Received status code 500 from server: broken")
-
-        when:
-        server.resetExpectations()
-        module.jar.expectGet()
-
-        then:
-        succeeds "retrieve"
-        file('libs').assertHasDescendants('projectA-1.2.jar')
-    }
-}
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
deleted file mode 100644
index e8dbea5..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,418 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class IvyChangingModuleRemoteResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def "detects changed module descriptor when flagged as changing"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-configurations.all {
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1.1", changing: true
-}
-
-task retrieve(type: Copy) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        when: "Version 1.1 is published"
-        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        and: "Server handles requests"
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        and: "We request 1.1 (changing)"
-        run 'retrieve'
-
-        then: "Version 1.1 jar is downloaded"
-        file('build').assertHasDescendants('projectA-1.1.jar')
-
-        when: "Module meta-data is changed (new artifact)"
-        module.artifact([name: 'other'])
-        module.dependsOn("group", "projectB", "2.0")
-        module.publish()
-        def moduleB = ivyHttpRepo.module("group", "projectB", "2.0").publish()
-
-        and: "Server handles requests"
-        server.resetExpectations()
-        // Server will be hit to get updated versions
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.getArtifact(name: 'other').expectGet()
-        moduleB.ivy.expectGet()
-        moduleB.jar.expectGet()
-
-        and: "We request 1.1 again"
-        run 'retrieve'
-
-        then: "We get all artifacts, including the new ones"
-        file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar', 'projectB-2.0.jar')
-    }
-
-    def "can mark a module as changing after first retrieval"() {
-        server.start()
-
-        given:
-        buildFile << """
-def isChanging = project.hasProperty('isChanging') ? true : false
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1.1", changing: isChanging
-}
-
-task retrieve(type: Copy) {
-    into 'build'
-    from configurations.compile
-}
-"""
-        and:
-        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-        module.allowAll()
-
-        when: 'original retrieve'
-        run 'retrieve'
-
-        then:
-        def jarSnapshot = file('build/projectA-1.1.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        module.publishWithChangedContent()
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-
-        and:
-        executer.withArguments('-PisChanging')
-        run 'retrieve'
-
-        then:
-        file('build/projectA-1.1.jar').assertHasChangedSince(jarSnapshot)
-    }
-
-    def "detects changed artifact when flagged as changing"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-configurations.all {
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1.1", changing: true
-}
-
-task retrieve(type: Copy) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        and:
-        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        run 'retrieve'
-
-        then:
-        def jarFile = file('build/projectA-1.1.jar')
-        jarFile.assertIsCopyOf(module.jarFile)
-        def snapshot = jarFile.snapshot()
-
-        when:
-        module.publishWithChangedContent()
-
-        server.resetExpectations()
-        // Server will be hit to get updated versions
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-
-        run 'retrieve'
-
-        then:
-        def changedJarFile = file('build/projectA-1.1.jar')
-        changedJarFile.assertHasChangedSince(snapshot)
-        changedJarFile.assertIsCopyOf(module.jarFile)
-    }
-
-    def "caches changing module descriptor and artifacts until cache expiry"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-
-if (project.hasProperty('doNotCacheChangingModules')) {
-    configurations.all {
-        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-    }
-}
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1.1", changing: true
-}
-
-task retrieve(type: Copy) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        when: "Version 1.1 is published"
-        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        and: "Server handles requests"
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        and: "We request 1.1 (changing)"
-        run 'retrieve'
-
-        then: "Version 1.1 jar is downloaded"
-        file('build').assertHasDescendants('projectA-1.1.jar')
-        def jarFile = file('build/projectA-1.1.jar')
-        jarFile.assertIsCopyOf(module.jarFile)
-        def snapshot = jarFile.snapshot()
-
-        when: "Module meta-data is changed and artifacts are modified"
-        server.resetExpectations()
-        module.artifact([name: 'other'])
-        module.publishWithChangedContent()
-
-        and: "We request 1.1 (changing), with module meta-data cached. No server requests."
-        run 'retrieve'
-
-        then: "Original module meta-data and artifacts are used"
-        file('build').assertHasDescendants('projectA-1.1.jar')
-        jarFile.assertHasNotChangedSince(snapshot)
-
-        when: "Server handles requests"
-        server.resetExpectations()
-        // Server will be hit to get updated versions
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-        module.getArtifact(name: 'other').expectGet()
-
-        and: "We request 1.1 (changing) again, with zero expiry for dynamic revision cache"
-        executer.withArguments("-PdoNotCacheChangingModules")
-        run 'retrieve'
-
-        then: "We get new artifacts based on the new meta-data"
-        file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar')
-        jarFile.assertHasChangedSince(snapshot)
-        jarFile.assertIsCopyOf(module.jarFile)
-    }
-
-    def "can use cache-control DSL to mimic changing pattern for ivy repository"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-import static java.util.concurrent.TimeUnit.SECONDS
-configurations.all {
-    resolutionStrategy.resolutionRules.with {
-        eachModule({ moduleResolve ->
-            if (moduleResolve.request.version.endsWith('-CHANGING')) {
-                moduleResolve.cacheFor(0, SECONDS)
-            }
-        } as Action)
-
-        eachArtifact({ artifactResolve ->
-            if (artifactResolve.request.moduleVersionIdentifier.version.endsWith('-CHANGING')) {
-                artifactResolve.cacheFor(0, SECONDS)
-            }
-        } as Action)
-    }
-}
-
-dependencies {
-    compile group: "group", name: "projectA", version: "1-CHANGING"
-}
-
-task retrieve(type: Copy) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        when: "Version 1-CHANGING is published"
-        def module = ivyHttpRepo.module("group", "projectA", "1-CHANGING").publish()
-
-        and: "Server handles requests"
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        and: "We request 1-CHANGING"
-        run 'retrieve'
-
-        then: "Version 1-CHANGING jar is used"
-        file('build').assertHasDescendants('projectA-1-CHANGING.jar')
-        def jarFile = file('build/projectA-1-CHANGING.jar')
-        jarFile.assertIsCopyOf(module.jarFile)
-        def snapshot = jarFile.snapshot()
-
-        when: "Module meta-data is changed and artifacts are modified"
-        module.artifact([name: 'other'])
-        module.publishWithChangedContent()
-
-        and: "Server handles requests"
-        server.resetExpectations()
-        // Server will be hit to get updated versions
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.sha1.expectGet()
-        module.jar.expectGet()
-        module.getArtifact(name: 'other').expectGet()
-
-        and: "We request 1-CHANGING again"
-        executer.withArguments()
-        run 'retrieve'
-
-        then: "We get new artifacts based on the new meta-data"
-        file('build').assertHasDescendants('projectA-1-CHANGING.jar', 'other-1-CHANGING.jar')
-        jarFile.assertHasChangedSince(snapshot)
-        jarFile.assertIsCopyOf(module.jarFile)
-    }
-
-    def "avoid redownload unchanged artifact when no checksum available"() {
-        server.start()
-
-        given:
-        buildFile << """
-            repositories {
-                ivy { url "${ivyHttpRepo.uri}" }
-            }
-
-            configurations { compile }
-
-            configurations.all {
-                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-            }
-
-            dependencies {
-                compile group: "group", name: "projectA", version: "1.1", changing: true
-            }
-
-            task retrieve(type: Copy) {
-                into 'build'
-                from configurations.compile
-            }
-        """
-
-        and:
-        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        def downloadedJar = file('build/projectA-1.1.jar')
-        downloadedJar.assertIsCopyOf(module.jarFile)
-        def snapshot = downloadedJar.snapshot()
-
-        when:
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.jar.expectHead()
-
-        and:
-        run 'retrieve'
-
-        then:
-        downloadedJar.assertHasNotChangedSince(snapshot)
-
-        when:
-        // Do change the jar, so we can check that the new version wasn't downloaded
-        module.publishWithChangedContent()
-
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGetMissing()
-        module.ivy.expectGet()
-        module.jar.expectHead()
-        module.jar.sha1.expectGetMissing()
-        module.jar.expectGet()
-
-        run 'retrieve'
-
-        then:
-        downloadedJar.assertHasChangedSince(snapshot)
-        downloadedJar.assertIsCopyOf(module.jarFile)
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy
deleted file mode 100644
index 0e32ff4..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesChangingModulesIntegrationTest
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-
-class IvyComponentMetadataRulesChangingModulesIntegrationTest extends ComponentMetadataRulesChangingModulesIntegrationTest {
-    IvyHttpRepository getRepo() {
-        ivyHttpRepo
-    }
-
-    String getRepoDeclaration() {
-"""
-repositories {
-    ivy {
-        url "$repo.uri"
-    }
-}
-"""
-    }
-
-    def setup() {
-        repo.allowDirectoryListGet("org.test", "moduleA")
-    }
-}
-
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
deleted file mode 100644
index 45ed7e2..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.resolve.ComponentMetadataRulesIntegrationTest
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-
-class IvyComponentMetadataRulesIntegrationTest extends ComponentMetadataRulesIntegrationTest {
-    @Override
-    IvyHttpRepository getRepo() {
-        ivyHttpRepo
-    }
-
-    @Override
-    String getRepoDeclaration() {
-"""
-repositories {
-    ivy {
-        url "$ivyHttpRepo.uri"
-    }
-}
-"""
-    }
-
-    @Override
-    String getDefaultStatus() {
-        "integration"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy
deleted file mode 100644
index e4944a1..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesStatusIntegrationTest
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-
-class IvyComponentMetadataRulesStatusIntegrationTest extends ComponentMetadataRulesStatusIntegrationTest {
-    @Override
-    IvyHttpRepository getRepo() {
-        ivyHttpRepo
-    }
-
-    @Override
-    String getRepoDeclaration() {
-"""
-repositories {
-    ivy {
-        url "$ivyHttpRepo.uri"
-    }
-}
-"""
-    }
-
-    def setup() {
-        repo.module('org.test', 'projectA', '1.0').withStatus("silver").publish().allowAll()
-    }
-
-    def "module with custom status can be resolved by adapting status scheme"() {
-        buildFile <<
-                """
-dependencies {
-    components {
-        eachComponent { details ->
-            assert details.status == "silver"
-            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"() {
-        expect:
-        fails 'resolve'
-        failure.assertHasCause(/Unexpected status 'silver' specified for org.test:projectA:1.0. Expected one of: [integration, milestone, release]/)
-    }
-
-    def "resolve fails if status doesn't match custom status scheme"() {
-        buildFile <<
-                """
-dependencies {
-    components {
-        eachComponent { details ->
-            details.statusScheme = ["gold", "bronze"]
-        }
-    }
-}
-"""
-
-        expect:
-        fails 'resolve'
-        failure.assertHasCause(/Unexpected status 'silver' specified for org.test:projectA:1.0. Expected one of: [gold, bronze]/)
-    }
-
-    def "rule can change status"() {
-        buildFile <<
-                """
-dependencies {
-    components {
-        eachComponent { details ->
-            details.status = "milestone"
-        }
-    }
-}
-"""
-
-        expect:
-        succeeds 'resolve'
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index c598206..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy
+++ /dev/null
@@ -1,107 +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.integtests.resolve.ivy
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class IvyCustomStatusLatestVersionIntegrationTest extends AbstractDependencyResolutionTest {
-    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"
-    }
-
-    def "uses status provided by component metadata rule for latest.xyz"() {
-        server.start()
-        given:
-        buildFile << """
-repositories {
-    ivy {
-        url "${ivyHttpRepo.uri}"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'org.test:projectA:latest.release'
-    components {
-        eachComponent { details ->
-            if (details.id.version == project.properties['releaseVersion']) {
-                details.status = 'release'
-            }
-        }
-    }
-}
-
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        and:
-        ivyHttpRepo.allowDirectoryListGet('org.test', 'projectA')
-        ivyHttpRepo.module('org.test', 'projectA', '1.0').withStatus("release").publish().allowAll()
-        ivyHttpRepo.module('org.test', 'projectA', '1.1').withStatus("integration").publish().allowAll()
-
-        when:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants("projectA-1.0.jar")
-
-        when:
-        executer.withArgument("-PreleaseVersion=1.1")
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants("projectA-1.1.jar")
-    }
-}
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
deleted file mode 100644
index aea2efb..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,240 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Unroll
-
-class IvyDescriptorResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def "substitutes system properties into ivy descriptor"() {
-        given:
-        ivyRepo.module("org.gradle", "test", "1.45")
-                .dependsOn('org.gradle.${sys_prop}', 'module_${sys_prop}', 'v_${sys_prop}')
-                .publish()
-
-        ivyRepo.module("org.gradle.111", "module_111", "v_111").publish()
-
-        and:
-        buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45"
-}
-
-task check << {
-    configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each {
-        it.children.each { transitive ->
-            assert transitive.moduleGroup == "org.gradle.111"
-            assert transitive.moduleName == "module_111"
-            assert transitive.moduleVersion == "v_111"
-        }
-    }
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'module_111-v_111.jar']
-}
-"""
-
-        when:
-        executer.withArgument("-Dsys_prop=111")
-
-        then:
-        succeeds "check"
-    }
-
-    def "merges values from parent descriptor file that is available locally"() {
-        given:
-        server.start()
-        def parentModule = ivyHttpRepo.module("org.gradle.parent", "parent_module", "1.1").dependsOn("org.gradle.dep", "dep_module", "1.1").publish()
-        def depModule = ivyHttpRepo.module("org.gradle.dep", "dep_module", "1.1").publish()
-
-        def module = ivyHttpRepo.module("org.gradle", "test", "1.45")
-        module.extendsFrom(organisation: "org.gradle.parent", module: "parent_module", revision: "1.1", location: parentModule.ivyFile.toURI().toURL())
-        parentModule.publish()
-        module.publish()
-
-        when:
-        buildFile << """
-repositories { ivy { url "${ivyHttpRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'dep_module-1.1.jar']
-}
-"""
-
-        and:
-        module.ivy.expectGet()
-        depModule.ivy.expectGet()
-        module.jar.expectGet()
-        depModule.jar.expectGet()
-
-        then:
-        succeeds "check"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        succeeds "check"
-    }
-
-    def "merges values from parent descriptor file"() {
-        given:
-        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 = ivyHttpRepo.module("org.gradle", "test", "1.45")
-        final extendAttributes = [organisation: "org.gradle.parent", module: "parent_module", revision: "1.1"]
-        module.extendsFrom(extendAttributes)
-        parentModule.publish()
-        module.publish()
-
-        when:
-        buildFile << """
-repositories { ivy { url "${ivyHttpRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'dep_module-1.1.jar']
-}
-"""
-
-        and:
-        module.ivy.expectGet()
-        parentModule.ivy.expectGet()
-        depModule.ivy.expectGet()
-        module.jar.expectGet()
-        depModule.jar.expectGet()
-
-        then:
-        succeeds "check"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        succeeds "check"
-    }
-
-    @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/IvyDescriptorValidationIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorValidationIntegrationTest.groovy
deleted file mode 100644
index 33ace34..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorValidationIntegrationTest.groovy
+++ /dev/null
@@ -1,54 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-import static org.hamcrest.CoreMatchers.containsString
-
-class IvyDescriptorValidationIntegrationTest extends AbstractDependencyResolutionTest {
-    def "incorrect value for revision attribute is flagged"() {
-        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/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
deleted file mode 100644
index e433c51..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,835 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.ivy
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.resolve.ResolveTestFixture
-import org.gradle.test.fixtures.Repository
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import spock.lang.Unroll
-
-class IvyDynamicRevisionRemoteResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    ResolveTestFixture resolve
-
-    def setup() {
-        settingsFile << "rootProject.name = 'test' "
-
-        resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        server.start()
-    }
-
-    def "uses latest version from version range and latest status"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-if (project.hasProperty('refreshDynamicVersions')) {
-    configurations.all {
-        resolutionStrategy.cacheDynamicVersionsFor 0, "seconds"
-    }
-}
-dependencies {
-    compile group: "group", name: "projectA", version: "1.+"
-    compile group: "group", name: "projectB", version: "latest.integration"
-}
-"""
-        when:
-        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-        ivyHttpRepo.module("group", "projectA", "2.0").publish()
-        def projectB1 = ivyHttpRepo.module("group", "projectB", "1.1").publish()
-
-        and:
-        expectGetDynamicRevision(projectA1)
-        expectGetDynamicRevision(projectB1)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.1",
-                     "group:projectB:latest.integration": "group:projectB:1.1"
-
-        when:
-        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
-        def projectB2 = ivyHttpRepo.module("group", "projectB", "2.2").publish()
-
-        and:
-        server.resetExpectations()
-        expectGetDynamicRevision(projectA2)
-        expectGetDynamicRevision(projectB2)
-
-        then:
-        executer.withArgument("-PrefreshDynamicVersions")
-        checkResolve "group:projectA:1.+": "group:projectA:1.2", "group:projectB:latest.integration": "group:projectB:2.2"
-    }
-
-    def "determines latest version with jar only"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-  compile group: "group", name: "projectA", version: "1.+"
-}
-"""
-
-        when:
-        ivyHttpRepo.module("group", "projectA", "1.1").withNoMetaData().publish()
-        def projectA12 = ivyHttpRepo.module("group", "projectA", "1.2").withNoMetaData().publish()
-        ivyHttpRepo.module("group", "projectA", "2.0").withNoMetaData().publish()
-
-        and:
-        ivyHttpRepo.expectDirectoryListGet("group", "projectA")
-        projectA12.ivy.expectGetMissing()
-        projectA12.jar.expectHead()
-        projectA12.jar.expectGet()
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-
-        when: "result is cached"
-        server.resetExpectations()
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-    }
-
-    def "uses latest version with correct status for latest.release and latest.milestone"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-def latestRevision = project.getProperty('latestRevision')
-configurations { compile }
-
-dependencies {
-    compile group: "group", name: "projectA", version: "latest.\${latestRevision}"
-}
-"""
-
-        when:
-        ivyHttpRepo.module("group", "projectA", "1.0").withStatus('release').publish()
-        ivyHttpRepo.module('group', 'projectA', '1.1').withStatus('milestone').publish()
-        ivyHttpRepo.module('group', 'projectA', '1.2').withStatus('integration').publish()
-        def release = ivyHttpRepo.module("group", "projectA", "2.0").withStatus('release').publish()
-        def milestone = ivyHttpRepo.module('group', 'projectA', '2.1').withStatus('milestone').publish()
-        def integration = ivyHttpRepo.module('group', 'projectA', '2.2').withStatus('integration').publish()
-
-        and:
-        ivyHttpRepo.expectDirectoryListGet("group", "projectA")
-        integration.ivy.expectGet()
-        milestone.ivy.expectGet()
-        release.ivy.expectGet()
-        release.jar.expectGet()
-
-        and:
-        executer.withArgument('-PlatestRevision=release')
-
-        then:
-        checkResolve "group:projectA:latest.release": "group:projectA:2.0"
-
-        when:
-        server.resetExpectations()
-        milestone.jar.expectGet()
-        executer.withArgument('-PlatestRevision=milestone')
-
-        then:
-        checkResolve "group:projectA:latest.milestone": "group:projectA:2.1"
-
-        when:
-        server.resetExpectations()
-        executer.withArgument('-PlatestRevision=milestone')
-
-        then:
-        checkResolve "group:projectA:latest.milestone": "group:projectA:2.1"
-    }
-
-    def "can use latest version from different remote repositories"() {
-        def repo1 = ivyHttpRepo("ivy1")
-        def repo2 = ivyHttpRepo("ivy2")
-
-        given:
-        useRepository repo1, repo2
-        buildFile << """
-    configurations { compile }
-    dependencies {
-        compile group: "group", name: "projectA", version: "latest.milestone"
-    }
-    """
-
-        when:
-        def version11 = repo1.module('group', 'projectA', '1.1').withStatus('milestone').publish()
-        def version12 = repo2.module('group', 'projectA', '1.2').withStatus('integration').publish()
-
-        and:
-        expectGetDynamicRevision(version11)
-
-        repo2.expectDirectoryListGet("group", "projectA")
-        version12.ivy.expectGet()
-
-        then:
-        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
-    }
-
-    def "can get latest version from repository with multiple ivyPatterns"() {
-        given:
-        def repo1 = ivyHttpRepo("ivyRepo1")
-        def repo1version2 = repo1.module('org.test', 'projectA', '1.2').withStatus("milestone").publish()
-        def repo1version3 = repo1.module('org.test', 'projectA', '1.3')
-        def repo2 = ivyHttpRepo("ivyRepo2")
-        repo2.module('org.test', 'projectA', '1.1').withStatus("integration").publish()
-        def repo2version3 = repo2.module('org.test', 'projectA', '1.3').withStatus("integration").publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy {
-        url "${repo1.uri}"
-        ivyPattern "${repo2.ivyPattern}"
-        artifactPattern "${repo2.artifactPattern}"
-    }
-}
-configurations { compile }
-dependencies {
-  compile 'org.test:projectA:latest.milestone'
-}
-"""
-        when:
-        repo1.expectDirectoryListGet("org.test", "projectA")
-        repo2.expectDirectoryListGet("org.test", "projectA")
-        // TODO - don't need this request
-        repo1version3.ivy.expectGetMissing()
-        repo2version3.ivy.expectGet()
-        repo1version2.ivy.expectGet()
-        repo1version2.jar.expectGet()
-
-        then:
-        checkResolve "org.test:projectA:latest.milestone": "org.test:projectA:1.2"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        checkResolve "org.test:projectA:latest.milestone": "org.test:projectA:1.2"
-    }
-
-    def "checks new repositories before returning any cached value"() {
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-
-        given:
-        buildFile << """
-repositories {
-    ivy { url "${repo1.uri}" }
-}
-
-if (project.hasProperty('addRepo2')) {
-    repositories {
-        ivy { url "${repo2.uri}" }
-    }
-}
-
-configurations { compile }
-dependencies {
-    compile group: "group", name: "projectA", version: "1.+"
-}
-"""
-
-        when:
-        def projectA11 = repo1.module("group", "projectA", "1.1").publish()
-        def projectA12 = repo2.module("group", "projectA", "1.2").publish()
-
-        and:
-        expectGetDynamicRevision(projectA11)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.1"
-
-        when:
-        server.resetExpectations()
-        expectGetDynamicRevision(projectA12)
-
-        then:
-        executer.withArguments("-PaddRepo2")
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        executer.withArguments("-PaddRepo2")
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-    }
-
-    def "recovers from broken modules in subsequent resolution"() {
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-
-        given:
-        useRepository repo1, repo2
-        buildFile << """
-    configurations { compile }
-    dependencies {
-        compile group: "group", name: "projectA", version: "1.+"
-    }
-    """
-
-        when:
-        def projectA12 = repo1.module("group", "projectA", "1.2").publish()
-        def projectA11 = repo2.module("group", "projectA", "1.1").publish()
-
-        and: "projectA is broken in repo1"
-        repo1.expectDirectoryListGetBroken("group", "projectA")
-        expectGetDynamicRevision(projectA11)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.1"
-
-        when:
-        server.resetExpectations()
-        expectGetDynamicRevision(projectA12)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-    }
-
-    def "uses and caches latest of versions obtained from multiple HTTP repositories"() {
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-        def repo3 = ivyHttpRepo("repo3")
-
-        given:
-        useRepository repo1, repo2, repo3
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "group", name: "projectA", version: "1.+"
-}
-"""
-
-        when:
-        def projectA11 = repo1.module("group", "projectA", "1.1").publish()
-        def projectA12 = repo3.module("group", "projectA", "1.2").publish()
-
-        and:
-        repo1.expectDirectoryListGet("group", "projectA")
-        // TODO Should not need to get this
-        projectA11.ivy.expectGet()
-        repo2.expectDirectoryListGet("group", "projectA")
-        expectGetDynamicRevision(projectA12)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-
-        when:
-        server.resetExpectations()
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-    }
-
-    def "reuses cached artifacts that match multiple dynamic versions"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "org.test", name: "projectA", version: project.getProperty('dependencyVersion')
-}
-"""
-
-        when:
-        ivyHttpRepo.module("org.test", "projectA", "1.1").publish()
-        def projectA12 = ivyHttpRepo.module("org.test", "projectA", "1.2").publish()
-
-        and:
-        expectGetDynamicRevision(projectA12)
-
-        and:
-        executer.withArgument("-PdependencyVersion=1.+")
-
-        then:
-        checkResolve "org.test:projectA:1.+": "org.test:projectA:1.2"
-
-        when:
-        server.resetExpectations()
-
-        and:
-        executer.withArgument("-PdependencyVersion=[1.0,2.0)")
-
-        then:
-        checkResolve "org.test:projectA:[1.0,2.0)": "org.test:projectA:1.2"
-    }
-
-    def "caches resolved revisions until cache expiry"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "group", name: "projectA", version: "1.+"
-}
-if (project.hasProperty('noDynamicRevisionCache')) {
-    configurations.all {
-        resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
-    }
-}
-"""
-
-        when: "Version 1.1 is published"
-        def version1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-
-        and:
-        expectGetDynamicRevision(version1)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.1"
-
-        when: "Version 1.2 is published"
-        server.resetExpectations()
-        def version2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
-
-        then: "Version 1.1 is still used, as the 1.+ -> 1.1 mapping is cached"
-        checkResolve "group:projectA:1.+": "group:projectA:1.1"
-
-        when: "zero expiry for dynamic revision cache"
-        executer.withArguments("-PnoDynamicRevisionCache")
-
-        and:
-        expectGetDynamicRevision(version2)
-
-        then: "Version 1.2 is used"
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-    }
-
-    def "uses and caches dynamic revisions for transitive dependencies"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "group", name: "main", version: "1.0"
-}
-
-if (project.hasProperty('noDynamicRevisionCache')) {
-    configurations.all {
-        resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
-    }
-}
-"""
-
-        when:
-        def mainProject = ivyHttpRepo.module("group", "main", "1.0")
-        mainProject.dependsOn("group", "projectA", "1.+")
-        mainProject.dependsOn("group", "projectB", "latest.integration")
-        mainProject.publish()
-
-        and:
-        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-        def projectB1 = ivyHttpRepo.module("group", "projectB", "1.1").publish()
-
-        and:
-        mainProject.ivy.expectGet()
-        mainProject.jar.expectGet()
-        expectGetDynamicRevision(projectA1)
-        expectGetDynamicRevision(projectB1)
-
-        then:
-        succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("group:main:1.0") {
-                    edge("group:projectA:1.+", "group:projectA:1.1")
-                    edge("group:projectB:latest.integration", "group:projectB:1.1")
-                }
-            }
-        }
-
-        when:
-        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
-        def projectB2 = ivyHttpRepo.module("group", "projectB", "2.2").publish()
-
-        and:
-        server.resetExpectations()
-
-        then:
-        succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("group:main:1.0") {
-                    edge("group:projectA:1.+", "group:projectA:1.1")
-                    edge("group:projectB:latest.integration", "group:projectB:1.1")
-                }
-            }
-        }
-
-        when: "Server handles requests"
-        server.resetExpectations()
-        expectGetDynamicRevision(projectA2)
-        expectGetDynamicRevision(projectB2)
-
-        and: "DynamicRevisionCache is bypassed"
-        executer.withArguments("-PnoDynamicRevisionCache")
-
-        then:
-        succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("group:main:1.0") {
-                    edge("group:projectA:1.+", "group:projectA:1.2")
-                    edge("group:projectB:latest.integration", "group:projectB:2.2")
-                }
-            }
-        }
-    }
-
-    public void "resolves dynamic version with 2 repositories where first repo results in 404 for directory listing"() {
-        given:
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-        def moduleA = repo2.module('group', 'projectA').publish()
-
-        and:
-        useRepository repo1, repo2
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.+'
-}
-"""
-
-        when:
-        repo1.expectDirectoryListGetMissing("group", "projectA")
-        expectGetDynamicRevision(moduleA)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.0"
-
-        when:
-        server.resetExpectations()
-        // No extra calls for cached dependencies
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.0"
-    }
-
-    def "reuses cached artifacts across repository types"() {
-        def ivyRepo = ivyHttpRepo('repo1')
-        def mavenRepo = mavenHttpRepo('repo2')
-        def ivyModule = ivyRepo.module("org.test", "a", "1.1").publish()
-        def mavenModule = mavenRepo.module("org.test", "a", "1.1").publish()
-        assert ivyModule.jarFile.bytes == mavenModule.artifactFile.bytes
-
-        given:
-        useRepository ivyRepo
-        buildFile << """
-configurations { compile }
-
-dependencies {
-    compile 'org.test:a:1+'
-}
-"""
-
-        when:
-        expectGetDynamicRevision(ivyModule)
-
-        then:
-        checkResolve "org.test:a:1+": "org.test:a:1.1"
-
-        when:
-        buildFile.text = """
-repositories {
-    maven { url '${mavenRepo.uri}' }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org.test:a:[1.0,2.0)'
-}
-"""
-        resolve.prepare()
-
-        and:
-        mavenRepo.getModuleMetaData("org.test", "a").expectGet()
-        mavenModule.pom.expectGet()
-        mavenModule.artifact.expectHead()
-        mavenModule.artifact.sha1.expectGet()
-
-        then:
-        checkResolve "org.test:a:[1.0,2.0)": "org.test:a:1.1"
-    }
-
-    def "can resolve dynamic versions with multiple ivy patterns"() {
-        given:
-        def repo1versions = [:]
-        def repo1 = ivyHttpRepo("ivyRepo1")
-        def repo2versions = [:]
-        def repo2 = ivyHttpRepo("ivyRepo2")
-        repo1versions.A1 = repo1.module('org.test', 'projectA', '1.1').publish()
-        repo1versions.A2 = repo1.module('org.test', 'projectA', '1.2').publish()
-        repo1versions.A3 = repo1.module('org.test', 'projectA', '1.3') // unpublished
-
-        repo2versions.A1 = repo2.module('org.test', 'projectA', '1.1').publish()
-        repo2versions.A3 = repo2.module('org.test', 'projectA', '1.3').publish()
-
-        repo1versions.B1 = repo1.module('org.test', 'projectB', '1.1').withStatus("integration").publish()
-        repo1versions.B2 = repo1.module('org.test', 'projectB', '1.2').withStatus("milestone").publish()
-        repo1versions.B3 = repo1.module('org.test', 'projectB', '1.3') // unpublished
-
-        repo2versions.B1 = repo2.module('org.test', 'projectB', '1.1').withStatus("milestone").publish()
-        repo2versions.B3 = repo2.module('org.test', 'projectB', '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 { compile }
-dependencies {
-  compile 'org.test:projectA:1.+'
-  compile 'org.test:projectB:latest.milestone'
-}
-"""
-
-        when:
-        repo1.expectDirectoryListGet("org.test", "projectA")
-        repo1versions.A3.ivy.expectGetMissing()
-        repo1versions.A3.jar.expectGetMissing()
-        expectGetDynamicRevision(repo2versions.A3)
-
-        and:
-        repo1versions.B3.ivy.expectGetMissing()
-        repo2.expectDirectoryListGet("org.test", "projectB")
-        repo2versions.B3.ivy.expectGet()
-        expectGetDynamicRevision(repo1versions.B2)
-
-        then:
-        checkResolve "org.test:projectA:1.+": "org.test:projectA:1.3",
-                     "org.test:projectB:latest.milestone": "org.test:projectB:1.2"
-
-        when: "resolve a second time"
-        server.resetExpectations()
-
-        then:
-        checkResolve "org.test:projectA:1.+": "org.test:projectA:1.3",
-                     "org.test:projectB:latest.milestone": "org.test:projectB:1.2"
-
-    }
-
-    def "versions are listed once only per resolve"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "group", name: "main", version: "1.0"
-    compile group: "group", name: "projectA", version: "latest.integration"
-}
-configurations.all {
-    resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
-}
-"""
-
-        when:
-        def projectA0 = ivyHttpRepo.module("group", "projectA", "1.0").publish()
-        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
-        def mainProject = ivyHttpRepo.module("group", "main", "1.0")
-        mainProject.dependsOn("group", "projectA", "1.+")
-        mainProject.publish()
-
-        and:
-        mainProject.ivy.expectGet()
-        mainProject.jar.expectGet()
-        ivyHttpRepo.expectDirectoryListGet("group", "projectA")
-        projectA1.ivy.expectGet()
-        projectA1.jar.expectGet()
-
-        then:
-        succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("group:main:1.0") {
-                    edge("group:projectA:1.+", "group:projectA:1.1")
-                }
-                edge("group:projectA:latest.integration", "group:projectA:1.1")
-            }
-        }
-
-        when:
-        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
-
-        and:
-        server.resetExpectations()
-        ivyHttpRepo.expectDirectoryListGet("group", "projectA")
-        projectA2.ivy.expectGet()
-        projectA2.jar.expectGet()
-
-        then:
-        succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("group:main:1.0") {
-                    edge("group:projectA:1.+", "group:projectA:1.2")
-                }
-                edge("group:projectA:latest.integration", "group:projectA:1.2")
-            }
-        }
-    }
-
-    @Unroll
-    def "checks remote for dynamic version before failing due to #scenario"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile group: "group", name: "projectA", version: "2.+"
-}
-"""
-
-        when: "no version > 2"
-        ivyHttpRepo.module("group", "projectA", "1.1").publish()
-        if (isMissing) {
-            ivyHttpRepo.expectDirectoryListGetMissing("group", "projectA")
-        } else {
-            ivyHttpRepo.expectDirectoryListGet("group", "projectA")
-        }
-
-        then:
-        fails "checkDeps"
-        failure.assertHasCause("Could not find any version that matches group:projectA:2.+.")
-
-        when:
-        def projectA2 = ivyHttpRepo.module("group", "projectA", "2.2").publish()
-
-        and:
-        server.resetExpectations()
-        expectGetDynamicRevision(projectA2)
-
-        then:
-        checkResolve "group:projectA:2.+": "group:projectA:2.2"
-
-        where:
-        scenario | isMissing
-        "module missing in cache listing" | true
-        "no valid version in cache listing" | false
-    }
-
-    @Unroll
-    def "finds best matching version in local and remote repository with #order"() {
-        given:
-        def fileRepo = ivyRepo("fileRepo")
-        def httpModule = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        and:
-        if (localFirst) {
-            useRepository fileRepo, ivyHttpRepo
-        } else {
-            useRepository ivyHttpRepo, fileRepo
-        }
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.+'
-}
-configurations.all {
-    resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
-}
-"""
-        when: "missing from local"
-        expectGetDynamicRevision(httpModule)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.2"
-
-        when: "missing from remote"
-        fileRepo.module('group', 'projectA', '1.1').publish()
-        ivyHttpRepo.expectDirectoryListGetMissing("group", "projectA")
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.1"
-
-        when: "present in both"
-        server.resetExpectations()
-        httpModule = ivyHttpRepo.module('group', 'projectA', '1.3').publish()
-        expectGetDynamicRevision(httpModule)
-
-        then:
-        checkResolve "group:projectA:1.+": "group:projectA:1.3"
-
-        where:
-        order          | localFirst
-        "local first"  | true
-        "remote first" | false
-    }
-
-    def "fails with reasonable error message when no cached version list in offline mode"() {
-        given:
-        useRepository ivyHttpRepo
-        buildFile << """
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.+'
-}
-"""
-        when:
-        executer.withArgument "--offline"
-
-        then:
-        fails "checkDeps"
-        failure.assertHasCause "Could not resolve all dependencies for configuration ':compile'."
-        failure.assertHasCause "No cached version listing for group:projectA:1.+ available for offline mode."
-    }
-
-    def checkResolve(Map edges) {
-        assert succeeds('checkDeps')
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edges.each {from, to ->
-                    edge(from, to)
-                }
-            }
-        }
-        true
-    }
-
-    def expectGetDynamicRevision(IvyHttpModule module) {
-        module.repository.expectDirectoryListGet(module.organisation, module.module)
-        module.ivy.expectGet()
-        module.jar.expectGet()
-    }
-
-    def useRepository(Repository... repo) {
-        buildFile << """
-repositories {
-"""
-        repo.each {
-            buildFile << "ivy { url '${it.uri}' }\n"
-        }
-        buildFile << """
-}
-"""
-    }
-
-}
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
deleted file mode 100644
index 1bdce10..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,430 +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.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.resolve.ResolveTestFixture
-import spock.lang.Issue
-
-class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def setup() {
-        settingsFile << "rootProject.name = 'test' "
-    }
-
-    @Issue("GRADLE-2502")
-    def "latest.integration selects highest version regardless of status"() {
-        given:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${ivyRepo.uri}"
-      }
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:latest.integration'
-  }
-  """
-
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.integration.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.0').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.integration", "org.test:projectA:1.0")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('integration').publish()
-        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('integration').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.integration", "org.test:projectA:1.2")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('release').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.integration", "org.test:projectA:1.3")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.4').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.integration", "org.test:projectA:1.4")
-            }
-        }
-    }
-
-    @Issue("GRADLE-2502")
-    def "latest.milestone selects highest version with milestone or release status"() {
-        given:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${ivyRepo.uri}"
-      }
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:latest.milestone'
-  }
-  """
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.milestone.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '2.0').withNoMetaData().publish()
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.milestone.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.milestone.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.0').withStatus('milestone').publish()
-        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('milestone').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('release').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2")
-            }
-        }
-    }
-
-    @Issue("GRADLE-2502")
-    public void "latest.release selects highest version with release status"() {
-        given:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${ivyRepo.uri}"
-      }
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:latest.release'
-  }
-  """
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.release.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '2.0').withNoMetaData().publish()
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.release.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
-        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('milestone').publish()
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:latest.release.'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.0').withStatus('release').publish()
-        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('release').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.release", "org.test:projectA:1.1")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('milestone').publish()
-        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.release", "org.test:projectA:1.1")
-            }
-        }
-    }
-
-    @Issue(["GRADLE-2502", "GRADLE-2794"])
-    def "version selector ending in + selects highest matching version"() {
-        given:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${ivyRepo.uri}"
-      }
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:1.2+'
-  }
-  """
-        and:
-        ivyRepo.module('org.test', 'projectA', '1.1.2').publish()
-        ivyRepo.module('org.test', 'projectA', '2.0').publish()
-
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:1.2+'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.1")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2.9').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.9")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2.10').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.10")
-            }
-        }
-    }
-
-    @Issue("GRADLE-2502")
-    def "version range selects highest matching version"() {
-        given:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${ivyRepo.uri}"
-      }
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:[1.2,2.0]'
-  }
-  """
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        and:
-        ivyRepo.module('org.test', 'projectA', '1.1.2').publish()
-        ivyRepo.module('org.test', 'projectA', '2.1').publish()
-
-        when:
-        runAndFail 'checkDeps'
-
-        then:
-        failureHasCause 'Could not find any version that matches org.test:projectA:[1.2,2.0]'
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2.1")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.3').publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.3")
-            }
-        }
-
-        when:
-        ivyRepo.module('org.test', 'projectA', '1.4').withNoMetaData().publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.4")
-            }
-        }
-    }
-
-    @Issue("GRADLE-2502")
-    def "can resolve dynamic version from different repositories"() {
-        given:
-        def repo1 = ivyRepo("ivyRepo1")
-        def repo2 = ivyRepo("ivyRepo2")
-
-        and:
-        buildFile << """
-  repositories {
-      ivy {
-          url "${repo1.uri}"
-      }
-      ivy {
-          url "${repo2.uri}"
-      }
-
-  }
-  configurations { compile }
-  dependencies {
-      compile 'org.test:projectA:latest.milestone'
-  }
-  """
-        and:
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        repo1.module('org.test', 'projectA', '1.1').withStatus("milestone").publish()
-        repo2.module('org.test', 'projectA', '1.2').withStatus("integration").publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1")
-            }
-        }
-
-        when:
-        repo2.module('org.test', 'projectA', '1.3').withStatus("milestone").publish()
-        run 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.3")
-            }
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy
deleted file mode 100644
index d1cd07f..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.junit.Rule
-
-class IvyHttpRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Rule ProgressLoggingFixture progressLogger = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    public void "can resolve and cache dependencies from an HTTP Ivy repository"() {
-        server.start()
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies { compile 'group:projectA:1.2' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        then:
-        succeeds 'listJars'
-        progressLogger.downloadProgressLogged("http://localhost:${server.port}/repo/group/projectA/1.2/ivy-1.2.xml")
-        progressLogger.downloadProgressLogged("http://localhost:${server.port}/repo/group/projectA/1.2/projectA-1.2.jar")
-
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'listJars'
-    }
-
-    public void "can resolve and cache artifact-only dependencies from an HTTP Ivy repository"() {
-        server.start()
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies { compile 'group:projectA:1.2 at jar' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-
-        when:
-        module.ivy.expectGet()
-        module.jar.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-        // No extra calls for cached dependencies
-
-        then:
-        succeeds('listJars')
-    }
-
-    def "can resolve and cache artifact-only dependencies with no descriptor from a HTTP repository"() {
-        server.start()
-        given:
-        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${ivyHttpRepo.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies { compile 'group:projectA:1.2 at jar' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-
-        when:
-        module.ivy.expectGetMissing()
-        module.jar.expectHead()
-        module.jar.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-        // No extra calls for cached dependencies
-
-        then:
-        succeeds('listJars')
-    }
-
-    public void "can resolve and cache dependencies from multiple HTTP Ivy repositories"() {
-        server.start()
-        given:
-        def repo1 = ivyHttpRepo("repo1")
-        def repo2 = ivyHttpRepo("repo2")
-        def moduleA = repo1.module('group', 'projectA').publish()
-        def missingModuleB = repo1.module('group', 'projectB')
-        def moduleB = repo2.module('group', 'projectB').publish()
-        def brokenModuleC = repo1.module('group', 'projectC')
-        def moduleC = repo2.module('group', 'projectC').publish()
-
-        and:
-        buildFile << """
-repositories {
-    ivy { url "${repo1.uri}" }
-    ivy { url "${repo2.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies {
-    compile 'group:projectA:1.0', 'group:projectB:1.0', 'group:projectC:1.0'
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar', 'projectC-1.0.jar']
-}
-"""
-
-        when:
-        moduleA.ivy.expectGet()
-        moduleA.jar.expectGet()
-
-        // Handles missing in repo1
-        missingModuleB.ivy.expectGetMissing()
-        missingModuleB.jar.expectHeadMissing()
-
-        moduleB.ivy.expectGet()
-        moduleB.jar.expectGet()
-
-        // Handles from broken url in repo1 (but does not cache)
-        brokenModuleC.ivy.expectGetBroken()
-
-        moduleC.ivy.expectGet()
-        moduleC.jar.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-        // Will always re-attempt a broken repository
-        brokenModuleC.ivy.expectHeadBroken()
-        // No extra calls for cached dependencies
-
-        then:
-        succeeds('listJars')
-    }
-
-    public void "uses all configured patterns to resolve artifacts and caches result"() {
-        server.start()
-
-        given:
-        def module = ivyRepo().module('group', 'projectA', '1.2').publish()
-
-        buildFile << """
-repositories {
-    ivy {
-        url "http://localhost:${server.port}/first"
-        artifactPattern "http://localhost:${server.port}/second/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
-        artifactPattern "http://localhost:${server.port}/third/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
-        ivyPattern "http://localhost:${server.port}/second/[module]/[revision]/ivy.xml"
-        ivyPattern "http://localhost:${server.port}/third/[module]/[revision]/ivy.xml"
-    }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies {
-    compile 'group:projectA:1.2'
-}
-task show << { println configurations.compile.files }
-"""
-
-        when:
-        server.expectGetMissing('/first/group/projectA/1.2/ivy-1.2.xml')
-        server.expectGetMissing('/first/group/projectA/1.2/projectA-1.2.jar')
-        server.expectGetMissing('/second/projectA/1.2/ivy.xml')
-        server.expectGetMissing('/second/projectA/1.2/projectA-1.2.jar')
-        server.expectGet('/third/projectA/1.2/ivy.xml', module.ivyFile)
-        server.expectGet('/third/projectA/1.2/projectA-1.2.jar', module.jarFile)
-
-        then:
-        succeeds('show')
-
-        when:
-        server.resetExpectations()
-
-        then:
-        succeeds('show')
-    }
-
-    public void "replaces org.name with org/name when using maven layout"() {
-        server.start()
-
-        given:
-        def module = ivyRepo().module('org.name.here', 'projectA', '1.2').publish()
-
-        buildFile << """
-repositories {
-    ivy {
-        url "http://localhost:${server.port}"
-        layout "maven"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'org.name.here:projectA:1.2'
-}
-
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        when:
-        server.expectGet('/org/name/here/projectA/1.2/ivy-1.2.xml', module.ivyFile)
-        server.expectGet('/org/name/here/projectA/1.2/projectA-1.2.jar', module.jarFile)
-
-        and:
-        succeeds('retrieve')
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar')
-    }
-
-    def "reuses cached details when switching ivy resolve mode"() {
-        given:
-        server.start()
-        buildFile << """
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies {
-    repositories {
-        ivy {
-            url "${ivyHttpRepo.uri}"
-            resolve.dynamicMode = project.hasProperty('useDynamicResolve')
-        }
-    }
-    compile 'org:projectA:1.2'
-}
-task retrieve(type: Sync) {
-  from configurations.compile
-  into 'libs'
-}
-"""
-        def moduleA = ivyHttpRepo.module('org', 'projectA', '1.2')
-                .dependsOn(organisation: 'org', module: 'projectB', revision: '1.5', revConstraint: 'latest.integration')
-                .publish()
-
-        def moduleB15 = ivyHttpRepo.module('org', 'projectB', '1.5')
-                .publish()
-
-        def moduleB16 = ivyHttpRepo.module('org', 'projectB', '1.6')
-                .publish()
-
-        when:
-        moduleA.ivy.expectGet()
-        moduleA.jar.expectGet()
-        moduleB15.ivy.expectGet()
-        moduleB15.jar.expectGet()
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar')
-
-        when:
-        server.resetExpectations()
-        ivyHttpRepo.expectDirectoryListGet('org', 'projectB')
-        moduleB16.ivy.expectGet()
-        moduleB16.jar.expectGet()
-        executer.withArguments("-PuseDynamicResolve=true")
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar')
-
-        when:
-        server.resetExpectations()
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar')
-
-        when:
-        server.resetExpectations()
-        executer.withArguments("-PuseDynamicResolve=true")
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy
deleted file mode 100644
index ab1bc8a..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy
+++ /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.integtests.resolve.ivy
-
-import org.gradle.integtests.resolve.http.AbstractHttpsRepoResolveIntegrationTest
-
-class IvyHttpsRepoResolveIntegrationTest extends AbstractHttpsRepoResolveIntegrationTest {
-    protected String setupRepo() {
-        def module = ivyRepo().module('my-group', 'my-module').publish()
-        server.allowGetOrHead('/repo1/my-group/my-module/1.0/ivy-1.0.xml', module.ivyFile)
-        server.allowGetOrHead('/repo1/my-group/my-module/1.0/my-module-1.0.jar', module.jarFile)
-        "ivy"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy
deleted file mode 100644
index 907163e..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.resolution.JvmLibraryJavadocArtifact
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.resolve.JvmLibraryArtifactResolveTestFixture
-import org.gradle.test.fixtures.ivy.IvyRepository
-import spock.lang.Unroll
-
-// TODO:DAZ Test can resolve multiple source/javadoc artifacts declared in 'sources'/'javadoc' configuration
-class IvyJvmLibraryArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    def fileRepo = ivyRepo
-    def httpRepo = ivyHttpRepo
-    def module = httpRepo.module("some.group", "some-artifact", "1.0")
-    JvmLibraryArtifactResolveTestFixture fixture
-
-    def setup() {
-        server.start()
-        initBuild(httpRepo)
-
-        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
-
-        publishModule()
-    }
-
-    def initBuild(IvyRepository repo, String module = "some.group:some-artifact:1.0") {
-        buildFile.text = """
-repositories {
-    ivy { url '$repo.uri' }
-}
-configurations { compile }
-dependencies {
-    compile "${module}"
-}
-"""
-    }
-
-    def "resolve sources artifacts"() {
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifact("my-sources")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolve javadoc artifacts"() {
-        fixture.requestingTypes(JvmLibraryJavadocArtifact)
-                .expectJavadocArtifact("my-javadoc")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-javadoc").expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolve all artifacts"() {
-        fixture.requestingTypes()
-                .expectSourceArtifact("my-sources")
-                .expectJavadocArtifact("my-javadoc")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGet()
-        module.getArtifact(classifier: "my-javadoc").expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    @Unroll
-    def "fetches missing artifacts for module #condition"() {
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifactNotFound("my-sources")
-                .prepare()
-        buildFile << """
-dependencies {
-    components {
-        eachComponent { ComponentMetadataDetails details ->
-            details.changing = true
-        }
-    }
-}
-
-if (project.hasProperty('nocache')) {
-    configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-"""
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGetMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-
-        when:
-        module.publishWithChangedContent()
-        fixture.clearExpectations()
-                .expectSourceArtifact("my-sources")
-                .createVerifyTask("verifyRefresh")
-
-        and:
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGet()
-
-        then:
-        executer.withArgument(execArg)
-        succeeds("verifyRefresh")
-
-        where:
-        condition                     | execArg
-        "with --refresh-dependencies" | "--refresh-dependencies"
-        "when ivy descriptor changes" | "-Pnocache"
-    }
-
-    @Unroll
-    def "updates artifacts for module #condition"() {
-        buildFile << """
-dependencies {
-    components {
-        eachComponent { ComponentMetadataDetails details ->
-            details.changing = true
-        }
-    }
-}
-
-if (project.hasProperty('nocache')) {
-    configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-"""
-
-        final sourceArtifact = module.getArtifact(classifier: "my-sources")
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifact("my-sources")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        sourceArtifact.expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-
-        when:
-        def snapshot = file("sources/some-artifact-1.0-my-sources.jar").snapshot()
-        module.publishWithChangedContent()
-
-        and:
-        server.resetExpectations()
-        module.ivy.expectHead()
-        module.ivy.sha1.expectGet()
-        module.ivy.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.sha1.expectGet()
-        sourceArtifact.expectGet()
-
-        then:
-        executer.withArgument(execArg)
-        succeeds("verify")
-        file("sources/some-artifact-1.0-my-sources.jar").assertHasChangedSince(snapshot)
-
-        where:
-        condition                     | execArg
-        "with --refresh-dependencies" | "--refresh-dependencies"
-        "when ivy descriptor changes" | "-Pnocache"
-    }
-
-    def "reports failure to resolve artifacts of non-existing component"() {
-        fixture.expectComponentNotFound().prepare()
-
-        when:
-        module.ivy.expectGetMissing()
-        module.jar.expectHeadMissing()
-
-        then:
-        succeeds("verify")
-    }
-
-    def "reports failure to resolve missing artifacts"() {
-        fixture.expectSourceArtifactNotFound("my-sources")
-                .expectJavadocArtifactNotFound("my-javadoc")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGetMissing()
-        module.getArtifact(classifier: "my-javadoc").expectGetMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolves when some artifacts are missing"() {
-        fixture.expectSourceArtifact("my-sources")
-                .expectJavadocArtifactNotFound("my-javadoc")
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGet()
-        module.getArtifact(classifier: "my-javadoc").expectGetMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolves when some artifacts are broken"() {
-        fixture.expectSourceArtifact("my-sources")
-                .expectJavadocArtifactFailure(new ArtifactResolveException("Could not download artifact 'some.group:some-artifact:1.0:some-artifact-my-javadoc.jar'"))
-                .prepare()
-
-        when:
-        module.ivy.expectGet()
-        module.getArtifact(classifier: "my-sources").expectGet()
-        module.getArtifact(classifier: "my-javadoc").expectGetBroken()
-
-        then:
-        succeeds("verify")
-
-        when:
-        fixture.clearExpectations()
-                .expectSourceArtifact("my-sources")
-                .expectJavadocArtifact("my-javadoc")
-                .createVerifyTask("verifyFixed")
-
-        and:
-        server.resetExpectations()
-        // Only the broken artifact is not cached
-        module.getArtifact(classifier: "my-javadoc").expectGet()
-
-        then:
-        succeeds("verifyFixed")
-    }
-
-    def "resolve and does not cache artifacts from local repository"() {
-        initBuild(fileRepo)
-
-        fixture.requestingTypes()
-                .expectSourceArtifact("my-sources")
-                .expectJavadocArtifact("my-javadoc")
-                .prepare()
-
-        when:
-        succeeds("verify")
-
-        and:
-        def snapshot = file("sources/some-artifact-1.0-my-sources.jar").snapshot()
-
-        and:
-        module.publishWithChangedContent()
-
-        then:
-        succeeds("verify")
-        file("sources/some-artifact-1.0-my-sources.jar").assertHasChangedSince(snapshot)
-    }
-
-    def "can resolve artifacts with maven scheme from ivy repository"() {
-        initBuild(httpRepo, "some.group:some-artifact:1.1")
-
-        // Published with no configurations, and a source artifact only
-        def moduleWithMavenScheme = httpRepo.module("some.group", "some-artifact", "1.1")
-        moduleWithMavenScheme.artifact(classifier: "sources")
-        moduleWithMavenScheme.publish()
-
-
-        fixture.withComponentVersion("some.group", "some-artifact", "1.1")
-                .requestingTypes(JvmLibrarySourcesArtifact, JvmLibraryJavadocArtifact)
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        when:
-        moduleWithMavenScheme.ivy.expectGet()
-        moduleWithMavenScheme.getArtifact(classifier: "sources").expectHead()
-        moduleWithMavenScheme.getArtifact(classifier: "sources").expectGet()
-        moduleWithMavenScheme.getArtifact(classifier: "javadoc").expectHeadMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def checkArtifactsResolvedAndCached() {
-        assert succeeds("verify")
-        server.resetExpectations()
-        assert succeeds("verify")
-        true
-    }
-
-    private publishModule() {
-        module.configuration("sources")
-        module.configuration("javadoc")
-        // use uncommon classifiers that are different from those used by maven, 
-        // in order to prove that artifact names don't matter
-        module.artifact(type: "source", classifier: "my-sources", ext: "jar", conf: "sources")
-        module.artifact(type: "javadoc", classifier: "my-javadoc", ext: "jar", conf: "javadoc")
-        module.publish()
-    }
-}
\ 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
deleted file mode 100644
index 54adec2..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,223 +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.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:
-        server.start()
-
-        buildFile << """
-configurations {
-    compile
-}
-dependencies {
-    repositories {
-        ivy { url "${ivyHttpRepo.uri}" }
-    }
-    compile group: 'ivy.configuration', name: 'projectA', version: '1.2', configuration: 'a'
-}
-task retrieve(type: Sync) {
-  from configurations.compile
-  into 'libs'
-}
-"""
-        def projectA = ivyHttpRepo.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()
-
-        def projectB = ivyHttpRepo.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()
-
-        def projectC = ivyHttpRepo.module('ivy.configuration', 'projectC', '1.7').publish()
-        def projectD = ivyHttpRepo.module('ivy.configuration', 'projectD', '1.7').publish()
-
-        projectA.allowAll()
-        projectB.allowAll()
-        projectC.allowAll()
-        projectD.allowAll()
-
-        when:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants(* (['projectA-1.2.jar'] + jars))
-
-        when:
-        server.resetExpectations()
-        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
deleted file mode 100644
index bc9fb8d..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.ivy
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class IvyResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def "dependency includes all artifacts and transitive dependencies of referenced configuration"() {
-        given:
-        ivyRepo.module("org.gradle", "test", "1.45")
-                .dependsOn("org.gradle", "other", "preview-1")
-                .artifact()
-                .artifact(classifier: "classifier")
-                .artifact(name: "test-extra")
-                .publish()
-
-        ivyRepo.module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        settingsFile << """
-rootProject.name = 'testproject'
-"""
-
-        buildFile << """
-group = 'org.gradle'
-version = '1.0'
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'test-1.45-classifier.jar', 'test-extra-1.45.jar', 'other-preview-1.jar']
-    def result = configurations.compile.incoming.resolutionResult
-
-    // Check root component
-    def rootId = result.root.id
-    assert rootId instanceof ProjectComponentIdentifier
-    def rootPublishedAs = result.root.moduleVersion
-    assert rootPublishedAs.group == 'org.gradle'
-    assert rootPublishedAs.name == 'testproject'
-    assert rootPublishedAs.version == '1.0'
-
-    // Check external module components
-    def externalComponents = result.root.dependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
-    assert externalComponents.size() == 1
-    def selectedExternalComponent = externalComponents[0]
-    assert selectedExternalComponent.id.group == 'org.gradle'
-    assert selectedExternalComponent.id.module == 'test'
-    assert selectedExternalComponent.id.version == '1.45'
-    assert selectedExternalComponent.moduleVersion.group == 'org.gradle'
-    assert selectedExternalComponent.moduleVersion.name == 'test'
-    assert selectedExternalComponent.moduleVersion.version == '1.45'
-
-    // Check external dependencies
-    def externalDependencies = result.root.dependencies.requested.findAll { it instanceof ModuleComponentSelector }
-    assert externalDependencies.size() == 1
-    def requestedExternalDependency = externalDependencies[0]
-    assert requestedExternalDependency.group == 'org.gradle'
-    assert requestedExternalDependency.module == 'test'
-    assert requestedExternalDependency.version == '1.45'
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    def "dependency that references a classifier includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
-        given:
-        ivyRepo.module("org.gradle", "test", "1.45")
-                .dependsOn("org.gradle", "other", "preview-1")
-                .artifact(classifier: "classifier")
-                .artifact(name: "test-extra")
-                .publish()
-        ivyRepo.module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45:classifier"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    def "dependency that references a classifier can resolve module with no metadata"() {
-        given:
-        def ivyModule = ivyRepo.module("org.gradle", "test", "1.45").withNoMetaData().artifact(classifier: "classifier").publish()
-
-        and:
-        buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45:classifier"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar']
-}
-"""
-
-        expect:
-        ivyModule.jarFile.assertDoesNotExist()
-        succeeds "check"
-    }
-
-    def "dependency that references an artifact includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
-        given:
-        ivyRepo.module("org.gradle", "test", "1.45")
-                .dependsOn("org.gradle", "other", "preview-1")
-                .artifact(classifier: "classifier")
-                .artifact(name: "test-extra")
-                .publish()
-        ivyRepo.module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations { compile }
-dependencies {
-    compile ("org.gradle:test:1.45") {
-        artifact {
-            name = 'test-extra'
-            type = 'jar'
-        }
-    }
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-extra-1.45.jar', 'other-preview-1.jar']
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    def "transitive flag of referenced configuration affects its transitive dependencies only"() {
-        given:
-        ivyRepo.module("org.gradle", "test", "1.45")
-                .dependsOn("org.gradle", "other", "preview-1")
-                .nonTransitive('default')
-                .publish()
-        ivyRepo.module("org.gradle", "other", "preview-1").dependsOn("org.gradle", "other2", "7").publish()
-        ivyRepo.module("org.gradle", "other2", "7").publish()
-
-        and:
-        buildFile << """
-repositories { ivy { url "${ivyRepo.uri}" } }
-configurations {
-    compile
-    runtime.extendsFrom compile
-}
-dependencies {
-    compile "org.gradle:test:1.45"
-    runtime "org.gradle:other:preview-1"
-}
-
-task check << {
-    def spec = { it.name == 'test' } as Spec
-
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
-    assert configurations.compile.resolvedConfiguration.getFiles(spec).collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
-
-    assert configurations.runtime.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar', 'other2-7.jar']
-    assert configurations.compile.resolvedConfiguration.getFiles(spec).collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-}
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
deleted file mode 100644
index c720ed1..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Issue
-
-class BadPomFileResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    @Issue("http://issues.gradle.org/browse/GRADLE-1005")
-    def "can handle self referencing dependency"() {
-        given:
-        mavenRepo().module('group', 'artifact', '1.0').dependsOn('group', 'artifact', '1.0').publish()
-
-        and:
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo().uri}" }
-            }
-            configurations { compile }
-            dependencies {
-                compile "group:artifact:1.0"
-            }
-            task libs << { assert configurations.compile.files.collect {it.name} == ['artifact-1.0.jar'] }
-        """
-
-        expect:
-        succeeds ":libs"
-    }
-
-    @Issue("http://issues.gradle.org/browse/GRADLE-2861")
-    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"
-    }
-
-    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}")
-            .assertHasCause("null name not allowed")
-    }
-
-    def "reports missing parent POM"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0")
-
-        def child = mavenHttpRepo.module("org", "child", "1.0")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task showBroken << { println configurations.compile.files }
-"""
-
-        when:
-        child.pom.expectGet()
-        parent.pom.expectGetMissing()
-
-        // Will always check for a default artifact with a module with 'pom' packaging
-        // TODO - should not make this request
-        parent.artifact.expectHeadMissing()
-
-        and:
-        fails 'showBroken'
-
-        then:
-        failure.assertResolutionFailure(':compile')
-                .assertHasCause("Could not parse POM ${child.pom.uri}")
-                .assertHasCause("Could not find any version that matches org:parent:1.0.")
-    }
-
-    def "reports parent POM that cannot be parsed"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0").publish()
-        parent.pomFile.text = "<project/>"
-
-        def child = mavenHttpRepo.module("org", "child", "1.0")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task showBroken << { println configurations.compile.files }
-"""
-
-        when:
-        child.pom.expectGet()
-        parent.pom.expectGet()
-
-        and:
-        fails 'showBroken'
-
-        then:
-        failure.assertResolutionFailure(":compile")
-            .assertHasCause("Could not parse POM ${child.pom.uri}")
-            .assertHasCause("Could not parse POM ${parent.pom.uri}")
-            .assertHasCause("null name not allowed")
-    }
-}
\ 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
deleted file mode 100644
index 6e2ff2e..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/LegacyMavenRepoResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,225 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class LegacyMavenRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def "can configure legacy Maven resolver to verify artifact using checksums"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module("group", "module", "1.2").publishWithChangedContent()
-        buildFile << """
-repositories {
-    def repo = mavenRepo url: '${mavenHttpRepo.uri}'
-    repo.checksums = 'sha1,md5'
-}
-
-configurations {
-    check
-}
-
-dependencies {
-    check 'group:module:1.2'
-}
-
-task check << {
-    configurations.check.files*.name == 'module-1.2.jar'
-}
-"""
-        and:
-        module.pom.expectGet()
-        module.pom.sha1.expectGetMissing()
-        module.pom.md5.expectGet()
-        module.artifact.expectGet()
-        module.artifact.sha1.expectGetMissing()
-        module.artifact.md5.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        expect:
-        succeeds 'check'
-
-        when:
-        module.publishWithChangedContent()
-
-        and:
-        server.resetExpectations()
-        module.pom.expectHead()
-        module.pom.sha1.expectGet()
-        module.pom.expectGet()
-        // TODO - shouldn't get checksum twice
-        module.pom.sha1.expectGet()
-        module.artifact.expectHead()
-        module.artifact.sha1.expectGet()
-        module.artifact.expectGet()
-        // TODO - shouldn't get checksum twice
-        module.artifact.sha1.expectGet()
-        executer.withDeprecationChecksDisabled()
-
-        then:
-        executer.withArguments("--refresh-dependencies")
-        succeeds 'check'
-    }
-
-    def "fails when checksum does not match artifact contents"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module("group", "module", "1.2").publishWithChangedContent()
-        buildFile << """
-repositories {
-    def repo = mavenRepo url: '${mavenHttpRepo.uri}'
-    repo.checksums = 'sha1'
-}
-
-configurations {
-    check
-}
-
-dependencies {
-    check 'group:module:1.2'
-}
-
-task check << {
-    configurations.check.files*.name == 'module-1.2.jar'
-}
-"""
-        and:
-        module.pom.expectGet()
-        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:module.jar'")
-        failureHasCause("invalid sha1: expected=1234 computed=2ee22701c9bd9af023ed20917897da5ce77336bc")
-    }
-
-    def "can configure resolver to fail when descriptor is not present"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module("group", "module", "1.2").publish()
-
-        buildFile << """
-repositories {
-    def repo = mavenRepo url: '${mavenHttpRepo.uri}'
-    repo.descriptor = "required"
-}
-
-configurations {
-    check
-}
-
-dependencies {
-    check 'group:module:1.2'
-}
-
-task check << {
-    configurations.check.files
-}
-"""
-        and:
-        module.pom.expectGetMissing()
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        expect:
-        fails 'check'
-        failureHasCause("Could not find group:module:1.2.")
-    }
-
-    def "can configure resolver to ignore poms"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module("group", "module", "1.2").publish()
-
-        buildFile << """
-repositories {
-    def repo = mavenRepo url: '${mavenHttpRepo.uri}'
-    repo.usepoms = false
-}
-
-configurations {
-    check
-}
-
-dependencies {
-    check 'group:module:1.2'
-}
-
-task check << {
-    configurations.check.files*.name == 'module-1.2.jar'
-}
-"""
-        and:
-        // TODO - do not need this head request
-        module.artifact.expectHead()
-        module.artifact.expectGet()
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        expect:
-        succeeds "check"
-    }
-
-    def "can configure resolver to ignore maven-metadata.xml when resolving snapshots"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module("group", "module", "1.2-SNAPSHOT").withNonUniqueSnapshots().publish()
-
-        buildFile << """
-repositories {
-    def repo = mavenRepo url: '${mavenHttpRepo.uri}'
-    repo.useMavenMetadata = false
-}
-
-configurations {
-    check
-}
-
-dependencies {
-    check 'group:module:1.2-SNAPSHOT'
-}
-
-task check << {
-    configurations.check.files*.name == 'module-1.2-SNAPSHOT.jar'
-}
-"""
-        and:
-        module.pom.expectGet()
-        module.artifact.expectGet()
-
-        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
deleted file mode 100644
index 024afa0..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,101 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class MavenBrokenRemoteResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    public void "reports and recovers from failed POM download"() {
-        server.start()
-
-        given:
-        def module = mavenHttpRepo.module('group', 'projectA', '1.3').publish()
-
-        buildFile << """
-repositories {
-    maven {
-        url "${ivyHttpRepo.uri}"
-    }
-}
-configurations { broken }
-dependencies {
-    broken 'group:projectA:1.3'
-}
-task showBroken << { println configurations.broken.files }
-"""
-
-        when:
-        module.pom.expectGetBroken()
-        fails("showBroken")
-
-        then:
-        failure
-            .assertHasDescription('Execution failed for task \':showBroken\'.')
-            .assertResolutionFailure(':broken')
-            .assertHasCause('Could not resolve group:projectA:1.3.')
-            .assertHasCause("Could not GET '${module.pom.uri}'. Received status code 500 from server: broken")
-
-        when:
-        server.resetExpectations()
-        module.pom.expectGet()
-        module.artifact.expectGet()
-
-        then:
-        succeeds("showBroken")
-    }
-
-    public void "reports and recovers from failed artifact download"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    maven {
-        url "${mavenHttpRepo.uri}"
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.2'
-}
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        and:
-        def module = mavenHttpRepo.module('group', 'projectA', '1.2').publish()
-
-        when:
-        module.pom.expectGet()
-        module.artifact.expectGetBroken()
-
-        then:
-        fails "retrieve"
-        failure.assertHasCause("Could not download artifact 'group:projectA:1.2:projectA.jar'")
-        failure.assertHasCause("Could not GET '${module.artifact.uri}'. Received status code 500 from server: broken")
-
-        when:
-        server.resetExpectations()
-        module.artifact.expectGet()
-
-        then:
-        succeeds "retrieve"
-        file('libs').assertHasDescendants('projectA-1.2.jar')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy
deleted file mode 100644
index 75e0535..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesChangingModulesIntegrationTest
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-
-class MavenComponentMetadataRulesChangingModulesIntegrationTest extends ComponentMetadataRulesChangingModulesIntegrationTest {
-    MavenHttpRepository getRepo() {
-        mavenHttpRepo
-    }
-
-    String getRepoDeclaration() {
-"""
-repositories {
-    maven {
-        url "$repo.uri"
-    }
-}
-"""
-    }
-
-    def setup() {
-        moduleA.rootMetaData.allowGetOrHead()
-    }
-
-    def "snapshot dependencies have changing flag initialized to true"() {
-        def moduleB = repo.module("org.test", "moduleB", "1.0-SNAPSHOT").publish()
-        moduleB.allowAll()
-
-        buildFile <<
-"""
-$repoDeclaration
-configurations {
-    modules
-}
-dependencies {
-    modules "org.test:moduleB:1.0-SNAPSHOT"
-    components {
-        eachComponent { details ->
-            file(details.id.name).text = details.changing
-        }
-    }
-}
-task resolve << {
-    configurations.modules.files
-}
-"""
-
-        when:
-        run("resolve")
-
-        then:
-        file("moduleB").text == "true"
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index cd8dac5..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.integtests.resolve.maven
-
-import org.gradle.integtests.resolve.ComponentMetadataRulesIntegrationTest
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-
-class MavenComponentMetadataRulesIntegrationTest extends ComponentMetadataRulesIntegrationTest {
-    @Override
-    MavenHttpRepository getRepo() {
-        mavenHttpRepo
-    }
-
-    @Override
-    String getRepoDeclaration() {
-"""
-repositories {
-    maven {
-        url "$repo.uri"
-    }
-}
-"""
-    }
-
-    @Override
-    String getDefaultStatus() {
-        "release"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
deleted file mode 100644
index 7a8c9e4..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesStatusIntegrationTest
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-
-class MavenComponentMetadataRulesStatusIntegrationTest extends ComponentMetadataRulesStatusIntegrationTest {
-    MavenHttpRepository getRepo() {
-        mavenHttpRepo
-    }
-
-    String getRepoDeclaration() {
-"""
-repositories {
-    maven {
-        url "$repo.uri"
-    }
-}
-"""
-    }
-
-    def "snapshot and release versions have correct status"() {
-        given:
-        repo.module('group1', 'projectA', '1.0').publish().allowAll()
-        repo.module('group2', 'projectB', '2.0-SNAPSHOT').publish().allowAll()
-
-        and:
-        buildFile.text =
-"""
-$repoDeclaration
-configurations { compile }
-dependencies {
-    compile 'group1:projectA:1.0'
-    compile 'group2:projectB:2.0-SNAPSHOT'
-    components {
-        eachComponent {
-            assert it.status == it.id.name == "projectA" ? "release" : "integration"
-            assert it.statusScheme == ['integration', 'milestone', 'release']
-        }
-    }
-}
-
-task resolve << {
-    configurations.compile.resolve()
-}
-"""
-
-        expect:
-        succeeds "resolve"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.groovy
deleted file mode 100644
index 642dc60..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,68 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Issue
-
-class MavenCustomPackagingResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Issue("http://issues.gradle.org/browse/GRADLE-2984")
-    def "can resolve dependency with custom packaging"() {
-        given:
-        executer.withDeprecationChecksDisabled()
-
-        def extension = "aar"
-        m2Installation.mavenRepo().module("local", "local", "1.0").hasType(extension).hasPackaging(extension).publish()
-        mavenHttpRepo.module("remote", "remote", "1.0").with {
-            allowAll()
-            hasType(extension).hasPackaging(extension).publish()
-        }
-
-        // Not publishing, just allowing the HTTP requests to 404
-        mavenHttpRepo.module("local", "local", "1.0").allowAll()
-
-        server.start()
-
-        when:
-        buildScript """
-            configurations {
-                local
-                remote
-            }
-            repositories {
-                mavenLocal() // there's a different code path for mavenLocal() so test it explicitly
-                maven { url "$mavenHttpRepo.uri" }
-            }
-            dependencies {
-                local "local:local:1.0"
-                remote "remote:remote:1.0"
-            }
-
-            task showDeps {
-                doLast {
-                    println configurations.local.files // trigger resolve
-                    println configurations.remote.files // trigger resolve
-                }
-            }
-        """
-
-        then:
-        succeeds("showDeps")
-    }
-
-}
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
deleted file mode 100644
index f1573d9..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.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"() {
-        given:
-        def module = mavenRepo().module("org.gradle", "test", "1.45")
-        module.dependsOn("org.gradle", "other", "preview-1")
-        module.artifact(classifier: 'classifier')
-        module.publish()
-        mavenRepo().module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        settingsFile << """
-rootProject.name = 'testproject'
-"""
-
-        buildFile << """
-group = 'org.gradle'
-version = '1.0'
-repositories { maven { url "${mavenRepo().uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
-    def result = configurations.compile.incoming.resolutionResult
-
-    // Check root component
-    def rootId = result.root.id
-    assert rootId instanceof ProjectComponentIdentifier
-    def rootPublishedAs = result.root.moduleVersion
-    assert rootPublishedAs.group == 'org.gradle'
-    assert rootPublishedAs.name == 'testproject'
-    assert rootPublishedAs.version == '1.0'
-
-    // Check external module components
-    def externalComponents = result.root.dependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
-    assert externalComponents.size() == 1
-    def selectedExternalComponent = externalComponents[0]
-    assert selectedExternalComponent.id.group == 'org.gradle'
-    assert selectedExternalComponent.id.module == 'test'
-    assert selectedExternalComponent.id.version == '1.45'
-    assert selectedExternalComponent.moduleVersion.group == 'org.gradle'
-    assert selectedExternalComponent.moduleVersion.name == 'test'
-    assert selectedExternalComponent.moduleVersion.version == '1.45'
-
-    // Check external dependencies
-    def externalDependencies = result.root.dependencies.requested.findAll { it instanceof ModuleComponentSelector }
-    assert externalDependencies.size() == 1
-    def requestedExternalDependency = externalDependencies[0]
-    assert requestedExternalDependency.group == 'org.gradle'
-    assert requestedExternalDependency.module == 'test'
-    assert requestedExternalDependency.version == '1.45'
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    def "dependency that references a classifier includes the matching artifact only plus the runtime dependencies of referenced module"() {
-        given:
-        def module = mavenRepo().module("org.gradle", "test", "1.45")
-        module.dependsOn("org.gradle", "other", "preview-1")
-        module.artifact(classifier: 'classifier')
-        module.artifact(classifier: 'some-other')
-        module.publish()
-        mavenRepo().module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        buildFile << """
-repositories { maven { url "${mavenRepo().uri}" } }
-configurations { compile }
-dependencies {
-    compile "org.gradle:test:1.45:classifier"
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-
-    def "dependency that references an artifact includes the matching artifact only plus the runtime dependencies of referenced module"() {
-        given:
-        def module = mavenRepo().module("org.gradle", "test", "1.45")
-        module.dependsOn("org.gradle", "other", "preview-1")
-        module.artifact(classifier: 'classifier')
-        module.publish()
-        mavenRepo().module("org.gradle", "other", "preview-1").publish()
-
-        and:
-        buildFile << """
-repositories { maven { url "${mavenRepo().uri}" } }
-configurations { compile }
-dependencies {
-    compile ("org.gradle:test:1.45") {
-        artifact {
-            name = 'test'
-            type = 'jar'
-            classifier = 'classifier'
-        }
-    }
-}
-
-task check << {
-    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
-}
-"""
-
-        expect:
-        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
-        // Logback classic depends on a later version of slf4j-api than required by hibernate core
-
-        given:
-        buildFile << """
-repositories {
-    mavenCentral()
-}
-
-configurations {
-    compile
-}
-
-dependencies {
-    compile "commons-collections:commons-collections:3.0"
-    compile "ch.qos.logback:logback-classic:0.9.30"
-    compile "org.hibernate:hibernate-core:3.6.7.Final"
-}
-
-task check << {
-    def compile = configurations.compile
-    assert compile.resolvedConfiguration.firstLevelModuleDependencies.collect { it.name } == [
-        'ch.qos.logback:logback-classic:0.9.30',
-        'org.hibernate:hibernate-core:3.6.7.Final',
-        'commons-collections:commons-collections:3.1'
-    ]
-
-    def filteredDependencies = compile.resolvedConfiguration.getFirstLevelModuleDependencies({ it.name == 'logback-classic' } as Spec)
-    assert filteredDependencies.collect { it.name } == [
-        'ch.qos.logback:logback-classic:0.9.30'
-    ]
-
-    def filteredFiles = compile.resolvedConfiguration.getFiles({ it.name == 'logback-classic' } as Spec)
-    assert filteredFiles.collect { it.name } == [
-        'logback-classic-0.9.30.jar',
-         'logback-core-0.9.30.jar',
-          'slf4j-api-1.6.2.jar'
-    ]
-
-    assert compile.collect { it.name } == [
-        'logback-classic-0.9.30.jar',
-        'hibernate-core-3.6.7.Final.jar',
-        'commons-collections-3.1.jar',
-        'logback-core-0.9.30.jar',
-        'slf4j-api-1.6.2.jar',
-        'antlr-2.7.6.jar',
-        'dom4j-1.6.1.jar',
-        'hibernate-commons-annotations-3.2.0.Final.jar',
-        'hibernate-jpa-2.0-api-1.0.1.Final.jar',
-        'jta-1.1.jar'
-    ]
-
-    assert compile.resolvedConfiguration.resolvedArtifacts.collect { it.file.name } == [
-        'logback-classic-0.9.30.jar',
-        'hibernate-core-3.6.7.Final.jar',
-        'logback-core-0.9.30.jar',
-        'slf4j-api-1.6.2.jar',
-        'antlr-2.7.6.jar',
-        'dom4j-1.6.1.jar',
-        'hibernate-commons-annotations-3.2.0.Final.jar',
-        'hibernate-jpa-2.0-api-1.0.1.Final.jar',
-        'jta-1.1.jar',
-        'commons-collections-3.1.jar'
-    ]
-}
-"""
-
-        expect:
-        succeeds "check"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy
deleted file mode 100644
index bed461e..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,228 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class MavenDynamicResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def "can resolve snapshot versions with version range"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    maven {
-        url "${mavenHttpRepo.uri}"
-    }
-}
-
-configurations { compile }
-
-dependencies {
-    compile group: "org.test", name: "projectA", version: "1.+"
-    compile group: "org.test", name: "projectB", version: "1.+"
-}
-
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'libs'
-}
-"""
-
-        when:
-        mavenHttpRepo.module("org.test", "projectA", "1.0").publish()
-        mavenHttpRepo.module("org.test", "projectA", "1.1-SNAPSHOT").publish()
-        def matchingA = mavenHttpRepo.module("org.test", "projectA", "1.1").publish()
-        mavenHttpRepo.module("org.test", "projectA", "2.0").publish()
-
-        mavenHttpRepo.module("org.test", "projectB", "1.1").publish()
-        def matchingB = mavenHttpRepo.module("org.test", "projectB", "1.2-SNAPSHOT").publish()
-        mavenHttpRepo.module("org.test", "projectB", "2.0").publish()
-
-        and:
-        mavenHttpRepo.getModuleMetaData("org.test", "projectA").expectGet()
-        matchingA.pom.expectGet()
-        matchingA.artifact.expectGet()
-
-        mavenHttpRepo.getModuleMetaData("org.test", "projectB").expectGet()
-        matchingB.metaData.expectGet()
-        matchingB.pom.expectGet()
-        matchingB.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.1.jar', 'projectB-1.2-SNAPSHOT.jar')
-        file('libs/projectA-1.1.jar').assertIsCopyOf(matchingA.artifactFile)
-        file('libs/projectB-1.2-SNAPSHOT.jar').assertIsCopyOf(matchingB.artifactFile)
-
-        when:
-        server.resetExpectations()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.1.jar', 'projectB-1.2-SNAPSHOT.jar')
-    }
-
-    def "can resolve dynamic version declared in pom as transitive dependency from HTTP Maven repository"() {
-        given:
-        server.start()
-
-        mavenHttpRepo.module('org.test', 'projectC', '1.1').publish()
-        def projectC = mavenHttpRepo.module('org.test', 'projectC', '1.5').publish()
-        mavenHttpRepo.module('org.test', 'projectC', '2.0').publish()
-        def projectB = mavenHttpRepo.module('org.test', 'projectB', '1.0').dependsOn("org.test", 'projectC', '[1.0, 2.0)').publish()
-        def projectA = mavenHttpRepo.module('org.test', 'projectA', '1.0').dependsOn('org.test', 'projectB', '1.0').publish()
-
-        buildFile << """
-    repositories {
-        maven { url '${mavenHttpRepo.uri}' }
-    }
-    configurations { compile }
-    dependencies {
-        compile 'org.test:projectA:1.0'
-    }
-
-    task retrieve(type: Sync) {
-        into 'libs'
-        from configurations.compile
-    }
-    """
-
-        when:
-        projectA.pom.expectGet()
-        projectA.getArtifact().expectGet()
-        projectB.pom.expectGet()
-        projectB.getArtifact().expectGet()
-        mavenHttpRepo.getModuleMetaData("org.test", "projectC").expectGet()
-        projectC.pom.expectGet()
-        projectC.getArtifact().expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar', 'projectC-1.5.jar')
-        def snapshot = file('libs/projectA-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
-    }
-
-    def "falls back to directory listing when maven-metadata.xml is missing"() {
-        given:
-        server.start()
-        mavenHttpRepo.module('org.test', 'projectA', '1.0').publish()
-        def projectA = mavenHttpRepo.module('org.test', 'projectA', '1.5').publish()
-
-        buildFile << """
-    repositories {
-        maven { url '${mavenHttpRepo.uri}' }
-    }
-    configurations { compile }
-    dependencies {
-        compile 'org.test:projectA:1.+'
-    }
-
-    task retrieve(type: Sync) {
-        into 'libs'
-        from configurations.compile
-    }
-    """
-
-        when:
-        mavenHttpRepo.getModuleMetaData("org.test", "projectA").expectGetMissing()
-        mavenHttpRepo.expectDirectoryListGet("org.test", "projectA")
-        projectA.pom.expectGet()
-        projectA.getArtifact().expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.5.jar')
-        def snapshot = file('libs/projectA-1.5.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs/projectA-1.5.jar').assertHasNotChangedSince(snapshot)
-    }
-
-    def "does not cache broken module information"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-        def projectA1 = repo1.module('group', 'projectA', '1.1').publish()
-        def projectA2 = repo2.module('group', 'projectA', '1.5').publish()
-
-        buildFile << """
-        repositories {
-            maven { url '${repo1.uri}' }
-            maven { url '${repo2.uri}' }
-        }
-        configurations { compile }
-        dependencies {
-            compile 'group:projectA:1.+'
-        }
-
-        task retrieve(type: Sync) {
-            into 'libs'
-            from configurations.compile
-        }
-        """
-
-        when:
-        repo1.getModuleMetaData("group", "projectA").expectGet()
-        projectA1.pom.expectGet()
-        projectA1.getArtifact().expectGet()
-
-        repo2.getModuleMetaData("group", "projectA")expectGet()
-        projectA2.pom.expectGetBroken()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.1.jar')
-
-        when:
-        server.resetExpectations()
-        repo2.getModuleMetaData("group", "projectA").expectGet()
-        projectA2.pom.expectGet()
-        projectA2.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.5.jar')
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy
deleted file mode 100644
index 1eb8d15..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,297 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.junit.Rule
-
-class MavenHttpRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    def "can resolve and cache dependencies from HTTP Maven repository"() {
-        given:
-        server.start()
-
-        def projectB = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
-        def projectA = mavenHttpRepo.module('group', 'projectA').dependsOn('group', 'projectB', '1.0').publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies {
-    compile 'group:projectA:1.0'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        projectA.pom.expectGet()
-        projectA.artifact.expectGet()
-        projectB.pom.expectGet()
-        projectB.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar')
-        def snapshot = file('libs/projectA-1.0.jar').snapshot()
-
-        and:
-        progressLogging.downloadProgressLogged(projectA.pom.uri)
-        progressLogging.downloadProgressLogged(projectA.artifact.uri)
-        progressLogging.downloadProgressLogged(projectB.pom.uri)
-        progressLogging.downloadProgressLogged(projectB.artifact.uri)
-
-        when:
-        server.resetExpectations()
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
-    }
-
-    def "can resolve and cache artifact-only dependencies from an HTTP Maven repository"() {
-        server.start()
-        given:
-        def module = mavenHttpRepo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies { compile 'group:projectA:1.2 at jar' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        // TODO: Should meta-data be fetched for an artifact-only dependency?
-        module.pom.expectGet()
-        module.artifact.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-        // No extra calls for cached dependencies
-
-        then:
-        succeeds('listJars')
-    }
-
-    def "can resolve and cache artifact-only dependencies with no pom from an HTTP Maven repository"() {
-        server.start()
-        given:
-        def module = mavenHttpRepo.module('group', 'projectA', '1.2')
-        module.publish()
-
-        and:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies { compile 'group:projectA:1.2 at jar' }
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
-}
-"""
-
-        when:
-        // TODO: Should meta-data be fetched for an artifact-only dependency?
-        module.pom.expectGetMissing()
-        module.artifact.expectHead()
-        module.artifact.expectGet()
-
-        then:
-        succeeds('listJars')
-
-        when:
-        server.resetExpectations()
-        // No extra calls for cached dependencies
-
-        then:
-        succeeds('listJars')
-    }
-
-    def "can resolve and cache dependencies from multiple HTTP Maven repositories"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-
-        buildFile << """
-repositories {
-    maven { url '${repo1.uri}' }
-    maven { url '${repo2.uri}' }
-}
-configurations {
-    compile {
-        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-    }
-}
-dependencies {
-    compile 'group:projectA:1.0', 'group:projectB:1.0'
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar']
-}
-"""
-
-        def projectA = repo1.module('group', 'projectA').publish()
-        def missingProjectB = repo1.module('group', 'projectB')
-        def projectB = repo2.module('group', 'projectB').publish()
-
-        when:
-        projectA.pom.expectGet()
-
-        // Looks for POM and JAR in repo1 before looking in repo2 (jar is an attempt to handle publication without module descriptor)
-        missingProjectB.pom.expectGetMissing()
-        missingProjectB.artifact.expectHeadMissing()
-        projectB.pom.expectGet()
-
-        projectA.artifact.expectGet()
-        projectB.artifact.expectGet()
-
-        then:
-        succeeds 'listJars'
-
-        when:
-        server.resetExpectations()
-        // No server requests when all jars cached
-
-        then:
-        succeeds 'listJars'
-    }
-
-    def "uses artifactsUrl to resolve artifacts"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-
-        buildFile << """
-repositories {
-    maven {
-        url '${repo1.uri}'
-        artifactUrls '${repo2.uri}'
-    }
-}
-configurations { compile }
-dependencies {
-    compile 'group:projectA:1.0', 'group:projectB:1.0'
-}
-task listJars << {
-    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar']
-}
-"""
-
-        def projectA = repo1.module('group', 'projectA').publish()
-        def projectB = repo1.module('group', 'projectB').publish()
-        def projectBArtifacts = repo2.module('group', 'projectB').publish()
-
-        when:
-        projectA.pom.expectGet()
-        projectB.pom.expectGet()
-
-        projectA.artifact.expectGet()
-        projectB.artifact.expectGetMissing()
-        projectBArtifacts.artifact.expectGet()
-
-        then:
-        succeeds 'listJars'
-    }
-
-    def "can resolve and cache dependencies from HTTP Maven repository with invalid settings.xml"() {
-        given:
-        server.start()
-
-        def projectB = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
-        def projectA = mavenHttpRepo.module('group', 'projectA').dependsOn('group', 'projectB', '1.0').publish()
-
-        buildFile << """
-    repositories {
-        maven { url '${mavenHttpRepo.uri}' }
-    }
-    configurations {
-        compile {
-            resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-        }
-    }
-    dependencies {
-        compile 'group:projectA:1.0'
-    }
-
-    task retrieve(type: Sync) {
-        into 'libs'
-        from configurations.compile
-    }
-    """
-
-        def m2Home = file("M2_REPO")
-        def settingsFile = m2Home.file("conf/settings.xml")
-        settingsFile << "invalid content... blabla"
-
-        when:
-        projectA.pom.expectGet()
-        projectA.artifact.expectGet()
-        projectB.pom.expectGet()
-        projectB.artifact.expectGet()
-
-        and:
-        executer.withEnvironmentVars(M2_HOME: m2Home.absolutePath)
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar')
-        def snapshot = file('libs/projectA-1.0.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
deleted file mode 100644
index dd37e57..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.resolution.JvmLibraryJavadocArtifact
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.resolve.JvmLibraryArtifactResolveTestFixture
-import org.gradle.test.fixtures.maven.MavenRepository
-import spock.lang.Unroll
-
-class MavenJvmLibraryArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
-    def repo = mavenHttpRepo
-    def fileRepo = mavenRepo
-    def module = repo.module("some.group", "some-artifact", "1.0")
-    def sourceArtifact = module.artifact(classifier: "sources")
-    def javadocArtifact = module.artifact(classifier: "javadoc")
-    JvmLibraryArtifactResolveTestFixture fixture
-
-    def setup() {
-        server.start()
-        initBuild(repo)
-
-        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
-
-        module.publish()
-    }
-
-    def initBuild(MavenRepository repo, String module = "some.group:some-artifact:1.0") {
-        buildFile.text = """
-repositories {
-    maven { url '$repo.uri' }
-}
-configurations { compile }
-dependencies {
-    compile "${module}"
-}
-"""
-    }
-
-    def "resolves and caches source artifacts"() {
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolve javadoc artifacts"() {
-        fixture.requestingTypes(JvmLibraryJavadocArtifact)
-                .expectJavadocArtifact("javadoc")
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        javadocArtifact.expectHead()
-        javadocArtifact.expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolves and caches all artifacts"() {
-        fixture.requestingTypes()
-                .expectSourceArtifact("sources")
-                .expectJavadocArtifact("javadoc")
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-        javadocArtifact.expectHead()
-        javadocArtifact.expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    @Unroll
-    def "fetches missing snapshot artifacts #condition"() {
-        initBuild(repo, "some.group:some-artifact:1.0-SNAPSHOT")
-        buildFile << """
-if (project.hasProperty('nocache')) {
-    configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-"""
-
-        def snapshotModule = repo.module("some.group", "some-artifact", "1.0-SNAPSHOT")
-        def snapshotSources = snapshotModule.artifact(classifier: "sources")
-        snapshotModule.publish()
-
-        fixture.withComponentVersion("some.group", "some-artifact", "1.0-SNAPSHOT")
-                .requestingTypes(JvmLibrarySourcesArtifact)
-                .prepare()
-
-        when:
-        snapshotModule.metaData.expectGet()
-        snapshotModule.pom.expectGet()
-        snapshotSources.expectHeadMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-
-        when:
-        snapshotModule.publishWithChangedContent()
-        fixture.clearExpectations()
-                .expectSourceArtifact("sources")
-                .createVerifyTask("verifyRefresh")
-
-        and:
-        server.resetExpectations()
-        snapshotModule.metaData.expectGet()
-        snapshotModule.pom.expectHead()
-        snapshotModule.pom.sha1.expectGet()
-        snapshotModule.pom.expectGet()
-        snapshotSources.expectHead()
-        snapshotSources.expectGet()
-
-        then:
-        executer.withArgument(execArg)
-        succeeds("verifyRefresh")
-
-        where:
-        condition | execArg
-        "with --refresh-dependencies" | "--refresh-dependencies"
-        "when snapshot pom changes" | "-Pnocache"
-    }
-
-    @Unroll
-    def "updates snapshot artifacts #condition"() {
-        initBuild(repo, "some.group:some-artifact:1.0-SNAPSHOT")
-        buildFile << """
-if (project.hasProperty('nocache')) {
-    configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-"""
-
-        def snapshotModule = repo.module("some.group", "some-artifact", "1.0-SNAPSHOT")
-        def snapshotSources = snapshotModule.artifact(classifier: "sources")
-        snapshotModule.publish()
-
-        fixture.withComponentVersion("some.group", "some-artifact", "1.0-SNAPSHOT")
-                .requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        when:
-        snapshotModule.metaData.expectGet()
-        snapshotModule.pom.expectGet()
-        snapshotSources.expectHead()
-        snapshotSources.expectGet()
-
-        then:
-        checkArtifactsResolvedAndCached()
-
-        when:
-        def snapshot = file("sources/some-artifact-1.0-SNAPSHOT-sources.jar").snapshot()
-        snapshotModule.publishWithChangedContent()
-
-        and:
-        server.resetExpectations()
-        snapshotModule.metaData.expectGet()
-        snapshotModule.pom.expectHead()
-        snapshotModule.pom.sha1.expectGet()
-        snapshotModule.pom.expectGet()
-        snapshotSources.expectHead()
-        // TODO:DAZ Extra head request should not be required
-        snapshotSources.expectHead()
-        snapshotSources.sha1.expectGet()
-        snapshotSources.expectGet()
-
-        then:
-        executer.withArgument(execArg)
-        succeeds("verify")
-        file("sources/some-artifact-1.0-SNAPSHOT-sources.jar").assertHasChangedSince(snapshot)
-
-        where:
-        condition | execArg
-        "with --refresh-dependencies" | "--refresh-dependencies"
-        "when snapshot pom changes" | "-Pnocache"
-    }
-
-    def "reports failure to resolve artifacts of non-existing component"() {
-        fixture.expectComponentNotFound().prepare()
-
-        when:
-        module.pom.expectGetMissing()
-        module.artifact.expectHeadMissing()
-
-        then:
-        succeeds("verify")
-    }
-
-    def "resolve and caches missing artifacts of existing component"() {
-        fixture.requestingTypes().prepare()
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectHeadMissing()
-        javadocArtifact.expectHeadMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "resolves and caches artifacts where some are present"() {
-        fixture.requestingTypes()
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-        javadocArtifact.expectHeadMissing()
-
-        then:
-        checkArtifactsResolvedAndCached()
-    }
-
-    def "reports on failure to list artifacts and recovers on subsequent resolve"() {
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectComponentResolutionFailure(new ArtifactResolveException("Could not determine artifacts for component 'some.group:some-artifact:1.0'"))
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        sourceArtifact.expectHeadBroken()
-
-        then:
-        succeeds("verify")
-
-        when:
-        fixture.clearExpectations()
-                .expectSourceArtifact("sources")
-                .createVerifyTask("verifyFixed")
-
-        and:
-        server.resetExpectations()
-        sourceArtifact.expectHead()
-        sourceArtifact.expectGet()
-
-        then:
-        succeeds("verifyFixed")
-    }
-
-    def "resolves and recovers from broken artifacts"() {
-        fixture.requestingTypes(JvmLibraryJavadocArtifact)
-                .expectJavadocArtifactFailure(new ArtifactResolveException("Could not download artifact 'some.group:some-artifact:1.0:some-artifact-javadoc.jar'"))
-                .prepare()
-
-        when:
-        module.pom.expectGet()
-        javadocArtifact.expectHead()
-        javadocArtifact.expectGetBroken()
-
-        then:
-        succeeds("verify")
-
-        when:
-        fixture.clearExpectations()
-                .expectJavadocArtifact("javadoc")
-                .createVerifyTask("verifyFixed")
-
-        and:
-        server.resetExpectations()
-        javadocArtifact.expectGet()
-
-        then:
-        succeeds("verifyFixed")
-    }
-
-    def "resolve and does not cache artifacts from local repository"() {
-        initBuild(fileRepo)
-
-        fixture.requestingTypes(JvmLibrarySourcesArtifact)
-                .expectSourceArtifact("sources")
-                .prepare()
-
-        when:
-        succeeds("verify")
-
-        and:
-        def snapshot = file("sources/some-artifact-1.0-sources.jar").snapshot()
-
-        and:
-        module.publishWithChangedContent()
-
-        then:
-        succeeds("verify")
-        file("sources/some-artifact-1.0-sources.jar").assertHasChangedSince(snapshot)
-    }
-
-    def checkArtifactsResolvedAndCached() {
-        assert succeeds("verify")
-        server.resetExpectations()
-        assert succeeds("verify")
-        true
-    }
-}
\ 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
deleted file mode 100644
index 25e0eac..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,98 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class MavenLatestResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def "latest selector works correctly when no snapshot 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.")
-    }
-
-    def "snapshot versions are considered integration status when using latest selector"() {
-        given:
-        server.start()
-        mavenHttpRepo.getModuleMetaData('group', 'projectA').allowGetOrHead()
-        mavenHttpRepo.module('group', 'projectA', '1.0').publish().allowAll()
-        mavenHttpRepo.module('group', 'projectA', '1.2-SNAPSHOT').publish().allowAll()
-
-        and:
-        buildFile << """
-configurations { compile }
-repositories { maven { url "${mavenHttpRepo.uri}" } }
-dependencies { compile 'group:projectA:latest.${status}' }
-task retrieve(type: Sync) {
-    from configurations.compile
-    into 'build'
-}
-"""
-
-        when:
-        run "retrieve"
-
-        then:
-        file("build").assertHasDescendants(latest)
-
-        where:
-        status        | latest
-        "release"     | "projectA-1.0.jar"
-        "milestone"   | "projectA-1.0.jar"
-        "integration" | "projectA-1.2-SNAPSHOT.jar"
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
deleted file mode 100644
index d2f9b0d..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.maven.MavenModule
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.hamcrest.Matchers.containsString
-
-class MavenLocalRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    @Rule SetSystemProperties sysProp = new SetSystemProperties()
-
-    def setup() {
-        requireOwnGradleUserHomeDir()
-        buildFile << """
-                repositories {
-                    mavenLocal()
-                }
-                configurations { compile }
-                dependencies {
-                    compile 'group:projectA:1.2'
-                }
-
-                task retrieve(type: Sync) {
-                    from configurations.compile
-                    into 'build'
-                }"""
-
-        using m2Installation
-    }
-
-    def "can resolve artifacts from local m2 when user settings.xml does not exist"() {
-        given:
-        def moduleA = m2Installation.mavenRepo().module('group', 'projectA', '1.2').publish()
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleA)
-
-    }
-
-    def "can resolve artifacts from local m2 with custom local repository defined in user settings.xml"() {
-        given:
-        def artifactRepo = mavenLocal("artifactrepo")
-        m2Installation.generateUserSettingsFile(artifactRepo)
-        def moduleA = artifactRepo.module('group', 'projectA', '1.2').publish()
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleA)
-    }
-
-    def "can resolve artifacts from local m2 with custom local repository defined in global settings.xml"() {
-        given:
-        def artifactRepo = mavenLocal("artifactrepo")
-        m2Installation.generateGlobalSettingsFile(artifactRepo)
-        def moduleA = artifactRepo.module('group', 'projectA', '1.2').publish()
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleA)
-    }
-
-    def "local repository in user settings take precedence over the local repository global settings"() {
-        given:
-        def globalRepo = mavenLocal("globalArtifactRepo")
-        def userRepo = mavenLocal("userArtifactRepo")
-        m2Installation.generateGlobalSettingsFile(globalRepo).generateUserSettingsFile(userRepo)
-        def moduleA = userRepo.module('group', 'projectA', '1.2').publish()
-        globalRepo.module('group', 'projectA', '1.2').publishWithChangedContent()
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleA)
-    }
-
-    def "fail with meaningful error message if settings.xml is invalid"() {
-        given:
-        m2Installation.userSettingsFile << "invalid content"
-
-        when:
-        runAndFail 'retrieve'
-
-        then:
-        failure.assertThatCause(containsString(String.format("Non-parseable settings %s:", m2Installation.userSettingsFile.absolutePath)));
-    }
-
-    def "mavenLocal is ignored if no local maven repository exists"() {
-        given:
-        def anotherRepo = maven("another-local-repo")
-        def moduleA = anotherRepo.module('group', 'projectA', '1.2').publishWithChangedContent()
-
-        and:
-        buildFile << """
-        repositories{
-            maven { url "${anotherRepo.uri}" }
-        }
-        """
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleA)
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal fails to resolve artifact if contains pom but not artifact"() {
-        given:
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2').publishPom()
-
-        when:
-        runAndFail 'retrieve'
-
-        then:
-        failure.assertHasCause('Could not find group:projectA:1.2')
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal skipped if contains pom but no artifact"() {
-        given:
-        def anotherRepo = maven("another-local-repo")
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2').publishPom()
-        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2').publish()
-
-        and:
-        buildFile << """
-        repositories{
-            maven { url "${anotherRepo.uri}" }
-        }
-        """
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleARemote)
-    }
-
-    def "mavenLocal includes jar for module with packaging 'pom'"() {
-        given:
-        m2Installation.mavenRepo().module('group', 'projectB', '1.2').publish()
-        def pomModule = m2Installation.mavenRepo().module('group', 'projectA', '1.2')
-        pomModule.packaging = 'pom'
-        pomModule.dependsOn('group', 'projectB', '1.2')
-        pomModule.artifact(type: "jar")
-        pomModule.publish()
-
-        when:
-        run 'retrieve'
-
-        then:
-        def buildDir = file('build')
-        buildDir.assertHasDescendants("projectA-1.2.jar","projectB-1.2.jar")
-    }
-
-    def "mavenLocal ignores missing jar for module with packaging 'pom'"() {
-        given:
-        m2Installation.mavenRepo().module('group', 'projectB', '1.2').publish()
-        def pomModule = m2Installation.mavenRepo().module('group', 'projectA', '1.2')
-        pomModule.packaging = 'pom'
-        pomModule.dependsOn('group', 'projectB', '1.2')
-        pomModule.publishPom()
-
-        when:
-        run 'retrieve'
-
-        then:
-        def buildDir = file('build')
-        buildDir.assertHasDescendants("projectB-1.2.jar")
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal fails to resolve snapshot artifact if contains pom but not artifact"() {
-        given:
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').publishPom()
-
-        and:
-        buildFile.text = """
-                repositories {
-                    mavenLocal()
-                }
-                configurations { compile }
-                dependencies {
-                    compile 'group:projectA:1.2-SNAPSHOT'
-                }
-
-                task retrieve(type: Sync) {
-                    from configurations.compile
-                    into 'build'
-                }"""
-
-        when:
-        runAndFail 'retrieve'
-
-        then:
-        failure.assertHasCause('Could not find group:projectA:1.2-SNAPSHOT')
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal fails to resolve non-unique snapshot artifact if contains pom but not artifact"() {
-        given:
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publishPom()
-
-        and:
-        buildFile.text = """
-                repositories {
-                    mavenLocal()
-                }
-                configurations { compile }
-                dependencies {
-                    compile 'group:projectA:1.2-SNAPSHOT'
-                }
-
-                task retrieve(type: Sync) {
-                    from configurations.compile
-                    into 'build'
-                }"""
-
-        when:
-        runAndFail 'retrieve'
-
-        then:
-        failure.assertHasCause('Could not find group:projectA:1.2-SNAPSHOT')
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal skipped if contains pom but no artifact for snapshot"() {
-        given:
-        def anotherRepo = maven("another-local-repo")
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').publishPom()
-        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2-SNAPSHOT').publish()
-
-        and:
-        buildFile.text = """
-                repositories {
-                    mavenLocal()
-                    maven { url "${anotherRepo.uri}" }
-                }
-                configurations { compile }
-                dependencies {
-                    compile 'group:projectA:1.2-SNAPSHOT'
-                }
-
-                task retrieve(type: Sync) {
-                    from configurations.compile
-                    into 'build'
-                }
-        """
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleARemote)
-    }
-
-    @Issue('GRADLE-2034')
-    def "mavenLocal skipped if contains pom but no artifact for non-unique snapshot"() {
-        given:
-        def anotherRepo = maven("another-local-repo")
-        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publishPom()
-        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publish()
-
-        and:
-        buildFile.text = """
-                repositories {
-                    mavenLocal()
-                    maven { url "${anotherRepo.uri}" }
-                }
-                configurations { compile }
-                dependencies {
-                    compile 'group:projectA:1.2-SNAPSHOT'
-                }
-
-                task retrieve(type: Sync) {
-                    from configurations.compile
-                    into 'build'
-                }
-        """
-
-        when:
-        run 'retrieve'
-
-        then:
-        hasArtifact(moduleARemote)
-    }
-
-    def hasArtifact(MavenModule module) {
-        def buildDir = file('build')
-        def artifactName = module.artifactFile.name
-        buildDir.assertHasDescendants(artifactName)
-        buildDir.file(artifactName).assertIsCopyOf(module.artifactFile)
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 2891987..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import spock.lang.Issue
-
-class MavenParentPomResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def "includes dependencies from parent pom"() {
-        given:
-        server.start()
-
-        def parentDep = mavenHttpRepo.module("org", "parent_dep", "1.2").publish()
-        def childDep = mavenHttpRepo.module("org", "child_dep", "1.7").publish()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0")
-        parent.hasPackaging('pom')
-        parent.dependsOn("org", "parent_dep", "1.2")
-        parent.publish()
-
-        def child = mavenHttpRepo.module("org", "child", "1.0")
-        child.dependsOn("org", "child_dep", "1.7")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        child.pom.expectGet()
-        parent.pom.expectGet()
-
-        child.artifact.expectGet()
-
-        parentDep.pom.expectGet()
-        parentDep.artifact.expectGet()
-        childDep.pom.expectGet()
-        childDep.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        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")
-    def "can handle parent pom with SNAPSHOT version"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0-SNAPSHOT")
-        parent.hasPackaging('pom')
-        parent.publish()
-
-        def child = mavenHttpRepo.module("org", "child", "1.0")
-        child.parent("org", "parent", "1.0-SNAPSHOT")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        child.pom.expectGet()
-        parent.metaData.expectGet()
-        parent.pom.expectGet()
-
-        child.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('child-1.0.jar')
-    }
-
-    def "looks for parent pom in different repository"() {
-        given:
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-
-        def parentInRepo1 = repo1.module("org", "parent")
-
-        def parentInRepo2 = repo2.module("org", "parent")
-        parentInRepo2.hasPackaging('pom')
-        parentInRepo2.publish()
-
-        def child = repo1.module("org", "child")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${repo1.uri}' }
-    maven { url '${repo2.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        child.pom.expectGet()
-        child.artifact.expectGet()
-
-        parentInRepo1.pom.expectGetMissing()
-        parentInRepo1.artifact.expectHeadMissing()
-
-        parentInRepo2.pom.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('child-1.0.jar')
-    }
-
-    def "fails with reasonable message if parent module is an ivy module"() {
-        given:
-        server.start()
-        def child = mavenHttpRepo.module("org", "child")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        def missingChild = ivyHttpRepo.module("org", "child")
-        def parent = ivyHttpRepo.module("org", "parent").publish()
-
-        buildFile << """
-repositories {
-    ivy { url '${ivyHttpRepo.uri}' }
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies { compile 'org:child:1.0' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        missingChild.ivy.expectGetMissing()
-        missingChild.jar.expectHeadMissing()
-        child.pom.expectGet()
-        parent.ivy.expectGet()
-
-        and:
-        fails 'retrieve'
-
-        then:
-        failure.assertHasCause "Could not resolve org:child:1.0."
-        failure.assertHasCause "Could not determine artifacts for component 'org:parent:1.0': Cannot locate artifacts of type MavenPomArtifact for 'org:parent:1.0' in repository 'ivy'"
-    }
-
-    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()
-
-        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')
-    }
-
-    @Issue("GRADLE-2861")
-    def "two modules that share common parent"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0")
-        parent.hasPackaging('pom')
-        parent.publish()
-
-        def child1 = mavenHttpRepo.module("org", "child1", "1.0")
-        child1.parent("org", "parent", "1.0")
-        child1.publish()
-        def child2 = mavenHttpRepo.module("org", "child2", "1.0")
-        child2.parent("org", "parent", "1.0")
-        child2.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org:child1:1.0'
-    compile 'org:child2:1.0'
-}
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        child1.pom.expectGet()
-        child2.pom.expectGet()
-        parent.pom.expectGet()
-
-        child1.artifact.expectGet()
-        child2.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('child1-1.0.jar', 'child2-1.0.jar')
-    }
-
-    @Issue("GRADLE-2861")
-    def "module that has a parent and grandparent module"() {
-        given:
-        server.start()
-
-        def grandParent = mavenHttpRepo.module("org", "grandparent", "1.0")
-        grandParent.hasPackaging('pom')
-        grandParent.publish()
-
-        def parent = mavenHttpRepo.module("org", "parent", "1.0")
-        parent.hasPackaging('pom')
-        parent.parent("org", "grandparent", "1.0")
-        parent.publish()
-
-        def child = mavenHttpRepo.module("org", "child", "1.0")
-        child.parent("org", "parent", "1.0")
-        child.publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations { compile }
-dependencies {
-    compile 'org:child:1.0'
-}
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        child.pom.expectGet()
-        parent.pom.expectGet()
-        grandParent.pom.expectGet()
-
-        child.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('child-1.0.jar')
-    }
-
-    def "parent pom parsing with custom properties for dependency coordinates"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module('group', 'parent', '1.0').publish()
-        parent.pomFile.text = parent.pomFile.text.replace("</project>", """
-<properties>
-    <some.group>my.group</some.group>
-    <some.artifact>myartifact</some.artifact>
-    <some.version>1.1</some.version>
-</properties>
-<dependencies>
-    <dependency>
-        <groupId>\${some.group}</groupId>
-        <artifactId>\${some.artifact}</artifactId>
-        <version>\${some.version}</version>
-    </dependency>
-</dependencies>
-</project>
-""")
-
-        def parentDepModule = mavenHttpRepo.module('my.group', 'myartifact', '1.1').publish()
-        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', 'myartifact-1.1.jar'] }
-        """
-
-        and:
-        parent.pom.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectGet()
-        parentDepModule.pom.expectGet()
-        parentDepModule.artifact.expectGet()
-
-        expect:
-        // have to run twice to trigger the failure, to parse the descriptor from the cache
-        succeeds ":libs"
-        succeeds ":libs"
-    }
-
-    def "dependency with same group ID and artifact ID defined in child and parent is used from child"() {
-        given:
-        server.start()
-
-        def parent = mavenHttpRepo.module('group', 'parent', '1.0').dependsOn('my.group', 'myartifact', '1.1').publish()
-        mavenHttpRepo.module('my.group', 'myartifact', '1.1').publish()
-        def depModule = mavenHttpRepo.module('my.group', 'myartifact', '1.3').publish()
-        def module = mavenHttpRepo.module('group', 'artifact', '1.0').parent('group', 'parent', '1.0').dependsOn('my.group', 'myartifact', '1.3').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', 'myartifact-1.3.jar'] }
-        """
-
-        and:
-        parent.pom.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectGet()
-        depModule.pom.expectGet()
-        depModule.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/MavenPomPackagingResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomPackagingResolveIntegrationTest.groovy
deleted file mode 100644
index 9d86a62..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomPackagingResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,378 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.maven.MavenHttpModule
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import spock.lang.FailsWith
-import spock.lang.Issue
-
-class MavenPomPackagingResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    MavenHttpRepository repo1
-    MavenHttpRepository repo2
-    MavenHttpModule projectARepo1
-    MavenHttpModule projectARepo2
-
-    public setup() {
-        server.start()
-        repo1 = mavenHttpRepo("repo1")
-        repo2 = mavenHttpRepo("repo2")
-        projectARepo1 = repo1.module('group', 'projectA')
-        projectARepo2 = repo2.module('group', 'projectA')
-    }
-
-    private void buildWithDependencies(def dependencies) {
-        buildFile << """
-repositories {
-    maven { url '${repo1.uri}' }
-    maven { url '${repo2.uri}' }
-}
-configurations { compile }
-dependencies {
-    $dependencies
-}
-task deleteDir(type: Delete) {
-    delete 'libs'
-}
-task retrieve(type: Copy, dependsOn: deleteDir) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-    }
-
-    def "includes jar artifact if present for pom with packaging of type 'pom'"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo2.hasPackaging("pom").publish()
-
-        and:
-        // First attempts to resolve in repo1
-        projectARepo1.pom.expectGetMissing()
-        projectARepo1.artifact.expectHeadMissing()
-
-        projectARepo2.pom.expectGet()
-        projectARepo2.artifact.expectHead()
-        projectARepo2.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0.jar')
-
-        when:
-        server.resetExpectations()
-        run 'retrieve'
-
-        then: // Uses cached artifacts
-        file('libs').assertHasDescendants('projectA-1.0.jar')
-    }
-
-    def "ignores missing jar artifact for pom with packaging of type 'pom'"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo1.hasPackaging("pom").publishPom()
-
-        and:
-        projectARepo1.pom.expectGet()
-        projectARepo1.artifact.expectHeadMissing()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertIsEmptyDir()
-
-        when:
-        server.resetExpectations()
-        run 'retrieve'
-
-        then: // Uses cached artifacts
-        file('libs').assertIsEmptyDir()
-    }
-
-    def "for a snapshot module with with packaging of type 'pom', will check for jar artifact that was previously missing on cache expiry"() {
-        when:
-        def snapshotA = repo1.module('group', 'projectA', '1.1-SNAPSHOT')
-        snapshotA.hasPackaging("pom").publish()
-
-        and:
-        buildWithDependencies("compile 'group:projectA:1.1-SNAPSHOT'")
-        buildFile << """
-if (project.hasProperty('skipCache')) {
-    configurations.compile.resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
-}
-"""
-
-        and:
-        snapshotA.metaData.expectGet()
-        snapshotA.pom.expectGet()
-        snapshotA.artifact.expectHeadMissing()
-
-        and:
-        run 'retrieve'
-
-        then:
-        skipped ':retrieve'
-
-        // Jar artifact presence is cached
-        when:
-        server.resetExpectations()
-
-        and:
-        run 'retrieve'
-
-        then:
-        skipped ':retrieve'
-
-        // New artifact is detected
-        when:
-        server.resetExpectations()
-        snapshotA.metaData.expectGet()
-        snapshotA.pom.expectHead()
-        snapshotA.artifact.expectHead()
-        snapshotA.artifact.expectGet()
-
-        and:
-        executer.withArgument('-PskipCache')
-        run 'retrieve'
-
-        then:
-        executed ':retrieve'
-        file('libs').assertHasDescendants('projectA-1.1-SNAPSHOT.jar')
-
-        // Jar artifact removal is detected
-        when:
-        server.resetExpectations()
-        snapshotA.metaData.expectGet()
-        snapshotA.pom.expectHead()
-        snapshotA.artifact.expectHeadMissing()
-
-        and:
-        executer.withArgument('-PskipCache')
-        run 'retrieve'
-
-        then:
-        skipped ':retrieve'
-    }
-
-    def "will use jar artifact for pom with packaging that maps to jar"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo1.hasPackaging(packaging).publish()
-
-        and:
-        projectARepo1.pom.expectGet()
-        projectARepo1.artifact.expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.jar')
-        file('libs/projectA-1.0.jar').assertIsCopyOf(projectARepo1.artifactFile)
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-
-        where:
-        packaging << ['', 'jar', 'eclipse-plugin', 'bundle']
-    }
-
-
-    @Issue('GRADLE-2188')
-    def "will use jar artifact for pom with packaging 'orbit'"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo1.hasPackaging('orbit').publish()
-
-        and:
-        projectARepo1.pom.expectGet()
-        projectARepo1.artifact(type: 'orbit').expectHeadMissing()
-        projectARepo1.artifact.expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.jar')
-        file('libs/projectA-1.0.jar').assertIsCopyOf(projectARepo1.artifactFile)
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-    }
-
-    @Issue('GRADLE-2188')
-    def "where 'module.custom' exists, will use it as main artifact for pom with packaging 'custom' and emit deprecation warning"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo1.hasPackaging("custom").hasType("custom").publish()
-
-        and:
-        projectARepo1.pom.expectGet()
-        projectARepo1.artifact.expectHead()
-        projectARepo1.artifact.expectGet()
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.custom')
-        file('libs/projectA-1.0.custom').assertIsCopyOf(projectARepo1.artifactFile)
-
-        and:
-        result.output.contains("Relying on packaging to define the extension of the main artifact has been deprecated")
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-    }
-
-    def "fails and reports type-based location if neither packaging-based or type-based artifact can be located"() {
-        when:
-        buildWithDependencies("compile 'group:projectA:1.0'")
-        projectARepo1.hasPackaging("custom").publishPom()
-
-        and:
-        projectARepo1.pom.expectGet()
-        projectARepo1.artifact(type: 'custom').expectHeadMissing()
-        projectARepo1.artifact(type: 'jar').expectGetMissing()
-
-        then:
-        fails 'retrieve'
-
-        and:
-        result.error.contains("Artifact 'group:projectA:1.0:projectA.jar' not found.")
-    }
-
-    def "will use non-jar dependency type to determine jar artifact location"() {
-        when:
-        buildWithDependencies("""
-compile('group:projectA:1.0') {
-    artifact {
-        name = 'projectA'
-        type = 'zip'
-    }
-}
-""")
-        projectARepo1.hasPackaging("custom").hasType("custom")
-        projectARepo1.artifact(type: 'zip')
-        projectARepo1.publish()
-
-        and:
-        projectARepo1.pom.expectGet()
-
-        // TODO:GRADLE-2188 This call should not be required, since "type='zip'" on the dependency alleviates the need to check for the packaging artifact
-        projectARepo1.artifact(type: 'custom').expectHeadMissing()
-        projectARepo1.artifact(type: 'zip').expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.zip')
-        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifact(type: 'zip').file)
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-    }
-
-    def "will use non-jar maven dependency type to determine artifact location"() {
-        when:
-        buildWithDependencies("""
-compile 'group:mavenProject:1.0'
-""")
-        def mavenProject = repo1.module('group', 'mavenProject', '1.0').hasPackaging('pom').dependsOn('group', 'projectA', '1.0', 'zip').publishPom()
-        projectARepo1.hasPackaging("custom")
-        projectARepo1.artifact(type: 'zip')
-        projectARepo1.publish()
-
-        and:
-        mavenProject.pom.expectGet()
-        mavenProject.artifact.expectHeadMissing()
-
-        projectARepo1.pom.expectGet()
-        // TODO:GRADLE-2188 This call should not be required, since "type='zip'" on the dependency alleviates the need to check for the packaging artifact
-        projectARepo1.artifact(type: 'custom').expectHeadMissing()
-        projectARepo1.artifact(type: 'zip').expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.zip')
-        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifact(type: 'zip').file)
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-    }
-
-    @FailsWith(value = AssertionError, reason = "Pending better fix for GRADLE-2188")
-    def "does not emit deprecation warning if dependency type is used to locate artifact, even if custom packaging matches file extension"() {
-        when:
-        buildWithDependencies("""
-compile('group:projectA:1.0') {
-    artifact {
-        name = 'projectA'
-        type = 'zip'
-    }
-}
-""")
-        projectARepo1.hasPackaging("zip").hasType("zip").publish()
-
-        and:
-        projectARepo1.pom.expectGet()
-        // TODO - should not need this head request
-        projectARepo1.artifact.expectHead()
-        projectARepo1.artifact.expectGet()
-
-        then:
-        succeeds 'retrieve'
-
-        and:
-        file('libs').assertHasDescendants('projectA-1.0.zip')
-        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifactFile)
-
-        and: "Stop the http server here to allow failure to be declared (otherwise occurs in tearDown) - remove this when the test is fixed"
-        server.stop()
-
-        // Check caching
-        when:
-        server.resetExpectations()
-        then:
-        succeeds 'retrieve'
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
deleted file mode 100644
index 957be86..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,65 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-
-class MavenPomResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    def "follows relocation to another group"() {
-        server.start()
-
-        given:
-        def original = mavenHttpRepo.module("groupA", "projectA", "1.2").publishPom()
-        original.pomFile.text = """
-<project>
-    <groupId>groupA</groupId>
-    <artifactId>projectA</artifactId>
-    <version>1.2</version>
-    <distributionManagement>
-        <relocation>
-            <groupId>newGroupA</groupId>
-        </relocation>
-    </distributionManagement>
-</project>
-"""
-
-        def newModule = mavenHttpRepo.module("newGroupA", "projectA", "1.2").publish()
-        newModule.publish()
-
-        and:
-        buildFile << """
-repositories { maven { url '${mavenHttpRepo.uri}' } }
-configurations { compile }
-dependencies { compile 'groupA:projectA:1.2' }
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        and:
-        original.pom.expectGet()
-        newModule.pom.expectGet()
-        newModule.artifact.expectGet()
-
-        when:
-        run "retrieve"
-
-        then:
-        file("libs").assertHasDescendants("projectA-1.2.jar")
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy b/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy
deleted file mode 100644
index 358a14c..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.resolve.ResolveTestFixture
-
-class MavenProfileResolveIntegrationTest extends AbstractDependencyResolutionTest {
-    ResolveTestFixture resolve
-
-    def setup() {
-        settingsFile << "rootProject.name = 'test' "
-        resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-        server.start()
-    }
-
-    def "uses properties from active profile to resolve dependency"() {
-        given:
-        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
-        requestedModule.pomFile.text = """
-<project>
-    <groupId>groupA</groupId>
-    <artifactId>artifactA</artifactId>
-    <version>1.2</version>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>groupB</groupId.prop>
-                <artifactId.prop>artifactB</artifactId.prop>
-                <version.prop>1.4</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
-
-        and:
-        buildFile << """
-repositories { maven { url '${mavenHttpRepo.uri}' } }
-configurations { compile }
-dependencies { compile 'groupA:artifactA:1.2' }
-"""
-
-        and:
-        requestedModule.pom.expectGet()
-        requestedModule.artifact.expectGet()
-        transitiveModule.pom.expectGet()
-        transitiveModule.artifact.expectGet()
-
-        when:
-        run "checkDeps"
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:unspecified") {
-                module("groupA:artifactA:1.2") {
-                    module("groupB:artifactB:1.4")
-                }
-            }
-        }
-    }
-
-    def "uses dependency management defaults from active profile to resolve dependency"() {
-        given:
-        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
-        requestedModule.pomFile.text = """
-<project>
-    <groupId>groupA</groupId>
-    <artifactId>artifactA</artifactId>
-    <version>1.2</version>
-    <dependencies>
-        <dependency>
-            <groupId>groupB</groupId>
-            <artifactId>artifactB</artifactId>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>groupB</groupId>
-                        <artifactId>artifactB</artifactId>
-                        <version>1.4</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
-
-        and:
-        buildFile << """
-repositories { maven { url '${mavenHttpRepo.uri}' } }
-configurations { compile }
-dependencies { compile 'groupA:artifactA:1.2' }
-"""
-
-        and:
-        requestedModule.pom.expectGet()
-        requestedModule.artifact.expectGet()
-        transitiveModule.pom.expectGet()
-        transitiveModule.artifact.expectGet()
-
-        when:
-        run "checkDeps"
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:unspecified") {
-                module("groupA:artifactA:1.2") {
-                    module("groupB:artifactB:1.4")
-                }
-            }
-        }
-    }
-
-    def "resolves dependency from active profile"() {
-        given:
-        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
-        requestedModule.pomFile.text = """
-<project>
-    <groupId>groupA</groupId>
-    <artifactId>artifactA</artifactId>
-    <version>1.2</version>
-    <dependencies>
-        <dependency>
-            <groupId>groupB</groupId>
-            <artifactId>artifactB</artifactId>
-            <version>1.3</version>
-        </dependency>
-        <dependency>
-            <groupId>groupB</groupId>
-            <artifactId>artifactB</artifactId>
-            <version>1.7</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>groupB</groupId>
-                    <artifactId>artifactB</artifactId>
-                    <version>1.4</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
-
-        and:
-        buildFile << """
-repositories { maven { url '${mavenHttpRepo.uri}' } }
-configurations { compile }
-dependencies { compile 'groupA:artifactA:1.2' }
-"""
-
-        and:
-        requestedModule.pom.expectGet()
-        requestedModule.artifact.expectGet()
-        transitiveModule.pom.expectGet()
-        transitiveModule.artifact.expectGet()
-
-        when:
-        run "checkDeps"
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:unspecified") {
-                module("groupA:artifactA:1.2") {
-                    module("groupB:artifactB:1.4")
-                }
-            }
-        }
-    }
-}
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
deleted file mode 100644
index 83cb4ec..0000000
--- a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,679 +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.integtests.resolve.maven
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.test.fixtures.maven.MavenHttpModule
-import spock.lang.Ignore
-import spock.lang.Issue
-
-class MavenSnapshotResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def "can find and cache snapshots in multiple Maven HTTP repositories"() {
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-
-        given:
-        buildFile << """
-repositories {
-    maven { url "${repo1.uri}" }
-    maven { url "${repo2.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT"
-    compile "org.gradle.integtests.resolve:projectB:1.0-SNAPSHOT"
-    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        and: "snapshot modules are published"
-        def repo1ProjectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT").publish()
-        def repo1ProjectB = repo1.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT")
-        def repo2ProjectB = repo2.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
-        def repo1NonUnique = repo1.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots()
-        def repo2NonUnique = repo2.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
-
-        when: "Server provides projectA from repo1"
-        expectModuleServed(repo1ProjectA)
-
-        and: "Server provides projectB from repo2"
-        expectModuleMissing(repo1ProjectB)
-        expectModuleServed(repo2ProjectB)
-
-        and: "Server provides nonunique snapshot from repo2"
-        expectModuleMissing(repo1NonUnique)
-        expectModuleServed(repo2NonUnique)
-
-        and: "We resolve dependencies"
-        run 'retrieve'
-
-        then: "Snapshots are downloaded"
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
-        def snapshotA = file('libs/projectA-1.0-SNAPSHOT.jar').snapshot()
-        def snapshotNonUnique = file('libs/nonunique-1.0-SNAPSHOT.jar').snapshot()
-
-        when: "We resolve with snapshots cached: no server requests"
-        server.resetExpectations()
-        def result = run('retrieve')
-
-        then: "Everything is up to date"
-        result.assertTaskSkipped(':retrieve')
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotA);
-        file('libs/nonunique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotNonUnique);
-    }
-
-    def "can find and cache snapshots in Maven HTTP repository with additional artifact urls"() {
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-        def repo2 = mavenHttpRepo("repo2")
-
-        given:
-        buildFile << """
-repositories {
-    maven {
-        url "${repo1.uri}"
-        artifactUrls "${repo2.uri}"
-    }
-}
-
-configurations { compile }
-
-dependencies {
-    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT"
-    compile "org.gradle.integtests.resolve:projectB:1.0-SNAPSHOT"
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        and: "snapshot modules are published"
-        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT").publish()
-        def repo1ProjectB = repo1.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
-        def repo2ProjectB = repo2.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
-
-        when: "Server provides projectA from repo1"
-        expectModuleServed(projectA)
-
-        and: "Server provides projectB with artifact in repo2"
-        repo1ProjectB.metaData.expectGet()
-        repo1ProjectB.pom.expectGet()
-        repo1ProjectB.artifact.expectGetMissing()
-        repo2ProjectB.artifact.expectGet()
-
-        and: "We resolve dependencies"
-        run 'retrieve'
-
-        then: "Snapshots are downloaded"
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0-SNAPSHOT.jar')
-        def snapshotA = file('libs/projectA-1.0-SNAPSHOT.jar').snapshot()
-        def snapshotB = file('libs/projectB-1.0-SNAPSHOT.jar').snapshot()
-
-        when: "We resolve with snapshots cached: no server requests"
-        server.resetExpectations()
-        def result = run('retrieve')
-
-        then: "Everything is up to date"
-        result.assertTaskSkipped(':retrieve')
-        file('libs/projectA-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotA);
-        file('libs/projectB-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotB);
-    }
-
-    def "can find and cache snapshots in Maven HTTP repository with artifact classifier"() {
-        server.start()
-        def repo1 = mavenHttpRepo("repo1")
-
-        given:
-        buildFile << """
-repositories {
-    maven {
-        url "${repo1.uri}"
-    }
-}
-
-configurations { compile }
-
-dependencies {
-    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT:tests"
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        and:
-        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT")
-        def classifierArtifact = projectA.artifact(classifier: "tests")
-        projectA.publish()
-
-        when:
-        projectA.metaData.expectGet()
-        projectA.pom.expectGet()
-        classifierArtifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT-tests.jar')
-        def snapshotA = file('libs/projectA-1.0-SNAPSHOT-tests.jar').snapshot()
-
-        when:
-        server.resetExpectations()
-        run 'retrieve'
-
-        then: "Everything is up to date"
-        skipped ':retrieve'
-        file('libs/projectA-1.0-SNAPSHOT-tests.jar').assertHasNotChangedSince(snapshotA);
-    }
-
-    def "will detect changed snapshot artifacts when pom has not changed"() {
-        server.start()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-
-configurations { compile }
-configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-
-dependencies { 
-    compile "org.gradle.integtests.resolve:unique:1.0-SNAPSHOT"
-    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when: "snapshot modules are published"
-        def uniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "unique", "1.0-SNAPSHOT").publish()
-        def nonUniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
-
-        and: "Server handles requests"
-        expectModuleServed(uniqueVersionModule)
-        expectModuleServed(nonUniqueVersionModule)
-
-        and: "We resolve dependencies"
-        run 'retrieve'
-
-        then: "Snapshots are downloaded"
-        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
-        def uniqueJarSnapshot = file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).snapshot()
-        def nonUniqueJarSnapshot = file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).snapshot()
-        server.resetExpectations()
-
-        when: "Change the snapshot artifacts directly: do not change the pom"
-        uniqueVersionModule.artifactFile << 'more content'
-        uniqueVersionModule.backingModule.sha1File(uniqueVersionModule.artifactFile)
-        nonUniqueVersionModule.artifactFile << 'more content'
-        nonUniqueVersionModule.backingModule.sha1File(nonUniqueVersionModule.artifactFile)
-
-        and: "No server requests"
-        expectChangedArtifactServed(uniqueVersionModule)
-        expectChangedArtifactServed(nonUniqueVersionModule)
-
-        and: "Resolve dependencies again"
-        run 'retrieve'
-
-        then:
-        file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).assertHasChangedSince(uniqueJarSnapshot)
-        file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).assertHasChangedSince(nonUniqueJarSnapshot)
-    }
-
-    def "uses cached snapshots from a Maven HTTP repository until the snapshot timeout is reached"() {
-        server.start()
-
-        given:
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-if (project.hasProperty('noTimeout')) {
-    configurations.all {
-        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-    }
-}
-
-dependencies {
-    compile "org.gradle.integtests.resolve:unique:1.0-SNAPSHOT"
-    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when: "snapshot modules are published"
-        def uniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "unique", "1.0-SNAPSHOT").publish()
-        def nonUniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
-
-        and: "Server handles requests"
-        expectModuleServed(uniqueVersionModule)
-        expectModuleServed(nonUniqueVersionModule)
-
-        and: "We resolve dependencies"
-        run 'retrieve'
-
-        then: "Snapshots are downloaded"
-        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
-        def uniqueJarSnapshot = file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).snapshot()
-        def nonUniqueJarSnapshot = file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).snapshot()
-
-        when: "Republish the snapshots"
-        uniqueVersionModule.publishWithChangedContent()
-        nonUniqueVersionModule.publishWithChangedContent()
-
-        and: "No server requests"
-        server.resetExpectations()
-
-        and: "Resolve dependencies again, with cached versions"
-        run 'retrieve'
-
-        then:
-        file('libs/unique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(uniqueJarSnapshot)
-        file('libs/nonunique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(nonUniqueJarSnapshot)
-
-        when: "Server handles requests"
-        expectChangedModuleServed(uniqueVersionModule)
-        expectChangedModuleServed(nonUniqueVersionModule)
-
-        and: "Resolve dependencies with cache expired"
-        executer.withArguments("-PnoTimeout")
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
-        file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).assertHasChangedSince(uniqueJarSnapshot)
-        file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).assertHasChangedSince(nonUniqueJarSnapshot);
-    }
-
-    def "does not download snapshot artifacts after expiry when snapshot has not changed"() {
-        server.start()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenHttpRepo.uri}" }
-}
-
-configurations { compile }
-
-configurations.all {
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-
-dependencies {
-    compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
-}
-
-task retrieve(type: Sync) {
-    into 'build'
-    from configurations.compile
-}
-"""
-
-        when: "Publish the first snapshot"
-        def module = mavenHttpRepo.module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").publish()
-
-        and: "Server handles requests"
-        expectModuleServed(module)
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
-        def snapshot = file('build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifactFile).snapshot()
-
-        when: "Server handles requests"
-        server.resetExpectations()
-        expectChangedProbe(module)
-
-        // Retrieve again with zero timeout should check for updated snapshot
-        and:
-        def result = run 'retrieve'
-
-        then:
-        result.assertTaskSkipped(':retrieve')
-        file('build/testproject-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshot);
-    }
-
-    def "does not download snapshot artifacts more than once per build"() {
-        server.start()
-        given:
-        def module = mavenHttpRepo.module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").publish()
-
-        and:
-        settingsFile << "include 'a', 'b'"
-        buildFile << """
-allprojects {
-    repositories {
-        maven { url "${mavenHttpRepo.uri}" }
-    }
-
-    configurations { compile }
-
-    configurations.all {
-        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-    }
-
-    dependencies {
-        compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
-    }
-
-    task retrieve(type: Sync) {
-        into 'build'
-        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)
-
-        then:
-        run 'retrieve'
-
-        and:
-        file('build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
-        file('a/build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
-        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:
-        def module = mavenHttpRepo("/repo", maven("repo1")).module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
-        def module2 = mavenHttpRepo("/repo", maven("repo2")).module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
-        module2.pomFile << '    ' // ensure it's a different length to the first one
-        module2.backingModule.sha1File(module2.pomFile)
-        module2.artifactFile << module2.artifactFile.bytes // ensure it's a different length to the first one
-        module2.backingModule.sha1File(module2.artifactFile)
-        and:
-        settingsFile << "include 'first', 'second'"
-        buildFile << """
-def fileLocks = [:]
-subprojects {
-    repositories {
-        maven { url "http://localhost:${server.port}/repo" }
-    }
-
-    configurations { compile }
-
-    configurations.all {
-        resolutionStrategy.resolutionRules.eachArtifact({ artifact ->
-            artifact.refresh()
-        } as Action)
-    }
-
-    dependencies {
-        compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
-    }
-
-    task lock << {
-        configurations.compile.each { file ->
-            println "locking " + file
-            def lockFile = new RandomAccessFile(file.canonicalPath, 'r')
-            fileLocks[file] = lockFile
-        }
-    }
-
-    task retrieve(type: Sync) {
-        into 'build'
-        from configurations.compile
-    }
-    retrieve.dependsOn 'lock'
-}
-project('second') {
-    lock.dependsOn ':first:lock'
-    retrieve.dependsOn ':first:retrieve'
-
-    task cleanup << {
-        fileLocks.each { key, value ->
-            println "unlocking " + key
-            value.close()
-        }
-    }
-    cleanup.dependsOn 'retrieve'
-}
-"""
-        when: "Module is requested once"
-        module.metaData.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectGet()
-
-        module2.artifact.expectHead()
-        module2.artifact.sha1.expectGet()
-        module2.artifact.expectGet()
-
-        then:
-        run 'cleanup'
-
-        and:
-        file('first/build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifactFile)
-        file('second/build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module2.artifactFile)
-    }
-
-    def "avoid redownload unchanged artifact when no checksum available"() {
-        server.start()
-
-        given:
-        buildFile << """
-            repositories {
-                maven { url "${mavenHttpRepo.uri}" }
-            }
-
-            configurations { compile }
-
-            configurations.all {
-                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-            }
-
-            dependencies {
-                compile group: "group", name: "projectA", version: "1.1-SNAPSHOT"
-            }
-
-            task retrieve(type: Copy) {
-                into 'build'
-                from configurations.compile
-            }
-        """
-
-        and:
-        def module = mavenHttpRepo.module("group", "projectA", "1.1-SNAPSHOT").withNonUniqueSnapshots().publish()
-        // Set the last modified to something that's not going to be anything “else”.
-        // There are lots of dates floating around in a resolution and we want to make
-        // sure we use this.
-        module.artifactFile.setLastModified(2000)
-        module.pom.file.setLastModified(6000)
-        def artifact = module.artifact
-
-        when:
-        expectModuleServed(module)
-
-        run "retrieve"
-
-        then:
-        def downloadedJarFile = file("build/projectA-1.1-SNAPSHOT.jar")
-        downloadedJarFile.assertIsCopyOf(module.artifactFile)
-        def initialDownloadJarFileSnapshot = downloadedJarFile.snapshot()
-
-        when:
-        server.resetExpectations()
-        expectChangedProbe(module)
-
-        run "retrieve"
-
-        then:
-        downloadedJarFile.assertHasNotChangedSince(initialDownloadJarFileSnapshot)
-
-        when:
-        module.publishWithChangedContent()
-        server.resetExpectations()
-        module.metaData.expectGet()
-        module.pom.expectHead()
-        module.pom.sha1.expectGetMissing()
-        module.pom.expectGet()
-        artifact.expectHead()
-        artifact.sha1.expectGetMissing()
-        artifact.expectGet()
-
-        run "retrieve"
-
-        then:
-        downloadedJarFile.assertHasChangedSince(initialDownloadJarFileSnapshot)
-        downloadedJarFile.assertIsCopyOf(module.artifactFile)
-    }
-
-    @Issue("GRADLE-3017")
-    def "resolves changed metadata in snapshot dependency"() {
-        given:
-        server.start()
-
-        def projectB1 = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
-        def projectB2 = mavenHttpRepo.module('group', 'projectB', '2.0').publish()
-        def projectA = mavenHttpRepo.module('group', 'projectA', "1.0-SNAPSHOT").dependsOn('group', 'projectB', '1.0').publish()
-
-        buildFile << """
-repositories {
-    maven { url '${mavenHttpRepo.uri}' }
-}
-configurations {
-    compile {
-        if (project.hasProperty('bypassCache')) {
-            resolutionStrategy.cacheChangingModulesFor(0, "seconds")
-        }
-    }
-}
-dependencies {
-    compile 'group:projectA:1.0-SNAPSHOT'
-}
-
-task retrieve(type: Sync) {
-    into 'libs'
-    from configurations.compile
-}
-"""
-
-        when:
-        projectA.pom.expectGet()
-        projectA.metaData.expectGet()
-        projectA.artifact.expectGet()
-        projectB1.pom.expectGet()
-        projectB1.artifact.expectGet()
-
-        and:
-        run 'retrieve'
-
-        then:
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0.jar')
-
-        when: "Project A is published with changed dependencies"
-        server.resetExpectations()
-        projectA = projectA.dependsOn('group', 'projectB', '2.0').publish()
-
-        and: "Resolve with caching"
-        run 'retrieve'
-
-        then: "Gets original ProjectA metadata from cache"
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0.jar')
-
-        when: "Resolve without cache"
-        projectA.metaData.expectGet()
-        projectA.pom.expectHead()
-        projectA.pom.sha1.expectGet()
-        projectA.pom.expectGet()
-        projectA.artifact.expectHead()
-        projectB2.pom.expectGet()
-        projectB2.artifact.expectGet()
-
-        and:
-        executer.withArguments("-PbypassCache")
-        run 'retrieve'
-
-        then: "Gets updated metadata"
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-2.0.jar')
-
-        when: "Resolve with caching"
-        server.resetExpectations()
-        run 'retrieve'
-
-        then: "Gets updated metadata from cache"
-        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-2.0.jar')
-    }
-
-    private expectModuleServed(MavenHttpModule module) {
-        module.metaData.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectGet()
-    }
-
-    private expectChangedModuleServed(MavenHttpModule module) {
-        module.metaData.expectGet()
-        module.pom.expectHead()
-        module.pom.sha1.expectGet()
-        module.pom.expectGet()
-        module.artifact.expectHead()
-        module.artifact.sha1.expectGet()
-        module.artifact.expectGet()
-    }
-
-    private expectChangedArtifactServed(MavenHttpModule module) {
-        module.metaData.expectGet()
-        module.pom.expectHead()
-        def artifact = module.artifact
-        artifact.expectHead()
-        artifact.sha1.expectGet()
-        artifact.expectGet()
-    }
-
-    private expectChangedProbe(MavenHttpModule module) {
-        module.metaData.expectGet()
-        module.pom.expectHead()
-        module.artifact.expectHead()
-    }
-
-    private expectModuleMissing(MavenHttpModule module) {
-        module.metaData.expectGetMissing()
-        module.pom.expectGetMissing()
-        module.artifact.expectHeadMissing()
-    }
-}
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
deleted file mode 100644
index 6313ce9..0000000
--- a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-configurations {
-    compile
-    runtime { extendsFrom compile }
-}
-dependencies {
-    repositories {
-        ivy {
-            artifactPattern projectDir.absolutePath + '/[artifact]-[revision].jar'
-            ivyPattern projectDir.absolutePath + '/[module]-[revision]-ivy.xml'
-            ivyPattern projectDir.absolutePath + '/[module]-[revision]-ivy.xml'
-        }
-    }
-    compile group: 'test', name: 'projectA', version: '1.2', configuration: 'api'
-    runtime group: 'test', name: 'projectA', version: '1.2'
-    runtime group: 'test', name: 'projectB', version: '1.5', configuration: 'extraRuntime'
-}
-
-file("projectA-1.2.jar").text = ''
-file("projectB-1.5.jar").text = ''
-file("projectB-api-1.5.jar").text = ''
-file("projectB-extraRuntime-1.5.jar").text = ''
-
-defaultTasks 'listJars'
-
-task listJars << {
-    def compile = configurations.compile
-
-    Set jars = compile.collect { it.name } as Set
-    assert ['projectA-1.2.jar', 'projectB-api-1.5.jar'] as Set == jars
-
-    def projectA = compile.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
-    def root = (projectA.parents as List)[0]
-    def artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-    assert ['projectA', 'projectB-api'] as Set == artifacts
-
-    def projectB = projectA.children.find { it.moduleName == 'projectB' && it.configuration == 'compileTime' }
-    artifacts = projectB.getAllArtifacts(projectA).collect { it.name } as Set
-    assert ['projectB-api'] as Set == artifacts
-
-    def runtime = configurations.runtime
-
-    jars = runtime.collect { it.name } as Set
-    assert ['projectA-1.2.jar', 'projectB-api-1.5.jar', 'projectB-1.5.jar', 'projectB-extraRuntime-1.5.jar'] as Set == jars
-
-    projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
-    root = (projectA.parents as List)[0]
-    artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-    assert ['projectA', 'projectB-api'] as Set == artifacts
-
-    projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'default' }
-    root = (projectA.parents as List)[0]
-    artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-    assert ['projectA', 'projectB'] as Set == artifacts
-
-    projectB = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectB' && it.configuration == 'extraRuntime' }
-    artifacts = projectB.getAllArtifacts(root).collect { it.name } as Set
-    assert ['projectB', 'projectB-extraRuntime'] as Set == artifacts
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle b/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle
deleted file mode 100644
index 12f2baa..0000000
--- a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/reportsUnknownDependencyError/projectWithUnknownDependency.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-configurations {
-    compile
-}
-dependencies {
-    compile 'test:unknownProjectA:1.2'
-    compile 'test:unknownProjectB:2.1.5'
-}
-
-defaultTasks 'listJars'
-
-task listJars << {
-    configurations.compile.resolve()
-    throw new RuntimeException("Should not reach here")
-}
\ No newline at end of file
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
deleted file mode 100644
index 929d327..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts;
-
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-
-import java.util.List;
-
-public interface ArtifactDependencyResolver {
-    void resolve(ConfigurationInternal configuration,
-                 List<? extends ResolutionAwareRepository> repositories,
-                 ModuleMetadataProcessor metadataProcessor,
-                 ResolverResults results) 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
deleted file mode 100644
index e5ddfb1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
+++ /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.artifacts;
-
-import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactIdentifier;
-
-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(DefaultModuleVersionArtifactIdentifier id) {
-        this(newId(id.getComponentIdentifier()), id.getName().getName(), id.getName().getType(), id.getName().getExtension(), id.getName().getClassifier());
-    }
-
-    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/DefaultDependencyManagementServices.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
deleted file mode 100644
index ff2b747..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
+++ /dev/null
@@ -1,188 +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.PublishArtifact;
-import org.gradle.api.artifacts.dsl.ArtifactHandler;
-import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-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.ArtifactResolutionQueryFactory;
-import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.PublishLocalComponentFactory;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
-import org.gradle.api.internal.artifacts.repositories.DefaultBaseRepositoryFactory;
-import org.gradle.api.internal.artifacts.repositories.legacy.LegacyDependencyResolverRepositoryFactory;
-import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
-import org.gradle.api.internal.artifacts.resolution.DefaultArtifactResolutionQueryFactory;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistration;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.listener.ListenerManager;
-
-public class DefaultDependencyManagementServices implements DependencyManagementServices {
-
-    private final ServiceRegistry parent;
-
-    public DefaultDependencyManagementServices(ServiceRegistry parent) {
-        this.parent = parent;
-    }
-
-    public DependencyResolutionServices create(FileResolver fileResolver, DependencyMetaDataProvider dependencyMetaDataProvider, ProjectFinder projectFinder, DomainObjectContext domainObjectContext) {
-        DefaultServiceRegistry services = new DefaultServiceRegistry(parent);
-        services.add(FileResolver.class, fileResolver);
-        services.add(DependencyMetaDataProvider.class, dependencyMetaDataProvider);
-        services.add(ProjectFinder.class, projectFinder);
-        services.add(DomainObjectContext.class, domainObjectContext);
-        services.addProvider(new DependencyResolutionScopeServices());
-        return services.get(DependencyResolutionServices.class);
-    }
-
-    public void addDslServices(ServiceRegistration registration) {
-        registration.addProvider(new DependencyResolutionScopeServices());
-    }
-
-    private static class DependencyResolutionScopeServices {
-        BaseRepositoryFactory createBaseRepositoryFactory(LocalMavenRepositoryLocator localMavenRepositoryLocator, Instantiator instantiator, FileResolver fileResolver,
-                                                          RepositoryTransportFactory repositoryTransportFactory, LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                                          LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory,
-                                                          ResolverStrategy resolverStrategy) {
-            return new DefaultBaseRepositoryFactory(
-                    localMavenRepositoryLocator,
-                    fileResolver,
-                    instantiator,
-                    repositoryTransportFactory,
-                    locallyAvailableResourceFinder,
-                    legacyDependencyResolverRepositoryFactory,
-                    resolverStrategy
-            );
-        }
-
-        RepositoryHandler createRepositoryHandler(Instantiator instantiator, BaseRepositoryFactory baseRepositoryFactory) {
-            return instantiator.newInstance(DefaultRepositoryHandler.class, baseRepositoryFactory, instantiator);
-        }
-
-        ConfigurationContainerInternal createConfigurationContainer(Instantiator instantiator, ConfigurationResolver configurationResolver, DomainObjectContext domainObjectContext,
-                                                                    ListenerManager listenerManager, DependencyMetaDataProvider metaDataProvider) {
-            return instantiator.newInstance(DefaultConfigurationContainer.class,
-                    configurationResolver,
-                    instantiator,
-                    domainObjectContext,
-                    listenerManager,
-                    metaDataProvider);
-        }
-
-        DependencyHandler createDependencyHandler(Instantiator instantiator, ConfigurationContainerInternal configurationContainer, DependencyFactory dependencyFactory,
-                                                  ProjectFinder projectFinder, ComponentMetadataHandler componentMetadataHandler, ArtifactResolutionQueryFactory resolutionQueryFactory) {
-            return instantiator.newInstance(DefaultDependencyHandler.class,
-                    configurationContainer,
-                    dependencyFactory,
-                    projectFinder,
-                    componentMetadataHandler,
-                    resolutionQueryFactory);
-        }
-
-        DefaultComponentMetadataHandler createComponentMetadataHandler(Instantiator instantiator) {
-            return instantiator.newInstance(DefaultComponentMetadataHandler.class, instantiator);
-        }
-
-        ArtifactHandler createArtifactHandler(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, ConfigurationContainerInternal configurationContainer) {
-            NotationParser<Object, PublishArtifact> publishArtifactNotationParser = new PublishArtifactNotationParserFactory(instantiator, dependencyMetaDataProvider).create();
-            return new DefaultArtifactHandler(configurationContainer, publishArtifactNotationParser);
-        }
-
-        ConfigurationResolver createDependencyResolver(ArtifactDependencyResolver artifactDependencyResolver, RepositoryHandler repositories,
-                                                       ModuleMetadataProcessor metadataProcessor) {
-            return new DefaultConfigurationResolver(artifactDependencyResolver, repositories, metadataProcessor);
-        }
-
-        ArtifactPublicationServices createArtifactPublicationServices(ServiceRegistry services) {
-            return new DefaultArtifactPublicationServices(services);
-        }
-
-        DependencyResolutionServices createDependencyResolutionServices(ServiceRegistry services) {
-            return new DefaultDependencyResolutionServices(services);
-        }
-
-        ArtifactResolutionQueryFactory createArtifactResolutionQueryFactory(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
-                                                                            ResolveIvyFactory ivyFactory, ModuleMetadataProcessor metadataProcessor,
-                                                                            CacheLockingManager cacheLockingManager) {
-            return new DefaultArtifactResolutionQueryFactory(configurationContainer, repositoryHandler, ivyFactory, metadataProcessor, cacheLockingManager);
-
-        }
-    }
-
-    private static class DefaultDependencyResolutionServices implements DependencyResolutionServices {
-        private final ServiceRegistry services;
-
-        private DefaultDependencyResolutionServices(ServiceRegistry services) {
-            this.services = services;
-        }
-
-        public RepositoryHandler getResolveRepositoryHandler() {
-            return services.get(RepositoryHandler.class);
-        }
-
-        public ConfigurationContainerInternal getConfigurationContainer() {
-            return services.get(ConfigurationContainerInternal.class);
-        }
-
-        public DependencyHandler getDependencyHandler() {
-            return services.get(DependencyHandler.class);
-        }
-    }
-
-    private static class DefaultArtifactPublicationServices implements ArtifactPublicationServices {
-        private final ServiceRegistry services;
-
-        public DefaultArtifactPublicationServices(ServiceRegistry services) {
-            this.services = services;
-        }
-
-        public RepositoryHandler createRepositoryHandler() {
-            Instantiator instantiator = services.get(Instantiator.class);
-            BaseRepositoryFactory baseRepositoryFactory = services.get(BaseRepositoryFactory.class);
-            return instantiator.newInstance(DefaultRepositoryHandler.class, baseRepositoryFactory, instantiator);
-        }
-
-        public ArtifactPublisher createArtifactPublisher() {
-            return new IvyBackedArtifactPublisher(
-                    services.get(PublishLocalComponentFactory.class),
-                    services.get(IvyContextManager.class),
-                    new DefaultIvyDependencyPublisher(),
-                    new IvyXmlModuleDescriptorWriter()
-            );
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java
deleted file mode 100755
index 382bf48..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java
+++ /dev/null
@@ -1,67 +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;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-
-public class DefaultModuleIdentifier implements ModuleIdentifier {
-    private final String group;
-    private final String name;
-
-    public DefaultModuleIdentifier(String group, String name) {
-        assert group != null : "group cannot be null";
-        assert name != null : "name cannot be null";
-        this.group = group;
-        this.name = name;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s:%s", group, name);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultModuleIdentifier other = (DefaultModuleIdentifier) obj;
-        if (!group.equals(other.group)) {
-            return false;
-        }
-        if (!name.equals(other.name)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return group.hashCode() ^ name.hashCode();
-    }
-}
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
deleted file mode 100755
index 5a78e91..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
+++ /dev/null
@@ -1,101 +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.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-
-public class DefaultModuleVersionIdentifier implements ModuleVersionIdentifier {
-
-    private final DefaultModuleIdentifier id;
-    private final String version;
-
-    public DefaultModuleVersionIdentifier(String group, String name, String version) {
-        assert group != null : "group cannot be null";
-        assert name != null : "name cannot be null";
-        assert version != null : "version cannot be null";
-        this.id = new DefaultModuleIdentifier(group, name);
-        this.version = version;
-    }
-
-    public DefaultModuleVersionIdentifier(ModuleIdentifier id, String version) {
-        this.id = new DefaultModuleIdentifier(id.getGroup(), id.getName());
-        this.version = version;
-    }
-
-    public String getGroup() {
-        return id.getGroup();
-    }
-
-    public String getName() {
-        return id.getName();
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s:%s:%s", id.getGroup(), id.getName(), version);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultModuleVersionIdentifier other = (DefaultModuleVersionIdentifier) obj;
-        if (!id.equals(other.id)) {
-            return false;
-        }
-        if (!version.equals(other.version)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return id.hashCode() ^ version.hashCode();
-    }
-
-    public ModuleIdentifier getModule() {
-        return id;
-    }
-
-    public static ModuleVersionIdentifier newId(Module module) {
-        return new DefaultModuleVersionIdentifier(module.getGroup(), module.getName(), module.getVersion());
-    }
-
-    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());
-    }
-
-    public static ModuleVersionIdentifier newId(ModuleComponentIdentifier componentId) {
-        return new DefaultModuleVersionIdentifier(componentId.getGroup(), componentId.getModule(), componentId.getVersion());
-    }
-}
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
deleted file mode 100644
index 70e0814..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts;
-
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.metadata.IvyArtifactName;
-import org.gradle.internal.Factory;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.File;
-
-public class DefaultResolvedArtifact implements ResolvedArtifact {
-    private final ResolvedModuleVersion owner;
-    private final IvyArtifactName artifact;
-    private long id;
-    private final Factory<ResolvedDependency> ownerSource;
-    private Factory<File> artifactSource;
-    private File file;
-
-    public DefaultResolvedArtifact(ResolvedModuleVersion owner, Factory<ResolvedDependency> ownerSource, IvyArtifactName artifact, Factory<File> artifactSource, long id) {
-        this.ownerSource = ownerSource;
-        this.owner = owner;
-        this.artifact = artifact;
-        this.id = id;
-        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()"
-        );
-        //resolvedDependency is expensive so lazily create it
-        return ownerSource.create();
-    }
-
-    public ResolvedModuleVersion getModuleVersion() {
-        return owner;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("[ResolvedArtifact dependency:%s name:%s classifier:%s extension:%s type:%s]", owner, getName(), getClassifier(), getExtension(), getType());
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultResolvedArtifact other = (DefaultResolvedArtifact) obj;
-        if (!other.owner.getId().equals(owner.getId())) {
-            return false;
-        }
-        if (!other.artifact.equals(artifact)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return owner.getId().hashCode() ^ getName().hashCode() ^ getType().hashCode() ^ getExtension().hashCode() ^ artifact.hashCode();
-    }
-
-    public String getName() {
-        return artifact.getName();
-    }
-
-    public String getType() {
-        return artifact.getType();
-    }
-
-    public String getExtension() {
-        return artifact.getExtension();
-    }
-
-    public String getClassifier() {
-        return artifact.getClassifier();
-    }
-    
-    public File getFile() {
-        if (file == null) {
-            file = artifactSource.create();
-            artifactSource = null;
-        }
-        return file;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
deleted file mode 100644
index 3e7ca5e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
+++ /dev/null
@@ -1,284 +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;
-
-import org.gradle.StartParameter;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.SingleFileBackedModuleVersionsCache;
-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.strategy.LatestStrategy;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionStrategy;
-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.DefaultModuleArtifactsCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.DefaultModuleMetaDataCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.PublishLocalComponentFactory;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectComponentRegistry;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublicationRegistry;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
-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.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.mvnsettings.*;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.DownloadingRepositoryArtifactCache;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.LocalFileRepositoryArtifactCache;
-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.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.FileLookup;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
-import org.gradle.api.internal.filestore.UniquePathKeyFileStore;
-import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore;
-import org.gradle.api.internal.notations.*;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectRegistry;
-import org.gradle.cache.CacheRepository;
-import org.gradle.initialization.ProjectAccessListener;
-import org.gradle.internal.SystemProperties;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.logging.ProgressLoggerFactory;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * The set of dependency management services that are created per build.
- */
-class DependencyManagementBuildScopeServices {
-    InMemoryDependencyMetadataCache createInMemoryDependencyMetadataCache() {
-        return new InMemoryDependencyMetadataCache();
-    }
-
-    DependencyManagementServices createDependencyManagementServices(ServiceRegistry parent) {
-        return new DefaultDependencyManagementServices(parent);
-    }
-
-    DependencyFactory createDependencyFactory(Instantiator instantiator,
-                                              ProjectAccessListener projectAccessListener,
-                                              StartParameter startParameter,
-                                              ClassPathRegistry classPathRegistry,
-                                              FileLookup fileLookup) {
-        DefaultProjectDependencyFactory factory = new DefaultProjectDependencyFactory(
-                projectAccessListener, instantiator, startParameter.isBuildProjectDependencies());
-
-        ProjectDependencyFactory projectDependencyFactory = new ProjectDependencyFactory(factory);
-        DependencyProjectNotationParser projParser = new DependencyProjectNotationParser(factory);
-
-        NotationParser<Object, ? extends Dependency> moduleMapParser = new DependencyMapNotationParser<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class);
-        NotationParser<Object, ? extends Dependency> moduleStringParser = new DependencyStringNotationParser<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class);
-        NotationParser<Object, ? extends Dependency> selfResolvingDependencyFactory = new DependencyFilesNotationParser(instantiator);
-
-        List<NotationParser<Object, ? extends Dependency>> notationParsers = Arrays.asList(
-                moduleStringParser,
-                moduleMapParser,
-                selfResolvingDependencyFactory,
-                projParser,
-                new DependencyClassPathNotationParser(instantiator, classPathRegistry, fileLookup.getFileResolver()));
-
-        return new DefaultDependencyFactory(
-                new DependencyNotationParser(notationParsers),
-                new ClientModuleNotationParserFactory(instantiator).create(),
-                projectDependencyFactory);
-    }
-
-    CacheLockingManager createCacheLockingManager(CacheRepository cacheRepository) {
-        return new DefaultCacheLockingManager(cacheRepository);
-    }
-
-    BuildCommencedTimeProvider createBuildTimeProvider() {
-        return new BuildCommencedTimeProvider();
-    }
-
-    ModuleVersionsCache createModuleVersionsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        return new SingleFileBackedModuleVersionsCache(
-                timeProvider,
-                cacheLockingManager
-        );
-    }
-
-    ModuleArtifactsCache createModuleArtifactsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        return new DefaultModuleArtifactsCache(
-                timeProvider,
-                cacheLockingManager
-        );
-    }
-
-    ModuleMetaDataCache createModuleDescriptorCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager, ResolverStrategy resolverStrategy) {
-        return new DefaultModuleMetaDataCache(
-                timeProvider,
-                cacheLockingManager,
-                resolverStrategy
-        );
-    }
-
-    ArtifactAtRepositoryCachedArtifactIndex createArtifactAtRepositoryCachedResolutionIndex(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        return new ArtifactAtRepositoryCachedArtifactIndex(
-                "artifact-at-repository",
-                timeProvider,
-                cacheLockingManager
-        );
-    }
-
-    ByUrlCachedExternalResourceIndex createArtifactUrlCachedResolutionIndex(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        return new ByUrlCachedExternalResourceIndex(
-                "artifact-at-url",
-                timeProvider,
-                cacheLockingManager
-        );
-    }
-
-    ArtifactIdentifierFileStore createArtifactRevisionIdFileStore(CacheLockingManager cacheLockingManager) {
-        return new ArtifactIdentifierFileStore(new UniquePathKeyFileStore(cacheLockingManager.getFileStoreDirectory()), new TmpDirTemporaryFileProvider());
-    }
-
-    MavenSettingsProvider createMavenSettingsProvider() {
-        return new DefaultMavenSettingsProvider(new DefaultMavenFileLocations());
-    }
-
-    LocalMavenRepositoryLocator createLocalMavenRepositoryLocator(MavenSettingsProvider mavenSettingsProvider) {
-        return new DefaultLocalMavenRepositoryLocator(mavenSettingsProvider, SystemProperties.asMap(), System.getenv());
-    }
-
-    LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> createArtifactRevisionIdLocallyAvailableResourceFinder(ArtifactCacheMetaData artifactCacheMetaData, LocalMavenRepositoryLocator localMavenRepositoryLocator, ArtifactIdentifierFileStore fileStore) {
-        LocallyAvailableResourceFinderFactory finderFactory = new LocallyAvailableResourceFinderFactory(
-                artifactCacheMetaData,
-                localMavenRepositoryLocator,
-                fileStore);
-        return finderFactory.create();
-    }
-
-    ResolverStrategy createResolverStrategy() {
-        return new ResolverStrategy();
-    }
-
-    VersionMatcher createVersionMatcher(ResolverStrategy resolverStrategy) {
-        return resolverStrategy.getVersionMatcher();
-    }
-
-    LatestStrategy createLatestStrategy(VersionMatcher versionMatcher) {
-        return new LatestVersionStrategy(versionMatcher);
-    }
-
-    LocalFileRepositoryArtifactCache createLocalRepositoryArtifactCache() {
-        return new LocalFileRepositoryArtifactCache();
-    }
-
-    DownloadingRepositoryArtifactCache createDownloadingRepositoryArtifactCache(ArtifactIdentifierFileStore artifactIdentifierFileStore, ByUrlCachedExternalResourceIndex externalResourceIndex,
-                                                                                TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
-        return new DownloadingRepositoryArtifactCache(artifactIdentifierFileStore,
-                externalResourceIndex,
-                temporaryFileProvider,
-                cacheLockingManager);
-    }
-
-    RepositoryTransportFactory createRepositoryTransportFactory(ProgressLoggerFactory progressLoggerFactory, LocalFileRepositoryArtifactCache localFileRepositoryArtifactCache,
-                                                                DownloadingRepositoryArtifactCache downloadingRepositoryArtifactCache, TemporaryFileProvider temporaryFileProvider,
-                                                                ByUrlCachedExternalResourceIndex externalResourceIndex, BuildCommencedTimeProvider buildCommencedTimeProvider) {
-        return new RepositoryTransportFactory(
-                progressLoggerFactory,
-                localFileRepositoryArtifactCache,
-                downloadingRepositoryArtifactCache,
-                temporaryFileProvider,
-                externalResourceIndex,
-                buildCommencedTimeProvider
-        );
-    }
-
-    LegacyDependencyResolverRepositoryFactory createCustomerResolverRepositoryFactory(ProgressLoggerFactory progressLoggerFactory, ArtifactIdentifierFileStore artifactIdentifierFileStore,
-                                                                                      TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
-        return new CustomIvyResolverRepositoryFactory(
-                progressLoggerFactory,
-                new LocalFileRepositoryCacheManager("local"),
-                new DownloadingRepositoryCacheManager(
-                        "downloading",
-                        artifactIdentifierFileStore,
-                        temporaryFileProvider,
-                        cacheLockingManager
-                )
-        );
-    }
-
-    ResolveIvyFactory createResolveIvyFactory(StartParameter startParameter, ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache, ModuleArtifactsCache moduleArtifactsCache,
-                                              ArtifactAtRepositoryCachedArtifactIndex artifactAtRepositoryCachedArtifactIndex, CacheLockingManager cacheLockingManager,
-                                              BuildCommencedTimeProvider buildCommencedTimeProvider, InMemoryDependencyMetadataCache inMemoryDependencyMetadataCache,
-                                              VersionMatcher versionMatcher, LatestStrategy latestStrategy) {
-        StartParameterResolutionOverride startParameterResolutionOverride = new StartParameterResolutionOverride(startParameter);
-        return new ResolveIvyFactory(
-                moduleVersionsCache,
-                moduleMetaDataCache,
-                moduleArtifactsCache,
-                artifactAtRepositoryCachedArtifactIndex,
-                cacheLockingManager,
-                startParameterResolutionOverride,
-                buildCommencedTimeProvider,
-                inMemoryDependencyMetadataCache,
-                versionMatcher,
-                latestStrategy);
-    }
-
-    ArtifactDependencyResolver createArtifactDependencyResolver(ResolveIvyFactory resolveIvyFactory, PublishLocalComponentFactory publishModuleDescriptorConverter,
-                                                                CacheLockingManager cacheLockingManager, IvyContextManager ivyContextManager, ResolutionResultsStoreFactory resolutionResultsStoreFactory,
-                                                                VersionMatcher versionMatcher, LatestStrategy latestStrategy, ProjectRegistry<ProjectInternal> projectRegistry,
-                                                                ComponentIdentifierFactory componentIdentifierFactory) {
-        ArtifactDependencyResolver resolver = new DefaultDependencyResolver(
-                resolveIvyFactory,
-                publishModuleDescriptorConverter,
-                new DefaultProjectComponentRegistry(
-                        publishModuleDescriptorConverter,
-                        projectRegistry),
-                cacheLockingManager,
-                ivyContextManager,
-                resolutionResultsStoreFactory,
-                versionMatcher,
-                latestStrategy);
-        return new ErrorHandlingArtifactDependencyResolver(
-                new ShortcircuitEmptyConfigsArtifactDependencyResolver(
-                        new SelfResolvingDependencyResolver(
-                                new CacheLockingArtifactDependencyResolver(
-                                        cacheLockingManager,
-                                        resolver)),
-                        componentIdentifierFactory));
-    }
-
-    ResolutionResultsStoreFactory createResolutionResultsStoreFactory(TemporaryFileProvider temporaryFileProvider) {
-        return new ResolutionResultsStoreFactory(temporaryFileProvider);
-    }
-
-    ProjectPublicationRegistry createProjectPublicationRegistry() {
-        return new DefaultProjectPublicationRegistry();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
deleted file mode 100644
index 77d9f07..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
+++ /dev/null
@@ -1,86 +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;
-
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.component.DefaultComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyContextManager;
-import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.*;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.*;
-
-class DependencyManagementGlobalScopeServices {
-    IvyContextManager createIvyContextManager() {
-        return new DefaultIvyContextManager();
-    }
-
-    ModuleDescriptorFactory createModuleDescriptorFactory() {
-        return new DefaultModuleDescriptorFactory();
-    }
-
-    ExcludeRuleConverter createExcludeRuleConverter() {
-        return new DefaultExcludeRuleConverter();
-    }
-
-    ComponentIdentifierFactory createComponentIdentifierFactory() {
-        return new DefaultComponentIdentifierFactory();
-    }
-
-    ExternalModuleIvyDependencyDescriptorFactory createExternalModuleDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        return new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverter);
-    }
-
-    ConfigurationsToModuleDescriptorConverter createConfigurationsToModuleDescriptorConverter() {
-        return new DefaultConfigurationsToModuleDescriptorConverter();
-    }
-
-    DependencyDescriptorFactory createDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter, ExternalModuleIvyDependencyDescriptorFactory descriptorFactory) {
-        DefaultClientModuleMetaDataFactory clientModuleDescriptorFactory = new DefaultClientModuleMetaDataFactory();
-        DependencyDescriptorFactory dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
-                new ClientModuleIvyDependencyDescriptorFactory(
-                        excludeRuleConverter,
-                        clientModuleDescriptorFactory
-                ),
-                new ProjectIvyDependencyDescriptorFactory(
-                        excludeRuleConverter),
-                descriptorFactory);
-        clientModuleDescriptorFactory.setDependencyDescriptorFactory(dependencyDescriptorFactory);
-        return dependencyDescriptorFactory;
-    }
-
-    ResolveLocalComponentFactory createResolveModuleDescriptorConverter(ModuleDescriptorFactory moduleDescriptorFactory,
-                                                                            ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter,
-                                                                            DependencyDescriptorFactory dependencyDescriptorFactory,
-                                                                            ExcludeRuleConverter excludeRuleConverter,
-                                                                            ComponentIdentifierFactory componentIdentifierFactory) {
-        return new ResolveLocalComponentFactory(
-                moduleDescriptorFactory,
-                configurationsToModuleDescriptorConverter,
-                new DefaultDependenciesToModuleDescriptorConverter(
-                        dependencyDescriptorFactory,
-                        excludeRuleConverter),
-                componentIdentifierFactory);
-
-    }
-
-    PublishLocalComponentFactory createPublishModuleDescriptorConverter(ResolveLocalComponentFactory moduleDescriptorConverter) {
-        return new PublishLocalComponentFactory(
-                moduleDescriptorConverter,
-                new DefaultConfigurationsToArtifactsConverter());
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyServices.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyServices.java
deleted file mode 100644
index a78f6b2..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DependencyServices.java
+++ /dev/null
@@ -1,33 +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;
-
-import org.gradle.internal.service.ServiceRegistration;
-import org.gradle.internal.service.scopes.PluginServiceRegistry;
-
-public class DependencyServices implements PluginServiceRegistry {
-    public void registerGlobalServices(ServiceRegistration registration) {
-        registration.addProvider(new DependencyManagementGlobalScopeServices());
-    }
-
-    public void registerBuildServices(ServiceRegistration registration) {
-        registration.addProvider(new DependencyManagementBuildScopeServices());
-    }
-
-    public void registerProjectServices(ServiceRegistration registration) {
-    }
-}
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
deleted file mode 100644
index 3512dc5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleMetadataProcessor.java
+++ /dev/null
@@ -1,22 +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;
-
-import org.gradle.api.internal.artifacts.metadata.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
deleted file mode 100644
index 4cd8137..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java
+++ /dev/null
@@ -1,39 +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;
-
-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 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());
-    }
-
-    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/ModuleVersionPublisher.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java
deleted file mode 100644
index da212d8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionPublisher.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.artifacts;
-
-import org.apache.ivy.core.settings.IvySettings;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionPublishMetaData;
-
-import java.io.IOException;
-
-public interface ModuleVersionPublisher {
-    void publish(ModuleVersionPublishMetaData moduleVersion) throws IOException;
-
-    void setSettings(IvySettings settings);
-}
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
deleted file mode 100644
index 8e43bcf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java
+++ /dev/null
@@ -1,41 +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;
-
-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
deleted file mode 100644
index e02ab3b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/PlexusLoggerAdapter.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts;
-
-import org.codehaus.plexus.logging.Logger;
-
-public class PlexusLoggerAdapter implements Logger {
-    org.slf4j.Logger logger;
-
-    public PlexusLoggerAdapter(org.slf4j.Logger logger) {
-        this.logger = logger;
-    }
-
-    public void debug(String s) {
-        logger.debug(s);
-    }
-
-    public void debug(String s, Throwable throwable) {
-        logger.debug(s, throwable);
-    }
-
-    public boolean isDebugEnabled() {
-        return logger.isDebugEnabled();
-    }
-
-    public void info(String s) {
-        logger.info(s);
-    }
-
-    public void info(String s, Throwable throwable) {
-        logger.info(s, throwable);
-    }
-
-    public boolean isInfoEnabled() {
-        return logger.isInfoEnabled();
-    }
-
-    public void warn(String s) {
-        logger.warn(s);
-    }
-
-    public void warn(String s, Throwable throwable) {
-        logger.warn(s, throwable);
-    }
-
-    public boolean isWarnEnabled() {
-        return logger.isWarnEnabled();
-    }
-
-    public void error(String s) {
-        logger.error(s);
-    }
-
-    public void error(String s, Throwable throwable) {
-        logger.error(s, throwable);
-    }
-
-    public boolean isErrorEnabled() {
-        return logger.isErrorEnabled();
-    }
-
-    public void fatalError(String s) {
-        logger.error(s);
-    }
-
-    public void fatalError(String s, Throwable throwable) {
-        logger.error(s, throwable);
-    }
-
-    public boolean isFatalErrorEnabled() {
-        return logger.isErrorEnabled();
-    }
-
-    public Logger getChildLogger(String s) {
-        throw new UnsupportedOperationException();
-    }
-
-    public int getThreshold() {
-        throw new UnsupportedOperationException();
-    }
-
-    public String getName() {
-        return logger.getName();
-    }
-}
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
deleted file mode 100644
index ed9accc..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java
+++ /dev/null
@@ -1,39 +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;
-
-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
deleted file mode 100644
index a230dd4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolverResults.java
+++ /dev/null
@@ -1,64 +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;
-
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.artifacts.ResolvedConfiguration;
-import org.gradle.api.artifacts.result.ResolutionResult;
-
-public class ResolverResults {
-    private ResolvedConfiguration resolvedConfiguration;
-    private ResolutionResult resolutionResult;
-    private ResolveException fatalFailure;
-
-    //old model, slowly being replaced by the new model
-    public ResolvedConfiguration getResolvedConfiguration() {
-        assertHasResult();
-        return resolvedConfiguration;
-    }
-
-    //new model
-    public ResolutionResult getResolutionResult() {
-        assertHasResult();
-        if (fatalFailure != null) {
-            throw fatalFailure;
-        }
-        return resolutionResult;
-    }
-
-    private void assertHasResult() {
-        if (resolvedConfiguration == null) {
-            throw new IllegalStateException("Resolution result has not been attached.");
-        }
-    }
-
-    public void withResolvedConfiguration(ResolvedConfiguration resolvedConfiguration) {
-        this.resolvedConfiguration = resolvedConfiguration;
-    }
-
-    public void resolved(ResolvedConfiguration resolvedConfiguration, ResolutionResult resolutionResult) {
-        this.resolvedConfiguration = resolvedConfiguration;
-        this.resolutionResult = resolutionResult;
-        this.fatalFailure = null;
-    }
-
-    public void failed(ResolvedConfiguration resolvedConfiguration, ResolveException failure) {
-        this.resolvedConfiguration = resolvedConfiguration;
-        this.resolutionResult = null;
-        this.fatalFailure = failure;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
deleted file mode 100644
index e78e615..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-
-public class DefaultComponentIdentifierFactory implements ComponentIdentifierFactory {
-    public ComponentIdentifier createComponentIdentifier(ModuleInternal module) {
-        String projectPath = module.getProjectPath();
-
-        if(projectPath != null) {
-            return new DefaultProjectComponentIdentifier(projectPath);
-        }
-
-        return new DefaultModuleComponentIdentifier(module.getGroup(), module.getName(), module.getVersion());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifier.java
deleted file mode 100644
index 3fa88f5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifier.java
+++ /dev/null
@@ -1,98 +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.component;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-
-public class DefaultModuleComponentIdentifier implements ModuleComponentIdentifier {
-    private final String displayName;
-    private final String group;
-    private final String module;
-    private final String version;
-
-    public DefaultModuleComponentIdentifier(String group, String module, String version) {
-        assert group != null : "group cannot be null";
-        assert module != null : "module cannot be null";
-        assert version != null : "version cannot be null";
-        displayName = String.format("%s:%s:%s", group, module, version);
-        this.group = group;
-        this.module = module;
-        this.version = version;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getModule() {
-        return module;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultModuleComponentIdentifier that = (DefaultModuleComponentIdentifier) o;
-
-        if (!group.equals(that.group)) {
-            return false;
-        }
-        if (!module.equals(that.module)) {
-            return false;
-        }
-        if (!version.equals(that.version)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = group.hashCode();
-        result = 31 * result + module.hashCode();
-        result = 31 * result + version.hashCode();
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return displayName;
-    }
-
-    public static ModuleComponentIdentifier newId(String group, String name, String version) {
-        return new DefaultModuleComponentIdentifier(group, name, version);
-    }
-
-    public static ModuleComponentIdentifier newId(ModuleVersionIdentifier moduleVersionIdentifier) {
-        return new DefaultModuleComponentIdentifier(moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion());
-    }
-}
-
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelector.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelector.java
deleted file mode 100644
index 480c3da..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelector.java
+++ /dev/null
@@ -1,107 +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.component;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentSelector;
-
-public class DefaultModuleComponentSelector implements ModuleComponentSelector {
-    private final String displayName;
-    private final String group;
-    private final String module;
-    private final String version;
-
-    public DefaultModuleComponentSelector(String group, String module, String version) {
-        assert group != null : "group cannot be null";
-        assert module != null : "module cannot be null";
-        assert version != null : "version cannot be null";
-        displayName = String.format("%s:%s:%s", group, module, version);
-        this.group = group;
-        this.module = module;
-        this.version = version;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getModule() {
-        return module;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public boolean matchesStrictly(ComponentIdentifier identifier) {
-        assert identifier != null : "identifier cannot be null";
-
-        if(identifier instanceof ModuleComponentIdentifier) {
-            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier)identifier;
-            return module.equals(moduleComponentIdentifier.getModule())
-                    && group.equals(moduleComponentIdentifier.getGroup())
-                    && version.equals(moduleComponentIdentifier.getVersion());
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultModuleComponentSelector that = (DefaultModuleComponentSelector) o;
-
-        if (!group.equals(that.group)) {
-            return false;
-        }
-        if (!module.equals(that.module)) {
-            return false;
-        }
-        if (!version.equals(that.version)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = group.hashCode();
-        result = 31 * result + module.hashCode();
-        result = 31 * result + version.hashCode();
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return displayName;
-    }
-
-    public static ModuleComponentSelector newSelector(String group, String name, String version) {
-        return new DefaultModuleComponentSelector(group, name, version);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifier.java
deleted file mode 100644
index 81c138a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifier.java
+++ /dev/null
@@ -1,69 +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.component;
-
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-
-public class DefaultProjectComponentIdentifier implements ProjectComponentIdentifier {
-    private final String projectPath;
-    private final String displayName;
-
-    public DefaultProjectComponentIdentifier(String projectPath) {
-        assert projectPath != null : "project path cannot be null";
-        this.projectPath = projectPath;
-        displayName = String.format("project %s", projectPath);
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getProjectPath() {
-        return projectPath;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultProjectComponentIdentifier that = (DefaultProjectComponentIdentifier) o;
-
-        if (!projectPath.equals(that.projectPath)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return projectPath.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return displayName;
-    }
-
-    public static ProjectComponentIdentifier newId(String projectPath) {
-        return new DefaultProjectComponentIdentifier(projectPath);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelector.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelector.java
deleted file mode 100644
index e75d84e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelector.java
+++ /dev/null
@@ -1,82 +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.component;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentSelector;
-
-public class DefaultProjectComponentSelector implements ProjectComponentSelector {
-    private final String projectPath;
-    private final String displayName;
-
-    public DefaultProjectComponentSelector(String projectPath) {
-        assert projectPath != null : "project path cannot be null";
-        this.projectPath = projectPath;
-        displayName = String.format("project %s", projectPath);
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getProjectPath() {
-        return projectPath;
-    }
-
-    public boolean matchesStrictly(ComponentIdentifier identifier) {
-        assert identifier != null : "identifier cannot be null";
-
-        if(identifier instanceof ProjectComponentIdentifier) {
-            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier)identifier;
-            return projectPath.equals(projectComponentIdentifier.getProjectPath());
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultProjectComponentSelector that = (DefaultProjectComponentSelector) o;
-
-        if (!projectPath.equals(that.projectPath)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return projectPath.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return displayName;
-    }
-
-    public static ProjectComponentSelector newSelector(String projectPath) {
-        return new DefaultProjectComponentSelector(projectPath);
-    }
-}
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
deleted file mode 100644
index 793c227..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/Configurations.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.configurations;
-
-import org.gradle.api.artifacts.Configuration;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-public class Configurations {
-    public static Set<String> getNames(Collection<? extends Configuration> configurations, boolean includeExtended) {
-        Set<Configuration> allConfigurations = new HashSet<Configuration>(configurations);
-        if (includeExtended) {
-            allConfigurations = createAllConfigurations(configurations);
-        }
-        Set<String> names = new HashSet<String>();
-        for (Configuration configuration : allConfigurations) {
-            names.add(configuration.getName());
-        }
-        return names;
-    }
-
-    public static Set<String> getNames(Collection<Configuration> configurations) {
-        return getNames(configurations, false);
-    }
-
-    private static Set<Configuration> createAllConfigurations(Collection<? extends Configuration> configurations) {
-        Set<Configuration> allConfigurations = new HashSet<Configuration>();
-        for (Configuration configuration : configurations) {
-            allConfigurations.addAll(configuration.getHierarchy());
-        }
-        return allConfigurations;
-    }
-
-    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/DefaultConfiguration.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
deleted file mode 100644
index 177b0c3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
+++ /dev/null
@@ -1,572 +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.configurations;
-
-import groovy.lang.Closure;
-import org.gradle.api.*;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.result.ResolutionResult;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.CompositeDomainObjectSet;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.api.internal.artifacts.*;
-import org.gradle.api.internal.file.AbstractFileCollection;
-import org.gradle.api.internal.tasks.AbstractTaskDependency;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
-import org.gradle.util.CollectionUtils;
-import org.gradle.util.ConfigureUtil;
-import org.gradle.util.WrapUtil;
-
-import java.io.File;
-import java.util.*;
-
-import static org.apache.ivy.core.module.descriptor.Configuration.Visibility;
-
-public class DefaultConfiguration extends AbstractFileCollection implements ConfigurationInternal {
-    private final String path;
-    private final String name;
-
-    private Visibility visibility = Visibility.PUBLIC;
-    private boolean transitive = true;
-    private Set<Configuration> extendsFrom = new LinkedHashSet<Configuration>();
-    private String description;
-    private ConfigurationsProvider configurationsProvider;
-    private final ConfigurationResolver resolver;
-    private final ListenerManager listenerManager;
-    private final DependencyMetaDataProvider metaDataProvider;
-    private final DefaultDependencySet dependencies;
-    private final CompositeDomainObjectSet<Dependency> inheritedDependencies;
-    private final DefaultDependencySet allDependencies;
-    private final DefaultPublishArtifactSet artifacts;
-    private final CompositeDomainObjectSet<PublishArtifact> inheritedArtifacts;
-    private final DefaultPublishArtifactSet allArtifacts;
-    private final ConfigurationResolvableDependencies resolvableDependencies = new ConfigurationResolvableDependencies();
-    private final ListenerBroadcast<DependencyResolutionListener> resolutionListenerBroadcast;
-    private Set<ExcludeRule> excludeRules = new LinkedHashSet<ExcludeRule>();
-
-    // This lock only protects the following fields
-    private final Object lock = new Object();
-    private State state = State.UNRESOLVED;
-    private ResolverResults cachedResolverResults;
-    private final ResolutionStrategyInternal resolutionStrategy;
-
-    public DefaultConfiguration(String path, String name, ConfigurationsProvider configurationsProvider,
-                                ConfigurationResolver resolver, ListenerManager listenerManager,
-                                DependencyMetaDataProvider metaDataProvider,
-                                ResolutionStrategyInternal resolutionStrategy) {
-        this.path = path;
-        this.name = name;
-        this.configurationsProvider = configurationsProvider;
-        this.resolver = resolver;
-        this.listenerManager = listenerManager;
-        this.metaDataProvider = metaDataProvider;
-        this.resolutionStrategy = resolutionStrategy;
-
-        resolutionListenerBroadcast = listenerManager.createAnonymousBroadcaster(DependencyResolutionListener.class);
-
-        DefaultDomainObjectSet<Dependency> ownDependencies = new DefaultDomainObjectSet<Dependency>(Dependency.class);
-        ownDependencies.beforeChange(new VetoContainerChangeAction());
-
-        dependencies = new DefaultDependencySet(String.format("%s dependencies", getDisplayName()), ownDependencies);
-        inheritedDependencies = new CompositeDomainObjectSet<Dependency>(Dependency.class, ownDependencies);
-        allDependencies = new DefaultDependencySet(String.format("%s all dependencies", getDisplayName()), inheritedDependencies);
-
-        DefaultDomainObjectSet<PublishArtifact> ownArtifacts = new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact.class);
-        ownArtifacts.beforeChange(new VetoContainerChangeAction());
-        artifacts = new DefaultPublishArtifactSet(String.format("%s artifacts", getDisplayName()), ownArtifacts);
-        inheritedArtifacts = new CompositeDomainObjectSet<PublishArtifact>(PublishArtifact.class, ownArtifacts);
-        allArtifacts = new DefaultPublishArtifactSet(String.format("%s all artifacts", getDisplayName()), inheritedArtifacts);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public State getState() {
-        synchronized (lock) {
-            return state;
-        }
-    }
-
-    public ModuleInternal getModule() {
-        return metaDataProvider.getModule();
-    }
-
-    public boolean isVisible() {
-        return visibility == Visibility.PUBLIC;
-    }
-
-    public Configuration setVisible(boolean visible) {
-        throwExceptionIfNotInUnresolvedState();
-        this.visibility = visible ? Visibility.PUBLIC : Visibility.PRIVATE;
-        return this;
-    }
-
-    public Set<Configuration> getExtendsFrom() {
-        return Collections.unmodifiableSet(extendsFrom);
-    }
-
-    public Configuration setExtendsFrom(Set<Configuration> extendsFrom) {
-        throwExceptionIfNotInUnresolvedState();
-        for (Configuration configuration : this.extendsFrom) {
-            inheritedArtifacts.removeCollection(configuration.getAllArtifacts());
-            inheritedDependencies.removeCollection(configuration.getAllDependencies());
-        }
-        this.extendsFrom = new HashSet<Configuration>();
-        for (Configuration configuration : extendsFrom) {
-            extendsFrom(configuration);
-        }
-        return this;
-    }
-
-    public Configuration extendsFrom(Configuration... extendsFrom) {
-        throwExceptionIfNotInUnresolvedState();
-        for (Configuration configuration : extendsFrom) {
-            if (configuration.getHierarchy().contains(this)) {
-                throw new InvalidUserDataException(String.format(
-                        "Cyclic extendsFrom from %s and %s is not allowed. See existing hierarchy: %s", this,
-                        configuration, configuration.getHierarchy()));
-            }
-            this.extendsFrom.add(configuration);
-            inheritedArtifacts.addCollection(configuration.getAllArtifacts());
-            inheritedDependencies.addCollection(configuration.getAllDependencies());
-        }
-        return this;
-    }
-
-    public boolean isTransitive() {
-        return transitive;
-    }
-
-    public Configuration setTransitive(boolean transitive) {
-        throwExceptionIfNotInUnresolvedState();
-        this.transitive = transitive;
-        return this;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public Configuration setDescription(String description) {
-        throwExceptionIfNotInUnresolvedState();
-        this.description = description;
-        return this;
-    }
-
-    public Set<Configuration> getHierarchy() {
-        Set<Configuration> result = WrapUtil.<Configuration>toLinkedSet(this);
-        collectSuperConfigs(this, result);
-        return result;
-    }
-
-    private void collectSuperConfigs(Configuration configuration, Set<Configuration> result) {
-        for (Configuration superConfig : configuration.getExtendsFrom()) {
-            if (result.contains(superConfig)) {
-                result.remove(superConfig);
-            }
-            result.add(superConfig);
-            collectSuperConfigs(superConfig, result);
-        }
-    }
-
-    public Set<Configuration> getAll() {
-        return configurationsProvider.getAll();
-    }
-
-    public Set<File> resolve() {
-        return getFiles();
-    }
-
-    public Set<File> getFiles() {
-        return fileCollection(Specs.SATISFIES_ALL).getFiles();
-    }
-
-    public Set<File> files(Dependency... dependencies) {
-        return fileCollection(dependencies).getFiles();
-    }
-
-    public Set<File> files(Closure dependencySpecClosure) {
-        return fileCollection(dependencySpecClosure).getFiles();
-    }
-
-    public Set<File> files(Spec<? super Dependency> dependencySpec) {
-        return fileCollection(dependencySpec).getFiles();
-    }
-
-    public FileCollection fileCollection(Spec<? super Dependency> dependencySpec) {
-        return new ConfigurationFileCollection(dependencySpec);
-    }
-
-    public FileCollection fileCollection(Closure dependencySpecClosure) {
-        return new ConfigurationFileCollection(dependencySpecClosure);
-    }
-
-    public FileCollection fileCollection(Dependency... dependencies) {
-        return new ConfigurationFileCollection(WrapUtil.toLinkedSet(dependencies));
-    }
-
-    public ResolvedConfiguration getResolvedConfiguration() {
-        resolveNow();
-        return cachedResolverResults.getResolvedConfiguration();
-    }
-
-    private void resolveNow() {
-        synchronized (lock) {
-            if (state == State.UNRESOLVED) {
-                DependencyResolutionListener broadcast = getDependencyResolutionBroadcast();
-                ResolvableDependencies incoming = getIncoming();
-                broadcast.beforeResolve(incoming);
-                cachedResolverResults = resolver.resolve(this);
-                if (cachedResolverResults.getResolvedConfiguration().hasError()) {
-                    state = State.RESOLVED_WITH_FAILURES;
-                } else {
-                    state = State.RESOLVED;
-                }
-                broadcast.afterResolve(incoming);
-            }
-        }
-    }
-
-    public TaskDependency getBuildDependencies() {
-        return allDependencies.getBuildDependencies();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public TaskDependency getTaskDependencyFromProjectDependency(final boolean useDependedOn, final String taskName) {
-        return new AbstractTaskDependency() {
-            public void resolve(TaskDependencyResolveContext context) {
-                if (useDependedOn) {
-                    addTaskDependenciesFromProjectsIDependOn(taskName, context);
-                } else {
-                    Project thisProject = context.getTask().getProject();
-                    addTaskDependenciesFromProjectsDependingOnMe(thisProject, taskName, context);
-                }
-            }
-
-            private void addTaskDependenciesFromProjectsIDependOn(final String taskName,
-                                                                  final TaskDependencyResolveContext context) {
-                Set<ProjectDependency> projectDependencies = getAllDependencies().withType(ProjectDependency.class);
-                for (ProjectDependency projectDependency : projectDependencies) {
-                    Task nextTask = projectDependency.getDependencyProject().getTasks().findByName(taskName);
-                    if (nextTask != null) {
-                        context.add(nextTask);
-                    }
-                }
-            }
-
-            private void addTaskDependenciesFromProjectsDependingOnMe(final Project thisProject, final String taskName,
-                                                                      final TaskDependencyResolveContext context) {
-                Set<Task> tasksWithName = thisProject.getRootProject().getTasksByName(taskName, true);
-                for (Task nextTask : tasksWithName) {
-                    Configuration configuration = nextTask.getProject().getConfigurations().findByName(getName());
-                    if (configuration != null && doesConfigurationDependOnProject(configuration, thisProject)) {
-                        context.add(nextTask);
-                    }
-                }
-            }
-        };
-    }
-
-    private static boolean doesConfigurationDependOnProject(Configuration configuration, Project project) {
-        Set<ProjectDependency> projectDependencies = configuration.getAllDependencies().withType(ProjectDependency.class);
-        for (ProjectDependency projectDependency : projectDependencies) {
-            if (projectDependency.getDependencyProject().equals(project)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public DependencySet getDependencies() {
-        return dependencies;
-    }
-
-    public DependencySet getAllDependencies() {
-        return allDependencies;
-    }
-
-    public PublishArtifactSet getArtifacts() {
-        return artifacts;
-    }
-
-    public PublishArtifactSet getAllArtifacts() {
-        return allArtifacts;
-    }
-
-    public Set<ExcludeRule> getExcludeRules() {
-        return Collections.unmodifiableSet(excludeRules);
-    }
-
-    public void setExcludeRules(Set<ExcludeRule> excludeRules) {
-        throwExceptionIfNotInUnresolvedState();
-        this.excludeRules = excludeRules;
-    }
-
-    public DefaultConfiguration exclude(Map<String, String> excludeRuleArgs) {
-        throwExceptionIfNotInUnresolvedState();
-        excludeRules.add(new DefaultExcludeRule(excludeRuleArgs.get(ExcludeRule.GROUP_KEY), excludeRuleArgs.get(ExcludeRule.MODULE_KEY)));
-        return this;
-    }
-
-    public String getUploadTaskName() {
-        return Configurations.uploadTaskName(getName());
-    }
-
-    public String getDisplayName() {
-        return String.format("configuration '%s'", path);
-    }
-
-    public ResolvableDependencies getIncoming() {
-        return resolvableDependencies;
-    }
-
-    public Configuration copy() {
-        return createCopy(getDependencies(), false);
-    }
-
-    public Configuration copyRecursive() {
-        return createCopy(getAllDependencies(), true);
-    }
-
-    public Configuration copy(Spec<? super Dependency> dependencySpec) {
-        return createCopy(CollectionUtils.filter(getDependencies(), dependencySpec), false);
-    }
-
-    public Configuration copyRecursive(Spec<? super Dependency> dependencySpec) {
-        return createCopy(CollectionUtils.filter(getAllDependencies(), dependencySpec), true);
-    }
-
-    private DefaultConfiguration createCopy(Set<Dependency> dependencies, boolean recursive) {
-        DetachedConfigurationsProvider configurationsProvider = new DetachedConfigurationsProvider();
-        DefaultConfiguration copiedConfiguration = new DefaultConfiguration(path + "Copy", name + "Copy",
-                configurationsProvider, resolver, listenerManager, metaDataProvider, resolutionStrategy.copy());
-        configurationsProvider.setTheOnlyConfiguration(copiedConfiguration);
-        // state, cachedResolvedConfiguration, and extendsFrom intentionally not copied - must re-resolve copy
-        // copying extendsFrom could mess up dependencies when copy was re-resolved
-
-        copiedConfiguration.visibility = visibility;
-        copiedConfiguration.transitive = transitive;
-        copiedConfiguration.description = description;
-
-        copiedConfiguration.getArtifacts().addAll(getAllArtifacts());
-
-        // todo An ExcludeRule is a value object but we don't enforce immutability for DefaultExcludeRule as strong as we
-        // should (we expose the Map). We should provide a better API for ExcludeRule (I don't want to use unmodifiable Map).
-        // As soon as DefaultExcludeRule is truly immutable, we don't need to create a new instance of DefaultExcludeRule.
-        Set<Configuration> excludeRuleSources = new LinkedHashSet<Configuration>();
-        excludeRuleSources.add(this);
-        if (recursive) {
-            excludeRuleSources.addAll(getHierarchy());
-        }
-
-        for (Configuration excludeRuleSource : excludeRuleSources) {
-            for (ExcludeRule excludeRule : excludeRuleSource.getExcludeRules()) {
-                copiedConfiguration.excludeRules.add(new DefaultExcludeRule(excludeRule.getGroup(), excludeRule.getModule()));
-            }
-        }
-
-        DomainObjectSet<Dependency> copiedDependencies = copiedConfiguration.getDependencies();
-        for (Dependency dependency : dependencies) {
-            copiedDependencies.add(dependency.copy());
-        }
-        return copiedConfiguration;
-    }
-
-    public Configuration copy(Closure dependencySpec) {
-        return copy(Specs.<Dependency>convertClosureToSpec(dependencySpec));
-    }
-
-    public Configuration copyRecursive(Closure dependencySpec) {
-        return copyRecursive(Specs.<Dependency>convertClosureToSpec(dependencySpec));
-    }
-
-    public DependencyResolutionListener getDependencyResolutionBroadcast() {
-        return resolutionListenerBroadcast.getSource();
-    }
-
-    public ResolutionStrategyInternal getResolutionStrategy() {
-        return resolutionStrategy;
-    }
-
-    public String getPath() {
-        return path;
-    }
-
-    public Configuration resolutionStrategy(Closure closure) {
-        ConfigureUtil.configure(closure, resolutionStrategy);
-        return this;
-    }
-
-    private void throwExceptionIfNotInUnresolvedState() {
-        if (getState() != State.UNRESOLVED) {
-            throw new InvalidUserDataException("You can't change a configuration which is not in unresolved state!");
-        }
-    }
-
-    class ConfigurationFileCollection extends AbstractFileCollection {
-        private Spec<? super Dependency> dependencySpec;
-
-        private ConfigurationFileCollection(Spec<? super Dependency> dependencySpec) {
-            this.dependencySpec = dependencySpec;
-        }
-
-        public ConfigurationFileCollection(Closure dependencySpecClosure) {
-            this.dependencySpec = Specs.convertClosureToSpec(dependencySpecClosure);
-        }
-
-        public ConfigurationFileCollection(final Set<Dependency> dependencies) {
-            this.dependencySpec = new Spec<Dependency>() {
-                public boolean isSatisfiedBy(Dependency element) {
-                    return dependencies.contains(element);
-                }
-            };
-        }
-
-        @Override
-        public TaskDependency getBuildDependencies() {
-            return DefaultConfiguration.this.getBuildDependencies();
-        }
-
-        public Spec<? super Dependency> getDependencySpec() {
-            return dependencySpec;
-        }
-
-        public String getDisplayName() {
-            return String.format("%s dependencies", DefaultConfiguration.this);
-        }
-
-        public Set<File> getFiles() {
-            synchronized (lock) {
-                ResolvedConfiguration resolvedConfiguration = getResolvedConfiguration();
-                if (getState() == State.RESOLVED_WITH_FAILURES) {
-                    resolvedConfiguration.rethrowFailure();
-                }
-                return resolvedConfiguration.getFiles(dependencySpec);
-            }
-        }
-    }
-
-    /**
-     * Print a formatted representation of a Configuration
-     */
-    public String dump() {
-        StringBuilder reply = new StringBuilder();
-
-        reply.append("\nConfiguration:");
-        reply.append("  class='" + this.getClass() + "'");
-        reply.append("  name='" + this.getName() + "'");
-        reply.append("  hashcode='" + this.hashCode() + "'");
-
-        reply.append("\nLocal Dependencies:");
-        if (getDependencies().size() > 0) {
-            for (Dependency d : getDependencies()) {
-                reply.append("\n   " + d);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        reply.append("\nLocal Artifacts:");
-        if (getArtifacts().size() > 0) {
-            for (PublishArtifact a : getArtifacts()) {
-                reply.append("\n   " + a);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        reply.append("\nAll Dependencies:");
-        if (getAllDependencies().size() > 0) {
-            for (Dependency d : getAllDependencies()) {
-                reply.append("\n   " + d);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-
-        reply.append("\nAll Artifacts:");
-        if (getAllArtifacts().size() > 0) {
-            for (PublishArtifact a : getAllArtifacts()) {
-                reply.append("\n   " + a);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        return reply.toString();
-    }
-
-    private class VetoContainerChangeAction implements Runnable {
-        public void run() {
-            throwExceptionIfNotInUnresolvedState();
-        }
-    }
-
-    private class ConfigurationResolvableDependencies implements ResolvableDependencies {
-        public String getName() {
-            return name;
-        }
-
-        public String getPath() {
-            return path;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("dependencies '%s'", path);
-        }
-
-        public FileCollection getFiles() {
-            return DefaultConfiguration.this.fileCollection(Specs.<Dependency>satisfyAll());
-        }
-
-        public DependencySet getDependencies() {
-            return getAllDependencies();
-        }
-
-        public void beforeResolve(Action<? super ResolvableDependencies> action) {
-            resolutionListenerBroadcast.add("beforeResolve", action);
-        }
-
-        public void beforeResolve(Closure action) {
-            resolutionListenerBroadcast.add(new ClosureBackedMethodInvocationDispatch("beforeResolve", action));
-        }
-
-        public void afterResolve(Action<? super ResolvableDependencies> action) {
-            resolutionListenerBroadcast.add("afterResolve", action);
-        }
-
-        public void afterResolve(Closure action) {
-            resolutionListenerBroadcast.add(new ClosureBackedMethodInvocationDispatch("afterResolve", action));
-        }
-
-        public ResolutionResult getResolutionResult() {
-            DefaultConfiguration.this.resolveNow();
-            return DefaultConfiguration.this.cachedResolverResults.getResolutionResult();
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 26d5085..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
-
-import groovy.lang.Closure;
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.UnknownDomainObjectException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.UnknownConfigurationException;
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.api.internal.DomainObjectContext;
-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;
-
-public class DefaultConfigurationContainer extends AbstractNamedDomainObjectContainer<Configuration>
-        implements ConfigurationContainerInternal, ConfigurationsProvider {
-    public static final String DETACHED_CONFIGURATION_DEFAULT_NAME = "detachedConfiguration";
-    
-    private final ConfigurationResolver resolver;
-    private final Instantiator instantiator;
-    private final DomainObjectContext context;
-    private final ListenerManager listenerManager;
-    private final DependencyMetaDataProvider dependencyMetaDataProvider;
-
-    private int detachedConfigurationDefaultNameCounter = 1;
-
-    public DefaultConfigurationContainer(ConfigurationResolver resolver,
-                                         Instantiator instantiator, DomainObjectContext context, ListenerManager listenerManager,
-                                         DependencyMetaDataProvider dependencyMetaDataProvider) {
-        super(Configuration.class, instantiator, new Configuration.Namer());
-        this.resolver = resolver;
-        this.instantiator = instantiator;
-        this.context = context;
-        this.listenerManager = listenerManager;
-        this.dependencyMetaDataProvider = dependencyMetaDataProvider;
-    }
-
-    @Override
-    protected Configuration doCreate(String name) {
-        return instantiator.newInstance(DefaultConfiguration.class, context.absoluteProjectPath(name),
-                name, this, resolver, listenerManager,
-                dependencyMetaDataProvider, instantiator.newInstance(DefaultResolutionStrategy.class));
-    }
-
-    public Set<Configuration> getAll() {
-        return this;
-    }
-
-    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);
-    }
-
-    @Override
-    public ConfigurationInternal getByName(String name) {
-        return (ConfigurationInternal) super.getByName(name);
-    }
-
-    @Override
-    public String getTypeDisplayName() {
-        return "configuration";
-    }
-
-    @Override
-    protected UnknownDomainObjectException createNotFoundException(String name) {
-        return new UnknownConfigurationException(String.format("Configuration with name '%s' not found.", name));
-    }
-
-    public ConfigurationInternal detachedConfiguration(Dependency... dependencies) {
-        String name = DETACHED_CONFIGURATION_DEFAULT_NAME + detachedConfigurationDefaultNameCounter++;
-        DetachedConfigurationsProvider detachedConfigurationsProvider = new DetachedConfigurationsProvider();
-        DefaultConfiguration detachedConfiguration = new DefaultConfiguration(
-                name, name, detachedConfigurationsProvider, resolver,
-                listenerManager, dependencyMetaDataProvider, new DefaultResolutionStrategy());
-        DomainObjectSet<Dependency> detachedDependencies = detachedConfiguration.getDependencies();
-        for (Dependency dependency : dependencies) {
-            detachedDependencies.add(dependency.copy());
-        }
-        detachedConfigurationsProvider.setTheOnlyConfiguration(detachedConfiguration);
-        return detachedConfiguration;
-    }
-    
-    /**
-     * Build a formatted representation of all Configurations in this ConfigurationContainer.
-     * Configuration(s) being toStringed are likely derivations of DefaultConfiguration.
-     */
-    public String dump() {
-        StringBuilder reply = new StringBuilder();
-        
-        reply.append("Configuration of type: " + getTypeDisplayName());
-        Collection<Configuration> configs = getAll();
-        for (Configuration c : configs) {
-            reply.append("\n  " + c.toString());
-        }
-        
-        return reply.toString();
-    }
-}
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
deleted file mode 100644
index 164cda6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.groovy
+++ /dev/null
@@ -1,67 +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.dsl
-
-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.internal.typeconversion.NotationParser
-import org.gradle.util.ConfigureUtil
-import org.gradle.util.GUtil
-
-class DefaultArtifactHandler implements ArtifactHandler {
-
-    ConfigurationContainer configurationContainer
-    NotationParser<Object, PublishArtifact> publishArtifactFactory
-
-    def DefaultArtifactHandler(ConfigurationContainer configurationContainer, NotationParser<Object, PublishArtifact> publishArtifactFactory) {
-        this.configurationContainer = configurationContainer;
-        this.publishArtifactFactory = publishArtifactFactory;
-    }
-
-    private PublishArtifact pushArtifact(org.gradle.api.artifacts.Configuration configuration, Object notation, Closure configureClosure) {
-        PublishArtifact publishArtifact = publishArtifactFactory.parseNotation(notation)
-        configuration.artifacts.add(publishArtifact)
-        ConfigureUtil.configure(configureClosure, publishArtifact)
-        return publishArtifact
-    }
-
-    PublishArtifact add(String configurationName, Object artifactNotation) {
-        return pushArtifact(configurationContainer.getByName(configurationName), artifactNotation, null)
-    }
-
-    PublishArtifact add(String configurationName, Object artifactNotation, Closure configureClosure) {
-        return pushArtifact(configurationContainer.getByName(configurationName), artifactNotation, configureClosure)
-    }
-
-    public def methodMissing(String name, args) {
-        Configuration configuration = configurationContainer.findByName(name)
-        if (configuration == null) {
-            if (!getMetaClass().respondsTo(this, name, args.size())) {
-                throw new MissingMethodException(name, this.getClass(), args);
-            }
-        }
-        Object[] normalizedArgs = GUtil.flatten(args as List, false)
-        if (normalizedArgs.length == 2 && normalizedArgs[1] instanceof Closure) {
-            return pushArtifact(configuration, normalizedArgs[0], (Closure) normalizedArgs[1])
-        } 
-        args.each {notation ->
-            pushArtifact(configuration, notation, null)
-        }
-    }
-}
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
deleted file mode 100644
index 0b717c6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java
+++ /dev/null
@@ -1,47 +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.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.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.metadata.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);
-        if (!metadata.getStatusScheme().contains(metadata.getStatus())) {
-            throw new ModuleVersionResolveException(metadata.getId(), "Unexpected status '" + metadata.getStatus() + "' specified for %s. Expected one of: " +  metadata.getStatusScheme());
-        }
-    }
-}
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
deleted file mode 100644
index b1fe488..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
+++ /dev/null
@@ -1,90 +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.dsl;
-
-import org.gradle.api.IllegalDependencyNotation;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-
-import java.util.Collection;
-import java.util.Set;
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
-
-public class ModuleVersionSelectorParsers {
-
-    public static NotationParser<Object, Set<ModuleVersionSelector>> multiParser() {
-        return builder().toFlatteningComposite();
-    }
-
-    public static NotationParser<Object, ModuleVersionSelector> parser() {
-        return builder().toComposite();
-    }
-
-    private static NotationParserBuilder<ModuleVersionSelector> builder() {
-        return new NotationParserBuilder<ModuleVersionSelector>()
-                .resultingType(ModuleVersionSelector.class)
-                .parser(new StringParser())
-                .parser(new MapParser());
-    }
-
-    static class MapParser extends MapNotationParser<ModuleVersionSelector> {
-        @Override
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Maps, e.g. [group: 'org.gradle', name:'gradle-core', version: '1.0'].");
-        }
-
-        protected ModuleVersionSelector parseMap(@MapKey("group") String group, @MapKey("name") String name, @MapKey("version") String version) {
-            return newSelector(group, name, version);
-        }
-    }
-
-    static class StringParser extends TypedNotationParser<CharSequence, ModuleVersionSelector> {
-
-        public StringParser() {
-            super(CharSequence.class);
-        }
-
-        @Override
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Strings/CharSequences, e.g. 'org.gradle:gradle-core:1.0'.");
-        }
-
-        public ModuleVersionSelector parseType(CharSequence notation) {
-            ParsedModuleStringNotation parsed;
-            try {
-                parsed = new ParsedModuleStringNotation(notation.toString(), null);
-            } catch (IllegalDependencyNotation e) {
-                throw new InvalidUserDataException(
-                        "Invalid format: '" + notation + "'. The Correct notation is a 3-part group:name:version notation, "
-                                + "e.g: 'org.gradle:gradle-core:1.0'");
-            }
-
-            if (parsed.getGroup() == null || parsed.getName() == null || parsed.getVersion() == null) {
-                throw new InvalidUserDataException(
-                        "Invalid format: '" + notation + "'. Group, name and version cannot be empty. Correct example: "
-                                + "'org.gradle:gradle-core:1.0'");
-            }
-            return newSelector(parsed.getGroup(), parsed.getName(), parsed.getVersion());
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index de16d59..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.dsl;
-
-import org.gradle.api.IllegalDependencyNotation;
-import org.gradle.util.GUtil;
-
-public class ParsedModuleStringNotation {
-    private String group;
-    private String name;
-    private String version;
-    private String classifier;
-    private String artifactType;
-
-    public ParsedModuleStringNotation(String moduleNotation, String artifactType) {
-        assignValuesFromModuleNotation(moduleNotation);
-        this.artifactType = artifactType;
-    }
-
-    private void assignValuesFromModuleNotation(String moduleNotation) {
-        String[] moduleNotationParts = moduleNotation.split(":");
-        if (moduleNotationParts.length < 2 || moduleNotationParts.length > 4) {
-            throw new IllegalDependencyNotation("The description " + moduleNotation + " is invalid");
-        }
-        group = GUtil.elvis(moduleNotationParts[0], null);
-        name = moduleNotationParts[1];
-        version = moduleNotationParts.length == 2 ? null : moduleNotationParts[2];
-        if (moduleNotationParts.length == 4) {
-            classifier = moduleNotationParts[3];
-        }
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public String getClassifier() {
-        return classifier;
-    }
-
-    public String getArtifactType() {
-        return artifactType;
-    }
-}
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
deleted file mode 100644
index 782389d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
+++ /dev/null
@@ -1,98 +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.dsl;
-
-import org.apache.tools.ant.Task;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
-import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-import org.gradle.api.tasks.bundling.AbstractArchiveTask;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.Instantiator;
-
-import java.io.File;
-import java.util.Collection;
-
-public class PublishArtifactNotationParserFactory implements Factory<NotationParser<Object, PublishArtifact>> {
-    private final Instantiator instantiator;
-    private final DependencyMetaDataProvider metaDataProvider;
-
-    public PublishArtifactNotationParserFactory(Instantiator instantiator, DependencyMetaDataProvider metaDataProvider) {
-        this.instantiator = instantiator;
-        this.metaDataProvider = metaDataProvider;
-    }
-
-    public NotationParser<Object, PublishArtifact> create() {
-        FileNotationParser fileParser = new FileNotationParser();
-        return new NotationParserBuilder<PublishArtifact>()
-                .resultingType(PublishArtifact.class)
-                .parser(new ArchiveTaskNotationParser())
-                .parser(new FileMapNotationParser(fileParser))
-                .parser(fileParser)
-                .toComposite();
-    }
-
-    private class ArchiveTaskNotationParser extends TypedNotationParser<AbstractArchiveTask, PublishArtifact> {
-        private ArchiveTaskNotationParser() {
-            super(AbstractArchiveTask.class);
-        }
-
-        @Override
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Instances of AbstractArchiveTask, e.g. jar.");
-        }
-
-        @Override
-        protected PublishArtifact parseType(AbstractArchiveTask notation) {
-            return instantiator.newInstance(ArchivePublishArtifact.class, notation);
-        }
-    }
-
-    private class FileMapNotationParser extends MapNotationParser<PublishArtifact> {
-        private final FileNotationParser fileParser;
-
-        private FileMapNotationParser(FileNotationParser fileParser) {
-            this.fileParser = fileParser;
-        }
-
-        protected PublishArtifact parseMap(@MapKey("file") File file) {
-            return fileParser.parseType(file);
-        }
-    }
-
-    private class FileNotationParser extends TypedNotationParser<File, PublishArtifact> {
-        private FileNotationParser() {
-            super(File.class);
-        }
-
-        @Override
-        protected PublishArtifact parseType(File file) {
-            Module module = metaDataProvider.getModule();
-            ArtifactFile artifactFile = new ArtifactFile(file, module.getVersion());
-            return instantiator.newInstance(DefaultPublishArtifact.class, artifactFile.getName(), artifactFile.getExtension(),
-                                            artifactFile.getExtension() == null? "":artifactFile.getExtension(),
-                                            artifactFile.getClassifier(), null, file, new Task[0]);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveContext.java
deleted file mode 100644
index 30ba366..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveContext.java
+++ /dev/null
@@ -1,21 +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;
-
-public interface ArtifactResolveContext {
-    String getId();
-    String getDescription();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveResult.java
deleted file mode 100644
index 98852c1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveResult.java
+++ /dev/null
@@ -1,35 +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;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-
-import java.io.File;
-
-public interface ArtifactResolveResult {
-    /**
-     * Returns the resolve failure, if any.
-     */
-    @Nullable
-    ArtifactResolveException getFailure();
-
-    /**
-     * @throws ArtifactResolveException If the resolution was unsuccessful.
-     */
-    File getFile() throws ArtifactResolveException;
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolver.java
deleted file mode 100644
index cfa543b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolver.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.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public interface ArtifactResolver {
-    /**
-     * Resolves a set of artifacts belonging to the given component, based on the supplied context. Any failures are packaged up in the result.
-     */
-    void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result);
-
-    /**
-     * Resolves the given artifact. Any failures are packaged up in the result.
-     */
-    // TODO:DAZ Make this less ModuleVersion centric: ModuleSource should be an attribute of the component subtype only
-    void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactSetResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactSetResolveResult.java
deleted file mode 100644
index ee9de55..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactSetResolveResult.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-
-import java.util.Set;
-
-public interface ArtifactSetResolveResult {
-    /**
-     * Returns the resolve failure, if any.
-     */
-    @Nullable
-    ArtifactResolveException getFailure();
-
-    Set<ComponentArtifactMetaData> getArtifacts();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeResolveContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeResolveContext.java
deleted file mode 100644
index 4854995..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeResolveContext.java
+++ /dev/null
@@ -1,38 +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;
-
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-
-public class ArtifactTypeResolveContext implements ArtifactResolveContext {
-    private final Class<? extends SoftwareArtifact> artifactType;
-
-    public ArtifactTypeResolveContext(Class<? extends SoftwareArtifact> artifactType) {
-        this.artifactType = artifactType;
-    }
-
-    public Class<? extends SoftwareArtifact> getArtifactType() {
-        return artifactType;
-    }
-
-    public String getId() {
-        return "artifacts:" + artifactType.getName();
-    }
-
-    public String getDescription() {
-        return String.format("artifacts of type %s", artifactType);
-    }
-}
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
deleted file mode 100644
index 3a9611f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactResolveResult.java
+++ /dev/null
@@ -1,39 +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;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier;
-
-import java.io.File;
-
-public interface BuildableArtifactResolveResult extends ArtifactResolveResult {
-    /**
-     * Marks the module version as resolved, with the given artifact resolver.
-     */
-    void resolved(File file);
-
-    /**
-     * Marks the resolve as failed with the given exception.
-     */
-    void failed(ArtifactResolveException failure);
-
-    /**
-     * Marks the artifact as not found.
-     */
-    void notFound(ComponentArtifactIdentifier artifact);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactSetResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactSetResolveResult.java
deleted file mode 100644
index c601afd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableArtifactSetResolveResult.java
+++ /dev/null
@@ -1,29 +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;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-
-import java.util.Collection;
-
-public interface BuildableArtifactSetResolveResult extends ArtifactSetResolveResult {
-    void resolved(Collection<? extends ComponentArtifactMetaData> artifacts);
-
-    void failed(ArtifactResolveException failure);
-
-    boolean hasResult();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableComponentResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableComponentResolveResult.java
deleted file mode 100644
index b402741..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/BuildableComponentResolveResult.java
+++ /dev/null
@@ -1,42 +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;
-
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public interface BuildableComponentResolveResult extends ComponentResolveResult {
-    /**
-     * Marks the component as resolved, with the given meta-data.
-     */
-    void resolved(ComponentMetaData metaData);
-
-    /**
-     * Marks the resolve as failed with the given exception.
-     */
-    BuildableComponentResolveResult failed(ModuleVersionResolveException failure);
-
-    /**
-     * Marks the component as not found.
-     */
-    void notFound(ModuleVersionSelector versionSelector);
-
-    /**
-     * Replaces the meta-data in the result. Result must already be resolved.
-     */
-    void setMetaData(ComponentMetaData metaData);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
deleted file mode 100644
index 4c6bbb4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
+++ /dev/null
@@ -1,59 +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;
-
-import org.gradle.util.VersionNumber;
-
-import java.io.File;
-
-public enum CacheLayout {
-    ROOT(null, "modules", 2),
-    FILE_STORE(ROOT, "files", 1),
-    META_DATA(ROOT, "metadata", 6);
-
-    private final String name;
-    private final CacheLayout parent;
-    private final int version;
-
-    private CacheLayout(CacheLayout parent, String name, int version) {
-        this.parent = parent;
-        this.name = name;
-        this.version = version;
-    }
-
-    public VersionNumber getVersion() {
-        return VersionNumber.parse(getFormattedVersion());
-    }
-
-    public String getKey() {
-        StringBuilder key = new StringBuilder();
-        key.append(name);
-        key.append("-");
-        key.append(getFormattedVersion());
-        return key.toString();
-    }
-
-    public String getFormattedVersion() {
-        if (parent == null) {
-            return String.valueOf(version);
-        }
-        return parent.getFormattedVersion() + '.' + String.valueOf(version);
-    }
-
-    public File getPath(File parentDir) {
-        return new File(parentDir, getKey());
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java
deleted file mode 100644
index 5146b05..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-
-import java.util.List;
-
-public class CacheLockingArtifactDependencyResolver implements ArtifactDependencyResolver {
-    private final CacheLockingManager lockingManager;
-    private final ArtifactDependencyResolver resolver;
-
-    public CacheLockingArtifactDependencyResolver(CacheLockingManager lockingManager, ArtifactDependencyResolver resolver) {
-        this.lockingManager = lockingManager;
-        this.resolver = resolver;
-    }
-
-    public void resolve(final ConfigurationInternal configuration,
-                                   final List<? extends ResolutionAwareRepository> repositories,
-                                   final ModuleMetadataProcessor metadataProcessor,
-                                   final ResolverResults results) throws ResolveException {
-        lockingManager.useCache(String.format("resolve %s", configuration), new Runnable() {
-            public void run() {
-                resolver.resolve(configuration, repositories, metadataProcessor, results);
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java
deleted file mode 100644
index f694329..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java
+++ /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.artifacts.ivyservice;
-
-import net.jcip.annotations.ThreadSafe;
-import org.gradle.cache.CacheAccess;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.io.File;
-
-/**
- * Provides synchronized access to the artifact cache.
- */
- at ThreadSafe
-public interface CacheLockingManager extends ArtifactCacheMetaData, CacheAccess {
-    /**
-     * Creates a cache implementation that is managed by this locking manager. This method may be used at any time.
-     *
-     * <p>The returned cache may only be used by an action being run from {@link #useCache(String, org.gradle.internal.Factory)}.
-     * In this instance, an exclusive lock will be held on the cache.
-     *
-     * <p>The returned cache may not be used by an action being run from {@link #longRunningOperation(String, org.gradle.internal.Factory)}.
-     */
-    <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer);
-
-    /**
-     * Returns the root directory for the file store.
-     *
-     * @return File store location
-     */
-    File getFileStoreDirectory();
-
-    /**
-     * Returns the root directory for the meta-data file store.
-     *
-     * @return Metadata store location
-     */
-    File createMetaDataStore();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ComponentResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ComponentResolveResult.java
deleted file mode 100644
index 5f28733..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ComponentResolveResult.java
+++ /dev/null
@@ -1,42 +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;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public interface ComponentResolveResult {
-    /**
-     * Returns the module version id of the component.
-     *
-     * @throws ModuleVersionResolveException If resolution was unsuccessful and the id is unknown.
-     */
-    ModuleVersionIdentifier getId() throws ModuleVersionResolveException;
-
-    /**
-     * Returns the meta-data for the component.
-     *
-     * @throws ModuleVersionResolveException If resolution was unsuccessful and the descriptor is not available.
-     */
-    ComponentMetaData getMetaData() throws ModuleVersionResolveException;
-
-    /**
-     * Returns the resolve failure, if any.
-     */
-    @Nullable
-    ModuleVersionResolveException getFailure();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ConfigurationResolveContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ConfigurationResolveContext.java
deleted file mode 100644
index c922e2b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ConfigurationResolveContext.java
+++ /dev/null
@@ -1,41 +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;
-
-public class ConfigurationResolveContext implements ArtifactResolveContext {
-    private final String configurationName;
-
-    public ConfigurationResolveContext(String configurationName) {
-        this.configurationName = configurationName;
-    }
-
-    public String getConfigurationName() {
-        return configurationName;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("configuration '%s'", configurationName);
-    }
-
-    public String getId() {
-        return "configuration:" + configurationName;
-    }
-
-    public String getDescription() {
-        return String.format("artifacts for configuration '%s'", configurationName);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.java
deleted file mode 100644
index c503663..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.java
+++ /dev/null
@@ -1,58 +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.Ivy;
-import org.gradle.api.Action;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public class ContextualArtifactResolver implements ArtifactResolver {
-    private final CacheLockingManager lockingManager;
-    private final IvyContextManager ivyContextManager;
-    private final ArtifactResolver delegate;
-
-    public ContextualArtifactResolver(CacheLockingManager lockingManager, IvyContextManager ivyContextManager, ArtifactResolver delegate) {
-        this.lockingManager = lockingManager;
-        this.ivyContextManager = ivyContextManager;
-        this.delegate = delegate;
-    }
-
-    public void resolveModuleArtifacts(final ComponentMetaData component, final ArtifactResolveContext context, final BuildableArtifactSetResolveResult result) {
-        lockingManager.useCache(String.format("Resolve %s for %s", context.getDescription(), component), new Runnable() {
-            public void run() {
-                ivyContextManager.withIvy(new Action<Ivy>() {
-                    public void execute(Ivy ivy) {
-                        delegate.resolveModuleArtifacts(component, context, result);
-                    }
-                });
-            }
-        });
-    }
-
-    public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
-        lockingManager.useCache(String.format("Resolve %s", artifact), new Runnable() {
-            public void run() {
-                ivyContextManager.withIvy(new Action<Ivy>() {
-                    public void execute(Ivy ivy) {
-                        delegate.resolveArtifact(artifact, moduleSource, result);
-                    }
-                });
-            }
-        });
-    }
-}
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
deleted file mode 100644
index 3607724..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResult.java
+++ /dev/null
@@ -1,63 +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;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactNotFoundException;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier;
-
-import java.io.File;
-
-public class DefaultBuildableArtifactResolveResult implements BuildableArtifactResolveResult {
-    private ArtifactResolveException failure;
-    private File file;
-
-    public void failed(ArtifactResolveException failure) {
-        this.failure = failure;
-    }
-
-    public void resolved(File file) {
-        this.file = file;
-    }
-
-    public void notFound(ComponentArtifactIdentifier artifact) {
-        failed(new ArtifactNotFoundException(artifact));
-    }
-
-    public ArtifactResolveException getFailure() {
-        assertHasResult();
-        return failure;
-    }
-
-    public File getFile() throws ArtifactResolveException {
-        assertResolved();
-        return file;
-    }
-
-    private void assertResolved() {
-        assertHasResult();
-        if (failure != null) {
-            throw failure;
-        }
-    }
-
-    private void assertHasResult() {
-        if (failure == null && file == null) {
-            throw new IllegalStateException("No result has been specified.");
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResult.java
deleted file mode 100644
index 4fc532e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResult.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ArtifactResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class DefaultBuildableArtifactSetResolveResult implements BuildableArtifactSetResolveResult {
-    private ArtifactResolveException failure;
-    private Set<ComponentArtifactMetaData> artifacts;
-
-    public void resolved(Collection<? extends ComponentArtifactMetaData> artifacts) {
-        this.artifacts = new LinkedHashSet<ComponentArtifactMetaData>(artifacts);
-    }
-
-    public void failed(ArtifactResolveException failure) {
-        this.failure = failure;
-    }
-
-    public boolean hasResult() {
-        return artifacts != null || failure != null;
-    }
-
-    public ArtifactResolveException getFailure() {
-        assertHasResult();
-        return failure;
-    }
-
-    public Set<ComponentArtifactMetaData> getArtifacts() {
-        assertResolved();
-        return artifacts;
-    }
-
-    private void assertResolved() {
-        assertHasResult();
-        if (failure != null) {
-            throw failure;
-        }
-    }
-
-    private void assertHasResult() {
-        if (!hasResult()) {
-            throw new IllegalStateException("No result has been specified.");
-        }
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResult.java
deleted file mode 100644
index 57eb6c9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResult.java
+++ /dev/null
@@ -1,73 +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;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public class DefaultBuildableComponentResolveResult implements BuildableComponentResolveResult {
-    private ComponentMetaData metaData;
-    private ModuleVersionResolveException failure;
-
-    public DefaultBuildableComponentResolveResult failed(ModuleVersionResolveException failure) {
-        metaData = null;
-        this.failure = failure;
-        return this;
-    }
-
-    public void notFound(ModuleVersionSelector versionSelector) {
-        failed(new ModuleVersionNotFoundException(versionSelector));
-    }
-
-    public void resolved(ComponentMetaData metaData) {
-        this.metaData = metaData;
-    }
-
-    public void setMetaData(ComponentMetaData metaData) {
-        assertResolved();
-        this.metaData = metaData;
-    }
-
-    public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
-        assertResolved();
-        return metaData.getId();
-    }
-
-    public ComponentMetaData getMetaData() throws ModuleVersionResolveException {
-        assertResolved();
-        return metaData;
-    }
-
-    public ModuleVersionResolveException getFailure() {
-        assertHasResult();
-        return failure;
-    }
-
-    private void assertResolved() {
-        assertHasResult();
-        if (failure != null) {
-            throw failure;
-        }
-    }
-
-    private void assertHasResult() {
-        if (failure == null && metaData == null) {
-            throw new IllegalStateException("No result has been specified.");
-        }
-    }
-}
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
deleted file mode 100644
index f28845f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.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.ivyservice;
-
-import org.gradle.cache.CacheRepository;
-import org.gradle.cache.PersistentCache;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.cache.PersistentIndexedCacheParameters;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.internal.Factory;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.util.VersionNumber;
-
-import java.io.File;
-
-import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
-
-public class DefaultCacheLockingManager implements CacheLockingManager {
-
-    // If you update this, also update DefaultGradleDistribution.getArtifactCacheLayoutVersion() (which is the historical record)
-    // You should also update LocallyAvailableResourceFinderFactory
-    public static final VersionNumber CACHE_LAYOUT_VERSION = CacheLayout.META_DATA.getVersion();
-
-    private final PersistentCache cache;
-
-    public DefaultCacheLockingManager(CacheRepository cacheRepository) {
-        cache = cacheRepository
-                .store(CacheLayout.ROOT.getKey())
-                .withCrossVersionCache()
-                .withDisplayName("artifact cache")
-                .withLockOptions(mode(FileLockManager.LockMode.None)) // Don't need to lock anything until we use the caches
-                .open();
-    }
-
-    public void close() {
-        cache.close();
-    }
-
-    public File getCacheDir() {
-        return cache.getBaseDir();
-    }
-
-    public void longRunningOperation(String operationDisplayName, final Runnable action) {
-        cache.longRunningOperation(operationDisplayName, action);
-    }
-
-    public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
-        return cache.useCache(operationDisplayName, action);
-    }
-
-    public void useCache(String operationDisplayName, Runnable action) {
-        cache.useCache(operationDisplayName, action);
-    }
-
-    public <T> T longRunningOperation(String operationDisplayName, Factory<? extends T> action) {
-        return cache.longRunningOperation(operationDisplayName, action);
-    }
-
-    public <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
-        String cacheFileInMetaDataStore = CacheLayout.META_DATA.getKey() + "/" + cacheName;
-        return cache.createCache(new PersistentIndexedCacheParameters<K, V>(cacheFileInMetaDataStore, keySerializer, valueSerializer));
-    }
-
-    public File getFileStoreDirectory() {
-        return createCacheRelativeDir(CacheLayout.FILE_STORE);
-    }
-
-    public File createMetaDataStore() {
-        return new File(createCacheRelativeDir(CacheLayout.META_DATA), "descriptors");
-    }
-
-    private File createCacheRelativeDir(CacheLayout cacheLayout) {
-        return cacheLayout.getPath(cache.getBaseDir());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java
deleted file mode 100644
index 5af74e6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.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.ivyservice;
-
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.internal.Transformers;
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
-import org.gradle.api.internal.artifacts.ConfigurationResolver;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.util.CollectionUtils;
-
-import java.util.List;
-
-public class DefaultConfigurationResolver implements ConfigurationResolver {
-    private final ArtifactDependencyResolver resolver;
-    private final RepositoryHandler repositories;
-    private final ModuleMetadataProcessor metadataProcessor;
-
-    public DefaultConfigurationResolver(ArtifactDependencyResolver resolver, RepositoryHandler repositories, ModuleMetadataProcessor metadataProcessor) {
-        this.resolver = resolver;
-        this.repositories = repositories;
-        this.metadataProcessor = metadataProcessor;
-    }
-
-    public ResolverResults resolve(ConfigurationInternal configuration) throws ResolveException {
-        List<ResolutionAwareRepository> resolutionAwareRepositories = CollectionUtils.collect(repositories, Transformers.cast(ResolutionAwareRepository.class));
-        ResolverResults results = new ResolverResults();
-        resolver.resolve(configuration, resolutionAwareRepositories, metadataProcessor, results);
-        return results;
-    }
-}
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
deleted file mode 100644
index 11349ec..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
+++ /dev/null
@@ -1,72 +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;
-
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
-import org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
-
-public class DefaultDependencyResolveDetails implements DependencyResolveDetailsInternal {
-    private final ModuleVersionSelector requested;
-    private ComponentSelectionReason selectionReason;
-    private ModuleVersionSelector target;
-
-    public DefaultDependencyResolveDetails(ModuleVersionSelector requested) {
-        this.requested = requested;
-        this.target = requested;
-    }
-
-    public ModuleVersionSelector getRequested() {
-        return requested;
-    }
-
-    public void useVersion(String version) {
-        useVersion(version, VersionSelectionReasons.SELECTED_BY_RULE);
-    }
-
-    public void useVersion(String version, ComponentSelectionReason selectionReason) {
-        assert selectionReason != null;
-        if (version == null) {
-            throw new IllegalArgumentException("Configuring the dependency resolve details with 'null' version is not allowed.");
-        }
-        if (!version.equals(target.getVersion())) {
-            target = newSelector(target.getGroup(), target.getName(), version);
-        }
-        this.selectionReason = selectionReason;
-    }
-
-    public void useTarget(Object notation) {
-        this.target = ModuleVersionSelectorParsers.parser().parseNotation(notation);
-        this.selectionReason = VersionSelectionReasons.SELECTED_BY_RULE;
-    }
-
-    public ComponentSelectionReason getSelectionReason() {
-        return selectionReason;
-    }
-
-    public ModuleVersionSelector getTarget() {
-        return target;
-    }
-
-    public boolean isUpdated() {
-        return selectionReason != null;
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 2dcb6b9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.metadata.*;
-import org.gradle.util.DeprecationLogger;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-public class DefaultIvyDependencyPublisher implements IvyDependencyPublisher {
-    private static Logger logger = LoggerFactory.getLogger(DefaultIvyDependencyPublisher.class);
-
-    public void publish(List<ModuleVersionPublisher> publishResolvers,
-                        ModuleVersionPublishMetaData publishMetaData) {
-        try {
-            // Make a copy of the publication and filter missing artifacts
-            DefaultModuleVersionPublishMetaData publication = new DefaultModuleVersionPublishMetaData(publishMetaData.getId());
-            for (ModuleVersionArtifactPublishMetaData artifact: publishMetaData.getArtifacts()) {
-                addPublishedArtifact(artifact, publication);
-            }
-            for (ModuleVersionPublisher publisher : publishResolvers) {
-                logger.info("Publishing to {}", publisher);
-                publisher.publish(publication);
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private void addPublishedArtifact(ModuleVersionArtifactPublishMetaData artifact, BuildableModuleVersionPublishMetaData publication) {
-        if (checkArtifactFileExists(artifact)) {
-            publication.addArtifact(artifact);
-        }
-    }
-
-    private boolean checkArtifactFileExists(ModuleVersionArtifactPublishMetaData artifact) {
-        File artifactFile = artifact.getFile();
-        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.getArtifactName())) {
-            String message = String.format("Attempted to publish an artifact '%s' that does not exist '%s'", artifact.getId(), artifactFile);
-            DeprecationLogger.nagUserOfDeprecatedBehaviour(message);
-        }
-        return false;
-    }
-
-    private boolean isSigningArtifact(IvyArtifactName artifact) {
-        return artifact.getType().endsWith(".asc") || artifact.getType().endsWith(".sig");
-    }
-}
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
deleted file mode 100644
index e296b08..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
+++ /dev/null
@@ -1,169 +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.*;
-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 LenientConfiguration {
-    private CacheLockingManager cacheLockingManager;
-    private final Configuration configuration;
-    private ResolvedConfigurationResults results;
-
-    public DefaultLenientConfiguration(Configuration configuration, ResolvedConfigurationResults results, CacheLockingManager cacheLockingManager) {
-        this.configuration = configuration;
-        this.results = results;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    public boolean hasError() {
-        return results.hasError();
-    }
-
-    public void rethrowFailure() throws ResolveException {
-        if (hasError()) {
-            List<Throwable> failures = new ArrayList<Throwable>();
-            for (UnresolvedDependency unresolvedDependency : results.getUnresolvedDependencies()) {
-                failures.add(unresolvedDependency.getProblem());
-            }
-            throw new ResolveException(configuration, failures);
-        }
-    }
-
-    public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
-        return results.getUnresolvedDependencies();
-    }
-
-    public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
-        return results.getArtifacts();
-    }
-
-    public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
-        Set<ResolvedDependency> matches = new LinkedHashSet<ResolvedDependency>();
-        for (Map.Entry<ModuleDependency, ResolvedDependency> entry : results.more().getFirstLevelDependencies().entrySet()) {
-            if (dependencySpec.isSatisfiedBy(entry.getKey())) {
-                matches.add(entry.getValue());
-            }
-        }
-        return matches;
-    }
-
-    public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
-        Set<ResolvedArtifact> artifacts = getArtifacts(dependencySpec);
-        return getFiles(artifacts);
-    }
-
-    public Set<File> getFilesStrict(Spec<? super Dependency> dependencySpec) {
-        Set<ResolvedArtifact> artifacts = getAllArtifacts(dependencySpec);
-        return getFiles(artifacts);
-    }
-
-    /**
-     * Recursive but excludes unsuccessfully resolved artifacts.
-     *
-     * @param dependencySpec dependency spec
-     */
-    public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
-        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(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;
-    }
-
-    /**
-     * Recursive, includes unsuccessfully resolved artifacts
-     *
-     * @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(results.more().getRoot()));
-            walker.add(resolvedDependency);
-        }
-
-        artifacts.addAll(walker.findValues());
-        return artifacts;
-    }
-
-    public Configuration getConfiguration() {
-        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<? super ResolvedArtifact> values,
-                                  Collection<? super ResolvedDependency> connectedNodes) {
-            connectedNodes.addAll(node.getChildren());
-        }
-
-        public void getEdgeValues(ResolvedDependency from, ResolvedDependency to,
-                                  Collection<ResolvedArtifact> values) {
-            values.addAll(to.getParentArtifacts(from));
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java
deleted file mode 100755
index 3348705..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java
+++ /dev/null
@@ -1,47 +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.ModuleVersionSelector;
-import org.gradle.api.artifacts.UnresolvedDependency;
-import org.gradle.util.DeprecationLogger;
-
-public class DefaultUnresolvedDependency implements UnresolvedDependency {
-    private final Throwable problem;
-    private final ModuleVersionSelector selector;
-
-    public DefaultUnresolvedDependency(ModuleVersionSelector selector, Throwable problem) {
-        this.selector = selector;
-        this.problem = problem;
-    }
-
-    public String getId() {
-        DeprecationLogger.nagUserOfReplacedMethod("UnresolvedDependency.getId()", "UnresolvedDependency.getSelector()");
-        return IvyUtil.createModuleRevisionId(selector.getGroup(), selector.getName(), selector.getVersion()).toString();
-    }
-
-    public ModuleVersionSelector getSelector() {
-        return selector;
-    }
-
-    public Throwable getProblem() {
-        return problem;
-    }
-
-    public String toString() {
-        return selector.getGroup() + ":" + selector.getName() + ":" + selector.getVersion();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionIdResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionIdResolver.java
deleted file mode 100755
index 92a30d8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionIdResolver.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.artifacts.ivyservice;
-
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-/**
- * Resolves a dependency to the id for a module.
- */
-public interface DependencyToModuleVersionIdResolver {
-    /**
-     * Resolves the given dependency to a module version id. Note that failures are packaged up into the result.
-     */
-    ModuleVersionIdResolveResult resolve(DependencyMetaData dependencyDescriptor);
-}
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
deleted file mode 100755
index 1a86684..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencyToModuleVersionResolver.java
+++ /dev/null
@@ -1,28 +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;
-
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-/**
- * Resolves a dependency to a component instance.
- */
-public interface DependencyToModuleVersionResolver {
-    /**
-     * Resolves the given dependency to component instance. Failures are packaged up in the returned result.
-     */
-    void resolve(DependencyMetaData dependency, BuildableComponentResolveResult 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
deleted file mode 100644
index 97e8832..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
+++ /dev/null
@@ -1,256 +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 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.ResolvedComponentResult;
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.specs.Spec;
-
-import java.io.File;
-import java.util.List;
-import java.util.Set;
-
-public class ErrorHandlingArtifactDependencyResolver implements ArtifactDependencyResolver {
-    private final ArtifactDependencyResolver dependencyResolver;
-
-    public ErrorHandlingArtifactDependencyResolver(ArtifactDependencyResolver dependencyResolver) {
-        this.dependencyResolver = dependencyResolver;
-    }
-
-    public void resolve(ConfigurationInternal configuration,
-                        List<? extends ResolutionAwareRepository> repositories,
-                        ModuleMetadataProcessor metadataProcessor,
-                        ResolverResults results) throws ResolveException {
-        try {
-            dependencyResolver.resolve(configuration, repositories, metadataProcessor, results);
-        } catch (final Throwable e) {
-            results.failed(new BrokenResolvedConfiguration(e, configuration), wrapException(e, configuration));
-            return;
-        }
-        ResolvedConfiguration wrappedConfiguration = new ErrorHandlingResolvedConfiguration(results.getResolvedConfiguration(), configuration);
-        ResolutionResult wrappedResult = new ErrorHandlingResolutionResult(results.getResolutionResult(), configuration);
-        results.resolved(wrappedConfiguration, wrappedResult);
-    }
-
-    private static ResolveException wrapException(Throwable e, Configuration configuration) {
-        if (e instanceof ResolveException) {
-            return (ResolveException) e;
-        }
-        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 ResolvedComponentResult 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<ResolvedComponentResult> getAllComponents() {
-            try {
-                return resolutionResult.getAllComponents();
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public void allComponents(Action<? super ResolvedComponentResult> action) {
-            resolutionResult.allComponents(action);
-        }
-
-        public void allComponents(Closure closure) {
-            resolutionResult.allComponents(closure);
-        }
-    }
-    
-    private static class ErrorHandlingResolvedConfiguration implements ResolvedConfiguration {
-        private final ResolvedConfiguration resolvedConfiguration;
-        private final Configuration configuration;
-
-        public ErrorHandlingResolvedConfiguration(ResolvedConfiguration resolvedConfiguration,
-                                                  Configuration configuration) {
-            this.resolvedConfiguration = resolvedConfiguration;
-            this.configuration = configuration;
-        }
-
-        public boolean hasError() {
-            return resolvedConfiguration.hasError();
-        }
-
-        public LenientConfiguration getLenientConfiguration() {
-            try {
-                return new ErrorHandlingLenientConfiguration(resolvedConfiguration.getLenientConfiguration(), configuration);
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public void rethrowFailure() throws ResolveException {
-            try {
-                resolvedConfiguration.rethrowFailure();
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            try {
-                return resolvedConfiguration.getFiles(dependencySpec);
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
-            try {
-                return resolvedConfiguration.getFirstLevelModuleDependencies();
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            try {
-                return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-
-        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
-            try {
-                return resolvedConfiguration.getResolvedArtifacts();
-            } catch (Throwable e) {
-                throw wrapException(e, configuration);
-            }
-        }
-    }
-
-    private static class BrokenResolvedConfiguration implements ResolvedConfiguration {
-        private final Throwable e;
-        private final Configuration configuration;
-
-        public BrokenResolvedConfiguration(Throwable e, Configuration configuration) {
-            this.e = e;
-            this.configuration = configuration;
-        }
-
-        public boolean hasError() {
-            return true;
-        }
-
-        public LenientConfiguration getLenientConfiguration() {
-            throw wrapException(e, configuration);
-        }
-
-        public void rethrowFailure() throws ResolveException {
-            throw wrapException(e, configuration);
-        }
-
-        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            throw wrapException(e, configuration);
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
-            throw wrapException(e, configuration);
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            throw wrapException(e, configuration);
-        }
-
-        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
-            throw wrapException(e, configuration);
-        }
-    }
-}
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
deleted file mode 100644
index f6a2f9a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
+++ /dev/null
@@ -1,84 +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.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.MDArtifact;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.PublishException;
-import org.gradle.api.internal.artifacts.ArtifactPublisher;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.metadata.BuildableModuleVersionPublishMetaData;
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData;
-import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class IvyBackedArtifactPublisher implements ArtifactPublisher {
-    private final LocalComponentFactory publishLocalComponentFactory;
-    private final IvyContextManager ivyContextManager;
-    private final IvyDependencyPublisher dependencyPublisher;
-    private final IvyModuleDescriptorWriter ivyModuleDescriptorWriter;
-
-    public IvyBackedArtifactPublisher(LocalComponentFactory publishLocalComponentFactory,
-                                      IvyContextManager ivyContextManager,
-                                      IvyDependencyPublisher dependencyPublisher,
-                                      IvyModuleDescriptorWriter ivyModuleDescriptorWriter) {
-        this.publishLocalComponentFactory = publishLocalComponentFactory;
-        this.ivyContextManager = ivyContextManager;
-        this.dependencyPublisher = dependencyPublisher;
-        this.ivyModuleDescriptorWriter = ivyModuleDescriptorWriter;
-    }
-
-    public void publish(final Iterable<? extends PublicationAwareRepository> repositories, final ModuleInternal module, final Configuration configuration, final File descriptor) throws PublishException {
-        ivyContextManager.withIvy(new Action<Ivy>() {
-            public void execute(Ivy ivy) {
-                Set<Configuration> allConfigurations = configuration.getAll();
-                Set<Configuration> configurationsToPublish = configuration.getHierarchy();
-
-                MutableLocalComponentMetaData componentMetaData = publishLocalComponentFactory.convert(allConfigurations, module);
-                if (descriptor != null) {
-                    ModuleDescriptor moduleDescriptor = componentMetaData.getModuleDescriptor();
-                    ivyModuleDescriptorWriter.write(moduleDescriptor, descriptor);
-                }
-
-                // Need to convert a second time, to determine which artifacts to publish (and yes, this isn't a great way to do things...)
-                componentMetaData = publishLocalComponentFactory.convert(configurationsToPublish, module);
-                BuildableModuleVersionPublishMetaData publishMetaData = componentMetaData.toPublishMetaData();
-                if (descriptor != null) {
-                    Artifact artifact = MDArtifact.newIvyArtifact(componentMetaData.getModuleDescriptor());
-                    publishMetaData.addArtifact(artifact, descriptor);
-                }
-
-                List<ModuleVersionPublisher> publishResolvers = new ArrayList<ModuleVersionPublisher>();
-                for (PublicationAwareRepository repository : repositories) {
-                    ModuleVersionPublisher publisher = repository.createPublisher();
-                    publisher.setSettings(ivy.getSettings());
-                    publishResolvers.add(publisher);
-                }
-
-                dependencyPublisher.publish(publishResolvers, publishMetaData);
-            }
-        });
-    }
-}
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
deleted file mode 100644
index f8d788f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.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.gradle.api.internal.artifacts.metadata.ModuleVersionPublishMetaData;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-
-import java.util.List;
-
-public interface IvyDependencyPublisher {
-    void publish(List<ModuleVersionPublisher> publishResolvers,
-                 ModuleVersionPublishMetaData publishMetaData);
-}
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
deleted file mode 100644
index 5fb10c3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyResolverBackedModuleVersionPublisher.java
+++ /dev/null
@@ -1,67 +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;
-
-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.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactPublishMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionPublishMetaData;
-
-import java.io.File;
-import java.io.IOException;
-
-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 = IvyUtil.createModuleRevisionId(id.getGroup(), id.getName(), id.getVersion());
-            resolver.beginPublishTransaction(ivyId, true);
-            for (ModuleVersionArtifactPublishMetaData artifactMetaData : moduleVersion.getArtifacts()) {
-                Artifact artifact = artifactMetaData.toIvyArtifact();
-                File artifactFile = artifactMetaData.getFile();
-                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/IvyUtil.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
deleted file mode 100644
index 2e95e50..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice;
-
-import org.apache.ivy.core.module.id.ModuleId;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.util.GUtil;
-
-import java.util.Map;
-
-import static java.util.Collections.emptyMap;
-
-public class IvyUtil {
-
-    private static final Object MODULE_ID_LOCK = new Object(); //see GRADLE-3027
-
-    public static ModuleRevisionId createModuleRevisionId(Module module) {
-        return createModuleRevisionId(module.getGroup(), module.getName(), module.getVersion());
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(Dependency dependency) {
-        return createModuleRevisionId(dependency.getGroup(), dependency.getName(), dependency.getVersion());
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(String group, String name, String version) {
-        return createModuleRevisionId(emptyStringIfNull(group), name, null, emptyStringIfNull(version), emptyMap());
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(ModuleVersionIdentifier id) {
-        return createModuleRevisionId(id.getGroup(), id.getName(), id.getVersion());
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(ModuleComponentIdentifier id) {
-        return createModuleRevisionId(id.getGroup(), id.getModule(), id.getVersion());
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(ModuleRevisionId revId, String version) {
-        return createModuleRevisionId(revId.getOrganisation(), revId.getName(), revId.getBranch(), version, revId.getQualifiedExtraAttributes());
-    }
-
-    private static String emptyStringIfNull(String value) {
-        return GUtil.elvis(value, "");
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(String org, String name, String branch, String rev, Map extraAttributes) {
-        return createModuleRevisionId(org, name, branch, rev, extraAttributes, true);
-    }
-
-    public static ModuleRevisionId createModuleRevisionId(String org, String name, String branch, String revConstraint, Map extraAttributes, boolean replaceNullBranchWithDefault) {
-        synchronized (MODULE_ID_LOCK) {
-            return ModuleRevisionId.newInstance(org, name, branch, revConstraint, extraAttributes, replaceNullBranchWithDefault);
-        }
-    }
-
-    public static ModuleId createModuleId(String org, String name) {
-        synchronized (MODULE_ID_LOCK) {
-            return ModuleId.newInstance(org, name);
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 2a57bdb..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
+++ /dev/null
@@ -1,402 +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;
-
-import com.google.common.base.Joiner;
-import org.apache.ivy.core.module.descriptor.*;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.util.extendable.ExtendableItem;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.xml.SimpleXmlWriter;
-import org.gradle.internal.UncheckedException;
-
-import java.io.*;
-import java.lang.reflect.Field;
-import java.text.SimpleDateFormat;
-import java.util.*;
-
-public class IvyXmlModuleDescriptorWriter implements IvyModuleDescriptorWriter {
-    public static final String IVY_DATE_PATTERN = "yyyyMMddHHmmss";
-    private final Field dependencyConfigField;
-
-    public IvyXmlModuleDescriptorWriter() {
-        try {
-            dependencyConfigField = DefaultDependencyDescriptor.class.getDeclaredField("confs");
-        } catch (NoSuchFieldException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-        dependencyConfigField.setAccessible(true);
-    }
-
-    private void writeTo(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        writer.startElement("ivy-module");
-        writer.attribute("version", "2.0");
-
-        Map<String, String> namespaces = md.getExtraAttributesNamespaces();
-        for (Map.Entry<String, String> entry : namespaces.entrySet()) {
-            writer.attribute("xmlns:" + entry.getKey(), entry.getValue());
-        }
-
-        printInfoTag(md, writer);
-        printConfigurations(md, writer);
-        printPublications(md, writer);
-        printDependencies(md, writer);
-
-        writer.endElement();
-    }
-
-    public void write(ModuleDescriptor md, File output) {
-        try {
-            output.getParentFile().mkdirs();
-            OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
-            try {
-                SimpleXmlWriter xmlWriter = new SimpleXmlWriter(outputStream, "  ");
-                writeTo(md, xmlWriter);
-                xmlWriter.flush();
-            } finally {
-                outputStream.close();
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private void printDependencies(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        DependencyDescriptor[] dds = md.getDependencies();
-        if (dds.length > 0) {
-            writer.startElement("dependencies");
-            for (int i = 0; i < dds.length; i++) {
-                DependencyDescriptor dep = dds[i];
-                printDependency(md, dep, writer);
-            }
-            printAllExcludes(md, writer);
-            writer.endElement();
-        }
-    }
-
-    protected void printDependency(ModuleDescriptor md, DependencyDescriptor dep,
-                                          SimpleXmlWriter writer) throws IOException {
-        writer.startElement("dependency");
-
-        ModuleRevisionId dependencyRevisionId = dep.getDependencyRevisionId();
-        writer.attribute("org", dependencyRevisionId.getOrganisation());
-        writer.attribute("name", dependencyRevisionId.getName());
-        if (dependencyRevisionId.getBranch() != null) {
-            writer.attribute("branch", dependencyRevisionId.getBranch());
-        }
-        writer.attribute("rev", dependencyRevisionId.getRevision());
-        if (!dep.getDynamicConstraintDependencyRevisionId().equals(dependencyRevisionId)) {
-            if (dep.getDynamicConstraintDependencyRevisionId().getBranch() != null) {
-                writer.attribute("branchConstraint", dep.getDynamicConstraintDependencyRevisionId().getBranch());
-            }
-            writer.attribute("revConstraint", dep.getDynamicConstraintDependencyRevisionId().getRevision());
-        }
-        if (dep.isForce()) {
-            writer.attribute("force", "true");
-        }
-        if (dep.isChanging()) {
-            writer.attribute("changing", "true");
-        }
-        if (!dep.isTransitive()) {
-            writer.attribute("transitive", "false");
-        }
-        writer.attribute("conf", getConfMapping(dep));
-
-        printExtraAttributes(dep, writer);
-
-        DependencyArtifactDescriptor[] depArtifacts = dep.getAllDependencyArtifacts();
-        printDependencyArtefacts(md, writer, depArtifacts);
-
-        IncludeRule[] includes = dep.getAllIncludeRules();
-        printDependencyIncludeRules(md, writer, includes);
-
-        ExcludeRule[] excludes = dep.getAllExcludeRules();
-        printDependencyExcludeRules(md, writer, excludes);
-
-        writer.endElement();
-    }
-
-    private String getConfMapping(DependencyDescriptor dep) {
-        StringBuilder confs = new StringBuilder();
-        String[] modConfs = dep.getModuleConfigurations();
-
-        Map<String, List<String>> configMappings;
-        if (dep instanceof DefaultDependencyDescriptor) {
-            // The `getDependencyConfigurations()` implementation for DefaultDependencyDescriptor does some interpretation of the RHS of the configuration
-            // mappings, and gets it wrong for mappings such as '*->@' pr '*->#'. So, instead, reach into the descriptor and get the raw mappings out.
-            try {
-                configMappings = (Map<String, List<String>>) dependencyConfigField.get(dep);
-            } catch (IllegalAccessException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        } else {
-            configMappings = new HashMap<String, List<String>>();
-            for (String modConf : modConfs) {
-                configMappings.put(modConf, Arrays.asList(dep.getDependencyConfigurations(modConfs)));
-            }
-        }
-
-        for (int j = 0; j < modConfs.length; j++) {
-            List<String> depConfs = configMappings.get(modConfs[j]);
-            confs.append(modConfs[j]).append("->");
-            for (int k = 0; k < depConfs.size(); k++) {
-                confs.append(depConfs.get(k));
-                if (k + 1 < depConfs.size()) {
-                    confs.append(",");
-                }
-            }
-            if (j + 1 < modConfs.length) {
-                confs.append(";");
-            }
-        }
-        return confs.toString();
-    }
-
-    private static void printAllExcludes(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        ExcludeRule[] excludes = md.getAllExcludeRules();
-        for (ExcludeRule exclude : excludes) {
-            writer.startElement("exclude");
-            writer.attribute("org", exclude.getId().getModuleId().getOrganisation());
-            writer.attribute("module", exclude.getId().getModuleId().getName());
-            writer.attribute("artifact", exclude.getId().getName());
-            writer.attribute("type", exclude.getId().getType());
-            writer.attribute("ext", exclude.getId().getExt());
-            String[] ruleConfs = exclude.getConfigurations();
-            if (!Arrays.asList(ruleConfs).equals(
-                    Arrays.asList(md.getConfigurationsNames()))) {
-                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
-            }
-            writer.attribute("matcher", exclude.getMatcher().getName());
-            writer.endElement();
-        }
-    }
-
-    private static void printDependencyExcludeRules(ModuleDescriptor md, SimpleXmlWriter writer,
-                                                    ExcludeRule[] excludes) throws IOException {
-        for (ExcludeRule exclude : excludes) {
-            writer.startElement("exclude");
-            writer.attribute("org", exclude.getId().getModuleId().getOrganisation());
-            writer.attribute("module", exclude.getId().getModuleId().getName());
-            writer.attribute("name", exclude.getId().getName());
-            writer.attribute("type", exclude.getId().getType());
-            writer.attribute("ext", exclude.getId().getExt());
-            String[] ruleConfs = exclude.getConfigurations();
-            if (!Arrays.asList(ruleConfs).equals(
-                    Arrays.asList(md.getConfigurationsNames()))) {
-                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
-            }
-            writer.attribute("matcher", exclude.getMatcher().getName());
-            writer.endElement();
-        }
-    }
-
-    private static void printDependencyIncludeRules(ModuleDescriptor md, SimpleXmlWriter writer,
-                                                    IncludeRule[] includes) throws IOException {
-        for (IncludeRule include : includes) {
-            writer.startElement("include");
-            writer.attribute("name", include.getId().getName());
-            writer.attribute("type", include.getId().getType());
-            writer.attribute("ext", include.getId().getExt());
-            String[] ruleConfs = include.getConfigurations();
-            if (!Arrays.asList(ruleConfs).equals(
-                    Arrays.asList(md.getConfigurationsNames()))) {
-                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
-            }
-            writer.attribute("matcher", include.getMatcher().getName());
-            writer.endElement();
-        }
-    }
-
-    private static void printDependencyArtefacts(ModuleDescriptor md, SimpleXmlWriter writer,
-                                                 DependencyArtifactDescriptor[] depArtifacts) throws IOException {
-        for (DependencyArtifactDescriptor depArtifact : depArtifacts) {
-            writer.startElement("artifact");
-            writer.attribute("name", depArtifact.getName());
-            writer.attribute("type", depArtifact.getType());
-            writer.attribute("ext", depArtifact.getExt());
-            String[] dadconfs = depArtifact.getConfigurations();
-            if (!Arrays.asList(dadconfs).equals(
-                    Arrays.asList(md.getConfigurationsNames()))) {
-                writer.attribute("conf", Joiner.on(',').join(dadconfs));
-            }
-            printExtraAttributes(depArtifact, writer);
-            writer.endElement();
-        }
-    }
-
-    /**
-     * Writes the extra attributes of the given {@link org.apache.ivy.util.extendable.ExtendableItem} to the given <tt>PrintWriter</tt>.
-     *
-     * @param item the {@link org.apache.ivy.util.extendable.ExtendableItem}, cannot be <tt>null</tt>
-     * @param writer the writer to use
-     */
-    private static void printExtraAttributes(ExtendableItem item, SimpleXmlWriter writer) throws IOException {
-        printExtraAttributes(item.getQualifiedExtraAttributes(), writer);
-    }
-
-    /**
-     * Writes the specified <tt>Map</tt> containing the extra attributes to the given <tt>PrintWriter</tt>.
-     *
-     * @param extra the extra attributes, can be <tt>null</tt>
-     * @param writer the writer to use
-     */
-    private static void printExtraAttributes(Map<String, ?> extra, SimpleXmlWriter writer) throws IOException {
-        if (extra == null) {
-            return;
-        }
-        for (Map.Entry<String, ?> entry : extra.entrySet()) {
-            writer.attribute(entry.getKey(), entry.getValue().toString());
-        }
-    }
-
-    private static void printPublications(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        writer.startElement("publications");
-        Artifact[] artifacts = md.getAllArtifacts();
-        for (int i = 0; i < artifacts.length; i++) {
-            writer.startElement("artifact");
-            writer.attribute("name", artifacts[i].getName());
-            writer.attribute("type", artifacts[i].getType());
-            writer.attribute("ext", artifacts[i].getExt());
-            writer.attribute("conf", getConfs(md, artifacts[i]));
-            printExtraAttributes(artifacts[i], writer);
-            writer.endElement();
-        }
-        writer.endElement();
-    }
-
-    private static void printConfigurations(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        Configuration[] confs = md.getConfigurations();
-        if (confs.length > 0) {
-            writer.startElement("configurations");
-            for (Configuration conf : confs) {
-                printConfiguration(conf, writer);
-            }
-            writer.endElement();
-        }
-    }
-
-    private static void printConfiguration(Configuration conf, SimpleXmlWriter writer) throws IOException {
-        writer.startElement("conf");
-        writer.attribute("name", conf.getName());
-        writer.attribute("visibility", conf.getVisibility().toString());
-        String description = conf.getDescription();
-        if (description != null) {
-            writer.attribute("description", description);
-        }
-        String[] exts = conf.getExtends();
-        if (exts.length > 0) {
-            writer.attribute("extends", Joiner.on(',').join(exts));
-        }
-        if (!conf.isTransitive()) {
-            writer.attribute("transitive", "false");
-        }
-        if (conf.getDeprecated() != null) {
-            writer.attribute("deprecated", conf.getDeprecated());
-        }
-        printExtraAttributes(conf, writer);
-        writer.endElement();
-    }
-
-    private static void printInfoTag(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
-        ModuleRevisionId moduleRevisionId = md.getModuleRevisionId();
-        writer.startElement("info");
-
-        writer.attribute("organisation", moduleRevisionId.getOrganisation());
-        writer.attribute("module", moduleRevisionId.getName());
-
-        ModuleRevisionId resolvedModuleRevisionId = md.getResolvedModuleRevisionId();
-        String branch = resolvedModuleRevisionId.getBranch();
-        if (branch != null) {
-            writer.attribute("branch", branch);
-        }
-        String revision = resolvedModuleRevisionId.getRevision();
-        if (revision != null) {
-            writer.attribute("revision", revision);
-        }
-        writer.attribute("status", md.getStatus());
-
-        SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_PATTERN);
-        writer.attribute("publication", ivyDateFormat.format(md.getResolvedPublicationDate()));
-        if (md.isDefault()) {
-            writer.attribute("default", "true");
-        }
-        if (md instanceof DefaultModuleDescriptor) {
-            DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
-            if (dmd.getNamespace() != null && !dmd.getNamespace().getName().equals("system")) {
-                writer.attribute("namespace", dmd.getNamespace().getName());
-            }
-        }
-        if (!md.getExtraAttributes().isEmpty()) {
-            printExtraAttributes(md, writer);
-        }
-
-        ExtendsDescriptor[] parents = md.getInheritedDescriptors();
-        if (parents.length != 0) {
-            throw new UnsupportedOperationException("Extends descriptors not supported.");
-        }
-
-        License[] licenses = md.getLicenses();
-        for (int i = 0; i < licenses.length; i++) {
-            License license = licenses[i];
-            writer.startElement("license");
-            if (license.getName() != null) {
-                writer.attribute("name", license.getName());
-            }
-            if (license.getUrl() != null) {
-                writer.attribute("url", license.getUrl());
-            }
-            writer.endElement();
-        }
-
-        if (md.getHomePage() != null || md.getDescription() != null) {
-            writer.startElement("description");
-            if (md.getHomePage() != null) {
-                writer.attribute("homepage", md.getHomePage());
-            }
-            if (md.getDescription() != null && md.getDescription().trim().length() > 0) {
-                writer.characters(md.getDescription());
-            }
-            writer.endElement();
-        }
-
-        for (Iterator it = md.getExtraInfo().entrySet().iterator(); it.hasNext();) {
-            Map.Entry extraDescr = (Map.Entry) it.next();
-            if (extraDescr.getValue() == null || ((String) extraDescr.getValue()).length() == 0) {
-                continue;
-            }
-            writer.startElement(extraDescr.getKey().toString());
-            writer.characters(extraDescr.getValue().toString());
-            writer.endElement();
-        }
-
-        writer.endElement();
-    }
-
-    private static String getConfs(ModuleDescriptor md, Artifact artifact) {
-        StringBuffer ret = new StringBuffer();
-        String[] confs = md.getConfigurationsNames();
-        for (int i = 0; i < confs.length; i++) {
-            if (Arrays.asList(md.getArtifacts(confs[i])).contains(artifact)) {
-                ret.append(confs[i]).append(",");
-            }
-        }
-        if (ret.length() > 0) {
-            ret.setLength(ret.length() - 1);
-        }
-        return ret.toString();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.java
deleted file mode 100644
index 80a5823..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.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.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData;
-
-import java.util.Set;
-
-public interface LocalComponentFactory {
-    MutableLocalComponentMetaData convert(Set<? extends Configuration> configurations, ModuleInternal 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
deleted file mode 100755
index b477ab0..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleToModuleVersionResolver.java
+++ /dev/null
@@ -1,29 +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;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-
-import java.util.Set;
-
-/**
- * Resolves a module to the meta-data for a module.
- */
-public interface ModuleToModuleVersionResolver {
-    void resolve(ModuleInternal module, Set<? extends Configuration> configurations, BuildableComponentResolveResult result);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionIdResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionIdResolveResult.java
deleted file mode 100644
index 02b08cc..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionIdResolveResult.java
+++ /dev/null
@@ -1,46 +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;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-
-public interface ModuleVersionIdResolveResult {
-    /**
-     * Returns the resolve failure, if any.
-     */
-    @Nullable
-    ModuleVersionResolveException getFailure();
-
-    /**
-     * Returns the identifier of this module version.
-     *
-     * @throws ModuleVersionResolveException If id resolution was unsuccessful and the id is unknown.
-     */
-    ModuleVersionIdentifier getId() throws ModuleVersionResolveException;
-
-    /**
-     * Resolves the meta-data for this module version, if required. Failures are packaged up in the result.
-     * @throws ModuleVersionResolveException If id resolution was unsuccessful and the id is unknown.
-     */
-    ComponentResolveResult resolve() throws ModuleVersionResolveException;
-
-    /**
-     * @return why given id was selected. Should return a value even if the resolve failed.
-     */
-    ComponentSelectionReason getSelectionReason();
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundException.java
deleted file mode 100755
index 6bfe2ec..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundException.java
+++ /dev/null
@@ -1,34 +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.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-
-public class ModuleVersionNotFoundException extends ModuleVersionResolveException {
-    @SuppressWarnings("UnusedDeclaration")
-    public ModuleVersionNotFoundException(ModuleVersionSelector selector, String messageFormat) {
-        super(selector, messageFormat);
-    }
-
-    public ModuleVersionNotFoundException(ModuleVersionSelector selector) {
-        super(selector, "Could not find any version that matches %s.");
-    }
-
-    public ModuleVersionNotFoundException(ModuleVersionIdentifier id) {
-        super(id, "Could not find %s.");
-    }
-}
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
deleted file mode 100644
index cf7cebb..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveException.java
+++ /dev/null
@@ -1,110 +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.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
-import org.gradle.internal.exceptions.Contextual;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Formatter;
-import java.util.List;
-
- at Contextual
-public class ModuleVersionResolveException extends AbstractMultiCauseException {
-    private final List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
-    private final String messageFormat;
-    private final ModuleVersionSelector selector;
-
-    public ModuleVersionResolveException(ModuleVersionSelector selector, String messageFormat) {
-        super(format(messageFormat, selector));
-        this.selector = selector;
-        this.messageFormat = messageFormat;
-    }
-
-    public ModuleVersionResolveException(ModuleVersionIdentifier id, String messageFormat) {
-        this(DefaultModuleVersionSelector.newSelector(id.getGroup(), id.getName(), id.getVersion()), messageFormat);
-    }
-
-    public ModuleVersionResolveException(ModuleVersionSelector selector, Throwable cause) {
-        this(selector, "Could not resolve %s.");
-        initCause(cause);
-    }
-
-    public ModuleVersionResolveException(ModuleVersionSelector selector, Iterable<? extends Throwable> causes) {
-        this(selector, "Could not resolve %s.");
-        initCauses(causes);
-    }
-
-    /**
-     * Returns the selector that could not be resolved.
-     */
-    public ModuleVersionSelector getSelector() {
-        return selector;
-    }
-
-    private static String format(String messageFormat, ModuleVersionSelector id) {
-        return format(messageFormat, id.getGroup(), id.getName(), id.getVersion());
-    }
-
-    private static String format(String messageFormat, String group, String name, String version) {
-        return String.format(messageFormat, String.format("%s:%s:%s", group, name, version));
-    }
-
-    /**
-     * Creates a copy of this exception, with the given incoming paths.
-     */
-    public ModuleVersionResolveException withIncomingPaths(Collection<? extends List<ModuleVersionIdentifier>> paths) {
-        ModuleVersionResolveException copy = createCopy();
-        copy.paths.addAll(paths);
-        copy.initCauses(getCauses());
-        copy.setStackTrace(getStackTrace());
-        return copy;
-    }
-
-    @Override
-    public String getMessage() {
-        if (paths.isEmpty()) {
-            return super.getMessage();
-        }
-        Formatter formatter = new Formatter();
-        formatter.format("%s%nRequired by:", super.getMessage());
-        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)));
-            }
-        }
-        return formatter.toString();
-    }
-
-    private String toString(ModuleVersionIdentifier identifier) {
-        return identifier.toString();
-    }
-
-    protected ModuleVersionResolveException createCopy() {
-        try {
-            return getClass().getConstructor(ModuleVersionSelector.class, String.class).newInstance(selector, messageFormat);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
deleted file mode 100644
index 106ecf7..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.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.artifacts.ivyservice;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
-import org.gradle.api.internal.artifacts.CachingDependencyResolveContext;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.specs.Spec;
-import org.gradle.util.CollectionUtils;
-
-import java.io.File;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-public class SelfResolvingDependencyResolver implements ArtifactDependencyResolver {
-    private final ArtifactDependencyResolver resolver;
-
-    public SelfResolvingDependencyResolver(ArtifactDependencyResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public void resolve(ConfigurationInternal configuration,
-                        List<? extends ResolutionAwareRepository> repositories,
-                        ModuleMetadataProcessor metadataProcessor,
-                        ResolverResults results) throws ResolveException {
-        resolver.resolve(configuration, repositories, metadataProcessor, results);
-        ResolvedConfiguration resolvedConfiguration = results.getResolvedConfiguration();
-        Set<Dependency> dependencies = configuration.getAllDependencies();
-        CachingDependencyResolveContext resolveContext = new CachingDependencyResolveContext(configuration.isTransitive());
-        SelfResolvingFilesProvider provider = new SelfResolvingFilesProvider(resolveContext, dependencies);
-
-        results.withResolvedConfiguration(new FilesAggregatingResolvedConfiguration(resolvedConfiguration, provider));
-    }
-
-    protected static class SelfResolvingFilesProvider {
-
-        final CachingDependencyResolveContext resolveContext;
-        final Set<Dependency> dependencies;
-
-        public SelfResolvingFilesProvider(CachingDependencyResolveContext resolveContext, Set<Dependency> dependencies) {
-            this.resolveContext = resolveContext;
-            this.dependencies = dependencies;
-        }
-
-        Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
-            Set<Dependency> selectedDependencies = CollectionUtils.filter(dependencies, dependencySpec);
-            for (Dependency dependency : selectedDependencies) {
-                resolveContext.add(dependency);
-            }
-            return resolveContext.resolve().getFiles();
-        }
-    }
-
-    protected static class FilesAggregatingResolvedConfiguration implements ResolvedConfiguration {
-        final ResolvedConfiguration resolvedConfiguration;
-        final SelfResolvingFilesProvider selfResolvingFilesProvider;
-
-        FilesAggregatingResolvedConfiguration(ResolvedConfiguration resolvedConfiguration, SelfResolvingFilesProvider selfResolvingFilesProvider) {
-            this.resolvedConfiguration = resolvedConfiguration;
-            this.selfResolvingFilesProvider = selfResolvingFilesProvider;
-        }
-
-        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
-            Set<File> files = new LinkedHashSet<File>();
-            files.addAll(selfResolvingFilesProvider.getFiles(dependencySpec));
-            files.addAll(resolvedConfiguration.getFiles(dependencySpec));
-            return files;
-        }
-
-        public Set<ResolvedArtifact> getResolvedArtifacts() {
-            return resolvedConfiguration.getResolvedArtifacts();
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
-            return resolvedConfiguration.getFirstLevelModuleDependencies();
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
-        }
-
-        public boolean hasError() {
-            return resolvedConfiguration.hasError();
-        }
-
-        public LenientConfiguration getLenientConfiguration() {
-            return resolvedConfiguration.getLenientConfiguration();
-        }
-
-        public void rethrowFailure() throws GradleException {
-            resolvedConfiguration.rethrowFailure();
-        }
-    }
-}
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
deleted file mode 100644
index 72c3427..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice;
-
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-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.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.DefaultResolutionResultBuilder;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.specs.Spec;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-public class ShortcircuitEmptyConfigsArtifactDependencyResolver implements ArtifactDependencyResolver {
-    private final ArtifactDependencyResolver dependencyResolver;
-    private final ComponentIdentifierFactory componentIdentifierFactory;
-
-    public ShortcircuitEmptyConfigsArtifactDependencyResolver(ArtifactDependencyResolver dependencyResolver, ComponentIdentifierFactory componentIdentifierFactory) {
-        this.dependencyResolver = dependencyResolver;
-        this.componentIdentifierFactory = componentIdentifierFactory;
-    }
-
-    public void resolve(ConfigurationInternal configuration,
-                        List<? extends ResolutionAwareRepository> repositories,
-                        ModuleMetadataProcessor metadataProcessor,
-                        ResolverResults results) throws ResolveException {
-        if (configuration.getAllDependencies().isEmpty()) {
-            ModuleVersionIdentifier id = DefaultModuleVersionIdentifier.newId(configuration.getModule());
-            ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(configuration.getModule());
-            ResolutionResult emptyResult = new DefaultResolutionResultBuilder().start(id, componentIdentifier).complete();
-            results.resolved(new EmptyResolvedConfiguration(), emptyResult);
-        } else {
-            dependencyResolver.resolve(configuration, repositories, metadataProcessor, results);
-        }
-    }
-
-    private static class EmptyResolvedConfiguration implements ResolvedConfiguration {
-
-        public boolean hasError() {
-            return false;
-        }
-
-        public LenientConfiguration getLenientConfiguration() {
-            return new LenientConfiguration() {
-                public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
-                    return Collections.emptySet();
-                }
-
-                public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
-                    return Collections.emptySet();
-                }
-
-                public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
-                    return Collections.emptySet();
-                }
-
-                public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
-                    return Collections.emptySet();
-                }
-            };
-        }
-
-        public void rethrowFailure() throws ResolveException {
-        }
-
-        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
-            return Collections.emptySet();
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
-            return Collections.emptySet();
-        }
-
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            return Collections.emptySet();
-        }
-
-        public Set<ResolvedArtifact> getResolvedArtifacts() {
-            return Collections.emptySet();
-        }
-    }
-}
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
deleted file mode 100644
index 1066dce..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/SubstitutedModuleVersionIdResolveResult.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;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-
-class SubstitutedModuleVersionIdResolveResult implements ModuleVersionIdResolveResult {
-
-    final ModuleVersionIdResolveResult result;
-    private final ComponentSelectionReason selectionReason;
-
-    public SubstitutedModuleVersionIdResolveResult(ModuleVersionIdResolveResult result, ComponentSelectionReason selectionReason) {
-        this.result = result;
-        this.selectionReason = selectionReason;
-    }
-
-    public ModuleVersionResolveException getFailure() {
-        return result.getFailure();
-    }
-
-    public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
-        return result.getId();
-    }
-
-    public ComponentResolveResult resolve() {
-        return result.resolve();
-    }
-
-    public ComponentSelectionReason getSelectionReason() {
-        return selectionReason;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolver.java
deleted file mode 100644
index cd90152..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolver.java
+++ /dev/null
@@ -1,75 +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.Action;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
-
-public class VersionForcingDependencyToModuleResolver implements DependencyToModuleVersionIdResolver {
-    private final DependencyToModuleVersionIdResolver resolver;
-    private Action<DependencyResolveDetailsInternal> rule;
-
-    public VersionForcingDependencyToModuleResolver(DependencyToModuleVersionIdResolver resolver, Action<DependencyResolveDetailsInternal> rule) {
-        this.resolver = resolver;
-        this.rule = rule;
-    }
-
-    public ModuleVersionIdResolveResult resolve(DependencyMetaData dependency) {
-        ModuleVersionSelector module = dependency.getRequested();
-        DefaultDependencyResolveDetails details = new DefaultDependencyResolveDetails(module);
-        try {
-            rule.execute(details);
-        } catch (Throwable e) {
-            return new FailedDependencyResolveRuleResult(module, e);
-        }
-        if (details.isUpdated()) {
-            DependencyMetaData substitutedDependency = dependency.withRequestedVersion(details.getTarget());
-            ModuleVersionIdResolveResult result = resolver.resolve(substitutedDependency);
-            return new SubstitutedModuleVersionIdResolveResult(result, details.getSelectionReason());
-        }
-        return resolver.resolve(dependency);
-    }
-
-    private class FailedDependencyResolveRuleResult implements ModuleVersionIdResolveResult {
-
-        private final ModuleVersionResolveException failure;
-
-        public FailedDependencyResolveRuleResult(ModuleVersionSelector module, Throwable problem) {
-            this.failure = new ModuleVersionResolveException(module, problem);
-        }
-
-        public ModuleVersionResolveException getFailure() {
-            return failure;
-        }
-
-        public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
-            throw failure;
-        }
-
-        public ComponentResolveResult resolve() throws ModuleVersionResolveException {
-            throw failure;
-        }
-
-        public ComponentSelectionReason getSelectionReason() {
-            return VersionSelectionReasons.REQUESTED;
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index c00f110..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
+++ /dev/null
@@ -1,47 +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.clientmodule;
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableComponentResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ClientModuleDependencyDescriptor;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-
-public class ClientModuleResolver implements DependencyToModuleVersionResolver {
-    private final DependencyToModuleVersionResolver resolver;
-
-    public ClientModuleResolver(DependencyToModuleVersionResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public void resolve(DependencyMetaData dependency, BuildableComponentResolveResult result) {
-        resolver.resolve(dependency, result);
-
-        DependencyDescriptor descriptor = dependency.getDescriptor();
-        if (result.getFailure() != null || !(descriptor instanceof ClientModuleDependencyDescriptor)) {
-            return;
-        }
-
-        ClientModuleDependencyDescriptor clientModuleDependencyDescriptor = (ClientModuleDependencyDescriptor) descriptor;
-        ModuleVersionMetaData clientModuleMetaData = clientModuleDependencyDescriptor.getTargetModule();
-        ModuleSource moduleSource = result.getMetaData().getSource();
-        result.setMetaData(clientModuleMetaData.withSource(moduleSource));
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.java
deleted file mode 100644
index c353522..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.java
+++ /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.dynamicversions;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-class DefaultCachedModuleVersionList implements ModuleVersionsCache.CachedModuleVersionList {
-    private final ModuleVersionListing moduleVersions;
-    private final long ageMillis;
-
-    public DefaultCachedModuleVersionList(ModuleVersionsCacheEntry entry, BuildCommencedTimeProvider timeProvider) {
-        this.moduleVersions = entry.moduleVersionListing;
-        ageMillis = timeProvider.getCurrentTime() - entry.createTimestamp;
-    }
-
-    public ModuleVersionListing getModuleVersions() {
-        return moduleVersions;
-    }
-
-    public long getAgeMillis() {
-        return ageMillis;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java
deleted file mode 100644
index 6d0fdb6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java
+++ /dev/null
@@ -1,33 +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.dynamicversions;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing;
-
-public interface ModuleVersionsCache {
-
-    void cacheModuleVersionList(ModuleVersionRepository repository, ModuleIdentifier moduleId, ModuleVersionListing listedVersions);
-
-    CachedModuleVersionList getCachedModuleResolution(ModuleVersionRepository repository, ModuleIdentifier moduleId);
-
-    interface CachedModuleVersionList {
-        ModuleVersionListing getModuleVersions();
-
-        long getAgeMillis();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.java
deleted file mode 100644
index 913f207..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.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.dynamicversions;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing;
-
-class ModuleVersionsCacheEntry {
-    public ModuleVersionListing moduleVersionListing;
-    public long createTimestamp;
-
-    ModuleVersionsCacheEntry(ModuleVersionListing moduleVersionListing, long createTimestamp) {
-        this.moduleVersionListing = moduleVersionListing;
-        this.createTimestamp = createTimestamp;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java
deleted file mode 100644
index bcb9628..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java
+++ /dev/null
@@ -1,140 +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.dynamicversions;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DefaultModuleVersionListing;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.util.BuildCommencedTimeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-public class SingleFileBackedModuleVersionsCache implements ModuleVersionsCache {
-    private static final Logger LOGGER = LoggerFactory.getLogger(SingleFileBackedModuleVersionsCache.class);
-
-    private final BuildCommencedTimeProvider timeProvider;
-    private final CacheLockingManager cacheLockingManager;
-    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> cache;
-
-    public SingleFileBackedModuleVersionsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        this.timeProvider = timeProvider;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> getCache() {
-        if (cache == null) {
-            cache = initCache();
-        }
-        return cache;
-    }
-
-    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> initCache() {
-        return cacheLockingManager.createCache("module-versions", new ModuleKeySerializer(), new ModuleVersionsCacheEntrySerializer());
-    }
-
-    public void cacheModuleVersionList(ModuleVersionRepository repository, ModuleIdentifier moduleId, ModuleVersionListing listedVersions) {
-        LOGGER.debug("Caching version list in module versions cache: Using '{}' for '{}'", listedVersions, moduleId);
-        getCache().put(createKey(repository, moduleId), createEntry(listedVersions));
-    }
-
-    public CachedModuleVersionList getCachedModuleResolution(ModuleVersionRepository repository, ModuleIdentifier moduleId) {
-        ModuleVersionsCacheEntry moduleVersionsCacheEntry = getCache().get(createKey(repository, moduleId));
-        if (moduleVersionsCacheEntry == null) {
-            return null;
-        }
-        return new DefaultCachedModuleVersionList(moduleVersionsCacheEntry, timeProvider);
-    }
-
-    private ModuleKey createKey(ModuleVersionRepository repository, ModuleIdentifier moduleId) {
-        return new ModuleKey(repository.getId(), moduleId);
-    }
-
-    private ModuleVersionsCacheEntry createEntry(ModuleVersionListing listedVersions) {
-        return new ModuleVersionsCacheEntry(listedVersions, timeProvider.getCurrentTime());
-    }
-
-    private static class ModuleKey {
-        private final String repositoryId;
-        private final ModuleIdentifier moduleId;
-
-        private ModuleKey(String repositoryId, ModuleIdentifier moduleId) {
-            this.repositoryId = repositoryId;
-            this.moduleId = moduleId;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == null || !(o instanceof ModuleKey)) {
-                return false;
-            }
-            ModuleKey other = (ModuleKey) o;
-            return repositoryId.equals(other.repositoryId) && moduleId.equals(other.moduleId);
-        }
-
-        @Override
-        public int hashCode() {
-            return repositoryId.hashCode() ^ moduleId.hashCode();
-        }
-    }
-
-    private static class ModuleKeySerializer implements Serializer<ModuleKey> {
-        public void write(Encoder encoder, ModuleKey value) throws Exception {
-            encoder.writeString(value.repositoryId);
-            encoder.writeString(value.moduleId.getGroup());
-            encoder.writeString(value.moduleId.getName());
-        }
-
-        public ModuleKey read(Decoder decoder) throws Exception {
-            String resolverId = decoder.readString();
-            String group = decoder.readString();
-            String module = decoder.readString();
-            return new ModuleKey(resolverId, new DefaultModuleIdentifier(group, module));
-        }
-    }
-
-    private static class ModuleVersionsCacheEntrySerializer implements Serializer<ModuleVersionsCacheEntry> {
-
-        public void write(Encoder encoder, ModuleVersionsCacheEntry value) throws Exception {
-            Set<Versioned> versions = value.moduleVersionListing.getVersions();
-            encoder.writeInt(versions.size());
-            for (Versioned version : versions) {
-                encoder.writeString(version.getVersion());
-            }
-            encoder.writeLong(value.createTimestamp);
-        }
-
-        public ModuleVersionsCacheEntry read(Decoder decoder) throws Exception {
-            int size = decoder.readInt();
-            DefaultModuleVersionListing versions = new DefaultModuleVersionListing();
-            for (int i = 0; i < size; i++) {
-                versions.add(decoder.readString());
-            }
-            long createTimestamp = decoder.readLong();
-            return new ModuleVersionsCacheEntry(versions, createTimestamp);
-        }
-    }
-
-}
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
deleted file mode 100644
index 291878d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactNotFoundException.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.artifacts.ivyservice.ivyresolve;
-
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier;
-
-public class ArtifactNotFoundException extends ArtifactResolveException {
-    public ArtifactNotFoundException(ComponentArtifactIdentifier artifact) {
-        super(String.format("Artifact '%s' not found.", artifact.getDisplayName()));
-    }
-}
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
deleted file mode 100644
index 336b602..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ArtifactResolveException.java
+++ /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.artifacts.ivyservice.ivyresolve;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier;
-import org.gradle.internal.exceptions.Contextual;
-import org.gradle.util.GUtil;
-
- at Contextual
-public class ArtifactResolveException extends GradleException {
-    public ArtifactResolveException(String message) {
-        super(message);
-    }
-
-    public ArtifactResolveException(ComponentIdentifier component, Throwable cause) {
-        super(format(component, ""), cause);
-    }
-
-    public ArtifactResolveException(ComponentIdentifier component, String message) {
-        super(format(component, message));
-    }
-
-    public ArtifactResolveException(ComponentArtifactIdentifier artifact, Throwable cause) {
-        super(format(artifact, ""), cause);
-    }
-
-    public ArtifactResolveException(ComponentArtifactIdentifier artifact, String message) {
-        super(format(artifact, message));
-    }
-
-    private static String format(ComponentArtifactIdentifier artifact, String message) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("Could not download artifact '");
-        builder.append(artifact.getDisplayName());
-        builder.append("'");
-        if (GUtil.isTrue(message)) {
-            builder.append(": ");
-            builder.append(message);
-        }
-        return builder.toString();
-    }
-
-    private static String format(ComponentIdentifier component, String message) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("Could not determine artifacts for component '");
-        builder.append(component.getDisplayName());
-        builder.append("'");
-        if (GUtil.isTrue(message)) {
-            builder.append(": ");
-            builder.append(message);
-        }
-        return builder.toString();
-    }
-}
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
deleted file mode 100644
index ba9f138..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionMetaDataResolveResult.java
+++ /dev/null
@@ -1,72 +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.gradle.api.Nullable;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-/**
- * 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.
-     */
-    MutableModuleVersionMetaData getMetaData() throws ModuleVersionResolveException;
-
-    @Nullable
-    ModuleVersionResolveException getFailure();
-
-    /**
-     * Marks the module version as resolved, with the given meta-data and source.
-     */
-    void resolved(MutableModuleVersionMetaData metaData, 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);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionSelectionResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionSelectionResolveResult.java
deleted file mode 100644
index a9f8b56..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BuildableModuleVersionSelectionResolveResult.java
+++ /dev/null
@@ -1,60 +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.gradle.api.Nullable;
-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 BuildableModuleVersionSelectionResolveResult {
-
-    static enum State {
-        Listed, ProbablyListed, Failed, Unknown
-    }
-
-    /**
-     * Returns the current state of this descriptor.
-     */
-    State getState();
-
-    /**
-     * Returns the versions that match the selector.
-     *
-     * @throws org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException If the resolution was not successful.
-     */
-    ModuleVersionListing getVersions() throws ModuleVersionResolveException;
-
-    @Nullable
-    ModuleVersionResolveException getFailure();
-
-    /**
-     * Marks the module as having been listed to have the specified versions available.
-     */
-    void listed(ModuleVersionListing versions);
-
-    /**
-     * Marks the module as probably having no versions available.
-     */
-    void probablyListed(ModuleVersionListing versions);
-
-    /**
-     * Marks the list as failed with the given exception.
-     */
-    void failed(ModuleVersionResolveException failure);
-}
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
deleted file mode 100644
index 8d33912..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockingModuleVersionRepository.java
+++ /dev/null
@@ -1,78 +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.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-/**
- * A wrapper around a {@link ModuleVersionRepository} that handles locking/unlocking the cache.
- */
-public class CacheLockingModuleVersionRepository implements LocalArtifactsModuleVersionRepository {
-    private final LocalArtifactsModuleVersionRepository repository;
-    private final CacheLockingManager cacheLockingManager;
-
-    public CacheLockingModuleVersionRepository(LocalArtifactsModuleVersionRepository repository, CacheLockingManager cacheLockingManager) {
-        this.repository = repository;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    public String getId() {
-        return repository.getId();
-    }
-
-    public String getName() {
-        return repository.getName();
-    }
-
-    public void listModuleVersions(final DependencyMetaData dependency, final BuildableModuleVersionSelectionResolveResult result) {
-        cacheLockingManager.longRunningOperation(String.format("List %s using repository %s", dependency, getId()), new Runnable() {
-            public void run() {
-                repository.listModuleVersions(dependency, 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);
-            }
-        });
-    }
-
-    public void localResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        repository.localResolveModuleArtifacts(component, context, result);
-    }
-
-    public void resolveModuleArtifacts(final ComponentMetaData component, final ArtifactResolveContext context, final BuildableArtifactSetResolveResult result) {
-        cacheLockingManager.longRunningOperation(String.format("Resolve %s for %s using repository %s", context, component, getId()), new Runnable() {
-            public void run() {
-                repository.resolveModuleArtifacts(component, context, result);
-            }
-        });
-    }
-
-    public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
-        cacheLockingManager.longRunningOperation(String.format("Download %s using repository %s", artifact, getId()), new Runnable() {
-            public void run() {
-                repository.resolveArtifact(artifact, moduleSource, result);
-            }
-        });
-    }
-}
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
deleted file mode 100644
index b7023b9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepository.java
+++ /dev/null
@@ -1,308 +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.module.id.ModuleRevisionId;
-import org.gradle.api.Transformer;
-import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.ModuleIdentifier;
-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.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
-import org.gradle.api.internal.artifacts.metadata.*;
-import org.gradle.api.internal.externalresource.cached.CachedArtifact;
-import org.gradle.api.internal.externalresource.cached.CachedArtifactIndex;
-import org.gradle.api.internal.externalresource.ivy.ArtifactAtRepositoryKey;
-import org.gradle.util.BuildCommencedTimeProvider;
-import org.gradle.util.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.math.BigInteger;
-import java.util.Set;
-
-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 ModuleVersionsCache moduleVersionsCache;
-    private final ModuleMetaDataCache moduleMetaDataCache;
-    private final ModuleArtifactsCache moduleArtifactsCache;
-    private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
-
-    private final CachePolicy cachePolicy;
-
-    private final LocalArtifactsModuleVersionRepository delegate;
-    private final BuildCommencedTimeProvider timeProvider;
-    private final ModuleMetadataProcessor metadataProcessor;
-    private final Transformer<ModuleIdentifier, ModuleVersionSelector> moduleExtractor;
-
-    public CachingModuleVersionRepository(LocalArtifactsModuleVersionRepository delegate, ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache,
-                                          ModuleArtifactsCache moduleArtifactsCache, CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
-                                          CachePolicy cachePolicy, BuildCommencedTimeProvider timeProvider,
-                                          ModuleMetadataProcessor metadataProcessor, Transformer<ModuleIdentifier, ModuleVersionSelector> moduleExtractor) {
-        this.delegate = delegate;
-        this.moduleMetaDataCache = moduleMetaDataCache;
-        this.moduleVersionsCache = moduleVersionsCache;
-        this.moduleArtifactsCache = moduleArtifactsCache;
-        this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
-        this.timeProvider = timeProvider;
-        this.cachePolicy = cachePolicy;
-        this.metadataProcessor = metadataProcessor;
-        this.moduleExtractor = moduleExtractor;
-    }
-
-    public String getId() {
-        return delegate.getId();
-    }
-
-    public String getName() {
-        return delegate.getName();
-    }
-
-    @Override
-    public String toString() {
-        return "Caching " + delegate.toString();
-    }
-
-    public void localListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        ModuleVersionSelector requested = dependency.getRequested();
-        final ModuleIdentifier moduleId = moduleExtractor.transform(requested);
-        ModuleVersionsCache.CachedModuleVersionList cachedModuleVersionList = moduleVersionsCache.getCachedModuleResolution(delegate, moduleId);
-        if (cachedModuleVersionList != null) {
-            ModuleVersionListing versionList = cachedModuleVersionList.getModuleVersions();
-            Set<ModuleVersionIdentifier> versions = CollectionUtils.collect(versionList.getVersions(), new Transformer<ModuleVersionIdentifier, Versioned>() {
-                public ModuleVersionIdentifier transform(Versioned original) {
-                    return new DefaultModuleVersionIdentifier(moduleId, original.getVersion());
-                }
-            });
-            if (cachePolicy.mustRefreshVersionList(moduleId, versions, cachedModuleVersionList.getAgeMillis())) {
-                LOGGER.debug("Version listing in dynamic revision cache is expired: will perform fresh resolve of '{}' in '{}'", requested, delegate.getName());
-            } else {
-                if (cachedModuleVersionList.getAgeMillis() == 0) {
-                    // Verified since the start of this build, assume still missing
-                    result.listed(versionList);
-                } else {
-                    // Was missing last time we checked
-                    result.probablyListed(versionList);
-                }
-            }
-        }
-    }
-
-    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        delegate.listModuleVersions(dependency, result);
-        switch (result.getState()) {
-            case Listed:
-                ModuleIdentifier moduleId = moduleExtractor.transform(dependency.getRequested());
-                ModuleVersionListing versionList = result.getVersions();
-                moduleVersionsCache.cacheModuleVersionList(delegate, moduleId, versionList);
-                break;
-            case Failed:
-                break;
-            default:
-                throw new IllegalStateException("Unexpected state on listModuleVersions: " + result.getState());
-        }
-    }
-
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        lookupModuleInCache(delegate, dependency, result);
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        DependencyMetaData forced = dependency.withChanging();
-        delegate.getDependency(forced, result);
-        switch (result.getState()) {
-            case Missing:
-                ModuleRevisionId dependencyRevisionId = dependency.getDescriptor().getDependencyRevisionId();
-                ModuleVersionIdentifier moduleVersionIdentifier = DefaultModuleVersionIdentifier.newId(dependencyRevisionId);
-                moduleMetaDataCache.cacheMissing(delegate, moduleVersionIdentifier);
-                break;
-            case Resolved:
-                MutableModuleVersionMetaData metaData = result.getMetaData();
-                ModuleSource moduleSource = result.getModuleSource();
-                ModuleMetaDataCache.CachedMetaData cachedMetaData = moduleMetaDataCache.cacheMetaData(delegate, metaData, moduleSource);
-                metadataProcessor.process(metaData);
-                result.setModuleSource(new CachingModuleSource(cachedMetaData.getDescriptorHash(), dependency.isChanging() || metaData.isChanging(), moduleSource));
-                break;
-            case Failed:
-                break;
-            default:
-                throw new IllegalStateException("Unexpected resolve state: " + result.getState());
-        }
-    }
-
-    private void lookupModuleInCache(ModuleVersionRepository repository, DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        ModuleRevisionId resolvedModuleVersionId = dependency.getDescriptor().getDependencyRevisionId();
-        ModuleVersionIdentifier moduleVersionIdentifier = newId(resolvedModuleVersionId);
-        ModuleMetaDataCache.CachedMetaData cachedMetaData = moduleMetaDataCache.getCachedModuleDescriptor(repository, moduleVersionIdentifier);
-        if (cachedMetaData == null) {
-            return;
-        }
-        if (cachedMetaData.isMissing()) {
-            if (cachePolicy.mustRefreshModule(moduleVersionIdentifier, null, resolvedModuleVersionId, cachedMetaData.getAgeMillis())) {
-                LOGGER.debug("Cached meta-data for missing module is expired: will perform fresh resolve of '{}' in '{}'", resolvedModuleVersionId, repository.getName());
-                return;
-            }
-            LOGGER.debug("Detected non-existence of module '{}' in resolver cache '{}'", resolvedModuleVersionId, repository.getName());
-            if (cachedMetaData.getAgeMillis() == 0) {
-                // Verified since the start of this build, assume still missing
-                result.missing();
-            } else {
-                // Was missing last time we checked
-                result.probablyMissing();
-            }
-            return;
-        }
-        MutableModuleVersionMetaData metaData = cachedMetaData.getMetaData();
-        metadataProcessor.process(metaData);
-        if (dependency.isChanging() || metaData.isChanging()) {
-            if (cachePolicy.mustRefreshChangingModule(moduleVersionIdentifier, cachedMetaData.getModuleVersion(), cachedMetaData.getAgeMillis())) {
-                LOGGER.debug("Cached meta-data for changing module is expired: will perform fresh resolve of '{}' in '{}'", resolvedModuleVersionId, repository.getName());
-                return;
-            }
-            LOGGER.debug("Found cached version of changing module '{}' in '{}'", resolvedModuleVersionId, repository.getName());
-        } else {
-            if (cachePolicy.mustRefreshModule(moduleVersionIdentifier, cachedMetaData.getModuleVersion(), null, cachedMetaData.getAgeMillis())) {
-                LOGGER.debug("Cached meta-data for module must be refreshed: will perform fresh resolve of '{}' in '{}'", resolvedModuleVersionId, repository.getName());
-                return;
-            }
-        }
-
-        LOGGER.debug("Using cached module metadata for module '{}' in '{}'", resolvedModuleVersionId, repository.getName());
-        result.resolved(metaData, new CachingModuleSource(cachedMetaData.getDescriptorHash(), metaData.isChanging(), cachedMetaData.getModuleSource()));
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        final CachingModuleSource cachedModuleSource = (CachingModuleSource) component.getSource();
-
-        // First try to determine the artifacts locally (e.g using the metadata): don't use the cache in this case
-        delegate.localResolveModuleArtifacts(component.withSource(cachedModuleSource.getDelegate()), context, result);
-        if (result.hasResult()) {
-            return;
-        }
-
-        resolveAndCacheModuleArtifacts(component, context, result);
-    }
-
-    private void resolveAndCacheModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        final CachingModuleSource cachedModuleSource = (CachingModuleSource) component.getSource();
-        ModuleArtifactsCache.CachedArtifacts cachedModuleArtifacts = moduleArtifactsCache.getCachedArtifacts(delegate, component.getId(), context.getId());
-        BigInteger moduleDescriptorHash = cachedModuleSource.getDescriptorHash();
-
-        if (cachedModuleArtifacts != null) {
-            if (!cachePolicy.mustRefreshModuleArtifacts(component.getId(), null, cachedModuleArtifacts.getAgeMillis(),
-                    cachedModuleSource.isChangingModule(), moduleDescriptorHash.equals(cachedModuleArtifacts.getDescriptorHash()))) {
-                Set<ModuleVersionArtifactMetaData> artifactMetaDataSet = CollectionUtils.collect(cachedModuleArtifacts.getArtifacts(), new ArtifactIdToMetaData());
-                result.resolved(artifactMetaDataSet);
-                return;
-            }
-
-            LOGGER.debug("Artifact listing has expired: will perform fresh resolve of '{}' for '{}' in '{}'", context.getDescription(), component.getId(), delegate.getName());
-        }
-
-        delegate.resolveModuleArtifacts(component.withSource(cachedModuleSource.getDelegate()), context, result);
-
-        if (result.getFailure() == null) {
-            Set<ModuleVersionArtifactIdentifier> artifactIdentifierSet = CollectionUtils.collect(result.getArtifacts(), new ArtifactMetaDataToId());
-            moduleArtifactsCache.cacheArtifacts(delegate, component.getId(), context.getId(), moduleDescriptorHash, artifactIdentifierSet);
-        }
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        // TODO:ADAM - Don't assume this
-        ModuleVersionArtifactMetaData moduleVersionArtifactMetaData = (ModuleVersionArtifactMetaData) artifact;
-        ArtifactAtRepositoryKey resolutionCacheIndexKey = new ArtifactAtRepositoryKey(delegate.getId(), moduleVersionArtifactMetaData.getId());
-        // 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) {
-            long age = timeProvider.getCurrentTime() - cached.getCachedAt();
-            final boolean isChangingModule = cachedModuleSource.isChangingModule();
-            ArtifactIdentifier artifactIdentifier = moduleVersionArtifactMetaData.toArtifactIdentifier();
-            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);
-                    result.notFound(artifact.getId());
-                    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, cachedArtifactFile);
-                    result.resolved(cachedArtifactFile);
-                    return;
-                }
-            }
-        }
-
-        delegate.resolveArtifact(artifact, cachedModuleSource.getDelegate(), result);
-        LOGGER.debug("Downloaded artifact '{}' from resolver: {}", artifact, delegate.getName());
-
-        if (result.getFailure() == null) {
-            artifactAtRepositoryCachedResolutionIndex.store(resolutionCacheIndexKey, result.getFile(), descriptorHash);
-        } else if (result.getFailure() instanceof ArtifactNotFoundException) {
-            artifactAtRepositoryCachedResolutionIndex.storeMissing(resolutionCacheIndexKey, descriptorHash);
-        }
-    }
-
-    static class CachingModuleSource implements ModuleSource {
-        private final BigInteger descriptorHash;
-        private final boolean changingModule;
-        private final ModuleSource delegate;
-
-        public CachingModuleSource(BigInteger descriptorHash, boolean changingModule, ModuleSource delegate) {
-            this.delegate = delegate;
-            this.descriptorHash = descriptorHash;
-            this.changingModule = changingModule;
-        }
-
-        public BigInteger getDescriptorHash() {
-            return descriptorHash;
-        }
-
-        public boolean isChangingModule() {
-            return changingModule;
-        }
-
-        public ModuleSource getDelegate() {
-            return delegate;
-        }
-    }
-
-    static class ArtifactIdToMetaData implements Transformer<ModuleVersionArtifactMetaData, ModuleVersionArtifactIdentifier> {
-        public ModuleVersionArtifactMetaData transform(ModuleVersionArtifactIdentifier original) {
-            return new DefaultModuleVersionArtifactMetaData(original);
-        }
-    }
-
-    static class ArtifactMetaDataToId implements Transformer<ModuleVersionArtifactIdentifier, ComponentArtifactMetaData> {
-        public ModuleVersionArtifactIdentifier transform(ComponentArtifactMetaData original) {
-            return ((ModuleVersionArtifactMetaData)original).getId();
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ChangingModuleDetector.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ChangingModuleDetector.java
deleted file mode 100644
index aa7b592..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ChangingModuleDetector.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.ivyservice.ivyresolve;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.plugins.matcher.Matcher;
-import org.apache.ivy.plugins.matcher.NoMatcher;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.resolver.AbstractResolver;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.internal.reflect.JavaReflectionUtil;
-
-public class ChangingModuleDetector {
-    private final DependencyResolver resolver;
-
-    public ChangingModuleDetector(DependencyResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public boolean isChangingModule(ModuleDescriptor moduleDescriptor) {
-        return getChangingMatcher().matches(moduleDescriptor.getResolvedModuleRevisionId().getRevision());
-    }
-
-    private Matcher getChangingMatcher() {
-        if (!(resolver instanceof AbstractResolver)) {
-            return NoMatcher.INSTANCE;
-        }
-
-        AbstractResolver abstractResolver = (AbstractResolver) resolver;
-        String changingMatcherName = JavaReflectionUtil.method(AbstractResolver.class, String.class, "getChangingMatcherName").invoke(abstractResolver);
-        String changingPattern = JavaReflectionUtil.method(AbstractResolver.class, String.class, "getChangingPattern").invoke(abstractResolver);
-        if (changingMatcherName == null || changingPattern == null) {
-            return NoMatcher.INSTANCE;
-        }
-        PatternMatcher matcher = abstractResolver.getSettings().getMatcher(changingMatcherName);
-        if (matcher == null) {
-            throw new IllegalStateException("unknown matcher '" + changingMatcherName
-                    + "'. It is set as changing matcher in " + this);
-        }
-        return matcher.getMatcher(changingPattern);
-    }
-}
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
deleted file mode 100644
index 76383be..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleVersionRepository.java
+++ /dev/null
@@ -1,23 +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;
-
-public interface ConfiguredModuleVersionRepository extends LocalArtifactsModuleVersionRepository {
-    boolean isDynamicResolveMode();
-
-    boolean isLocal();
-}
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
deleted file mode 100644
index 5fda402..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResult.java
+++ /dev/null
@@ -1,95 +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.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-public class DefaultBuildableModuleVersionMetaDataResolveResult implements BuildableModuleVersionMetaDataResolveResult {
-    private State state = State.Unknown;
-    private ModuleSource moduleSource;
-    private ModuleVersionResolveException failure;
-    private MutableModuleVersionMetaData metaData;
-
-    private void reset(State state) {
-        this.state = state;
-        metaData = null;
-        failure = null;
-        moduleSource = null;
-    }
-
-    public void reset() {
-        reset(State.Unknown);
-    }
-
-    public void resolved(MutableModuleVersionMetaData metaData, ModuleSource moduleSource) {
-        reset(State.Resolved);
-        this.metaData = metaData;
-        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;
-    }
-
-    public MutableModuleVersionMetaData getMetaData() throws ModuleVersionResolveException {
-        assertResolved();
-        return metaData;
-    }
-
-    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 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/DefaultBuildableModuleVersionSelectionResolveResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionSelectionResolveResult.java
deleted file mode 100644
index b1a788d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionSelectionResolveResult.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-
-public class DefaultBuildableModuleVersionSelectionResolveResult implements BuildableModuleVersionSelectionResolveResult {
-    private State state = State.Unknown;
-    private ModuleVersionResolveException failure;
-    private ModuleVersionListing versions;
-
-    private void reset(State state) {
-        this.state = state;
-        versions = null;
-        failure = null;
-    }
-
-    public State getState() {
-        return state;
-    }
-
-    public ModuleVersionListing getVersions() throws ModuleVersionResolveException {
-        return versions;
-    }
-
-    public ModuleVersionResolveException getFailure() {
-        return failure;
-    }
-
-    public void listed(ModuleVersionListing versions) {
-        reset(State.Listed);
-        this.versions = versions;
-    }
-
-    public void probablyListed(ModuleVersionListing versions) {
-        reset(State.ProbablyListed);
-        this.versions = versions;
-    }
-
-    public void failed(ModuleVersionResolveException failure) {
-        reset(State.Failed);
-        this.failure = failure;
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultModuleVersionListing.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultModuleVersionListing.java
deleted file mode 100644
index 8a81e7a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultModuleVersionListing.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.Transformer;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-import org.gradle.util.CollectionUtils;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultModuleVersionListing implements ModuleVersionListing {
-    private final Set<Versioned> versions = new HashSet<Versioned>();
-
-    public DefaultModuleVersionListing(String version) {
-        add(version);
-    }
-
-    public DefaultModuleVersionListing() {
-    }
-
-    public void add(String version) {
-        versions.add(new DefaultAvailableVersion(version));
-    }
-
-    public Set<Versioned> getVersions() {
-        return versions;
-    }
-
-    public boolean isEmpty() {
-        return versions.isEmpty();
-    }
-
-    public List<Versioned> sortLatestFirst(LatestStrategy latestStrategy) {
-        List<Versioned> sorted = latestStrategy.sort(versions);
-        Collections.reverse(sorted);
-        return sorted;
-    }
-
-    @Override
-    public String toString() {
-        return CollectionUtils.collect(versions, new Transformer<String, Versioned>() {
-            public String transform(Versioned original) {
-                return original.getVersion();
-            }
-        }).toString();
-    }
-
-    private static class DefaultAvailableVersion implements Versioned {
-        private final String version;
-
-        public DefaultAvailableVersion(String version) {
-            this.version = version;
-        }
-
-        public String getVersion() {
-            return version;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            return o instanceof DefaultAvailableVersion
-                    && version.equals(((DefaultAvailableVersion) o).version);
-        }
-
-        @Override
-        public int hashCode() {
-            return version.hashCode();
-        }
-
-        @Override
-        public String toString() {
-            return String.format("AvailableVersion '%s'", 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
deleted file mode 100644
index 12c3d90..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.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.api.internal.artifacts.ivyservice.ivyresolve;
-
-import org.apache.ivy.plugins.resolver.AbstractPatternsBasedResolver;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
-import org.gradle.util.CollectionUtils;
-import org.gradle.internal.hash.HashUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DependencyResolverIdentifier {
-    public static String forIvyResolver(DependencyResolver resolver) {
-        List<String> parts = new ArrayList<String>();
-        parts.add(resolver.getClass().getName());
-        if (resolver instanceof AbstractPatternsBasedResolver) {
-            AbstractPatternsBasedResolver patternsBasedResolver = (AbstractPatternsBasedResolver) resolver;
-            parts.add(joinPatterns(patternsBasedResolver.getIvyPatterns()));
-            parts.add(joinPatterns(patternsBasedResolver.getArtifactPatterns()));
-            if (patternsBasedResolver.isM2compatible()) {
-                parts.add("m2compatible");
-            }
-        } else {
-            parts.add(resolver.getName());
-            // TODO We should not be assuming equality between resolvers here based on name...
-        }
-
-        return calculateId(parts);
-    }
-
-    // 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 static String calculateId(List<String> parts) {
-        String idString = CollectionUtils.join("::", parts);
-        return HashUtil.createHash(idString, "MD5").asHexString();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
deleted file mode 100644
index cdfac2c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-public class ErrorHandlingArtifactResolver implements ArtifactResolver {
-    private final ArtifactResolver resolver;
-
-    public ErrorHandlingArtifactResolver(ArtifactResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        try {
-            resolver.resolveModuleArtifacts(component, context, result);
-        } catch (Throwable t) {
-            result.failed(new ArtifactResolveException(component.getComponentId(), t));
-        }
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        try {
-            resolver.resolveArtifact(artifact, moduleSource, result);
-        } catch (Throwable t) {
-            result.failed(new ArtifactResolveException(artifact.getId(), t));
-        }
-    }
-}
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
deleted file mode 100644
index 1b66ddc..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyAwareModuleVersionRepository.java
+++ /dev/null
@@ -1,26 +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.resolve.ResolveData;
-import org.apache.ivy.core.settings.IvySettings;
-
-public interface IvyAwareModuleVersionRepository extends ModuleVersionRepository {
-    void setSettings(IvySettings settings);
-
-    void setResolveData(ResolveData resolveData);
-}
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
deleted file mode 100644
index fec7c74..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepository.java
+++ /dev/null
@@ -1,81 +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.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.metadata.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class IvyDynamicResolveModuleVersionRepository implements LocalAwareModuleVersionRepository {
-    private final LocalAwareModuleVersionRepository repository;
-
-    public IvyDynamicResolveModuleVersionRepository(LocalAwareModuleVersionRepository repository) {
-        this.repository = repository;
-    }
-
-    public String getId() {
-        return repository.getId();
-    }
-
-    public String getName() {
-        return repository.getName();
-    }
-
-    public void localListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        repository.localListModuleVersions(dependency, result);
-    }
-
-    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        repository.listModuleVersions(dependency, result);
-    }
-
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        repository.getLocalDependency(dependency, result);
-        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
-            transformDependencies(result);
-        }
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        repository.getDependency(dependency, result);
-        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
-            transformDependencies(result);
-        }
-    }
-
-    private void transformDependencies(BuildableModuleVersionMetaDataResolveResult result) {
-        MutableModuleVersionMetaData metaData = result.getMetaData();
-        List<DependencyMetaData> transformed = new ArrayList<DependencyMetaData>();
-        for (DependencyMetaData dependency : metaData.getDependencies()) {
-            transformed.add(dependency.withRequestedVersion(dependency.getDescriptor().getDynamicConstraintDependencyRevisionId().getRevision()));
-        }
-        metaData.setDependencies(transformed);
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        repository.resolveModuleArtifacts(component, context, result);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        repository.resolveArtifact(artifact, moduleSource, result);
-    }
-
-}
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
deleted file mode 100755
index 90705f1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolver.java
+++ /dev/null
@@ -1,155 +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.module.descriptor.Configuration;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-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;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-/**
- * A {@link org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionIdResolver} implementation which returns lazy resolvers that don't actually retrieve module descriptors until
- * required.
- */
-public class LazyDependencyToModuleResolver implements DependencyToModuleVersionIdResolver {
-    private final DependencyToModuleVersionResolver dependencyResolver;
-    private final VersionMatcher versionMatcher;
-
-    public LazyDependencyToModuleResolver(DependencyToModuleVersionResolver dependencyResolver, VersionMatcher versionMatcher) {
-        this.dependencyResolver = dependencyResolver;
-        this.versionMatcher = versionMatcher;
-    }
-
-    public ModuleVersionIdResolveResult resolve(DependencyMetaData dependency) {
-        if (versionMatcher.isDynamic(dependency.getRequested().getVersion())) {
-            DynamicVersionResolveResult result = new DynamicVersionResolveResult(dependency);
-            result.resolve();
-            return result;
-        }
-        return new StaticVersionResolveResult(dependency);
-    }
-
-    private abstract class AbstractVersionResolveResult implements ModuleVersionIdResolveResult {
-        final DependencyMetaData dependency;
-        private BuildableComponentResolveResult resolveResult;
-
-        public AbstractVersionResolveResult(DependencyMetaData dependency) {
-            this.dependency = dependency;
-        }
-
-        public ModuleVersionResolveException getFailure() {
-            return null;
-        }
-
-        public ComponentResolveResult resolve() {
-            if (resolveResult == null) {
-                resolveResult = new DefaultBuildableComponentResolveResult();
-                try {
-                    try {
-                        dependencyResolver.resolve(dependency, resolveResult);
-                    } catch (Throwable t) {
-                        throw new ModuleVersionResolveException(dependency.getRequested(), t);
-                    }
-                    if (resolveResult.getFailure() instanceof ModuleVersionNotFoundException) {
-                        throw notFound();
-                    }
-                    if (resolveResult.getFailure() != null) {
-                        throw resolveResult.getFailure();
-                    }
-                    checkDescriptor(resolveResult.getMetaData());
-                } catch (ModuleVersionResolveException e) {
-                    resolveResult.failed(e);
-                }
-            }
-
-            return resolveResult;
-        }
-
-        public ComponentSelectionReason getSelectionReason() {
-            return VersionSelectionReasons.REQUESTED;
-        }
-
-        protected void checkDescriptor(ComponentMetaData metaData) {
-            ModuleDescriptor moduleDescriptor = metaData.getDescriptor();
-            for (Configuration configuration : moduleDescriptor.getConfigurations()) {
-                for (String parent : configuration.getExtends()) {
-                    if (moduleDescriptor.getConfiguration(parent) == null) {
-                        throw new ModuleVersionResolveException(metaData.getId(), String.format("Configuration '%s' extends unknown configuration '%s' in module descriptor for %%s.", configuration.getName(), parent));
-                    }
-                }
-            }
-        }
-
-        protected abstract ModuleVersionNotFoundException notFound();
-    }
-
-    private class StaticVersionResolveResult extends AbstractVersionResolveResult {
-        private final ModuleVersionIdentifier id;
-
-        public StaticVersionResolveResult(DependencyMetaData dependency) {
-            super(dependency);
-            ModuleVersionSelector requested = dependency.getRequested();
-            id = new DefaultModuleVersionIdentifier(requested.getGroup(), requested.getName(), requested.getVersion());
-        }
-
-        public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
-            return id;
-        }
-
-        public ComponentSelectionReason getSelectionReason() {
-            return VersionSelectionReasons.REQUESTED;
-        }
-
-        @Override
-        protected void checkDescriptor(ComponentMetaData metaData) {
-            if (!id.equals(metaData.getId())) {
-                throw new ModuleVersionResolveException(dependency.getRequested(), String.format("Received unexpected module descriptor %s for dependency %%s.", metaData.getId()));
-            }
-            super.checkDescriptor(metaData);
-        }
-
-        protected ModuleVersionNotFoundException notFound() {
-            return new ModuleVersionNotFoundException(id);
-        }
-    }
-
-    private class DynamicVersionResolveResult extends AbstractVersionResolveResult {
-        public DynamicVersionResolveResult(DependencyMetaData dependency) {
-            super(dependency);
-        }
-
-        @Override
-        public ModuleVersionResolveException getFailure() {
-            return resolve().getFailure();
-        }
-
-        public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
-            return resolve().getId();
-        }
-
-        @Override
-        protected ModuleVersionNotFoundException notFound() {
-            return new ModuleVersionNotFoundException(dependency.getRequested());
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalArtifactsModuleVersionRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalArtifactsModuleVersionRepository.java
deleted file mode 100644
index fe63cd9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalArtifactsModuleVersionRepository.java
+++ /dev/null
@@ -1,31 +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.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-// TODO This is a bit of a hack to allow us to avoid caching on the filesystem when the set of artifacts can be calculated cheaply.
-// A better long term solution might be to push the caching down to the resource layer, so that the answer to artifactExists() could be cached.
-public interface LocalArtifactsModuleVersionRepository extends ModuleVersionRepository {
-    /**
-     * Resolves a set of artifacts belonging to the gi  ven module, without making any network requests. Any failures are packaged up in the result.
-     * If no local resolution is possible, then the result should be set via {@link #resolveModuleArtifacts}.
-     */
-    void localResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult 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
deleted file mode 100644
index f910094..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalAwareModuleVersionRepository.java
+++ /dev/null
@@ -1,42 +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.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-public interface LocalAwareModuleVersionRepository extends ModuleVersionRepository {
-    /**
-     * Lists the available versions for a module, using only local resources.
-     */
-    void localListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result);
-
-    /**
-     * Lists the available versions for a module, using whichever resources are appropriate.
-     * Always called after {@link #localListModuleVersions(org.gradle.api.internal.artifacts.metadata.DependencyMetaData, BuildableModuleVersionSelectionResolveResult)}.
-     */
-    void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result);
-
-    /**
-     * Locates the given dependency, using only local resources.
-     */
-    void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
-
-    /**
-     * Locates the given dependency, using whichever resources are appropriate. Always called after {@link #getLocalDependency(DependencyMetaData, BuildableModuleVersionMetaDataResolveResult)}.
-     */
-    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
deleted file mode 100644
index 2ba8838..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleVersionRepository.java
+++ /dev/null
@@ -1,69 +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.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-public class LocalModuleVersionRepository implements LocalAwareModuleVersionRepository {
-    private final ModuleVersionRepository delegate;
-    private final ModuleMetadataProcessor processor;
-
-    public LocalModuleVersionRepository(ModuleVersionRepository delegate, ModuleMetadataProcessor processor) {
-        this.delegate = delegate;
-        this.processor = processor;
-    }
-
-    public void localListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        delegate.listModuleVersions(dependency, result);
-    }
-
-    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-    }
-
-    public void getLocalDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        delegate.getDependency(dependency, result);
-        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
-            processor.process(result.getMetaData());
-        }
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        result.missing();
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        delegate.resolveModuleArtifacts(component, context, result);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        delegate.resolveArtifact(artifact, moduleSource, result);
-    }
-
-    public String getId() {
-        return delegate.getId();
-    }
-
-    public String getName() {
-        return delegate.getName();
-    }
-}
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
deleted file mode 100644
index 03ee5d8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LoopbackDependencyResolver.java
+++ /dev/null
@@ -1,186 +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.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.*;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.DefaultDependencyMetaData;
-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 implements DependencyResolver {
-    private final String name;
-    private final DependencyToModuleVersionResolver dependencyResolver;
-    private final ArtifactResolver artifactResolver;
-    private final CacheLockingManager cacheLockingManager;
-
-    public LoopbackDependencyResolver(String name, RepositoryChain repositoryChain, CacheLockingManager cacheLockingManager) {
-        this.name = name;
-        this.dependencyResolver = repositoryChain.getDependencyResolver();
-        this.artifactResolver = repositoryChain.getArtifactResolver();
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setSettings(ResolverSettings settings) {
-        // don't care
-    }
-
-    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>() {
-            public ResolvedModuleRevision create() {
-                DefaultBuildableComponentResolveResult result = new DefaultBuildableComponentResolveResult();
-                DefaultDependencyMetaData dependency = new DefaultDependencyMetaData(dd);
-                IvyContext ivyContext = IvyContext.pushNewCopyContext();
-                try {
-                    ivyContext.setResolveData(data);
-                    dependencyResolver.resolve(dependency, result);
-                } finally {
-                    IvyContext.popContext();
-                }
-                return new ResolvedModuleRevision(loopback, loopback, result.getMetaData().getDescriptor(), null);
-            }
-        });
-    }
-
-    public ArtifactOrigin locate(final Artifact artifact) {
-        return cacheLockingManager.useCache(String.format("Locate %s", artifact), new Factory<ArtifactOrigin>() {
-            public ArtifactOrigin create() {
-                try {
-                    DependencyDescriptor dependencyDescriptor = new DefaultDependencyDescriptor(artifact.getModuleRevisionId(), false);
-                    DefaultBuildableComponentResolveResult resolveResult = new DefaultBuildableComponentResolveResult();
-                    DefaultDependencyMetaData dependency = new DefaultDependencyMetaData(dependencyDescriptor);
-                    dependencyResolver.resolve(dependency, resolveResult);
-                    DefaultBuildableArtifactResolveResult artifactResolveResult = new DefaultBuildableArtifactResolveResult();
-                    ComponentArtifactMetaData artifactMetaData = resolveResult.getMetaData().artifact(artifact);
-                    artifactResolver.resolveArtifact(artifactMetaData, resolveResult.getMetaData().getSource(), artifactResolveResult);
-                    File artifactFile = artifactResolveResult.getFile();
-                    return new ArtifactOrigin(artifact, false, artifactFile.getAbsolutePath());
-                } catch (ModuleVersionNotFoundException e) {
-                    return null;
-                } catch (ArtifactNotFoundException e) {
-                    return null;
-                }
-            }
-        });
-    }
-
-    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
deleted file mode 100644
index a2a980b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleSource.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.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/ModuleVersionListing.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionListing.java
deleted file mode 100644
index 51b33e8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionListing.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-
-import java.util.List;
-import java.util.Set;
-
-public interface ModuleVersionListing {
-
-    Set<Versioned> getVersions();
-
-    boolean isEmpty();
-
-    List<Versioned> sortLatestFirst(LatestStrategy latestStrategy);
-}
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
deleted file mode 100644
index 6ff7911..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleVersionRepository.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.api.internal.artifacts.ivyservice.ivyresolve;
-
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-/**
- * A repository of module versions.
- *
- * 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 extends ArtifactResolver {
-    String getId();
-
-    String getName();
-
-    /**
-     * Resolves the given dependency to a list of module versions.
-     */
-    void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result);
-
-    /**
-     * Resolves the given dependency to the corresponding module version meta-data.
-     */
-    void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoOpRepositoryCacheManager.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoOpRepositoryCacheManager.java
deleted file mode 100644
index 96c190f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoOpRepositoryCacheManager.java
+++ /dev/null
@@ -1,76 +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.*;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadStatus;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
-import org.apache.ivy.plugins.repository.ResourceDownloader;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.apache.ivy.plugins.resolver.util.ResolvedResource;
-
-import java.text.ParseException;
-
-/**
- * A cache manager which does nothing. Only useful for local meta-data only repositories.
- */
-public class NoOpRepositoryCacheManager implements RepositoryCacheManager {
-    private final String name;
-
-    public NoOpRepositoryCacheManager(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void saveResolvers(ModuleDescriptor descriptor, String metadataResolverName, String artifactResolverName) {
-    }
-
-    public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
-        return null;
-    }
-
-    public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId, CacheMetadataOptions options, String expectedResolver) {
-        return null;
-    }
-
-    public ArtifactDownloadReport download(Artifact artifact, ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader, CacheDownloadOptions options) {
-        ArtifactDownloadReport report = new ArtifactDownloadReport(null);
-        report.setDownloadStatus(DownloadStatus.NO);
-        return report;
-    }
-
-    public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, DependencyDescriptor dd, Artifact requestedMetadataArtifact, ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
-        return null;
-    }
-
-    public void originalToCachedModuleDescriptor(DependencyResolver resolver, ResolvedResource orginalMetadataRef, Artifact requestedMetadataArtifact, ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
-    }
-
-    public void clean() {
-    }
-
-    public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision) {
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java
deleted file mode 100644
index d861337..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
-
-public interface RepositoryChain {
-    public DependencyToModuleVersionResolver getDependencyResolver();
-    public ArtifactResolver getArtifactResolver();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
deleted file mode 100644
index 063ec66..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.internal.Transformers;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-// TODO:DAZ Unit test
-class RepositoryChainArtifactResolver implements ArtifactResolver {
-    private final Map<String, ModuleVersionRepository> repositories = new LinkedHashMap<String, ModuleVersionRepository>();
-
-    void add(ModuleVersionRepository repository) {
-        repositories.put(repository.getId(), repository);
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        findSourceRepository(component.getSource()).resolveModuleArtifacts(unpackSource(component), context, result);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource source, BuildableArtifactResolveResult result) {
-        findSourceRepository(source).resolveArtifact(artifact, unpackSource(source), result);
-    }
-
-    private ModuleVersionRepository findSourceRepository(ModuleSource originalSource) {
-        ModuleVersionRepository moduleVersionRepository = repositories.get(repositorySource(originalSource).getRepositoryId());
-        if (moduleVersionRepository == null) {
-            throw new IllegalStateException("Attempting to resolve artifacts from invalid repository");
-        }
-        return moduleVersionRepository;
-    }
-
-    private RepositoryChainModuleSource repositorySource(ModuleSource original) {
-        return Transformers.cast(RepositoryChainModuleSource.class).transform(original);
-    }
-
-    private ModuleSource unpackSource(ModuleSource original) {
-        return repositorySource(original).getDelegate();
-    }
-
-    private ComponentMetaData unpackSource(ComponentMetaData component) {
-        return component.withSource(repositorySource(component.getSource()).getDelegate());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java
deleted file mode 100644
index 9946d15..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java
+++ /dev/null
@@ -1,297 +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.gradle.api.Transformer;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableComponentResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-// TODO:DAZ This needs to be broken up
-public class RepositoryChainDependencyResolver implements DependencyToModuleVersionResolver {
-    private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryChainDependencyResolver.class);
-
-    private final List<LocalAwareModuleVersionRepository> moduleVersionRepositories = new ArrayList<LocalAwareModuleVersionRepository>();
-    private final List<String> moduleVersionRepositoryNames = new ArrayList<String>();
-    private final VersionMatcher versionMatcher;
-    private final LatestStrategy latestStrategy;
-    private final Transformer<ModuleVersionMetaData, RepositoryChainModuleResolution> metaDataFactory;
-
-    public RepositoryChainDependencyResolver(VersionMatcher versionMatcher, LatestStrategy latestStrategy, Transformer<ModuleVersionMetaData, RepositoryChainModuleResolution> metaDataFactory) {
-        this.versionMatcher = versionMatcher;
-        this.latestStrategy = latestStrategy;
-        this.metaDataFactory = metaDataFactory;
-    }
-
-    public void add(LocalAwareModuleVersionRepository repository) {
-        moduleVersionRepositories.add(repository);
-        moduleVersionRepositoryNames.add(repository.getName());
-    }
-
-    public void resolve(DependencyMetaData dependency, BuildableComponentResolveResult result) {
-        ModuleVersionSelector requested = dependency.getRequested();
-        LOGGER.debug("Attempting to resolve module '{}' using repositories {}", requested, moduleVersionRepositoryNames);
-        List<Throwable> errors = new ArrayList<Throwable>();
-        final RepositoryChainModuleResolution latestResolved = findLatestModule(dependency, errors);
-        if (latestResolved != null) {
-            LOGGER.debug("Using module '{}' from repository '{}'", latestResolved.module.getId(), latestResolved.repository.getName());
-            for (Throwable error : errors) {
-                LOGGER.debug("Discarding resolve failure.", error);
-            }
-
-            result.resolved(metaDataFactory.transform(latestResolved));
-            return;
-        }
-        if (!errors.isEmpty()) {
-            result.failed(new ModuleVersionResolveException(requested, errors));
-        } else {
-            result.notFound(requested);
-        }
-    }
-
-    private RepositoryChainModuleResolution findLatestModule(DependencyMetaData dependency, Collection<Throwable> failures) {
-        LinkedList<RepositoryResolveState> queue = new LinkedList<RepositoryResolveState>();
-        for (LocalAwareModuleVersionRepository repository : moduleVersionRepositories) {
-            queue.add(createRepositoryResolveState(repository, dependency));
-        }
-        LinkedList<RepositoryResolveState> missing = new LinkedList<RepositoryResolveState>();
-
-        // A first pass to do local resolves only
-        RepositoryChainModuleResolution best = findLatestModule(dependency, queue, failures, missing);
-        if (best != null) {
-            return best;
-        }
-
-        // Nothing found - do a second pass
-        queue.addAll(missing);
-        missing.clear();
-        return findLatestModule(dependency, queue, failures, missing);
-    }
-
-    private RepositoryResolveState createRepositoryResolveState(LocalAwareModuleVersionRepository repository, DependencyMetaData dependency) {
-        if (versionMatcher.isDynamic(dependency.getRequested().getVersion())) {
-            return new DynamicVersionRepositoryResolveState(repository);
-        }
-        return new StaticVersionRepositoryResolveState(repository);
-    }
-
-    private RepositoryChainModuleResolution findLatestModule(DependencyMetaData dependency, LinkedList<RepositoryResolveState> queue, Collection<Throwable> failures, Collection<RepositoryResolveState> missing) {
-        boolean isStaticVersion = !versionMatcher.isDynamic(dependency.getRequested().getVersion());
-        RepositoryChainModuleResolution best = null;
-        while (!queue.isEmpty()) {
-            RepositoryResolveState request = queue.removeFirst();
-            try {
-                request.resolve(dependency);
-            } catch (Throwable t) {
-                failures.add(t);
-                continue;
-            }
-            switch (request.resolveResult.getState()) {
-                case Missing:
-                    break;
-                case ProbablyMissing:
-                    // Queue this up for checking again later
-                    if (request.canMakeFurtherAttempts()) {
-                        missing.add(request);
-                    }
-                    break;
-                case Unknown:
-                    // Resolve again now
-                    if (request.canMakeFurtherAttempts()) {
-                        queue.addFirst(request);
-                    }
-                    break;
-                case Resolved:
-                    RepositoryChainModuleResolution moduleResolution = new RepositoryChainModuleResolution(request.repository, request.resolveResult.getMetaData(), request.resolveResult.getModuleSource());
-                    if (isStaticVersion && !moduleResolution.isGeneratedModuleDescriptor()) {
-                        return moduleResolution;
-                    }
-                    best = chooseBest(best, moduleResolution);
-                    break;
-                default:
-                    throw new IllegalStateException("Unexpected state for resolution: " + request.resolveResult.getState());
-            }
-        }
-
-        return best;
-    }
-
-    private RepositoryChainModuleResolution chooseBest(RepositoryChainModuleResolution one, RepositoryChainModuleResolution two) {
-        if (one == null || two == null) {
-            return two == null ? one : two;
-        }
-        if (one.module == null || two.module == null) {
-            return two.module == null ? one : two;
-        }
-
-        int comparison = latestStrategy.compare(one, two);
-
-        if (comparison == 0) {
-            if (one.isGeneratedModuleDescriptor() && !two.isGeneratedModuleDescriptor()) {
-                return two;
-            }
-            return one;
-        }
-
-        return comparison < 0 ? two : one;
-    }
-
-    public static abstract class RepositoryResolveState {
-        final LocalAwareModuleVersionRepository repository;
-        final DefaultBuildableModuleVersionSelectionResolveResult selectionResult = new DefaultBuildableModuleVersionSelectionResolveResult();
-        final DefaultBuildableModuleVersionMetaDataResolveResult resolveResult = new DefaultBuildableModuleVersionMetaDataResolveResult();
-        boolean searchedLocally;
-        boolean searchedRemotely;
-
-        public RepositoryResolveState(LocalAwareModuleVersionRepository repository) {
-            this.repository = repository;
-        }
-
-        void resolve(DependencyMetaData dependency) {
-            if (!searchedLocally) {
-                searchedLocally = true;
-                process(dependency, new LocalModuleAccess());
-            } else {
-                searchedRemotely = true;
-                process(dependency, new RemoteModuleAccess());
-            }
-            if (resolveResult.getState() == BuildableModuleVersionMetaDataResolveResult.State.Failed) {
-                throw resolveResult.getFailure();
-            }
-        }
-
-        protected abstract void process(DependencyMetaData dependency, ModuleAccess localModuleAccess);
-
-        public boolean canMakeFurtherAttempts() {
-            return !searchedRemotely;
-        }
-
-        protected interface ModuleAccess {
-            void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result);
-            void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result);
-        }
-
-        protected class LocalModuleAccess implements ModuleAccess {
-            public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-                repository.localListModuleVersions(dependency, result);
-            }
-
-            public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-                repository.getLocalDependency(dependency, result);
-            }
-        }
-
-        protected class RemoteModuleAccess implements ModuleAccess {
-            public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-                repository.listModuleVersions(dependency, result);
-            }
-
-            public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-                repository.getDependency(dependency, result);
-            }
-        }
-    }
-
-    private class StaticVersionRepositoryResolveState extends RepositoryResolveState {
-
-        public StaticVersionRepositoryResolveState(LocalAwareModuleVersionRepository repository) {
-            super(repository);
-        }
-
-        protected void process(DependencyMetaData dependency, ModuleAccess moduleAccess) {
-            moduleAccess.getDependency(dependency, resolveResult);
-        }
-    }
-
-    private class DynamicVersionRepositoryResolveState extends RepositoryResolveState {
-
-        public DynamicVersionRepositoryResolveState(LocalAwareModuleVersionRepository repository) {
-            super(repository);
-        }
-
-        protected void process(DependencyMetaData dependency, ModuleAccess moduleAccess) {
-            moduleAccess.listModuleVersions(dependency, selectionResult);
-            switch (selectionResult.getState()) {
-                case Failed:
-                    resolveResult.failed(selectionResult.getFailure());
-                    break;
-                case ProbablyListed:
-                    if (!resolveDependency(dependency, moduleAccess)) {
-                        resolveResult.probablyMissing();
-                    }
-                    break;
-                case Listed:
-                    if (!resolveDependency(dependency, moduleAccess)) {
-                        resolveResult.missing();
-                    }
-            }
-        }
-
-        private boolean resolveDependency(DependencyMetaData dependency, ModuleAccess moduleAccess) {
-            if (versionMatcher.needModuleMetadata(dependency.getRequested().getVersion())) {
-                return getBestMatchingDependencyWithMetaData(dependency, moduleAccess);
-            } else {
-                return getBestMatchingDependency(dependency, moduleAccess);
-            }
-        }
-
-        private boolean getBestMatchingDependency(DependencyMetaData dependency, ModuleAccess moduleAccess) {
-            ModuleVersionSelector selector = dependency.getRequested();
-            for (Versioned candidate : selectionResult.getVersions().sortLatestFirst(latestStrategy)) {
-                if (versionMatcher.accept(selector.getVersion(), candidate.getVersion())) {
-                    moduleAccess.getDependency(dependency.withRequestedVersion(candidate.getVersion()), resolveResult);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private boolean getBestMatchingDependencyWithMetaData(DependencyMetaData dependency, ModuleAccess moduleAccess) {
-            ModuleVersionSelector selector = dependency.getRequested();
-            for (Versioned candidate : selectionResult.getVersions().sortLatestFirst(latestStrategy)) {
-                // Resolve the metadata
-                DependencyMetaData moduleVersionDependency = dependency.withRequestedVersion(candidate.getVersion());
-                moduleAccess.getDependency(moduleVersionDependency, resolveResult);
-                if (resolveResult.getState() != BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
-                    // Couldn't load listed module
-                    LOGGER.warn("Could not load metadata for '{}' of listed module '{}' - ignoring.", candidate, selector);
-                    resolveResult.reset();
-                    return true;
-                }
-                if (versionMatcher.accept(selector.getVersion(), resolveResult.getMetaData())) {
-                    // We already resolved the correct module.
-                    return true;
-                }
-                resolveResult.reset();
-            }
-            return false;
-        }
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
deleted file mode 100644
index d018e2d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-class RepositoryChainModuleResolution implements Versioned {
-    public final ModuleVersionRepository repository;
-    public final MutableModuleVersionMetaData module;
-    public final ModuleSource moduleSource;
-
-    public RepositoryChainModuleResolution(ModuleVersionRepository repository, MutableModuleVersionMetaData module, ModuleSource moduleSource) {
-        this.repository = repository;
-        this.module = module;
-        this.moduleSource = moduleSource;
-    }
-
-    public boolean isGeneratedModuleDescriptor() {
-        return module.getDescriptor().isDefault();
-    }
-
-    public String getVersion() {
-        return module.getId().getVersion();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
deleted file mode 100644
index b53f7b5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
-
-class RepositoryChainModuleSource implements ModuleSource {
-    private final String repositoryId;
-    private final ModuleSource delegate;
-
-    public RepositoryChainModuleSource(String repositoryId, ModuleSource delegate) {
-        this.repositoryId = repositoryId;
-        this.delegate = delegate;
-    }
-
-    public String getRepositoryId() {
-        return repositoryId;
-    }
-
-    public ModuleSource getDelegate() {
-        return delegate;
-    }
-}
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
deleted file mode 100644
index e87d006..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
+++ /dev/null
@@ -1,202 +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.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.Transformer;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.cache.ResolutionRules;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
-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.VersionMatcher;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-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.util.BuildCommencedTimeProvider;
-import org.gradle.util.WrapUtil;
-
-public class ResolveIvyFactory {
-    private final ModuleVersionsCache moduleVersionsCache;
-    private final ModuleMetaDataCache moduleMetaDataCache;
-    private final ModuleArtifactsCache moduleArtifactsCache;
-    private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
-    private final CacheLockingManager cacheLockingManager;
-    private final StartParameterResolutionOverride startParameterResolutionOverride;
-    private final BuildCommencedTimeProvider timeProvider;
-    private final InMemoryDependencyMetadataCache inMemoryCache;
-    private final VersionMatcher versionMatcher;
-    private final LatestStrategy latestStrategy;
-
-    public ResolveIvyFactory(ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache, ModuleArtifactsCache moduleArtifactsCache,
-                             CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
-                             CacheLockingManager cacheLockingManager, StartParameterResolutionOverride startParameterResolutionOverride,
-                             BuildCommencedTimeProvider timeProvider, InMemoryDependencyMetadataCache inMemoryCache, VersionMatcher versionMatcher, LatestStrategy latestStrategy) {
-        this.moduleVersionsCache = moduleVersionsCache;
-        this.moduleMetaDataCache = moduleMetaDataCache;
-        this.moduleArtifactsCache = moduleArtifactsCache;
-        this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
-        this.cacheLockingManager = cacheLockingManager;
-        this.startParameterResolutionOverride = startParameterResolutionOverride;
-        this.timeProvider = timeProvider;
-        this.inMemoryCache = inMemoryCache;
-        this.versionMatcher = versionMatcher;
-        this.latestStrategy = latestStrategy;
-    }
-
-    public RepositoryChain create(ConfigurationInternal configuration,
-                                  Iterable<? extends ResolutionAwareRepository> repositories,
-                                  ModuleMetadataProcessor metadataProcessor) {
-        ResolutionRules resolutionRules = configuration.getResolutionStrategy().getResolutionRules();
-        CachePolicy cachePolicy = configuration.getResolutionStrategy().getCachePolicy();
-
-        startParameterResolutionOverride.addResolutionRules(resolutionRules);
-
-        UserResolverChain userResolverChain = new UserResolverChain(versionMatcher, latestStrategy);
-        RepositoryChain parentLookupResolver = new ParentModuleLookupResolver(userResolverChain, cacheLockingManager);
-
-        for (ResolutionAwareRepository repository : repositories) {
-            ConfiguredModuleVersionRepository moduleVersionRepository = repository.createResolver();
-
-            if (moduleVersionRepository instanceof IvyAwareModuleVersionRepository) {
-                ivyContextualize((IvyAwareModuleVersionRepository) moduleVersionRepository, userResolverChain, configuration.getName());
-            }
-            if (moduleVersionRepository instanceof ExternalResourceResolver) {
-                ((ExternalResourceResolver) moduleVersionRepository).setRepositoryChain(parentLookupResolver);
-            }
-
-            LocalAwareModuleVersionRepository localAwareRepository;
-            if (moduleVersionRepository.isLocal()) {
-                localAwareRepository = new LocalModuleVersionRepository(moduleVersionRepository, metadataProcessor);
-            } else {
-                LocalArtifactsModuleVersionRepository wrapperRepository = new CacheLockingModuleVersionRepository(moduleVersionRepository, cacheLockingManager);
-                wrapperRepository = startParameterResolutionOverride.overrideModuleVersionRepository(wrapperRepository);
-                localAwareRepository = new CachingModuleVersionRepository(wrapperRepository, moduleVersionsCache, moduleMetaDataCache, moduleArtifactsCache, artifactAtRepositoryCachedResolutionIndex,
-                        cachePolicy, timeProvider, metadataProcessor, getModuleExtractor(moduleVersionRepository));
-            }
-            if (moduleVersionRepository.isDynamicResolveMode()) {
-                localAwareRepository = new IvyDynamicResolveModuleVersionRepository(localAwareRepository);
-            }
-            localAwareRepository = inMemoryCache.cached(localAwareRepository);
-            userResolverChain.add(localAwareRepository);
-        }
-
-        return userResolverChain;
-    }
-
-    private void ivyContextualize(IvyAwareModuleVersionRepository ivyAwareRepository, RepositoryChain 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) {
-        ResolveOptions options = new ResolveOptions();
-        options.setDownload(false);
-        options.setConfs(WrapUtil.toArray(configurationName));
-        return new ResolveData(ivy.getResolveEngine(), options);
-    }
-
-    private Transformer<ModuleIdentifier, ModuleVersionSelector> getModuleExtractor(ConfiguredModuleVersionRepository rootRepository) {
-        // If the backing repository is a custom ivy resolver, then we don't get a full listing
-        if (rootRepository instanceof IvyAwareModuleVersionRepository) {
-            return new LegacyIvyResolverModuleIdExtractor();
-        }
-        return new DefaultModuleIdExtractor();
-    }
-
-    /**
-     * Provides access to the top-level resolver chain for looking up parent modules when parsing module descriptor files.
-     */
-    private static class ParentModuleLookupResolver implements RepositoryChain, DependencyToModuleVersionResolver, ArtifactResolver {
-        private final DependencyToModuleVersionResolver dependencyResolver;
-        private final ArtifactResolver artifactResolver;
-        private final CacheLockingManager cacheLockingManager;
-
-        public ParentModuleLookupResolver(RepositoryChain repositoryChain, CacheLockingManager cacheLockingManager) {
-            this.dependencyResolver = repositoryChain.getDependencyResolver();
-            this.artifactResolver = repositoryChain.getArtifactResolver();
-            this.cacheLockingManager = cacheLockingManager;
-        }
-
-        public ArtifactResolver getArtifactResolver() {
-            return this;
-        }
-
-        public DependencyToModuleVersionResolver getDependencyResolver() {
-            return this;
-        }
-
-        public void resolve(final DependencyMetaData dependency, final BuildableComponentResolveResult result) {
-            cacheLockingManager.useCache(String.format("Resolve %s", dependency), new Runnable() {
-                public void run() {
-                    dependencyResolver.resolve(dependency, result);
-                }
-            });
-        }
-
-        public void resolveModuleArtifacts(final ComponentMetaData component, final ArtifactResolveContext context, final BuildableArtifactSetResolveResult result) {
-            cacheLockingManager.useCache(String.format("Resolve %s for %s", context.getDescription(), component), new Runnable() {
-                public void run() {
-                    artifactResolver.resolveModuleArtifacts(component, context, result);
-                }
-            });
-        }
-
-        public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
-            cacheLockingManager.useCache(String.format("Resolve %s", artifact), new Runnable() {
-                public void run() {
-                    artifactResolver.resolveArtifact(artifact, moduleSource, result);
-                }
-            });
-        }
-    }
-
-    private static class DefaultModuleIdExtractor implements Transformer<ModuleIdentifier, ModuleVersionSelector> {
-        public ModuleIdentifier transform(ModuleVersionSelector original) {
-            return new DefaultModuleIdentifier(original.getGroup(), original.getName());
-        }
-    }
-
-    // When caching module listing for ivy resolvers, we can only cache for a particular version request
-    // TODO: Remove this when we remove support for Ivy DependencyResolvers
-    private static class LegacyIvyResolverModuleIdExtractor implements Transformer<ModuleIdentifier, ModuleVersionSelector> {
-        public ModuleIdentifier transform(ModuleVersionSelector original) {
-            return new DefaultModuleIdentifier(original.getGroup(), original.getName() + ":" + original.getVersion());
-        }
-    }
-}
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
deleted file mode 100644
index 3cf09f5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
+++ /dev/null
@@ -1,123 +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.gradle.StartParameter;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.cache.ArtifactResolutionControl;
-import org.gradle.api.artifacts.cache.DependencyResolutionControl;
-import org.gradle.api.artifacts.cache.ModuleResolutionControl;
-import org.gradle.api.artifacts.cache.ResolutionRules;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-import java.util.concurrent.TimeUnit;
-
-public class StartParameterResolutionOverride {
-    private final StartParameter startParameter;
-
-    public StartParameterResolutionOverride(StartParameter startParameter) {
-        this.startParameter = startParameter;
-    }
-
-    public void addResolutionRules(ResolutionRules resolutionRules) {
-        if (startParameter.isOffline()) {
-            resolutionRules.eachDependency(new Action<DependencyResolutionControl>() {
-                public void execute(DependencyResolutionControl dependencyResolutionControl) {
-                    dependencyResolutionControl.useCachedResult();
-                }
-            });
-            resolutionRules.eachModule(new Action<ModuleResolutionControl>() {
-                public void execute(ModuleResolutionControl moduleResolutionControl) {
-                    moduleResolutionControl.useCachedResult();
-                }
-            });
-            resolutionRules.eachArtifact(new Action<ArtifactResolutionControl>() {
-                public void execute(ArtifactResolutionControl artifactResolutionControl) {
-                    artifactResolutionControl.useCachedResult();
-                }
-            });
-        } else if (startParameter.isRefreshDependencies()) {
-            resolutionRules.eachDependency(new Action<DependencyResolutionControl>() {
-                public void execute(DependencyResolutionControl dependencyResolutionControl) {
-                    dependencyResolutionControl.cacheFor(0, TimeUnit.SECONDS);
-                }
-            });
-            resolutionRules.eachModule(new Action<ModuleResolutionControl>() {
-                public void execute(ModuleResolutionControl moduleResolutionControl) {
-                    moduleResolutionControl.cacheFor(0, TimeUnit.SECONDS);
-                }
-            });
-            resolutionRules.eachArtifact(new Action<ArtifactResolutionControl>() {
-                public void execute(ArtifactResolutionControl artifactResolutionControl) {
-                    artifactResolutionControl.cacheFor(0, TimeUnit.SECONDS);
-                }
-            });
-        }
-    }
-
-    public LocalArtifactsModuleVersionRepository overrideModuleVersionRepository(LocalArtifactsModuleVersionRepository original) {
-        if (startParameter.isOffline()) {
-            return new OfflineModuleVersionRepository(original);
-        }
-        return original;
-    }
-
-    private static class OfflineModuleVersionRepository implements ModuleVersionRepository, LocalArtifactsModuleVersionRepository {
-        private final LocalArtifactsModuleVersionRepository original;
-
-        public OfflineModuleVersionRepository(LocalArtifactsModuleVersionRepository original) {
-            this.original = original;
-        }
-
-        public String getId() {
-            return original.getId();
-        }
-
-        public String getName() {
-            return original.getName();
-        }
-
-        public boolean isLocal() {
-            return false;
-        }
-
-        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-            result.failed(new ModuleVersionResolveException(dependency.getRequested(), "No cached version listing for %s available for offline mode."));
-        }
-
-        public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-            result.failed(new ModuleVersionResolveException(dependency.getRequested(), "No cached version of %s available for offline mode."));
-        }
-
-        public void localResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-            original.localResolveModuleArtifacts(component, context, result);
-        }
-
-        public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-            result.failed(new ArtifactResolveException(component.getComponentId(), "No cached version available for offline mode"));
-        }
-
-        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-            result.failed(new ArtifactResolveException(artifact.getId(), "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
deleted file mode 100644
index 797fd3e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
+++ /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.artifacts.ivyservice.ivyresolve;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver;
-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;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-
-public class UserResolverChain implements RepositoryChain {
-    private final RepositoryChainDependencyResolver dependencyResolver;
-    private final RepositoryChainArtifactResolver artifactResolver = new RepositoryChainArtifactResolver();
-
-    public UserResolverChain(VersionMatcher versionMatcher, LatestStrategy latestStrategy) {
-        this.dependencyResolver = new RepositoryChainDependencyResolver(versionMatcher, latestStrategy, new ModuleTransformer());
-    }
-
-    public DependencyToModuleVersionResolver getDependencyResolver() {
-        return dependencyResolver;
-    }
-
-    public ArtifactResolver getArtifactResolver() {
-        return artifactResolver;
-    }
-
-    public void add(LocalAwareModuleVersionRepository repository) {
-        dependencyResolver.add(repository);
-        artifactResolver.add(repository);
-    }
-
-    private static class ModuleTransformer implements Transformer<ModuleVersionMetaData, RepositoryChainModuleResolution> {
-        public ModuleVersionMetaData transform(RepositoryChainModuleResolution original) {
-            RepositoryChainModuleSource moduleSource = new RepositoryChainModuleSource(original.repository.getId(), original.moduleSource);
-            return original.module.withSource(moduleSource);
-        }
-    }
-}
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
deleted file mode 100644
index ed10f9b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java
+++ /dev/null
@@ -1,55 +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.memcache;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-import static org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult.State.*;
-
-class CachedModuleVersionResult {
-    private final BuildableModuleVersionMetaDataResolveResult.State state;
-    private final MutableModuleVersionMetaData metaData;
-    private final ModuleSource moduleSource;
-
-    public CachedModuleVersionResult(BuildableModuleVersionMetaDataResolveResult result) {
-        this.state = result.getState();
-        if (state == Resolved) {
-            this.metaData = result.getMetaData().copy();
-            this.moduleSource = result.getModuleSource();
-        } else {
-            this.metaData = null;
-            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(metaData.copy(), 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
deleted file mode 100644
index bf0c0a5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepository.java
+++ /dev/null
@@ -1,90 +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.memcache;
-
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionSelectionResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-// TODO:DAZ Add in-memory caching for resolveModuleArtifacts()
-// TODO:DAZ Change this so that it's a decoration over the file-system caching, rather than something completely separate.
-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 localListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        if(!cache.supplyLocalModuleVersions(dependency.getRequested(), result)) {
-            delegate.localListModuleVersions(dependency, result);
-            cache.newLocalModuleVersions(dependency.getRequested(), result);
-        }
-    }
-
-    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        if(!cache.supplyModuleVersions(dependency.getRequested(), result)) {
-            delegate.listModuleVersions(dependency, result);
-            cache.newModuleVersions(dependency.getRequested(), result);
-        }
-    }
-
-    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 resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        delegate.resolveModuleArtifacts(component, context, result);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        if (!cache.supplyArtifact(artifact.getId(), result)) {
-            delegate.resolveArtifact(artifact, moduleSource, result);
-            cache.newArtifact(artifact.getId(), 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
deleted file mode 100644
index b2d35ca..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCache.java
+++ /dev/null
@@ -1,123 +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.memcache;
-
-import org.gradle.api.artifacts.ModuleVersionSelector;
-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.BuildableModuleVersionSelectionResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionSelectionResolveResult.State.*;
-
-class DependencyMetadataCache {
-    private final Map<ModuleVersionSelector, ModuleVersionListing> localModuleVersionListing = new HashMap<ModuleVersionSelector, ModuleVersionListing>();
-    private final Map<ModuleVersionSelector, ModuleVersionListing> moduleVersionListing = new HashMap<ModuleVersionSelector, ModuleVersionListing>();
-    private final Map<ModuleVersionSelector, CachedModuleVersionResult> localMetaData = new HashMap<ModuleVersionSelector, CachedModuleVersionResult>();
-    private final Map<ModuleVersionSelector, CachedModuleVersionResult> metaData = new HashMap<ModuleVersionSelector, CachedModuleVersionResult>();
-    private final Map<ComponentArtifactIdentifier, File> artifacts = new HashMap<ComponentArtifactIdentifier, File>();
-    private DependencyMetadataCacheStats stats;
-
-    DependencyMetadataCache(DependencyMetadataCacheStats stats) {
-        this.stats = stats;
-    }
-
-    public boolean supplyLocalModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result) {
-        return supply(requested, result, localModuleVersionListing);
-    }
-
-    public void newLocalModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result) {
-        newResult(requested, result, localModuleVersionListing);
-    }
-
-    public boolean supplyModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result) {
-        return supply(requested, result, moduleVersionListing);
-    }
-
-    public void newModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result) {
-        newResult(requested, result, moduleVersionListing);
-    }
-
-    private boolean supply(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result, Map<ModuleVersionSelector, ModuleVersionListing> map) {
-        ModuleVersionListing moduleVersionListing = map.get(requested);
-        if (moduleVersionListing == null) {
-            return false;
-        }
-        result.listed(moduleVersionListing);
-        return true;
-    }
-
-    private void newResult(ModuleVersionSelector requested, BuildableModuleVersionSelectionResolveResult result, Map<ModuleVersionSelector, ModuleVersionListing> map) {
-        if (result.getState() == Listed || result.getState() == ProbablyListed) {
-            map.put(requested, result.getVersions());
-        }
-    }
-
-    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(ComponentArtifactIdentifier id, BuildableArtifactResolveResult result) {
-        File fromCache = artifacts.get(id);
-        if (fromCache != null) {
-            result.resolved(fromCache);
-            stats.artifactsServed++;
-            return true;
-        }
-        return false;
-    }
-
-    public void newArtifact(ComponentArtifactIdentifier id, BuildableArtifactResolveResult result) {
-        if (result.getFailure() == null) {
-            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
deleted file mode 100644
index 127d5e6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheStats.java
+++ /dev/null
@@ -1,29 +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.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
deleted file mode 100644
index b4b07b8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCache.java
+++ /dev/null
@@ -1,62 +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.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.concurrent.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().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/AbstractModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
deleted file mode 100644
index 398a413..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
+++ /dev/null
@@ -1,55 +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.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-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 MetaDataParser {
-    public MutableModuleVersionMetaData parseMetaData(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 MutableModuleVersionMetaData parseMetaData(DescriptorParseContext ivySettings, File descriptorFile) throws MetaDataParseException {
-        return parseMetaData(ivySettings, descriptorFile, true);
-    }
-
-    public MutableModuleVersionMetaData parseMetaData(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource) throws MetaDataParseException {
-        return parseDescriptor(ivySettings, resource, true);
-    }
-
-    protected MutableModuleVersionMetaData 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 MutableModuleVersionMetaData 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/DescriptorParseContext.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java
deleted file mode 100644
index adabd7d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java
+++ /dev/null
@@ -1,27 +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.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-
-public interface DescriptorParseContext {
-    boolean artifactExists(ModuleVersionArtifactMetaData artifact);
-
-    LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, Class<? extends SoftwareArtifact> artifactType);
-}
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
deleted file mode 100644
index dc7dab8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java
+++ /dev/null
@@ -1,37 +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.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-
-/**
- * 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 implements DescriptorParseContext {
-    public boolean artifactExists(ModuleVersionArtifactMetaData artifact) {
-        return false;
-    }
-
-    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, Class<? extends SoftwareArtifact> artifactType) {
-        throw new UnsupportedOperationException();
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.java
deleted file mode 100644
index c311610..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.java
+++ /dev/null
@@ -1,65 +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.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.text.ParseException;
-import java.util.Date;
-import java.util.Map;
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId;
-
-public class DisconnectedIvyXmlModuleDescriptorParser extends IvyXmlModuleDescriptorParser {
-    public DisconnectedIvyXmlModuleDescriptorParser(ResolverStrategy resolverStrategy) {
-        super(resolverStrategy);
-    }
-
-    @Override
-    protected Parser createParser(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, Map<String, String> properties, ResolverStrategy resolverStrategy) throws MalformedURLException {
-        return new DisconnectedParser(parseContext, resource, resource.getLocalResource().getFile().toURI().toURL(), properties, resolverStrategy);
-    }
-
-    private static class DisconnectedParser extends Parser {
-        public DisconnectedParser(DescriptorParseContext parseContext, ExternalResource res, URL descriptorURL, Map<String, String> properties, ResolverStrategy resolverStrategy) {
-            super(parseContext, res, descriptorURL, properties, resolverStrategy);
-        }
-
-        @Override
-        public Parser newParser(ExternalResource res, URL descriptorURL) {
-            Parser parser = new DisconnectedParser(getParseContext(), res, descriptorURL, properties, resolverStrategy);
-            parser.setValidate(isValidate());
-            return parser;
-        }
-
-        @Override
-        protected ModuleDescriptor parseOtherIvyFile(String parentOrganisation,
-                                                     String parentModule, String parentRevision) throws IOException, ParseException, SAXException {
-            ModuleRevisionId parentMrid = createModuleRevisionId(parentOrganisation, parentModule, parentRevision);
-            return new DefaultModuleDescriptor(parentMrid, "release", new Date());
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 6a253ce..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
+++ /dev/null
@@ -1,377 +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.Ivy;
-import org.apache.ivy.core.module.descriptor.*;
-import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
-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.plugins.matcher.ExactPatternMatcher;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader.PomDependencyData;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.util.DeprecationLogger;
-
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
-/**
- * This a straight copy of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder, with minor changes: 1) Do not create artifact for empty classifier. (Previously did so for all non-null
- * classifiers)
- */
-public class GradlePomModuleDescriptorBuilder {
-    public static final Configuration[] MAVEN2_CONFIGURATIONS = new Configuration[]{
-            new Configuration("default", Visibility.PUBLIC,
-                    "runtime dependencies and master artifact can be used with this conf",
-                    new String[]{"runtime", "master"}, true, null),
-            new Configuration("master", Visibility.PUBLIC,
-                    "contains only the artifact published by this module itself, "
-                            + "with no transitive dependencies",
-                    new String[0], true, null),
-            new Configuration("compile", Visibility.PUBLIC,
-                    "this is the default scope, used if none is specified. "
-                            + "Compile dependencies are available in all classpaths.",
-                    new String[0], true, null),
-            new Configuration("provided", Visibility.PUBLIC,
-                    "this is much like compile, but indicates you expect the JDK or a container "
-                            + "to provide it. "
-                            + "It is only available on the compilation classpath, and is not transitive.",
-                    new String[0], true, null),
-            new Configuration("runtime", Visibility.PUBLIC,
-                    "this scope indicates that the dependency is not required for compilation, "
-                            + "but is for execution. It is in the runtime and test classpaths, "
-                            + "but not the compile classpath.",
-                    new String[]{"compile"}, true, null),
-            new Configuration("test", Visibility.PRIVATE,
-                    "this scope indicates that the dependency is not required for normal use of "
-                            + "the application, and is only available for the test compilation and "
-                            + "execution phases.",
-                    new String[]{"runtime"}, true, null),
-            new Configuration("system", Visibility.PUBLIC,
-                    "this scope is similar to provided except that you have to provide the JAR "
-                            + "which contains it explicitly. The artifact is always available and is not "
-                            + "looked up in a repository.",
-                    new String[0], true, null),
-            new Configuration("sources", Visibility.PUBLIC,
-                    "this configuration contains the source artifact of this module, if any.",
-                    new String[0], true, null),
-            new Configuration("javadoc", Visibility.PUBLIC,
-                    "this configuration contains the javadoc artifact of this module, if any.",
-                    new String[0], true, null),
-            new Configuration("optional", Visibility.PUBLIC,
-                    "contains all optional dependencies", new String[0], true, null)
-    };
-
-    static final Map<String, ConfMapper> MAVEN2_CONF_MAPPING = new HashMap<String, ConfMapper>();
-
-    private static final Collection<String> JAR_PACKAGINGS = Arrays.asList("ejb", "bundle", "maven-plugin", "eclipse-plugin");
-    private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("(.+)-\\d{8}\\.\\d{6}-\\d+");
-
-    static interface ConfMapper {
-        public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional);
-    }
-
-    static {
-        MAVEN2_CONF_MAPPING.put("compile", new ConfMapper() {
-            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
-                if (isOptional) {
-                    dd.addDependencyConfiguration("optional", "compile(*)");
-                    //dd.addDependencyConfiguration("optional", "provided(*)");
-                    dd.addDependencyConfiguration("optional", "master(*)");
-
-                } else {
-                    dd.addDependencyConfiguration("compile", "compile(*)");
-                    //dd.addDependencyConfiguration("compile", "provided(*)");
-                    dd.addDependencyConfiguration("compile", "master(*)");
-                    dd.addDependencyConfiguration("runtime", "runtime(*)");
-                }
-            }
-        });
-        MAVEN2_CONF_MAPPING.put("provided", new ConfMapper() {
-            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
-                if (isOptional) {
-                    dd.addDependencyConfiguration("optional", "compile(*)");
-                    dd.addDependencyConfiguration("optional", "provided(*)");
-                    dd.addDependencyConfiguration("optional", "runtime(*)");
-                    dd.addDependencyConfiguration("optional", "master(*)");
-                } else {
-                    dd.addDependencyConfiguration("provided", "compile(*)");
-                    dd.addDependencyConfiguration("provided", "provided(*)");
-                    dd.addDependencyConfiguration("provided", "runtime(*)");
-                    dd.addDependencyConfiguration("provided", "master(*)");
-                }
-            }
-        });
-        MAVEN2_CONF_MAPPING.put("runtime", new ConfMapper() {
-            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
-                if (isOptional) {
-                    dd.addDependencyConfiguration("optional", "compile(*)");
-                    dd.addDependencyConfiguration("optional", "provided(*)");
-                    dd.addDependencyConfiguration("optional", "master(*)");
-
-                } else {
-                    dd.addDependencyConfiguration("runtime", "compile(*)");
-                    dd.addDependencyConfiguration("runtime", "runtime(*)");
-                    dd.addDependencyConfiguration("runtime", "master(*)");
-                }
-            }
-        });
-        MAVEN2_CONF_MAPPING.put("test", new ConfMapper() {
-            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
-                //optional doesn't make sense in the test scope
-                dd.addDependencyConfiguration("test", "runtime(*)");
-                dd.addDependencyConfiguration("test", "master(*)");
-            }
-        });
-        MAVEN2_CONF_MAPPING.put("system", new ConfMapper() {
-            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
-                //optional doesn't make sense in the system scope
-                dd.addDependencyConfiguration("system", "master(*)");
-            }
-        });
-    }
-
-
-    private final DefaultModuleDescriptor ivyModuleDescriptor;
-
-    private ModuleRevisionId mrid;
-
-    private DescriptorParseContext parserSettings;
-    private final PomReader pomReader;
-
-    public GradlePomModuleDescriptorBuilder(ExternalResource res, DescriptorParseContext ivySettings, PomReader pomReader) {
-        ivyModuleDescriptor = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
-        ivyModuleDescriptor.setResolvedPublicationDate(new Date(res.getLastModified()));
-        for (Configuration maven2Configuration : MAVEN2_CONFIGURATIONS) {
-            ivyModuleDescriptor.addConfiguration(maven2Configuration);
-        }
-        ivyModuleDescriptor.setMappingOverride(true);
-        ivyModuleDescriptor.addExtraAttributeNamespace("m", Ivy.getIvyHomeURL() + "maven");
-        parserSettings = ivySettings;
-        this.pomReader = pomReader;
-    }
-
-    public DefaultModuleDescriptor getModuleDescriptor() {
-        return ivyModuleDescriptor;
-    }
-
-    public void setModuleRevId(String group, String module, String version) {
-        String effectiveVersion = version;
-        if (version != null) {
-            Matcher matcher = TIMESTAMP_PATTERN.matcher(version);
-            if (matcher.matches()) {
-                effectiveVersion = matcher.group(1) + "-SNAPSHOT";
-            }
-        }
-
-        this.mrid = ModuleRevisionId.newInstance(group, module, effectiveVersion);
-        ivyModuleDescriptor.setModuleRevisionId(mrid);
-
-        if (effectiveVersion != null && effectiveVersion.endsWith("SNAPSHOT")) {
-            ivyModuleDescriptor.setStatus("integration");
-        } else {
-            ivyModuleDescriptor.setStatus("release");
-        }
-    }
-
-    public void setHomePage(String homePage) {
-        ivyModuleDescriptor.setHomePage(homePage);
-    }
-
-    public void setDescription(String description) {
-        ivyModuleDescriptor.setDescription(description);
-    }
-
-    public void setLicenses(Iterable<License> licenses) {
-        for (License license : licenses) {
-            ivyModuleDescriptor.addLicense(license);
-        }
-    }
-
-    public void addMainArtifact(String artifactId, String packaging) {
-        if ("pom".equals(packaging)) {
-            return;
-        }
-
-        if (!isKnownJarPackaging(packaging)) {
-            ModuleComponentIdentifier componentIdentifier = DefaultModuleComponentIdentifier.newId(mrid.getOrganisation(), mrid.getName(), mrid.getRevision());
-            DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, packaging, packaging);
-            ModuleVersionArtifactMetaData artifactMetaData = new DefaultModuleVersionArtifactMetaData(componentIdentifier, artifact);
-            if (parserSettings.artifactExists(artifactMetaData)) {
-                ivyModuleDescriptor.addArtifact("master", artifact);
-
-                DeprecationLogger.nagUserOfDeprecated("Relying on packaging to define the extension of the main artifact");
-                return;
-            }
-        }
-
-        ivyModuleDescriptor.addArtifact("master", new DefaultArtifact(mrid, new Date(), artifactId, packaging, "jar"));
-    }
-
-    private boolean isKnownJarPackaging(String packaging) {
-        return "jar".equals(packaging) || JAR_PACKAGINGS.contains(packaging);
-    }
-
-    public void addDependency(PomDependencyData dep) {
-        String scope = dep.getScope();
-        if ((scope != null) && (scope.length() > 0) && !MAVEN2_CONF_MAPPING.containsKey(scope)) {
-            // unknown scope, defaulting to 'compile'
-            scope = "compile";
-        }
-
-        String version = determineVersion(dep);
-        ModuleRevisionId moduleRevId = IvyUtil.createModuleRevisionId(dep.getGroupId(), dep.getArtifactId(), version);
-
-        // Some POMs depend on themselves, don't add this dependency: Ivy doesn't allow this!
-        // Example: http://repo2.maven.org/maven2/net/jini/jsk-platform/2.1/jsk-platform-2.1.pom
-        ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
-        if ((mRevId != null) && mRevId.getModuleId().equals(moduleRevId.getModuleId())) {
-            return;
-        }
-
-        DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(ivyModuleDescriptor, moduleRevId, true, false, true);
-        scope = (scope == null || scope.length() == 0) ? getDefaultScope(dep) : scope;
-        ConfMapper mapping = MAVEN2_CONF_MAPPING.get(scope);
-        mapping.addMappingConfs(dd, dep.isOptional());
-        Map<String, String> extraAtt = new HashMap<String, String>();
-        boolean hasClassifier = dep.getClassifier() != null && dep.getClassifier().length() > 0;
-        boolean hasNonJarType = dep.getType() != null && !"jar".equals(dep.getType());
-        if (hasClassifier || hasNonJarType) {
-            String type = "jar";
-            if (dep.getType() != null) {
-                type = dep.getType();
-            }
-            String ext = type;
-
-            // if type is 'test-jar', the extension is 'jar' and the classifier is 'tests'
-            // Cfr. http://maven.apache.org/guides/mini/guide-attached-tests.html
-            if ("test-jar".equals(type)) {
-                ext = "jar";
-                extraAtt.put("m:classifier", "tests");
-            } else if (JAR_PACKAGINGS.contains(type)) {
-                ext = "jar";
-            }
-
-            // we deal with classifiers by setting an extra attribute and forcing the
-            // dependency to assume such an artifact is published
-            if (dep.getClassifier() != null) {
-                extraAtt.put("m:classifier", dep.getClassifier());
-            }
-            DefaultDependencyArtifactDescriptor depArtifact = new DefaultDependencyArtifactDescriptor(dd, dd.getDependencyId().getName(), type, ext, null, extraAtt);
-            // here we have to assume a type and ext for the artifact, so this is a limitation
-            // compared to how m2 behave with classifiers
-            String optionalizedScope = dep.isOptional() ? "optional" : scope;
-            dd.addDependencyArtifact(optionalizedScope, depArtifact);
-        }
-
-        // experimentation shows the following, excluded modules are
-        // inherited from parent POMs if either of the following is true:
-        // the <exclusions> element is missing or the <exclusions> element
-        // is present, but empty.
-        List /*<ModuleId>*/ excluded = dep.getExcludedModules();
-        if (excluded.isEmpty()) {
-            excluded = getDependencyMgtExclusions(dep);
-        }
-        for (Object anExcluded : excluded) {
-            ModuleId excludedModule = (ModuleId) anExcluded;
-            String[] confs = dd.getModuleConfigurations();
-            for (String conf : confs) {
-                dd.addExcludeRule(conf, new DefaultExcludeRule(new ArtifactId(
-                        excludedModule, PatternMatcher.ANY_EXPRESSION,
-                        PatternMatcher.ANY_EXPRESSION,
-                        PatternMatcher.ANY_EXPRESSION),
-                        ExactPatternMatcher.INSTANCE, null));
-            }
-        }
-
-        ivyModuleDescriptor.addDependency(dd);
-    }
-
-    /**
-     * Determines the version of a dependency. Uses the specified version if declared for the as coordinate. If the version is not declared, try to resolve it from the dependency management section.
-     * In case the version cannot be resolved with any of these methods, throw an exception of type {@see org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.UnresolvedDependencyVersionException}.
-     *
-     * @param dependency Dependency
-     * @return Resolved dependency version
-     */
-    private String determineVersion(PomDependencyData dependency) {
-        String version = dependency.getVersion();
-        version = (version == null || version.length() == 0) ? getDefaultVersion(dependency) : version;
-
-        if (version == null) {
-            throw new UnresolvedDependencyVersionException(dependency.getId());
-        }
-
-        return version;
-    }
-
-    public void addDependency(DependencyDescriptor descriptor) {
-        // Some POMs depend on themselves 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();
-        ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
-        if ((mRevId != null) && mRevId.getModuleId().equals(dependencyId)) {
-            return;
-        }
-
-        ivyModuleDescriptor.addDependency(descriptor);
-    }
-
-    private String getDefaultVersion(PomDependencyData dep) {
-        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
-        if (pomDependencyMgt != null) {
-            return pomDependencyMgt.getVersion();
-        }
-        return null;
-    }
-
-    private String getDefaultScope(PomDependencyData dep) {
-        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
-        String result = null;
-        if (pomDependencyMgt != null) {
-            result = pomDependencyMgt.getScope();
-        }
-        if ((result == null) || !MAVEN2_CONF_MAPPING.containsKey(result)) {
-            result = "compile";
-        }
-        return result;
-    }
-
-    private List<ModuleId> getDependencyMgtExclusions(PomDependencyData dep) {
-        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
-        if (pomDependencyMgt != null) {
-            return pomDependencyMgt.getExcludedModules();
-        }
-
-        return Collections.emptyList();
-    }
-
-    private PomDependencyMgt findDependencyDefault(PomDependencyData dependency) {
-        return pomReader.findDependencyDefaults(dependency.getId());
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index a1d0b1d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
+++ /dev/null
@@ -1,216 +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.module.descriptor.Configuration;
-import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-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.parser.PomReader.PomDependencyData;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.resolution.MavenPomArtifact;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-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 extends AbstractModuleDescriptorParser {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GradlePomModuleDescriptorParser.class);
-    private static final String DEPENDENCY_IMPORT_SCOPE = "import";
-
-    @Override
-    protected String getTypeName() {
-        return "POM";
-    }
-
-    public String toString() {
-        return "gradle pom parser";
-    }
-
-    protected MutableModuleVersionMetaData doParseDescriptor(DescriptorParseContext parserSettings, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException, SAXException {
-        PomReader pomReader = new PomReader(resource);
-        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(resource, parserSettings, pomReader);
-
-        doParsePom(parserSettings, mdBuilder, pomReader);
-
-        String artifactId = pomReader.getArtifactId();
-        if (pomReader.getRelocation() == null) {
-            mdBuilder.addMainArtifact(artifactId, pomReader.getPackaging());
-        }
-
-        DefaultModuleDescriptor moduleDescriptor = mdBuilder.getModuleDescriptor();
-        ModuleDescriptorAdapter adapter = new ModuleDescriptorAdapter(moduleDescriptor);
-        if ("pom".equals(pomReader.getPackaging())) {
-            adapter.setMetaDataOnly(true);
-        }
-        return adapter;
-    }
-
-    private void doParsePom(DescriptorParseContext parserSettings, GradlePomModuleDescriptorBuilder mdBuilder, PomReader pomReader) throws IOException, SAXException {
-        if (pomReader.hasParent()) {
-            //Is there any other parent properties?
-
-            ModuleVersionIdentifier parentId = DefaultModuleVersionIdentifier.newId(
-                    pomReader.getParentGroupId(),
-                    pomReader.getParentArtifactId(),
-                    pomReader.getParentVersion());
-            PomReader parentPomReader = parseOtherPom(parserSettings, parentId);
-            pomReader.setPomParent(parentPomReader);
-        }
-        pomReader.resolveGAV();
-
-        String groupId = pomReader.getGroupId();
-        String artifactId = pomReader.getArtifactId();
-        String version = pomReader.getVersion();
-        mdBuilder.setModuleRevId(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.");
-                PomReader relocatedModule = parseOtherPom(parserSettings, DefaultModuleVersionIdentifier.newId(relocation));
-
-                Collection<PomDependencyData> pomDependencyDataList = relocatedModule.getDependencies().values();
-                for(PomDependencyData pomDependencyData : pomDependencyDataList) {
-                    mdBuilder.addDependency(pomDependencyData);
-                }
-
-            } else {
-                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());
-                    }
-                }
-                mdBuilder.addDependency(dd);
-            }
-        } else {
-            overrideDependencyMgtsWithImported(parserSettings, pomReader);
-
-            for (PomDependencyData dependency : pomReader.getDependencies().values()) {
-                mdBuilder.addDependency(dependency);
-            }
-        }
-    }
-
-    /**
-     * Overrides existing dependency management information with imported ones if existing.
-     *
-     * @param parseContext Parse context
-     * @param pomReader POM reader
-     * @throws IOException
-     * @throws SAXException
-     */
-    private void overrideDependencyMgtsWithImported(DescriptorParseContext parseContext, PomReader pomReader) throws IOException, SAXException {
-        Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = parseImportedDependencyMgts(parseContext, pomReader.parseDependencyMgt());
-        pomReader.addImportedDependencyMgts(importedDependencyMgts);
-    }
-
-    /**
-     * Parses imported dependency management information.
-     *
-     * @param parseContext Parse context
-     * @param currentDependencyMgts Current dependency management information
-     * @return Imported dependency management information
-     * @throws IOException
-     * @throws SAXException
-     */
-    private Map<MavenDependencyKey, PomDependencyMgt> parseImportedDependencyMgts(DescriptorParseContext parseContext, Collection<PomDependencyMgt> currentDependencyMgts) throws IOException, SAXException {
-        Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
-
-        for(PomDependencyMgt currentDependencyMgt : currentDependencyMgts) {
-            if(isDependencyImportScoped(currentDependencyMgt)) {
-                PomReader importDescr = parseImportedPom(parseContext, currentDependencyMgt);
-                importedDependencyMgts.putAll(importDescr.getDependencyMgt());
-            }
-        }
-
-        return importedDependencyMgts;
-    }
-
-    /**
-     * Checks if dependency has scope "import".
-     *
-     * @param dependencyMgt Dependency management element
-     * @return Flag
-     */
-    private boolean isDependencyImportScoped(PomDependencyMgt dependencyMgt) {
-        return DEPENDENCY_IMPORT_SCOPE.equals(dependencyMgt.getScope());
-    }
-
-    /**
-     * Parses imported POM.
-     *
-     * @param parseContext Parse context
-     * @param pomDependencyMgt Dependency management information
-     * @return POM reader
-     * @throws IOException
-     * @throws SAXException
-     */
-    private PomReader parseImportedPom(DescriptorParseContext parseContext, PomDependencyMgt pomDependencyMgt) throws IOException, SAXException {
-        ModuleVersionIdentifier importedId = DefaultModuleVersionIdentifier.newId(pomDependencyMgt.getGroupId(), pomDependencyMgt.getArtifactId(), pomDependencyMgt.getVersion());
-        return parseOtherPom(parseContext, importedId);
-    }
-
-    /**
-     * Parses other POM.
-     *
-     * @param parseContext Parse context
-     * @param parentId Parent module revision ID
-     * @return POM reader
-     * @throws IOException
-     * @throws SAXException
-     */
-    private PomReader parseOtherPom(DescriptorParseContext parseContext, ModuleVersionIdentifier parentId) throws IOException, SAXException {
-        LocallyAvailableExternalResource localResource = parseContext.getMetaDataArtifact(parentId, MavenPomArtifact.class);
-        PomReader pomReader = new PomReader(localResource);
-        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(localResource, parseContext, pomReader);
-        doParsePom(parseContext, mdBuilder, pomReader);
-        return pomReader;
-    }
-}
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
deleted file mode 100644
index e44ee58..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
+++ /dev/null
@@ -1,1226 +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.parser;
-
-import com.google.common.base.Joiner;
-import org.apache.ivy.core.IvyPatternHelper;
-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.plugins.matcher.PatternMatcher;
-import org.apache.ivy.plugins.namespace.Namespace;
-import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
-import org.apache.ivy.util.XMLHelper;
-import org.apache.ivy.util.extendable.DefaultExtendableItem;
-import org.gradle.api.Action;
-import org.gradle.api.Transformer;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.resolution.IvyDescriptorArtifact;
-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.CollectionUtils;
-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.*;
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId;
-
-/**
- * Copied from org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser into Gradle codebase.
- */
-public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser {
-    static final String[] DEPENDENCY_REGULAR_ATTRIBUTES =
-            new String[] {"org", "name", "branch", "branchConstraint", "rev", "revConstraint", "force", "transitive", "changing", "conf"};
-
-    public static final String IVY_DATE_FORMAT_PATTERN = "yyyyMMddHHmmss";
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(IvyXmlModuleDescriptorParser.class);
-    private final ResolverStrategy resolverStrategy;
-
-    public IvyXmlModuleDescriptorParser(ResolverStrategy resolverStrategy) {
-        this.resolverStrategy = resolverStrategy;
-    }
-
-    protected MutableModuleVersionMetaData doParseDescriptor(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException {
-        Parser parser = createParser(parseContext, resource, populateProperties(), resolverStrategy);
-        return doParseDescriptorWithProvidedParser(parser, validate);
-    }
-
-    protected Parser createParser(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, Map<String, String> properties, ResolverStrategy resolverStrategy) throws MalformedURLException {
-        return new Parser(parseContext, resource, resource.getLocalResource().getFile().toURI().toURL(), properties, resolverStrategy);
-    }
-
-    private MutableModuleVersionMetaData doParseDescriptorWithProvidedParser(Parser parser, boolean validate) throws IOException, ParseException {
-        parser.setValidate(validate);
-        parser.parse();
-        DefaultModuleDescriptor moduleDescriptor = parser.getModuleDescriptor();
-        postProcess(moduleDescriptor);
-        return new ModuleDescriptorAdapter(moduleDescriptor);
-    }
-
-    protected void postProcess(DefaultModuleDescriptor moduleDescriptor) {
-    }
-
-    @Override
-    protected String getTypeName() {
-        return "Ivy file";
-    }
-
-    private Map<String, String> populateProperties() {
-        HashMap<String, String> properties = new HashMap<String, String>();
-        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));
-        }
-        return properties;
-    }
-
-    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;
-
-        protected AbstractParser(ExternalResource resource) {
-            this.res = resource; // used for log and date only
-            md = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
-            md.setLastModified(res.getLastModified());
-        }
-
-        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(createModuleRevisionId("", "", ""), 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 {
-        public static final class State {
-            public static final int NONE = 0;
-
-            public static final int INFO = 1;
-
-            public static final int CONF = 2;
-
-            public static final int PUB = 3;
-
-            public static final int DEP = 4;
-
-            public static final int DEP_ARTIFACT = 5;
-
-            public static final int ARTIFACT_INCLUDE = 6;
-
-            public static final int ARTIFACT_EXCLUDE = 7;
-
-            public static final int CONFLICT = 8;
-
-            public static final int EXCLUDE = 9;
-
-            public static final int DEPS = 10;
-
-            public static final int DESCRIPTION = 11;
-
-            public static final int EXTRA_INFO = 12;
-
-            private State() {
-            }
-        }
-
-        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 final DescriptorParseContext parseContext;
-        private final RelativeUrlResolver relativeUrlResolver = new NormalRelativeUrlResolver();
-        private final URL descriptorURL;
-        private boolean validate = true;
-
-        /* Parsing state */
-        private int state = State.NONE;
-        private PatternMatcher defaultMatcher;
-        private DefaultDependencyDescriptor dd;
-        private ConfigurationAware confAware;
-        private MDArtifact artifact;
-        private String conf;
-        private boolean artifactsDeclared;
-        private StringBuffer buffer;
-        private String descriptorVersion;
-        private String[] publicationsDefaultConf;
-        final Map<String, String> properties;
-        final ResolverStrategy resolverStrategy;
-
-        public Parser(DescriptorParseContext parseContext, ExternalResource res, URL descriptorURL, Map<String, String> properties, ResolverStrategy resolverStrategy) {
-            super(res);
-            this.parseContext = parseContext;
-            this.descriptorURL = descriptorURL;
-            this.properties = properties;
-            this.resolverStrategy = resolverStrategy;
-        }
-
-        public Parser newParser(ExternalResource res, URL descriptorURL) {
-            Parser parser = new Parser(parseContext, res, descriptorURL, properties, resolverStrategy);
-            parser.setValidate(validate);
-            return parser;
-        }
-
-        public void setValidate(boolean validate) {
-            this.validate = validate;
-        }
-
-        public boolean isValidate() {
-            return validate;
-        }
-
-        public DescriptorParseContext getParseContext() {
-            return parseContext;
-        }
-
-        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);
-                    }
-                }
-            });
-            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)
-                throws SAXException {
-            try {
-                if (state == State.DESCRIPTION) {
-                    // make sure we don't interpret any tag while in description tag
-                    descriptionStarted(qName, attributes);
-                } else if ("ivy-module".equals(qName)) {
-                    ivyModuleStarted(attributes);
-                } else if ("info".equals(qName)) {
-                    infoStarted(attributes);
-                } else if (state == State.INFO && "extends".equals(qName)) {
-                    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 && "ivyauthor".equals(qName)) {
-                    // nothing to do, we don't store this
-                    return;
-                } else if (state == State.INFO && "repository".equals(qName)) {
-                    // nothing to do, we don't store this
-                    return;
-                } else if (state == State.INFO && "description".equals(qName)) {
-                    getMd().setHomePage(substitute(attributes.getValue("homepage")));
-                    state = State.DESCRIPTION;
-                    buffer = new StringBuffer();
-                } else if (state == State.INFO && isOtherNamespace(qName)) {
-                    buffer = new StringBuffer();
-                    state = State.EXTRA_INFO;
-                } else if ("configurations".equals(qName)) {
-                    configurationStarted(attributes);
-                } else if ("publications".equals(qName)) {
-                    publicationsStarted(attributes);
-                } else if ("dependencies".equals(qName)) {
-                    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: " + getResource().getName());
-                    }
-                    state = State.CONFLICT;
-                    checkConfigurations();
-                } else if ("artifact".equals(qName)) {
-                    artifactStarted(qName, attributes);
-                } else if ("include".equals(qName) && state == State.DEP) {
-                    addIncludeRule(qName, attributes);
-                } else if ("exclude".equals(qName) && state == State.DEP) {
-                    addExcludeRule(qName, attributes);
-                } else if ("exclude".equals(qName) && state == State.DEPS) {
-                    state = State.EXCLUDE;
-                    parseRule(qName, attributes);
-                    getMd().addExcludeRule((ExcludeRule) confAware);
-                } else if ("dependency".equals(qName)) {
-                    dependencyStarted(attributes);
-                } else if ("conf".equals(qName)) {
-                    confStarted(attributes);
-                } else if ("mapped".equals(qName)) {
-                    dd.addDependencyConfiguration(conf, substitute(attributes.getValue("name")));
-                } else if (("conflict".equals(qName) && state == State.DEPS) || "manager".equals(qName) && state == State.CONFLICT) {
-                    LOGGER.debug("Ivy.xml conflict managers are not supported by Gradle. Ignoring conflict manager declared in {}", getResource().getName());
-                } else if ("override".equals(qName) && state == State.DEPS) {
-                    LOGGER.debug("Ivy.xml dependency overrides are not supported by Gradle. Ignoring override declared in {}", getResource().getName());
-                } else if ("include".equals(qName) && state == State.CONF) {
-                    includeConfStarted(attributes);
-                } else if (validate && state != State.EXTRA_INFO && state != State.DESCRIPTION) {
-                    addError("unknown tag " + qName);
-                }
-            } catch (Exception ex) {
-                if (ex instanceof SAXException) {
-                    throw (SAXException) ex;
-                }
-                SAXException sax = new SAXException("Problem occurred while parsing ivy file: "
-                        + ex.getMessage(), ex);
-                sax.initCause(ex);
-                throw sax;
-            }
-        }
-
-        private void extendsStarted(Attributes attributes) throws ParseException {
-            String parentOrganisation = attributes.getValue("organisation");
-            String parentModule = attributes.getValue("module");
-            String parentRevision = attributes.getValue("revision");
-            String location = elvis(attributes.getValue("location"), "../ivy.xml");
-
-            String extendType = elvis(attributes.getValue("extendType"), "all").toLowerCase();
-            List<String> extendTypes = Arrays.asList(extendType.split(","));
-
-            ModuleDescriptor parent = null;
-            try {
-                LOGGER.debug("Trying to parse included ivy file :" + location);
-                parent = parseOtherIvyFileOnFileSystem(location);
-
-                //verify that the parsed descriptor is the correct parent module.
-                ModuleId expected = IvyUtil.createModuleId(parentOrganisation, parentModule);
-                ModuleId pid = parent.getModuleRevisionId().getModuleId();
-                if (!expected.equals(pid)) {
-                    LOGGER.warn("Ignoring parent Ivy file " + location + "; expected " + expected + " but found " + pid);
-                    parent = null;
-                }
-
-            } catch (ParseException e) {
-                LOGGER.debug("Unable to parse included ivy file " + location + ": " + e.getMessage());
-            } catch (IOException e) {
-                LOGGER.debug("Unable to parse included ivy file " + location + ": " + e.getMessage());
-            }
-
-            // if the included ivy file is not found on file system, tries to resolve using
-            // repositories
-            if (parent == null) {
-                try {
-                    LOGGER.debug("Trying to parse included ivy file by asking repository for module :"
-                                            + parentOrganisation
-                                            + "#"
-                                            + parentModule
-                                            + ";"
-                                            + parentRevision);
-                    parent = parseOtherIvyFile(parentOrganisation, parentModule, parentRevision);
-                } catch(IOException e) {
-                    LOGGER.warn("Unable to access included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision);
-                } catch (ParseException e) {
-                    LOGGER.warn("Unable to parse included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision);
-                } catch(SAXException e) {
-                    LOGGER.warn("Unable to parse included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision);
-                }
-            }
-
-            if (parent == null) {
-                throw new ParseException("Unable to parse included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision, 0);
-            }
-
-            mergeWithOtherModuleDescriptor(extendTypes, parent);
-        }
-
-        private void mergeWithOtherModuleDescriptor(List<String> extendTypes, ModuleDescriptor parent) {
-
-            if (extendTypes.contains("all")) {
-                mergeAll(parent);
-            } else {
-                if (extendTypes.contains("info")) {
-                    mergeInfo(parent);
-                }
-
-                if (extendTypes.contains("configurations")) {
-                    mergeConfigurations(parent.getModuleRevisionId(), parent.getConfigurations());
-                }
-
-                if (extendTypes.contains("dependencies")) {
-                    mergeDependencies(parent.getDependencies());
-                }
-
-                if (extendTypes.contains("description")) {
-                    mergeDescription(parent.getDescription());
-                }
-            }
-        }
-
-        private void mergeAll(ModuleDescriptor parent) {
-            ModuleRevisionId sourceMrid = parent.getModuleRevisionId();
-            mergeInfo(parent);
-            mergeConfigurations(sourceMrid, parent.getConfigurations());
-            mergeDependencies(parent.getDependencies());
-            mergeDescription(parent.getDescription());
-        }
-
-        private void mergeInfo(ModuleDescriptor parent) {
-            ModuleRevisionId parentMrid = parent.getModuleRevisionId();
-
-            DefaultModuleDescriptor descriptor = getMd();
-            ModuleRevisionId currentMrid = descriptor.getModuleRevisionId();
-
-            ModuleRevisionId mergedMrid = createModuleRevisionId(
-                    mergeValue(parentMrid.getOrganisation(), currentMrid.getOrganisation()),
-                    currentMrid.getName(),
-                    mergeValue(parentMrid.getBranch(), currentMrid.getBranch()),
-                    mergeValue(parentMrid.getRevision(), currentMrid.getRevision()),
-                    mergeValues(parentMrid.getQualifiedExtraAttributes(), currentMrid.getQualifiedExtraAttributes())
-            );
-
-            descriptor.setModuleRevisionId(mergedMrid);
-            descriptor.setResolvedModuleRevisionId(mergedMrid);
-
-            descriptor.setStatus(mergeValue(parent.getStatus(), descriptor.getStatus()));
-            if (descriptor.getNamespace() == null && parent instanceof DefaultModuleDescriptor) {
-                Namespace parentNamespace = ((DefaultModuleDescriptor) parent).getNamespace();
-                descriptor.setNamespace(parentNamespace);
-            }
-        }
-
-        private static String mergeValue(String inherited, String override) {
-            return override == null ? inherited : override;
-        }
-
-        private static Map mergeValues(Map inherited, Map overrides) {
-            LinkedHashMap dup = new LinkedHashMap(inherited.size() + overrides.size());
-            dup.putAll(inherited);
-            dup.putAll(overrides);
-            return dup;
-        }
-
-        private void mergeConfigurations(ModuleRevisionId sourceMrid, Configuration[] configurations) {
-            DefaultModuleDescriptor md = getMd();
-            for (Configuration configuration : configurations) {
-                LOGGER.debug("Merging configuration with: " + configuration.getName());
-                //copy configuration from parent descriptor
-                md.addConfiguration(new Configuration(configuration, sourceMrid));
-            }
-        }
-
-        private void mergeDependencies(DependencyDescriptor[] dependencies) {
-            DefaultModuleDescriptor md = getMd();
-            for (DependencyDescriptor dependencyDescriptor : dependencies) {
-                LOGGER.debug("Merging dependency with: " + dependencyDescriptor.getDependencyRevisionId().toString());
-                md.addDependency(dependencyDescriptor);
-            }
-        }
-
-        private void mergeDescription(String description) {
-            String current = getMd().getDescription();
-            if (current == null || current.trim().length() == 0) {
-                getMd().setDescription(description);
-            }
-        }
-
-        private ModuleDescriptor parseOtherIvyFileOnFileSystem(String location)
-                throws ParseException, IOException {
-            URL url = relativeUrlResolver.getURL(descriptorURL, location);
-            LOGGER.debug("Trying to load included ivy file from " + url.toString());
-            return parseModuleDescriptor(new UrlExternalResource(url), url);
-        }
-
-        protected ModuleDescriptor parseOtherIvyFile(String parentOrganisation, String parentModule, String parentRevision) throws IOException, ParseException, SAXException {
-            ModuleVersionIdentifier importedId = new DefaultModuleVersionIdentifier(parentOrganisation, parentModule, parentRevision);
-            LocallyAvailableExternalResource externalResource = parseContext.getMetaDataArtifact(importedId, IvyDescriptorArtifact.class);
-
-            return parseModuleDescriptor(externalResource, externalResource.getLocalResource().getFile().toURI().toURL());
-        }
-
-        private ModuleDescriptor parseModuleDescriptor(ExternalResource externalResource, URL descriptorURL) throws IOException, ParseException {
-            Parser parser = newParser(externalResource, descriptorURL);
-            parser.parse();
-            return parser.getModuleDescriptor();
-        }
-
-        private void publicationsStarted(Attributes attributes) {
-            state = State.PUB;
-            artifactsDeclared = true;
-            checkConfigurations();
-            String defaultConf = substitute(attributes.getValue("defaultconf"));
-            if (defaultConf != null) {
-                this.publicationsDefaultConf = defaultConf.split(",");
-            }
-        }
-
-        private boolean isOtherNamespace(String qName) {
-            return qName.indexOf(':') != -1;
-        }
-
-        private void includeConfStarted(Attributes attributes)
-                throws SAXException, IOException, ParserConfigurationException, ParseException {
-            URL url = relativeUrlResolver.getURL(descriptorURL, substitute(attributes.getValue("file")), substitute(attributes.getValue("url")));
-            if (url == null) {
-                throw new SAXException("include tag must have a file or an url attribute");
-            }
-
-            // create a new temporary parser to read the configurations from
-            // the specified file.
-            Parser parser = newParser(new UrlExternalResource(url), url);
-            XMLHelper.parse(url , null, parser);
-
-            // add the configurations from this temporary parser to this module descriptor
-            Configuration[] configs = parser.getModuleDescriptor().getConfigurations();
-            for (Configuration config : configs) {
-                getMd().addConfiguration(config);
-            }
-            if (parser.getDefaultConfMapping() != null) {
-                LOGGER.debug("setting default conf mapping from imported configurations file: " + parser.getDefaultConfMapping());
-                setDefaultConfMapping(parser.getDefaultConfMapping());
-            }
-            if (parser.getDefaultConf() != null) {
-                LOGGER.debug("setting default conf from imported configurations file: " + parser.getDefaultConf());
-                setDefaultConf(parser.getDefaultConf());
-            }
-            if (parser.getMd().isMappingOverride()) {
-                LOGGER.debug("enabling mapping-override from imported configurations file");
-                getMd().setMappingOverride(true);
-            }
-        }
-
-        private void confStarted(Attributes attributes) {
-            String conf = substitute(attributes.getValue("name"));
-            switch (state) {
-                case State.CONF:
-                    Configuration.Visibility visibility = Configuration.Visibility.getVisibility(elvis(substitute(attributes.getValue("visibility")), "public"));
-                    String description = substitute(attributes.getValue("description"));
-                    String[] extend = substitute(attributes.getValue("extends")) == null ? null : substitute(attributes.getValue("extends")).split(",");
-                    String transitiveValue = attributes.getValue("transitive");
-                    boolean transitive = (transitiveValue == null) || Boolean.valueOf(attributes.getValue("transitive"));
-                    String deprecated = attributes.getValue("deprecated");
-                    Configuration configuration = new Configuration(conf, visibility, description, extend, transitive, deprecated);
-                    fillExtraAttributes(configuration, attributes,
-                            new String[]{"name", "visibility", "extends", "transitive", "description", "deprecated"});
-                    getMd().addConfiguration(configuration);
-                    break;
-                case State.PUB:
-                    if ("*".equals(conf)) {
-                        String[] confs = getMd().getConfigurationsNames();
-                        for (String confName : confs) {
-                            artifact.addConfiguration(confName);
-                            getMd().addArtifact(confName, artifact);
-                        }
-                    } else {
-                        artifact.addConfiguration(conf);
-                        getMd().addArtifact(conf, artifact);
-                    }
-                    break;
-                case State.DEP:
-                    this.conf = conf;
-                    String mappeds = substitute(attributes.getValue("mapped"));
-                    if (mappeds != null) {
-                        String[] mapped = mappeds.split(",");
-                        for (String depConf : mapped) {
-                            dd.addDependencyConfiguration(conf, depConf.trim());
-                        }
-                    }
-                    break;
-                case State.DEP_ARTIFACT:
-                case State.ARTIFACT_INCLUDE:
-                case State.ARTIFACT_EXCLUDE:
-                    addConfiguration(conf);
-                    break;
-                default:
-                    if (validate) {
-                        addError("conf tag found in invalid tag: " + state);
-                    }
-                    break;
-            }
-        }
-
-        private void dependencyStarted(Attributes attributes) {
-            state = State.DEP;
-            String org = substitute(attributes.getValue("org"));
-            if (org == null) {
-                org = getMd().getModuleRevisionId().getOrganisation();
-            }
-            boolean force = Boolean.valueOf(substitute(attributes.getValue("force")));
-            boolean changing = Boolean.valueOf(substitute(attributes.getValue("changing")));
-
-            String transitiveValue = substitute(attributes.getValue("transitive"));
-            boolean transitive = (transitiveValue == null) ? true : Boolean.valueOf(transitiveValue);
-
-            String name = substitute(attributes.getValue("name"));
-            String branch = substitute(attributes.getValue("branch"));
-            String branchConstraint = substitute(attributes.getValue("branchConstraint"));
-            String rev = substitute(attributes.getValue("rev"));
-            String revConstraint = substitute(attributes.getValue("revConstraint"));
-
-            String[] ignoredAttributeNames = DEPENDENCY_REGULAR_ATTRIBUTES;
-            Map extraAttributes = getExtraAttributes(attributes, ignoredAttributeNames);
-
-            ModuleRevisionId revId = createModuleRevisionId(org, name, branch, rev, extraAttributes);
-            ModuleRevisionId dynamicId;
-            if ((revConstraint == null) && (branchConstraint == null)) {
-                // no dynamic constraints defined, so dynamicId equals revId
-                dynamicId = createModuleRevisionId(org, name, branch, rev, extraAttributes, false);
-            } else {
-                if (branchConstraint == null) {
-                    // this situation occurs when there was no branch defined
-                    // in the original dependency descriptor. So the dynamicId
-                    // shouldn't contain a branch neither
-                    dynamicId = createModuleRevisionId(org, name, null, revConstraint, extraAttributes, false);
-                } else {
-                    dynamicId = createModuleRevisionId(org, name, branchConstraint, revConstraint, extraAttributes);
-                }
-            }
-
-            dd = new DefaultDependencyDescriptor(getMd(), revId, dynamicId, force, changing, transitive);
-            getMd().addDependency(dd);
-            String confs = substitute(attributes.getValue("conf"));
-            if (confs != null && confs.length() > 0) {
-                parseDepsConfs(confs, dd);
-            }
-        }
-
-        private void artifactStarted(String qName, Attributes attributes)
-                throws MalformedURLException {
-            if (state == State.PUB) {
-                // this is a published artifact
-                String artName = elvis(substitute(attributes.getValue("name")), getMd().getModuleRevisionId().getName());
-                String type = elvis(substitute(attributes.getValue("type")), "jar");
-                String ext = elvis(substitute(attributes.getValue("ext")), type);
-                String url = substitute(attributes.getValue("url"));
-                Map extraAttributes = getExtraAttributes(attributes, new String[]{"ext", "type", "name", "conf"});
-                artifact = new MDArtifact(getMd(), artName, type, ext, url == null ? null : new URL(url), extraAttributes);
-                String confs = substitute(attributes.getValue("conf"));
-                
-                // Only add confs if they are specified. if they aren't, endElement will handle this only if there are no conf defined in sub elements
-                if (confs != null && confs.length() > 0) {
-                    String[] conf;
-                    if ("*".equals(confs)) {
-                        conf = getMd().getConfigurationsNames();
-                    } else {
-                        conf = confs.split(",");
-                    }
-                    for (String confName : conf) {
-                        artifact.addConfiguration(confName.trim());
-                        getMd().addArtifact(confName.trim(), artifact);
-                    }
-                }
-            } else if (state == State.DEP) {
-                // this is an artifact asked for a particular dependency
-                addDependencyArtifacts(qName, attributes);
-            } else if (validate) {
-                addError("artifact tag found in invalid tag: " + state);
-            }
-        }
-
-        private void dependenciesStarted(Attributes attributes) {
-            state = State.DEPS;
-            String defaultConf = substitute(attributes.getValue("defaultconf"));
-            if (defaultConf != null) {
-                setDefaultConf(defaultConf);
-            }
-            String defaultConfMapping = substitute(attributes.getValue("defaultconfmapping"));
-            if (defaultConfMapping != null) {
-                setDefaultConfMapping(defaultConfMapping);
-            }
-            String confMappingOverride = substitute(attributes.getValue("confmappingoverride"));
-            if (confMappingOverride != null) {
-                getMd().setMappingOverride(Boolean.valueOf(confMappingOverride));
-            }
-            checkConfigurations();
-        }
-
-        private void configurationStarted(Attributes attributes) {
-            state = State.CONF;
-            setDefaultConfMapping(substitute(attributes.getValue("defaultconfmapping")));
-            setDefaultConf(substitute(attributes.getValue("defaultconf")));
-            getMd().setMappingOverride(Boolean.valueOf(substitute(attributes.getValue("confmappingoverride"))));
-        }
-
-        private void infoStarted(Attributes attributes) {
-            state = State.INFO;
-            String org = substitute(attributes.getValue("organisation"));
-            String module = substitute(attributes.getValue("module"));
-            String revision = substitute(attributes.getValue("revision"));
-            String branch = substitute(attributes.getValue("branch"));
-            Map extraAttributes = getExtraAttributes(attributes, new String[]{"organisation", "module", "revision", "status", "publication", "branch", "namespace", "default", "resolver"});
-            getMd().setModuleRevisionId(createModuleRevisionId(org, module, branch, revision, extraAttributes));
-
-            getMd().setStatus(elvis(substitute(attributes.getValue("status")), "integration"));
-            getMd().setDefault(Boolean.valueOf(substitute(attributes.getValue("default"))));
-            String pubDate = substitute(attributes.getValue("publication"));
-            if (pubDate != null && pubDate.length() > 0) {
-                try {
-                    final SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_FORMAT_PATTERN);
-                    getMd().setPublicationDate(ivyDateFormat.parse(pubDate));
-                } catch (ParseException e) {
-                    addError("invalid publication date format: " + pubDate);
-                    getMd().setPublicationDate(getDefaultPubDate());
-                }
-            } else {
-                getMd().setPublicationDate(getDefaultPubDate());
-            }
-        }
-
-        private void ivyModuleStarted(Attributes attributes) throws SAXException {
-            descriptorVersion = attributes.getValue("version");
-            int versionIndex = ALLOWED_VERSIONS.indexOf(descriptorVersion);
-            if (versionIndex == -1) {
-                addError("invalid version " + descriptorVersion);
-                throw new SAXException("invalid version " + descriptorVersion);
-            }
-            if (versionIndex >= ALLOWED_VERSIONS.indexOf("1.3")) {
-                LOGGER.debug("post 1.3 ivy file: using " + PatternMatcher.EXACT + " as default matcher");
-                defaultMatcher = getMatcher(PatternMatcher.EXACT);
-            } else {
-                LOGGER.debug("pre 1.3 ivy file: using " + PatternMatcher.EXACT_OR_REGEXP + " as default matcher");
-                defaultMatcher = getMatcher(PatternMatcher.EXACT_OR_REGEXP);
-            }
-
-            for (int i = 0; i < attributes.getLength(); i++) {
-                if (attributes.getQName(i).startsWith("xmlns:")) {
-                    getMd().addExtraAttributeNamespace(attributes.getQName(i).substring("xmlns:".length()), attributes.getValue(i));
-                }
-            }
-        }
-
-        private void descriptionStarted(String qName, Attributes attributes) {
-            buffer.append("<").append(qName);
-            for (int i = 0; i < attributes.getLength(); i++) {
-                buffer.append(" ");
-                buffer.append(attributes.getQName(i));
-                buffer.append("=\"");
-                buffer.append(attributes.getValue(i));
-                buffer.append("\"");
-            }
-            buffer.append(">");
-        }
-
-        private void addDependencyArtifacts(String tag, Attributes attributes)
-                throws MalformedURLException {
-            state = State.DEP_ARTIFACT;
-            parseRule(tag, attributes);
-        }
-
-        private void addIncludeRule(String tag, Attributes attributes)
-                throws MalformedURLException {
-            state = State.ARTIFACT_INCLUDE;
-            parseRule(tag, attributes);
-        }
-
-        private void addExcludeRule(String tag, Attributes attributes)
-                throws MalformedURLException {
-            state = State.ARTIFACT_EXCLUDE;
-            parseRule(tag, attributes);
-        }
-
-        private void parseRule(String tag, Attributes attributes) throws MalformedURLException {
-            String name = substitute(attributes.getValue("name"));
-            if (name == null) {
-                name = substitute(attributes.getValue("artifact"));
-                if (name == null) {
-                    name = "artifact".equals(tag) ? dd.getDependencyId().getName()
-                            : PatternMatcher.ANY_EXPRESSION;
-                }
-            }
-            String type = substitute(attributes.getValue("type"));
-            if (type == null) {
-                type = "artifact".equals(tag) ? "jar" : PatternMatcher.ANY_EXPRESSION;
-            }
-            String ext = substitute(attributes.getValue("ext"));
-            ext = ext != null ? ext : type;
-            if (state == State.DEP_ARTIFACT) {
-                String url = substitute(attributes.getValue("url"));
-                Map extraAttributes = getExtraAttributes(attributes, new String[]{"name", "type", "ext", "url", "conf"});
-                confAware = new DefaultDependencyArtifactDescriptor(dd, name, type, ext, url == null ? null : new URL(url), extraAttributes);
-            } else if (state == State.ARTIFACT_INCLUDE) {
-                PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
-                String org = elvis(substitute(attributes.getValue("org")), PatternMatcher.ANY_EXPRESSION);
-                String module = elvis(substitute(attributes.getValue("module")), PatternMatcher.ANY_EXPRESSION);
-                ArtifactId aid = new ArtifactId(IvyUtil.createModuleId(org, module), name, type, ext);
-                Map extraAttributes = getExtraAttributes(attributes, new String[]{"org", "module", "name", "type", "ext", "matcher", "conf"});
-                confAware = new DefaultIncludeRule(aid, matcher, extraAttributes);
-            } else { // _state == ARTIFACT_EXCLUDE || EXCLUDE
-                PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
-                String org = elvis(substitute(attributes.getValue("org")), PatternMatcher.ANY_EXPRESSION);
-                String module = elvis(substitute(attributes.getValue("module")), PatternMatcher.ANY_EXPRESSION);
-                ArtifactId aid = new ArtifactId(IvyUtil.createModuleId(org, module), name, type, ext);
-                Map extraAttributes = getExtraAttributes(attributes, new String[]{"org", "module", "name", "type", "ext", "matcher", "conf"});
-                confAware = new DefaultExcludeRule(aid, matcher, extraAttributes);
-            }
-            String confs = substitute(attributes.getValue("conf"));
-            // only add confs if they are specified. if they aren't, endElement will handle this
-            // only if there are no conf defined in sub elements
-            if (confs != null && confs.length() > 0) {
-                String[] conf;
-                if ("*".equals(confs)) {
-                    conf = getMd().getConfigurationsNames();
-                } else {
-                    conf = confs.split(",");
-                }
-                for (String confName : conf) {
-                    addConfiguration(confName.trim());
-                }
-            }
-        }
-
-        private void addConfiguration(String c) {
-            confAware.addConfiguration(c);
-            if (state != State.EXCLUDE) {
-                // we are currently adding a configuration to either an include, exclude or artifact
-                // element
-                // of a dependency. This means that we have to add this element to the corresponding
-                // conf
-                // of the current dependency descriptor
-                if (confAware instanceof DependencyArtifactDescriptor) {
-                    dd.addDependencyArtifact(c, (DependencyArtifactDescriptor) confAware);
-                } else if (confAware instanceof IncludeRule) {
-                    dd.addIncludeRule(c, (IncludeRule) confAware);
-                } else if (confAware instanceof ExcludeRule) {
-                    dd.addExcludeRule(c, (ExcludeRule) confAware);
-                }
-            }
-        }
-
-        private PatternMatcher getPatternMatcher(String m) {
-            String matcherName = substitute(m);
-            PatternMatcher matcher = matcherName == null ? defaultMatcher : getMatcher(matcherName);
-            if (matcher == null) {
-                throw new IllegalArgumentException("unknown matcher " + matcherName);
-            }
-            return matcher;
-        }
-
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            if (buffer != null) {
-                buffer.append(ch, start, length);
-            }
-        }
-
-        public void endElement(String uri, String localName, String qName) throws SAXException {
-            if (state == State.PUB && "artifact".equals(qName) && artifact.getConfigurations().length == 0) {
-                String[] confs = publicationsDefaultConf == null ? getMd().getConfigurationsNames() : publicationsDefaultConf;
-                for (String confName : confs) {
-                    artifact.addConfiguration(confName.trim());
-                    getMd().addArtifact(confName.trim(), artifact);
-                }
-            } else if ("configurations".equals(qName)) {
-                checkConfigurations();
-            } else if ((state == State.DEP_ARTIFACT && "artifact".equals(qName))
-                    || (state == State.ARTIFACT_INCLUDE && "include".equals(qName))
-                    || (state == State.ARTIFACT_EXCLUDE && "exclude".equals(qName))) {
-                state = State.DEP;
-                if (confAware.getConfigurations().length == 0) {
-                    String[] confs = getMd().getConfigurationsNames();
-                    for (String confName : confs) {
-                        addConfiguration(confName);
-                    }
-                }
-                confAware = null;
-            } else if ("exclude".equals(qName) && state == State.EXCLUDE) {
-                if (confAware.getConfigurations().length == 0) {
-                    String[] confs = getMd().getConfigurationsNames();
-                    for (String confName : confs) {
-                        addConfiguration(confName);
-                    }
-                }
-                confAware = null;
-                state = State.DEPS;
-            } else if ("dependency".equals(qName) && state == State.DEP) {
-                if (dd.getModuleConfigurations().length == 0) {
-                    parseDepsConfs(getDefaultConf(), dd);
-                }
-                state = State.DEPS;
-            } else if ("dependencies".equals(qName) && state == State.DEPS) {
-                state = State.NONE;
-            } else if (state == State.INFO && "info".equals(qName)) {
-                state = State.NONE;
-            } else if (state == State.DESCRIPTION && "description".equals(qName)) {
-                getMd().setDescription(buffer == null ? "" : buffer.toString().trim());
-                buffer = null;
-                state = State.INFO;
-            } else if (state == State.EXTRA_INFO) {
-                getMd().addExtraInfo(qName, buffer == null ? "" : buffer.toString());
-                buffer = null;
-                state = State.INFO;
-            } else if (state == State.DESCRIPTION) {
-                if (buffer.toString().endsWith("<" + qName + ">")) {
-                    buffer.deleteCharAt(buffer.length() - 1);
-                    buffer.append("/>");
-                } else {
-                    buffer.append("</").append(qName).append(">");
-                }
-            }
-        }
-
-        private void checkConfigurations() {
-            if (getMd().getConfigurations().length == 0) {
-                getMd().addConfiguration(new Configuration("default"));
-            }
-        }
-
-        private void replaceConfigurationWildcards() {
-            Configuration[] configs = getMd().getConfigurations();
-            for (Configuration config : configs) {
-                config.replaceWildcards(getMd());
-            }
-        }
-
-        private URL getSchemaURL() {
-            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) {
-            return value != null ? value : defaultValue;
-        }
-
-        private String substitute(String name) {
-            return IvyPatternHelper.substituteVariables(name, properties);
-        }
-
-        private Map getExtraAttributes(Attributes attributes, String[] ignoredAttributeNames) {
-            Map ret = new HashMap();
-            Collection ignored = Arrays.asList(ignoredAttributeNames);
-            for (int i = 0; i < attributes.getLength(); i++) {
-                if (!ignored.contains(attributes.getQName(i))) {
-                    ret.put(attributes.getQName(i), substitute(attributes.getValue(i)));
-                }
-            }
-            return ret;
-        }
-
-        private void fillExtraAttributes(DefaultExtendableItem item, Attributes attributes, String[] ignoredAttNames) {
-            Map extraAttributes = getExtraAttributes(attributes, ignoredAttNames);
-            for (Object name : extraAttributes.keySet()) {
-                item.setExtraAttribute((String) name, (String) extraAttributes.get(name));
-            }
-        }
-
-        private PatternMatcher getMatcher(String matcherName) {
-            return resolverStrategy.getPatternMatcher(matcherName);
-        }
-    }
-
-    public String toString() {
-        return "ivy parser";
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 4307a09..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java
+++ /dev/null
@@ -1,32 +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.gradle.api.GradleException;
-import org.gradle.internal.exceptions.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
deleted file mode 100644
index 584e45a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java
+++ /dev/null
@@ -1,29 +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.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-
-import java.io.File;
-
-public interface MetaDataParser {
-    MutableModuleVersionMetaData parseMetaData(DescriptorParseContext context, LocallyAvailableExternalResource resource) throws MetaDataParseException;
-
-    MutableModuleVersionMetaData parseMetaData(DescriptorParseContext ivySettings, File descriptorFile) throws MetaDataParseException;
-
-    MutableModuleVersionMetaData parseMetaData(DescriptorParseContext ivySettings, File descriptorFile, boolean validate) throws MetaDataParseException;
-}
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
deleted file mode 100644
index 7ba8117..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java
+++ /dev/null
@@ -1,776 +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.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.util.XMLHelper;
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomProfile;
-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 implements PomParent {
-
-    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 TYPE = "type";
-    private static final String PROFILES = "profiles";
-    private static final String PROFILE = "profile";
-    private static final String PROFILE_ID = "id";
-    private static final String PROFILE_ACTIVATION = "activation";
-    private static final String PROFILE_ACTIVATION_ACTIVE_BY_DEFAULT = "activeByDefault";
-
-    private PomParent pomParent = new RootPomParent();
-    private final Map<String, String> properties = new HashMap<String, String>();
-    private List<PomDependencyMgt> declaredDependencyMgts;
-    private List<PomProfile> declaredActivePomProfiles;
-    private Map<MavenDependencyKey, PomDependencyMgt> resolvedDependencyMgts;
-    private final Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
-    private Map<MavenDependencyKey, PomDependencyData> resolvedDependencies;
-
-    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);
-
-        setDefaultParentGavProperties();
-        setPomProperties();
-        setActiveProfileProperties();
-    }
-
-    public void setPomParent(PomParent pomParent) {
-        this.pomParent = pomParent;
-        setPomParentProperties();
-    }
-
-    private void setPomParentProperties() {
-        Map<String, String> parentPomProps = pomParent.getProperties();
-
-        for(Map.Entry<String, String> entry : parentPomProps.entrySet()) {
-            setProperty(entry.getKey(), entry.getValue());
-        }
-    }
-
-    private void setPomProperties() {
-        for(Map.Entry<String, String> pomProperty : getPomProperties().entrySet()) {
-            setProperty(pomProperty.getKey(), pomProperty.getValue());
-        }
-    }
-
-    /**
-     * Sets properties for all active profiles. Properties from an active profile override existing POM properties.
-     */
-    private void setActiveProfileProperties() {
-        for(PomProfile activePomProfile : parseActivePomProfiles()) {
-            for(Map.Entry<String, String> property : activePomProfile.getProperties().entrySet()) {
-                properties.put(property.getKey(), property.getValue());
-            }
-        }
-    }
-
-    private void setDefaultParentGavProperties() {
-        setGavPropertyValueWithoutReplacement(GavProperty.PARENT_GROUP_ID, getParentGroupId());
-        setGavPropertyValueWithoutReplacement(GavProperty.PARENT_VERSION, getParentVersion());
-    }
-
-    private void setGavPropertyValueWithoutReplacement(GavProperty gavProperty, String propertyValue) {
-        for(String name : gavProperty.getNames()) {
-            setProperty(name, propertyValue);
-        }
-    }
-
-    private enum GavProperty {
-        PARENT_VERSION("parent.version", "project.parent.version"),
-        PARENT_GROUP_ID("parent.groupId", "project.parent.groupId"),
-        GROUP_ID("project.groupId", "pom.groupId", "groupId"),
-        ARTIFACT_ID("project.artifactId", "pom.artifactId", "artifactId"),
-        VERSION("project.version", "pom.version", "version");
-
-        private final String[] names;
-
-        private GavProperty(String... names) {
-            this.names = names;
-        }
-
-        public String[] getNames() {
-            return names;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return projectElement.getOwnerDocument().getDocumentURI();
-    }
-
-    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 guarantee 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 Map<String, String> getProperties() {
-        return properties;
-    }
-
-    public void addImportedDependencyMgts(Map<MavenDependencyKey, PomDependencyMgt> inherited) {
-        if (resolvedDependencyMgts != null) {
-            throw new IllegalStateException("Cannot add imported dependency management elements after dependency management elements have been resolved for this POM.");
-        }
-        importedDependencyMgts.putAll(inherited);
-    }
-
-    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 List<License> getLicenses() {
-        Element licenses = getFirstChildElement(projectElement, LICENSES);
-        if (licenses == null) {
-            return Collections.emptyList();
-        }
-        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;
-    }
-
-    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 IvyUtil.createModuleRevisionId(relocGroupId, relocArtId, relocVersion);
-        }
-    }
-
-    /**
-     * Returns all dependencies for this POM, including those inherited from parent POMs.
-     */
-    public Map<MavenDependencyKey, PomDependencyData> getDependencies() {
-        if (resolvedDependencies == null) {
-            resolvedDependencies = resolveDependencies();
-        }
-        return resolvedDependencies;
-    }
-
-    private Map<MavenDependencyKey, PomDependencyData> resolveDependencies() {
-        Map<MavenDependencyKey, PomDependencyData> dependencies = new LinkedHashMap<MavenDependencyKey, PomDependencyData>();
-
-        for(PomDependencyData dependency : getDependencyData(projectElement)) {
-            dependencies.put(dependency.getId(), dependency);
-        }
-
-        // Maven adds inherited dependencies last
-        for (Map.Entry<MavenDependencyKey, PomDependencyData> entry : pomParent.getDependencies().entrySet()) {
-            if (!dependencies.containsKey(entry.getKey())) {
-                dependencies.put(entry.getKey(), entry.getValue());
-            }
-        }
-
-        for(PomProfile pomProfile : parseActivePomProfiles()) {
-            for(PomDependencyData dependency : pomProfile.getDependencies()) {
-                dependencies.put(dependency.getId(), dependency);
-            }
-        }
-
-        return dependencies;
-    }
-
-    private List<PomDependencyData> getDependencyData(Element parentElement) {
-        List<PomDependencyData> depElements = new ArrayList<PomDependencyData>();
-        Element dependenciesElement = getFirstChildElement(parentElement, DEPENDENCIES);
-        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())) {
-                    depElements.add(new PomDependencyData((Element) node));
-                }
-            }
-        }
-
-        return depElements;
-    }
-
-    /**
-     * Returns all dependency management elements for this POM, including those inherited from parent and imported POMs.
-     */
-    public Map<MavenDependencyKey, PomDependencyMgt> getDependencyMgt() {
-        if(resolvedDependencyMgts == null) {
-            resolvedDependencyMgts = resolveDependencyMgt();
-        }
-        return resolvedDependencyMgts;
-    }
-
-    private Map<MavenDependencyKey, PomDependencyMgt> resolveDependencyMgt() {
-        Map<MavenDependencyKey, PomDependencyMgt> dependencies = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
-        dependencies.putAll(pomParent.getDependencyMgt());
-        dependencies.putAll(importedDependencyMgts);
-        for(PomDependencyMgt dependencyMgt : parseDependencyMgt()) {
-            dependencies.put(dependencyMgt.getId(), dependencyMgt);
-        }
-        return dependencies;
-    }
-
-    /**
-     * Parses the dependency management elements declared in this POM without removing the duplicates.
-     *
-     * @return Parsed dependency management elements
-     */
-    public List<PomDependencyMgt> parseDependencyMgt() {
-        if(declaredDependencyMgts == null) {
-            List<PomDependencyMgt> dependencyMgts = getDependencyMgt(projectElement);
-
-            for(PomProfile pomProfile : parseActivePomProfiles()) {
-                for(PomDependencyMgt dependencyMgt : pomProfile.getDependencyMgts()) {
-                    dependencyMgts.add(dependencyMgt);
-                }
-            }
-
-            declaredDependencyMgts = dependencyMgts;
-        }
-
-        return declaredDependencyMgts;
-    }
-
-    private List<PomDependencyMgt> getDependencyMgt(Element parentElement) {
-        List<PomDependencyMgt> depMgmtElements = new ArrayList<PomDependencyMgt>();
-        Element dependenciesElement = getFirstChildElement(parentElement, DEPENDENCY_MGT);
-        dependenciesElement = getFirstChildElement(dependenciesElement, DEPENDENCIES);
-
-        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())) {
-                    depMgmtElements.add(new PomDependencyMgtElement((Element) node));
-                }
-            }
-        }
-
-        return depMgmtElements;
-    }
-
-    public PomDependencyMgt findDependencyDefaults(MavenDependencyKey dependencyKey) {
-        return getDependencyMgt().get(dependencyKey);
-    }
-
-    public void resolveGAV() {
-        setGavPropertyValue(GavProperty.GROUP_ID, getGroupId());
-        setGavPropertyValue(GavProperty.ARTIFACT_ID, getArtifactId());
-        setGavPropertyValue(GavProperty.VERSION, getVersion());
-    }
-
-    private void setGavPropertyValue(GavProperty gavProperty, String propertyValue) {
-        for(String name : gavProperty.getNames()) {
-            properties.put(name, propertyValue);
-        }
-    }
-
-    public class PomDependencyMgtElement implements PomDependencyMgt {
-        private final Element depElement;
-
-        PomDependencyMgtElement(Element depElement) {
-            this.depElement = depElement;
-        }
-
-        public MavenDependencyKey getId() {
-            return new MavenDependencyKey(getGroupId(), getArtifactId(), getType(), getClassifier());
-        }
-
-        /* (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 String getType() {
-            String val = getFirstChildText(depElement , TYPE);
-            val = replaceProps(val);
-
-            if(val == null) {
-                val = "jar";
-            }
-
-            return val;
-        }
-
-        public String getClassifier() {
-            String val = getFirstChildText(depElement , CLASSIFIER);
-            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(IvyUtil.createModuleId(groupId, artifactId));
-                        }
-                    }
-                }
-            }
-            return exclusions;
-        }
-    }
-
-    public class PomDependencyData extends PomDependencyMgtElement {
-        private final Element depElement;
-        PomDependencyData(Element depElement) {
-            super(depElement);
-            this.depElement = depElement;
-        }
-
-        public boolean isOptional() {
-            Element e = getFirstChildElement(depElement, OPTIONAL);
-            return (e != null) && "true".equalsIgnoreCase(getTextContent(e));
-        }
-    }
-
-    public class PomProfileElement implements PomProfile {
-        private final Element element;
-        private List<PomDependencyMgt> declaredDependencyMgts;
-        private List<PomDependencyData> declaredDependencies;
-
-        PomProfileElement(Element element) {
-            this.element = element;
-        }
-
-        public String getId() {
-            return getFirstChildText(element, PROFILE_ID);
-        }
-
-        public Map<String, String> getProperties() {
-            return getPomProperties(element);
-        }
-
-        public List<PomDependencyMgt> getDependencyMgts() {
-            if(declaredDependencyMgts == null) {
-                declaredDependencyMgts = getDependencyMgt(element);
-            }
-
-            return declaredDependencyMgts;
-        }
-
-        public List<PomDependencyData> getDependencies() {
-            if(declaredDependencies == null) {
-                declaredDependencies = getDependencyData(element);
-            }
-
-            return declaredDependencies;
-        }
-    }
-
-    /**
-     * Parses all active profiles that can be found in POM.
-     *
-     * @return Active POM profiles
-     */
-    private List<PomProfile> parseActivePomProfiles() {
-        if(declaredActivePomProfiles == null) {
-            List<PomProfile> activePomProfiles = new ArrayList<PomProfile>();
-            Element profilesElement = getFirstChildElement(projectElement, PROFILES);
-
-            if(profilesElement != null) {
-                for(Element profileElement : getAllChilds(profilesElement)) {
-                    if(PROFILE.equals(profileElement.getNodeName())) {
-                        Element activationElement = getFirstChildElement(profileElement, PROFILE_ACTIVATION);
-
-                        if(activationElement != null) {
-                            String activeByDefault = getFirstChildText(activationElement, PROFILE_ACTIVATION_ACTIVE_BY_DEFAULT);
-
-                            if(activeByDefault != null && "true".equals(activeByDefault)) {
-                                activePomProfiles.add(new PomProfileElement(profileElement));
-                            }
-                        }
-                    }
-                }
-            }
-
-            declaredActivePomProfiles = activePomProfiles;
-        }
-
-        return declaredActivePomProfiles;
-    }
-
-    /**
-     * @return the content of the properties tag into the pom.
-     */
-    public Map<String, String> getPomProperties() {
-        return getPomProperties(projectElement);
-    }
-
-    private Map<String, String> getPomProperties(Element parentElement) {
-        Map<String, String> pomProperties = new HashMap<String, String>();
-        Element propsEl = getFirstChildElement(parentElement, 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
deleted file mode 100644
index 8f69f51..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcher.java
+++ /dev/null
@@ -1,64 +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.strategy;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.internal.artifacts.metadata.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) {
-        return getCompatibleMatcher(selector).needModuleMetadata(selector);
-    }
-
-    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
deleted file mode 100644
index 75b5a7d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcher.java
+++ /dev/null
@@ -1,113 +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.strategy;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.gradle.api.internal.artifacts.metadata.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) {
-        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
deleted file mode 100644
index c442e20..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestStrategy.java
+++ /dev/null
@@ -1,42 +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.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
deleted file mode 100644
index 20372db..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcher.java
+++ /dev/null
@@ -1,47 +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.strategy;
-
-import org.gradle.api.internal.artifacts.metadata.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) {
-        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
deleted file mode 100644
index 1be6c40..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategy.java
+++ /dev/null
@@ -1,65 +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.strategy;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
-import org.gradle.util.CollectionUtils;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-public 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) {
-        return CollectionUtils.sort(versions, this);
-    }
-
-    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
deleted file mode 100644
index 9ff546e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java
+++ /dev/null
@@ -1,53 +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.strategy;
-
-import com.google.common.collect.Maps;
-import org.apache.ivy.plugins.matcher.*;
-
-import java.util.Map;
-
-public class ResolverStrategy {
-    private final VersionMatcher versionMatcher;
-    private final Map<String, PatternMatcher> matchers = Maps.newHashMap();
-
-    public 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;
-
-        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 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
deleted file mode 100644
index e8ac493..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcher.java
+++ /dev/null
@@ -1,59 +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.strategy;
-
-import org.gradle.api.internal.artifacts.metadata.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) {
-        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
deleted file mode 100644
index 374dc13..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionMatcher.java
+++ /dev/null
@@ -1,67 +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.strategy;
-
-import org.gradle.api.internal.artifacts.metadata.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 a candidate version.
-     */
-    public boolean needModuleMetadata(String selector);
-
-    /**
-     * Indicates if the given version selector matches the given candidate version.
-     * Only called if {@link #needModuleMetadata} returned {@code false} for the given selector.
-     */
-    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
deleted file mode 100644
index b05899b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcher.java
+++ /dev/null
@@ -1,180 +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.strategy;
-
-import org.gradle.api.internal.artifacts.metadata.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.
- */
-public 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) {
-        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
deleted file mode 100644
index 3345e16..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java
+++ /dev/null
@@ -1,38 +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.modulecache;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource;
-
-/**
- * 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 implements DescriptorParseContext {
-
-    public boolean artifactExists(ModuleVersionArtifactMetaData artifact) {
-        throw new UnsupportedOperationException();
-    }
-
-    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, Class<? extends SoftwareArtifact> artifactType) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java
deleted file mode 100644
index 37755d9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java
+++ /dev/null
@@ -1,71 +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.modulecache;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.math.BigInteger;
-
-class DefaultCachedMetaData implements ModuleMetaDataCache.CachedMetaData {
-    private final ModuleSource moduleSource;
-    private final BigInteger descriptorHash;
-    private final long ageMillis;
-    private final MutableModuleVersionMetaData metaData;
-
-    public DefaultCachedMetaData(ModuleDescriptorCacheEntry entry, ModuleDescriptor moduleDescriptor, BuildCommencedTimeProvider timeProvider) {
-        this.moduleSource = entry.moduleSource;
-        this.descriptorHash = entry.moduleDescriptorHash;
-        this.ageMillis = timeProvider.getCurrentTime() - entry.createTimestamp;
-        if (moduleDescriptor == null) {
-            metaData = null;
-        } else {
-            ModuleDescriptorAdapter moduleDescriptorAdapter = new ModuleDescriptorAdapter(moduleDescriptor);
-            moduleDescriptorAdapter.setChanging(entry.isChanging);
-            moduleDescriptorAdapter.setMetaDataOnly(entry.isMetaDataOnly);
-            metaData = moduleDescriptorAdapter;
-        }
-    }
-
-    public boolean isMissing() {
-        return metaData == null;
-    }
-
-    public ModuleSource getModuleSource() {
-        return moduleSource;
-    }
-
-    public ResolvedModuleVersion getModuleVersion() {
-        return isMissing() ? null : new DefaultResolvedModuleVersion(getMetaData().getId());
-    }
-
-    public MutableModuleVersionMetaData getMetaData() {
-        return metaData;
-    }
-
-    public long getAgeMillis() {
-        return ageMillis;
-    }
-
-    public BigInteger getDescriptorHash() {
-        return descriptorHash;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java
deleted file mode 100644
index eff486c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java
+++ /dev/null
@@ -1,182 +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.modulecache;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifierSerializer;
-import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.messaging.serialize.SetSerializer;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.math.BigInteger;
-import java.util.Set;
-
-public class DefaultModuleArtifactsCache implements ModuleArtifactsCache {
-    private final BuildCommencedTimeProvider timeProvider;
-    private final CacheLockingManager cacheLockingManager;
-    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> cache;
-
-    public DefaultModuleArtifactsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        this.timeProvider = timeProvider;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> getCache() {
-        if (cache == null) {
-            cache = initCache();
-        }
-        return cache;
-    }
-
-    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> initCache() {
-        return cacheLockingManager.createCache("module-artifacts", new ModuleArtifactsKeySerializer(), new ModuleArtifactsCacheEntrySerializer());
-    }
-
-    public CachedArtifacts cacheArtifacts(ModuleVersionRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context, BigInteger descriptorHash, Set<ModuleVersionArtifactIdentifier> artifacts) {
-        ModuleArtifactsKey key = new ModuleArtifactsKey(repository.getId(), moduleMetaDataId, context);
-        ModuleArtifactsCacheEntry entry = new ModuleArtifactsCacheEntry(artifacts, timeProvider.getCurrentTime(), descriptorHash);
-        getCache().put(key, entry);
-        return createCacheArtifacts(entry);
-    }
-
-    public CachedArtifacts getCachedArtifacts(ModuleVersionRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context) {
-        ModuleArtifactsKey key = new ModuleArtifactsKey(repository.getId(), moduleMetaDataId, context);
-        ModuleArtifactsCacheEntry entry = getCache().get(key);
-        if (entry == null) {
-            return null;
-        }
-        return createCacheArtifacts(entry);
-    }
-
-    private CachedArtifacts createCacheArtifacts(ModuleArtifactsCacheEntry entry) {
-        long entryAge = timeProvider.getCurrentTime() - entry.createTimestamp;
-        return new DefaultCachedArtifacts(entry.artifacts, entry.moduleDescriptorHash, entryAge);
-    }
-
-    private static class ModuleArtifactsKey {
-        private final String repositoryId;
-        private final ModuleVersionIdentifier moduleId;
-        private final String context;
-
-        private ModuleArtifactsKey(String repositoryId, ModuleVersionIdentifier moduleId, String context) {
-            this.repositoryId = repositoryId;
-            this.moduleId = moduleId;
-            this.context = context;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (!(o instanceof ModuleArtifactsKey)) {
-                return false;
-            }
-
-            ModuleArtifactsKey that = (ModuleArtifactsKey) o;
-            return repositoryId.equals(that.repositoryId) && moduleId.equals(that.moduleId) && context.equals(that.context);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = repositoryId.hashCode();
-            result = 31 * result + moduleId.hashCode();
-            result = 31 * result + context.hashCode();
-            return result;
-        }
-    }
-
-    private static class ModuleArtifactsKeySerializer implements Serializer<ModuleArtifactsKey> {
-        private final ModuleVersionIdentifierSerializer identifierSerializer = new ModuleVersionIdentifierSerializer();
-
-        public void write(Encoder encoder, ModuleArtifactsKey value) throws Exception {
-            encoder.writeString(value.repositoryId);
-            identifierSerializer.write(encoder, value.moduleId);
-            encoder.writeString(value.context);
-        }
-
-        public ModuleArtifactsKey read(Decoder decoder) throws Exception {
-            String resolverId = decoder.readString();
-            ModuleVersionIdentifier moduleVersionIdentifier = identifierSerializer.read(decoder);
-            String context = decoder.readString();
-            return new ModuleArtifactsKey(resolverId, moduleVersionIdentifier, context);
-        }
-    }
-
-    private static class ModuleArtifactsCacheEntry {
-        private final Set<ModuleVersionArtifactIdentifier> artifacts;
-        public BigInteger moduleDescriptorHash;
-        public long createTimestamp;
-
-        ModuleArtifactsCacheEntry(Set<ModuleVersionArtifactIdentifier> artifacts, long createTimestamp, BigInteger moduleDescriptorHash) {
-            this.artifacts = artifacts;
-            this.createTimestamp = createTimestamp;
-            this.moduleDescriptorHash = moduleDescriptorHash;
-        }
-    }
-
-
-    private static class ModuleArtifactsCacheEntrySerializer implements Serializer<ModuleArtifactsCacheEntry> {
-        private final Serializer<Set<ModuleVersionArtifactIdentifier>> artifactsSerializer = 
-                new SetSerializer<ModuleVersionArtifactIdentifier>(new ModuleVersionArtifactIdentifierSerializer());
-        public void write(Encoder encoder, ModuleArtifactsCacheEntry value) throws Exception {
-            encoder.writeLong(value.createTimestamp);
-            byte[] hash = value.moduleDescriptorHash.toByteArray();
-            encoder.writeBinary(hash);
-            artifactsSerializer.write(encoder, value.artifacts);
-        }
-
-        public ModuleArtifactsCacheEntry read(Decoder decoder) throws Exception {
-            long createTimestamp = decoder.readLong();
-            byte[] encodedHash = decoder.readBinary();
-            BigInteger hash = new BigInteger(encodedHash);
-            Set<ModuleVersionArtifactIdentifier> artifacts = artifactsSerializer.read(decoder);
-            return new ModuleArtifactsCacheEntry(artifacts, createTimestamp, hash);
-        }
-    }
-
-    private static class DefaultCachedArtifacts implements ModuleArtifactsCache.CachedArtifacts {
-        private final Set<ModuleVersionArtifactIdentifier> artifacts;
-        private final BigInteger descriptorHash;
-        private final long ageMillis;
-
-        private DefaultCachedArtifacts(Set<ModuleVersionArtifactIdentifier> artifacts, BigInteger descriptorHash, long ageMillis) {
-            this.ageMillis = ageMillis;
-            this.artifacts = artifacts;
-            this.descriptorHash = descriptorHash;
-        }
-
-        public Set<ModuleVersionArtifactIdentifier> getArtifacts() {
-            return artifacts;
-        }
-
-        public BigInteger getDescriptorHash() {
-            return descriptorHash;
-        }
-
-        public long getAgeMillis() {
-            return ageMillis;
-        }
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java
deleted file mode 100644
index 05d953d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java
+++ /dev/null
@@ -1,176 +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.modulecache;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-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.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-import org.gradle.api.internal.filestore.PathKeyFileStore;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.internal.hash.HashValue;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.DefaultSerializer;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.util.BuildCommencedTimeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.math.BigInteger;
-
-public class DefaultModuleMetaDataCache implements ModuleMetaDataCache {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultModuleMetaDataCache.class);
-
-    private final BuildCommencedTimeProvider timeProvider;
-    private final CacheLockingManager cacheLockingManager;
-
-    private final ModuleDescriptorStore moduleDescriptorStore;
-    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> cache;
-
-    public DefaultModuleMetaDataCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager, ResolverStrategy resolverStrategy) {
-        this.timeProvider = timeProvider;
-        this.cacheLockingManager = cacheLockingManager;
-
-        moduleDescriptorStore = new ModuleDescriptorStore(new PathKeyFileStore(cacheLockingManager.createMetaDataStore()), new IvyXmlModuleDescriptorWriter(), new IvyXmlModuleDescriptorParser(resolverStrategy));
-    }
-
-    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> getCache() {
-        if (cache == null) {
-            cache = initCache();
-        }
-        return cache;
-    }
-
-    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> initCache() {
-        return cacheLockingManager.createCache("module-metadata", new RevisionKeySerializer(), new ModuleDescriptorCacheEntrySerializer());
-    }
-
-    public CachedMetaData getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
-        ModuleDescriptorCacheEntry moduleDescriptorCacheEntry = getCache().get(createKey(repository, moduleVersionIdentifier));
-        if (moduleDescriptorCacheEntry == null) {
-            return null;
-        }
-        if (moduleDescriptorCacheEntry.isMissing) {
-            return new DefaultCachedMetaData(moduleDescriptorCacheEntry, null, timeProvider);
-        }
-        ModuleDescriptor descriptor = moduleDescriptorStore.getModuleDescriptor(repository, moduleVersionIdentifier);
-        if (descriptor == null) {
-            // Descriptor file has been manually deleted - ignore the entry
-            return null;
-        }
-        return new DefaultCachedMetaData(moduleDescriptorCacheEntry, descriptor, timeProvider);
-    }
-
-    public CachedMetaData cacheMissing(ModuleVersionRepository repository, ModuleVersionIdentifier id) {
-        LOGGER.debug("Recording absence of module descriptor in cache: {} [changing = {}]", id, false);
-        ModuleDescriptorCacheEntry entry = createMissingEntry(false);
-        getCache().put(createKey(repository, id), entry);
-        return new DefaultCachedMetaData(entry, null, timeProvider);
-    }
-
-    public CachedMetaData cacheMetaData(ModuleVersionRepository repository, ModuleVersionMetaData metaData, ModuleSource moduleSource) {
-        ModuleDescriptor moduleDescriptor = metaData.getDescriptor();
-        LOGGER.debug("Recording module descriptor in cache: {} [changing = {}]", moduleDescriptor.getModuleRevisionId(), metaData.isChanging());
-        LocallyAvailableResource resource = moduleDescriptorStore.putModuleDescriptor(repository, moduleDescriptor);
-        ModuleDescriptorCacheEntry entry = createEntry(metaData.isChanging(), metaData.isMetaDataOnly(), resource.getSha1(), moduleSource);
-        getCache().put(createKey(repository, metaData.getId()), entry);
-        return new DefaultCachedMetaData(entry, null, timeProvider);
-    }
-
-    private RevisionKey createKey(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
-        return new RevisionKey(repository.getId(), moduleVersionIdentifier);
-    }
-
-    private ModuleDescriptorCacheEntry createMissingEntry(boolean changing) {
-        return new ModuleDescriptorCacheEntry(changing, false, true, timeProvider.getCurrentTime(), BigInteger.ZERO, null);
-    }
-
-    private ModuleDescriptorCacheEntry createEntry(boolean changing, boolean metaDataOnly, HashValue moduleDescriptorHash, ModuleSource moduleSource) {
-        return new ModuleDescriptorCacheEntry(changing, metaDataOnly, false, timeProvider.getCurrentTime(), moduleDescriptorHash.asBigInteger(), moduleSource);
-    }
-
-    private static class RevisionKey {
-        private final String repositoryId;
-        private final ModuleVersionIdentifier moduleVersionIdentifier;
-
-        private RevisionKey(String repositoryId, ModuleVersionIdentifier moduleVersionIdentifier) {
-            this.repositoryId = repositoryId;
-            this.moduleVersionIdentifier = moduleVersionIdentifier;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == null || !(o instanceof RevisionKey)) {
-                return false;
-            }
-            RevisionKey other = (RevisionKey) o;
-            return repositoryId.equals(other.repositoryId) && moduleVersionIdentifier.equals(other.moduleVersionIdentifier);
-        }
-
-        @Override
-        public int hashCode() {
-            return repositoryId.hashCode() ^ moduleVersionIdentifier.hashCode();
-        }
-    }
-
-    private static class RevisionKeySerializer implements Serializer<RevisionKey> {
-        private final ModuleVersionIdentifierSerializer identifierSerializer = new ModuleVersionIdentifierSerializer();
-
-        public void write(Encoder encoder, RevisionKey value) throws Exception {
-            encoder.writeString(value.repositoryId);
-            identifierSerializer.write(encoder, value.moduleVersionIdentifier);
-        }
-
-        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 implements Serializer<ModuleDescriptorCacheEntry> {
-        private final DefaultSerializer<ModuleSource> moduleSourceSerializer = new DefaultSerializer<ModuleSource>(ModuleSource.class.getClassLoader());
-
-        public void write(Encoder encoder, ModuleDescriptorCacheEntry value) throws Exception {
-            encoder.writeBoolean(value.isMissing);
-            encoder.writeBoolean(value.isChanging);
-            encoder.writeBoolean(value.isMetaDataOnly);
-            encoder.writeLong(value.createTimestamp);
-            moduleSourceSerializer.write(encoder, value.moduleSource);
-            byte[] hash = value.moduleDescriptorHash.toByteArray();
-            encoder.writeBinary(hash);
-        }
-
-        public ModuleDescriptorCacheEntry read(Decoder decoder) throws Exception {
-            boolean isMissing = decoder.readBoolean();
-            boolean isChanging = decoder.readBoolean();
-            boolean isMetaData = decoder.readBoolean();
-            long createTimestamp = decoder.readLong();
-            ModuleSource moduleSource = moduleSourceSerializer.read(decoder);
-            byte[] encodedHash = decoder.readBinary();
-            BigInteger hash = new BigInteger(encodedHash);
-            return new ModuleDescriptorCacheEntry(isChanging, isMetaData, isMissing, createTimestamp, hash, moduleSource);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.java
deleted file mode 100644
index 7ef8324..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.java
+++ /dev/null
@@ -1,37 +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.modulecache;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier;
-
-import java.math.BigInteger;
-import java.util.Set;
-
-public interface ModuleArtifactsCache {
-    CachedArtifacts cacheArtifacts(ModuleVersionRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context, BigInteger descriptorHash, Set<ModuleVersionArtifactIdentifier> artifacts);
-
-    CachedArtifacts getCachedArtifacts(ModuleVersionRepository delegate, ModuleVersionIdentifier moduleMetaDataId, String context);
-
-    interface CachedArtifacts {
-        Set<ModuleVersionArtifactIdentifier> getArtifacts();
-
-        BigInteger getDescriptorHash();
-
-        long getAgeMillis();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.java
deleted file mode 100644
index b75f6b5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.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.artifacts.ivyservice.modulecache;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-
-import java.math.BigInteger;
-
-class ModuleDescriptorCacheEntry {
-    public boolean isChanging;
-    public boolean isMetaDataOnly;
-    public boolean isMissing;
-    public long createTimestamp;
-    public ModuleSource moduleSource;
-    public BigInteger moduleDescriptorHash;
-
-    ModuleDescriptorCacheEntry(boolean isChanging, boolean isMetaDataOnly, boolean isMissing, long createTimestamp, BigInteger moduleDescriptorHash, ModuleSource moduleSource) {
-        this.isChanging = isChanging;
-        this.isMetaDataOnly = isMetaDataOnly;
-        this.isMissing = isMissing;
-        this.createTimestamp = createTimestamp;
-        this.moduleSource = moduleSource;
-        this.moduleDescriptorHash = moduleDescriptorHash;
-    }
-}
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
deleted file mode 100644
index 46dbfb3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.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.Action;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-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.DescriptorParseContext;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser;
-import org.gradle.api.internal.filestore.PathKeyFileStore;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-
-import java.io.File;
-
-public class ModuleDescriptorStore {
-
-    public static final String FILE_PATH_PATTERN = "%s/%s/%s/%s/ivy.xml";
-    private final IvyXmlModuleDescriptorParser descriptorParser;
-    private final PathKeyFileStore metaDataStore;
-    private final IvyModuleDescriptorWriter descriptorWriter;
-
-    public ModuleDescriptorStore(PathKeyFileStore metaDataStore, IvyModuleDescriptorWriter descriptorWriter, IvyXmlModuleDescriptorParser ivyXmlModuleDescriptorParser) {
-        this.metaDataStore = metaDataStore;
-        this.descriptorWriter = descriptorWriter;
-        this.descriptorParser = ivyXmlModuleDescriptorParser;
-    }
-
-    public ModuleDescriptor getModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
-        String filePath = getFilePath(repository, moduleVersionIdentifier);
-        final LocallyAvailableResource resource = metaDataStore.get(filePath);
-        if (resource != null) {
-            return parseModuleDescriptorFile(resource.getFile());
-        }
-        return null;
-    }
-
-    public LocallyAvailableResource putModuleDescriptor(ModuleVersionRepository repository, final ModuleDescriptor moduleDescriptor) {
-        String filePath = getFilePath(repository, moduleDescriptor.getModuleRevisionId());
-        return metaDataStore.add(filePath, new Action<File>() {
-            public void execute(File moduleDescriptorFile) {
-                try {
-                    descriptorWriter.write(moduleDescriptor, moduleDescriptorFile);
-                } catch (Exception e) {
-                    throw UncheckedException.throwAsUncheckedException(e);
-                }
-            }
-        });
-    }
-
-    private ModuleDescriptor parseModuleDescriptorFile(File moduleDescriptorFile) {
-        DescriptorParseContext parserSettings = new CachedModuleDescriptorParseContext();
-        return descriptorParser.parseMetaData(parserSettings, moduleDescriptorFile, false).getDescriptor();
-    }
-
-    private String getFilePath(ModuleVersionRepository repository, ModuleRevisionId moduleRevisionId) {
-        return String.format(FILE_PATH_PATTERN, moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision(), repository.getId());
-    }
-
-    private String getFilePath(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier) {
-        return String.format(FILE_PATH_PATTERN, moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion(), repository.getId());
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.java
deleted file mode 100644
index 7874cf0..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.java
+++ /dev/null
@@ -1,47 +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.modulecache;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-import java.math.BigInteger;
-
-public interface ModuleMetaDataCache {
-    CachedMetaData cacheMissing(ModuleVersionRepository repository, ModuleVersionIdentifier id);
-
-    CachedMetaData cacheMetaData(ModuleVersionRepository repository, ModuleVersionMetaData metaData, ModuleSource moduleSource);
-
-    CachedMetaData getCachedModuleDescriptor(ModuleVersionRepository repository, ModuleVersionIdentifier moduleVersionIdentifier);
-
-    interface CachedMetaData {
-        ResolvedModuleVersion getModuleVersion();
-
-        MutableModuleVersionMetaData getMetaData();
-
-        long getAgeMillis();
-
-        BigInteger getDescriptorHash();
-
-        boolean isMissing();
-
-        ModuleSource getModuleSource();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java
deleted file mode 100644
index 08bc815..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData;
-
-public interface ConfigurationsToArtifactsConverter {
-    void addArtifacts(MutableLocalComponentMetaData 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
deleted file mode 100644
index 2b82fe3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-
-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/DefaultConfigurationsToArtifactsConverter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverter.java
deleted file mode 100644
index 523488b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
-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.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.metadata.MutableLocalComponentMetaData;
-import org.gradle.util.GUtil;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class DefaultConfigurationsToArtifactsConverter implements ConfigurationsToArtifactsConverter {
-
-    public void addArtifacts(MutableLocalComponentMetaData metaData, Iterable<? extends Configuration> configurations) {
-        DefaultModuleDescriptor moduleDescriptor = metaData.getModuleDescriptor();
-        for (Configuration configuration : configurations) {
-            for (PublishArtifact publishArtifact : configuration.getArtifacts()) {
-                Artifact ivyArtifact = createIvyArtifact(publishArtifact, moduleDescriptor.getModuleRevisionId());
-                metaData.addArtifact(configuration.getName(), ivyArtifact, publishArtifact.getFile());
-            }
-        }
-    }
-
-    public Artifact createIvyArtifact(PublishArtifact publishArtifact, ModuleRevisionId moduleRevisionId) {
-        Map<String, String> extraAttributes = new HashMap<String, String>();
-        if (GUtil.isTrue(publishArtifact.getClassifier())) {
-            extraAttributes.put(Dependency.CLASSIFIER, publishArtifact.getClassifier());
-        }
-        String name = publishArtifact.getName();
-        if (!GUtil.isTrue(name)) {
-            name = moduleRevisionId.getName();
-        }
-        return new DefaultArtifact(
-                moduleRevisionId,
-                publishArtifact.getDate(),
-                name,
-                publishArtifact.getType(),
-                publishArtifact.getExtension(),
-                extraAttributes);
-    }
-}
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
deleted file mode 100644
index c7cdc2d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.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 java.util.Arrays;
-
-public class DefaultConfigurationsToModuleDescriptorConverter implements ConfigurationsToModuleDescriptorConverter {
-    public void addConfigurations(DefaultModuleDescriptor moduleDescriptor, Iterable<? extends Configuration> configurations) {
-        for (Configuration configuration : configurations) {
-            moduleDescriptor.addConfiguration(getIvyConfiguration(configuration));
-        }
-    }
-
-    public org.apache.ivy.core.module.descriptor.Configuration getIvyConfiguration(Configuration configuration) {
-        String[] superConfigs = Configurations.getNames(configuration.getExtendsFrom(), false).toArray(new String[configuration.getExtendsFrom().size()]);
-        Arrays.sort(superConfigs);
-        return new org.apache.ivy.core.module.descriptor.Configuration(
-                configuration.getName(),
-                configuration.isVisible() ? org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC : org.apache.ivy.core.module.descriptor.Configuration.Visibility.PRIVATE,
-                configuration.getDescription(),
-                superConfigs,
-                configuration.isTransitive(),
-                null);
-    }
-}
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
deleted file mode 100644
index 5c1ffe3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactory.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-
-public class DefaultModuleDescriptorFactory implements ModuleDescriptorFactory {
-    public DefaultModuleDescriptor createModuleDescriptor(Module module) {
-        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/ModuleDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java
deleted file mode 100644
index afc2fe5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ModuleDescriptorFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Module;
-
-public interface ModuleDescriptorFactory {
-    DefaultModuleDescriptor createModuleDescriptor(Module module);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishLocalComponentFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishLocalComponentFactory.java
deleted file mode 100644
index f9c9f5a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishLocalComponentFactory.java
+++ /dev/null
@@ -1,47 +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.moduleconverter;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData;
-
-import java.util.Set;
-
-public class PublishLocalComponentFactory implements LocalComponentFactory {
-    static final String IVY_MAVEN_NAMESPACE = "http://ant.apache.org/ivy/maven";
-    static final String IVY_MAVEN_NAMESPACE_PREFIX = "m";
-
-    private LocalComponentFactory resolveLocalComponentFactory;
-    private ConfigurationsToArtifactsConverter configurationsToArtifactsConverter;
-
-    public PublishLocalComponentFactory(LocalComponentFactory resolveLocalComponentFactory,
-                                        ConfigurationsToArtifactsConverter configurationsToArtifactsConverter) {
-        this.resolveLocalComponentFactory = resolveLocalComponentFactory;
-        this.configurationsToArtifactsConverter = configurationsToArtifactsConverter;
-    }
-
-    public MutableLocalComponentMetaData convert(Set<? extends Configuration> configurations, ModuleInternal module) {
-        MutableLocalComponentMetaData publishMetaData = resolveLocalComponentFactory.convert(configurations, module);
-        DefaultModuleDescriptor moduleDescriptor = publishMetaData.getModuleDescriptor();
-        moduleDescriptor.addExtraAttributeNamespace(IVY_MAVEN_NAMESPACE_PREFIX, IVY_MAVEN_NAMESPACE);
-        configurationsToArtifactsConverter.addArtifacts(publishMetaData, configurations);
-        return publishMetaData;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java
deleted file mode 100644
index 9c582b6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java
+++ /dev/null
@@ -1,55 +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.moduleconverter;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter;
-import org.gradle.api.internal.artifacts.metadata.DefaultLocalComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData;
-
-import java.util.Set;
-
-public class ResolveLocalComponentFactory implements LocalComponentFactory {
-    private final ModuleDescriptorFactory moduleDescriptorFactory;
-    private final ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter;
-    private final DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter;
-    private final ComponentIdentifierFactory componentIdentifierFactory;
-
-    public ResolveLocalComponentFactory(ModuleDescriptorFactory moduleDescriptorFactory,
-                                        ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter,
-                                        DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter,
-                                        ComponentIdentifierFactory componentIdentifierFactory) {
-        this.moduleDescriptorFactory = moduleDescriptorFactory;
-        this.configurationsToModuleDescriptorConverter = configurationsToModuleDescriptorConverter;
-        this.dependenciesToModuleDescriptorConverter = dependenciesToModuleDescriptorConverter;
-        this.componentIdentifierFactory = componentIdentifierFactory;
-    }
-
-    public MutableLocalComponentMetaData convert(Set<? extends Configuration> configurations, ModuleInternal 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);
-        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module);
-        return new DefaultLocalComponentMetaData(moduleDescriptor, componentIdentifier);
-    }
-}
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
deleted file mode 100644
index 60d7fd6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.DependencyArtifact;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-import org.gradle.util.WrapUtil;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Set;
-
-public abstract class AbstractIvyDependencyDescriptorFactory implements IvyDependencyDescriptorFactory {
-    private ExcludeRuleConverter excludeRuleConverter;
-
-    public AbstractIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        this.excludeRuleConverter = excludeRuleConverter;
-    }
-
-    protected void addExcludesArtifactsAndDependencies(String configuration, ModuleDependency dependency,
-                                                       EnhancedDependencyDescriptor dependencyDescriptor) {
-        addArtifacts(configuration, dependency.getArtifacts(), dependencyDescriptor);
-        addExcludes(configuration, dependency.getExcludeRules(), dependencyDescriptor);
-        addDependencyConfiguration(configuration, dependency, dependencyDescriptor);
-    }
-
-    private void addArtifacts(String configuration, Set<DependencyArtifact> artifacts,
-                              EnhancedDependencyDescriptor dependencyDescriptor) {
-        for (DependencyArtifact artifact : artifacts) {
-            DefaultDependencyArtifactDescriptor artifactDescriptor;
-            try {
-                artifactDescriptor = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, artifact.getName(),
-                        artifact.getType(),
-                        artifact.getExtension() != null ? artifact.getExtension() : artifact.getType(),
-                        artifact.getUrl() != null ? new URL(artifact.getUrl()) : null,
-                        artifact.getClassifier() != null ? WrapUtil.toMap(Dependency.CLASSIFIER,
-                                artifact.getClassifier()) : null);
-            } catch (MalformedURLException e) {
-                throw new InvalidUserDataException("URL for artifact can't be parsed: " + artifact.getUrl(), e);
-            }
-            dependencyDescriptor.addDependencyArtifact(configuration, artifactDescriptor);
-        }
-    }
-
-    private void addDependencyConfiguration(String configuration, ModuleDependency dependency,
-                                            DefaultDependencyDescriptor dependencyDescriptor) {
-        dependencyDescriptor.addDependencyConfiguration(configuration, dependency.getConfiguration());
-    }
-
-    private void addExcludes(String configuration, Set<ExcludeRule> excludeRules,
-                             DefaultDependencyDescriptor dependencyDescriptor) {
-        for (ExcludeRule excludeRule : excludeRules) {
-            dependencyDescriptor.addExcludeRule(configuration, excludeRuleConverter.createExcludeRule(configuration,
-                    excludeRule));
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptor.java
deleted file mode 100644
index b5285ef..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleDependencyDescriptor.java
+++ /dev/null
@@ -1,34 +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.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-
-public class ClientModuleDependencyDescriptor extends EnhancedDependencyDescriptor {
-    private final ModuleVersionMetaData targetModule;
-
-    public ClientModuleDependencyDescriptor(ClientModule moduleDependency, ModuleDescriptor md, ModuleVersionMetaData targetModule, ModuleRevisionId mrid, boolean force, boolean changing, boolean transitive) {
-        super(moduleDependency, md, mrid, force, changing, transitive);
-        this.targetModule = targetModule;
-    }
-
-    public ModuleVersionMetaData getTargetModule() {
-        return targetModule;
-    }
-}
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
deleted file mode 100644
index 2ee9907..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,63 +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.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-public class ClientModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
-    private ClientModuleMetaDataFactory clientModuleMetaDataFactory;
-
-    public ClientModuleIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter, ClientModuleMetaDataFactory clientModuleMetaDataFactory) {
-        super(excludeRuleConverter);
-        this.clientModuleMetaDataFactory = clientModuleMetaDataFactory;
-    }
-
-    private ModuleRevisionId createModuleRevisionId(ModuleDependency dependency) {
-        return IvyUtil.createModuleRevisionId(dependency);
-    }
-
-    public EnhancedDependencyDescriptor createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor parent) {
-        ModuleRevisionId moduleRevisionId = createModuleRevisionId(dependency);
-        ClientModule clientModule = getClientModule(dependency);
-        MutableModuleVersionMetaData moduleVersionMetaData = clientModuleMetaDataFactory.createModuleDescriptor(
-                moduleRevisionId, clientModule.getDependencies());
-
-        EnhancedDependencyDescriptor dependencyDescriptor = new ClientModuleDependencyDescriptor(
-                clientModule,
-                parent,
-                moduleVersionMetaData,
-                moduleRevisionId,
-                clientModule.isForce(),
-                false,
-                clientModule.isTransitive());
-        addExcludesArtifactsAndDependencies(configuration, clientModule, dependencyDescriptor);
-        return dependencyDescriptor;
-    }
-
-    private ClientModule getClientModule(ModuleDependency dependency) {
-        return (ClientModule) dependency;
-    }
-
-    public boolean canConvert(ModuleDependency dependency) {
-        return dependency instanceof ClientModule;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleMetaDataFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleMetaDataFactory.java
deleted file mode 100644
index 7dd76ce..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleMetaDataFactory.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-import java.util.Set;
-
-public interface ClientModuleMetaDataFactory {
-    MutableModuleVersionMetaData createModuleDescriptor(ModuleRevisionId moduleRevisionId, Set<ModuleDependency> dependencies);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactory.java
deleted file mode 100644
index 05df71c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactory.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.Configuration;
-import org.apache.ivy.core.module.descriptor.DefaultArtifact;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-
-import java.util.Set;
-
-public class DefaultClientModuleMetaDataFactory implements ClientModuleMetaDataFactory {
-    // Because of bidirectional dependencies we need setter injection
-    private DependencyDescriptorFactory dependencyDescriptorFactory;
-
-    public MutableModuleVersionMetaData createModuleDescriptor(ModuleRevisionId moduleRevisionId, Set<ModuleDependency> dependencies) {
-        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(moduleRevisionId,
-                "release", null);
-        moduleDescriptor.addConfiguration(new Configuration(Dependency.DEFAULT_CONFIGURATION));
-        addDependencyDescriptors(moduleDescriptor, dependencies, dependencyDescriptorFactory);
-        moduleDescriptor.addArtifact(Dependency.DEFAULT_CONFIGURATION,
-                new DefaultArtifact(moduleRevisionId, null, moduleRevisionId.getName(), "jar", "jar"));
-        return new ModuleDescriptorAdapter(moduleDescriptor);
-    }
-
-    private void addDependencyDescriptors(DefaultModuleDescriptor moduleDescriptor, Set<ModuleDependency> dependencies,
-                                          DependencyDescriptorFactory dependencyDescriptorFactory) {
-        for (ModuleDependency dependency : dependencies) {
-            dependencyDescriptorFactory.addDependencyDescriptor(Dependency.DEFAULT_CONFIGURATION, moduleDescriptor,
-                    dependency);
-        }
-    }
-
-    public void setDependencyDescriptorFactory(DependencyDescriptorFactory dependencyDescriptorFactory) {
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
-    }
-}
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
deleted file mode 100644
index c50beff..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-
-import java.util.Collection;
-
-public class DefaultDependenciesToModuleDescriptorConverter implements DependenciesToModuleDescriptorConverter {
-    private DependencyDescriptorFactory dependencyDescriptorFactory;
-    private ExcludeRuleConverter excludeRuleConverter;
-
-    public DefaultDependenciesToModuleDescriptorConverter(DependencyDescriptorFactory dependencyDescriptorFactory,
-                                                          ExcludeRuleConverter excludeRuleConverter) {
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
-        this.excludeRuleConverter = excludeRuleConverter;
-    }
-
-    public void addDependencyDescriptors(DefaultModuleDescriptor moduleDescriptor, Collection<? extends Configuration> configurations) {
-        assert !configurations.isEmpty();
-        addDependencies(moduleDescriptor, configurations);
-        addExcludeRules(moduleDescriptor, configurations);
-    }
-
-    private void addDependencies(DefaultModuleDescriptor moduleDescriptor, Collection<? extends Configuration> configurations) {
-        for (Configuration configuration : configurations) {
-            for (ModuleDependency dependency : configuration.getDependencies().withType(ModuleDependency.class)) {
-                dependencyDescriptorFactory.addDependencyDescriptor(configuration.getName(), moduleDescriptor, dependency);
-            }
-        }
-    }
-
-    private void addExcludeRules(DefaultModuleDescriptor moduleDescriptor, Collection<? extends Configuration> configurations) {
-        for (Configuration configuration : configurations) {
-            for (ExcludeRule excludeRule : configuration.getExcludeRules()) {
-                org.apache.ivy.core.module.descriptor.ExcludeRule rule = excludeRuleConverter.createExcludeRule(
-                        configuration.getName(), excludeRule);
-                moduleDescriptor.addExcludeRule(rule);
-            }
-        }
-    }
-}
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
deleted file mode 100644
index 8ac55c4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.util.WrapUtil;
-
-import java.util.List;
-
-public class DefaultDependencyDescriptorFactory implements DependencyDescriptorFactory {
-    private List<IvyDependencyDescriptorFactory> dependencyDescriptorFactories;
-
-    public DefaultDependencyDescriptorFactory(IvyDependencyDescriptorFactory... dependencyDescriptorFactories) {
-        this.dependencyDescriptorFactories = WrapUtil.toList(dependencyDescriptorFactories);
-    }
-
-    public void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor,
-                                        ModuleDependency dependency) {
-        IvyDependencyDescriptorFactory factoryInternal = findFactoryForDependency(dependency);
-        EnhancedDependencyDescriptor dependencyDescriptor = factoryInternal.createDependencyDescriptor(configuration, dependency, moduleDescriptor);
-        moduleDescriptor.addDependency(dependencyDescriptor);
-    }
-
-    private IvyDependencyDescriptorFactory findFactoryForDependency(ModuleDependency dependency) {
-        for (IvyDependencyDescriptorFactory ivyDependencyDescriptorFactory : dependencyDescriptorFactories) {
-            if (ivyDependencyDescriptorFactory.canConvert(dependency)) {
-                return ivyDependencyDescriptorFactory;
-            }
-        }
-        throw new InvalidUserDataException("Can't map dependency of type: " + dependency.getClass());
-    }
-}
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
deleted file mode 100644
index 91ce740..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-
-import java.util.Collection;
-
-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
deleted file mode 100644
index 5073472..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
+++ /dev/null
@@ -1,23 +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.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.ModuleDependency;
-
-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/EnhancedDependencyDescriptor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/EnhancedDependencyDescriptor.java
deleted file mode 100644
index 372cb48..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/EnhancedDependencyDescriptor.java
+++ /dev/null
@@ -1,34 +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.moduleconverter.dependencies;
-
-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.artifacts.ModuleDependency;
-
-public class EnhancedDependencyDescriptor extends DefaultDependencyDescriptor {
-    private final ModuleDependency moduleDependency;
-
-    public EnhancedDependencyDescriptor(ModuleDependency moduleDependency, ModuleDescriptor md, ModuleRevisionId mrid, boolean force, boolean changing, boolean transitive) {
-        super(md, mrid, force, changing, transitive);
-        this.moduleDependency = moduleDependency;
-    }
-
-    public ModuleDependency getModuleDependency() {
-        return moduleDependency;
-    }
-}
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
deleted file mode 100644
index 0cffb8b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.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.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ExternalModuleDependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-
-public class ExternalModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
-    public ExternalModuleIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        super(excludeRuleConverter);
-    }
-
-    private ModuleRevisionId createModuleRevisionId(ModuleDependency dependency) {
-        return IvyUtil.createModuleRevisionId(dependency);
-    }
-
-    public EnhancedDependencyDescriptor createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor parent) {
-        ModuleRevisionId moduleRevisionId = createModuleRevisionId(dependency);
-        EnhancedDependencyDescriptor dependencyDescriptor = new EnhancedDependencyDescriptor(
-                dependency,
-                parent,
-                moduleRevisionId,
-                getExternalModuleDependency(dependency).isForce(),
-                getExternalModuleDependency(dependency).isChanging(),
-                getExternalModuleDependency(dependency).isTransitive());
-        addExcludesArtifactsAndDependencies(configuration, getExternalModuleDependency(dependency), dependencyDescriptor);
-        return dependencyDescriptor;
-    }
-
-    private ExternalModuleDependency getExternalModuleDependency(ModuleDependency dependency) {
-        return (ExternalModuleDependency) dependency;
-    }
-
-    public boolean canConvert(ModuleDependency dependency) {
-        return dependency instanceof ExternalModuleDependency;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java
deleted file mode 100644
index a8ef3dd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.artifacts.ModuleDependency;
-
-public interface IvyDependencyDescriptorFactory {
-    EnhancedDependencyDescriptor createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor moduleDescriptor);
-
-    boolean canConvert(ModuleDependency dependency);
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptor.java
deleted file mode 100644
index 10b070f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptor.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.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.project.ProjectInternal;
-
-public class ProjectDependencyDescriptor extends EnhancedDependencyDescriptor {
-    public ProjectDependencyDescriptor(ProjectDependency projectDependency, ModuleDescriptor md, ModuleRevisionId mrid, boolean force, boolean changing, boolean transitive) {
-        super(projectDependency, md, mrid, force, changing, transitive);
-    }
-
-    public ProjectInternal getTargetProject() {
-        return (ProjectInternal) ((ProjectDependency) getModuleDependency()).getDependencyProject();
-    }
-}
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
deleted file mode 100644
index fb16663..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,51 +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.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-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;
-
-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);
-        ProjectDependencyDescriptor dependencyDescriptor = new ProjectDependencyDescriptor(projectDependency, parent, moduleRevisionId, false, false, dependency.isTransitive());
-        addExcludesArtifactsAndDependencies(configuration, dependency, dependencyDescriptor);
-        return dependencyDescriptor;
-    }
-
-    public boolean canConvert(ModuleDependency dependency) {
-        return dependency instanceof ProjectDependency;
-    }
-
-    private ModuleRevisionId createModuleRevisionId(ModuleDependency dependency) {
-        ProjectDependency projectDependency = (ProjectDependency) dependency;
-        Module module = ((ProjectInternal) projectDependency.getDependencyProject()).getModule();
-        return IvyUtil.createModuleRevisionId(module);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.java
deleted file mode 100644
index 58e04c9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.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.projectmodule;
-
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
-import org.gradle.api.internal.artifacts.metadata.LocalComponentMetaData;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectRegistry;
-
-public class DefaultProjectComponentRegistry implements ProjectComponentRegistry {
-    private final LocalComponentFactory localComponentFactory;
-    private final ProjectRegistry<ProjectInternal> projectRegistry;
-
-    public DefaultProjectComponentRegistry(LocalComponentFactory localComponentFactory, ProjectRegistry<ProjectInternal> projectRegistry) {
-        this.localComponentFactory = localComponentFactory;
-        this.projectRegistry = projectRegistry;
-    }
-
-    public LocalComponentMetaData getProject(String projectPath) {
-        ProjectInternal project = projectRegistry.getProject(projectPath);
-        return localComponentFactory.convert(project.getConfigurations(), project.getModule());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java
deleted file mode 100644
index 8f3d235..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectmodule;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-
-public class DefaultProjectPublication implements ProjectPublication {
-    private final ModuleVersionIdentifier id;
-
-    public DefaultProjectPublication(ModuleVersionIdentifier id) {
-        this.id = id;
-    }
-
-    public ModuleVersionIdentifier getId() {
-        return id;
-    }
-
-    public boolean equals(Object other) {
-        if (!(other instanceof ProjectPublication)) { return false; }
-        return id.equals(((ProjectPublication) other).getId());
-    }
-
-    public int hashCode() {
-        return id.hashCode();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
deleted file mode 100644
index e5cf966..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectmodule;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.LocalArtifactMetaData;
-
-import java.util.Set;
-
-public class ProjectArtifactResolver implements ArtifactResolver {
-    private final ArtifactResolver delegate;
-
-    public ProjectArtifactResolver(ArtifactResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        if (isProjectModule(component.getComponentId())) {
-            if (context instanceof ConfigurationResolveContext) {
-                String configurationName = ((ConfigurationResolveContext) context).getConfigurationName();
-                Set<ComponentArtifactMetaData> artifacts = component.getConfiguration(configurationName).getArtifacts();
-                result.resolved(artifacts);
-                return;
-            }
-            throw new UnsupportedOperationException(String.format("Resolving %s for project modules is not yet supported", context.getDescription()));
-        }
-
-        delegate.resolveModuleArtifacts(component, context, result);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        if (isProjectModule(artifact.getComponentId())) {
-            LocalArtifactMetaData localArtifact = (LocalArtifactMetaData) artifact;
-            if (localArtifact.getFile() != null) {
-                result.resolved(localArtifact.getFile());
-            } else {
-                result.notFound(artifact.getId());
-            }
-        } else {
-            delegate.resolveArtifact(artifact, moduleSource, result);
-        }
-    }
-
-    private boolean isProjectModule(ComponentIdentifier componentId) {
-        return componentId instanceof ProjectComponentIdentifier;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.java
deleted file mode 100644
index 2a01fcf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.java
+++ /dev/null
@@ -1,22 +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.projectmodule;
-
-import org.gradle.api.internal.artifacts.metadata.LocalComponentMetaData;
-
-public interface ProjectComponentRegistry {
-    LocalComponentMetaData getProject(String projectPath);
-}
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
deleted file mode 100644
index 8bb755c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
+++ /dev/null
@@ -1,57 +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.projectmodule;
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableComponentResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver;
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleToModuleVersionResolver;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.gradle.api.internal.artifacts.metadata.LocalComponentMetaData;
-
-import java.util.Set;
-
-public class ProjectDependencyResolver implements DependencyToModuleVersionResolver, ModuleToModuleVersionResolver {
-    private final ProjectComponentRegistry projectComponentRegistry;
-    private final DependencyToModuleVersionResolver delegate;
-    private final LocalComponentFactory localComponentFactory;
-
-    public ProjectDependencyResolver(ProjectComponentRegistry projectComponentRegistry, LocalComponentFactory localComponentFactory, DependencyToModuleVersionResolver delegate) {
-        this.projectComponentRegistry = projectComponentRegistry;
-        this.delegate = delegate;
-        this.localComponentFactory = localComponentFactory;
-    }
-
-    public void resolve(DependencyMetaData dependency, BuildableComponentResolveResult result) {
-        DependencyDescriptor descriptor = dependency.getDescriptor();
-        if (descriptor instanceof ProjectDependencyDescriptor) {
-            ProjectDependencyDescriptor desc = (ProjectDependencyDescriptor) descriptor;
-            LocalComponentMetaData componentMetaData = projectComponentRegistry.getProject(desc.getTargetProject().getPath());
-            result.resolved(componentMetaData.toResolveMetaData());
-        } else {
-            delegate.resolve(dependency, result);
-        }
-    }
-
-    public void resolve(ModuleInternal module, Set<? extends Configuration> configurations, BuildableComponentResolveResult result) {
-        LocalComponentMetaData componentMetaData = localComponentFactory.convert(configurations, module);
-        result.resolved(componentMetaData.toResolveMetaData());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java
deleted file mode 100644
index df5082a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java
+++ /dev/null
@@ -1,254 +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.resolutionstrategy;
-
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.artifacts.cache.*;
-import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-public class DefaultCachePolicy implements CachePolicy, ResolutionRules {
-    private static final int SECONDS_IN_DAY = 24 * 60 * 60;
-
-    final List<Action<? super DependencyResolutionControl>> dependencyCacheRules;
-    final List<Action<? super ModuleResolutionControl>> moduleCacheRules;
-    final List<Action<? super ArtifactResolutionControl>> artifactCacheRules;
-
-    public DefaultCachePolicy() {
-        this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>();
-        this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>();
-        this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>();
-
-        cacheDynamicVersionsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
-        cacheChangingModulesFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
-        cacheMissingModulesAndArtifactsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
-    }
-
-    DefaultCachePolicy(DefaultCachePolicy policy) {
-        this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>(policy.dependencyCacheRules);
-        this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>(policy.moduleCacheRules);
-        this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>(policy.artifactCacheRules);
-    }
-
-    public void eachDependency(Action<? super DependencyResolutionControl> rule) {
-        dependencyCacheRules.add(0, rule);
-    }
-
-    public void eachModule(Action<? super ModuleResolutionControl> rule) {
-        moduleCacheRules.add(0, rule);
-    }
-
-    public void eachArtifact(Action<? super ArtifactResolutionControl> rule) {
-        artifactCacheRules.add(0, rule);
-    }
-
-    public void cacheDynamicVersionsFor(final int value, final TimeUnit unit) {
-        eachDependency(new Action<DependencyResolutionControl>() {
-            public void execute(DependencyResolutionControl dependencyResolutionControl) {
-                dependencyResolutionControl.cacheFor(value, unit);
-            }
-        });
-    }
-
-    public void cacheChangingModulesFor(final int value, final TimeUnit units) {
-        eachModule(new Action<ModuleResolutionControl>() {
-            public void execute(ModuleResolutionControl moduleResolutionControl) {
-                if (moduleResolutionControl.isChanging()) {
-                    moduleResolutionControl.cacheFor(value, units);
-                }
-            }
-        });
-        eachArtifact(new Action<ArtifactResolutionControl>() {
-            public void execute(ArtifactResolutionControl artifactResolutionControl) {
-                if (artifactResolutionControl.belongsToChangingModule()) {
-                    artifactResolutionControl.cacheFor(value, units);
-                }
-            }
-        });
-    }
-
-    private void cacheMissingModulesAndArtifactsFor(final int value, final TimeUnit units) {
-        eachModule(new Action<ModuleResolutionControl>() {
-            public void execute(ModuleResolutionControl moduleResolutionControl) {
-                if (moduleResolutionControl.getCachedResult() == null) {
-                    moduleResolutionControl.cacheFor(value, units);
-                }
-            }
-        });
-        eachArtifact(new Action<ArtifactResolutionControl>() {
-            public void execute(ArtifactResolutionControl artifactResolutionControl) {
-                if (artifactResolutionControl.getCachedResult() == null) {
-                    artifactResolutionControl.cacheFor(value, units);
-                }
-            }
-        });
-    }
-
-    public boolean mustRefreshVersionList(final ModuleIdentifier moduleIdentifier, Set<ModuleVersionIdentifier> matchingVersions, long ageMillis) {
-        CachedDependencyResolutionControl dependencyResolutionControl = new CachedDependencyResolutionControl(moduleIdentifier, matchingVersions, ageMillis);
-
-        for (Action<? super DependencyResolutionControl> rule : dependencyCacheRules) {
-            rule.execute(dependencyResolutionControl);
-            if (dependencyResolutionControl.ruleMatch()) {
-                return dependencyResolutionControl.mustCheck();
-            }
-        }
-
-        return false;
-    }
-
-    public boolean mustRefreshModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion resolvedModuleVersion, ModuleRevisionId moduleRevisionId, final long ageMillis) {
-        return mustRefreshModule(moduleVersionId, resolvedModuleVersion, ageMillis, false);
-    }
-
-    public boolean mustRefreshChangingModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion resolvedModuleVersion, long ageMillis) {
-        return mustRefreshModule(moduleVersionId, resolvedModuleVersion, ageMillis, true);
-    }
-
-    private boolean mustRefreshModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion version, long ageMillis, boolean changingModule) {
-        CachedModuleResolutionControl moduleResolutionControl = new CachedModuleResolutionControl(moduleVersionId, version, changingModule, ageMillis);
-
-        for (Action<? super ModuleResolutionControl> rule : moduleCacheRules) {
-            rule.execute(moduleResolutionControl);
-            if (moduleResolutionControl.ruleMatch()) {
-                return moduleResolutionControl.mustCheck();
-            }
-        }
-
-        return false;
-    }
-
-    public boolean mustRefreshModuleArtifacts(ModuleVersionIdentifier moduleVersionId, Set<ArtifactIdentifier> artifacts,
-                                              long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
-        if (belongsToChangingModule && !moduleDescriptorInSync) {
-            return true;
-        }
-        return mustRefreshModule(moduleVersionId, new DefaultResolvedModuleVersion(moduleVersionId), ageMillis, belongsToChangingModule);
-    }
-
-    public boolean mustRefreshArtifact(ArtifactIdentifier artifactIdentifier, File cachedArtifactFile, long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
-        CachedArtifactResolutionControl artifactResolutionControl = new CachedArtifactResolutionControl(artifactIdentifier, cachedArtifactFile, ageMillis, belongsToChangingModule);
-        if(belongsToChangingModule && !moduleDescriptorInSync){
-            return true;
-        }
-        for (Action<? super ArtifactResolutionControl> rule : artifactCacheRules) {
-            rule.execute(artifactResolutionControl);
-            if (artifactResolutionControl.ruleMatch()) {
-                return artifactResolutionControl.mustCheck();
-            }
-        }
-        return false;
-    }
-
-    DefaultCachePolicy copy() {
-        return new DefaultCachePolicy(this);
-    }
-
-    private abstract static class AbstractResolutionControl<A, B> implements ResolutionControl<A, B> {
-        private final A request;
-        private final B cachedResult;
-        private final long ageMillis;
-        private boolean ruleMatch;
-        private boolean mustCheck;
-
-        private AbstractResolutionControl(A request, B cachedResult, long ageMillis) {
-            this.request = request;
-            this.cachedResult = cachedResult;
-            this.ageMillis = ageMillis;
-        }
-
-        public A getRequest() {
-            return request;
-        }
-
-        public B getCachedResult() {
-            return cachedResult;
-        }
-
-        public void cacheFor(int value, TimeUnit units) {
-            long timeoutMillis = TimeUnit.MILLISECONDS.convert(value, units);
-            if (ageMillis <= timeoutMillis) {
-                setMustCheck(false);
-            } else {
-                setMustCheck(true);
-            }
-        }
-
-        public void useCachedResult() {
-            setMustCheck(false);
-        }
-
-        public void refresh() {
-            setMustCheck(true);
-        }
-
-        private void setMustCheck(boolean val) {
-            ruleMatch = true;
-            mustCheck = val;
-        }
-
-        public boolean ruleMatch() {
-            return ruleMatch;
-        }
-
-        public boolean mustCheck() {
-            return mustCheck;
-        }
-    }
-
-    private class CachedDependencyResolutionControl extends AbstractResolutionControl<ModuleIdentifier, Set<ModuleVersionIdentifier>> implements DependencyResolutionControl {
-        private CachedDependencyResolutionControl(ModuleIdentifier request, Set<ModuleVersionIdentifier> result, long ageMillis) {
-            super(request, result, ageMillis);
-        }
-    }
-
-    private class CachedModuleResolutionControl extends AbstractResolutionControl<ModuleVersionIdentifier, ResolvedModuleVersion> implements ModuleResolutionControl {
-        private final boolean changing;
-
-        private CachedModuleResolutionControl(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion cachedVersion, boolean changing, long ageMillis) {
-            super(moduleVersionId, cachedVersion, ageMillis);
-            this.changing = changing;
-        }
-
-        public boolean isChanging() {
-            return changing;
-        }
-    }
-
-    private class CachedArtifactResolutionControl extends AbstractResolutionControl<ArtifactIdentifier, File> implements ArtifactResolutionControl {
-        private final boolean belongsToChangingModule;
-
-        private CachedArtifactResolutionControl(ArtifactIdentifier artifactIdentifier, File cachedResult, long ageMillis, boolean belongsToChangingModule) {
-            super(artifactIdentifier, cachedResult, ageMillis);
-            this.belongsToChangingModule = belongsToChangingModule;
-        }
-
-        public boolean belongsToChangingModule() {
-            return belongsToChangingModule;
-        }
-    }
-}
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
deleted file mode 100644
index ad5a7ab..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
+++ /dev/null
@@ -1,127 +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.resolutionstrategy;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ConflictResolution;
-import org.gradle.api.artifacts.DependencyResolveDetails;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.ResolutionStrategy;
-import org.gradle.api.artifacts.cache.ResolutionRules;
-import org.gradle.internal.Actions;
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
-import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
-import org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers;
-import org.gradle.internal.typeconversion.NormalizedTimeUnit;
-import org.gradle.internal.typeconversion.TimeUnitsParser;
-
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static org.gradle.util.GUtil.flattenElements;
-
-public class DefaultResolutionStrategy implements ResolutionStrategyInternal {
-
-    private Set<ModuleVersionSelector> forcedModules = new LinkedHashSet<ModuleVersionSelector>();
-    private ConflictResolution conflictResolution = new LatestConflictResolution();
-
-    final Set<Action<? super DependencyResolveDetails>> dependencyResolveRules;
-    private final DefaultCachePolicy cachePolicy;
-
-    public DefaultResolutionStrategy() {
-        this(new DefaultCachePolicy(), new LinkedHashSet<Action<? super DependencyResolveDetails>>());
-    }
-
-    DefaultResolutionStrategy(DefaultCachePolicy cachePolicy, Set<Action<? super DependencyResolveDetails>> dependencyResolveRules) {
-        this.cachePolicy = cachePolicy;
-        this.dependencyResolveRules = dependencyResolveRules;
-    }
-
-    public Set<ModuleVersionSelector> getForcedModules() {
-        return forcedModules;
-    }
-
-    public ResolutionStrategy failOnVersionConflict() {
-        this.conflictResolution = new StrictConflictResolution();
-        return this;
-    }
-
-    public ConflictResolution getConflictResolution() {
-        return this.conflictResolution;
-    }
-
-    public ResolutionRules getResolutionRules() {
-        return cachePolicy;
-    }
-
-    public DefaultResolutionStrategy force(Object... moduleVersionSelectorNotations) {
-        Set<ModuleVersionSelector> modules = ModuleVersionSelectorParsers.multiParser().parseNotation(moduleVersionSelectorNotations);
-        this.forcedModules.addAll(modules);
-        return this;
-    }
-
-    public ResolutionStrategy eachDependency(Action<? super DependencyResolveDetails> rule) {
-        dependencyResolveRules.add(rule);
-        return this;
-    }
-
-    public Action<DependencyResolveDetailsInternal> getDependencyResolveRule() {
-        Collection allRules = flattenElements(new ModuleForcingResolveRule(forcedModules), dependencyResolveRules);
-        return Actions.composite(allRules);
-    }
-
-    public DefaultResolutionStrategy setForcedModules(Object ... moduleVersionSelectorNotations) {
-        Set<ModuleVersionSelector> forcedModules = ModuleVersionSelectorParsers.multiParser().parseNotation(moduleVersionSelectorNotations);
-        this.forcedModules = forcedModules;
-        return this;
-    }
-
-    public DefaultCachePolicy getCachePolicy() {
-        return cachePolicy;
-    }
-
-    public void cacheDynamicVersionsFor(int value, String units) {
-        NormalizedTimeUnit timeUnit = new TimeUnitsParser().parseNotation(units, value);
-        cacheDynamicVersionsFor(timeUnit.getValue(), timeUnit.getTimeUnit());
-    }
-
-    public void cacheDynamicVersionsFor(int value, TimeUnit units) {
-        this.cachePolicy.cacheDynamicVersionsFor(value, units);
-    }
-
-    public void cacheChangingModulesFor(int value, String units) {
-        NormalizedTimeUnit timeUnit = new TimeUnitsParser().parseNotation(units, value);
-        cacheChangingModulesFor(timeUnit.getValue(), timeUnit.getTimeUnit());
-    }
-
-    public void cacheChangingModulesFor(int value, TimeUnit units) {
-        this.cachePolicy.cacheChangingModulesFor(value, units);
-    }
-
-    public DefaultResolutionStrategy copy() {
-        DefaultResolutionStrategy out = new DefaultResolutionStrategy(cachePolicy.copy(),
-                new LinkedHashSet<Action<? super DependencyResolveDetails>>(dependencyResolveRules));
-
-        if (conflictResolution instanceof StrictConflictResolution) {
-            out.failOnVersionConflict();
-        }
-        out.setForcedModules(getForcedModules());
-        return out;
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index a227620..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
+++ /dev/null
@@ -1,54 +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.resolutionstrategy;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-public class ModuleForcingResolveRule implements Action<DependencyResolveDetailsInternal> {
-
-    private final Map<ModuleIdentifier, String> forcedModules;
-
-    public ModuleForcingResolveRule(Collection<? extends ModuleVersionSelector> forcedModules) {
-        if (!forcedModules.isEmpty()) {
-            this.forcedModules = new HashMap<ModuleIdentifier, String>();
-            for (ModuleVersionSelector module : forcedModules) {
-                this.forcedModules.put(new DefaultModuleIdentifier(module.getGroup(), module.getName()), module.getVersion());
-            }
-        } else {
-            this.forcedModules = null;
-        }
-    }
-
-    public void execute(DependencyResolveDetailsInternal details) {
-        if (forcedModules == null) {
-            return;
-        }
-        ModuleIdentifier key = new DefaultModuleIdentifier(details.getRequested().getGroup(), details.getRequested().getName());
-        if (forcedModules.containsKey(key)) {
-            details.useVersion(forcedModules.get(key), VersionSelectionReasons.FORCED);
-        }
-    }
-}
\ 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
deleted file mode 100755
index f5f1bd9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
+++ /dev/null
@@ -1,130 +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.resolveengine;
-
-import org.apache.ivy.Ivy;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.artifacts.result.ResolvedComponentResult;
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LazyDependencyToModuleResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectComponentRegistry;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.StrictConflictResolution;
-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.TransientConfigurationResultsBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.StreamingResolutionResultBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.StoreSet;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.internal.cache.BinaryStore;
-import org.gradle.api.internal.cache.Store;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-public class DefaultDependencyResolver implements ArtifactDependencyResolver {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDependencyResolver.class);
-    private final LocalComponentFactory localComponentFactory;
-    private final ResolveIvyFactory ivyFactory;
-    private final ProjectComponentRegistry projectComponentRegistry;
-    private final CacheLockingManager cacheLockingManager;
-    private final IvyContextManager ivyContextManager;
-    private final ResolutionResultsStoreFactory storeFactory;
-    private final VersionMatcher versionMatcher;
-    private final LatestStrategy latestStrategy;
-
-    public DefaultDependencyResolver(ResolveIvyFactory ivyFactory, LocalComponentFactory localComponentFactory,
-                                     ProjectComponentRegistry projectComponentRegistry, CacheLockingManager cacheLockingManager, IvyContextManager ivyContextManager,
-                                     ResolutionResultsStoreFactory storeFactory, VersionMatcher versionMatcher, LatestStrategy latestStrategy) {
-        this.ivyFactory = ivyFactory;
-        this.localComponentFactory = localComponentFactory;
-        this.projectComponentRegistry = projectComponentRegistry;
-        this.cacheLockingManager = cacheLockingManager;
-        this.ivyContextManager = ivyContextManager;
-        this.storeFactory = storeFactory;
-        this.versionMatcher = versionMatcher;
-        this.latestStrategy = latestStrategy;
-    }
-
-    public void resolve(final ConfigurationInternal configuration,
-                        final List<? extends ResolutionAwareRepository> repositories,
-                        final ModuleMetadataProcessor metadataProcessor,
-                        final ResolverResults results) throws ResolveException {
-        LOGGER.debug("Resolving {}", configuration);
-        ivyContextManager.withIvy(new Action<Ivy>() {
-            public void execute(Ivy ivy) {
-                RepositoryChain repositoryChain = ivyFactory.create(configuration, repositories, metadataProcessor);
-
-                DependencyToModuleVersionResolver dependencyResolver = repositoryChain.getDependencyResolver();
-                dependencyResolver = new ClientModuleResolver(dependencyResolver);
-                ProjectDependencyResolver projectDependencyResolver = new ProjectDependencyResolver(projectComponentRegistry, localComponentFactory, dependencyResolver);
-                dependencyResolver = projectDependencyResolver;
-                DependencyToModuleVersionIdResolver idResolver = new LazyDependencyToModuleResolver(dependencyResolver, versionMatcher);
-                idResolver = new VersionForcingDependencyToModuleResolver(idResolver, configuration.getResolutionStrategy().getDependencyResolveRule());
-
-                ArtifactResolver artifactResolver = createArtifactResolver(repositoryChain);
-
-                ModuleConflictResolver conflictResolver;
-                if (configuration.getResolutionStrategy().getConflictResolution() instanceof StrictConflictResolution) {
-                    conflictResolver = new StrictConflictResolver();
-                } else {
-                    conflictResolver = new LatestModuleConflictResolver(latestStrategy);
-                }
-                conflictResolver = new VersionSelectionReasonResolver(conflictResolver);
-
-                DependencyGraphBuilder builder = new DependencyGraphBuilder(idResolver, projectDependencyResolver, artifactResolver, conflictResolver, new DefaultDependencyToConfigurationResolver());
-
-                StoreSet stores = storeFactory.createStoreSet();
-
-                BinaryStore newModelStore = stores.nextBinaryStore();
-                Store<ResolvedComponentResult> newModelCache = stores.oldModelStore();
-                ResolutionResultBuilder newModelBuilder = new StreamingResolutionResultBuilder(newModelStore, newModelCache);
-
-                BinaryStore oldModelStore = stores.nextBinaryStore();
-                Store<TransientConfigurationResults> oldModelCache = stores.newModelStore();
-                TransientConfigurationResultsBuilder oldTransientModelBuilder = new TransientConfigurationResultsBuilder(oldModelStore, oldModelCache);
-                DefaultResolvedConfigurationBuilder oldModelBuilder = new DefaultResolvedConfigurationBuilder(oldTransientModelBuilder);
-
-                builder.resolve(configuration, newModelBuilder, oldModelBuilder);
-                DefaultLenientConfiguration result = new DefaultLenientConfiguration(configuration, oldModelBuilder, cacheLockingManager);
-                results.resolved(new DefaultResolvedConfiguration(result), newModelBuilder.complete());
-            }
-        });
-    }
-
-    private ArtifactResolver createArtifactResolver(RepositoryChain repositoryChain) {
-        ArtifactResolver artifactResolver = repositoryChain.getArtifactResolver();
-        artifactResolver = new ProjectArtifactResolver(artifactResolver);
-        artifactResolver = new ContextualArtifactResolver(cacheLockingManager, ivyContextManager, artifactResolver);
-        artifactResolver = new ErrorHandlingArtifactResolver(artifactResolver);
-        return artifactResolver;
-    }
-}
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
deleted file mode 100644
index 16b1b4b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java
+++ /dev/null
@@ -1,81 +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.resolveengine;
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.ConfigurationMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-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, ComponentMetaData targetComponent) {
-        // TODO - resolve directly to config meta data
-        ModuleDescriptor targetDescriptor = targetComponent.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.getComponent().getId(), fromConfiguration.getName(),
-                        targetConfigurationName, targetComponent.getId()));
-            }
-            ConfigurationMetaData targetConfiguration = targetComponent.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
deleted file mode 100644
index d049cd5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilder.java
+++ /dev/null
@@ -1,985 +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.resolveengine;
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleId;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-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.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.ResolutionResultBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.ConfigurationMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-public class DependencyGraphBuilder {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DependencyGraphBuilder.class);
-    private final DependencyToModuleVersionIdResolver dependencyResolver;
-    private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
-    private final InternalConflictResolver conflictResolver;
-    private final ModuleToModuleVersionResolver moduleResolver;
-    private final ArtifactResolver artifactResolver;
-
-    public DependencyGraphBuilder(DependencyToModuleVersionIdResolver dependencyResolver,
-                                  ModuleToModuleVersionResolver moduleResolver,
-                                  ArtifactResolver artifactResolver,
-                                  ModuleConflictResolver conflictResolver,
-                                  DependencyToConfigurationResolver dependencyToConfigurationResolver) {
-        this.dependencyResolver = dependencyResolver;
-        this.moduleResolver = moduleResolver;
-        this.artifactResolver = artifactResolver;
-        this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
-        this.conflictResolver = new InternalConflictResolver(conflictResolver);
-    }
-
-    public void resolve(ConfigurationInternal configuration,
-                        ResolutionResultBuilder newModelBuilder,
-                        ResolvedConfigurationBuilder oldModelBuilder) throws ResolveException {
-        DefaultBuildableComponentResolveResult rootModule = new DefaultBuildableComponentResolveResult();
-        moduleResolver.resolve(configuration.getModule(), configuration.getAll(), rootModule);
-
-        ResolveState resolveState = new ResolveState(rootModule, configuration.getName(), dependencyResolver, dependencyToConfigurationResolver, artifactResolver, oldModelBuilder);
-        traverseGraph(resolveState);
-
-        assembleResult(resolveState, oldModelBuilder, newModelBuilder);
-    }
-
-    /**
-     * Traverses the dependency graph, resolving conflicts and building the paths from the root configuration.
-     */
-    private void traverseGraph(ResolveState resolveState) {
-        Set<ModuleIdentifier> conflicts = new LinkedHashSet<ModuleIdentifier>();
-
-        resolveState.onMoreSelected(resolveState.root);
-
-        List<DependencyEdge> dependencies = new ArrayList<DependencyEdge>();
-        while (resolveState.peek() != null || !conflicts.isEmpty()) {
-            if (resolveState.peek() != null) {
-                ConfigurationNode node = resolveState.pop();
-                LOGGER.debug("Visiting configuration {}.", node);
-
-                // Calculate the outgoing edges of this configuration
-                dependencies.clear();
-                node.visitOutgoingDependencies(dependencies);
-
-                for (DependencyEdge dependency : dependencies) {
-                    LOGGER.debug("Visiting dependency {}", dependency);
-
-                    // Resolve dependency to a particular revision
-                    ModuleVersionResolveState moduleRevision = dependency.resolveModuleRevisionId();
-                    if (moduleRevision == null) {
-                        // Failed to resolve.
-                        continue;
-                    }
-                    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<ModuleVersionResolveState> versions = module.getVersions();
-                        if (versions.size() == 1) {
-                            // First version of this module. Select it for now
-                            LOGGER.debug("Selecting new module version {}", moduleRevision);
-                            module.select(moduleRevision);
-                        } else {
-                            // Not the first version of this module. We have a new conflict
-                            LOGGER.debug("Found new conflicting module version {}", moduleRevision);
-                            conflicts.add(moduleId);
-
-                            // 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
-                            ModuleVersionResolveState previouslySelected = module.clearSelection();
-                            if (previouslySelected != null) {
-                                for (ConfigurationNode configuration : previouslySelected.configurations) {
-                                    configuration.deselect();
-                                }
-                            }
-                        }
-                    }
-
-                    dependency.attachToTargetConfigurations();
-                }
-            } else {
-                // We have some batched up conflicts. Resolve the first, and continue traversing the graph
-                ModuleIdentifier moduleId = conflicts.iterator().next();
-                conflicts.remove(moduleId);
-                ModuleResolveState module = resolveState.getModule(moduleId);
-                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
-                // matching selected configuration. For the select configuration, this mean traversing its dependencies.
-                module.restart(selected);
-            }
-        }
-    }
-
-    /**
-     * Populates the result from the graph traversal state.
-     */
-    private void assembleResult(ResolveState resolveState, ResolvedConfigurationBuilder oldModelBuilder, ResolutionResultBuilder newModelBuilder) {
-        FailureState failureState = new FailureState(resolveState.root);
-        newModelBuilder.start(resolveState.root.moduleRevision.id, resolveState.root.metaData.getComponent().getComponentId());
-
-        // Visit the nodes
-        for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
-            if (resolvedConfiguration.isSelected()) {
-                resolvedConfiguration.validate();
-                oldModelBuilder.newResolvedDependency(resolvedConfiguration.id);
-                resolvedConfiguration.collectFailures(failureState);
-                newModelBuilder.resolvedModuleVersion(resolvedConfiguration.moduleRevision);
-            }
-        }
-        // Visit the edges
-        for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
-            if (resolvedConfiguration.isSelected()) {
-                resolvedConfiguration.attachToParents(oldModelBuilder);
-                newModelBuilder.resolvedConfiguration(resolvedConfiguration.toId(), resolvedConfiguration.outgoingEdges);
-            }
-        }
-        failureState.attachFailures(oldModelBuilder);
-        oldModelBuilder.done(resolveState.root.id);
-    }
-
-    private static class FailureState {
-        final Map<ModuleVersionSelector, BrokenDependency> failuresByRevisionId = new LinkedHashMap<ModuleVersionSelector, BrokenDependency>();
-        final ConfigurationNode root;
-
-        private FailureState(ConfigurationNode root) {
-            this.root = root;
-        }
-
-        public void attachFailures(ResolvedConfigurationBuilder result) {
-            for (Map.Entry<ModuleVersionSelector, BrokenDependency> entry : failuresByRevisionId.entrySet()) {
-                Collection<List<ModuleVersionIdentifier>> paths = calculatePaths(entry.getValue());
-                result.addUnresolvedDependency(new DefaultUnresolvedDependency(entry.getKey(), entry.getValue().failure.withIncomingPaths(paths)));
-            }
-        }
-
-        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<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<ModuleVersionResolveState> directDependees = new LinkedHashSet<ModuleVersionResolveState>();
-            for (ConfigurationNode node : brokenDependency.requiredBy) {
-                directDependees.add(node.moduleRevision);
-            }
-
-            Set<ModuleVersionResolveState> seen = new HashSet<ModuleVersionResolveState>();
-            LinkedList<ModuleVersionResolveState> queue = new LinkedList<ModuleVersionResolveState>();
-            queue.addAll(directDependees);
-            while (!queue.isEmpty()) {
-                ModuleVersionResolveState version = queue.getFirst();
-                if (version == root.moduleRevision) {
-                    queue.removeFirst();
-                } else if (seen.add(version)) {
-                    for (ConfigurationNode configuration : version.configurations) {
-                        for (DependencyEdge dependencyEdge : configuration.incomingEdges) {
-                            queue.add(0, dependencyEdge.from.moduleRevision);
-                        }
-                    }
-                } else {
-                    queue.remove();
-                    List<ModuleVersionIdentifier> shortest = null;
-                    for (ConfigurationNode configuration : version.configurations) {
-                        for (DependencyEdge dependencyEdge : configuration.incomingEdges) {
-                            List<ModuleVersionIdentifier> candidate = shortestPaths.get(dependencyEdge.from.moduleRevision);
-                            if (candidate == null) {
-                                continue;
-                            }
-                            if (shortest == null) {
-                                shortest = candidate;
-                            } else if (shortest.size() > candidate.size()) {
-                                shortest = candidate;
-                            }
-                        }
-                    }
-                    if (shortest == null) {
-                        continue;
-                    }
-                    List<ModuleVersionIdentifier> path = new ArrayList<ModuleVersionIdentifier>();
-                    path.addAll(shortest);
-                    path.add(version.id);
-                    shortestPaths.put(version, path);
-                }
-            }
-
-            List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
-            for (ModuleVersionResolveState version : directDependees) {
-                List<ModuleVersionIdentifier> path = shortestPaths.get(version);
-                paths.add(path);
-            }
-            return paths;
-        }
-
-        public void addUnresolvedDependency(DependencyEdge dependency, ModuleVersionSelector requested, ModuleVersionResolveException failure) {
-            BrokenDependency breakage = failuresByRevisionId.get(requested);
-            if (breakage == null) {
-                breakage = new BrokenDependency(failure);
-                failuresByRevisionId.put(requested, breakage);
-            }
-            breakage.requiredBy.add(dependency.from);
-        }
-        
-        private static class BrokenDependency {
-            final ModuleVersionResolveException failure;
-            final List<ConfigurationNode> requiredBy = new ArrayList<ConfigurationNode>();
-
-            private BrokenDependency(ModuleVersionResolveException failure) {
-                this.failure = failure;
-            }
-        }
-    }
-
-    /**
-     * 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 ResolveState resolveState;
-        private final ModuleVersionSpec selectorSpec;
-        private final Set<ConfigurationNode> targetConfigurations = new LinkedHashSet<ConfigurationNode>();
-        private final ModuleVersionSelectorResolveState selector;
-        private ModuleVersionResolveState targetModuleRevision;
-
-        public DependencyEdge(ConfigurationNode from, DependencyMetaData dependencyMetaData, ModuleVersionSpec selectorSpec, ResolveState resolveState) {
-            this.from = from;
-            this.dependencyMetaData = dependencyMetaData;
-            this.dependencyDescriptor = dependencyMetaData.getDescriptor();
-            this.selectorSpec = selectorSpec;
-            this.resolveState = resolveState;
-            selector = resolveState.getSelector(dependencyMetaData);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("%s -> %s(%s)", from.toString(), dependencyMetaData.getRequested(), dependencyDescriptor);
-        }
-
-        /**
-         * @return The resolved module version
-         */
-        public ModuleVersionResolveState resolveModuleRevisionId() {
-            if (targetModuleRevision == null) {
-                targetModuleRevision = selector.resolveModuleRevisionId();
-                selector.getSelectedModule().addUnattachedDependency(this);
-            }
-            return targetModuleRevision;
-        }
-
-        public boolean isTransitive() {
-            return from.isTransitive() && dependencyMetaData.isTransitive();
-        }
-
-        public void attachToTargetConfigurations() {
-            if (targetModuleRevision.state != ModuleState.Selected) {
-                return;
-            }
-            calculateTargetConfigurations();
-            for (ConfigurationNode targetConfiguration : targetConfigurations) {
-                targetConfiguration.addIncomingEdge(this);
-            }
-            if (!targetConfigurations.isEmpty()) {
-                selector.getSelectedModule().removeUnattachedDependency(this);
-            }
-        }
-
-        public void removeFromTargetConfigurations() {
-            for (ConfigurationNode targetConfiguration : targetConfigurations) {
-                targetConfiguration.removeIncomingEdge(this);
-            }
-            targetConfigurations.clear();
-            if (targetModuleRevision != null) {
-                selector.getSelectedModule().removeUnattachedDependency(this);
-            }
-        }
-
-        public void restart(ModuleVersionResolveState selected) {
-            targetModuleRevision = selected;
-            attachToTargetConfigurations();
-        }
-
-        private void calculateTargetConfigurations() {
-            targetConfigurations.clear();
-            ComponentMetaData targetModuleVersion = targetModuleRevision.getMetaData();
-            if (targetModuleVersion == null) {
-                // Broken version
-                return;
-            }
-
-            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) {
-            Set<ComponentArtifactMetaData> dependencyArtifacts = dependencyMetaData.getArtifacts(from.metaData, childConfiguration.metaData);
-            if (dependencyArtifacts.isEmpty()) {
-                return Collections.emptySet();
-            }
-            Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
-            for (ComponentArtifactMetaData artifact : dependencyArtifacts) {
-                artifacts.add(resolveState.builder.newArtifact(childConfiguration.id, childConfiguration.metaData.getComponent(), artifact, resolveState.artifactResolver));
-            }
-            return artifacts;
-        }
-
-        public void attachToParents(ConfigurationNode childConfiguration, ResolvedConfigurationBuilder oldModelBuilder) {
-            ResolvedConfigurationIdentifier parent = from.id;
-            ResolvedConfigurationIdentifier child = childConfiguration.id;
-            oldModelBuilder.addChild(parent, child);
-
-            Set<ResolvedArtifact> artifacts = getArtifacts(childConfiguration);
-            if (artifacts.isEmpty()) {
-                artifacts = childConfiguration.getArtifacts();
-            }
-            //TODO SF merge with addChild
-            oldModelBuilder.addParentSpecificArtifacts(child, parent, artifacts);
-
-            if (parent == resolveState.root.id) {
-                EnhancedDependencyDescriptor enhancedDependencyDescriptor = (EnhancedDependencyDescriptor) dependencyDescriptor;
-                oldModelBuilder.addFirstLevelDependency(enhancedDependencyDescriptor.getModuleDependency(), child);
-            }
-        }
-
-        public ModuleVersionSpec getSelector() {
-            String[] configurations = from.metaData.getHierarchy().toArray(new String[from.metaData.getHierarchy().size()]);
-            ModuleVersionSpec selector = ModuleVersionSpec.forExcludes(dependencyDescriptor.getExcludeRules(configurations));
-            return selector.intersect(selectorSpec);
-        }
-
-        public ComponentSelector getRequested() {
-            return dependencyMetaData.getSelector();
-        }
-
-        public ModuleVersionResolveException getFailure() {
-            return selector.getFailure();
-        }
-
-        public ModuleVersionIdentifier getSelected() {
-            return selector.getSelected().getSelectedId();
-        }
-
-        public ComponentSelectionReason getReason() {
-            return selector.getSelectionReason();
-        }
-
-        public void collectFailures(FailureState failureState) {
-            ModuleVersionResolveException failure = getFailure();
-            if (failure != null) {
-                failureState.addUnresolvedDependency(this, selector.dependencyMetaData.getRequested(), failure);
-            }
-        }
-    }
-
-    /**
-     * Global resolution state.
-     */
-    private static class ResolveState {
-        private final Map<ModuleIdentifier, ModuleResolveState> modules = new LinkedHashMap<ModuleIdentifier, ModuleResolveState>();
-        private final Map<ResolvedConfigurationIdentifier, ConfigurationNode> nodes = new LinkedHashMap<ResolvedConfigurationIdentifier, ConfigurationNode>();
-        private final Map<ModuleVersionSelector, ModuleVersionSelectorResolveState> selectors = new LinkedHashMap<ModuleVersionSelector, ModuleVersionSelectorResolveState>();
-        private final RootConfigurationNode root;
-        private final DependencyToModuleVersionIdResolver resolver;
-        private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
-        private final ArtifactResolver artifactResolver;
-        private final ResolvedConfigurationBuilder builder;
-        private final Set<ConfigurationNode> queued = new HashSet<ConfigurationNode>();
-        private final LinkedList<ConfigurationNode> queue = new LinkedList<ConfigurationNode>();
-
-        public ResolveState(ComponentResolveResult rootResult, String rootConfigurationName, DependencyToModuleVersionIdResolver resolver,
-                            DependencyToConfigurationResolver dependencyToConfigurationResolver, ArtifactResolver artifactResolver, ResolvedConfigurationBuilder builder) {
-            this.resolver = resolver;
-            this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
-            this.artifactResolver = artifactResolver;
-            this.builder = builder;
-            ModuleVersionResolveState rootVersion = getRevision(rootResult.getId());
-            rootVersion.setResolveResult(rootResult);
-            root = new RootConfigurationNode(rootVersion, new ResolvedConfigurationIdentifier(rootVersion.id, rootConfigurationName), this);
-            nodes.put(root.id, root);
-            root.moduleRevision.module.select(root.moduleRevision);
-        }
-
-        public ModuleResolveState getModule(ModuleIdentifier id) {
-            ModuleResolveState module = modules.get(id);
-            if (module == null) {
-                module = new ModuleResolveState(id, this);
-                modules.put(id, module);
-            }
-            return module;
-        }
-
-        public ModuleVersionResolveState getRevision(ModuleVersionIdentifier id) {
-            return getModule(id.getModule()).getVersion(id);
-        }
-
-        public Collection<ConfigurationNode> getConfigurationNodes() {
-            return nodes.values();
-        }
-
-        public ConfigurationNode getConfigurationNode(ModuleVersionResolveState module, String configurationName) {
-            ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(module.id, configurationName);
-            ConfigurationNode configuration = nodes.get(id);
-            if (configuration == null) {
-                configuration = new ConfigurationNode(module, id, this);
-                nodes.put(id, configuration);
-            }
-            return configuration;
-        }
-
-        public ModuleVersionSelectorResolveState getSelector(DependencyMetaData dependencyMetaData) {
-            ModuleVersionSelector requested = dependencyMetaData.getRequested();
-            ModuleVersionSelectorResolveState resolveState = selectors.get(requested);
-            if (resolveState == null) {
-                resolveState = new ModuleVersionSelectorResolveState(dependencyMetaData, resolver, this);
-                selectors.put(requested, resolveState);
-            }
-            return resolveState;
-        }
-
-        public ConfigurationNode peek() {
-            return queue.isEmpty() ? null : queue.getFirst();
-        }
-
-        public ConfigurationNode pop() {
-            ConfigurationNode next = queue.removeFirst();
-            queued.remove(next);
-            return next;
-        }
-
-        /**
-         * Called when a change is made to a configuration node, such that its dependency graph <em>may</em> now be larger than it previously was, and the node should be visited.
-         */
-        public void onMoreSelected(ConfigurationNode configuration) {
-            // Add to the end of the queue, so that we traverse the graph in breadth-wise order to pick up as many conflicts as
-            // possible before attempting to resolve them
-            if (queued.add(configuration)) {
-                queue.addLast(configuration);
-            }
-        }
-
-        /**
-         * Called when a change is made to a configuration node, such that its dependency graph <em>may</em> now be smaller than it previously was, and the node should be visited.
-         */
-        public void onFewerSelected(ConfigurationNode configuration) {
-            // Add to the front of the queue, to flush out configurations that are no longer required.
-            if (queued.add(configuration)) {
-                queue.addFirst(configuration);
-            }
-        }
-    }
-
-    enum ModuleState {
-        New,
-        Selected,
-        Conflict,
-        Evicted
-    }
-
-    /**
-     * Resolution state for a given module.
-     */
-    private static class ModuleResolveState {
-        final ModuleIdentifier id;
-        final Set<DependencyEdge> unattachedDependencies = new LinkedHashSet<DependencyEdge>();
-        final Map<ModuleVersionIdentifier, ModuleVersionResolveState> versions = new LinkedHashMap<ModuleVersionIdentifier, ModuleVersionResolveState>();
-        final Set<ModuleVersionSelectorResolveState> selectors = new HashSet<ModuleVersionSelectorResolveState>();
-        final ResolveState resolveState;
-        ModuleVersionResolveState selected;
-
-        private ModuleResolveState(ModuleIdentifier id, ResolveState resolveState) {
-            this.id = id;
-            this.resolveState = resolveState;
-        }
-
-        @Override
-        public String toString() {
-            return id.toString();
-        }
-
-        public Collection<ModuleVersionResolveState> getVersions() {
-            return versions.values();
-        }
-
-        public void select(ModuleVersionResolveState selected) {
-            assert this.selected == null;
-            this.selected = selected;
-            for (ModuleVersionResolveState version : versions.values()) {
-                version.state = ModuleState.Evicted;
-            }
-            selected.state = ModuleState.Selected;
-        }
-
-        public ModuleVersionResolveState clearSelection() {
-            ModuleVersionResolveState previousSelection = selected;
-            selected = null;
-            for (ModuleVersionResolveState version : versions.values()) {
-                version.state = ModuleState.Conflict;
-            }
-            return previousSelection;
-        }
-
-        public void restart(ModuleVersionResolveState selected) {
-            select(selected);
-            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);
-            }
-            unattachedDependencies.clear();
-        }
-
-        public void addUnattachedDependency(DependencyEdge edge) {
-            unattachedDependencies.add(edge);
-        }
-
-        public void removeUnattachedDependency(DependencyEdge edge) {
-            unattachedDependencies.remove(edge);
-        }
-
-        public ModuleVersionResolveState getVersion(ModuleVersionIdentifier id) {
-            ModuleVersionResolveState moduleRevision = versions.get(id);
-            if (moduleRevision == null) {
-                moduleRevision = new ModuleVersionResolveState(this, id, resolveState);
-                versions.put(id, moduleRevision);
-            }
-
-            return moduleRevision;
-        }
-
-        public void addSelector(ModuleVersionSelectorResolveState selector) {
-            selectors.add(selector);
-        }
-    }
-
-    /**
-     * Resolution state for a given module version.
-     */
-    private static class ModuleVersionResolveState implements ModuleRevisionResolveState, ModuleVersionSelection {
-        final ModuleResolveState module;
-        final ModuleVersionIdentifier id;
-        final ResolveState resolveState;
-        final Set<ConfigurationNode> configurations = new LinkedHashSet<ConfigurationNode>();
-        ComponentMetaData metaData;
-        ModuleState state = ModuleState.New;
-        ComponentSelectionReason selectionReason = VersionSelectionReasons.REQUESTED;
-        ModuleVersionIdResolveResult idResolveResult;
-        ComponentResolveResult resolveResult;
-        ModuleVersionResolveException failure;
-
-        private ModuleVersionResolveState(ModuleResolveState module, ModuleVersionIdentifier id, ResolveState resolveState) {
-            this.module = module;
-            this.id = id;
-            this.resolveState = resolveState;
-        }
-
-        @Override
-        public String toString() {
-            return id.toString();
-        }
-
-        public String getVersion() {
-            return id.getVersion();
-        }
-
-        public String getId() {
-            return String.format("%s:%s:%s", id.getGroup(), id.getName(), id.getVersion());
-        }
-
-        public ModuleVersionResolveException getFailure() {
-            return failure;
-        }
-
-        public void restart(ModuleVersionResolveState selected) {
-            for (ConfigurationNode configuration : configurations) {
-                configuration.restart(selected);
-            }
-        }
-
-        public void addResolver(ModuleVersionSelectorResolveState resolver) {
-            if (this.idResolveResult == null) {
-                idResolveResult = resolver.idResolveResult;
-            }
-        }
-
-        public ComponentResolveResult 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 ComponentMetaData getMetaData() {
-            if (metaData == null) {
-                resolve();
-            }
-            return metaData;
-        }
-
-        public void setResolveResult(ComponentResolveResult resolveResult) {
-            this.resolveResult = resolveResult;
-            this.metaData = resolveResult.getMetaData();
-            this.failure = null;
-        }
-
-        public void addConfiguration(ConfigurationNode configurationNode) {
-            configurations.add(configurationNode);
-        }
-
-        public ModuleVersionIdentifier getSelectedId() {
-            return id;
-        }
-
-        public ComponentSelectionReason getSelectionReason() {
-            return selectionReason;
-        }
-
-        public void setSelectionReason(ComponentSelectionReason reason) {
-            this.selectionReason = reason;
-        }
-
-        public ComponentIdentifier getComponentId() {
-            return getMetaData().getComponentId();
-        }
-    }
-
-    /**
-     * Represents a node in the dependency graph.
-     */
-    private static class ConfigurationNode {
-        final ModuleVersionResolveState moduleRevision;
-        final ConfigurationMetaData metaData;
-        final ResolveState resolveState;
-        final Set<DependencyEdge> incomingEdges = new LinkedHashSet<DependencyEdge>();
-        final Set<DependencyEdge> outgoingEdges = new LinkedHashSet<DependencyEdge>();
-        final ResolvedConfigurationIdentifier id;
-        ModuleVersionSpec previousTraversal;
-        Set<ResolvedArtifact> artifacts;
-
-        private ConfigurationNode(ModuleVersionResolveState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState) {
-            this.moduleRevision = moduleRevision;
-            this.resolveState = resolveState;
-            this.metaData = moduleRevision.metaData.getConfiguration(id.getConfiguration());
-            this.id = id;
-            moduleRevision.addConfiguration(this);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("%s(%s)", moduleRevision, metaData.getName());
-        }
-
-        public Set<ResolvedArtifact> getArtifacts() {
-            if (artifacts == null) {
-                artifacts = new LinkedHashSet<ResolvedArtifact>();
-
-                BuildableArtifactSetResolveResult result = new DefaultBuildableArtifactSetResolveResult();
-                resolveState.artifactResolver.resolveModuleArtifacts(metaData.getComponent(), new ConfigurationResolveContext(metaData.getName()), result);
-
-                for (ComponentArtifactMetaData artifact : result.getArtifacts()) {
-                    artifacts.add(resolveState.builder.newArtifact(id, metaData.getComponent(), artifact, resolveState.artifactResolver));
-                }
-            }
-            return artifacts;
-        }
-
-        public boolean isTransitive() {
-            return metaData.isTransitive();
-        }
-
-        public void visitOutgoingDependencies(Collection<DependencyEdge> target) {
-            // If this configuration's version is in conflict, don't do anything
-            // If not traversed before, add all selected outgoing edges
-            // 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 are transitive, then the node has no outgoing edges
-
-            if (moduleRevision.state != ModuleState.Selected) {
-                LOGGER.debug("version for {} is not selected. ignoring.", this);
-                return;
-            }
-
-            List<DependencyEdge> transitiveIncoming = new ArrayList<DependencyEdge>();
-            for (DependencyEdge edge : incomingEdges) {
-                if (edge.isTransitive()) {
-                    transitiveIncoming.add(edge);
-                }
-            }
-
-            if (transitiveIncoming.isEmpty() && this != resolveState.root) {
-                if (previousTraversal != null) {
-                    removeOutgoingEdges();
-                }
-                if (incomingEdges.isEmpty()) {
-                    LOGGER.debug("{} has no incoming edges. ignoring.", this);
-                } else {
-                    LOGGER.debug("{} has no transitive incoming edges. ignoring outgoing edges.", this);
-                }
-                return;
-            }
-
-            ModuleVersionSpec selectorSpec = getSelector(transitiveIncoming);
-            if (previousTraversal != null) {
-                if (previousTraversal.acceptsSameModulesAs(selectorSpec)) {
-                    LOGGER.debug("Changed edges for {} selects same versions as previous traversal. ignoring", this);
-                    return;
-                }
-                removeOutgoingEdges();
-            }
-
-            for (DependencyMetaData dependency : metaData.getDependencies()) {
-                DependencyDescriptor dependencyDescriptor = dependency.getDescriptor();
-                ModuleId targetModuleId = dependencyDescriptor.getDependencyRevisionId().getModuleId();
-                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;
-        }
-
-        public void addIncomingEdge(DependencyEdge dependencyEdge) {
-            incomingEdges.add(dependencyEdge);
-            resolveState.onMoreSelected(this);
-        }
-
-        public void removeIncomingEdge(DependencyEdge dependencyEdge) {
-            incomingEdges.remove(dependencyEdge);
-            resolveState.onFewerSelected(this);
-        }
-
-        public boolean isSelected() {
-            return moduleRevision.state == ModuleState.Selected;
-        }
-
-        public void attachToParents(ResolvedConfigurationBuilder oldModelBuilder) {
-            LOGGER.debug("Attaching {} to its parents.", this);
-            for (DependencyEdge dependency : incomingEdges) {
-                dependency.attachToParents(this, oldModelBuilder);
-            }
-        }
-
-        public void collectFailures(FailureState failureState) {
-            for (DependencyEdge dependency : outgoingEdges) {
-                dependency.collectFailures(failureState);
-            }
-        }
-
-        private ModuleVersionSpec getSelector(List<DependencyEdge> transitiveEdges) {
-            ModuleVersionSpec selector;
-            if (transitiveEdges.isEmpty()) {
-                selector = ModuleVersionSpec.forExcludes(); //includes all
-            } else {
-                selector = transitiveEdges.get(0).getSelector();
-                for (int i = 1; i < transitiveEdges.size(); i++) {
-                    DependencyEdge dependencyEdge = transitiveEdges.get(i);
-                    selector = selector.union(dependencyEdge.getSelector());
-                }
-            }
-            selector = selector.intersect(ModuleVersionSpec.forExcludes(metaData.getExcludeRules()));
-            return selector;
-        }
-
-        public void removeOutgoingEdges() {
-            for (DependencyEdge outgoingDependency : outgoingEdges) {
-                outgoingDependency.removeFromTargetConfigurations();
-            }
-            outgoingEdges.clear();
-            previousTraversal = null;
-        }
-
-        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 remove our incoming edges, which triggers them to be moved across to the selected configuration
-            if (moduleRevision == selected) {
-                resolveState.onMoreSelected(this);
-            } else {
-                for (DependencyEdge dependency : incomingEdges) {
-                    dependency.restart(selected);
-                }
-                incomingEdges.clear();
-            }
-        }
-
-        private ModuleVersionIdentifier toId() {
-            return moduleRevision.id;
-        }
-
-        public void validate() {
-            for (DependencyEdge incomingEdge : incomingEdges) {
-                ConfigurationNode fromNode = incomingEdge.from;
-                if (!fromNode.isSelected()) {
-                    throw new IllegalStateException(String.format("Unexpected state %s for parent node for dependency from %s to %s.", fromNode.moduleRevision.state, fromNode, this));
-                }
-            }
-        }
-
-        public void deselect() {
-            removeOutgoingEdges();
-        }
-    }
-
-    private static class RootConfigurationNode extends ConfigurationNode {
-        private RootConfigurationNode(ModuleVersionResolveState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState) {
-            super(moduleRevision, id, resolveState);
-        }
-
-        @Override
-        public boolean isSelected() {
-            return true;
-        }
-
-        @Override
-        public void deselect() {
-        }
-    }
-
-    /**
-     * Resolution state for a given module version selector.
-     */
-    private static class ModuleVersionSelectorResolveState {
-        final DependencyToModuleVersionIdResolver resolver;
-        final ResolveState resolveState;
-        final DependencyMetaData dependencyMetaData;
-        ModuleVersionResolveException failure;
-        ModuleResolveState targetModule;
-        ModuleVersionResolveState targetModuleRevision;
-        ModuleVersionIdResolveResult idResolveResult;
-
-        private ModuleVersionSelectorResolveState(DependencyMetaData dependencyMetaData, DependencyToModuleVersionIdResolver resolver, ResolveState resolveState) {
-            this.dependencyMetaData = dependencyMetaData;
-            this.resolver = resolver;
-            this.resolveState = resolveState;
-            targetModule = resolveState.getModule(new DefaultModuleIdentifier(dependencyMetaData.getRequested().getGroup(), dependencyMetaData.getRequested().getName()));
-        }
-
-        @Override
-        public String toString() {
-            return dependencyMetaData.toString();
-        }
-
-        private ModuleVersionResolveException getFailure() {
-            return failure != null ? failure : targetModuleRevision.getFailure();
-        }
-
-        public ComponentSelectionReason 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 ModuleVersionResolveState resolveModuleRevisionId() {
-            if (targetModuleRevision != null) {
-                return targetModuleRevision;
-            }
-            if (failure != null) {
-                return null;
-            }
-
-            idResolveResult = resolver.resolve(dependencyMetaData);
-            if (idResolveResult.getFailure() != null) {
-                failure = idResolveResult.getFailure();
-                return null;
-            }
-
-            targetModuleRevision = resolveState.getRevision(idResolveResult.getId());
-            targetModuleRevision.addResolver(this);
-            targetModuleRevision.selectionReason = idResolveResult.getSelectionReason();
-            targetModule = targetModuleRevision.module;
-            targetModule.addSelector(this);
-
-            return targetModuleRevision;
-        }
-
-        public void restart(ModuleVersionResolveState moduleRevision) {
-            this.targetModuleRevision = moduleRevision;
-            this.targetModule = moduleRevision.module;
-        }
-    }
-
-    private static class InternalConflictResolver {
-        private final ModuleConflictResolver resolver;
-
-        private InternalConflictResolver(ModuleConflictResolver resolver) {
-            this.resolver = resolver;
-        }
-
-        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)) {
-                        outgoingEdge.targetModuleRevision.selectionReason = VersionSelectionReasons.FORCED;
-                        return outgoingEdge.targetModuleRevision;
-                    }
-                }
-            }
-            return 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
deleted file mode 100644
index 04bec5d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java
+++ /dev/null
@@ -1,30 +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.resolveengine;
-
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.ConfigurationMetaData;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-
-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, ComponentMetaData targetComponent);
-}
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
deleted file mode 100644
index e49b0ca..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
+++ /dev/null
@@ -1,32 +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.resolveengine;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-
-import java.util.Collection;
-
-class LatestModuleConflictResolver implements ModuleConflictResolver {
-    private final LatestStrategy latestStrategy;
-
-        LatestModuleConflictResolver(LatestStrategy latestStrategy) {
-        this.latestStrategy = latestStrategy;
-    }
-
-    public <T extends ModuleRevisionResolveState> T select(Collection<? extends T> 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
deleted file mode 100644
index e1c6784..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
+++ /dev/null
@@ -1,22 +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.resolveengine;
-
-import java.util.Collection;
-
-interface ModuleConflictResolver {
-    <T extends ModuleRevisionResolveState> T select(Collection<? extends T> 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
deleted file mode 100644
index 155a4b8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleRevisionResolveState.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.api.internal.artifacts.ivyservice.resolveengine;
-
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
-
-interface ModuleRevisionResolveState extends Versioned {
-    String getId();
-
-    ComponentSelectionReason getSelectionReason();
-
-    void setSelectionReason(ComponentSelectionReason componentSelectionReason);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpec.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpec.java
deleted file mode 100644
index 307b8db..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpec.java
+++ /dev/null
@@ -1,556 +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.resolveengine;
-
-import org.apache.ivy.core.module.descriptor.ExcludeRule;
-import org.apache.ivy.core.module.id.ModuleId;
-import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
-import org.apache.ivy.plugins.matcher.MatcherHelper;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.gradle.api.specs.Spec;
-
-import java.util.*;
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId;
-
-/**
- * Manages sets of exclude rules, allowing union and intersection operations on the rules.
- *
- * <p>This class attempts to reduce execution time, by flattening union and intersection specs, at the cost of more analysis at construction time. This is taken advantage of by {@link
- * DependencyGraphBuilder}, on the assumption that there are many more edges in the dependency graph than there are exclude rules (ie we evaluate the rules much more often that we construct them).
- * </p>
- *
- * <p>Also, this class attempts to be quite accurate in determining if 2 specs will match exactly the same set of modules. {@link DependencyGraphBuilder} uses this to avoid traversing the
- * dependency graph of a particular version that has already been traversed when a new incoming edge is added (eg a newly discovered dependency) and when an incoming edge is removed (eg a conflict
- * evicts a version that depends on the given version). </p>
- */
-public abstract class ModuleVersionSpec implements Spec<ModuleId> {
-    private static final AcceptAllSpec ALL_SPEC = new AcceptAllSpec();
-
-    public static ModuleVersionSpec forExcludes(ExcludeRule... excludeRules) {
-        return forExcludes(Arrays.asList(excludeRules));
-    }
-
-    /**
-     * Returns a spec that accepts only those module versions that do not match any of the
-     */
-    public static ModuleVersionSpec forExcludes(Collection<ExcludeRule> excludeRules) {
-        if (excludeRules.isEmpty()) {
-            return ALL_SPEC;
-        }
-        return new ExcludeRuleBackedSpec(excludeRules);
-    }
-
-    /**
-     * Returns a spec that accepts the union of those module versions that are accepted by this spec and the given spec.
-     */
-    public final ModuleVersionSpec union(ModuleVersionSpec other) {
-        if (other == this) {
-            return this;
-        }
-        if (other == ALL_SPEC) {
-            return other;
-        }
-        if (this == ALL_SPEC) {
-            return this;
-        }
-        List<ModuleVersionSpec> specs = new ArrayList<ModuleVersionSpec>();
-        unpackUnion(specs);
-        other.unpackUnion(specs);
-        for (int i = 0; i < specs.size();) {
-            ModuleVersionSpec spec = specs.get(i);
-            ModuleVersionSpec merged = null;
-            for (int j = i + 1; j < specs.size(); j++) {
-                merged = spec.doUnion(specs.get(j));
-                if (merged != null) {
-                    specs.remove(j);
-                    break;
-                }
-            }
-            if (merged != null) {
-                specs.set(i, merged);
-            } else {
-                i++;
-            }
-        }
-        if (specs.size() == 1) {
-            return specs.get(0);
-        }
-        return new UnionSpec(specs);
-    }
-
-    protected void unpackUnion(Collection<ModuleVersionSpec> specs) {
-        specs.add(this);
-    }
-
-    protected ModuleVersionSpec doUnion(ModuleVersionSpec other) {
-        return null;
-    }
-
-    /**
-     * Determines if this spec accepts the same set of modules as the given spec.
-     *
-     * @return true if the specs accept the same set of modules. Returns false if they may not, or if it is unknown.
-     */
-    public final boolean acceptsSameModulesAs(ModuleVersionSpec other) {
-        if (other == this) {
-            return true;
-        }
-        if (!other.getClass().equals(getClass())) {
-            return false;
-        }
-        return doAcceptsSameModulesAs(other);
-    }
-
-    /**
-     * Only called when this and the other spec have the same class.
-     */
-    protected boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-        return false;
-    }
-
-    /**
-     * Returns a spec that accepts the intersection of those module versions that are accepted by this spec and the given spec.
-     */
-    public final ModuleVersionSpec intersect(ModuleVersionSpec other) {
-        if (other == this) {
-            return this;
-        }
-        if (other == ALL_SPEC) {
-            return this;
-        }
-        if (this == ALL_SPEC) {
-            return other;
-        }
-        return doIntersection(other);
-    }
-
-    protected ModuleVersionSpec doIntersection(ModuleVersionSpec other) {
-        return new IntersectSpec(this, other);
-    }
-
-    private static class AcceptAllSpec extends ModuleVersionSpec {
-        @Override
-        public String toString() {
-            return "{accept-all}";
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            return true;
-        }
-    }
-
-    private static abstract class CompositeSpec extends ModuleVersionSpec {
-        abstract Collection<ModuleVersionSpec> getSpecs();
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("{");
-            builder.append(getClass().getSimpleName());
-            for (ModuleVersionSpec spec : getSpecs()) {
-                builder.append(' ');
-                builder.append(spec);
-            }
-            builder.append("}");
-            return builder.toString();
-        }
-
-        @Override
-        protected boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-            CompositeSpec spec = (CompositeSpec) other;
-            return implies(spec) && spec.implies(this);
-        }
-
-        /**
-         * Returns true if for every spec in this spec, there is a corresponding spec in the given spec that acceptsSameModulesAs().
-         */
-        protected boolean implies(CompositeSpec spec) {
-            for (ModuleVersionSpec thisSpec : getSpecs()) {
-                boolean found = false;
-                for (ModuleVersionSpec otherSpec : spec.getSpecs()) {
-                    if (thisSpec.acceptsSameModulesAs(otherSpec)) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    static class ExcludeRuleBackedSpec extends CompositeSpec {
-        private final Set<ModuleVersionSpec> excludeSpecs = new HashSet<ModuleVersionSpec>();
-
-        private ExcludeRuleBackedSpec(Iterable<ExcludeRule> excludeRules) {
-            for (ExcludeRule rule : excludeRules) {
-                if (!(rule.getMatcher() instanceof ExactPatternMatcher)) {
-                    excludeSpecs.add(new ExcludeRuleSpec(rule));
-                    continue;
-                }
-                ModuleId moduleId = rule.getId().getModuleId();
-                boolean wildcardGroup = PatternMatcher.ANY_EXPRESSION.equals(moduleId.getOrganisation());
-                boolean wildcardModule = PatternMatcher.ANY_EXPRESSION.equals(moduleId.getName());
-                if (wildcardGroup && wildcardModule) {
-                    excludeSpecs.add(new ExcludeRuleSpec(rule));
-                } else if (wildcardGroup) {
-                    excludeSpecs.add(new ModuleNameSpec(moduleId.getName()));
-                } else if (wildcardModule) {
-                    excludeSpecs.add(new GroupNameSpec(moduleId.getOrganisation()));
-                } else {
-                    excludeSpecs.add(new ModuleIdSpec(moduleId));
-                }
-            }
-        }
-
-        public ExcludeRuleBackedSpec(Collection<ModuleVersionSpec> specs) {
-            this.excludeSpecs.addAll(specs);
-        }
-
-        @Override
-        Collection<ModuleVersionSpec> getSpecs() {
-            return excludeSpecs;
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            for (ModuleVersionSpec excludeSpec : excludeSpecs) {
-                if (excludeSpec.isSatisfiedBy(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        protected ModuleVersionSpec doUnion(ModuleVersionSpec other) {
-            if (!(other instanceof ExcludeRuleBackedSpec)) {
-                return super.doUnion(other);
-            }
-
-            ExcludeRuleBackedSpec excludeRuleBackedSpec = (ExcludeRuleBackedSpec) other;
-            if (excludeSpecs.equals(excludeRuleBackedSpec.excludeSpecs)) {
-                return this;
-            }
-
-            // Can only merge exact match rules, so don't try if this or the other spec contains any other type of rule
-            for (ModuleVersionSpec excludeSpec : excludeSpecs) {
-                if (excludeSpec instanceof ExcludeRuleSpec) {
-                    return super.doUnion(other);
-                }
-            }
-            for (ModuleVersionSpec excludeSpec : excludeRuleBackedSpec.excludeSpecs) {
-                if (excludeSpec instanceof ExcludeRuleSpec) {
-                    return super.doUnion(other);
-                }
-            }
-
-            // Calculate the intersection of the rules
-            List<ModuleVersionSpec> merged = new ArrayList<ModuleVersionSpec>();
-            for (ModuleVersionSpec thisSpec : excludeSpecs) {
-                for (ModuleVersionSpec otherSpec : excludeRuleBackedSpec.excludeSpecs) {
-                    intersect(thisSpec, otherSpec, merged);
-                }
-            }
-            if (merged.isEmpty()) {
-                return ALL_SPEC;
-            }
-            return new ExcludeRuleBackedSpec(merged);
-        }
-
-        private void intersect(ModuleVersionSpec spec1, ModuleVersionSpec spec2, List<ModuleVersionSpec> merged) {
-            if (spec1 instanceof GroupNameSpec) {
-                intersect((GroupNameSpec) spec1, spec2, merged);
-            } else if (spec2 instanceof GroupNameSpec) {
-                intersect((GroupNameSpec) spec2, spec1, merged);
-            } else if (spec1 instanceof ModuleNameSpec) {
-                intersect((ModuleNameSpec) spec1, spec2, merged);
-            } else if (spec2 instanceof ModuleNameSpec) {
-                intersect((ModuleNameSpec) spec2, spec1, merged);
-            } else if ((spec1 instanceof ModuleIdSpec) && (spec2 instanceof ModuleIdSpec)) {
-                ModuleIdSpec moduleSpec1 = (ModuleIdSpec) spec1;
-                ModuleIdSpec moduleSpec2 = (ModuleIdSpec) spec2;
-                if (moduleSpec1.moduleId.equals(moduleSpec2.moduleId)) {
-                    merged.add(moduleSpec1);
-                }
-            } else {
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        private void intersect(GroupNameSpec spec1, ModuleVersionSpec spec2, List<ModuleVersionSpec> merged) {
-            if (spec2 instanceof GroupNameSpec) {
-                GroupNameSpec groupNameSpec = (GroupNameSpec) spec2;
-                if (spec1.group.equals(groupNameSpec.group)) {
-                    merged.add(spec1);
-                }
-            } else if (spec2 instanceof ModuleNameSpec) {
-                ModuleNameSpec moduleNameSpec = (ModuleNameSpec) spec2;
-                merged.add(new ModuleIdSpec(createModuleId(spec1.group, moduleNameSpec.module)));
-            } else if (spec2 instanceof ModuleIdSpec) {
-                ModuleIdSpec moduleIdSpec = (ModuleIdSpec) spec2;
-                if (moduleIdSpec.moduleId.getOrganisation().equals(spec1.group)) {
-                    merged.add(spec2);
-                }
-            } else {
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        private void intersect(ModuleNameSpec spec1, ModuleVersionSpec spec2, List<ModuleVersionSpec> merged) {
-            if (spec2 instanceof ModuleNameSpec) {
-                ModuleNameSpec moduleNameSpec = (ModuleNameSpec) spec2;
-                if (spec1.module.equals(moduleNameSpec.module)) {
-                    merged.add(spec1);
-                }
-            } else if (spec2 instanceof ModuleIdSpec) {
-                ModuleIdSpec moduleIdSpec = (ModuleIdSpec) spec2;
-                if (moduleIdSpec.moduleId.getName().equals(spec1.module)) {
-                    merged.add(spec2);
-                }
-            } else {
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        @Override
-        protected ModuleVersionSpec doIntersection(ModuleVersionSpec other) {
-            if (!(other instanceof ExcludeRuleBackedSpec)) {
-                return super.doIntersection(other);
-            }
-
-            ExcludeRuleBackedSpec otherExcludeRuleSpec = (ExcludeRuleBackedSpec) other;
-            Set<ModuleVersionSpec> allSpecs = new HashSet<ModuleVersionSpec>();
-            allSpecs.addAll(excludeSpecs);
-            allSpecs.addAll(otherExcludeRuleSpec.excludeSpecs);
-            return new ExcludeRuleBackedSpec(allSpecs);
-        }
-    }
-
-    static class UnionSpec extends CompositeSpec {
-        private final List<ModuleVersionSpec> specs;
-
-        public UnionSpec(List<ModuleVersionSpec> specs) {
-            this.specs = specs;
-        }
-
-        @Override
-        Collection<ModuleVersionSpec> getSpecs() {
-            return specs;
-        }
-
-        @Override
-        protected void unpackUnion(Collection<ModuleVersionSpec> specs) {
-            specs.addAll(this.specs);
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            for (ModuleVersionSpec spec : specs) {
-                if (spec.isSatisfiedBy(element)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-    }
-
-    private static class IntersectSpec extends CompositeSpec {
-        private final List<ModuleVersionSpec> specs;
-
-        private IntersectSpec(ModuleVersionSpec... specs) {
-            this.specs = Arrays.asList(specs);
-        }
-
-        @Override
-        Collection<ModuleVersionSpec> getSpecs() {
-            return specs;
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            for (ModuleVersionSpec spec : specs) {
-                if (!spec.isSatisfiedBy(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    private static class ModuleIdSpec extends ModuleVersionSpec {
-        private final ModuleId moduleId;
-
-        private ModuleIdSpec(ModuleId moduleId) {
-            this.moduleId = moduleId;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("{module-id %s}", moduleId);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (o == null || o.getClass() != getClass()) {
-                return false;
-            }
-            ModuleIdSpec other = (ModuleIdSpec) o;
-            return moduleId.equals(other.moduleId);
-        }
-
-        @Override
-        public int hashCode() {
-            return moduleId.hashCode();
-        }
-
-        @Override
-        protected boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-            ModuleIdSpec moduleIdSpec = (ModuleIdSpec) other;
-            return moduleId.equals(moduleIdSpec.moduleId);
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            return element.equals(moduleId);
-        }
-    }
-
-    private static class ModuleNameSpec extends ModuleVersionSpec {
-        private final String module;
-
-        private ModuleNameSpec(String module) {
-            this.module = module;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("{module %s}", module);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (o == null || o.getClass() != getClass()) {
-                return false;
-            }
-            ModuleNameSpec other = (ModuleNameSpec) o;
-            return module.equals(other.module);
-        }
-
-        @Override
-        public int hashCode() {
-            return module.hashCode();
-        }
-
-        @Override
-        public boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-            ModuleNameSpec moduleNameSpec = (ModuleNameSpec) other;
-            return module.equals(moduleNameSpec.module);
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            return element.getName().equals(module);
-        }
-    }
-
-    private static class GroupNameSpec extends ModuleVersionSpec {
-        private final String group;
-
-        private GroupNameSpec(String group) {
-            this.group = group;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("{group %s}", group);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (o == null || o.getClass() != getClass()) {
-                return false;
-            }
-            GroupNameSpec other = (GroupNameSpec) o;
-            return group.equals(other.group);
-        }
-
-        @Override
-        public int hashCode() {
-            return group.hashCode();
-        }
-
-        @Override
-        public boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-            GroupNameSpec groupNameSpec = (GroupNameSpec) other;
-            return group.equals(groupNameSpec.group);
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            return element.getOrganisation().equals(group);
-        }
-    }
-
-    private static class ExcludeRuleSpec extends ModuleVersionSpec {
-        private final ExcludeRule rule;
-
-        private ExcludeRuleSpec(ExcludeRule rule) {
-            this.rule = rule;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("{exclude-rule %s}", rule);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (o == null || o.getClass() != getClass()) {
-                return false;
-            }
-            ExcludeRuleSpec other = (ExcludeRuleSpec) o;
-            // Can't use equals(), as DefaultExcludeRule.equals() does not consider the pattern matcher
-            return rule == other.rule;
-        }
-
-        @Override
-        public int hashCode() {
-            return rule.hashCode();
-        }
-
-        @Override
-        protected boolean doAcceptsSameModulesAs(ModuleVersionSpec other) {
-            ExcludeRuleSpec excludeRuleSpec = (ExcludeRuleSpec) other;
-            // Can't use equals(), as DefaultExcludeRule.equals() does not consider the pattern matcher
-            return rule == excludeRuleSpec.rule;
-        }
-
-        public boolean isSatisfiedBy(ModuleId element) {
-            return MatcherHelper.matches(rule.getMatcher(), rule.getId().getModuleId(), element);
-        }
-    }
-}
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
deleted file mode 100644
index 511a5d8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.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.artifacts.ivyservice.resolveengine;
-
-import java.util.Collection;
-import java.util.Formatter;
-
-class StrictConflictResolver implements ModuleConflictResolver {
-    public <T extends ModuleRevisionResolveState> T select(Collection<? extends T> candidates) {
-        Formatter formatter = new Formatter();
-        formatter.format("A conflict was found between the following modules:");
-        for (ModuleRevisionResolveState candidate : candidates) {
-            formatter.format("%n - %s", candidate.getId());
-        }
-        throw new RuntimeException(formatter.toString());
-    }
-}
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
deleted file mode 100644
index 37588bf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java
+++ /dev/null
@@ -1,36 +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.resolveengine;
-
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
-
-import java.util.Collection;
-
-public class VersionSelectionReasonResolver implements ModuleConflictResolver {
-
-    private final ModuleConflictResolver delegate;
-
-    public VersionSelectionReasonResolver(ModuleConflictResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public <T extends ModuleRevisionResolveState> T select(Collection<? extends T> candidates) {
-        T 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
deleted file mode 100644
index 1cc87e4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java
+++ /dev/null
@@ -1,151 +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.resolveengine.oldresult;
-
-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.DefaultBuildableArtifactResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-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 final TransientConfigurationResultsBuilder builder;
-
-    public DefaultResolvedConfigurationBuilder(TransientConfigurationResultsBuilder builder) {
-        this.builder = builder;
-    }
-
-    public void addUnresolvedDependency(UnresolvedDependency unresolvedDependency) {
-        unresolvedDependencies.add(unresolvedDependency);
-    }
-
-    public void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedConfigurationIdentifier dependency) {
-        builder.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) {
-        builder.done(root);
-    }
-
-    public void addChild(ResolvedConfigurationIdentifier parent, ResolvedConfigurationIdentifier child) {
-        builder.parentChildMapping(parent, child);
-    }
-
-    public void addParentSpecificArtifacts(ResolvedConfigurationIdentifier child, ResolvedConfigurationIdentifier parent, Set<ResolvedArtifact> artifacts) {
-        for (ResolvedArtifact a : artifacts) {
-            builder.parentSpecificArtifact(child, parent, ((DefaultResolvedArtifact) a).getId());
-        }
-    }
-
-    public void newResolvedDependency(ResolvedConfigurationIdentifier id) {
-        builder.resolvedDependency(id);
-    }
-
-    public ResolvedArtifact newArtifact(ResolvedConfigurationIdentifier owner, ComponentMetaData compnent, ComponentArtifactMetaData artifact, ArtifactResolver artifactResolver) {
-        Factory<File> artifactSource = new LazyArtifactSource(artifact, compnent.getSource(), artifactResolver);
-        Factory<ResolvedDependency> dependencySource = new LazyResolvedDependencySource(owner, builder, this);
-        long id = idGenerator.generateId();
-        ResolvedArtifact newArtifact = new DefaultResolvedArtifact(new DefaultResolvedModuleVersion(owner.getId()), dependencySource, artifact.getName(), artifactSource, id);
-        artifacts.put(id, newArtifact);
-        return newArtifact;
-    }
-
-    public boolean hasError() {
-        return !unresolvedDependencies.isEmpty();
-    }
-
-    public TransientConfigurationResults more() {
-        return builder.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 LazyArtifactSource implements Factory<File> {
-        private final ArtifactResolver artifactResolver;
-        private final ModuleSource moduleSource;
-        private final ComponentArtifactMetaData artifact;
-
-        private LazyArtifactSource(ComponentArtifactMetaData artifact, ModuleSource moduleSource, ArtifactResolver artifactResolver) {
-            this.artifact = artifact;
-            this.artifactResolver = artifactResolver;
-            this.moduleSource = moduleSource;
-        }
-
-        public File create() {
-            DefaultBuildableArtifactResolveResult result = new DefaultBuildableArtifactResolveResult();
-            artifactResolver.resolveArtifact(artifact, moduleSource, result);
-            return result.getFile();
-        }
-    }
-
-    private static class LazyResolvedDependencySource implements Factory<ResolvedDependency> {
-        private final ResolvedConfigurationIdentifier owner;
-        private TransientConfigurationResultsBuilder builder;
-        private ResolvedContentsMapping mapping;
-
-        public LazyResolvedDependencySource(ResolvedConfigurationIdentifier owner, TransientConfigurationResultsBuilder builder, ResolvedContentsMapping mapping) {
-            this.owner = owner;
-            this.builder = builder;
-            this.mapping = mapping;
-        }
-
-        public ResolvedDependency create() {
-            return builder.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
deleted file mode 100644
index 0c8adad..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java
+++ /dev/null
@@ -1,45 +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.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
deleted file mode 100644
index 69c2473..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.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.artifacts.ivyservice.resolveengine.oldresult;
-
-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 org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-
-import java.util.Set;
-
-//builds old model of resolved dependency graph based on the result events
-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, ComponentMetaData component, ComponentArtifactMetaData artifact, ArtifactResolver artifactResolver);
-}
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
deleted file mode 100644
index 634f969..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java
+++ /dev/null
@@ -1,32 +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.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/TransientConfigurationResultsBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResultsBuilder.java
deleted file mode 100644
index b1f84d0..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResultsBuilder.java
+++ /dev/null
@@ -1,186 +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.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.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.Encoder;
-import org.gradle.util.Clock;
-
-import java.io.IOException;
-
-import static com.google.common.collect.Sets.newHashSet;
-import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
-
-//TODO SF unit coverage
-public class TransientConfigurationResultsBuilder {
-
-    private final static Logger LOG = Logging.getLogger(TransientConfigurationResultsBuilder.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 TransientConfigurationResultsBuilder(BinaryStore binaryStore, Store<TransientConfigurationResults> cache) {
-        this.binaryStore = binaryStore;
-        this.cache = cache;
-    }
-
-    private void writeId(final byte type, final ResolvedConfigurationIdentifier... ids) {
-        binaryStore.write(new BinaryStore.WriteAction() {
-            public void write(Encoder 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(Encoder encoder) throws IOException {
-                encoder.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(Decoder decoder) throws IOException {
-                                return deserialize(decoder, mapping);
-                            }
-                        });
-                    } finally {
-                        try {
-                            binaryData.close();
-                        } catch (IOException e) {
-                            throw throwAsUncheckedException(e);
-                        }
-                    }
-                }
-            });
-        }
-    }
-
-    private TransientConfigurationResults deserialize(Decoder decoder, ResolvedContentsMapping mapping) {
-        Clock clock = new Clock();
-        DefaultTransientConfigurationResults results = new DefaultTransientConfigurationResults();
-        int valuesRead = 0;
-        byte type = -1;
-        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
deleted file mode 100644
index 6dc5ecf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
+++ /dev/null
@@ -1,56 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedComponentResult;
-import org.gradle.api.artifacts.result.ResolvedDependencyResult;
-import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult;
-import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult;
-import org.gradle.api.internal.artifacts.result.DefaultUnresolvedDependencyResult;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static java.util.Arrays.asList;
-
-public class CachingDependencyResultFactory {
-
-    private final Map<List, DefaultUnresolvedDependencyResult> unresolvedDependencies = new HashMap<List, DefaultUnresolvedDependencyResult>();
-    private final Map<List, DefaultResolvedDependencyResult> resolvedDependencies = new HashMap<List, DefaultResolvedDependencyResult>();
-
-    public UnresolvedDependencyResult createUnresolvedDependency(ComponentSelector requested, ResolvedComponentResult from,
-                                                       ComponentSelectionReason reason, ModuleVersionResolveException failure) {
-        List<Object> key = asList(requested, from);
-        if (!unresolvedDependencies.containsKey(key)) {
-            unresolvedDependencies.put(key, new DefaultUnresolvedDependencyResult(requested, reason, from, failure));
-        }
-        return unresolvedDependencies.get(key);
-    }
-
-    public ResolvedDependencyResult createResolvedDependency(ComponentSelector requested, ResolvedComponentResult from, DefaultResolvedComponentResult selected) {
-        List<Object> key = asList(requested, from, selected);
-        if (!resolvedDependencies.containsKey(key)) {
-            resolvedDependencies.put(key, new DefaultResolvedDependencyResult(requested, selected, from));
-        }
-        return resolvedDependencies.get(key);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.java
deleted file mode 100644
index 4976933..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.java
+++ /dev/null
@@ -1,76 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.io.IOException;
-
-public class ComponentIdentifierSerializer implements Serializer<ComponentIdentifier> {
-    public ComponentIdentifier read(Decoder decoder) throws IOException {
-        byte id = decoder.readByte();
-
-        if(Implementation.BUILD.getId() == id) {
-            return new DefaultProjectComponentIdentifier(decoder.readString());
-        } else if(Implementation.MODULE.getId() == id) {
-            return new DefaultModuleComponentIdentifier(decoder.readString(), decoder.readString(), decoder.readString());
-        }
-
-        throw new IllegalArgumentException("Unable to find component identifier with id: " + id);
-    }
-
-    public void write(Encoder encoder, ComponentIdentifier value) throws IOException {
-        if(value == null) {
-            throw new IllegalArgumentException("Provided component identifier may not be null");
-        }
-
-        if(value instanceof DefaultModuleComponentIdentifier) {
-            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier)value;
-            encoder.writeByte(Implementation.MODULE.getId());
-            encoder.writeString(moduleComponentIdentifier.getGroup());
-            encoder.writeString(moduleComponentIdentifier.getModule());
-            encoder.writeString(moduleComponentIdentifier.getVersion());
-        } else if(value instanceof DefaultProjectComponentIdentifier) {
-            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier)value;
-            encoder.writeByte(Implementation.BUILD.getId());
-            encoder.writeString(projectComponentIdentifier.getProjectPath());
-        } else {
-            throw new IllegalArgumentException("Unsupported component identifier class: " + value.getClass());
-        }
-    }
-
-    private static enum Implementation {
-        MODULE((byte) 1), BUILD((byte) 2);
-
-        private final byte id;
-
-        private Implementation(byte id) {
-            this.id = id;
-        }
-
-        private byte getId() {
-            return id;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.java
deleted file mode 100644
index d041655..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.java
+++ /dev/null
@@ -1,59 +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.resolveengine.result;
-
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-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 ComponentSelectionReasonSerializer implements Serializer<ComponentSelectionReason> {
-
-    private static final BiMap<Byte, ComponentSelectionReason> 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 ComponentSelectionReason read(Decoder decoder) throws IOException {
-        byte id = decoder.readByte();
-        ComponentSelectionReason 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, ComponentSelectionReason 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/ComponentSelectorSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java
deleted file mode 100644
index 0846947..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java
+++ /dev/null
@@ -1,76 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.component.ProjectComponentSelector;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.io.IOException;
-
-public class ComponentSelectorSerializer implements Serializer<ComponentSelector> {
-    public ComponentSelector read(Decoder decoder) throws IOException {
-        byte id = decoder.readByte();
-
-        if(Implementation.BUILD.getId() == id) {
-            return new DefaultProjectComponentSelector(decoder.readString());
-        } else if(Implementation.MODULE.getId() == id) {
-            return new DefaultModuleComponentSelector(decoder.readString(), decoder.readString(), decoder.readString());
-        }
-
-        throw new IllegalArgumentException("Unable to find component selector with id: " + id);
-    }
-
-    public void write(Encoder encoder, ComponentSelector value) throws IOException {
-        if(value == null) {
-            throw new IllegalArgumentException("Provided component selector may not be null");
-        }
-
-        if(value instanceof DefaultModuleComponentSelector) {
-            ModuleComponentSelector moduleComponentSelector = (ModuleComponentSelector)value;
-            encoder.writeByte(Implementation.MODULE.getId());
-            encoder.writeString(moduleComponentSelector.getGroup());
-            encoder.writeString(moduleComponentSelector.getModule());
-            encoder.writeString(moduleComponentSelector.getVersion());
-        } else if(value instanceof DefaultProjectComponentSelector) {
-            ProjectComponentSelector projectComponentSelector = (ProjectComponentSelector)value;
-            encoder.writeByte(Implementation.BUILD.getId());
-            encoder.writeString(projectComponentSelector.getProjectPath());
-        } else {
-            throw new IllegalArgumentException("Unsupported component selector class: " + value.getClass());
-        }
-    }
-
-    private static enum Implementation {
-        MODULE((byte) 1), BUILD((byte) 2);
-
-        private final byte id;
-
-        private Implementation(byte id) {
-            this.id = id;
-        }
-
-        private byte getId() {
-            return id;
-        }
-    }
-}
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
deleted file mode 100644
index e87b0d4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java
+++ /dev/null
@@ -1,59 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-
-public class DefaultInternalDependencyResult implements InternalDependencyResult {
-
-    private final ComponentSelector requested;
-    private final ModuleVersionIdentifier selected;
-    private final ComponentSelectionReason reason;
-    private ModuleVersionResolveException failure;
-
-    public DefaultInternalDependencyResult(ComponentSelector requested,
-                                           ModuleVersionIdentifier selected,
-                                           ComponentSelectionReason reason,
-                                           ModuleVersionResolveException failure) {
-        assert requested != null;
-        assert failure != null || selected != null;
-
-        this.requested = requested;
-        this.reason = reason;
-        this.selected = selected;
-        this.failure = failure;
-    }
-
-    public ComponentSelector getRequested() {
-        return requested;
-    }
-
-    public ModuleVersionIdentifier getSelected() {
-        return selected;
-    }
-
-    public ComponentSelectionReason 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
deleted file mode 100644
index 5e54b98..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java
+++ /dev/null
@@ -1,45 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-
-class DefaultModuleVersionSelection implements ModuleVersionSelection {
-    private ModuleVersionIdentifier id;
-    private ComponentSelectionReason reason;
-    private ComponentIdentifier componentIdentifier;
-
-    public DefaultModuleVersionSelection(ModuleVersionIdentifier id, ComponentSelectionReason reason, ComponentIdentifier componentIdentifier) {
-        this.id = id;
-        this.reason = reason;
-        this.componentIdentifier = componentIdentifier;
-    }
-
-    public ModuleVersionIdentifier getSelectedId() {
-        return id;
-    }
-
-    public ComponentSelectionReason getSelectionReason() {
-        return reason;
-    }
-
-    public ComponentIdentifier getComponentId() {
-        return componentIdentifier;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
deleted file mode 100644
index 291404c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
+++ /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.api.internal.artifacts.ivyservice.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.*;
-import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
-import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult;
-import org.gradle.internal.Factory;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class DefaultResolutionResultBuilder implements ResolutionResultBuilder {
-
-    private DefaultResolvedComponentResult rootModule;
-
-    private Map<ModuleVersionIdentifier, DefaultResolvedComponentResult> modules
-            = new LinkedHashMap<ModuleVersionIdentifier, DefaultResolvedComponentResult>();
-
-    CachingDependencyResultFactory dependencyResultFactory = new CachingDependencyResultFactory();
-
-    public DefaultResolutionResultBuilder start(ModuleVersionIdentifier root, ComponentIdentifier componentIdentifier) {
-        rootModule = createOrGet(root, VersionSelectionReasons.ROOT, componentIdentifier);
-        return this;
-    }
-
-    public ResolutionResult complete() {
-        return new DefaultResolutionResult(new RootFactory(rootModule));
-    }
-
-    public void resolvedModuleVersion(ModuleVersionSelection moduleVersion) {
-        createOrGet(moduleVersion.getSelectedId(), moduleVersion.getSelectionReason(), moduleVersion.getComponentId());
-    }
-
-    public void resolvedConfiguration(ModuleVersionIdentifier id, Collection<? extends InternalDependencyResult> dependencies) {
-        for (InternalDependencyResult d : dependencies) {
-            DefaultResolvedComponentResult from = modules.get(id);
-            DependencyResult dependency;
-            if (d.getFailure() != null) {
-                dependency = dependencyResultFactory.createUnresolvedDependency(d.getRequested(), from, d.getReason(), d.getFailure());
-            } else {
-                DefaultResolvedComponentResult selected = modules.get(d.getSelected());
-                dependency = dependencyResultFactory.createResolvedDependency(d.getRequested(), from, selected);
-                selected.addDependent((ResolvedDependencyResult) dependency);
-            }
-            from.addDependency(dependency);
-        }
-    }
-
-    private DefaultResolvedComponentResult createOrGet(ModuleVersionIdentifier id, ComponentSelectionReason selectionReason, ComponentIdentifier componentId) {
-        if (!modules.containsKey(id)) {
-            modules.put(id, new DefaultResolvedComponentResult(id, selectionReason, componentId));
-        }
-        return modules.get(id);
-    }
-
-    private static class RootFactory implements Factory<ResolvedComponentResult> {
-        private DefaultResolvedComponentResult rootModule;
-
-        public RootFactory(DefaultResolvedComponentResult rootModule) {
-            this.rootModule = rootModule;
-        }
-
-        public ResolvedComponentResult 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/InternalDependencyResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
deleted file mode 100644
index 8b4c4c4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
+++ /dev/null
@@ -1,40 +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.resolveengine.result;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-
-public interface InternalDependencyResult {
-
-    ComponentSelector getRequested();
-
-    @Nullable
-    ModuleVersionResolveException getFailure();
-
-    @Nullable
-    ModuleVersionIdentifier getSelected();
-
-    /**
-     * Not null only when failure is not null.
-     */
-    @Nullable
-    ComponentSelectionReason getReason();
-}
\ No newline at end of file
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
deleted file mode 100644
index 826def5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java
+++ /dev/null
@@ -1,62 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
-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 ComponentSelectorSerializer componentSelectorSerializer = new ComponentSelectorSerializer();
-    private final ComponentSelectionReasonSerializer componentSelectionReasonSerializer = new ComponentSelectionReasonSerializer();
-    private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
-
-    public InternalDependencyResult read(Decoder decoder, Map<ComponentSelector, ModuleVersionResolveException> failures) throws IOException {
-        ComponentSelector requested = componentSelectorSerializer.read(decoder);
-        byte resultByte = decoder.readByte();
-        if (resultByte == SUCCESSFUL) {
-            ModuleVersionIdentifier selected = moduleVersionIdentifierSerializer.read(decoder);
-            return new DefaultInternalDependencyResult(requested, selected, null, null);
-        } else if (resultByte == FAILED) {
-            ComponentSelectionReason reason = componentSelectionReasonSerializer.read(decoder);
-            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 {
-        componentSelectorSerializer.write(encoder, value.getRequested());
-        if (value.getFailure() == null) {
-            encoder.writeByte(SUCCESSFUL);
-            moduleVersionIdentifierSerializer.write(encoder, value.getSelected());
-        } else {
-            encoder.writeByte(FAILED);
-            componentSelectionReasonSerializer.write(encoder, value.getReason());
-        }
-    }
-}
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
deleted file mode 100644
index 63845b1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java
+++ /dev/null
@@ -1,30 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-
-public interface ModuleVersionSelection {
-
-    ModuleVersionIdentifier getSelectedId();
-
-    ComponentSelectionReason getSelectionReason();
-
-    ComponentIdentifier getComponentId();
-}
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
deleted file mode 100644
index 26a4b23..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java
+++ /dev/null
@@ -1,47 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-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 ComponentSelectionReasonSerializer reasonSerializer = new ComponentSelectionReasonSerializer();
-    private final ComponentIdentifierSerializer componentIdSerializer = new ComponentIdentifierSerializer();
-
-    public ModuleVersionSelection read(Decoder decoder) throws IOException {
-        ModuleVersionIdentifier id = idSerializer.read(decoder);
-        ComponentSelectionReason reason = reasonSerializer.read(decoder);
-        ComponentIdentifier componentId = componentIdSerializer.read(decoder);
-        return new DefaultModuleVersionSelection(id, reason, componentId);
-    }
-
-    public void write(Encoder encoder, ModuleVersionSelection value) throws IOException {
-        idSerializer.write(encoder, value.getSelectedId());
-        reasonSerializer.write(encoder, value.getSelectionReason());
-        componentIdSerializer.write(encoder, value.getComponentId());
-    }
-}
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
deleted file mode 100644
index 91f85f1..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
+++ /dev/null
@@ -1,200 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.result.ResolutionResult;
-import org.gradle.api.artifacts.result.ResolvedComponentResult;
-import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-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.Encoder;
-import org.gradle.util.Clock;
-
-import java.io.IOException;
-import java.util.*;
-
-import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
-
-public class StreamingResolutionResultBuilder implements ResolutionResultBuilder {
-
-    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<ComponentSelector, ModuleVersionResolveException> failures = new HashMap<ComponentSelector, ModuleVersionResolveException>();
-    private final BinaryStore store;
-    private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
-    private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
-    private final Store<ResolvedComponentResult> cache;
-    private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
-    private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
-
-    public StreamingResolutionResultBuilder(BinaryStore store, Store<ResolvedComponentResult> cache) {
-        this.store = store;
-        this.cache = cache;
-    }
-
-    public ResolutionResult complete() {
-        store.write(new BinaryStore.WriteAction() {
-            public void write(Encoder encoder) throws IOException {
-                encoder.writeByte(DONE);
-            }
-        });
-        BinaryStore.BinaryData data = store.done();
-        RootFactory rootSource = new RootFactory(data, failures, cache);
-        return new DefaultResolutionResult(rootSource);
-    }
-
-    public ResolutionResultBuilder start(final ModuleVersionIdentifier root, final ComponentIdentifier componentIdentifier) {
-        store.write(new BinaryStore.WriteAction() {
-            public void write(Encoder encoder) throws IOException {
-                encoder.writeByte(ROOT);
-                moduleVersionIdentifierSerializer.write(encoder, root);
-                componentIdentifierSerializer.write(encoder, componentIdentifier);
-            }
-        });
-        return this;
-    }
-
-    Set<ModuleVersionIdentifier> visitedModules = new HashSet<ModuleVersionIdentifier>();
-
-    public void resolvedModuleVersion(final ModuleVersionSelection moduleVersion) {
-        if (visitedModules.add(moduleVersion.getSelectedId())) {
-            store.write(new BinaryStore.WriteAction() {
-                public void write(Encoder 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 BinaryStore.WriteAction() {
-                public void write(Encoder encoder) throws IOException {
-                    encoder.writeByte(DEPENDENCY);
-                    moduleVersionIdentifierSerializer.write(encoder, from);
-                    encoder.writeSmallInt(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<ResolvedComponentResult> {
-
-        private final static Logger LOG = Logging.getLogger(RootFactory.class);
-        private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
-
-        private final BinaryStore.BinaryData data;
-        private final Map<ComponentSelector, ModuleVersionResolveException> failures;
-        private final Store<ResolvedComponentResult> cache;
-        private final Object lock = new Object();
-        private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
-        private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
-        private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
-
-        public RootFactory(BinaryStore.BinaryData data, Map<ComponentSelector, ModuleVersionResolveException> failures,
-                           Store<ResolvedComponentResult> cache) {
-            this.data = data;
-            this.failures = failures;
-            this.cache = cache;
-        }
-
-        public ResolvedComponentResult create() {
-            synchronized (lock) {
-                return cache.load(new Factory<ResolvedComponentResult>() {
-                    public ResolvedComponentResult create() {
-                        try {
-                            return data.read(new BinaryStore.ReadAction<ResolvedComponentResult>() {
-                                public ResolvedComponentResult read(Decoder decoder) throws IOException {
-                                    return deserialize(decoder);
-                                }
-                            });
-                        } finally {
-                            try {
-                                data.close();
-                            } catch (IOException e) {
-                                throw throwAsUncheckedException(e);
-                            }
-                        }
-                    }
-                });
-            }
-        }
-
-        private ResolvedComponentResult deserialize(Decoder decoder) {
-            int valuesRead = 0;
-            byte type = -1;
-            Clock clock = new Clock();
-            try {
-                DefaultResolutionResultBuilder builder = new DefaultResolutionResultBuilder();
-                while (true) {
-                    type = decoder.readByte();
-                    valuesRead++;
-                    switch (type) {
-                        case ROOT:
-                            ModuleVersionIdentifier id = moduleVersionIdentifierSerializer.read(decoder);
-                            ComponentIdentifier componentIdentifier = componentIdentifierSerializer.read(decoder);
-                            builder.start(id, componentIdentifier);
-                            break;
-                        case MODULE:
-                            ModuleVersionSelection sel = moduleVersionSelectionSerializer.read(decoder);
-                            builder.resolvedModuleVersion(sel);
-                            break;
-                        case DEPENDENCY:
-                            id = moduleVersionIdentifierSerializer.read(decoder);
-                            int size = decoder.readSmallInt();
-                            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:
-                            ResolvedComponentResult 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
deleted file mode 100644
index 96cf754..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java
+++ /dev/null
@@ -1,81 +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.resolveengine.result;
-
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-
-public class VersionSelectionReasons {
-    public static final ComponentSelectionReason REQUESTED = new DefaultComponentSelectionReason(false, false, false, true, "requested");
-    public static final ComponentSelectionReason ROOT = new DefaultComponentSelectionReason(false, false, false, true, "root");
-    public static final ComponentSelectionReason FORCED = new DefaultComponentSelectionReason(true, false, false, false, "forced");
-    public static final ComponentSelectionReason CONFLICT_RESOLUTION = new DefaultComponentSelectionReason(false, true, false, false, "conflict resolution");
-    public static final ComponentSelectionReason SELECTED_BY_RULE = new DefaultComponentSelectionReason(false, false, true, false, "selected by rule");
-    public static final ComponentSelectionReason CONFLICT_RESOLUTION_BY_RULE = new DefaultComponentSelectionReason(false, true, true, false, "selected by rule and conflict resolution");
-
-    public static ComponentSelectionReason withConflictResolution(ComponentSelectionReason reason) {
-        if (reason.isConflictResolution()) {
-            return reason;
-        } else if (reason == SELECTED_BY_RULE) {
-            return CONFLICT_RESOLUTION_BY_RULE;
-        } else if (reason == REQUESTED) {
-            return CONFLICT_RESOLUTION;
-        }
-        throw new IllegalArgumentException("Cannot create conflict resolution selection reason for input: " + reason);
-    }
-
-    private static class DefaultComponentSelectionReason implements ComponentSelectionReason {
-
-        private final boolean forced;
-        private final boolean conflictResolution;
-        private final boolean selectedByRule;
-        private final boolean expected;
-        private final String description;
-
-        private DefaultComponentSelectionReason(boolean forced, boolean conflictResolution, boolean selectedByRule, boolean expected, String description) {
-            this.forced = forced;
-            this.conflictResolution = conflictResolution;
-            this.selectedByRule = selectedByRule;
-            this.expected = expected;
-            assert description != null;
-            this.description = description;
-        }
-
-        public boolean isForced() {
-            return forced;
-        }
-
-        public boolean isConflictResolution() {
-            return conflictResolution;
-        }
-
-        public boolean isSelectedByRule() {
-            return selectedByRule;
-        }
-
-        public boolean isExpected() {
-            return expected;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public String toString() {
-            return description;
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 931211b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java
+++ /dev/null
@@ -1,102 +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.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.TimeUnit;
-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).expireAfterAccess(10000, TimeUnit.MILLISECONDS).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
deleted file mode 100644
index 57df3b5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java
+++ /dev/null
@@ -1,147 +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.resolveengine.store;
-
-import org.gradle.api.internal.cache.BinaryStore;
-import org.gradle.internal.concurrent.CompositeStoppable;
-import org.gradle.internal.io.RandomAccessFileInputStream;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
-
-import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
-
-class DefaultBinaryStore implements BinaryStore {
-    private File file;
-    private KryoBackedEncoder encoder;
-    private int offset = -1;
-
-    public DefaultBinaryStore(File file) {
-        this.file = file;
-    }
-
-    public void write(WriteAction write) {
-        if (encoder == null) {
-            try {
-                encoder = new KryoBackedEncoder(new FileOutputStream(file));
-            } catch (FileNotFoundException e) {
-                throw throwAsUncheckedException(e);
-            }
-        }
-        if (offset == -1) {
-            offset = encoder.getWritePosition();
-            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(encoder);
-        } catch (Exception 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 (encoder != null) {
-                encoder.flush();
-            }
-            return new SimpleBinaryData(file, offset, diagnose());
-        } finally {
-            offset = -1;
-        }
-    }
-
-    public void close() {
-        try {
-            if (encoder != null) {
-                encoder.close();
-            }
-        } finally {
-            file.delete();
-            encoder = null;
-            file = null;
-        }
-    }
-
-    File getFile() {
-        return file;
-    }
-
-    long getSize() {
-        return file.length();
-    }
-
-    private static class SimpleBinaryData implements BinaryStore.BinaryData {
-        private final int offset;
-        private final File inputFile;
-        private final String sourceDescription;
-
-        private Decoder decoder;
-        private CompositeStoppable resources;
-
-        public SimpleBinaryData(File inputFile, int offset, String sourceDescription) {
-            this.inputFile = inputFile;
-            this.offset = offset;
-            this.sourceDescription = sourceDescription;
-        }
-
-        public <T> T read(BinaryStore.ReadAction<T> readAction) {
-            try {
-                if (decoder == null) {
-                    RandomAccessFile randomAccess = new RandomAccessFile(inputFile, "r");
-                    randomAccess.seek(offset);
-                    decoder = new KryoBackedDecoder(new RandomAccessFileInputStream(randomAccess));
-                    resources = new CompositeStoppable().add(randomAccess, decoder);
-                }
-                return readAction.read(decoder);
-            } catch (Exception e) {
-                throw new RuntimeException("Problems reading data from " + sourceDescription, e);
-            }
-        }
-
-        public void close() {
-            try {
-                if (resources != null) {
-                    resources.stop();
-                }
-            } catch (Exception e) {
-                throw new RuntimeException("Problems cleaning resources of " + sourceDescription, e);
-            } finally {
-                decoder = null;
-                resources = null;
-            }
-        }
-
-        public String toString() {
-            return sourceDescription;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/AbstractModuleDescriptorBackedMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/AbstractModuleDescriptorBackedMetaData.java
deleted file mode 100644
index b4e208f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/AbstractModuleDescriptorBackedMetaData.java
+++ /dev/null
@@ -1,233 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.*;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-import org.gradle.util.CollectionUtils;
-
-import java.util.*;
-
-public abstract class AbstractModuleDescriptorBackedMetaData implements ComponentMetaData {
-    private static final List<String> DEFAULT_STATUS_SCHEME = Arrays.asList("integration", "milestone", "release");
-
-    private final ModuleVersionIdentifier moduleVersionIdentifier;
-    private final ModuleDescriptor moduleDescriptor;
-    private final ComponentIdentifier componentIdentifier;
-    private ModuleSource moduleSource;
-    private boolean changing;
-    private String status;
-    private List<String> statusScheme = DEFAULT_STATUS_SCHEME;
-    private List<DependencyMetaData> dependencies;
-    private Map<String, DefaultConfigurationMetaData> configurations = new HashMap<String, DefaultConfigurationMetaData>();
-
-    public AbstractModuleDescriptorBackedMetaData(ModuleVersionIdentifier moduleVersionIdentifier, ModuleDescriptor moduleDescriptor, ComponentIdentifier componentIdentifier) {
-        this.moduleVersionIdentifier = moduleVersionIdentifier;
-        this.moduleDescriptor = moduleDescriptor;
-        this.componentIdentifier = componentIdentifier;
-        status = moduleDescriptor.getStatus();
-    }
-
-    protected void copyTo(AbstractModuleDescriptorBackedMetaData copy) {
-        copy.dependencies = dependencies;
-        copy.changing = changing;
-        copy.status = status;
-        copy.statusScheme = statusScheme;
-        copy.moduleSource = moduleSource;
-    }
-
-    @Override
-    public String toString() {
-        return moduleVersionIdentifier.toString();
-    }
-
-    public ModuleVersionIdentifier getId() {
-        return moduleVersionIdentifier;
-    }
-
-    public ModuleSource getSource() {
-        return moduleSource;
-    }
-
-    public void setModuleSource(ModuleSource moduleSource) {
-        this.moduleSource = moduleSource;
-    }
-
-    public ModuleDescriptor getDescriptor() {
-        return moduleDescriptor;
-    }
-
-    public boolean isChanging() {
-        return changing;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public List<String> getStatusScheme() {
-        return statusScheme;
-    }
-
-    public ComponentIdentifier getComponentId() {
-        return componentIdentifier;
-    }
-
-    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;
-    }
-
-    public List<DependencyMetaData> getDependencies() {
-        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) {
-        this.dependencies = CollectionUtils.toList(dependencies);
-        for (DefaultConfigurationMetaData configuration : configurations.values()) {
-            configuration.dependencies = null;
-        }
-    }
-
-    public DefaultConfigurationMetaData getConfiguration(final String name) {
-        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(name, descriptor, hierarchy);
-            configurations.put(name, configuration);
-        }
-        return configuration;
-    }
-
-    protected abstract Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configuration);
-
-    private class DefaultConfigurationMetaData implements ConfigurationMetaData {
-        private final String name;
-        private final Configuration descriptor;
-        private final Set<String> hierarchy;
-        private List<DependencyMetaData> dependencies;
-        private Set<ComponentArtifactMetaData> artifacts;
-        private LinkedHashSet<ExcludeRule> excludeRules;
-
-        private DefaultConfigurationMetaData(String name, Configuration descriptor, Set<String> hierarchy) {
-            this.name = name;
-            this.descriptor = descriptor;
-            this.hierarchy = hierarchy;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("%s:%s", moduleVersionIdentifier, name);
-        }
-
-        public ComponentMetaData getComponent() {
-            return AbstractModuleDescriptorBackedMetaData.this;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        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 : AbstractModuleDescriptorBackedMetaData.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<ComponentArtifactMetaData> getArtifacts() {
-            if (artifacts == null) {
-                artifacts = getArtifactsForConfiguration(this);
-            }
-            return artifacts;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/BuildableModuleVersionPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/BuildableModuleVersionPublishMetaData.java
deleted file mode 100644
index 911aa07..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/BuildableModuleVersionPublishMetaData.java
+++ /dev/null
@@ -1,27 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-
-import java.io.File;
-
-public interface BuildableModuleVersionPublishMetaData extends ModuleVersionPublishMetaData {
-    void addArtifact(ModuleVersionArtifactPublishMetaData artifact);
-
-    void addArtifact(Artifact artifact, File file);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactIdentifier.java
deleted file mode 100644
index d77e76e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactIdentifier.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.metadata;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * An immutable identifier for an artifact that belongs to some component instance.
- */
-public interface ComponentArtifactIdentifier {
-    /**
-     * Returns the id of the component that this artifact belongs to.
-     */
-    ComponentIdentifier getComponentIdentifier();
-
-    /**
-     * Returns some human-consumable display name for this artifact.
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactMetaData.java
deleted file mode 100644
index 3a0f67a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentArtifactMetaData.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.metadata;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * Meta-data for an artifact that belongs to some component.
- */
-public interface ComponentArtifactMetaData {
-    /**
-     * Returns the identifier for this artifact.
-     */
-    ComponentArtifactIdentifier getId();
-
-    /**
-     * Returns the identifier for the component that this artifact belongs to.
-     */
-    ComponentIdentifier getComponentId();
-
-    /**
-     * Returns this artifact as an Ivy artifact. This method is here to allow the artifact to be exposed in a backward-compatible way.
-     */
-    IvyArtifactName getName();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentMetaData.java
deleted file mode 100644
index f5321c7..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ComponentMetaData.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * The meta-data for a component instance that is required during dependency resolution.
- */
-public interface ComponentMetaData {
-    /**
-     * Returns the identifier for this component.
-     */
-    ComponentIdentifier getComponentId();
-
-    /**
-     * Returns the module version identifier for this component. This is a legacy identifier and is here while we transition the meta-data away from ivy-like
-     * module versions to the more general component instances. Currently, the module version and component identifiers are used interchangeably, however, over
-     * time more things will use the component identifier. At some point, the module version identifier will become optional for a component.
-     */
-    ModuleVersionIdentifier getId();
-
-    /**
-     * Returns the source (eg location) for this component.
-     */
-    ModuleSource getSource();
-
-    /**
-     * Makes a copy of this meta-data with the given source.
-     */
-    ComponentMetaData withSource(ModuleSource source);
-
-    /**
-     * Returns this module version as an Ivy ModuleDescriptor. This method is here to allow us to migrate away from the Ivy types
-     * and will be removed.
-     */
-    ModuleDescriptor getDescriptor();
-
-    List<DependencyMetaData> getDependencies();
-
-    /**
-     * Locates the configuration with the given name, if any.
-     */
-    @Nullable
-    ConfigurationMetaData getConfiguration(String name);
-
-    /**
-     * Converts the given Ivy artifact to the corresponding artifact meta-data. This method is here to allow us to migrate away from the Ivy types and
-     * will be removed.
-     */
-    ComponentArtifactMetaData artifact(Artifact artifact);
-
-    /**
-     * Returns the known artifacts for this component. There may be additional component available that are not included in this set.
-     */
-    Set<? extends ComponentArtifactMetaData> getArtifacts();
-
-    boolean isChanging();
-
-    String getStatus();
-
-    List<String> getStatusScheme();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ConfigurationMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ConfigurationMetaData.java
deleted file mode 100644
index da408e2..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ConfigurationMetaData.java
+++ /dev/null
@@ -1,41 +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.metadata;
-
-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();
-
-    ComponentMetaData getComponent();
-
-    List<DependencyMetaData> getDependencies();
-
-    Set<ComponentArtifactMetaData> getArtifacts();
-
-    Set<ExcludeRule> getExcludeRules();
-
-    boolean isTransitive();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaData.java
deleted file mode 100644
index c665773..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaData.java
+++ /dev/null
@@ -1,122 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.*;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector;
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ReflectiveDependencyDescriptorFactory;
-import org.gradle.internal.UncheckedException;
-
-import java.lang.reflect.Field;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class DefaultDependencyMetaData implements DependencyMetaData {
-    private final DependencyDescriptor dependencyDescriptor;
-    private final DefaultModuleVersionSelector requested;
-
-    public DefaultDependencyMetaData(DependencyDescriptor dependencyDescriptor) {
-        this.dependencyDescriptor = dependencyDescriptor;
-        ModuleRevisionId dependencyRevisionId = dependencyDescriptor.getDependencyRevisionId();
-        requested = new DefaultModuleVersionSelector(dependencyRevisionId.getOrganisation(), dependencyRevisionId.getName(), dependencyRevisionId.getRevision());
-    }
-
-    @Override
-    public String toString() {
-        return dependencyDescriptor.toString();
-    }
-
-    public ModuleVersionSelector getRequested() {
-        return requested;
-    }
-
-    public boolean isChanging() {
-        return dependencyDescriptor.isChanging();
-    }
-
-    public boolean isTransitive() {
-        return dependencyDescriptor.isTransitive();
-    }
-
-    public DependencyDescriptor getDescriptor() {
-        return dependencyDescriptor;
-    }
-
-    public Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData fromConfiguration, ConfigurationMetaData toConfiguration) {
-        String[] targetConfigurations = fromConfiguration.getHierarchy().toArray(new String[fromConfiguration.getHierarchy().size()]);
-        DependencyArtifactDescriptor[] dependencyArtifacts = dependencyDescriptor.getDependencyArtifacts(targetConfigurations);
-        if (dependencyArtifacts.length == 0) {
-            return Collections.emptySet();
-        }
-        Set<ComponentArtifactMetaData> artifacts = new LinkedHashSet<ComponentArtifactMetaData>();
-        for (DependencyArtifactDescriptor artifactDescriptor : dependencyArtifacts) {
-            ModuleRevisionId id = toConfiguration.getComponent().getDescriptor().getModuleRevisionId();
-            Artifact artifact = new DefaultArtifact(id, null, artifactDescriptor.getName(), artifactDescriptor.getType(), artifactDescriptor.getExt(), artifactDescriptor.getUrl(), artifactDescriptor.getQualifiedExtraAttributes());
-            artifacts.add(toConfiguration.getComponent().artifact(artifact));
-        }
-        return artifacts;
-    }
-
-    public DependencyMetaData withRequestedVersion(String requestedVersion) {
-        if (requestedVersion.equals(requested.getVersion())) {
-            return this;
-        }
-        return new DefaultDependencyMetaData(dependencyDescriptor.clone(IvyUtil.createModuleRevisionId(dependencyDescriptor.getDependencyRevisionId(), requestedVersion)));
-    }
-
-    public DependencyMetaData withRequestedVersion(ModuleVersionSelector requestedVersion) {
-        if (requestedVersion.equals(requested)) {
-            return this;
-        }
-
-        ModuleRevisionId requestedId = IvyUtil.createModuleRevisionId(requestedVersion.getGroup(), requestedVersion.getName(), requestedVersion.getVersion());
-        DependencyDescriptor substitutedDescriptor = new ReflectiveDependencyDescriptorFactory().create(dependencyDescriptor, requestedId);
-        return new DefaultDependencyMetaData(substitutedDescriptor);
-    }
-
-    public DependencyMetaData withChanging() {
-        if (dependencyDescriptor.isChanging()) {
-            return this;
-        }
-
-        DependencyDescriptor forcedChanging = dependencyDescriptor.clone(dependencyDescriptor.getDependencyRevisionId());
-        try {
-            Field field = DefaultDependencyDescriptor.class.getDeclaredField("isChanging");
-            field.setAccessible(true);
-            field.set(forcedChanging, true);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-        return new DefaultDependencyMetaData(forcedChanging);
-    }
-
-    public ComponentSelector getSelector() {
-        if(dependencyDescriptor instanceof ProjectDependencyDescriptor) {
-            return new DefaultProjectComponentSelector(((ProjectDependencyDescriptor)dependencyDescriptor).getTargetProject().getPath());
-        }
-
-        return DefaultModuleComponentSelector.newSelector(requested.getGroup(), requested.getName(), requested.getVersion());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactName.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactName.java
deleted file mode 100644
index 37628ac..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactName.java
+++ /dev/null
@@ -1,97 +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.metadata;
-
-import com.google.common.base.Objects;
-import org.gradle.api.Nullable;
-import org.gradle.util.GUtil;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-public class DefaultIvyArtifactName implements IvyArtifactName {
-    private static final String CLASSIFIER = "classifier";
-    private final String name;
-    private final String type;
-    private final String extension;
-    private final Map<String, String> attributes;
-
-    public DefaultIvyArtifactName(String name, String type, @Nullable String extension, Map<String, String> attributes) {
-        this.name = name;
-        this.type = type;
-        this.extension = extension;
-        this.attributes = attributes.isEmpty() ? Collections.<String, String>emptyMap() : new HashMap<String, String>(attributes);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append(name);
-        String classifier = attributes.get(CLASSIFIER);
-        if (GUtil.isTrue(classifier)) {
-            result.append("-");
-            result.append(classifier);
-        }
-        if (GUtil.isTrue(extension)) {
-            result.append(".");
-            result.append(extension);
-        }
-        return result.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        return name.hashCode() ^ type.hashCode() ^ (extension == null ? 0 : extension.hashCode()) ^ attributes.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultIvyArtifactName other = (DefaultIvyArtifactName) obj;
-        return other.name.equals(name)
-                && other.type.equals(type)
-                && Objects.equal(other.extension, extension)
-                && other.attributes.equals(attributes);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public String getExtension() {
-        return extension;
-    }
-
-    @Nullable
-    public String getClassifier() {
-        return attributes.get(CLASSIFIER);
-    }
-
-    public Map<String, String> getAttributes() {
-        return attributes;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifier.java
deleted file mode 100644
index c69a34f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifier.java
+++ /dev/null
@@ -1,69 +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.metadata;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-import java.util.Map;
-
-public class DefaultLocalArtifactIdentifier implements ComponentArtifactIdentifier {
-    private final ComponentIdentifier componentIdentifier;
-    private final String componentDisplayName;
-    private final IvyArtifactName name;
-
-    // The componentDisplayName parameter is temporary
-    public DefaultLocalArtifactIdentifier(ComponentIdentifier componentIdentifier, String componentDisplayName, String name, String type, @Nullable String extension, Map<String, String> attributes) {
-        this.componentIdentifier = componentIdentifier;
-        this.componentDisplayName = componentDisplayName;
-        this.name = new DefaultIvyArtifactName(name, type, extension, attributes);
-    }
-
-    public String getDisplayName() {
-        return String.format("%s:%s", componentDisplayName, name);
-    }
-
-    public IvyArtifactName getName() {
-        return name;
-    }
-
-    public ComponentIdentifier getComponentIdentifier() {
-        return componentIdentifier;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    @Override
-    public int hashCode() {
-        return componentIdentifier.hashCode() ^ name.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultLocalArtifactIdentifier other = (DefaultLocalArtifactIdentifier) obj;
-        return other.componentIdentifier.equals(componentIdentifier) && other.name.equals(name);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaData.java
deleted file mode 100644
index 48b91ae..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaData.java
+++ /dev/null
@@ -1,157 +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.metadata;
-
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.id.ArtifactRevisionId;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-
-import java.io.File;
-import java.util.*;
-
-public class DefaultLocalComponentMetaData implements MutableLocalComponentMetaData {
-    private final Map<ComponentArtifactIdentifier, DefaultLocalArtifactMetaData> artifactsById = new LinkedHashMap<ComponentArtifactIdentifier, DefaultLocalArtifactMetaData>();
-    private final Map<ArtifactRevisionId, DefaultLocalArtifactMetaData> artifactsByIvy = new LinkedHashMap<ArtifactRevisionId, DefaultLocalArtifactMetaData>();
-    private final Multimap<String, DefaultLocalArtifactMetaData> artifactsByConfig = LinkedHashMultimap.create();
-    private final DefaultModuleDescriptor moduleDescriptor;
-    private final ModuleVersionIdentifier id;
-    private final ComponentIdentifier componentIdentifier;
-
-    public DefaultLocalComponentMetaData(DefaultModuleDescriptor moduleDescriptor, ComponentIdentifier componentIdentifier) {
-        this.moduleDescriptor = moduleDescriptor;
-        id = DefaultModuleVersionIdentifier.newId(moduleDescriptor.getModuleRevisionId());
-        this.componentIdentifier = componentIdentifier;
-    }
-
-    public ModuleVersionIdentifier getId() {
-        return id;
-    }
-
-    public DefaultModuleDescriptor getModuleDescriptor() {
-        return moduleDescriptor;
-    }
-
-    public void addArtifact(String configuration, Artifact artifact, File file) {
-        moduleDescriptor.addArtifact(configuration, artifact);
-        DefaultLocalArtifactMetaData artifactMetaData = new DefaultLocalArtifactMetaData(componentIdentifier, id.toString(), artifact, file);
-        artifactsById.put(artifactMetaData.id, artifactMetaData);
-        artifactsByConfig.put(configuration, artifactMetaData);
-        artifactsByIvy.put(artifact.getId(), artifactMetaData);
-    }
-
-    public Collection<? extends LocalArtifactMetaData> getArtifacts() {
-        return artifactsByConfig.values();
-    }
-
-    public LocalArtifactMetaData getArtifact(ComponentArtifactIdentifier artifactIdentifier) {
-        return artifactsById.get(artifactIdentifier);
-    }
-
-    public ComponentMetaData toResolveMetaData() {
-        return new LocalComponentResolveMetaData();
-    }
-
-    public BuildableModuleVersionPublishMetaData toPublishMetaData() {
-        DefaultModuleVersionPublishMetaData publishMetaData = new DefaultModuleVersionPublishMetaData(id);
-        for (DefaultLocalArtifactMetaData artifact : artifactsById.values()) {
-            publishMetaData.addArtifact(artifact.artifact, artifact.file);
-        }
-        return publishMetaData;
-    }
-
-    private static class DefaultLocalArtifactMetaData implements LocalArtifactMetaData {
-        private final ComponentIdentifier componentIdentifier;
-        private final DefaultLocalArtifactIdentifier id;
-        private final Artifact artifact;
-        private final File file;
-
-        private DefaultLocalArtifactMetaData(ComponentIdentifier componentIdentifier, String displayName, Artifact artifact, File file) {
-            this.componentIdentifier = componentIdentifier;
-            Map<String, String> attrs = new HashMap<String, String>();
-            attrs.putAll(artifact.getExtraAttributes());
-            attrs.put("file", file == null ? "null" : file.getAbsolutePath());
-            this.id = new DefaultLocalArtifactIdentifier(componentIdentifier, displayName, artifact.getName(), artifact.getType(), artifact.getExt(), attrs);
-            this.artifact = artifact;
-            this.file = file;
-        }
-
-        @Override
-        public String toString() {
-            return id.toString();
-        }
-
-        public IvyArtifactName getName() {
-            return id.getName();
-        }
-
-        public ComponentIdentifier getComponentId() {
-            return componentIdentifier;
-        }
-
-        public ComponentArtifactIdentifier getId() {
-            return id;
-        }
-
-        public File getFile() {
-            return file;
-        }
-    }
-
-    private class LocalComponentResolveMetaData extends AbstractModuleDescriptorBackedMetaData {
-        public LocalComponentResolveMetaData() {
-            // TODO:ADAM - need to clone the descriptor
-            super(id, moduleDescriptor, componentIdentifier);
-        }
-
-        public MutableModuleVersionMetaData copy() {
-            throw new UnsupportedOperationException();
-        }
-
-        public ModuleVersionMetaData withSource(ModuleSource source) {
-            throw new UnsupportedOperationException();
-        }
-
-        public ComponentArtifactMetaData artifact(Artifact artifact) {
-            DefaultLocalArtifactMetaData candidate = artifactsByIvy.get(artifact.getId());
-            return candidate != null ? candidate : new DefaultLocalArtifactMetaData(componentIdentifier, id.toString(), artifact, null);
-        }
-
-        public Set<ComponentArtifactMetaData> getArtifacts() {
-            return new LinkedHashSet<ComponentArtifactMetaData>(artifactsById.values());
-        }
-
-        @Override
-        protected Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configurationMetaData) {
-            Set<ComponentArtifactMetaData> result = new LinkedHashSet<ComponentArtifactMetaData>();
-            Set<ComponentArtifactIdentifier> seen = new HashSet<ComponentArtifactIdentifier>();
-            for (String configName : configurationMetaData.getHierarchy()) {
-                for (DefaultLocalArtifactMetaData localArtifactMetaData : artifactsByConfig.get(configName)) {
-                    if (seen.add(localArtifactMetaData.id)) {
-                        result.add(localArtifactMetaData);
-                    }
-                }
-            }
-            return result;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifier.java
deleted file mode 100644
index 8310733..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifier.java
+++ /dev/null
@@ -1,79 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class DefaultModuleVersionArtifactIdentifier implements ModuleVersionArtifactIdentifier {
-    private final ModuleComponentIdentifier componentIdentifier;
-    private final IvyArtifactName name;
-
-    public DefaultModuleVersionArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, Artifact artifact) {
-        this(componentIdentifier, artifact.getName(), artifact.getType(), artifact.getExt(), artifact.getExtraAttributes());
-    }
-
-    public DefaultModuleVersionArtifactIdentifier(ModuleVersionIdentifier moduleVersionIdentifier, String name, String type, @Nullable String extension) {
-        this(DefaultModuleComponentIdentifier.newId(moduleVersionIdentifier), name, type, extension, Collections.<String, String>emptyMap());
-    }
-
-    public DefaultModuleVersionArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, String name, String type, @Nullable String extension, Map<String, String> attributes) {
-        this.componentIdentifier = componentIdentifier;
-        this.name = new DefaultIvyArtifactName(name, type, extension, attributes);
-    }
-
-    public String getDisplayName() {
-        return String.format("%s:%s", componentIdentifier, name);
-    }
-
-    public IvyArtifactName getName() {
-        return name;
-    }
-
-    public ModuleComponentIdentifier getComponentIdentifier() {
-        return componentIdentifier;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    @Override
-    public int hashCode() {
-        return componentIdentifier.hashCode() ^ name.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (obj == null || obj.getClass() != getClass()) {
-            return false;
-        }
-        DefaultModuleVersionArtifactIdentifier other = (DefaultModuleVersionArtifactIdentifier) obj;
-        return other.componentIdentifier.equals(componentIdentifier)
-                && other.name.equals(name);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaData.java
deleted file mode 100644
index 5324c51..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaData.java
+++ /dev/null
@@ -1,63 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultArtifact;
-import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-
-public class DefaultModuleVersionArtifactMetaData implements ModuleVersionArtifactMetaData {
-    private final DefaultModuleVersionArtifactIdentifier id;
-
-    public DefaultModuleVersionArtifactMetaData(ModuleComponentIdentifier componentIdentifier, Artifact artifact) {
-        this(new DefaultModuleVersionArtifactIdentifier(componentIdentifier, artifact));
-    }
-
-    public DefaultModuleVersionArtifactMetaData(ModuleVersionArtifactIdentifier moduleVersionArtifactIdentifier) {
-        this.id = (DefaultModuleVersionArtifactIdentifier) moduleVersionArtifactIdentifier;
-    }
-
-    @Override
-    public String toString() {
-        return id.toString();
-    }
-
-    public ModuleVersionArtifactIdentifier getId() {
-        return id;
-    }
-
-    public ComponentIdentifier getComponentId() {
-        return id.getComponentIdentifier();
-    }
-
-    public ArtifactIdentifier toArtifactIdentifier() {
-        return new DefaultArtifactIdentifier(id);
-    }
-
-    public Artifact toIvyArtifact() {
-        IvyArtifactName ivyArtifactName = id.getName();
-        return new DefaultArtifact(IvyUtil.createModuleRevisionId(id.getComponentIdentifier()), null, ivyArtifactName.getName(), ivyArtifactName.getType(), ivyArtifactName.getExtension(), ivyArtifactName.getAttributes());
-    }
-
-    public IvyArtifactName getName() {
-        return id.getName();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaData.java
deleted file mode 100644
index 6e45f24..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaData.java
+++ /dev/null
@@ -1,84 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class DefaultModuleVersionPublishMetaData implements BuildableModuleVersionPublishMetaData {
-    private final ModuleVersionIdentifier id;
-    private final Map<ModuleVersionArtifactIdentifier, ModuleVersionArtifactPublishMetaData> artifactsById = new LinkedHashMap<ModuleVersionArtifactIdentifier, ModuleVersionArtifactPublishMetaData>();
-
-    public DefaultModuleVersionPublishMetaData(ModuleVersionIdentifier id) {
-        this.id = id;
-    }
-
-    public ModuleVersionIdentifier getId() {
-        return id;
-    }
-
-    public void addArtifact(Artifact artifact, File file) {
-        DefaultModuleVersionArtifactPublishMetaData publishMetaData = new DefaultModuleVersionArtifactPublishMetaData(id, artifact, file);
-        artifactsById.put(publishMetaData.getId(), publishMetaData);
-    }
-
-    public void addArtifact(ModuleVersionArtifactPublishMetaData artifact) {
-        artifactsById.put(artifact.getId(), artifact);
-    }
-
-    public Collection<ModuleVersionArtifactPublishMetaData> getArtifacts() {
-        return artifactsById.values();
-    }
-
-    public ModuleVersionArtifactPublishMetaData getArtifact(ModuleVersionArtifactIdentifier artifactIdentifier) {
-        return artifactsById.get(artifactIdentifier);
-    }
-
-    private static class DefaultModuleVersionArtifactPublishMetaData implements ModuleVersionArtifactPublishMetaData {
-        private final DefaultModuleVersionArtifactIdentifier id;
-        private final Artifact artifact;
-        private final File file;
-
-        private DefaultModuleVersionArtifactPublishMetaData(ModuleVersionIdentifier moduleVersionIdentifier, Artifact artifact, File file) {
-            this.id = new DefaultModuleVersionArtifactIdentifier(DefaultModuleComponentIdentifier.newId(moduleVersionIdentifier), artifact);
-            this.artifact = artifact;
-            this.file = file;
-        }
-
-        public IvyArtifactName getArtifactName() {
-            return id.getName();
-        }
-
-        public Artifact toIvyArtifact() {
-            return artifact;
-        }
-
-        public ModuleVersionArtifactIdentifier getId() {
-            return id;
-        }
-
-        public File getFile() {
-            return file;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DependencyMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DependencyMetaData.java
deleted file mode 100644
index 3e36c66..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/DependencyMetaData.java
+++ /dev/null
@@ -1,65 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.component.ComponentSelector;
-
-import java.util.Set;
-
-public interface DependencyMetaData {
-    ModuleVersionSelector getRequested();
-
-    /**
-     * Returns this dependency as an Ivy DependencyDescriptor. This method is here to allow us to migrate away from the Ivy types
-     * and will be removed.
-     */
-    DependencyDescriptor getDescriptor();
-
-    boolean isChanging();
-
-    boolean isTransitive();
-
-    /**
-     * Returns the artifacts defined by this dependency, if any.
-     */
-    // TODO:ADAM - fromConfiguration should be implicit in this metadata
-    Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData fromConfiguration, ConfigurationMetaData toConfiguration);
-
-    /**
-     * Returns a copy of this dependency with the given requested version.
-     */
-    DependencyMetaData withRequestedVersion(String requestedVersion);
-
-    /**
-     * Returns a copy of this dependency with the given requested version.
-     */
-    DependencyMetaData withRequestedVersion(ModuleVersionSelector requestedVersion);
-
-    /**
-     * Returns a copy of this dependency with the changing flag set.
-     */
-    DependencyMetaData withChanging();
-
-    /**
-     * Returns the component selector for this dependency.
-     *
-     * @return Component selector
-     */
-    ComponentSelector getSelector();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/IvyArtifactName.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/IvyArtifactName.java
deleted file mode 100644
index 8e6cb07..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/IvyArtifactName.java
+++ /dev/null
@@ -1,37 +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.metadata;
-
-import org.gradle.api.Nullable;
-
-import java.util.Map;
-
-/**
- * Represents the 'name' part of an Ivy artifact, independent of which module version the artifact might belong to.
- */
-public interface IvyArtifactName {
-    String getName();
-
-    String getType();
-
-    @Nullable
-    String getExtension();
-
-    String getClassifier();
-
-    Map<String, String> getAttributes();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalArtifactMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalArtifactMetaData.java
deleted file mode 100644
index e962d4b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalArtifactMetaData.java
+++ /dev/null
@@ -1,23 +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.metadata;
-
-import java.io.File;
-
-public interface LocalArtifactMetaData extends ComponentArtifactMetaData {
-    File getFile();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalComponentMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalComponentMetaData.java
deleted file mode 100644
index 2a8987c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/LocalComponentMetaData.java
+++ /dev/null
@@ -1,40 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-
-public interface LocalComponentMetaData {
-    ModuleVersionIdentifier getId();
-
-    ModuleDescriptor getModuleDescriptor();
-
-    /**
-     * Converts this component to resolve meta-data.
-     */
-    ComponentMetaData toResolveMetaData();
-
-    /**
-     * Converts this component to publication meta-data.
-     */
-    BuildableModuleVersionPublishMetaData toPublishMetaData();
-
-    @Nullable
-    LocalArtifactMetaData getArtifact(ComponentArtifactIdentifier artifactIdentifier);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapter.java
deleted file mode 100644
index 747f8ca..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapter.java
+++ /dev/null
@@ -1,112 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.*;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-
-import java.util.*;
-
-public class ModuleDescriptorAdapter extends AbstractModuleDescriptorBackedMetaData implements MutableModuleVersionMetaData {
-    private boolean metaDataOnly;
-    private Set<ModuleVersionArtifactMetaData> artifacts;
-
-    public static ModuleDescriptorAdapter defaultForDependency(DependencyMetaData dependencyMetaData) {
-        DependencyDescriptor dependencyDescriptor = dependencyMetaData.getDescriptor();
-        DefaultModuleDescriptor moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(dependencyDescriptor.getDependencyRevisionId(), dependencyDescriptor.getAllDependencyArtifacts());
-        moduleDescriptor.setStatus("integration");
-        return new ModuleDescriptorAdapter(moduleDescriptor);
-    }
-
-    public ModuleDescriptorAdapter(ModuleDescriptor moduleDescriptor) {
-        this(DefaultModuleVersionIdentifier.newId(moduleDescriptor.getModuleRevisionId()), moduleDescriptor);
-    }
-
-    public ModuleDescriptorAdapter(ModuleVersionIdentifier identifier, ModuleDescriptor moduleDescriptor) {
-        this(identifier, moduleDescriptor, DefaultModuleComponentIdentifier.newId(identifier));
-    }
-
-    public ModuleDescriptorAdapter(ModuleVersionIdentifier moduleVersionIdentifier, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier) {
-        super(moduleVersionIdentifier, moduleDescriptor, componentIdentifier);
-    }
-
-    public ModuleDescriptorAdapter copy() {
-        // TODO:ADAM - need to make a copy of the descriptor (it's effectively immutable at this point so it's not a problem yet)
-        ModuleDescriptorAdapter copy = new ModuleDescriptorAdapter(getId(), getDescriptor(), getComponentId());
-        copyTo(copy);
-        copy.metaDataOnly = metaDataOnly;
-        return copy;
-    }
-
-    public ModuleVersionMetaData withSource(ModuleSource source) {
-        ModuleDescriptorAdapter copy = copy();
-        copy.setModuleSource(source);
-        return copy;
-    }
-
-    @Override
-    public ModuleComponentIdentifier getComponentId() {
-        return (ModuleComponentIdentifier) super.getComponentId();
-    }
-
-    public boolean isMetaDataOnly() {
-        return metaDataOnly;
-    }
-
-    public void setMetaDataOnly(boolean metaDataOnly) {
-        this.metaDataOnly = metaDataOnly;
-    }
-
-    public ModuleVersionArtifactMetaData artifact(Artifact artifact) {
-        return new DefaultModuleVersionArtifactMetaData(getComponentId(), artifact);
-    }
-
-    public ModuleVersionArtifactMetaData artifact(String type, @Nullable String extension, @Nullable String classifier) {
-        Map extraAttributes = classifier == null ? Collections.emptyMap() : Collections.singletonMap("m:classifier", classifier);
-        Artifact artifact = new DefaultArtifact(getDescriptor().getModuleRevisionId(), null, getId().getName(), type, "jar", extraAttributes);
-        return new DefaultModuleVersionArtifactMetaData(getComponentId(), artifact);
-    }
-
-    public Set<ModuleVersionArtifactMetaData> getArtifacts() {
-        if (artifacts == null) {
-            artifacts = new LinkedHashSet<ModuleVersionArtifactMetaData>();
-            for (Artifact artifact : getDescriptor().getAllArtifacts()) {
-                artifacts.add(artifact(artifact));
-            }
-        }
-        return artifacts;
-    }
-
-    protected Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configurationMetaData) {
-        Set<Artifact> artifacts = new HashSet<Artifact>();
-        Set<ComponentArtifactMetaData> artifactMetaData = new LinkedHashSet<ComponentArtifactMetaData>();
-        for (String ancestor : configurationMetaData.getHierarchy()) {
-            for (Artifact artifact : getDescriptor().getArtifacts(ancestor)) {
-                if (artifacts.add(artifact)) {
-                    artifactMetaData.add(new DefaultModuleVersionArtifactMetaData(getComponentId(), artifact));
-                }
-            }
-        }
-        return artifactMetaData;
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifier.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifier.java
deleted file mode 100644
index 6b321bb..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifier.java
+++ /dev/null
@@ -1,29 +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.metadata;
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-
-/**
- * An immutable identifier for an artifact that belongs to some module version.
- */
-public interface ModuleVersionArtifactIdentifier extends ComponentArtifactIdentifier {
-    /**
-     * Returns the id of the component that this artifact belongs to.
-     */
-    ModuleComponentIdentifier getComponentIdentifier();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.java
deleted file mode 100644
index 4e90f86..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.java
+++ /dev/null
@@ -1,51 +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.metadata;
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentIdentifierSerializer;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.MapSerializer;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.util.Map;
-
-import static org.gradle.messaging.serialize.BaseSerializerFactory.STRING_SERIALIZER;
-
-public class ModuleVersionArtifactIdentifierSerializer implements Serializer<ModuleVersionArtifactIdentifier> {
-    private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
-    private final MapSerializer<String, String> attributesSerializer = new MapSerializer<String, String>(STRING_SERIALIZER, STRING_SERIALIZER);
-
-    public void write(Encoder encoder, ModuleVersionArtifactIdentifier value) throws Exception {
-        DefaultModuleVersionArtifactIdentifier artifact = (DefaultModuleVersionArtifactIdentifier) value;
-        componentIdentifierSerializer.write(encoder, artifact.getComponentIdentifier());
-        IvyArtifactName ivyArtifactName = artifact.getName();
-        encoder.writeString(ivyArtifactName.getName());
-        encoder.writeString(ivyArtifactName.getType());
-        encoder.writeNullableString(ivyArtifactName.getExtension());
-        attributesSerializer.write(encoder, ivyArtifactName.getAttributes());
-    }
-
-    public ModuleVersionArtifactIdentifier read(Decoder decoder) throws Exception {
-        ModuleComponentIdentifier componentIdentifier = (ModuleComponentIdentifier) componentIdentifierSerializer.read(decoder);
-        String artifactName = decoder.readString();
-        String type = decoder.readString();
-        String extension = decoder.readNullableString();
-        Map<String, String> attributes = attributesSerializer.read(decoder);
-        return new DefaultModuleVersionArtifactIdentifier(componentIdentifier, artifactName, type, extension, attributes);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactMetaData.java
deleted file mode 100644
index 35a67e9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactMetaData.java
+++ /dev/null
@@ -1,38 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.artifacts.ArtifactIdentifier;
-
-/**
- * Meta-data for an artifact that belongs to some module version.
- */
-public interface ModuleVersionArtifactMetaData extends ComponentArtifactMetaData {
-    ModuleVersionArtifactIdentifier getId();
-
-    /**
-     * Converts this artifact to an Ivy artifact. This method is here while we transition away from the Ivy types.
-     */
-    Artifact toIvyArtifact();
-
-    /**
-     * Produces an ArtifactIdentifier for this artifact (it's not actually an identifier - just a bucket of attributes).
-     * TODO:ADAM - remove this
-     */
-    ArtifactIdentifier toArtifactIdentifier();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactPublishMetaData.java
deleted file mode 100644
index 3320235..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactPublishMetaData.java
+++ /dev/null
@@ -1,35 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-
-import java.io.File;
-
-// TODO:ADAM - This is actually Ivy artifact publish meta data
-public interface ModuleVersionArtifactPublishMetaData {
-    ModuleVersionArtifactIdentifier getId();
-
-    /**
-     * Converts this artifact to an Ivy artifact. This method is here while we transition away from the Ivy types.
-     */
-    Artifact toIvyArtifact();
-
-    IvyArtifactName getArtifactName();
-
-    File getFile();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionMetaData.java
deleted file mode 100644
index 9f24472..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionMetaData.java
+++ /dev/null
@@ -1,41 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource;
-
-import java.util.Set;
-
-/**
- * The meta-data for a module version that is required during dependency resolution.
- */
-public interface ModuleVersionMetaData extends ComponentMetaData {
-    ModuleComponentIdentifier getComponentId();
-
-    ModuleVersionMetaData withSource(ModuleSource source);
-
-    Set<ModuleVersionArtifactMetaData> getArtifacts();
-
-    ModuleVersionArtifactMetaData artifact(Artifact artifact);
-
-    ModuleVersionArtifactMetaData artifact(String type, @Nullable String extension, @Nullable String classifier);
-
-    // TODO:DAZ This is not set correctly when read from cache
-    boolean isMetaDataOnly();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionPublishMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionPublishMetaData.java
deleted file mode 100644
index 358636e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/ModuleVersionPublishMetaData.java
+++ /dev/null
@@ -1,28 +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.metadata;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-
-import java.util.Collection;
-
-// TODO:ADAM - This is actually Ivy module publish meta data
-public interface ModuleVersionPublishMetaData {
-    ModuleVersionIdentifier getId();
-
-    Collection<ModuleVersionArtifactPublishMetaData> getArtifacts();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableLocalComponentMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableLocalComponentMetaData.java
deleted file mode 100644
index dca6877..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableLocalComponentMetaData.java
+++ /dev/null
@@ -1,28 +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.metadata;
-
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-
-import java.io.File;
-
-public interface MutableLocalComponentMetaData extends LocalComponentMetaData {
-    DefaultModuleDescriptor getModuleDescriptor();
-
-    void addArtifact(String configuration, Artifact artifact, File file);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableModuleVersionMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableModuleVersionMetaData.java
deleted file mode 100644
index 2b6910d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/metadata/MutableModuleVersionMetaData.java
+++ /dev/null
@@ -1,34 +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.metadata;
-
-import java.util.List;
-
-public interface MutableModuleVersionMetaData extends ModuleVersionMetaData {
-    /**
-     * Creates a deep copy of this meta-data.
-     */
-    MutableModuleVersionMetaData copy();
-
-    void setChanging(boolean changing);
-    void setStatus(String status);
-    void setStatusScheme(List<String> statusScheme);
-
-    /**
-     * 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/mvnsettings/DefaultLocalMavenRepositoryLocator.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
deleted file mode 100644
index 4797f5e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.mvnsettings;
-
-import org.gradle.mvn3.org.apache.maven.settings.Settings;
-import org.gradle.mvn3.org.apache.maven.settings.building.SettingsBuildingException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DefaultLocalMavenRepositoryLocator implements LocalMavenRepositoryLocator {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLocalMavenRepositoryLocator.class);
-    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{([^\\}]*)\\}");
-
-    private final Map<String, String> systemProperties;
-    private final Map<String, String> environmentVariables;
-    private final MavenSettingsProvider settingsProvider;
-
-    public DefaultLocalMavenRepositoryLocator(MavenSettingsProvider settingsProvider, Map<String, String> systemProperties,
-                                              Map<String, String> environmentVariables) {
-        this.systemProperties = systemProperties;
-        this.environmentVariables = environmentVariables;
-        this.settingsProvider = settingsProvider;
-    }
-
-    public File getLocalMavenRepository() throws CannotLocateLocalMavenRepositoryException{
-        try {
-            Settings settings = settingsProvider.buildSettings();
-            String repoPath = settings.getLocalRepository();
-            if (repoPath != null) {
-                return new File(resolvePlaceholders(repoPath.trim()));
-            } else {
-                File defaultLocation = new File(System.getProperty("user.home"), "/.m2/repository").getAbsoluteFile();
-                LOGGER.debug(String.format("No local repository in Settings file defined. Using default path: %s", defaultLocation));
-                return defaultLocation;
-            }
-        } catch (SettingsBuildingException e) {
-            throw new CannotLocateLocalMavenRepositoryException("Unable to parse local Maven settings.", e);
-        }
-    }
-
-    private String resolvePlaceholders(String value) {
-        StringBuffer result = new StringBuffer();
-        Matcher matcher = PLACEHOLDER_PATTERN.matcher(value);
-        while (matcher.find()) {
-            String placeholder = matcher.group(1);
-            String replacement = placeholder.startsWith("env.") ? environmentVariables.get(placeholder.substring(4)) : systemProperties.get(placeholder);
-            if (replacement == null) {
-                throw new CannotLocateLocalMavenRepositoryException(String.format("Cannot resolve placeholder '%s' in value '%s'", placeholder, value));
-            }
-            matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
-        }
-        matcher.appendTail(result);
-
-        return result.toString();
-    }
-}
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
deleted file mode 100644
index b07ce89..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.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.mvnsettings;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.SystemProperties;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.File;
-
-public class DefaultMavenFileLocations implements MavenFileLocations {
-    public File getUserMavenDir() {
-        return new File(SystemProperties.getUserHome(), ".m2");
-    }
-
-    @Nullable
-    public File getGlobalMavenDir() {
-        String m2Home = System.getenv("M2_HOME");
-        if (m2Home == null) {
-            m2Home = System.getProperty("M2_HOME");
-            if(m2Home==null){
-                return null;
-            }
-            DeprecationLogger.nagUserOfDeprecated("Found defined M2_HOME system property. Handling M2_HOME system property", "Please use the M2_HOME environment variable instead");
-        }
-        return new File(m2Home);
-    }
-
-    public File getUserSettingsFile() {
-        return new File(getUserMavenDir(), "settings.xml");
-    }
-
-    @Nullable
-    public File getGlobalSettingsFile() {
-        File dir = getGlobalMavenDir();
-        if (dir == null) {
-            return null;
-        }
-        return new File(dir, "conf/settings.xml");
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 04ec293..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java
+++ /dev/null
@@ -1,40 +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.mvnsettings;
-
-import org.gradle.mvn3.org.apache.maven.settings.Settings;
-import org.gradle.mvn3.org.apache.maven.settings.building.*;
-
-public class DefaultMavenSettingsProvider implements MavenSettingsProvider {
-
-    private final MavenFileLocations mavenFileLocations;
-
-    public DefaultMavenSettingsProvider(MavenFileLocations mavenFileLocations) {
-        this.mavenFileLocations = mavenFileLocations;
-    }
-
-    public Settings buildSettings() throws SettingsBuildingException {
-        DefaultSettingsBuilderFactory factory = new DefaultSettingsBuilderFactory();
-        DefaultSettingsBuilder defaultSettingsBuilder = factory.newInstance();
-        DefaultSettingsBuildingRequest settingsBuildingRequest = new DefaultSettingsBuildingRequest();
-        settingsBuildingRequest.setSystemProperties(System.getProperties());
-        settingsBuildingRequest.setUserSettingsFile(mavenFileLocations.getUserMavenDir());
-        settingsBuildingRequest.setUserSettingsFile(mavenFileLocations.getUserSettingsFile());
-        settingsBuildingRequest.setGlobalSettingsFile(mavenFileLocations.getGlobalSettingsFile());
-        SettingsBuildingResult settingsBuildingResult = defaultSettingsBuilder.build(settingsBuildingRequest);
-        return settingsBuildingResult.getEffectiveSettings();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.java
deleted file mode 100644
index 84bb36f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.java
+++ /dev/null
@@ -1,42 +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;
-
-import org.gradle.api.NamedDomainObjectCollection;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
-import org.gradle.util.DeprecationLogger;
-
-public abstract class AbstractArtifactRepository implements ArtifactRepositoryInternal, ResolutionAwareRepository, PublicationAwareRepository {
-
-    private String name;
-    private boolean isPartOfContainer;
-
-    public void onAddToContainer(NamedDomainObjectCollection<ArtifactRepository> container) {
-        isPartOfContainer = true;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        if (isPartOfContainer) {
-            DeprecationLogger.nagUserOfDeprecated("Changing the name of an ArtifactRepository that is part of a container", "Set the name when creating the repository");
-        }
-        this.name = name;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
deleted file mode 100644
index d4c5460..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
+++ /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.repositories;
-
-import groovy.lang.Closure;
-import org.gradle.api.artifacts.repositories.AuthenticationSupported;
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.util.ConfigureUtil;
-
-public abstract class AbstractAuthenticationSupportedRepository extends AbstractArtifactRepository implements AuthenticationSupported {
-    private final PasswordCredentials passwordCredentials;
-
-    AbstractAuthenticationSupportedRepository(PasswordCredentials credentials) {
-        this.passwordCredentials = credentials;
-    }
-
-    public PasswordCredentials getCredentials() {
-        return passwordCredentials;
-    }
-
-    public void credentials(Closure closure) {
-        ConfigureUtil.configure(closure, passwordCredentials);
-    }
-}
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
deleted file mode 100644
index c2a0e75..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
+++ /dev/null
@@ -1,134 +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;
-
-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.dsl.DefaultRepositoryHandler;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
-import org.gradle.api.internal.artifacts.repositories.legacy.FixedResolverArtifactRepository;
-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.util.ConfigureUtil;
-
-import java.io.File;
-import java.util.Map;
-
-public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
-    private final LocalMavenRepositoryLocator localMavenRepositoryLocator;
-    private final FileResolver fileResolver;
-    private final Instantiator instantiator;
-    private final RepositoryTransportFactory transportFactory;
-    private final LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder;
-    private final LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory;
-    private final ResolverStrategy resolverStrategy;
-
-    public DefaultBaseRepositoryFactory(LocalMavenRepositoryLocator localMavenRepositoryLocator,
-                                        FileResolver fileResolver,
-                                        Instantiator instantiator,
-                                        RepositoryTransportFactory transportFactory,
-                                        LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                        LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory,
-                                        ResolverStrategy resolverStrategy) {
-        this.localMavenRepositoryLocator = localMavenRepositoryLocator;
-        this.fileResolver = fileResolver;
-        this.instantiator = instantiator;
-        this.transportFactory = transportFactory;
-        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.legacyDependencyResolverRepositoryFactory = legacyDependencyResolverRepositoryFactory;
-        this.resolverStrategy = resolverStrategy;
-    }
-
-    public ArtifactRepository createRepository(Object userDescription) {
-        if (userDescription instanceof ArtifactRepository) {
-            return (ArtifactRepository) userDescription;
-        }
-
-        if (userDescription instanceof String) {
-            MavenArtifactRepository repository = createMavenRepository();
-            repository.setUrl(userDescription);
-            return repository;
-        } else if (userDescription instanceof Map) {
-            Map<String, ?> userDescriptionMap = (Map<String, ?>) userDescription;
-            MavenArtifactRepository repository = createMavenRepository();
-            ConfigureUtil.configureByMap(userDescriptionMap, repository);
-            return repository;
-        }
-
-        if (userDescription instanceof DependencyResolver) {
-            return legacyDependencyResolverRepositoryFactory.createRepository((DependencyResolver) userDescription);
-        }
-        throw new InvalidUserDataException(String.format("Cannot create a DependencyResolver instance from %s", userDescription));
-    }
-
-    public FlatDirectoryArtifactRepository createFlatDirRepository() {
-        return instantiator.newInstance(DefaultFlatDirArtifactRepository.class, fileResolver, transportFactory,
-                locallyAvailableResourceFinder, resolverStrategy);
-    }
-
-    public MavenArtifactRepository createMavenLocalRepository() {
-        MavenArtifactRepository mavenRepository = instantiator.newInstance(DefaultMavenLocalArtifactRepository.class, fileResolver, createPasswordCredentials(), transportFactory,
-                locallyAvailableResourceFinder, resolverStrategy);
-        final File localMavenRepository = localMavenRepositoryLocator.getLocalMavenRepository();
-        mavenRepository.setUrl(localMavenRepository);
-        return mavenRepository;
-    }
-
-    public MavenArtifactRepository createJCenterRepository() {
-        MavenArtifactRepository mavenRepository = createMavenRepository();
-        String url = System.getProperty(JCENTER_REPO_OVERRIDE_URL_PROPERTY, DefaultRepositoryHandler.BINTRAY_JCENTER_URL);
-        mavenRepository.setUrl(url);
-        return mavenRepository;
-    }
-
-    public MavenArtifactRepository createMavenCentralRepository() {
-        MavenArtifactRepository mavenRepository = createMavenRepository();
-        mavenRepository.setUrl(RepositoryHandler.MAVEN_CENTRAL_URL);
-        return mavenRepository;
-    }
-
-    public IvyArtifactRepository createIvyRepository() {
-        return instantiator.newInstance(DefaultIvyArtifactRepository.class, fileResolver, createPasswordCredentials(), transportFactory,
-                locallyAvailableResourceFinder, instantiator, resolverStrategy);
-    }
-
-    public MavenArtifactRepository createMavenRepository() {
-        return instantiator.newInstance(DefaultMavenArtifactRepository.class, fileResolver, createPasswordCredentials(), transportFactory,
-                locallyAvailableResourceFinder, resolverStrategy
-        );
-    }
-
-    public DependencyResolver toResolver(ArtifactRepository repository) {
-        return ((ArtifactRepositoryInternal) repository).createLegacyDslObject();
-    }
-
-    public FixedResolverArtifactRepository createResolverBackedRepository(DependencyResolver resolver) {
-        return new FixedResolverArtifactRepository(resolver);
-    }
-
-    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
deleted file mode 100644
index 34e4138..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
+++ /dev/null
@@ -1,98 +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;
-
-import com.google.common.collect.Lists;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultFlatDirArtifactRepository extends AbstractArtifactRepository implements FlatDirectoryArtifactRepository {
-    private final FileResolver fileResolver;
-    private List<Object> dirs = new ArrayList<Object>();
-    private final RepositoryTransportFactory transportFactory;
-    private final LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder;
-    private final ResolverStrategy resolverStrategy;
-
-    public DefaultFlatDirArtifactRepository(FileResolver fileResolver,
-                                            RepositoryTransportFactory transportFactory,
-                                            LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                            ResolverStrategy resolverStrategy) {
-        this.fileResolver = fileResolver;
-        this.transportFactory = transportFactory;
-        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.resolverStrategy = resolverStrategy;
-    }
-
-    public Set<File> getDirs() {
-        return fileResolver.resolveFiles(dirs).getFiles();
-    }
-
-    public void setDirs(Iterable<?> dirs) {
-        this.dirs = Lists.newArrayList(dirs);
-    }
-
-    public void dir(Object dir) {
-        dirs(dir);
-    }
-
-    public void dirs(Object... dirs) {
-        this.dirs.addAll(Arrays.asList(dirs));
-    }
-
-    public ModuleVersionPublisher createPublisher() {
-        return createRealResolver();
-    }
-
-    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.");
-        }
-
-        IvyResolver resolver = new IvyResolver(getName(), transportFactory.createFileTransport(getName()),
-                locallyAvailableResourceFinder, false, resolverStrategy);
-        for (File root : dirs) {
-            resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact]-[revision](-[classifier]).[ext]");
-            resolver.addArtifactPattern(root.getAbsolutePath() + "/[artifact](-[classifier]).[ext]");
-        }
-        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
deleted file mode 100644
index 7e928ea..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
+++ /dev/null
@@ -1,201 +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;
-
-import groovy.lang.Closure;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-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.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.ConfigureUtil;
-import org.gradle.util.WrapUtil;
-
-import java.net.URI;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupportedRepository implements IvyArtifactRepository {
-    private Object baseUrl;
-    private RepositoryLayout layout;
-    private final AdditionalPatternsRepositoryLayout additionalPatternsLayout;
-    private final FileResolver fileResolver;
-    private final RepositoryTransportFactory transportFactory;
-    private final LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder;
-    private final MetaDataProvider metaDataProvider;
-    private final Instantiator instantiator;
-    private final ResolverStrategy resolverStrategy;
-
-    public DefaultIvyArtifactRepository(FileResolver fileResolver, PasswordCredentials credentials, RepositoryTransportFactory transportFactory,
-                                        LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder, Instantiator instantiator,
-                                        ResolverStrategy resolverStrategy) {
-        super(credentials);
-        this.fileResolver = fileResolver;
-        this.transportFactory = transportFactory;
-        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.resolverStrategy = resolverStrategy;
-        this.additionalPatternsLayout = new AdditionalPatternsRepositoryLayout(fileResolver);
-        this.layout = new GradleRepositoryLayout();
-        this.metaDataProvider = new MetaDataProvider();
-        this.instantiator = instantiator;
-    }
-
-    public DependencyResolver createLegacyDslObject() {
-        IvyResolver resolver = createRealResolver();
-        return new LegacyDependencyResolver(resolver);
-    }
-
-    public ModuleVersionPublisher createPublisher() {
-        return createRealResolver();
-    }
-
-    public ConfiguredModuleVersionRepository createResolver() {
-        return createRealResolver();
-    }
-
-    protected IvyResolver createRealResolver() {
-        URI uri = getUrl();
-
-        Set<String> schemes = new LinkedHashSet<String>();
-        layout.addSchemes(uri, schemes);
-        additionalPatternsLayout.addSchemes(uri, schemes);
-
-        IvyResolver resolver = createResolver(schemes);
-
-        layout.apply(uri, resolver);
-        additionalPatternsLayout.apply(uri, resolver);
-
-        return resolver;
-    }
-
-    private IvyResolver createResolver(Set<String> schemes) {
-        if (schemes.isEmpty()) {
-            throw new InvalidUserDataException("You must specify a base url or at least one artifact pattern for an Ivy repository.");
-        }
-        if (!WrapUtil.toSet("http", "https", "file").containsAll(schemes)) {
-            throw new InvalidUserDataException("You may only specify 'file', 'http' and 'https' urls for an Ivy repository.");
-        }
-        if (WrapUtil.toSet("http", "https").containsAll(schemes)) {
-            return createResolver(transportFactory.createHttpTransport(getName(), getCredentials()));
-        }
-        if (WrapUtil.toSet("file").containsAll(schemes)) {
-            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,
-                metaDataProvider.dynamicResolve, resolverStrategy);
-    }
-
-    public URI getUrl() {
-        return baseUrl == null ? null : fileResolver.resolveUri(baseUrl);
-    }
-
-    public void setUrl(Object url) {
-        baseUrl = url;
-    }
-
-    public void artifactPattern(String pattern) {
-        additionalPatternsLayout.artifactPatterns.add(pattern);
-    }
-
-    public void ivyPattern(String pattern) {
-        additionalPatternsLayout.ivyPatterns.add(pattern);
-    }
-
-    public void layout(String layoutName) {
-        if ("maven".equals(layoutName)) {
-            layout = instantiator.newInstance(MavenRepositoryLayout.class);
-        } else if ("pattern".equals(layoutName)) {
-            layout = instantiator.newInstance(PatternRepositoryLayout.class);
-        } else {
-            layout = instantiator.newInstance(GradleRepositoryLayout.class);
-        }
-    }
-
-    public void layout(String layoutName, Closure config) {
-        layout(layoutName);
-        ConfigureUtil.configure(config, layout);
-    }
-
-    public IvyArtifactRepositoryMetaDataProvider getResolve() {
-        return metaDataProvider;
-    }
-
-    /**
-     * Layout for applying additional patterns added via {@link #artifactPatterns} and {@link #ivyPatterns}.
-     */
-    private static class AdditionalPatternsRepositoryLayout extends RepositoryLayout {
-        private final FileResolver fileResolver;
-        private final Set<String> artifactPatterns = new LinkedHashSet<String>();
-        private final Set<String> ivyPatterns = new LinkedHashSet<String>();
-
-        public AdditionalPatternsRepositoryLayout(FileResolver fileResolver) {
-            this.fileResolver = fileResolver;
-        }
-
-        public void apply(URI baseUri, PatternBasedResolver resolver) {
-            for (String artifactPattern : artifactPatterns) {
-                ResolvedPattern resolvedPattern = new ResolvedPattern(artifactPattern, fileResolver);
-                resolver.addArtifactLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
-            }
-
-            Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
-            for (String ivyPattern : usedIvyPatterns) {
-                ResolvedPattern resolvedPattern = new ResolvedPattern(ivyPattern, fileResolver);
-                resolver.addDescriptorLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
-            }
-        }
-
-        @Override
-        public void addSchemes(URI baseUri, Set<String> schemes) {
-            for (String pattern : artifactPatterns) {
-                schemes.add(new ResolvedPattern(pattern, fileResolver).scheme);
-            }
-            for (String pattern : ivyPatterns) {
-                schemes.add(new ResolvedPattern(pattern, fileResolver).scheme);
-            }
-        }
-    }
-
-    private static class MetaDataProvider implements IvyArtifactRepositoryMetaDataProvider {
-        boolean dynamicResolve;
-
-        public boolean isDynamicMode() {
-            return dynamicResolve;
-        }
-
-        public void setDynamicMode(boolean mode) {
-            this.dynamicResolve = mode;
-        }
-    }
-}
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
deleted file mode 100644
index 44e930f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
+++ /dev/null
@@ -1,123 +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;
-
-import com.google.common.collect.Lists;
-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.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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.local.LocallyAvailableResourceFinder;
-import org.gradle.api.internal.file.FileResolver;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultMavenArtifactRepository extends AbstractAuthenticationSupportedRepository implements MavenArtifactRepository {
-    private final FileResolver fileResolver;
-    private final RepositoryTransportFactory transportFactory;
-    private Object url;
-    private List<Object> additionalUrls = new ArrayList<Object>();
-    private final LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder;
-    private final ResolverStrategy resolverStrategy;
-
-    public DefaultMavenArtifactRepository(FileResolver fileResolver, PasswordCredentials credentials, RepositoryTransportFactory transportFactory,
-                                          LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                          ResolverStrategy resolverStrategy) {
-        super(credentials);
-        this.fileResolver = fileResolver;
-        this.transportFactory = transportFactory;
-        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.resolverStrategy = resolverStrategy;
-    }
-
-    public URI getUrl() {
-        return url == null ? null : fileResolver.resolveUri(url);
-    }
-
-    public void setUrl(Object url) {
-        this.url = url;
-    }
-
-    public Set<URI> getArtifactUrls() {
-        Set<URI> result = new LinkedHashSet<URI>();
-        for (Object additionalUrl : additionalUrls) {
-            result.add(fileResolver.resolveUri(additionalUrl));
-        }
-        return result;
-    }
-
-    public void artifactUrls(Object... urls) {
-        additionalUrls.addAll(Lists.newArrayList(urls));
-    }
-
-    public void setArtifactUrls(Iterable<?> urls) {
-        additionalUrls = Lists.newArrayList(urls);
-    }
-
-    public ModuleVersionPublisher createPublisher() {
-        return createRealResolver();
-    }
-
-    public DependencyResolver createLegacyDslObject() {
-        MavenResolver resolver = createRealResolver();
-        return new LegacyMavenResolver(resolver);
-    }
-
-    public ConfiguredModuleVersionRepository createResolver() {
-        return createRealResolver();
-    }
-
-    protected MavenResolver createRealResolver() {
-        URI rootUri = getUrl();
-        if (rootUri == null) {
-            throw new InvalidUserDataException("You must specify a URL for a Maven repository.");
-        }
-
-        MavenResolver resolver = new MavenResolver(getName(), rootUri, getTransport(rootUri.getScheme()),
-                locallyAvailableResourceFinder, resolverStrategy);
-        for (URI repoUrl : getArtifactUrls()) {
-            resolver.addArtifactLocation(repoUrl, null);
-        }
-        return resolver;
-    }
-
-    protected RepositoryTransport getTransport(String scheme) {
-        if (scheme.equalsIgnoreCase("file")) {
-            return transportFactory.createFileTransport(getName());
-        } else {
-            return transportFactory.createHttpTransport(getName(), getCredentials());
-        }
-    }
-
-    protected LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> getLocallyAvailableResourceFinder() {
-        return locallyAvailableResourceFinder;
-    }
-
-    protected ResolverStrategy getResolverStrategy() {
-        return resolverStrategy;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
deleted file mode 100644
index bfcd254..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
+++ /dev/null
@@ -1,50 +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.repositories;
-
-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.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.repositories.resolver.MavenLocalResolver;
-import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
-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.net.URI;
-
-public class DefaultMavenLocalArtifactRepository extends DefaultMavenArtifactRepository implements MavenArtifactRepository {
-    public DefaultMavenLocalArtifactRepository(FileResolver fileResolver, PasswordCredentials credentials, RepositoryTransportFactory transportFactory,
-                                        LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                        ResolverStrategy resolverStrategy) {
-        super(fileResolver, credentials, transportFactory, locallyAvailableResourceFinder, resolverStrategy);
-    }
-
-    protected MavenResolver createRealResolver() {
-        URI rootUri = getUrl();
-        if (rootUri == null) {
-            throw new InvalidUserDataException("You must specify a URL for a Maven repository.");
-        }
-
-        MavenResolver resolver = new MavenLocalResolver(getName(), rootUri, getTransport(rootUri.getScheme()), getLocallyAvailableResourceFinder(), getResolverStrategy());
-        for (URI repoUrl : getArtifactUrls()) {
-            resolver.addArtifactLocation(repoUrl, null);
-        }
-        return resolver;
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 3bfa9a0..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyDependencyResolver.java
+++ /dev/null
@@ -1,294 +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;
-
-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.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.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;
-
-/**
- * A wrapper over a {@link org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver} that is exposed through the DSL,
- * for backwards compatibility.
- */
-public class LegacyDependencyResolver implements DependencyResolver, ResolutionAwareRepository {
-    private final ExternalResourceResolver resolver;
-    private ResolverSettings settings;
-
-    public LegacyDependencyResolver(ExternalResourceResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public String getName() {
-        return resolver.getName();
-    }
-
-    public void setName(String name) {
-        resolver.setName(name);
-    }
-
-    public String toString() {
-        return resolver.toString();
-    }
-
-    public ConfiguredModuleVersionRepository createResolver() {
-        return resolver;
-    }
-
-    public void setSettings(ResolverSettings settings) {
-        this.settings = settings;
-    }
-
-    public ResolverSettings getSettings() {
-        return settings;
-    }
-
-    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-        // This is not used
-        throw new UnsupportedOperationException();
-    }
-
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-        // This is not used
-        throw new UnsupportedOperationException();
-    }
-
-    public boolean exists(Artifact artifact) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public ArtifactOrigin locate(Artifact artifact) {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    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 {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void commitPublishTransaction() throws IOException {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public Namespace getNamespace() {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-
-    public void addIvyPattern(String pattern) {
-        resolver.addIvyPattern(pattern);
-    }
-
-    public void addArtifactPattern(String pattern) {
-        resolver.addArtifactPattern(pattern);
-    }
-
-    public List<String> getIvyPatterns() {
-        return resolver.getIvyPatterns();
-    }
-
-    public List<String> getArtifactPatterns() {
-        return resolver.getArtifactPatterns();
-    }
-
-    public void dumpSettings() {
-        // this is not used
-        throw new UnsupportedOperationException();
-    }
-
-    public boolean isM2compatible() {
-        return resolver.isM2compatible();
-    }
-
-    public void setM2compatible(boolean compatible) {
-        resolver.setM2compatible(compatible);
-    }
-
-    public boolean isCheckconsistency() {
-        return resolver.isCheckconsistency();
-    }
-
-    public void setCheckconsistency(boolean checkConsistency) {
-        resolver.setCheckconsistency(checkConsistency);
-    }
-
-    public void setForce(boolean force) {
-        resolver.setForce(force);
-    }
-
-    public boolean isForce() {
-        return resolver.isForce();
-    }
-
-    public boolean isAllownomd() {
-        return resolver.isAllownomd();
-    }
-
-    public void setAllownomd(boolean b) {
-        resolver.setAllownomd(b);
-    }
-
-    /**
-     * Sets the module descriptor presence rule.
-     * Should be one of {@link org.apache.ivy.plugins.resolver.BasicResolver#DESCRIPTOR_REQUIRED} or {@link org.apache.ivy.plugins.resolver.BasicResolver#DESCRIPTOR_OPTIONAL}.
-     *
-     * @param descriptorRule the descriptor rule to use with this resolver.
-     */
-    public void setDescriptor(String 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() {
-        return resolver.getChecksumAlgorithms();
-    }
-
-    public void setChecksums(String checksums) {
-        resolver.setChecksums(checksums);
-    }
-
-    public LatestStrategy getLatestStrategy() {
-        throw new UnsupportedOperationException("getLatestStrategy");
-    }
-
-    public void setLatestStrategy(LatestStrategy latestStrategy) {
-        throw new UnsupportedOperationException("setLatestStrategy");
-    }
-
-    public String getLatest() {
-        throw new UnsupportedOperationException("getLatest");
-    }
-
-    public void setLatest(String strategyName) {
-        throw new UnsupportedOperationException("setLatest");
-    }
-
-    public void setChangingMatcher(String changingMatcherName) {
-        resolver.setChangingMatcher(changingMatcherName);
-    }
-
-    protected String getChangingMatcherName() {
-        return resolver.getChangingMatcherName();
-    }
-
-    public void setChangingPattern(String changingPattern) {
-        resolver.setChangingPattern(changingPattern);
-    }
-
-    protected String getChangingPattern() {
-        return resolver.getChangingPattern();
-    }
-
-    public void setCheckmodified(boolean check) {
-        throw new UnsupportedOperationException();
-    }
-
-    public RepositoryCacheManager getRepositoryCacheManager() {
-        // This is never used
-        throw new UnsupportedOperationException();
-    }
-}
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
deleted file mode 100644
index 0810786..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/LegacyMavenResolver.java
+++ /dev/null
@@ -1,61 +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;
-
-import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
-
-public class LegacyMavenResolver extends LegacyDependencyResolver {
-    private final MavenResolver resolver;
-
-    public LegacyMavenResolver(MavenResolver resolver) {
-        super(resolver);
-        this.resolver = resolver;
-    }
-
-    // A bunch of configuration properties that we don't (yet) support in our model via the DSL. Users can still tweak these on the resolver using mavenRepo().
-    public boolean isUsepoms() {
-        return resolver.isUsepoms();
-    }
-
-    public void setUsepoms(boolean usepoms) {
-        resolver.setUsepoms(usepoms);
-    }
-
-    public boolean isUseMavenMetadata() {
-        return resolver.isUseMavenMetadata();
-    }
-
-    public void setUseMavenMetadata(boolean useMavenMetadata) {
-        resolver.setUseMavenMetadata(useMavenMetadata);
-    }
-
-    public String getPattern() {
-        return resolver.getPattern();
-    }
-
-    public void setPattern(String pattern) {
-        resolver.setPattern(pattern);
-    }
-
-    public String getRoot() {
-        return resolver.getRoot();
-    }
-
-    public void setRoot(String root) {
-        resolver.setRoot(root);
-    }
-}
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
deleted file mode 100644
index f901ab8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
+++ /dev/null
@@ -1,26 +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;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
-
-public interface ResolutionAwareRepository {
-    /**
-     * Creates a resolver for this repository. Result may also implement {@link org.gradle.api.internal.artifacts.ivyservice.ivyresolve.IvyAwareModuleVersionRepository}.
-     */
-    ConfiguredModuleVersionRepository createResolver();
-}
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
deleted file mode 100644
index bc8d22f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/DownloadingRepositoryArtifactCache.java
+++ /dev/null
@@ -1,73 +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.repositories.cachemanager;
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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<ModuleVersionArtifactMetaData> fileStore;
-    private final CachedExternalResourceIndex<String> artifactUrlCachedResolutionIndex;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final CacheLockingManager cacheLockingManager;
-
-    public DownloadingRepositoryArtifactCache(FileStore<ModuleVersionArtifactMetaData> 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 ModuleVersionArtifactMetaData artifact, 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", artifact), new Factory<LocallyAvailableExternalResource>() {
-                public LocallyAvailableExternalResource create() {
-                    LocallyAvailableResource cachedResource = fileStore.move(artifact, 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/LocalFileRepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryArtifactCache.java
deleted file mode 100644
index 07d923d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/LocalFileRepositoryArtifactCache.java
+++ /dev/null
@@ -1,43 +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.repositories.cachemanager;
-
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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(ModuleVersionArtifactMetaData 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/RepositoryArtifactCache.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/RepositoryArtifactCache.java
deleted file mode 100644
index 0a23579..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/cachemanager/RepositoryArtifactCache.java
+++ /dev/null
@@ -1,45 +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.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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 artifact 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(ModuleVersionArtifactMetaData artifact, 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/GradleRepositoryLayout.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
deleted file mode 100644
index 2555bf5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.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.artifacts.repositories.layout;
-
-import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
-
-import java.net.URI;
-
-/**
- * A Repository Layout that applies the following patterns:
- * <ul>
- *     <li>Artifacts: $baseUri/{@value IvyArtifactRepository#GRADLE_ARTIFACT_PATTERN}</li>
- *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#GRADLE_IVY_PATTERN}</li>
- * </ul>
- *
- * Note the pattern is the same for both artifacts and ivy files.
- */
-public class GradleRepositoryLayout extends RepositoryLayout {
-
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
-        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.GRADLE_IVY_PATTERN);
-    }
-}
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
deleted file mode 100644
index 97156fd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.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.artifacts.repositories.layout;
-
-import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
-
-import java.net.URI;
-
-/**
- * A Repository Layout that applies the following patterns:
- * <ul>
- *     <li>Artifacts: $baseUri/{@value IvyArtifactRepository#MAVEN_ARTIFACT_PATTERN}</li>
- *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#MAVEN_IVY_PATTERN}</li>
- * </ul>
- *
- * 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 {
-
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        resolver.setM2compatible(true); // Replace '.' with '/' in organisation
-
-        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
-        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.MAVEN_IVY_PATTERN);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/PatternRepositoryLayout.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/PatternRepositoryLayout.java
deleted file mode 100644
index 52cc63a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/PatternRepositoryLayout.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.artifacts.repositories.layout;
-
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
-
-import java.net.URI;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * A repository layout that uses user-supplied patterns. Each pattern will be appended to the base URI for the repository.
- * At least one artifact pattern must be specified. If no Ivy patterns are specified, then the artifact patterns will be used.
- * Optionally supports a Maven style layout for the 'organisation' part, replacing any dots with forward slashes.
- */
-public class PatternRepositoryLayout extends RepositoryLayout {
-    private final Set<String> artifactPatterns = new LinkedHashSet<String>();
-    private final Set<String> ivyPatterns = new LinkedHashSet<String>();
-    private boolean m2compatible;
-
-    /**
-     * Adds an Ivy artifact pattern to define where artifacts are located in this repository.
-     * @param pattern The ivy pattern
-     */
-    public void artifact(String pattern) {
-        artifactPatterns.add(pattern);
-    }
-
-    /**
-     * Adds an Ivy pattern to define where ivy files are located in this repository.
-     * @param pattern The ivy pattern
-     */
-    public void ivy(String pattern) {
-        ivyPatterns.add(pattern);
-    }
-
-    /**
-     * Tells whether a Maven style layout is to be used for the 'organisation' part, replacing any dots with forward slashes.
-     * Defaults to {@code false}.
-     */
-    public boolean getM2Compatible() {
-        return m2compatible;
-    }
-
-    /**
-     * Sets whether a Maven style layout is to be used for the 'organisation' part, replacing any dots with forward slashes.
-     * Defaults to {@code false}.
-     *
-     * @param m2compatible whether a Maven style layout is to be used for the 'organisation' part
-     */
-    public void setM2compatible(boolean m2compatible) {
-        this.m2compatible = m2compatible;
-    }
-
-    @Override
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        for (String artifactPattern : artifactPatterns) {
-            resolver.addArtifactLocation(baseUri, artifactPattern);
-        }
-
-        Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
-        for (String ivyPattern : usedIvyPatterns) {
-            resolver.addDescriptorLocation(baseUri, ivyPattern);
-        }
-
-        resolver.setM2compatible(m2compatible);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/RepositoryLayout.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/RepositoryLayout.java
deleted file mode 100644
index 451db67..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/RepositoryLayout.java
+++ /dev/null
@@ -1,47 +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.layout;
-
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
-
-import java.net.URI;
-import java.util.Set;
-
-/**
- * Represents the directory structure for a repository.
- */
-public abstract class RepositoryLayout {
-    /**
-     * Given the base URI, apply the patterns and other configuration for this layout to the supplied resolver.
-     *
-     * @param baseUri The base URI for the repository.
-     * @param resolver The ivy resolver that will be used to resolve this layout.
-     */
-    public abstract void apply(URI baseUri, PatternBasedResolver resolver);
-
-    /**
-     * Add any schemes registered as patterns in this layout, given the supplied base URI.
-     * These are used to determine which repository implementation can be used (local file, http, etc).
-     *
-     * @param baseUri The baseUri of the repository.
-     * @param schemes The set of schemes to add to.
-     */
-    public void addSchemes(URI baseUri, Set<String> schemes) {
-        if (baseUri != null) {
-            schemes.add(baseUri.getScheme());
-        }
-    }
-}
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
deleted file mode 100644
index bbf1446..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/AbstractRepositoryCacheManager.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.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
deleted file mode 100644
index 541cb0e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomIvyResolverRepositoryFactory.java
+++ /dev/null
@@ -1,41 +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.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.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/CustomResolverArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomResolverArtifactRepository.java
deleted file mode 100644
index abcef81..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/CustomResolverArtifactRepository.java
+++ /dev/null
@@ -1,72 +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.legacy;
-
-import org.apache.ivy.core.cache.RepositoryCacheManager;
-import org.apache.ivy.plugins.repository.Repository;
-import org.apache.ivy.plugins.repository.TransferListener;
-import org.apache.ivy.plugins.resolver.*;
-import org.gradle.api.internal.artifacts.repositories.transport.ProgressLoggingTransferListener;
-import org.gradle.logging.ProgressLoggerFactory;
-
-import java.util.List;
-
-public class CustomResolverArtifactRepository extends FixedResolverArtifactRepository {
-    private final TransferListener transferListener;
-    private final RepositoryCacheManager downloadingCacheManager;
-    private final RepositoryCacheManager localCacheManager;
-
-    public CustomResolverArtifactRepository(DependencyResolver resolver, ProgressLoggerFactory progressLoggerFactory,
-                                            RepositoryCacheManager localCacheManager, RepositoryCacheManager downloadingCacheManager) {
-        super(resolver);
-        this.localCacheManager = localCacheManager;
-        this.downloadingCacheManager = downloadingCacheManager;
-        this.transferListener = new ProgressLoggingTransferListener(progressLoggerFactory, CustomResolverArtifactRepository.class);
-        configureResolver(resolver, true);
-    }
-
-    private void configureResolver(DependencyResolver dependencyResolver, boolean isTopLevel) {
-        if (isTopLevel) {
-            if (resolver instanceof AbstractResolver && !(resolver instanceof FileSystemResolver)) {
-                ((AbstractResolver) resolver).setRepositoryCacheManager(downloadingCacheManager);
-            }
-        }
-
-        if (dependencyResolver instanceof FileSystemResolver) {
-            ((FileSystemResolver) dependencyResolver).setLocal(true);
-            ((FileSystemResolver) dependencyResolver).setRepositoryCacheManager(localCacheManager);
-        }
-        if (dependencyResolver instanceof RepositoryResolver) {
-            Repository repository = ((RepositoryResolver) dependencyResolver).getRepository();
-            if (!repository.hasTransferListener(transferListener)) {
-                repository.addTransferListener(transferListener);
-            }
-        }
-        if (dependencyResolver instanceof DualResolver) {
-            DualResolver dualResolver = (DualResolver) dependencyResolver;
-            configureResolver(dualResolver.getIvyResolver(), false);
-            configureResolver(dualResolver.getArtifactResolver(), false);
-        }
-        if (dependencyResolver instanceof ChainResolver) {
-            ChainResolver chainResolver = (ChainResolver) dependencyResolver;
-            @SuppressWarnings("unchecked") List<DependencyResolver> resolvers = chainResolver.getResolvers();
-            for (DependencyResolver resolver : resolvers) {
-                configureResolver(resolver, false);
-            }
-        }
-    }
-
-}
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
deleted file mode 100644
index 9d88092..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManager.java
+++ /dev/null
@@ -1,161 +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.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.ModuleRevisionId;
-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.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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<ModuleVersionArtifactMetaData> fileStore;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final CacheLockingManager cacheLockingManager;
-
-    public DownloadingRepositoryCacheManager(String name, FileStore<ModuleVersionArtifactMetaData> 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);
-                }
-
-                ModuleRevisionId moduleRevisionId = artifact.getModuleRevisionId();
-                ModuleComponentIdentifier componentIdentifier = DefaultModuleComponentIdentifier.newId(moduleRevisionId.getOrganisation(), moduleRevisionId.getName(), moduleRevisionId.getRevision());
-                File artifactFile = downloadAndCacheArtifactFile(new DefaultModuleVersionArtifactMetaData(componentIdentifier, artifact), 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 ModuleVersionArtifactMetaData id, 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", id), new Factory<File>() {
-                public File create() {
-                    return fileStore.move(id, 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
deleted file mode 100644
index 50ff9b9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/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.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/FixedResolverArtifactRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/FixedResolverArtifactRepository.java
deleted file mode 100644
index e714d32..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/FixedResolverArtifactRepository.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.api.internal.artifacts.repositories.legacy;
-
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-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.repositories.AbstractArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-
-public class FixedResolverArtifactRepository extends AbstractArtifactRepository {
-    protected final DependencyResolver resolver;
-
-    public FixedResolverArtifactRepository(DependencyResolver resolver) {
-        this.resolver = resolver;
-    }
-
-    public String getName() {
-        return resolver.getName();
-    }
-
-    public void setName(String name) {
-        resolver.setName(name);
-
-        // We are doing this because we are relying on the deprecation warning that
-        // AbstractArtifactRepository (super) issues. This is a bit awkward.
-        super.setName(name);
-    }
-
-    public ModuleVersionPublisher createPublisher() {
-        return new IvyResolverBackedModuleVersionPublisher(resolver);
-    }
-
-    public ConfiguredModuleVersionRepository createResolver() {
-        // Handle a repository wrapped in a resolver for backwards compatibility
-        if (resolver instanceof ResolutionAwareRepository) {
-            ResolutionAwareRepository resolutionAwareRepository = (ResolutionAwareRepository) resolver;
-            return resolutionAwareRepository.createResolver();
-        }
-        return new IvyDependencyResolverAdapter(resolver);
-    }
-
-    public DependencyResolver createLegacyDslObject() {
-        return resolver;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/IvyDependencyResolverAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/IvyDependencyResolverAdapter.java
deleted file mode 100644
index 968db04..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/IvyDependencyResolverAdapter.java
+++ /dev/null
@@ -1,202 +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.legacy;
-
-import com.google.common.collect.ImmutableSet;
-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.resolution.JvmLibraryJavadocArtifact;
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.*;
-import org.gradle.api.internal.artifacts.metadata.*;
-import org.gradle.api.internal.artifacts.resolution.IvyDescriptorArtifact;
-import org.gradle.internal.UncheckedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.text.ParseException;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A {@link org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionRepository} wrapper around an Ivy {@link DependencyResolver}.
- */
-public class IvyDependencyResolverAdapter implements ConfiguredModuleVersionRepository, IvyAwareModuleVersionRepository, LocalArtifactsModuleVersionRepository {
-    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) {
-        this.resolver = resolver;
-        identifier = DependencyResolverIdentifier.forIvyResolver(resolver);
-    }
-
-    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 listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        IvyContext.getContext().setResolveData(resolveData);
-        try {
-            ResolvedModuleRevision revision = resolver.getDependency(dependency.getDescriptor(), resolveData);
-            if (revision == null) {
-                result.listed(new DefaultModuleVersionListing());
-            } else {
-                result.listed(new DefaultModuleVersionListing(revision.getId().getRevision()));
-            }
-        } catch (ParseException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        IvyContext.getContext().setResolveData(resolveData);
-        try {
-            ResolvedModuleRevision revision = resolver.getDependency(dependency.getDescriptor(), resolveData);
-            if (revision == null) {
-                LOGGER.debug("Performed resolved of module '{}' in repository '{}': not found", dependency.getRequested(), getName());
-                result.missing();
-            } else {
-                LOGGER.debug("Performed resolved of module '{}' in repository '{}': found", dependency.getRequested(), getName());
-                ModuleDescriptorAdapter metaData = new ModuleDescriptorAdapter(revision.getDescriptor());
-                metaData.setChanging(isChanging(revision));
-                result.resolved(metaData, null);
-            }
-        } catch (ParseException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        Artifact ivyArtifact = ((ModuleVersionArtifactMetaData) artifact).toIvyArtifact();
-        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(artifact.getId(), enhancedReport.getFailure()));
-            } else {
-                result.failed(new ArtifactResolveException(artifact.getId(), artifactDownloadReport.getDownloadDetails()));
-            }
-            return;
-        }
-
-        File localFile = artifactDownloadReport.getLocalFile();
-        if (localFile != null) {
-            result.resolved(localFile);
-        } else {
-            result.notFound(artifact.getId());
-        }
-    }
-
-    public void localResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        doResolveModuleArtifacts(component, context, result, true);
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        doResolveModuleArtifacts(component, context, result, false);
-    }
-
-    // TODO:DAZ This "local-only" pattern is quite ugly: improve it.
-    private void doResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        ModuleVersionMetaData moduleVersion = (ModuleVersionMetaData) component;
-        if (context instanceof ConfigurationResolveContext) {
-            String configurationName = ((ConfigurationResolveContext) context).getConfigurationName();
-            result.resolved(component.getConfiguration(configurationName).getArtifacts());
-            return;
-        }
-
-        if (!localOnly && context instanceof ArtifactTypeResolveContext) {
-            Class<? extends SoftwareArtifact> artifactType = ((ArtifactTypeResolveContext) context).getArtifactType();
-            try {
-                result.resolved(doGetCandidateArtifacts(moduleVersion, artifactType));
-            } catch (Exception e) {
-                result.failed(new ArtifactResolveException(component.getComponentId(), e));
-            }
-        }
-    }
-
-    private Set<ModuleVersionArtifactMetaData> doGetCandidateArtifacts(ModuleVersionMetaData module, Class<? extends SoftwareArtifact> artifactType) {
-        if (artifactType == IvyDescriptorArtifact.class) {
-            Artifact metadataArtifact = module.getDescriptor().getMetadataArtifact();
-            return ImmutableSet.of(module.artifact(metadataArtifact));
-        }
-
-        if (artifactType == JvmLibraryJavadocArtifact.class) {
-            return createArtifactMetaData(module, "javadoc", "javadoc");
-        }
-
-        if (artifactType == JvmLibrarySourcesArtifact.class) {
-            return createArtifactMetaData(module, "source", "sources");
-        }
-
-        throw new IllegalArgumentException(String.format("Cannot find artifacts of type %s in %s", artifactType.getName(), module));
-    }
-
-    private Set<ModuleVersionArtifactMetaData> createArtifactMetaData(ModuleVersionMetaData module, String type, String classifier) {
-        ModuleVersionArtifactMetaData artifact = module.artifact(type, "jar", classifier);
-        if (resolver.exists(artifact.toIvyArtifact())) {
-            return ImmutableSet.of(artifact);
-        }
-        return Collections.emptySet();
-    }
-
-    private boolean downloadFailed(ArtifactDownloadReport artifactReport) {
-        // Ivy reports FAILED with MISSING_ARTIFACT message when the artifact doesn't exist.
-        return artifactReport.getDownloadStatus() == DownloadStatus.FAILED
-                && !artifactReport.getDownloadDetails().equals(ArtifactDownloadReport.MISSING_ARTIFACT);
-    }
-
-    private boolean isChanging(ResolvedModuleRevision resolvedModuleRevision) {
-        return new ChangingModuleDetector(resolver).isChangingModule(resolvedModuleRevision.getDescriptor());
-    }
-}
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
deleted file mode 100644
index 9a27910..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyDependencyResolverRepositoryFactory.java
+++ /dev/null
@@ -1,24 +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.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
deleted file mode 100644
index 8030383..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/LegacyResolverParserSettings.java
+++ /dev/null
@@ -1,98 +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.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
deleted file mode 100644
index 2e1520c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/legacy/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.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
deleted file mode 100644
index f423aae..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/AbstractVersionList.java
+++ /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.api.internal.artifacts.repositories.resolver;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-
-import java.util.Collections;
-import java.util.List;
-
-abstract class AbstractVersionList implements VersionList {
-    public boolean isEmpty() {
-        return getVersions().isEmpty();
-    }
-
-    public List<ListedVersion> sortLatestFirst(LatestStrategy latestStrategy) {
-        List<ListedVersion> versions = Lists.newArrayList(getVersions());
-        List<ListedVersion> sorted = latestStrategy.sort(versions);
-        Collections.reverse(sorted);
-
-        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
deleted file mode 100644
index 45f6af0..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
+++ /dev/null
@@ -1,79 +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.resolver;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.resource.ResourceException;
-import org.gradle.api.internal.resource.ResourceNotFoundException;
-import org.gradle.util.DeprecationLogger;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-public class ChainedVersionLister implements VersionLister {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
-    private final List<VersionLister> versionListers;
-
-    public ChainedVersionLister(VersionLister... versionlisters) {
-        this.versionListers = Arrays.asList(versionlisters);
-    }
-
-    public VersionList getVersionList(final ModuleIdentifier module)  {
-        final List<VersionList> versionLists = new ArrayList<VersionList>();
-        for (VersionLister lister : versionListers) {
-            versionLists.add(lister.getVersionList(module));
-        }
-        return new AbstractVersionList() {
-            public void visit(ResourcePattern pattern, ModuleVersionArtifactMetaData artifact) throws ResourceException {
-                final Iterator<VersionList> versionListIterator = versionLists.iterator();
-                while (versionListIterator.hasNext()) {
-                    VersionList list = versionListIterator.next();
-                    try {
-                        list.visit(pattern, artifact);
-                        return;
-                    } catch (ResourceNotFoundException e) {
-                        if (!versionListIterator.hasNext()) {
-                            throw e;
-                        }
-                    } catch (Exception e) {
-                        if (versionListIterator.hasNext()) {
-                            String deprecationMessage = String.format(
-                                    "Error listing versions of %s using %s. Will attempt an alternate way to list versions",
-                                    module, list.getClass()
-                            );
-                            DeprecationLogger.nagUserOfDeprecatedBehaviour(deprecationMessage);
-                            LOGGER.debug(deprecationMessage, e);
-                        } else {
-                            throw new ResourceException(String.format("Failed to list versions for %s.", module), e);
-                        }
-                    }
-                }
-            }
-
-            public Set<ListedVersion> getVersions() {
-                Set<ListedVersion> allVersions = new HashSet<ListedVersion>();
-                for (VersionList versionList : versionLists) {
-                    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
deleted file mode 100644
index abb1756..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java
+++ /dev/null
@@ -1,58 +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.repositories.resolver;
-
-import org.gradle.api.artifacts.ComponentMetadataDetails;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.metadata.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
deleted file mode 100644
index 8f416d5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/DefaultVersionList.java
+++ /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.api.internal.artifacts.repositories.resolver;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public abstract class DefaultVersionList extends AbstractVersionList {
-    private final Map<String, ListedVersion> versions = new HashMap<String, ListedVersion>();
-
-    protected void add(ListedVersion newVersion) {
-        if (versions.containsKey(newVersion.getVersion())) {
-            return;
-        }
-        versions.put(newVersion.getVersion(), newVersion);
-    }
-
-    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
deleted file mode 100644
index fd094f9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
+++ /dev/null
@@ -1,599 +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.resolver;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import org.apache.ivy.core.settings.IvySettings;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.gradle.api.Nullable;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.resolution.JvmLibraryJavadocArtifact;
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-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.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.*;
-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;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.internal.SystemProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import static org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache.ExternalResourceDownloader;
-
-public abstract class ExternalResourceResolver implements ModuleVersionPublisher, ConfiguredModuleVersionRepository, LocalArtifactsModuleVersionRepository {
-    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
-
-    private final MetaDataParser metaDataParser;
-
-    private List<String> ivyPatterns = new ArrayList<String>();
-    private List<String> artifactPatterns = new ArrayList<String>();
-    private boolean m2Compatible;
-    private boolean checkConsistency = true;
-    private boolean allowMissingDescriptor = true;
-    private boolean force;
-    private String checksums;
-    private String name;
-    private RepositoryArtifactCache repositoryCacheManager;
-    private String changingMatcherName;
-    private String changingPattern;
-    private RepositoryChain repositoryChain;
-
-    private final ExternalResourceRepository repository;
-    private final LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder;
-    private final ResolverStrategy resolverStrategy;
-
-    protected VersionLister versionLister;
-
-
-    public ExternalResourceResolver(String name,
-                                    ExternalResourceRepository repository,
-                                    VersionLister versionLister,
-                                    LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                                    MetaDataParser metaDataParser,
-                                    ResolverStrategy resolverStrategy) {
-        this.name = name;
-        this.versionLister = versionLister;
-        this.repository = repository;
-        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
-        this.metaDataParser = metaDataParser;
-        this.resolverStrategy = resolverStrategy;
-    }
-
-    public String getId() {
-        return DependencyResolverIdentifier.forExternalResourceResolver(this);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public boolean isDynamicResolveMode() {
-        return false;
-    }
-
-    public String toString() {
-        return String.format("Repository '%s'", getName());
-    }
-
-    public void setRepositoryChain(RepositoryChain resolver) {
-        this.repositoryChain = resolver;
-    }
-
-    protected ExternalResourceRepository getRepository() {
-        return repository;
-    }
-
-    public boolean isLocal() {
-        return repositoryCacheManager.isLocal();
-    }
-
-    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionSelectionResolveResult result) {
-        ModuleIdentifier module  = new DefaultModuleIdentifier(dependency.getRequested().getGroup(), dependency.getRequested().getName());
-        VersionList versionList = versionLister.getVersionList(module);
-        // List modules based on metadata files
-        ModuleVersionArtifactMetaData metaDataArtifact = getMetaDataArtifactFor(dependency);
-        listVersionsForAllPatterns(getIvyPatterns(), metaDataArtifact, versionList);
-
-        // List modules with missing metadata files
-        if (isAllownomd()) {
-            for (ModuleVersionArtifactMetaData otherArtifact : getDefaultMetaData(dependency).getArtifacts()) {
-                listVersionsForAllPatterns(getArtifactPatterns(), otherArtifact, versionList);
-            }
-        }
-        DefaultModuleVersionListing moduleVersions = new DefaultModuleVersionListing();
-        for (VersionList.ListedVersion listedVersion : versionList.getVersions()) {
-            moduleVersions.add(listedVersion.getVersion());
-        }
-        result.listed(moduleVersions);
-    }
-
-    private void listVersionsForAllPatterns(List<String> patternList, ModuleVersionArtifactMetaData artifact, VersionList versionList) {
-        for (String pattern : patternList) {
-            ResourcePattern resourcePattern = toResourcePattern(pattern);
-            versionList.visit(resourcePattern, artifact);
-        }
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        resolveStaticDependency(dependency, result, createArtifactResolver());
-    }
-
-    protected final void resolveStaticDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result, ArtifactResolver artifactResolver) {
-        MutableModuleVersionMetaData metaDataArtifactMetaData = findMetaDataArtifact(dependency, artifactResolver);
-
-        if (metaDataArtifactMetaData != null) {
-            LOGGER.debug("Metadata file found for module '{}' in repository '{}'.", dependency.getRequested(), getName());
-            result.resolved(metaDataArtifactMetaData, null);
-            return;
-        }
-
-        if (isAllownomd()) {
-            MutableModuleVersionMetaData defaultArtifactMetaData = findDefaultArtifact(dependency, artifactResolver);
-            if (defaultArtifactMetaData != null) {
-                LOGGER.debug("Artifact file found for module '{}' in repository '{}'.", dependency.getRequested(), getName());
-                result.resolved(defaultArtifactMetaData, null);
-                return;
-            }
-        }
-
-        LOGGER.debug("No meta-data file or artifact found for module '{}' in repository '{}'.", dependency.getRequested(), getName());
-        result.missing();
-    }
-
-    protected MutableModuleVersionMetaData findMetaDataArtifact(DependencyMetaData dependency, ArtifactResolver artifactResolver) {
-        ModuleVersionArtifactMetaData artifact = getMetaDataArtifactFor(dependency);
-        if (artifact == null) {
-            return null;
-        }
-        ExternalResource metaDataResource = artifactResolver.resolveMetaDataArtifact(artifact);
-        if (metaDataResource == null) {
-            return null;
-        }
-        MutableModuleVersionMetaData moduleVersionMetaData = getArtifactMetadata(artifact, metaDataResource);
-
-        if (isCheckconsistency()) {
-            ModuleVersionSelector requested = dependency.getRequested();
-            ModuleVersionIdentifier requestedId = DefaultModuleVersionIdentifier.newId(requested.getGroup(), requested.getName(), requested.getVersion());
-            checkMetadataConsistency(requestedId, moduleVersionMetaData);
-        }
-        return moduleVersionMetaData;
-    }
-
-    protected MutableModuleVersionMetaData getArtifactMetadata(ModuleVersionArtifactMetaData artifact, ExternalResource resource) {
-        ExternalResourceResolverDescriptorParseContext context = new ExternalResourceResolverDescriptorParseContext(repositoryChain, this);
-        LocallyAvailableExternalResource cachedResource;
-        try {
-            cachedResource = downloadAndCacheResource(artifact, resource);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-
-        MutableModuleVersionMetaData metaData =  metaDataParser.parseMetaData(context, cachedResource);
-        return processMetaData(metaData);
-    }
-
-    private MutableModuleVersionMetaData findDefaultArtifact(DependencyMetaData dependency, ArtifactResolver artifactResolver) {
-        MutableModuleVersionMetaData metaData = getDefaultMetaData(dependency);
-
-        if (hasArtifacts(metaData, artifactResolver)) {
-            LOGGER.debug("No meta-data file found for module '{}' in repository '{}', using default data instead.", dependency.getRequested(), getName());
-
-            return metaData;
-        }
-        return null;
-    }
-
-    protected MutableModuleVersionMetaData getDefaultMetaData(DependencyMetaData dependency) {
-        MutableModuleVersionMetaData metaData = ModuleDescriptorAdapter.defaultForDependency(dependency);
-        return processMetaData(metaData);
-    }
-
-    private MutableModuleVersionMetaData processMetaData(MutableModuleVersionMetaData metaData) {
-        metaData.setChanging(isChanging(metaData.getId().getVersion()));
-        return metaData;
-    }
-
-    private void checkMetadataConsistency(ModuleVersionIdentifier expectedId, ModuleVersionMetaData metadata) throws MetaDataParseException {
-        List<String> errors = new ArrayList<String>();
-        if (!expectedId.getGroup().equals(metadata.getId().getGroup())) {
-            errors.add("bad group: expected='" + expectedId.getGroup() + "' found='" + metadata.getId().getGroup() + "'");
-        }
-        if (!expectedId.getName().equals(metadata.getId().getName())) {
-            errors.add("bad module name: expected='" + expectedId.getName() + "' found='" + metadata.getId().getName() + "'");
-        }
-        if (!expectedId.getVersion().equals(metadata.getId().getVersion())) {
-            errors.add("bad version: expected='" + expectedId.getVersion() + "' found='" + metadata.getId().getVersion() + "'");
-        }
-        if (errors.size() > 0) {
-            throw new MetaDataParseException(String.format("inconsistent module metadata found. Descriptor: %s Errors: %s",
-                    metadata.getId(), Joiner.on(SystemProperties.getLineSeparator()).join(errors)));
-        }
-    }
-
-    @Nullable
-    private ModuleVersionArtifactMetaData getMetaDataArtifactFor(DependencyMetaData dependency) {
-        return getMetaDataArtifactFor(DefaultModuleVersionIdentifier.newId(dependency.getDescriptor().getDependencyRevisionId()));
-    }
-
-    public void localResolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        doResolveModuleArtifacts((ModuleVersionMetaData) component, context, result, true);
-    }
-
-    public void resolveModuleArtifacts(ComponentMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result) {
-        doResolveModuleArtifacts((ModuleVersionMetaData) component, context, result, false);
-    }
-
-    // TODO:DAZ This "local-only" pattern is quite ugly: improve it.
-    private void doResolveModuleArtifacts(ModuleVersionMetaData component, ArtifactResolveContext context, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        try {
-            if (context instanceof ConfigurationResolveContext) {
-                String configurationName = ((ConfigurationResolveContext) context).getConfigurationName();
-                ConfigurationMetaData configuration = component.getConfiguration(configurationName);
-                resolveConfigurationArtifacts(component, configuration, result, localOnly);
-            } else {
-                Class<? extends SoftwareArtifact> artifactType = ((ArtifactTypeResolveContext) context).getArtifactType();
-                if (artifactType == JvmLibraryJavadocArtifact.class) {
-                    resolveJavadocArtifacts(component, result, localOnly);
-                } else if (artifactType == JvmLibrarySourcesArtifact.class) {
-                    resolveSourceArtifacts(component, result, localOnly);
-                } else if (isMetaDataArtifact(artifactType)) {
-                    resolveMetaDataArtifacts(component, result);
-                }
-
-                if (!localOnly && !result.hasResult()) {
-                    result.failed(new ArtifactResolveException(component.getComponentId(),
-                            String.format("Cannot locate artifacts of type %s for '%s' in repository '%s'", artifactType.getSimpleName(), component, name)));
-                }
-            }
-        } catch (Exception e) {
-            result.failed(new ArtifactResolveException(component.getComponentId(), e));
-        }
-    }
-
-    protected void resolveConfigurationArtifacts(ModuleVersionMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        result.resolved(configuration.getArtifacts());
-    }
-
-    protected abstract boolean isMetaDataArtifact(Class<? extends SoftwareArtifact> artifactType);
-
-    protected void resolveMetaDataArtifacts(ModuleVersionMetaData module, BuildableArtifactSetResolveResult result) {
-        ModuleVersionArtifactMetaData artifact = getMetaDataArtifactFor(module.getId());
-        if (artifact != null) {
-            result.resolved(ImmutableSet.of(artifact));
-        }
-    }
-
-    protected void resolveJavadocArtifacts(ModuleVersionMetaData module, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        if (!localOnly) {
-            result.resolved(findOptionalArtifacts(module, "javadoc", "javadoc"));
-        }
-    }
-
-    protected void resolveSourceArtifacts(ModuleVersionMetaData module, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        if (!localOnly) {
-            result.resolved(findOptionalArtifacts(module, "source", "sources"));
-        }
-    }
-
-    protected Set<ModuleVersionArtifactMetaData> findOptionalArtifacts(ModuleVersionMetaData module, String type, String classifier) {
-        ModuleVersionArtifactMetaData artifact = module.artifact(type, "jar", classifier);
-        if (createArtifactResolver(module.getSource()).artifactExists(artifact)) {
-            return ImmutableSet.of(artifact);
-        }
-        return Collections.emptySet();
-    }
-
-    @Nullable
-    protected abstract ModuleVersionArtifactMetaData getMetaDataArtifactFor(ModuleVersionIdentifier moduleVersionIdentifier);
-
-    protected boolean hasArtifacts(ModuleVersionMetaData metaData, ArtifactResolver artifactResolver) {
-        for (ModuleVersionArtifactMetaData artifactMetaData : metaData.getArtifacts()) {
-            if (artifactResolver.artifactExists(artifactMetaData)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean artifactExists(ModuleVersionArtifactMetaData artifact) {
-        return createArtifactResolver().artifactExists(artifact);
-    }
-
-    // TODO:DAZ This is currently required to handle maven snapshots: if the timestamp was part of the identifier this wouldn't be required
-    protected ArtifactResolver createArtifactResolver(ModuleSource moduleSource) {
-        return createArtifactResolver();
-    }
-
-    private LocallyAvailableExternalResource downloadAndCacheResource(ModuleVersionArtifactMetaData artifact, ExternalResource resource) throws IOException {
-        final ExternalResourceDownloader resourceDownloader = new VerifyingExternalResourceDownloader(getChecksumAlgorithms(), getRepository());
-        return repositoryCacheManager.downloadAndCacheArtifactFile(artifact, resourceDownloader, resource);
-    }
-
-    public void resolveArtifact(ComponentArtifactMetaData componentArtifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
-        ModuleVersionArtifactMetaData artifact = (ModuleVersionArtifactMetaData) componentArtifact;
-
-        File localFile;
-        try {
-            localFile = download(artifact, moduleSource);
-        } catch (Throwable e) {
-            result.failed(new ArtifactResolveException(artifact.getId(), e));
-            return;
-        }
-
-        if (localFile != null) {
-            result.resolved(localFile);
-        } else {
-            result.notFound(artifact.getId());
-        }
-    }
-
-    protected File download(ModuleVersionArtifactMetaData artifact, ModuleSource moduleSource) throws IOException {
-        return downloadArtifact(artifact, createArtifactResolver(moduleSource));
-    }
-
-    protected File downloadArtifact(ModuleVersionArtifactMetaData artifact, ArtifactResolver artifactResolver) throws IOException {
-        ExternalResource artifactResource = artifactResolver.resolveArtifact(artifact);
-        if (artifactResource == null) {
-            return null;
-        }
-
-        return downloadAndCacheResource(artifact, artifactResource).getLocalResource().getFile();
-    }
-
-    protected ArtifactResolver createArtifactResolver() {
-        return new ArtifactResolver(getIvyPatterns(), getArtifactPatterns());
-    }
-
-    public void setSettings(IvySettings settings) {
-    }
-
-    public void publish(ModuleVersionPublishMetaData moduleVersion) throws IOException {
-        for (ModuleVersionArtifactPublishMetaData artifact : moduleVersion.getArtifacts()) {
-            publish(new DefaultModuleVersionArtifactMetaData(artifact.getId()), artifact.getFile());
-        }
-    }
-
-    private void publish(ModuleVersionArtifactMetaData artifact, File src) throws IOException {
-        String destinationPattern;
-        if ("ivy".equals(artifact.getName().getType()) && !getIvyPatterns().isEmpty()) {
-            destinationPattern = getIvyPatterns().get(0);
-        } else if (!getArtifactPatterns().isEmpty()) {
-            destinationPattern = getArtifactPatterns().get(0);
-        } else {
-            throw new IllegalStateException("impossible to publish " + artifact + " using " + this + ": no artifact pattern defined");
-        }
-        String destination = toResourcePattern(destinationPattern).toPath(artifact);
-
-        put(src, destination);
-        LOGGER.info("Published {} to {}", artifact, destination);
-    }
-
-    private void put(File src, String destination) throws IOException {
-        String[] checksums = getChecksumAlgorithms();
-        if (checksums.length != 0) {
-            // Should not be reachable for publishing
-            throw new UnsupportedOperationException();
-        }
-
-        repository.put(src, destination);
-    }
-
-    public void addIvyPattern(String pattern) {
-        ivyPatterns.add(pattern);
-    }
-
-    public void addArtifactPattern(String pattern) {
-        artifactPatterns.add(pattern);
-    }
-
-    public List<String> getIvyPatterns() {
-        return Collections.unmodifiableList(ivyPatterns);
-    }
-
-    public List<String> getArtifactPatterns() {
-        return Collections.unmodifiableList(artifactPatterns);
-    }
-
-    protected void setIvyPatterns(List<String> patterns) {
-        ivyPatterns = patterns;
-    }
-
-    protected void setArtifactPatterns(List<String> patterns) {
-        artifactPatterns = patterns;
-    }
-
-    public boolean isM2compatible() {
-        return m2Compatible;
-    }
-
-    public void setM2compatible(boolean compatible) {
-        m2Compatible = compatible;
-    }
-
-    public boolean isCheckconsistency() {
-        return checkConsistency;
-    }
-
-    public void setCheckconsistency(boolean checkConsistency) {
-        this.checkConsistency = checkConsistency;
-    }
-
-    public void setForce(boolean force) {
-        this.force = force;
-    }
-
-    public boolean isForce() {
-        return force;
-    }
-
-    public boolean isAllownomd() {
-        return allowMissingDescriptor;
-    }
-
-    public void setAllownomd(boolean allowMissingDescriptor) {
-        this.allowMissingDescriptor = allowMissingDescriptor;
-    }
-
-    public String[] getChecksumAlgorithms() {
-        if (checksums == null) {
-            return new String[0];
-        }
-        // csDef is a comma separated list of checksum algorithms to use with this resolver
-        // we parse and return it as a String[]
-        String[] checksums = this.checksums.split(",");
-        List<String> algos = new ArrayList<String>();
-        for (int i = 0; i < checksums.length; i++) {
-            String cs = checksums[i].trim();
-            if (!"".equals(cs) && !"none".equals(cs)) {
-                algos.add(cs);
-            }
-        }
-        return algos.toArray(new String[algos.size()]);
-    }
-
-    public void setChecksums(String checksums) {
-        this.checksums = checksums;
-    }
-
-    public String getChangingMatcherName() {
-        return changingMatcherName;
-    }
-
-    public void setChangingMatcher(String changingMatcherName) {
-        this.changingMatcherName = changingMatcherName;
-    }
-
-    public String getChangingPattern() {
-        return changingPattern;
-    }
-
-    public void setChangingPattern(String changingPattern) {
-        this.changingPattern = changingPattern;
-    }
-
-    public void setRepositoryCacheManager(RepositoryArtifactCache repositoryCacheManager) {
-        this.repositoryCacheManager = repositoryCacheManager;
-    }
-
-    protected ResourcePattern toResourcePattern(String pattern) {
-        return isM2compatible() ? new M2ResourcePattern(pattern) : new IvyResourcePattern(pattern);
-    }
-
-    private boolean isChanging(String version) {
-        if (changingMatcherName == null || changingPattern == null) {
-            return false;
-        }
-        PatternMatcher matcher = resolverStrategy.getPatternMatcher(changingMatcherName);
-        if (matcher == null) {
-            throw new IllegalStateException("unknown matcher '" + changingMatcherName
-                    + "'. It is set as changing matcher in " + this);
-        }
-        return matcher.getMatcher(changingPattern).matches(version);
-    }
-
-    // TODO:DAZ Extract this properly: make this static
-    protected class ArtifactResolver {
-        private final List<String> ivyPatterns;
-        private final List<String> artifactPatterns;
-
-        public ArtifactResolver(List<String> ivyPatterns, List<String> artifactPatterns) {
-            this.ivyPatterns = ivyPatterns;
-            this.artifactPatterns = artifactPatterns;
-        }
-
-        public ExternalResource resolveMetaDataArtifact(ModuleVersionArtifactMetaData artifact) {
-            return findStaticResourceUsingPatterns(ivyPatterns, artifact, true);
-        }
-
-        public ExternalResource resolveArtifact(ModuleVersionArtifactMetaData artifact) {
-            return findStaticResourceUsingPatterns(artifactPatterns, artifact, true);
-        }
-
-        public boolean artifactExists(ModuleVersionArtifactMetaData artifact) {
-            return findStaticResourceUsingPatterns(artifactPatterns, artifact, false) != null;
-        }
-
-        private ExternalResource findStaticResourceUsingPatterns(List<String> patternList, ModuleVersionArtifactMetaData artifact, boolean forDownload) {
-            for (String pattern : patternList) {
-                ResourcePattern resourcePattern = toResourcePattern(pattern);
-                String resourceName = resourcePattern.toPath(artifact);
-                LOGGER.debug("Loading {}", resourceName);
-                ExternalResource resource = getResource(resourceName, artifact, forDownload);
-                if (resource.exists()) {
-                    return resource;
-                } else {
-                    LOGGER.debug("Resource not reachable for {}: res={}", artifact, resource);
-                    discardResource(resource);
-                }
-            }
-            return null;
-        }
-
-        private ExternalResource getResource(String source, ModuleVersionArtifactMetaData target, boolean forDownload) {
-            try {
-                if (forDownload) {
-                    LocallyAvailableResourceCandidates localCandidates = locallyAvailableResourceFinder.findCandidates(target);
-                    ExternalResource resource = repository.getResource(source, localCandidates);
-                    return resource == null ? new MissingExternalResource(source) : resource;
-                } else {
-                    // TODO - there's a potential problem here in that we don't carry correct isLocal data in MetaDataOnlyExternalResource
-                    ExternalResourceMetaData metaData = repository.getResourceMetaData(source);
-                    return metaData == null ? new MissingExternalResource(source) : new MetaDataOnlyExternalResource(source, metaData);
-                }
-            } catch (IOException e) {
-                throw new RuntimeException(String.format("Could not get resource '%s'.", source), e);
-            }
-        }
-
-        protected void discardResource(ExternalResource resource) {
-            try {
-                resource.close();
-            } catch (IOException e) {
-                LOGGER.warn("Exception closing resource " + resource.getName(), e);
-            }
-        }
-    }
-}
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
deleted file mode 100644
index df22bdd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
+++ /dev/null
@@ -1,72 +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.repositories.resolver;
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.DefaultDependencyMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-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;
-
-/**
- * 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 implements DescriptorParseContext {
-    private final RepositoryChain mainResolvers;
-    private final ExternalResourceResolver moduleResolver;
-
-    public ExternalResourceResolverDescriptorParseContext(RepositoryChain mainResolvers, ExternalResourceResolver moduleResolver) {
-        this.mainResolvers = mainResolvers;
-        this.moduleResolver = moduleResolver;
-    }
-
-    public boolean artifactExists(ModuleVersionArtifactMetaData artifact) {
-        return moduleResolver.artifactExists(artifact);
-    }
-
-    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, Class<? extends SoftwareArtifact> artifactType) {
-        File resolvedArtifactFile = resolveMetaDataArtifactFile(moduleVersionIdentifier, mainResolvers.getDependencyResolver(), mainResolvers.getArtifactResolver(), artifactType);
-        LocallyAvailableResource localResource = new DefaultLocallyAvailableResource(resolvedArtifactFile);
-        return new DefaultLocallyAvailableExternalResource(resolvedArtifactFile.toURI().toString(), localResource);
-    }
-
-    private File resolveMetaDataArtifactFile(ModuleVersionIdentifier moduleVersionIdentifier, DependencyToModuleVersionResolver dependencyResolver,
-                                             ArtifactResolver artifactResolver, Class<? extends SoftwareArtifact> artifactType) {
-        BuildableComponentResolveResult moduleVersionResolveResult = new DefaultBuildableComponentResolveResult();
-        dependencyResolver.resolve(new DefaultDependencyMetaData(new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId(moduleVersionIdentifier), true)), moduleVersionResolveResult);
-
-        BuildableArtifactSetResolveResult moduleArtifactsResolveResult = new DefaultBuildableArtifactSetResolveResult();
-        ArtifactTypeResolveContext context = new ArtifactTypeResolveContext(artifactType);
-        artifactResolver.resolveModuleArtifacts(moduleVersionResolveResult.getMetaData(), context, moduleArtifactsResolveResult);
-
-        BuildableArtifactResolveResult artifactResolveResult = new DefaultBuildableArtifactResolveResult();
-        ComponentArtifactMetaData artifactMetaData = moduleArtifactsResolveResult.getArtifacts().iterator().next();
-        artifactResolver.resolveArtifact(artifactMetaData, moduleVersionResolveResult.getMetaData().getSource(), artifactResolveResult);
-        return artifactResolveResult.getFile();
-    }
-}
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
deleted file mode 100644
index c1e9000..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
+++ /dev/null
@@ -1,92 +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.resolver;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DownloadedIvyModuleDescriptorParser;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.*;
-import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
-import org.gradle.api.internal.artifacts.resolution.IvyDescriptorArtifact;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-
-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<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                       boolean dynamicResolve, ResolverStrategy resolverStrategy) {
-        super(name, transport.getRepository(), new ResourceVersionLister(transport.getRepository()),
-                locallyAvailableResourceFinder, new DownloadedIvyModuleDescriptorParser(resolverStrategy),
-                resolverStrategy);
-        this.transport = transport;
-        this.transport.configureCacheManager(this);
-        this.dynamicResolve = dynamicResolve;
-    }
-
-    @Override
-    public boolean isDynamicResolveMode() {
-        return dynamicResolve;
-    }
-
-    @Override
-    protected boolean isMetaDataArtifact(Class<? extends SoftwareArtifact> artifactType) {
-        return artifactType == IvyDescriptorArtifact.class;
-    }
-
-    @Nullable
-    protected ModuleVersionArtifactMetaData getMetaDataArtifactFor(ModuleVersionIdentifier moduleVersionIdentifier) {
-        DefaultModuleVersionArtifactIdentifier artifactId = new DefaultModuleVersionArtifactIdentifier(moduleVersionIdentifier, "ivy", "ivy", "xml");
-        return new DefaultModuleVersionArtifactMetaData(artifactId);
-    }
-
-    public void addArtifactLocation(URI baseUri, String pattern) {
-        String artifactPattern = transport.convertToPath(baseUri) + pattern;
-        addArtifactPattern(artifactPattern);
-    }
-
-    public void addDescriptorLocation(URI baseUri, String pattern) {
-        String descriptorPattern = transport.convertToPath(baseUri) + pattern;
-        addIvyPattern(descriptorPattern);
-    }
-
-    @Override
-    protected void resolveJavadocArtifacts(ModuleVersionMetaData module, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        ConfigurationMetaData configuration = module.getConfiguration("javadoc");
-        if (configuration != null) {
-            result.resolved(configuration.getArtifacts());
-        } else {
-            super.resolveJavadocArtifacts(module, result, localOnly);
-        }
-    }
-
-    @Override
-    protected void resolveSourceArtifacts(ModuleVersionMetaData module, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        ConfigurationMetaData configuration = module.getConfiguration("sources");
-        if (configuration != null) {
-            result.resolved(configuration.getArtifacts());
-        } else {
-            super.resolveSourceArtifacts(module, result, localOnly);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java
deleted file mode 100644
index 1771ed6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java
+++ /dev/null
@@ -1,84 +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.resolver;
-
-import org.apache.ivy.core.IvyPatternHelper;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.metadata.IvyArtifactName;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class IvyResourcePattern implements ResourcePattern {
-    private final String pattern;
-
-    public IvyResourcePattern(String pattern) {
-        this.pattern = pattern;
-    }
-
-    public String getPattern() {
-        return pattern;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Ivy pattern '%s'", pattern);
-    }
-
-    public String toPath(ModuleVersionArtifactMetaData artifact) {
-        Map<String, Object> attributes = toAttributes(artifact);
-        return IvyPatternHelper.substituteTokens(pattern, attributes);
-    }
-
-    public String toVersionListPattern(ModuleVersionArtifactMetaData artifactId) {
-        Map<String, Object> attributes = toAttributes(artifactId);
-        attributes.remove(IvyPatternHelper.REVISION_KEY);
-        return IvyPatternHelper.substituteTokens(pattern, attributes);
-    }
-
-    public String toModulePath(ModuleIdentifier module) {
-        throw new UnsupportedOperationException("not implemented yet.");
-    }
-
-    public String toModuleVersionPath(ModuleVersionArtifactMetaData artifact) {
-        throw new UnsupportedOperationException("not implemented yet.");
-    }
-
-    // TODO:DAZ Handle extra attributes
-    protected Map<String, Object> toAttributes(ModuleVersionArtifactMetaData artifact) {
-        HashMap<String, Object> attributes = new HashMap<String, Object>();
-        ModuleComponentIdentifier moduleVersionIdentifier = artifact.getId().getComponentIdentifier();
-        IvyArtifactName ivyArtifact = artifact.getName();
-        attributes.put(IvyPatternHelper.ORGANISATION_KEY, moduleVersionIdentifier.getGroup());
-        attributes.put(IvyPatternHelper.MODULE_KEY, moduleVersionIdentifier.getModule());
-        attributes.put(IvyPatternHelper.REVISION_KEY, moduleVersionIdentifier.getVersion());
-        attributes.put(IvyPatternHelper.ARTIFACT_KEY, ivyArtifact.getName());
-        attributes.put(IvyPatternHelper.TYPE_KEY, ivyArtifact.getType());
-        attributes.put(IvyPatternHelper.EXT_KEY, ivyArtifact.getExtension());
-        attributes.put("classifier", ivyArtifact.getClassifier());
-        return attributes;
-    }
-
-    protected Map<String, Object> toAttributes(ModuleIdentifier module) {
-        HashMap<String, Object> attributes = new HashMap<String, Object>();
-        attributes.put(IvyPatternHelper.ORGANISATION_KEY, module.getGroup());
-        attributes.put(IvyPatternHelper.MODULE_KEY, module.getName());
-        return attributes;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java
deleted file mode 100644
index 3df3368..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java
+++ /dev/null
@@ -1,79 +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.resolver;
-
-import org.apache.ivy.core.IvyPatternHelper;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-
-import java.util.Map;
-
-// TODO:DAZ Should extend common base class
-public class M2ResourcePattern extends IvyResourcePattern {
-    public M2ResourcePattern(String pattern) {
-        super(pattern);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("M2 pattern '%s'", getPattern());
-    }
-
-    @Override
-    public String toModulePath(ModuleIdentifier module) {
-        String pattern = getPattern();
-        if (!pattern.endsWith(MavenPattern.M2_PATTERN)) {
-            throw new UnsupportedOperationException("Cannot locate module for non-maven layout.");
-        }
-        String metaDataPattern = pattern.substring(0, pattern.length() - MavenPattern.M2_PER_MODULE_PATTERN.length() - 1);
-        return IvyPatternHelper.substituteTokens(metaDataPattern, toAttributes(module));
-    }
-
-    @Override
-    public String toPath(ModuleVersionArtifactMetaData artifact) {
-        Map<String, Object> attributes = toAttributes(artifact);
-        return IvyPatternHelper.substituteTokens(getPattern(), attributes);
-    }
-
-    @Override
-    public String toModuleVersionPath(ModuleVersionArtifactMetaData artifact) {
-        String pattern = getPattern();
-        if (!pattern.endsWith(MavenPattern.M2_PATTERN)) {
-            throw new UnsupportedOperationException("Cannot locate module version for non-maven layout.");
-        }
-        String metaDataPattern = pattern.substring(0, pattern.length() - MavenPattern.M2_PER_MODULE_VERSION_PATTERN.length() - 1);
-        return IvyPatternHelper.substituteTokens(metaDataPattern, toAttributes(artifact));
-    }
-
-    @Override
-    protected Map<String, Object> toAttributes(ModuleVersionArtifactMetaData artifact) {
-        return mapOrganisation(super.toAttributes(artifact));
-    }
-
-    @Override
-    protected Map<String, Object> toAttributes(ModuleIdentifier module) {
-        return mapOrganisation(super.toAttributes(module));
-    }
-
-    private Map<String, Object> mapOrganisation(Map<String, Object> attributes) {
-        String org = (String) attributes.get(IvyPatternHelper.ORGANISATION_KEY);
-        if (org != null) {
-            attributes.put(IvyPatternHelper.ORGANISATION_KEY, org.replace(".", "/"));
-        }
-        return attributes;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java
deleted file mode 100644
index 3097423..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java
+++ /dev/null
@@ -1,57 +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.repositories.resolver;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.URI;
-
-public class MavenLocalResolver extends MavenResolver {
-    private static final Logger LOGGER = LoggerFactory.getLogger(MavenResolver.class);
-
-    public MavenLocalResolver(String name, URI rootUri, RepositoryTransport transport,
-                              LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                              ResolverStrategy resolverStrategy) {
-        super(name, rootUri, transport, locallyAvailableResourceFinder, resolverStrategy);
-    }
-
-    @Override
-    protected MutableModuleVersionMetaData findMetaDataArtifact(DependencyMetaData dependency, ArtifactResolver artifactResolver) {
-        MutableModuleVersionMetaData metaData = super.findMetaDataArtifact(dependency, artifactResolver);
-        if (isOrphanedPom(metaData, artifactResolver)) {
-            return null;
-        }
-        return metaData;
-    }
-
-    private boolean isOrphanedPom(ModuleVersionMetaData metaData, ArtifactResolver artifactResolver) {
-        if (!metaData.isMetaDataOnly()) {
-            if (!hasArtifacts(metaData, artifactResolver)) {
-                LOGGER.debug("POM file found for module '{}' in repository '{}' but no artifact found. Ignoring.", metaData.getId(), getName());
-                return true;
-            }
-        }
-        return false;
-    }
-}
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
deleted file mode 100644
index 7f794a2..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
+++ /dev/null
@@ -1,89 +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.resolver;
-
-import org.apache.ivy.util.ContextualSAXHandler;
-import org.apache.ivy.util.XMLHelper;
-import org.gradle.internal.ErroringAction;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.io.InputStream;
-
-class MavenMetadataLoader {
-    private static final Logger LOGGER = LoggerFactory.getLogger(MavenMetadataLoader.class);
-
-    private final ExternalResourceRepository repository;
-
-    public MavenMetadataLoader(ExternalResourceRepository repository) {
-        this.repository = repository;
-    }
-
-    public MavenMetadata load(String metadataLocation) throws ResourceNotFoundException, ResourceException {
-        MavenMetadata metadata = new MavenMetadata();
-        try {
-            parseMavenMetadataInfo(metadataLocation, metadata);
-        } catch (ResourceNotFoundException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new ResourceException(String.format("Unable to load Maven meta-data from %s.", metadataLocation), e);
-        }
-        return metadata;
-    }
-
-    private void parseMavenMetadataInfo(final String metadataLocation, final MavenMetadata metadata) throws Exception {
-        final ExternalResource resource = repository.getResource(metadataLocation);
-        if (resource == null) {
-            throw new ResourceNotFoundException(String.format("Maven meta-data not available: %s", metadataLocation));
-        }
-        try {
-            parseMavenMetadataInto(resource, metadata);
-        } finally {
-            resource.close();
-        }
-    }
-
-    private void parseMavenMetadataInto(ExternalResource metadataResource, final MavenMetadata mavenMetadata) throws IOException, SAXException, ParserConfigurationException {
-        LOGGER.debug("parsing maven-metadata: {}", metadataResource);
-        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);
-            }
-        });
-    }
-}
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
deleted file mode 100644
index ea84791..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
+++ /dev/null
@@ -1,275 +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.resolver;
-
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.gradle.api.Transformer;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult;
-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.parser.GradlePomModuleDescriptorParser;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
-import org.gradle.api.internal.artifacts.metadata.*;
-import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
-import org.gradle.api.internal.artifacts.resolution.MavenPomArtifact;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-import org.gradle.api.internal.resource.ResourceNotFoundException;
-import org.gradle.api.resources.ResourceException;
-import org.gradle.util.CollectionUtils;
-import org.gradle.util.DeprecationLogger;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.URI;
-import java.util.*;
-
-public class MavenResolver extends ExternalResourceResolver implements PatternBasedResolver {
-    private static final Logger LOGGER = LoggerFactory.getLogger(MavenResolver.class);
-    private final RepositoryTransport transport;
-    private final String root;
-    private final List<String> artifactRoots = new ArrayList<String>();
-    private String pattern = MavenPattern.M2_PATTERN;
-    private boolean usepoms = true;
-    private boolean useMavenMetadata = true;
-    private final MavenMetadataLoader mavenMetaDataLoader;
-
-    public MavenResolver(String name, URI rootUri, RepositoryTransport transport,
-                         LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> locallyAvailableResourceFinder,
-                         ResolverStrategy resolverStrategy) {
-        super(name, transport.getRepository(),
-                new ChainedVersionLister(new MavenVersionLister(transport.getRepository()), new ResourceVersionLister(transport.getRepository())),
-                locallyAvailableResourceFinder, new GradlePomModuleDescriptorParser(), resolverStrategy);
-        transport.configureCacheManager(this);
-
-        this.mavenMetaDataLoader = new MavenMetadataLoader(transport.getRepository());
-        this.transport = transport;
-        this.root = transport.convertToPath(rootUri);
-
-        super.setM2compatible(true);
-
-        // SNAPSHOT revisions are changing revisions
-        setChangingMatcher(PatternMatcher.REGEXP);
-        setChangingPattern(".*-SNAPSHOT");
-
-        updatePatterns();
-    }
-
-    public void getDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result) {
-        if (isSnapshotVersion(dependency)) {
-            final TimestampedModuleSource uniqueSnapshotVersion = findUniqueSnapshotVersion(dependency.getDescriptor().getDependencyRevisionId());
-            if (uniqueSnapshotVersion != null) {
-                resolveUniqueSnapshotDependency(dependency, result, uniqueSnapshotVersion);
-                return;
-            }
-        }
-
-        resolveStaticDependency(dependency, result, createArtifactResolver());
-    }
-
-    @Override
-    protected boolean isMetaDataArtifact(Class<? extends SoftwareArtifact> artifactType) {
-        return artifactType == MavenPomArtifact.class;
-    }
-
-    private void resolveUniqueSnapshotDependency(DependencyMetaData dependency, BuildableModuleVersionMetaDataResolveResult result, TimestampedModuleSource snapshotSource) {
-        resolveStaticDependency(dependency, result, createArtifactResolver(snapshotSource));
-        if (result.getState() == BuildableModuleVersionMetaDataResolveResult.State.Resolved) {
-            result.setModuleSource(snapshotSource);
-        }
-    }
-
-    private boolean isSnapshotVersion(DependencyMetaData dd) {
-        return dd.getRequested().getVersion().endsWith("SNAPSHOT");
-    }
-
-    @Override
-    protected ArtifactResolver createArtifactResolver(ModuleSource moduleSource) {
-
-        if (moduleSource instanceof TimestampedModuleSource) {
-
-            final String timestampedVersion = ((TimestampedModuleSource) moduleSource).getTimestampedVersion();
-            Transformer<String, String> patternTransformer = new Transformer<String, String>() {
-                public String transform(String original) {
-                    return original.replaceFirst("\\-\\[revision\\]", "-" + timestampedVersion);
-                }
-            };
-            return new ArtifactResolver(
-                    CollectionUtils.collect(getIvyPatterns(), patternTransformer),
-                    CollectionUtils.collect(getArtifactPatterns(), patternTransformer));
-        }
-
-        return super.createArtifactResolver(moduleSource);
-    }
-
-    public void addArtifactLocation(URI baseUri, String pattern) {
-        if (pattern != null && pattern.length() > 0) {
-            throw new IllegalArgumentException("Maven Resolver only supports a single pattern. It cannot be provided on a per-location basis.");
-        }
-        artifactRoots.add(transport.convertToPath(baseUri));
-
-        updatePatterns();
-    }
-
-    public void addDescriptorLocation(URI baseUri, String pattern) {
-        throw new UnsupportedOperationException("Cannot have multiple descriptor urls for MavenResolver");
-    }
-
-    private String getWholePattern() {
-        return root + pattern;
-    }
-
-    private void updatePatterns() {
-        if (isUsepoms()) {
-            setIvyPatterns(Collections.singletonList(getWholePattern()));
-        } else {
-            setIvyPatterns(Collections.<String>emptyList());
-        }
-
-        List<String> artifactPatterns = new ArrayList<String>();
-        artifactPatterns.add(getWholePattern());
-        for (String artifactRoot : artifactRoots) {
-            artifactPatterns.add(artifactRoot + pattern);
-        }
-        setArtifactPatterns(artifactPatterns);
-    }
-
-    @Override
-    protected ModuleVersionArtifactMetaData getMetaDataArtifactFor(ModuleVersionIdentifier moduleVersionIdentifier) {
-        if (isUsepoms()) {
-            DefaultModuleVersionArtifactIdentifier artifactId = new DefaultModuleVersionArtifactIdentifier(moduleVersionIdentifier, moduleVersionIdentifier.getName(), "pom", "pom");
-            return new DefaultModuleVersionArtifactMetaData(artifactId);
-        }
-
-        return null;
-    }
-
-    private TimestampedModuleSource findUniqueSnapshotVersion(ModuleRevisionId moduleRevisionId) {
-        ModuleVersionIdentifier moduleVersionIdentifier = DefaultModuleVersionIdentifier.newId(moduleRevisionId);
-        ModuleVersionArtifactIdentifier artifactId = new DefaultModuleVersionArtifactIdentifier(moduleVersionIdentifier, moduleVersionIdentifier.getName(), "pom", "pom");
-        DefaultModuleVersionArtifactMetaData pomArtifact = new DefaultModuleVersionArtifactMetaData(artifactId);
-        String metadataLocation = toResourcePattern(getWholePattern()).toModuleVersionPath(pomArtifact) + "/maven-metadata.xml";
-        MavenMetadata mavenMetadata = parseMavenMetadata(metadataLocation);
-
-        if (mavenMetadata.timestamp != null) {
-            // we have found a timestamp, so this is a snapshot unique version
-            String rev = moduleRevisionId.getRevision();
-            rev = rev.substring(0, rev.length() - "SNAPSHOT".length());
-            rev = rev + mavenMetadata.timestamp + "-" + mavenMetadata.buildNumber;
-            return new TimestampedModuleSource(rev);
-        }
-        return null;
-    }
-
-    private MavenMetadata parseMavenMetadata(String metadataLocation) {
-        if (shouldUseMavenMetadata(pattern)) {
-            try {
-                return mavenMetaDataLoader.load(metadataLocation);
-            } catch (ResourceNotFoundException e) {
-                return new MavenMetadata();
-            } catch (ResourceException e) {
-                LOGGER.warn("impossible to access Maven metadata file, ignored.", e);
-            }
-        }
-        return new MavenMetadata();
-    }
-
-    // A bunch of configuration properties that we don't (yet) support in our model via the DSL. Users can still tweak these on the resolver using mavenRepo().
-    public boolean isUsepoms() {
-        return usepoms;
-    }
-
-    public void setUsepoms(boolean usepoms) {
-        this.usepoms = usepoms;
-        updatePatterns();
-    }
-
-    public boolean isUseMavenMetadata() {
-        return useMavenMetadata;
-    }
-
-    @Deprecated
-    public void setUseMavenMetadata(boolean useMavenMetadata) {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("MavenResolver.setUseMavenMetadata(boolean)");
-        this.useMavenMetadata = useMavenMetadata;
-        if (useMavenMetadata) {
-            this.versionLister = new ChainedVersionLister(
-                    new MavenVersionLister(getRepository()),
-                    new ResourceVersionLister(getRepository()));
-        } else {
-            this.versionLister = new ResourceVersionLister(getRepository());
-        }
-    }
-
-    private boolean shouldUseMavenMetadata(String pattern) {
-        return isUseMavenMetadata() && pattern.endsWith(MavenPattern.M2_PATTERN);
-    }
-
-    public String getPattern() {
-        return pattern;
-    }
-
-    public void setPattern(String pattern) {
-        if (pattern == null) {
-            throw new NullPointerException("pattern must not be null");
-        }
-        this.pattern = pattern;
-        updatePatterns();
-    }
-
-    public String getRoot() {
-        return root;
-    }
-
-    public void setRoot(String root) {
-        throw new UnsupportedOperationException("Cannot configure root on mavenRepo. Use 'url' property instead.");
-    }
-
-    @Override
-    public void setM2compatible(boolean compatible) {
-        if (!compatible) {
-            throw new IllegalArgumentException("Cannot set m2compatible = false on mavenRepo.");
-        }
-    }
-
-    @Override
-    protected void resolveConfigurationArtifacts(ModuleVersionMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result, boolean localOnly) {
-        if (module.isMetaDataOnly()) {
-            if (!localOnly) {
-                Set<ComponentArtifactMetaData> artifacts = new LinkedHashSet<ComponentArtifactMetaData>(configuration.getArtifacts());
-                artifacts.addAll(findOptionalArtifacts(module, "jar", null));
-                result.resolved(artifacts);
-            }
-        } else {
-            result.resolved(configuration.getArtifacts());
-        }
-    }
-
-    protected static class TimestampedModuleSource implements ModuleSource {
-        public String getTimestampedVersion() {
-            return timestampedVersion;
-        }
-
-        private final String timestampedVersion;
-
-        public TimestampedModuleSource(String uniqueSnapshotVersion) {
-            this.timestampedVersion = uniqueSnapshotVersion;
-        }
-    }
-}
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
deleted file mode 100644
index 31f4521..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
+++ /dev/null
@@ -1,50 +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.resolver;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.api.internal.resource.ResourceException;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class MavenVersionLister implements VersionLister {
-    private final MavenMetadataLoader mavenMetadataLoader;
-
-    public MavenVersionLister(ExternalResourceRepository repository) {
-        this.mavenMetadataLoader = new MavenMetadataLoader(repository);
-    }
-
-    public VersionList getVersionList(final ModuleIdentifier module) {
-        return new DefaultVersionList() {
-            final Set<String> searched = new HashSet<String>();
-
-            public void visit(ResourcePattern resourcePattern, ModuleVersionArtifactMetaData artifact) throws ResourceException {
-                String metadataLocation = resourcePattern.toModulePath(module) + "/maven-metadata.xml";
-                if (!searched.add(metadataLocation)) {
-                    return;
-                }
-                MavenMetadata mavenMetaData = mavenMetadataLoader.load(metadataLocation);
-                for (String version : mavenMetaData.versions) {
-                    add(new ListedVersion(version, resourcePattern));
-                }
-            }
-        };
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 3bdb961..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.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.repositories.resolver;
-
-import java.net.URI;
-import java.util.List;
-
-public interface PatternBasedResolver {
-    void addArtifactLocation(URI baseUri, String pattern);
-
-    void addDescriptorLocation(URI baseUri, String pattern);
-
-    void setM2compatible(boolean b);
-
-    List<String> getIvyPatterns();
-
-    List<String> getArtifactPatterns();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.java
deleted file mode 100644
index a22e1b8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.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.api.internal.artifacts.repositories.resolver;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-
-public interface ResourcePattern {
-    /**
-     * Returns the path to the given artifact.
-     */
-    String toPath(ModuleVersionArtifactMetaData artifact);
-
-    /**
-     * Returns the pattern which can be used to search for versions of the given artifact.
-     * The returned pattern should include at least one [revision] placeholder.
-     */
-    String toVersionListPattern(ModuleVersionArtifactMetaData artifact);
-
-    /**
-     * Returns the path to the given module.
-     */
-    String toModulePath(ModuleIdentifier moduleIdentifier);
-
-    /**
-     * Returns the path to the module version for the given artifact.
-     */
-    String toModuleVersionPath(ModuleVersionArtifactMetaData artifact);
-}
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
deleted file mode 100644
index 8299605..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
+++ /dev/null
@@ -1,165 +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.resolver;
-
-import org.apache.ivy.core.IvyPatternHelper;
-import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.api.internal.resource.ResourceException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class ResourceVersionLister implements VersionLister {
-    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceVersionLister.class);
-    private static final String REVISION_TOKEN = IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY);
-    public static final int REV_TOKEN_LENGTH = REVISION_TOKEN.length();
-
-    private final ExternalResourceRepository repository;
-    private final String fileSeparator = "/";
-
-    public ResourceVersionLister(ExternalResourceRepository repository) {
-        this.repository = repository;
-    }
-
-    public VersionList getVersionList(final ModuleIdentifier module) {
-        return new DefaultVersionList() {
-            final Set<String> directories = new HashSet<String>();
-
-            public void visit(ResourcePattern resourcePattern, ModuleVersionArtifactMetaData artifact) throws ResourceException {
-                String partiallyResolvedPattern = resourcePattern.toVersionListPattern(artifact);
-                LOGGER.debug("Listing all in {}", partiallyResolvedPattern);
-                try {
-                    List<String> versionStrings = listRevisionToken(partiallyResolvedPattern);
-                    for (String versionString : versionStrings) {
-                        add(new ListedVersion(versionString, resourcePattern));
-                    }
-                } catch (ResourceException e) {
-                    throw e;
-                } catch (Exception e) {
-                    throw new ResourceException(String.format("Could not list versions using %s.", resourcePattern), e);
-                }
-            }
-
-            // lists all the values a revision token listed by a given url lister
-            private List<String> listRevisionToken(String pattern) throws IOException {
-                pattern = standardize(pattern);
-                if (!pattern.contains(REVISION_TOKEN)) {
-                    LOGGER.debug("revision token not defined in pattern {}.", pattern);
-                    return Collections.emptyList();
-                }
-                String prefix = pattern.substring(0, pattern.indexOf(REVISION_TOKEN));
-                if (revisionMatchesDirectoryName(pattern)) {
-                    return listAll(prefix);
-                } else {
-                    int parentFolderSlashIndex = prefix.lastIndexOf(fileSeparator);
-                    String revisionParentFolder = parentFolderSlashIndex == -1 ? "" : prefix.substring(0, parentFolderSlashIndex + 1);
-                    LOGGER.debug("using {} to list all in {} ", repository, revisionParentFolder);
-                    if (!directories.add(revisionParentFolder)) {
-                        return Collections.emptyList();
-                    }
-                    List<String> all = repository.list(revisionParentFolder);
-                    if (all == null) {
-                        return Collections.emptyList();
-                    }
-                    LOGGER.debug("found {} urls", all.size());
-                    Pattern regexPattern = createRegexPattern(pattern, parentFolderSlashIndex);
-                    List<String> ret = filterMatchedValues(all, regexPattern);
-                    LOGGER.debug("{} matched {}" + pattern, ret.size(), pattern);
-                    return ret;
-                }
-            }
-
-            private String standardize(String source) {
-                return source.replace('\\', '/');
-            }
-
-            private List<String> filterMatchedValues(List<String> all, final Pattern p) {
-                List<String> ret = new ArrayList<String>(all.size());
-                for (String path : all) {
-                    Matcher m = p.matcher(path);
-                    if (m.matches()) {
-                        String value = m.group(1);
-                        ret.add(value);
-                    }
-                }
-                return ret;
-            }
-
-            private Pattern createRegexPattern(String pattern, int prefixLastSlashIndex) {
-                int endNameIndex = pattern.indexOf(fileSeparator, prefixLastSlashIndex + 1);
-                String namePattern;
-                if (endNameIndex != -1) {
-                    namePattern = pattern.substring(prefixLastSlashIndex + 1, endNameIndex);
-                } else {
-                    namePattern = pattern.substring(prefixLastSlashIndex + 1);
-                }
-                namePattern = namePattern.replaceAll("\\.", "\\\\.");
-
-                String acceptNamePattern = ".*?"
-                        + namePattern.replaceAll("\\[revision\\]", "([^" + fileSeparator + "]+)")
-                        + "($|" + fileSeparator + ".*)";
-
-                return Pattern.compile(acceptNamePattern);
-            }
-
-            private boolean revisionMatchesDirectoryName(String pattern) {
-                int startToken = pattern.indexOf(REVISION_TOKEN);
-                if (startToken > 0 && !pattern.substring(startToken - 1, startToken).equals(fileSeparator)) {
-                    // previous character is not a separator
-                    return false;
-                }
-                int endToken = startToken + REV_TOKEN_LENGTH;
-                if (endToken < pattern.length() && !pattern.substring(endToken, endToken + 1).equals(fileSeparator)) {
-                    // next character is not a separator
-                    return false;
-                }
-                return true;
-            }
-
-            private List<String> listAll(String parent) throws IOException {
-                if (!directories.add(parent)) {
-                    return Collections.emptyList();
-                }
-                LOGGER.debug("using {} to list all in {}", repository, parent);
-                List<String> fullPaths = repository.list(parent);
-                if (fullPaths == null) {
-                    return Collections.emptyList();
-                }
-                LOGGER.debug("found {} resources", fullPaths.size());
-                return extractVersionInfoFromPaths(fullPaths);
-            }
-
-            private List<String> extractVersionInfoFromPaths(List<String> paths) {
-                List<String> ret = new ArrayList<String>(paths.size());
-                for (String fullpath : paths) {
-                    if (fullpath.endsWith(fileSeparator)) {
-                        fullpath = fullpath.substring(0, fullpath.length() - 1);
-                    }
-                    int slashIndex = fullpath.lastIndexOf(fileSeparator);
-                    ret.add(fullpath.substring(slashIndex + 1));
-                }
-                return ret;
-            }
-        };
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VerifyingExternalResourceDownloader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VerifyingExternalResourceDownloader.java
deleted file mode 100644
index c3dd43a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VerifyingExternalResourceDownloader.java
+++ /dev/null
@@ -1,86 +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.repositories.resolver;
-
-import org.apache.ivy.util.ChecksumHelper;
-import org.gradle.api.internal.artifacts.repositories.cachemanager.RepositoryArtifactCache;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.util.GFileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-
-class VerifyingExternalResourceDownloader implements RepositoryArtifactCache.ExternalResourceDownloader {
-    private static final Logger LOGGER = LoggerFactory.getLogger(VerifyingExternalResourceDownloader.class);
-
-    private final ExternalResourceRepository repository;
-    private final String[] checksumAlgorithms;
-
-    VerifyingExternalResourceDownloader(String[] checksumAlgorithms, ExternalResourceRepository repository) {
-        this.checksumAlgorithms = checksumAlgorithms;
-        this.repository = repository;
-    }
-
-    public void download(ExternalResource resource, File dest) throws IOException {
-        get(resource, dest);
-        verifyChecksums(resource, dest);
-    }
-
-    private void get(ExternalResource resource, File destination) throws IOException {
-        LOGGER.debug("Downloading {} to {}", resource.getName(), destination);
-        if (destination.getParentFile() != null) {
-            GFileUtils.mkdirs(destination.getParentFile());
-        }
-
-        try {
-            resource.writeTo(destination);
-        } finally {
-            resource.close();
-        }
-    }
-
-    private void verifyChecksums(ExternalResource resource, File dest) throws IOException {
-        boolean checked = false;
-        for (int i = 0; i < checksumAlgorithms.length && !checked; i++) {
-            checked = check(resource, dest, checksumAlgorithms[i]);
-        }
-    }
-
-    private boolean check(ExternalResource resource, File dest, String algorithm) throws IOException {
-        if (!ChecksumHelper.isKnownAlgorithm(algorithm)) {
-            throw new IllegalArgumentException("Unknown checksum algorithm: " + algorithm);
-        }
-
-        ExternalResource checksumResource = repository.getResource(resource.getName() + "." + algorithm);
-        if (checksumResource != null && checksumResource.exists()) {
-            LOGGER.debug(algorithm + " file found for " + resource + ": checking...");
-            File csFile = File.createTempFile("ivytmp", algorithm);
-            try {
-                get(checksumResource, csFile);
-                ChecksumHelper.check(dest, csFile, algorithm);
-                LOGGER.debug("{} OK for {}", algorithm, resource);
-                return true;
-            } finally {
-                csFile.delete();
-            }
-        } else {
-            return false;
-        }
-    }
-}
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
deleted file mode 100644
index 588c834..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionList.java
+++ /dev/null
@@ -1,74 +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.resolver;
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.resource.ResourceException;
-
-import java.util.List;
-import java.util.Set;
-
-public interface VersionList {
-    /**
-     * <p>Adds those versions available for the given pattern.</p>
-     *
-     * If no versions are listed with the given pattern, then no versions are added.
-     * 
-     * @throws ResourceException If information for versions cannot be loaded.
-     */
-    void visit(ResourcePattern pattern, ModuleVersionArtifactMetaData artifact) throws ResourceException;
-
-    Set<ListedVersion> getVersions();
-
-    boolean isEmpty();
-
-    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
deleted file mode 100644
index 966950e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
+++ /dev/null
@@ -1,26 +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.resolver;
-
-import org.gradle.api.artifacts.ModuleIdentifier;
-
-public interface VersionLister {
-    /**
-     * Creates an empty version list for the given module. Call {@link VersionList#visit(ResourcePattern, org.apache.ivy.core.module.descriptor.Artifact)} to search for versions.
-     */
-    VersionList getVersionList(ModuleIdentifier module);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListener.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListener.java
deleted file mode 100644
index 8013e05..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListener.java
+++ /dev/null
@@ -1,58 +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.transport;
-
-import org.apache.ivy.plugins.repository.Resource;
-import org.apache.ivy.plugins.repository.TransferEvent;
-import org.apache.ivy.plugins.repository.TransferListener;
-import org.gradle.api.internal.externalresource.transfer.AbstractProgressLoggingHandler;
-import org.gradle.api.internal.externalresource.transfer.ResourceOperation;
-import org.gradle.logging.ProgressLoggerFactory;
-
-public class ProgressLoggingTransferListener extends AbstractProgressLoggingHandler implements TransferListener {
-    private final Class loggingClass;
-    private ResourceOperation resourceOperation;
-
-    public ProgressLoggingTransferListener(ProgressLoggerFactory progressLoggerFactory, Class loggingClass) {
-        super(progressLoggerFactory);
-        this.loggingClass = loggingClass;
-    }
-
-    public void transferProgress(TransferEvent evt) {
-        final Resource resource = evt.getResource();
-        if (resource.isLocal()) {
-            return;
-        }
-        final int eventType = evt.getEventType();
-        if (eventType == TransferEvent.TRANSFER_STARTED) {
-            resourceOperation = createResourceOperation(resource.getName(), getRequestType(evt), loggingClass, evt.getTotalLength());
-        }
-        if (eventType == TransferEvent.TRANSFER_PROGRESS) {
-            resourceOperation.logProcessedBytes(evt.getLength());
-        }
-        if (eventType == TransferEvent.TRANSFER_COMPLETED) {
-            resourceOperation.completed();
-        }
-    }
-
-    private ResourceOperation.Type getRequestType(TransferEvent evt) {
-        if (evt.getRequestType() == TransferEvent.REQUEST_PUT) {
-            return ResourceOperation.Type.upload;
-        } else {
-            return ResourceOperation.Type.download;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java
deleted file mode 100644
index 910735c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java
+++ /dev/null
@@ -1,29 +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.transport;
-
-import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-
-import java.net.URI;
-
-public interface RepositoryTransport {
-    ExternalResourceRepository getRepository();
-
-    void configureCacheManager(ExternalResourceResolver resolver);
-
-    String convertToPath(URI uri);
-}
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
deleted file mode 100644
index 7a63dcb..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
+++ /dev/null
@@ -1,56 +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.transport;
-
-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;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.logging.ProgressLoggerFactory;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-public class RepositoryTransportFactory {
-    private final RepositoryArtifactCache downloadingCacheManager;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final CachedExternalResourceIndex<String> cachedExternalResourceIndex;
-    private final RepositoryArtifactCache localCacheManager;
-    private final ProgressLoggerFactory progressLoggerFactory;
-    private final BuildCommencedTimeProvider timeProvider;
-
-    public RepositoryTransportFactory(ProgressLoggerFactory progressLoggerFactory,
-                                      RepositoryArtifactCache localCacheManager,
-                                      RepositoryArtifactCache downloadingCacheManager,
-                                      TemporaryFileProvider temporaryFileProvider,
-                                      CachedExternalResourceIndex<String> cachedExternalResourceIndex,
-                                      BuildCommencedTimeProvider timeProvider) {
-        this.progressLoggerFactory = progressLoggerFactory;
-        this.localCacheManager = localCacheManager;
-        this.downloadingCacheManager = downloadingCacheManager;
-        this.temporaryFileProvider = temporaryFileProvider;
-        this.cachedExternalResourceIndex = cachedExternalResourceIndex;
-        this.timeProvider = timeProvider;
-    }
-
-    public RepositoryTransport createHttpTransport(String name, PasswordCredentials credentials) {
-        return new HttpTransport(name, credentials, downloadingCacheManager, progressLoggerFactory, temporaryFileProvider, cachedExternalResourceIndex, timeProvider);
-    }
-
-    public RepositoryTransport createFileTransport(String name) {
-        return new FileTransport(name, localCacheManager, temporaryFileProvider);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareArtifact.java
deleted file mode 100644
index f3e3460..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareArtifact.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-
-import java.io.File;
-
-public abstract class AbstractSoftwareArtifact implements SoftwareArtifact {
-    private final File file;
-    private final GradleException failure;
-
-    protected AbstractSoftwareArtifact(File file) {
-        this.file = file;
-        failure = null;
-    }
-
-    protected AbstractSoftwareArtifact(GradleException failure) {
-        file = null;
-        this.failure = failure;
-    }
-
-    public File getFile() throws GradleException {
-        assertNoFailure();
-        return file;
-    }
-
-    public GradleException getFailure() {
-        return failure;
-    }
-
-    private void assertNoFailure() {
-        if (failure != null) {
-            throw failure;
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareComponent.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareComponent.java
deleted file mode 100644
index 829b214..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/AbstractSoftwareComponent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.resolution.SoftwareComponent;
-
-public abstract class AbstractSoftwareComponent implements SoftwareComponent {
-    private final ComponentIdentifier componentId;
-
-    protected AbstractSoftwareComponent(ComponentIdentifier componentId) {
-        this.componentId = componentId;
-    }
-
-    public ComponentIdentifier getId() {
-        return componentId;
-    }
-
-    @Override
-    public final boolean equals(Object other) {
-        if (this == other) {
-            return true;
-        }
-
-        if (other == null || getClass() != other.getClass()) {
-            return false;
-        }
-
-        return componentId.equals(((AbstractSoftwareComponent) other).componentId);
-    }
-
-    @Override
-    public final int hashCode() {
-        return componentId.hashCode();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/ComponentMetaDataArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/ComponentMetaDataArtifact.java
deleted file mode 100644
index f9d42d9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/ComponentMetaDataArtifact.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-
-public interface ComponentMetaDataArtifact extends SoftwareArtifact {
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQuery.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQuery.java
deleted file mode 100644
index dba4a2a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQuery.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import com.google.common.collect.Sets;
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.resolution.*;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingArtifactResolver;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData;
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData;
-import org.gradle.api.internal.artifacts.metadata.DefaultDependencyMetaData;
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.internal.Factory;
-import org.gradle.internal.Transformers;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.CollectionUtils;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultArtifactResolutionQuery implements ArtifactResolutionQuery {
-    private final ConfigurationContainerInternal configurationContainer;
-    private final RepositoryHandler repositoryHandler;
-    private final ResolveIvyFactory ivyFactory;
-    private final ModuleMetadataProcessor metadataProcessor;
-    private final CacheLockingManager lockingManager;
-
-    private Set<ComponentIdentifier> componentIds = Sets.newLinkedHashSet();
-    private Class<? extends SoftwareComponent> componentType;
-    private Set<Class<? extends SoftwareArtifact>> artifactTypes = Sets.newLinkedHashSet();
-
-    public DefaultArtifactResolutionQuery(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
-                                          ResolveIvyFactory ivyFactory, ModuleMetadataProcessor metadataProcessor, CacheLockingManager lockingManager) {
-        this.configurationContainer = configurationContainer;
-        this.repositoryHandler = repositoryHandler;
-        this.ivyFactory = ivyFactory;
-        this.metadataProcessor = metadataProcessor;
-        this.lockingManager = lockingManager;
-    }
-
-    public ArtifactResolutionQuery forComponents(Iterable<? extends ComponentIdentifier> componentIds) {
-        CollectionUtils.addAll(this.componentIds, componentIds);
-        return this;
-    }
-
-    public ArtifactResolutionQuery forComponents(ComponentIdentifier... componentIds) {
-        CollectionUtils.addAll(this.componentIds, componentIds);
-        return this;
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T extends SoftwareComponent, U extends SoftwareArtifact> ArtifactResolutionQuery withArtifacts(Class<T> componentType, Class<U>... artifactTypes) {
-        this.componentType = componentType;
-        if (artifactTypes.length == 0) {
-            this.artifactTypes = (Set) Sets.newHashSet(JvmLibrarySourcesArtifact.class, JvmLibraryJavadocArtifact.class);
-        } else {
-            this.artifactTypes.addAll(Arrays.asList(artifactTypes));
-        }
-        return this;
-    }
-
-    // TODO:DAZ This is ugly and needs a major cleanup and unit tests
-    // TODO:DAZ Also need to add a 'result' layer to the api
-    public ArtifactResolutionQueryResult execute() {
-        List<ResolutionAwareRepository> repositories = CollectionUtils.collect(repositoryHandler, Transformers.cast(ResolutionAwareRepository.class));
-        ConfigurationInternal configuration = configurationContainer.detachedConfiguration();
-        final RepositoryChain repositoryChain = ivyFactory.create(configuration, repositories, metadataProcessor);
-        final ArtifactResolver artifactResolver = new ErrorHandlingArtifactResolver(repositoryChain.getArtifactResolver());
-
-        return lockingManager.useCache("resolve artifacts", new Factory<ArtifactResolutionQueryResult>() {
-            public ArtifactResolutionQueryResult create() {
-                Set<JvmLibrary> jvmLibraries = Sets.newHashSet();
-                Set<UnresolvedSoftwareComponent> unresolvedComponents = Sets.newHashSet();
-
-                for (ComponentIdentifier componentId : componentIds) {
-                    if (!(componentId instanceof ModuleComponentIdentifier)) {
-                        throw new IllegalArgumentException(String.format("Cannot resolve the artifacts for component %s with unsupported type %s.", componentId.getDisplayName(), componentId.getClass().getName()));
-                    }
-                    ModuleComponentIdentifier moduleComponentId = (ModuleComponentIdentifier) componentId;
-                    BuildableComponentResolveResult moduleResolveResult = new DefaultBuildableComponentResolveResult();
-                    repositoryChain.getDependencyResolver().resolve(new DefaultDependencyMetaData(new DefaultDependencyDescriptor(toModuleRevisionId(moduleComponentId), true)), moduleResolveResult);
-
-                    try {
-                        DefaultJvmLibrary jvmLibrary = buildJvmLibrary(moduleResolveResult.getMetaData(), artifactResolver);
-                        jvmLibraries.add(jvmLibrary);
-                    } catch (Throwable t) {
-                        unresolvedComponents.add(new DefaultUnresolvedSoftwareComponent(moduleComponentId, t));
-                    }
-                }
-
-                return new DefaultArtifactResolutionQueryResult(jvmLibraries, unresolvedComponents);
-            }
-        });
-    }
-
-    private DefaultJvmLibrary buildJvmLibrary(ComponentMetaData component, ArtifactResolver artifactResolver) {
-        Set<JvmLibraryJavadocArtifact> javadocs = Sets.newLinkedHashSet();
-        Set<JvmLibrarySourcesArtifact> sources = Sets.newLinkedHashSet();
-        for (Class<? extends SoftwareArtifact> artifactType : artifactTypes) {
-            if (artifactType == JvmLibraryJavadocArtifact.class) {
-                addJvmLibraryArtifacts(javadocs, JvmLibraryJavadocArtifact.class, DefaultJvmLibraryJavadocArtifact.class, component, artifactResolver);
-            } else if (artifactType == JvmLibrarySourcesArtifact.class) {
-                addJvmLibraryArtifacts(sources, JvmLibrarySourcesArtifact.class, DefaultJvmLibrarySourcesArtifact.class, component, artifactResolver);
-            } else {
-                throw new IllegalArgumentException(String.format("Cannot resolve artifacts with unsupported type %s.", artifactType.getName()));
-            }
-        }
-        return new DefaultJvmLibrary(component.getComponentId(), sources, javadocs);
-    }
-
-    private <T extends JvmLibraryArtifact> void addJvmLibraryArtifacts(Set<T> artifacts, Class<T> type, Class<? extends T> implType, ComponentMetaData component, ArtifactResolver artifactResolver) {
-        ArtifactResolveContext context = new ArtifactTypeResolveContext(type);
-        BuildableArtifactSetResolveResult artifactSetResolveResult = new DefaultBuildableArtifactSetResolveResult();
-        artifactResolver.resolveModuleArtifacts(component, context, artifactSetResolveResult);
-
-        Instantiator instantiator = new DirectInstantiator();
-        for (ComponentArtifactMetaData artifactMetaData : artifactSetResolveResult.getArtifacts()) {
-            BuildableArtifactResolveResult resolveResult = new DefaultBuildableArtifactResolveResult();
-            artifactResolver.resolveArtifact(artifactMetaData, component.getSource(), resolveResult);
-            if (resolveResult.getFailure() != null) {
-                artifacts.add(instantiator.newInstance(implType, resolveResult.getFailure()));
-            } else {
-                artifacts.add(instantiator.newInstance(implType, resolveResult.getFile()));
-            }
-        }
-    }
-
-    private ModuleRevisionId toModuleRevisionId(ModuleComponentIdentifier componentId) {
-        return ModuleRevisionId.newInstance(componentId.getGroup(), componentId.getModule(), componentId.getVersion());
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryFactory.java
deleted file mode 100644
index ec9844a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.resolution.ArtifactResolutionQuery;
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ArtifactResolutionQueryFactory;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
-
-public class DefaultArtifactResolutionQueryFactory implements ArtifactResolutionQueryFactory {
-    private final ConfigurationContainerInternal configurationContainer;
-    private final RepositoryHandler repositoryHandler;
-    private final ResolveIvyFactory ivyFactory;
-    private final ModuleMetadataProcessor metadataProcessor;
-    private final CacheLockingManager cacheLockingManager;
-
-    public DefaultArtifactResolutionQueryFactory(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
-                                                 ResolveIvyFactory ivyFactory, ModuleMetadataProcessor metadataProcessor,
-                                                 CacheLockingManager cacheLockingManager) {
-        this.configurationContainer = configurationContainer;
-        this.repositoryHandler = repositoryHandler;
-        this.ivyFactory = ivyFactory;
-        this.metadataProcessor = metadataProcessor;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    public ArtifactResolutionQuery createArtifactResolutionQuery() {
-        return new DefaultArtifactResolutionQuery(configurationContainer, repositoryHandler, ivyFactory, metadataProcessor, cacheLockingManager);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryResult.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryResult.java
deleted file mode 100644
index f06fd9f..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultArtifactResolutionQueryResult.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import org.gradle.api.artifacts.resolution.ArtifactResolutionQueryResult;
-import org.gradle.api.artifacts.resolution.SoftwareComponent;
-import org.gradle.api.artifacts.resolution.UnresolvedSoftwareComponent;
-
-import java.util.Set;
-
-public class DefaultArtifactResolutionQueryResult implements ArtifactResolutionQueryResult {
-    private final Set<? extends SoftwareComponent> components;
-    private final Set<UnresolvedSoftwareComponent> unresolvedComponents;
-
-    public DefaultArtifactResolutionQueryResult(Set<? extends SoftwareComponent> components, Set<UnresolvedSoftwareComponent> unresolvedComponents) {
-        this.components = components;
-        this.unresolvedComponents = unresolvedComponents;
-    }
-
-    public Set<? extends SoftwareComponent> getComponents() {
-        return components;
-    }
-
-    public <T extends SoftwareComponent> Set<T> getComponents(final Class<T> type) {
-        return Sets.newHashSet(Iterables.filter(components, type));
-    }
-
-    public Set<UnresolvedSoftwareComponent> getUnresolvedComponents() {
-        return unresolvedComponents;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrary.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrary.java
deleted file mode 100644
index c17ae53..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrary.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.resolution.JvmLibrary;
-import org.gradle.api.artifacts.resolution.JvmLibraryJavadocArtifact;
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class DefaultJvmLibrary extends AbstractSoftwareComponent implements JvmLibrary {
-    private final Set<JvmLibrarySourcesArtifact> sourceArtifacts;
-    private final Set<JvmLibraryJavadocArtifact> javadocArtifacts;
-
-    public DefaultJvmLibrary(ComponentIdentifier componentId,
-                             Set<? extends JvmLibrarySourcesArtifact> sourceArtifacts,
-                             Set<? extends JvmLibraryJavadocArtifact> javadocArtifacts) {
-        super(componentId);
-        this.sourceArtifacts = new LinkedHashSet<JvmLibrarySourcesArtifact>(sourceArtifacts);
-        this.javadocArtifacts = new LinkedHashSet<JvmLibraryJavadocArtifact>(javadocArtifacts);
-    }
-
-    public Set<JvmLibrarySourcesArtifact> getSourcesArtifacts() {
-        return sourceArtifacts;
-    }
-
-    public Set<JvmLibraryJavadocArtifact> getJavadocArtifacts() {
-        return javadocArtifacts;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibraryJavadocArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibraryJavadocArtifact.java
deleted file mode 100644
index f52b904..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibraryJavadocArtifact.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.resolution.JvmLibraryJavadocArtifact;
-
-import java.io.File;
-
-public class DefaultJvmLibraryJavadocArtifact extends AbstractSoftwareArtifact implements JvmLibraryJavadocArtifact {
-    public DefaultJvmLibraryJavadocArtifact(File file) {
-        super(file);
-    }
-
-    public DefaultJvmLibraryJavadocArtifact(GradleException failure) {
-        super(failure);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrarySourcesArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrarySourcesArtifact.java
deleted file mode 100644
index 31082c8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultJvmLibrarySourcesArtifact.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact;
-
-import java.io.File;
-
-public class DefaultJvmLibrarySourcesArtifact extends AbstractSoftwareArtifact implements JvmLibrarySourcesArtifact {
-    public DefaultJvmLibrarySourcesArtifact(File file) {
-        super(file);
-    }
-
-    public DefaultJvmLibrarySourcesArtifact(GradleException failure) {
-        super(failure);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultUnresolvedSoftwareComponent.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultUnresolvedSoftwareComponent.java
deleted file mode 100644
index 96a026b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/DefaultUnresolvedSoftwareComponent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolution;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.resolution.UnresolvedSoftwareComponent;
-
-public class DefaultUnresolvedSoftwareComponent implements UnresolvedSoftwareComponent {
-    private final ComponentIdentifier id;
-    private final Throwable failure;
-
-    public DefaultUnresolvedSoftwareComponent(ComponentIdentifier id, Throwable failure) {
-        this.id = id;
-        this.failure = failure;
-    }
-
-    public ComponentIdentifier getId() {
-        return id;
-    }
-
-    public Throwable getFailure() {
-        return failure;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/IvyDescriptorArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/IvyDescriptorArtifact.java
deleted file mode 100644
index f00b5ae..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/IvyDescriptorArtifact.java
+++ /dev/null
@@ -1,21 +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.resolution;
-
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-
-public interface IvyDescriptorArtifact extends SoftwareArtifact {
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/MavenPomArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/MavenPomArtifact.java
deleted file mode 100644
index f47e69d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/resolution/MavenPomArtifact.java
+++ /dev/null
@@ -1,22 +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.resolution;
-
-import org.gradle.api.artifacts.resolution.SoftwareArtifact;
-
-public interface MavenPomArtifact extends SoftwareArtifact {
-}
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
deleted file mode 100644
index 3374c98..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java
+++ /dev/null
@@ -1,56 +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.result;
-
-import org.gradle.api.artifacts.ModuleVersionSelector;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedComponentResult;
-import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector;
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException;
-
-public class DefaultUnresolvedDependencyResult extends AbstractDependencyResult implements UnresolvedDependencyResult {
-    private final ComponentSelectionReason reason;
-    private final ModuleVersionResolveException failure;
-
-    public DefaultUnresolvedDependencyResult(ComponentSelector requested, ComponentSelectionReason reason,
-                                             ResolvedComponentResult from, ModuleVersionResolveException failure) {
-        super(requested, from);
-        this.reason = reason;
-        this.failure = failure;
-    }
-
-    public ModuleVersionResolveException getFailure() {
-        return failure;
-    }
-
-    public ModuleComponentSelector getAttempted() {
-        ModuleVersionSelector moduleVersionSelector = failure.getSelector();
-        return DefaultModuleComponentSelector.newSelector(moduleVersionSelector.getGroup(), moduleVersionSelector.getName(), moduleVersionSelector.getVersion());
-    }
-
-    public ComponentSelectionReason getAttemptedReason() {
-        return reason;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s -> %s - %s", getRequested(), getAttempted(), failure.getMessage());
-    }
-}
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
deleted file mode 100644
index 1142f22..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/AbstractExternalResource.java
+++ /dev/null
@@ -1,68 +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.apache.commons.io.IOUtils;
-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);
-        try {
-            writeTo(output);
-        } finally {
-            output.close();
-        }
-    }
-
-    public void writeTo(OutputStream output) throws IOException {
-        InputStream input = openStream();
-        try {
-            IOUtils.copy(input, output);
-        } finally {
-            input.close();
-        }
-    }
-
-    public void withContent(Action<? super InputStream> readAction) throws IOException {
-        InputStream input = openStream();
-        try {
-            readAction.execute(input);
-        } finally {
-            input.close();
-        }
-    }
-
-    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
deleted file mode 100644
index 7cb2d2b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/DefaultLocallyAvailableExternalResource.java
+++ /dev/null
@@ -1,53 +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.externalresource;
-
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.internal.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;
-    }
-
-    @Override
-    public String toString() {
-        return locallyAvailableResource.toString();
-    }
-
-    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
deleted file mode 100644
index 1e2ce4a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ExternalResource.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.externalresource;
-
-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 {
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    ExternalResourceMetaData getMetaData();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocalFileStandInExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocalFileStandInExternalResource.java
deleted file mode 100644
index 26f60f6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocalFileStandInExternalResource.java
+++ /dev/null
@@ -1,92 +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.externalresource;
-
-import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.internal.hash.HashValue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Used when we find a file locally that matches the checksum of some external resource.
- *
- * It saves us downloading the file, but we don't get any metadata for it.
- */
-public class LocalFileStandInExternalResource extends AbstractExternalResource {
-
-    private final File localFile;
-    private final String source;
-    private HashValue sha1;
-    private ExternalResourceMetaData metaData;
-
-    public LocalFileStandInExternalResource(String source, File localFile, ExternalResourceMetaData metaData) {
-        this.source = source;
-        this.localFile = localFile;
-        this.metaData = metaData;
-    }
-
-    public String getName() {
-        return source;
-    }
-
-    public long getLastModified() {
-        return -1;
-    }
-
-    public long getContentLength() {
-        return localFile.length();
-    }
-
-    public boolean exists() {
-        return true;
-    }
-
-    public boolean isLocal() {
-        return true;
-    }
-
-    public InputStream openStream() throws IOException {
-        return new FileInputStream(localFile);
-    }
-
-    public ExternalResourceMetaData getMetaData() {
-        if (metaData == null) {
-            metaData = new DefaultExternalResourceMetaData(source, getLastModified(), getContentLength(), null, getLocalFileSha1());
-        }
-        return metaData;
-    }
-
-    protected File getLocalFile() {
-        return localFile;
-    }
-
-    protected HashValue getSha1(File contentFile) {
-        return HashUtil.createHash(contentFile, "SHA1");
-    }
-
-    protected HashValue getLocalFileSha1() {
-        if (sha1 == null) {
-            sha1 = getSha1(getLocalFile());
-        }
-        return sha1;
-    }
-}
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
deleted file mode 100644
index 0d7e582..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/LocallyAvailableExternalResource.java
+++ /dev/null
@@ -1,27 +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.externalresource;
-
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-
-/**
- * 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/MetaDataOnlyExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/MetaDataOnlyExternalResource.java
deleted file mode 100644
index 004b574..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/MetaDataOnlyExternalResource.java
+++ /dev/null
@@ -1,73 +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.externalresource;
-
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class MetaDataOnlyExternalResource extends AbstractExternalResource {
-
-    private final ExternalResourceMetaData metaData;
-    private final String source;
-    private final boolean local;
-
-    public MetaDataOnlyExternalResource(String source, ExternalResourceMetaData metaData) {
-        this(source, metaData, false);
-    }
-
-    public MetaDataOnlyExternalResource(String source, ExternalResourceMetaData metaData, boolean local) {
-        this.source = source;
-        this.metaData = metaData;
-        this.local = local;
-    }
-
-    public ExternalResourceMetaData getMetaData() {
-        return metaData;
-    }
-
-    @Override
-    public String toString() {
-        return "MetaDataOnlyResource: " + getName();
-    }
-
-    public String getName() {
-        return source;
-    }
-
-    public boolean exists() {
-        return true;
-    }
-
-    public long getLastModified() {
-        return -1;
-    }
-
-    public long getContentLength() {
-        return -1;
-    }
-
-    public boolean isLocal() {
-        return local;
-    }
-
-    public InputStream openStream() throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/MissingExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/MissingExternalResource.java
deleted file mode 100644
index d1dcfaa..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/MissingExternalResource.java
+++ /dev/null
@@ -1,68 +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.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;
-
-/**
- * Sentinel for representing that there is no resource at some source location.
- */
-public class MissingExternalResource extends AbstractExternalResource {
-    private final String source;
-    private final ExternalResourceMetaData metaData;
-
-    public MissingExternalResource(String source) {
-        this.source = source;
-        this.metaData = new DefaultExternalResourceMetaData(source);
-    }
-
-    @Override
-    public String toString() {
-        return "MissingResource: " + getName();
-    }
-
-    public String getName() {
-        return source;
-    }
-
-    public boolean exists() {
-        return false;
-    }
-
-    public long getLastModified() {
-        return -1;
-    }
-
-    public long getContentLength() {
-        return -1;
-    }
-
-    public boolean isLocal() {
-        throw new UnsupportedOperationException();
-    }
-
-    public InputStream openStream() throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    public ExternalResourceMetaData getMetaData() {
-        return this.metaData;
-    }
-}
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
deleted file mode 100644
index f47a3bf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/UrlExternalResource.java
+++ /dev/null
@@ -1,65 +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.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/cached/ByUrlCachedExternalResourceIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/ByUrlCachedExternalResourceIndex.java
deleted file mode 100644
index e2d9109..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/ByUrlCachedExternalResourceIndex.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.externalresource.cached;
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-public class ByUrlCachedExternalResourceIndex extends DefaultCachedExternalResourceIndex<String> {
-
-    public ByUrlCachedExternalResourceIndex(String persistentCacheFile, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        super(persistentCacheFile, String.class, timeProvider, cacheLockingManager);
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifact.java
deleted file mode 100644
index 4d3ec1a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifact.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.api.internal.externalresource.cached;
-
-import java.math.BigInteger;
-
-public interface CachedArtifact extends CachedItem {
-    BigInteger getDescriptorHash();
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifactIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifactIndex.java
deleted file mode 100644
index d4b8b6b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedArtifactIndex.java
+++ /dev/null
@@ -1,63 +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.externalresource.cached;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.ivy.ArtifactAtRepositoryKey;
-
-import java.io.File;
-import java.math.BigInteger;
-
-public interface CachedArtifactIndex {
-    /**
-     * Adds a resolution to the index.
-     *
-     * The incoming file is expected to be in the persistent local. This method will not move/copy the file there. <p>
-     *
-     * @param key The key to cache this resolution under in the index. Cannot be null.
-     * @param artifactFile The artifact file in the persistent file store. Cannot be null
-     * @param moduleDescriptorHash The checksum (SHA1) of the related moduledescriptor.
-     * @see #storeMissing(ArtifactAtRepositoryKey, BigInteger)
-     */
-    void store(ArtifactAtRepositoryKey key, File artifactFile, BigInteger moduleDescriptorHash);
-
-    /**
-     * Record that the artifact with the given key was missing.
-     *
-     * @param key The key to cache this resolution under in the index.
-     * @param descriptorHash The SHA1 hash of the related moduleDescriptor
-     */
-    void storeMissing(ArtifactAtRepositoryKey key, BigInteger descriptorHash);
-
-    /**
-     * Lookup a cached resolution.
-     *
-     * The {@link CachedExternalResource#getCachedFile()} is guaranteed to exist at the time that the entry is returned from this method.
-     *
-     * @param key The key to search the index for
-     * @return The cached artifact resolution if one exists, otherwise null.
-     */
-    @Nullable
-    CachedArtifact lookup(ArtifactAtRepositoryKey key);
-
-    /**
-     * Remove the entry for the given key if it exists.
-     *
-     * @param key The key of the item to remove.
-     */
-    void clear(ArtifactAtRepositoryKey key);
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResource.java
deleted file mode 100644
index 4264581..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResource.java
+++ /dev/null
@@ -1,56 +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.externalresource.cached;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-
-import java.util.Date;
-
-/**
- * A record of some kind of external resource that has been cached locally (typically into the filestore).
- *
- * Note that this is not a kind of {@link org.gradle.api.internal.externalresource.ExternalResource}. There is an adapter that can represent a cached resource as an external resource as {@link
- * CachedExternalResourceAdapter}.
- */
-public interface CachedExternalResource extends CachedItem {
-
-    /**
-     * Always the actual content length of the cached file, not the external source.
-     *
-     * @return The content length of the cached file.
-     */
-    long getContentLength();
-
-    @Nullable
-    ExternalResourceMetaData getExternalResourceMetaData();
-
-    /**
-     * Null safe shortcut for getExternalResourceMetaData().getLastModified();
-     *
-     * @return The external last modified, or null if unavailable.
-     */
-    Date getExternalLastModified();
-
-    /**
-     * Null safe shortcut for getExternalResourceMetaData().getLastModified() as a timestamp.
-     *
-     * @return The external last modified as a timestamp, or < 0 if unavailable.
-     */
-    long getExternalLastModifiedAsTimestamp();
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapter.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapter.java
deleted file mode 100644
index f4434d6..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapter.java
+++ /dev/null
@@ -1,75 +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.cached;
-
-import org.gradle.api.internal.externalresource.LocalFileStandInExternalResource;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.transfer.ExternalResourceAccessor;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Creates an ExternalResource from something that has been cached locally.
- */
-public class CachedExternalResourceAdapter extends LocalFileStandInExternalResource {
-    private final CachedExternalResource cached;
-    private final ExternalResourceAccessor accessor;
-
-    public CachedExternalResourceAdapter(String source, CachedExternalResource cached, ExternalResourceAccessor accessor) {
-        this(source, cached, accessor, null);
-    }
-
-    public CachedExternalResourceAdapter(String source, CachedExternalResource cached, ExternalResourceAccessor accessor, ExternalResourceMetaData metaData) {
-        super(source, cached.getCachedFile(), metaData);
-        this.cached = cached;
-        this.accessor = accessor;
-    }
-
-    @Override
-    public String toString() {
-        return "CachedResource: " + cached.getCachedFile() + " for " + getName();
-    }
-
-    public long getLastModified() {
-        return cached.getExternalLastModifiedAsTimestamp();
-    }
-
-    public long getContentLength() {
-        return cached.getContentLength();
-    }
-
-    public void writeTo(File destination) throws IOException {
-        try {
-            super.writeTo(destination);
-        } catch (IOException e) {
-            downloadResourceDirect(destination);
-            return;
-        }
-
-        // If the checksum of the downloaded file does not match the cached artifact, download it directly.
-        // This may be the case if the cached artifact was changed before copying
-        if (!getSha1(destination).equals(getLocalFileSha1())) {
-            downloadResourceDirect(destination);
-        }
-    }
-
-    private void downloadResourceDirect(File destination) throws IOException {
-        // Perform a regular download, without considering external caches
-        accessor.getResource(getName()).writeTo(destination);
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceIndex.java
deleted file mode 100644
index 8110de5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceIndex.java
+++ /dev/null
@@ -1,71 +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.cached;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-
-import java.io.File;
-
-/**
- * Provides an indexed view into cached artifacts and a record of resolution attempts, successful or not.
- *
- * Maintains references to the location of files in the persistent local. Does not deal with moving files into the local.
- * 
- * @param <K> The type of the key to the index
- */
-public interface CachedExternalResourceIndex<K> {
-
-    /**
-     * Adds a resolution to the index.
-     * 
-     * The incoming file is expected to be in the persistent local. This method will not move/copy the file there.
-     * <p>
-     *
-     * @param key The key to cache this resolution under in the index. Cannot be null.
-     * @param artifactFile The artifact file in the persistent file store. Cannot be null
-     * @param metaData Information about this resource at its source
-     * @see #storeMissing(Object)
-     */
-    void store(K key, File artifactFile, @Nullable ExternalResourceMetaData metaData);
-
-    /**
-     * Record that the artifact with the given key was missing.
-     *
-     * @param key The key to cache this resolution under in the index.
-     */
-    void storeMissing(K key);
-
-    /**
-     * Lookup a cached resolution.
-     *
-     * The {@link CachedExternalResource#getCachedFile()} is guaranteed
-     * to exist at the time that the entry is returned from this method.
-     *
-     * @param key The key to search the index for
-     * @return The cached artifact resolution if one exists, otherwise null.
-     */
-    @Nullable
-    CachedExternalResource lookup(K key);
-
-    /**
-     * Remove the entry for the given key if it exists.
-     *
-     * @param key The key of the item to remove.
-     */
-    void clear(K key);
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedItem.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedItem.java
deleted file mode 100644
index 740c74a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/CachedItem.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.externalresource.cached;
-
-import org.gradle.api.Nullable;
-
-import java.io.File;
-
-public interface CachedItem {
-    /**
-     * True if this cache entry represents that the resource does not exist.
-     *
-     * For a missing resource, all of the values will be null or similar “non” values.
-     *
-     * @return Whether this is a “missing” entry or not.
-     */
-    boolean isMissing();
-
-    /**
-     * The cached version of the external resource as a local file.
-     *
-     * @return The cached version of the external resource as a local file, or null if this {@link #isMissing()}.
-     */
-    @Nullable
-    File getCachedFile();
-
-    /**
-     * The timestamp of when this cache entry was created.
-     *
-     * @return The timestamp of when this cache entry was created.
-     */
-    long getCachedAt();
-
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedArtifact.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedArtifact.java
deleted file mode 100644
index b1da4ff..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedArtifact.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.externalresource.cached;
-
-import java.io.File;
-import java.io.Serializable;
-import java.math.BigInteger;
-
-public class DefaultCachedArtifact implements CachedArtifact, Serializable {
-    private final File cachedFile;
-    private final long cachedAt;
-    private final BigInteger descriptorHash;
-
-    public DefaultCachedArtifact(File cachedFile, long cachedAt, BigInteger descriptorHash) {
-        this.cachedFile = cachedFile;
-        this.cachedAt = cachedAt;
-        this.descriptorHash = descriptorHash;
-    }
-
-    public DefaultCachedArtifact(long cachedAt, BigInteger descriptorHash) {
-        this.cachedAt = cachedAt;
-        this.cachedFile = null;
-        this.descriptorHash = descriptorHash;
-    }
-
-    public boolean isMissing() {
-        return cachedFile == null;
-    }
-
-    public File getCachedFile() {
-        return cachedFile;
-    }
-
-    public long getCachedAt() {
-        return cachedAt;
-    }
-
-    public BigInteger getDescriptorHash() {
-        return descriptorHash;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResource.java
deleted file mode 100644
index b6f0cb7..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResource.java
+++ /dev/null
@@ -1,72 +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.externalresource.cached;
-
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.Date;
-
-public class DefaultCachedExternalResource implements CachedExternalResource, Serializable {
-    private final File cachedFile;
-    private final long cachedAt;
-    private final ExternalResourceMetaData externalResourceMetaData;
-
-    public DefaultCachedExternalResource(File cachedFile, long cachedAt, ExternalResourceMetaData externalResourceMetaData) {
-        this.cachedFile = cachedFile;
-        this.cachedAt = cachedAt;
-        this.externalResourceMetaData = externalResourceMetaData;
-    }
-
-    public DefaultCachedExternalResource(long cachedAt) {
-        this.cachedAt = cachedAt;
-
-        this.cachedFile = null;
-        this.externalResourceMetaData = null;
-    }
-
-    public boolean isMissing() {
-        return cachedFile == null;
-    }
-
-    public File getCachedFile() {
-        return cachedFile;
-    }
-
-    public long getCachedAt() {
-        return cachedAt;
-    }
-
-    public ExternalResourceMetaData getExternalResourceMetaData() {
-        return externalResourceMetaData;
-    }
-
-    public Date getExternalLastModified() {
-        return externalResourceMetaData != null ? externalResourceMetaData.getLastModified() : null;
-    }
-
-    public long getExternalLastModifiedAsTimestamp() {
-        Date externalLastModified = getExternalLastModified();
-        return externalLastModified == null ? -1 : externalLastModified.getTime();
-    }
-
-    public long getContentLength() {
-        return isMissing() ? -1 : cachedFile.length();
-    }
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResourceIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResourceIndex.java
deleted file mode 100644
index 7f51eea..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/cached/DefaultCachedExternalResourceIndex.java
+++ /dev/null
@@ -1,51 +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.externalresource.cached;
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.externalresource.ivy.AbstractCachedIndex;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.messaging.serialize.DefaultSerializer;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.io.File;
-import java.io.Serializable;
-
-public class DefaultCachedExternalResourceIndex<K extends Serializable> extends AbstractCachedIndex<K, CachedExternalResource> implements CachedExternalResourceIndex<K> {
-
-    private final BuildCommencedTimeProvider timeProvider;
-
-    public DefaultCachedExternalResourceIndex(String persistentCacheFile, Class<K> keyType, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        super(persistentCacheFile, new DefaultSerializer<K>(keyType.getClassLoader()), new DefaultSerializer<CachedExternalResource>(CachedExternalResource.class.getClassLoader()), cacheLockingManager);
-        this.timeProvider = timeProvider;
-    }
-
-    private DefaultCachedExternalResource createEntry(File artifactFile, ExternalResourceMetaData externalResourceMetaData) {
-        return new DefaultCachedExternalResource(artifactFile, timeProvider.getCurrentTime(), externalResourceMetaData);
-    }
-
-    public void store(final K key, final File artifactFile, ExternalResourceMetaData externalResourceMetaData) {
-        assertArtifactFileNotNull(artifactFile);
-        assertKeyNotNull(key);
-
-        storeInternal(key, createEntry(artifactFile, externalResourceMetaData));
-    }
-
-    public void storeMissing(K key) {
-        storeInternal(key, new DefaultCachedExternalResource(timeProvider.getCurrentTime()));
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/AbstractCachedIndex.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/AbstractCachedIndex.java
deleted file mode 100644
index 88bfe6e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/AbstractCachedIndex.java
+++ /dev/null
@@ -1,104 +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.externalresource.ivy;
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.externalresource.cached.CachedItem;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.internal.Factory;
-import org.gradle.messaging.serialize.Serializer;
-
-import java.io.File;
-
-abstract public class AbstractCachedIndex<K, V extends CachedItem> {
-    private final String persistentCacheFile;
-    private final Serializer<K> keySerializer;
-    private final Serializer<V> valueSerializer;
-    private final CacheLockingManager cacheLockingManager;
-
-    private PersistentIndexedCache<K, V> persistentCache;
-
-    public AbstractCachedIndex(String persistentCacheFile, Serializer<K> keySerializer, Serializer<V> valueSerializer, CacheLockingManager cacheLockingManager) {
-
-        this.persistentCacheFile = persistentCacheFile;
-        this.keySerializer = keySerializer;
-        this.valueSerializer = valueSerializer;
-        this.cacheLockingManager = cacheLockingManager;
-    }
-
-    private PersistentIndexedCache<K, V> getPersistentCache() {
-        if (persistentCache == null) {
-            persistentCache = initPersistentCache();
-        }
-        return persistentCache;
-    }
-
-    private PersistentIndexedCache<K, V> initPersistentCache() {
-        return cacheLockingManager.createCache(persistentCacheFile, keySerializer, valueSerializer);
-    }
-
-    private String operationName(String action) {
-        return String.format("%s artifact resolution cache '%s'", action, persistentCacheFile);
-    }
-
-    public V lookup(final K key) {
-        assertKeyNotNull(key);
-
-        return cacheLockingManager.useCache(operationName("lookup from"), new Factory<V>() {
-            public V create() {
-                V found = getPersistentCache().get(key);
-                if (found == null) {
-                    return null;
-                } else if (found.isMissing() || found.getCachedFile().exists()) {
-                    return found;
-                } else {
-                    clear(key);
-                    return null;
-                }
-            }
-        });
-    }
-
-    protected void storeInternal(final K key, final V entry) {
-        cacheLockingManager.useCache(operationName("store into"), new Runnable() {
-            public void run() {
-                getPersistentCache().put(key, entry);
-            }
-        });
-    }
-
-    protected void assertKeyNotNull(K key) {
-        if (key == null) {
-            throw new IllegalArgumentException("key cannot be null");
-        }
-    }
-
-    protected void assertArtifactFileNotNull(File artifactFile) {
-        if (artifactFile == null) {
-            throw new IllegalArgumentException("artifactFile cannot be null");
-        }
-    }
-
-    public void clear(final K key) {
-        assertKeyNotNull(key);
-        cacheLockingManager.useCache(operationName("clear from"), new Runnable() {
-            public void run() {
-                getPersistentCache().remove(key);
-            }
-        });
-    }
-}
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
deleted file mode 100644
index 6a225df..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
+++ /dev/null
@@ -1,99 +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.externalresource.ivy;
-
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifierSerializer;
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier;
-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.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.io.File;
-import java.math.BigInteger;
-
-public class ArtifactAtRepositoryCachedArtifactIndex extends AbstractCachedIndex<ArtifactAtRepositoryKey, CachedArtifact> implements CachedArtifactIndex {
-    private final BuildCommencedTimeProvider timeProvider;
-
-    public ArtifactAtRepositoryCachedArtifactIndex(String persistentCacheFile, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
-        super(persistentCacheFile, new ArtifactAtRepositoryKeySerializer(), new CachedArtifactSerializer(), cacheLockingManager);
-        this.timeProvider = timeProvider;
-    }
-
-    private DefaultCachedArtifact createEntry(File artifactFile, BigInteger moduleDescriptorHash) {
-        return new DefaultCachedArtifact(artifactFile, timeProvider.getCurrentTime(), moduleDescriptorHash);
-    }
-
-    public void store(final ArtifactAtRepositoryKey key, final File artifactFile, BigInteger moduleDescriptorHash) {
-        assertArtifactFileNotNull(artifactFile);
-        assertKeyNotNull(key);
-        storeInternal(key, createEntry(artifactFile, moduleDescriptorHash));
-    }
-
-    public void storeMissing(ArtifactAtRepositoryKey key, BigInteger descriptorHash) {
-        storeInternal(key, createMissingEntry(descriptorHash));
-    }
-
-    CachedArtifact createMissingEntry(BigInteger descriptorHash) {
-        return new DefaultCachedArtifact(timeProvider.getCurrentTime(), descriptorHash);
-    }
-
-    private static class ArtifactAtRepositoryKeySerializer implements Serializer<ArtifactAtRepositoryKey> {
-        private final Serializer<ModuleVersionArtifactIdentifier> artifactIdSerializer = new ModuleVersionArtifactIdentifierSerializer();
-
-        public void write(Encoder encoder, ArtifactAtRepositoryKey value) throws Exception {
-            encoder.writeString(value.getRepositoryId());
-            artifactIdSerializer.write(encoder, value.getArtifactId());
-        }
-
-        public ArtifactAtRepositoryKey read(Decoder decoder) throws Exception {
-            String repositoryId = decoder.readString();
-            ModuleVersionArtifactIdentifier artifactIdentifier = artifactIdSerializer.read(decoder);
-            return new ArtifactAtRepositoryKey(repositoryId, artifactIdentifier);
-        }
-    }
-
-    private static class CachedArtifactSerializer implements Serializer<CachedArtifact> {
-        public void write(Encoder encoder, CachedArtifact value) throws Exception {
-            encoder.writeBoolean(value.isMissing());
-            if (!value.isMissing()) {
-                encoder.writeString(value.getCachedFile().getPath());
-            }
-            encoder.writeLong(value.getCachedAt());
-            byte[] hash = value.getDescriptorHash().toByteArray();
-            encoder.writeBinary(hash);
-        }
-
-        public CachedArtifact read(Decoder decoder) throws Exception {
-            boolean isMissing = decoder.readBoolean();
-            File file;
-            if (!isMissing) {
-                file = new File(decoder.readString());
-            } else {
-                file = null;
-            }
-            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
deleted file mode 100644
index 6385f28..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryKey.java
+++ /dev/null
@@ -1,56 +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.externalresource.ivy;
-
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier;
-
-public class ArtifactAtRepositoryKey {
-    private final String repositoryId;
-    private final ModuleVersionArtifactIdentifier artifactId;
-
-    public ArtifactAtRepositoryKey(String repositoryId, ModuleVersionArtifactIdentifier artifactId) {
-        this.repositoryId = repositoryId;
-        this.artifactId = artifactId;
-    }
-
-    public ModuleVersionArtifactIdentifier getArtifactId() {
-        return artifactId;
-    }
-
-    public String getRepositoryId() {
-        return repositoryId;
-    }
-
-    @Override
-    public String toString() {
-        return repositoryId + ":" + artifactId;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || !(o instanceof ArtifactAtRepositoryKey)) {
-            return false;
-        }
-        ArtifactAtRepositoryKey other = (ArtifactAtRepositoryKey) o;
-        return repositoryId.equals(other.repositoryId) && artifactId.equals(other.artifactId);
-    }
-
-    @Override
-    public int hashCode() {
-        return repositoryId.hashCode() ^ artifactId.hashCode();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/AbstractLocallyAvailableResourceFinder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/AbstractLocallyAvailableResourceFinder.java
deleted file mode 100644
index 2d71841..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/AbstractLocallyAvailableResourceFinder.java
+++ /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.api.internal.externalresource.local;
-
-import org.gradle.api.Transformer;
-import org.gradle.internal.Factory;
-
-import java.io.File;
-import java.util.List;
-
-public class AbstractLocallyAvailableResourceFinder<C> implements LocallyAvailableResourceFinder<C> {
-
-    private final Transformer<Factory<List<File>>, C> producer;
-
-    public AbstractLocallyAvailableResourceFinder(Transformer<Factory<List<File>>, C> producer) {
-        this.producer = producer;
-    }
-
-    public LocallyAvailableResourceCandidates findCandidates(C criterion) {
-        return new LazyLocallyAvailableResourceCandidates(producer.transform(criterion));
-    }
-
-}
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
deleted file mode 100644
index 5fcdf96..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinder.java
+++ /dev/null
@@ -1,70 +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.externalresource.local;
-
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.internal.hash.HashValue;
-
-import java.util.LinkedList;
-import java.util.List;
-
-public class CompositeLocallyAvailableResourceFinder<C> implements LocallyAvailableResourceFinder<C> {
-
-    private final List<LocallyAvailableResourceFinder<C>> composites;
-
-    public CompositeLocallyAvailableResourceFinder(List<LocallyAvailableResourceFinder<C>> composites) {
-        this.composites = composites;
-    }
-
-    public LocallyAvailableResourceCandidates findCandidates(C criterion) {
-        List<LocallyAvailableResourceCandidates> allCandidates = new LinkedList<LocallyAvailableResourceCandidates>();
-        for (LocallyAvailableResourceFinder<C> finder : composites) {
-            allCandidates.add(finder.findCandidates(criterion));
-        }
-
-        return new CompositeLocallyAvailableResourceCandidates(allCandidates);
-    }
-    
-    private static class CompositeLocallyAvailableResourceCandidates implements LocallyAvailableResourceCandidates {
-        private final List<LocallyAvailableResourceCandidates> allCandidates;
-
-        public CompositeLocallyAvailableResourceCandidates(List<LocallyAvailableResourceCandidates> allCandidates) {
-            this.allCandidates = allCandidates;
-        }
-
-        public boolean isNone() {
-            for (LocallyAvailableResourceCandidates candidates : allCandidates) {
-                if (!candidates.isNone()) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        public LocallyAvailableResource findByHashValue(HashValue hashValue) {
-            for (LocallyAvailableResourceCandidates candidates : allCandidates) {
-                LocallyAvailableResource match = candidates.findByHashValue(hashValue);
-                if (match != null) {
-                    return match;
-                }
-            }
-
-            return null;
-        }
-    }
-}
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
deleted file mode 100644
index c7cece9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidates.java
+++ /dev/null
@@ -1,60 +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.externalresource.local;
-
-import org.gradle.internal.Factory;
-import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.internal.hash.HashValue;
-
-import java.io.File;
-import java.util.List;
-
-public class LazyLocallyAvailableResourceCandidates implements LocallyAvailableResourceCandidates {
-
-    private final Factory<List<File>> filesFactory;
-    private List<File> files;
-
-    public LazyLocallyAvailableResourceCandidates(Factory<List<File>> filesFactory) {
-        this.filesFactory = filesFactory;        
-    }
-
-    protected List<File> getFiles() {
-        if (files == null) {
-            files = filesFactory.create();
-        }
-        return files;
-    }
-    
-    public boolean isNone() {
-        return getFiles().isEmpty();
-    }
-
-    public LocallyAvailableResource findByHashValue(HashValue targetHash) {
-        HashValue thisHash;
-        for (File file : getFiles()) {
-            thisHash = HashUtil.sha1(file);
-            if (thisHash.equals(targetHash)) {
-                return new DefaultLocallyAvailableResource(file, thisHash);
-            }
-        }
-
-        return null;
-    }
-
-}
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
deleted file mode 100644
index 3687182..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceCandidates.java
+++ /dev/null
@@ -1,31 +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.externalresource.local;
-
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.internal.hash.HashValue;
-
-/**
- * A set of locally available resources that were “selected” through some means.
- */
-public interface LocallyAvailableResourceCandidates {
-
-    boolean isNone();
-
-    LocallyAvailableResource findByHashValue(HashValue 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
deleted file mode 100644
index 5c84337..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinder.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.api.internal.externalresource.local;
-
-/**
- * Can find a locally available candidates for an external resource, through some means.
- *
- * 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).
- *
- * @param <C> The type of the criterion object used to find candidates
- */
-public interface LocallyAvailableResourceFinder<C> {
-
-    LocallyAvailableResourceCandidates findCandidates(C criterion);
-
-}
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
deleted file mode 100644
index 6047525..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java
+++ /dev/null
@@ -1,54 +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.externalresource.local;
-
-import org.gradle.api.Transformer;
-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;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Makes a LocallyAvailableResourceFinder out of a FileStoreSearcher.
- * @param <C> The type of criterion the filestore can be searched for, and therefore locally available resources searched for.
- */
-public class LocallyAvailableResourceFinderSearchableFileStoreAdapter<C> extends AbstractLocallyAvailableResourceFinder<C> {
-
-    public LocallyAvailableResourceFinderSearchableFileStoreAdapter(final FileStoreSearcher<C> fileStore) {
-        super(new Transformer<Factory<List<File>>, C>() {
-            public Factory<List<File>> transform(final C criterion) {
-                return new Factory<List<File>>() {
-                    public List<File> create() {
-                        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
deleted file mode 100644
index 26cdfca..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/LocallyAvailableResourceFinderFactory.java
+++ /dev/null
@@ -1,158 +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.ivy;
-
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.mvnsettings.CannotLocateLocalMavenRepositoryException;
-import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
-import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePattern;
-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.CompositeLocallyAvailableResourceFinder;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinderSearchableFileStoreAdapter;
-import org.gradle.internal.Factory;
-import org.gradle.internal.filestore.FileStoreSearcher;
-import org.gradle.internal.hash.HashValue;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-
-public class LocallyAvailableResourceFinderFactory implements Factory<LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>> {
-    private static final Logger LOGGER = LoggerFactory.getLogger(LocallyAvailableResourceFinderFactory.class);
-
-    private final File rootCachesDirectory;
-    private final LocalMavenRepositoryLocator localMavenRepositoryLocator;
-    private final FileStoreSearcher<ModuleVersionArtifactMetaData> fileStore;
-
-    public LocallyAvailableResourceFinderFactory(
-            ArtifactCacheMetaData artifactCacheMetaData, LocalMavenRepositoryLocator localMavenRepositoryLocator, FileStoreSearcher<ModuleVersionArtifactMetaData> fileStore) {
-        this.rootCachesDirectory = artifactCacheMetaData.getCacheDir().getParentFile();
-        this.localMavenRepositoryLocator = localMavenRepositoryLocator;
-        this.fileStore = fileStore;
-    }
-
-    public LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> create() {
-        List<LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>> finders = new LinkedList<LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>>();
-
-        // Order is important here, because they will be searched in that order
-
-        // The current filestore
-        finders.add(new LocallyAvailableResourceFinderSearchableFileStoreAdapter<ModuleVersionArtifactMetaData>(fileStore));
-
-        // 1.9
-//        addForPattern(finders, "modules-2/files-2.1/[organisation]/[module](/[branch])/[revision]/*/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // 1.8
-        addForPattern(finders, "artifacts-26/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // 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])");
-
-        // 1.1, 1.2
-        addForPattern(finders, "artifacts-14/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // rc-1, 1.0
-        addForPattern(finders, "artifacts-13/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // Milestone 8 and 9
-        addForPattern(finders, "artifacts-8/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // Milestone 7
-        addForPattern(finders, "artifacts-7/artifacts/*/[organisation]/[module](/[branch])/[revision]/[type]/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // Milestone 6
-        addForPattern(finders, "artifacts-4/[organisation]/[module](/[branch])/*/[type]s/[artifact]-[revision](-[classifier])(.[ext])");
-        addForPattern(finders, "artifacts-4/[organisation]/[module](/[branch])/*/pom.originals/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // Milestone 3
-        addForPattern(finders, "../cache/[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])");
-
-        // Maven local
-        try {
-            File localMavenRepository = localMavenRepositoryLocator.getLocalMavenRepository();
-            if (localMavenRepository.exists()) {
-                addForPattern(finders, localMavenRepository, new M2ResourcePattern("[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"));
-            }
-        } catch (CannotLocateLocalMavenRepositoryException ex) {
-            finders.add(new NoMavenLocalRepositoryResourceFinder(ex));
-        }
-        return new CompositeLocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>(finders);
-    }
-
-    private void addForPattern(List<LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>> finders, String pattern) {
-        int wildcardPos = pattern.indexOf("/*/");
-        int patternPos = pattern.indexOf("/[");
-        if (wildcardPos < 0 && patternPos < 0) {
-            throw new IllegalArgumentException(String.format("Unsupported pattern '%s'", pattern));
-        }
-        int chopAt;
-        if (wildcardPos >= 0 && patternPos >= 0) {
-            chopAt = Math.min(wildcardPos, patternPos);
-        } else if (wildcardPos >= 0) {
-            chopAt = wildcardPos;
-        } else {
-            chopAt = patternPos;
-        }
-        String pathPart = pattern.substring(0, chopAt);
-        String patternPart = pattern.substring(chopAt + 1);
-        addForPattern(finders, new File(rootCachesDirectory, pathPart), new IvyResourcePattern(patternPart));
-    }
-
-    private void addForPattern(List<LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData>> finders, File baseDir, ResourcePattern pattern) {
-        if (baseDir.exists()) {
-            finders.add(new PatternBasedLocallyAvailableResourceFinder(baseDir, pattern));
-        }
-    }
-
-    private class NoMavenLocalRepositoryResourceFinder implements LocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> {
-        private final CannotLocateLocalMavenRepositoryException ex;
-        private boolean logged;
-
-        public NoMavenLocalRepositoryResourceFinder(CannotLocateLocalMavenRepositoryException ex) {
-            this.ex = ex;
-        }
-
-        public LocallyAvailableResourceCandidates findCandidates(ModuleVersionArtifactMetaData criterion) {
-            if(!logged){
-                LOGGER.warn("Unable to locate local Maven repository.");
-                LOGGER.debug("Problems while locating local Maven repository.", ex);
-                logged = true;
-            }
-            return new LocallyAvailableResourceCandidates() {
-                public boolean isNone() {
-                    return true;
-                }
-
-                public LocallyAvailableResource findByHashValue(HashValue hashValue) {
-                    return null;
-                }
-            };
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/PatternBasedLocallyAvailableResourceFinder.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/PatternBasedLocallyAvailableResourceFinder.java
deleted file mode 100644
index 7601cf5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/local/ivy/PatternBasedLocallyAvailableResourceFinder.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.externalresource.local.ivy;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.file.EmptyFileVisitor;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
-import org.gradle.api.internal.externalresource.local.AbstractLocallyAvailableResourceFinder;
-import org.gradle.api.internal.file.collections.MinimalFileTree;
-import org.gradle.api.internal.file.collections.SingleIncludePatternFileTree;
-import org.gradle.internal.Factory;
-
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-
-public class PatternBasedLocallyAvailableResourceFinder extends AbstractLocallyAvailableResourceFinder<ModuleVersionArtifactMetaData> {
-
-    public PatternBasedLocallyAvailableResourceFinder(File baseDir, ResourcePattern pattern) {
-        super(createProducer(baseDir, pattern));
-    }
-
-    private static Transformer<Factory<List<File>>, ModuleVersionArtifactMetaData> createProducer(final File baseDir, final ResourcePattern pattern) {
-        return new Transformer<Factory<List<File>>, ModuleVersionArtifactMetaData>() {
-            public Factory<List<File>> transform(final ModuleVersionArtifactMetaData artifact) {
-                return new Factory<List<File>>() {
-                    public List<File> create() {
-                        final List<File> files = new LinkedList<File>();
-                        if (artifact != null) {
-                            getMatchingFiles(artifact).visit(new EmptyFileVisitor() {
-                                public void visitFile(FileVisitDetails fileDetails) {
-                                    files.add(fileDetails.getFile());
-                                }
-                            });
-                        }
-                        return files;
-                    }
-                };
-            }
-
-            private MinimalFileTree getMatchingFiles(ModuleVersionArtifactMetaData artifact) {
-                String patternString = pattern.toPath(artifact);
-                return new SingleIncludePatternFileTree(baseDir, patternString);
-            }
-
-        };
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaData.java
deleted file mode 100644
index 16505e9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaData.java
+++ /dev/null
@@ -1,70 +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.externalresource.metadata;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.hash.HashValue;
-
-import java.io.Serializable;
-import java.util.Date;
-
-public class DefaultExternalResourceMetaData implements ExternalResourceMetaData, Serializable {
-
-    private final String location;
-    private final Date lastModified;
-    private final long contentLength;
-    private final String etag;
-    private final String sha1;
-
-    public DefaultExternalResourceMetaData(String location) {
-        this(location, -1, -1, null, null);
-    }
-
-    public DefaultExternalResourceMetaData(String location, long lastModified, long contentLength, @Nullable String etag, @Nullable HashValue sha1) {
-        this(location, lastModified > 0 ? new Date(lastModified) : null, contentLength, etag, sha1);
-    }
-    
-    public DefaultExternalResourceMetaData(String location, @Nullable Date lastModified, long contentLength, @Nullable String etag, @Nullable HashValue sha1) {
-        this.location = location;
-        this.lastModified = lastModified;
-        this.contentLength = contentLength;
-        this.etag = etag;
-        this.sha1 = sha1 == null ? null : sha1.asHexString();
-    }
-
-    public String getLocation() {
-        return location;
-    }
-
-    @Nullable
-    public Date getLastModified() {
-        return lastModified;
-    }
-
-    public long getContentLength() {
-        return contentLength;
-    }
-
-    @Nullable
-    public String getEtag() {
-        return etag;
-    }
-
-    public HashValue getSha1() {
-        return sha1 == null ? null : HashValue.parse(sha1);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaData.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaData.java
deleted file mode 100644
index e49d7fd..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaData.java
+++ /dev/null
@@ -1,54 +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.externalresource.metadata;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.hash.HashValue;
-
-import java.util.Date;
-
-public interface ExternalResourceMetaData {
-
-    String getLocation();
-
-    @Nullable
-    Date getLastModified();
-
-    long getContentLength();
-
-    /**
-     * Some kind of opaque checksum that was advertised by the remote “server”.
-     * 
-     * For HTTP this is likely the value of the ETag header but it may be any kind of opaque checksum.
-     * 
-     * @return The entity tag, or null if there was no advertised or suitable etag.
-     */
-    @Nullable
-    String getEtag();
-
-    /**
-     * The advertised sha-1 of the external resource.
-     *
-     * This should only be collected if it is very cheap to do so. For example, some HTTP servers send an
-     * “X-Checksum-Sha1” that makes the sha1 available cheaply. In this case it makes sense to advertise this as metadata here.
-     *
-     * @return The sha1, or null if it's unknown.
-     */
-    @Nullable
-    HashValue getSha1();
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompare.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompare.java
deleted file mode 100644
index 10921a3..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompare.java
+++ /dev/null
@@ -1,66 +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.externalresource.metadata;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.Factory;
-
-import java.util.Date;
-
-public abstract class ExternalResourceMetaDataCompare {
-    public static boolean isDefinitelyUnchanged(@Nullable ExternalResourceMetaData local, Factory<ExternalResourceMetaData> remoteFactory) {
-        if (local == null) {
-            return false;
-        }
-
-        String localEtag = local.getEtag();
-
-        Date localLastModified = local.getLastModified();
-        if (localEtag == null && localLastModified == null) {
-            return false;
-        }
-
-        long localContentLength = local.getContentLength();
-        if (localEtag == null && localContentLength < 1) {
-            return false;
-        }
-
-        // We have enough local data to make a comparison, get the remote metadata
-        ExternalResourceMetaData remote = remoteFactory.create();
-        if (remote == null) {
-            return false;
-        }
-
-        String remoteEtag = remote.getEtag();
-        if (localEtag != null && remoteEtag != null) {
-            return localEtag.equals(remoteEtag);
-        }
-
-        Date remoteLastModified = remote.getLastModified();
-        if (remoteLastModified == null) {
-            return false;
-        }
-
-        long remoteContentLength = remote.getContentLength();
-        //noinspection SimplifiableIfStatement
-        if (remoteContentLength < 1) {
-            return false;
-        }
-
-        return localContentLength == remoteContentLength && remoteLastModified.equals(localLastModified);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/AbstractProgressLoggingHandler.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/AbstractProgressLoggingHandler.java
deleted file mode 100644
index 3f80387..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/AbstractProgressLoggingHandler.java
+++ /dev/null
@@ -1,41 +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.externalresource.transfer;
-
-import org.gradle.logging.ProgressLogger;
-import org.gradle.logging.ProgressLoggerFactory;
-
-public class AbstractProgressLoggingHandler {
-    protected final ProgressLoggerFactory progressLoggerFactory;
-
-    public AbstractProgressLoggingHandler(ProgressLoggerFactory progressLoggerFactory) {
-        this.progressLoggerFactory = progressLoggerFactory;
-    }
-
-    protected ResourceOperation createResourceOperation(String resourceName, ResourceOperation.Type operationType, Class loggingClazz, long contentLength) {
-        ProgressLogger progressLogger = startProgress(String.format("%s %s", operationType.getCapitalized(), resourceName), loggingClazz);
-        return new ResourceOperation(progressLogger, operationType, contentLength);
-    }
-
-    private ProgressLogger startProgress(String description, Class loggingClass) {
-        ProgressLogger progressLogger = progressLoggerFactory.newOperation(loggingClass != null ? loggingClass : getClass());
-        progressLogger.setDescription(description);
-        progressLogger.setLoggingHeader(description);
-        progressLogger.started();
-        return progressLogger;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/CacheAwareExternalResourceAccessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/CacheAwareExternalResourceAccessor.java
deleted file mode 100644
index 64b86a9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/CacheAwareExternalResourceAccessor.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.externalresource.transfer;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
-
-import java.io.IOException;
-
-public interface CacheAwareExternalResourceAccessor {
-
-    ExternalResource getResource(String source, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException;
-
-}
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
deleted file mode 100644
index e24fd2a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessor.java
+++ /dev/null
@@ -1,118 +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.externalresource.transfer;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultExternalResourceCachePolicy;
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.ExternalResourceCachePolicy;
-import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource;
-import org.gradle.api.internal.externalresource.ExternalResource;
-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.LocallyAvailableResourceCandidates;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaDataCompare;
-import org.gradle.internal.Factory;
-import org.gradle.internal.hash.HashValue;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.util.BuildCommencedTimeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-public class DefaultCacheAwareExternalResourceAccessor implements CacheAwareExternalResourceAccessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCacheAwareExternalResourceAccessor.class);
-
-    private final ExternalResourceAccessor delegate;
-    private final CachedExternalResourceIndex<String> cachedExternalResourceIndex;
-    private final BuildCommencedTimeProvider timeProvider;
-    private final ExternalResourceCachePolicy externalResourceCachePolicy = new DefaultExternalResourceCachePolicy();
-
-    public DefaultCacheAwareExternalResourceAccessor(ExternalResourceAccessor delegate, CachedExternalResourceIndex<String> cachedExternalResourceIndex, BuildCommencedTimeProvider timeProvider) {
-        this.delegate = delegate;
-        this.cachedExternalResourceIndex = cachedExternalResourceIndex;
-        this.timeProvider = timeProvider;
-    }
-
-    public ExternalResource getResource(final String location, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException {
-        LOGGER.debug("Constructing external resource: {}", location);
-        CachedExternalResource cached = cachedExternalResourceIndex.lookup(location);
-
-        // If we have no caching options, just get the thing directly
-        if (cached == null && (localCandidates == null || localCandidates.isNone())) {
-            return delegate.getResource(location);
-        }
-
-        // We might be able to use a cached/locally available version
-        if (cached != null && !externalResourceCachePolicy.mustRefreshExternalResource(getAgeMillis(timeProvider, cached))) {
-            return new CachedExternalResourceAdapter(location, cached, delegate, cached.getExternalResourceMetaData());
-        }
-
-        // Get the metadata first to see if it's there
-        final ExternalResourceMetaData remoteMetaData = delegate.getMetaData(location);
-        if (remoteMetaData == null) {
-            return null;
-        }
-
-        // Is the cached version still current?
-        if (cached != null) {
-            boolean isUnchanged = ExternalResourceMetaDataCompare.isDefinitelyUnchanged(
-                    cached.getExternalResourceMetaData(),
-                    new Factory<ExternalResourceMetaData>() {
-                        public ExternalResourceMetaData create() {
-                            return remoteMetaData;
-                        }
-                    }
-            );
-
-            if (isUnchanged) {
-                LOGGER.info("Cached resource is up-to-date (lastModified: {}). [HTTP: {}]", cached.getExternalLastModified(), location);
-                // TODO - should we use the remote metadata? It may be “better”
-                return new CachedExternalResourceAdapter(location, cached, delegate, remoteMetaData);
-            }
-        }
-
-        // Either no cached, or it's changed. See if we can find something local with the same checksum
-        boolean hasLocalCandidates = localCandidates != null && !localCandidates.isNone();
-        if (hasLocalCandidates) {
-            // The “remote” may have already given us the checksum
-            HashValue remoteChecksum = remoteMetaData.getSha1();
-
-            if (remoteChecksum == null) {
-                remoteChecksum = delegate.getResourceSha1(location);
-            }
-
-            if (remoteChecksum != null) {
-                LocallyAvailableResource local = localCandidates.findByHashValue(remoteChecksum);
-                if (local != null) {
-                    LOGGER.info("Found locally available resource with matching checksum: [{}, {}]", location, local.getFile());
-                    return new DefaultLocallyAvailableExternalResource(location, local, remoteMetaData);
-                }
-            }
-        }
-
-        // All local/cached options failed, get directly
-        return delegate.getResource(location);
-    }
-
-    public long getAgeMillis(BuildCommencedTimeProvider timeProvider, CachedExternalResource cached) {
-        return timeProvider.getCurrentTime() - cached.getCachedAt();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceAccessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceAccessor.java
deleted file mode 100644
index e735e43..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceAccessor.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.api.internal.externalresource.transfer;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.internal.hash.HashValue;
-
-import java.io.IOException;
-
-public interface ExternalResourceAccessor {
-
-    /**
-     * Obtain the resource at the given location.
-     *
-     * If the resource does not exist, this method should return null.
-     *
-     * If the resource may exist but can't be accessed due to some configuration issue, the implementation
-     * may either return null or throw an {@link IOException} to indicate a fatal condition.
-     *
-     * @param location The address of the resource to obtain
-     * @return The resource if it exists, otherwise null
-     * @throws IOException If the resource may exist, but not could be obtained for some reason
-     */
-    @Nullable
-    ExternalResource getResource(String location) throws IOException;
-
-    /**
-     * Obtain the SHA-1 checksum for the resource at the given location.
-     *
-     * Implementation is optional. If it is not feasible to obtain this without reading the
-     * entire resource, implementations should return null.
-     *
-     * @param location The address of the resource to obtain the sha-1 of
-     * @return The sha-1 if it can be cheaply obtained, otherwise null.
-     */
-    @Nullable
-    HashValue getResourceSha1(String location);
-
-    /**
-     * Obtains only the metadata about the resource.
-     *
-     * If it is determined that the resource does not exist, this method should return null.
-     *
-     * If it is not possible to determine whether the resource exists or not, this method should
-     * return a metadata instance with null/non value values (e.g. -1 for content length) to indicate
-     * that the resource may indeed exist, but the metadata for it cannot be obtained.
-     *
-     * If the resource may exist but can't be accessed due to some configuration issue, the implementation
-     * may either return an empty metadata object or throw an {@link IOException} to indicate a fatal condition.
-     *
-     * @param location The location of the resource to obtain the metadata for
-     * @return The available metadata if possible, an “empty” metadata object if the
-     *         metadata can't be reliably be obtained, null if the resource doesn't exist
-     * @throws IOException If the resource may exist, but not could be obtained for some reason
-     */
-    @Nullable
-    ExternalResourceMetaData getMetaData(String location) throws IOException;
-    
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceLister.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceLister.java
deleted file mode 100644
index 7c177c5..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceLister.java
+++ /dev/null
@@ -1,26 +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.externalresource.transfer;
-
-import java.io.IOException;
-import java.util.List;
-
-public interface ExternalResourceLister {
-
-    public List<String> list(String parent) throws IOException;
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceUploader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceUploader.java
deleted file mode 100644
index e0c9f7d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ExternalResourceUploader.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.api.internal.externalresource.transfer;
-
-import org.gradle.internal.Factory;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public interface ExternalResourceUploader {
-
-    void upload(Factory<InputStream> sourceFactory, Long contentLength, String destination) throws IOException;
-}
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
deleted file mode 100644
index df39de9..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessor.java
+++ /dev/null
@@ -1,157 +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.externalresource.transfer;
-
-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;
-import org.gradle.internal.hash.HashValue;
-
-import java.io.*;
-
-public class ProgressLoggingExternalResourceAccessor extends AbstractProgressLoggingHandler implements ExternalResourceAccessor {
-    private final ExternalResourceAccessor delegate;
-
-    public ProgressLoggingExternalResourceAccessor(ExternalResourceAccessor delegate, ProgressLoggerFactory progressLoggerFactory) {
-        super(progressLoggerFactory);
-        this.delegate = delegate;
-    }
-
-    public ExternalResource getResource(String location) throws IOException {
-        ExternalResource resource = delegate.getResource(location);
-        if (resource != null) {
-            return new ProgressLoggingExternalResource(resource);
-        } else {
-            return null;
-        }
-    }
-
-    @Nullable
-    public HashValue getResourceSha1(String location) {
-        return delegate.getResourceSha1(location);
-    }
-
-    @Nullable
-    public ExternalResourceMetaData getMetaData(String location) throws IOException {
-        return delegate.getMetaData(location);
-    }
-
-    private class ProgressLoggingExternalResource implements ExternalResource {
-        private ExternalResource resource;
-
-        private ProgressLoggingExternalResource(ExternalResource resource) {
-            this.resource = resource;
-        }
-
-        /**
-         * This redirect allows us to deprecate ExternalResource#writeto and replace usages later.
-         */
-        public void writeTo(File destination) throws IOException {
-            FileOutputStream output = new FileOutputStream(destination);
-            try {
-                writeTo(output);
-            } finally {
-                output.close();
-            }
-        }
-
-        public void writeTo(OutputStream outputStream) throws IOException {
-            final ResourceOperation downloadOperation = createResourceOperation(resource.getName(), ResourceOperation.Type.download, getClass(), resource.getContentLength());
-            final ProgressLoggingOutputStream progressLoggingOutputStream = new ProgressLoggingOutputStream(outputStream, downloadOperation);
-            try {
-                resource.writeTo(progressLoggingOutputStream);
-            } finally {
-                downloadOperation.completed();
-            }
-        }
-
-        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 {
-            resource.close();
-        }
-
-        @Nullable
-        public ExternalResourceMetaData getMetaData() {
-            return resource.getMetaData();
-        }
-
-        public String getName() {
-            return resource.getName();
-        }
-
-        public long getLastModified() {
-            return resource.getLastModified();
-        }
-
-        public long getContentLength() {
-            return resource.getContentLength();
-        }
-
-        public boolean exists() {
-            return resource.exists();
-        }
-
-        public boolean isLocal() {
-            return resource.isLocal();
-        }
-
-        public String toString(){
-            return resource.toString();
-        }
-    }
-
-    private class ProgressLoggingOutputStream extends OutputStream {
-        private OutputStream outputStream;
-        private final ResourceOperation resourceOperation;
-
-        public ProgressLoggingOutputStream(OutputStream outputStream, ResourceOperation resourceOperation) {
-            this.outputStream = outputStream;
-            this.resourceOperation = resourceOperation;
-        }
-
-        @Override
-        public void flush() throws IOException {
-            outputStream.flush();
-        }
-
-        @Override
-        public void close() throws IOException {
-            outputStream.close();
-        }
-
-        @Override
-        public void write(int b) throws IOException {
-            outputStream.write(b);
-            resourceOperation.logProcessedBytes(1l);
-        }
-
-        public void write(byte b[], int off, int len) throws IOException {
-            outputStream.write(b, off, len);
-            resourceOperation.logProcessedBytes(len);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploader.java
deleted file mode 100644
index 947a65e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploader.java
+++ /dev/null
@@ -1,81 +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.externalresource.transfer;
-
-import org.gradle.internal.Factory;
-import org.gradle.logging.ProgressLoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class ProgressLoggingExternalResourceUploader extends AbstractProgressLoggingHandler implements ExternalResourceUploader {
-    private final ExternalResourceUploader delegate;
-
-    public ProgressLoggingExternalResourceUploader(ExternalResourceUploader delegate, ProgressLoggerFactory progressLoggerFactory) {
-        super(progressLoggerFactory);
-        this.delegate = delegate;
-    }
-    public void upload(final Factory<InputStream> source, final Long contentLength, String destination) throws IOException {
-        final ResourceOperation uploadOperation = createResourceOperation(destination, ResourceOperation.Type.upload, getClass(), contentLength);
-
-        try {
-            delegate.upload(new Factory<InputStream>() {
-                public InputStream create() {
-                    return new ProgressLoggingInputStream(source.create(), uploadOperation);
-                }
-            }, contentLength, destination);
-        } finally {
-            uploadOperation.completed();
-        }
-    }
-
-    private class ProgressLoggingInputStream extends InputStream {
-        private InputStream inputStream;
-        private final ResourceOperation resourceOperation;
-
-        public ProgressLoggingInputStream(InputStream inputStream, ResourceOperation resourceOperation) {
-            this.inputStream = inputStream;
-            this.resourceOperation = resourceOperation;
-        }
-
-        @Override
-        public void close() throws IOException {
-            inputStream.close();
-        }
-
-        @Override
-        public int read() throws IOException {
-            int result = inputStream.read();
-            if (result >= 0) {
-                doLogProgress(1);
-            }
-            return result;
-        }
-
-        public int read(byte[] b, int off, int len) throws IOException {
-            int read = inputStream.read(b, off, len);
-            if (read > 0) {
-                doLogProgress(read);
-            }
-            return read;
-        }
-
-        private void doLogProgress(long numberOfBytes) {
-            resourceOperation.logProcessedBytes(numberOfBytes);
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperation.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperation.java
deleted file mode 100644
index b1a8102..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperation.java
+++ /dev/null
@@ -1,70 +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.externalresource.transfer;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.logging.ProgressLogger;
-
-public class ResourceOperation {
-    public enum Type{
-        download,
-        upload;
-
-        public String getCapitalized() {
-            return StringUtils.capitalize(toString());
-        }
-    }
-    private final ProgressLogger progressLogger;
-    private final Type operationType;
-    private final String contentLengthString;
-
-    private long loggedKBytes;
-    private long totalProcessedBytes;
-
-    public ResourceOperation(ProgressLogger progressLogger, Type type, long contentLength) {
-        this.progressLogger = progressLogger;
-        this.operationType = type;
-        this.contentLengthString = getLengthText(contentLength != 0 ? contentLength : null);
-    }
-
-    private String getLengthText(Long bytes) {
-        if (bytes == null) {
-            return "unknown size";
-        }
-        if (bytes < 1024) {
-            return bytes + " B";
-        } else if (bytes < 1048576) {
-            return (bytes / 1024) + " KB";
-        } else {
-            return String.format("%.2f MB", bytes / 1048576.0);
-        }
-    }
-
-    public void logProcessedBytes(long processedBytes) {
-        totalProcessedBytes += processedBytes;
-        long processedKB = totalProcessedBytes / 1024;
-        if (processedKB > loggedKBytes) {
-            loggedKBytes = processedKB;
-            final String progressMessage = String.format("%s/%s %sed", getLengthText(totalProcessedBytes), contentLengthString, operationType);
-            progressLogger.progress(progressMessage);
-        }
-    }
-
-    public void completed() {
-        this.progressLogger.completed();
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/DefaultExternalResourceRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/DefaultExternalResourceRepository.java
deleted file mode 100644
index ea94d03..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/DefaultExternalResourceRepository.java
+++ /dev/null
@@ -1,125 +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.externalresource.transport;
-
-
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.transfer.CacheAwareExternalResourceAccessor;
-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.api.internal.file.TemporaryFileProvider;
-import org.gradle.internal.Factory;
-import org.gradle.internal.UncheckedException;
-import org.gradle.util.GFileUtils;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.internal.hash.HashValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.util.List;
-
-public class DefaultExternalResourceRepository implements ExternalResourceRepository {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExternalResourceRepository.class);
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final String name;
-    private final ExternalResourceAccessor accessor;
-    private final ExternalResourceUploader uploader;
-    private final ExternalResourceLister lister;
-
-    private final CacheAwareExternalResourceAccessor cacheAwareAccessor;
-
-    public DefaultExternalResourceRepository(String name, ExternalResourceAccessor accessor, ExternalResourceUploader uploader,
-                                             ExternalResourceLister lister, TemporaryFileProvider temporaryFileProvider,
-                                             CacheAwareExternalResourceAccessor cacheAwareAccessor) {
-        this.name = name;
-        this.accessor = accessor;
-        this.uploader = uploader;
-        this.lister = lister;
-        this.temporaryFileProvider = temporaryFileProvider;
-        this.cacheAwareAccessor = cacheAwareAccessor;
-    }
-
-    public ExternalResource getResource(String source) throws IOException {
-        return accessor.getResource(source);
-    }
-
-    public ExternalResource getResource(String source, LocallyAvailableResourceCandidates localCandidates) throws IOException {
-        return cacheAwareAccessor.getResource(source, localCandidates);
-    }
-
-    public ExternalResourceMetaData getResourceMetaData(String source) throws IOException {
-        return accessor.getMetaData(source);
-    }
-
-    public void put(File source, String destination) throws IOException {
-        doPut(source, destination);
-        putChecksum("SHA1", 40, source, destination);
-    }
-
-    private void putChecksum(String algorithm, int checksumlength, File source, String destination) throws IOException {
-        File checksumFile = createChecksumFile(source, algorithm, checksumlength);
-        String checksumDestination = destination + "." + algorithm.toLowerCase();
-        doPut(checksumFile, checksumDestination);
-    }
-
-    private File createChecksumFile(File src, String algorithm, int checksumlength) {
-        File csFile = temporaryFileProvider.createTemporaryFile("gradle_upload", algorithm);
-        HashValue hash = HashUtil.createHash(src, algorithm);
-        String formattedHashString = formatHashString(hash.asHexString(), checksumlength);
-        GFileUtils.writeFile(formattedHashString, csFile, "US-ASCII");
-        return csFile;
-    }
-
-    private String formatHashString(String hashKey, int length) {
-        while (hashKey.length() < length) {
-            hashKey = "0" + hashKey;
-        }
-        return hashKey;
-    }
-
-    protected void doPut(final File source, String destination) throws IOException {
-        LOGGER.debug("Attempting to put resource {}.", destination);
-        assert source.isFile();
-        try {
-            uploader.upload(new Factory<InputStream>() {
-                public InputStream create() {
-                    try {
-                        return new FileInputStream(source);
-                    } catch (FileNotFoundException e) {
-                        throw UncheckedException.throwAsUncheckedException(e);
-                    }
-                }
-            }, source.length(), destination);
-        } catch (IOException e) {
-            throw e;
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public List<String> list(String parent) throws IOException {
-        return lister.list(parent);
-    }
-
-    public String toString() {
-        return name;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/ExternalResourceRepository.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/ExternalResourceRepository.java
deleted file mode 100644
index 227ccb8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/ExternalResourceRepository.java
+++ /dev/null
@@ -1,70 +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.externalresource.transport;
-
-import org.gradle.api.Nullable;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-public interface ExternalResourceRepository {
-
-    /**
-     * Attempts to fetch the given resource.
-     *
-     * @return null if the resource is not found.
-     */
-    ExternalResource getResource(String source) throws IOException;
-
-    /**
-     * Attempts to fetch the given resource.
-     *
-     * @return null if the resource is not found.
-     */
-    ExternalResource getResource(String source, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException;
-
-    /**
-     * Transfer a resource to the repository
-     *
-     * @param source The local file to be transferred.
-     * @param destination Where to transfer the resource.
-     * @throws IOException On publication failure.
-     */
-    void put(File source, String destination) throws IOException;
-
-    /**
-     * Fetches only the metadata for the result.
-     *
-     * @param source The location of the resource to obtain the metadata for
-     * @return The resource metadata, or null if the resource does not exist
-     */
-    @Nullable
-    ExternalResourceMetaData getResourceMetaData(String source) throws IOException;
-
-    /**
-     * Return a listing of resources names
-     *
-     * @param parent The parent directory from which to generate the listing.
-     * @return A listing of the parent directory's file content, as a List of String.
-     * @throws IOException On listing failure.
-     */
-    List<String> list(String parent) throws IOException;
-}
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
deleted file mode 100644
index 4ed60f8..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileResourceConnector.java
+++ /dev/null
@@ -1,99 +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.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.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.internal.hash.HashValue;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-public class FileResourceConnector implements ExternalResourceLister, ExternalResourceAccessor, ExternalResourceUploader {
-    public List<String> list(String parent) throws IOException {
-        File dir = getFile(parent);
-        if (dir.exists() && dir.isDirectory()) {
-            String[] names = dir.list();
-            if (names != null) {
-                List<String> ret = new ArrayList<String>(names.length);
-                for (String name : names) {
-                    ret.add(parent + '/' + name);
-                }
-                return ret;
-            }
-        }
-        return null;
-    }
-
-    public void upload(Factory<InputStream> source, Long contentLength, String destination) throws IOException {
-        File target = getFile(destination);
-        if (!target.canWrite()) {
-            target.delete();
-        } // if target is writable, the copy will overwrite it without requiring a delete
-        GFileUtils.mkdirs(target.getParentFile());
-        FileOutputStream fileOutputStream = new FileOutputStream(target);
-        try {
-            InputStream sourceInputStream = source.create();
-            try {
-                if (IOUtils.copy(sourceInputStream, fileOutputStream) == -1) {
-                    throw new IOException(String.format("File copy failed from %s to %s", source, target));
-                }
-            } finally {
-                sourceInputStream.close();
-            }
-        } finally {
-            fileOutputStream.close();
-        }
-    }
-
-    public ExternalResource getResource(String location) throws IOException {
-        File localFile = getFile(location);
-        if (!localFile.exists()) {
-            return null;
-        }
-        return new DefaultLocallyAvailableExternalResource(location, new DefaultLocallyAvailableResource(localFile));
-    }
-
-    public ExternalResourceMetaData getMetaData(String location) throws IOException {
-        ExternalResource resource = getResource(location);
-        return resource == null ? null : resource.getMetaData();
-    }
-
-    public HashValue getResourceSha1(String location) {
-        // TODO Read sha1 from published .sha1 file
-        return null;
-    }
-
-    private static File getFile(String absolutePath) {
-        File f = new File(absolutePath);
-        if (!f.isAbsolute()) {
-            throw new IllegalArgumentException("Filename must be absolute: " + absolutePath);
-        }
-        return f;
-    }
-}
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
deleted file mode 100644
index 240e7d4..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/file/FileTransport.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.externalresource.transport.file;
-
-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;
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates;
-import org.gradle.api.internal.externalresource.transfer.CacheAwareExternalResourceAccessor;
-import org.gradle.api.internal.externalresource.transport.DefaultExternalResourceRepository;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-
-public class FileTransport implements RepositoryTransport {
-    private final String name;
-    private final RepositoryArtifactCache repositoryCacheManager;
-    private final ExternalResourceRepository repository;
-
-    public FileTransport(String name, RepositoryArtifactCache repositoryCacheManager, TemporaryFileProvider temporaryFileProvider) {
-        this.name = name;
-        this.repositoryCacheManager = repositoryCacheManager;
-        repository = createRepository(temporaryFileProvider);
-    }
-
-    public ExternalResourceRepository getRepository() {
-        return repository;
-    }
-
-    public ExternalResourceRepository createRepository(TemporaryFileProvider temporaryFileProvider) {
-        FileResourceConnector connector = new FileResourceConnector();
-        return new DefaultExternalResourceRepository(name, connector, connector, connector, temporaryFileProvider, new NoOpCacheAwareExternalResourceAccessor(connector));
-    }
-
-    public void configureCacheManager(ExternalResourceResolver resolver) {
-        resolver.setRepositoryCacheManager(repositoryCacheManager);
-    }
-
-    public String convertToPath(URI uri) {
-        return normalisePath(new File(uri).getAbsolutePath());
-    }
-
-    private String normalisePath(String path) {
-        if (path.endsWith("/")) {
-            return path;
-        }
-        return path + "/";
-    }
-
-    private static class NoOpCacheAwareExternalResourceAccessor implements CacheAwareExternalResourceAccessor {
-        private final FileResourceConnector connector;
-
-        public NoOpCacheAwareExternalResourceAccessor(FileResourceConnector connector) {
-            this.connector = connector;
-        }
-
-        public ExternalResource getResource(String source, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException {
-            return connector.getResource(source);
-        }
-    }
-}
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
deleted file mode 100644
index 437d14e..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParser.java
+++ /dev/null
@@ -1,131 +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.externalresource.transport.http;
-
-import org.cyberneko.html.parsers.SAXParser;
-import org.gradle.api.internal.resource.ResourceException;
-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.helpers.DefaultHandler;
-
-import java.io.*;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ApacheDirectoryListingParser {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(ApacheDirectoryListingParser.class);
-
-    public ApacheDirectoryListingParser() {
-    }
-
-    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 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);
-        htmlParser.parse(inputSource);
-
-        List<String> hrefs = anchorListerHandler.getHrefs();
-        List<URI> uris = resolveURIs(baseURI, hrefs);
-        return filterNonDirectChilds(baseURI, uris);
-    }
-
-    private URI addTrailingSlashes(URI uri) throws IOException, URISyntaxException {
-        if(uri.getPath() == null){
-            uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), "/", uri.getQuery(), uri.getFragment());
-        }else if (!uri.getPath().endsWith("/") && !uri.getPath().endsWith(".html")) {
-            uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath() + "/", uri.getQuery(), uri.getFragment());
-
-        }
-        return uri;
-    }
-
-    private List<URI> filterNonDirectChilds(URI baseURI, List<URI> inputURIs) throws MalformedURLException {
-        final int baseURIPort = baseURI.getPort();
-        final String baseURIHost = baseURI.getHost();
-        final String baseURIScheme = baseURI.getScheme();
-
-        List<URI> uris = new ArrayList<URI>();
-        final String prefixPath = baseURI.getPath();
-        for (URI parsedURI : inputURIs) {
-            if (parsedURI.getHost() != null && !parsedURI.getHost().equals(baseURIHost)) {
-                continue;
-            }
-            if (parsedURI.getScheme() != null && !parsedURI.getScheme().equals(baseURIScheme)) {
-                continue;
-            }
-            if (parsedURI.getPort() != baseURIPort) {
-                continue;
-            }
-            if (parsedURI.getPath() != null && !parsedURI.getPath().startsWith(prefixPath)) {
-                continue;
-            }
-            String childPathPart = parsedURI.getPath().substring(prefixPath.length(), parsedURI.getPath().length());
-            if (childPathPart.startsWith("../")) {
-                continue;
-            }
-            if (childPathPart.equals("") || childPathPart.split("/").length > 1) {
-                continue;
-            }
-
-            uris.add(parsedURI);
-        }
-        return uris;
-    }
-
-    private List<URI> resolveURIs(URI baseURI, List<String> hrefs) {
-        List<URI> uris = new ArrayList<URI>();
-        for (String href : hrefs) {
-            try {
-                uris.add(baseURI.resolve(href));
-            } catch (IllegalArgumentException ex) {
-                LOGGER.debug(String.format("Cannot resolve anchor: %s", href));
-            }
-        }
-        return uris;
-    }
-
-    private class AnchorListerHandler extends DefaultHandler {
-        List<String> hrefs = new ArrayList<String>();
-
-        public List<String> getHrefs() {
-            return hrefs;
-        }
-
-        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
-            if (qName.equalsIgnoreCase("A")) {
-                final String href = atts.getValue("href");
-                if (href != null) {
-                    hrefs.add(href);
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/DefaultHttpSettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/DefaultHttpSettings.java
deleted file mode 100644
index 1ab3458..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/DefaultHttpSettings.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.externalresource.transport.http;
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-
-public class DefaultHttpSettings implements HttpSettings {
-    private final PasswordCredentials passwordCredentials;
-    private final HttpProxySettings proxySettings = new JavaSystemPropertiesHttpProxySettings();
-
-    public DefaultHttpSettings(PasswordCredentials passwordCredentials) {
-        this.passwordCredentials = passwordCredentials;
-    }
-
-    public PasswordCredentials getCredentials() {
-        return passwordCredentials;
-    }
-
-    public HttpProxySettings getProxySettings() {
-        return proxySettings;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurer.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurer.java
deleted file mode 100644
index 5acf28d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurer.java
+++ /dev/null
@@ -1,128 +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.transport.http;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
-import org.apache.http.auth.*;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.HttpRequestRetryHandler;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.client.params.AuthPolicy;
-import org.apache.http.client.protocol.ClientContext;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.protocol.HttpContext;
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.api.internal.externalresource.transport.http.ntlm.NTLMCredentials;
-import org.gradle.api.internal.externalresource.transport.http.ntlm.NTLMSchemeFactory;
-import org.gradle.api.internal.resource.UriResource;
-import org.gradle.util.GUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-public class HttpClientConfigurer {
-    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfigurer.class);
-
-    private final HttpSettings httpSettings;
-
-    public HttpClientConfigurer(HttpSettings httpSettings) {
-        this.httpSettings = httpSettings;
-    }
-
-    public void configure(DefaultHttpClient httpClient) {
-        NTLMSchemeFactory.register(httpClient);
-        configureCredentials(httpClient, httpSettings.getCredentials());
-        configureProxyCredentials(httpClient, httpSettings.getProxySettings());
-        configureRetryHandler(httpClient);
-        configureUserAgent(httpClient);
-    }
-
-    private void configureCredentials(DefaultHttpClient httpClient, PasswordCredentials credentials) {
-        if (GUtil.isTrue(credentials.getUsername())) {
-            useCredentials(httpClient, credentials, AuthScope.ANY_HOST, AuthScope.ANY_PORT);
-
-            // Use preemptive authorisation if no other authorisation has been established
-            httpClient.addRequestInterceptor(new PreemptiveAuth(new BasicScheme()), 0);
-        }
-    }
-
-    private void configureProxyCredentials(DefaultHttpClient httpClient, HttpProxySettings proxySettings) {
-        HttpProxySettings.HttpProxy proxy = proxySettings.getProxy();
-        if (proxy != null && proxy.credentials != null) {
-            useCredentials(httpClient, proxy.credentials, proxy.host, proxy.port);
-        }
-    }
-
-    private void useCredentials(DefaultHttpClient httpClient, PasswordCredentials credentials, String host, int port) {
-        Credentials basicCredentials = new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword());
-        httpClient.getCredentialsProvider().setCredentials(new AuthScope(host, port), basicCredentials);
-
-        NTLMCredentials ntlmCredentials = new NTLMCredentials(credentials);
-        Credentials ntCredentials = new NTCredentials(ntlmCredentials.getUsername(), ntlmCredentials.getPassword(), ntlmCredentials.getWorkstation(), ntlmCredentials.getDomain());
-        httpClient.getCredentialsProvider().setCredentials(new AuthScope(host, port, AuthScope.ANY_REALM, AuthPolicy.NTLM), ntCredentials);
-
-        LOGGER.debug("Using {} and {} for authenticating against '{}:{}'", new Object[]{credentials, ntlmCredentials, host, port});
-    }
-
-    private void configureRetryHandler(DefaultHttpClient httpClient) {
-        httpClient.setHttpRequestRetryHandler(new HttpRequestRetryHandler() {
-            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
-                return false;
-            }
-        });
-    }
-
-    public void configureUserAgent(DefaultHttpClient httpClient) {
-        HttpProtocolParams.setUserAgent(httpClient.getParams(), UriResource.getUserAgentString());
-    }
-
-    static class PreemptiveAuth implements HttpRequestInterceptor {
-        private final AuthScheme authScheme;
-
-        PreemptiveAuth(AuthScheme authScheme) {
-            this.authScheme = authScheme;
-        }
-
-        public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
-
-            AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
-
-            if (authState.getAuthScheme() != null || authState.hasAuthOptions()) {
-                return;
-            }
-
-            // If no authState has been established and this is a PUT or POST request, add preemptive authorisation
-            String requestMethod = request.getRequestLine().getMethod();
-            if (requestMethod.equals(HttpPut.METHOD_NAME) || requestMethod.equals(HttpPost.METHOD_NAME)) {
-                CredentialsProvider credentialsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
-                HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
-                Credentials credentials = credentialsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
-                if (credentials == null) {
-                    throw new HttpException("No credentials for preemptive authentication");
-                }
-                authState.update(authScheme, credentials);
-            }
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelper.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelper.java
deleted file mode 100644
index b482f77..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelper.java
+++ /dev/null
@@ -1,129 +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.externalresource.transport.http;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpHead;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.impl.client.*;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.util.EntityUtils;
-import org.gradle.api.UncheckedIOException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-
-/**
- * Provides some convenience and unified logging.
- */
-public class HttpClientHelper {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientHelper.class);
-    private final HttpClient client;
-    private final BasicHttpContext httpContext = new BasicHttpContext();
-
-    public HttpClientHelper(HttpSettings settings) {
-        alwaysUseKeepAliveConnections();
-
-        DefaultHttpClient client = new SystemDefaultHttpClient();
-        new HttpClientConfigurer(settings).configure(client);
-        this.client = new DecompressingHttpClient(client);
-    }
-
-    private void alwaysUseKeepAliveConnections() {
-        // HttpClient 4.2.2 does not use the correct default value for "http.keepAlive" system property (default is "true").
-        // HttpClient NTLM authentication fails badly when this property value is true.
-        // So we force it to be true here: effectively, we're ignoring any user-supplied value for our HttpClient configuration.
-        System.setProperty("http.keepAlive", "true");
-    }
-
-    public HttpResponse performRawHead(String source) {
-        return performRequest(new HttpHead(source));        
-    }
-    
-    public HttpResponse performHead(String source) {
-        return processResponse(source, "HEAD", performRawHead(source));
-    }
-
-    public HttpResponse performRawGet(String source) {
-        return performRequest(new HttpGet(source));
-    }
-
-    public HttpResponse performGet(String source) {
-        return processResponse(source, "GET", performRawGet(source));
-    }
-
-    public HttpResponse performRequest(HttpRequestBase request) {
-        String method = request.getMethod();
-
-        HttpResponse response;
-        try {
-            response = executeGetOrHead(request);
-        } catch (IOException e) {
-            throw new HttpRequestException(String.format("Could not %s '%s'.", method, request.getURI()), e);
-        }
-
-        return response;
-    }
-
-    protected HttpResponse executeGetOrHead(HttpRequestBase method) throws IOException {
-        HttpResponse httpResponse = performHttpRequest(method);
-        // Consume content for non-successful, responses. This avoids the connection being left open.
-        if (!wasSuccessful(httpResponse)) {
-            EntityUtils.consume(httpResponse.getEntity());
-            return httpResponse;
-        }
-        return httpResponse;
-    }
-
-    public boolean wasMissing(HttpResponse response) {
-        int statusCode = response.getStatusLine().getStatusCode();
-        return statusCode == 404;
-    }
-
-    public boolean wasSuccessful(HttpResponse response) {
-        int statusCode = response.getStatusLine().getStatusCode();
-        return statusCode >= 200 && statusCode < 300;
-    }
-
-    public HttpResponse performHttpRequest(HttpRequestBase request) throws IOException {
-        // Without this, HTTP Client prohibits multiple redirects to the same location within the same context
-        httpContext.removeAttribute(DefaultRedirectStrategy.REDIRECT_LOCATIONS);
-
-        LOGGER.debug("Performing HTTP {}: {}", request.getMethod(), request.getURI());
-        return client.execute(request, httpContext);
-    }
-
-    private HttpResponse processResponse(String source, String method, HttpResponse response) {
-        if (wasMissing(response)) {
-            LOGGER.info("Resource missing. [HTTP {}: {}]", method, source);
-            return null;
-        }
-        if (!wasSuccessful(response)) {
-            LOGGER.info("Failed to get resource: {}. [HTTP {}: {}]", new Object[]{method, response.getStatusLine(), source});
-            throw new UncheckedIOException(String.format("Could not %s '%s'. Received status code %s from server: %s",
-                    method, source, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
-        }
-
-        return response;
-    }
-
-
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpProxySettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpProxySettings.java
deleted file mode 100644
index 25b6aaf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpProxySettings.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.externalresource.transport.http;
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.api.internal.artifacts.repositories.DefaultPasswordCredentials;
-
-public interface HttpProxySettings {
-    
-    HttpProxy getProxy();
-    
-    HttpProxy getProxy(String host);
-
-    public class HttpProxy {
-        public final String host;
-        public final int port;
-        public final PasswordCredentials credentials;
-
-        public HttpProxy(String host, int port, String username, String password) {
-            this.host = host;
-            this.port = port;
-            if (username == null || username.length() == 0) {
-                credentials = null;
-            } else {
-                credentials = new DefaultPasswordCredentials(username, password);
-            }
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpRequestException.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpRequestException.java
deleted file mode 100644
index 78c2581..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpRequestException.java
+++ /dev/null
@@ -1,31 +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.externalresource.transport.http;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.internal.exceptions.Contextual;
-
-/**
- * Signals that some error occurred when making an HTTP request.
- * This is different from a HTTP request returning an HTTP error code.
- */
- at Contextual
-public class HttpRequestException extends UncheckedIOException {
-    public HttpRequestException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceAccessor.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceAccessor.java
deleted file mode 100644
index 87cce8a..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceAccessor.java
+++ /dev/null
@@ -1,108 +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.externalresource.transport.http;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.util.EntityUtils;
-import org.gradle.api.internal.externalresource.ExternalResource;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.transfer.ExternalResourceAccessor;
-import org.gradle.internal.hash.HashValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class HttpResourceAccessor implements ExternalResourceAccessor {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(HttpResourceAccessor.class);
-    private final HttpClientHelper http;
-
-    private final List<ExternalResource> openResources = new ArrayList<ExternalResource>();
-
-    public HttpResourceAccessor(HttpClientHelper http) {
-        this.http = http;
-    }
-
-    public HttpResponseResource getResource(String location) throws IOException {
-        abortOpenResources();
-        LOGGER.debug("Constructing external resource: {}", location);
-        HttpResponse response = http.performGet(location);
-        if (response != null) {
-            HttpResponseResource resource = new HttpResponseResource("GET", location, response) {
-                @Override
-                public void close() throws IOException {
-                    super.close();
-                    HttpResourceAccessor.this.openResources.remove(this);
-                }
-            };
-
-            return recordOpenGetResource(resource);
-        } else {
-            return null;
-        }
-    }
-
-    public ExternalResourceMetaData getMetaData(String location) {
-        abortOpenResources();
-        LOGGER.debug("Constructing external resource metadata: {}", location);
-        HttpResponse response = http.performHead(location);
-        return response == null ? null : new HttpResponseResource("HEAD", location, response).getMetaData();
-    }
-
-    private HttpResponseResource recordOpenGetResource(HttpResponseResource httpResource) {
-        openResources.add(httpResource);
-        return httpResource;
-    }
-
-    private void abortOpenResources() {
-        for (ExternalResource openResource : openResources) {
-            LOGGER.warn("Forcing close on abandoned resource: " + openResource);
-            try {
-                openResource.close();
-            } catch (IOException e) {
-                LOGGER.warn("Failed to close abandoned resource", e);
-            }
-        }
-        openResources.clear();
-    }
-
-    public HashValue getResourceSha1(String location) {
-        String checksumUrl = location + ".sha1";
-        return downloadSha1(checksumUrl);
-    }
-
-    private HashValue downloadSha1(String checksumUrl) {
-        try {
-            HttpResponse httpResponse = http.performRawGet(checksumUrl);
-            if (http.wasSuccessful(httpResponse)) {
-                String checksumValue = EntityUtils.toString(httpResponse.getEntity());
-                return HashValue.parse(checksumValue);
-            }
-            if (!http.wasMissing(httpResponse)) {
-                LOGGER.info("Request for checksum at {} failed: {}", checksumUrl, httpResponse.getStatusLine());
-            }
-            return null;
-        } catch (Exception e) {
-            LOGGER.warn("Checksum missing at {} due to: {}", checksumUrl, e.getMessage());
-            return null;
-        }
-    }
-
-}
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
deleted file mode 100644
index ad5179d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceLister.java
+++ /dev/null
@@ -1,73 +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.externalresource.transport.http;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.externalresource.transfer.ExternalResourceLister;
-import org.gradle.api.internal.resource.ResourceException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class HttpResourceLister implements ExternalResourceLister {
-    private HttpResourceAccessor accessor;
-
-    public HttpResourceLister(HttpResourceAccessor accessor) {
-        this.accessor = accessor;
-    }
-
-    public List<String> list(String parent) throws IOException {
-        final URI baseURI;
-        try {
-            baseURI = new URI(parent);
-        } catch (URISyntaxException ex) {
-            throw new ResourceException(String.format("Unable to create URI from string '%s' ", parent), ex);
-        }
-        final HttpResponseResource resource = accessor.getResource(baseURI.toString());
-        if (resource == null) {
-            return null;
-        }
-        try {
-            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();
-        }
-    }
-
-    private List<String> convertToStringList(List<URI> uris) {
-        List<String> ret = new ArrayList<String>(uris.size());
-        for (URI url : uris) {
-            ret.add(url.toString());
-        }
-        return ret;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceUploader.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceUploader.java
deleted file mode 100644
index 60ce022..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceUploader.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.externalresource.transport.http;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.ContentType;
-import org.apache.http.util.EntityUtils;
-import org.gradle.api.internal.externalresource.transfer.ExternalResourceUploader;
-import org.gradle.internal.Factory;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class HttpResourceUploader implements ExternalResourceUploader {
-
-    private final HttpClientHelper http;
-
-    public HttpResourceUploader(HttpClientHelper http) {
-        this.http = http;
-    }
-
-    public void upload(Factory<InputStream> source, Long contentLength, String destination) throws IOException {
-        HttpPut method = new HttpPut(destination);
-        final RepeatableInputStreamEntity entity = new RepeatableInputStreamEntity(source, contentLength, ContentType.APPLICATION_OCTET_STREAM);
-        method.setEntity(entity);
-        HttpResponse response = http.performHttpRequest(method);
-        EntityUtils.consume(response.getEntity());
-        if (!http.wasSuccessful(response)) {
-            throw new IOException(String.format("Could not PUT '%s'. Received status code %s from server: %s",
-                    destination, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
-        }
-
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResource.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResource.java
deleted file mode 100644
index 342016c..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResource.java
+++ /dev/null
@@ -1,141 +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.transport.http;
-
-import org.apache.http.Header;
-import org.apache.http.HttpHeaders;
-import org.apache.http.HttpResponse;
-import org.apache.http.impl.cookie.DateUtils;
-import org.apache.http.util.EntityUtils;
-import org.gradle.api.internal.externalresource.AbstractExternalResource;
-import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData;
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData;
-import org.gradle.internal.hash.HashValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-class HttpResponseResource extends AbstractExternalResource {
-    private static final Logger LOGGER = LoggerFactory.getLogger(HttpResponseResource.class);
-
-    private final String method;
-    private final String source;
-    private final HttpResponse response;
-    private final ExternalResourceMetaData metaData;
-    private boolean wasOpened;
-
-    public HttpResponseResource(String method, String source, HttpResponse response) {
-        this.method = method;
-        this.source = source;
-        this.response = response;
-
-        String etag = getEtag(response);
-        this.metaData = new DefaultExternalResourceMetaData(source, getLastModified(), getContentLength(), etag, getSha1(response, etag));
-    }
-
-    public String getName() {
-        return source;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Http %s Resource: %s", method, source);
-    }
-
-    public ExternalResourceMetaData getMetaData() {
-        return metaData;
-    }
-
-    public long getLastModified() {
-        Header responseHeader = response.getFirstHeader("last-modified");
-        if (responseHeader == null) {
-            return 0;
-        }
-        try {
-            return DateUtils.parseDate(responseHeader.getValue()).getTime();
-        } catch (Exception e) {
-            return 0;
-        }
-    }
-
-    public long getContentLength() {
-        Header header = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
-        if (header == null) {
-            return -1;            
-        }
-
-        String value = header.getValue();
-        if (value == null) {
-            return -1;
-        }
-
-        try {
-            return Long.parseLong(value);
-        } catch (NumberFormatException e) {
-            return -1;
-        }
-    }
-
-    public String getContentType() {
-        final Header header = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
-        return header == null ? null : header.getValue();
-    }
-
-    public boolean exists() {
-        return true;
-    }
-
-    public boolean isLocal() {
-        return false;
-    }
-
-    public InputStream openStream() throws IOException {
-        if(wasOpened){
-            throw new IOException("Unable to open Stream as it was opened before.");
-        }
-        LOGGER.debug("Attempting to download resource {}.", source);
-        this.wasOpened = true;
-        return response.getEntity().getContent();
-    }
-
-    @Override
-    public void close() throws IOException {
-        EntityUtils.consume(response.getEntity());
-    }
-
-    private static String getEtag(HttpResponse response) {
-        Header etagHeader = response.getFirstHeader(HttpHeaders.ETAG);
-        return etagHeader == null ? null : etagHeader.getValue();
-    }
-    
-    private static HashValue getSha1(HttpResponse response, String etag) {
-        Header sha1Header = response.getFirstHeader("X-Checksum-Sha1");
-        if (sha1Header != null) {
-            return new HashValue(sha1Header.getValue());    
-        }
-
-        // Nexus uses sha1 etags, with a constant prefix
-        // e.g {SHA1{b8ad5573a5e9eba7d48ed77a48ad098e3ec2590b}}
-        if (etag != null && etag.startsWith("{SHA1{")) {
-            String hash = etag.substring(6, etag.length() - 2);
-            return new HashValue(hash);
-        }
-
-        return null;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpSettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpSettings.java
deleted file mode 100644
index a51dbdf..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpSettings.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.externalresource.transport.http;
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-
-public interface HttpSettings {
-    PasswordCredentials getCredentials();
-
-    HttpProxySettings getProxySettings();
-}
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
deleted file mode 100644
index 7696c25..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/HttpTransport.java
+++ /dev/null
@@ -1,81 +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.transport.http;
-
-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;
-import org.gradle.api.internal.externalresource.transfer.DefaultCacheAwareExternalResourceAccessor;
-import org.gradle.api.internal.externalresource.transfer.ProgressLoggingExternalResourceAccessor;
-import org.gradle.api.internal.externalresource.transfer.ProgressLoggingExternalResourceUploader;
-import org.gradle.api.internal.externalresource.transport.DefaultExternalResourceRepository;
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.logging.ProgressLoggerFactory;
-import org.gradle.util.BuildCommencedTimeProvider;
-
-import java.net.URI;
-
-public class HttpTransport implements RepositoryTransport {
-    private final String name;
-    private final RepositoryArtifactCache repositoryCacheManager;
-    private final ExternalResourceRepository repository;
-
-    public HttpTransport(String name, PasswordCredentials credentials, RepositoryArtifactCache repositoryCacheManager,
-                         ProgressLoggerFactory progressLoggerFactory, TemporaryFileProvider temporaryFileProvider,
-                         CachedExternalResourceIndex<String> cachedExternalResourceIndex, BuildCommencedTimeProvider timeProvider) {
-        this.name = name;
-        this.repositoryCacheManager = repositoryCacheManager;
-        repository = createRepository(credentials, progressLoggerFactory, temporaryFileProvider, cachedExternalResourceIndex, timeProvider);
-    }
-
-    public ExternalResourceRepository getRepository() {
-        return repository;
-    }
-
-    private ExternalResourceRepository createRepository(PasswordCredentials credentials, ProgressLoggerFactory progressLoggerFactory,
-                                                        TemporaryFileProvider temporaryFileProvider, CachedExternalResourceIndex<String> cachedExternalResourceIndex, BuildCommencedTimeProvider timeProvider) {
-        HttpClientHelper http = new HttpClientHelper(new DefaultHttpSettings(credentials));
-        HttpResourceAccessor accessor = new HttpResourceAccessor(http);
-        HttpResourceUploader uploader = new HttpResourceUploader(http);
-        ProgressLoggingExternalResourceAccessor loggingAccessor = new ProgressLoggingExternalResourceAccessor(accessor, progressLoggerFactory);
-        return new DefaultExternalResourceRepository(
-                name,
-                accessor,
-                new ProgressLoggingExternalResourceUploader(uploader, progressLoggerFactory),
-                new HttpResourceLister(accessor),
-                temporaryFileProvider,
-                new DefaultCacheAwareExternalResourceAccessor(loggingAccessor, cachedExternalResourceIndex, timeProvider)
-        );
-    }
-
-    public void configureCacheManager(ExternalResourceResolver resolver) {
-        resolver.setRepositoryCacheManager(repositoryCacheManager);
-    }
-
-    public String convertToPath(URI uri) {
-        return normalisePath(uri.toString());
-    }
-
-    private String normalisePath(String path) {
-        if (path.endsWith("/")) {
-            return path;
-        }
-        return path + "/";
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettings.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettings.java
deleted file mode 100644
index 038f6ff..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettings.java
+++ /dev/null
@@ -1,104 +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.transport.http;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Pattern;
-
-public class JavaSystemPropertiesHttpProxySettings implements HttpProxySettings {
-    private static final Logger LOGGER = LoggerFactory.getLogger(JavaSystemPropertiesHttpProxySettings.class);
-    private static final int DEFAULT_PROXY_PORT = 80;
-
-    private final HttpProxy proxy;
-    private final List<Pattern> nonProxyHosts;
-
-    public JavaSystemPropertiesHttpProxySettings() {
-        this(System.getProperty("http.proxyHost"), System.getProperty("http.proxyPort"), 
-                System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword"), 
-                System.getProperty("http.nonProxyHosts"));
-    }
-
-    JavaSystemPropertiesHttpProxySettings(String proxyHost, String proxyPortString, String proxyUser, String proxyPassword, String nonProxyHostsString) {
-        if (StringUtils.isBlank(proxyHost)) {
-            this.proxy = null;
-        } else {
-            this.proxy = new HttpProxy(proxyHost, initProxyPort(proxyPortString), proxyUser, proxyPassword);
-        }
-        this.nonProxyHosts = initNonProxyHosts(nonProxyHostsString);
-    }
-
-    private int initProxyPort(String proxyPortString) {
-        if (StringUtils.isBlank(proxyPortString)) {
-            return DEFAULT_PROXY_PORT;
-        }
-        
-        try {
-            return Integer.parseInt(proxyPortString);
-        } catch (NumberFormatException e) {
-            LOGGER.warn("Invalid value for java system property 'http.proxyPort': {}. Default port '{}' will be used.", System.getProperty("http.proxyPort"), DEFAULT_PROXY_PORT);
-            return DEFAULT_PROXY_PORT;
-        }
-    }
-
-    private List<Pattern> initNonProxyHosts(String nonProxyHostsString) {
-        if (StringUtils.isBlank(nonProxyHostsString)) {
-            return Collections.emptyList();
-        }
-
-        LOGGER.debug("Found java system property 'http.nonProxyHosts': {}. Will ignore proxy settings for these hosts.", nonProxyHostsString);
-        List<Pattern> patterns = new ArrayList<Pattern>();
-        for (String nonProxyHost : nonProxyHostsString.split("\\|")) {
-            patterns.add(createHostMatcher(nonProxyHost));
-        }
-        return patterns;
-    }
-
-    private Pattern createHostMatcher(String nonProxyHost) {
-        if (nonProxyHost.startsWith("*")) {
-            return Pattern.compile(".*" + Pattern.quote(nonProxyHost.substring(1)));
-        }
-        if (nonProxyHost.endsWith("*")) {
-            return Pattern.compile(Pattern.quote(nonProxyHost.substring(0, nonProxyHost.length() - 1)) + ".*");
-        }
-        return Pattern.compile(Pattern.quote(nonProxyHost));
-    }
-
-    public HttpProxy getProxy() {
-        return proxy;
-    }
-
-    public HttpProxy getProxy(String host) {
-        if (proxy == null || isNonProxyHost(host)) {
-            return null;
-        }
-        return proxy;
-    }
-
-    private boolean isNonProxyHost(String host) {        
-        for (Pattern nonProxyHost : nonProxyHosts) {
-            if (nonProxyHost.matcher(host).matches()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/RepeatableInputStreamEntity.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/RepeatableInputStreamEntity.java
deleted file mode 100644
index f416f75..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/RepeatableInputStreamEntity.java
+++ /dev/null
@@ -1,60 +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.externalresource.transport.http;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.http.entity.AbstractHttpEntity;
-import org.apache.http.entity.ContentType;
-import org.gradle.internal.Factory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class RepeatableInputStreamEntity extends AbstractHttpEntity {
-    private final Factory<InputStream> source;
-    private final Long contentLength;
-
-    public RepeatableInputStreamEntity(Factory<InputStream> source, Long contentLength, ContentType contentType) {
-        super();
-        this.source = source;
-        this.contentLength = contentLength;
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
-    }
-
-    public boolean isRepeatable() {
-        return true;
-    }
-
-    public long getContentLength() {
-        return contentLength;
-    }
-
-    public InputStream getContent() throws IOException, IllegalStateException {
-        return source.create();
-    }
-
-    public void writeTo(OutputStream outstream) throws IOException {
-        IOUtils.copy(getContent(), outstream);
-    }
-
-    public boolean isStreaming() {
-        return true;
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentials.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentials.java
deleted file mode 100644
index 7adaacc..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentials.java
+++ /dev/null
@@ -1,92 +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.transport.http.ntlm;
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-public class NTLMCredentials {
-    private static final String DEFAULT_DOMAIN = "";
-    private static final String DEFAULT_WORKSTATION = "";
-    private final String domain;
-    private final String username;
-    private final String password;
-    private final String workstation;
-
-    public NTLMCredentials(PasswordCredentials credentials) {
-        String domain;
-        String username = credentials.getUsername();
-        int slashPos = username.indexOf('\\');
-        slashPos = slashPos >= 0 ? slashPos : username.indexOf('/');
-        if (slashPos >= 0) {
-            domain = username.substring(0, slashPos);
-            username = username.substring(slashPos + 1);
-        } else {
-            domain = System.getProperty("http.auth.ntlm.domain", DEFAULT_DOMAIN);
-        }
-        this.domain = domain == null ? null : domain.toUpperCase();
-        this.username = username;
-        this.password = credentials.getPassword();
-        this.workstation = determineWorkstationName();
-    }
-
-    private String determineWorkstationName() {
-        // TODO:DAZ This is a temporary (hidden) property that may be useful to track down issues. Remove when NTLM Auth is solid.
-        String sysPropWorkstation = System.getProperty("http.auth.ntlm.workstation");
-        if (sysPropWorkstation != null) {
-            return sysPropWorkstation;
-        }
-
-        try {
-            return removeDotSuffix(getHostName()).toUpperCase();
-        } catch (UnknownHostException e) {
-            return DEFAULT_WORKSTATION;
-        }
-    }
-
-    protected String getHostName() throws UnknownHostException {
-        return InetAddress.getLocalHost().getHostName();
-    }
-
-    private String removeDotSuffix(String val) {
-        int dotPos = val.indexOf('.');
-        return dotPos == -1 ? val : val.substring(0, dotPos);
-    }
-
-
-    public String getDomain() {
-        return domain;
-    }
-
-    public String getUsername() {
-        return username;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public String getWorkstation() {
-        return workstation;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("NTLM Credentials [user: %s, domain: %s, workstation: %s]", username, domain, workstation);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMSchemeFactory.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMSchemeFactory.java
deleted file mode 100644
index 29f6739..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMSchemeFactory.java
+++ /dev/null
@@ -1,64 +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.transport.http.ntlm;
-
-import jcifs.ntlmssp.Type1Message;
-import jcifs.ntlmssp.Type2Message;
-import jcifs.ntlmssp.Type3Message;
-import jcifs.util.Base64;
-import org.apache.http.auth.AuthScheme;
-import org.apache.http.auth.AuthSchemeFactory;
-import org.apache.http.impl.auth.NTLMEngine;
-import org.apache.http.impl.auth.NTLMEngineException;
-import org.apache.http.impl.auth.NTLMScheme;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpParams;
-
-import java.io.IOException;
-
-// Copied from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
-public class NTLMSchemeFactory implements AuthSchemeFactory {
-
-    public static void register(DefaultHttpClient httpClient) {
-        httpClient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
-    }
-
-    public AuthScheme newInstance(HttpParams params) {
-        return new NTLMScheme(new JCIFSEngine());
-    }
-
-    private static class JCIFSEngine implements NTLMEngine {
-
-        public String generateType1Msg(String domain, String workstation) throws NTLMEngineException {
-            Type1Message type1Message = new Type1Message(Type1Message.getDefaultFlags(), domain, workstation);
-            return Base64.encode(type1Message.toByteArray());
-        }
-
-        public String generateType3Msg(String username, String password, String domain, String workstation, String challenge) throws NTLMEngineException {
-            Type2Message type2Message = decodeType2Message(challenge);
-            Type3Message type3Message = new Type3Message(type2Message, password, domain, username, workstation, Type3Message.getDefaultFlags());
-            return Base64.encode(type3Message.toByteArray());
-        }
-
-        private Type2Message decodeType2Message(String challenge) throws NTLMEngineException {
-            try {
-                return new Type2Message(Base64.decode(challenge));
-            } catch (final IOException exception) {
-                throw new NTLMEngineException("Invalid Type2 message", exception);
-            }
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java
deleted file mode 100644
index 311941b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java
+++ /dev/null
@@ -1,44 +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.ivy;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData;
-import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePattern;
-import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.filestore.GroupedAndNamedUniqueFileStore;
-import org.gradle.api.internal.filestore.PathKeyFileStore;
-
-public class ArtifactIdentifierFileStore extends GroupedAndNamedUniqueFileStore<ModuleVersionArtifactMetaData> {
-
-    private static final String GROUP_PATTERN = "[organisation]/[module](/[branch])/[revision]";
-    private static final String NAME_PATTERN = "[artifact]-[revision](-[classifier])(.[ext])";
-
-    public ArtifactIdentifierFileStore(PathKeyFileStore pathKeyFileStore, TemporaryFileProvider temporaryFileProvider) {
-        super(pathKeyFileStore, temporaryFileProvider, toTransformer(GROUP_PATTERN), toTransformer(NAME_PATTERN));
-    }
-
-    private static Transformer<String, ModuleVersionArtifactMetaData> toTransformer(final String pattern) {
-        final ResourcePattern resourcePattern = new IvyResourcePattern(pattern);
-        return new Transformer<String, ModuleVersionArtifactMetaData>() {
-             public String transform(ModuleVersionArtifactMetaData artifact) {
-                 return resourcePattern.toPath(artifact);
-             }
-         };
-    }
-}
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
deleted file mode 100644
index e3ec371..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.notations;
-
-import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-
-public class ClientModuleNotationParserFactory implements Factory<NotationParser<Object, ClientModule>> {
-
-    private final Instantiator instantiator;
-
-    public ClientModuleNotationParserFactory(Instantiator instantiator) {
-        this.instantiator = instantiator;
-    }
-
-    public NotationParser<Object, ClientModule> create() {
-        return new NotationParserBuilder<ClientModule>()
-                .resultingType(ClientModule.class)
-                .parser(new DependencyStringNotationParser<DefaultClientModule>(instantiator, DefaultClientModule.class))
-                .parser(new DependencyMapNotationParser<DefaultClientModule>(instantiator, DefaultClientModule.class))
-                .toComposite();
-
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParser.java
deleted file mode 100755
index d88557b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParser.java
+++ /dev/null
@@ -1,56 +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.notations;
-
-import org.gradle.api.artifacts.SelfResolvingDependency;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency;
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-
-import java.io.File;
-import java.util.Collection;
-
-public class DependencyClassPathNotationParser
-        extends TypedNotationParser<DependencyFactory.ClassPathNotation, SelfResolvingDependency> {
-
-    private final ClassPathRegistry classPathRegistry;
-    private final Instantiator instantiator;
-    private final FileResolver fileResolver;
-
-    public DependencyClassPathNotationParser(Instantiator instantiator, ClassPathRegistry classPathRegistry,
-                                             FileResolver fileResolver) {
-        super(DependencyFactory.ClassPathNotation.class);
-
-        this.instantiator = instantiator;
-        this.classPathRegistry = classPathRegistry;
-        this.fileResolver = fileResolver;
-    }
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("ClassPathNotation, e.g. gradleApi().");
-    }
-
-    public SelfResolvingDependency parseType(DependencyFactory.ClassPathNotation notation) {
-        Collection<File> classpath = classPathRegistry.getClassPath(notation.name()).getAsFiles();
-        FileCollection files = fileResolver.resolveFiles(classpath);
-        return instantiator.newInstance(DefaultSelfResolvingDependency.class, files);
-    }
-}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyFilesNotationParser.java b/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyFilesNotationParser.java
deleted file mode 100644
index bd19502..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyFilesNotationParser.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.notations;
-
-import org.gradle.api.artifacts.SelfResolvingDependency;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-
-import java.util.Collection;
-
-public class DependencyFilesNotationParser
-        extends TypedNotationParser<FileCollection, SelfResolvingDependency> {
-
-    private final Instantiator instantiator;
-
-    public DependencyFilesNotationParser(Instantiator instantiator) {
-        super(FileCollection.class);
-        this.instantiator = instantiator;
-    }
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("FileCollections, e.g. files('some.jar', 'someOther.jar').");
-    }
-
-    public SelfResolvingDependency parseType(FileCollection notation) {
-        return instantiator.newInstance(DefaultSelfResolvingDependency.class, notation);
-    }
-}
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
deleted file mode 100644
index 160137b..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyMapNotationParser.java
+++ /dev/null
@@ -1,53 +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.notations;
-
-import org.gradle.api.artifacts.ExternalDependency;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ModuleFactoryHelper;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.api.tasks.Optional;
-import org.gradle.internal.reflect.Instantiator;
-
-import java.util.Collection;
-
-public class DependencyMapNotationParser<T extends ExternalDependency> extends MapNotationParser<T> {
-
-    private final Instantiator instantiator;
-    private final Class<T> resultingType;
-
-    public DependencyMapNotationParser(Instantiator instantiator, Class<T> resultingType) {
-        this.instantiator = instantiator;
-        this.resultingType = resultingType;
-    }
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Maps, e.g. [group: 'org.gradle', name: 'gradle-core', version: '1.0'].");
-    }
-
-    protected T parseMap(@MapKey("group") @Optional String group,
-                         @MapKey("name") @Optional String name,
-                         @MapKey("version") @Optional String version,
-                         @MapKey("configuration") @Optional String configuration,
-                         @MapKey("ext") @Optional String ext,
-                         @MapKey("classifier") @Optional String classifier) {
-        T dependency = instantiator.newInstance(resultingType, group, name, version, configuration);
-        ModuleFactoryHelper.addExplicitArtifactsIfDefined(dependency, ext, classifier);
-        return dependency;
-    }
-
-}
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
deleted file mode 100644
index 9d3b355..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyNotationParser.java
+++ /dev/null
@@ -1,55 +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.notations;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-
-import java.util.Collection;
-
-public class DependencyNotationParser implements NotationParser<Object, Dependency> {
-
-    private final NotationParser<Object, Dependency> delegate;
-
-    public DependencyNotationParser(Iterable<NotationParser<Object, ? extends Dependency>> compositeParsers) {
-        delegate = new NotationParserBuilder<Dependency>()
-                .resultingType(Dependency.class)
-                .parsers(compositeParsers)
-                .invalidNotationMessage("Comprehensive documentation on dependency notations is available in DSL reference for DependencyHandler type.")
-                .toComposite();
-    }
-
-    DependencyNotationParser(NotationParser<Object, Dependency> delegate) {
-        this.delegate = delegate;
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        delegate.describe(candidateFormats);
-    }
-
-    public Dependency parseNotation(Object dependencyNotation) {
-        try {
-            return delegate.parseNotation(dependencyNotation);
-        } catch (GradleException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not create a dependency using notation: %s", dependencyNotation), e);
-        }
-    }
-}
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
deleted file mode 100644
index 66c045d..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyProjectNotationParser.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.api.internal.notations;
-
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-
-import java.util.Collection;
-
-public class DependencyProjectNotationParser extends TypedNotationParser<Project, ProjectDependency> {
-
-    private final DefaultProjectDependencyFactory factory;
-
-    public DependencyProjectNotationParser(DefaultProjectDependencyFactory factory) {
-        super(Project.class);
-        this.factory = factory;
-    }
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Projects, e.g. project(':some:project:path').");
-    }
-
-    public ProjectDependency parseType(Project notation) {
-        return factory.create(notation);
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 8e79315..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/DependencyStringNotationParser.java
+++ /dev/null
@@ -1,74 +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.notations;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.artifacts.ExternalDependency;
-import org.gradle.api.internal.artifacts.dsl.ParsedModuleStringNotation;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ModuleFactoryHelper;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-import org.gradle.internal.reflect.Instantiator;
-
-import java.util.Collection;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DependencyStringNotationParser<T extends ExternalDependency> extends TypedNotationParser<CharSequence, T> {
-
-    private final Instantiator instantiator;
-    private final Class<T> wantedType;
-
-    public DependencyStringNotationParser(Instantiator instantiator, Class<T> wantedType) {
-        super(CharSequence.class);
-        this.instantiator = instantiator;
-        this.wantedType = wantedType;
-    }
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Strings/CharSequences, e.g. 'org.gradle:gradle-core:1.0'.");
-    }
-
-    protected T parseType(CharSequence notation) {
-        return createDependencyFromString(notation.toString());
-    }
-
-    public static final Pattern EXTENSION_SPLITTER = Pattern.compile("^(.+)\\@([^:]+$)");
-
-    private T createDependencyFromString(String notation) {
-
-        ParsedModuleStringNotation parsedNotation = splitModuleFromExtension(notation);
-        T moduleDependency = instantiator.newInstance(wantedType,
-                parsedNotation.getGroup(), parsedNotation.getName(), parsedNotation.getVersion());
-        ModuleFactoryHelper.addExplicitArtifactsIfDefined(moduleDependency, parsedNotation.getArtifactType(), parsedNotation.getClassifier());
-
-        return moduleDependency;
-    }
-
-    private ParsedModuleStringNotation splitModuleFromExtension(String notation) {
-        Matcher matcher = EXTENSION_SPLITTER.matcher(notation);
-        boolean hasArtifactType = matcher.matches();
-        if (hasArtifactType && !ClientModule.class.isAssignableFrom(wantedType)) {
-            if (matcher.groupCount() != 2) {
-                throw new InvalidUserDataException("The dependency notation " + notation + " is invalid");
-            }
-            return new ParsedModuleStringNotation(matcher.group(1), matcher.group(2));
-        }
-        return new ParsedModuleStringNotation(notation, null);
-    }
-}
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
deleted file mode 100644
index e518a67..0000000
--- a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/notations/ProjectDependencyFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.notations;
-
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.api.tasks.Optional;
-
-import java.util.Collection;
-import java.util.Map;
-
-public class ProjectDependencyFactory {
-    private final DefaultProjectDependencyFactory factory;
-
-    public ProjectDependencyFactory(DefaultProjectDependencyFactory factory) {
-        this.factory = factory;
-    }
-
-    public ProjectDependency createFromMap(ProjectFinder projectFinder,
-                                           Map<? extends String, ? extends Object> map) {
-        return new ProjectDependencyMapNotationParser(projectFinder, factory).parseNotation(map);
-    }
-
-    static class ProjectDependencyMapNotationParser extends MapNotationParser<ProjectDependency> {
-
-        private final ProjectFinder projectFinder;
-        private final DefaultProjectDependencyFactory factory;
-
-        public ProjectDependencyMapNotationParser(ProjectFinder projectFinder, DefaultProjectDependencyFactory factory) {
-            this.projectFinder = projectFinder;
-            this.factory = factory;
-        }
-
-        protected ProjectDependency parseMap(@MapKey("path") String path, @Optional @MapKey("configuration") String configuration) {
-            return factory.create(projectFinder.getProject(path), configuration);
-        }
-
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Map with mandatory 'path' and optional 'configuration' key, e.g. [path: ':someProj', configuration: 'someConf']");
-        }
-    }
-}
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
deleted file mode 100644
index d441b67..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
+++ /dev/null
@@ -1,92 +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.Action
-import org.gradle.api.artifacts.dsl.ArtifactHandler
-import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
-import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer
-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.dependencies.DefaultDependencyHandler
-import org.gradle.api.internal.artifacts.ivyservice.IvyBackedArtifactPublisher
-import org.gradle.api.internal.artifacts.repositories.DefaultBaseRepositoryFactory
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.DefaultServiceRegistry
-import org.gradle.internal.service.ServiceRegistration
-import org.gradle.internal.service.ServiceRegistry
-import spock.lang.Specification
-
-import java.lang.reflect.ParameterizedType
-
-class DefaultDependencyManagementServicesTest extends Specification {
-    final ServiceRegistry parent = Mock()
-    final Instantiator instantiator = new DirectInstantiator()
-    final DefaultDependencyManagementServices services = new DefaultDependencyManagementServices(parent)
-
-    def setup() {
-        _ * parent.get(Instantiator) >> instantiator
-        _ * parent.get({it instanceof Class}) >> { Class t -> Stub(t) }
-        _ * parent.get({it instanceof ParameterizedType}) >> { ParameterizedType t -> Stub(t.rawType) }
-    }
-
-    def "can create dependency resolution DSL services"() {
-        given:
-        def registry = new DefaultServiceRegistry(parent)
-
-        when:
-        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
-        def resolutionServices = registry.get(DependencyResolutionServices)
-
-        then:
-        resolutionServices.resolveRepositoryHandler instanceof DefaultRepositoryHandler
-        resolutionServices.configurationContainer instanceof DefaultConfigurationContainer
-        resolutionServices.dependencyHandler instanceof DefaultDependencyHandler
-        registry.get(ComponentMetadataHandler) instanceof DefaultComponentMetadataHandler
-        registry.get(ArtifactHandler) instanceof DefaultArtifactHandler
-        registry.get(BaseRepositoryFactory) instanceof DefaultBaseRepositoryFactory
-    }
-
-    def "publish services provide a repository handler"() {
-        given:
-        def registry = new DefaultServiceRegistry(parent)
-
-        when:
-        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
-        def publishServices = registry.get(ArtifactPublicationServices)
-
-        then:
-        def publishResolverHandler = publishServices.createRepositoryHandler()
-        publishResolverHandler instanceof DefaultRepositoryHandler
-        !publishResolverHandler.is(publishServices.createRepositoryHandler())
-    }
-
-    def "publish services provide an ArtifactPublisher"() {
-        given:
-        def registry = new DefaultServiceRegistry(parent)
-
-        when:
-        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
-        def publishServices = registry.get(ArtifactPublicationServices)
-
-        then:
-        def ivyService = publishServices.createArtifactPublisher()
-        ivyService instanceof IvyBackedArtifactPublisher
-        !ivyService.is(publishServices.createArtifactPublisher())
-    }
-}
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
deleted file mode 100644
index 38dd852..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy
+++ /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.artifacts
-
-import org.gradle.api.artifacts.ResolvedModuleVersion
-import org.gradle.api.internal.artifacts.metadata.IvyArtifactName
-import org.gradle.internal.Factory
-import org.gradle.util.Matchers
-import spock.lang.Specification
-
-class DefaultResolvedArtifactTest extends Specification {
-    final Factory artifactSource = Mock()
-
-    def "artifacts are equal when module and name are equal"() {
-        def dependency = dep("group", "module1", "1.2")
-        def dependencySameModule = dep("group", "module1", "1.2")
-        def dependency2 = dep("group", "module2", "1-beta")
-        def ivyArt = Stub(IvyArtifactName)
-        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, Stub(IvyArtifactName), artifactSource, 0)
-
-        expect:
-        artifact Matchers.strictlyEqual(equalArtifact)
-        artifact != differentModule
-        artifact != differentName
-    }
-
-    def dep(String group, String moduleName, String version) {
-        ResolvedModuleVersion module = Mock()
-        _ * module.id >> new DefaultModuleVersionIdentifier(group, moduleName, version)
-        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
deleted file mode 100644
index a28a53f..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
+++ /dev/null
@@ -1,92 +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.ResolvedArtifact
-import spock.lang.Specification
-
-class DefaultResolvedDependencySpec extends Specification {
-    final dependency = new DefaultResolvedDependency(DefaultModuleVersionIdentifier.newId("group", "module", "version"), "config")
-
-    def "provides meta-data about the module"() {
-        expect:
-        dependency.module.id.group == "group"
-        dependency.module.id.name == "module"
-        dependency.module.id.version == "version"
-    }
-
-    def "artifacts are ordered by name then classifier then extension then type"() {
-        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
-        ResolvedArtifact artifact2 = artifact("b", null, "jar", "jar")
-        ResolvedArtifact artifact3 = artifact("b", "a-classifier", "jar", "jar")
-        ResolvedArtifact artifact4 = artifact("b", "b-classifier", "b-type", "a-ext")
-        ResolvedArtifact artifact5 = artifact("b", "b-classifier", "a-type", "b-ext")
-        ResolvedArtifact artifact6 = artifact("b", "b-classifier", "b-type", "b-ext")
-        ResolvedArtifact artifact7 = artifact("c", "a-classifier", "jar", "jar")
-
-        given:
-        dependency.addModuleArtifact(artifact6)
-        dependency.addModuleArtifact(artifact1)
-        dependency.addModuleArtifact(artifact3)
-        dependency.addModuleArtifact(artifact5)
-        dependency.addModuleArtifact(artifact2)
-        dependency.addModuleArtifact(artifact7)
-        dependency.addModuleArtifact(artifact4)
-
-        expect:
-        dependency.moduleArtifacts as List == [artifact1, artifact2, artifact3, artifact4, artifact5, artifact6, artifact7]
-    }
-
-    def "does not discard artifacts with the same name and classifier and extension and type"() {
-        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
-        ResolvedArtifact artifact2 = artifact("a", null, "jar", "jar")
-
-        given:
-        dependency.addModuleArtifact(artifact1)
-        dependency.addModuleArtifact(artifact2)
-
-        expect:
-        dependency.moduleArtifacts == [artifact1, artifact2] as Set
-    }
-
-    def "parent specific artifacts are ordered by name then classifier then extension then type"() {
-        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
-        ResolvedArtifact artifact2 = artifact("b", null, "jar", "jar")
-        ResolvedArtifact artifact3 = artifact("b", "a-classifier", "jar", "jar")
-        ResolvedArtifact artifact4 = artifact("b", "b-classifier", "b-type", "a-ext")
-        ResolvedArtifact artifact5 = artifact("b", "b-classifier", "a-type", "b-ext")
-        ResolvedArtifact artifact6 = artifact("b", "b-classifier", "b-type", "b-ext")
-        ResolvedArtifact artifact7 = artifact("c", "a-classifier", "jar", "jar")
-        DefaultResolvedDependency parent = Mock()
-
-        given:
-        dependency.parents.add(parent)
-        dependency.addParentSpecificArtifacts(parent, [artifact6, artifact1, artifact7, artifact5, artifact2, artifact3, artifact4] as Set)
-
-        expect:
-        dependency.getParentArtifacts(parent) as List == [artifact1, artifact2, artifact3, artifact4, artifact5, artifact6, artifact7]
-    }
-
-    def artifact(String name, String classifier, String type, String extension) {
-        ResolvedArtifact artifact = Mock()
-        _ * artifact.toString() >> "$name-$classifier-$type.$extension"
-        _ * artifact.name >> name
-        _ * artifact.classifier >> classifier
-        _ * artifact.type >> type
-        _ * artifact.extension >> extension
-        return artifact
-    }
-}
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
deleted file mode 100644
index f88f6a1..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvalidUserDataException;
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.api.artifacts.ResolvedModuleVersion;
-import org.gradle.api.internal.artifacts.metadata.IvyArtifactName;
-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;
-
-import java.io.File;
-import java.util.Collections;
-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.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;
-
-public class DefaultResolvedDependencyTest {
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-
-    @Test
-    public void init() {
-        String someGroup = "someGroup";
-        String someName = "someName";
-        String someVersion = "someVersion";
-        String someConfiguration = "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));
-        assertThat(resolvedDependency.getModuleVersion(), equalTo(someVersion));
-        assertThat(resolvedDependency.getConfiguration(), equalTo(someConfiguration));
-        assertThat(resolvedDependency.getModuleArtifacts(), equalTo(Collections.<ResolvedArtifact>emptySet()));
-        assertThat(resolvedDependency.getChildren(), equalTo(Collections.<ResolvedDependency>emptySet()));
-        assertThat(resolvedDependency.getParents(), equalTo(Collections.<ResolvedDependency>emptySet()));
-    }
-
-    @Test
-    public void getAllModuleArtifacts() {
-        ResolvedArtifact moduleArtifact = createArtifact("moduleArtifact");
-        ResolvedArtifact childModuleArtifact = createArtifact("childModuleArtifact");
-        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency(newId("someGroup", "someName", "someVersion"), "someConfiguration");
-        resolvedDependency.addModuleArtifact(moduleArtifact);
-        DefaultResolvedDependency childDependency = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
-        childDependency.addModuleArtifact(childModuleArtifact);
-        resolvedDependency.getChildren().add(childDependency);
-        assertThat(resolvedDependency.getAllModuleArtifacts(), equalTo(toSet(moduleArtifact, childModuleArtifact)));
-    }
-
-    @Test
-    public void getParentArtifacts() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        Set<ResolvedArtifact> parent1SpecificArtifacts = toSet(createArtifact("parent1Specific"));
-        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
-
-        Set<ResolvedArtifact> parent2SpecificArtifacts = toSet(createArtifact("parent2Specific"));
-        DefaultResolvedDependency parentResolvedDependency2 = createAndAddParent("parent2", resolvedDependency, parent2SpecificArtifacts);
-
-        assertThat(resolvedDependency.getParentArtifacts(parentResolvedDependency1), equalTo(parent1SpecificArtifacts));
-        assertThat(resolvedDependency.getParentArtifacts(parentResolvedDependency2), equalTo(parent2SpecificArtifacts));
-    }
-
-    private ResolvedArtifact createArtifact(String name) {
-        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 IvyArtifactName artifactStub = context.mock(IvyArtifactName.class, "artifact" + name);
-        context.checking(new Expectations() {{
-            allowing(artifactStub).getName();
-            will(returnValue(name));
-            allowing(artifactStub).getType();
-            will(returnValue(type));
-            allowing(artifactStub).getExtension();
-            will(returnValue(extension));
-            allowing(artifactStub).getClassifier();
-            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(newId("someGroup", "someName", "someVersion"), "someConfiguration");
-    }
-
-    @Test
-    public void getArtifacts() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        Set<ResolvedArtifact> parent1SpecificArtifacts = toSet(createArtifact("parent1Specific"));
-        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
-
-        assertThat(resolvedDependency.getArtifacts(parentResolvedDependency1), equalTo(parent1SpecificArtifacts));
-    }
-
-    @Test
-    public void getArtifactsWithParentWithoutParentArtifacts() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
-        resolvedDependency.getParents().add(parent);
-        assertThat(resolvedDependency.getArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
-    }
-
-    @Test
-    public void getParentArtifactsWithParentWithoutParentArtifacts() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
-        resolvedDependency.getParents().add(parent);
-        assertThat(resolvedDependency.getParentArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void getParentArtifactsWithUnknownParent() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
-        assertThat(resolvedDependency.getParentArtifacts(unknownParent),
-                equalTo(Collections.<ResolvedArtifact>emptySet()));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void getArtifactsWithUnknownParent() {
-        Set<ResolvedArtifact> someModuleArtifacts = toSet(createArtifact("someModuleResolvedArtifact"));
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
-        assertThat(resolvedDependency.getParentArtifacts(unknownParent),
-                equalTo(someModuleArtifacts));
-    }
-
-    @Test
-    public void getAllArtifacts() {
-        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
-
-        Set<ResolvedArtifact> parent1SpecificArtifacts = newHashSet(createArtifact("parent1Specific"));
-        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
-
-        createAndAddParent("parent2", resolvedDependency, newHashSet(createArtifact("parent2Specific")));
-
-        DefaultResolvedDependency child = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
-        resolvedDependency.getChildren().add(child);
-
-        Set<ResolvedArtifact> childParent1SpecificArtifacts = newHashSet(createArtifact("childParent1Specific"));
-        createAndAddParent("childParent1", child, childParent1SpecificArtifacts);
-
-        Set<ResolvedArtifact> childParent2SpecificArtifacts = newHashSet(createArtifact("childParent2Specific"));
-        createAndAddParent("childParent2", child, childParent2SpecificArtifacts);
-
-        Iterable<ResolvedArtifact> allArtifacts = newHashSet(concat(parent1SpecificArtifacts, childParent1SpecificArtifacts, childParent2SpecificArtifacts));
-        assertThat(resolvedDependency.getAllArtifacts(parentResolvedDependency1), equalTo(allArtifacts));
-    }
-
-    @Test
-    public void equalsAndHashCode() {
-        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)));
-        assertThat(dependency, not(equalTo(differentName)));
-        assertThat(dependency, not(equalTo(differentVersion)));
-        assertThat(dependency, not(equalTo(differentConfiguration)));
-    }
-
-    private DefaultResolvedDependency createAndAddParent(String parentName, DefaultResolvedDependency resolvedDependency, Set<ResolvedArtifact> parentSpecificArtifacts) {
-        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
deleted file mode 100644
index 40e3c28..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy
+++ /dev/null
@@ -1,33 +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
-
-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
deleted file mode 100644
index 233d792..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.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.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/ResolverResultsSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
deleted file mode 100644
index 596d32b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
+++ /dev/null
@@ -1,52 +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
-
-import org.gradle.api.artifacts.ResolveException
-import org.gradle.api.artifacts.ResolvedConfiguration
-import org.gradle.api.artifacts.result.ResolutionResult
-import spock.lang.Specification
-
-class ResolverResultsSpec extends Specification {
-    private resolvedConfiguration = Mock(ResolvedConfiguration)
-    private resolutionResult = Mock(ResolutionResult)
-    private fatalFailure = Mock(ResolveException)
-    private results = new ResolverResults()
-
-    def "does not provide ResolutionResult in case of fatal failure"() {
-        when:
-        results.failed(resolvedConfiguration, fatalFailure)
-
-        then:
-        results.resolvedConfiguration
-
-        when:
-        results.resolutionResult
-        then:
-        def ex = thrown(ResolveException)
-        ex == fatalFailure
-    }
-
-    def "provides resolve results"() {
-        when:
-        results.resolved(resolvedConfiguration, resolutionResult)
-
-        then:
-        results.resolvedConfiguration == resolvedConfiguration
-        results.resolutionResult == resolutionResult
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy
deleted file mode 100644
index 3353a45..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component
-
-import org.gradle.api.Project
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.DefaultModule
-import org.gradle.api.internal.artifacts.ModuleInternal
-import org.gradle.api.internal.artifacts.ProjectBackedModule
-import org.gradle.api.internal.project.ProjectInternal
-import spock.lang.Specification
-
-class DefaultComponentIdentifierFactoryTest extends Specification {
-    ComponentIdentifierFactory componentIdentifierFactory = new DefaultComponentIdentifierFactory()
-
-    def "can create project component identifier"() {
-        given:
-        Project project = Mock(ProjectInternal)
-        ModuleInternal module = new ProjectBackedModule(project)
-
-        when:
-        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module)
-
-        then:
-        project.path >> ':a'
-        componentIdentifier == new DefaultProjectComponentIdentifier(':a')
-    }
-
-    def "can create module component identifier"() {
-        given:
-        ModuleInternal module = new DefaultModule('some-group', 'some-name', '1.0')
-
-        when:
-        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module)
-
-        then:
-        componentIdentifier == new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifierTest.groovy
deleted file mode 100644
index 4a3119d..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentIdentifierTest.groovy
+++ /dev/null
@@ -1,81 +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.component
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import static org.gradle.util.Matchers.strictlyEquals
-
-class DefaultModuleComponentIdentifierTest extends Specification {
-    def "is instantiated with non-null constructor parameter values"() {
-        when:
-        ModuleComponentIdentifier defaultModuleComponentIdentifier = new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
-
-        then:
-        defaultModuleComponentIdentifier.group == 'some-group'
-        defaultModuleComponentIdentifier.module == 'some-name'
-        defaultModuleComponentIdentifier.version == '1.0'
-        defaultModuleComponentIdentifier.displayName == 'some-group:some-name:1.0'
-        defaultModuleComponentIdentifier.toString() == 'some-group:some-name:1.0'
-    }
-
-    @Unroll
-    def "is instantiated with null constructor parameter values (#group, #name, #version)"() {
-        when:
-        new DefaultModuleComponentIdentifier(group, name, version)
-
-        then:
-        thrown(AssertionError)
-
-        where:
-        group        | name        | version
-        null         | 'some-name' | '1.0'
-        'some-group' | null        | '1.0'
-        'some-group' | 'some-name' | null
-    }
-
-    @Unroll
-    def "can compare with other instance (#group, #name, #version)"() {
-        expect:
-        ModuleComponentIdentifier defaultModuleComponentIdentifier1 = new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
-        ModuleComponentIdentifier defaultModuleComponentIdentifier2 = new DefaultModuleComponentIdentifier(group, name, version)
-        strictlyEquals(defaultModuleComponentIdentifier1, defaultModuleComponentIdentifier2) == equality
-        (defaultModuleComponentIdentifier1.hashCode() == defaultModuleComponentIdentifier2.hashCode()) == hashCode
-        (defaultModuleComponentIdentifier1.toString() == defaultModuleComponentIdentifier2.toString()) == stringRepresentation
-
-        where:
-        group         | name         | version | equality | hashCode | stringRepresentation
-        'some-group'  | 'some-name'  | '1.0'   | true     | true     | true
-        'other-group' | 'some-name'  | '1.0'   | false    | false    | false
-        'some-group'  | 'other-name' | '1.0'   | false    | false    | false
-        'some-group'  | 'some-name'  | '2.0'   | false    | false    | false
-    }
-
-    def "can create new ID"() {
-        when:
-        ModuleComponentIdentifier defaultModuleComponentIdentifier = DefaultModuleComponentIdentifier.newId('some-group', 'some-name', '1.0')
-
-        then:
-        defaultModuleComponentIdentifier.group == 'some-group'
-        defaultModuleComponentIdentifier.module == 'some-name'
-        defaultModuleComponentIdentifier.version == '1.0'
-        defaultModuleComponentIdentifier.displayName == 'some-group:some-name:1.0'
-        defaultModuleComponentIdentifier.toString() == 'some-group:some-name:1.0'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelectorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelectorTest.groovy
deleted file mode 100644
index a9eee5e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultModuleComponentSelectorTest.groovy
+++ /dev/null
@@ -1,117 +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.component
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import static org.gradle.util.Matchers.strictlyEquals
-
-class DefaultModuleComponentSelectorTest extends Specification {
-    def "is instantiated with non-null constructor parameter values"() {
-        when:
-        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
-
-        then:
-        defaultModuleComponentSelector.group == 'some-group'
-        defaultModuleComponentSelector.module == 'some-name'
-        defaultModuleComponentSelector.version == '1.0'
-        defaultModuleComponentSelector.displayName == 'some-group:some-name:1.0'
-        defaultModuleComponentSelector.toString() == 'some-group:some-name:1.0'
-    }
-
-    @Unroll
-    def "is instantiated with null constructor parameter values (#group, #name, #version)"() {
-        when:
-        new DefaultModuleComponentSelector(group, name, version)
-
-        then:
-        Throwable t = thrown(AssertionError)
-        assert t.message == assertionMessage
-
-        where:
-        group        | name        | version | assertionMessage
-        null         | 'some-name' | '1.0'   | 'group cannot be null'
-        'some-group' | null        | '1.0'   | 'module cannot be null'
-        'some-group' | 'some-name' | null    | 'version cannot be null'
-    }
-
-    @Unroll
-    def "can compare with other instance (#group, #name, #version)"() {
-        expect:
-        ModuleComponentSelector defaultModuleComponentSelector1 = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
-        ModuleComponentSelector defaultModuleComponentSelector2 = new DefaultModuleComponentSelector(group, name, version)
-        strictlyEquals(defaultModuleComponentSelector1, defaultModuleComponentSelector2) == equality
-        (defaultModuleComponentSelector1.hashCode() == defaultModuleComponentSelector2.hashCode()) == hashCode
-        (defaultModuleComponentSelector1.toString() == defaultModuleComponentSelector2.toString()) == stringRepresentation
-
-        where:
-        group         | name         | version | equality | hashCode | stringRepresentation
-        'some-group'  | 'some-name'  | '1.0'   | true     | true     | true
-        'other-group' | 'some-name'  | '1.0'   | false    | false    | false
-        'some-group'  | 'other-name' | '1.0'   | false    | false    | false
-        'some-group'  | 'some-name'  | '2.0'   | false    | false    | false
-    }
-
-    def "can create new selector"() {
-        when:
-        ModuleComponentSelector defaultModuleComponentSelector = DefaultModuleComponentSelector.newSelector('some-group', 'some-name', '1.0')
-
-        then:
-        defaultModuleComponentSelector.group == 'some-group'
-        defaultModuleComponentSelector.module == 'some-name'
-        defaultModuleComponentSelector.version == '1.0'
-        defaultModuleComponentSelector.displayName == 'some-group:some-name:1.0'
-        defaultModuleComponentSelector.toString() == 'some-group:some-name:1.0'
-    }
-
-    def "prevents matching of null id"() {
-        when:
-        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
-        defaultModuleComponentSelector.matchesStrictly(null)
-
-        then:
-        Throwable t = thrown(AssertionError)
-        assert t.message == 'identifier cannot be null'
-    }
-
-    def "does not match id for unexpected component selector type"() {
-        when:
-        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
-        boolean matches = defaultModuleComponentSelector.matchesStrictly(new DefaultProjectComponentIdentifier(':mypath'))
-
-        then:
-        assert !matches
-    }
-
-    @Unroll
-    def "matches id (#group, #name, #version)"() {
-        expect:
-        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
-        ModuleComponentIdentifier defaultModuleComponentIdentifier = new DefaultModuleComponentIdentifier(group, name, version)
-        defaultModuleComponentSelector.matchesStrictly(defaultModuleComponentIdentifier) == matchesId
-
-        where:
-        group         | name         | version | matchesId
-        'some-group'  | 'some-name'  | '1.0'   | true
-        'other-group' | 'some-name'  | '1.0'   | false
-        'some-group'  | 'other-name' | '1.0'   | false
-        'some-group'  | 'some-name'  | '2.0'   | false
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifierTest.groovy
deleted file mode 100644
index ddf9516..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentIdentifierTest.groovy
+++ /dev/null
@@ -1,58 +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.component
-
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import static org.gradle.util.Matchers.strictlyEquals
-
-class DefaultProjectComponentIdentifierTest extends Specification {
-    def "is instantiated with non-null constructor parameter values"() {
-        when:
-        ProjectComponentIdentifier defaultBuildComponentIdentifier = new DefaultProjectComponentIdentifier(':myPath')
-
-        then:
-        defaultBuildComponentIdentifier.projectPath == ':myPath'
-        defaultBuildComponentIdentifier.displayName == 'project :myPath'
-        defaultBuildComponentIdentifier.toString() == 'project :myPath'
-    }
-
-    def "is instantiated with null constructor parameter value"() {
-        when:
-        new DefaultProjectComponentIdentifier(null)
-
-        then:
-        Throwable t = thrown(AssertionError)
-        t.message == 'project path cannot be null'
-    }
-
-    @Unroll
-    def "can compare with other instance (#projectPath)"() {
-        expect:
-        ProjectComponentIdentifier defaultBuildComponentIdentifier1 = new DefaultProjectComponentIdentifier(':myProjectPath1')
-        ProjectComponentIdentifier defaultBuildComponentIdentifier2 = new DefaultProjectComponentIdentifier(projectPath)
-        strictlyEquals(defaultBuildComponentIdentifier1, defaultBuildComponentIdentifier2) == equality
-        (defaultBuildComponentIdentifier1.hashCode() == defaultBuildComponentIdentifier2.hashCode()) == hashCode
-        (defaultBuildComponentIdentifier1.toString() == defaultBuildComponentIdentifier2.toString()) == stringRepresentation
-
-        where:
-        projectPath       | equality | hashCode | stringRepresentation
-        ':myProjectPath1' | true     | true     | true
-        ':myProjectPath2' | false    | false    | false
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelectorTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelectorTest.groovy
deleted file mode 100644
index a0f1d22..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultProjectComponentSelectorTest.groovy
+++ /dev/null
@@ -1,91 +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.component
-
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier
-import org.gradle.api.artifacts.component.ProjectComponentSelector
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import static org.gradle.util.Matchers.strictlyEquals
-
-class DefaultProjectComponentSelectorTest extends Specification {
-    def "is instantiated with non-null constructor parameter values"() {
-        when:
-        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
-
-        then:
-        defaultBuildComponentSelector.projectPath == ':myPath'
-        defaultBuildComponentSelector.displayName == 'project :myPath'
-        defaultBuildComponentSelector.toString() == 'project :myPath'
-    }
-
-    def "is instantiated with null constructor parameter value"() {
-        when:
-        new DefaultProjectComponentSelector(null)
-
-        then:
-        Throwable t = thrown(AssertionError)
-        t.message == 'project path cannot be null'
-    }
-
-    @Unroll
-    def "can compare with other instance (#projectPath)"() {
-        expect:
-        ProjectComponentSelector defaultBuildComponentSelector1 = new DefaultProjectComponentSelector(':myProjectPath1')
-        ProjectComponentSelector defaultBuildComponentSelector2 = new DefaultProjectComponentSelector(projectPath)
-        strictlyEquals(defaultBuildComponentSelector1, defaultBuildComponentSelector2) == equality
-        (defaultBuildComponentSelector1.hashCode() == defaultBuildComponentSelector2.hashCode()) == hashCode
-        (defaultBuildComponentSelector1.toString() == defaultBuildComponentSelector2.toString()) == stringRepresentation
-
-        where:
-        projectPath       | equality | hashCode | stringRepresentation
-        ':myProjectPath1' | true     | true     | true
-        ':myProjectPath2' | false    | false    | false
-    }
-
-    def "prevents matching of null id"() {
-        when:
-        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
-        defaultBuildComponentSelector.matchesStrictly(null)
-
-        then:
-        Throwable t = thrown(AssertionError)
-        assert t.message == 'identifier cannot be null'
-    }
-
-    def "does not match id for unexpected component selector type"() {
-        when:
-        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
-        boolean matches = defaultBuildComponentSelector.matchesStrictly(new DefaultModuleComponentIdentifier('group', 'name', '1.0'))
-
-        then:
-        assert !matches
-    }
-
-    @Unroll
-    def "matches id (#projectPath)"() {
-        expect:
-        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myProjectPath1')
-        ProjectComponentIdentifier defaultBuildComponentIdentifier = new DefaultProjectComponentIdentifier(projectPath)
-        defaultBuildComponentSelector.matchesStrictly(defaultBuildComponentIdentifier) == matchesId
-
-        where:
-        projectPath       | matchesId
-        ':myProjectPath1' | true
-        ':myProjectPath2' | false
-    }
-}
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
deleted file mode 100644
index 848d31c..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
+++ /dev/null
@@ -1,100 +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.configurations
-
-import org.gradle.api.artifacts.UnknownConfigurationException
-import org.gradle.api.internal.DomainObjectContext
-import org.gradle.api.internal.artifacts.ConfigurationResolver
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.listener.ListenerManager
-import spock.lang.Specification
-
-public class DefaultConfigurationContainerSpec extends Specification {
-
-    private ConfigurationResolver resolver = Mock()
-    private Instantiator instantiator = Mock()
-    private DomainObjectContext domainObjectContext = Mock()
-    private ListenerManager listenerManager = Mock()
-    private DependencyMetaDataProvider metaDataProvider = Mock()
-
-    def ConfigurationInternal conf = Mock()
-
-    private DefaultConfigurationContainer configurationContainer = new DefaultConfigurationContainer(
-            resolver, instantiator, domainObjectContext,
-            listenerManager, metaDataProvider);
-
-    def "adds and gets"() {
-        _ * conf.getName() >> "compile"
-        1 * domainObjectContext.absoluteProjectPath("compile") >> ":compile"
-        1 * instantiator.newInstance(DefaultResolutionStrategy.class) >> { new DefaultResolutionStrategy() }
-        1 * instantiator.newInstance(DefaultConfiguration.class, ":compile", "compile", configurationContainer,
-                resolver, listenerManager, metaDataProvider, _ as ResolutionStrategyInternal) >> conf
-
-        when:
-        def compile = configurationContainer.create("compile")
-
-        then:
-        configurationContainer.getByName("compile") == compile
-
-        when:
-        configurationContainer.getByName("fooo")
-
-        then:
-        thrown(UnknownConfigurationException)
-    }
-
-    def "configures and finds"() {
-        _ * conf.getName() >> "compile"
-        1 * domainObjectContext.absoluteProjectPath("compile") >> ":compile"
-        1 * instantiator.newInstance(DefaultResolutionStrategy.class) >> { new DefaultResolutionStrategy() }
-        1 * instantiator.newInstance(DefaultConfiguration.class, ":compile", "compile", configurationContainer,
-                resolver, listenerManager, metaDataProvider, _ as ResolutionStrategyInternal) >> conf
-
-        when:
-        def compile = configurationContainer.add("compile") {
-            description = "I compile!"
-        }
-
-        then:
-        configurationContainer.getByName("compile") == compile
-        1 * conf.setDescription("I compile!")
-
-        //finds configurations
-        configurationContainer.findByName("compile") == compile
-        configurationContainer.findByName("foo") == null
-        configurationContainer.findAll { it.name == "compile" } as Set == [compile] as Set
-        configurationContainer.findAll { it.name == "foo" } as Set == [] as Set
-
-        configurationContainer as List == [compile] as List
-    }
-
-    def "creates detached"() {
-        given:
-        def dependency1 = new DefaultExternalModuleDependency("group", "name", "version")
-        def dependency2 = new DefaultExternalModuleDependency("group", "name2", "version")
-
-        when:
-        def detached = configurationContainer.detachedConfiguration(dependency1, dependency2);
-
-        then:
-        detached.getAll() == [detached] as Set
-        detached.getHierarchy() == [detached] as Set
-        [dependency1, dependency2].each { detached.getDependencies().contains(it) }
-        detached.getDependencies().size() == 2
-    }
-}
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
deleted file mode 100644
index 6efd9ed..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts.configurations
-
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.UnknownConfigurationException
-import org.gradle.api.internal.AsmBackedClassGenerator
-import org.gradle.api.internal.ClassGeneratorBackedInstantiator
-import org.gradle.api.internal.DomainObjectContext
-import org.gradle.api.internal.MissingMethodException
-import org.gradle.api.internal.artifacts.ConfigurationResolver
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.listener.ListenerManager
-import org.gradle.util.JUnit4GroovyMockery
-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.assertThat
-
-
- at RunWith(JMock)
-class DefaultConfigurationContainerTest {
-    private JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-
-    private ConfigurationResolver resolver = context.mock(ConfigurationResolver)
-    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 configurationContainer = instantiator.newInstance(DefaultConfigurationContainer.class,
-            resolver, instantiator, { name -> name } as DomainObjectContext,
-            listenerManager, metaDataProvider)
-
-    @Before
-    public void setup() {
-        context.checking {
-            ignoring(listenerManager)
-        }
-    }
-
-    @Test
-    void addsNewConfigurationWhenConfiguringSelf() {
-        configurationContainer.configure {
-            newConf
-        }
-        assertThat(configurationContainer.findByName('newConf'), notNullValue())
-        assertThat(configurationContainer.newConf, notNullValue())
-    }
-
-    @Test(expected = UnknownConfigurationException)
-    void doesNotAddNewConfigurationWhenNotConfiguringSelf() {
-        configurationContainer.getByName('unknown')
-    }
-
-    @Test
-    void makesExistingConfigurationAvailableAsProperty() {
-        Configuration configuration = configurationContainer.create('newConf')
-        assertThat(configuration, notNullValue())
-        assertThat(configurationContainer.getByName("newConf"), sameInstance(configuration))
-        assertThat(configurationContainer.newConf, sameInstance(configuration))
-    }
-
-    @Test
-    void addsNewConfigurationWithClosureWhenConfiguringSelf() {
-        String someDesc = 'desc1'
-        configurationContainer.configure {
-            newConf {
-                description = someDesc
-            }
-        }
-        assertThat(configurationContainer.newConf.getDescription(), equalTo(someDesc))
-    }
-
-    @Test
-    void makesExistingConfigurationAvailableAsConfigureMethod() {
-        String someDesc = 'desc1'
-        configurationContainer.create('newConf')
-        Configuration configuration = configurationContainer.newConf {
-            description = someDesc
-        }
-        assertThat(configuration.getDescription(), equalTo(someDesc))
-    }
-
-    @Test
-    void makesExistingConfigurationAvailableAsConfigureMethodWhenConfiguringSelf() {
-        String someDesc = 'desc1'
-        Configuration configuration = configurationContainer.create('newConf')
-        configurationContainer.configure {
-            newConf {
-                description = someDesc
-            }
-        }
-        assertThat(configuration.getDescription(), equalTo(someDesc))
-    }
-
-    @Test(expected = MissingMethodException)
-    void newConfigurationWithNonClosureParametersShouldThrowMissingMethodEx() {
-        configurationContainer.newConf('a', 'b')
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
deleted file mode 100644
index 0d01f1c..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
+++ /dev/null
@@ -1,320 +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.configurations
-
-import org.gradle.api.Action
-import org.gradle.api.Task
-import org.gradle.api.artifacts.result.ResolutionResult
-import org.gradle.api.internal.artifacts.ConfigurationResolver
-import org.gradle.api.internal.artifacts.ResolverResults
-import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
-import org.gradle.api.tasks.TaskDependency
-import org.gradle.listener.ListenerBroadcast
-import org.gradle.listener.ListenerManager
-import spock.lang.Specification
-import org.gradle.api.artifacts.*
-
-class DefaultConfigurationSpec extends Specification {
-
-    ConfigurationsProvider configurationsProvider = Mock()
-    ConfigurationResolver resolver = Mock()
-    ListenerManager listenerManager = Mock()
-    DependencyMetaDataProvider metaDataProvider = Mock()
-    ResolutionStrategyInternal resolutionStrategy = Mock()
-
-    DefaultConfiguration conf(String confName = "conf", String path = ":conf") {
-        new DefaultConfiguration(path, confName, configurationsProvider, resolver, listenerManager, metaDataProvider, resolutionStrategy)
-    }
-
-    DefaultPublishArtifact artifact(String name) {
-        artifact(name: name)
-    }
-
-    DefaultPublishArtifact artifact(Map props = [:]) {
-        new DefaultPublishArtifact(
-            props.name ?: "artifact",
-            props.extension ?: "artifact",
-            props.type,
-            props.classifier,
-            props.date,
-            props.file,
-            props.tasks ?: []
-        )
-    }
-
-    // You need to wrap this in an interaction {} block when calling it
-    ResolvedConfiguration resolvedConfiguration(Configuration config, ConfigurationResolver dependencyResolver = resolver) {
-        ResolvedConfiguration resolvedConfiguration = Mock()
-        def results = new ResolverResults()
-        results.resolved(resolvedConfiguration, Mock(ResolutionResult))
-        1 * dependencyResolver.resolve(config) >> results
-        resolvedConfiguration
-    }
-
-    def setup() {
-        ListenerBroadcast<DependencyResolutionListener> broadcast = new ListenerBroadcast<DependencyResolutionListener>(DependencyResolutionListener)
-        _ * listenerManager.createAnonymousBroadcaster(DependencyResolutionListener) >> broadcast
-    }
-
-    def "all artifacts collection has immediate artifacts"() {
-        given:
-        def c = conf()
-
-        when:
-        c.artifacts << artifact()
-        c.artifacts << artifact()
-
-        then:
-        c.allArtifacts.size() == 2
-    }
-
-    def "all artifacts collection has inherited artifacts"() {
-        given:
-        def master = conf()
-
-        def masterParent1 = conf()
-        def masterParent2 = conf()
-        master.extendsFrom masterParent1, masterParent2
-
-        def masterParent1Parent1 = conf()
-        def masterParent1Parent2 = conf()
-        masterParent1.extendsFrom masterParent1Parent1, masterParent1Parent2
-
-        def masterParent2Parent1 = conf()
-        def masterParent2Parent2 = conf()
-        masterParent2.extendsFrom masterParent2Parent1, masterParent2Parent2
-
-        def allArtifacts = master.allArtifacts
-
-        def added = []
-        allArtifacts.whenObjectAdded { added << it.name }
-        def removed = []
-        allArtifacts.whenObjectRemoved { removed << it.name }
-
-        expect:
-        allArtifacts.empty
-
-        when:
-        masterParent1.artifacts << artifact("p1-1")
-        masterParent1Parent1.artifacts << artifact("p1p1-1")
-        masterParent1Parent2.artifacts << artifact("p1p2-1")
-        masterParent2.artifacts << artifact("p2-1")
-        masterParent2Parent1.artifacts << artifact("p2p1-1")
-        masterParent2Parent2.artifacts << artifact("p2p2-1")
-
-        then:
-        allArtifacts.size() == 6
-        added == ["p1-1", "p1p1-1", "p1p2-1", "p2-1", "p2p1-1", "p2p2-1"]
-
-        when:
-        masterParent2Parent2.artifacts.remove masterParent2Parent2.artifacts.toList().first()
-
-        then:
-        allArtifacts.size() == 5
-        removed == ["p2p2-1"]
-
-        when:
-        removed.clear()
-        masterParent1.extendsFrom = []
-
-        then:
-        allArtifacts.size() == 3
-        removed == ["p1p1-1", "p1p2-1"]
-    }
-
-    def "incoming dependencies set has same name and path as owner configuration"() {
-        def config = conf("conf", ":path")
-
-        expect:
-        config.incoming.name == "conf"
-        config.incoming.path == ":path"
-    }
-
-    def "incoming dependencies set contains immediate dependencies"() {
-        def config = conf("conf")
-        Dependency dep1 = Mock()
-
-        given:
-        config.dependencies.add(dep1)
-
-        expect:
-        config.incoming.dependencies as List == [dep1]
-    }
-
-    def "incoming dependencies set contains inherited dependencies"() {
-        def parent = conf("conf")
-        def config = conf("conf")
-        Dependency dep1 = Mock()
-
-        given:
-        config.extendsFrom parent
-        parent.dependencies.add(dep1)
-
-        expect:
-        config.incoming.dependencies as List == [dep1]
-    }
-
-    def "incoming dependencies set files are resolved lazily"() {
-        setup:
-        def config = conf("conf")
-
-        when:
-        def files = config.incoming.files
-
-        then:
-        0 * _._
-
-        when:
-        files.files
-
-        then:
-        interaction { resolvedConfiguration(config) }
-        0 * resolver._
-    }
-
-    def "incoming dependencies set depends on all self resolving dependencies"() {
-        SelfResolvingDependency dependency = Mock()
-        Task task = Mock()
-        TaskDependency taskDep = Mock()
-        def config = conf("conf")
-
-        given:
-        config.dependencies.add(dependency)
-
-        when:
-        def depTaskDeps = config.incoming.dependencies.buildDependencies.getDependencies(null)
-        def fileTaskDeps = config.incoming.files.buildDependencies.getDependencies(null)
-
-        then:
-        depTaskDeps == [task] as Set
-        fileTaskDeps == [task] as Set
-        _ * dependency.buildDependencies >> taskDep
-        _ * taskDep.getDependencies(_) >> ([task] as Set)
-        0 * _._
-    }
-
-    def "notifies beforeResolve action on incoming dependencies set when dependencies are resolved"() {
-        Action<ResolvableDependencies> action = Mock()
-        def config = conf("conf")
-
-        given:
-        config.incoming.beforeResolve(action)
-
-        when:
-        config.resolvedConfiguration
-
-        then:
-        interaction { resolvedConfiguration(config) }
-        1 * action.execute(config.incoming)
-    }
-
-    def "calls beforeResolve closure on incoming dependencies set when dependencies are resolved"() {
-        def config = conf("conf")
-        resolvedConfiguration(config)
-        def called = false
-
-        expect:
-        config.incoming.afterResolve {
-            assert it == config.incoming
-            called = true
-        }
-
-        when:
-        config.resolvedConfiguration
-
-        then:
-        called
-    }
-
-    def "notifies afterResolve action on incoming dependencies set when dependencies are resolved"() {
-        Action<ResolvableDependencies> action = Mock()
-        def config = conf("conf")
-
-        given:
-        config.incoming.afterResolve(action)
-
-        when:
-        config.resolvedConfiguration
-
-        then:
-        interaction { resolvedConfiguration(config) }
-        1 * action.execute(config.incoming)
-
-    }
-
-    def "calls afterResolve closure on incoming dependencies set when dependencies are resolved"() {
-        def config = conf("conf")
-        resolvedConfiguration(config)
-        def called = false
-
-        expect:
-        config.incoming.afterResolve {
-            assert it == config.incoming
-            called = true
-        }
-
-        when:
-        config.resolvedConfiguration
-
-        then:
-        called
-    }
-    
-    def "a recursive copy of a configuration includes inherited exclude rules"() {
-        given:
-        def (p1, p2, child) = [conf("p1"), conf("p2"), conf("child")]
-        child.extendsFrom p1, p2
-        
-        and:
-        def (p1Exclude, p2Exclude) = [[group: 'p1', module: 'p1'], [group: 'p2', module: 'p2']]
-        p1.exclude p1Exclude
-        p2.exclude p2Exclude
-        
-        when:
-        def copied = child.copyRecursive()
-        
-        then:
-        copied.excludeRules.size() == 2
-        copied.excludeRules.collect{[group: it.group, module: it.module]}.sort { it.group } == [p1Exclude, p2Exclude]
-    }
-
-    def "copied configuration has own instance of resolution strategy"() {
-        def strategy = Mock(ResolutionStrategyInternal)
-        def conf = conf()
-
-        when:
-        def copy = conf.copy()
-
-        then:
-        1 * resolutionStrategy.copy() >> strategy
-        conf.resolutionStrategy != copy.resolutionStrategy
-        copy.resolutionStrategy == strategy
-    }
-
-    def "provides resolution result"() {
-        def config = conf("conf")
-        def result = Mock(ResolutionResult)
-        def resolverResults = new ResolverResults()
-        resolverResults.resolved(Mock(ResolvedConfiguration), result)
-
-        when:
-        def out = config.incoming.resolutionResult
-
-        then:
-        1 * resolver.resolve(config) >> resolverResults
-        out == result
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index 76d3270..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
+++ /dev/null
@@ -1,965 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
-
-import groovy.lang.Closure;
-import org.gradle.api.GradleException;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.result.ResolutionResult;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.artifacts.*;
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy;
-import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
-import org.gradle.util.*;
-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 java.util.*;
-
-import static org.gradle.util.Matchers.hasSameItems;
-import static org.gradle.util.Matchers.isEmpty;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class DefaultConfigurationTest {
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-    private ConfigurationResolver dependencyResolver = context.mock(ConfigurationResolver.class);
-    private ConfigurationsProvider configurationContainer;
-    private ListenerManager listenerManager = context.mock(ListenerManager.class);
-    private DependencyMetaDataProvider metaDataProvider = context.mock(DependencyMetaDataProvider.class);
-    private DefaultConfiguration configuration;
-    private DependencyResolutionListener dependencyResolutionBroadcast = context.mock(DependencyResolutionListener.class);
-    private ListenerBroadcast resolutionListenerBroadcast = context.mock(ListenerBroadcast.class); 
-
-    @Before
-    public void setUp() {
-        configurationContainer = context.mock(ConfigurationsProvider.class);
-        context.checking(new Expectations(){{
-            allowing(listenerManager).createAnonymousBroadcaster(DependencyResolutionListener.class);
-            will(returnValue(resolutionListenerBroadcast));
-            allowing(resolutionListenerBroadcast).getSource();
-            will(returnValue(dependencyResolutionBroadcast));
-            allowing(dependencyResolutionBroadcast).afterResolve(with(any(ResolvableDependencies.class)));
-            allowing(dependencyResolutionBroadcast).beforeResolve(with(any(ResolvableDependencies.class)));
-            will(returnValue(null));
-        }});
-        configuration = createNamedConfiguration("path", "name");
-    }
-
-    @Test
-    public void defaultValues() {
-        assertThat(configuration.getName(), equalTo("name"));
-        assertThat(configuration.isVisible(), equalTo(true));
-        assertThat(configuration.getExtendsFrom().size(), equalTo(0));
-        assertThat(configuration.isTransitive(), equalTo(true));
-        assertThat(configuration.getDescription(), nullValue());
-        assertThat(configuration.getState(), equalTo(Configuration.State.UNRESOLVED));
-        assertThat(configuration.getDisplayName(), equalTo("configuration 'path'"));
-    }
-
-    @Test
-    public void hasUsefulDisplayName() {
-        assertThat(configuration.getDisplayName(), equalTo("configuration 'path'"));
-        assertThat(configuration.toString(), equalTo("configuration 'path'"));
-        assertThat(configuration.getIncoming().toString(), equalTo("dependencies 'path'"));
-    }
-
-    @Test
-    public void withPrivateVisibility() {
-        configuration.setVisible(false);
-        assertFalse(configuration.isVisible());
-    }
-
-    @Test
-    public void withIntransitive() {
-        configuration.setTransitive(false);
-        assertFalse(configuration.isTransitive());
-    }
-
-    @Test
-    public void exclude() {
-        Map<String, String> excludeArgs1 = toMap("group", "aGroup");
-        Map<String, String> excludeArgs2 = toMap("module", "aModule");
-        assertThat(configuration.exclude(excludeArgs1), sameInstance(configuration));
-        configuration.exclude(excludeArgs2);
-        assertThat(configuration.getExcludeRules(), equalTo(WrapUtil.<ExcludeRule>toSet(
-                new DefaultExcludeRule("aGroup", null), new DefaultExcludeRule(null, "aModule"))));
-    }
-
-    @Test
-    public void setExclude() {
-        Set<ExcludeRule> excludeRules = WrapUtil.<ExcludeRule>toSet(new DefaultExcludeRule("groupValue", null));
-        configuration.setExcludeRules(excludeRules);
-        assertThat(configuration.getExcludeRules(), equalTo(excludeRules));
-    }
-
-    @Test
-    public void withDescription() {
-        configuration.setDescription("description");
-        assertThat(configuration.getDescription(), equalTo("description"));
-    }
-
-    @Test
-    public void extendsOtherConfigurations() {
-        Configuration configuration1 = createNamedConfiguration("otherConf1");
-        configuration.extendsFrom(configuration1);
-        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1)));
-
-        Configuration configuration2 = createNamedConfiguration("otherConf2");
-        configuration.extendsFrom(configuration2);
-        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1, configuration2)));
-    }
-
-    @Test
-    public void setExtendsFrom() {
-        Configuration configuration1 = createNamedConfiguration("otherConf1");
-
-        configuration.setExtendsFrom(toSet(configuration1));
-        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1)));
-
-        Configuration configuration2 = createNamedConfiguration("otherConf2");
-        configuration.setExtendsFrom(toSet(configuration2));
-        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration2)));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void extendsFromWithDirectCycleShouldThrowInvalidUserDataEx() {
-        Configuration otherConfiguration = createNamedConfiguration("otherConf");
-        otherConfiguration.extendsFrom(configuration);
-        configuration.extendsFrom(otherConfiguration);
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void extendsFromWithIndirectCycleShouldThrowInvalidUserDataEx() {
-        Configuration otherConfiguration1 = createNamedConfiguration("otherConf1");
-        Configuration otherConfiguration2 = createNamedConfiguration("otherConf2");
-        configuration.extendsFrom(otherConfiguration1);
-        otherConfiguration1.extendsFrom(otherConfiguration2);
-        otherConfiguration2.extendsFrom(configuration);
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void setExtendsFromWithCycleShouldThrowInvalidUserDataEx() {
-        Configuration otherConfiguration = createNamedConfiguration("otherConf");
-        otherConfiguration.extendsFrom(configuration);
-        configuration.setExtendsFrom(toSet(otherConfiguration));
-    }
-
-    @Test
-    public void getHierarchy() {
-        Configuration root1 = createNamedConfiguration("root1");
-        Configuration middle1 = createNamedConfiguration("middle1").extendsFrom(root1);
-        Configuration root2 = createNamedConfiguration("root2");
-        Configuration middle2 = createNamedConfiguration("middle2").extendsFrom(root1, root2);
-        createNamedConfiguration("root3");
-        Configuration leaf = createNamedConfiguration("leaf1").extendsFrom(middle1, middle2);
-        Set<Configuration> hierarchy = leaf.getHierarchy();
-        assertThat(hierarchy.size(), equalTo(5));
-        assertThat(hierarchy.iterator().next(), equalTo(leaf));
-        assertBothExistsAndOneIsBeforeOther(hierarchy, middle1, root1);
-        assertBothExistsAndOneIsBeforeOther(hierarchy, middle2, root2);
-    }
-
-    private void assertBothExistsAndOneIsBeforeOther(Set<Configuration> hierarchy, Configuration beforeConf, Configuration afterConf) {
-        assertThat(hierarchy, hasItem(beforeConf));
-        assertThat(hierarchy, hasItem(afterConf));
-
-        boolean foundBeforeConf = false;
-        for (Configuration configuration : hierarchy) {
-            if (configuration.equals(beforeConf)) {
-                foundBeforeConf = true;
-            }
-            if (configuration.equals(afterConf)) {
-                assertThat(foundBeforeConf, equalTo(true));
-            }
-        }
-    }
-
-    @Test
-    public void getAll() {
-        final Configuration conf1 = createNamedConfiguration("testConf1");
-        final Configuration conf2 = createNamedConfiguration("testConf2");
-        context.checking(new Expectations(){{
-            one(configurationContainer).getAll();
-            will(returnValue(toSet(conf1, conf2)));
-        }});
-        assertThat(configuration.getAll(), equalTo(toSet(conf1, conf2)));
-    }
-
-    @Test(expected = GradleException.class)
-    public void getAsPathShouldRethrownFailure() {
-        prepareForResolveWithErrors();
-        configuration.resolve();
-    }
-
-    @Test
-    public void resolve() {
-        final Set<File> fileSet = toSet(new File("somePath"));
-        makeResolveReturnFileSet(fileSet);
-        assertThat(configuration.resolve(), equalTo(fileSet));
-        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
-    }
-
-    @Test
-    public void filesWithDependencies() {
-        final Set<File> fileSet = toSet(new File("somePath"));
-        prepareForFilesBySpec(fileSet);
-        assertThat(configuration.files(context.mock(Dependency.class)), equalTo(fileSet));
-        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
-    }
-
-    @Test
-    public void fileCollectionWithDependencies() {
-        Dependency dependency1 = createDependency("group1", "name", "version");
-        Dependency dependency2 = createDependency("group2", "name", "version");
-        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
-                configuration.fileCollection(dependency1);
-        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(dependency1),
-                equalTo(true));
-        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(dependency2),
-                equalTo(false));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void filesWithSpec() {
-        final Set<File> fileSet = toSet(new File("somePath"));
-        prepareForFilesBySpec(fileSet);
-        assertThat(configuration.files(context.mock(Spec.class)), equalTo(fileSet));
-        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
-    }
-
-    @Test
-    public void fileCollectionWithSpec() {
-        @SuppressWarnings("unchecked")
-        Spec<Dependency> spec = context.mock(Spec.class);
-        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
-                configuration.fileCollection(spec);
-        assertThat(fileCollection.getDependencySpec(), sameInstance((Object)spec));
-    }
-
-    @Test
-    public void filesWithClosureSpec() {
-        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
-        final Set<File> fileSet = toSet(new File("somePath"));
-        prepareForFilesBySpec(fileSet);
-        assertThat(configuration.files(closure), equalTo(fileSet));
-        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
-    }
-
-    @Test
-    public void fileCollectionWithClosureSpec() {
-        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
-        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
-                configuration.fileCollection(closure);
-        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(createDependency("group1", "name", "version")),
-                equalTo(true));
-        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(createDependency("group2", "name", "version")),
-                equalTo(false));
-    }
-
-    @SuppressWarnings("unchecked")
-    private void prepareForFilesBySpec(final Set<File> fileSet) {
-        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
-        prepareResolve(resolvedConfiguration, false);
-        context.checking(new Expectations() {{
-            one(resolvedConfiguration).getFiles(with(any(Spec.class)));
-            will(returnValue(fileSet));
-        }});
-    }
-
-    @Test(expected = GradleException.class)
-    public void resolveShouldRethrowFailure() {
-        prepareForResolveWithErrors();
-        configuration.resolve();
-    }
-
-    private void prepareForResolveWithErrors() {
-        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
-        prepareResolve(resolvedConfiguration, true);
-        context.checking(new Expectations(){{
-            one(resolvedConfiguration).rethrowFailure();
-            will(throwException(new GradleException()));
-        }});
-    }
-
-    private void makeResolveReturnFileSet(final Set<File> fileSet) {
-        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
-        context.checking(new Expectations() {{
-            prepareResolve(resolvedConfiguration, false);
-            allowing(resolvedConfiguration).getFiles(Specs.SATISFIES_ALL);
-            will(returnValue(fileSet));
-        }});
-    }
-
-    @Test
-    public void resolveSuccessfullyAsResolvedConfiguration() {
-        ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
-        prepareResolve(resolvedConfiguration, false);
-        assertThat(configuration.getResolvedConfiguration(), equalTo(resolvedConfiguration));
-        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
-    }
-
-    private void prepareResolve(final ResolvedConfiguration resolvedConfiguration, final boolean withErrors) {
-        context.checking(new Expectations() {{
-            ResolverResults result = new ResolverResults();
-            result.resolved(resolvedConfiguration, context.mock(ResolutionResult.class));
-            allowing(dependencyResolver).resolve(configuration);
-            will(returnValue(result));
-            allowing(resolvedConfiguration).hasError();
-            will(returnValue(withErrors));
-        }});
-    }
-
-    @Test
-    public void multipleResolvesShouldUseCachedResult() {
-        prepareResolve(context.mock(ResolvedConfiguration.class), true);
-        assertThat(configuration.getResolvedConfiguration(), sameInstance(configuration.getResolvedConfiguration()));
-    }
-
-    @Test
-    public void uploadTaskName() {
-        assertThat(configuration.getUploadTaskName(), equalTo("uploadName"));
-    }
-
-    private DefaultConfiguration createNamedConfiguration(String confName) {
-        return new DefaultConfiguration(confName, confName, configurationContainer,
-                dependencyResolver, listenerManager, metaDataProvider, new DefaultResolutionStrategy());
-    }
-    
-    private DefaultConfiguration createNamedConfiguration(String path, String confName) {
-        return new DefaultConfiguration(path, confName, configurationContainer,
-                dependencyResolver, listenerManager, metaDataProvider, new DefaultResolutionStrategy());
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void buildArtifacts() {
-        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
-        final Task artifactTaskMock = context.mock(Task.class, "artifactTask");
-        final Configuration otherConfiguration = context.mock(Configuration.class);
-        final TaskDependency otherArtifactTaskDependencyMock = context.mock(TaskDependency.class, "otherConfTaskDep");
-        final PublishArtifact otherArtifact = context.mock(PublishArtifact.class, "otherArtifact");
-        final PublishArtifactSet inheritedArtifacts = new DefaultPublishArtifactSet("artifacts", toDomainObjectSet(PublishArtifact.class, otherArtifact));
-        DefaultPublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-        artifact.builtBy(artifactTaskMock);
-        configuration.getArtifacts().add(artifact);
-
-        context.checking(new Expectations() {{
-            allowing(otherConfiguration).getHierarchy();
-            will(returnValue(toSet()));
-
-            allowing(otherConfiguration).getAllArtifacts();
-            will(returnValue(inheritedArtifacts));
-
-            allowing(otherConfiguration).getAllDependencies();
-
-            allowing(otherArtifact).getBuildDependencies();
-            will(returnValue(otherArtifactTaskDependencyMock));
-            
-            allowing(otherArtifactTaskDependencyMock).getDependencies(with(any(Task.class)));
-            will(returnValue(toSet(otherConfTaskMock)));
-        }});
-        configuration.setExtendsFrom(toSet(otherConfiguration));
-        assertThat((Set<Task>) configuration.getAllArtifacts().getBuildDependencies().getDependencies(context.mock(Task.class, "caller")),
-                equalTo(toSet(artifactTaskMock, otherConfTaskMock)));
-    }
-
-    @Test
-    public void getAllArtifactFiles() {
-        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
-        final Task artifactTaskMock = context.mock(Task.class, "artifactTask");
-        final Configuration otherConfiguration = context.mock(Configuration.class);
-        final TaskDependency otherConfTaskDependencyMock = context.mock(TaskDependency.class, "otherConfTaskDep");
-        final TaskDependency artifactTaskDependencyMock = context.mock(TaskDependency.class, "artifactTaskDep");
-        final File artifactFile1 = new File("artifact1");
-        final File artifactFile2 = new File("artifact2");
-        final PublishArtifact artifact = context.mock(PublishArtifact.class, "artifact");
-        final PublishArtifact otherArtifact = context.mock(PublishArtifact.class, "otherArtifact");
-        final PublishArtifactSet otherArtifacts = new DefaultPublishArtifactSet("artifacts", toDomainObjectSet(PublishArtifact.class, otherArtifact));
-
-        context.checking(new Expectations() {{
-            allowing(otherConfiguration).getHierarchy();
-            will(returnValue(toSet()));
-
-            allowing(otherConfiguration).getAllArtifacts();
-            will(returnValue(otherArtifacts));
-
-            allowing(otherConfiguration).getAllDependencies();
-
-            allowing(otherConfiguration).getExtendsFrom();
-            will(returnValue(toSet()));
-
-            allowing(otherConfiguration).getArtifacts();
-            will(returnValue(toSet(otherArtifact)));
-
-            allowing(otherConfTaskDependencyMock).getDependencies(with(any(Task.class)));
-            will(returnValue(toSet(otherConfTaskMock)));
-
-            allowing(artifactTaskDependencyMock).getDependencies(with(any(Task.class)));
-            will(returnValue(toSet(artifactTaskMock)));
-
-            allowing(artifact).getFile();
-            will(returnValue(artifactFile1));
-
-            allowing(otherArtifact).getFile();
-            will(returnValue(artifactFile2));
-
-            allowing(artifact).getBuildDependencies();
-            will(returnValue(artifactTaskDependencyMock));
-
-            allowing(otherArtifact).getBuildDependencies();
-            will(returnValue(otherConfTaskDependencyMock));
-        }});
-
-        configuration.getArtifacts().add(artifact);
-        configuration.setExtendsFrom(toSet(otherConfiguration));
-
-        FileCollection files = configuration.getAllArtifacts().getFiles();
-        assertThat(files.getFiles(), equalTo(toSet(artifactFile1, artifactFile2)));
-        assertThat(files.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(otherConfTaskMock, artifactTaskMock)));
-    }
-
-    @Test
-    public void buildDependenciesDelegatesToAllSelfResolvingDependencies() {
-        final Task target = context.mock(Task.class, "target");
-        final Task projectDepTaskDummy = context.mock(Task.class, "projectDepTask");
-        final Task fileDepTaskDummy = context.mock(Task.class, "fileDepTask");
-        final ProjectDependency projectDependencyStub = context.mock(ProjectDependency.class);
-        final FileCollectionDependency fileCollectionDependencyStub = context.mock(FileCollectionDependency.class);
-
-        context.checking(new Expectations() {{
-            TaskDependency projectTaskDependencyDummy = context.mock(TaskDependency.class, "projectDep");
-            TaskDependency fileTaskDependencyStub = context.mock(TaskDependency.class, "fileDep");
-
-            allowing(projectDependencyStub).getBuildDependencies();
-            will(returnValue(projectTaskDependencyDummy));
-
-            allowing(projectTaskDependencyDummy).getDependencies(target);
-            will(returnValue(toSet(projectDepTaskDummy)));
-
-            allowing(fileCollectionDependencyStub).getBuildDependencies();
-            will(returnValue(fileTaskDependencyStub));
-
-            allowing(fileTaskDependencyStub).getDependencies(target);
-            will(returnValue(toSet(fileDepTaskDummy)));
-        }});
-
-        configuration.getDependencies().add(projectDependencyStub);
-        configuration.getDependencies().add(fileCollectionDependencyStub);
-
-        assertThat(configuration.getBuildDependencies().getDependencies(target), equalTo((Set) toSet(fileDepTaskDummy,
-                projectDepTaskDummy)));
-    }
-
-    @Test
-    public void buildDependenciesDelegatesToInheritedConfigurations() {
-        final Task target = context.mock(Task.class, "target");
-        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
-        final TaskDependency dependencyTaskDependencyStub = context.mock(TaskDependency.class, "otherConfTaskDep");
-        final Configuration otherConfiguration = context.mock(Configuration.class, "otherConf");
-        final FileCollectionDependency fileCollectionDependencyStub = context.mock(FileCollectionDependency.class);
-        final DependencySet inherited = new DefaultDependencySet("dependencies", toDomainObjectSet(Dependency.class, fileCollectionDependencyStub));
-
-        context.checking(new Expectations() {{
-            allowing(otherConfiguration).getHierarchy();
-            will(returnValue(toSet()));
-
-            allowing(otherConfiguration).getAllArtifacts();
-
-            allowing(otherConfiguration).getAllDependencies();
-            will(returnValue(inherited));
-
-            allowing(fileCollectionDependencyStub).getBuildDependencies();
-            will(returnValue(dependencyTaskDependencyStub));
-
-            allowing(dependencyTaskDependencyStub).getDependencies(target);
-            will(returnValue(toSet(otherConfTaskMock)));
-        }});
-
-        configuration.extendsFrom(otherConfiguration);
-
-        assertThat(configuration.getBuildDependencies().getDependencies(target), equalTo((Set) toSet(otherConfTaskMock)));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test public void taskDependencyFromProjectDependencyUsingNeeded() {
-        Configuration superConfig = createNamedConfiguration("superConf");
-        configuration.extendsFrom(superConfig);
-
-        final ProjectDependency projectDependencyStub = context.mock(ProjectDependency.class);
-        superConfig.getDependencies().add(projectDependencyStub);
-
-        final Project projectStub = context.mock(Project.class);
-        final TaskContainer taskContainerStub = context.mock(TaskContainer.class);
-        final Task taskStub = context.mock(Task.class);
-        final String taskName = "testit";
-
-        context.checking(new Expectations() {{
-            allowing(projectDependencyStub).getDependencyProject(); will(returnValue(projectStub));
-            allowing(projectStub).getTasks(); will(returnValue(taskContainerStub));
-            allowing(taskContainerStub).findByName(taskName); will(returnValue(taskStub));
-        }});
-
-        TaskDependency td = configuration.getTaskDependencyFromProjectDependency(true, taskName);
-        Task unusedTask = context.mock(Task.class, "unused");
-
-        assertThat((Set<Task>) td.getDependencies(unusedTask), equalTo(toSet(taskStub)));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test public void taskDependencyFromProjectDependencyUsingDependents() {
-        final String configName = configuration.getName();
-        final String taskName = "testit";
-        final Task tdTask = context.mock(Task.class, "tdTask");
-        final Project taskProject = context.mock(Project.class, "taskProject");
-        final Project rootProject = context.mock(Project.class, "rootProject");
-        final Project dependentProject = context.mock(Project.class, "dependentProject");
-        final Task desiredTask = context.mock(Task.class, "desiredTask");
-        final Set<Task> taskSet = toSet(desiredTask);
-        final ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class);
-        final Configuration dependentConfig = context.mock(Configuration.class);
-        final ProjectDependency projectDependency = context.mock(ProjectDependency.class);
-        final Set<ProjectDependency> projectDependencies = toDomainObjectSet(ProjectDependency.class, projectDependency);
-        final DependencySet otherDependencies = context.mock(DependencySet.class);
-
-        context.checking(new Expectations() {{
-            allowing(tdTask).getProject(); will(returnValue(taskProject));
-            allowing(taskProject).getRootProject(); will(returnValue(rootProject));
-            allowing(rootProject).getTasksByName(taskName, true); will(returnValue(taskSet));
-            allowing(desiredTask).getProject(); will(returnValue(dependentProject));
-            allowing(dependentProject).getConfigurations(); will(returnValue(configurationContainer));
-            allowing(configurationContainer).findByName(configName); will(returnValue(dependentConfig));
-
-            allowing(dependentConfig).getAllDependencies(); will(returnValue(otherDependencies));
-            allowing(otherDependencies).withType(ProjectDependency.class); will(returnValue(projectDependencies));
-            allowing(projectDependency).getDependencyProject(); will(returnValue(taskProject));
-        }});
-
-        TaskDependency td = configuration.getTaskDependencyFromProjectDependency(false, taskName);
-        assertThat((Set<Task>) td.getDependencies(tdTask), equalTo(toSet(desiredTask)));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test public void taskDependencyFromProjectDependencyWithoutCommonConfiguration() {
-        // This test exists because a NullPointerException was thrown by
-        // getTaskDependencyFromProjectDependency() if the rootProject
-        // defined a task as the same name as a subproject's task, but did
-        // not define the same configuration.
-        final String configName = configuration.getName();
-        final String taskName = "testit";
-        final Task tdTask = context.mock(Task.class, "tdTask");
-        final Project taskProject = context.mock(Project.class, "taskProject");
-        final Project rootProject = context.mock(Project.class, "rootProject");
-        final Project dependentProject = context.mock(Project.class, "dependentProject");
-        final Task desiredTask = context.mock(Task.class, "desiredTask");
-        final Set<Task> taskSet = toSet(desiredTask);
-        final ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class);
-
-        context.checking(new Expectations() {{
-            allowing(tdTask).getProject(); will(returnValue(taskProject));
-            allowing(taskProject).getRootProject(); will(returnValue(rootProject));
-            allowing(rootProject).getTasksByName(taskName, true); will(returnValue(taskSet));
-            allowing(desiredTask).getProject(); will(returnValue(dependentProject));
-            allowing(dependentProject).getConfigurations(); will(returnValue(configurationContainer));
-
-            // return null to mock not finding the given configuration
-            allowing(configurationContainer).findByName(configName); will(returnValue(null));
-        }});
-
-        TaskDependency td = configuration.getTaskDependencyFromProjectDependency(false, taskName);
-        assertThat(td.getDependencies(tdTask), equalTo(Collections.EMPTY_SET));
-    }
-
-
-    @Test
-    public void getDependencies() {
-        Dependency dependency = context.mock(Dependency.class);
-        configuration.getDependencies().add(dependency);
-        assertThat(configuration.getDependencies(), hasSameItems(toSet(dependency)));
-    }
-
-    @Test
-    public void getTypedDependencies() {
-        ProjectDependency projectDependency = context.mock(ProjectDependency.class);
-        configuration.getDependencies().add(context.mock(Dependency.class));
-        configuration.getDependencies().add(projectDependency);
-        assertThat(configuration.getDependencies().withType(ProjectDependency.class), hasSameItems(toSet(projectDependency)));
-    }
-
-    @Test
-    public void getTypedDependenciesReturnsEmptySetWhenNoMatches() {
-        configuration.getDependencies().add(context.mock(Dependency.class));
-        assertThat(configuration.getDependencies().withType(ProjectDependency.class), isEmpty());
-    }
-
-    @Test
-    public void getAllDependencies() {
-        Dependency dependencyConf = createDependency("group1", "name1", "version1");
-        Dependency dependencyOtherConf1 = createDependency("group1", "name1", "version1");
-        Dependency dependencyOtherConf2 = context.mock(Dependency.class, "dep2");
-        Configuration otherConf = createNamedConfiguration("otherConf");
-        configuration.getDependencies().add(dependencyConf);
-        configuration.extendsFrom(otherConf);
-        otherConf.getDependencies().add(dependencyOtherConf1);
-        otherConf.getDependencies().add(dependencyOtherConf2);
-
-        assertThat(configuration.getAllDependencies(), hasSameItems(toLinkedSet(dependencyConf, dependencyOtherConf2)));
-        assertCorrectInstanceInAllDependencies(configuration.getAllDependencies(), dependencyConf);
-    }
-
-    @Test
-    public void getAllTypedDependencies() {
-        ProjectDependency projectDependencyCurrentConf = context.mock(ProjectDependency.class, "projectDepCurrentConf");
-        configuration.getDependencies().add(context.mock(Dependency.class, "depCurrentConf"));
-        configuration.getDependencies().add(projectDependencyCurrentConf);
-        Configuration otherConf = createNamedConfiguration("otherConf");
-        configuration.extendsFrom(otherConf);
-        ProjectDependency projectDependencyExtendedConf = context.mock(ProjectDependency.class, "projectDepExtendedConf");
-        otherConf.getDependencies().add(context.mock(Dependency.class, "depExtendedConf"));
-        otherConf.getDependencies().add(projectDependencyExtendedConf);
-
-        assertThat(configuration.getAllDependencies().withType(ProjectDependency.class), hasSameItems(toLinkedSet(projectDependencyCurrentConf, projectDependencyExtendedConf)));
-    }
-
-    @Test
-    public void getAllTypedDependenciesReturnsEmptySetWhenNoMatches() {
-        configuration.getDependencies().add(context.mock(Dependency.class, "depCurrentConf"));
-        Configuration otherConf = createNamedConfiguration("otherConf");
-        configuration.extendsFrom(otherConf);
-        otherConf.getDependencies().add(context.mock(Dependency.class, "depExtendedConf"));
-
-        assertThat(configuration.getAllDependencies().withType(ProjectDependency.class), isEmpty());
-    }
-
-    @Test
-    public void getAllArtifacts() {
-        PublishArtifact artifactConf = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-        PublishArtifact artifactOtherConf2 = createPublishArtifact("name2", "ext2", "type2", "classifier2");
-        Configuration otherConf = createNamedConfiguration("otherConf");
-        configuration.getArtifacts().add(artifactConf);
-        configuration.extendsFrom(otherConf);
-        otherConf.getArtifacts().add(artifactOtherConf2);
-        assertThat(configuration.getAllArtifacts(), hasSameItems(toLinkedSet(artifactConf, artifactOtherConf2)));
-    }
-
-    @Test
-    public void artifactAddedAction() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        final PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-
-        context.checking(new Expectations() {{
-            one(closure).call(artifact);
-        }});
-
-        configuration.getArtifacts().whenObjectAdded(TestUtil.toClosure(closure));
-        configuration.getArtifacts().add(artifact);
-    }
-
-    @Test
-    public void artifactRemovedAction() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        final PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-
-        configuration.getArtifacts().add(artifact);
-
-        context.checking(new Expectations() {{
-            one(closure).call(artifact);
-        }});
-
-        configuration.getArtifacts().whenObjectRemoved(TestUtil.toClosure(closure));
-
-        configuration.getArtifacts().remove(artifact);
-    }
-
-    @Test
-    public void removeArtifact() {
-        PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-        configuration.getArtifacts().add(artifact);
-        configuration.getArtifacts().remove(artifact);
-        assertThat(configuration.getAllArtifacts(), Matchers.isEmpty());
-    }
-
-    @Test
-    public void removeArtifactWithUnknownArtifact() {
-        PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
-        configuration.getArtifacts().add(artifact);
-        configuration.getArtifacts().remove(createPublishArtifact("name2", "ext1", "type1", "classifier1"));
-        assertThat(configuration.getAllArtifacts(), hasSameItems(toSet(artifact)));
-    }
-
-    private void assertCorrectInstanceInAllDependencies(Set<Dependency> allDependencies, Dependency correctInstance) {
-        for (Dependency dependency : allDependencies) {
-            if (dependency == correctInstance) {
-                return;
-            }
-        }
-        fail("Correct instance is missing!");
-    }
-
-    @Test
-    public void copy() {
-        prepareConfigurationForCopyTest();
-
-        Configuration copiedConfiguration = configuration.copy();
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, configuration.getDependencies());
-    }
-
-    @Test
-    public void copyWithSpec() {
-        prepareConfigurationForCopyTest();
-        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getDependencies());
-        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
-
-        Configuration copiedConfiguration = configuration.copy(new Spec<Dependency>() {
-            public boolean isSatisfiedBy(Dependency element) {
-                return !element.getGroup().equals("group3");
-            }
-        });
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
-    }
-
-    @Test
-    public void copyWithClosure() {
-        prepareConfigurationForCopyTest();
-        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getDependencies());
-        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
-
-        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
-        Configuration copiedConfiguration = configuration.copy(specClosure);
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
-    }
-
-    private void prepareConfigurationForCopyTest() {
-        configuration.setVisible(false);
-        configuration.setTransitive(false);
-        configuration.setDescription("descript");
-        configuration.exclude(toMap("group", "value"));
-        configuration.exclude(toMap("group", "value2"));
-        configuration.getArtifacts().add(createPublishArtifact("name1", "ext1", "type1", "classifier1"));
-        configuration.getArtifacts().add(createPublishArtifact("name2", "ext2", "type2", "classifier2"));
-        configuration.getDependencies().add(createDependency("group1", "name1", "version1"));
-        configuration.getDependencies().add(createDependency("group2", "name2", "version2"));
-    }
-
-    private void assertThatCopiedConfigurationHasElementsAndName(Configuration copiedConfiguration, Set<Dependency> expectedDependencies) {
-        assertThat(copiedConfiguration.getName(), equalTo(configuration.getName() + "Copy"));
-        assertThat(copiedConfiguration.isVisible(), equalTo(configuration.isVisible()));
-        assertThat(copiedConfiguration.isTransitive(), equalTo(configuration.isTransitive()));
-        assertThat(copiedConfiguration.getDescription(), equalTo(configuration.getDescription()));
-        assertThat(asSet(copiedConfiguration.getAllArtifacts()), equalTo(asSet(configuration.getAllArtifacts())));
-        assertThat(copiedConfiguration.getExcludeRules(), equalTo(configuration.getExcludeRules()));
-        assertThat(copiedConfiguration.getExcludeRules().iterator().next(), not(sameInstance(configuration.getExcludeRules().iterator().next())));
-        assertThat(WrapUtil.asSet(copiedConfiguration.getDependencies()), equalTo(WrapUtil.asSet(expectedDependencies)));
-        assertNotSameInstances(copiedConfiguration.getDependencies(), expectedDependencies);
-    }
-
-    @Test
-    public void copyRecursive() {
-        prepareConfigurationForCopyRecursiveTest();
-
-        Configuration copiedConfiguration = configuration.copyRecursive();
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, configuration.getAllDependencies());
-    }
-
-    @Test
-    public void copyRecursiveWithSpec() {
-        prepareConfigurationForCopyRecursiveTest();
-        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getAllDependencies());
-        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
-
-        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
-        Configuration copiedConfiguration = configuration.copyRecursive(specClosure);
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
-    }
-
-    @Test
-    public void copyRecursiveWithClosure() {
-        prepareConfigurationForCopyRecursiveTest();
-        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getAllDependencies());
-        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
-
-        Configuration copiedConfiguration = configuration.copyRecursive(new Spec<Dependency>() {
-            public boolean isSatisfiedBy(Dependency element) {
-                return !element.getGroup().equals("group3");
-            }
-        });
-
-        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
-    }
-
-    private void prepareConfigurationForCopyRecursiveTest() {
-        prepareConfigurationForCopyTest();
-        Dependency similarDependency2InOtherConf = createDependency("group2", "name2", "version2");
-        Dependency otherConfDependency = createDependency("group4", "name4", "version4");
-        Configuration otherConf = createNamedConfiguration("otherConf");
-        otherConf.getDependencies().add(similarDependency2InOtherConf);
-        otherConf.getDependencies().add(otherConfDependency);
-        configuration.extendsFrom(otherConf);
-    }
-
-    private void assertNotSameInstances(Set<Dependency> dependencies, Set<Dependency> otherDependencies) {
-        for (Dependency dependency : dependencies) {
-            assertHasEqualButNotSameInstance(dependency, otherDependencies);
-        }
-    }
-
-    private void assertHasEqualButNotSameInstance(Dependency dependency, Set<Dependency> otherDependencies) {
-        assertThat(otherDependencies, hasItem(dependency));
-        for (Dependency otherDependency : otherDependencies) {
-            if (otherDependency.equals(dependency)) {
-                assertThat(otherDependency, not(sameInstance(dependency)));
-            }
-        }
-    }
-
-    @Test
-    public void propertyChangeWithNonUnresolvedStateShouldThrowEx() {
-        makeResolveReturnFileSet(new HashSet<File>());
-        configuration.resolve();
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.setTransitive(true);
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.setDescription("someDesc");
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.setExcludeRules(new HashSet<ExcludeRule>());
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.setExtendsFrom(new HashSet<Configuration>());
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.setVisible(true);
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.getDependencies().add(context.mock(Dependency.class));
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.getDependencies().add(context.mock(Dependency.class));
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.exclude(new HashMap<String, String>());
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.extendsFrom(context.mock(Configuration.class));
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.getArtifacts().add(context.mock(PublishArtifact.class));
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.getArtifacts().remove(context.mock(PublishArtifact.class, "removeArtifact"));
-            }
-        });
-        assertInvalidUserDataException(new Executer() {
-            public void execute() {
-                configuration.getArtifacts().add(context.mock(PublishArtifact.class, "removeArtifact"));
-            }
-        });
-    }
-    
-    @Test
-    public void dumpString() {
-        Dependency configurationDependency = createDependency("dumpgroup1", "dumpname1", "dumpversion1");
-        Dependency otherConfSimilarDependency = createDependency("dumpgroup1", "dumpname1", "dumpversion1");
-        Dependency otherConfDependency = createDependency("dumpgroup2", "dumpname2", "dumpversion2");
-        Configuration otherConf = createNamedConfiguration("dumpConf");
-        configuration.extendsFrom(otherConf);
-        otherConf.getDependencies().add(otherConfDependency);
-        otherConf.getDependencies().add(otherConfSimilarDependency);
-        configuration.getDependencies().add(configurationDependency);
-
-        assertThat(configuration.dump(),
-                containsString(
-                "\nConfiguration:"
-                + "  class='class org.gradle.api.internal.artifacts.configurations.DefaultConfiguration'"
-                + "  name='name'"
-                + "  hashcode='"+ configuration.hashCode() +"'"
-                + "\nLocal Dependencies:"
-                + "\n   DefaultExternalModuleDependency{group='dumpgroup1', name='dumpname1', version='dumpversion1', configuration='default'}"
-                + "\nLocal Artifacts:"
-                + "\n   none"
-                + "\nAll Dependencies:"
-                + "\n   DefaultExternalModuleDependency{group='dumpgroup1', name='dumpname1', version='dumpversion1', configuration='default'}"
-                + "\n   DefaultExternalModuleDependency{group='dumpgroup2', name='dumpname2', version='dumpversion2', configuration='default'}"
-                + "\nAll Artifacts:"
-                + "\n   none"));
-    }
-
-    ModuleDependency createDependency(String group, String name, String version) {
-        return new DefaultExternalModuleDependency(group, name, version);
-    }
-
-    DefaultPublishArtifact createPublishArtifact(String name, String extension, String type, String classifier) {
-        return new DefaultPublishArtifact(name, extension, type, classifier, new Date(), new File(""));
-    }
-
-    private void assertInvalidUserDataException(Executer executer) {
-        try {
-            executer.execute();
-            fail();
-        } catch (InvalidUserDataException e) {
-            // ignore
-        }
-    }
-
-    private static interface Executer {
-        void execute();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy
deleted file mode 100644
index 974615b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import org.gradle.internal.reflect.DirectInstantiator
-import spock.lang.Specification
-
-class DefaultComponentMetadataHandlerTest extends Specification {
-    def handler = new DefaultComponentMetadataHandler(new DirectInstantiator())
-
-    def "processing fails when status is not present in status scheme"() {
-        def metadata = Stub(MutableModuleVersionMetaData) {
-            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
-            getStatus() >> "green"
-            getStatusScheme() >> ["alpha", "beta"]
-        }
-
-        when:
-        handler.process(metadata)
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e.message == /Unexpected status 'green' specified for group:module:version. Expected one of: [alpha, beta]/
-    }
-}
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
deleted file mode 100644
index 03dbf4b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
+++ /dev/null
@@ -1,152 +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.dsl;
-
-
-import org.gradle.api.InvalidUserDataException
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.multiParser
-import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.parser
-
-public class ModuleVersionSelectorParsersTest extends Specification {
-
-    def "understands group:name:version notation"() {
-        when:
-        def v = multiParser().parseNotation("org.foo:bar:1.0") as List
-
-        then:
-        v.size() == 1
-        v[0].group == 'org.foo'
-        v[0].name  == 'bar'
-        v[0].version  == '1.0'
-    }
-
-    def "works with CharSequences"() {
-        when:
-        def sb = new StringBuilder().append("org.foo:charsequence:1.0")
-        def v = multiParser().parseNotation(sb) as List
-
-        then:
-        v.size() == 1
-        v[0].name  == 'charsequence'
-    }
-
-    def "allows exact type on input"() {
-        def id = newSelector("org.foo", "bar", "2.0")
-
-        when:
-        def v = multiParser().parseNotation(id) as List
-
-        then:
-        v.size() == 1
-        v[0].group == 'org.foo'
-        v[0].name  == 'bar'
-        v[0].version  == '2.0'
-    }
-
-    def "allows list of objects on input"() {
-        def id = newSelector("org.foo", "bar", "2.0")
-
-        when:
-        def v = multiParser().parseNotation([id, ["hey:man:1.0"], [group:'i', name:'like', version:'maps']]) as List
-
-        then:
-        v.size() == 3
-        v[0].name == 'bar'
-        v[1].name == 'man'
-        v[2].name == 'like'
-    }
-
-    def "allows map on input"() {
-        when:
-        def v = multiParser().parseNotation([group: 'org.foo', name: 'bar', version:'1.0']) as List
-
-        then:
-        v.size() == 1
-        v[0].group == 'org.foo'
-        v[0].name  == 'bar'
-        v[0].version  == '1.0'
-    }
-
-    def "fails for unknown types"() {
-        when:
-        multiParser().parseNotation(new Object())
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "reports missing keys for map notation"() {
-        when:
-        multiParser().parseNotation([name: "bar", version: "1.0"])
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "reports wrong keys for map notation"() {
-        when:
-        multiParser().parseNotation([groop: 'groop', name: "bar", version: "1.0"])
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "reports invalid format for string notation"() {
-        when:
-        multiParser().parseNotation(["blahblah"])
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "reports invalid missing data for string notation"() {
-        when:
-        multiParser().parseNotation([":foo:"])
-
-        then:
-        def ex = thrown(InvalidUserDataException)
-        ex.message.contains 'cannot be empty'
-    }
-
-    def "null is an invalid input"() {
-        when:
-        multiParser().parseNotation(null)
-
-        then:
-        thrown(InvalidUserDataException)
-
-        when:
-        parser().parseNotation(null)
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "single parser understands String notation"() {
-        //just smoke testing the single parser, it is covered in multiParser, too.
-        when:
-        def v = parser().parseNotation("org.foo:bar:1.0")
-
-        then:
-        v.group == 'org.foo'
-        v.name  == 'bar'
-        v.version  == '1.0'
-    }
-}
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
deleted file mode 100644
index 1afbfac..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactoryTest.groovy
+++ /dev/null
@@ -1,122 +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.dsl
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.Task
-import org.gradle.api.artifacts.PublishArtifact
-import org.gradle.api.internal.ThreadGlobalInstantiator
-import org.gradle.api.internal.artifacts.ModuleInternal
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
-import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
-import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
-import org.gradle.api.tasks.bundling.AbstractArchiveTask
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.typeconversion.NotationParser
-import spock.lang.Specification
-
-import java.awt.*
-
-public class PublishArtifactNotationParserFactoryTest extends Specification {
-    final DependencyMetaDataProvider provider = Mock()
-    final Instantiator instantiator = ThreadGlobalInstantiator.getOrCreate()
-    final PublishArtifactNotationParserFactory publishArtifactNotationParserFactory = new PublishArtifactNotationParserFactory(instantiator, provider)
-    final NotationParser<Object, PublishArtifact> publishArtifactNotationParser = publishArtifactNotationParserFactory.create();
-
-    def setup() {
-        ModuleInternal module = Mock()
-        _ * provider.module >> module
-        _ * module.version >> '1.2'
-    }
-
-    def createArtifactFromPublishArtifactInstance() {
-        PublishArtifact original = Mock()
-
-        when:
-        def publishArtifact = publishArtifactNotationParser.parseNotation(original)
-
-        then:
-        publishArtifact == original
-    }
-
-    def createArtifactFromArchiveTask() {
-        AbstractArchiveTask archiveTask = Mock()
-        archiveTask.getArchivePath() >> new File("")
-
-        when:
-        def publishArtifact = publishArtifactNotationParser.parseNotation(archiveTask)
-
-        then:
-        publishArtifact instanceof ArchivePublishArtifact
-        publishArtifact.archiveTask == archiveTask
-    }
-
-    def createArtifactFromFile() {
-        def file = new File("some.zip")
-
-        when:
-        def publishArtifact = publishArtifactNotationParser.parseNotation(file)
-
-        then:
-        publishArtifact instanceof DefaultPublishArtifact
-        publishArtifact.file == file
-    }
-
-    def "creates artifact from extension-less file"() {
-        def file = new File("someFile")
-
-        when:
-        def publishArtifact = publishArtifactNotationParser.parseNotation(file)
-
-        then:
-        publishArtifact instanceof DefaultPublishArtifact
-        publishArtifact.file == file
-        publishArtifact.type == ''
-    }
-
-    def createArtifactFromFileInMap() {
-        Task task = Mock()
-        def file = new File("some-file-1.2-classifier.zip")
-
-        when:
-        def publishArtifact = publishArtifactNotationParser.parseNotation(file: file, type: 'someType', builtBy: task)
-
-        then:
-        publishArtifact instanceof DefaultPublishArtifact
-        publishArtifact.file == file
-        publishArtifact.type == 'someType'
-        publishArtifact.name == 'some-file'
-        publishArtifact.extension == 'zip'
-        publishArtifact.classifier == 'classifier'
-        publishArtifact.buildDependencies.getDependencies(null) == [task] as Set
-    }
-
-    public void createArtifactWithNullNotationShouldThrowInvalidUserDataEx() {
-        when:
-        publishArtifactNotationParser.parseNotation(null)
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    public void createArtifactWithUnknownNotationShouldThrowInvalidUserDataEx() {
-        when:
-        publishArtifactNotationParser.parseNotation(new Point(1, 2))
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
deleted file mode 100644
index 037e189..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
+++ /dev/null
@@ -1,54 +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
-
-import org.gradle.util.VersionNumber
-import spock.lang.Specification
-
-class CacheLayoutTest extends Specification {
-    def "use root layout"() {
-        when:
-        CacheLayout cacheLayout = CacheLayout.ROOT
-
-        then:
-        cacheLayout.key == 'modules-2'
-        cacheLayout.version == VersionNumber.parse("2.0.0")
-        cacheLayout.formattedVersion == '2'
-        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/modules-2')
-    }
-
-    def "use file store layout"() {
-        when:
-        CacheLayout cacheLayout = CacheLayout.FILE_STORE
-
-        then:
-        cacheLayout.key == 'files-2.1'
-        cacheLayout.version == VersionNumber.parse("2.1.0")
-        cacheLayout.formattedVersion == '2.1'
-        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/files-2.1')
-    }
-
-    def "use metadata store layout"() {
-        when:
-        CacheLayout cacheLayout = CacheLayout.META_DATA
-
-        then:
-        cacheLayout.key == 'metadata-2.6'
-        cacheLayout.version == VersionNumber.parse("2.6.0")
-        cacheLayout.formattedVersion == '2.6'
-        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/metadata-2.6')
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.groovy
deleted file mode 100644
index 34a3818..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.groovy
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice
-
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
-import org.gradle.api.internal.artifacts.ResolverResults
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
-import spock.lang.Specification
-
-class CacheLockingArtifactDependencyResolverTest extends Specification {
-    final CacheLockingManager lockingManager = Mock()
-    final ArtifactDependencyResolver target = Mock()
-    final ModuleMetadataProcessor metadataProcessor = Stub()
-    final List<ResolutionAwareRepository> repositories = [Mock(ResolutionAwareRepository)]
-    final CacheLockingArtifactDependencyResolver resolver = new CacheLockingArtifactDependencyResolver(lockingManager, target)
-
-    def "resolves while holding a lock on the cache"() {
-        ConfigurationInternal configuration = Mock()
-        ResolverResults resolverResults = Mock()
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, resolverResults)
-
-        then:
-        1 * lockingManager.useCache("resolve $configuration", !null) >> { String s, Runnable r ->
-            r.run()
-        }
-        1 * target.resolve(configuration, repositories, metadataProcessor, resolverResults)
-    }
-}
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
deleted file mode 100644
index 8872dbe..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactResolveResultTest.groovy
+++ /dev/null
@@ -1,64 +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
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactNotFoundException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import spock.lang.Specification
-
-class DefaultBuildableArtifactResolveResultTest extends Specification {
-    final result = new DefaultBuildableArtifactResolveResult()
-
-    def "cannot get file when no result specified"() {
-        when:
-        result.file
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get failure when no result specified"() {
-        when:
-        result.failure
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get file when resolve failed"() {
-        def failure = new ArtifactResolveException("broken")
-
-        when:
-        result.failed(failure)
-        result.file
-
-        then:
-        ArtifactResolveException e = thrown()
-        e == failure
-    }
-
-    def "fails with not found exception when artifact not found"() {
-        when:
-        result.notFound(Stub(ModuleVersionArtifactIdentifier))
-
-        then:
-        result.failure instanceof ArtifactNotFoundException
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResultTest.groovy
deleted file mode 100644
index c8b8257..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableArtifactSetResolveResultTest.groovy
+++ /dev/null
@@ -1,75 +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
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData
-import spock.lang.Specification
-
-class DefaultBuildableArtifactSetResolveResultTest extends Specification {
-    final result = new DefaultBuildableArtifactSetResolveResult()
-
-    def "cannot get artifacts when no result specified"() {
-        when:
-        result.artifacts
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get failure when no result specified"() {
-        when:
-        result.failure
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get artifacts when resolve failed"() {
-        def failure = new ArtifactResolveException("broken")
-
-        when:
-        result.failed(failure)
-        result.artifacts
-
-        then:
-        ArtifactResolveException e = thrown()
-        e == failure
-    }
-
-    def "has result when artifacts set"() {
-        when:
-        def artifact = Mock(ComponentArtifactMetaData)
-        result.resolved([artifact])
-
-        then:
-        result.hasResult()
-        result.failure == null
-        result.artifacts == [artifact] as Set
-    }
-
-    def "has result when failure set"() {
-        when:
-        final failure = new ArtifactResolveException("bad")
-        result.failed(failure)
-
-        then:
-        result.hasResult()
-        result.failure == failure
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResultTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResultTest.groovy
deleted file mode 100644
index c4d9eb3..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultBuildableComponentResolveResultTest.groovy
+++ /dev/null
@@ -1,109 +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
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.artifacts.ModuleVersionSelector
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class DefaultBuildableComponentResolveResultTest extends Specification {
-    def result = new DefaultBuildableComponentResolveResult()
-
-    def "can query id and meta-data when resolved"() {
-        ModuleVersionIdentifier id = Stub()
-        ModuleVersionMetaData metaData = Stub() {
-            getId() >> id
-        }
-
-        when:
-        result.resolved(metaData)
-
-        then:
-        result.id == id
-        result.metaData == metaData
-    }
-
-    def "cannot get id when no result has been specified"() {
-        when:
-        result.id
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get meta-data when no result has been specified"() {
-        when:
-        result.metaData
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get failure when no result has been specified"() {
-        when:
-        result.failure
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'No result has been specified.'
-    }
-
-    def "cannot get id when resolve failed"() {
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-
-        when:
-        result.failed(failure)
-        result.id
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e == failure
-    }
-
-    def "cannot get meta-data when resolve failed"() {
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-
-        when:
-        result.failed(failure)
-        result.metaData
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e == failure
-    }
-
-    def "failure is null when successfully resolved"() {
-        when:
-        result.resolved(Mock(ModuleVersionMetaData))
-
-        then:
-        result.failure == null
-    }
-
-    def "fails with a not found exception when not found"() {
-        when:
-        result.notFound(Mock(ModuleVersionSelector))
-
-        then:
-        result.failure instanceof ModuleVersionNotFoundException
-    }
-}
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
deleted file mode 100644
index 16e513a..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
+++ /dev/null
@@ -1,152 +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
-
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class DefaultDependencyResolveDetailsSpec extends Specification {
-
-    def "can specify version to use"() {
-        def requested = newSelector("org", "foo", "1.0")
-
-        when:
-        def details = new DefaultDependencyResolveDetails(requested)
-
-        then:
-        details.requested == requested
-        details.target == requested
-        !details.updated
-        !details.selectionReason
-
-        when:
-        details.useVersion("1.0") //the same version
-
-        then:
-        details.requested == requested
-        details.target == requested
-        details.updated
-        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
-
-        when:
-        details.useVersion("2.0") //different version
-
-        then:
-        details.requested == requested
-        details.target != requested
-        details.updated
-        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
-
-        details.target.version == "2.0"
-        details.target.name == requested.name
-        details.target.group == requested.group
-    }
-
-    def "can specify version with selection reason"() {
-        def requested = newSelector("org", "foo", "1.0")
-        def details = new DefaultDependencyResolveDetails(requested)
-
-        when:
-        details.useVersion("1.0", VersionSelectionReasons.FORCED) //same version
-
-        then:
-        details.requested == requested
-        details.target == requested
-        details.updated
-        details.selectionReason == VersionSelectionReasons.FORCED
-
-        when:
-        details.useVersion("3.0", VersionSelectionReasons.FORCED) //different version
-
-        then:
-        details.requested == requested
-        details.target.version == "3.0"
-        details.target.name == requested.name
-        details.target.group == requested.group
-        details.updated
-        details.selectionReason == VersionSelectionReasons.FORCED
-    }
-
-    def "can override version and selection reason"() {
-        def requested = newSelector("org", "foo", "1.0")
-        def details = new DefaultDependencyResolveDetails(requested)
-
-        when:
-        details.useVersion("2.0", VersionSelectionReasons.FORCED)
-        details.useVersion("3.0", VersionSelectionReasons.SELECTED_BY_RULE)
-
-        then:
-        details.requested == requested
-        details.target.version == "3.0"
-        details.target.name == requested.name
-        details.target.group == requested.group
-        details.updated
-        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
-    }
-
-    def "does not allow null version"() {
-        def details = new DefaultDependencyResolveDetails(newSelector("org", "foo", "1.0"))
-
-        when:
-        details.useVersion(null)
-
-        then:
-        thrown(IllegalArgumentException)
-
-        when:
-        details.useVersion(null, VersionSelectionReasons.SELECTED_BY_RULE)
-
-        then:
-        thrown(IllegalArgumentException)
-    }
-
-    def "can specify target module"() {
-        def details = new DefaultDependencyResolveDetails(newSelector("org", "foo", "1.0"))
-
-        when:
-        details.useTarget("org:bar:2.0")
-
-        then:
-        details.target.toString() == 'org:bar:2.0'
-        details.updated
-        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
-    }
-
-    def "can mix configuring version and target module"() {
-        def details = new DefaultDependencyResolveDetails(newSelector("org", "foo", "1.0"))
-
-        when:
-        details.useVersion("1.5")
-
-        then:
-        details.target.toString() == 'org:foo:1.5'
-
-        when:
-        details.useTarget("com:bar:3.0")
-
-        then:
-        details.target.toString() == 'com:bar:3.0'
-
-        when:
-        details.useVersion('5.0')
-
-        then:
-        details.target.toString() == 'com:bar:5.0'
-    }
-}
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
deleted file mode 100644
index 93ee3b5..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
+++ /dev/null
@@ -1,35 +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
-
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import spock.lang.Specification
-
-class DefaultUnresolvedDependencySpec extends Specification {
-
-    def "provides module details"() {
-        when:
-        def dep = new DefaultUnresolvedDependency(DefaultModuleVersionSelector.newSelector('org.foo', "foo", '1.0'), new RuntimeException("boo!"))
-
-        then:
-        dep.selector.group == 'org.foo'
-        dep.selector.name == 'foo'
-        dep.selector.version == '1.0'
-        dep.id == 'org.foo#foo;1.0'
-        dep.toString() == 'org.foo:foo:1.0'
-    }
-}
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
deleted file mode 100644
index 592cedc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
+++ /dev/null
@@ -1,156 +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.gradle.api.artifacts.LenientConfiguration
-import org.gradle.api.artifacts.ResolveException
-import org.gradle.api.artifacts.ResolvedConfiguration
-import org.gradle.api.artifacts.result.ResolutionResult
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
-import org.gradle.api.internal.artifacts.ResolverResults
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
-import org.gradle.api.specs.Specs
-import spock.lang.Specification
-
-import static org.junit.Assert.fail
-
-class ErrorHandlingArtifactDependencyResolverTest extends Specification {
-    private delegate = Mock(ArtifactDependencyResolver)
-    private resolvedConfiguration = Mock(ResolvedConfiguration)
-    private resolutionResult = Mock(ResolutionResult)
-    private configuration = Mock(ConfigurationInternal.class, name: 'coolConf')
-    private repositories = [Mock(ResolutionAwareRepository)]
-    private metadataProcessor = Stub(ModuleMetadataProcessor)
-    private results = new ResolverResults()
-    private resolver = new ErrorHandlingArtifactDependencyResolver(delegate);
-
-    void "delegates to backing service"() {
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        1 * delegate.resolve(configuration, repositories, metadataProcessor, results) >> {
-            results.resolved(resolvedConfiguration, resolutionResult)
-        }
-    }
-
-    void "wraps operations with the failure"() {
-        given:
-        def failure = new RuntimeException()
-        delegate.resolve(configuration, repositories, metadataProcessor, results) >> { throw failure }
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        results.resolvedConfiguration.hasError()
-
-        failsWith(failure)
-            .when { results.resolvedConfiguration.rethrowFailure(); }
-            .when { results.resolvedConfiguration.getFiles(Specs.satisfyAll()); }
-            .when { results.resolvedConfiguration.getFirstLevelModuleDependencies(); }
-            .when { results.resolvedConfiguration.getResolvedArtifacts(); }
-    }
-
-    void "wraps exceptions thrown by resolved configuration"() {
-        given:
-        def failure = new RuntimeException()
-
-        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, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, resolutionResult) }
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def result = results.resolvedConfiguration
-        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, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, resolutionResult) }
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def result = results.resolvedConfiguration.lenientConfiguration
-        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, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, resolutionResult) }
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def result = results.resolutionResult
-        failsWith(failure)
-                .when { result.root }
-    }
-
-    ExceptionFixture failsWith(Throwable failure) {
-        new ExceptionFixture(failure: failure)
-    }
-
-    class ExceptionFixture {
-        Throwable failure
-        ExceptionFixture when(Closure cl) {
-            try {
-                cl();
-                fail();
-            } catch (ResolveException e) {
-                assert e.message == "Could not resolve all dependencies for Mock for type 'ConfigurationInternal' named 'coolConf'."
-                assert e.cause.is(failure);
-            }
-            this
-        }
-    }
-}
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
deleted file mode 100644
index 57f9a9f..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.api.internal.artifacts.ivyservice
-
-import org.apache.ivy.core.module.id.ModuleRevisionId
-import org.junit.Test
-
-import static org.junit.Assert.assertEquals
-
-class IvyUtilTest {
-    @Test public void testModuleRevisionId() {
-        List l = ['myorg', 'myname', 'myrev']
-        ModuleRevisionId moduleRevisionId = IvyUtil.createModuleRevisionId(*l)
-        assertEquals(l[0], moduleRevisionId.organisation)
-        assertEquals(l[1], moduleRevisionId.name)
-        assertEquals(l[2], moduleRevisionId.revision)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy
deleted file mode 100644
index 6ef3b64..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy
+++ /dev/null
@@ -1,148 +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
-
-import org.apache.ivy.core.module.descriptor.*
-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.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-import java.text.SimpleDateFormat
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId
-
-class IvyXmlModuleDescriptorWriterTest extends Specification {
-
-    private @Rule TestNameTestDirectoryProvider temporaryFolder;
-    private ModuleDescriptor md = Mock();
-    private ModuleRevisionId moduleRevisionId = Mock()
-    private ModuleRevisionId resolvedModuleRevisionId = Mock()
-    def ivyXmlModuleDescriptorWriter = new IvyXmlModuleDescriptorWriter()
-
-    def "can create ivy (unmodified) descriptor"() {
-        setup:
-        assertMockMethodInvocations()
-        def dependency1 = mockDependencyDescriptor("Dep1")
-        def dependency2 = mockDependencyDescriptor("Dep2")
-        1 * dependency2.force >> true
-        1 * dependency2.changing >> true
-        1 * md.dependencies >> [dependency1, dependency2]
-        1 * dependency1.transitive >> true
-        when:
-        File ivyFile = temporaryFolder.file("test/ivy/ivy.xml")
-        ivyXmlModuleDescriptorWriter.write(md, ivyFile);
-        then:
-        def ivyModule = new XmlSlurper().parse(ivyFile);
-        assert ivyModule. at version == "2.0"
-        assert ivyModule.info. at organisation == "org.test"
-        assert ivyModule.info. at module == "projectA"
-        assert ivyModule.info. at revision == "1.0"
-        assert ivyModule.info. at status == "integration"
-        assert ivyModule.info. at publication == "20120817120000"
-        assert ivyModule.info. at buildNr == "815"
-        assert ivyModule.configurations.conf.collect {it. at name } == ["archives", "compile", "runtime"]
-        assert ivyModule.publications.artifact.collect {it. at name } == ["testartifact"]
-        assert ivyModule.dependencies.dependency.collect { "${it. at org}:${it. at name}:${it. at rev}" } == ["org.test:Dep1:1.0", "org.test:Dep2:1.0"]
-    }
-
-    private void assertMockMethodInvocations() {
-        1 * md.extraAttributesNamespaces >> [:]
-        1 * md.extraAttributes >> [buildNr: "815"]
-        1 * md.qualifiedExtraAttributes >> [buildNr: "815"]
-        1 * md.getExtraInfo() >> Collections.emptyMap()
-        1 * md.getLicenses() >> Collections.emptyList()
-        1 * md.inheritedDescriptors >> Collections.emptyList()
-        1 * md.resolvedModuleRevisionId >> resolvedModuleRevisionId
-        1 * md.resolvedPublicationDate >> date("20120817120000")
-        1 * md.status >> "integration"
-        1 * resolvedModuleRevisionId.revision >> "1.0"
-        1 * md.moduleRevisionId >> moduleRevisionId;
-        1 * moduleRevisionId.organisation >> "org.test"
-        1 * moduleRevisionId.name >> "projectA"
-        1 * md.configurations >> [mockConfiguration("archives"), mockConfiguration("compile"), mockConfiguration("runtime", ["compile"])]
-        1 * md.configurationsNames >> ["archives", "runtime", "compile"]
-        1 * md.allExcludeRules >> []
-        1 * md.allArtifacts >> [mockArtifact()]
-        0 * md.allDependencyDescriptorMediators
-    }
-
-    def "does not evaluate dependency descriptor mediators"() {
-        given:
-        ModuleRevisionId moduleRevisionId = createModuleRevisionId('org.test', 'projectA', '2.0')
-        ModuleDescriptor moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(moduleRevisionId)
-        ModuleId moduleId = createModuleId('org.test', 'projectA')
-        DependencyDescriptorMediator mediator = DefaultModuleDescriptor.newDefaultInstance(moduleRevisionId)
-        moduleDescriptor.addDependencyDescriptorMediator(moduleId, new ExactPatternMatcher(), mediator)
-        assert moduleDescriptor.allDependencyDescriptorMediators.allRules.size() == 1
-
-        when:
-        File ivyFile = temporaryFolder.file("test/ivy/ivy.xml")
-        ivyXmlModuleDescriptorWriter.write(moduleDescriptor, ivyFile)
-
-        then:
-        def ivyModule = new XmlSlurper().parse(ivyFile)
-        notThrown(UnsupportedOperationException)
-        assert ivyModule.info. at organisation == 'org.test'
-        assert ivyModule.info. at module == 'projectA'
-        assert ivyModule.info. at revision == '2.0'
-    }
-
-    def date(String timestamp) {
-        def format = new SimpleDateFormat("yyyyMMddHHmmss")
-        format.parse(timestamp)
-    }
-
-    def mockDependencyDescriptor(String organisation = "org.test", String moduleName, String revision = "1.0") {
-        DependencyDescriptor dependencyDescriptor = Mock()
-        ModuleRevisionId moduleRevisionId = Mock()
-        1 * moduleRevisionId.organisation >> organisation
-        1 * moduleRevisionId.name >> moduleName
-        1 * moduleRevisionId.revision >> revision
-        1 * dependencyDescriptor.dependencyRevisionId >> moduleRevisionId
-        1 * dependencyDescriptor.dynamicConstraintDependencyRevisionId >> moduleRevisionId
-        1 * dependencyDescriptor.moduleConfigurations >> ["default"]
-        1 * dependencyDescriptor.getDependencyConfigurations("default") >> ["compile, archives"]
-        1 * dependencyDescriptor.allDependencyArtifacts >> []
-        1 * dependencyDescriptor.allIncludeRules >> []
-        1 * dependencyDescriptor.allExcludeRules >> []
-        dependencyDescriptor
-    }
-
-    def mockArtifact() {
-        Artifact artifact = Mock()
-        1 * artifact.name >> "testartifact"
-        1 * artifact.ext >> "jar"
-        1 * artifact.type >> "jar"
-        1 * md.getArtifacts("archives") >> [artifact]
-        1 * md.getArtifacts("compile") >> []
-        1 * md.getArtifacts("runtime") >> []
-        artifact
-    }
-
-    def mockConfiguration(String configurationName, List extended = []) {
-        Configuration configuration = Mock()
-        1 * configuration.name >> configurationName
-        1 * configuration.description >> "just another test configuration"
-        1 * configuration.extends >> extended
-        1 * configuration.visibility >> Configuration.Visibility.PUBLIC
-        configuration
-    }
-}
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
deleted file mode 100644
index 6090360..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionNotFoundExceptionTest.groovy
+++ /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.ivyservice
-
-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.util.TextUtil.toPlatformLineSeparators
-
-class ModuleVersionNotFoundExceptionTest extends Specification {
-    def "formats message to include selector"() {
-        def exception1 = new ModuleVersionNotFoundException(newId("org", "a", "1.2"))
-        def exception2 = new ModuleVersionNotFoundException(newSelector("org", "a", "1.2"))
-
-        expect:
-        exception1.message == 'Could not find org:a:1.2.'
-        exception2.message == 'Could not find any version that matches org:a:1.2.'
-    }
-
-    def "can add incoming paths to exception"() {
-        def a = newId("org", "a", "1.2")
-        def b = newId("org", "b", "5")
-        def c = newId("org", "c", "1.0")
-
-        def exception = new ModuleVersionNotFoundException(newId("a", "b", "c"))
-        def onePath = exception.withIncomingPaths([[a, b, c]])
-
-        expect:
-        onePath.message == toPlatformLineSeparators('''Could not find a:b:c.
-Required by:
-    org:a:1.2 > org:b:5 > org:c:1.0''')
-        onePath.stackTrace == exception.stackTrace
-    }
-
-}
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
deleted file mode 100644
index eefb341..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ModuleVersionResolveExceptionTest.groovy
+++ /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.api.internal.artifacts.ivyservice
-
-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.util.TextUtil.toPlatformLineSeparators
-
-class ModuleVersionResolveExceptionTest extends Specification {
-    def "formats message to include selector"() {
-        def exception1 = new ModuleVersionResolveException(newSelector("org", "a", "1.2"), new RuntimeException())
-        def exception2 = new ModuleVersionResolveException(newSelector("org", "a", "1.2+"), "%s is broken")
-        def exception3 = new ModuleVersionResolveException(newId("org", "a", "1.2"), "%s is broken")
-
-        expect:
-        exception1.message == 'Could not resolve org:a:1.2.'
-        exception2.message == 'org:a:1.2+ is broken'
-        exception3.message == 'org:a:1.2 is broken'
-    }
-
-    def "can add incoming paths to exception"() {
-        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(newSelector("a", "b", "c"), cause)
-        def onePath = exception.withIncomingPaths([[a, b, c]])
-        def twoPaths = exception.withIncomingPaths([[a, b, c], [a, c]])
-
-        expect:
-        exception.message == 'Could not resolve a:b:c.'
-
-        onePath.message == toPlatformLineSeparators('''Could not resolve a:b:c.
-Required by:
-    org:a:1.2 > org:b:5 > org:c:1.0''')
-        onePath.stackTrace == exception.stackTrace
-        onePath.cause == cause
-
-        twoPaths.message == toPlatformLineSeparators('''Could not resolve a:b:c.
-Required by:
-    org:a:1.2 > org:b:5 > org:c:1.0
-    org:a:1.2 > org:c:1.0''')
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy
deleted file mode 100644
index 0c2fbed..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy
+++ /dev/null
@@ -1,135 +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;
-
-
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.DependencySet
-import org.gradle.api.artifacts.ResolvedConfiguration
-import org.gradle.api.artifacts.result.ResolutionResult
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
-import org.gradle.api.internal.artifacts.CachingDependencyResolveContext
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
-import org.gradle.api.internal.artifacts.ResolverResults
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
-import org.gradle.api.specs.Spec
-import org.gradle.api.specs.Specs
-import spock.lang.Specification
-
-public class SelfResolvingDependencyResolverTest extends Specification {
-
-    private delegate = Mock(ArtifactDependencyResolver)
-    private resolvedConfiguration = Mock(ResolvedConfiguration)
-    private configuration = Mock(ConfigurationInternal)
-    private repositories = [Mock(ResolutionAwareRepository)]
-    private dependencies = Mock(DependencySet)
-    private metadataProcessor = Stub(ModuleMetadataProcessor)
-    private results = new ResolverResults()
-    private resolver = new SelfResolvingDependencyResolver(delegate);
-
-    void "returns correct resolved configuration"() {
-        given:
-        delegate.resolve(configuration, repositories, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult)) }
-        configuration.getAllDependencies() >> dependencies
-        configuration.isTransitive() >> true
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def conf = (SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration) results.resolvedConfiguration
-        conf.resolvedConfiguration == resolvedConfiguration
-        conf.selfResolvingFilesProvider
-        conf.selfResolvingFilesProvider.resolveContext.transitive
-        conf.selfResolvingFilesProvider.dependencies == dependencies
-    }
-
-    void "uses configuration transitive setting"() {
-        given:
-        delegate.resolve(configuration, repositories, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult)) }
-        configuration.getAllDependencies() >> dependencies
-        configuration.isTransitive() >> false
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def conf = (SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration) results.resolvedConfiguration
-        !conf.selfResolvingFilesProvider.resolveContext.transitive
-    }
-
-    void "delegates to provided resolved configuration"() {
-        given:
-        delegate.resolve(configuration, repositories, metadataProcessor, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult)) }
-        configuration.getAllDependencies() >> dependencies
-        configuration.isTransitive() >> true
-
-        when:
-        resolver.resolve(configuration, repositories, metadataProcessor, results)
-        results.resolvedConfiguration.getFirstLevelModuleDependencies(Specs.satisfyAll())
-        results.resolvedConfiguration.getResolvedArtifacts()
-        results.resolvedConfiguration.hasError()
-        results.resolvedConfiguration.rethrowFailure()
-        results.resolvedConfiguration.getLenientConfiguration()
-
-        then:
-        1 * resolvedConfiguration.getFirstLevelModuleDependencies(Specs.satisfyAll())
-        1 * resolvedConfiguration.getResolvedArtifacts()
-        1 * resolvedConfiguration.hasError()
-        1 * resolvedConfiguration.rethrowFailure()
-        1 * resolvedConfiguration.getLenientConfiguration()
-    }
-
-    void "knows how to extract self resolving files"() {
-        given:
-        def resolvedFiles = Mock(FileCollection)
-        def resolveContext = Mock(CachingDependencyResolveContext)
-        def fooDep = new DefaultExternalModuleDependency("org", "foo", "1.0")
-        Set<Dependency> dependencies = [fooDep, new DefaultExternalModuleDependency("org", "bar", "1.0")]
-
-        def provider = new SelfResolvingDependencyResolver.SelfResolvingFilesProvider(resolveContext, dependencies)
-
-        when:
-        def files = provider.getFiles({ it.name == 'foo' } as Spec)
-
-        then:
-        1 * resolveContext.add(fooDep)
-        1 * resolveContext.resolve() >> resolvedFiles
-        1 * resolvedFiles.getFiles() >> [new File('foo.jar')]
-        0 * _._
-
-        files*.name == ['foo.jar']
-    }
-
-    void "aggregates files with self resolving files first"() {
-        given:
-        def provider = Mock(SelfResolvingDependencyResolver.SelfResolvingFilesProvider) {
-            getFiles(Specs.satisfyAll()) >> [new File("foo.jar")]
-        }
-        resolvedConfiguration.getFiles(Specs.satisfyAll()) >> new HashSet<File>([new File("bar.jar")])
-
-        def conf = new SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration(resolvedConfiguration, provider)
-
-        when:
-        def files = conf.getFiles(Specs.satisfyAll())
-
-        then:
-        files*.name == ['foo.jar', 'bar.jar']
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy
deleted file mode 100644
index 95e1fde..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice
-
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.DependencySet
-import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
-import org.gradle.api.internal.artifacts.ResolverResults
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
-import org.gradle.api.specs.Specs
-import spock.lang.Specification
-
-class ShortcircuitEmptyConfigsArtifactDependencyResolverSpec extends Specification {
-
-    def delegate = Mock(ArtifactDependencyResolver)
-    def configuration = Stub(ConfigurationInternal)
-    def repositories = [Stub(ResolutionAwareRepository)]
-    def metadataProcessor = Stub(ModuleMetadataProcessor)
-    def dependencies = Stub(DependencySet)
-    def componentIdentifierFactory = Mock(ComponentIdentifierFactory)
-    def results = new ResolverResults()
-    def dependencyResolver = new ShortcircuitEmptyConfigsArtifactDependencyResolver(delegate, componentIdentifierFactory);
-
-    def "returns empty result when no dependencies"() {
-        given:
-        dependencies.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-
-        when:
-        dependencyResolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        def resolvedConfig = results.resolvedConfiguration
-        !resolvedConfig.hasError()
-        resolvedConfig.rethrowFailure();
-
-        resolvedConfig.getFiles(Specs.<Dependency> satisfyAll()).isEmpty()
-        resolvedConfig.getFirstLevelModuleDependencies().isEmpty()
-        resolvedConfig.getResolvedArtifacts().isEmpty()
-
-        and:
-        def result = results.resolutionResult
-        result.allComponents.size() == 1
-        result.allDependencies.empty
-
-        and:
-        0 * delegate._
-    }
-
-    def "delegates to backing service when there are one or more dependencies"() {
-        given:
-        dependencies.isEmpty() >> false
-        configuration.getAllDependencies() >> dependencies
-
-        when:
-        dependencyResolver.resolve(configuration, repositories, metadataProcessor, results)
-
-        then:
-        1 * delegate.resolve(configuration, repositories, metadataProcessor, results)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolverSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolverSpec.groovy
deleted file mode 100644
index 5b06b2f..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/VersionForcingDependencyToModuleResolverSpec.groovy
+++ /dev/null
@@ -1,107 +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
-
-import org.gradle.api.Action
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId
-
-class VersionForcingDependencyToModuleResolverSpec extends Specification {
-    final target = Mock(DependencyToModuleVersionIdResolver)
-    final resolvedVersion = Mock(ModuleVersionIdResolveResult)
-    final forced = createModuleRevisionId('group', 'module', 'forced')
-
-    def "passes through dependency when it does not match any rule"() {
-        def dep = dependency('org', 'module', '1.0')
-        def rule = Mock(Action)
-        def resolver = new VersionForcingDependencyToModuleResolver(target, rule)
-
-        when:
-        def result = resolver.resolve(dep)
-
-        then:
-        result == resolvedVersion
-
-        and:
-        1 * target.resolve(dep) >> resolvedVersion
-        1 * rule.execute( {it.requested.group == 'org' && it.requested.name == 'module' && it.requested.version == '1.0'} )
-        0 * target._
-    }
-
-    def "replaces dependency by rule"() {
-        def dep = dependency('org', 'module', '0.5')
-        def modified = dependency('org', 'module', '1.0')
-
-        def force = { it.useVersion("1.0") } as Action
-
-        def resolver = new VersionForcingDependencyToModuleResolver(target, force)
-
-        when:
-        SubstitutedModuleVersionIdResolveResult result = resolver.resolve(dep)
-
-        then:
-        result.result == resolvedVersion
-        result.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
-
-        and:
-        1 * dep.withRequestedVersion(DefaultModuleVersionSelector.newInstance("org", "module", "1.0")) >> modified
-        1 * target.resolve(modified) >> resolvedVersion
-        0 * target._
-    }
-
-    def "explosive rule yields failure result that provides context"() {
-        def force = { throw new Error("Boo!") } as Action
-        def resolver = new VersionForcingDependencyToModuleResolver(target, force)
-
-        when:
-        def result = resolver.resolve(dependency('org', 'module', '0.5'))
-
-        then:
-        result.failure.message == "Could not resolve org:module:0.5."
-        result.failure.cause.message == 'Boo!'
-        result.selectionReason == VersionSelectionReasons.REQUESTED
-    }
-
-    def "failed result uses correct exception"() {
-        def force = { throw new Error("Boo!") } as Action
-        def resolver = new VersionForcingDependencyToModuleResolver(target, force)
-        def result = resolver.resolve(dependency('org', 'module', '0.5'))
-
-        when:
-        result.getId()
-
-        then:
-        def ex = thrown(ModuleVersionResolveException)
-        ex == result.failure
-
-        when:
-        result.resolve()
-
-        then:
-        def ex2 = thrown(ModuleVersionResolveException)
-        ex2 == result.failure
-    }
-
-    def dependency(String group, String module, String version) {
-        Mock(DependencyMetaData) {
-            getRequested() >> new DefaultModuleVersionSelector(group, module, version)
-        }
-    }
-}
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
deleted file mode 100644
index 1b72673..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
+++ /dev/null
@@ -1,95 +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.clientmodule
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor
-import org.gradle.api.internal.artifacts.ivyservice.BuildableComponentResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
-import org.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ClientModuleDependencyDescriptor
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class ClientModuleResolverTest extends Specification {
-    final ModuleSource moduleSource = Mock()
-    final ModuleVersionMetaData moduleMetaData = Mock()
-    final DependencyToModuleVersionResolver target = Mock()
-    final ClientModuleResolver resolver = new ClientModuleResolver(target)
-
-    def "replaces meta-data for a client module dependency"() {
-        ClientModuleDependencyDescriptor dependencyDescriptor = Mock()
-        DependencyMetaData dependencyMetaData = Mock()
-        BuildableComponentResolveResult result = Mock()
-        ModuleVersionMetaData resolvedMetaData = Mock()
-
-        given:
-
-        _ * dependencyMetaData.descriptor >> dependencyDescriptor
-        _ * dependencyDescriptor.getTargetModule() >> moduleMetaData
-
-        when:
-        resolver.resolve(dependencyMetaData, result)
-
-        then:
-        1 * target.resolve(dependencyMetaData, result)
-        1 * result.getMetaData() >> resolvedMetaData
-        1 * resolvedMetaData.getSource() >> moduleSource
-        1 * dependencyDescriptor.getTargetModule() >> moduleMetaData
-        1 * moduleMetaData.withSource(moduleSource) >> moduleMetaData
-        1 * result.setMetaData(moduleMetaData)
-        _ * result.failure >> null
-        0 * result._
-    }
-
-    def "does not replace meta-data for unknown module version"() {
-        DependencyDescriptor dependencyDescriptor = Mock()
-        DependencyMetaData dependencyMetaData = Mock()
-        BuildableComponentResolveResult result = Mock()
-
-        given:
-        _ * dependencyMetaData.descriptor >> dependencyDescriptor
-
-        when:
-        resolver.resolve(dependencyMetaData, result)
-
-        then:
-        1 * target.resolve(dependencyMetaData, result)
-        _ * result.failure >> null
-        0 * result._
-    }
-
-    def "does not replace meta-data for broken module version"() {
-        ClientModuleDependencyDescriptor dependencyDescriptor = Mock()
-        DependencyMetaData dependencyMetaData = Mock()
-        BuildableComponentResolveResult result = Mock()
-
-        given:
-        _ * dependencyMetaData.descriptor >> dependencyDescriptor
-
-        when:
-        resolver.resolve(dependencyMetaData, result)
-
-        then:
-        1 * target.resolve(dependencyMetaData, result)
-        _ * result.failure >> new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-        0 * result._
-    }
-}
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
deleted file mode 100644
index d6b9591..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleVersionRepositoryTest.groovy
+++ /dev/null
@@ -1,101 +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.gradle.api.Transformer
-import org.gradle.api.internal.artifacts.ModuleMetadataProcessor
-import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.DefaultBuildableArtifactSetResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache
-import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import org.gradle.api.internal.externalresource.cached.CachedArtifactIndex
-import org.gradle.api.internal.externalresource.ivy.ArtifactAtRepositoryKey
-import org.gradle.util.BuildCommencedTimeProvider
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class CachingModuleVersionRepositoryTest extends Specification {
-    def realRepo = Stub(LocalArtifactsModuleVersionRepository) {
-        getId() >> "repo-id"
-    }
-    def moduleResolutionCache = Stub(ModuleVersionsCache)
-    def moduleDescriptorCache = Mock(ModuleMetaDataCache)
-    def moduleArtifactsCache = Mock(ModuleArtifactsCache)
-    def artifactAtRepositoryCache = Mock(CachedArtifactIndex)
-    def cachePolicy = Stub(CachePolicy)
-    def metadataProcessor = Stub(ModuleMetadataProcessor)
-    def moduleExtractor = Mock(Transformer)
-    def repo = new CachingModuleVersionRepository(realRepo, moduleResolutionCache, moduleDescriptorCache, moduleArtifactsCache, artifactAtRepositoryCache,
-            cachePolicy, new BuildCommencedTimeProvider(), metadataProcessor, moduleExtractor)
-
-    @Unroll
-    def "last modified date is cached - lastModified = #lastModified"() {
-        given:
-        def artifactId = Stub(ModuleVersionArtifactIdentifier)
-        def artifact = Stub(ModuleVersionArtifactMetaData) {
-            getId() >> artifactId
-        }
-
-        def file = new File("local")
-        def result = Stub(BuildableArtifactResolveResult) {
-            getFile() >> file
-            getFailure() >> null
-        }
-
-        def descriptorHash = 1234G
-        def moduleSource = Stub(CachingModuleVersionRepository.CachingModuleSource) {
-            getDescriptorHash() >> descriptorHash
-        }
-
-        ArtifactAtRepositoryKey atRepositoryKey = new ArtifactAtRepositoryKey("repo-id", artifactId)
-
-        when:
-        repo.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * artifactAtRepositoryCache.store(atRepositoryKey, file, descriptorHash)
-        0 * moduleDescriptorCache._
-
-        where:
-        lastModified << [new Date(), null]
-    }
-
-    def "does not use cache when artifact set can be determined locally"() {
-        def component = Mock(ComponentMetaData)
-        def source = Mock(ModuleSource)
-        def cachingSource = new CachingModuleVersionRepository.CachingModuleSource(BigInteger.ONE, false, source)
-        def context = Mock(ArtifactResolveContext)
-        def result = new DefaultBuildableArtifactSetResolveResult()
-
-        when:
-        repo.resolveModuleArtifacts(component, context, result)
-
-        then:
-        1 * component.getSource() >> cachingSource
-        1 * component.withSource(source) >> component
-        realRepo.localResolveModuleArtifacts(component, context, result) >> {
-            result.resolved([Mock(ComponentArtifactMetaData)])
-        }
-        0 * _
-    }
-}
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
deleted file mode 100644
index 1388a95..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultBuildableModuleVersionMetaDataResolveResultTest.groovy
+++ /dev/null
@@ -1,174 +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.gradle.api.internal.artifacts.ivyservice.ModuleVersionResolveException
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class DefaultBuildableModuleVersionMetaDataResolveResultTest extends Specification {
-    def descriptor = new DefaultBuildableModuleVersionMetaDataResolveResult()
-    def moduleSource = Stub(ModuleSource)
-
-    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 using meta-data"() {
-        def metaData = Stub(MutableModuleVersionMetaData)
-
-        when:
-        descriptor.resolved(metaData, moduleSource)
-
-        then:
-        descriptor.state == BuildableModuleVersionMetaDataResolveResult.State.Resolved
-        descriptor.failure == null
-        descriptor.metaData == metaData
-        descriptor.moduleSource == moduleSource
-    }
-
-    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 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
deleted file mode 100644
index 952294e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
+++ /dev/null
@@ -1,123 +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.AbstractPatternsBasedResolver
-import org.apache.ivy.plugins.resolver.DependencyResolver
-import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver
-import spock.lang.Specification
-
-public class DependencyResolverIdentifierTest extends Specification {
-    def "dependency resolvers of unknown type are identified by their name"() {
-        given:
-        DependencyResolver resolver1 = Mock()
-        DependencyResolver resolver1a = Mock()
-        DependencyResolver resolver2 = Mock()
-
-        resolver1.name >> 'name1'
-        resolver1a.name >> 'name1'
-        resolver2.name >> 'name2'
-
-        expect:
-        id(resolver1) == id(resolver1a)
-        id(resolver1) != id(resolver2)
-    }
-
-    def "dependency resolvers of type AbstractPatternBasedResolver are differentiated by their patterns"() {
-        given:
-        AbstractPatternsBasedResolver resolver1 = Mock()
-        AbstractPatternsBasedResolver resolver1a = Mock()
-        AbstractPatternsBasedResolver resolver2 = Mock()
-        AbstractPatternsBasedResolver resolver2a = Mock()
-
-        resolver1.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver1.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver1a.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver1a.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver2.ivyPatterns >> ['ivy1', 'different']
-        resolver2.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver2a.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver2a.artifactPatterns >> ['artifact1', 'different']
-
-        expect:
-        id(resolver1) == id(resolver1a)
-        id(resolver1) != id(resolver2)
-        id(resolver1) != id(resolver2a)
-        id(resolver2) != id(resolver2a)
-    }
-
-    def "dependency resolvers of type ExternalResourceResolver are differentiated by their patterns"() {
-        given:
-        ExternalResourceResolver resolver1 = Mock()
-        ExternalResourceResolver resolver1a = Mock()
-        ExternalResourceResolver resolver2 = Mock()
-        ExternalResourceResolver resolver2a = Mock()
-
-        resolver1.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver1.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver1a.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver1a.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver2.ivyPatterns >> ['ivy1', 'different']
-        resolver2.artifactPatterns >> ['artifact1', 'artifact2']
-        resolver2a.ivyPatterns >> ['ivy1', 'ivy2']
-        resolver2a.artifactPatterns >> ['artifact1', 'different']
-
-        expect:
-        id(resolver1) == id(resolver1a)
-        id(resolver1) != id(resolver2)
-        id(resolver1) != id(resolver2a)
-        id(resolver2) != id(resolver2a)
-    }
-
-    def "dependency resolvers of type AbstractPatternBasedResolver are differentiated by m2compatible flag"() {
-        given:
-        AbstractPatternsBasedResolver resolver1 = Mock()
-        AbstractPatternsBasedResolver resolver2 = Mock()
-
-        resolver1.ivyPatterns >> ['ivy1']
-        resolver1.artifactPatterns >> ['artifact1']
-        resolver1.m2compatible >> false
-        resolver2.ivyPatterns >> ['ivy1']
-        resolver2.artifactPatterns >> ['artifact1']
-        resolver2.m2compatible >> true
-
-        expect:
-        id(resolver1) != id(resolver2)
-    }
-
-    def "dependency resolvers of type ExternalResourceResolver are differentiated by m2compatible flag"() {
-        given:
-        ExternalResourceResolver resolver1 = Mock()
-        ExternalResourceResolver resolver2 = Mock()
-
-        resolver1.ivyPatterns >> ['ivy1']
-        resolver1.artifactPatterns >> ['artifact1']
-        resolver2.ivyPatterns >> ['ivy1']
-        resolver2.artifactPatterns >> ['artifact1']
-        resolver2.m2compatible >> true
-
-        expect:
-        id(resolver1) != id(resolver2)
-    }
-
-    def id(DependencyResolver resolver) {
-        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/ErrorHandlingArtifactResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
deleted file mode 100644
index 3cc195b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
+++ /dev/null
@@ -1,82 +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
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolver
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ComponentMetaData
-import spock.lang.Specification
-
-class ErrorHandlingArtifactResolverTest extends Specification {
-    def delegate = Mock(ArtifactResolver)
-    def artifactResolver = new ErrorHandlingArtifactResolver(delegate)
-    def artifactSetResolveResult = Mock(BuildableArtifactSetResolveResult)
-
-    def "wraps resolveArtifact exception as failure"() {
-        def componentArtifactId = Stub(ComponentArtifactIdentifier) {
-            getDisplayName() >> "component-artifact"
-        }
-        def componentArtifact = Stub(ComponentArtifactMetaData) {
-            getId() >> componentArtifactId
-        }
-        def moduleSource = Mock(ModuleSource)
-        def artifactResolveResult = Mock(BuildableArtifactResolveResult)
-        def failure = new RuntimeException("foo")
-
-        when:
-        delegate.resolveArtifact(componentArtifact, moduleSource, artifactResolveResult) >> { throw failure }
-
-        and:
-        artifactResolver.resolveArtifact(componentArtifact, moduleSource, artifactResolveResult)
-
-        then:
-        1 * artifactResolveResult.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
-            assert e.message == "Could not download artifact 'component-artifact'"
-            assert e.cause == failure
-        }
-    }
-
-    def "wraps resolveModuleArtifacts exception as failure"() {
-        def componentId = Stub(ComponentIdentifier) {
-            getDisplayName() >> "component"
-        }
-        def component = Stub(ComponentMetaData) {
-            getComponentId() >> componentId
-        }
-        def context = Stub(ArtifactResolveContext) {
-            getId() >> "artifact-context"
-        }
-        def result = Mock(BuildableArtifactSetResolveResult)
-        def failure = new RuntimeException("foo")
-
-        when:
-        delegate.resolveModuleArtifacts(component, context, result) >> { throw failure }
-
-        and:
-        artifactResolver.resolveModuleArtifacts(component, context, result)
-
-        then:
-        1 * result.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
-            assert e.message == "Could not determine artifacts for component 'component'"
-            assert e.cause == failure
-        }
-    }
-}
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
deleted file mode 100644
index 3433bd7..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleVersionRepositoryTest.groovy
+++ /dev/null
@@ -1,69 +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.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import spock.lang.Specification
-
-class IvyDynamicResolveModuleVersionRepositoryTest extends Specification {
-    final target = Mock(LocalAwareModuleVersionRepository)
-    final metaData = Mock(MutableModuleVersionMetaData)
-    final requestedDependency = Mock(DependencyMetaData)
-    final result = Mock(BuildableModuleVersionMetaDataResolveResult)
-    final repository = new IvyDynamicResolveModuleVersionRepository(target)
-
-    def "replaces each dependency version with revConstraint"() {
-        def original = dependency('1.2+')
-        def transformed = dependency()
-
-        given:
-        result.state >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
-        result.metaData >> metaData
-
-        when:
-        repository.getLocalDependency(requestedDependency, result)
-
-        then:
-        1 * target.getLocalDependency(requestedDependency, result)
-
-        and:
-        1 * metaData.dependencies >> [original]
-        1 * original.withRequestedVersion('1.2+') >> transformed
-        1 * metaData.setDependencies([transformed])
-    }
-
-    def "does nothing when dependency has not been resolved"() {
-        when:
-        repository.getLocalDependency(requestedDependency, result)
-
-        then:
-        1 * target.getLocalDependency(requestedDependency, result)
-        _ * result.state >> BuildableModuleVersionMetaDataResolveResult.State.Missing
-        0 * result._
-    }
-
-    def dependency(String revConstraint = '1.0') {
-        def dep = Mock(DependencyMetaData)
-        def descriptor = Mock(DependencyDescriptor)
-        _ * descriptor.dynamicConstraintDependencyRevisionId >> IvyUtil.createModuleRevisionId('org', 'module', revConstraint)
-        _ * dep.descriptor >> descriptor
-        return dep
-    }
-}
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
deleted file mode 100644
index e768248..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LazyDependencyToModuleResolverTest.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.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.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 org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class LazyDependencyToModuleResolverTest extends Specification {
-    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"() {
-        def dependency = dependency()
-        def metaData = module()
-
-        when:
-        def idResolveResult = resolver.resolve(dependency)
-
-        then:
-        idResolveResult.id == metaData.id
-
-        and:
-        0 * target._
-
-        when:
-        def moduleResolveResult = idResolveResult.resolve()
-
-        then:
-        moduleResolveResult.id == metaData.id
-
-        moduleResolveResult.metaData == metaData
-
-        1 * target.resolve(dependency, _) >> { args -> args[1].resolved(metaData)}
-        0 * target._
-    }
-
-    def "resolves module for dynamic version dependency immediately"() {
-        def dependency = dependency()
-        def metaData = module()
-
-        given:
-        matcher.isDynamic(_) >> true
-
-        when:
-        def idResolveResult = resolver.resolve(dependency)
-        def id = idResolveResult.id
-
-        then:
-        id == metaData.id
-
-        and:
-        1 * target.resolve(dependency, _) >> { args -> args[1].resolved(metaData)}
-        0 * target._
-
-        when:
-        def moduleResolveResult = idResolveResult.resolve()
-
-        then:
-        moduleResolveResult.id == metaData.id
-        moduleResolveResult.metaData == metaData
-
-        and:
-        0 * target._
-    }
-
-    def moduleIdentifier(ModuleDescriptor moduleDescriptor) {
-        return new DefaultModuleVersionIdentifier(moduleDescriptor.moduleRevisionId.organisation, moduleDescriptor.moduleRevisionId.name, moduleDescriptor.moduleRevisionId.revision)
-    }
-
-    def "does not resolve module more than once"() {
-        def dependency = dependency()
-        def module = module()
-
-        when:
-        def idResolveResult = resolver.resolve(dependency)
-        idResolveResult.resolve()
-
-        then:
-        1 * target.resolve(dependency, _) >> { args -> args[1].resolved(module.moduleRevisionId, module, Mock(ArtifactResolver))}
-        0 * target._
-
-        when:
-        idResolveResult.resolve()
-
-        then:
-        0 * target._
-    }
-
-    def "collects failure to resolve module"() {
-        def dependency = dependency()
-        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-
-        when:
-        def idFailureResult = resolver.resolve(dependency)
-
-        then:
-        idFailureResult.failure == null;
-
-        and:
-        0 * target._
-
-        when:
-        def resolveResult = idFailureResult.resolve()
-
-        then:
-        resolveResult.failure.is(failure)
-
-        and:
-        1 * target.resolve(dependency, _) >> { args -> args[1].failed(failure)}
-        0 * target._
-
-        when:
-        resolveResult.metaData
-
-        then:
-        ModuleVersionResolveException e = thrown()
-        e.is(resolveResult.failure)
-
-        and:
-        0 * target._
-    }
-
-    def "collects and wraps module not found"() {
-        def dependency = dependency()
-
-        when:
-        def resolveResult = resolver.resolve(dependency).resolve()
-
-        then:
-        resolveResult.failure instanceof ModuleVersionNotFoundException
-        resolveResult.failure.message == "Could not find group:module:1.0."
-
-        and:
-        1 * target.resolve(dependency, _) >> { args -> args[1].failed(new ModuleVersionNotFoundException(newId("org", "a", "1.2")))}
-    }
-
-    def "collects and wraps unexpected module resolve failure"() {
-        def dependency = dependency()
-        def failure = new RuntimeException("broken")
-
-        when:
-        def resolveResult = resolver.resolve(dependency).resolve()
-
-        then:
-        resolveResult.failure instanceof ModuleVersionResolveException
-        resolveResult.failure.message == "Could not resolve group:module:1.0."
-
-        and:
-        1 * target.resolve(dependency, _) >> { throw failure }
-    }
-
-    def "collects and wraps module not found for missing dynamic version"() {
-        def dependency = dependency()
-
-        given:
-        matcher.isDynamic(_) >> true
-
-        when:
-        def idResolveResult = resolver.resolve(dependency)
-
-        then:
-        idResolveResult.failure instanceof ModuleVersionNotFoundException
-        idResolveResult.failure.message == "Could not find any version that matches group:module:1.0."
-
-        and:
-        1 * target.resolve(dependency, _) >> { args -> args[1].failed(new ModuleVersionNotFoundException(newId("org", "a", "1.2")))}
-
-        when:
-        idResolveResult.id
-
-        then:
-        ModuleVersionNotFoundException e = thrown()
-        e.is(idResolveResult.failure)
-
-        and:
-        0 * target._
-
-        when:
-        def resolveResult = idResolveResult.resolve()
-        resolveResult.metaData
-
-        then:
-        e = thrown()
-        e.is(idResolveResult.failure)
-    }
-
-    def module() {
-        ModuleRevisionId id = IvyUtil.createModuleRevisionId("group", "module", "1.0")
-        return new ModuleDescriptorAdapter(new DefaultModuleDescriptor(id, "release", new Date()))
-    }
-
-    def dependency() {
-        DependencyDescriptor descriptor = Mock()
-        ModuleRevisionId id = IvyUtil.createModuleRevisionId("group", "module", "1.0")
-        _ * descriptor.dependencyRevisionId >> id
-        DependencyMetaData metaData = Mock()
-        _ * metaData.descriptor >> descriptor
-        ModuleVersionSelector requested = DefaultModuleVersionSelector.newSelector("group", "module", "1.0")
-        _ * metaData.requested >> requested
-        return metaData
-    }
-
-    def artifact() {
-        Artifact artifact = Mock()
-        _ * artifact.moduleRevisionId >> IvyUtil.createModuleRevisionId("group", "module", "1.0")
-        _ * artifact.name >> 'artifact'
-        _ * artifact.ext >> 'zip'
-        return Stub(ModuleVersionArtifactMetaData) {
-            getArtifact() >> artifact
-            getId() >> Stub(ModuleVersionArtifactIdentifier) {
-                getDisplayName() >> "artifact.zip"
-            }
-        }
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy
deleted file mode 100644
index b4aa7d3..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy
+++ /dev/null
@@ -1,639 +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.gradle.api.Transformer
-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.BuildableComponentResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestStrategy
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import spock.lang.Ignore
-import spock.lang.Specification
-
-class RepositoryChainDependencyResolverTest extends Specification {
-    final metaData = metaData("1.2")
-    final dependencyId = Stub(ModuleVersionSelector)
-    final dependency = Stub(DependencyMetaData)
-    final dependencyDescriptor = Stub(DependencyDescriptor)
-    final matcher = Stub(VersionMatcher)
-    final latestStrategy = Stub(LatestStrategy) {
-        compare(_, _) >> { a, b ->
-            a.version.compareTo(b.version)
-        }
-    }
-    final Transformer<ModuleVersionMetaData, RepositoryChainModuleResolution> transformer = Mock(Transformer)
-    final result = Mock(BuildableComponentResolveResult)
-    final moduleSource = Mock(ModuleSource)
-
-    final RepositoryChainDependencyResolver resolver = new RepositoryChainDependencyResolver(matcher, latestStrategy, transformer)
-
-    ModuleVersionIdentifier moduleVersionIdentifier(ModuleDescriptor moduleDescriptor) {
-        def moduleRevId = moduleDescriptor.moduleRevisionId
-        new DefaultModuleVersionIdentifier(moduleRevId.organisation, moduleRevId.name, moduleRevId.revision)
-    }
-
-    def setup() {
-        _ * dependencyId.group >> "group"
-        _ * dependencyId.name >> "project"
-        _ * dependencyId.version >> "1.0"
-        _ * dependency.requested >> dependencyId
-        _ * dependency.descriptor >> dependencyDescriptor
-    }
-
-    def "uses local dependency when available"() {
-        given:
-        def repo = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo.name >> "repo"
-        0 * repo._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is unknown"() {
-        given:
-        def repo = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo.getLocalDependency(dependency, _)
-        1 * repo.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-        and:
-        _ * repo.name >> "repo"
-        0 * repo._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is probably missing"() {
-        given:
-        def repo = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo.name >> "repo"
-        0 * repo._
-        0 * result._
-    }
-
-    def "fails with not found when local dependency is marked as missing"() {
-        given:
-        def repo = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo.getLocalDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * result.notFound(dependencyId)
-
-        and:
-        _ * repo.name >> "repo"
-        0 * repo._
-        0 * result._
-    }
-
-    def "fails with not found when local and remote dependency marked as missing"() {
-        given:
-        def repo = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo.getDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * result.notFound(dependencyId)
-
-        and:
-        _ * repo.name >> "repo"
-        0 * repo._
-        0 * result._
-    }
-
-    // TODO:DAZ Add more tests for dynamic versions and fix this
-    @Ignore
-    def "searches all repositories for a dynamic version"() {
-        given:
-        _ * matcher.isDynamic(_) >> true
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        def repo3 = Mock(LocalAwareModuleVersionRepository)
-        def version2 = metaData("1.2")
-        resolver.add(repo1)
-        resolver.add(repo2)
-        resolver.add(repo3)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData("1.1"), null)
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(version2, moduleSource)
-        }
-        1 * repo3.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData("1.0"), null)
-        }
-        1 * result.resolved(_, _) >> { metaData, source ->
-            assert metaData == version2
-            assert source.delegate == repo2
-            assert source.moduleSource == moduleSource
-        }
-
-        and:
-        _ * repo1.name >> "repo1"
-        _ * repo2.name >> "repo2"
-        _ * repo3.name >> "repo3"
-        0 * repo1._
-        0 * repo2._
-        0 * repo3._
-        0 * result._
-    }
-
-    def "stops on first available local dependency for static version"() {
-        given:
-        _ * matcher.isDynamic(_) >> false
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        def repo3 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-        resolver.add(repo3)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo1
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo1"
-        _ * repo2.name >> "repo2"
-        _ * repo3.name >> "repo3"
-        0 * repo1._
-        0 * repo2._
-        0 * repo3._
-        0 * result._
-    }
-
-    def "uses local dependency when available in one repository and missing from all other repositories"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "uses local dependency when available in one repository and probably missing in all other repositories"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "uses remote dependency when local dependency is unknown for a given repository and probably missing in other repositories"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo2.getLocalDependency(dependency, _)
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is probably missing in all repositories"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo1.getDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "does not attempt to resolve remote dependency when local dependency is missing"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is missing or unknown in all repositories"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            result.probablyMissing()
-        }
-        1 * repo2.getLocalDependency(dependency, _)
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * repo1.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo1
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "ignores failure to resolve local dependency when available in another repository"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            throw new RuntimeException("broken")
-        }
-        1 * repo2.getLocalDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "ignores failure to resolve remote dependency when available in another repository"() {
-        given:
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _)
-        1 * repo1.getDependency(dependency, _) >> { dep, result ->
-            throw new RuntimeException("broken")
-        }
-        1 * repo2.getLocalDependency(dependency, _)
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.resolved(metaData, moduleSource)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.moduleSource == moduleSource
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ModuleVersionMetaData metaData ->
-            assert metaData == this.metaData
-        }
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "rethrows failure to resolve local dependency when not available in any repository"() {
-        given:
-        def failure = new RuntimeException("broken")
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _) >> { dep, result ->
-            throw failure
-        }
-        1 * repo2.getLocalDependency(dependency, _)
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * result.failed({ it.cause == failure })
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def "rethrows failure to resolve remote dependency when not available in any repository"() {
-        given:
-        def failure = new RuntimeException("broken")
-        def repo1 = Mock(LocalAwareModuleVersionRepository)
-        def repo2 = Mock(LocalAwareModuleVersionRepository)
-        resolver.add(repo1)
-        resolver.add(repo2)
-
-        when:
-        resolver.resolve(dependency, result)
-
-        then:
-        1 * repo1.getLocalDependency(dependency, _)
-        1 * repo1.getDependency(dependency, _) >> { dep, result ->
-            throw failure
-        }
-        1 * repo2.getLocalDependency(dependency, _)
-        1 * repo2.getDependency(dependency, _) >> { dep, result ->
-            result.missing()
-        }
-        1 * result.failed({ it.cause == failure })
-
-        and:
-        _ * repo1.name >> "repo"
-        _ * repo2.name >> "repo"
-        0 * repo1._
-        0 * repo2._
-        0 * result._
-    }
-
-    def descriptor(String version) {
-        def descriptor = Stub(ModuleDescriptor)
-        descriptor.resolvedModuleRevisionId >> IvyUtil.createModuleRevisionId("org", "module", version)
-        return descriptor
-    }
-
-    def metaData(String version) {
-        return Stub(MutableModuleVersionMetaData) {
-            toString() >> version
-            getId() >> DefaultModuleVersionIdentifier.newId("org", "module", version)
-            getDescriptor() >> descriptor(version)
-        }
-    }
-}
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
deleted file mode 100644
index c0b631d..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy
+++ /dev/null
@@ -1,99 +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.memcache
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import spock.lang.Specification
-
-class CachedModuleVersionResultTest extends Specification {
-
-    def "knows if result is cachable"() {
-        def resolved = Mock(BuildableModuleVersionMetaDataResolveResult) {
-            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
-            getMetaData() >> Stub(MutableModuleVersionMetaData)
-        }
-        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(MutableModuleVersionMetaData)
-    }
-
-    def "supplies cached data"() {
-        def suppliedMetaData = Mock(MutableModuleVersionMetaData)
-        def cachedMetaData = Mock(MutableModuleVersionMetaData)
-        def metaData = Mock(MutableModuleVersionMetaData)
-        def source = Mock(ModuleSource)
-        def resolved = Mock(BuildableModuleVersionMetaDataResolveResult) {
-            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
-            getMetaData() >> metaData
-            getModuleSource() >> source
-        }
-        def missing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.Missing }
-        def probablyMissing = Mock(BuildableModuleVersionMetaDataResolveResult) { getState() >> BuildableModuleVersionMetaDataResolveResult.State.ProbablyMissing }
-
-        def result = Mock(BuildableModuleVersionMetaDataResolveResult)
-
-        when:
-        def cached = new CachedModuleVersionResult(resolved)
-
-        then:
-        1 * metaData.copy() >> cachedMetaData
-
-        when:
-        cached.supply(result)
-
-        then:
-        1 * cachedMetaData.copy() >> suppliedMetaData
-        1 * result.resolved(suppliedMetaData, source)
-
-        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
deleted file mode 100644
index 5e9b898..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedRepositoryTest.groovy
+++ /dev/null
@@ -1,178 +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.memcache
-import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveContext
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactSetResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionSelectionResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.LocalAwareModuleVersionRepository
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-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 listingResult = Mock(BuildableModuleVersionSelectionResolveResult)
-    def metaDataResult = 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 module version listings"() {
-        when:
-        repo.localListModuleVersions(dep, listingResult)
-
-        then:
-        1 * cache.supplyLocalModuleVersions(lib, listingResult) >> false
-        1 * delegate.localListModuleVersions(dep, listingResult)
-        1 * cache.newLocalModuleVersions(lib, listingResult)
-        0 * _
-
-        when:
-        repo.listModuleVersions(dep, listingResult)
-
-        then:
-        1 * cache.supplyModuleVersions(lib, listingResult) >> false
-        1 * delegate.listModuleVersions(dep, listingResult)
-        1 * cache.newModuleVersions(lib, listingResult)
-        0 * _
-    }
-
-    def "uses module version listings from cache"() {
-        when:
-        repo.localListModuleVersions(dep, listingResult)
-
-        then:
-        1 * cache.supplyLocalModuleVersions(lib, listingResult) >> true
-        0 * _
-
-        when:
-        repo.listModuleVersions(dep, listingResult)
-
-        then:
-        1 * cache.supplyModuleVersions(lib, listingResult) >> true
-        0 * _
-    }
-
-    def "retrieves and caches local dependencies"() {
-        when:
-        repo.getLocalDependency(dep, metaDataResult)
-
-        then:
-        1 * cache.supplyLocalMetaData(lib, metaDataResult) >> false
-        1 * delegate.getLocalDependency(dep, metaDataResult)
-        1 * cache.newLocalDependencyResult(lib, metaDataResult)
-        0 * _
-    }
-
-    def "uses local dependencies from cache"() {
-        when:
-        repo.getLocalDependency(dep, metaDataResult)
-
-        then:
-        1 * cache.supplyLocalMetaData(lib, metaDataResult) >> true
-        0 * _
-    }
-
-    def "retrieves and caches dependencies"() {
-        when:
-        repo.getDependency(dep, metaDataResult)
-
-        then:
-        1 * cache.supplyMetaData(lib, metaDataResult) >> false
-        1 * delegate.getDependency(dep, metaDataResult)
-        1 * cache.newDependencyResult(lib, metaDataResult)
-        0 * _
-    }
-
-    def "uses dependencies from cache"() {
-        when:
-        repo.getDependency(dep, metaDataResult)
-
-        then:
-        1 * cache.supplyMetaData(lib, metaDataResult) >> true
-        0 * _
-    }
-
-    def "delegates request for module artifacts"() {
-        def moduleMetaData = Stub(ModuleVersionMetaData)
-        def context = Stub(ArtifactResolveContext)
-        def result = Mock(BuildableArtifactSetResolveResult)
-
-        when:
-        repo.resolveModuleArtifacts(moduleMetaData, context, result)
-
-        then:
-        1 * delegate.resolveModuleArtifacts(moduleMetaData, context, result)
-        0 * _
-    }
-
-    def "retrieves and caches artifacts"() {
-        def result = Mock(BuildableArtifactResolveResult)
-        def artifactId = Stub(ModuleVersionArtifactIdentifier)
-        def artifact = Stub(ModuleVersionArtifactMetaData) {
-            getId() >> artifactId
-        }
-        def moduleSource = Mock(ModuleSource)
-
-        when:
-        repo.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * cache.supplyArtifact(artifactId, result) >> false
-        1 * delegate.resolveArtifact(artifact, moduleSource, result)
-        1 * cache.newArtifact(artifactId, result)
-        0 * _
-    }
-
-    def "uses artifacts from cache"() {
-        def result = Mock(BuildableArtifactResolveResult)
-        def artifactId = Stub(ModuleVersionArtifactIdentifier)
-        def artifact = Stub(ModuleVersionArtifactMetaData) {
-            getId() >> artifactId
-        }
-        def moduleSource = Mock(ModuleSource)
-
-        when:
-        repo.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * cache.supplyArtifact(artifactId, 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
deleted file mode 100644
index e04bf62..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/DependencyMetadataCacheTest.groovy
+++ /dev/null
@@ -1,225 +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.memcache
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionMetaDataResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.BuildableModuleVersionSelectionResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleSource
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleVersionListing
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import spock.lang.Specification
-
-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 local and remote module versions"() {
-        def remoteListing = Mock(ModuleVersionListing)
-        def localListing = Mock(ModuleVersionListing)
-        def result = Mock(BuildableModuleVersionSelectionResolveResult)
-        def missingResult = Mock(BuildableModuleVersionSelectionResolveResult)
-
-        given:
-        cache.newModuleVersions(newSelector("org", "foo-remote", "1.0"), Stub(BuildableModuleVersionSelectionResolveResult) {
-            getState() >> BuildableModuleVersionSelectionResolveResult.State.Listed
-            getVersions() >> remoteListing
-        })
-        cache.newLocalModuleVersions(newSelector("org", "foo-local", "1.0"), Stub(BuildableModuleVersionSelectionResolveResult) {
-            getState() >> BuildableModuleVersionSelectionResolveResult.State.Listed
-            getVersions() >> localListing
-        })
-
-        when:
-        def local = cache.supplyLocalModuleVersions(newSelector("org", "foo-local", "1.0"), result)
-        def missingLocal = cache.supplyLocalModuleVersions(newSelector("org", "foo-remote", "1.0"), missingResult)
-
-        then:
-        local
-        1 * result.listed(localListing)
-
-        and:
-        !missingLocal
-        0 * missingResult._
-
-        when:
-        def remote = cache.supplyModuleVersions(newSelector("org", "foo-remote", "1.0"), result)
-        def missingRemote = cache.supplyModuleVersions(newSelector("org", "foo-local", "1.0"), missingResult)
-
-        then:
-        remote
-        1 * result.listed(remoteListing)
-
-        and:
-        !missingRemote
-        0 * missingResult._
-    }
-
-    def "does not cache failed module version listing"() {
-        def failedResult = Stub(BuildableModuleVersionSelectionResolveResult) {
-            getState() >> BuildableModuleVersionSelectionResolveResult.State.Failed
-        }
-        cache.newModuleVersions(newSelector("org", "lib", "1.0"), failedResult)
-        cache.newLocalModuleVersions(newSelector("org", "lib", "1.0"), failedResult)
-
-        def result = Mock(BuildableModuleVersionSelectionResolveResult)
-
-        when:
-        def remoteFromCache = cache.supplyModuleVersions(newSelector("org", "lib", "1.0"), result)
-        def localFromCache = cache.supplyLocalModuleVersions(newSelector("org", "lib", "1.0"), result)
-
-        then:
-        !remoteFromCache
-        !localFromCache
-        0 * result._
-    }
-
-    def "caches and supplies remote metadata"() {
-        def suppliedMetaData = Stub(MutableModuleVersionMetaData)
-        def cachedCopy = Stub(MutableModuleVersionMetaData)
-        def originalMetaData = Stub(MutableModuleVersionMetaData)
-        def source = Stub(ModuleSource)
-        def resolvedResult = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
-            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
-            getMetaData() >> originalMetaData
-            getModuleSource() >> source
-        }
-        def result = Mock(BuildableModuleVersionMetaDataResolveResult.class)
-
-        given:
-        _ * originalMetaData.copy() >> cachedCopy
-        cache.newDependencyResult(newSelector("org", "foo", "1.0"), resolvedResult)
-
-        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
-        _ * cachedCopy.copy() >> suppliedMetaData
-        1 * result.resolved(suppliedMetaData, source)
-    }
-
-    def "caches and supplies remote and local metadata"() {
-        def localSource = Stub(ModuleSource)
-        def localMetaData = Stub(MutableModuleVersionMetaData)
-        _ * localMetaData.copy() >> localMetaData
-        def remoteSource = Stub(ModuleSource)
-        def remoteMetaData = Stub(MutableModuleVersionMetaData)
-        _ * remoteMetaData.copy() >> remoteMetaData
-        def resolvedLocal = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
-            getMetaData() >> localMetaData
-            getModuleSource() >> localSource
-            getState() >> BuildableModuleVersionMetaDataResolveResult.State.Resolved
-        }
-        def resolvedRemote = Mock(BuildableModuleVersionMetaDataResolveResult.class) {
-            getMetaData() >> remoteMetaData
-            getModuleSource() >> remoteSource
-            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)
-
-        then:
-        local
-        stats.metadataServed == 1
-        1 * result.resolved(localMetaData, localSource)
-
-        when:
-        def remote = cache.supplyMetaData(newSelector("org", "remote", "1.0"), result)
-
-        then:
-        remote
-        stats.metadataServed == 2
-        1 * result.resolved(remoteMetaData, remoteSource)
-    }
-
-    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 fooId = Stub(ModuleVersionArtifactIdentifier)
-        def fooFile = new File("foo")
-        def fooResult = Mock(BuildableArtifactResolveResult) { getFile() >> fooFile }
-        def anotherFooResult = Mock(BuildableArtifactResolveResult)
-
-        def differentId = Stub(ModuleVersionArtifactIdentifier)
-        def differentResult = Mock(BuildableArtifactResolveResult)
-
-        cache.newArtifact(fooId, fooResult)
-
-        when:
-        def differentCached = cache.supplyArtifact(differentId, differentResult )
-
-        then:
-        !differentCached
-        0 * differentResult._
-
-        when:
-        def fooCached = cache.supplyArtifact(fooId, anotherFooResult )
-
-        then:
-        fooCached
-        1 * anotherFooResult.resolved(fooFile)
-    }
-
-    def "does not cache failed artifact resolves"() {
-        def artifactId = Stub(ModuleVersionArtifactIdentifier)
-        def failedResult = Stub(BuildableArtifactResolveResult) { getFailure() >> new ArtifactResolveException("bad") }
-        cache.newArtifact(artifactId, failedResult)
-
-        def result = Mock(BuildableArtifactResolveResult)
-
-        when:
-        def fromCache = cache.supplyArtifact(artifactId, result)
-
-        then:
-        !fromCache
-        0 * result._
-    }
-}
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
deleted file mode 100644
index fe3067b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryDependencyMetadataCacheTest.groovy
+++ /dev/null
@@ -1,76 +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.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/AbstractGradlePomModuleDescriptorParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractGradlePomModuleDescriptorParserTest.groovy
deleted file mode 100644
index 7063de6..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractGradlePomModuleDescriptorParserTest.groovy
+++ /dev/null
@@ -1,76 +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.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.ModuleRevisionId
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-abstract class AbstractGradlePomModuleDescriptorParserTest extends Specification {
-    @Rule
-    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    final GradlePomModuleDescriptorParser parser = new GradlePomModuleDescriptorParser()
-    final parseContext = Mock(DescriptorParseContext)
-    TestFile pomFile
-
-    def "setup"() {
-        pomFile = tmpDir.file('foo')
-    }
-
-    protected ModuleDescriptor parsePom() {
-        parseMetaData().descriptor
-    }
-
-    protected MutableModuleVersionMetaData parseMetaData() {
-        parser.parseMetaData(parseContext, pomFile, true)
-    }
-
-    protected void hasArtifact(ModuleDescriptor descriptor, String name, String type, String ext, String classifier = null) {
-        assert descriptor.allArtifacts.length == 1
-        def artifact = descriptor.allArtifacts.first()
-        assert artifact.id == artifactId(descriptor.moduleRevisionId, name, type, ext)
-        assert artifact.extraAttributes['classifier'] == classifier
-    }
-
-    protected void hasDefaultDependencyArtifact(DependencyDescriptor descriptor) {
-        assert descriptor.allDependencyArtifacts.length == 0
-    }
-
-    protected void hasDependencyArtifact(DependencyDescriptor descriptor, String name, String type, String ext, String classifier = null) {
-        assert descriptor.allDependencyArtifacts.length == 1
-        def artifact = descriptor.allDependencyArtifacts.first()
-        assert artifact.name == name
-        assert artifact.type == type
-        assert artifact.ext == ext
-        assert artifact.extraAttributes['classifier'] == classifier
-    }
-
-    protected ModuleRevisionId moduleId(String group, String name, String version) {
-        IvyUtil.createModuleRevisionId(group, name, version)
-    }
-
-    protected ArtifactRevisionId artifactId(ModuleRevisionId moduleId, String name, String type, String ext) {
-        ArtifactRevisionId.newInstance(moduleId, name, type, ext)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.groovy
deleted file mode 100644
index d7f16f9..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.groovy
+++ /dev/null
@@ -1,71 +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.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt
-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 org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-abstract class AbstractPomReaderTest extends Specification {
-    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    PomReader pomReader
-    TestFile pomFile
-    LocallyAvailableExternalResource locallyAvailableExternalResource
-
-    def setup() {
-        pomFile = tmpDir.file('pom.xml')
-        pomFile.createFile()
-        LocallyAvailableResource locallyAvailableResource = new DefaultLocallyAvailableResource(pomFile)
-        locallyAvailableExternalResource = new DefaultLocallyAvailableExternalResource(pomFile.toURI().toURL().toString(), locallyAvailableResource)
-    }
-
-    protected void assertResolvedPomDependency(MavenDependencyKey key, String version) {
-        assert pomReader.dependencies.containsKey(key)
-        PomReader.PomDependencyData dependency = pomReader.dependencies[key]
-        assertPomDependencyValues(key, version, dependency)
-    }
-
-    protected void assertResolvedPomDependencyManagement(MavenDependencyKey key, String version) {
-        assert pomReader.dependencyMgt.containsKey(key)
-        PomDependencyMgt dependency = pomReader.dependencyMgt[key]
-        assertPomDependencyValues(key, version, dependency)
-    }
-
-    protected void assertPomDependencyValues(MavenDependencyKey key, String version, PomDependencyMgt dependency) {
-        assert dependency.groupId == key.groupId
-        assert dependency.artifactId == key.artifactId
-        assert dependency.type == key.type
-        assert dependency.classifier == key.classifier
-        assert dependency.version == version
-    }
-
-    protected PomReader createPomReader(String pomFileName, String pomDefinition) {
-        TestFile pomFile = tmpDir.file(pomFileName)
-        pomFile.createFile()
-        pomFile << pomDefinition
-        LocallyAvailableResource locallyAvailableResource = new DefaultLocallyAvailableResource(pomFile)
-        LocallyAvailableExternalResource locallyAvailableExternalResource = new DefaultLocallyAvailableExternalResource(pomFile.toURI().toURL().toString(), locallyAvailableResource)
-        return new PomReader(locallyAvailableExternalResource)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.groovy
deleted file mode 100644
index b6ac55f..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.groovy
+++ /dev/null
@@ -1,80 +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.module.descriptor.ModuleDescriptor
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-import org.gradle.api.internal.externalresource.ExternalResource
-import org.gradle.api.internal.externalresource.LocallyAvailableExternalResource
-import org.gradle.internal.resource.local.LocallyAvailableResource
-import spock.lang.Specification
-
-class DisconnectedIvyXmlModuleDescriptorParserTest extends Specification {
-    LocallyAvailableExternalResource localExternalResource = Mock()
-    LocallyAvailableResource localResource = Mock()
-    ExternalResource externalResource = Mock()
-    ResolverStrategy resolverStrategy = Stub()
-    IvyXmlModuleDescriptorParser parser = new DisconnectedIvyXmlModuleDescriptorParser(resolverStrategy)
-    DescriptorParseContext parseContext = Mock()
-
-    def "creates overridden internal Ivy parser"() throws Exception {
-        when:
-        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
-
-        then:
-        localExternalResource.getLocalResource() >> localResource
-        localResource.getFile() >> new File('ivy.xml')
-        disconnectedParser != null
-        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
-    }
-
-    def "creates new internal Ivy parser"() throws Exception {
-        when:
-        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
-
-        and:
-        IvyXmlModuleDescriptorParser.Parser newDisconnectedParser = disconnectedParser.newParser(externalResource, new File('ivy.xml').toURI().toURL())
-
-        then:
-        localExternalResource.getLocalResource() >> localResource
-        localResource.getFile() >> new File('ivy.xml')
-        disconnectedParser != null
-        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
-        newDisconnectedParser != null
-        newDisconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
-    }
-
-    def "parses other Ivy file and return with standard module descriptor"() throws Exception {
-        when:
-        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
-
-        and:
-        ModuleDescriptor moduleDescriptor = disconnectedParser.parseOtherIvyFile('myorg', 'parentMod', 'parentRev')
-
-        then:
-        localExternalResource.getLocalResource() >> localResource
-        localResource.getFile() >> new File('ivy.xml')
-        disconnectedParser
-        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
-        moduleDescriptor != null
-        moduleDescriptor.status == 'release'
-        moduleDescriptor.publicationDate != null
-        moduleDescriptor.moduleRevisionId.organisation == 'myorg'
-        moduleDescriptor.moduleRevisionId.moduleId.name == 'parentMod'
-        moduleDescriptor.moduleRevisionId.revision == 'parentRev'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy
deleted file mode 100644
index e19dbfa..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy
+++ /dev/null
@@ -1,827 +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.gradle.api.internal.artifacts.resolution.MavenPomArtifact
-import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource
-import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
-
-class GradlePomModuleDescriptorParserProfileTest extends AbstractGradlePomModuleDescriptorParserTest {
-    def "pom with project coordinates defined by active profile properties"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>\${some.group}</groupId>
-    <artifactId>\${some.artifact}</artifactId>
-    <version>\${some.version}</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <some.group>group-one</some.group>
-                <some.artifact>artifact-one</some.artifact>
-                <some.version>version-one</some.version>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 0
-    }
-
-    def "pom with dependency coordinates defined by active profile properties"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${some.group}</groupId>
-            <artifactId>\${some.artifact}</artifactId>
-            <version>\${some.version}</version>
-        </dependency>
-    </dependencies>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <some.group>group-two</some.group>
-                <some.artifact>artifact-two</some.artifact>
-                <some.version>version-two</some.version>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDefaultDependencyArtifact(descriptor.dependencies.first())
-    }
-
-    def "uses parent properties from active profile to provide default values for a dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <some.group>group-two</some.group>
-                <some.artifact>artifact-two</some.artifact>
-                <some.version>version-two</some.version>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${some.group}</groupId>
-            <artifactId>\${some.artifact}</artifactId>
-            <version>\${some.version}</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact.class) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses grand parent properties from active profile to provide default values for a dependency"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <some.group>group-two</some.group>
-                <some.artifact>artifact-two</some.artifact>
-                <some.version>version-two</some.version>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${some.group}</groupId>
-            <artifactId>\${some.artifact}</artifactId>
-            <version>\${some.version}</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses dependency management section in active profile 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>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <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>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        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 == createModuleId('group-three', 'artifact-three')
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom dependency management section in active profile to provide default values for a dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>1.2</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses grand parent pom dependency management section in active profile to provide default values for a dependency"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>1.2</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses dependency management section from imported POM in active profile to define defaults for main POM body dependency"() {
-        given:
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        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>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>different-group</groupId>
-                        <artifactId>imported</artifactId>
-                        <version>different-version</version>
-                        <type>pom</type>
-                        <scope>import</scope>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "pom with dependency defined by active profile"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDefaultDependencyArtifact(descriptor.dependencies.first())
-    }
-
-    def "uses parent dependency from active profile"() {
-        given:
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses grand parent dependency from active profile"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>1.2</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent dependency over grand parent dependency with same groupId and artifactId from active profile"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>1.2</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <profiles>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>1.3</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.3')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses dependency management section from imported POM in active profile to define defaults for profile dependency"() {
-        given:
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>different-group</groupId>
-                        <artifactId>imported</artifactId>
-                        <version>different-version</version>
-                        <type>pom</type>
-                        <scope>import</scope>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-}
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
deleted file mode 100644
index 274f3e8..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
+++ /dev/null
@@ -1,2087 +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.gradle.api.internal.artifacts.resolution.MavenPomArtifact
-import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource
-import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
-import spock.lang.Issue
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
-
-class GradlePomModuleDescriptorParserTest extends AbstractGradlePomModuleDescriptorParserTest {
-    def "parses simple pom"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <description>The first test artifact</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>version-two</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDefaultDependencyArtifact(descriptor.dependencies.first())
-        parser.typeName == 'POM'
-        parser.toString() == 'gradle pom parser'
-    }
-
-    def "converts timestamp version to SNAPSHOT version"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>my-version-20141012.121000-1</version>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'my-version-SNAPSHOT')
-    }
-
-    def "merges dependencies declared in pom with those declared in parent"() {
-        given:
-        def parent = tmpDir.file("parent.xlm") << """
-<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>
-            <version>1.2</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-three</artifactId>
-            <version>1.2</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-one</artifactId>
-            <version>11</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-three</artifactId>
-            <version>11</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 3
-
-        def dep1 = descriptor.dependencies[0]
-        dep1.dependencyRevisionId == moduleId('group-two', 'artifact-one', '11')
-        dep1.moduleConfigurations == ['compile', 'runtime']
-
-        def dep2 = descriptor.dependencies[1]
-        dep2.dependencyRevisionId == moduleId('group-two', 'artifact-three', '11')
-        dep2.moduleConfigurations == ['compile', 'runtime']
-
-        def inheritedDep = descriptor.dependencies[2]
-        inheritedDep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.2')
-        inheritedDep.moduleConfigurations == ['compile', 'runtime']
-    }
-
-    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>
-"""
-
-        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 == createModuleId('group-three', 'artifact-three')
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "throws exception if parent pom dependency management section does not provide default values for dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-three</groupId>
-                <artifactId>artifact-three</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        parsePom()
-
-        then:
-        Throwable t = thrown(MetaDataParseException)
-        t.cause instanceof UnresolvedDependencyVersionException
-        t.cause.message == "Unable to resolve version for dependency 'group-two:artifact-two:jar'"
-    }
-
-    def "uses parent pom dependency management section to provide default values for a dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-                <scope>test</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        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']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom dependency management section with multiple versions of same dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.3</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.1</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.1')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses imported child pom over parent pom dependency management section"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.5</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>different-group</groupId>
-                <artifactId>imported</artifactId>
-                <version>different-version</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.5')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses importing pom dependency management over imported pom definition with same group ID and artifact ID "() {
-        given:
-
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.5</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>different-group</groupId>
-                <artifactId>imported</artifactId>
-                <version>different-version</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses child dependency over parent dependency with same group ID and artifact ID"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<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>
-            <version>1.5</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>1.2</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom dependency with multiple versions of same dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<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>
-            <version>1.3</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>1.5</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>1.4</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.4')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom over grand parent pom dependency management section"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.5</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses grand parent pom properties for parent pom dependency management section"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <properties>
-        <grandparent.groupid>group-two</grandparent.groupid>
-        <grandparent.artifactid>artifact-two</grandparent.artifactid>
-    </properties>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>\${grandparent.groupid}</groupId>
-                <artifactId>\${grandparent.artifactid}</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom properties over grand parent pom properties for dependency management if overridden"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <properties>
-        <my.groupid>group-three</my.groupid>
-        <my.artifactid>artifact-three</my.artifactid>
-    </properties>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <properties>
-        <my.groupid>group-two</my.groupid>
-        <my.artifactid>artifact-two</my.artifactid>
-    </properties>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>\${my.groupid}</groupId>
-                <artifactId>\${my.artifactid}</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses properties from parent pom to replace variable placeholders in pom"() {
-        given:
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <properties>
-        <artifact-two.version>v2</artifact-two.version>
-        <artifact-three.version>ignore-me</artifact-three.version>
-    </properties>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <properties>
-        <artifact-three.version>v3</artifact-three.version>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>\${artifact-two.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>group-three</groupId>
-            <artifactId>artifact-three</artifactId>
-            <version>\${artifact-three.version}</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 2
-        def artifactTwo = descriptor.dependencies[0]
-        artifactTwo.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'v2')
-        hasDefaultDependencyArtifact(artifactTwo)
-        def artifactThree = descriptor.dependencies[1]
-        artifactThree.dependencyRevisionId == moduleId('group-three', 'artifact-three', 'v3')
-        hasDefaultDependencyArtifact(artifactThree)
-    }
-
-    def "replaces variable placeholders in parent pom dependency management section"() {
-        given:
-        def grandParent = tmpDir.file("grand-parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>\${project.groupId}</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>\${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>\${project.groupId}</groupId>
-                <artifactId>artifact-three</artifactId>
-                <version>\${project.version}</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${project.groupId}</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>group-one</groupId>
-            <artifactId>artifact-three</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 2
-        def artifactTwo = descriptor.dependencies[0]
-        artifactTwo.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
-        artifactTwo.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(artifactTwo)
-        def artifactThree = descriptor.dependencies[1]
-        artifactThree.dependencyRevisionId == moduleId('group-one', 'artifact-three', 'version-one')
-        artifactThree.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(artifactThree)
-    }
-
-    @Issue("GRADLE-2918")
-    def "uses imported BOM in grand parent dependency management section"() {
-        given:
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <properties>
-        <myversion>some-version</myversion>
-    </properties>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-one</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>\${myversion}</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        def grandParent = tmpDir.file("grand-parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>different-group</groupId>
-                <artifactId>imported</artifactId>
-                <version>different-version</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xlm") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-one</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def artifactTwo = descriptor.dependencies[0]
-        artifactTwo.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'some-version')
-        artifactTwo.moduleConfigurations == ['compile', 'runtime']
-    }
-
-
-
-    def "pom with dependency with classifier"() {
-        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>
-            <version>version-two</version>
-            <classifier>classifier-two</classifier>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDependencyArtifact(descriptor.dependencies.first(), 'artifact-two', 'jar', 'jar', 'classifier-two')
-    }
-
-    @Issue("GRADLE-2068")
-    def "pom with dependency with empty classifier is treated like dependency without classifier"() {
-        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>
-            <version>version-two</version>
-            <classifier></classifier>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDefaultDependencyArtifact(descriptor.dependencies.first())
-    }
-
-    @Issue("GRADLE-2076")
-    def "pom with packaging of type eclipse-plugin creates jar artifact"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <packaging>eclipse-plugin</packaging>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'eclipse-plugin', 'jar')
-        descriptor.dependencies.length == 0
-    }
-
-    def "fails when POM is not well formed XML"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion
-</project>
-"""
-
-        when:
-        parseMetaData()
-
-        then:
-        def e = thrown(MetaDataParseException)
-        e.message == "Could not parse POM ${pomFile.toURI()}"
-        e.cause.message.contains('Element type "modelVersion"')
-    }
-
-
-    def "pom with no dependencies "() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <packaging>pom</packaging>
-</project>
-"""
-
-        when:
-        def metaData = parseMetaData()
-        def descriptor = metaData.descriptor
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        descriptor.dependencies.length == 0
-    }
-
-    def "pom with packaging 'pom'"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <packaging>pom</packaging>
-</project>
-"""
-
-        when:
-        def metaData = parseMetaData()
-        def descriptor = metaData.descriptor
-
-        then:
-        descriptor.allArtifacts.length == 0
-        metaData.metaDataOnly //flag to indicate that there may not actually be any artifacts
-    }
-
-    def "pom with project coordinates defined by custom properties"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>\${some.group}</groupId>
-    <artifactId>\${some.artifact}</artifactId>
-    <version>\${some.version}</version>
-    <properties>
-        <some.group>group-one</some.group>
-        <some.artifact>artifact-one</some.artifact>
-        <some.version>version-one</some.version>
-    </properties>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 0
-    }
-
-    def "pom with dependency coordinates defined by custom properties"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <properties>
-        <some.group>group-two</some.group>
-        <some.artifact>artifact-two</some.artifact>
-        <some.version>version-two</some.version>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${some.group}</groupId>
-            <artifactId>\${some.artifact}</artifactId>
-            <version>\${some.version}</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        hasDefaultDependencyArtifact(descriptor.dependencies.first())
-    }
-
-    def "uses parent pom over grand parent pom dependency"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>1.5</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>1.2</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses grand parent pom properties for parent pom dependency"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <properties>
-        <grandparent.groupid>group-two</grandparent.groupid>
-        <grandparent.artifactid>artifact-two</grandparent.artifactid>
-    </properties>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${grandparent.groupid}</groupId>
-            <artifactId>\${grandparent.artifactid}</artifactId>
-            <version>1.2</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "uses parent pom properties over grand parent pom properties for dependency if overridden"() {
-        given:
-        def grandParent = tmpDir.file("grandparent.xml") << """
-<project>
-    <groupId>different-group</groupId>
-    <artifactId>grandparent</artifactId>
-    <version>different-version</version>
-
-    <properties>
-        <my.groupid>group-three</my.groupid>
-        <my.artifactid>artifact-three</my.artifactid>
-    </properties>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>different-group</groupId>
-        <artifactId>grandparent</artifactId>
-        <version>different-version</version>
-    </parent>
-
-    <properties>
-        <my.groupid>group-two</my.groupid>
-        <my.artifactid>artifact-two</my.artifactid>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${my.groupid}</groupId>
-            <artifactId>\${my.artifactid}</artifactId>
-            <version>1.2</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(grandParent)) }
-
-        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 == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-
-    def "defines relocation but doesn't include any explicit dependencies or artifacts"() {
-        given:
-        def relocated = tmpDir.file("relocated.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-relocated</groupId>
-    <artifactId>relocated</artifactId>
-    <version>version-one</version>
-</project>
-"""
-
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-three</groupId>
-            <artifactId>artifact-three</artifactId>
-            <version>1.3</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <distributionManagement>
-        <relocation>
-            <groupId>group-relocated</groupId>
-            <artifactId>relocated</artifactId>
-        </relocation>
-    </distributionManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-        parseContext.getMetaDataArtifact({ it.name == 'relocated' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(relocated.toURI().toURL().toString(), new DefaultLocallyAvailableResource(relocated)) }
-
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-relocated', 'relocated', 'version-one')
-        dep.moduleConfigurations == ['default', 'master', 'compile', 'provided', 'runtime', 'system', 'sources', 'javadoc', 'optional']
-        hasDefaultDependencyArtifact(dep)
-
-        and:
-        descriptor.allArtifacts.length == 0
-    }
-
-    @Issue("GRADLE-2931")
-    def "handles dependencies with same group ID and artifact ID but different type and classifier"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.1</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>test-jar</type>
-                <version>1.2</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <artifactId>artifact-one</artifactId>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 2
-        def depCompile = descriptor.dependencies[0]
-        depCompile.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.1')
-        depCompile.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(depCompile)
-        def depTest = descriptor.dependencies[1]
-        depTest.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.2')
-        depTest.moduleConfigurations == ['test']
-        hasDependencyArtifact(depTest, 'artifact-two', 'test-jar', 'jar', 'tests')
-    }
-
-    @Issue("GRADLE-2931")
-    def "throws exception if parent dependency management doesn't provide correct defaults for dependency"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>parent</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.1</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <artifactId>artifact-one</artifactId>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        parsePom()
-
-        then:
-        Throwable t = thrown(MetaDataParseException)
-        t.cause instanceof UnresolvedDependencyVersionException
-        t.cause.message == "Unable to resolve version for dependency 'group-two:artifact-two:test-jar'"
-    }
-
-    @Issue("GRADLE-2931")
-    def "picks version of last dependency defined by artifact ID, group ID, type and classifier"() {
-        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>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-two</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-three</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-four</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies[0]
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-four')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDependencyArtifact(dep, 'artifact-two', 'jar', 'jar', 'myjar')
-    }
-
-    @Issue("GRADLE-2931")
-    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier"() {
-        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>
-            <version>version-two</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-three</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <version>version-four</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <classifier>test</classifier>
-            <version>version-five</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>ejb-client</type>
-            <classifier>client</classifier>
-            <version>version-six</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 5
-        def defDep = descriptor.dependencies[0]
-        defDep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        defDep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(defDep)
-        def depJar = descriptor.dependencies[1]
-        depJar.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-three')
-        depJar.moduleConfigurations == ['compile', 'runtime']
-        hasDependencyArtifact(depJar, 'artifact-two', 'jar', 'jar', 'myjar')
-        def depTestJar = descriptor.dependencies[2]
-        depTestJar.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-four')
-        depTestJar.moduleConfigurations == ['compile', 'runtime']
-        hasDependencyArtifact(depTestJar, 'artifact-two', 'test-jar', 'jar', 'tests')
-        def depTestJarWithClassifier = descriptor.dependencies[3]
-        depTestJarWithClassifier.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-five')
-        depTestJarWithClassifier.moduleConfigurations == ['compile', 'runtime']
-        hasDependencyArtifact(depTestJarWithClassifier, 'artifact-two', 'test-jar', 'jar', 'test')
-        def depEjbClient = descriptor.dependencies[4]
-        depEjbClient.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-six')
-        depEjbClient.moduleConfigurations == ['compile', 'runtime']
-        hasDependencyArtifact(depEjbClient, 'artifact-two', 'ejb-client', 'ejb-client', 'client')
-    }
-
-    @Issue("GRADLE-2371")
-    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier and scope"() {
-        given:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-    <dependencies>
-        <dependency>
-            <groupId>group-one</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>version-one</version>
-        </dependency>
-        <dependency>
-            <groupId>group-one</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <version>version-one</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${project.groupId}</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>\${project.groupId}</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>
-"""
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
-        hasArtifact(descriptor, 'artifact-one', 'jar', 'jar')
-        descriptor.dependencies.length == 2
-        def defDep = descriptor.dependencies[0]
-        defDep.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
-        defDep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(defDep)
-        def depJar = descriptor.dependencies[1]
-        depJar.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
-        depJar.moduleConfigurations == ['test']
-        hasDependencyArtifact(depJar, 'artifact-two', 'test-jar', 'jar', 'tests')
-    }
-
-    @Issue("GRADLE-2938")
-    def "uses default dependency type if only the dependency management or dependency element declares it"() {
-        given:
-        def parent = tmpDir.file("parent.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-                <type>jar</type>
-            </dependency>
-            <dependency>
-                <groupId>group-three</groupId>
-                <artifactId>artifact-three</artifactId>
-                <version>version-three</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-one</groupId>
-        <artifactId>parent</artifactId>
-        <version>version-one</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>group-three</groupId>
-            <artifactId>artifact-three</artifactId>
-            <type>jar</type>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(parent.toURI().toURL().toString(), new DefaultLocallyAvailableResource(parent)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 2
-        def depGroupTwo = descriptor.dependencies[0]
-        depGroupTwo.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
-        depGroupTwo.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(depGroupTwo)
-        def depGroupThree = descriptor.dependencies[1]
-        depGroupThree.dependencyRevisionId == moduleId('group-three', 'artifact-three', 'version-three')
-        depGroupThree.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(depGroupThree)
-    }
-
-    @Issue("GRADLE-2982")
-    def "use imported pom even though dependency is declared multiple times with different scopes"() {
-        given:
-        def imported = tmpDir.file("imported.xml") << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>different-group</groupId>
-    <artifactId>imported</artifactId>
-    <version>different-version</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>1.5</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>different-group</groupId>
-                <artifactId>imported</artifactId>
-                <version>different-version</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-            <dependency>
-                <groupId>different-group</groupId>
-                <artifactId>imported</artifactId>
-                <version>different-version</version>
-                <type>pom</type>
-                <scope>provided</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        and:
-        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MavenPomArtifact) >> { new DefaultLocallyAvailableExternalResource(imported.toURI().toURL().toString(), new DefaultLocallyAvailableResource(imported)) }
-
-        when:
-        def descriptor = parsePom()
-
-        then:
-        descriptor.dependencies.length == 1
-        def dep = descriptor.dependencies.first()
-        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.5')
-        dep.moduleConfigurations == ['compile', 'runtime']
-        hasDefaultDependencyArtifact(dep)
-    }
-}
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
deleted file mode 100644
index f4568d2..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
+++ /dev/null
@@ -1,761 +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.ivyresolve.parser
-import org.apache.ivy.core.module.descriptor.*
-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.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-import org.gradle.api.internal.artifacts.resolution.IvyDescriptorArtifact
-import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource
-import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Resources
-import org.junit.Rule
-import spock.lang.Issue
-import spock.lang.Specification
-
-import static org.junit.Assert.*
-
-class IvyXmlModuleDescriptorParserTest extends Specification {
-    @Rule
-    public final Resources resources = new Resources()
-    @Rule TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-
-    ResolverStrategy resolverStrategy = Stub()
-    IvyXmlModuleDescriptorParser parser = new IvyXmlModuleDescriptorParser(resolverStrategy)
-    DescriptorParseContext parseContext = Mock()
-
-    def setup() {
-        resolverStrategy.getPatternMatcher("exact") >> ExactPatternMatcher.INSTANCE
-        resolverStrategy.getPatternMatcher("glob") >> GlobPatternMatcher.INSTANCE
-        resolverStrategy.getPatternMatcher("regexp") >> RegexpPatternMatcher.INSTANCE
-    }
-
-    def "parses Ivy descriptor with empty dependencies section"() throws Exception {
-        when:
-        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.parseMetaData(parseContext, file, true).descriptor
-
-        then:
-        md != null
-        md.moduleRevisionId.organisation == "myorg"
-        md.moduleRevisionId.name == "mymodule"
-        md.moduleRevisionId.revision == "myrev"
-        md.status == "integration"
-        md.configurations*.name == ["default"]
-        md.getArtifacts("default") != null
-        md.getArtifacts("default").length == 1
-        md.getArtifacts("default")[0].name == "mymodule"
-        md.getArtifacts("default")[0].type == "jar"
-        md.dependencies.length == 0
-        md.inheritedDescriptors.length == 0
-    }
-
-    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.parseMetaData(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.parseMetaData(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.parseMetaData(parseContext, file, true)
-
-        then:
-        def e = thrown(MetaDataParseException)
-        e.message == "Could not parse Ivy file ${file.toURI()}"
-        e.cause.message.contains('Element type "info"')
-    }
-
-    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.parseMetaData(parseContext, file, true)
-
-        then:
-        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 "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.parseMetaData(parseContext, file, true).descriptor
-
-        then:
-        assertNotNull(md)
-        assertEquals("myorg", md.getModuleRevisionId().getOrganisation())
-        assertEquals("mymodule", md.getModuleRevisionId().getName())
-        assertEquals("myrev", md.getModuleRevisionId().getRevision())
-        assertEquals("integration", md.getStatus())
-        Date pubdate = new GregorianCalendar(2004, 10, 1, 11, 0, 0).getTime()
-        assertEquals(pubdate, md.getPublicationDate())
-
-        License[] licenses = md.getLicenses()
-        assertEquals(1, licenses.length)
-        assertEquals("MyLicense", licenses[0].getName())
-        assertEquals("http://www.my.org/mymodule/mylicense.html", licenses[0].getUrl())
-
-        assertEquals("http://www.my.org/mymodule/", md.getHomePage())
-        assertEquals("This module is <b>great</b> !<br/>\n\t"
-                + "You can use it especially with myconf1 and myconf2, "
-                + "and myconf4 is not too bad too.",
-                md.getDescription().replaceAll("\r\n", "\n").replace('\r', '\n'))
-
-        assertEquals(1, md.getExtraInfo().size())
-        assertEquals("56576", md.getExtraInfo().get("e:someExtra"))
-
-        Configuration[] confs = md.getConfigurations()
-        assertNotNull(confs)
-        assertEquals(5, confs.length)
-
-        assertConf(md, "myconf1", "desc 1", Configuration.Visibility.PUBLIC, new String[0])
-        assertConf(md, "myconf2", "desc 2", Configuration.Visibility.PUBLIC, new String[0])
-        assertConf(md, "myconf3", "desc 3", Configuration.Visibility.PRIVATE, new String[0])
-        assertConf(md, "myconf4", "desc 4", Configuration.Visibility.PUBLIC, ["myconf1", "myconf2"].toArray(new String[2]))
-        assertConf(md, "myoldconf", "my old desc", Configuration.Visibility.PUBLIC, new String[0])
-
-        assertArtifacts(md.getArtifacts("myconf1"), ["myartifact1", "myartifact2", "myartifact3", "myartifact4"].toArray(new String[4]))
-
-        assertArtifacts(md.getArtifacts("myconf2"), ["myartifact1", "myartifact3"].toArray(new String[2]))
-        assertArtifacts(md.getArtifacts("myconf3"), ["myartifact1", "myartifact3", "myartifact4"].toArray(new String[3]))
-        assertArtifacts(md.getArtifacts("myconf4"), ["myartifact1"].toArray(new String[1]))
-
-        DependencyDescriptor[] dependencies = md.getDependencies()
-        assertNotNull(dependencies)
-        assertEquals(13, dependencies.length)
-
-        verifyFullDependencies(dependencies)
-
-        ExcludeRule[] rules = md.getAllExcludeRules()
-        assertNotNull(rules)
-        assertEquals(2, rules.length)
-        assertEquals(GlobPatternMatcher.INSTANCE, rules[0].getMatcher())
-        assertEquals(ExactPatternMatcher.INSTANCE, rules[1].getMatcher())
-        assertEquals(Arrays.asList("myconf1"), Arrays.asList(rules[0]
-                .getConfigurations()))
-        assertEquals(Arrays.asList("myconf1", "myconf2", "myconf3", "myconf4", "myoldconf"), Arrays.asList(rules[1].getConfigurations()))
-        md.inheritedDescriptors.length == 0
-    }
-
-    def "merges values from parent Ivy descriptor"() throws Exception {
-        given:
-        def parentFile = temporaryFolder.file("parent.xml") << """
-<ivy-module version="1.0">
-    <info organisation="myorg"
-          module="parent"
-          revision="parentrev"
-          status="integration"
-          publication="20041101110000"
-    />
-    <configurations>
-        <conf name='default'/>
-    </configurations>
-    <dependencies>
-        <dependency conf="*->*" org="deporg" name="depname" rev="deprev"/>
-    </dependencies>
-</ivy-module>
-"""
-        def file = temporaryFolder.file("ivy.xml") << """
-<ivy-module version="1.0">
-    <info module="mymodule" revision="myrev">
-        <extends organisation="myorg" module="parent" revision="parentrev"/>
-    </info>
-</ivy-module>
-"""
-        and:
-        parseContext.getMetaDataArtifact(_, IvyDescriptorArtifact.class) >> new DefaultLocallyAvailableExternalResource(parentFile.toURI().toString(), new DefaultLocallyAvailableResource(parentFile))
-
-        when:
-        ModuleDescriptor md = parser.parseMetaData(parseContext, file, true).descriptor
-
-        then:
-        md != null
-        md.moduleRevisionId.organisation == "myorg"
-        md.moduleRevisionId.name == "mymodule"
-        md.moduleRevisionId.revision == "myrev"
-        md.status == "integration"
-        md.configurations*.name == ["default"]
-        md.dependencies.length == 1
-        md.dependencies[0].dependencyRevisionId.organisation == 'deporg'
-        md.dependencies[0].dependencyRevisionId.name == 'depname'
-        md.dependencies[0].dependencyRevisionId.revision == 'deprev'
-        md.inheritedDescriptors.length == 0
-    }
-
-    @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.parseMetaData(parseContext, file, false).descriptor
-        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"/>
-                    <dependency name="mymodule2" conf="*->*"/>
-                    <dependency name="mymodule2" conf=""/>
-                    <dependency name="mymodule2"/>
-                </dependencies>
-            </ivy-module>
-        """
-
-        when:
-        def descriptor = parser.parseMetaData(parseContext, file, false).descriptor
-
-        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 dependency11 = descriptor.dependencies[10]
-        dependency11.moduleConfigurations == ["*"]
-        dependency11.getDependencyConfigurations("a") == ["*"]
-        dependency11.getDependencyConfigurations("a", "requested") == ["*"]
-
-        def dependency12 = descriptor.dependencies[11]
-        dependency12.moduleConfigurations == ["*"]
-        dependency12.getDependencyConfigurations("a") == ["*"]
-        dependency12.getDependencyConfigurations("a", "requested") == ["*"]
-
-        def dependency13 = descriptor.dependencies[12]
-        dependency13.moduleConfigurations == ["*"]
-        dependency13.getDependencyConfigurations("a") == ["*"]
-        dependency13.getDependencyConfigurations("a", "requested") == ["*"]
-    }
-
-    def "parses dependency config mappings with defaults"() {
-        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 defaultconf="a" defaultconfmapping="a->a1;b->b1,b2">
-                    <dependency name="mymodule2"/>
-                    <dependency name="mymodule2" conf=""/>
-                    <dependency name="mymodule2" conf="a"/>
-                    <dependency name="mymodule2" conf="b"/>
-                    <dependency name="mymodule2" conf="a->other"/>
-                    <dependency name="mymodule2" conf="*->@"/>
-                    <dependency name="mymodule2" conf="c->other"/>
-                    <dependency name="mymodule2" conf="a->"/>
-                </dependencies>
-            </ivy-module>
-        """
-
-        when:
-        def descriptor = parser.parseMetaData(parseContext, file, false).descriptor
-
-        then:
-        def dependency1 = descriptor.dependencies[0]
-        dependency1.moduleConfigurations == ["a"]
-        dependency1.getDependencyConfigurations("a") == ["a1"]
-        dependency1.getDependencyConfigurations("a", "requested") == ["a1"]
-
-        def dependency2 = descriptor.dependencies[1]
-        dependency2.moduleConfigurations == ["a"]
-        dependency2.getDependencyConfigurations("a") == ["a1"]
-        dependency2.getDependencyConfigurations("a", "requested") == ["a1"]
-
-        def dependency3 = descriptor.dependencies[2]
-        dependency3.moduleConfigurations == ["a"]
-        dependency3.getDependencyConfigurations("a") == ["a1"]
-        dependency3.getDependencyConfigurations("a", "requested") == ["a1"]
-
-        def dependency4 = descriptor.dependencies[3]
-        dependency4.moduleConfigurations == ["b"]
-        dependency4.getDependencyConfigurations("b") == ["b1", "b2"]
-        dependency4.getDependencyConfigurations("b", "requested") == ["b1", "b2"]
-
-        def dependency5 = descriptor.dependencies[4]
-        dependency5.moduleConfigurations == ["a"]
-        dependency5.getDependencyConfigurations("a") == ["other"]
-        dependency5.getDependencyConfigurations("a", "requested") == ["other"]
-
-        def dependency6 = descriptor.dependencies[5]
-        dependency6.moduleConfigurations == ["*"]
-        dependency6.getDependencyConfigurations("a") == ["a"]
-        dependency6.getDependencyConfigurations("a", "requested") == ["a"]
-
-        def dependency7 = descriptor.dependencies[6]
-        dependency7.moduleConfigurations == ["c"]
-        dependency7.getDependencyConfigurations("c") == ["other"]
-        dependency7.getDependencyConfigurations("c", "requested") == ["other"]
-
-        def dependency8 = descriptor.dependencies[7]
-        dependency8.moduleConfigurations == ["a"]
-        dependency8.getDependencyConfigurations("a") == ["a1"]
-        dependency8.getDependencyConfigurations("a", "requested") == ["a1"]
-    }
-
-    def "parses artifact config mappings"() {
-        given:
-        def file = temporaryFolder.createFile("ivy.xml")
-        file.text = """
-           <ivy-module version="2.0">
-                <info organisation="myorg"
-                      module="mymodule"
-                      revision="myrev">
-                </info>
-                <configurations>
-                    <conf name="a" />
-                    <conf name="b" />
-                    <conf name="c" extends="a"/>
-                    <conf name="d" />
-                </configurations>
-                <publications>
-                    <artifact/>
-                    <artifact name='art2' type='type' ext='ext' conf='*'/>
-                    <artifact name='art3' type='type2' conf='a, b'/>
-                </publications>
-            </ivy-module>
-        """
-
-        when:
-        def descriptor = parser.parseMetaData(parseContext, file, false).descriptor
-
-        then:
-        descriptor.allArtifacts.length == 3
-        descriptor.allArtifacts[0].configurations == ['a', 'b', 'c', 'd']
-        descriptor.allArtifacts[1].configurations == ['a', 'b', 'c', 'd']
-        descriptor.allArtifacts[2].configurations == ['a', 'b']
-
-        and:
-        descriptor.getArtifacts("a")*.name == ['mymodule', 'art2', 'art3']
-        descriptor.getArtifacts("a")*.type == ['jar', 'type', 'type2']
-        descriptor.getArtifacts("a")*.ext == ['jar', 'ext', 'type2']
-
-        and:
-        descriptor.getArtifacts("b")*.name == ['mymodule', 'art2', 'art3']
-        descriptor.getArtifacts("c")*.name == ['mymodule', 'art2']
-        descriptor.getArtifacts("d")*.name == ['mymodule', 'art2']
-    }
-
-    def verifyFullDependencies(DependencyDescriptor[] dependencies) {
-        // no conf def => equivalent to *->*
-        DependencyDescriptor dd = getDependency(dependencies, "mymodule2")
-        assertNotNull(dd)
-        assertEquals("myorg", dd.getDependencyId().getOrganisation())
-        assertEquals("2.0", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["*"], Arrays.asList(dd.getModuleConfigurations()))
-        assertEquals(["*"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals(["*"], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-        assertFalse(dd.isChanging())
-        assertTrue(dd.isTransitive())
-
-        // changing = true
-        dd = getDependency(dependencies, "mymodule3")
-        assertNotNull(dd)
-        assertTrue(getDependency(dependencies, "mymodule3").isChanging())
-        assertFalse(getDependency(dependencies, "mymodule3").isTransitive())
-        // conf="myconf1" => equivalent to myconf1->myconf1
-        dd = getDependency(dependencies, "yourmodule1")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("trunk", dd.getDependencyRevisionId().getBranch())
-        assertEquals("1.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals("branch1", dd.getDynamicConstraintDependencyRevisionId().getBranch())
-        assertEquals("1+", dd.getDynamicConstraintDependencyRevisionId().getRevision())
-        assertEquals("yourorg#yourmodule1#branch1;1+", dd.getDynamicConstraintDependencyRevisionId().toString())
-
-        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
-        assertEquals(["myconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        // conf="myconf1->yourconf1"
-        dd = getDependency(dependencies, "yourmodule2")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("2+", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
-        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        // conf="myconf1->yourconf1, yourconf2"
-        dd = getDependency(dependencies, "yourmodule3")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("3.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        // conf="myconf1, myconf2->yourconf1, yourconf2"
-        dd = getDependency(dependencies, "yourmodule4")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("4.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(new HashSet(["myconf1", "myconf2"]), new HashSet(
-                Arrays.asList(dd.getModuleConfigurations())))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd
-                .getDependencyConfigurations("myconf1")))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd
-                .getDependencyConfigurations("myconf2")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        // conf="myconf1->yourconf1myconf2->yourconf1, yourconf2"
-        dd = getDependency(dependencies, "yourmodule5")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("5.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["myconf1", "myconf2"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        // conf="*->@"
-        dd = getDependency(dependencies, "yourmodule11")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("11.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["*"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertEquals(["myconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals(["myconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
-        assertEquals(["myconf3"], Arrays.asList(dd.getDependencyConfigurations("myconf3")))
-        assertEquals(["myconf4"], Arrays.asList(dd.getDependencyConfigurations("myconf4")))
-
-        dd = getDependency(dependencies, "yourmodule6")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("latest.integration", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["myconf1", "myconf2"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        dd = getDependency(dependencies, "yourmodule7")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("7.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(new HashSet(["myconf1", "myconf2"]), new HashSet(
-                Arrays.asList(dd.getModuleConfigurations())))
-        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
-        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
-        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
-
-        dd = getDependency(dependencies, "yourmodule8")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("8.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["*"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertDependencyArtifacts(dd, ["myconf1"], ["yourartifact8-1", "yourartifact8-2"])
-        assertDependencyArtifacts(dd, ["myconf2"], ["yourartifact8-1", "yourartifact8-2"])
-        assertDependencyArtifacts(dd, ["myconf3"], ["yourartifact8-1", "yourartifact8-2"])
-        assertDependencyArtifacts(dd, ["myconf4"], ["yourartifact8-1", "yourartifact8-2"])
-        dd = getDependency(dependencies, "yourmodule9")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("9.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(["myconf1", "myconf2", "myconf3"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertDependencyArtifacts(dd, ["myconf1"], ["yourartifact9-1"])
-        assertDependencyArtifacts(dd, ["myconf2"], ["yourartifact9-1", "yourartifact9-2"])
-        assertDependencyArtifacts(dd, ["myconf3"], ["yourartifact9-2"])
-        assertDependencyArtifacts(dd, ["myconf4"], [])
-        assertDependencyArtifactExcludeRules(dd, ["myconf1"], [])
-        assertDependencyArtifactExcludeRules(dd, ["myconf2"], [])
-        assertDependencyArtifactExcludeRules(dd, ["myconf3"], [])
-        assertDependencyArtifactExcludeRules(dd, ["myconf4"], [])
-
-        dd = getDependency(dependencies, "yourmodule10")
-        assertNotNull(dd)
-        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
-        assertEquals("10.1", dd.getDependencyRevisionId().getRevision())
-        assertEquals(new HashSet(["*"]), new HashSet(Arrays.asList(dd.getModuleConfigurations())))
-        assertDependencyArtifactIncludeRules(dd, ["myconf1"], ["your.*", PatternMatcher.ANY_EXPRESSION])
-        assertDependencyArtifactIncludeRules(dd, ["myconf2"], ["your.*", PatternMatcher.ANY_EXPRESSION])
-        assertDependencyArtifactIncludeRules(dd, ["myconf3"], ["your.*", PatternMatcher.ANY_EXPRESSION])
-        assertDependencyArtifactIncludeRules(dd, ["myconf4"], ["your.*", PatternMatcher.ANY_EXPRESSION])
-        assertDependencyArtifactExcludeRules(dd, ["myconf1"], ["toexclude"])
-        assertDependencyArtifactExcludeRules(dd, ["myconf2"], ["toexclude"])
-        assertDependencyArtifactExcludeRules(dd, ["myconf3"], ["toexclude"])
-        assertDependencyArtifactExcludeRules(dd, ["myconf4"], ["toexclude"])
-        true
-    }
-
-    void assertDependencyArtifactExcludeRules(DependencyDescriptor dd, List<String> confs,
-                                              List<String> artifactsNames) {
-        ExcludeRule[] rules = dd.getExcludeRules(confs.toArray(new String[confs.size()]))
-        assertNotNull(rules)
-        assertEquals(artifactsNames.size(), rules.length)
-        for (String artifactName : artifactsNames) {
-            boolean found = false
-            for (int j = 0; j < rules.length; j++) {
-                assertNotNull(rules[j])
-                if (rules[j].getId().getName().equals(artifactName)) {
-                    found = true
-                    break
-                }
-            }
-            assertTrue("dependency exclude not found: " + artifactName, found)
-        }
-    }
-
-    protected void assertDependencyArtifactIncludeRules(DependencyDescriptor dd, List<String> confs,
-                                                        List<String> artifactsNames) {
-        IncludeRule[] dads = dd.getIncludeRules(confs.toArray(new String[confs.size()]))
-        assertNotNull(dads)
-        assertEquals(artifactsNames.size(), dads.length)
-        for (String artifactName : artifactsNames) {
-            boolean found = false
-            for (int j = 0; j < dads.length; j++) {
-                assertNotNull(dads[j])
-                if (dads[j].getId().getName().equals(artifactName)) {
-                    found = true
-                    break
-                }
-            }
-            assertTrue("dependency include not found: " + artifactName, found)
-        }
-    }
-
-    protected void assertArtifacts(Artifact[] artifacts, String[] artifactsNames) {
-        assertNotNull(artifacts)
-        assertEquals(artifactsNames.length, artifacts.length)
-        for (int i = 0; i < artifactsNames.length; i++) {
-            boolean found = false
-            for (int j = 0; j < artifacts.length; j++) {
-                assertNotNull(artifacts[j])
-                if (artifacts[j].getName().equals(artifactsNames[i])) {
-                    found = true
-                    break
-                }
-            }
-            assertTrue("artifact not found: " + artifactsNames[i], found)
-        }
-    }
-
-    protected void assertConf(ModuleDescriptor md, String name, String desc, Configuration.Visibility visibility,
-                              String[] exts) {
-        Configuration conf = md.getConfiguration(name)
-        assertNotNull("configuration not found: " + name, conf)
-        assertEquals(name, conf.getName())
-        assertEquals(desc, conf.getDescription())
-        assertEquals(visibility, conf.getVisibility())
-        assertEquals(Arrays.asList(exts), Arrays.asList(conf.getExtends()))
-    }
-
-    protected DependencyDescriptor getDependency(DependencyDescriptor[] dependencies, String name) {
-        for (int i = 0; i < dependencies.length; i++) {
-            assertNotNull(dependencies[i])
-            assertNotNull(dependencies[i].getDependencyId())
-            if (name.equals(dependencies[i].getDependencyId().getName())) {
-                return dependencies[i]
-            }
-        }
-        return null
-    }
-
-    protected void assertDependencyArtifacts(DependencyDescriptor dd, List<String> confs,
-                                             List<String> artifactsNames) {
-        DependencyArtifactDescriptor[] dads = dd.getDependencyArtifacts(confs.toArray(new String[confs.size()]));
-        assertNotNull(dads);
-        assertEquals(artifactsNames.size(), dads.length);
-        for (String artifactName : artifactsNames) {
-            boolean found = false;
-            for (int j = 0; j < dads.length; j++) {
-                assertNotNull(dads[j]);
-                if (dads[j].getName().equals(artifactName)) {
-                    found = true;
-                    break;
-                }
-            }
-            assertTrue("dependency artifact not found: " + artifactName, found);
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy
deleted file mode 100644
index f040a30..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy
+++ /dev/null
@@ -1,1566 +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.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomProfile
-
-class PomReaderProfileTest extends AbstractPomReaderTest {
-    def "parse POM without active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation/>
-        </profile>
-        <profile>
-            <id>profile-3</id>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.parseActivePomProfiles().size() == 0
-    }
-
-    def "parse POM with multiple active profiles having the same ID"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.parseActivePomProfiles().size() == 4
-    }
-
-    def "parse POM with a mix of active and inactive profiles"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-            <properties>
-                <prop1>myproperty1</prop1>
-            </properties>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <prop2>myproperty2</prop2>
-            </properties>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-three</groupId>
-                        <artifactId>artifact-three</artifactId>
-                        <version>version-three</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-        <profile>
-            <id>profile-3</id>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-            <properties>
-                <prop3>myproperty3</prop3>
-            </properties>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-four</groupId>
-                        <artifactId>artifact-four</artifactId>
-                        <version>version-four</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.parseActivePomProfiles().size() == 1
-        pomReader.properties.size() == 5
-        !pomReader.properties.containsKey('prop1')
-        !pomReader.properties.containsKey('prop3')
-        pomReader.properties['prop2'] == 'myproperty2'
-        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
-        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-four', 'artifact-four', 'jar', null))
-        pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null))
-    }
-
-    def "cannot use POM property to set profile ID"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <properties>
-        <activate.id>profile-1</activate.id>
-    </properties>
-    <profiles>
-        <profile>
-            <id>\${activate.id}</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
-        activePomProfiles.size() == 1
-        activePomProfiles[0].id == '${activate.id}'
-    }
-
-    def "cannot use POM property to control profile activation"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <properties>
-        <activate.profile>true</activate.profile>
-    </properties>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>\${activate.profile}</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
-        activePomProfiles.size() == 0
-        !pomReader.properties.containsKey('groupId.prop')
-        !pomReader.properties.containsKey('artifactId.prop')
-        !pomReader.properties.containsKey('version.prop')
-    }
-
-    def "parse POM with active profile providing properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
-        activePomProfiles.size() == 1
-        PomProfile pomProfile = activePomProfiles[0]
-        pomProfile
-        pomProfile.id == 'profile-1'
-        pomProfile.properties.size() == 3
-        pomProfile.properties['groupId.prop'] == 'group-two'
-        pomProfile.properties['artifactId.prop'] == 'artifact-two'
-        pomProfile.properties['version.prop'] == 'version-two'
-        pomReader.properties.size() == 7
-        pomReader.properties['groupId.prop'] == 'group-two'
-        pomReader.properties['artifactId.prop'] == 'artifact-two'
-        pomReader.properties['version.prop'] == 'version-two'
-    }
-
-    def "parse POM with multiple active profile providing same properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.parseActivePomProfiles().size() == 2
-        pomReader.properties.size() == 7
-        pomReader.properties['groupId.prop'] == 'group-two'
-        pomReader.properties['artifactId.prop'] == 'artifact-two'
-        pomReader.properties['version.prop'] == 'version-two'
-    }
-
-    def "get dependencies with custom properties of active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    def "custom properties from last active profile determines dependency coordinates"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-three</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-
-    def "properties from an active profile override existing POM properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <properties>
-        <groupId.prop>group-two</groupId.prop>
-        <artifactId.prop>artifact-two</artifactId.prop>
-        <version.prop>version-two</version.prop>
-    </properties>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-three</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-
-    def "multiple active profiles can determine different dependency coordinates"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-        <dependency>
-            <groupId>\${otherGroupId.prop}</groupId>
-            <artifactId>\${otherArtifactId.prop}</artifactId>
-            <version>\${otherVersion.prop}</version>
-        </dependency>
-    </dependencies>
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <otherGroupId.prop>group-three</otherGroupId.prop>
-                <otherArtifactId.prop>artifact-three</otherArtifactId.prop>
-                <otherVersion.prop>version-three</otherVersion.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyDep1 = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyDep2 = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 2
-        assertResolvedPomDependency(keyDep1, 'version-two')
-        assertResolvedPomDependency(keyDep2, 'version-three')
-    }
-
-    def "get dependencies management without properties from active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
-        activePomProfiles.size() == 1
-        activePomProfiles[0].getDependencyMgts().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-    }
-
-    def "get dependencies management with properties from active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>\${groupId.prop}</groupId>
-                        <artifactId>\${artifactId.prop}</artifactId>
-                        <version>\${version.prop}</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
-        activePomProfiles.size() == 1
-        activePomProfiles[0].getDependencyMgts().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-    }
-
-    def "finds dependency default with properties defined in main body of POM"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <properties>
-        <groupId.prop>group-two</groupId.prop>
-        <artifactId.prop>artifact-two</artifactId.prop>
-        <version.prop>version-two</version.prop>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-    </dependencies>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>\${groupId.prop}</groupId>
-                        <artifactId>\${artifactId.prop}</artifactId>
-                        <version>\${version.prop}</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
-    }
-
-    def "finds dependency default if declared in active profile"() {
-        when:
-        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>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-three</artifactId>
-        </dependency>
-    </dependencies>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-        !pomReader.findDependencyDefaults(keyGroupThree)
-    }
-
-    def "uses dependency default from active profile over POM dependency default"() {
-        when:
-        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>version-two</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-three</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-three')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-    }
-
-    def "finds dependency default if declared in multiple active profiles"() {
-        when:
-        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>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-three</artifactId>
-        </dependency>
-    </dependencies>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-three</artifactId>
-                        <version>version-three</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 2
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-        assertResolvedPomDependencyManagement(keyGroupThree, 'version-three')
-        pomReader.findDependencyDefaults(keyGroupThree) == pomReader.dependencyMgt[keyGroupThree]
-    }
-
-    def "finds dependency default if declared in parent POM active profile"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-        </profile>
-    </profiles>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-four</groupId>
-            <artifactId>artifact-four</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>group-five</groupId>
-            <artifactId>artifact-five</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-        !pomReader.findDependencyDefaults(keyGroupThree)
-    }
-
-    def "uses child properties over parent properties if defined in active profile with the different values"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-three</groupId.prop>
-                <artifactId.prop>artifact-three</artifactId.prop>
-                <version.prop>version-three</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-four</groupId.prop>
-                <artifactId.prop>artifact-four</artifactId.prop>
-                <version.prop>version-four</version.prop>
-            </properties>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey key = new MavenDependencyKey('group-four', 'artifact-four', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-four')
-    }
-
-    def "gets dependencies declared in a single active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-                <dependency>
-                    <groupId>group-three</groupId>
-                    <artifactId>artifact-three</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 2
-        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
-        assertResolvedPomDependency(keyArtifactThree, 'version-three')
-    }
-
-    def "uses last declaration of dependency with same groupId and artifactId in a single active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-
-    def "gets dependencies declared in a multiple active profiles"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-three</groupId>
-                    <artifactId>artifact-three</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
-            <id>profile-3</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-four</groupId>
-                    <artifactId>artifact-four</artifactId>
-                    <version>version-four</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-        MavenDependencyKey keyArtifactFour = new MavenDependencyKey('group-four', 'artifact-four', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 3
-        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
-        assertResolvedPomDependency(keyArtifactThree, 'version-three')
-        assertResolvedPomDependency(keyArtifactFour, 'version-four')
-    }
-
-    def "uses last declaration of dependency with same groupId and artifactId in multiple active profiles"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-
-    def "gets dependency with provided properties declared in active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <properties>
-                <groupId.prop>group-two</groupId.prop>
-                <artifactId.prop>artifact-two</artifactId.prop>
-                <version.prop>version-two</version.prop>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>\${groupId.prop}</groupId>
-                    <artifactId>\${artifactId.prop}</artifactId>
-                    <version>\${version.prop}</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    def "gets dependency in active profile with provided properties in POM main body"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <properties>
-        <groupId.prop>group-two</groupId.prop>
-        <artifactId.prop>artifact-two</artifactId.prop>
-        <version.prop>version-two</version.prop>
-    </properties>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>\${groupId.prop}</groupId>
-                    <artifactId>\${artifactId.prop}</artifactId>
-                    <version>\${version.prop}</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    def "gets dependency with provided defaults declared in active profile"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencyManagement>
-                <dependencies>
-                    <dependency>
-                        <groupId>group-two</groupId>
-                        <artifactId>artifact-two</artifactId>
-                        <version>version-two</version>
-                    </dependency>
-                </dependencies>
-            </dependencyManagement>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
-    }
-
-    def "gets dependency with provided defaults declared in POM main body"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
-    }
-
-    def "uses active profile dependency over dependency with same groupId and artifactId declared in POM main body"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-
-    <dependency>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </dependency>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-
-    def "uses dependency if declared in parent POM active profile"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    def "uses dependency if declared in parent and child POM active profile"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-
-    <profiles>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-three</groupId>
-                    <artifactId>artifact-three</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 2
-        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
-        assertResolvedPomDependency(keyArtifactThree, 'version-three')
-    }
-
-    def "uses child dependency over parent dependency in POM active profile"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <profiles>
-        <profile>
-            <id>profile-1</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-two</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-
-    <profiles>
-        <profile>
-            <id>profile-2</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>group-two</groupId>
-                    <artifactId>artifact-two</artifactId>
-                    <version>version-three</version>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-three')
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy
deleted file mode 100644
index 0cbe355..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy
+++ /dev/null
@@ -1,818 +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.module.descriptor.License
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey
-import org.xml.sax.SAXParseException
-import spock.lang.Issue
-
-class PomReaderTest extends AbstractPomReaderTest {
-    def "parse POM with invalid XML"() {
-        when:
-        pomFile << """
-<projectx>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <description>The first test artifact</description>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        thrown(MetaDataParseException)
-    }
-
-    def "parse malformed POM"() {
-        when:
-        pomFile << """
-<someothertag>
-    <project>
-        <modelVersion>4.0.0</modelVersion>
-        <groupId>group-one</groupId>
-        <artifactId>artifact-one</artifactId>
-        <version>version-one</version>
-        <name>Test Artifact One</name>
-        <description>The first test artifact</description>
-    </project>
-</someothertag>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        Throwable t = thrown(SAXParseException)
-        t.message == 'project must be the root tag'
-    }
-
-    def "parse simple POM"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <name>Test Artifact One</name>
-    <description>The first test artifact</description>
-    <url>http://www.myproject.com</url>
-    <licenses>
-        <license>
-            <name>The Apache Software License, Version 2.0</name>
-            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
-        </license>
-    </licenses>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.description == 'The first test artifact'
-        pomReader.homePage == 'http://www.myproject.com'
-        pomReader.packaging == 'jar'
-        pomReader.licenses.size() == 1
-        pomReader.licenses[0].name == 'The Apache Software License, Version 2.0'
-        pomReader.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.properties.size() == 4
-        pomReader.properties['parent.version'] == 'version-one'
-        pomReader.properties['parent.groupId'] == 'group-one'
-        pomReader.properties['project.parent.version'] == 'version-one'
-        pomReader.properties['project.parent.groupId'] == 'group-one'
-        pomReader.relocation == null
-    }
-
-    def "use custom properties in POM project coordinates"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>\${groupId.prop}</groupId>
-    <artifactId>\${artifactId.prop}</artifactId>
-    <version>\${version.prop}</version>
-    <name>Test Artifact One</name>
-    <description>The first test artifact</description>
-    <properties>
-        <some.prop1>test1</some.prop1>
-        <some.prop2>test2</some.prop2>
-        <groupId.prop>group-one</groupId.prop>
-        <artifactId.prop>artifact-one</artifactId.prop>
-        <version.prop>version-one</version.prop>
-    </properties>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.description == 'The first test artifact'
-        pomReader.packaging == 'jar'
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 5
-        pomReader.pomProperties.containsKey('some.prop1')
-        pomReader.pomProperties.containsKey('some.prop2')
-        pomReader.properties.size() == 9
-        pomReader.properties.containsKey('some.prop1')
-        pomReader.properties.containsKey('some.prop2')
-    }
-
-    def "get dependencies without custom properties"() {
-        when:
-        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>
-            <version>version-two</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    def "get dependencies with custom properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <properties>
-        <groupId.prop>group-two</groupId.prop>
-        <artifactId.prop>artifact-two</artifactId.prop>
-        <version.prop>version-two</version.prop>
-    </properties>
-    <dependencies>
-        <dependency>
-            <groupId>\${groupId.prop}</groupId>
-            <artifactId>\${artifactId.prop}</artifactId>
-            <version>\${version.prop}</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-two')
-    }
-
-    @Issue("GRADLE-2931")
-    def "picks version of last dependency defined by artifact ID, group ID, type and classifier"() {
-        when:
-        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>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-two</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-three</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-four</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
-
-        then:
-        pomReader.getDependencies().size() == 1
-        assertResolvedPomDependency(key, 'version-four')
-    }
-
-    @Issue("GRADLE-2931")
-    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier"() {
-        when:
-        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>
-            <type>jar</type>
-            <classifier>myjar</classifier>
-            <version>version-two</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>test-jar</type>
-            <classifier>test</classifier>
-            <version>version-three</version>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <type>ejb-client</type>
-            <classifier>client</classifier>
-            <version>version-four</version>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey dependencyJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
-        MavenDependencyKey dependencyTestJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'test-jar', 'test')
-        MavenDependencyKey dependencyEjbClientKey = new MavenDependencyKey('group-two', 'artifact-two', 'ejb-client', 'client')
-
-        then:
-        pomReader.getDependencies().size() == 3
-        assertResolvedPomDependency(dependencyJarkey, 'version-two')
-        assertResolvedPomDependency(dependencyTestJarkey, 'version-three')
-        assertResolvedPomDependency(dependencyEjbClientKey, 'version-four')
-    }
-
-    def "get dependencies management without custom properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-    }
-
-    def "get dependencies management with custom properties"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <properties>
-        <groupId.prop>group-two</groupId.prop>
-        <artifactId.prop>artifact-two</artifactId.prop>
-        <version.prop>version-two</version.prop>
-    </properties>
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>\${groupId.prop}</groupId>
-                <artifactId>\${artifactId.prop}</artifactId>
-                <version>\${version.prop}</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-two')
-    }
-
-    def "picks version of last dependency management defined by artifact ID, group ID, type and classifier"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>jar</type>
-                <classifier>myjar</classifier>
-                <version>version-two</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>jar</type>
-                <classifier>myjar</classifier>
-                <version>version-three</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>jar</type>
-                <classifier>myjar</classifier>
-                <version>version-four</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(key, 'version-four')
-    }
-
-    def "can declare multiple dependency managements with same artifact ID and group ID but different type and classifier"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>jar</type>
-                <classifier>myjar</classifier>
-                <version>version-two</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>test-jar</type>
-                <classifier>test</classifier>
-                <version>version-three</version>
-            </dependency>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <type>ejb-client</type>
-                <classifier>client</classifier>
-                <version>version-four</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey dependencyJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
-        MavenDependencyKey dependencyTestJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'test-jar', 'test')
-        MavenDependencyKey dependencyEjbClientKey = new MavenDependencyKey('group-two', 'artifact-two', 'ejb-client', 'client')
-
-        then:
-        pomReader.getDependencyMgt().size() == 3
-        assertResolvedPomDependencyManagement(dependencyJarkey, 'version-two')
-        assertResolvedPomDependencyManagement(dependencyTestJarkey, 'version-three')
-        assertResolvedPomDependencyManagement(dependencyEjbClientKey, 'version-four')
-    }
-
-    def "parse POM with parent POM"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>artifact-one</artifactId>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-two'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-two'
-        pomReader.parentGroupId == 'group-two'
-        pomReader.parentArtifactId == 'artifact-two'
-        pomReader.parentVersion == 'version-two'
-    }
-
-    def "Parse minimal POM and resolve GAV"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.resolveGAV()
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.packaging == 'jar'
-        pomReader.homePage == ''
-        pomReader.description == ''
-        pomReader.licenses == new License[0]
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.properties.size() == 13
-        pomReader.properties['parent.version'] == 'version-one'
-        pomReader.properties['parent.groupId'] == 'group-one'
-        pomReader.properties['project.parent.version'] == 'version-one'
-        pomReader.properties['project.parent.groupId'] == 'group-one'
-        pomReader.properties['project.groupId'] == 'group-one'
-        pomReader.properties['pom.groupId'] == 'group-one'
-        pomReader.properties['groupId'] == 'group-one'
-        pomReader.properties['project.artifactId'] == 'artifact-one'
-        pomReader.properties['pom.artifactId'] == 'artifact-one'
-        pomReader.properties['artifactId'] == 'artifact-one'
-        pomReader.properties['project.version'] == 'version-one'
-        pomReader.properties['pom.version'] == 'version-one'
-        pomReader.properties['version'] == 'version-one'
-    }
-
-    def "Parse relocated POM without provided coordinates"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <distributionManagement>
-        <relocation>
-        </relocation>
-    </distributionManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.packaging == 'jar'
-        pomReader.homePage == ''
-        pomReader.description == ''
-        pomReader.licenses == new License[0]
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.relocation != null
-        pomReader.relocation == IvyUtil.createModuleRevisionId('group-one', 'artifact-one', 'version-one')
-    }
-
-    def "Parse relocated POM with provided group ID"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <distributionManagement>
-        <relocation>
-            <groupId>group-two</groupId>
-        </relocation>
-    </distributionManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.packaging == 'jar'
-        pomReader.homePage == ''
-        pomReader.description == ''
-        pomReader.licenses == new License[0]
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.relocation != null
-        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-one', 'version-one')
-    }
-
-    def "Parse relocated POM with provided group ID and artifact ID"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <distributionManagement>
-        <relocation>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </relocation>
-    </distributionManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.packaging == 'jar'
-        pomReader.homePage == ''
-        pomReader.description == ''
-        pomReader.licenses == new License[0]
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.relocation != null
-        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-two', 'version-one')
-    }
-
-    def "Parse relocated POM with all provided coordinates"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-    <distributionManagement>
-        <relocation>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-            <version>version-two</version>
-        </relocation>
-    </distributionManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-
-        then:
-        pomReader.groupId == 'group-one'
-        pomReader.artifactId == 'artifact-one'
-        pomReader.version == 'version-one'
-        pomReader.packaging == 'jar'
-        pomReader.homePage == ''
-        pomReader.description == ''
-        pomReader.licenses == new License[0]
-        !pomReader.hasParent()
-        pomReader.pomProperties.size() == 0
-        pomReader.relocation != null
-        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-two', 'version-two')
-    }
-
-    @Issue("GRADLE-2938")
-    def "uses default type for dependency if not declared"() {
-        when:
-        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>
-            <version>version-two</version>
-        </dependency>
-        <dependency>
-            <groupId>group-three</groupId>
-            <artifactId>artifact-three</artifactId>
-            <version>version-three</version>
-            <type>jar</type>
-        </dependency>
-        <dependency>
-            <groupId>group-four</groupId>
-            <artifactId>artifact-four</artifactId>
-            <version>version-four</version>
-            <type>ejb-client</type>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-        MavenDependencyKey keyGroupFour = new MavenDependencyKey('group-four', 'artifact-four', 'ejb-client', null)
-
-        then:
-        pomReader.getDependencies().size() == 3
-        assertResolvedPomDependency(keyGroupTwo, 'version-two')
-        assertResolvedPomDependency(keyGroupThree, 'version-three')
-        assertResolvedPomDependency(keyGroupFour, 'version-four')
-    }
-
-    @Issue("GRADLE-2938")
-    def "uses default type for dependency management if not declared"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-            </dependency>
-            <dependency>
-                <groupId>group-three</groupId>
-                <artifactId>artifact-three</artifactId>
-                <version>version-three</version>
-                <type>jar</type>
-            </dependency>
-            <dependency>
-                <groupId>group-four</groupId>
-                <artifactId>artifact-four</artifactId>
-                <version>version-four</version>
-                <type>ejb-client</type>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
-        MavenDependencyKey keyGroupFour = new MavenDependencyKey('group-four', 'artifact-four', 'ejb-client', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 3
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        assertResolvedPomDependencyManagement(keyGroupThree, 'version-three')
-        assertResolvedPomDependencyManagement(keyGroupFour, 'version-four')
-    }
-
-    def "finds dependency default if declared in same POM"() {
-        when:
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-two</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>group-two</groupId>
-            <artifactId>artifact-three</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-        !pomReader.findDependencyDefaults(keyGroupThree)
-    }
-
-    def "finds dependency default if declared in parent POM"() {
-        when:
-        String parentPom = """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-two</groupId>
-    <artifactId>artifact-two</artifactId>
-    <version>version-two</version>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>group-two</groupId>
-                <artifactId>artifact-two</artifactId>
-                <version>version-two</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-</project>
-"""
-        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
-        pomFile << """
-<project>
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>group-one</groupId>
-    <artifactId>artifact-one</artifactId>
-    <version>version-one</version>
-
-    <parent>
-        <groupId>group-two</groupId>
-        <artifactId>artifact-two</artifactId>
-        <version>version-two</version>
-    </parent>
-
-    <dependencies>
-        <dependency>
-            <groupId>group-four</groupId>
-            <artifactId>artifact-four</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>group-five</groupId>
-            <artifactId>artifact-five</artifactId>
-        </dependency>
-    </dependencies>
-</project>
-"""
-        pomReader = new PomReader(locallyAvailableExternalResource)
-        pomReader.setPomParent(parentPomReader)
-        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
-        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
-
-        then:
-        pomReader.getDependencyMgt().size() == 1
-        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
-        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
-        !pomReader.findDependencyDefaults(keyGroupThree)
-    }
-}
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
deleted file mode 100644
index bfb45ff..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ChainVersionMatcherTest.groovy
+++ /dev/null
@@ -1,129 +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.strategy
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.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+")
-
-        then:
-        1 * matcher1.canHandle("1+") >> false
-        1 * matcher2.canHandle("1+") >> true
-        1 * matcher2.needModuleMetadata("1+") >> 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
deleted file mode 100644
index b486d71..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionMatcherTest.groovy
+++ /dev/null
@@ -1,181 +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.strategy
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.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")
-        !matcher.needModuleMetadata("[1.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
deleted file mode 100644
index 529b3cd..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionMatcherTest.groovy
+++ /dev/null
@@ -1,83 +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.strategy
-
-import org.gradle.api.internal.artifacts.metadata.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")
-        matcher.needModuleMetadata("latest.foo")
-        matcher.needModuleMetadata("latest.123")
-    }
-
-    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
deleted file mode 100644
index 36db721..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionStrategyTest.groovy
+++ /dev/null
@@ -1,78 +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.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
deleted file mode 100644
index bf719ff..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionMatcherTest.groovy
+++ /dev/null
@@ -1,84 +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.strategy
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.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+")
-        !matcher.needModuleMetadata("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
deleted file mode 100644
index 31395ac..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeMatcherTest.groovy
+++ /dev/null
@@ -1,208 +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.strategy
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.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]")
-        !matcher.needModuleMetadata("[1.0,)")
-        !matcher.needModuleMetadata("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
deleted file mode 100644
index 2ed8810..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy
+++ /dev/null
@@ -1,83 +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.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.PathKeyFileStore
-import org.gradle.internal.resource.local.LocallyAvailableResource
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ModuleDescriptorStoreTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temporaryFolder
-    ModuleDescriptorStore store
-    PathKeyFileStore pathKeyFileStore = Mock()
-    ModuleRevisionId moduleRevisionId = Mock()
-    ModuleVersionRepository repository = 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);
-        _ * repository.getId() >> "repositoryId"
-        _ * moduleVersionIdentifier.group >> "org.test"
-        _ * moduleVersionIdentifier.name >> "testArtifact"
-        _ * moduleVersionIdentifier.version >> "1.0"
-        _ * moduleDescriptor.getModuleRevisionId() >> moduleRevisionId
-    }
-
-    def "getModuleDescriptorFile returns null for not cached descriptors"() {
-        when:
-        pathKeyFileStore.get("org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
-        then:
-        null == store.getModuleDescriptor(repository, moduleVersionIdentifier)
-    }
-
-    def "getModuleDescriptorFile uses PathKeyFileStore to get file"() {
-        when:
-        store.getModuleDescriptor(repository, moduleVersionIdentifier);
-        then:
-        1 * pathKeyFileStore.get("org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
-    }
-
-    def "putModuleDescriptor uses PathKeyFileStore to write file"() {
-        setup:
-        _ * moduleRevisionId.organisation >> "org.test"
-        _ * moduleRevisionId.name >> "testArtifact"
-        _ * moduleRevisionId.revision >> "1.0"
-        File descriptorFile = temporaryFolder.createFile("fileStoreEntry")
-        when:
-        store.putModuleDescriptor(repository, moduleDescriptor);
-        then:
-        1 * pathKeyFileStore.add("org.test/testArtifact/1.0/repositoryId/ivy.xml", _) >> { path, action ->
-            action.execute(descriptorFile); fileStoreEntry
-        };
-        1 * ivyModuleDescriptorWriter.write(moduleDescriptor, descriptorFile)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.groovy
deleted file mode 100644
index b933f91..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.groovy
+++ /dev/null
@@ -1,103 +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.moduleconverter
-
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.PublishArtifact
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.artifacts.DefaultPublishArtifactSet
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData
-import spock.lang.Specification
-
-class DefaultConfigurationsToArtifactsConverterTest extends Specification {
-    def converter = new DefaultConfigurationsToArtifactsConverter()
-
-    def "adds artifacts from each configuration"() {
-        def metaData = Mock(MutableLocalComponentMetaData)
-        def config1 = Stub(Configuration)
-        def config2 = Stub(Configuration)
-        def artifact1 = Stub(PublishArtifact)
-        def artifact2 = Stub(PublishArtifact)
-        def file1 = new File("file-1")
-        def file2 = new File("file-2")
-
-        given:
-        config1.name >> "config1"
-        config1.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact1]))
-        config2.name >> "config2"
-        config2.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact2]))
-
-        and:
-        artifact1.name >> 'art1'
-        artifact1.type >> 'type1'
-        artifact1.extension >> 'ext1'
-        artifact2.name >> 'art2'
-        artifact2.type >> 'type2'
-        artifact2.extension >> 'ext2'
-        artifact2.classifier >> 'classifier'
-
-        when:
-        converter.addArtifacts(metaData, [config1, config2])
-
-        then:
-        1 * metaData.addArtifact("config1", _, file1) >> { name, Artifact artifact, file ->
-            assert artifact.name == 'art1'
-            assert artifact.type == 'type1'
-            assert artifact.ext == 'ext1'
-            assert artifact.qualifiedExtraAttributes == [:]
-        }
-        1 * metaData.addArtifact("config2", _, file2) >> { name, Artifact artifact, file ->
-            assert artifact.name == 'art2'
-            assert artifact.type == 'type2'
-            assert artifact.ext == 'ext2'
-            assert artifact.qualifiedExtraAttributes == [(Dependency.CLASSIFIER): 'classifier']
-        }
-        _ * metaData.moduleDescriptor >> Stub(DefaultModuleDescriptor)
-        0 * metaData._
-    }
-
-    def "artifact name defaults to module name when not specified"() {
-        def metaData = Mock(MutableLocalComponentMetaData)
-        def config = Stub(Configuration)
-        def artifact = Stub(PublishArtifact)
-        def file = new File("file-1")
-
-        given:
-        config.name >> "config1"
-        config.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact]))
-
-        and:
-        artifact.type >> 'type1'
-        artifact.extension >> 'ext1'
-
-        when:
-        converter.addArtifacts(metaData, [config])
-
-        then:
-        1 * metaData.addArtifact("config1", _, file) >> { name, Artifact ivyArtifact, f ->
-            assert ivyArtifact.name == 'module'
-        }
-        _ * metaData.moduleDescriptor >> Stub(DefaultModuleDescriptor) {
-            getModuleRevisionId() >> IvyUtil.createModuleRevisionId("group", "module", "version")
-        }
-        0 * metaData._
-    }
-}
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
deleted file mode 100644
index 69688de..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.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.TestUtil;
-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.util.Collections;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class DefaultConfigurationsToModuleDescriptorConverterTest {
-    private DefaultConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter = new DefaultConfigurationsToModuleDescriptorConverter();
-
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    @Test
-    public void testAddConfigurations() {
-        Configuration configurationStub1 = createNamesAndExtendedConfigurationStub("conf1");
-        Configuration configurationStub2 = createNamesAndExtendedConfigurationStub("conf2", configurationStub1);
-        final DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(Collections.EMPTY_SET);
-
-        configurationsToModuleDescriptorConverter.addConfigurations(moduleDescriptor, WrapUtil.toSet(configurationStub1, configurationStub2));
-
-        assertIvyConfigurationIsCorrect(moduleDescriptor.getConfiguration(configurationStub1.getName()),
-                expectedIvyConfiguration(configurationStub1));
-        assertIvyConfigurationIsCorrect(moduleDescriptor.getConfiguration(configurationStub2.getName()),
-                expectedIvyConfiguration(configurationStub2));
-        assertThat(moduleDescriptor.getConfigurations().length, equalTo(2));
-    }
-
-    private void assertIvyConfigurationIsCorrect(org.apache.ivy.core.module.descriptor.Configuration actualConfiguration,
-                                                 org.apache.ivy.core.module.descriptor.Configuration expectedConfiguration) {
-        assertThat(actualConfiguration.getDescription(), equalTo(expectedConfiguration.getDescription()));
-        assertThat(actualConfiguration.isTransitive(), equalTo(expectedConfiguration.isTransitive()));
-        assertThat(actualConfiguration.getVisibility(), equalTo(expectedConfiguration.getVisibility()));
-        assertThat(actualConfiguration.getName(), equalTo(expectedConfiguration.getName()));
-        assertThat(actualConfiguration.getExtends(), equalTo(expectedConfiguration.getExtends()));
-    }
-
-    private org.apache.ivy.core.module.descriptor.Configuration expectedIvyConfiguration(Configuration configuration) {
-        return new org.apache.ivy.core.module.descriptor.Configuration(
-                configuration.getName(),
-                configuration.isVisible() ? org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC : org.apache.ivy.core.module.descriptor.Configuration.Visibility.PRIVATE,
-                configuration.getDescription(),
-                Configurations.getNames(configuration.getExtendsFrom(), false).toArray(new String[configuration.getExtendsFrom().size()]),
-                configuration.isTransitive(),
-                null);
-    }
-
-    private Configuration createNamesAndExtendedConfigurationStub(final String name, final Configuration... extendsFromConfigurations) {
-        final Configuration configurationStub = IvyConverterTestUtil.createNamedConfigurationStub(name, context);
-        context.checking(new Expectations() {{
-            allowing(configurationStub).isTransitive();
-            will(returnValue(true));
-
-            allowing(configurationStub).getDescription();
-            will(returnValue(TestUtil.createUniqueId()));
-
-            allowing(configurationStub).isVisible();
-            will(returnValue(true));
-
-            allowing(configurationStub).getExtendsFrom();
-            will(returnValue(WrapUtil.toSet(extendsFromConfigurations)));
-        }});
-        return configurationStub;
-    }
-}
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
deleted file mode 100644
index 49ec853..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultModuleDescriptorFactoryTest.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.artifacts.Module
-import org.gradle.api.internal.artifacts.DefaultModule
-import spock.lang.Specification
-
-public class DefaultModuleDescriptorFactoryTest extends Specification {
-    final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory()
-
-    public void testCreateModuleDescriptor() {
-        given:
-        Module module = new DefaultModule("org", "name", "version", "status");
-
-        when:
-        DefaultModuleDescriptor moduleDescriptor = factory.createModuleDescriptor(module);
-
-        then:
-        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/PublishLocalComponentFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishLocalComponentFactoryTest.groovy
deleted file mode 100644
index 7d292cb..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishLocalComponentFactoryTest.groovy
+++ /dev/null
@@ -1,56 +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.moduleconverter
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.internal.artifacts.ModuleInternal
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData
-import spock.lang.Specification
-
-public class PublishLocalComponentFactoryTest extends Specification {
-
-    def "converts"() {
-        given:
-        def configurationsDummy = [Mock(Configuration)] as Set
-        def moduleDummy = Mock(ModuleInternal)
-        def moduleDescriptorDummy = Mock(DefaultModuleDescriptor)
-        def componentMetaDataDummy = Mock(MutableLocalComponentMetaData)
-        def artifactsToModuleDescriptorConverter = Mock(ConfigurationsToArtifactsConverter)
-        def resolveModuleDescriptorConverter = Mock(LocalComponentFactory)
-
-        def publishModuleDescriptorConverter = new PublishLocalComponentFactory(
-                resolveModuleDescriptorConverter,
-                artifactsToModuleDescriptorConverter);
-
-        and:
-        componentMetaDataDummy.moduleDescriptor >> moduleDescriptorDummy
-        resolveModuleDescriptorConverter.convert(configurationsDummy, moduleDummy) >> componentMetaDataDummy
-
-        when:
-        def actualMetaData = publishModuleDescriptorConverter.convert(configurationsDummy, moduleDummy);
-
-        then:
-        1 * moduleDescriptorDummy.addExtraAttributeNamespace(PublishLocalComponentFactory.IVY_MAVEN_NAMESPACE_PREFIX,
-                    PublishLocalComponentFactory.IVY_MAVEN_NAMESPACE);
-        1 * artifactsToModuleDescriptorConverter.addArtifacts(componentMetaDataDummy, configurationsDummy)
-
-        and:
-        actualMetaData == componentMetaDataDummy
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy
deleted file mode 100644
index 15aac06..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy
+++ /dev/null
@@ -1,91 +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.moduleconverter
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.internal.artifacts.DefaultModule
-import org.gradle.api.internal.artifacts.ProjectBackedModule
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter
-import org.gradle.api.internal.artifacts.metadata.DefaultLocalComponentMetaData
-import spock.lang.Specification
-
-public class ResolveLocalComponentFactoryTest extends Specification {
-    def moduleDescriptor = Mock(DefaultModuleDescriptor)
-    def moduleDescriptorFactory = Mock(ModuleDescriptorFactory)
-    def configurationsConverter = Mock(ConfigurationsToModuleDescriptorConverter)
-    def dependenciesConverter = Mock(DependenciesToModuleDescriptorConverter)
-    def componentIdentifierFactory = Mock(ComponentIdentifierFactory)
-
-    ResolveLocalComponentFactory resolveModuleDescriptorConverter = new ResolveLocalComponentFactory(
-            moduleDescriptorFactory,
-            configurationsConverter,
-            dependenciesConverter,
-            componentIdentifierFactory);
-
-    def "converts for provided default module"() {
-        given:
-        def configurations = [Mock(Configuration), Mock(Configuration)] as Set
-        def module = new DefaultModule('group-one', 'name-one', 'version-one')
-
-        and:
-        moduleDescriptor.moduleRevisionId >> IvyUtil.createModuleRevisionId("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)
-        1 * componentIdentifierFactory.createComponentIdentifier(module) >> new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
-
-        and:
-        actualDescriptor instanceof DefaultLocalComponentMetaData
-        actualDescriptor.moduleDescriptor == moduleDescriptor
-        actualDescriptor.toResolveMetaData().componentId == new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
-    }
-
-    def "converts for provided project backed module"() {
-        given:
-        def configurations = [Mock(Configuration), Mock(Configuration)] as Set
-        def project = Mock(Project)
-        def module = new ProjectBackedModule(project)
-
-        and:
-        moduleDescriptor.moduleRevisionId >> IvyUtil.createModuleRevisionId("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)
-        1 * componentIdentifierFactory.createComponentIdentifier(module) >> new DefaultProjectComponentIdentifier(':myPath')
-
-        and:
-        actualDescriptor instanceof DefaultLocalComponentMetaData
-        actualDescriptor.moduleDescriptor == moduleDescriptor
-        actualDescriptor.toResolveMetaData().componentId == new DefaultProjectComponentIdentifier(':myPath')
-    }
-}
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
deleted file mode 100644
index d4fbbf3..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
-import org.apache.ivy.core.module.id.ArtifactId;
-import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
-import org.apache.ivy.plugins.matcher.PatternMatcher;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.DependencyArtifact;
-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.IvyUtil;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
-import org.gradle.util.TestUtil;
-import org.gradle.util.WrapUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.List;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public abstract class AbstractDependencyDescriptorFactoryInternalTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    protected static final String TEST_CONF = "conf";
-    protected static final String TEST_DEP_CONF = "depconf1";
-
-    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 = 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");
-
-    @Before
-    public void setUp() {
-        expectExcludeRuleConversion(TEST_EXCLUDE_RULE, TEST_IVY_EXCLUDE_RULE);
-    }
-
-    protected void expectExcludeRuleConversion(final ExcludeRule excludeRule, final org.apache.ivy.core.module.descriptor.ExcludeRule ivyExcludeRule) {
-        context.checking(new Expectations() {{
-            allowing(excludeRuleConverterStub).createExcludeRule(TEST_CONF, excludeRule);
-            will(returnValue(ivyExcludeRule));
-        }});
-    }
-
-    protected Dependency setUpDependency(ModuleDependency dependency) {
-        return dependency.addArtifact(artifact).
-                addArtifact(artifactWithClassifiers).
-                exclude(WrapUtil.toMap("group", TEST_EXCLUDE_RULE.getGroup())).
-                setTransitive(true);
-    }
-
-    protected void assertDependencyDescriptorHasCommonFixtureValues(DefaultDependencyDescriptor dependencyDescriptor) {
-        assertThat(dependencyDescriptor.getParentRevisionId(), equalTo(moduleDescriptor.getModuleRevisionId()));
-        assertEquals(TEST_IVY_EXCLUDE_RULE, dependencyDescriptor.getExcludeRules(TEST_CONF)[0]);
-        assertThat(dependencyDescriptor.getDependencyConfigurations(TEST_CONF), equalTo(WrapUtil.toArray(TEST_DEP_CONF)));
-        assertThat(dependencyDescriptor.isTransitive(), equalTo(true));
-        assertDependencyDescriptorHasArtifacts(dependencyDescriptor);
-    }
-
-    private void assertDependencyDescriptorHasArtifacts(DefaultDependencyDescriptor dependencyDescriptor) {
-        List<DependencyArtifactDescriptor> artifactDescriptors = WrapUtil.toList(dependencyDescriptor.getDependencyArtifacts(TEST_CONF));
-        assertThat(artifactDescriptors.size(), equalTo(2));
-
-        
-        DependencyArtifactDescriptor artifactDescriptorWithoutClassifier = findDescriptor(artifactDescriptors, artifact);
-        assertEquals(new HashMap(), artifactDescriptorWithoutClassifier.getExtraAttributes());
-        assertEquals(null, artifactDescriptorWithoutClassifier.getUrl());
-        compareArtifacts(artifact, artifactDescriptorWithoutClassifier);
-        assertEquals(artifact.getType(), artifactDescriptorWithoutClassifier.getExt());
-
-        DependencyArtifactDescriptor artifactDescriptorWithClassifierAndConfs = findDescriptor(artifactDescriptors, artifactWithClassifiers);
-        assertEquals(WrapUtil.toMap(Dependency.CLASSIFIER, artifactWithClassifiers.getClassifier()), artifactDescriptorWithClassifierAndConfs.getQualifiedExtraAttributes());
-        compareArtifacts(artifactWithClassifiers, artifactDescriptorWithClassifierAndConfs);
-        assertEquals(artifactWithClassifiers.getExtension(), artifactDescriptorWithClassifierAndConfs.getExt());
-        try {
-            assertEquals(new URL(artifactWithClassifiers.getUrl()), artifactDescriptorWithClassifierAndConfs.getUrl());
-        } catch (MalformedURLException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private DependencyArtifactDescriptor findDescriptor(List<DependencyArtifactDescriptor> artifactDescriptors, DefaultDependencyArtifact dependencyArtifact) {
-        for (DependencyArtifactDescriptor artifactDescriptor : artifactDescriptors) {
-            if (artifactDescriptor.getName().equals(dependencyArtifact.getName())) {
-                return artifactDescriptor;
-            }
-        }
-        throw new RuntimeException("Descriptor could not be found");
-    }
-
-    private void compareArtifacts(DependencyArtifact artifact, DependencyArtifactDescriptor artifactDescriptor) {
-        assertEquals(artifact.getName(), artifactDescriptor.getName());
-        assertEquals(artifact.getType(), artifactDescriptor.getType());
-    }
-
-    private static DefaultExcludeRule getTestExcludeRule() {
-        return new DefaultExcludeRule(new ArtifactId(
-                IvyUtil.createModuleId("org", "testOrg"), PatternMatcher.ANY_EXPRESSION,
-                PatternMatcher.ANY_EXPRESSION,
-                PatternMatcher.ANY_EXPRESSION),
-                ExactPatternMatcher.INSTANCE, null);
-    }
-}
-
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactoryTest.java
deleted file mode 100644
index 9a81239..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ClientModuleIvyDependencyDescriptorFactoryTest.java
+++ /dev/null
@@ -1,89 +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.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.gradle.api.artifacts.ClientModule;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.util.WrapUtil;
-import org.hamcrest.Matchers;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.*;
-
-public class ClientModuleIvyDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    private ClientModuleMetaDataFactory clientModuleMetaDataFactory = context.mock(ClientModuleMetaDataFactory.class);
-    private ClientModuleIvyDependencyDescriptorFactory clientModuleDependencyDescriptorFactory = new ClientModuleIvyDependencyDescriptorFactory(
-            excludeRuleConverterStub,
-            clientModuleMetaDataFactory
-    );
-
-    @Test
-    public void canConvert() {
-        assertThat(clientModuleDependencyDescriptorFactory.canConvert(context.mock(ProjectDependency.class)), Matchers.equalTo(false));
-        assertThat(clientModuleDependencyDescriptorFactory.canConvert(context.mock(ClientModule.class)), Matchers.equalTo(true));
-    }
-
-    @Test
-    public void testAddDependencyDescriptorForClientModule() {
-        final ModuleDependency dependencyDependency = context.mock(ModuleDependency.class, "dependencyDependency");
-        final DefaultClientModule clientModule = new DefaultClientModule("org.gradle", "gradle-core", "1.0", TEST_DEP_CONF);
-        final ModuleRevisionId testModuleRevisionId = IvyUtil.createModuleRevisionId(clientModule);
-
-        setUpDependency(clientModule);
-        clientModule.addDependency(dependencyDependency);
-        final MutableModuleVersionMetaData moduleVersionMetaData = context.mock(MutableModuleVersionMetaData.class);
-        context.checking(new Expectations() {{
-            allowing(clientModuleMetaDataFactory).createModuleDescriptor(
-                    testModuleRevisionId,
-                    WrapUtil.toSet(dependencyDependency)
-            );
-            will(returnValue(moduleVersionMetaData));
-        }});
-
-        DefaultDependencyDescriptor dependencyDescriptor = clientModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, clientModule, moduleDescriptor);
-        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
-        assertEquals(testModuleRevisionId, dependencyDescriptor.getDependencyRevisionId());
-        assertFalse(dependencyDescriptor.isChanging());
-    }
-
-    @Test
-    public void testAddWithNullGroupAndNullVersionShouldHaveEmptyStringModuleRevisionValues() {
-        final ClientModule clientModule = new DefaultClientModule(null, "gradle-core", null, TEST_DEP_CONF);
-        final ModuleRevisionId testModuleRevisionId = IvyUtil.createModuleRevisionId(clientModule);
-        final MutableModuleVersionMetaData moduleVersionMetaData = context.mock(MutableModuleVersionMetaData.class);
-        context.checking(new Expectations() {{
-            allowing(clientModuleMetaDataFactory).createModuleDescriptor(
-                    testModuleRevisionId,
-                    WrapUtil.<ModuleDependency>toSet()
-            );
-            will(returnValue(moduleVersionMetaData));
-        }});
-
-        DefaultDependencyDescriptor dependencyDescriptor = clientModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, clientModule, moduleDescriptor);
-        assertThat(dependencyDescriptor.getDependencyRevisionId(), equalTo(testModuleRevisionId));
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactoryTest.java b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactoryTest.java
deleted file mode 100644
index bacaee7..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultClientModuleMetaDataFactoryTest.java
+++ /dev/null
@@ -1,89 +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.dependencies;
-
-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.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.metadata.MutableModuleVersionMetaData;
-import org.gradle.util.WrapUtil;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-public class DefaultClientModuleMetaDataFactoryTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    @Test
-    public void testCreateModuleDescriptor() {
-        DependencyDescriptor dependencyDescriptorDummy = context.mock(DependencyDescriptor.class);
-        DependencyDescriptorFactorySpy dependencyDescriptorFactorySpy = new DependencyDescriptorFactorySpy(dependencyDescriptorDummy);
-        DefaultClientModuleMetaDataFactory clientModuleDescriptorFactory =
-                new DefaultClientModuleMetaDataFactory();
-        clientModuleDescriptorFactory.setDependencyDescriptorFactory(dependencyDescriptorFactorySpy);
-        ModuleDependency dependencyMock = context.mock(ModuleDependency.class);
-        final ModuleRevisionId moduleRevisionId = IvyUtil.createModuleRevisionId("org", "name", "version");
-
-        MutableModuleVersionMetaData metaData = clientModuleDescriptorFactory.createModuleDescriptor(
-                moduleRevisionId,
-                WrapUtil.toSet(dependencyMock));
-
-        ModuleDescriptor moduleDescriptor = metaData.getDescriptor();
-        assertThat(moduleDescriptor.getModuleRevisionId(), equalTo(moduleRevisionId));
-        assertThatDescriptorHasOnlyDefaultConfiguration(moduleDescriptor);
-        assertCorrectCallToDependencyDescriptorFactory(dependencyDescriptorFactorySpy, Dependency.DEFAULT_CONFIGURATION, moduleDescriptor, dependencyMock);
-    }
-
-    private void assertThatDescriptorHasOnlyDefaultConfiguration(ModuleDescriptor moduleDescriptor) {
-        assertThat(moduleDescriptor.getConfigurations().length, equalTo(1));
-        assertThat(moduleDescriptor.getConfigurationsNames()[0], equalTo(Dependency.DEFAULT_CONFIGURATION));
-    }
-
-    private void assertCorrectCallToDependencyDescriptorFactory(DependencyDescriptorFactorySpy dependencyDescriptorFactorySpy,
-                                                                String configuration,
-                                                                ModuleDescriptor parent,
-                                                                Dependency dependency) {
-        assertThat(dependencyDescriptorFactorySpy.configuration, equalTo(configuration));
-        assertThat(dependencyDescriptorFactorySpy.parent, equalTo(parent));
-        assertThat(dependencyDescriptorFactorySpy.dependency, equalTo(dependency));
-    }
-
-    private static class DependencyDescriptorFactorySpy implements DependencyDescriptorFactory {
-        DependencyDescriptor dependencyDescriptor;
-
-        String configuration;
-        ModuleDescriptor parent;
-        Dependency dependency;
-
-        private DependencyDescriptorFactorySpy(DependencyDescriptor dependencyDescriptor) {
-            this.dependencyDescriptor = dependencyDescriptor;
-        }
-
-        public void addDependencyDescriptor(String configuration, DefaultModuleDescriptor moduleDescriptor,
-                                            ModuleDependency dependency) {
-            this.configuration = configuration;
-            this.parent = moduleDescriptor;
-            this.dependency = dependency;
-        }
-
-    }
-}
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
deleted file mode 100644
index 1ac36a0..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.DependencySet;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.artifacts.ModuleDependency;
-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.TestUtil;
-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.util.Collections;
-
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class DefaultDependenciesToModuleDescriptorConverterTest {
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-
-    private static final ExcludeRule GRADLE_EXCLUDE_RULE_DUMMY_1 = new DefaultExcludeRule("testOrg", null);
-    private static final ExcludeRule GRADLE_EXCLUDE_RULE_DUMMY_2 = new DefaultExcludeRule(null, "testModule");
-
-    private ModuleDependency dependencyDummy1 = context.mock(ModuleDependency.class, "dep1");
-    private ModuleDependency dependencyDummy2 = context.mock(ModuleDependency.class, "dep2");
-    private ModuleDependency similarDependency1 = createDependency("group", "name", "version");
-    private ModuleDependency similarDependency2 = createDependency("group", "name", "version");
-    private ModuleDependency similarDependency3 = createDependency("group", "name", "version");
-    private org.apache.ivy.core.module.descriptor.ExcludeRule ivyExcludeRuleStub1 = context.mock(org.apache.ivy.core.module.descriptor.ExcludeRule.class, "rule1");
-    private org.apache.ivy.core.module.descriptor.ExcludeRule ivyExcludeRuleStub2 = context.mock(org.apache.ivy.core.module.descriptor.ExcludeRule.class, "rule2");
-
-    private DependencyDescriptorFactory dependencyDescriptorFactoryStub = context.mock(DependencyDescriptorFactory.class);
-    private ExcludeRuleConverter excludeRuleConverterStub = context.mock(ExcludeRuleConverter.class);
-
-    @Test
-    public void testAddDependencyDescriptors() {
-        DefaultDependenciesToModuleDescriptorConverter converter = new DefaultDependenciesToModuleDescriptorConverter(
-                dependencyDescriptorFactoryStub, excludeRuleConverterStub);
-        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 = TestUtil.createModuleDescriptor(toSet(configurationStub1.getName(),
-                configurationStub2.getName()));
-        associateDependencyWithDescriptor(dependencyDummy1, moduleDescriptor, configurationStub1);
-        associateDependencyWithDescriptor(dependencyDummy2, moduleDescriptor, configurationStub2);
-        associateDependencyWithDescriptor(similarDependency1, moduleDescriptor, configurationStub1);
-        associateDependencyWithDescriptor(similarDependency2, moduleDescriptor, configurationStub2);
-        associateDependencyWithDescriptor(similarDependency3, moduleDescriptor, configurationStub3);
-        associateGradleExcludeRuleWithIvyExcludeRule(GRADLE_EXCLUDE_RULE_DUMMY_1, ivyExcludeRuleStub1, configurationStub1);
-        associateGradleExcludeRuleWithIvyExcludeRule(GRADLE_EXCLUDE_RULE_DUMMY_2, ivyExcludeRuleStub2, configurationStub2);
-
-        converter.addDependencyDescriptors(moduleDescriptor, toSet(configurationStub1, configurationStub2, configurationStub3));
-
-        assertThat(moduleDescriptor.getExcludeRules(toArray(configurationStub1.getName())), equalTo(toArray(
-                ivyExcludeRuleStub1)));
-        assertThat(moduleDescriptor.getExcludeRules(toArray(configurationStub2.getName())), equalTo(toArray(
-                ivyExcludeRuleStub2)));
-
-    }
-
-    private void associateGradleExcludeRuleWithIvyExcludeRule(final ExcludeRule gradleExcludeRule,
-                                                              final org.apache.ivy.core.module.descriptor.ExcludeRule ivyExcludeRule,
-                                                              final Configuration configuration) {
-        final String expectedConfigurationName = configuration.getName();
-        context.checking(new Expectations() {{
-            allowing(excludeRuleConverterStub).createExcludeRule(expectedConfigurationName, gradleExcludeRule);
-            will(returnValue(ivyExcludeRule));
-
-            allowing(ivyExcludeRule).getConfigurations();
-            will(returnValue(WrapUtil.toArray(configuration.getName())));
-        }});
-    }
-
-    private void associateDependencyWithDescriptor(final ModuleDependency dependency, final DefaultModuleDescriptor parent,
-                                                   final Configuration configuration) {
-        context.checking(new Expectations() {{
-            allowing(dependencyDescriptorFactoryStub).addDependencyDescriptor(with(equal(configuration.getName())),
-                    with(equal(parent)), with(sameInstance(dependency)));
-        }});
-    }
-    
-    private Configuration createNamedConfigurationStubWithDependenciesAndExcludeRules(final String name, final ExcludeRule excludeRule,
-                                                                                      final ModuleDependency... dependencies) {
-        final Configuration configurationStub = IvyConverterTestUtil.createNamedConfigurationStub(name, context);
-        final DependencySet dependencySet = context.mock(DependencySet.class);
-
-        context.checking(new Expectations() {{
-            allowing(configurationStub).getDependencies();
-            will(returnValue(dependencySet));
-
-            allowing(dependencySet).withType(ModuleDependency.class);
-            will(returnValue(toDomainObjectSet(ModuleDependency.class, dependencies)));
-
-            allowing(configurationStub).getExcludeRules();
-            will(returnValue(excludeRule == null ? Collections.emptySet() : toSet(excludeRule)));
-        }});
-        return configurationStub;
-    }
-
-    ModuleDependency createDependency(String group, String name, String version) {
-        return new DefaultExternalModuleDependency(group, name, version);
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
deleted file mode 100644
index 367df66..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
+++ /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.artifacts.ivyservice.moduleconverter.dependencies
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.ProjectDependency
-import spock.lang.Specification
-
-public class DefaultDependencyDescriptorFactoryTest extends Specification {
-    def configurationName = "conf";
-    def moduleDescriptor = Mock(DefaultModuleDescriptor);
-    def projectDependency = Mock(ProjectDependency);
-    def dependencyDescriptor = Mock(EnhancedDependencyDescriptor)
-
-    def "delegates to internal factory"() {
-        given:
-        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory);
-        def ivyDependencyDescriptorFactory2 = Mock(IvyDependencyDescriptorFactory);
-
-        when:
-        1 * ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
-        1 * ivyDependencyDescriptorFactory2.canConvert(projectDependency) >> true
-        1 * ivyDependencyDescriptorFactory2.createDependencyDescriptor(configurationName, projectDependency, moduleDescriptor) >> dependencyDescriptor
-        1 * moduleDescriptor.addDependency(dependencyDescriptor)
-
-        then:
-        DefaultDependencyDescriptorFactory dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
-                ivyDependencyDescriptorFactory1, ivyDependencyDescriptorFactory2
-        );
-        dependencyDescriptorFactory.addDependencyDescriptor(configurationName, moduleDescriptor, projectDependency);
-    }
-
-    def "fails where no internal factory can handle dependency type"() {
-        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory);
-
-        when:
-        ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
-
-        and:
-        DefaultDependencyDescriptorFactory dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
-                ivyDependencyDescriptorFactory1
-        );
-        dependencyDescriptorFactory.addDependencyDescriptor(configurationName, moduleDescriptor, projectDependency);
-
-        then:
-        thrown InvalidUserDataException
-    }
-}
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
deleted file mode 100644
index 7cd5814..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.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.descriptor.DefaultDependencyDescriptor;
-import org.gradle.api.artifacts.ExternalModuleDependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.hamcrest.Matchers;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class ExternalModuleDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    ExternalModuleIvyDependencyDescriptorFactory externalModuleDependencyDescriptorFactory =
-            new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverterStub);
-    
-    @Test
-    public void canConvert() {
-        assertThat(externalModuleDependencyDescriptorFactory.canConvert(context.mock(ProjectDependency.class)), Matchers.equalTo(false));
-        assertThat(externalModuleDependencyDescriptorFactory.canConvert(context.mock(ExternalModuleDependency.class)), Matchers.equalTo(true));
-    }
-
-    @Test
-    public void testAddWithNullGroupAndNullVersionShouldHaveEmptyStringModuleRevisionValues() {
-        ModuleDependency dependency = new DefaultExternalModuleDependency(null, "gradle-core", null, TEST_DEP_CONF);
-        DefaultDependencyDescriptor dependencyDescriptor = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, dependency, moduleDescriptor);
-        assertThat(dependencyDescriptor.getDependencyRevisionId(), equalTo(IvyUtil.createModuleRevisionId(dependency)));
-    }
-
-    @Test
-    public void testCreateFromModuleDependency() {
-        DefaultExternalModuleDependency moduleDependency = new DefaultExternalModuleDependency("org.gradle",
-                "gradle-core", "1.0", TEST_DEP_CONF);
-        setUpDependency(moduleDependency);
-
-        DefaultDependencyDescriptor dependencyDescriptor = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, moduleDependency, moduleDescriptor);
-
-        assertEquals(moduleDependency.isChanging(), dependencyDescriptor.isChanging());
-        assertEquals(dependencyDescriptor.isForce(), moduleDependency.isForce());
-        assertEquals(IvyUtil.createModuleRevisionId(moduleDependency), dependencyDescriptor.getDependencyRevisionId());
-        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
-    }
-}
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
deleted file mode 100644
index ab40b8a..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies
-
-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.artifacts.ivyservice.IvyUtil
-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(IvyUtil.createModuleRevisionId("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/projectmodule/ProjectDependencyResolverTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
deleted file mode 100644
index 790f3c6..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
+++ /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.api.internal.artifacts.ivyservice.projectmodule
-
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor
-import org.gradle.api.internal.artifacts.ivyservice.BuildableComponentResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.DependencyToModuleVersionResolver
-import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyDescriptor
-import org.gradle.api.internal.artifacts.metadata.DependencyMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-import org.gradle.api.internal.artifacts.metadata.MutableLocalComponentMetaData
-import org.gradle.api.internal.project.ProjectInternal
-import spock.lang.Specification
-
-class ProjectDependencyResolverTest extends Specification {
-    final ProjectComponentRegistry registry = Mock()
-    final DependencyToModuleVersionResolver target = Mock()
-    final LocalComponentFactory converter = Mock()
-    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, converter, target)
-
-    def "resolves project dependency"() {
-        setup:
-        def resolveMetaData = Stub(ModuleVersionMetaData)
-        def componentMetaData = Stub(MutableLocalComponentMetaData) {
-            toResolveMetaData() >> resolveMetaData
-        }
-        def result = Mock(BuildableComponentResolveResult)
-        def dependencyProject = Stub(ProjectInternal) {
-            getPath() >> ":project"
-        }
-        def dependencyDescriptor = Stub(ProjectDependencyDescriptor) {
-            getTargetProject() >> dependencyProject
-        }
-        def dependencyMetaData = Stub(DependencyMetaData) {
-            getDescriptor() >> dependencyDescriptor
-        }
-
-        when:
-        resolver.resolve(dependencyMetaData, result)
-
-        then:
-        1 * registry.getProject(":project") >> componentMetaData
-        1 * result.resolved(resolveMetaData)
-        0 * result._
-    }
-
-    def "delegates to backing resolver for non-project dependency"() {
-        def result = Mock(BuildableComponentResolveResult)
-        def dependencyDescriptor = Stub(DependencyDescriptor)
-        def dependencyMetaData = Stub(DependencyMetaData) {
-            getDescriptor() >> dependencyDescriptor
-        }
-
-        when:
-        resolver.resolve(dependencyMetaData, result)
-
-        then:
-        1 * target.resolve(dependencyMetaData, result)
-        0 * _
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy
deleted file mode 100644
index 8a70368..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy
+++ /dev/null
@@ -1,276 +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.resolutionstrategy;
-
-
-import org.gradle.api.Action
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.artifacts.ResolvedModuleVersion
-import org.gradle.api.artifacts.cache.ArtifactResolutionControl
-import org.gradle.api.artifacts.cache.DependencyResolutionControl
-import org.gradle.api.artifacts.cache.ModuleResolutionControl
-import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import spock.lang.Specification
-
-import java.util.concurrent.TimeUnit
-
-import static org.gradle.util.Assertions.assertThat
-
-public class DefaultCachePolicySpec extends Specification {
-    private static final int SECOND = 1000;
-    private static final int MINUTE = SECOND * 60;
-    private static final int HOUR = MINUTE * 60;
-    private static final int DAY = HOUR * 24;
-    private static final int WEEK = DAY * 7;
-    private static final int FOREVER = Integer.MAX_VALUE
-
-    DefaultCachePolicy cachePolicy = new DefaultCachePolicy()
-
-    def "will cache default"() {
-        expect:
-        hasDynamicVersionTimeout(DAY)
-        hasChangingModuleTimeout(DAY)
-        hasModuleTimeout(FOREVER)
-        hasMissingArtifactTimeout(DAY)
-        hasMissingModuleTimeout(DAY)
-    }
-
-    def "uses changing module timeout for changing modules"() {
-        when:
-        cachePolicy.cacheChangingModulesFor(10, TimeUnit.SECONDS);
-
-        then:
-        hasDynamicVersionTimeout(DAY);
-        hasChangingModuleTimeout(10 * SECOND)
-        hasModuleTimeout(FOREVER)
-        hasMissingModuleTimeout(DAY)
-        hasChangingArtifactTimeout(DAY)
-    }
-
-    def "uses dynamic version timeout for dynamic versions"() {
-        when:
-        cachePolicy.cacheDynamicVersionsFor(10, TimeUnit.SECONDS)
-
-        then:
-        hasDynamicVersionTimeout(10 * SECOND)
-        hasChangingModuleTimeout(DAY)
-        hasMissingModuleTimeout(DAY)
-        hasMissingArtifactTimeout(DAY)
-        hasModuleTimeout(FOREVER)
-    }
-
-    def "applies invalidate rule for dynamic versions"() {
-        when:
-        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
-            void execute(DependencyResolutionControl t) {
-                t.refresh()
-            }
-        })
-
-        then:
-        cachePolicy.mustRefreshVersionList(null, null, 2 * SECOND)
-    }
-
-    def "applies useCachedResult for dynamic versions"() {
-        when:
-        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
-            void execute(DependencyResolutionControl t) {
-                t.useCachedResult()
-            }
-        })
-
-        then:
-        !cachePolicy.mustRefreshVersionList(null, null, 2 * SECOND)
-    }
-
-    def "applies cacheFor rules for dynamic versions"() {
-        when:
-        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
-            void execute(DependencyResolutionControl t) {
-                t.cacheFor(100, TimeUnit.SECONDS)
-            }
-        })
-
-        then:
-        hasDynamicVersionTimeout(100 * SECOND)
-    }
-
-    def "provides details of cached version list"() {
-        expect:
-        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
-            void execute(DependencyResolutionControl t) {
-                assert t.request.group == 'g'
-                assert t.request.name == 'n'
-                assertId(t.cachedResult.iterator().next(), 'group', 'name', 'version')
-                t.refresh()
-            }
-        })
-        cachePolicy.mustRefreshVersionList(moduleIdentifier('g', 'n', 'v').module, [moduleIdentifier('group', 'name', 'version')] as Set, 0)
-    }
-
-    def "provides details of cached module"() {
-        expect:
-        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
-            void execute(ModuleResolutionControl t) {
-                assertId(t.request, 'g', 'n', 'v')
-                assertId(t.cachedResult.id, 'group', 'name', 'version')
-                assert !t.changing
-                t.refresh()
-            }
-        })
-        cachePolicy.mustRefreshModule(moduleIdentifier('g', 'n', 'v'), moduleVersion('group', 'name', 'version'), null, 0)
-    }
-
-    def "provides details of cached changing module"() {
-        expect:
-        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
-            void execute(ModuleResolutionControl t) {
-                assertId(t.request, 'g', 'n', 'v')
-                assertId(t.cachedResult.id, 'group', 'name', 'version')
-                assert t.changing
-                t.refresh()
-            }
-        })
-        cachePolicy.mustRefreshChangingModule(moduleIdentifier('g', 'n', 'v'), moduleVersion('group', 'name', 'version'), 0)
-    }
-
-    def "provides details of cached artifact"() {
-        expect:
-        cachePolicy.eachArtifact(new Action<ArtifactResolutionControl>() {
-            void execute(ArtifactResolutionControl t) {
-                assertId(t.request.moduleVersionIdentifier, 'group', 'name', 'version')
-                assert t.request.name == 'artifact'
-                assert t.request.type == 'type'
-                assert t.request.extension == 'ext'
-                assert t.request.classifier == 'classifier'
-                assert t.cachedResult == null
-                t.refresh()
-            }
-        })
-        def artifactIdentifier = new DefaultArtifactIdentifier(moduleIdentifier('group', 'name', 'version'), 'artifact', 'type', 'ext', 'classifier')
-        cachePolicy.mustRefreshArtifact(artifactIdentifier, null, 0, true, true)
-    }
-
-    def "can use cacheFor to control missing module and artifact timeout"() {
-        when:
-        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
-            void execute(ModuleResolutionControl t) {
-                if (t.cachedResult == null) {
-                    t.cacheFor(10, TimeUnit.SECONDS)
-                }
-            }
-        });
-        cachePolicy.eachArtifact(new Action<ArtifactResolutionControl>() {
-            void execute(ArtifactResolutionControl t) {
-                if (t.cachedResult == null) {
-                    t.cacheFor(20, TimeUnit.SECONDS)
-                }
-            }
-        });
-
-        then:
-        hasDynamicVersionTimeout(DAY)
-        hasChangingModuleTimeout(DAY)
-        hasModuleTimeout(FOREVER)
-        hasMissingModuleTimeout(10 * SECOND)
-        hasMissingArtifactTimeout(20 * SECOND)
-    }
-
-    def "must refresh artifact for changing modules when moduledescriptorhash not in sync"() {
-        expect:
-        !cachePolicy.mustRefreshArtifact(null, null, 1000, false, true)
-        !cachePolicy.mustRefreshArtifact(null, null, 1000, false, false)
-        cachePolicy.mustRefreshArtifact(null, null, 1000, true, false)
-    }
-
-    def "provides a copy"() {
-        expect:
-        def copy = cachePolicy.copy()
-
-        !copy.is(cachePolicy)
-        assertThat(copy).doesNotShareStateWith(cachePolicy)
-
-        copy.dependencyCacheRules == cachePolicy.dependencyCacheRules
-        copy.moduleCacheRules == cachePolicy.moduleCacheRules
-        copy.artifactCacheRules == cachePolicy.artifactCacheRules
-    }
-
-    private def hasDynamicVersionTimeout(int timeout) {
-        def moduleId = moduleIdentifier('group', 'name', 'version')
-        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, 100)
-        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout);
-        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout - 1)
-        cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout + 1)
-    }
-
-    private def hasChangingModuleTimeout(int timeout) {
-        def module = moduleVersion('group', 'name', 'version')
-        assert !cachePolicy.mustRefreshChangingModule(null, module, timeout - 1)
-        assert !cachePolicy.mustRefreshChangingModule(null, module, timeout);
-        cachePolicy.mustRefreshChangingModule(null, module, timeout + 1)
-    }
-
-    private def hasModuleTimeout(int timeout) {
-        def module = moduleVersion('group', 'name', 'version')
-        assert !cachePolicy.mustRefreshModule(null, module, null, timeout);
-        assert !cachePolicy.mustRefreshModule(null, module, null, timeout - 1)
-        if (timeout == FOREVER) {
-            return true
-        }
-        cachePolicy.mustRefreshModule(null, module, timeout + 1)
-    }
-
-    private def hasMissingModuleTimeout(int timeout) {
-        assert !cachePolicy.mustRefreshModule(null, null, null, timeout);
-        assert !cachePolicy.mustRefreshModule(null, null, null, timeout - 1)
-        cachePolicy.mustRefreshModule(null, null, null, timeout + 1)
-    }
-
-    private def hasChangingArtifactTimeout(int timeout){
-        cachePolicy.mustRefreshArtifact(null, null, timeout, true, true)
-    }
-
-    private def hasMissingArtifactTimeout(int timeout) {
-        assert !cachePolicy.mustRefreshArtifact(null, null, timeout, true, true);
-        assert !cachePolicy.mustRefreshArtifact(null, null, timeout - 1, true, true)
-        cachePolicy.mustRefreshArtifact(null, null, timeout + 1, true, true)
-    }
-
-    private def assertId(def moduleId, String group, String name, String version) {
-        assert moduleId.group == group
-        assert moduleId.name == name
-        assert moduleId.version == version
-    }
-
-    private def moduleSelector(String group, String name, String version) {
-        new DefaultModuleVersionSelector(group, name, version)
-    }
-
-    private def moduleIdentifier(String group, String name, String version) {
-        new DefaultModuleVersionIdentifier(group, name, version)
-    }
-
-    private def moduleVersion(String group, String name, String version) {
-        return new ResolvedModuleVersion() {
-            ModuleVersionIdentifier getId() {
-                return new DefaultModuleVersionIdentifier(group, name, version);
-            }
-        }
-    }
-
-}
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
deleted file mode 100644
index 577f4eb..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
+++ /dev/null
@@ -1,193 +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.resolutionstrategy;
-
-
-import org.gradle.api.Action
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import spock.lang.Specification
-
-import java.util.concurrent.TimeUnit
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-import static org.gradle.util.Assertions.assertThat
-
-public class DefaultResolutionStrategySpec extends Specification {
-
-    def cachePolicy = Mock(DefaultCachePolicy)
-    def strategy = new DefaultResolutionStrategy(cachePolicy, [] as Set)
-
-    def "allows setting forced modules"() {
-        expect:
-        strategy.forcedModules.empty
-
-        when:
-        strategy.force 'org.foo:bar:1.0', 'org.foo:baz:2.0'
-
-        then:
-        def versions = strategy.forcedModules as List
-        versions.size() == 2
-
-        versions[0].group == 'org.foo'
-        versions[0].name == 'bar'
-        versions[0].version == '1.0'
-
-        versions[1].group == 'org.foo'
-        versions[1].name == 'baz'
-        versions[1].version == '2.0'
-    }
-
-    def "allows replacing forced modules"() {
-        given:
-        strategy.force 'org.foo:bar:1.0'
-
-        when:
-        strategy.forcedModules = ['hello:world:1.0', [group:'g', name:'n', version:'1']]
-
-        then:
-        def versions = strategy.forcedModules as List
-        versions.size() == 2
-        versions[0].group == 'hello'
-        versions[1].group == 'g'
-    }
-
-    def "provides no op resolve rule when no rules or forced modules configured"() {
-        given:
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        strategy.dependencyResolveRule.execute(details)
-
-        then:
-        0 * details._
-    }
-
-    def "provides dependency resolve rule that forces modules"() {
-        given:
-        strategy.force 'org:bar:1.0', 'org:foo:2.0'
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        strategy.dependencyResolveRule.execute(details)
-
-        then:
-        _ * details.getRequested() >> newSelector("org", "foo", "1.0")
-        1 * details.useVersion("2.0", VersionSelectionReasons.FORCED)
-        0 * details._
-    }
-
-    def "provides dependency resolve rule that orderly aggregates user specified rules"() {
-        given:
-        strategy.eachDependency({ it.useVersion("1.0") } as Action)
-        strategy.eachDependency({ it.useVersion("2.0") } as Action)
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        strategy.dependencyResolveRule.execute(details)
-
-        then:
-        1 * details.useVersion("1.0")
-        then:
-        1 * details.useVersion("2.0")
-        0 * details._
-    }
-
-    def "provides dependency resolve rule with forced modules first and then user specified rules"() {
-        given:
-        strategy.force 'org:bar:1.0', 'org:foo:2.0'
-        strategy.eachDependency({ it.useVersion("5.0") } as Action)
-        strategy.eachDependency({ it.useVersion("6.0") } as Action)
-
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        strategy.dependencyResolveRule.execute(details)
-
-        then: //forced modules:
-        _ * details.requested >> newSelector("org", "foo", "1.0")
-        1 * details.useVersion("2.0", VersionSelectionReasons.FORCED)
-
-        then: //user rules, in order:
-        1 * details.useVersion("5.0")
-        then:
-        1 * details.useVersion("6.0")
-        0 * details._
-    }
-
-    def "copied instance does not share state"() {
-        when:
-        def copy = strategy.copy()
-
-        then:
-        !copy.is(strategy)
-        assertThat(copy).doesNotShareStateWith(strategy)
-    }
-
-    def "provides a copy"() {
-        given:
-        def newCachePolicy = Mock(DefaultCachePolicy)
-        cachePolicy.copy() >> newCachePolicy
-
-        strategy.failOnVersionConflict()
-        strategy.force("org:foo:1.0")
-        strategy.eachDependency(Mock(Action))
-
-        when:
-        def copy = strategy.copy()
-
-        then:
-        copy.forcedModules == strategy.forcedModules
-        copy.dependencyResolveRules == strategy.dependencyResolveRules
-        copy.conflictResolution instanceof StrictConflictResolution
-
-        strategy.cachePolicy == cachePolicy
-        copy.cachePolicy == newCachePolicy
-    }
-
-    def "configures changing modules cache with jdk5+ units"() {
-        when:
-        strategy.cacheChangingModulesFor(30000, "milliseconds")
-
-        then:
-        1 * cachePolicy.cacheChangingModulesFor(30000, TimeUnit.MILLISECONDS)
-    }
-
-    def "configures changing modules cache with jdk6+ units"() {
-        when:
-        strategy.cacheChangingModulesFor(5, "minutes")
-
-        then:
-        1 * cachePolicy.cacheChangingModulesFor(5 * 60 * 1000, TimeUnit.MILLISECONDS)
-    }
-
-    def "configures dynamic version cache with jdk5+ units"() {
-        when:
-        strategy.cacheDynamicVersionsFor(10000, "milliseconds")
-
-        then:
-        1 * cachePolicy.cacheDynamicVersionsFor(10000, TimeUnit.MILLISECONDS)
-    }
-
-    def "configures dynamic version cache with jdk6+ units"() {
-        when:
-        strategy.cacheDynamicVersionsFor(1, "hours")
-
-        then:
-        1 * cachePolicy.cacheDynamicVersionsFor(1 * 60 * 60 * 1000, TimeUnit.MILLISECONDS)
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index e806a87..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
+++ /dev/null
@@ -1,89 +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.resolutionstrategy
-
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
-
-class ModuleForcingResolveRuleSpec extends Specification {
-
-    def "forces modules"() {
-        given:
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        new ModuleForcingResolveRule([
-            newSelector("org",  "module1", "1.0"),
-            newSelector("org",  "module2", "2.0"),
-            //exotic module with colon in the name
-            newSelector("org",  "module:with:colon", "3.0"),
-            newSelector("org:with:colon",  "module2", "4.0")
-        ]).execute(details)
-
-        then:
-        _ * details.getRequested() >> requested
-        1 * details.useVersion(forcedVersion, VersionSelectionReasons.FORCED)
-        0 * details._
-
-        where:
-        requested                                        | forcedVersion
-        newSelector("org",  "module2", "0.9")            | "2.0"
-        newSelector("org",  "module2", "2.1")            | "2.0"
-        newSelector("org",  "module:with:colon", "2.0")  | "3.0"
-        newSelector("org:with:colon",  "module2", "5.0") | "4.0"
-    }
-
-    def "does not force modules if they dont match"() {
-        given:
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        new ModuleForcingResolveRule([
-            newSelector("org",  "module1", "1.0"),
-            newSelector("org",  "module2", "2.0"),
-            newSelector("org",  "module:with:colon", "3.0"),
-            newSelector("org:with:colon",  "module2", "4.0")
-        ]).execute(details)
-
-        then:
-        _ * details.getRequested() >> requested
-        0 * details._
-
-        where:
-        requested << [
-            newSelector("orgX", "module2", "0.9"),
-            newSelector("org",  "moduleX", "2.9"),
-            newSelector("orgX",  "module:with:colon", "2.9"),
-            newSelector("org:with:colon",  "moduleX", "2.9"),
-            newSelector("org:with",  "colon:module2", "2.9"),
-            newSelector("org",  "with:colon:module2", "2.9"),
-        ]
-    }
-
-    def "does not force anything when input empty"() {
-        def details = Mock(DependencyResolveDetailsInternal)
-
-        when:
-        new ModuleForcingResolveRule([]).execute(details)
-
-        then:
-        0 * details._
-    }
-}
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
deleted file mode 100644
index c9b42b8..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
+++ /dev/null
@@ -1,957 +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.resolveengine
-
-import org.apache.ivy.core.module.descriptor.*
-import org.apache.ivy.core.module.id.ArtifactId
-import org.apache.ivy.core.module.id.ModuleRevisionId
-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.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.ivyservice.*
-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.TransientConfigurationResultsBuilder
-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.ResolutionResultBuilder
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import org.gradle.api.internal.artifacts.metadata.ModuleDescriptorAdapter
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionMetaData
-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 static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId
-
-class DependencyGraphBuilderTest extends Specification {
-    final ConfigurationInternal configuration = Mock()
-    final ModuleConflictResolver conflictResolver = Mock()
-    final DependencyToModuleVersionIdResolver dependencyResolver = Mock()
-    final ArtifactResolver artifactResolver = Mock()
-    final ResolutionResultBuilder resultBuilder = Mock()
-    final ModuleVersionMetaData root = revision('root')
-    final ModuleToModuleVersionResolver moduleResolver = Mock()
-    final DependencyToConfigurationResolver dependencyToConfigurationResolver = new DefaultDependencyToConfigurationResolver()
-    final DependencyGraphBuilder builder = new DependencyGraphBuilder(dependencyResolver, moduleResolver, artifactResolver, conflictResolver, dependencyToConfigurationResolver)
-
-    def setup() {
-        config(root, 'root', 'default')
-        _ * configuration.name >> 'root'
-        _ * configuration.path >> 'root'
-        _ * moduleResolver.resolve(_, _, _) >> { it[2].resolved(root) }
-
-        _ * artifactResolver.resolveModuleArtifacts(_, _, _,) >> { ModuleVersionMetaData module, ArtifactResolveContext context, BuildableArtifactSetResolveResult result ->
-            result.resolved(module.artifacts)
-        }
-    }
-
-    def "does not resolve a given module selector more than once"() {
-        given:
-        def a = revision("a")
-        def b = revision("b")
-        def c = revision("c")
-        traverses root, a
-        traverses root, b
-        traverses a, c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c)
-    }
-
-    private DefaultLenientConfiguration resolve() {
-        def results = new DefaultResolvedConfigurationBuilder(new TransientConfigurationResultsBuilder(new DummyBinaryStore(), new DummyStore()))
-        builder.resolve(configuration, resultBuilder, results)
-        new DefaultLenientConfiguration(configuration, results, Stub(CacheLockingManager))
-    }
-
-    def "correctly notifies the resolution result builder"() {
-        given:
-        def a = revision("a")
-        def b = revision("b")
-        def c = revision("c")
-        def d = revision("d")
-        traverses root, a
-        traverses root, b
-        traverses a, c
-        traversesMissing a, d
-
-        when:
-        resolve()
-
-        then:
-        1 * resultBuilder.start(newId("group", "root", "1.0"), new DefaultModuleComponentIdentifier("group", "root", "1.0"))
-        then:
-        1 * resultBuilder.resolvedConfiguration({ it.name == 'root' }, { it*.requested.module == ['a', 'b'] })
-        then:
-        1 * resultBuilder.resolvedConfiguration({ it.name == 'a' }, { it*.requested.module == ['c', 'd'] && it*.failure.count { it != null } == 1 })
-    }
-
-    def "does not resolve a given dynamic module selector more than once"() {
-        given:
-        def a = revision("a")
-        def b = revision("b")
-        def c = revision("c")
-        def d = revision("d")
-        traverses root, a
-        traverses root, b
-        traverses root, c
-        traverses a, d, revision: 'latest'
-        doesNotResolve b, d, revision: 'latest'
-        doesNotResolve c, d
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c, d)
-    }
-
-    def "does not include evicted module when selected module already traversed before conflict detected"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, selected
-        traverses selected, c
-        traverses root, b
-        traverses b, d
-        doesNotResolve d, evicted // Conflict is deeper than all dependencies of selected module
-        doesNotResolve evicted, e
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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._
-
-        and:
-        modules(result) == ids(selected, b, c, d)
-    }
-
-    def "does not include evicted module when evicted module already traversed before conflict detected"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, evicted
-        traverses evicted, c
-        traverses root, b
-        traverses b, d
-        traverses d, selected // Conflict is deeper than all dependencies of other module
-        traverses selected, e
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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._
-
-        and:
-        modules(result) == ids(selected, b, d, e)
-    }
-
-    def "does not include evicted module when path through evicted module is queued for traversal when conflict detected"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, evicted
-        traverses evicted, c
-        doesNotResolve c, d
-        traverses root, b
-        traverses b, selected
-        traverses selected, e
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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._
-
-        and:
-        modules(result) == ids(selected, b, e)
-    }
-
-    def "resolves when path through selected module is queued for traversal when conflict detected"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, selected
-        traverses selected, b
-        doesNotResolve root, evicted
-        doesNotResolve evicted, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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._
-
-        and:
-        modules(result) == ids(selected, b)
-    }
-
-    def "does not include evicted module when another path through evicted module traversed after conflict detected"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        traverses root, evicted
-        doesNotResolve evicted, d
-        traverses root, selected
-        traverses selected, c
-        traverses root, b
-        doesNotResolve b, evicted
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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._
-
-        and:
-        modules(result) == ids(selected, b, c)
-    }
-
-    def "restarts conflict resolution when later conflict on same module discovered"() {
-        given:
-        def selectedA = revision('a', '1.2')
-        def evictedA1 = revision('a', '1.1')
-        def evictedA2 = revision('a', '1.0')
-        def selectedB = revision('b', '2.2')
-        def evictedB = revision('b', '2.1')
-        def c = revision('c')
-        traverses root, evictedA1
-        traverses root, selectedA
-        traverses selectedA, c
-        traverses root, evictedB
-        traverses root, selectedB
-        doesNotResolve selectedB, evictedA2
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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*.version == ['2.1', '2.2'] }) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '2.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._
-
-        and:
-        modules(result) == ids(selectedA, c, selectedB)
-    }
-
-    def "does not include module version that is excluded after conflict resolution has been applied"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def evicted = revision('c', '1')
-        def selected = revision('c', '2')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, evicted
-        traverses root, a, exclude: b
-        doesNotResolve evicted, a
-        traverses a, b
-        traverses root, d
-        traverses d, e
-        traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '2' }
-        }
-        0 * conflictResolver._
-
-        and:
-        modules(result) == ids(a, selected, d, e)
-    }
-
-    def "does not include dependencies of module version that is no longer transitive after conflict resolution has been applied"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def evicted = revision('c', '1')
-        def selected = revision('c', '2')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, evicted
-        traverses root, a, transitive: false
-        doesNotResolve evicted, a
-        traverses a, b
-        traverses root, d
-        traverses d, e
-        traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '2' }
-        }
-        0 * conflictResolver._
-
-        and:
-        modules(result) == ids(a, selected, d, e)
-    }
-
-    def "does not attempt to resolve a dependency whose target module is excluded earlier in the path"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses a, b, exclude: c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b)
-    }
-
-    def "does not include the artifacts of evicted modules"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        traverses root, selected
-        doesNotResolve root, evicted
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        1 * conflictResolver.select(!null) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '1.2' }
-        }
-
-        and:
-        artifacts(result) == ids(selected)
-    }
-
-    def "does not include the artifacts of excluded modules when excluded by all paths"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        traverses root, a
-        traverses a, b, exclude: c
-        doesNotResolve b, c
-        traverses root, d, exclude: c
-        doesNotResolve d, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, d)
-        artifacts(result) == ids(a, b, d)
-    }
-
-    def "includes a module version when there is a path to the version that does not exclude it"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        traverses root, a
-        traverses a, b, exclude: c
-        doesNotResolve b, c
-        traverses root, d
-        traverses d, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c, d)
-        artifacts(result) == ids(a, b, c, d)
-    }
-
-    def "ignores a new incoming path that includes a subset of those already included"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses a, b
-        traverses root, c, exclude: b
-        doesNotResolve c, a
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c)
-        artifacts(result) == ids(a, b, c)
-    }
-
-    def "ignores a new incoming path that includes the same set of module versions"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, a, exclude: e
-        traverses a, b
-        traverses a, c
-        traverses b, d
-        doesNotResolve c, d
-        doesNotResolve d, e
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c, d)
-        artifacts(result) == ids(a, b, c, d)
-    }
-
-    def "restarts traversal when new incoming path excludes fewer module versions"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a, exclude: b
-        traverses root, c
-        doesNotResolve c, a
-        traverses a, b
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b, c)
-        artifacts(result) == ids(a, b, c)
-    }
-
-    def "does not traverse outgoing paths of a non-transitive dependency"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses a, b, transitive: false
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(a, b)
-        artifacts(result) == ids(a, b)
-    }
-
-    def "reports shortest incoming paths for a failed dependency"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses root, b
-        doesNotResolve b, a
-        traversesBroken a, c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
-        unresolved.problem instanceof ModuleVersionResolveException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionResolveException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0"
-        e.cause.message.contains "group:root:1.0 > group:b:1.0"
-        !e.cause.message.contains("group:root:1.0 > group:b:1.0 > group:a:1.0")
-    }
-
-    def "reports failure to resolve version selector to module version"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        traverses root, a
-        traverses root, b
-        doesNotResolve b, a
-        brokenSelector b, 'unknown'
-        brokenSelector a, 'unknown'
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'unknown', '1.0')
-        unresolved.problem instanceof ModuleVersionResolveException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionResolveException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0"
-        e.cause.message.contains "group:root:1.0 > group:b:1.0"
-    }
-
-    def "merges all failures for all dependencies with a given module version selector"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses root, b
-        traversesBroken a, c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
-        unresolved.problem instanceof ModuleVersionResolveException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionResolveException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0"
-        e.cause.message.contains "group:root:1.0 > group:b:1.0"
-    }
-
-    def "reports shortest incoming paths for a missing module version"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses root, b
-        doesNotResolve b, a
-        traversesMissing a, c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
-        unresolved.problem instanceof ModuleVersionNotFoundException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionNotFoundException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0"
-        e.cause.message.contains "group:root:1.0 > group:b:1.0"
-        !e.cause.message.contains("group:root:1.0 > group:b:1.0 > group:a:1.0")
-    }
-
-    def "merges all dependencies with a given module version selector when reporting missing version"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses root, b
-        traversesMissing a, c
-        doesNotResolve b, c
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
-        unresolved.problem instanceof ModuleVersionResolveException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionNotFoundException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0"
-        e.cause.message.contains "group:root:1.0 > group:b:1.0"
-    }
-
-    def "can handle a cycle in the incoming paths of a broken module"() {
-        given:
-        def a = revision('a')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, a
-        traverses a, b
-        doesNotResolve b, a
-        traversesMissing b, c
-
-        when:
-        def result = resolve()
-
-        then:
-        result.unresolvedModuleDependencies.size() == 1
-        def unresolved = result.unresolvedModuleDependencies.iterator().next()
-        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
-        unresolved.problem instanceof ModuleVersionResolveException
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException e = thrown()
-        e.cause instanceof ModuleVersionNotFoundException
-        e.cause.message.contains "group:root:1.0 > group:a:1.0 > group:b:1.0"
-    }
-
-    def "does not report a path through an evicted version"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        def d = revision('d')
-        def e = revision('e')
-        traverses root, evicted
-        traverses evicted, b
-        traversesMissing b, c
-        traverses root, d
-        traverses d, e
-        traverses e, selected
-        doesNotResolve selected, c
-
-        when:
-        def result = resolve()
-
-        then:
-        1 * conflictResolver.select(!null) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '1.2' }
-        }
-
-        when:
-        result.rethrowFailure()
-
-        then:
-        ResolveException ex = thrown()
-        ex.cause instanceof ModuleVersionNotFoundException
-        !ex.cause.message.contains("group:a:1.1")
-        ex.cause.message.contains "group:root:1.0 > group:a:1.2"
-
-        and:
-        modules(result) == ids(selected, d, e)
-    }
-
-    def "fails when conflict resolution selects a version that does not exist"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        traverses root, evicted
-        traverses root, b
-        traversesMissing b, selected
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        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")
-    }
-
-    def "does not fail when conflict resolution evicts a version that does not exist"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        traversesMissing root, evicted
-        traverses root, b
-        traverses b, selected
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        1 * conflictResolver.select(!null) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '1.2' }
-        }
-
-        and:
-        modules(result) == ids(selected, b)
-    }
-
-    def "does not fail when a broken version is evicted"() {
-        given:
-        def selected = revision('a', '1.2')
-        def evicted = revision('a', '1.1')
-        def b = revision('b')
-        def c = revision('c')
-        traverses root, evicted
-        traversesBroken evicted, b
-        traverses root, c
-        traverses c, selected
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        1 * conflictResolver.select(!null) >> {
-            Collection<ModuleRevisionResolveState> candidates = it[0]
-            return candidates.find { it.version == '1.2' }
-        }
-
-        and:
-        modules(result) == ids(selected, c)
-    }
-
-    def "direct dependency can force a particular version"() {
-        given:
-        def forced = revision("a", "1")
-        def evicted = revision("a", "2")
-        def b = revision("b")
-        traverses root, b
-        traverses root, forced, force: true
-        doesNotResolve b, evicted
-
-        when:
-        def result = resolve()
-        result.rethrowFailure()
-
-        then:
-        modules(result) == ids(forced, b)
-    }
-
-    def revision(String name, String revision = '1.0') {
-        DefaultModuleDescriptor descriptor = new DefaultModuleDescriptor(createModuleRevisionId("group", name, revision), "release", new Date())
-        ModuleVersionMetaData metaData = new ModuleDescriptorAdapter(descriptor)
-        config(metaData, 'default')
-        descriptor.addArtifact('default', new DefaultArtifact(descriptor.moduleRevisionId, new Date(), "art1", "art", "zip"))
-        return metaData
-    }
-
-    def config(ModuleVersionMetaData metaData, String name, String... extendsFrom) {
-        def configuration = new Configuration(name, Configuration.Visibility.PUBLIC, null, extendsFrom, true, null)
-        metaData.descriptor.addConfiguration(configuration)
-        return configuration
-    }
-
-    def traverses(Map<String, ?> args = [:], ModuleVersionMetaData from, ModuleVersionMetaData to) {
-        def descriptor = dependsOn(args, from.descriptor, to.descriptor.moduleRevisionId)
-        def idResolveResult = selectorResolvesTo(descriptor, to.id);
-        ComponentResolveResult resolveResult = Mock()
-        1 * idResolveResult.resolve() >> resolveResult
-        _ * 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
-        _ * result.id >> to.id;
-        _ * result.failure >> null
-        _ * result.selectionReason >> null
-        0 * result._
-    }
-
-    def traversesMissing(Map<String, ?> args = [:], ModuleVersionMetaData from, ModuleVersionMetaData to) {
-        def descriptor = dependsOn(args, from.descriptor, to.descriptor.moduleRevisionId)
-        def idResolveResult = selectorResolvesTo(descriptor, to.id)
-        ComponentResolveResult resolveResult = Mock()
-        1 * idResolveResult.resolve() >> resolveResult
-        _ * resolveResult.failure >> { return new ModuleVersionNotFoundException(newId("org", "a", "1.2")) }
-    }
-
-    def traversesBroken(Map<String, ?> args = [:], ModuleVersionMetaData from, ModuleVersionMetaData to) {
-        def descriptor = dependsOn(args, from.descriptor, to.descriptor.moduleRevisionId)
-        def idResolveResult = selectorResolvesTo(descriptor, to.id)
-        ComponentResolveResult resolveResult = Mock()
-        1 * idResolveResult.resolve() >> resolveResult
-        _ * resolveResult.failure >> { return new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken") }
-    }
-
-    ModuleVersionIdentifier toModuleVersionIdentifier(ModuleRevisionId moduleRevisionId) {
-        ModuleVersionIdentifier moduleVersionIdentifier = Mock();
-        (0..2) * moduleVersionIdentifier.group >> moduleRevisionId.organisation;
-        (0..2) * moduleVersionIdentifier.name >> moduleRevisionId.name;
-        (0..2) * moduleVersionIdentifier.version >> moduleRevisionId.revision;
-        moduleVersionIdentifier
-    }
-
-    def brokenSelector(Map<String, ?> args = [:], ModuleVersionMetaData from, String to) {
-        def descriptor = dependsOn(args, from.descriptor, createModuleRevisionId("group", to, "1.0"))
-        ModuleVersionIdResolveResult result = Mock()
-        (0..1) * dependencyResolver.resolve({it.descriptor == descriptor}) >> result
-        _ * result.failure >> new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
-        _ * result.selectionReason >> VersionSelectionReasons.REQUESTED
-        0 * result._
-    }
-
-    def dependsOn(Map<String, ?> args = [:], DefaultModuleDescriptor from, ModuleRevisionId to) {
-        ModuleDependency moduleDependency = Mock()
-        def dependencyId = args.revision ? createModuleRevisionId(to.moduleId.organisation, to.moduleId.name, args.revision) : to
-        boolean transitive = args.transitive == null || args.transitive
-        boolean force = args.force
-        def descriptor = new EnhancedDependencyDescriptor(moduleDependency, from, dependencyId, force, false, transitive)
-        descriptor.addDependencyConfiguration("default", "default")
-        if (args.exclude) {
-            descriptor.addExcludeRule("default", new DefaultExcludeRule(new ArtifactId(
-                    args.exclude.descriptor.moduleRevisionId.moduleId, PatternMatcher.ANY_EXPRESSION,
-                    PatternMatcher.ANY_EXPRESSION,
-                    PatternMatcher.ANY_EXPRESSION),
-                    ExactPatternMatcher.INSTANCE, null))
-        }
-        from.addDependency(descriptor)
-        return descriptor
-    }
-
-    def selectorResolvesTo(DependencyDescriptor descriptor, ModuleVersionIdentifier to) {
-        ModuleVersionIdResolveResult result = Mock()
-        1 * dependencyResolver.resolve({it.descriptor == descriptor}) >> result
-        1 * result.id >> to
-        return result
-    }
-
-    def ids(ModuleVersionMetaData... descriptors) {
-        return descriptors.collect { it.id } as Set
-    }
-
-    def modules(LenientConfiguration config) {
-        Set<ModuleVersionIdentifier> result = new LinkedHashSet<ModuleVersionIdentifier>()
-        List<ResolvedDependency> queue = []
-        queue.addAll(config.getFirstLevelModuleDependencies({ true } as Spec))
-        while (!queue.empty) {
-            def node = queue.remove(0)
-            result.add(node.module.id)
-            queue.addAll(0, node.children)
-        }
-        return result
-    }
-
-    def artifacts(LenientConfiguration config) {
-        return config.resolvedArtifacts.collect { it.moduleVersion.id } as Set
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpecTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpecTest.groovy
deleted file mode 100644
index e9a4ca1..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleVersionSpecTest.groovy
+++ /dev/null
@@ -1,368 +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.resolveengine
-
-import org.apache.ivy.core.module.descriptor.DefaultExcludeRule
-import org.apache.ivy.core.module.id.ArtifactId
-import org.apache.ivy.plugins.matcher.ExactPatternMatcher
-import org.apache.ivy.plugins.matcher.RegexpPatternMatcher
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
-
-class ModuleVersionSpecTest extends Specification {
-    def "accepts all module by default"() {
-        def spec = ModuleVersionSpec.forExcludes()
-
-        expect:
-        spec.isSatisfiedBy(createModuleId("org", "module"))
-    }
-
-    def "default specs accept the same modules as each other"() {
-        expect:
-        ModuleVersionSpec.forExcludes().acceptsSameModulesAs(ModuleVersionSpec.forExcludes())
-    }
-
-    def "does not accept module version that matches any exclude rule"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def rule3 = excludeRule("org2", "*")
-        def rule4 = excludeRule("*", "module4")
-        def rule5 = regExpExcludeRule("regexp-\\d+", "module\\d+")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4, rule5)
-
-        expect:
-        !spec.isSatisfiedBy(createModuleId("org", "module"))
-        !spec.isSatisfiedBy(createModuleId("org", "module2"))
-        !spec.isSatisfiedBy(createModuleId("org2", "anything"))
-        !spec.isSatisfiedBy(createModuleId("other", "module4"))
-        !spec.isSatisfiedBy(createModuleId("regexp-72", "module12"))
-        spec.isSatisfiedBy(createModuleId("org", "other"))
-        spec.isSatisfiedBy(createModuleId("regexp-72", "other"))
-        spec.isSatisfiedBy(createModuleId("regexp", "module2"))
-    }
-
-    def "specs with the same set of exclude rules accept the same modules as each other"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def rule3 = excludeRule("org2", "*")
-        def rule4 = excludeRule("*", "module4")
-        def rule5 = regExpExcludeRule("pattern1", "pattern2")
-        def exactMatchSpec = ModuleVersionSpec.forExcludes(rule1)
-        def moduleWildcard = ModuleVersionSpec.forExcludes(rule3)
-        def groupWildcard = ModuleVersionSpec.forExcludes(rule4)
-        def regexp = ModuleVersionSpec.forExcludes(rule5)
-        def manyRules = ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4, rule5)
-
-        expect:
-        exactMatchSpec.acceptsSameModulesAs(exactMatchSpec)
-        exactMatchSpec.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1))
-
-        !exactMatchSpec.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule2))
-        !exactMatchSpec.acceptsSameModulesAs(ModuleVersionSpec.forExcludes())
-        !exactMatchSpec.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2))
-
-        moduleWildcard.acceptsSameModulesAs(moduleWildcard)
-        moduleWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule3))
-
-        !moduleWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1))
-        !moduleWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule3))
-        !moduleWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes())
-        !moduleWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(excludeRule("org3", "*")))
-
-        groupWildcard.acceptsSameModulesAs(groupWildcard)
-        groupWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule4))
-
-        !groupWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1))
-        !groupWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule4))
-        !groupWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes())
-        !groupWildcard.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(excludeRule("*", "module5")))
-
-        regexp.acceptsSameModulesAs(regexp)
-        regexp.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule5))
-
-        !regexp.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1))
-        !regexp.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule5))
-        !regexp.acceptsSameModulesAs(ModuleVersionSpec.forExcludes())
-        !regexp.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(regExpExcludeRule("pattern", "other")))
-
-        manyRules.acceptsSameModulesAs(manyRules)
-        manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4, rule5))
-
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule3, rule4, rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule4, rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4))
-
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, excludeRule("org", "module3"), rule3, rule4, rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, excludeRule("org3", "*"), rule4, rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule3, excludeRule("*", "module5"), rule5))
-        !manyRules.acceptsSameModulesAs(ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4, regExpExcludeRule("other", "other")))
-    }
-
-    def "union accepts all modules when one spec has empty set of exclude rules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes()
-
-        expect:
-        spec.union(spec2) == spec2
-        spec2.union(spec) == spec2
-    }
-
-    def "union of a spec with itself returns the original spec"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-
-        expect:
-        spec.union(spec) == spec
-    }
-
-    def "union of two specs with the same exclude rule instances returns one of the original specs"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = regExpExcludeRule("org", "module2")
-        def rule3 = excludeRule("org", "*")
-        def rule4 = excludeRule("*", "module")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2, rule3, rule4)
-        def spec2 = ModuleVersionSpec.forExcludes(rule2, rule3, rule1, rule4)
-
-        expect:
-        spec.union(spec2) == spec
-    }
-
-    def "union of two specs with exact matching exclude rules uses the intersection of the exclude rules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def rule3 = excludeRule("org", "module3")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes(rule1, rule3)
-
-        expect:
-        def union = spec.union(spec2)
-        union instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union.excludeSpecs.size() == 1
-        union.excludeSpecs.any { it.moduleId == rule1.id.moduleId }
-    }
-
-    def "union of spec with module wildcard uses the most specific matching exclude rules"() {
-        def rule1 = excludeRule("org", "*")
-        def rule2 = excludeRule("org", "module")
-        def rule3 = excludeRule("org", "module2")
-        def rule4 = excludeRule("other", "module")
-        def rule5 = excludeRule("*", "module3")
-        def rule6 = excludeRule("org2", "*")
-        def spec = ModuleVersionSpec.forExcludes(rule1)
-
-        expect:
-        def union = spec.union(ModuleVersionSpec.forExcludes(rule2, rule3, rule4))
-        union instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union.excludeSpecs.size() == 2
-        union.excludeSpecs.any { it.moduleId == rule2.id.moduleId }
-        union.excludeSpecs.any { it.moduleId == rule3.id.moduleId }
-        
-        def union2 = spec.union(ModuleVersionSpec.forExcludes(rule5))
-        union2 instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union2.excludeSpecs.size() == 1
-        union2.excludeSpecs.any { it.moduleId.organisation == 'org' && it.moduleId.name == 'module3' }
-
-        def union3 = spec.union(ModuleVersionSpec.forExcludes(rule6, rule2))
-        union3 instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union3.excludeSpecs.size() == 1
-        union3.excludeSpecs.any { it.moduleId == rule2.id.moduleId }
-    }
-
-    def "union of spec with group wildcard uses the most specific matching exclude rules"() {
-        def rule1 = excludeRule("*", "module")
-        def rule2 = excludeRule("org", "module")
-        def rule3 = excludeRule("org", "module2")
-        def rule4 = excludeRule("other", "module")
-        def rule5 = excludeRule("org", "*")
-        def rule6 = excludeRule("*", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1)
-
-        expect:
-        def union = spec.union(ModuleVersionSpec.forExcludes(rule2, rule3, rule4))
-        union instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union.excludeSpecs.size() == 2
-        union.excludeSpecs.any { it.moduleId == rule2.id.moduleId }
-        union.excludeSpecs.any { it.moduleId == rule4.id.moduleId }
-
-        def union2 = spec.union(ModuleVersionSpec.forExcludes(rule5))
-        union2 instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        union2.excludeSpecs.size() == 1
-        union2.excludeSpecs.any { it.moduleId.organisation == 'org' && it.moduleId.name == 'module' }
-
-        def union3 = spec.union(ModuleVersionSpec.forExcludes(rule6))
-        union3 == ModuleVersionSpec.forExcludes()
-    }
-
-    def "union of two specs with disjoint exact matching exclude rules matches all modules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1)
-        def spec2 = ModuleVersionSpec.forExcludes(rule2)
-
-        expect:
-        def union = spec.union(spec2)
-        union == ModuleVersionSpec.forExcludes()
-    }
-
-    def "union of two specs with non-exact matching exclude rules is a union spec"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = regExpExcludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1)
-        def spec2 = ModuleVersionSpec.forExcludes(rule2)
-
-        expect:
-        def union = spec.union(spec2)
-        union instanceof ModuleVersionSpec.UnionSpec
-        union.specs.size() == 2
-        union.specs[0] == spec
-        union.specs[1] == spec2
-    }
-
-    def "union of union specs is the union of the original specs"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def rule3 = regExpExcludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1)
-        def spec2 = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec3 = ModuleVersionSpec.forExcludes(rule3)
-
-        expect:
-        def union = spec.union(spec3).union(spec2)
-
-        union instanceof ModuleVersionSpec.UnionSpec
-        union.specs.size() == 2
-        union.specs.any {
-            it instanceof ModuleVersionSpec.ExcludeRuleBackedSpec && it.excludeSpecs == spec.excludeSpecs
-        }
-        union.specs.contains(spec3)
-    }
-
-    def "union accept module that is accepted by any merged exclude rule"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes(rule1)
-
-        expect:
-        def union = spec.union(spec2)
-
-        !spec.isSatisfiedBy(createModuleId("org", "module"))
-        !union.isSatisfiedBy(createModuleId("org", "module"))
-
-        !spec.isSatisfiedBy(createModuleId("org", "module2"))
-        union.isSatisfiedBy(createModuleId("org", "module2"))
-    }
-
-    def "unions accepts same modules when original specs accept same modules"() {
-        def rule1 = regExpExcludeRule("org", "module")
-        def rule2 = regExpExcludeRule("org", "module2")
-        def rule3 = regExpExcludeRule("org", "module3")
-        def spec1 = ModuleVersionSpec.forExcludes(rule1)
-        def spec2 = ModuleVersionSpec.forExcludes(rule2)
-        def spec3 = ModuleVersionSpec.forExcludes(rule3)
-
-        expect:
-        spec1.union(spec2).acceptsSameModulesAs(spec2.union(spec1))
-
-        !spec1.union(spec2).acceptsSameModulesAs(spec2)
-        !spec1.union(spec2).acceptsSameModulesAs(spec1)
-        !spec1.union(spec2).acceptsSameModulesAs(spec1.union(spec3))
-    }
-
-    def "intersection accepts those modules accepted by other spec when one spec has empty set of exclude rules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes()
-
-        expect:
-        spec.intersect(spec2) == spec
-        spec2.intersect(spec) == spec
-    }
-
-    def "intersection does not accept module that is not accepted by any merged exclude rules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes(rule1)
-
-        expect:
-        def intersect = spec.intersect(spec2)
-
-        !spec.isSatisfiedBy(createModuleId("org", "module"))
-        !intersect.isSatisfiedBy(createModuleId("org", "module"))
-
-        !spec.isSatisfiedBy(createModuleId("org", "module2"))
-        !intersect.isSatisfiedBy(createModuleId("org", "module2"))
-
-        spec.isSatisfiedBy(createModuleId("org", "module3"))
-        spec2.isSatisfiedBy(createModuleId("org", "module3"))
-        intersect.isSatisfiedBy(createModuleId("org", "module3"))
-    }
-
-    def "intersection of a spec with itself returns the original spec"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-
-        expect:
-        spec.intersect(spec) == spec
-    }
-
-    def "intersection of two specs with exclude rules is the union of the exclude rules"() {
-        def rule1 = excludeRule("org", "module")
-        def rule2 = excludeRule("org", "module2")
-        def spec = ModuleVersionSpec.forExcludes(rule1, rule2)
-        def spec2 = ModuleVersionSpec.forExcludes(rule1)
-
-        expect:
-        def intersection = spec.intersect(spec2)
-        intersection instanceof ModuleVersionSpec.ExcludeRuleBackedSpec
-        intersection.excludeSpecs.size() == 2
-        intersection.excludeSpecs.any { it.moduleId == rule1.id.moduleId }
-        intersection.excludeSpecs.any { it.moduleId == rule2.id.moduleId }
-    }
-
-    def "intersections accepts same modules when original specs accept same modules"() {
-        def rule1 = regExpExcludeRule("org", "module")
-        def rule2 = regExpExcludeRule("org", "module2")
-        def rule3 = regExpExcludeRule("org", "module3")
-        def spec1 = ModuleVersionSpec.forExcludes(rule1).union(ModuleVersionSpec.forExcludes(rule2))
-        def spec2 = ModuleVersionSpec.forExcludes(rule2).union(ModuleVersionSpec.forExcludes(rule1))
-        def spec3 = ModuleVersionSpec.forExcludes(rule3)
-        assert spec1.acceptsSameModulesAs(spec2)
-
-        expect:
-        spec1.intersect(spec2).acceptsSameModulesAs(spec2.intersect(spec1))
-
-        !spec1.intersect(spec2).acceptsSameModulesAs(spec1)
-        !spec1.intersect(spec2).acceptsSameModulesAs(spec2)
-        !spec1.intersect(spec2).acceptsSameModulesAs(spec1.intersect(spec3))
-    }
-
-    def excludeRule(String org, String module) {
-        return new DefaultExcludeRule(new ArtifactId(createModuleId(org, module), "ivy", "ivy", "ivy"), ExactPatternMatcher.INSTANCE, [:])
-    }
-
-    def regExpExcludeRule(String org, String module) {
-        return new DefaultExcludeRule(new ArtifactId(createModuleId(org, module), "ivy", "ivy", "ivy"), RegexpPatternMatcher.INSTANCE, [:])
-    }
-}
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
deleted file mode 100644
index ff4ada1..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy
+++ /dev/null
@@ -1,43 +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.resolveengine
-
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
-import spock.lang.Specification
-
-class VersionSelectionReasonResolverTest extends Specification {
-
-    def "configures selection reason"() {
-        def delegate = Mock(ModuleConflictResolver)
-        VersionSelectionReasonResolver resolver = new VersionSelectionReasonResolver(delegate)
-
-        def candidate1 = Mock(ModuleRevisionResolveState)
-        def candidate2 = Mock(ModuleRevisionResolveState)
-
-        when:
-        def out = resolver.select([candidate1, candidate2])
-
-        then:
-        out == candidate2
-
-        and:
-        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
deleted file mode 100644
index 7e1d0b2..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.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.ivyservice.resolveengine.result
-
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-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
-
-class CachingDependencyResultFactoryTest extends Specification {
-
-    CachingDependencyResultFactory factory = new CachingDependencyResultFactory()
-
-    def "creates and caches resolved dependencies"() {
-        def fromModule = newModule('from')
-        def selectedModule = newModule('selected')
-
-        when:
-        def dep = factory.createResolvedDependency(selector('requested'), fromModule, selectedModule)
-        def same = factory.createResolvedDependency(selector('requested'), fromModule, selectedModule)
-
-        def differentRequested = factory.createResolvedDependency(selector('xxx'), fromModule, selectedModule)
-        def differentFrom = factory.createResolvedDependency(selector('requested'), newModule('xxx'), selectedModule)
-        def differentSelected = factory.createResolvedDependency(selector('requested'), fromModule, newModule('xxx'))
-
-        then:
-        dep.is(same)
-        !dep.is(differentFrom)
-        !dep.is(differentRequested)
-        !dep.is(differentSelected)
-    }
-
-    def "creates and caches unresolved dependencies"() {
-        def fromModule = newModule('from')
-        def selectedModule = Mock(ComponentSelectionReason)
-
-        when:
-        def dep = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
-        def same = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
-
-        def differentRequested = factory.createUnresolvedDependency(selector('xxx'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('xxx'), "foo"))
-        def differentFrom = factory.createUnresolvedDependency(selector('requested'), newModule('xxx'), selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
-        def differentFailure = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
-
-        then:
-        dep.is(same)
-        !dep.is(differentFrom)
-        !dep.is(differentRequested)
-        dep.is(differentFailure) //the same dependency edge cannot have different failures
-    }
-
-    def selector(String group='a', String module='a', String version='1') {
-        DefaultModuleComponentSelector.newSelector(group, module, version)
-    }
-
-    def moduleVersionSelector(String group='a', String module='a', String version='1') {
-        newSelector(group, module, version)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
deleted file mode 100644
index b410cf2..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
+++ /dev/null
@@ -1,60 +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.resolveengine.result
-
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentIdentifier
-import org.gradle.messaging.serialize.SerializerSpec
-
-class ComponentIdentifierSerializerTest extends SerializerSpec {
-    ComponentIdentifierSerializer serializer = new ComponentIdentifierSerializer()
-
-    def "throws exception if null is provided"() {
-        when:
-        serialize(null, serializer)
-
-        then:
-        Throwable t = thrown(IllegalArgumentException)
-        t.message == 'Provided component identifier may not be null'
-    }
-
-    def "serializes ModuleComponentIdentifier"() {
-        given:
-        ModuleComponentIdentifier selection = new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
-
-        when:
-        ModuleComponentIdentifier result = serialize(selection, serializer)
-
-        then:
-        result.group == 'group-one'
-        result.module == 'name-one'
-        result.version == 'version-one'
-    }
-
-    def "serializes BuildComponentIdentifier"() {
-        given:
-        ProjectComponentIdentifier selection = new DefaultProjectComponentIdentifier(':myPath')
-
-        when:
-        ProjectComponentIdentifier result = serialize(selection, serializer)
-
-        then:
-        result.projectPath == ':myPath'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.groovy
deleted file mode 100644
index 6e3a776..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.groovy
+++ /dev/null
@@ -1,54 +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.resolveengine.result
-
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.messaging.serialize.InputStreamBackedDecoder
-import org.gradle.messaging.serialize.SerializerSpec
-
-class ComponentSelectionReasonSerializerTest extends SerializerSpec {
-
-    private serializer = new ComponentSelectionReasonSerializer()
-
-    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 ComponentSelectionReason)
-        then:
-        thrown(IllegalArgumentException)
-
-        when:
-        serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream("foo".bytes)))
-
-        then:
-        thrown(IllegalArgumentException)
-    }
-
-    void check(ComponentSelectionReason 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/ComponentSelectorSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
deleted file mode 100644
index 8171354..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
+++ /dev/null
@@ -1,60 +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.resolveengine.result
-
-import org.gradle.api.artifacts.component.ProjectComponentSelector
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-import org.gradle.messaging.serialize.SerializerSpec
-
-public class ComponentSelectorSerializerTest extends SerializerSpec {
-    ComponentSelectorSerializer serializer = new ComponentSelectorSerializer()
-
-    def "throws exception if null is provided"() {
-        when:
-        serialize(null, serializer)
-
-        then:
-        Throwable t = thrown(IllegalArgumentException)
-        t.message == 'Provided component selector may not be null'
-    }
-
-    def "serializes ModuleComponentSelector"() {
-        given:
-        ModuleComponentSelector selection = new DefaultModuleComponentSelector('group-one', 'name-one', 'version-one')
-
-        when:
-        ModuleComponentSelector result = serialize(selection, serializer)
-
-        then:
-        result.group == 'group-one'
-        result.module == 'name-one'
-        result.version == 'version-one'
-    }
-
-    def "serializes BuildComponentSelector"() {
-        given:
-        ProjectComponentSelector selection = new DefaultProjectComponentSelector(':myPath')
-
-        when:
-        ProjectComponentSelector result = serialize(selection, serializer)
-
-        then:
-        result.projectPath == ':myPath'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
deleted file mode 100644
index feef498..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
+++ /dev/null
@@ -1,284 +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.resolveengine.result
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.artifacts.component.ComponentSelector
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.artifacts.result.ResolvedDependencyResult
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-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
-
-class DefaultResolutionResultBuilderSpec extends Specification {
-
-    def builder = new DefaultResolutionResultBuilder()
-
-    def "builds basic graph"() {
-        given:
-        builder.start(confId("root"), createComponentIdentifier("root"))
-
-        node("root")
-        node("mid1")
-        node("mid2")
-        node("leaf1")
-        node("leaf2")
-        node("leaf3")
-        node("leaf4")
-
-        resolvedConf("root", [dep("mid1"), dep("mid2")])
-
-        resolvedConf("mid1", [dep("leaf1"), dep("leaf2")])
-        resolvedConf("mid2", [dep("leaf3"), dep("leaf4")])
-
-        resolvedConf("leaf1", [])
-        resolvedConf("leaf2", [])
-        resolvedConf("leaf3", [])
-        resolvedConf("leaf4", [])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        printGraph(result.root) == """x:root:1
-  x:mid1:1 [root]
-    x:leaf1:1 [mid1]
-    x:leaf2:1 [mid1]
-  x:mid2:1 [root]
-    x:leaf3:1 [mid2]
-    x:leaf4:1 [mid2]
-"""
-    }
-
-    def "graph with multiple dependents"() {
-        given:
-        builder.start(confId("a"), createComponentIdentifier("a"))
-
-        node("a")
-        node("b1")
-        node("b2")
-        node("b3")
-
-        resolvedConf("a", [dep("b1"), dep("b2"), dep("b3")])
-
-        resolvedConf("b1", [dep("b2"), dep("b3")])
-        resolvedConf("b2", [dep("b3")])
-        resolvedConf("b3", [])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        printGraph(result.root) == """x:a:1
-  x:b1:1 [a]
-    x:b2:1 [a,b1]
-      x:b3:1 [a,b1,b2]
-  x:b2:1 [a,b1]
-    x:b3:1 [a,b1,b2]
-  x:b3:1 [a,b1,b2]
-"""
-    }
-
-    def "builds graph with cycles"() {
-        given:
-        builder.start(confId("a"), createComponentIdentifier("a"))
-        node("a")
-        node("b")
-        node("c")
-        resolvedConf("a", [dep("b")])
-        resolvedConf("b", [dep("c")])
-        resolvedConf("c", [dep("a")])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        printGraph(result.root) == """x:a:1
-  x:b:1 [a]
-    x:c:1 [b]
-      x:a:1 [c]
-"""
-    }
-
-    def "includes selection reason"() {
-        given:
-        builder.start(confId("a"), createComponentIdentifier("a"))
-        node("b", VersionSelectionReasons.FORCED)
-        node("c", VersionSelectionReasons.CONFLICT_RESOLUTION)
-        node("d")
-        resolvedConf("a", [dep("b"), dep("c"), dep("d", new RuntimeException("Boo!"))])
-        resolvedConf("b", [])
-        resolvedConf("c", [])
-        resolvedConf("d", [])
-
-        when:
-        def deps = builder.complete().root.dependencies
-
-        then:
-        def b = deps.find { it.selected.id.module == 'b' }
-        def c = deps.find { it.selected.id.module == 'c' }
-
-        b.selected.selectionReason.forced
-        c.selected.selectionReason.conflictResolution
-    }
-
-    def "links dependents correctly"() {
-        given:
-        builder.start(confId("a"), createComponentIdentifier("a"))
-        node("a")
-        node("b")
-        node("c")
-        resolvedConf("a", [dep("b")])
-        resolvedConf("b", [dep("c")])
-        resolvedConf("c", [dep("a")])
-
-        when:
-        def a = builder.complete().root
-
-        then:
-        def b  = first(a.dependencies).selected
-        def c  = first(b.dependencies).selected
-        def a2 = first(c.dependencies).selected
-
-        a2.is(a)
-
-        first(b.dependents).is(first(a.dependencies))
-        first(c.dependents).is(first(b.dependencies))
-        first(a.dependents).is(first(c.dependencies))
-
-        first(b.dependents).from.is(a)
-        first(c.dependents).from.is(b)
-        first(a.dependents).from.is(c)
-    }
-
-    ResolvedDependencyResult first(Set<? extends ResolvedDependencyResult> dependencies) {
-        dependencies.iterator().next()
-    }
-
-    def "accumulates and avoids duplicate dependencies"() {
-        given:
-        builder.start(confId("root"), createComponentIdentifier("root"))
-        node("root")
-        node("mid1")
-        node("leaf1")
-        node("leaf2")
-
-        resolvedConf("root", [dep("mid1")])
-
-        resolvedConf("mid1", [dep("leaf1")])
-        resolvedConf("mid1", [dep("leaf1")]) //dupe
-        resolvedConf("mid1", [dep("leaf2")])
-
-        resolvedConf("leaf1", [])
-        resolvedConf("leaf2", [])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        printGraph(result.root) == """x:root:1
-  x:mid1:1 [root]
-    x:leaf1:1 [mid1]
-    x:leaf2:1 [mid1]
-"""
-    }
-
-    def "accumulates and avoids duplicate unresolved dependencies"() {
-        given:
-        builder.start(confId("root"), createComponentIdentifier("root"))
-        node("mid1")
-        node("leaf1")
-        node("leaf2")
-        resolvedConf("root", [dep("mid1")])
-
-        resolvedConf("mid1", [dep("leaf1", new RuntimeException("foo!"))])
-        resolvedConf("mid1", [dep("leaf1", new RuntimeException("bar!"))]) //dupe
-        resolvedConf("mid1", [dep("leaf2", new RuntimeException("baz!"))])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        def mid1 = first(result.root.dependencies)
-        mid1.selected.dependencies.size() == 2
-        mid1.selected.dependencies*.requested.module == ['leaf1', 'leaf2']
-    }
-
-    def "graph includes unresolved deps"() {
-        given:
-        builder.start(confId("a"), createComponentIdentifier("a"))
-        node("b")
-        node("c")
-        resolvedConf("a", [dep("b"), dep("c"), dep("U", new RuntimeException("unresolved!"))])
-        resolvedConf("b", [])
-        resolvedConf("c", [])
-
-        when:
-        def result = builder.complete()
-
-        then:
-        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.
-"""
-    }
-
-    private void node(String module, ComponentSelectionReason reason = VersionSelectionReasons.REQUESTED) {
-        def moduleVersion = new DummyModuleVersionSelection(selectedId: newId("x", module, "1"), selectionReason: reason, componentId: new DefaultModuleComponentIdentifier("x", module, "1"))
-        builder.resolvedModuleVersion(moduleVersion)
-    }
-
-    private void resolvedConf(String module, List<InternalDependencyResult> deps) {
-        builder.resolvedConfiguration(confId(module), deps)
-    }
-
-    private InternalDependencyResult dep(String requested, Exception failure = null, String selected = requested) {
-        def selection = newId("x", selected, "1")
-        def selector = new DefaultModuleComponentSelector("x", requested, "1")
-        def moduleVersionSelector = newSelector("x", requested, "1")
-        failure = failure == null ? null : new ModuleVersionResolveException(moduleVersionSelector, failure)
-        new DummyInternalDependencyResult(requested: selector, selected: selection, failure: failure)
-    }
-
-    private ModuleVersionIdentifier confId(String module) {
-        newId("x", module, "1")
-    }
-
-    private ComponentIdentifier createComponentIdentifier(String module) {
-        new DefaultModuleComponentIdentifier("x", module, "1")
-    }
-
-    class DummyModuleVersionSelection implements ModuleVersionSelection {
-        ModuleVersionIdentifier selectedId
-        ComponentSelectionReason selectionReason
-        ComponentIdentifier componentId
-    }
-
-    class DummyInternalDependencyResult implements InternalDependencyResult {
-        ComponentSelector requested
-        ModuleVersionIdentifier selected
-        ModuleVersionResolveException failure
-        ComponentSelectionReason reason
-    }
-}
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
deleted file mode 100644
index 7661972..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
+++ /dev/null
@@ -1,48 +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.resolveengine.result
-
-import org.gradle.api.internal.cache.BinaryStore
-import org.gradle.messaging.serialize.Decoder
-import org.gradle.messaging.serialize.Encoder
-import org.gradle.messaging.serialize.InputStreamBackedDecoder
-import org.gradle.messaging.serialize.OutputStreamBackedEncoder
-
-public class DummyBinaryStore implements BinaryStore {
-
-    private final ByteArrayOutputStream bytes = new ByteArrayOutputStream()
-    private Encoder output = new OutputStreamBackedEncoder(bytes)
-
-    void write(BinaryStore.WriteAction write) {
-        write.write(output)
-    }
-
-    BinaryStore.BinaryData done() {
-        new BinaryStore.BinaryData() {
-            Decoder decoder
-            def <T> T read(BinaryStore.ReadAction<T> readAction) {
-                if (decoder == null) {
-                    decoder = new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray()))
-                }
-                readAction.read(decoder)
-            }
-
-            void close() {
-                decoder = null
-            }
-        }
-    }
-}
\ No newline at end of file
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
deleted file mode 100644
index d575fbc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy
+++ /dev/null
@@ -1,80 +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.resolveengine.result
-
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-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() >> DefaultModuleComponentSelector.newSelector("org", "foo", "1.0")
-            getFailure() >> null
-            getSelected() >> newId("org", "foo", "1.0")
-            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 == DefaultModuleComponentSelector.newSelector("org", "foo", "1.0")
-        out.failure == null
-        out.selected == newId("org", "foo", "1.0")
-    }
-
-    def "serializes failed dependency result"() {
-        ModuleComponentSelector requested = DefaultModuleComponentSelector.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<ModuleComponentSelector, ModuleVersionResolveException> map = new HashMap<>()
-        map.put(requested, failure)
-        def out = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray())), map)
-
-        then:
-        out.requested == DefaultModuleComponentSelector.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/ModuleVersionSelectionSerializerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy
deleted file mode 100644
index 499ecb8..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy
+++ /dev/null
@@ -1,40 +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.resolveengine.result
-
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-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 componentIdentifier = new DefaultModuleComponentIdentifier('group', 'module', 'version')
-        def selection = new DefaultModuleVersionSelection(newId("org", "foo", "2.0"), VersionSelectionReasons.REQUESTED, componentIdentifier)
-
-        when:
-        def result = serialize(selection, serializer)
-
-        then:
-        result.selectionReason == VersionSelectionReasons.REQUESTED
-        result.selectedId == newId("org", "foo", "2.0")
-        result.componentId == componentIdentifier
-    }
-}
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
deleted file mode 100644
index 1efdecc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
+++ /dev/null
@@ -1,139 +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.resolveengine.result
-
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-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"), new DefaultModuleComponentIdentifier("org", "root", "1.0"))
-
-        when:
-        def result = builder.complete()
-
-        then:
-        with(result) {
-            root.id == DefaultModuleComponentIdentifier.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"), new DefaultModuleComponentIdentifier("org", "root", "1.0"))
-
-        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", CONFLICT_RESOLUTION))
-        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
-                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), CONFLICT_RESOLUTION, null),
-                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), 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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), REQUESTED, null),
-        ])
-        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
-                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep2", "2.0"), newId("org", "dep2", "2.0"), 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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), null, REQUESTED, new ModuleVersionResolveException(newSelector("org", "dep1", "1.0"), new RuntimeException())),
-            new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep2", "2.0"), newId("org", "dep2", "2.0"), REQUESTED, null),
-        ])
-        builder.resolvedConfiguration(newId("org", "dep2", "2.0"), [
-            new DefaultInternalDependencyResult(DefaultModuleComponentSelector.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, ComponentSelectionReason reason) {
-        new DefaultModuleVersionSelection(newId(org, name, ver), reason, new DefaultModuleComponentIdentifier(org, name, ver))
-    }
-}
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
deleted file mode 100644
index 164491e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
+++ /dev/null
@@ -1,41 +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.resolveengine.result
-
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.*
-
-class VersionSelectionReasonsTest extends Specification {
-
-    def "decorates with conflict resolution"() {
-        expect:
-        withConflictResolution(REQUESTED) == CONFLICT_RESOLUTION
-        withConflictResolution(SELECTED_BY_RULE) == CONFLICT_RESOLUTION_BY_RULE
-        withConflictResolution(CONFLICT_RESOLUTION) == CONFLICT_RESOLUTION
-        withConflictResolution(CONFLICT_RESOLUTION_BY_RULE) == CONFLICT_RESOLUTION_BY_RULE
-    }
-
-    def "does not decorate unsupported reasons"() {
-        when:
-        withConflictResolution(FORCED)
-
-        then:
-        def ex = thrown(IllegalArgumentException)
-        ex.message.contains FORCED.toString()
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaDataTest.groovy
deleted file mode 100644
index d181764..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultDependencyMetaDataTest.groovy
+++ /dev/null
@@ -1,162 +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.metadata
-
-import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor
-import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.artifacts.component.ComponentSelector
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import org.gradle.api.artifacts.component.ProjectComponentSelector
-import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
-import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-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 DefaultDependencyMetaDataTest extends Specification {
-    final requestedModuleId = IvyUtil.createModuleRevisionId("org", "module", "1.2+")
-
-    def "constructs selector from descriptor"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        expect:
-        metaData.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.2+")
-    }
-
-    def "creates a copy with new requested version"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        given:
-
-        when:
-        def copy = metaData.withRequestedVersion("1.3+")
-
-        then:
-        copy.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.3+")
-        copy.descriptor.dependencyRevisionId == IvyUtil.createModuleRevisionId("org", "module", "1.3+")
-    }
-
-    def "returns this if new requested version is the same as current requested version"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        expect:
-        metaData.withRequestedVersion("1.2+").is(metaData)
-        metaData.withRequestedVersion(DefaultModuleVersionSelector.newSelector("org", "module", "1.2+")).is(metaData)
-    }
-
-    def "can set changing flag"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        when:
-        def copy = metaData.withChanging()
-
-        then:
-        copy.descriptor.dependencyRevisionId == requestedModuleId
-        copy.descriptor.changing
-    }
-
-    def "returns this when changing is already true"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, true)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        expect:
-        metaData.withChanging().is(metaData)
-    }
-
-    def "returns empty set of artifacts when dependency descriptor does not declare any artifacts"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-        def fromConfiguration = Stub(ConfigurationMetaData)
-        def toConfiguration = Stub(ConfigurationMetaData)
-
-        expect:
-        metaData.getArtifacts(fromConfiguration, toConfiguration).empty
-    }
-
-    def "returns empty set of artifacts when dependency descriptor does not declare any artifacts for source configuration"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-        def fromConfiguration = Stub(ConfigurationMetaData)
-        def toConfiguration = Stub(ConfigurationMetaData)
-
-        given:
-        descriptor.addDependencyArtifact("other", new DefaultDependencyArtifactDescriptor(descriptor, "art", "type", "ext", null, [:]))
-
-        expect:
-        metaData.getArtifacts(fromConfiguration, toConfiguration).empty
-    }
-
-    def "uses artifacts defined by dependency descriptor"() {
-        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-        def fromConfiguration = Stub(ConfigurationMetaData)
-        def targetComponent = Stub(ComponentMetaData)
-        def toConfiguration = Stub(ConfigurationMetaData)
-        def artifact1 = Stub(ComponentArtifactMetaData)
-        def artifact2 = Stub(ComponentArtifactMetaData)
-
-        given:
-        fromConfiguration.hierarchy >> (['config', 'super'] as LinkedHashSet)
-        toConfiguration.component >> targetComponent
-        descriptor.addDependencyArtifact("config", new DefaultDependencyArtifactDescriptor(descriptor, "art1", "type", "ext", null, [:]))
-        descriptor.addDependencyArtifact("other", new DefaultDependencyArtifactDescriptor(descriptor, "art2", "type", "ext", null, [:]))
-        descriptor.addDependencyArtifact("super", new DefaultDependencyArtifactDescriptor(descriptor, "art3", "type", "ext", null, [:]))
-        targetComponent.artifact({it.name == 'art1'}) >> artifact1
-        targetComponent.artifact({it.name == 'art3'}) >> artifact2
-
-        expect:
-        metaData.getArtifacts(fromConfiguration, toConfiguration) == [artifact1, artifact2] as Set
-    }
-
-    def "returns a build component selector if descriptor indicates a project dependency"() {
-        given:
-        def project = Mock(ProjectInternal)
-        def projectDependency = new DefaultProjectDependency(project, 'conf1', {} as ProjectAccessListener, true)
-        def descriptor = new ProjectDependencyDescriptor(projectDependency, DefaultModuleDescriptor.newDefaultInstance(requestedModuleId), requestedModuleId, false, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        when:
-        ComponentSelector componentSelector = metaData.getSelector()
-
-        then:
-        1 * project.path >> ':myPath'
-        componentSelector instanceof ProjectComponentSelector
-        componentSelector.projectPath == ':myPath'
-    }
-
-    def "returns a module component selector if descriptor indicates a default dependency"() {
-        given:
-        def descriptor = new DefaultDependencyDescriptor(DefaultModuleDescriptor.newDefaultInstance(requestedModuleId), requestedModuleId, false, false, false)
-        def metaData = new DefaultDependencyMetaData(descriptor)
-
-        when:
-        ComponentSelector componentSelector = metaData.getSelector()
-
-        then:
-        componentSelector instanceof ModuleComponentSelector
-        componentSelector.group == 'org'
-        componentSelector.module == 'module'
-        componentSelector.version == '1.2+'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactNameTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactNameTest.groovy
deleted file mode 100644
index c54be68..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultIvyArtifactNameTest.groovy
+++ /dev/null
@@ -1,53 +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.metadata
-
-import org.gradle.util.Matchers
-import spock.lang.Specification
-
-class DefaultIvyArtifactNameTest extends Specification {
-    def "has useful string representation"() {
-        expect:
-        def name = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
-        name.toString() == "name.ext"
-    }
-
-    def "is equal when all fields are equal"() {
-        def name = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
-        def same = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
-        def differentName = new DefaultIvyArtifactName("other", "type", "ext", [attr1: "attr"])
-        def differentType = new DefaultIvyArtifactName("name", "other", "ext", [attr1: "attr"])
-        def differentExt = new DefaultIvyArtifactName("name", "type", "other", [attr1: "attr"])
-        def differentAttr = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "other"])
-        def differentAttrs = new DefaultIvyArtifactName("name", "type", "ext", [other: null])
-
-        expect:
-        name Matchers.strictlyEqual(same)
-        name != differentName
-        name != differentType
-        name != differentExt
-        name != differentAttr
-        name != differentAttrs
-    }
-
-    def "uses extended attributes to determine classifier"() {
-        def name = new DefaultIvyArtifactName("name", "type", "ext", ['classifier': 'classifier'])
-
-        expect:
-        name.classifier == 'classifier'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifierTest.groovy
deleted file mode 100644
index eacb21d..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalArtifactIdentifierTest.groovy
+++ /dev/null
@@ -1,67 +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.metadata
-
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.util.Matchers
-import spock.lang.Specification
-
-class DefaultLocalArtifactIdentifierTest extends Specification {
-    def "has useful string representation"() {
-        def componentId = Stub(ComponentIdentifier)
-
-        expect:
-        def noClassifier = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", "ext", [:])
-        noClassifier.displayName == "<comp>:name.ext"
-        noClassifier.toString() == "<comp>:name.ext"
-
-        def withClassifier = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", "ext", ['classifier': 'classifier'])
-        withClassifier.displayName == "<comp>:name-classifier.ext"
-        withClassifier.toString() == "<comp>:name-classifier.ext"
-
-        def noExtension = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", null, ['classifier': 'classifier'])
-        noExtension.displayName == "<comp>:name-classifier"
-        noExtension.toString() == "<comp>:name-classifier"
-    }
-
-    def "is equal when all attributes and module version are the same"() {
-        def moduleVersion = DefaultModuleVersionIdentifier.newId("group", "module", "version")
-        def componentId = DefaultModuleComponentIdentifier.newId(moduleVersion)
-
-        def withClassifier = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': 'classifier'])
-        def same = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': 'classifier'])
-        def differentName = new DefaultLocalArtifactIdentifier(componentId, "comp", "2", "type", "ext", ['classifier': 'classifier'])
-        def differentType = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "2", "ext", ['classifier': 'classifier'])
-        def differentExt = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "2", ['classifier': 'classifier'])
-        def differentAttributes = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': '2'])
-        def emptyParts = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", null, [:])
-        def emptyPartsSame = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", null, [:])
-
-        expect:
-        withClassifier Matchers.strictlyEqual(same)
-        withClassifier != differentName
-        withClassifier != differentType
-        withClassifier != differentExt
-        withClassifier != differentAttributes
-        withClassifier != emptyParts
-
-        emptyParts Matchers.strictlyEqual(emptyPartsSame)
-        emptyParts != withClassifier
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaDataTest.groovy
deleted file mode 100644
index 31e6563..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultLocalComponentMetaDataTest.groovy
+++ /dev/null
@@ -1,146 +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.metadata
-
-import org.apache.ivy.core.module.descriptor.Configuration
-import org.apache.ivy.core.module.descriptor.DefaultArtifact
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import spock.lang.Specification
-
-class DefaultLocalComponentMetaDataTest extends Specification {
-    def moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(IvyUtil.createModuleRevisionId("group", "module", "version"))
-    def componentIdentifier = Mock(ComponentIdentifier)
-    def metaData = new DefaultLocalComponentMetaData(moduleDescriptor, componentIdentifier)
-
-    def "can add artifacts"() {
-        def artifact = artifact()
-        def file = new File("artifact.zip")
-
-        given:
-        moduleDescriptor.addConfiguration(new Configuration("conf"))
-
-        when:
-        metaData.addArtifact("conf", artifact, file)
-
-        then:
-        metaData.artifacts.size() == 1
-        def publishArtifact = (metaData.artifacts as List).first()
-        publishArtifact.id
-        publishArtifact.file == file
-
-        and:
-        metaData.getArtifact(publishArtifact.id) == publishArtifact
-
-        and:
-        def resolveMetaData = metaData.toResolveMetaData()
-        resolveMetaData.artifacts.size() == 1
-        def resolveArtifact = (resolveMetaData.artifacts as List).first()
-        resolveArtifact.componentId == resolveMetaData.componentId
-
-        and:
-        moduleDescriptor.getArtifacts("conf") == [artifact]
-    }
-
-    def "can lookup an artifact given an Ivy artifact"() {
-        def artifact = artifact()
-        def file = new File("artifact.zip")
-
-        given:
-        moduleDescriptor.addConfiguration(new Configuration("conf"))
-
-        and:
-        metaData.addArtifact("conf", artifact, file)
-
-        expect:
-        def resolveArtifact = metaData.toResolveMetaData().artifact(artifact)
-        resolveArtifact.file == file
-        resolveArtifact == metaData.getArtifact(resolveArtifact.id)
-    }
-
-    def "can lookup an unknown artifact given an Ivy artifact"() {
-        def artifact = artifact()
-
-        expect:
-        def resolveArtifact = metaData.toResolveMetaData().artifact(artifact)
-        resolveArtifact != null
-        resolveArtifact.file == null
-        metaData.getArtifact(resolveArtifact.id) == null
-    }
-
-    def "handles artifacts with duplicate attributes and different files"() {
-        def artifact1 = artifact()
-        def artifact2 = artifact()
-        def file1 = new File("artifact-1.zip")
-        def file2 = new File("artifact-2.zip")
-
-        given:
-        moduleDescriptor.addConfiguration(new Configuration("conf1"))
-        moduleDescriptor.addConfiguration(new Configuration("conf2"))
-        metaData.addArtifact("conf1", artifact1, file1)
-        metaData.addArtifact("conf2", artifact2, file2)
-
-        when:
-        def resolveMetaData = metaData.toResolveMetaData()
-
-        then:
-        def conf1Artifacts = resolveMetaData.getConfiguration("conf1").artifacts as List
-        conf1Artifacts.size() == 1
-        def artifactMetadata1 = conf1Artifacts[0]
-
-        def conf2Artifacts = resolveMetaData.getConfiguration("conf2").artifacts as List
-        conf2Artifacts.size() == 1
-        def artifactMetadata2 = conf2Artifacts[0]
-
-        and:
-        artifactMetadata1.id != artifactMetadata2.id
-
-        and:
-        resolveMetaData.artifacts == [artifactMetadata1, artifactMetadata2] as Set
-
-        and:
-        metaData.getArtifact(artifactMetadata1.id).file == file1
-        metaData.getArtifact(artifactMetadata2.id).file == file2
-    }
-
-    def "can convert to publish meta-data"() {
-        def artifact = artifact()
-        def file = new File("artifact.zip")
-
-        given:
-        moduleDescriptor.addConfiguration(new Configuration("conf"))
-        metaData.addArtifact("conf", artifact, file)
-
-        when:
-        def publishMetaData = metaData.toPublishMetaData()
-
-        then:
-        publishMetaData.id == metaData.id
-
-        and:
-        publishMetaData.artifacts.size() == 1
-        def artifacts = publishMetaData.artifacts as List
-        def publishArtifact = artifacts[0]
-        publishArtifact.artifact == artifact
-        publishArtifact.file == file
-    }
-
-    def artifact() {
-        return new DefaultArtifact(moduleDescriptor.getModuleRevisionId(), null, "artifact", "type", "ext")
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifierTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifierTest.groovy
deleted file mode 100644
index 695885b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactIdentifierTest.groovy
+++ /dev/null
@@ -1,67 +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.metadata
-
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.util.Matchers
-import spock.lang.Specification
-
-class DefaultModuleVersionArtifactIdentifierTest extends Specification {
-    def "has useful string representation"() {
-        def componentId = DefaultModuleComponentIdentifier.newId("group", "module", "version")
-
-        expect:
-        def noClassifier = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", "ext", [:])
-        noClassifier.displayName == "group:module:version:name.ext"
-        noClassifier.toString() == "group:module:version:name.ext"
-
-        def withClassifier = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': 'classifier'])
-        withClassifier.displayName == "group:module:version:name-classifier.ext"
-        withClassifier.toString() == "group:module:version:name-classifier.ext"
-
-        def noExtension = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", null, ['classifier': 'classifier'])
-        noExtension.displayName == "group:module:version:name-classifier"
-        noExtension.toString() == "group:module:version:name-classifier"
-    }
-
-    def "is equal when all attributes and module version are the same"() {
-        def componentId = DefaultModuleComponentIdentifier.newId("group", "module", "version")
-        def otherComponentId = DefaultModuleComponentIdentifier.newId("group", "module", "2")
-
-        def withClassifier = new DefaultModuleVersionArtifactIdentifier(componentId,  "name", "type", "ext", ['classifier': 'classifier'])
-        def same = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': 'classifier'])
-        def differentModule = new DefaultModuleVersionArtifactIdentifier(otherComponentId, "name", "type", "ext", ['classifier': 'classifier'])
-        def differentName = new DefaultModuleVersionArtifactIdentifier(componentId, "2", "type", "ext", ['classifier': 'classifier'])
-        def differentType = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "2", "ext", ['classifier': 'classifier'])
-        def differentExt = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", "2", ['classifier': 'classifier'])
-        def differentAttributes = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': '2'])
-        def emptyParts = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", null, [:])
-        def emptyPartsSame = new DefaultModuleVersionArtifactIdentifier(componentId, "name", "type", null, [:])
-
-        expect:
-        withClassifier Matchers.strictlyEqual(same)
-        withClassifier != differentModule
-        withClassifier != differentName
-        withClassifier != differentType
-        withClassifier != differentExt
-        withClassifier != differentAttributes
-        withClassifier != emptyParts
-
-        emptyParts Matchers.strictlyEqual(emptyPartsSame)
-        emptyParts != withClassifier
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaDataTest.groovy
deleted file mode 100644
index 020124e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionArtifactMetaDataTest.groovy
+++ /dev/null
@@ -1,71 +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.metadata
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import spock.lang.Specification
-
-class DefaultModuleVersionArtifactMetaDataTest extends Specification {
-    def "has reasonable string representation"() {
-        expect:
-        def artifact = new DefaultModuleVersionArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", ['classifier': 'classifier']))
-        artifact.toString() == artifact.id.toString()
-    }
-
-    def "extracts attributes from provided artifact instance"() {
-        expect:
-        def artifact = new DefaultModuleVersionArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", ['classifier': 'classifier']))
-        artifact.name.name == "name"
-        artifact.name.type == "type"
-        artifact.name.extension == "ext"
-        artifact.name.classifier == "classifier"
-
-        and:
-        def noClassifier = new DefaultModuleVersionArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", [:]))
-        noClassifier.name.name == "name"
-        noClassifier.name.type == "type"
-        noClassifier.name.extension == "ext"
-        noClassifier.name.classifier == null
-
-        and:
-        def noExtension = new DefaultModuleVersionArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", null, [:]))
-        noExtension.name.name == "name"
-        noExtension.name.type == "type"
-        noExtension.name.extension == null
-        noExtension.name.classifier == null
-    }
-
-    def "converts to Ivy artifact"() {
-        expect:
-        def original = ivyArtifact("name", "type", "ext", ['classifier': 'classifier'])
-        def artifact = new DefaultModuleVersionArtifactMetaData(Stub(ModuleComponentIdentifier), original)
-        def ivyArtifact = artifact.toIvyArtifact()
-        ivyArtifact.name == "name"
-        ivyArtifact.type == "type"
-        ivyArtifact.ext == "ext"
-        ivyArtifact.extraAttributes == [classifier: "classifier"]
-    }
-
-    def ivyArtifact(String name, String type, String extension, Map attributes) {
-        def artifact = Mock(Artifact)
-        _ * artifact.name >> name
-        _ * artifact.type >> type
-        _ * artifact.ext >> extension
-        _ * artifact.extraAttributes >> attributes
-        return artifact
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaDataTest.groovy
deleted file mode 100644
index 17a2e9e..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/DefaultModuleVersionPublishMetaDataTest.groovy
+++ /dev/null
@@ -1,42 +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.metadata
-
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import spock.lang.Specification
-
-class DefaultModuleVersionPublishMetaDataTest extends Specification {
-    def metaData = new DefaultModuleVersionPublishMetaData(Stub(ModuleVersionIdentifier))
-
-    def "can add artifacts"() {
-        def artifact = Stub(Artifact)
-        def file = new File("artifact.zip")
-
-        when:
-        metaData.addArtifact(artifact, file)
-
-        then:
-        metaData.artifacts.size() == 1
-        def publishArtifact = metaData.artifacts.iterator().next()
-        publishArtifact.artifact == artifact
-        publishArtifact.file == file
-
-        and:
-        metaData.getArtifact(publishArtifact.id) == publishArtifact
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapterTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapterTest.groovy
deleted file mode 100644
index de16c66..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/metadata/ModuleDescriptorAdapterTest.groovy
+++ /dev/null
@@ -1,296 +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.metadata
-
-import org.apache.ivy.core.module.descriptor.*
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import spock.lang.Specification
-
-class ModuleDescriptorAdapterTest extends Specification {
-    def id = Stub(ModuleVersionIdentifier)
-    def moduleDescriptor = Mock(ModuleDescriptor)
-    def metaData = new ModuleDescriptorAdapter(id, moduleDescriptor)
-
-    def "has useful string representation"() {
-        given:
-        def config = Stub(Configuration)
-        moduleDescriptor.getConfiguration('config') >> config
-        id.toString() >> 'group:module:version'
-
-        expect:
-        metaData.toString() == 'group:module:version'
-        metaData.getConfiguration('config').toString() == 'group:module:version:config'
-    }
-
-    def "builds and caches the dependency meta-data from the module descriptor"() {
-        def dependency1 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        def dependency2 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-
-        given:
-        moduleDescriptor.dependencies >> ([dependency1, dependency2] as DependencyDescriptor[])
-
-        when:
-        def deps = metaData.dependencies
-
-        then:
-        deps.size() == 2
-        deps[0].descriptor == dependency1
-        deps[1].descriptor == dependency2
-
-        when:
-        def deps2 = metaData.dependencies
-
-        then:
-        deps2.is(deps)
-
-        and:
-        0 * moduleDescriptor._
-    }
-
-    def "builds and caches the configuration meta-data from the module descriptor"() {
-        when:
-        def config = metaData.getConfiguration("conf")
-
-        then:
-        1 * moduleDescriptor.getConfiguration("conf") >> Stub(Configuration)
-
-        when:
-        def config2 = metaData.getConfiguration("conf")
-
-        then:
-        config2.is(config)
-
-        and:
-        0 * moduleDescriptor._
-    }
-
-    def "returns null for unknown configuration"() {
-        given:
-        moduleDescriptor.getConfiguration("conf") >> null
-
-        expect:
-        metaData.getConfiguration("conf") == null
-    }
-
-    def "builds and caches dependencies for a configuration"() {
-        def config = Stub(Configuration)
-        def parent = Stub(Configuration)
-        def dependency1 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        dependency1.addDependencyConfiguration("conf", "a")
-        def dependency2 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        dependency2.addDependencyConfiguration("*", "b")
-        def dependency3 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        dependency3.addDependencyConfiguration("super", "c")
-        def dependency4 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        dependency4.addDependencyConfiguration("other", "d")
-        def dependency5 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
-        dependency5.addDependencyConfiguration("%", "e")
-
-        given:
-        moduleDescriptor.dependencies >> ([dependency1, dependency2, dependency3, dependency4, dependency5] as DependencyDescriptor[])
-        moduleDescriptor.getConfiguration("conf") >> config
-        moduleDescriptor.getConfiguration("super") >> parent
-        config.extends >> ["super"]
-
-        when:
-        def dependencies = metaData.getConfiguration("conf").dependencies
-
-        then:
-        dependencies*.descriptor == [dependency1, dependency2, dependency3, dependency5]
-
-        and:
-        metaData.getConfiguration("conf").dependencies.is(dependencies)
-
-        when:
-        metaData.setDependencies([])
-
-        then:
-        metaData.getConfiguration("conf").dependencies == []
-    }
-
-    def "builds and caches artifacts from the module descriptor"() {
-        def artifact1 = artifact("one")
-        def artifact2 = artifact("two")
-
-        given:
-        moduleDescriptor.getAllArtifacts() >> ([artifact1, artifact2] as Artifact[])
-
-        when:
-        def artifacts = metaData.artifacts
-
-        then:
-        artifacts*.name.name == ["one", "two"]
-
-        and:
-        metaData.artifacts.is(artifacts)
-    }
-
-    Artifact artifact(String name) {
-        return Stub(Artifact) {
-            getName() >> name
-            getType() >> "type"
-            getExt() >> "ext"
-            getExtraAttributes() >> [classifier: "classifier"]
-        }
-    }
-
-    def "builds and caches artifacts for a configuration"() {
-        def artifact1 = artifact("one")
-        def artifact2 = artifact("two")
-        def config = Stub(Configuration)
-
-        given:
-        moduleDescriptor.getConfiguration("conf") >> config
-        moduleDescriptor.allArtifacts >> ([artifact1, artifact2] as Artifact[])
-        moduleDescriptor.getArtifacts("conf") >> ([artifact1, artifact2] as Artifact[])
-
-        when:
-        def artifacts = metaData.getConfiguration("conf").artifacts
-
-        then:
-        artifacts*.name.name == ["one", "two"]
-        metaData.artifacts*.name.name == ["one", "two"]
-
-        and:
-        metaData.getConfiguration("conf").artifacts.is(artifacts)
-    }
-
-    def "can adapt an Ivy artifact to a Gradle artifact"() {
-        def artifact = artifact("one")
-
-        expect:
-        def artifactMetaData = metaData.artifact(artifact)
-        artifactMetaData.componentId == metaData.componentId
-        artifactMetaData.id.componentIdentifier == metaData.componentId
-        artifactMetaData.name.name == "one"
-        artifactMetaData.name.type == "type"
-        artifactMetaData.name.extension == "ext"
-        artifactMetaData.name.classifier == "classifier"
-    }
-
-    def "artifacts include union of those inherited from other configurations"() {
-        def config = Stub(Configuration)
-        def parent = Stub(Configuration)
-        def artifact1 = artifact("one")
-        def artifact2 = artifact("two")
-        def artifact3 = artifact("three")
-
-        given:
-        moduleDescriptor.getConfiguration("conf") >> config
-        moduleDescriptor.getConfiguration("super") >> parent
-        config.extends >> ["super"]
-        moduleDescriptor.allArtifacts >> ([artifact1, artifact2, artifact3] as Artifact[])
-        moduleDescriptor.getArtifacts("conf") >> ([artifact1, artifact2] as Artifact[])
-        moduleDescriptor.getArtifacts("super") >> ([artifact2, artifact3] as Artifact[])
-
-        when:
-        def artifacts = metaData.getConfiguration("conf").artifacts
-
-        then:
-        artifacts*.name.name == ["one", "two", "three"]
-    }
-
-    def "builds and caches exclude rules for a configuration"() {
-        def rule1 = Stub(ExcludeRule)
-        def rule2 = Stub(ExcludeRule)
-        def rule3 = Stub(ExcludeRule)
-        def config = Stub(Configuration)
-        def parent = Stub(Configuration)
-
-        given:
-        rule1.configurations >> ["conf"]
-        rule2.configurations >> ["super"]
-        rule3.configurations >> ["other"]
-
-        and:
-        moduleDescriptor.getConfiguration("conf") >> config
-        moduleDescriptor.getConfiguration("super") >> parent
-        config.extends >> ["super"]
-        moduleDescriptor.allExcludeRules >> ([rule1, rule2, rule3] as ExcludeRule[])
-
-        when:
-        def excludeRules = metaData.getConfiguration("conf").excludeRules
-
-        then:
-        excludeRules as List == [rule1, rule2]
-
-        and:
-        metaData.getConfiguration("conf").excludeRules.is(excludeRules)
-    }
-
-    def "can replace the dependencies for the module version"() {
-        def dependency1 = Stub(DependencyMetaData)
-        def dependency2 = Stub(DependencyMetaData)
-
-        when:
-        metaData.dependencies = [dependency1, dependency2]
-
-        then:
-        metaData.dependencies == [dependency1, dependency2]
-
-        and:
-        0 * moduleDescriptor._
-    }
-
-    def "can make a copy"() {
-        def dependency1 = Stub(DependencyMetaData)
-        def dependency2 = Stub(DependencyMetaData)
-
-        given:
-        metaData.changing = true
-        metaData.metaDataOnly = true
-        metaData.dependencies = [dependency1, dependency2]
-        metaData.status = 'a'
-        metaData.statusScheme = ['a', 'b', 'c']
-
-        when:
-        def copy = metaData.copy()
-
-        then:
-        copy != metaData
-        copy.descriptor == moduleDescriptor
-        copy.changing
-        copy.metaDataOnly
-        copy.dependencies == [dependency1, dependency2]
-        copy.status == 'a'
-        copy.statusScheme == ['a', 'b', 'c']
-    }
-
-    def "creates ModuleComponentIdentifier as component ID if not provided in constructor"() {
-        when:
-        ModuleVersionIdentifier moduleVersionIdentifier = new DefaultModuleVersionIdentifier('group', 'name', 'version')
-        def metaData = new ModuleDescriptorAdapter(moduleVersionIdentifier, moduleDescriptor)
-
-        then:
-        metaData.componentId == new DefaultModuleComponentIdentifier('group', 'name', 'version')
-    }
-
-    def "uses component ID if provided in constructor"() {
-        when:
-        def moduleVersionIdentifier = new DefaultModuleVersionIdentifier('group', 'name', 'version')
-        def componentIdentifier = new DefaultModuleComponentIdentifier('group', 'override', '1.2')
-        def metaData = new ModuleDescriptorAdapter(moduleVersionIdentifier, moduleDescriptor, componentIdentifier)
-
-        then:
-        metaData.componentId == componentIdentifier
-    }
-}
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
deleted file mode 100644
index 32615ae..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.mvnsettings
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class DefaultLocalMavenRepositoryLocatorTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    SimpleMavenFileLocations locations
-    DefaultLocalMavenRepositoryLocator locator
-
-    Map systemProperties = ["sys.prop": "sys/prop/value"]
-    Map environmentVariables = [ENV_VAR: "env/var/value"]
-
-    File repo1 = tmpDir.file("repo1")
-    File repo2 = tmpDir.file("repo2")
-
-    def setup() {
-        locations = new SimpleMavenFileLocations()
-        locator = new DefaultLocalMavenRepositoryLocator(new DefaultMavenSettingsProvider(locations), systemProperties, environmentVariables)
-    }
-
-    def "returns default location if no settings file exists"() {
-        expect:
-        // this default comes from DefaultMavenSettingsBuilder which uses System.getProperty() directly
-        locator.localMavenRepository == new File("${System.getProperty("user.home")}/.m2/repository")
-    }
-
-    def "throws exception on broken global settings file with decent error message"() {
-        given:
-        def settingsFile = locations.globalSettingsFile
-        settingsFile << "broken content"
-        when:
-        locator.localMavenRepository
-        then:
-        def ex = thrown(CannotLocateLocalMavenRepositoryException);
-        ex.message == "Unable to parse local Maven settings."
-        ex.cause.message.contains(settingsFile.absolutePath)
-    }
-
-    def "throws exception on broken user settings file with decent error message"() {
-        given:
-        def settingsFile = locations.userSettingsFile
-        settingsFile << "broken content"
-        when:
-        locator.localMavenRepository
-        then:
-        def ex = thrown(CannotLocateLocalMavenRepositoryException)
-        ex.message == "Unable to parse local Maven settings."
-        ex.cause.message.contains(settingsFile.absolutePath)
-    }
-
-    def "honors location specified in user settings file"() {
-        writeSettingsFile(locations.userSettingsFile, repo1)
-
-        expect:
-        locator.localMavenRepository == repo1
-    }
-
-    def "honors location specified in global settings file"() {
-        writeSettingsFile(locations.globalSettingsFile, repo1)
-
-        expect:
-        locator.localMavenRepository == repo1
-    }
-
-    def "prefers location specified in user settings file over that in global settings file"() {
-        writeSettingsFile(locations.userSettingsFile, repo1)
-        writeSettingsFile(locations.globalSettingsFile, repo2)
-
-        expect:
-        locator.localMavenRepository == repo1
-    }
-
-    def "handles the case where (potential) location of global settings file cannot be determined"() {
-        locations.globalSettingsFile = null
-
-        expect:
-        locator.localMavenRepository == new File("${System.getProperty("user.home")}/.m2/repository")
-
-        when:
-        writeSettingsFile(locations.userSettingsFile, repo1)
-
-        then:
-        locator.localMavenRepository == repo1
-    }
-
-    def "replaces placeholders for system properties and environment variables"() {
-        writeSettingsFile(locations.userSettingsFile, tmpDir.file('${sys.prop}/${env.ENV_VAR}'))
-
-        expect:
-        locator.localMavenRepository == tmpDir.file("sys/prop/value/env/var/value")
-    }
-
-    @Unroll
-    def "unresolvable placeholder for #propType throws exception with decent error message"() {
-        TestFile repoPath = tmpDir.file("\${$prop}")
-        writeSettingsFile(locations.userSettingsFile, repoPath)
-        when:
-        locator.localMavenRepository
-        then:
-        def ex = thrown(CannotLocateLocalMavenRepositoryException);
-        ex.message == "Cannot resolve placeholder '${prop}' in value '${repoPath.absolutePath}'"
-        where:
-        prop                  |   propType
-        'sys.unknown.prop'    |   "system property"
-        'env.unknown.ENV_VAR' |   "environment variable"
-    }
-
-
-
-    private void writeSettingsFile(File settings, File repo) {
-        writeSettingsFile(settings, repo.absolutePath)
-    }
-
-    private void writeSettingsFile(File settings, String repo) {
-        settings << """
-<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <localRepository>$repo</localRepository>
-</settings>"""
-    }
-
-    private class SimpleMavenFileLocations implements MavenFileLocations {
-        File userMavenDir
-        File globalMavenDir
-        File userSettingsFile = tmpDir.file("userSettingsFile")
-        File globalSettingsFile = tmpDir.file("globalSettingsFile")
-    }
-}
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
deleted file mode 100644
index f1e72dd..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
+++ /dev/null
@@ -1,162 +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.repositories
-
-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.dsl.DefaultRepositoryHandler
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-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 spock.lang.Specification
-
-class DefaultBaseRepositoryFactoryTest extends Specification {
-    static final URI RESOLVER_URL = new URI('http://a.b.c/')
-
-    final LocalMavenRepositoryLocator localMavenRepoLocator = Mock()
-    final FileResolver fileResolver = Mock()
-    final RepositoryTransportFactory transportFactory = Mock()
-    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final ProgressLoggerFactory progressLoggerFactory = Mock()
-    final LegacyDependencyResolverRepositoryFactory legacyDependencyResolverRepositoryFactory = Mock()
-    final ResolverStrategy resolverStrategy = Mock()
-
-    final DefaultBaseRepositoryFactory factory = new DefaultBaseRepositoryFactory(
-            localMavenRepoLocator, fileResolver, new DirectInstantiator(), transportFactory, locallyAvailableResourceFinder,
-            legacyDependencyResolverRepositoryFactory, resolverStrategy
-    )
-
-    def testCreateResolverWithStringDescription() {
-        when:
-        fileResolver.resolveUri('uri') >> RESOLVER_URL
-
-        then:
-        def repository = factory.createRepository('uri')
-
-        repository instanceof DefaultMavenArtifactRepository
-        repository.name == null
-        repository.url == RESOLVER_URL
-        repository.artifactUrls.isEmpty()
-    }
-
-    def testCreateResolverWithMapDescription() {
-        when:
-        fileResolver.resolveUri('uri') >> RESOLVER_URL
-
-        then:
-        def repository = factory.createRepository([name: 'name', url: 'uri'])
-
-        repository instanceof DefaultMavenArtifactRepository
-        repository.name == 'name'
-        repository.url == RESOLVER_URL
-        repository.artifactUrls.isEmpty()
-    }
-
-    def testCreateResolverWithResolverDescription() {
-        def repository = Mock(ArtifactRepository)
-        def resolver = Mock(DependencyResolver)
-
-        when:
-        legacyDependencyResolverRepositoryFactory.createRepository(resolver) >> repository
-
-        then:
-        factory.createRepository(resolver) == repository
-    }
-
-    def testCreateResolverWithArtifactRepositoryDescription() {
-        when:
-        ArtifactRepository repo = Mock(ArtifactRepository)
-
-        then:
-        factory.createRepository(repo) == repo
-    }
-
-    def testCreateResolverForUnknownDescription() {
-        when:
-        def someIllegalDescription = new NullPointerException()
-
-        factory.createRepository(someIllegalDescription)
-
-        then:
-        thrown InvalidUserDataException
-    }
-
-    def testCreateFlatDirResolver() {
-        expect:
-        def repo =factory.createFlatDirRepository()
-        repo instanceof DefaultFlatDirArtifactRepository
-    }
-
-    def testCreateLocalMavenRepo() {
-        given:
-        File repoDir = new File(".m2/repository")
-
-        when:
-        localMavenRepoLocator.getLocalMavenRepository() >> repoDir
-        fileResolver.resolveUri(repoDir) >> repoDir.toURI()
-
-        then:
-        def repo = factory.createMavenLocalRepository()
-        repo instanceof DefaultMavenLocalArtifactRepository
-        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
-    }
-
-    def testCreateMavenCentralRepo() {
-        given:
-        def centralUrl = new URI(RepositoryHandler.MAVEN_CENTRAL_URL)
-
-        when:
-        fileResolver.resolveUri(RepositoryHandler.MAVEN_CENTRAL_URL) >> centralUrl
-
-        then:
-        def repo = factory.createMavenCentralRepository()
-        repo instanceof DefaultMavenArtifactRepository
-        repo.url == centralUrl
-    }
-
-    def createIvyRepository() {
-        expect:
-        def repo = factory.createIvyRepository()
-        repo instanceof DefaultIvyArtifactRepository
-    }
-
-    def createMavenRepository() {
-        expect:
-        def repo = factory.createMavenRepository()
-        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
deleted file mode 100644
index d05e927..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
+++ /dev/null
@@ -1,82 +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
-
-import org.apache.ivy.core.module.id.ArtifactRevisionId
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-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 ExternalResourceRepository resourceRepository = Mock()
-    final RepositoryTransport repositoryTransport = Mock()
-    final RepositoryTransportFactory transportFactory = Mock()
-    final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder = Mock()
-    final ResolverStrategy resolverStrategy = Stub()
-
-    final DefaultFlatDirArtifactRepository repository = new DefaultFlatDirArtifactRepository(
-            fileResolver, transportFactory, locallyAvailableResourceFinder, resolverStrategy)
-
-    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.createResolver()
-
-        then:
-        1 * transportFactory.createFileTransport("repo-name") >> repositoryTransport
-
-        and:
-        repo instanceof IvyResolver
-        def expectedPatterns = [
-                "$dir1.absolutePath/[artifact]-[revision](-[classifier]).[ext]",
-                "$dir1.absolutePath/[artifact](-[classifier]).[ext]",
-                "$dir2.absolutePath/[artifact]-[revision](-[classifier]).[ext]",
-                "$dir2.absolutePath/[artifact](-[classifier]).[ext]"
-        ]
-        repo.ivyPatterns == []
-        repo.artifactPatterns == expectedPatterns
-        repo.allownomd
-    }
-
-    def "fails when no directories specified"() {
-        given:
-        _ * fileResolver.resolveFiles(_) >> new SimpleFileCollection()
-
-        when:
-        repository.createResolver()
-
-        then:
-        InvalidUserDataException e = thrown()
-        e.message == 'You must specify at least one directory for a flat directory repository.'
-    }
-}
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
deleted file mode 100644
index 76ac7ce..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
+++ /dev/null
@@ -1,320 +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
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-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.internal.reflect.DirectInstantiator
-import org.gradle.logging.ProgressLoggerFactory
-import spock.lang.Specification
-
-class DefaultIvyArtifactRepositoryTest extends Specification {
-    final FileResolver fileResolver = Mock()
-    final PasswordCredentials credentials = Mock()
-    final RepositoryTransportFactory transportFactory = Mock()
-    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final ExternalResourceRepository resourceRepository = Mock()
-    final ProgressLoggerFactory progressLoggerFactory = Mock()
-    final ResolverStrategy resolverStrategy = Stub()
-
-    final DefaultIvyArtifactRepository repository = new DefaultIvyArtifactRepository(
-            fileResolver, credentials, transportFactory, locallyAvailableResourceFinder,
-            new DirectInstantiator(), resolverStrategy
-    )
-
-    def "default values"() {
-        expect:
-        repository.url == null
-        !repository.resolve.dynamicMode
-    }
-
-    def "cannot create a resolver for url with unknown scheme"() {
-        repository.name = 'name'
-        repository.artifactPattern 'pattern1'
-
-        given:
-        fileResolver.resolveUri('pattern1') >> new URI('scheme:resource1')
-
-        when:
-        repository.createResolver()
-
-        then:
-        InvalidUserDataException e = thrown()
-        e.message == "You may only specify 'file', 'http' and 'https' urls for an Ivy repository."
-    }
-
-    def "cannot creates a resolver for mixed url scheme"() {
-        repository.name = 'name'
-        repository.artifactPattern 'pattern1'
-        repository.artifactPattern 'pattern2'
-
-        given:
-        fileResolver.resolveUri('pattern1') >> new URI('http:resource1')
-        fileResolver.resolveUri('pattern2') >> new URI('file:resource2')
-
-        when:
-        repository.createResolver()
-
-        then:
-        InvalidUserDataException e = thrown()
-        e.message == "You cannot mix file and http(s) urls for a single Ivy repository. Please declare 2 separate repositories."
-    }
-
-    def "creates a resolver for HTTP patterns"() {
-        repository.name = 'name'
-        repository.artifactPattern 'http://host/[organisation]/[artifact]-[revision].[ext]'
-        repository.artifactPattern 'http://other/[module]/[artifact]-[revision].[ext]'
-        repository.ivyPattern 'http://host/[module]/ivy-[revision].xml'
-
-        given:
-        fileResolver.resolveUri('http://host/') >> new URI('http://host/')
-        fileResolver.resolveUri('http://other/') >> new URI('http://other/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository == resourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[organisation]/[artifact]-[revision].[ext]', 'http://other/[module]/[artifact]-[revision].[ext]']
-            ivyPatterns == ['http://host/[module]/ivy-[revision].xml']
-        }
-    }
-
-    def "creates a resolver for file patterns"() {
-        repository.name = 'name'
-        repository.artifactPattern 'repo/[organisation]/[artifact]-[revision].[ext]'
-        repository.artifactPattern 'repo/[organisation]/[module]/[artifact]-[revision].[ext]'
-        repository.ivyPattern 'repo/[organisation]/[module]/ivy-[revision].xml'
-        def file = new File("test")
-        def fileUri = file.toURI()
-
-        given:
-        fileResolver.resolveUri('repo/') >> fileUri
-        transportFactory.createFileTransport('name') >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ["${fileUri}/[organisation]/[artifact]-[revision].[ext]", "${fileUri}/[organisation]/[module]/[artifact]-[revision].[ext]"]
-            ivyPatterns == ["${fileUri}/[organisation]/[module]/ivy-[revision].xml"]
-        }
-    }
-
-    def "creates a DSL wrapper for resolver"() {
-        repository.name = 'name'
-        repository.artifactPattern 'repo/[organisation]/[artifact]-[revision].[ext]'
-        def file = new File("test")
-        def fileUri = file.toURI()
-
-        given:
-        fileResolver.resolveUri('repo/') >> fileUri
-        transportFactory.createFileTransport('name') >> transport()
-
-        when:
-        def wrapper = repository.createLegacyDslObject()
-
-        then:
-        with(wrapper) {
-            it instanceof LegacyDependencyResolver
-            resolver instanceof IvyResolver
-        }
-
-        when:
-        def repo = wrapper.createResolver()
-
-        then:
-        repo.is(wrapper.resolver)
-    }
-
-    def "uses gradle patterns with specified url and default layout"() {
-        repository.name = 'name'
-        repository.url = 'http://host'
-
-        given:
-        fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])']
-            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml"]
-        }
-    }
-
-    def "uses maven patterns with specified url and maven layout"() {
-        repository.name = 'name'
-        repository.url = 'http://host'
-        repository.layout 'maven'
-
-        given:
-        fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])']
-            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml"]
-            m2compatible
-        }
-    }
-
-    def "uses specified base url with configured pattern layout"() {
-        repository.name = 'name'
-        repository.url = 'http://host'
-        repository.layout 'pattern', {
-            artifact '[module]/[revision]/[artifact](.[ext])'
-            ivy '[module]/[revision]/ivy.xml'
-        }
-
-        given:
-        fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[module]/[revision]/[artifact](.[ext])']
-            ivyPatterns == ["http://host/[module]/[revision]/ivy.xml"]
-            !resolver.m2compatible
-        }
-    }
-
-    def "when requested uses maven patterns with configured pattern layout"() {
-        repository.name = 'name'
-        repository.url = 'http://host'
-        repository.layout 'pattern', {
-            artifact '[module]/[revision]/[artifact](.[ext])'
-            ivy '[module]/[revision]/ivy.xml'
-            m2compatible = true
-        }
-
-        given:
-        fileResolver.resolveUri('http://host') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[module]/[revision]/[artifact](.[ext])']
-            ivyPatterns == ["http://host/[module]/[revision]/ivy.xml"]
-            m2compatible
-        }
-    }
-
-    def "combines layout patterns with additionally specified patterns"() {
-        repository.name = 'name'
-        repository.url = 'http://host/'
-        repository.artifactPattern 'http://host/[other]/artifact'
-        repository.ivyPattern 'http://host/[other]/ivy'
-
-        given:
-        fileResolver.resolveUri('http://host/') >> new URI('http://host/')
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        def resolver = repository.createResolver()
-
-        then:
-        with(resolver) {
-            it instanceof IvyResolver
-            repository instanceof ExternalResourceRepository
-            name == 'name'
-            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])', 'http://host/[other]/artifact']
-            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml", 'http://host/[other]/ivy']
-        }
-    }
-
-    def "uses artifact pattern for ivy files when no ivy pattern provided"() {
-        repository.name = 'name'
-        repository.url = 'http://host'
-        repository.layout 'pattern', {
-            artifact '[layoutPattern]'
-        }
-        repository.artifactPattern 'http://other/[additionalPattern]'
-        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.createResolver()
-
-        then:
-        resolver.artifactPatterns == ['http://host/[layoutPattern]', 'http://other/[additionalPattern]']
-        resolver.ivyPatterns == resolver.artifactPatterns
-    }
-
-    def "fails when no artifact patterns specified"() {
-        given:
-        transportFactory.createHttpTransport('name', credentials) >> transport()
-
-        when:
-        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 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
deleted file mode 100644
index 62d9fcb..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
+++ /dev/null
@@ -1,150 +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
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-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.local.LocallyAvailableResourceFinder
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
-import org.gradle.api.internal.file.FileResolver
-import spock.lang.Specification
-
-class DefaultMavenArtifactRepositoryTest extends Specification {
-    final FileResolver resolver = Mock()
-    final PasswordCredentials credentials = Mock()
-    final RepositoryTransportFactory transportFactory = Mock()
-    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final ExternalResourceRepository resourceRepository = Mock()
-    final ResolverStrategy resolverStrategy = Stub()
-
-    final DefaultMavenArtifactRepository repository = new DefaultMavenArtifactRepository(
-            resolver, credentials, transportFactory, locallyAvailableResourceFinder, resolverStrategy)
-
-    def "creates local repository"() {
-        given:
-        def file = new File('repo')
-        def uri = file.toURI()
-        _ * resolver.resolveUri('repo-dir') >> uri
-        transportFactory.createFileTransport('repo') >> transport()
-
-        and:
-        repository.name = 'repo'
-        repository.url = 'repo-dir'
-
-        when:
-        def repo = repository.createRealResolver()
-
-        then:
-        repo instanceof MavenResolver
-        repo.root == "${uri}/"
-    }
-
-    def "creates http repository"() {
-        given:
-        def uri = new URI("http://localhost:9090/repo")
-        _ * resolver.resolveUri('repo-dir') >> uri
-        _ * credentials.getUsername() >> 'username'
-        _ * credentials.getPassword() >> 'password'
-        transportFactory.createHttpTransport('repo', credentials) >> transport()
-
-        and:
-        repository.name = 'repo'
-        repository.url = 'repo-dir'
-
-        when:
-        def repo = repository.createRealResolver()
-
-        then:
-        repo instanceof MavenResolver
-        repo.root == "${uri}/"
-    }
-
-    def "creates a DSL wrapper for the repository"() {
-        given:
-        def file = new File('repo')
-        def uri = file.toURI()
-        _ * this.resolver.resolveUri('repo-dir') >> uri
-        transportFactory.createFileTransport('repo') >> transport()
-
-        and:
-        repository.name = 'repo'
-        repository.url = 'repo-dir'
-
-        when:
-        def resolver = repository.createLegacyDslObject()
-
-        then:
-        resolver instanceof LegacyMavenResolver
-        resolver.resolver instanceof MavenResolver
-
-        when:
-        def repo = resolver.createResolver()
-
-        then:
-        repo.is(resolver.resolver)
-    }
-
-    def "creates repository with additional artifact URLs"() {
-        given:
-        def uri = new URI("http://localhost:9090/repo")
-        def uri1 = new URI("http://localhost:9090/repo1")
-        def uri2 = new URI("http://localhost:9090/repo2")
-        _ * resolver.resolveUri('repo-dir') >> uri
-        _ * resolver.resolveUri('repo1') >> uri1
-        _ * resolver.resolveUri('repo2') >> uri2
-        transportFactory.createHttpTransport('repo', credentials) >> transport()
-
-        and:
-        repository.name = 'repo'
-        repository.url = 'repo-dir'
-        repository.artifactUrls('repo1', 'repo2')
-
-        when:
-        def repo = repository.createRealResolver()
-
-        then:
-        repo instanceof MavenResolver
-        repo.root == "${uri}/"
-        repo.artifactPatterns.size() == 3
-        repo.artifactPatterns.any { it.startsWith uri.toString() }
-        repo.artifactPatterns.any { it.startsWith uri1.toString() }
-        repo.artifactPatterns.any { it.startsWith uri2.toString() }
-    }
-
-    def "fails when no root url specified"() {
-        when:
-        repository.createLegacyDslObject()
-
-        then:
-        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/DefaultMavenLocalRepositoryTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalRepositoryTest.groovy
deleted file mode 100644
index 65e0f55..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalRepositoryTest.groovy
+++ /dev/null
@@ -1,69 +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.repositories
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-import org.gradle.api.internal.artifacts.repositories.resolver.MavenLocalResolver
-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.logging.ProgressLoggerFactory
-import spock.lang.Specification
-
-class DefaultMavenLocalRepositoryTest extends Specification {
-    final FileResolver resolver = Mock()
-    final PasswordCredentials credentials = Mock()
-    final RepositoryTransportFactory transportFactory = Mock()
-    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
-    final ExternalResourceRepository resourceRepository = Mock()
-    final ResolverStrategy resolverStrategy = Stub()
-
-    final DefaultMavenArtifactRepository repository = new DefaultMavenLocalArtifactRepository(
-            resolver, credentials, transportFactory, locallyAvailableResourceFinder, resolverStrategy)
-    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') >> transport()
-
-        and:
-        repository.name = 'repo'
-        repository.url = 'repo-dir'
-
-        when:
-        def repo = repository.createRealResolver()
-
-        then:
-        repo instanceof MavenLocalResolver
-        repo.root == "${uri}/"
-    }
-
-    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/legacy/DownloadingRepositoryCacheManagerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManagerTest.groovy
deleted file mode 100644
index 5bee2fa..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/legacy/DownloadingRepositoryCacheManagerTest.groovy
+++ /dev/null
@@ -1,67 +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.repositories.legacy
-import org.apache.ivy.core.module.descriptor.Artifact
-import org.apache.ivy.plugins.repository.Resource
-import org.apache.ivy.plugins.repository.ResourceDownloader
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-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<ModuleVersionArtifactMetaData> fileStore = Mock()
-    CacheLockingManager lockingManager = Mock()
-    TemporaryFileProvider tmpFileProvider = Mock()
-    ModuleVersionArtifactMetaData artifactMetaData = new DefaultModuleVersionArtifactMetaData(new DefaultModuleVersionArtifactIdentifier(DefaultModuleVersionIdentifier.newId("group", "module", "version"), "name", "type", "ext"))
-    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")
-
-        _ * fileStoreEntry.file >> storeFile;
-        _ * tmpFileProvider._ >> downloadFile
-
-        when:
-        downloadingRepositoryCacheManager.downloadAndCacheArtifactFile(artifactMetaData, artifact, resourceDownloader, resource)
-
-        then:
-        1 * lockingManager.useCache(_, _) >> {name, action ->
-            return action.create()
-        }
-        1 * resourceDownloader.download(artifact, resource, downloadFile)
-        1 * fileStore.move(artifactMetaData, 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
deleted file mode 100644
index 8be66c7..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
+++ /dev/null
@@ -1,125 +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.resolver
-
-import org.gradle.api.artifacts.ModuleIdentifier
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import org.gradle.api.internal.resource.ResourceException
-import org.gradle.api.internal.resource.ResourceNotFoundException
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class ChainedVersionListerTest extends Specification {
-
-    VersionLister lister1 = Mock()
-    VersionLister lister2 = Mock()
-
-    VersionList versionList1 = Mock()
-    VersionList versionList2 = Mock()
-
-    ResourcePattern pattern = Mock()
-    ModuleIdentifier module = Mock()
-    ModuleVersionArtifactMetaData artifact = Mock()
-
-    def chainedVersionLister = new ChainedVersionLister(lister1, lister2)
-
-    def "visit stops listing after first success"() {
-        when:
-        VersionList versionList = chainedVersionLister.getVersionList(module);
-
-        then:
-        1 * lister1.getVersionList(module) >> versionList1
-        1 * lister2.getVersionList(module) >> versionList2
-
-        when:
-        versionList.visit(pattern, artifact)
-
-        then:
-        1 * versionList1.visit(pattern, artifact)
-        0 * _._
-
-        when:
-        def result = versionList.versions
-
-        then:
-        result == ["1.0", "1.2"] as Set
-
-        and:
-        versionList1.versions >> ["1.0", "1.2"]
-        versionList2.versions >> []
-    }
-
-    @Unroll
-    def "visit ignores #exception.class.simpleName of failed VersionLister"() {
-        given:
-        lister1.getVersionList(module) >> versionList1
-        lister2.getVersionList(module) >> versionList2
-
-        VersionList versionList = chainedVersionLister.getVersionList(module)
-
-        when:
-        versionList.visit(pattern, artifact)
-
-        then:
-        1 * versionList1.visit(pattern, artifact) >> { throw exception }
-        1 * versionList2.visit(pattern, artifact)
-
-        where:
-        exception << [new ResourceNotFoundException("test resource not found exception"), new ResourceException("test resource exception"), new RuntimeException("broken")]
-    }
-
-    def "visit rethrows ResourceNotFoundException of failed last VersionLister"() {
-        given:
-        def exception = new ResourceNotFoundException("not found")
-        lister1.getVersionList(module) >> versionList1
-        lister2.getVersionList(module) >> versionList2
-
-        VersionList versionList = chainedVersionLister.getVersionList(module)
-
-        when:
-        versionList.visit(pattern, artifact)
-
-        then:
-        def e = thrown(ResourceNotFoundException)
-        e == exception
-
-        and:
-        1 * versionList1.visit(pattern, artifact) >> { throw new ResourceNotFoundException("ignore me") }
-        1 * versionList2.visit(pattern, artifact) >> { throw exception }
-    }
-
-    def "visit wraps failed last VersionLister"() {
-        given:
-        def exception = new RuntimeException("broken")
-        lister1.getVersionList(module) >> versionList1
-        lister2.getVersionList(module) >> versionList2
-
-        VersionList versionList = chainedVersionLister.getVersionList(module)
-
-        when:
-        versionList.visit(pattern, artifact)
-
-        then:
-        def e = thrown(ResourceException)
-        e.message == "Failed to list versions for ${module}."
-        e.cause == exception
-
-        and:
-        1 * versionList1.visit(pattern, artifact) >> { throw new ResourceNotFoundException("ignore me") }
-        1 * versionList2.visit(pattern, artifact) >> { throw exception }
-    }
-}
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
deleted file mode 100644
index 2345e28..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
+++ /dev/null
@@ -1,122 +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.resolver
-
-import org.gradle.api.artifacts.ArtifactIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.BuildableArtifactResolveResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ArtifactResolveException
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceFinder
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
-import spock.lang.Specification
-
-class ExternalResourceResolverTest extends Specification {
-    String name = "TestResolver"
-    ExternalResourceRepository repository = Mock()
-    VersionLister versionLister = Mock()
-    LocallyAvailableResourceFinder<ArtifactIdentifier> locallyAvailableResourceFinder = Mock()
-    BuildableArtifactResolveResult result = Mock()
-    MetaDataParser parser = Mock()
-    final ResolverStrategy resolverStrategy = Mock()
-    ModuleVersionArtifactIdentifier artifactIdentifier = Stub() {
-        getDisplayName() >> 'some-artifact'
-    }
-    ModuleVersionArtifactMetaData artifact = Stub() {
-        getId() >> artifactIdentifier
-    }
-    MavenResolver.TimestampedModuleSource moduleSource = Mock()
-    File downloadedFile = Mock(File)
-    ExternalResourceResolver resolver
-
-    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, parser, resolverStrategy])
-    }
-
-    def reportsNotFoundArtifactResolveResult() {
-        given:
-        artifactIsMissing()
-
-        when:
-        resolver.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * result.notFound(artifactIdentifier)
-        0 * result._
-    }
-
-    def reportsFailedArtifactResolveResult() {
-        given:
-        downloadIsFailing(new IOException("DOWNLOAD FAILURE"))
-
-        when:
-        resolver.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * result.failed(_) >> { ArtifactResolveException exception ->
-            assert exception.message == "Could not download artifact 'some-artifact'"
-            assert exception.cause.message == "DOWNLOAD FAILURE"
-        }
-        0 * result._
-    }
-
-    def reportsResolvedArtifactResolveResult() {
-        given:
-        artifactCanBeResolved()
-
-        when:
-        resolver.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * result.resolved(_)
-        0 * result._
-    }
-
-    def reportsResolvedArtifactResolveResultWithSnapshotVersion() {
-        given:
-        artifactIsTimestampedSnapshotVersion()
-        artifactCanBeResolved()
-
-        when:
-        resolver.resolveArtifact(artifact, moduleSource, result)
-
-        then:
-        1 * result.resolved(_)
-        0 * result._
-    }
-
-    def artifactIsTimestampedSnapshotVersion() {
-        _ * moduleSource.timestampedVersion >> "1.0-20100101.120001-1"
-    }
-
-    def artifactIsMissing() {
-        resolver.download(_, _) >> null
-    }
-
-    def downloadIsFailing(IOException failure) {
-        resolver.download(_, _) >> {
-            throw failure
-        }
-    }
-
-    def artifactCanBeResolved() {
-        resolver.download(_, _) >> downloadedFile
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy
deleted file mode 100644
index 7164801..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy
+++ /dev/null
@@ -1,50 +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.resolver
-
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import spock.lang.Specification
-
-class IvyResourcePatternTest extends Specification {
-    def "substitutes artifact attributes into pattern"() {
-        def pattern = new IvyResourcePattern("prefix/[organisation]-[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
-        def artifact1 = artifact("group", "projectA", "1.2")
-        def artifact2 = artifact("org.group", "projectA", "1.2")
-
-        expect:
-        pattern.toPath(artifact1) == 'prefix/group-projectA/1.2/ivys/1.2/ivy.xml'
-        pattern.toPath(artifact2) == 'prefix/org.group-projectA/1.2/ivys/1.2/ivy.xml'
-    }
-
-    def "substitutes artifact attributes without revision into pattern"() {
-        def pattern = new IvyResourcePattern("prefix/[organisation]-[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
-        def artifact1 = artifact("group", "projectA", "1.2")
-        def artifact2 = artifact("org.group", "projectA", "1.2")
-
-        expect:
-        pattern.toVersionListPattern(artifact1) == 'prefix/group-projectA/[revision]/ivys/[revision]/ivy.xml'
-        pattern.toVersionListPattern(artifact2) == 'prefix/org.group-projectA/[revision]/ivys/[revision]/ivy.xml'
-    }
-
-    private static ModuleVersionArtifactMetaData artifact(String group, String name, String version) {
-        final moduleVersionId = DefaultModuleVersionIdentifier.newId(group, name, version)
-        return new DefaultModuleVersionArtifactMetaData(new DefaultModuleVersionArtifactIdentifier(moduleVersionId, "ivy", "ivy", "xml"))
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy
deleted file mode 100644
index 216cd52..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy
+++ /dev/null
@@ -1,84 +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.resolver
-
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactMetaData
-import spock.lang.Specification
-
-class M2ResourcePatternTest extends Specification {
-    def "substitutes artifact attributes into pattern"() {
-        def pattern = new M2ResourcePattern("prefix/[organisation]/[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
-        final String group = "group"
-        final String name = "projectA"
-        final String version = "1.2"
-        def artifact1 = artifact(group, name, version)
-        def artifact2 = artifact("org.group", "projectA", "1.2")
-
-        expect:
-        pattern.toPath(artifact1) == 'prefix/group/projectA/1.2/ivys/1.2/ivy.xml'
-        pattern.toPath(artifact2) == 'prefix/org/group/projectA/1.2/ivys/1.2/ivy.xml'
-    }
-
-    def "substitutes module attributes into pattern to determine module pattern"() {
-        def pattern = new M2ResourcePattern("prefix/[organisation]/[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
-        def artifact1 = artifact("group", "projectA", "1.2")
-        def artifact2 = artifact("org.group", "projectA", "1.2")
-
-        expect:
-        pattern.toVersionListPattern(artifact1) == 'prefix/group/projectA/[revision]/ivys/[revision]/ivy.xml'
-        pattern.toVersionListPattern(artifact2) == 'prefix/org/group/projectA/[revision]/ivys/[revision]/ivy.xml'
-    }
-
-    def "can build module path"() {
-        def pattern = new M2ResourcePattern("prefix/" + MavenPattern.M2_PATTERN)
-        def module1 = new DefaultModuleIdentifier("group", "projectA")
-        def module2 = new DefaultModuleIdentifier("org.group", "projectA")
-
-        expect:
-        pattern.toModulePath(module1) == 'prefix/group/projectA'
-        pattern.toModulePath(module2) == 'prefix/org/group/projectA'
-    }
-
-    def "can build module version path"() {
-        def pattern = new M2ResourcePattern("prefix/" + MavenPattern.M2_PATTERN)
-        def artifact1 = artifact("group", "projectA", "1.2")
-        def artifact2 = artifact("org.group", "projectA", "1.2")
-
-        expect:
-        pattern.toModuleVersionPath(artifact1) == 'prefix/group/projectA/1.2'
-        pattern.toModuleVersionPath(artifact2) == 'prefix/org/group/projectA/1.2'
-    }
-
-    def "throws UnsupportedOperationException for non M2 compatible pattern"() {
-        def pattern = new M2ResourcePattern("/non/m2/pattern")
-
-        when:
-        pattern.toModulePath(new DefaultModuleIdentifier("group", "module"))
-
-        then:
-        thrown(UnsupportedOperationException)
-    }
-
-    private static ModuleVersionArtifactMetaData artifact(String group, String name, String version) {
-        final moduleVersionId = DefaultModuleVersionIdentifier.newId(group, name, version)
-        return new DefaultModuleVersionArtifactMetaData(new DefaultModuleVersionArtifactIdentifier(moduleVersionId, "ivy", "ivy", "xml"))
-    }
-}
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
deleted file mode 100644
index f900983..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
+++ /dev/null
@@ -1,53 +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.resolver
-
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
-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
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class MavenResolverTest extends Specification {
-    def repositoryTransport = Mock(RepositoryTransport)
-    def repository = Mock(ExternalResourceRepository)
-
-    def rootUri = URI.create("localhost:8081:/testrepo/")
-    def locallyAvailableResourceFinder = Mock(LocallyAvailableResourceFinder)
-    def parser = Mock(MetaDataParser)
-    def resolverStrategy = Stub(ResolverStrategy)
-
-    def setup() {
-        repositoryTransport.getRepository() >> repository
-    }
-
-    @Unroll
-    def "setUseMavenMetaData '#value' adapts versionLister to #classname"() {
-        setup:
-        MavenResolver testresolver = new MavenResolver("test maven resolver", rootUri, repositoryTransport,
-                locallyAvailableResourceFinder, resolverStrategy)
-        when:
-        testresolver.setUseMavenMetadata(value)
-        then:
-        testresolver.versionLister.class.name == classname
-        where:
-        value << [true, false]
-        classname << [ChainedVersionLister.class.name, ResourceVersionLister.class.name]
-    }
-}
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
deleted file mode 100644
index 0d3d608..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
+++ /dev/null
@@ -1,195 +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.resolver
-import org.gradle.api.Action
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-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.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData
-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 = IvyUtil.createModuleRevisionId("org.acme", "testproject", "1.0")
-    def module = new DefaultModuleIdentifier("org.acme", "testproject")
-    def moduleVersion = new DefaultModuleVersionIdentifier(module, "1.0")
-    def artifact = new DefaultModuleVersionArtifactMetaData(new DefaultModuleVersionArtifactIdentifier(moduleVersion, "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 MavenVersionLister lister = new MavenVersionLister(repository)
-
-    def "visit parses maven-metadata.xml"() {
-        ExternalResource resource = Mock()
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern, artifact)
-
-        then:
-        sort(versionList).collect {it.version} == ['1.2', '1.1']
-
-        and:
-        1 * repository.getResource(metaDataResource) >> resource
-        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
-<metadata>
-    <versioning>
-        <versions>
-            <version>1.1</version>
-            <version>1.2</version>
-        </versions>
-    </versioning>
-</metadata>""".bytes))
-        }
-        1 * resource.close()
-        0 * repository._
-        0 * resource._
-    }
-
-    def "visit builds union of versions"() {
-        ExternalResource resource1 = Mock()
-        ExternalResource resource2 = Mock()
-
-        when:
-        def versionList = lister.getVersionList(module)
-        final pattern1 = pattern("prefix1/" + MavenPattern.M2_PATTERN)
-        versionList.visit(pattern1, artifact)
-        final pattern2 = pattern("prefix2/" + MavenPattern.M2_PATTERN)
-        versionList.visit(pattern2, artifact)
-
-        then:
-        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.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
-<metadata>
-    <versioning>
-        <versions>
-            <version>1.1</version>
-            <version>1.2</version>
-        </versions>
-    </versioning>
-</metadata>""".bytes))
-        }
-        1 * repository.getResource('prefix2/org/acme/testproject/maven-metadata.xml') >> resource2
-        1 * resource2.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
-<metadata>
-    <versioning>
-        <versions>
-            <version>1.2</version>
-            <version>1.3</version>
-        </versions>
-    </versioning>
-</metadata>""".bytes))
-        }
-    }
-
-    def "visit ignores duplicate patterns"() {
-        ExternalResource resource = Mock()
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern, artifact)
-        versionList.visit(pattern, artifact)
-
-        then:
-        sort(versionList).collect {it.version} == ['1.2', '1.1']
-
-        and:
-        1 * repository.getResource(metaDataResource) >> resource
-        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
-<metadata>
-    <versioning>
-        <versions>
-            <version>1.1</version>
-            <version>1.2</version>
-        </versions>
-    </versioning>
-</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(module)
-        versionList.visit(pattern, artifact)
-
-        then:
-        ResourceNotFoundException e = thrown()
-        e.message == "Maven meta-data not available: $metaDataResource"
-        1 * repository.getResource(metaDataResource) >> null
-        0 * repository._
-    }
-
-    def "visit throws ResourceException when maven-metadata cannot be parsed"() {
-        ExternalResource resource = Mock()
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern, artifact)
-
-        then:
-        ResourceException e = thrown()
-        e.message == "Unable to load Maven meta-data from $metaDataResource."
-        e.cause instanceof UncheckedException
-        e.cause.cause instanceof SAXParseException
-        1 * resource.close()
-        1 * repository.getResource(metaDataResource) >> resource;
-        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("yo".bytes)) }
-        0 * repository._
-    }
-
-    def "visit throws ResourceException when maven-metadata cannot be loaded"() {
-        def failure = new IOException()
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern, artifact)
-
-        then:
-        ResourceException e = thrown()
-        e.message == "Unable to load Maven meta-data from $metaDataResource."
-        e.cause == failure
-        1 * repository.getResource(metaDataResource) >> { throw failure }
-        0 * repository._
-    }
-
-    def pattern(String 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
deleted file mode 100644
index 3433fcc..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
+++ /dev/null
@@ -1,199 +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.resolver
-
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-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.artifacts.metadata.DefaultModuleVersionArtifactIdentifier
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionArtifactMetaData
-import org.gradle.api.internal.externalresource.transport.ExternalResourceRepository
-import org.gradle.api.internal.resource.ResourceException
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class ResourceVersionListerTest extends Specification {
-
-    def repo = Mock(ExternalResourceRepository)
-    def moduleRevisionId = IvyUtil.createModuleRevisionId("org.acme", "proj1", "1.0")
-    def module = new DefaultModuleIdentifier("org.acme", "proj1")
-    def moduleVersion = new DefaultModuleVersionIdentifier(module, "1.0")
-    def artifact = new DefaultModuleVersionArtifactMetaData(new DefaultModuleVersionArtifactIdentifier(moduleVersion, "proj1", "jar", "jar"))
-
-    def ResourceVersionLister lister;
-
-    def setup() {
-        lister = new ResourceVersionLister(repo)
-    }
-
-    def "visit propagates Exceptions as ResourceException"() {
-        setup:
-        def failure = new IOException("Test IO Exception")
-        def testPattern = pattern("/a/pattern/with/[revision]/")
-        1 * repo.list(_) >> { throw failure }
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(testPattern, artifact)
-
-        then:
-        ResourceException e = thrown()
-        e.message == "Could not list versions using Ivy pattern '/a/pattern/with/[revision]/'."
-        e.cause == failure
-    }
-
-    def "visit produces empty versionList for missing resource"() {
-        setup:
-        1 * repo.list(_) >> null
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern(testPattern), artifact)
-
-        then:
-        versionList.empty
-
-        where:
-        testPattern << ["/some/[revision]", "/some/version-[revision]"]
-    }
-
-    def "visit returns empty VersionList when repository contains empty list"() {
-        setup:
-        1 * repo.list(_) >> []
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern("/some/[revision]"), artifact)
-
-        then:
-        versionList.empty
-    }
-
-    @Unroll
-    def "visit resolves versions from from pattern with '#testPattern'"() {
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern(testPattern), artifact)
-
-        then:
-        sort(versionList).collect { it.version } == ["2.1", "1", "a-version"]
-
-        and:
-        1 * repo.list(repoListingPath) >> repoResult
-        0 * repo._
-
-        where:
-        testPattern                              | repoListingPath | repoResult
-        "[revision]"                             | ""              | ["1", "2.1/", "a-version"]
-        "[revision]/"                            | ""              | ["1", "2.1/", "a-version"]
-        "/[revision]"                            | "/"             | ["1", "2.1/", "a-version"]
-        "/[revision]/"                           | "/"             | ["1", "2.1/", "a-version"]
-        "/some/[revision]"                       | "/some/"        | ["/some/1", "/some/2.1/", "/some/a-version"]
-        "/some/[revision]/"                      | "/some/"        | ["/some/1", "/some/2.1/", "/some/a-version"]
-        "/some/[revision]/lib"                   | "/some/"        | ["/some/1/", "/some/2.1", "/some/a-version"]
-        "/some/version-[revision]"               | "/some/"        | ["/some/version-1", "/some/version-2.1", "/some/version-a-version", "/some/nonmatching"]
-        "/some/version-[revision]/lib"           | "/some/"        | ["/some/version-1", "/some/version-2.1", "/some/version-a-version", "/some/nonmatching"]
-        "/some/version-[revision]/lib/"          | "/some/"        | ["/some/version-1", "/some/version-2.1", "/some/version-a-version", "/some/nonmatching"]
-        "/some/[revision]-version"               | "/some/"        | ["/some/1-version", "/some/2.1-version", "/some/a-version-version", "/some/nonmatching"]
-        "/some/[revision]-version/lib"           | "/some/"        | ["/some/1-version", "/some/2.1-version", "/some/a-version-version", "/some/nonmatching"]
-        "/some/[revision]-lib.[ext]"             | "/some/"        | ["/some/1-lib.jar", "/some/1-lib.zip", "/some/2.1-lib.jar", "/some/a-version-lib.jar", "/some/nonmatching"]
-        "/some/any-[revision]-version/lib"       | "/some/"        | ["/some/any-1-version", "/some/any-2.1-version", "/some/any-a-version-version", "/some/nonmatching"]
-        "/some/any-[revision]-version/lib/"      | "/some/"        | ["/some/any-1-version", "/some/any-2.1-version", "/some/any-a-version-version", "/some/nonmatching"]
-        "/some/[revision]/lib/myjar-[revision]/" | "/some/"        | ["/some/1", "/some/2.1", "/some/a-version"]
-        "/some/proj-[revision]/[revision]/lib/"  | "/some/"        | ["/some/proj-1", "/some/proj-2.1", "/some/proj-a-version"]
-    }
-
-    def "visit builds union of versions"() {
-        when:
-        def versionList = lister.getVersionList(module)
-        def pattern1 = pattern("/[revision]/[artifact]-[revision].[ext]")
-        def pattern2 = pattern("/[organisation]/[revision]/[artifact]-[revision].[ext]")
-        versionList.visit(pattern1, artifact)
-        versionList.visit(pattern2, artifact)
-
-        then:
-        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"]
-        1 * repo.list("/org.acme/") >> ["1.3", "1.4"]
-        0 * repo._
-    }
-
-    def "visit ignores duplicate patterns"() {
-        when:
-        def versionList = lister.getVersionList(module)
-        final patternA = pattern("/a/[revision]/[artifact]-[revision].[ext]")
-        versionList.visit(patternA, artifact)
-        versionList.visit(pattern("/a/[revision]/[artifact]-[revision]"), artifact)
-
-        then:
-        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(module)
-        versionList.visit(pattern(inputPattern), artifact)
-
-        then:
-        1 * repo.list(repoPath) >> ['1.2']
-
-        where:
-        inputPattern                                  | repoPath
-        "/[organisation]/[revision]"                  | "/org.acme/"
-        "/[organization]/[revision]"                  | "/org.acme/"
-        "/[module]/[revision]"                        | "/proj1/"
-        "/[module]/[revision]-lib.[ext]"              | "/proj1/"
-        "/[organisation]/[module]/[revision]"         | "/org.acme/proj1/"
-        "/[revision]/[module]/[organisation]"         | "/"
-        "/[type]s/[module]/[organisation]/[revision]" | "/jars/proj1/org.acme/"
-    }
-
-    def "visit returns empty version list when pattern has no revision token"() {
-        setup:
-        repo.list(_) >> repoResult
-
-        when:
-        def versionList = lister.getVersionList(module)
-        versionList.visit(pattern(testPattern), artifact)
-
-        then:
-        versionList.empty
-
-        where:
-        testPattern                      | repoResult
-        "/some/pattern/with/no/revision" | ["/some/1-version", "/some/2.1-version", "/some/a-version-version"]
-    }
-
-    def pattern(String pattern) {
-        return new IvyResourcePattern(pattern)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListenerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListenerTest.groovy
deleted file mode 100644
index dd2c72a..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/ProgressLoggingTransferListenerTest.groovy
+++ /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.repositories.transport
-
-import spock.lang.Specification
-import org.apache.ivy.plugins.repository.TransferEvent
-import org.apache.ivy.plugins.repository.Resource
-import org.gradle.logging.ProgressLoggerFactory
-import org.gradle.logging.ProgressLogger
-
-class ProgressLoggingTransferListenerTest extends Specification {
-    TransferEvent transferEvent = Mock()
-    Resource resource = Mock()
-    ProgressLoggerFactory progressLoggerFactory = Mock()
-    org.gradle.api.internal.artifacts.repositories.transport.ProgressLoggingTransferListener progressLoggingTransferListener = new org.gradle.api.internal.artifacts.repositories.transport.ProgressLoggingTransferListener(progressLoggerFactory, null)
-    ProgressLogger progressLogger = Mock()
-
-    def setup() {
-        transferEvent.getResource() >> resource
-    }
-
-    def "transferProgress does not log operations on local resources"() {
-        setup:
-        resource.isLocal() >> true
-        when:
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        then:
-        0 * progressLoggerFactory.newOperation(_)
-        0 * progressLogger.started()
-        0 * progressLogger.progress(_)
-        0 * progressLogger.completed()
-    }
-
-    def "transferProgress logs started transfers"() {
-        setup:
-        transferEvent.getEventType() >> TransferEvent.TRANSFER_STARTED
-        when:
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        then:
-        1 * progressLoggerFactory.newOperation(_) >> progressLogger
-
-        0 * progressLogger.progress(_)
-        0 * progressLogger.completed()
-    }
-
-    def "transferProgress logs progress on transfers"() {
-        setup:
-        progressLoggerFactory.newOperation(_) >> progressLogger
-        transferEvent.getLength() >>> [512, 512, 2048, 256]
-        transferEvent.getEventType() >>> [TransferEvent.TRANSFER_STARTED, TransferEvent.TRANSFER_PROGRESS, TransferEvent.TRANSFER_PROGRESS, TransferEvent.TRANSFER_PROGRESS, TransferEvent.TRANSFER_PROGRESS]
-        when:
-        //create progressLogger
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        and:
-        //log progress
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        progressLoggingTransferListener.transferProgress(transferEvent)
-        then:
-        2 * progressLogger.progress(_)
-        0 * progressLogger.completed()
-    }
-
-}
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
deleted file mode 100644
index 0a13a29..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
+++ /dev/null
@@ -1,113 +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.result
-
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-import org.gradle.internal.Factory
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
-
-class DefaultResolutionResultTest extends Specification {
-
-    def "provides all modules and dependencies including unresolved"() {
-        given:
-        def dep1 = newDependency('dep1')
-        def dep2 = newDependency('dep2')
-
-        def root = newModule('root').addDependency(dep1).addDependency(dep2)
-
-        def dep3 = newDependency('dep3')
-        def dep4 = newUnresolvedDependency('dep4')
-
-        dep2.selected.addDependency(dep3).addDependency(dep4)
-
-        when:
-        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
-        def modules = new DefaultResolutionResult({root} as Factory).allComponents
-
-        then:
-        deps == [dep1, dep2, dep3, dep4] as Set
-
-        and:
-        //does not contain unresolved dep, contains root
-        modules == [root, dep1.selected, dep2.selected, dep3.selected] as Set
-    }
-
-    def "provides hooks for iterating each module or dependency exactly once"() {
-        given:
-        //root -> dep1,dep2; dep1 -> dep3
-        def dep = newDependency('dep1')
-        def dep3 = newDependency('dep3')
-        def root = newModule('root').addDependency(dep).addDependency(newDependency('dep2')).addDependency(dep3)
-        dep.selected.addDependency(dep3)
-
-        def result = new DefaultResolutionResult({root} as Factory)
-
-        when:
-        def deps = []
-        def modules = []
-        result.allDependencies { deps << it }
-        result.allComponents { modules << it }
-
-        then:
-        deps*.requested.group == ['dep1', 'dep3', 'dep2', 'dep3']
-
-        and:
-        modules*.id.group == ['root', 'dep1', 'dep3', 'dep2']
-    }
-
-    def "deals with dependency cycles"() {
-        given:
-        // a->b->a
-        def root = newModule('a', 'a', '1')
-        def dep1 = newDependency('b', 'b', '1')
-        root.addDependency(dep1)
-        dep1.selected.addDependency(new DefaultResolvedDependencyResult(DefaultModuleComponentSelector.newSelector('a', 'a', '1'), root, dep1.selected))
-
-        when:
-        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
-        def modules = new DefaultResolutionResult({root} as Factory).allComponents
-
-        then:
-        deps.size() == 2
-        modules.size() == 2
-    }
-
-    def "mutating all dependencies or modules is harmless"() {
-        given:
-        def dep1 = newDependency('dep1')
-        def dep2 = newDependency('dep2')
-
-        def root = newModule('root').addDependency(dep1).addDependency(dep2)
-
-        when:
-        def result = new DefaultResolutionResult({root} as Factory)
-
-        then:
-        result.allDependencies == [dep1, dep2] as Set
-        result.allComponents == [root, dep1.selected, dep2.selected] as Set
-
-        when:
-        result.allDependencies << newDependency('dep3')
-        result.allComponents << newModule('foo')
-
-        then:
-        result.allDependencies == [dep1, dep2] as Set
-        result.allComponents == [root, dep1.selected, dep2.selected] 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
deleted file mode 100644
index e19bc8c..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedModuleVersionResultSpec.groovy
+++ /dev/null
@@ -1,63 +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.result
-
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
-
-class DefaultResolvedModuleVersionResultSpec extends Specification {
-
-    def "mutating dependencies or dependents is harmless"() {
-        given:
-        def module = newModule("a", "c", "1")
-        def dependency  = newDependency("a", "x", "1")
-        def dependent   = newDependency("a", "x2", "1")
-
-        when:
-        module.addDependency(dependency)
-        module.addDependent(dependent)
-
-        then:
-        module.dependencies == [dependency] as Set
-        module.dependents   == [dependent] as Set
-
-        when:
-        module.dependencies << newDependency("a", "y", "1")
-        then:
-        thrown(UnsupportedOperationException)
-
-        when:
-        module.dependents <<   newDependency("a", "y2", "1")
-        then:
-        thrown(UnsupportedOperationException)
-    }
-
-    def "includes unresolved dependencies"() {
-        given:
-        def module = newModule()
-        def dependency = newDependency()
-        def unresolved = newUnresolvedDependency()
-
-        when:
-        module.addDependency(dependency)
-        module.addDependency(unresolved)
-
-        then:
-        module.dependencies == [dependency, unresolved] as Set
-    }
-}
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
deleted file mode 100644
index 988904c..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/CachedExternalResourceAdapterTest.groovy
+++ /dev/null
@@ -1,78 +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.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.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
-        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
-
-        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
deleted file mode 100644
index 8d72fd0..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/cached/DefaultArtifactResolutionCacheTest.groovy
+++ /dev/null
@@ -1,84 +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.externalresource.cached
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
-import org.gradle.api.internal.externalresource.metadata.DefaultExternalResourceMetaData
-import org.gradle.messaging.serialize.Serializer
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.testfixtures.internal.InMemoryIndexedCache
-import org.gradle.util.BuildCommencedTimeProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class DefaultArtifactResolutionCacheTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
-
-    BuildCommencedTimeProvider timeProvider = Stub(BuildCommencedTimeProvider) {
-        getCurrentTime() >> 0
-    }
-
-    def cacheLockingManager = Stub(CacheLockingManager) {
-        useCache(_, _) >> { displayName, action ->
-            if (action instanceof org.gradle.internal.Factory) {
-                return action.create()
-            } else {
-                action.run()
-            }
-        }
-
-        createCache(_, _, _) >> { String file, Serializer keySerializer, Serializer valueSerializer ->
-            return new InMemoryIndexedCache<>(valueSerializer)
-        }
-    }
-    
-    DefaultCachedExternalResourceIndex<String> index
-
-    def setup() {
-        index = new DefaultCachedExternalResourceIndex("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
deleted file mode 100644
index fb4c835..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
+++ /dev/null
@@ -1,128 +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.externalresource.ivy
-
-import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifier
-import org.gradle.api.internal.externalresource.cached.CachedArtifact
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData
-import org.gradle.cache.PersistentIndexedCache
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.BuildCommencedTimeProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ArtifactAtRepositoryCachedArtifactIndexTest extends Specification {
-
-    CacheLockingManager cacheLockingManager = Mock()
-    BuildCommencedTimeProvider timeProvider = Mock()
-    ArtifactAtRepositoryKey key = Mock()
-    ExternalResourceMetaData metaData = Mock()
-    @Rule TestNameTestDirectoryProvider folder = new TestNameTestDirectoryProvider();
-
-    PersistentIndexedCache persistentIndexedCache = Mock()
-
-    ArtifactAtRepositoryCachedArtifactIndex index
-    String persistentCacheFile
-    CachedArtifact cachedArtifact = Mock()
-
-
-    def setup() {
-        persistentCacheFile = "cacheFile"
-        index = new ArtifactAtRepositoryCachedArtifactIndex(persistentCacheFile, timeProvider, cacheLockingManager)
-    }
-
-    def "storing null artifactFile not supported"() {
-        when:
-        index.store(key, null, 0)
-
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "artifactFile cannot be null"
-    }
-
-    def "artifact key must be provided"() {
-        when:
-        index.store(null, Mock(File), 0)
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "key cannot be null"
-    }
-
-    def "stored artifact is put into persistentIndexedCache"() {
-        setup:
-        1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
-        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ModuleVersionArtifactIdentifier));
-        def testFile = folder.createFile("aTestFile");
-        when:
-        index.store(key, testFile, BigInteger.TEN)
-
-        then:
-        1 * cacheLockingManager.useCache("store into artifact resolution cache \'cacheFile\'", _) >> {descr, action -> action.run()}
-        1 * timeProvider.currentTime >> 123
-        1 * persistentIndexedCache.put(key, _) >> { k, v ->
-            assert v.cachedAt == 123
-            assert v.descriptorHash == BigInteger.TEN
-            assert v.cachedFile == testFile
-        }
-    }
-
-    def "lookup key must not be null"() {
-        when:
-        index.lookup(null)
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "key cannot be null"
-    }
-
-    def "loads missing CachedArtifact from persistentIndexedCache"() {
-        setup:
-        def key = createEntryInPersistentCache()
-        _ * cachedArtifact.missing >> true
-
-        when:
-        def fromCache = index.lookup(key)
-
-        then:
-        fromCache == cachedArtifact
-        fromCache.isMissing()
-    }
-
-    def "loads CachedArtifact with file ref from persistentIndexedCache"() {
-        setup:
-        def key = createEntryInPersistentCache()
-        _ * cachedArtifact.missing >> false
-        File cachedFile = Mock()
-        cachedFile.exists() >> true;
-        cachedArtifact.getCachedFile() >> cachedFile
-        when:
-        def fromCache = index.lookup(key)
-
-        then:
-        fromCache == cachedArtifact
-        !fromCache.isMissing()
-        fromCache.cachedFile == cachedArtifact.cachedFile
-    }
-
-    def createEntryInPersistentCache() {
-        1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
-        1 * cacheLockingManager.useCache("lookup from artifact resolution cache \'cacheFile\'", _) >> {descr, factory -> factory.create()}
-        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ModuleVersionArtifactIdentifier));
-        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
deleted file mode 100644
index d2e0b5f..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/CompositeLocallyAvailableResourceFinderTest.groovy
+++ /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.api.internal.externalresource.local
-
-import org.gradle.internal.resource.local.LocallyAvailableResource
-import spock.lang.Specification
-import org.gradle.internal.hash.HashUtil
-
-class CompositeLocallyAvailableResourceFinderTest extends Specification {
-    
-    def "interrogates composites in turn as needed"() {
-        given:
-        def f1 = Mock(LocallyAvailableResourceFinder)
-        def c1 = Mock(LocallyAvailableResourceCandidates)
-        def f2 = Mock(LocallyAvailableResourceFinder)
-        def c2 = Mock(LocallyAvailableResourceCandidates)
-        def f3 = Mock(LocallyAvailableResourceFinder)
-        def c3 = Mock(LocallyAvailableResourceCandidates)
-        def hash = HashUtil.sha1("abc".bytes)
-
-        def composite = new CompositeLocallyAvailableResourceFinder<String>([f1, f2, f3])
-        def criterion = "abc"
-
-        when:
-        def candidates = composite.findCandidates(criterion)
-
-        then:
-        1 * f1.findCandidates(criterion) >> c1
-
-        then:
-        1 * f2.findCandidates(criterion) >> c2
-
-        then:
-        1 * f3.findCandidates(criterion) >> c3
-
-        and:
-        0 * c1._(*_)
-        0 * c2._(*_)
-        0 * c3._(*_)
-
-        when:
-        def isNone = candidates.isNone()
-
-        then:
-        1 * c1.isNone() >> true
-
-        and:
-        1 * c2.isNone() >> false
-        0 * c3.isNone()
-
-        when:
-        def match = candidates.findByHashValue(hash)
-
-        then:
-        1 * c1.findByHashValue(hash) >> null
-
-        and:
-        1 * c2.findByHashValue(hash) >> Mock(LocallyAvailableResource)
-        0 * c3.findByHashValue(_)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidatesTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidatesTest.groovy
deleted file mode 100644
index add05c1..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/local/LazyLocallyAvailableResourceCandidatesTest.groovy
+++ /dev/null
@@ -1,57 +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.externalresource.local
-
-import org.gradle.internal.Factory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.internal.hash.HashUtil
-import org.junit.Rule
-import spock.lang.Specification
-
-class LazyLocallyAvailableResourceCandidatesTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider tmp
-    
-    def "does not query factory until necessary"() {
-        given:
-        def factory = Mock(Factory)
-
-        when:
-        def candidates = new LazyLocallyAvailableResourceCandidates(factory)
-
-        then:
-        0 * factory.create()
-        
-        when:
-        def isNone = candidates.isNone()
-        
-        then:
-        !isNone
-        1 * factory.create() >> [file("abc"), file("def")]
-        
-        when:
-        def candidate = candidates.findByHashValue(HashUtil.sha1("def".bytes))
-
-        then:
-        candidate.file.name == "def"
-        0 * factory.create()
-    }
-    
-    File file(path) {
-        tmp.createFile(path) << path
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaDataTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaDataTest.groovy
deleted file mode 100644
index 57b1d0b..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/DefaultExternalResourceMetaDataTest.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.api.internal.externalresource.metadata
-
-import spock.lang.Specification
-import org.gradle.internal.hash.HashValue
-
-class DefaultExternalResourceMetaDataTest extends Specification {
-
-    def "hash value is preserved"() {
-        given:
-        def sha1 = "abc"
-        def md = new DefaultExternalResourceMetaData("somewhere", -1, -1, null, new HashValue(sha1))
-
-        expect:
-        md.sha1.equals(new HashValue(sha1))
-    }
-
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompareTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompareTest.groovy
deleted file mode 100644
index 124b6e2..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/metadata/ExternalResourceMetaDataCompareTest.groovy
+++ /dev/null
@@ -1,172 +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.externalresource.metadata
-
-import org.gradle.internal.Factory
-import spock.lang.Shared
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class ExternalResourceMetaDataCompareTest extends Specification {
-
-    @Shared now = new Date()
-
-    def local = Mock(ExternalResourceMetaData)
-    def remote = Mock(ExternalResourceMetaData)
-    def factory = Mock(Factory)
-
-    def unchanged = false
-
-    def "always changed with no local metadata"() {
-        when:
-        compare(null, factory)
-
-        then:
-        !unchanged
-        0 * factory.create()
-    }
-
-    @Unroll
-    "always unchanged with incomplete local metadata"() {
-        given:
-        configureMetadata(local, etag, lastModified, contentLength)
-
-        when:
-        compare(local, factory)
-
-        then:
-        !unchanged
-        0 * factory.create()
-
-        where:
-        etag | lastModified | contentLength
-        null | null         | -1
-        null | now          | -1
-        null | null         | -1
-    }
-
-    @Unroll
-    "always unchanged with incomplete remote metadata"() {
-        given:
-        configureMetadata(local)
-        configureMetadata(remote, null, lastModified, contentLength)
-
-        when:
-        compare(local, factory)
-
-        then:
-        !unchanged
-        1 * factory.create() >> remote
-
-        where:
-        lastModified | contentLength
-        null         | -1
-        now          | -1
-        null         | -1
-    }
-
-    def "always changed with no remote metadata"() {
-        given:
-        configureMetadata(local)
-
-        when:
-        compare(local, factory)
-
-        then:
-        !unchanged
-        1 * factory.create() >> null
-    }
-
-    def "is unchanged if everything is equal"() {
-        given:
-        configureMetadata(local)
-        configureMetadataForEtagMatch(remote)
-
-        when:
-        compare(local, remote)
-
-        then:
-        unchanged
-    }
-
-    def "matching etags are enough to be considered equal"() {
-        given:
-        configureMetadata(local, "abc", null, -1)
-        configureMetadataForEtagMatch(remote, "abc")
-
-        when:
-        compare(local, remote)
-
-        then:
-        unchanged
-    }
-
-    def "non matching etags, no mod date, but matching content length does not match"() {
-        given:
-        configureMetadata(local, "abc", null, 10)
-        configureMetadataForEtagMatch(remote, "cde")
-
-        when:
-        compare(local, remote)
-
-        then:
-        !unchanged
-    }
-
-    def "non matching etags, matching mod date, but different content length does not match"() {
-        given:
-        configureMetadata(local, "abc", now, -1)
-        configureMetadataForEtagMatch(remote, "cde")
-
-        when:
-        compare(local, remote)
-
-        then:
-        !unchanged
-    }
-
-    def configureMetadata(ExternalResourceMetaData metaData, String etag = "abc", Date lastModified = now, long contentLength = 100) {
-        interaction {
-            1 * metaData.getEtag() >> etag
-
-            1 * metaData.getLastModified() >> lastModified
-            if (lastModified != null || etag != null) {
-                1 * metaData.getContentLength() >> contentLength
-            } else {
-                0 * metaData.getContentLength()
-            }
-        }
-    }
-
-    def configureMetadataForEtagMatch(ExternalResourceMetaData metaData, String etag = "abc") {
-        interaction {
-            1 * metaData.getEtag() >> etag
-            0 * metaData.getLastModified()
-            0 * metaData.getContentLength()
-        }
-    }
-    boolean compare(ExternalResourceMetaData local, ExternalResourceMetaData remote) {
-        compare(local, new Factory() {
-            def create() { remote }
-        })
-    }
-
-    boolean compare(ExternalResourceMetaData local, Factory<ExternalResourceMetaData> remoteFactory) {
-        unchanged = ExternalResourceMetaDataCompare.isDefinitelyUnchanged(local, remoteFactory)
-        unchanged
-    }
-}
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
deleted file mode 100644
index be40d75..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
+++ /dev/null
@@ -1,63 +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.externalresource.transfer
-
-import org.gradle.api.internal.externalresource.cached.CachedExternalResourceIndex
-import org.gradle.util.BuildCommencedTimeProvider
-import spock.lang.Specification
-import org.gradle.api.internal.externalresource.local.LocallyAvailableResourceCandidates
-import org.gradle.api.internal.externalresource.cached.CachedExternalResource
-import org.gradle.api.internal.externalresource.metadata.ExternalResourceMetaData
-import org.gradle.internal.hash.HashValue
-import org.gradle.internal.resource.local.LocallyAvailableResource
-import org.gradle.api.internal.externalresource.DefaultLocallyAvailableExternalResource
-
-class DefaultCacheAwareExternalResourceAccessorTest extends Specification {
-    final accessor = Mock(ExternalResourceAccessor)
-    final index = Mock(CachedExternalResourceIndex)
-    final timeProvider = Mock(BuildCommencedTimeProvider)
-    final cache = new DefaultCacheAwareExternalResourceAccessor(accessor, index, timeProvider)
-
-    def "will use sha1 from metadata for finding candidates if available"() {
-        given:
-        def localCandidates = Mock(LocallyAvailableResourceCandidates)
-        def cached = Mock(CachedExternalResource)
-        def sha1 = HashValue.parse("abc")
-        def cachedMetaData = Mock(ExternalResourceMetaData)
-        def remoteMetaData = Mock(ExternalResourceMetaData)
-        def localCandidate = Mock(LocallyAvailableResource)
-
-        and:
-        index.lookup("location") >> cached
-        cached.getExternalResourceMetaData() >> cachedMetaData
-        accessor.getMetaData("location") >> remoteMetaData
-        localCandidates.isNone() >> false
-        remoteMetaData.sha1 >> sha1
-        
-        when:
-        def foundResource = cache.getResource("location", localCandidates)
-
-        then:
-        1 * timeProvider.currentTime >> new Date().time
-        1 * cached.cachedAt >> new Date().time + 2
-        0 * accessor.getResourceSha1(_)
-        1 * localCandidates.findByHashValue(sha1) >> localCandidate
-
-        and:
-        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
deleted file mode 100644
index e5168cd..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
+++ /dev/null
@@ -1,111 +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.externalresource.transfer
-
-import org.gradle.api.internal.externalresource.ExternalResource
-import org.gradle.logging.ProgressLogger
-import org.gradle.logging.ProgressLoggerFactory
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class ProgressLoggingExternalResourceAccessorTest extends Specification {
-
-    ExternalResourceAccessor accessor = Mock()
-    ProgressLoggerFactory progressLoggerFactory = Mock();
-    ProgressLoggingExternalResourceAccessor progressLoggerAccessor = new ProgressLoggingExternalResourceAccessor(accessor, progressLoggerFactory)
-    ProgressLogger progressLogger = Mock()
-    ExternalResource externalResource = Mock()
-
-    @Unroll
-    def "delegates #method to delegate resource accessor"() {
-        when:
-        progressLoggerAccessor."$method"("location")
-        then:
-        1 * accessor."$method"("location")
-        where:
-        method << ['getMetaData', 'getResource', 'getResourceSha1']
-    }
-
-    def "getResource returns null when delegate returns null"() {
-        setup:
-        accessor.getResource("location") >> null
-        when:
-        def loadedResource = progressLoggerAccessor.getResource("location")
-        then:
-        loadedResource == null
-    }
-
-    def "getResource wraps loaded Resource from delegate in ProgressLoggingExternalResource"() {
-        setup:
-        accessor.getResource("location") >> externalResource
-        when:
-        def loadedResource = progressLoggerAccessor.getResource("location")
-        then:
-        loadedResource != null
-        loadedResource instanceof ProgressLoggingExternalResourceAccessor.ProgressLoggingExternalResource
-    }
-
-    def "ProgressLoggingExternalResource.writeTo wraps delegate call in progress logger"() {
-        setup:
-        accessor.getResource("location") >> externalResource
-        externalResource.getName() >> "test resource"
-        externalResource.getContentLength() >> 2060
-        externalResource.writeTo(_) >> { OutputStream stream ->
-            stream.write(12)
-            stream.write(2)
-            stream.write(112)
-            stream.write(new byte[1024])
-        }
-        when:
-        progressLoggerAccessor.getResource("location").writeTo(new ByteArrayOutputStream())
-        then:
-        1 * progressLoggerFactory.newOperation(_) >> progressLogger
-        1 * progressLogger.started()
-        1 * progressLogger.progress(_)
-        1 * progressLogger.completed()
-    }
-
-    def "no progress events logged for resources smaller 1024 bytes"() {
-        setup:
-        accessor.getResource("location") >> externalResource
-        externalResource.getName() >> "test resource"
-        externalResource.getContentLength() >> 1023
-        externalResource.writeTo(_) >> { OutputStream stream ->
-            stream.write(new byte[1023])
-        }
-        when:
-        progressLoggerAccessor.getResource("location").writeTo(new ByteArrayOutputStream())
-        then:
-        1 * progressLoggerFactory.newOperation(_) >> progressLogger
-        1 * progressLogger.started()
-        1 * progressLogger.completed()
-        0 * progressLogger.progress(_)
-    }
-
-    @Unroll
-    def "ProgressLoggingExternalResource delegates #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']
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploaderTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploaderTest.groovy
deleted file mode 100644
index 25d09fb..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ProgressLoggingExternalResourceUploaderTest.groovy
+++ /dev/null
@@ -1,67 +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.externalresource.transfer
-
-import org.gradle.internal.Factory
-import org.gradle.logging.ProgressLogger
-import org.gradle.logging.ProgressLoggerFactory
-import spock.lang.Specification
-
-class ProgressLoggingExternalResourceUploaderTest extends Specification {
-    ExternalResourceUploader uploader = Mock()
-    ProgressLoggerFactory progressLoggerFactory = Mock();
-    def progressLoggerUploader = new ProgressLoggingExternalResourceUploader(uploader, progressLoggerFactory)
-    ProgressLogger progressLogger = Mock()
-    InputStream inputStream = Mock();
-    Factory<InputStream> delegateFactory = Mock();
-
-    def "delegates upload to delegate uploader and logs progress"() {
-        setup:
-        startsProgress()
-
-        when:
-        progressLoggerUploader.upload(inputStreamFactory(), 5 * 1024, "http://a/remote/path")
-        then:
-        1 * delegateFactory.create() >> inputStream
-        1 * uploader.upload(_, 5 * 1024, "http://a/remote/path") >> {factory, length, destination ->
-            def stream = factory.create();
-            assert stream.read(new byte[1024]) == 1024
-            assert stream.read() == 48
-        }
-        1 * inputStream.read(_, 0, 1024) >> 1024
-        1 * inputStream.read() >> 48
-        1 * progressLogger.progress(_)
-        1 * progressLogger.completed()
-    }
-
-    private Factory inputStreamFactory() {
-        new Factory<InputStream>() {
-            InputStream create() {
-                return delegateFactory.create()
-            }
-        }
-    }
-
-    def startsProgress() {
-        1 * progressLoggerFactory.newOperation(ProgressLoggingExternalResourceUploader.class) >> progressLogger;
-        1 * progressLogger.setDescription("Upload http://a/remote/path")
-        1 * progressLogger.setLoggingHeader("Upload http://a/remote/path")
-        1 * progressLogger.started()
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperationTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperationTest.groovy
deleted file mode 100644
index 00e33c6..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transfer/ResourceOperationTest.groovy
+++ /dev/null
@@ -1,95 +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.externalresource.transfer
-
-import spock.lang.Specification
-import org.gradle.logging.ProgressLogger
-
-class ResourceOperationTest extends Specification {
-
-    ProgressLogger progressLogger = Mock()
-
-    def "no progress event is logged for files < 1024bytes"(){
-        given:
-        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 1023)
-        when:
-        operation.logProcessedBytes(1023)
-        then:
-        0 * progressLogger.progress(_)
-    }
-
-    def "logs processed bytes in kbyte intervalls"() {
-        given:
-        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 1024 * 10)
-        when:
-        operation.logProcessedBytes(512 * 0)
-        operation.logProcessedBytes(512 * 1)
-        then:
-        0 * progressLogger.progress(_)
-
-        when:
-        operation.logProcessedBytes(512 * 1)
-        operation.logProcessedBytes(512 * 2)
-        then:
-        1 * progressLogger.progress("1 KB/10 KB downloaded")
-        1 * progressLogger.progress("2 KB/10 KB downloaded")
-        0 * progressLogger.progress(_)
-    }
-
-    def "last chunk of bytes <1k is not logged"(){
-        given:
-        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 2000)
-        when:
-        operation.logProcessedBytes(1000)
-        operation.logProcessedBytes(1000)
-        then:
-        1 * progressLogger.progress("1 KB/1 KB downloaded")
-        0 * progressLogger.progress(_)
-    }
-
-    def "adds operationtype information in progress output"() {
-        given:
-        def operation = new ResourceOperation(progressLogger, type, 1024 * 10)
-        when:
-        operation.logProcessedBytes(1024)
-        then:
-        1 * progressLogger.progress(message)
-        where:
-        type                            | message
-        ResourceOperation.Type.download | "1 KB/10 KB downloaded"
-        ResourceOperation.Type.upload   | "1 KB/10 KB uploaded"
-    }
-
-    void "completed completes progressLogger"() {
-        given:
-        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.upload, 1)
-        when:
-        operation.completed()
-        then:
-        1 * progressLogger.completed()
-    }
-
-    void "handles unknown content length"() {
-        given:
-        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.upload, 0)
-        when:
-        operation.logProcessedBytes(1024)
-        then:
-        1 * progressLogger.progress("1 KB/unknown size uploaded")
-    }
-}
-
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
deleted file mode 100644
index 62ef640..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ApacheDirectoryListingParserTest.groovy
+++ /dev/null
@@ -1,169 +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.externalresource.transport.http
-
-import org.gradle.api.internal.resource.ResourceException;
-import org.gradle.util.Resources
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import static org.junit.Assert.assertNotNull
-
-class ApacheDirectoryListingParserTest extends Specification {
-    @Rule public final Resources resources = new Resources();
-
-    private static final CONTENT_TYPE = "text/html;charset=utf-8";
-    private URI baseUrl = URI.create("http://testrepo/")
-    private ApacheDirectoryListingParser parser = new ApacheDirectoryListingParser();
-
-    def "parse returns empty List if no link can be found"() {
-        expect:
-        List urls = parser.parse(baseUrl, new ByteArrayInputStream("<html>no link here</html>".bytes), CONTENT_TYPE)
-        assertNotNull(urls)
-        urls.isEmpty()
-    }
-
-    def "addTrailingSlashes adds trailing slashes on relative URL if not exist"() {
-        expect:
-        new URI(resultingURI) == parser.addTrailingSlashes(new URI(inputURI))
-        where:
-        inputURI                     | resultingURI
-        "http://testrepo"            | "http://testrepo/"
-        "http://testrepo/"           | "http://testrepo/"
-        "http://testrepo/index.html" | "http://testrepo/index.html"
-    }
-
-    def "parse handles multiple listed links"() {
-        def html = """
-        <a href="directory1">directory1</a>
-        <a href="directory2">directory2</a>
-        <a href="directory3">directory3</a>
-        <a href="directory4"/>"""
-        expect:
-        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"]
-    }
-
-    def "only text/html content type is supported"() {
-        def html = """
-        <a href="directory1">directory1</a>
-        <a href="directory2">directory2</a>"""
-        when:
-        parser.parse(baseUrl, new ByteArrayInputStream(html.bytes), contentType)
-        then:
-        thrown(ResourceException)
-        where:
-        contentType << ["text/plain", "application/octetstream"]
-    }
-
-    def "uses charset specified in content type"() {
-        def html = """
-        <a href="\u00c1\u00d2">directory1</a>
-        """
-        def encodedHtml = html.getBytes('ISO-8859-1')
-        assert !Arrays.equals(encodedHtml, html.getBytes("utf-8"))
-
-        expect:
-        def uris = parser.parse(baseUrl, new ByteArrayInputStream(encodedHtml), 'text/html;charset=ISO-8859-1')
-        uris.collect {it.toString()} == ["http://testrepo/\u00c1\u00d2"]
-    }
-
-    def "defaults to utf-8 when no charset specified"() {
-        def html = """
-        <a href="\u0321\u0322">directory1</a>
-        """
-        def encodedHtml = html.getBytes('utf-8')
-
-        expect:
-        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, 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"
-        "../"                                               | "links to parent URLs of base URL"
-        "http://[2h:23:3]"                                  | "invalid URLs"
-        "dir1/subdir1"                                      | "links to nested subdirectories"
-        "<![CDATA[<a href=\"directory2\">directory2</a>]]>" | "links in CDATA blocks"
-        "#achor"                                            | "anchor links"
-        "<a name=\"anchorname\">headline</a>"               | "anchor definitions"
-    }
-
-
-    @Unroll
-    def "parseLink handles #urlDescr"() {
-        def listingParser = new ApacheDirectoryListingParser()
-        expect:
-        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:
-        baseUri                | href                         | urlDescr
-        "http://testrepo"      | "directory1"                 | "relative URLS"
-        "http://testrepo"      | "/directory1"                | "absolute URLS"
-        "http://testrepo"      | "./directory1"               | "explicit relative URLS"
-        "http://testrepo"      | "http://testrepo/directory1" | "complete URLS"
-        "http://testrepo"      | "http://testrepo/directory1" | "hrefs with truncated text"
-        "http://testrepo"      | "http://testrepo/directory1" | "hrefs with truncated text"
-        "http://[2001:db8::7]" | "directory1"                 | "ipv6 host with relative URLS"
-        "http://[2001:db8::7]" | "./directory1"               | "ipv6 host with explicit relative URLS"
-        "http://192.0.0.10"    | "directory1"                 | "ipv4 host with relative URLS"
-        "http://192.0.0.10"    | "./directory1"               | "ipv4 host with relative URLS"
-    }
-
-    @Unroll
-    def "parse is compatible with #repoType"() {
-        setup:
-        def byte[] content = resources.getResource("${repoType}_dirlisting.html").bytes
-        expect:
-        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/",
-                "${artifactRootURI}3.8.2/",
-                "${artifactRootURI}4.0/",
-                "${artifactRootURI}4.1/",
-                "${artifactRootURI}4.10/",
-                "${artifactRootURI}4.2/",
-                "${artifactRootURI}4.3/",
-                "${artifactRootURI}4.3.1/",
-                "${artifactRootURI}4.4/",
-                "${artifactRootURI}4.5/",
-                "${artifactRootURI}4.6/",
-                "${artifactRootURI}4.7/",
-                "${artifactRootURI}4.8/",
-                "${artifactRootURI}4.8.1/",
-                "${artifactRootURI}4.8.2/",
-                "${artifactRootURI}4.9/",
-                "${artifactRootURI}maven-metadata.xml",
-                "${artifactRootURI}maven-metadata.xml.md5",
-                "${artifactRootURI}maven-metadata.xml.sha1"] as Set
-        where:
-        artifactRootURI                                                         | repoType
-        "http://localhost:8081/artifactory/repo1/junit/junit/"                  | "artifactory"
-        "http://repo1.maven.org/maven2/junit/junit/"                            | "mavencentral"
-        "http://localhost:8081/nexus/content/repositories/central/junit/junit/" | "nexus"
-    }
-
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurerTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurerTest.groovy
deleted file mode 100644
index e11c232..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientConfigurerTest.groovy
+++ /dev/null
@@ -1,101 +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.transport.http
-
-import org.apache.http.auth.AuthScope
-import org.apache.http.impl.client.DefaultHttpClient
-import org.apache.http.params.HttpProtocolParams
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.api.internal.resource.UriResource
-import spock.lang.Specification
-
-public class HttpClientConfigurerTest extends Specification {
-    DefaultHttpClient httpClient = new DefaultHttpClient()
-    PasswordCredentials credentials = Mock()
-    HttpSettings httpSettings = Mock()
-    HttpProxySettings proxySettings = Mock()
-    HttpClientConfigurer configurer = new HttpClientConfigurer(httpSettings)
-
-    def "configures http client with no credentials or proxy"() {
-        httpSettings.credentials >> credentials
-        httpSettings.proxySettings >> proxySettings
-
-        when:
-        configurer.configure(httpClient)
-
-        then:
-        !httpClient.getHttpRequestRetryHandler().retryRequest(new IOException(), 1, null)
-    }
-
-    def "configures http client with proxy credentials"() {
-        httpSettings.credentials >> credentials
-        httpSettings.proxySettings >> proxySettings
-        proxySettings.proxy >> new HttpProxySettings.HttpProxy("host", 1111, "domain/proxyUser", "proxyPass")
-
-        when:
-        configurer.configure(httpClient)
-
-        then:
-        def proxyCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope("host", 1111))
-        proxyCredentials.userPrincipal.name == "domain/proxyUser"
-        proxyCredentials.password == "proxyPass"
-
-        and:
-        def ntlmCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope("host", 1111, AuthScope.ANY_REALM, "ntlm"))
-        ntlmCredentials.userPrincipal.name == 'DOMAIN/proxyUser'
-        ntlmCredentials.domain == 'DOMAIN'
-        ntlmCredentials.userName == 'proxyUser'
-        ntlmCredentials.password == 'proxyPass'
-        ntlmCredentials.workstation != ''
-    }
-
-    def "configures http client with credentials"() {
-        httpSettings.credentials >> credentials
-        credentials.username >> "domain/user"
-        credentials.password >> "pass"
-        httpSettings.proxySettings >> proxySettings
-
-        when:
-        configurer.configure(httpClient)
-
-        then:
-        def basicCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT))
-        basicCredentials.userPrincipal.name == "domain/user"
-        basicCredentials.password == "pass"
-
-        and:
-        def ntlmCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "ntlm"))
-        ntlmCredentials.userPrincipal.name == 'DOMAIN/user'
-        ntlmCredentials.domain == 'DOMAIN'
-        ntlmCredentials.userName == 'user'
-        ntlmCredentials.password == 'pass'
-        ntlmCredentials.workstation != ''
-
-        and:
-        httpClient.getRequestInterceptor(0) instanceof HttpClientConfigurer.PreemptiveAuth
-    }
-
-    def "configures http client with user agent"() {
-        httpSettings.credentials >> credentials
-        httpSettings.proxySettings >> proxySettings
-
-        when:
-        configurer.configure(httpClient)
-
-        then:
-        HttpProtocolParams.getUserAgent(httpClient.params) == UriResource.userAgentString
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelperTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelperTest.groovy
deleted file mode 100644
index e4e9fe5..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpClientHelperTest.groovy
+++ /dev/null
@@ -1,62 +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.externalresource.transport.http
-import org.apache.http.HttpResponse
-import org.apache.http.client.methods.HttpGet
-import org.apache.http.client.methods.HttpRequestBase
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
-import spock.lang.Specification
-
-class HttpClientHelperTest extends Specification {
-    @Rule SetSystemProperties sysProp = new SetSystemProperties()
-
-    def "throws HttpRequestException if an IO error occurs during a request"() {
-        def client = new HttpClientHelper(httpSettings) {
-            @Override
-            protected HttpResponse executeGetOrHead(HttpRequestBase method) {
-                throw new IOException("ouch")
-            }
-        }
-
-        when:
-        client.performRequest(new HttpGet("http://gradle.org"))
-
-        then:
-        HttpRequestException e = thrown()
-        e.cause.message == "ouch"
-    }
-
-    def "always sets http.keepAlive system property to 'true'"() {
-        given:
-        System.setProperty("http.keepAlive", "false")
-
-        when:
-        new HttpClientHelper(httpSettings)
-
-        then:
-        System.getProperty("http.keepAlive", "true")
-    }
-
-    private HttpSettings getHttpSettings() {
-        return Stub(HttpSettings) {
-            getCredentials() >> Stub(PasswordCredentials)
-            getProxySettings() >> Stub(HttpProxySettings)
-        }
-    }
-}
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
deleted file mode 100644
index e68c664..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResourceListerTest.groovy
+++ /dev/null
@@ -1,45 +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.externalresource.transport.http
-
-import org.gradle.api.Transformer
-import spock.lang.Specification
-
-class HttpResourceListerTest extends Specification {
-
-    HttpResourceAccessor accessorMock = Mock()
-    HttpResponseResource externalResource = Mock()
-    HttpResourceLister lister = new HttpResourceLister(accessorMock)
-
-    def "consumeExternalResource closes resource after reading into stream"() {
-        setup:
-        accessorMock.getResource("http://testrepo/") >> externalResource;
-        when:
-        lister.list("http://testrepo/")
-        then:
-        1 * externalResource.withContent(_) >> {Transformer action -> return action.transform(new ByteArrayInputStream("<a href='child'/>".bytes))}
-        1 * externalResource.getContentType() >> "text/html"
-        1 * externalResource.close()
-    }
-
-    def "list returns null if HttpAccessor returns null"(){
-        setup:
-        accessorMock.getResource("http://testrepo/")  >> null
-        expect:
-        null == lister.list("http://testrepo")
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResourceTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResourceTest.groovy
deleted file mode 100644
index d93cfc5..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/HttpResponseResourceTest.groovy
+++ /dev/null
@@ -1,72 +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.externalresource.transport.http
-
-import org.apache.http.Header
-import org.apache.http.HttpHeaders
-import org.apache.http.HttpResponse
-import org.apache.http.message.BasicHeader
-import org.gradle.api.internal.externalresource.ExternalResource
-import spock.lang.Specification
-import org.apache.http.HttpEntity
-
-class HttpResponseResourceTest extends Specification {
-
-    def sourceUrl = "http://gradle.org"
-    def method = "GET"
-    def response = Mock(HttpResponse)
-
-    def "extracts etag"() {
-        given:
-        addHeader(HttpHeaders.ETAG, "abc")
-
-        expect:
-        resource().metaData.etag == "abc"
-    }
-
-    def "handles no etag"() {
-        expect:
-        resource().metaData.etag == null
-    }
-
-    def "is not openable more than once"() {
-        setup:
-        1 * response.entity >> Mock(HttpEntity)
-        when:
-        def resource = resource();
-        resource.openStream();
-        and:
-        resource.openStream()
-        then:
-        def ex = thrown(IOException);
-        ex.message == "Unable to open Stream as it was opened before."
-    }
-
-    ExternalResource resource() {
-        new HttpResponseResource(method, sourceUrl, response)
-    }
-
-    void addHeader(String name, String value) {
-        interaction {
-            1 * response.getFirstHeader(name) >> header(name, value)
-        }
-    }
-
-    Header header(String name, String value) {
-        new BasicHeader(name, value)
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy
deleted file mode 100644
index 10a3c85..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy
+++ /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.externalresource.transport.http;
-
-
-import spock.lang.Specification
-
-class JavaSystemPropertiesHttpProxySettingsTest extends Specification {
-    def "proxy is not configured when proxyHost property not set"() {
-        expect:
-        def settings = settings(proxyHost, proxyPort, nonProxyHosts)
-        settings.getProxy(requestHost) == null
-
-        where:
-        proxyHost | proxyPort | nonProxyHosts | requestHost
-        null      | null      | null          | null
-        null      | null      | null          | "foo"
-        null      | "111"     | null          | "foo"
-        null      | null      | "foo|bar|baz" | "foo"
-        ""        | null      | null          | null
-        ""        | ""        | null          | null
-        null      | ""        | null          | null
-    }
-
-    private JavaSystemPropertiesHttpProxySettings settings(host, proxyPort, nonProxyHosts) {
-        return new JavaSystemPropertiesHttpProxySettings(host, proxyPort, null, null, nonProxyHosts)
-    }
-
-    def "proxy is not configured when host is in list of nonproxy hosts"() {
-        expect:
-        settings("proxyHost", "111", nonProxyHosts).getProxy(host)?.host == proxyHost
-
-        where:
-        nonProxyHosts | host     | proxyHost
-        null          | "foo"    | "proxyHost"
-        ""            | "foo"    | "proxyHost"
-        "bar"         | "foo"    | "proxyHost"
-        "foo"         | "foo"    | null
-        "fo"          | "foo"    | "proxyHost"
-        "foo|bar|baz" | "foo"    | null
-        "foo.*"       | "foo.bar"| null
-        "*.bar"       | "foo.bar"| null
-        "*.ba"        | "foo.bar"| "proxyHost"
-        "*"           | "foo"    | null
-        "*"           | "foo"    | null
-        "foo.*|baz"   | "foo.bar"| null
-    }
-
-    def "uses specified port property and default port when port property not set or invalid"() {
-        expect:
-       settings("proxyHost", prop, null).getProxy("host").port == value
-
-        where:
-        prop     | value
-        null     | 80
-        ""       | 80
-        "notInt" | 80
-        "0"      | 0
-        "111"    | 111
-    }
-
-    def "uses specified proxy user and password"() {
-        expect:
-        def proxy = new JavaSystemPropertiesHttpProxySettings("proxyHost", null, user, password, null).getProxy("host")
-        proxy.credentials?.username == proxyUser
-        proxy.credentials?.password == proxyPassword
-
-        where:
-        user     | password  | proxyUser | proxyPassword
-        "user"   | "password"| "user"    | "password"
-        "user"   | ""        | "user"    | ""
-        ""       | "password"| null      | null
-        null     | "anything"| null      | null
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentialsTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentialsTest.groovy
deleted file mode 100644
index 626edeb..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/externalresource/transport/http/ntlm/NTLMCredentialsTest.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.externalresource.transport.http.ntlm;
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
-import spock.lang.Specification
-
-public class NTLMCredentialsTest extends Specification {
-    PasswordCredentials credentials = Mock()
-
-    @Rule
-    public SetSystemProperties systemProperties = new SetSystemProperties()
-
-    def "uses domain when encoded in username"() {
-        credentials.username >> "domain\\username"
-        credentials.password >> "password"
-
-        when:
-        def ntlmCredentials = new NTLMCredentials(credentials)
-
-        then:
-        ntlmCredentials.domain == 'DOMAIN'
-        ntlmCredentials.username == 'username'
-        ntlmCredentials.password == 'password'
-    }
-
-    def "uses domain when encoded in username with forward slash"() {
-        credentials.username >> "domain/username"
-        credentials.password >> "password"
-
-        when:
-        def ntlmCredentials = new NTLMCredentials(credentials)
-
-        then:
-        ntlmCredentials.domain == 'DOMAIN'
-        ntlmCredentials.username == 'username'
-        ntlmCredentials.password == 'password'
-    }
-
-    def "uses default domain when not encoded in username"() {
-        credentials.username >> "username"
-        credentials.password >> "password"
-
-        when:
-        def ntlmCredentials = new NTLMCredentials(credentials)
-
-        then:
-        ntlmCredentials.domain == ''
-        ntlmCredentials.username == 'username'
-        ntlmCredentials.password == 'password'
-    }
-
-    def "uses system property for domain when not encoded in username"() {
-        System.setProperty("http.auth.ntlm.domain", "domain")
-        credentials.username >> "username"
-        credentials.password >> "password"
-
-        when:
-        def ntlmCredentials = new NTLMCredentials(credentials)
-
-        then:
-        ntlmCredentials.domain == 'DOMAIN'
-        ntlmCredentials.username == 'username'
-        ntlmCredentials.password == 'password'
-    }
-
-    def "uses truncated hostname for workstation"() {
-        credentials.username >> "username"
-        credentials.password >> "password"
-
-        when:
-        def ntlmCredentials = new NTLMCredentials(credentials) {
-            protected String getHostName() {
-                return "hostname.domain.org"
-            }
-        }
-
-        then:
-        ntlmCredentials.workstation == 'HOSTNAME'
-    }
-}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParserTest.groovy b/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParserTest.groovy
deleted file mode 100755
index 56055f4..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationParserTest.groovy
+++ /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.notations
-
-import org.gradle.api.artifacts.SelfResolvingDependency
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.ClassPathRegistry
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
-import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
-import org.gradle.api.internal.file.FileResolver
-import spock.lang.Specification
-import org.gradle.internal.classpath.ClassPath
-
-public class DependencyClassPathNotationParserTest extends Specification {
-    def instantiator = Mock(Instantiator.class)
-    def classPathRegistry = Mock(ClassPathRegistry.class)
-    def fileResolver = Mock(FileResolver.class)
-    def DependencyClassPathNotationParser factory = new DependencyClassPathNotationParser(instantiator, classPathRegistry, fileResolver)
-
-    def "parses classpath literals"() {
-        given:
-        def dependency = Mock(SelfResolvingDependency.class)
-        def fileCollection = Mock(FileCollection.class)
-        def classpath = Mock(ClassPath.class)
-        def files = [new File('foo')]
-
-        and:
-        classPathRegistry.getClassPath('GRADLE_API') >> classpath
-        classpath.asFiles >> files
-        fileResolver.resolveFiles(files) >> fileCollection
-        instantiator.newInstance(DefaultSelfResolvingDependency.class, fileCollection as Object) >> dependency
-
-        when:
-        def out = factory.parseType(DependencyFactory.ClassPathNotation.GRADLE_API)
-
-        then:
-        out.is dependency
-    }
-}
-
-
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
deleted file mode 100644
index 8d71ab8..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationParserTest.groovy
+++ /dev/null
@@ -1,137 +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.notations
-
-import org.gradle.api.artifacts.DependencyArtifact
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.internal.reflect.DirectInstantiator
-import spock.lang.Specification
-
-public class DependencyMapNotationParserTest extends Specification {
-
-    def parser = new DependencyMapNotationParser<DefaultExternalModuleDependency>(new DirectInstantiator(), DefaultExternalModuleDependency.class);
-
-    def "with artifact"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name: 'gradle-core', version: '4.4-beta2', ext: 'mytype'])
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '4.4-beta2'
-
-        !d.force
-        !d.transitive
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == null && it.type == 'mytype' }
-    }
-
-    def "with classified artifact"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name: 'gradle-core', version: '10', ext: 'zip', classifier: 'jdk-1.4'])
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10'
-
-        !d.force
-        !d.transitive
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' && it.type == 'zip' }
-    }
-
-    def "with classifier"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'10', classifier:'jdk-1.4']);
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10'
-        d.transitive
-
-        !d.force
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' &&
-                it.type == DependencyArtifact.DEFAULT_TYPE && it.extension == DependencyArtifact.DEFAULT_TYPE }
-    }
-
-    def "with 3-element map"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0']);
-
-        then:
-        d.group == 'org.gradle'
-        d.name == 'gradle-core'
-        d.version == '1.0'
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with 3-element map and configuration"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0', configuration:'compile']);
-
-        then:
-        d.group == 'org.gradle'
-        d.name == 'gradle-core'
-        d.version == '1.0'
-        d.configuration == 'compile'
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with 3-element map and property"() {
-        when:
-        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0', transitive:false]);
-
-        then:
-        d.group == 'org.gradle'
-        d.name == 'gradle-core'
-        d.version == '1.0'
-
-        !d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with no group and no version"() {
-        when:
-        def d = parser.parseNotation([name:'foo'])
-
-        then:
-        d.group == null
-        d.name == 'foo'
-        d.version == null
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-}
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
deleted file mode 100644
index a550d19..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyNotationParserTest.groovy
+++ /dev/null
@@ -1,54 +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.notations;
-
-
-import org.gradle.api.GradleException
-import org.gradle.internal.typeconversion.NotationParser
-import spock.lang.Specification
-
-public class DependencyNotationParserTest extends Specification {
-
-    def notationParser = Mock(NotationParser)
-    def parser = new DependencyNotationParser(notationParser);
-
-    def "consumes notation and forwards gradle exception"() {
-        given:
-        def exc = new GradleException("Hey!")
-        notationParser.parseNotation("foo") >> { throw exc }
-
-        when:
-        parser.parseNotation("foo")
-
-        then:
-        def e = thrown(Exception)
-        exc == e
-    }
-
-    def "wraps programmer error with higher level exception"() {
-        given:
-        def exc = new RuntimeException("ka-boom!")
-        notationParser.parseNotation("foo") >> { throw exc }
-
-        when:
-        parser.parseNotation("foo")
-
-        then:
-        def e = thrown(GradleException)
-        exc == e.cause
-    }
-}
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
deleted file mode 100644
index a6c99b5..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationParserTest.groovy
+++ /dev/null
@@ -1,171 +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.notations
-
-import org.gradle.api.artifacts.DependencyArtifact
-import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-public class DependencyStringNotationParserTest extends Specification {
-
-    def parser = new DependencyStringNotationParser(new DirectInstantiator(), DefaultExternalModuleDependency.class);
-
-    def "with artifact"() {
-        when:
-        def d = parser.parseNotation('org.gradle:gradle-core:4.4-beta2 at mytype');
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '4.4-beta2'
-
-        !d.force
-        !d.transitive
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == null && it.type == 'mytype' }
-    }
-
-    def "with classified artifact"() {
-        when:
-        def d = parser.parseNotation('org.gradle:gradle-core:10:jdk-1.4 at zip');
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10'
-
-        !d.force
-        !d.transitive
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' && it.type == 'zip' }
-    }
-
-    def "with classifier"() {
-        when:
-        def d = parser.parseNotation('org.gradle:gradle-core:10:jdk-1.4');
-
-        then:
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10'
-        d.transitive
-
-        !d.force
-        !d.changing
-
-        d.artifacts.size() == 1
-        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' &&
-                it.type == DependencyArtifact.DEFAULT_TYPE && it.extension == DependencyArtifact.DEFAULT_TYPE }
-    }
-
-    def "with 3-element GString"() {
-        when:
-        def gstring = TestUtil.createScript("descriptor = 'org.gradle:gradle-core:1.0'; \"\$descriptor\"").run()
-        def d = parser.parseNotation(gstring);
-
-        then:
-        d.group == 'org.gradle'
-        d.name == 'gradle-core'
-        d.version == '1.0'
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with no group"() {
-        when:
-        def d = parser.parseNotation(":foo:1.0");
-
-        then:
-        d.group == null
-        d.name == 'foo'
-        d.version == '1.0'
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with no version"() {
-        when:
-        def d = parser.parseNotation("hey:foo:");
-
-        then:
-        d.group == 'hey'
-        d.name == 'foo'
-        d.version == null
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "with no version and no group"() {
-        when:
-        def d = parser.parseNotation(":foo:");
-
-        then:
-        d.group == null
-        d.name == 'foo'
-        d.version == null
-        d.transitive
-
-        !d.force
-        !d.changing
-    }
-
-    def "can create client module"() {
-        parser = new DependencyStringNotationParser(new DirectInstantiator(), DefaultClientModule);
-
-        when:
-        def d = parser.parseNotation('org.gradle:gradle-core:10')
-
-        then:
-        d instanceof DefaultClientModule
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10'
-        d.transitive
-
-        !d.force
-    }
-
-    def "client module ignores the artifact only notation"() {
-        parser = new DependencyStringNotationParser(new DirectInstantiator(), DefaultClientModule);
-
-        when:
-        def d = parser.parseNotation('org.gradle:gradle-core:10 at jar')
-
-        then:
-        d instanceof DefaultClientModule
-        d.name == 'gradle-core'
-        d.group == 'org.gradle'
-        d.version == '10 at jar'
-        d.transitive
-
-        !d.force
-        d.artifacts.size() == 0
-    }
-}
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
deleted file mode 100644
index 32a6d94..0000000
--- a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.notations;
-
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.initialization.ProjectAccessListener
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.util.GUtil
-import spock.lang.Specification
-
-public class ProjectDependencyFactoryTest extends Specification {
-
-    def projectDummy = Mock(ProjectInternal)
-    def projectFinder = Mock(ProjectFinder)
-
-    def depFactory = new DefaultProjectDependencyFactory(Mock(ProjectAccessListener), new DirectInstantiator(), true)
-    def factory = new ProjectDependencyFactory(depFactory)
-
-    def "creates project dependency with map notation"() {
-        given:
-        boolean expectedTransitive = false;
-        final Map<String, Object> mapNotation = GUtil.map("path", ":foo:bar", "configuration", "compile", "transitive", expectedTransitive);
-
-        and:
-        projectFinder.getProject(':foo:bar') >> projectDummy
-
-        when:
-        def projectDependency = factory.createFromMap(projectFinder, mapNotation);
-
-        then:
-        projectDependency.getDependencyProject() == projectDummy
-        projectDependency.getConfiguration() == "compile"
-        projectDependency.isTransitive() == expectedTransitive
-    }
-
-    def "fails with decent message if provided map is invalid"() {
-        given:
-        projectFinder.getProject(':foo:bar') >> projectDummy
-
-        when:
-        factory.createFromMap(projectFinder, GUtil.map("paths", ":foo:bar"));
-
-        then:
-        def ex = thrown(InvalidUserDataException)
-        ex.message.contains("Required keys [path] are missing from map")
-    }
-}
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
deleted file mode 100644
index dbc6812..0000000
--- a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!--
-   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="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
-	<info organisation="myorg"
-	       module="mymodule"
-	       revision="myrev"
-	       status="integration"
-	       publication="20041101110000"
-	       e:attr1="value1">
-	       
-		<license name="MyLicense" url="http://www.my.org/mymodule/mylicense.html"/>
-		<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.
-		</description>
-
-		<e:someExtra>56576</e:someExtra>
-	</info>
-	<configurations>
-		<conf name="myconf1" description="desc 1" e:attr2="value2"/>
-		<conf name="myconf2" description="desc 2" visibility="public"/>
-		<conf name="myconf3" description="desc 3" visibility="private"/>
-		<conf name="myconf4" description="desc 4" extends="myconf1, myconf2"/>		
-		<conf name="myoldconf" description="my old desc" deprecated="20050115"/>
-	</configurations>
-	<publications>
-		<artifact name="myartifact1" type="jar" e:attr3="value3"/>
-		<artifact name="myartifact2" type="jar" conf="myconf1"/>
-		<artifact name="myartifact3" type="jar" conf="myconf1, myconf2, myconf3"/>
-		<artifact name="myartifact4" type="jar">
-			<conf name="myconf1"/>
-			<conf name="myconf3"/>
-		</artifact>
-	</publications>
-	<dependencies>
-		<dependency name="mymodule2" rev="2.0" e:attr4="value4"/>
-		<dependency name="mymodule3" rev="2.0" changing="true" transitive="false"/>
-		<dependency org="yourorg" name="yourmodule1" branch="trunk" rev="1.1" branchConstraint="branch1" revConstraint="1+" conf="myconf1"/>
-		<dependency org="yourorg" name="yourmodule2" rev="2+" conf="myconf1->yourconf1"/>
-		<dependency org="yourorg" name="yourmodule3" rev="3.1" conf="myconf1->yourconf1, yourconf2"/>
-		<dependency org="yourorg" name="yourmodule4" rev="4.1" conf="myconf1, myconf2->yourconf1, yourconf2"/>
-		<dependency org="yourorg" name="yourmodule5" rev="5.1" conf="myconf1->yourconf1;myconf2->yourconf1, yourconf2"/>
-
-		<dependency org="yourorg" name="yourmodule6" rev="latest.integration">
-			<conf name="myconf1" mapped="yourconf1"/>
-			<conf name="myconf2" mapped="yourconf1, yourconf2"/>
-		</dependency>
-
-		<dependency org="yourorg" name="yourmodule7" rev="7.1">
-			<conf name="myconf1">
-				<mapped name="yourconf1"/>
-			</conf>
-			<conf name="myconf2">
-				<mapped name="yourconf1"/>
-				<mapped name="yourconf2"/>
-			</conf>
-		</dependency>
-
-		<dependency org="yourorg" name="yourmodule8" rev="8.1">
-			<artifact name="yourartifact8-1" type="jar" e:attr5="value5"/>
-			<artifact name="yourartifact8-2" type="jar"/>
-		</dependency>		
-
-		<dependency org="yourorg" name="yourmodule9" rev="9.1" conf="myconf1,myconf2,myconf3->default">
-			<artifact name="yourartifact9-1" type="jar" conf="myconf1,myconf2"/>
-			<artifact name="yourartifact9-2" type="jar">
-				<conf name="myconf2"/>
-				<conf name="myconf3"/>
-			</artifact>
-		</dependency>		
-
-		<dependency org="yourorg" name="yourmodule10" rev="10.1">
-			<include name="your.*" type="jar"/>
-			<include ext="xml"/>
-			<exclude name="toexclude"/>
-		</dependency>
-		<dependency org="yourorg" name="yourmodule11" rev="11.1" conf="*->@"/>
-		
-		<exclude module="*servlet*" matcher="glob" conf="myconf1" /> 
-		<exclude org="acme" module="test" artifact="test" type="source" ext="jar" />
-        <override org="yourorg" module=".*1" matcher="regexp" branch="BRANCH" rev="1.0" /> 
-		<conflict org="yourorg" module=".*" matcher="regexp" manager="all" />
-		<conflict org="theirorg" module="theirmodule1" rev="1.0, 1.1"/>
-	</dependencies>
-</ivy-module>
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
deleted file mode 100644
index 81409ed..0000000
--- a/subprojects/core-impl/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
+++ /dev/null
@@ -1,48 +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.result
-
-import org.gradle.api.artifacts.component.ComponentSelector
-import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-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
-
-class ResolutionResultDataBuilder {
-
-    static DefaultResolvedDependencyResult newDependency(String group='a', String module='a', String version='1', String selectedVersion='1') {
-        new DefaultResolvedDependencyResult(DefaultModuleComponentSelector.newSelector(group, module, version), newModule(group, module, selectedVersion), newModule())
-    }
-
-    static DefaultUnresolvedDependencyResult newUnresolvedDependency(String group='x', String module='x', String version='1', String selectedVersion='1') {
-        def requested = DefaultModuleComponentSelector.newSelector(group, module, version)
-        new DefaultUnresolvedDependencyResult(requested, VersionSelectionReasons.REQUESTED, newModule(group, module, selectedVersion), new ModuleVersionResolveException(newSelector(group, module, version), "broken"))
-    }
-
-    static DefaultResolvedComponentResult newModule(String group='a', String module='a', String version='1',
-                                                        ComponentSelectionReason selectionReason = VersionSelectionReasons.REQUESTED) {
-        new DefaultResolvedComponentResult(newId(group, module, version), selectionReason, new DefaultModuleComponentIdentifier(group, module, version))
-    }
-
-    static DefaultResolvedDependencyResult newDependency(ComponentSelector componentSelector, String group='a', String module='a', String selectedVersion='1') {
-        new DefaultResolvedDependencyResult(componentSelector, newModule(group, module, selectedVersion), newModule())
-    }
-}
diff --git a/subprojects/core/core.gradle b/subprojects/core/core.gradle
index 4a9667d..6b35120 100755
--- a/subprojects/core/core.gradle
+++ b/subprojects/core/core.gradle
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-apply from: "$rootDir/gradle/providedConfiguration.gradle"
-
 configurations {
     reports
 }
@@ -25,18 +23,19 @@ dependencies {
 
     publishCompile libraries.slf4j_api
     publishCompile project(":baseServices")
+    publishCompile project(":baseServicesGroovy"), {
+        exclude group: "org.codehaus.groovy", module: "groovy-all"
+    }
     publishCompile project(":messaging")
+    publishCompile project(":resources")
 
-    compile project(":baseServicesGroovy")
-    compile project(":resources")
+    compile project(":modelCore")
+    compile project(":modelGroovy")
     compile libraries.asm
     compile libraries.ant
     compile libraries.commons_collections
     compile libraries.commons_io
     compile libraries.commons_lang
-    compile libraries.ivy
-    compile libraries.logback_core
-    compile libraries.logback_classic
     compile libraries.guava
     compile libraries.jcip
     compile libraries.jul_to_slf4j
@@ -48,17 +47,6 @@ dependencies {
 
     runtime project(":docs")
 
-    compile(group: 'com.jfrog.bintray.client', name: 'bintray-client-java-impl', version: '0.1.0') {
-        exclude module:'groovy-all'
-        exclude module:'groovy'
-        exclude group: 'org.slf4j'
-        exclude module: 'commons-collections'
-        exclude module: 'commons-lang'
-        exclude module: 'httpclient'
-        exclude module: 'nekohtml'
-    }
-    compile libraries.commons_httpclient // Needed by bintray client
-
     runtime libraries.log4j_to_slf4j
     runtime libraries.jcl_to_slf4j
 
@@ -70,15 +58,23 @@ dependencies {
     testRuntime project(":diagnostics")
 
     testFixturesCompile project(":internalTesting")
-    testFixturesRuntime project(':coreImpl')
+    testFixturesCompile libraries.ivy
+
+    testFixturesRuntime project(':dependencyManagement')
+    testFixturesRuntime project(':pluginUse')
 
     integTestCompile project(":internalIntegTesting")
 
-    reports 'css3-pie:css3-pie:1.0beta3'
+    integTestRuntime project(":plugins")
 }
 
 useTestFixtures()
 useTestFixtures(project: ":messaging")
+useTestFixtures(project: ":modelCore")
+
+test {
+    forkEvery = 200
+}
 
 [compileGroovy, compileTestGroovy]*.groovyOptions*.fork(memoryInitialSize: '128M', memoryMaximumSize: '1G')
 
@@ -89,12 +85,6 @@ task buildReceiptResource(type: Copy, dependsOn: rootProject.createBuildReceipt)
 
 sourceSets.main.output.dir generatedResourcesDir, builtBy: buildReceiptResource
 
-task reportResources(type: Copy) {
-    from configurations.reports
-    into "$generatedResourcesDir/org/gradle/reporting"
-}
-sourceSets.main.output.dir generatedResourcesDir, builtBy: reportResources
-
 task pluginsManifest(type: PluginsManifest)
 sourceSets.main.output.dir generatedResourcesDir, builtBy: pluginsManifest
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
index 031ee8d..35a3997 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
@@ -28,10 +28,6 @@ task broken(type: DeprecatedTask) {
     otherFeature()
 }
 
-repositories {
-    mavenRepo url: 'build/repo'
-}
-
 def someFeature() {
     DeprecationLogger.nagUserOfDiscontinuedMethod("someFeature()")
 }
@@ -52,8 +48,6 @@ class DeprecatedTask extends DefaultTask {
         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:
@@ -65,8 +59,6 @@ class DeprecatedTask extends DefaultTask {
         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:
@@ -76,7 +68,6 @@ class DeprecatedTask extends DefaultTask {
         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 == ""
     }
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy
index e3f3daa..e602483 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ApplyPluginIntegSpec.groovy
@@ -89,17 +89,12 @@ class ApplyPluginIntegSpec extends AbstractIntegrationSpec {
             package com.acme
 import org.gradle.api.Project
 import org.gradle.testfixtures.ProjectBuilder
-import spock.lang.*
+import org.junit.Test
 
-class BreakingTest extends Specification {
-  Project project
-
-  def setup() {
-    project = ProjectBuilder.builder().build()
-  }
-
-  def "can evaluate ProjectBuilder"() {
-    expect:
+class BreakingTest {
+  @Test
+  void "can evaluate ProjectBuilder"() {
+    def project = ProjectBuilder.builder().build()
     project.apply(plugin: 'groovy')
     project.evaluate()
   }
@@ -117,9 +112,6 @@ class BreakingTest extends Specification {
             dependencies {
                 compile gradleApi()
                 compile localGroovy()
-                testCompile "org.spockframework:spock-core:0.7-groovy-1.8", {
-                    exclude module: "groovy-all"
-                }
             }
         '''
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationSpec.groovy
new file mode 100644
index 0000000..66f9fce
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationSpec.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 BuildScriptExecutionIntegrationSpec extends AbstractIntegrationSpec {
+
+    def "build scripts must be encoded using utf-8"() {
+        given:
+        executer.withDefaultCharacterEncoding("ISO-8859-15")
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert java.nio.charset.Charset.defaultCharset().name() == "ISO-8859-15"
+    // embed a euro character in the text - this is encoded differently in ISO-8859-12 and UTF-8
+    assert '\u20AC'.charAt(0) == 0x20AC
+}
+""", "UTF-8")
+        assert file('build.gradle').getText("ISO-8859-15") != file('build.gradle').getText("UTF-8")
+        expect:
+        succeeds 'check'
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..267fafc
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/BuildScriptExecutionIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.api
+
+import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Test
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.not
+import static org.junit.Assert.assertThat
+
+class BuildScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+
+    @Test
+    public void executesBuildScriptWithCorrectEnvironment() {
+        def implClassName = 'com.google.common.collect.Multimap'
+        TestFile buildScript = testFile('build.gradle')
+        buildScript << """
+println 'quiet message'
+logging.captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert project != null
+assert "${buildScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
+assert "${buildScript.toURI()}" == buildscript.sourceURI as String
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+Gradle.class.classLoader.loadClass('${implClassName}')
+try {
+    buildscript.classLoader.loadClass('${implClassName}')
+    assert false: 'should fail'
+} catch (ClassNotFoundException e) {
+    // expected
+}
+
+            task doStuff
+"""
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void buildScriptCanContainATaskDefinition() {
+        testFile('build.gradle') << '''
+            task t(type: SomeTask)
+
+            class SomeTask extends DefaultTask {
+            }
+'''
+
+        inTestDirectory().withTaskList().run()
+    }
+
+    @Test
+    public void buildScriptCanContainOnlyClassDefinitions() {
+        testFile('build.gradle') << '''
+            class TestComparable implements Comparable<TestComparable>, SomeInterface {
+                int compareTo(TestComparable t) {
+                    return 0
+                }
+                void main() { }
+            }
+
+            interface SomeInterface {
+                void main()
+            }
+'''
+
+        inTestDirectory().withTaskList().run()
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
index 41e4b21..3a47572 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
@@ -156,7 +156,7 @@ project(':api') {
         fixture.assertProjectsConfigured(":", ":api", ':impl')
     }
 
-    def "follows project dependencies when ran in subproject"() {
+    def "follows project dependencies when run in subproject"() {
         settingsFile << "include 'api', 'impl', 'util'"
 
         file("api/build.gradle") << "configurations { api }"
@@ -331,4 +331,177 @@ project(':api') {
         then:
         fixture.assertProjectsConfigured(":", ":a", ":b")
     }
+
+    def "handles buildNeeded"() {
+        settingsFile << "include 'a', 'b', 'c'"
+        file("a/build.gradle") << """ apply plugin: 'java' """
+        file("b/build.gradle") << """
+            apply plugin: 'java'
+            project(':b') {
+                dependencies { compile project(':a') }
+            }
+        """
+
+        when:
+        run(":b:buildNeeded")
+
+        then:
+        result.executedTasks.containsAll ':b:buildNeeded', ':a:buildNeeded'
+        fixture.assertProjectsConfigured(":", ":b", ":a")
+    }
+
+    def "handles buildDependents"() {
+        settingsFile << "include 'a', 'b', 'c'"
+        file("a/build.gradle") << """ apply plugin: 'java' """
+        file("b/build.gradle") << """
+            apply plugin: 'java'
+            project(':b') {
+                dependencies { compile project(':a') }
+            }
+        """
+
+        when:
+        run(":a:buildDependents")
+
+        then:
+        result.executedTasks.containsAll ':b:buildDependents', ':a:buildDependents'
+        //unfortunately buildDependents requires all projects to be configured
+        fixture.assertProjectsConfigured(":", ":a", ":b", ":c")
+    }
+
+    def "task command-line argument may look like a task path"() {
+        settingsFile << "include 'a', 'b', 'c'"
+        file("a/build.gradle") << """
+task one(type: SomeTask)
+task two(type: SomeTask)
+
+class SomeTask extends DefaultTask {
+    @org.gradle.api.internal.tasks.options.Option(description="some value")
+    String value
+}
+"""
+
+        when:
+        run(":a:one", "--value", ":b:thing", "a:two", "--value", "unknown:unknown")
+
+        then:
+        result.assertTasksExecuted(":a:one", ":a:two")
+        fixture.assertProjectsConfigured(":", ":a")
+    }
+
+    def "does not configure all projects when excluded task path is not qualified and is exact match for task in default project"() {
+        settingsFile << "include 'a', 'a:child', 'b', 'b:child', 'c'"
+        file('a').mkdirs()
+        file('b').mkdirs()
+        buildFile << """
+allprojects {
+    task one
+    task two
+    task three
+}
+"""
+
+        when:
+        run(":a:one", "-x", "two", "-x", "three")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":a")
+
+        when:
+        executer.usingProjectDirectory(file('a'))
+        run(":a:one", "-x", "two", "-x", "three")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":a")
+
+        when:
+        executer.usingProjectDirectory(file('b'))
+        run(":a:one", "-x", "two", "-x", "three")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":b", ":a")
+    }
+
+    def "does not configure all projects when excluded task path is not qualified and an exact match for task has already been seen in some sub-project of default project"() {
+        settingsFile << "include 'a', 'b', 'c', 'c:child'"
+        file('c').mkdirs()
+        buildFile << """
+allprojects {
+    task one
+}
+project(':b') {
+    task two
+}
+"""
+
+        when:
+        run(":a:one", "-x", "two")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":a")
+
+        when:
+        executer.usingProjectDirectory(file("c"))
+        runAndFail(":a:one", "-x", "two")
+
+        then:
+        failure.assertHasDescription("Task 'two' not found in project ':c'.")
+        fixture.assertProjectsConfigured(":", ":c", ':c:child')
+    }
+
+    def "configures all subprojects of default project when excluded task path is not qualified and an exact match not found in default project"() {
+        settingsFile << "include 'a', 'b', 'c', 'c:child'"
+        file('c').mkdirs()
+        buildFile << """
+allprojects {
+    task one
+}
+"""
+        file("b/build.gradle") << "task two"
+
+        when:
+        run(":a:one", "-x", "two")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":a", ":b", ":c", ":c:child")
+
+        when:
+        executer.usingProjectDirectory(file("c"))
+        runAndFail(":a:one", "-x", "two")
+
+        then:
+        failure.assertHasDescription("Task 'two' not found in project ':c'.")
+        fixture.assertProjectsConfigured(":", ":c", ':c:child')
+    }
+
+    def "configures all subprojects of default projects when excluded task path is not qualified and uses camel case matching"() {
+        settingsFile << "include 'a', 'b', 'b:child', 'c'"
+        file('b').mkdirs()
+        buildFile << """
+allprojects {
+    task one
+    task two
+}
+"""
+
+        when:
+        run(":a:one", "-x", "tw")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":a", ":b", ":c", ":b:child")
+
+        when:
+        executer.usingProjectDirectory(file('b'))
+        run(":a:one", "-x", "tw")
+
+        then:
+        result.assertTasksExecuted(":a:one")
+        fixture.assertProjectsConfigured(":", ":b", ":b:child", ":a")
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy
index f70c11a..87b4615 100755
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/DeferredConfigurableExtensionIntegrationTest.groovy
@@ -30,7 +30,7 @@ public class CustomPlugin implements Plugin<Project> {
 
 public class BrokenCustomPlugin implements Plugin<Project> {
     public void apply(Project project) {
-        project.getPlugins().apply(CustomPlugin)
+        project.pluginManager.apply(CustomPlugin)
         project.getExtensions().configure(CustomExtension, {
             throw new RuntimeException("broken configuration in plugin")
         } as Action)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationSpec.groovy
new file mode 100644
index 0000000..d0c2c8d
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationSpec.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.server.http.HttpServer
+
+class ExternalScriptExecutionIntegrationSpec extends AbstractIntegrationSpec {
+    @org.junit.Rule
+    public final HttpServer server = new HttpServer()
+
+    def "uses encoding specified by http server"() {
+        given:
+        executer.withDefaultCharacterEncoding("UTF-8")
+        server.start()
+
+        and:
+        def scriptFile = file("script.gradle")
+        scriptFile.setText("""
+task check << {
+    assert java.nio.charset.Charset.defaultCharset().name() == "UTF-8"
+    // embed a euro character in the text - this is encoded differently in ISO-8859-15 and UTF-8
+    assert '\u20AC'.charAt(0) == 0x20AC
+}
+""", "ISO-8859-15")
+        assert scriptFile.getText("ISO-8859-15") != scriptFile.getText("UTF-8")
+        server.expectGet('/script.gradle', scriptFile).contentType("text/plain; charset=ISO-8859-15")
+
+        and:
+        buildFile << "apply from: 'http://localhost:${server.port}/script.gradle'"
+
+        expect:
+        succeeds 'check'
+    }
+
+    def "assumes utf-8 encoding when none specified by http server"() {
+        given:
+        executer.withDefaultCharacterEncoding("ISO-8859-15")
+        server.start()
+
+        and:
+        def scriptFile = file("script.gradle")
+        scriptFile.setText("""
+task check << {
+    assert java.nio.charset.Charset.defaultCharset().name() == "ISO-8859-15"
+    // embed a euro character in the text - this is encoded differently in ISO-8859-15 and UTF-8
+    assert '\u20AC'.charAt(0) == 0x20AC
+}
+""", "UTF-8")
+        assert scriptFile.getText("ISO-8859-15") != scriptFile.getText("UTF-8")
+        server.expectGet('/script.gradle', scriptFile).contentType("text/plain")
+
+        and:
+        buildFile << "apply from: 'http://localhost:${server.port}/script.gradle'"
+
+        expect:
+        succeeds 'check'
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationTest.groovy
new file mode 100755
index 0000000..8db5110
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ExternalScriptExecutionIntegrationTest.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
+
+import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.executer.ArtifactBuilder
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.matchers.UserAgentMatcher
+import org.gradle.util.GradleVersion
+import org.junit.Rule
+import org.junit.Test
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.not
+import static org.junit.Assert.assertThat
+
+public class ExternalScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final HttpServer server = new HttpServer()
+
+    @Test
+    public void executesExternalScriptAgainstAProjectWithCorrectEnvironment() {
+        createExternalJar()
+        createBuildSrc()
+
+        def implClassName = 'com.google.common.collect.Multimap'
+        TestFile externalScript = testFile('external.gradle')
+        externalScript << """
+buildscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+new BuildSrcClass()
+println 'quiet message'
+logging.captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert project != null
+assert "${externalScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
+assert "${externalScript.toURI()}" == buildscript.sourceURI as String
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+assert project.buildscript.classLoader != buildscript.classLoader
+Gradle.class.classLoader.loadClass('${implClassName}')
+try {
+    buildscript.classLoader.loadClass('${implClassName}')
+    assert false: 'should fail'
+} catch (ClassNotFoundException e) {
+    // expected
+}
+
+task doStuff
+ext.someProp = 'value'
+"""
+        testFile('build.gradle') << '''
+apply { from 'external.gradle' }
+assert 'value' == someProp
+'''
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void canExecuteExternalScriptAgainstAnArbitraryObject() {
+        createBuildSrc()
+
+        testFile('external.gradle') << '''
+println 'quiet message'
+getLogging().captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+new BuildSrcClass()
+assert 'doStuff' == name
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+ext.someProp = 'value'
+'''
+        testFile('build.gradle') << '''
+task doStuff
+apply {
+    to doStuff
+    from 'external.gradle'
+}
+assert 'value' == doStuff.someProp
+'''
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromSettingsScript() {
+        testFile('settings.gradle') << ''' apply { from 'other.gradle' } '''
+        testFile('other.gradle') << ''' include 'child' '''
+        testFile('build.gradle') << ''' assert ['child'] == subprojects*.name '''
+
+        inTestDirectory().withTaskList().run()
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromInitScript() {
+        TestFile initScript = testFile('init.gradle') << ''' apply { from 'other.gradle' } '''
+        testFile('other.gradle') << '''
+addListener(new ListenerImpl())
+class ListenerImpl extends BuildAdapter {
+    public void projectsEvaluated(Gradle gradle) {
+        gradle.rootProject.task('doStuff')
+    }
+}
+'''
+        inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
+    }
+
+    @Test
+    public void canExecuteExternalScriptFromExternalScript() {
+        testFile('build.gradle') << ''' apply { from 'other1.gradle' } '''
+        testFile('other1.gradle') << ''' apply { from 'other2.gradle' } '''
+        testFile('other2.gradle') << ''' task doStuff '''
+
+        inTestDirectory().withTasks('doStuff').run()
+    }
+
+    @Test
+    public void canFetchScriptViaHttp() {
+        TestFile script = testFile('external.gradle')
+        server.expectUserAgent(UserAgentMatcher.matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
+        server.expectGet('/external.gradle', script)
+        server.start()
+
+        script << """
+            task doStuff
+            assert buildscript.sourceFile == null
+            assert "http://localhost:$server.port/external.gradle" == buildscript.sourceURI as String
+"""
+
+        testFile('build.gradle') << """
+            apply from: 'http://localhost:$server.port/external.gradle'
+            defaultTasks 'doStuff'
+"""
+
+        inTestDirectory().run()
+    }
+
+    @Test
+    public void cachesScriptClassForAGivenScript() {
+        testFile('settings.gradle') << 'include \'a\', \'b\''
+        testFile('external.gradle') << 'ext.appliedScript = this'
+        testFile('build.gradle') << '''
+allprojects {
+   apply from: "$rootDir/external.gradle"
+}
+subprojects {
+    assert appliedScript.class == rootProject.appliedScript.class
+}
+task doStuff
+'''
+        inTestDirectory().withTasks('doStuff').run()
+    }
+
+    private TestFile createBuildSrc() {
+        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
+            public class BuildSrcClass { }
+'''
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(testFile("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy
index f9806f4..6fac912 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/FinalizerTaskIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.TextUtil
 import spock.lang.Ignore
 import spock.lang.Unroll
 
@@ -183,13 +182,13 @@ class FinalizerTaskIntegrationTest extends AbstractIntegrationSpec {
         fails 'a'
 
         then:
-        failure.assertHasDescription TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+        failure.assertHasDescription """Circular dependency between the following tasks:
 :a
 \\--- :c
      \\--- :b
           \\--- :a (*)
 
-(*) - details omitted (listed previously)""")
+(*) - details omitted (listed previously)"""
     }
 
     void 'finalizer task can be used by multiple tasks that depend on one another'(){
@@ -213,6 +212,8 @@ class FinalizerTaskIntegrationTest extends AbstractIntegrationSpec {
 
     private void setupProject() {
         buildFile << """
+            class NotParallel extends DefaultTask {}
+
             task a {
                 finalizedBy 'b'
                 dependsOn 'c'
@@ -220,8 +221,8 @@ class FinalizerTaskIntegrationTest extends AbstractIntegrationSpec {
             task b {
                 dependsOn 'd'
             }
-            task c
-            task d
+            task c(type: NotParallel)
+            task d(type: NotParallel)
         """
     }
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..ee98407
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/InitScriptExecutionIntegrationTest.groovy
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.ArtifactBuilder
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+
+class InitScriptExecutionIntegrationTest extends AbstractIntegrationSpec {
+    def "executes init.gradle from user home dir"() {
+        given:
+        executer.requireOwnGradleUserHomeDir()
+
+        and:
+        executer.gradleUserHomeDir.file('init.gradle') << 'println "greetings from user home"'
+
+        when:
+        run()
+
+        then:
+        output.contains("greetings from user home")
+    }
+
+    def "executes init scripts from init.d directory in user home dir in alphabetical order"() {
+        given:
+        executer.requireOwnGradleUserHomeDir()
+
+        and:
+        executer.gradleUserHomeDir.file('init.d/a.gradle') << 'println "init #a#"'
+        executer.gradleUserHomeDir.file('init.d/b.gradle') << 'println "init #b#"'
+        executer.gradleUserHomeDir.file('init.d/c.gradle') << 'println "init #c#"'
+
+        when:
+        run()
+
+        then:
+        def a = output.indexOf('init #a#')
+        def b = output.indexOf('init #b#')
+        def c = output.indexOf('init #c#')
+        a < b
+        b < c
+    }
+
+    def "executes init script with correct environment"() {
+        given:
+        def implClassName = 'com.google.common.collect.Multimap'
+        createExternalJar();
+
+        and:
+        TestFile initScript = file('init.gradle')
+        initScript << """
+initscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+println 'quiet message'
+logging.captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert gradle != null
+assert initscript.classLoader == getClass().classLoader.parent
+assert initscript.classLoader == Thread.currentThread().contextClassLoader
+Gradle.class.classLoader.loadClass('${implClassName}')
+try {
+    initscript.classLoader.loadClass('${implClassName}')
+    assert false: 'should fail'
+} catch (ClassNotFoundException e) {
+    // expected
+}
+"""
+
+        and:
+        buildFile << 'task doStuff'
+
+        when:
+        ExecutionResult result = executer.usingInitScript(initScript).withTasks('doStuff').run()
+
+        then:
+        result.output.contains('quiet message')
+        !result.output.contains('error message')
+        result.error.contains('error message')
+        !result.error.contains('quiet message')
+    }
+
+    def "each init script has independent ClassLoader"() {
+        given:
+        createExternalJar()
+
+        and:
+        TestFile initScript1 = file('init1.gradle')
+        initScript1 << '''
+initscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+'''
+        TestFile initScript2 = file('init2.gradle')
+        initScript2 << '''
+try {
+    Class.forName('org.gradle.test.BuildClass')
+    fail()
+} catch (ClassNotFoundException e) {
+}
+'''
+
+        buildFile << 'task doStuff'
+
+        when:
+        executer.usingInitScript(initScript1).usingInitScript(initScript2)
+
+        then:
+        notThrown(Throwable)
+    }
+
+    def "init script can inject configuration into the root project and all projects"() {
+        given:
+        settingsFile << "include 'a', 'b'"
+
+        and:
+        file("init.gradle") << """
+allprojects {
+    task worker
+}
+rootProject {
+    task root(dependsOn: allprojects*.worker)
+}
+        """
+
+        when:
+        executer.withArguments("-I", "init.gradle")
+        run "root"
+
+        then:
+        result.assertTasksExecuted(':worker', ':a:worker', ':b:worker', ':root')
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(file("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/PluginApplicationErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/PluginApplicationErrorIntegrationTest.groovy
new file mode 100644
index 0000000..80227a0
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/PluginApplicationErrorIntegrationTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.plugin.PluginBuilder
+
+class PluginApplicationErrorIntegrationTest extends AbstractIntegrationSpec {
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    def "reports failure to apply plugin by id"() {
+        given:
+        pluginBuilder.addPlugin("throw new Exception('throwing plugin')", "broken")
+        pluginBuilder.publishTo(executer, file('external.jar'))
+
+        buildFile << '''
+buildscript {
+    dependencies {
+        classpath files('external.jar')
+    }
+}
+apply plugin: 'broken'
+'''
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasCause("Failed to apply plugin [id 'broken']")
+        failure.assertHasCause("throwing plugin")
+    }
+
+    def "reports failure to apply plugin by type"() {
+        buildFile << '''
+apply plugin: BrokenPlugin
+
+class BrokenPlugin implements Plugin<Project> {
+    void apply(Project target) {
+        throw new Exception('throwing plugin')
+    }
+}
+'''
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasCause("Failed to apply plugin [class 'BrokenPlugin']")
+        failure.assertHasCause("throwing plugin")
+    }
+
+    def "cannot apply a plugin that does not implement Plugin and does not extend RuleSource"() {
+        buildFile << '''
+apply plugin: BrokenPlugin
+
+class BrokenPlugin {
+    void apply(Project target) {
+    }
+}
+'''
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasCause("Failed to apply plugin [class 'BrokenPlugin']")
+        failure.assertHasCause("'BrokenPlugin' is neither a plugin or a rule source and cannot be applied.")
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..5245365
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptExecutionIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * 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
+
+import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.executer.ArtifactBuilder
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Test
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.not
+import static org.junit.Assert.assertThat
+
+class SettingsScriptExecutionIntegrationTest extends AbstractIntegrationTest {
+    @Test
+    public void executesSettingsScriptWithCorrectEnvironment() {
+        createExternalJar()
+        createBuildSrc()
+        def implClassName = 'com.google.common.collect.Multimap'
+
+        testFile('settings.gradle') << """
+buildscript {
+    dependencies { classpath files('repo/test-1.3.jar') }
+}
+new org.gradle.test.BuildClass()
+new BuildSrcClass();
+println 'quiet message'
+logging.captureStandardOutput(LogLevel.ERROR)
+println 'error message'
+assert settings != null
+assert buildscript.classLoader == getClass().classLoader.parent
+assert buildscript.classLoader == Thread.currentThread().contextClassLoader
+Gradle.class.classLoader.loadClass('${implClassName}')
+try {
+    buildscript.classLoader.loadClass('${implClassName}')
+    assert false: 'should fail'
+} catch (ClassNotFoundException e) {
+    // expected
+}
+"""
+        testFile('build.gradle') << 'task doStuff'
+
+        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
+        assertThat(result.output, containsString('quiet message'))
+        assertThat(result.output, not(containsString('error message')))
+        assertThat(result.error, containsString('error message'))
+        assertThat(result.error, not(containsString('quiet message')))
+    }
+
+    private TestFile createBuildSrc() {
+        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
+            public class BuildSrcClass { }
+'''
+    }
+
+    private def createExternalJar() {
+        ArtifactBuilder builder = artifactBuilder();
+        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
+            package org.gradle.test;
+            public class BuildClass { }
+'''
+        builder.buildJar(testFile("repo/test-1.3.jar"))
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy
index 836b0b8..d8bbcef 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/ConcurrentClassDecorationSpec.groovy
@@ -23,7 +23,7 @@ import spock.lang.Issue
 
 class ConcurrentClassDecorationSpec extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2836")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2836")
     def "can decorate classes concurrently"() {
         given:
         file("buildSrc/src/main/java/Thing.java") << "public class Thing {}"
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/DynamicObjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/DynamicObjectIntegrationTest.groovy
index ee9788c..ab940d2 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/DynamicObjectIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/DynamicObjectIntegrationTest.groovy
@@ -447,9 +447,9 @@ assert 'overridden value' == global
         executer.withTasks("run").run()
     }
 
-    @Test void warnsWhenNewPropertiesAreAddedDirectlyOnTargetObject() {
-        
-        file("build.gradle") << """
+    @Test void failsWhenNewPropertiesAreAddedDirectlyOnTargetObject() {
+        file('settings.gradle') << "rootProject.name = 'test'"
+        buildFile << """
             assert !hasProperty("p1")
 
             p1 = 1
@@ -465,12 +465,8 @@ assert 'overridden value' == global
             }
         """
 
-        executer.withDeprecationChecksDisabled()
-        def result = executer.withTasks("run").run()
-
-        assert result.output.contains('Creating properties on demand (a.k.a. dynamic properties) has been deprecated')
-        assert result.output.contains('Deprecated dynamic property: "p1" on "root project ')
-        assert result.output.contains('Deprecated dynamic property: "p2" on "task \':run\'", value: "2".')
+        def result = executer.withTasks("run").runWithFailure()
+        result.assertHasCause("No such property: p1 for class: org.gradle.api.internal.project.DefaultProject_Decorated")
     }
 
     @Issue("GRADLE-2163")
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/PluginDetectionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/PluginDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..fd84bb7
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/dsl/PluginDetectionIntegrationTest.groovy
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Plugin
+import org.gradle.api.plugins.AppliedPlugin
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import spock.lang.Issue
+import spock.lang.Unroll
+
+/**
+ * Tests various aspects of detecting the existence of plugins by their ID.
+ */
+class PluginDetectionIntegrationTest extends AbstractIntegrationSpec {
+
+    public static final List<String> JAVA_PLUGIN_IDS = ["java", "org.gradle.java"]
+
+    @Unroll
+    def "core plugins are detectable - applied by #appliedBy, detected by #detectedBy"() {
+        buildFile << """
+            def operations = []
+            plugins.withId("$detectedBy") {
+                operations << 'withId for ' + it.class.simpleName
+            }
+            pluginManager.withPlugin("$detectedBy") {
+                // assert we are using our closure decoration and not closure coercion
+                assert delegate instanceof $AppliedPlugin.name
+                operations << 'withPlugin'
+            }
+            operations << "applying"
+            apply plugin: '$appliedBy'
+            operations << "applied"
+
+            assert plugins["$detectedBy"]
+            assert plugins.getPlugin("$detectedBy")
+            assert pluginManager.hasPlugin("$detectedBy")
+            assert pluginManager.findPlugin("$detectedBy").id == "$detectedBy"
+
+            task verify << { assert operations == ['applying', 'withId for JavaPlugin', 'withPlugin', 'applied'] }
+        """
+
+        expect:
+        run("verify")
+
+        where:
+        appliedBy << JAVA_PLUGIN_IDS * 2
+        detectedBy << JAVA_PLUGIN_IDS + JAVA_PLUGIN_IDS.reverse()
+    }
+
+    def "unqualified ids from classpath are detectable"() {
+        def pluginBuilder = new PluginBuilder(testDirectory)
+        pluginBuilder.addPlugin("")
+        pluginBuilder.addRuleSource("test-rule-source")
+        pluginBuilder.publishTo(executer, file("plugin.jar"))
+
+        buildFile << """
+            def operations = []
+
+            def loader = new URLClassLoader([file("plugin.jar").toURL()] as URL[], getClass().classLoader)
+            def pluginClass = loader.loadClass("${pluginBuilder.packageName}.TestPlugin")
+            def ruleSourceClass = loader.loadClass("${pluginBuilder.packageName}.TestRuleSource")
+
+            plugins.withType(pluginClass) {
+                operations << 'withType'
+            }
+
+            plugins.withId("test-plugin") {
+                operations << 'withId'
+            }
+
+            pluginManager.withPlugin("test-rule-source") {
+                // assert we are using our closure decoration and not closure coercion
+                assert delegate instanceof $AppliedPlugin.name
+                operations << 'withPlugin'
+            }
+
+            operations << "applying"
+            apply plugin: pluginClass
+            apply type: ruleSourceClass
+            operations << "applied"
+
+            task verify << { assert operations == ['applying', 'withType', 'withId', 'withPlugin', 'applied'] }
+        """
+
+        expect:
+        run("verify")
+    }
+
+    def "plugin manager with id is fired after the plugin is applied for imperative plugins"() {
+        when:
+        buildFile << """
+            pluginManager.withPlugin("java") {
+              assert tasks.jar
+            }
+
+            apply plugin: "java"
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    def "plugin manager with id is fired after the plugin is applied for hybrid plugins"() {
+        when:
+        file("buildSrc/src/main/groovy/MyPlugin.groovy") << """
+            import org.gradle.api.Plugin
+            import org.gradle.api.Task
+            import org.gradle.model.*
+
+            class MyPlugin implements Plugin {
+                void apply(project) {
+                  project.tasks.create("imperative-sentinel")
+                }
+
+                static class Rules extends RuleSource {
+                    @Model String thing() {
+                        "foo"
+                    }
+                }
+            }
+        """
+
+        file("buildSrc/src/main/resources/META-INF/gradle-plugins/my.properties") << "implementation-class=MyPlugin"
+
+        buildFile << """
+            import org.gradle.model.internal.core.ModelPath
+
+            pluginManager.withPlugin("my") {
+              assert tasks."imperative-sentinel"
+              // note: modelRegistry property is internal on project
+              assert modelRegistry.node(ModelPath.path("thing")) != null
+            }
+
+            pluginManager.apply(MyPlugin)
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    def "plugin manager with id is fired after the plugin is applied for rule plugins"() {
+        when:
+        file("buildSrc/src/main/groovy/MyPlugin.groovy") << """
+            import org.gradle.model.*
+
+            class Rules extends RuleSource {
+                @Model String thing() {
+                    "foo"
+                }
+            }
+        """
+
+        file("buildSrc/src/main/resources/META-INF/gradle-plugins/my.properties") << "implementation-class=Rules"
+
+        buildFile << """
+            import org.gradle.model.internal.core.ModelPath
+
+            pluginManager.withPlugin("my") {
+              // note: modelRegistry property is internal on project
+              assert modelRegistry.node(ModelPath.path("thing")) != null
+            }
+
+            pluginManager.apply("my")
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    @Issue("http://discuss.gradle.org/t/concurrentmodification-exception-on-java-8-for-plugins-withid-with-gradle-2-4/8928")
+    def "can nest detection"() {
+        // Actual plugins in use here are insignificant
+        when:
+        file("buildSrc/src/main/groovy/PluginA.groovy") << """
+            class PluginA implements $Plugin.name {
+                void apply(project) {}
+            }
+        """
+        file("buildSrc/src/main/resources/META-INF/gradle-plugins/a.properties") << "implementation-class=PluginA"
+
+        file("buildSrc/src/main/groovy/PluginB.groovy") << """
+            class PluginB implements $Plugin.name {
+                void apply(project) {}
+            }
+        """
+        file("buildSrc/src/main/resources/META-INF/gradle-plugins/b.properties") << "implementation-class=PluginB"
+
+        file("buildSrc/src/main/groovy/PluginC.groovy") << """
+            class PluginC implements $Plugin.name {
+                void apply(project) {}
+            }
+        """
+        file("buildSrc/src/main/resources/META-INF/gradle-plugins/c.properties") << "implementation-class=PluginC"
+
+        buildScript """
+            class ExamplePlugin implements Plugin<Project> {
+                void apply(final Project project) {
+                    project.plugins.withId('a') {
+                        project.plugins.hasPlugin('b')
+                    }
+                    project.plugins.withId('c') {
+                        project.plugins.hasPlugin('b')
+                    }
+                }
+            }
+
+            apply plugin: ExamplePlugin
+            apply plugin: "a"
+        """
+
+        then:
+        succeeds "help"
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileResolutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..2e37a8b
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileResolutionIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.integtests.fixtures.AbstractIntegrationSpec
+
+class FileResolutionIntegrationTest extends AbstractIntegrationSpec {
+    def "gives reasonable error message when value cannot be converted to file"() {
+        buildFile << """
+def f = file(12)
+"""
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasCause("""Cannot convert the provided notation to a File or URI: 12.
+The following types/formats are supported:
+  - A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
+  - A String or CharSequence URI, for example 'file:/usr/include'.
+  - A File instance.
+  - A URI or URL instance.""")
+    }
+
+    def "gives reasonable error message when value cannot be converted to file collection"() {
+        buildFile << """
+def f = files({[12]})
+f.files.each { println it }
+"""
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasCause("""Cannot convert the provided notation to a File or URI: 12.
+The following types/formats are supported:
+  - A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
+  - A String or CharSequence URI, for example 'file:/usr/include'.
+  - A File instance.
+  - A URI or URL instance.""")
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoadersCachingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoadersCachingIntegrationTest.groovy
new file mode 100644
index 0000000..1a1c966
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoadersCachingIntegrationTest.groovy
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.Ignore
+import spock.lang.IgnoreIf
+
+//classloaders are cached in process so the test only makes sense if gradle invocations share the process
+ at IgnoreIf({ !GradleContextualExecuter.longLivingProcess })
+class ClassLoadersCachingIntegrationTest extends AbstractIntegrationSpec {
+
+    def cacheSizePerRun = []
+
+    def setup() {
+        executer.requireIsolatedDaemons()
+        file("cacheCheck.gradle") << """
+            def cache = gradle.services.get(org.gradle.api.internal.initialization.loadercache.ClassLoaderCache)
+            gradle.buildFinished {
+                println "### cache size: " + cache.size()
+
+                cache.assertInternalIntegrity()
+            }
+        """
+        executer.beforeExecute {
+            withArgument("-I").withArgument("cacheCheck.gradle")
+        }
+    }
+
+    def getIsCachedCheck() {
+        """
+            class StaticState {
+                static set = new java.util.concurrent.atomic.AtomicBoolean()
+            }
+            println project.path + " cached: " + StaticState.set.getAndSet(true)
+        """
+    }
+
+    def addIsCachedCheck(String project = null) {
+        file((project ? "$project/" : "") + "build.gradle") << isCachedCheck
+    }
+
+    private boolean isCached(String projectPath = ":") {
+        assert output.contains("$projectPath cached:"): "no cache flag for project"
+        output.contains("$projectPath cached: true")
+    }
+
+    private boolean isNotCached(String projectPath = ":") {
+        assert output.contains("$projectPath cached:"): "no cache flag for project"
+        output.contains("$projectPath cached: false")
+    }
+
+    private void assertCacheDidNotGrow() {
+        assert cacheSizePerRun.size() > 1: "only one build has been run"
+        assert cacheSizePerRun[-1] <= cacheSizePerRun[-2]
+    }
+
+    private void assertCacheSizeChange(int expectedCacheSizeChange) {
+        assert cacheSizePerRun.size() > 1: "only one build has been run"
+        assert cacheSizePerRun[-1] - cacheSizePerRun[-2] == expectedCacheSizeChange
+    }
+
+    ExecutionResult run(String... tasks) {
+        def result = super.run(tasks)
+        def m = output =~ /(?s).*### cache size: (\d+).*/
+        m.matches()
+        cacheSizePerRun << m.group(1).toInteger()
+        result
+    }
+
+    def "classloader is cached"() {
+        given:
+        addIsCachedCheck()
+
+        when:
+        run()
+        run()
+
+        then:
+        isCached()
+        assertCacheDidNotGrow()
+    }
+
+    def "refreshes when buildscript changes"() {
+        given:
+        addIsCachedCheck()
+        run()
+        buildFile << """
+            task newTask
+        """
+
+        expect:
+        run "newTask" //knows new task
+        isNotCached()
+        assertCacheDidNotGrow()
+    }
+
+    def "refreshes when buildSrc changes"() {
+        addIsCachedCheck()
+        file("buildSrc/src/main/groovy/Foo.groovy") << "class Foo {}"
+
+        when:
+        run()
+        run()
+
+        then:
+        isCached()
+        assertCacheDidNotGrow()
+
+        when:
+        file("buildSrc/src/main/groovy/Foo.groovy").text = "class Foo { static int x = 5; }"
+        run()
+
+        then:
+        isNotCached()
+        assertCacheDidNotGrow()
+    }
+
+    def "refreshes when new build script plugin added"() {
+        addIsCachedCheck()
+        file("plugin.gradle") << "task foo"
+
+        when:
+        run()
+        buildFile << "apply from: 'plugin.gradle'"
+        run("foo")
+
+        then:
+        isNotCached()
+        assertCacheSizeChange(1)
+    }
+
+    def "does not refresh main script loader when build script plugin changes"() {
+        addIsCachedCheck()
+
+        when:
+        buildFile << "apply from: 'plugin.gradle'"
+        file("plugin.gradle") << "task foo"
+        run("foo")
+        file("plugin.gradle").text = "task foobar"
+
+        then:
+        run("foobar") //new task is detected
+        isCached()
+    }
+
+    def "caches subproject classloader"() {
+        settingsFile << "include 'foo'"
+        addIsCachedCheck()
+        addIsCachedCheck "foo"
+
+        when:
+        run()
+        run()
+
+        then:
+        isCached(":foo")
+    }
+
+    def "uses cached subproject classloader when parent changes"() {
+        settingsFile << "include 'foo'"
+        addIsCachedCheck()
+        addIsCachedCheck "foo"
+
+        when:
+        run()
+        buildFile << "task foo"
+        run()
+
+        then:
+        isNotCached()
+        isCached(":foo")
+        assertCacheDidNotGrow()
+    }
+
+    def "refreshes when buildscript classpath gets new dependency"() {
+        addIsCachedCheck()
+        file("foo.jar") << "foo"
+
+        when:
+        run()
+        buildFile << """
+            buildscript { dependencies { classpath files("foo.jar") } }
+        """
+        run()
+
+        then:
+        isNotCached()
+        assertCacheSizeChange(2) // 1 new loader for first pass, 1 for second pass
+
+        then:
+        run()
+        isCached()
+        assertCacheDidNotGrow()
+    }
+
+    def "cache shrinks when buildscript disappears"() {
+        addIsCachedCheck()
+        file("foo.jar") << "foo"
+        buildFile << """
+            buildscript { dependencies { classpath files("foo.jar") } }
+
+            task foo
+        """
+
+        when:
+        run()
+        buildScript isCachedCheck
+        buildFile << "task foo"
+        run()
+
+        then:
+        assertCacheSizeChange(-2)
+
+        then:
+        run()
+        isCached()
+        assertCacheSizeChange(0)
+
+        then:
+        buildFile.delete()
+        run()
+        assertCacheSizeChange(-1)
+    }
+
+    def "refreshes when root project buildscript classpath changes"() {
+        settingsFile << "include 'foo'"
+        addIsCachedCheck()
+        addIsCachedCheck "foo"
+        buildFile << """
+            buildscript { dependencies { classpath files("lib") } }
+        """
+        file("lib/foo.jar") << "foo"
+
+        when:
+        run()
+        run()
+
+        then:
+        isCached(":")
+        isCached(":foo")
+
+        when:
+        file("lib/foo.jar") << "bar"
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        isNotCached(":")
+        isNotCached(":foo")
+
+        when:
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        isCached(":")
+        isCached(":foo")
+    }
+
+    def "refreshes when jar is removed from buildscript classpath"() {
+        addIsCachedCheck()
+        file("foo.jar") << "yyy"
+        buildFile << """
+            buildscript { dependencies { classpath files("foo.jar") }}
+        """
+
+        when:
+        run()
+        assert file("foo.jar").delete()
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        notCached
+
+        when:
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        isCached()
+    }
+
+    def "refreshes when dir is removed from buildscript classpath"() {
+        addIsCachedCheck()
+        file("lib/foo.jar") << "foo"
+        buildFile << """
+            buildscript { dependencies { classpath files("lib") }}
+        """
+
+        when:
+        run()
+        assert file("lib").deleteDir()
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        notCached
+
+        when:
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        isCached()
+    }
+
+    def "refreshes when buildscript when jar dependency replaced with dir"() {
+        addIsCachedCheck()
+        file("foo.jar") << "xxx"
+        buildFile << """
+            buildscript { dependencies { classpath files("foo.jar") }}
+        """
+
+        when:
+        run()
+        assert file("foo.jar").delete()
+        assert file("foo.jar").mkdirs()
+        assert file("foo.jar/someFile.txt").touch()
+
+        run()
+
+        then:
+        notCached
+        assertCacheDidNotGrow()
+    }
+
+    def "refreshes when buildscript when dir dependency replaced with jar"() {
+        addIsCachedCheck()
+        assert file("foo.jar").mkdirs()
+        assert file("foo.jar/someFile.txt").touch()
+
+        buildFile << """
+            buildscript { dependencies { classpath files("foo.jar") }}
+        """
+
+        when:
+        run()
+        assert file("foo.jar").deleteDir()
+        file("foo.jar") << "xxx"
+        run()
+
+        then:
+        notCached
+        assertCacheDidNotGrow()
+    }
+
+    def "reuse classloader when init script changed"() {
+        addIsCachedCheck()
+
+        when:
+        run()
+        file("init.gradle") << "println 'init x'"
+        run("-I", "init.gradle")
+
+        then:
+        isCached()
+        assertCacheSizeChange(1)
+
+        when:
+        file("init.gradle") << "println 'init y'"
+        run("-I", "init.gradle")
+
+        then:
+        isCached()
+        output.contains "init y"
+        assertCacheDidNotGrow()
+    }
+
+    def "reuse classloader when settings script changed"() {
+        addIsCachedCheck()
+
+        when:
+        run()
+        file("settings.gradle") << "println 'settings x'"
+        run()
+
+        then:
+        isCached()
+        assertCacheSizeChange(1)
+
+        when:
+        file("settings.gradle") << "println 'settings y'"
+        run()
+
+        then:
+        isCached()
+        output.contains "settings y"
+        assertCacheDidNotGrow()
+
+        when:
+        assert settingsFile.delete()
+        run()
+
+        then:
+        isCached()
+        !output.contains("settings y")
+        assertCacheSizeChange(-1)
+    }
+
+    def "cache growth is linear as projects are added"() {
+        when:
+        settingsFile << "System.getProperty('projects')?.split(':')?.each { include \"\$it\" }"
+        addIsCachedCheck()
+        addIsCachedCheck "a"
+        addIsCachedCheck "b"
+
+        then:
+        run("tasks")
+        run("tasks")
+        assertCacheDidNotGrow()
+
+        and:
+        args("-Dprojects=a")
+        run("tasks")
+        isCached()
+        isNotCached("a")
+        assertCacheSizeChange(1)
+        args("-Dprojects=a")
+        run("tasks")
+        isCached()
+        isCached("a")
+        assertCacheDidNotGrow()
+
+        and:
+        args("-Dprojects=a:b")
+        run("tasks")
+        isCached()
+        isCached("a")
+        isNotCached("b")
+        assertCacheSizeChange(1)
+        args("-Dprojects=a:b")
+        run("tasks")
+        isCached()
+        isCached("a")
+        isCached("b")
+        assertCacheDidNotGrow()
+
+        then:
+        args("-Dprojects=a:b")
+        file("b/build.gradle") << "\ntask c"
+        run("tasks")
+        assertCacheDidNotGrow()
+        isCached()
+        isCached(":a")
+        isNotCached(":b")
+
+        then:
+        args("-Dprojects=")
+        run("tasks")
+        assertCacheSizeChange(0) // we don't reclaim loaders for “orphaned” build scripts
+        isCached()
+    }
+
+    def "changing non root buildsript classpath does affect child projects"() {
+        when:
+        settingsFile << "include 'a', 'a:a'"
+        addIsCachedCheck()
+        addIsCachedCheck("a")
+        addIsCachedCheck("a/a")
+
+        // Add this to make the hierarchy unique, avoiding interference with the numbers from previous builds
+        file("build.gradle") << """
+            buildscript {
+                dependencies { classpath files("thing.jar") }
+            }
+        """
+
+        run()
+        run()
+
+        then:
+        isCached("a")
+        isCached("a:a")
+        assertCacheDidNotGrow()
+
+        when:
+        file("a/build.gradle") << """
+            buildscript {
+                dependencies { classpath files("thing.jar") }
+            }
+        """
+        run()
+
+        then:
+        assertCacheSizeChange(2)
+        isNotCached("a")
+        isNotCached("a:a")
+
+        when:
+        run()
+
+        then:
+        assertCacheDidNotGrow()
+        isCached("a")
+        isCached("a:a")
+
+        when:
+        file("a/a/build.gradle") << """
+            buildscript {
+                dependencies { classpath files("thing.jar") }
+            }
+        """
+        run()
+
+        then:
+        assertCacheSizeChange(2) // can't just reuse, because the parent is different
+        isCached("a")
+        isNotCached("a:a")
+
+        when:
+        file("a/build.gradle").text = getIsCachedCheck() // remove the middle buildscript
+        run()
+
+        then:
+        assertCacheSizeChange(-2) //
+        isNotCached("a")
+        isNotCached("a:a")
+
+        when:
+        file("a/a/build.gradle").text = getIsCachedCheck() // remove the leaf buildscript
+        run()
+
+        then:
+        assertCacheSizeChange(-2)
+        isCached("a")
+        isNotCached("a:a")
+    }
+
+    @Ignore
+    //I see that any change to the build script (including adding an empty line)
+    //causes the some of the compiled *.class to be different on a binary level
+    def "change that does not impact bytecode  classloader when settings script changed"() {
+        when:
+        run()
+        buildFile << "//comment"
+        run()
+
+        then:
+        cached
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy
new file mode 100644
index 0000000..5d2c2ec
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.resource
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.junit.Rule
+import spock.lang.IgnoreIf
+
+class TextResourceIntegrationTest extends AbstractIntegrationSpec {
+    @Rule TestResources resource = new TestResources(this)
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "string backed text resource"() {
+        when:
+        run("stringText")
+
+        then:
+        executedTasks == [":stringText"]
+        file("output.txt").text == "my config"
+
+        when:
+        run("stringText")
+
+        then:
+        skippedTasks == [":stringText"] as Set
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "file backed text resource"() {
+        when:
+        run("generateConfigFile")
+        run("fileText")
+
+        then:
+        executedTasks == [":fileText"]
+        file("output.txt").text == "my config"
+
+        when:
+        run("fileText")
+
+        then:
+        skippedTasks == [":fileText"] as Set
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "single-element file collection backed text resource"() {
+        when:
+        run("fileCollectionText")
+
+        then:
+        executedTasks == [":generateConfigFile", ":fileCollectionText"]
+        file("output.txt").text == "my config"
+
+        when:
+        run("fileCollectionText")
+
+        then:
+        skippedTasks == [":generateConfigFile", ":fileCollectionText"] as Set
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "archive entry backed text resource"() {
+        when:
+        run("archiveEntryText")
+
+        then:
+        executedTasks == [":generateConfigFile", ":generateConfigZip", ":archiveEntryText"]
+        file("output.txt").text == "my config"
+
+        when:
+        run("archiveEntryText")
+
+        then:
+        skippedTasks == [":generateConfigFile", ":generateConfigZip", ":archiveEntryText"] as Set
+    }
+}
\ No newline at end of file
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 9637d29..895465b 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
@@ -20,6 +20,7 @@ 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.hamcrest.Matchers
 import org.junit.Rule
 
 import static org.hamcrest.Matchers.equalTo
@@ -230,9 +231,9 @@ public class ArchiveIntegrationTest extends AbstractIntegrationSpec {
 '''
         when:
         def failure = runAndFail('copy')
+
         then:
-        assert failure.error.contains("Unable to expand TAR")
-        assert failure.error.contains("compression based on the file extension")
+        failure.assertThatDescription(Matchers.startsWith("Unable to expand TAR"))
     }
 
     def cannotCreateAnEmptyZip() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
index 6885f50..1e5c7c3 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
@@ -29,6 +29,30 @@ class CopyErrorIntegrationTest extends AbstractIntegrationTest {
     @Rule public PreconditionVerifier verifier = new PreconditionVerifier()
 
     @Test
+    public void givesReasonableErrorMessageWhenPathCannotBeConverted() {
+        file('src/thing.txt').createFile()
+
+        testFile('build.gradle') << '''
+            task copy(type: Copy) {
+                from('src') {
+                    into project.repositories
+                }
+                into 'dest'
+            }
+'''
+
+        ExecutionFailure failure = inTestDirectory().withTasks('copy').runWithFailure()
+        failure.assertHasCause("""Cannot convert the provided notation to a String: [].
+The following types/formats are supported:
+  - String or CharSequence instances, for example 'some/path'.
+  - Boolean values, for example true, Boolean.TRUE.
+  - Number values, for example 42, 3.14.
+  - A File instance
+  - A Closure that returns any supported value.
+  - A Callable that returns any supported value.""")
+    }
+
+    @Test
     @Requires(TestPrecondition.SYMLINKS)
     public void reportsSymLinkWhichPointsToNothing() {
         TestFile link = testFile('src/file')
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
index b0a2726..803f910 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
@@ -20,6 +20,7 @@ import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
+import spock.lang.Unroll
 
 import static org.junit.Assert.assertTrue
 
@@ -93,6 +94,7 @@ class CopyPermissionsIntegrationTest extends AbstractIntegrationSpec {
     }
 
     @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Unroll
     def "fileMode can be modified in copy task"() {
         given:
 
@@ -117,6 +119,7 @@ class CopyPermissionsIntegrationTest extends AbstractIntegrationSpec {
     }
 
     @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Unroll
     def "fileMode can be modified in copy action"() {
         given:
         file("reference.txt") << 'test file"'
@@ -143,6 +146,7 @@ class CopyPermissionsIntegrationTest extends AbstractIntegrationSpec {
     }
 
     @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Unroll
     def "dirMode can be modified in copy task"() {
         given:
         TestFile parent = getTestDirectory().createDir("testparent")
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 ed8cb8b..19d2c1e 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
@@ -18,11 +18,13 @@ package org.gradle.api.tasks
 
 import org.gradle.api.plugins.ExtensionAware
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2181")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2181")
     def "can copy files with unicode characters in name with non-unicode platform encoding"() {
         given:
         def weirdFileName = "القيادة والسيطرة - الإدارة.lnk"
@@ -46,7 +48,7 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         file("build/resources", weirdFileName).exists()
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2181")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2181")
     def "can copy files with unicode characters in name with default platform encoding"() {
         given:
         def weirdFileName = "القيادة والسيطرة - الإدارة.lnk"
@@ -94,7 +96,8 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         file("out/rename").exists()
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2838")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2838")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "include empty dirs works when nested"() {
         given:
         file("a/a.txt") << "foo"
@@ -123,6 +126,7 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         destinationDir.listFiles().findAll { it.directory }*.name.toSet() == ["dirA"].toSet()
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "include empty dirs is overridden by subsequent"() {
         given:
         file("a/a.txt") << "foo"
@@ -156,7 +160,7 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         destinationDir.listFiles().findAll { it.directory }*.name.toSet() == ["dirA", "dirB"].toSet()
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2902")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2902")
     def "internal copy spec methods are not visible to users"() {
         when:
         file("res/foo.txt") << "bar"
@@ -188,4 +192,28 @@ class CopyTaskIntegrationSpec extends AbstractIntegrationSpec {
         file("task/dir/foo.txt").exists()
     }
 
+    @Issue("https://issues.gradle.org/browse/GRADLE-3022")
+    def "filesMatching must match against sourcePath"() {
+        given:
+        file("a/b.txt") << "\$foo"
+
+        when:
+        buildScript """
+           task c(type: Copy) {
+               from("a") {
+                   filesMatching("b.txt") {
+                       expand foo: "bar"
+                   }
+                   into "nested"
+               }
+               into "out"
+           }
+        """
+
+        then:
+        succeeds "c"
+
+        and:
+        file("out/nested/b.txt").text == "bar"
+    }
 }
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 9d43b3d..2d77711 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
@@ -19,6 +19,7 @@ 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.Ignore
 import org.junit.Rule
 import org.junit.Test
 
@@ -164,7 +165,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void copySingleFiles() {
+    @Test
+    public void copySingleFiles() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 "task copyIt << {",
                 "   copy {",
@@ -185,7 +187,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
      * If these filters are chained in the correct order, you should get 6, 11, and 16
      */
 
-    @Test public void copyMultipleFilterTest() {
+    @Test
+    public void copyMultipleFilterTest() {
         TestFile buildFile = testFile('build.gradle').writelns(
                 """task (copy, type:Copy) {
                    into 'dest'
@@ -205,7 +208,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         assertThat(it.next(), startsWith('16'))
     }
 
-    @Test public void chainedTransformations() {
+    @Test
+    public void chainedTransformations() {
         def buildFile = testFile('build.gradle') << '''
             task copy(type: Copy) {
                 into 'dest'
@@ -237,7 +241,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         assertThat(it.next(), equalTo('[prefix: line 2]'))
     }
 
-    @Test public void testCopyFromFileTree() {
+    @Test
+    public void testCopyFromFileTree() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 """task cpy << {
                    copy {
@@ -257,7 +262,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void testCopyFromFileCollection() {
+    @Test
+    public void testCopyFromFileCollection() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 """task copy << {
                    copy {
@@ -279,7 +285,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void testCopyFromCompositeFileCollection() {
+    @Test
+    public void testCopyFromCompositeFileCollection() {
         testFile('a.jar').touch()
 
         TestFile buildFile = testFile("build.gradle").writelns(
@@ -304,7 +311,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void testCopyFromTask() {
+    @Test
+    public void testCopyFromTask() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 """
                     configurations { compile }
@@ -337,7 +345,8 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void testCopyFromTaskOutputs() {
+    @Test
+    public void testCopyFromTaskOutputs() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 """
                         configurations { compile }
@@ -370,10 +379,11 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void testCopyWithCopyspec() {
+    @Test
+    public void testCopyWithCopyspec() {
         TestFile buildFile = testFile("build.gradle").writelns(
                 """
-                def spec = copySpec {
+                def parentSpec = copySpec {
                     from 'src'
                     exclude '**/ignore/**'
                     include '*/*.a'
@@ -381,7 +391,7 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
                 }
                 task copy(type: Copy) {
                     into 'dest'
-                    with spec
+                    with parentSpec
                 }"""
         )
         usingBuildFile(buildFile).withTasks("copy").run()
@@ -391,6 +401,107 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
+    @Test
+    public void testTransformWithCopyspec() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """
+                def parentSpec = copySpec {
+                    from 'src'
+                    include '*/*.a'
+                    into 'subdir'
+                    eachFile { fcd -> fcd.relativePath = fcd.relativePath.prepend('transformedAgain')}
+                }
+                task copy(type: Copy) {
+                    into 'dest'
+                    with parentSpec
+                    eachFile { fcd -> fcd.relativePath = fcd.relativePath.prepend('transformed') }
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'transformedAgain/transformed/subdir/one/one.a',
+                'transformedAgain/transformed/subdir/two/two.a'
+        )
+    }
+
+    @Test
+    @Ignore
+    //this does not pass with current implementation
+    public void testIncludeExcludeWithCopyspec() {
+        TestFile buildFile = testFile("build.gradle").writelns(
+                """
+                def parentSpec = copySpec {
+                    from 'src'
+                    include '**/one/**'
+                    exclude '**/ignore/**'
+                    into 'subdir'
+                }
+                task copy(type: Copy) {
+                    into 'dest'
+                    include '**/two/**'
+                    exclude '**/*.b'
+                    with parentSpec
+                }"""
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'subdir/one/one.a',
+                'subdir/one/sub/onesub.a',
+                'subdir/two/two.a'
+        )
+    }
+
+    /*
+   * two.a starts off with "$one\n${one+1}\n${one+1+1}\n"
+   * If these filters are chained in the correct order, you should get 6, 11, and 16
+   */
+
+    @Test
+    public void testMultipleFilterWithCopyspec() {
+        TestFile buildFile = testFile('build.gradle').writelns(
+                """
+                  def parentSpec = copySpec {
+                    from('src/two/two.a')
+                    filter { (Integer.parseInt(it) / 2) as String }
+                  }
+                  task (copy, type:Copy) {
+                   into 'dest'
+                   expand(one: 1)
+                   filter { (Integer.parseInt(it) * 10) as String }
+                   filter { (Integer.parseInt(it) + 2) as String }
+                   with parentSpec
+                }
+                """
+        )
+        usingBuildFile(buildFile).withTasks("copy").run()
+        Iterator<String> it = testFile('dest/two.a').readLines().iterator()
+        assertThat(it.next(), startsWith('6'))
+        assertThat(it.next(), startsWith('11'))
+        assertThat(it.next(), startsWith('16'))
+    }
+
+    @Test
+    void testRenameWithCopySpec() {
+        TestFile buildFile = testFile("build.gradle") << '''
+            def parentSpec = copySpec {
+               from 'src/one'
+               exclude '**/ignore/**'
+               rename '(.*).b$', '$1.renamed'
+            }
+            task (copy, type:Copy) {
+               with parentSpec
+               into 'dest'
+               rename { it.startsWith('one.') ? "renamed_$it" : it }
+            }'''
+        usingBuildFile(buildFile).withTasks("copy").run()
+        testFile('dest').assertHasDescendants(
+                'renamed_one.renamed',
+                'renamed_one.a',
+                'sub/onesub.renamed',
+                'sub/onesub.a',
+        )
+    }
+
     // can't use TestResources here because Git doesn't support committing empty directories
     @Test
     void emptyDirsAreCopiedByDefault() {
@@ -487,19 +598,19 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
     }
 
     @Test
-    public void testChainMatchingRules() {
+    public void testEachChainedMatchingRuleAlwaysMatchesAgainstInitialSourcePath() {
         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') {
+                filesMatching ('**/a*') {
                     expand(attr: 'some value')
                     path = path.replace('template', 'concrete')
                 }
@@ -509,4 +620,88 @@ public class CopyTaskIntegrationTest extends AbstractIntegrationTest {
         file('dest').assertHasDescendants('bcd.txt', 'abc.txt.concrete')
         file('dest/abc.txt.concrete').text = 'test file with some value'
     }
+
+    @Test
+    public void testChainedMatchingRulesDoNotMatchAgainstDestinationPathSetByPreviousChainElement() {
+        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.template')
+        file('dest/abc.txt.template').text = 'test file with some $attr'
+    }
+
+    @Test
+    public void testAccessSourceNameFromFileCopyDetails() {
+        file('path/abc.txt').createFile().write('content')
+        file('path/bcd.txt').createFile()
+
+        def buildFile = testFile('build.gradle') <<
+                '''
+            task copy(type: Copy) {
+                from 'path'
+                into 'dest'
+                filesMatching ('**/a*') {
+                    name = "DEST-" + sourceName
+                }
+            }'''
+
+        usingBuildFile(buildFile).withTasks('copy').run();
+        file('dest').assertHasDescendants('bcd.txt', 'DEST-abc.txt')
+        file('dest/DEST-abc.txt').text = 'content'
+    }
+
+    @Test
+    public void testAccessSourcePathFromFileCopyDetails() {
+        file('path/abc.txt').createFile().write('content')
+        file('path/bcd.txt').createFile()
+
+        def buildFile = testFile('build.gradle') <<
+                '''
+            task copy(type: Copy) {
+                from 'path'
+                into 'dest'
+                filesMatching ('**/a*') {
+                    path = sourcePath.replace('txt', 'log')
+                }
+            }'''
+
+        usingBuildFile(buildFile).withTasks('copy').run();
+        file('dest').assertHasDescendants('bcd.txt', 'abc.log')
+        file('dest/abc.log').text = 'content'
+    }
+
+    @Test
+    public void testAccessRelativeSourcePathFromFileCopyDetails() {
+        file('path/abc.txt').createFile().write('content')
+        file('path/bcd.txt').createFile()
+
+        def buildFile = testFile('build.gradle') <<
+                '''
+            task copy(type: Copy) {
+                from 'path'
+                into 'dest'
+                filesMatching ('**/a*') {
+                    relativePath = relativeSourcePath.replaceLastName('abc.log')
+                }
+            }'''
+
+        usingBuildFile(buildFile).withTasks('copy').run();
+        file('dest').assertHasDescendants('bcd.txt', 'abc.log')
+        file('dest/abc.log').text = 'content'
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ExecutionTimeTaskConfigurationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ExecutionTimeTaskConfigurationIntegrationTest.groovy
index 110fd58..24b9549 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ExecutionTimeTaskConfigurationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ExecutionTimeTaskConfigurationIntegrationTest.groovy
@@ -17,148 +17,80 @@
 package org.gradle.api.tasks
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.internal.SystemProperties
+import spock.lang.Unroll
 
 class ExecutionTimeTaskConfigurationIntegrationTest extends AbstractIntegrationSpec {
+    @Unroll
+    def "fails when task is configured using #config during execution time"() {
+        buildFile.text = """
+            def anAction = {} as Action
 
-    private static final String NL = SystemProperties.getLineSeparator()
-
-    def "throws decent warnings when task is configured during execution time"() {
-
-        setup:
-        def invalidConfig = """
-            def anAction = new Action() {
-                public void execute(Object object) {
+            task broken {
+                doLast {
+                    $config
                 }
             }
-            doFirst(anAction)
-            doLast(anAction)
-            doFirst {}
-            doLast {}
-
-            actions.set(0, anAction)
-            actions.add(anAction)
-            actions.addAll([anAction])
-
-            def iter = actions.iterator()
-            iter.next()
-            iter.remove()
-
-            actions.removeAll([anAction, anAction])
-            actions.clear()
-
-            onlyIf {false}
-            setActions(new ArrayList())
-            dependsOn bar
-            dependsOn = [bar]
-            setOnlyIf({false})
-            setOnlyIf({false})
-
-            Spec spec = new Spec() {
-                public boolean isSatisfiedBy(Object element) {
-                    return false;
-                }
-            };
-            setOnlyIf(spec)
-            onlyIf(spec)
-            enabled = false
-            deleteAllActions()
 
-            inputs.file("afile")
-            inputs.files("anotherfile")
-            inputs.dir("aDir")
-            inputs.property("propertyName", "propertyValue")
-            inputs.properties(["propertyName": "propertyValue"])
-            inputs.source("aSource")
-            inputs.sourceDir("aSourceDir")
+            task broken2 << {
+                $config
+            }
 
-            outputs.upToDateWhen {false}
-            outputs.upToDateWhen(spec)
-            outputs.file("afile")
-            outputs.files("anotherfile")
-            outputs.dir("aDir")
+            task broken3 << { }
 
-            """
-        when:
-        buildFile.text = """
-            task bar{
-                doLast{
-                    $invalidConfig
+            task broken4 {
+                dependsOn broken3
+                doLast {
+                    broken3.configure { $config }
                 }
             }
-
-            task foo << {
-                $invalidConfig
-            }
         """
-        and:
-        executer.withDeprecationChecksDisabled()
-        then:
-        succeeds("bar", "foo")
 
-        output.contains("Calling Task.doFirst(Action) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.doFirst(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.doLast(Action) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.doLast(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().add() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().addAll() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().set(int, Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().remove() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().removeAll() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.getActions().clear() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.onlyIf(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.setActions(Actions<Task>) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.dependsOn(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.setDependsOn(Iterable) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.setOnlyIf(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.setOnlyIf(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.onlyIf(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.setEnabled(boolean) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling Task.deleteAllActions() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.dir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.files(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.file(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.property(String, Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.properties(Map) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.source(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskInputs.sourceDir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskOutputs.upToDateWhen(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskOutputs.upToDateWhen(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskOutputs.file(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskOutputs.files(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        output.contains("Calling TaskOutputs.dir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':bar'.$NL")
-        and:
+        when:
+        executer.withArgument("--continue")
+        fails("broken", "broken2", "broken4")
 
-        output.contains("Calling Task.doFirst(Action) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.doFirst(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.doLast(Action) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.doLast(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().add() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().addAll() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().set(int, Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().remove() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().removeAll() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.getActions().clear() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.onlyIf(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.setActions(Actions<Task>) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.dependsOn(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.setDependsOn(Iterable) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.setOnlyIf(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.setOnlyIf(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.onlyIf(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.setEnabled(boolean) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling Task.deleteAllActions() after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.dir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.files(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.file(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.property(String, Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.properties(Map) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.source(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskInputs.sourceDir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskOutputs.upToDateWhen(Closure) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskOutputs.upToDateWhen(Spec) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskOutputs.file(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskOutputs.files(Object...) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
-        output.contains("Calling TaskOutputs.dir(Object) after task execution has started has been deprecated and is scheduled to be removed in Gradle 2.0. Check the configuration of task ':foo'. You may have misused '<<' at task declaration.$NL")
+        then:
+        failure.assertHasCause("Cannot call ${description} on task ':broken' after task has started execution.")
+        failure.assertHasCause("Cannot call ${description} on task ':broken2' after task has started execution. Check the configuration of task ':broken2' as you may have misused '<<' at task declaration.")
+        failure.assertHasCause("Cannot call ${description} on task ':broken3' after task has started execution.")
+
+        where:
+        config                                                      | description
+        "doFirst(anAction)"                                         | "Task.doFirst(Action)"
+        "doFirst({})"                                               | "Task.doFirst(Closure)"
+        "doLast(anAction)"                                          | "Task.doLast(Action)"
+        "doLast({})"                                                | "Task.doLast(Closure)"
+        "actions.add(anAction)"                                     | "Task.getActions().add()"
+        "actions.addAll([anAction])"                                | "Task.getActions().addAll()"
+        "actions.set(0, anAction)"                                  | "Task.getActions().set(int, Object)"
+        "actions.removeAll(actions)"                                | "Task.getActions().removeAll()"
+        "actions.remove(actions[0])"                                | "Task.getActions().remove()"
+        "actions.clear()"                                           | "Task.getActions().clear()"
+        "def iter = actions.iterator(); iter.next(); iter.remove()" | "Task.getActions().remove()"
+        "actions = []"                                              | "Task.setActions(List<Action>)"
+        "deleteAllActions()"                                        | "Task.deleteAllActions()"
+        "onlyIf { }"                                                | "Task.onlyIf(Closure)"
+        "onlyIf({ } as Spec)"                                       | "Task.onlyIf(Spec)"
+        "setOnlyIf({ })"                                            | "Task.setOnlyIf(Closure)"
+        "onlyIf = ({ } as Spec)"                                    | "Task.setOnlyIf(Spec)"
+        "enabled = false"                                           | "Task.setEnabled(boolean)"
+        "dependsOn 'a', 'b'"                                        | "Task.dependsOn(Object...)"
+        "dependsOn = ['a', 'b']"                                    | "Task.setDependsOn(Iterable)"
+        "mustRunAfter 'a', 'b'"                                     | "Task.mustRunAfter(Object...)"
+        "mustRunAfter = ['a', 'b']"                                 | "Task.setMustRunAfter(Iterable)"
+        "finalizedBy 'a', 'b'"                                      | "Task.finalizedBy(Object...)"
+        "finalizedBy = ['a', 'b']"                                  | "Task.setFinalizedBy(Iterable)"
+        "inputs.file('a')"                                          | "TaskInputs.file(Object)"
+        "inputs.files('a')"                                         | "TaskInputs.files(Object...)"
+        "inputs.dir('a')"                                           | "TaskInputs.dir(Object)"
+        "inputs.property('key', 'value')"                           | "TaskInputs.property(String, Object)"
+        "inputs.properties([key: 'value'])"                         | "TaskInputs.properties(Map)"
+        "inputs.source('a')"                                        | "TaskInputs.source(Object)"
+        "inputs.sourceDir('a')"                                     | "TaskInputs.sourceDir(Object)"
+        "outputs.upToDateWhen { }"                                  | "TaskOutputs.upToDateWhen(Closure)"
+        "outputs.upToDateWhen({ } as Spec)"                         | "TaskOutputs.upToDateWhen(Spec)"
+        "outputs.file('a')"                                         | "TaskOutputs.file(Object)"
+        "outputs.files('a')"                                        | "TaskOutputs.files(Object...)"
+        "outputs.dir('a')"                                          | "TaskOutputs.dir(Object)"
     }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/FailingIncrementalTasksIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/FailingIncrementalTasksIntegrationTest.groovy
new file mode 100644
index 0000000..510d93f
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/FailingIncrementalTasksIntegrationTest.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.tasks
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class FailingIncrementalTasksIntegrationTest extends AbstractIntegrationSpec {
+
+    def "consecutively failing task has correct up-to-date status and failure"() {
+        buildFile << """
+            task foo {
+                outputs.file("out.txt")
+                doLast {
+                    if (project.file("out.txt").exists()) {
+                        throw new RuntimeException("Boo!")
+                    }
+                    project.file("out.txt") << "xxx"
+                }
+            }
+        """
+
+        when:
+        run "foo"
+        file("out.txt") << "force rerun"
+        def failure1 = runAndFail "foo"
+        def failure2 = runAndFail "foo"
+
+        then:
+        failure1.assertHasCause("Boo!")
+        failure2.assertHasCause("Boo!")
+        //this exposes an issue we used to have with in-memory cache.
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..369e6de
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Rule
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertThat
+
+class IncrementalBuildIntegrationTest extends AbstractIntegrationTest {
+    @Rule public final TestResources resource = new TestResources(testDirectoryProvider)
+
+    @Test
+    public void skipsTaskWhenOutputFileIsUpToDate() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('src.a.txt')
+}
+task b(type: org.gradle.integtests.TransformerTask, dependsOn: a) {
+    inputFile = a.outputFile
+    outputFile = file('src.b.txt')
+}
+'''
+        TestFile inputFile = testFile('src.txt')
+        TestFile outputFileA = testFile('src.a.txt')
+        TestFile outputFileB = testFile('src.b.txt')
+
+        inputFile.text = 'content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        TestFile.Snapshot aSnapshot = outputFileA.snapshot()
+        TestFile.Snapshot bSnapshot = outputFileB.snapshot()
+        assertThat(outputFileA.text, equalTo('[content]'))
+        assertThat(outputFileB.text, equalTo('[[content]]'))
+
+        // No changes
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputFileA.assertHasNotChangedSince(aSnapshot)
+        outputFileB.assertHasNotChangedSince(bSnapshot)
+
+        // Update timestamp, no content changes
+
+        inputFile.setLastModified(inputFile.lastModified() - 10000);
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputFileA.assertHasNotChangedSince(aSnapshot)
+        outputFileB.assertHasNotChangedSince(bSnapshot)
+
+        // Change content
+
+        inputFile.text = 'new content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputFileA.assertHasChangedSince(aSnapshot)
+        outputFileB.assertHasChangedSince(bSnapshot)
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Delete intermediate output file
+
+        outputFileA.delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Delete final output file
+
+        outputFileB.delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        assertThat(outputFileA.text, equalTo('[new content]'))
+        assertThat(outputFileB.text, equalTo('[[new content]]'))
+
+        // Change build file in a way which does not affect the task
+
+        testFile('build.gradle').text += '''
+task c
+'''
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        // Change an input property of the first task (the content format)
+        testFile('build.gradle').text += '''
+a.format = '     %s     '
+'''
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        assertThat(outputFileA.text, equalTo('     new content     '))
+        assertThat(outputFileB.text, equalTo('[     new content     ]'))
+
+        // Change final output file destination
+        testFile('build.gradle').text += '''
+b.outputFile = file('new-output.txt')
+'''
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+        outputFileB = testFile('new-output.txt')
+        outputFileB.assertIsFile()
+
+        // Run with --rerun-tasks command-line options
+        inTestDirectory().withTasks('b').withArguments('--rerun-tasks').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        // Output files already exist before using this version of Gradle
+        // delete .gradle dir to simulate this
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputFileB.delete()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void skipsTaskWhenOutputDirContentsAreUpToDate() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src')
+    outputDir = file('build/a')
+}
+task b(type: org.gradle.integtests.DirTransformerTask, dependsOn: a) {
+    inputDir = a.outputDir
+    outputDir = file('build/b')
+}
+'''
+
+        testFile('src').createDir()
+        testFile('src/file1.txt').write('content')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        TestFile outputAFile = testFile('build/a/file1.txt')
+        TestFile outputBFile = testFile('build/b/file1.txt')
+        TestFile.Snapshot aSnapshot = outputAFile.snapshot()
+        TestFile.Snapshot bSnapshot = outputBFile.snapshot()
+
+        outputAFile.assertContents(equalTo('[content]'))
+        outputBFile.assertContents(equalTo('[[content]]'))
+
+        // No changes
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        outputAFile.assertHasNotChangedSince(aSnapshot)
+        outputBFile.assertHasNotChangedSince(bSnapshot)
+
+        // Change content
+
+        testFile('src/file1.txt').write('new content')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        outputAFile.assertHasChangedSince(aSnapshot)
+        outputBFile.assertHasChangedSince(bSnapshot)
+        outputAFile.assertContents(equalTo('[new content]'))
+        outputBFile.assertContents(equalTo('[[new content]]'))
+
+        // Add file
+
+        testFile('src/file2.txt').write('content2')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build/a/file2.txt').assertContents(equalTo('[content2]'))
+        testFile('build/b/file2.txt').assertContents(equalTo('[[content2]]'))
+
+        // Remove file
+
+        testFile('src/file1.txt').delete()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        // Output files already exist before using this version of Gradle
+        // delete .gradle dir to simulate this
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build/b').deleteDir()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void skipsTaskWhenInputPropertiesHaveNotChanged() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.GeneratorTask) {
+    text = project.text
+    outputFile = file('dest.txt')
+}
+'''
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped()
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped(':a')
+
+        inTestDirectory().withTasks('a').withArguments('-Ptext=newtext').run().assertTasksExecuted(':a').assertTasksSkipped()
+    }
+
+    @Test
+    public void multipleTasksCanGenerateIntoOverlappingOutputDirectories() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src/a')
+    outputDir = file('build')
+}
+task b(type: org.gradle.integtests.DirTransformerTask) {
+    inputDir = file('src/b')
+    outputDir = file('build')
+}
+'''
+
+        testFile('src/a/file1.txt') << 'content'
+        testFile('src/b/file2.txt') << 'content'
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        // No changes
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+
+        // Delete an output file
+
+        testFile('build/file1.txt').delete()
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
+
+        // Change an output file
+
+        testFile('build/file2.txt').write('something else')
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
+
+        // Change to new version of Gradle
+        // Simulate this by removing the .gradle dir
+        testFile('.gradle').assertIsDir().deleteDir()
+
+        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+
+        testFile('build').deleteDir()
+
+        inTestDirectory().withTasks('a').run().assertTasksExecuted(':a').assertTasksSkipped()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':b').assertTasksSkipped()
+    }
+
+    @Test
+    public void canUseUpToDatePredicateToForceTaskToExecute() {
+        testFile('build.gradle') << '''
+task inputsAndOutputs {
+    inputs.files 'src.txt'
+    outputs.files 'src.a.txt'
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst {
+        outputs.files.singleFile.text = "[${inputs.files.singleFile.text}]"
+    }
+}
+task noOutputs {
+    inputs.files 'src.txt'
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst { }
+}
+task nothing {
+    outputs.upToDateWhen { project.hasProperty('uptodate') }
+    doFirst { }
+}
+'''
+        TestFile srcFile = testFile('src.txt')
+        srcFile.text = 'content'
+
+        // Task with input files, output files and a predicate
+        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped(':inputsAndOutputs')
+
+        // Changed input file
+        srcFile.text = 'different'
+        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Predicate is false
+        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
+
+        // Task with input files and a predicate
+        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped(':noOutputs')
+
+        // Changed input file
+        srcFile.text = 'different again'
+        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Predicate is false
+        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
+
+        // Task a predicate only
+        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
+
+        // Is up to date
+        inTestDirectory().withArguments('-Puptodate').withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped(':nothing')
+
+        // Predicate is false
+        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
+    }
+
+    @Test
+    public void lifecycleTaskIsUpToDateWhenAllDependenciesAreSkipped() {
+        testFile('build.gradle') << '''
+task a(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('out.txt')
+}
+task b(dependsOn: a)
+'''
+
+        testFile('src.txt').text = 'content'
+
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
+        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
+    }
+
+    @Test
+    public void canShareArtifactsBetweenBuilds() {
+        def buildFile = testFile('build.gradle') << '''
+task otherBuild(type: GradleBuild) {
+    buildFile = 'build.gradle'
+    tasks = ['generate']
+    startParameter.searchUpwards = false
+}
+task transform(type: org.gradle.integtests.TransformerTask) {
+    dependsOn otherBuild
+    inputFile = file('generated.txt')
+    outputFile = file('out.txt')
+}
+task generate(type: org.gradle.integtests.TransformerTask) {
+    inputFile = file('src.txt')
+    outputFile = file('generated.txt')
+}
+'''
+        testFile('settings.gradle') << 'rootProject.name = "build"'
+        testFile('src.txt').text = 'content'
+
+        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped()
+        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped(':transform', ':build:generate')
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTaskIntegrationTest.groovy
deleted file mode 100644
index bcb82bd..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTaskIntegrationTest.groovy
+++ /dev/null
@@ -1,47 +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.tasks
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class IncrementalTaskIntegrationTest extends AbstractIntegrationSpec {
-
-    def "consecutively failing task has correct up-to-date status and failure"() {
-        buildFile << """
-            task foo {
-                outputs.file("out.txt")
-                doLast {
-                    if (project.file("out.txt").exists()) {
-                        throw new RuntimeException("Boo!")
-                    }
-                    project.file("out.txt") << "xxx"
-                }
-            }
-        """
-
-        when:
-        run "foo"
-        file("out.txt") << "force rerun"
-        def failure1 = runAndFail "foo"
-        def failure2 = runAndFail "foo"
-
-        then:
-        failure1.assertHasCause("Boo!")
-        failure2.assertHasCause("Boo!")
-        //this exposes an issue we used to have with in-memory cache.
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTasksIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTasksIntegrationTest.groovy
new file mode 100644
index 0000000..ce0ff49
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalTasksIntegrationTest.groovy
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+class IncrementalTasksIntegrationTest extends AbstractIntegrationSpec {
+    def "setup"() {
+        buildFile << buildFileBase
+        buildFile << """
+    task incremental(type: IncrementalTask) {
+        inputDir = project.mkdir('inputs')
+        outputDir = project.mkdir('outputs')
+        prop = 'foo'
+    }
+"""
+        file('inputs/file0.txt') << "inputFile0"
+        file('inputs/file1.txt') << "inputFile1"
+        file('inputs/file2.txt') << "inputFile2"
+
+        file('outputs/file1.txt') << "outputFile1"
+        file('outputs/file2.txt') << "outputFile2"
+    }
+
+    private static String getBuildFileBase() {
+        """
+    class BaseIncrementalTask extends DefaultTask {
+        @InputDirectory
+        def File inputDir
+
+        @TaskAction
+        void execute(IncrementalTaskInputs inputs) {
+            assert !(inputs instanceof ExtensionAware)
+
+            if (project.hasProperty('forceFail')) {
+                throw new RuntimeException('failed')
+            }
+
+            incrementalExecution = inputs.incremental
+
+            inputs.outOfDate { change ->
+                if (change.added) {
+                    addedFiles << change.file
+                } else {
+                    changedFiles << change.file
+                }
+            }
+
+            inputs.removed { change ->
+                removedFiles << change.file
+            }
+
+            touchOutputs()
+        }
+
+        def touchOutputs() {
+        }
+
+        def addedFiles = []
+        def changedFiles = []
+        def removedFiles = []
+        def incrementalExecution
+    }
+
+    class IncrementalTask extends BaseIncrementalTask {
+        @Input
+        def String prop
+
+        @OutputDirectory
+        def File outputDir
+
+        @Override
+        def touchOutputs() {
+            outputDir.eachFile {
+                it << "more content"
+            }
+        }
+    }
+
+    ext {
+        incrementalExecution = true
+        added = []
+        changed = []
+        removed = []
+    }
+
+    task incrementalCheck(dependsOn: "incremental") << {
+        assert incremental.incrementalExecution == project.ext.incrementalExecution
+        assert incremental.addedFiles.collect({ it.name }).sort() == project.ext.added
+        assert incremental.changedFiles.collect({ it.name }).sort() == project.ext.changed
+        assert incremental.removedFiles.collect({ it.name }).sort() == project.ext.removed
+    }
+"""
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when run for the first time"() {
+        expect:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is skipped when run with no changes since last execution"() {
+        given:
+        previousExecution()
+
+        when:
+        run "incremental"
+
+        then:
+        ":incremental" in skippedTasks
+    }
+
+    def "incremental task is informed of 'out-of-date' files when input file modified"() {
+        given:
+        previousExecution()
+
+        when:
+        file('inputs/file1.txt') << "changed content"
+
+        then:
+        executesWithIncrementalContext("ext.changed = ['file1.txt']");
+    }
+
+    def "incremental task is informed of 'out-of-date' files when input file added"() {
+        given:
+        previousExecution()
+
+        when:
+        file('inputs/file3.txt') << "file3 content"
+
+        then:
+        executesWithIncrementalContext("ext.added = ['file3.txt']")
+    }
+
+    def "incremental task is informed of 'out-of-date' files when input file removed"() {
+        given:
+        previousExecution()
+
+        when:
+        file('inputs/file2.txt').delete()
+
+        then:
+        executesWithIncrementalContext("ext.removed = ['file2.txt']")
+    }
+
+    def "incremental task is informed of 'out-of-date' files when all input files removed"() {
+        given:
+        previousExecution()
+
+        when:
+        file('inputs/file1.txt').delete()
+        file('inputs/file2.txt').delete()
+
+        then:
+        executesWithIncrementalContext("ext.removed = ['file1.txt', 'file2.txt']")
+    }
+
+    def "incremental task is informed of 'out-of-date' files with added, removed and modified files"() {
+        given:
+        previousExecution()
+
+        when:
+        file('inputs/file1.txt') << "changed content"
+        file('inputs/file2.txt').delete()
+        file('inputs/file3.txt') << "new file 3"
+        file('inputs/file4.txt') << "new file 4"
+
+        then:
+        executesWithIncrementalContext("""
+ext.changed = ['file1.txt']
+ext.removed = ['file2.txt']
+ext.added = ['file3.txt', 'file4.txt']
+""")
+    }
+
+    def "incremental task is informed of 'out-of-date' files when task has no declared outputs or properties"() {
+        given:
+        buildFile.text = buildFileBase
+        buildFile << """
+    task incremental(type: BaseIncrementalTask) {
+        inputDir = project.mkdir('inputs')
+    }
+"""
+        and:
+        previousExecution()
+
+        when:
+        file('inputs/file3.txt') << "file3 content"
+
+        then:
+        executesWithIncrementalContext("ext.added = ['file3.txt']")
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when input property has changed"() {
+        given:
+        previousExecution()
+
+        when:
+        buildFile << "incremental.prop = 'changed'"
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when task class has changed"() {
+        given:
+        previousExecution()
+
+        when:
+        buildFile.text = buildFileBase
+        buildFile << """
+    class IncrementalTask2 extends BaseIncrementalTask {}
+    task incremental(type: IncrementalTask2) {
+        inputDir = project.mkdir('inputs')
+    }
+"""
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when output directory is changed"() {
+        given:
+        previousExecution()
+
+        when:
+        buildFile << "incremental.outputDir = project.mkdir('new-outputs')"
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when output file has changed"() {
+        given:
+        previousExecution()
+
+        when:
+        file("outputs/file1.txt") << "further change"
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when output file has been removed"() {
+        given:
+        previousExecution()
+
+        when:
+        file("outputs/file1.txt").delete()
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when all output files have been removed"() {
+        given:
+        previousExecution()
+
+        when:
+        file("outputs").deleteDir()
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when Task.upToDate() is false"() {
+        given:
+        previousExecution()
+
+        when:
+        buildFile << "incremental.outputs.upToDateWhen { false }"
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed that all input files are 'out-of-date' when gradle is executed with --rerun-tasks"() {
+        given:
+        previousExecution()
+
+        when:
+        executer.withArgument("--rerun-tasks")
+
+        then:
+        executesWithRebuildContext()
+    }
+
+    def "incremental task is informed of 'out-of-date' files since previous successful execution"() {
+        given:
+        previousExecution()
+
+        and:
+        file('inputs/file1.txt') << "changed content"
+
+        when:
+        failedExecution()
+
+        then:
+        executesWithIncrementalContext("ext.changed = ['file1.txt']");
+    }
+    /*
+     7. Sad-day cases
+         - Incremental task has input files declared
+         - Incremental task action throws exception
+         - Incremental task action processes outOfDate files multiple times
+         - Attempt to process removed files without first processing outOfDate files
+     */
+
+    def previousExecution() {
+        run "incremental"
+    }
+
+    def failedExecution() {
+        executer.withArgument("-PforceFail=yep")
+        assert fails("incremental")
+        executer.withArguments()
+    }
+
+    def executesWithIncrementalContext(String fileChanges) {
+        buildFile << fileChanges
+        succeeds "incrementalCheck"
+    }
+
+    def executesWithRebuildContext() {
+        buildFile << """
+    ext.changed = ['file0.txt', 'file1.txt', 'file2.txt']
+    ext.incrementalExecution = false
+"""
+        succeeds "incrementalCheck"
+    }
+}
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
index ad33dff..8dbafc3 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCommandLineConfigurationIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCommandLineConfigurationIntegrationSpec.groovy
@@ -249,7 +249,6 @@ class TaskCommandLineConfigurationIntegrationSpec extends AbstractIntegrationSpe
         failure.assertHasDescription("Incorrect command line arguments: [-l, -l]. Task options require double dash, for example: 'gradle tasks --all'.")
     }
 
-
     def "decent error for invalid enum value"() {
         given:
         file("build.gradle") << """
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskInputPropertiesIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskInputPropertiesIntegrationTest.groovy
new file mode 100644
index 0000000..99caba5
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskInputPropertiesIntegrationTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+class TaskInputPropertiesIntegrationTest extends AbstractIntegrationSpec {
+
+    def "reports which properties are not serializable"() {
+        buildFile << """
+            task foo {
+                inputs.property "a", "hello"
+                inputs.property "b", new Foo()
+                outputs.file "foo.txt"
+                doLast { file("foo.txt") << "" }
+            }
+
+            class Foo {
+                int x
+                String toString() { "xxx" }
+            }
+        """
+
+        when: fails "foo"
+        then: failure.assertHasCause("Unable to store task input properties. Property 'b' with value 'xxx")
+    }
+
+    def "deals gracefully with not serializable contents of GStrings"() {
+        buildFile << """
+            task foo {
+                inputs.property "a", "hello \${new Foo()}"
+                outputs.file "foo.txt"
+                doLast { file("foo.txt") << "" }
+            }
+
+            class Foo {
+                int x
+                String toString() { "xxx" }
+            }
+        """
+
+        expect:
+        run("foo").assertTaskNotSkipped(":foo")
+        run("foo").assertTaskSkipped(":foo")
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskRemovalIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskRemovalIntegrationTest.groovy
index 10ad45a..82eb139 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskRemovalIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskRemovalIntegrationTest.groovy
@@ -55,34 +55,58 @@ class TaskRemovalIntegrationTest extends AbstractIntegrationSpec {
     }
 
     @Unroll
-    def "cant remove task in after evaluate if task is used by a #ruleClass"() {
+    def "can remove task in after evaluate if task is used by unbound #annotationClass rule"() {
         given:
         buildScript """
             import org.gradle.model.*
 
             task foo {}
-            task bar {}
 
             afterEvaluate {
                 tasks.remove(foo)
             }
 
-            // No DSL for rules dependent on other model yet
-            project.services.get(ModelRules).rule(new $ruleClass() {
-                void linkFooToBar(@Path("tasks.bar") Task bar, @Path("tasks.foo") Task foo) {
-                    // do nothing
+            class Rules extends RuleSource {
+                @$annotationClass
+                void linkFooToBar(String bar, @Path("tasks.foo") Task foo) {
+                   // do nothing
                 }
-            })
+            }
+
+            apply plugin: Rules
         """
 
         when:
-        fails "foo"
+        fails "dependencies"
 
         then:
-        failure.assertThatCause(Matchers.startsWith("Tried to remove model tasks.foo but it is depended on by other model elements"))
+        failure.assertThatCause(Matchers.startsWith("The following model rules are unbound"))
 
         where:
-        ruleClass << ["ModelRule", "ModelFinalizer"]
+        annotationClass << ["Defaults", "Mutate", "Finalize", "Validate"]
     }
 
+    def "cant remove task if used by rule"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            task foo {}
+            task bar { doLast { tasks.remove(foo) } }
+
+            class Rules extends RuleSource {
+                @Mutate
+                void linkFooToBar(@Path("tasks.bar") Task bar, @Path("tasks.foo") Task foo) {
+                   // do nothing
+                }
+            }
+
+            apply plugin: Rules
+        """
+
+        then:
+        fails ":bar"
+        failure.assertThatCause(Matchers.startsWith("Tried to remove model tasks.foo but it is depended on by: tasks.bar"))
+
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskSelectionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskSelectionIntegrationTest.groovy
new file mode 100644
index 0000000..bf4f05b
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskSelectionIntegrationTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+class TaskSelectionIntegrationTest extends AbstractIntegrationSpec {
+    def "given an unqualified name traverse project tree from current project and select all tasks with matching name"() {
+        settingsFile << "include 'a', 'b', 'a:a', 'b:b'"
+
+        buildFile << """
+            subprojects {
+                task thing
+                projectDir.mkdirs()
+            }
+            """
+
+        when:
+        run "thing"
+
+        then:
+        result.assertTasksExecuted(":a:thing", ":b:thing", ":a:a:thing", ":b:b:thing")
+
+        when:
+        executer.inDirectory(file("a"))
+        run "thing"
+
+        then:
+        result.assertTasksExecuted(":a:thing", ":a:a:thing")
+
+        // camel case matching
+        when:
+        run "th"
+
+        then:
+        result.assertTasksExecuted(":a:thing", ":b:thing", ":a:a:thing", ":b:b:thing")
+    }
+
+    def "stops traversing sub-projects when task implies sub-projects"() {
+        settingsFile << "include 'a', 'b', 'a:a', 'b:b'"
+
+        buildFile << """
+            subprojects {
+                task thing
+                projectDir.mkdirs()
+            }
+            project(":a") {
+                thing.impliesSubProjects = true
+            }
+            """
+
+        when:
+        run "thing"
+
+        then:
+        result.assertTasksExecuted(":a:thing", ":b:thing", ":b:b:thing")
+
+        when:
+        executer.inDirectory(file("a"))
+        run "thing"
+
+        then:
+        result.assertTasksExecuted(":a:thing")
+
+        // camel case matching
+        when:
+        run "th"
+
+        then:
+        result.assertTasksExecuted(":a:thing", ":b:thing", ":b:b:thing")
+    }
+
+    def "can use camel-case for all segments of qualified task name"() {
+        settingsFile << "include 'child', 'child:child'"
+
+        buildFile << """
+allprojects { task thing }
+"""
+        when:
+        run "chi:chi:th"
+
+        then:
+        result.assertTasksExecuted(":child:child:thing")
+    }
+
+    def "executes project default tasks when none specified"() {
+        settingsFile << "include 'a'"
+
+        buildFile << """
+    allprojects {
+        task a
+        task b
+    }
+    b.dependsOn a
+    defaultTasks 'b'
+"""
+        when:
+        run()
+
+        then:
+        result.assertTasksExecuted(":a", ":b", ":a:b")
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/RuleBasedTaskExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/RuleBasedTaskExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..166e478
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/RuleBasedTaskExecutionIntegrationTest.groovy
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.model.internal.core.ModelNode
+
+class RuleBasedTaskExecutionIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+            gradle.buildFinished {
+                file("tasks.txt").text = allprojects*.tasks.flatten()*.path.join("\\n")
+            }
+        """
+    }
+
+    List<String> getCreatedTasks() {
+        file("tasks.txt").readLines().sort()
+    }
+
+    List<String> createdTasksFor(String... tasks) {
+        succeeds(tasks)
+        createdTasks
+    }
+
+    def "does not create rule based tasks in projects without required tasks"() {
+        when:
+        settingsFile << "include 'a', 'b', 'c'"
+        buildFile << """
+            allprojects {
+                model {
+                    tasks {
+                        create("t1")
+                    }
+                }
+            }
+        """
+
+        then:
+        createdTasksFor(":a:t1") == [":a:t1"]
+        createdTasksFor(":a:t1", "b:t1") == [":a:t1", ":b:t1"]
+        createdTasksFor(":t1") == [":t1"]
+        createdTasksFor("t1") == [":a:t1", ":b:t1", ":c:t1", ":t1"]
+    }
+
+    def "rule based tasks that are not requested on the command line are not realised"() {
+        when:
+        buildFile << """
+            model {
+                tasks {
+                    create("t1")
+                    create("t2")
+                }
+            }
+        """
+
+        then:
+        createdTasksFor("t1") == [":t1"]
+    }
+
+    def "task container is self closed by task selection and can be later graph closed"() {
+        when:
+        buildFile << '''
+            import org.gradle.model.internal.core.*
+
+            model {
+                tasks {
+                    create("t1")
+                    create("t2")
+                }
+            }
+
+            gradle.buildFinished {
+                def tasksPath = ModelPath.path("tasks")
+                def registry = project.modelRegistry
+                println "task container node state at the end of build: ${registry.state(tasksPath)}"
+                registry.atState(tasksPath, ModelNode.State.GraphClosed)
+                println "task container node state after graph closing: ${registry.state(tasksPath)}"
+            }
+        '''
+
+        then:
+        succeeds "t1"
+
+        and:
+        output.contains "task container node state at the end of build: ${ModelNode.State.SelfClosed}"
+        output.contains "task container node state after graph closing: ${ModelNode.State.GraphClosed}"
+    }
+
+    def "tasks added via task container and not explicitly required but executed are self closed"() {
+        given:
+        buildScript '''
+            import org.gradle.model.collection.*
+
+            class EchoTask extends DefaultTask {
+                String text = "default"
+
+                @TaskAction
+                void print() {
+                    println "$name: $text"
+                }
+            }
+
+            class Rules extends RuleSource {
+                @Mutate
+                void configureDependencyTask(@Path("tasks.dependency") EchoTask task) {
+                    task.text = "configured"
+                }
+
+                @Mutate
+                void configureFinalizerTask(@Path("tasks.finalizer") EchoTask task) {
+                    task.text = "configured"
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("requested") {
+                        dependsOn "dependency"
+                        finalizedBy "finalizer"
+                    }
+                }
+            }
+
+            apply type: Rules
+
+            tasks.create("dependency", EchoTask)
+            tasks.create("finalizer", EchoTask)
+        '''
+
+        when:
+        succeeds "requested"
+
+        then:
+        output.contains "dependency: configured"
+        output.contains "finalizer: configured"
+    }
+
+    def "task container is self closed for projects of which any tasks are being executed"() {
+        settingsFile << "include 'a', 'b'"
+
+        buildScript """
+            import org.gradle.model.collection.*
+
+            project(':a') {
+                apply type: ProjectARules
+            }
+
+            project(':b') {
+                apply type: ProjectBRules
+            }
+
+            class ProjectARules extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("executed") {
+                        dependsOn ":b:dependency"
+                    }
+                }
+            }
+
+            class ProjectBRules extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("dependency")
+                }
+            }
+        """
+
+        when:
+        succeeds ":a:executed"
+
+        then:
+        ":b:dependency" in executedTasks
+    }
+
+    def "can get name of task defined in rules only script plugin after configuration"() {
+        when:
+        buildScript """
+            apply from: "fooTask.gradle"
+            task check << {
+              assert getTasksByName("foo", false).toList().first().name == "foo"
+            }
+        """
+
+        file("fooTask.gradle") << """
+            model {
+                tasks { create("foo") }
+            }
+        """
+
+        then:
+        succeeds "check", "foo"
+    }
+
+    def "cant get name of task defined in rules only script plugin during configuration"() {
+        // Not really a test, more of a documentation of the current behaviour
+        // getTasksByName() doesn't exhaustively check for rule based tasks
+        when:
+        buildScript """
+            apply from: "fooTask.gradle"
+            task check {
+              def fooTasks = getTasksByName("foo", false).toList()
+              doFirst {
+                assert fooTasks.isEmpty()
+              }
+            }
+        """
+
+        file("fooTask.gradle") << """
+            model {
+                tasks { create("foo") }
+            }
+        """
+
+        then:
+        succeeds "check", "foo"
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/StatementLabelsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/StatementLabelsIntegrationTest.groovy
index 0ef3d46..1ec6ce3 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/StatementLabelsIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/StatementLabelsIntegrationTest.groovy
@@ -17,27 +17,30 @@
 package org.gradle.groovy.scripts
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.hamcrest.Matchers
 
 class StatementLabelsIntegrationTest extends AbstractIntegrationSpec {
-    def "use of statement label in build script is flagged"() {
+    def "use of statement label in build script is reported"() {
         buildFile << """
 version: '1.0'
         """
 
         expect:
-        executer.withDeprecationChecksDisabled()
-        succeeds("tasks")
-        output.contains("Usage of statement labels in build scripts has been deprecated")
-        output.contains("version")
+        fails("tasks")
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertHasLineNumber(2)
+        failure.assertHasDescription("Could not compile build file '${buildFile}'.")
+        failure.assertThatCause(Matchers.containsString("build file '${buildFile}': 2: Statement labels may not be used in build scripts."))
 
         // try again to make sure that warning sticks if build script is cached
-        executer.withDeprecationChecksDisabled()
-        succeeds("tasks")
-        output.contains("Usage of statement labels in build scripts has been deprecated")
-        output.contains("version")
+        fails("tasks")
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertHasLineNumber(2)
+        failure.assertHasDescription("Could not compile build file '${buildFile}'.")
+        failure.assertThatCause(Matchers.containsString("build file '${buildFile}': 2: Statement labels may not be used in build scripts."))
     }
 
-    def "all usages of statement labels are flagged"() {
+    def "all usages of statement labels are reported"() {
         buildFile << """
 version: '1.0'
 group = "foo"
@@ -45,15 +48,15 @@ description: "bar"
         """
 
         expect:
-        executer.withDeprecationChecksDisabled()
-        succeeds("tasks")
-        output.contains("Usage of statement labels in build scripts has been deprecated")
-        output.contains("version")
-        !output.contains("group")
-        output.contains("description")
+        fails("tasks")
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertHasLineNumber(2)
+        failure.assertHasDescription("Could not compile build file '${buildFile}'.")
+        failure.assertThatCause(Matchers.containsString("build file '${buildFile}': 2: Statement labels may not be used in build scripts."))
+        failure.assertThatCause(Matchers.containsString("build file '${buildFile}': 4: Statement labels may not be used in build scripts."))
     }
 
-    def "nested use of statement label in build script is flagged"() {
+    def "nested use of statement label in build script is reported"() {
         buildFile << """
 def foo() {
     1.times {
@@ -65,10 +68,11 @@ def foo() {
         """
 
         expect:
-        executer.withDeprecationChecksDisabled()
-        succeeds("tasks")
-        output.contains("Usage of statement labels in build scripts has been deprecated")
-        output.contains("label")
+        fails("tasks")
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertHasLineNumber(5)
+        failure.assertHasDescription("Could not compile build file '${buildFile}'.")
+        failure.assertThatCause(Matchers.containsString("build file '${buildFile}': 5: Statement labels may not be used in build scripts."))
     }
 
     def "use of statement label in class inside build script is allowed"() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/plugin/NonImperativeBuildScriptEvaluationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/plugin/NonImperativeBuildScriptEvaluationIntegrationTest.groovy
new file mode 100644
index 0000000..3517de2
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/plugin/NonImperativeBuildScriptEvaluationIntegrationTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class NonImperativeBuildScriptEvaluationIntegrationTest extends AbstractIntegrationSpec {
+
+    def "all non-imperative script plugins applied to a project get evaluated"() {
+        when:
+        file("scriptPlugin1.gradle") << """
+            model {
+                tasks {
+                    create("fromScriptPlugin1")
+                }
+            }
+        """
+
+        file("scriptPlugin2.gradle") << """
+            model {
+                tasks {
+                    create("fromScriptPlugin2")
+                }
+            }
+        """
+
+        buildScript """
+            apply from: "scriptPlugin1.gradle"
+            apply from: "scriptPlugin2.gradle"
+        """
+
+        then:
+        succeeds "fromScriptPlugin1", "fromScriptPlugin2"
+    }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/plugin/PluginHandlerScriptIntegTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/plugin/PluginHandlerScriptIntegTest.groovy
deleted file mode 100644
index 81bfed3..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/plugin/PluginHandlerScriptIntegTest.groovy
+++ /dev/null
@@ -1,554 +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.plugin
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.bintray.BintrayApi
-import org.gradle.test.fixtures.bintray.BintrayTestServer
-import org.gradle.test.fixtures.plugin.PluginBuilder
-import org.gradle.util.GradleVersion
-import org.junit.Rule
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-
-class PluginHandlerScriptIntegTest extends AbstractIntegrationSpec {
-
-    private static final String SCRIPT = "plugins { println 'in' }; println 'out'"
-
-    @Rule BintrayTestServer bintray = new BintrayTestServer(executer, mavenRepo) // provides a double for JCenter
-
-    int pluginCounter = 0
-
-    def pluginMessage = "from plugin"
-    def pluginTaskName = "pluginTask"
-    def pluginVersion = "1.0"
-
-    PluginBuilder pluginBuilder() {
-        new PluginBuilder(executer, file("plugin${++pluginCounter}"))
-    }
-
-    def setup() {
-        executer.requireOwnGradleUserHomeDir() // to negate caching effects
-    }
-
-    def "build scripts have plugin blocks"() {
-        when:
-        buildFile << SCRIPT
-
-        then:
-        executesCorrectly()
-    }
-
-    def "settings scripts have plugin blocks"() {
-        when:
-        settingsFile << SCRIPT
-
-        then:
-        executesCorrectly()
-    }
-
-    def "init scripts have plugin blocks"() {
-        def initScript = file("init.gradle")
-
-        when:
-        initScript << SCRIPT
-
-        then:
-        args "-I", initScript.absolutePath
-        executesCorrectly()
-    }
-
-    def "cannot use plugin block when script target is not plugin capable"() {
-        buildFile << """
-            task foo {}
-            apply {
-                from "plugin.gradle"
-                to foo
-            }
-        """
-
-        file("plugin.gradle") << """
-            plugins {
-                apply plugin: "foo"
-            }
-        """
-
-        when:
-        fails "foo"
-
-        then:
-        errorOutput.contains("cannot have plugins applied to it")
-    }
-
-
-    def void executesCorrectly() {
-        succeeds "tasks"
-        assert output.contains(toPlatformLineSeparators("in\nout\n"))
-    }
-
-    void "plugins block has no implicit access to owner context"() {
-        when:
-        buildScript """
-            plugins {
-                try {
-                    buildscript {}
-                } catch(MissingMethodException e) {
-                    // ok
-                }
-
-                try {
-                    version
-                } catch(MissingPropertyException e) {
-                    // ok
-                }
-
-                assert delegate == null
-                assert this instanceof ${PluginHandler.name}
-                assert owner == this
-
-                println "end-of-plugins"
-            }
-        """
-
-        then:
-        succeeds "tasks"
-        and:
-        output.contains("end-of-plugins")
-    }
-
-    void "can resolve core plugins"() {
-        when:
-        buildScript """
-            plugins {
-              apply plugin: 'java'
-            }
-        """
-
-        then:
-        succeeds "javadoc"
-    }
-
-    void "core plugins cannot have a version number"() {
-        given:
-        buildScript """
-            plugins {
-                apply plugin: "java", version: "1.0"
-            }
-        """
-
-        when:
-        fails "tasks"
-
-        then:
-        failure.assertHasCause("Plugin 'java' is a core Gradle plugin, which cannot be specified with a version number")
-    }
-
-    def void publishPluginToBintray(PluginBuilder pluginBuilder, String group, String name, String version = pluginVersion) {
-        def module = bintray.jcenter.module(group, name, version)
-        module.allowAll()
-        def artifact = module.artifact([:])
-        module.publish()
-        pluginBuilder.publishTo(artifact.file)
-    }
-
-    void "can use plugin classes in script"() {
-        given:
-        bintray.start()
-        def pb = pluginBuilder()
-        pb.groovy("EchoTask.groovy") << """
-            package $pb.packageName
-
-            class EchoTask extends ${DefaultTask.name} {
-                @${TaskAction.name}
-                void doEcho() {
-                    println "$pluginMessage"
-                }
-            }
-        """
-
-        pb.addPlugin("", "test")
-        publishPluginToBintray(pb, "test", "test")
-        bintray.api.expectPackageSearch("test", new BintrayApi.FoundPackage("foo", "test:test"))
-
-        buildScript """
-          plugins {
-            apply plugin: "test", version: "$pluginVersion"
-          }
-
-          task echo(type: ${pb.packageName}.EchoTask) {}
-        """
-
-        when:
-        succeeds "echo"
-
-        then:
-        output.contains pluginMessage
-    }
-
-    void "plugins block does not leak into build script proper"() {
-        given:
-        buildFile << """
-            configurations {
-                plugins {
-                    transitive = false // just use some configuration specific API here
-                }
-            }
-
-            task showConfigurationsSize << {
-                println "configurations: " + configurations.size()
-                println "plugins transitive: " + configurations.plugins.transitive
-            }
-        """
-
-        when:
-        succeeds "sCS"
-
-        then:
-        output.contains("configurations: 1")
-        output.contains("plugins transitive: false")
-    }
-
-    def "buildscript blocks are allowed before plugin statements"() {
-        when:
-        buildScript """
-            buildscript {}
-            plugins {}
-        """
-
-        then:
-        succeeds "tasks"
-    }
-
-    def "buildscript blocks are not allowed after plugin blocks"() {
-        when:
-        buildScript """
-            plugins {}
-            buildscript {}
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasLineNumber 3
-        errorOutput.contains("all buildscript {} blocks must appear before any plugins {} blocks")
-    }
-
-    def "build logic cannot precede plugins block"() {
-        when:
-        buildScript """
-            someThing()
-            plugins {}
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasLineNumber 3
-        errorOutput.contains "only buildscript {} and and other plugins {} script blocks are allowed before plugins {} blocks, no other statements are allowed"
-    }
-
-    def "build logic cannot precede any plugins block"() {
-        when:
-        buildScript """
-            plugins {}
-            someThing()
-            plugins {}
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasLineNumber 4
-        errorOutput.contains "only buildscript {} and and other plugins {} script blocks are allowed before plugins {} blocks, no other statements are allowed"
-    }
-
-    def "failed resolution provides helpful error message"() {
-        given:
-        bintray.start()
-
-        buildScript """
-            plugins {
-                apply plugin: "foo"
-            }
-        """
-
-        and:
-        bintray.api.expectPackageSearch("foo")
-
-        when:
-        fails "tasks"
-
-        then:
-        errorOutput.contains "Cannot resolve plugin request [plugin: 'foo'] from plugin repositories:"
-        errorOutput.contains "- Gradle Distribution Plugins (listing: http://gradle.org/docs/${GradleVersion.current().version}/userguide/standard_plugins.html)"
-        errorOutput.contains "- Gradle Bintray Plugin Repository (listing: https://bintray.com/gradle-plugins-development/gradle-plugins)"
-    }
-
-    private publishTestPlugin() {
-        def pluginBuilder = new PluginBuilder(executer, testDirectory.file("plugin"))
-
-        def module = mavenRepo.module("plugin", "plugin")
-        def artifactFile = module.artifact([:]).artifactFile
-        module.publish()
-
-        def message = "from plugin"
-        def taskName = "pluginTask"
-        pluginBuilder.addPluginWithPrintlnTask(taskName, message, "plugin")
-        pluginBuilder.publishTo(artifactFile)
-    }
-
-    private testPluginBuildscriptBlock() {
-        return """
-            buildscript {
-                repositories {
-                    maven { url "$mavenRepo.uri" }
-                }
-                dependencies {
-                    classpath "plugin:plugin:1.0"
-                }
-            }
-        """
-    }
-
-    private testPluginPluginsBlock() {
-        return """
-            plugins {
-                apply plugin: "plugin", version: "1.0"
-            }
-        """
-    }
-
-    def "cannot apply plugins added to buildscript classpath in plugins block"() {
-        given:
-        publishTestPlugin()
-
-        when:
-        buildScript """
-            ${testPluginBuildscriptBlock()}
-            ${testPluginPluginsBlock()}
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        errorOutput.contains "Plugin 'plugin' is already on the script classpath (plugins on the script classpath cannot be used in a plugins {} block; move \"apply plugin: 'plugin'\" outside of the plugins {} block)"
-    }
-
-    def "cannot apply plugins added to parent buildscript classpath in plugins block"() {
-        given:
-        publishTestPlugin()
-
-        when:
-        buildScript """
-            ${testPluginBuildscriptBlock()}
-        """
-
-        settingsFile << "include 'sub'"
-
-        file("sub/build.gradle") << """
-            ${testPluginPluginsBlock()}
-        """
-
-        then:
-        fails "sub:tasks"
-
-        and:
-        errorOutput.contains "Plugin 'plugin' is already on the script classpath (plugins on the script classpath cannot be used in a plugins {} block; move \"apply plugin: 'plugin'\" outside of the plugins {} block)"
-    }
-
-    def "plugin classes are reused across projects and resolution is cached"() {
-        when:
-        bintray.start()
-        def pb = pluginBuilder().addPlugin("", "test")
-        publishPluginToBintray(pb, "test", "test")
-
-        // Only receiving one search request verifies that the result is cached
-        bintray.api.expectPackageSearch("test", new BintrayApi.FoundPackage("foo", "test:test"))
-
-        settingsFile << "include 'sub1', 'sub2'"
-
-        ["sub1/build.gradle", "sub2/build.gradle", "build.gradle"].each {
-            file(it) << """
-                plugins {
-                    apply plugin: "test", version: "$pluginVersion"
-                }
-                ext.pluginClass = org.gradle.test.TestPlugin
-            """
-        }
-
-        file("build.gradle") << """
-            evaluationDependsOnChildren()
-
-            pluginClass.is project(":sub1").pluginClass
-            pluginClass.is project(":sub2").pluginClass
-            project(":sub1").pluginClass.is project(":sub2").pluginClass
-        """
-
-        then:
-        succeeds "tasks"
-    }
-
-    def "classes from plugin block are not visible to classes from buildscript block"() {
-        given:
-        def pb1 = pluginBuilder()
-        def pb2 = pluginBuilder()
-
-        pb1.addPlugin("getClass().classLoader.loadClass('org.gradle.test.PluginOne')", "p1", "PluginOne")
-        pb2.addPlugin("getClass().classLoader.loadClass('org.gradle.test.PluginOne')", "p2", "PluginTwo")
-
-        bintray.start()
-
-        publishPluginToBintray(pb1, "p1", "p1")
-        bintray.api.expectPackageSearch("p1", new BintrayApi.FoundPackage(pluginVersion, "p1:p1"))
-
-        publishPluginToBintray(pb2, "p2", "p2")
-
-        when:
-        settingsFile << "rootProject.name = 'tp'"
-        buildScript """
-            buildscript {
-                repositories {
-                    jcenter()
-                }
-                dependencies {
-                    classpath "p2:p2:$pluginVersion"
-                }
-            }
-            plugins {
-                apply plugin: "p1", version: "1.0"
-            }
-
-            apply plugin: "p2"
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasDescription("A problem occurred evaluating root project 'tp'.")
-        failure.assertHasCause("org.gradle.test.PluginOne") // message of ClassNotFoundException
-        failure.assertHasLineNumber(14) // asserts that  'apply plugin: "p2"' is the line that fails
-    }
-
-    def "plugin classes are not available to child projects"() {
-        given:
-        bintray.start()
-        def pb = pluginBuilder().addPlugin("", "plugin")
-        publishPluginToBintray(pb, "plugin", "plugin")
-        bintray.api.expectPackageSearch("plugin", new BintrayApi.FoundPackage(pluginVersion, "plugin:plugin"))
-
-        when:
-        settingsFile << "include 'child'"
-        buildScript """
-            ${testPluginPluginsBlock()}
-            import org.gradle.test.TestPlugin
-        """
-        file("child/build.gradle") << """
-            import org.gradle.test.TestPlugin
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasDescription("Could not compile build file '${file("child/build.gradle")}'.")
-    }
-
-    def "plugins cannot be applied by child projects"() {
-        given:
-        bintray.start()
-        def pb = pluginBuilder().addPlugin("", "plugin")
-        publishPluginToBintray(pb, "plugin", "plugin")
-        bintray.api.expectPackageSearch("plugin", new BintrayApi.FoundPackage(pluginVersion, "plugin:plugin"))
-
-        when:
-        settingsFile << "include 'child'"
-        buildScript """
-            ${testPluginPluginsBlock()}
-            import org.gradle.test.TestPlugin
-        """
-        file("child/build.gradle") << """
-            apply plugin: 'plugin'
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasDescription("A problem occurred evaluating project ':child'.")
-        failure.assertHasCause("Plugin with id 'plugin' not found.")
-    }
-
-    def "plugins added via plugins block cannot be applied with project.apply"() {
-        given:
-        bintray.start()
-        def pb = pluginBuilder().addPlugin("project.task('foo')", "plugin")
-        publishPluginToBintray(pb, "plugin", "plugin")
-        bintray.api.expectPackageSearch("plugin", new BintrayApi.FoundPackage(pluginVersion, "plugin:plugin"))
-
-        when:
-        settingsFile << 'rootProject.name = "tp"'
-        buildScript """
-            ${testPluginPluginsBlock()}
-            apply plugin: 'plugin'
-        """
-
-        then:
-        fails "tasks"
-
-        and:
-        failure.assertHasDescription("A problem occurred evaluating root project 'tp'.")
-        failure.assertHasCause("Plugin with id 'plugin' not found.")
-    }
-
-    def "plugins added in root buildscript can be applied in subprojects"() {
-        given:
-        executer.withEagerClassLoaderCreationCheckDisabled()
-        pluginBuilder().addPlugin("project.task('foo')", "plugin").publishTo(mavenRepo.module("g", "a").artifactFile)
-
-        settingsFile << "include 'sub'"
-
-        buildScript """
-            buildscript {
-                repositories {
-                    maven { url "$mavenRepo.uri" }
-                }
-                dependencies {
-                    classpath "g:a:1.0"
-                }
-            }
-
-            apply plugin: 'plugin'
-
-            subprojects {
-                apply plugin: 'plugin'
-            }
-        """
-
-        when:
-        succeeds "sub:foo"
-
-        then:
-        ":sub:foo" in executedTasks
-    }
-}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/plugin/ScriptPluginClassLoadingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/plugin/ScriptPluginClassLoadingIntegrationTest.groovy
index 058edd5..9a1ebb2 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/plugin/ScriptPluginClassLoadingIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/plugin/ScriptPluginClassLoadingIntegrationTest.groovy
@@ -17,11 +17,16 @@
 package org.gradle.plugin
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.plugin.PluginBuilder
 import spock.lang.Issue
 
+import static org.gradle.util.Matchers.containsText
+
 class ScriptPluginClassLoadingIntegrationTest extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-3069")
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3069")
     def "second level and beyond script plugins have same class loader scope as original target"() {
         when:
         file("buildSrc/src/main/java/pkg/Thing.java") << """
@@ -51,4 +56,223 @@ class ScriptPluginClassLoadingIntegrationTest extends AbstractIntegrationSpec {
         succeeds "sayMessageFrom1", "sayMessageFrom2", "sayMessageFrom3"
         output.contains "hello"
     }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3079")
+    def "methods defined in script are available to used script plugins"() {
+        given:
+        buildScript """
+          def addTask(project) {
+            project.tasks.create("hello").doLast { println "hello from method" }
+          }
+
+          apply from: "script.gradle"
+        """
+
+        file("script.gradle") << "addTask(project)"
+
+        when:
+        succeeds "hello"
+
+        then:
+        output.contains "hello from method"
+    }
+
+    def "methods defined in script plugin are not inherited by applied script plugin"() {
+        given:
+        buildScript """
+            apply from: "script1.gradle"
+        """
+
+        file("script1.gradle") << """
+            def someMethod() {}
+            apply from: "script2.gradle"
+        """
+
+        file("script2.gradle") << """
+            someMethod()
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasFileName("Script '${file("script2.gradle").absolutePath}'")
+        failure.assertThatCause(containsText("Could not find method someMethod()"))
+    }
+
+    def "methods defined in settings script are not inherited by scripts"() {
+        given:
+        settingsFile << """
+            def someMethod() {}
+        """
+
+        buildScript """
+            someMethod()
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasFileName("Build file '${buildFile.absolutePath}'")
+        failure.assertThatCause(containsText("Could not find method someMethod()"))
+    }
+
+    def "methods defined in init script are not inherited by scripts"() {
+        given:
+        file("init.gradle") << """
+            def someMethod() {}
+        """
+
+        buildScript """
+            someMethod()
+        """
+
+        when:
+        fails "tasks", "-I", file("init.gradle").absolutePath
+
+        then:
+        failure.assertHasFileName("Build file '${buildFile.absolutePath}'")
+        failure.assertThatCause(containsText("Could not find method someMethod()"))
+    }
+
+    def "methods defined in a build script are visible to scripts applied to sub projects"() {
+        given:
+        settingsFile << "include 'sub'"
+
+        buildScript """
+            def someMethod() {
+                println "from some method"
+            }
+        """
+
+        file("sub/build.gradle") << "apply from: 'script.gradle'"
+        file("sub/script.gradle") << "someMethod()"
+
+        when:
+        run "tasks"
+
+        then:
+        output.contains("from some method")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3082")
+    def "can use apply block syntax to apply multiple scripts"() {
+        given:
+        buildScript """
+          apply {
+            from "script1.gradle"
+            from "script2.gradle"
+          }
+        """
+
+        file("script1.gradle") << "task hello1 << { println 'hello from script1' }"
+        file("script2.gradle") << "task hello2 << { println 'hello from script2' }"
+
+        when:
+        succeeds "hello1", "hello2"
+
+        then:
+        output.contains "hello from script1"
+        output.contains "hello from script2"
+    }
+
+    def "separate classloaders are used when using multi apply syntax"() {
+        given:
+        buildScript """
+          apply {
+            from "script1.gradle"
+            from "script2.gradle"
+          }
+        """
+
+        file("script1.gradle") << "class Foo {}"
+        file("script2.gradle") << "new Foo()"
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasFileName("Script '${file("script2.gradle").absolutePath}'")
+        failure.assertThatCause(containsText("unable to resolve class Foo"))
+    }
+
+    def "script plugin buildscript does not affect client"() {
+        given:
+        def jar = file("plugin.jar")
+        pluginBuilder.addPlugin("project.task('hello')")
+        pluginBuilder.publishTo(executer, jar)
+
+        settingsFile << "include 'sub'"
+
+        buildScript """
+            apply from: "script.gradle"
+
+            try {
+                getClass().classLoader.loadClass(pluginClass.name)
+                assert false
+            } catch (ClassNotFoundException ignore) {
+                println "not in root"
+            }
+
+        """
+
+        file("script.gradle") << """
+          buildscript {
+            dependencies { classpath files("plugin.jar") }
+          }
+
+          apply plugin: org.gradle.test.TestPlugin
+          ext.pluginClass = org.gradle.test.TestPlugin
+        """
+
+        file("sub/build.gradle") << """
+            try {
+                getClass().classLoader.loadClass(pluginClass.name)
+                assert false
+            } catch (ClassNotFoundException ignore) {
+                println "not in sub"
+            }
+        """
+
+        when:
+        succeeds "hello"
+
+        then:
+        output.contains "not in root"
+        output.contains "not in sub"
+    }
+
+    def "script plugin cannot access classed added by buildscript in applying script"() {
+        given:
+        def jar = file("plugin.jar")
+        pluginBuilder.addPlugin("project.task('hello')")
+        pluginBuilder.publishTo(executer, jar)
+
+        buildScript """
+            buildscript {
+                dependencies { classpath files("plugin.jar") }
+            }
+
+            apply plugin: org.gradle.test.TestPlugin
+            ext.pluginClass = org.gradle.test.TestPlugin
+
+            apply from: "script.gradle"
+        """
+
+        file("script.gradle") << """
+            try {
+                getClass().classLoader.loadClass(pluginClass.name)
+                assert false
+            } catch (ClassNotFoundException ignore) {
+                println "not in script"
+            }
+        """
+
+        when:
+        succeeds "hello"
+
+        then:
+        output.contains "not in script"
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/plugin/bintray/BintrayPluginResolutionIntegTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/plugin/bintray/BintrayPluginResolutionIntegTest.groovy
deleted file mode 100644
index e4cd608..0000000
--- a/subprojects/core/src/integTest/groovy/org/gradle/plugin/bintray/BintrayPluginResolutionIntegTest.groovy
+++ /dev/null
@@ -1,115 +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.plugin.bintray
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.bintray.BintrayApi
-import org.gradle.test.fixtures.bintray.BintrayTestServer
-import org.gradle.test.fixtures.plugin.PluginBuilder
-import org.hamcrest.Matchers
-import org.junit.Rule
-
-class BintrayPluginResolutionIntegTest extends AbstractIntegrationSpec {
-
-    @Rule BintrayTestServer bintray = new BintrayTestServer(executer, mavenRepo)
-    def pluginBuilder = new PluginBuilder(executer, file("plugin"))
-
-    def setup() {
-        executer.requireOwnGradleUserHomeDir() // negate caching behaviour
-        bintray.start()
-    }
-
-    def "can resolve and use plugin from bintray"() {
-        given:
-        def group = "org.gradle.test"
-        def name = "foo"
-        def id = "fake-plugin"
-        def version = "2.0"
-
-        bintray.api.expectPackageSearch(id, new BintrayApi.FoundPackage(version, "$group:$name"))
-
-        def module = bintray.jcenter.module(group, name, version)
-        module.allowAll()
-        def artifact = module.artifact([:])
-        module.publish()
-
-        def message = "from plugin"
-        def taskName = "pluginTask"
-        pluginBuilder.addPluginWithPrintlnTask(taskName, message, id)
-        pluginBuilder.publishTo(artifact.file)
-
-        buildScript """
-          plugins {
-            apply plugin: "$id", version: "2.0"
-          }
-        """
-
-        when:
-        succeeds taskName
-
-        then:
-        output.contains message
-    }
-
-    def "plugin version number is required"() {
-        given:
-        def group = "org.gradle.test"
-        def name = "foo"
-        def id = "fake-plugin"
-        def version = "2.0"
-
-        bintray.api.expectPackageSearch(id, new BintrayApi.FoundPackage(version, "$group:$name"))
-
-        def module = bintray.jcenter.module(group, name, version)
-        module.allowAll()
-        def artifact = module.artifact([:])
-        module.publish()
-
-        pluginBuilder.addPlugin("", id)
-        pluginBuilder.publishTo(artifact.file)
-
-        buildScript """
-          plugins {
-            apply plugin: "$id"
-          }
-        """
-
-        when:
-        fails "tasks"
-
-        then:
-        failure.assertHasCause("No version number supplied for plugin 'fake-plugin'. A version number must be supplied for plugins resolved from 'https://bintray.com/gradle-plugins-development/gradle-plugins'.")
-    }
-
-    def "does not assert has version number if plugin not found"() {
-        given:
-        def id = "fake-plugin"
-        bintray.api.expectPackageSearch(id)
-        buildScript """
-          plugins {
-            apply plugin: "$id"
-          }
-        """
-
-        when:
-        fails "tasks"
-
-        then:
-        failure.assertThatCause(Matchers.startsWith("Cannot resolve plugin request"))
-    }
-
-}
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
index 2d0b4af..bef8bba 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegTest.groovy
@@ -17,7 +17,6 @@
 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
@@ -31,18 +30,18 @@ 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.internal.event.ListenerBroadcast
 import org.gradle.messaging.remote.MessagingServer
 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.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.gradle.util.GradleVersion
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Ignore
-import spock.lang.IgnoreIf
 import spock.lang.Specification
 import spock.lang.Unroll
 
@@ -56,12 +55,12 @@ class PathLimitationIntegTest extends Specification {
 
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServices.getInstance().get(org.gradle.internal.nativeplatform.ProcessEnvironment.class));
+    private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServicesTestFixture.getInstance().get(org.gradle.internal.nativeintegration.ProcessEnvironment.class));
     private final CacheFactory factory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler()));
-    private final CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(tmpDir.getTestDirectory(), null, GradleVersion.current()), CacheUsage.ON, factory);
+    private final CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(tmpDir.getTestDirectory(), null, GradleVersion.current()), 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 DefaultWorkerProcessFactory workerFactory = new DefaultWorkerProcessFactory(LogLevel.INFO, server, classPathRegistry, TestFiles.resolver(tmpDir.getTestDirectory()), new LongIdGenerator(), null);
     private final ListenerBroadcast<TestListenerInterface> broadcast = new ListenerBroadcast<TestListenerInterface>(TestListenerInterface.class);
     private final RemoteExceptionListener exceptionListener = new RemoteExceptionListener(broadcast.source);
 
@@ -73,7 +72,7 @@ class PathLimitationIntegTest extends Specification {
         messagingServices.stop();
     }
 
-    @IgnoreIf({OperatingSystem.current().isWindows()})
+    @Requires(TestPrecondition.NOT_WINDOWS)
     @Unroll
     def "WorkerProcessBuilder handles workingDir with absolute path length #absolutePathLength"() throws Throwable {
         when:
@@ -85,7 +84,7 @@ class PathLimitationIntegTest extends Specification {
         absolutePathLength << [258, 259, 260]
     }
 
-    @IgnoreIf({OperatingSystem.current().isWindows()})
+    @Requires(TestPrecondition.NOT_WINDOWS)
     @Unroll
     def "JavaProcessBuilder handles workingDir with absolute path length #absolutePathLength"() throws Throwable {
         when:
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 a1ca484..260d689 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
@@ -17,7 +17,6 @@
 package org.gradle.process.internal;
 
 import org.apache.tools.ant.Project;
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.api.internal.DefaultClassPathProvider;
@@ -31,14 +30,14 @@ import org.gradle.cache.internal.*;
 import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler;
 import org.gradle.internal.Actions;
 import org.gradle.internal.id.LongIdGenerator;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.listener.ListenerBroadcast;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.messaging.remote.MessagingServer;
 import org.gradle.messaging.remote.ObjectConnectionBuilder;
 import org.gradle.messaging.remote.internal.MessagingServices;
 import org.gradle.process.internal.child.WorkerProcessClassPathProvider;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.GradleVersion;
 import org.jmock.Expectations;
 import org.jmock.Sequence;
@@ -70,13 +69,13 @@ public class WorkerProcessIntegrationTest {
     private final MessagingServer server = messagingServices.get(MessagingServer.class);
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServices.getInstance().get(ProcessEnvironment.class));
+    private final ProcessMetaDataProvider metaDataProvider = new DefaultProcessMetaDataProvider(NativeServicesTestFixture.getInstance().get(ProcessEnvironment.class));
     private final CacheFactory factory = new DefaultCacheFactory(new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler()));
     private final CacheScopeMapping scopeMapping = new DefaultCacheScopeMapping(tmpDir.getTestDirectory(), null, GradleVersion.current());
-    private final CacheRepository cacheRepository = new DefaultCacheRepository(scopeMapping, CacheUsage.ON, factory);
+    private final CacheRepository cacheRepository = new DefaultCacheRepository(scopeMapping, 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 DefaultWorkerProcessFactory workerFactory = new DefaultWorkerProcessFactory(LogLevel.INFO, server, classPathRegistry, TestFiles.resolver(tmpDir.getTestDirectory()), new LongIdGenerator(), null);
     private final ListenerBroadcast<TestListenerInterface> broadcast = new ListenerBroadcast<TestListenerInterface>(TestListenerInterface.class);
     private final RemoteExceptionListener exceptionListener = new RemoteExceptionListener(broadcast.getSource());
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/testfixtures/ProjectBuilderIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/testfixtures/ProjectBuilderIntegrationTest.groovy
index 080f99d..b569ed0 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/testfixtures/ProjectBuilderIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/testfixtures/ProjectBuilderIntegrationTest.groovy
@@ -19,7 +19,7 @@ package org.gradle.testfixtures
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.junit.Rule
-import org.gradle.test.fixtures.maven.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
 
 class ProjectBuilderIntegrationTest extends AbstractIntegrationSpec {
     @Rule HttpServer server
diff --git a/subprojects/core/src/integTest/resources/org/gradle/api/resource/TextResourceIntegrationTest/shared/build.gradle b/subprojects/core/src/integTest/resources/org/gradle/api/resource/TextResourceIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..67f0da3
--- /dev/null
+++ b/subprojects/core/src/integTest/resources/org/gradle/api/resource/TextResourceIntegrationTest/shared/build.gradle
@@ -0,0 +1,53 @@
+class MyTask extends DefaultTask {
+    @Nested
+    TextResource config
+
+    @OutputFile
+    File output
+
+    @TaskAction
+    void generate() {
+        getOutput().text = getConfig().asString()
+    }
+}
+
+task generateConfigFile {
+    outputs.file "config.txt"
+    doLast {
+        file("config.txt").text = "my config"
+    }
+}
+
+task generateConfigZip(type: Zip) {
+    destinationDir = projectDir
+    archiveName = "config.zip"
+    from generateConfigFile
+}
+
+configurations {
+    sharedConfig
+}
+
+dependencies {
+    sharedConfig generateConfigZip.outputs.files
+}
+
+task stringText(type: MyTask) {
+    config = resources.text.fromString("my config")
+    output = project.file("output.txt")
+}
+
+task fileText(type: MyTask) {
+    config = resources.text.fromFile("config.txt")
+    output = project.file("output.txt")
+}
+
+task fileCollectionText(type: MyTask) {
+    config = resources.text.fromFile(generateConfigFile)
+    output = project.file("output.txt")
+}
+
+task archiveEntryText(type: MyTask) {
+    config = resources.text.fromArchiveEntry(configurations.sharedConfig, "config.txt")
+    output = project.file("output.txt")
+}
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java b/subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
similarity index 100%
rename from subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
rename to subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/DirTransformerTask.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java b/subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
similarity index 100%
rename from subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
rename to subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/GeneratorTask.java
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java b/subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
similarity index 100%
rename from subprojects/integ-test/src/integTest/resources/org/gradle/integtests/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
rename to subprojects/core/src/integTest/resources/org/gradle/api/tasks/IncrementalBuildIntegrationTest/shared/buildSrc/src/main/java/TransformerTask.java
diff --git a/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java b/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
index c3358f8..e3c1401 100644
--- a/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/BuildExceptionReporter.java
@@ -15,13 +15,12 @@
  */
 package org.gradle;
 
-import org.codehaus.groovy.runtime.StackTraceUtils;
 import org.gradle.api.Action;
-import org.gradle.internal.exceptions.LocationAwareException;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.internal.exceptions.FailureResolutionAware;
+import org.gradle.internal.exceptions.LocationAwareException;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.ShowStacktrace;
 import org.gradle.logging.StyledTextOutput;
@@ -41,7 +40,7 @@ import static org.gradle.logging.StyledTextOutput.Style.*;
  */
 public class BuildExceptionReporter extends BuildAdapter implements Action<Throwable> {
     private enum ExceptionStyle {
-        NONE, SANITIZED, FULL
+        NONE, FULL
     }
 
     private final StyledTextOutputFactory textOutputFactory;
@@ -115,10 +114,7 @@ public class BuildExceptionReporter extends BuildAdapter implements Action<Throw
     }
 
     private void reportBuildFailure(String granularity, Throwable failure, FailureDetails details) {
-        if (loggingConfiguration.getShowStacktrace() == ShowStacktrace.ALWAYS || loggingConfiguration.getLogLevel() == LogLevel.DEBUG) {
-            details.exceptionStyle = ExceptionStyle.SANITIZED;
-        }
-        if (loggingConfiguration.getShowStacktrace() == ShowStacktrace.ALWAYS_FULL) {
+        if (loggingConfiguration.getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS) {
             details.exceptionStyle = ExceptionStyle.FULL;
         }
 
@@ -144,16 +140,7 @@ public class BuildExceptionReporter extends BuildAdapter implements Action<Throw
                     if (node == scriptException) {
                         details.details.text(getMessage(scriptException.getCause()));
                     } else {
-                        details.details.format("%n");
-                        StringBuilder prefix = new StringBuilder();
-                        for (int i = 1; i < depth; i++) {
-                            prefix.append("   ");
-                        }
-                        details.details.text(prefix);
-                        prefix.append("  ");
-                        details.details.style(Info).text("> ").style(Normal);
-
-                        final LinePrefixingStyledTextOutput output = new LinePrefixingStyledTextOutput(details.details, prefix);
+                        final LinePrefixingStyledTextOutput output = getLinePrefixingStyledTextOutput();
                         output.text(getMessage(node));
                     }
                 }
@@ -167,6 +154,19 @@ public class BuildExceptionReporter extends BuildAdapter implements Action<Throw
                 public void endChildren() {
                     depth--;
                 }
+
+                private LinePrefixingStyledTextOutput getLinePrefixingStyledTextOutput() {
+                    details.details.format("%n");
+                    StringBuilder prefix = new StringBuilder();
+                    for (int i = 1; i < depth; i++) {
+                        prefix.append("   ");
+                    }
+                    details.details.text(prefix);
+                    prefix.append("  ");
+                    details.details.style(Info).text("> ").style(Normal);
+
+                    return new LinePrefixingStyledTextOutput(details.details, prefix);
+                }
             });
         } else {
             details.details.text(getMessage(failure));
@@ -230,9 +230,6 @@ public class BuildExceptionReporter extends BuildAdapter implements Action<Throw
         switch (details.exceptionStyle) {
             case NONE:
                 break;
-            case SANITIZED:
-                exception = StackTraceUtils.deepSanitize(details.failure);
-                break;
             case FULL:
                 exception = details.failure;
                 break;
diff --git a/subprojects/core/src/main/groovy/org/gradle/BuildLogger.java b/subprojects/core/src/main/groovy/org/gradle/BuildLogger.java
index 254485e..620e1a4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/BuildLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/BuildLogger.java
@@ -68,7 +68,7 @@ public class BuildLogger implements BuildListener, TaskExecutionGraphListener {
     }
 
     public void graphPopulated(TaskExecutionGraph graph) {
-        logger.info(String.format("Tasks to be executed: %s", graph.getAllTasks()));
+        logger.info("Tasks to be executed: {}", graph.getAllTasks());
     }
 
     public void buildFinished(BuildResult result) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/BuildResult.java b/subprojects/core/src/main/groovy/org/gradle/BuildResult.java
index 303b1df..d9bf36e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/BuildResult.java
+++ b/subprojects/core/src/main/groovy/org/gradle/BuildResult.java
@@ -19,7 +19,7 @@ import org.gradle.api.GradleException;
 import org.gradle.api.invocation.Gradle;
 
 /**
- * <p>A {@code BuildResult} packages up the results of a build executed by a {@link GradleLauncher} instance.</p>
+ * <p>A {@code BuildResult} packages up the results of a build executed by a {@link org.gradle.initialization.GradleLauncher} instance.</p>
  */
 public class BuildResult {
     private final Throwable failure;
diff --git a/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java b/subprojects/core/src/main/groovy/org/gradle/CacheUsage.java
deleted file mode 100644
index 0d044a9..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/CacheUsage.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;
-
-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.
- */
- at Deprecated
-public enum CacheUsage {
-    ON, REBUILD;
-
-    public static CacheUsage fromString(String usagestr) {
-        try {
-            return valueOf(usagestr.toUpperCase());
-        } catch (IllegalArgumentException e) {
-            throw new InvalidUserDataException(String.format("Unknown cache usage '%s' specified.", usagestr));
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java b/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
deleted file mode 100644
index 442e50b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/GradleLauncher.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle;
-
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.initialization.GradleLauncherFactory;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.internal.service.ServiceRegistryBuilder;
-import org.gradle.internal.service.scopes.GlobalScopeServices;
-import org.gradle.logging.LoggingServiceRegistry;
-import org.gradle.util.DeprecationLogger;
-
-/**
- * <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 (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>
- *
- * <li>Optionally create a {@link StartParameter} instance and configure it with the desired properties. The properties
- * of {@code StartParameter} generally correspond to the command-line options of Gradle. You can use {@link
- * #createStartParameter(String...)} to create a {@link StartParameter} from a set of command-line options.</li>
- *
- * <li>Obtain a {@code GradleLauncher} instance by calling {@link #newInstance}, passing in the {@code StartParameter},
- * or an array of Strings that will be treated as command line arguments.</li>
- *
- * <li>Optionally add one or more listeners to the {@code GradleLauncher}.</li>
- *
- * <li>Call {@link #run} to execute the build. This will return a {@link BuildResult}. Note that if the build fails, the
- * resulting exception will be contained in the {@code BuildResult}.</li>
- *
- * <li>Query the build result. You might want to call {@link BuildResult#rethrowFailure()} to rethrow any build
- * failure.</li>
- *
- * </ol>
- *
- * @deprecated Use the tooling API instead.
- */
- at Deprecated
-public abstract class GradleLauncher {
-
-    private static GradleLauncherFactory factory;
-
-    /**
-     * <p>Executes the build for this {@code GradleLauncher} instance and returns the result. Note that when the build
-     * fails, the exception is available using {@link org.gradle.BuildResult#getFailure()}.</p>
-     *
-     * @return The result. Never returns null.
-     */
-    public abstract BuildResult run();
-
-    /**
-     * Evaluates the settings and all the projects. The information about available tasks and projects is accessible via
-     * the {@link org.gradle.api.invocation.Gradle#getRootProject()} object.
-     *
-     * @return The result. Never returns null.
-     */
-    public abstract BuildResult getBuildAnalysis();
-
-    /**
-     * Evaluates the settings and all the projects. The information about available tasks and projects is accessible via
-     * the {@link org.gradle.api.invocation.Gradle#getRootProject()} object. Fills the execution plan without running
-     * the build. The tasks to be executed tasks are available via {@link org.gradle.api.invocation.Gradle#getTaskGraph()}.
-     *
-     * @return The result. Never returns null.
-     */
-    public abstract BuildResult getBuildAndRunAnalysis();
-
-    /**
-     * Returns a {@code GradleLauncher} instance based on the passed start parameter.
-     *
-     * @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.
-     */
-    @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();
-    }
-
-    private static synchronized GradleLauncherFactory doGetFactory() {
-        if (factory == null) {
-            factory = ServiceRegistryBuilder.builder()
-                    .displayName("Global services")
-                    .parent(LoggingServiceRegistry.newProcessLogging())
-                    .parent(NativeServices.getInstance())
-                    .provider(new GlobalScopeServices(false))
-                    .build()
-                    .get(GradleLauncherFactory.class);
-        }
-        return factory;
-    }
-
-    /**
-     * Returns a {@code GradleLauncher} instance based on the passed command line syntax arguments.
-     *
-     * @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) {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("GradleLauncher.newInstance()");
-        GradleLauncherFactory gradleLauncherFactory = doGetFactory();
-        return gradleLauncherFactory.newInstance(gradleLauncherFactory.createStartParameter(commandLineArgs));
-    }
-
-    /**
-     * Returns a {@code StartParameter} object out of command line syntax arguments. Each command line option has an
-     * associated field in the {@code StartParameter} object.
-     *
-     * @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.
-     */
-    @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;
-    }
-
-    /**
-     * <p>Adds a listener to this build instance. The listener is notified of events which occur during the execution of
-     * the build. See {@link org.gradle.api.invocation.Gradle#addListener(Object)} for supported listener types.</p>
-     *
-     * @param listener The listener to add. Has no effect if the listener has already been added.
-     */
-    public abstract void addListener(Object listener);
-
-    /**
-     * Use the given listener. See {@link org.gradle.api.invocation.Gradle#useLogger(Object)} for details.
-     *
-     * @param logger The logger to use.
-     */
-    public abstract void useLogger(Object logger);
-
-    /**
-     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to
-     * standard output by Gradle's logging system
-     *
-     * @param listener The listener to add. Has no effect if the listener has already been added.
-     */
-    public abstract void addStandardOutputListener(StandardOutputListener listener);
-
-    /**
-     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to standard error by Gradle's logging system
-     *
-     * @param listener The listener to add. Has no effect if the listener has already been added.
-     */
-    public abstract void addStandardErrorListener(StandardOutputListener listener);
-
-    /**
-     * Returns the {@link StartParameter} used by this build instance.
-     * @return The parameter. Never null.
-     */
-    public abstract StartParameter getStartParameter();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/RefreshOptions.java b/subprojects/core/src/main/groovy/org/gradle/RefreshOptions.java
deleted file mode 100644
index 6e75e3e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/RefreshOptions.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;
-
-import org.gradle.cli.CommandLineArgumentException;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The options supplied by a user to refresh dependencies and other external resources.
- * @deprecated Use {@link StartParameter#setRefreshDependencies(boolean)} instead.
- */
- at Deprecated
-public class RefreshOptions implements Serializable {
-    public static final RefreshOptions NONE = new RefreshOptions(Collections.<Option>emptyList());
-
-    private final List<Option> options;
-
-    public RefreshOptions(List<Option> options) {
-        this.options = options;
-    }
-
-    public static RefreshOptions fromCommandLineOptions(List<String> optionNames) {
-        if (optionNames.size() == 0) {
-            return new RefreshOptions(Arrays.asList(Option.values()));
-        } 
-        
-        List<Option> options = new ArrayList<Option>();
-        for (String optionName : optionNames) {
-            try {
-                options.add(Option.valueOf(optionName.toUpperCase()));
-            } catch (IllegalArgumentException e) {
-                throw new CommandLineArgumentException(String.format("Unknown refresh option '%s' specified.", optionName));
-            }
-        }
-        return new RefreshOptions(options);
-    }
-
-    public boolean refreshDependencies() {
-        return options.contains(Option.DEPENDENCIES);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        RefreshOptions that = (RefreshOptions) o;
-        return options.equals(that.options);
-
-    }
-
-    @Override
-    public int hashCode() {
-        return options.hashCode();
-    }
-
-    /**
-     * The set of allowable options.
-     */
-    public enum Option {
-        DEPENDENCIES
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/StartParameter.java b/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
index 577e073..207243a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/StartParameter.java
@@ -26,9 +26,8 @@ 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.internal.DefaultTaskExecutionRequest;
 import org.gradle.logging.LoggingConfiguration;
-import org.gradle.util.DeprecationLogger;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -39,31 +38,28 @@ import java.util.*;
  * <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>
  *
- * @see GradleLauncher
+ * @see org.gradle.initialization.GradleLauncher
  */
 public class StartParameter extends LoggingConfiguration implements Serializable {
-    public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home";
+    public static final String GRADLE_USER_HOME_PROPERTY_KEY = BuildLayoutParameters.GRADLE_USER_HOME_PROPERTY_KEY;
+
     /**
      * The default user home directory.
      */
-    public static final File DEFAULT_GRADLE_USER_HOME = new File(SystemProperties.getUserHome() + "/.gradle");
+    public static final File DEFAULT_GRADLE_USER_HOME = new BuildLayoutParameters().getGradleUserHomeDir();
 
-    private List<String> taskNames = new ArrayList<String>();
+    private List<TaskExecutionRequest> taskRequests = new ArrayList<TaskExecutionRequest>();
     private Set<String> excludedTaskNames = new LinkedHashSet<String>();
     private boolean buildProjectDependencies = true;
     private File currentDir;
     private File projectDir;
-    private String projectPath;
     private boolean searchUpwards;
     private Map<String, String> projectProperties = new HashMap<String, String>();
     private Map<String, String> systemPropertiesArgs = new HashMap<String, String>();
     private File gradleUserHomeDir;
     private File gradleHomeDir;
-    private CacheUsage cacheUsage = CacheUsage.ON;
     private File settingsFile;
     private boolean useEmptySettings;
     private File buildFile;
@@ -76,8 +72,9 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     private File projectCacheDir;
     private boolean refreshDependencies;
     private boolean recompileScripts;
-    private int parallelThreadCount;
+    private boolean parallelProjectExecution;
     private boolean configureOnDemand;
+    private int maxWorkerCount;
 
     /**
      * Sets the project's cache location. Set to null to use the default location.
@@ -101,10 +98,12 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     public StartParameter() {
         gradleHomeDir = new DefaultModuleRegistry().getGradleHome();
 
-        BuildLayoutParameters layoutDefaults = new BuildLayoutParameters();
-        searchUpwards = layoutDefaults.getSearchUpwards();
-        currentDir = layoutDefaults.getProjectDir();
-        gradleUserHomeDir = layoutDefaults.getGradleUserHomeDir();
+        BuildLayoutParameters layoutParameters = new BuildLayoutParameters();
+        searchUpwards = layoutParameters.getSearchUpwards();
+        currentDir = layoutParameters.getCurrentDir();
+        projectDir = layoutParameters.getProjectDir();
+        gradleUserHomeDir = layoutParameters.getGradleUserHomeDir();
+        maxWorkerCount = Runtime.getRuntime().availableProcessors();
     }
 
     /**
@@ -120,10 +119,9 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         prepareNewBuild(p);
         p.buildFile = buildFile;
         p.projectDir = projectDir;
-        p.projectPath = projectPath;
         p.settingsFile = settingsFile;
         p.useEmptySettings = useEmptySettings;
-        p.taskNames = new ArrayList<String>(taskNames);
+        p.taskRequests = new ArrayList<TaskExecutionRequest>(taskRequests);
         p.excludedTaskNames = new LinkedHashSet<String>(excludedTaskNames);
         p.buildProjectDependencies = buildProjectDependencies;
         p.currentDir = currentDir;
@@ -149,9 +147,8 @@ public class StartParameter extends LoggingConfiguration implements Serializable
 
     protected StartParameter prepareNewBuild(StartParameter p) {
         p.gradleUserHomeDir = gradleUserHomeDir;
-        p.cacheUsage = cacheUsage;
         p.setLogLevel(getLogLevel());
-        p.setColorOutput(isColorOutput());
+        p.setConsoleOutput(getConsoleOutput());
         p.setShowStacktrace(getShowStacktrace());
         p.profile = profile;
         p.continueOnFailure = continueOnFailure;
@@ -159,8 +156,9 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         p.rerunTasks = rerunTasks;
         p.recompileScripts = recompileScripts;
         p.refreshDependencies = refreshDependencies;
-        p.parallelThreadCount = parallelThreadCount;
+        p.parallelProjectExecution = parallelProjectExecution;
         p.configureOnDemand = configureOnDemand;
+        p.maxWorkerCount = maxWorkerCount;
         return p;
     }
 
@@ -213,16 +211,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
-     * Deprecated. Use {@link #useEmptySettings()}.
-     *
-     * @deprecated use {@link #useEmptySettings()}
-     */
-    @Deprecated
-    public StartParameter useEmptySettingsScript() {
-        return useEmptySettings();
-    }
-
-    /**
      * Returns whether an empty settings script will be used regardless of whether one exists in the default location.
      *
      * @return Whether to use empty settings or not.
@@ -233,10 +221,15 @@ public class StartParameter extends LoggingConfiguration implements Serializable
 
     /**
      * Returns the names of the tasks to execute in this build. When empty, the default tasks for the project will be executed.
+     * If {@link TaskExecutionRequest}s are set for this build then names from these task parameters are returned.
      *
      * @return the names of the tasks to execute in this build. Never returns null.
      */
     public List<String> getTaskNames() {
+        List<String> taskNames = Lists.newArrayList();
+        for (TaskExecutionRequest taskRequest : taskRequests) {
+            taskNames.addAll(taskRequest.getArgs());
+        }
         return taskNames;
     }
 
@@ -247,7 +240,32 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * @param taskNames the names of the tasks to execute in this build.
      */
     public void setTaskNames(Iterable<String> taskNames) {
-        this.taskNames = Lists.newArrayList(taskNames);
+        if (taskNames == null) {
+            this.taskRequests = Collections.emptyList();
+        } else {
+            this.taskRequests = Arrays.<TaskExecutionRequest>asList(new DefaultTaskExecutionRequest(taskNames));
+        }
+    }
+
+    /**
+     * Returns the tasks to execute in this build. When empty, the default tasks for the project will be executed.
+     *
+     * @return the tasks to execute in this build. Never returns null.
+     */
+    @Incubating
+    public List<TaskExecutionRequest> getTaskRequests() {
+        return taskRequests;
+    }
+
+    /**
+     * <p>Sets the task parameters to execute in this build. Set to an empty list, to execute the default tasks for the project. The tasks are executed in the order provided, subject to dependency
+     * between the tasks.</p>
+     *
+     * @param taskParameters the tasks to execute in this build.
+     */
+    @Incubating
+    public void setTaskRequests(Iterable<? extends TaskExecutionRequest> taskParameters) {
+        this.taskRequests = Lists.newArrayList(taskParameters);
     }
 
     /**
@@ -286,7 +304,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         if (currentDir != null) {
             this.currentDir = GFileUtils.canonicalise(currentDir);
         } else {
-            this.currentDir = new BuildLayoutParameters().getProjectDir();
+            this.currentDir = new BuildLayoutParameters().getCurrentDir();
         }
     }
 
@@ -315,24 +333,6 @@ 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());
-        return merged;
-    }
-
-    /**
      * Returns the directory to use as the user home directory.
      *
      * @return The home directory.
@@ -347,7 +347,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * @param gradleUserHomeDir The home directory. May be null.
      */
     public void setGradleUserHomeDir(File gradleUserHomeDir) {
-        this.gradleUserHomeDir = gradleUserHomeDir == null ? DEFAULT_GRADLE_USER_HOME : GFileUtils.canonicalise(gradleUserHomeDir);
+        this.gradleUserHomeDir = gradleUserHomeDir == null ? new BuildLayoutParameters().getGradleUserHomeDir() : GFileUtils.canonicalise(gradleUserHomeDir);
     }
 
     /**
@@ -367,24 +367,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
         return this;
     }
 
-    /**
-     *  Returns the configured CacheUsage.
-     *  @deprecated Use {@link #isRecompileScripts} and/or {@link #isRerunTasks} instead.
-      */
-    @Deprecated
-    public CacheUsage getCacheUsage() {
-        return cacheUsage;
-    }
-
-    /**
-     *  Sets the Cache usage.
-     *  @deprecated Use {@link #setRecompileScripts} and/or {@link #setRerunTasks} instead.
-      */
-    @Deprecated
-    public void setCacheUsage(CacheUsage cacheUsage) {
-        this.cacheUsage = cacheUsage;
-    }
-
     public boolean isDryRun() {
         return dryRun;
     }
@@ -394,27 +376,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
-     * Returns task optimization disabled flag.
-     *
-     * @deprecated Use {@link #isRerunTasks} instead.
-      */
-    @Deprecated
-    public boolean isNoOpt() {
-        return rerunTasks;
-    }
-
-   /**
-    * Get task optimization disabled.
-    *
-    * @param noOpt The boolean value for disabling task optimization.
-    * @deprecated Use {@link #setRerunTasks(boolean)} instead.
-    */
-    @Deprecated
-    public void setNoOpt(boolean noOpt) {
-        this.rerunTasks = noOpt;
-    }
-
-    /**
      * Sets the settings file to use for the build. Use null to use the default settings file.
      *
      * @param settingsFile The settings file to use. May be null.
@@ -513,26 +474,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
-     * Sets the project path to use to select the default project. Use null to use the default criteria for selecting the default project.
-     *
-     * @param projectPath The project path. May be null.
-     */
-    @Incubating
-    public void setProjectPath(String projectPath) {
-        this.projectPath = projectPath;
-    }
-
-    /**
-     * Returns the project path to use to select the default project.
-     *
-     * @return The project path. May be null when the project path is not used to select the default project.
-     */
-    @Incubating
-    public String getProjectPath() {
-        return projectPath;
-    }
-
-    /**
      * Specifies if a profile report should be generated.
      *
      * @param profile true if a profile report should be generated
@@ -577,24 +518,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     }
 
     /**
-     * Supplies the refresh options to use for the build.
-     * @deprecated Use {@link #setRefreshDependencies(boolean)} instead.
-     */
-    @Deprecated
-    public void setRefreshOptions(RefreshOptions refreshOptions) {
-        this.refreshDependencies = refreshOptions.refreshDependencies();
-    }
-
-    /**
-     * Returns the refresh options used for the build.
-     * @deprecated Use {@link #isRefreshDependencies()} instead.
-     */
-    @Deprecated
-    public RefreshOptions getRefreshOptions() {
-        return isRefreshDependencies() ? new RefreshOptions(Arrays.asList(RefreshOptions.Option.DEPENDENCIES)) : RefreshOptions.NONE;
-    }
-
-    /**
      * Specifies whether the dependencies should be refreshed..
      */
     public boolean isRefreshDependencies() {
@@ -642,18 +565,85 @@ public class StartParameter extends LoggingConfiguration implements Serializable
      * <0: Automatically determine the optimal number of executors to use.
      *  0: Do not use parallel execution.
      * >0: Use this many parallel execution threads.
+     *
+     * @deprecated Use getMaxWorkerCount or isParallelProjectExecutionEnabled instead.
+     *
+     * @see #getMaxWorkerCount()
+     * @see #isParallelProjectExecutionEnabled()
      */
+    @Deprecated
     public int getParallelThreadCount() {
-        return parallelThreadCount;
+        if (isParallelProjectExecutionEnabled()) {
+            return getMaxWorkerCount();
+        }
+        return 0;
     }
 
     /**
      * Specifies the number of parallel threads to use for build execution.
-     * 
+     *
      * @see #getParallelThreadCount()
      */
+    @Deprecated
     public void setParallelThreadCount(int parallelThreadCount) {
-        this.parallelThreadCount = parallelThreadCount;
+        setParallelProjectExecutionEnabled(parallelThreadCount!=0);
+
+        if (parallelThreadCount < 1) {
+            setMaxWorkerCount(Runtime.getRuntime().availableProcessors());
+        } else {
+            setMaxWorkerCount(parallelThreadCount);
+        }
+    }
+
+    /**
+     * Returns true if parallel project execution is enabled.
+     *
+     * @see #getParallelThreadCount()
+     */
+    @Incubating
+    public boolean isParallelProjectExecutionEnabled() {
+        return parallelProjectExecution;
+    }
+
+    /**
+     * Enables/disables parallel project execution.
+     *
+     * @see #isParallelProjectExecutionEnabled()
+     */
+    @Incubating
+    public void setParallelProjectExecutionEnabled(boolean parallelProjectExecution) {
+        this.parallelProjectExecution = parallelProjectExecution;
+    }
+
+    /**
+     * Returns the maximum number of concurrent workers used for underlying build operations.
+     *
+     * Workers can be threads, processes or whatever Gradle considers a "worker".
+     *
+     * Defaults to the number of processors available to the Java virtual machine.
+     *
+     * @see java.lang.Runtime#availableProcessors()
+     *
+     * @return maximum number of concurrent workers, always >= 1.
+     */
+    @Incubating
+    public int getMaxWorkerCount() {
+        return maxWorkerCount;
+    }
+
+    /**
+     * Specifies the maximum number of concurrent workers used for underlying build operations.
+     *
+     * @throws IllegalArgumentException if {@code maxWorkerCount} is < 1
+     * @see #getMaxWorkerCount()
+     */
+    @Incubating
+    public void setMaxWorkerCount(int maxWorkerCount) {
+        if (maxWorkerCount < 1) {
+            throw new IllegalArgumentException("Max worker count must be > 0");
+        } else {
+            this.maxWorkerCount = maxWorkerCount;
+        }
     }
 
     /**
@@ -667,7 +657,7 @@ public class StartParameter extends LoggingConfiguration implements Serializable
     @Override
     public String toString() {
         return "StartParameter{"
-                + "taskNames=" + taskNames
+                + "taskRequests=" + taskRequests
                 + ", excludedTaskNames=" + excludedTaskNames
                 + ", currentDir=" + currentDir
                 + ", searchUpwards=" + searchUpwards
@@ -675,7 +665,6 @@ public class StartParameter extends LoggingConfiguration implements Serializable
                 + ", systemPropertiesArgs=" + systemPropertiesArgs
                 + ", gradleUserHomeDir=" + gradleUserHomeDir
                 + ", gradleHome=" + gradleHomeDir
-                + ", cacheUsage=" + cacheUsage
                 + ", logLevel=" + getLogLevel()
                 + ", showStacktrace=" + getShowStacktrace()
                 + ", buildFile=" + buildFile
@@ -685,8 +674,9 @@ public class StartParameter extends LoggingConfiguration implements Serializable
                 + ", recompileScripts=" + recompileScripts
                 + ", offline=" + offline
                 + ", refreshDependencies=" + refreshDependencies
-                + ", parallelThreadCount=" + parallelThreadCount
+                + ", parallelProjectExecution=" + parallelProjectExecution
                 + ", configureOnDemand=" + configureOnDemand
+                + ", maxWorkerCount=" + maxWorkerCount
                 + '}';
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
index 6e7775e..c4e37fc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionLogger.java
@@ -47,7 +47,7 @@ public class TaskExecutionLogger implements TaskExecutionListener {
 
         ProgressLogger currentTask = progressLoggerFactory.newOperation(TaskExecutionLogger.class, parentLoggerPovider.getLogger());
         String displayName = getDisplayName(task);
-        currentTask.setDescription(String.format("Execute %s", displayName));
+        currentTask.setDescription("Execute ".concat(displayName));
         currentTask.setShortDescription(displayName);
         currentTask.setLoggingHeader(displayName);
         currentTask.started();
diff --git a/subprojects/core/src/main/groovy/org/gradle/TaskExecutionRequest.java b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionRequest.java
new file mode 100644
index 0000000..0779432
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/TaskExecutionRequest.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;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+import java.util.List;
+
+/**
+ * A request to execute some tasks, along with an optional project path context to provide information necessary to select the tasks
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface TaskExecutionRequest {
+    /**
+     * The arguments to use to select and optionally configure the tasks, as if provided on the command-line.
+     *
+     * @return task name.
+     */
+    List<String> getArgs();
+
+    /**
+     * Project path associated with this task request if any.
+     *
+     * @return project path or {@code null} to use the default project path.
+     */
+    @Nullable String getProjectPath();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/AntBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/AntBuilder.java
index 76b657f..6c74ea0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/AntBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/AntBuilder.java
@@ -45,6 +45,20 @@ public abstract class AntBuilder extends groovy.util.AntBuilder {
     public abstract void importBuild(Object antBuildFile);
 
     /**
+     * Imports an Ant build into the associated Gradle project, potentially providing alternative names for Gradle tasks that correspond to Ant targets.
+     * <p>
+     * For each Ant target that is to be converted to a Gradle task, the given {@code taskNamer} receives the Ant target name as input
+     * and is expected to return the desired name for the corresponding Gradle task.
+     * The transformer may be called multiple times with the same input.
+     * Implementations should ensure uniqueness of the return value for a distinct input.
+     * That is, no two inputs should yield the same return value.
+     *  @param antBuildFile The build file. This is resolved as per {@link org.gradle.api.Project#file(Object)}.
+     * @param taskNamer A transformer that calculates the name of the Gradle task for a corresponding Ant target.
+     */
+    @Incubating
+    public abstract void importBuild(Object antBuildFile, Transformer<? extends String, ? super String> taskNamer);
+
+    /**
      * Returns this AntBuilder. Useful when you need to pass this builder to methods from within closures.
      * @return this
      */
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/BuildCancelledException.java b/subprojects/core/src/main/groovy/org/gradle/api/BuildCancelledException.java
new file mode 100644
index 0000000..fea22fe
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/BuildCancelledException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api;
+
+/**
+ * <p>A <code>BuildCancelledException</code> is thrown when a build is interrupted due to cancellation request.
+ *
+ * @since 2.1
+ */
+ at Incubating
+public class BuildCancelledException extends GradleException {
+    public BuildCancelledException() {
+        this("Build cancelled.");
+    }
+
+    public BuildCancelledException(String message) {
+        super(message);
+    }
+
+    public BuildCancelledException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/BuildableModelElement.java b/subprojects/core/src/main/groovy/org/gradle/api/BuildableModelElement.java
new file mode 100644
index 0000000..c4a73fe
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/BuildableModelElement.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;
+
+/**
+ * A model element that is directly buildable.
+ * Such an element mirrors a specified lifecycle task in the task graph, and can accept dependencies which are then associated with the lifecycle task.
+ */
+ at Incubating
+public interface BuildableModelElement extends Buildable {
+    /**
+     * Returns the 'lifecycle' task associated with the construction of this element.
+     */
+    @Nullable
+    Task getBuildTask();
+
+    /**
+     * Associates a 'lifecycle' task with the construction of this element.
+     */
+    void setBuildTask(Task lifecycleTask);
+
+    /**
+     * Adds a task that is required for the construction of this element.
+     * A task added this way is then added as a dependency of the associated lifecycle task.
+     */
+    void builtBy(Object... tasks);
+
+    boolean hasBuildDependencies();
+}
\ No newline at end of file
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 c41c01e..c4ebf00 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/DefaultTask.java
@@ -18,10 +18,12 @@ package org.gradle.api;
 
 import org.gradle.api.internal.AbstractTask;
 import org.gradle.api.internal.NoConventionMapping;
+import org.gradle.api.tasks.ParallelizableTask;
 
 /**
  * {@code DefaultTask} is the standard {@link Task} implementation. You can extend this to implement your own task types.
  */
 @NoConventionMapping
+ at ParallelizableTask
 public class DefaultTask extends AbstractTask {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java b/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java
deleted file mode 100644
index 8391b3f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/IllegalOperationAtExecutionTimeException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api;
-
-/**
- * A <code>IllegalOperationAtExecutionTimeException</code> is thrown if you try to trigger an operation at runtime,
- * which is only allowed at configuration time.
- *
- * @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/NamedDomainObjectCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
index d5d5e82..49fb45d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/NamedDomainObjectCollection.java
@@ -90,6 +90,8 @@ public interface NamedDomainObjectCollection<T> extends DomainObjectCollection<T
 
     /**
      * An object that represents the naming strategy used to name objects of this collection.
+     *
+     * @return Object representing the naming strategy.
      */
     Namer<T> getNamer();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/PolymorphicDomainObjectContainer.java b/subprojects/core/src/main/groovy/org/gradle/api/PolymorphicDomainObjectContainer.java
index 8e85746..391237f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/PolymorphicDomainObjectContainer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/PolymorphicDomainObjectContainer.java
@@ -15,12 +15,15 @@
  */
 package org.gradle.api;
 
+import org.gradle.internal.HasInternalProtocol;
+
 /**
  * A {@link NamedDomainObjectContainer} that allows to create domain objects with different types.
  *
  * @param <T> the (base) type of domain objects in the container
  */
 @Incubating
+ at HasInternalProtocol
 public interface PolymorphicDomainObjectContainer<T> extends NamedDomainObjectContainer<T> {
     /**
      * Creates a domain object with the specified name and type, and adds it to the container.
@@ -71,4 +74,13 @@ public interface PolymorphicDomainObjectContainer<T> extends NamedDomainObjectCo
      * or the container does not support creating a domain object with the specified type
      */
     <U extends T> U create(String name, Class<U> type, Action<? super U> configuration) throws InvalidUserDataException;
+
+    /**
+     * Creates a regular container that wraps the polymorphic container presenting all elements of a specified type.
+     *
+     * @param type the type of the container elements
+     * @param <U> the type of the container elements
+     * @return a {@link NamedDomainObjectContainer} providing access to elements of type U.
+     */
+    <U extends T> NamedDomainObjectContainer<U> containerWithType(Class<U> type);
 }
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 ecde8ef..2243744 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Project.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Project.java
@@ -37,6 +37,8 @@ import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.TaskContainer;
 import org.gradle.api.tasks.WorkResult;
 import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 
 import java.io.File;
 import java.net.URI;
@@ -65,7 +67,7 @@ import java.util.Set;
  * <code>Project</code> instances.</li>
  *
  * <li>Finally, evaluate each <code>Project</code> by executing its <code>{@value #DEFAULT_BUILD_FILE}</code> file, if
- * present, against the project. The project are evaluated in breadth-wise order, such that a project is evaluated
+ * present, against the project. The projects are evaluated in breadth-wise order, such that a project is evaluated
  * before its child projects. This order can be overridden by calling <code>{@link #evaluationDependsOnChildren()}</code> or by adding an
  * explicit evaluation dependency using <code>{@link #evaluationDependsOn(String)}</code>.</li>
  *
@@ -75,7 +77,7 @@ import java.util.Set;
  *
  * <p>A project is essentially a collection of {@link Task} objects. Each task performs some basic piece of work, such
  * as compiling classes, or running unit tests, or zipping up a WAR file. You add tasks to a project using one of the
- * {@code add()} methods on {@link TaskContainer}, such as {@link TaskContainer#add(String)}.  You can locate existing
+ * {@code create()} methods on {@link TaskContainer}, such as {@link TaskContainer#create(String)}.  You can locate existing
  * tasks using one of the lookup methods on {@link TaskContainer}, such as {@link org.gradle.api.tasks.TaskCollection#getByName(String)}.</p>
  *
  * <h3>Dependencies</h3>
@@ -94,6 +96,13 @@ import java.util.Set;
  * <p>Projects are arranged into a hierarchy of projects. A project has a name, and a fully qualified path which
  * uniquely identifies it in the hierarchy.</p>
  *
+ * <h3>Plugins</h3>
+ *
+ * <p>
+ * Plugins can be used to modularise and reuse project configuration.
+ * Plugins can be applied using the {@link PluginAware#apply(java.util.Map)} method, or by using the {@link org.gradle.plugin.use.PluginDependenciesSpec plugins script block}.
+ * </p>
+ *
  * <a name="properties"/> <h3>Properties</h3>
  *
  * <p>Gradle executes the project's build file against the <code>Project</code> instance to configure the project. Any
@@ -118,36 +127,35 @@ import java.util.Set;
  * <code>rootProject</code> property.  The properties of this scope are readable or writable depending on the presence
  * of the corresponding getter or setter method.</li>
  *
- * <li>The <em>additional</em> properties of the project.  Each project maintains a map of additional properties, which
- * can contain any arbitrary name -> value pair.  The properties of this scope are readable and writable.</li>
+ * <li>The <em>extra</em> properties of the project.  Each project maintains a map of extra properties, which
+ * can contain any arbitrary name -> value pair.  Once defined, the properties of this scope are readable and writable.
+ * See <a href="#extraproperties">extra properties</a> for more details.</li>
  *
- * <li>The <em>convention</em> properties added to the project by each {@link Plugin} applied to the project. A {@link
- * Plugin} can add properties and methods to a project through the project's {@link Convention} object.  The properties
- * of this scope may be readable or writable, depending on the convention objects.</li>
+ * <li>The <em>extensions</em> added to the project by the plugins. Each extension is available as a read-only property with the same name as the extension.</li>
+ *
+ * <li>The <em>convention</em> properties added to the project by the plugins. A plugin can add properties and methods
+ * to a project through the project's {@link Convention} object.  The properties of this scope may be readable or writable, depending on the convention objects.</li>
  *
  * <li>The tasks of the project.  A task is accessible by using its name as a property name.  The properties of this
  * scope are read-only. For example, a task called <code>compile</code> is accessible as the <code>compile</code>
  * property.</li>
  *
- * <li>The additional properties and convention properties of the project's parent project, recursively up to the root
+ * <li>The extra properties and convention properties inherited from the project's parent, recursively up to the root
  * project. The properties of this scope are read-only.</li>
  *
  * </ul>
  *
  * <p>When reading a property, the project searches the above scopes in order, and returns the value from the first
- * scope it finds the property in.  See {@link #property(String)} for more details.</p>
+ * scope it finds the property in. If not found, an exception is thrown. See {@link #property(String)} for more details.</p>
  *
  * <p>When writing a property, the project searches the above scopes in order, and sets the property in the first scope
- * it finds the property in. If not found, the project adds the property to its map of additional properties.  For the
- * next few releases a deprecation warning will be issued when trying to set a property that does not exist. Dynamic
- * properties will eventually be removed entirely, meaning that this will be a fatal error in future versions of Gradle.
- * See Extra Properties to learn how to add properties dynamically. </p>
+ * it finds the property in. If not found, an exception is thrown. See {@link #setProperty(String, Object)} for more details.</p>
  *
  * <a name="extraproperties"/> <h4>Extra Properties</h4>
  *
- * All extra properties must be created through the "ext" namespace. Once extra properties have been created,
- * they are available on the owning object (in the below case the Project, Task, and sub-projects respectively) and can
- * be read and changed. It's only the initial declaration that needs to be done via the namespace.
+ * All extra properties must be defined through the "ext" namespace. Once an extra property has been defined,
+ * it is available directly on the owning object (in the below case the Project, Task, and sub-projects respectively) and can
+ * be read and updated. Only the initial declaration that needs to be done via the namespace.
  *
  * <pre>
  * project.ext.prop1 = "foo"
@@ -176,15 +184,18 @@ import java.util.Set;
  *
  * <li>The build file. The project searches for a matching method declared in the build file.</li>
  *
- * <li>The <em>convention</em> methods added to the project by each {@link Plugin} applied to the project. A {@link
- * Plugin} can add properties and method to a project through the project's {@link Convention} object.</li>
+ * <li>The <em>extensions</em> added to the project by the plugins. Each extension is available as a method which takes
+ * a closure or {@link org.gradle.api.Action} as a parameter.</li>
+ *
+ * <li>The <em>convention</em> methods added to the project by the plugins. A plugin can add properties and method to
+ * a project through the project's {@link Convention} object.</li>
  *
  * <li>The tasks of the project. A method is added for each task, using the name of the task as the method name and
- * taking a single closure parameter. The method calls the {@link Task#configure(groovy.lang.Closure)} method for the
+ * taking a single closure or {@link org.gradle.api.Action} parameter. The method calls the {@link Task#configure(groovy.lang.Closure)} method for the
  * associated task with the provided closure. For example, if the project has a task called <code>compile</code>, then a
  * method is added with the following signature: <code>void compile(Closure configureClosure)</code>.</li>
  *
- * <li>The parent project, recursively up to the root project.</li>
+ * <li>The methods of the parent project, recursively up to the root project.</li>
  *
  * </ul>
  */
@@ -285,7 +296,7 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
 
     /**
      * <p>Returns the group of this project. Gradle always uses the {@code toString()} value of the group. The group
-     * defaults to the path with dots a separators.</p>
+     * defaults to the path with dots as separators.</p>
      *
      * @return The group of this project. Never returns null.
      */
@@ -334,19 +345,12 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     /**
      * <p>Returns the direct children of this project.</p>
      *
-     * @return A map from child project name to child project. Returns an empty map if this this project does not have
+     * @return A map from child project name to child project. Returns an empty map if this project does not have
      *         any children.
      */
     Map<String, Project> getChildProjects();
 
     /**
-     * <p>Returns the set of projects which this project depends on.</p>
-     *
-     * @return The set of projects. Returns an empty set if this project depends on no projects.
-     */
-    Set<Project> getDependsOnProjects();
-
-    /**
      * <p>Sets a property of this project.  This method searches for a property with the given name in the following
      * locations, and sets the property on the first location where it finds the property.</p>
      *
@@ -357,17 +361,16 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      * <li>The project's {@link Convention} object.  For example, the <code>srcRootName</code> java plugin
      * property.</li>
      *
-     * <li>The project's additional properties.</li>
+     * <li>The project's extra properties.</li>
      *
      * </ol>
      *
-     * <p>If the property is not found in any of these locations, it is added to the project's additional
-     * properties.</p>
+     * If the property is not found, a {@link groovy.lang.MissingPropertyException} is thrown.
      *
      * @param name The name of the property
      * @param value The value of the property
      */
-    void setProperty(String name, Object value);
+    void setProperty(String name, Object value) throws MissingPropertyException;
 
     /**
      * <p>Returns this project. This method is useful in build files to explicitly access project properties and
@@ -514,27 +517,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     void defaultTasks(String... defaultTasks);
 
     /**
-     * <p>Declares that this project has an execution dependency on the project with the given path.</p>
-     *
-     * @deprecated Use {@link Task#dependsOn(Object...)} instead.
-     * @param path The path of the project which this project depends on.
-     * @throws UnknownProjectException If no project with the given path exists.
-     */
-    @Deprecated
-    void dependsOn(String path) throws UnknownProjectException;
-
-    /**
-     * <p>Declares that this project has an execution dependency on the project with the given path.</p>
-     *
-     * @deprecated Use {@link Task#dependsOn(Object...)} instead.
-     * @param path The path of the project which this project depends on.
-     * @param evaluateDependsOnProject If true, adds an evaluation dependency.
-     * @throws UnknownProjectException If no project with the given path exists.
-     */
-    @Deprecated
-    void dependsOn(String path, boolean evaluateDependsOnProject) throws UnknownProjectException;
-
-    /**
      * <p>Declares that this project has an evaluation dependency on the project with the given path.</p>
      *
      * @param path The path of the project which this project depends on.
@@ -550,36 +532,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     void evaluationDependsOnChildren();
 
     /**
-     * <p>Declares that all child projects of this project have an execution dependency on this project.</p>
-     *
-     * @deprecated Use {@link Task#dependsOn(Object...)} instead.
-     * @return this project.
-     */
-    @Deprecated
-    Project childrenDependOnMe();
-
-    /**
-     * <p>Declares that this project has an execution dependency on each of its child projects.</p>
-     *
-     * @deprecated Use {@link Task#dependsOn(Object...)} instead.
-     * @return this project.
-     */
-    @Deprecated
-    Project dependsOnChildren();
-
-    /**
-     * <p>Declares that this project has an execution dependency on each of its child projects.</p>
-     *
-     * @deprecated To definde task dependencies use {@link Task#dependsOn(Object...)} instead.
-     * For declaring evaluation dependencies to child projects, use evaluation dependencies
-     * use {@link #evaluationDependsOnChildren()}.
-     * @param evaluateDependsOnProject If true, adds an evaluation dependency.
-     * @return this project.
-     */
-    @Deprecated
-    Project dependsOnChildren(boolean evaluateDependsOnProject);
-
-    /**
      * <p>Locates a project by path. If the path is relative, it is interpreted relative to this project.</p>
      *
      * @param path The path.
@@ -652,8 +604,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      *
      * <li>A {@link java.util.concurrent.Callable}. The callable's return value is resolved recursively.</li>
      *
-     * <li>An Object. Its {@code toString()} value is treated the same way as a String.
-     * This is deprecated and will be removed in the next version of Gradle.</li>
      * </ul>
      *
      * @param path The object to resolve as a File.
@@ -759,17 +709,20 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      * <p>Creates a new {@code ConfigurableFileTree} using the given base directory. The given baseDir path is evaluated
      * as per {@link #file(Object)}.</p>
      *
-     * <p><b>Note:</b> to use a closure as the baseDir, you must explicitly cast the closure to {@code Object} to force
-     * the use of this method instead of {@link #fileTree(Closure)}. Example:</p>
-     *
-     * <pre>
-     * fileTree((Object){ someDir })
-     * </pre>
-     *
      * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
      * queried. The file tree is also live, so that it scans for files each time the contents of the file tree are
      * queried.</p>
      *
+     * <pre autoTested=''>
+     * def myTree = fileTree("src")
+     * myTree.include "**/*.java"
+     * myTree.builtBy "someTask"
+     *
+     * task copy(type: Copy) {
+     *    from myTree
+     * }
+     * </pre>
+     *
      * @param baseDir The base directory of the file tree. Evaluated as per {@link #file(Object)}.
      * @return the file tree. Never returns null.
      */
@@ -780,10 +733,15 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      * as per {@link #file(Object)}. The closure will be used to configure the new file tree.
      * The file tree is passed to the closure as its delegate.  Example:</p>
      *
-     * <pre>
-     * fileTree('src') {
-     *    exclude '**/.svn/**'
-     * }.copy { into 'dest'}
+     * <pre autoTested=''>
+     * def myTree = fileTree('src') {
+     *    exclude '**/.data/**'
+     *    builtBy 'someTask'
+     * }
+     *
+     * task copy(type: Copy) {
+     *    from myTree
+     * }
      * </pre>
      *
      * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
@@ -800,8 +758,12 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      * <p>Creates a new {@code ConfigurableFileTree} using the provided map of arguments.  The map will be applied as
      * properties on the new file tree.  Example:</p>
      *
-     * <pre>
-     * fileTree(dir:'src', excludes:['**/ignore/**','**/.svn/**'])
+     * <pre autoTested=''>
+     * def myTree = fileTree(dir:'src', excludes:['**/ignore/**', '**/.data/**'])
+     *
+     * task copy(type: Copy) {
+     *     from myTree
+     * }
      * </pre>
      *
      * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
@@ -814,28 +776,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     ConfigurableFileTree fileTree(Map<String, ?> args);
 
     /**
-     * <p>Creates a new {@code ConfigurableFileTree} using the provided closure.  The closure will be used to configure
-     * the new file tree. The file tree is passed to the closure as its delegate.  Example:</p>
-     *
-     * <pre>
-     * fileTree {
-     *    from 'src'
-     *    exclude '**/.svn/**'
-     * }.copy { into 'dest'}
-     * </pre>
-     *
-     * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
-     * queried. The file tree is also live, so that it scans for files each time the contents of the file tree are
-     * queried.</p>
-     *
-     * @deprecated Use {@link #fileTree(Object,Closure)} instead.
-     * @param closure Closure to configure the {@code ConfigurableFileTree} object
-     * @return the configured file tree. Never returns null.
-     */
-    @Deprecated
-    ConfigurableFileTree fileTree(Closure closure);
-
-    /**
      * <p>Creates a new {@code FileTree} which contains the contents of the given ZIP file. The given zipPath path is
      * evaluated as per {@link #file(Object)}. You can combine this method with the {@link #copy(groovy.lang.Closure)}
      * method to unzip a ZIP file.</p>
@@ -849,7 +789,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      */
     FileTree zipTree(Object zipPath);
 
-
     /**
      * Creates a new {@code FileTree} which contains the contents of the given TAR file. The given tarPath path can be:
      * <ul>
@@ -913,6 +852,17 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     ExecResult javaexec(Closure closure);
 
     /**
+     * Executes an external Java process.
+     * <p>
+     * The given action configures a {@link org.gradle.process.JavaExecSpec}, which is used to launch the process.
+     * This method blocks until the process terminates, with its result being returned.
+     *
+     * @param action The action for configuring the execution.
+     * @return the result of the execution
+     */
+    ExecResult javaexec(Action<? super JavaExecSpec> action);
+
+    /**
      * Executes an external command. The closure configures a {@link org.gradle.process.ExecSpec}.
      *
      * @param closure The closure for configuring the execution.
@@ -921,6 +871,17 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     ExecResult exec(Closure closure);
 
     /**
+     * Executes an external command.
+     * <p>
+     * The given action configures a {@link org.gradle.process.ExecSpec}, which is used to launch the process.
+     * This method blocks until the process terminates, with its result being returned.
+     *
+     * @param action The action for configuring the execution.
+     * @return the result of the execution
+     */
+    ExecResult exec(Action<? super ExecSpec> action);
+
+    /**
      * <p>Converts a name to an absolute project path, resolving names relative to this project.</p>
      *
      * @param path The path to convert.
@@ -1189,17 +1150,19 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      *
      * <li>If this project object has a property with the given name, return the value of the property.</li>
      *
+     * <li>If this project has an extension with the given name, return the extension.</li>
+     *
      * <li>If this project's convention object has a property with the given name, return the value of the
      * property.</li>
      *
-     * <li>If this project has an additional property with the given name, return the value of the property.</li>
+     * <li>If this project has an extra property with the given name, return the value of the property.</li>
      *
      * <li>If this project has a task with the given name, return the task.</li>
      *
-     * <li>Search up through this project's ancestor projects for a convention property or additional property with the
+     * <li>Search up through this project's ancestor projects for a convention property or extra property with the
      * given name.</li>
      *
-     * <li>If not found, throw {@link MissingPropertyException}</li>
+     * <li>If not found, a {@link MissingPropertyException} is thrown.</li>
      *
      * </ol>
      *
@@ -1323,14 +1286,6 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
     void dependencies(Closure configureClosure);
 
     /**
-     * Returns the plugins container for this project. The returned container can be used to manage the plugins which
-     * are used by this project.
-     *
-     * @return the plugin container. Never returns null.
-     */
-    PluginContainer getPlugins();
-
-    /**
      * Returns the build script handler for this project. You can use this handler to query details about the build
      * script for this project, and manage the classpath used to compile and execute the project's build script.
      *
@@ -1381,36 +1336,24 @@ public interface Project extends Comparable<Project>, ExtensionAware, PluginAwar
      * Creates a {@link CopySpec} which can later be used to copy files or create an archive. The given closure is used
      * to configure the {@link CopySpec} before it is returned by this method.
      *
+     * <pre autoTested=''>
+     * def baseSpec = copySpec {
+     *    from "source"
+     *    include "**/*.java"
+     * }
+     *
+     * task copy(type: Copy) {
+     *    into "target"
+     *    with baseSpec
+     * }
+     * </pre>
+     *
      * @param closure Closure to configure the CopySpec
      * @return The CopySpec
      */
     CopySpec copySpec(Closure closure);
 
     /**
-     * <p>Configures this project using plugins or scripts. The given closure is used to configure an {@link
-     * org.gradle.api.plugins.ObjectConfigurationAction} which is then used to configure this project.</p>
-     *
-     * @param closure The closure to configure the {@code ObjectConfigurationAction}.
-     */
-    void apply(Closure closure);
-
-    /**
-     * <p>Configures this project using plugins or scripts. The following options are available:</p>
-     *
-     * <ul><li>{@code from}: A script to apply to the project. Accepts any path supported by {@link #uri(Object)}.</li>
-     *
-     * <li>{@code plugin}: The id or implementation class of the plugin to apply to the project.</li>
-     *
-     * <li>{@code to}: The target delegate object or objects. Use this to configure objects other than the
-     * project.</li></ul>
-     *
-     * <p>For more detail, see {@link org.gradle.api.plugins.ObjectConfigurationAction}.</p>
-     *
-     * @param options The options to use to configure the {@code ObjectConfigurationAction}.
-     */
-    void apply(Map<String, ?> options);
-
-    /**
      * Returns the evaluation state of this project. You can use this to access information about the evaluation of this
      * project, such as whether it has failed.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/Script.java b/subprojects/core/src/main/groovy/org/gradle/api/Script.java
index 5c7722c..57b764d 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/Script.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Script.java
@@ -26,6 +26,8 @@ import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.resources.ResourceHandler;
 import org.gradle.api.tasks.WorkResult;
 import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 
 import java.io.File;
 import java.net.URI;
@@ -150,13 +152,6 @@ public interface Script {
      * <p>Creates a new {@code ConfigurableFileTree} using the given base directory. The given baseDir path is evaluated
      * as per {@link #file(Object)}.</p>
      * 
-     * <p><b>Note:</b> to use a closure as the baseDir, you must explicitly cast the closure to {@code Object} to force
-     * the use of this method instead of {@link #fileTree(Closure)}. Example:</p>
-     *
-     * <pre>
-     * fileTree((Object){ someDir })
-     * </pre>
-     *
      * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
      * queried. The file tree is also live, so that it scans for files each time the contents of the file tree are
      * queried.</p>
@@ -182,25 +177,6 @@ public interface Script {
     ConfigurableFileTree fileTree(Map<String, ?> args);
 
     /**
-     * <p>Creates a new {@code ConfigurableFileTree} using the provided closure.  The closure will be used to configure
-     * the new file tree. The file tree is passed to the closure as its delegate.  Example:</p>
-     * <pre>
-     * fileTree {
-     *    from 'src'
-     *    exclude '**/.svn/**'
-     * }.copy { into 'dest'}
-     * </pre>
-     * <p>The returned file tree is lazy, so that it scans for files only when the contents of the file tree are
-     * queried. The file tree is also live, so that it scans for files each time the contents of the file tree are
-     * queried.</p>
-     *
-     * @deprecated Use {@link #fileTree(Object,Closure)} instead.
-     * @param closure Closure to configure the {@code ConfigurableFileTree} object
-     * @return the configured file tree. Never returns null.
-     */
-    ConfigurableFileTree fileTree(Closure closure);
-
-    /**
      * <p>Creates a new {@code ConfigurableFileTree} using the given base directory. The given baseDir path is evaluated
      * as per {@link #file(Object)}. The closure will be used to configure the new file tree.
      * The file tree is passed to the closure as its delegate.  Example:</p>
@@ -337,6 +313,14 @@ public interface Script {
     ExecResult javaexec(Closure closure);
 
     /**
+     * Executes a Java main class.
+     *
+     * @param action The action for configuring the execution.
+     * @return the result of the execution
+     */
+    ExecResult javaexec(Action<? super JavaExecSpec> action);
+
+    /**
      * Executes an external command. The closure configures a {@link org.gradle.process.ExecSpec}.
      *
      * @param closure The closure for configuring the execution.
@@ -345,6 +329,14 @@ public interface Script {
     ExecResult exec(Closure closure);
 
     /**
+     * Executes an external command.
+     *
+     * @param action The action for configuring the execution.
+     * @return the result of the execution
+     */
+    ExecResult exec(Action<? super ExecSpec> action);
+
+    /**
      * Returns the {@link org.gradle.api.logging.LoggingManager} which can be used to control the logging level and
      * standard output/error capture for this script. By default, System.out is redirected to the Gradle logging system
      * at the QUIET log level, and System.err is redirected at the ERROR log level.
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 3f64d82..db26325 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/Task.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/Task.java
@@ -38,7 +38,7 @@ import java.util.Set;
  *
  * <p>Each task belongs to a {@link Project}. You can use the various methods on {@link
  * org.gradle.api.tasks.TaskContainer} to create and lookup task instances. For example, {@link
- * org.gradle.api.tasks.TaskContainer#add(String)} creates an empty task with the given name. You can also use the
+ * org.gradle.api.tasks.TaskContainer#create(String)} creates an empty task with the given name. You can also use the
  * {@code task} keyword in your build file: </p>
  * <pre>
  * task myTask
@@ -109,8 +109,8 @@ import java.util.Set;
  *
  * <a name="properties"/> <h4>Dynamic Properties</h4>
  *
- * <p>A {@code Task} has 3 'scopes' for properties. You can access these properties by name from the build file or by
- * calling the {@link #property(String)} method.</p>
+ * <p>A {@code Task} has 4 'scopes' for properties. You can access these properties by name from the build file or by
+ * calling the {@link #property(String)} method. You can change the value of these properties by calling the {@link #setProperty(String, Object)} method.</p>
  *
  * <ul>
  *
@@ -118,19 +118,29 @@ import java.util.Set;
  * implementation class.  The properties of this scope are readable or writable based on the presence of the
  * corresponding getter and setter methods.</li>
  *
- * <li>The <em>additional properties</em> of the task. Each task object maintains a map of additional properties. These
- * are arbitrary name -> value pairs which you can use to dynamically add properties to a task object.  The properties
- * of this scope are readable and writable.</li>
+ * <li>The <em>extensions</em> added to the task by plugins. Each extension is available as a read-only property with the same
+ * name as the extension.</li>
+ *
+ * <li>The <em>convention</em> properties added to the task by plugins. A plugin can add properties and methods to a task through
+ * the task's {@link Convention} object.  The properties of this scope may be readable or writable, depending on the convention objects.</li>
  *
- * <li>The <em>convention</em> properties added to the task by each {@link Plugin} applied to the project. A {@link
- * Plugin} can add properties and methods to a task through the task's {@link Convention} object.  The properties of
- * this scope may be readable or writable, depending on the convention objects.</li>
+ * <li>The <em>extra properties</em> of the task. Each task object maintains a map of additional properties. These
+ * are arbitrary name -> value pairs which you can use to dynamically add properties to a task object.  Once defined, the properties
+ * of this scope are readable and writable.</li>
  *
  * </ul>
  *
  * <h4>Dynamic Methods</h4>
  *
  * <p>A {@link Plugin} may add methods to a {@code Task} using its {@link Convention} object.</p>
+ *
+ * <h4>Parallel Execution</h4>
+ * <p>
+ * By default, tasks are not executed in parallel.
+ * Parallel execution can be enabled by the <code>--parallel</code> flag when the build is initiated.
+ * In parallel mode, the tasks of different projects (i.e. in a multi project build) are able to be executed in parallel.
+ * If a task is annotated with {@link org.gradle.api.tasks.ParallelizableTask}, it may also be executed in parallel with other tasks of the same project.
+ * See {@link org.gradle.api.tasks.ParallelizableTask} for more details on writing parallelizable tasks.
  */
 public interface Task extends Comparable<Task>, ExtensionAware {
     public static final String TASK_NAME = "name";
@@ -307,7 +317,7 @@ public interface Task extends Comparable<Task>, ExtensionAware {
 
     /**
      * <p>Checks if the task actually did any work.  Even if a Task executes, it may determine that it has nothing to
-     * do.  For example, the Compile task may determine that source files have not changed since the last time a the
+     * do.  For example, a compilation task may determine that source files have not changed since the last time a the
      * task was run.</p>
      *
      * @return true if this task did any work
@@ -427,10 +437,12 @@ public interface Task extends Comparable<Task>, ExtensionAware {
      *
      * <li>If this task object has a property with the given name, return the value of the property.</li>
      *
-     * <li>If this task has an additional property with the given name, return the value of the property.</li>
+     * <li>If this task has an extension with the given name, return the extension. </li>
      *
      * <li>If this task's convention object has a property with the given name, return the value of the property.</li>
      *
+     * <li>If this task has an extra property with the given name, return the value of the property.</li>
+     *
      * <li>If not found, throw {@link MissingPropertyException}</li>
      *
      * </ol>
@@ -460,17 +472,16 @@ public interface Task extends Comparable<Task>, ExtensionAware {
      *
      * <li>The task's convention object.</li>
      *
-     * <li>The task's additional properties.</li>
+     * <li>The task's extra properties.</li>
      *
      * </ol>
      *
-     * <p>If the property is not found in any of these locations, it is added to the project's additional
-     * properties.</p>
+     * If the property is not found, a {@link groovy.lang.MissingPropertyException} is thrown.
      *
      * @param name The name of the property
      * @param value The value of the property
      */
-    void setProperty(String name, Object value);
+    void setProperty(String name, Object value) throws MissingPropertyException;
 
     /**
      * <p>Returns the {@link Convention} object for this task. A {@link Plugin} can use the convention object to
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 9640e79..690f956 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
@@ -16,14 +16,10 @@
 package org.gradle.api.artifacts;
 
 import groovy.lang.Closure;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.NamedDomainObjectList;
 import org.gradle.api.artifacts.repositories.ArtifactRepository;
 import org.gradle.util.Configurable;
 
-import java.util.List;
-
 /**
  * <p>A {@code ResolverContainer} is responsible for managing a set of {@link ArtifactRepository} instances. Repositories are arranged in a sequence.</p>
  *
@@ -51,20 +47,7 @@ import java.util.List;
 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";
+    String MAVEN_CENTRAL_URL = "https://repo1.maven.org/maven2/";
 
     /**
      * Adds a repository to this container, at the end of the repository sequence.
@@ -88,145 +71,6 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
     void addLast(ArtifactRepository repository);
 
     /**
-     * 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);
-
-    /**
-     * 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}.
-     * @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);
-
-    /**
-     * Adds a repository to this container, at the end of the repository sequence. The given {@code userDescription} can be
-     * one of:
-     *
-     * <ul>
-     *
-     * <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 repository. The map must contain an {@value #RESOLVER_NAME} entry and a
-     * {@value #RESOLVER_URL} entry.</li>
-     *
-     * <li>A {@link DependencyResolver}.</li>
-     *
-     * <li>A {@link ArtifactRepository}.</li>
-     *
-     * </ul>
-     *
-     * @param userDescription The resolver definition.
-     * @return The added resolver.
-     * @throws InvalidUserDataException when a resolver with the given name already exists in this container.
-     * @deprecated Use {@link org.gradle.api.artifacts.dsl.RepositoryHandler#maven(groovy.lang.Closure)} or {@link #add(ArtifactRepository)} instead.
-     */
-    @Deprecated
-    DependencyResolver addLast(Object userDescription) throws InvalidUserDataException;
-
-    /**
-     * Adds a resolver to this container, at the end of the resolver sequence. The resolver is configured using the
-     * given configure closure.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @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 org.gradle.api.artifacts.dsl.RepositoryHandler#maven(groovy.lang.Closure)} or {@link #add(ArtifactRepository)} instead.
-     */
-    @Deprecated
-    DependencyResolver addLast(Object userDescription, Closure configureClosure) throws InvalidUserDataException;
-
-    /**
-     * Adds a resolver to this container, before the given resolver.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @param nextResolver The existing resolver to add the new resolver before.
-     * @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;
-
-    /**
-     * Adds a resolver to this container, before the given resolver. The resolver is configured using the given
-     * configure closure.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @param nextResolver The existing resolver to add the new resolver before.
-     * @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.
-     * @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;
-
-    /**
-     * Adds a resolver to this container, after the given resolver.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @param previousResolver The existing resolver to add the new resolver after.
-     * @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;
-
-    /**
-     * Adds a resolver to this container, after the given resolver. The resolver is configured using the given configure
-     * closure.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @param previousResolver The existing resolver to add the new resolver after.
-     * @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.
-     * @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;
-
-    /**
-     * Adds a resolver to this container, at the start of the resolver sequence.
-     *
-     * @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;
-
-    /**
-     * Adds a resolver to this container, at the start of the resolver sequence. The resolver is configured using the
-     * given configure closure.
-     *
-     * @param userDescription The resolver definition. See {@link #addLast(Object)} for details of this parameter.
-     * @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;
-
-    /**
      * {@inheritDoc}
      */
     ArtifactRepository getByName(String name) throws UnknownRepositoryException;
@@ -240,13 +84,4 @@ public interface ArtifactRepositoryContainer extends NamedDomainObjectList<Artif
      * {@inheritDoc}
      */
     ArtifactRepository getAt(String name) throws UnknownRepositoryException;
-
-    /**
-     * Returns the resolvers in this container, in sequence.
-     *
-     * @return The resolvers in sequence. Returns an empty list if this container is empty.
-     * @deprecated No replacement.
-     */
-    @Deprecated
-    List<DependencyResolver> getResolvers();
 }
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 198c13d..e03f7bf 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
@@ -23,7 +23,7 @@ import java.util.Set;
  * a client module you can declare a module dependency without the need of a module descriptor in a
  * remote repository.
  */
-public interface ClientModule extends ExternalDependency {
+public interface ClientModule extends ExternalModuleDependency {
     /**
      * Add a dependency to the client module. Such a dependency is transitive dependency for the
      * project that has a dependency on the client module.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadata.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadata.java
new file mode 100644
index 0000000..66f6ee1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadata.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.List;
+
+/**
+ * Provides a read-only view of a resolved component's metadata, which typically originates from
+ * a component descriptor (Ivy file, Maven POM).
+ */
+ at Incubating
+public interface ComponentMetadata {
+    /**
+     * 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();
+
+}
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
index 2dd34c3..3fdd949 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadataDetails.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentMetadataDetails.java
@@ -29,38 +29,7 @@ import java.util.List;
  */
 @Incubating
 @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();
-
+public interface ComponentMetadataDetails extends ComponentMetadata {
     /**
      * Sets whether the component is changing or immutable.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadata.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadata.java
new file mode 100644
index 0000000..f2ffbb3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadata.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Nullable;
+
+/**
+ * Contains immutable component module metadata information.
+ *
+ * @since 2.2
+ */
+ at Incubating
+public interface ComponentModuleMetadata {
+
+    /**
+     * The identifier of the module.
+     */
+    ModuleIdentifier getId();
+
+    /**
+     * The identifier of module that replaces this module.
+     * A real world example: 'com.google.collections:google-collections' is replaced by 'com.google.guava:guava'.
+     */
+    @Nullable ModuleIdentifier getReplacedBy();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadataDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadataDetails.java
new file mode 100644
index 0000000..98a76d3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentModuleMetadataDetails.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * Contains and allows configuring component module metadata information.
+ * For information and examples please see {@link org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler}
+ *
+ * @since 2.2
+ */
+ at Incubating
+public interface ComponentModuleMetadataDetails extends ComponentModuleMetadata {
+
+    /**
+     * Configures a replacement module for this module.
+     * A real world example: 'com.google.collections:google-collections' is replaced by 'com.google.guava:guava'.
+     *
+     * Subsequent invocations of this method replace the previous 'replacedBy' value.
+     *
+     * For information and examples please see {@link org.gradle.api.artifacts.dsl.ComponentMetadataHandler}.
+     *
+     * @param moduleNotation a String like 'com.google.guava:guava', an instance of {@link org.gradle.api.artifacts.ModuleVersionIdentifier}, null is not permitted
+     */
+    void replacedBy(Object moduleNotation);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelection.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelection.java
new file mode 100644
index 0000000..3e78422
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelection.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.HasInternalProtocol;
+
+/***
+ * Represents a tuple of the component selector of a module and a candidate version
+ * to be evaluated in a component selection rule.
+ */
+ at HasInternalProtocol
+ at Incubating
+public interface ComponentSelection {
+    /**
+     * Gets the candidate version of the module.
+     *
+     * @return the candidate version of the module
+     */
+    ModuleComponentIdentifier getCandidate();
+
+    /**
+     * Rejects the candidate for the resolution.
+     *
+     * @param reason The reason the candidate was rejected.
+     */
+    void reject(String reason);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelectionRules.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelectionRules.java
new file mode 100644
index 0000000..076946e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ComponentSelectionRules.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.artifacts;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+/***
+ * Represents a container for component selection rules.  Rules can be applied as part of the
+ * resolutionStrategy of a configuration and individual components can be explicitly accepted
+ * or rejected by rule.  Components that are neither accepted or rejected will be subject to
+ * the default version matching strategies.
+ *
+ * <pre autoTested=''>
+ *     configurations {
+ *         conf {
+ *             resolutionStrategy {
+ *                 componentSelection {
+ *                     all { ComponentSelection selection ->
+ *                         if (selection.candidate.name == 'someModule' && selection.candidate.version == '1.1') {
+ *                             selection.reject("bad version '1.1' for 'someModule'")
+ *                         }
+ *                     }
+ *                     all { ComponentSelection selection, IvyModuleDescriptor descriptor, ComponentMetadata metadata ->
+ *                         if (selection.candidate.name == 'someModule' && descriptor.branch == 'testing') {
+ *                             if (metadata.status != 'milestone') {
+ *                                 selection.reject("only use milestones for someModule:testing")
+ *                             }
+ *                         }
+ *                     }
+ *                     withModule("org.sample:api") { ComponentSelection selection ->
+ *                         if (selection.candidate.version == "1.1") {
+ *                             selection.reject("known bad version")
+ *                         }
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * </pre>
+ */
+ at HasInternalProtocol
+ at Incubating
+public interface ComponentSelectionRules {
+    /**
+     * Adds a simple component selection rule that will apply to all resolved components.
+     * Each rule will receive a {@link ComponentSelection} object as an argument.
+     *
+     * @param selectionAction the Action that implements a rule to be applied
+     * @return this
+     */
+    public ComponentSelectionRules all(Action<? super ComponentSelection> selectionAction);
+
+    /**
+     * Adds a component selection rule that will apply to all resolved components.
+     *
+     * Each rule will receive a {@link ComponentSelection} object as an argument
+     * as well as any other arguments specified for the closure.
+     * Allowable closure arguments are {@link ComponentSelection} (required),
+     * {@link org.gradle.api.artifacts.ComponentMetadata} and/or
+     * {@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor}.
+     *
+     * @param closure the Closure that implements a rule to be applied
+     * @return this
+     */
+    public ComponentSelectionRules all(Closure<?> closure);
+
+    /**
+     * Adds a rule-source backed component selection rule that will apply to all resolved components.
+     *
+     * The ruleSource provides the rule as exactly one rule method annotated with {@link org.gradle.model.Mutate}.
+     *
+     * This rule method:
+     * <ul>
+     *     <li>must return void.</li>
+     *     <li>must have {@link org.gradle.api.artifacts.ComponentSelection} as the first parameter.</li>
+     *     <li>may have additional parameters of type {@link org.gradle.api.artifacts.ComponentMetadata} and/or {@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor}.</li>
+     * </ul>
+     *
+     * @param ruleSource an instance providing a rule implementation
+     * @return this
+     */
+    public ComponentSelectionRules all(Object ruleSource);
+
+    /**
+     * Adds a component selection rule that will apply to the specified module.
+     * Each rule will receive a {@link ComponentSelection} object as an argument.
+     *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param selectionAction the Action that implements a rule to be applied
+     * @return this
+     */
+    public ComponentSelectionRules withModule(Object id, Action<? super ComponentSelection> selectionAction);
+
+    /**
+     * Adds a component selection rule that will apply to the specified module.
+     *
+     * Each rule will receive a {@link ComponentSelection} object as an argument
+     * as well as any other arguments specified for the closure.
+     * Allowable closure arguments are {@link ComponentSelection} (required),
+     * {@link org.gradle.api.artifacts.ComponentMetadata} and/or
+     * {@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor}.
+     *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param closure the Closure that implements a rule to be applied
+     * @return this
+     */
+    public ComponentSelectionRules withModule(Object id, Closure<?> closure);
+
+    /**
+     * Adds a rule-source backed component selection rule that will apply to the specified module.
+     *
+     * The ruleSource provides the rule as exactly one rule method annotated with {@link org.gradle.model.Mutate}.
+     *
+     * This rule method:
+     * <ul>
+     *     <li>must return void.</li>
+     *     <li>must have {@link org.gradle.api.artifacts.ComponentSelection} as the first parameter.</li>
+     *     <li>may have additional parameters of type {@link org.gradle.api.artifacts.ComponentMetadata} and/or {@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor}.</li>
+     * </ul>
+     *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param ruleSource an instance providing a rule implementation
+     * @return this
+     */
+    public ComponentSelectionRules withModule(Object id, Object ruleSource);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
index be8aab1..1aba04c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
@@ -17,9 +17,9 @@ package org.gradle.api.artifacts;
 
 import groovy.lang.Closure;
 import org.gradle.api.file.FileCollection;
-import org.gradle.internal.HasInternalProtocol;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskDependency;
+import org.gradle.internal.HasInternalProtocol;
 
 import java.io.File;
 import java.util.Map;
@@ -121,7 +121,7 @@ public interface Configuration extends FileCollection {
      * @param superConfigs The super configuration. Should not be null.
      * @return this configuration
      */
-    Configuration setExtendsFrom(Set<Configuration> superConfigs);
+    Configuration setExtendsFrom(Iterable<Configuration> superConfigs);
 
     /**
      * Adds the given configurations to the set of configuration which this configuration extends from.
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 c30e0ea..6822ab1 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
@@ -16,7 +16,6 @@
 package org.gradle.api.artifacts;
 
 import groovy.lang.Closure;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.internal.HasInternalProtocol;
 
@@ -98,30 +97,6 @@ public interface ConfigurationContainer extends NamedDomainObjectContainer<Confi
     Configuration getByName(String name, Closure configureClosure) throws UnknownConfigurationException;
 
     /**
-     * Adds a configuration with the given name.
-     *
-     * @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;
-
-    /**
-     * Adds a configuration with the given name. The given configuration closure is executed against the configuration
-     * before it is returned from this method.
-     *
-     * @param name The name of the new configuration.
-     * @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;
-
-    /**
      * Creates a configuration, but does not add it to this container.
      *
      * @param dependencies The dependencies of the configuration.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyResolveDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyResolveDetails.java
index dea71e1..000b6e4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyResolveDetails.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencyResolveDetails.java
@@ -70,4 +70,4 @@ public interface DependencyResolveDetails {
      * Never returns null. Target module is updated when methods like {@link #useVersion(String)} are used.
      */
     ModuleVersionSelector getTarget();
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitution.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitution.java
new file mode 100644
index 0000000..80849a8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitution.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.artifacts.component.ComponentSelector;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Provides means to substitute a different dependency during resolution.
+ *
+ * @param <T> the type of the requested component.
+ *
+ * @since 2.4
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface DependencySubstitution<T extends ComponentSelector> {
+    /**
+     * The dependency, before it is resolved.
+     * The requested dependency does not change even if there are multiple dependency substitution rules
+     * that manipulate the dependency metadata.
+     */
+    T getRequested();
+
+    /**
+     * This method can be used to replace a dependency before it is resolved,
+     * e.g. change group, name or version (or all three of them), or replace it
+     * with a project dependency.
+     *
+     * @param notation the notation that gets parsed into an instance of {@link ComponentSelector}.
+     * You can pass Strings like 'org.gradle:gradle-core:2.4',
+     * Maps like [group: 'org.gradle', name: 'gradle-core', version: '2.4'],
+     * Projects like <code>project(":api")</code>,
+     * or instances of <code>ModuleComponentSelector</code>.
+     */
+    void useTarget(Object notation);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitutions.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitutions.java
new file mode 100644
index 0000000..57cc54b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/DependencySubstitutions.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.artifacts;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Allows replacing dependencies with other dependencies.
+ *
+ * <pre>
+ * // add dependency substitution rules
+ * dependencySubstitution {
+ *   //specifying a fixed version for all libraries with 'org.gradle' group
+ *   eachModule { ModuleDependencySubstitution details ->
+ *     if (details.requested.group == 'org.gradle') {
+ *       details.useVersion '2.4'
+ *     }
+ *     //changing 'groovy-all' into 'groovy':
+ *     if (details.requested.name == 'groovy-all') {
+ *       details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version
+ *     }
+ *   }
+ * }
+ * </pre>
+ */
+ at HasInternalProtocol
+ at Incubating
+public interface DependencySubstitutions {
+    /**
+     * Adds a dependency substitution rule that is triggered for every dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link DependencySubstitution<ComponentSelector>}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    // TODO:PREZI Perhaps we should call this eachDependency(), as we do it for example in ResolutionRules?
+    DependencySubstitutions all(Action<? super DependencySubstitution<? super ComponentSelector>> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for every dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link DependencySubstitution<ComponentSelector>}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions all(Closure<?> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for every module dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ModuleDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions eachModule(Action<? super ModuleDependencySubstitution> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for every module dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ModuleDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions eachModule(Closure<?> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for a given module dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ModuleDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions withModule(Object id, Action<? super ModuleDependencySubstitution> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for a given module dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ModuleDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions withModule(Object id, Closure<?> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for every project dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ProjectDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions eachProject(Action<? super ProjectDependencySubstitution> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for every project dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ProjectDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions eachProject(Closure<?> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for a given project dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ProjectDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions withProject(Object id, Action<? super ProjectDependencySubstitution> rule);
+
+    /**
+     * Adds a dependency substitution rule that is triggered for a given project dependency (including transitive)
+     * when the configuration is being resolved. The action receives an instance of {@link ProjectDependencySubstitution}
+     * that can be used to find out what dependency is being resolved and to influence the resolution process.
+     *
+     * The rules are evaluated in order they are declared. Rules are evaluated after forced modules are applied (see {@link ResolutionStrategy#force(Object...)}
+     *
+     * @return this
+     * @since 2.4
+     */
+    DependencySubstitutions withProject(Object id, Closure<?> rule);
+}
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 bbd4f38..df9af4c 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
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.artifacts;
 
-import java.util.Map;
-
 /**
  * An {@code ExcludeRule} is used to describe transitive dependencies that should be excluded when resolving
  * dependencies.
@@ -34,17 +32,4 @@ public interface ExcludeRule {
      * The exact name of the module that should be excluded.
      */
     String getModule();
-    
-    /**
-     * Returns the arguments of an exclude rule. The possible keys for the map are:
-     *
-     * <ul>
-     * <li><code>group</code> - The exact name of the organization or group that should be excluded.
-     * <li><code>module</code> - The exact name of the module that should be excluded.
-     * </ul>
-     * 
-     * @deprecated Use {@link #getGroup()} or {@link #getModule()} instead.
-     */
-    @Deprecated
-    Map<String, String> getExcludeArgs();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ModuleDependencySubstitution.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ModuleDependencySubstitution.java
new file mode 100644
index 0000000..0283ca7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ModuleDependencySubstitution.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.artifacts.component.ModuleComponentSelector;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Provides means to substitute a different dependency in place of a module dependency.
+ *
+ * @since 2.4
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface ModuleDependencySubstitution extends DependencySubstitution<ModuleComponentSelector> {
+    /**
+     * Allows to override the version when the dependency {@link #getRequested()} is resolved.
+     * Can be used to select a version that is different than requested.
+     * Forcing modules via {@link ResolutionStrategy#force(Object...)} uses this capability.
+     * <p>
+     * If you need to change not only the version but also group or name please use the {@link #useTarget(Object)} method.
+     *
+     * @param version to use when resolving this dependency, cannot be null.
+     * It is valid to configure the same version as requested.
+     */
+    void useVersion(String version);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependencySubstitution.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependencySubstitution.java
new file mode 100644
index 0000000..1429ddf
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ProjectDependencySubstitution.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.artifacts.component.ProjectComponentSelector;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Provides means to substitute a different dependency in place of a project dependency.
+ *
+ * @since 2.4
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface ProjectDependencySubstitution extends DependencySubstitution<ProjectComponentSelector> {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java
index 974ccee..66fb322 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/PublishException.java
@@ -27,4 +27,8 @@ public class PublishException extends GradleException {
     public PublishException(String message, Throwable cause) {
         super(message, cause);
     }
+
+    public PublishException(String message) {
+        super(message);
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolutionStrategy.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolutionStrategy.java
index 5edf7f7..dabf64e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolutionStrategy.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ResolutionStrategy.java
@@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
 
 /**
  * Defines the strategies around dependency resolution.
- * For example, forcing certain dependency versions, conflict resolutions or snapshot timeouts.
+ * For example, forcing certain dependency versions, substitutions, conflict resolutions or snapshot timeouts.
  * <p>
  * Examples:
  * <pre autoTested=''>
@@ -42,15 +42,17 @@ import java.util.concurrent.TimeUnit;
  *     //  *replace existing forced modules with new ones:
  *     forcedModules = ['asm:asm-all:3.3.1']
  *
- *     // add a dependency resolve rule
- *     eachDependency { DependencyResolveDetails details ->
+ *     // add dependency substitution rules
+ *     dependencySubstitution {
  *       //specifying a fixed version for all libraries with 'org.gradle' group
- *       if (details.requested.group == 'org.gradle') {
- *           details.useVersion'1.4'
- *       }
- *       //changing 'groovy-all' into 'groovy':
- *       if (details.requested.name == 'groovy-all') {
- *          details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version
+ *       eachModule { ModuleDependencySubstitution details ->
+ *         if (details.requested.group == 'org.gradle') {
+ *           details.useVersion '2.4'
+ *         }
+ *         //changing 'groovy-all' into 'groovy':
+ *         if (details.requested.name == 'groovy-all') {
+ *           details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version
+ *         }
  *       }
  *     }
  *
@@ -140,7 +142,7 @@ public interface ResolutionStrategy {
     Set<ModuleVersionSelector> getForcedModules();
 
     /**
-     * Adds a dependency resolve rule that is triggered for every dependency (including transitive)
+     * Adds a dependency substitution rule that is triggered for every dependency (including transitive)
      * when the configuration is being resolved. The action receives an instance of {@link DependencyResolveDetails}
      * that can be used to find out what dependency is being resolved and to influence the resolution process.
      * Example:
@@ -219,4 +221,31 @@ public interface ResolutionStrategy {
      * @since 1.0-milestone-6
      */
     void cacheChangingModulesFor(int value, TimeUnit units);
+
+    /**
+     * Returns the currently configured version selection rules object.
+     *
+     * @return the version selection rules
+     * @since 2.2
+     */
+    @Incubating
+    ComponentSelectionRules getComponentSelection();
+
+    /**
+     * The componentSelection block provides rules to filter or blacklist certain components from appearing in the resolution result.
+     *
+     * @param action Action to be applied to the {@link ComponentSelectionRules}
+     * @return this ResolutionStrategy instance
+     * @since 2.2
+     */
+    @Incubating
+    ResolutionStrategy componentSelection(Action<? super ComponentSelectionRules> action);
+/*
+
+    @Incubating
+    DependencySubstitutions getDependencySubstitution();
+
+    @Incubating
+    ResolutionStrategy dependencySubstitution(Action<? super DependencySubstitutions> action);
+*/
 }
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 2810b24..8ffb3b5 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
@@ -16,14 +16,14 @@
 
 package org.gradle.api.artifacts;
 
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
 import org.gradle.internal.exceptions.Contextual;
 
 /**
  * <p>A <code>ResolveException</code> is thrown when a dependency configuration cannot be resolved for some reason.</p>
  */
 @Contextual
-public class ResolveException extends AbstractMultiCauseException {
+public class ResolveException extends DefaultMultiCauseException {
     public ResolveException(Configuration configuration, Throwable cause) {
         super(buildMessage(configuration), cause);
     }
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 13d50ce..54efc43 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
@@ -30,15 +30,6 @@ public interface ResolvedArtifact {
      */
     ResolvedModuleVersion getModuleVersion();
 
-    /**
-     * Returns one of the dependencies which this artifact belongs to.
-     *
-     * @return One of the dependencies which this artifact belongs to.
-     * @deprecated An artifact can belong to multiple dependencies. Use {@link #getModuleVersion()} instead.
-     */
-    @Deprecated
-    ResolvedDependency getResolvedDependency();
-
     String getName();
 
     String getType();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnresolvedDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnresolvedDependency.java
index 5865857..bf36965 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnresolvedDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/UnresolvedDependency.java
@@ -19,15 +19,6 @@ package org.gradle.api.artifacts;
  * Unsuccessfully resolved dependency.
  */
 public interface UnresolvedDependency {
-
-    /**
-     * Deprecated. Please use {@link #getSelector()}
-     * <p>
-     * Returns the identifier of the dependency, for example group:name:version
-     */
-    @Deprecated
-    String getId();
-
     /**
      * The module selector of the dependency.
      *
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
index 39f21af..2e5b299 100644
--- 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
@@ -15,18 +15,26 @@
  */
 package org.gradle.api.artifacts.dsl;
 
+import groovy.lang.Closure;
 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.
+ * Allows the build to provide rules that modify the metadata of depended-on software components.
+ *
+ * <p>Possible uses of component metadata rules are:
+ * <ul>
+ *     <li>Setting the status and status scheme of a component, overriding the value specified in the component descriptor.</li>
+ *     <li>Declaring whether or not a component is 'changing', thus impacting the cache behaviour of the component.</li>
+ * </ul>
  *
  * <p> Example:
  * <pre autoTested=''>
  * dependencies {
  *     components {
- *         eachComponent { ComponentMetadataDetails details ->
+ *         // Set the status and status scheme for every component belonging to a module in the group "org.foo"
+ *         all { ComponentMetadataDetails details ->
  *             if (details.id.group == "org.foo") {
  *                 def version = details.id.version
  *                 // assuming status is last part of version string
@@ -34,6 +42,11 @@ import org.gradle.api.artifacts.ComponentMetadataDetails;
  *                 details.statusScheme = ["bronze", "silver", "gold", "platinum"]
  *             }
  *         }
+ *
+ *         // Treat all components in the module "org.foo:bar" as changing
+ *         withModule("org.foo:bar") { ComponentMetadataDetails details ->
+ *             details.changing = true
+ *         }
  *     }
  * }
  * </pre>
@@ -43,12 +56,79 @@ import org.gradle.api.artifacts.ComponentMetadataDetails;
 @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.
+     * Adds a rule action that may modify the metadata of any resolved software component.
+     *
+     * @param rule the rule to be added
+     * @return this
+     */
+    ComponentMetadataHandler all(Action<? super ComponentMetadataDetails> rule);
+
+    /**
+     * Adds a rule closure that may modify the metadata of any resolved software component.
+     *
+     * <p>The supplied rule closure must declare a {@link ComponentMetadataDetails} as it's first parameter,
+     * allowing the component metadata to be modified.
+     *
+     * <p>In addition, the rule can declare additional (read-only) parameters, which may provide extra details
+     * about the component. The order of these additional parameters is not significant.
+     *
+     * <p>The following additional parameter types are supported:
+     * <ul>
+     *     <li>{@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor} - additional Ivy-specific
+     *     metadata. Rules declaring this parameter will only be invoked for components packaged as an Ivy module.</li>
+     * </ul>
+     *
+     * @param rule the rule to be added
+     * @return this
+     */
+    ComponentMetadataHandler all(Closure<?> rule);
+
+    /**
+     * Adds a rule that may modify the metadata of any resolved software component.
+     *
+     * <p>The ruleSource is an Object that has a single rule method annotated with {@link org.gradle.model.Mutate}.
+     *
+     * <p>This rule method:
+     * <ul>
+     *     <li>must return void.</li>
+     *     <li>must have {@link ComponentMetadataDetails} as the first parameter.</li>
+     *     <li>may have an additional parameter of type {@link org.gradle.api.artifacts.ivy.IvyModuleDescriptor}.</li>
+     * </ul>
+     *
+     * @param ruleSource  the rule source object to be added
+     * @return this
+     */
+    ComponentMetadataHandler all(Object ruleSource);
+
+    /**
+     * Adds a rule that may modify the metadata of any resolved software component belonging to the specified module.
      *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
      * @param rule the rule to be added
+     * @return this
      */
-    void eachComponent(Action<? super ComponentMetadataDetails> rule);
+    ComponentMetadataHandler withModule(Object id, Action<? super ComponentMetadataDetails> rule);
+
+    /**
+     * Adds a rule that may modify the metadata of any resolved software component belonging to the specified module.
+     *
+     * <p>The rule closure parameter is subject to the same requirements as {@link #all(groovy.lang.Closure)}.
+     *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param rule the rule to be added
+     * @return this
+     */
+    ComponentMetadataHandler withModule(Object id, Closure<?> rule);
+
+    /**
+     * Adds a rule that may modify the metadata of any resolved software component belonging to the specified module.
+     *
+     * <p>The rule source parameter is subject to the same requirements as {@link #all(Object)}.
+     *
+     * @param id the module to apply this rule to in "group:module" format or as a {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param ruleSource the rule source object to be added
+     * @return this
+     */
+    ComponentMetadataHandler withModule(Object id, Object ruleSource);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentModuleMetadataHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentModuleMetadataHandler.java
new file mode 100644
index 0000000..c85870b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/dsl/ComponentModuleMetadataHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ComponentModuleMetadata;
+
+/**
+ * Allows to modify the metadata of depended-on software components.
+ *
+ * <p> Example:
+ * <pre autoTested=''>
+ * dependencies {
+ *     modules {
+ *         //Configuring component module metadata for the entire "google-collections" module,
+ *         // declaring that legacy library was replaced with "guava".
+ *         //This way, Gradle's conflict resolution can use this information and use "guava"
+ *         // in case both libraries appear in the same dependency tree.
+ *         module("com.google.collections:google-collections") {
+ *             replacedBy("com.google.guava:guava")
+ *         }
+ *     }
+ * }
+ * </pre>
+ *
+ * @since 2.2
+ */
+ at Incubating
+public interface ComponentModuleMetadataHandler {
+    /**
+     * Enables configuring component module metadata.
+     * This metadata applies to the entire component module (e.g. "group:name", like "org.gradle:gradle-core") regardless of the component version.
+     *
+     * <pre autoTested=''>
+     * //declaring that google collections are replaced by guava
+     * //so that conflict resolution can take advantage of this information:
+     * dependencies.modules.module('com.google.collections:google-collections') { replacedBy('com.google.guava:guava') }
+     * </pre>
+     *
+     * @param moduleNotation an identifier of the module. String "group:name", e.g. 'org.gradle:gradle-core'
+     * or an instance of {@link org.gradle.api.artifacts.ModuleIdentifier}
+     * @param rule a rule that applies to the components of the specified module
+     * @since 2.2
+     */
+    public void module(Object moduleNotation, Action<? super ComponentModuleMetadata> 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 4522d20..81998db 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
@@ -19,7 +19,7 @@ import groovy.lang.Closure;
 import org.gradle.api.Action;
 import org.gradle.api.Incubating;
 import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.resolution.ArtifactResolutionQuery;
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
 
 import java.util.Map;
 
@@ -137,15 +137,15 @@ import java.util.Map;
  *
  * <h3>External dependencies</h3>
  *
- * <p>There are 2 notations supported for declaring a dependency on an external module.
- * One is a String notation formatted this way: group:name:version</p>
+ * <p>There are two notations supported for declaring a dependency on an external module.
+ * One is a string notation formatted this way:</p>
  *
- * <code><i>configurationName</i> "<i>group</i>:<i>name</i>:<i>version</i>:<i>classifier</i>"</code>
+ * <code><i>configurationName</i> "<i>group</i>:<i>name</i>:<i>version</i>:<i>classifier</i>@<i>extension</i>"</code>
  *
  * <p>The other is a map notation:</p>
  *
  * <code><i>configurationName</i> group: <i>group</i>:, name: <i>name</i>, version: <i>version</i>, classifier:
- * <i>classifier</i></code>
+ * <i>classifier</i>, ext: <i>extension</i></code>
  *
  * <p>In both notations, all properties, except name, are optional.</p>
  *
@@ -333,7 +333,7 @@ public interface DependencyHandler {
     ComponentMetadataHandler getComponents();
 
     /**
-     * Configures module metadata for this project.
+     * Configures component metadata for this project.
      *
      * <p>This method executes the given action against the {@link org.gradle.api.artifacts.dsl.ComponentMetadataHandler} for this project.
      *
@@ -343,6 +343,32 @@ public interface DependencyHandler {
     @Incubating
     void components(Action<? super ComponentMetadataHandler> configureAction);
 
+    /**
+     * Returns the component module 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 module metadata handler for this project
+     * @since 2.2
+     */
+    @Incubating
+    ComponentModuleMetadataHandler getModules();
+
+    /**
+     * Configures module metadata for this project.
+     *
+     * <p>This method executes the given action against the {@link org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler} for this project.
+     *
+     * @param configureAction the action to use to configure module metadata
+     * @since 2.2
+     */
+    @Incubating
+    void modules(Action<? super ComponentModuleMetadataHandler> configureAction);
+
+    /**
+     * Creates an artifact resolution query.
+     *
+     * @since 2.0
+     */
     @Incubating
     ArtifactResolutionQuery createArtifactResolutionQuery();
 }
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 4f2caa1..966f132 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
@@ -16,7 +16,6 @@
 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.artifacts.ArtifactRepositoryContainer;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
@@ -84,8 +83,8 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
     /**
      * 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)}.
+     * The URL used to access this repository is {@literal "https://jcenter.bintray.com/"}.
+     * The behavior of this repository is otherwise the same as those added by {@link #maven(org.gradle.api.Action)}.
      * <p>
      * Examples:
      * <pre autoTested="">
@@ -108,8 +107,8 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
     /**
      * 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()}.
+     * The URL used to access this repository is {@literal "https://jcenter.bintray.com/"}.
+     * The behavior of this repository is otherwise the same as those added by {@link #maven(org.gradle.api.Action)}.
      * <p>
      * Examples:
      * <pre autoTested="">
@@ -125,10 +124,9 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
 
     /**
      * 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)}.
+     * {@value org.gradle.api.artifacts.ArtifactRepositoryContainer#MAVEN_CENTRAL_URL}.
      *
-     * The following parameter are accepted as keys for the map:
+     * <p>The following parameter are accepted as keys for the map:
      *
      * <table summary="Shows property keys and associated values">
      * <tr><th>Key</th>
@@ -187,76 +185,22 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
      * }
      * </pre>
      * </p>
+     * <p>
+     * The location for the repository is determined as follows (in order of precedence):
+     * <ol>
+     * <li>The value of system property 'maven.repo.local' if set;</li>
+     * <li>The value of element <localRepository> of <code>~/.m2/settings.xml</code> if this file exists and element is set;</li>
+     * <li>The value of element <localRepository> of <code>$M2_HOME/conf/settings.xml</code> (where <code>$M2_HOME</code> is the value of the environment variable with that name) if this file exists and element is set;</li>
+     * <li>The path <code>~/.m2/repository</code>.</li>
+     * </ol>
+     * </p>
      *
      * @return the added resolver
      */
     MavenArtifactRepository mavenLocal();
 
     /**
-     * Adds a repository which is Maven compatible. The compatibility is in regard to layout, snapshot handling and
-     * dealing with the pom.xml. This repository can't be used for publishing in a Maven compatible way. For publishing
-     * 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
-     * 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.
-     *
-     * The following parameter are accepted as keys for the map:
-     *
-     * <table summary="Shows property keys and associated values">
-     * <tr><th>Key</th>
-     *     <th>Description of Associated Value</th></tr>
-     * <tr><td><code>name</code></td>
-     *     <td><em>(optional)</em> The name of the repository. The default is the URL of the root repo.
-     * The name is used in the console output,
-     * to point to information related to a particular repository. A name must be unique amongst a repository group.
-     * </td></tr>
-     * <tr><td><code>url</code></td>
-     *     <td>The root repository where POM files and artifacts are located.
-     * The provided values are evaluated as per {@link org.gradle.api.Project#uri(Object)}.</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 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.
-     * The provided values are evaluated as per {@link org.gradle.api.Project#uri(Object)}.</td></tr>
-     * </table>
-     *
-     * <p>Examples:
-     * <pre>
-     * repositories {
-     *     mavenRepo url: "http://www.mycompany.com/repository", artifactUrls: ["http://www.mycompany.com/artifacts1", "http://www.mycompany.com/artifacts2"]
-     *     mavenRepo name: "nonDefaultName", url: "http://www.mycompany.com/repository"
-     * }
-     * </pre>
-     * </p>
-     *
-     * For Ivy related reasons, Maven Snapshot dependencies are only properly resolved if no additional jar locations
-     * are specified. This is unfortunate and we hope to improve this in a future release.
-     *
-     * @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);
-
-    /**
-     * Adds and configures a Maven repository.
+     * Adds and configures a Maven repository. Newly created instance of {@code MavenArtifactRepository} is passed as an argument to the closure.
      *
      * @param closure The closure to use to configure the repository.
      * @return The added repository.
@@ -272,7 +216,7 @@ public interface RepositoryHandler extends ArtifactRepositoryContainer {
     MavenArtifactRepository maven(Action<? super MavenArtifactRepository> action);
 
     /**
-     * Adds and configures an Ivy repository.
+     * Adds and configures an Ivy repository. Newly created instance of {@code IvyArtifactRepository} is passed as an argument to the closure.
      *
      * @param closure The closure to use to configure the repository.
      * @return The added repository.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyExtraInfo.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyExtraInfo.java
new file mode 100644
index 0000000..0f63ac7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyExtraInfo.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ivy;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Nullable;
+
+import javax.xml.namespace.QName;
+import java.util.Map;
+
+/**
+ * Represents the set of "extra" info elements in the Ivy descriptor.  These elements
+ * are children of the "ivy" element, but are not defined in the Ivy schema and come
+ * from other namespaces.
+ */
+ at Incubating
+public interface IvyExtraInfo {
+    /**
+     * Returns the value of the element with the unique element name.  If there are multiple elements with the same element name,
+     * in different namespaces, a {@link org.gradle.api.InvalidUserDataException} will be thrown.
+     *
+     * @param name The unique name of the element whose value should be returned
+     * @return The value of the element, or null if there is no such element.
+     */
+    @Nullable
+    String get(String name) throws InvalidUserDataException;
+
+    /**
+     * Returns the value of the element with the name and namespace provided.
+     *
+     * @param namespace The namespace of the element whose value should be returned
+     * @param name The name of the element whose value should be returned
+     * @return The value of the element, or null if there is no such element.
+     */
+    @Nullable
+    String get(String namespace, String name);
+
+    /**
+     * Returns a map view of the 'extra' info elements such that each key is a javax.xml.namespace.QName
+     * representing the namespace and name of the element and each value is the content of the element.
+     *
+     * @return The map view of the extra info elements. Returns an empty map if there are no elements.
+     */
+    Map<QName, String> asMap();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java
new file mode 100644
index 0000000..fa9bfa0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ivy;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+/**
+ * The metadata about an Ivy module that acts as an input to a component metadata rule.
+ */
+ at Incubating
+public interface IvyModuleDescriptor {
+    /***
+     * Returns the branch attribute of the info element in this descriptor.
+     *
+     * @return the branch for this descriptor, or null if no branch was declared in the descriptor.
+     */
+    @Nullable
+    String getBranch();
+
+    /**
+     * Returns the status attribute of the info element in this descriptor.  Note that this <i>always</i> represents
+     * the status from the ivy.xml for this module.  It is not affected by changes to the status made via
+     * the {@link org.gradle.api.artifacts.ComponentMetadataDetails} interface in a component metadata rule.
+     *
+     * @return the status for this descriptor
+     */
+    String getIvyStatus();
+
+    /**
+     * Returns an {@link org.gradle.api.artifacts.ivy.IvyExtraInfo} representing the "extra" info declared
+     * in this descriptor.
+     * <p>
+     * The extra info is the set of all non-standard subelements of the <em>info</em> element.
+     *
+     * @return an {@link org.gradle.api.artifacts.ivy.IvyExtraInfo} representing the extra info declared in this descriptor
+     */
+    IvyExtraInfo getExtraInfo();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/package-info.java
new file mode 100644
index 0000000..4ba4db9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/ivy/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for declaring and using Ivy modules.
+ */
+package org.gradle.api.artifacts.ivy;
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/ArtifactResolutionQuery.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/ArtifactResolutionQuery.java
new file mode 100644
index 0000000..f23f8a4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/ArtifactResolutionQuery.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.query;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.component.Artifact;
+import org.gradle.api.artifacts.result.ArtifactResolutionResult;
+import org.gradle.api.component.Component;
+
+/**
+ * A builder to construct a query that can resolve selected software artifacts of the specified components.
+ *
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ *
+ * task resolveCompileSources << {
+ *     def componentIds = configurations.compile.incoming.resolutionResult.allDependencies.collect { it.selected.id }
+ *
+ *     def result = dependencies.createArtifactResolutionQuery()
+ *                              .forComponents(componentIds)
+ *                              .withArtifacts(JvmLibrary, SourcesArtifact, JavadocArtifact)
+ *                              .execute()
+ *
+ *     for (component in result.resolvedComponents) {
+ *         component.getArtifacts(SourcesArtifact).each { println "Source artifact for ${component.id}: ${it.file}" }
+ *     }
+ * }
+ * </pre>
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ArtifactResolutionQuery {
+    /**
+     * Specifies the set of components to include in the result.
+     * @param componentIds The identifiers of the components to be queried.
+     */
+    ArtifactResolutionQuery forComponents(Iterable<? extends ComponentIdentifier> componentIds);
+
+    /**
+     * Specifies the set of components to include in the result.
+     * @param componentIds The identifiers of the components to be queried.
+     */
+    ArtifactResolutionQuery forComponents(ComponentIdentifier... componentIds);
+
+    /**
+     * Defines the type of component that is expected in the result, and the artifacts to retrieve for components of this type.
+     *
+     * Presently, only a single component type and set of artifacts is permitted.
+     *
+     * @param componentType The expected type of the component.
+     * @param artifactTypes The artifacts to retrieve for the queried components.
+     */
+    ArtifactResolutionQuery withArtifacts(Class<? extends Component> componentType, Class<? extends Artifact>... artifactTypes);
+
+    /**
+     * Actually execute the query, returning a query result.
+     * Note that {@link #withArtifacts(Class, Class[])} must be called before executing the query.
+     */
+    ArtifactResolutionResult execute();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/package-info.java
new file mode 100644
index 0000000..0556e08
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/query/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes used for querying the artifacts.
+ */
+package org.gradle.api.artifacts.query;
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/AuthenticationSupported.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/AuthenticationSupported.java
index a9a5c92..b8ecdf3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/AuthenticationSupported.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/AuthenticationSupported.java
@@ -15,7 +15,9 @@
  */
 package org.gradle.api.artifacts.repositories;
 
-import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.credentials.Credentials;
 
 /**
  * An artifact repository which supports username/password authentication.
@@ -23,17 +25,37 @@ import groovy.lang.Closure;
 public interface AuthenticationSupported {
 
     /**
-     * Provides the Credentials used to authenticate to this repository.
-     * @return The credentials
+     * Returns the username and password credentials used to authenticate to this repository.
+     * <p>
+     * If no credentials have been assigned to this repository, an empty set of username and password credentials is assigned to this repository and returned.
+     * <p>
+     * If you are using a different type of credentials than {@link PasswordCredentials}, please use {@link #getCredentials(Class)} to obtain the credentials.
+     *
+     * @return the credentials
+     * @throws IllegalStateException if the credential type was previously set with {@link #credentials(Class, Action)} where the type was not {@link PasswordCredentials}
      */
     PasswordCredentials getCredentials();
 
     /**
-     * Configure the Credentials for this repository using the supplied Closure.
+     * Returns the credentials of the specified type used to authenticate with this repository.
+     * <p>
+     * If no credentials have been assigned to this repository, an empty set of credentials of the specified type is assigned to this repository and returned.
      *
+     * @param credentialsType type of the credential
+     * @return The credentials
+     * @throws IllegalArgumentException when the credentials assigned to this repository are not assignable to the specified type
+     */
+    @Incubating
+    public <T extends Credentials> T getCredentials(Class<T> credentialsType);
+
+    /**
+     * Configures the username and password credentials for this repository using the supplied action.
+     * <p>
+     * If no credentials have been assigned to this repository, an empty set of username and password credentials is assigned to this repository and passed to the action.
      * <pre autoTested=''>
      * repositories {
      *     maven {
+     *         url "${url}"
      *         credentials {
      *             username = 'joe'
      *             password = 'secret'
@@ -41,6 +63,37 @@ public interface AuthenticationSupported {
      *     }
      * }
      * </pre>
+     *
+     * @throws IllegalStateException when the credentials assigned to this repository are not of type {@link PasswordCredentials}
+     */
+    void credentials(Action<? super PasswordCredentials> action);
+
+    /**
+     * Configures the credentials for this repository using the supplied action.
+     * <p>
+     * If no credentials have been assigned to this repository, an empty set of credentials of the specified type will be assigned to this repository and given to the configuration action.
+     * If credentials have already been specified for this repository, they will be passed to the given configuration action.
+     * <pre autoTested=''>
+     * repositories {
+     *     maven {
+     *         url "${url}"
+     *         credentials(AwsCredentials) {
+     *             accessKey "myAccessKey"
+     *             secretKey "mySecret"
+     *         }
+     *     }
+     * }
+     * </pre>
+     * <p>
+     * The following credential types are currently supported for the {@code credentialsType} argument:
+     * <ul>
+     * <li>{@link org.gradle.api.artifacts.repositories.PasswordCredentials}</li>
+     * <li>{@link org.gradle.api.credentials.AwsCredentials}</li>
+     * </ul>
+     *
+     * @throws IllegalArgumentException if {@code credentialsType} is not of a supported type
+     * @throws IllegalArgumentException if {@code credentialsType} is of a different type to the credentials previously specified for this repository
      */
-    void credentials(Closure closure);
+    @Incubating
+    <T extends Credentials> void credentials(Class<T> credentialsType, Action<? super T> action);
 }
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 bf92523..96b1067 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
@@ -16,24 +16,28 @@
 package org.gradle.api.artifacts.repositories;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.Incubating;
 
 import java.net.URI;
 
 /**
- * <p>An artifact repository which uses an Ivy format to store artifacts and meta-data.</p>
- *
- * <p>When used to resolve metadata and artifact files, all available patterns will be searched.</p>
- *
- * <p>When used to upload metadata and artifact files, only a single, primary pattern will be used:
+ * An artifact repository which uses an Ivy format to store artifacts and meta-data.
+ * <p>
+ * When used to resolve metadata and artifact files, all available patterns will be searched.
+ * <p>
+ * When used to upload metadata and artifact files, only a single, primary pattern will be used:
  * <ol>
- *     <li>If a URL is specified via {@link #setUrl} then that URL will be used for upload, combined with the applied {@link #layout(String)}.</li>
- *     <li>If no URL has been specified but additional patterns have been added via {@link #artifactPattern} or {@link #ivyPattern}, then the first defined pattern will be used.</li>
+ * <li>If a URL is specified via {@link #setUrl} then that URL will be used for upload, combined with the applied {@link #layout(String)}.</li>
+ * <li>If no URL has been specified but additional patterns have been added via {@link #artifactPattern} or {@link #ivyPattern}, then the first defined pattern will be used.</li>
  * </ol>
- * </p>
+ * <p>
+ * Repositories of this type are created by the {@link org.gradle.api.artifacts.dsl.RepositoryHandler#ivy(org.gradle.api.Action)} group of methods.
  */
 public interface IvyArtifactRepository extends ArtifactRepository, AuthenticationSupported {
 
+    String IVY_ARTIFACT_PATTERN = "[organisation]/[module]/[revision]/[type]s/[artifact](.[ext])";
+
     String GRADLE_ARTIFACT_PATTERN = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])";
     String GRADLE_IVY_PATTERN = "[organisation]/[module]/[revision]/ivy-[revision].xml";
 
@@ -62,7 +66,7 @@ public interface IvyArtifactRepository extends ArtifactRepository, Authenticatio
      * ivy pattern is supplied via {@link #ivyPattern(String)}.
      *
      * If this pattern is not a fully-qualified URL, it will be interpreted as a file relative to the project directory.
-     * It is not interpreted relative the the URL specified in {@link #setUrl(Object)}.
+     * It is not interpreted relative the URL specified in {@link #setUrl(Object)}.
      *
      * Patterns added in this way will be in addition to any layout-based patterns added via {@link #setUrl}.
      *
@@ -74,7 +78,7 @@ public interface IvyArtifactRepository extends ArtifactRepository, Authenticatio
      * Adds an independent pattern that will be used to locate ivy files in this repository.
      *
      * If this pattern is not a fully-qualified URL, it will be interpreted as a file relative to the project directory.
-     * It is not interpreted relative the the URL specified in {@link #setUrl(Object)}.
+     * It is not interpreted relative the URL specified in {@link #setUrl(Object)}.
      *
      * Patterns added in this way will be in addition to any layout-based patterns added via {@link #setUrl}.
      *
@@ -91,24 +95,42 @@ public interface IvyArtifactRepository extends ArtifactRepository, Authenticatio
     void layout(String layoutName);
 
     /**
-     * Specifies the layout to use with this repository, based on the root url. The returned layout is configured with the supplied closure.
-     * Available layouts are outlined below.
+     * Specifies how the items of the repository are organized.
+     * <p>
+     * The layout is configured with the supplied closure.
+     * <p>
+     * Recognised values are as follows:
+     * </p>
      * <h4>'gradle'</h4>
+     * <p>
      * A Repository Layout that applies the following patterns:
      * <ul>
      *     <li>Artifacts: <code>$baseUri/{@value #GRADLE_ARTIFACT_PATTERN}</code></li>
      *     <li>Ivy: <code>$baseUri/{@value #GRADLE_IVY_PATTERN}</code></li>
      * </ul>
-     *
+     * </p>
      * <h4>'maven'</h4>
+     * <p>
      * A Repository Layout that applies the following patterns:
      * <ul>
      *     <li>Artifacts: <code>$baseUri/{@value #MAVEN_ARTIFACT_PATTERN}</code></li>
      *     <li>Ivy: <code>$baseUri/{@value #MAVEN_IVY_PATTERN}</code></li>
      * </ul>
+     * </p>
+     * <p>
      * Following the Maven convention, the 'organisation' value is further processed by replacing '.' with '/'.
-     *
+     * </p>
+     * <h4>'ivy'</h4>
+     * <p>
+     * A Repository Layout that applies the following patterns:
+     * <ul>
+     *     <li>Artifacts: <code>$baseUri/{@value #IVY_ARTIFACT_PATTERN}</code></li>
+     *     <li>Ivy: <code>$baseUri/{@value #IVY_ARTIFACT_PATTERN}</code></li>
+     * </ul>
+     * </p>
+     * <p><b>Note:</b> this pattern is currently {@link org.gradle.api.Incubating incubating}.</p>
      * <h4>'pattern'</h4>
+     * <p>
      * A repository layout that allows custom patterns to be defined. eg:
      * <pre autoTested="">
      * repositories {
@@ -120,9 +142,21 @@ public interface IvyArtifactRepository extends ArtifactRepository, Authenticatio
      *     }
      * }
      * </pre>
+     * </p>
+     * <p>The available pattern tokens are listed as part of <a href="http://ant.apache.org/ivy/history/latest-milestone/concept.html#patterns">Ivy's Main Concepts documentation</a>.</p>
+     *
+     * @param layoutName The name of the layout to use.
+     * @param config The action used to configure the layout.
+     * @since 2.3 (feature was already present in Groovy DSL, this particular method introduced in 2.3)
+     */
+    void layout(String layoutName, Action<? extends RepositoryLayout> config);
+
+    /**
+     * Specifies how the items of the repository are organized. See {@link #layout(String, org.gradle.api.Action)}
      *
      * @param layoutName The name of the layout to use.
      * @param config The closure used to configure the layout.
+     * An instance of {@link RepositoryLayout} is passed as a parameter to the closure.
      */
     void layout(String layoutName, Closure config);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyPatternRepositoryLayout.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyPatternRepositoryLayout.java
new file mode 100644
index 0000000..e6dcf7c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/IvyPatternRepositoryLayout.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.artifacts.repositories;
+
+/**
+ * A repository layout that uses user-supplied patterns. Each pattern will be appended to the base URI for the repository.
+ * At least one artifact pattern must be specified. If no Ivy patterns are specified, then the artifact patterns will be used.
+ * Optionally supports a Maven style layout for the 'organisation' part, replacing any dots with forward slashes.
+ *
+ * For examples see the reference for {@link org.gradle.api.artifacts.repositories.IvyArtifactRepository#layout(String, org.gradle.api.Action)}.
+ *
+ * @since 2.3 (feature was already present in Groovy DSL, this type introduced in 2.3)
+ */
+public interface IvyPatternRepositoryLayout extends RepositoryLayout {
+
+    /**
+     * Adds an Ivy artifact pattern to define where artifacts are located in this repository.
+     * @param pattern The ivy pattern
+     */
+    void artifact(String pattern);
+
+    /**
+     * Adds an Ivy pattern to define where ivy files are located in this repository.
+     * @param pattern The ivy pattern
+     */
+    void ivy(String pattern);
+
+    /**
+     * Tells whether a Maven style layout is to be used for the 'organisation' part, replacing any dots with forward slashes.
+     * Defaults to {@code false}.
+     */
+    boolean getM2Compatible();
+
+    /**
+     * Sets whether a Maven style layout is to be used for the 'organisation' part, replacing any dots with forward slashes.
+     * Defaults to {@code false}.
+     *
+     * @param m2compatible whether a Maven style layout is to be used for the 'organisation' part
+     */
+    void setM2compatible(boolean m2compatible);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/MavenArtifactRepository.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/MavenArtifactRepository.java
index f6da525..2e36d35 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/MavenArtifactRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/MavenArtifactRepository.java
@@ -20,6 +20,8 @@ import java.util.Set;
 
 /**
  * An artifact repository which uses a Maven format to store artifacts and meta-data.
+ * <p>
+ * Repositories of this type are created by the {@link org.gradle.api.artifacts.dsl.RepositoryHandler#maven(org.gradle.api.Action)} group of methods.
  */
 public interface MavenArtifactRepository extends ArtifactRepository, AuthenticationSupported {
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/PasswordCredentials.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/PasswordCredentials.java
index 01c93cd..8a5416b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/PasswordCredentials.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/PasswordCredentials.java
@@ -15,10 +15,13 @@
  */
 package org.gradle.api.artifacts.repositories;
 
+import org.gradle.api.credentials.Credentials;
+
 /**
  * A username/password credentials that can be used to login to password-protected remote repository.
  */
-public interface PasswordCredentials {
+public interface PasswordCredentials extends Credentials {
+
     /**
      * Returns the user name to use when authenticating to this repository.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/RepositoryLayout.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/RepositoryLayout.java
new file mode 100644
index 0000000..a715930
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/repositories/RepositoryLayout.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.artifacts.repositories;
+
+/**
+ * Represents the directory structure for a repository.
+ * For examples see the reference for {@link org.gradle.api.artifacts.repositories.IvyArtifactRepository#layout(String, org.gradle.api.Action)}
+ *
+ * @since 2.3 (feature was already present in Groovy DSL, this type introduced in 2.3)
+ */
+public interface RepositoryLayout {}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQuery.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQuery.java
deleted file mode 100644
index c26823c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQuery.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * Resolves selected software artifacts of the given components.
- *
- * @since 1.12
- */
- at Incubating
-public interface ArtifactResolutionQuery {
-    ArtifactResolutionQuery forComponents(Iterable<? extends ComponentIdentifier> componentIds);
-    ArtifactResolutionQuery forComponents(ComponentIdentifier... componentIds);
-    <T extends SoftwareComponent, U extends SoftwareArtifact> ArtifactResolutionQuery withArtifacts(Class<T> componentType, Class<U>... artifactTypes);
-    ArtifactResolutionQueryResult execute();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQueryResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQueryResult.java
deleted file mode 100644
index b6cdb15..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/ArtifactResolutionQueryResult.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-
-import java.util.Set;
-
-/**
- * The result of executing an artifact resolution query.
- *
- * @since 1.12
- */
- at Incubating
-public interface ArtifactResolutionQueryResult {
-    Set<? extends SoftwareComponent> getComponents();
-    <T extends SoftwareComponent> Set<T> getComponents(Class<T> type);
-    Set<UnresolvedSoftwareComponent> getUnresolvedComponents();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrary.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrary.java
deleted file mode 100644
index 007c283..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrary.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-
-import java.util.Set;
-
-/**
- * Software component representing a JVM library.
- *
- * @since 1.12
- */
- at Incubating
-public interface JvmLibrary extends SoftwareComponent {
-    Set<JvmLibrarySourcesArtifact> getSourcesArtifacts();
-    Set<JvmLibraryJavadocArtifact> getJavadocArtifacts();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryArtifact.java
deleted file mode 100644
index 1341ee6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryArtifact.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-
-/**
- * Base interface for the artifacts of a JVM library software component.
- *
- * @since 1.12
- */
- at Incubating
-public interface JvmLibraryArtifact extends SoftwareArtifact {
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryJavadocArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryJavadocArtifact.java
deleted file mode 100644
index f8868c1..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibraryJavadocArtifact.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-
-/**
- * An artifact containing sources for a JVM library software component.
- *
- * @since 1.12
- */
- at Incubating
-public interface JvmLibraryJavadocArtifact extends JvmLibraryArtifact {
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrarySourcesArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrarySourcesArtifact.java
deleted file mode 100644
index 0788768..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/JvmLibrarySourcesArtifact.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-
-/**
- * An artifact containing sources for a JVM library software component.
- *
- * @since 1.12
- */
- at Incubating
-public interface JvmLibrarySourcesArtifact extends JvmLibraryArtifact {
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareArtifact.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareArtifact.java
deleted file mode 100644
index e8c2356..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareArtifact.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.Incubating;
-import org.gradle.api.Nullable;
-
-import java.io.File;
-
-/**
- * An artifact of a software component.
- *
- * @since 1.12
- */
- at Incubating
-public interface SoftwareArtifact {
-    /**
-     * The file for the artifact. If resolving the artifact caused a failure, that failure will be rethrown.
-     *
-     * @return the file for the artifact
-     */
-    File getFile() throws GradleException;
-
-    /**
-     * Returns the failure that occurred when the artifact was resolved, or {@code null} if no failure occurred.
-     *
-     * @return the failure that occurred when the artifact was resolved, or {@code null} if no failure occurred
-     */
-    @Nullable
-    GradleException getFailure();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareComponent.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareComponent.java
deleted file mode 100644
index 08c5ffc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/SoftwareComponent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * A software component with resolved artifacts.
- *
- * Implementations have the following equals contract:
- * {@code other != null && getClass() == other.getClass() && getId().equals(other.getId());}
- *
- * @since 1.12
- */
- at Incubating
-public interface SoftwareComponent {
-    ComponentIdentifier getId();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/UnresolvedSoftwareComponent.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/UnresolvedSoftwareComponent.java
deleted file mode 100644
index 530fc7f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/UnresolvedSoftwareComponent.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.resolution;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * A software component that couldn't be resolved.
- *
- * @since 1.12
- */
- at Incubating
-public interface UnresolvedSoftwareComponent {
-    /**
-     * Returns the ID of the component.
-     *
-     * @return the ID of the component
-     */
-    ComponentIdentifier getId();
-
-    /**
-     * Returns the failure that occurred when trying to resolve the component.
-     *
-     * @return the failure that occurred when trying to resolve the component
-     */
-    Throwable getFailure();
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/package-info.java
deleted file mode 100644
index 9d4903f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/resolution/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 comprising the artifact resolution API.
- */
-package org.gradle.api.artifacts.resolution;
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResolutionResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResolutionResult.java
new file mode 100644
index 0000000..dab85d8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResolutionResult.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+
+import java.util.Set;
+
+/**
+ * The result of executing an artifact resolution query.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ArtifactResolutionResult {
+    /**
+     * <p>Return a set of ComponentResults representing all requested components.
+     *
+     * <p>Each element in the returned set is declared as an opaque {@link org.gradle.api.artifacts.result.ComponentResult}.
+     *    However each element in the result will also implement one of the following interfaces:</p>
+     *
+     * <ul>
+     *     <li>{@link ComponentArtifactsResult} for any component whose ID could be resolved in the set of repositories.</li>
+     *     <li>{@link org.gradle.api.artifacts.result.UnresolvedComponentResult} for any component whose ID could not be resolved from the set of repositories.</li>
+     * </ul>
+     * @return the set of results for all requested components
+     */
+    Set<ComponentResult> getComponents();
+
+    /**
+     * <p>Return a set of ComponentResults representing all successfully resolved components.
+     *
+     * <p>Calling this method is the same as calling {@link #getComponents()} and filtering the resulting set for elements of type {@link ComponentArtifactsResult}.
+     * @return the set of all successfully resolved components
+     */
+    Set<ComponentArtifactsResult> getResolvedComponents();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResult.java
new file mode 100644
index 0000000..51e9d18
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ArtifactResult.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+/**
+ * The result of resolving an artifact.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ArtifactResult {
+    Class<? extends Artifact> getType();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentArtifactsResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentArtifactsResult.java
new file mode 100644
index 0000000..f2a3e47
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentArtifactsResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+import java.util.Set;
+
+/**
+ * The result of successfully resolving a component with a set of artifacts.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ComponentArtifactsResult extends ComponentResult {
+    /**
+     * <p>Returns the component artifacts of the specified type. Includes resolved and unresolved artifacts (if any).
+     *
+     * <p>The elements of the returned collection are declared as {@link ArtifactResult}, however the artifact instances will also implement one of the
+     * following instances:</p>
+     *
+     * <ul>
+     *     <li>{@link ResolvedArtifactResult} for artifacts which were successfully resolved.</li>
+     *     <li>{@link UnresolvedArtifactResult} for artifacts which could not be resolved for some reason.</li>
+     * </ul>
+     *
+     * @return the artifacts of this component with the specified type, or an empty set if no artifacts of the type exist.
+     */
+    Set<ArtifactResult> getArtifacts(Class<? extends Artifact> type);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentResult.java
new file mode 100644
index 0000000..8036762
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentResult.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+/**
+ * The result of resolving a component.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ComponentResult {
+    /**
+     * Returns the ID of the requested component.
+     */
+    ComponentIdentifier getId();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentSelectionReason.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentSelectionReason.java
index 3e66244..a8cb84c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentSelectionReason.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ComponentSelectionReason.java
@@ -42,17 +42,16 @@ public interface ComponentSelectionReason {
     boolean isConflictResolution();
 
     /**
-     * Informs whether the component was selected by the dependency resolve rule.
-     * Users can configure dependency resolve rules via {@link org.gradle.api.artifacts.ResolutionStrategy#eachDependency(org.gradle.api.Action)}
+     * Informs whether the component was selected by the dependency substitution rule.
+     * Users can configure dependency substitution rules via {@link org.gradle.api.artifacts.ResolutionStrategy#getDependencySubstitution()}
      *
      * @since 1.4
      */
     boolean isSelectedByRule();
 
     /**
-     * Informs whether the component is an expected selection.
+     * Informs whether the component is the requested selection of all dependency declarations, and was not replaced for some reason, such as conflict resolution.
      *
-     * @return Flag
      * @since 1.11
      */
     boolean isExpected();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedArtifactResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedArtifactResult.java
new file mode 100644
index 0000000..a41bfd8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedArtifactResult.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * The result of successfully downloading an artifact.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface ResolvedArtifactResult extends ArtifactResult {
+    /**
+     * The file for the artifact.
+     */
+    File getFile();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedComponentResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedComponentResult.java
index 74c27af..ec6989f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedComponentResult.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/ResolvedComponentResult.java
@@ -27,6 +27,7 @@ import java.util.Set;
  * Represents a component instance in the resolved dependency graph. Provides some basic identity and dependency information about the component.
  */
 @Incubating
+// TODO:DAZ Rename to ComponentGraphResult
 public interface ResolvedComponentResult {
 
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedArtifactResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedArtifactResult.java
new file mode 100644
index 0000000..1b47d4e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedArtifactResult.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+
+/**
+ * An artifact the could not be resolved.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface UnresolvedArtifactResult extends ArtifactResult {
+    /**
+     * The failure that occurred when the artifact was resolved.
+     */
+    Throwable getFailure();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedComponentResult.java b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedComponentResult.java
new file mode 100644
index 0000000..e93df05
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/artifacts/result/UnresolvedComponentResult.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.result;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A component that could not be resolved.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface UnresolvedComponentResult extends ComponentResult {
+    /**
+     * Returns the failure that occurred when trying to resolve the component.
+     */
+    Throwable getFailure();
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/component/Artifact.java b/subprojects/core/src/main/groovy/org/gradle/api/component/Artifact.java
new file mode 100644
index 0000000..6dd0f12
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/component/Artifact.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.component;
+
+import org.gradle.api.Incubating;
+
+/**
+ * An artifact of a software component that may be requested in the result of an artifact query.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface Artifact {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/component/Component.java b/subprojects/core/src/main/groovy/org/gradle/api/component/Component.java
new file mode 100644
index 0000000..326530f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/component/Component.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.component;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A software component that can be queried via the Artifact Query API.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface Component {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/credentials/AwsCredentials.java b/subprojects/core/src/main/groovy/org/gradle/api/credentials/AwsCredentials.java
new file mode 100644
index 0000000..2da4133
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/credentials/AwsCredentials.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.credentials;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Represents credentials used to authenticate with Amazon Web Services.
+ */
+ at Incubating
+public interface AwsCredentials extends Credentials {
+
+    /**
+     * Returns the access key to use to authenticate with AWS.
+     */
+    String getAccessKey();
+
+    /**
+     * Sets the access key to use to authenticate with AWS.
+     */
+    void setAccessKey(String accessKey);
+
+    /**
+     * Returns the secret key to use to authenticate with AWS.
+     */
+    String getSecretKey();
+
+    /**
+     * Sets the secret key to use to authenticate with AWS.
+     */
+    void setSecretKey(String secretKey);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/credentials/Credentials.java b/subprojects/core/src/main/groovy/org/gradle/api/credentials/Credentials.java
new file mode 100644
index 0000000..d24b751
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/credentials/Credentials.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.credentials;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NonExtensible;
+
+/**
+ * Base interface for credentials used for different authentication purposes.
+ * (e.g authenticated {@link org.gradle.api.artifacts.dsl.RepositoryHandler})
+ * */
+ at Incubating
+ at NonExtensible
+public interface Credentials {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/credentials/package-info.java b/subprojects/core/src/main/groovy/org/gradle/api/credentials/package-info.java
new file mode 100644
index 0000000..264f995
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/credentials/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * general credentials related classes.
+ *
+ * @since 2.4
+ */
+ at Incubating
+package org.gradle.api.credentials;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
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 7a52935..7460b5d 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
@@ -51,20 +51,37 @@ import java.util.regex.Pattern;
  * CopySpecs may be nested by passing a closure to one of the from methods.  The closure creates a child CopySpec and
  * delegates methods in the closure to the child. Child CopySpecs inherit any values specified in the parent. This
  * allows constructs like:
- * <pre>
- * into('webroot')
- * exclude('**/.svn/**')
- * from('src/main/webapp') {
- *    include '**/*.jsp'
- * }
- * from('src/main/js') {
- *    include '**/*.js'
+ * <pre autoTested=''>
+ * def myCopySpec = project.copySpec {
+ *   into('webroot')
+ *   exclude('**/.data/**')
+ *   from('src/main/webapp') {
+ *     include '**/*.jsp'
+ *   }
+ *   from('src/main/js') {
+ *     include '**/*.js'
+ *   }
  * }
  * </pre>
  *
  * In this example, the <code>into</code> and <code>exclude</code> specifications at the root level are inherited by the
  * two child CopySpecs.
  *
+ * Copy specs can be reused in other copy specs via {@link #with(CopySpec...)} method. This enables reuse of the copy spec instances.
+ *
+ * <pre autoTested=''>
+ * def contentSpec = copySpec {
+ *   from("content") {
+ *     include "**/*.txt"
+ *   }
+ * }
+ *
+ * task copy(type: Copy) {
+ *   into "$buildDir/copy"
+ *   with contentSpec
+ * }
+ * </pre>
+ *
  * @see org.gradle.api.tasks.Copy Copy Task
  * @see org.gradle.api.Project#copy(groovy.lang.Closure) Project.copy()
  */
@@ -144,6 +161,19 @@ public interface CopySpec extends CopySourceSpec, CopyProcessingSpec, PatternFil
     /**
      * Adds the given specs as a child of this spec.
      *
+     * <pre autoTested=''>
+     * def contentSpec = copySpec {
+     *   from("content") {
+     *     include "**/*.txt"
+     *   }
+     * }
+     *
+     * task copy(type: Copy) {
+     *   into "$buildDir/copy"
+     *   with contentSpec
+     * }
+     * </pre>
+     *
      * @param sourceSpecs The specs to add
      * @return this
      */
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 925ad24..f424c3a 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
@@ -37,7 +37,7 @@ public interface FileCopyDetails extends FileTreeElement, ContentFilterable {
     /**
      * Sets the destination name of this file.
      *
-     * @param name The name of this file.
+     * @param name The destination name of this file.
      */
     void setName(String name);
 
@@ -80,20 +80,51 @@ public interface FileCopyDetails extends FileTreeElement, ContentFilterable {
     DuplicatesStrategy getDuplicatesStrategy();
 
     /**
+     * Returns the base name of this file at the copy destination.
+     *
+     * @return The destination name. Never returns null.
+     */
+    String getName();
+
+    /**
      * Returns the path of this file, relative to the root of the copy destination.
      * <p>
      * Always uses '/' as the hierarchy separator, regardless of platform file separator.
      * Same as calling <code>getRelativePath().getPathString()</code>.
      *
-     * @return The path. Never returns null.
+     * @return The path, relative to the root of the copy destination. Never returns null.
      */
     String getPath();
 
     /**
      * Returns the path of this file, relative to the root of the copy destination.
      *
-     * @return The path. Never returns null.
+     * @return The path, relative to the root of the copy destination. Never returns null.
      */
     RelativePath getRelativePath();
 
+    /**
+     * Returns the base name of this file at the copy source.
+     *
+     * @return The source name. Never returns null.
+     */
+    String getSourceName();
+
+    /**
+     * Returns the path of this file, relative to the root of the containing file tree.
+     * <p>
+     * Always uses '/' as the hierarchy separator, regardless of platform file separator.
+     * Same as calling <code>getRelativeSourcePath().getPathString()</code>.
+     *
+     * @return The path, relative to the root of the containing file tree. Never returns null.
+     */
+    String getSourcePath();
+
+    /**
+     * Returns the path of this file, relative to the root of the containing file tree.
+     *
+     * @return The path, relative to the root of the containing file tree. Never returns null.
+     */
+    RelativePath getRelativeSourcePath();
+
 }
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 97ffeb2..d93d87b 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
@@ -70,7 +70,7 @@ public interface Settings extends PluginAware {
     /**
      * <p>Adds the given projects to the build. Each path in the supplied list is treated as the path of a project to
      * add to the build. Note that these path are not file paths, but instead specify the location of the new project in
-     * the project heirarchy. As such, the supplied paths must use the ':' character as separator.</p>
+     * the project hierarchy. As such, the supplied paths must use the ':' character as separator.</p>
      *
      * <p>The last element of the supplied path is used as the project name. The supplied path is converted to a project
      * directory relative to the root project directory.</p>
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractBuildableModelElement.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractBuildableModelElement.java
new file mode 100644
index 0000000..17c910c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractBuildableModelElement.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;
+
+import org.gradle.api.BuildableModelElement;
+import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class AbstractBuildableModelElement implements BuildableModelElement {
+    private final DefaultTaskDependency buildDependencies = new DefaultTaskDependency();
+    private Task lifecycleTask;
+
+    public Task getBuildTask() {
+        return lifecycleTask;
+    }
+
+    public void setBuildTask(Task lifecycleTask) {
+        this.lifecycleTask = lifecycleTask;
+        lifecycleTask.dependsOn(buildDependencies);
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return new TaskDependency() {
+            public Set<? extends Task> getDependencies(Task other) {
+                if (lifecycleTask == null) {
+                    return buildDependencies.getDependencies(other);
+                }
+                return Collections.singleton(lifecycleTask);
+            }
+        };
+    }
+
+    public void builtBy(Object... tasks) {
+        buildDependencies.add(tasks);
+    }
+
+    public boolean hasBuildDependencies() {
+        return buildDependencies.getDependencies(lifecycleTask).size() > 0;
+    }
+}
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 cf3b497..d99ca33 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
@@ -16,31 +16,46 @@
 
 package org.gradle.api.internal;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import groovy.lang.*;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
 import org.apache.commons.collections.map.AbstractReferenceMap;
 import org.apache.commons.collections.map.ReferenceMap;
-import org.codehaus.groovy.reflection.CachedClass;
 import org.gradle.api.Action;
 import org.gradle.api.GradleException;
+import org.gradle.api.NonExtensible;
+import org.gradle.api.Nullable;
 import org.gradle.api.plugins.ExtensionAware;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.*;
 
+import javax.inject.Inject;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.*;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+/**
+ * Generates a subclass of the target class to mix-in some DSL behaviour.
+ *
+ * <ul>
+ *     <li>For each property, a convention mapping is applied. These properties may have a setter method.</li>
+ *     <li>For each property whose getter is annotated with {@code Inject}, a service instance will be injected instead. These properties may have a setter method.</li>
+ *     <li>For each mutable property as set method is generated.</li>
+ *     <li>For each method whose last parameter is an {@link org.gradle.api.Action}, an override is generated that accepts a {@link groovy.lang.Closure} instead.</li>
+ *     <li>Coercion from string to enum property is mixed in.</li>
+ *     <li>{@link groovy.lang.GroovyObject} is mixed in to the class.</li>
+ * </ul>
+ */
 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();
+    private static final Collection<String> SKIP_PROPERTIES = Arrays.asList("class", "metaClass", "conventionMapping", "convention", "asDynamicObject", "extensions");
 
     public <T> T newInstance(Class<T> type, Object... parameters) {
-        Instantiator instantiator = new DirectInstantiator();
-        return instantiator.newInstance(generate(type), parameters);
+        return DirectInstantiator.instantiate(generate(type), parameters);
     }
 
     public <T> Class<? extends T> generate(Class<T> type) {
@@ -56,7 +71,7 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
         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
-            // However, the generated class has a strong reference to the source class (it extends it), so the keys will always be
+            // However, the generated class has a strong reference to the source class (by extending it), so the keys will always be
             // strongly reachable while this Class is strongly reachable. Use weak references for both key and value of the mapping instead.
             cache = new ReferenceMap(AbstractReferenceMap.WEAK, AbstractReferenceMap.WEAK);
             GENERATED_CLASSES.put(getClass(), cache);
@@ -77,11 +92,11 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
 
         Class<? extends T> subclass;
         try {
-            ClassBuilder<T> builder = start(type);
+            ClassMetaData classMetaData = inspectType(type);
 
-            boolean isConventionAware = type.getAnnotation(NoConventionMapping.class) == null;
+            ClassBuilder<T> builder = start(type, classMetaData);
 
-            builder.startClass(isConventionAware);
+            builder.startClass();
 
             if (!DynamicObjectAware.class.isAssignableFrom(type)) {
                 if (ExtensionAware.class.isAssignableFrom(type)) {
@@ -93,7 +108,7 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
                 builder.mixInGroovyObject();
             }
             builder.addDynamicMethods();
-            if (isConventionAware && !IConventionAware.class.isAssignableFrom(type)) {
+            if (classMetaData.conventionAware && !IConventionAware.class.isAssignableFrom(type)) {
                 builder.mixInConventionAware();
             }
 
@@ -104,109 +119,75 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
                 }
             }
 
-            Collection<String> skipProperties = Arrays.asList("metaClass", "conventionMapping", "convention", "asDynamicObject", "extensions");
+            Set<PropertyMetaData> conventionProperties = new HashSet<PropertyMetaData>();
 
-            Set<MetaBeanProperty> settableProperties = new HashSet<MetaBeanProperty>();
-            Set<MetaBeanProperty> conventionProperties = new HashSet<MetaBeanProperty>();
-
-            MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(type);
-            for (MetaProperty property : metaClass.getProperties()) {
-                if (skipProperties.contains(property.getName())) {
+            for (PropertyMetaData property : classMetaData.properties.values()) {
+                if (SKIP_PROPERTIES.contains(property.name)) {
                     continue;
                 }
-                if (property instanceof MetaBeanProperty) {
-                    MetaBeanProperty metaBeanProperty = (MetaBeanProperty) property;
-
-                    boolean needsConventionMapping = true;
-                    MetaMethod getter = metaBeanProperty.getGetter();
-                    if (getter == null) {
-                        needsConventionMapping = false;
-                    } else {
-                        if (Modifier.isFinal(getter.getModifiers()) || Modifier.isPrivate(getter.getModifiers())) {
-                            needsConventionMapping = false;
-                        } else {
-                            Class declaringClass = getter.getDeclaringClass().getTheClass();
-                            if (declaringClass.isAssignableFrom(noMappingClass)) {
-                                needsConventionMapping = false;
-                            }
-                        }
-                    }
 
-                    if (needsConventionMapping) {
-                        conventionProperties.add(metaBeanProperty);
-                        builder.addGetter(metaBeanProperty);
+                if (property.injector) {
+                    builder.addInjectorProperty(property);
+                    for (Method getter : property.getters) {
+                        builder.applyServiceInjectionToGetter(property, getter);
                     }
-
-                    MetaMethod setter = metaBeanProperty.getSetter();
-                    if (setter == null || Modifier.isPrivate(setter.getModifiers())) {
-                        continue;
+                    for (Method setter : property.setters) {
+                        builder.applyServiceInjectionToSetter(property, setter);
                     }
+                    continue;
+                }
 
-                    if (needsConventionMapping && !Modifier.isFinal(setter.getModifiers())) {
-                        builder.addSetter(metaBeanProperty);
+                boolean needsConventionMapping = false;
+                if (classMetaData.isExtensible()) {
+                    for (Method getter : property.getters) {
+                        if (!Modifier.isFinal(getter.getModifiers()) && !getter.getDeclaringClass().isAssignableFrom(noMappingClass)) {
+                            needsConventionMapping = true;
+                            break;
+                        }
                     }
+                }
 
-                    if (Iterable.class.isAssignableFrom(property.getType())) {
-                        continue;
+                if (needsConventionMapping) {
+                    conventionProperties.add(property);
+                    builder.addConventionProperty(property);
+                    for (Method getter : property.getters) {
+                        builder.applyConventionMappingToGetter(property, getter);
                     }
+                }
 
-                    settableProperties.add(metaBeanProperty);
+                if (needsConventionMapping) {
+                    for (Method setter : property.setters) {
+                        if (!Modifier.isFinal(setter.getModifiers())) {
+                            builder.applyConventionMappingToSetter(property, setter);
+                        }
+                    }
                 }
             }
 
-            Multimap<String, MetaMethod> methods = HashMultimap.create();
-            Set<MetaMethod> actionMethods = new HashSet<MetaMethod>();
+            Set<Method> actionMethods = classMetaData.missingOverloads;
+            for (Method method : actionMethods) {
+                builder.addActionMethod(method);
+            }
 
-            for (MetaMethod method : metaClass.getMethods()) {
-                if (method.isPrivate()) {
+            // Adds a set method for each mutable property
+            for (PropertyMetaData property : classMetaData.properties.values()) {
+                if (property.setters.isEmpty()) {
                     continue;
                 }
-                CachedClass[] parameterTypes = method.getParameterTypes();
-                if (parameterTypes.length == 0) {
+                if (Iterable.class.isAssignableFrom(property.getType())) {
+                    // Currently not supported
                     continue;
                 }
-                methods.put(method.getName(), method);
 
-                CachedClass lastParameter = parameterTypes[parameterTypes.length - 1];
-                if (lastParameter.getTheClass().equals(Action.class)) {
-                    actionMethods.add(method);
-                }
-            }
-
-            for (MetaMethod method : actionMethods) {
-                boolean hasClosure = false;
-                Class[] actionMethodParameterTypes = method.getNativeParameterTypes();
-                int numParams = actionMethodParameterTypes.length;
-                Class[] closureMethodParameterTypes = new Class[actionMethodParameterTypes.length];
-                System.arraycopy(actionMethodParameterTypes, 0, closureMethodParameterTypes, 0, actionMethodParameterTypes.length);
-                closureMethodParameterTypes[numParams - 1] = Closure.class;
-                for (MetaMethod otherMethod : methods.get(method.getName())) {
-                    if (Arrays.equals(otherMethod.getNativeParameterTypes(), closureMethodParameterTypes)) {
-                        hasClosure = true;
-                        break;
+                if (property.setMethods.isEmpty()) {
+                    for (Method setter : property.setters) {
+                        builder.addSetMethod(property, setter);
                     }
-                }
-                if (!hasClosure) {
-                    builder.addActionMethod(method);
-                }
-            }
-
-            // Adds a set method for each mutable property
-            for (MetaBeanProperty property : settableProperties) {
-                Collection<MetaMethod> methodsForProperty = methods.get(property.getName());
-                boolean hasSetMethod = false;
-                for (MetaMethod method : methodsForProperty) {
-                    if (method.getParameterTypes().length == 1) {
-                        if (conventionProperties.contains(property)) {
-                            builder.overrideSetMethod(property, method);
-                        }
-                        hasSetMethod = true;
+                } else if (conventionProperties.contains(property)) {
+                    for (Method setMethod : property.setMethods) {
+                        builder.applyConventionMappingToSetMethod(property, setMethod);
                     }
                 }
-
-                if (!hasSetMethod) {
-                    builder.addSetMethod(property);
-                }
             }
 
             for (Constructor<?> constructor : type.getConstructors()) {
@@ -225,10 +206,198 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
         return subclass;
     }
 
-    protected abstract <T> ClassBuilder<T> start(Class<T> type);
+    protected abstract <T> ClassBuilder<T> start(Class<T> type, ClassMetaData classMetaData);
+
+    private ClassMetaData inspectType(Class<?> type) {
+        boolean isConventionAware = type.getAnnotation(NoConventionMapping.class) == null;
+        boolean extensible = JavaReflectionUtil.getAnnotation(type, NonExtensible.class) == null;
+
+        ClassMetaData classMetaData = new ClassMetaData(extensible, isConventionAware);
+        inspectType(type, classMetaData);
+        attachSetMethods(classMetaData);
+        findMissingClosureOverloads(classMetaData);
+        classMetaData.complete();
+        return classMetaData;
+    }
+
+    private void findMissingClosureOverloads(ClassMetaData classMetaData) {
+        for (Method method : classMetaData.actionMethods) {
+            Method overload = findClosureOverload(method, classMetaData.closureMethods.get(method.getName()));
+            if (overload == null) {
+                classMetaData.actionMethodRequiresOverload(method);
+            }
+        }
+    }
+
+    private Method findClosureOverload(Method method, Collection<Method> candidates) {
+        for (Method candidate : candidates) {
+            if (candidate.getParameterTypes().length != method.getParameterTypes().length) {
+                continue;
+            }
+            boolean matches = true;
+            for (int i = 0; matches && i < candidate.getParameterTypes().length - 1; i++) {
+                if (!candidate.getParameterTypes()[i].equals(method.getParameterTypes()[i])) {
+                    matches = false;
+                }
+            }
+            if (matches) {
+                return candidate;
+            }
+        }
+        return null;
+    }
+
+    private void attachSetMethods(ClassMetaData classMetaData) {
+        for (Method method : classMetaData.setMethods) {
+            PropertyMetaData property = classMetaData.getProperty(method.getName());
+            if (property != null) {
+                property.addSetMethod(method);
+            }
+        }
+    }
+
+    private void inspectType(Class<?> type, ClassMetaData classMetaData) {
+        ClassDetails classDetails = ClassInspector.inspect(type);
+        for (Method method : classDetails.getAllMethods()) {
+            if (method.getAnnotation(Inject.class) != null) {
+                if (!Modifier.isPublic(method.getModifiers()) && !Modifier.isProtected(method.getModifiers())) {
+                    throw new UnsupportedOperationException(String.format("Cannot attach @Inject to method %s.%s() as it is not public or protected.", method.getDeclaringClass().getSimpleName(), method.getName()));
+                }
+                if (Modifier.isStatic(method.getModifiers())) {
+                    throw new UnsupportedOperationException(String.format("Cannot attach @Inject to method %s.%s() as it is static.", method.getDeclaringClass().getSimpleName(), method.getName()));
+                }
+            }
+        }
+        for (PropertyDetails property : classDetails.getProperties()) {
+            PropertyMetaData propertyMetaData = classMetaData.property(property.getName());
+            for (Method method : property.getGetters()) {
+                propertyMetaData.addGetter(method);
+            }
+            for (Method method : property.getSetters()) {
+                propertyMetaData.addSetter(method);
+            }
+        }
+        for (Method method : classDetails.getInstanceMethods()) {
+            Class<?>[] parameterTypes = method.getParameterTypes();
+            if (parameterTypes.length == 1) {
+                classMetaData.addCandidateSetMethod(method);
+            }
+            if (parameterTypes.length > 0 && parameterTypes[parameterTypes.length - 1].equals(Action.class)) {
+                classMetaData.addActionMethod(method);
+            } else if (parameterTypes.length > 0 && parameterTypes[parameterTypes.length - 1].equals(Closure.class)) {
+                classMetaData.addClosureMethod(method);
+            }
+        }
+    }
+
+    protected static class ClassMetaData {
+        private final Map<String, PropertyMetaData> properties = new LinkedHashMap<String, PropertyMetaData>();
+        private final Set<Method> missingOverloads = new LinkedHashSet<Method>();
+        private final boolean extensible;
+        private final boolean conventionAware;
+        private List<Method> actionMethods = new ArrayList<Method>();
+        private SetMultimap<String, Method> closureMethods = LinkedHashMultimap.create();
+        private List<Method> setMethods = new ArrayList<Method>();
+
+        public ClassMetaData(boolean extensible, boolean conventionAware) {
+            this.extensible = extensible;
+            this.conventionAware = conventionAware;
+        }
+
+        @Nullable
+        public PropertyMetaData getProperty(String name) {
+            return properties.get(name);
+        }
+
+        public PropertyMetaData property(String name) {
+            PropertyMetaData property = properties.get(name);
+            if (property == null) {
+                property = new PropertyMetaData(name);
+                properties.put(name, property);
+            }
+            return property;
+        }
+
+        public void addActionMethod(Method method) {
+            actionMethods.add(method);
+        }
+
+        public void addClosureMethod(Method method) {
+            closureMethods.put(method.getName(), method);
+        }
+
+        public void addCandidateSetMethod(Method method) {
+            setMethods.add(method);
+        }
+
+        public void complete() {
+            setMethods = null;
+            actionMethods = null;
+            closureMethods = null;
+        }
+
+        public void actionMethodRequiresOverload(Method method) {
+            missingOverloads.add(method);
+        }
+
+        public boolean providesDynamicObjectImplementation() {
+            PropertyMetaData property = properties.get("asDynamicObject");
+            return property != null && !property.getters.isEmpty();
+        }
+
+        public boolean isExtensible() {
+            return extensible;
+        }
+
+        public boolean isConventionAware() {
+            return conventionAware;
+        }
+    }
+
+    protected static class PropertyMetaData {
+        final String name;
+        final List<Method> getters = new ArrayList<Method>();
+        final List<Method> setters = new ArrayList<Method>();
+        final List<Method> setMethods = new ArrayList<Method>();
+        boolean injector;
+
+        private PropertyMetaData(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[property %s]", name);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Class<?> getType() {
+            if (!getters.isEmpty()) {
+                return getters.get(0).getReturnType();
+            }
+            return setters.get(0).getParameterTypes()[0];
+        }
+
+        public void addGetter(Method method) {
+            if (getters.add(method) && method.getAnnotation(Inject.class) != null) {
+                injector = true;
+            }
+        }
+
+        public void addSetter(Method method) {
+            setters.add(method);
+        }
+
+        public void addSetMethod(Method method) {
+            setMethods.add(method);
+        }
+    }
 
     protected interface ClassBuilder<T> {
-        void startClass(boolean isConventionAware);
+        void startClass();
 
         void addConstructor(Constructor<?> constructor) throws Exception;
 
@@ -240,16 +409,24 @@ public abstract class AbstractClassGenerator implements ClassGenerator {
 
         void addDynamicMethods() throws Exception;
 
-        void addGetter(MetaBeanProperty property) throws Exception;
+        void addInjectorProperty(PropertyMetaData property);
 
-        void addSetter(MetaBeanProperty property) throws Exception;
+        void applyServiceInjectionToGetter(PropertyMetaData property, Method getter) throws Exception;
 
-        void overrideSetMethod(MetaBeanProperty property, MetaMethod metaMethod) throws Exception;
+        void applyServiceInjectionToSetter(PropertyMetaData property, Method setter) throws Exception;
 
-        void addSetMethod(MetaBeanProperty property) throws Exception;
+        void addConventionProperty(PropertyMetaData property) throws Exception;
 
-        Class<? extends T> generate() throws Exception;
+        void applyConventionMappingToGetter(PropertyMetaData property, Method getter) throws Exception;
+
+        void applyConventionMappingToSetter(PropertyMetaData property, Method setter) throws Exception;
+
+        void applyConventionMappingToSetMethod(PropertyMetaData property, Method metaMethod) throws Exception;
 
-        void addActionMethod(MetaMethod method) throws Exception;
+        void addSetMethod(PropertyMetaData propertyMetaData, Method setter) throws Exception;
+
+        void addActionMethod(Method method) throws Exception;
+
+        Class<? extends T> generate() throws Exception;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractDynamicObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractDynamicObject.java
index 964e691..11ed215 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractDynamicObject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/AbstractDynamicObject.java
@@ -40,7 +40,7 @@ public abstract class AbstractDynamicObject implements DynamicObject {
     }
 
     protected MissingPropertyException propertyMissingException(String name) {
-        throw new MissingPropertyException(String.format("Could not find property '%s' on %s.", name,
+        return new MissingPropertyException(String.format("Could not find property '%s' on %s.", name,
                 getDisplayName()), name, null);
     }
 
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 72d35c4..76d87e0 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
@@ -15,6 +15,8 @@
  */
 package org.gradle.api.internal;
 
+import groovy.lang.Closure;
+import groovy.lang.MissingPropertyException;
 import org.gradle.api.*;
 import org.gradle.api.internal.plugins.DefaultConvention;
 import org.gradle.api.plugins.Convention;
@@ -22,13 +24,10 @@ import org.gradle.internal.Transformers;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.ConfigureUtil;
 
-import groovy.lang.Closure;
-import groovy.lang.MissingPropertyException;
-
-import java.util.Map;
+import java.util.*;
 
 public abstract class AbstractPolymorphicDomainObjectContainer<T>
-        extends AbstractNamedDomainObjectContainer<T> implements PolymorphicDomainObjectContainer<T> {
+        extends AbstractNamedDomainObjectContainer<T> implements PolymorphicDomainObjectContainerInternal<T> {
 
     private final ContainerElementsDynamicObject elementsDynamicObject = new ContainerElementsDynamicObject();
     private final Convention convention;
@@ -40,10 +39,6 @@ public abstract class AbstractPolymorphicDomainObjectContainer<T>
         this.dynamicObject = new ExtensibleDynamicObject(this, new ContainerDynamicObject(elementsDynamicObject), convention);
     }
 
-    protected AbstractPolymorphicDomainObjectContainer(Class<T> type, Instantiator instantiator) {
-        this(type, instantiator, Named.Namer.forType(type));
-    }
-
     protected abstract <U extends T> U doCreate(String name, Class<U> type);
 
     public <U extends T> U create(String name, Class<U> type) {
@@ -150,4 +145,9 @@ public abstract class AbstractPolymorphicDomainObjectContainer<T>
                     && hasProperty(name);
         }
     }
+
+    public <U extends T> NamedDomainObjectContainer<U> containerWithType(Class<U> type) {
+        return getInstantiator().newInstance(TypedDomainObjectContainerWrapper.class, type, this, getInstantiator());
+    }
+
 }
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 e53e9ec..a14512e 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
@@ -30,7 +30,6 @@ 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;
-import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.specs.AndSpec;
@@ -38,7 +37,6 @@ import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.api.tasks.TaskInputs;
 import org.gradle.api.tasks.TaskInstantiationException;
-import org.gradle.api.tasks.TaskState;
 import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
@@ -47,6 +45,7 @@ import org.gradle.logging.StandardOutputCapture;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.GFileUtils;
 
+import javax.inject.Inject;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
@@ -85,22 +84,21 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
 
     private AndSpec<Task> onlyIfSpec = new AndSpec<Task>(createNewOnlyIfSpec());
 
-    private final TaskOutputsInternal outputs;
-
-    private final TaskInputs inputs;
-
     private TaskExecuter executer;
 
     private final ServiceRegistry services;
 
     private final TaskStateInternal state;
 
-    private final LoggingManagerInternal loggingManager;
-
     private List<TaskValidator> validators = new ArrayList<TaskValidator>();
 
-    private final TaskStatusNagger taskStatusNagger;
+    private final TaskMutator taskMutator;
     private ObservableList observableActionList;
+    private boolean impliesSubProjects;
+    private boolean hasCustomActions;
+
+    // toString() of AbstractTask is called a lot, so precompute.
+    private final String toStringValue;
 
     protected AbstractTask() {
         this(taskInfo());
@@ -126,19 +124,16 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         finalizedBy = new DefaultTaskDependency(project.getTasks());
         shouldRunAfter = new DefaultTaskDependency(project.getTasks());
         services = project.getServiceRegistryFactory().createFor(this);
-        extensibleDynamicObject = new ExtensibleDynamicObject(this, getServices().get(Instantiator.class));
-        taskStatusNagger = services.get(TaskStatusNagger.class);
-        outputs = services.get(TaskOutputsInternal.class);
-        inputs = services.get(TaskInputs.class);
-        executer = services.get(TaskExecuter.class);
+        extensibleDynamicObject = new ExtensibleDynamicObject(this, services.get(Instantiator.class));
+        taskMutator = services.get(TaskMutator.class);
 
         observableActionList = new ObservableActionWrapperList(actions);
         observableActionList.addPropertyChangeListener(new PropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent evt) {
-                taskStatusNagger.nagAboutMutatingListIfTaskNotInConfigurableState("Task.getActions()", evt);
+                taskMutator.assertMutable("Task.getActions()", evt);
             }
         });
-        loggingManager = services.get(LoggingManagerInternal.class);
+        toStringValue = "task '".concat(path).concat("'");
     }
 
     public static <T extends Task> T injectIntoNewInstance(ProjectInternal project, String name, Callable<T> factory) {
@@ -156,11 +151,7 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         }
     }
 
-    public TaskState getState() {
-        return state;
-    }
-
-    public TaskStateInternal getStateInternal() {
+    public TaskStateInternal getState() {
         return state;
     }
 
@@ -192,12 +183,11 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return observableActionList;
     }
 
-    public void setActions(final List<Action<? super Task>> actions) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setActions(Actions<Task>)");
-        taskStatusNagger.whileDisabled(new Runnable() {
+    public void setActions(final List<Action<? super Task>> replacements) {
+        taskMutator.mutate("Task.setActions(List<Action>)", new Runnable() {
             public void run() {
-                deleteAllActions();
-                for (Action<? super Task> action : actions) {
+                actions.clear();
+                for (Action<? super Task> action : replacements) {
                     doLast(action);
                 }
             }
@@ -212,29 +202,44 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return dependencies.getValues();
     }
 
-    public void setDependsOn(Iterable<?> dependsOn) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setDependsOn(Iterable)");
-        dependencies.setValues(dependsOn);
+    public void setDependsOn(final Iterable<?> dependsOn) {
+        taskMutator.mutate("Task.setDependsOn(Iterable)", new Runnable() {
+            public void run() {
+                dependencies.setValues(dependsOn);
+            }
+        });
     }
 
-    public void onlyIf(Closure onlyIfClosure) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.onlyIf(Closure)");
-        this.onlyIfSpec = this.onlyIfSpec.and(onlyIfClosure);
+    public void onlyIf(final Closure onlyIfClosure) {
+        taskMutator.mutate("Task.onlyIf(Closure)", new Runnable() {
+            public void run() {
+                onlyIfSpec = onlyIfSpec.and(onlyIfClosure);
+            }
+        });
     }
 
-    public void onlyIf(Spec<? super Task> onlyIfSpec) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.onlyIf(Spec)");
-        this.onlyIfSpec = this.onlyIfSpec.and(onlyIfSpec);
+    public void onlyIf(final Spec<? super Task> spec) {
+        taskMutator.mutate("Task.onlyIf(Spec)", new Runnable() {
+            public void run() {
+                onlyIfSpec = onlyIfSpec.and(spec);
+            }
+        });
     }
 
-    public void setOnlyIf(Spec<? super Task> spec) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setOnlyIf(Spec)");
-        onlyIfSpec = createNewOnlyIfSpec().and(spec);
+    public void setOnlyIf(final Spec<? super Task> spec) {
+        taskMutator.mutate("Task.setOnlyIf(Spec)", new Runnable() {
+            public void run() {
+                onlyIfSpec = createNewOnlyIfSpec().and(spec);
+            }
+        });
     }
 
-    public void setOnlyIf(Closure onlyIfClosure) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setOnlyIf(Closure)");
-        onlyIfSpec = createNewOnlyIfSpec().and(onlyIfClosure);
+    public void setOnlyIf(final Closure onlyIfClosure) {
+        taskMutator.mutate("Task.setOnlyIf(Closure)", new Runnable() {
+            public void run() {
+                onlyIfSpec = createNewOnlyIfSpec().and(onlyIfClosure);
+            }
+        });
     }
 
     private AndSpec<Task> createNewOnlyIfSpec() {
@@ -265,9 +270,20 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return enabled;
     }
 
-    public void setEnabled(boolean enabled) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setEnabled(boolean)");
-        this.enabled = enabled;
+    public void setEnabled(final boolean enabled) {
+        taskMutator.mutate("Task.setEnabled(boolean)", new Runnable() {
+            public void run() {
+                AbstractTask.this.enabled = enabled;
+            }
+        });
+    }
+
+    public boolean getImpliesSubProjects() {
+        return impliesSubProjects;
+    }
+
+    public void setImpliesSubProjects(boolean impliesSubProjects) {
+        this.impliesSubProjects = impliesSubProjects;
     }
 
     public String getPath() {
@@ -275,8 +291,13 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     }
 
     public Task deleteAllActions() {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.deleteAllActions()");
-        actions.clear();
+        taskMutator.mutate("Task.deleteAllActions()",
+                new Runnable() {
+                    public void run() {
+                        actions.clear();
+                    }
+                }
+        );
         return this;
     }
 
@@ -286,10 +307,13 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     }
 
     public void executeWithoutThrowingTaskFailure() {
-        executer.execute(this, state, new DefaultTaskExecutionContext());
+        getExecuter().execute(this, state, new DefaultTaskExecutionContext());
     }
 
     public TaskExecuter getExecuter() {
+        if (executer == null) {
+            executer = services.get(TaskExecuter.class);
+        }
         return executer;
     }
 
@@ -297,27 +321,38 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         this.executer = executer;
     }
 
-    public Task dependsOn(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.dependsOn(Object...)");
-        dependencies.add(paths);
+    public Task dependsOn(final Object... paths) {
+        taskMutator.mutate("Task.dependsOn(Object...)", new Runnable() {
+            public void run() {
+                dependencies.add(paths);
+            }
+        });
         return this;
     }
 
-    public Task doFirst(Action<? super Task> action) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.doFirst(Action)");
+    public Task doFirst(final Action<? super Task> action) {
+        hasCustomActions = true;
         if (action == null) {
             throw new InvalidUserDataException("Action must not be null!");
         }
-        actions.add(0, wrap(action));
+        taskMutator.mutate("Task.doFirst(Action)", new Runnable() {
+            public void run() {
+                actions.add(0, wrap(action));
+            }
+        });
         return this;
     }
 
-    public Task doLast(Action<? super Task> action) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.doLast(Action)");
+    public Task doLast(final Action<? super Task> action) {
+        hasCustomActions = true;
         if (action == null) {
             throw new InvalidUserDataException("Action must not be null!");
         }
-        actions.add(wrap(action));
+        taskMutator.mutate("Task.doLast(Action)", new Runnable() {
+            public void run() {
+                actions.add(wrap(action));
+            }
+        });
         return this;
     }
 
@@ -331,19 +366,21 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     }
 
     public String toString() {
-        return String.format("task '%s'", path);
+        return toStringValue;
     }
 
     public Logger getLogger() {
         return buildLogger;
     }
 
-    public LoggingManager getLogging() {
-        return loggingManager;
+    @Inject
+    public LoggingManagerInternal getLogging() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     public StandardOutputCapture getStandardOutputCapture() {
-        return loggingManager;
+        return getLogging();
     }
 
     public Object property(String propertyName) throws MissingPropertyException {
@@ -386,12 +423,16 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         this.group = group;
     }
 
+    @Inject
     public TaskInputs getInputs() {
-        return inputs;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
+    @Inject
     public TaskOutputsInternal getOutputs() {
-        return outputs;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     protected ServiceRegistry getServices() {
@@ -408,30 +449,42 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return false;
     }
 
-    public Task doFirst(Closure action) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.doFirst(Closure)");
+    public Task doFirst(final Closure action) {
+        hasCustomActions = true;
         if (action == null) {
             throw new InvalidUserDataException("Action must not be null!");
         }
-        actions.add(0, convertClosureToAction(action));
+        taskMutator.mutate("Task.doFirst(Closure)", new Runnable() {
+            public void run() {
+                actions.add(0, convertClosureToAction(action));
+            }
+        });
         return this;
     }
 
-    public Task doLast(Closure action) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.doLast(Closure)");
+    public Task doLast(final Closure action) {
+        hasCustomActions = true;
         if (action == null) {
             throw new InvalidUserDataException("Action must not be null!");
         }
-        actions.add(convertClosureToAction(action));
+        taskMutator.mutate("Task.doLast(Closure)", new Runnable() {
+            public void run() {
+                actions.add(convertClosureToAction(action));
+            }
+        });
         return this;
     }
 
     public Task leftShift(final Closure action) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.leftShit(Closure)");
+        hasCustomActions = true;
         if (action == null) {
             throw new InvalidUserDataException("Action must not be null!");
         }
-        actions.add(taskStatusNagger.leftShift(convertClosureToAction(action)));
+        taskMutator.mutate("Task.leftShit(Closure)", new Runnable() {
+            public void run() {
+                actions.add(taskMutator.leftShift(convertClosureToAction(action)));
+            }
+        });
         return this;
     }
 
@@ -467,6 +520,9 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
     }
 
     private ContextAwareTaskAction wrap(final Action<? super Task> action) {
+        if (action instanceof ContextAwareTaskAction) {
+            return (ContextAwareTaskAction) action;
+        }
         return new TaskActionWrapper(action);
     }
 
@@ -560,14 +616,20 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         }
     }
 
-    public void setMustRunAfter(Iterable<?> mustRunAfterTasks) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setMustRunAfter(Iterable)");
-        mustRunAfter.setValues(mustRunAfterTasks);
+    public void setMustRunAfter(final Iterable<?> mustRunAfterTasks) {
+        taskMutator.mutate("Task.setMustRunAfter(Iterable)", new Runnable() {
+            public void run() {
+                mustRunAfter.setValues(mustRunAfterTasks);
+            }
+        });
     }
 
-    public Task mustRunAfter(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.mustRunAfter(Object...)");
-        mustRunAfter.add(paths);
+    public Task mustRunAfter(final Object... paths) {
+        taskMutator.mutate("Task.mustRunAfter(Object...)", new Runnable() {
+            public void run() {
+                mustRunAfter.add(paths);
+            }
+        });
         return this;
     }
 
@@ -575,14 +637,20 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return mustRunAfter;
     }
 
-    public void setFinalizedBy(Iterable<?> finalizedByTasks) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setFinalizedBy(Iterable)");
-        finalizedBy.setValues(finalizedByTasks);
+    public void setFinalizedBy(final Iterable<?> finalizedByTasks) {
+        taskMutator.mutate("Task.setFinalizedBy(Iterable)", new Runnable() {
+            public void run() {
+                finalizedBy.setValues(finalizedByTasks);
+            }
+        });
     }
 
-    public Task finalizedBy(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.finalizedBy(Object...)");
-        finalizedBy.add(paths);
+    public Task finalizedBy(final Object... paths) {
+        taskMutator.mutate("Task.finalizedBy(Object...)", new Runnable() {
+            public void run() {
+                finalizedBy.add(paths);
+            }
+        });
         return this;
     }
 
@@ -590,15 +658,21 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         return finalizedBy;
     }
 
-    public TaskDependency shouldRunAfter(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.shouldRunAfter(Object...)");
-        shouldRunAfter.add(paths);
+    public TaskDependency shouldRunAfter(final Object... paths) {
+        taskMutator.mutate("Task.shouldRunAfter(Object...)", new Runnable() {
+            public void run() {
+                shouldRunAfter.add(paths);
+            }
+        });
         return shouldRunAfter;
     }
 
-    public void setShouldRunAfter(Iterable<?> shouldRunAfterTasks) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("Task.setShouldRunAfter(Iterable)");
-        shouldRunAfter.setValues(shouldRunAfterTasks);
+    public void setShouldRunAfter(final Iterable<?> shouldRunAfterTasks) {
+        taskMutator.mutate("Task.setShouldRunAfter(Iterable)", new Runnable() {
+            public void run() {
+                shouldRunAfter.setValues(shouldRunAfterTasks);
+            }
+        });
     }
 
     public TaskDependency getShouldRunAfter() {
@@ -649,7 +723,7 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
 
         @Override
         public boolean remove(Object action) {
-            return super.remove(wrap((Action<? super Task>)action));
+            return super.remove(wrap((Action<? super Task>) action));
         }
 
         private Collection<ContextAwareTaskAction> transformToContextAwareTaskActions(Collection<Object> c) {
@@ -660,4 +734,22 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
             });
         }
     }
+
+    public void prependParallelSafeAction(final Action<? super Task> action) {
+        if (action == null) {
+            throw new InvalidUserDataException("Action must not be null!");
+        }
+        actions.add(0, wrap(action));
+    }
+
+    public void appendParallelSafeAction(final Action<? super Task> action) {
+        if (action == null) {
+            throw new InvalidUserDataException("Action must not be null!");
+        }
+        actions.add(wrap(action));
+    }
+
+    public boolean isHasCustomActions() {
+        return hasCustomActions;
+    }
 }
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 7e87be7..0160fd3 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
@@ -15,14 +15,16 @@
  */
 package org.gradle.api.internal;
 
+import com.google.common.collect.ImmutableSet;
 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;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.reflect.JavaMethod;
 import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.util.CollectionUtils;
-import org.gradle.internal.reflect.JavaMethod;
 import org.objectweb.asm.*;
 import org.objectweb.asm.Type;
 
@@ -33,24 +35,26 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.*;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 public class AsmBackedClassGenerator extends AbstractClassGenerator {
     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) {
-        return new ClassBuilderImpl<T>(type);
+    protected <T> ClassBuilder<T> start(Class<T> type, ClassMetaData classMetaData) {
+        return new ClassBuilderImpl<T>(type, classMetaData);
     }
 
     private static class ClassBuilderImpl<T> implements ClassBuilder<T> {
+        public static final Set<? extends Class<?>> PRIMITIVE_TYPES = ImmutableSet.of(Byte.TYPE, Boolean.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE);
+        private static final String DYNAMIC_OBJECT_HELPER_FIELD = "__dyn_obj__";
+        private static final String MAPPING_FIELD = "__mapping__";
+        private static final String META_CLASS_FIELD = "__meta_class__";
         private final ClassWriter visitor;
         private final Class<T> type;
         private final String typeName;
         private final Type generatedType;
         private final Type superclassType;
-        private MethodCodeBody initDynamicObjectHelper;
-        private MethodCodeBody initConventionAwareHelper;
-        private MethodCodeBody initMetaClass;
         private final Type conventionAwareType = Type.getType(IConventionAware.class);
         private final Type dynamicObjectAwareType = Type.getType(DynamicObjectAware.class);
         private final Type extensionAwareType = Type.getType(ExtensionAware.class);
@@ -62,22 +66,25 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         private final Type extensibleDynamicObjectHelperType = Type.getType(MixInExtensibleDynamicObject.class);
         private final Type nonExtensibleDynamicObjectHelperType = Type.getType(BeanDynamicObject.class);
 
+        private final boolean conventionAware;
         private final boolean extensible;
+        private final boolean providesOwnDynamicObject;
 
-        private ClassBuilderImpl(Class<T> type) {
+        private ClassBuilderImpl(Class<T> type, ClassMetaData classMetaData) {
             this.type = type;
 
             visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS);
             typeName = type.getName() + "_Decorated";
             generatedType = Type.getType("L" + typeName.replaceAll("\\.", "/") + ";");
             superclassType = Type.getType(type);
-
-            extensible = JavaReflectionUtil.getAnnotation(type, NonExtensible.class) == null;
+            extensible = classMetaData.isExtensible();
+            conventionAware = classMetaData.isConventionAware();
+            providesOwnDynamicObject = classMetaData.providesDynamicObjectImplementation();
         }
 
-        public void startClass(boolean isConventionAware) {
+        public void startClass() {
             List<String> interfaceTypes = new ArrayList<String>();
-            if (isConventionAware && extensible) {
+            if (conventionAware && extensible) {
                 interfaceTypes.add(conventionAwareType.getInternalName());
             }
 
@@ -89,6 +96,8 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             interfaceTypes.add(dynamicObjectAwareType.getInternalName());
             interfaceTypes.add(groovyObjectType.getInternalName());
 
+            includeNotInheritedAnnotations();
+
             visitor.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, generatedType.getInternalName(), null,
                     superclassType.getInternalName(), interfaceTypes.toArray(new String[interfaceTypes.size()]));
         }
@@ -101,10 +110,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, paramTypes.toArray(
                     new Type[paramTypes.size()]));
 
-            String signature = signature(constructor);
-
-            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", methodDescriptor, signature,
-                    new String[0]);
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", methodDescriptor, signature(constructor), new String[0]);
 
             for (Annotation annotation : constructor.getDeclaredAnnotations()) {
                 if (annotation.annotationType().getAnnotation(Inherited.class) != null) {
@@ -125,16 +131,6 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>",
                     methodDescriptor);
 
-            if (initDynamicObjectHelper != null) {
-                initDynamicObjectHelper.add(methodVisitor);
-            }
-            if (initConventionAwareHelper != null) {
-                initConventionAwareHelper.add(methodVisitor);
-            }
-            if (initMetaClass != null) {
-                initMetaClass.add(methodVisitor);
-            }
-
             methodVisitor.visitInsn(Opcodes.RETURN);
             methodVisitor.visitMaxs(0, 0);
             methodVisitor.visitEnd();
@@ -145,9 +141,44 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
          */
         private String signature(Constructor<?> constructor) {
             StringBuilder builder = new StringBuilder();
-            if (constructor.getTypeParameters().length > 0) {
+            visitFormalTypeParameters(builder, constructor.getTypeParameters());
+            visitParameters(builder, constructor.getGenericParameterTypes());
+            builder.append("V");
+            visitExceptions(builder, constructor.getGenericExceptionTypes());
+            return builder.toString();
+        }
+
+        /**
+         * Generates the signature for the given method
+         */
+        private String signature(Method method) {
+            StringBuilder builder = new StringBuilder();
+            visitFormalTypeParameters(builder, method.getTypeParameters());
+            visitParameters(builder, method.getGenericParameterTypes());
+            visitType(method.getGenericReturnType(), builder);
+            visitExceptions(builder, method.getGenericExceptionTypes());
+            return builder.toString();
+        }
+
+        private void visitExceptions(StringBuilder builder, java.lang.reflect.Type[] exceptionTypes) {
+            for (java.lang.reflect.Type exceptionType : exceptionTypes) {
+                builder.append('^');
+                visitType(exceptionType, builder);
+            }
+        }
+
+        private void visitParameters(StringBuilder builder, java.lang.reflect.Type[] parameterTypes) {
+            builder.append('(');
+            for (java.lang.reflect.Type paramType : parameterTypes) {
+                visitType(paramType, builder);
+            }
+            builder.append(")");
+        }
+
+        private void visitFormalTypeParameters(StringBuilder builder, TypeVariable<?>[] typeParameters) {
+            if (typeParameters.length > 0) {
                 builder.append('<');
-                for (TypeVariable<?> typeVariable : constructor.getTypeParameters()) {
+                for (TypeVariable<?> typeVariable : typeParameters) {
                     builder.append(typeVariable.getName());
                     for (java.lang.reflect.Type bound : typeVariable.getBounds()) {
                         builder.append(':');
@@ -156,16 +187,6 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                 }
                 builder.append('>');
             }
-            builder.append('(');
-            for (java.lang.reflect.Type paramType : constructor.getGenericParameterTypes()) {
-                visitType(paramType, builder);
-            }
-            builder.append(")V");
-            for (java.lang.reflect.Type exceptionType : constructor.getGenericExceptionTypes()) {
-                builder.append('^');
-                visitType(exceptionType, builder);
-            }
-            return builder.toString();
         }
 
         private void visitType(java.lang.reflect.Type type, StringBuilder builder) {
@@ -237,35 +258,28 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
         public void mixInDynamicAware() throws Exception {
 
-            // GENERATE private MixInExtensibleDynamicObject dynamicObjectHelper = new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
+            // GENERATE private DynamicObject dynamicObjectHelper
 
-            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 {
-                    generateCreateDynamicObject(visitor);
-                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
-                            fieldSignature);
-                    // END
-                }
-            };
+            Type extensibleObjectFieldType = extensible ? extensibleDynamicObjectHelperType : nonExtensibleDynamicObjectHelperType;
+            final String fieldSignature = extensibleObjectFieldType.getDescriptor();
+            visitor.visitField(Opcodes.ACC_PRIVATE, DYNAMIC_OBJECT_HELPER_FIELD, fieldSignature, null, null);
 
             // END
 
+            final Method getAsDynamicObject = DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject");
             if (extensible) {
-                // GENERATE public Convention getConvention() { return dynamicObjectHelper.getConvention(); }
+                // GENERATE public Convention getConvention() { return getAsDynamicObject().getConvention(); }
 
                 addGetter(HasConvention.class.getDeclaredMethod("getConvention"), new MethodCodeBody() {
                     public void add(MethodVisitor visitor) throws Exception {
 
-                        // GENERATE dynamicObjectHelper.getConvention()
+                        // GENERATE ((MixInExtensibleDynamicObject)getAsDynamicObject()).getConvention()
 
                         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, generatedType.getInternalName(), "getAsDynamicObject",
+                                Type.getMethodDescriptor(getAsDynamicObject));
+                        visitor.visitTypeInsn(Opcodes.CHECKCAST, extensibleDynamicObjectHelperType.getInternalName());
+                        String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod("getConvention"));
                         visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, extensibleDynamicObjectHelperType.getInternalName(), "getConvention",
                                 getterDescriptor);
                     }
@@ -281,8 +295,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                         // GENERATE getConvention()
 
                         visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                        String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod(
-                                "getConvention"));
+                        String getterDescriptor = Type.getMethodDescriptor(ExtensibleDynamicObject.class.getDeclaredMethod("getConvention"));
                         visitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), "getConvention",
                                 getterDescriptor);
                     }
@@ -291,13 +304,26 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
             // END
 
-            // GENERATE public DynamicObject.getAsDynamicObject() { return dynamicObjectHelper; }
-
-            addGetter(DynamicObjectAware.class.getDeclaredMethod("getAsDynamicObject"), new MethodCodeBody() {
+            // GENERATE public DynamicObject getAsDynamicObject() {
+            //      if (dynamicObjectHelper == null) {
+            //          dynamicObjectHelper = <init>
+            //      }
+            //      return dynamicObjectHelper;
+            // }
+            addGetter(getAsDynamicObject, new MethodCodeBody() {
                 public void add(MethodVisitor visitor) {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "dynamicObjectHelper",
-                            fieldSignature);
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), DYNAMIC_OBJECT_HELPER_FIELD, fieldSignature);
+                    Label returnValue = new Label();
+                    visitor.visitInsn(Opcodes.DUP);
+                    visitor.visitJumpInsn(Opcodes.IFNONNULL, returnValue);
+                    visitor.visitInsn(Opcodes.POP);
+                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                    generateCreateDynamicObject(visitor);
+                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), DYNAMIC_OBJECT_HELPER_FIELD, fieldSignature);
+                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), DYNAMIC_OBJECT_HELPER_FIELD, fieldSignature);
+                    visitor.visitLabel(returnValue);
                 }
             });
 
@@ -307,27 +333,20 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         private void generateCreateDynamicObject(MethodVisitor visitor) {
             if (extensible) {
 
-                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
-                        Type.getType(Object.class), dynamicObjectType
-                });
-
-                // GENERATE dynamicObjectHelper = new MixInExtensibleDynamicObject(this, super.getAsDynamicObject())
-
-                visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class), dynamicObjectType);
 
                 // 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) {
+                if (providesOwnDynamicObject) {
                     // GENERATE super.getAsDynamicObject()
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
                     visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getType(type).getInternalName(),
-                            "getAsDynamicObject", Type.getMethodDescriptor(dynamicObjectType, new Type[0]));
+                            "getAsDynamicObject", Type.getMethodDescriptor(dynamicObjectType));
                 } else {
                     // GENERATE null
                     visitor.visitInsn(Opcodes.ACONST_NULL);
@@ -337,15 +356,10 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                         helperTypeConstructorDesc);
                 // END
             } else {
-                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
-                        Type.getType(Object.class)
-                });
+                String helperTypeConstructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class));
 
                 // GENERATE new BeanDynamicObject(this)
 
-                visitor.visitVarInsn(Opcodes.ALOAD, 0);
-
-                // GENERATE new BeanDynamicObject(this)
                 visitor.visitTypeInsn(Opcodes.NEW, nonExtensibleDynamicObjectHelperType.getInternalName());
                 visitor.visitInsn(Opcodes.DUP);
 
@@ -365,11 +379,11 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             // GENERATE private ConventionMapping mapping = new ConventionAwareHelper(this, getConvention())
 
             final String mappingFieldSignature = Type.getDescriptor(ConventionMapping.class);
-            final String getConventionDesc = Type.getMethodDescriptor(conventionType, new Type[0]);
+            final String getConventionDesc = Type.getMethodDescriptor(conventionType);
 
-            visitor.visitField(Opcodes.ACC_PRIVATE, "mapping", mappingFieldSignature, null, null);
+            visitor.visitField(Opcodes.ACC_PRIVATE, MAPPING_FIELD, mappingFieldSignature, null, null);
 
-            initConventionAwareHelper = new MethodCodeBody() {
+            final MethodCodeBody initConventionAwareHelper = new MethodCodeBody() {
                 public void add(MethodVisitor visitor) throws Exception {
                     Type helperType = Type.getType(ConventionAwareHelper.class);
 
@@ -388,13 +402,11 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
                     // END
 
-                    String constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
-                            conventionAwareType, conventionType
-                    });
+                    String constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, conventionAwareType, conventionType);
                     visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, helperType.getInternalName(), "<init>",
                             constructorDesc);
 
-                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "mapping",
+                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), MAPPING_FIELD,
                             mappingFieldSignature);
 
                     // END
@@ -403,28 +415,25 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
             // END
 
-            // GENERATE public ConventionMapping getConventionMapping() { if (mapping != null) { return mapping; } else { return new ConventionAwareHelper(this); } }
-            // the null check is for when getConventionMapping() is called by a superclass constructor (eg when the constructor calls a getter method)
+            // GENERATE public ConventionMapping getConventionMapping() {
+            //     if (mapping == null) {
+            //         mapping = new ConventionAwareHelper(this, getConvention);
+            //     }
+            //     return mapping;
+            // }
 
             addGetter(IConventionAware.class.getDeclaredMethod("getConventionMapping"), new MethodCodeBody() {
-                public void add(MethodVisitor visitor) {
-                    // GENERATE if (mapping != null) {... }
+                public void add(MethodVisitor visitor) throws Exception {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "mapping",
-                            mappingFieldSignature);
-                    visitor.visitInsn(Opcodes.DUP);
-                    Label nullBranch = new Label();
-                    visitor.visitJumpInsn(Opcodes.IFNULL, nullBranch);
-                    visitor.visitInsn(Opcodes.ARETURN);
-                    // GENERATE else { return new ConventionAwareHelper(this); }
-                    visitor.visitLabel(nullBranch);
-                    Type conventionAwareHelperType = Type.getType(ConventionAwareHelper.class);
-                    String constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(IConventionAware.class)});
-                    visitor.visitTypeInsn(Opcodes.NEW, conventionAwareHelperType.getInternalName());
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), MAPPING_FIELD, mappingFieldSignature);
                     visitor.visitInsn(Opcodes.DUP);
+                    Label returnValue = new Label();
+                    visitor.visitJumpInsn(Opcodes.IFNONNULL, returnValue);
+                    visitor.visitInsn(Opcodes.POP);
+                    initConventionAwareHelper.add(visitor);
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, conventionAwareHelperType.getInternalName(), "<init>", constructorDesc);
-                    visitor.visitInsn(Opcodes.ARETURN);
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), MAPPING_FIELD, mappingFieldSignature);
+                    visitor.visitLabel(returnValue);
                 }
             });
         }
@@ -434,9 +443,9 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             // GENERATE private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass())
 
             final String metaClassFieldSignature = Type.getDescriptor(MetaClass.class);
-            visitor.visitField(Opcodes.ACC_PRIVATE, "metaClass", metaClassFieldSignature, null, null);
+            visitor.visitField(Opcodes.ACC_PRIVATE, META_CLASS_FIELD, metaClassFieldSignature, null, null);
 
-            initMetaClass = new MethodCodeBody() {
+            final MethodCodeBody initMetaClass = new MethodCodeBody() {
                 public void add(MethodVisitor visitor) throws Exception {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
 
@@ -458,18 +467,30 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                     visitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getType(
                             MetaClassRegistry.class).getInternalName(), "getMetaClass", getMetaClassDesc);
 
-                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "metaClass",
+                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), META_CLASS_FIELD,
                             metaClassFieldSignature);
                 }
             };
 
-            // GENERATE public MetaClass getMetaClass() { return metaClass }
+            // GENERATE public MetaClass getMetaClass() {
+            //     if (metaClass == null) {
+            //         metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
+            //     }
+            //     return metaClass;
+            // }
 
             addGetter(GroovyObject.class.getDeclaredMethod("getMetaClass"), new MethodCodeBody() {
                 public void add(MethodVisitor visitor) throws Exception {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), "metaClass",
-                            metaClassFieldSignature);
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), META_CLASS_FIELD, metaClassFieldSignature);
+                    visitor.visitInsn(Opcodes.DUP);
+                    Label returnValue = new Label();
+                    visitor.visitJumpInsn(Opcodes.IFNONNULL, returnValue);
+                    visitor.visitInsn(Opcodes.POP);
+                    initMetaClass.add(visitor);
+                    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+                    visitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), META_CLASS_FIELD, metaClassFieldSignature);
+                    visitor.visitLabel(returnValue);
                 }
             });
 
@@ -479,7 +500,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                 public void add(MethodVisitor visitor) throws Exception {
                     visitor.visitVarInsn(Opcodes.ALOAD, 0);
                     visitor.visitVarInsn(Opcodes.ALOAD, 1);
-                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), "metaClass",
+                    visitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), META_CLASS_FIELD,
                             metaClassFieldSignature);
                 }
             });
@@ -487,8 +508,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
         private void addSetter(Method method, MethodCodeBody body) throws Exception {
             String methodDescriptor = Type.getMethodDescriptor(method);
-            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor,
-                    null, new String[0]);
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
             body.add(methodVisitor);
             methodVisitor.visitInsn(Opcodes.RETURN);
@@ -503,8 +523,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
         }
 
         private void addGetter(String methodName, String methodDescriptor, MethodCodeBody body) throws Exception {
-            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, methodName, methodDescriptor,
-                    null, new String[0]);
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, methodName, methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
             body.add(methodVisitor);
             methodVisitor.visitInsn(Opcodes.ARETURN);
@@ -538,7 +557,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
             // GENERATE public boolean hasProperty(String name) { return getAsDynamicObject().hasProperty(name) }
 
-            String methodDescriptor = Type.getMethodDescriptor(Type.getType(Boolean.TYPE), new Type[]{Type.getType(String.class)});
+            String methodDescriptor = Type.getMethodDescriptor(Type.getType(Boolean.TYPE), Type.getType(String.class));
             MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "hasProperty", methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
@@ -590,9 +609,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             addGetter(GroovyObject.class.getDeclaredMethod("invokeMethod", String.class, Object.class),
                     new MethodCodeBody() {
                         public void add(MethodVisitor methodVisitor) throws Exception {
-                            String invokeMethodDesc = Type.getMethodDescriptor(Type.getType(Object.class), new Type[]{
-                                    Type.getType(String.class), Type.getType(Object[].class)
-                            });
+                            String invokeMethodDesc = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(String.class), Type.getType(Object[].class));
                             String objArrayDesc = Type.getType(Object[].class).getDescriptor();
 
                             // GENERATE getAsDynamicObject().invokeMethod(name, (args instanceof Object[]) ? args : new Object[] { args })
@@ -613,7 +630,6 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                             methodVisitor.visitJumpInsn(Opcodes.IFEQ, notArray);
 
                             // Generate args
-//                            methodVisitor.visitInsn(Opcodes.ACONST_NULL);
                             methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
                             methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, objArrayDesc);
                             methodVisitor.visitJumpInsn(Opcodes.GOTO, end);
@@ -635,51 +651,118 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
                     });
         }
 
-        public void addGetter(MetaBeanProperty property) throws Exception {
-            if (!extensible) {
-                return;
-            }
-            MetaMethod getter = property.getGetter();
+        public void addInjectorProperty(PropertyMetaData property) {
+            // GENERATE private <type> <property-field-name>;
+            String flagName = propFieldName(property);
+            visitor.visitField(Opcodes.ACC_PRIVATE, flagName, Type.getDescriptor(property.getType()), null, null);
+        }
 
-            // GENERATE private boolean <prop>Set;
+        public void applyServiceInjectionToGetter(PropertyMetaData property, Method getter) throws Exception {
+            // GENERATE public <type> <getter>() { if (<field> == null) { <field> = getServices().get(getClass().getDeclaredMethod(<getter-name>).getGenericReturnType()); } return <field> }
 
-            String flagName = String.format("%sSet", property.getName());
-            visitor.visitField(Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null);
-
-            addConventionGetter(getter.getName(), flagName, property);
+            Type serviceRegistryType = Type.getType(ServiceRegistry.class);
+            Type classType = Type.getType(Class.class);
+            Type methodType = Type.getType(Method.class);
+            Type typeType = Type.getType(java.lang.reflect.Type.class);
 
             String getterName = getter.getName();
-            Class<?> returnType = getter.getReturnType();
-
-            // If it's a boolean property, there can be get or is type variants.
-            // If this class has both, decorate both.
-            if (returnType.equals(Boolean.TYPE)) {
-                boolean getterIsIsMethod = getterName.startsWith("is");
-                String propertyNameComponent = getterName.substring(getterIsIsMethod ? 2 : 3);
-                String alternativeGetterName = String.format("%s%s", getterIsIsMethod ? "get" : "is", propertyNameComponent);
-
-                try {
-                    type.getMethod(alternativeGetterName);
-                    addConventionGetter(alternativeGetterName, flagName, property);
-                } catch (NoSuchMethodException e) {
-                    // ignore, no method to override
-                }
-            }
+            Type returnType = Type.getType(getter.getReturnType());
+            String methodDescriptor = Type.getMethodDescriptor(returnType);
+            Type serviceType = Type.getType(property.getType());
+            String propFieldName = propFieldName(property);
+
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, getterName, methodDescriptor, signature(getter), new String[0]);
+            methodVisitor.visitCode();
+
+            // if (this.<field> == null) { ...  }
+
+            // this.field
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), propFieldName, serviceType.getDescriptor());
+
+            Label alreadyLoaded = new Label();
+            methodVisitor.visitJumpInsn(Opcodes.IFNONNULL, alreadyLoaded);
+
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+
+            // this.getServices()
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), "getServices", Type.getMethodDescriptor(serviceRegistryType));
+
+            // this.getClass()
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), "getClass", Type.getMethodDescriptor(classType));
+
+            // <class>.getDeclaredMethod(<getter-name>)
+            methodVisitor.visitLdcInsn(getterName);
+            methodVisitor.visitInsn(Opcodes.ICONST_0);
+            methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, classType.getInternalName());
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classType.getInternalName(), "getDeclaredMethod", Type.getMethodDescriptor(methodType, Type.getType(String.class), Type.getType(Class[].class)));
+
+            // <method>.getGenericReturnType()
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, methodType.getInternalName(), "getGenericReturnType", Type.getMethodDescriptor(typeType));
+
+            // get(<type>)
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, serviceRegistryType.getInternalName(), "get", Type.getMethodDescriptor(Type.getType(Object.class), typeType));
+
+            // this.field = (<type>)<service>
+            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, serviceType.getInternalName());
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), propFieldName, serviceType.getDescriptor());
+
+            // this.field
+            methodVisitor.visitLabel(alreadyLoaded);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), propFieldName, serviceType.getDescriptor());
+
+            // return
+            methodVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+            methodVisitor.visitMaxs(0, 0);
+            methodVisitor.visitEnd();
+        }
+
+        public void applyServiceInjectionToSetter(PropertyMetaData property, Method setter) throws Exception {
+            // GENERATE public void <setter>(<type> value) { <field> == value }
+            String methodDescriptor = Type.getMethodDescriptor(setter);
+            Type serviceType = Type.getType(property.getType());
+            String propFieldName = propFieldName(property);
+
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, setter.getName(), methodDescriptor, signature(setter), new String[0]);
+            methodVisitor.visitCode();
+
+            // this.field = value
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), propFieldName, serviceType.getDescriptor());
+
+            // return
+            methodVisitor.visitInsn(Opcodes.RETURN);
+            methodVisitor.visitMaxs(0, 0);
+            methodVisitor.visitEnd();
+        }
+
+        public void addConventionProperty(PropertyMetaData property) throws Exception {
+            // GENERATE private boolean <flag-name>;
+            String flagName = propFieldName(property);
+            visitor.visitField(Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null);
         }
 
-        private void addConventionGetter(String getterName, String flagName, MetaBeanProperty property) throws Exception {
-            // GENERATE public <type> <getter>() { return (<type>)getConventionMapping().getConventionValue(super.<getter>(), '<prop>', <prop>Set); }
-            MetaMethod getter = property.getGetter();
+        private String propFieldName(PropertyMetaData property) {
+            return String.format("__%s__", property.getName());
+        }
+
+        public void applyConventionMappingToGetter(PropertyMetaData property, Method getter) throws Exception {
+            // GENERATE public <type> <getter>() { return (<type>)getConventionMapping().getConventionValue(super.<getter>(), '<prop>', __<prop>__); }
+            String flagName = propFieldName(property);
+            String getterName = getter.getName();
 
             Type returnType = Type.getType(getter.getReturnType());
-            String methodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
-            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, getterName, methodDescriptor,
-                    null, new String[0]);
+            String methodDescriptor = Type.getMethodDescriptor(returnType);
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, getterName, methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, conventionAwareType.getInternalName(),
-                    "getConventionMapping", Type.getMethodDescriptor(conventionMappingType, new Type[0]));
+                    "getConventionMapping", Type.getMethodDescriptor(conventionMappingType));
 
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), getterName,
@@ -689,7 +772,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             if (getter.getReturnType().isPrimitive()) {
                 // Box value
                 boxedType = Type.getType(JavaReflectionUtil.getWrapperTypeForPrimitiveType(getter.getReturnType()));
-                String valueOfMethodDescriptor = Type.getMethodDescriptor(boxedType, new Type[]{returnType});
+                String valueOfMethodDescriptor = Type.getMethodDescriptor(boxedType, returnType);
                 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, boxedType.getInternalName(), "valueOf", valueOfMethodDescriptor);
             }
 
@@ -707,7 +790,7 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             if (getter.getReturnType().isPrimitive()) {
                 // Unbox value
                 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, boxedType.getInternalName());
-                String valueMethodDescriptor = Type.getMethodDescriptor(returnType, new Type[0]);
+                String valueMethodDescriptor = Type.getMethodDescriptor(returnType);
                 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, boxedType.getInternalName(), getter.getReturnType().getName() + "Value", valueMethodDescriptor);
             } else {
                 // Cast to return type
@@ -721,17 +804,12 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitEnd();
         }
 
-        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; }
+        public void applyConventionMappingToSetter(PropertyMetaData property, Method setter) throws Exception {
+            // GENERATE public <return-type> <setter>(<type> v) { <return-type> v = super.<setter>(v); __<prop>__ = true; return v; }
 
-            Type paramType = Type.getType(setter.getParameterTypes()[0].getTheClass());
+            Type paramType = Type.getType(setter.getParameterTypes()[0]);
             Type returnType = Type.getType(setter.getReturnType());
-            String setterDescriptor = Type.getMethodDescriptor(returnType, new Type[]{paramType});
+            String setterDescriptor = Type.getMethodDescriptor(returnType, paramType);
             MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, setter.getName(), setterDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
@@ -744,12 +822,11 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
 
             // END
 
-            // GENERATE <prop>Set = true
+            // GENERATE __<prop>__ = true
 
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             methodVisitor.visitLdcInsn(true);
-            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), String.format("%sSet",
-                    property.getName()), Type.BOOLEAN_TYPE.getDescriptor());
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), propFieldName(property), Type.BOOLEAN_TYPE.getDescriptor());
 
             // END
 
@@ -758,14 +835,13 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitEnd();
         }
 
-        public void addSetMethod(MetaBeanProperty property) throws Exception {
-            MetaMethod setter = property.getSetter();
-            Type paramType = Type.getType(setter.getParameterTypes()[0].getTheClass());
+        public void addSetMethod(PropertyMetaData property, Method setter) throws Exception {
+            Type paramType = Type.getType(setter.getParameterTypes()[0]);
             Type returnType = Type.getType(setter.getReturnType());
-            String setterDescriptor = Type.getMethodDescriptor(returnType, new Type[]{paramType});
+            String setterDescriptor = Type.getMethodDescriptor(returnType, paramType);
 
             // GENERATE public void <propName>(<type> v) { <setter>(v) }
-            String setMethodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{paramType});
+            String setMethodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, paramType);
             MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, property.getName(), setMethodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
@@ -783,18 +859,13 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitEnd();
         }
 
-        public void overrideSetMethod(MetaBeanProperty property, MetaMethod metaMethod) throws Exception {
-            if (metaMethod.getParameterTypes().length != 1) {
-                throw new IllegalArgumentException("Can only override set methods that take one argument: " + metaMethod.toString());
-            } else 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});
+        public void applyConventionMappingToSetMethod(PropertyMetaData property, Method method) throws Exception {
+            Type paramType = Type.getType(method.getParameterTypes()[0]);
+            Type returnType = Type.getType(method.getReturnType());
+            String methodDescriptor = Type.getMethodDescriptor(returnType, paramType);
 
-            // GENERATE public <returnType> <propName>(<type> v) { val = super.<propName>(v); <prop>Set = true; return val; }
-            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, metaMethod.getName(), methodDescriptor, null, new String[0]);
+            // GENERATE public <returnType> <propName>(<type> v) { val = super.<propName>(v); __<prop>__ = true; return val; }
+            MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
             // GENERATE super.<propName>(v)
@@ -802,14 +873,13 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             methodVisitor.visitVarInsn(paramType.getOpcode(Opcodes.ILOAD), 1);
 
-            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), metaMethod.getName(), methodDescriptor);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), method.getName(), methodDescriptor);
 
-            // GENERATE <prop>Set = true
+            // GENERATE __<prop>__ = true
 
             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
             methodVisitor.visitLdcInsn(true);
-            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), String.format("%sSet",
-                    property.getName()), Type.BOOLEAN_TYPE.getDescriptor());
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), propFieldName(property), Type.BOOLEAN_TYPE.getDescriptor());
 
             // END
 
@@ -818,11 +888,12 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitEnd();
         }
 
-        public void addActionMethod(MetaMethod method) throws Exception {
+        public void addActionMethod(Method method) throws Exception {
             Type actionImplType = Type.getType(ClosureBackedAction.class);
             Type closureType = Type.getType(Closure.class);
+            Type returnType = Type.getType(method.getReturnType());
 
-            Type[] originalParameterTypes = CollectionUtils.collectArray(method.getNativeParameterTypes(), Type.class, new Transformer<Type, Class>() {
+            Type[] originalParameterTypes = CollectionUtils.collectArray(method.getParameterTypes(), Type.class, new Transformer<Type, Class>() {
                 public Type transform(Class clazz) {
                     return Type.getType(clazz);
                 }
@@ -832,9 +903,9 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             System.arraycopy(originalParameterTypes, 0, closurisedParameterTypes, 0, numParams);
             closurisedParameterTypes[numParams - 1] = closureType;
 
-            String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, closurisedParameterTypes);
+            String methodDescriptor = Type.getMethodDescriptor(returnType, closurisedParameterTypes);
 
-            // GENERATE public void <method>(Closure v) { <method>(…, new ClosureBackedAction(v)); }
+            // GENERATE public <return type> <method>(Closure v) { return <method>(…, new ClosureBackedAction(v)); }
             MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methodDescriptor, null, new String[0]);
             methodVisitor.visitCode();
 
@@ -849,18 +920,90 @@ public class AsmBackedClassGenerator extends AbstractClassGenerator {
             methodVisitor.visitTypeInsn(Opcodes.NEW, actionImplType.getInternalName());
             methodVisitor.visitInsn(Opcodes.DUP);
             methodVisitor.visitVarInsn(Opcodes.ALOAD, numParams);
-            String constuctorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{closureType});
+            String constuctorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, closureType);
             methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, actionImplType.getInternalName(), "<init>", constuctorDescriptor);
 
 
             methodDescriptor = Type.getMethodDescriptor(Type.getType(method.getReturnType()), originalParameterTypes);
             methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedType.getInternalName(), method.getName(), methodDescriptor);
 
-            methodVisitor.visitInsn(Opcodes.RETURN);
+            methodVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
             methodVisitor.visitMaxs(0, 0);
             methodVisitor.visitEnd();
         }
 
+        private void includeNotInheritedAnnotations() {
+            for (Annotation annotation : type.getDeclaredAnnotations()) {
+                if (annotation.annotationType().getAnnotation(Inherited.class) != null) {
+                    continue;
+                }
+                Retention retention = annotation.annotationType().getAnnotation(Retention.class);
+                boolean visible = retention != null && retention.value() == RetentionPolicy.RUNTIME;
+                AnnotationVisitor annotationVisitor = visitor.visitAnnotation(Type.getType(annotation.annotationType()).getDescriptor(), visible);
+                visitAnnotationValues(annotation, annotationVisitor);
+                annotationVisitor.visitEnd();
+            }
+        }
+
+        private void visitAnnotationValues(Annotation annotation, AnnotationVisitor annotationVisitor) {
+            for (Method method : annotation.annotationType().getDeclaredMethods()) {
+                String name = method.getName();
+                Class<?> returnType = method.getReturnType();
+                if (returnType.isEnum()) {
+                    annotationVisitor.visitEnum(name, Type.getType(returnType).getDescriptor(), getAnnotationParameterValue(annotation, method).toString());
+                } else if (returnType.isArray() && !PRIMITIVE_TYPES.contains(returnType.getComponentType())) {
+                    AnnotationVisitor arrayVisitor = annotationVisitor.visitArray(name);
+                    Object[] elements = (Object[]) getAnnotationParameterValue(annotation, method);
+                    visitArrayElements(arrayVisitor, returnType.getComponentType(), elements);
+                    arrayVisitor.visitEnd();
+                } else if (returnType.equals(Class.class)) {
+                    Class<?> clazz = (Class<?>) getAnnotationParameterValue(annotation, method);
+                    annotationVisitor.visit(name, Type.getType(clazz));
+                } else if (returnType.isAnnotation()) {
+                    Annotation nestedAnnotation = (Annotation) getAnnotationParameterValue(annotation, method);
+                    AnnotationVisitor nestedAnnotationVisitor = annotationVisitor.visitAnnotation(name, Type.getType(returnType).getDescriptor());
+                    visitAnnotationValues(nestedAnnotation, nestedAnnotationVisitor);
+                    nestedAnnotationVisitor.visitEnd();
+                } else {
+                    annotationVisitor.visit(name, getAnnotationParameterValue(annotation, method));
+                }
+            }
+        }
+
+        private void visitArrayElements(AnnotationVisitor arrayVisitor, Class arrayElementType, Object[] arrayElements) {
+            if (arrayElementType.isEnum()) {
+                String enumDescriptor = Type.getType(arrayElementType).getDescriptor();
+                for (Object value : arrayElements) {
+                    arrayVisitor.visitEnum(null, enumDescriptor, value.toString());
+                }
+            } else if (arrayElementType.equals(Class.class)) {
+                for (Object value : arrayElements) {
+                    Class<?> clazz = (Class<?>) value;
+                    arrayVisitor.visit(null, Type.getType(clazz));
+                }
+            } else if (arrayElementType.isAnnotation()) {
+                for (Object annotation : arrayElements) {
+                    AnnotationVisitor nestedAnnotationVisitor = arrayVisitor.visitAnnotation(null, Type.getType(arrayElementType).getDescriptor());
+                    visitAnnotationValues((Annotation) annotation, nestedAnnotationVisitor);
+                    nestedAnnotationVisitor.visitEnd();
+                }
+            } else {
+                for (Object value : arrayElements) {
+                    arrayVisitor.visit(null, value);
+                }
+            }
+        }
+
+        private Object getAnnotationParameterValue(Annotation annotation, Method method) {
+            try {
+                return method.invoke(annotation);
+            } catch (IllegalAccessException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            } catch (InvocationTargetException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
         public Class<? extends T> generate() {
             visitor.visitEnd();
 
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 3ee214c..790f34c 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
@@ -19,12 +19,10 @@ import groovy.lang.*;
 import groovy.lang.MissingMethodException;
 import org.codehaus.groovy.runtime.InvokerInvocationException;
 import org.gradle.api.internal.coerce.MethodArgumentsTransformer;
-import org.gradle.api.internal.coerce.TypeCoercingMethodArgumentsTransformer;
+import org.gradle.api.internal.coerce.PropertySetTransformer;
+import org.gradle.api.internal.coerce.StringToEnumTransformer;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * A {@link DynamicObject} which uses groovy reflection to provide access to the properties and methods of a bean.
@@ -37,7 +35,8 @@ public class BeanDynamicObject extends AbstractDynamicObject {
     private final boolean implementsMissing;
 
     // NOTE: If this guy starts caching internally, consider sharing an instance
-    private final MethodArgumentsTransformer argsTransformer = new TypeCoercingMethodArgumentsTransformer();
+    private final MethodArgumentsTransformer argsTransformer = StringToEnumTransformer.INSTANCE;
+    private final PropertySetTransformer propertySetTransformer = StringToEnumTransformer.INSTANCE;
 
     public BeanDynamicObject(Object bean) {
         this(bean, true);
@@ -124,9 +123,20 @@ public class BeanDynamicObject extends AbstractDynamicObject {
 
     @Override
     public Object invokeMethod(String name, Object... arguments) throws MissingMethodException {
-        // Maybe transform the arguments before calling the method (e.g. type coercion)
-        arguments = argsTransformer.transform(bean, name, arguments);
-        return delegate.invokeMethod(name, arguments);
+        try {
+            return delegate.invokeMethod(name, arguments);
+        } catch (MissingMethodException e) {
+            if (e.isStatic() || !e.getMethod().equals(name) || !Arrays.equals(e.getArguments(), arguments)) {
+                throw e;
+            } else {
+                Object[] transformedArguments = argsTransformer.transform(bean, name, arguments);
+                if (transformedArguments != arguments) {
+                    return delegate.invokeMethod(name, transformedArguments);
+                } else {
+                    throw e;
+                }
+            }
+        }
     }
 
     private class MetaClassAdapter implements DynamicObject {
@@ -180,10 +190,7 @@ public class BeanDynamicObject extends AbstractDynamicObject {
                 };
             }
             try {
-
-                // Attempt type coercion before trying to set the property
-                value = argsTransformer.transform(bean, MetaProperty.getSetterName(name), value)[0];
-
+                value = propertySetTransformer.transformValue(bean, property, value);
                 metaClass.setProperty(bean, name, value);
             } catch (InvokerInvocationException e) {
                 if (e.getCause() instanceof RuntimeException) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java
deleted file mode 100644
index db7cd91..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ClosureBackedAction.java
+++ /dev/null
@@ -1,99 +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;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.InvalidActionClosureException;
-import org.gradle.util.Configurable;
-
-public class ClosureBackedAction<T> implements Action<T> {
-    private final Closure closure;
-    private final boolean configureableAware;
-    private final int resolveStrategy;
-
-    public ClosureBackedAction(Closure closure) {
-        this(closure, Closure.DELEGATE_FIRST, true);
-    }
-
-    public ClosureBackedAction(Closure closure, int resolveStrategy) {
-        this(closure, resolveStrategy, false);
-    }
-
-    public ClosureBackedAction(Closure closure, int resolveStrategy, boolean configureableAware) {
-        this.closure = closure;
-        this.configureableAware = configureableAware;
-        this.resolveStrategy = resolveStrategy;
-    }
-
-    public void execute(T delegate) {
-        if (closure == null) {
-            return;
-        }
-
-        try {
-            if (configureableAware && delegate instanceof Configurable) {
-                ((Configurable)delegate).configure(closure);
-            } else {
-                Closure copy = (Closure) closure.clone();
-                copy.setResolveStrategy(resolveStrategy);
-                copy.setDelegate(delegate);
-                if (copy.getMaximumNumberOfParameters() == 0) {
-                    copy.call();
-                } else {
-                    copy.call(delegate);
-                }
-            }
-        } catch (groovy.lang.MissingMethodException e) {
-            if (e.getType().equals(closure.getClass()) && e.getMethod().equals("doCall")) {
-                throw new InvalidActionClosureException(closure, delegate);
-            }
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        ClosureBackedAction that = (ClosureBackedAction) o;
-
-        if (configureableAware != that.configureableAware) {
-            return false;
-        }
-        if (resolveStrategy != that.resolveStrategy) {
-            return false;
-        }
-        if (!closure.equals(that.closure)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = closure.hashCode();
-        result = 31 * result + (configureableAware ? 1 : 0);
-        result = 31 * result + resolveStrategy;
-        return result;
-    }
-}
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 e028783..92f7e41 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
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal;
 
+import com.google.common.collect.Iterators;
 import org.apache.commons.collections.collection.CompositeCollection;
 import org.gradle.api.Action;
 import org.gradle.api.DomainObjectCollection;
@@ -22,6 +23,7 @@ import org.gradle.api.specs.Spec;
 import org.gradle.internal.Actions;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 
@@ -30,28 +32,32 @@ import java.util.LinkedHashSet;
  *
  * @param <T> The type of domain objects in the component collections of this collection.
  */
-public class CompositeDomainObjectSet<T> extends DefaultDomainObjectSet<T> {
+public class CompositeDomainObjectSet<T> extends DelegatingDomainObjectSet<T> {
 
-    private Spec<T> uniqueSpec = new ItemIsUniqueInCompositeSpec();
-    private Spec<T> notInSpec = new ItemNotInCompositeSpec();
-    
-    public CompositeDomainObjectSet(Class<T> type) {
+    private final Spec<T> uniqueSpec = new ItemIsUniqueInCompositeSpec();
+    private final Spec<T> notInSpec = new ItemNotInCompositeSpec();
+    private final DefaultDomainObjectSet<T> backingSet;
+
+    public static <T> CompositeDomainObjectSet<T> create(Class<T> type, DomainObjectCollection<? extends T>... collections) {
         //noinspection unchecked
-        super(type, new CompositeCollection());
+        DefaultDomainObjectSet<T> backingSet = new DefaultDomainObjectSet<T>(type, new CompositeCollection());
+        CompositeDomainObjectSet<T> out = new CompositeDomainObjectSet<T>(backingSet);
+        for (DomainObjectCollection<? extends T> c : collections) {
+            out.addCollection(c);
+        }
+        return out;
     }
 
-    public CompositeDomainObjectSet(Class<T> type, DomainObjectCollection<? extends T>... collections) {
-        this(type);
-        for (DomainObjectCollection<? extends T> collection : collections) {
-            addCollection(collection);
-        }
+    CompositeDomainObjectSet(DefaultDomainObjectSet<T> backingSet) {
+        super(backingSet);
+        this.backingSet = backingSet; //TODO SF try avoiding keeping this state here
     }
 
     public class ItemIsUniqueInCompositeSpec implements Spec<T> {
         public boolean isSatisfiedBy(T element) {
             int matches = 0;
             for (Object collection : getStore().getCollections()) {
-                if (((Collection)collection).contains(element)) {
+                if (((Collection) collection).contains(element)) {
                     if (++matches > 1) {
                         return false;
                     }
@@ -70,49 +76,50 @@ public class CompositeDomainObjectSet<T> extends DefaultDomainObjectSet<T> {
 
     @SuppressWarnings("unchecked")
     protected CompositeCollection getStore() {
-        return (CompositeCollection)super.getStore();
+        return (CompositeCollection) this.backingSet.getStore();
     }
 
     public Action<? super T> whenObjectAdded(Action<? super T> action) {
-        return super.whenObjectAdded(Actions.<T>filter(action, uniqueSpec));
+        return super.whenObjectAdded(Actions.filter(action, uniqueSpec));
     }
 
     public Action<? super T> whenObjectRemoved(Action<? super T> action) {
-        return super.whenObjectRemoved(Actions.<T>filter(action, notInSpec));
+        return super.whenObjectRemoved(Actions.filter(action, notInSpec));
     }
-    
+
     public void addCollection(DomainObjectCollection<? extends T> collection) {
         if (!getStore().getCollections().contains(collection)) {
             getStore().addComposited(collection);
-            collection.all(getEventRegister().getAddAction());
-            collection.whenObjectRemoved(getEventRegister().getRemoveAction());
+            collection.all(backingSet.getEventRegister().getAddAction());
+            collection.whenObjectRemoved(backingSet.getEventRegister().getRemoveAction());
         }
     }
 
     public void removeCollection(DomainObjectCollection<? extends T> collection) {
         getStore().removeComposited(collection);
-        Action<? super T> action = getEventRegister().getRemoveAction();
+        Action<? super T> action = this.backingSet.getEventRegister().getRemoveAction();
         for (T item : collection) {
             action.execute(item);
         }
     }
 
+    @SuppressWarnings({"NullableProblems", "unchecked"})
+    @Override
     public Iterator<T> iterator() {
-        //noinspection unchecked
-        return new LinkedHashSet<T>(getStore()).iterator();
+        return Iterators.unmodifiableIterator(new LinkedHashSet<T>(getStore()).iterator());
     }
 
+    @SuppressWarnings("unchecked")
     public int size() {
-        //noinspection unchecked
-        return new LinkedHashSet<T>(getStore()).size();
+        return new HashSet<T>(getStore()).size();
     }
-    
+
     public void all(Action<? super T> action) {
+        //calling overloaded method with extra behavior:
         whenObjectAdded(action);
 
         for (T t : this) {
             action.execute(t);
         }
     }
-
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDynamicObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDynamicObject.java
index 33032f0..f919797 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDynamicObject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/CompositeDynamicObject.java
@@ -29,8 +29,11 @@ import java.util.Map;
  * Can be used to provide a dynamic view of an object with enhancements.
  */
 public abstract class CompositeDynamicObject extends AbstractDynamicObject {
-    private DynamicObject[] objects = new DynamicObject[0];
-    private DynamicObject[] updateObjects = new DynamicObject[0];
+
+    private static final DynamicObject[] NONE = new DynamicObject[0];
+
+    private DynamicObject[] objects = NONE;
+    private DynamicObject[] updateObjects = NONE;
 
     protected void setObjects(DynamicObject... objects) {
         this.objects = objects;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConfigureDelegate.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConfigureDelegate.java
index cf4de20..827da7a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ConfigureDelegate.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ConfigureDelegate.java
@@ -61,21 +61,30 @@ public class ConfigureDelegate extends GroovyObjectSupport {
             try {
                 return _delegate.invokeMethod(name, params);
             } catch (groovy.lang.MissingMethodException e) {
+                // TODO - should check the type as well, and below too, however we have no idea what the final type is going to be.
+                // Rework the DynamicObject contract to allow us to know if the method was found or not
+                if (!name.equals(e.getMethod())) {
+                    throw e;
+                }
                 failure = e;
             }
 
+            if (!isAlreadyConfiguring && _isConfigureMethod(name, params)) {
+                return _configure(name, params);
+            }
+
             // try the owner
             try {
                 return _owner.invokeMethod(name, params);
             } catch (groovy.lang.MissingMethodException e) {
+                if (!name.equals(e.getMethod())) {
+                    throw e;
+                }
                 // ignore
             }
 
-            if (isAlreadyConfiguring || !_isConfigureMethod(name, params)) {
-                throw failure;
-            }
+            throw failure;
 
-            return _configure(name, params);
         } finally {
             _configuring.set(isAlreadyConfiguring);
         }
@@ -89,6 +98,9 @@ public class ConfigureDelegate extends GroovyObjectSupport {
             try {
                 return _delegate.getProperty(name);
             } catch (MissingPropertyException e) {
+                if (!name.equals(e.getProperty())) {
+                    throw e;
+                }
                 failure = e;
             }
 
@@ -96,6 +108,9 @@ public class ConfigureDelegate extends GroovyObjectSupport {
             try {
                 return _owner.getProperty(name);
             } catch (MissingPropertyException e) {
+                if (!name.equals(e.getProperty())) {
+                    throw e;
+                }
                 // Ignore
             }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
index 3012683..4f06094 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultClassPathProvider.java
@@ -38,6 +38,9 @@ public class DefaultClassPathProvider implements ClassPathProvider {
         if (name.equals("GRADLE_CORE")) {
             return moduleRegistry.getModule("gradle-core").getImplementationClasspath();
         }
+        if (name.equals("GRADLE_BASE_SERVICES")) {
+            return moduleRegistry.getModule("gradle-base-services").getImplementationClasspath();
+        }
         if (name.equals("COMMONS_CLI")) {
             return moduleRegistry.getExternalModule("commons-cli").getClasspath();
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultDomainObjectCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultDomainObjectCollection.java
index 12ed220..3e00ecb 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultDomainObjectCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultDomainObjectCollection.java
@@ -161,6 +161,7 @@ public class DefaultDomainObjectCollection<T> extends AbstractCollection<T> impl
 
     private boolean doAdd(T toAdd) {
         if (getStore().add(toAdd)) {
+            didAdd(toAdd);
             eventRegister.getAddAction().execute(toAdd);
             return true;
         } else {
@@ -168,6 +169,9 @@ public class DefaultDomainObjectCollection<T> extends AbstractCollection<T> impl
         }
     }
 
+    protected void didAdd(T toAdd) {
+    }
+
     public boolean addAll(Collection<? extends T> c) {
         assertMutable();
         boolean changed = false;
@@ -184,7 +188,7 @@ public class DefaultDomainObjectCollection<T> extends AbstractCollection<T> impl
         Object[] c = toArray();
         getStore().clear();
         for (Object o : c) {
-            eventRegister.getRemoveAction().execute((T)o);
+            eventRegister.getRemoveAction().execute((T) o);
         }
     }
 
@@ -207,13 +211,18 @@ public class DefaultDomainObjectCollection<T> extends AbstractCollection<T> impl
 
     private boolean doRemove(Object o) {
         if (getStore().remove(o)) {
-            eventRegister.getRemoveAction().execute((T)o);
+            @SuppressWarnings("unchecked") T cast = (T) o;
+            didRemove(cast);
+            eventRegister.getRemoveAction().execute(cast);
             return true;
         } else {
             return false;
         }
     }
 
+    protected void didRemove(T t) {
+    }
+
     public boolean removeAll(Collection<?> c) {
         assertMutable();
         boolean changed = false;
@@ -279,6 +288,7 @@ public class DefaultDomainObjectCollection<T> extends AbstractCollection<T> impl
         public void remove() {
             assertMutable();
             iterator.remove();
+            didRemove(currentElement);
             getEventRegister().getRemoveAction().execute(currentElement);
             currentElement = null;
         }
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 77a7b2b..8176090 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
@@ -34,6 +34,7 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
 
     private final Instantiator instantiator;
     private final Namer<? super T> namer;
+    private final Index<T> index;
 
     private final ContainerElementsDynamicObject elementsDynamicObject = new ContainerElementsDynamicObject();
     private final Convention convention;
@@ -48,19 +49,28 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
         this.convention = new DefaultConvention(instantiator);
         this.dynamicObject = new ExtensibleDynamicObject(this, new ContainerDynamicObject(elementsDynamicObject), convention);
         this.namer = namer;
+        this.index = new UnfilteredIndex<T>();
+        index();
     }
 
-    protected DefaultNamedDomainObjectCollection(Class<? extends T> type, Collection<T> store, CollectionEventRegister<T> eventRegister, Instantiator instantiator, Namer<? super T> namer) {
+    protected void index() {
+        for (T t : getStore()) {
+            index.put(namer.determineName(t), t);
+        }
+    }
+
+    protected DefaultNamedDomainObjectCollection(Class<? extends T> type, Collection<T> store, CollectionEventRegister<T> eventRegister, Index<T> index, Instantiator instantiator, Namer<? super T> namer) {
         super(type, store, eventRegister);
         this.instantiator = instantiator;
         this.convention = new DefaultConvention(instantiator);
         this.dynamicObject = new ExtensibleDynamicObject(this, new ContainerDynamicObject(elementsDynamicObject), convention);
         this.namer = namer;
+        this.index = index;
     }
 
     // should be protected, but use of the class generator forces it to be public
     public DefaultNamedDomainObjectCollection(DefaultNamedDomainObjectCollection<? super T> collection, CollectionFilter<T> filter, Instantiator instantiator, Namer<? super T> namer) {
-        this(filter.getType(), collection.filteredStore(filter), collection.filteredEvents(filter), instantiator, namer);
+        this(filter.getType(), collection.filteredStore(filter), collection.filteredEvents(filter), collection.filteredIndex(filter), instantiator, namer);
     }
 
     /**
@@ -75,12 +85,29 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
         }
     }
 
+    @Override
+    protected void didAdd(T toAdd) {
+        index.put(namer.determineName(toAdd), toAdd);
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+        index.clear();
+    }
+
+
+    @Override
+    protected void didRemove(T t) {
+        index.remove(namer.determineName(t));
+    }
+
+
     /**
-     * <p>Subclass hook for implementations wanting to throw an exception when an attempt is made to add
-     * an item with the same name as an existing item.</p>
-     * 
+     * <p>Subclass hook for implementations wanting to throw an exception when an attempt is made to add an item with the same name as an existing item.</p>
+     *
      * <p>This implementation does not thrown an exception, meaning that {@code add(T)} will simply return {@code false}.
-     * 
+     *
      * @param o The item that is being attempted to add.
      */
     protected void handleAttemptToAddItemWithNonUniqueName(T o) {
@@ -104,13 +131,17 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
     }
 
     public Namer<T> getNamer() {
-        return (Namer)this.namer;
+        return (Namer) this.namer;
     }
-    
+
     protected Instantiator getInstantiator() {
         return instantiator;
     }
 
+    protected <S extends T> Index<S> filteredIndex(CollectionFilter<S> filter) {
+        return index.filter(filter);
+    }
+
     /**
      * Creates a filtered version of this collection.
      */
@@ -123,19 +154,11 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
     }
 
     public SortedMap<String, T> getAsMap() {
-        SortedMap<String, T> map = new TreeMap<String, T>();
-        for (T o : getStore()) {
-            map.put(namer.determineName(o), o);
-        }
-        return map;
+        return index.asMap();
     }
 
     public SortedSet<String> getNames() {
-        SortedSet<String> set = new TreeSet<String>();
-        for (T o : getStore()) {
-            set.add(namer.determineName(o));
-        }
-        return set;
+        return index.asMap().navigableKeySet();
     }
 
     public <S extends T> NamedDomainObjectCollection<S> withType(Class<S> type) {
@@ -164,12 +187,7 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
     }
 
     protected T findByNameWithoutRules(String name) {
-        for (T o : getStore()) {
-            if (name.equals(namer.determineName(o))) {
-                return o;
-            }
-        }
-        return null;
+        return index.get(name);
     }
 
     protected T removeByName(String name) {
@@ -205,8 +223,7 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
     }
 
     /**
-     * Returns a {@link DynamicObject} which can be used to access the domain objects as dynamic properties and
-     * methods.
+     * Returns a {@link DynamicObject} which can be used to access the domain objects as dynamic properties and methods.
      *
      * @return The dynamic object
      */
@@ -331,4 +348,105 @@ public class DefaultNamedDomainObjectCollection<T> extends DefaultDomainObjectCo
             return (arguments.length == 1 && arguments[0] instanceof Closure) && hasProperty(name);
         }
     }
+
+    protected static interface Index<T> {
+        void put(String name, T value);
+
+        T get(String name);
+
+        void remove(String name);
+
+        void clear();
+
+        NavigableMap<String, T> asMap();
+
+        <S extends T> Index<S> filter(CollectionFilter<S> filter);
+    }
+
+    protected static class UnfilteredIndex<T> implements Index<T> {
+
+        private final NavigableMap<String, T> map = new TreeMap<String, T>();
+
+        @Override
+        public NavigableMap<String, T> asMap() {
+            return map;
+        }
+
+        @Override
+        public void put(String name, T value) {
+            map.put(name, value);
+        }
+
+        @Override
+        public T get(String name) {
+            return map.get(name);
+        }
+
+        @Override
+        public void remove(String name) {
+            map.remove(name);
+        }
+
+        @Override
+        public void clear() {
+            map.clear();
+        }
+
+        @Override
+        public <S extends T> Index<S> filter(CollectionFilter<S> filter) {
+            return new FilteredIndex<S>(this, filter);
+        }
+    }
+
+    private static class FilteredIndex<T> implements Index<T> {
+
+        private final Index<? super T> delegate;
+        private final CollectionFilter<T> filter;
+
+        public FilteredIndex(Index<? super T> delegate, CollectionFilter<T> filter) {
+            this.delegate = delegate;
+            this.filter = filter;
+        }
+
+        @Override
+        public void put(String name, T value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public T get(String name) {
+            return filter.filter(delegate.get(name));
+        }
+
+        @Override
+        public void remove(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NavigableMap<String, T> asMap() {
+            NavigableMap<String, ? super T> delegateMap = delegate.asMap();
+
+            NavigableMap<String, T> filtered = new TreeMap<String, T>();
+            for (Map.Entry<String, ? super T> entry : delegateMap.entrySet()) {
+                T obj = filter.filter(entry.getValue());
+                if (obj != null) {
+                    filtered.put(entry.getKey(), obj);
+                }
+            }
+
+            return filtered;
+        }
+
+        @Override
+        public <S extends T> Index<S> filter(CollectionFilter<S> filter) {
+            return new FilteredIndex<S>(delegate, this.filter.and(filter));
+        }
+    }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectList.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectList.java
index 7eac48a..a3bcfb0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectList.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectList.java
@@ -33,7 +33,7 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
     }
 
     public DefaultNamedDomainObjectList(Class<T> type, CollectionEventRegister<T> collectionEventRegister, Instantiator instantiator, Namer<? super T> namer) {
-        super(type, new ArrayList<T>(), collectionEventRegister, instantiator, namer);
+        super(type, new ArrayList<T>(), collectionEventRegister, new UnfilteredIndex<T>(), instantiator, namer);
     }
 
     public DefaultNamedDomainObjectList(Class<T> type, Instantiator instantiator, Namer<? super T> namer) {
@@ -44,6 +44,7 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
         assertMutable();
         assertCanAdd(element);
         getStore().add(index, element);
+        didAdd(element);
         getEventRegister().getAddAction().execute(element);
     }
 
@@ -54,6 +55,7 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
         for (T t : c) {
             if (!hasWithName(getNamer().determineName(t))) {
                 getStore().add(current, t);
+                didAdd(t);
                 getEventRegister().getAddAction().execute(t);
                 changed = true;
                 current++;
@@ -75,7 +77,11 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
         assertMutable();
         assertCanAdd(element);
         T oldElement = getStore().set(index, element);
+        if (oldElement != null) {
+            didRemove(oldElement);
+        }
         getEventRegister().getRemoveAction().execute(oldElement);
+        didAdd(element);
         getEventRegister().getAddAction().execute(element);
         return oldElement;
     }
@@ -83,6 +89,9 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
     public T remove(int index) {
         assertMutable();
         T element = getStore().remove(index);
+        if (element != null) {
+            didRemove(element);
+        }
         getEventRegister().getRemoveAction().execute(element);
         return element;
     }
@@ -139,7 +148,7 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
         public ListIteratorImpl(ListIterator<T> iterator) {
             this.iterator = iterator;
         }
-        
+
         public boolean hasNext() {
             return iterator.hasNext();
         }
@@ -170,12 +179,14 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
             assertMutable();
             assertCanAdd(t);
             iterator.add(t);
+            didAdd(t);
             getEventRegister().getAddAction().execute(t);
         }
 
         public void remove() {
             assertMutable();
             iterator.remove();
+            didRemove(lastElement);
             getEventRegister().getRemoveAction().execute(lastElement);
             lastElement = null;
         }
@@ -184,7 +195,9 @@ public class DefaultNamedDomainObjectList<T> extends DefaultNamedDomainObjectCol
             assertMutable();
             assertCanAdd(t);
             iterator.set(t);
+            didRemove(lastElement);
             getEventRegister().getRemoveAction().execute(lastElement);
+            didAdd(t);
             getEventRegister().getAddAction().execute(t);
             lastElement = null;
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSet.java
index 34e7307..d8b2206 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DefaultNamedDomainObjectSet.java
@@ -44,12 +44,12 @@ public class DefaultNamedDomainObjectSet<T> extends DefaultNamedDomainObjectColl
      * Subclasses using this constructor must ensure that the {@code store} uses a name based equality strategy as per the contract on NamedDomainObjectContainer.
      */
     protected DefaultNamedDomainObjectSet(Class<? extends T> type, Set<T> store, CollectionEventRegister<T> eventRegister, Instantiator instantiator, Namer<? super T> namer) {
-        super(type, store, eventRegister, instantiator, namer);
+        super(type, store, eventRegister, new UnfilteredIndex<T>(), instantiator, namer);
     }
 
     // should be protected, but use of the class generator forces it to be public
     public DefaultNamedDomainObjectSet(DefaultNamedDomainObjectSet<? super T> collection, CollectionFilter<T> filter, Instantiator instantiator, Namer<? super T> namer) {
-        this(filter.getType(), collection.filteredStore(filter), collection.filteredEvents(filter), instantiator, namer);
+        super(collection, filter, instantiator, namer);
     }
 
     @Override
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 5d7ddaa..d27558a 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
@@ -16,23 +16,23 @@
 package org.gradle.api.internal;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import groovy.lang.Closure;
 import org.gradle.api.*;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
 
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorphicDomainObjectContainer<T>
         implements ExtensiblePolymorphicDomainObjectContainer<T> {
-    @Nullable
-    private NamedDomainObjectFactory<? extends T> defaultFactory;
-
-    private final Map<Class<?>, NamedDomainObjectFactory<?>> factories =
-            new HashMap<Class<?>, NamedDomainObjectFactory<?>>();
+    protected final Map<Class<? extends T>, NamedDomainObjectFactory<? extends T>> factories = Maps.newHashMap();
+    private final NamedEntityInstantiator<T> instantiator = new DefaultNamedEntityInstantiator();
 
     public DefaultPolymorphicDomainObjectContainer(Class<T> type, Instantiator instantiator, Namer<? super T> namer) {
         super(type, instantiator, namer);
@@ -42,13 +42,19 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
         this(type, instantiator, Named.Namer.forType(type));
     }
 
+    @Override
+    public NamedEntityInstantiator<T> getEntityInstantiator() {
+        return instantiator;
+    }
+
     protected T doCreate(String name) {
-        if (defaultFactory == null) {
+        NamedDomainObjectFactory<? extends T> factory = factories.get(getType());
+        if (factory == null) {
             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);
+        return factory.create(name);
     }
 
     protected <U extends T> U doCreate(String name, Class<U> type) {
@@ -61,8 +67,8 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
         return factory.create(name);
     }
 
-    public void registerDefaultFactory(NamedDomainObjectFactory<? extends T> factory) {
-        defaultFactory = factory;
+    public <U extends T> void registerDefaultFactory(NamedDomainObjectFactory<U> factory) {
+        factories.put(getType(), factory);
     }
 
     public <U extends T> void registerFactory(Class<U> type, NamedDomainObjectFactory<? extends U> factory) {
@@ -70,6 +76,10 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
             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()));
         }
+        if(factories.containsKey(type)){
+            throw new GradleException(String.format("Cannot register a factory for type %s because "
+                    + "a factory for this type is already registered.", type.getSimpleName()));
+        }
         factories.put(type, factory);
     }
 
@@ -99,4 +109,15 @@ public class DefaultPolymorphicDomainObjectContainer<T> extends AbstractPolymorp
         Collections.sort(names);
         return names.isEmpty() ? "(None)" : Joiner.on(", ").join(names);
     }
+
+    public Set<? extends Class<? extends T>> getCreateableTypes() {
+        return ImmutableSet.copyOf(factories.keySet());
+    }
+
+    private class DefaultNamedEntityInstantiator implements NamedEntityInstantiator<T> {
+        @Override
+        public <S extends T> S create(String name, Class<S> type) {
+            return doCreate(name, type);
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DelegatingDomainObjectSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DelegatingDomainObjectSet.java
index 11b9295..2099a2d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DelegatingDomainObjectSet.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DelegatingDomainObjectSet.java
@@ -20,6 +20,7 @@ import org.gradle.api.Action;
 import org.gradle.api.DomainObjectCollection;
 import org.gradle.api.DomainObjectSet;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
 
 import java.util.Collection;
 import java.util.Iterator;
@@ -33,7 +34,7 @@ public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
     }
 
     public DomainObjectSet<T> matching(Closure spec) {
-        return backingSet.matching(spec);
+        return matching(Specs.convertClosureToSpec(spec));
     }
 
     public DomainObjectSet<T> matching(Spec<? super T> spec) {
@@ -49,7 +50,7 @@ public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
     }
 
     public void all(Closure action) {
-        backingSet.all(action);
+        all(new ClosureBackedAction<T>(action));
     }
 
     public Action<? super T> whenObjectAdded(Action<? super T> action) {
@@ -57,7 +58,7 @@ public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
     }
 
     public void whenObjectAdded(Closure action) {
-        backingSet.whenObjectAdded(action);
+        whenObjectAdded(new ClosureBackedAction<T>(action));
     }
 
     public Action<? super T> whenObjectRemoved(Action<? super T> action) {
@@ -65,7 +66,7 @@ public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
     }
 
     public void whenObjectRemoved(Closure action) {
-        backingSet.whenObjectRemoved(action);
+        whenObjectRemoved(new ClosureBackedAction<T>(action));
     }
 
     public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
@@ -73,7 +74,7 @@ public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
     }
 
     public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
-        return backingSet.withType(type, configureClosure);
+        return withType(type, new ClosureBackedAction<T>(configureClosure));
     }
 
     public boolean add(T o) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyClassPathProvider.java
index 5423975..f006c51 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DependencyClassPathProvider.java
@@ -19,10 +19,11 @@ package org.gradle.api.internal;
 import org.gradle.api.internal.classpath.Module;
 import org.gradle.api.internal.classpath.ModuleRegistry;
 import org.gradle.api.internal.classpath.PluginModuleRegistry;
-import org.gradle.api.internal.classpath.UnknownModuleException;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
 
+import java.util.Arrays;
+
 import static org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory.ClassPathNotation.GRADLE_API;
 import static org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory.ClassPathNotation.LOCAL_GROOVY;
 
@@ -38,15 +39,10 @@ public class DependencyClassPathProvider implements ClassPathProvider {
     public ClassPath findClassPath(String name) {
         if (name.equals(GRADLE_API.name())) {
             ClassPath classpath = new DefaultClassPath();
-            Module core = moduleRegistry.getModule("gradle-core");
-            for (Module module : core.getAllRequiredModules()) {
-                classpath = classpath.plus(module.getClasspath());
-            }
-            classpath = classpath.plus(moduleRegistry.getModule("gradle-core-impl").getClasspath());
-            try {
-                classpath = classpath.plus(moduleRegistry.getModule("gradle-tooling-api").getImplementationClasspath());
-            } catch (UnknownModuleException e) {
-                // Ignore
+            for (String moduleName : Arrays.asList("gradle-core", "gradle-dependency-management", "gradle-plugin-use", "gradle-tooling-api")) {
+                for (Module module : moduleRegistry.getModule(moduleName).getAllRequiredModules()) {
+                    classpath = classpath.plus(module.getClasspath());
+                }
             }
             for (Module pluginModule : pluginModuleRegistry.getPluginModules()) {
                 classpath = classpath.plus(pluginModule.getClasspath());
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 2d220b9..fc53f29 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
@@ -16,19 +16,16 @@
 
 package org.gradle.api.internal;
 
-import org.gradle.api.Action;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.gradle.internal.reflect.ObjectInstantiationException;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.util.DeprecationLogger;
 
 import javax.inject.Inject;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -36,26 +33,15 @@ import java.util.List;
  */
 public class DependencyInjectingInstantiator implements Instantiator {
     private final ServiceRegistry services;
-    private final Action<String> onDeprecationWarning;
 
     public DependencyInjectingInstantiator(ServiceRegistry services) {
         this.services = services;
-        onDeprecationWarning = new Action<String>() {
-            public void execute(String message) {
-                DeprecationLogger.nagUserWith(message);
-            }
-        };
-    }
-
-    DependencyInjectingInstantiator(ServiceRegistry services, Action<String> onDeprecationWarning) {
-        this.services = services;
-        this.onDeprecationWarning = onDeprecationWarning;
     }
 
     public <T> T newInstance(Class<? extends T> type, Object... parameters) {
         try {
             validateType(type);
-            Constructor<?> constructor = selectConstructor(type, parameters);
+            Constructor<?> constructor = selectConstructor(type);
             constructor.setAccessible(true);
             Object[] resolvedParameters = convertParameters(type, constructor, parameters);
             try {
@@ -93,82 +79,42 @@ public class DependencyInjectingInstantiator implements Instantiator {
         return resolvedParameters;
     }
 
-    private <T> Constructor<?> selectConstructor(Class<T> type, Object... parameters) {
+    private <T> Constructor<?> selectConstructor(Class<T> type) {
         Constructor<?>[] constructors = type.getDeclaredConstructors();
 
-        // For backwards compatibility, first we look for a public constructor that accepts the provided parameters
-        // Then we find a candidate constructor as per JSR-330
-        // If we find an old style match, then warn if this is not the same as the JSR-330 match. Use the old style match
-        // If we find a JSR-330 match and no old style match, use the JSR-330 match
-        // Otherwise, fail
+        if (constructors.length == 1) {
+            Constructor<?> constructor = constructors[0];
+            if (constructor.getParameterTypes().length == 0 && isPublicOrPackageScoped(type, constructor)) {
+                return constructor;
+            }
+        }
 
-        Constructor<?> defaultConstructor = null;
         List<Constructor<?>> injectConstructors = new ArrayList<Constructor<?>>();
         for (Constructor<?> constructor : constructors) {
             if (constructor.getAnnotation(Inject.class) != null) {
                 injectConstructors.add(constructor);
             }
-            if (constructor.getParameterTypes().length == 0) {
-                defaultConstructor = constructor;
-            }
-        }
-        if (injectConstructors.isEmpty() && constructors.length == 1 && defaultConstructor != null) {
-            injectConstructors.add(defaultConstructor);
         }
 
-        Constructor<?> parameterMatchConstructor = null;
-        for (Constructor<?> constructor : type.getConstructors()) {
-            Class<?>[] parameterTypes = constructor.getParameterTypes();
-            if (parameterTypes.length == parameters.length) {
-                boolean match = true;
-                for (int i = 0; match && i < parameters.length; i++) {
-                    Class<?> targetType = parameterTypes[i];
-                    if (targetType.isPrimitive()) {
-                        targetType = JavaReflectionUtil.getWrapperTypeForPrimitiveType(targetType);
-                    }
-                    if (!targetType.isInstance(parameters[i])) {
-                        match = false;
-                    }
-                }
-                if (match) {
-                    if (parameterMatchConstructor != null) {
-                        throw new IllegalArgumentException(String.format("Class %s has multiple constructors that accept parameters %s.", type.getName(), Arrays.toString(parameters)));
-                    }
-                    parameterMatchConstructor = constructor;
-                }
-            }
+        if (injectConstructors.isEmpty()) {
+            throw new IllegalArgumentException(String.format("Class %s has no constructor that is annotated with @Inject.", type.getName()));
         }
-
-        if (parameterMatchConstructor == null && type.getConstructors().length == 1) {
-            // No match - allow a single constructor
-            parameterMatchConstructor = type.getConstructors()[0];
+        if (injectConstructors.size() > 1) {
+            throw new IllegalArgumentException(String.format("Class %s has multiple constructors that are annotated with @Inject.", type.getName()));
         }
+        return injectConstructors.get(0);
+    }
 
-        if (parameterMatchConstructor == null) {
-            // Use JSR-330 semantics
-            if (injectConstructors.isEmpty()) {
-                throw new IllegalArgumentException(String.format("Class %s has no constructor that accepts parameters %s or that is annotated with @Inject.", type.getName(), Arrays.toString(parameters)));
-            }
-            if (injectConstructors.size() > 1) {
-                throw new IllegalArgumentException(String.format("Class %s has multiple constructors with @Inject annotation.", type.getName()));
-            }
-            return injectConstructors.get(0);
+    private static boolean isPublicOrPackageScoped(Class<?> type, Constructor<?> constructor) {
+        if (isPackagePrivate(type.getModifiers())) {
+            return !Modifier.isPrivate(constructor.getModifiers()) && !Modifier.isProtected(constructor.getModifiers());
+        } else {
+            return Modifier.isPublic(constructor.getModifiers());
         }
+    }
 
-        // Use backwards compatible semantics, but warn when they don't match
-
-        if (injectConstructors.isEmpty()) {
-            if (type.getConstructors().length == 1) {
-                onDeprecationWarning.execute(String.format("Constructor for class %s is not annotated with @Inject. In Gradle 2.0 this will be treated as an error.", type.getName()));
-            } else {
-                onDeprecationWarning.execute(String.format("Class %s has multiple constructors and no constructor is annotated with @Inject. In Gradle 2.0 this will be treated as an error.", type.getName()));
-            }
-        } else if (injectConstructors.size() > 1) {
-            onDeprecationWarning.execute(String.format("Class %s has multiple constructors with @Inject annotation. In Gradle 2.0 this will be treated as an error.", type.getName()));
-        } else if (!injectConstructors.get(0).equals(parameterMatchConstructor)) {
-            onDeprecationWarning.execute(String.format("Class %s has @Inject annotation on an unexpected constructor. In Gradle 2.0 the constructor annotated with @Inject will be used instead of the current default constructor.", type.getName()));
-        }
-        return parameterMatchConstructor;
+    private static boolean isPackagePrivate(int modifiers) {
+        return !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers) && !Modifier.isPublic(modifiers);
     }
 
     private <T> void validateType(Class<T> type) {
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 2213414..f479d4b 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
@@ -35,6 +35,10 @@ public class DocumentationRegistry {
         return String.format("http://gradle.org/docs/%s/userguide/%s.html", gradleVersion.getVersion(), id);
     }
 
+    public String getDocumentationFor(String id, String section) {
+        return String.format("http://gradle.org/docs/%s/userguide/%s.html#%s", gradleVersion.getVersion(), id, section);
+    }
+
     public String getDslRefForProperty(Class<?> clazz, String property) {
         String className = clazz.getName();
         return String.format("http://gradle.org/docs/%s/dsl/%s.html#%s:%s", gradleVersion.getVersion(), className, className, property);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/DynamicModulesClassPathProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/DynamicModulesClassPathProvider.java
index dfca66a..8bd78ce 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/DynamicModulesClassPathProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/DynamicModulesClassPathProvider.java
@@ -21,6 +21,9 @@ import org.gradle.api.internal.classpath.PluginModuleRegistry;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
 
+import java.util.Arrays;
+import java.util.Set;
+
 public class DynamicModulesClassPathProvider implements ClassPathProvider {
     private final ModuleRegistry moduleRegistry;
     private final PluginModuleRegistry pluginModuleRegistry;
@@ -31,16 +34,21 @@ public class DynamicModulesClassPathProvider implements ClassPathProvider {
     }
 
     public ClassPath findClassPath(String name) {
-        if (name.equals("GRADLE_PLUGINS")) {
+        if (name.equals("GRADLE_EXTENSIONS")) {
+            Set<Module> coreModules = moduleRegistry.getModule("gradle-core").getAllRequiredModules();
             ClassPath classpath = new DefaultClassPath();
+            for (String moduleName : Arrays.asList("gradle-dependency-management", "gradle-plugin-use")) {
+                for (Module module : moduleRegistry.getModule(moduleName).getAllRequiredModules()) {
+                    if (!coreModules.contains(module)) {
+                        classpath = classpath.plus(module.getClasspath());
+                    }
+                }
+            }
             for (Module pluginModule : pluginModuleRegistry.getPluginModules()) {
                 classpath = classpath.plus(pluginModule.getClasspath());
             }
             return classpath;
         }
-        if (name.equals("GRADLE_CORE_IMPL")) {
-            return moduleRegistry.getModule("gradle-core-impl").getClasspath();
-        }
 
         return null;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ExtensibleDynamicObject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ExtensibleDynamicObject.java
index 102ec3f..6061591 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ExtensibleDynamicObject.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ExtensibleDynamicObject.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal;
 
+import com.google.common.collect.Lists;
 import groovy.lang.MissingPropertyException;
 import org.gradle.api.internal.plugins.DefaultConvention;
 import org.gradle.api.internal.plugins.ExtraPropertiesDynamicObjectAdapter;
@@ -22,7 +23,6 @@ import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtraPropertiesExtension;
 import org.gradle.internal.reflect.Instantiator;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -69,13 +69,13 @@ public class ExtensibleDynamicObject extends CompositeDynamicObject implements H
     public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Convention convention) {
         this.dynamicDelegate = dynamicDelegate;
         this.convention = convention;
-        this.extraPropertiesDynamicObject = new ExtraPropertiesDynamicObjectAdapter(delegate, this, convention.getExtraProperties());
+        this.extraPropertiesDynamicObject = new ExtraPropertiesDynamicObjectAdapter(delegate.getClass(), convention.getExtraProperties());
 
         updateDelegates();
     }
 
     private void updateDelegates() {
-        List<DynamicObject> delegates = new ArrayList<DynamicObject>();
+        List<DynamicObject> delegates = Lists.newLinkedList();
         delegates.add(dynamicDelegate);
         delegates.add(extraPropertiesDynamicObject);
         if (beforeConvention != null) {
@@ -87,12 +87,16 @@ public class ExtensibleDynamicObject extends CompositeDynamicObject implements H
         if (afterConvention != null) {
             delegates.add(afterConvention);
         }
+        boolean addedParent = false;
         if (parent != null) {
+            addedParent = true;
             delegates.add(parent);
         }
         setObjects(delegates.toArray(new DynamicObject[delegates.size()]));
 
-        delegates.remove(parent);
+        if (addedParent) {
+            delegates.remove(delegates.size() - 1);
+        }
         delegates.add(extraPropertiesDynamicObject);
         setObjectsForUpdate(delegates.toArray(new DynamicObject[delegates.size()]));
     }
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 ddfab12..8775645 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
@@ -59,7 +59,7 @@ public interface GradleInternal extends Gradle {
     /**
      * Called by the BuildLoader after the root project is determined.  Until the BuildLoader
      * is executed, {@link #getRootProject()} will return null.
-      @param rootProject The root project for this build.
+     @param rootProject The root project for this build.
      */
     void setRootProject(ProjectInternal rootProject);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerInternal.java
new file mode 100644
index 0000000..3d86cfd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/PolymorphicDomainObjectContainerInternal.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;
+
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
+
+import java.util.Set;
+
+public interface PolymorphicDomainObjectContainerInternal<T> extends PolymorphicDomainObjectContainer<T> {
+
+    Set<? extends Class<? extends T>> getCreateableTypes();
+
+    NamedEntityInstantiator<T> getEntityInstantiator();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ProcessOperations.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ProcessOperations.java
index 9202d25..d501e2a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ProcessOperations.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ProcessOperations.java
@@ -15,11 +15,15 @@
  */
 package org.gradle.api.internal;
 
-import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 
 public interface ProcessOperations {
-    ExecResult javaexec(Closure cl);
 
-    ExecResult exec(Closure cl);
+    ExecResult javaexec(Action<? super JavaExecSpec> action);
+
+    ExecResult exec(Action<? super ExecSpec> action);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ReflectiveNamedDomainObjectFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ReflectiveNamedDomainObjectFactory.java
index 8db6a20..d4e7b59 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ReflectiveNamedDomainObjectFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ReflectiveNamedDomainObjectFactory.java
@@ -25,7 +25,7 @@ public class ReflectiveNamedDomainObjectFactory<T> implements NamedDomainObjectF
     private final Instantiator instantiator;
 
     public ReflectiveNamedDomainObjectFactory(Class<? extends T> type, Object... extraArgs) {
-        this(type, new DirectInstantiator(), extraArgs);
+        this(type, DirectInstantiator.INSTANCE, extraArgs);
     }
     
     public ReflectiveNamedDomainObjectFactory(Class<? extends T> type, Instantiator instantiator, Object... extraArgs) {
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 9db7540..89c0eac 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
@@ -17,6 +17,7 @@
 package org.gradle.api.internal;
 
 import org.gradle.StartParameter;
+import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.project.ProjectRegistry;
@@ -24,7 +25,14 @@ import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.initialization.DefaultProjectDescriptor;
 
 public interface SettingsInternal extends Settings {
-
+    /**
+     * Returns the scope containing classes that should be visible to all settings and build scripts invoked by this build.
+     */
+    ClassLoaderScope getRootClassLoaderScope();
+
+    /**
+     * Returns the scope into which the main settings script should define classes, and from which plugins applied to this settings object should be resolved.
+     */
     ClassLoaderScope getClassLoaderScope();
 
     StartParameter getStartParameter();
@@ -33,4 +41,7 @@ public interface SettingsInternal extends Settings {
 
     ProjectRegistry<DefaultProjectDescriptor> getProjectRegistry();
 
+    ProjectDescriptor getDefaultProject();
+
+    void setDefaultProject(ProjectDescriptor defaultProject);
 }
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 37eaaa0..24099bb 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
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal;
 
+import org.gradle.api.Action;
 import org.gradle.api.Task;
 import org.gradle.api.internal.tasks.ContextAwareTaskAction;
 import org.gradle.api.internal.tasks.TaskExecuter;
@@ -53,11 +54,22 @@ public interface TaskInternal extends Task, Configurable<Task> {
 
     void addValidator(TaskValidator validator);
 
-    TaskStateInternal getStateInternal();
+    TaskStateInternal getState();
+
+    boolean getImpliesSubProjects();
+
+    void setImpliesSubProjects(boolean impliesSubProjects);
+
     /**
      * The returned factory is expected to return the same file each time.
      * <p>
      * The getTemporaryDir() method creates the directory which can be problematic. Use this to delay that creation.
      */
     Factory<File> getTemporaryDirFactory();
+
+    void prependParallelSafeAction(Action<? super Task> action);
+
+    void appendParallelSafeAction(Action<? super Task> action);
+
+    boolean isHasCustomActions();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/ThreadGlobalInstantiator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/ThreadGlobalInstantiator.java
index b9653a9..12667ea 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/ThreadGlobalInstantiator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/ThreadGlobalInstantiator.java
@@ -57,7 +57,7 @@ public abstract class ThreadGlobalInstantiator {
         if (instantiator != null) {
             return instantiator;
         } else {
-            return new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator());
+            return new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE);
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java
new file mode 100644
index 0000000..6142dff
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal;
+
+import groovy.lang.Closure;
+import org.gradle.api.*;
+import org.gradle.api.internal.plugins.DefaultConvention;
+import org.gradle.api.plugins.Convention;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.util.ConfigureUtil;
+
+import java.util.*;
+
+public class TypedDomainObjectContainerWrapper<U> implements NamedDomainObjectContainer<U>, DynamicObjectAware {
+    private final Class<U> type;
+    private final PolymorphicDomainObjectContainer<? super U> parent;
+    private final NamedDomainObjectSet<U> delegate;
+    private final Convention convention;
+
+    public TypedDomainObjectContainerWrapper(Class<U> type, PolymorphicDomainObjectContainer<? super U> parent, Instantiator instantiator) {
+        this.parent = parent;
+        this.type = type;
+        this.delegate = parent.withType(type);
+        this.convention = new DefaultConvention(instantiator);
+    }
+
+    public Convention getConvention() {
+        return convention;
+    }
+
+    public U create(String name) throws InvalidUserDataException {
+        return parent.create(name, type);
+    }
+
+    public U create(String name, Action<? super U> configureAction) throws InvalidUserDataException {
+        return parent.create(name, type, configureAction);
+    }
+
+    public U create(String name, Closure configureClosure) throws InvalidUserDataException {
+        return parent.create(name, type, new ClosureBackedAction<U>(configureClosure));
+    }
+
+    public U maybeCreate(String name) {
+        return parent.maybeCreate(name, type);
+    }
+
+    public DynamicObject getAsDynamicObject() {
+        return ((DynamicObjectAware) delegate).getAsDynamicObject();
+    }
+
+    public NamedDomainObjectContainer<U> configure(Closure configureClosure) {
+        NamedDomainObjectContainerConfigureDelegate delegate = new NamedDomainObjectContainerConfigureDelegate(configureClosure.getOwner(), this);
+        ConfigureUtil.configure(configureClosure, delegate);
+        return this;
+    }
+
+    public Set<U> findAll(Closure spec) {
+        return delegate.findAll(spec);
+    }
+
+    public NamedDomainObjectSet<U> matching(Closure spec) {
+        return delegate.matching(spec);
+    }
+
+    public NamedDomainObjectSet<U> matching(Spec<? super U> spec) {
+        return delegate.matching(spec);
+    }
+
+    public <S extends U> NamedDomainObjectSet<S> withType(Class<S> type) {
+        return delegate.withType(type);
+    }
+
+    public boolean add(U e) {
+        return delegate.add(e);
+    }
+
+    public boolean addAll(Collection<? extends U> c) {
+        return delegate.addAll(c);
+    }
+
+    public Rule addRule(String description, Closure ruleAction) {
+        return delegate.addRule(description, ruleAction);
+    }
+
+    public Rule addRule(Rule rule) {
+        return delegate.addRule(rule);
+    }
+
+    public U findByName(String name) {
+        return delegate.findByName(name);
+    }
+
+    public SortedMap<String, U> getAsMap() {
+        return delegate.getAsMap();
+    }
+
+    public U getAt(String name) throws UnknownDomainObjectException {
+        return delegate.getAt(name);
+    }
+
+    public U getByName(String name) throws UnknownDomainObjectException {
+        return delegate.getByName(name);
+    }
+
+    public U getByName(String name, Closure configureClosure) throws UnknownDomainObjectException {
+        return delegate.getByName(name, configureClosure);
+    }
+
+    public Namer<U> getNamer() {
+        return delegate.getNamer();
+    }
+
+    public SortedSet<String> getNames() {
+        return delegate.getNames();
+    }
+
+    public List<Rule> getRules() {
+        return delegate.getRules();
+    }
+
+    public void all(Action<? super U> action) {
+        delegate.all(action);
+    }
+
+    public void all(Closure action) {
+        delegate.all(action);
+    }
+
+    public Action<? super U> whenObjectAdded(Action<? super U> action) {
+        return delegate.whenObjectAdded(action);
+    }
+
+    public void whenObjectAdded(Closure action) {
+        delegate.whenObjectAdded(action);
+    }
+
+    public Action<? super U> whenObjectRemoved(Action<? super U> action) {
+        return delegate.whenObjectRemoved(action);
+    }
+
+    public void whenObjectRemoved(Closure action) {
+        delegate.whenObjectRemoved(action);
+    }
+
+    public <S extends U> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
+        return delegate.withType(type, configureAction);
+    }
+
+    public <S extends U> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
+        return delegate.withType(type, configureClosure);
+    }
+
+    public void clear() {
+        delegate.clear();
+    }
+
+    public boolean contains(Object o) {
+        return delegate.contains(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        return delegate.containsAll(c);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return delegate.equals(o);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    public Iterator<U> iterator() {
+        return delegate.iterator();
+    }
+
+    public boolean remove(Object o) {
+        return delegate.remove(o);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        return delegate.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        return delegate.retainAll(c);
+    }
+
+    public int size() {
+        return delegate.size();
+    }
+
+    public Object[] toArray() {
+        return delegate.toArray();
+    }
+
+    public <T> T[] toArray(T[] a) {
+        return delegate.toArray(a);
+    }
+}
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 31a2661..77b56db 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
@@ -15,22 +15,14 @@
  */
 package org.gradle.api.internal.artifacts;
 
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.plugin.internal.PluginResolverFactory;
 
 /**
- * Factory for {@link ArtifactRepository} implementations.
+ * Factory for {@link org.gradle.api.artifacts.repositories.ArtifactRepository} implementations.
  */
 public interface BaseRepositoryFactory {
-
-    String JCENTER_REPO_OVERRIDE_URL_PROPERTY = PluginResolverFactory.class.getName() + ".jcenter.override";
-
-    ArtifactRepository createRepository(Object userDescription);
-
     FlatDirectoryArtifactRepository createFlatDirRepository();
 
     MavenArtifactRepository createMavenLocalRepository();
@@ -42,8 +34,4 @@ public interface BaseRepositoryFactory {
     IvyArtifactRepository createIvyRepository();
 
     MavenArtifactRepository createMavenRepository();
-
-    DependencyResolver toResolver(ArtifactRepository repository);
-
-    ArtifactRepository createResolverBackedRepository(DependencyResolver resolver);
 }
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 14b76d0..5b47b66 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
@@ -17,45 +17,38 @@
 package org.gradle.api.internal.artifacts;
 
 import groovy.lang.Closure;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.Action;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Namer;
 import org.gradle.api.UnknownDomainObjectException;
 import org.gradle.api.artifacts.ArtifactRepositoryContainer;
 import org.gradle.api.artifacts.UnknownRepositoryException;
 import org.gradle.api.artifacts.repositories.ArtifactRepository;
-import org.gradle.internal.Actions;
 import org.gradle.api.internal.DefaultNamedDomainObjectList;
 import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal;
+import org.gradle.internal.Actions;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.util.ConfigureUtil;
-import org.gradle.util.DeprecationLogger;
 import org.gradle.util.GUtil;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.internal.Cast.cast;
-
 public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObjectList<ArtifactRepository>
         implements ArtifactRepositoryContainer {
 
-    private final BaseRepositoryFactory baseRepositoryFactory;
     private final Action<ArtifactRepository> addLastAction = new Action<ArtifactRepository>() {
         public void execute(ArtifactRepository repository) {
             DefaultArtifactRepositoryContainer.super.add(repository);
         }
     };
-    private final Action<ArtifactRepository> addFirstAction = new Action<ArtifactRepository>() {
-        public void execute(ArtifactRepository repository) {
-            DefaultArtifactRepositoryContainer.super.add(0, repository);
-        }
-    };
 
-    public DefaultArtifactRepositoryContainer(BaseRepositoryFactory baseRepositoryFactory, Instantiator instantiator) {
+    public DefaultArtifactRepositoryContainer(Instantiator instantiator) {
         super(ArtifactRepository.class, instantiator, new RepositoryNamer());
-        this.baseRepositoryFactory = baseRepositoryFactory;
+        whenObjectAdded(new Action<ArtifactRepository>() {
+            public void execute(ArtifactRepository artifactRepository) {
+                if (artifactRepository instanceof ArtifactRepositoryInternal) {
+                    ArtifactRepositoryInternal repository = (ArtifactRepositoryInternal) artifactRepository;
+                    repository.onAddToContainer(DefaultArtifactRepositoryContainer.this);
+                }
+            }
+        });
     }
 
     private static class RepositoryNamer implements Namer<ArtifactRepository> {
@@ -81,102 +74,11 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
         add(repository);
     }
 
-    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;
-    }
-
-    public DependencyResolver addFirst(Object userDescription) {
-        return addFirst(userDescription, null);
-    }
-
-    public DependencyResolver addFirst(Object userDescription, Closure configureClosure) {
-        DeprecationLogger.nagUserOfReplacedMethod("ArtifactRepositoryContainer.addFirst(Object)", "addFirst(ArtifactRepository");
-        return addCustomDependencyResolver(userDescription, configureClosure, addFirstAction);
-    }
-
-    @Deprecated
-    public DependencyResolver addLast(Object userDescription) {
-        DeprecationLogger.nagUserOfReplacedMethod("ArtifactRepositoryContainer.addLast()", "maven() or add()");
-        return addCustomDependencyResolver(userDescription, null, addLastAction);
-    }
-
-    @Deprecated
-    public DependencyResolver addLast(Object userDescription, Closure configureClosure) {
-        DeprecationLogger.nagUserOfReplacedMethod("ArtifactRepositoryContainer.addLast()", "maven() or add()");
-        return addCustomDependencyResolver(userDescription, configureClosure, addLastAction);
-    }
-
-    public DependencyResolver addBefore(Object userDescription, String afterResolverName) {
-        return addBefore(userDescription, afterResolverName, null);
-    }
-
-    public DependencyResolver addBefore(Object userDescription, final String afterResolverName, Closure configureClosure) {
-        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) {
-                DefaultArtifactRepositoryContainer.super.add(indexOf(after), repository);
-            }
-        });
-    }
-
-    public DependencyResolver addAfter(Object userDescription, final String beforeResolverName) {
-        return addAfter(userDescription, beforeResolverName, null);
-    }
-
-    public DependencyResolver addAfter(Object userDescription, final String beforeResolverName, Closure configureClosure) {
-        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>() {
-            public void execute(ArtifactRepository repository) {
-                int insertPos = indexOf(before) + 1;
-                if (insertPos == size()) {
-                    DefaultArtifactRepositoryContainer.super.add(repository);
-                } else {
-                    DefaultArtifactRepositoryContainer.this.add(insertPos, repository);
-                }
-            }
-        });
-    }
-
-    private DependencyResolver addCustomDependencyResolver(Object userDescription, Closure configureClosure, Action<ArtifactRepository> orderAction) {
-        ArtifactRepository repository = baseRepositoryFactory.createRepository(userDescription);
-        DependencyResolver resolver = baseRepositoryFactory.toResolver(repository);
-        ConfigureUtil.configure(configureClosure, resolver);
-        ArtifactRepository resolverRepository = baseRepositoryFactory.createResolverBackedRepository(resolver);
-        addWithUniqueName(resolverRepository, "repository", orderAction);
-        return resolver;
-    }
-
     @Override
     protected UnknownDomainObjectException createNotFoundException(String name) {
         return new UnknownRepositoryException(String.format("Repository with name '%s' not found.", name));
     }
 
-    public List<DependencyResolver> getResolvers() {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("ArtifactRepositoryContainer.getResolvers()");
-        List<DependencyResolver> returnedResolvers = new ArrayList<DependencyResolver>();
-        for (ArtifactRepository repository : this) {
-            returnedResolvers.add(baseRepositoryFactory.toResolver(repository));
-        }
-        return returnedResolvers;
-    }
-
     public <T extends ArtifactRepository> T addRepository(T repository, String defaultName) {
         return addRepository(repository, defaultName, Actions.doNothing());
     }
@@ -196,7 +98,6 @@ public class DefaultArtifactRepositoryContainer extends DefaultNamedDomainObject
 
         assertCanAdd(repository.getName());
         insertion.execute(repository);
-        cast(ArtifactRepositoryInternal.class, repository).onAddToContainer(this);
         return repository;
     }
 
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 249e3bd..4b65baf 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
@@ -16,10 +16,6 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.util.DeprecationLogger;
-
-import java.util.HashMap;
-import java.util.Map;
 
 public class DefaultExcludeRule implements ExcludeRule {
     private String group;
@@ -49,14 +45,6 @@ public class DefaultExcludeRule implements ExcludeRule {
         this.module = moduleValue;
     }
 
-    public Map<String, String> getExcludeArgs() {
-        DeprecationLogger.nagUserOfDeprecated("The getExcludeArgs method", "Please use the getGroup() method or the getModule() method instead");
-        Map excludeArgsAsMap = new HashMap();
-        excludeArgsAsMap.put(ExcludeRule.GROUP_KEY, group);
-        excludeArgsAsMap.put(ExcludeRule.MODULE_KEY, module);
-        return excludeArgsAsMap;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
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 b1a0f73..89bb776 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
@@ -17,7 +17,6 @@ package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.artifacts.ExcludeRuleContainer;
-import org.gradle.internal.typeconversion.NotationParser;
 
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -26,18 +25,15 @@ import java.util.Set;
 
 public class DefaultExcludeRuleContainer implements ExcludeRuleContainer {
     private Set<ExcludeRule> addedRules = new LinkedHashSet<ExcludeRule>();
-    private NotationParser<Object, ExcludeRule> notationParser = new ExcludeRuleNotationParser();
-    //TODO has usage of NotationParserBuilder here any advantage?
 
-    public DefaultExcludeRuleContainer() {
-    }
+    public DefaultExcludeRuleContainer() {}
 
     public DefaultExcludeRuleContainer(Set<ExcludeRule> addedRules) {
         this.addedRules = new HashSet<ExcludeRule>(addedRules);
     }
 
     public void add(Map<String, String> args) {
-        addedRules.add(notationParser.parseNotation(args));
+        addedRules.add(ExcludeRuleNotationConverter.parser().parseNotation(args));
     }
 
     public Set<ExcludeRule> getRules() {
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 df3e931..7ff9336 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
@@ -15,14 +15,15 @@
  */
 package org.gradle.api.internal.artifacts;
 
+import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
 
 public interface DependencyResolutionServices {
     RepositoryHandler getResolveRepositoryHandler();
 
-    ConfigurationContainerInternal getConfigurationContainer();
+    ConfigurationContainer getConfigurationContainer();
 
     DependencyHandler getDependencyHandler();
+
 }
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 7c29c81..894741e 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
@@ -23,4 +23,7 @@ public interface DependencyResolveDetailsInternal extends DependencyResolveDetai
 
     void useVersion(String version, ComponentSelectionReason selectionReason);
 
+    ComponentSelectionReason getSelectionReason();
+
+    boolean isUpdated();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencySubstitutionInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencySubstitutionInternal.java
new file mode 100644
index 0000000..7da7871
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/DependencySubstitutionInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.DependencySubstitution;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+
+public interface DependencySubstitutionInternal<T extends ComponentSelector> extends DependencySubstitution<T> {
+
+    ModuleVersionSelector getOldRequested();
+
+    void useTarget(Object notation, ComponentSelectionReason selectionReason);
+
+    ComponentSelector getTarget();
+
+    ComponentSelectionReason getSelectionReason();
+
+    boolean isUpdated();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverter.java
new file mode 100644
index 0000000..46f96d8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.MapKey;
+import org.gradle.internal.typeconversion.MapNotationConverter;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+
+public class ExcludeRuleNotationConverter extends MapNotationConverter<ExcludeRule> {
+
+    private static final NotationParser<Object, ExcludeRule> PARSER =
+            NotationParserBuilder.toType(ExcludeRule.class).converter(new ExcludeRuleNotationConverter()).toComposite();
+
+    public static NotationParser<Object, ExcludeRule> parser() {
+        return PARSER;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("Maps with 'group' and/or 'module'").example("[group: 'com.google.collections', module: 'google-collections']");
+    }
+
+    protected ExcludeRule parseMap(@MapKey(ExcludeRule.GROUP_KEY) @Optional String group,
+                         @MapKey(ExcludeRule.MODULE_KEY) @Optional String module) {
+        if (group == null && module == null) {
+            throw new InvalidUserDataException("Dependency exclude rule requires 'group' and/or 'module' specified. For example: [group: 'com.google.collections']");
+        }
+        return new DefaultExcludeRule(group, module);
+    }
+}
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
deleted file mode 100644
index 766e3af..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParser.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.api.tasks.Optional;
-
-import java.util.Collection;
-
-public class ExcludeRuleNotationParser extends MapNotationParser<ExcludeRule> {
-
-    @Override
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Maps, e.g. [group: 'org.gradle', module:'gradle-core'].");
-    }
-
-    protected ExcludeRule parseMap(@MapKey(ExcludeRule.GROUP_KEY) @Optional String group,
-                         @MapKey(ExcludeRule.MODULE_KEY) @Optional String module) {
-        if (group == null && module == null) {
-            throw new InvalidUserDataException("Either a group or module must be specified. For example: [group:'org.gradle']");
-        }
-        DefaultExcludeRule excluderule = new DefaultExcludeRule();
-        excluderule.setGroup(group);
-        excluderule.setModule(module);
-        return excluderule;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleDependencySubstitutionInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleDependencySubstitutionInternal.java
new file mode 100644
index 0000000..36b9333
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ModuleDependencySubstitutionInternal.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleDependencySubstitution;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+
+public interface ModuleDependencySubstitutionInternal extends ModuleDependencySubstitution, DependencySubstitutionInternal<ModuleComponentSelector> {
+    void useVersion(String version, ComponentSelectionReason selectionReason);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependencySubstitutionInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependencySubstitutionInternal.java
new file mode 100644
index 0000000..2b07a4b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependencySubstitutionInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ProjectDependencySubstitution;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+
+public interface ProjectDependencySubstitutionInternal extends ProjectDependencySubstitution, DependencySubstitutionInternal<ProjectComponentSelector> {
+}
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
deleted file mode 100644
index 2995f7d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.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.api.internal.artifacts.configurations;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.DependencyResolutionListener;
-
-public interface ConfigurationInternal extends Configuration, DependencyMetaDataProvider {
-    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/ResolutionStrategyInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
deleted file mode 100644
index 9ece2d4..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
+++ /dev/null
@@ -1,56 +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.configurations;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ConflictResolution;
-import org.gradle.api.artifacts.ResolutionStrategy;
-import org.gradle.api.artifacts.cache.ResolutionRules;
-import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
-import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
-
-public interface ResolutionStrategyInternal extends ResolutionStrategy {
-
-    /**
-     * Gets the current expiry policy for dynamic revisions.
-     *
-     * @return the expiry policy
-     */
-    CachePolicy getCachePolicy();
-
-    /**
-     * Until the feature 'settles' and we receive more feedback, it's internal
-     *
-     * @return conflict resolution
-     */
-    ConflictResolution getConflictResolution();
-
-    /**
-     * The nascent DSL for cache control, and possibly other per-module resolution overrides
-     * @return the resolution rules
-     */
-    ResolutionRules getResolutionRules();
-
-    /**
-     * @return the dependency resolve rule (may aggregate multiple rules)
-     */
-    Action<DependencyResolveDetailsInternal> getDependencyResolveRule();
-
-    /**
-     * @return copy of this resolution strategy. See the contract of {@link org.gradle.api.artifacts.Configuration#copy()}.
-     */
-    ResolutionStrategyInternal copy();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/dynamicversion/CachePolicy.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/dynamicversion/CachePolicy.java
index 2fa2ad7..4e22907 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/dynamicversion/CachePolicy.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/dynamicversion/CachePolicy.java
@@ -15,11 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.configurations.dynamicversion;
 
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ArtifactIdentifier;
 import org.gradle.api.artifacts.ModuleIdentifier;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 
 import java.io.File;
 import java.util.Set;
@@ -27,9 +27,11 @@ import java.util.Set;
 public interface CachePolicy {
     boolean mustRefreshVersionList(ModuleIdentifier selector, Set<ModuleVersionIdentifier> moduleVersions, long ageMillis);
 
-    boolean mustRefreshModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion resolvedModuleVersion, ModuleRevisionId moduleRevisionId, long ageMillis);
+    boolean mustRefreshMissingModule(ModuleComponentIdentifier component, long ageMillis);
 
-    boolean mustRefreshChangingModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion resolvedModuleVersion, long ageMillis);
+    boolean mustRefreshModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis);
+
+    boolean mustRefreshChangingModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis);
 
     boolean mustRefreshModuleArtifacts(ModuleVersionIdentifier moduleVersionId, Set<ArtifactIdentifier> artifacts, long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalDependency.java
deleted file mode 100644
index 1d60c37..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalDependency.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.dependencies;
-
-import org.gradle.api.artifacts.ExternalDependency;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.internal.artifacts.ModuleVersionSelectorStrictSpec;
-
-public abstract class AbstractExternalDependency extends AbstractModuleDependency implements ExternalDependency {
-    public AbstractExternalDependency(String configuration) {
-        super(configuration);
-    }
-
-    protected void copyTo(AbstractExternalDependency target) {
-        super.copyTo(target);
-        target.setForce(isForce());
-    }
-
-    protected boolean isContentEqualsFor(ExternalDependency dependencyRhs) {
-        if (!isKeyEquals(dependencyRhs) || !isCommonContentEquals(dependencyRhs)) {
-            return false;
-        }
-        return isForce() == dependencyRhs.isForce();
-    }
-
-    public boolean matchesStrictly(ModuleVersionIdentifier identifier) {
-        return new ModuleVersionSelectorStrictSpec(this).isSatisfiedBy(identifier);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java
new file mode 100644
index 0000000..b7ee7ef
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractExternalModuleDependency.java
@@ -0,0 +1,86 @@
+/*
+ * 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.dependencies;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ExternalModuleDependency;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionSelectorStrictSpec;
+
+public abstract class AbstractExternalModuleDependency extends AbstractModuleDependency implements ExternalModuleDependency {
+    private String group;
+    private String name;
+    private String version;
+    private boolean changing;
+    private boolean force;
+
+    public AbstractExternalModuleDependency(String group, String name, String version, String configuration) {
+        super(configuration);
+        if (name == null) {
+            throw new InvalidUserDataException("Name must not be null!");
+        }
+        this.group = group;
+        this.name = name;
+        this.version = version;
+    }
+
+    protected void copyTo(AbstractExternalModuleDependency target) {
+        super.copyTo(target);
+        target.setForce(isForce());
+        target.setChanging(isChanging());
+    }
+
+    protected boolean isContentEqualsFor(ExternalModuleDependency dependencyRhs) {
+        if (!isKeyEquals(dependencyRhs) || !isCommonContentEquals(dependencyRhs)) {
+            return false;
+        }
+        return force == dependencyRhs.isForce() && changing == dependencyRhs.isChanging();
+    }
+
+    public boolean matchesStrictly(ModuleVersionIdentifier identifier) {
+        return new ModuleVersionSelectorStrictSpec(this).isSatisfiedBy(identifier);
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public boolean isForce() {
+        return force;
+    }
+
+    public ExternalModuleDependency setForce(boolean force) {
+        this.force = force;
+        return this;
+    }
+
+    public boolean isChanging() {
+        return changing;
+    }
+
+    public ExternalModuleDependency setChanging(boolean changing) {
+        this.changing = changing;
+        return this;
+    }
+}
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 3de7134..7cc7c98 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
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.dependencies;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.ClientModule;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ModuleDependency;
@@ -24,15 +23,7 @@ import org.gradle.api.artifacts.ModuleDependency;
 import java.util.HashSet;
 import java.util.Set;
 
-public class DefaultClientModule extends AbstractExternalDependency implements ClientModule {
-
-    private String group;
-
-    private String name;
-
-    private String version;
-
-    private boolean force;
+public class DefaultClientModule extends AbstractExternalModuleDependency implements ClientModule {
 
     private Set<ModuleDependency> dependencies = new HashSet<ModuleDependency>();
 
@@ -41,13 +32,11 @@ public class DefaultClientModule extends AbstractExternalDependency implements C
     }
 
     public DefaultClientModule(String group, String name, String version, String configuration) {
-        super(configuration);
-        if (name == null) {
-            throw new InvalidUserDataException("Name must not be null!");
-        }
-        this.group = group;
-        this.name = name;
-        this.version = version;
+        super(group, name, version, configuration);
+    }
+
+    public String getId() {
+        return emptyStringIfNull(getGroup()) + ":" + getName() + ":" + emptyStringIfNull(getVersion());
     }
 
     private String emptyStringIfNull(String value) {
@@ -58,53 +47,12 @@ public class DefaultClientModule extends AbstractExternalDependency implements C
         return dependencies;
     }
 
-    public String getId() {
-        return emptyStringIfNull(group) + ":" + emptyStringIfNull(name) + ":" + emptyStringIfNull(version);
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public ClientModule setGroup(String group) {
-        this.group = group;
-        return this;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public ClientModule setName(String name) {
-        this.name = name;
-        return this;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public ClientModule setVersion(String version) {
-        this.version = version;
-        return this;
-    }
-
-    public boolean isForce() {
-        return force;
-    }
-
-    public ClientModule setForce(boolean force) {
-        this.force = force;
-        return this;
-    }
-
     public void addDependency(ModuleDependency dependency) {
         this.dependencies.add(dependency);
     }
 
     public ClientModule copy() {
-        DefaultClientModule copiedClientModule = new DefaultClientModule(getGroup(), getName(), getVersion(),
-                getConfiguration());
+        DefaultClientModule copiedClientModule = new DefaultClientModule(getGroup(), getName(), getVersion(), getConfiguration());
         copyTo(copiedClientModule);
         for (ModuleDependency dependency : dependencies) {
             copiedClientModule.addDependency(dependency.copy());
@@ -121,11 +69,8 @@ public class DefaultClientModule extends AbstractExternalDependency implements C
         }
 
         ClientModule that = (ClientModule) dependency;
-        if (!isContentEqualsFor(that)) {
-            return false;
-        }
+        return isContentEqualsFor(that) && dependencies.equals(that.getDependencies());
 
-        return dependencies.equals(that.getDependencies());
     }
 
     @Override
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 4de8446..05099aa 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
@@ -16,67 +16,22 @@
 
 package org.gradle.api.internal.artifacts.dependencies;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ExternalModuleDependency;
 
-public class DefaultExternalModuleDependency extends AbstractExternalDependency implements ExternalModuleDependency {
-    private String group;
-    private String name;
-    private String version;
-
-    private boolean force;
-    private boolean changing;
+public class DefaultExternalModuleDependency extends AbstractExternalModuleDependency implements ExternalModuleDependency {
 
     public DefaultExternalModuleDependency(String group, String name, String version) {
         this(group, name, version, null);
     }
 
     public DefaultExternalModuleDependency(String group, String name, String version, String configuration) {
-        super(configuration);
-        if (name == null) {
-            throw new InvalidUserDataException("Name must not be null!");
-        }
-        this.group = group;
-        this.name = name;
-        this.version = version;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public boolean isForce() {
-        return force;
-    }
-
-    public DefaultExternalModuleDependency setForce(boolean force) {
-        this.force = force;
-        return this;
-    }
-
-    public boolean isChanging() {
-        return changing;
-    }
-
-    public DefaultExternalModuleDependency setChanging(boolean changing) {
-        this.changing = changing;
-        return this;
+        super(group, name, version, configuration);
     }
 
     public DefaultExternalModuleDependency copy() {
-        DefaultExternalModuleDependency copiedModuleDependency = new DefaultExternalModuleDependency(getGroup(),
-                getName(), getVersion(), getConfiguration());
+        DefaultExternalModuleDependency copiedModuleDependency = new DefaultExternalModuleDependency(getGroup(), getName(), getVersion(), getConfiguration());
         copyTo(copiedModuleDependency);
-        copiedModuleDependency.setChanging(isChanging());
         return copiedModuleDependency;
     }
 
@@ -88,12 +43,9 @@ public class DefaultExternalModuleDependency extends AbstractExternalDependency
             return false;
         }
 
-        DefaultExternalModuleDependency that = (DefaultExternalModuleDependency) dependency;
-        if (!isContentEqualsFor(that)) {
-            return false;
-        }
+        ExternalModuleDependency that = (ExternalModuleDependency) dependency;
+        return isContentEqualsFor(that);
 
-        return changing == that.isChanging();
     }
 
     @Override
@@ -116,7 +68,7 @@ public class DefaultExternalModuleDependency extends AbstractExternalDependency
 
     @Override
     public String toString() {
-        return "DefaultExternalModuleDependency{" + "group='" + group + '\'' + ", name='" + name + '\'' + ", version='"
-                + version + '\'' + ", configuration='" + getConfiguration() + '\'' + '}';
+        return String.format("DefaultExternalModuleDependency{group='%s', name='%s', version='%s', configuration='%s'}",
+                getGroup(), getName(), getVersion(), getConfiguration());
     }
 }
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 5ca739b..7461f8f 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
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts.dsl;
 
 import groovy.lang.Closure;
-import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
@@ -27,11 +26,8 @@ import org.gradle.api.internal.ConfigureByMapAction;
 import org.gradle.api.internal.artifacts.BaseRepositoryFactory;
 import org.gradle.api.internal.artifacts.DefaultArtifactRepositoryContainer;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.ConfigureUtil;
-import org.gradle.util.DeprecationLogger;
 
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import static org.gradle.util.CollectionUtils.flattenCollections;
@@ -39,7 +35,7 @@ import static org.gradle.util.CollectionUtils.flattenCollections;
 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 BINTRAY_JCENTER_URL = "https://jcenter.bintray.com/";
 
     public static final String FLAT_DIR_DEFAULT_NAME = "flatDir";
     private static final String MAVEN_REPO_DEFAULT_NAME = "maven";
@@ -48,7 +44,7 @@ public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer
     private final BaseRepositoryFactory repositoryFactory;
 
     public DefaultRepositoryHandler(BaseRepositoryFactory repositoryFactory, Instantiator instantiator) {
-        super(repositoryFactory, instantiator);
+        super(instantiator);
         this.repositoryFactory = repositoryFactory;
     }
 
@@ -82,15 +78,6 @@ public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer
 
     public MavenArtifactRepository mavenCentral(Map<String, ?> args) {
         Map<String, Object> modifiedArgs = new HashMap<String, Object>(args);
-        if (modifiedArgs.containsKey("urls")) {
-            DeprecationLogger.nagUserOfDeprecated(
-                    "The 'urls' property of the RepositoryHandler.mavenCentral() method",
-                    "You should use the 'artifactUrls' property to define additional artifact locations"
-            );
-            List<?> urls = flattenCollections(modifiedArgs.remove("urls"));
-            modifiedArgs.put("artifactUrls", urls);
-        }
-
         return addRepository(repositoryFactory.createMavenCentralRepository(), DEFAULT_MAVEN_CENTRAL_REPO_NAME, new ConfigureByMapAction<MavenArtifactRepository>(modifiedArgs));
     }
 
@@ -98,31 +85,6 @@ public class DefaultRepositoryHandler extends DefaultArtifactRepositoryContainer
         return addRepository(repositoryFactory.createMavenLocalRepository(), DEFAULT_MAVEN_LOCAL_REPO_NAME);
     }
 
-    public DependencyResolver mavenRepo(Map<String, ?> args) {
-        return mavenRepo(args, null);
-    }
-
-    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 = flattenCollections(modifiedArgs.remove("urls"));
-            if (!urls.isEmpty()) {
-                modifiedArgs.put("url", urls.get(0));
-                List<?> extraUrls = urls.subList(1, urls.size());
-                modifiedArgs.put("artifactUrls", extraUrls);
-            }
-        }
-
-        MavenArtifactRepository repository = repositoryFactory.createMavenRepository();
-        ConfigureUtil.configureByMap(modifiedArgs, repository);
-        DependencyResolver resolver = repositoryFactory.toResolver(repository);
-        ConfigureUtil.configure(configClosure, resolver);
-        addRepository(repositoryFactory.createResolverBackedRepository(resolver), "mavenRepo");
-        return resolver;
-    }
-
-
     public MavenArtifactRepository maven(Action<? super MavenArtifactRepository> action) {
         return addRepository(repositoryFactory.createMavenRepository(), MAVEN_REPO_DEFAULT_NAME, action);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ArtifactResolutionQueryFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ArtifactResolutionQueryFactory.java
deleted file mode 100644
index af9ded5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ArtifactResolutionQueryFactory.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.dependencies;
-
-import org.gradle.api.artifacts.resolution.ArtifactResolutionQuery;
-
-public interface ArtifactResolutionQueryFactory {
-    ArtifactResolutionQuery createArtifactResolutionQuery();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java
index c53f183..bb21507 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java
@@ -24,8 +24,10 @@ 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.ComponentModuleMetadataHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.resolution.ArtifactResolutionQuery;
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
+import org.gradle.api.internal.artifacts.query.ArtifactResolutionQueryFactory;
 import org.gradle.util.CollectionUtils;
 import org.gradle.util.ConfigureUtil;
 
@@ -36,16 +38,18 @@ public class DefaultDependencyHandler extends GroovyObjectSupport implements Dep
     private final ConfigurationContainer configurationContainer;
     private final DependencyFactory dependencyFactory;
     private final ProjectFinder projectFinder;
-    private final ComponentMetadataHandler metadataHandler;
+    private final ComponentMetadataHandler componentMetadataHandler;
+    private final ComponentModuleMetadataHandler componentModuleMetadataHandler;
     private final ArtifactResolutionQueryFactory resolutionQueryFactory;
 
     public DefaultDependencyHandler(ConfigurationContainer configurationContainer, DependencyFactory dependencyFactory,
-                                    ProjectFinder projectFinder, ComponentMetadataHandler metadataHandler,
+                                    ProjectFinder projectFinder, ComponentMetadataHandler componentMetadataHandler, ComponentModuleMetadataHandler componentModuleMetadataHandler,
                                     ArtifactResolutionQueryFactory resolutionQueryFactory) {
         this.configurationContainer = configurationContainer;
         this.dependencyFactory = dependencyFactory;
         this.projectFinder = projectFinder;
-        this.metadataHandler = metadataHandler;
+        this.componentMetadataHandler = componentMetadataHandler;
+        this.componentModuleMetadataHandler = componentModuleMetadataHandler;
         this.resolutionQueryFactory = resolutionQueryFactory;
     }
 
@@ -126,7 +130,15 @@ public class DefaultDependencyHandler extends GroovyObjectSupport implements Dep
     }
 
     public ComponentMetadataHandler getComponents() {
-        return metadataHandler;
+        return componentMetadataHandler;
+    }
+
+    public void modules(Action<? super ComponentModuleMetadataHandler> configureAction) {
+        configureAction.execute(getModules());
+    }
+
+    public ComponentModuleMetadataHandler getModules() {
+        return componentModuleMetadataHandler;
     }
 
     public ArtifactResolutionQuery createArtifactResolutionQuery() {
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/ModuleFactoryDelegate.groovy
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleDescriptorDelegate.groovy
rename to subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/ModuleFactoryDelegate.groovy
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/query/ArtifactResolutionQueryFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/query/ArtifactResolutionQueryFactory.java
new file mode 100644
index 0000000..7439eeb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/query/ArtifactResolutionQueryFactory.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.query;
+
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
+
+public interface ArtifactResolutionQueryFactory {
+    ArtifactResolutionQuery createArtifactResolutionQuery();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java
index 858f4d6..4dfe03b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryInternal.java
@@ -15,17 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.repositories;
 
-import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.NamedDomainObjectCollection;
 import org.gradle.api.artifacts.repositories.ArtifactRepository;
 
 public interface ArtifactRepositoryInternal extends ArtifactRepository {
 
-    /**
-     * Create a DependencyResolver implementation to use to expose this repository via the old DSL.
-     */
-    DependencyResolver createLegacyDslObject();
-
     void onAddToContainer(NamedDomainObjectCollection<ArtifactRepository> container);
 
 }
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
index eb77db5..c41bddf 100644
--- 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
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.cache;
 
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
 
 import java.io.Closeable;
 import java.io.IOException;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Cache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Cache.java
index 78b53f1..eee0410 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Cache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Cache.java
@@ -18,5 +18,5 @@ package org.gradle.api.internal.cache;
 import org.gradle.internal.Factory;
 
 public interface Cache<K, V>  {
-    <T extends K> V get(T key, Factory<? extends V> factory);
+    V get(K key, Factory<V> factory);
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheAccessSerializer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheAccessSerializer.java
index e0a87cb..f6c9de3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheAccessSerializer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheAccessSerializer.java
@@ -27,7 +27,7 @@ public class CacheAccessSerializer<K, V> implements Cache<K, V> {
         this.cache = cache;
     }
 
-    public <T extends K> V get(final T key, final Factory<? extends V> factory) {
+    public V get(final K key, final Factory<V> factory) {
         return synchronizer.synchronize(new Factory<V>() {
             public V create() {
                 return cache.get(key, factory);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheSupport.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheSupport.java
index 2dd8e0c..24ee680 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheSupport.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/CacheSupport.java
@@ -19,7 +19,7 @@ import org.gradle.internal.Factory;
 
 abstract public class CacheSupport<K, V> implements Cache<K, V> {
     
-    public <T extends K> V get(T key, Factory<? extends V> factory) {
+    public V get(K key, Factory<V> factory) {
         V value = doGet(key);
         if (value == null) {
             value = factory.create();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Loader.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Loader.java
new file mode 100644
index 0000000..f3692f5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Loader.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+/**
+ * Provides an object of given type.
+ *
+ * @param <T>
+ */
+public interface Loader<T> {
+    T get();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/MinimalPersistentCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/MinimalPersistentCache.java
new file mode 100644
index 0000000..ef39401
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/MinimalPersistentCache.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cache.*;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.internal.Factory;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.serialize.Serializer;
+
+import static org.apache.commons.lang.WordUtils.uncapitalize;
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
+import static org.gradle.util.GUtil.toCamelCase;
+
+/**
+ * Very simple cache implementation that uses Gradle's standard persistence cache mechanism.
+ * Provides synchronisation when get() method is used.
+ * Locking is extremely fine-grained, every load operation is synchronized, every store operation is synchronized.
+ * Useful as a starting point, before profiler shows that locking needs to be more coarse grained.
+ */
+public class MinimalPersistentCache<K, V> implements Cache<K, V>, Stoppable {
+
+    private final PersistentCache cacheAccess;
+    private final PersistentIndexedCache<K, V> cache;
+    private final String cacheName;
+
+    public MinimalPersistentCache(CacheRepository cacheRepository, String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
+        this.cacheName = cacheName;
+        String identifier = uncapitalize(toCamelCase(cacheName));
+        cacheAccess = cacheRepository
+                .cache(identifier)
+                .withDisplayName(cacheName + " cache")
+                .withLockOptions(mode(FileLockManager.LockMode.None))
+                .open();
+
+        PersistentIndexedCacheParameters<K, V> params =
+                new PersistentIndexedCacheParameters<K, V>(identifier, keySerializer, valueSerializer);
+        cache = cacheAccess.createCache(params);
+    }
+
+    //TODO SF if this refactoring makes sense, unit-test
+    public V get(final K key, Factory<V> factory) {
+        V cached = cacheAccess.useCache("Loading " + cacheName, new Factory<V>() {
+            public V create() {
+                return cache.get(key);
+            }
+        });
+        if (cached != null) {
+            return cached;
+        }
+
+        final V value = factory.create(); //don't synchronize value creation
+        //we could potentially avoid creating value that is already being created by a different thread.
+
+        cacheAccess.useCache("Storing " + cacheName, new Runnable() {
+            public void run() {
+                cache.put(key, value);
+            }
+        });
+        return value;
+    }
+
+    public CacheAccess getCacheAccess() {
+        return cacheAccess;
+    }
+
+    public PersistentIndexedCache<K, V> getCache() {
+        return cache;
+    }
+
+    public void stop() {
+        cacheAccess.close();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/SingleOperationPersistentStore.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/SingleOperationPersistentStore.java
new file mode 100644
index 0000000..0209304
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/SingleOperationPersistentStore.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.cache.PersistentIndexedCacheParameters;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.internal.serialize.Serializer;
+
+import static org.apache.commons.lang.WordUtils.uncapitalize;
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
+import static org.gradle.internal.serialize.BaseSerializerFactory.LONG_SERIALIZER;
+import static org.gradle.util.GUtil.toCamelCase;
+
+public class SingleOperationPersistentStore<V> {
+
+    //The cache only keeps single value, so we're always use the same index.
+    //We probably should improve our cross-process caching infrastructure so that we support Stores (e.g. not-indexed caches).
+    private final static long CACHE_KEY = 0;
+
+    private final CacheRepository cacheRepository;
+
+    private final Object scope;
+    private final String cacheName;
+    private final Serializer<V> valueSerializer;
+
+    private PersistentIndexedCache<Long, V> cache;
+    private PersistentCache cacheAccess;
+
+    public SingleOperationPersistentStore(CacheRepository cacheRepository, Object scope, String cacheName, Serializer<V> valueSerializer) {
+        this.cacheRepository = cacheRepository;
+        this.scope = scope;
+        this.cacheName = cacheName;
+        this.valueSerializer = valueSerializer;
+    }
+
+    //Opens and closes the cache for operation
+    public void putAndClose(final V value) {
+        initCaches("write");
+        try {
+            cache.put(CACHE_KEY, value);
+        } finally {
+            closeCaches();
+        }
+    }
+
+    //Opens and closes the cache for operation
+    public V getAndClose() {
+        initCaches("read");
+        try {
+            return cache.get(CACHE_KEY);
+        } finally {
+            cacheAccess.close();
+        }
+    }
+
+    private void initCaches(String operation) {
+        String identifier = uncapitalize(toCamelCase(cacheName));
+        cacheAccess = cacheRepository.store(scope, identifier)
+                .withDisplayName(cacheName + " " + operation + " cache")
+                .withLockOptions(mode(FileLockManager.LockMode.Exclusive))
+                .open();
+
+        cache = cacheAccess.createCache(new PersistentIndexedCacheParameters<Long, V>(identifier, LONG_SERIALIZER, valueSerializer));
+    }
+
+    private void closeCaches() {
+        cacheAccess.close();
+        cache = null;
+        cacheAccess = null;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Stash.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Stash.java
new file mode 100644
index 0000000..edebc01
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/cache/Stash.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+/**
+ * Stashes an object of given type.
+ *
+ * @param <T>
+ */
+public interface Stash<T> {
+    void put(T object);
+}
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
index 590f179..d824585 100644
--- 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
@@ -19,6 +19,7 @@ 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.internal.changedetection.state.FilesSnapshotSet;
 import org.gradle.api.tasks.incremental.InputFileDetails;
 
 import java.util.ArrayList;
@@ -28,7 +29,8 @@ public class ChangesOnlyIncrementalTaskInputs extends StatefulIncrementalTaskInp
     private final TaskStateChanges inputFilesState;
     private List<InputFileDetails> removedFiles = new ArrayList<InputFileDetails>();
 
-    public ChangesOnlyIncrementalTaskInputs(TaskStateChanges inputFilesState) {
+    public ChangesOnlyIncrementalTaskInputs(TaskStateChanges inputFilesState, FilesSnapshotSet inputFilesSnapshot) {
+        super(inputFilesSnapshot);
         this.inputFilesState = inputFilesState;
     }
 
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
index 4a727c9..3b48f38 100644
--- 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
@@ -87,9 +87,9 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
             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(ChangesOnlyIncrementalTaskInputs.class, getStates().getInputFilesChanges(), getStates().getInputFilesSnapshot());
             }
-            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task);
+            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task, getStates().getInputFilesSnapshot());
         }
 
         private boolean canPerformIncrementalBuild() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/IncrementalTaskInputsInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/IncrementalTaskInputsInternal.java
new file mode 100644
index 0000000..871d68d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/changes/IncrementalTaskInputsInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.changedetection.state.FilesSnapshotSet;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+
+public interface IncrementalTaskInputsInternal extends IncrementalTaskInputs {
+    FilesSnapshotSet getInputFilesSnapshot();
+}
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
index e227766..108d493 100644
--- 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
@@ -18,6 +18,7 @@ package org.gradle.api.internal.changedetection.changes;
 
 import org.gradle.api.Action;
 import org.gradle.api.Task;
+import org.gradle.api.internal.changedetection.state.FilesSnapshotSet;
 import org.gradle.api.tasks.incremental.InputFileDetails;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,7 +30,8 @@ public class RebuildIncrementalTaskInputs extends StatefulIncrementalTaskInputs
 
     private final Task task;
 
-    public RebuildIncrementalTaskInputs(Task task) {
+    public RebuildIncrementalTaskInputs(Task task, FilesSnapshotSet inputFilesSnapshot) {
+        super(inputFilesSnapshot);
         LOGGER.info("All input files are considered out-of-date for incremental {}.", task);
         this.task = task;
     }
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
index 9bb3ad8..312cd89 100755
--- 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
@@ -20,6 +20,7 @@ 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.state.FilesSnapshotSet;
 import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
 import org.gradle.internal.reflect.Instantiator;
 
@@ -73,7 +74,7 @@ public class ShortCircuitTaskArtifactStateRepository implements TaskArtifactStat
         }
 
         public IncrementalTaskInputs getInputChanges() {
-            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task);
+            return instantiator.newInstance(RebuildIncrementalTaskInputs.class, task, FilesSnapshotSet.EMPTY);
         }
 
         public TaskExecutionHistory getExecutionHistory() {
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
index 35bca19..a819398 100644
--- 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
@@ -17,13 +17,22 @@
 package org.gradle.api.internal.changedetection.changes;
 
 import org.gradle.api.Action;
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.internal.changedetection.state.FilesSnapshotSet;
 import org.gradle.api.tasks.incremental.InputFileDetails;
 
-abstract class StatefulIncrementalTaskInputs implements IncrementalTaskInputs {
+abstract class StatefulIncrementalTaskInputs implements IncrementalTaskInputsInternal {
+    private final FilesSnapshotSet inputFilesSnapshot;
     private boolean outOfDateProcessed;
     private boolean removedProcessed;
 
+    protected StatefulIncrementalTaskInputs(FilesSnapshotSet inputFilesSnapshot) {
+        this.inputFilesSnapshot = inputFilesSnapshot;
+    }
+
+    public FilesSnapshotSet getInputFilesSnapshot() {
+        return inputFilesSnapshot;
+    }
+
     public void outOfDate(final Action<? super InputFileDetails> outOfDateAction) {
         if (outOfDateProcessed) {
             throw new IllegalStateException("Cannot process outOfDate files multiple times");
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
index 648a599..791e771 100644
--- 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
@@ -16,9 +16,7 @@
 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.FileCollectionSnapshotter;
 import org.gradle.api.internal.changedetection.state.TaskExecution;
 import org.gradle.util.ChangeListener;
 
@@ -29,11 +27,8 @@ 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 FileCollectionSnapshotter inputFilesSnapshotter) {
-        final FileCollectionSnapshot inputFilesSnapshot = inputFilesSnapshotter.snapshot(task.getInputs().getFiles());
-
+    public static TaskStateChanges create(final TaskExecution previousExecution, final TaskExecution currentExecution, final FileCollectionSnapshot inputFilesSnapshot) {
         return new TaskStateChanges() {
-
             public Iterator<TaskStateChange> iterator() {
                 if (previousExecution.getInputFilesSnapshot() == null) {
                     return Collections.<TaskStateChange>singleton(new DescriptiveChange("Input file history is not available.")).iterator();
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
index 7cbd09b..8936002 100644
--- 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
@@ -16,16 +16,16 @@
 
 package org.gradle.api.internal.changedetection.rules;
 
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.state.FileCollectionSnapshotter;
-import org.gradle.api.internal.changedetection.state.TaskExecution;
-import org.gradle.api.internal.changedetection.state.TaskHistoryRepository;
+import org.gradle.api.internal.changedetection.state.*;
 
 /**
  * Represents the complete changes in a tasks state
  */
 public class TaskUpToDateState {
     private static final int MAX_OUT_OF_DATE_MESSAGES = 3;
+    private final FilesSnapshotSet inputFilesSnapshot;
 
     private TaskStateChanges noHistoryState;
     private TaskStateChanges inputFilesState;
@@ -42,8 +42,23 @@ public class TaskUpToDateState {
         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));
+
+        // Capture outputs state
+        try {
+            outputFilesState = caching(OutputFilesStateChangeRule.create(task, lastExecution, thisExecution, outputFilesSnapshotter));
+        } catch (UncheckedIOException e) {
+            throw new UncheckedIOException(String.format("Failed to capture snapshot of output files for task '%s' during up-to-date check.  See stacktrace for details.", task.getName()), e);
+        }
+
+        // Capture inputs state
+        try {
+            FileCollectionSnapshot inputFilesSnapshot = inputFilesSnapshotter.snapshot(task.getInputs().getFiles());
+            this.inputFilesSnapshot = inputFilesSnapshot.getSnapshot();
+            inputFilesState = caching(InputFilesStateChangeRule.create(lastExecution, thisExecution, inputFilesSnapshot));
+        } catch (UncheckedIOException e) {
+            throw new UncheckedIOException(String.format("Failed to capture snapshot of input files for task '%s' during up-to-date check.  See stacktrace for details.", task.getName()), e);
+        }
+
         allTaskChanges = new SummaryTaskStateChanges(MAX_OUT_OF_DATE_MESSAGES, noHistoryState, taskTypeState, inputPropertiesState, outputFilesState, inputFilesState);
         rebuildChanges = new SummaryTaskStateChanges(1, noHistoryState, taskTypeState, inputPropertiesState, outputFilesState);
     }
@@ -63,4 +78,8 @@ public class TaskUpToDateState {
     public TaskStateChanges getRebuildChanges() {
         return rebuildChanges;
     }
+
+    public FilesSnapshotSet getInputFilesSnapshot() {
+        return inputFilesSnapshot;
+    }
 }
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
index 7b146f9..e32a6a7 100644
--- 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
@@ -18,7 +18,7 @@ package org.gradle.api.internal.changedetection.state;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.id.RandomLongIdGenerator;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 
 public class CacheBackedFileSnapshotRepository implements FileSnapshotRepository {
     private final PersistentIndexedCache<Long, FileCollectionSnapshot> cache;
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
index dd6795a..7af01a5 100644
--- 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
@@ -18,10 +18,9 @@ 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.Decoder;
-import org.gradle.messaging.serialize.DefaultSerializer;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.io.File;
 import java.util.*;
@@ -229,10 +228,10 @@ public class CacheBackedTaskHistoryRepository implements TaskHistoryRepository {
         }
 
         static class TaskHistorySerializer implements Serializer<LazyTaskExecution> {
-            private ClassLoader classLoader;
+            private InputPropertiesSerializer inputPropertiesSerializer;
 
             public TaskHistorySerializer(ClassLoader classLoader) {
-                this.classLoader = classLoader;
+                this.inputPropertiesSerializer = new InputPropertiesSerializer(classLoader);
             }
 
             public LazyTaskExecution read(Decoder decoder) throws Exception {
@@ -249,8 +248,7 @@ public class CacheBackedTaskHistoryRepository implements TaskHistoryRepository {
 
                 boolean inputProperties = decoder.readBoolean();
                 if (inputProperties) {
-                    DefaultSerializer<Map> defaultSerializer = new DefaultSerializer<Map>(classLoader);
-                    Map<String, Object> map = defaultSerializer.read(decoder);
+                    Map<String, Object> map = inputPropertiesSerializer.read(decoder);
                     execution.setInputProperties(map);
                 } else {
                     execution.setInputProperties(new HashMap<String, Object>());
@@ -270,8 +268,7 @@ public class CacheBackedTaskHistoryRepository implements TaskHistoryRepository {
                     encoder.writeBoolean(false);
                 } else {
                     encoder.writeBoolean(true);
-                    DefaultSerializer<Map> defaultSerializer = new DefaultSerializer<Map>(classLoader);
-                    defaultSerializer.write(encoder, execution.getInputProperties());
+                    inputPropertiesSerializer.write(encoder, execution.getInputProperties());
                 }
             }
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingFileSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingFileSnapshotter.java
index c258f39..ff44dae 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingFileSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/CachingFileSnapshotter.java
@@ -18,10 +18,9 @@ package org.gradle.api.internal.changedetection.state;
 import org.gradle.api.internal.hash.Hasher;
 import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.cache.PersistentStore;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.messaging.serialize.SerializerRegistry;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.io.File;
 
@@ -35,10 +34,6 @@ public class CachingFileSnapshotter implements FileSnapshotter {
         this.cache = store.createCache("fileHashes", File.class, serializer);
     }
 
-    public void registerSerializers(SerializerRegistry<FileSnapshot> registry) {
-        registry.register(FileInfo.class, serializer);
-    }
-
     public FileInfo snapshot(File file) {
         FileInfo info = cache.get(file);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotter.java
index d6442ad..8fa6895 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotter.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.changedetection.state;
 
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.messaging.serialize.SerializerRegistry;
+import org.gradle.internal.serialize.SerializerRegistry;
 import org.gradle.util.ChangeListener;
 import org.gradle.util.NoOpChangeListener;
 
@@ -40,15 +40,18 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
     }
 
     public FileCollectionSnapshot emptySnapshot() {
-        return new FileCollectionSnapshotImpl(new HashMap<String, FileSnapshot>());
+        return new FileCollectionSnapshotImpl(new HashMap<String, IncrementalFileSnapshot>());
     }
 
-    public FileCollectionSnapshot snapshot(FileCollection sourceFiles) {
-        final Map<String, FileSnapshot> snapshots = new HashMap<String, FileSnapshot>();
-        final Set<File> theFiles = sourceFiles.getAsFileTree().getFiles();
+    public FileCollectionSnapshot snapshot(FileCollection input) {
+        final Set<File> files = input.getAsFileTree().getFiles();
+        if (files.isEmpty()) {
+            return new FileCollectionSnapshotImpl(Collections.<String, IncrementalFileSnapshot>emptyMap());
+        }
+        final Map<String, IncrementalFileSnapshot> snapshots = new HashMap<String, IncrementalFileSnapshot>();
         cacheAccess.useCache("Create file snapshot", new Runnable() {
             public void run() {
-                for (File file : theFiles) {
+                for (File file : files) {
                     if (file.isFile()) {
                         snapshots.put(file.getAbsolutePath(), new FileHashSnapshot(snapshotter.snapshot(file).getHash()));
                     } else if (file.isDirectory()) {
@@ -62,18 +65,18 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
         return new FileCollectionSnapshotImpl(snapshots);
     }
 
-    static interface FileSnapshot {
-        boolean isUpToDate(FileSnapshot snapshot);
+    static interface IncrementalFileSnapshot {
+        boolean isUpToDate(IncrementalFileSnapshot snapshot);
     }
 
-    static class FileHashSnapshot implements FileSnapshot {
+    static class FileHashSnapshot implements IncrementalFileSnapshot, FileSnapshot {
         final byte[] hash;
 
         public FileHashSnapshot(byte[] hash) {
             this.hash = hash;
         }
 
-        public boolean isUpToDate(FileSnapshot snapshot) {
+        public boolean isUpToDate(IncrementalFileSnapshot snapshot) {
             if (!(snapshot instanceof FileHashSnapshot)) {
                 return false;
             }
@@ -86,30 +89,34 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
         public String toString() {
             return new BigInteger(1, hash).toString(16);
         }
+
+        public byte[] getHash() {
+            return hash;
+        }
     }
 
-    static class DirSnapshot implements FileSnapshot {
-        public boolean isUpToDate(FileSnapshot snapshot) {
+    static class DirSnapshot implements IncrementalFileSnapshot {
+        public boolean isUpToDate(IncrementalFileSnapshot snapshot) {
             return snapshot instanceof DirSnapshot;
         }
     }
 
-    static class MissingFileSnapshot implements FileSnapshot {
-        public boolean isUpToDate(FileSnapshot snapshot) {
+    static class MissingFileSnapshot implements IncrementalFileSnapshot {
+        public boolean isUpToDate(IncrementalFileSnapshot snapshot) {
             return snapshot instanceof MissingFileSnapshot;
         }
     }
 
     static class FileCollectionSnapshotImpl implements FileCollectionSnapshot {
-        final Map<String, FileSnapshot> snapshots;
+        final Map<String, IncrementalFileSnapshot> snapshots;
 
-        public FileCollectionSnapshotImpl(Map<String, FileSnapshot> snapshots) {
+        public FileCollectionSnapshotImpl(Map<String, IncrementalFileSnapshot> snapshots) {
             this.snapshots = snapshots;
         }
 
         public FileCollection getFiles() {
             List<File> files = new ArrayList<File>();
-            for (Map.Entry<String, FileSnapshot> entry : snapshots.entrySet()) {
+            for (Map.Entry<String, IncrementalFileSnapshot> entry : snapshots.entrySet()) {
                 if (entry.getValue() instanceof FileHashSnapshot) {
                     files.add(new File(entry.getKey()));
                 }
@@ -117,9 +124,21 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
             return new SimpleFileCollection(files);
         }
 
+        public FilesSnapshotSet getSnapshot() {
+            return new FilesSnapshotSet() {
+                public FileSnapshot findSnapshot(File file) {
+                    IncrementalFileSnapshot s = snapshots.get(file.getAbsolutePath());
+                    if (s instanceof FileSnapshot) {
+                        return (FileSnapshot) s;
+                    }
+                    return null;
+                }
+            };
+        }
+
         public ChangeIterator<String> iterateChangesSince(FileCollectionSnapshot oldSnapshot) {
             FileCollectionSnapshotImpl other = (FileCollectionSnapshotImpl) oldSnapshot;
-            final Map<String, FileSnapshot> otherSnapshots = new HashMap<String, FileSnapshot>(other.snapshots);
+            final Map<String, IncrementalFileSnapshot> otherSnapshots = new HashMap<String, IncrementalFileSnapshot>(other.snapshots);
             final Iterator<String> currentFiles = snapshots.keySet().iterator();
 
             return new ChangeIterator<String>() {
@@ -128,7 +147,7 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
                 public boolean next(ChangeListener<String> listener) {
                     while (currentFiles.hasNext()) {
                         String currentFile = currentFiles.next();
-                        FileSnapshot otherFile = otherSnapshots.remove(currentFile);
+                        IncrementalFileSnapshot otherFile = otherSnapshots.remove(currentFile);
 
                         if (otherFile == null) {
                             listener.added(currentFile);
@@ -163,25 +182,25 @@ public class DefaultFileCollectionSnapshotter implements FileCollectionSnapshott
 
                 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));
+                    final Map<String, IncrementalFileSnapshot> newSnapshots = new HashMap<String, IncrementalFileSnapshot>(target.snapshots);
+                    diff(snapshots, other.snapshots, new MapMergeChangeListener<String, IncrementalFileSnapshot>(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());
+        private void diff(Map<String, IncrementalFileSnapshot> snapshots, Map<String, IncrementalFileSnapshot> oldSnapshots,
+                          ChangeListener<Map.Entry<String, IncrementalFileSnapshot>> listener) {
+            Map<String, IncrementalFileSnapshot> otherSnapshots = new HashMap<String, IncrementalFileSnapshot>(oldSnapshots);
+            for (Map.Entry<String, IncrementalFileSnapshot> entry : snapshots.entrySet()) {
+                IncrementalFileSnapshot 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()) {
+            for (Map.Entry<String, IncrementalFileSnapshot> 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
index aa69ce1..744156f 100644
--- 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
@@ -16,16 +16,16 @@
 
 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 org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.util.HashMap;
 import java.util.Map;
 
 class DefaultFileSnapshotterSerializer implements Serializer<DefaultFileCollectionSnapshotter.FileCollectionSnapshotImpl> {
     public DefaultFileCollectionSnapshotter.FileCollectionSnapshotImpl read(Decoder decoder) throws Exception {
-        Map<String, DefaultFileCollectionSnapshotter.FileSnapshot> snapshots = new HashMap<String, DefaultFileCollectionSnapshotter.FileSnapshot>();
+        Map<String, DefaultFileCollectionSnapshotter.IncrementalFileSnapshot> snapshots = new HashMap<String, DefaultFileCollectionSnapshotter.IncrementalFileSnapshot>();
         DefaultFileCollectionSnapshotter.FileCollectionSnapshotImpl snapshot = new DefaultFileCollectionSnapshotter.FileCollectionSnapshotImpl(snapshots);
         int snapshotsCount = decoder.readSmallInt();
         for (int i = 0; i < snapshotsCount; i++) {
@@ -51,14 +51,14 @@ class DefaultFileSnapshotterSerializer implements Serializer<DefaultFileCollecti
         encoder.writeSmallInt(value.snapshots.size());
         for (String key : value.snapshots.keySet()) {
             encoder.writeString(key);
-            DefaultFileCollectionSnapshotter.FileSnapshot fileSnapshot = value.snapshots.get(key);
-            if (fileSnapshot instanceof DefaultFileCollectionSnapshotter.DirSnapshot) {
+            DefaultFileCollectionSnapshotter.IncrementalFileSnapshot incrementalFileSnapshot = value.snapshots.get(key);
+            if (incrementalFileSnapshot instanceof DefaultFileCollectionSnapshotter.DirSnapshot) {
                 encoder.writeByte((byte) 1);
-            } else if (fileSnapshot instanceof DefaultFileCollectionSnapshotter.MissingFileSnapshot) {
+            } else if (incrementalFileSnapshot instanceof DefaultFileCollectionSnapshotter.MissingFileSnapshot) {
                 encoder.writeByte((byte) 2);
-            } else if (fileSnapshot instanceof DefaultFileCollectionSnapshotter.FileHashSnapshot) {
+            } else if (incrementalFileSnapshot instanceof DefaultFileCollectionSnapshotter.FileHashSnapshot) {
                 encoder.writeByte((byte) 3);
-                byte[] hash = ((DefaultFileCollectionSnapshotter.FileHashSnapshot) fileSnapshot).hash;
+                byte[] hash = ((DefaultFileCollectionSnapshotter.FileHashSnapshot) incrementalFileSnapshot).hash;
                 encoder.writeByte((byte) hash.length);
                 encoder.writeBytes(hash);
             }
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
index 8e6845e..12d4ec5 100644
--- 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
@@ -23,11 +23,13 @@ import org.gradle.cache.PersistentIndexedCacheParameters;
 import org.gradle.cache.internal.CacheDecorator;
 import org.gradle.cache.internal.FileLockManager;
 import org.gradle.internal.Factory;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.Closeable;
 
 import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
 
-public class DefaultTaskArtifactStateCacheAccess implements TaskArtifactStateCacheAccess {
+public class DefaultTaskArtifactStateCacheAccess implements TaskArtifactStateCacheAccess, Closeable {
     private final CacheDecorator inMemoryDecorator;
     private final PersistentCache cache;
 
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
index e5bbf38..480e42c 100755
--- 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
@@ -30,6 +30,8 @@ public interface FileCollectionSnapshot {
 
     FileCollection getFiles();
 
+    FilesSnapshotSet getSnapshot();
+
     public interface Diff {
         /**
          * Applies this diff to the given snapshot. Adds any added or changed files in this diff to the given snapshot.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshotter.java
index e7e023e..1b485be 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileCollectionSnapshotter.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.changedetection.state;
 
 import org.gradle.api.file.FileCollection;
-import org.gradle.messaging.serialize.SerializerRegistry;
+import org.gradle.internal.serialize.SerializerRegistry;
 
 public interface FileCollectionSnapshotter {
     /**
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshot.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshot.java
new file mode 100644
index 0000000..690faa0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FileSnapshot.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 FileSnapshot {
+    byte[] getHash();
+}
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
index 43e709e..b785754 100644
--- 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
@@ -16,22 +16,12 @@
 
 package org.gradle.api.internal.changedetection.state;
 
-import org.gradle.messaging.serialize.SerializerRegistry;
-
 import java.io.File;
 
 public interface FileSnapshotter {
     /**
-     * Registers Serializers to use to persist the {@link FileSnapshot} instances that this snapshotter produces.
-     */
-    void registerSerializers(SerializerRegistry<FileSnapshot> registry);
-
-    /**
      * Takes a snapshot of the current content of the given file. The provided file must exist and be a file (rather than, say, a directory).
      */
     FileSnapshot snapshot(File file);
 
-    interface FileSnapshot {
-        byte[] getHash();
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FilesSnapshotSet.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FilesSnapshotSet.java
new file mode 100644
index 0000000..245ab49
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/FilesSnapshotSet.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+
+import java.io.File;
+
+public interface FilesSnapshotSet {
+
+    /**
+     * Provides snapshot of a given file. null if it does not contain information about the snapshot of given input file.
+     * @param file input
+     * @return file snapshot for given input or null.
+     */
+    @Nullable FileSnapshot findSnapshot(File file);
+
+    static FilesSnapshotSet EMPTY = new FilesSnapshotSet() {
+        public FileSnapshot findSnapshot(File file) {
+            return null;
+        }
+    };
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializer.java
new file mode 100644
index 0000000..991bac5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.GradleException;
+import org.gradle.internal.serialize.*;
+
+import java.util.Map;
+
+import static java.lang.String.format;
+
+class InputPropertiesSerializer implements Serializer<Map<String, Object>> {
+
+    private final MapSerializer<String, Object> serializer;
+
+    InputPropertiesSerializer(ClassLoader classloader) {
+        this.serializer = new MapSerializer<String, Object>(BaseSerializerFactory.STRING_SERIALIZER, new DefaultSerializer<Object>(classloader));
+    }
+
+    public Map<String, Object> read(Decoder decoder) throws Exception {
+        return serializer.read(decoder);
+    }
+
+    public void write(Encoder encoder, Map<String, Object> properties) throws Exception {
+        try {
+            serializer.write(encoder, properties);
+        } catch (MapSerializer.EntrySerializationException e) {
+            throw new GradleException(format("Unable to store task input properties. Property '%s' with value '%s' cannot be serialized.", e.getKey(), e.getValue()), e);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesCollectionSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesCollectionSnapshotter.java
index 491d012..7452536 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesCollectionSnapshotter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/changedetection/state/OutputFilesCollectionSnapshotter.java
@@ -19,9 +19,9 @@ 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.DefaultSerializerRegistry;
-import org.gradle.messaging.serialize.LongSerializer;
-import org.gradle.messaging.serialize.SerializerRegistry;
+import org.gradle.internal.serialize.DefaultSerializerRegistry;
+import org.gradle.internal.serialize.LongSerializer;
+import org.gradle.internal.serialize.SerializerRegistry;
 import org.gradle.util.ChangeListener;
 import org.gradle.util.DiffUtil;
 import org.gradle.util.NoOpChangeListener;
@@ -102,6 +102,10 @@ public class OutputFilesCollectionSnapshotter implements FileCollectionSnapshott
             return filesSnapshot.getFiles();
         }
 
+        public FilesSnapshotSet getSnapshot() {
+            return filesSnapshot.getSnapshot();
+        }
+
         public Diff changesSince(final FileCollectionSnapshot oldSnapshot) {
             OutputFilesSnapshot other = (OutputFilesSnapshot) oldSnapshot;
             return new OutputFilesDiff(rootFileIds, other.rootFileIds, filesSnapshot.changesSince(other.filesSnapshot));
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
index faad603..52857c7 100644
--- 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
@@ -16,9 +16,9 @@
 
 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 org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.util.HashMap;
 import java.util.Map;
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 9edcde8..d15925c 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
@@ -23,6 +23,7 @@ import org.gradle.internal.classloader.ClasspathUtil;
 import org.gradle.util.GUtil;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
@@ -44,6 +45,12 @@ import java.util.zip.ZipFile;
  * Determines the classpath for a module by looking for a '${module}-classpath.properties' resource with 'name' set to the name of the module.
  */
 public class DefaultModuleRegistry implements ModuleRegistry, GradleDistributionLocator {
+    public static final FileFilter DIRECTORY_FILTER = new FileFilter() {
+        public boolean accept(File pathname) {
+            return pathname.isDirectory();
+        }
+    };
+
     private final ClassLoader classLoader;
     private final File distDir;
     private final Map<String, Module> modules = new HashMap<String, Module>();
@@ -66,8 +73,20 @@ public class DefaultModuleRegistry implements ModuleRegistry, GradleDistribution
         }
 
         if (distDir != null) {
-            libDirs.add(new File(distDir, "lib"));
-            libDirs.add(new File(distDir, "lib/plugins"));
+            libDirs.addAll(findLibDirs(distDir));
+        }
+    }
+
+    private List<File> findLibDirs(File distDir) {
+        List<File> libDirAndSubdirs = new ArrayList<File>();
+        collectWithSubdirectories(new File(distDir, "lib"), libDirAndSubdirs);
+        return libDirAndSubdirs;
+    }
+
+    private void collectWithSubdirectories(File root, Collection<File> collection) {
+        collection.add(root);
+        for (File subdirectory : root.listFiles(DIRECTORY_FILTER)) {
+            collectWithSubdirectories(subdirectory, collection);
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/PropertySetTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/PropertySetTransformer.java
new file mode 100644
index 0000000..b923dfc
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/PropertySetTransformer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 groovy.lang.MetaProperty;
+
+public interface PropertySetTransformer {
+
+    Object transformValue(Object target, MetaProperty property, Object value);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/StringToEnumTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/StringToEnumTransformer.java
new file mode 100644
index 0000000..886d553
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/StringToEnumTransformer.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.coerce;
+
+import groovy.lang.MetaProperty;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.typeconversion.EnumFromCharSequenceNotationParser;
+
+import java.lang.reflect.Method;
+
+public class StringToEnumTransformer implements MethodArgumentsTransformer, PropertySetTransformer {
+
+    public static final StringToEnumTransformer INSTANCE = new StringToEnumTransformer();
+
+    public Object[] transform(Object target, final String methodName, Object... args) {
+        if (args.length != 1 || !(args[0] instanceof CharSequence)) {
+            return args;
+        }
+
+        final CharSequence charSequenceArg = (CharSequence) args[0];
+
+        Method enumMethod = JavaReflectionUtil.findMethod(target.getClass(), new Spec<Method>() {
+            @Override
+            public boolean isSatisfiedBy(Method method) {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (method.getName().equals(methodName) && parameterTypes.length == 1) {
+                    Class<?> parameterType = parameterTypes[0];
+
+                    if (parameterType.isEnum()) {
+                        return true; // stop searching
+                    }
+                }
+
+                return false;
+            }
+        });
+
+        if (enumMethod == null) {
+            return args;
+        } else {
+            @SuppressWarnings("unchecked")
+            Class<? extends Enum> enumType = (Class<? extends Enum>) enumMethod.getParameterTypes()[0];
+            return new Object[]{toEnumValue(enumType, charSequenceArg)};
+        }
+    }
+
+    @Override
+    public Object transformValue(Object target, MetaProperty property, Object value) {
+        if (value instanceof CharSequence && property.getType().isEnum()) {
+            @SuppressWarnings("unchecked") Class<? extends Enum> enumType = (Class<? extends Enum>) property.getType();
+            final String setterName = MetaProperty.getSetterName(property.getName());
+            Method setter = JavaReflectionUtil.findMethod(target.getClass(), new Spec<Method>() {
+                @Override
+                public boolean isSatisfiedBy(Method element) {
+                    return element.getName().equals(setterName) && element.getParameterTypes().length == 1;
+                }
+            });
+
+            if (setter == null || setter.getParameterTypes()[0].equals(enumType)) {
+                return toEnumValue(enumType, (CharSequence) value);
+            }
+        }
+
+        return value;
+    }
+
+    static public <T extends Enum<T>> T toEnumValue(Class<T> enumType, CharSequence charSequence) {
+        EnumFromCharSequenceNotationParser<T> notationParser = new EnumFromCharSequenceNotationParser<T>(enumType);
+        return notationParser.parseNotation(charSequence);
+    }
+}
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
deleted file mode 100644
index 344bfbe..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformer.java
+++ /dev/null
@@ -1,91 +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.coerce;
-
-import org.gradle.api.Transformer;
-import org.gradle.internal.typeconversion.EnumFromCharSequenceNotationParser;
-import org.gradle.internal.reflect.JavaReflectionUtil;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-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)};
-    }
-
-    public <T extends Enum<T>> T toEnumValue(Class<T> enumType, CharSequence charSequence) {
-        EnumFromCharSequenceNotationParser<T> notationParser = new EnumFromCharSequenceNotationParser<T>(enumType);
-        return notationParser.parseNotation(charSequence);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/collections/CollectionFilter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/collections/CollectionFilter.java
index dbb1aa4..a13035b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/collections/CollectionFilter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/collections/CollectionFilter.java
@@ -55,4 +55,9 @@ public class CollectionFilter<T> implements Spec<T> {
     public boolean isSatisfiedBy(T element) {
         return filter(element) != null;
     }
+
+    @SuppressWarnings("unchecked")
+    public <S extends T> CollectionFilter<S> and(CollectionFilter<S> other) {
+        return new CollectionFilter<S>(other.type, Specs.and(spec, other.spec));
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ArtifactType.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ArtifactType.java
new file mode 100644
index 0000000..3f99261
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ArtifactType.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.api.internal.component;
+
+import org.gradle.util.GUtil;
+
+public enum ArtifactType {
+    SOURCES, JAVADOC, IVY_DESCRIPTOR, MAVEN_POM;
+
+    public String toString() {
+        return String.format("'%s' artifacts", GUtil.toWords(name()));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/BuildableJavaComponent.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/BuildableJavaComponent.java
new file mode 100644
index 0000000..343614c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/BuildableJavaComponent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.component;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.file.FileCollection;
+
+import java.util.Collection;
+
+/**
+ * Meta-info about a Java component.
+ *
+ * TODO - this is some legacy stuff, to be merged into other component interfaces
+ */
+public interface BuildableJavaComponent {
+    Collection<String> getRebuildTasks();
+
+    Collection<String> getBuildTasks();
+
+    FileCollection getRuntimeClasspath();
+
+    Configuration getCompileDependencies();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentRegistry.java
new file mode 100644
index 0000000..e98188b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentRegistry.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.component;
+
+/**
+ * TODO - merge this and the component container
+ */
+public class ComponentRegistry {
+    private BuildableJavaComponent mainComponent;
+
+    public BuildableJavaComponent getMainComponent() {
+        return mainComponent;
+    }
+
+    public void setMainComponent(BuildableJavaComponent mainComponent) {
+        this.mainComponent = mainComponent;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistration.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistration.java
new file mode 100644
index 0000000..0e4627d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistration.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.component;
+
+import org.gradle.api.component.Artifact;
+
+public interface ComponentTypeRegistration {
+    ArtifactType getArtifactType(Class<? extends Artifact> artifact);
+
+    ComponentTypeRegistration registerArtifactType(Class<? extends Artifact> artifact, ArtifactType artifactType);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistry.java
new file mode 100644
index 0000000..102f6a3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/ComponentTypeRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.component;
+
+import org.gradle.api.component.Component;
+
+public interface ComponentTypeRegistry {
+    ComponentTypeRegistration maybeRegisterComponentType(Class<? extends Component> componentType);
+
+    ComponentTypeRegistration getComponentRegistration(Class<? extends Component> componentType);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistry.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistry.java
new file mode 100644
index 0000000..5421fe3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistry.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.component;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.component.Artifact;
+import org.gradle.api.component.Component;
+
+import java.util.Map;
+
+public class DefaultComponentTypeRegistry implements ComponentTypeRegistry {
+    private final Map<Class<? extends Component>, ComponentTypeRegistration> componentRegistrations = Maps.newHashMap();
+
+    public ComponentTypeRegistration maybeRegisterComponentType(Class<? extends Component> componentType) {
+        ComponentTypeRegistration registration = componentRegistrations.get(componentType);
+        if (registration == null) {
+            registration = new DefaultComponentTypeRegistration(componentType);
+            componentRegistrations.put(componentType, registration);
+        }
+        return registration;
+    }
+
+    public ComponentTypeRegistration getComponentRegistration(Class<? extends Component> componentType) {
+        ComponentTypeRegistration registration = componentRegistrations.get(componentType);
+        if (registration == null) {
+            throw new IllegalArgumentException(String.format("Not a registered component type: %s.", componentType.getName()));
+        }
+        return registration;
+    }
+
+    private static class DefaultComponentTypeRegistration implements ComponentTypeRegistration {
+        private final Class<? extends Component> componentType;
+        private final Map<Class<? extends Artifact>, ArtifactType> typeRegistrations = Maps.newHashMap();
+
+        private DefaultComponentTypeRegistration(Class<? extends Component> componentType) {
+            this.componentType = componentType;
+        }
+
+        public ArtifactType getArtifactType(Class<? extends Artifact> artifact) {
+            ArtifactType type = typeRegistrations.get(artifact);
+            if (type == null) {
+                throw new IllegalArgumentException(String.format("Artifact type %s is not registered for component type %s.", artifact.getName(), componentType.getName()));
+            }
+            return type;
+        }
+
+        public ComponentTypeRegistration registerArtifactType(Class<? extends Artifact> artifact, ArtifactType artifactType) {
+            if (typeRegistrations.containsKey(artifact)) {
+                throw new IllegalStateException(String.format("Artifact type %s is already registered for component type %s.", artifact.getName(), componentType.getName()));
+            }
+            typeRegistrations.put(artifact, artifactType);
+            return this;
+        }
+    }
+}
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 e793a5f..fad52f4 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
@@ -22,30 +22,32 @@ import org.gradle.api.UncheckedIOException;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
 import org.gradle.api.resources.ReadableResource;
 import org.gradle.internal.Factory;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.UnsupportedNotationException;
 import org.gradle.util.CollectionUtils;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.regex.Pattern;
 
 public abstract class AbstractFileResolver implements FileResolver {
+    private static final Pattern FILE_SEPARATOR_PATTERN = Pattern.compile("[/" + Pattern.quote(File.separator) + "]");
+
     private final FileSystem fileSystem;
-    private FileOrUriNotationParser fileNotationParser;
+    private final NotationParser<Object, Object> fileNotationParser;
 
     protected AbstractFileResolver(FileSystem fileSystem) {
         this.fileSystem = fileSystem;
-        this.fileNotationParser = new FileOrUriNotationParser(fileSystem);
+        this.fileNotationParser = FileOrUriNotationConverter.parser(fileSystem);
     }
 
     public FileSystem getFileSystem() {
@@ -67,8 +69,9 @@ public abstract class AbstractFileResolver implements FileResolver {
                 return resolve(notation, PathValidation.NONE);
             }
 
-            public void describe(Collection<String> candidateFormats) {
-                candidateFormats.add("Anything that can be converted to a file, as per Project.file()");
+            @Override
+            public void describe(DiagnosticsVisitor visitor) {
+                visitor.candidate("Anything that can be converted to a file, as per Project.file()");
             }
         };
     }
@@ -93,8 +96,7 @@ public abstract class AbstractFileResolver implements FileResolver {
                 // on Windows, File.getCanonicalFile() doesn't resolve symlinks
                 return file.getCanonicalFile();
             }
-
-            String[] segments = file.getPath().split(String.format("[/%s]", Pattern.quote(File.separator)));
+            String[] segments = FILE_SEPARATOR_PATTERN.split(file.getPath());
             List<String> path = new ArrayList<String>(segments.length);
             for (String segment : segments) {
                 if (segment.equals("..")) {
@@ -186,7 +188,6 @@ public abstract class AbstractFileResolver implements FileResolver {
             return (File) converted;
         }
         throw new InvalidUserDataException(String.format("Cannot convert URL '%s' to a file.", converted));
-
     }
 
     private Object unpack(Object path) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTreeElement.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTreeElement.java
index 242ce8d..f9c12f3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTreeElement.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/AbstractFileTreeElement.java
@@ -19,8 +19,8 @@ import org.apache.commons.io.IOUtils;
 import org.gradle.api.GradleException;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.file.FileTreeElement;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.util.GFileUtils;
 
 import java.io.*;
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 e67d58c..faf908e 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
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.file;
 
 import org.apache.commons.lang.StringUtils;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.util.CollectionUtils;
 import org.gradle.util.GUtil;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BasicFileResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BasicFileResolver.java
new file mode 100644
index 0000000..e59319b
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/BasicFileResolver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Transformer;
+import org.gradle.internal.UncheckedException;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.regex.Pattern;
+
+/**
+ * A minimal resolver, which does not use any native services. Used during bootstrap only. You should generally use {@link FileResolver} instead.
+ *
+ * TODO - share more stuff with AbstractFileResolver.
+ */
+public class BasicFileResolver implements Transformer<File, String> {
+    private static final Pattern URI_SCHEME = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-\\.]*:.+");
+    private final File baseDir;
+
+    public BasicFileResolver(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    public File transform(String original) {
+        if (original.startsWith("file:")) {
+            try {
+                return GFileUtils.canonicalise(new File(new URI(original)));
+            } catch (URISyntaxException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        File file = new File(original);
+        if (file.isAbsolute()) {
+            return GFileUtils.canonicalise(file);
+        }
+
+        if (URI_SCHEME.matcher(original).matches()) {
+            throw new InvalidUserDataException(String.format("Cannot convert URL '%s' to a file.", original));
+        }
+
+        file = new File(baseDir, original);
+        return GFileUtils.canonicalise(file);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileLookup.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileLookup.java
index f781b7e..55a9a31 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileLookup.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileLookup.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.file;
 
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 
 import java.io.File;
 
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 065d6f0..6e47ef9 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
@@ -15,12 +15,10 @@
  */
 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;
@@ -34,21 +32,22 @@ 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.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.process.ExecResult;
-import org.gradle.process.internal.*;
-import org.gradle.util.ConfigureUtil;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
+import org.gradle.process.internal.DefaultExecAction;
+import org.gradle.process.internal.DefaultJavaExecAction;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.JavaExecAction;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
 import java.net.URI;
-import java.util.Collections;
 import java.util.Map;
 
-import static org.gradle.util.ConfigureUtil.configure;
-
-public class DefaultFileOperations implements FileOperations, ProcessOperations, ExecActionFactory {
+public class DefaultFileOperations implements FileOperations, ProcessOperations {
     private final FileResolver fileResolver;
     private final TaskResolver taskResolver;
     private final TemporaryFileProvider temporaryFileProvider;
@@ -64,7 +63,7 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations,
         this.temporaryFileProvider = temporaryFileProvider;
         this.instantiator = instantiator;
         this.deleteAction = new DeleteActionImpl(fileResolver);
-        this.resourceHandler = new DefaultResourceHandler(fileResolver);
+        this.resourceHandler = new DefaultResourceHandler(this, temporaryFileProvider);
         fileCopier = new FileCopier(this.instantiator, this.fileResolver, fileLookup);
         fileSystem = fileLookup.getFileSystem();
     }
@@ -80,33 +79,19 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations,
     public URI uri(Object path) {
         return fileResolver.resolveUri(path);
     }
-    
+
     public ConfigurableFileCollection files(Object... paths) {
         return new DefaultConfigurableFileCollection(fileResolver, taskResolver, paths);
     }
 
-    public ConfigurableFileCollection files(Object paths, Closure configureClosure) {
-        return configure(configureClosure, files(paths));
-    }
-
     public ConfigurableFileTree fileTree(Object baseDir) {
         return new DefaultConfigurableFileTree(baseDir, fileResolver, taskResolver, fileCopier);
     }
 
-    public ConfigurableFileTree fileTree(Object baseDir, Closure closure) {
-        return ConfigureUtil.configure(closure, fileTree(baseDir));
-    }
-
     public ConfigurableFileTree fileTree(Map<String, ?> args) {
         return new DefaultConfigurableFileTree(args, fileResolver, taskResolver, fileCopier);
     }
 
-    @Deprecated
-    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, fileCopier));
-    }
-
     public FileTree zipTree(Object zipPath) {
         return new FileTreeAdapter(new ZipFileTree(file(zipPath), getExpandDir(), fileSystem));
     }
@@ -139,18 +124,14 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations,
         return deleteAction.delete(paths);
     }
 
-    public WorkResult copy(Closure closure) {
-        return fileCopier.copy(new ClosureBackedAction<CopySpec>(closure));
+    public WorkResult copy(Action<? super CopySpec> action) {
+        return fileCopier.copy(action);
     }
 
     public WorkResult sync(Action<? super CopySpec> action) {
         return fileCopier.sync(action);
     }
 
-    public CopySpec copySpec(Closure closure) {
-        return copySpec(new ClosureBackedAction<CopySpec>(closure));
-    }
-
     public CopySpec copySpec(Action<? super CopySpec> action) {
         DefaultCopySpec copySpec = instantiator.newInstance(DefaultCopySpec.class, fileResolver, instantiator);
         action.execute(copySpec);
@@ -161,20 +142,18 @@ public class DefaultFileOperations implements FileOperations, ProcessOperations,
         return fileResolver;
     }
 
-    public ExecResult javaexec(Closure cl) {
-        JavaExecAction javaExecAction = ConfigureUtil.configure(cl, instantiator.newInstance(DefaultJavaExecAction.class, fileResolver));
+    public ExecResult javaexec(Action<? super JavaExecSpec> action) {
+        JavaExecAction javaExecAction = instantiator.newInstance(DefaultJavaExecAction.class, fileResolver);
+        action.execute(javaExecAction);
         return javaExecAction.execute();
     }
 
-    public ExecResult exec(Closure cl) {
-        ExecAction execAction = ConfigureUtil.configure(cl, instantiator.newInstance(DefaultExecAction.class, fileResolver));
+    public ExecResult exec(Action<? super ExecSpec> action) {
+        ExecAction execAction = instantiator.newInstance(DefaultExecAction.class, fileResolver);
+        action.execute(execAction);
         return execAction.execute();
     }
 
-    public ExecAction newExecAction() {
-        return new DefaultExecAction(fileResolver);
-    }
-
     public DefaultResourceHandler getResources() {
         return resourceHandler;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileTreeElement.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileTreeElement.java
index f549091..807130d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileTreeElement.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileTreeElement.java
@@ -15,14 +15,12 @@
  */
 package org.gradle.api.internal.file;
 
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.file.RelativePath;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.Stat;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.Stat;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.InputStream;
 
 public class DefaultFileTreeElement extends AbstractFileTreeElement {
@@ -66,10 +64,6 @@ public class DefaultFileTreeElement extends AbstractFileTreeElement {
     }
 
     public int getMode() {
-        try {
-            return stat.getUnixMode(file);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
+        return stat.getUnixMode(file);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileVisitDetails.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileVisitDetails.java
index f8caa25..63a9967 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileVisitDetails.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/DefaultFileVisitDetails.java
@@ -17,8 +17,8 @@ package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.RelativePath;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.Stat;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.Stat;
 
 import java.io.File;
 import java.util.concurrent.atomic.AtomicBoolean;
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileLookup.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileLookup.java
index b57a934..1df1bbc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileLookup.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileLookup.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.file;
 
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 
 import java.io.File;
 
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 71e21cf..bfdcaa7 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
@@ -15,7 +15,6 @@
  */
 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;
@@ -42,26 +41,17 @@ public interface FileOperations {
 
     ConfigurableFileCollection files(Object... paths);
 
-    ConfigurableFileCollection files(Object paths, Closure configureClosure);
-
     ConfigurableFileTree fileTree(Object baseDir);
 
     ConfigurableFileTree fileTree(Map<String, ?> args);
 
-    @Deprecated
-    ConfigurableFileTree fileTree(Closure closure);
-
-    ConfigurableFileTree fileTree(Object baseDir, Closure closure);
-
     FileTree zipTree(Object zipPath);
 
     FileTree tarTree(Object tarPath);
 
-    CopySpec copySpec(Closure closure);
-
     CopySpec copySpec(Action<? super CopySpec> action);
 
-    WorkResult copy(Closure closure);
+    WorkResult copy(Action<? super CopySpec> action);
 
     WorkResult sync(Action<? super CopySpec> action);
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationConverter.java
new file mode 100644
index 0000000..a0251b9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationConverter.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.typeconversion.*;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FileOrUriNotationConverter implements NotationConverter<Object, Object> {
+
+    private static final Pattern URI_SCHEME = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-\\.]*:.+");
+    private static final Pattern ENCODED_URI = Pattern.compile("%([0-9a-fA-F]{2})");
+    private final FileSystem fileSystem;
+
+    public FileOrUriNotationConverter(FileSystem fileSystem) {
+        this.fileSystem = fileSystem;
+    }
+
+    public static NotationParser<Object, Object> parser(FileSystem fileSystem) {
+        return NotationParserBuilder
+                .toType(Object.class)
+                .typeDisplayName("a File or URI")
+                .converter(new FileOrUriNotationConverter(fileSystem))
+                .toComposite();
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("A String or CharSequence path").example("'src/main/java' or '/usr/include'");
+        visitor.candidate("A String or CharSequence URI").example("'file:/usr/include'");
+        visitor.candidate("A File instance.");
+        visitor.candidate("A URI or URL instance.");
+    }
+
+    public void convert(Object notation, NotationConvertResult<? super Object> result) throws TypeConversionException {
+        if (notation instanceof File) {
+            result.converted(notation);
+            return;
+        }
+        if (notation instanceof URL) {
+            try {
+                notation = ((URL) notation).toURI();
+            } catch (URISyntaxException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+        if (notation instanceof URI) {
+            URI uri = (URI) notation;
+            if (uri.getScheme().equals("file")) {
+                result.converted(new File(uri.getPath()));
+            } else {
+                result.converted(uri);
+            }
+            return;
+        }
+        if (notation instanceof CharSequence) {
+            String notationString = notation.toString();
+            if (notationString.startsWith("file:")) {
+                result.converted(new File(uriDecode(notationString.substring(5))));
+                return;
+            }
+            for (File file : File.listRoots()) {
+                String rootPath = file.getAbsolutePath();
+                String normalisedStr = notationString;
+                if (!fileSystem.isCaseSensitive()) {
+                    rootPath = rootPath.toLowerCase();
+                    normalisedStr = normalisedStr.toLowerCase();
+                }
+                if (normalisedStr.startsWith(rootPath) || normalisedStr.startsWith(rootPath.replace(File.separatorChar, '/'))) {
+                    result.converted(new File(notationString));
+                    return;
+                }
+            }
+            // Check if string starts with a URI scheme
+            if (URI_SCHEME.matcher(notationString).matches()) {
+                try {
+                    result.converted(new URI(notationString));
+                    return;
+                } catch (URISyntaxException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+            result.converted(new File(notationString));
+        }
+    }
+
+    private String uriDecode(String path) {
+        StringBuffer builder = new StringBuffer();
+        Matcher matcher = ENCODED_URI.matcher(path);
+        while (matcher.find()) {
+            String val = matcher.group(1);
+            matcher.appendReplacement(builder, String.valueOf((char) (Integer.parseInt(val, 16))));
+        }
+        matcher.appendTail(builder);
+        return builder.toString();
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationParser.java
deleted file mode 100644
index f5675f6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/FileOrUriNotationParser.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.file;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.File;
-import java.io.Serializable;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Collection;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class FileOrUriNotationParser<T extends Serializable> implements NotationParser<Object, T> {
-
-    private static final Pattern URI_SCHEME = Pattern.compile("[a-zA-Z][a-zA-Z0-9+-\\.]*:.+");
-    private static final Pattern ENCODED_URI = Pattern.compile("%([0-9a-fA-F]{2})");
-    private final FileSystem fileSystem;
-
-    public FileOrUriNotationParser(FileSystem fileSystem) {
-        this.fileSystem = fileSystem;
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("File, URI, URL or CharSequence is supported");
-    }
-
-    public T parseNotation(Object notation) {
-        if (notation instanceof File) {
-            return (T) notation;
-        }
-        if (notation instanceof URL) {
-            try {
-                notation = ((URL) notation).toURI();
-            } catch (URISyntaxException e) {
-                throw new UncheckedIOException(e);
-            }
-        }
-        if (notation instanceof URI) {
-            URI uri = (URI) notation;
-            if (uri.getScheme().equals("file")) {
-                return (T) new File(uri.getPath());
-            } else {
-                return (T) uri;
-            }
-        }
-        if (notation instanceof CharSequence) {
-            String notationString = notation.toString();
-            if (notationString.startsWith("file:")) {
-                return (T) new File(uriDecode(notationString.substring(5)));
-            }
-            for (File file : File.listRoots()) {
-                String rootPath = file.getAbsolutePath();
-                String normalisedStr = notationString;
-                if (!fileSystem.isCaseSensitive()) {
-                    rootPath = rootPath.toLowerCase();
-                    normalisedStr = normalisedStr.toLowerCase();
-                }
-                if (normalisedStr.startsWith(rootPath) || normalisedStr.startsWith(rootPath.replace(File.separatorChar,
-                        '/'))) {
-                    return (T) new File(notationString);
-                }
-            }
-            // Check if string starts with a URI scheme
-            if (URI_SCHEME.matcher(notationString).matches()) {
-                try {
-                    return (T) new URI(notationString);
-                } catch (URISyntaxException e) {
-                    throw new UncheckedIOException(e);
-                }
-            }
-        } else {
-            DeprecationLogger.nagUserOfDeprecated(
-                    String.format("Converting class %s to File using toString() method", notation.getClass().getName()),
-                    "Please use java.io.File, java.lang.String, java.net.URL, or java.net.URI instead"
-            );
-        }
-        return (T) new File(notation.toString());
-    }
-
-    private String uriDecode(String path) {
-        StringBuffer builder = new StringBuffer();
-        Matcher matcher = ENCODED_URI.matcher(path);
-        while (matcher.find()) {
-            String val = matcher.group(1);
-            matcher.appendReplacement(builder, String.valueOf((char) (Integer.parseInt(val, 16))));
-        }
-        matcher.appendTail(builder);
-        return builder.toString();
-    }
-
-}
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 02ab3e4..58b22e6 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,8 +15,8 @@
  */
 package org.gradle.api.internal.file;
 
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.nativeplatform.services.FileSystems;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.services.FileSystems;
 
 import java.io.File;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/RelativeFile.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/RelativeFile.java
index bcc61f8..07390b3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/RelativeFile.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/RelativeFile.java
@@ -39,4 +39,16 @@ public class RelativeFile implements Serializable {
         return relativePath;
     }
 
+    public File getBaseDir() {
+        if (file == null || relativePath == null) {
+            return null;
+        }
+        int relativeSegments = relativePath.getSegments().length;
+        File parentFile = file;
+        for (int i=0; i<relativeSegments; i++) {
+            parentFile = parentFile.getParentFile();
+        }
+        return parentFile;
+    }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TemporaryFileProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TemporaryFileProvider.java
index 86f9cb3..683dfbc 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TemporaryFileProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TemporaryFileProvider.java
@@ -34,7 +34,7 @@ public interface TemporaryFileProvider {
      * Allocates and creates a new temporary file with the given prefix, suffix,
      * and path, relative to the temporary file directory.
      */
-    File createTemporaryFile(@Nullable String prefix, @Nullable String suffix, @Nullable String... path);
+    File createTemporaryFile(String prefix, @Nullable String suffix, @Nullable String... path);
     
     File createTemporaryDirectory(@Nullable String prefix, @Nullable String suffix, @Nullable String... path);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TmpDirTemporaryFileProvider.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TmpDirTemporaryFileProvider.java
index 3c1fb5b..b5ac2a6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TmpDirTemporaryFileProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/TmpDirTemporaryFileProvider.java
@@ -26,7 +26,7 @@ public class TmpDirTemporaryFileProvider extends DefaultTemporaryFileProvider {
     public TmpDirTemporaryFileProvider() {
         super(new Factory<File>() {
             public File create() {
-                return GFileUtils.canonicalise(new File(SystemProperties.getJavaIoTmpDir()));
+                return GFileUtils.canonicalise(new File(SystemProperties.getInstance().getJavaIoTmpDir()));
             }
         });
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
index 3815558..17c5999 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/TarFileTree.java
@@ -26,13 +26,11 @@ import org.gradle.api.internal.file.AbstractFileTreeElement;
 import org.gradle.api.internal.file.collections.DirectoryFileTree;
 import org.gradle.api.internal.file.collections.FileSystemMirroringFileTree;
 import org.gradle.api.internal.file.collections.MinimalFileTree;
-import org.gradle.api.resources.MissingResourceException;
 import org.gradle.api.resources.ReadableResource;
 import org.gradle.api.resources.ResourceException;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.util.DeprecationLogger;
-import org.gradle.util.GFileUtils;
 import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.util.GFileUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -63,12 +61,6 @@ public class TarFileTree implements MinimalFileTree, FileSystemMirroringFileTree
         InputStream inputStream;
         try {
             inputStream = resource.read();
-            assert inputStream != null;
-        } catch (MissingResourceException e) {
-            DeprecationLogger.nagUserOfDeprecatedBehaviour(
-                    String.format("The specified tar file %s does not exist and will be silently ignored", getDisplayName())
-            );
-            return;
         } catch (ResourceException e) {
             throw new InvalidUserDataException(String.format("Cannot expand %s.", getDisplayName()), e);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
index e46dfb5..a7c71d5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/archive/ZipFileTree.java
@@ -27,10 +27,9 @@ import org.gradle.api.internal.file.AbstractFileTreeElement;
 import org.gradle.api.internal.file.collections.DirectoryFileTree;
 import org.gradle.api.internal.file.collections.FileSystemMirroringFileTree;
 import org.gradle.api.internal.file.collections.MinimalFileTree;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.util.DeprecationLogger;
 import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 
 import java.io.File;
 import java.io.IOException;
@@ -63,10 +62,7 @@ public class ZipFileTree implements MinimalFileTree, FileSystemMirroringFileTree
 
     public void visit(FileVisitor visitor) {
         if (!zipFile.exists()) {
-            DeprecationLogger.nagUserOfDeprecatedBehaviour(
-                    String.format("The specified zip file %s does not exist and will be silently ignored", getDisplayName())
-            );
-            return;
+            throw new InvalidUserDataException(String.format("Cannot expand %s as it does not exist.", getDisplayName()));
         }
         if (!zipFile.isFile()) {
             throw new InvalidUserDataException(String.format("Cannot expand %s as it is not a file.", getDisplayName()));
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileTree.java
new file mode 100644
index 0000000..65998e3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/DelegatingFileTree.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.file.collections;
+
+import groovy.lang.Closure;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.tasks.util.PatternFilterable;
+
+/**
+ * A file tree that delegates each method call to the
+ * file tree returned by {@link #getDelegate()}.
+ */
+public abstract class DelegatingFileTree extends DelegatingFileCollection implements FileTree {
+    public abstract FileTree getDelegate();
+
+    public FileTree matching(Closure filterConfigClosure) {
+        return getDelegate().matching(filterConfigClosure);
+    }
+
+    public FileTree matching(PatternFilterable patterns) {
+        return getDelegate().matching(patterns);
+    }
+
+    public FileTree visit(FileVisitor visitor) {
+        return getDelegate().visit(visitor);
+    }
+
+    public FileTree visit(Closure visitor) {
+        return getDelegate().visit(visitor);
+    }
+
+    public FileTree plus(FileTree fileTree) {
+        return getDelegate().plus(fileTree);
+    }
+
+    public FileTree getAsFileTree() {
+        return getDelegate().getAsFileTree();
+    }
+}
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 65e38ec..36c28cf 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
@@ -25,8 +25,8 @@ import org.gradle.api.logging.Logging;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.util.PatternFilterable;
 import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.nativeplatform.services.FileSystems;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.services.FileSystems;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.GUtil;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileTree.java
new file mode 100644
index 0000000..a79a8fb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/LazilyInitializedFileTree.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.FileTree;
+
+/**
+ * A {@link org.gradle.api.internal.file.collections.DelegatingFileTree} whose delegate is created lazily.
+ */
+public abstract class LazilyInitializedFileTree extends DelegatingFileTree {
+    private FileTree delegate;
+
+    public abstract FileTree createDelegate();
+
+    @Override
+    public final synchronized FileTree getDelegate() {
+        if (delegate == null) {
+            delegate = createDelegate();
+        }
+        return delegate;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java
index c454b50..77dc589 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/MapFileTree.java
@@ -16,12 +16,14 @@
 package org.gradle.api.internal.file.collections;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
+import org.gradle.api.internal.ClosureBackedAction;
 import org.gradle.api.internal.file.AbstractFileTreeElement;
 import org.gradle.internal.Factory;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
 
 import java.io.File;
 import java.io.InputStream;
@@ -36,12 +38,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
  * A {@link MinimalFileTree} which is composed using a mapping from relative path to file source.
  */
 public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree {
-    private final Map<RelativePath, Closure> elements = new LinkedHashMap<RelativePath, Closure>();
+    private final Map<RelativePath, Action<OutputStream>> elements = new LinkedHashMap<RelativePath, Action<OutputStream>>();
     private final Factory<File> tmpDirSource;
     private final Chmod chmod;
 
     public MapFileTree(final File tmpDir, Chmod chmod) {
-        this(new Factory<File>() { public File create() { return tmpDir; }}, chmod);
+        this(new Factory<File>() {
+                public File create() {
+                    return tmpDir;
+                }
+        }, chmod);
     }
 
     public MapFileTree(Factory<File> tmpDirSource, Chmod chmod) {
@@ -64,12 +70,12 @@ public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree
     public void visit(FileVisitor visitor) {
         AtomicBoolean stopFlag = new AtomicBoolean();
         Visit visit = new Visit(visitor, stopFlag);
-        for (Map.Entry<RelativePath, Closure> entry : elements.entrySet()) {
+        for (Map.Entry<RelativePath, Action<OutputStream>> entry : elements.entrySet()) {
             if (stopFlag.get()) {
                 break;
             }
             RelativePath path = entry.getKey();
-            Closure generator = entry.getValue();
+            Action<OutputStream> generator = entry.getValue();
             visit.visit(path, generator);
         }
     }
@@ -79,7 +85,12 @@ public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree
      * of the element to.
      */
     public void add(String path, Closure contentClosure) {
-        elements.put(RelativePath.parse(true, path), contentClosure);
+        Action<OutputStream> action = new ClosureBackedAction<OutputStream>(contentClosure);
+        add(path, action);
+    }
+
+    public void add(String path, Action<OutputStream> contentWriter) {
+        elements.put(RelativePath.parse(true, path), contentWriter);
     }
 
     private class Visit {
@@ -101,7 +112,7 @@ public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree
             visitor.visitDir(new FileVisitDetailsImpl(path, null, stopFlag, chmod));
         }
 
-        public void visit(RelativePath path, Closure generator) {
+        public void visit(RelativePath path, Action<OutputStream> generator) {
             visitDirs(path.getParent(), visitor);
             visitor.visitFile(new FileVisitDetailsImpl(path, generator, stopFlag, chmod));
         }
@@ -109,12 +120,12 @@ public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree
 
     private class FileVisitDetailsImpl extends AbstractFileTreeElement implements FileVisitDetails {
         private final RelativePath path;
-        private final Closure generator;
+        private final Action<OutputStream> generator;
         private final long lastModified;
         private final AtomicBoolean stopFlag;
         private File file;
 
-        public FileVisitDetailsImpl(RelativePath path, Closure generator, AtomicBoolean stopFlag, Chmod chmod) {
+        public FileVisitDetailsImpl(RelativePath path, Action<OutputStream> generator, AtomicBoolean stopFlag, Chmod chmod) {
             super(chmod);
             this.path = path;
             this.generator = generator;
@@ -152,7 +163,7 @@ public class MapFileTree implements MinimalFileTree, FileSystemMirroringFileTree
         }
 
         public void copyTo(OutputStream outstr) {
-            generator.call(outstr);
+            generator.execute(outstr);
         }
 
         public InputStream open() {
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 24fbc0c..93425f7 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
@@ -26,8 +26,8 @@ import org.gradle.api.internal.file.pattern.PatternStepFactory;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.nativeplatform.services.FileSystems;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.services.FileSystems;
 
 import java.io.File;
 import java.util.Arrays;
@@ -90,7 +90,9 @@ public class SingleIncludePatternFileTree implements MinimalFileTree {
                 throw new GradleException(String.format("Could not list contents of '%s'.", file));
             }
             for (File child : children) {
-                if (stopFlag.get()) { break; }
+                if (stopFlag.get()) {
+                    break;
+                }
                 if (step.matches(child.getName())) {
                     relativePath.addLast(child.getName());
                     doVisitDirOrFile(visitor, child, relativePath, segmentIndex + 1, stopFlag);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java
index 35cfb2d..71b0c2b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/collections/SingletonFileTree.java
@@ -18,8 +18,8 @@ package org.gradle.api.internal.file.collections;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
 import org.gradle.api.internal.file.DefaultFileVisitDetails;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.nativeplatform.services.FileSystems;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.services.FileSystems;
 
 import java.io.File;
 import java.util.concurrent.atomic.AtomicBoolean;
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
index 886b73d..53fa288 100644
--- 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
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 
 public class CopyActionExecuter {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImpl.java
index 6b22189..741a3b8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImpl.java
@@ -20,17 +20,17 @@ import org.gradle.api.file.FileCopyDetails;
 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.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 
 public class CopyFileVisitorImpl implements FileVisitor {
-    private final CopySpecInternal spec;
+    private final CopySpecResolver copySpecResolver;
     private final CopyActionProcessingStreamAction action;
     private final Instantiator instantiator;
     private final FileSystem fileSystem;
 
-    public CopyFileVisitorImpl(CopySpecInternal spec, CopyActionProcessingStreamAction action, Instantiator instantiator, FileSystem fileSystem) {
-        this.spec = spec;
+    public CopyFileVisitorImpl(CopySpecResolver spec, CopyActionProcessingStreamAction action, Instantiator instantiator, FileSystem fileSystem) {
+        this.copySpecResolver = spec;
         this.action = action;
         this.instantiator = instantiator;
         this.fileSystem = fileSystem;
@@ -51,7 +51,7 @@ public class CopyFileVisitorImpl implements FileVisitor {
 
     private void processFile(FileVisitDetails visitDetails) {
         DefaultFileCopyDetails details = createDefaultFileCopyDetails(visitDetails);
-        for (Action<? super FileCopyDetails> action : spec.getAllCopyActions()) {
+        for (Action<? super FileCopyDetails> action : copySpecResolver.getAllCopyActions()) {
             action.execute(details);
             if (details.isExcluded()) {
                 return;
@@ -61,6 +61,6 @@ public class CopyFileVisitorImpl implements FileVisitor {
     }
 
     private DefaultFileCopyDetails createDefaultFileCopyDetails(FileVisitDetails visitDetails) {
-        return instantiator.newInstance(DefaultFileCopyDetails.class, visitDetails, spec, fileSystem);
+        return instantiator.newInstance(DefaultFileCopyDetails.class, visitDetails, copySpecResolver, fileSystem);
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecActionImpl.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecActionImpl.java
index b4dbf05..5eb0614 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecActionImpl.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecActionImpl.java
@@ -18,10 +18,10 @@ package org.gradle.api.internal.file.copy;
 import org.gradle.api.Action;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 
-public class CopySpecActionImpl implements Action<CopySpecInternal> {
+public class CopySpecActionImpl implements Action<CopySpecResolver> {
     private final CopyActionProcessingStreamAction action;
     private final Instantiator instantiator;
     private final FileSystem fileSystem;
@@ -32,8 +32,8 @@ public class CopySpecActionImpl implements Action<CopySpecInternal> {
         this.fileSystem = fileSystem;
     }
 
-    public void execute(final CopySpecInternal spec) {
-        FileTree source = spec.getSource();
-        source.visit(new CopyFileVisitorImpl(spec, action, instantiator, fileSystem));
+    public void execute(final CopySpecResolver specResolver) {
+        FileTree source = specResolver.getSource();
+        source.visit(new CopyFileVisitorImpl(specResolver, action, instantiator, fileSystem));
     }
 }
\ No newline at end of file
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
index c9657ea..193bdae 100644
--- 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
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 
 public class CopySpecBackedCopyActionProcessingStream implements CopyActionProcessingStream {
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
index 0d82f79..50b1be4 100644
--- 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
@@ -17,31 +17,24 @@ 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();
-
+    //TODO - does this belong here or on the resolver? PEZ
     boolean hasSource();
 
-    Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions();
-
     Iterable<CopySpecInternal> getChildren();
 
-    void walk(Action<? super CopySpecInternal> action);
-
     CopySpecInternal addChild();
 
     CopySpecInternal addChildBeforeSpec(CopySpecInternal childSpec);
 
     CopySpecInternal addFirst();
+
+    void walk(Action<? super CopySpecResolver> action);
+
+    CopySpecResolver buildRootResolver();
+
+    CopySpecResolver buildResolverRelativeToParent(CopySpecResolver parent);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecResolver.java
new file mode 100644
index 0000000..f8103ea
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecResolver.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.specs.Spec;
+
+import java.util.Collection;
+import java.util.List;
+
+public interface CopySpecResolver {
+
+    boolean isCaseSensitive();
+    Integer getFileMode();
+    Integer getDirMode();
+    boolean getIncludeEmptyDirs();
+
+    RelativePath getDestPath();
+
+    FileTree getSource();
+
+    FileTree getAllSource();
+
+    Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions();
+
+    public List<String> getAllIncludes();
+
+    public List<String> getAllExcludes();
+
+    public List<Spec<FileTreeElement>> getAllIncludeSpecs();
+
+    public List<Spec<FileTreeElement>> getAllExcludeSpecs();
+
+    DuplicatesStrategy getDuplicatesStrategy();
+
+    void walk(Action<? super CopySpecResolver> action);
+
+
+}
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
index 5b74c08..693b9e8 100644
--- 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
@@ -28,6 +28,7 @@ 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.internal.typeconversion.NotationParser;
 import org.gradle.util.ConfigureUtil;
 
 import java.io.File;
@@ -37,38 +38,31 @@ import java.util.regex.Pattern;
 
 @NonExtensible
 public class DefaultCopySpec implements CopySpecInternal {
-    private final FileResolver resolver;
+    private static final NotationParser<Object, String> PATH_NOTATION_PARSER = PathNotationConverter.parser();
+    protected final FileResolver fileResolver;
     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>>();
+    protected final List<CopySpecInternal> childSpecs;
+    protected final Instantiator instantiator;
+    private final List<Action<? super FileCopyDetails>> copyActions = 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;
+    public DefaultCopySpec(FileResolver resolver, Instantiator instantiator) {
+        this.fileResolver = 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);
+        duplicatesStrategy = null;
     }
 
-    protected FileResolver getResolver() {
-        return resolver;
+    protected List<Action<? super FileCopyDetails>> getCopyActions() {
+        return copyActions;
     }
 
     public CopySpec with(CopySpec... copySpecs) {
@@ -80,7 +74,7 @@ public class DefaultCopySpec implements CopySpecInternal {
             } else {
                 copySpecInternal = (CopySpecInternal) copySpec;
             }
-            childSpecs.add(new RelativizedCopySpec(this, copySpecInternal));
+            childSpecs.add(copySpecInternal);
         }
         return this;
     }
@@ -107,14 +101,14 @@ public class DefaultCopySpec implements CopySpecInternal {
         return addChildAtPosition(0);
     }
 
-    private CopySpecInternal addChildAtPosition(int position) {
-        DefaultCopySpec child = instantiator.newInstance(DefaultCopySpec.class, resolver, instantiator, this);
+    protected CopySpecInternal addChildAtPosition(int position) {
+        DefaultCopySpec child = instantiator.newInstance(SingleParentCopySpec.class, fileResolver, instantiator, buildRootResolver());
         childSpecs.add(position, child);
         return child;
     }
 
     public CopySpecInternal addChild() {
-        DefaultCopySpec child = new DefaultCopySpec(resolver, instantiator, this);
+        DefaultCopySpec child = new SingleParentCopySpec(fileResolver, instantiator, buildRootResolver());
         childSpecs.add(child);
         return child;
     }
@@ -128,20 +122,6 @@ public class DefaultCopySpec implements CopySpecInternal {
         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;
@@ -159,47 +139,8 @@ public class DefaultCopySpec implements CopySpecInternal {
         }
     }
 
-    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;
+        return buildRootResolver().isCaseSensitive();
     }
 
     public void setCaseSensitive(boolean caseSensitive) {
@@ -207,13 +148,7 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public boolean getIncludeEmptyDirs() {
-        if (includeEmptyDirs != null) {
-            return includeEmptyDirs;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getIncludeEmptyDirs();
-        }
-        return true;
+        return buildRootResolver().getIncludeEmptyDirs();
     }
 
     public void setIncludeEmptyDirs(boolean includeEmptyDirs) {
@@ -221,13 +156,7 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public DuplicatesStrategy getDuplicatesStrategy() {
-        if (duplicatesStrategy != null) {
-            return duplicatesStrategy;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getDuplicatesStrategy();
-        }
-        return DuplicatesStrategy.INCLUDE;
+        return buildRootResolver().getDuplicatesStrategy();
     }
 
     public void setDuplicatesStrategy(DuplicatesStrategy strategy) {
@@ -275,24 +204,6 @@ public class DefaultCopySpec implements CopySpecInternal {
         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;
@@ -323,17 +234,17 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public CopySpec rename(String sourceRegEx, String replaceWith) {
-        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
+        copyActions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
         return this;
     }
 
     public CopySpec rename(Pattern sourceRegEx, String replaceWith) {
-        actions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
+        copyActions.add(new RenamingCopyAction(new RegExpNameMapper(sourceRegEx, replaceWith)));
         return this;
     }
 
     public CopySpec filter(final Class<? extends FilterReader> filterType) {
-        actions.add(new Action<FileCopyDetails>() {
+        copyActions.add(new Action<FileCopyDetails>() {
             public void execute(FileCopyDetails fileCopyDetails) {
                 fileCopyDetails.filter(filterType);
             }
@@ -342,7 +253,7 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public CopySpec filter(final Closure closure) {
-        actions.add(new Action<FileCopyDetails>() {
+        copyActions.add(new Action<FileCopyDetails>() {
             public void execute(FileCopyDetails fileCopyDetails) {
                 fileCopyDetails.filter(closure);
             }
@@ -351,7 +262,7 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public CopySpec filter(final Map<String, ?> properties, final Class<? extends FilterReader> filterType) {
-        actions.add(new Action<FileCopyDetails>() {
+        copyActions.add(new Action<FileCopyDetails>() {
             public void execute(FileCopyDetails fileCopyDetails) {
                 fileCopyDetails.filter(properties, filterType);
             }
@@ -360,7 +271,7 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public CopySpec expand(final Map<String, ?> properties) {
-        actions.add(new Action<FileCopyDetails>() {
+        copyActions.add(new Action<FileCopyDetails>() {
             public void execute(FileCopyDetails fileCopyDetails) {
                 fileCopyDetails.expand(properties);
             }
@@ -371,28 +282,16 @@ public class DefaultCopySpec implements CopySpecInternal {
     public CopySpec rename(Closure closure) {
         ChainingTransformer<String> transformer = new ChainingTransformer<String>(String.class);
         transformer.add(closure);
-        actions.add(new RenamingCopyAction(transformer));
+        copyActions.add(new RenamingCopyAction(transformer));
         return this;
     }
 
     public Integer getDirMode() {
-        if (dirMode != null) {
-            return dirMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getDirMode();
-        }
-        return null;
+        return buildRootResolver().getDirMode();
     }
 
     public Integer getFileMode() {
-        if (fileMode != null) {
-            return fileMode;
-        }
-        if (parentSpec != null) {
-            return parentSpec.getFileMode();
-        }
-        return null;
+        return buildRootResolver().getFileMode();
     }
 
     public CopyProcessingSpec setDirMode(Integer mode) {
@@ -406,52 +305,21 @@ public class DefaultCopySpec implements CopySpecInternal {
     }
 
     public CopySpec eachFile(Action<? super FileCopyDetails> action) {
-        actions.add(action);
+        copyActions.add(action);
         return this;
     }
 
     public CopySpec eachFile(Closure closure) {
-        actions.add(new ClosureBackedAction<FileCopyDetails>(closure));
+        copyActions.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 void walk(Action<? super CopySpecResolver> action) {
+        buildRootResolver().walk(action);
     }
 
     public boolean hasSource() {
@@ -466,4 +334,173 @@ public class DefaultCopySpec implements CopySpecInternal {
         return false;
     }
 
+    public CopySpecResolver buildResolverRelativeToParent(CopySpecResolver parent) {
+        return this.new DefaultCopySpecResolver(parent);
+    }
+
+    public CopySpecResolver buildRootResolver() {
+        return this.new DefaultCopySpecResolver(null);
+    }
+
+    public class DefaultCopySpecResolver implements CopySpecResolver {
+
+        private CopySpecResolver parentResolver;
+
+        private DefaultCopySpecResolver(CopySpecResolver parent) {
+            this.parentResolver = parent;
+        }
+
+        public RelativePath getDestPath() {
+
+            RelativePath parentPath;
+            if (parentResolver == null) {
+                parentPath = new RelativePath(false);
+            } else {
+                parentPath = parentResolver.getDestPath();
+            }
+
+            if (destDir == null) {
+                return parentPath;
+            }
+
+            String path = PATH_NOTATION_PARSER.parseNotation(destDir);
+            if (path.startsWith("/") || path.startsWith(File.separator)) {
+                return RelativePath.parse(false, path);
+            }
+
+            return RelativePath.parse(false, parentPath, path);
+        }
+
+        public FileTree getSource() {
+            return fileResolver.resolveFilesAsTree(sourcePaths).matching(this.getPatternSet());
+        }
+
+        public FileTree getAllSource() {
+            final ImmutableList.Builder<FileTree> builder = ImmutableList.builder();
+            walk(new Action<CopySpecResolver>() {
+                public void execute(CopySpecResolver copySpecResolver) {
+                    builder.add(copySpecResolver.getSource());
+                }
+            });
+
+            return fileResolver.compositeFileTree(builder.build());
+        }
+
+        public Collection<? extends Action<? super FileCopyDetails>> getAllCopyActions() {
+            if (parentResolver == null) {
+                return copyActions;
+            }
+            List<Action<? super FileCopyDetails>> allActions = new ArrayList<Action<? super FileCopyDetails>>();
+            allActions.addAll(parentResolver.getAllCopyActions());
+            allActions.addAll(copyActions);
+            return allActions;
+        }
+
+        public List<String> getAllIncludes() {
+            List<String> result = new ArrayList<String>();
+            if (parentResolver != null) {
+                result.addAll(parentResolver.getAllIncludes());
+            }
+            result.addAll(patternSet.getIncludes());
+            return result;
+        }
+
+        public List<String> getAllExcludes() {
+            List<String> result = new ArrayList<String>();
+            if (parentResolver != null) {
+                result.addAll(parentResolver.getAllExcludes());
+            }
+            result.addAll(patternSet.getExcludes());
+            return result;
+        }
+
+
+        public List<Spec<FileTreeElement>> getAllExcludeSpecs() {
+            List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
+            if (parentResolver != null) {
+                result.addAll(parentResolver.getAllExcludeSpecs());
+            }
+            result.addAll(patternSet.getExcludeSpecs());
+            return result;
+        }
+
+        public DuplicatesStrategy getDuplicatesStrategy() {
+            if (duplicatesStrategy != null) {
+                return duplicatesStrategy;
+            }
+            if (parentResolver != null) {
+                return parentResolver.getDuplicatesStrategy();
+            }
+            return DuplicatesStrategy.INCLUDE;
+        }
+
+
+        public boolean isCaseSensitive() {
+            if (caseSensitive != null) {
+                return caseSensitive;
+            }
+            if (parentResolver != null) {
+                return parentResolver.isCaseSensitive();
+            }
+            return true;
+        }
+
+        public Integer getFileMode() {
+            if (fileMode != null) {
+                return fileMode;
+            }
+            if (parentResolver != null) {
+                return parentResolver.getFileMode();
+            }
+            return null;
+        }
+
+        public Integer getDirMode() {
+            if (dirMode != null) {
+                return dirMode;
+            }
+            if (parentResolver != null) {
+                return parentResolver.getDirMode();
+            }
+            return null;
+        }
+
+        public boolean getIncludeEmptyDirs() {
+            if (includeEmptyDirs != null) {
+                return includeEmptyDirs;
+            }
+            if (parentResolver != null) {
+                return parentResolver.getIncludeEmptyDirs();
+            }
+            return true;
+        }
+
+        public List<Spec<FileTreeElement>> getAllIncludeSpecs() {
+            List<Spec<FileTreeElement>> result = new ArrayList<Spec<FileTreeElement>>();
+            if (parentResolver != null) {
+                result.addAll(parentResolver.getAllIncludeSpecs());
+            }
+            result.addAll(patternSet.getIncludeSpecs());
+            return result;
+        }
+
+        public PatternSet getPatternSet() {
+            PatternSet patterns = new PatternSet();
+            patterns.setCaseSensitive(isCaseSensitive());
+            patterns.include(this.getAllIncludes());
+            patterns.includeSpecs(getAllIncludeSpecs());
+            patterns.exclude(this.getAllExcludes());
+            patterns.excludeSpecs(getAllExcludeSpecs());
+            return patterns;
+        }
+
+        public void walk(Action<? super CopySpecResolver> action) {
+            action.execute(this);
+            for (CopySpecInternal child : getChildren()) {
+                child.buildResolverRelativeToParent(this).walk(action);
+            }
+        }
+    }
+
 }
+
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
index 3494dca..8caf503 100644
--- 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
@@ -17,35 +17,34 @@
 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.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
 
 import java.io.*;
 import java.util.Map;
 
 public class DefaultFileCopyDetails extends AbstractFileTreeElement implements FileVisitDetails, FileCopyDetailsInternal {
     private final FileVisitDetails fileDetails;
-    private final CopySpecInternal spec;
+    private final CopySpecResolver specResolver;
     private final FilterChain filterChain = new FilterChain();
     private RelativePath relativePath;
     private boolean excluded;
     private Integer mode;
     private DuplicatesStrategy duplicatesStrategy;
 
-    public DefaultFileCopyDetails(FileVisitDetails fileDetails, CopySpecInternal spec, Chmod chmod) {
+    public DefaultFileCopyDetails(FileVisitDetails fileDetails, CopySpecResolver specResolver, Chmod chmod) {
         super(chmod);
         this.fileDetails = fileDetails;
-        this.spec = spec;
-        this.duplicatesStrategy = spec.getDuplicatesStrategy();
+        this.specResolver = specResolver;
+        this.duplicatesStrategy = specResolver.getDuplicatesStrategy();
     }
 
     public boolean isIncludeEmptyDirs() {
-        return spec.getIncludeEmptyDirs();
+        return specResolver.getIncludeEmptyDirs();
     }
 
     public String getDisplayName() {
@@ -109,20 +108,14 @@ public class DefaultFileCopyDetails extends AbstractFileTreeElement implements F
     }
 
     private void adaptPermissions(File target) {
-        final Integer specMode = getMode();
-        if(specMode !=null){
-            try {
-                getChmod().chmod(target, specMode);
-            } catch (IOException e) {
-                throw new GradleException(String.format("Could not set permission %s on '%s'.", specMode, target), e);
-            }
-        }
+        int specMode = getMode();
+        getChmod().chmod(target, specMode);
     }
 
     public RelativePath getRelativePath() {
         if (relativePath == null) {
             RelativePath path = fileDetails.getRelativePath();
-            relativePath = spec.getDestPath().append(path.isFile(), path.getSegments());
+            relativePath = specResolver.getDestPath().append(path.isFile(), path.getSegments());
         }
         return relativePath;
     }
@@ -141,7 +134,7 @@ public class DefaultFileCopyDetails extends AbstractFileTreeElement implements F
     }
 
     private Integer getSpecMode() {
-        return fileDetails.isDirectory() ? spec.getDirMode() : spec.getFileMode();
+        return fileDetails.isDirectory() ? specResolver.getDirMode() : specResolver.getFileMode();
     }
 
     public void setRelativePath(RelativePath path) {
@@ -196,6 +189,18 @@ public class DefaultFileCopyDetails extends AbstractFileTreeElement implements F
         return this.duplicatesStrategy;
     }
 
+    public String getSourceName() {
+        return this.fileDetails.getName();
+    }
+
+    public String getSourcePath() {
+        return this.fileDetails.getPath();
+    }
+
+    public RelativePath getRelativeSourcePath() {
+        return this.fileDetails.getRelativePath();
+    }
+
     private static class ByteCountingOutputStream extends OutputStream {
         long size;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecInternal.java
index 60ec285..e1c0ff4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecInternal.java
@@ -22,7 +22,6 @@ 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;
@@ -31,22 +30,10 @@ abstract public class DelegatingCopySpecInternal 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();
     }
@@ -75,7 +62,7 @@ abstract public class DelegatingCopySpecInternal implements CopySpecInternal {
         return getDelegateCopySpec().filesMatching(pattern, action);
     }
 
-     public CopySpec filesNotMatching(String pattern, Action<? super FileCopyDetails> action) {
+    public CopySpec filesNotMatching(String pattern, Action<? super FileCopyDetails> action) {
         return getDelegateCopySpec().filesNotMatching(pattern, action);
     }
 
@@ -203,10 +190,6 @@ abstract public class DelegatingCopySpecInternal implements CopySpecInternal {
         return getDelegateCopySpec().getChildren();
     }
 
-    public FileTree getAllSource() {
-        return getDelegateCopySpec().getAllSource();
-    }
-
     public CopySpecInternal addChild() {
         return getDelegateCopySpec().addChild();
     }
@@ -219,10 +202,15 @@ abstract public class DelegatingCopySpecInternal implements CopySpecInternal {
         return getDelegateCopySpec().addFirst();
     }
 
-    public void walk(Action<? super CopySpecInternal> action) {
-        action.execute(this);
-        for (CopySpecInternal child : getChildren()) {
-            child.walk(action);
-        }
+    public void walk(Action<? super CopySpecResolver> action) {
+        getDelegateCopySpec().walk(action);
+    }
+
+    public CopySpecResolver buildRootResolver() {
+        return getDelegateCopySpec().buildRootResolver();
+    }
+
+    public CopySpecResolver buildResolverRelativeToParent(CopySpecResolver parent) {
+        return getDelegateCopySpec().buildResolverRelativeToParent(parent);
     }
 }
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
index c101d26..3e04826 100644
--- 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
@@ -48,4 +48,5 @@ public class DestinationRootCopySpec extends DelegatingCopySpecInternal {
         return destinationDir == null ? null : fileResolver.resolve(destinationDir);
     }
 
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
index 297f226..23c1a3a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/LineFilter.java
@@ -66,7 +66,7 @@ public class LineFilter extends Reader {
         StringBuilder result = new StringBuilder();
         result.append(closure.call(line.toString()).toString());
         if (eol) {
-            result.append(SystemProperties.getLineSeparator());
+            result.append(SystemProperties.getInstance().getLineSeparator());
         }
         return result.toString();
     }
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
index 74329be..3df82cf 100644
--- 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
@@ -32,7 +32,7 @@ public class MatchingCopyAction implements Action<FileCopyDetails> {
     }
 
     public void execute(FileCopyDetails details) {
-        if (matchSpec.isSatisfiedBy(details.getRelativePath())) {
+        if (matchSpec.isSatisfiedBy(details.getRelativeSourcePath())) {
             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
index bdc267a..e37f9ab 100644
--- 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
@@ -24,7 +24,7 @@ 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 org.gradle.internal.nativeplatform.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
 
 import java.io.File;
 import java.io.FilterReader;
@@ -174,6 +174,18 @@ public class NormalizingCopyActionDecorator implements CopyAction {
             throw new UnsupportedOperationException();
         }
 
+        public String getSourceName() {
+            throw new UnsupportedOperationException();
+        }
+
+        public String getSourcePath() {
+            throw new UnsupportedOperationException();
+        }
+
+        public RelativePath getRelativeSourcePath() {
+            throw new UnsupportedOperationException();
+        }
+
         public ContentFilterable filter(Map<String, ?> properties, Class<? extends FilterReader> filterType) {
             throw new UnsupportedOperationException();
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationConverter.java
new file mode 100644
index 0000000..e649228
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationConverter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.file.copy;
+
+import groovy.lang.Closure;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+
+import java.io.File;
+import java.util.concurrent.Callable;
+
+public class PathNotationConverter implements NotationConverter<Object, String> {
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("String or CharSequence instances").example("'some/path'");
+        visitor.candidate("Boolean values").example("true").example("Boolean.TRUE");
+        visitor.candidate("Number values").example("42").example("3.14");
+        visitor.candidate("A File instance");
+        visitor.candidate("A Closure that returns any supported value.");
+        visitor.candidate("A Callable that returns any supported value.");
+    }
+
+    public static NotationParser<Object, String> parser() {
+        return NotationParserBuilder
+                .toType(String.class)
+                .noImplicitConverters()
+                .allowNullInput()
+                .converter(new PathNotationConverter())
+                .toComposite();
+    }
+
+    @Override
+    public void convert(Object notation, NotationConvertResult<? super String> result) throws TypeConversionException {
+        if (notation == null) {
+            result.converted(null);
+        } else if (notation instanceof CharSequence
+                || notation instanceof File
+                || notation instanceof Number
+                || notation instanceof Boolean) {
+            result.converted(notation.toString());
+        } else if (notation instanceof Closure) {
+            final Closure closure = (Closure) notation;
+            final Object called = closure.call();
+            convert(called, result);
+        } else if (notation instanceof Callable) {
+            try {
+                final Callable callableNotation = (Callable) notation;
+                final Object called = callableNotation.call();
+                convert(called, result);
+                if (!result.hasResult()) {
+                    throw new TypeConversionException("Couldn't convert " + notation);
+                }
+            } catch (Exception e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationParser.java
deleted file mode 100644
index 975da75..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/PathNotationParser.java
+++ /dev/null
@@ -1,63 +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.file.copy;
-
-import groovy.lang.Closure;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.UncheckedException;
-import org.gradle.util.DeprecationLogger;
-
-import java.util.Collection;
-import java.util.concurrent.Callable;
-
-public class PathNotationParser<T extends String> implements NotationParser<Object, T> {
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Strings, Boolean, Number like: 'path/to', true, Boolean.TRUE, 42, 3.14");
-        candidateFormats.add("Closures, Callables");
-    }
-
-    public T parseNotation(Object notation) {
-        if (notation == null) {
-            return null;
-        }
-        if (notation instanceof CharSequence
-                || notation instanceof Number
-                || notation instanceof Boolean) {
-            return (T) notation.toString();
-        }
-        if (notation instanceof Closure) {
-            final Closure closure = (Closure) notation;
-            final Object called = closure.call();
-            return parseNotation(called);
-        }
-        if (notation instanceof Callable) {
-            try {
-                final Callable callableNotation = (Callable) notation;
-                final Object called = callableNotation.call();
-                return parseNotation(called);
-            } catch (Exception e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-        DeprecationLogger.nagUserOfDeprecated(
-                String.format("Converting class %s to path using toString() method", notation.getClass().getName()),
-                "Please use java.io.File, java.lang.CharSequence, java.lang.Number, java.util.concurrent.Callable or groovy.lang.Closure"
-        );
-        return (T) notation.toString();
-    }
-}
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
deleted file mode 100644
index 429735d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/RelativizedCopySpec.java
+++ /dev/null
@@ -1,51 +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.file.copy;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.file.RelativePath;
-
-import static org.gradle.util.CollectionUtils.collect;
-
-public class RelativizedCopySpec extends DelegatingCopySpecInternal {
-
-    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/SingleParentCopySpec.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SingleParentCopySpec.java
new file mode 100644
index 0000000..d89a48a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/file/copy/SingleParentCopySpec.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.api.internal.file.copy;
+
+import org.gradle.api.file.DuplicatesStrategy;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+
+public class SingleParentCopySpec extends DefaultCopySpec {
+
+    CopySpecResolver parentResolver;
+
+    public SingleParentCopySpec(FileResolver resolver, Instantiator instantiator, CopySpecResolver parentResolver) {
+        super(resolver, instantiator);
+        this.parentResolver = parentResolver;
+    }
+
+    public CopySpecInternal addChild() {
+        DefaultCopySpec child = new SingleParentCopySpec(fileResolver, instantiator, buildResolverRelativeToParent(parentResolver));
+        childSpecs.add(child);
+        return child;
+    }
+
+    protected CopySpecInternal addChildAtPosition(int position) {
+        DefaultCopySpec child = instantiator.newInstance(SingleParentCopySpec.class, fileResolver, instantiator, buildResolverRelativeToParent(parentResolver));
+        childSpecs.add(position, child);
+        return child;
+    }
+
+    public boolean isCaseSensitive() {
+        return buildResolverRelativeToParent(parentResolver).isCaseSensitive();
+    }
+
+    public boolean getIncludeEmptyDirs() {
+        return buildResolverRelativeToParent(parentResolver).getIncludeEmptyDirs();
+    }
+
+    public DuplicatesStrategy getDuplicatesStrategy() {
+        return buildResolverRelativeToParent(parentResolver).getDuplicatesStrategy();
+    }
+
+    public Integer getDirMode() {
+        return buildResolverRelativeToParent(parentResolver).getDirMode();
+    }
+
+    public Integer getFileMode() {
+        return buildResolverRelativeToParent(parentResolver).getFileMode();
+    }
+
+}
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
deleted file mode 100644
index b83a92b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/GroupedAndNamedUniqueFileStore.java
+++ /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.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.internal.hash.HashUtil;
-
-import java.io.File;
-import java.util.Set;
-
-public class GroupedAndNamedUniqueFileStore<K> implements FileStore<K>, FileStoreSearcher<K> {
-
-    private PathKeyFileStore delegate;
-    private final TemporaryFileProvider temporaryFileProvider;
-    private final Transformer<String, K> grouper;
-    private final Transformer<String, K> namer;
-
-
-    public GroupedAndNamedUniqueFileStore(PathKeyFileStore delegate, TemporaryFileProvider temporaryFileProvider, Transformer<String, K> grouper, Transformer<String, K> namer) {
-        this.delegate = delegate;
-        this.temporaryFileProvider = temporaryFileProvider;
-        this.grouper = grouper;
-        this.namer = namer;
-    }
-
-    public LocallyAvailableResource move(K key, File source) {
-        return delegate.move(toPath(key, getChecksum(source)), source);
-    }
-
-    public LocallyAvailableResource copy(K key, File source) {
-        return delegate.copy(toPath(key, getChecksum(source)), source);
-    }
-
-    public Set<? extends LocallyAvailableResource> search(K key) {
-        return delegate.search(toPath(key, "*"));
-    }
-
-    protected String toPath(K key, String checksumPart) {
-        String group = grouper.transform(key);
-        String name = namer.transform(key);
-
-        return String.format("%s/%s/%s", group, checksumPart, name);
-    }
-
-    private String getChecksum(File contentFile) {
-        return HashUtil.createHash(contentFile, "SHA1").asHexString();
-    }
-
-    public File getTempFile() {
-        return temporaryFileProvider.createTemporaryFile("filestore", "bin");
-    }
-
-    public void moveFilestore(File destination) {
-        delegate.moveFilestore(destination);
-    }
-
-    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();
-        addAction.execute(tempFile);
-        final String groupedAndNamedKey = toPath(key, getChecksum(tempFile));
-        return delegate.move(groupedAndNamedKey, tempFile);
-    }
-}
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
deleted file mode 100644
index 8a2923e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathKeyFileStore.java
+++ /dev/null
@@ -1,202 +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.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.api.file.DeleteAction;
-import org.gradle.api.file.EmptyFileVisitor;
-import org.gradle.api.file.FileVisitDetails;
-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;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * File store that accepts the target path as the key for the entry.
- *
- * This implementation is explicitly NOT THREAD SAFE. Concurrent access must be organised externally.
- * <p>
- * There is always at most one entry for a given key for this file store. If an entry already exists at the given path, it will be overwritten.
- * Paths can contain directory components, which will be created on demand.
- * <p>
- * This file store is self repairing in so far that any files partially written before a fatal error will be ignored and
- * removed at a later time.
- * <p>
- * This file store also provides searching via relative ant path patterns.
- */
-public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<String> {
-
-    /*
-        When writing a file into the filestore a marker file with this suffix is written alongside,
-        then removed after the write. This is used to detect partially written files (due to a serious crash)
-        and to silently clean them.
-     */
-    public static final String IN_PROGRESS_MARKER_FILE_SUFFIX = ".fslck";
-
-    private File baseDir;
-    private final DeleteAction deleteAction = new DeleteActionImpl(new IdentityFileResolver());
-
-    public PathKeyFileStore(File baseDir) {
-        this.baseDir = baseDir;
-    }
-
-    protected File getBaseDir() {
-        return baseDir;
-    }
-
-    public LocallyAvailableResource move(String path, File source) {
-        return saveIntoFileStore(source, getFile(path), true);
-    }
-
-    public LocallyAvailableResource copy(String path, File source) {
-        return saveIntoFileStore(source, getFile(path), false);
-    }
-
-    private File getFile(String path) {
-        return new File(baseDir, path);
-    }
-
-    private File getFileWhileCleaningInProgress(String path) {
-        File file = getFile(path);
-        File markerFile = getInProgressMarkerFile(file);
-        if (markerFile.exists()) {
-            deleteAction.delete(file);
-            deleteAction.delete(markerFile);
-        }
-        return file;
-    }
-
-    public void moveFilestore(File destination) {
-        if (baseDir.exists()) {
-            GFileUtils.moveDirectory(baseDir, destination);
-        }
-        baseDir = destination;
-    }
-
-    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 LocallyAvailableResource saveIntoFileStore(final File source, final File destination, final boolean isMove) {
-        String verb = isMove ? "move" : "copy";
-
-        if (!source.exists()) {
-            throw new GradleException(String.format("Cannot %s '%s' into filestore @ '%s' as it does not exist", verb, source, destination));
-        }
-
-        String error = String.format("Failed to %s file '%s' into filestore at '%s' ", verb, source, destination);
-
-        return doAdd(destination, error, new Action<File>() {
-            public void execute(File file) {
-                if (isMove) {
-                    GFileUtils.moveFile(source, destination);
-                } else {
-                    GFileUtils.copyFile(source, destination);
-                }
-            }
-        });
-    }
-
-    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
-        try {
-            GFileUtils.parentMkdirs(destination);
-            File inProgressMarkerFile = getInProgressMarkerFile(destination);
-            GFileUtils.touch(inProgressMarkerFile);
-            try {
-                deleteAction.delete(destination);
-                action.execute(destination);
-            } catch (Throwable t) {
-                deleteAction.delete(destination);
-                throw t;
-            } finally {
-                deleteAction.delete(inProgressMarkerFile);
-            }
-        } catch (Throwable t) {
-            throw new GradleException(failureDescription, t);
-        }
-        return entryAt(destination);
-    }
-
-    public Set<? extends LocallyAvailableResource> search(String pattern) {
-        if (!getBaseDir().exists()) {
-            return Collections.emptySet();
-        }
-
-        final Set<LocallyAvailableResource> entries = new HashSet<LocallyAvailableResource>();
-        findFiles(pattern).visit(new EmptyFileVisitor() {
-            public void visitFile(FileVisitDetails fileDetails) {
-                final File file = fileDetails.getFile();
-                // We cannot clean in progress markers, or in progress files here because
-                // the file system visitor stuff can't handle the file system mutating while visiting
-                if (!isInProgressMarkerFile(file) && !isInProgressFile(file)) {
-                    entries.add(entryAt(file));
-                }
-            }
-        });
-
-        return entries;
-    }
-
-    private File getInProgressMarkerFile(File file) {
-        return new File(file.getParent(), file.getName() + IN_PROGRESS_MARKER_FILE_SUFFIX);
-    }
-
-    private boolean isInProgressMarkerFile(File file) {
-        return file.getName().endsWith(IN_PROGRESS_MARKER_FILE_SUFFIX);
-    }
-
-    private boolean isInProgressFile(File file) {
-        return getInProgressMarkerFile(file).exists();
-    }
-
-    private MinimalFileTree findFiles(String pattern) {
-        return new SingleIncludePatternFileTree(baseDir, pattern);
-    }
-
-    protected LocallyAvailableResource entryAt(File file) {
-        return entryAt(GFileUtils.relativePath(baseDir, file));
-    }
-
-    protected LocallyAvailableResource entryAt(final String path) {
-        return new AbstractLocallyAvailableResource() {
-            public File getFile() {
-                return new File(baseDir, path);
-            }
-        };
-    }
-
-    public LocallyAvailableResource get(String key) {
-        final File file = getFileWhileCleaningInProgress(key);
-        if (file.exists()) {
-            return entryAt(file);
-        } else {
-            return null;
-        }
-    }
-}
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
deleted file mode 100644
index be47d6e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStore.java
+++ /dev/null
@@ -1,66 +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.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;
-
-public class PathNormalisingKeyFileStore implements FileStore<String>, FileStoreSearcher<String> {
-
-    private final PathKeyFileStore delegate;
-
-    public PathNormalisingKeyFileStore(File baseDir) {
-        this(new PathKeyFileStore(baseDir));
-    }
-
-    public PathNormalisingKeyFileStore(PathKeyFileStore delegate) {
-        this.delegate = delegate;
-    }
-
-    public LocallyAvailableResource move(String key, File source) {
-        return delegate.move(normalizePath(key), source);
-    }
-
-    public LocallyAvailableResource copy(String key, File source) {
-        return delegate.copy(key, source);
-    }
-
-    protected String normalizePath(String path) {
-        return path.replaceAll("[^\\d\\w\\./]", "_");
-    }
-
-    protected String normalizeSearchPath(String path) {
-        return path.replaceAll("[^\\d\\w\\.\\*/]", "_");
-    }
-
-    public void moveFilestore(File destination) {
-        delegate.moveFilestore(destination);
-    }
-
-    public LocallyAvailableResource add(String key, Action<File> addAction) {
-        return delegate.add(normalizePath(key), addAction);
-    }
-
-    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
deleted file mode 100644
index 4ba34b0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStore.java
+++ /dev/null
@@ -1,52 +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.api.Action;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-
-/**
- * Assumes that files do not need to be replaced in the filestore.
- *
- * Can be used as an optimisation if path contains a checksum of the file, as there is no point to perform the replace in that circumstance.
- */
-public class UniquePathKeyFileStore extends PathKeyFileStore {
-
-    public UniquePathKeyFileStore(File baseDir) {
-        super(baseDir);
-    }
-
-    @Override
-    public LocallyAvailableResource move(String path, File source) {
-        LocallyAvailableResource entry = super.move(path, source);
-        if (source.exists()) {
-            GFileUtils.deleteQuietly(source);
-        }
-        return entry;
-    }
-
-    @Override
-    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
-        if (destination.exists()) {
-            return entryAt(destination);
-        }
-        return super.doAdd(destination, failureDescription, action);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/html/SimpleHtmlWriter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/html/SimpleHtmlWriter.java
deleted file mode 100644
index afd493e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/html/SimpleHtmlWriter.java
+++ /dev/null
@@ -1,41 +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.html;
-
-import org.gradle.api.internal.xml.SimpleMarkupWriter;
-
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * <p>A streaming HTML writer.</p>
- */
-public class SimpleHtmlWriter extends SimpleMarkupWriter {
-
-    public SimpleHtmlWriter(Writer writer) throws IOException {
-        this(writer, null);
-    }
-
-    public SimpleHtmlWriter(Writer writer, String indent) throws IOException {
-        super(writer, indent);
-        writeHtmlHeader();
-    }
-
-    private void writeHtmlHeader() throws IOException {
-        writeRaw("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/BasicDomainObjectContext.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/BasicDomainObjectContext.java
new file mode 100644
index 0000000..52856c4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/BasicDomainObjectContext.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization;
+
+import org.gradle.api.internal.DomainObjectContext;
+
+public class BasicDomainObjectContext implements DomainObjectContext {
+    public String absoluteProjectPath(String name) {
+        return name;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderCache.java
deleted file mode 100644
index c7bf7e6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderCache.java
+++ /dev/null
@@ -1,27 +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.initialization;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.classloader.FilteringClassLoader;
-import org.gradle.internal.classpath.ClassPath;
-
-public interface ClassLoaderCache {
-
-    ClassLoader get(ClassLoader parent, ClassPath classPath, @Nullable FilteringClassLoader.Spec filterSpec);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderIds.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderIds.java
new file mode 100644
index 0000000..f7ba42a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderIds.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization;
+
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
+
+import java.util.Arrays;
+
+/**
+ * Provides implementations of classloader ids
+ */
+public class ClassLoaderIds {
+
+    public enum Type {
+        SCRIPT,
+        TEST_TASK_CLASSPATH
+    }
+
+    private static ClassLoaderId of(Type type, String... attributes) {
+        return new DefaultClassLoaderId(type, attributes);
+    }
+
+    public static ClassLoaderId buildScript(String fileName, String operationId) {
+        return of(Type.SCRIPT, fileName, operationId);
+    }
+
+    public static ClassLoaderId testTaskClasspath(String testTaskPath) {
+        return of(Type.TEST_TASK_CLASSPATH, testTaskPath);
+    }
+
+    private static class DefaultClassLoaderId implements ClassLoaderId {
+        private final Type type;
+        private final String[] attributes;
+
+        public DefaultClassLoaderId(Type type, String[] attributes) {
+            this.type = type;
+            this.attributes = attributes;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            DefaultClassLoaderId that = (DefaultClassLoaderId) o;
+
+            return type == that.type && Arrays.equals(attributes, that.attributes);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = type.hashCode();
+            result = 31 * result + Arrays.hashCode(attributes);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "ClassLoaderId{type=" + type + ", attributes=" + Arrays.toString(attributes) + '}';
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScope.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScope.java
index 8cceee6..b94db6c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScope.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScope.java
@@ -21,66 +21,70 @@ import org.gradle.internal.classpath.ClassPath;
 /**
  * Represents a particular node in the ClassLoader graph.
  *
- * Certain domain objects (e.g. Gradle, Settings, Project) have an associated class loader scope.
- * This is used for evaluating associated scripts and script plugins.
+ * Certain domain objects (e.g. Gradle, Settings, Project) have an associated class loader scope. This is used for evaluating associated scripts and script plugins.
  *
- * Use of this class allows class loader creation to be lazy, and potentially optimised.
- * It also provides a central location for class loader reuse.
+ * Use of this class allows class loader creation to be lazy, and potentially optimised. It also provides a central location for class loader reuse.
  */
 public interface ClassLoaderScope {
 
     /**
-     * The effective class loader for this scope.
-     *
-     * It is strongly preferable to only call this after {@link #lock()}ing the scope as it allows the structure to be optimized.
+     * The classloader for use at this node.
+     * <p>
+     * Contains exported classes of the parent scope and all local and exported additions to this scope.
+     * It is strongly preferable to only call this after {@link #lock() locking} the scope as it allows the structure to be optimized.
      */
-    ClassLoader getScopeClassLoader();
+    ClassLoader getLocalClassLoader();
 
     /**
-     * The class loader that children inherit.
+     * The classloader for use by child nodes.
+     * <p>
+     * Contains exported classes of the parent scope and all local and exported additions to this scope.
+     * It is strongly preferable to only call this after {@link #lock() locking} the scope as it allows the structure to be optimized.
      */
-    ClassLoader getChildClassLoader();
+    ClassLoader getExportClassLoader();
 
     /**
-     * The base scope defines the parent for local additions.
+     * The parent of this scope.
      */
-    ClassLoaderScope getBase();
+    ClassLoaderScope getParent();
 
     /**
-     * Adds a class path to this scope, but not to children.
-     *
-     * Can not be called after being locked.
+     * Returns true if this scope defines the given Class. That is, the class is local and/or exported by this scope and not inherited from
+     * some parent.
      */
-    ClassLoader addLocal(ClassPath classpath);
+    boolean defines(Class<?> clazz);
 
     /**
-     * Adds a class path to this scope, but not to children.
+     * Makes the provided classes visible to this scope, but not to children. The classes are loaded in their own ClassLoader whose parent is the export
+     * ClassLoader of the parent scope.
      *
-     * Can not be called after being locked.
-     */
-    ClassLoader export(ClassPath classpath);
-
-    /**
-     * Creates a scope with the same parent as this scope.
+     * <p>Can not be called after being locked.
+     *
+     * @return this
      */
-    ClassLoaderScope createSibling();
+    ClassLoaderScope local(ClassPath classPath);
 
     /**
-     * Creates a scope with the same parent as this scope.
+     * Makes the provided classes visible to this scope and its children. The classes are loaded in their own ClassLoader whose parent is the export ClassLoader
+     * of the parent scope.
      *
-     * Local class paths added to the return child will NOT inherit from the exported classpath of this and parents (though exported class paths will)
+     * <p>Can not be called after being locked.
+     *
+     * @return this
      */
-    ClassLoaderScope createChild();
+    ClassLoaderScope export(ClassPath classPath);
 
     /**
-     * Creates a scope with the same parent as this scope.
+     * Creates a scope with this scope as parent.
      *
-     * Local class paths added to the return child WILL inherit from the exported classpath of this and parents (exported class paths also will)
+     * @param id an identifier for the child loader
      */
-    ClassLoaderScope createRebasedChild();
+    ClassLoaderScope createChild(String id);
 
     /**
      * Signal that no more modifications are to come, allowing the structure to be optimised if possible.
+     *
+     * @return this
      */
     ClassLoaderScope lock();
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifier.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifier.java
new file mode 100644
index 0000000..2699b72
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifier.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
+
+import java.util.List;
+
+class ClassLoaderScopeIdentifier {
+
+    private final ClassLoaderScopeIdentifier parent;
+    private final String name;
+
+    public ClassLoaderScopeIdentifier(ClassLoaderScopeIdentifier parent, String name) {
+        this.parent = parent;
+        this.name = name;
+    }
+
+    public ClassLoaderScopeIdentifier child(String name) {
+        return new ClassLoaderScopeIdentifier(this, name);
+    }
+
+    ClassLoaderId localId() {
+        return new Id(this, false);
+    }
+
+    ClassLoaderId exportId() {
+        return new Id(this, true);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ClassLoaderScopeIdentifier that = (ClassLoaderScopeIdentifier) o;
+
+        return name.equals(that.name) && !(parent != null ? !parent.equals(that.parent) : that.parent != null);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = parent != null ? parent.hashCode() : 0;
+        result = 31 * result + name.hashCode();
+        return result;
+    }
+
+    private String getPath() {
+        List<String> names = Lists.newLinkedList();
+        names.add(name);
+        ClassLoaderScopeIdentifier nextParent = parent;
+        while (nextParent != null) {
+            names.add(0, nextParent.name);
+            nextParent = nextParent.parent;
+        }
+        return Joiner.on(":").join(names);
+    }
+
+    @Override
+    public String toString() {
+        return "ClassLoaderScopeIdentifier{" + getPath() + "}";
+    }
+
+    private static class Id implements ClassLoaderId {
+        private final ClassLoaderScopeIdentifier identifier;
+        private final boolean export;
+
+        public Id(ClassLoaderScopeIdentifier identifier, boolean export) {
+            this.identifier = identifier;
+            this.export = export;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Id id = (Id) o;
+            return export == id.export && identifier.equals(id.identifier);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = identifier.hashCode();
+            result = 31 * result + (export ? 1 : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "ClassLoaderScopeIdentifier.Id{" + identifier.getPath() + "(" + (export ? "export" : "local") + ")}";
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCache.java
deleted file mode 100644
index a2e2d84..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCache.java
+++ /dev/null
@@ -1,102 +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.initialization;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import org.gradle.api.Nullable;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.classloader.FilteringClassLoader;
-import org.gradle.internal.classpath.ClassPath;
-
-import java.net.URLClassLoader;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-
-public class DefaultClassLoaderCache implements ClassLoaderCache {
-
-    public static class Key {
-        private final ClassLoader parent;
-        private final ClassPath classPath;
-        private final FilteringClassLoader.Spec filterSpec;
-
-        private Key(ClassLoader parent, ClassPath classPath, FilteringClassLoader.Spec filterSpec) {
-            this.parent = parent;
-            this.classPath = classPath;
-            this.filterSpec = filterSpec;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Key key = (Key) o;
-
-            if (!classPath.equals(key.classPath)) {
-                return false;
-            }
-            if (filterSpec != null ? !filterSpec.equals(key.filterSpec) : key.filterSpec != null) {
-                return false;
-            }
-            if (!parent.equals(key.parent)) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = parent.hashCode();
-            result = 31 * result + classPath.hashCode();
-            result = 31 * result + (filterSpec != null ? filterSpec.hashCode() : 0);
-            return result;
-        }
-    }
-
-    private final Cache<Key, ClassLoader> cache;
-
-    public DefaultClassLoaderCache() {
-        this(CacheBuilder.newBuilder().<Key, ClassLoader>build());
-    }
-
-    public DefaultClassLoaderCache(Cache<Key, ClassLoader> cache) {
-        this.cache = cache;
-    }
-
-    public ClassLoader get(final ClassLoader parent, final ClassPath classPath, @Nullable final FilteringClassLoader.Spec filterSpec) {
-        try {
-            return cache.get(new Key(parent, classPath, filterSpec), new Callable<ClassLoader>() {
-                public ClassLoader call() throws Exception {
-                    if (filterSpec == null) {
-                        return new URLClassLoader(classPath.getAsURLArray(), parent);
-                    } else {
-                        return new FilteringClassLoader(get(parent, classPath, null), filterSpec);
-                    }
-                }
-            });
-        } catch (ExecutionException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScope.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScope.java
index 502bfb9..7e0f82f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScope.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScope.java
@@ -16,9 +16,12 @@
 
 package org.gradle.api.internal.initialization;
 
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.internal.classloader.CachingClassLoader;
 import org.gradle.internal.classloader.MultiParentClassLoader;
 import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.DefaultClassPath;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -27,133 +30,153 @@ public class DefaultClassLoaderScope implements ClassLoaderScope {
 
     public static final String STRICT_MODE_PROPERTY = "org.gradle.classloaderscope.strict";
 
+    final ClassLoaderScopeIdentifier id;
     private final ClassLoaderScope parent;
-    private final ClassLoaderScope base;
     private final ClassLoaderCache classLoaderCache;
 
     private boolean locked;
 
-    private List<ClassLoader> local;
-
-    private ClassLoader scopeClassLoader;
+    private ClassPath export = new DefaultClassPath();
+    private ClassPath local = new DefaultClassPath();
+    private List<ClassLoader> ownLoaders = new ArrayList<ClassLoader>();
 
+    // If these are not null, we are pessimistic (loaders asked for before locking)
     private MultiParentClassLoader exportingClassLoader;
     private MultiParentClassLoader localClassLoader;
-    private ClassLoader childrenParent;
 
-    public DefaultClassLoaderScope(ClassLoaderScope parent, ClassLoaderScope base, ClassLoaderCache classLoaderCache) {
+    // What is actually exposed
+    private ClassLoader effectiveLocalClassLoader;
+    private ClassLoader effectiveExportClassLoader;
+
+    public DefaultClassLoaderScope(ClassLoaderScopeIdentifier id, ClassLoaderScope parent, ClassLoaderCache classLoaderCache) {
+        this.id = id;
         this.parent = parent;
-        this.base = base;
         this.classLoaderCache = classLoaderCache;
     }
 
-    public ClassLoader getChildClassLoader() {
-        getScopeClassLoader(); // trigger calculation
-        return childrenParent;
+    private ClassLoader buildLockedLoader(ClassLoaderId id, ClassPath classPath) {
+        if (classPath.isEmpty()) {
+            return parent.getExportClassLoader();
+        }
+        return loader(id, classPath);
     }
 
-    public ClassLoaderScope getBase() {
-        return base;
+    private ClassLoader buildLockedLoader(ClassLoaderId id, ClassLoader additional, ClassPath classPath) {
+        if (classPath.isEmpty()) {
+            return additional;
+        }
+        return new CachingClassLoader(new MultiParentClassLoader(additional, loader(id, classPath)));
     }
 
-    public ClassLoader getScopeClassLoader() {
-        if (scopeClassLoader == null) {
+    private void buildEffectiveLoaders() {
+        if (effectiveLocalClassLoader == null) {
             if (locked) {
-                if (local == null && exportingClassLoader == null) { // best case, no additions
-                    scopeClassLoader = parent.getChildClassLoader();
-                    childrenParent = scopeClassLoader;
-                } else if (exportingClassLoader == null) { // no impact on children
-                    localClassLoader = new MultiParentClassLoader(parent.getChildClassLoader());
-                    scopeClassLoader = new CachingClassLoader(localClassLoader);
-                    childrenParent = parent.getChildClassLoader();
-                } else if (local == null) {
-                    scopeClassLoader = exportingClassLoader;
-                    childrenParent = scopeClassLoader;
+                if (local.isEmpty() && export.isEmpty()) {
+                    classLoaderCache.remove(id.localId());
+                    classLoaderCache.remove(id.exportId());
+                    effectiveLocalClassLoader = parent.getExportClassLoader();
+                    effectiveExportClassLoader = parent.getExportClassLoader();
+                } else if (export.isEmpty()) {
+                    classLoaderCache.remove(id.exportId());
+                    effectiveLocalClassLoader = buildLockedLoader(id.localId(), local);
+                    effectiveExportClassLoader = parent.getExportClassLoader();
+                } else if (local.isEmpty()) {
+                    classLoaderCache.remove(id.localId());
+                    effectiveLocalClassLoader = buildLockedLoader(id.exportId(), export);
+                    effectiveExportClassLoader = effectiveLocalClassLoader;
                 } else {
-                    createFlexibleLoaderStructure();
+                    effectiveExportClassLoader = buildLockedLoader(id.exportId(), export);
+                    effectiveLocalClassLoader = buildLockedLoader(id.localId(), effectiveExportClassLoader, local);
                 }
             } else { // creating before locking, have to create the most flexible setup
                 if (Boolean.getBoolean(STRICT_MODE_PROPERTY)) {
                     throw new IllegalStateException("Attempt to define scope class loader before scope is locked");
                 }
 
-                createFlexibleLoaderStructure();
-            }
+                exportingClassLoader = new MultiParentClassLoader(parent.getExportClassLoader(), loader(id.exportId(), export));
+                effectiveExportClassLoader = new CachingClassLoader(exportingClassLoader);
 
-            if (local != null) {
-                for (ClassLoader localClassLoader : local) {
-                    addLocal(localClassLoader);
-                }
+                localClassLoader = new MultiParentClassLoader(effectiveExportClassLoader, loader(id.localId(), local));
+                effectiveLocalClassLoader = new CachingClassLoader(localClassLoader);
             }
-        }
 
-        return scopeClassLoader;
+            export = null;
+            local = null;
+        }
     }
 
-    private void addLocal(ClassLoader newClassLoader) {
-        assert localClassLoader != null;
-        localClassLoader.addParent(newClassLoader);
+    public ClassLoader getExportClassLoader() {
+        buildEffectiveLoaders();
+        return effectiveExportClassLoader;
     }
 
-    private void createFlexibleLoaderStructure() {
-        if (exportingClassLoader == null) {
-            exportingClassLoader = new MultiParentClassLoader(parent.getChildClassLoader());
-        }
+    public ClassLoader getLocalClassLoader() {
+        buildEffectiveLoaders();
+        return effectiveLocalClassLoader;
+    }
 
-        localClassLoader = new MultiParentClassLoader(exportingClassLoader);
-        scopeClassLoader = new CachingClassLoader(localClassLoader);
-        childrenParent = exportingClassLoader;
+    public ClassLoaderScope getParent() {
+        return parent;
     }
 
-    public ClassLoader addLocal(ClassPath classpath) {
-        if (locked) {
-            throw new IllegalStateException("class loader scope is locked");
-        }
-        if (!classpath.isEmpty()) {
-            if (local == null) {
-                local = new ArrayList<ClassLoader>(1);
+    @Override
+    public boolean defines(Class<?> clazz) {
+        for (ClassLoader ownLoader : ownLoaders) {
+            if (ownLoader.equals(clazz.getClassLoader())) {
+                return true;
             }
+        }
+        return false;
+    }
 
-            ClassLoader newClassLoader = classLoaderCache.get(base.getChildClassLoader(), classpath, null);
-            local.add(newClassLoader);
+    private ClassLoader loader(ClassLoaderId id, ClassPath classPath) {
+        ClassLoader classLoader = classLoaderCache.get(id, classPath, parent.getExportClassLoader(), null);
+        ownLoaders.add(classLoader);
+        return classLoader;
+    }
 
-            if (localClassLoader != null) { // classloader was eagerly created, have to add
-                addLocal(newClassLoader);
-            }
+    public ClassLoaderScope local(ClassPath classPath) {
+        if (classPath.isEmpty()) {
+            return this;
+        }
 
-            return newClassLoader;
+        assertNotLocked();
+        if (localClassLoader != null) {
+            ClassLoader loader = loader(id.localId(), classPath);
+            localClassLoader.addParent(loader);
         } else {
-            return base.getChildClassLoader();
+            local = local.plus(classPath);
         }
+
+        return this;
     }
 
-    public ClassLoader export(ClassPath classpath) {
-        if (locked) {
-            throw new IllegalStateException("class loader scope is locked");
+    public ClassLoaderScope export(ClassPath classPath) {
+        if (classPath.isEmpty()) {
+            return this;
         }
-        if (classpath.isEmpty()) {
-            return parent.getChildClassLoader();
-        } else {
-            if (exportingClassLoader == null) {
-                exportingClassLoader = new MultiParentClassLoader();
-            }
 
-            ClassLoader classLoader = classLoaderCache.get(parent.getChildClassLoader(), classpath, null);
-            exportingClassLoader.addParent(classLoader);
-            return classLoader;
+        assertNotLocked();
+        if (exportingClassLoader != null) {
+            exportingClassLoader.addParent(loader(id.exportId(), classPath));
+        } else {
+            export = export.plus(classPath);
         }
-    }
 
-    public ClassLoaderScope createSibling() {
-        return new DefaultClassLoaderScope(parent, base, classLoaderCache);
+        return this;
     }
 
-    public ClassLoaderScope createChild() {
-        return new DefaultClassLoaderScope(this, base, classLoaderCache);
+    private void assertNotLocked() {
+        if (locked) {
+            throw new IllegalStateException("class loader scope is locked");
+        }
     }
 
-    public ClassLoaderScope createRebasedChild() {
-        return new DefaultClassLoaderScope(this, this, classLoaderCache);
+    public ClassLoaderScope createChild(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("'name' cannot be null");
+        }
+        return new DefaultClassLoaderScope(id.child(name), this, classLoaderCache);
     }
 
     public ClassLoaderScope lock() {
@@ -164,5 +187,4 @@ public class DefaultClassLoaderScope implements ClassLoaderScope {
     public boolean isLocked() {
         return locked;
     }
-
 }
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 05e64a3..978ca17 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
@@ -60,9 +60,4 @@ public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
         return new DefaultScriptHandler(scriptSource, repositoryHandler, dependencyHandler, configurationContainer, new ScriptHandlerClassLoaderFactory(scriptSource, classLoaderScope));
     }
 
-    private static class BasicDomainObjectContext implements DomainObjectContext {
-        public String absoluteProjectPath(String name) {
-            return name;
-        }
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/RootClassLoaderScope.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/RootClassLoaderScope.java
index 3fe14b9..b3be6fb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/RootClassLoaderScope.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/RootClassLoaderScope.java
@@ -16,48 +16,53 @@
 
 package org.gradle.api.internal.initialization;
 
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
 import org.gradle.internal.classpath.ClassPath;
 
 public class RootClassLoaderScope implements ClassLoaderScope {
 
-    private final ClassLoader classLoader;
+    private final ClassLoader localClassLoader;
+    private final ClassLoader exportClassLoader;
     private final ClassLoaderCache classLoaderCache;
+    private final ClassLoaderScopeIdentifier id;
 
-    public RootClassLoaderScope(ClassLoader classLoader, ClassLoaderCache classLoaderCache) {
-        this.classLoader = classLoader;
+    public RootClassLoaderScope(ClassLoader localClassLoader, ClassLoader exportClassLoader, ClassLoaderCache classLoaderCache) {
+        this.localClassLoader = localClassLoader;
+        this.exportClassLoader = exportClassLoader;
         this.classLoaderCache = classLoaderCache;
+        this.id = new ClassLoaderScopeIdentifier(null, "root");
     }
 
-    public ClassLoader getScopeClassLoader() {
-        return classLoader;
+    public ClassLoader getLocalClassLoader() {
+        return localClassLoader;
     }
 
-    public ClassLoader getChildClassLoader() {
-        return classLoader;
+    public ClassLoader getExportClassLoader() {
+        return exportClassLoader;
     }
 
-    public ClassLoaderScope getBase() {
-        return this;
+    public ClassLoaderScope getParent() {
+        return this; // should this be null?
     }
 
-    public ClassLoader addLocal(ClassPath classpath) {
-        throw new UnsupportedOperationException("root class loader scope is immutable");
+    @Override
+    public boolean defines(Class<?> clazz) {
+        return localClassLoader.equals(clazz.getClassLoader()) || exportClassLoader.equals(clazz.getClassLoader());
     }
 
-    public ClassLoader export(ClassPath classpath) {
+    public ClassLoaderScope local(ClassPath classPath) {
         throw new UnsupportedOperationException("root class loader scope is immutable");
     }
 
-    public ClassLoaderScope createSibling() {
-        return createChild();
-    }
-
-    public ClassLoaderScope createChild() {
-        return new DefaultClassLoaderScope(this, this, classLoaderCache);
+    public ClassLoaderScope export(ClassPath classPath) {
+        throw new UnsupportedOperationException("root class loader scope is immutable");
     }
 
-    public ClassLoaderScope createRebasedChild() {
-        return createChild();
+    public ClassLoaderScope createChild(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("'name' cannot be null");
+        }
+        return new DefaultClassLoaderScope(id.child(name), this, classLoaderCache);
     }
 
     public ClassLoaderScope lock() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ScriptHandlerClassLoaderFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ScriptHandlerClassLoaderFactory.java
index 0dc4fc7..70b508f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ScriptHandlerClassLoaderFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/ScriptHandlerClassLoaderFactory.java
@@ -37,7 +37,7 @@ public class ScriptHandlerClassLoaderFactory implements Factory<ClassLoader> {
         if (!classLoaderScope.isLocked()) {
             LOGGER.debug("Eager creation of script class loader for {}. This may result in performance issues.", scriptSource);
         }
-        return classLoaderScope.getScopeClassLoader();
+        return classLoaderScope.getLocalClassLoader();
     }
 
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderCache.java
new file mode 100644
index 0000000..51cb9f2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderCache.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.initialization.loadercache;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.classloader.FilteringClassLoader;
+import org.gradle.internal.classpath.ClassPath;
+
+public interface ClassLoaderCache {
+
+    int size();
+
+    ClassLoader get(ClassLoaderId id, ClassPath classPath, @Nullable ClassLoader parent, @Nullable FilteringClassLoader.Spec filterSpec);
+
+    void remove(ClassLoaderId id);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderId.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderId.java
new file mode 100644
index 0000000..cce6148
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassLoaderId.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+/**
+ * Opaque identifier of the classloader. Needed for correct behavior of classloader invalidation.
+ */
+public interface ClassLoaderId {
+    public boolean equals(Object o);
+    public int hashCode();
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshot.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshot.java
new file mode 100644
index 0000000..40adb7f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshot.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+/**
+ * Represents the snapshot of given classpath
+ */
+public interface ClassPathSnapshot {
+    boolean equals(Object other);
+    int hashCode();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshotter.java
new file mode 100644
index 0000000..dd73f85
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/ClassPathSnapshotter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+import org.gradle.internal.classpath.ClassPath;
+
+/**
+ * Creates snapshots of classpaths
+ */
+public interface ClassPathSnapshotter {
+     ClassPathSnapshot snapshot(ClassPath classPath);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCache.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCache.java
new file mode 100644
index 0000000..5b45573
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCache.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import org.gradle.api.Nullable;
+import org.gradle.internal.classloader.FilteringClassLoader;
+import org.gradle.internal.classpath.ClassPath;
+
+import java.net.URLClassLoader;
+import java.util.Map;
+
+public class DefaultClassLoaderCache implements ClassLoaderCache {
+
+    private final Object lock = new Object();
+    private final Map<ClassLoaderId, CachedClassLoader> byId = Maps.newHashMap();
+    private final Map<ClassLoaderSpec, CachedClassLoader> bySpec = Maps.newHashMap();
+    private final ClassPathSnapshotter snapshotter;
+
+    public DefaultClassLoaderCache(ClassPathSnapshotter snapshotter) {
+        this.snapshotter = snapshotter;
+    }
+
+    public ClassLoader get(ClassLoaderId id, ClassPath classPath, ClassLoader parent, @Nullable FilteringClassLoader.Spec filterSpec) {
+        ClassPathSnapshot classPathSnapshot = snapshotter.snapshot(classPath);
+        ClassLoaderSpec spec = new ClassLoaderSpec(parent, classPathSnapshot, filterSpec);
+
+        synchronized (lock) {
+            CachedClassLoader cachedLoader = byId.get(id);
+            if (cachedLoader == null || !cachedLoader.is(spec)) {
+                CachedClassLoader newLoader = getAndRetainLoader(classPath, spec, id);
+                byId.put(id, newLoader);
+
+                if (cachedLoader != null) {
+                    cachedLoader.release(id);
+                }
+
+                return newLoader.classLoader;
+            } else {
+                return cachedLoader.classLoader;
+            }
+        }
+    }
+
+    @Override
+    public void remove(ClassLoaderId id) {
+        CachedClassLoader cachedClassLoader = byId.remove(id);
+        if (cachedClassLoader != null) {
+            cachedClassLoader.release(id);
+        }
+    }
+
+    private CachedClassLoader getAndRetainLoader(ClassPath classPath, ClassLoaderSpec spec, ClassLoaderId id) {
+        CachedClassLoader cachedLoader = bySpec.get(spec);
+        if (cachedLoader == null) {
+            ClassLoader classLoader;
+            CachedClassLoader parentCachedLoader = null;
+            if (spec.isFiltered()) {
+                parentCachedLoader = getAndRetainLoader(classPath, spec.unfiltered(), id);
+                classLoader = new FilteringClassLoader(parentCachedLoader.classLoader, spec.filterSpec);
+            } else {
+                classLoader = new URLClassLoader(classPath.getAsURLArray(), spec.parent);
+            }
+            cachedLoader = new CachedClassLoader(classLoader, spec, parentCachedLoader);
+            bySpec.put(spec, cachedLoader);
+        }
+
+        return cachedLoader.retain(id);
+    }
+
+    @Override
+    public int size() {
+        return bySpec.size();
+    }
+
+    private static class ClassLoaderSpec {
+        private final ClassLoader parent;
+        private final ClassPathSnapshot classPathSnapshot;
+        private final FilteringClassLoader.Spec filterSpec;
+
+        public ClassLoaderSpec(ClassLoader parent, ClassPathSnapshot classPathSnapshot, FilteringClassLoader.Spec filterSpec) {
+            this.parent = parent;
+            this.classPathSnapshot = classPathSnapshot;
+            this.filterSpec = filterSpec;
+        }
+
+        public ClassLoaderSpec unfiltered() {
+            return new ClassLoaderSpec(parent, classPathSnapshot, null);
+        }
+
+        public boolean isFiltered() {
+            return filterSpec != null;
+        }
+
+        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
+        @Override
+        public boolean equals(Object o) {
+            ClassLoaderSpec that = (ClassLoaderSpec) o;
+            return Objects.equal(this.parent, that.parent)
+                    && this.classPathSnapshot.equals(that.classPathSnapshot)
+                    && Objects.equal(this.filterSpec, that.filterSpec);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = classPathSnapshot.hashCode();
+            result = 31 * result + (filterSpec != null ? filterSpec.hashCode() : 0);
+            result = 31 * result + (parent != null ? parent.hashCode() : 0);
+            return result;
+        }
+    }
+
+    private class CachedClassLoader {
+        private final ClassLoader classLoader;
+        private final ClassLoaderSpec spec;
+        private final CachedClassLoader parent;
+        private final Multiset<ClassLoaderId> usedBy = HashMultiset.create();
+
+        private CachedClassLoader(ClassLoader classLoader, ClassLoaderSpec spec, @Nullable CachedClassLoader parent) {
+            this.classLoader = classLoader;
+            this.spec = spec;
+            this.parent = parent;
+        }
+
+        public boolean is(ClassLoaderSpec spec) {
+            return this.spec.equals(spec);
+        }
+
+        public CachedClassLoader retain(ClassLoaderId loaderId) {
+            usedBy.add(loaderId);
+            return this;
+        }
+
+        public void release(ClassLoaderId loaderId) {
+            if (usedBy.isEmpty()) {
+                throw new IllegalStateException("Cannot release already released classloader: " + classLoader);
+            }
+
+            if (usedBy.remove(loaderId)) {
+                if (usedBy.isEmpty()) {
+                    if (parent != null) {
+                        parent.release(loaderId);
+                    }
+                    bySpec.remove(spec);
+                }
+            } else {
+                throw new IllegalStateException("Classloader '" + this + "' not used by '" + loaderId + "'");
+            }
+        }
+    }
+
+    // Used in org.gradle.api.internal.initialization.loadercache.ClassLoadersCachingIntegrationTest
+    @SuppressWarnings("UnusedDeclaration")
+    public void assertInternalIntegrity() {
+        Map<ClassLoaderId, CachedClassLoader> orphaned = Maps.newHashMap();
+        for (Map.Entry<ClassLoaderId, CachedClassLoader> entry : byId.entrySet()) {
+            if (!bySpec.containsKey(entry.getValue().spec)) {
+                orphaned.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        if (!orphaned.isEmpty()) {
+            throw new IllegalStateException("The following class loaders are orphaned: " + Joiner.on(",").withKeyValueSeparator(":").join(orphaned));
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/FileClassPathSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/FileClassPathSnapshotter.java
new file mode 100644
index 0000000..87eaa2f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/FileClassPathSnapshotter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+import org.gradle.internal.classpath.ClassPath;
+
+/**
+ * Creates snapshot based on file paths.
+ */
+public class FileClassPathSnapshotter implements ClassPathSnapshotter {
+    public ClassPathSnapshot snapshot(final ClassPath classPath) {
+        return new ClassPathSnapshotImpl(classPath);
+    }
+
+    private class ClassPathSnapshotImpl implements ClassPathSnapshot {
+        private final ClassPath classPath;
+        public ClassPathSnapshotImpl(ClassPath classPath) {
+            assert classPath != null;
+            this.classPath = classPath;
+        }
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof ClassPathSnapshotImpl)) {
+                return false;
+            }
+
+            ClassPathSnapshotImpl that = (ClassPathSnapshotImpl) o;
+            return classPath.equals(that.classPath);
+        }
+        public int hashCode() {
+            return classPath.hashCode();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotter.java
new file mode 100644
index 0000000..b532b2f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.Adler32;
+
+public class HashClassPathSnapshotter implements ClassPathSnapshotter {
+
+    private final FileSnapshotter fileSnapshotter;
+
+    public HashClassPathSnapshotter(FileSnapshotter fileSnapshotter) {
+        this.fileSnapshotter = fileSnapshotter;
+    }
+
+    public ClassPathSnapshot snapshot(ClassPath classPath) {
+        List<String> visitedFilePaths = Lists.newLinkedList();
+        Set<File> visitedDirs = Sets.newLinkedHashSet();
+        List<File> cpFiles = classPath.getAsFiles();
+
+        Adler32 checksum = new Adler32();
+        hash(checksum, visitedFilePaths, visitedDirs, cpFiles.iterator());
+        return new ClassPathSnapshotImpl(visitedFilePaths, checksum.getValue());
+    }
+
+    private void hash(Adler32 combinedHash, List<String> visitedFilePaths, Set<File> visitedDirs, Iterator<File> toHash) {
+        while (toHash.hasNext()) {
+            File file = toHash.next();
+            file = GFileUtils.canonicalise(file);
+            if (file.isDirectory()) {
+                if (visitedDirs.add(file)) {
+                    //in theory, awkward symbolic links can lead to recursion problems.
+                    //TODO - figure out a way to test it. I only tested it 'manually' and the feature is needed.
+                    hash(combinedHash, visitedFilePaths, visitedDirs, Iterators.forArray(file.listFiles()));
+                }
+            } else if (file.isFile()) {
+                visitedFilePaths.add(file.getAbsolutePath());
+                combinedHash.update(fileSnapshotter.snapshot(file).getHash());
+            }
+            //else an empty folder - a legit situation
+        }
+    }
+
+    private static class ClassPathSnapshotImpl implements ClassPathSnapshot {
+        private final List<String> files;
+        private final long hash;
+
+        public ClassPathSnapshotImpl(List<String> files, long hash) {
+            assert files != null;
+
+            this.files = files;
+            this.hash = hash;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            ClassPathSnapshotImpl that = (ClassPathSnapshotImpl) o;
+
+            return hash == that.hash && files.equals(that.files);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = files.hashCode();
+            result = 31 * result + (int) (hash ^ (hash >>> 32));
+            return result;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultAppliedPlugin.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultAppliedPlugin.java
new file mode 100644
index 0000000..85fdde8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultAppliedPlugin.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.AppliedPlugin;
+import org.gradle.plugin.internal.PluginId;
+
+class DefaultAppliedPlugin implements AppliedPlugin {
+
+    private final PluginId pluginId;
+
+    public DefaultAppliedPlugin(PluginId pluginId) {
+        this.pluginId = pluginId;
+    }
+
+    public String getId() {
+        return pluginId.toString();
+    }
+
+    public String getNamespace() {
+        return pluginId.getNamespace();
+    }
+
+    public String getName() {
+        return pluginId.getName();
+    }
+
+}
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 420749d..8d0966b 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
@@ -25,7 +25,6 @@ import org.gradle.api.internal.DynamicObject;
 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.*;
 
@@ -103,11 +102,6 @@ public class DefaultConvention implements Convention, ExtensionContainerInternal
         }
     }
 
-    public void add(String name, Class<?> type, Object... constructionArguments) {
-        DeprecationLogger.nagUserOfReplacedMethod("extensions.add(String, Class, Object...)", "extensions.create(String, Class, Object...)");
-        create(name, type, constructionArguments);
-    }
-
     public <T> T create(String name, Class<T> type, Object... constructionArguments) {
         T instance = getInstantiator().newInstance(type, constructionArguments);
         add(name, instance);
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 a703b0e..eb84650 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
@@ -34,26 +34,21 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 public class DefaultObjectConfigurationAction implements ObjectConfigurationAction {
+
     private final FileResolver resolver;
     private final ScriptPluginFactory configurerFactory;
     private final ScriptHandlerFactory scriptHandlerFactory;
     private final Set<Object> targets = new LinkedHashSet<Object>();
     private final Set<Runnable> actions = new LinkedHashSet<Runnable>();
     private final ClassLoaderScope classLoaderScope;
-    private final Object[] defaultTargets;
-
-    public DefaultObjectConfigurationAction(
-            FileResolver resolver,
-            ScriptPluginFactory configurerFactory,
-            ScriptHandlerFactory scriptHandlerFactory,
-            ClassLoaderScope classLoaderScope,
-            Object... defaultTargets
-    ) {
+    private final Object defaultTarget;
+
+    public DefaultObjectConfigurationAction(FileResolver resolver, ScriptPluginFactory configurerFactory, ScriptHandlerFactory scriptHandlerFactory, ClassLoaderScope classLoaderScope, Object defaultTarget) {
         this.resolver = resolver;
         this.configurerFactory = configurerFactory;
         this.scriptHandlerFactory = scriptHandlerFactory;
         this.classLoaderScope = classLoaderScope;
-        this.defaultTargets = defaultTargets;
+        this.defaultTarget = defaultTarget;
     }
 
     public ObjectConfigurationAction to(Object... targets) {
@@ -82,7 +77,16 @@ public class DefaultObjectConfigurationAction implements ObjectConfigurationActi
     public ObjectConfigurationAction plugin(final String pluginId) {
         actions.add(new Runnable() {
             public void run() {
-                applyPlugin(pluginId);
+                applyType(pluginId);
+            }
+        });
+        return this;
+    }
+
+    public ObjectConfigurationAction type(final Class<?> pluginClass) {
+        actions.add(new Runnable() {
+            public void run() {
+                applyType(pluginClass);
             }
         });
         return this;
@@ -91,38 +95,41 @@ public class DefaultObjectConfigurationAction implements ObjectConfigurationActi
     private void applyScript(Object script) {
         URI scriptUri = resolver.resolveUri(script);
         UriScriptSource scriptSource = new UriScriptSource("script", scriptUri);
-        ScriptHandler scriptHandler = scriptHandlerFactory.create(scriptSource, classLoaderScope);
-        ScriptPlugin configurer = configurerFactory.create(scriptSource, scriptHandler, classLoaderScope, "buildscript", DefaultScript.class);
+        ClassLoaderScope classLoaderScopeChild = classLoaderScope.createChild("script-" + scriptUri.toString());
+        ScriptHandler scriptHandler = scriptHandlerFactory.create(scriptSource, classLoaderScopeChild);
+        ScriptPlugin configurer = configurerFactory.create(scriptSource, scriptHandler, classLoaderScopeChild, classLoaderScope, "buildscript", DefaultScript.class, false);
         for (Object target : targets) {
             configurer.apply(target);
         }
     }
 
     private void applyPlugin(Class<? extends Plugin> pluginClass) {
+        applyType(pluginClass);
+    }
+
+    private void applyType(String pluginId) {
         for (Object target : targets) {
             if (target instanceof PluginAware) {
-                PluginAware pluginAware = (PluginAware) target;
-                pluginAware.getPlugins().apply(pluginClass);
+                ((PluginAware) target).getPluginManager().apply(pluginId);
             } else {
-                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()));
+                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()));
             }
         }
     }
 
-    private void applyPlugin(String pluginId) {
+    private void applyType(Class<?> pluginClass) {
         for (Object target : targets) {
             if (target instanceof PluginAware) {
-                PluginAware pluginAware = (PluginAware) target;
-                pluginAware.getPlugins().apply(pluginId);
+                ((PluginAware) target).getPluginManager().apply(pluginClass);
             } else {
-                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()));
+                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()));
             }
         }
     }
 
     public void execute() {
         if (targets.isEmpty()) {
-            to(defaultTargets);
+            to(defaultTarget);
         }
 
         for (Runnable action : actions) {
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
index 1e88e9c..21eda2d 100644
--- 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
@@ -15,27 +15,49 @@
  */
 package org.gradle.api.internal.plugins;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.gradle.api.Action;
 import org.gradle.api.Plugin;
-import org.gradle.api.plugins.PluginAware;
+import org.gradle.api.plugins.PluginCollection;
 import org.gradle.api.plugins.PluginContainer;
 import org.gradle.api.plugins.UnknownPluginException;
+import org.gradle.api.specs.Spec;
+import org.gradle.plugin.internal.PluginId;
 
-public class DefaultPluginContainer<T extends PluginAware> extends DefaultPluginCollection<Plugin> implements PluginContainer {
-    private PluginRegistry pluginRegistry;
-    private final T pluginAware;
+public class DefaultPluginContainer extends DefaultPluginCollection<Plugin> implements PluginContainer {
 
-    public DefaultPluginContainer(PluginRegistry pluginRegistry, T pluginAware) {
+    private final PluginRegistry pluginRegistry;
+    private final PluginManagerInternal pluginManager;
+
+    public DefaultPluginContainer(PluginRegistry pluginRegistry, final PluginManagerInternal pluginManager) {
         super(Plugin.class);
         this.pluginRegistry = pluginRegistry;
-        this.pluginAware = pluginAware;
+        this.pluginManager = pluginManager;
+
+        // Need this to make withId() work when someone does project.plugins.add(new SomePlugin());
+        whenObjectAdded(new Action<Plugin>() {
+            public void execute(Plugin plugin) {
+                pluginManager.addImperativePlugin(plugin.getClass());
+            }
+        });
     }
 
     public Plugin apply(String id) {
-        return addPluginInternal(getTypeForId(id));
+        PluginImplementation plugin = pluginRegistry.lookup(PluginId.unvalidated(id));
+        if (plugin == null) {
+            throw new UnknownPluginException("Plugin with id '" + id + "' not found.");
+        }
+
+        if (!Plugin.class.isAssignableFrom(plugin.asClass())) {
+            throw new IllegalArgumentException("Plugin implementation '" + plugin.asClass().getName() + "' does not implement the Plugin interface. This plugin cannot be applied directly via the PluginContainer.");
+        } else {
+            return pluginManager.addImperativePlugin(plugin);
+        }
     }
 
-    public <T extends Plugin> T apply(Class<T> type) {
-        return addPluginInternal(type);
+    public <P extends Plugin> P apply(Class<P> type) {
+        return pluginManager.addImperativePlugin(type);
     }
 
     public boolean hasPlugin(String id) {
@@ -46,15 +68,27 @@ public class DefaultPluginContainer<T extends PluginAware> extends DefaultPlugin
         return findPlugin(type) != null;
     }
 
-    public Plugin findPlugin(String id) {
-        try {
-            return findPlugin(getTypeForId(id));
-        } catch (UnknownPluginException e) {
-            return null;
+    private Plugin doFindPlugin(String id) {
+        for (final PluginManagerInternal.PluginWithId pluginWithId : pluginManager.pluginsForId(id)) {
+            Plugin plugin = Iterables.tryFind(DefaultPluginContainer.this, new Predicate<Plugin>() {
+                public boolean apply(Plugin plugin) {
+                    return pluginWithId.clazz.equals(plugin.getClass());
+                }
+            }).orNull();
+
+            if (plugin != null) {
+                return plugin;
+            }
         }
+
+        return null;
     }
 
-    public <T extends Plugin> T findPlugin(Class<T> type) {
+    public Plugin findPlugin(String id) {
+        return doFindPlugin(id);
+    }
+
+    public <P extends Plugin> P findPlugin(Class<P> type) {
         for (Plugin plugin : this) {
             if (plugin.getClass().equals(type)) {
                 return type.cast(plugin);
@@ -63,14 +97,6 @@ public class DefaultPluginContainer<T extends PluginAware> extends DefaultPlugin
         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) {
@@ -83,25 +109,40 @@ public class DefaultPluginContainer<T extends PluginAware> extends DefaultPlugin
         return getPlugin(id);
     }
 
-    public <T extends Plugin> T getAt(Class<T> type) throws UnknownPluginException {
+    public <P extends Plugin> P getAt(Class<P> type) throws UnknownPluginException {
         return getPlugin(type);
     }
 
-    public <T extends Plugin> T getPlugin(Class<T> type) throws UnknownPluginException {
-        Plugin plugin = findPlugin(type);
+    public <P extends Plugin> P getPlugin(Class<P> type) throws UnknownPluginException {
+        P 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);
+    public void withId(final String pluginId, final Action<? super Plugin> action) {
+        Action<DefaultPluginManager.PluginWithId> wrappedAction = new Action<DefaultPluginManager.PluginWithId>() {
+            public void execute(final DefaultPluginManager.PluginWithId pluginWithId) {
+                matching(new Spec<Plugin>() {
+                    public boolean isSatisfiedBy(Plugin element) {
+                        return pluginWithId.clazz.equals(element.getClass());
+                    }
+                }).all(action);
+            }
+        };
+
+        pluginManager.pluginsForId(pluginId).all(wrappedAction);
     }
 
-    private Plugin<T> providePlugin(Class<? extends Plugin> type) {
-        Plugin<T> plugin = pluginRegistry.loadPlugin(type);
-        plugin.apply(pluginAware);
-        return plugin;
+    @Override
+    public <S extends Plugin> PluginCollection<S> withType(Class<S> type) {
+        // runtime check because method is used from Groovy where type bounds are not respected
+        if (!Plugin.class.isAssignableFrom(type)) {
+            throw new IllegalArgumentException(String.format("'%s' does not implement the Plugin interface.", type.getName()));
+        }
+
+        return super.withType(type);
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginManager.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginManager.java
new file mode 100644
index 0000000..e241a8c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPluginManager.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Nullable;
+import org.gradle.api.Plugin;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.plugins.*;
+import org.gradle.internal.Cast;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.ObjectInstantiationException;
+import org.gradle.plugin.internal.PluginId;
+
+import java.util.Map;
+
+ at NotThreadSafe
+public class DefaultPluginManager implements PluginManagerInternal {
+
+    public static final String CORE_PLUGIN_NAMESPACE = "org" + PluginId.SEPARATOR + "gradle";
+    public static final String CORE_PLUGIN_PREFIX = CORE_PLUGIN_NAMESPACE + PluginId.SEPARATOR;
+
+    private final Instantiator instantiator;
+    private final PluginApplicator applicator;
+    private final PluginRegistry pluginRegistry;
+    private final DefaultPluginContainer pluginContainer;
+    private final Map<Class<?>, PluginImplementation<?>> plugins = Maps.newHashMap();
+    private final Map<Class<?>, Plugin<?>> instances = Maps.newHashMap();
+    private final Map<PluginId, DomainObjectSet<PluginWithId>> idMappings = Maps.newHashMap();
+
+    public DefaultPluginManager(final PluginRegistry pluginRegistry, Instantiator instantiator, final PluginApplicator applicator) {
+        this.instantiator = instantiator;
+        this.applicator = applicator;
+        this.pluginRegistry = pluginRegistry;
+        this.pluginContainer = new DefaultPluginContainer(pluginRegistry, this);
+    }
+
+    private <T> T instantiatePlugin(Class<T> type) {
+        try {
+            return instantiator.newInstance(type);
+        } catch (ObjectInstantiationException e) {
+            throw new PluginInstantiationException(String.format("Could not create plugin of type '%s'.", type.getSimpleName()), e.getCause());
+        }
+    }
+
+    @Override
+    public <P extends Plugin> P addImperativePlugin(PluginImplementation<P> plugin) {
+        doApply(plugin);
+        Class<? extends P> pluginClass = plugin.asClass();
+        return pluginClass.cast(instances.get(pluginClass));
+    }
+
+    public <P extends Plugin> P addImperativePlugin(Class<P> type) {
+        return addImperativePlugin(pluginRegistry.inspect(type));
+    }
+
+    @Nullable // if the plugin has already been added
+    private Runnable addPluginInternal(final PluginImplementation<?> plugin) {
+        final Class<?> pluginClass = plugin.asClass();
+        if (plugins.containsKey(pluginClass)) {
+            return null;
+        }
+
+        plugins.put(pluginClass, plugin);
+        return new Runnable() {
+            @Override
+            public void run() {
+                // Take a copy because adding to an idMappings value may result in new mappings being added (i.e. ConcurrentModificationException)
+                Iterable<PluginId> pluginIds = Lists.newArrayList(idMappings.keySet());
+                for (PluginId id : pluginIds) {
+                    if (plugin.isAlsoKnownAs(id)) {
+                        idMappings.get(id).add(new PluginWithId(id, pluginClass));
+                    }
+                }
+            }
+        };
+    }
+
+    public PluginContainer getPluginContainer() {
+        return pluginContainer;
+    }
+
+    @Override
+    public void apply(PluginImplementation<?> plugin) {
+        doApply(plugin);
+    }
+
+    public void apply(String pluginId) {
+        PluginImplementation<?> plugin = pluginRegistry.lookup(PluginId.unvalidated(pluginId));
+        if (plugin == null) {
+            throw new UnknownPluginException("Plugin with id '" + pluginId + "' not found.");
+        }
+        doApply(plugin);
+    }
+
+    public void apply(Class<?> type) {
+        doApply(pluginRegistry.inspect(type));
+    }
+
+    private void doApply(PluginImplementation<?> plugin) {
+        PluginId pluginId = plugin.getPluginId();
+        String pluginIdStr = pluginId == null ? null : pluginId.toString();
+        Class<?> pluginClass = plugin.asClass();
+        try {
+            if (plugin.getType().equals(PotentialPlugin.Type.UNKNOWN)) {
+                throw new InvalidPluginException("'" + pluginClass.getName() + "' is neither a plugin or a rule source and cannot be applied.");
+            } else {
+                boolean imperative = plugin.isImperative();
+                Runnable adder = addPluginInternal(plugin);
+                if (adder != null) {
+                    if (imperative) {
+                        // This insanity is needed for the case where someone calls pluginContainer.add(new SomePlugin())
+                        // That is, the plugin container has the instance that we want, but we don't think (we can't know) it has been applied
+                        Object instance = findInstance(pluginClass, pluginContainer);
+                        if (instance == null) {
+                            instance = instantiatePlugin(pluginClass);
+                        }
+
+                        Plugin<?> pluginInstance = Cast.uncheckedCast(instance);
+                        instances.put(pluginClass, pluginInstance);
+
+                        if (plugin.isHasRules()) {
+                            applicator.applyImperativeRulesHybrid(pluginIdStr, pluginInstance);
+                        } else {
+                            applicator.applyImperative(pluginIdStr, pluginInstance);
+                        }
+
+                        // Important not to add until after it has been applied as there can be
+                        // plugins.withType() callbacks waiting to build on what the plugin did
+                        pluginContainer.add(pluginInstance);
+                    } else {
+                        applicator.applyRules(pluginIdStr, pluginClass);
+                    }
+
+                    adder.run();
+                }
+            }
+        } catch (PluginApplicationException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PluginApplicationException(plugin.getDisplayName(), e);
+        }
+    }
+
+    private <T> T findInstance(Class<T> clazz, Iterable<?> instances) {
+        for (Object instance : instances) {
+            if (instance.getClass().equals(clazz)) {
+                return clazz.cast(instance);
+            }
+        }
+
+        return null;
+    }
+
+    public DomainObjectSet<PluginWithId> pluginsForId(String id) {
+        PluginId pluginId = PluginId.unvalidated(id);
+        DomainObjectSet<PluginWithId> pluginsForId = idMappings.get(pluginId);
+        if (pluginsForId == null) {
+            pluginsForId = new DefaultDomainObjectSet<PluginWithId>(PluginWithId.class, Sets.<PluginWithId>newLinkedHashSet());
+            idMappings.put(pluginId, pluginsForId);
+            for (PluginImplementation<?> plugin : plugins.values()) {
+                if (plugin.isAlsoKnownAs(pluginId)) {
+                    pluginsForId.add(new PluginWithId(pluginId, plugin.asClass()));
+                }
+            }
+        }
+
+        return pluginsForId;
+    }
+
+    public AppliedPlugin findPlugin(final String id) {
+        DomainObjectSet<PluginWithId> pluginWithIds = pluginsForId(id);
+        if (!pluginWithIds.isEmpty()) {
+            return pluginWithIds.iterator().next().asAppliedPlugin();
+        }
+        return null;
+    }
+
+    public boolean hasPlugin(String id) {
+        return findPlugin(id) != null;
+    }
+
+    public void withPlugin(final String id, final Action<? super AppliedPlugin> action) {
+        Action<PluginWithId> wrappedAction = new Action<PluginWithId>() {
+            public void execute(PluginWithId pluginWithId) {
+                action.execute(pluginWithId.asAppliedPlugin());
+            }
+        };
+        pluginsForId(id).all(wrappedAction);
+    }
+
+}
+
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 4e1559d..9261a87 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
@@ -16,101 +16,214 @@
 
 package org.gradle.api.internal.plugins;
 
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Plugin;
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import org.gradle.api.Nullable;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.plugins.PluginInstantiationException;
-import org.gradle.api.plugins.UnknownPluginException;
-import org.gradle.internal.Factories;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.reflect.ObjectInstantiationException;
+import org.gradle.api.plugins.InvalidPluginException;
+import org.gradle.internal.Cast;
+import org.gradle.internal.UncheckedException;
+import org.gradle.plugin.internal.PluginId;
 import org.gradle.util.GUtil;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.concurrent.ExecutionException;
 
 public class DefaultPluginRegistry implements PluginRegistry {
-    private final Map<String, Class<? extends Plugin>> idMappings = new HashMap<String, Class<? extends Plugin>>();
-    private final DefaultPluginRegistry parent;
-    private final Factory<ClassLoader> classLoaderFactory;
-    private final Instantiator instantiator;
+    private final PluginRegistry parent;
+    private final PluginInspector pluginInspector;
+    private final ClassLoaderScope classLoaderScope;
 
-    public DefaultPluginRegistry(ClassLoader classLoaderFactory, Instantiator instantiator) {
-        this(null, Factories.constant(classLoaderFactory), instantiator);
+    private final LoadingCache<Class<?>, PluginImplementation<?>> classMappings;
+    private final LoadingCache<PluginIdLookupCacheKey, Optional<PluginImplementation<?>>> idMappings;
+
+    public DefaultPluginRegistry(PluginInspector pluginInspector, ClassLoaderScope classLoaderScope) {
+        this(null, pluginInspector, classLoaderScope);
     }
 
-    private DefaultPluginRegistry(DefaultPluginRegistry parent, Factory<ClassLoader> classLoaderFactory, Instantiator instantiator) {
+    private DefaultPluginRegistry(PluginRegistry parent, final PluginInspector pluginInspector, ClassLoaderScope classLoaderScope) {
         this.parent = parent;
-        this.classLoaderFactory = classLoaderFactory;
-        this.instantiator = instantiator;
-    }
+        this.pluginInspector = pluginInspector;
+        this.classLoaderScope = classLoaderScope;
+        this.classMappings = CacheBuilder.newBuilder().build(new PotentialPluginCacheLoader(pluginInspector));
+        this.idMappings = CacheBuilder.newBuilder().build(new CacheLoader<PluginIdLookupCacheKey, Optional<PluginImplementation<?>>>() {
+            @Override
+            public Optional<PluginImplementation<?>> load(@SuppressWarnings("NullableProblems") PluginIdLookupCacheKey key) throws Exception {
+                PluginId pluginId = key.getId();
+                ClassLoader classLoader = key.getClassLoader();
+
+                PluginDescriptorLocator locator = new ClassloaderBackedPluginDescriptorLocator(classLoader);
+
+                PluginDescriptor pluginDescriptor = locator.findPluginDescriptor(pluginId.toString());
+                if (pluginDescriptor == null) {
+                    return Optional.absent();
+                }
+
+                String implClassName = pluginDescriptor.getImplementationClassName();
+                if (!GUtil.isTrue(implClassName)) {
+                    throw new InvalidPluginException(String.format("No implementation class specified for plugin '%s' in %s.", pluginId, pluginDescriptor));
+                }
+
+                final Class<?> implClass;
+                try {
+                    implClass = classLoader.loadClass(implClassName);
+                } catch (ClassNotFoundException e) {
+                    throw new InvalidPluginException(String.format(
+                            "Could not find implementation class '%s' for plugin '%s' specified in %s.", implClassName, pluginId,
+                            pluginDescriptor), e);
+                }
 
-    public PluginRegistry createChild(final ClassLoaderScope lookupScope, Instantiator instantiator) {
-        Factory<ClassLoader> classLoaderFactory = new Factory<ClassLoader>() {
-            public ClassLoader create() {
-                return lookupScope.getScopeClassLoader();
+                PotentialPlugin<?> potentialPlugin = pluginInspector.inspect(implClass);
+                PluginImplementation<Object> withId = new RegistryAwarePluginImplementation(classLoader, pluginId, potentialPlugin);
+                return Cast.uncheckedCast(Optional.of(withId));
             }
-        };
-        return new DefaultPluginRegistry(this, classLoaderFactory, instantiator);
+        });
     }
 
-    public <T extends Plugin> T loadPlugin(Class<T> pluginClass) {
-        if (!Plugin.class.isAssignableFrom(pluginClass)) {
-            throw new InvalidUserDataException(String.format(
-                    "Cannot create plugin of type '%s' as it does not implement the Plugin interface.",
-                    pluginClass.getSimpleName()));
+    public PluginRegistry createChild(final ClassLoaderScope lookupScope) {
+        return new DefaultPluginRegistry(this, pluginInspector, lookupScope);
+    }
+
+    @Nullable
+    @Override
+    public <T> PluginImplementation<T> maybeInspect(Class<T> clazz) {
+        if (classLoaderScope.defines(clazz)) {
+            return Cast.uncheckedCast(uncheckedGet(classMappings, clazz));
         }
-        try {
-            return instantiator.newInstance(pluginClass);
-        } catch (ObjectInstantiationException e) {
-            throw new PluginInstantiationException(String.format("Could not create plugin of type '%s'.",
-                    pluginClass.getSimpleName()), e.getCause());
+
+        if (parent != null) {
+            PluginImplementation<T> implementation = parent.maybeInspect(clazz);
+            if (implementation != null) {
+                return implementation;
+            }
         }
+
+        return null;
+    }
+
+    public <T> PluginImplementation<T> inspect(Class<T> clazz) {
+        PluginImplementation<T> implementation = maybeInspect(clazz);
+        if (implementation != null) {
+            return implementation;
+        }
+
+        // Unknown type - just inspect ourselves. Should instead share this with all registries
+        return Cast.uncheckedCast(uncheckedGet(classMappings, clazz));
     }
 
-    public Class<? extends Plugin> getTypeForId(String pluginId) {
+    @Nullable
+    @Override
+    public PluginImplementation<?> lookup(PluginId pluginId) {
+        PluginImplementation lookup;
         if (parent != null) {
-            try {
-                return parent.getTypeForId(pluginId);
-            } catch (UnknownPluginException e) {
-                // Ignore
+            lookup = parent.lookup(pluginId);
+            if (lookup != null) {
+                return lookup;
+            }
+        }
+
+        return lookup(pluginId, classLoaderScope.getLocalClassLoader());
+    }
+
+    @Nullable
+    private PluginImplementation<?> lookup(PluginId pluginId, ClassLoader classLoader) {
+        // Don't go up the parent chain.
+        // Don't want to risk classes crossing “scope” boundaries and being non collectible.
+
+        PluginImplementation lookup;
+        if (!pluginId.isQualified()) {
+            PluginId qualified = pluginId.maybeQualify(DefaultPluginManager.CORE_PLUGIN_NAMESPACE);
+            lookup = uncheckedGet(idMappings, new PluginIdLookupCacheKey(qualified, classLoader)).orNull();
+            if (lookup != null) {
+                return lookup;
             }
         }
 
-        Class<? extends Plugin> implClass = idMappings.get(pluginId);
-        if (implClass != null) {
-            return implClass;
+        return uncheckedGet(idMappings, new PluginIdLookupCacheKey(pluginId, classLoader)).orNull();
+    }
+
+    private static <K, V> V uncheckedGet(LoadingCache<K, V> cache, K key) {
+        try {
+            return cache.get(key);
+        } catch (ExecutionException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getCause());
+        } catch (UncheckedExecutionException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getCause());
         }
+    }
 
-        ClassLoader classLoader = this.classLoaderFactory.create();
+    static class PluginIdLookupCacheKey {
 
-        PluginDescriptorLocator pluginDescriptorLocator = new ClassloaderBackedPluginDescriptorLocator(classLoader);
-        PluginDescriptor pluginDescriptor = pluginDescriptorLocator.findPluginDescriptor(pluginId);
-        if (pluginDescriptor == null) {
-            throw new UnknownPluginException("Plugin with id '" + pluginId + "' not found.");
+        private final ClassLoader classLoader;
+        private final PluginId id;
+
+        PluginIdLookupCacheKey(PluginId id, ClassLoader classLoader) {
+            this.classLoader = classLoader;
+            this.id = id;
         }
 
-        String implClassName = pluginDescriptor.getImplementationClassName();
-        if (!GUtil.isTrue(implClassName)) {
-            throw new PluginInstantiationException(String.format(
-                    "No implementation class specified for plugin '%s' in %s.", pluginId, pluginDescriptor));
+        public PluginId getId() {
+            return id;
         }
 
-        try {
-            Class<?> rawClass = classLoader.loadClass(implClassName);
-            if (!Plugin.class.isAssignableFrom(rawClass)) {
-                throw new PluginInstantiationException(String.format("Implementation class '%s' specified for plugin '%s' does not implement the Plugin interface. Specified in %s.",
-                        implClassName, pluginId, pluginDescriptor));
+        public ClassLoader getClassLoader() {
+            return classLoader;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
             }
-            implClass = rawClass.asSubclass(Plugin.class);
-        } catch (ClassNotFoundException e) {
-            throw new PluginInstantiationException(String.format(
-                    "Could not find implementation class '%s' for plugin '%s' specified in %s.", implClassName, pluginId,
-                    pluginDescriptor), e);
+
+            PluginIdLookupCacheKey that = (PluginIdLookupCacheKey) o;
+
+            return classLoader.equals(that.classLoader) && id.equals(that.id);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = classLoader.hashCode();
+            result = 31 * result + id.hashCode();
+            return result;
+        }
+    }
+
+    private class PotentialPluginCacheLoader extends CacheLoader<Class<?>, PluginImplementation<?>> {
+        private final PluginInspector pluginInspector;
+
+        public PotentialPluginCacheLoader(PluginInspector pluginInspector) {
+            this.pluginInspector = pluginInspector;
+        }
+
+        @Override
+        public PluginImplementation<?> load(@SuppressWarnings("NullableProblems") Class<?> key) throws Exception {
+            return new RegistryAwarePluginImplementation(key.getClassLoader(), null, pluginInspector.inspect(key));
+        }
+    }
+
+    private class RegistryAwarePluginImplementation extends DefaultPotentialPluginWithId<Object> {
+        private final ClassLoader classLoader;
+        private final PluginId pluginId;
+
+        public RegistryAwarePluginImplementation(ClassLoader classLoader, PluginId pluginId, PotentialPlugin<?> potentialPlugin) {
+            super(pluginId, potentialPlugin);
+            this.classLoader = classLoader;
+            this.pluginId = pluginId;
         }
 
-        idMappings.put(pluginId, implClass);
-        return implClass;
+        @Override
+        public boolean isAlsoKnownAs(PluginId id) {
+            if (id.equals(pluginId)) {
+                return true;
+            }
+            PluginImplementation<?> other = lookup(id, classLoader);
+            return other != null && other.asClass().equals(asClass());
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPotentialPluginWithId.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPotentialPluginWithId.java
new file mode 100644
index 0000000..db15a6f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/DefaultPotentialPluginWithId.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.plugin.internal.PluginId;
+
+public class DefaultPotentialPluginWithId<T> implements PluginImplementation<T> {
+
+    private final PluginId pluginId;
+    private final PotentialPlugin<? extends T> potentialPlugin;
+
+    public static <T> DefaultPotentialPluginWithId<T> of(PluginId pluginId, PotentialPlugin<T> potentialPlugin) {
+        return new DefaultPotentialPluginWithId<T>(pluginId, potentialPlugin);
+    }
+
+    protected DefaultPotentialPluginWithId(PluginId pluginId, PotentialPlugin<? extends T> potentialPlugin) {
+        this.pluginId = pluginId;
+        this.potentialPlugin = potentialPlugin;
+    }
+
+    @Override
+    public String getDisplayName() {
+        if (pluginId == null) {
+            return String.format("class '%s'", asClass().getName());
+        }
+        return String.format("id '%s'", pluginId);
+    }
+
+    public PluginId getPluginId() {
+        return pluginId;
+    }
+
+    public Class<? extends T> asClass() {
+        return potentialPlugin.asClass();
+    }
+
+    public boolean isImperative() {
+        return potentialPlugin.isImperative();
+    }
+
+    public boolean isHasRules() {
+        return potentialPlugin.isHasRules();
+    }
+
+    public Type getType() {
+        return potentialPlugin.getType();
+    }
+
+    @Override
+    public boolean isAlsoKnownAs(PluginId id) {
+        return false;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/EmbeddableJavaProject.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/EmbeddableJavaProject.java
deleted file mode 100644
index 357fa9b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/EmbeddableJavaProject.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.plugins;
-
-import org.gradle.api.file.FileCollection;
-
-import java.util.Collection;
-
-/**
- * Meta-info about a Java project which can be embedded in the build.
- */
-public interface EmbeddableJavaProject {
-    Collection<String> getRebuildTasks();
-
-    Collection<String> getBuildTasks();
-
-    FileCollection getRuntimeClasspath();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapter.java
index a67a9ab..7c32f43 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapter.java
@@ -18,22 +18,17 @@ package org.gradle.api.internal.plugins;
 
 import groovy.lang.MissingPropertyException;
 import org.gradle.api.internal.BeanDynamicObject;
-import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.plugins.ExtraPropertiesExtension;
-import org.gradle.util.DeprecationLogger;
 
 import java.util.Map;
 
 public class ExtraPropertiesDynamicObjectAdapter extends BeanDynamicObject {
-
     private final ExtraPropertiesExtension extension;
-    private final Object delegate;
-    private final DynamicObject dynamicOwner;
+    private final Class<?> delegateType;
 
-    public ExtraPropertiesDynamicObjectAdapter(Object delegate, DynamicObject dynamicOwner, ExtraPropertiesExtension extension) {
+    public ExtraPropertiesDynamicObjectAdapter(Class<?> delegateType, ExtraPropertiesExtension extension) {
         super(extension);
-        this.delegate = delegate;
-        this.dynamicOwner = dynamicOwner;
+        this.delegateType = delegateType;
         this.extension = extension;
     }
 
@@ -47,10 +42,20 @@ public class ExtraPropertiesDynamicObjectAdapter extends BeanDynamicObject {
 
     @Override
     public void setProperty(String name, Object value) throws MissingPropertyException {
-        if (!dynamicOwner.hasProperty(name)) {
-            DeprecationLogger.nagUserAboutDynamicProperty(name, delegate, value);
+        if (!hasProperty(name)) {
+            throw new MissingPropertyException(name, delegateType);
         }
 
         super.setProperty(name, value);
     }
+
+    @Override
+    public boolean isMayImplementMissingMethods() {
+        return false;
+    }
+
+    @Override
+    public boolean isMayImplementMissingProperties() {
+        return false;
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ImperativeOnlyPluginApplicator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ImperativeOnlyPluginApplicator.java
new file mode 100644
index 0000000..0c1ea9f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/ImperativeOnlyPluginApplicator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.api.Plugin;
+
+import static org.gradle.internal.Cast.uncheckedCast;
+
+public class ImperativeOnlyPluginApplicator<T> implements PluginApplicator {
+
+    private final T target;
+
+    public ImperativeOnlyPluginApplicator(T target) {
+        this.target = target;
+    }
+
+    public void applyImperative(@Nullable String pluginId, Plugin<?> plugin) {
+        // TODO validate that the plugin accepts this kind of argument
+        Plugin<T> cast = uncheckedCast(plugin);
+        cast.apply(target);
+    }
+
+    public void applyRules(@Nullable String pluginId, Class<?> clazz) {
+        String message = String.format("Cannot apply model rules of plugin '%s' as the target '%s' is not model rule aware", clazz.getName(), target.toString());
+        throw new UnsupportedOperationException(message);
+    }
+
+    public void applyImperativeRulesHybrid(@Nullable String pluginId, Plugin<?> plugin) {
+        applyRules(pluginId, plugin.getClass());
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicationException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicationException.java
new file mode 100644
index 0000000..775a749
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicationException.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.plugins;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+
+ at Contextual
+public class PluginApplicationException extends GradleException {
+
+    public PluginApplicationException(String pluginIdentity, Throwable cause) {
+        super(String.format("Failed to apply plugin [" + pluginIdentity + "]"), cause);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicator.java
new file mode 100644
index 0000000..d362008
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginApplicator.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.api.Plugin;
+
+public interface PluginApplicator {
+
+    // Implementations should not wrap exceptions, this is done in DefaultObjectConfigurationAction
+
+    void applyImperative(@Nullable String pluginId, Plugin<?> plugin);
+
+    void applyRules(@Nullable String pluginId, Class<?> clazz);
+
+    void applyImperativeRulesHybrid(@Nullable String pluginId, Plugin<?> plugin);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginAwareInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginAwareInternal.java
new file mode 100644
index 0000000..4705db4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginAwareInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.PluginAware;
+
+public interface PluginAwareInternal extends PluginAware {
+    PluginManagerInternal getPluginManager();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginDescriptor.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginDescriptor.java
index a29d059..301bbbd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginDescriptor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginDescriptor.java
@@ -34,6 +34,10 @@ public class PluginDescriptor {
         return properties.getProperty("implementation-class");
     }
 
+    public URL getPropertiesFileUrl() {
+        return propertiesFileUrl;
+    }
+
     @Override
     public String toString() {
         return propertiesFileUrl.toString();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginImplementation.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginImplementation.java
new file mode 100644
index 0000000..1eba9ab
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginImplementation.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.plugin.internal.PluginId;
+
+public interface PluginImplementation<T> extends PotentialPlugin<T> {
+    String getDisplayName();
+
+    /**
+     * An id for the plugin implementation, if known.
+     */
+    @Nullable
+    PluginId getPluginId();
+
+    boolean isAlsoKnownAs(PluginId id);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginInspector.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginInspector.java
new file mode 100644
index 0000000..9dfcf7a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginInspector.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Plugin;
+import org.gradle.internal.Cast;
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector;
+
+ at ThreadSafe
+public class PluginInspector {
+
+    private final ModelRuleSourceDetector modelRuleSourceDetector;
+
+    public PluginInspector(ModelRuleSourceDetector modelRuleSourceDetector) {
+        this.modelRuleSourceDetector = modelRuleSourceDetector;
+    }
+
+    public <T> PotentialPlugin<T> inspect(Class<T> type) {
+        boolean implementsInterface = Plugin.class.isAssignableFrom(type);
+        boolean hasRules = this.modelRuleSourceDetector.hasRules(type);
+
+        if (implementsInterface) {
+            @SuppressWarnings("unchecked") Class<? extends Plugin<?>> cast = (Class<? extends Plugin<?>>) type;
+            return Cast.uncheckedCast(toImperative(cast, hasRules));
+        } else if (hasRules) {
+            return new PotentialPureRuleSourceClassPlugin<T>(type);
+        } else {
+            return new PotentialUnknownTypePlugin<T>(type);
+        }
+    }
+
+    private <T extends Plugin<?>> PotentialPlugin<T> toImperative(Class<T> type, boolean hasRules) {
+        if (hasRules) {
+            return new PotentialHybridImperativeAndRulesPlugin<T>(type);
+        } else {
+            return new PotentialImperativeClassPlugin<T>(type);
+        }
+    }
+
+    private static class PotentialImperativeClassPlugin<T extends Plugin<?>> implements PotentialPlugin<T> {
+
+        private final Class<T> clazz;
+
+        public PotentialImperativeClassPlugin(Class<T> clazz) {
+            this.clazz = clazz;
+        }
+
+        public Class<T> asClass() {
+            return clazz;
+        }
+
+        public boolean isImperative() {
+            return true;
+        }
+
+        public Type getType() {
+            return Type.IMPERATIVE_CLASS;
+        }
+
+        public boolean isHasRules() {
+            return false;
+        }
+    }
+
+    private static class PotentialHybridImperativeAndRulesPlugin<T extends Plugin<?>> implements PotentialPlugin<T> {
+
+        private final Class<T> clazz;
+
+        public PotentialHybridImperativeAndRulesPlugin(Class<T> clazz) {
+            this.clazz = clazz;
+        }
+
+        public Class<T> asClass() {
+            return clazz;
+        }
+
+        public boolean isImperative() {
+            return true;
+        }
+
+        public boolean isHasRules() {
+            return true;
+        }
+
+        public Type getType() {
+            return Type.HYBRID_IMPERATIVE_AND_RULES_CLASS;
+        }
+
+    }
+
+    private static class PotentialPureRuleSourceClassPlugin<T> implements PotentialPlugin<T> {
+
+        private final Class<T> clazz;
+
+        public PotentialPureRuleSourceClassPlugin(Class<T> clazz) {
+            this.clazz = clazz;
+        }
+
+        public Class<T> asClass() {
+            return clazz;
+        }
+
+        public boolean isImperative() {
+            return false;
+        }
+
+        public Type getType() {
+            return Type.PURE_RULE_SOURCE_CLASS;
+        }
+
+        public boolean isHasRules() {
+            return false;
+        }
+    }
+
+    private static class PotentialUnknownTypePlugin<T> implements PotentialPlugin<T> {
+
+        private final Class<T> clazz;
+
+        public PotentialUnknownTypePlugin(Class<T> clazz) {
+            this.clazz = clazz;
+        }
+
+        public Class<T> asClass() {
+            return clazz;
+        }
+
+        public boolean isImperative() {
+            return false;
+        }
+
+        public boolean isHasRules() {
+            return false;
+        }
+
+        public Type getType() {
+            return Type.UNKNOWN;
+        }
+
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginManagerInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginManagerInternal.java
new file mode 100644
index 0000000..ec3fdf3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PluginManagerInternal.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.DomainObjectSet;
+import org.gradle.api.Plugin;
+import org.gradle.api.plugins.AppliedPlugin;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.plugins.PluginManager;
+import org.gradle.plugin.internal.PluginId;
+
+public interface PluginManagerInternal extends PluginManager {
+    void apply(PluginImplementation<?> plugin);
+
+    <P extends Plugin> P addImperativePlugin(PluginImplementation<P> plugin);
+
+    <P extends Plugin> P addImperativePlugin(Class<P> plugin);
+
+    PluginContainer getPluginContainer();
+
+    DomainObjectSet<PluginWithId> pluginsForId(String id);
+
+    class PluginWithId {
+        final PluginId id;
+        final Class<?> clazz;
+
+        public PluginWithId(PluginId id, Class<?> clazz) {
+            this.id = id;
+            this.clazz = clazz;
+        }
+
+        AppliedPlugin asAppliedPlugin() {
+            return new DefaultAppliedPlugin(id);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            PluginWithId that = (PluginWithId) o;
+
+            return clazz.equals(that.clazz) && id.equals(that.id);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = id.hashCode();
+            result = 31 * result + clazz.hashCode();
+            return result;
+        }
+    }
+}
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 a4e1690..bc57162 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2014 the original author 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,19 +16,27 @@
 
 package org.gradle.api.internal.plugins;
 
-import org.gradle.api.Plugin;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.plugins.PluginInstantiationException;
-import org.gradle.api.plugins.UnknownPluginException;
-import org.gradle.internal.reflect.Instantiator;
+import org.gradle.plugin.internal.PluginId;
 
+ at ThreadSafe
 public interface PluginRegistry {
-    <T extends Plugin> T loadPlugin(Class<T> pluginClass) throws PluginInstantiationException;
+    <T> PluginImplementation<T> inspect(Class<T> clazz);
 
-    Class<? extends Plugin> getTypeForId(String pluginId) throws UnknownPluginException, PluginInstantiationException;
+    /**
+     * Extracts plugin information for the given class, if known to this registry.
+     */
+    @Nullable
+    <T> PluginImplementation<T> maybeInspect(Class<T> clazz);
 
     /**
-     * Creates a child registry which uses the plugins declared in the given script scope.
+     * Locates the plugin with the given id. Note that the id of the result may be different to the requested id.
      */
-    PluginRegistry createChild(ClassLoaderScope lookupScope, Instantiator instantiator);
+    @Nullable
+    PluginImplementation<?> lookup(PluginId pluginId);
+
+    PluginRegistry createChild(ClassLoaderScope lookupScope);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PotentialPlugin.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PotentialPlugin.java
new file mode 100644
index 0000000..72d048a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/PotentialPlugin.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+/**
+ * A plugin that could be applied.
+ *
+ * This may represent an invalid plugin.
+ *
+ * At the moment it does not encompass plugins that aren't implemented as classes, but it is likely to in the future.
+ */
+public interface PotentialPlugin<T> {
+
+    static enum Type {
+        UNKNOWN,
+        IMPERATIVE_CLASS,
+        PURE_RULE_SOURCE_CLASS,
+        HYBRID_IMPERATIVE_AND_RULES_CLASS
+    }
+
+    Class<? extends T> asClass();
+
+    boolean isImperative();
+
+    boolean isHasRules();
+
+    Type getType();
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/RuleBasedPluginApplicator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/RuleBasedPluginApplicator.java
new file mode 100644
index 0000000..3b4fcc3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/plugins/RuleBasedPluginApplicator.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.api.Plugin;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.ExtractedModelRule;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistryScope;
+
+public class RuleBasedPluginApplicator<T extends ModelRegistryScope & PluginAwareInternal> implements PluginApplicator {
+
+    private final T target;
+    private final PluginApplicator imperativeApplicator;
+    private final ModelRuleExtractor ruleInspector;
+    private final ModelRuleSourceDetector ruleDetector;
+
+    public RuleBasedPluginApplicator(T target, ModelRuleExtractor ruleInspector, ModelRuleSourceDetector ruleDetector) {
+        this.target = target;
+        this.ruleInspector = ruleInspector;
+        this.ruleDetector = ruleDetector;
+        this.imperativeApplicator = new ImperativeOnlyPluginApplicator<T>(target);
+    }
+
+    public void applyImperative(@Nullable String pluginId, Plugin<?> plugin) {
+        imperativeApplicator.applyImperative(pluginId, plugin);
+    }
+
+    public void applyRules(@Nullable String pluginId, Class<?> clazz) {
+        ModelRegistry modelRegistry = target.getModelRegistry();
+        Iterable<Class<? extends RuleSource>> declaredSources = ruleDetector.getDeclaredSources(clazz);
+        for (Class<? extends RuleSource> ruleSource : declaredSources) {
+            Iterable<ExtractedModelRule> rules = ruleInspector.extract(ruleSource);
+            for (ExtractedModelRule rule : rules) {
+                for (Class<?> dependency : rule.getRuleDependencies()) {
+                    target.getPluginManager().apply(dependency);
+                }
+
+                if (rule.getType().equals(ExtractedModelRule.Type.ACTION)) {
+                    modelRegistry.configure(rule.getActionRole(), rule.getAction());
+                } else if (rule.getType().equals(ExtractedModelRule.Type.CREATOR)) {
+                    modelRegistry.create(rule.getCreator());
+                } else if (!rule.getType().equals(ExtractedModelRule.Type.DEPENDENCIES)) {
+                    throw new IllegalStateException("unhandled extracted model rule type: " + rule.getType());
+                }
+            }
+        }
+    }
+
+    public void applyImperativeRulesHybrid(@Nullable String pluginId, Plugin<?> plugin) {
+        applyImperative(pluginId, plugin);
+        applyRules(pluginId, plugin.getClass());
+    }
+
+}
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
index baf09da..5c97780 100644
--- 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 the original author or authors.
+ * Copyright 2014 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,36 +17,39 @@
 package org.gradle.api.internal.project;
 
 import groovy.lang.Closure;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.Action;
+import org.gradle.api.internal.ClosureBackedAction;
 import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
-import org.gradle.api.plugins.PluginAware;
-import org.gradle.configuration.ScriptPluginFactory;
+import org.gradle.api.internal.plugins.PluginAwareInternal;
+import org.gradle.api.plugins.ObjectConfigurationAction;
+import org.gradle.api.plugins.PluginContainer;
 import org.gradle.util.ConfigureUtil;
 
 import java.util.Map;
 
-abstract public class AbstractPluginAware implements PluginAware {
+abstract public class AbstractPluginAware implements PluginAwareInternal {
 
+    @SuppressWarnings("unchecked")
     public void apply(Closure closure) {
-        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), getScriptHandlerFactory(), getClassLoaderScope().getBase().createChild(), this);
-        ConfigureUtil.configure(closure, action);
-        action.execute();
+        apply(ClosureBackedAction.of(closure));
+    }
+
+    public void apply(Action<? super ObjectConfigurationAction> action) {
+        DefaultObjectConfigurationAction configAction = createObjectConfigurationAction();
+        action.execute(configAction);
+        configAction.execute();
     }
 
     public void apply(Map<String, ?> options) {
-        DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), getScriptHandlerFactory(), getClassLoaderScope().getBase().createChild(), this);
+        DefaultObjectConfigurationAction action = createObjectConfigurationAction();
         ConfigureUtil.configureByMap(options, action);
         action.execute();
     }
 
-    protected abstract FileResolver getFileResolver();
-
-    protected abstract ScriptPluginFactory getScriptPluginFactory();
-
-    protected abstract ScriptHandlerFactory getScriptHandlerFactory();
+    public PluginContainer getPlugins() {
+        return getPluginManager().getPluginContainer();
+    }
 
-    protected abstract ClassLoaderScope getClassLoaderScope();
+    abstract protected DefaultObjectConfigurationAction createObjectConfigurationAction();
 
 }
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 196926e..ac55f29 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
@@ -16,9 +16,11 @@
 
 package org.gradle.api.internal.project;
 
+import com.google.common.collect.Maps;
 import groovy.lang.Closure;
 import groovy.lang.MissingPropertyException;
 import org.gradle.api.*;
+import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.ArtifactHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
@@ -30,45 +32,48 @@ import org.gradle.api.file.FileTree;
 import org.gradle.api.initialization.dsl.ScriptHandler;
 import org.gradle.api.internal.*;
 import org.gradle.api.internal.artifacts.ModuleInternal;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
 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.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
 import org.gradle.api.internal.plugins.ExtensionContainerInternal;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 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.PluginContainer;
+import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.resources.ResourceHandler;
-import org.gradle.api.tasks.Directory;
-import org.gradle.api.tasks.TaskContainer;
 import org.gradle.api.tasks.WorkResult;
 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.event.ListenerBroadcast;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.StandardOutputCapture;
-import org.gradle.model.ModelPath;
-import org.gradle.model.ModelRules;
-import org.gradle.model.dsl.internal.GroovyModelDsl;
-import org.gradle.model.internal.ModelRegistry;
+import org.gradle.model.dsl.internal.NonTransformedModelDslBacking;
+import org.gradle.model.dsl.internal.TransformedModelDslBacking;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.registry.ModelRegistry;
 import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 import org.gradle.util.Configurable;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.DeprecationLogger;
 import org.gradle.util.Path;
 
+import javax.inject.Inject;
 import java.io.File;
 import java.net.URI;
 import java.util.*;
@@ -78,8 +83,10 @@ import static org.gradle.util.GUtil.addMaps;
 import static org.gradle.util.GUtil.isTrue;
 
 public abstract class AbstractProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware {
+
     private static Logger buildLogger = Logging.getLogger(Project.class);
     private final ClassLoaderScope classLoaderScope;
+    private final ClassLoaderScope baseClassLoaderScope;
     private ServiceRegistry services;
 
     private final ProjectInternal rootProject;
@@ -102,17 +109,13 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
 
     private Object status;
 
-    private final Map<String, Project> childProjects = new HashMap<String, Project>();
+    private final Map<String, Project> childProjects = Maps.newTreeMap();
 
     private List<String> defaultTasks = new ArrayList<String>();
 
-    private Set<Project> dependsOnProjects = new HashSet<Project>();
-
     private ProjectStateInternal state;
 
     private FileResolver fileResolver;
-    private FileOperations fileOperations;
-    private ProcessOperations processOperations;
 
     private Factory<AntBuilder> antBuilderFactory;
 
@@ -120,44 +123,23 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
 
     private Object buildDir = Project.DEFAULT_BUILD_DIR_NAME;
 
-    private PluginContainer pluginContainer;
-
     private final int depth;
 
     private TaskContainerInternal taskContainer;
 
-    private TaskContainerInternal implicitTasksContainer;
-
-    private ProjectRegistry<ProjectInternal> projectRegistry;
-
     private DependencyHandler dependencyHandler;
 
-    private ConfigurationContainerInternal configurationContainer;
+    private ConfigurationContainer configurationContainer;
 
     private ArtifactHandler artifactHandler;
 
-    private RepositoryHandler repositoryHandler;
-
-    private ScriptHandler scriptHandler;
-
     private ListenerBroadcast<ProjectEvaluationListener> evaluationListener = new ListenerBroadcast<ProjectEvaluationListener>(ProjectEvaluationListener.class);
 
-    private LoggingManagerInternal loggingManager;
-
-    private SoftwareComponentContainer softwareComponentContainer;
-
     private ExtensibleDynamicObject extensibleDynamicObject;
 
-    private ProjectConfigurationActionContainer configurationActions;
-
-    private final ModelRegistry modelRegistry;
-    private final ModelRules modelRules;
-
     private String description;
 
     private final Path path;
-    private final ScriptPluginFactory scriptPluginFactory;
-    private final ScriptHandlerFactory scriptHandlerFactory;
 
     public AbstractProject(String name,
                            ProjectInternal parent,
@@ -165,9 +147,10 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
                            ScriptSource buildScriptSource,
                            GradleInternal gradle,
                            ServiceRegistryFactory serviceRegistryFactory,
-                           ClassLoaderScope classLoaderScope
-    ) {
-        this.classLoaderScope = classLoaderScope;
+                           ClassLoaderScope selfClassLoaderScope,
+                           ClassLoaderScope baseClassLoaderScope) {
+        this.classLoaderScope = selfClassLoaderScope;
+        this.baseClassLoaderScope = baseClassLoaderScope;
         assert name != null;
         this.rootProject = parent != null ? parent.getRootProject() : this;
         this.projectDir = projectDir;
@@ -187,27 +170,7 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         }
 
         services = serviceRegistryFactory.createFor(this);
-        fileResolver = services.get(FileResolver.class);
-        antBuilderFactory = services.getFactory(AntBuilder.class);
         taskContainer = services.newInstance(TaskContainerInternal.class);
-        implicitTasksContainer = services.newInstance(TaskContainerInternal.class);
-        fileOperations = services.get(FileOperations.class);
-        processOperations = services.get(ProcessOperations.class);
-        projectEvaluator = services.get(ProjectEvaluator.class);
-        repositoryHandler = services.get(RepositoryHandler.class);
-        configurationContainer = services.get(ConfigurationContainerInternal.class);
-        pluginContainer = services.get(PluginContainer.class);
-        artifactHandler = services.get(ArtifactHandler.class);
-        dependencyHandler = services.get(DependencyHandler.class);
-        scriptHandler = services.get(ScriptHandler.class);
-        projectRegistry = services.get(ProjectRegistry.class);
-        loggingManager = services.get(LoggingManagerInternal.class);
-        softwareComponentContainer = services.get(SoftwareComponentContainer.class);
-        scriptPluginFactory = services.get(ScriptPluginFactory.class);
-        scriptHandlerFactory = services.get(ScriptHandlerFactory.class);
-        configurationActions = services.get(ProjectConfigurationActionContainer.class);
-        modelRegistry = services.get(ModelRegistry.class);
-        modelRules = services.get(ModelRules.class);
 
         extensibleDynamicObject = new ExtensibleDynamicObject(this, services.get(Instantiator.class));
         if (parent != null) {
@@ -217,33 +180,54 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
 
         evaluationListener.add(gradle.getProjectEvaluationBroadcaster());
 
-        final ModelPath tasksModelPath = ModelPath.path(TaskContainerInternal.MODEL_PATH);
-        modelRules.register(tasksModelPath.toString(), taskContainer);
-        taskContainer.all(new Action<Task>() {
-            public void execute(Task task) {
-                String name = task.getName();
-                modelRules.register(tasksModelPath.child(name).toString(), Task.class, new TaskFactory(taskContainer, name));
-            }
-        });
-        taskContainer.whenObjectRemoved(new Action<Task>() {
-            public void execute(Task task) {
-                modelRules.remove(tasksModelPath.child(task.getName()).toString());
-            }
-        });
-    }
-
-    private static class TaskFactory implements Factory<Task> {
-        private final TaskContainer tasks;
-        private final String name;
-
-        private TaskFactory(TaskContainer tasks, String name) {
-            this.tasks = tasks;
-            this.name = name;
-        }
-
-        public Task create() {
-            return tasks.getByName(name);
-        }
+        populateModelRegistry(services.get(ModelRegistry.class));
+    }
+
+    private void populateModelRegistry(ModelRegistry modelRegistry) {
+        ModelPath taskFactoryPath = ModelPath.path("taskFactory");
+        ModelCreator taskFactoryCreator = ModelCreators.bridgedInstance(ModelReference.of(taskFactoryPath, ITaskFactory.class), services.get(ITaskFactory.class))
+                .descriptor("Project.<init>.taskFactory")
+                .ephemeral(true)
+                .hidden(true)
+                .build();
+
+        modelRegistry.createOrReplace(taskFactoryCreator);
+
+        modelRegistry.createOrReplace(
+                ModelCreators.bridgedInstance(ModelReference.of("serviceRegistry", ServiceRegistry.class), services)
+                        .descriptor("Project.<init>.serviceRegistry()")
+                        .ephemeral(true)
+                        .hidden(true)
+                        .build()
+        );
+
+        modelRegistry.createOrReplace(
+                ModelCreators.unmanagedInstance(ModelReference.of("buildDir", File.class), new Factory<File>() {
+                    public File create() {
+                        return getBuildDir();
+                    }
+                })
+                        .descriptor("Project.<init>.buildDir()")
+                        .ephemeral(true)
+                        .hidden(true)
+                        .build()
+        );
+
+        modelRegistry.createOrReplace(
+                ModelCreators.bridgedInstance(ModelReference.of("projectIdentifier", ProjectIdentifier.class), this)
+                        .descriptor("Project.<init>.projectIdentifier()")
+                        .ephemeral(true)
+                        .hidden(true)
+                        .build()
+        );
+
+        modelRegistry.createOrReplace(
+                ModelCreators.bridgedInstance(ModelReference.of("extensions", ExtensionContainer.class), getExtensions())
+                        .descriptor("Project.<init>.extensions()")
+                        .ephemeral(true)
+                        .hidden(true)
+                        .build()
+        );
     }
 
     public ProjectInternal getRootProject() {
@@ -254,11 +238,10 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return gradle;
     }
 
-    public PluginContainer getPlugins() {
-        return pluginContainer;
-    }
-
     public ProjectEvaluator getProjectEvaluator() {
+        if (projectEvaluator == null) {
+            projectEvaluator = services.get(ProjectEvaluator.class);
+        }
         return projectEvaluator;
     }
 
@@ -266,8 +249,10 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         this.projectEvaluator = projectEvaluator;
     }
 
+    @Inject
     public ScriptHandler getBuildscript() {
-        return scriptHandler;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     public File getBuildFile() {
@@ -356,15 +341,14 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         this.defaultTasks = defaultTasks;
     }
 
-    public Set<Project> getDependsOnProjects() {
-        return dependsOnProjects;
-    }
-
     public ProjectStateInternal getState() {
         return state;
     }
 
     public FileResolver getFileResolver() {
+        if (fileResolver == null) {
+            fileResolver = services.get(FileResolver.class);
+        }
         return fileResolver;
     }
 
@@ -377,6 +361,9 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
     }
 
     public ArtifactHandler getArtifacts() {
+        if (artifactHandler == null) {
+            artifactHandler = services.get(ArtifactHandler.class);
+        }
         return artifactHandler;
     }
 
@@ -384,15 +371,20 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         this.artifactHandler = artifactHandler;
     }
 
+    @Inject
     public RepositoryHandler getRepositories() {
-        return repositoryHandler;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
-    public ConfigurationContainerInternal getConfigurations() {
+    public ConfigurationContainer getConfigurations() {
+        if (configurationContainer == null) {
+            configurationContainer = services.get(ConfigurationContainer.class);
+        }
         return configurationContainer;
     }
 
-    public void setConfigurationContainer(ConfigurationContainerInternal configurationContainer) {
+    public void setConfigurationContainer(ConfigurationContainer configurationContainer) {
         this.configurationContainer = configurationContainer;
     }
 
@@ -408,8 +400,10 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return depth;
     }
 
+    @Inject
     public ProjectRegistry<ProjectInternal> getProjectRegistry() {
-        return projectRegistry;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     public int depthCompare(Project otherProject) {
@@ -445,15 +439,15 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         if (!isTrue(path)) {
             throw new InvalidUserDataException("A path must be specified!");
         }
-        return projectRegistry.getProject(absoluteProjectPath(path));
+        return getProjectRegistry().getProject(absoluteProjectPath(path));
     }
 
     public Set<Project> getAllprojects() {
-        return new TreeSet<Project>(projectRegistry.getAllProjects(getPath()));
+        return new TreeSet<Project>(getProjectRegistry().getAllProjects(getPath()));
     }
 
     public Set<Project> getSubprojects() {
-        return new TreeSet<Project>(projectRegistry.getSubProjects(getPath()));
+        return new TreeSet<Project>(getProjectRegistry().getSubProjects(getPath()));
     }
 
     public void subprojects(Action<? super Project> action) {
@@ -479,7 +473,7 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
     }
 
     public AntBuilder createAntBuilder() {
-        return antBuilderFactory.create();
+        return getAntBuilderFactory().create();
     }
 
     /**
@@ -490,17 +484,23 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
     }
 
     public AbstractProject evaluate() {
-        projectEvaluator.evaluate(this, state);
+        getProjectEvaluator().evaluate(this, state);
         state.rethrowFailure();
         return this;
     }
 
-    public TaskContainerInternal getTasks() {
-        return taskContainer;
+    @Override
+    public ProjectInternal bindAllModelRules() {
+        try {
+            getModelRegistry().bindAllReferences();
+        } catch (Exception e) {
+            throw new ProjectConfigurationException(String.format("A problem occurred configuring %s.", this), e);
+        }
+        return this;
     }
 
-    public TaskContainerInternal getImplicitTasks() {
-        return implicitTasksContainer;
+    public TaskContainerInternal getTasks() {
+        return taskContainer;
     }
 
     public void defaultTasks(String... defaultTasks) {
@@ -516,28 +516,6 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         }
     }
 
-    public Task createTask(Map args, String name, Closure action) {
-        warnCreateTaskDeprecated();
-        Map<String, Object> allArgs = new HashMap<String, Object>(args);
-        allArgs.put(Task.TASK_NAME, name);
-        allArgs.put(Task.TASK_ACTION, action);
-        return taskContainer.create(allArgs);
-    }
-
-    public Task createTask(Map<String, ?> args, String name, Action<? super Task> action) {
-        warnCreateTaskDeprecated();
-        Map<String, Object> allArgs = new HashMap<String, Object>(args);
-        allArgs.put(Task.TASK_NAME, name);
-        if (action != null) {
-            allArgs.put(Task.TASK_ACTION, action);
-        }
-        return taskContainer.create(allArgs);
-    }
-
-    private void warnCreateTaskDeprecated() {
-        DeprecationLogger.nagUserOfReplacedMethod("Project.createTask()", "task()");
-    }
-
     public void addChildProject(ProjectInternal childProject) {
         childProjects.put(childProject.getName(), childProject);
     }
@@ -554,26 +532,6 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         buildDir = path;
     }
 
-    public void dependsOn(final String path) {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Project.dependsOn(String path)");
-        DeprecationLogger.whileDisabled(new Factory<Void>() {
-            public Void create() {
-                dependsOn(path, true);
-                return null;
-            }
-        });
-    }
-
-    public void dependsOn(String path, boolean evaluateDependsOnProject) {
-        if (!isTrue(path)) {
-            throw new InvalidUserDataException("You must specify a project!");
-        }
-        dependsOnProjects.add(project(path));
-        if (evaluateDependsOnProject) {
-            evaluationDependsOn(path);
-        }
-    }
-
     public void evaluationDependsOnChildren() {
         for (Project project : childProjects.values()) {
             DefaultProject defaultProjectToEvaluate = (DefaultProject) project;
@@ -597,48 +555,15 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return projectToEvaluate.evaluate();
     }
 
-    public Project childrenDependOnMe() {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Project.childrenDependOnMe()");
-        DeprecationLogger.whileDisabled(new Factory<Void>() {
-            public Void create() {
-                for (Project project : childProjects.values()) {
-                    project.dependsOn(getPath(), false);
-                }
-                return null;
-            }
-        });
-
-        return this;
-    }
-
-    public Project dependsOnChildren() {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Project.dependsOnChildren()");
-        return DeprecationLogger.whileDisabled(new Factory<Project>() {
-            public Project create() {
-                return dependsOnChildren(false);
-            }
-        });
-    }
-
-    public Project dependsOnChildren(final boolean evaluateDependsOnProject) {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Project.dependsOnChildren(boolean)");
-        DeprecationLogger.whileDisabled(new Factory<Void>() {
-            public Void create() {
-                for (Project project : childProjects.values()) {
-                    dependsOn(project.getPath(), evaluateDependsOnProject);
-                }
-                return null;
-            }
-        });
-        return this;
-    }
-
     public String toString() {
-        if (parent != null) {
-            return String.format("project '%s'", path);
-        } else {
-            return String.format("root project '%s'", name);
+        StringBuilder builder = new StringBuilder();
+        if (parent == null) {
+            builder.append("root ");
         }
+        builder.append("project '");
+        builder.append(parent == null ? name : path);
+        builder.append("'");
+        return builder.toString();
     }
 
     public Map<Project, Set<Task>> getAllTasks(boolean recursive) {
@@ -663,6 +588,9 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         final Set<Task> foundTasks = new HashSet<Task>();
         Action<Project> action = new Action<Project>() {
             public void execute(Project project) {
+                // Don't force evaluation of rules here, let the task container do what it needs to
+                ((ProjectInternal) project).evaluate();
+
                 Task task = project.getTasks().findByName(name);
                 if (task != null) {
                     foundTasks.add(task);
@@ -677,103 +605,79 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return foundTasks;
     }
 
+    @Inject
+    protected FileOperations getFileOperations() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
     public File file(Object path) {
-        return fileOperations.file(path);
+        return getFileOperations().file(path);
     }
 
     public File file(Object path, PathValidation validation) {
-        return fileOperations.file(path, validation);
+        return getFileOperations().file(path, validation);
     }
 
     public URI uri(Object path) {
-        return fileOperations.uri(path);
+        return getFileOperations().uri(path);
     }
 
     public ConfigurableFileCollection files(Object... paths) {
-        return fileOperations.files(paths);
+        return getFileOperations().files(paths);
     }
 
     public ConfigurableFileCollection files(Object paths, Closure closure) {
-        return fileOperations.files(paths, closure);
+        return ConfigureUtil.configure(closure, getFileOperations().files(paths));
     }
 
     public ConfigurableFileTree fileTree(Object baseDir) {
-        return fileOperations.fileTree(baseDir);
+        return getFileOperations().fileTree(baseDir);
     }
 
     public ConfigurableFileTree fileTree(Object baseDir, Closure closure) {
-        return fileOperations.fileTree(baseDir, closure);
+        return ConfigureUtil.configure(closure, getFileOperations().fileTree(baseDir));
     }
 
     public ConfigurableFileTree fileTree(Map<String, ?> args) {
-        return fileOperations.fileTree(args);
-    }
-
-    public ConfigurableFileTree fileTree(Closure closure) {
-        DeprecationLogger.nagUserOfDeprecated("fileTree(Closure)", "Use fileTree((Object){ baseDir }) to have the closure used as the file tree base directory");
-        return fileOperations.fileTree(closure);
+        return getFileOperations().fileTree(args);
     }
 
     public FileTree zipTree(Object zipPath) {
-        return fileOperations.zipTree(zipPath);
+        return getFileOperations().zipTree(zipPath);
     }
 
     public FileTree tarTree(Object tarPath) {
-        return fileOperations.tarTree(tarPath);
+        return getFileOperations().tarTree(tarPath);
     }
 
     public ResourceHandler getResources() {
-        return fileOperations.getResources();
+        return getFileOperations().getResources();
     }
 
     public String relativePath(Object path) {
-        return fileOperations.relativePath(path);
+        return getFileOperations().relativePath(path);
     }
 
     public File mkdir(Object path) {
-        return fileOperations.mkdir(path);
+        return getFileOperations().mkdir(path);
     }
 
     public boolean delete(Object... paths) {
-        return fileOperations.delete(paths);
-    }
-
-    /**
-     * @deprecated Use the {@link #mkdir(Object)} instead.
-     */
-    @Deprecated
-    public Directory dir(String path) {
-        DeprecationLogger.nagUserOfReplacedMethod("AbstractProject.dir()", "mkdir()");
-        String[] pathElements = path.split("/");
-        String name = "";
-        Directory dirTask = null;
-        for (String pathElement : pathElements) {
-            name += name.length() != 0 ? "/" + pathElement : pathElement;
-            Task task = taskContainer.findByName(name);
-            if (task instanceof Directory) {
-                dirTask = (Directory) task;
-            } 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.create(name, Directory.class);
-            }
-        }
-        return dirTask;
-    }
-
-    public void setTaskContainer(TaskContainerInternal taskContainer) {
-        this.taskContainer = taskContainer;
+        return getFileOperations().delete(paths);
     }
 
     public Factory<AntBuilder> getAntBuilderFactory() {
+        if (antBuilderFactory == null) {
+            antBuilderFactory = services.getFactory(AntBuilder.class);
+        }
         return antBuilderFactory;
     }
 
-    public void setAntBuilderFactory(Factory<AntBuilder> antBuilderFactory) {
-        this.antBuilderFactory = antBuilderFactory;
-    }
-
     public DependencyHandler getDependencies() {
+        if (dependencyHandler == null) {
+            dependencyHandler = services.get(DependencyHandler.class);
+        }
         return dependencyHandler;
     }
 
@@ -806,15 +710,19 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
     }
 
     public StandardOutputCapture getStandardOutputCapture() {
-        return loggingManager;
+        return getLogging();
     }
 
-    public LoggingManager getLogging() {
-        return loggingManager;
+    @Inject
+    public LoggingManagerInternal getLogging() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
+    @Inject
     public SoftwareComponentContainer getComponents() {
-        return softwareComponentContainer;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     public Object property(String propertyName) throws MissingPropertyException {
@@ -838,27 +746,45 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
     }
 
     public WorkResult copy(Closure closure) {
-        return fileOperations.copy(closure);
+        return copy(new ClosureBackedAction<CopySpec>(closure));
+    }
+
+    public WorkResult copy(Action<? super CopySpec> action) {
+        return getFileOperations().copy(action);
     }
 
     public WorkResult sync(Action<? super CopySpec> action) {
-        return fileOperations.sync(action);
+        return getFileOperations().sync(action);
     }
 
     public CopySpec copySpec(Closure closure) {
-        return fileOperations.copySpec(closure);
+        return copySpec(new ClosureBackedAction<CopySpec>(closure));
     }
 
     public CopySpec copySpec(Action<? super CopySpec> action) {
-        return fileOperations.copySpec(action);
+        return getFileOperations().copySpec(action);
+    }
+
+    @Inject
+    protected ProcessOperations getProcessOperations() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     public ExecResult javaexec(Closure closure) {
-        return processOperations.javaexec(closure);
+        return javaexec(new ClosureBackedAction<JavaExecSpec>(closure));
+    }
+
+    public ExecResult javaexec(Action<? super JavaExecSpec> action) {
+        return getProcessOperations().javaexec(action);
     }
 
     public ExecResult exec(Closure closure) {
-        return processOperations.exec(closure);
+        return exec(new ClosureBackedAction<ExecSpec>(closure));
+    }
+
+    public ExecResult exec(Action<? super ExecSpec> action) {
+        return getProcessOperations().exec(action);
     }
 
     public ServiceRegistry getServices() {
@@ -952,29 +878,61 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return task(options, task.toString(), configureClosure);
     }
 
+    @Inject
     public ProjectConfigurationActionContainer getConfigurationActions() {
-        return configurationActions;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
+    @Inject
     public ModelRegistry getModelRegistry() {
-        return modelRegistry;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ModelSchemaStore getModelSchemaStore() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ModelCreatorFactory getModelCreatorFactory() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
     @Override
+    protected DefaultObjectConfigurationAction createObjectConfigurationAction() {
+        return new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), getScriptHandlerFactory(), getBaseClassLoaderScope(), this);
+    }
+
+    @Inject
+    public PluginManagerInternal getPluginManager() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
     protected ScriptPluginFactory getScriptPluginFactory() {
-        return scriptPluginFactory;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
-    @Override
+    @Inject
     protected ScriptHandlerFactory getScriptHandlerFactory() {
-        return scriptHandlerFactory;
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
-    @Override
     public ClassLoaderScope getClassLoaderScope() {
         return classLoaderScope;
     }
 
+    public ClassLoaderScope getBaseClassLoaderScope() {
+        return baseClassLoaderScope;
+    }
+
     /**
      * This is called by the task creation DSL. Need to find a cleaner way to do this...
      */
@@ -1001,11 +959,32 @@ public abstract class AbstractProject extends AbstractPluginAware implements Pro
         return (ExtensionContainerInternal) getConvention();
     }
 
-    // This is here temporarily as a quick way to expose it in the build script
-    // Longer term it will not be available via Project, but be only available in a build script
-    public void model(Closure action) {
-        new GroovyModelDsl(modelRules).configure(action);
+
+    public void model(Closure<?> modelRules) {
+        ModelRegistry modelRegistry = getModelRegistry();
+        ModelSchemaStore modelSchemaStore = getModelSchemaStore();
+        ModelCreatorFactory modelCreatorFactory = getModelCreatorFactory();
+
+        if (TransformedModelDslBacking.isTransformedBlock(modelRules)) {
+            ClosureBackedAction.execute(new TransformedModelDslBacking(modelRegistry, modelSchemaStore, modelCreatorFactory), modelRules);
+        } else {
+            new NonTransformedModelDslBacking(modelRegistry, modelSchemaStore, modelCreatorFactory).configure(modelRules);
+        }
+    }
+
+    @Inject
+    protected DeferredProjectConfiguration getDeferredProjectConfiguration() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
     }
 
+    public void addDeferredConfiguration(Runnable configuration) {
+        getDeferredProjectConfiguration().add(configuration);
+    }
+
+    @Override
+    public void fireDeferredConfiguration() {
+        getDeferredProjectConfiguration().fire();
+    }
 
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.java
index e7e691c..ddfc00c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilder.java
@@ -16,6 +16,8 @@
 
 package org.gradle.api.internal.project;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import groovy.lang.GroovyObject;
 import groovy.lang.MissingPropertyException;
 import groovy.util.ObservableMap;
@@ -23,13 +25,12 @@ import org.apache.tools.ant.MagicNames;
 import org.apache.tools.ant.ProjectHelper;
 import org.apache.tools.ant.PropertyHelper;
 import org.apache.tools.ant.Target;
-import org.gradle.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
+import org.gradle.api.*;
 import org.gradle.api.internal.project.ant.BasicAntBuilder;
 import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.tasks.TaskDependency;
 import org.gradle.api.tasks.ant.AntTarget;
+import org.gradle.internal.Transformers;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
@@ -86,6 +87,10 @@ public class DefaultAntBuilder extends BasicAntBuilder implements GroovyObject {
     }
 
     public void importBuild(Object antBuildFile) {
+        importBuild(antBuildFile, Transformers.<String>noOpTransformer());
+    }
+
+    public void importBuild(Object antBuildFile, Transformer<? extends String, ? super String> taskNamer) {
         File file = gradleProject.file(antBuildFile);
         final File baseDir = file.getParentFile();
 
@@ -108,18 +113,36 @@ public class DefaultAntBuilder extends BasicAntBuilder implements GroovyObject {
         Set<String> newAntTargets = new HashSet<String>(getAntProject().getTargets().keySet());
         newAntTargets.removeAll(existingAntTargets);
         for (String name : newAntTargets) {
-            Target target = getAntProject().getTargets().get(name);
-            AntTarget task = gradleProject.getTasks().create(target.getName(), AntTarget.class);
-            task.setTarget(target);
-            task.setBaseDir(baseDir);
-            addDependencyOrdering(target.getDependencies());
+            final Target target = getAntProject().getTargets().get(name);
+            String taskName = taskNamer.transform(target.getName());
+            final AntTarget task = gradleProject.getTasks().create(taskName, AntTarget.class);
+            configureTask(target, task, baseDir, taskNamer);
+        }
+    }
+
+    private static void configureTask(Target target, AntTarget task, File baseDir, Transformer<? extends String, ? super String> taskNamer) {
+        task.setTarget(target);
+        task.setBaseDir(baseDir);
+
+        final List<String> taskDependencyNames = getTaskDependencyNames(target, taskNamer);
+        task.dependsOn(new AntTargetsTaskDependency(taskDependencyNames));
+        addDependencyOrdering(taskDependencyNames, task.getProject().getTasks());
+    }
+
+    private static List<String> getTaskDependencyNames(Target target, Transformer<? extends String, ? super String> taskNamer) {
+        Enumeration<String> dependencies = target.getDependencies();
+        List<String> taskDependencyNames = Lists.newLinkedList();
+        while (dependencies.hasMoreElements()) {
+            String targetName = dependencies.nextElement();
+            String taskName = taskNamer.transform(targetName);
+            taskDependencyNames.add(taskName);
         }
+        return taskDependencyNames;
     }
 
-    private void addDependencyOrdering(Enumeration<String> dependencies) {
-        TaskContainer tasks = gradleProject.getTasks();
+    private static void addDependencyOrdering(List<String> dependencies, TaskContainer tasks) {
         String previous = null;
-        for (final String dependency : Collections.list(dependencies)) {
+        for (final String dependency : dependencies) {
             if (previous != null) {
                 final String finalPrevious = previous;
                 tasks.all(new Action<Task>() {
@@ -135,4 +158,23 @@ public class DefaultAntBuilder extends BasicAntBuilder implements GroovyObject {
         }
     }
 
+    private static class AntTargetsTaskDependency implements TaskDependency {
+        private final List<String> taskDependencyNames;
+
+        public AntTargetsTaskDependency(List<String> taskDependencyNames) {
+            this.taskDependencyNames = taskDependencyNames;
+        }
+
+        public Set<? extends Task> getDependencies(Task task) {
+            Set<Task> tasks = Sets.newHashSetWithExpectedSize(taskDependencyNames.size());
+            for (String dependedOnTaskName : taskDependencyNames) {
+                Task dependency = task.getProject().getTasks().findByName(dependedOnTaskName);
+                if (dependency == null) {
+                    throw new UnknownTaskException(String.format("Imported Ant target '%s' depends on target or task '%s' which does not exist", task.getName(), dependedOnTaskName));
+                }
+                tasks.add(dependency);
+            }
+            return tasks;
+        }
+    }
 }
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 d879255..324d3dd 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
@@ -21,7 +21,9 @@ import org.gradle.api.Project;
 import org.gradle.internal.Factory;
 import org.gradle.internal.concurrent.CompositeStoppable;
 
-public class DefaultAntBuilderFactory implements Factory<AntBuilder> {
+import java.io.Closeable;
+
+public class DefaultAntBuilderFactory implements Factory<AntBuilder>, Closeable {
     private final BuildListener buildListener;
     private final Project project;
     private final CompositeStoppable stoppable = new CompositeStoppable();
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 ce7ebd0..3967fe9 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
@@ -15,93 +15,85 @@
  */
 package org.gradle.api.internal.project
 
+import com.google.common.collect.Lists
 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.api.logging.LogLevel
+import org.gradle.api.logging.Logger
 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.ClassPath
 import org.gradle.internal.classpath.DefaultClassPath
+import org.gradle.internal.jvm.Jvm
+import org.gradle.util.ConfigureUtil
+
+// TODO: should be threadsafe; is stateful and of build scope
 
 class DefaultIsolatedAntBuilder implements IsolatedAntBuilder {
-    private final Map<List<File>, ClassLoader> baseClassloaders = [:]
-    private final Map<List<File>, Map<String, Object>> classloaders = [:]
+
+    private final ClassLoader baseAntLoader
+    private final ClassLoader gradleLoader
+    private final Map<ClassPath, ClassLoader> classloaders
     private final ClassPathRegistry classPathRegistry
     private final ClassLoaderFactory classLoaderFactory
-    private final Iterable<File> groovyClasspath
-    private final Iterable<File> libClasspath = []
+    private final ClassPath libClasspath
 
-    def DefaultIsolatedAntBuilder(ClassPathRegistry classPathRegistry, ClassLoaderFactory classLoaderFactory) {
+    DefaultIsolatedAntBuilder(ClassPathRegistry classPathRegistry, ClassLoaderFactory classLoaderFactory) {
         this.classPathRegistry = classPathRegistry
         this.classLoaderFactory = classLoaderFactory
-        groovyClasspath = classPathRegistry.getClassPath("GROOVY").asFiles
+        this.classloaders = [:]
+        this.libClasspath = new DefaultClassPath()
+
+        List<File> antClasspath = Lists.newArrayList(classPathRegistry.getClassPath("ANT").asFiles)
+        // Need tools.jar for compile tasks
+        File toolsJar = Jvm.current().toolsJar
+        if (toolsJar) {
+            antClasspath += toolsJar
+        }
+
+        def antLoader = classLoaderFactory.createIsolatedClassLoader(new DefaultClassPath(antClasspath))
+        def loggingLoader = new FilteringClassLoader(getClass().classLoader)
+        loggingLoader.allowPackage('org.slf4j')
+        loggingLoader.allowPackage('org.apache.commons.logging')
+        loggingLoader.allowPackage('org.apache.log4j')
+        loggingLoader.allowClass(Logger)
+        loggingLoader.allowClass(LogLevel)
+        this.baseAntLoader = new MultiParentClassLoader(antLoader, loggingLoader)
+
+        // Need gradle core to pick up ant logging adapter, AntBuilder and such
+        def gradleCoreUrls = classPathRegistry.getClassPath("GRADLE_CORE")
+        gradleCoreUrls += classPathRegistry.getClassPath("GROOVY")
+
+        // Need Transformer (part of AntBuilder API) from base services
+        gradleCoreUrls += classPathRegistry.getClassPath("GRADLE_BASE_SERVICES")
+        this.gradleLoader = new MutableURLClassLoader(baseAntLoader, gradleCoreUrls)
     }
 
-    private DefaultIsolatedAntBuilder(DefaultIsolatedAntBuilder copy, Iterable<File> groovyClasspath, Iterable<File> libClasspath) {
+    private DefaultIsolatedAntBuilder(DefaultIsolatedAntBuilder copy, Iterable<File> libClasspath) {
         this.classPathRegistry = copy.classPathRegistry
         this.classLoaderFactory = copy.classLoaderFactory
         this.classloaders = copy.classloaders
-        this.baseClassloaders = copy.baseClassloaders
-        this.groovyClasspath = groovyClasspath
-        this.libClasspath = libClasspath
-    }
-
-    IsolatedAntBuilder withGroovy(Iterable<File> classpath) {
-        return new DefaultIsolatedAntBuilder(this, classpath, libClasspath);
+        this.baseAntLoader = copy.baseAntLoader
+        this.gradleLoader = copy.gradleLoader
+        this.libClasspath = new DefaultClassPath(libClasspath)
     }
 
     IsolatedAntBuilder withClasspath(Iterable<File> classpath) {
-        return new DefaultIsolatedAntBuilder(this, groovyClasspath, classpath);
+        return new DefaultIsolatedAntBuilder(this, classpath)
     }
 
     void execute(Closure antClosure) {
-        List<File> baseClasspath = []
-        baseClasspath.addAll(classPathRegistry.getClassPath("ANT").asFiles)
-        baseClasspath.addAll(groovyClasspath as List)
-
-        ClassLoader baseLoader = baseClassloaders[baseClasspath]
-        if (baseLoader == null) {
-            // Need tools.jar for compile tasks
-            List<File> fullClasspath = baseClasspath
-            File toolsJar = Jvm.current().toolsJar
-            if (toolsJar) {
-                fullClasspath += toolsJar
-            }
-            baseLoader = classLoaderFactory.createIsolatedClassLoader(new DefaultClassPath(fullClasspath))
-            baseClassloaders[baseClasspath] = baseLoader
-        }
-
-        List<File> normalisedClasspath = []
-        normalisedClasspath.addAll(libClasspath as List)
-
-        Map<String, Object> classloadersForPath = classloaders[normalisedClasspath]
-        ClassLoader antLoader
-        ClassLoader gradleLoader
-        if (classloadersForPath) {
-            antLoader = classloadersForPath.antLoader
-            gradleLoader = classloadersForPath.gradleLoader
-        } else {
-            // Need gradle core to pick up ant logging adapter, AntBuilder and such
-            def gradleCoreUrls = classPathRegistry.getClassPath("GRADLE_CORE")
-
-            FilteringClassLoader loggingLoader = new FilteringClassLoader(getClass().classLoader)
-            loggingLoader.allowPackage('org.slf4j')
-            loggingLoader.allowPackage('org.apache.commons.logging')
-            loggingLoader.allowPackage('org.apache.log4j')
-            ClassLoader parent = new MultiParentClassLoader(baseLoader, loggingLoader)
-
-            List<URL> classpathUrls = normalisedClasspath.collect { it.toURI().toURL() }
-            antLoader = new URLClassLoader(classpathUrls as URL[], parent)
-            gradleLoader = new MutableURLClassLoader(parent, gradleCoreUrls)
-
-            classloaders[normalisedClasspath] = [antLoader: antLoader, gradleLoader: gradleLoader]
+        def classLoader = classloaders[libClasspath]
+        if (!classLoader) {
+            classLoader = new URLClassLoader(libClasspath.asURLArray, baseAntLoader)
+            classloaders[libClasspath] = classLoader
         }
 
         ClassLoader originalLoader = Thread.currentThread().contextClassLoader
-        Thread.currentThread().contextClassLoader = antLoader
+        Thread.currentThread().contextClassLoader = classLoader
         try {
             Object antBuilder = gradleLoader.loadClass(BasicAntBuilder.class.name).newInstance()
 
@@ -112,12 +104,13 @@ class DefaultIsolatedAntBuilder implements IsolatedAntBuilder {
             // Ideally, we'd delegate directly to the AntBuilder, but it's Closure class is different to our caller's
             // Closure class, so the AntBuilder's methodMissing() doesn't work. It just converts our Closures to String
             // because they are not an instanceof it's Closure class
-            Object delegate = new AntBuilderDelegate(antBuilder, antLoader)
+            Object delegate = new AntBuilderDelegate(antBuilder, classLoader)
             ConfigureUtil.configure(antClosure, delegate)
         } finally {
             Thread.currentThread().contextClassLoader = originalLoader
         }
     }
+
 }
 
 class AntBuilderDelegate extends BuilderSupport {
@@ -132,17 +125,17 @@ class AntBuilderDelegate extends BuilderSupport {
     def getAnt() {
         return this
     }
-    
+
     def taskdef(Map<String, ?> args) {
         if (args.keySet() == ['name', 'classname'] as Set) {
-            builder.project.addTaskDefinition(args.name, antlibClassLoader.loadClass(args.classname))            
+            builder.project.addTaskDefinition(args.name, antlibClassLoader.loadClass(args.classname))
         } else if (args.keySet() == ['resource'] as Set) {
             antlibClassLoader.getResource(args.resource).withInputStream { instr ->
                 def xml = new XmlParser().parse(instr)
                 xml.taskdef.each {
                     builder.project.addTaskDefinition(it. at name, antlibClassLoader.loadClass(it. at classname))
                 }
-            }            
+            }
         } else {
             throw new RuntimeException("Unsupported parameters for taskdef().")
         }
@@ -157,7 +150,11 @@ class AntBuilderDelegate extends BuilderSupport {
     }
 
     protected Object createNode(Object name, Map attributes) {
-        builder.createNode(name, attributes)
+        if (name == "taskdef") {
+            taskdef(attributes)
+        } else {
+            builder.createNode(name, attributes)
+        }
     }
 
     protected Object createNode(Object name, Map attributes, Object value) {
@@ -173,6 +170,10 @@ class AntBuilderDelegate extends BuilderSupport {
     }
 
     protected void nodeCompleted(Object parent, Object node) {
+        if (parent == null && node == null) { // happens when dispatching to taskdef via createNode()
+            return
+        }
+
         builder.nodeCompleted(parent, node)
     }
 
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 5cc774f..aab2543 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
@@ -27,7 +27,7 @@ import java.io.File;
 @NoConventionMapping
 public class DefaultProject extends AbstractProject {
     public DefaultProject(String name, ProjectInternal parent, File projectDir, ScriptSource buildScriptSource,
-                          GradleInternal gradle, ServiceRegistryFactory serviceRegistryFactory, ClassLoaderScope classLoaderScope) {
-        super(name, parent, projectDir, buildScriptSource, gradle, serviceRegistryFactory, classLoaderScope);
+                          GradleInternal gradle, ServiceRegistryFactory serviceRegistryFactory, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope) {
+        super(name, parent, projectDir, buildScriptSource, gradle, serviceRegistryFactory, selfClassLoaderScope, baseClassLoaderScope);
     }
 }
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 7583d3c..0a4167b 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
@@ -21,10 +21,15 @@ import org.gradle.initialization.ProjectAccessListener;
 public class DefaultProjectAccessListener implements ProjectAccessListener {
 
     public void beforeRequestingTaskByPath(ProjectInternal targetProject) {
-        targetProject.evaluate();
+        evaluateProjectAndDiscoverTasks(targetProject);
+    }
+
+    public void beforeResolvingProjectDependency(ProjectInternal targetProject) {
+        evaluateProjectAndDiscoverTasks(targetProject);
     }
 
-    public void beforeResolvingProjectDependency(ProjectInternal dependencyProject) {
-        dependencyProject.evaluate();
+    private void evaluateProjectAndDiscoverTasks(ProjectInternal targetProject) {
+        targetProject.evaluate();
+        targetProject.getTasks().discoverTasks();
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectTaskLister.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectTaskLister.java
new file mode 100644
index 0000000..6915751
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DefaultProjectTaskLister.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.project;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
+
+import java.util.Collection;
+
+public class DefaultProjectTaskLister implements ProjectTaskLister {
+    public Collection<Task> listProjectTasks(Project project) {
+        ProjectInternal projectInternal = (ProjectInternal) project;
+        TaskContainerInternal tasks = projectInternal.getTasks();
+        tasks.realize();
+        return tasks;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DeferredProjectConfiguration.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DeferredProjectConfiguration.java
new file mode 100644
index 0000000..d6ed911
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/DeferredProjectConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.collect.Lists;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Project;
+
+import java.util.List;
+
+ at NotThreadSafe
+public class DeferredProjectConfiguration {
+
+    private final static String TRACE = "org.gradle.trace.deferred.project.configuration";
+
+    private final Project project;
+    private final List<Runnable> configuration = Lists.newLinkedList();
+    private boolean fired;
+
+    private Throwable firedSentinel;
+
+    public DeferredProjectConfiguration(Project project) {
+        this.project = project;
+    }
+
+    public void add(Runnable configuration) {
+        if (fired) {
+            String message = "Cannot add deferred configuration for project " + project.getPath();
+            if (firedSentinel == null) {
+                throw new IllegalStateException(message);
+            } else {
+                throw new IllegalStateException(message, firedSentinel);
+            }
+        } else {
+            this.configuration.add(configuration);
+        }
+    }
+
+    public void fire() {
+        if (!fired) {
+            if (Boolean.getBoolean(TRACE)) {
+                firedSentinel = new Exception("Project '" + project.getPath() + "' deferred configuration fired");
+            }
+            fired = true;
+            for (Runnable runnable : configuration) {
+                runnable.run();
+            }
+        }
+    }
+
+}
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 93e782c..a79c462 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
@@ -23,5 +23,5 @@ import org.gradle.api.internal.initialization.ClassLoaderScope;
  * Creates a {@link ProjectInternal} implementation.
  */
 public interface IProjectFactory {
-    ProjectInternal createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle, ClassLoaderScope classLoaderScope);
+    ProjectInternal createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IsolatedAntBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IsolatedAntBuilder.java
index 23213bf..19e25f8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IsolatedAntBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/IsolatedAntBuilder.java
@@ -23,14 +23,6 @@ import java.io.File;
  * Executes a closure against an isolated {@link org.gradle.api.AntBuilder} instance.
  */
 public interface IsolatedAntBuilder {
-    /**
-     * Creates a copy of this builder which uses the given version of Groovy. The default is to use the version of
-     * Groovy which Gradle is using.
-     *
-     * @param classpath The Groovy classpath.
-     * @return a copy of this builder
-     */
-    IsolatedAntBuilder withGroovy(Iterable<File> classpath);
 
     /**
      * Creates a copy of this builder which uses the given libraries. These classes are visible for use in
@@ -45,8 +37,6 @@ public interface IsolatedAntBuilder {
      * Executes the given closure against an isolated {@link org.gradle.api.AntBuilder} instance. The builder will
      * have visible to it an isolated version of Ant, Groovy and the specified libraries (if any). Each call to this
      * method is given a separate Ant project.
-     *
-     * @param antClosure The closure to execute
      */
     void execute(Closure antClosure);
 }
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 4e6c7f7..698fb97 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
@@ -20,7 +20,6 @@ import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.UriScriptSource;
 import org.gradle.internal.reflect.Instantiator;
 
@@ -35,15 +34,9 @@ public class ProjectFactory implements IProjectFactory {
         this.projectRegistry = projectRegistry;
     }
 
-    public DefaultProject createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
+    public DefaultProject createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope) {
         File buildFile = projectDescriptor.getBuildFile();
-        ScriptSource source;
-        if (!buildFile.exists()) {
-            source = new StringScriptSource("empty build file", "");
-        } else {
-            source = new UriScriptSource("build file", buildFile);
-        }
-
+        ScriptSource source = UriScriptSource.file("build file", buildFile);
         DefaultProject project = instantiator.newInstance(DefaultProject.class,
                 projectDescriptor.getName(),
                 parent,
@@ -51,8 +44,9 @@ public class ProjectFactory implements IProjectFactory {
                 source,
                 gradle,
                 gradle.getServiceRegistryFactory(),
-                classLoaderScope
-                );
+                selfClassLoaderScope,
+                baseClassLoaderScope
+        );
 
         if (parent != null) {
             parent.addChildProject(project);
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 0a08323..e5f42c8 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
@@ -23,12 +23,12 @@ import org.gradle.api.internal.DomainObjectContext;
 import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.ProcessOperations;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
 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.initialization.ClassLoaderScope;
 import org.gradle.api.internal.plugins.ExtensionContainerInternal;
+import org.gradle.api.internal.plugins.PluginAwareInternal;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.configuration.project.ProjectConfigurationActionContainer;
 import org.gradle.groovy.scripts.ScriptAware;
@@ -36,20 +36,26 @@ import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.logging.StandardOutputCapture;
-import org.gradle.model.internal.ModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistryScope;
+
+public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware, FileOperations, ProcessOperations, DomainObjectContext, DependencyMetaDataProvider, ModelRegistryScope, PluginAwareInternal {
+
+    // These constants are defined here and not with the rest of their kind in HelpTasksPlugin because they are referenced
+    // in the ‘core’ and ‘ui’ modules, which don't depend on ‘plugins’ where HelpTasksPlugin is defined.
+    String HELP_TASK = "help";
+    String TASKS_TASK = "tasks";
+    String PROJECTS_TASK = "projects";
 
-public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware, FileOperations, ProcessOperations, DomainObjectContext, DependencyMetaDataProvider {
     ProjectInternal getParent();
 
     ProjectInternal getRootProject();
 
     Project evaluate();
 
-    TaskContainerInternal getTasks();
-
-    TaskContainerInternal getImplicitTasks();
+    ProjectInternal bindAllModelRules();
 
-    ConfigurationContainerInternal getConfigurations();
+    TaskContainerInternal getTasks();
 
     ScriptSource getBuildScriptSource();
 
@@ -85,4 +91,9 @@ public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware
 
     ClassLoaderScope getClassLoaderScope();
 
+    ClassLoaderScope getBaseClassLoaderScope();
+
+    void addDeferredConfiguration(Runnable configuration);
+
+    void fireDeferredConfiguration();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectTaskLister.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectTaskLister.java
new file mode 100644
index 0000000..bf46863
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ProjectTaskLister.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.project;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+
+import java.util.Collection;
+
+/**
+ * Service to provide all tasks in a project including both regular tasks,
+ * and implicit tasks.
+ */
+public interface ProjectTaskLister {
+    Collection<Task> listProjectTasks(Project project);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/BasicAntBuilder.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/BasicAntBuilder.java
index e1d9b98..f395958 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/BasicAntBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/ant/BasicAntBuilder.java
@@ -18,14 +18,16 @@ package org.gradle.api.internal.project.ant;
 import groovy.util.AntBuilder;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.Target;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.file.ant.AntFileResource;
 import org.gradle.api.internal.file.ant.BaseDirSelector;
 
+import java.io.Closeable;
 import java.lang.reflect.Field;
 import java.util.List;
 import java.util.Map;
 
-public class BasicAntBuilder extends org.gradle.api.AntBuilder {
+public class BasicAntBuilder extends org.gradle.api.AntBuilder implements Closeable {
     private final Field nodeField;
     private final List children;
 
@@ -63,6 +65,10 @@ public class BasicAntBuilder extends org.gradle.api.AntBuilder {
         throw new UnsupportedOperationException();
     }
 
+    public void importBuild(Object antBuildFile, Transformer<? extends String, ? super String> taskNamer) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     protected void nodeCompleted(Object parent, Object node) {
         ClassLoader original = Thread.currentThread().getContextClassLoader();
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 28059ef..7cb5e65 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
@@ -96,12 +96,19 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
     }
 
     public TaskInternal createTask(Map<String, ?> args) {
-        TaskInternal task = taskFactory.createTask(args);
+        return process(taskFactory.createTask(args));
+    }
+
+    @Override
+    public <S extends TaskInternal> S create(String name, Class<S> type) {
+        return process(taskFactory.create(name, type));
+    }
+
+    private <S extends TaskInternal> S process(S task) {
         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.
+            // Add a dummy upToDateWhen spec: this will force TaskOutputs.hasOutputs() to be true.
             task.getOutputs().upToDateWhen(new Spec<Task>() {
                 public boolean isSatisfiedBy(Task element) {
                     return true;
@@ -110,11 +117,11 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
         }
 
         for (Factory<Action<Task>> actionFactory : taskClassInfo.taskActions) {
-            task.doFirst(actionFactory.create());
+            task.prependParallelSafeAction(actionFactory.create());
         }
 
         if (taskClassInfo.validator != null) {
-            task.doFirst(taskClassInfo.validator);
+            task.prependParallelSafeAction(taskClassInfo.validator);
             taskClassInfo.validator.addInputsAndOutputs(task);
         }
 
@@ -335,7 +342,7 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
                 return;
             }
 
-            Annotation optional = annotationTarget.getAnnotation(Optional.class);
+            Annotation optional = annotationTarget.getAnnotation(org.gradle.api.tasks.Optional.class);
             if (optional == null) {
                 propertyInfo.setNotNullValidator(notNullValidator);
             }
@@ -369,7 +376,7 @@ public class AnnotationProcessingTaskFactory implements ITaskFactory {
             }
         };
         private static final UpdateAction NO_OP_CONFIGURATION_ACTION = new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(TaskInternal task, Callable<Object> futureValue) {
             }
         };
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/DependencyAutoWireTaskFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/DependencyAutoWireTaskFactory.java
index 3976acb..6d28956 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/DependencyAutoWireTaskFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/DependencyAutoWireTaskFactory.java
@@ -36,7 +36,15 @@ public class DependencyAutoWireTaskFactory implements ITaskFactory {
     }
 
     public TaskInternal createTask(Map<String, ?> args) {
-        TaskInternal task = taskFactory.createTask(args);
+        return autoWire(taskFactory.createTask(args));
+    }
+
+    @Override
+    public <S extends TaskInternal> S create(String name, Class<S> type) {
+        return autoWire(taskFactory.create(name, type));
+    }
+
+    private <S extends TaskInternal> S autoWire(S task) {
         task.dependsOn(task.getInputs().getFiles());
         return task;
     }
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 c18d79a..bcc667c 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
@@ -18,10 +18,11 @@ package org.gradle.api.internal.project.taskfactory;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
 
 import java.util.Map;
 
-public interface ITaskFactory {
+public interface ITaskFactory extends NamedEntityInstantiator<TaskInternal> {
     public ITaskFactory createChild(ProjectInternal project, Instantiator instantiator);
 
     public TaskInternal createTask(Map<String, ?> args);
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
index 43a0ba4..7a0666f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputDirectoryPropertyAnnotationHandler.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
 import org.gradle.api.file.ConfigurableFileTree;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.InputDirectory;
 import org.gradle.api.tasks.SkipWhenEmpty;
 
@@ -45,7 +45,7 @@ public class InputDirectoryPropertyAnnotationHandler implements PropertyAnnotati
         context.setValidationAction(inputDirValidation);
         final boolean isSourceDir = context.getTarget().getAnnotation(SkipWhenEmpty.class) != null;
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(TaskInternal task, Callable<Object> futureValue) {
                 if (isSourceDir) {
                     task.getInputs().sourceDir(futureValue);
                 } else {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
index f31b7b5..de7294d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilePropertyAnnotationHandler.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.InputFile;
 
 import java.io.File;
@@ -42,7 +42,7 @@ public class InputFilePropertyAnnotationHandler implements PropertyAnnotationHan
     public void attachActions(PropertyActionContext context) {
         context.setValidationAction(inputFileValidation);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(TaskInternal task, Callable<Object> futureValue) {
                 task.getInputs().files(futureValue);
             }
         });
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
index 148c481..a430930 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputFilesPropertyAnnotationHandler.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.SkipWhenEmpty;
 
@@ -30,7 +30,7 @@ public class InputFilesPropertyAnnotationHandler implements PropertyAnnotationHa
     public void attachActions(PropertyActionContext context) {
         final boolean isSourceFiles = context.getTarget().getAnnotation(SkipWhenEmpty.class) != null;
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(TaskInternal task, Callable<Object> futureValue) {
                 if (isSourceFiles) {
                     task.getInputs().source(futureValue);
                 } else {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputPropertyAnnotationHandler.java
index 933f3a7..028182b 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/InputPropertyAnnotationHandler.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.Input;
 
 import java.lang.annotation.Annotation;
@@ -28,7 +28,7 @@ public class InputPropertyAnnotationHandler implements PropertyAnnotationHandler
 
     public void attachActions(final PropertyActionContext context) {
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, Callable<Object> futureValue) {
+            public void update(TaskInternal task, Callable<Object> futureValue) {
                 task.getInputs().property(context.getName(), futureValue);
             }
         });
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
index 607d019..8b7e9dd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.Nested;
 
 import java.lang.annotation.Annotation;
@@ -34,7 +34,7 @@ public class NestedBeanPropertyAnnotationHandler implements PropertyAnnotationHa
         }
         context.attachActions(nestedType);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, final Callable<Object> futureValue) {
+            public void update(TaskInternal task, final Callable<Object> futureValue) {
                 task.getInputs().property(context.getName() + ".class", new Callable<Object>() {
                     public Object call() throws Exception {
                         Object bean = futureValue.call();
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
index b39b0c2..21da2b3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputDirectoryPropertyAnnotationHandler.java
@@ -18,6 +18,7 @@ package org.gradle.api.internal.project.taskfactory;
 import org.gradle.api.Action;
 import org.gradle.api.Task;
 import org.gradle.api.Transformer;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.internal.UncheckedException;
 import org.gradle.util.GFileUtils;
 
@@ -61,9 +62,9 @@ public class OutputDirectoryPropertyAnnotationHandler implements PropertyAnnotat
     public void attachActions(final PropertyActionContext context) {
         context.setValidationAction(outputDirValidation);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, final Callable<Object> futureValue) {
+            public void update(TaskInternal task, final Callable<Object> futureValue) {
                 task.getOutputs().files(futureValue);
-                task.doFirst(new Action<Task>() {
+                task.prependParallelSafeAction(new Action<Task>() {
                     public void execute(Task task) {
                         Iterable<File> files;
                         try {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
index 567c5fc..8a57e2c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/OutputFilePropertyAnnotationHandler.java
@@ -18,6 +18,7 @@ package org.gradle.api.internal.project.taskfactory;
 import org.gradle.api.Action;
 import org.gradle.api.Task;
 import org.gradle.api.Transformer;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.internal.UncheckedException;
 import org.gradle.util.GFileUtils;
 
@@ -60,9 +61,9 @@ public class OutputFilePropertyAnnotationHandler implements PropertyAnnotationHa
     public void attachActions(final PropertyActionContext context) {
         context.setValidationAction(outputDirValidation);
         context.setConfigureAction(new UpdateAction() {
-            public void update(Task task, final Callable<Object> futureValue) {
+            public void update(TaskInternal task, final Callable<Object> futureValue) {
                 task.getOutputs().files(futureValue);
-                task.doFirst(new Action<Task>() {
+                task.prependParallelSafeAction(new Action<Task>() {
                     public void execute(Task task) {
                         Iterable<File> files;
                         try {
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 e3fef78..f89d38b 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
@@ -36,7 +36,6 @@ import java.util.Set;
 import java.util.concurrent.Callable;
 
 public class TaskFactory implements ITaskFactory {
-    public static final String GENERATE_SUBCLASS = "generateSubclass";
     private final ClassGenerator generator;
     private final ProjectInternal project;
     private final Instantiator instantiator;
@@ -51,7 +50,6 @@ public class TaskFactory implements ITaskFactory {
         this.project = project;
         this.instantiator = instantiator;
 
-
         validTaskArguments = new HashSet<String>();
         validTaskArguments.add(Task.TASK_ACTION);
         validTaskArguments.add(Task.TASK_DEPENDS_ON);
@@ -60,7 +58,6 @@ public class TaskFactory implements ITaskFactory {
         validTaskArguments.add(Task.TASK_NAME);
         validTaskArguments.add(Task.TASK_OVERWRITE);
         validTaskArguments.add(Task.TASK_TYPE);
-
     }
 
     public ITaskFactory createChild(ProjectInternal project, Instantiator instantiator) {
@@ -77,8 +74,7 @@ public class TaskFactory implements ITaskFactory {
         }
 
         Class<? extends TaskInternal> type = (Class) actualArgs.get(Task.TASK_TYPE);
-        Boolean generateSubclass = Boolean.valueOf(actualArgs.get(GENERATE_SUBCLASS).toString());
-        TaskInternal task = createTaskObject(project, type, name, generateSubclass);
+        TaskInternal task = create(name, type);
 
         Object dependsOnTasks = actualArgs.get(Task.TASK_DEPENDS_ON);
         if (dependsOnTasks != null) {
@@ -104,22 +100,23 @@ public class TaskFactory implements ITaskFactory {
         return task;
     }
 
-    private TaskInternal createTaskObject(ProjectInternal project, final Class<? extends TaskInternal> type, String name, boolean generateGetters) {
+    @Override
+    public <S extends TaskInternal> S create(String name, final Class<S> type) {
         if (!Task.class.isAssignableFrom(type)) {
             throw new InvalidUserDataException(String.format(
                     "Cannot create task of type '%s' as it does not implement the Task interface.",
                     type.getSimpleName()));
         }
 
-        final Class<? extends TaskInternal> generatedType;
-        if (generateGetters) {
-            generatedType = generator.generate(type);
+        final Class<? extends Task> generatedType;
+        if (type.isAssignableFrom(DefaultTask.class)) {
+            generatedType = generator.generate(DefaultTask.class);
         } else {
-            generatedType = type;
+            generatedType = generator.generate(type);
         }
 
-        return AbstractTask.injectIntoNewInstance(project, name, new Callable<TaskInternal>() {
-            public TaskInternal call() throws Exception {
+        return type.cast(AbstractTask.injectIntoNewInstance(project, name, new Callable<Task>() {
+            public Task call() throws Exception {
                 try {
                     return instantiator.newInstance(generatedType);
                 } catch (ObjectInstantiationException e) {
@@ -127,17 +124,13 @@ public class TaskFactory implements ITaskFactory {
                             e.getCause());
                 }
             }
-        });
+        }));
     }
 
     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)) {
-            args.put(Task.TASK_TYPE, DefaultTask.class);
-        }
-        setIfNull(args, GENERATE_SUBCLASS, "true");
     }
 
     private void validateArgs(Map<String, Object> args) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/UpdateAction.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/UpdateAction.java
index 2d5f918..7992079 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/UpdateAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/project/taskfactory/UpdateAction.java
@@ -15,10 +15,10 @@
  */
 package org.gradle.api.internal.project.taskfactory;
 
-import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 
 import java.util.concurrent.Callable;
 
 public interface UpdateAction {
-    void update(Task task, Callable<Object> futureValue);
+    void update(TaskInternal task, Callable<Object> futureValue);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/CachingResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/CachingResource.java
deleted file mode 100644
index 6098db0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/CachingResource.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.api.internal.resource;
-
-public class CachingResource extends DelegatingResource {
-    private String content;
-    private boolean fetched;
-
-    public CachingResource(Resource resource) {
-        super(resource);
-    }
-
-    @Override
-    public boolean getExists() {
-        maybeFetch();
-        return content != null;
-    }
-
-    @Override
-    public String getText() {
-        maybeFetch();
-        return content;
-    }
-
-    private void maybeFetch() {
-        if (!fetched) {
-            content = getResource().getText();
-            fetched = true;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/DelegatingResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/DelegatingResource.java
deleted file mode 100644
index 8a2be29..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/DelegatingResource.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-import java.io.File;
-import java.net.URI;
-
-public class DelegatingResource implements Resource {
-    private final Resource resource;
-
-    public DelegatingResource(Resource resource) {
-        this.resource = resource;
-    }
-
-    public Resource getResource() {
-        return resource;
-    }
-
-    public String getDisplayName() {
-        return resource.getDisplayName();
-    }
-
-    public File getFile() {
-        return resource.getFile();
-    }
-
-    public URI getURI() {
-        return resource.getURI();
-    }
-
-    public boolean getExists() {
-        return resource.getExists();
-    }
-
-    public String getText() {
-        return resource.getText();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/Resource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/Resource.java
deleted file mode 100644
index 5675034..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/Resource.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-import java.io.File;
-import java.io.Serializable;
-import java.net.URI;
-
-/**
- * A {@code Resource} represents some binary artifact.
- *
- * <p>Implementations are not required to be thread-safe.</p>
- */
-public interface Resource extends Serializable {
-    /**
-     * Returns a display name for this resource. This can be used in log and error messages.
-     *
-     * @return the display name
-     */
-    String getDisplayName();
-
-    /**
-     * Returns a file representing this resource. Not all resources are available as a file.
-     *
-     * @return A file representing this resource. Returns null if this resource is not available as a file.
-     */
-    File getFile();
-
-    /**
-     * Returns the URI for this resource. Not all resources have a URI.
-     *
-     * @return The URI for this resource. Returns null if this resource does not have a URI.
-     */
-    URI getURI();
-
-    /**
-     * Returns true if this resource exists, false if it does not exist.
-     *
-     * @return true if this resource exists.
-     */
-    boolean getExists();
-
-    /**
-     * Returns the content of this resource, as a String.
-     *
-     * @return the content. Never returns null.
-     * @throws ResourceNotFoundException When this resource does not exist.
-     */
-    String getText() throws ResourceNotFoundException;
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceException.java
deleted file mode 100644
index 9b21136..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-import org.gradle.api.GradleException;
-import org.gradle.internal.exceptions.Contextual;
-
- at Contextual
-public class ResourceException extends GradleException {
-    public ResourceException(String message) {
-        super(message);
-    }
-
-    public ResourceException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceNotFoundException.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceNotFoundException.java
deleted file mode 100644
index 923578a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/ResourceNotFoundException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-/**
- * An exception thrown when attempting to access the content of a {@link Resource} which does not exist.
- */
-public class ResourceNotFoundException extends ResourceException {
-    public ResourceNotFoundException(String message) {
-        super(message);
-    }
-
-    public ResourceNotFoundException(String message, Throwable t) {
-        super(message, t);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/StringResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/StringResource.java
deleted file mode 100644
index af98ef3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/StringResource.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-import java.io.File;
-import java.net.URI;
-
-public class StringResource implements Resource {
-    private final String displayName;
-    private final CharSequence contents;
-
-    public StringResource(String displayName, CharSequence contents) {
-        this.displayName = displayName;
-        this.contents = contents;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getText() {
-        return contents.toString();
-    }
-
-    public File getFile() {
-        return null;
-    }
-
-    public URI getURI() {
-        return null;
-    }
-
-    public boolean getExists() {
-        return true;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/UriResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/UriResource.java
deleted file mode 100644
index cd57b20..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/resource/UriResource.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.resource;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.internal.SystemProperties;
-import org.gradle.util.GradleVersion;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URLConnection;
-
-import static org.gradle.util.GFileUtils.canonicalise;
-
-public class UriResource implements Resource {
-    private final File sourceFile;
-    private final URI sourceUri;
-    private final String description;
-
-    public UriResource(String description, File sourceFile) {
-        this.description = description;
-        this.sourceFile = canonicalise(sourceFile);
-        this.sourceUri = sourceFile.toURI();
-    }
-
-    public UriResource(String description, URI sourceUri) {
-        this.description = description;
-        this.sourceFile = sourceUri.getScheme().equals("file") ? canonicalise(new File(sourceUri.getPath())) : null;
-        this.sourceUri = sourceUri;
-    }
-
-    public String getDisplayName() {
-        return String.format("%s '%s'", description, sourceFile != null ? sourceFile.getAbsolutePath() : sourceUri);
-    }
-
-    public String getText() {
-        if (sourceFile != null && sourceFile.isDirectory()) {
-            throw new ResourceException(String.format("Could not read %s as it is a directory.", getDisplayName()));
-        }
-        try {
-            InputStream inputStream = getInputStream(sourceUri);
-            try {
-                return IOUtils.toString(inputStream);
-            } finally {
-                inputStream.close();
-            }
-        } catch (FileNotFoundException e) {
-            throw new ResourceNotFoundException(String.format("Could not read %s as it does not exist.", getDisplayName()));
-        } catch (Exception e) {
-            throw new ResourceException(String.format("Could not read %s.", getDisplayName()), e);
-        }
-    }
-
-    public boolean getExists() {
-        try {
-            InputStream inputStream = getInputStream(sourceUri);
-            try {
-                return true;
-            } finally {
-                inputStream.close();
-            }
-        } catch (FileNotFoundException e) {
-            return false;
-        } catch (Exception e) {
-            throw new ResourceException(String.format("Could not determine if %s exists.", getDisplayName()), e);
-        }
-    }
-
-    private InputStream getInputStream(URI url) throws IOException {
-        final URLConnection urlConnection = url.toURL().openConnection();
-        urlConnection.setRequestProperty("User-Agent", getUserAgentString());
-        return urlConnection.getInputStream();
-    }
-
-    public File getFile() {
-        return sourceFile;
-    }
-
-    public URI getURI() {
-        return sourceUri;
-    }
-
-    public static String getUserAgentString() {
-        String osName = System.getProperty("os.name");
-        String osVersion = System.getProperty("os.version");
-        String osArch = System.getProperty("os.arch");
-        String javaVendor = System.getProperty("java.vendor");
-        String javaVersion = SystemProperties.getJavaVersion();
-        String javaVendorVersion = System.getProperty("java.vm.version");
-        return String.format("Gradle/%s (%s;%s;%s) (%s;%s;%s)",
-                GradleVersion.current().getVersion(),
-                osName,
-                osVersion,
-                osArch,
-                javaVendor,
-                javaVersion,
-                javaVendorVersion);
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResource.java
new file mode 100644
index 0000000..e2da848
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResource.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources;
+
+import com.google.common.io.CharSource;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.resources.TextResource;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.Set;
+
+public class CharSourceBackedTextResource implements TextResource {
+
+    private final CharSource charSource;
+
+    public CharSourceBackedTextResource(CharSource charSource) {
+        this.charSource = charSource;
+    }
+
+    @Override
+    public String asString() {
+        try {
+            return charSource.read();
+        } catch (IOException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    @Override
+    public Reader asReader() {
+        try {
+            return charSource.openStream();
+        } catch (IOException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    @Override
+    public File asFile(String charset) {
+        throw new UnsupportedOperationException("Cannot create file for char source " + charSource);
+    }
+
+    @Override
+    public File asFile() {
+        throw new UnsupportedOperationException("Cannot create file for char source " + charSource);
+    }
+
+    @Override
+    public Object getInputProperties() {
+        return null;
+    }
+
+    @Override
+    public FileCollection getInputFiles() {
+        return null;
+    }
+
+    @Override
+    public TaskDependency getBuildDependencies() {
+        return new TaskDependency() {
+            @Override
+            public Set<? extends Task> getDependencies(Task task) {
+                return Collections.emptySet();
+            }
+        };
+    }
+}
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 450cf92..e4855ac 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
@@ -16,26 +16,30 @@
 
 package org.gradle.api.internal.resources;
 
-import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.FileOperations;
 import org.gradle.api.internal.file.MaybeCompressedFileResource;
+import org.gradle.api.internal.file.TemporaryFileProvider;
 import org.gradle.api.internal.file.archive.compression.Bzip2Archiver;
 import org.gradle.api.internal.file.archive.compression.GzipArchiver;
 import org.gradle.api.resources.ReadableResource;
 import org.gradle.api.resources.ResourceHandler;
+import org.gradle.api.resources.TextResourceFactory;
 
 public class DefaultResourceHandler implements ResourceHandler {
-    private final FileResolver resolver;
+    private final FileOperations fileOperations;
+    private final TextResourceFactory textResourceFactory;
 
-    public DefaultResourceHandler(FileResolver resolver) {
-        this.resolver = resolver;
+    public DefaultResourceHandler(FileOperations fileOperations, TemporaryFileProvider tempFileProvider) {
+        this.fileOperations = fileOperations;
+        textResourceFactory = new DefaultTextResourceFactory(fileOperations, tempFileProvider);
     }
 
     public ReadableResource gzip(Object path) {
-        return new GzipArchiver(resolver.resolveResource(path));
+        return new GzipArchiver(fileOperations.getFileResolver().resolveResource(path));
     }
 
     public ReadableResource bzip2(Object path) {
-        return new Bzip2Archiver(resolver.resolveResource(path));
+        return new Bzip2Archiver(fileOperations.getFileResolver().resolveResource(path));
     }
 
     //this method is not on the interface, at least for now
@@ -43,7 +47,11 @@ public class DefaultResourceHandler implements ResourceHandler {
         if (tarPath instanceof ReadableResource) {
             return (ReadableResource) tarPath;
         } else {
-            return new MaybeCompressedFileResource(resolver.resolveResource(tarPath));
+            return new MaybeCompressedFileResource(fileOperations.getFileResolver().resolveResource(tarPath));
         }
     }
+
+    public TextResourceFactory getText() {
+        return textResourceFactory;
+    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultTextResourceFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultTextResourceFactory.java
new file mode 100644
index 0000000..8a5fce0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/DefaultTextResourceFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources;
+
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.resources.TextResource;
+import org.gradle.api.resources.TextResourceFactory;
+
+import java.nio.charset.Charset;
+
+public class DefaultTextResourceFactory implements TextResourceFactory {
+    private final FileOperations fileOperations;
+    private final TemporaryFileProvider tempFileProvider;
+
+    public DefaultTextResourceFactory(FileOperations fileOperations, TemporaryFileProvider tempFileProvider) {
+        this.fileOperations = fileOperations;
+        this.tempFileProvider = tempFileProvider;
+    }
+
+    public TextResource fromString(String string) {
+        return new StringBackedTextResource(tempFileProvider, string);
+    }
+
+    public TextResource fromFile(Object file, String charset) {
+        return new FileCollectionBackedTextResource(tempFileProvider, fileOperations.files(file), Charset.forName(charset));
+    }
+
+    public TextResource fromFile(Object file) {
+        return fromFile(file, Charset.defaultCharset().name());
+
+    }
+
+    public TextResource fromArchiveEntry(Object archive, String entryPath, String charset) {
+        return new FileCollectionBackedArchiveTextResource(fileOperations, tempFileProvider, fileOperations.files(archive), entryPath, Charset.forName(charset));
+    }
+
+    public TextResource fromArchiveEntry(Object archive, String entryPath) {
+        return fromArchiveEntry(archive, entryPath, Charset.defaultCharset().name());
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedArchiveTextResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedArchiveTextResource.java
new file mode 100644
index 0000000..850cc8e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedArchiveTextResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.resources;
+
+import com.google.common.io.Files;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.internal.file.collections.LazilyInitializedFileTree;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.util.PatternSet;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+public class FileCollectionBackedArchiveTextResource extends FileCollectionBackedTextResource {
+    public FileCollectionBackedArchiveTextResource(final FileOperations fileOperations,
+                                                   final TemporaryFileProvider tempFileProvider,
+                                                   final FileCollection fileCollection,
+                                                   final String path, Charset charset) {
+        super(tempFileProvider, new LazilyInitializedFileTree() {
+            @Override
+            public FileTree createDelegate() {
+                File archiveFile = fileCollection.getSingleFile();
+                String fileExtension = Files.getFileExtension(archiveFile.getName());
+                FileTree archiveContents = fileExtension.equals("jar") || fileExtension.equals("zip")
+                        ? fileOperations.zipTree(archiveFile) : fileOperations.tarTree(archiveFile);
+                PatternSet patternSet = new PatternSet();
+                patternSet.include(path);
+                return archiveContents.matching(patternSet);
+            }
+            public TaskDependency getBuildDependencies() {
+                return fileCollection.getBuildDependencies();
+            }
+        }, charset);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResource.java
new file mode 100644
index 0000000..8942dad
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResource.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.resources;
+
+import com.google.common.io.Files;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.resources.TextResource;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+public class FileCollectionBackedTextResource implements TextResource {
+    private final TemporaryFileProvider tempFileProvider;
+    private final FileCollection fileCollection;
+    private final Charset charset;
+
+    public FileCollectionBackedTextResource(TemporaryFileProvider tempFileProvider, FileCollection fileCollection, Charset charset) {
+        this.tempFileProvider = tempFileProvider;
+        this.fileCollection = fileCollection;
+        this.charset = charset;
+    }
+
+    public String asString() {
+        try {
+            return Files.toString(asFile(), charset);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public Reader asReader() {
+        try {
+            return Files.newReader(asFile(), charset);
+        } catch (FileNotFoundException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public File asFile(String targetCharset) {
+        Charset targetCharsetObj = Charset.forName(targetCharset);
+
+        if (targetCharsetObj.equals(charset)) {
+            return fileCollection.getSingleFile();
+        }
+
+        File targetFile = tempFileProvider.createTemporaryFile("fileCollection", ".txt", "resource");
+        try {
+            Files.asCharSource(fileCollection.getSingleFile(), charset).copyTo(Files.asCharSink(targetFile, targetCharsetObj));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return targetFile;
+    }
+
+    public File asFile() {
+        return asFile(Charset.defaultCharset().name());
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return fileCollection.getBuildDependencies();
+    }
+
+    public Object getInputProperties() {
+        return charset.name();
+    }
+
+    public FileCollection getInputFiles() {
+        return fileCollection;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/StringBackedTextResource.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/StringBackedTextResource.java
new file mode 100644
index 0000000..3e0f0ef
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/resources/StringBackedTextResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.resources;
+
+import com.google.common.io.Files;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.resources.TextResource;
+import org.gradle.api.tasks.TaskDependency;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+
+public class StringBackedTextResource implements TextResource {
+    private final TemporaryFileProvider tempFileProvider;
+    private final String string;
+
+    public StringBackedTextResource(TemporaryFileProvider tempFileProvider, String string) {
+        this.tempFileProvider = tempFileProvider;
+        this.string = string;
+    }
+
+    public String asString() {
+        return string;
+    }
+
+    public Reader asReader() {
+        return new StringReader(string);
+    }
+
+    public File asFile(String charset) {
+        File file = tempFileProvider.createTemporaryFile("string", ".txt", "resource");
+        try {
+            Files.write(string, file, Charset.forName(charset));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return file;
+    }
+
+    public File asFile() {
+        return asFile(Charset.defaultCharset().name());
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return new DefaultTaskDependency();
+    }
+
+    public Object getInputProperties() {
+        return string;
+    }
+
+    public FileCollection getInputFiles() {
+        return null;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskCollection.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskCollection.java
index 54bbc86..b68b2dc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskCollection.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskCollection.java
@@ -21,31 +21,26 @@ import org.gradle.api.Task;
 import org.gradle.api.UnknownDomainObjectException;
 import org.gradle.api.UnknownTaskException;
 import org.gradle.api.internal.DefaultNamedDomainObjectSet;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.api.internal.collections.CollectionEventRegister;
 import org.gradle.api.internal.collections.CollectionFilter;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.TaskCollection;
-
-import java.util.Set;
+import org.gradle.internal.reflect.Instantiator;
 
 public class DefaultTaskCollection<T extends Task> extends DefaultNamedDomainObjectSet<T> implements TaskCollection<T> {
+    private static final Task.Namer NAMER = new Task.Namer();
+
     protected final ProjectInternal project;
 
     public DefaultTaskCollection(Class<T> type, Instantiator instantiator, ProjectInternal project) {
-        super(type, instantiator, new Task.Namer());
-        this.project = project;
-    }
-
-    protected DefaultTaskCollection(Class<? extends T> type, Set<T> store, CollectionEventRegister<T> eventRegister, Instantiator instantiator, ProjectInternal project) {
-        super(type, store, eventRegister, instantiator, new Task.Namer());
+        super(type, instantiator, NAMER);
         this.project = project;
     }
 
     public DefaultTaskCollection(DefaultTaskCollection<? super T> collection, CollectionFilter<T> filter, Instantiator instantiator, ProjectInternal project) {
-        this(filter.getType(), collection.filteredStore(filter), collection.filteredEvents(filter), instantiator, project);
+        super(collection, filter, instantiator, NAMER);
+        this.project = project;
     }
 
     protected <S extends T> DefaultTaskCollection<S> filtered(CollectionFilter<S> filter) {
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 675bb29..57e7359 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
@@ -15,18 +15,24 @@
  */
 package org.gradle.api.internal.tasks;
 
+import com.google.common.collect.Sets;
 import groovy.lang.Closure;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.*;
 import org.gradle.api.internal.DynamicObject;
 import org.gradle.api.internal.NamedDomainObjectContainerConfigureDelegate;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.BiAction;
 import org.gradle.internal.Transformers;
 import org.gradle.internal.graph.CachingDirectedGraphWalker;
 import org.gradle.internal.graph.DirectedGraph;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.DeprecationLogger;
 import org.gradle.util.GUtil;
@@ -34,14 +40,20 @@ import org.gradle.util.GUtil;
 import java.util.*;
 
 public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements TaskContainerInternal {
+    private final MutableModelNode modelNode;
+    private final ModelReference<NamedEntityInstantiator<Task>> instantiatorReference;
     private final ITaskFactory taskFactory;
     private final ProjectAccessListener projectAccessListener;
-    private Map<String, Runnable> placeholders = new HashMap<String, Runnable>();
+    private final Set<String> placeholders = Sets.newHashSet();
+    private final NamedEntityInstantiator<Task> instantiator;
 
-    public DefaultTaskContainer(ProjectInternal project, Instantiator instantiator, ITaskFactory taskFactory, ProjectAccessListener projectAccessListener) {
+    public DefaultTaskContainer(MutableModelNode modelNode, ModelReference<NamedEntityInstantiator<Task>> instantiatorReference, ProjectInternal project, Instantiator instantiator, ITaskFactory taskFactory, ProjectAccessListener projectAccessListener) {
         super(Task.class, instantiator, project);
+        this.modelNode = modelNode;
+        this.instantiatorReference = instantiatorReference;
         this.taskFactory = taskFactory;
         this.projectAccessListener = projectAccessListener;
+        this.instantiator = new TaskInstantiator(taskFactory);
     }
 
     public Task create(Map<String, ?> options) {
@@ -53,6 +65,10 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         Task task = taskFactory.createTask(mutableOptions);
         String name = task.getName();
 
+        if (placeholders.remove(name)) {
+            modelNode.removeLink(name);
+        }
+
         Task existing = findByNameWithoutRules(name);
         if (existing != null) {
             if (replace) {
@@ -76,29 +92,14 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return create(name, type);
     }
 
-    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 {
-        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) {
-        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
-        return create(name, type);
-    }
-
     public Task create(String name) {
         return create(GUtil.map(Task.TASK_NAME, name));
     }
@@ -117,11 +118,6 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return create(name);
     }
 
-    public Task add(String name) {
-        DeprecationLogger.nagUserOfReplacedMethod("TaskContainer.add()", "create()");
-        return create(name);
-    }
-
     public Task replace(String name) {
         return create(GUtil.map(Task.TASK_NAME, name, Task.TASK_OVERWRITE, true));
     }
@@ -130,11 +126,6 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return create(name).configure(configureClosure);
     }
 
-    public Task add(String name, Closure 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);
@@ -163,17 +154,11 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return project.getTasks().findByName(StringUtils.substringAfterLast(path, Project.PATH_SEPARATOR));
     }
 
-    public Task resolveTask(Object path) {
+    public Task resolveTask(String path) {
         if (!GUtil.isTrue(path)) {
             throw new InvalidUserDataException("A path must be specified!");
         }
-        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"
-            );
-        }
-        return getByPath(path.toString());
+        return getByPath(path);
     }
 
     public Task getByPath(String path) throws UnknownTaskException {
@@ -189,38 +174,40 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         return this;
     }
 
+    @Override
+    public NamedEntityInstantiator<Task> getEntityInstantiator() {
+        return instantiator;
+    }
+
     public DynamicObject getTasksAsDynamicObject() {
         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;
+        return Sets.newTreeSet(modelNode.getLinkNames(ModelType.of(Task.class)));
     }
 
-    public void actualize() {
+    public void realize() {
+        project.getModelRegistry().realizeNode(modelNode.getPath());
+
         new CachingDirectedGraphWalker<Task, Void>(new DirectedGraph<Task, Void>() {
             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);
-        }
-
+    @Override
+    public void discoverTasks() {
+        project.fireDeferredConfiguration();
+        project.getModelRegistry().atStateOrLater(modelNode.getPath(), ModelNode.State.SelfClosed);
     }
 
-    public Map<String, Runnable> getPlaceholderActions() {
-        return placeholders;
+    @Override
+    public void maybeRealizeTask(String name) {
+        if (modelNode.hasLink(name)) {
+            realizeTask(MODEL_PATH.child(name));
+        }
     }
 
     public Task findByName(String name) {
@@ -229,19 +216,79 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
             return task;
         }
         maybeMaterializePlaceholder(name);
+        maybeRealizeTask(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();
+        if (placeholders.remove(name)) {
+            if (super.findByName(name) == null && modelNode.hasLink(name)) {
+                realizeTask(MODEL_PATH.child(name));
             }
         }
     }
 
-    public void addPlaceholderAction(String placeholderName, Runnable runnable) {
-        placeholders.put(placeholderName, runnable);
+    private Task realizeTask(ModelPath taskPath) {
+        return project.getModelRegistry().realize(taskPath, ModelType.of(Task.class));
+    }
+
+    public <T extends TaskInternal> void addPlaceholderAction(final String placeholderName, final Class<T> taskType, final Action<? super T> configure) {
+        if (!modelNode.hasLink(placeholderName)) {
+            final ModelType<T> taskModelType = ModelType.of(taskType);
+            ModelPath path = MODEL_PATH.child(placeholderName);
+            addPlaceholderModelLink(placeholderName, taskType, configure, taskModelType, path, instantiatorReference, modelNode);
+        }
+        if (findByNameWithoutRules(placeholderName) == null) {
+            placeholders.add(placeholderName);
+        }
+    }
+
+    private static <T extends TaskInternal> void addPlaceholderModelLink(final String placeholderName, final Class<T> taskType, final Action<? super T> configure, final ModelType<T> taskModelType, ModelPath path, final ModelReference<NamedEntityInstantiator<Task>> instantiatorReference, final MutableModelNode modelNode) {
+        modelNode.addLink(
+                ModelCreators
+                        .of(ModelReference.of(path), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                            @Override
+                            public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                                NamedEntityInstantiator<Task> instantiator = ModelViews.getInstance(inputs.get(0), instantiatorReference);
+                                final T task = instantiator.create(placeholderName, taskType);
+                                configure.execute(task);
+                                DeprecationLogger.whileDisabled(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        modelNode.getPrivateData(ModelType.of(TaskContainerInternal.class)).add(task);
+                                    }
+                                });
+                                mutableModelNode.setPrivateData(taskModelType, task);
+                            }
+                        })
+                        .inputs(instantiatorReference)
+                        .withProjection(new UnmanagedModelProjection<T>(taskModelType, true, true))
+                        .descriptor(new SimpleModelRuleDescriptor("tasks.addPlaceholderAction(" + placeholderName + ")"))
+                        .build()
+        );
+    }
+
+    public <U extends Task> NamedDomainObjectContainer<U> containerWithType(Class<U> type) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Set<? extends Class<? extends Task>> getCreateableTypes() {
+        return Collections.singleton(getType());
+    }
+
+    private static class TaskInstantiator implements NamedEntityInstantiator<Task> {
+        private final ITaskFactory taskFactory;
+
+        public TaskInstantiator(ITaskFactory taskFactory) {
+            this.taskFactory = taskFactory;
+        }
+
+        @Override
+        public <S extends Task> S create(String name, Class<S> type) {
+            if (type.isAssignableFrom(TaskInternal.class)) {
+                return type.cast(taskFactory.create(name, TaskInternal.class));
+            }
+            return type.cast(taskFactory.create(name, type.asSubclass(TaskInternal.class)));
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
index 95f7b62..22b1673 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
@@ -16,25 +16,71 @@
 package org.gradle.api.internal.tasks;
 
 import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.Transformer;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.tasks.TaskContainer;
 import org.gradle.initialization.ProjectAccessListener;
 import org.gradle.internal.Factory;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.model.collection.internal.BridgedCollections;
+import org.gradle.model.internal.core.ModelNode;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.MutableModelNode;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
 
 public class DefaultTaskContainerFactory implements Factory<TaskContainerInternal> {
+    private final ModelRegistry modelRegistry;
     private final Instantiator instantiator;
     private final ITaskFactory taskFactory;
     private Project project;
     public ProjectAccessListener projectAccessListener;
 
-    public DefaultTaskContainerFactory(Instantiator instantiator, ITaskFactory taskFactory, Project project, ProjectAccessListener projectAccessListener) {
+    public DefaultTaskContainerFactory(ModelRegistry modelRegistry, Instantiator instantiator, ITaskFactory taskFactory, Project project, ProjectAccessListener projectAccessListener) {
+        this.modelRegistry = modelRegistry;
         this.instantiator = instantiator;
         this.taskFactory = taskFactory;
         this.project = project;
         this.projectAccessListener = projectAccessListener;
     }
 
+    ModelType<Task> taskModelType = ModelType.of(Task.class);
+
     public TaskContainerInternal create() {
-        return instantiator.newInstance(DefaultTaskContainer.class, project, instantiator, taskFactory, projectAccessListener);
+        BridgedCollections.staticTypes(
+                modelRegistry,
+                TaskContainerInternal.MODEL_PATH,
+                TaskContainerInternal.MODEL_TYPE,
+                taskModelType,
+                ModelType.of(TaskContainer.class),
+                new Transformer<TaskContainerInternal, MutableModelNode>() {
+                    @Override
+                    public TaskContainerInternal transform(MutableModelNode mutableModelNode) {
+                        ModelReference<NamedEntityInstantiator<Task>> instantiatorReference = BridgedCollections.instantiatorReference(TaskContainerInternal.MODEL_PATH, TaskContainerInternal.TASK_MODEL_TYPE);
+                        return instantiator.newInstance(DefaultTaskContainer.class, mutableModelNode, instantiatorReference, project, instantiator, taskFactory, projectAccessListener);
+                    }
+                },
+                new Task.Namer(),
+                "Project.<init>.tasks()",
+                new Namer()
+        );
+
+        ModelNode modelNode = modelRegistry.atStateOrLater(TaskContainerInternal.MODEL_PATH, ModelNode.State.Created);
+        if (modelNode == null) {
+            throw new IllegalStateException("Couldn't get task container from model registry");
+        }
+
+        // TODO LD use something more stable than a cast here
+        MutableModelNode mutableModelNode = (MutableModelNode) modelNode;
+        return mutableModelNode.getPrivateData(TaskContainerInternal.MODEL_TYPE);
     }
+
+    private static class Namer implements Transformer<String, String> {
+        public String transform(String s) {
+            return "Project.<init>.tasks." + s + "()";
+        }
+    }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskDependency.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskDependency.java
index 239756e..4ac1dc3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskDependency.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskDependency.java
@@ -22,17 +22,13 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.typeconversion.UnsupportedNotationException;
 import org.gradle.util.GUtil;
 
 import java.util.*;
 import java.util.concurrent.Callable;
 
 public class DefaultTaskDependency extends AbstractTaskDependency {
-    private static final TaskResolver FAILING_RESOLVER = new TaskResolver() {
-        public Task resolveTask(Object path) {
-            throw new IllegalArgumentException(String.format("Cannot convert %s to a task.", path));
-        }
-    };
     private final Set<Object> values = new HashSet<Object>();
     private final TaskResolver resolver;
 
@@ -41,7 +37,7 @@ public class DefaultTaskDependency extends AbstractTaskDependency {
     }
 
     public DefaultTaskDependency(TaskResolver resolver) {
-        this.resolver = resolver == null ? FAILING_RESOLVER : resolver;
+        this.resolver = resolver;
     }
 
     public void resolve(TaskDependencyResolveContext context) {
@@ -80,8 +76,20 @@ public class DefaultTaskDependency extends AbstractTaskDependency {
                 if (callableResult != null) {
                     queue.add(0, callableResult);
                 }
+            } else if (resolver != null && dependency instanceof CharSequence) {
+                context.add(resolver.resolveTask(dependency.toString()));
             } else {
-                context.add(resolver.resolveTask(dependency));
+                List<String> formats = new ArrayList<String>();
+                if (resolver != null) {
+                    formats.add("A String or CharSequence task name or path");
+                }
+                formats.add("A Task instance");
+                formats.add("A Buildable instance");
+                formats.add("A TaskDependency instance");
+                formats.add("A Closure instance that returns any of the above types");
+                formats.add("A Callable instance that returns any of the above types");
+                formats.add("An Iterable, Collection, Map or array instance that contains any of the above types");
+                throw new UnsupportedNotationException(dependency, String.format("Cannot convert %s to a task.", dependency), null, formats);
             }
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
index 7bc7c8f..327d149 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
@@ -16,6 +16,7 @@
 package org.gradle.api.internal.tasks;
 
 import groovy.lang.Closure;
+import groovy.lang.GString;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.file.FileResolver;
@@ -32,14 +33,14 @@ public class DefaultTaskInputs implements TaskInputs {
     private final DefaultConfigurableFileCollection inputFiles;
     private final DefaultConfigurableFileCollection sourceFiles;
     private final FileResolver resolver;
-    private final TaskStatusNagger taskStatusNagger;
+    private final TaskMutator taskMutator;
     private final Map<String, Object> properties = new HashMap<String, Object>();
 
-    public DefaultTaskInputs(FileResolver resolver, TaskInternal task, TaskStatusNagger taskStatusNagger) {
+    public DefaultTaskInputs(FileResolver resolver, TaskInternal task, TaskMutator taskMutator) {
         this.resolver = resolver;
-        this.taskStatusNagger = taskStatusNagger;
-        inputFiles = new DefaultConfigurableFileCollection(String.format("%s input files", task), resolver, null);
-        sourceFiles = new DefaultConfigurableFileCollection(String.format("%s source files", task), resolver, null);
+        this.taskMutator = taskMutator;
+        inputFiles = new DefaultConfigurableFileCollection(task + " input files", resolver, null);
+        sourceFiles = new DefaultConfigurableFileCollection(task + " source files", resolver, null);
     }
 
     public boolean getHasInputs() {
@@ -50,21 +51,30 @@ public class DefaultTaskInputs implements TaskInputs {
         return new UnionFileCollection(inputFiles, sourceFiles);
     }
 
-    public TaskInputs files(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.files(Object...)");
-        inputFiles.from(paths);
+    public TaskInputs files(final Object... paths) {
+        taskMutator.mutate("TaskInputs.files(Object...)", new Runnable() {
+            public void run() {
+                inputFiles.from(paths);
+            }
+        });
         return this;
     }
 
-    public TaskInputs file(Object path) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.file(Object)");
-        files(path);
+    public TaskInputs file(final Object path) {
+        taskMutator.mutate("TaskInputs.file(Object)", new Runnable() {
+            public void run() {
+                inputFiles.from(path);
+            }
+        });
         return this;
     }
 
-    public TaskInputs dir(Object dirPath) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.dir(Object)");
-        inputFiles.from(resolver.resolveFilesAsTree(dirPath));
+    public TaskInputs dir(final Object dirPath) {
+        taskMutator.mutate("TaskInputs.dir(Object)", new Runnable() {
+            public void run() {
+                inputFiles.from(resolver.resolveFilesAsTree(dirPath));
+            }
+        });
         return this;
     }
 
@@ -76,34 +86,43 @@ public class DefaultTaskInputs implements TaskInputs {
         return sourceFiles;
     }
 
-    public TaskInputs source(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.source(Object...)");
-        sourceFiles.from(paths);
+    public TaskInputs source(final Object... paths) {
+        taskMutator.mutate("TaskInputs.source(Object...)", new Runnable() {
+            public void run() {
+                sourceFiles.from(paths);
+            }
+        });
         return this;
     }
 
-    public TaskInputs source(Object path) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.source(Object)");
-        sourceFiles.from(path);
+    public TaskInputs source(final Object path) {
+        taskMutator.mutate("TaskInputs.source(Object)", new Runnable() {
+            public void run() {
+                sourceFiles.from(path);
+            }
+        });
         return this;
     }
 
-    public TaskInputs sourceDir(Object path) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.sourceDir(Object)");
-        sourceFiles.from(resolver.resolveFilesAsTree(path));
+    public TaskInputs sourceDir(final Object path) {
+        taskMutator.mutate("TaskInputs.sourceDir(Object)", new Runnable() {
+            public void run() {
+                sourceFiles.from(resolver.resolveFilesAsTree(path));
+            }
+        });
         return this;
     }
 
     public Map<String, Object> getProperties() {
         Map<String, Object> actualProperties = new HashMap<String, Object>();
         for (Map.Entry<String, Object> entry : properties.entrySet()) {
-            Object value = unwrap(entry.getValue());
+            Object value = prepareValue(entry.getValue());
             actualProperties.put(entry.getKey(), value);
         }
         return actualProperties;
     }
 
-    private Object unwrap(Object value) {
+    private Object prepareValue(Object value) {
         while (true) {
             if (value instanceof Callable) {
                 Callable callable = (Callable) value;
@@ -119,20 +138,30 @@ public class DefaultTaskInputs implements TaskInputs {
                 FileCollection fileCollection = (FileCollection) value;
                 return fileCollection.getFiles();
             } else {
-                return value;
+                return avoidGString(value);
             }
         }
     }
 
-    public TaskInputs property(String name, Object value) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.property(String, Object)");
-        properties.put(name, value);
+    private static Object avoidGString(Object value) {
+        return (value instanceof GString)? value.toString() : value;
+    }
+
+    public TaskInputs property(final String name, final Object value) {
+        taskMutator.mutate("TaskInputs.property(String, Object)", new Runnable() {
+            public void run() {
+                properties.put(name, value);
+            }
+        });
         return this;
     }
 
-    public TaskInputs properties(Map<String, ?> properties) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.properties(Map)");
-        this.properties.putAll(properties);
+    public TaskInputs properties(final Map<String, ?> newProps) {
+        taskMutator.mutate("TaskInputs.properties(Map)", new Runnable() {
+            public void run() {
+                properties.putAll(newProps);
+            }
+        });
         return this;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
index 8d195af..1467be5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
@@ -32,10 +32,10 @@ public class DefaultTaskOutputs implements TaskOutputsInternal {
     private final DefaultConfigurableFileCollection outputFiles;
     private AndSpec<TaskInternal> upToDateSpec = new AndSpec<TaskInternal>();
     private TaskExecutionHistory history;
-    private final TaskStatusNagger taskStatusNagger;
+    private final TaskMutator taskMutator;
 
-    public DefaultTaskOutputs(FileResolver resolver, TaskInternal task, TaskStatusNagger taskStatusNagger) {
-        this.taskStatusNagger = taskStatusNagger;
+    public DefaultTaskOutputs(FileResolver resolver, TaskInternal task, TaskMutator taskMutator) {
+        this.taskMutator = taskMutator;
         outputFiles = new DefaultConfigurableFileCollection(String.format("%s output files", task), resolver, null);
         outputFiles.builtBy(task);
     }
@@ -44,14 +44,20 @@ public class DefaultTaskOutputs implements TaskOutputsInternal {
         return upToDateSpec;
     }
 
-    public void upToDateWhen(Closure upToDateClosure) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.upToDateWhen(Closure)");
-        upToDateSpec = upToDateSpec.and(upToDateClosure);
+    public void upToDateWhen(final Closure upToDateClosure) {
+        taskMutator.mutate("TaskOutputs.upToDateWhen(Closure)", new Runnable() {
+            public void run() {
+                upToDateSpec = upToDateSpec.and(upToDateClosure);
+            }
+        });
     }
 
-    public void upToDateWhen(Spec<? super Task> upToDateSpec) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.upToDateWhen(Spec)");
-        this.upToDateSpec = this.upToDateSpec.and(upToDateSpec);
+    public void upToDateWhen(final Spec<? super Task> spec) {
+        taskMutator.mutate("TaskOutputs.upToDateWhen(Spec)", new Runnable() {
+            public void run() {
+                upToDateSpec = upToDateSpec.and(spec);
+            }
+        });
     }
 
     public boolean getHasOutput() {
@@ -62,21 +68,30 @@ public class DefaultTaskOutputs implements TaskOutputsInternal {
         return outputFiles;
     }
 
-    public TaskOutputs files(Object... paths) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.files(Object...)");
-        outputFiles.from(paths);
+    public TaskOutputs files(final Object... paths) {
+        taskMutator.mutate("TaskOutputs.files(Object...)", new Runnable() {
+            public void run() {
+                outputFiles.from(paths);
+            }
+        });
         return this;
     }
 
-    public TaskOutputs file(Object path) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.file(Object)");
-        files(path);
+    public TaskOutputs file(final Object path) {
+        taskMutator.mutate("TaskOutputs.file(Object)", new Runnable() {
+            public void run() {
+                outputFiles.from(path);
+            }
+        });
         return this;
     }
 
     public TaskOutputs dir(final Object path) {
-        taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.dir(Object)");
-        outputFiles.from(path);
+        taskMutator.mutate("TaskOutputs.dir(Object)", new Runnable() {
+            public void run() {
+                outputFiles.from(path);
+            }
+        });
         return this;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/PublicTaskSpecification.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/PublicTaskSpecification.java
new file mode 100644
index 0000000..f384d36
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/PublicTaskSpecification.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.base.Strings;
+import org.gradle.api.Task;
+import org.gradle.api.specs.Spec;
+
+/**
+ * Decides whether a {@link org.gradle.api.Task} is a public task or not.
+ */
+public final class PublicTaskSpecification implements Spec<Task> {
+
+    public static final Spec<Task> INSTANCE = new PublicTaskSpecification();
+
+    private PublicTaskSpecification() {
+    }
+
+    @Override
+    public boolean isSatisfiedBy(Task task) {
+        return !Strings.isNullOrEmpty(task.getGroup());
+    }
+
+}
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 b75a114..f737a3c 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
@@ -15,27 +15,25 @@
  */
 package org.gradle.api.internal.tasks;
 
+import org.gradle.api.Action;
+import org.gradle.api.Task;
 import org.gradle.api.internal.DynamicObject;
+import org.gradle.api.internal.PolymorphicDomainObjectContainerInternal;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.tasks.TaskContainer;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
 
-import java.util.Map;
-
-public interface TaskContainerInternal extends TaskContainer, TaskResolver {
+public interface TaskContainerInternal extends TaskContainer, TaskResolver, PolymorphicDomainObjectContainerInternal<Task> {
 
     // The path to the project's task container in the model registry
-    public String MODEL_PATH = "tasks";
+    ModelPath MODEL_PATH = ModelPath.path("tasks");
+    ModelType<TaskContainerInternal> MODEL_TYPE = ModelType.of(TaskContainerInternal.class);
+    ModelType<Task> TASK_MODEL_TYPE = ModelType.of(Task.class);
 
     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);
+    <T extends TaskInternal> void addPlaceholderAction(String placeholderName, Class<T> type, Action<? super T> configure);
 
     /**
      * Force the entire graph to come into existence.
@@ -46,7 +44,14 @@ public interface TaskContainerInternal extends TaskContainer, TaskResolver {
      *
      * As part of this, all placeholder actions are materialized to show up in 'tasks' and 'tasks --all' overview.
      */
-    void actualize();
+    void realize();
+
+    /**
+     * Performs work to discover more tasks.
+     *
+     * This method differs from {@link #realize} in that it does not discover new tasks by traversing task dependencies.
+     */
+    void discoverTasks();
 
-    Map<String, Runnable> getPlaceholderActions();
-}
+    void maybeRealizeTask(String name);
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskMutator.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskMutator.java
new file mode 100644
index 0000000..3f8ec99
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskMutator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.tasks;
+
+import groovy.util.ObservableList;
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
+
+import java.beans.PropertyChangeEvent;
+
+public class TaskMutator {
+    private final TaskInternal task;
+    private boolean executingleftShiftAction;
+
+    public TaskMutator(TaskInternal task) {
+        this.task = task;
+    }
+
+    public void mutate(String method, Runnable action) {
+        if (!task.getState().isConfigurable()) {
+            throw new IllegalStateException(format(method));
+        }
+        action.run();
+    }
+
+    public void assertMutable(String listname, PropertyChangeEvent evt) {
+        String method = null;
+        if (evt instanceof ObservableList.ElementEvent) {
+            switch (((ObservableList.ElementEvent) evt).getChangeType()) {
+                case ADDED:
+                    method = String.format("%s.%s", listname, "add()");
+                    break;
+                case UPDATED:
+                    method = String.format("%s.%s", listname, "set(int, Object)");
+                    break;
+                case REMOVED:
+                    method = String.format("%s.%s", listname, "remove()");
+                    break;
+                case CLEARED:
+                    method = String.format("%s.%s", listname, "clear()");
+                    break;
+                case MULTI_ADD:
+                    method = String.format("%s.%s", listname, "addAll()");
+                    break;
+                case MULTI_REMOVE:
+                    method = String.format("%s.%s", listname, "removeAll()");
+                    break;
+            }
+        }
+        if (method == null) {
+            return;
+        }
+        if (!task.getState().isConfigurable()) {
+            throw new IllegalStateException(format(method));
+        }
+    }
+
+    public ContextAwareTaskAction leftShift(final ContextAwareTaskAction action) {
+        return new ContextAwareTaskAction() {
+            public void execute(Task task) {
+                executingleftShiftAction = true;
+                try {
+                    action.execute(task);
+                } finally {
+                    executingleftShiftAction = false;
+                }
+            }
+
+            public void contextualise(TaskExecutionContext context) {
+                action.contextualise(context);
+            }
+        };
+    }
+
+    private String format(String method) {
+        if (executingleftShiftAction) {
+            return String.format("Cannot call %s on %s after task has started execution. Check the configuration of %s as you may have misused '<<' at task declaration.", method, task, task);
+        }
+        return String.format("Cannot call %s on %s after task has started execution.", method, task);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskResolver.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskResolver.java
index 45fe037..fcd63a8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskResolver.java
@@ -18,5 +18,5 @@ package org.gradle.api.internal.tasks;
 import org.gradle.api.Task;
 
 public interface TaskResolver {
-    Task resolveTask(Object path);
+    Task resolveTask(String path);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
index c8ca938..3f4d2a0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
@@ -55,6 +55,7 @@ public class TaskStateInternal implements TaskState {
     public boolean isConfigurable(){
         return !executed && !executing;
     }
+
     /**
      * Marks this task as executed with the given failure. This method can be called at most once.
      */
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
deleted file mode 100644
index 74533c0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/TaskStatusNagger.java
+++ /dev/null
@@ -1,105 +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.tasks;
-
-import groovy.util.ObservableList;
-import org.gradle.api.Task;
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.util.DeprecationLogger;
-
-import java.beans.PropertyChangeEvent;
-
-public class TaskStatusNagger {
-    private static final String DEPRECATION_MESSAGE = "Calling %s after task execution has started";
-    private static final String EXPLANAITION = "Check the configuration of %s";
-    private static final String EXPLANAITION_WITH_HINT = EXPLANAITION + ". You may have misused '<<' at task declaration";
-
-    private final TaskInternal taskInternal;
-    private boolean nagUser = true;
-    private boolean executingleftShiftAction;
-
-    public TaskStatusNagger(TaskInternal taskInternal) {
-        this.taskInternal = taskInternal;
-    }
-
-    public void nagIfTaskNotInConfigurableState(String method) {
-        if (!taskInternal.getStateInternal().isConfigurable() && nagUser) {
-            warn(method);
-        }
-    }
-
-    public void nagAboutMutatingListIfTaskNotInConfigurableState(String listname, PropertyChangeEvent evt) {
-        if (!taskInternal.getStateInternal().isConfigurable() && nagUser) {
-            if (evt instanceof ObservableList.ElementEvent) {
-                switch (((ObservableList.ElementEvent) evt).getChangeType()) {
-                    case ADDED:
-                        warn(String.format("%s.%s", listname, "add()"));
-                        break;
-                    case UPDATED:
-                        warn(String.format("%s.%s", listname, "set(int, Object)"));
-                        break;
-                    case REMOVED:
-                        warn(String.format("%s.%s", listname, "remove()"));
-                        break;
-                    case CLEARED:
-                        warn(String.format("%s.%s", listname, "clear()"));
-                        break;
-                    case MULTI_ADD:
-                        warn(String.format("%s.%s", listname, "addAll()"));
-                        break;
-                    case MULTI_REMOVE:
-                        warn(String.format("%s.%s", listname, "removeAll()"));
-                        break;
-                }
-            }
-        }
-    }
-
-    public ContextAwareTaskAction leftShift(final ContextAwareTaskAction action) {
-        return new ContextAwareTaskAction() {
-            public void execute(Task task) {
-                executingleftShiftAction = true;
-                try {
-                    action.execute(task);
-                } finally {
-                    executingleftShiftAction = false;
-                }
-            }
-
-            public void contextualise(TaskExecutionContext context) {
-                action.contextualise(context);
-            }
-        };
-    }
-
-    private void warn(String method) {
-        if (executingleftShiftAction) {
-            DeprecationLogger.nagUserOfDeprecated(String.format(DEPRECATION_MESSAGE, method), String.format(EXPLANAITION_WITH_HINT, taskInternal));
-        } else {
-            DeprecationLogger.nagUserOfDeprecated(String.format(DEPRECATION_MESSAGE, method), String.format(EXPLANAITION, taskInternal));
-        }
-    }
-
-    public void whileDisabled(Runnable runnable) {
-        nagUser = false;
-        try {
-            runnable.run();
-        } finally {
-            nagUser = true;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactory.java
index 535bd40..a980e60 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactory.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.tasks.options;
 
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 import org.gradle.internal.typeconversion.*;
 
 import java.util.ArrayList;
@@ -31,10 +32,9 @@ public class OptionNotationParserFactory {
             parsers.add(new UnsupportedNotationParser());
         }
         if (targetType.isAssignableFrom(String.class)) {
-            parsers.add(new NoDescriptionValuesJustReturningParser<Object>(targetType));
+            parsers.add(new NoDescriptionValuesJustReturningParser());
         }
         if (targetType.isEnum()) {
-            parsers.add(new NoDescriptionValuesJustReturningParser<Object>(targetType));
             parsers.add(new EnumFromCharSequenceNotationParser<Enum>(targetType.asSubclass(Enum.class)));
         }
         if (parsers.isEmpty()) {
@@ -43,30 +43,35 @@ public class OptionNotationParserFactory {
         return new ValueAwareCompositeNotationParser<Object>(parsers);
     }
 
-    private class UnsupportedNotationParser implements ValueAwareNotationParser<Object> {
+    private static class UnsupportedNotationParser implements ValueAwareNotationParser<Object> {
 
         public Object parseNotation(CharSequence notation) throws UnsupportedNotationException, TypeConversionException {
             throw new UnsupportedOperationException();
         }
 
-        public void describe(Collection<String> candidateFormats) {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
         }
 
         public void describeValues(Collection<String> collector) {
         }
     }
 
-    private class NoDescriptionValuesJustReturningParser<T> extends JustReturningParser<CharSequence, T> implements ValueAwareNotationParser<T> {
-        public NoDescriptionValuesJustReturningParser(Class<? extends T> targetType) {
-            super(targetType);
+    private static class NoDescriptionValuesJustReturningParser implements ValueAwareNotationParser<String> {
+        public String parseNotation(CharSequence notation) {
+            return notation.toString();
         }
 
-        public void describeValues(Collection<String> collector) {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Instances of String or CharSequence.");
+        }
 
+        public void describeValues(Collection<String> collector) {
         }
     }
 
-    private class ValueAwareCompositeNotationParser<T> extends CompositeNotationParser<CharSequence, T> implements ValueAwareNotationParser<T> {
+    private static class ValueAwareCompositeNotationParser<T> extends CompositeNotationParser<CharSequence, T> implements ValueAwareNotationParser<T> {
         private final Collection<ValueAwareNotationParser<? extends T>> delegates;
 
         public ValueAwareCompositeNotationParser(Collection<ValueAwareNotationParser<? extends T>> delegates) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleMarkupWriter.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleMarkupWriter.java
deleted file mode 100644
index 44c2a86..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleMarkupWriter.java
+++ /dev/null
@@ -1,421 +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.xml;
-
-import org.gradle.internal.SystemProperties;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.LinkedList;
-
-/**
- * <p>A streaming markup writer. Encodes characters and CDATA. Provides only basic state validation, and some simple indentation.</p>
- *
- * <p>This class also is-a {@link Writer}, and any characters written to this writer will be encoded as appropriate. Note, however, that
- * calling {@link #close()} on this object does not close the backing stream.
- * </p>
- */
-public class SimpleMarkupWriter extends Writer {
-    private enum Context {
-        Outside, Text, CData, StartTag, ElementContent
-    }
-
-    private final Writer output;
-    private final LinkedList<String> elements = new LinkedList<String>();
-    private Context context = Context.Outside;
-    private int squareBrackets;
-    private final String indent;
-
-    protected SimpleMarkupWriter(Writer writer, String indent) throws IOException {
-        this.indent = indent;
-        this.output = writer;
-    }
-
-    @Override
-    public void write(char[] chars, int offset, int length) throws IOException {
-        characters(chars, offset, length);
-    }
-
-    @Override
-    public void flush() throws IOException {
-        output.flush();
-    }
-
-    @Override
-    public void close() throws IOException {
-        // Does nothing
-    }
-
-    public SimpleMarkupWriter characters(char[] characters) throws IOException {
-        characters(characters, 0, characters.length);
-        return this;
-    }
-
-    public SimpleMarkupWriter characters(char[] characters, int start, int count) throws IOException {
-        if (context == Context.CData) {
-            writeCDATA(characters, start, count);
-        } else {
-            maybeStartText();
-            writeXmlEncoded(characters, start, count);
-        }
-        return this;
-    }
-
-    public SimpleMarkupWriter characters(CharSequence characters) throws IOException {
-        if (context == Context.CData) {
-            writeCDATA(characters);
-        } else {
-            maybeStartText();
-            writeXmlEncoded(characters);
-        }
-        return this;
-    }
-
-    private void maybeStartText() throws IOException {
-        if (context == Context.Outside) {
-            throw new IllegalStateException("Cannot write text, as there are no started elements.");
-        }
-        if (context == Context.StartTag) {
-            writeRaw(">");
-        }
-        context = Context.Text;
-    }
-
-    private void maybeFinishStartTag() throws IOException {
-        if (context == Context.StartTag) {
-            writeRaw(">");
-            context = Context.ElementContent;
-        }
-    }
-
-    public SimpleMarkupWriter startElement(String name) throws IOException {
-        if (!isValidXmlName(name)) {
-            throw new IllegalArgumentException(String.format("Invalid element name: '%s'", name));
-        }
-        if (context == Context.CData) {
-            throw new IllegalStateException("Cannot start element, as current CDATA node has not been closed.");
-        }
-        maybeFinishStartTag();
-        if (indent != null) {
-            writeRaw(SystemProperties.getLineSeparator());
-            for (int i = 0; i < elements.size(); i++) {
-                writeRaw(indent);
-            }
-        }
-        context = Context.StartTag;
-        elements.add(name);
-        writeRaw("<");
-        writeRaw(name);
-        return this;
-    }
-
-    public SimpleMarkupWriter endElement() throws IOException {
-        if (context == Context.Outside) {
-            throw new IllegalStateException("Cannot end element, as there are no started elements.");
-        }
-        if (context == Context.CData) {
-            throw new IllegalStateException("Cannot end element, as current CDATA node has not been closed.");
-        }
-        if (context == Context.StartTag) {
-            writeRaw("/>");
-            elements.removeLast();
-        } else {
-            if (context != Context.Text && indent != null) {
-                writeRaw(SystemProperties.getLineSeparator());
-                for (int i = 1; i < elements.size(); i++) {
-                    writeRaw(indent);
-                }
-            }
-            writeRaw("</");
-            writeRaw(elements.removeLast());
-            writeRaw(">");
-        }
-        if (elements.isEmpty()) {
-            if (indent != null) {
-                writeRaw(SystemProperties.getLineSeparator());
-            }
-            output.flush();
-            context = Context.Outside;
-        } else {
-            context = Context.ElementContent;
-        }
-        return this;
-    }
-
-    private void writeCDATA(char[] cdata, int offset, int count) throws IOException {
-        int end = offset + count;
-        for (int i = offset; i < end; i++) {
-            writeCDATA(cdata[i]);
-        }
-    }
-
-    private void writeCDATA(CharSequence cdata) throws IOException {
-        int len = cdata.length();
-        for (int i = 0; i < len; i++) {
-            writeCDATA(cdata.charAt(i));
-        }
-    }
-
-    private void writeCDATA(char ch) throws IOException {
-        if (needsCDATAEscaping(ch)) {
-            writeRaw("]]><![CDATA[>");
-        } else if (!isLegalCharacter(ch)) {
-            writeRaw('?');
-        } else if (isRestrictedCharacter(ch)) {
-            writeRaw("]]>");
-            writeCharacterReference(ch);
-            writeRaw("<![CDATA[");
-        } else {
-            writeRaw(ch);
-        }
-    }
-
-    private void writeCharacterReference(char ch) throws IOException {
-        writeRaw("&#x");
-        writeRaw(Integer.toHexString(ch));
-        writeRaw(";");
-    }
-
-    private boolean needsCDATAEscaping(char ch) {
-        switch (ch) {
-            case ']':
-                squareBrackets++;
-                return false;
-            case '>':
-                if (squareBrackets >= 2) {
-                    squareBrackets = 0;
-                    return true;
-                }
-                return false;
-            default:
-                squareBrackets = 0;
-                return false;
-        }
-    }
-
-    public SimpleMarkupWriter startCDATA() throws IOException {
-        if (context == Context.CData) {
-            throw new IllegalStateException("Cannot start CDATA node, as current CDATA node has not been closed.");
-        }
-        maybeFinishStartTag();
-        writeRaw("<![CDATA[");
-        context = Context.CData;
-        squareBrackets = 0;
-        return this;
-    }
-
-    public SimpleMarkupWriter endCDATA() throws IOException {
-        if (context != Context.CData) {
-            throw new IllegalStateException("Cannot end CDATA node, as not currently in a CDATA node.");
-        }
-        writeRaw("]]>");
-        context = Context.Text;
-        return this;
-    }
-
-    public SimpleMarkupWriter attribute(String name, String value) throws IOException {
-        if (!isValidXmlName(name)) {
-            throw new IllegalArgumentException(String.format("Invalid attribute name: '%s'", name));
-        }
-        if (context != Context.StartTag) {
-            throw new IllegalStateException("Cannot write attribute [" + name + ":" + value + "]. You should write start element first.");
-        }
-
-        writeRaw(" ");
-        writeRaw(name);
-        writeRaw("=\"");
-        writeXmlAttributeEncoded(value);
-        writeRaw("\"");
-        return this;
-    }
-
-    private static boolean isValidXmlName(String name) {
-        int length = name.length();
-        if (length == 0) {
-            return false;
-        }
-        char ch = name.charAt(0);
-        if (!isValidNameStartChar(ch)) {
-            return false;
-        }
-        for (int i = 1; i < length; i++) {
-            ch = name.charAt(i);
-            if (!isValidNameChar(ch)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static boolean isValidNameChar(char ch) {
-        if (isValidNameStartChar(ch)) {
-            return true;
-        }
-        if (ch >= '0' && ch <= '9') {
-            return true;
-        }
-        if (ch == '-' || ch == '.' || ch == '\u00b7') {
-            return true;
-        }
-        if (ch >= '\u0300' && ch <= '\u036f') {
-            return true;
-        }
-        if (ch >= '\u203f' && ch <= '\u2040') {
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean isValidNameStartChar(char ch) {
-        if (ch >= 'A' && ch <= 'Z') {
-            return true;
-        }
-        if (ch >= 'a' && ch <= 'z') {
-            return true;
-        }
-        if (ch == ':' || ch == '_') {
-            return true;
-        }
-        if (ch >= '\u00c0' && ch <= '\u00d6') {
-            return true;
-        }
-        if (ch >= '\u00d8' && ch <= '\u00f6') {
-            return true;
-        }
-        if (ch >= '\u00f8' && ch <= '\u02ff') {
-            return true;
-        }
-        if (ch >= '\u0370' && ch <= '\u037d') {
-            return true;
-        }
-        if (ch >= '\u037f' && ch <= '\u1fff') {
-            return true;
-        }
-        if (ch >= '\u200c' && ch <= '\u200d') {
-            return true;
-        }
-        if (ch >= '\u2070' && ch <= '\u218f') {
-            return true;
-        }
-        if (ch >= '\u2c00' && ch <= '\u2fef') {
-            return true;
-        }
-        if (ch >= '\u3001' && ch <= '\ud7ff') {
-            return true;
-        }
-        if (ch >= '\uf900' && ch <= '\ufdcf') {
-            return true;
-        }
-        if (ch >= '\ufdf0' && ch <= '\ufffd') {
-            return true;
-        }
-        return false;
-    }
-
-    private void writeRaw(char c) throws IOException {
-        output.write(c);
-    }
-
-    private boolean isLegalCharacter(final char c) {
-        if (c == 0x9 || c == 0xA || c == 0xD) {
-            return true;
-        } else if (c < 0x20) {
-            return false;
-        } else if (c <= 0xD7FF) {
-            return true;
-        } else if (c < 0xE000) {
-            return false;
-        } else if (c <= 0xFFFD) {
-            return true;
-        } else if (c < 0x10000) {
-            return false;
-        } else if (c <= 0x10FFFF) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isRestrictedCharacter(char c) {
-        if (c == 0x9 || c == 0xA || c == 0xD || c == 0x85) {
-            return false;
-        } else if (c <= 0x1F) {
-            return true;
-        } else if (c < 0x7F) {
-            return false;
-        } else if (c <= 0x9F) {
-            return true;
-        }
-        return false;
-    }
-
-    protected void writeRaw(String message) throws IOException {
-        output.write(message);
-    }
-
-    private void writeXmlEncoded(char[] message, int offset, int count) throws IOException {
-        int end = offset + count;
-        for (int i = offset; i < end; i++) {
-            writeXmlEncoded(message[i]);
-        }
-    }
-
-    private void writeXmlAttributeEncoded(CharSequence message) throws IOException {
-        assert message != null;
-        int len = message.length();
-        for (int i = 0; i < len; i++) {
-            writeXmlAttributeEncoded(message.charAt(i));
-        }
-    }
-
-    private void writeXmlAttributeEncoded(char ch) throws IOException {
-        if (ch == 9) {
-            writeRaw("	");
-        } else if (ch == 10) {
-            writeRaw("
");
-        } else if (ch == 13) {
-            writeRaw("
");
-        } else {
-            writeXmlEncoded(ch);
-        }
-    }
-
-    private void writeXmlEncoded(CharSequence message) throws IOException {
-        assert message != null;
-        int len = message.length();
-        for (int i = 0; i < len; i++) {
-            writeXmlEncoded(message.charAt(i));
-        }
-    }
-
-    private void writeXmlEncoded(char ch) throws IOException {
-        if (ch == '<') {
-            writeRaw("<");
-        } else if (ch == '>') {
-            writeRaw(">");
-        } else if (ch == '&') {
-            writeRaw("&");
-        } else if (ch == '"') {
-            writeRaw(""");
-        } else if (!isLegalCharacter(ch)) {
-            writeRaw('?');
-        } else if (isRestrictedCharacter(ch)) {
-            writeCharacterReference(ch);
-        } else {
-            writeRaw(ch);
-        }
-    }
-}
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
deleted file mode 100644
index d6b6f0f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/SimpleXmlWriter.java
+++ /dev/null
@@ -1,45 +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.xml;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-
-/**
- * <p>A streaming XML writer.</p>
- */
-public class SimpleXmlWriter extends SimpleMarkupWriter {
-
-    public SimpleXmlWriter(OutputStream output) throws IOException {
-        this(output, null);
-    }
-
-    public SimpleXmlWriter(OutputStream output, String indent) throws IOException {
-        this(new OutputStreamWriter(output, "UTF-8"), indent, "UTF-8");
-    }
-
-    public SimpleXmlWriter(Writer writer, String indent, String encoding) throws IOException {
-        super(writer, indent);
-        writeXmlDeclaration(encoding);
-    }
-
-    private void writeXmlDeclaration(String encoding) throws IOException {
-        writeRaw(String.format("<?xml version=\"1.0\" encoding=\"%s\"?>", encoding));
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/XmlTransformer.java b/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/XmlTransformer.java
deleted file mode 100644
index 63c7b4d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/internal/xml/XmlTransformer.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.xml;
-
-import groovy.lang.Closure;
-import groovy.util.IndentPrinter;
-import groovy.util.Node;
-import groovy.util.XmlNodePrinter;
-import groovy.util.XmlParser;
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Action;
-import org.gradle.api.Transformer;
-import org.gradle.api.XmlProvider;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.api.internal.DomNode;
-import org.gradle.internal.IoActions;
-import org.gradle.internal.SystemProperties;
-import org.gradle.internal.UncheckedException;
-import org.gradle.util.GUtil;
-import org.gradle.util.TextUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.InputSource;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
-
-public class XmlTransformer implements Transformer<String, String> {
-    private final List<Action<? super XmlProvider>> actions = new ArrayList<Action<? super XmlProvider>>();
-    private String indentation = "  ";
-
-    public void addAction(Action<? super XmlProvider> provider) {
-        actions.add(provider);
-    }
-
-    public void setIndentation(String indentation) {
-        this.indentation = indentation;
-    }
-
-    public void addAction(Closure closure) {
-        actions.add(new ClosureBackedAction<XmlProvider>(closure));
-    }
-
-    public void transform(File destination, final String encoding, final Action<? super Writer> generator) {
-        IoActions.writeTextFile(destination, encoding, new Action<Writer>() {
-            public void execute(Writer writer) {
-                transform(writer, encoding, generator);
-            }
-        });
-    }
-
-    public void transform(File destination, final Action<? super Writer> generator) {
-        IoActions.writeTextFile(destination, new Action<Writer>() {
-            public void execute(Writer writer) {
-                transform(writer, generator);
-            }
-        });
-    }
-
-    public void transform(Writer destination, Action<? super Writer> generator) {
-        StringWriter stringWriter = new StringWriter();
-        generator.execute(stringWriter);
-        transform(stringWriter.toString(), destination);
-    }
-
-    public void transform(Writer destination, String encoding, Action<? super Writer> generator) {
-        StringWriter stringWriter = new StringWriter();
-        generator.execute(stringWriter);
-        doTransform(stringWriter.toString()).writeTo(destination, encoding);
-    }
-
-    public String transform(String original) {
-        return doTransform(original).toString();
-    }
-
-    public void transform(String original, Writer destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(String original, OutputStream destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(Node original, Writer destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(Node original, OutputStream destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(Node original, File destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(DomNode original, Writer destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    public void transform(DomNode original, OutputStream destination) {
-        doTransform(original).writeTo(destination);
-    }
-
-    private XmlProviderImpl doTransform(String original) {
-        return doTransform(new XmlProviderImpl(original));
-    }
-
-    private XmlProviderImpl doTransform(Node original) {
-        return doTransform(new XmlProviderImpl(original));
-    }
-
-    private XmlProviderImpl doTransform(DomNode original) {
-        return doTransform(new XmlProviderImpl(original));
-    }
-
-    private XmlProviderImpl doTransform(XmlProviderImpl provider) {
-        provider.apply(actions);
-        return provider;
-    }
-
-    private class XmlProviderImpl implements XmlProvider {
-        private StringBuilder builder;
-        private Node node;
-        private String stringValue;
-        private Element element;
-        private String publicId;
-        private String systemId;
-
-        public XmlProviderImpl(String original) {
-            this.stringValue = original;
-        }
-
-        public XmlProviderImpl(Node original) {
-            this.node = original;
-        }
-
-        public XmlProviderImpl(DomNode original) {
-            this.node = original;
-            publicId = original.getPublicId();
-            systemId = original.getSystemId();
-        }
-
-        public void apply(Iterable<Action<? super XmlProvider>> actions) {
-            for (Action<? super XmlProvider> action : actions) {
-                action.execute(this);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringWriter writer = new StringWriter();
-            writeTo(writer);
-            return writer.toString();
-        }
-
-        public void writeTo(Writer writer) {
-            doWriteTo(writer, null);
-        }
-
-        public void writeTo(Writer writer, String encoding) {
-            doWriteTo(writer, encoding);
-        }
-
-        public void writeTo(File file) {
-            try {
-                OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
-                try {
-                    writeTo(outputStream);
-                } finally {
-                    outputStream.close();
-                }
-            } catch (IOException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        public void writeTo(OutputStream stream) {
-            try {
-                Writer writer = new OutputStreamWriter(stream, "UTF-8");
-                doWriteTo(writer, "UTF-8");
-                writer.flush();
-            } catch (IOException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        public StringBuilder asString() {
-            if (builder == null) {
-                builder = new StringBuilder(toString());
-                node = null;
-                element = null;
-            }
-            return builder;
-        }
-
-        public Node asNode() {
-            if (node == null) {
-                try {
-                    node = new XmlParser().parseText(toString());
-                } catch (Exception e) {
-                    throw UncheckedException.throwAsUncheckedException(e);
-                }
-                builder = null;
-                element = null;
-            }
-            return node;
-        }
-
-        public Element asElement() {
-            if (element == null) {
-                Document document;
-                try {
-                    document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(toString())));
-                } catch (Exception e) {
-                    throw UncheckedException.throwAsUncheckedException(e);
-                }
-                element = document.getDocumentElement();
-                builder = null;
-                node = null;
-            }
-            return element;
-        }
-
-        private void doWriteTo(Writer writer, String encoding) {
-            writeXmlDeclaration(writer, encoding);
-
-            try {
-                if (node != null) {
-                    printNode(node, writer);
-                } else if (element != null) {
-                    printDomNode(element, writer);
-                } else if (builder != null) {
-                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(builder)));
-                } else {
-                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(stringValue)));
-                }
-            } catch (IOException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        private void printNode(Node node, Writer writer) {
-            final PrintWriter printWriter = new PrintWriter(writer);
-            if (GUtil.isTrue(publicId)) {
-                printWriter.format("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">%n", node.name(), publicId, systemId);
-            }
-            IndentPrinter indentPrinter = new IndentPrinter(printWriter, indentation) {
-                @Override
-                public void println() {
-                    printWriter.println();
-                }
-            };
-            XmlNodePrinter nodePrinter = new XmlNodePrinter(indentPrinter);
-            nodePrinter.setPreserveWhitespace(true);
-            nodePrinter.print(node);
-            printWriter.flush();
-        }
-
-        private void printDomNode(org.w3c.dom.Node node, Writer destination) {
-            removeEmptyTextNodes(node); // empty text nodes hinder subsequent formatting
-            int indentAmount = determineIndentAmount();
-
-            try {
-                TransformerFactory factory = TransformerFactory.newInstance();
-                try {
-                    factory.setAttribute("indent-number", indentAmount);
-                } catch (IllegalArgumentException ignored) {
-                    /* unsupported by this transformer */
-                }
-
-                javax.xml.transform.Transformer transformer = factory.newTransformer();
-                transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-                transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
-                if (GUtil.isTrue(publicId)) {
-                    transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicId);
-                    transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemId);
-                }
-                try {
-                    // some impls support this but not factory.setAttribute("indent-number")
-                    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indentAmount));
-                } catch (IllegalArgumentException ignored) {
-                    /* unsupported by this transformer */
-                }
-
-                transformer.transform(new DOMSource(node), new StreamResult(destination));
-            } catch (TransformerException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        private int determineIndentAmount() {
-            if (indentation.equals("\t")) { // not supported by javax.xml.transform.Transformer; use two spaces instead
-                return 2;
-            }
-            return indentation.length(); // assume indentation uses spaces
-        }
-
-        private void removeEmptyTextNodes(org.w3c.dom.Node node) {
-            org.w3c.dom.NodeList children = node.getChildNodes();
-
-            for (int i = 0; i < children.getLength(); i++) {
-                org.w3c.dom.Node child = children.item(i);
-                if (child.getNodeType() == org.w3c.dom.Node.TEXT_NODE && child.getNodeValue().trim().length() == 0) {
-                    node.removeChild(child);
-                } else {
-                    removeEmptyTextNodes(child);
-                }
-            }
-        }
-
-        private void writeXmlDeclaration(Writer writer, String encoding) {
-            try {
-                writer.write("<?xml version=\"1.0\"");
-                if (encoding != null) {
-                    writer.write(" encoding=\"");
-                    writer.write(encoding);
-                    writer.write("\"");
-                }
-                writer.write("?>");
-                writer.write(SystemProperties.getLineSeparator());
-            } catch (IOException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-        private boolean hasXmlDeclaration(String xml) {
-            return xml.startsWith("<?xml"); // XML declarations must be located at first position of first line
-        }
-
-        private String stripXmlDeclaration(CharSequence sequence) {
-            String str = sequence.toString();
-            if (hasXmlDeclaration(str)) {
-                str = str.substring(str.indexOf("?>") + 2);
-                str = StringUtils.stripStart(str, null);
-            }
-            return str;
-        }
-    }
-}
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 1935167..0c0a7c5 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
@@ -19,90 +19,10 @@ package org.gradle.api.logging;
  * The log levels supported by Gradle.
  */
 public enum LogLevel {
-    DEBUG {
-        boolean isEnabled(Logger logger) {
-            return logger.isDebugEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.debug(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.debug(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.debug(message, throwable);
-        }},
-    INFO {
-        boolean isEnabled(Logger logger) {
-            return logger.isInfoEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.info(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.info(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.info(message, throwable);
-        }},
-    LIFECYCLE {
-        boolean isEnabled(Logger logger) {
-            return logger.isLifecycleEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.lifecycle(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.lifecycle(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.lifecycle(message, throwable);
-        }},
-    WARN {
-        boolean isEnabled(Logger logger) {
-            return logger.isWarnEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.warn(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.warn(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.warn(message, throwable);
-        }},
-    QUIET {
-        boolean isEnabled(Logger logger) {
-            return logger.isQuietEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.quiet(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.quiet(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.quiet(message, throwable);
-        }},
-    ERROR {
-        boolean isEnabled(Logger logger) {
-            return logger.isErrorEnabled();
-        }
-        void log(Logger logger, String message) {
-            logger.error(message);
-        }
-        void log(Logger logger, String message, Object... objects) {
-            logger.error(message, objects);
-        }
-        void log(Logger logger, String message, Throwable throwable) {
-            logger.error(message, throwable);
-        }};
-
-    abstract boolean isEnabled(Logger logger);
-
-    abstract void log(Logger logger, String message);
-
-    abstract void log(Logger logger, String message, Object... objects);
-
-    abstract void log(Logger logger, String message, Throwable throwable);
+    DEBUG,
+    INFO,
+    LIFECYCLE,
+    WARN,
+    QUIET,
+    ERROR
 }
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 7d2b455..a23124d 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
@@ -16,7 +16,7 @@
 
 package org.gradle.api.logging;
 
-import org.apache.ivy.util.Message;
+import org.apache.tools.ant.Project;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Marker;
 import org.slf4j.MarkerFactory;
@@ -39,7 +39,7 @@ public class Logging {
      * @return the logger. Never returns null.
      */
     public static Logger getLogger(Class c) {
-        return new LoggerImpl(LoggerFactory.getLogger(c));
+        return (Logger) LoggerFactory.getLogger(c);
     }
 
     /**
@@ -49,315 +49,15 @@ public class Logging {
      * @return the logger. Never returns null.
      */
     public static Logger getLogger(String name) {
-        return new LoggerImpl(LoggerFactory.getLogger(name));
+        return (Logger) LoggerFactory.getLogger(name);
     }
 
     public static final Map<Integer, LogLevel> ANT_IVY_2_SLF4J_LEVEL_MAPPER = new HashMap<Integer, LogLevel>() {
         {
-            put(Message.MSG_ERR, LogLevel.ERROR);
-            put(Message.MSG_WARN, LogLevel.WARN);
-            put(Message.MSG_INFO, LogLevel.INFO);
-            put(Message.MSG_DEBUG, LogLevel.DEBUG);
-            put(Message.MSG_VERBOSE, LogLevel.DEBUG);
+            put(Project.MSG_ERR, LogLevel.ERROR);
+            put(Project.MSG_WARN, LogLevel.WARN);
+            put(Project.MSG_INFO, LogLevel.INFO);
+            put(Project.MSG_DEBUG, LogLevel.DEBUG);
+            put(Project.MSG_VERBOSE, LogLevel.DEBUG);
         }};
-
-    private static class LoggerImpl implements Logger {
-        private final org.slf4j.Logger logger;
-
-        public LoggerImpl(org.slf4j.Logger logger) {
-            this.logger = logger;
-        }
-
-        public boolean isEnabled(LogLevel level) {
-            return level.isEnabled(this);
-        }
-
-        public void log(LogLevel level, String message) {
-            level.log(this, message);
-        }
-
-        public void log(LogLevel level, String message, Object... objects) {
-            level.log(this, message, objects);
-        }
-
-        public void log(LogLevel level, String message, Throwable throwable) {
-            level.log(this, message, throwable);
-        }
-
-        public boolean isLifecycleEnabled() {
-            return logger.isInfoEnabled(LIFECYCLE);
-        }
-
-        public void lifecycle(String message) {
-            logger.info(LIFECYCLE, message);
-        }
-
-        public void lifecycle(String message, Object... objects) {
-            logger.info(LIFECYCLE, message, objects);
-        }
-
-        public void lifecycle(String message, Throwable throwable) {
-            logger.info(LIFECYCLE, message, throwable);
-        }
-
-        public boolean isQuietEnabled() {
-            return logger.isInfoEnabled(QUIET);
-        }
-
-        public void quiet(String message) {
-            logger.info(QUIET, message);
-        }
-
-        public void quiet(String message, Object... objects) {
-            logger.info(QUIET, message, objects);
-        }
-
-        public void quiet(String message, Throwable throwable) {
-            logger.info(QUIET, message, throwable);
-        }
-
-        public void debug(Marker marker, String s) {
-            logger.debug(marker, s);
-        }
-
-        public void debug(Marker marker, String s, Object o) {
-            logger.debug(marker, s, o);
-        }
-
-        public void debug(Marker marker, String s, Object o, Object o1) {
-            logger.debug(marker, s, o, o1);
-        }
-
-        public void debug(Marker marker, String s, Object[] objects) {
-            logger.debug(marker, s, objects);
-        }
-
-        public void debug(Marker marker, String s, Throwable throwable) {
-            logger.debug(marker, s, throwable);
-        }
-
-        public void debug(String s) {
-            logger.debug(s);
-        }
-
-        public void debug(String s, Object o) {
-            logger.debug(s, o);
-        }
-
-        public void debug(String s, Object o, Object o1) {
-            logger.debug(s, o, o1);
-        }
-
-        public void debug(String s, Object[] objects) {
-            logger.debug(s, objects);
-        }
-
-        public void debug(String s, Throwable throwable) {
-            logger.debug(s, throwable);
-        }
-
-        public void error(Marker marker, String s) {
-            logger.error(marker, s);
-        }
-
-        public void error(Marker marker, String s, Object o) {
-            logger.error(marker, s, o);
-        }
-
-        public void error(Marker marker, String s, Object o, Object o1) {
-            logger.error(marker, s, o, o1);
-        }
-
-        public void error(Marker marker, String s, Object[] objects) {
-            logger.error(marker, s, objects);
-        }
-
-        public void error(Marker marker, String s, Throwable throwable) {
-            logger.error(marker, s, throwable);
-        }
-
-        public void error(String s) {
-            logger.error(s);
-        }
-
-        public void error(String s, Object o) {
-            logger.error(s, o);
-        }
-
-        public void error(String s, Object o, Object o1) {
-            logger.error(s, o, o1);
-        }
-
-        public void error(String s, Object[] objects) {
-            logger.error(s, objects);
-        }
-
-        public void error(String s, Throwable throwable) {
-            logger.error(s, throwable);
-        }
-
-        public String getName() {
-            return logger.getName();
-        }
-
-        public void info(Marker marker, String s) {
-            logger.info(marker, s);
-        }
-
-        public void info(Marker marker, String s, Object o) {
-            logger.info(marker, s, o);
-        }
-
-        public void info(Marker marker, String s, Object o, Object o1) {
-            logger.info(marker, s, o, o1);
-        }
-
-        public void info(Marker marker, String s, Object[] objects) {
-            logger.info(marker, s, objects);
-        }
-
-        public void info(Marker marker, String s, Throwable throwable) {
-            logger.info(marker, s, throwable);
-        }
-
-        public void info(String s) {
-            logger.info(s);
-        }
-
-        public void info(String s, Object o) {
-            logger.info(s, o);
-        }
-
-        public void info(String s, Object o, Object o1) {
-            logger.info(s, o, o1);
-        }
-
-        public void info(String s, Object[] objects) {
-            logger.info(s, objects);
-        }
-
-        public void info(String s, Throwable throwable) {
-            logger.info(s, throwable);
-        }
-
-        public boolean isDebugEnabled() {
-            return logger.isDebugEnabled();
-        }
-
-        public boolean isDebugEnabled(Marker marker) {
-            return logger.isDebugEnabled(marker);
-        }
-
-        public boolean isErrorEnabled() {
-            return logger.isErrorEnabled();
-        }
-
-        public boolean isErrorEnabled(Marker marker) {
-            return logger.isErrorEnabled(marker);
-        }
-
-        public boolean isInfoEnabled() {
-            return logger.isInfoEnabled();
-        }
-
-        public boolean isInfoEnabled(Marker marker) {
-            return logger.isInfoEnabled(marker);
-        }
-
-        public boolean isTraceEnabled() {
-            return logger.isTraceEnabled();
-        }
-
-        public boolean isTraceEnabled(Marker marker) {
-            return logger.isTraceEnabled(marker);
-        }
-
-        public boolean isWarnEnabled() {
-            return logger.isWarnEnabled();
-        }
-
-        public boolean isWarnEnabled(Marker marker) {
-            return logger.isWarnEnabled(marker);
-        }
-
-        public void trace(Marker marker, String s) {
-            logger.trace(marker, s);
-        }
-
-        public void trace(Marker marker, String s, Object o) {
-            logger.trace(marker, s, o);
-        }
-
-        public void trace(Marker marker, String s, Object o, Object o1) {
-            logger.trace(marker, s, o, o1);
-        }
-
-        public void trace(Marker marker, String s, Object[] objects) {
-            logger.trace(marker, s, objects);
-        }
-
-        public void trace(Marker marker, String s, Throwable throwable) {
-            logger.trace(marker, s, throwable);
-        }
-
-        public void trace(String s) {
-            logger.trace(s);
-        }
-
-        public void trace(String s, Object o) {
-            logger.trace(s, o);
-        }
-
-        public void trace(String s, Object o, Object o1) {
-            logger.trace(s, o, o1);
-        }
-
-        public void trace(String s, Object[] objects) {
-            logger.trace(s, objects);
-        }
-
-        public void trace(String s, Throwable throwable) {
-            logger.trace(s, throwable);
-        }
-
-        public void warn(Marker marker, String s) {
-            logger.warn(marker, s);
-        }
-
-        public void warn(Marker marker, String s, Object o) {
-            logger.warn(marker, s, o);
-        }
-
-        public void warn(Marker marker, String s, Object o, Object o1) {
-            logger.warn(marker, s, o, o1);
-        }
-
-        public void warn(Marker marker, String s, Object[] objects) {
-            logger.warn(marker, s, objects);
-        }
-
-        public void warn(Marker marker, String s, Throwable throwable) {
-            logger.warn(marker, s, throwable);
-        }
-
-        public void warn(String s) {
-            logger.warn(s);
-        }
-
-        public void warn(String s, Object o) {
-            logger.warn(s, o);
-        }
-
-        public void warn(String s, Object o, Object o1) {
-            logger.warn(s, o, o1);
-        }
-
-        public void warn(String s, Object[] objects) {
-            logger.warn(s, objects);
-        }
-
-        public void warn(String s, Throwable throwable) {
-            logger.warn(s, throwable);
-        }
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
index 0fa81d4..b7d58e5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingManager.java
@@ -16,10 +16,13 @@
 
 package org.gradle.api.logging;
 
+import org.gradle.internal.HasInternalProtocol;
+
 /**
  * <p>A {@code LoggingManager} provides access to and control over the Gradle logging system. Using this interface, you
  * can control the current logging level and standard output and error capture.</p>
  */
+ at HasInternalProtocol
 public interface LoggingManager extends LoggingOutput {
     /**
      * Requests that output written to System.out be routed to Gradle's logging system. The default is that System.out
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
index a5b50ee..4d01150 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
@@ -16,9 +16,12 @@
 
 package org.gradle.api.logging;
 
+import org.gradle.internal.HasInternalProtocol;
+
 /**
  * Provides access to the output of the Gradle logging system.
  */
+ at HasInternalProtocol
 public interface LoggingOutput {
     /**
      * Adds a listener which receives output written to standard output by the Gradle logging system.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/AppliedPlugin.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/AppliedPlugin.java
new file mode 100644
index 0000000..ae54c34
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/AppliedPlugin.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+/**
+ * Represents a plugin that has been applied.
+ * <p>
+ * Currently just provides information about the ID of the plugin.
+ *
+ * @see org.gradle.api.plugins.PluginAware
+ * @since 2.3
+ */
+ at Incubating
+public interface AppliedPlugin {
+
+    /**
+     * The ID of the plugin.
+     * <p>
+     * An example of a plugin ID would be {@code "org.gradle.java"}.
+     * This method always returns the fully qualified ID, regardless of whether the fully qualified ID was used to apply the plugin or not.
+     * <p>
+     * This value is guaranteed to be unique, for a given {@link org.gradle.api.plugins.PluginAware}.
+     *
+     * @return the ID of the plugin
+     */
+    String getId();
+
+    /**
+     * The namespace of the plugin.
+     * <p>
+     * An example of a plugin namespace would be {@code "org.gradle"} for the plugin with ID {@code "org.gradle.java"}.
+     * This method always returns the namespace, regardless of whether the fully qualified ID was used to apply the plugin or not.
+     * <p>
+     * If the plugin has an unqualified ID, this method will return {@code null}.
+     *
+     * @return the namespace of the plugin
+     */
+    @Nullable
+    String getNamespace();
+
+    /**
+     * The name of the plugin.
+     * <p>
+     * An example of a plugin name would be {@code "java"} for the plugin with ID {@code "org.gradle.java"}.
+     * This method always returns the name, regardless of whether the fully qualified ID was used to apply the plugin or not.
+     * <p>
+     * If the plugin has an unqualified ID, this method will return the same value as {@link #getId()}.
+     *
+     * @return the name of the plugin
+     */
+    String getName();
+
+}
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 7256711..8abf239 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
@@ -49,7 +49,7 @@ public interface Convention extends ExtensionContainer {
      *
      * @param type The convention object type.
      * @return The object. Returns null if there is no such object.
-     * @throws IllegalStateException When there there are multiple matching objects.
+     * @throws IllegalStateException When there are multiple matching objects.
      */
     <T> T findPlugin(Class<T> type) throws IllegalStateException;
 
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 7e0fd94..4bccaf6 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
@@ -38,17 +38,6 @@ public interface ExtensionContainer {
     void add(String name, Object extension);
 
     /**
-     * Deprecated. Use {@link #create}
-     *
-     * @param name The name for the extension
-     * @param type The type of the extension
-     * @param constructionArguments The arguments to be used to construct the extension instance
-     * @deprecated use {@link #create}
-     */
-    @Deprecated
-    void add(String name, Class<?> type, Object... constructionArguments);
-
-    /**
      * Adds a new extension to this container, that itself is dynamically made {@link ExtensionAware}.
      *
      * A new instance of the given {@code type} will be created using the given {@code constructionArguments}. The new
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtraPropertiesExtension.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtraPropertiesExtension.java
index a7c58e2..4405faa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtraPropertiesExtension.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ExtraPropertiesExtension.java
@@ -67,15 +67,6 @@ import java.util.Map;
  * Groovy syntax is used or not. If Groovy property syntax is used, the Groovy {@link groovy.lang.MissingPropertyException} will be thrown.
  * When the {@link #get(String)} method is used, an {@link UnknownPropertyException} will be thrown.
  *
- * <b>This mechanism replaces the ability to create new properties on domain objects directly.</b> In versions of Gradle prior to {@code 1.0-milestone-9},
- * new properties could be added to objects by assignment:
- *
- * <pre>
- * project.myProp = "myValue"
- * </pre>
- *
- * This functionality has been <b>deprecated</b> (a deprecation warning message will be issued when this is attempted). Future versions of Gradle will
- * remove this functionality completely, resulting in an exception being thrown.
  */
 public interface ExtraPropertiesExtension {
 
@@ -179,7 +170,7 @@ public interface ExtraPropertiesExtension {
      */
     public static class UnknownPropertyException extends InvalidUserDataException {
         public UnknownPropertyException(ExtraPropertiesExtension extension, String propertyName) {
-            super(String.format("cannot get property '%s' on extra properties extension as it does not exist", propertyName, extension));
+            super(String.format("Cannot get property '%s' on extra properties extension as it does not exist", propertyName));
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/InvalidPluginException.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/InvalidPluginException.java
new file mode 100644
index 0000000..cb939ba
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/InvalidPluginException.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.plugins;
+
+import org.gradle.api.GradleException;
+
+/**
+ * Thrown when a plugin is found to be invalid when it is loaded.
+ */
+public class InvalidPluginException extends GradleException {
+
+    public InvalidPluginException(String message) {
+        super(message);
+    }
+
+    public InvalidPluginException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ObjectConfigurationAction.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ObjectConfigurationAction.java
index 8eab018..76374a5 100755
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/ObjectConfigurationAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/ObjectConfigurationAction.java
@@ -52,6 +52,17 @@ public interface ObjectConfigurationAction {
     ObjectConfigurationAction plugin(Class<? extends Plugin> pluginClass);
 
     /**
+     * Adds the plugin implemented by the given class to the target.
+     * <p>
+     * The class is expected to either implement {@link Plugin}, or extend {@link org.gradle.model.RuleSource}.
+     * An exception will be thrown if the class is not a valid plugin implementation.
+     *
+     * @param pluginClass the plugin to apply
+     * @return this
+     */
+    ObjectConfigurationAction type(Class<?> pluginClass);
+
+    /**
      * Adds a {@link org.gradle.api.Plugin} to use to configure the target objects. You can call this method multiple
      * times, to use multiple plugins. Scripts and plugins are applied in the order that they are added.
      *
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
index 2dd774b..3fb43bd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginAware.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginAware.java
@@ -17,46 +17,86 @@
 package org.gradle.api.plugins;
 
 import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
 
 import java.util.Map;
 
 /**
- * Objects a {@link org.gradle.api.Plugin} can be applied to.
- *
+ * Something that can have plugins applied to it.
+ * <p>
+ * The {@link #getPluginManager() plugin manager} can be used for applying and detecting whether plugins have been applied.
  * <p>
  * For more on writing and applying plugins, see {@link org.gradle.api.Plugin}.
- * </p>
  */
+ at HasInternalProtocol
 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.
+     * The container of plugins that have been applied to this object.
+     * <p>
+     * While not deprecated, it is preferred to use the methods of this interface or the {@link #getPluginManager() plugin manager} than use the plugin container.
+     * <p>
+     * Use one of the 'apply' methods on this interface or on the {@link #getPluginManager() plugin manager} to apply plugins instead of applying via the plugin container.
+     * <p>
+     * Use {@link PluginManager#hasPlugin(String)} or similar to query for the application of plugins instead of doing so via the plugin container.
      *
-     * @return the plugin container. Never returns null.
+     * @return the plugin container
+     * @see #apply
+     * @see PluginManager#hasPlugin(String)
      */
     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>
+     * Applies zero or more plugins or scripts.
+     * <p>
+     * The given closure is used to configure an {@link ObjectConfigurationAction}, which “builds” the plugin application.
+     * <p>
+     * This method differs from {@link #apply(java.util.Map)} in that it allows methods of the configuration action to be invoked more than once.
      *
-     * @param closure The closure to configure the {@code ObjectConfigurationAction}.
+     * @param closure the closure to configure an {@link ObjectConfigurationAction} with before “executing” it
+     * @see #apply(java.util.Map)
      */
     void apply(Closure closure);
 
     /**
-     * <p>Configures this Object using plugins or scripts. The following options are available:</p>
+     * Applies zero or more plugins or scripts.
+     * <p>
+     * The given closure is used to configure an {@link ObjectConfigurationAction}, which “builds” the plugin application.
+     * <p>
+     * This method differs from {@link #apply(java.util.Map)} in that it allows methods of the configuration action to be invoked more than once.
+     *
+     * @param action the action to configure an {@link ObjectConfigurationAction} with before “executing” it
+     * @see #apply(java.util.Map)
+     */
+    void apply(Action<? super ObjectConfigurationAction> action);
+
+    /**
+     * Applies a plugin or script, using the given options provided as a map. Does nothing if the plugin has already been applied.
+     * <p>
+     * The given map is applied as a series of method calls to a newly created {@link ObjectConfigurationAction}.
+     * That is, each key in the map is expected to be the name of a method {@link ObjectConfigurationAction} and the value to be compatible arguments to that method.
      *
-     * <ul><li>{@code from}: A script to apply to the object. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.</li>
+     * <p>The following options are available:</p>
      *
-     * <li>{@code plugin}: The id or implementation class of the plugin to apply to the object.</li>
+     * <ul><li>{@code from}: A script to apply. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.</li>
      *
-     * <li>{@code to}: The target delegate object or objects. Use this to configure objects other than this
-     * object.</li></ul>
+     * <li>{@code plugin}: The id or implementation class of the plugin to apply.</li>
      *
-     * <p>For more detail, see {@link ObjectConfigurationAction}.</p>
+     * <li>{@code to}: The target delegate object or objects. The default is this plugin aware object. Use this to configure objects other than this object.</li></ul>
      *
-     * @param options The options to use to configure the {@code ObjectConfigurationAction}.
+     * @param options the options to use to configure and {@link ObjectConfigurationAction} before “executing” it
      */
     void apply(Map<String, ?> options);
+
+    /**
+     * The plugin manager for this plugin aware object.
+     *
+     * @return the plugin manager
+     * @since 2.3
+     */
+    @Incubating
+    PluginManager getPluginManager();
+
 }
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 ef8025f..22cb7fe 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
@@ -16,6 +16,8 @@
 
 package org.gradle.api.plugins;
 
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
 import org.gradle.api.Plugin;
 
 /**
@@ -27,7 +29,7 @@ import org.gradle.api.Plugin;
  */
 public interface PluginContainer extends PluginCollection<Plugin> {
     /**
-     * Has the same behavior as {@link #apply(Class)} except that the the plugin is specified via its id. Not all
+     * Has the same behavior as {@link #apply(Class)} except that the plugin is specified via its id. Not all
      * plugins have an id.
      *
      * @param id The id of the plugin to be applied.
@@ -108,4 +110,18 @@ public interface PluginContainer extends PluginCollection<Plugin> {
      * @throws UnknownPluginException When there is no plugin with the given type.
      */
     <T extends Plugin> T getAt(Class<T> type) throws UnknownPluginException;
+
+    /**
+     * Executes or registers an action for a plugin with given id.
+     * If the plugin was already applied, the action is executed.
+     * If the plugin is applied sometime later the action will be executed after the plugin is applied.
+     * If the plugin is never applied, the action is never executed.
+     * The behavior is similar to {@link #withType(Class, org.gradle.api.Action)}.
+     *
+     * @param pluginId the id of the plugin
+     * @param action the action
+     */
+    @Incubating
+    void withId(String pluginId, Action<? super Plugin> action);
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginManager.java b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginManager.java
new file mode 100644
index 0000000..ee5e3b5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/plugins/PluginManager.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.NonExtensible;
+import org.gradle.api.Nullable;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Facilitates applying plugins and determining which plugins have been applied to a {@link PluginAware} object.
+ *
+ * @see PluginAware
+ * @since 2.3
+ */
+ at Incubating
+ at HasInternalProtocol
+ at NonExtensible
+public interface PluginManager {
+
+    /**
+     * Applies the plugin with the given ID. Does nothing if the plugin has already been applied.
+     * <p>
+     * Plugins in the {@code "org.gradle"} namespace can be applied directly via name.
+     * That is, the following two lines are equivalent…
+     * <pre autoTested='true'>
+     * pluginManager.apply "org.gradle.java"
+     * pluginManager.apply "java"
+     * </pre>
+     *
+     * @param pluginId the ID of the plugin to apply
+     * @since 2.3
+     */
+    @Incubating
+    void apply(String pluginId);
+
+    /**
+     * Applies the given plugin. Does nothing if the plugin has already been applied.
+     * <p>
+     * The given class should implement the {@link org.gradle.api.Plugin} interface, and be parameterized for a compatible type of {@code this}.
+     * <p>
+     * The following two lines are equivalent…
+     * <pre autoTested='true'>
+     * pluginManager.apply org.gradle.api.plugins.JavaPlugin
+     * pluginManager.apply "org.gradle.java"
+     * </pre>
+     *
+     * @param type the plugin class to apply
+     * @since 2.3
+     */
+    @Incubating
+    void apply(Class<?> type);
+
+    /**
+     * Returns the information about the plugin that has been applied with the given ID, or null if no plugin has been applied with the given ID.
+     * <p>
+     * Plugins in the {@code "org.gradle"} namespace (that is, core Gradle plugins) can be specified by either name (e.g. {@code "java"}) or ID {@code "org.gradle.java"}.
+     * All other plugins must be queried for by their full ID (e.g. {@code "org.company.some-plugin"}).
+     * <p>
+     * Some Gradle plugins have not yet migrated to fully qualified plugin IDs.
+     * Such plugins can be detected with this method by simply using the unqualified ID (e.g. {@code "some-third-party-plugin"}.
+     *
+     * @param id the plugin ID
+     * @return information about the applied plugin, or {@code null} if no plugin has been applied with the given ID
+     * @since 2.3
+     */
+    @Nullable
+    @Incubating
+    AppliedPlugin findPlugin(String id);
+
+    /**
+     * Returns {@code true} if a plugin with the given ID has already been applied, otherwise {@code false}.
+     *
+     * @param id the plugin ID. See {@link #findPlugin(String)} for details about this parameter.
+     * @return {@code true} if the plugin has been applied
+     * @since 2.3
+     */
+    @Incubating
+    boolean hasPlugin(String id);
+
+    /**
+     * Executes the given action when the specified plugin is applied.
+     * <p>
+     * If a plugin with the specified ID has already been applied, the supplied action will be executed immediately.
+     * Otherwise, the action will executed immediately after a plugin with the specified ID is applied.
+     * <p>
+     * The given action is always executed after the plugin has been applied.
+     *
+     * @param id the plugin ID. See {@link #findPlugin(String)} for details about this parameter.
+     * @param action the action to execute if/when the plugin is applied
+     * @since 2.3
+     */
+    @Incubating
+    void withPlugin(String id, Action<? super AppliedPlugin> action);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/resources/ResourceHandler.java b/subprojects/core/src/main/groovy/org/gradle/api/resources/ResourceHandler.java
index 511edcd..b46f8ba 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/resources/ResourceHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/resources/ResourceHandler.java
@@ -16,6 +16,8 @@
 
 package org.gradle.api.resources;
 
+import org.gradle.api.Incubating;
+
 /**
  * Provides access to resource-specific utility methods, for example factory methods that create various resources.
  */
@@ -36,4 +38,15 @@ public interface ResourceHandler {
      * @param path The path evaluated as per {@link org.gradle.api.Project#file(Object)}.
      */
     ReadableResource bzip2(Object path);
+
+    /**
+     * Returns a factory for creating {@code TextResource}s from various sources such as
+     * strings, files, and archive entries.
+     *
+     * @since 2.2
+     *
+     * @return a factory for creating {@code TextResource}s
+     */
+    @Incubating
+    TextResourceFactory getText();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResource.java b/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResource.java
new file mode 100644
index 0000000..80a2283
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResource.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.resources;
+
+import org.gradle.api.Buildable;
+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.Optional;
+
+import java.io.File;
+import java.io.Reader;
+
+/**
+ * A read-only body of text backed by a string, file, archive entry, or other source.
+ * To create a text resource, use one of the factory methods in {@link TextResourceFactory}
+ * (e.g. {@code project.resources.text.fromFile(myFile)}).
+ *
+ * @since 2.2
+ */
+ at Incubating
+public interface TextResource extends Buildable {
+    /**
+     * Returns a string containing the resource's text
+     *
+     * @return a string containing the resource's text
+     */
+    String asString();
+
+    /**
+     * Returns a reader that allows to read the resource's text.
+     *
+     * @return a reader that allows to read the resource's text
+     */
+    Reader asReader();
+
+    /**
+     * Returns a file containing the resource's text and using the given character encoding.
+     * If this resource is backed by a file with a matching encoding, that file may be
+     * returned. Otherwise, a temporary file will be created and returned.
+     *
+     * @param charset a character encoding (e.g. {@code "utf-8"})
+     *
+     * @return a file containing the resource's text and using the given character encoding
+     */
+    File asFile(String charset);
+
+    /**
+     * Same as {@code asFile(Charset.defaultCharset().name())}.
+     */
+    File asFile();
+
+    /**
+     * Returns the input properties registered when this resource is used as task input.
+     * Not typically used directly.
+     *
+     * @return the input properties registered when this resource is used as task input
+     */
+    @Input
+    @Optional
+    Object getInputProperties();
+
+    /**
+     * Returns the input files registered when this resource is used as task input.
+     * Not typically used directly.
+     *
+     * @return the input files registered when this resource is used as task input
+     */
+    @InputFiles
+    @Optional
+    FileCollection getInputFiles();
+}
+
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResourceFactory.java b/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResourceFactory.java
new file mode 100644
index 0000000..7fe13ca
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/resources/TextResourceFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.resources;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Creates {@code TextResource}s backed by sources such as strings, files, and archive entries.
+ *
+ * <p>Example usages:
+ *
+ * <pre autoTested=''>
+ * def sourcedFromString = resources.text.fromString("some text content")
+ *
+ * def sourcedFromFile = resources.text.fromFile("path/to/file.txt")
+ *
+ * task someTask {} // assumption: produces a text file and declares it as output
+ * def sourcedFromTask = resources.text.fromFile(someTask)
+ *
+ * def sourcedFromArchiveEntry =
+ *   resources.text.fromArchiveEntry("path/to/archive.zip", "path/to/archive/entry.txt")
+ *
+ * configurations { someConfig } // assumption: contains a single archive
+ * def sourcedFromConfiguration =
+ *   resources.text.fromArchiveEntry(configurations.someConfig, "path/to/archive/entry.txt")
+ * </pre>
+ *
+ * File based factory methods optionally accept a character encoding. If no encoding is specified,
+ * the platform's default encoding is used.
+ *
+ * @since 2.2
+ */
+ at Incubating
+public interface TextResourceFactory {
+    /**
+     * Creates a text resource backed by the given string.
+     *
+     * @param string a string
+     * @return a text resource backed by the given string
+     */
+    TextResource fromString(String string);
+
+    /**
+     * Creates a text resource backed by the given file.
+     *
+     * @param file a text file evaluated as per {@link org.gradle.api.Project#files(Object...)}
+     * @param charset the file's character encoding (e.g. {@code "utf-8"})
+     * @return a text resource backed by the given file
+     */
+    TextResource fromFile(Object file, String charset);
+
+    /**
+     * Same as {@code fromFile(file, Charset.defaultCharset())}.
+     */
+    TextResource fromFile(Object file);
+
+    /**
+     * Creates a text resource backed by the archive entry at the given path within the given archive.
+     * The archive format is determined based on the archive's file extension. If the archive format
+     * is not supported or cannot be determined, any attempt to access the resource will fail with an exception.
+     *
+     * @param archive an archive file evaluated as per {@link org.gradle.api.Project#files(Object...)}
+     * @param entryPath the path to an archive entry
+     * @param charset the archive entry's character encoding (e.g. {@code "utf-8"})
+     *
+     * @return a text resource backed by the archive entry at the given path within the given archive
+     */
+    TextResource fromArchiveEntry(Object archive, String entryPath, String charset);
+
+    /**
+     * Same as {@code fromArchiveEntry(archive, path, Charset.defaultCharset().name())}.
+     */
+    TextResource fromArchiveEntry(Object archive, String path);
+}
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
deleted file mode 100644
index 0430238..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/specs/Specs.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.specs;
-
-import groovy.lang.Closure;
-import org.gradle.api.specs.internal.ClosureSpec;
-import org.gradle.util.DeprecationLogger;
-
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Provides a number of {@link org.gradle.api.specs.Spec} implementations.
- */
-public class Specs {
-
-    /*
-        Note: This should be in baseServicesGroovy, but it needs the DeprecationLogger which needs commons-lang
-              It as
-     */
-    public static final Spec<Object> SATISFIES_ALL = new Spec<Object>() {
-        public boolean isSatisfiedBy(Object element) {
-            return true;
-        }
-    };
-
-    public static <T> Spec<T> satisfyAll() {
-        return (Spec<T>)SATISFIES_ALL;
-    }
-
-    public static final Spec<Object> SATISFIES_NONE = new Spec<Object>() {
-        public boolean isSatisfiedBy(Object element) {
-            return false;
-        }
-    };
-    
-    public static <T> Spec<T> satisfyNone() {
-        return (Spec<T>)SATISFIES_NONE;
-    }
-
-    public static <T> Spec<T> convertClosureToSpec(final Closure closure) {
-        return new ClosureSpec<T>(closure);
-    }
-
-    public static <T> Set<T> filterIterable(Iterable<? extends T> iterable, Spec<? super T> spec) {
-        DeprecationLogger.nagUserOfReplacedMethod("Specs.filterIterable", "CollectionUtils.filter");
-        Set<T> result = new LinkedHashSet<T>();
-        for (T t : iterable) {
-            if (spec.isSatisfiedBy(t)) {
-                result.add(t);
-            }
-        }
-        return result;
-    }
-
-    public static <T> AndSpec<T> and(Spec<? super T>... specs) {
-        return new AndSpec<T>(specs);  
-    }
-
-    public static <T> OrSpec<T> or(Spec<? super T>... specs) {
-        return new OrSpec<T>(specs);
-    }
-
-    public static <T> NotSpec<T> not(Spec<? super T> spec) {
-        return new NotSpec<T>(spec);  
-    }
-
-    public static <T> Spec<T> or(final boolean defaultWhenNoSpecs, List<? extends Spec<? super T>> specs) {
-        if (specs.isEmpty()) {
-            return new Spec<T>() {
-                public boolean isSatisfiedBy(T element) {
-                    return defaultWhenNoSpecs;
-                }
-            };
-        }
-        return new OrSpec<T>(specs.toArray(new Spec[specs.size()]));
-    }
-}
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 cb3630f..f03acd1 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,14 +19,14 @@ 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.FileLookup;
 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 javax.inject.Inject;
 import java.io.FilterReader;
 import java.util.Map;
 import java.util.Set;
@@ -46,19 +46,37 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
     }
 
     protected CopySpecInternal createRootSpec() {
-        Instantiator instantiator = getServices().get(Instantiator.class);
-        FileResolver fileResolver = getServices().get(FileResolver.class);
+        Instantiator instantiator = getInstantiator();
+        FileResolver fileResolver = getFileResolver();
         return instantiator.newInstance(DefaultCopySpec.class, fileResolver, instantiator);
     }
 
     protected abstract CopyAction createCopyAction();
 
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected FileSystem getFileSystem() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected FileLookup getFileLookup() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     protected void copy() {
-        configureRootSpec();
-
-        Instantiator instantiator = getServices().get(Instantiator.class);
-        FileSystem fileSystem = getServices().get(FileSystem.class);
+        Instantiator instantiator = getInstantiator();
+        FileSystem fileSystem = getFileSystem();
 
         CopyActionExecuter copyActionExecuter = new CopyActionExecuter(instantiator, fileSystem);
         CopyAction copyAction = createCopyAction();
@@ -66,43 +84,15 @@ public abstract class AbstractCopyTask extends ConventionTask implements CopySpe
         setDidWork(didWork.getDidWork());
     }
 
-    protected void configureRootSpec() {
-        if (!rootSpec.hasSource()) {
-            Object srcDirs = getDefaultSource();
-            if (srcDirs != null) {
-                from(srcDirs);
-            }
-        }
-    }
-
-    /**
-     * Returns the default source for this task.
-     * @deprecated Use getSource() instead.
-     */
-    @Deprecated
-    public FileCollection getDefaultSource() {
-        DeprecationLogger.nagUserOfReplacedMethod("AbstractCopyTask.getDefaultSource()", "getSource()");
-        return null;
-    }
-
     /**
      * Returns the source files for this task.
      * @return The source files. Never returns null.
      */
     @InputFiles @SkipWhenEmpty @Optional
     public FileCollection getSource() {
-        if (rootSpec.hasSource()){
-            return rootSpec.getAllSource();
-        }else{
-            return DeprecationLogger.whileDisabled(new Factory<FileCollection>() {
-                public FileCollection create() {
-                    return getDefaultSource();
-                }
-            });
-        }
+        return rootSpec.buildRootResolver().getAllSource();
     }
 
-
     public CopySpecInternal getRootSpec() {
         return rootSpec;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractExecTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractExecTask.java
new file mode 100644
index 0000000..1b0dc14
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/AbstractExecTask.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks;
+
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.ProcessForkOptions;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@code AbstractExecTask} is the base class for all exec tasks.
+ *
+ * @param <T> The concrete type of the class.
+ */
+public abstract class AbstractExecTask<T extends AbstractExecTask> extends ConventionTask implements ExecSpec {
+    private final Class<T> taskType;
+    private ExecAction execAction;
+    private ExecResult execResult;
+
+    public AbstractExecTask(Class<T> taskType) {
+        execAction = getExecActionFactory().newExecAction();
+        this.taskType = taskType;
+    }
+
+    @Inject
+    protected ExecActionFactory getExecActionFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    protected void exec() {
+        execResult = execAction.execute();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T commandLine(Object... arguments) {
+        execAction.commandLine(arguments);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T commandLine(Iterable<?> args) {
+        execAction.commandLine(args);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T args(Object... args) {
+        execAction.args(args);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T args(Iterable<?> args) {
+        execAction.args(args);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T setArgs(Iterable<?> arguments) {
+        execAction.setArgs(arguments);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<String> getArgs() {
+        return execAction.getArgs();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<String> getCommandLine() {
+        return execAction.getCommandLine();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCommandLine(Iterable<?> args) {
+        execAction.setCommandLine(args);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCommandLine(Object... args) {
+        execAction.setCommandLine(args);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getExecutable() {
+        return execAction.getExecutable();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setExecutable(Object executable) {
+        execAction.setExecutable(executable);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T executable(Object executable) {
+        execAction.executable(executable);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public File getWorkingDir() {
+        return execAction.getWorkingDir();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setWorkingDir(Object dir) {
+        execAction.setWorkingDir(dir);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T workingDir(Object dir) {
+        execAction.workingDir(dir);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, Object> getEnvironment() {
+        return execAction.getEnvironment();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setEnvironment(Map<String, ?> environmentVariables) {
+        execAction.setEnvironment(environmentVariables);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T environment(String name, Object value) {
+        execAction.environment(name, value);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T environment(Map<String, ?> environmentVariables) {
+        execAction.environment(environmentVariables);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T copyTo(ProcessForkOptions target) {
+        execAction.copyTo(target);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T setStandardInput(InputStream inputStream) {
+        execAction.setStandardInput(inputStream);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream getStandardInput() {
+        return execAction.getStandardInput();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T setStandardOutput(OutputStream outputStream) {
+        execAction.setStandardOutput(outputStream);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public OutputStream getStandardOutput() {
+        return execAction.getStandardOutput();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T setErrorOutput(OutputStream outputStream) {
+        execAction.setErrorOutput(outputStream);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public OutputStream getErrorOutput() {
+        return execAction.getErrorOutput();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T setIgnoreExitValue(boolean ignoreExitValue) {
+        execAction.setIgnoreExitValue(ignoreExitValue);
+        return taskType.cast(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isIgnoreExitValue() {
+        return execAction.isIgnoreExitValue();
+    }
+
+    void setExecAction(ExecAction execAction) {
+        this.execAction = execAction;
+    }
+
+    /**
+     * Returns the result for the command run by this task. Returns {@code null} if this task has not been executed yet.
+     *
+     * @return The result. Returns {@code null} if this task has not been executed yet.
+     */
+    public ExecResult getExecResult() {
+        return execResult;
+    }
+}
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 7cd2625..562dd18 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 the original author or authors.
+ * Copyright 2014 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.gradle.api.tasks;
 import org.gradle.api.internal.IConventionAware;
 import org.gradle.api.plugins.Convention;
 
+// 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.
 /**
  * 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.
@@ -34,4 +35,4 @@ public interface ConventionValue {
      * @param conventionAwareObject The convention aware object
      */
     Object getValue(Convention convention, IConventionAware conventionAwareObject);
-}
\ No newline at end of file
+}
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 053be4d..0740d39 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
@@ -17,7 +17,6 @@
 package org.gradle.api.tasks;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.copy.CopyAction;
 import org.gradle.api.internal.file.copy.CopySpecInternal;
@@ -41,6 +40,12 @@ import java.io.File;
  * //for Ant filter
  * import org.apache.tools.ant.filters.ReplaceTokens
  *
+ * //for including in the copy task
+ * def dataContent = copySpec {
+ *     from 'src/data'
+ *     include '*.data'
+ * }
+ *
  * task initConfig(type: Copy) {
  *     from('src/main/config') {
  *         include '**/*.properties'
@@ -57,9 +62,12 @@ import java.io.File;
  *     exclude '**/*.bak'
  *
  *     includeEmptyDirs = false
+ *
+ *     with dataContent
  * }
  * </pre>
  */
+ at ParallelizableTask
 public class Copy extends AbstractCopyTask {
 
     @Override
@@ -68,13 +76,13 @@ public class Copy extends AbstractCopyTask {
         if (destinationDir == null) {
             throw new InvalidUserDataException("No copy destination directory has been specified, use 'into' to specify a target directory.");
         }
-        return new FileCopyAction(getServices().get(FileLookup.class).getFileResolver(destinationDir));
+        return new FileCopyAction(getFileLookup().getFileResolver(destinationDir));
     }
 
     @Override
     protected CopySpecInternal createRootSpec() {
-        Instantiator instantiator = getServices().get(Instantiator.class);
-        FileResolver fileResolver = getServices().get(FileResolver.class);
+        Instantiator instantiator = getInstantiator();
+        FileResolver fileResolver = getFileResolver();
 
         return instantiator.newInstance(DestinationRootCopySpec.class, fileResolver, super.createRootSpec());
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Directory.groovy b/subprojects/core/src/main/groovy/org/gradle/api/tasks/Directory.groovy
deleted file mode 100644
index b965f19..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/Directory.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.api.tasks
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.InvalidUserDataException
-import org.gradle.util.DeprecationLogger
-import org.gradle.util.GFileUtils
-
-/**
- * Creates a directory.
- * @deprecated Use {@link org.gradle.api.Project#mkdir(java.lang.Object)} to create directories in Gradle.
- */
- at Deprecated
-public class Directory extends DefaultTask {
-    File dir
-    
-    Directory() {
-        DeprecationLogger.nagUserOfReplacedTaskType("Directory", "Project.mkdir(java.lang.Object) method");
-        if (new File(name).isAbsolute()) { throw new InvalidUserDataException('Path must not be absolute.')}
-        dir = project.file(name)
-    }
-
-    @TaskAction
-    protected void mkdir() {
-        GFileUtils.mkdirs(dir)
-    }
-}
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 22f0603..ddeae84 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
@@ -15,19 +15,6 @@
  */
 package org.gradle.api.tasks;
 
-import org.gradle.api.internal.ConventionTask;
-import org.gradle.process.ExecResult;
-import org.gradle.process.ExecSpec;
-import org.gradle.process.ProcessForkOptions;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecActionFactory;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Executes a command line process. Example:
  * <pre autoTested=''>
@@ -50,239 +37,8 @@ import java.util.Map;
  * }
  * </pre>
  */
-public class Exec extends ConventionTask implements ExecSpec {
-    private ExecAction execAction;
-    private ExecResult execResult;
-
+public class Exec extends AbstractExecTask {
     public Exec() {
-        execAction = getServices().get(ExecActionFactory.class).newExecAction();
-    }
-
-    @TaskAction
-    void exec() {
-        execResult = execAction.execute();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec commandLine(Object... arguments) {
-        execAction.commandLine(arguments);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public ExecSpec commandLine(Iterable<?> args) {
-        execAction.commandLine(args);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec args(Object... args) {
-        execAction.args(args);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public ExecSpec args(Iterable<?> args) {
-        execAction.args(args);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec setArgs(Iterable<?> arguments) {
-        execAction.setArgs(arguments);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public List<String> getArgs() {
-        return execAction.getArgs();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public List<String> getCommandLine() {
-        return execAction.getCommandLine();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setCommandLine(Iterable<?> args) {
-        execAction.setCommandLine(args);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setCommandLine(Object... args) {
-        execAction.setCommandLine(args);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public String getExecutable() {
-        return execAction.getExecutable();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setExecutable(Object executable) {
-        execAction.setExecutable(executable);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec executable(Object executable) {
-        execAction.executable(executable);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public File getWorkingDir() {
-        return execAction.getWorkingDir();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setWorkingDir(Object dir) {
-        execAction.setWorkingDir(dir);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec workingDir(Object dir) {
-        execAction.workingDir(dir);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Map<String, Object> getEnvironment() {
-        return execAction.getEnvironment();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setEnvironment(Map<String, ?> environmentVariables) {
-        execAction.setEnvironment(environmentVariables);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec environment(String name, Object value) {
-        execAction.environment(name, value);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec environment(Map<String, ?> environmentVariables) {
-        execAction.environment(environmentVariables);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec copyTo(ProcessForkOptions target) {
-        execAction.copyTo(target);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec setStandardInput(InputStream inputStream) {
-        execAction.setStandardInput(inputStream);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public InputStream getStandardInput() {
-        return execAction.getStandardInput();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec setStandardOutput(OutputStream outputStream) {
-        execAction.setStandardOutput(outputStream);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public OutputStream getStandardOutput() {
-        return execAction.getStandardOutput();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Exec setErrorOutput(OutputStream outputStream) {
-        execAction.setErrorOutput(outputStream);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public OutputStream getErrorOutput() {
-        return execAction.getErrorOutput();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public ExecSpec setIgnoreExitValue(boolean ignoreExitValue) {
-        execAction.setIgnoreExitValue(ignoreExitValue);
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isIgnoreExitValue() {
-        return execAction.isIgnoreExitValue();
-    }
-
-    void setExecAction(ExecAction execAction) {
-        this.execAction = execAction;
-    }
-
-    /**
-     * Returns the result for the command run by this task. Returns {@code null} if this task has not been executed yet.
-     *
-     * @return The result. Returns {@code null} if this task has not been executed yet.
-     */
-    public ExecResult getExecResult() {
-        return execResult;
+        super(Exec.class);
     }
 }
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 06fc04c..be4145e 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
@@ -1,118 +1,122 @@
-/*
- * 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.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;
-
-/**
- * Executes a Gradle build.
- */
-public class GradleBuild extends ConventionTask {
-    private final GradleLauncherFactory gradleLauncherFactory;
-    private StartParameter startParameter;
-
-    @Inject
-    public GradleBuild(StartParameter currentBuild, GradleLauncherFactory gradleLauncherFactory) {
-        this.gradleLauncherFactory = gradleLauncherFactory;
-        this.startParameter = currentBuild.newBuild();
-        startParameter.setCurrentDir(getProject().getProjectDir());
-    }
-
-    /**
-     * Returns the full set of parameters that will be used to execute the build.
-     *
-     * @return the parameters. Never returns null.
-     */
-    public StartParameter getStartParameter() {
-        return startParameter;
-    }
-
-    /**
-     * Sets the full set of parameters that will be used to execute the build.
-     *
-     * @param startParameter the parameters. Should not be null.
-     */
-    public void setStartParameter(StartParameter startParameter) {
-        this.startParameter = startParameter;
-    }
-
-    /**
-     * Returns the project directory for the build. Defaults to the project directory.
-     *
-     * @return The project directory. Never returns null.
-     */
-    public File getDir() {
-        return getStartParameter().getCurrentDir();
-    }
-
-    /**
-     * Sets the project directory for the build.
-     *
-     * @param dir The project directory. Should not be null.
-     */
-    public void setDir(Object dir) {
-        getStartParameter().setCurrentDir(getProject().file(dir));
-    }
-
-    /**
-     * Returns the build file that should be used for this build. Defaults to {@value
-     * org.gradle.api.Project#DEFAULT_BUILD_FILE} in the project directory.
-     *
-     * @return The build file. May be null.
-     */
-    public File getBuildFile() {
-        return getStartParameter().getBuildFile();
-    }
-
-    /**
-     * Sets the build file that should be used for this build.
-     *
-     * @param file The build file. May be null to use the default build file for the build.
-     */
-    public void setBuildFile(Object file) {
-        getStartParameter().setBuildFile(getProject().file(file));
-    }
-
-    /**
-     * Returns the tasks that should be executed for this build.
-     *
-     * @return The sequence. May be empty. Never returns null.
-     */
-    public List<String> getTasks() {
-        return getStartParameter().getTaskNames();
-    }
-
-    /**
-     * Sets the tasks that should be executed for this build.
-     *
-     * @param tasks The task names. May be empty or null to use the default tasks for the build.
-     */
-    public void setTasks(Collection<String> tasks) {
-        getStartParameter().setTaskNames(tasks);
-    }
-
-    @TaskAction
-    void build() {
-        gradleLauncherFactory.newInstance(getStartParameter()).run().rethrowFailure();
-    }
-}
+/*
+ * 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.StartParameter;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.initialization.GradleLauncher;
+import org.gradle.initialization.GradleLauncherFactory;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Executes a Gradle build.
+ */
+public class GradleBuild extends ConventionTask {
+    private final GradleLauncherFactory gradleLauncherFactory;
+    private StartParameter startParameter;
+
+    public GradleBuild() {
+        this.gradleLauncherFactory = getServices().get(GradleLauncherFactory.class);
+        this.startParameter = getServices().get(StartParameter.class).newBuild();
+        startParameter.setCurrentDir(getProject().getProjectDir());
+    }
+
+    /**
+     * Returns the full set of parameters that will be used to execute the build.
+     *
+     * @return the parameters. Never returns null.
+     */
+    public StartParameter getStartParameter() {
+        return startParameter;
+    }
+
+    /**
+     * Sets the full set of parameters that will be used to execute the build.
+     *
+     * @param startParameter the parameters. Should not be null.
+     */
+    public void setStartParameter(StartParameter startParameter) {
+        this.startParameter = startParameter;
+    }
+
+    /**
+     * Returns the project directory for the build. Defaults to the project directory.
+     *
+     * @return The project directory. Never returns null.
+     */
+    public File getDir() {
+        return getStartParameter().getCurrentDir();
+    }
+
+    /**
+     * Sets the project directory for the build.
+     *
+     * @param dir The project directory. Should not be null.
+     */
+    public void setDir(Object dir) {
+        getStartParameter().setCurrentDir(getProject().file(dir));
+    }
+
+    /**
+     * Returns the build file that should be used for this build. Defaults to {@value
+     * org.gradle.api.Project#DEFAULT_BUILD_FILE} in the project directory.
+     *
+     * @return The build file. May be null.
+     */
+    public File getBuildFile() {
+        return getStartParameter().getBuildFile();
+    }
+
+    /**
+     * Sets the build file that should be used for this build.
+     *
+     * @param file The build file. May be null to use the default build file for the build.
+     */
+    public void setBuildFile(Object file) {
+        getStartParameter().setBuildFile(getProject().file(file));
+    }
+
+    /**
+     * Returns the tasks that should be executed for this build.
+     *
+     * @return The sequence. May be empty. Never returns null.
+     */
+    public List<String> getTasks() {
+        return getStartParameter().getTaskNames();
+    }
+
+    /**
+     * Sets the tasks that should be executed for this build.
+     *
+     * @param tasks The task names. May be empty or null to use the default tasks for the build.
+     */
+    public void setTasks(Collection<String> tasks) {
+        getStartParameter().setTaskNames(tasks);
+    }
+
+    @TaskAction
+    void build() {
+        GradleLauncher launcher = gradleLauncherFactory.newInstance(getStartParameter());
+        try {
+            launcher.run().rethrowFailure();
+        } finally {
+            launcher.stop();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/InputDirectory.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/InputDirectory.java
index 3130c73..0ff0ec2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/InputDirectory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/InputDirectory.java
@@ -18,8 +18,7 @@ package org.gradle.api.tasks;
 import java.lang.annotation.*;
 
 /**
- * <p>Marks a property as specifying an input directory for a task.</p> <p>This annotation should be attached to the
- * getter method or the field for the property.</p>
+ * <p>Marks a property as specifying an input directory for a task.</p>
  *
  * <p>This annotation should be attached to the getter method or the field for the property.</p>
  */
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 5b180e1..c0f4db4 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
@@ -26,6 +26,7 @@ import org.gradle.process.ProcessForkOptions;
 import org.gradle.process.internal.DefaultJavaExecAction;
 import org.gradle.process.internal.JavaExecAction;
 
+import javax.inject.Inject;
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -44,8 +45,12 @@ public class JavaExec extends ConventionTask implements JavaExecSpec {
     private JavaExecAction javaExecHandleBuilder;
 
     public JavaExec() {
-        FileResolver fileResolver = getServices().get(FileResolver.class);
-        javaExecHandleBuilder = new DefaultJavaExecAction(fileResolver);
+        javaExecHandleBuilder = new DefaultJavaExecAction(getFileResolver());
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
     }
 
     @TaskAction
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/ParallelizableTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ParallelizableTask.java
new file mode 100644
index 0000000..dd07ae7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ParallelizableTask.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares that the associated task can be safely executed in parallel with other tasks.
+ *
+ * <h3>Implementation requirements of parallelizable tasks</h3>
+ * <p>
+ * Tasks are not parallelizable by default because it is possible for tasks to interfere with each other in unsafe ways.
+ * That is, it is possible for tasks to mutate shared data structures during their execution.
+ * The presence of this annotation on task class declares that the task does not do this and is therefore parallelizable.
+ * <p>
+ * For a task to be safely parallelizable, it should not change any data that may be read by other tasks.
+ * It should not, for example, update project extensions, other tasks or any other shared data.
+ * It may change internal variables and properties of the task, the filesystem and other external resources.
+ *
+ * <h3>Inheritance</h3>
+ * <p>
+ * This annotation is not inherited.
+ * A task class that extends from another task class that declares itself to be parallel safe is not implicitly also parallel safe.
+ * If the subclass is indeed parallel safe, it must also have this annotation.
+ *
+ * <h3>Task usage and configuration requirements for parallel execution</h3>
+ *
+ * <h4>Custom actions</h4>
+ * <p>
+ * Any task that has custom actions (i.e. ones added via {@link org.gradle.api.Task#doLast(org.gradle.api.Action)} or {@link org.gradle.api.Task#doFirst(org.gradle.api.Action)})
+ * is not considered parallelizable even if its type carries this annotation.
+ * This is because it cannot be known whether the added action is parallel safe or not.
+ *
+ * <h4>File system outputs</h4>
+ * <p>
+ * Any two tasks that declare overlapping file system outputs will not be run in parallel with each other even if they carry this annotation, to prevent data corruption.
+ * In general tasks should not be configured to write to the same file or directory on the filesystem and they should only write into filesystem locations that are declared as their outputs for this reason.
+ * Two tasks that write to overlapping parts of the filesystem are only prevented from running in parallel with each other, not other parallel enabled tasks.
+ * <p>
+ * Care must be taken with symbolic links, and other types of file system links, as Gradle does not exhaustively check that outputs of tasks do not overlap.
+ * It only considers the top level declared {@link OutputFile output files} and {@link OutputDirectory output directories} of the task.
+ * <p>
+ * If a task traverses a link that is a child of a declared output in order to change or create files, the real location of the files will not be considered.
+ * However, declared outputs that are children of links <b>are</b> resolved to their real location.
+ *
+ * <h3>Prevent parallel execution</h3>
+ * <p>
+ * In order to prevent two tasks from executing in parallel, a {@link org.gradle.api.Task#mustRunAfter(Object...)} relationship can be set up between the tasks.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+ at Incubating
+public @interface ParallelizableTask {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/SourceTask.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/SourceTask.java
index df7c01d..597cc09 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/SourceTask.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/SourceTask.java
@@ -23,8 +23,6 @@ import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.util.PatternFilterable;
 import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.internal.Factory;
-import org.gradle.util.DeprecationLogger;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,32 +43,11 @@ public class SourceTask extends ConventionTask implements PatternFilterable {
     @InputFiles
     @SkipWhenEmpty
     public FileTree getSource() {
-        FileTree src;
-        if (this.source.isEmpty()) {
-            src = DeprecationLogger.whileDisabled(new Factory<FileTree>() {
-                public FileTree create() {
-                    return getDefaultSource();
-                }
-            });
-        } else {
-            src = getProject().files(new ArrayList<Object>(this.source)).getAsFileTree();
-        }
+        FileTree src = getProject().files(new ArrayList<Object>(this.source)).getAsFileTree();
         return src == null ? getProject().files().getAsFileTree() : src.matching(patternSet);
     }
 
     /**
-     * Returns the default source for this task, if any.
-     *
-     * @return The source. May return null.
-     * @deprecated Use getSource() instead.
-     */
-    @Deprecated
-    protected FileTree getDefaultSource() {
-        DeprecationLogger.nagUserOfReplacedMethod("SourceTask.getDefaultSource()", "getSource()");
-        return null;
-    }
-
-    /**
      * Sets the source for this task. The given source object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
      *
      * @param source The source.
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 42974df..33d7c5e 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
@@ -17,7 +17,6 @@
 package org.gradle.api.tasks;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.copy.*;
 import org.gradle.internal.reflect.Instantiator;
@@ -35,13 +34,13 @@ public class Sync extends AbstractCopyTask {
         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(getServices().get(FileLookup.class).getFileResolver(destinationDir)));
+        return new SyncCopyActionDecorator(destinationDir, new FileCopyAction(getFileLookup().getFileResolver(destinationDir)));
     }
 
     @Override
     protected CopySpecInternal createRootSpec() {
-        Instantiator instantiator = getServices().get(Instantiator.class);
-        FileResolver fileResolver = getServices().get(FileResolver.class);
+        Instantiator instantiator = getInstantiator();
+        FileResolver fileResolver = getFileResolver();
 
         return instantiator.newInstance(DestinationRootCopySpec.class, fileResolver, super.createRootSpec());
     }
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 f7168aa..64ee9f1 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
@@ -17,6 +17,7 @@ package org.gradle.api.tasks;
 
 import groovy.lang.Closure;
 import org.gradle.api.*;
+import org.gradle.internal.HasInternalProtocol;
 
 import java.util.Map;
 
@@ -26,6 +27,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>
  */
+ at HasInternalProtocol
 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
@@ -71,42 +73,11 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
      * <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.
-     */
-    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>
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_GROUP}</code></td><td>The group of the task.</td><td><code>null
+     * </code></td></tr>
      *
-     * <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>
+     * <tr><td><code>{@value org.gradle.api.Task#TASK_DESCRIPTION}</code></td><td>The description of the task.</td><td>
+     * <code>null</code></td></tr>
      *
      * </table>
      *
@@ -119,14 +90,12 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
      * @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;
+    Task create(Map<String, ?> options) 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
+     * control how the task is created. See {@link #create(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
@@ -140,23 +109,6 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
     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;
-
-    /**
      * <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>
      *
@@ -171,22 +123,6 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
     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;
-
-    /**
      * <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
@@ -199,20 +135,6 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
     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;
-
-    /**
      * <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
@@ -226,21 +148,6 @@ public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainOb
     <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
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskState.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskState.java
index d9e086e..64b160a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskState.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskState.java
@@ -45,7 +45,7 @@ public interface TaskState {
 
     /**
      * <p>Checks if the task actually did any work.  Even if a task executes, it may determine that it has nothing to
-     * do.  For example, the Compile task may determine that source files have not changed since the last time a the
+     * do.  For example, a compilation task may determine that source files have not changed since the last time a the
      * task was run.</p>
      *
      * @return true if this task has been executed and did any work.
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java
index c93ddb8..8f93e96 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/TaskValidationException.java
@@ -16,7 +16,7 @@
 package org.gradle.api.tasks;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
 import org.gradle.internal.exceptions.Contextual;
 
 import java.util.List;
@@ -25,7 +25,7 @@ import java.util.List;
  * A {@code TaskValidationException} is thrown when there is some validation problem with a task.
  */
 @Contextual
-public class TaskValidationException extends AbstractMultiCauseException {
+public class TaskValidationException extends DefaultMultiCauseException {
     public TaskValidationException(String message, List<InvalidUserDataException> causes) {
         super(message, causes);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/tasks/ant/AntTarget.java b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ant/AntTarget.java
index 7d44707..04c7caa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/api/tasks/ant/AntTarget.java
+++ b/subprojects/core/src/main/groovy/org/gradle/api/tasks/ant/AntTarget.java
@@ -16,46 +16,19 @@
 package org.gradle.api.tasks.ant;
 
 import org.apache.tools.ant.Target;
-import org.gradle.api.Task;
-import org.gradle.api.UnknownTaskException;
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.TaskDependency;
 
 import java.io.File;
-import java.util.Enumeration;
-import java.util.LinkedHashSet;
-import java.util.Set;
 
 /**
  * A task which executes an Ant target.
  */
 public class AntTarget extends ConventionTask {
+
     private Target target;
     private File baseDir;
 
-    public AntTarget() {
-        dependsOn(new TaskDependency() {
-            public Set<? extends Task> getDependencies(Task task) {
-                return getAntTargetDependencies();
-            }
-        });
-    }
-
-    private Set<Task> getAntTargetDependencies() {
-        Set<Task> tasks = new LinkedHashSet<Task>();
-        Enumeration dependencies = target.getDependencies();
-        while (dependencies.hasMoreElements()) {
-            String name = (String) dependencies.nextElement();
-            Task dependency = getProject().getTasks().findByName(name);
-            if (dependency == null) {
-                throw new UnknownTaskException(String.format("Imported Ant target '%s' depends on target or task '%s' which does not exist", getName(), name));
-            }
-            tasks.add(dependency);
-        }
-        return tasks;
-    }
-
     @TaskAction
     protected void executeAntTarget() {
         File oldBaseDir = target.getProject().getBaseDir();
@@ -112,4 +85,5 @@ public class AntTarget extends ConventionTask {
             target.setDescription(description);
         }
     }
+
 }
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 3058b41..e58b95c 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
@@ -63,7 +63,7 @@ public abstract class AbstractArchiveTask extends AbstractCopyTask {
     private String maybe(String prefix, String value) {
         if (GUtil.isTrue(value)) {
             if (GUtil.isTrue(prefix)) {
-                return String.format("-%s", value);
+                return "-".concat(value);
             } else {
                 return value;
             }
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 863fe28..1caab9e 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
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.tasks.bundling;
 
-import org.gradle.util.DeprecationLogger;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -38,15 +36,6 @@ public enum Compression {
         this.supportedExtensions.add(defaultExtension);
     }
 
-    /**
-     * <p>Returns the file extension of the type of Compression.</p>
-     * @deprecated Use {@link #getDefaultExtension()} instead.
-     */
-    public String getExtension() {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Compression.getExtension()");
-        return defaultExtension;
-    }
-
     public String getDefaultExtension(){
         return defaultExtension;
     }
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
index fc97522..dc072fa 100644
--- 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
@@ -118,5 +118,4 @@ public interface IncrementalTaskInputs {
      * @throws IllegalStateException if invoked prior to {@link #outOfDate}, or if invoked more than once.
      */
     void removed(Action<? super InputFileDetails> removedAction);
-
-}
+}
\ 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 fe84ee8..7ea7507 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,15 +25,17 @@ 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.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.CharSequenceNotationParser;
 import org.gradle.api.specs.*;
 import org.gradle.api.tasks.AntBuilderAware;
 import org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
 import org.gradle.util.CollectionUtils;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Standalone implementation of {@link PatternFilterable}.
@@ -44,6 +46,7 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
     private final Set<String> excludes = Sets.newLinkedHashSet();
     private final Set<Spec<FileTreeElement>> includeSpecs = Sets.newLinkedHashSet();
     private final Set<Spec<FileTreeElement>> excludeSpecs = Sets.newLinkedHashSet();
+    private static final NotationParser<Object, String> PARSER = NotationParserBuilder.toType(String.class).fromCharSequence().toComposite();
 
     boolean caseSensitive = true;
 
@@ -104,7 +107,7 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
         }
 
         public Spec<FileTreeElement> getAsSpec() {
-            return new AndSpec<FileTreeElement>(super.getAsSpec(), other.getAsSpec());
+            return Specs.and(super.getAsSpec(), other.getAsSpec());
         }
 
         public Object addToAntBuilder(Object node, String childNodeName) {
@@ -118,7 +121,7 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
     }
 
     public Spec<FileTreeElement> getAsSpec() {
-        return new AndSpec<FileTreeElement>(getAsIncludeSpec(), new NotSpec<FileTreeElement>(getAsExcludeSpec()));
+        return Specs.and(getAsIncludeSpec(), Specs.not(getAsExcludeSpec()));
     }
 
     public Spec<FileTreeElement> getAsIncludeSpec() {
@@ -129,7 +132,7 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
         }
 
         matchers.addAll(includeSpecs);
-        return new OrSpec<FileTreeElement>(matchers);
+        return Specs.or(matchers);
     }
 
     public Spec<FileTreeElement> getAsExcludeSpec() {
@@ -143,7 +146,7 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
         }
 
         matchers.addAll(excludeSpecs);
-        return new OrSpec<FileTreeElement>(matchers);
+        return Specs.or(false, matchers);
     }
 
     public Set<String> getIncludes() {
@@ -164,12 +167,8 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
     }
 
     public PatternSet include(Iterable includes) {
-        NotationParser<Object, String> parser = new NotationParserBuilder<String>()
-                .resultingType(String.class)
-                .parser(new CharSequenceNotationParser())
-                .toComposite();
         for (Object include : includes) {
-            this.includes.add(parser.parseNotation(include));
+            this.includes.add(PARSER.parseNotation(include));
         }
         return this;
     }
@@ -220,9 +219,8 @@ public class PatternSet implements AntBuilderAware, PatternFilterable {
     }
 
     public PatternSet exclude(Iterable excludes) {
-        CharSequenceNotationParser parser = new CharSequenceNotationParser();
         for (Object exclude : excludes) {
-            this.excludes.add(parser.parseNotation(exclude));
+            this.excludes.add(PARSER.parseNotation(exclude));
         }
         return this;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/PersistentIndexedCacheParameters.java b/subprojects/core/src/main/groovy/org/gradle/cache/PersistentIndexedCacheParameters.java
index 9384225..879bd1e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/PersistentIndexedCacheParameters.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/PersistentIndexedCacheParameters.java
@@ -17,8 +17,8 @@ package org.gradle.cache;
 
 import org.gradle.api.Nullable;
 import org.gradle.cache.internal.CacheDecorator;
-import org.gradle.messaging.serialize.BaseSerializerFactory;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.BaseSerializerFactory;
+import org.gradle.internal.serialize.Serializer;
 
 public class PersistentIndexedCacheParameters<K, V> {
     private static final BaseSerializerFactory SERIALIZER_FACTORY = new BaseSerializerFactory();
@@ -33,6 +33,10 @@ public class PersistentIndexedCacheParameters<K, V> {
         this.valueSerializer = valueSerializer;
     }
 
+    public static <K, V> PersistentIndexedCacheParameters<K, V> of(String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
+        return new PersistentIndexedCacheParameters<K, V>(cacheName, keySerializer, valueSerializer);
+    }
+
     public PersistentIndexedCacheParameters(String cacheName, Class<K> keyType, Serializer<V> valueSerializer) {
         this(cacheName, SERIALIZER_FACTORY.getSerializerFor(keyType), valueSerializer);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/PersistentStore.java b/subprojects/core/src/main/groovy/org/gradle/cache/PersistentStore.java
index d275b36..d43ad54 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/PersistentStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/PersistentStore.java
@@ -16,13 +16,11 @@
 
 package org.gradle.cache;
 
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 
 /**
  * Represents some persistent store.
  *
- * <p>Use can use</p>
- *
  * <p>You can use {@link #useCache(String, org.gradle.internal.Factory)} to perform some action on the store while holding an exclusive
  * lock on the store.</p>
  */
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/CacheFactory.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/CacheFactory.java
index 8eb4f7b..dc57ae6 100755
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/CacheFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/CacheFactory.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.cache.CacheOpenException;
 import org.gradle.cache.CacheValidator;
@@ -28,5 +27,5 @@ import java.util.Map;
 public interface CacheFactory {
     PersistentCache openStore(File storeDir, String displayName, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException;
 
-    PersistentCache open(File cacheDir, String displayName, CacheUsage usage, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException;
+    PersistentCache open(File cacheDir, String displayName, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException;
 }
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 b03633a..caedd89 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
@@ -27,7 +27,7 @@ import org.gradle.internal.Factories;
 import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.CompositeStoppable;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 
 import java.io.File;
 import java.util.HashSet;
@@ -180,10 +180,15 @@ public class DefaultCacheAccess implements CacheCoordinator {
             throw new UnsupportedOperationException("Not implemented yet.");
         }
 
-        takeOwnership(operationDisplayName);
         boolean wasStarted = false;
+        lock.lock();
         try {
+            takeOwnership(operationDisplayName);
             wasStarted = onStartWork();
+        } finally {
+            lock.unlock();
+        }
+        try {
             return factory.create();
         } finally {
             lock.lock();
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheFactory.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheFactory.java
index 54f486d..5c9cb3f 100755
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheFactory.java
@@ -15,13 +15,12 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.cache.*;
 import org.gradle.cache.internal.filelock.LockOptions;
 import org.gradle.internal.Factory;
 import org.gradle.internal.concurrent.CompositeStoppable;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 import org.gradle.util.GFileUtils;
 
 import java.io.Closeable;
@@ -30,7 +29,7 @@ import java.util.*;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-public class DefaultCacheFactory implements CacheFactory {
+public class DefaultCacheFactory implements CacheFactory, Closeable {
     private final Map<File, DirCacheReference> dirCaches = new HashMap<File, DirCacheReference>();
     private final FileLockManager lockManager;
     private final Lock lock = new ReentrantLock();
@@ -45,10 +44,10 @@ public class DefaultCacheFactory implements CacheFactory {
     void onClose(Object cache) {
     }
 
-    public PersistentCache open(File cacheDir, String displayName, CacheUsage usage, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException {
+    public PersistentCache open(File cacheDir, String displayName, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException {
         lock.lock();
         try {
-            return doOpen(cacheDir, displayName, usage, cacheValidator, properties, lockOptions, initializer);
+            return doOpen(cacheDir, displayName, cacheValidator, properties, lockOptions, initializer);
         } finally {
             lock.unlock();
         }
@@ -73,18 +72,15 @@ public class DefaultCacheFactory implements CacheFactory {
         }
     }
 
-    private PersistentCache doOpen(File cacheDir, String displayName, CacheUsage usage, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> action) {
+    private PersistentCache doOpen(File cacheDir, String displayName, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> action) {
         File canonicalDir = GFileUtils.canonicalise(cacheDir);
         DirCacheReference dirCacheReference = dirCaches.get(canonicalDir);
         if (dirCacheReference == null) {
-            ReferencablePersistentCache cache = new DefaultPersistentDirectoryCache(canonicalDir, displayName, usage, validator, properties, lockOptions, action, lockManager);
+            ReferencablePersistentCache cache = new DefaultPersistentDirectoryCache(canonicalDir, displayName, validator, properties, lockOptions, action, lockManager);
             cache.open();
-            dirCacheReference = new DirCacheReference(cache, properties, lockOptions, usage == CacheUsage.REBUILD);
+            dirCacheReference = new DirCacheReference(cache, properties, lockOptions);
             dirCaches.put(canonicalDir, dirCacheReference);
         } else {
-            if (usage == CacheUsage.REBUILD && !dirCacheReference.rebuild) {
-                throw new IllegalStateException(String.format("Cannot rebuild cache '%s' as it is already open.", cacheDir));
-            }
             if (!lockOptions.equals(dirCacheReference.lockOptions)) {
                 throw new IllegalStateException(String.format("Cache '%s' is already open with different options.", cacheDir));
             }
@@ -104,7 +100,7 @@ public class DefaultCacheFactory implements CacheFactory {
         if (dirCacheReference == null) {
             ReferencablePersistentCache cache = new DefaultPersistentDirectoryStore(canonicalDir, displayName, lockOptions, lockManager);
             cache.open();
-            dirCacheReference = new DirCacheReference(cache, Collections.<String, Object>emptyMap(), lockOptions, false);
+            dirCacheReference = new DirCacheReference(cache, Collections.<String, Object>emptyMap(), lockOptions);
             dirCaches.put(canonicalDir, dirCacheReference);
         }
         return new ReferenceTrackingCache(dirCacheReference);
@@ -115,13 +111,11 @@ public class DefaultCacheFactory implements CacheFactory {
         private final LockOptions lockOptions;
         private final ReferencablePersistentCache cache;
         private final Set<ReferenceTrackingCache> references = new HashSet<ReferenceTrackingCache>();
-        private final boolean rebuild;
 
-        public DirCacheReference(ReferencablePersistentCache cache, Map<String, ?> properties, LockOptions lockOptions, boolean rebuild) {
+        public DirCacheReference(ReferencablePersistentCache cache, Map<String, ?> properties, LockOptions lockOptions) {
             this.cache = cache;
             this.properties = properties;
             this.lockOptions = lockOptions;
-            this.rebuild = rebuild;
             onOpen(cache);
         }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheRepository.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheRepository.java
index a99d2f8..804e3c8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheRepository.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultCacheRepository.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.cache.CacheBuilder;
 import org.gradle.cache.CacheRepository;
@@ -31,14 +30,12 @@ import static org.gradle.cache.internal.FileLockManager.LockMode;
 import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
 
 public class DefaultCacheRepository implements CacheRepository {
-    private final CacheUsage cacheUsage;
     private final CacheScopeMapping cacheScopeMapping;
     private final CacheFactory factory;
 
-    public DefaultCacheRepository(CacheScopeMapping cacheScopeMapping, CacheUsage cacheUsage, CacheFactory factory) {
+    public DefaultCacheRepository(CacheScopeMapping cacheScopeMapping, CacheFactory factory) {
         this.cacheScopeMapping = cacheScopeMapping;
         this.factory = factory;
-        this.cacheUsage = cacheUsage;
     }
 
     public CacheBuilder store(String key) {
@@ -138,7 +135,7 @@ public class DefaultCacheRepository implements CacheRepository {
 
         @Override
         protected PersistentCache doOpen(File cacheDir, Map<String, ?> properties, CacheValidator validator) {
-            return factory.open(cacheDir, displayName, cacheUsage, validator, properties, lockOptions, initializer);
+            return factory.open(cacheDir, displayName, validator, properties, lockOptions, initializer);
         }
     }
 
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 a5991e6..9ed6c2f 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
@@ -322,8 +322,7 @@ public class DefaultFileLockManager implements FileLockManager {
                         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.
+                //TODO SF we should inform on the progress/status bar that we're waiting
                 Thread.sleep(200L);
             } while (System.currentTimeMillis() < waitUntil);
             return null;
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCache.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCache.java
index 4ba8658..ce70555 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCache.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.cache.CacheValidator;
 import org.gradle.cache.PersistentCache;
@@ -33,15 +32,13 @@ public class DefaultPersistentDirectoryCache extends DefaultPersistentDirectoryS
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPersistentDirectoryCache.class);
     private final File propertiesFile;
     private final Properties properties = new Properties();
-    private final CacheUsage cacheUsage;
     private final Action<? super PersistentCache> initAction;
     private final CacheValidator validator;
     private boolean didRebuild;
 
-    public DefaultPersistentDirectoryCache(File dir, String displayName, CacheUsage cacheUsage, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initAction, FileLockManager lockManager) {
+    public DefaultPersistentDirectoryCache(File dir, String displayName, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initAction, FileLockManager lockManager) {
         super(dir, displayName, lockOptions, lockManager);
         this.validator = validator;
-        this.cacheUsage = cacheUsage;
         this.initAction = initAction;
         propertiesFile = new File(dir, "cache.properties");
         this.properties.putAll(properties);
@@ -65,10 +62,6 @@ public class DefaultPersistentDirectoryCache extends DefaultPersistentDirectoryS
     private class Initializer implements CacheInitializationAction {
         public boolean requiresInitialization(FileLock lock) {
             if (!didRebuild) {
-                if (cacheUsage == CacheUsage.REBUILD) {
-                    LOGGER.debug("Invalidating {} as cache usage is set to rebuild.", this);
-                    return true;
-                }
                 if (validator!=null && !validator.isValid()) {
                     LOGGER.debug("Invalidating {} as cache validator return false.", this);
                     return true;
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
index 58bf79a..eb631b4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
@@ -20,7 +20,7 @@ import org.gradle.cache.PersistentIndexedCache;
 import org.gradle.cache.PersistentIndexedCacheParameters;
 import org.gradle.cache.internal.filelock.LockOptions;
 import org.gradle.internal.Factory;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProvider.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProvider.java
index c1a5b1f..169ec1b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProvider.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProvider.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.internal.nativeplatform.*;
+import org.gradle.internal.nativeintegration.*;
 
 public class DefaultProcessMetaDataProvider implements ProcessMetaDataProvider {
     private final ProcessEnvironment environment;
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
index 2544d7a..bb7fc09 100644
--- a/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockCommunicator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/FileLockCommunicator.java
@@ -35,7 +35,7 @@ public class FileLockCommunicator {
     public FileLockCommunicator(InetAddressFactory addressFactory) {
         this.addressFactory = addressFactory;
         try {
-            socket = new DatagramSocket();
+            socket = new DatagramSocket(0, addressFactory.findLocalBindingAddress());
         } catch (SocketException e) {
             throw throwAsUncheckedException(e);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/cache/internal/NonThreadsafeInMemoryStore.java b/subprojects/core/src/main/groovy/org/gradle/cache/internal/NonThreadsafeInMemoryStore.java
new file mode 100644
index 0000000..c1de36d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/cache/internal/NonThreadsafeInMemoryStore.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 com.google.common.collect.Maps;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.cache.PersistentStore;
+import org.gradle.internal.Factory;
+import org.gradle.internal.serialize.Serializer;
+
+import java.util.Map;
+
+public class NonThreadsafeInMemoryStore implements PersistentStore {
+
+    @Override
+    public <K, V> PersistentIndexedCache<K, V> createCache(String name, Class<K> keyType, Serializer<V> valueSerializer) {
+        return new CacheImpl<K, V>();
+    }
+
+    @Override
+    public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
+        return action.create();
+    }
+
+    @Override
+    public void useCache(String operationDisplayName, Runnable action) {
+        action.run();
+    }
+
+    @Override
+    public <T> T longRunningOperation(String operationDisplayName, Factory<? extends T> action) {
+        return action.create();
+    }
+
+    @Override
+    public void longRunningOperation(String operationDisplayName, Runnable action) {
+        action.run();
+    }
+
+    private static class CacheImpl<K, V> implements PersistentIndexedCache<K, V> {
+
+        Map<K, V> entries = Maps.newHashMap();
+
+        @Override
+        public V get(K key) {
+            return entries.get(key);
+        }
+
+        @Override
+        public void put(K key, V value) {
+            entries.put(key, value);
+        }
+
+        @Override
+        public void remove(K key) {
+            entries.remove(key);
+
+        }
+    }
+}
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 370b61e..67cff70 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,9 +19,9 @@ 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 org.gradle.internal.serialize.InputStreamBackedDecoder;
+import org.gradle.internal.serialize.OutputStreamBackedEncoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.io.*;
 
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 aef8f06..002ab05 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,9 +17,9 @@ package org.gradle.cache.internal.btree;
 
 import org.gradle.api.UncheckedIOException;
 import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.messaging.serialize.Serializer;
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
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
index fb612db..083d05c 100644
--- 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
@@ -21,6 +21,7 @@ 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.Stoppable;
 import org.gradle.internal.concurrent.StoppableExecutor;
 import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
 
@@ -29,7 +30,7 @@ import java.util.Map;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-public class DefaultFileLockContentionHandler implements FileLockContentionHandler {
+public class DefaultFileLockContentionHandler implements FileLockContentionHandler, Stoppable {
     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>();
@@ -125,10 +126,6 @@ public class DefaultFileLockContentionHandler implements FileLockContentionHandl
     }
 
     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;
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 d14c7eb..7e381e0 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
@@ -16,27 +16,30 @@
 package org.gradle.configuration;
 
 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.execution.ProjectConfigurer;
 import org.gradle.util.SingleMessageLogger;
 
 public class DefaultBuildConfigurer implements BuildConfigurer {
+    private final ProjectConfigurer projectConfigurer;
+
+    public DefaultBuildConfigurer(ProjectConfigurer projectConfigurer) {
+        this.projectConfigurer = projectConfigurer;
+    }
+
     public void configure(GradleInternal gradle) {
         maybeInformAboutIncubatingMode(gradle.getStartParameter());
         if (gradle.getStartParameter().isConfigureOnDemand()) {
-            gradle.getRootProject().evaluate();
+            projectConfigurer.configure(gradle.getRootProject());
         } else {
-            for (Project project : gradle.getRootProject().getAllprojects()) {
-                ((ProjectInternal) project).evaluate();
-            }
+            projectConfigurer.configureHierarchy(gradle.getRootProject());
         }
     }
 
     private void maybeInformAboutIncubatingMode(StartParameter startParameter) {
-        if (startParameter.getParallelThreadCount() != 0 && startParameter.isConfigureOnDemand()) {
+        if (startParameter.isParallelProjectExecutionEnabled() && startParameter.isConfigureOnDemand()) {
             SingleMessageLogger.incubatingFeatureUsed("Parallel execution with configuration on demand");
-        } else if (startParameter.getParallelThreadCount() != 0) {
+        } else if (startParameter.isParallelProjectExecutionEnabled()) {
             SingleMessageLogger.incubatingFeatureUsed("Parallel execution");
         } else if (startParameter.isConfigureOnDemand()) {
             SingleMessageLogger.incubatingFeatureUsed("Configuration on demand");
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultImportsReader.java b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultImportsReader.java
new file mode 100644
index 0000000..02c94a1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultImportsReader.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.io.LineProcessor;
+import com.google.common.io.Resources;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+public class DefaultImportsReader implements ImportsReader {
+
+    private static final String RESOURCE = "/default-imports.txt";
+    private final String[] importPackages;
+
+    public DefaultImportsReader() {
+        try {
+            URL url = getClass().getResource(RESOURCE);
+            if (url == null) {
+                throw new IllegalStateException("Could not load default imports resource: " + RESOURCE);
+            }
+            this.importPackages = Resources.asCharSource(url, Charsets.UTF_8).readLines(new LineProcessor<String[]>() {
+                private final List<String> packages = Lists.newLinkedList();
+
+                @Override
+                public boolean processLine(@SuppressWarnings("NullableProblems") String line) throws IOException {
+                    packages.add(line.substring(7, line.length() - 2));
+                    return true;
+                }
+
+                @Override
+                public String[] getResult() {
+                    return packages.toArray(new String[packages.size()]);
+                }
+            });
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public String[] getImportPackages() {
+        return importPackages;
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultInitScriptProcessor.java b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultInitScriptProcessor.java
index 88bd935..cb2360d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultInitScriptProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultInitScriptProcessor.java
@@ -21,6 +21,10 @@ import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.initialization.InitScript;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.internal.id.LongIdGenerator;
+
+import java.net.URI;
 
 /**
  * Processes (and runs) an init script for a specified build.  Handles defining
@@ -29,6 +33,7 @@ import org.gradle.initialization.InitScript;
 public class DefaultInitScriptProcessor implements InitScriptProcessor {
     private final ScriptPluginFactory configurerFactory;
     private final ScriptHandlerFactory scriptHandlerFactory;
+    private final IdGenerator<Long> idGenerator = new LongIdGenerator();
 
     public DefaultInitScriptProcessor(ScriptPluginFactory configurerFactory, ScriptHandlerFactory scriptHandlerFactory) {
         this.configurerFactory = configurerFactory;
@@ -36,9 +41,12 @@ public class DefaultInitScriptProcessor implements InitScriptProcessor {
     }
 
     public void process(final ScriptSource initScript, GradleInternal gradle) {
-        ClassLoaderScope classLoaderScope = gradle.getClassLoaderScope().createSibling();
-        ScriptHandler scriptHandler = scriptHandlerFactory.create(initScript, classLoaderScope);
-        ScriptPlugin configurer = configurerFactory.create(initScript, scriptHandler, classLoaderScope, "initscript", InitScript.class);
+        ClassLoaderScope baseScope = gradle.getClassLoaderScope();
+        URI uri = initScript.getResource().getURI();
+        String id = uri == null ? idGenerator.generateId().toString() : uri.toString();
+        ClassLoaderScope scriptScope = baseScope.createChild("init-" + id);
+        ScriptHandler scriptHandler = scriptHandlerFactory.create(initScript, scriptScope);
+        ScriptPlugin configurer = configurerFactory.create(initScript, scriptHandler, scriptScope, baseScope, "initscript", InitScript.class, false);
         configurer.apply(gradle);
     }
 }
\ 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 6fe42ac..6b66328 100755
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
@@ -16,74 +16,80 @@
 
 package org.gradle.configuration;
 
-import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
-import org.gradle.api.plugins.PluginAware;
+import org.gradle.api.internal.plugins.PluginAwareInternal;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectScript;
 import org.gradle.groovy.scripts.*;
-import org.gradle.groovy.scripts.internal.BuildScriptTransformer;
-import org.gradle.groovy.scripts.internal.PluginsAndBuildscriptTransformer;
-import org.gradle.groovy.scripts.internal.StatementExtractingScriptTransformer;
+import org.gradle.groovy.scripts.internal.*;
+import org.gradle.internal.Actions;
 import org.gradle.internal.Factory;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.serialize.BaseSerializerFactory;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.plugin.PluginHandler;
-import org.gradle.plugin.internal.*;
-import org.gradle.plugin.resolve.internal.PluginRequest;
-import org.gradle.plugin.resolve.internal.PluginResolver;
-
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import org.gradle.model.dsl.internal.transform.ClosureCreationInterceptingVerifier;
+import org.gradle.model.dsl.internal.transform.ModelBlockTransformer;
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector;
+import org.gradle.plugin.use.internal.PluginRequestApplicator;
+import org.gradle.plugin.use.internal.PluginRequests;
+import org.gradle.plugin.use.internal.PluginRequestsSerializer;
 
 public class DefaultScriptPluginFactory implements ScriptPluginFactory {
+
     private final ScriptCompilerFactory scriptCompilerFactory;
-    private final ImportsReader importsReader;
     private final Factory<LoggingManagerInternal> loggingManagerFactory;
     private final Instantiator instantiator;
     private final ScriptHandlerFactory scriptHandlerFactory;
-    private final PluginResolverFactory pluginResolverFactory;
+    private final PluginRequestApplicator pluginRequestApplicator;
     private final FileLookup fileLookup;
+    private final DocumentationRegistry documentationRegistry;
+    private final ModelRuleSourceDetector modelRuleSourceDetector;
 
     public DefaultScriptPluginFactory(ScriptCompilerFactory scriptCompilerFactory,
-                                      ImportsReader importsReader,
                                       Factory<LoggingManagerInternal> loggingManagerFactory,
                                       Instantiator instantiator,
                                       ScriptHandlerFactory scriptHandlerFactory,
-                                      PluginResolverFactory pluginResolverFactory,
-                                      FileLookup fileLookup) {
+                                      PluginRequestApplicator pluginRequestApplicator,
+                                      FileLookup fileLookup,
+                                      DocumentationRegistry documentationRegistry,
+                                      ModelRuleSourceDetector modelRuleSourceDetector) {
         this.scriptCompilerFactory = scriptCompilerFactory;
-        this.importsReader = importsReader;
         this.loggingManagerFactory = loggingManagerFactory;
         this.instantiator = instantiator;
         this.scriptHandlerFactory = scriptHandlerFactory;
-        this.pluginResolverFactory = pluginResolverFactory;
+        this.pluginRequestApplicator = pluginRequestApplicator;
         this.fileLookup = fileLookup;
+        this.documentationRegistry = documentationRegistry;
+        this.modelRuleSourceDetector = modelRuleSourceDetector;
     }
 
-    public ScriptPlugin create(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope classLoaderScope, String classpathClosureName, Class<? extends BasicScript> scriptClass) {
-        return new ScriptPluginImpl(scriptSource, scriptHandler, classLoaderScope, classpathClosureName, scriptClass);
+    public ScriptPlugin create(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope targetScope, ClassLoaderScope baseScope, String classpathClosureName, Class<? extends BasicScript> scriptClass, boolean ownerScript) {
+        return new ScriptPluginImpl(scriptSource, scriptHandler, targetScope, baseScope, classpathClosureName, scriptClass, ownerScript);
     }
 
     private class ScriptPluginImpl implements ScriptPlugin {
         private final ScriptSource scriptSource;
-        private final ClassLoaderScope classLoaderScope;
+        private final ClassLoaderScope targetScope;
+        private final ClassLoaderScope baseScope;
         private final String classpathClosureName;
         private final Class<? extends BasicScript> scriptType;
         private final ScriptHandler scriptHandler;
+        private final boolean ownerScript;
 
-        public ScriptPluginImpl(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope classLoaderScope, String classpathClosureName, Class<? extends BasicScript> scriptType) {
+        public ScriptPluginImpl(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope targetScope, ClassLoaderScope baseScope, String classpathClosureName, Class<? extends BasicScript> scriptType, boolean ownerScript) {
             this.scriptSource = scriptSource;
-            this.classLoaderScope = classLoaderScope;
+            this.targetScope = targetScope;
+            this.baseScope = baseScope;
             this.classpathClosureName = classpathClosureName;
             this.scriptHandler = scriptHandler;
             this.scriptType = scriptType;
+            this.ownerScript = ownerScript;
         }
 
 
@@ -92,65 +98,63 @@ public class DefaultScriptPluginFactory implements ScriptPluginFactory {
         }
 
         public void apply(final Object target) {
-            DefaultServiceRegistry services = new DefaultServiceRegistry();
+            final DefaultServiceRegistry services = new DefaultServiceRegistry();
             services.add(ScriptPluginFactory.class, DefaultScriptPluginFactory.this);
             services.add(ScriptHandlerFactory.class, scriptHandlerFactory);
-            services.add(ClassLoaderScope.class, classLoaderScope);
+            services.add(ClassLoaderScope.class, targetScope);
             services.add(LoggingManagerInternal.class, loggingManagerFactory.create());
             services.add(Instantiator.class, instantiator);
             services.add(ScriptHandler.class, scriptHandler);
             services.add(FileLookup.class, fileLookup);
+            services.add(ModelRuleSourceDetector.class, modelRuleSourceDetector);
 
-            ScriptSource withImports = importsReader.withImports(scriptSource);
-
-            List<PluginRequest> pluginRequests = new LinkedList<PluginRequest>();
-            if (target instanceof PluginAware) {
-                services.add(PluginHandler.class, new DefaultPluginHandler(pluginRequests));
-            } else {
-                services.add(PluginHandler.class, new NonPluggableTargetPluginHandler(target));
-            }
-
-            ScriptCompiler compiler = scriptCompilerFactory.createCompiler(withImports);
-            compiler.setClassloader(classLoaderScope.getBase().getChildClassLoader());
+            final ScriptCompiler compiler = scriptCompilerFactory.createCompiler(scriptSource);
 
-            PluginsAndBuildscriptTransformer scriptBlockTransformer = new PluginsAndBuildscriptTransformer(classpathClosureName);
+            // Pass 1, extract plugin requests and execute buildscript {}, ignoring (i.e. not even compiling) anything else
 
-            StatementExtractingScriptTransformer classpathScriptTransformer = new StatementExtractingScriptTransformer(classpathClosureName, scriptBlockTransformer);
+            boolean supportsPluginsBlock = ProjectScript.class.isAssignableFrom(scriptType);
+            String onPluginBlockError = supportsPluginsBlock ? null : "Only Project build scripts can contain plugins {} blocks";
 
-            compiler.setTransformer(classpathScriptTransformer);
+            InitialPassStatementTransformer initialPassStatementTransformer = new InitialPassStatementTransformer(classpathClosureName, onPluginBlockError, scriptSource, documentationRegistry);
+            SubsetScriptTransformer initialTransformer = new SubsetScriptTransformer(initialPassStatementTransformer);
+            CompileOperation<PluginRequests> initialOperation = new FactoryBackedCompileOperation<PluginRequests>(classpathClosureName, initialTransformer, initialPassStatementTransformer, PluginRequestsSerializer.INSTANCE);
 
-            ScriptRunner<? extends BasicScript> classPathScriptRunner = compiler.compile(scriptType);
-            classPathScriptRunner.getScript().init(target, services);
-            classPathScriptRunner.run();
+            ScriptRunner<? extends BasicScript, PluginRequests> initialRunner = compiler.compile(scriptType, initialOperation, baseScope.getExportClassLoader(), classpathClosureName, Actions.doNothing());
+            initialRunner.getScript().init(target, services);
+            initialRunner.run();
 
-            Configuration classpathConfiguration = scriptHandler.getConfigurations().getByName(ScriptHandler.CLASSPATH_CONFIGURATION);
-            Set<File> files = classpathConfiguration.getFiles();
-            ClassPath classPath = new DefaultClassPath(files);
+            PluginRequests pluginRequests = initialRunner.getCompiledScript().getData();
+            PluginManagerInternal pluginManager = target instanceof PluginAwareInternal ? ((PluginAwareInternal) target).getPluginManager() : null;
+            pluginRequestApplicator.applyPlugins(pluginRequests, scriptHandler, pluginManager, targetScope);
 
-            ClassLoader exportedClassLoader = classLoaderScope.export(classPath);
+            // Pass 2, compile everything except buildscript {} and plugin requests, then run
 
-            if (!pluginRequests.isEmpty()) {
-                PluginResolver pluginResolver = pluginResolverFactory.createPluginResolver(exportedClassLoader);
-                @SuppressWarnings("ConstantConditions")
-                PluginResolutionApplicator resolutionApplicator = new PluginResolutionApplicator((PluginAware) target, classLoaderScope);
-                PluginRequestApplicator requestApplicator = new PluginRequestApplicator(pluginResolver, resolutionApplicator);
-                requestApplicator.applyPlugin(pluginRequests);
+            BuildScriptTransformer buildScriptTransformer = new BuildScriptTransformer(classpathClosureName, scriptSource);
+            String operationId = "no_" + classpathClosureName;
+            if (ModelBlockTransformer.isEnabled()) {
+                operationId = "m_".concat(operationId);
             }
-
-            classLoaderScope.lock();
-
-            compiler.setClassloader(classLoaderScope.getScopeClassLoader());
-
-            compiler.setTransformer(new BuildScriptTransformer("no_" + classpathScriptTransformer.getId(), classpathScriptTransformer.invert()));
-            ScriptRunner<? extends BasicScript> runner = compiler.compile(scriptType);
-
-            BasicScript script = runner.getScript();
-            script.init(target, services);
-            if (target instanceof ScriptAware) {
-                ((ScriptAware) target).setScript(script);
+            CompileOperation<Boolean> operation = new FactoryBackedCompileOperation<Boolean>(operationId, buildScriptTransformer, buildScriptTransformer, BaseSerializerFactory.BOOLEAN_SERIALIZER);
+
+            final ScriptRunner<? extends BasicScript, Boolean> runner = compiler.compile(scriptType, operation, targetScope.getLocalClassLoader(), classpathClosureName, ClosureCreationInterceptingVerifier.INSTANCE);
+
+            Runnable buildScriptRunner = new Runnable() {
+                public void run() {
+                    BasicScript script = runner.getScript();
+                    script.init(target, services);
+                    if (ownerScript && target instanceof ScriptAware) {
+                        ((ScriptAware) target).setScript(script);
+                    }
+                    runner.run();
+                }
+            };
+
+            Boolean hasImperativeStatements = runner.getCompiledScript().getData();
+            if (!hasImperativeStatements && target instanceof ProjectInternal) {
+                ((ProjectInternal) target).addDeferredConfiguration(buildScriptRunner);
+            } else {
+                buildScriptRunner.run();
             }
-            runner.run();
         }
-
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
deleted file mode 100644
index 600d78f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.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.configuration;
-
-public class ImplicitTasksConfigurer {
-    public static final String HELP_GROUP = "help";
-    public static final String HELP_TASK = "help";
-    public static final String PROJECTS_TASK = "projects";
-    public static final String TASKS_TASK = "tasks";
-    public static final String PROPERTIES_TASK = "properties";
-    public static final String DEPENDENCIES_TASK = "dependencies";
-    public static final String DEPENDENCY_INSIGHT_TASK = "dependencyInsight";
-}
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 b01f074..99a0fc5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2015 the original author 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,46 +16,6 @@
 
 package org.gradle.configuration;
 
-import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.internal.UncheckedException;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-
-public class ImportsReader {
-
-    private String importsText;
-
-    public String getImports() {
-        if (importsText == null) {
-            try {
-                URL url = getClass().getResource("/default-imports.txt");
-                InputStreamReader reader = new InputStreamReader(url.openStream(), "UTF8");
-                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();
-                }
-            } catch (IOException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        return importsText;
-    }
-
-    public ScriptSource withImports(ScriptSource source) {
-        return new ImportsScriptSource(source, this);
-    }
+public interface ImportsReader {
+    String[] getImportPackages();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsScriptSource.java
deleted file mode 100644
index b5a1f1e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ImportsScriptSource.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.configuration;
-
-import org.gradle.api.internal.resource.DelegatingResource;
-import org.gradle.api.internal.resource.Resource;
-import org.gradle.groovy.scripts.DelegatingScriptSource;
-import org.gradle.groovy.scripts.ScriptSource;
-
-public class ImportsScriptSource extends DelegatingScriptSource {
-    private final ImportsReader importsReader;
-
-    public ImportsScriptSource(ScriptSource source, ImportsReader importsReader) {
-        super(source);
-        this.importsReader = importsReader;
-    }
-
-    @Override
-    public Resource getResource() {
-        return new ImportsResource(super.getResource());
-    }
-
-    private class ImportsResource extends DelegatingResource {
-        private ImportsResource(Resource resource) {
-            super(resource);
-        }
-
-        @Override
-        public String getText() {
-            String text = getResource().getText();
-            assert text != null;
-
-            if (text.matches("\\s*")) {
-                return text;
-            } else {
-                return text + '\n' + importsReader.getImports();
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/configuration/ScriptPluginFactory.java b/subprojects/core/src/main/groovy/org/gradle/configuration/ScriptPluginFactory.java
index febb169..6897068 100755
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/ScriptPluginFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/ScriptPluginFactory.java
@@ -21,5 +21,5 @@ import org.gradle.groovy.scripts.BasicScript;
 import org.gradle.groovy.scripts.ScriptSource;
 
 public interface ScriptPluginFactory {
-    ScriptPlugin create(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope classLoaderScope, String classpathClosureName, Class<? extends BasicScript> scriptClass);
+    ScriptPlugin create(ScriptSource scriptSource, ScriptHandler scriptHandler, ClassLoaderScope targetScope, ClassLoaderScope baseScope, String classpathClosureName, Class<? extends BasicScript> scriptClass, boolean canonicalScript);
 }
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
index d6ab218..5e6244d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/project/BuildScriptProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/BuildScriptProcessor.java
@@ -35,8 +35,7 @@ public class BuildScriptProcessor implements ProjectConfigureAction {
         LOGGER.info(String.format("Evaluating %s using %s.", project, project.getBuildScriptSource().getDisplayName()));
         Clock clock = new Clock();
         try {
-            ScriptPlugin configurer = configurerFactory.create(project.getBuildScriptSource(), project.getBuildscript(), project.getClassLoaderScope(), "buildscript", ProjectScript.class);
-
+            ScriptPlugin configurer = configurerFactory.create(project.getBuildScriptSource(), project.getBuildscript(), project.getClassLoaderScope(), project.getBaseClassLoaderScope(), "buildscript", ProjectScript.class, true);
             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/LifecycleProjectEvaluator.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java
index 1a1547a..e990910 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/LifecycleProjectEvaluator.java
@@ -24,7 +24,7 @@ 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 {
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
index 834f60b..0285427 100644
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/project/PluginsProjectConfigureActions.java
+++ b/subprojects/core/src/main/groovy/org/gradle/configuration/project/PluginsProjectConfigureActions.java
@@ -16,18 +16,19 @@
 
 package org.gradle.configuration.project;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.internal.service.ServiceLocator;
 
 public class PluginsProjectConfigureActions implements ProjectConfigureAction {
-    private final ServiceLocator serviceLocator;
+    private final Iterable<ProjectConfigureAction> actions;
 
     public PluginsProjectConfigureActions(ClassLoader pluginsClassLoader) {
-        this.serviceLocator = new ServiceLocator(pluginsClassLoader);
+        actions = ImmutableList.copyOf(new ServiceLocator(pluginsClassLoader).getAll(ProjectConfigureAction.class));
     }
 
     public void execute(ProjectInternal project) {
-        for (ProjectConfigureAction configureAction : serviceLocator.getAll(ProjectConfigureAction.class)) {
+        for (ProjectConfigureAction configureAction : actions) {
             configureAction.execute(project);
         }
     }
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
deleted file mode 100644
index 688b698..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolver.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.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/TaskModelRealizingConfigurationAction.java b/subprojects/core/src/main/groovy/org/gradle/configuration/project/TaskModelRealizingConfigurationAction.java
deleted file mode 100644
index 5164736..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/configuration/project/TaskModelRealizingConfigurationAction.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.configuration.project;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.TaskContainerInternal;
-
-/**
- * Realizes the project tasks by getting the closed task container from the model registry.
- */
-public class TaskModelRealizingConfigurationAction implements ProjectConfigureAction {
-
-    public void execute(ProjectInternal projectInternal) {
-        projectInternal.getModelRegistry().get(TaskContainerInternal.MODEL_PATH, Object.class);
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java b/subprojects/core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
index 5a1e292..5591c20 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
@@ -47,6 +47,7 @@ public class DefaultBuildExecuter implements BuildExecuter {
             public void proceed() {
                 configure(index + 1);
             }
+
         });
     }
 
@@ -66,6 +67,7 @@ public class DefaultBuildExecuter implements BuildExecuter {
             public void proceed() {
                 execute(index + 1);
             }
+
         });
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/DefaultTasksBuildExecutionAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/DefaultTasksBuildExecutionAction.java
index 6d8f614..f086882 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/DefaultTasksBuildExecutionAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/DefaultTasksBuildExecutionAction.java
@@ -16,8 +16,8 @@
 package org.gradle.execution;
 
 import org.gradle.StartParameter;
+import org.gradle.TaskExecutionRequest;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.util.GUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,20 +30,31 @@ import java.util.List;
  */
 public class DefaultTasksBuildExecutionAction implements BuildConfigurationAction {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTasksBuildExecutionAction.class);
+    private final ProjectConfigurer projectConfigurer;
+
+    public DefaultTasksBuildExecutionAction(ProjectConfigurer projectConfigurer) {
+        this.projectConfigurer = projectConfigurer;
+    }
 
     public void configure(BuildExecutionContext context) {
         StartParameter startParameter = context.getGradle().getStartParameter();
 
-        if (!startParameter.getTaskNames().isEmpty()) {
-            context.proceed();
-            return;
+        for (TaskExecutionRequest request : startParameter.getTaskRequests()) {
+            if (!request.getArgs().isEmpty()) {
+                context.proceed();
+                return;
+            }
         }
 
         // Gather the default tasks from this first group project
         ProjectInternal project = context.getGradle().getDefaultProject();
+
+        //so that we don't miss out default tasks
+        projectConfigurer.configure(project);
+
         List<String> defaultTasks = project.getDefaultTasks();
         if (defaultTasks.size() == 0) {
-            defaultTasks = Arrays.asList(ImplicitTasksConfigurer.HELP_TASK);
+            defaultTasks = Arrays.asList(ProjectInternal.HELP_TASK);
             LOGGER.info("No tasks specified. Using default task {}", GUtil.toString(defaultTasks));
         } else {
             LOGGER.info("No tasks specified. Using project default tasks {}", GUtil.toString(defaultTasks));
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationAction.java
index f9e800a..ac716b1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationAction.java
@@ -18,6 +18,7 @@ package org.gradle.execution;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -26,21 +27,21 @@ import java.util.Set;
  * A {@link BuildConfigurationAction} which filters excluded tasks.
  */
 public class ExcludedTaskFilteringBuildConfigurationAction implements BuildConfigurationAction {
+    private final TaskSelector taskSelector;
+
+    public ExcludedTaskFilteringBuildConfigurationAction(TaskSelector taskSelector) {
+        this.taskSelector = taskSelector;
+    }
 
     public void configure(BuildExecutionContext context) {
         GradleInternal gradle = context.getGradle();
         Set<String> excludedTaskNames = gradle.getStartParameter().getExcludedTaskNames();
         if (!excludedTaskNames.isEmpty()) {
-            TaskSelector selector = gradle.getServices().get(TaskSelector.class);
-            final Set<Task> excludedTasks = new HashSet<Task>();
+            final Set<Spec<Task>> filters = new HashSet<Spec<Task>>();
             for (String taskName : excludedTaskNames) {
-                excludedTasks.addAll(selector.getSelection(taskName).getTasks());
+                filters.add(taskSelector.getFilter(taskName));
             }
-            gradle.getTaskGraph().useFilter(new Spec<Task>() {
-                public boolean isSatisfiedBy(Task task) {
-                    return !excludedTasks.contains(task);
-                }
-            });
+            gradle.getTaskGraph().useFilter(Specs.and(filters));
         }
 
         context.proceed();
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/MultipleBuildFailures.java b/subprojects/core/src/main/groovy/org/gradle/execution/MultipleBuildFailures.java
index e202727..88ca890 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/MultipleBuildFailures.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/MultipleBuildFailures.java
@@ -16,11 +16,11 @@
 
 package org.gradle.execution;
 
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
 
 import java.util.List;
 
-public class MultipleBuildFailures extends AbstractMultiCauseException {
+public class MultipleBuildFailures extends DefaultMultiCauseException {
     public MultipleBuildFailures(Iterable<? extends Throwable> causes) {
         super("Multiple build failures", causes);
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/ProjectConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/execution/ProjectConfigurer.java
new file mode 100644
index 0000000..bb85d52
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/ProjectConfigurer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal.project.ProjectInternal;
+
+public interface ProjectConfigurer {
+    /**
+     * Configures the given project.
+     */
+    void configure(ProjectInternal project);
+
+    /**
+     * Configures the given project and all its sub-projects.
+     */
+    void configureHierarchy(ProjectInternal project);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java
deleted file mode 100644
index 81b82e8..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/execution/ProjectEvaluatingAction.java
+++ /dev/null
@@ -1,54 +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.execution;
-
-import org.gradle.StartParameter;
-import org.gradle.api.internal.project.ProjectInternal;
-
-import java.util.List;
-
-/**
- * Ensures that projects resolved from the command line task names are evaluated.
- */
-public class ProjectEvaluatingAction implements BuildConfigurationAction {
-
-    private final TaskPathProjectEvaluator evaluator;
-
-    public ProjectEvaluatingAction() {
-        this(new TaskPathProjectEvaluator());
-    }
-
-    public ProjectEvaluatingAction(TaskPathProjectEvaluator evaluator) {
-        this.evaluator = evaluator;
-    }
-
-    public void configure(BuildExecutionContext context) {
-        StartParameter param = context.getGradle().getStartParameter();
-        List<String> taskNames = param.getTaskNames();
-        ProjectInternal project = context.getGradle().getDefaultProject();
-
-        if (param.getTaskNames().isEmpty()) {
-            //so that we don't miss out default tasks
-            project.evaluate();
-        }
-
-        for (String path : taskNames) {
-            evaluator.evaluateByPath(project, path);
-        }
-        context.proceed();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/SelectedTaskExecutionAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/SelectedTaskExecutionAction.java
index f56d5f3..1dfb025 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/SelectedTaskExecutionAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/SelectedTaskExecutionAction.java
@@ -15,8 +15,15 @@
  */
 package org.gradle.execution;
 
+import com.google.common.collect.Sets;
+import org.gradle.api.Project;
 import org.gradle.api.Task;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.execution.TaskExecutionGraphListener;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+
+import java.util.Set;
 
 public class SelectedTaskExecutionAction implements BuildExecutionAction {
     public void execute(BuildExecutionContext context) {
@@ -26,6 +33,7 @@ public class SelectedTaskExecutionAction implements BuildExecutionAction {
             taskGraph.useFailureHandler(new ContinueOnFailureHandler());
         }
 
+        taskGraph.addTaskExecutionGraphListener(new BindAllReferencesOfProjectsToExecuteListener());
         taskGraph.execute();
     }
 
@@ -34,4 +42,17 @@ public class SelectedTaskExecutionAction implements BuildExecutionAction {
             // Do nothing
         }
     }
+
+    private static class BindAllReferencesOfProjectsToExecuteListener implements TaskExecutionGraphListener {
+        @Override
+        public void graphPopulated(TaskExecutionGraph graph) {
+            Set<Project> seen = Sets.newHashSet();
+            for (Task task : graph.getAllTasks()) {
+                if (seen.add(task.getProject())) {
+                    ProjectInternal projectInternal = (ProjectInternal) task.getProject();
+                    projectInternal.bindAllModelRules();
+                }
+            }
+        }
+    }
 }
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 30d5c37..495f316 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
@@ -15,76 +15,193 @@
  */
 package org.gradle.execution;
 
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.gradle.api.Nullable;
 import org.gradle.api.Project;
+import org.gradle.api.ProjectConfigurationException;
 import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.tasks.TaskContainer;
+import org.gradle.model.internal.core.ModelNode;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
 
-import java.util.Collections;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.api.internal.tasks.TaskContainerInternal.TASK_MODEL_TYPE;
 
 public class TaskNameResolver {
 
-    public SetMultimap<String, TaskSelectionResult> select(String name, Project project) {
-        return select(name, (ProjectInternal) project, Collections.<Project>emptySet());
-    }
+    /**
+     * Non-exhaustively searches for at least one task with the given name, by not evaluating projects before searching.
+     */
+    public boolean tryFindUnqualifiedTaskCheaply(String name, ProjectInternal project) {
+        // don't evaluate children, see if we know it's without validating it
+        for (Project project1 : project.getAllprojects()) {
+            if (project1.getTasks().getNames().contains(name)) {
+                return true;
+            }
+        }
 
-    public SetMultimap<String, TaskSelectionResult> selectAll(String name, Project project) {
-        return select(name, (ProjectInternal) project, project.getSubprojects());
+        return false;
     }
 
-    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(), new LazyTaskSelectionResult(task.getName(), project.getTasks()));
+    /**
+     * Finds tasks that will have exactly the given name, without necessarily creating or configuring the tasks. Returns null if no such match found.
+     */
+    @Nullable
+    public TaskSelectionResult selectWithName(final String taskName, final ProjectInternal project, boolean includeSubProjects) {
+        if (includeSubProjects) {
+            Set<Task> tasks = Sets.newLinkedHashSet();
+            new MultiProjectTaskSelectionResult(taskName, project).collectTasks(tasks);
+            if (!tasks.isEmpty()) {
+                return new FixedTaskSelectionResult(tasks);
+            }
         } else {
-            task = project.getImplicitTasks().findByName(name);
-            if (task != null) {
-                selected.put(task.getName(), new LazyTaskSelectionResult(task.getName(), project.getImplicitTasks()));
+            if (hasTask(taskName, project)) {
+                return new TaskSelectionResult() {
+                    @Override
+                    public void collectTasks(Collection<? super Task> tasks) {
+                        tasks.add(getExistingTask(project, taskName));
+                    }
+                };
             }
         }
-        for (Project additionalProject : additionalProjects) {
-            task = additionalProject.getTasks().findByName(name);
-            if (task != null) {
-                selected.put(task.getName(), new LazyTaskSelectionResult(task.getName(), additionalProject.getTasks()));
+
+        return null;
+    }
+
+    /**
+     * Finds the names of all tasks, without necessarily creating or configuring the tasks. Returns an empty map when none are found.
+     */
+    public Map<String, TaskSelectionResult> selectAll(ProjectInternal project, boolean includeSubProjects) {
+        Map<String, TaskSelectionResult> selected = Maps.newLinkedHashMap();
+
+        if (includeSubProjects) {
+            Set<String> taskNames = Sets.newLinkedHashSet();
+            collectTaskNames(project, taskNames);
+            for (String taskName : taskNames) {
+                selected.put(taskName, new MultiProjectTaskSelectionResult(taskName, project));
+            }
+        } else {
+            for (String taskName : getTaskNames(project)) {
+                selected.put(taskName, new SingleProjectTaskSelectionResult(taskName, project.getTasks()));
             }
         }
-        if (!selected.isEmpty()) {
-            return selected;
-        }
 
-        for (String taskName : project.getTasks().getNames()) {
-            selected.put(taskName, new LazyTaskSelectionResult(taskName, project.getTasks()));
+        return selected;
+    }
+
+    private static ModelNode selfClose(ModelRegistry modelRegistry, ModelPath modelPath) {
+        ModelNode modelNode = modelRegistry.atStateOrLater(modelPath, ModelNode.State.SelfClosed);
+        if (modelNode == null) {
+            throw new IllegalStateException("Did not find " + modelPath + " in project model registry");
         }
-        for (String taskName : project.getImplicitTasks().getNames()) {
-            if (!selected.containsKey(taskName)) {
-                selected.put(taskName, new LazyTaskSelectionResult(taskName, project.getImplicitTasks()));
-            }
+
+        return modelNode;
+    }
+
+    private static ModelNode selfClosedTasksNode(ProjectInternal project) {
+        ModelRegistry modelRegistry = project.getModelRegistry();
+        ModelNode modelNode;
+        try {
+            project.fireDeferredConfiguration();
+            modelNode = selfClose(modelRegistry, TaskContainerInternal.MODEL_PATH);
+        } catch (Throwable e) {
+            throw new ProjectConfigurationException(String.format("A problem occurred configuring %s.", project), e);
         }
+        return modelNode;
+    }
+
+    private static Set<String> getTaskNames(ProjectInternal project) {
+        return selfClosedTasksNode(project).getLinkNames(TASK_MODEL_TYPE);
+    }
 
-        for (Project additionalProject : additionalProjects) {
-            for (String taskName : additionalProject.getTasks().getNames()) {
-                selected.put(taskName, new LazyTaskSelectionResult(taskName, additionalProject.getTasks()));
+    private static boolean hasTask(String taskName, ProjectInternal project) {
+        // ask registry first as tasks.findByName will fully realise the task if it is defined through the registry
+        // i.e. avoid configuring tasks defined through the registry at this point, because we just need to know if it exists
+        return selfClosedTasksNode(project).hasLink(taskName, TASK_MODEL_TYPE) || project.getTasks().findByName(taskName) != null; // look at task container to trigger rules / placeholders
+    }
+
+    private static TaskInternal getExistingTask(ProjectInternal project, String taskName) {
+        ModelRegistry modelRegistry = project.getModelRegistry();
+        ModelPath path = TaskContainerInternal.MODEL_PATH.child(taskName);
+        try {
+            if (modelRegistry.node(path) == null) {
+                // The tasks exists but isn't in the model registry, was created after the registry was closed
+                // This can happen for a few reasons, use of task container rules being a common one
+                return (TaskInternal) project.getTasks().getByName(taskName);
+            } else {
+                // pull through the registry, even for tasks defined via legacy DSL, to ensure all configuration rules get applied
+                return (TaskInternal) modelRegistry.realize(path, TASK_MODEL_TYPE);
             }
+        } catch (Throwable e) {
+            throw new ProjectConfigurationException(String.format("A problem occurred configuring %s.", project), e);
         }
+    }
 
-        return selected;
+    private void collectTaskNames(ProjectInternal project, Set<String> result) {
+        result.addAll(getTaskNames(project));
+        for (Project subProject : project.getChildProjects().values()) {
+            collectTaskNames((ProjectInternal) subProject, result);
+        }
     }
 
+    private static class FixedTaskSelectionResult implements TaskSelectionResult {
+        private final Collection<Task> tasks;
 
-    public static class LazyTaskSelectionResult implements TaskSelectionResult {
+        FixedTaskSelectionResult(Collection<Task> tasks) {
+            this.tasks = tasks;
+        }
+
+        public void collectTasks(Collection<? super Task> tasks) {
+            tasks.addAll(this.tasks);
+        }
+    }
+
+    private static class SingleProjectTaskSelectionResult implements TaskSelectionResult {
         private final TaskContainer taskContainer;
         private final String taskName;
 
-        public LazyTaskSelectionResult(String taskName, TaskContainer tasksContainer) {
+        SingleProjectTaskSelectionResult(String taskName, TaskContainer tasksContainer) {
             this.taskContainer = tasksContainer;
             this.taskName = taskName;
         }
 
-        public Task getTask() {
-            return taskContainer.getByName(taskName);
+        public void collectTasks(Collection<? super Task> tasks) {
+            tasks.add(taskContainer.getByName(taskName));
+        }
+    }
+
+    private static class MultiProjectTaskSelectionResult implements TaskSelectionResult {
+        private final ProjectInternal project;
+        private final String taskName;
+
+        MultiProjectTaskSelectionResult(String taskName, ProjectInternal project) {
+            this.project = project;
+            this.taskName = taskName;
+        }
+
+        public void collectTasks(Collection<? super Task> tasks) {
+            collect(project, tasks);
+        }
+
+        private void collect(ProjectInternal project, Collection<? super Task> tasks) {
+            if (hasTask(taskName, project)) {
+                TaskInternal task = getExistingTask(project, taskName);
+                tasks.add(task);
+                if (task.getImpliesSubProjects()) {
+                    return;
+                }
+            }
+            for (Project subProject : project.getChildProjects().values()) {
+                collect((ProjectInternal) subProject, tasks);
+            }
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationAction.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationAction.java
index 87263c1..8fcc908 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationAction.java
@@ -15,11 +15,9 @@
  */
 package org.gradle.execution;
 
-import com.google.common.collect.Multimap;
-import org.gradle.api.Task;
+import org.gradle.TaskExecutionRequest;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.execution.commandline.CommandLineTaskParser;
-import org.gradle.util.GUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,27 +30,22 @@ import java.util.List;
 public class TaskNameResolvingBuildConfigurationAction implements BuildConfigurationAction {
     private static final Logger LOGGER = LoggerFactory.getLogger(TaskNameResolvingBuildConfigurationAction.class);
     private final CommandLineTaskParser commandLineTaskParser;
-    private final TaskSelector selector;
 
-    public TaskNameResolvingBuildConfigurationAction(CommandLineTaskParser commandLineTaskParser, TaskSelector selector) {
+    public TaskNameResolvingBuildConfigurationAction(CommandLineTaskParser commandLineTaskParser) {
         this.commandLineTaskParser = commandLineTaskParser;
-        this.selector = selector;
     }
 
     public void configure(BuildExecutionContext context) {
         GradleInternal gradle = context.getGradle();
-        List<String> taskNames = gradle.getStartParameter().getTaskNames();
-        Multimap<String, Task> selectedTasks = commandLineTaskParser.parseTasks(taskNames, selector);
-
         TaskGraphExecuter executer = gradle.getTaskGraph();
-        for (String name : selectedTasks.keySet()) {
-            executer.addTasks(selectedTasks.get(name));
-        }
 
-        if (selectedTasks.keySet().size() == 1) {
-            LOGGER.info("Selected primary task {}", GUtil.toString(selectedTasks.keySet()));
-        } else {
-            LOGGER.info("Selected primary tasks {}", GUtil.toString(selectedTasks.keySet()));
+        List<TaskExecutionRequest> taskParameters = gradle.getStartParameter().getTaskRequests();
+        for (TaskExecutionRequest taskParameter : taskParameters) {
+            List<TaskSelector.TaskSelection> taskSelections = commandLineTaskParser.parseTasks(taskParameter);
+            for (TaskSelector.TaskSelection taskSelection : taskSelections) {
+                LOGGER.info("Selected primary task '{}' from project {}", taskSelection.getTaskName(), taskSelection.getProjectPath());
+                executer.addTasks(taskSelection.getTasks());
+            }
         }
 
         context.proceed();
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 d1bc839..eef8b81 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskPathProjectEvaluator.java
@@ -16,32 +16,35 @@
 
 package org.gradle.execution;
 
+import org.gradle.api.BuildCancelledException;
 import org.gradle.api.Project;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.execution.taskpath.ResolvedTaskPath;
-import org.gradle.execution.taskpath.TaskPathResolver;
+import org.gradle.initialization.BuildCancellationToken;
 
-public class TaskPathProjectEvaluator {
+public class TaskPathProjectEvaluator implements ProjectConfigurer {
+    private final BuildCancellationToken cancellationToken;
 
-    private final TaskPathResolver taskPathResolver;
-
-    public TaskPathProjectEvaluator() {
-        this(new TaskPathResolver());
+    public TaskPathProjectEvaluator(BuildCancellationToken cancellationToken) {
+        this.cancellationToken = cancellationToken;
     }
 
-    TaskPathProjectEvaluator(TaskPathResolver taskPathResolver) {
-        this.taskPathResolver = taskPathResolver;
+    public void configure(ProjectInternal project) {
+        if (cancellationToken.isCancellationRequested()) {
+            throw new BuildCancelledException();
+        }
+        project.evaluate();
     }
 
-    public void evaluateByPath(ProjectInternal project, String path) {
-        ResolvedTaskPath taskPath = taskPathResolver.resolvePath(path, project);
-        if (taskPath.isQualified()) {
-            taskPath.getProject().evaluate();
-        } else {
-            project.evaluate();
-            for (Project sub : project.getSubprojects()) {
-                ((ProjectInternal) sub).evaluate();
+    public void configureHierarchy(ProjectInternal project) {
+        if (cancellationToken.isCancellationRequested()) {
+            throw new BuildCancelledException();
+        }
+        project.evaluate();
+        for (Project sub : project.getSubprojects()) {
+            if (cancellationToken.isCancellationRequested()) {
+                throw new BuildCancelledException();
             }
+            ((ProjectInternal) sub).evaluate();
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionException.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionException.java
index 1b7c041..9375fb3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionException.java
@@ -15,9 +15,9 @@
  */
 package org.gradle.execution;
 
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.internal.exceptions.FailureResolutionAware;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.logging.StyledTextOutput;
 
@@ -34,7 +34,7 @@ public class TaskSelectionException extends InvalidUserDataException implements
 
     public void appendResolution(StyledTextOutput output, BuildClientMetaData clientMetaData) {
         output.text("Run ");
-        clientMetaData.describeCommand(output.withStyle(UserInput), ImplicitTasksConfigurer.TASKS_TASK);
+        clientMetaData.describeCommand(output.withStyle(UserInput), ProjectInternal.TASKS_TASK);
         output.text(" to get a list of available tasks.");
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java
index 4946a48..7031469 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelectionResult.java
@@ -18,6 +18,8 @@ package org.gradle.execution;
 
 import org.gradle.api.Task;
 
+import java.util.Collection;
+
 public interface TaskSelectionResult {
-    Task getTask();
+    void collectTasks(Collection<? super Task> tasks);
 }
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 b781b97..8ae46a2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/TaskSelector.java
@@ -15,78 +15,136 @@
  */
 package org.gradle.execution;
 
-import com.google.common.collect.SetMultimap;
+import org.gradle.api.Nullable;
+import org.gradle.api.Project;
 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.api.specs.Spec;
 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.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 
 public class TaskSelector {
     private final TaskNameResolver taskNameResolver;
     private final GradleInternal gradle;
+    private final ProjectConfigurer configurer;
     private final TaskPathResolver taskPathResolver = new TaskPathResolver();
 
-    public TaskSelector(GradleInternal gradle) {
-        this(gradle, new TaskNameResolver());
+    public TaskSelector(GradleInternal gradle, ProjectConfigurer projectConfigurer) {
+        this(gradle, new TaskNameResolver(), projectConfigurer);
     }
 
-    public TaskSelector(GradleInternal gradle, TaskNameResolver taskNameResolver) {
+    public TaskSelector(GradleInternal gradle, TaskNameResolver taskNameResolver, ProjectConfigurer configurer) {
         this.taskNameResolver = taskNameResolver;
         this.gradle = gradle;
+        this.configurer = configurer;
     }
 
     public TaskSelection getSelection(String path) {
-        SetMultimap<String, TaskSelectionResult> tasksByName;
-        ProjectInternal project = gradle.getDefaultProject();
-        ResolvedTaskPath taskPath = taskPathResolver.resolvePath(path, project);
+        return getSelection(path, gradle.getDefaultProject());
+    }
+
+    public Spec<Task> getFilter(String path) {
+        final ResolvedTaskPath taskPath = taskPathResolver.resolvePath(path, gradle.getDefaultProject());
+        if (!taskPath.isQualified()) {
+            ProjectInternal targetProject = taskPath.getProject();
+            configurer.configure(targetProject);
+            if (taskNameResolver.tryFindUnqualifiedTaskCheaply(taskPath.getTaskName(), taskPath.getProject())) {
+                // An exact match in the target project - can just filter tasks by path to avoid configuring sub-projects at this point
+                return new TaskPathSpec(targetProject, taskPath.getTaskName());
+            }
+        }
+
+        final Set<Task> selectedTasks = getSelection(path, gradle.getDefaultProject()).getTasks();
+        return new Spec<Task>() {
+            public boolean isSatisfiedBy(Task element) {
+                return !selectedTasks.contains(element);
+            }
+        };
+    }
+
+    public TaskSelection getSelection(@Nullable String projectPath, String path) {
+        ProjectInternal project = projectPath != null
+                ? gradle.getRootProject().findProject(projectPath)
+                : gradle.getDefaultProject();
+        return getSelection(path, project);
+    }
 
+    private TaskSelection getSelection(String path, ProjectInternal project) {
+        ResolvedTaskPath taskPath = taskPathResolver.resolvePath(path, project);
+        ProjectInternal targetProject = taskPath.getProject();
         if (taskPath.isQualified()) {
-            tasksByName = taskNameResolver.select(taskPath.getTaskName(), taskPath.getProject());
+            configurer.configure(targetProject);
         } else {
-            tasksByName = taskNameResolver.selectAll(taskPath.getTaskName(), taskPath.getProject());
+            configurer.configureHierarchy(targetProject);
         }
 
-        Set<TaskSelectionResult> tasks = tasksByName.get(taskPath.getTaskName());
-        if (!tasks.isEmpty()) {
+        TaskSelectionResult tasks = taskNameResolver.selectWithName(taskPath.getTaskName(), taskPath.getProject(), !taskPath.isQualified());
+        if (tasks != null) {
             // An exact match
-            return new TaskSelection(path, tasks);
+            return new TaskSelection(taskPath.getProject().getPath(), path, tasks);
         }
 
+        Map<String, TaskSelectionResult> tasksByName = taskNameResolver.selectAll(taskPath.getProject(), !taskPath.isQualified());
         NameMatcher matcher = new NameMatcher();
         String actualName = matcher.find(taskPath.getTaskName(), tasksByName.keySet());
         if (actualName != null) {
-            return new TaskSelection(taskPath.getPrefix() + actualName, tasksByName.get(actualName));
+            return new TaskSelection(taskPath.getProject().getPath(), taskPath.getPrefix() + actualName, tasksByName.get(actualName));
         }
 
         throw new TaskSelectionException(matcher.formatErrorMessage("task", taskPath.getProject()));
     }
 
     public static class TaskSelection {
-        private String taskName;
-        private Collection<TaskSelectionResult> taskSelectionResult;
-        public TaskSelection(String taskName, Set<TaskSelectionResult> tasks) {
+        private final String projectPath;
+        private final String taskName;
+        private final TaskSelectionResult taskSelectionResult;
+
+        public TaskSelection(String projectPath, String taskName, TaskSelectionResult tasks) {
+            this.projectPath = projectPath;
             this.taskName = taskName;
             taskSelectionResult = tasks;
         }
 
+        public String getProjectPath() {
+            return projectPath;
+        }
+
         public String getTaskName() {
             return taskName;
         }
 
         public Set<Task> getTasks() {
-            return CollectionUtils.collect(taskSelectionResult, new LinkedHashSet<Task>(), new Transformer<Task, TaskSelectionResult>() {
-                public Task transform(TaskSelectionResult original) {
-                    return original.getTask();
+            LinkedHashSet<Task> result = new LinkedHashSet<Task>();
+            taskSelectionResult.collectTasks(result);
+            return result;
+        }
+    }
+
+    private static class TaskPathSpec implements Spec<Task> {
+        private final ProjectInternal targetProject;
+        private final String taskName;
+
+        public TaskPathSpec(ProjectInternal targetProject, String taskName) {
+            this.targetProject = targetProject;
+            this.taskName = taskName;
+        }
+
+        public boolean isSatisfiedBy(Task element) {
+            if (!element.getName().equals(taskName)) {
+                return true;
+            }
+            for (Project current = element.getProject(); current != null; current = current.getParent()) {
+                if (current.equals(targetProject)) {
+                    return false;
                 }
-            });
+            }
+            return true;
         }
     }
 }
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 305658c..9eb112b 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
@@ -16,9 +16,8 @@
 
 package org.gradle.execution.commandline;
 
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Lists;
+import org.gradle.TaskExecutionRequest;
 import org.gradle.api.Task;
 import org.gradle.execution.TaskSelector;
 
@@ -28,21 +27,22 @@ import java.util.Set;
 
 public class CommandLineTaskParser {
     private final CommandLineTaskConfigurer taskConfigurer;
+    private final TaskSelector taskSelector;
 
-    public CommandLineTaskParser(CommandLineTaskConfigurer commandLineTaskConfigurer) {
+    public CommandLineTaskParser(CommandLineTaskConfigurer commandLineTaskConfigurer, TaskSelector taskSelector) {
         this.taskConfigurer = commandLineTaskConfigurer;
+        this.taskSelector = taskSelector;
     }
 
-    public Multimap<String, Task> parseTasks(List<String> taskPaths, TaskSelector taskSelector) {
-        SetMultimap<String, Task> out = LinkedHashMultimap.create();
-        List<String> remainingPaths = new LinkedList<String>(taskPaths);
+    public List<TaskSelector.TaskSelection> parseTasks(TaskExecutionRequest taskExecutionRequest) {
+        List<TaskSelector.TaskSelection> out = Lists.newArrayList();
+        List<String> remainingPaths = new LinkedList<String>(taskExecutionRequest.getArgs());
         while (!remainingPaths.isEmpty()) {
             String path = remainingPaths.remove(0);
-            TaskSelector.TaskSelection selection = taskSelector.getSelection(path);
+            TaskSelector.TaskSelection selection = taskSelector.getSelection(taskExecutionRequest.getProjectPath(), path);
             Set<Task> tasks = selection.getTasks();
             remainingPaths = taskConfigurer.configureTasks(tasks, remainingPaths);
-
-            out.putAll(selection.getTaskName(), tasks);
+            out.add(selection);
         }
         return out;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/TaskConfigurationException.java b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/TaskConfigurationException.java
index f2c001c..b4f8a57 100644
--- a/subprojects/core/src/main/groovy/org/gradle/execution/commandline/TaskConfigurationException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/execution/commandline/TaskConfigurationException.java
@@ -17,7 +17,7 @@
 package org.gradle.execution.commandline;
 
 import org.gradle.api.GradleException;
-import org.gradle.configuration.ImplicitTasksConfigurer;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.internal.exceptions.Contextual;
 import org.gradle.internal.exceptions.FailureResolutionAware;
@@ -37,7 +37,7 @@ public class TaskConfigurationException extends GradleException implements Failu
 
     public void appendResolution(StyledTextOutput output, BuildClientMetaData clientMetaData) {
         output.text("Run ");
-        clientMetaData.describeCommand(output.withStyle(UserInput), ImplicitTasksConfigurer.HELP_TASK);
+        clientMetaData.describeCommand(output.withStyle(UserInput), ProjectInternal.HELP_TASK);
         output.withStyle(UserInput).format(" --task %s", taskPath);
         output.text(" to get task usage details.");
     }
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 557b800..b516973 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
@@ -16,15 +16,24 @@
 
 package org.gradle.execution.taskgraph;
 
-import org.gradle.api.CircularReferenceException;
-import org.gradle.api.Task;
-import org.gradle.api.Transformer;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.StandardSystemProperty;
+import com.google.common.collect.*;
+import org.gradle.api.*;
 import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.CachingTaskDependencyResolveContext;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskDependency;
 import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.execution.TaskFailureHandler;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.internal.Pair;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.graph.CachingDirectedGraphWalker;
 import org.gradle.internal.graph.DirectedGraph;
@@ -32,7 +41,10 @@ import org.gradle.internal.graph.DirectedGraphRenderer;
 import org.gradle.internal.graph.GraphNodeRenderer;
 import org.gradle.logging.StyledTextOutput;
 import org.gradle.util.CollectionUtils;
+import org.gradle.util.TextUtil;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.StringWriter;
 import java.util.*;
 import java.util.concurrent.locks.Condition;
@@ -40,21 +52,48 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * A reusable implementation of TaskExecutionPlan. The {@link #addToTaskGraph(java.util.Collection)} and {@link #clear()} methods are NOT threadsafe, and callers must synchronize
- * access to these methods.
+ * A reusable implementation of TaskExecutionPlan. The {@link #addToTaskGraph(java.util.Collection)} and {@link #clear()} methods are NOT threadsafe, and callers must synchronize access to these
+ * methods.
  */
-class DefaultTaskExecutionPlan implements TaskExecutionPlan {
+public class DefaultTaskExecutionPlan implements TaskExecutionPlan {
+
+    public static final String INTRA_PROJECT_TOGGLE = "org.gradle.parallel.intra";
+
+    private final static Logger LOGGER = Logging.getLogger(DefaultTaskExecutionPlan.class);
+
     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<TaskInfo> executionQueue = new LinkedList<TaskInfo>();
     private final List<Throwable> failures = new ArrayList<Throwable>();
     private Spec<? super Task> filter = Specs.satisfyAll();
 
     private TaskFailureHandler failureHandler = new RethrowingFailureHandler();
-    private final List<String> runningProjects = new ArrayList<String>();
+    private final BuildCancellationToken cancellationToken;
+    private final Multiset<String> projectsWithRunningTasks = HashMultiset.create();
+    private final Multiset<String> projectsWithRunningNonParallelizableTasks = HashMultiset.create();
+    private final Set<TaskInternal> runningTasks = Sets.newIdentityHashSet();
+    private final Map<Task, Set<String>> canonicalizedOutputCache = Maps.newIdentityHashMap();
+    private final Map<Task, Boolean> isParallelSafeCache = Maps.newIdentityHashMap();
+    private boolean tasksCancelled;
+
+    private final boolean intraProjectParallelization;
+
+    public DefaultTaskExecutionPlan(BuildCancellationToken cancellationToken, boolean intraProjectParallelization) {
+        this.cancellationToken = cancellationToken;
+        this.intraProjectParallelization = intraProjectParallelization;
+
+        if (intraProjectParallelization) {
+            LOGGER.info("intra project task parallelization is enabled");
+        }
+    }
+
+    public DefaultTaskExecutionPlan(BuildCancellationToken cancellationToken) {
+        this(cancellationToken, Boolean.getBoolean(INTRA_PROJECT_TOGGLE));
+    }
 
     public void addToTaskGraph(Collection<? extends Task> tasks) {
         List<TaskInfo> queue = new ArrayList<TaskInfo>();
@@ -63,7 +102,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         Collections.sort(sortedTasks);
         for (Task task : sortedTasks) {
             TaskInfo node = graph.addNode(task);
-            if (node.getMustNotRun()) {
+            if (node.isMustNotRun()) {
                 requireWithDependencies(node);
             } else if (filter.isSatisfiedBy(task)) {
                 node.require();
@@ -96,7 +135,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
             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<? extends Task> dependsOnTasks = context.getDependencies(task);
+                Set<? extends Task> dependsOnTasks = realizedDependencies(task, context);
                 for (Task dependsOnTask : dependsOnTasks) {
                     TaskInfo targetNode = graph.addNode(dependsOnTask);
                     node.addDependencySuccessor(targetNode);
@@ -104,7 +143,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
                         queue.add(0, targetNode);
                     }
                 }
-                for (Task finalizerTask : task.getFinalizedBy().getDependencies(task)) {
+                for (Task finalizerTask : realizedDependencies(task, task.getFinalizedBy())) {
                     TaskInfo targetNode = graph.addNode(finalizerTask);
                     addFinalizerNode(node, targetNode);
                     if (!visiting.contains(targetNode)) {
@@ -138,6 +177,15 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         resolveTasksInUnknownState();
     }
 
+    Set<? extends Task> realizedDependencies(Task task, TaskDependency dependencies) {
+        Set<? extends Task> resolvedDependencies = dependencies.getDependencies(task);
+        for (Task resolvedDependency : resolvedDependencies) {
+            ProjectInternal project = (ProjectInternal) resolvedDependency.getProject();
+            project.getTasks().maybeRealizeTask(resolvedDependency.getName());
+        }
+        return resolvedDependencies;
+    }
+
     private void resolveTasksInUnknownState() {
         List<TaskInfo> queue = new ArrayList<TaskInfo>(tasksInUnknownState);
         Set<TaskInfo> visiting = new HashSet<TaskInfo>();
@@ -160,7 +208,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
                 visiting.remove(task);
                 task.mustNotRun();
                 for (TaskInfo predecessor : task.getDependencyPredecessors()) {
-                    assert predecessor.isRequired() || predecessor.getMustNotRun();
+                    assert predecessor.isRequired() || predecessor.isMustNotRun();
                     if (predecessor.isRequired()) {
                         task.require();
                         break;
@@ -187,7 +235,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     }
 
     private void requireWithDependencies(TaskInfo taskInfo) {
-        if (taskInfo.getMustNotRun() && filter.isSatisfiedBy(taskInfo.getTask())) {
+        if (taskInfo.isMustNotRun() && filter.isSatisfiedBy(taskInfo.getTask())) {
             taskInfo.require();
             for (TaskInfo dependency : taskInfo.getDependencySuccessors()) {
                 requireWithDependencies(dependency);
@@ -196,33 +244,48 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     }
 
     public void determineExecutionPlan() {
-        List<TaskInfo> nodeQueue = new ArrayList<TaskInfo>(entryTasks);
-        Set<TaskInfo> visitingNodes = new HashSet<TaskInfo>();
-        Stack<TaskDependencyGraphEdge> walkedShouldRunAfterEdges = new Stack<TaskDependencyGraphEdge>();
+        List<TaskInfoInVisitingSegment> nodeQueue = Lists.newArrayList(Iterables.transform(entryTasks, new Function<TaskInfo, TaskInfoInVisitingSegment>() {
+            int index;
+
+            public TaskInfoInVisitingSegment apply(TaskInfo taskInfo) {
+                return new TaskInfoInVisitingSegment(taskInfo, index++);
+            }
+        }));
+        int visitingSegmentCounter = nodeQueue.size();
+
+        HashMultimap<TaskInfo, Integer> visitingNodes = HashMultimap.create();
+        Stack<GraphEdge> walkedShouldRunAfterEdges = new Stack<GraphEdge>();
         Stack<TaskInfo> path = new Stack<TaskInfo>();
         HashMap<TaskInfo, Integer> planBeforeVisiting = new HashMap<TaskInfo, Integer>();
+
         while (!nodeQueue.isEmpty()) {
-            TaskInfo taskNode = nodeQueue.get(0);
+            TaskInfoInVisitingSegment taskInfoInVisitingSegment = nodeQueue.get(0);
+            int currentSegment = taskInfoInVisitingSegment.visitingSegment;
+            TaskInfo taskNode = taskInfoInVisitingSegment.taskInfo;
 
             if (taskNode.isIncludeInGraph() || executionPlan.containsKey(taskNode.getTask())) {
                 nodeQueue.remove(0);
+                maybeRemoveProcessedShouldRunAfterEdge(walkedShouldRunAfterEdges, taskNode);
                 continue;
             }
 
-            if (visitingNodes.add(taskNode)) {
+            boolean alreadyVisited = visitingNodes.containsKey(taskNode);
+            visitingNodes.put(taskNode, currentSegment);
+
+            if (!alreadyVisited) {
                 // Have not seen this task before - add its dependencies to the head of the queue and leave this
                 // task in the queue
                 recordEdgeIfArrivedViaShouldRunAfter(walkedShouldRunAfterEdges, path, taskNode);
-                removeShouldRunAfterSuccessorsIfTheyImposeACycle(visitingNodes, taskNode);
+                removeShouldRunAfterSuccessorsIfTheyImposeACycle(visitingNodes, taskInfoInVisitingSegment);
                 takePlanSnapshotIfCanBeRestoredToCurrentTask(planBeforeVisiting, taskNode);
                 ArrayList<TaskInfo> successors = new ArrayList<TaskInfo>();
                 addAllSuccessorsInReverseOrder(taskNode, successors);
                 for (TaskInfo successor : successors) {
-                    if (visitingNodes.contains(successor)) {
+                    if (visitingNodes.containsEntry(successor, currentSegment)) {
                         if (!walkedShouldRunAfterEdges.empty()) {
                             //remove the last walked should run after edge and restore state from before walking it
-                            TaskDependencyGraphEdge toBeRemoved = walkedShouldRunAfterEdges.pop();
-                            toBeRemoved.getFrom().removeShouldRunAfterSuccessor(toBeRemoved.getTo());
+                            GraphEdge toBeRemoved = walkedShouldRunAfterEdges.pop();
+                            toBeRemoved.from.removeShouldRunAfterSuccessor(toBeRemoved.to);
                             restorePath(path, toBeRemoved);
                             restoreQueue(nodeQueue, visitingNodes, toBeRemoved);
                             restoreExecutionPlan(planBeforeVisiting, toBeRemoved);
@@ -231,30 +294,38 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
                             onOrderingCycle();
                         }
                     }
-                    nodeQueue.add(0, successor);
+                    nodeQueue.add(0, new TaskInfoInVisitingSegment(successor, currentSegment));
                 }
                 path.push(taskNode);
             } else {
                 // Have visited this task's dependencies - add it to the end of the plan
                 nodeQueue.remove(0);
-                visitingNodes.remove(taskNode);
+                visitingNodes.remove(taskNode, currentSegment);
                 path.pop();
                 executionPlan.put(taskNode.getTask(), taskNode);
                 // Add any finalizers to the queue
                 ArrayList<TaskInfo> finalizerTasks = new ArrayList<TaskInfo>();
                 addAllReversed(finalizerTasks, taskNode.getFinalizers());
                 for (TaskInfo finalizer : finalizerTasks) {
-                    if (!visitingNodes.contains(finalizer) && !nodeQueue.contains(finalizer)) {
-                        nodeQueue.add(finalizerTaskPosition(finalizer, nodeQueue), finalizer);
+                    if (!visitingNodes.containsKey(finalizer)) {
+                        nodeQueue.add(finalizerTaskPosition(finalizer, nodeQueue), new TaskInfoInVisitingSegment(finalizer, visitingSegmentCounter++));
                     }
                 }
             }
         }
+        executionQueue.clear();
+        executionQueue.addAll(executionPlan.values());
+    }
+
+    private void maybeRemoveProcessedShouldRunAfterEdge(Stack<GraphEdge> walkedShouldRunAfterEdges, TaskInfo taskNode) {
+        if (!walkedShouldRunAfterEdges.isEmpty() && walkedShouldRunAfterEdges.peek().to.equals(taskNode)) {
+            walkedShouldRunAfterEdges.pop();
+        }
     }
 
-    private void restoreExecutionPlan(HashMap<TaskInfo, Integer> planBeforeVisiting, TaskDependencyGraphEdge toBeRemoved) {
+    private void restoreExecutionPlan(HashMap<TaskInfo, Integer> planBeforeVisiting, GraphEdge toBeRemoved) {
         Iterator<Map.Entry<Task, TaskInfo>> executionPlanIterator = executionPlan.entrySet().iterator();
-        for (int i = 0; i < planBeforeVisiting.get(toBeRemoved.getFrom()); i++) {
+        for (int i = 0; i < planBeforeVisiting.get(toBeRemoved.from); i++) {
             executionPlanIterator.next();
         }
         while (executionPlanIterator.hasNext()) {
@@ -263,20 +334,20 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
-    private void restoreQueue(List<TaskInfo> nodeQueue, Set<TaskInfo> visitingNodes, TaskDependencyGraphEdge toBeRemoved) {
-        TaskInfo nextInQueue = null;
-        while (!toBeRemoved.getFrom().equals(nextInQueue)) {
+    private void restoreQueue(List<TaskInfoInVisitingSegment> nodeQueue, HashMultimap<TaskInfo, Integer> visitingNodes, GraphEdge toBeRemoved) {
+        TaskInfoInVisitingSegment nextInQueue = null;
+        while (nextInQueue == null || !toBeRemoved.from.equals(nextInQueue.taskInfo)) {
             nextInQueue = nodeQueue.get(0);
-            visitingNodes.remove(nextInQueue);
-            if (!toBeRemoved.getFrom().equals(nextInQueue)) {
+            visitingNodes.remove(nextInQueue.taskInfo, nextInQueue.visitingSegment);
+            if (!toBeRemoved.from.equals(nextInQueue.taskInfo)) {
                 nodeQueue.remove(0);
             }
         }
     }
 
-    private void restorePath(Stack<TaskInfo> path, TaskDependencyGraphEdge toBeRemoved) {
+    private void restorePath(Stack<TaskInfo> path, GraphEdge toBeRemoved) {
         TaskInfo removedFromPath = null;
-        while (!toBeRemoved.getFrom().equals(removedFromPath)) {
+        while (!toBeRemoved.from.equals(removedFromPath)) {
             removedFromPath = path.pop();
         }
     }
@@ -287,12 +358,13 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         addAllReversed(dependsOnTasks, taskNode.getShouldSuccessors());
     }
 
-    private void removeShouldRunAfterSuccessorsIfTheyImposeACycle(Set<TaskInfo> visitingNodes, TaskInfo taskNode) {
-        for (TaskInfo shouldRunAfterSuccessor : taskNode.getShouldSuccessors()) {
-            if (visitingNodes.contains(shouldRunAfterSuccessor)) {
-                taskNode.removeShouldRunAfterSuccessor(shouldRunAfterSuccessor);
+    private void removeShouldRunAfterSuccessorsIfTheyImposeACycle(final HashMultimap<TaskInfo, Integer> visitingNodes, final TaskInfoInVisitingSegment taskNodeWithVisitingSegment) {
+        TaskInfo taskNode = taskNodeWithVisitingSegment.taskInfo;
+        Iterables.removeIf(taskNode.getShouldSuccessors(), new Predicate<TaskInfo>() {
+            public boolean apply(TaskInfo input) {
+                return visitingNodes.containsEntry(input, taskNodeWithVisitingSegment.visitingSegment);
             }
-        }
+        });
     }
 
     private void takePlanSnapshotIfCanBeRestoredToCurrentTask(HashMap<TaskInfo, Integer> planBeforeVisiting, TaskInfo taskNode) {
@@ -301,13 +373,13 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
-    private void recordEdgeIfArrivedViaShouldRunAfter(Stack<TaskDependencyGraphEdge> walkedShouldRunAfterEdges, Stack<TaskInfo> path, TaskInfo taskNode) {
+    private void recordEdgeIfArrivedViaShouldRunAfter(Stack<GraphEdge> walkedShouldRunAfterEdges, Stack<TaskInfo> path, TaskInfo taskNode) {
         if (!path.empty() && path.peek().getShouldSuccessors().contains(taskNode)) {
-            walkedShouldRunAfterEdges.push(new TaskDependencyGraphEdge(path.peek(), taskNode));
+            walkedShouldRunAfterEdges.push(new GraphEdge(path.peek(), taskNode));
         }
     }
 
-    private int finalizerTaskPosition(TaskInfo finalizer, final List<TaskInfo> nodeQueue) {
+    private int finalizerTaskPosition(TaskInfo finalizer, final List<TaskInfoInVisitingSegment> nodeQueue) {
         if (nodeQueue.size() == 0) {
             return 0;
         }
@@ -317,8 +389,12 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         dependsOnTasks.addAll(finalizer.getMustSuccessors());
         dependsOnTasks.addAll(finalizer.getShouldSuccessors());
         List<Integer> dependsOnTaskIndexes = CollectionUtils.collect(dependsOnTasks, new Transformer<Integer, TaskInfo>() {
-            public Integer transform(TaskInfo dependsOnTask) {
-                return nodeQueue.indexOf(dependsOnTask);
+            public Integer transform(final TaskInfo dependsOnTask) {
+                return Iterables.indexOf(nodeQueue, new Predicate<TaskInfoInVisitingSegment>() {
+                    public boolean apply(TaskInfoInVisitingSegment taskInfoInVisitingSegment) {
+                        return taskInfoInVisitingSegment.taskInfo.equals(dependsOnTask);
+                    }
+                });
             }
         });
         return Collections.max(dependsOnTaskIndexes) + 1;
@@ -359,8 +435,13 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
             graph.clear();
             entryTasks.clear();
             executionPlan.clear();
+            executionQueue.clear();
             failures.clear();
-            runningProjects.clear();
+            projectsWithRunningTasks.clear();
+            projectsWithRunningNonParallelizableTasks.clear();
+            canonicalizedOutputCache.clear();
+            isParallelSafeCache.clear();
+            runningTasks.clear();
         } finally {
             lock.unlock();
         }
@@ -382,12 +463,20 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         lock.lock();
         try {
             while (true) {
+                if (cancellationToken.isCancellationRequested()) {
+                    if (abortExecution()) {
+                        tasksCancelled = true;
+                    }
+                }
                 TaskInfo nextMatching = null;
                 boolean allTasksComplete = true;
-                for (TaskInfo taskInfo : executionPlan.values()) {
+                Iterator<TaskInfo> iterator = executionQueue.iterator();
+                while (iterator.hasNext()) {
+                    TaskInfo taskInfo = iterator.next();
                     allTasksComplete = allTasksComplete && taskInfo.isComplete();
-                    if (taskInfo.isReady() && taskInfo.allDependenciesComplete() && !runningProjects.contains(taskInfo.getTask().getProject().getPath())) {
+                    if (taskInfo.isReady() && taskInfo.allDependenciesComplete() && canRunWithWithCurrentlyExecutedTasks(taskInfo)) {
                         nextMatching = taskInfo;
+                        iterator.remove();
                         break;
                     }
                 }
@@ -403,7 +492,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
                 } else {
                     if (nextMatching.allDependenciesSuccessful()) {
                         nextMatching.startExecution();
-                        runningProjects.add(nextMatching.getTask().getProject().getPath());
+                        recordTaskStarted(nextMatching);
                         return nextMatching;
                     } else {
                         nextMatching.skipExecution();
@@ -416,6 +505,136 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
+    private boolean canRunWithWithCurrentlyExecutedTasks(TaskInfo taskInfo) {
+        TaskInternal task = taskInfo.getTask();
+        String projectPath = task.getProject().getPath();
+
+        if (isParallelizable(task)) {
+            if (projectsWithRunningNonParallelizableTasks.contains(projectPath)) {
+                return false;
+            }
+        } else {
+            if (projectsWithRunningTasks.contains(projectPath)) {
+                return false;
+            }
+        }
+
+        Pair<TaskInternal, String> overlap = firstTaskWithOverlappingOutput(task);
+        if (overlap == null) {
+            return true;
+        } else {
+            LOGGER.info("Cannot execute task " + task.getPath() + " in parallel with task " + overlap.left.getPath() + " due to overlapping output: " + overlap.right);
+        }
+
+        return false;
+    }
+
+    private Set<String> canonicalizedOutputPaths(TaskInternal task) {
+        Set<String> paths = canonicalizedOutputCache.get(task);
+        if (paths == null) {
+            paths = Sets.newHashSet(Iterables.transform(task.getOutputs().getFiles(), new Function<File, String>() {
+                @Override
+                public String apply(File file) {
+                    String path;
+                    try {
+                        path = file.getCanonicalPath();
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                    return path;
+                }
+            }));
+            canonicalizedOutputCache.put(task, paths);
+        }
+
+        return paths;
+    }
+
+    @Nullable
+    private Pair<TaskInternal, String> firstTaskWithOverlappingOutput(TaskInternal candidateTask) {
+        if (runningTasks.isEmpty()) {
+            return null;
+        }
+
+        for (String candidateTaskOutputPath : canonicalizedOutputPaths(candidateTask)) {
+            for (TaskInternal runningTask : runningTasks) {
+                for (String runningTaskOutputPath : canonicalizedOutputPaths(runningTask)) {
+                    if (pathsOverlap(candidateTaskOutputPath, runningTaskOutputPath)) {
+                        return Pair.of(runningTask, TextUtil.shorterOf(candidateTaskOutputPath, runningTaskOutputPath));
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    private boolean pathsOverlap(String firstPath, String secondPath) {
+        if (firstPath.equals(secondPath)) {
+            return true;
+        }
+
+        String shorter;
+        String longer;
+        if (firstPath.length() > secondPath.length()) {
+            shorter = secondPath;
+            longer = firstPath;
+        } else {
+            shorter = firstPath;
+            longer = secondPath;
+        }
+        return longer.startsWith(shorter + StandardSystemProperty.FILE_SEPARATOR.value());
+    }
+
+    boolean isParallelizable(TaskInternal task) {
+        if (intraProjectParallelization) {
+            Boolean safe = isParallelSafeCache.get(task);
+            if (safe == null) {
+                safe = detectIsParallelizable(task);
+                isParallelSafeCache.put(task, safe);
+            }
+
+            return safe;
+        }
+
+        return false;
+    }
+
+    private boolean detectIsParallelizable(TaskInternal task) {
+        if (task.getClass().isAnnotationPresent(ParallelizableTask.class)) {
+            if (task.isHasCustomActions()) {
+                LOGGER.info("Unable to parallelize task " + task.getPath() + " due to presence of custom actions (e.g. doFirst()/doLast())");
+            } else {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void recordTaskStarted(TaskInfo taskInfo) {
+        TaskInternal task = taskInfo.getTask();
+        String projectPath = task.getProject().getPath();
+        if (!isParallelizable(task)) {
+            projectsWithRunningNonParallelizableTasks.add(projectPath);
+        }
+        projectsWithRunningTasks.add(projectPath);
+        runningTasks.add(task);
+    }
+
+    private void recordTaskCompleted(TaskInfo taskInfo) {
+        TaskInternal task = taskInfo.getTask();
+        String projectPath = task.getProject().getPath();
+        if (!isParallelizable(task)) {
+            projectsWithRunningNonParallelizableTasks.remove(projectPath);
+        }
+        projectsWithRunningTasks.remove(projectPath);
+        canonicalizedOutputCache.remove(task);
+        isParallelSafeCache.remove(task);
+        runningTasks.remove(task);
+    }
+
     public void taskComplete(TaskInfo taskInfo) {
         lock.lock();
         try {
@@ -425,7 +644,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
             }
 
             taskInfo.finishExecution();
-            runningProjects.remove(taskInfo.getTask().getProject().getPath());
+            recordTaskCompleted(taskInfo);
             condition.signalAll();
         } finally {
             lock.unlock();
@@ -434,7 +653,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
 
     private void enforceFinalizerTasks(TaskInfo taskInfo) {
         for (TaskInfo finalizerNode : taskInfo.getFinalizers()) {
-            if (finalizerNode.isRequired() || finalizerNode.getMustNotRun()) {
+            if (finalizerNode.isRequired() || finalizerNode.isMustNotRun()) {
                 enforceWithDependencies(finalizerNode);
             }
         }
@@ -444,7 +663,7 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         for (TaskInfo dependencyNode : node.getDependencySuccessors()) {
             enforceWithDependencies(dependencyNode);
         }
-        if (node.getMustNotRun() || node.isRequired()) {
+        if (node.isMustNotRun() || node.isRequired()) {
             node.enforceRun();
         }
     }
@@ -469,13 +688,16 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         }
     }
 
-    private void abortExecution() {
+    private boolean abortExecution() {
         // Allow currently executing and enforced tasks to complete, but skip everything else.
+        boolean aborted = false;
         for (TaskInfo taskInfo : executionPlan.values()) {
             if (taskInfo.isRequired()) {
                 taskInfo.skipExecution();
+                aborted = true;
             }
         }
+        return aborted;
     }
 
     public void awaitCompletion() {
@@ -495,6 +717,9 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
     }
 
     private void rethrowFailures() {
+        if (tasksCancelled) {
+            failures.add(new BuildCancelledException());
+        }
         if (failures.isEmpty()) {
             return;
         }
@@ -515,6 +740,26 @@ class DefaultTaskExecutionPlan implements TaskExecutionPlan {
         return true;
     }
 
+    private static class GraphEdge {
+        private final TaskInfo from;
+        private final TaskInfo to;
+
+        private GraphEdge(TaskInfo from, TaskInfo to) {
+            this.from = from;
+            this.to = to;
+        }
+    }
+
+    private static class TaskInfoInVisitingSegment {
+        private final TaskInfo taskInfo;
+        private final int visitingSegment;
+
+        private TaskInfoInVisitingSegment(TaskInfo taskInfo, int visitingSegment) {
+            this.taskInfo = taskInfo;
+            this.visitingSegment = visitingSegment;
+        }
+    }
+
     private static class RethrowingFailureHandler implements TaskFailureHandler {
         public void onTaskFailure(Task task) {
             task.getState().rethrowFailure();
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 5b319e8..81d696e 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
@@ -23,9 +23,10 @@ import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.specs.Spec;
 import org.gradle.execution.TaskFailureHandler;
 import org.gradle.execution.TaskGraphExecuter;
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.util.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +45,14 @@ public class DefaultTaskGraphExecuter implements TaskGraphExecuter {
     private final TaskPlanExecutor taskPlanExecutor;
     private final ListenerBroadcast<TaskExecutionGraphListener> graphListeners;
     private final ListenerBroadcast<TaskExecutionListener> taskListeners;
-    private final DefaultTaskExecutionPlan taskExecutionPlan = new DefaultTaskExecutionPlan();
+    private final DefaultTaskExecutionPlan taskExecutionPlan;
     private TaskGraphState taskGraphState = TaskGraphState.EMPTY;
 
-    public DefaultTaskGraphExecuter(ListenerManager listenerManager, TaskPlanExecutor taskPlanExecutor) {
+    public DefaultTaskGraphExecuter(ListenerManager listenerManager, TaskPlanExecutor taskPlanExecutor, BuildCancellationToken cancellationToken) {
         this.taskPlanExecutor = taskPlanExecutor;
         graphListeners = listenerManager.createAnonymousBroadcaster(TaskExecutionGraphListener.class);
         taskListeners = listenerManager.createAnonymousBroadcaster(TaskExecutionListener.class);
+        taskExecutionPlan = new DefaultTaskExecutionPlan(cancellationToken);
     }
 
     public void useFailureHandler(TaskFailureHandler handler) {
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 f49b834..c8559d7 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
@@ -16,18 +16,12 @@
 
 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.logging.Logger;
 import org.gradle.api.logging.Logging;
 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 java.util.concurrent.Executor;
 
 class ParallelTaskPlanExecutor extends AbstractTaskPlanExecutor {
@@ -56,22 +50,11 @@ class ParallelTaskPlanExecutor extends AbstractTaskPlanExecutor {
     }
 
     private void startAdditionalWorkers(TaskExecutionPlan taskExecutionPlan, TaskExecutionListener taskListener, Executor executor) {
-        List<Project> projects = getAllProjects(taskExecutionPlan);
-        int numExecutors = Math.min(executorCount, projects.size());
+        LOGGER.info("Using {} parallel executor threads", executorCount);
 
-        LOGGER.info("Using {} parallel executor threads", numExecutors);
-
-        for (int i = 1; i < numExecutors; i++) {
+        for (int i = 1; i < executorCount; i++) {
             Runnable worker = taskWorker(taskExecutionPlan, taskListener);
             executor.execute(worker);
         }
     }
-
-    private List<Project> getAllProjects(TaskExecutionPlan taskExecutionPlan) {
-        final Set<Project> uniqueProjects = new LinkedHashSet<Project>();
-        for (Task task : taskExecutionPlan.getTasks()) {
-            uniqueProjects.add(task.getProject());
-        }
-        return new ArrayList<Project>(uniqueProjects);
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphEdge.groovy b/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphEdge.groovy
deleted file mode 100644
index 9a84e4c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/TaskDependencyGraphEdge.groovy
+++ /dev/null
@@ -1,36 +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
-
-class TaskDependencyGraphEdge {
-
-    TaskInfo from
-    TaskInfo to
-
-    TaskDependencyGraphEdge(TaskInfo from, TaskInfo to) {
-        this.from = from
-        this.to = to
-    }
-
-    TaskInfo getFrom() {
-        return from
-    }
-
-    TaskInfo getTo() {
-        return to
-    }
-}
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 a9d448e..4c05e98 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
@@ -50,7 +50,7 @@ public class TaskInfo implements Comparable<TaskInfo> {
         return state == TaskExecutionState.SHOULD_RUN;
     }
 
-    public boolean getMustNotRun() {
+    public boolean isMustNotRun() {
         return state == TaskExecutionState.MUST_NOT_RUN;
     }
 
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 f074bec..8fee41e 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
@@ -51,4 +51,13 @@ public class ResolvedTaskPath {
         return project;
     }
 
+    @Override
+    public String toString() {
+        return "ResolvedTaskPath{"
+                + "prefix='" + prefix + '\''
+                + ", taskName='" + taskName + '\''
+                + ", project=" + project.getPath()
+                + ", isQualified=" + isQualified
+                + '}';
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/AbstractUriScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/AbstractUriScriptSource.java
new file mode 100644
index 0000000..35246e3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/AbstractUriScriptSource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.internal.hash.HashUtil;
+
+import java.net.URI;
+
+public abstract class AbstractUriScriptSource implements ScriptSource {
+
+    private String className;
+
+    /**
+     * Returns the class name for use for this script source.  The name is intended to be unique to support mapping
+     * class names to source files even if many sources have the same file name (e.g. build.gradle).
+     */
+    public String getClassName() {
+        if (className == null) {
+            URI sourceUri = getResource().getURI();
+            String name = StringUtils.substringBeforeLast(StringUtils.substringAfterLast(sourceUri.toString(), "/"), ".");
+            StringBuilder className = new StringBuilder(name.length());
+            for (int i = 0; i < name.length(); i++) {
+                char ch = name.charAt(i);
+                if (Character.isJavaIdentifierPart(ch)) {
+                    className.append(ch);
+                } else {
+                    className.append('_');
+                }
+            }
+            if (!Character.isJavaIdentifierStart(className.charAt(0))) {
+                className.insert(0, '_');
+            }
+            className.setLength(Math.min(className.length(), 30));
+            className.append('_');
+            String path = sourceUri.toString();
+            className.append(HashUtil.createCompactMD5(path));
+
+            this.className = className.toString();
+        }
+
+        return className;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/CachingScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/CachingScriptSource.java
index 8ee4c5d..5d274ba 100755
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/CachingScriptSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/CachingScriptSource.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.CachingResource;
-import org.gradle.api.internal.resource.Resource;
+import org.gradle.internal.resource.CachingResource;
+import org.gradle.internal.resource.Resource;
 
 public class CachingScriptSource extends DelegatingScriptSource {
     private Resource resource;
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 0bbaec6..16b5eb9 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
@@ -25,6 +25,7 @@ import org.gradle.api.file.ConfigurableFileTree;
 import org.gradle.api.file.CopySpec;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.internal.ClosureBackedAction;
 import org.gradle.api.internal.ProcessOperations;
 import org.gradle.api.internal.file.DefaultFileOperations;
 import org.gradle.api.internal.file.FileLookup;
@@ -42,8 +43,9 @@ import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 import org.gradle.util.ConfigureUtil;
-import org.gradle.util.DeprecationLogger;
 
 import java.io.File;
 import java.net.URI;
@@ -56,7 +58,6 @@ public abstract class DefaultScript extends BasicScript {
     private ProcessOperations processOperations;
     private LoggingManager loggingManager;
 
-    public static final String SCRIPT_SERVICES_PROPERTY = "__scriptServices";
     public ServiceRegistry __scriptServices;
 
     public void init(final Object target, ServiceRegistry services) {
@@ -81,9 +82,13 @@ public abstract class DefaultScript extends BasicScript {
     }
 
     private DefaultObjectConfigurationAction createObjectConfigurationAction() {
-        ClassLoaderScope classLoaderScope = __scriptServices.get(ClassLoaderScope.class).createChild();
+        ClassLoaderScope classLoaderScope = __scriptServices.get(ClassLoaderScope.class);
         return new DefaultObjectConfigurationAction(
-                getFileResolver(), __scriptServices.get(ScriptPluginFactory.class), __scriptServices.get(ScriptHandlerFactory.class), classLoaderScope, getScriptTarget()
+                getFileResolver(),
+                __scriptServices.get(ScriptPluginFactory.class),
+                __scriptServices.get(ScriptHandlerFactory.class),
+                classLoaderScope,
+                getScriptTarget()
         );
     }
 
@@ -124,7 +129,7 @@ public abstract class DefaultScript extends BasicScript {
     }
 
     public ConfigurableFileCollection files(Object paths, Closure configureClosure) {
-        return fileOperations.files(paths, configureClosure);
+        return ConfigureUtil.configure(configureClosure, fileOperations.files(paths));
     }
 
     public String relativePath(Object path) {
@@ -139,14 +144,8 @@ public abstract class DefaultScript extends BasicScript {
         return fileOperations.fileTree(args);
     }
 
-    public ConfigurableFileTree fileTree(Closure closure) {
-        DeprecationLogger.nagUserOfDeprecated("fileTree(Closure)", "Use fileTree((Object){ baseDir }) to have the closure used as the file tree base directory");
-        //noinspection deprecation
-        return fileOperations.fileTree(closure);
-    }
-
     public ConfigurableFileTree fileTree(Object baseDir, Closure configureClosure) {
-        return fileOperations.fileTree(baseDir, configureClosure);
+        return ConfigureUtil.configure(configureClosure, fileOperations.fileTree(baseDir));
     }
 
     public FileTree zipTree(Object zipPath) {
@@ -162,7 +161,11 @@ public abstract class DefaultScript extends BasicScript {
     }
 
     public WorkResult copy(Closure closure) {
-        return fileOperations.copy(closure);
+        return copy(new ClosureBackedAction<CopySpec>(closure));
+    }
+
+    public WorkResult copy(Action<? super CopySpec> action) {
+        return fileOperations.copy(action);
     }
 
     public WorkResult sync(Action<? super CopySpec> action) {
@@ -170,7 +173,7 @@ public abstract class DefaultScript extends BasicScript {
     }
 
     public CopySpec copySpec(Closure closure) {
-        return fileOperations.copySpec(closure);
+        return fileOperations.copySpec(new ClosureBackedAction<CopySpec>(closure));
     }
 
     public CopySpec copySpec(Action<? super CopySpec> action) {
@@ -186,11 +189,19 @@ public abstract class DefaultScript extends BasicScript {
     }
 
     public ExecResult javaexec(Closure closure) {
-        return processOperations.javaexec(closure);
+        return processOperations.javaexec(new ClosureBackedAction<JavaExecSpec>(closure));
+    }
+
+    public ExecResult javaexec(Action<? super JavaExecSpec> action) {
+        return processOperations.javaexec(action);
     }
 
     public ExecResult exec(Closure closure) {
-        return processOperations.exec(closure);
+        return processOperations.exec(new ClosureBackedAction<ExecSpec>(closure));
+    }
+
+    public ExecResult exec(Action<? super ExecSpec> action) {
+        return processOperations.exec(action);
     }
 
     public LoggingManager getLogging() {
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 fa34188..2c31a15 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,10 +15,14 @@
  */
 package org.gradle.groovy.scripts;
 
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.ClassLoaderIds;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
+import org.gradle.groovy.scripts.internal.CompiledScript;
+import org.gradle.groovy.scripts.internal.CompileOperation;
 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;
 
 public class DefaultScriptCompilerFactory implements ScriptCompilerFactory {
     private final ScriptRunnerFactory scriptRunnerFactory;
@@ -35,30 +39,16 @@ public class DefaultScriptCompilerFactory implements ScriptCompilerFactory {
 
     private class ScriptCompilerImpl implements ScriptCompiler {
         private final ScriptSource source;
-        private ClassLoader classloader;
-        private Transformer transformer;
-        private final Instantiator instantiator = new DirectInstantiator();
 
         public ScriptCompilerImpl(ScriptSource source) {
             this.source = new CachingScriptSource(source);
         }
 
-        public ScriptCompiler setClassloader(ClassLoader classloader) {
-            this.classloader = classloader;
-            return this;
-        }
-
-        public ScriptCompiler setTransformer(Transformer transformer) {
-            this.transformer = transformer;
-            return this;
-        }
-
-        public <T extends Script> ScriptRunner<T> compile(Class<T> scriptType) {
-            Class<? extends T> scriptClass = scriptClassCompiler.compile(source, classloader, transformer, scriptType);
-            T script = instantiator.newInstance(scriptClass);
-            script.setScriptSource(source);
-            script.setContextClassloader(classloader);
-            return scriptRunnerFactory.create(script);
+        @Override
+        public <T extends Script, M> ScriptRunner<T, M> compile(Class<T> scriptType, CompileOperation<M> extractingTransformer, ClassLoader classloader, String classpathClosureName, Action<? super ClassNode> verifier) {
+            ClassLoaderId classLoaderId = ClassLoaderIds.buildScript(source.getFileName(), extractingTransformer.getId());
+            CompiledScript<T, M> scriptClass = scriptClassCompiler.compile(source, classloader, classLoaderId, extractingTransformer, classpathClosureName, scriptType, verifier);
+            return scriptRunnerFactory.create(scriptClass, source, classloader);
         }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DelegatingScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DelegatingScriptSource.java
index 47592c9..c6a7c71 100755
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DelegatingScriptSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/DelegatingScriptSource.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.Resource;
+import org.gradle.internal.resource.Resource;
 
 public class DelegatingScriptSource implements ScriptSource {
     private final ScriptSource source;
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/NonExistentFileScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/NonExistentFileScriptSource.java
new file mode 100644
index 0000000..bf57e18
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/NonExistentFileScriptSource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts;
+
+import org.gradle.internal.resource.Resource;
+import org.gradle.internal.resource.UriResource;
+
+import java.io.File;
+
+public class NonExistentFileScriptSource extends AbstractUriScriptSource {
+
+    private final Resource resource;
+    private final File file;
+
+    public NonExistentFileScriptSource(String description, File file) {
+        this.resource = new EmptyFileResource(description, file);
+        this.file = file;
+    }
+
+    @Override
+    public Resource getResource() {
+        return resource;
+    }
+
+    @Override
+    public String getFileName() {
+        return file.getAbsolutePath();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return resource.getDisplayName();
+    }
+
+    private static class EmptyFileResource extends UriResource {
+        public EmptyFileResource(String description, File sourceFile) {
+            super(description, sourceFile);
+        }
+
+        @Override
+        public String getText() {
+            return "";
+        }
+
+        @Override
+        public boolean getExists() {
+            return false;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompiler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompiler.java
index a49297e..d5cfe91 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompiler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptCompiler.java
@@ -15,26 +15,20 @@
  */
 package org.gradle.groovy.scripts;
 
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.groovy.scripts.internal.CompileOperation;
+
 /**
  * Compiles a script into a {@code Script} object.
  */
 public interface ScriptCompiler {
-    /**
-     * Sets the parent classloader for the script. Can be null, defaults to the context classloader.
-     */
-    ScriptCompiler setClassloader(ClassLoader classloader);
-
-    /**
-     * Sets the transformer to use to compile the script. Can be null, in which case no transformations are applied to
-     * the script.
-     */
-    ScriptCompiler setTransformer(Transformer transformer);
 
     /**
-     * Compiles the script into a {@code Script} object of the given type. 
+     * Compiles the script into a {@code Script} object of the given type.
      *
-     * @returns a {@code ScriptRunner} for the script.
+     * @return a {@code ScriptRunner} for the script.
      * @throws ScriptCompilationException On compilation failure.
      */
-    <T extends Script> ScriptRunner<T> compile(Class<T> scriptType) throws ScriptCompilationException;
+    <T extends Script, M> ScriptRunner<T, M> compile(Class<T> scriptType, CompileOperation<M> extractingTransformer, ClassLoader classloader, String classpathClosureName, Action<? super ClassNode> verifier);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptRunner.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptRunner.java
index 2ff62f7..ecbf76c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptRunner.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptRunner.java
@@ -17,11 +17,12 @@ package org.gradle.groovy.scripts;
 
 import groovy.lang.Script;
 import org.gradle.api.GradleScriptException;
+import org.gradle.groovy.scripts.internal.CompiledScript;
 
 /**
  * Executes a script of type T.
  */
-public interface ScriptRunner<T extends Script> extends Runnable {
+public interface ScriptRunner<T extends Script, M> extends Runnable {
     /**
      * Returns the script which will be executed by this runner.
      *
@@ -29,6 +30,8 @@ public interface ScriptRunner<T extends Script> extends Runnable {
      */
     T getScript();
 
+    CompiledScript<T, M> getCompiledScript();
+
     /**
      * Executes the script.
      *
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptSource.java
index a975cf4..1012533 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/ScriptSource.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.Resource;
+import org.gradle.internal.resource.Resource;
 
 import java.io.Serializable;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/StringScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/StringScriptSource.java
index ebed4b9..dc60637 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/StringScriptSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/StringScriptSource.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.Resource;
-import org.gradle.api.internal.resource.StringResource;
+import org.gradle.internal.resource.Resource;
+import org.gradle.internal.resource.StringResource;
 import org.gradle.internal.hash.HashUtil;
 
 public class StringScriptSource implements ScriptSource {
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/Transformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/Transformer.java
deleted file mode 100644
index 78264cc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/Transformer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.groovy.scripts;
-
-import org.codehaus.groovy.control.CompilationUnit;
-
-public interface Transformer {
-    /**
-     * A unique id for this transformer. Used to distinguish between the classes compiled from the same script with
-     * different transformers, so should be a valid java identifier.
-     */
-    String getId();
-
-    void register(CompilationUnit compilationUnit);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/UriScriptSource.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/UriScriptSource.java
index 1a4b6b0..caf2b07 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/UriScriptSource.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/UriScriptSource.java
@@ -15,10 +15,8 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.internal.resource.Resource;
-import org.gradle.api.internal.resource.UriResource;
-import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.resource.Resource;
+import org.gradle.internal.resource.UriResource;
 
 import java.io.File;
 import java.net.URI;
@@ -26,9 +24,16 @@ import java.net.URI;
 /**
  * A {@link ScriptSource} which loads the script from a URI.
  */
-public class UriScriptSource implements ScriptSource {
+public class UriScriptSource extends AbstractUriScriptSource {
     private final Resource resource;
-    private String className;
+
+    public static ScriptSource file(String description, File sourceFile) {
+        if (sourceFile.exists()) {
+            return new UriScriptSource(description, sourceFile);
+        } else {
+            return new NonExistentFileScriptSource(description, sourceFile);
+        }
+    }
 
     public UriScriptSource(String description, File sourceFile) {
         resource = new UriResource(description, sourceFile);
@@ -38,37 +43,6 @@ public class UriScriptSource implements ScriptSource {
         resource = new UriResource(description, sourceUri);
     }
 
-    /**
-     * Returns the class name for use for this script source.  The name is intended to be unique to support mapping
-     * class names to source files even if many sources have the same file name (e.g. build.gradle).
-     */
-    public String getClassName() {
-        if (className == null) {
-            URI sourceUri = resource.getURI();
-            String name = StringUtils.substringBeforeLast(StringUtils.substringAfterLast(sourceUri.toString(), "/"), ".");
-            StringBuilder className = new StringBuilder(name.length());
-            for (int i = 0; i < name.length(); i++) {
-                char ch = name.charAt(i);
-                if (Character.isJavaIdentifierPart(ch)) {
-                    className.append(ch);
-                } else {
-                    className.append('_');
-                }
-            }
-            if (!Character.isJavaIdentifierStart(className.charAt(0))) {
-                className.insert(0, '_');
-            }
-            className.setLength(Math.min(className.length(), 30));
-            className.append('_');
-            String path = sourceUri.toString();
-            className.append(HashUtil.createCompactMD5(path));
-
-            this.className = className.toString();
-        }
-
-        return className;
-    }
-
     public Resource getResource() {
         return resource;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java
deleted file mode 100644
index 4ab1a6e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/AstUtils.java
+++ /dev/null
@@ -1,91 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.GroovyCodeVisitor;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.SourceUnit;
-
-import java.util.ListIterator;
-
-/**
- * Self contained utility functions for dealing with AST.
- */
-public abstract class AstUtils {
-
-    private AstUtils() {
-    }
-
-    public static boolean isMethodOnThis(MethodCallExpression call, String name) {
-        boolean hasName = call.getMethod() instanceof ConstantExpression && call.getMethod().getText().equals(name);
-        return hasName && targetIsThis(call);
-    }
-
-    public static boolean targetIsThis(MethodCallExpression call) {
-        Expression target = call.getObjectExpression();
-        return target instanceof VariableExpression && target.getText().equals("this");
-    }
-
-    public static void visitScriptCode(SourceUnit source, GroovyCodeVisitor transformer) {
-        source.getAST().getStatementBlock().visit(transformer);
-        for (Object method : source.getAST().getMethods()) {
-            MethodNode methodNode = (MethodNode) method;
-            methodNode.getCode().visit(transformer);
-        }
-    }
-
-    public static ClassNode getScriptClass(SourceUnit source) {
-        if (source.getAST().getStatementBlock().getStatements().isEmpty() && source.getAST().getMethods().isEmpty()) {
-            // There is no script class when there are no statements or methods declared in the script
-            return null;
-        }
-        return source.getAST().getClasses().get(0);
-    }
-
-    public static void removeMethod(ClassNode declaringClass, MethodNode methodNode) {
-        declaringClass.getMethods().remove(methodNode);
-        declaringClass.getDeclaredMethods(methodNode.getName()).clear();
-    }
-
-    public static void filterAndTransformStatements(SourceUnit source, StatementTransformer transformer) {
-        ListIterator<Statement> statementIterator = source.getAST().getStatementBlock().getStatements().listIterator();
-        while (statementIterator.hasNext()) {
-            Statement originalStatement = statementIterator.next();
-            Statement transformedStatement = transformer.transform(source, originalStatement);
-            if (transformedStatement == null) {
-                statementIterator.remove();
-            } else if (transformedStatement != originalStatement) {
-                statementIterator.set(transformedStatement);
-            }
-        }
-    }
-
-    public static boolean isVisible(SourceUnit source, String className) {
-        try {
-            source.getClassLoader().loadClass(className);
-            return true;
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformer.java
index d2ebe05..11c85d2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformer.java
@@ -15,27 +15,47 @@
  */
 package org.gradle.groovy.scripts.internal;
 
+import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.control.CompilationUnit;
+import org.gradle.api.specs.Spec;
+import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.Transformer;
+import org.gradle.internal.Factory;
+import org.gradle.model.dsl.internal.transform.ModelBlockTransformer;
 
-public class BuildScriptTransformer implements Transformer {
+import java.util.Arrays;
+import java.util.List;
 
-    private final String id;
-    private final Transformer extractionTransformer;
+public class BuildScriptTransformer implements Transformer, Factory<Boolean> {
 
-    public BuildScriptTransformer(String id, Transformer extractionTransformer) {
-        this.id = id;
-        this.extractionTransformer = extractionTransformer;
-    }
+    private final Spec<? super Statement> filter;
+    private final ScriptSource scriptSource;
+
+    private final ImperativeStatementDetectingTransformer imperativeStatementDetectingTransformer = new ImperativeStatementDetectingTransformer();
 
-    public String getId() {
-        return id;
+    public BuildScriptTransformer(String classpathClosureName, ScriptSource scriptSource) {
+        final List<String> blocksToIgnore = Arrays.asList(classpathClosureName, InitialPassStatementTransformer.PLUGINS);
+        this.filter = new Spec<Statement>() {
+            @Override
+            public boolean isSatisfiedBy(Statement statement) {
+                return AstUtils.detectScriptBlock(statement, blocksToIgnore) != null;
+            }
+        };
+        this.scriptSource = scriptSource;
     }
 
     public void register(CompilationUnit compilationUnit) {
-        extractionTransformer.register(compilationUnit);
+        new FilteringScriptTransformer(filter).register(compilationUnit);
         new TaskDefinitionScriptTransformer().register(compilationUnit);
         new FixMainScriptTransformer().register(compilationUnit); // TODO - remove this
         new StatementLabelsScriptTransformer().register(compilationUnit);
+        new ScriptSourceDescriptionTransformer(scriptSource.getDisplayName()).register(compilationUnit);
+        new ModelBlockTransformer().register(compilationUnit);
+        imperativeStatementDetectingTransformer.register(compilationUnit);
+    }
+
+    @Override
+    public Boolean create() {
+        return imperativeStatementDetectingTransformer.create();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompiler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompiler.java
index 871be5e..e6c0b08 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompiler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompiler.java
@@ -15,27 +15,73 @@
  */
 package org.gradle.groovy.scripts.internal;
 
+import com.google.common.collect.Maps;
 import groovy.lang.Script;
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.Transformer;
+import org.gradle.internal.Cast;
 
-import java.util.*;
+import java.util.Map;
 
 public class CachingScriptClassCompiler implements ScriptClassCompiler {
-    private final Map<Collection<Object>, Class<?>> cachedClasses = new HashMap<Collection<Object>, Class<?>>();
+    private final Map<Key, CompiledScript<?, ?>> cachedCompiledScripts = Maps.newHashMap();
     private final ScriptClassCompiler scriptClassCompiler;
 
     public CachingScriptClassCompiler(ScriptClassCompiler scriptClassCompiler) {
         this.scriptClassCompiler = scriptClassCompiler;
     }
 
-    public <T extends Script> Class<? extends T> compile(ScriptSource source, ClassLoader classLoader, Transformer transformer, Class<T> scriptBaseClass) {
-        List<Object> key = Arrays.asList(source.getClassName(), classLoader, transformer.getId(), scriptBaseClass.getName());
-        Class<?> c = cachedClasses.get(key);
-        if (c == null) {
-            c = scriptClassCompiler.compile(source, classLoader, transformer, scriptBaseClass);
-            cachedClasses.put(key, c);
+    @Override
+    public <T extends Script, M> CompiledScript<T, M> compile(ScriptSource source, ClassLoader classLoader, final ClassLoaderId classLoaderId, CompileOperation<M> operation, String classpathClosureName, Class<T> scriptBaseClass, Action<? super ClassNode> verifier) {
+        Key key = new Key(source.getClassName(), classLoader, operation.getId(), scriptBaseClass.getName());
+        CompiledScript<T, M> compiledScript = Cast.uncheckedCast(cachedCompiledScripts.get(key));
+        if (compiledScript == null) {
+            compiledScript = scriptClassCompiler.compile(source, classLoader, classLoaderId, operation, classpathClosureName, scriptBaseClass, verifier);
+            cachedCompiledScripts.put(key, compiledScript);
         }
-        return c.asSubclass(scriptBaseClass);
+        return compiledScript;
     }
+
+    private static class Key {
+        private final String className;
+        private final ClassLoader classLoader;
+        private final String transformerId;
+        private final String baseClassName;
+
+        public Key(String className, ClassLoader classLoader, String transformerId, String baseClassName) {
+            this.className = className;
+            this.classLoader = classLoader;
+            this.transformerId = transformerId;
+            this.baseClassName = baseClassName;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Key key = (Key) o;
+
+            return baseClassName.equals(key.baseClassName)
+                    && classLoader.equals(key.classLoader)
+                    && className.equals(key.className)
+                    && transformerId.equals(key.transformerId);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = className.hashCode();
+            result = 31 * result + classLoader.hashCode();
+            result = 31 * result + transformerId.hashCode();
+            result = 31 * result + baseClassName.hashCode();
+            return result;
+        }
+    }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClassCachingCompiledScript.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClassCachingCompiledScript.java
new file mode 100644
index 0000000..9055ebe
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ClassCachingCompiledScript.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import groovy.lang.Script;
+
+public class ClassCachingCompiledScript<T extends Script, M> implements CompiledScript<T, M> {
+
+    private final CompiledScript<T, M> delegate;
+    private Class<? extends T> scriptClass;
+
+    public ClassCachingCompiledScript(CompiledScript<T, M> delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Class<? extends T> loadClass() {
+        if (scriptClass == null) {
+            scriptClass = delegate.loadClass();
+        }
+        return scriptClass;
+    }
+
+    @Override
+    public M getData() {
+        return delegate.getData();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompileOperation.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompileOperation.java
new file mode 100644
index 0000000..998c6ab
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompileOperation.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.gradle.groovy.scripts.Transformer;
+import org.gradle.internal.serialize.Serializer;
+
+/**
+ * A stateful “backing” for a compilation operation.
+ * <p>
+ * The compilation may extract data from the source under compilation, made available after compilation by {@link #getExtractedData()}.
+ * The exposed transformer typically gathers the data while transforming.
+ * <p>
+ * As these objects are stateful, they can only be used for a single compile operation.
+ *
+ * @param <T> the type of data extracted by this operation
+ */
+public interface CompileOperation<T> {
+
+    /**
+     * A unique id for this operations.
+     * <p>
+     * Used to distinguish between the classes compiled from the same script with different transformers, so should be a valid java identifier.
+     */
+    String getId();
+
+    Transformer getTransformer();
+
+    T getExtractedData();
+
+    Serializer<T> getDataSerializer();
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompiledScript.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompiledScript.java
new file mode 100644
index 0000000..1328bd0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/CompiledScript.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import groovy.lang.Script;
+
+public interface CompiledScript<T extends Script, D> {
+
+    Class<? extends T> loadClass();
+
+    D getData();
+}
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 3b143e6..4bbe6f2 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
@@ -18,50 +18,68 @@ package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.GroovyClassLoader;
 import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyResourceLoader;
 import groovy.lang.Script;
 import groovyjarjarasm.asm.ClassWriter;
 import org.apache.commons.lang.StringUtils;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.Verifier;
 import org.codehaus.groovy.control.*;
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
 import org.codehaus.groovy.syntax.SyntaxException;
+import org.gradle.api.Action;
 import org.gradle.api.GradleException;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
+import org.gradle.configuration.ImportsReader;
 import org.gradle.groovy.scripts.ScriptCompilationException;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.Transformer;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
 import org.gradle.util.Clock;
 import org.gradle.util.GFileUtils;
-import org.gradle.util.WrapUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
+import java.io.*;
 import java.lang.reflect.Field;
-import java.net.URLClassLoader;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.security.CodeSource;
 import java.util.List;
 
 public class DefaultScriptCompilationHandler implements ScriptCompilationHandler {
     private Logger logger = LoggerFactory.getLogger(DefaultScriptCompilationHandler.class);
+    private static final NoOpGroovyResourceLoader NO_OP_GROOVY_RESOURCE_LOADER = new NoOpGroovyResourceLoader();
     private static final String EMPTY_SCRIPT_MARKER_FILE_NAME = "emptyScript.txt";
+    private static final String METADATA_FILE_NAME = "metadata.bin";
     private final EmptyScriptGenerator emptyScriptGenerator;
+    private final ClassLoaderCache classLoaderCache;
+    private final String[] defaultImportPackages;
 
-    public DefaultScriptCompilationHandler(EmptyScriptGenerator emptyScriptGenerator) {
+    public DefaultScriptCompilationHandler(EmptyScriptGenerator emptyScriptGenerator, ClassLoaderCache classLoaderCache, ImportsReader importsReader) {
         this.emptyScriptGenerator = emptyScriptGenerator;
+        this.classLoaderCache = classLoaderCache;
+        defaultImportPackages = importsReader.getImportPackages();
     }
 
-    public void compileToDir(ScriptSource source, ClassLoader classLoader, File classesDir,
-                             Transformer transformer, Class<? extends Script> scriptBaseClass) {
+    @Override
+    public void compileToDir(ScriptSource source, ClassLoader classLoader, File classesDir, File metadataDir, CompileOperation<?> extractingTransformer, String classpathClosureName,
+                             Class<? extends Script> scriptBaseClass, Action<? super ClassNode> verifier) {
         Clock clock = new Clock();
         GFileUtils.deleteDirectory(classesDir);
         GFileUtils.mkdirs(classesDir);
         CompilerConfiguration configuration = createBaseCompilerConfiguration(scriptBaseClass);
         configuration.setTargetDirectory(classesDir);
         try {
-            compileScript(source, classLoader, configuration, classesDir, transformer);
+            compileScript(source, classLoader, configuration, classesDir, metadataDir, extractingTransformer, verifier, classpathClosureName);
         } catch (GradleException e) {
             GFileUtils.deleteDirectory(classesDir);
             throw e;
@@ -71,8 +89,9 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
                 clock.getTime());
     }
 
-    private void compileScript(final ScriptSource source, ClassLoader classLoader, CompilerConfiguration configuration,
-                               File classesDir, final Transformer transformer) {
+    private void compileScript(final ScriptSource source, ClassLoader classLoader, CompilerConfiguration configuration, File classesDir, File metadataDir,
+                               final CompileOperation<?> extractingTransformer, final Action<? super ClassNode> customVerifier, String classpathClosureName) {
+        final Transformer transformer = extractingTransformer != null ? extractingTransformer.getTransformer() : null;
         logger.info("Compiling {} using {}.", source.getDisplayName(), transformer != null ? transformer.getClass().getSimpleName() : "no transformer");
 
         final EmptyScriptDetector emptyScriptDetector = new EmptyScriptDetector();
@@ -81,23 +100,11 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
             @Override
             protected CompilationUnit createCompilationUnit(CompilerConfiguration compilerConfiguration,
                                                             CodeSource codeSource) {
-                CompilationUnit compilationUnit = new CompilationUnit(compilerConfiguration, codeSource, this) {
-                    // This creepy bit of code is here to put the full source path of the script into the debug info for
-                    // the class.  This makes it possible for a debugger to find the source file for the class.  By default
-                    // Groovy will only put the filename into the class, but that does not help a debugger for Gradle
-                    // because it does not know where Gradle scripts might live.
-                    @Override
-                    protected groovyjarjarasm.asm.ClassVisitor createClassVisitor() {
-                        return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
-                            // ignore the sourcePath that is given by Groovy (this is only the filename) and instead
-                            // insert the full path if our script source has a source file
-                            @Override
-                            public void visitSource(String sourcePath, String debugInfo) {
-                                super.visitSource(source.getFileName(), debugInfo);
-                            }
-                        };
-                    }
-                };
+                ImportCustomizer customizer = new ImportCustomizer();
+                customizer.addStarImports(defaultImportPackages);
+                compilerConfiguration.addCompilationCustomizers(customizer);
+
+                CompilationUnit compilationUnit = new CustomCompilationUnit(compilerConfiguration, codeSource, customVerifier, source, this);
 
                 if (transformer != null) {
                     transformer.register(compilationUnit);
@@ -107,7 +114,10 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
                 compilationUnit.addPhaseOperation(emptyScriptDetector, Phases.CANONICALIZATION);
                 return compilationUnit;
             }
+
         };
+
+        groovyClassLoader.setResourceLoader(NO_OP_GROOVY_RESOURCE_LOADER);
         String scriptText = source.getResource().getText();
         String scriptName = source.getClassName();
         GroovyCodeSource codeSource = new GroovyCodeSource(scriptText == null ? "" : scriptText, scriptName, "/groovy/script");
@@ -126,6 +136,32 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
         if (emptyScriptDetector.isEmptyScript()) {
             GFileUtils.touch(new File(classesDir, EMPTY_SCRIPT_MARKER_FILE_NAME));
         }
+        serializeMetadata(source, extractingTransformer, metadataDir);
+    }
+
+    private <M> void serializeMetadata(ScriptSource scriptSource, CompileOperation<M> extractingTransformer, File metadataDir) {
+        if (extractingTransformer == null || extractingTransformer.getDataSerializer() == null) {
+            return;
+        }
+        GFileUtils.mkdirs(metadataDir);
+        File metadataFile = new File(metadataDir, METADATA_FILE_NAME);
+        FileOutputStream outputStream;
+        try {
+            outputStream = new FileOutputStream(metadataFile);
+        } catch (FileNotFoundException e) {
+            throw new UncheckedIOException("Could not create or open build script metadata file " + metadataFile.getAbsolutePath(), e);
+        }
+        KryoBackedEncoder encoder = new KryoBackedEncoder(outputStream);
+        Serializer<M> serializer = extractingTransformer.getDataSerializer();
+        try {
+            serializer.write(encoder, extractingTransformer.getExtractedData());
+        } catch (Exception e) {
+            String transformerName = extractingTransformer.getTransformer().getClass().getName();
+            throw new IllegalStateException(String.format("Failed to serialize script metadata extracted using %s for %s", transformerName, scriptSource.getDisplayName()), e);
+        } finally {
+            encoder.close();
+        }
+
     }
 
     private void wrapCompilationFailure(ScriptSource source, MultipleCompilationErrorsException e) {
@@ -158,22 +194,63 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
         return configuration;
     }
 
-    public <T extends Script> Class<? extends T> loadFromDir(ScriptSource source, ClassLoader classLoader, File scriptCacheDir,
-                                              Class<T> scriptBaseClass) {
-        if (new File(scriptCacheDir, EMPTY_SCRIPT_MARKER_FILE_NAME).isFile()) {
-            return emptyScriptGenerator.generate(scriptBaseClass);
+    public <T extends Script, M> CompiledScript<T, M> loadFromDir(final ScriptSource source, final ClassLoader classLoader, final File scriptCacheDir,
+                                                                  File metadataCacheDir, final CompileOperation<M> transformer, final Class<T> scriptBaseClass, final ClassLoaderId classLoaderId) {
+
+        final M metadata = deserializeMetadata(source, transformer, metadataCacheDir);
+
+        return new ClassCachingCompiledScript<T, M>(new CompiledScript<T, M>() {
+
+            @Override
+            public Class<? extends T> loadClass() {
+                if (new File(scriptCacheDir, EMPTY_SCRIPT_MARKER_FILE_NAME).isFile()) {
+                    classLoaderCache.remove(classLoaderId);
+                    return emptyScriptGenerator.generate(scriptBaseClass);
+                }
+
+                try {
+                    ClassLoader loader = classLoaderCache.get(classLoaderId, new DefaultClassPath(scriptCacheDir), classLoader, null);
+                    return loader.loadClass(source.getClassName()).asSubclass(scriptBaseClass);
+                } catch (Exception e) {
+                    File expectedClassFile = new File(scriptCacheDir, source.getClassName() + ".class");
+                    if (!expectedClassFile.exists()) {
+                        throw new GradleException(String.format("Could not load compiled classes for %s from cache. Expected class file %s does not exist.", source.getDisplayName(), expectedClassFile.getAbsolutePath()), e);
+                    }
+                    throw new GradleException(String.format("Could not load compiled classes for %s from cache.", source.getDisplayName()), e);
+                }
+            }
+
+            @Override
+            public M getData() {
+                return metadata;
+            }
+        });
+    }
+
+    private <M> M deserializeMetadata(ScriptSource scriptSource, CompileOperation<M> extractingTransformer, File metadataCacheDir) {
+        if (extractingTransformer == null || extractingTransformer.getDataSerializer() == null) {
+            return null;
         }
-        
+        File metadataFile = new File(metadataCacheDir, METADATA_FILE_NAME);
+        FileInputStream inputStream;
         try {
-            URLClassLoader urlClassLoader = new URLClassLoader(WrapUtil.toArray(scriptCacheDir.toURI().toURL()),
-                    classLoader);
-            return urlClassLoader.loadClass(source.getClassName()).asSubclass(scriptBaseClass);
+            inputStream = new FileInputStream(metadataFile);
+        } catch (FileNotFoundException e) {
+            throw new UncheckedIOException("Could not open build script metadata file " + metadataFile.getAbsolutePath(), e);
+        }
+        KryoBackedDecoder decoder = new KryoBackedDecoder(inputStream);
+        Serializer<M> serializer = extractingTransformer.getDataSerializer();
+        try {
+            return serializer.read(decoder);
         } catch (Exception e) {
-            File expectedClassFile = new File(scriptCacheDir, source.getClassName()+".class");
-            if(!expectedClassFile.exists()){
-                throw new GradleException(String.format("Could not load compiled classes for %s from cache. Expected class file %s does not exist.", source.getDisplayName(), expectedClassFile.getAbsolutePath()), e);
+            String transformerName = extractingTransformer.getTransformer().getClass().getName();
+            throw new IllegalStateException(String.format("Failed to deserialize script metadata extracted using %s for %s", transformerName, scriptSource.getDisplayName()), e);
+        } finally {
+            try {
+                decoder.close();
+            } catch (IOException e) {
+                throw new UncheckedIOException("Failed to close script metadata file decoder backed by " + metadataFile.getAbsolutePath(), e);
             }
-            throw new GradleException(String.format("Could not load compiled classes for %s from cache.", source.getDisplayName()), e);
         }
     }
 
@@ -206,22 +283,52 @@ public class DefaultScriptCompilationHandler implements ScriptCompilationHandler
                 return true;
             }
 
-            Statement statement = statements.get(0);
-            if (statement instanceof ReturnStatement) {
-                ReturnStatement returnStatement = (ReturnStatement) statement;
-                if (returnStatement.getExpression() instanceof ConstantExpression) {
-                    ConstantExpression constantExpression = (ConstantExpression) returnStatement.getExpression();
-                    if (constantExpression.getValue() == null) {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
+            return AstUtils.isReturnNullStatement(statements.get(0));
         }
 
         public boolean isEmptyScript() {
             return emptyScript;
         }
     }
+
+    private static class NoOpGroovyResourceLoader implements GroovyResourceLoader {
+        @Override
+        public URL loadGroovySource(String filename) throws MalformedURLException {
+            return null;
+        }
+    }
+
+    private class CustomCompilationUnit extends CompilationUnit {
+
+        private final ScriptSource source;
+
+        public CustomCompilationUnit(CompilerConfiguration compilerConfiguration, CodeSource codeSource, final Action<? super ClassNode> customVerifier, ScriptSource source, GroovyClassLoader groovyClassLoader) {
+            super(compilerConfiguration, codeSource, groovyClassLoader);
+            this.source = source;
+            this.verifier = new Verifier() {
+                public void visitClass(ClassNode node) {
+                    customVerifier.execute(node);
+                    super.visitClass(node);
+                }
+
+            };
+        }
+
+        // This creepy bit of code is here to put the full source path of the script into the debug info for
+        // the class.  This makes it possible for a debugger to find the source file for the class.  By default
+        // Groovy will only put the filename into the class, but that does not help a debugger for Gradle
+        // because it does not know where Gradle scripts might live.
+        @Override
+        protected groovyjarjarasm.asm.ClassVisitor createClassVisitor() {
+            return new ClassWriter(ClassWriter.COMPUTE_MAXS) {
+                @Override
+                public byte[] toByteArray() {
+                    // ignore the sourcePath that is given by Groovy (this is only the filename) and instead
+                    // insert the full path if our script source has a source file
+                    visitSource(source.getFileName(), null);
+                    return super.toByteArray();
+                }
+            };
+        }
+    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactory.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactory.java
index b74fb42..2f98d3e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactory.java
@@ -19,31 +19,53 @@ import org.gradle.api.GradleScriptException;
 import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptExecutionListener;
 import org.gradle.groovy.scripts.ScriptRunner;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.reflect.Instantiator;
 
 public class DefaultScriptRunnerFactory implements ScriptRunnerFactory {
     private final ScriptExecutionListener listener;
+    private final Instantiator instantiator;
 
-    public DefaultScriptRunnerFactory(ScriptExecutionListener listener) {
+    public DefaultScriptRunnerFactory(ScriptExecutionListener listener, Instantiator instantiator) {
         this.listener = listener;
+        this.instantiator = instantiator;
     }
 
-    public <T extends Script> ScriptRunner<T> create(T script) {
-        return new ScriptRunnerImpl<T>(script);
+    public <T extends Script, M> ScriptRunner<T, M> create(CompiledScript<T, M> script, ScriptSource source, ClassLoader contextClassLoader) {
+        return new ScriptRunnerImpl<T, M>(script, source, contextClassLoader);
     }
 
-    private class ScriptRunnerImpl<T extends Script> implements ScriptRunner<T> {
-        private final T script;
+    private class ScriptRunnerImpl<T extends Script, M> implements ScriptRunner<T, M> {
+        private final ScriptSource source;
+        private final ClassLoader contextClassLoader;
+        private T script;
+        private final CompiledScript<T, M> compiledScript;
 
-        public ScriptRunnerImpl(T script) {
-            this.script = script;
+        public ScriptRunnerImpl(CompiledScript<T, M> compiledScript, ScriptSource source, ClassLoader contextClassLoader) {
+            this.compiledScript = compiledScript;
+            this.source = source;
+            this.contextClassLoader= contextClassLoader;
         }
 
+        @Override
         public T getScript() {
+            if (script == null) {
+                script = instantiator.newInstance(compiledScript.loadClass());
+                script.setScriptSource(source);
+                script.setContextClassloader(contextClassLoader);
+            }
             return script;
         }
 
+        @Override
+        public CompiledScript<T, M> getCompiledScript() {
+            return compiledScript;
+        }
+
+        @Override
         public void run() throws GradleScriptException {
             ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
+            T script = getScript();
             listener.beforeScript(script);
             GradleScriptException failure = null;
             Thread.currentThread().setContextClassLoader(script.getContextClassloader());
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FactoryBackedCompileOperation.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FactoryBackedCompileOperation.java
new file mode 100644
index 0000000..f1dc624
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FactoryBackedCompileOperation.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.gradle.groovy.scripts.Transformer;
+import org.gradle.internal.Factory;
+import org.gradle.internal.serialize.Serializer;
+
+public class FactoryBackedCompileOperation<T> implements CompileOperation<T> {
+
+    private final String id;
+    private final Transformer transformer;
+    private final Factory<T> dataFactory;
+    private final Serializer<T> serializer;
+
+    public FactoryBackedCompileOperation(String id, Transformer transformer, Factory<T> dataFactory, Serializer<T> serializer) {
+        this.id = id;
+        this.transformer = transformer;
+        this.dataFactory = dataFactory;
+        this.serializer = serializer;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public Transformer getTransformer() {
+        return transformer;
+    }
+
+    @Override
+    public T getExtractedData() {
+        return dataFactory.create();
+    }
+
+    @Override
+    public Serializer<T> getDataSerializer() {
+        return serializer;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
index 2d31bc9..2443bbd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
@@ -16,17 +16,19 @@
 package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.Script;
+import org.codehaus.groovy.ast.ClassNode;
 import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.cache.CacheRepository;
 import org.gradle.cache.CacheValidator;
 import org.gradle.cache.PersistentCache;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.Transformer;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.hash.HashUtil;
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
 
+import java.io.Closeable;
 import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
@@ -34,39 +36,45 @@ import java.util.Map;
 /**
  * A {@link ScriptClassCompiler} which compiles scripts to a cache directory, and loads them from there.
  */
-public class FileCacheBackedScriptClassCompiler implements ScriptClassCompiler {
+public class FileCacheBackedScriptClassCompiler implements ScriptClassCompiler, Closeable {
     private final ScriptCompilationHandler scriptCompilationHandler;
-    private ProgressLoggerFactory progressLoggerFactory;
+    private final ProgressLoggerFactory progressLoggerFactory;
     private final CacheRepository cacheRepository;
     private final CacheValidator validator;
     private final CompositeStoppable caches = new CompositeStoppable();
 
-    public FileCacheBackedScriptClassCompiler(CacheRepository cacheRepository, CacheValidator validator, ScriptCompilationHandler scriptCompilationHandler, ProgressLoggerFactory progressLoggerFactory) {
+    public FileCacheBackedScriptClassCompiler(CacheRepository cacheRepository, CacheValidator validator, ScriptCompilationHandler scriptCompilationHandler,
+                                              ProgressLoggerFactory progressLoggerFactory) {
         this.cacheRepository = cacheRepository;
         this.validator = validator;
         this.scriptCompilationHandler = scriptCompilationHandler;
         this.progressLoggerFactory = progressLoggerFactory;
     }
 
-    public <T extends Script> Class<? extends T> compile(ScriptSource source, ClassLoader classLoader, Transformer transformer, Class<T> scriptBaseClass) {
+    @Override
+    public <T extends Script, M> CompiledScript<T, M> compile(final ScriptSource source, final ClassLoader classLoader, final ClassLoaderId classLoaderId, CompileOperation<M> operation, String classpathClosureName, final Class<T> scriptBaseClass,
+                                                              Action<? super ClassNode> verifier) {
         Map<String, Object> properties = new HashMap<String, Object>();
         properties.put("source.filename", source.getFileName());
         properties.put("source.hash", HashUtil.createCompactMD5(source.getResource().getText()));
 
-        String cacheName = String.format("scripts/%s/%s/%s", source.getClassName(), scriptBaseClass.getSimpleName(), transformer.getId());
+        String transformerId = operation.getId();
+        String cacheName = String.format("scripts/%s/%s/%s", source.getClassName(), scriptBaseClass.getSimpleName(), transformerId);
         PersistentCache cache = cacheRepository.cache(cacheName)
                 .withProperties(properties)
                 .withValidator(validator)
-                .withDisplayName(String.format("%s class cache for %s", transformer.getId(), source.getDisplayName()))
-                .withInitializer(new ProgressReportingInitializer(progressLoggerFactory, new CacheInitializer(source, classLoader, transformer, scriptBaseClass)))
+                .withDisplayName(String.format("%s class cache for %s", transformerId, source.getDisplayName()))
+                .withInitializer(new ProgressReportingInitializer(progressLoggerFactory, new CacheInitializer(source, classLoader, operation, classpathClosureName, verifier, scriptBaseClass)))
                 .open();
 
         // This isn't quite right. The cache will be closed at the end of the build, releasing the shared lock on the classes. Instead, the cache for a script should be
         // closed once we no longer require the script classes. This may be earlier than the end of the current build, or it may used across multiple builds
         caches.add(cache);
 
-        File classesDir = classesDir(cache);
-        return scriptCompilationHandler.loadFromDir(source, classLoader, classesDir, scriptBaseClass);
+        final File classesDir = classesDir(cache);
+        final File metadataDir = metadataDir(cache);
+
+        return scriptCompilationHandler.loadFromDir(source, classLoader, classesDir, metadataDir, operation, scriptBaseClass, classLoaderId);
     }
 
     public void close() {
@@ -77,22 +85,32 @@ public class FileCacheBackedScriptClassCompiler implements ScriptClassCompiler {
         return new File(cache.getBaseDir(), "classes");
     }
 
+    private File metadataDir(PersistentCache cache) {
+        return new File(cache.getBaseDir(), "metadata");
+    }
+
     private class CacheInitializer implements Action<PersistentCache> {
+        private final Action<? super ClassNode> verifier;
         private final Class<? extends Script> scriptBaseClass;
         private final ClassLoader classLoader;
-        private final Transformer transformer;
+        private final CompileOperation<?> transformer;
+        private final String classpathClosureName;
         private final ScriptSource source;
 
-        private CacheInitializer(ScriptSource source, ClassLoader classLoader, Transformer transformer, Class<? extends Script> scriptBaseClass) {
+        public <T extends Script> CacheInitializer(ScriptSource source, ClassLoader classLoader, CompileOperation<?> transformer, String classpathClosureName,
+                                                   Action<? super ClassNode> verifier, Class<T> scriptBaseClass) {
             this.source = source;
             this.classLoader = classLoader;
             this.transformer = transformer;
+            this.classpathClosureName = classpathClosureName;
+            this.verifier = verifier;
             this.scriptBaseClass = scriptBaseClass;
         }
 
         public void execute(PersistentCache cache) {
             File classesDir = classesDir(cache);
-            scriptCompilationHandler.compileToDir(source, classLoader, classesDir, transformer, scriptBaseClass);
+            File metadataDir = metadataDir(cache);
+            scriptCompilationHandler.compileToDir(source, classLoader, classesDir, metadataDir, transformer, classpathClosureName, scriptBaseClass, verifier);
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteredTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteredTransformer.java
deleted file mode 100644
index fe07df9..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteredTransformer.java
+++ /dev/null
@@ -1,28 +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.groovy.scripts.internal;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.specs.Spec;
-
-public interface FilteredTransformer<R, T> {
-
-    Spec<T> getSpec();
-
-    Transformer<R, T> getTransformer();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringScriptTransformer.java
new file mode 100644
index 0000000..51d4a1d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringScriptTransformer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.api.specs.Spec;
+
+import java.util.ListIterator;
+
+public class FilteringScriptTransformer extends AbstractScriptTransformer {
+
+    private final Spec<? super Statement> spec;
+
+    public FilteringScriptTransformer(Spec<? super Statement> spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    protected int getPhase() {
+        return Phases.CONVERSION;
+    }
+
+    @Override
+    public void call(SourceUnit source) throws CompilationFailedException {
+        ListIterator<Statement> iterator = source.getAST().getStatementBlock().getStatements().listIterator();
+        while (iterator.hasNext()) {
+            if (spec.isSatisfiedBy(iterator.next())) {
+                iterator.remove();
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringStatementTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringStatementTransformer.java
deleted file mode 100644
index 615a445..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FilteringStatementTransformer.java
+++ /dev/null
@@ -1,43 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.SourceUnit;
-import org.gradle.api.specs.Spec;
-
-public class FilteringStatementTransformer implements StatementTransformer {
-
-    private final Spec<? super Statement> spec;
-
-    public FilteringStatementTransformer(Spec<? super Statement> spec) {
-        this.spec = spec;
-    }
-
-    public Statement transform(SourceUnit sourceUnit, Statement statement) {
-        if (spec.isSatisfiedBy(statement)) {
-            return statement;
-        } else {
-            return null;
-        }
-    }
-
-    public Spec<? super Statement> getSpec() {
-        return spec;
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FixMainScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FixMainScriptTransformer.java
index d058272..fce72cb 100755
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FixMainScriptTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/FixMainScriptTransformer.java
@@ -26,10 +26,6 @@ import org.codehaus.groovy.control.SourceUnit;
  * the static method.
  */
 public class FixMainScriptTransformer extends AbstractScriptTransformer {
-    public String getId() {
-        return "fixMain";
-    }
-
     @Override
     protected int getPhase() {
         return Phases.CONVERSION;
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ImperativeStatementDetectingTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ImperativeStatementDetectingTransformer.java
new file mode 100644
index 0000000..273cd6c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ImperativeStatementDetectingTransformer.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.internal.Factory;
+import org.gradle.model.dsl.internal.transform.ModelBlockTransformer;
+
+import java.util.List;
+
+public class ImperativeStatementDetectingTransformer extends AbstractScriptTransformer implements Factory<Boolean>, GroovyCodeVisitor {
+
+    private boolean imperativeStatementDetected;
+
+    @Override
+    public void register(CompilationUnit compilationUnit) {
+        super.register(compilationUnit);
+    }
+
+    @Override
+    protected int getPhase() {
+        return Phases.CANONICALIZATION;
+    }
+
+    @Override
+    public Boolean create() {
+        return imperativeStatementDetected;
+    }
+
+    @Override
+    public void call(SourceUnit source) throws CompilationFailedException {
+        if (!source.getAST().getMethods().isEmpty()) {
+            imperativeStatementDetected = true;
+            return;
+        }
+
+        BlockStatement statementBlock = source.getAST().getStatementBlock();
+        List<Statement> statements = statementBlock.getStatements();
+        if (statements.size() == 1 && AstUtils.isReturnNullStatement(statements.get(0))) {
+            return;
+        }
+
+        for (int i = 0; i < statements.size() && !imperativeStatementDetected; i++) {
+            statements.get(i).visit(this);
+        }
+    }
+
+
+    @Override
+    public void visitBlockStatement(BlockStatement block) {
+        for (Statement statement : block.getStatements()) {
+            statement.visit(this);
+        }
+    }
+
+    @Override
+    public void visitForLoop(ForStatement forLoop) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitWhileLoop(WhileStatement loop) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitIfElse(IfStatement ifElse) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    @Override
+    public void visitReturnStatement(ReturnStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitAssertStatement(AssertStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitTryCatchFinally(TryCatchStatement finally1) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitSwitch(SwitchStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitCaseStatement(CaseStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitBreakStatement(BreakStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitContinueStatement(ContinueStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitThrowStatement(ThrowStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitSynchronizedStatement(SynchronizedStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitCatchStatement(CatchStatement statement) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        String methodName = AstUtils.extractConstantMethodName(call);
+        if (methodName == null) {
+            imperativeStatementDetected = true;
+            return;
+        }
+
+        ClosureExpression closureExpression = AstUtils.getSingleClosureArg(call);
+        if (closureExpression == null || !methodName.equals(ModelBlockTransformer.MODEL)) {
+            imperativeStatementDetected = true;
+        }
+    }
+
+    @Override
+    public void visitStaticMethodCallExpression(StaticMethodCallExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitConstructorCallExpression(ConstructorCallExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitTernaryExpression(TernaryExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitShortTernaryExpression(ElvisOperatorExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitBinaryExpression(BinaryExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitPrefixExpression(PrefixExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitPostfixExpression(PostfixExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitBooleanExpression(BooleanExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitClosureExpression(ClosureExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitTupleExpression(TupleExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitMapExpression(MapExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitMapEntryExpression(MapEntryExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitListExpression(ListExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitRangeExpression(RangeExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitPropertyExpression(PropertyExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitAttributeExpression(AttributeExpression attributeExpression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitFieldExpression(FieldExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitMethodPointerExpression(MethodPointerExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitConstantExpression(ConstantExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitClassExpression(ClassExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitVariableExpression(VariableExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+    }
+
+    @Override
+    public void visitGStringExpression(GStringExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitArrayExpression(ArrayExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitSpreadExpression(SpreadExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitSpreadMapExpression(SpreadMapExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitNotExpression(NotExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitCastExpression(CastExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitArgumentlistExpression(ArgumentListExpression expression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitClosureListExpression(ClosureListExpression closureListExpression) {
+        imperativeStatementDetected = true;
+    }
+
+    @Override
+    public void visitBytecodeExpression(BytecodeExpression expression) {
+        imperativeStatementDetected = true;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java
new file mode 100644
index 0000000..2b8bf05
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.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.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.Factory;
+import org.gradle.plugin.use.internal.PluginRequests;
+import org.gradle.plugin.use.internal.PluginUseScriptBlockMetadataExtractor;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class InitialPassStatementTransformer implements StatementTransformer, Factory<PluginRequests> {
+
+    public static final String PLUGINS = "plugins";
+
+    private final String classpathBlockName;
+    private final String pluginsBlockMessage;
+
+    private final PluginUseScriptBlockMetadataExtractor pluginBlockMetadataExtractor;
+    private boolean seenNonClasspathStatement;
+    private boolean seenPluginsBlock;
+    private final List<String> scriptBlockNames;
+
+    public InitialPassStatementTransformer(String classpathBlockName, String pluginsBlockMessage,
+                                           ScriptSource scriptSource, DocumentationRegistry documentationRegistry) {
+        this.classpathBlockName = classpathBlockName;
+        this.scriptBlockNames = Arrays.asList(classpathBlockName, PLUGINS);
+        this.pluginsBlockMessage = pluginsBlockMessage;
+        this.pluginBlockMetadataExtractor = new PluginUseScriptBlockMetadataExtractor(scriptSource, documentationRegistry);
+    }
+
+    public Statement transform(SourceUnit sourceUnit, Statement statement) {
+        ScriptBlock scriptBlock = AstUtils.detectScriptBlock(statement, scriptBlockNames);
+        if (scriptBlock == null) {
+            seenNonClasspathStatement = true;
+            return null;
+        } else {
+            if (scriptBlock.getName().equals(PLUGINS)) {
+                String failMessage = null;
+
+                if (pluginsBlockMessage != null) {
+                    failMessage = pluginBlockMetadataExtractor.formatErrorMessage(pluginsBlockMessage);
+                } else {
+                    seenPluginsBlock = true;
+                    if (seenNonClasspathStatement) {
+                        failMessage = String.format(
+                                pluginBlockMetadataExtractor.formatErrorMessage("only %s {} and other %s {} script blocks are allowed before %s {} blocks, no other statements are allowed"),
+                                classpathBlockName, PLUGINS, PLUGINS
+                        );
+                    } else {
+                        pluginBlockMetadataExtractor.extract(sourceUnit, scriptBlock);
+                    }
+                }
+
+                if (failMessage != null) {
+                    sourceUnit.getErrorCollector().addError(
+                            new SyntaxException(failMessage, statement.getLineNumber(), statement.getColumnNumber()),
+                            sourceUnit
+                    );
+                }
+
+                return null;
+            } else {
+                if (seenPluginsBlock) {
+                    String message = String.format(
+                            pluginBlockMetadataExtractor.formatErrorMessage("all %s {} blocks must appear before any %s {} blocks in the script"),
+                            classpathBlockName, PLUGINS
+                    );
+                    sourceUnit.getErrorCollector().addError(
+                            new SyntaxException(message, statement.getLineNumber(), statement.getColumnNumber()),
+                            sourceUnit
+                    );
+                }
+                return statement; // don't transform classpathBlockName
+            }
+        }
+    }
+
+    @Override
+    public PluginRequests create() {
+        return pluginBlockMetadataExtractor.getRequests();
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/PluginsAndBuildscriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/PluginsAndBuildscriptTransformer.java
deleted file mode 100644
index 6b6d733..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/PluginsAndBuildscriptTransformer.java
+++ /dev/null
@@ -1,127 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.syntax.SyntaxException;
-import org.gradle.api.specs.Spec;
-import org.gradle.groovy.scripts.DefaultScript;
-import org.gradle.plugin.PluginHandler;
-
-public class PluginsAndBuildscriptTransformer implements StatementTransformer {
-
-    private static final String PLUGINS = "plugins";
-    private static final ScriptBlockToServiceConfigurationTransformer PLUGIN_BLOCK_TRANSFORMER = new ScriptBlockToServiceConfigurationTransformer(DefaultScript.SCRIPT_SERVICES_PROPERTY, PluginHandler.class);
-
-    private final String classpathBlockName;
-    private boolean seenNonClasspathStatement;
-    private boolean seenPluginsBlock;
-
-    public PluginsAndBuildscriptTransformer(String classpathBlockName) {
-        this.classpathBlockName = classpathBlockName;
-    }
-
-    public Statement transform(SourceUnit sourceUnit, Statement statement) {
-        ScriptBlock scriptBlock = detectScriptBlock(statement);
-        if (scriptBlock == null) {
-            seenNonClasspathStatement = true;
-            return null;
-        } else {
-            if (scriptBlock.getName().equals(PLUGINS)) {
-                seenPluginsBlock = true;
-                if (seenNonClasspathStatement) {
-                    String message = String.format(
-                            "only %s {} and and other %s {} script blocks are allowed before %s {} blocks, no other statements are allowed",
-                            classpathBlockName, PLUGINS, PLUGINS
-                    );
-                    sourceUnit.getErrorCollector().addError(
-                            new SyntaxException(message, statement.getLineNumber(), statement.getColumnNumber()),
-                            sourceUnit
-                    );
-                    return statement;
-                } else {
-                    return PLUGIN_BLOCK_TRANSFORMER.transform(scriptBlock);
-                }
-            } else {
-                if (seenPluginsBlock) {
-                    String message = String.format(
-                            "all %s {} blocks must appear before any %s {} blocks in the script",
-                            classpathBlockName, PLUGINS
-                    );
-                    sourceUnit.getErrorCollector().addError(
-                            new SyntaxException(message, statement.getLineNumber(), statement.getColumnNumber()),
-                            sourceUnit
-                    );
-                }
-                return statement; // don't transform classpathBlockName
-            }
-        }
-    }
-
-    public Spec<Statement> getSpec() {
-        return new Spec<Statement>() {
-            public boolean isSatisfiedBy(Statement statement) {
-                return detectScriptBlock(statement) != null;
-            }
-        };
-    }
-
-    // returns null if the statement is not a script block
-    private ScriptBlock detectScriptBlock(Statement statement) {
-        if (!(statement instanceof ExpressionStatement)) {
-            return null;
-        }
-
-        ExpressionStatement expressionStatement = (ExpressionStatement) statement;
-        if (!(expressionStatement.getExpression() instanceof MethodCallExpression)) {
-            return null;
-        }
-
-        MethodCallExpression methodCall = (MethodCallExpression) expressionStatement.getExpression();
-        if (!AstUtils.targetIsThis(methodCall)) {
-            return null;
-        }
-
-        if (!(methodCall.getMethod() instanceof ConstantExpression)) {
-            return null;
-        }
-
-        String methodName = methodCall.getMethod().getText();
-
-        if (methodName.equals(PLUGINS) || methodName.equals(classpathBlockName)) {
-            if (!(methodCall.getArguments() instanceof ArgumentListExpression)) {
-                return null;
-            }
-
-            ArgumentListExpression args = (ArgumentListExpression) methodCall.getArguments();
-            if (args.getExpressions().size() == 1 && args.getExpression(0) instanceof ClosureExpression) {
-                return new ScriptBlock(methodName, (ClosureExpression) args.getExpression(0));
-            } else {
-                return null;
-            }
-        } else {
-            return null;
-        }
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.java
deleted file mode 100644
index 3ac1f4d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlock.java
+++ /dev/null
@@ -1,37 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-
-class ScriptBlock {
-    private final String name;
-    private final ClosureExpression closureExpression;
-
-    ScriptBlock(String name, ClosureExpression closureExpression) {
-        this.name = name;
-        this.closureExpression = closureExpression;
-    }
-
-    String getName() {
-        return name;
-    }
-
-    ClosureExpression getClosureExpression() {
-        return closureExpression;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlockToServiceConfigurationTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlockToServiceConfigurationTransformer.java
deleted file mode 100644
index 9be89dd..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptBlockToServiceConfigurationTransformer.java
+++ /dev/null
@@ -1,54 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.gradle.api.Transformer;
-
-public class ScriptBlockToServiceConfigurationTransformer implements Transformer<Statement, ScriptBlock> {
-
-    private final String servicesFieldName;
-    private final Class<?> serviceClass;
-
-    public ScriptBlockToServiceConfigurationTransformer(String servicesFieldName, Class<?> serviceClass) {
-        this.servicesFieldName = servicesFieldName;
-        this.serviceClass = serviceClass;
-    }
-
-    public Statement transform(ScriptBlock scriptBlock) {
-        Expression closureArg = scriptBlock.getClosureExpression();
-
-        PropertyExpression servicesProperty = new PropertyExpression(VariableExpression.THIS_EXPRESSION, servicesFieldName);
-        MethodCallExpression getServiceMethodCall = new MethodCallExpression(servicesProperty, "get",
-                new ArgumentListExpression(
-                        new ClassExpression(new ClassNode(serviceClass))
-                )
-        );
-
-        // Remove access to any surrounding context
-        Expression hydrateMethodCall = new MethodCallExpression(closureArg, "rehydrate", new ArgumentListExpression(
-                ConstantExpression.NULL, getServiceMethodCall, getServiceMethodCall
-        ));
-
-        Expression closureCall = new MethodCallExpression(hydrateMethodCall, "call", ArgumentListExpression.EMPTY_ARGUMENTS);
-
-        return new ExpressionStatement(closureCall);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptClassCompiler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptClassCompiler.java
index de621d4..8c2db37 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptClassCompiler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptClassCompiler.java
@@ -15,9 +15,13 @@
  */
 package org.gradle.groovy.scripts.internal;
 
+import groovy.lang.Script;
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.Transformer;
 
 public interface ScriptClassCompiler {
-    <T extends groovy.lang.Script> Class<? extends T> compile(ScriptSource source, ClassLoader classLoader, Transformer transformer, Class<T> scriptBaseClass);
+    <T extends Script, M> CompiledScript<T, M> compile(ScriptSource source, ClassLoader classLoader, ClassLoaderId classLoaderId, CompileOperation<M> transformer, String classpathClosureName, Class<T> scriptBaseClass,
+                                                       Action<? super ClassNode> verifier);
 }
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 3642cfc..02305c9 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
@@ -16,15 +16,18 @@
 package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.Script;
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.Transformer;
 
 import java.io.File;
 
 public interface ScriptCompilationHandler {
-    void compileToDir(ScriptSource source, ClassLoader classLoader, File scriptCacheDir, Transformer transformer,
-                      Class<? extends Script> scriptBaseClass);
 
-    <T extends Script> Class<? extends T> loadFromDir(ScriptSource source, ClassLoader classLoader, File scriptCacheDir,
-                                       Class<T> scriptBaseClass);
+    void compileToDir(ScriptSource source, ClassLoader classLoader, File classesDir, File metadataDir, CompileOperation<?> transformer, String classpathClosureName,
+                      Class<? extends Script> scriptBaseClass, Action<? super ClassNode> verifier);
+
+    <T extends Script, M> CompiledScript<T, M> loadFromDir(ScriptSource source, ClassLoader classLoader, File scriptCacheDir,
+                                                           File metadataCacheDir, CompileOperation<M> transformer, Class<T> scriptBaseClass, final ClassLoaderId classLoaderId);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptRunnerFactory.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptRunnerFactory.java
index 4fa2b2b..5c69d0c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptRunnerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ScriptRunnerFactory.java
@@ -17,7 +17,8 @@ package org.gradle.groovy.scripts.internal;
 
 import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptRunner;
+import org.gradle.groovy.scripts.ScriptSource;
 
 public interface ScriptRunnerFactory {
-    <T extends Script> ScriptRunner<T> create(T script);
+    <T extends Script, M> ScriptRunner<T, M> create(CompiledScript<T, M> scriptClass, ScriptSource source, ClassLoader contextClassLoader);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompiler.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompiler.java
index 483ece2..b04d4ef 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompiler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompiler.java
@@ -16,22 +16,41 @@
 package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.Script;
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.groovy.scripts.Transformer;
 
 public class ShortCircuitEmptyScriptCompiler implements ScriptClassCompiler {
     private final ScriptClassCompiler compiler;
     private final EmptyScriptGenerator emptyScriptGenerator;
+    private final ClassLoaderCache classLoaderCache;
 
-    public ShortCircuitEmptyScriptCompiler(ScriptClassCompiler compiler, EmptyScriptGenerator emptyScriptGenerator) {
+    public ShortCircuitEmptyScriptCompiler(ScriptClassCompiler compiler, EmptyScriptGenerator emptyScriptGenerator, ClassLoaderCache classLoaderCache) {
         this.compiler = compiler;
         this.emptyScriptGenerator = emptyScriptGenerator;
+        this.classLoaderCache = classLoaderCache;
     }
 
-    public <T extends Script> Class<? extends T> compile(ScriptSource source, ClassLoader classLoader, Transformer transformer, Class<T> scriptBaseClass) {
+    @Override
+    public <T extends Script, M> CompiledScript<T, M> compile(final ScriptSource source, final ClassLoader classLoader, final ClassLoaderId classLoaderId, final CompileOperation<M> operation, String classpathClosureName,
+                                                              final Class<T> scriptBaseClass, Action<? super ClassNode> verifier) {
         if (source.getResource().getText().matches("\\s*")) {
-            return emptyScriptGenerator.generate(scriptBaseClass);
+            return new ClassCachingCompiledScript<T, M>(new CompiledScript<T, M>() {
+
+                public Class<? extends T> loadClass() {
+                    classLoaderCache.remove(classLoaderId);
+                    return emptyScriptGenerator.generate(scriptBaseClass);
+                }
+
+                @Override
+                public M getData() {
+                    return operation.getExtractedData();
+                }
+            });
         }
-        return compiler.compile(source, classLoader, transformer, scriptBaseClass);
+        return compiler.compile(source, classLoader, classLoaderId, operation, classpathClosureName, scriptBaseClass, verifier);
     }
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementExtractingScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementExtractingScriptTransformer.java
deleted file mode 100644
index 36a75c3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementExtractingScriptTransformer.java
+++ /dev/null
@@ -1,145 +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.groovy.scripts.internal;
-
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.ImportNode;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.CompilationFailedException;
-import org.codehaus.groovy.control.Phases;
-import org.codehaus.groovy.control.SourceUnit;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
-import org.gradle.groovy.scripts.Transformer;
-import org.gradle.internal.UncheckedException;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Excludes everything from a script except statements that are satisfied by a given predicate, and imports for accessible classes. <p> *All* other kinds of constructs are filtered, including:
- * classes, methods etc.
- */
-public class StatementExtractingScriptTransformer extends AbstractScriptTransformer {
-
-    private final String id;
-    private final StatementTransformer transformer;
-
-    public StatementExtractingScriptTransformer(String id, StatementTransformer transformer) {
-        this.id = id;
-        this.transformer = transformer;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    protected int getPhase() {
-        return Phases.CONVERSION;
-    }
-
-    public void call(SourceUnit source) throws CompilationFailedException {
-        AstUtils.filterAndTransformStatements(source, transformer);
-
-        // Filter imported classes which are not available yet
-
-        Iterator<ImportNode> iter = source.getAST().getImports().iterator();
-        while (iter.hasNext()) {
-            ImportNode importedClass = iter.next();
-            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
-                try {
-                    Field field = ModuleNode.class.getDeclaredField("imports");
-                    field.setAccessible(true);
-                    Map value = (Map) field.get(source.getAST());
-                    value.remove(importedClass.getAlias());
-                } catch (Exception e) {
-                    throw UncheckedException.throwAsUncheckedException(e);
-                }
-            }
-        }
-
-        iter = source.getAST().getStaticImports().values().iterator();
-        while (iter.hasNext()) {
-            ImportNode importedClass = iter.next();
-            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
-                iter.remove();
-            }
-        }
-
-        iter = source.getAST().getStaticStarImports().values().iterator();
-        while (iter.hasNext()) {
-            ImportNode importedClass = iter.next();
-            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
-                iter.remove();
-            }
-        }
-
-        ClassNode scriptClass = AstUtils.getScriptClass(source);
-
-        // Remove all the classes other than the main class
-        Iterator<ClassNode> classes = source.getAST().getClasses().iterator();
-        while (classes.hasNext()) {
-            ClassNode classNode = classes.next();
-            if (classNode != scriptClass) {
-                classes.remove();
-            }
-        }
-
-        // Remove all the methods from the main class
-        if (scriptClass != null) {
-            for (MethodNode methodNode : new ArrayList<MethodNode>(scriptClass.getMethods())) {
-                if (!methodNode.getName().equals("run")) {
-                    AstUtils.removeMethod(scriptClass, methodNode);
-                }
-            }
-        }
-
-        source.getAST().getMethods().clear();
-    }
-
-    public Transformer invert() {
-        return new Inverse("no_" + StatementExtractingScriptTransformer.this.getId(), Specs.not(transformer.getSpec()));
-    }
-
-    private static class Inverse extends AbstractScriptTransformer {
-        private final String id;
-        private final Spec<? super Statement> spec;
-
-        private Inverse(String id, Spec<? super Statement> spec) {
-            this.id = id;
-            this.spec = spec;
-        }
-
-        protected int getPhase() {
-            return Phases.CANONICALIZATION;
-        }
-
-        public String getId() {
-            return id;
-        }
-
-        @Override
-        public void call(SourceUnit source) throws CompilationFailedException {
-            AstUtils.filterAndTransformStatements(source, new FilteringStatementTransformer(spec));
-        }
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsDeprecationLogger.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsDeprecationLogger.java
deleted file mode 100644
index eade02d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsDeprecationLogger.java
+++ /dev/null
@@ -1,34 +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.groovy.scripts.internal;
-
-import org.gradle.util.DeprecationLogger;
-
-/**
- * Used by {@link StatementLabelsScriptTransformer} which weaves calls to the log() method into build scripts.
- */
-public class StatementLabelsDeprecationLogger {
-    public static void log(String label, String sample) {
-        DeprecationLogger.nagUserOfDeprecated(
-                "Usage of statement labels in build scripts",
-                String.format(
-                        "In case you tried to configure a property named '%s', replace ':' with '=' or ' '. Otherwise it will not have the desired effect. \n\n%s",
-                        label, sample
-                )
-        );
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsScriptTransformer.java
index c6631f4..a93ab04 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsScriptTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementLabelsScriptTransformer.java
@@ -16,32 +16,20 @@
 
 package org.gradle.groovy.scripts.internal;
 
-import com.google.common.collect.Lists;
 import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
-import org.codehaus.groovy.ast.ClassHelper;
-import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.control.CompilationFailedException;
 import org.codehaus.groovy.control.Phases;
 import org.codehaus.groovy.control.SourceUnit;
 
-import java.util.List;
-
 public class StatementLabelsScriptTransformer extends AbstractScriptTransformer {
     @Override
     protected int getPhase() {
         return Phases.CANONICALIZATION;
     }
 
-    public String getId() {
-        return "labels";
-    }
-
     @Override
     public void call(final SourceUnit source) throws CompilationFailedException {
-        final List<Statement> logStats = Lists.newArrayList();
-
         // currently we only look in script code; could extend this to build script classes
         AstUtils.visitScriptCode(source, new ClassCodeVisitorSupport() {
             @Override
@@ -52,18 +40,11 @@ public class StatementLabelsScriptTransformer extends AbstractScriptTransformer
             @Override
             protected void visitStatement(Statement statement) {
                 if (statement.getStatementLabel() != null) {
-                    // Because we aren't failing the build, the script will be cached and this transformer won't run the next time.
-                    // In order to make the deprecation warning stick, we have to weave the call to StatementLabelsDeprecationLogger
-                    // into the build script.
-                    String label = statement.getStatementLabel();
-                    String sample = source.getSample(statement.getLineNumber(), statement.getColumnNumber(), null);
-                    Expression logExpr = new StaticMethodCallExpression(ClassHelper.makeWithoutCaching(StatementLabelsDeprecationLogger.class), "log",
-                            new ArgumentListExpression(new ConstantExpression(label), new ConstantExpression(sample)));
-                    logStats.add(new ExpressionStatement(logExpr));
+                    String message = String.format("Statement labels may not be used in build scripts.%nIn case you tried to configure a property named '%s', replace ':' with '=' or ' ', otherwise it will not have the desired effect.",
+                            statement.getStatementLabel());
+                    addError(message, statement);
                 }
             }
         });
-
-        source.getAST().getStatementBlock().addStatements(logStats);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.java
deleted file mode 100644
index 854aef6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/StatementTransformer.java
+++ /dev/null
@@ -1,30 +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.groovy.scripts.internal;
-
-
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.SourceUnit;
-import org.gradle.api.specs.Spec;
-
-public interface StatementTransformer {
-
-    // Return null to remove the statement
-    Statement transform(SourceUnit sourceUnit, Statement statement);
-
-    Spec<? super Statement> getSpec();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/SubsetScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/SubsetScriptTransformer.java
new file mode 100644
index 0000000..aa10159
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/SubsetScriptTransformer.java
@@ -0,0 +1,109 @@
+/*
+ * 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.groovy.scripts.internal;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ImportNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.internal.UncheckedException;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Excludes everything from a script except statements that are satisfied by a given predicate, and imports for accessible classes.
+ * <p>
+ * All other kinds of constructs are filtered, including classes and methods etc.
+ */
+public class SubsetScriptTransformer extends AbstractScriptTransformer {
+
+    private final StatementTransformer transformer;
+
+    public SubsetScriptTransformer(StatementTransformer transformer) {
+        this.transformer = transformer;
+    }
+
+    protected int getPhase() {
+        return Phases.CONVERSION;
+    }
+
+    public void call(SourceUnit source) throws CompilationFailedException {
+        AstUtils.filterAndTransformStatements(source, transformer);
+
+        // Filter imported classes which are not available yet
+
+        Iterator<ImportNode> iter = source.getAST().getImports().iterator();
+        while (iter.hasNext()) {
+            ImportNode importedClass = iter.next();
+            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
+                try {
+                    Field field = ModuleNode.class.getDeclaredField("imports");
+                    field.setAccessible(true);
+                    Map value = (Map) field.get(source.getAST());
+                    value.remove(importedClass.getAlias());
+                } catch (Exception e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+        }
+
+        iter = source.getAST().getStaticImports().values().iterator();
+        while (iter.hasNext()) {
+            ImportNode importedClass = iter.next();
+            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
+                iter.remove();
+            }
+        }
+
+        iter = source.getAST().getStaticStarImports().values().iterator();
+        while (iter.hasNext()) {
+            ImportNode importedClass = iter.next();
+            if (!AstUtils.isVisible(source, importedClass.getClassName())) {
+                iter.remove();
+            }
+        }
+
+        ClassNode scriptClass = AstUtils.getScriptClass(source);
+
+        // Remove all the classes other than the main class
+        Iterator<ClassNode> classes = source.getAST().getClasses().iterator();
+        while (classes.hasNext()) {
+            ClassNode classNode = classes.next();
+            if (classNode != scriptClass) {
+                classes.remove();
+            }
+        }
+
+        // Remove all the methods from the main class
+        if (scriptClass != null) {
+            for (MethodNode methodNode : new ArrayList<MethodNode>(scriptClass.getMethods())) {
+                if (!methodNode.getName().equals("run")) {
+                    AstUtils.removeMethod(scriptClass, methodNode);
+                }
+            }
+        }
+
+        source.getAST().getMethods().clear();
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/TaskDefinitionScriptTransformer.java b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/TaskDefinitionScriptTransformer.java
index ff72f40..0023730 100644
--- a/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/TaskDefinitionScriptTransformer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/groovy/scripts/internal/TaskDefinitionScriptTransformer.java
@@ -31,10 +31,6 @@ public class TaskDefinitionScriptTransformer extends AbstractScriptTransformer {
         return Phases.CANONICALIZATION;
     }
 
-    public String getId() {
-        return "tasks";
-    }
-
     public void call(SourceUnit source) throws CompilationFailedException {
         AstUtils.visitScriptCode(source, new TaskDefinitionTransformer());
     }
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 adf92f9..552eba2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/AbstractProjectSpec.java
@@ -15,32 +15,25 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.internal.project.ProjectRegistry;
-import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.internal.project.ProjectRegistry;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 public abstract class AbstractProjectSpec implements ProjectSpec {
-    public boolean containsProject(ProjectRegistry<?> registry) {
+    public boolean containsProject(ProjectRegistry<? extends ProjectIdentifier> registry) {
         checkPreconditions(registry);
-        for (ProjectIdentifier project : registry.getAllProjects()) {
-            if (select(project)) {
-                return true;
-            }
-        }
-        return false;
+        List<ProjectIdentifier> matches = new ArrayList<ProjectIdentifier>();
+        select(registry, matches);
+        return !matches.isEmpty();
     }
 
     public <T extends ProjectIdentifier> T selectProject(ProjectRegistry<? extends T> registry) {
         checkPreconditions(registry);
         List<T> matches = new ArrayList<T>();
-        for (T project : registry.getAllProjects()) {
-            if (select(project)) {
-                matches.add(project);
-            }
-        }
+        select(registry, matches);
         if (matches.isEmpty()) {
             throw new InvalidUserDataException(formatNoMatchesMessage());
         }
@@ -57,5 +50,5 @@ public abstract class AbstractProjectSpec implements ProjectSpec {
 
     protected abstract String formatNoMatchesMessage();
 
-    protected abstract boolean select(ProjectIdentifier project);
+    protected abstract <T extends ProjectIdentifier> void select(ProjectRegistry<? extends T> candidates, List<? super T> 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 e62fd90..8dac70a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BaseSettings.java
@@ -24,19 +24,20 @@ import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
 import org.gradle.api.internal.project.AbstractPluginAware;
 import org.gradle.api.internal.project.ProjectRegistry;
-import org.gradle.api.plugins.PluginContainer;
 import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 
+import javax.inject.Inject;
 import java.io.File;
 
 public class BaseSettings extends AbstractPluginAware implements SettingsInternal {
     public static final String DEFAULT_BUILD_SRC_DIR = "buildSrc";
-
     private ScriptSource settingsScript;
 
     private StartParameter startParameter;
@@ -45,32 +46,24 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
 
     private DefaultProjectDescriptor rootProjectDescriptor;
 
-    private GradleInternal gradle;
-
-    private ProjectDescriptorRegistry projectDescriptorRegistry;
-
-    private PluginContainer plugins;
+    private ProjectDescriptor defaultProjectDescriptor;
 
-    private FileResolver fileResolver;
+    private GradleInternal gradle;
 
-    private final ScriptPluginFactory scriptPluginFactory;
-    private final ScriptHandlerFactory scriptHandlerFactory;
     private final ClassLoaderScope classLoaderScope;
+    private final ClassLoaderScope rootClassLoaderScope;
+    private final ServiceRegistry services;
 
     public BaseSettings(ServiceRegistryFactory serviceRegistryFactory, GradleInternal gradle,
-                        ClassLoaderScope classLoaderScope, File settingsDir, ScriptSource settingsScript,
-                        StartParameter startParameter) {
+                        ClassLoaderScope classLoaderScope, ClassLoaderScope rootClassLoaderScope, File settingsDir,
+                        ScriptSource settingsScript, StartParameter startParameter) {
         this.gradle = gradle;
+        this.rootClassLoaderScope = rootClassLoaderScope;
         this.settingsDir = settingsDir;
         this.settingsScript = settingsScript;
         this.startParameter = startParameter;
         this.classLoaderScope = classLoaderScope;
-        ServiceRegistry services = serviceRegistryFactory.createFor(this);
-        this.plugins = services.get(PluginContainer.class);
-        this.fileResolver = services.get(FileResolver.class);
-        this.scriptPluginFactory = services.get(ScriptPluginFactory.class);
-        this.scriptHandlerFactory = services.get(ScriptHandlerFactory.class);
-        this.projectDescriptorRegistry = services.get(ProjectDescriptorRegistry.class);
+        services = serviceRegistryFactory.createFor(this);
         rootProjectDescriptor = createProjectDescriptor(null, settingsDir.getName(), settingsDir);
     }
 
@@ -88,19 +81,19 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
     }
 
     public DefaultProjectDescriptor createProjectDescriptor(DefaultProjectDescriptor parent, String name, File dir) {
-        return new DefaultProjectDescriptor(parent, name, dir, projectDescriptorRegistry, fileResolver);
+        return new DefaultProjectDescriptor(parent, name, dir, getProjectDescriptorRegistry(), getFileResolver());
     }
 
     public DefaultProjectDescriptor findProject(String path) {
-        return projectDescriptorRegistry.getProject(path);
+        return getProjectDescriptorRegistry().getProject(path);
     }
 
     public DefaultProjectDescriptor findProject(File projectDir) {
-        return projectDescriptorRegistry.getProject(projectDir);
+        return getProjectDescriptorRegistry().getProject(projectDir);
     }
 
     public DefaultProjectDescriptor project(String path) {
-        DefaultProjectDescriptor projectDescriptor = projectDescriptorRegistry.getProject(path);
+        DefaultProjectDescriptor projectDescriptor = getProjectDescriptorRegistry().getProject(path);
         if (projectDescriptor == null) {
             throw new UnknownProjectException(String.format("Project with path '%s' could not be found.", path));
         }
@@ -108,7 +101,7 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
     }
 
     public DefaultProjectDescriptor project(File projectDir) {
-        DefaultProjectDescriptor projectDescriptor = projectDescriptorRegistry.getProject(projectDir);
+        DefaultProjectDescriptor projectDescriptor = getProjectDescriptorRegistry().getProject(projectDir);
         if (projectDescriptor == null) {
             throw new UnknownProjectException(String.format("Project with path '%s' could not be found.", projectDir));
         }
@@ -122,7 +115,7 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
             DefaultProjectDescriptor parentProjectDescriptor = rootProjectDescriptor;
             for (String pathElement : pathElements) {
                 subPath = subPath + ":" + pathElement;
-                DefaultProjectDescriptor projectDescriptor = projectDescriptorRegistry.getProject(subPath);
+                DefaultProjectDescriptor projectDescriptor = getProjectDescriptorRegistry().getProject(subPath);
                 if (projectDescriptor == null) {
                     parentProjectDescriptor = createProjectDescriptor(parentProjectDescriptor, pathElement, new File(parentProjectDescriptor.getProjectDir(), pathElement));
                 } else {
@@ -154,6 +147,14 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
         this.rootProjectDescriptor = rootProjectDescriptor;
     }
 
+    public ProjectDescriptor getDefaultProject() {
+        return defaultProjectDescriptor;
+    }
+
+    public void setDefaultProject(ProjectDescriptor defaultProjectDescriptor) {
+        this.defaultProjectDescriptor = defaultProjectDescriptor;
+    }
+
     public File getRootDir() {
         return rootProjectDescriptor.getProjectDir();
     }
@@ -182,40 +183,49 @@ public class BaseSettings extends AbstractPluginAware implements SettingsInterna
         this.settingsScript = settingsScript;
     }
 
+    @Inject
     public ProjectDescriptorRegistry getProjectDescriptorRegistry() {
-        return projectDescriptorRegistry;
+        throw new UnsupportedOperationException();
     }
 
-    public void setProjectDescriptorRegistry(ProjectDescriptorRegistry projectDescriptorRegistry) {
-        this.projectDescriptorRegistry = projectDescriptorRegistry;
+    public ProjectRegistry<DefaultProjectDescriptor> getProjectRegistry() {
+        return getProjectDescriptorRegistry();
     }
 
-    public ProjectRegistry<DefaultProjectDescriptor> getProjectRegistry() {
-        return projectDescriptorRegistry;
+    @Override
+    protected DefaultObjectConfigurationAction createObjectConfigurationAction() {
+        return new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), getScriptHandlerFactory(), getRootClassLoaderScope(), this);
     }
 
-    public PluginContainer getPlugins() {
-        return plugins;
+    public ClassLoaderScope getRootClassLoaderScope() {
+        return rootClassLoaderScope;
     }
 
+    public ClassLoaderScope getClassLoaderScope() {
+        return classLoaderScope;
+    }
 
-    @Override
-    protected FileResolver getFileResolver() {
-        return fileResolver;
+    protected ServiceRegistry getServices() {
+        return services;
     }
 
-    @Override
+    @Inject
+    protected ScriptHandlerFactory getScriptHandlerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
     protected ScriptPluginFactory getScriptPluginFactory() {
-        return scriptPluginFactory;
+        throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected ScriptHandlerFactory getScriptHandlerFactory() {
-        return scriptHandlerFactory;
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
     }
 
-    @Override
-    public ClassLoaderScope getClassLoaderScope() {
-        return classLoaderScope;
+    @Inject
+    public PluginManagerInternal getPluginManager() {
+        throw new UnsupportedOperationException();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.java
deleted file mode 100644
index 258e0ee..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildAction.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.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/BuildCancellationToken.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildCancellationToken.java
new file mode 100644
index 0000000..16d0425
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildCancellationToken.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * Propagates notification that the build should be cancelled.
+ */
+public interface BuildCancellationToken {
+    boolean isCancellationRequested();
+
+    /**
+     * @return current state of cancellation request before callback was added.
+     */
+    boolean addCallback(Runnable cancellationHandler);
+
+    /**
+     * Removes a callback called when cancellation request happens.
+     *
+     * @param cancellationHandler removed callback.
+     */
+    void removeCallback(Runnable cancellationHandler);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildCompletionListener.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildCompletionListener.java
new file mode 100644
index 0000000..3d93180
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildCompletionListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public interface BuildCompletionListener {
+    /**
+     * Called after a build has completed and all services and domain objects torn down.
+     */
+    void completed();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java
deleted file mode 100644
index f74f6ea..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildController.java
+++ /dev/null
@@ -1,45 +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.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/BuildEventConsumer.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildEventConsumer.java
new file mode 100644
index 0000000..d299a65
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildEventConsumer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.messaging.dispatch.Dispatch;
+
+/**
+ * A consumer for build events provided by the build requester. This can be used to forward events to the build requester.
+ */
+public interface BuildEventConsumer extends Dispatch<Object> {
+}
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 3058929..5607fc9 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildFileProjectSpec.java
@@ -15,26 +15,20 @@
  */
 package org.gradle.initialization;
 
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.api.internal.project.ProjectRegistry;
-import org.gradle.api.InvalidUserDataException;
 
 import java.io.File;
-import java.io.Serializable;
+import java.util.List;
 
-public class BuildFileProjectSpec extends AbstractProjectSpec implements Serializable {
+public class BuildFileProjectSpec extends AbstractProjectSpec {
     private final File buildFile;
 
     public BuildFileProjectSpec(File buildFile) {
         this.buildFile = buildFile;
     }
 
-    public String getDisplayName() {
-        return String.format("project has build file '%s'", buildFile);
-    }
-
     protected String formatNoMatchesMessage() {
         return String.format("No projects in this build have build file '%s'.", buildFile);
     }
@@ -43,8 +37,13 @@ public class BuildFileProjectSpec extends AbstractProjectSpec implements Seriali
         return String.format("Multiple projects in this build have build file '%s': %s", buildFile, matches);
     }
 
-    protected boolean select(ProjectIdentifier project) {
-        return buildFile.equals(project.getBuildFile());
+    @Override
+    protected <T extends ProjectIdentifier> void select(ProjectRegistry<? extends T> candidates, List<? super T> matches) {
+        for (T candidate : candidates.getAllProjects()) {
+            if (candidate.getBuildFile().equals(buildFile)) {
+                matches.add(candidate);
+            }
+        }
     }
 
     @Override
@@ -56,12 +55,4 @@ public class BuildFileProjectSpec extends AbstractProjectSpec implements Seriali
             throw new InvalidUserDataException(String.format("Build file '%s' is not a file.", buildFile));
         }
     }
-
-    public boolean equals(Object obj) {
-        return EqualsBuilder.reflectionEquals(this, obj);
-    }
-
-    public int hashCode() {
-        return HashCodeBuilder.reflectionHashCode(this);
-    }
-}
\ No newline at end of file
+}
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 a8d0d81..cdbe58e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLayoutParameters.java
@@ -16,7 +16,7 @@
 
 package org.gradle.initialization;
 
-import org.gradle.StartParameter;
+import org.gradle.api.Nullable;
 import org.gradle.internal.SystemProperties;
 
 import java.io.File;
@@ -24,17 +24,20 @@ import java.io.File;
 import static org.gradle.util.GFileUtils.canonicalise;
 
 public class BuildLayoutParameters {
+    public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home";
+    private static final File DEFAULT_GRADLE_USER_HOME = new File(SystemProperties.getInstance().getUserHome() + "/.gradle");
 
     private boolean searchUpwards = true;
-    private File projectDir = canonicalise(SystemProperties.getCurrentDir());
+    private File currentDir = canonicalise(SystemProperties.getInstance().getCurrentDir());
+    private File projectDir;
     private File gradleUserHomeDir;
 
     public BuildLayoutParameters() {
-        String gradleUserHome = System.getProperty(StartParameter.GRADLE_USER_HOME_PROPERTY_KEY);
+        String gradleUserHome = System.getProperty(GRADLE_USER_HOME_PROPERTY_KEY);
         if (gradleUserHome == null) {
             gradleUserHome = System.getenv("GRADLE_USER_HOME");
             if (gradleUserHome == null) {
-                gradleUserHome = StartParameter.DEFAULT_GRADLE_USER_HOME.getAbsolutePath();
+                gradleUserHome = DEFAULT_GRADLE_USER_HOME.getAbsolutePath();
             }
         }
         gradleUserHomeDir = canonicalise(new File(gradleUserHome));
@@ -55,10 +58,24 @@ public class BuildLayoutParameters {
         return this;
     }
 
+    public BuildLayoutParameters setCurrentDir(File currentDir) {
+        this.currentDir = currentDir;
+        return this;
+    }
+
+    public File getCurrentDir() {
+        return currentDir;
+    }
+
+    @Nullable
     public File getProjectDir() {
         return projectDir;
     }
 
+    public File getSearchDir() {
+        return projectDir != null ? projectDir : currentDir;
+    }
+
     public File getGradleUserHomeDir() {
         return gradleUserHomeDir;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLoader.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLoader.java
index 94195b4..1afda92 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildLoader.java
@@ -24,5 +24,5 @@ public interface BuildLoader {
      * Creates the {@link org.gradle.api.internal.GradleInternal} and {@link org.gradle.api.internal.project.ProjectInternal} instances for the given root project,
      * ready for the projects to be evaluated.
      */
-    void load(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope classLoaderScope);
+    void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope classLoaderScope);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/BuildRequestContext.java b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildRequestContext.java
new file mode 100644
index 0000000..0e73950
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/BuildRequestContext.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * Provides access to services provided by build requester.
+ */
+public interface BuildRequestContext extends BuildRequestMetaData {
+    /**
+     * Returns the cancellation token through which the requester can cancel the build.
+     */
+    BuildCancellationToken getCancellationToken();
+
+    /**
+     * Returns an event consumer that will forward events to the build requester.
+     */
+    BuildEventConsumer getEventConsumer();
+}
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 e638ee6..9bba011 100755
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderRegistry.java
@@ -27,12 +27,12 @@ public interface ClassLoaderRegistry {
     ClassLoader getRuntimeClassLoader();
 
     /**
-     * Returns the class loader for the coreImpl project.
+     * Returns the class loader for the plugins.
      */
-    ClassLoader getCoreImplClassLoader();
+    ClassLoader getPluginsClassLoader();
 
     /**
-     * Returns the class loader for the plugins.
+     * Just the core API, no core plugins.
      */
-    ClassLoader getPluginsClassLoader();
+    ClassLoader getGradleCoreApiClassLoader();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderScopeRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderScopeRegistry.java
new file mode 100644
index 0000000..39a7986
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ClassLoaderScopeRegistry.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.initialization.ClassLoaderScope;
+
+public interface ClassLoaderScopeRegistry {
+
+    ClassLoaderScope getCoreAndPluginsScope();
+
+    ClassLoaderScope getCoreScope();
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildCancellationToken.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildCancellationToken.java
new file mode 100644
index 0000000..fb98fcc
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildCancellationToken.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal.exceptions.DefaultMultiCauseException;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class DefaultBuildCancellationToken implements BuildCancellationToken {
+    private final Object lock = new Object();
+    private boolean cancelled;
+    private List<Runnable> callbacks = new LinkedList<Runnable>();
+
+    public boolean isCancellationRequested() {
+        synchronized (lock) {
+            return cancelled;
+        }
+    }
+
+    public boolean addCallback(Runnable cancellationHandler) {
+        boolean returnValue;
+        synchronized (lock) {
+            returnValue = cancelled;
+            if (!cancelled) {
+                callbacks.add(cancellationHandler);
+            }
+        }
+        if (returnValue) {
+            cancellationHandler.run();
+        }
+        return returnValue;
+    }
+
+    public void removeCallback(Runnable cancellationHandler) {
+        synchronized (lock) {
+            callbacks.remove(cancellationHandler);
+        }
+    }
+
+    public void doCancel() {
+        List<Runnable> toCall = new ArrayList<Runnable>();
+        synchronized (lock) {
+            if (cancelled) {
+                return;
+            }
+            cancelled = true;
+            toCall.addAll(callbacks);
+            callbacks.clear();
+        }
+
+        List<Throwable> failures = new ArrayList<Throwable>();
+        for (Runnable callback : toCall) {
+            try {
+                callback.run();
+            } catch (Throwable ex) {
+                failures.add(ex);
+            }
+        }
+        if (!failures.isEmpty()) {
+            throw new DefaultMultiCauseException("Failed to run cancellation actions.", failures);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildRequestContext.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildRequestContext.java
new file mode 100644
index 0000000..14eecf2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultBuildRequestContext.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.initialization;
+
+import org.gradle.util.Clock;
+
+public class DefaultBuildRequestContext implements BuildRequestContext {
+    private final BuildCancellationToken token;
+    private final BuildEventConsumer buildEventConsumer;
+    private final BuildRequestMetaData metaData;
+
+    public DefaultBuildRequestContext(BuildRequestMetaData metaData, BuildCancellationToken token, BuildEventConsumer buildEventConsumer) {
+        this.metaData = metaData;
+        this.token = token;
+        this.buildEventConsumer = buildEventConsumer;
+    }
+
+    @Override
+    public BuildEventConsumer getEventConsumer() {
+        return buildEventConsumer;
+    }
+
+    @Override
+    public BuildCancellationToken getCancellationToken() {
+        return token;
+    }
+
+    @Override
+    public BuildClientMetaData getClient() {
+        return metaData.getClient();
+    }
+
+    @Override
+    public Clock getBuildTimeClock() {
+        return metaData.getBuildTimeClock();
+    }
+}
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 93a2fcd..cd4a669 100755
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderRegistry.java
@@ -26,43 +26,43 @@ import java.io.File;
 import java.net.URLClassLoader;
 
 public class DefaultClassLoaderRegistry implements ClassLoaderRegistry, JdkToolsInitializer {
-    private final ClassLoader rootClassLoader;
-    private final ClassLoader coreImplClassLoader;
-    private final ClassLoader pluginsClassLoader;
+    private final ClassLoader apiOnlyClassLoader;
+    private final ClassLoader apiAndPluginsClassLoader;
+    private final ClassLoader extensionsClassLoader;
+    private final ClassLoaderFactory classLoaderFactory;
 
     public DefaultClassLoaderRegistry(ClassPathRegistry classPathRegistry, ClassLoaderFactory classLoaderFactory) {
+        this.classLoaderFactory = classLoaderFactory;
         ClassLoader runtimeClassLoader = getClass().getClassLoader();
 
-        // Core impl
-        ClassPath coreImplClassPath = classPathRegistry.getClassPath("GRADLE_CORE_IMPL");
-        coreImplClassLoader = new MutableURLClassLoader(runtimeClassLoader, coreImplClassPath);
+        apiOnlyClassLoader = restrictToGradleApi(runtimeClassLoader);
 
-        // Add in libs for plugins
-        ClassPath pluginsClassPath = classPathRegistry.getClassPath("GRADLE_PLUGINS");
-        ClassLoader pluginsImports = new CachingClassLoader(new MultiParentClassLoader(runtimeClassLoader, coreImplClassLoader));
-        pluginsClassLoader = new MutableURLClassLoader(pluginsImports, pluginsClassPath);
+        ClassPath pluginsClassPath = classPathRegistry.getClassPath("GRADLE_EXTENSIONS");
+        extensionsClassLoader = new MutableURLClassLoader(runtimeClassLoader, pluginsClassPath);
 
-        FilteringClassLoader rootClassLoader = classLoaderFactory.createFilteringClassLoader(pluginsClassLoader);
+        this.apiAndPluginsClassLoader = restrictToGradleApi(extensionsClassLoader);
+    }
+
+    private ClassLoader restrictToGradleApi(ClassLoader classLoader) {
+        FilteringClassLoader rootClassLoader = classLoaderFactory.createFilteringClassLoader(classLoader);
         rootClassLoader.allowPackage("org.gradle");
         rootClassLoader.allowResources("META-INF/gradle-plugins");
         rootClassLoader.allowPackage("org.apache.tools.ant");
         rootClassLoader.allowPackage("groovy");
         rootClassLoader.allowPackage("org.codehaus.groovy");
         rootClassLoader.allowPackage("groovyjarjarantlr");
-        rootClassLoader.allowPackage("org.apache.ivy");
         rootClassLoader.allowPackage("org.slf4j");
         rootClassLoader.allowPackage("org.apache.commons.logging");
         rootClassLoader.allowPackage("org.apache.log4j");
         rootClassLoader.allowPackage("javax.inject");
-
-        this.rootClassLoader = new CachingClassLoader(rootClassLoader);
+        return new CachingClassLoader(rootClassLoader);
     }
 
     public void initializeJdkTools() {
         // Add in tools.jar to the systemClassloader parent
         File toolsJar = Jvm.current().getToolsJar();
         if (toolsJar != null) {
-            final ClassLoader systemClassLoaderParent = ClassLoader.getSystemClassLoader().getParent();
+            final ClassLoader systemClassLoaderParent = classLoaderFactory.getIsolatedSystemClassLoader();
             ClasspathUtil.addUrl((URLClassLoader) systemClassLoaderParent, new DefaultClassPath(toolsJar).getAsURLs());
         }
     }
@@ -72,14 +72,14 @@ public class DefaultClassLoaderRegistry implements ClassLoaderRegistry, JdkTools
     }
 
     public ClassLoader getGradleApiClassLoader() {
-        return rootClassLoader;
+        return apiAndPluginsClassLoader;
     }
 
-    public ClassLoader getCoreImplClassLoader() {
-        return coreImplClassLoader;
+    public ClassLoader getPluginsClassLoader() {
+        return extensionsClassLoader;
     }
 
-    public ClassLoader getPluginsClassLoader() {
-        return pluginsClassLoader;
+    public ClassLoader getGradleCoreApiClassLoader() {
+        return apiOnlyClassLoader;
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderScopeRegistry.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderScopeRegistry.java
new file mode 100644
index 0000000..bc90b42
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderScopeRegistry.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.internal.initialization.RootClassLoaderScope;
+
+public class DefaultClassLoaderScopeRegistry implements ClassLoaderScopeRegistry {
+
+    private final ClassLoaderScope coreAndPluginsScope;
+    private final ClassLoaderScope coreScope;
+
+    public DefaultClassLoaderScopeRegistry(ClassLoaderRegistry loaderRegistry, ClassLoaderCache classLoaderCache) {
+        this.coreScope = new RootClassLoaderScope(loaderRegistry.getRuntimeClassLoader(), loaderRegistry.getGradleCoreApiClassLoader(), classLoaderCache);
+        this.coreAndPluginsScope = new RootClassLoaderScope(loaderRegistry.getPluginsClassLoader(), loaderRegistry.getGradleApiClassLoader(), classLoaderCache);
+    }
+
+    public ClassLoaderScope getCoreAndPluginsScope() {
+        return coreAndPluginsScope;
+    }
+
+    public ClassLoaderScope getCoreScope() {
+        return coreScope;
+    }
+}
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 0b2b666..60a4d7f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
@@ -15,18 +15,15 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.CacheUsage;
-import org.gradle.RefreshOptions;
 import org.gradle.StartParameter;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.file.DefaultFileLookup;
-import org.gradle.api.internal.file.FileLookup;
-import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.BasicFileResolver;
 import org.gradle.cli.*;
-import org.gradle.internal.nativeplatform.services.FileSystems;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.internal.LoggingCommandLineConverter;
 
+import java.io.File;
+import java.util.HashMap;
 import java.util.Map;
 
 import static org.gradle.StartParameter.GRADLE_USER_HOME_PROPERTY_KEY;
@@ -36,15 +33,12 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
     private static final String BUILD_FILE = "b";
     public static final String INIT_SCRIPT = "I";
     private static final String SETTINGS_FILE = "c";
-    private static final String CACHE = "C";
     private static final String DRY_RUN = "m";
-    private static final String NO_OPT = "no-opt";
     private static final String RERUN_TASKS = "rerun-tasks";
     private static final String EXCLUDE_TASK = "x";
     private static final String PROFILE = "profile";
     private static final String CONTINUE = "continue";
     private static final String OFFLINE = "offline";
-    private static final String REFRESH = "refresh";
     private static final String REFRESH_DEPENDENCIES = "refresh-dependencies";
     private static final String PROJECT_CACHE_DIR = "project-cache-dir";
     private static final String RECOMPILE_SCRIPTS = "recompile-scripts";
@@ -52,17 +46,17 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
     private static final String PARALLEL = "parallel";
     private static final String PARALLEL_THREADS = "parallel-threads";
 
+    private static final String MAX_WORKERS = "max-workers";
+
     private static final String CONFIGURE_ON_DEMAND = "configure-on-demand";
 
     private final CommandLineConverter<LoggingConfiguration> loggingConfigurationCommandLineConverter = new LoggingCommandLineConverter();
     private final SystemPropertiesCommandLineConverter systemPropertiesCommandLineConverter = new SystemPropertiesCommandLineConverter();
     private final ProjectPropertiesCommandLineConverter projectPropertiesCommandLineConverter = new ProjectPropertiesCommandLineConverter();
     private final LayoutCommandLineConverter layoutCommandLineConverter;
-    private final FileLookup fileLookup;
 
     public DefaultCommandLineConverter() {
-        this.fileLookup = new DefaultFileLookup(FileSystems.getDefault());
-        layoutCommandLineConverter = new LayoutCommandLineConverter(fileLookup);
+        layoutCommandLineConverter = new LayoutCommandLineConverter();
     }
 
     public void configure(CommandLineParser parser) {
@@ -72,72 +66,61 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
         layoutCommandLineConverter.configure(parser);
 
         parser.allowMixedSubcommandsAndOptions();
-        parser.option(CACHE, "cache").hasArgument().hasDescription("Specifies how compiled build scripts should be cached. Possible values are: 'rebuild' and 'on'. Default value is 'on'")
-                    .deprecated("Use '--rerun-tasks' or '--recompile-scripts' instead");
         parser.option(PROJECT_CACHE_DIR).hasArgument().hasDescription("Specifies the project-specific cache directory. Defaults to .gradle in the root project directory.");
         parser.option(DRY_RUN, "dry-run").hasDescription("Runs the builds with all task actions disabled.");
         parser.option(INIT_SCRIPT, "init-script").hasArguments().hasDescription("Specifies an initialization script.");
         parser.option(SETTINGS_FILE, "settings-file").hasArgument().hasDescription("Specifies the settings file.");
         parser.option(BUILD_FILE, "build-file").hasArgument().hasDescription("Specifies the build file.");
         parser.option(NO_PROJECT_DEPENDENCY_REBUILD, "no-rebuild").hasDescription("Do not rebuild project dependencies.");
-        parser.option(NO_OPT).hasDescription("Ignore any task optimization.").deprecated("Use '--rerun-tasks' instead");
         parser.option(RERUN_TASKS).hasDescription("Ignore previously cached task results.");
         parser.option(RECOMPILE_SCRIPTS).hasDescription("Force build script recompiling.");
         parser.option(EXCLUDE_TASK, "exclude-task").hasArguments().hasDescription("Specify a task to be excluded from execution.");
         parser.option(PROFILE).hasDescription("Profiles build execution time and generates a report in the <build_dir>/reports/profile directory.");
         parser.option(CONTINUE).hasDescription("Continues task execution after a task failure.");
         parser.option(OFFLINE).hasDescription("The build should operate without accessing network resources.");
-        parser.option(REFRESH).hasArguments().hasDescription("Refresh the state of resources of the type(s) specified. Currently only 'dependencies' is supported.").deprecated("Use '--refresh-dependencies' instead.");
         parser.option(REFRESH_DEPENDENCIES).hasDescription("Refresh the state of dependencies.");
         parser.option(PARALLEL).hasDescription("Build projects in parallel. Gradle will attempt to determine the optimal number of executor threads to use.").incubating();
-        parser.option(PARALLEL_THREADS).hasArgument().hasDescription("Build projects in parallel, using the specified number of executor threads.").incubating();
+        parser.option(PARALLEL_THREADS).hasArgument().hasDescription("Build projects in parallel, using the specified number of executor threads.").
+                deprecated("Please use --parallel, optionally in conjunction with --max-workers.").incubating();
+        parser.option(MAX_WORKERS).hasArgument().hasDescription("Configure the number of concurrent workers Gradle is allowed to use.").incubating();
         parser.option(CONFIGURE_ON_DEMAND).hasDescription("Only relevant projects are configured in this build run. This means faster build for large multi-project builds.").incubating();
-    }
-
-    @Override
-    protected StartParameter newInstance() {
-        return new StartParameter();
+        parser.allowOneOf(MAX_WORKERS, PARALLEL_THREADS);
     }
 
     public StartParameter convert(final ParsedCommandLine options, final StartParameter startParameter) throws CommandLineArgumentException {
         loggingConfigurationCommandLineConverter.convert(options, startParameter);
-        FileResolver resolver = fileLookup.getFileResolver(startParameter.getCurrentDir());
+        Transformer<File, String> resolver = new BasicFileResolver(startParameter.getCurrentDir());
 
-        Map<String, String> systemProperties = systemPropertiesCommandLineConverter.convert(options);
+        Map<String, String> systemProperties = systemPropertiesCommandLineConverter.convert(options, new HashMap<String, String>());
         convertCommandLineSystemProperties(systemProperties, startParameter, resolver);
 
-        Map<String, String> projectProperties = projectPropertiesCommandLineConverter.convert(options);
+        Map<String, String> projectProperties = projectPropertiesCommandLineConverter.convert(options, new HashMap<String, String>());
         startParameter.getProjectProperties().putAll(projectProperties);
 
         BuildLayoutParameters layout = new BuildLayoutParameters()
                 .setGradleUserHomeDir(startParameter.getGradleUserHomeDir())
-                .setProjectDir(startParameter.getCurrentDir());
+                .setProjectDir(startParameter.getProjectDir())
+                .setCurrentDir(startParameter.getCurrentDir());
         layoutCommandLineConverter.convert(options, layout);
         startParameter.setGradleUserHomeDir(layout.getGradleUserHomeDir());
-        startParameter.setProjectDir(layout.getProjectDir());
+        if (layout.getProjectDir() != null) {
+            startParameter.setProjectDir(layout.getProjectDir());
+        }
         startParameter.setSearchUpwards(layout.getSearchUpwards());
 
         if (options.hasOption(BUILD_FILE)) {
-            startParameter.setBuildFile(resolver.resolve(options.option(BUILD_FILE).getValue()));
+            startParameter.setBuildFile(resolver.transform(options.option(BUILD_FILE).getValue()));
         }
         if (options.hasOption(SETTINGS_FILE)) {
-            startParameter.setSettingsFile(resolver.resolve(options.option(SETTINGS_FILE).getValue()));
+            startParameter.setSettingsFile(resolver.transform(options.option(SETTINGS_FILE).getValue()));
         }
 
         for (String script : options.option(INIT_SCRIPT).getValues()) {
-            startParameter.addInitScript(resolver.resolve(script));
-        }
-
-        if (options.hasOption(CACHE)) {
-            try {
-                startParameter.setCacheUsage(CacheUsage.fromString(options.option(CACHE).getValue()));
-            } catch (InvalidUserDataException e) {
-                throw new CommandLineArgumentException(e.getMessage());
-            }
+            startParameter.addInitScript(resolver.transform(script));
         }
 
         if (options.hasOption(PROJECT_CACHE_DIR)) {
-            startParameter.setProjectCacheDir(resolver.resolve(options.option(PROJECT_CACHE_DIR).getValue()));
+            startParameter.setProjectCacheDir(resolver.transform(options.option(PROJECT_CACHE_DIR).getValue()));
         }
 
         if (options.hasOption(NO_PROJECT_DEPENDENCY_REBUILD)) {
@@ -152,10 +135,6 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
             startParameter.setDryRun(true);
         }
 
-        if (options.hasOption(NO_OPT)) {
-            startParameter.setNoOpt(true);
-        }
-
         if (options.hasOption(RERUN_TASKS)) {
             startParameter.setRerunTasks(true);
         }
@@ -180,16 +159,12 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
             startParameter.setOffline(true);
         }
 
-        if (options.hasOption(REFRESH)) {
-            startParameter.setRefreshOptions(RefreshOptions.fromCommandLineOptions(options.option(REFRESH).getValues()));
-        }
-
         if (options.hasOption(REFRESH_DEPENDENCIES)) {
             startParameter.setRefreshDependencies(true);
         }
 
-        if (options.hasOption(PARALLEL)) {
-            startParameter.setParallelThreadCount(-1);
+        if (options.hasOption(PARALLEL) || options.hadOptionRemoved(PARALLEL_THREADS)) {
+            startParameter.setParallelProjectExecutionEnabled(true);
         }
 
         if (options.hasOption(PARALLEL_THREADS)) {
@@ -201,6 +176,19 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
             }
         }
 
+        if (options.hasOption(MAX_WORKERS)) {
+            String value = options.option(MAX_WORKERS).getValue();
+            try {
+                int workerCount = Integer.parseInt(value);
+                if (workerCount < 1) {
+                    invalidMaxWorkersSwitchValue(value);
+                }
+                startParameter.setMaxWorkerCount(workerCount);
+            } catch (NumberFormatException e) {
+                invalidMaxWorkersSwitchValue(value);
+            }
+        }
+
         if (options.hasOption(CONFIGURE_ON_DEMAND)) {
             startParameter.setConfigureOnDemand(true);
         }
@@ -208,10 +196,14 @@ public class DefaultCommandLineConverter extends AbstractCommandLineConverter<St
         return startParameter;
     }
 
-    void convertCommandLineSystemProperties(Map<String, String> systemProperties, StartParameter startParameter, FileResolver resolver) {
+    private StartParameter invalidMaxWorkersSwitchValue(String value) {
+        throw new CommandLineArgumentException(String.format("Argument value '%s' given for --%s option is invalid (must be a positive, non-zero, integer)", value, MAX_WORKERS));
+    }
+
+    void convertCommandLineSystemProperties(Map<String, String> systemProperties, StartParameter startParameter, Transformer<File, String> resolver) {
         startParameter.getSystemPropertiesArgs().putAll(systemProperties);
         if (systemProperties.containsKey(GRADLE_USER_HOME_PROPERTY_KEY)) {
-            startParameter.setGradleUserHomeDir(resolver.resolve(systemProperties.get(GRADLE_USER_HOME_PROPERTY_KEY)));
+            startParameter.setGradleUserHomeDir(resolver.transform(systemProperties.get(GRADLE_USER_HOME_PROPERTY_KEY)));
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
index 7795d46..498e508 100755
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultExceptionAnalyser.java
@@ -24,7 +24,7 @@ import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptCompilationException;
 import org.gradle.groovy.scripts.ScriptExecutionListener;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerManager;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -65,9 +65,9 @@ public class DefaultExceptionAnalyser implements ExceptionAnalyser, ScriptExecut
                     Throwable currentException = actualException; currentException != null;
                     currentException = currentException.getCause()) {
                 for (StackTraceElement element : currentException.getStackTrace()) {
-                    if (scripts.containsKey(element.getFileName())) {
+                    if (element.getLineNumber() >= 0 && scripts.containsKey(element.getFileName())) {
                         source = scripts.get(element.getFileName());
-                        lineNumber = element.getLineNumber() >= 0 ? element.getLineNumber() : null;
+                        lineNumber = element.getLineNumber();
                         break;
                     }
                 }
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 9f394c7..3d6635f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
@@ -17,19 +17,20 @@ package org.gradle.initialization;
 
 import org.gradle.BuildListener;
 import org.gradle.BuildResult;
-import org.gradle.GradleLauncher;
-import org.gradle.StartParameter;
 import org.gradle.api.internal.ExceptionAnalyser;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.configuration.BuildConfigurer;
 import org.gradle.execution.BuildExecuter;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.logging.LoggingManagerInternal;
 
+import java.io.Closeable;
+
 public class DefaultGradleLauncher extends GradleLauncher {
     private enum Stage {
-        Configure, PopulateTaskGraph, Build
+        Configure, Build
     }
 
     private final GradleInternal gradle;
@@ -42,17 +43,19 @@ public class DefaultGradleLauncher extends GradleLauncher {
     private final LoggingManagerInternal loggingManager;
     private final ModelConfigurationListener modelConfigurationListener;
     private final TasksCompletionListener tasksCompletionListener;
+    private final BuildCompletionListener buildCompletionListener;
     private final BuildExecuter buildExecuter;
+    private final Closeable buildServices;
 
     /**
-     * Creates a new instance.  Don't call this directly, use {@link #newInstance(org.gradle.StartParameter)} or {@link
-     * #newInstance(String...)} instead.
+     * Creates a new instance.
      */
     public DefaultGradleLauncher(GradleInternal gradle, InitScriptHandler initScriptHandler, SettingsHandler settingsHandler,
                                  BuildLoader buildLoader, BuildConfigurer buildConfigurer, BuildListener buildListener,
                                  ExceptionAnalyser exceptionAnalyser, LoggingManagerInternal loggingManager,
                                  ModelConfigurationListener modelConfigurationListener, TasksCompletionListener tasksCompletionListener,
-                                 BuildExecuter buildExecuter) {
+                                 BuildExecuter buildExecuter, BuildCompletionListener buildCompletionListener,
+                                 Closeable buildServices) {
         this.gradle = gradle;
         this.initScriptHandler = initScriptHandler;
         this.settingsHandler = settingsHandler;
@@ -64,6 +67,8 @@ public class DefaultGradleLauncher extends GradleLauncher {
         this.modelConfigurationListener = modelConfigurationListener;
         this.tasksCompletionListener = tasksCompletionListener;
         this.buildExecuter = buildExecuter;
+        this.buildCompletionListener = buildCompletionListener;
+        this.buildServices = buildServices;
     }
 
     public GradleInternal getGradle() {
@@ -92,18 +97,6 @@ public class DefaultGradleLauncher extends GradleLauncher {
         return doBuild(Stage.Configure);
     }
 
-    /**
-     * Evaluates the settings and all the projects. The information about available tasks and projects is accessible via
-     * the {@link org.gradle.api.invocation.Gradle#getRootProject()} object. Fills the execution plan without running
-     * the build. The tasks to be executed tasks are available via {@link org.gradle.api.invocation.Gradle#getTaskGraph()}.
-     *
-     * @return A BuildResult object. Never returns null.
-     */
-    @Override
-    public BuildResult getBuildAndRunAnalysis() {
-        return doBuild(Stage.PopulateTaskGraph);
-    }
-
     private BuildResult doBuild(Stage upTo) {
         loggingManager.start();
         buildListener.buildStarted(gradle);
@@ -117,12 +110,6 @@ public class DefaultGradleLauncher extends GradleLauncher {
         BuildResult buildResult = new BuildResult(gradle, failure);
         buildListener.buildFinished(buildResult);
 
-        // Switching Logging off is important if the Gradle factory is used to
-        // run multiple Gradle builds (each one requiring a new instances of GradleLauncher).
-        // Switching it off shouldn't be strictly necessary as StandardOutput capturing should
-        // always be closed. But as we expose this functionality to the builds, we can't
-        // guarantee this.
-        loggingManager.stop();
         return buildResult;
     }
 
@@ -135,7 +122,7 @@ public class DefaultGradleLauncher extends GradleLauncher {
         buildListener.settingsEvaluated(settings);
 
         // Load build
-        buildLoader.load(settings.getRootProject(), gradle, settings.getClassLoaderScope().createSibling());
+        buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
         buildListener.projectsLoaded(gradle);
 
         // Configure build
@@ -158,10 +145,6 @@ public class DefaultGradleLauncher extends GradleLauncher {
             buildListener.projectsEvaluated(gradle);
         }
 
-        if (upTo == Stage.PopulateTaskGraph) {
-            return;
-        }
-
         // Execute build
         buildExecuter.execute();
         tasksCompletionListener.onTasksFinished(gradle);
@@ -169,8 +152,6 @@ public class DefaultGradleLauncher extends GradleLauncher {
         assert upTo == Stage.Build;
     }
 
-    // This is used for mocking
-
     /**
      * <p>Adds a listener to this build instance. The listener is notified of events which occur during the
      * execution of the build. See {@link org.gradle.api.invocation.Gradle#addListener(Object)} for supported listener
@@ -184,16 +165,6 @@ public class DefaultGradleLauncher extends GradleLauncher {
     }
 
     /**
-     * Use the given listener. See {@link org.gradle.api.invocation.Gradle#useLogger(Object)} for details.
-     *
-     * @param logger The logger to use.
-     */
-    @Override
-    public void useLogger(Object logger) {
-        gradle.useLogger(logger);
-    }
-    
-    /**
      * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to
      * standard output by Gradle's logging system
      *
@@ -215,8 +186,12 @@ public class DefaultGradleLauncher extends GradleLauncher {
         loggingManager.addStandardErrorListener(listener);
     }
 
-    @Override
-    public StartParameter getStartParameter() {
-        return gradle.getStartParameter();
+    public void stop() {
+        try {
+            loggingManager.stop();
+            CompositeStoppable.stoppable(buildServices).stop();
+        } finally {
+            buildCompletionListener.completed();
+        }
     }
 }
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 eec61d9..5f5e727 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
@@ -16,28 +16,28 @@
 
 package org.gradle.initialization;
 
-import org.gradle.*;
+import org.gradle.BuildLogger;
+import org.gradle.StartParameter;
+import org.gradle.TaskExecutionLogger;
 import org.gradle.api.internal.ExceptionAnalyser;
 import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.internal.progress.BuildProgressFilter;
-import org.gradle.internal.progress.BuildProgressLogger;
-import org.gradle.internal.progress.LoggerProvider;
-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.event.ListenerManager;
 import org.gradle.internal.featurelifecycle.ScriptUsageLocationReporter;
+import org.gradle.internal.progress.BuildProgressFilter;
+import org.gradle.internal.progress.BuildProgressLogger;
+import org.gradle.internal.progress.LoggerProvider;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.BuildScopeServices;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.invocation.DefaultGradle;
-import org.gradle.listener.ListenerManager;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.logging.StyledTextOutputFactory;
@@ -45,13 +45,10 @@ import org.gradle.profile.ProfileEventAdapter;
 import org.gradle.profile.ReportGeneratingProfileListener;
 import org.gradle.util.DeprecationLogger;
 
-import java.util.Arrays;
-
 public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     private final ServiceRegistry sharedServices;
     private final NestedBuildTracker tracker;
     private final BuildProgressLogger buildProgressLogger;
-    private CommandLineConverter<StartParameter> commandLineConverter;
 
     public DefaultGradleLauncherFactory(ServiceRegistry globalServices) {
         sharedServices = globalServices;
@@ -62,8 +59,6 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
         buildProgressLogger = new BuildProgressLogger(sharedServices.get(ProgressLoggerFactory.class));
         listenerManager.addListener(new BuildProgressFilter(buildProgressLogger));
         listenerManager.useLogger(new DependencyResolutionLogger(sharedServices.get(ProgressLoggerFactory.class)));
-
-        GradleLauncher.injectCustomFactory(this);
     }
 
     public void addListener(Object listener) {
@@ -74,33 +69,36 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
         sharedServices.get(ListenerManager.class).removeListener(listener);
     }
 
-    public StartParameter createStartParameter(String... commandLineArgs) {
-        if (commandLineConverter == null) {
-            commandLineConverter = sharedServices.get(CommandLineConverter.class);
-        }
-        return commandLineConverter.convert(Arrays.asList(commandLineArgs));
-    }
-
     public DefaultGradleLauncher newInstance(StartParameter startParameter) {
         BuildRequestMetaData requestMetaData;
+        BuildCancellationToken cancellationToken;
+        BuildEventConsumer buildEventConsumer;
         if (tracker.getCurrentBuild() != null) {
-            requestMetaData = new DefaultBuildRequestMetaData(tracker.getCurrentBuild().getServices().get(BuildClientMetaData.class), System.currentTimeMillis());
+            ServiceRegistry services = tracker.getCurrentBuild().getServices();
+            requestMetaData = new DefaultBuildRequestMetaData(services.get(BuildClientMetaData.class), System.currentTimeMillis());
+            cancellationToken = services.get(BuildCancellationToken.class);
+            buildEventConsumer = services.get(BuildEventConsumer.class);
         } else {
             requestMetaData = new DefaultBuildRequestMetaData(System.currentTimeMillis());
+            cancellationToken = new FixedBuildCancellationToken();
+            buildEventConsumer = new NoOpBuildEventConsumer();
         }
-        return doNewInstance(startParameter, requestMetaData);
+        return doNewInstance(startParameter, cancellationToken, requestMetaData, buildEventConsumer);
     }
 
-    public DefaultGradleLauncher newInstance(StartParameter startParameter, BuildRequestMetaData requestMetaData) {
+    @Override
+    public GradleLauncher newInstance(StartParameter startParameter, BuildRequestContext requestContext) {
         // This should only be used for top-level builds
         assert tracker.getCurrentBuild() == null;
-        return doNewInstance(startParameter, requestMetaData);
+        return doNewInstance(startParameter, requestContext.getCancellationToken(), requestContext, requestContext.getEventConsumer());
     }
 
-    private DefaultGradleLauncher doNewInstance(StartParameter startParameter, BuildRequestMetaData requestMetaData) {
+    private DefaultGradleLauncher doNewInstance(StartParameter startParameter, BuildCancellationToken cancellationToken, BuildRequestMetaData requestMetaData, BuildEventConsumer buildEventConsumer) {
         final BuildScopeServices serviceRegistry = new BuildScopeServices(sharedServices, startParameter);
         serviceRegistry.add(BuildRequestMetaData.class, requestMetaData);
         serviceRegistry.add(BuildClientMetaData.class, requestMetaData.getClient());
+        serviceRegistry.add(BuildEventConsumer.class, buildEventConsumer);
+        serviceRegistry.add(BuildCancellationToken.class, cancellationToken);
         ListenerManager listenerManager = serviceRegistry.get(ListenerManager.class);
         LoggingManagerInternal loggingManager = serviceRegistry.newInstance(LoggingManagerInternal.class);
         loggingManager.setLevel(startParameter.getLogLevel());
@@ -109,13 +107,12 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
         loggingManager.addStandardOutputListener(listenerManager.getBroadcaster(StandardOutputListener.class));
         loggingManager.addStandardErrorListener(listenerManager.getBroadcaster(StandardOutputListener.class));
 
-        LoggerProvider loggerProvider = (tracker.getCurrentBuild() == null)? buildProgressLogger: LoggerProvider.NO_OP;
+        LoggerProvider loggerProvider = (tracker.getCurrentBuild() == null) ? buildProgressLogger : LoggerProvider.NO_OP;
         listenerManager.useLogger(new TaskExecutionLogger(serviceRegistry.get(ProgressLoggerFactory.class), loggerProvider));
         if (tracker.getCurrentBuild() == null) {
             listenerManager.useLogger(new BuildLogger(Logging.getLogger(BuildLogger.class), serviceRegistry.get(StyledTextOutputFactory.class), startParameter, requestMetaData));
         }
         listenerManager.addListener(tracker);
-        listenerManager.addListener(new BuildCleanupListener(serviceRegistry));
 
         listenerManager.addListener(serviceRegistry.get(ProfileEventAdapter.class));
         if (startParameter.isProfile()) {
@@ -135,8 +132,9 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
                         serviceRegistry.get(SettingsProcessor.class),
                         new BuildSourceBuilder(
                                 this,
-                                serviceRegistry.get(ClassLoaderScope.class),
-                                serviceRegistry.get(CacheRepository.class))),
+                                serviceRegistry.get(ClassLoaderScopeRegistry.class).getCoreAndPluginsScope(),
+                                serviceRegistry.get(CacheRepository.class))
+                ),
                 serviceRegistry.get(BuildLoader.class),
                 serviceRegistry.get(BuildConfigurer.class),
                 gradle.getBuildListenerBroadcaster(),
@@ -144,25 +142,9 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
                 loggingManager,
                 listenerManager.getBroadcaster(ModelConfigurationListener.class),
                 listenerManager.getBroadcaster(TasksCompletionListener.class),
-                gradle.getServices().get(BuildExecuter.class));
-    }
-
-    public void setCommandLineConverter(
-            CommandLineConverter<StartParameter> commandLineConverter) {
-        this.commandLineConverter = commandLineConverter;
+                gradle.getServices().get(BuildExecuter.class),
+                listenerManager.getBroadcaster(BuildCompletionListener.class),
+                serviceRegistry
+        );
     }
-
-    private static class BuildCleanupListener extends BuildAdapter {
-        private final BuildScopeServices services;
-
-        private BuildCleanupListener(BuildScopeServices services) {
-            this.services = services;
-        }
-
-        @Override
-        public void buildFinished(BuildResult result) {
-            services.close();
-        }
-    }
-
 }
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 56c77de..55b4ff3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultProjectSpec.java
@@ -15,17 +15,39 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.api.internal.SettingsInternal;
+import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.api.internal.project.ProjectRegistry;
 
 import java.io.File;
+import java.util.List;
 
-public class DefaultProjectSpec extends ProjectDirectoryProjectSpec {
-    public DefaultProjectSpec(File dir) {
-        super(dir);
+public class DefaultProjectSpec extends AbstractProjectSpec {
+    private final boolean useRootWhenNoMatch;
+    private final File currentDir;
+
+    public DefaultProjectSpec(File currentDir, SettingsInternal settings) {
+        this.currentDir = currentDir;
+        this.useRootWhenNoMatch = currentDir.equals(settings.getSettingsDir());
     }
 
     @Override
-    protected void checkPreconditions(ProjectRegistry<?> registry) {
-        // Ignore
+    protected <T extends ProjectIdentifier> void select(ProjectRegistry<? extends T> candidates, List<? super T> matches) {
+        for (T candidate : candidates.getAllProjects()) {
+            if (candidate.getProjectDir().equals(currentDir)) {
+                matches.add(candidate);
+            }
+        }
+        if (useRootWhenNoMatch && matches.isEmpty()) {
+            matches.add(candidates.getProject(":"));
+        }
+    }
+
+    protected String formatNoMatchesMessage() {
+        return String.format("No projects in this build have project directory '%s'.", currentDir);
+    }
+
+    protected String formatMultipleMatchesMessage(Iterable<? extends ProjectIdentifier> matches) {
+        return String.format("Multiple projects in this build have project directory '%s': %s", currentDir, matches);
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.java b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.java
index df0b7bd..b12867c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DefaultSettings.java
@@ -27,10 +27,10 @@ import java.io.File;
 public class DefaultSettings extends BaseSettings {
 
     public DefaultSettings(ServiceRegistryFactory serviceRegistryFactory,
-                    GradleInternal gradle,
-                    ClassLoaderScope classLoaderScope, File settingsDir,
+                    GradleInternal gradle, ClassLoaderScope classLoaderScope,
+                    ClassLoaderScope rootClassLoaderScope, File settingsDir,
                     ScriptSource settingsScript, StartParameter startParameter) {
-        super(serviceRegistryFactory, gradle, classLoaderScope, settingsDir, settingsScript, startParameter);
+        super(serviceRegistryFactory, gradle, classLoaderScope, rootClassLoaderScope, settingsDir, settingsScript, startParameter);
     }
 
 }
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 f028c56..de2415d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/DependencyResolutionLogger.java
@@ -34,7 +34,6 @@ public class DependencyResolutionLogger implements DependencyResolutionListener
         this.loggerFactory = loggerFactory;
     }
 
-    //TODO SF add concurrent unit test coverage
     public void beforeResolve(ResolvableDependencies dependencies) {
         LinkedList<ProgressLogger> loggers = progressLoggers.get();
         progressLoggers.set(loggers);
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/FixedBuildCancellationToken.java b/subprojects/core/src/main/groovy/org/gradle/initialization/FixedBuildCancellationToken.java
new file mode 100644
index 0000000..732728a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/FixedBuildCancellationToken.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/**
+ * Implementation used for toolingApi requests from consumer without cancellation support.
+ */
+public class FixedBuildCancellationToken implements BuildCancellationToken {
+    public boolean isCancellationRequested() {
+        return false;
+    }
+
+    public boolean addCallback(Runnable cancellationHandler) {
+        return false;
+    }
+
+    public void removeCallback(Runnable cancellationHandler) {
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncher.java b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncher.java
new file mode 100644
index 0000000..a763abf
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncher.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.BuildResult;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.internal.concurrent.Stoppable;
+
+/**
+ * This was the old Gradle embedding API (it used to be in the public `org.gradle` package). It is now internal and is due to be merged into
+ * {@link org.gradle.internal.invocation.BuildController}.
+ */
+public abstract class GradleLauncher implements Stoppable {
+
+    /**
+     * <p>Executes the build for this {@code GradleLauncher} instance and returns the result. Note that when the build
+     * fails, the exception is available using {@link org.gradle.BuildResult#getFailure()}.</p>
+     *
+     * @return The result. Never returns null.
+     */
+    public abstract BuildResult run();
+
+    /**
+     * Evaluates the settings and all the projects. The information about available tasks and projects is accessible via
+     * the {@link org.gradle.api.invocation.Gradle#getRootProject()} object.
+     *
+     * @return The result. Never returns null.
+     */
+    public abstract BuildResult getBuildAnalysis();
+
+    /**
+     * <p>Adds a listener to this build instance. The listener is notified of events which occur during the execution of
+     * the build. See {@link org.gradle.api.invocation.Gradle#addListener(Object)} for supported listener types.</p>
+     *
+     * @param listener The listener to add. Has no effect if the listener has already been added.
+     */
+    public abstract void addListener(Object listener);
+
+    /**
+     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to
+     * standard output by Gradle's logging system
+     *
+     * @param listener The listener to add. Has no effect if the listener has already been added.
+     */
+    public abstract void addStandardOutputListener(StandardOutputListener listener);
+
+    /**
+     * <p>Adds a {@link StandardOutputListener} to this build instance. The listener is notified of any text written to standard error by Gradle's logging system
+     *
+     * @param listener The listener to add. Has no effect if the listener has already been added.
+     */
+    public abstract void addStandardErrorListener(StandardOutputListener listener);
+}
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 d749341..619c36b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/GradleLauncherFactory.java
@@ -15,23 +15,26 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
 
 /**
- * <p>A {@code GradleLauncherFactory} is responsible for creating a {@link org.gradle.GradleLauncher} instance for a build, from a {@link
+ * <p>A {@code GradleLauncherFactory} is responsible for creating a {@link GradleLauncher} instance for a build, from a {@link
  * org.gradle.StartParameter}.</p>
  */
 public interface GradleLauncherFactory {
     /**
-     * Creates a new {@link org.gradle.GradleLauncher} instance for the given parameters.
+     * Creates a new {@link GradleLauncher} instance for the given parameters.
+     * Caller must call {@link GradleLauncher#stop()} when finished with the launcher.
      *
-     * @param startParameter The parameters to use for the build.
-     * @return The new instance.
+     * @param startParameter The settings for the build.
+     * @param requestContext The context in which the build is running.
      */
-    GradleLauncher newInstance(StartParameter startParameter, BuildRequestMetaData requestMetaData);
+    GradleLauncher newInstance(StartParameter startParameter, BuildRequestContext requestContext);
 
+    /**
+     * Creates a new {@link GradleLauncher} instance for the given parameters.
+     * Caller must call {@link GradleLauncher#stop()} when finished with the launcher.
+     */
     GradleLauncher newInstance(StartParameter startParameter);
 
-    StartParameter createStartParameter(String... commandLineArgs);
 }
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 dd678c1..7585f6d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/InstantiatingBuildLoader.java
@@ -16,8 +16,6 @@
 
 package org.gradle.initialization;
 
-import org.gradle.api.GradleException;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
@@ -32,34 +30,27 @@ public class InstantiatingBuildLoader implements BuildLoader {
     }
 
     /**
-     * Creates the {@link org.gradle.api.internal.GradleInternal} and {@link ProjectInternal} instances for the given root project,
-     * ready for the projects to be evaluated.
+     * Creates the {@link org.gradle.api.internal.GradleInternal} and {@link ProjectInternal} instances for the given root project, ready for the projects to be configured.
      */
-    public void load(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
-        createProjects(rootProjectDescriptor, gradle, classLoaderScope);
-        attachDefaultProject(gradle);
+    public void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope baseClassLoaderScope) {
+        createProjects(rootProjectDescriptor, gradle, baseClassLoaderScope);
+        attachDefaultProject(defaultProject, gradle);
     }
 
-    private void attachDefaultProject(GradleInternal gradle) {
-        ProjectSpec spec = ProjectSpecs.forStartParameter(gradle.getStartParameter());
-        try {
-            gradle.setDefaultProject(spec.selectProject(gradle.getRootProject().getProjectRegistry()));
-        } catch (InvalidUserDataException e) {
-            throw new GradleException(String.format("Could not select the default project for this build. %s",
-                    e.getMessage()), e);
-        }
+    private void attachDefaultProject(ProjectDescriptor defaultProject, GradleInternal gradle) {
+        gradle.setDefaultProject(gradle.getRootProject().getProjectRegistry().getProject(defaultProject.getPath()));
     }
 
-    private void createProjects(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
-        ProjectInternal rootProject = projectFactory.createProject(rootProjectDescriptor, null, gradle, classLoaderScope);
+    private void createProjects(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope baseClassLoaderScope) {
+        ProjectInternal rootProject = projectFactory.createProject(rootProjectDescriptor, null, gradle, baseClassLoaderScope.createChild("root-project"), baseClassLoaderScope);
         gradle.setRootProject(rootProject);
-        addProjects(rootProject, rootProjectDescriptor, gradle);
+        addProjects(rootProject, rootProjectDescriptor, gradle, baseClassLoaderScope);
     }
 
-    private void addProjects(ProjectInternal parent, ProjectDescriptor parentProjectDescriptor, GradleInternal gradle) {
+    private void addProjects(ProjectInternal parent, ProjectDescriptor parentProjectDescriptor, GradleInternal gradle, ClassLoaderScope baseClassLoaderScope) {
         for (ProjectDescriptor childProjectDescriptor : parentProjectDescriptor.getChildren()) {
-            ProjectInternal childProject = projectFactory.createProject(childProjectDescriptor, parent, gradle, parent.getClassLoaderScope().createChild());
-            addProjects(childProject, childProjectDescriptor, gradle);
+            ProjectInternal childProject = projectFactory.createProject(childProjectDescriptor, parent, gradle, parent.getClassLoaderScope().createChild("project-" + childProjectDescriptor.getName()), baseClassLoaderScope);
+            addProjects(childProject, childProjectDescriptor, gradle, baseClassLoaderScope);
         }
     }
 }
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 96e34b4..708d6ac 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/LayoutCommandLineConverter.java
@@ -16,39 +16,31 @@
 
 package org.gradle.initialization;
 
+import org.gradle.api.Transformer;
 import org.gradle.api.initialization.Settings;
-import org.gradle.api.internal.file.FileLookup;
-import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.BasicFileResolver;
 import org.gradle.cli.AbstractCommandLineConverter;
 import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
 
-public class LayoutCommandLineConverter extends AbstractCommandLineConverter<BuildLayoutParameters> {
+import java.io.File;
 
+public class LayoutCommandLineConverter extends AbstractCommandLineConverter<BuildLayoutParameters> {
     public static final String GRADLE_USER_HOME = "g";
     private static final String NO_SEARCH_UPWARDS = "u";
     private static final String PROJECT_DIR = "p";
-    private final FileLookup fileLookup;
-
-    public LayoutCommandLineConverter(FileLookup fileLookup) {
-        this.fileLookup = fileLookup;
-    }
-
-    protected BuildLayoutParameters newInstance() {
-        return new BuildLayoutParameters();
-    }
 
     public BuildLayoutParameters convert(ParsedCommandLine options, BuildLayoutParameters target) throws CommandLineArgumentException {
-        FileResolver resolver = fileLookup.getFileResolver(target.getProjectDir());
+        Transformer<File, String> resolver = new BasicFileResolver(target.getCurrentDir());
         if (options.hasOption(NO_SEARCH_UPWARDS)) {
             target.setSearchUpwards(false);
         }
         if (options.hasOption(PROJECT_DIR)) {
-            target.setProjectDir(resolver.resolve(options.option(PROJECT_DIR).getValue()));
+            target.setProjectDir(resolver.transform(options.option(PROJECT_DIR).getValue()));
         }
         if (options.hasOption(GRADLE_USER_HOME)) {
-            target.setGradleUserHomeDir(resolver.resolve(options.option(GRADLE_USER_HOME).getValue()));
+            target.setGradleUserHomeDir(resolver.transform(options.option(GRADLE_USER_HOME).getValue()));
         }
         return target;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/NoOpBuildEventConsumer.java b/subprojects/core/src/main/groovy/org/gradle/initialization/NoOpBuildEventConsumer.java
new file mode 100644
index 0000000..658854f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/NoOpBuildEventConsumer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class NoOpBuildEventConsumer implements BuildEventConsumer {
+    @Override
+    public void dispatch(Object message) {
+    }
+}
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 72991d6..486756a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectDirectoryProjectSpec.java
@@ -15,26 +15,20 @@
  */
 package org.gradle.initialization;
 
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.project.ProjectIdentifier;
 import org.gradle.api.internal.project.ProjectRegistry;
-import org.gradle.api.InvalidUserDataException;
 
 import java.io.File;
-import java.io.Serializable;
+import java.util.List;
 
-public class ProjectDirectoryProjectSpec extends AbstractProjectSpec implements Serializable {
+public class ProjectDirectoryProjectSpec extends AbstractProjectSpec {
     private final File dir;
 
     public ProjectDirectoryProjectSpec(File dir) {
         this.dir = dir;
     }
 
-    public String getDisplayName() {
-        return String.format("with project directory '%s'", dir);
-    }
-
     protected String formatNoMatchesMessage() {
         return String.format("No projects in this build have project directory '%s'.", dir);
     }
@@ -43,8 +37,13 @@ public class ProjectDirectoryProjectSpec extends AbstractProjectSpec implements
         return String.format("Multiple projects in this build have project directory '%s': %s", dir, matches);
     }
 
-    protected boolean select(ProjectIdentifier project) {
-        return project.getProjectDir().equals(dir);
+    @Override
+    protected <T extends ProjectIdentifier> void select(ProjectRegistry<? extends T> candidates, List<? super T> matches) {
+        for (T candidate : candidates.getAllProjects()) {
+            if (candidate.getProjectDir().equals(dir)) {
+                matches.add(candidate);
+            }
+        }
     }
 
     @Override
@@ -56,12 +55,4 @@ public class ProjectDirectoryProjectSpec extends AbstractProjectSpec implements
             throw new InvalidUserDataException(String.format("Project directory '%s' is not a directory.", dir));
         }
     }
-
-    public boolean equals(Object obj) {
-        return EqualsBuilder.reflectionEquals(this, obj);
-    }
-
-    public int hashCode() {
-        return HashCodeBuilder.reflectionHashCode(this);
-    }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPathProjectSpec.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPathProjectSpec.java
deleted file mode 100644
index 4acf378..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPathProjectSpec.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.initialization;
-
-import com.google.common.base.Strings;
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.api.internal.project.ProjectRegistry;
-
-import java.io.Serializable;
-
-public class ProjectPathProjectSpec extends AbstractProjectSpec implements Serializable {
-    private final String projectPath;
-
-    public ProjectPathProjectSpec(String projectPath) {
-        this.projectPath = projectPath;
-    }
-
-    public String getDisplayName() {
-        return String.format("project has path '%s'", projectPath);
-    }
-
-    protected String formatNoMatchesMessage() {
-        return String.format("No projects in this build have path '%s'.", projectPath);
-    }
-
-    protected String formatMultipleMatchesMessage(Iterable<? extends ProjectIdentifier> matches) {
-        return String.format("Multiple projects in this build have path '%s': %s", projectPath, matches);
-    }
-
-    protected boolean select(ProjectIdentifier project) {
-        return projectPath.equals(project.getPath());
-    }
-
-    @Override
-    protected void checkPreconditions(ProjectRegistry<?> registry) {
-        // TODO(radimk): pattern for path?
-        if (Strings.isNullOrEmpty(projectPath)) {
-            throw new InvalidUserDataException("Project path must not be empty.");
-        }
-    }
-
-    public boolean equals(Object obj) {
-        return EqualsBuilder.reflectionEquals(this, obj);
-    }
-
-    public int hashCode() {
-        return HashCodeBuilder.reflectionHashCode(this);
-    }
-}
\ No newline at end of file
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 7af724a..0951b45 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoader.java
@@ -16,6 +16,7 @@
 
 package org.gradle.initialization;
 
+import groovy.lang.MissingPropertyException;
 import org.gradle.api.Project;
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.GradleInternal;
@@ -42,8 +43,8 @@ public class ProjectPropertySettingBuildLoader implements BuildLoader {
         this.propertiesLoader = propertiesLoader;
     }
 
-    public void load(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
-        buildLoader.load(rootProjectDescriptor, gradle, classLoaderScope);
+    public void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
+        buildLoader.load(rootProjectDescriptor, defaultProject, gradle, classLoaderScope);
         setProjectProperties(gradle.getRootProject());
     }
 
@@ -69,9 +70,13 @@ public class ProjectPropertySettingBuildLoader implements BuildLoader {
         Map<String, String> mergedProperties = propertiesLoader.mergeProperties(new HashMap(projectProperties));
         ExtraPropertiesExtension extraProperties = new DslObject(project).getExtensions().getExtraProperties();
         for (Map.Entry<String, String> entry: mergedProperties.entrySet()) {
-            if (project.hasProperty(entry.getKey())) {
-                project.setProperty(entry.getKey(), entry.getValue());    
-            } else {
+            try {
+                project.setProperty(entry.getKey(), entry.getValue());
+            } catch (MissingPropertyException e) {
+                if (!entry.getKey().equals(e.getProperty())) {
+                    throw e;
+                }
+                // Ignore and define as an extra property
                 extraProperties.set(entry.getKey(), entry.getValue());
             }
         }
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 2b2133e..44bb17f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpec.java
@@ -15,26 +15,21 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.project.ProjectIdentifier;
 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(ProjectRegistry<?> registry);
+    boolean containsProject(ProjectRegistry<? extends ProjectIdentifier> 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(ProjectRegistry<? extends T> registry) throws
-            InvalidUserDataException;
-
-    /**
-     * Returns the display name of this spec. Used for logging and error messages.
+     * @return the project.
+     * @throws InvalidUserDataException When project cannot be selected due to some user input mismatch, or when there are no matching projects
+     * or multiple matching projects.
      */
-    String getDisplayName();
+    <T extends ProjectIdentifier> T selectProject(ProjectRegistry<? extends T> registry) throws InvalidUserDataException;
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpecs.java b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpecs.java
index d230000..4b79d75 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpecs.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ProjectSpecs.java
@@ -17,20 +17,21 @@
 package org.gradle.initialization;
 
 import org.gradle.StartParameter;
+import org.gradle.api.internal.SettingsInternal;
 
 import java.io.File;
 
 class ProjectSpecs {
 
-    static ProjectSpec forStartParameter(StartParameter startParameter) {
-        String explicitProjectPath = startParameter.getProjectPath();
+    static ProjectSpec forStartParameter(StartParameter startParameter, SettingsInternal settings) {
         File explicitProjectDir = startParameter.getProjectDir();
         File explicitBuildFile = startParameter.getBuildFile();
-        ProjectSpec spec = explicitProjectPath != null
-                ? new ProjectPathProjectSpec(explicitProjectPath)
-                : explicitBuildFile != null
-                ? new BuildFileProjectSpec(explicitBuildFile)
-                : explicitProjectDir == null ? new DefaultProjectSpec(startParameter.getCurrentDir()) : new ProjectDirectoryProjectSpec(explicitProjectDir);
-        return spec;
+        if (explicitBuildFile != null) {
+            return new BuildFileProjectSpec(explicitBuildFile);
+        }
+        if (explicitProjectDir != null) {
+            return new ProjectDirectoryProjectSpec(explicitProjectDir);
+        }
+        return new DefaultProjectSpec(startParameter.getCurrentDir(), settings);
     }
 }
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 dd0d359..83ba64c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessor.java
@@ -31,9 +31,9 @@ public class PropertiesLoadingSettingsProcessor implements SettingsProcessor {
 
     public SettingsInternal process(GradleInternal gradle,
                                     SettingsLocation settingsLocation,
-                                    ClassLoaderScope classLoaderScope,
+                                    ClassLoaderScope baseClassLoaderScope,
                                     StartParameter startParameter) {
         propertiesLoader.loadProperties(settingsLocation.getSettingsDir());
-        return processor.process(gradle, settingsLocation, classLoaderScope, startParameter);
+        return processor.process(gradle, settingsLocation, baseClassLoaderScope, 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 795b02c..1bc80df 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
@@ -53,12 +53,12 @@ public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
 
     public SettingsInternal process(GradleInternal gradle,
                                     SettingsLocation settingsLocation,
-                                    ClassLoaderScope classLoaderScope,
+                                    ClassLoaderScope baseClassLoaderScope,
                                     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, classLoaderScope);
+                settingsLocation.getSettingsScriptSource(), properties, startParameter, baseClassLoaderScope);
         applySettingsScript(settingsLocation, settings);
         logger.debug("Timing: Processing settings took: {}", settingsProcessingClock.getTime());
         return settings;
@@ -66,9 +66,9 @@ public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
 
     private void applySettingsScript(SettingsLocation settingsLocation, final SettingsInternal settings) {
         ScriptSource settingsScriptSource = settingsLocation.getSettingsScriptSource();
-        ClassLoaderScope classLoaderScope = settings.getClassLoaderScope();
-        ScriptHandler scriptHandler = scriptHandlerFactory.create(settingsScriptSource, classLoaderScope);
-        ScriptPlugin configurer = configurerFactory.create(settingsScriptSource, scriptHandler, classLoaderScope, "buildscript", SettingsScript.class);
+        ClassLoaderScope settingsClassLoaderScope = settings.getClassLoaderScope();
+        ScriptHandler scriptHandler = scriptHandlerFactory.create(settingsScriptSource, settingsClassLoaderScope);
+        ScriptPlugin configurer = configurerFactory.create(settingsScriptSource, scriptHandler, settingsClassLoaderScope, settings.getRootClassLoaderScope(), "buildscript", SettingsScript.class, false);
         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 fe319ec..15c910d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsFactory.java
@@ -37,10 +37,10 @@ public class SettingsFactory {
 
     public SettingsInternal createSettings(GradleInternal gradle, File settingsDir, ScriptSource settingsScript,
                                            Map<String, String> gradleProperties, StartParameter startParameter,
-                                           ClassLoaderScope classLoaderScope) {
+                                           ClassLoaderScope rootClassLoaderScope) {
 
         DefaultSettings settings = instantiator.newInstance(DefaultSettings.class,
-                serviceRegistryFactory, gradle, classLoaderScope, settingsDir, settingsScript, startParameter
+                serviceRegistryFactory, gradle, rootClassLoaderScope.createChild("settings"), rootClassLoaderScope, 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 4e09148..d38d400 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsHandler.java
@@ -45,30 +45,32 @@ public class SettingsHandler {
         StartParameter startParameter = gradle.getStartParameter();
         SettingsInternal settings = findSettingsAndLoadIfAppropriate(gradle, startParameter);
 
-        ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter);
-
-        if (!spec.containsProject(settings.getProjectRegistry())) {
-            // The settings we found did not include the desired default project. Try again with an empty settings file.
-
-            StartParameter noSearchParameter = startParameter.newInstance();
-            noSearchParameter.useEmptySettings();
-            settings = findSettingsAndLoadIfAppropriate(gradle, noSearchParameter);
-            if (settings == null) // not using an assert to make sure it is not disabled
-            {
-                throw new InternalError("Empty settings file does not contain expected project.");
-            }
-
-            // Set explicit build file, if required
-            if (noSearchParameter.getBuildFile() != null) {
-                ProjectDescriptor rootProject = settings.getRootProject();
-                assert noSearchParameter.getBuildFile().getParentFile().equals(rootProject.getProjectDir());
-                rootProject.setBuildFileName(noSearchParameter.getBuildFile().getName());
-            }
+        ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter, settings);
+
+        if (spec.containsProject(settings.getProjectRegistry())) {
+            setDefaultProject(spec, settings);
+            return settings;
+        }
+
+        // Try again with empty settings
+        StartParameter noSearchParameter = startParameter.newInstance();
+        noSearchParameter.useEmptySettings();
+        settings = findSettingsAndLoadIfAppropriate(gradle, noSearchParameter);
+
+        // Set explicit build file, if required
+        if (noSearchParameter.getBuildFile() != null) {
+            ProjectDescriptor rootProject = settings.getRootProject();
+            rootProject.setBuildFileName(noSearchParameter.getBuildFile().getName());
         }
+        setDefaultProject(spec, settings);
 
         return settings;
     }
 
+    private void setDefaultProject(ProjectSpec spec, SettingsInternal settings) {
+        settings.setDefaultProject(spec.selectProject(settings.getProjectRegistry()));
+    }
+
     /**
      * Finds the settings.gradle for the given startParameter, and loads it if contains the project selected by the
      * startParameter, or if the startParameter explicitly specifies a settings script.  If the settings file is not
@@ -81,20 +83,14 @@ public class SettingsHandler {
         // We found the desired settings file, now build the associated buildSrc before loading settings.  This allows
         // the settings script to reference classes in the buildSrc.
         StartParameter buildSrcStartParameter = startParameter.newBuild();
-        buildSrcStartParameter.setCurrentDir(new File(settingsLocation.getSettingsDir(),
-                BaseSettings.DEFAULT_BUILD_SRC_DIR));
+        buildSrcStartParameter.setCurrentDir(new File(settingsLocation.getSettingsDir(), BaseSettings.DEFAULT_BUILD_SRC_DIR));
         ClassLoaderScope buildSourceClassLoader = buildSourceBuilder.buildAndCreateClassLoader(buildSrcStartParameter);
 
-        return loadSettings(gradle, settingsLocation, buildSourceClassLoader.createRebasedChild(), startParameter);
+        return settingsProcessor.process(gradle, settingsLocation, buildSourceClassLoader, startParameter);
     }
 
     private SettingsLocation findSettings(StartParameter startParameter) {
         return settingsFinder.find(startParameter);
     }
-
-    private SettingsInternal loadSettings(GradleInternal gradle, SettingsLocation settingsLocation,
-                                          ClassLoaderScope classLoaderScope, StartParameter startParameter) {
-        return settingsProcessor.process(gradle, settingsLocation, classLoaderScope, startParameter);
-    }
 }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsLocation.java b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsLocation.java
index 54de28e..85d6ece 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsLocation.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsLocation.java
@@ -29,7 +29,7 @@ public class SettingsLocation {
         this.settingsDir = settingsDir;
         this.settingsScriptSource = settingsFile == null
                 ? new StringScriptSource("empty settings script", "")
-                : new UriScriptSource("settings file", settingsFile);
+                : UriScriptSource.file("settings file", settingsFile);
     }
 
     /**
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 0db94ef..57257a6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/SettingsProcessor.java
@@ -26,6 +26,6 @@ import org.gradle.api.internal.initialization.ClassLoaderScope;
 public interface SettingsProcessor {
     SettingsInternal process(GradleInternal gradle,
                              SettingsLocation settingsLocation,
-                             ClassLoaderScope classLoaderScope,
+                             ClassLoaderScope baseClassLoaderScope,
                              StartParameter startParameter);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/StackTraceSanitizingExceptionAnalyser.java b/subprojects/core/src/main/groovy/org/gradle/initialization/StackTraceSanitizingExceptionAnalyser.java
new file mode 100644
index 0000000..c4e16c7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/StackTraceSanitizingExceptionAnalyser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.codehaus.groovy.runtime.StackTraceUtils;
+import org.gradle.api.internal.ExceptionAnalyser;
+
+public class StackTraceSanitizingExceptionAnalyser implements ExceptionAnalyser {
+    private final ExceptionAnalyser analyser;
+
+    public StackTraceSanitizingExceptionAnalyser(ExceptionAnalyser analyser) {
+        this.analyser = analyser;
+    }
+
+    public Throwable transform(Throwable exception) {
+        return StackTraceUtils.deepSanitize(analyser.transform(exception));
+    }
+}
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
index cfd286a..b694ca1 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilder.java
@@ -16,12 +16,12 @@
 
 package org.gradle.initialization.buildsrc;
 
-import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.cache.CacheRepository;
 import org.gradle.cache.PersistentCache;
 import org.gradle.cache.internal.FileLockManager;
+import org.gradle.initialization.GradleLauncher;
 import org.gradle.initialization.GradleLauncherFactory;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
@@ -49,14 +49,10 @@ public class BuildSourceBuilder {
 
     public ClassLoaderScope buildAndCreateClassLoader(StartParameter startParameter) {
         ClassPath classpath = createBuildSourceClasspath(startParameter);
-        if (classpath.isEmpty()) {
-            return classLoaderScope;
-        } else {
-            ClassLoaderScope childScope = classLoaderScope.createChild();
-            childScope.export(classpath);
-            childScope.lock();
-            return childScope;
-        }
+        ClassLoaderScope childScope = classLoaderScope.createChild(startParameter.getCurrentDir().getAbsolutePath());
+        childScope.export(classpath);
+        childScope.lock();
+        return childScope;
     }
 
     ClassPath createBuildSourceClasspath(StartParameter startParameter) {
@@ -74,7 +70,11 @@ public class BuildSourceBuilder {
         final PersistentCache buildSrcCache = createCache(startParameter);
         try {
             GradleLauncher gradleLauncher = buildGradleLauncher(startParameter);
-            return buildSrcCache.useCache("rebuild buildSrc", new BuildSrcUpdateFactory(buildSrcCache, gradleLauncher, new BuildSrcBuildListenerFactory()));
+            try {
+                return buildSrcCache.useCache("rebuild buildSrc", new BuildSrcUpdateFactory(buildSrcCache, gradleLauncher, new BuildSrcBuildListenerFactory()));
+            } finally {
+                gradleLauncher.stop();
+            }
         } finally {
             // This isn't quite right. We should not unlock the classes until we're finished with them, and the classes may be used across multiple builds
             buildSrcCache.close();
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
index e52586d..89f811f 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
@@ -18,7 +18,8 @@ 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.internal.component.BuildableJavaComponent;
+import org.gradle.api.internal.component.ComponentRegistry;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.initialization.ModelConfigurationListener;
 import org.gradle.util.WrapUtil;
@@ -53,7 +54,7 @@ public class BuildSrcBuildListenerFactory {
         }
 
         public void onConfigure(GradleInternal gradle) {
-            EmbeddableJavaProject projectInfo = gradle.getRootProject().getConvention().getPlugin(EmbeddableJavaProject.class);
+            BuildableJavaComponent projectInfo = gradle.getRootProject().getServices().get(ComponentRegistry.class).getMainComponent();
             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
index 31fe50b..2c6494b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java
@@ -16,7 +16,7 @@
 
 package org.gradle.initialization.buildsrc;
 
-import org.gradle.GradleLauncher;
+import org.gradle.initialization.GradleLauncher;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayout.java b/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayout.java
index 0f1031a..699abb7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayout.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayout.java
@@ -22,6 +22,8 @@ import java.io.File;
 public class BuildLayout extends SettingsLocation {
     private final File rootDirectory;
 
+    // Note: `null` for `settingsFile` means explicitly no settings
+    //       A non null value can be a non existent file, which is semantically equivalent to an empty file
     public BuildLayout(File rootDirectory, File settingsDir, File settingsFile) {
         super(settingsDir, settingsFile);
         this.rootDirectory = rootDirectory;
diff --git a/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayoutFactory.java b/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayoutFactory.java
index ecaf71b..4be55b8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayoutFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/initialization/layout/BuildLayoutFactory.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.initialization.layout;
 
+import org.gradle.internal.resource.ResourceNotFoundException;
+
 import java.io.File;
 
 public class BuildLayoutFactory {
@@ -32,8 +34,12 @@ public class BuildLayoutFactory {
         if (configuration.isUseEmptySettings()) {
             return new BuildLayout(configuration.getCurrentDir(), configuration.getCurrentDir(), null);
         }
-        if (configuration.getSettingsFile() != null) {
-            return new BuildLayout(configuration.getCurrentDir(), configuration.getCurrentDir(), configuration.getSettingsFile());
+        File explicitSettingsFile = configuration.getSettingsFile();
+        if (explicitSettingsFile != null) {
+            if (!explicitSettingsFile.isFile()) {
+                throw new ResourceNotFoundException(explicitSettingsFile.toURI(), String.format("Could not read settings file '%s' as it does not exist.", explicitSettingsFile.getAbsolutePath()));
+            }
+            return new BuildLayout(configuration.getCurrentDir(), configuration.getCurrentDir(), explicitSettingsFile);
         }
 
         File currentDir = configuration.getCurrentDir();
@@ -56,11 +62,7 @@ public class BuildLayoutFactory {
                 return layout(candidate, settingsFile.getParentFile(), settingsFile);
             }
         }
-        return layout(currentDir, currentDir);
-    }
-
-    private BuildLayout layout(File rootDir, File settingsDir) {
-        return new BuildLayout(rootDir, settingsDir, null);
+        return layout(currentDir, currentDir, settingsFile);
     }
 
     private BuildLayout layout(File rootDir, File settingsDir, File settingsFile) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/DefaultTaskExecutionRequest.java b/subprojects/core/src/main/groovy/org/gradle/internal/DefaultTaskExecutionRequest.java
new file mode 100644
index 0000000..674e2b4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/DefaultTaskExecutionRequest.java
@@ -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.internal;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import org.gradle.TaskExecutionRequest;
+import org.gradle.api.Nullable;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class DefaultTaskExecutionRequest implements TaskExecutionRequest, Serializable {
+    private final List<String> args;
+    private final String projectPath;
+
+    public DefaultTaskExecutionRequest(Iterable<String> args) {
+        this(args, null);
+    }
+
+    public DefaultTaskExecutionRequest(Iterable<String> args, @Nullable String projectPath) {
+        this.args = Lists.newArrayList(args);
+        this.projectPath = projectPath;
+    }
+
+    public List<String> getArgs() {
+        return args;
+    }
+
+    public String getProjectPath() {
+        return projectPath;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultTaskExecutionRequest that = (DefaultTaskExecutionRequest) o;
+        if (!Objects.equal(projectPath, that.projectPath)) {
+            return false;
+        }
+        if (!Objects.equal(args, that.args)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = getArgs().hashCode();
+        result = 31 * result + (projectPath != null ? projectPath.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultTaskExecutionRequest{"
+                + "args=" + args
+                + ",projectPath='" + projectPath + '\''
+                + '}';
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/artifacts/repositories/AuthenticationSupportedInternal.java b/subprojects/core/src/main/groovy/org/gradle/internal/artifacts/repositories/AuthenticationSupportedInternal.java
new file mode 100644
index 0000000..70e4592
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/artifacts/repositories/AuthenticationSupportedInternal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.artifacts.repositories;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.repositories.AuthenticationSupported;
+import org.gradle.api.credentials.Credentials;
+
+public interface AuthenticationSupportedInternal extends AuthenticationSupported {
+    /**
+     * Returns the configured credentials used to authenticate with this repository, or <code>null</code> if no credentials have been configured.
+     */
+    @Incubating
+    @Nullable
+    Credentials getConfiguredCredentials();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/credentials/DefaultAwsCredentials.java b/subprojects/core/src/main/groovy/org/gradle/internal/credentials/DefaultAwsCredentials.java
new file mode 100644
index 0000000..83491dd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/credentials/DefaultAwsCredentials.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.credentials;
+
+import org.gradle.api.credentials.AwsCredentials;
+
+public class DefaultAwsCredentials implements AwsCredentials {
+
+    private String accessKey;
+    private String secretKey;
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/AbstractMultiCauseException.java b/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/AbstractMultiCauseException.java
deleted file mode 100644
index ed70abc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/AbstractMultiCauseException.java
+++ /dev/null
@@ -1,111 +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.internal.exceptions;
-
-import org.gradle.api.GradleException;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public class AbstractMultiCauseException extends GradleException implements MultiCauseException {
-    private final List<Throwable> causes = new CopyOnWriteArrayList<Throwable>();
-    private transient ThreadLocal<Boolean> hideCause = threadLocal();
-
-    public AbstractMultiCauseException(String message) {
-        super(message);
-    }
-
-    public AbstractMultiCauseException(String message, Throwable... causes) {
-        super(message);
-        this.causes.addAll(Arrays.asList(causes));
-    }
-
-    public AbstractMultiCauseException(String message, Iterable<? extends Throwable> causes) {
-        super(message);
-        initCauses(causes);
-    }
-
-    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
-        inputStream.defaultReadObject();
-        hideCause = threadLocal();
-    }
-
-    private ThreadLocal<Boolean> threadLocal() {
-        return new ThreadLocal<Boolean>() {
-            @Override
-            protected Boolean initialValue() {
-                return false;
-            }
-        };
-    }
-
-    public List<? extends Throwable> getCauses() {
-        return causes;
-    }
-
-    @Override
-    public Throwable initCause(Throwable throwable) {
-        causes.clear();
-        causes.add(throwable);
-        return null;
-    }
-
-    public void initCauses(Iterable<? extends Throwable> causes) {
-        this.causes.clear();
-        for (Throwable cause : causes) {
-            this.causes.add(cause);
-        }
-    }
-
-    @Override
-    public Throwable getCause() {
-        if (hideCause.get()) {
-            return null;
-        }
-        return causes.isEmpty() ? null : causes.get(0);
-    }
-
-    @Override
-    public void printStackTrace(PrintStream printStream) {
-        PrintWriter writer = new PrintWriter(printStream);
-        printStackTrace(writer);
-        writer.flush();
-    }
-
-    @Override
-    public void printStackTrace(PrintWriter printWriter) {
-        if (causes.size() <= 1) {
-            super.printStackTrace(printWriter);
-            return;
-        }
-
-        hideCause.set(true);
-        try {
-            super.printStackTrace(printWriter);
-            for (int i = 0; i < causes.size(); i++) {
-                Throwable cause = causes.get(i);
-                printWriter.format("Cause %s: ", i + 1);
-                cause.printStackTrace(printWriter);
-            }
-        } finally {
-            hideCause.set(false);
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/LocationAwareException.java b/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/LocationAwareException.java
index a1d17e4..6464e79 100755
--- a/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/LocationAwareException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/exceptions/LocationAwareException.java
@@ -29,22 +29,25 @@ import java.util.List;
  * A {@code LocationAwareException} is an exception which can be annotated with a location in a script.
  */
 public class LocationAwareException extends GradleException implements FailureResolutionAware {
-    private final ScriptSource source;
+    private final String sourceDisplayName;
     private final Integer lineNumber;
 
     public LocationAwareException(Throwable cause, ScriptSource source, Integer lineNumber) {
-        this.source = source;
+        this(cause, source != null ? source.getDisplayName() : null, lineNumber);
+    }
+
+    public LocationAwareException(Throwable cause, String sourceDisplayName, Integer lineNumber) {
+        this.sourceDisplayName = sourceDisplayName;
         this.lineNumber = lineNumber;
         initCause(cause);
     }
 
     /**
-     * <p>Returns the source of the script where this exception occurred.</p>
-     *
-     * @return The source. May return null.
+     * <p>Returns the display name of the script where this exception occurred.</p>
+     * @return The source display name. May return null.
      */
-    public ScriptSource getScriptSource() {
-        return source;
+    public String getSourceDisplayName() {
+        return sourceDisplayName;
     }
 
     /**
@@ -53,10 +56,10 @@ public class LocationAwareException extends GradleException implements FailureRe
      * @return The location description. May return null.
      */
     public String getLocation() {
-        if (source == null) {
+        if (sourceDisplayName == null) {
             return null;
         }
-        String sourceMsg = StringUtils.capitalize(source.getDisplayName());
+        String sourceMsg = StringUtils.capitalize(sourceDisplayName);
         if (lineNumber == null) {
             return sourceMsg;
         }
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
index 4b10d5d..7c8694d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
@@ -51,7 +51,7 @@ public class LoggingDeprecatedFeatureHandler implements DeprecatedFeatureHandler
             StringBuilder message = new StringBuilder();
             locationReporter.reportLocation(usage, message);
             if (message.length() > 0) {
-                message.append(SystemProperties.getLineSeparator());
+                message.append(SystemProperties.getInstance().getLineSeparator());
             }
             message.append(usage.getMessage());
             logTraceIfNecessary(usage.getStack(), message);
@@ -62,7 +62,7 @@ public class LoggingDeprecatedFeatureHandler implements DeprecatedFeatureHandler
     private void logTraceIfNecessary(List<StackTraceElement> stack, StringBuilder message) {
         if (isTraceLoggingEnabled()) {
             for (StackTraceElement frame : stack) {
-                message.append(SystemProperties.getLineSeparator());
+                message.append(SystemProperties.getInstance().getLineSeparator());
                 message.append("    ");
                 message.append(frame.toString());
             }
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/html/SimpleHtmlWriter.java b/subprojects/core/src/main/groovy/org/gradle/internal/html/SimpleHtmlWriter.java
new file mode 100644
index 0000000..334ea78
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/html/SimpleHtmlWriter.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.internal.html;
+
+import org.gradle.internal.xml.SimpleMarkupWriter;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * <p>A streaming HTML writer.</p>
+ */
+public class SimpleHtmlWriter extends SimpleMarkupWriter {
+
+    public SimpleHtmlWriter(Writer writer) throws IOException {
+        this(writer, null);
+    }
+
+    public SimpleHtmlWriter(Writer writer, String indent) throws IOException {
+        super(writer, indent);
+        writeHtmlHeader();
+    }
+
+    private void writeHtmlHeader() throws IOException {
+        writeRaw("<!DOCTYPE html>");
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildAction.java b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildAction.java
new file mode 100644
index 0000000..c4000c8
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildAction.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.internal.invocation;
+
+import org.gradle.StartParameter;
+
+/**
+ * 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 {
+    StartParameter getStartParameter();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildActionRunner.java b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildActionRunner.java
new file mode 100644
index 0000000..1805bb6
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildActionRunner.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.invocation;
+
+/**
+ * Responsible for executing a {@link BuildAction} and generating the result.
+ */
+public interface BuildActionRunner {
+    /**
+     * Runs the given action, and attaches the result, if any, to the provided controller.
+     */
+    void run(BuildAction action, BuildController buildController);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildController.java b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildController.java
new file mode 100644
index 0000000..e032245
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/invocation/BuildController.java
@@ -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.internal.invocation;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.internal.GradleInternal;
+
+/**
+ * This is intended to eventually replace {@link org.gradle.initialization.GradleLauncher} internally. It's pretty rough at the moment.
+ */
+public interface BuildController {
+    /**
+     * @return The {@link org.gradle.api.internal.GradleInternal} object that represents the build invocation.
+     */
+    GradleInternal getGradle();
+
+    /**
+     * Configures the build and schedules and executes tasks specified in the {@link org.gradle.StartParameter} associated with the build.
+     *
+     * @return The {@link org.gradle.api.internal.GradleInternal} object that represents the build invocation.
+     */
+    GradleInternal run();
+
+    /**
+     * Configures the build but does not schedule or run any tasks.
+     *
+     * @return The {@link org.gradle.api.internal.GradleInternal} object that represents the build invocation.
+     */
+    GradleInternal configure();
+
+    /**
+     * Returns true if a result (possibly null) has been specified.
+     */
+    boolean hasResult();
+
+    /**
+     * Returns the result for the build action.
+     */
+    @Nullable
+    Object getResult();
+
+    /**
+     * Sets the result for the build action.
+     */
+    void setResult(@Nullable Object result);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogInfo.java b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogInfo.java
new file mode 100644
index 0000000..d6c9dcd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging;
+
+import java.io.File;
+
+class BuildOperationLogInfo {
+    private final File outputFile;
+    private final int maximumFailures;
+    private final String taskName;
+
+    BuildOperationLogInfo(String taskName, File outputFile, int maximumFailures) {
+        this.outputFile = outputFile;
+        this.maximumFailures = maximumFailures;
+        this.taskName = taskName;
+    }
+
+    public int getMaximumFailedOperationsShown() {
+        return maximumFailures;
+    }
+
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    public String getTaskName() {
+        return taskName;
+    }
+
+    public String toString() {
+        return "log for " + getTaskName() + " to " + getOutputFile().getAbsolutePath() + ", showing up to " + getMaximumFailedOperationsShown() + " failures";
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogger.java b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogger.java
new file mode 100644
index 0000000..690a7a3
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLogger.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging;
+
+public interface BuildOperationLogger {
+    void start();
+    void operationSuccess(String description, String output);
+    void operationFailed(String description, String output);
+    void done();
+    String getLogLocation();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLoggerFactory.java b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLoggerFactory.java
new file mode 100644
index 0000000..0fc2c64
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/BuildOperationLoggerFactory.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging;
+
+import java.io.File;
+
+public interface BuildOperationLoggerFactory {
+    BuildOperationLogger newOperationLogger(String taskName, File outputDir);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLogger.java b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLogger.java
new file mode 100644
index 0000000..6cc7580
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLogger.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.logging.ConsoleRenderer;
+
+import java.io.PrintWriter;
+
+class DefaultBuildOperationLogger implements BuildOperationLogger {
+    private final BuildOperationLogInfo configuration;
+    private final Logger logger;
+    private final PrintWriter logWriter;
+
+    private boolean started;
+    private int numberOfFailedOperationsSeen;
+
+    DefaultBuildOperationLogger(BuildOperationLogInfo configuration, Logger logger, PrintWriter logWriter) {
+        this.configuration = configuration;
+        this.logger = logger;
+        this.logWriter = logWriter;
+
+        this.numberOfFailedOperationsSeen = 0;
+        this.started = false;
+    }
+
+    @Override
+    public void start() {
+        assert !started;
+        logInBoth(LogLevel.INFO, String.format("See %s for all output for %s.", getLogLocation(), configuration.getTaskName()));
+        started = true;
+    }
+
+    @Override
+    public synchronized void operationSuccess(String description, String output) {
+        assert started;
+        logInBoth(LogLevel.DEBUG, description.concat(" successful."));
+        maybeShowSuccess(output);
+    }
+
+    @Override
+    public synchronized void operationFailed(String description, String output) {
+        assert started;
+        logInBoth(LogLevel.DEBUG, description.concat(" failed."));
+        maybeShowFailure(output);
+    }
+
+    @Override
+    public void done() {
+        assert started;
+        int suppressedCount = numberOfFailedOperationsSeen - configuration.getMaximumFailedOperationsShown();
+        if (suppressedCount > 0) {
+            logger.log(LogLevel.ERROR, String.format("...output for %d more failed operation(s) continued in %s.", suppressedCount, getLogLocation()));
+        }
+        logInBoth(LogLevel.INFO, String.format("Finished %s, see full log %s.", configuration.getTaskName(), getLogLocation()));
+        logWriter.close();
+        started = false;
+    }
+
+    private void maybeShowSuccess(String output) {
+        logger.log(LogLevel.INFO, output);
+        logWriter.println(output);
+    }
+
+    private void maybeShowFailure(String output) {
+        if (numberOfFailedOperationsSeen < configuration.getMaximumFailedOperationsShown()) {
+            logger.log(LogLevel.ERROR, output);
+        }
+        logWriter.println(output);
+        numberOfFailedOperationsSeen++;
+    }
+
+    private void logInBoth(LogLevel logLevel, String message) {
+        logger.log(logLevel, message);
+        logWriter.println(message);
+    }
+
+    public String getLogLocation() {
+        return new ConsoleRenderer().asClickableFileUrl(configuration.getOutputFile());
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactory.java b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactory.java
new file mode 100644
index 0000000..47a0a1f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.UncheckedException;
+import org.gradle.util.GFileUtils;
+
+import java.io.*;
+
+public class DefaultBuildOperationLoggerFactory implements BuildOperationLoggerFactory {
+    private static final int MAX_FAILURES = 10;
+
+    private final Logger logger;
+
+    DefaultBuildOperationLoggerFactory(Logger logger) {
+        this.logger = logger;
+    }
+
+    public DefaultBuildOperationLoggerFactory() {
+        this(Logging.getLogger(DefaultBuildOperationLoggerFactory.class));
+    }
+
+
+    @Override
+    public BuildOperationLogger newOperationLogger(String taskName, File outputDir) {
+        final File outputFile = createOutputFile(outputDir);
+        final PrintWriter logWriter = createWriter(outputFile);
+        final BuildOperationLogInfo configuration = createLogInfo(taskName, outputFile, MAX_FAILURES);
+        return new DefaultBuildOperationLogger(configuration, logger, logWriter);
+    }
+
+    protected File createOutputFile(File outputDir) {
+        GFileUtils.mkdirs(outputDir);
+        return new File(outputDir, "output.txt");
+    }
+
+    protected PrintWriter createWriter(File outputFile) {
+        PrintWriter logWriter = null;
+        try {
+            logWriter = new PrintWriter(new FileWriter(outputFile), true);
+        } catch (IOException e) {
+            UncheckedException.throwAsUncheckedException(e);
+        }
+        return logWriter;
+    }
+
+    protected BuildOperationLogInfo createLogInfo(String taskName, File outputFile, int maximumFailures) {
+        final BuildOperationLogInfo configuration;
+        if (logger.isDebugEnabled()) {
+            // show all operation output when debug is enabled
+            configuration = new BuildOperationLogInfo(taskName, outputFile, Integer.MAX_VALUE);
+        } else {
+            configuration = new BuildOperationLogInfo(taskName, outputFile, maximumFailures);
+        }
+        return configuration;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/GroupedAndNamedUniqueFileStore.java b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/GroupedAndNamedUniqueFileStore.java
new file mode 100644
index 0000000..1087668
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/GroupedAndNamedUniqueFileStore.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.internal.resource.local;
+
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.hash.HashUtil;
+
+import java.io.File;
+import java.util.Set;
+
+public class GroupedAndNamedUniqueFileStore<K> implements FileStore<K>, FileStoreSearcher<K> {
+
+    private PathKeyFileStore delegate;
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final Transformer<String, K> grouper;
+    private final Transformer<String, K> namer;
+
+
+    public GroupedAndNamedUniqueFileStore(PathKeyFileStore delegate, TemporaryFileProvider temporaryFileProvider, Transformer<String, K> grouper, Transformer<String, K> namer) {
+        this.delegate = delegate;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.grouper = grouper;
+        this.namer = namer;
+    }
+
+    public LocallyAvailableResource move(K key, File source) {
+        return delegate.move(toPath(key, getChecksum(source)), source);
+    }
+
+    public LocallyAvailableResource copy(K key, File source) {
+        return delegate.copy(toPath(key, getChecksum(source)), source);
+    }
+
+    public Set<? extends LocallyAvailableResource> search(K key) {
+        return delegate.search(toPath(key, "*"));
+    }
+
+    protected String toPath(K key, String checksumPart) {
+        String group = grouper.transform(key);
+        String name = namer.transform(key);
+
+        return String.format("%s/%s/%s", group, checksumPart, name);
+    }
+
+    private String getChecksum(File contentFile) {
+        return HashUtil.createHash(contentFile, "SHA1").asHexString();
+    }
+
+    public File getTempFile() {
+        return temporaryFileProvider.createTemporaryFile("filestore", "bin");
+    }
+
+    public void moveFilestore(File destination) {
+        delegate.moveFilestore(destination);
+    }
+
+    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();
+        addAction.execute(tempFile);
+        final String groupedAndNamedKey = toPath(key, getChecksum(tempFile));
+        return delegate.move(groupedAndNamedKey, tempFile);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathKeyFileStore.java
new file mode 100644
index 0000000..4fcf37f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathKeyFileStore.java
@@ -0,0 +1,198 @@
+/*
+ * 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.resource.local;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.DeleteAction;
+import org.gradle.api.file.EmptyFileVisitor;
+import org.gradle.api.file.FileVisitDetails;
+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.util.GFileUtils;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * File store that accepts the target path as the key for the entry.
+ *
+ * This implementation is explicitly NOT THREAD SAFE. Concurrent access must be organised externally.
+ * <p>
+ * There is always at most one entry for a given key for this file store. If an entry already exists at the given path, it will be overwritten.
+ * Paths can contain directory components, which will be created on demand.
+ * <p>
+ * This file store is self repairing in so far that any files partially written before a fatal error will be ignored and
+ * removed at a later time.
+ * <p>
+ * This file store also provides searching via relative ant path patterns.
+ */
+public class PathKeyFileStore implements FileStore<String>, FileStoreSearcher<String> {
+
+    /*
+        When writing a file into the filestore a marker file with this suffix is written alongside,
+        then removed after the write. This is used to detect partially written files (due to a serious crash)
+        and to silently clean them.
+     */
+    public static final String IN_PROGRESS_MARKER_FILE_SUFFIX = ".fslck";
+
+    private File baseDir;
+    private final DeleteAction deleteAction = new DeleteActionImpl(new IdentityFileResolver());
+
+    public PathKeyFileStore(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    protected File getBaseDir() {
+        return baseDir;
+    }
+
+    public LocallyAvailableResource move(String path, File source) {
+        return saveIntoFileStore(source, getFile(path), true);
+    }
+
+    public LocallyAvailableResource copy(String path, File source) {
+        return saveIntoFileStore(source, getFile(path), false);
+    }
+
+    private File getFile(String path) {
+        return new File(baseDir, path);
+    }
+
+    private File getFileWhileCleaningInProgress(String path) {
+        File file = getFile(path);
+        File markerFile = getInProgressMarkerFile(file);
+        if (markerFile.exists()) {
+            deleteAction.delete(file);
+            deleteAction.delete(markerFile);
+        }
+        return file;
+    }
+
+    public void moveFilestore(File destination) {
+        if (baseDir.exists()) {
+            GFileUtils.moveDirectory(baseDir, destination);
+        }
+        baseDir = destination;
+    }
+
+    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 LocallyAvailableResource saveIntoFileStore(final File source, final File destination, final boolean isMove) {
+        String verb = isMove ? "move" : "copy";
+
+        if (!source.exists()) {
+            throw new GradleException(String.format("Cannot %s '%s' into filestore @ '%s' as it does not exist", verb, source, destination));
+        }
+
+        String error = String.format("Failed to %s file '%s' into filestore at '%s' ", verb, source, destination);
+
+        return doAdd(destination, error, new Action<File>() {
+            public void execute(File file) {
+                if (isMove) {
+                    GFileUtils.moveFile(source, destination);
+                } else {
+                    GFileUtils.copyFile(source, destination);
+                }
+            }
+        });
+    }
+
+    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
+        try {
+            GFileUtils.parentMkdirs(destination);
+            File inProgressMarkerFile = getInProgressMarkerFile(destination);
+            GFileUtils.touch(inProgressMarkerFile);
+            try {
+                deleteAction.delete(destination);
+                action.execute(destination);
+            } catch (Throwable t) {
+                deleteAction.delete(destination);
+                throw t;
+            } finally {
+                deleteAction.delete(inProgressMarkerFile);
+            }
+        } catch (Throwable t) {
+            throw new GradleException(failureDescription, t);
+        }
+        return entryAt(destination);
+    }
+
+    public Set<? extends LocallyAvailableResource> search(String pattern) {
+        if (!getBaseDir().exists()) {
+            return Collections.emptySet();
+        }
+
+        final Set<LocallyAvailableResource> entries = new HashSet<LocallyAvailableResource>();
+        findFiles(pattern).visit(new EmptyFileVisitor() {
+            public void visitFile(FileVisitDetails fileDetails) {
+                final File file = fileDetails.getFile();
+                // We cannot clean in progress markers, or in progress files here because
+                // the file system visitor stuff can't handle the file system mutating while visiting
+                if (!isInProgressMarkerFile(file) && !isInProgressFile(file)) {
+                    entries.add(entryAt(file));
+                }
+            }
+        });
+
+        return entries;
+    }
+
+    private File getInProgressMarkerFile(File file) {
+        return new File(file.getParent(), file.getName() + IN_PROGRESS_MARKER_FILE_SUFFIX);
+    }
+
+    private boolean isInProgressMarkerFile(File file) {
+        return file.getName().endsWith(IN_PROGRESS_MARKER_FILE_SUFFIX);
+    }
+
+    private boolean isInProgressFile(File file) {
+        return getInProgressMarkerFile(file).exists();
+    }
+
+    private MinimalFileTree findFiles(String pattern) {
+        return new SingleIncludePatternFileTree(baseDir, pattern);
+    }
+
+    protected LocallyAvailableResource entryAt(File file) {
+        return entryAt(GFileUtils.relativePath(baseDir, file));
+    }
+
+    protected LocallyAvailableResource entryAt(final String path) {
+        return new AbstractLocallyAvailableResource() {
+            public File getFile() {
+                return new File(baseDir, path);
+            }
+        };
+    }
+
+    public LocallyAvailableResource get(String key) {
+        final File file = getFileWhileCleaningInProgress(key);
+        if (file.exists()) {
+            return entryAt(file);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStore.java
new file mode 100644
index 0000000..ecf5eb0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStore.java
@@ -0,0 +1,63 @@
+/*
+ * 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.resource.local;
+
+import org.gradle.api.Action;
+
+import java.io.File;
+import java.util.Set;
+
+public class PathNormalisingKeyFileStore implements FileStore<String>, FileStoreSearcher<String> {
+
+    private final PathKeyFileStore delegate;
+
+    public PathNormalisingKeyFileStore(File baseDir) {
+        this(new PathKeyFileStore(baseDir));
+    }
+
+    public PathNormalisingKeyFileStore(PathKeyFileStore delegate) {
+        this.delegate = delegate;
+    }
+
+    public LocallyAvailableResource move(String key, File source) {
+        return delegate.move(normalizePath(key), source);
+    }
+
+    public LocallyAvailableResource copy(String key, File source) {
+        return delegate.copy(key, source);
+    }
+
+    protected String normalizePath(String path) {
+        return path.replaceAll("[^\\d\\w\\./]", "_");
+    }
+
+    protected String normalizeSearchPath(String path) {
+        return path.replaceAll("[^\\d\\w\\.\\*/]", "_");
+    }
+
+    public void moveFilestore(File destination) {
+        delegate.moveFilestore(destination);
+    }
+
+    public LocallyAvailableResource add(String key, Action<File> addAction) {
+        return delegate.add(normalizePath(key), addAction);
+    }
+
+    public Set<? extends LocallyAvailableResource> search(String key) {
+        return delegate.search(normalizeSearchPath(key));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStore.java b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStore.java
new file mode 100644
index 0000000..8a7d151
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStore.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.internal.resource.local;
+
+import org.apache.commons.io.FileUtils;
+import org.gradle.api.Action;
+
+import java.io.File;
+
+/**
+ * Assumes that files do not need to be replaced in the filestore.
+ *
+ * Can be used as an optimisation if path contains a checksum of the file, as there is no point to perform the replace in that circumstance.
+ */
+public class UniquePathKeyFileStore extends PathKeyFileStore {
+
+    public UniquePathKeyFileStore(File baseDir) {
+        super(baseDir);
+    }
+
+    @Override
+    public LocallyAvailableResource move(String path, File source) {
+        LocallyAvailableResource entry = super.move(path, source);
+        if (source.exists()) {
+            FileUtils.deleteQuietly(source);
+        }
+        return entry;
+    }
+
+    @Override
+    protected LocallyAvailableResource doAdd(File destination, String failureDescription, Action<File> action) {
+        if (destination.exists()) {
+            return entryAt(destination);
+        }
+        return super.doAdd(destination, failureDescription, action);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
index 90a826e..d63a605 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/service/scopes/BuildScopeServiceRegistryFactory.java
@@ -21,7 +21,9 @@ import org.gradle.api.internal.SettingsInternal;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.service.ServiceRegistry;
 
-class BuildScopeServiceRegistryFactory implements ServiceRegistryFactory {
+import java.io.Closeable;
+
+class BuildScopeServiceRegistryFactory implements ServiceRegistryFactory, Closeable {
     private final ServiceRegistry services;
     private final CompositeStoppable registries = new CompositeStoppable();
 
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
index 494934f..31a95b9 100644
--- 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
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.service.scopes;
 
-import com.google.common.cache.CacheBuilder;
 import org.gradle.StartParameter;
 import org.gradle.api.Action;
 import org.gradle.api.Project;
@@ -27,10 +26,15 @@ import org.gradle.api.internal.artifacts.ModuleInternal;
 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.component.ComponentTypeRegistry;
+import org.gradle.api.internal.component.DefaultComponentTypeRegistry;
 import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.initialization.*;
+import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
+import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
 import org.gradle.api.internal.plugins.DefaultPluginRegistry;
+import org.gradle.api.internal.plugins.PluginInspector;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.api.internal.project.*;
 import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory;
@@ -44,6 +48,8 @@ import org.gradle.cache.internal.DefaultCacheRepository;
 import org.gradle.cache.internal.DefaultCacheScopeMapping;
 import org.gradle.configuration.*;
 import org.gradle.configuration.project.*;
+import org.gradle.execution.ProjectConfigurer;
+import org.gradle.execution.TaskPathProjectEvaluator;
 import org.gradle.groovy.scripts.DefaultScriptCompilerFactory;
 import org.gradle.groovy.scripts.ScriptCompilerFactory;
 import org.gradle.groovy.scripts.ScriptExecutionListener;
@@ -54,18 +60,24 @@ 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.event.ListenerManager;
 import org.gradle.internal.id.LongIdGenerator;
+import org.gradle.internal.operations.logging.BuildOperationLoggerFactory;
+import org.gradle.internal.operations.logging.DefaultBuildOperationLoggerFactory;
+import org.gradle.internal.reflect.DirectInstantiator;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ListenerManager;
+import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.logging.ShowStacktrace;
 import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.messaging.actor.internal.DefaultActorFactory;
 import org.gradle.messaging.remote.MessagingServer;
-import org.gradle.plugin.internal.PluginResolverFactory;
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector;
+import org.gradle.plugin.use.internal.PluginRequestApplicator;
 import org.gradle.process.internal.DefaultWorkerProcessFactory;
 import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.process.internal.child.WorkerProcessClassPathProvider;
@@ -77,6 +89,7 @@ import org.gradle.util.GradleVersion;
  * Contains the singleton services for a single build invocation.
  */
 public class BuildScopeServices extends DefaultServiceRegistry {
+
     public BuildScopeServices(final ServiceRegistry parent, final StartParameter startParameter) {
         super(parent);
         register(new Action<ServiceRegistration>() {
@@ -89,10 +102,6 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         });
     }
 
-    protected ImportsReader createImportsReader() {
-        return new ImportsReader();
-    }
-
     protected TimeProvider createTimeProvider() {
         return new TrueTimeProvider();
     }
@@ -114,7 +123,8 @@ public class BuildScopeServices extends DefaultServiceRegistry {
                 new DefaultClassPathProvider(get(ModuleRegistry.class)),
                 new DependencyClassPathProvider(get(ModuleRegistry.class),
                         get(PluginModuleRegistry.class)),
-                get(WorkerProcessClassPathProvider.class));
+                get(WorkerProcessClassPathProvider.class)
+        );
     }
 
     protected WorkerProcessClassPathProvider createWorkerProcessClassPathProvider(CacheRepository cacheRepository, ModuleRegistry moduleRegistry) {
@@ -145,7 +155,6 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         DefaultCacheScopeMapping scopeMapping = new DefaultCacheScopeMapping(startParameter.getGradleUserHomeDir(), startParameter.getProjectCacheDir(), GradleVersion.current());
         return new DefaultCacheRepository(
                 scopeMapping,
-                startParameter.getCacheUsage(),
                 factory);
     }
 
@@ -153,9 +162,7 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         ConfigureActionsProjectEvaluator withActionsEvaluator = new ConfigureActionsProjectEvaluator(
                 new PluginsProjectConfigureActions(get(ClassLoaderRegistry.class).getPluginsClassLoader()),
                 new BuildScriptProcessor(get(ScriptPluginFactory.class)),
-                new DelayedConfigurationActions(),
-                new TaskModelRealizingConfigurationAction(),
-                new ProjectDependencies2TaskResolver()
+                new DelayedConfigurationActions()
         );
         return new LifecycleProjectEvaluator(withActionsEvaluator);
     }
@@ -164,25 +171,35 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         return new DependencyAutoWireTaskFactory(
                 new AnnotationProcessingTaskFactory(
                         new TaskFactory(
-                                get(ClassGenerator.class))));
+                                get(ClassGenerator.class))
+                )
+        );
     }
 
-    protected ScriptCompilerFactory createScriptCompileFactory(ListenerManager listenerManager, EmptyScriptGenerator emptyScriptGenerator, FileCacheBackedScriptClassCompiler scriptCompiler) {
+    protected ScriptCompilerFactory createScriptCompileFactory(ListenerManager listenerManager, EmptyScriptGenerator emptyScriptGenerator, FileCacheBackedScriptClassCompiler scriptCompiler, ClassLoaderCache classLoaderCache) {
         ScriptExecutionListener scriptExecutionListener = listenerManager.getBroadcaster(ScriptExecutionListener.class);
         return new DefaultScriptCompilerFactory(
                 new CachingScriptClassCompiler(
                         new ShortCircuitEmptyScriptCompiler(
                                 scriptCompiler,
-                                emptyScriptGenerator)),
+                                emptyScriptGenerator,
+                                classLoaderCache
+                        )
+                ),
                 new DefaultScriptRunnerFactory(
-                        scriptExecutionListener));
+                        scriptExecutionListener,
+                        DirectInstantiator.INSTANCE
+                )
+        );
     }
 
     protected EmptyScriptGenerator createEmptyScriptGenerator() {
         return new AsmBackedEmptyScriptGenerator();
     }
 
-    protected FileCacheBackedScriptClassCompiler createFileCacheBackedScriptClassCompiler(CacheRepository cacheRepository, EmptyScriptGenerator emptyScriptGenerator, final StartParameter startParameter, ProgressLoggerFactory progressLoggerFactory) {
+    protected FileCacheBackedScriptClassCompiler createFileCacheBackedScriptClassCompiler(
+            CacheRepository cacheRepository, EmptyScriptGenerator emptyScriptGenerator, final StartParameter startParameter,
+            ProgressLoggerFactory progressLoggerFactory, ClassLoaderCache classLoaderCache, ImportsReader importsReader) {
         CacheValidator scriptCacheInvalidator = new CacheValidator() {
             public boolean isValid() {
                 return !startParameter.isRecompileScripts();
@@ -191,20 +208,21 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         return new FileCacheBackedScriptClassCompiler(
                 cacheRepository,
                 scriptCacheInvalidator,
-                new DefaultScriptCompilationHandler(
-                        emptyScriptGenerator),
-                progressLoggerFactory);
+                new DefaultScriptCompilationHandler(emptyScriptGenerator, classLoaderCache, importsReader),
+                progressLoggerFactory
+        );
     }
 
     protected ScriptPluginFactory createScriptObjectConfigurerFactory() {
         return new DefaultScriptPluginFactory(
                 get(ScriptCompilerFactory.class),
-                get(ImportsReader.class),
                 getFactory(LoggingManagerInternal.class),
                 get(Instantiator.class),
                 get(ScriptHandlerFactory.class),
-                get(PluginResolverFactory.class),
-                get(FileLookup.class)
+                get(PluginRequestApplicator.class),
+                get(FileLookup.class),
+                get(DocumentationRegistry.class),
+                get(ModelRuleSourceDetector.class)
         );
     }
 
@@ -226,30 +244,25 @@ public class BuildScopeServices extends DefaultServiceRegistry {
                                 get(Instantiator.class),
                                 get(ServiceRegistryFactory.class)
                         ),
-                        get(IGradlePropertiesLoader.class)),
-                get(IGradlePropertiesLoader.class));
+                        get(IGradlePropertiesLoader.class)
+                ),
+                get(IGradlePropertiesLoader.class)
+        );
     }
 
-    protected ExceptionAnalyser createExceptionAnalyser() {
-        return new MultipleBuildFailuresExceptionAnalyser(new DefaultExceptionAnalyser(get(ListenerManager.class)));
+    protected ExceptionAnalyser createExceptionAnalyser(ListenerManager listenerManager, LoggingConfiguration loggingConfiguration) {
+        ExceptionAnalyser exceptionAnalyser = new MultipleBuildFailuresExceptionAnalyser(new DefaultExceptionAnalyser(listenerManager));
+        if (loggingConfiguration.getShowStacktrace() != ShowStacktrace.ALWAYS_FULL) {
+            exceptionAnalyser = new StackTraceSanitizingExceptionAnalyser(exceptionAnalyser);
+        }
+        return exceptionAnalyser;
     }
 
     protected ScriptHandlerFactory createScriptHandlerFactory() {
         return new DefaultScriptHandlerFactory(
                 get(DependencyManagementServices.class),
                 get(FileResolver.class),
-                new DependencyMetaDataProviderImpl());
-    }
-
-    protected PluginResolverFactory createPluginResolverFactory() {
-        return new PluginResolverFactory(
-                get(PluginRegistry.class),
-                get(Instantiator.class),
-                get(DependencyManagementServices.class),
-                get(FileResolver.class),
-                new DependencyMetaDataProviderImpl(),
-                get(DocumentationRegistry.class),
-                get(CacheRepository.class)
+                get(DependencyMetaDataProvider.class)
         );
     }
 
@@ -260,11 +273,16 @@ public class BuildScopeServices extends DefaultServiceRegistry {
                 messagingServer,
                 classPathRegistry,
                 fileResolver,
-                new LongIdGenerator());
+                new LongIdGenerator(),
+                startParameter.getGradleUserHomeDir());
     }
 
-    protected BuildConfigurer createBuildConfigurer() {
-        return new DefaultBuildConfigurer();
+    protected ProjectConfigurer createProjectConfigurer(BuildCancellationToken cancellationToken) {
+        return new TaskPathProjectEvaluator(cancellationToken);
+    }
+
+    protected BuildConfigurer createBuildConfigurer(ProjectConfigurer projectConfigurer) {
+        return new DefaultBuildConfigurer(projectConfigurer);
     }
 
     protected ProjectAccessListener createProjectAccessListener() {
@@ -275,20 +293,32 @@ public class BuildScopeServices extends DefaultServiceRegistry {
         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 PluginRegistry createPluginRegistry(ClassLoaderScopeRegistry scopeRegistry, PluginInspector pluginInspector) {
+        return new DefaultPluginRegistry(pluginInspector, scopeRegistry.getCoreAndPluginsScope());
     }
 
     protected ServiceRegistryFactory createServiceRegistryFactory(final ServiceRegistry services) {
         return new BuildScopeServiceRegistryFactory(services);
     }
 
-    protected ClassLoaderCache createClassLoaderCache() {
-        return new DefaultClassLoaderCache(CacheBuilder.newBuilder().<DefaultClassLoaderCache.Key, ClassLoader>build());
+    protected ClassLoaderScopeRegistry createClassLoaderScopeRegistry(ClassLoaderRegistry classLoaderRegistry, ClassLoaderCache classLoaderCache) {
+        return new DefaultClassLoaderScopeRegistry(classLoaderRegistry, classLoaderCache);
+    }
+
+    protected ProjectTaskLister createProjectTaskLister() {
+        return new DefaultProjectTaskLister();
+    }
+
+    protected DependencyMetaDataProvider createDependencyMetaDataProvider() {
+        return new DependencyMetaDataProviderImpl();
     }
 
-    protected ClassLoaderScope createClassLoaderScope(ClassLoaderRegistry classLoaderRegistry, ClassLoaderCache classLoaderCache) {
-        return new RootClassLoaderScope(classLoaderRegistry.getGradleApiClassLoader(), classLoaderCache);
+    protected ComponentTypeRegistry createComponentTypeRegistry() {
+        return new DefaultComponentTypeRegistry();
+    }
+
+    protected PluginInspector createPluginInspector(ModelRuleSourceDetector modelRuleSourceDetector) {
+        return new PluginInspector(modelRuleSourceDetector);
     }
 
     private class DependencyMetaDataProviderImpl implements DependencyMetaDataProvider {
@@ -296,4 +326,8 @@ public class BuildScopeServices extends DefaultServiceRegistry {
             return new DefaultModule("unspecified", "unspecified", Project.DEFAULT_VERSION, Project.DEFAULT_STATUS);
         }
     }
+
+    protected BuildOperationLoggerFactory createBuildOperationLoggerFactory() {
+        return new DefaultBuildOperationLoggerFactory();
+    }
 }
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
index 4f6eb0f..c11844f 100755
--- 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
@@ -16,44 +16,64 @@
 
 package org.gradle.internal.service.scopes;
 
+import com.google.common.collect.Iterables;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.*;
+import org.gradle.api.internal.changedetection.state.CachingFileSnapshotter;
 import org.gradle.api.internal.changedetection.state.InMemoryTaskArtifactCache;
 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.*;
+import org.gradle.api.internal.hash.DefaultHasher;
+import org.gradle.api.internal.initialization.loadercache.*;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
 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.configuration.DefaultImportsReader;
+import org.gradle.configuration.ImportsReader;
+import org.gradle.initialization.ClassLoaderRegistry;
+import org.gradle.initialization.DefaultClassLoaderRegistry;
+import org.gradle.initialization.DefaultCommandLineConverter;
+import org.gradle.initialization.DefaultGradleLauncherFactory;
 import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.classloader.DefaultClassLoaderFactory;
 import org.gradle.internal.concurrent.DefaultExecutorFactory;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.environment.GradleBuildEnvironment;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.event.DefaultListenerManager;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.DirectInstantiator;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceLocator;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
 import org.gradle.messaging.remote.MessagingServer;
 import org.gradle.messaging.remote.internal.MessagingServices;
 import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
+import org.gradle.model.internal.core.ModelCreatorFactory;
+import org.gradle.model.internal.inspect.*;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore;
+import org.gradle.model.internal.persist.AlwaysNewModelRegistryStore;
+import org.gradle.model.internal.persist.ModelRegistryStore;
+import org.gradle.model.internal.persist.ReusingModelRegistryStore;
 
 import java.util.List;
 
 /**
- * Defines the services shared by all builds in a given process.
+ * Defines the global services shared by all services in a given process. This includes the Gradle CLI, daemon and tooling API provider.
  */
 public class GlobalScopeServices {
 
+    private static final Logger LOGGER = Logging.getLogger(GlobalScopeServices.class);
+
     private GradleBuildEnvironment environment;
 
     public GlobalScopeServices(final boolean longLiving) {
@@ -65,14 +85,14 @@ public class GlobalScopeServices {
     }
 
     void configure(ServiceRegistration registration, ClassLoaderRegistry classLoaderRegistry) {
-        final List<PluginServiceRegistry> pluginServiceFactories = new ServiceLocator(classLoaderRegistry.getRuntimeClassLoader(), classLoaderRegistry.getCoreImplClassLoader(), classLoaderRegistry.getPluginsClassLoader()).getAll(PluginServiceRegistry.class);
+        final List<PluginServiceRegistry> pluginServiceFactories = new ServiceLocator(classLoaderRegistry.getRuntimeClassLoader(), classLoaderRegistry.getPluginsClassLoader()).getAll(PluginServiceRegistry.class);
         for (PluginServiceRegistry pluginServiceRegistry : pluginServiceFactories) {
             registration.add(PluginServiceRegistry.class, pluginServiceRegistry);
             pluginServiceRegistry.registerGlobalServices(registration);
         }
     }
 
-    GradleLauncherFactory createGradleLauncherFactory(ServiceRegistry services) {
+    DefaultGradleLauncherFactory createGradleLauncherFactory(ServiceRegistry services) {
         return new DefaultGradleLauncherFactory(services);
     }
 
@@ -118,13 +138,13 @@ public class GlobalScopeServices {
     ListenerManager createListenerManager() {
         return new DefaultListenerManager();
     }
-   
+
     ClassLoaderFactory createClassLoaderFactory() {
         return new DefaultClassLoaderFactory();
     }
 
     MessagingServices createMessagingServices(ClassLoaderRegistry classLoaderRegistry) {
-        return new MessagingServices(classLoaderRegistry.getPluginsClassLoader());
+        return new MessagingServices(getClass().getClassLoader());
     }
 
     MessagingServer createMessagingServer(MessagingServices messagingServices) {
@@ -136,7 +156,7 @@ public class GlobalScopeServices {
     }
 
     Instantiator createInstantiator(ClassGenerator classGenerator) {
-        return new ClassGeneratorBackedInstantiator(classGenerator, new DirectInstantiator());
+        return new ClassGeneratorBackedInstantiator(classGenerator, DirectInstantiator.INSTANCE);
     }
 
     ExecutorFactory createExecutorFactory() {
@@ -169,4 +189,49 @@ public class GlobalScopeServices {
         return new DefaultFileLookup(fileSystem);
     }
 
+    ModelRuleExtractor createModelRuleInspector(ServiceRegistry services, ModelSchemaStore modelSchemaStore, ModelCreatorFactory modelCreatorFactory) {
+        List<MethodModelRuleExtractor> extractors = services.getAll(MethodModelRuleExtractor.class);
+        List<MethodModelRuleExtractor> coreExtractors = MethodModelRuleExtractors.coreExtractors(modelSchemaStore, modelCreatorFactory);
+        return new ModelRuleExtractor(Iterables.concat(coreExtractors, extractors));
+    }
+
+    ClassPathSnapshotter createClassPathSnapshotter(GradleBuildEnvironment environment) {
+        if (environment.isLongLivingProcess()) {
+            CachingFileSnapshotter fileSnapshotter = new CachingFileSnapshotter(new DefaultHasher(), new NonThreadsafeInMemoryStore());
+            return new HashClassPathSnapshotter(fileSnapshotter);
+        } else {
+            return new FileClassPathSnapshotter();
+        }
+    }
+
+    ClassLoaderCache createClassLoaderCache(ClassPathSnapshotter classPathSnapshotter) {
+        return new DefaultClassLoaderCache(classPathSnapshotter);
+    }
+
+    private DefaultModelCreatorFactory createModelCreatorFactory(ModelSchemaStore modelSchemaStore) {
+        return new DefaultModelCreatorFactory(modelSchemaStore);
+    }
+
+    protected ModelSchemaStore createModelSchemaStore() {
+        return DefaultModelSchemaStore.getInstance();
+    }
+
+    protected ModelRuleSourceDetector createModelRuleSourceDetector() {
+        return new ModelRuleSourceDetector();
+    }
+
+    protected ModelRegistryStore createModelRegistryStore(GradleBuildEnvironment buildEnvironment, ModelRuleExtractor ruleExtractor) {
+        if (buildEnvironment.isLongLivingProcess() && Boolean.getBoolean(ReusingModelRegistryStore.TOGGLE)) {
+            LOGGER.warn(ReusingModelRegistryStore.BANNER);
+            return new ReusingModelRegistryStore(ruleExtractor);
+        } else {
+            return new AlwaysNewModelRegistryStore(ruleExtractor);
+        }
+    }
+
+    protected ImportsReader createImportsReader() {
+        return new DefaultImportsReader();
+    }
+
+
 }
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
index 7f30327..6b44446 100644
--- 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
@@ -15,24 +15,26 @@
  */
 package org.gradle.internal.service.scopes;
 
-import org.gradle.StartParameter;
+import org.gradle.api.Action;
 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.plugins.*;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.options.OptionReader;
-import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.invocation.Gradle;
 import org.gradle.execution.*;
 import org.gradle.execution.commandline.CommandLineTaskConfigurer;
 import org.gradle.execution.commandline.CommandLineTaskParser;
 import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter;
 import org.gradle.execution.taskgraph.TaskPlanExecutor;
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ListenerManager;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -46,32 +48,36 @@ public class GradleScopeServices extends DefaultServiceRegistry {
 
     private final CompositeStoppable registries = new CompositeStoppable();
 
-    public GradleScopeServices(ServiceRegistry parent, GradleInternal gradle) {
+    public GradleScopeServices(final ServiceRegistry parent, GradleInternal gradle) {
         super(parent);
         add(GradleInternal.class, gradle);
         addProvider(new TaskExecutionServices());
+        register(new Action<ServiceRegistration>() {
+            public void execute(ServiceRegistration registration) {
+                for (PluginServiceRegistry pluginServiceRegistry : parent.getAll(PluginServiceRegistry.class)) {
+                    pluginServiceRegistry.registerGradleServices(registration);
+                }
+            }
+        });
     }
 
-    TaskSelector createTaskSelector(GradleInternal gradle) {
-        return new TaskSelector(gradle);
+    TaskSelector createTaskSelector(GradleInternal gradle, ProjectConfigurer projectConfigurer) {
+        return new TaskSelector(gradle, projectConfigurer);
     }
 
     OptionReader createOptionReader() {
         return new OptionReader();
     }
 
-    CommandLineTaskParser createCommandLineTaskParser(OptionReader optionReader) {
-        return new CommandLineTaskParser(new CommandLineTaskConfigurer(optionReader));
+    CommandLineTaskParser createCommandLineTaskParser(OptionReader optionReader, TaskSelector taskSelector) {
+        return new CommandLineTaskParser(new CommandLineTaskConfigurer(optionReader), taskSelector);
     }
 
-    BuildExecuter createBuildExecuter(CommandLineTaskParser commandLineTaskParser, TaskSelector taskSelector) {
+    BuildExecuter createBuildExecuter(CommandLineTaskParser commandLineTaskParser, TaskSelector taskSelector, ProjectConfigurer projectConfigurer) {
         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(commandLineTaskParser, taskSelector));
+        configs.add(new DefaultTasksBuildExecutionAction(projectConfigurer));
+        configs.add(new ExcludedTaskFilteringBuildConfigurationAction(taskSelector));
+        configs.add(new TaskNameResolvingBuildConfigurationAction(commandLineTaskParser));
 
         return new DefaultBuildExecuter(
                 configs,
@@ -87,8 +93,8 @@ public class GradleScopeServices extends DefaultServiceRegistry {
         };
     }
 
-    TaskGraphExecuter createTaskGraphExecuter(ListenerManager listenerManager, TaskPlanExecutor taskPlanExecutor) {
-        return new DefaultTaskGraphExecuter(listenerManager, taskPlanExecutor);
+    TaskGraphExecuter createTaskGraphExecuter(ListenerManager listenerManager, TaskPlanExecutor taskPlanExecutor, BuildCancellationToken cancellationToken) {
+        return new DefaultTaskGraphExecuter(listenerManager, taskPlanExecutor, cancellationToken);
     }
 
     ServiceRegistryFactory createServiceRegistryFactory(final ServiceRegistry services) {
@@ -105,11 +111,12 @@ public class GradleScopeServices extends DefaultServiceRegistry {
     }
 
     PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
-        return parentRegistry.createChild(get(GradleInternal.class).getClassLoaderScope(), new DependencyInjectingInstantiator(this));
+        return parentRegistry.createChild(get(GradleInternal.class).getClassLoaderScope());
     }
 
-    PluginContainer createPluginContainer(GradleInternal gradle, PluginRegistry pluginRegistry) {
-        return new DefaultPluginContainer<GradleInternal>(pluginRegistry, gradle);
+    PluginManagerInternal createPluginManager(Instantiator instantiator, GradleInternal gradleInternal, PluginRegistry pluginRegistry) {
+        PluginApplicator applicator = new ImperativeOnlyPluginApplicator<Gradle>(gradleInternal);
+        return instantiator.newInstance(DefaultPluginManager.class, pluginRegistry, new DependencyInjectingInstantiator(this), applicator);
     }
 
     @Override
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
index caf5a3d..2804f71 100644
--- 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
@@ -36,6 +36,12 @@ public interface PluginServiceRegistry {
     void registerBuildServices(ServiceRegistration registration);
 
     /**
+     * Called once per build, to registry any gradle scoped services. These services are discarded at the end of the current build.
+     * All build scoped services are visible to the gradle scope services, but not vice versa.
+     */
+    void registerGradleServices(ServiceRegistration registration);
+
+    /**
      * Called once per project per build, to registry any project scoped services. These services are discarded at the end of the current build.
      * All global and build scoped services are visible to the project scope services, but not vice versa.
      */
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
index c9ac691..4474b37 100644
--- 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
@@ -26,33 +26,35 @@ import org.gradle.api.internal.artifacts.ModuleInternal;
 import org.gradle.api.internal.artifacts.ProjectBackedModule;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.component.ComponentRegistry;
 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.ScriptHandlerFactory;
-import org.gradle.api.internal.plugins.DefaultPluginContainer;
-import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.plugins.*;
 import org.gradle.api.internal.project.DefaultAntBuilderFactory;
+import org.gradle.api.internal.project.DeferredProjectConfiguration;
 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.nativeintegration.filesystem.FileSystem;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.model.ModelRules;
-import org.gradle.model.internal.DefaultModelRegistry;
-import org.gradle.model.internal.ModelRegistry;
-import org.gradle.model.internal.ModelRegistryBackedModelRules;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.persist.ModelRegistryStore;
+import org.gradle.process.internal.DefaultExecActionFactory;
+import org.gradle.process.internal.ExecActionFactory;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
 import org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry;
 
@@ -79,7 +81,11 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
     }
 
     protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
-        return parentRegistry.createChild(project.getClassLoaderScope().createChild().lock(), new DependencyInjectingInstantiator(this));
+        return parentRegistry.createChild(project.getClassLoaderScope().createChild("plugins").lock());
+    }
+
+    protected DeferredProjectConfiguration createDeferredProjectConfiguration() {
+        return new DeferredProjectConfiguration(project);
     }
 
     protected FileResolver createFileResolver() {
@@ -98,6 +104,10 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
         return new DefaultFileOperations(get(FileResolver.class), project.getTasks(), get(TemporaryFileProvider.class), get(Instantiator.class), get(FileLookup.class));
     }
 
+    protected ExecActionFactory createExecActionFactory() {
+        return new DefaultExecActionFactory(get(FileResolver.class));
+    }
+
     protected TemporaryFileProvider createTemporaryFileProvider() {
         return new DefaultTemporaryFileProvider(new Factory<File>() {
             public File create() {
@@ -114,8 +124,9 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
         return new DefaultToolingModelBuilderRegistry();
     }
 
-    protected PluginContainer createPluginContainer() {
-        return new DefaultPluginContainer(get(PluginRegistry.class), project);
+    protected PluginManagerInternal createPluginManager(Instantiator instantiator) {
+        PluginApplicator applicator = new RuleBasedPluginApplicator<ProjectInternal>(project, get(ModelRuleExtractor.class), get(ModelRuleSourceDetector.class));
+        return instantiator.newInstance(DefaultPluginManager.class, get(PluginRegistry.class), new DependencyInjectingInstantiator(this), applicator);
     }
 
     protected ITaskFactory createTaskFactory(ITaskFactory parentFactory) {
@@ -123,7 +134,7 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
     }
 
     protected Factory<TaskContainerInternal> createTaskContainerInternal() {
-        return new DefaultTaskContainerFactory(get(Instantiator.class), get(ITaskFactory.class), project, get(ProjectAccessListener.class));
+        return new DefaultTaskContainerFactory(get(ModelRegistry.class), get(Instantiator.class), get(ITaskFactory.class), project, get(ProjectAccessListener.class));
     }
 
     protected SoftwareComponentContainer createSoftwareComponentContainer() {
@@ -139,12 +150,8 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
         };
     }
 
-    protected ModelRegistry createModelRegistry() {
-        return new DefaultModelRegistry();
-    }
-
-    protected ModelRules createModelRules() {
-        return get(Instantiator.class).newInstance(ModelRegistryBackedModelRules.class, get(ModelRegistry.class));
+    protected ModelRegistry createModelRegistry(ModelRegistryStore modelRegistryStore) {
+        return modelRegistryStore.get(project);
     }
 
     protected ScriptHandler createScriptHandler() {
@@ -173,4 +180,9 @@ public class ProjectScopeServices extends DefaultServiceRegistry {
             }
         };
     }
+
+    protected ComponentRegistry createComponentRegistry() {
+        return new ComponentRegistry();
+    }
+
 }
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
index 67e8019..4f96cec 100644
--- 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
@@ -20,12 +20,11 @@ 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.api.internal.plugins.*;
 import org.gradle.initialization.DefaultProjectDescriptorRegistry;
 import org.gradle.initialization.ProjectDescriptorRegistry;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistry;
 
@@ -42,11 +41,12 @@ public class SettingsScopeServices extends DefaultServiceRegistry {
     }
 
     protected PluginRegistry createPluginRegistry(PluginRegistry parentRegistry) {
-        return parentRegistry.createChild(settings.getClassLoaderScope(), new DependencyInjectingInstantiator(this));
+        return parentRegistry.createChild(settings.getClassLoaderScope());
     }
 
-    protected PluginContainer createPluginContainer() {
-        return new DefaultPluginContainer(get(PluginRegistry.class), settings);
+    protected PluginManagerInternal createPluginManager(Instantiator instantiator, PluginRegistry pluginRegistry) {
+        PluginApplicator applicator = new ImperativeOnlyPluginApplicator<SettingsInternal>(settings);
+        return instantiator.newInstance(DefaultPluginManager.class, pluginRegistry, new DependencyInjectingInstantiator(this), applicator);
     }
 
     protected ProjectDescriptorRegistry createProjectDescriptorRegistry() {
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
index 67cb586..d6911a6 100644
--- 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
@@ -32,10 +32,12 @@ import org.gradle.execution.taskgraph.TaskPlanExecutorFactory;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.environment.GradleBuildEnvironment;
 import org.gradle.internal.id.RandomLongIdGenerator;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.DefaultBuildOperationProcessor;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.listener.ListenerManager;
-import org.gradle.messaging.serialize.DefaultSerializerRegistry;
-import org.gradle.messaging.serialize.SerializerRegistry;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.internal.serialize.DefaultSerializerRegistry;
+import org.gradle.internal.serialize.SerializerRegistry;
 
 public class TaskExecutionServices {
     TaskExecuter createTaskExecuter(TaskArtifactStateRepository repository, ListenerManager listenerManager) {
@@ -94,4 +96,8 @@ public class TaskExecutionServices {
     TaskPlanExecutor createTaskExecutorFactory(StartParameter startParameter, ExecutorFactory executorFactory) {
         return new TaskPlanExecutorFactory(startParameter.getParallelThreadCount(), executorFactory).create();
     }
+
+    BuildOperationProcessor createBuildOperationProcessor(StartParameter startParameter, ExecutorFactory executorFactory) {
+        return new DefaultBuildOperationProcessor(executorFactory, startParameter.getMaxWorkerCount());
+    }
 }
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
index 6a862e3..4efad8e 100644
--- 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
@@ -21,7 +21,7 @@ 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.internal.tasks.TaskMutator;
 import org.gradle.api.tasks.TaskInputs;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistry;
@@ -41,15 +41,15 @@ public class TaskScopeServices extends DefaultServiceRegistry {
     }
 
     protected TaskInputs createTaskInputs() {
-        return new DefaultTaskInputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
+        return new DefaultTaskInputs(project.getFileResolver(), taskInternal, get(TaskMutator.class));
     }
 
     protected TaskOutputsInternal createTaskOutputs() {
-        return new DefaultTaskOutputs(project.getFileResolver(), taskInternal, get(TaskStatusNagger.class));
+        return new DefaultTaskOutputs(project.getFileResolver(), taskInternal, get(TaskMutator.class));
     }
 
-    protected TaskStatusNagger createTaskStatusNagger() {
-        return new TaskStatusNagger(taskInternal);
+    protected TaskMutator createTaskStatusNagger() {
+        return new TaskMutator(taskInternal);
     }
 
     protected LoggingManagerInternal createLoggingManager() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverter.java
new file mode 100644
index 0000000..851191d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+class CharSequenceNotationConverter<N, T> implements NotationConverter<N, T> {
+    private final NotationConverter<String, ? extends T> delegate;
+
+    public CharSequenceNotationConverter(NotationConverter<String, ? extends T> delegate) {
+        this.delegate = delegate;
+    }
+
+    public void convert(N notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        if (notation instanceof CharSequence) {
+            CharSequence charSequence = (CharSequence) notation;
+            delegate.convert(charSequence.toString(), result);
+        }
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        delegate.describe(visitor);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParser.java
index eeb8089..50b6ff3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParser.java
@@ -16,13 +16,15 @@
 
 package org.gradle.internal.typeconversion;
 
-public class CharSequenceNotationParser extends TypedNotationParser<CharSequence, String> {
-    public CharSequenceNotationParser() {
-        super(CharSequence.class);
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+class CharSequenceNotationParser implements NotationConverter<String, String> {
+    public void convert(String notation, NotationConvertResult<? super String> result) throws TypeConversionException {
+        result.converted(notation);
     }
 
     @Override
-    protected String parseType(CharSequence notation) {
-        return notation.toString();
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("String or CharSequence instances.");
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverter.java
new file mode 100644
index 0000000..fd5b4a5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverter.java
@@ -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.internal.typeconversion;
+
+import groovy.lang.Closure;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+public class ClosureToSpecNotationConverter<T> implements NotationConverter<Closure, Spec<T>> {
+    private final Class<T> type;
+
+    public ClosureToSpecNotationConverter(Class<T> type) {
+        this.type = type;
+    }
+
+    public void convert(Closure notation, NotationConvertResult<? super Spec<T>> result) throws TypeConversionException {
+        Spec<T> spec = Specs.convertClosureToSpec(notation);
+        result.converted(spec);
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate(String.format("Closure that returns boolean and takes a single %s as a parameter.", type.getSimpleName()));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParser.java
deleted file mode 100644
index 9d6f5e9..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParser.java
+++ /dev/null
@@ -1,36 +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.typeconversion;
-
-import groovy.lang.Closure;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.specs.Specs;
-
-import java.util.Collection;
-
-public class ClosureToSpecNotationParser<T> implements NotationParser<Object, Spec<T>> {
-    public Spec<T> parseNotation(Object notation) throws UnsupportedNotationException {
-        if (notation instanceof Closure) {
-            return Specs.convertClosureToSpec((Closure) notation);
-        }
-        throw new UnsupportedNotationException(notation);
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Closure that returns boolean. See the DSL reference for information what parameters are passed into the closure.");
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationConverter.java
new file mode 100644
index 0000000..220e0bd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationConverter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+import java.util.List;
+
+public class CompositeNotationConverter<N, T> implements NotationConverter<N, T> {
+    private final List<NotationConverter<N, ? extends T>> converters;
+
+    public CompositeNotationConverter(List<NotationConverter<N, ? extends T>> converters) {
+        this.converters = converters;
+    }
+
+    public void convert(N notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        for (int i = 0; !result.hasResult() && i < converters.size(); i++) {
+            NotationConverter<N, ? extends T> converter = converters.get(i);
+            converter.convert(notation, result);
+        }
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        for (NotationConverter<N, ? extends T> converter : converters) {
+            converter.describe(visitor);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationParser.java
index 6095e26..7c8951d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/CompositeNotationParser.java
@@ -16,6 +16,8 @@
 
 package org.gradle.internal.typeconversion;
 
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
 import java.util.Collection;
 
 public class CompositeNotationParser<N, T> implements NotationParser<N, T> {
@@ -26,9 +28,10 @@ public class CompositeNotationParser<N, T> implements NotationParser<N, T> {
         this.delegates = delegates;
     }
 
-    public void describe(Collection<String> candidateFormats) {
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
         for (NotationParser<?, ?> delegate : delegates) {
-            delegate.describe(candidateFormats);
+            delegate.describe(visitor);
         }
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParser.java
index 644eedd..0118fdc 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParser.java
@@ -17,8 +17,11 @@
 package org.gradle.internal.typeconversion;
 
 import org.gradle.api.specs.Spec;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 import org.gradle.util.CollectionUtils;
+import org.gradle.util.GUtil;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -33,7 +36,7 @@ public class EnumFromCharSequenceNotationParser<T extends Enum> implements Value
 
     public T parseNotation(CharSequence notation) throws UnsupportedNotationException, TypeConversionException {
         final String enumString = notation.toString();
-        List<T> enumConstants = Arrays.asList(type.getEnumConstants());
+        List<? extends T> enumConstants = Arrays.asList(type.getEnumConstants());
         T match = CollectionUtils.findFirst(enumConstants, new Spec<T>() {
             public boolean isSatisfiedBy(T enumValue) {
                 return enumValue.name().equalsIgnoreCase(enumString);
@@ -50,8 +53,11 @@ public class EnumFromCharSequenceNotationParser<T extends Enum> implements Value
         }
     }
 
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add(String.format("A String representing an Enum instance of type %s", type.getName()));
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        List<String> values = new ArrayList<String>();
+        describeValues(values);
+        visitor.candidate(String.format("One of the following values: %s", GUtil.toString(values)));
     }
 
     public void describeValues(Collection<String> collector) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParser.java
index f90c969..944f6e6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParser.java
@@ -16,51 +16,43 @@
 
 package org.gradle.internal.typeconversion;
 
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.util.GUtil;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.exceptions.FormattingDiagnosticsVisitor;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Formatter;
-import java.util.List;
-
-public class ErrorHandlingNotationParser<N, T> implements NotationParser<N, T> {
+class ErrorHandlingNotationParser<N, T> implements NotationParser<N, T> {
     private final String targetTypeDisplayName;
     private final String invalidNotationMessage;
+    private final boolean allowNullInput;
     private final NotationParser<N, T> delegate;
 
-    public ErrorHandlingNotationParser(String targetTypeDisplayName, String invalidNotationMessage, NotationParser<N, T> delegate) {
+    public ErrorHandlingNotationParser(String targetTypeDisplayName, String invalidNotationMessage, boolean allowNullInput, NotationParser<N, T> delegate) {
         this.targetTypeDisplayName = targetTypeDisplayName;
         this.invalidNotationMessage = invalidNotationMessage;
+        this.allowNullInput = allowNullInput;
         this.delegate = delegate;
     }
 
-    public void describe(Collection<String> candidateFormats) {
-        delegate.describe(candidateFormats);
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        delegate.describe(visitor);
     }
 
     public T parseNotation(N notation) {
-        Formatter message = new Formatter();
-        if (notation == null) {
-            //we don't support null input at the moment. If you need this please implement it.
-            message.format("Cannot convert a null value to an object of type %s.%n", targetTypeDisplayName);
+        String failure;
+        //TODO SF add quotes to both formats (there will be *lots* of tests failing so I'm not sure if it is worth it).
+        if (notation == null && !allowNullInput) {
+            failure = String.format("Cannot convert a null value to %s.", targetTypeDisplayName);
         } else {
             try {
                 return delegate.parseNotation(notation);
             } catch (UnsupportedNotationException e) {
-                message.format("Cannot convert the provided notation to an object of type %s: %s.%n", targetTypeDisplayName, e.getNotation());
+                failure = String.format("Cannot convert the provided notation to %s: %s.", targetTypeDisplayName, e.getNotation());
             }
         }
 
-        message.format("The following types/formats are supported:");
-        List<String> formats = new ArrayList<String>();
-        describe(formats);
-        for (String format : formats) {
-            message.format("%n  - %s", format);
-        }
-        if (GUtil.isTrue(invalidNotationMessage)) {
-            message.format("%n%s", invalidNotationMessage);
-        }
-        throw new InvalidUserDataException(message.toString());
+        FormattingDiagnosticsVisitor visitor = new FormattingDiagnosticsVisitor();
+        describe(visitor);
+
+        throw new UnsupportedNotationException(notation, failure, invalidNotationMessage, visitor.getCandidates());
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/FlatteningNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/FlatteningNotationParser.java
index bc7926a..0600081 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/FlatteningNotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/FlatteningNotationParser.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.typeconversion;
 
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 import org.gradle.util.GUtil;
 
 import java.util.Collection;
@@ -34,9 +35,10 @@ public class FlatteningNotationParser<T> implements NotationParser<Object, Set<T
         this.delegate = delegate;
     }
 
-    public void describe(Collection<String> candidateFormats) {
-        delegate.describe(candidateFormats);
-        candidateFormats.add("Collections or arrays of any other supported format. Nested collections/arrays will be flattened.");
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        delegate.describe(visitor);
+        visitor.candidate("Collections or arrays of any other supported format. Nested collections/arrays will be flattened.");
     }
 
     public Set<T> parseNotation(Object notation) {
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningConverter.java
new file mode 100644
index 0000000..dceb3a5
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningConverter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+public class JustReturningConverter<N, T> implements NotationConverter<N, T> {
+
+    private final Class<? extends T> passThroughType;
+
+    public JustReturningConverter(Class<? extends T> passThroughType) {
+        this.passThroughType = passThroughType;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate(String.format("Instances of %s.", passThroughType.getSimpleName()));
+    }
+
+    @Override
+    public void convert(N notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        if (passThroughType.isInstance(notation)) {
+            result.converted(passThroughType.cast(notation));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningParser.java
deleted file mode 100644
index 46c83d3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/JustReturningParser.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.internal.typeconversion;
-
-import java.util.Collection;
-
-public class JustReturningParser<N, T> implements NotationParser<N, T> {
-
-    private final Class<? extends T> passThroughType;
-
-    public JustReturningParser(Class<? extends T> passThroughType) {
-        this.passThroughType = passThroughType;
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add(String.format("Instances of %s.", passThroughType.getSimpleName()));
-    }
-
-    public T parseNotation(N notation) {
-        if (!passThroughType.isInstance(notation)) {
-            throw new UnsupportedNotationException(notation);
-        }
-        return passThroughType.cast(notation);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationConverter.java
new file mode 100644
index 0000000..0dcb9cd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationConverter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.typeconversion;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.util.ConfigureUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Converts a {@code Map<String, Object>} to the target type. Subclasses should define a {@code T parseMap()} method which takes a parameter 
+ * for each key value required from the source map. Each parameter should be annotated with a {@code @MapKey} annotation, and can also
+ * be annotated with a {@code @optional} annotation.
+ */
+public abstract class MapNotationConverter<T> extends TypedNotationConverter<Map, T> {
+    private final Method convertMethod;
+    private final String[] keyNames;
+    private final boolean[] optional;
+
+    public MapNotationConverter() {
+        super(Map.class);
+        convertMethod = findConvertMethod();
+        Annotation[][] parameterAnnotations = convertMethod.getParameterAnnotations();
+        keyNames = new String[parameterAnnotations.length];
+        optional = new boolean[parameterAnnotations.length];
+        for (int i = 0; i < parameterAnnotations.length; i++) {
+            Annotation[] annotations = parameterAnnotations[i];
+            keyNames[i] = keyName(annotations);
+            optional[i] = optional(annotations);
+        }
+    }
+
+    private Method findConvertMethod() {
+        for (Method method : getClass().getDeclaredMethods()) {
+            if (method.getName().equals("parseMap")) {
+                method.setAccessible(true);
+                return method;
+            }
+        }
+        throw new UnsupportedOperationException(String.format("No parseMap() method found on class %s.", getClass().getSimpleName()));
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("Maps");
+    }
+
+    public T parseType(Map values) throws UnsupportedNotationException {
+        Map<String, Object> mutableValues = new HashMap<String, Object>(values);
+        Set<String> missing = new TreeSet<String>();
+
+        Object[] params = new Object[convertMethod.getParameterTypes().length];
+        for (int i = 0; i < params.length; i++) {
+            String keyName = keyNames[i];
+            boolean optional = this.optional[i];
+            Class<?> type = convertMethod.getParameterTypes()[i];
+            Object value;
+            if (type.equals(String.class)) {
+                value = get(mutableValues, keyName);
+            } else {
+                value = type.cast(mutableValues.get(keyName));
+            }
+            if (!optional && value == null) {
+                missing.add(keyName);
+            }
+            mutableValues.remove(keyName);
+            params[i] = value;
+        }
+        if (!missing.isEmpty()) {
+            //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));
+        }
+
+        T result;
+        try {
+            result = (T) convertMethod.invoke(this, params);
+        } catch (IllegalAccessException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.unwrapAndRethrow(e);
+        }
+
+        ConfigureUtil.configureByMap(mutableValues, result);
+        return result;
+    }
+
+    private boolean optional(Annotation[] annotations) {
+        for (Annotation annotation : annotations) {
+            if (annotation instanceof Optional) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String keyName(Annotation[] annotations) {
+        for (Annotation annotation : annotations) {
+            if (annotation instanceof MapKey) {
+                return ((MapKey) annotation).value();
+            }
+        }
+        throw new UnsupportedOperationException("No @Key annotation on parameter of parseMap() method");
+    }
+
+    protected String get(Map<String, Object> args, String key) {
+        Object value = args.get(key);
+        String str = value != null ? value.toString() : null;
+        if (str != null && str.length() == 0) {
+            return null;
+        }
+        return str;
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationParser.java
deleted file mode 100644
index 1a4692d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/MapNotationParser.java
+++ /dev/null
@@ -1,131 +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.internal.typeconversion;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.tasks.Optional;
-import org.gradle.internal.UncheckedException;
-import org.gradle.util.ConfigureUtil;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.*;
-
-/**
- * Converts a {@code Map<String, Object>} to the target type. Subclasses should define a {@code T parseMap()} method which takes a parameter 
- * for each key value required from the source map. Each parameter should be annotated with a {@code @MapKey} annotation, and can also
- * be annotated with a {@code @optional} annotation.
- */
-public abstract class MapNotationParser<T> extends TypedNotationParser<Map, T> {
-    private final Method convertMethod;
-    private final String[] keyNames;
-    private final boolean[] optional;
-
-    public MapNotationParser() {
-        super(Map.class);
-        convertMethod = findConvertMethod();
-        keyNames = new String[convertMethod.getParameterAnnotations().length];
-        optional = new boolean[convertMethod.getParameterAnnotations().length];
-        for (int i = 0; i < convertMethod.getParameterAnnotations().length; i++) {
-            Annotation[] annotations = convertMethod.getParameterAnnotations()[i];
-            keyNames[i] = keyName(annotations);
-            optional[i] = optional(annotations);
-        }
-    }
-
-    private Method findConvertMethod() {
-        for (Method method : getClass().getDeclaredMethods()) {
-            if (method.getName().equals("parseMap")) {
-                method.setAccessible(true);
-                return method;
-            }
-        }
-        throw new UnsupportedOperationException(String.format("No parseMap() method found on class %s.", getClass().getSimpleName()));
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Maps");
-    }
-
-    public T parseType(Map values) throws UnsupportedNotationException {
-        Map<String, Object> mutableValues = new HashMap<String, Object>(values);
-        Set<String> missing = new TreeSet<String>();
-
-        Object[] params = new Object[convertMethod.getParameterTypes().length];
-        for (int i = 0; i < params.length; i++) {
-            String keyName = keyNames[i];
-            boolean optional = this.optional[i];
-            Class<?> type = convertMethod.getParameterTypes()[i];
-            Object value;
-            if (type.equals(String.class)) {
-                value = get(mutableValues, keyName);
-            } else {
-                value = type.cast(mutableValues.get(keyName));
-            }
-            if (!optional && value == null) {
-                missing.add(keyName);
-            }
-            mutableValues.remove(keyName);
-            params[i] = value;
-        }
-        if (!missing.isEmpty()) {
-            //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));
-        }
-
-        T result;
-        try {
-            result = (T) convertMethod.invoke(this, params);
-        } catch (IllegalAccessException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        } catch (InvocationTargetException e) {
-            throw UncheckedException.unwrapAndRethrow(e);
-        }
-
-        ConfigureUtil.configureByMap(mutableValues, result);
-        return result;
-    }
-
-    private boolean optional(Annotation[] annotations) {
-        for (Annotation annotation : annotations) {
-            if (annotation instanceof Optional) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private String keyName(Annotation[] annotations) {
-        for (Annotation annotation : annotations) {
-            if (annotation instanceof MapKey) {
-                return ((MapKey) annotation).value();
-            }
-        }
-        throw new UnsupportedOperationException("No @Key annotation on parameter of parseMap() method");
-    }
-
-    protected String get(Map<String, Object> args, String key) {
-        Object value = args.get(key);
-        String str = value != null ? value.toString() : null;
-        if (str != null && str.length() == 0) {
-            return null;
-        }
-        return str;
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConvertResult.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConvertResult.java
new file mode 100644
index 0000000..305bde9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConvertResult.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+public interface NotationConvertResult<T> {
+    boolean hasResult();
+
+    /**
+     * Invoked when a {@link NotationConverter} is able to convert a notation to a result.
+     */
+    void converted(T result);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverter.java
new file mode 100644
index 0000000..364846e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+/**
+ * A converter from notations of type {@link N} to results of type {@link T}.
+ *
+ * <p>This interface represents an SPI used to implement notation parsers, not the API to use to perform the conversions. Use {@link NotationParser} instead for this.
+ */
+public interface NotationConverter<N, T> {
+    /**
+     * Attempt to convert the given notation.
+     *
+     * @throws TypeConversionException when the notation is recognized but cannot be converted for some reason.
+     */
+    void convert(N notation, NotationConvertResult<? super T> result) throws TypeConversionException;
+
+    /**
+     * Describes the formats that this converter accepts.
+     */
+    void describe(DiagnosticsVisitor visitor);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapter.java
new file mode 100644
index 0000000..0c5bd69
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+public class NotationConverterToNotationParserAdapter<N, T> implements NotationParser<N, T> {
+    private final NotationConverter<N, ? extends T> converter;
+
+    public NotationConverterToNotationParserAdapter(NotationConverter<N, ? extends T> converter) {
+        this.converter = converter;
+    }
+
+    public T parseNotation(N notation) throws UnsupportedNotationException, TypeConversionException {
+        ResultImpl<T> result = new ResultImpl<T>();
+        converter.convert(notation, result);
+        if (!result.hasResult) {
+            throw new UnsupportedNotationException(notation);
+        }
+        return result.result;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        converter.describe(visitor);
+    }
+
+    private static class ResultImpl<T> implements NotationConvertResult<T> {
+        private boolean hasResult;
+        private T result;
+
+        public boolean hasResult() {
+            return hasResult;
+        }
+
+        public void converted(T result) {
+            hasResult = true;
+            this.result = result;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParser.java
index b2d4b6a..ec43be2 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParser.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParser.java
@@ -16,8 +16,12 @@
 
 package org.gradle.internal.typeconversion;
 
-import java.util.Collection;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 
+/**
+ * A parser from notations of type {@link N} to a result of type {@link T}. This interface is used by clients to perform the parsing. To implement a parser, you should use {@link NotationConverter}
+ * instead.
+ */
 public interface NotationParser<N, T> {
     /**
      * @throws UnsupportedNotationException When the supplied notation is not handled by this parser.
@@ -27,6 +31,6 @@ public interface NotationParser<N, T> {
 
     /**
      * Describes the formats that the parser accepts.
-     * */
-    void describe(Collection<String> candidateFormats);
+     */
+    void describe(DiagnosticsVisitor visitor);
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParserBuilder.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParserBuilder.java
index 78e937a..b9503f5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParserBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/NotationParserBuilder.java
@@ -16,8 +16,6 @@
 
 package org.gradle.internal.typeconversion;
 
-import org.gradle.util.GUtil;
-
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
@@ -26,35 +24,90 @@ import java.util.Set;
 public class NotationParserBuilder<T> {
     private TypeInfo<T> resultingType;
     private String invalidNotationMessage;
-    private Collection<NotationParser<Object, ? extends T>> notationParsers = new LinkedList<NotationParser<Object, ? extends T>>();
-    private boolean withJustReturningParser = true;
+    private String typeDisplayName;
+    private boolean implicitConverters = true;
+    private boolean allowNullInput;
+    private final Collection<NotationConverter<Object, ? extends T>> notationParsers = new LinkedList<NotationConverter<Object, ? extends T>>();
+
+    public static <T> NotationParserBuilder<T> toType(Class<T> resultingType) {
+        return new NotationParserBuilder<T>(new TypeInfo<T>(resultingType));
+    }
 
-    public NotationParserBuilder<T> resultingType(Class<T> resultingType) {
-        return resultingType(new TypeInfo<T>(resultingType));
+    public static <T> NotationParserBuilder<T> toType(TypeInfo<T> resultingType) {
+        return new NotationParserBuilder<T>(resultingType);
     }
 
-    public NotationParserBuilder<T> resultingType(TypeInfo<T> resultingType) {
+    private NotationParserBuilder(TypeInfo<T> resultingType) {
         this.resultingType = resultingType;
+        typeDisplayName = resultingType.getTargetType().equals(String.class) ? "a String" : "an object of type ".concat(resultingType.getTargetType().getSimpleName());
+    }
+
+    /**
+     * Specifies the display name for the target type, to use in error messages. By default the target type's simple name is used.
+     */
+    public NotationParserBuilder<T> typeDisplayName(String name) {
+        this.typeDisplayName = name;
         return this;
     }
 
-    public NotationParserBuilder<T> withDefaultJustReturnParser(boolean withJustReturningParser) {
-        this.withJustReturningParser = withJustReturningParser;
+    /**
+     * Use only those converters that are explicitly registered, and disable any implicit conversion that may normally be done.
+     */
+    public NotationParserBuilder<T> noImplicitConverters() {
+        implicitConverters = false;
         return this;
     }
 
-    public NotationParserBuilder<T> parser(NotationParser<Object, ? extends T> parser) {
-        this.notationParsers.add(parser);
+    /**
+     * Allow null as a valid input. The default is to disallow null.
+     *
+     * <p>When this is enabled, all converters must be null safe.
+     *
+     * TODO - attach the null safety to each converter and infer whether null is a valid input or not.
+     */
+    public NotationParserBuilder<T> allowNullInput() {
+        allowNullInput = true;
         return this;
     }
 
-    public NotationParserBuilder<T> invalidNotationMessage(String invalidNotationMessage) {
-        this.invalidNotationMessage = invalidNotationMessage;
+    /**
+     * Adds a converter to use to parse notations. Converters are used in the order added.
+     */
+    public NotationParserBuilder<T> converter(NotationConverter<Object, ? extends T> converter) {
+        this.notationParsers.add(converter);
+        return this;
+    }
+
+    /**
+     * Adds a converter that accepts only notations of the given type.
+     */
+    public <S> NotationParserBuilder<T> fromType(Class<S> notationType, NotationConverter<? super S, ? extends T> converter) {
+        this.notationParsers.add(new TypeFilteringNotationConverter<Object, S, T>(notationType, converter));
         return this;
     }
 
-    public NotationParserBuilder<T> parsers(Iterable<? extends NotationParser<Object, ? extends T>> notationParsers) {
-        GUtil.addToCollection(this.notationParsers, notationParsers);
+    /**
+     * Adds a converter that accepts any CharSequence notation.
+     */
+    public NotationParserBuilder<T> fromCharSequence(NotationConverter<String, ? extends T> converter) {
+        this.notationParsers.add(new CharSequenceNotationConverter<Object, T>(converter));
+        return this;
+    }
+
+    /**
+     * Adds a converter that accepts any CharSequence notation. Can only be used when the target type is String.
+     */
+    public NotationParserBuilder<T> fromCharSequence() {
+        if (!resultingType.getTargetType().equals(String.class)) {
+            throw new UnsupportedOperationException("Can only convert from CharSequence when the target type is String.");
+        }
+        NotationConverter notationParser = new CharSequenceNotationParser();
+        fromCharSequence(notationParser);
+        return this;
+    }
+
+    public NotationParserBuilder<T> invalidNotationMessage(String invalidNotationMessage) {
+        this.invalidNotationMessage = invalidNotationMessage;
         return this;
     }
 
@@ -67,18 +120,16 @@ public class NotationParserBuilder<T> {
     }
 
     private <S> NotationParser<Object, S> wrapInErrorHandling(NotationParser<Object, S> parser) {
-        return new ErrorHandlingNotationParser<Object, S>(resultingType.getTargetType().getSimpleName(), invalidNotationMessage, parser);
+        return new ErrorHandlingNotationParser<Object, S>(typeDisplayName, invalidNotationMessage, allowNullInput, parser);
     }
 
-    private CompositeNotationParser<Object, T> create() {
-        assert resultingType != null : "resultingType cannot be null";
-
-        List<NotationParser<Object, ? extends T>> composites = new LinkedList<NotationParser<Object, ? extends T>>();
-        if(withJustReturningParser){
-            composites.add(new JustReturningParser<Object, T>(resultingType.getTargetType()));
+    private NotationParser<Object, T> create() {
+        List<NotationConverter<Object, ? extends T>> composites = new LinkedList<NotationConverter<Object, ? extends T>>();
+        if (!resultingType.getTargetType().equals(Object.class) && implicitConverters) {
+            composites.add(new JustReturningConverter<Object, T>(resultingType.getTargetType()));
         }
         composites.addAll(this.notationParsers);
 
-        return new CompositeNotationParser<Object, T>(composites);
+        return new NotationConverterToNotationParserAdapter<Object, T>(new CompositeNotationConverter<Object, T>(composites));
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverter.java
new file mode 100644
index 0000000..1d72c4a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+class TypeFilteringNotationConverter<N, S, T> implements NotationConverter<N, T> {
+    private final Class<S> type;
+    private final NotationConverter<? super S, ? extends T> delegate;
+
+    public TypeFilteringNotationConverter(Class<S> type, NotationConverter<? super S, ? extends T> delegate) {
+        this.type = type;
+        this.delegate = delegate;
+    }
+
+    public void convert(N notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        if (type.isInstance(notation)) {
+            delegate.convert(type.cast(notation), result);
+        }
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        delegate.describe(visitor);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationConverter.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationConverter.java
new file mode 100644
index 0000000..57449f1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationConverter.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.internal.typeconversion;
+
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+
+public abstract class TypedNotationConverter<N, T> implements NotationConverter<Object, T> {
+
+    private final Class<N> typeToken;
+
+    public TypedNotationConverter(Class<N> typeToken) {
+        assert typeToken != null : "typeToken cannot be null";
+        this.typeToken = typeToken;
+    }
+
+    public TypedNotationConverter(TypeInfo<N> typeToken) {
+        assert typeToken != null : "typeToken cannot be null";
+        this.typeToken = typeToken.getTargetType();
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate(String.format("Instances of %s.", typeToken.getSimpleName()));
+    }
+
+    @Override
+    public void convert(Object notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        if (typeToken.isInstance(notation)) {
+            result.converted(parseType(typeToken.cast(notation)));
+        }
+    }
+
+    abstract protected T parseType(N notation);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationParser.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationParser.java
deleted file mode 100644
index 640f5ed..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/TypedNotationParser.java
+++ /dev/null
@@ -1,47 +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.internal.typeconversion;
-
-import java.util.Collection;
-
-public abstract class TypedNotationParser<N, T> implements NotationParser<Object, T> {
-
-    private final Class<N> typeToken;
-
-    public TypedNotationParser(Class<N> typeToken) {
-        assert typeToken != null : "typeToken cannot be null";
-        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()));
-    }
-
-    public T parseNotation(Object notation) {
-        if (!typeToken.isInstance(notation)) {
-            throw new UnsupportedNotationException(notation);
-        }
-        return parseType(typeToken.cast(notation));
-    }
-
-    abstract protected T parseType(N notation);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/UnsupportedNotationException.java b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/UnsupportedNotationException.java
index 9f9c2ca..b25495a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/UnsupportedNotationException.java
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/typeconversion/UnsupportedNotationException.java
@@ -15,6 +15,12 @@
  */
 package org.gradle.internal.typeconversion;
 
+import org.gradle.api.Nullable;
+import org.gradle.util.GUtil;
+
+import java.util.Collection;
+import java.util.Formatter;
+
 public class UnsupportedNotationException extends RuntimeException {
     private final Object notation;
 
@@ -22,6 +28,24 @@ public class UnsupportedNotationException extends RuntimeException {
         this.notation = notation;
     }
 
+    public UnsupportedNotationException(Object notation, String failure, @Nullable String resolution, Collection<String> candidateTypes) {
+        super(format(failure, resolution, candidateTypes));
+        this.notation = notation;
+    }
+
+    private static String format(String failure, String resolution, Collection<String> formats) {
+        Formatter message = new Formatter();
+        message.format("%s%n", failure);
+        message.format("The following types/formats are supported:");
+        for (String format : formats) {
+            message.format("%n  - %s", format);
+        }
+        if (GUtil.isTrue(resolution)) {
+            message.format("%n%n%s", resolution);
+        }
+        return message.toString();
+    }
+
     public Object getNotation() {
         return notation;
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleMarkupWriter.java b/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleMarkupWriter.java
new file mode 100644
index 0000000..9596785
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleMarkupWriter.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.xml;
+
+import org.gradle.internal.SystemProperties;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.LinkedList;
+
+/**
+ * <p>A streaming markup writer. Encodes characters and CDATA. Provides only basic state validation, and some simple indentation.</p>
+ *
+ * <p>This class also is-a {@link Writer}, and any characters written to this writer will be encoded as appropriate. Note, however, that
+ * calling {@link #close()} on this object does not close the backing stream.
+ * </p>
+ */
+public class SimpleMarkupWriter extends Writer {
+    private enum Context {
+        Outside, Text, CData, StartTag, ElementContent
+    }
+
+    private final Writer output;
+    private final LinkedList<String> elements = new LinkedList<String>();
+    private Context context = Context.Outside;
+    private int squareBrackets;
+    private final String indent;
+
+    protected SimpleMarkupWriter(Writer writer, String indent) throws IOException {
+        this.indent = indent;
+        this.output = writer;
+    }
+
+    @Override
+    public void write(char[] chars, int offset, int length) throws IOException {
+        characters(chars, offset, length);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        output.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        // Does nothing
+    }
+
+    public SimpleMarkupWriter characters(char[] characters) throws IOException {
+        characters(characters, 0, characters.length);
+        return this;
+    }
+
+    public SimpleMarkupWriter characters(char[] characters, int start, int count) throws IOException {
+        if (context == Context.CData) {
+            writeCDATA(characters, start, count);
+        } else {
+            maybeStartText();
+            writeXmlEncoded(characters, start, count);
+        }
+        return this;
+    }
+
+    public SimpleMarkupWriter characters(CharSequence characters) throws IOException {
+        if (context == Context.CData) {
+            writeCDATA(characters);
+        } else {
+            maybeStartText();
+            writeXmlEncoded(characters);
+        }
+        return this;
+    }
+
+    private void maybeStartText() throws IOException {
+        if (context == Context.Outside) {
+            throw new IllegalStateException("Cannot write text, as there are no started elements.");
+        }
+        if (context == Context.StartTag) {
+            writeRaw(">");
+        }
+        context = Context.Text;
+    }
+
+    private void maybeFinishStartTag() throws IOException {
+        if (context == Context.StartTag) {
+            writeRaw(">");
+            context = Context.ElementContent;
+        }
+    }
+
+    public SimpleMarkupWriter startElement(String name) throws IOException {
+        if (!XmlValidation.isValidXmlName(name)) {
+            throw new IllegalArgumentException(String.format("Invalid element name: '%s'", name));
+        }
+        if (context == Context.CData) {
+            throw new IllegalStateException("Cannot start element, as current CDATA node has not been closed.");
+        }
+        maybeFinishStartTag();
+        if (indent != null) {
+            writeRaw(SystemProperties.getInstance().getLineSeparator());
+            for (int i = 0; i < elements.size(); i++) {
+                writeRaw(indent);
+            }
+        }
+        context = Context.StartTag;
+        elements.add(name);
+        writeRaw("<");
+        writeRaw(name);
+        return this;
+    }
+
+    public SimpleMarkupWriter endElement() throws IOException {
+        if (context == Context.Outside) {
+            throw new IllegalStateException("Cannot end element, as there are no started elements.");
+        }
+        if (context == Context.CData) {
+            throw new IllegalStateException("Cannot end element, as current CDATA node has not been closed.");
+        }
+        if (context == Context.StartTag) {
+            writeRaw("/>");
+            elements.removeLast();
+        } else {
+            if (context != Context.Text && indent != null) {
+                writeRaw(SystemProperties.getInstance().getLineSeparator());
+                for (int i = 1; i < elements.size(); i++) {
+                    writeRaw(indent);
+                }
+            }
+            writeRaw("</");
+            writeRaw(elements.removeLast());
+            writeRaw(">");
+        }
+        if (elements.isEmpty()) {
+            if (indent != null) {
+                writeRaw(SystemProperties.getInstance().getLineSeparator());
+            }
+            output.flush();
+            context = Context.Outside;
+        } else {
+            context = Context.ElementContent;
+        }
+        return this;
+    }
+
+    private void writeCDATA(char[] cdata, int offset, int count) throws IOException {
+        int end = offset + count;
+        for (int i = offset; i < end; i++) {
+            writeCDATA(cdata[i]);
+        }
+    }
+
+    private void writeCDATA(CharSequence cdata) throws IOException {
+        int len = cdata.length();
+        for (int i = 0; i < len; i++) {
+            writeCDATA(cdata.charAt(i));
+        }
+    }
+
+    private void writeCDATA(char ch) throws IOException {
+        if (needsCDATAEscaping(ch)) {
+            writeRaw("]]><![CDATA[>");
+        } else if (!XmlValidation.isLegalCharacter(ch)) {
+            writeRaw('?');
+        } else if (XmlValidation.isRestrictedCharacter(ch)) {
+            writeRaw("]]>");
+            writeCharacterReference(ch);
+            writeRaw("<![CDATA[");
+        } else {
+            writeRaw(ch);
+        }
+    }
+
+    private void writeCharacterReference(char ch) throws IOException {
+        writeRaw("&#x");
+        writeRaw(Integer.toHexString(ch));
+        writeRaw(";");
+    }
+
+    private boolean needsCDATAEscaping(char ch) {
+        switch (ch) {
+            case ']':
+                squareBrackets++;
+                return false;
+            case '>':
+                if (squareBrackets >= 2) {
+                    squareBrackets = 0;
+                    return true;
+                }
+                return false;
+            default:
+                squareBrackets = 0;
+                return false;
+        }
+    }
+
+    public SimpleMarkupWriter startCDATA() throws IOException {
+        if (context == Context.CData) {
+            throw new IllegalStateException("Cannot start CDATA node, as current CDATA node has not been closed.");
+        }
+        maybeFinishStartTag();
+        writeRaw("<![CDATA[");
+        context = Context.CData;
+        squareBrackets = 0;
+        return this;
+    }
+
+    public SimpleMarkupWriter endCDATA() throws IOException {
+        if (context != Context.CData) {
+            throw new IllegalStateException("Cannot end CDATA node, as not currently in a CDATA node.");
+        }
+        writeRaw("]]>");
+        context = Context.Text;
+        return this;
+    }
+
+    public SimpleMarkupWriter attribute(String name, String value) throws IOException {
+        if (!XmlValidation.isValidXmlName(name)) {
+            throw new IllegalArgumentException(String.format("Invalid attribute name: '%s'", name));
+        }
+        if (context != Context.StartTag) {
+            throw new IllegalStateException("Cannot write attribute [" + name + ":" + value + "]. You should write start element first.");
+        }
+
+        writeRaw(" ");
+        writeRaw(name);
+        writeRaw("=\"");
+        writeXmlAttributeEncoded(value);
+        writeRaw("\"");
+        return this;
+    }
+
+    private void writeRaw(char c) throws IOException {
+        output.write(c);
+    }
+
+    protected void writeRaw(String message) throws IOException {
+        output.write(message);
+    }
+
+    private void writeXmlEncoded(char[] message, int offset, int count) throws IOException {
+        int end = offset + count;
+        for (int i = offset; i < end; i++) {
+            writeXmlEncoded(message[i]);
+        }
+    }
+
+    private void writeXmlAttributeEncoded(CharSequence message) throws IOException {
+        assert message != null;
+        int len = message.length();
+        for (int i = 0; i < len; i++) {
+            writeXmlAttributeEncoded(message.charAt(i));
+        }
+    }
+
+    private void writeXmlAttributeEncoded(char ch) throws IOException {
+        if (ch == 9) {
+            writeRaw("	");
+        } else if (ch == 10) {
+            writeRaw("
");
+        } else if (ch == 13) {
+            writeRaw("
");
+        } else {
+            writeXmlEncoded(ch);
+        }
+    }
+
+    private void writeXmlEncoded(CharSequence message) throws IOException {
+        assert message != null;
+        int len = message.length();
+        for (int i = 0; i < len; i++) {
+            writeXmlEncoded(message.charAt(i));
+        }
+    }
+
+    private void writeXmlEncoded(char ch) throws IOException {
+        if (ch == '<') {
+            writeRaw("<");
+        } else if (ch == '>') {
+            writeRaw(">");
+        } else if (ch == '&') {
+            writeRaw("&");
+        } else if (ch == '"') {
+            writeRaw(""");
+        } else if (!XmlValidation.isLegalCharacter(ch)) {
+            writeRaw('?');
+        } else if (XmlValidation.isRestrictedCharacter(ch)) {
+            writeCharacterReference(ch);
+        } else {
+            writeRaw(ch);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleXmlWriter.java b/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleXmlWriter.java
new file mode 100644
index 0000000..ff0f737
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/xml/SimpleXmlWriter.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.internal.xml;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * <p>A streaming XML writer.</p>
+ */
+public class SimpleXmlWriter extends SimpleMarkupWriter {
+
+    public SimpleXmlWriter(OutputStream output) throws IOException {
+        this(output, null);
+    }
+
+    public SimpleXmlWriter(OutputStream output, String indent) throws IOException {
+        this(new OutputStreamWriter(output, "UTF-8"), indent, "UTF-8");
+    }
+
+    public SimpleXmlWriter(Writer writer, String indent, String encoding) throws IOException {
+        super(writer, indent);
+        writeXmlDeclaration(encoding);
+    }
+
+    private void writeXmlDeclaration(String encoding) throws IOException {
+        writeRaw(String.format("<?xml version=\"1.0\" encoding=\"%s\"?>", encoding));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlTransformer.java b/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlTransformer.java
new file mode 100644
index 0000000..989f2c0
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlTransformer.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.xml;
+
+import groovy.lang.Closure;
+import groovy.util.IndentPrinter;
+import groovy.util.Node;
+import groovy.util.XmlNodePrinter;
+import groovy.util.XmlParser;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.XmlProvider;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.DomNode;
+import org.gradle.internal.IoActions;
+import org.gradle.internal.SystemProperties;
+import org.gradle.internal.UncheckedException;
+import org.gradle.util.GUtil;
+import org.gradle.util.TextUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class XmlTransformer implements Transformer<String, String> {
+    private final List<Action<? super XmlProvider>> actions = new ArrayList<Action<? super XmlProvider>>();
+    private String indentation = "  ";
+
+    public void addAction(Action<? super XmlProvider> provider) {
+        actions.add(provider);
+    }
+
+    public void setIndentation(String indentation) {
+        this.indentation = indentation;
+    }
+
+    public void addAction(Closure closure) {
+        actions.add(new ClosureBackedAction<XmlProvider>(closure));
+    }
+
+    public void transform(File destination, final String encoding, final Action<? super Writer> generator) {
+        IoActions.writeTextFile(destination, encoding, new Action<Writer>() {
+            public void execute(Writer writer) {
+                transform(writer, encoding, generator);
+            }
+        });
+    }
+
+    public void transform(File destination, final Action<? super Writer> generator) {
+        IoActions.writeTextFile(destination, new Action<Writer>() {
+            public void execute(Writer writer) {
+                transform(writer, generator);
+            }
+        });
+    }
+
+    public void transform(Writer destination, Action<? super Writer> generator) {
+        StringWriter stringWriter = new StringWriter();
+        generator.execute(stringWriter);
+        transform(stringWriter.toString(), destination);
+    }
+
+    public void transform(Writer destination, String encoding, Action<? super Writer> generator) {
+        StringWriter stringWriter = new StringWriter();
+        generator.execute(stringWriter);
+        doTransform(stringWriter.toString()).writeTo(destination, encoding);
+    }
+
+    public String transform(String original) {
+        return doTransform(original).toString();
+    }
+
+    public void transform(String original, Writer destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(String original, OutputStream destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(Node original, Writer destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(Node original, OutputStream destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(Node original, File destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(DomNode original, Writer destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(DomNode original, OutputStream destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    private XmlProviderImpl doTransform(String original) {
+        return doTransform(new XmlProviderImpl(original));
+    }
+
+    private XmlProviderImpl doTransform(Node original) {
+        return doTransform(new XmlProviderImpl(original));
+    }
+
+    private XmlProviderImpl doTransform(DomNode original) {
+        return doTransform(new XmlProviderImpl(original));
+    }
+
+    private XmlProviderImpl doTransform(XmlProviderImpl provider) {
+        provider.apply(actions);
+        return provider;
+    }
+
+    private class XmlProviderImpl implements XmlProvider {
+        private StringBuilder builder;
+        private Node node;
+        private String stringValue;
+        private Element element;
+        private String publicId;
+        private String systemId;
+
+        public XmlProviderImpl(String original) {
+            this.stringValue = original;
+        }
+
+        public XmlProviderImpl(Node original) {
+            this.node = original;
+        }
+
+        public XmlProviderImpl(DomNode original) {
+            this.node = original;
+            publicId = original.getPublicId();
+            systemId = original.getSystemId();
+        }
+
+        public void apply(Iterable<Action<? super XmlProvider>> actions) {
+            for (Action<? super XmlProvider> action : actions) {
+                action.execute(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringWriter writer = new StringWriter();
+            writeTo(writer);
+            return writer.toString();
+        }
+
+        public void writeTo(Writer writer) {
+            doWriteTo(writer, null);
+        }
+
+        public void writeTo(Writer writer, String encoding) {
+            doWriteTo(writer, encoding);
+        }
+
+        public void writeTo(File file) {
+            try {
+                OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
+                try {
+                    writeTo(outputStream);
+                } finally {
+                    outputStream.close();
+                }
+            } catch (IOException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        public void writeTo(OutputStream stream) {
+            try {
+                Writer writer = new OutputStreamWriter(stream, "UTF-8");
+                doWriteTo(writer, "UTF-8");
+                writer.flush();
+            } catch (IOException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        public StringBuilder asString() {
+            if (builder == null) {
+                builder = new StringBuilder(toString());
+                node = null;
+                element = null;
+            }
+            return builder;
+        }
+
+        public Node asNode() {
+            if (node == null) {
+                try {
+                    node = new XmlParser().parseText(toString());
+                } catch (Exception e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+                builder = null;
+                element = null;
+            }
+            return node;
+        }
+
+        public Element asElement() {
+            if (element == null) {
+                Document document;
+                try {
+                    document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(toString())));
+                } catch (Exception e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+                element = document.getDocumentElement();
+                builder = null;
+                node = null;
+            }
+            return element;
+        }
+
+        private void doWriteTo(Writer writer, String encoding) {
+            writeXmlDeclaration(writer, encoding);
+
+            try {
+                if (node != null) {
+                    printNode(node, writer);
+                } else if (element != null) {
+                    printDomNode(element, writer);
+                } else if (builder != null) {
+                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(builder)));
+                } else {
+                    writer.append(TextUtil.toPlatformLineSeparators(stripXmlDeclaration(stringValue)));
+                }
+            } catch (IOException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        private void printNode(Node node, Writer writer) {
+            final PrintWriter printWriter = new PrintWriter(writer);
+            if (GUtil.isTrue(publicId)) {
+                printWriter.format("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">%n", node.name(), publicId, systemId);
+            }
+            IndentPrinter indentPrinter = new IndentPrinter(printWriter, indentation) {
+                @Override
+                public void println() {
+                    printWriter.println();
+                }
+            };
+            XmlNodePrinter nodePrinter = new XmlNodePrinter(indentPrinter);
+            nodePrinter.setPreserveWhitespace(true);
+            nodePrinter.print(node);
+            printWriter.flush();
+        }
+
+        private void printDomNode(org.w3c.dom.Node node, Writer destination) {
+            removeEmptyTextNodes(node); // empty text nodes hinder subsequent formatting
+            int indentAmount = determineIndentAmount();
+
+            try {
+                TransformerFactory factory = TransformerFactory.newInstance();
+                try {
+                    factory.setAttribute("indent-number", indentAmount);
+                } catch (IllegalArgumentException ignored) {
+                    /* unsupported by this transformer */
+                }
+
+                javax.xml.transform.Transformer transformer = factory.newTransformer();
+                transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+                transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+                if (GUtil.isTrue(publicId)) {
+                    transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicId);
+                    transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemId);
+                }
+                try {
+                    // some impls support this but not factory.setAttribute("indent-number")
+                    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indentAmount));
+                } catch (IllegalArgumentException ignored) {
+                    /* unsupported by this transformer */
+                }
+
+                transformer.transform(new DOMSource(node), new StreamResult(destination));
+            } catch (TransformerException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        private int determineIndentAmount() {
+            if (indentation.equals("\t")) { // not supported by javax.xml.transform.Transformer; use two spaces instead
+                return 2;
+            }
+            return indentation.length(); // assume indentation uses spaces
+        }
+
+        private void removeEmptyTextNodes(org.w3c.dom.Node node) {
+            org.w3c.dom.NodeList children = node.getChildNodes();
+
+            for (int i = 0; i < children.getLength(); i++) {
+                org.w3c.dom.Node child = children.item(i);
+                if (child.getNodeType() == org.w3c.dom.Node.TEXT_NODE && child.getNodeValue().trim().length() == 0) {
+                    node.removeChild(child);
+                } else {
+                    removeEmptyTextNodes(child);
+                }
+            }
+        }
+
+        private void writeXmlDeclaration(Writer writer, String encoding) {
+            try {
+                writer.write("<?xml version=\"1.0\"");
+                if (encoding != null) {
+                    writer.write(" encoding=\"");
+                    writer.write(encoding);
+                    writer.write("\"");
+                }
+                writer.write("?>");
+                writer.write(SystemProperties.getInstance().getLineSeparator());
+            } catch (IOException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+        private boolean hasXmlDeclaration(String xml) {
+            return xml.startsWith("<?xml"); // XML declarations must be located at first position of first line
+        }
+
+        private String stripXmlDeclaration(CharSequence sequence) {
+            String str = sequence.toString();
+            if (hasXmlDeclaration(str)) {
+                str = str.substring(str.indexOf("?>") + 2);
+                str = StringUtils.stripStart(str, null);
+            }
+            return str;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlValidation.java b/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlValidation.java
new file mode 100644
index 0000000..d4a4e85
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/internal/xml/XmlValidation.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.xml;
+
+public class XmlValidation {
+    public static boolean isValidXmlName(String name) {
+        // element names can only contain 0 or 1 colon
+        // See http://www.w3.org/TR/2004/REC-xml-names11-20040204/#Conformance
+        if (name.indexOf(':') != name.lastIndexOf(':')) {
+            return false;
+        }
+
+        // If the name has a prefix, evaluate both prefix and name
+        if (name.indexOf(':') != -1 && name.charAt(0) != ':') {
+            return isValidXmlName(name.substring(0, name.indexOf(':')))
+                    && isValidXmlName(name.substring(name.indexOf(':')+1));
+        }
+
+        int length = name.length();
+        if (length == 0) {
+            return false;
+        }
+        char ch = name.charAt(0);
+        if (!isValidNameStartChar(ch)) {
+            return false;
+        }
+        for (int i = 1; i < length; i++) {
+            ch = name.charAt(i);
+            if (!isValidNameChar(ch)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isValidNameChar(char ch) {
+        if (isValidNameStartChar(ch)) {
+            return true;
+        }
+        if (ch >= '0' && ch <= '9') {
+            return true;
+        }
+        if (ch == '-' || ch == '.' || ch == '\u00b7') {
+            return true;
+        }
+        if (ch >= '\u0300' && ch <= '\u036f') {
+            return true;
+        }
+        if (ch >= '\u203f' && ch <= '\u2040') {
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean isValidNameStartChar(char ch) {
+        if (ch >= 'A' && ch <= 'Z') {
+            return true;
+        }
+        if (ch >= 'a' && ch <= 'z') {
+            return true;
+        }
+        if (ch == ':' || ch == '_') {
+            return true;
+        }
+        if (ch >= '\u00c0' && ch <= '\u00d6') {
+            return true;
+        }
+        if (ch >= '\u00d8' && ch <= '\u00f6') {
+            return true;
+        }
+        if (ch >= '\u00f8' && ch <= '\u02ff') {
+            return true;
+        }
+        if (ch >= '\u0370' && ch <= '\u037d') {
+            return true;
+        }
+        if (ch >= '\u037f' && ch <= '\u1fff') {
+            return true;
+        }
+        if (ch >= '\u200c' && ch <= '\u200d') {
+            return true;
+        }
+        if (ch >= '\u2070' && ch <= '\u218f') {
+            return true;
+        }
+        if (ch >= '\u2c00' && ch <= '\u2fef') {
+            return true;
+        }
+        if (ch >= '\u3001' && ch <= '\ud7ff') {
+            return true;
+        }
+        if (ch >= '\uf900' && ch <= '\ufdcf') {
+            return true;
+        }
+        if (ch >= '\ufdf0' && ch <= '\ufffd') {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isLegalCharacter(final char c) {
+        if (c == 0x9 || c == 0xA || c == 0xD) {
+            return true;
+        } else if (c < 0x20) {
+            return false;
+        } else if (c <= 0xD7FF) {
+            return true;
+        } else if (c < 0xE000) {
+            return false;
+        } else if (c <= 0xFFFD) {
+            return true;
+        } else if (c < 0x10000) {
+            return false;
+        } else if (c <= 0x10FFFF) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isRestrictedCharacter(char c) {
+        if (c == 0x9 || c == 0xA || c == 0xD || c == 0x85) {
+            return false;
+        } else if (c <= 0x1F) {
+            return true;
+        } else if (c < 0x7F) {
+            return false;
+        } else if (c <= 0x9F) {
+            return true;
+        }
+        return false;
+    }
+}
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 bea78db..1ea072a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
+++ b/subprojects/core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
@@ -28,56 +28,44 @@ import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
 import org.gradle.api.internal.project.AbstractPluginAware;
 import org.gradle.api.internal.project.ProjectInternal;
 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.initialization.ClassLoaderScopeRegistry;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.listener.ActionBroadcast;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.util.GradleVersion;
 
+import javax.inject.Inject;
 import java.io.File;
 
 public class DefaultGradle extends AbstractPluginAware implements GradleInternal {
     private ProjectInternal rootProject;
     private ProjectInternal defaultProject;
-    private final TaskGraphExecuter taskGraph;
     private final Gradle parent;
     private final StartParameter startParameter;
-    private final ListenerManager listenerManager;
     private final ServiceRegistry services;
-    private final GradleDistributionLocator distributionLocator;
     private final ListenerBroadcast<BuildListener> buildListenerBroadcast;
     private final ListenerBroadcast<ProjectEvaluationListener> projectEvaluationListenerBroadcast;
     private ActionBroadcast<Project> rootProjectActions = new ActionBroadcast<Project>();
 
-    private PluginContainer pluginContainer;
-    private FileResolver fileResolver;
-
-    private final ScriptPluginFactory scriptPluginFactory;
     private final ClassLoaderScope classLoaderScope;
-    private final ScriptHandlerFactory scriptHandlerFactory;
 
     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);
-        taskGraph = services.get(TaskGraphExecuter.class);
-        distributionLocator = services.get(GradleDistributionLocator.class);
-        classLoaderScope = services.get(ClassLoaderScope.class);
-        pluginContainer = services.get(PluginContainer.class);
-        fileResolver = services.get(FileResolver.class);
-        scriptPluginFactory = services.get(ScriptPluginFactory.class);
-        scriptHandlerFactory = services.get(ScriptHandlerFactory.class);
-        buildListenerBroadcast = listenerManager.createAnonymousBroadcaster(BuildListener.class);
-        projectEvaluationListenerBroadcast = listenerManager.createAnonymousBroadcaster(ProjectEvaluationListener.class);
+        classLoaderScope = services.get(ClassLoaderScopeRegistry.class).getCoreAndPluginsScope();
+        buildListenerBroadcast = getListenerManager().createAnonymousBroadcaster(BuildListener.class);
+        projectEvaluationListenerBroadcast = getListenerManager().createAnonymousBroadcaster(ProjectEvaluationListener.class);
         buildListenerBroadcast.add(new BuildAdapter() {
             @Override
             public void projectsLoaded(Gradle gradle) {
@@ -101,7 +89,7 @@ public class DefaultGradle extends AbstractPluginAware implements GradleInternal
     }
 
     public File getGradleHomeDir() {
-        return distributionLocator.getGradleHome();
+        return getDistributionLocator().getGradleHome();
     }
 
     public File getGradleUserHomeDir() {
@@ -148,8 +136,9 @@ public class DefaultGradle extends AbstractPluginAware implements GradleInternal
         this.defaultProject = defaultProject;
     }
 
+    @Inject
     public TaskGraphExecuter getTaskGraph() {
-        return taskGraph;
+        throw new UnsupportedOperationException();
     }
 
     public ProjectEvaluationListener addProjectEvaluationListener(ProjectEvaluationListener listener) {
@@ -190,15 +179,15 @@ public class DefaultGradle extends AbstractPluginAware implements GradleInternal
     }
 
     public void addListener(Object listener) {
-        listenerManager.addListener(listener);
+        getListenerManager().addListener(listener);
     }
 
     public void removeListener(Object listener) {
-        listenerManager.removeListener(listener);
+        getListenerManager().removeListener(listener);
     }
 
     public void useLogger(Object logger) {
-        listenerManager.useLogger(logger);
+        getListenerManager().useLogger(logger);
     }
 
     public ProjectEvaluationListener getProjectEvaluationBroadcaster() {
@@ -221,31 +210,47 @@ public class DefaultGradle extends AbstractPluginAware implements GradleInternal
         return services;
     }
 
+    @Inject
     public ServiceRegistryFactory getServiceRegistryFactory() {
-        return services.get(ServiceRegistryFactory.class);
+        throw new UnsupportedOperationException();
     }
 
-    public PluginContainer getPlugins() {
-        return pluginContainer;
+    @Override
+    protected DefaultObjectConfigurationAction createObjectConfigurationAction() {
+        return new DefaultObjectConfigurationAction(getFileResolver(), getScriptPluginFactory(), getScriptHandlerFactory(), getClassLoaderScope(), this);
     }
 
-    @Override
-    protected FileResolver getFileResolver() {
-        return fileResolver;
+    public ClassLoaderScope getClassLoaderScope() {
+        return classLoaderScope;
     }
 
-    @Override
+    @Inject
+    protected ScriptHandlerFactory getScriptHandlerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
     protected ScriptPluginFactory getScriptPluginFactory() {
-        return scriptPluginFactory;
+        throw new UnsupportedOperationException();
     }
 
-    @Override
-    protected ScriptHandlerFactory getScriptHandlerFactory() {
-        return scriptHandlerFactory;
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
     }
 
-    @Override
-    public ClassLoaderScope getClassLoaderScope() {
-        return classLoaderScope;
+    @Inject
+    protected GradleDistributionLocator getDistributionLocator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ListenerManager getListenerManager() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    public PluginManagerInternal getPluginManager() {
+        throw new UnsupportedOperationException();
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/BroadcastDispatch.java b/subprojects/core/src/main/groovy/org/gradle/listener/BroadcastDispatch.java
deleted file mode 100755
index dac4c5a..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/listener/BroadcastDispatch.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.listener;
-
-import org.gradle.api.Action;
-import org.gradle.internal.UncheckedException;
-import org.gradle.messaging.dispatch.Dispatch;
-import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.messaging.dispatch.ReflectionDispatch;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class BroadcastDispatch<T> implements Dispatch<MethodInvocation> {
-    private final Class<T> type;
-    private final Map<Object, Dispatch<MethodInvocation>> handlers = new LinkedHashMap<Object, Dispatch<MethodInvocation>>();
-
-    public BroadcastDispatch(Class<T> type) {
-        this.type = type;
-    }
-
-    public Class<T> getType() {
-        return type;
-    }
-
-    public void add(Dispatch<MethodInvocation> dispatch) {
-        handlers.put(dispatch, dispatch);
-    }
-
-    public void add(T listener) {
-        handlers.put(listener, new ReflectionDispatch(listener));
-    }
-
-    public void add(String methodName, Action<?> action) {
-        assertIsMethod(methodName);
-        handlers.put(action, new ActionInvocationHandler(methodName, action));
-    }
-
-    private void assertIsMethod(String methodName) {
-        for (Method method : type.getMethods()) {
-            if (method.getName().equals(methodName)) {
-                return;
-            }
-        }
-        throw new IllegalArgumentException(String.format("Method %s() not found for listener type %s.", methodName,
-                type.getSimpleName()));
-    }
-
-    public void remove(Object listener) {
-        handlers.remove(listener);
-    }
-
-    public void removeAll() {
-        handlers.clear();
-    }
-
-    private String getErrorMessage() {
-        String typeDescription = type.getSimpleName().replaceAll("(\\p{Upper})", " $1").trim().toLowerCase();
-        return String.format("Failed to notify %s.", typeDescription);
-    }
-
-    public void dispatch(MethodInvocation invocation) {
-        List<Throwable> failures = new ArrayList<Throwable>();
-        for (Dispatch<MethodInvocation> handler : new ArrayList<Dispatch<MethodInvocation>>(handlers.values())) {
-            try {
-                handler.dispatch(invocation);
-            } catch (UncheckedException e) {
-                failures.add(e.getCause());
-            } catch (Throwable t) {
-                failures.add(t);
-            }
-        }
-        if (failures.size() == 1 && failures.get(0) instanceof RuntimeException) {
-            throw (RuntimeException) failures.get(0);
-        }
-        if (!failures.isEmpty()) {
-            throw new ListenerNotificationException(getErrorMessage(), failures);
-        }
-    }
-
-    private class ActionInvocationHandler implements Dispatch<MethodInvocation> {
-        private final String methodName;
-        private final Action action;
-
-        public ActionInvocationHandler(String methodName, Action action) {
-            this.methodName = methodName;
-            this.action = action;
-        }
-
-        public void dispatch(MethodInvocation message) {
-            if (message.getMethod().getName().equals(methodName)) {
-                action.execute(message.getArguments()[0]);
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/ContextClassLoaderProxy.java b/subprojects/core/src/main/groovy/org/gradle/listener/ContextClassLoaderProxy.java
deleted file mode 100644
index 30e4976..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/listener/ContextClassLoaderProxy.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.listener;
-
-import org.gradle.messaging.dispatch.ContextClassLoaderDispatch;
-import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.messaging.dispatch.ProxyDispatchAdapter;
-import org.gradle.messaging.dispatch.ReflectionDispatch;
-
-/**
- * Creates a proxy object which sets the context ClassLoader when invoking methods on the target object.
- *
- * @param <T>
- */
-public class ContextClassLoaderProxy<T> {
-    private final ProxyDispatchAdapter<T> adapter;
-
-    /**
-     * Creates a proxy which dispatches to the given target object.
-     */
-    public ContextClassLoaderProxy(Class<T> type, T target, ClassLoader contextClassLoader) {
-        adapter = new ProxyDispatchAdapter<T>(new ContextClassLoaderDispatch<MethodInvocation>(new ReflectionDispatch(target), contextClassLoader), type);
-    }
-
-    public T getSource() {
-        return adapter.getSource();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/DefaultListenerManager.java b/subprojects/core/src/main/groovy/org/gradle/listener/DefaultListenerManager.java
deleted file mode 100644
index 2d0aebd..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/listener/DefaultListenerManager.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.listener;
-
-import groovy.lang.Closure;
-import org.gradle.messaging.dispatch.Dispatch;
-import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.messaging.dispatch.ReflectionDispatch;
-
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
- at SuppressWarnings({"unchecked"})
-public class DefaultListenerManager implements ListenerManager {
-    private final Set<Object> allListeners = new LinkedHashSet<Object>();
-    private final Set<Object> allLoggers = new LinkedHashSet<Object>();
-    private final Map<Class<?>, ListenerBroadcast> broadcasters = new HashMap<Class<?>, ListenerBroadcast>();
-    private final Map<Class<?>, LoggerDispatch> loggers = new HashMap<Class<?>, LoggerDispatch>();
-    private final Map<Class<?>, BroadcastDispatch> dispatchers = new HashMap<Class<?>, BroadcastDispatch>();
-    private final Object lock = new Object();
-    private final DefaultListenerManager parent;
-
-    public DefaultListenerManager() {
-        this(null);
-    }
-
-    private DefaultListenerManager(DefaultListenerManager parent) {
-        this.parent = parent;
-    }
-
-    public void addListener(Object listener) {
-        synchronized (lock) {
-            if (allListeners.add(listener)) {
-                for (BroadcastDispatch<?> broadcaster : dispatchers.values()) {
-                    maybeAddToDispatcher(broadcaster, listener);
-                }
-            }
-        }
-    }
-
-    public void addListener(Class<?> listenerType, String method, Closure listenerClosure) {
-        addListener(new ClosureListener(listenerType, method, listenerClosure));
-    }
-
-    public void removeListener(Object listener) {
-        synchronized (lock) {
-            if (allListeners.remove(listener)) {
-                for (BroadcastDispatch<?> broadcaster : dispatchers.values()) {
-                    broadcaster.remove(listener);
-                }
-            }
-        }
-    }
-
-    public void useLogger(Object logger) {
-        synchronized (lock) {
-            if (allLoggers.add(logger)) {
-                for (LoggerDispatch dispatch : loggers.values()) {
-                    dispatch.maybeSetLogger(logger);
-                }
-            }
-        }
-    }
-
-    public <T> T getBroadcaster(Class<T> listenerClass) {
-        return getBroadcasterInternal(listenerClass).getSource();
-    }
-
-    public <T> ListenerBroadcast<T> createAnonymousBroadcaster(Class<T> listenerClass) {
-        ListenerBroadcast<T> broadcast = new ListenerBroadcast(listenerClass);
-        broadcast.add(getBroadcasterInternal(listenerClass).getSource());
-        return broadcast;
-    }
-
-    private <T> ListenerBroadcast<T> getBroadcasterInternal(Class<T> listenerClass) {
-        synchronized (lock) {
-            ListenerBroadcast<T> broadcaster = broadcasters.get(listenerClass);
-            if (broadcaster == null) {
-                broadcaster = new ListenerBroadcast<T>(listenerClass);
-                broadcaster.add(getLogger(listenerClass));
-                broadcaster.add(getDispatcher(listenerClass));
-                if (parent != null) {
-                    broadcaster.add(parent.getDispatcher(listenerClass));
-                }
-                broadcasters.put(listenerClass, broadcaster);
-            }
-
-            return broadcaster;
-        }
-    }
-
-    private <T> BroadcastDispatch<T> getDispatcher(Class<T> listenerClass) {
-        synchronized (lock) {
-            BroadcastDispatch<T> dispatcher = dispatchers.get(listenerClass);
-            if (dispatcher == null) {
-                dispatcher = new BroadcastDispatch<T>(listenerClass);
-                dispatchers.put(listenerClass, dispatcher);
-                for (Object listener : allListeners) {
-                    maybeAddToDispatcher(dispatcher, listener);
-                }
-            }
-            return dispatcher;
-        }
-    }
-
-    private LoggerDispatch getLogger(Class<?> listenerClass) {
-        synchronized (lock) {
-            LoggerDispatch dispatch = loggers.get(listenerClass);
-            if (dispatch == null) {
-                dispatch = new LoggerDispatch(listenerClass, parent == null ? null : parent.getLogger(listenerClass));
-                for (Object logger : allLoggers) {
-                    dispatch.maybeSetLogger(logger);
-                }
-                loggers.put(listenerClass, dispatch);
-            }
-            return dispatch;
-        }
-    }
-
-    private void maybeAddToDispatcher(BroadcastDispatch broadcaster, Object listener) {
-        if (listener instanceof ClosureListener) {
-            ClosureListener closureListener = (ClosureListener) listener;
-            if (broadcaster.getType().isAssignableFrom(closureListener.listenerType)) {
-                broadcaster.add(new ClosureBackedMethodInvocationDispatch(closureListener.method, closureListener.closure));
-            }
-        } else if (broadcaster.getType().isInstance(listener)) {
-            broadcaster.add(listener);
-        }
-    }
-
-    public ListenerManager createChild() {
-        return new DefaultListenerManager(this);
-    }
-
-    private static class ClosureListener {
-        final Class<?> listenerType;
-        final String method;
-        final Closure closure;
-
-        private ClosureListener(Class<?> listenerType, String method, Closure closure) {
-            this.listenerType = listenerType;
-            this.method = method;
-            this.closure = closure;
-        }
-    }
-
-    private static class LoggerDispatch implements Dispatch<MethodInvocation> {
-        private final Class<?> type;
-        private Dispatch<MethodInvocation> dispatch;
-
-        private LoggerDispatch(Class<?> type, LoggerDispatch parentDispatch) {
-            this.type = type;
-            this.dispatch = parentDispatch;
-        }
-
-        public void dispatch(MethodInvocation message) {
-            if (dispatch != null) {
-                dispatch.dispatch(message);
-            }
-        }
-
-        public void maybeSetLogger(Object logger) {
-            if (type.isInstance(logger)) {
-                dispatch = new ReflectionDispatch(logger);
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/LazyCreationProxy.java b/subprojects/core/src/main/groovy/org/gradle/listener/LazyCreationProxy.java
deleted file mode 100644
index 71e0f84..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/listener/LazyCreationProxy.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.listener;
-
-import org.gradle.internal.Factory;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-public class LazyCreationProxy<T> {
-    private final T source;
-
-    public LazyCreationProxy(Class<T> type, final Factory<? extends T> factory) {
-        source = type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new LazyInvocationHandler(factory)));
-    }
-
-    public T getSource() {
-        return source;
-    }
-
-    private static class LazyInvocationHandler implements InvocationHandler {
-        private Object target;
-        private final Factory<?> factory;
-
-        public LazyInvocationHandler(Factory<?> factory) {
-            this.factory = factory;
-        }
-
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-            if (target == null) {
-                target = factory.create();
-            }
-            try {
-                return method.invoke(target, args);
-            } catch (InvocationTargetException e) {
-                throw e.getCause();
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerBroadcast.java b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerBroadcast.java
index d84b4bd..fbc554d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerBroadcast.java
+++ b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerBroadcast.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2014 the original author 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,116 +16,11 @@
 
 package org.gradle.listener;
 
-import org.gradle.api.Action;
-import org.gradle.messaging.dispatch.Dispatch;
-import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.messaging.dispatch.ProxyDispatchAdapter;
-
 /**
- * <p>Manages a set of listeners of type T. Provides an implementation of T which can be used to broadcast to all
- * registered listeners.</p>
- *
- * <p>Ordering is maintained for events, so that events are delivered to listeners in the order they are generated.
- * Events are delivered to listeners in the order that listeners are added to this broadcaster.</p>
+ * DO NOT REMOVE.
  *
- * @param <T> The listener type.
+ * @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.
  */
-public class ListenerBroadcast<T> implements Dispatch<MethodInvocation> {
-    private final ProxyDispatchAdapter<T> source;
-    private final BroadcastDispatch<T> broadcast;
-    private final Class<T> type;
-
-    public ListenerBroadcast(Class<T> type) {
-        this.type = type;
-        broadcast = new BroadcastDispatch<T>(type);
-        source = new ProxyDispatchAdapter<T>(broadcast, type);
-    }
-
-    /**
-     * Returns the broadcaster. Any method call on this object is broadcast to all listeners.
-     *
-     * @return The broadcaster.
-     */
-    public T getSource() {
-        return source.getSource();
-    }
-
-    /**
-     * Returns the type of listener to which this class broadcasts.
-     *
-     * @return The type of the broadcaster.
-     */
-    public Class<T> getType() {
-        return type;
-    }
-
-    /**
-     * Adds a listener.
-     *
-     * @param listener The listener.
-     */
-    public void add(T listener) {
-        broadcast.add(listener);
-    }
-
-    /**
-     * Adds the given listeners.
-     *
-     * @param listeners The listeners
-     */
-    public void addAll(Iterable<? extends T> listeners) {
-        for (T listener : listeners) {
-            broadcast.add(listener);
-        }
-    }
-
-    /**
-     * Adds a {@link org.gradle.messaging.dispatch.Dispatch} to receive events from this broadcast.
-     */
-    public void add(Dispatch<MethodInvocation> dispatch) {
-        broadcast.add(dispatch);
-    }
-
-    /**
-     * Adds an action to be executed when the given method is called.
-     */
-    public void add(String methodName, Action<?> action) {
-        broadcast.add(methodName, action);
-    }
-
-    /**
-     * Removes the given listener.
-     *
-     * @param listener The listener.
-     */
-    public void remove(Object listener) {
-        broadcast.remove(listener);
-    }
-
-    /**
-     * Removes the given listeners.
-     *
-     * @param listeners The listeners
-     */
-    public void removeAll(Iterable<?> listeners) {
-        for (Object listener : listeners) {
-            remove(listener);
-        }
-    }
-
-    /**
-     * Removes all listeners.
-     */
-    public void removeAll() {
-        broadcast.removeAll();
-    }
-
-    /**
-     * Broadcasts the given event to all listeners.
-     *
-     * @param event The event
-     */
-    public void dispatch(MethodInvocation event) {
-        broadcast.dispatch(event);
-    }
+ at Deprecated
+public class ListenerBroadcast {
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerManager.java b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerManager.java
index d3f9990..204a9ec 100644
--- a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * Copyright 2014 the original author 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,86 +16,11 @@
 
 package org.gradle.listener;
 
-import groovy.lang.Closure;
-
 /**
- * Unified manager for all listeners for Gradle.  Provides a simple way to find all listeners of a given type in the
- * system.
+ * DO NOT REMOVE.
  *
- * While the methods work with any Object, in general only interfaces should be used as listener types.  Also, due to
- * implementation details, any listener method with a non-void return type will return a null.
+ * @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.
  */
+ at Deprecated
 public interface ListenerManager {
-    /**
-     * Added a listener.  A single object can implement multiple interfaces, and all interfaces are registered by a
-     * single invocation of this method.  There is no order dependency: if a broadcaster has already been made for type
-     * T, the listener will be registered with it if <code>(listener instanceof T)</code> returns true.
-     *
-     * @param listener the listener to add.
-     */
-    void addListener(Object listener);
-
-    /**
-     * Added a listener implemented via a Groovy closure.  The closure can implement a single method of the given
-     * listenerType class.  There is no order dependency: if a broadcaster has already been made for listenerType, the
-     * closure will be registered with it.
-     *
-     * @param listenerType The class of the listener type for which the closure is a method implementation.
-     * @param method The name of the method in the listenerType class for which the closure is an implementation.
-     * @param listenerClosure The closure containing the implementation of the listener method.
-     */
-    void addListener(Class<?> listenerType, String method, Closure listenerClosure);
-
-    /**
-     * Removes a listener.  A single object can implement multiple interfaces, and all interfaces are unregistered by a
-     * single invocation of this method.  There is no order dependency: if a broadcaster has already been made for type
-     * T, the listener will be unregistered with it if <code>(listener instanceof T)</code> returns true.
-     *
-     * @param listener the listener to remove.
-     */
-    void removeListener(Object listener);
-
-    /**
-     * Returns a broadcaster for the given listenerClass.  If there are no registered listeners for that type, a
-     * broadcaster is returned which does not forward method calls to any listeners.  The returned broadcasters are
-     * live, that is their list of listeners can be updated by calls to {@link #addListener(Object)} and {@link
-     * #removeListener(Object)} after they have been returned.  Broadcasters are also cached, so that repeatedly calling
-     * this method with the same listenerClass returns the same broadcaster object.
-     *
-     * @param listenerClass The type of listener for which to return a broadcaster.
-     * @return The broadcaster that forwards method calls to all listeners of the same type that have been (or will be)
-     *         registered with this manager.
-     */
-    <T> T getBroadcaster(Class<T> listenerClass);
-
-    /**
-     * Returns a broadcaster for the given listenerClass.  The returned broadcaster will delegate to the canonical
-     * broadcaster returned by {@link #getBroadcaster(Class)} for the given listener type.  However, it can also have
-     * listeners assigned/removed directly to/from it.  This allows these "anonymous" broadcasters to specialize what
-     * listeners receive messages.  Each call creates a new broadcaster, so that client code can create as many "facets"
-     * of the listener as they need.  The client code must provide some way for its users to register listeners on the
-     * specialized broadcasters.
-     *
-     * @param listenerClass The type of listener for which to create a broadcaster.
-     * @return A broadcaster that forwards method calls to all listeners assigned to it, or of the same type that have
-     *         been (or will be) registered with this manager.
-     */
-    <T> ListenerBroadcast<T> createAnonymousBroadcaster(Class<T> listenerClass);
-
-    /**
-     * Uses the given object as a logger. Each listener class has exactly one logger associated with it. Any existing
-     * logger for the listener class is discarded.
-     *
-     * @param logger The new logger to use.
-     */
-    void useLogger(Object logger);
-
-    /**
-     * Creates a child {@code ListenerManager}. All events broadcast in the child will be received by the listeners
-     * registered in the parent. However, the reverse is not true: events broadcast in the parent are not received
-     * by the listeners in the children. The child inherits the loggers of its parent, though these can be replaced.
-     *
-     * @return The child
-     */
-    ListenerManager createChild();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java b/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java
deleted file mode 100755
index 4f151f9..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/listener/ListenerNotificationException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.listener;
-
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
-import org.gradle.internal.exceptions.Contextual;
-
-/**
- * A {@code ListenerNotificationException} is thrown when a listener cannot be notified of an event.
- */
- at Contextual
-public class ListenerNotificationException extends AbstractMultiCauseException {
-    public ListenerNotificationException(String message, Iterable<? extends Throwable> causes) {
-        super(message, causes);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/ConsoleOutput.java b/subprojects/core/src/main/groovy/org/gradle/logging/ConsoleOutput.java
new file mode 100644
index 0000000..2daddba
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/ConsoleOutput.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Specifies how to treat color and dynamic console output.
+ */
+ at Incubating
+public enum ConsoleOutput {
+    /**
+     * Disable all color and rich output. Generate plain text only.
+     */
+    Plain,
+    /**
+     * Enable color and rich output when the current process is attached to a console, disable when not attached to a console.
+     */
+    Auto,
+    /**
+     * Enable color and rich output, regardless of whether the current process is attached to a console or not.
+     * When not attached to a console, the color and rich output is encoded using ANSI control characters.
+     */
+    Rich
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java
index b54b5cf..ed66d15 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.logging;
 
+import org.gradle.api.Incubating;
 import org.gradle.api.logging.LogLevel;
 
 import java.io.Serializable;
@@ -22,7 +23,7 @@ import java.io.Serializable;
 public class LoggingConfiguration implements Serializable {
     private LogLevel logLevel = LogLevel.LIFECYCLE;
     private ShowStacktrace showStacktrace = ShowStacktrace.INTERNAL_EXCEPTIONS;
-    private boolean colorOutput = true;
+    private ConsoleOutput consoleOutput = ConsoleOutput.Auto;
 
     public LogLevel getLogLevel() {
         return logLevel;
@@ -39,7 +40,7 @@ public class LoggingConfiguration implements Serializable {
      * @return true if logging output should be displayed in color.
      */
     public boolean isColorOutput() {
-        return colorOutput;
+        return consoleOutput != ConsoleOutput.Plain;
     }
 
     /**
@@ -48,7 +49,17 @@ public class LoggingConfiguration implements Serializable {
      * @param colorOutput true if logging output should be displayed in color.
      */
     public void setColorOutput(boolean colorOutput) {
-        this.colorOutput = colorOutput;
+        this.consoleOutput = colorOutput ? ConsoleOutput.Auto : ConsoleOutput.Plain;
+    }
+
+    @Incubating
+    public ConsoleOutput getConsoleOutput() {
+        return consoleOutput;
+    }
+
+    @Incubating
+    public void setConsoleOutput(ConsoleOutput colorOutput) {
+        this.consoleOutput = colorOutput;
     }
 
     public ShowStacktrace getShowStacktrace() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
index ad10d02..5dee8d3 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
@@ -16,14 +16,14 @@
 
 package org.gradle.logging;
 
-import org.gradle.internal.Actions;
 import org.gradle.cli.CommandLineConverter;
+import org.gradle.internal.Actions;
 import org.gradle.internal.Factory;
 import org.gradle.internal.TimeProvider;
 import org.gradle.internal.TrueTimeProvider;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.logging.internal.*;
-import org.gradle.logging.internal.logback.LogbackLoggingConfigurer;
+import org.gradle.logging.internal.slf4j.Slf4jLoggingConfigurer;
 
 /**
  * A {@link org.gradle.internal.service.ServiceRegistry} implementation that provides the logging services. To use this:
@@ -40,50 +40,35 @@ public abstract class LoggingServiceRegistry extends DefaultServiceRegistry {
     private TextStreamOutputEventListener stdoutListener;
 
     /**
-     * Creates a set of logging services which are suitable to use in a command-line process. In particular:
+     * Creates a set of logging services which are suitable to use globally in a process. In particular:
      *
      * <ul>
      *     <li>Replaces System.out and System.err with implementations that route output through the logging system.</li>
-     *     <li>Configures slf4j, logback, log4j and java util logging to route log messages through the logging system.</li>
-     *     <li>Routes logging output to the original System.out and System.err.</li>
+     *     <li>Configures slf4j, log4j and java util logging to route log messages through the logging system.</li>
+     *     <li>Routes logging output to the original System.out and System.err as per {@link LoggingManagerInternal#attachSystemOutAndErr()}.</li>
      * </ul>
      *
      * <p>Does nothing until started.</p>
      *
-     * <p>Allows dynamic and colored output to be written to the console. Use {@link LoggingManagerInternal#attachConsole(boolean)} to enable this.</p>
+     * <p>Allows dynamic and colored output to be written to the console. Use {@link LoggingManagerInternal#attachProcessConsole(boolean,boolean)} to enable this.</p>
      */
     public static LoggingServiceRegistry newCommandLineProcessLogging() {
         return new CommandLineLogging();
     }
 
     /**
-     * Creates a set of logging services which are suitable to use globally in a process. In particular:
-     *
-     * <ul>
-     *     <li>Replaces System.out and System.err with implementations that route output through the logging system.</li>
-     *     <li>Configures slf4j, logback, log4j and java util logging to route log messages through the logging system.</li>
-     *     <li>Routes logging output to the original System.out and System.err.</li>
-     * </ul>
-     *
-     * <p>Does nothing until started.</p>
-     */
-    public static LoggingServiceRegistry newProcessLogging() {
-        return new ChildProcessLogging();
-    }
-
-    /**
      * Creates a set of logging services which are suitable to use embedded in another application. In particular:
      *
      * <ul>
-     *     <li>Routes logging output to System.out and System.err.</li>
-     *     <li>Configures slf4j and logback.</li>
+     *     <li>Routes logging output to the original System.out and System.err as per {@link LoggingManagerInternal#attachSystemOutAndErr()}.</li>
+     *     <li>Configures slf4j and log4j to route log messages through the logging system.</li>
      * </ul>
      *
      * <p>Does not:</p>
      *
      * <ul>
      *     <li>Replace System.out and System.err to capture output written to these destinations.</li>
-     *     <li>Configure log4j or java util logging.</li>
+     *     <li>Configure java util logging.</li>
      * </ul>
      *
      * <p>Does nothing until started.</p>
@@ -93,9 +78,9 @@ public abstract class LoggingServiceRegistry extends DefaultServiceRegistry {
     }
 
     /**
-     * Creates a set of logging services to set up a new logging scope. Does not configure any static state.
+     * Creates a set of logging services to set up a new logging scope without an existing scope. Does not configure any state or route output to any destinations.
      */
-    public LoggingServiceRegistry newLogging() {
+    public static LoggingServiceRegistry newNestedLogging() {
         return new NestedLogging();
     }
 
@@ -126,30 +111,28 @@ public abstract class LoggingServiceRegistry extends DefaultServiceRegistry {
 
     protected OutputEventRenderer createOutputEventRenderer() {
         OutputEventRenderer renderer = new OutputEventRenderer(Actions.doNothing());
-        renderer.addStandardOutputAndError();
+        renderer.attachSystemOutAndErr();
         return renderer;
     }
 
-    private static class ChildProcessLogging extends LoggingServiceRegistry {
+    private static class CommandLineLogging extends LoggingServiceRegistry {
         protected Factory<LoggingManagerInternal> createLoggingManagerFactory() {
             OutputEventRenderer renderer = get(OutputEventRenderer.class);
-            // Configure logback and java util logging, and capture stdout and stderr
+            // Configure slf4j and java util logging, and capture stdout and stderr
             LoggingSystem stdout = new DefaultStdOutLoggingSystem(getStdoutListener(), get(TimeProvider.class));
             LoggingSystem stderr = new DefaultStdErrLoggingSystem(new TextStreamOutputEventListener(get(OutputEventListener.class)), get(TimeProvider.class));
             return new DefaultLoggingManagerFactory(
                     new DefaultLoggingConfigurer(renderer,
-                            new LogbackLoggingConfigurer(renderer),
+                            new Slf4jLoggingConfigurer(renderer),
                             new JavaUtilLoggingConfigurer()),
                     renderer,
                     stdout,
                     stderr);
         }
-    }
 
-    private static class CommandLineLogging extends ChildProcessLogging {
         protected OutputEventRenderer createOutputEventRenderer() {
             OutputEventRenderer renderer = new OutputEventRenderer(new ConsoleConfigureAction());
-            renderer.addStandardOutputAndError();
+            renderer.attachSystemOutAndErr();
             return renderer;
         }
     }
@@ -157,10 +140,10 @@ public abstract class LoggingServiceRegistry extends DefaultServiceRegistry {
     private static class EmbeddedLogging extends LoggingServiceRegistry {
         protected Factory<LoggingManagerInternal> createLoggingManagerFactory() {
             OutputEventRenderer renderer = get(OutputEventRenderer.class);
-            // Configure logback only
+            // Configure slf4j only
             return new DefaultLoggingManagerFactory(
                     new DefaultLoggingConfigurer(renderer,
-                            new LogbackLoggingConfigurer(renderer)),
+                            new Slf4jLoggingConfigurer(renderer)),
                     renderer,
                     new NoOpLoggingSystem(),
                     new NoOpLoggingSystem());
@@ -176,5 +159,9 @@ public abstract class LoggingServiceRegistry extends DefaultServiceRegistry {
                     new NoOpLoggingSystem(),
                     new NoOpLoggingSystem());
         }
+
+        protected OutputEventRenderer createOutputEventRenderer() {
+            return new OutputEventRenderer(Actions.doNothing());
+        }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutput.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutput.java
index 36d47b8..2e811c7 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutput.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutput.java
@@ -25,7 +25,7 @@ public abstract class AbstractLineChoppingStyledTextOutput extends AbstractStyle
     private int seenCharsFromEol;
 
     protected AbstractLineChoppingStyledTextOutput() {
-        eol = SystemProperties.getLineSeparator().toCharArray();
+        eol = SystemProperties.getInstance().getLineSeparator().toCharArray();
     }
 
     @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
index 912efa7..39a1f03 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
@@ -65,7 +65,7 @@ public abstract class AbstractStyledTextOutput implements StyledTextOutput, Stan
     }
 
     public StyledTextOutput println() {
-        text(SystemProperties.getLineSeparator());
+        text(SystemProperties.getInstance().getLineSeparator());
         return this;
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
index c783ad1..6007d80 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
@@ -31,13 +31,19 @@ public class AnsiConsole implements Console {
     private final TextAreaImpl textArea;
     private final Screen container;
     private final ColorMap colorMap;
+    private final boolean forceAnsi;
 
     public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap) {
+        this(target, flushable, colorMap, false);
+    }
+
+    public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap, boolean forceAnsi) {
         this.target = target;
         this.flushable = flushable;
         this.colorMap = colorMap;
         container = new Screen();
         textArea = new TextAreaImpl(container);
+        this.forceAnsi = forceAnsi;
     }
 
     public Label getStatusBar() {
@@ -69,7 +75,11 @@ public class AnsiConsole implements Console {
     }
 
     Ansi createAnsi() {
-        return Ansi.ansi();
+        if(forceAnsi) {
+            return new Ansi();
+        } else {
+            return Ansi.ansi();
+        }
     }
 
     private interface Container {
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleConfigureAction.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleConfigureAction.java
index dfdc7a4..62a83fb 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleConfigureAction.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ConsoleConfigureAction.java
@@ -16,33 +16,48 @@
 
 package org.gradle.logging.internal;
 
-import org.gradle.StartParameter;
 import org.gradle.api.Action;
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData;
-import org.gradle.internal.nativeplatform.console.ConsoleDetector;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.console.ConsoleDetector;
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData;
+import org.gradle.internal.nativeintegration.console.FallbackConsoleMetaData;
+import org.gradle.internal.nativeintegration.services.NativeServices;
+import org.gradle.logging.ConsoleOutput;
 
-import java.io.PrintStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 
 public class ConsoleConfigureAction implements Action<OutputEventRenderer> {
+
     public void execute(OutputEventRenderer renderer) {
-        StartParameter startParameter = new StartParameter();
-        NativeServices.initialize(startParameter.getGradleUserHomeDir());
+        ConsoleOutput consoleOutput = renderer.getConsoleOutput();
+        if (consoleOutput == ConsoleOutput.Plain) {
+            return;
+        }
+
         ConsoleDetector consoleDetector = NativeServices.getInstance().get(ConsoleDetector.class);
         ConsoleMetaData consoleMetaData = consoleDetector.getConsole();
+        boolean force = false;
         if (consoleMetaData == null) {
-            return;
+            if (consoleOutput == ConsoleOutput.Auto) {
+                return;
+            }
+            assert consoleOutput == ConsoleOutput.Rich;
+            consoleMetaData = new FallbackConsoleMetaData();
+            force = true;
         }
+
         boolean stdOutIsTerminal = consoleMetaData.isStdOut();
         boolean stdErrIsTerminal = consoleMetaData.isStdErr();
         if (stdOutIsTerminal) {
-            PrintStream outStr = new PrintStream(org.fusesource.jansi.AnsiConsole.wrapOutputStream(renderer.getOriginalStdOut()));
-            Console console = new AnsiConsole(outStr, outStr, renderer.getColourMap());
+            OutputStream originalStdOut = renderer.getOriginalStdOut();
+            OutputStreamWriter outStr = new OutputStreamWriter(force ? originalStdOut : org.fusesource.jansi.AnsiConsole.wrapOutputStream(originalStdOut));
+            Console console = new AnsiConsole(outStr, outStr, renderer.getColourMap(), force);
             renderer.addConsole(console, true, stdErrIsTerminal, consoleMetaData);
         } else if (stdErrIsTerminal) {
             // Only stderr is connected to a terminal
-            PrintStream errStr = new PrintStream(org.fusesource.jansi.AnsiConsole.wrapOutputStream(renderer.getOriginalStdErr()));
-            Console console = new AnsiConsole(errStr, errStr, renderer.getColourMap());
+            OutputStream originalStdErr = renderer.getOriginalStdErr();
+            OutputStreamWriter errStr = new OutputStreamWriter(force ? originalStdErr : org.fusesource.jansi.AnsiConsole.wrapOutputStream(originalStdErr));
+            Console console = new AnsiConsole(errStr, errStr, renderer.getColourMap(), force);
             renderer.addConsole(console, false, true, consoleMetaData);
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java
index d0a5fb1..4512af6 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java
@@ -23,17 +23,15 @@ import java.util.Map;
 
 import static org.fusesource.jansi.Ansi.Attribute;
 import static org.fusesource.jansi.Ansi.Attribute.*;
-import static org.fusesource.jansi.Ansi.Attribute.ITALIC;
-import static org.fusesource.jansi.Ansi.Color.*;
+import static org.fusesource.jansi.Ansi.Color.DEFAULT;
 import static org.gradle.logging.StyledTextOutput.Style.*;
-import static org.gradle.logging.StyledTextOutput.Style.Success;
+import static org.gradle.logging.StyledTextOutput.Style.Error;
 
 public class DefaultColorMap implements ColorMap {
     private static final String STATUSBAR = "statusbar";
     private static final String BOLD = "bold";
     private final Map<String, String> defaults = new HashMap<String, String>();
     private final Map<String, Color> colors = new HashMap<String, Color>();
-    private boolean useColor = true;
     private final Color noDecoration = new Color() {
         public void on(Ansi ansi) {
         }
@@ -63,10 +61,6 @@ public class DefaultColorMap implements ColorMap {
         defaults.put(style, color);
     }
 
-    public void setUseColor(boolean useColor) {
-        this.useColor = useColor;
-    }
-
     public Color getStatusBarColor() {
         return getColor(STATUSBAR);
     }
@@ -76,10 +70,6 @@ public class DefaultColorMap implements ColorMap {
     }
 
     private Color getColor(String style) {
-        if (!useColor) {
-            return noDecoration;
-        }
-
         Color color = colors.get(style);
         if (color == null) {
             color = createColor(style);
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 3f07a49..b76ce69 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
@@ -20,12 +20,15 @@ import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.logging.ConsoleOutput;
 import org.gradle.logging.LoggingManagerInternal;
 
+import java.io.Closeable;
+import java.io.OutputStream;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-public class DefaultLoggingManager implements LoggingManagerInternal {
+public class DefaultLoggingManager implements LoggingManagerInternal, Closeable {
     private boolean started;
     private final StartableLoggingSystem loggingSystem;
     private final StartableLoggingSystem stdOutLoggingSystem;
@@ -79,6 +82,10 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
         return this;
     }
 
+    public void close() {
+        stop();
+    }
+
     public DefaultLoggingManager setLevel(LogLevel logLevel) {
         loggingSystem.setLevel(logLevel);
         return this;
@@ -118,6 +125,14 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
         }
     }
 
+    public void addStandardOutputListener(OutputStream outputStream) {
+        addStandardOutputListener(new StreamBackedStandardOutputListener(outputStream));
+    }
+
+    public void addStandardErrorListener(OutputStream outputStream) {
+        addStandardErrorListener(new StreamBackedStandardOutputListener(outputStream));
+    }
+
     public void removeStandardOutputListener(StandardOutputListener listener) {
         if (stdoutListeners.remove(listener) && started) {
             loggingOutput.removeStandardOutputListener(listener);
@@ -142,12 +157,20 @@ public class DefaultLoggingManager implements LoggingManagerInternal {
         }
     }
 
-    public void attachConsole(boolean colorOutput) {
-        loggingOutput.attachConsole(colorOutput);
+    public void removeAllOutputEventListeners() {
+        loggingOutput.removeAllOutputEventListeners();
+    }
+
+    public void attachProcessConsole(ConsoleOutput consoleOutput) {
+        loggingOutput.attachProcessConsole(consoleOutput);
+    }
+
+    public void attachAnsiConsole(OutputStream outputStream) {
+        loggingOutput.attachAnsiConsole(outputStream);
     }
 
-    public void addStandardOutputAndError() {
-        loggingOutput.addStandardOutputAndError();
+    public void attachSystemOutAndErr() {
+        loggingOutput.attachSystemOutAndErr();
     }
 
     private static class StartableLoggingSystem implements Stoppable {
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultStatusBarFormatter.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultStatusBarFormatter.java
index d73c31e..51a4fb8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultStatusBarFormatter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/DefaultStatusBarFormatter.java
@@ -16,7 +16,7 @@
 
 package org.gradle.logging.internal;
 
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData;
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData;
 import org.gradle.logging.internal.progress.ProgressOperation;
 
 public class DefaultStatusBarFormatter {
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 9ba8e82..753a2b1 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
@@ -22,13 +22,11 @@ import org.gradle.cli.AbstractCommandLineConverter;
 import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
+import org.gradle.logging.ConsoleOutput;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.ShowStacktrace;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 public class LoggingCommandLineConverter extends AbstractCommandLineConverter<LoggingConfiguration> {
     public static final String DEBUG = "d";
@@ -38,12 +36,14 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
     public static final String QUIET = "q";
     public static final String QUIET_LONG = "quiet";
     public static final String NO_COLOR = "no-color";
+    public static final String CONSOLE = "console";
     public static final String FULL_STACKTRACE = "S";
     public static final String FULL_STACKTRACE_LONG = "full-stacktrace";
     public static final String STACKTRACE = "s";
     public static final String STACKTRACE_LONG = "stacktrace";
     private final BiMap<String, LogLevel> logLevelMap = HashBiMap.create();
     private final BiMap<String, ShowStacktrace> showStacktraceMap = HashBiMap.create();
+    private final Map<String, ConsoleOutput> consoleOutputMap = new HashMap<String, ConsoleOutput>();
 
     public LoggingCommandLineConverter() {
         logLevelMap.put(QUIET, LogLevel.QUIET);
@@ -51,11 +51,9 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
         logLevelMap.put(DEBUG, LogLevel.DEBUG);
         showStacktraceMap.put(FULL_STACKTRACE, ShowStacktrace.ALWAYS_FULL);
         showStacktraceMap.put(STACKTRACE, ShowStacktrace.ALWAYS);
-    }
-
-    @Override
-    protected LoggingConfiguration newInstance() {
-        return new LoggingConfiguration();
+        consoleOutputMap.put("plain", ConsoleOutput.Plain);
+        consoleOutputMap.put("auto", ConsoleOutput.Auto);
+        consoleOutputMap.put("rich", ConsoleOutput.Rich);
     }
 
     public LoggingConfiguration convert(ParsedCommandLine commandLine, LoggingConfiguration loggingConfiguration) throws CommandLineArgumentException {
@@ -72,7 +70,16 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
         }
 
         if (commandLine.hasOption(NO_COLOR)) {
-            loggingConfiguration.setColorOutput(false);
+            loggingConfiguration.setConsoleOutput(ConsoleOutput.Plain);
+        }
+
+        if (commandLine.hasOption(CONSOLE)) {
+            String value = commandLine.option(CONSOLE).getValue();
+            ConsoleOutput colorOutput = consoleOutputMap.get(value.toLowerCase());
+            if (colorOutput == null) {
+                throw new CommandLineArgumentException(String.format("Unrecognized value '%s' for %s.", value, CONSOLE));
+            }
+            loggingConfiguration.setConsoleOutput(colorOutput);
         }
 
         return loggingConfiguration;
@@ -84,7 +91,9 @@ public class LoggingCommandLineConverter extends AbstractCommandLineConverter<Lo
         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(NO_COLOR).deprecated("use --console=plain instead").hasDescription("Do not use color in the console output.");
+        parser.option(CONSOLE).hasArgument().hasDescription("Specifies which type of console output to generate. Values are 'plain', 'auto' (default) or 'rich'.");
+        parser.allowOneOf(NO_COLOR, CONSOLE);
 
         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.");
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java
index b82c628..a70bf8a 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java
@@ -16,19 +16,56 @@
 package org.gradle.logging.internal;
 
 import org.gradle.api.logging.LoggingOutput;
+import org.gradle.logging.ConsoleOutput;
+
+import java.io.OutputStream;
 
 public interface LoggingOutputInternal extends LoggingOutput {
     /**
-     * Add standard output and error as logging destinations.
+     * Adds System.out and System.err as logging destinations. The output will include plain text only, with no color or dynamic text.
      */
-    void addStandardOutputAndError();
+    void attachSystemOutAndErr();
 
     /**
-     * Adds the console as logging destination, if available.
+     * Adds the current processes' stdout and stderr as logging destinations. The output will also include color and dynamic text when one of these
+     * is connected to a console.
+     *
+     * <p>Removes standard output and/or error as a side-effect.
      */
-    void attachConsole(boolean colorOutput);
+    void attachProcessConsole(ConsoleOutput consoleOutput);
 
+    /**
+     * Adds the given {@link java.io.OutputStream} as a logging destination. The stream receives stdout and stderr logging formatted according to the current logging settings
+     * and encoded using the system character encoding. The output also includes color and dynamic text encoded using ANSI control sequences.
+     *
+     * <p>Removes standard output and/or error as a side-effect.
+     */
+    void attachAnsiConsole(OutputStream outputStream);
+
+    /**
+     * Adds the given {@link java.io.OutputStream} as a logging destination. The stream receives stdout logging formatted according to the current logging settings and
+     * encoded using the system character encoding.
+     */
+    void addStandardOutputListener(OutputStream outputStream);
+
+    /**
+     * Adds the given {@link java.io.OutputStream} as a logging destination. The stream receives stderr logging formatted according to the current logging settings and
+     * encoded using the system character encoding.
+     */
+    void addStandardErrorListener(OutputStream outputStream);
+
+    /**
+     * Adds the given listener as a logging destination.
+     */
     void addOutputEventListener(OutputEventListener listener);
 
+    /**
+     * Adds the given listener.
+     */
     void removeOutputEventListener(OutputEventListener listener);
+
+    /**
+     * Removes all non-standard output event listeners (also the ones attached with attachConsole)
+     */
+    void removeAllOutputEventListeners();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
index 18f913a..cd4c3d8 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
@@ -19,10 +19,13 @@ import net.jcip.annotations.ThreadSafe;
 import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData;
-import org.gradle.listener.ListenerBroadcast;
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData;
+import org.gradle.internal.nativeintegration.console.FallbackConsoleMetaData;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.logging.ConsoleOutput;
 
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 
 /**
  * A {@link org.gradle.logging.internal.OutputEventListener} implementation which renders output events to various
@@ -30,6 +33,7 @@ import java.io.OutputStream;
  */
 @ThreadSafe
 public class OutputEventRenderer implements OutputEventListener, LoggingConfigurer, LoggingOutputInternal {
+    private final ListenerBroadcast<OutputEventListener> stdOutAndErrorFormatters = new ListenerBroadcast<OutputEventListener>(OutputEventListener.class);
     private final ListenerBroadcast<OutputEventListener> formatters = new ListenerBroadcast<OutputEventListener>(OutputEventListener.class);
     private final ListenerBroadcast<StandardOutputListener> stdoutListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
     private final ListenerBroadcast<StandardOutputListener> stderrListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
@@ -41,12 +45,13 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
     private OutputStream originalStdErr;
     private StreamBackedStandardOutputListener stdOutListener;
     private StreamBackedStandardOutputListener stdErrListener;
+    private ConsoleOutput consoleOutput;
 
     public OutputEventRenderer(Action<? super OutputEventRenderer> consoleConfigureAction) {
         OutputEventListener stdOutChain = onNonError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stdoutListeners.getSource())), false));
-        formatters.add(stdOutChain);
+        stdOutAndErrorFormatters.add(stdOutChain);
         OutputEventListener stdErrChain = onError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stderrListeners.getSource())), false));
-        formatters.add(stdErrChain);
+        stdOutAndErrorFormatters.add(stdErrChain);
         this.consoleConfigureAction = consoleConfigureAction;
     }
 
@@ -54,6 +59,10 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
         return colourMap;
     }
 
+    public ConsoleOutput getConsoleOutput() {
+        return consoleOutput;
+    }
+
     public OutputStream getOriginalStdOut() {
         return originalStdOut;
     }
@@ -62,30 +71,87 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
         return originalStdErr;
     }
 
-    public void attachConsole(boolean colorOutput) {
+    public void attachProcessConsole(ConsoleOutput consoleOutput) {
         synchronized (lock) {
-            colourMap.setUseColor(colorOutput);
+            this.consoleOutput = consoleOutput;
             consoleConfigureAction.execute(this);
         }
     }
 
-    public void addStandardOutputAndError() {
+    public void attachAnsiConsole(OutputStream outputStream) {
+        synchronized (lock) {
+            OutputStreamWriter writer = new OutputStreamWriter(outputStream);
+            Console console = new AnsiConsole(writer, writer, colourMap, true);
+            addConsole(console, true, true, new FallbackConsoleMetaData());
+        }
+    }
+
+    public void attachSystemOutAndErr() {
+        addStandardOutputListener();
+        addStandardErrorListener();
+    }
+
+    private void addStandardOutputListener() {
         synchronized (lock) {
             originalStdOut = System.out;
-            originalStdErr = System.err;
+            if (stdOutListener != null) {
+                stdoutListeners.remove(stdOutListener);
+            }
             stdOutListener = new StreamBackedStandardOutputListener((Appendable) System.out);
-            stdErrListener = new StreamBackedStandardOutputListener((Appendable) System.err);
             addStandardOutputListener(stdOutListener);
+        }
+    }
+
+    private void addStandardErrorListener() {
+        synchronized (lock) {
+            originalStdErr = System.err;
+            if(stdErrListener != null) {
+                stderrListeners.remove(stdErrListener);
+            }
+            stdErrListener = new StreamBackedStandardOutputListener((Appendable) System.err);
             addStandardErrorListener(stdErrListener);
         }
     }
 
+    public void removeStandardOutputAndError() {
+        removeStandardOutputListener();
+        removeStandardErrorListener();
+    }
+
+    private void removeStandardOutputListener() {
+        synchronized (lock) {
+            if (stdOutListener != null) {
+                stdoutListeners.remove(stdOutListener);
+                stdOutListener = null;
+            }
+        }
+    }
+
+    private void removeStandardErrorListener() {
+        synchronized (lock) {
+            if(stdErrListener != null) {
+                stderrListeners.remove(stdErrListener);
+                stdErrListener = null;
+            }
+        }
+    }
+
     public void addOutputEventListener(OutputEventListener listener) {
-        formatters.add(listener);
+        synchronized (lock) {
+            formatters.add(listener);
+        }
     }
 
     public void removeOutputEventListener(OutputEventListener listener) {
-        formatters.remove(listener);
+        synchronized (lock) {
+            formatters.remove(listener);
+        }
+    }
+
+    public void removeAllOutputEventListeners() {
+        synchronized (lock) {
+            formatters.removeAll();
+        }
     }
 
     public OutputEventRenderer addConsole(Console console, boolean stdout, boolean stderr, ConsoleMetaData consoleMetaData) {
@@ -97,14 +163,13 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
         synchronized (lock) {
             if (stdout && stderr) {
                 formatters.add(consoleChain);
-                stdoutListeners.remove(this.stdOutListener);
-                stderrListeners.remove(this.stdErrListener);
+                removeStandardOutputAndError();
             } else if (stdout) {
                 formatters.add(onNonError(consoleChain));
-                stdoutListeners.remove(this.stdOutListener);
+                removeStandardOutputListener();
             } else {
                 formatters.add(onError(consoleChain));
-                stderrListeners.remove(this.stdErrListener);
+                removeStandardErrorListener();
             }
             consoleChain.onOutput(new LogLevelChangeEvent(logLevel));
         }
@@ -143,6 +208,15 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
         }
     }
 
+    public void addStandardOutputListener(OutputStream outputStream) {
+        addStandardOutputListener(new StreamBackedStandardOutputListener(outputStream));
+    }
+
+    public void addStandardErrorListener(OutputStream outputStream) {
+        addStandardErrorListener(new StreamBackedStandardOutputListener(outputStream));
+    }
+
+
     public void removeStandardOutputListener(StandardOutputListener listener) {
         synchronized (lock) {
             stdoutListeners.remove(listener);
@@ -172,6 +246,7 @@ public class OutputEventRenderer implements OutputEventListener, LoggingConfigur
                 }
                 this.logLevel = newLogLevel;
             }
+            stdOutAndErrorFormatters.getSource().onOutput(event);
             formatters.getSource().onOutput(event);
         }
     }
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
index e05ed85..68230aa 100644
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
@@ -28,7 +28,7 @@ import static org.gradle.logging.StyledTextOutput.Style;
  * progress of operations.
  */
 public class ProgressLogEventGenerator implements OutputEventListener {
-    private static final String EOL = SystemProperties.getLineSeparator();
+    private static final String EOL = SystemProperties.getInstance().getLineSeparator();
 
     private final OutputEventListener listener;
     private final boolean deferHeader;
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogLevelConverter.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogLevelConverter.java
deleted file mode 100644
index 3e13524..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogLevelConverter.java
+++ /dev/null
@@ -1,70 +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.logging.internal.logback;
-
-import ch.qos.logback.classic.Level;
-import org.gradle.api.Nullable;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.Logging;
-import org.slf4j.Marker;
-
-public class LogLevelConverter {
-    /**
-     * Maps a Logback log level and optional marker to a Gradle log level.
-     * Returns null if there is no equivalent Gradle log level (such as for TRACE).
-     */
-    @Nullable
-    public static LogLevel toGradleLogLevel(Level level, @Nullable Marker marker) {
-        switch(level.toInt()) {
-            case Level.TRACE_INT:
-                return null;
-            case Level.DEBUG_INT:
-                return LogLevel.DEBUG;
-            case Level.INFO_INT:
-                if (marker == Logging.LIFECYCLE) {
-                    return LogLevel.LIFECYCLE;
-                }
-                if (marker == Logging.QUIET) {
-                    return LogLevel.QUIET;
-                }
-                return LogLevel.INFO;
-            case Level.WARN_INT:
-                return LogLevel.WARN;
-            case Level.ERROR_INT:
-                return LogLevel.ERROR;
-            default:
-                throw new IllegalArgumentException("Don't know how to map Logback log level '" + level + "' to a Gradle log level");
-        }
-    }
-
-    public static Level toLogbackLevel(LogLevel level) {
-        switch (level) {
-            case DEBUG:
-                return Level.DEBUG;
-            case INFO:
-            case LIFECYCLE:
-            case QUIET:
-                return Level.INFO;
-            case WARN:
-                return Level.WARN;
-            case ERROR:
-                return Level.ERROR;
-            default:
-                throw new IllegalArgumentException("Don't know how to map Gradle log level '" + level + "' to a Logback log level");
-        }
-    }
-}
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
deleted file mode 100644
index 4eba18f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurer.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging.internal.logback;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.PatternLayout;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.classic.spi.ThrowableProxy;
-import ch.qos.logback.classic.turbo.TurboFilter;
-import ch.qos.logback.core.AppenderBase;
-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.LogEvent;
-import org.gradle.logging.internal.LoggingConfigurer;
-import org.gradle.logging.internal.OutputEventListener;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-
-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}.
- */
-public class LogbackLoggingConfigurer implements LoggingConfigurer {
-    private final OutputEventListener outputEventListener;
-    private final PrintStream defaultStandardOut = System.out;
-
-    private LogLevel currentLevel;
-
-    public LogbackLoggingConfigurer(OutputEventListener outputListener) {
-        outputEventListener = outputListener;
-    }
-
-    public void configure(LogLevel logLevel) {
-        if (logLevel == currentLevel) {
-            return;
-        }
-
-        try {
-            doConfigure(logLevel);
-        } catch (Throwable e) {
-            doFailSafeConfiguration();
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    private void doConfigure(LogLevel logLevel) {
-        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
-        Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
-
-        if (currentLevel == null) {
-            context.reset();
-            context.addTurboFilter(new GradleFilter());
-            context.getLogger("org.apache.http.wire").setLevel(Level.OFF);
-            GradleAppender appender = new GradleAppender();
-            appender.setContext(context);
-            appender.start();
-            rootLogger.addAppender(appender);
-        }
-
-        currentLevel = logLevel;
-        rootLogger.setLevel(LogLevelConverter.toLogbackLevel(logLevel));
-    }
-
-    private void doFailSafeConfiguration() {
-        // Not really fail-safe, just less likely to fail
-        final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
-        context.reset();
-        Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
-        rootLogger.setLevel(Level.INFO);
-
-        ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
-        rootLogger.addAppender(appender);
-        appender.setContext(context);
-        appender.setTarget("System.err");
-
-        PatternLayout layout = new PatternLayout();
-        appender.setLayout(layout);
-        layout.setPattern("%msg%n%ex");
-        layout.setContext(context);
-
-        layout.start();
-        appender.start();
-    }
-
-    private class GradleFilter extends TurboFilter {
-        @Override
-        public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
-            Level loggerLevel = logger.getEffectiveLevel();
-            if (loggerLevel == Level.INFO && (level == Level.INFO || level == Level.WARN)
-                    || level == Level.INFO && (loggerLevel == Level.INFO || loggerLevel == Level.WARN)) {
-                // Need to take into account Gradle's LIFECYCLE and QUIET markers. Whether those are set can only be determined
-                // for the global log level, but not for the logger's log level (at least not without walking the logger's
-                // hierarchy, which is something that Logback is designed to avoid for performance reasons).
-                // Hence we base our decision on the global log level.
-                LogLevel eventLevel = LogLevelConverter.toGradleLogLevel(level, marker);
-                return eventLevel.compareTo(currentLevel) >= 0 ? FilterReply.ACCEPT : FilterReply.DENY;
-            }
-
-            return level.isGreaterOrEqual(loggerLevel) ? FilterReply.ACCEPT : FilterReply.DENY;
-        }
-    }
-
-    private class GradleAppender extends AppenderBase<ILoggingEvent> {
-        @Override
-        protected void append(ILoggingEvent event) {
-            try {
-                ThrowableProxy throwableProxy = (ThrowableProxy) event.getThrowableProxy();
-                Throwable throwable = throwableProxy == null ? null : throwableProxy.getThrowable();
-                String message = event.getFormattedMessage();
-                LogLevel level = LogLevelConverter.toGradleLogLevel(event.getLevel(), event.getMarker());
-                outputEventListener.onOutput(new LogEvent(event.getTimeStamp(), event.getLoggerName(), level, message, throwable));
-            } catch (Throwable t) {
-                // fall back to standard out
-                t.printStackTrace(defaultStandardOut);
-            }
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLogger.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLogger.java
new file mode 100644
index 0000000..6f93de6
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLogger.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.TimeProvider;
+import org.gradle.logging.internal.LogEvent;
+import org.gradle.logging.internal.OutputEventListener;
+import org.slf4j.Marker;
+import org.slf4j.helpers.MessageFormatter;
+
+public class OutputEventListenerBackedLogger implements Logger {
+
+    private final String name;
+    private final OutputEventListenerBackedLoggerContext context;
+    private final TimeProvider timeProvider;
+
+    public OutputEventListenerBackedLogger(String name, OutputEventListenerBackedLoggerContext context, TimeProvider timeProvider) {
+        this.name = name;
+        this.context = context;
+        this.timeProvider = timeProvider;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    private boolean isLevelAtMost(LogLevel levelLimit) {
+        return levelLimit.compareTo(context.getLevel()) >= 0;
+    }
+
+    public boolean isTraceEnabled() {
+        return false;
+    }
+
+    public boolean isTraceEnabled(Marker marker) {
+        return isTraceEnabled();
+    }
+
+    public boolean isDebugEnabled() {
+        return isLevelAtMost(LogLevel.DEBUG);
+    }
+
+    public boolean isDebugEnabled(Marker marker) {
+        return isDebugEnabled();
+    }
+
+    public boolean isInfoEnabled() {
+        return isLevelAtMost(LogLevel.INFO);
+    }
+
+    public boolean isInfoEnabled(Marker marker) {
+        return isLevelAtMost(toLogLevel(marker));
+    }
+
+    public boolean isWarnEnabled() {
+        return isLevelAtMost(LogLevel.WARN);
+    }
+
+    public boolean isWarnEnabled(Marker marker) {
+        return isWarnEnabled();
+    }
+
+    public boolean isErrorEnabled() {
+        return isLevelAtMost(LogLevel.ERROR);
+    }
+
+    public boolean isErrorEnabled(Marker marker) {
+        return isErrorEnabled();
+    }
+
+    @Override
+    public boolean isLifecycleEnabled() {
+        return isLevelAtMost(LogLevel.LIFECYCLE);
+    }
+
+    @Override
+    public boolean isQuietEnabled() {
+        return isLevelAtMost(LogLevel.QUIET);
+    }
+
+    public void trace(String msg) {
+    }
+
+    public void trace(String format, Object arg) {
+    }
+
+    public void trace(String format, Object arg1, Object arg2) {
+    }
+
+    public void trace(String format, Object... arguments) {
+    }
+
+    public void trace(String msg, Throwable t) {
+    }
+
+    public void trace(Marker marker, String msg) {
+    }
+
+    public void trace(Marker marker, String format, Object arg) {
+    }
+
+    public void trace(Marker marker, String format, Object arg1, Object arg2) {
+    }
+
+    public void trace(Marker marker, String format, Object... argArray) {
+    }
+
+    public void trace(Marker marker, String msg, Throwable t) {
+    }
+
+    private void log(LogLevel logLevel, Throwable throwable, String message) {
+        LogEvent logEvent = new LogEvent(timeProvider.getCurrentTime(), name, logLevel, message, throwable);
+        OutputEventListener outputEventListener = context.getOutputEventListener();
+        try {
+            outputEventListener.onOutput(logEvent);
+        } catch (Throwable e) {
+            // fall back to standard out
+            e.printStackTrace(System.out);
+        }
+    }
+
+    private void log(LogLevel logLevel, Throwable throwable, String format, Object arg) {
+        log(logLevel, throwable, MessageFormatter.format(format, arg).getMessage());
+    }
+
+    private void log(LogLevel logLevel, Throwable throwable, String format, Object arg1, Object arg2) {
+        log(logLevel, throwable, MessageFormatter.format(format, arg1, arg2).getMessage());
+    }
+
+    private void log(LogLevel logLevel, Throwable throwable, String format, Object[] args) {
+        log(logLevel, throwable, MessageFormatter.arrayFormat(format, args).getMessage());
+    }
+
+    public void debug(String message) {
+        if (isDebugEnabled()) {
+            log(LogLevel.DEBUG, null, message);
+        }
+    }
+
+    public void debug(String format, Object arg) {
+        if (isDebugEnabled()) {
+            log(LogLevel.DEBUG, null, format, arg);
+        }
+    }
+
+    public void debug(String format, Object arg1, Object arg2) {
+        if (isDebugEnabled()) {
+            log(LogLevel.DEBUG, null, format, arg1, arg2);
+        }
+    }
+
+    public void debug(String format, Object... arguments) {
+        if (isDebugEnabled()) {
+            log(LogLevel.DEBUG, null, format, arguments);
+        }
+    }
+
+    public void debug(String msg, Throwable t) {
+        if (isDebugEnabled()) {
+            log(LogLevel.DEBUG, t, msg);
+        }
+    }
+
+    public void debug(Marker marker, String msg) {
+        if (isDebugEnabled(marker)) {
+            log(LogLevel.DEBUG, null, msg);
+        }
+    }
+
+    public void debug(Marker marker, String format, Object arg) {
+        if (isDebugEnabled(marker)) {
+            log(LogLevel.DEBUG, null, format, arg);
+        }
+    }
+
+    public void debug(Marker marker, String format, Object arg1, Object arg2) {
+        if (isDebugEnabled(marker)) {
+            log(LogLevel.DEBUG, null, format, arg1, arg2);
+        }
+    }
+
+    public void debug(Marker marker, String format, Object... argArray) {
+        if (isDebugEnabled(marker)) {
+            log(LogLevel.DEBUG, null, format, argArray);
+        }
+    }
+
+    public void debug(Marker marker, String msg, Throwable t) {
+        if (isDebugEnabled(marker)) {
+            log(LogLevel.DEBUG, t, msg);
+        }
+    }
+
+    public void info(String message) {
+        if (isInfoEnabled()) {
+            log(LogLevel.INFO, null, message);
+        }
+    }
+
+    public void info(String format, Object arg) {
+        if (isInfoEnabled()) {
+            log(LogLevel.INFO, null, format, arg);
+        }
+    }
+
+    public void info(String format, Object arg1, Object arg2) {
+        if (isInfoEnabled()) {
+            log(LogLevel.INFO, null, format, arg1, arg2);
+        }
+    }
+
+    public void info(String format, Object... arguments) {
+        if (isInfoEnabled()) {
+            log(LogLevel.INFO, null, format, arguments);
+        }
+    }
+
+    @Override
+    public void lifecycle(String message) {
+        if (isLifecycleEnabled()) {
+            log(LogLevel.LIFECYCLE, null, message);
+        }
+    }
+
+    @Override
+    public void lifecycle(String message, Object... objects) {
+        if (isLifecycleEnabled()) {
+            log(LogLevel.LIFECYCLE, null, message, objects);
+        }
+    }
+
+    @Override
+    public void lifecycle(String message, Throwable throwable) {
+        if (isLifecycleEnabled()) {
+            log(LogLevel.LIFECYCLE, throwable, message);
+        }
+    }
+
+
+    @Override
+    public void quiet(String message) {
+        if (isQuietEnabled()) {
+            log(LogLevel.QUIET, null, message);
+        }
+    }
+
+    @Override
+    public void quiet(String message, Object... objects) {
+        if (isQuietEnabled()) {
+            log(LogLevel.QUIET, null, message, objects);
+        }
+    }
+
+    @Override
+    public void quiet(String message, Throwable throwable) {
+        if (isQuietEnabled()) {
+            log(LogLevel.QUIET, throwable, message);
+        }
+    }
+
+    @Override
+    public boolean isEnabled(LogLevel level) {
+        return isLevelAtMost(level);
+    }
+
+    @Override
+    public void log(LogLevel level, String message) {
+        if (isEnabled(level)) {
+            log(level, null, message);
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String message, Object... objects) {
+        if (isEnabled(level)) {
+            log(level, null, message, objects);
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String message, Throwable throwable) {
+        if (isEnabled(level)) {
+            log(level, throwable, message);
+        }
+    }
+
+    public void info(String msg, Throwable t) {
+        if (isInfoEnabled()) {
+            log(LogLevel.INFO, t, msg);
+        }
+    }
+
+    private LogLevel toLogLevel(Marker marker) {
+        if (marker == null) {
+            return LogLevel.INFO;
+        }
+        if (marker == Logging.LIFECYCLE) {
+            return LogLevel.LIFECYCLE;
+        }
+        if (marker == Logging.QUIET) {
+            return LogLevel.QUIET;
+        }
+        return LogLevel.INFO;
+    }
+
+    public void info(Marker marker, String msg) {
+        if (isInfoEnabled(marker)) {
+            log(toLogLevel(marker), null, msg);
+        }
+    }
+
+    public void info(Marker marker, String format, Object arg) {
+        if (isInfoEnabled(marker)) {
+            log(toLogLevel(marker), null, format, arg);
+        }
+    }
+
+    public void info(Marker marker, String format, Object arg1, Object arg2) {
+        if (isInfoEnabled(marker)) {
+            log(toLogLevel(marker), null, format, arg1, arg2);
+        }
+    }
+
+    public void info(Marker marker, String format, Object... argArray) {
+        if (isInfoEnabled(marker)) {
+            log(toLogLevel(marker), null, format, argArray);
+        }
+    }
+
+    public void info(Marker marker, String msg, Throwable t) {
+        if (isInfoEnabled(marker)) {
+            log(toLogLevel(marker), t, msg);
+        }
+    }
+
+    public void warn(String message) {
+        if (isWarnEnabled()) {
+            log(LogLevel.WARN, null, message);
+        }
+    }
+
+    public void warn(String format, Object arg) {
+        if (isWarnEnabled()) {
+            log(LogLevel.WARN, null, format, arg);
+        }
+    }
+
+    public void warn(String format, Object arg1, Object arg2) {
+        if (isWarnEnabled()) {
+            log(LogLevel.WARN, null, format, arg1, arg2);
+        }
+    }
+
+    public void warn(String format, Object... arguments) {
+        if (isWarnEnabled()) {
+            log(LogLevel.WARN, null, format, arguments);
+        }
+    }
+
+    public void warn(String msg, Throwable t) {
+        if (isWarnEnabled()) {
+            log(LogLevel.WARN, t, msg);
+        }
+    }
+
+    public void warn(Marker marker, String msg) {
+        if (isWarnEnabled(marker)) {
+            log(LogLevel.WARN, null, msg);
+        }
+    }
+
+    public void warn(Marker marker, String format, Object arg) {
+        if (isWarnEnabled(marker)) {
+            log(LogLevel.WARN, null, format, arg);
+        }
+    }
+
+    public void warn(Marker marker, String format, Object arg1, Object arg2) {
+        if (isWarnEnabled(marker)) {
+            log(LogLevel.WARN, null, format, arg1, arg2);
+        }
+    }
+
+    public void warn(Marker marker, String format, Object... argArray) {
+        if (isWarnEnabled(marker)) {
+            log(LogLevel.WARN, null, format, argArray);
+        }
+    }
+
+    public void warn(Marker marker, String msg, Throwable t) {
+        if (isWarnEnabled(marker)) {
+            log(LogLevel.WARN, t, msg);
+        }
+    }
+
+    public void error(String message) {
+        if (isErrorEnabled()) {
+            log(LogLevel.ERROR, null, message);
+        }
+    }
+
+    public void error(String format, Object arg) {
+        if (isErrorEnabled()) {
+            log(LogLevel.ERROR, null, format, arg);
+        }
+    }
+
+    public void error(String format, Object arg1, Object arg2) {
+        if (isErrorEnabled()) {
+            log(LogLevel.ERROR, null, format, arg1, arg2);
+        }
+    }
+
+    public void error(String format, Object... arguments) {
+        if (isErrorEnabled()) {
+            log(LogLevel.ERROR, null, format, arguments);
+        }
+    }
+
+    public void error(String msg, Throwable t) {
+        if (isErrorEnabled()) {
+            log(LogLevel.ERROR, t, msg);
+        }
+    }
+
+    public void error(Marker marker, String msg) {
+        if (isErrorEnabled(marker)) {
+            log(LogLevel.ERROR, null, msg);
+        }
+    }
+
+    public void error(Marker marker, String format, Object arg) {
+        if (isErrorEnabled(marker)) {
+            log(LogLevel.ERROR, null, format, arg);
+        }
+    }
+
+    public void error(Marker marker, String format, Object arg1, Object arg2) {
+        if (isErrorEnabled(marker)) {
+            log(LogLevel.ERROR, null, format, arg1, arg2);
+        }
+    }
+
+    public void error(Marker marker, String format, Object... argArray) {
+        if (isErrorEnabled(marker)) {
+            log(LogLevel.ERROR, null, format, argArray);
+        }
+    }
+
+    public void error(Marker marker, String msg, Throwable t) {
+        if (isErrorEnabled(marker)) {
+            log(LogLevel.ERROR, t, msg);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContext.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContext.java
new file mode 100644
index 0000000..69109d2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContext.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.internal.Actions;
+import org.gradle.internal.TimeProvider;
+import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.logging.internal.OutputEventRenderer;
+import org.slf4j.ILoggerFactory;
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+
+import java.io.OutputStream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class OutputEventListenerBackedLoggerContext implements ILoggerFactory {
+
+    private static final LogLevel DEFAULT_LOG_LEVEL = LogLevel.LIFECYCLE;
+
+    static final String HTTP_CLIENT_WIRE_LOGGER_NAME = "org.apache.http.wire";
+    static final String META_INF_EXTENSION_MODULE_LOGGER_NAME = "org.codehaus.groovy.runtime.m12n.MetaInfExtensionModule";
+
+    private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
+    private final OutputStream defaultOutputStream;
+    private final OutputStream defaultErrorStream;
+    private final AtomicReference<LogLevel> level = new AtomicReference<LogLevel>();
+    private final AtomicReference<OutputEventListener> outputEventListener = new AtomicReference<OutputEventListener>();
+    private final TimeProvider timeProvider;
+
+    public OutputEventListenerBackedLoggerContext(OutputStream defaultOutputStream, OutputStream defaultErrorStream, TimeProvider timeProvider) {
+        this.defaultOutputStream = defaultOutputStream;
+        this.defaultErrorStream = defaultErrorStream;
+        this.timeProvider = timeProvider;
+        applyDefaultLoggersConfig();
+        reset();
+    }
+
+    private void applyDefaultLoggersConfig() {
+        addNoOpLogger(HTTP_CLIENT_WIRE_LOGGER_NAME);
+        addNoOpLogger("org.apache.http.headers");
+        addNoOpLogger(META_INF_EXTENSION_MODULE_LOGGER_NAME);
+        addNoOpLogger("org.littleshoot.proxy.HttpRequestHandler");
+    }
+
+    private void addNoOpLogger(String name) {
+        loggers.put(name, new NoOpLogger(name));
+    }
+
+    public void setOutputEventListener(OutputEventListener outputEventListener) {
+        this.outputEventListener.set(outputEventListener);
+    }
+
+    public OutputEventListener getOutputEventListener() {
+        return outputEventListener.get();
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = loggers.get(name);
+        if (logger != null) {
+            return logger;
+        }
+
+        logger = loggers.putIfAbsent(name, new OutputEventListenerBackedLogger(name, this, timeProvider));
+        return logger != null ? logger : loggers.get(name);
+    }
+
+    public void reset() {
+        setLevel(DEFAULT_LOG_LEVEL);
+        OutputEventRenderer renderer = new OutputEventRenderer(Actions.doNothing());
+        renderer.addStandardOutputListener(defaultOutputStream);
+        renderer.addStandardErrorListener(defaultErrorStream);
+        setOutputEventListener(renderer);
+    }
+
+    public LogLevel getLevel() {
+        return level.get();
+    }
+
+    public void setLevel(LogLevel level) {
+        if (level == null) {
+            throw new IllegalArgumentException("Global log level cannot be set to null");
+        }
+        this.level.set(level);
+    }
+
+    private static class NoOpLogger implements org.gradle.api.logging.Logger {
+
+        private final String name;
+
+        public NoOpLogger(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public boolean isTraceEnabled() {
+            return false;
+        }
+
+        @Override
+        public void trace(String msg) {
+        }
+
+        @Override
+        public void trace(String format, Object arg) {
+        }
+
+        @Override
+        public void trace(String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void trace(String format, Object... arguments) {
+        }
+
+        @Override
+        public void trace(String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isTraceEnabled(Marker marker) {
+            return false;
+        }
+
+        @Override
+        public void trace(Marker marker, String msg) {
+        }
+
+        @Override
+        public void trace(Marker marker, String format, Object arg) {
+        }
+
+        @Override
+        public void trace(Marker marker, String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void trace(Marker marker, String format, Object... argArray) {
+        }
+
+        @Override
+        public void trace(Marker marker, String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isDebugEnabled() {
+            return false;
+        }
+
+        @Override
+        public void debug(String msg) {
+        }
+
+        @Override
+        public void debug(String format, Object arg) {
+        }
+
+        @Override
+        public void debug(String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public boolean isLifecycleEnabled() {
+            return false;
+        }
+
+        @Override
+        public void debug(String format, Object... arguments) {
+        }
+
+        @Override
+        public void lifecycle(String message) {
+        }
+
+        @Override
+        public void lifecycle(String message, Object... objects) {
+        }
+
+        @Override
+        public void lifecycle(String message, Throwable throwable) {
+        }
+
+        @Override
+        public void debug(String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isDebugEnabled(Marker marker) {
+            return false;
+        }
+
+        @Override
+        public void debug(Marker marker, String msg) {
+        }
+
+        @Override
+        public void debug(Marker marker, String format, Object arg) {
+        }
+
+        @Override
+        public void debug(Marker marker, String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void debug(Marker marker, String format, Object... arguments) {
+        }
+
+        @Override
+        public void debug(Marker marker, String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isInfoEnabled() {
+            return false;
+        }
+
+        @Override
+        public void info(String msg) {
+        }
+
+        @Override
+        public void info(String format, Object arg) {
+        }
+
+        @Override
+        public void info(String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void info(String format, Object... arguments) {
+        }
+
+        @Override
+        public boolean isQuietEnabled() {
+            return false;
+        }
+
+        @Override
+        public void quiet(String message) {
+        }
+
+        @Override
+        public void quiet(String message, Object... objects) {
+        }
+
+        @Override
+        public void quiet(String message, Throwable throwable) {
+        }
+
+        @Override
+        public boolean isEnabled(LogLevel level) {
+            return false;
+        }
+
+        @Override
+        public void log(LogLevel level, String message) {
+        }
+
+        @Override
+        public void log(LogLevel level, String message, Object... objects) {
+        }
+
+        @Override
+        public void log(LogLevel level, String message, Throwable throwable) {
+        }
+
+        @Override
+        public void info(String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isInfoEnabled(Marker marker) {
+            return false;
+        }
+
+        @Override
+        public void info(Marker marker, String msg) {
+        }
+
+        @Override
+        public void info(Marker marker, String format, Object arg) {
+        }
+
+        @Override
+        public void info(Marker marker, String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void info(Marker marker, String format, Object... arguments) {
+        }
+
+        @Override
+        public void info(Marker marker, String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isWarnEnabled() {
+            return false;
+        }
+
+        @Override
+        public void warn(String msg) {
+        }
+
+        @Override
+        public void warn(String format, Object arg) {
+        }
+
+        @Override
+        public void warn(String format, Object... arguments) {
+        }
+
+        @Override
+        public void warn(String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void warn(String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isWarnEnabled(Marker marker) {
+            return false;
+        }
+
+        @Override
+        public void warn(Marker marker, String msg) {
+        }
+
+        @Override
+        public void warn(Marker marker, String format, Object arg) {
+        }
+
+        @Override
+        public void warn(Marker marker, String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void warn(Marker marker, String format, Object... arguments) {
+        }
+
+        @Override
+        public void warn(Marker marker, String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isErrorEnabled() {
+            return false;
+        }
+
+        @Override
+        public void error(String msg) {
+        }
+
+        @Override
+        public void error(String format, Object arg) {
+        }
+
+        @Override
+        public void error(String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void error(String format, Object... arguments) {
+        }
+
+        @Override
+        public void error(String msg, Throwable t) {
+        }
+
+        @Override
+        public boolean isErrorEnabled(Marker marker) {
+            return false;
+        }
+
+        @Override
+        public void error(Marker marker, String msg) {
+        }
+
+        @Override
+        public void error(Marker marker, String format, Object arg) {
+        }
+
+        @Override
+        public void error(Marker marker, String format, Object arg1, Object arg2) {
+        }
+
+        @Override
+        public void error(Marker marker, String format, Object... arguments) {
+        }
+
+        @Override
+        public void error(Marker marker, String msg, Throwable t) {
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurer.java
new file mode 100644
index 0000000..1b194fb
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.internal.LoggingConfigurer;
+import org.gradle.logging.internal.OutputEventListener;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link org.gradle.logging.internal.LoggingConfigurer} implementation which configures custom slf4j binding to route logging events to a provided {@link
+ * org.gradle.logging.internal.OutputEventListener}.
+ */
+public class Slf4jLoggingConfigurer implements LoggingConfigurer {
+    private final OutputEventListener outputEventListener;
+
+    private LogLevel currentLevel;
+
+    public Slf4jLoggingConfigurer(OutputEventListener outputListener) {
+        outputEventListener = outputListener;
+    }
+
+    public void configure(LogLevel logLevel) {
+        if (logLevel == currentLevel) {
+            return;
+        }
+
+        OutputEventListenerBackedLoggerContext context = (OutputEventListenerBackedLoggerContext) LoggerFactory.getILoggerFactory();
+
+        if (currentLevel == null) {
+            context.setOutputEventListener(outputEventListener);
+        }
+
+        currentLevel = logLevel;
+        context.setLevel(logLevel);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/ModelFinalizer.java b/subprojects/core/src/main/groovy/org/gradle/model/ModelFinalizer.java
deleted file mode 100644
index 8e1a0cc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/ModelFinalizer.java
+++ /dev/null
@@ -1,30 +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.model;
-
-import org.gradle.api.Incubating;
-
-/**
- * An model object mutation rule, provided to {@link ModelRules#rule(ModelRule)}.
- *
- * <p>Rules that are an instance of this class are executed after those rules that are not. This allows some
- * basic control over rule ordering.
- */
- at Incubating
-public abstract class ModelFinalizer extends ModelRule {
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/ModelPath.java b/subprojects/core/src/main/groovy/org/gradle/model/ModelPath.java
deleted file mode 100644
index 030eda3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/ModelPath.java
+++ /dev/null
@@ -1,90 +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.model;
-
-import org.gradle.api.Incubating;
-
- at Incubating
-public class ModelPath {
-
-    public static final String SEPARATOR = ".";
-
-    private final String path;
-
-    public ModelPath(String path) {
-        this.path = path;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        ModelPath modelPath = (ModelPath) o;
-
-        if (!path.equals(modelPath.path)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return path.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return path;
-    }
-
-    public static ModelPath path(String path) {
-        return new ModelPath(path);
-    }
-
-    public ModelPath child(String child) {
-        return path(path + SEPARATOR + child);
-    }
-
-    public ModelPath getParent() {
-        int lastIndex = path.lastIndexOf(SEPARATOR);
-        if (lastIndex == -1) {
-            return null;
-        } else {
-            return path(path.substring(0, lastIndex));
-        }
-    }
-
-    public String getName() {
-        int lastIndex = path.lastIndexOf(SEPARATOR);
-        if (lastIndex == -1) {
-            return path;
-        } else {
-            return path.substring(lastIndex + 1);
-        }
-    }
-
-    public boolean isDirectChild(ModelPath other) {
-        ModelPath otherParent = other.getParent();
-        return otherParent == null ? false : otherParent.equals(this);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/ModelRule.java b/subprojects/core/src/main/groovy/org/gradle/model/ModelRule.java
deleted file mode 100644
index 9ccd8b5..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/ModelRule.java
+++ /dev/null
@@ -1,40 +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.model;
-
-import org.gradle.api.Incubating;
-
-/**
- * An model object mutation rule, provided to {@link ModelRules#rule(ModelRule)}.
- *
- * <p>Subclasses should provide one and only one public method, with any name and any signature.
- * This method will be inspected for bindings.
- *
- * <p>The first parameter of this method is considered the target or 'output' of the rule. The rule is free to modify the
- * object as appropriate.
- *
- * <p>The subsequent parameters of this method are considered the parameters or 'inputs' of the rule. The rule should not
- * modify these objects. Rules are ordered so that the input to a rule is completely configured before the rule is invoked.
- *
- * <p>The ordering of rules with the same target object is currently undefined and rules are executed in some arbitrary but
- * fixed order. There is some basic support for controlling the ordering, where all rules that extend {@link ModelFinalizer}
- * are executed after all rules that extend {@link ModelRule}.
- */
- at Incubating
-public abstract class ModelRule {
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/ModelRules.java b/subprojects/core/src/main/groovy/org/gradle/model/ModelRules.java
deleted file mode 100644
index 836b79f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/ModelRules.java
+++ /dev/null
@@ -1,57 +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.model;
-
-import org.gradle.api.Action;
-import org.gradle.api.Incubating;
-import org.gradle.internal.Factory;
-
-/**
- * A service for registering model rules.
- *
- * Plugins can inject an instance of this.
- */
- at Incubating
-public interface ModelRules {
-    /**
-     * Registers a model object under the given path.
-     */
-    <T> void register(String path, T model);
-
-    /**
-     * Registers a model object under the given path. The provided factory will be used to create the model object when it is required.
-     */
-    <T> void register(String path, Class<T> type, Factory<? extends T> model);
-
-    /**
-     * Registers an action that configures the model object at the given path. The provided action will be executed against the model object when
-     * the model object is required.
-     */
-    <T> void config(String path, Action<T> action);
-
-    /**
-     * Registers a rule. The rule is inspected to determine its inputs and outputs.
-     *
-     * @see ModelRule For details.
-     */
-    void rule(ModelRule rule);
-
-    /**
-     * Removes the model object from the given path.
-     */
-    void remove(String path);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/BridgedCollections.java b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/BridgedCollections.java
new file mode 100644
index 0000000..4ca41bd
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/BridgedCollections.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.Namer;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.PolymorphicDomainObjectContainerInternal;
+import org.gradle.internal.BiAction;
+import org.gradle.internal.Transformers;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+
+public abstract class BridgedCollections {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(BridgedCollections.class);
+
+    private BridgedCollections() {
+    }
+
+    public static <I> ModelReference<NamedEntityInstantiator<I>> instantiatorReference(ModelPath containerPath, ModelType<I> itemType) {
+        final String instantiatorNodeName = "__instantiator";
+        return ModelReference.of(
+                containerPath.child(instantiatorNodeName),
+                new ModelType.Builder<NamedEntityInstantiator<I>>() {
+                }.where(new ModelType.Parameter<I>() {
+                }, itemType).build()
+        );
+    }
+
+    private static class ContainerInfo<I> {
+        final ModelCreators.Builder creatorBuilder;
+        final ModelReference<NamedEntityInstantiator<I>> instantiatorReference;
+        final ModelReference<? extends Collection<I>> storeReference;
+
+        public ContainerInfo(ModelCreators.Builder creatorBuilder, ModelReference<NamedEntityInstantiator<I>> instantiatorReference, ModelReference<? extends Collection<I>> storeReference) {
+            this.creatorBuilder = creatorBuilder;
+            this.instantiatorReference = instantiatorReference;
+            this.storeReference = storeReference;
+        }
+    }
+
+    private static <I, C extends PolymorphicDomainObjectContainerInternal<I>> ContainerInfo<I> creator(
+            final ModelRegistry modelRegistry,
+            final ModelReference<C> containerReference,
+            final ModelType<I> itemType,
+            final Transformer<? extends C, ? super MutableModelNode> containerFactory,
+            final Namer<? super I> namer,
+            String descriptor,
+            final Transformer<String, String> itemDescriptorGenerator
+    ) {
+        assert containerReference.getPath() != null : "container reference path cannot be null";
+
+        final String storeNodeName = "__store";
+
+        final ModelReference<NamedEntityInstantiator<I>> instantiatorReference = instantiatorReference(containerReference.getPath(), itemType);
+
+        final ModelReference<C> storeReference = ModelReference.of(
+                containerReference.getPath().child(storeNodeName),
+                containerReference.getType()
+        );
+
+        ModelCreators.Builder creatorBuilder = ModelCreators.of(
+                containerReference,
+                new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                    public void execute(final MutableModelNode containerNode, List<ModelView<?>> inputs) {
+
+                        C container = containerFactory.transform(containerNode);
+
+                        ModelCreator storeCreator = ModelCreators.bridgedInstance(storeReference, container)
+                                .ephemeral(true)
+                                .hidden(true)
+                                .descriptor(itemDescriptorGenerator.transform(storeNodeName))
+                                .build();
+
+                        modelRegistry.createOrReplace(storeCreator);
+
+                        @SuppressWarnings("ConstantConditions")
+                        String instantiatorNodeName = instantiatorReference.getPath().getName();
+
+                        ModelCreator instantiatorCreator = ModelCreators.bridgedInstance(instantiatorReference, container.getEntityInstantiator())
+                                .ephemeral(true)
+                                .hidden(true)
+                                .descriptor(itemDescriptorGenerator.transform(instantiatorNodeName))
+                                .build();
+
+                        modelRegistry.createOrReplace(instantiatorCreator);
+
+                        containerNode.setPrivateData(containerReference.getType(), container);
+                        container.all(new Action<I>() {
+                            public void execute(final I item) {
+                                final String name = namer.determineName(item);
+
+                                // For now, ignore elements added after the container has been closed
+                                if (!containerNode.isMutable()) {
+                                    LOGGER.debug("Ignoring element '{}' added to '{}' after it is closed.", containerReference.getPath(), name);
+                                    return;
+                                }
+
+                                if (!containerNode.hasLink(name)) {
+                                    ModelType<I> itemType = ModelType.typeOf(item);
+                                    ModelReference<I> itemReference = ModelReference.of(containerReference.getPath().child(name), itemType);
+                                    ModelCreator itemCreator = ModelCreators.of(itemReference, new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                                        @Override
+                                        public void execute(MutableModelNode modelNode, List<ModelView<?>> modelViews) {
+                                            C container = ModelViews.assertType(modelViews.get(0), storeReference.getType()).getInstance();
+                                            I item = container.getByName(name);
+                                            modelNode.setPrivateData(ModelType.typeOf(item), item);
+                                        }
+                                    })
+                                            .inputs(storeReference)
+                                            .withProjection(new UnmanagedModelProjection<I>(itemType, true, true))
+                                            .descriptor(itemDescriptorGenerator.transform(name)).build();
+
+                                    containerNode.addLink(itemCreator);
+                                }
+                            }
+                        });
+                        container.whenObjectRemoved(new Action<I>() {
+                            public void execute(I item) {
+                                String name = namer.determineName(item);
+                                containerNode.removeLink(name);
+                            }
+                        });
+                    }
+                }
+        )
+                .ephemeral(true)
+                .descriptor(descriptor);
+
+        return new ContainerInfo<I>(creatorBuilder, instantiatorReference, storeReference);
+    }
+
+    public static <I, C extends PolymorphicDomainObjectContainerInternal<I>, P /* super C */> void dynamicTypes(
+            ModelRegistry modelRegistry,
+            ModelPath modelPath,
+            String descriptor,
+            ModelType<P> publicType,
+            ModelType<C> containerType,
+            ModelType<I> itemType,
+            C container,
+            Namer<? super I> namer,
+            Transformer<String, String> itemDescriptorGenerator
+    ) {
+        ModelReference<C> containerReference = ModelReference.of(modelPath, containerType);
+
+        ContainerInfo<I> containerInfo = creator(modelRegistry, containerReference, itemType, Transformers.constant(container), namer, descriptor, itemDescriptorGenerator);
+
+        modelRegistry.createOrReplace(containerInfo.creatorBuilder
+                .withProjection(new DynamicTypesDomainObjectContainerModelProjection<C, I>(container, itemType, containerInfo.instantiatorReference, containerInfo.storeReference))
+                .withProjection(new UnmanagedModelProjection<P>(publicType, true, true))
+                .build());
+    }
+
+    public static <I, C extends PolymorphicDomainObjectContainerInternal<I>, P /* super C */> void staticTypes(
+            ModelRegistry modelRegistry,
+            ModelPath modelPath,
+            ModelType<C> containerType,
+            ModelType<I> itemType,
+            ModelType<P> publicType,
+            Transformer<? extends C, ? super MutableModelNode> containerFactory,
+            Namer<? super I> namer,
+            String descriptor,
+            Transformer<String, String> itemDescriptorGenerator
+    ) {
+        ModelReference<C> containerReference = ModelReference.of(modelPath, containerType);
+
+        ContainerInfo<I> containerInfo = creator(modelRegistry, containerReference, itemType, containerFactory, namer, descriptor, itemDescriptorGenerator);
+
+        modelRegistry.createOrReplace(containerInfo.creatorBuilder
+                .withProjection(new StaticTypeDomainObjectContainerModelProjection<C, I>(containerType, itemType, containerInfo.instantiatorReference, containerInfo.storeReference))
+                .withProjection(new UnmanagedModelProjection<P>(publicType, true, true))
+                .build());
+    }
+
+    public static Transformer<String, String> itemDescriptor(String parentDescriptor) {
+        return new StandardItemDescriptorFactory(parentDescriptor);
+    }
+
+    private static class StandardItemDescriptorFactory implements Transformer<String, String> {
+        private final String descriptor;
+
+        public StandardItemDescriptorFactory(String descriptor) {
+            this.descriptor = descriptor;
+        }
+
+        @Override
+        public String transform(String s) {
+            return descriptor + '.' + s + "()";
+        }
+    }
+}
+
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DomainObjectContainerModelProjection.java b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DomainObjectContainerModelProjection.java
new file mode 100644
index 0000000..765079d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DomainObjectContainerModelProjection.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import org.gradle.api.Nullable;
+import org.gradle.api.internal.PolymorphicDomainObjectContainerInternal;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Collection;
+import java.util.List;
+
+public abstract class DomainObjectContainerModelProjection<C extends PolymorphicDomainObjectContainerInternal<M>, M> implements ModelProjection {
+
+    protected final Class<M> baseItemType;
+    private final ModelReference<NamedEntityInstantiator<M>> instantiatorModelReference;
+    private final ModelType<M> baseItemModelType;
+    private final ModelReference<? extends Collection<? super M>> storeReference;
+
+    public DomainObjectContainerModelProjection(ModelType<M> baseItemType, ModelReference<NamedEntityInstantiator<M>> instantiatorModelReference, ModelReference<? extends Collection<? super M>> storeReference) {
+        this.baseItemModelType = baseItemType;
+        this.storeReference = storeReference;
+        this.baseItemType = baseItemType.getConcreteClass();
+        this.instantiatorModelReference = instantiatorModelReference;
+    }
+
+    public <T> ModelView<? extends T> asWritable(ModelType<T> targetType, MutableModelNode node, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> inputs) {
+        Class<? extends M> itemType = itemType(targetType);
+        if (itemType != null) {
+            return toView(ruleDescriptor, node, itemType);
+        }
+        return null;
+    }
+
+    protected <T, S extends M> ModelView<? extends T> toView(ModelRuleDescriptor sourceDescriptor, MutableModelNode node, Class<S> itemClass) {
+        ModelType<S> itemType = ModelType.of(itemClass);
+        CollectionBuilder<M> builder = new DefaultCollectionBuilder<M>(baseItemModelType, sourceDescriptor, node, DefaultCollectionBuilder.createAndStoreVia(instantiatorModelReference, storeReference));
+
+        CollectionBuilder<S> subBuilder = builder.withType(itemClass);
+        CollectionBuilderModelView<S> view = new CollectionBuilderModelView<S>(node.getPath(), DefaultCollectionBuilder.typeOf(itemType), subBuilder, sourceDescriptor);
+        @SuppressWarnings("unchecked") ModelView<T> cast = (ModelView<T>) view;
+        return cast;
+    }
+
+    public static String getBuilderTypeDescriptionForCreatableTypes(Collection<? extends Class<?>> createableTypes) {
+        StringBuilder sb = new StringBuilder(CollectionBuilder.class.getName());
+        if (createableTypes.size() == 1) {
+            @SuppressWarnings("ConstantConditions")
+            String onlyType = Iterables.getFirst(createableTypes, null).getName();
+            sb.append("<").append(onlyType).append(">");
+        } else {
+            sb.append("<T>; where T is one of [");
+            Joiner.on(", ").appendTo(sb, CollectionUtils.sort(Iterables.transform(createableTypes, new Function<Class<?>, String>() {
+                public String apply(Class<?> input) {
+                    return input.getName();
+                }
+            })));
+            sb.append("]");
+        }
+        return sb.toString();
+    }
+
+    public <T> boolean canBeViewedAsWritable(ModelType<T> targetType) {
+        return itemType(targetType) != null;
+    }
+
+    protected Class<? extends M> itemType(ModelType<?> targetType) {
+        Class<?> targetClass = targetType.getRawClass();
+        if (targetClass.equals(CollectionBuilder.class)) {
+            Class<?> targetItemClass = targetType.getTypeVariables().get(0).getRawClass();
+            if (targetItemClass.isAssignableFrom(baseItemType)) {
+                return baseItemType;
+            }
+            if (baseItemType.isAssignableFrom(targetItemClass)) {
+                return targetItemClass.asSubclass(baseItemType);
+            }
+            return null;
+        }
+        if (targetClass.isAssignableFrom(CollectionBuilder.class)) {
+            return baseItemType;
+        }
+        return null;
+    }
+
+    public <T> boolean canBeViewedAsReadOnly(ModelType<T> type) {
+        return canBeViewedAsWritable(type);
+    }
+
+    public <T> ModelView<? extends T> asReadOnly(ModelType<T> type, MutableModelNode modelNode, @Nullable ModelRuleDescriptor ruleDescriptor) {
+        return asWritable(type, modelNode, ruleDescriptor, null);
+    }
+
+    public Iterable<String> getReadableTypeDescriptions() {
+        return getWritableTypeDescriptions();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DomainObjectContainerModelProjection that = (DomainObjectContainerModelProjection) o;
+
+        return baseItemType.equals(that.baseItemType) && instantiatorModelReference.equals(that.instantiatorModelReference);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = baseItemType.hashCode();
+        result = 31 * result + instantiatorModelReference.hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DynamicTypesDomainObjectContainerModelProjection.java b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DynamicTypesDomainObjectContainerModelProjection.java
new file mode 100644
index 0000000..c1cabe1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/DynamicTypesDomainObjectContainerModelProjection.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal;
+
+import org.gradle.api.internal.PolymorphicDomainObjectContainerInternal;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * When reporting its writable views, considers what factories have been registered.
+ *
+ * @see StaticTypeDomainObjectContainerModelProjection
+ */
+public class DynamicTypesDomainObjectContainerModelProjection<C extends PolymorphicDomainObjectContainerInternal<M>, M> extends DomainObjectContainerModelProjection<C, M> {
+
+    private final C container;
+
+    public DynamicTypesDomainObjectContainerModelProjection(C container, ModelType<M> itemType, ModelReference<NamedEntityInstantiator<M>> instantiatorModelReference, ModelReference<? extends Collection<? super M>> storeReference) {
+        super(itemType, instantiatorModelReference, storeReference);
+        this.container = container;
+    }
+
+    public Iterable<String> getWritableTypeDescriptions() {
+        return Collections.singleton(getBuilderTypeDescriptionForCreatableTypes(container.getCreateableTypes()));
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/StaticTypeDomainObjectContainerModelProjection.java b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/StaticTypeDomainObjectContainerModelProjection.java
new file mode 100644
index 0000000..b18512e
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/collection/internal/StaticTypeDomainObjectContainerModelProjection.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal;
+
+import org.gradle.api.internal.PolymorphicDomainObjectContainerInternal;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.NamedEntityInstantiator;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class StaticTypeDomainObjectContainerModelProjection<C extends PolymorphicDomainObjectContainerInternal<M>, M> extends DomainObjectContainerModelProjection<C, M> {
+
+    private final ModelType<C> collectionType;
+
+    public StaticTypeDomainObjectContainerModelProjection(ModelType<C> collectionType, ModelType<M> itemType, ModelReference<NamedEntityInstantiator<M>> instantiatorModelReference, ModelReference<? extends Collection<? super M>> storeReference) {
+        super(itemType, instantiatorModelReference, storeReference);
+        this.collectionType = collectionType;
+    }
+
+    @Override
+    public Iterable<String> getWritableTypeDescriptions() {
+        return Collections.singleton(getBuilderTypeDescriptionForCreatableTypes(Collections.singleton(baseItemType)));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        StaticTypeDomainObjectContainerModelProjection<?, ?> that = (StaticTypeDomainObjectContainerModelProjection<?, ?>) o;
+
+        return collectionType.equals(that.collectionType);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + collectionType.hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/dsl/ModelDsl.java b/subprojects/core/src/main/groovy/org/gradle/model/dsl/ModelDsl.java
deleted file mode 100644
index 5404e94..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/dsl/ModelDsl.java
+++ /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.model.dsl;
-
-import org.gradle.api.Incubating;
-
-/**
- * Exposes the Groovy level DSL for configuring model elements in a build script.
- */
- at Incubating
-public interface ModelDsl {
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/dsl/internal/GroovyModelDsl.java b/subprojects/core/src/main/groovy/org/gradle/model/dsl/internal/GroovyModelDsl.java
deleted file mode 100644
index 818ebfa..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/dsl/internal/GroovyModelDsl.java
+++ /dev/null
@@ -1,84 +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.model.dsl.internal;
-
-import groovy.lang.Closure;
-import groovy.lang.GroovyObjectSupport;
-import groovy.lang.MissingMethodException;
-import groovy.lang.MissingPropertyException;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.model.ModelPath;
-import org.gradle.model.ModelRules;
-import org.gradle.model.dsl.ModelDsl;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class GroovyModelDsl extends GroovyObjectSupport implements ModelDsl {
-    private final ModelPath modelPath;
-    private final ModelRules modelRules;
-    private AtomicBoolean executingDsl;
-
-    public GroovyModelDsl(ModelRules modelRules) {
-        this(new AtomicBoolean(), null, modelRules);
-    }
-
-    private GroovyModelDsl(AtomicBoolean executingDsl, ModelPath modelPath, ModelRules modelRules) {
-        this.executingDsl = executingDsl;
-        this.modelPath = modelPath;
-        this.modelRules = modelRules;
-    }
-
-    private GroovyModelDsl getChildPath(String name) {
-        ModelPath path = modelPath == null ? ModelPath.path(name) : modelPath.child(name);
-        return new GroovyModelDsl(executingDsl, path, modelRules);
-    }
-
-    private void registerConfigurationAction(Closure<?> action) {
-        modelRules.config(modelPath.toString(), new ClosureBackedAction<Object>(action));
-    }
-
-    public void configure(Closure<?> action) {
-        executingDsl.set(true);
-        try {
-            new ClosureBackedAction<Object>(action).execute(this);
-        } finally {
-            executingDsl.set(false);
-        }
-    }
-
-    public GroovyModelDsl propertyMissing(String name) {
-        if (!executingDsl.get()) {
-            throw new MissingPropertyException(name, getClass());
-        }
-        return getChildPath(name);
-    }
-
-    public Void methodMissing(String name, Object argsObj) {
-        Object[] args = (Object[]) argsObj;
-
-        if (!executingDsl.get() || args.length != 1 || !(args[0] instanceof Closure)) {
-            throw new MissingMethodException(name, getClass(), args);
-        }
-
-        Closure closure = (Closure) args[0];
-
-        getChildPath(name).registerConfigurationAction(closure);
-
-        return null;
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultInputs.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultInputs.java
deleted file mode 100644
index dfc8041..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultInputs.java
+++ /dev/null
@@ -1,42 +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.model.internal;
-
-import com.google.common.collect.ImmutableList;
-
-public class DefaultInputs implements Inputs {
-
-    private final ImmutableList<?> inputs;
-
-    public DefaultInputs(ImmutableList<?> inputs) {
-        this.inputs = inputs;
-    }
-
-    public <T> T get(int i, Class<T> type) {
-        Object input = inputs.get(i);
-        if (type.isInstance(input)) {
-            return type.cast(input);
-        } else {
-            throw new RuntimeException("Can't convert input '" + i + "' with type '" + input.getClass() + "' to type '" + type + "'");
-        }
-    }
-
-    public int size() {
-        return inputs.size();
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultModelRegistry.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultModelRegistry.java
deleted file mode 100644
index e8ec27d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/DefaultModelRegistry.java
+++ /dev/null
@@ -1,253 +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.model.internal;
-
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import org.gradle.api.Transformer;
-import org.gradle.internal.Transformers;
-import org.gradle.model.ModelPath;
-
-import java.util.*;
-
-import static org.gradle.util.CollectionUtils.collect;
-
-public class DefaultModelRegistry implements ModelRegistry {
-
-    private final Map<ModelPath, Object> store = new HashMap<ModelPath, Object>();
-
-    private final Map<ModelPath, ModelCreation> creations = new HashMap<ModelPath, ModelCreation>();
-    private final Multimap<ModelPath, ModelMutation<?>> mutators = ArrayListMultimap.create();
-    private final Multimap<ModelPath, ImmutableList<ModelPath>> usedMutators = ArrayListMultimap.create();
-    private final Multimap<ModelPath, ModelMutation<?>> finalizers = ArrayListMultimap.create();
-    private final Multimap<ModelPath, ImmutableList<ModelPath>> usedFinalizers = ArrayListMultimap.create();
-
-    private final List<ModelCreationListener> modelCreationListeners = new LinkedList<ModelCreationListener>();
-
-    public <T> void create(String path, List<String> inputPaths, ModelCreator<T> creator) {
-        ModelPath creationModelPath = ModelPath.path(path);
-        if (creations.containsKey(creationModelPath)) {
-            throw new IllegalStateException("creator already registered for '" + creationModelPath + "'");
-        }
-        if (store.containsKey(creationModelPath)) {
-            throw new IllegalStateException("model already created for '" + creationModelPath + "'");
-        }
-
-        notifyCreationListeners(creationModelPath, creator);
-        creations.put(creationModelPath, new ModelCreation(creator, toModelPaths(inputPaths)));
-    }
-
-    private static ImmutableList<ModelPath> toModelPaths(List<String> inputPaths) {
-        return ImmutableList.copyOf(collect(inputPaths, new ToModelPath()));
-    }
-
-    public <T> void mutate(String path, List<String> inputPaths, ModelMutator<T> mutator) {
-        mutate(path, new ModelMutation<T>(mutator, toModelPaths(inputPaths)));
-    }
-
-    public <T> void mutate(String path, ModelMutation<T> mutation) {
-        ModelPath mutationModelPath = assertNotFinalized(path);
-        mutators.put(mutationModelPath, mutation);
-    }
-
-    public <T> void finalize(String path, List<String> inputPaths, ModelMutator<T> mutator) {
-        finalize(path, new ModelMutation<T>(mutator, toModelPaths(inputPaths)));
-    }
-
-    public <T> void finalize(String path, ModelMutation<T> mutation) {
-        ModelPath mutationModelPath = assertNotFinalized(path);
-        finalizers.put(mutationModelPath, mutation);
-    }
-
-    private ModelPath assertNotFinalized(String path) {
-        ModelPath mutationModelPath = ModelPath.path(path);
-        if (store.containsKey(mutationModelPath)) {
-            throw new IllegalStateException("model '" + mutationModelPath + "' is finalized");
-        }
-        return mutationModelPath;
-    }
-
-    public <T> T get(String path, Class<T> type) {
-        ModelPath modelPath = ModelPath.path(path);
-        Object model = getClosedModel(modelPath);
-
-        if (type.isInstance(model)) {
-            return type.cast(model);
-        } else {
-            throw new RuntimeException("Can't convert model at path '" + path + "' with type '" + model.getClass() + "' to target type '" + type + "'");
-        }
-    }
-
-    public void registerListener(ModelCreationListener listener) {
-        boolean remove;
-
-        for (Map.Entry<ModelPath, ModelCreation> entry : creations.entrySet()) {
-            remove = listener.onCreate(entry.getKey(), entry.getValue().getCreator().getType());
-            if (remove) {
-                return;
-            }
-        }
-
-        modelCreationListeners.add(listener);
-    }
-
-    public void remove(String path) {
-        ModelPath modelPath = ModelPath.path(path);
-        if (creations.remove(modelPath) == null) {
-            if (store.remove(modelPath) == null) {
-                throw new RuntimeException("Tried to remove model " + path + " but it is not registered");
-            } else {
-                if (isDependedOn(modelPath)) {
-                    throw new RuntimeException("Tried to remove model " + path + " but it is depended on by other model elements");
-                }
-            }
-        }
-    }
-
-    private boolean isDependedOn(ModelPath candidate) {
-        Transformer<Iterable<ModelPath>, ModelMutation<?>> extractInputPaths = new Transformer<Iterable<ModelPath>, ModelMutation<?>>() {
-            public Iterable<ModelPath> transform(ModelMutation<?> original) {
-                return original.getInputPaths();
-            }
-        };
-
-        Transformer<ImmutableList<ModelPath>, ImmutableList<ModelPath>> passThrough = Transformers.noOpTransformer();
-
-        return hasModelPath(candidate, mutators.values(), extractInputPaths)
-                || hasModelPath(candidate, usedMutators.values(), passThrough)
-                || hasModelPath(candidate, finalizers.values(), extractInputPaths)
-                || hasModelPath(candidate, usedFinalizers.values(), passThrough);
-    }
-
-    private <T> boolean hasModelPath(ModelPath candidate, Iterable<T> things, Transformer<? extends Iterable<ModelPath>, T> transformer) {
-        for (T thing : things) {
-            for (ModelPath path : transformer.transform(thing)) {
-                if (path.equals(candidate)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    private Set<ModelPath> getPromisedPaths() {
-        return ImmutableSet.<ModelPath>builder().addAll(creations.keySet()).build();
-    }
-
-    private Object getClosedModel(ModelPath path) {
-        if (store.containsKey(path)) {
-            return store.get(path);
-        }
-
-        Object model = createModel(path);
-        Collection<ModelMutation<?>> modelMutations = mutators.removeAll(path);
-        for (ModelMutation<?> modelMutation : modelMutations) {
-            fireMutation(model, modelMutation);
-            @SuppressWarnings("unchecked") ImmutableList<ModelPath> inputPaths = modelMutation.getInputPaths();
-            usedMutators.put(path, inputPaths);
-        }
-
-        modelMutations = finalizers.removeAll(path);
-        for (ModelMutation modelMutation : modelMutations) {
-            fireMutation(model, modelMutation);
-            @SuppressWarnings("unchecked") ImmutableList<ModelPath> inputPaths = modelMutation.getInputPaths();
-            usedFinalizers.put(path, inputPaths);
-        }
-
-        // close all the child objects
-        Set<ModelPath> promisedPaths = getPromisedPaths();
-        for (ModelPath modelPath : promisedPaths) {
-            if (path.isDirectChild(modelPath)) {
-                getClosedModel(modelPath);
-            }
-        }
-
-        store.put(path, model);
-
-        return model;
-    }
-
-    private Object createModel(final ModelPath path) {
-        ModelCreation creation = creations.remove(path);
-        if (creation == null) {
-            throw new IllegalStateException("No creator for '" + path + "'");
-        }
-
-        Inputs inputs = toInputs(creation.getInputPaths());
-        Object created = creation.getCreator().create(inputs);
-        store.put(path, created);
-        return created;
-    }
-
-    private <T> void fireMutation(Object model, ModelMutation<T> modelMutation) {
-        ModelMutator<T> mutator = modelMutation.getMutator();
-        Inputs inputs = toInputs(modelMutation.getInputPaths());
-        T cast = mutator.getType().cast(model);
-        mutator.mutate(cast, inputs);
-    }
-
-    private Inputs toInputs(Iterable<ModelPath> inputPaths) {
-        ImmutableList.Builder<Object> builder = ImmutableList.builder();
-        for (ModelPath inputPath : inputPaths) {
-            builder.add(getClosedModel(inputPath));
-        }
-        return new DefaultInputs(builder.build());
-    }
-
-    private <T> void notifyCreationListeners(ModelPath path, ModelCreator<T> creator) {
-        ListIterator<ModelCreationListener> modelCreationListenerListIterator = modelCreationListeners.listIterator();
-        while (modelCreationListenerListIterator.hasNext()) {
-            ModelCreationListener next = modelCreationListenerListIterator.next();
-            boolean remove = next.onCreate(path, creator.getType());
-            if (remove) {
-                modelCreationListenerListIterator.remove();
-            }
-        }
-    }
-
-    private static class ModelCreation {
-
-        private final ModelCreator<?> creator;
-        private final ImmutableList<ModelPath> inputPaths;
-
-        public ModelCreation(ModelCreator<?> creator, ImmutableList<ModelPath> inputPaths) {
-            this.creator = creator;
-            this.inputPaths = inputPaths;
-        }
-
-        public ModelCreator<?> getCreator() {
-            return creator;
-        }
-
-        public ImmutableList<ModelPath> getInputPaths() {
-            return inputPaths;
-        }
-
-    }
-
-    private static class ToModelPath implements Transformer<ModelPath, String> {
-        public ModelPath transform(String string) {
-            return ModelPath.path(string);
-        }
-    }
-
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/Inputs.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/Inputs.java
deleted file mode 100644
index 0f233f0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/Inputs.java
+++ /dev/null
@@ -1,25 +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.model.internal;
-
-public interface Inputs {
-
-    <T> T get(int i, Class<T> type);
-
-    int size();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreationListener.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreationListener.java
deleted file mode 100644
index 6318cd0..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreationListener.java
+++ /dev/null
@@ -1,25 +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.model.internal;
-
-import org.gradle.model.ModelPath;
-
-public interface ModelCreationListener {
-
-    boolean onCreate(ModelPath path, Class<?> type);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreator.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreator.java
deleted file mode 100644
index e750a0e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelCreator.java
+++ /dev/null
@@ -1,25 +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.model.internal;
-
-public interface ModelCreator<T> {
-
-    Class<T> getType();
-
-    T create(Inputs inputs);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutation.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutation.java
deleted file mode 100644
index 5a89ab6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutation.java
+++ /dev/null
@@ -1,39 +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.model.internal;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.model.ModelPath;
-
-class ModelMutation<T> {
-
-    private final ModelMutator<T> mutator;
-    private final ImmutableList<ModelPath> inputPaths;
-
-    public ModelMutation(ModelMutator<T> mutator, ImmutableList<ModelPath> inputPaths) {
-        this.mutator = mutator;
-        this.inputPaths = inputPaths;
-    }
-
-    public ModelMutator<T> getMutator() {
-        return mutator;
-    }
-
-    public ImmutableList<ModelPath> getInputPaths() {
-        return inputPaths;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutator.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutator.java
deleted file mode 100644
index 2675b5f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelMutator.java
+++ /dev/null
@@ -1,25 +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.model.internal;
-
-public interface ModelMutator<T> {
-
-    Class<T> getType();
-
-    void mutate(T object, Inputs inputs);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistry.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistry.java
deleted file mode 100644
index 96e0e07..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistry.java
+++ /dev/null
@@ -1,38 +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.model.internal;
-
-import java.util.List;
-
-public interface ModelRegistry {
-
-    public <T> void create(String path, List<String> inputPaths, ModelCreator<T> creator);
-
-    public <T> void mutate(String path, List<String> inputPaths, ModelMutator<T> mutator);
-
-    <T> void mutate(String path, ModelMutation<T> mutation);
-
-    public <T> void finalize(String path, List<String> inputPaths, ModelMutator<T> mutator);
-
-    <T> void finalize(String path, ModelMutation<T> mutation);
-
-    public <T> T get(String path, Class<T> type);
-
-    public void registerListener(ModelCreationListener listener);
-
-    void remove(String path);
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistryBackedModelRules.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistryBackedModelRules.java
deleted file mode 100644
index 8195189..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/ModelRegistryBackedModelRules.java
+++ /dev/null
@@ -1,129 +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.model.internal;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.Action;
-import org.gradle.api.specs.Spec;
-import org.gradle.internal.Factories;
-import org.gradle.internal.Factory;
-import org.gradle.model.ModelRule;
-import org.gradle.model.ModelRules;
-import org.gradle.model.internal.rules.ReflectiveRule;
-
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.util.Collections;
-
-import static org.gradle.util.CollectionUtils.findFirst;
-
-public class ModelRegistryBackedModelRules implements ModelRules {
-
-    private final ModelRegistry modelRegistry;
-
-    public ModelRegistryBackedModelRules(ModelRegistry modelRegistry) {
-        this.modelRegistry = modelRegistry;
-    }
-
-    public <T> void register(String path, final T model) {
-        @SuppressWarnings("unchecked") Class<T> aClass = (Class<T>) model.getClass();
-        register(path, aClass, Factories.constant(model));
-    }
-
-    public <T> void register(String path, final Class<T> type, final Factory<? extends T> model) {
-        modelRegistry.create(path, ImmutableList.<String>of(), new ModelCreator<T>() {
-            public Class<T> getType() {
-                return type;
-            }
-
-            public T create(Inputs inputs) {
-                return model.create();
-            }
-        });
-    }
-
-    public void remove(String path) {
-        modelRegistry.remove(path);
-    }
-
-    public void rule(ModelRule rule) {
-        ReflectiveRule.rule(modelRegistry, rule);
-    }
-
-    public <T> void config(String path, Action<T> action) {
-        final Class<T> modelType = getActionObjectType(action);
-        modelRegistry.mutate(path, Collections.<String>emptyList(), ActionBackedModelMutator.<T>create(modelType, action));
-    }
-
-    private <T> Class<T> getActionObjectType(Action<T> action) {
-        Class<? extends Action> aClass = action.getClass();
-        Type[] genericInterfaces = aClass.getGenericInterfaces();
-        Type actionType = findFirst(genericInterfaces, new Spec<Type>() {
-            public boolean isSatisfiedBy(Type element) {
-                return element instanceof ParameterizedType && ((ParameterizedType) element).getRawType().equals(Action.class);
-            }
-        });
-
-        final Class<?> modelType;
-
-        if (actionType == null) {
-            modelType = Object.class;
-        } else {
-            ParameterizedType actionParamaterizedType = (ParameterizedType) actionType;
-            Type tType = actionParamaterizedType.getActualTypeArguments()[0];
-
-            if (tType instanceof Class) {
-                modelType = (Class) tType;
-            } else if (tType instanceof ParameterizedType) {
-                modelType = (Class) ((ParameterizedType) tType).getRawType();
-            } else if (tType instanceof TypeVariable) {
-                TypeVariable  typeVariable = (TypeVariable) tType;
-                Type[] bounds = typeVariable.getBounds();
-                return (Class<T>) bounds[0];
-            } else {
-                throw new RuntimeException("Don't know how to handle type: " + tType.getClass());
-            }
-        }
-
-        @SuppressWarnings("unchecked") Class<T> castModelType = (Class<T>) modelType;
-        return castModelType;
-    }
-
-    private static class ActionBackedModelMutator<T> implements ModelMutator<T> {
-        private final Class<T> modelType;
-        private final Action<T> action;
-
-        public static <T> ModelMutator<T> create(Class<T> type, Action<T> action) {
-            return new ActionBackedModelMutator<T>(type, action);
-        }
-
-        public ActionBackedModelMutator(Class<T> modelType, Action<T> action) {
-            this.modelType = modelType;
-            this.action = action;
-        }
-
-        public Class<T> getType() {
-            return modelType;
-        }
-
-        public void mutate(T object, Inputs inputs) {
-            action.execute(object);
-        }
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/AlwaysNewModelRegistryStore.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/AlwaysNewModelRegistryStore.java
new file mode 100644
index 0000000..8a0017a
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/AlwaysNewModelRegistryStore.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.persist;
+
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.registry.DefaultModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistry;
+
+public class AlwaysNewModelRegistryStore implements ModelRegistryStore {
+
+    private final ModelRuleExtractor extractor;
+
+    public AlwaysNewModelRegistryStore(ModelRuleExtractor extractor) {
+        this.extractor = extractor;
+    }
+
+    @Override
+    public ModelRegistry get(ProjectIdentifier projectIdentifier) {
+        return new DefaultModelRegistry(extractor);
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ModelRegistryStore.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ModelRegistryStore.java
new file mode 100644
index 0000000..c05cfb2
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ModelRegistryStore.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.persist;
+
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.model.internal.registry.ModelRegistry;
+
+public interface ModelRegistryStore {
+
+    ModelRegistry get(ProjectIdentifier projectIdentifier);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ReusingModelRegistryStore.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ReusingModelRegistryStore.java
new file mode 100644
index 0000000..3cc3c7d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/model/internal/persist/ReusingModelRegistryStore.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.persist;
+
+import com.google.common.collect.Maps;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.registry.DefaultModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistry;
+
+import java.util.Map;
+
+ at NotThreadSafe
+public class ReusingModelRegistryStore implements ModelRegistryStore {
+
+    private static final Logger LOGGER = Logging.getLogger(ReusingModelRegistryStore.class);
+
+    public static final String TOGGLE = "org.gradle.model.reuse";
+    public static final String BANNER = "Experimental model reuse is enabled.";
+
+    private final ModelRuleExtractor ruleExtractor;
+    private final Map<String, ModelRegistry> store = Maps.newHashMap();
+
+    public ReusingModelRegistryStore(ModelRuleExtractor ruleExtractor) {
+        this.ruleExtractor = ruleExtractor;
+    }
+
+    @Override
+    public ModelRegistry get(ProjectIdentifier projectIdentifier) {
+        ModelRegistry modelRegistry = store.get(projectIdentifier.getProjectDir().getAbsolutePath());
+        if (modelRegistry == null) {
+            LOGGER.info("creating new model registry for project: " + projectIdentifier.getPath());
+            modelRegistry = new DefaultModelRegistry(ruleExtractor);
+            store.put(projectIdentifier.getProjectDir().getAbsolutePath(), modelRegistry);
+        } else {
+            LOGGER.info("reusing model for project: " + projectIdentifier.getPath());
+            // TODO - we should be doing this after the build, after the daemon has returned to the user doesn't wait for it
+            modelRegistry.prepareForReuse();
+        }
+
+        return modelRegistry;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/internal/rules/ReflectiveRule.java b/subprojects/core/src/main/groovy/org/gradle/model/internal/rules/ReflectiveRule.java
deleted file mode 100644
index 504578e..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/model/internal/rules/ReflectiveRule.java
+++ /dev/null
@@ -1,207 +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.model.internal.rules;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.Nullable;
-import org.gradle.api.Transformer;
-import org.gradle.api.specs.Spec;
-import org.gradle.internal.UncheckedException;
-import org.gradle.model.ModelFinalizer;
-import org.gradle.model.ModelPath;
-import org.gradle.model.ModelRule;
-import org.gradle.model.Path;
-import org.gradle.model.internal.Inputs;
-import org.gradle.model.internal.ModelCreationListener;
-import org.gradle.model.internal.ModelMutator;
-import org.gradle.model.internal.ModelRegistry;
-import org.gradle.util.CollectionUtils;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static java.lang.reflect.Modifier.isPrivate;
-import static java.lang.reflect.Modifier.isStatic;
-import static org.gradle.util.CollectionUtils.*;
-
-public abstract class ReflectiveRule {
-
-    public static void rule(final ModelRegistry modelRegistry, final ModelRule modelRule) {
-        final Method bindingMethod = findBindingMethod(modelRule);
-        final List<BindableParameter<?>> initialBindings = bindings(bindingMethod);
-
-        boolean unsatisfied = CollectionUtils.any(initialBindings, new Spec<BindableParameter<?>>() {
-            public boolean isSatisfiedBy(BindableParameter<?> element) {
-                return element.getPath() == null;
-            }
-        });
-
-        if (unsatisfied) {
-            modelRegistry.registerListener(new ModelCreationListener() {
-
-                private List<BindableParameter<?>> bindings = initialBindings;
-
-                public boolean onCreate(ModelPath path, Class<?> type) {
-                    ImmutableList.Builder<BindableParameter<?>> bindingsBuilder = ImmutableList.builder();
-
-                    boolean unsatisfied = false;
-
-                    for (BindableParameter<?> binding : bindings) {
-                        if (binding.getPath() == null) {
-                            if (binding.getType().isAssignableFrom(type)) {
-                                bindingsBuilder.add(copyBindingWithPath(path, binding));
-                                continue;
-                            } else {
-                                unsatisfied = true;
-                            }
-                        }
-
-                        bindingsBuilder.add(binding);
-                    }
-
-                    bindings = bindingsBuilder.build();
-
-                    if (unsatisfied) {
-                        return false;
-                    } else {
-                        registerMutator(modelRegistry, modelRule, bindingMethod, bindings);
-                        return true;
-                    }
-                }
-            });
-        } else {
-            registerMutator(modelRegistry, modelRule, bindingMethod, initialBindings);
-        }
-    }
-
-    private static void registerMutator(ModelRegistry modelRegistry, ModelRule modelRule, final Method bindingMethod, final List<BindableParameter<?>> bindings) {
-        BindableParameter<?> first = bindings.get(0);
-        List<BindableParameter<?>> tail = bindings.subList(1, bindings.size());
-        ModelMutator<?> modelMutator = toMutator(modelRule, bindingMethod, first, tail);
-
-        String path = first.getPath().toString();
-        List<String> bindingPaths = CollectionUtils.collect(tail, new Transformer<String, BindableParameter<?>>() {
-            public String transform(BindableParameter<?> bindableParameter) {
-                return bindableParameter.getPath().toString();
-            }
-        });
-
-        if (modelRule instanceof ModelFinalizer) {
-            modelRegistry.finalize(path, bindingPaths, modelMutator);
-        } else {
-            modelRegistry.mutate(path, bindingPaths, modelMutator);
-        }
-    }
-
-    private static <T> ModelMutator<T> toMutator(final ModelRule modelRule, final Method bindingMethod, final BindableParameter<T> first, final List<BindableParameter<?>> tail) {
-        return new ModelMutator<T>() {
-            public Class<T> getType() {
-                return first.getType();
-            }
-
-            public void mutate(T object, Inputs inputs) {
-                Object[] args = new Object[1 + tail.size()];
-                args[0] = object;
-                for (int i = 0; i < inputs.size(); ++i) {
-                    args[i + 1] = inputs.get(i, tail.get(i).getType());
-                }
-
-                bindingMethod.setAccessible(true);
-
-                try {
-                    bindingMethod.invoke(modelRule, args);
-                } catch (Exception e) {
-                    Throwable t = e;
-                    if (t instanceof InvocationTargetException) {
-                        t = e.getCause();
-                    }
-
-                    UncheckedException.throwAsUncheckedException(t);
-                }
-            }
-        };
-    }
-
-    private static <T> BindableParameter<T> copyBindingWithPath(ModelPath path, BindableParameter<T> binding) {
-        return new BindableParameter<T>(path, binding.getType());
-    }
-
-    public static Method findBindingMethod(Object object) {
-        Class<?> objectClass = object.getClass();
-        List<Method> declaredMethods = filter(Arrays.asList(objectClass.getDeclaredMethods()), new Spec<Method>() {
-            public boolean isSatisfiedBy(Method element) {
-                int modifiers = element.getModifiers();
-                return !isPrivate(modifiers) && !isStatic(modifiers) && !element.isSynthetic();
-            }
-        });
-        if (declaredMethods.size() != 1) {
-            throw new IllegalArgumentException(objectClass + " rule must have exactly 1 public method, has: " + join(", ", toStringList(declaredMethods)));
-        }
-
-        return declaredMethods.get(0);
-    }
-
-    private static List<BindableParameter<?>> bindings(Method method) {
-        return bindings(method.getParameterTypes(), method.getParameterAnnotations());
-    }
-
-    static List<BindableParameter<?>> bindings(Class[] types, Annotation[][] annotations) {
-        ImmutableList.Builder<BindableParameter<?>> inputBindingBuilder = ImmutableList.builder();
-
-        for (int i = 0; i < types.length; i++) {
-            Class<?> paramType = types[i];
-            Annotation[] paramAnnotations = annotations[i];
-
-            inputBindingBuilder.add(binding(paramType, paramAnnotations));
-        }
-
-        return inputBindingBuilder.build();
-    }
-
-    private static <T> BindableParameter binding(Class<T> type, Annotation[] annotations) {
-        Path pathAnnotation = (Path) findFirst(annotations, new Spec<Annotation>() {
-            public boolean isSatisfiedBy(Annotation element) {
-                return element.annotationType().equals(Path.class);
-            }
-        });
-        String path = pathAnnotation == null ? null : pathAnnotation.value();
-        return new BindableParameter<T>(path == null ? null : ModelPath.path(path), type);
-    }
-
-
-    public static class BindableParameter<T> {
-
-        private final ModelPath path;
-        private final Class<T> type;
-
-        public BindableParameter(@Nullable ModelPath path, Class<T> type) {
-            this.path = path;
-            this.type = type;
-        }
-
-        public ModelPath getPath() {
-            return path;
-        }
-
-        public Class<T> getType() {
-            return type;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/PluginHandler.java b/subprojects/core/src/main/groovy/org/gradle/plugin/PluginHandler.java
deleted file mode 100644
index 19b9aaf..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/PluginHandler.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.plugin;
-
-import org.gradle.api.Incubating;
-
-import java.util.Map;
-
-/**
- * A manager of plugins.
- */
- at Incubating
-public interface PluginHandler {
-
-    void apply(Map<String, ?> attributes);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/DefaultPluginHandler.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/DefaultPluginHandler.java
deleted file mode 100644
index 45e5801..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/DefaultPluginHandler.java
+++ /dev/null
@@ -1,48 +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.plugin.internal;
-
-import org.gradle.api.tasks.Optional;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.plugin.PluginHandler;
-import org.gradle.plugin.resolve.internal.DefaultPluginRequest;
-import org.gradle.plugin.resolve.internal.PluginRequest;
-
-import java.util.List;
-import java.util.Map;
-
-public class DefaultPluginHandler implements PluginHandler {
-
-    private final List<PluginRequest> pluginRequests;
-
-    public DefaultPluginHandler(List<PluginRequest> pluginRequests) {
-        this.pluginRequests = pluginRequests;
-    }
-
-    private static class PluginRequestNotationParser extends MapNotationParser<PluginRequest> {
-        protected PluginRequest parseMap(@MapKey("plugin") String id, @MapKey("version") @Optional String version) {
-            return version == null ? new DefaultPluginRequest(id) : new DefaultPluginRequest(id, version);
-        }
-    }
-
-    public void apply(Map<String, ?> attributes) {
-        PluginRequest pluginRequest = new PluginRequestNotationParser().parseType(attributes);
-        pluginRequests.add(pluginRequest);
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/InvalidPluginIdException.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/InvalidPluginIdException.java
new file mode 100644
index 0000000..d54d048
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/InvalidPluginIdException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.internal;
+
+import org.gradle.api.GradleException;
+
+public class InvalidPluginIdException extends GradleException {
+
+    private final String reason;
+
+    public InvalidPluginIdException(String pluginId, String reason) {
+        super(String.format("plugin id '%s' is invalid: %s", pluginId, reason));
+        this.reason = reason;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/NonPluggableTargetPluginHandler.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/NonPluggableTargetPluginHandler.java
deleted file mode 100644
index 068023f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/NonPluggableTargetPluginHandler.java
+++ /dev/null
@@ -1,38 +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.plugin.internal;
-
-import org.gradle.plugin.PluginHandler;
-
-import java.util.Map;
-
-public class NonPluggableTargetPluginHandler implements PluginHandler {
-
-    private final Object target;
-
-    public NonPluggableTargetPluginHandler(Object target) {
-        this.target = target;
-    }
-
-    public void apply(Map<String, ?> attributes) {
-        throw fail();
-    }
-
-    private RuntimeException fail() {
-        return new UnsupportedOperationException("Script target " + target + " cannot have plugins applied to it");
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginId.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginId.java
new file mode 100644
index 0000000..e8c9282
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginId.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.internal;
+
+import com.google.common.base.CharMatcher;
+import org.gradle.api.Nullable;
+
+import static com.google.common.base.CharMatcher.anyOf;
+import static com.google.common.base.CharMatcher.inRange;
+
+public class PluginId {
+
+    public static final String ID_SEPARATOR_ON_START_OR_END = "cannot begin or end with '" + PluginId.SEPARATOR + "'";
+    public static final String DOUBLE_SEPARATOR = "cannot contain '" + PluginId.SEPARATOR + PluginId.SEPARATOR + "'";
+
+    public static final String PLUGIN_ID_VALID_CHARS_DESCRIPTION = "ASCII alphanumeric characters, '.', '_' and '-'";
+    public static final CharMatcher INVALID_PLUGIN_ID_CHAR_MATCHER = inRange('a', 'z')
+            .or(inRange('A', 'Z'))
+            .or(inRange('0', '9'))
+            .or(anyOf(".-_"))
+            .negate();
+    public static final String SEPARATOR = ".";
+
+    private final String value;
+
+    private PluginId(String value) {
+        this.value = value;
+    }
+
+    public static PluginId of(String value) throws InvalidPluginIdException {
+        validate(value);
+        return new PluginId(value);
+    }
+
+    public static PluginId unvalidated(String value) {
+        return new PluginId(value);
+    }
+
+    public static void validate(String value) throws InvalidPluginIdException {
+        if (value.startsWith(SEPARATOR) || value.endsWith(SEPARATOR)) {
+            throw new InvalidPluginIdException(value, ID_SEPARATOR_ON_START_OR_END);
+        } else if (value.contains(PluginId.SEPARATOR + PluginId.SEPARATOR)) {
+            throw new InvalidPluginIdException(value, DOUBLE_SEPARATOR);
+        } else {
+            int invalidCharIndex = PluginId.INVALID_PLUGIN_ID_CHAR_MATCHER.indexIn(value);
+            if (invalidCharIndex >= 0) {
+                char invalidChar = value.charAt(invalidCharIndex);
+                throw new InvalidPluginIdException(value, invalidPluginIdCharMessage(invalidChar));
+            }
+        }
+    }
+
+    public static String invalidPluginIdCharMessage(char invalidChar) {
+        return "Plugin id contains invalid char '" + invalidChar + "' (only " + PluginId.PLUGIN_ID_VALID_CHARS_DESCRIPTION + " characters are valid)";
+    }
+
+    public boolean isQualified() {
+        return value.contains(PluginId.SEPARATOR);
+    }
+
+    public PluginId maybeQualify(String qualification) {
+        return isQualified() ? this : new PluginId(qualification + PluginId.SEPARATOR + value);
+    }
+
+    @Nullable
+    public String getNamespace() {
+        return isQualified() ? value.substring(0, value.lastIndexOf(SEPARATOR)) : null;
+    }
+
+    public boolean inNamespace(String namespace) {
+        return isQualified() && getNamespace().equals(namespace);
+    }
+
+    public String getName() {
+        return isQualified() ? value.substring(value.lastIndexOf(PluginId.SEPARATOR) + 1) : value;
+    }
+
+    public PluginId getUnqualified() {
+        return isQualified() ? new PluginId(getName()) : this;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+
+    public String asString() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        PluginId pluginId = (PluginId) o;
+
+        return value.equals(pluginId.value);
+
+    }
+
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginRequestApplicator.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginRequestApplicator.java
deleted file mode 100644
index 9c8a2a6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginRequestApplicator.java
+++ /dev/null
@@ -1,50 +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.plugin.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.plugins.UnknownPluginException;
-import org.gradle.plugin.resolve.internal.PluginRequest;
-import org.gradle.plugin.resolve.internal.PluginResolution;
-import org.gradle.plugin.resolve.internal.PluginResolver;
-
-public class PluginRequestApplicator {
-
-    private final PluginResolver pluginResolver;
-    private final Action<? super PluginResolution> pluginResolutionHandler;
-
-    public PluginRequestApplicator(PluginResolver pluginResolver, Action<? super PluginResolution> pluginResolutionHandler) {
-        this.pluginResolver = pluginResolver;
-        this.pluginResolutionHandler = pluginResolutionHandler;
-    }
-
-    public void applyPlugin(Iterable<? extends PluginRequest> requests) {
-        for (PluginRequest request : requests) {
-            applyPlugin(request);
-        }
-    }
-
-    public void applyPlugin(PluginRequest request) {
-        PluginResolution resolution = pluginResolver.resolve(request);
-        if (resolution == null) {
-            throw new UnknownPluginException("Cannot resolve plugin request " + request + " from " + pluginResolver.getDescriptionForNotFoundMessage());
-        }
-
-        pluginResolutionHandler.execute(resolution);
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolutionApplicator.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolutionApplicator.java
deleted file mode 100644
index 6d3bae7..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolutionApplicator.java
+++ /dev/null
@@ -1,39 +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.plugin.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.plugins.PluginAware;
-import org.gradle.plugin.resolve.internal.PluginResolution;
-
-public class PluginResolutionApplicator implements Action<PluginResolution> {
-
-    private final PluginAware target;
-    private final ClassLoaderScope classLoaderScope;
-
-    public PluginResolutionApplicator(PluginAware target, ClassLoaderScope classLoaderScope) {
-        this.target = target;
-        this.classLoaderScope = classLoaderScope;
-    }
-
-    public void execute(PluginResolution pluginResolution) {
-        Class<? extends Plugin> pluginClass = pluginResolution.resolve(classLoaderScope);
-        target.getPlugins().apply(pluginClass);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolverFactory.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolverFactory.java
deleted file mode 100644
index 989b74c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolverFactory.java
+++ /dev/null
@@ -1,95 +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.plugin.internal;
-
-import org.gradle.api.UnknownProjectException;
-import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.artifacts.DependencyManagementServices;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.plugins.ClassloaderBackedPluginDescriptorLocator;
-import org.gradle.api.internal.plugins.PluginDescriptorLocator;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.cache.CacheRepository;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.plugin.resolve.internal.CompositePluginResolver;
-import org.gradle.plugin.resolve.internal.NotInPluginRegistryPluginResolverCheck;
-import org.gradle.plugin.resolve.internal.PluginRegistryPluginResolver;
-import org.gradle.plugin.resolve.internal.PluginResolver;
-
-import java.util.LinkedList;
-import java.util.List;
-
-import static org.gradle.plugin.internal.PluginResolvers.jcenterGradleOfficial;
-
-public class PluginResolverFactory {
-
-    private final PluginRegistry pluginRegistry;
-    private final Instantiator instantiator;
-    private final DependencyManagementServices dependencyManagementServices;
-    private final FileResolver fileResolver;
-    private final DependencyMetaDataProvider dependencyMetaDataProvider;
-    private final DocumentationRegistry documentationRegistry;
-    private final CacheRepository cacheRepository;
-
-    private final ProjectFinder projectFinder = new ProjectFinder() {
-        public ProjectInternal getProject(String path) {
-            throw new UnknownProjectException("Cannot use project dependencies in a plugin resolution definition.");
-        }
-    };
-
-    public PluginResolverFactory(PluginRegistry pluginRegistry, Instantiator instantiator, DependencyManagementServices dependencyManagementServices, FileResolver fileResolver, DependencyMetaDataProvider dependencyMetaDataProvider, DocumentationRegistry documentationRegistry, CacheRepository cacheRepository) {
-        this.pluginRegistry = pluginRegistry;
-        this.instantiator = instantiator;
-        this.dependencyManagementServices = dependencyManagementServices;
-        this.fileResolver = fileResolver;
-        this.dependencyMetaDataProvider = dependencyMetaDataProvider;
-        this.documentationRegistry = documentationRegistry;
-        this.cacheRepository = cacheRepository;
-    }
-
-    public PluginResolver createPluginResolver(ClassLoader scriptClassLoader) {
-        List<PluginResolver> resolvers = new LinkedList<PluginResolver>();
-        addDefaultResolvers(resolvers);
-        CompositePluginResolver compositePluginResolver = new CompositePluginResolver(resolvers);
-
-        PluginDescriptorLocator scriptClasspathPluginDescriptorLocator = new ClassloaderBackedPluginDescriptorLocator(scriptClassLoader);
-        PluginResolver notAlreadyOnClasspathCheck = new NotInPluginRegistryPluginResolverCheck(compositePluginResolver, pluginRegistry, scriptClasspathPluginDescriptorLocator);
-
-        return notAlreadyOnClasspathCheck;
-    }
-
-    private void addDefaultResolvers(List<PluginResolver> resolvers) {
-        resolvers.add(new PluginRegistryPluginResolver(documentationRegistry, pluginRegistry));
-        resolvers.add(jcenterGradleOfficial(instantiator, createDependencyResolutionServices(), cacheRepository));
-    }
-
-    private DependencyResolutionServices createDependencyResolutionServices() {
-        return dependencyManagementServices.create(fileResolver, dependencyMetaDataProvider, projectFinder, new BasicDomainObjectContext());
-    }
-
-    private static class BasicDomainObjectContext implements DomainObjectContext {
-        public String absoluteProjectPath(String name) {
-            return name;
-        }
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolvers.java b/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolvers.java
deleted file mode 100644
index ae57eb6..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/internal/PluginResolvers.java
+++ /dev/null
@@ -1,59 +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.plugin.internal;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.cache.CacheRepository;
-import org.gradle.cache.PersistentCache;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.cache.PersistentIndexedCacheParameters;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.cache.internal.filelock.LockOptionsBuilder;
-import org.gradle.internal.Factory;
-import org.gradle.internal.Supplier;
-import org.gradle.internal.Suppliers;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.messaging.serialize.BaseSerializerFactory;
-import org.gradle.plugin.resolve.internal.*;
-
-public abstract class PluginResolvers {
-
-    public static PluginResolver jcenterGradleOfficial(Instantiator instantiator, DependencyResolutionServices dependencyResolutionServices, final CacheRepository cacheRepository) {
-        Supplier<PersistentIndexedCache<PluginRequest, String>> cacheSupplier = Suppliers.wrap(
-                Suppliers.ofQuietlyClosed(new Factory<PersistentCache>() {
-                    public PersistentCache create() {
-                        return cacheRepository.cache("plugins").withLockOptions(LockOptionsBuilder.mode(FileLockManager.LockMode.Exclusive)).open();
-                    }
-                }),
-                new Transformer<PersistentIndexedCache<PluginRequest, String>, PersistentCache>() {
-                    public PersistentIndexedCache<PluginRequest, String> transform(PersistentCache original) {
-                        PersistentIndexedCacheParameters<PluginRequest, String> cacheParams = new PersistentIndexedCacheParameters<PluginRequest, String>("jcenter", new PluginRequestSerializer(), BaseSerializerFactory.STRING_SERIALIZER);
-                        return original.createCache(cacheParams);
-                    }
-                });
-
-        final JCenterPluginMapper mapper = new JCenterPluginMapper(cacheSupplier);
-
-        return new ModuleMappingPluginResolver("jcenter plugin resolver", dependencyResolutionServices, instantiator, mapper, new JCenterRepositoryConfigurer()) {
-            @Override
-            public String getDescriptionForNotFoundMessage() {
-                return String.format("Gradle Bintray Plugin Repository (listing: %s)", mapper.getBintrayRepoUrl());
-            }
-        };
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ClassPathPluginResolution.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ClassPathPluginResolution.java
deleted file mode 100644
index dd16b46..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ClassPathPluginResolution.java
+++ /dev/null
@@ -1,46 +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.plugin.resolve.internal;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.internal.plugins.DefaultPluginRegistry;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.internal.Factory;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.reflect.Instantiator;
-
-class ClassPathPluginResolution implements PluginResolution {
-
-    private final String pluginId;
-    private final Instantiator instantiator;
-    private final Factory<? extends ClassPath> classPathFactory;
-
-    public ClassPathPluginResolution(Instantiator instantiator, String pluginId, Factory<? extends ClassPath> classPathFactory) {
-        this.pluginId = pluginId;
-        this.instantiator = instantiator;
-        this.classPathFactory = classPathFactory;
-    }
-
-    public Class<? extends Plugin> resolve(ClassLoaderScope classLoaderScope) {
-        ClassPath classPath = classPathFactory.create();
-        ClassLoader classLoader = classLoaderScope.addLocal(classPath);
-        PluginRegistry pluginRegistry = new DefaultPluginRegistry(classLoader, instantiator);
-        Class<? extends Plugin> typeForId = pluginRegistry.getTypeForId(pluginId);
-        return typeForId;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/CompositePluginResolver.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/CompositePluginResolver.java
deleted file mode 100644
index f488fcb..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/CompositePluginResolver.java
+++ /dev/null
@@ -1,48 +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.plugin.resolve.internal;
-
-import java.util.List;
-
-public class CompositePluginResolver implements PluginResolver {
-
-    private final List<PluginResolver> repositories;
-
-    public CompositePluginResolver(List<PluginResolver> repositories) {
-        this.repositories = repositories;
-    }
-
-    public PluginResolution resolve(PluginRequest pluginRequest) {
-        PluginResolution resolution = null;
-        for (PluginResolver repository : repositories) {
-            resolution = repository.resolve(pluginRequest);
-            if (resolution != null) {
-                break;
-            }
-        }
-
-        return resolution;
-    }
-
-    public String getDescriptionForNotFoundMessage() {
-        StringBuilder sb = new StringBuilder("plugin repositories:");
-        for (PluginResolver repository : repositories) {
-            sb.append("\n - ").append(repository.getDescriptionForNotFoundMessage());
-        }
-        return sb.toString();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DefaultPluginRequest.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DefaultPluginRequest.java
deleted file mode 100644
index db0c2bd..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DefaultPluginRequest.java
+++ /dev/null
@@ -1,78 +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.plugin.resolve.internal;
-
-public class DefaultPluginRequest implements PluginRequest {
-
-    private final String id;
-    private final String version;
-
-    public DefaultPluginRequest(String id) {
-        this.id = id;
-        this.version = null;
-    }
-
-    public DefaultPluginRequest(String id, String version) {
-        this.id = id;
-        this.version = version;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    @Override
-    public String toString() {
-        if (version == null) {
-            return String.format("[plugin: '%s']", id);
-        } else {
-            return String.format("[plugin: '%s', version: '%s']", id, version);
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultPluginRequest that = (DefaultPluginRequest) o;
-
-        if (!id.equals(that.id)) {
-            return false;
-        }
-        if (version != null ? !version.equals(that.version) : that.version != null) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = id.hashCode();
-        result = 31 * result + (version != null ? version.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DependencyResolvingClasspathProvider.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DependencyResolvingClasspathProvider.java
deleted file mode 100644
index 51c26ee..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/DependencyResolvingClasspathProvider.java
+++ /dev/null
@@ -1,46 +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.plugin.resolve.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.internal.Factory;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.classpath.DefaultClassPath;
-
-public class DependencyResolvingClasspathProvider implements Factory<ClassPath> {
-
-    private final DependencyResolutionServices dependencyResolutionServices;
-    private final Dependency dependency;
-    private final Action<? super RepositoryHandler> repositoriesConfigurer;
-
-    public DependencyResolvingClasspathProvider(DependencyResolutionServices dependencyResolutionServices, Dependency dependency, Action<? super RepositoryHandler> repositoriesConfigurer) {
-        this.dependencyResolutionServices = dependencyResolutionServices;
-        this.dependency = dependency;
-        this.repositoriesConfigurer = repositoriesConfigurer;
-    }
-
-    public ClassPath create() {
-        Configuration configuration = dependencyResolutionServices.getConfigurationContainer().detachedConfiguration(dependency);
-        repositoriesConfigurer.execute(dependencyResolutionServices.getResolveRepositoryHandler());
-        return new DefaultClassPath(configuration.resolve());
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/InvalidPluginRequestException.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/InvalidPluginRequestException.java
deleted file mode 100644
index 2c82e5b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/InvalidPluginRequestException.java
+++ /dev/null
@@ -1,25 +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.plugin.resolve.internal;
-
-import org.gradle.api.GradleException;
-
-public class InvalidPluginRequestException extends GradleException {
-    public InvalidPluginRequestException(String message) {
-        super(message);
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterPluginMapper.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterPluginMapper.java
deleted file mode 100644
index f8719c3..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterPluginMapper.java
+++ /dev/null
@@ -1,113 +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.plugin.resolve.internal;
-
-import com.jfrog.bintray.client.api.handle.Bintray;
-import com.jfrog.bintray.client.api.model.Pkg;
-import com.jfrog.bintray.client.impl.BintrayClient;
-import org.gradle.api.Transformer;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.internal.Supplier;
-
-import java.util.List;
-
-public class JCenterPluginMapper implements ModuleMappingPluginResolver.Mapper {
-
-    public static final String BINTRAY_API_OVERRIDE_URL_PROPERTY = JCenterPluginMapper.class.getName() + ".bintray.override";
-
-    public static final String GRADLE_PLUGINS_ORG = "gradle-plugins-development";
-    public static final String GRADLE_PLUGINS_REPO = "gradle-plugins";
-    public static final String PLUGIN_ID_ATTRIBUTE_NAME = "gradle-plugin-id";
-
-    private static final String NOT_FOUND = "";
-
-    private final Supplier<PersistentIndexedCache<PluginRequest, String>> cacheSupplier;
-
-    public JCenterPluginMapper(Supplier<PersistentIndexedCache<PluginRequest, String>> cacheSupplier) {
-        this.cacheSupplier = cacheSupplier;
-    }
-
-    public Dependency map(final PluginRequest request, DependencyHandler dependencyHandler) {
-        final String pluginId = request.getId();
-
-        String systemId = cacheSupplier.supplyTo(new Transformer<String, PersistentIndexedCache<PluginRequest, String>>() {
-            public String transform(PersistentIndexedCache<PluginRequest, String> cache) {
-                return doCacheAwareSearch(request, pluginId, cache);
-            }
-        });
-
-        if (systemId.equals(NOT_FOUND)) {
-            return null;
-        } else {
-            return dependencyHandler.create(systemId + ":" + request.getVersion());
-        }
-    }
-
-    private String doCacheAwareSearch(PluginRequest request, String pluginId, PersistentIndexedCache<PluginRequest, String> indexedCache) {
-        String cached = indexedCache.get(request);
-        if (cached != null) {
-            return cached;
-        }
-
-        Bintray bintrayClient = createBintrayClient();
-        List<Pkg> results = bintrayClient.
-                subject(GRADLE_PLUGINS_ORG).
-                repository(GRADLE_PLUGINS_REPO).
-                searchForPackage().
-                byAttributeName(PLUGIN_ID_ATTRIBUTE_NAME).
-                equals(pluginId).
-                search();
-
-        String systemId;
-
-        if (results.isEmpty()) {
-            systemId = NOT_FOUND;
-        } else if (request.getVersion() == null) {
-            throw new InvalidPluginRequestException(String.format("No version number supplied for plugin '%s'. A version number must be supplied for plugins resolved from '%s'.", pluginId, getBintrayRepoUrl()));
-        } else if (results.size() > 1) {
-            throw new InvalidPluginRequestException("Found more than one plugin for plugin id " + pluginId);
-        } else {
-            Pkg pluginPackage = results.get(0);
-            List<String> systemIds = pluginPackage.systemIds();
-            if (systemIds.isEmpty()) {
-                throw new InvalidPluginRequestException("No artifacts in maven layout found for plugin id" + pluginId);
-            }
-
-            systemId = systemIds.get(0);
-        }
-
-        indexedCache.put(request, systemId);
-        return systemId;
-    }
-
-    private Bintray createBintrayClient() {
-        String override = System.getProperty(BINTRAY_API_OVERRIDE_URL_PROPERTY);
-        if (override == null) {
-            return BintrayClient.create();
-        } else {
-            return BintrayClient.create(override, null, null);
-        }
-    }
-
-    public String getBintrayRepoUrl() {
-        return String.format("https://bintray.com/%s/%s", GRADLE_PLUGINS_ORG, GRADLE_PLUGINS_REPO);
-    }
-
-}
-
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterRepositoryConfigurer.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterRepositoryConfigurer.java
deleted file mode 100644
index c0b1691..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/JCenterRepositoryConfigurer.java
+++ /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.plugin.resolve.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-
-public class JCenterRepositoryConfigurer implements Action<RepositoryHandler> {
-    public void execute(RepositoryHandler repositories) {
-        repositories.jcenter();
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ModuleMappingPluginResolver.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ModuleMappingPluginResolver.java
deleted file mode 100644
index 8e3063b..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/ModuleMappingPluginResolver.java
+++ /dev/null
@@ -1,68 +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.plugin.resolve.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.Nullable;
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.dsl.DependencyHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.internal.artifacts.DependencyResolutionServices;
-import org.gradle.internal.Factory;
-import org.gradle.internal.classpath.ClassPath;
-import org.gradle.internal.reflect.Instantiator;
-
-public abstract class ModuleMappingPluginResolver implements PluginResolver {
-
-    private final String name;
-    private final DependencyResolutionServices dependencyResolutionServices;
-    private final Instantiator instantiator;
-    private final Mapper mapper;
-    private Action<? super RepositoryHandler> repositoriesConfigurer;
-
-    public interface Mapper {
-        @Nullable
-        Dependency map(PluginRequest request, DependencyHandler dependencyHandler);
-    }
-
-    public ModuleMappingPluginResolver(String name, DependencyResolutionServices dependencyResolutionServices, Instantiator instantiator, Mapper mapper, Action<? super RepositoryHandler> repositoriesConfigurer) {
-        this.name = name;
-        this.dependencyResolutionServices = dependencyResolutionServices;
-        this.instantiator = instantiator;
-        this.mapper = mapper;
-        this.repositoriesConfigurer = repositoriesConfigurer;
-    }
-
-    public PluginResolution resolve(final PluginRequest pluginRequest) {
-        final Dependency dependency = mapper.map(pluginRequest, dependencyResolutionServices.getDependencyHandler());
-        if (dependency == null) {
-            return null;
-        } else {
-            // TODO the dependency resolution config of this guy needs to be externalized
-            Factory<ClassPath> classPathFactory = new DependencyResolvingClasspathProvider(dependencyResolutionServices, dependency, repositoriesConfigurer);
-
-            return new ClassPathPluginResolution(instantiator, pluginRequest.getId(), classPathFactory);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getName() + "[" + name + "]";
-    }
-
-    public abstract String getDescriptionForNotFoundMessage();
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/NotInPluginRegistryPluginResolverCheck.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/NotInPluginRegistryPluginResolverCheck.java
deleted file mode 100644
index d48b484..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/NotInPluginRegistryPluginResolverCheck.java
+++ /dev/null
@@ -1,61 +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.plugin.resolve.internal;
-
-import org.gradle.api.internal.plugins.PluginDescriptor;
-import org.gradle.api.internal.plugins.PluginDescriptorLocator;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.plugins.UnknownPluginException;
-
-public class NotInPluginRegistryPluginResolverCheck implements PluginResolver {
-
-    private final PluginResolver delegate;
-    private final PluginRegistry corePluginRegistry;
-    private final PluginDescriptorLocator pluginDescriptorLocator;
-
-    public NotInPluginRegistryPluginResolverCheck(PluginResolver delegate, PluginRegistry corePluginRegistry, PluginDescriptorLocator pluginDescriptorLocator) {
-        this.delegate = delegate;
-        this.corePluginRegistry = corePluginRegistry;
-        this.pluginDescriptorLocator = pluginDescriptorLocator;
-    }
-
-    public PluginResolution resolve(PluginRequest pluginRequest) {
-        String pluginId = pluginRequest.getId();
-        PluginDescriptor pluginDescriptor = pluginDescriptorLocator.findPluginDescriptor(pluginId);
-        if (pluginDescriptor == null || isCorePlugin(pluginId)) {
-            return delegate.resolve(pluginRequest);
-        } else {
-            throw new InvalidPluginRequestException(
-                    String.format("Plugin '%s' is already on the script classpath (plugins on the script classpath cannot be used in a plugins {} block; move \"apply plugin: '%s'\" outside of the plugins {} block)", pluginId, pluginId)
-            );
-        }
-    }
-
-    private boolean isCorePlugin(String pluginId) {
-        try {
-            corePluginRegistry.getTypeForId(pluginId);
-            return true;
-        } catch (UnknownPluginException ignore) {
-            return false;
-        }
-    }
-
-    public String getDescriptionForNotFoundMessage() {
-        return delegate.getDescriptionForNotFoundMessage();
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRegistryPluginResolver.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRegistryPluginResolver.java
deleted file mode 100644
index 0465bb4..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRegistryPluginResolver.java
+++ /dev/null
@@ -1,56 +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.plugin.resolve.internal;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.api.internal.plugins.PluginRegistry;
-import org.gradle.api.plugins.UnknownPluginException;
-
-public class PluginRegistryPluginResolver implements PluginResolver {
-
-    private final DocumentationRegistry documentationRegistry;
-    private final PluginRegistry pluginRegistry;
-
-    public PluginRegistryPluginResolver(DocumentationRegistry documentationRegistry, PluginRegistry pluginRegistry) {
-        this.documentationRegistry = documentationRegistry;
-        this.pluginRegistry = pluginRegistry;
-    }
-
-    public PluginResolution resolve(PluginRequest pluginRequest) {
-        try {
-            Class<? extends Plugin> typeForId = pluginRegistry.getTypeForId(pluginRequest.getId());
-            if (pluginRequest.getVersion() != null) {
-                throw new InvalidPluginRequestException(
-                        "Plugin '" + pluginRequest.getId() + "' is a core Gradle plugin, which cannot be specified with a version number. "
-                        + "Such plugins are versioned as part of Gradle. Please remove the version number from the declaration.");
-            }
-            return new SimplePluginResolution(typeForId);
-        } catch (UnknownPluginException e) {
-            return null;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "PluginRegistryPluginResolver[" + pluginRegistry + "]";
-    }
-
-    public String getDescriptionForNotFoundMessage() {
-        return String.format("Gradle Distribution Plugins (listing: %s)", documentationRegistry.getDocumentationFor("standard_plugins"));
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequest.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequest.java
deleted file mode 100644
index 97ff8dc..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequest.java
+++ /dev/null
@@ -1,33 +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.plugin.resolve.internal;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Nullable;
-
-/**
- * Represents a request for a plugin.
- */
- at Incubating
-public interface PluginRequest {
-
-    String getId();
-
-    @Nullable
-    String getVersion();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequestSerializer.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequestSerializer.java
deleted file mode 100644
index 6921d4d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginRequestSerializer.java
+++ /dev/null
@@ -1,34 +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.plugin.resolve.internal;
-
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.Serializer;
-
-public class PluginRequestSerializer implements Serializer<PluginRequest> {
-
-    public PluginRequest read(Decoder decoder) throws Exception {
-        return new DefaultPluginRequest(decoder.readString(), decoder.readNullableString());
-    }
-
-    public void write(Encoder encoder, PluginRequest value) throws Exception {
-        encoder.writeString(value.getId());
-        encoder.writeNullableString(value.getVersion());
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolution.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolution.java
deleted file mode 100644
index 2e3314f..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolution.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.plugin.resolve.internal;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-
-/**
- * The result of attempting to resolve a plugin to a classpath.
- */
- at Incubating
-public interface PluginResolution {
-
-    Class<? extends Plugin> resolve(ClassLoaderScope classLoaderScope);
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolver.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolver.java
deleted file mode 100644
index 71a674d..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/PluginResolver.java
+++ /dev/null
@@ -1,33 +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.plugin.resolve.internal;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Nullable;
-
-/**
- * A repository of plugins.
- */
- at Incubating
-public interface PluginResolver {
-
-    @Nullable
-    PluginResolution resolve(PluginRequest pluginRequest);
-
-    String getDescriptionForNotFoundMessage();
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/SimplePluginResolution.java b/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/SimplePluginResolution.java
deleted file mode 100644
index 90980ed..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/plugin/resolve/internal/SimplePluginResolution.java
+++ /dev/null
@@ -1,34 +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.plugin.resolve.internal;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-
-public class SimplePluginResolution implements PluginResolution {
-
-    private final Class<? extends Plugin> pluginClass;
-
-    public SimplePluginResolution(Class<? extends Plugin> pluginClass) {
-        this.pluginClass = pluginClass;
-    }
-
-    public Class<? extends Plugin> resolve(ClassLoaderScope classLoaderScope) {
-        return pluginClass;
-    }
-
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependenciesSpec.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependenciesSpec.java
new file mode 100644
index 0000000..817b251
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependenciesSpec.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The DSL for declaring plugins to use in a script.
+ * <p>
+ * In a build script, the <code>plugins {}</code> script block API is of this type.
+ * That is, you can use this API in the body of the plugins script block to declare plugins to be used for the script.
+ * </p>
+ * <h3>Relationship with the apply() method</h3>
+ * <p>
+ * The <code>plugins {}</code> block serves a similar purpose to the {@link org.gradle.api.plugins.PluginAware#apply(java.util.Map)} method
+ * that can be used to apply a plugin directly to a {@code Project} object or similar.
+ * A key difference is that plugins applied via the <code>plugins {}</code> block are conceptually applied to the script, and by extension the script target.
+ * At this time there is no observable practical difference between the two approaches with regard to the end result.
+ * The <code>plugins {}</code> block is a new, incubating, Gradle feature that will evolve to offer benefits over the {@code apply()} approach.
+ * </p>
+ * <h3>Strict Syntax</h3>
+ * <p>
+ * The <code>plugins {}</code> block only allows a strict subset of the full build script programming language.
+ * Only the API of this type can be used, and values must be literal (e.g. constant strings, not variables).
+ * Moreover, the <code>plugins {}</code> block must be the first code of a build script.
+ * There is one exception to this, in that the {@code buildscript {}} block (used for declaring script dependencies) must precede it.
+ * </p>
+ * <p>
+ * This implies the following constraints:
+ * </p>
+ * <ul>
+ * <li>Only {@link #id(String)} method calls may be top level statements</li>
+ * <li>{@link #id(String)} calls may only be followed by a {@link PluginDependencySpec#version(String)} method call on the returned object</li>
+ * <li>{@link #id(String)} and {@link PluginDependencySpec#version(String)} methods must be called with a literal literal argument (i.e. not a variable)</li>
+ * <li>The <code>plugins {}</code> script block must follow any <code>buildscript {}</code> script block, but must precede all other logic in the script</li>
+ * </ul>
+ * <h3>Available Plugins</h3>
+ * <h4>Core Plugins</h4>
+ * <p>
+ * Core Gradle plugins are able to be applied using the <code>plugins {}</code> block.
+ * Core plugins must be specified without a version number, and can have a <i>qualified</i> or <i>unqualified</i> id.
+ * That is, the {@code java} plugin can be used via:
+ * </p>
+ * <pre>
+ * plugins {
+ *   id 'java'
+ * }
+ * </pre>
+ * <p>
+ * Or via:
+ * </p>
+ * <pre>
+ * plugins {
+ *   id 'org.gradle.java'
+ * }
+ * </pre>
+ * <p>
+ * Core Gradle plugins use the {@code org.gradle} namespace.
+ * </p>
+ * <p>
+ * For the list of available core plugins for a particular Gradle version, please consult the User Guide.
+ * </p>
+ * <h4>Community Plugins</h4>
+ * <p>
+ * Non-core plugins are available from the <a href="http://plugins.gradle.org">Gradle Plugin Portal</a>.
+ * These plugins are contributed by users of Gradle to extend Gradle's functionality.
+ * Visit <a href="http://plugins.gradle.org">plugins.gradle.org</a> to browse the available plugins and versions.
+ * </p>
+ * <p>
+ * To use a community plugin, the fully qualified id must be specified along with a version.
+ * </p>
+ */
+ at Incubating
+public interface PluginDependenciesSpec {
+
+    /**
+     * Add a dependency on the plugin with the given id.
+     * <p>
+     * <pre>
+     * plugins {
+     *     id "org.company.myplugin"
+     * }
+     * </pre>
+     * Further constraints (e.g. version number) can be specified by the methods of the return value.
+     * <pre>
+     * plugins {
+     *     id "org.company.myplugin" version "1.3"
+     * }
+     * </pre>
+     *
+     * @param id the id of the plugin to depend on
+     * @return a mutable plugin dependency specification that can be used to further refine the dependency
+     */
+    PluginDependencySpec id(String id);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependencySpec.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependencySpec.java
new file mode 100644
index 0000000..f9f5465
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/PluginDependencySpec.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.plugin.use;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+/**
+ * A mutable specification of a dependency on a plugin.
+ * <p>
+ * Can be used to specify the version of the plugin to use.
+ * </p>
+ * <p>
+ * See {@link PluginDependenciesSpec} for more information about declaring plugin dependencies.
+ * </p>
+ */
+ at Incubating
+public interface PluginDependencySpec {
+
+    /**
+     * Specify the version of the plugin to depend on.
+     * <p>
+     * <pre>
+     * plugins {
+     *     id "org.company.myplugin" version "1.0"
+     * }
+     * </pre>
+     * <p>
+     * By default, dependencies have no (i.e. {@code null}) version.
+     * <p>
+     * Core plugins must not include a version number specification.
+     * Community plugins must include a version number specification.
+     *
+     * @param version the version string ({@code null} for no specified version, which is the default)
+     */
+    void version(@Nullable String version);
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequest.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequest.java
new file mode 100644
index 0000000..1758f28
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.plugin.internal.PluginId;
+
+public class DefaultPluginRequest implements PluginRequest {
+
+    private final PluginId id;
+    private final String version;
+    private final int lineNumber;
+    private final String scriptDisplayName;
+
+    public DefaultPluginRequest(String id, String version, int lineNumber, ScriptSource scriptSource) {
+        this(PluginId.of(id), version, lineNumber, scriptSource);
+    }
+
+    public DefaultPluginRequest(PluginId id, String version, int lineNumber, ScriptSource scriptSource) {
+        this(id, version, lineNumber, scriptSource.getDisplayName());
+    }
+
+    public DefaultPluginRequest(String id, String version, int lineNumber, String scriptDisplayName) {
+        this(PluginId.of(id), version, lineNumber, scriptDisplayName);
+    }
+
+    public DefaultPluginRequest(PluginId id, String version, int lineNumber, String scriptDisplayName) {
+        this.id = id;
+        this.version = version;
+        this.lineNumber = lineNumber;
+        this.scriptDisplayName = scriptDisplayName;
+    }
+
+    public PluginId getId() {
+        return id;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public String getScriptDisplayName() {
+        return scriptDisplayName;
+    }
+
+    @Override
+    public String toString() {
+        if (version == null) {
+            return String.format("[id: '%s']", id);
+        } else {
+            return String.format("[id: '%s', version: '%s']", id, version);
+        }
+    }
+
+    public String getDisplayName() {
+        return toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultPluginRequest that = (DefaultPluginRequest) o;
+
+        if (!id.equals(that.id)) {
+            return false;
+        }
+        if (version != null ? !version.equals(that.version) : that.version != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id.hashCode();
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        return result;
+    }
+
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequests.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequests.java
new file mode 100644
index 0000000..34e643c
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/DefaultPluginRequests.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class DefaultPluginRequests implements PluginRequests {
+
+    private final List<PluginRequest> requests;
+
+    public DefaultPluginRequests(List<PluginRequest> requests) {
+        this.requests = requests;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return requests.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return requests.size();
+    }
+
+    @Override
+    public Iterator<PluginRequest> iterator() {
+        return requests.iterator();
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/InvalidPluginRequestException.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/InvalidPluginRequestException.java
new file mode 100644
index 0000000..58e6d2f
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/InvalidPluginRequestException.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.plugin.use.internal;
+
+import org.gradle.api.GradleException;
+
+public class InvalidPluginRequestException extends GradleException {
+    private final PluginRequest pluginRequest;
+
+    public InvalidPluginRequestException(PluginRequest pluginRequest, String message) {
+        super(message);
+        this.pluginRequest = pluginRequest;
+    }
+
+    public PluginRequest getPluginRequest() {
+        return pluginRequest;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequest.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequest.java
new file mode 100644
index 0000000..38bafa4
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequest.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.plugin.use.internal;
+
+import org.gradle.api.Nullable;
+import org.gradle.plugin.internal.PluginId;
+
+public interface PluginRequest {
+
+    PluginId getId();
+
+    @Nullable
+    String getVersion();
+
+    int getLineNumber();
+
+    String getScriptDisplayName();
+
+    String getDisplayName();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestApplicator.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestApplicator.java
new file mode 100644
index 0000000..0f2fe74
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestApplicator.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.internal.plugins.PluginManagerInternal;
+
+// Implementation is provided by 'plugin-use' module
+public interface PluginRequestApplicator {
+    void applyPlugins(PluginRequests requests, ScriptHandler scriptHandler, PluginManagerInternal target, ClassLoaderScope classLoaderScope);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestCollector.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestCollector.java
new file mode 100644
index 0000000..ffc37b1
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestCollector.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.plugin.use.internal;
+
+import com.google.common.collect.ListMultimap;
+import org.gradle.api.Transformer;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.exceptions.LocationAwareException;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.PluginDependenciesSpec;
+import org.gradle.plugin.use.PluginDependencySpec;
+import org.gradle.util.CollectionUtils;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.gradle.util.CollectionUtils.collect;
+
+/**
+ * The real delegate of the plugins {} block.
+ *
+ * The PluginUseScriptBlockTransformer interacts with this type.
+ */
+public class PluginRequestCollector {
+
+    private final ScriptSource scriptSource;
+
+    public PluginRequestCollector(ScriptSource scriptSource) {
+        this.scriptSource = scriptSource;
+    }
+
+    private static class DependencySpecImpl implements PluginDependencySpec {
+        private final PluginId id;
+        private String version;
+        private final int lineNumber;
+
+        private DependencySpecImpl(String id, int lineNumber) {
+            this.id = PluginId.of(id);
+            this.lineNumber = lineNumber;
+        }
+
+        public void version(String version) {
+            this.version = version;
+        }
+    }
+
+    private final List<DependencySpecImpl> specs = new LinkedList<DependencySpecImpl>();
+
+    public PluginDependenciesSpec createSpec(final int lineNumber) {
+        return new PluginDependenciesSpec() {
+            public PluginDependencySpec id(String id) {
+                DependencySpecImpl spec = new DependencySpecImpl(id, lineNumber);
+                specs.add(spec);
+                return spec;
+            }
+        };
+    }
+
+    public List<PluginRequest> getRequests() {
+        List<PluginRequest> pluginRequests = collect(specs, new Transformer<PluginRequest, DependencySpecImpl>() {
+            public PluginRequest transform(DependencySpecImpl original) {
+                return new DefaultPluginRequest(original.id, original.version, original.lineNumber, scriptSource);
+            }
+        });
+
+        ListMultimap<PluginId, PluginRequest> groupedById = CollectionUtils.groupBy(pluginRequests, new Transformer<PluginId, PluginRequest>() {
+            public PluginId transform(PluginRequest pluginRequest) {
+                return pluginRequest.getId();
+            }
+        });
+
+        // Check for duplicates
+        for (PluginId key : groupedById.keySet()) {
+            List<PluginRequest> pluginRequestsForId = groupedById.get(key);
+            if (pluginRequestsForId.size() > 1) {
+                PluginRequest first = pluginRequests.get(0);
+                PluginRequest second = pluginRequests.get(1);
+
+                InvalidPluginRequestException exception = new InvalidPluginRequestException(second, "Plugin with id '" + key + "' was already requested at line " + first.getLineNumber());
+                throw new LocationAwareException(exception, second.getScriptDisplayName(), second.getLineNumber());
+            }
+        }
+
+        return pluginRequests;
+    }
+
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequests.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequests.java
new file mode 100644
index 0000000..b2e35d6
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequests.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+public interface PluginRequests extends Iterable<PluginRequest> {
+
+    boolean isEmpty();
+
+    int size();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializer.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializer.java
new file mode 100644
index 0000000..e76b531
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.plugin.internal.PluginId;
+
+import java.util.List;
+
+public class PluginRequestsSerializer implements Serializer<PluginRequests> {
+
+    public static final Serializer<PluginRequests> INSTANCE = new PluginRequestsSerializer();
+
+    @Override
+    public PluginRequests read(Decoder decoder) throws Exception {
+        int requestCount = decoder.readInt();
+        List<PluginRequest> requests = Lists.newArrayListWithCapacity(requestCount);
+        for (int i = 0; i < requestCount; i++) {
+            PluginId pluginId = PluginId.unvalidated(decoder.readString());
+            String version = decoder.readNullableString();
+            int lineNumber = decoder.readInt();
+            String scriptDisplayName = decoder.readString();
+
+            requests.add(i, new DefaultPluginRequest(pluginId, version, lineNumber, scriptDisplayName));
+        }
+        return new DefaultPluginRequests(requests);
+    }
+
+    @Override
+    public void write(Encoder encoder, PluginRequests requests) throws Exception {
+        encoder.writeInt(requests.size());
+        for (PluginRequest request : requests) {
+            encoder.writeString(request.getId().asString());
+            encoder.writeNullableString(request.getVersion());
+            encoder.writeInt(request.getLineNumber());
+            encoder.writeString(request.getScriptDisplayName());
+        }
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataExtractor.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataExtractor.java
new file mode 100644
index 0000000..ffd6ba9
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataExtractor.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.groovy.scripts.internal.RestrictiveCodeVisitor;
+import org.gradle.groovy.scripts.internal.ScriptBlock;
+import org.gradle.plugin.internal.InvalidPluginIdException;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.PluginDependencySpec;
+
+import static org.gradle.groovy.scripts.internal.AstUtils.isString;
+
+public class PluginUseScriptBlockMetadataExtractor {
+
+    public static final String INVALID_ARGUMENT_LIST = "argument list must be exactly 1 literal non empty string";
+    public static final String BASE_MESSAGE = "only id(String) method calls allowed in plugins {} script block";
+    public static final String VERSION_MESSAGE = "only version(String) method calls allowed in plugins {} script block";
+    private static final String NOT_LITERAL_METHOD_NAME = "method name must be literal (i.e. not a variable)";
+    private static final String NOT_LITERAL_ID_METHOD_NAME = BASE_MESSAGE + " - " + NOT_LITERAL_METHOD_NAME;
+
+    private final DocumentationRegistry documentationRegistry;
+    private final PluginRequestCollector pluginRequestCollector;
+
+    public PluginUseScriptBlockMetadataExtractor(ScriptSource scriptSource, DocumentationRegistry documentationRegistry) {
+        this.pluginRequestCollector = new PluginRequestCollector(scriptSource);
+        this.documentationRegistry = documentationRegistry;
+    }
+
+    public void extract(SourceUnit sourceUnit, ScriptBlock scriptBlock) {
+        ClosureExpression closureArg = scriptBlock.getClosureExpression();
+
+        closureArg.getCode().visit(new RestrictiveCodeVisitor(sourceUnit, formatErrorMessage(BASE_MESSAGE)) {
+
+            @Override
+            public void visitBlockStatement(BlockStatement block) {
+                for (Statement statement : block.getStatements()) {
+                    statement.visit(this);
+                }
+            }
+
+            @Override
+            public void visitMethodCallExpression(MethodCallExpression call) {
+                if (!call.isImplicitThis()) {
+                    Expression target = call.getObjectExpression();
+                    if (!(target instanceof MethodCallExpression)) {
+                        restrict(target, formatErrorMessage(BASE_MESSAGE));
+                        return;
+                    }
+
+                    visitMethodCallExpression((MethodCallExpression) target);
+                }
+
+                if (call.getMethod() instanceof ConstantExpression) {
+                    ConstantExpression methodName = (ConstantExpression) call.getMethod();
+                    if (isString(methodName)) {
+                        String methodNameText = methodName.getText();
+                        if (methodNameText.equals("id") || methodNameText.equals("version")) {
+                            ConstantExpression argumentExpression = hasSingleConstantStringArg(call);
+                            if (argumentExpression == null) {
+                                return;
+                            }
+
+                            String argStringValue = argumentExpression.getValue().toString();
+                            if (argStringValue.length() == 0) {
+                                restrict(argumentExpression, formatErrorMessage(INVALID_ARGUMENT_LIST));
+                                return;
+                            }
+
+                            if (methodName.getText().equals("id")) {
+                                if (call.isImplicitThis()) {
+                                    try {
+                                        PluginId.validate(argStringValue);
+                                        call.setNodeMetaData(PluginDependencySpec.class, pluginRequestCollector.createSpec(call.getLineNumber()).id(argStringValue));
+                                    } catch (InvalidPluginIdException e) {
+                                        restrict(argumentExpression, formatErrorMessage(e.getReason()));
+                                    }
+                                } else {
+                                    restrict(call, formatErrorMessage(BASE_MESSAGE));
+                                }
+                            }
+
+                            if (methodName.getText().equals("version")) {
+                                Expression objectExpression = call.getObjectExpression();
+                                if (objectExpression instanceof MethodCallExpression) {
+                                    PluginDependencySpec spec = objectExpression.getNodeMetaData(PluginDependencySpec.class);
+                                    if (spec != null) {
+                                        spec.version(argStringValue);
+                                    }
+                                } else {
+                                    restrict(call, formatErrorMessage(BASE_MESSAGE));
+                                }
+                            }
+                        } else {
+                            if (!call.isImplicitThis()) {
+                                restrict(methodName, formatErrorMessage(VERSION_MESSAGE));
+                            } else {
+                                restrict(methodName, formatErrorMessage(BASE_MESSAGE));
+                            }
+                        }
+                    } else {
+                        restrict(methodName, formatErrorMessage(NOT_LITERAL_ID_METHOD_NAME));
+                    }
+                } else {
+                    restrict(call);
+                }
+            }
+
+            private ConstantExpression hasSingleConstantStringArg(MethodCallExpression call) {
+                ArgumentListExpression argumentList = (ArgumentListExpression) call.getArguments();
+                if (argumentList.getExpressions().size() == 1) {
+                    Expression argumentExpression = argumentList.getExpressions().get(0);
+                    if (argumentExpression instanceof ConstantExpression) {
+                        ConstantExpression constantArgumentExpression = (ConstantExpression) argumentExpression;
+                        if (isString(constantArgumentExpression)) {
+                            return constantArgumentExpression;
+                        } else {
+                            restrict(constantArgumentExpression, formatErrorMessage(INVALID_ARGUMENT_LIST));
+                        }
+                    } else {
+                        restrict(argumentExpression, formatErrorMessage(INVALID_ARGUMENT_LIST));
+                    }
+                } else {
+                    restrict(argumentList, formatErrorMessage(INVALID_ARGUMENT_LIST));
+                }
+
+                return null;
+            }
+
+            @Override
+            public void visitExpressionStatement(ExpressionStatement statement) {
+                statement.getExpression().visit(this);
+            }
+        });
+    }
+
+    public PluginRequests getRequests() {
+        return new DefaultPluginRequests(pluginRequestCollector.getRequests());
+    }
+
+    public String formatErrorMessage(String message) {
+        return String.format("%s%n%nSee %s for information on the plugins {} block%n%n", message, documentationRegistry.getDocumentationFor("plugins", "sec:plugins_block"));
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/plugin/use/package-info.java b/subprojects/core/src/main/groovy/org/gradle/plugin/use/package-info.java
new file mode 100644
index 0000000..0ba5411
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/plugin/use/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for managing plugin resolution and use.
+ */
+package org.gradle.plugin.use;
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecActionFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecActionFactory.java
new file mode 100644
index 0000000..363def7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/DefaultExecActionFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.process.internal;
+
+import org.gradle.api.internal.file.FileResolver;
+
+public class DefaultExecActionFactory implements ExecActionFactory {
+    private final FileResolver fileResolver;
+
+    public DefaultExecActionFactory(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+    }
+
+    @Override
+    public ExecAction newExecAction() {
+        return new DefaultExecAction(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 378098a..c55fc07 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
@@ -22,8 +22,8 @@ import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.concurrent.DefaultExecutorFactory;
 import org.gradle.internal.concurrent.StoppableExecutor;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.listener.ListenerBroadcast;
+import org.gradle.internal.nativeintegration.services.NativeServices;
+import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.process.ExecResult;
 import org.gradle.process.internal.shutdown.ShutdownHookActionRegister;
 import org.gradle.process.internal.streams.StreamsHandler;
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 a2cb5f7..743d634 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
@@ -29,15 +29,11 @@ import org.gradle.messaging.remote.MessagingServer;
 import org.gradle.messaging.remote.ObjectConnection;
 import org.gradle.process.internal.child.ApplicationClassesInIsolatedClassLoaderWorkerFactory;
 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.GUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
+import java.io.File;
 import java.net.URL;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -49,15 +45,17 @@ public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder
     private final ClassPathRegistry classPathRegistry;
     private final FileResolver resolver;
     private final IdGenerator<?> idGenerator;
+    private final File gradleUserHomeDir;
 
     public DefaultWorkerProcessFactory(LogLevel workerLogLevel, MessagingServer server,
                                        ClassPathRegistry classPathRegistry, FileResolver resolver,
-                                       IdGenerator<?> idGenerator) {
+                                       IdGenerator<?> idGenerator, File gradleUserHomeDir) {
         this.workerLogLevel = workerLogLevel;
         this.server = server;
         this.classPathRegistry = classPathRegistry;
         this.resolver = resolver;
         this.idGenerator = idGenerator;
+        this.gradleUserHomeDir = gradleUserHomeDir;
     }
 
     public WorkerProcessBuilder create() {
@@ -68,6 +66,7 @@ public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder
         public DefaultWorkerProcessBuilder() {
             super(resolver);
             setLogLevel(workerLogLevel);
+            setGradleUserHomeDir(gradleUserHomeDir);
         }
 
         @Override
@@ -104,22 +103,14 @@ public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder
             LOGGER.debug("Using implementation classpath {}", implementationClassPath);
 
             JavaExecHandleBuilder javaCommand = getJavaCommand();
-            attachStdInContent(workerFactory, javaCommand);
             workerFactory.prepareJavaCommand(javaCommand);
             javaCommand.setDisplayName(displayName);
+            javaCommand.args("'" + displayName + "'");
             ExecHandle execHandle = javaCommand.build();
 
             workerProcess.setExecHandle(execHandle);
 
             return workerProcess;
         }
-
-        private void attachStdInContent(WorkerFactory workerFactory, JavaExecHandleBuilder javaCommand) {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            OutputStream encoded = new EncodedStream.EncodedOutput(bytes);
-            GUtil.serialize(workerFactory.create(), encoded);
-            ByteArrayInputStream stdinContent = new ByteArrayInputStream(bytes.toByteArray());
-            javaCommand.setStandardInput(stdinContent);
-        }
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecActionFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecActionFactory.java
index 11069a0..1b978f5 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecActionFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/ExecActionFactory.java
@@ -17,5 +17,9 @@
 package org.gradle.process.internal;
 
 public interface ExecActionFactory {
+
+    // Instances returned should not be expected to be decorated.
+    // Use ProcessOperations to create DSL facing actions
     ExecAction newExecAction();
+
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/JvmOptions.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/JvmOptions.java
index ab9f04d..1b06b98 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/JvmOptions.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/JvmOptions.java
@@ -16,6 +16,7 @@
 
 package org.gradle.process.internal;
 
+import com.google.common.collect.ImmutableSet;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
@@ -30,14 +31,26 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class JvmOptions {
+
     private static final Pattern SYS_PROP_PATTERN = Pattern.compile("(?s)-D(.+?)=(.*)");
     private static final Pattern NO_ARG_SYS_PROP_PATTERN = Pattern.compile("-D([^=]+)");
     private static final Pattern MIN_HEAP_PATTERN = Pattern.compile("-Xms(.+)");
     private static final Pattern MAX_HEAP_PATTERN = Pattern.compile("-Xmx(.+)");
     private static final Pattern BOOTSTRAP_PATTERN = Pattern.compile("-Xbootclasspath:(.+)");
     private static final String FILE_ENCODING_KEY = "file.encoding";
+    private static final String USER_LANGUAGE_KEY = "user.language";
+    private static final String USER_COUNTRY_KEY = "user.country";
+    private static final String USER_VARIANT_KEY = "user.variant";
     private static final String JMX_REMOTE_KEY = "com.sun.management.jmxremote";
 
+    private static final Set<String> IMMUTABLE_SYSTEM_PROPERTIES = ImmutableSet.of(
+            FILE_ENCODING_KEY, USER_LANGUAGE_KEY, USER_COUNTRY_KEY, USER_VARIANT_KEY, JMX_REMOTE_KEY
+    );
+
+    // Store this because Locale.default is mutable and we want the unchanged default
+    // We are assuming this class will be initialized before any code has a chance to change the default
+    private static final Locale DEFAULT_LOCALE = Locale.getDefault();
+
     private final List<Object> extraJvmArgs = new ArrayList<Object>();
     private final Map<String, Object> systemProperties = new TreeMap<String, Object>();
     private final Map<String, Object> immutableSystemProperties = new TreeMap<String, Object>();
@@ -50,6 +63,9 @@ public class JvmOptions {
     public JvmOptions(FileResolver resolver) {
         this.bootstrapClasspath = new DefaultConfigurableFileCollection(resolver, null);
         immutableSystemProperties.put(FILE_ENCODING_KEY, Charset.defaultCharset().name());
+        immutableSystemProperties.put(USER_LANGUAGE_KEY, DEFAULT_LOCALE.getLanguage());
+        immutableSystemProperties.put(USER_COUNTRY_KEY, DEFAULT_LOCALE.getCountry());
+        immutableSystemProperties.put(USER_VARIANT_KEY, DEFAULT_LOCALE.getVariant());
     }
 
     /**
@@ -168,7 +184,7 @@ public class JvmOptions {
             }
             matcher = BOOTSTRAP_PATTERN.matcher(argStr);
             if (matcher.matches()) {
-                setBootstrapClasspath((Object[])matcher.group(1).split(Pattern.quote(File.pathSeparator)));
+                setBootstrapClasspath((Object[]) matcher.group(1).split(Pattern.quote(File.pathSeparator)));
                 continue;
             }
             if (argStr.equals("-ea") || argStr.equals("-enableassertions")) {
@@ -221,7 +237,7 @@ public class JvmOptions {
     }
 
     public void systemProperty(String name, Object value) {
-        if (name.equals(FILE_ENCODING_KEY) || name.equals(JMX_REMOTE_KEY)) {
+        if (IMMUTABLE_SYSTEM_PROPERTIES.contains(name)) {
             immutableSystemProperties.put(name, value);
         } else {
             systemProperties.put(name, value);
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/WorkerProcessBuilder.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/WorkerProcessBuilder.java
index dc8c7ce..bfa6617 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/WorkerProcessBuilder.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/WorkerProcessBuilder.java
@@ -43,6 +43,7 @@ public abstract class WorkerProcessBuilder {
     private LogLevel logLevel = LogLevel.LIFECYCLE;
     private boolean loadApplicationInSystemClassLoader;
     private String baseName = "Gradle Worker";
+    private File gradleUserHomeDir;
 
     public WorkerProcessBuilder(FileResolver fileResolver) {
         javaCommand = new JavaExecHandleBuilder(fileResolver);
@@ -109,5 +110,13 @@ public abstract class WorkerProcessBuilder {
         this.loadApplicationInSystemClassLoader = loadApplicationInSystemClassLoader;
     }
 
+    public File getGradleUserHomeDir() {
+        return gradleUserHomeDir;
+    }
+
+    public void setGradleUserHomeDir(File gradleUserHomeDir) {
+        this.gradleUserHomeDir = gradleUserHomeDir;
+    }
+
     public abstract WorkerProcess build();
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ActionExecutionWorker.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ActionExecutionWorker.java
index c542421..6cb5c3d 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ActionExecutionWorker.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ActionExecutionWorker.java
@@ -17,6 +17,7 @@
 package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.messaging.remote.Address;
 import org.gradle.messaging.remote.MessagingClient;
 import org.gradle.messaging.remote.ObjectConnection;
@@ -25,6 +26,7 @@ import org.gradle.process.internal.WorkerProcessContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
 import java.io.Serializable;
 
 /**
@@ -38,13 +40,15 @@ public class ActionExecutionWorker implements Action<WorkerContext>, Serializabl
     private final Object workerId;
     private final String displayName;
     private final Address serverAddress;
+    private final File gradleUserHomeDir;
 
     public ActionExecutionWorker(Action<? super WorkerProcessContext> action, Object workerId, String displayName,
-                                 Address serverAddress) {
+                                 Address serverAddress, File gradleUserHomeDir) {
         this.action = action;
         this.workerId = workerId;
         this.displayName = displayName;
         this.serverAddress = serverAddress;
+        this.gradleUserHomeDir = gradleUserHomeDir;
     }
 
     public void execute(final WorkerContext workerContext) {
@@ -74,6 +78,9 @@ public class ActionExecutionWorker implements Action<WorkerContext>, Serializabl
 
                 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
                 Thread.currentThread().setContextClassLoader(action.getClass().getClassLoader());
+
+                NativeServices.initialize(gradleUserHomeDir, false);
+
                 try {
                     action.execute(context);
                 } finally {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInIsolatedClassLoaderWorkerFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInIsolatedClassLoaderWorkerFactory.java
index e815bfb..9646441 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInIsolatedClassLoaderWorkerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInIsolatedClassLoaderWorkerFactory.java
@@ -16,17 +16,22 @@
 
 package org.gradle.process.internal.child;
 
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.messaging.remote.Address;
 import org.gradle.process.JavaExecSpec;
 import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.process.internal.launcher.GradleWorkerMain;
+import org.gradle.process.internal.launcher.IsolatedGradleWorkerMain;
+import org.gradle.util.GUtil;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
 import java.util.Collection;
-import java.util.concurrent.Callable;
 
 /**
  * A factory for a worker process which loads application classes using an isolated ClassLoader.
@@ -70,16 +75,32 @@ public class ApplicationClassesInIsolatedClassLoaderWorkerFactory implements Wor
     }
 
     public void prepareJavaCommand(JavaExecSpec execSpec) {
-        execSpec.setMain(GradleWorkerMain.class.getName());
+        execSpec.setMain(IsolatedGradleWorkerMain.class.getName());
         execSpec.classpath(classPathRegistry.getClassPath("WORKER_PROCESS").getAsFiles());
+        Collection<URI> applicationClassPath = new DefaultClassPath(processBuilder.getApplicationClasspath()).getAsURIs();
+
+        // Write configuration to stdin. This is consumed by IsolatedGradleWorkerMain
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        try {
+            DataOutputStream outstr = new DataOutputStream(new EncodedStream.EncodedOutput(bytes));
+            // Write application classpath
+            outstr.writeInt(applicationClassPath.size());
+            for (URI entry : applicationClassPath) {
+                outstr.writeUTF(entry.toString());
+            }
+            // Write serialized worker
+            GUtil.serialize(create(), outstr);
+            outstr.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        execSpec.setStandardInput(new ByteArrayInputStream(bytes.toByteArray()));
     }
 
-    public Callable<?> create() {
-        Collection<URI> applicationClassPath = new DefaultClassPath(processBuilder.getApplicationClasspath()).getAsURIs();
+    private ImplementationClassLoaderWorker create() {
         ActionExecutionWorker injectedWorker = new ActionExecutionWorker(processBuilder.getWorker(), workerId,
-                displayName, serverAddress);
-        ImplementationClassLoaderWorker worker = new ImplementationClassLoaderWorker(processBuilder.getLogLevel(),
-                processBuilder.getSharedPackages(), implementationClassPath, injectedWorker);
-        return new IsolatedApplicationClassLoaderWorker(applicationClassPath, worker);
+                displayName, serverAddress, processBuilder.getGradleUserHomeDir());
+        return new ImplementationClassLoaderWorker(processBuilder.getLogLevel(),
+                processBuilder.getSharedPackages(), implementationClassPath, GUtil.serialize(injectedWorker));
     }
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInSystemClassLoaderWorkerFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInSystemClassLoaderWorkerFactory.java
index f7613eb..b653235 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInSystemClassLoaderWorkerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/ApplicationClassesInSystemClassLoaderWorkerFactory.java
@@ -16,23 +16,20 @@
 
 package org.gradle.process.internal.child;
 
-import com.google.common.io.ByteStreams;
-import com.google.common.io.InputSupplier;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
+import org.gradle.api.logging.LogLevel;
 import org.gradle.messaging.remote.Address;
 import org.gradle.process.JavaExecSpec;
 import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.process.internal.launcher.BootstrapClassLoaderWorker;
 import org.gradle.process.internal.launcher.GradleWorkerMain;
 import org.gradle.util.GUtil;
 
 import java.io.*;
 import java.net.URL;
+import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.Callable;
+import java.util.Set;
 
 /**
  * A factory for a worker process which loads the application classes using the JVM's system ClassLoader.
@@ -43,7 +40,7 @@ import java.util.concurrent.Callable;
  *                                 |
  *                +----------------+--------------+
  *                |                               |
- *            jvm system                      worker bootstrap
+ *            jvm system                     infrastructure
  *  (GradleWorkerMain, application) (SystemApplicationClassLoaderWorker, logging)
  *                |                   (ImplementationClassLoaderWorker)
  *                |                               |
@@ -57,9 +54,6 @@ import java.util.concurrent.Callable;
  * </pre>
  */
 public class ApplicationClassesInSystemClassLoaderWorkerFactory implements WorkerFactory {
-
-    private final static Logger LOGGER = Logging.getLogger(ApplicationClassesInSystemClassLoaderWorkerFactory.class);
-
     private final Object workerId;
     private final String displayName;
     private final WorkerProcessBuilder processBuilder;
@@ -82,39 +76,58 @@ public class ApplicationClassesInSystemClassLoaderWorkerFactory implements Worke
         execSpec.setMain("jarjar." + GradleWorkerMain.class.getName());
         execSpec.classpath(classPathRegistry.getClassPath("WORKER_MAIN").getAsFiles());
         Object requestedSecurityManager = execSpec.getSystemProperties().get("java.security.manager");
-        if (requestedSecurityManager != null) {
-            execSpec.systemProperty("org.gradle.security.manager", requestedSecurityManager);
-        }
         execSpec.systemProperty("java.security.manager", "jarjar." + BootstrapSecurityManager.class.getName());
+        Collection<URL> workerClassPath = classPathRegistry.getClassPath("WORKER_PROCESS").getAsURLs();
+        ActionExecutionWorker worker = create();
+        Collection<File> applicationClasspath = processBuilder.getApplicationClasspath();
+        LogLevel logLevel = processBuilder.getLogLevel();
+        Set<String> sharedPackages = processBuilder.getSharedPackages();
+
+        // Serialize configuration for the worker process to it stdin
+
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         try {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
             DataOutputStream outstr = new DataOutputStream(new EncodedStream.EncodedOutput(bytes));
-            LOGGER.debug("Writing an application classpath to child process' standard input.");
-            outstr.writeInt(processBuilder.getApplicationClasspath().size());
-            for (File file : processBuilder.getApplicationClasspath()) {
+            // Serialize the application classpath, this is consumed by BootstrapSecurityManager
+            outstr.writeInt(applicationClasspath.size());
+            for (File file : applicationClasspath) {
                 outstr.writeUTF(file.getAbsolutePath());
             }
-            outstr.close();
-            final InputStream originalStdin = execSpec.getStandardInput();
-            InputStream input = ByteStreams.join(ByteStreams.newInputStreamSupplier(bytes.toByteArray()), new InputSupplier<InputStream>() {
-                public InputStream getInput() throws IOException {
-                    return originalStdin;
-                }
-            }).getInput();
-            execSpec.setStandardInput(input);
+            outstr.writeUTF(requestedSecurityManager == null ? "" : requestedSecurityManager.toString());
+
+            // Serialize the infrastructure classpath, this is consumed by GradleWorkerMain
+            outstr.writeInt(workerClassPath.size());
+            for (URL entry : workerClassPath) {
+                outstr.writeUTF(entry.toString());
+            }
+
+            // Serialize the worker configuration, this is consumed by GradleWorkerMain
+            outstr.writeInt(logLevel.ordinal());
+            outstr.writeInt(sharedPackages.size());
+            for (String str : sharedPackages) {
+                outstr.writeUTF(str);
+            }
+
+            // Serialize the worker implementation classpath, this is consumed by GradleWorkerMain
+            outstr.writeInt(implementationClassPath.size());
+            for (URL entry : implementationClassPath) {
+                outstr.writeUTF(entry.toString());
+            }
+
+            // Serialize the worker, this is consumed by GradleWorkerMain
+            byte[] serializedWorker = GUtil.serialize(worker);
+            outstr.writeInt(serializedWorker.length);
+            outstr.write(serializedWorker);
+
+            outstr.flush();
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
+        execSpec.setStandardInput(new ByteArrayInputStream(bytes.toByteArray()));
     }
 
-    public Callable<?> create() {
-        // Serialize the bootstrap worker, so it can be transported through the system ClassLoader
-        ActionExecutionWorker injectedWorker = new ActionExecutionWorker(processBuilder.getWorker(), workerId, displayName, serverAddress);
-        ImplementationClassLoaderWorker worker = new ImplementationClassLoaderWorker(processBuilder.getLogLevel(), processBuilder.getSharedPackages(),
-                implementationClassPath, injectedWorker);
-        byte[] serializedWorker = GUtil.serialize(worker);
-
-        return new BootstrapClassLoaderWorker(classPathRegistry.getClassPath("WORKER_PROCESS").getAsURLs(), serializedWorker);
+    private ActionExecutionWorker create() {
+        return new ActionExecutionWorker(processBuilder.getWorker(), workerId, displayName, serverAddress, processBuilder.getGradleUserHomeDir());
     }
 
 }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/BootstrapSecurityManager.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/BootstrapSecurityManager.java
index a9067cf..be41525 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/BootstrapSecurityManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/BootstrapSecurityManager.java
@@ -56,6 +56,7 @@ public class BootstrapSecurityManager extends SecurityManager {
         System.setSecurityManager(null);
 
         URLClassLoader systemClassLoader = target != null ? target : (URLClassLoader) getClass().getClassLoader();
+        String securityManagerType;
         try {
             Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
             addUrlMethod.setAccessible(true);
@@ -73,19 +74,19 @@ public class BootstrapSecurityManager extends SecurityManager {
                 classpathStr.append(file.toString());
             }
             System.setProperty("java.class.path", classpathStr.toString());
+            securityManagerType = inputStream.readUTF();
         } catch (Exception e) {
             throw new RuntimeException("Could not initialise system classpath.", e);
         }
 
-        String secManagerType = System.getProperty("org.gradle.security.manager");
-        if (secManagerType != null) {
-            System.setProperty("java.security.manager", secManagerType);
+        if (securityManagerType.length() > 0) {
+            System.setProperty("java.security.manager", securityManagerType);
             SecurityManager securityManager;
             try {
-                Class<?> aClass = systemClassLoader.loadClass(secManagerType);
+                Class<?> aClass = systemClassLoader.loadClass(securityManagerType);
                 securityManager = (SecurityManager) aClass.newInstance();
             } catch (Exception e) {
-                throw new RuntimeException("Could not create an instance of '" + secManagerType + "' specified for system SecurityManager.", e);
+                throw new RuntimeException("Could not create an instance of '" + securityManagerType + "' specified for system SecurityManager.", e);
             }
             System.setSecurityManager(securityManager);
         }
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 144fbe3..b658287 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
@@ -18,6 +18,7 @@ package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.classloader.CachingClassLoader;
 import org.gradle.internal.classloader.FilteringClassLoader;
@@ -26,7 +27,6 @@ import org.gradle.internal.classloader.MutableURLClassLoader;
 import org.gradle.internal.io.ClassLoaderObjectInputStream;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
-import org.gradle.util.*;
 
 import java.io.ByteArrayInputStream;
 import java.io.ObjectInputStream;
@@ -35,7 +35,7 @@ import java.net.URL;
 import java.util.Collection;
 
 /**
- * <p>A stage of the worker process start-up. Instantiated in the worker bootstrap ClassLoader and takes care of
+ * <p>A stage of the worker process start-up. Instantiated in the infrastructure ClassLoader and takes care of
  * creating the implementation ClassLoader and executing the next stage of start-up in that ClassLoader. </p>
  */
 public class ImplementationClassLoaderWorker implements Action<WorkerContext>, Serializable {
@@ -46,11 +46,11 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
 
     protected ImplementationClassLoaderWorker(LogLevel logLevel, Collection<String> sharedPackages,
                                               Collection<URL> implementationClassPath,
-                                              Action<WorkerContext> workerAction) {
+                                              byte[] serializedWorkerAction) {
         this.logLevel = logLevel;
         this.sharedPackages = sharedPackages;
         this.implementationClassPath = implementationClassPath;
-        serializedWorkerAction = GUtil.serialize(workerAction);
+        this.serializedWorkerAction = serializedWorkerAction;
     }
 
     public void execute(WorkerContext workerContext) {
@@ -59,6 +59,8 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
 
         FilteringClassLoader filteredWorkerClassLoader = new FilteringClassLoader(getClass().getClassLoader());
         filteredWorkerClassLoader.allowPackage("org.slf4j");
+        filteredWorkerClassLoader.allowClass(Logger.class);
+        filteredWorkerClassLoader.allowClass(LogLevel.class);
         filteredWorkerClassLoader.allowClass(Action.class);
         filteredWorkerClassLoader.allowClass(WorkerContext.class);
 
@@ -86,7 +88,7 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
     }
 
     LoggingManagerInternal createLoggingManager() {
-        return LoggingServiceRegistry.newProcessLogging().newInstance(LoggingManagerInternal.class);
+        return LoggingServiceRegistry.newCommandLineProcessLogging().newInstance(LoggingManagerInternal.class);
     }
 
     MutableURLClassLoader createImplementationClassLoader(ClassLoader system, ClassLoader application) {
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 dec4ff6..fc2bfce 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
@@ -17,28 +17,34 @@
 package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
-import org.gradle.internal.io.ClassLoaderObjectInputStream;
+import org.gradle.api.logging.LogLevel;
 
-import java.io.ByteArrayInputStream;
+import java.net.URL;
+import java.util.Collection;
 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>
+ * <p>Stage 2 of the start-up for a worker process with the application classes loaded in the system ClassLoader. Takes
+ * care of deserializing and then invoking the next stage of start-up.</p>
  *
- * <p> Instantiated in the worker bootstrap ClassLoader and invoked from {@link org.gradle.process.internal.launcher.BootstrapClassLoaderWorker}.
+ * <p> Instantiated in the infrastructure ClassLoader and invoked from {@link org.gradle.process.internal.launcher.GradleWorkerMain}.
  * See {@link ApplicationClassesInSystemClassLoaderWorkerFactory} for details.</p>
  */
 public class SystemApplicationClassLoaderWorker implements Callable<Void> {
+    private final int logLevel;
+    private final Collection<String> sharedPackages;
+    private final Collection<URL> workerClassPath;
     private final byte[] serializedWorker;
 
-    public SystemApplicationClassLoaderWorker(byte[] serializedWorker) {
+    public SystemApplicationClassLoaderWorker(int logLevel, Collection<String> sharedPackages, Collection<URL> workerClassPath, byte[] serializedWorker) {
+        this.logLevel = logLevel;
+        this.sharedPackages = sharedPackages;
+        this.workerClassPath = workerClassPath;
         this.serializedWorker = serializedWorker;
     }
 
     public Void call() throws Exception {
-        ClassLoaderObjectInputStream instr = new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedWorker), getClass().getClassLoader());
-        final Action<WorkerContext> action = (Action<WorkerContext>) instr.readObject();
+        final Action<WorkerContext> action = new ImplementationClassLoaderWorker(LogLevel.values()[logLevel], sharedPackages, workerClassPath, serializedWorker);
 
         action.execute(new WorkerContext() {
             public ClassLoader getApplicationClassLoader() {
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerFactory.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerFactory.java
index c2cad59..b79748b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerFactory.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/child/WorkerFactory.java
@@ -18,15 +18,8 @@ package org.gradle.process.internal.child;
 
 import org.gradle.process.JavaExecSpec;
 
-import java.util.concurrent.Callable;
-
 public interface WorkerFactory {
     /**
-     * Creates the worker action to be serialized across to the child process.
-     */
-    Callable<?> create();
-
-    /**
      * Configures the Java command that will be used to launch the child process.
      */
     void prepareJavaCommand(JavaExecSpec execSpec);
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 be3eed7..1181556 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
@@ -29,18 +29,18 @@ import org.gradle.cache.CacheRepository;
 import org.gradle.cache.PersistentCache;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.process.internal.launcher.BootstrapClassLoaderWorker;
 import org.gradle.process.internal.launcher.GradleWorkerMain;
 import org.gradle.util.AntUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.util.*;
 
-public class WorkerProcessClassPathProvider implements ClassPathProvider {
+public class WorkerProcessClassPathProvider implements ClassPathProvider, Closeable {
     private static final Logger LOGGER = LoggerFactory.getLogger(WorkerProcessClassPathProvider.class);
     private final CacheRepository cacheRepository;
     private final ModuleRegistry moduleRegistry;
@@ -63,8 +63,6 @@ public class WorkerProcessClassPathProvider implements ClassPathProvider {
             classpath = classpath.plus(moduleRegistry.getModule("gradle-native").getImplementationClasspath());
             classpath = classpath.plus(moduleRegistry.getModule("gradle-messaging").getImplementationClasspath());
             classpath = classpath.plus(moduleRegistry.getExternalModule("slf4j-api").getClasspath());
-            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-jdk5").getClasspath());
             return classpath;
@@ -119,7 +117,6 @@ public class WorkerProcessClassPathProvider implements ClassPathProvider {
                     BootstrapSecurityManager.class,
                     EncodedStream.EncodedInput.class);
             List<Class<?>> classes = new ArrayList<Class<?>>();
-            classes.add(BootstrapClassLoaderWorker.class);
             classes.addAll(renamedClasses);
             for (Class<?> aClass : classes) {
                 final String fileName = aClass.getName().replace('.', '/') + ".class";
@@ -164,12 +161,17 @@ public class WorkerProcessClassPathProvider implements ClassPathProvider {
                 }
             });
 
-            for (Class<?> renamedClass : renamedClasses) {
-                Rule rule = new Rule();
-                rule.setPattern(renamedClass.getName());
-                rule.setResult("jarjar. at 0");
-                task.addConfiguredRule(rule);
-            }
+            // Don't rename references to this class
+            Rule rule = new Rule();
+            rule.setPattern(SystemApplicationClassLoaderWorker.class.getName());
+            rule.setResult(SystemApplicationClassLoaderWorker.class.getName());
+            task.addConfiguredRule(rule);
+
+            // Rename everything else
+            rule = new Rule();
+            rule.setPattern("org.gradle.**");
+            rule.setResult("jarjar. at 0");
+            task.addConfiguredRule(rule);
 
             AntUtil.execute(task);
         }
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/BootstrapClassLoaderWorker.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/BootstrapClassLoaderWorker.java
deleted file mode 100644
index 9811f35..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/BootstrapClassLoaderWorker.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.process.internal.launcher;
-
-import java.io.Serializable;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collection;
-import java.util.concurrent.Callable;
-
-/**
- * <p>Stage 2 of the start-up for a worker process with the application classes loaded in the system ClassLoader. Takes
- * care of creating the worker bootstrap ClassLoader and executing the next stage of start-up in that ClassLoader.</p>
- *
- * <p> Instantiated in the system ClassLoader and called from {@link GradleWorkerMain}. See
- * {@link org.gradle.process.internal.child.ApplicationClassesInSystemClassLoaderWorkerFactory} for details.</p>
- */
-public class BootstrapClassLoaderWorker implements Callable<Void>, Serializable {
-    private final Collection<URL> bootstrapClasspath;
-    private final byte[] serializedWorker;
-
-    public BootstrapClassLoaderWorker(Collection<URL> bootstrapClasspath, byte[] serializedWorker) {
-        this.bootstrapClasspath = bootstrapClasspath;
-        this.serializedWorker = serializedWorker;
-    }
-
-    public Void call() throws Exception {
-        URL[] bootstrapUrls = bootstrapClasspath.toArray(new URL[bootstrapClasspath.size()]);
-        URLClassLoader classLoader = new URLClassLoader(bootstrapUrls, ClassLoader.getSystemClassLoader().getParent());
-        Class<? extends Callable> workerClass = classLoader.loadClass("org.gradle.process.internal.child.SystemApplicationClassLoaderWorker").asSubclass(Callable.class);
-        Callable<Void> main = workerClass.getConstructor(byte[].class).newInstance(serializedWorker);
-        return main.call();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/GradleWorkerMain.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/GradleWorkerMain.java
index ac23f74..f314472 100644
--- a/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/GradleWorkerMain.java
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/GradleWorkerMain.java
@@ -18,17 +18,54 @@ package org.gradle.process.internal.launcher;
 
 import org.gradle.process.internal.child.EncodedStream;
 
-import java.io.ObjectInputStream;
+import java.io.DataInputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.Callable;
 
 /**
- * The main entry point for a worker process. Reads a serialized Callable from stdin, and executes it.
+ * The main entry point for a worker process that is using the system ClassLoader strategy. Reads worker configuration and a serialized worker action from stdin,
+ * sets up the worker ClassLoader, and then delegates to {@link org.gradle.process.internal.child.SystemApplicationClassLoaderWorker} to deserialize and execute the action.
  */
 public class GradleWorkerMain {
     public void run() throws Exception {
-        // Read the main action from stdin and execute it
-        ObjectInputStream instr = new ObjectInputStream(new EncodedStream.EncodedInput(System.in));
-        Callable<?> main = (Callable<?>) instr.readObject();
+        DataInputStream instr = new DataInputStream(new EncodedStream.EncodedInput(System.in));
+
+        // Read infrastructure classpath
+        int classPathLength = instr.readInt();
+        URL[] infrastructureClassPath = new URL[classPathLength];
+        for (int i = 0; i < classPathLength; i++) {
+            String url = instr.readUTF();
+            infrastructureClassPath[i] = new URL(url);
+        }
+
+        // Read worker configuration
+        int logLevel = instr.readInt();
+        int sharedPackagesCount = instr.readInt();
+        List<String> sharedPackages = new ArrayList<String>(sharedPackagesCount);
+        for (int i = 0; i < sharedPackagesCount; i++) {
+            sharedPackages.add(instr.readUTF());
+        }
+
+        // Reader worker implementation classpath
+        classPathLength = instr.readInt();
+        List<URL> implementationClassPath = new ArrayList<URL>(classPathLength);
+        for (int i = 0; i < classPathLength; i++) {
+            String url = instr.readUTF();
+            implementationClassPath.add(new URL(url));
+        }
+
+        // Read serialized worker
+        int serializedWorkerLength = instr.readInt();
+        byte[] serializedWorker = new byte[serializedWorkerLength];
+        instr.readFully(serializedWorker);
+
+        URLClassLoader classLoader = new URLClassLoader(infrastructureClassPath, ClassLoader.getSystemClassLoader().getParent());
+        Class<? extends Callable> workerClass = classLoader.loadClass("org.gradle.process.internal.child.SystemApplicationClassLoaderWorker").asSubclass(Callable.class);
+        Callable<Void> main = workerClass.getConstructor(Integer.TYPE, Collection.class, Collection.class, byte[].class).newInstance(logLevel, sharedPackages, implementationClassPath, serializedWorker);
         main.call();
     }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/IsolatedGradleWorkerMain.java b/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/IsolatedGradleWorkerMain.java
new file mode 100644
index 0000000..843d167
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/process/internal/launcher/IsolatedGradleWorkerMain.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.launcher;
+
+import org.gradle.api.Action;
+import org.gradle.process.internal.child.EncodedStream;
+import org.gradle.process.internal.child.IsolatedApplicationClassLoaderWorker;
+import org.gradle.process.internal.child.WorkerContext;
+
+import java.io.DataInputStream;
+import java.io.ObjectInputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * The main entry point for a worker process, using isolated ClassLoader strategy. Reads the application classpath and a serialized worker action from stdin, and delegates
+ * to {@link org.gradle.process.internal.child.IsolatedApplicationClassLoaderWorker} to create the appropriate ClassLoader and run the action.
+ */
+public class IsolatedGradleWorkerMain {
+    public void run() throws Exception {
+        // Read the main action from stdin and execute it
+        DataInputStream instr = new DataInputStream(new EncodedStream.EncodedInput(System.in));
+        int applicationClassPathLength = instr.readInt();
+        Collection<URI> classpath = new ArrayList<URI>();
+        for (int i = 0; i < applicationClassPathLength; i++) {
+            String uri = instr.readUTF();
+            classpath.add(new URI(uri));
+        }
+        ObjectInputStream objectInputStream = new ObjectInputStream(instr);
+        Action<WorkerContext> worker = (Action<WorkerContext>) objectInputStream.readObject();
+
+        new IsolatedApplicationClassLoaderWorker(classpath, worker).call();
+    }
+
+    public static void main(String[] args) {
+        try {
+            new IsolatedGradleWorkerMain().run();
+            System.exit(0);
+        } catch (Throwable throwable) {
+            throwable.printStackTrace(System.err);
+            System.exit(1);
+        }
+    }
+}
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 7727436..849b081 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/BuildProfile.java
@@ -36,7 +36,7 @@ import java.util.Map;
  * <li>setBuildStarted</li>
  * <li>setSettingsEvaluated</li>
  * <li>setProjectsLoaded</li>
- * <li>setProjectsConfigured</li>
+ * <li>setProjectsEvaluated</li>
  * <li>setBuildFinished</li>
  * </ul>
  */
@@ -50,6 +50,7 @@ public class BuildProfile {
     private long buildStarted;
     private long settingsEvaluated;
     private long projectsLoaded;
+    private long projectsEvaluated;
     private long buildFinished;
     private StartParameter startParameter;
     private boolean successful;
@@ -171,6 +172,15 @@ 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
@@ -213,6 +223,14 @@ public class BuildProfile {
     }
 
     /**
+     * Get the elapsed time (in mSec) between the projectsLoaded event and the projectsEvaluated event.
+     * @return
+     */
+    public long getElapsedProjectsConfiguration() {
+        return projectsEvaluated - projectsLoaded;
+    }
+
+    /**
      * Get the total task execution time from all projects.
      * @return
      */
@@ -231,4 +249,6 @@ public class BuildProfile {
     public StartParameter getStartParameter() {
         return startParameter;
     }
+
+
 }
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 f28693c..21d6918 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileEventAdapter.java
@@ -27,13 +27,14 @@ import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.tasks.TaskState;
+import org.gradle.initialization.BuildCompletionListener;
 import org.gradle.initialization.BuildRequestMetaData;
 import org.gradle.internal.TimeProvider;
 
 /**
  * Adapts various events to build a {@link BuildProfile} model, and then notifies a {@link ReportGeneratingProfileListener} when the model is ready.
  */
-public class ProfileEventAdapter implements BuildListener, ProjectEvaluationListener, TaskExecutionListener, DependencyResolutionListener {
+public class ProfileEventAdapter implements BuildListener, ProjectEvaluationListener, TaskExecutionListener, DependencyResolutionListener, BuildCompletionListener {
     private final BuildRequestMetaData buildMetaData;
     private final TimeProvider timeProvider;
     private final ProfileListener listener;
@@ -61,11 +62,16 @@ public class ProfileEventAdapter implements BuildListener, ProjectEvaluationList
         buildProfile.setProjectsLoaded(timeProvider.getCurrentTime());
     }
 
-    public void projectsEvaluated(Gradle gradle) {}
+    public void projectsEvaluated(Gradle gradle) {
+        buildProfile.setProjectsEvaluated(timeProvider.getCurrentTime());
+    }
 
     public void buildFinished(BuildResult result) {
-        buildProfile.setBuildFinished(timeProvider.getCurrentTime());
         buildProfile.setSuccessful(result.getFailure() == null);
+    }
+
+    public void completed() {
+        buildProfile.setBuildFinished(timeProvider.getCurrentTime());
         try {
             listener.buildFinished(buildProfile);
         } finally {
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 3db502b..8bf3487 100644
--- a/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/profile/ProfileReportRenderer.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.profile;
 
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 import org.gradle.reporting.DurationFormatter;
 import org.gradle.reporting.HtmlReportRenderer;
 import org.gradle.reporting.ReportRenderer;
@@ -23,25 +23,30 @@ import org.gradle.reporting.TabbedPageRenderer;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URL;
 
 public class ProfileReportRenderer {
+    private static final DurationFormatter DURATION_FORMAT = new DurationFormatter();
+
     public void writeTo(BuildProfile buildProfile, File file) {
         HtmlReportRenderer renderer = new HtmlReportRenderer();
-        renderer.requireResource(getClass().getResource("/org/gradle/reporting/base-style.css"));
-        renderer.requireResource(getClass().getResource("/org/gradle/reporting/report.js"));
-        renderer.requireResource(getClass().getResource("/org/gradle/reporting/css3-pie-1.0beta3.htc"));
-        renderer.requireResource(getClass().getResource("style.css"));
-        renderer.renderer(new ProfilePageRenderer()).writeTo(buildProfile, file);
+        renderer.renderSinglePage(buildProfile, new ProfilePageRenderer(), file);
     }
-    private static final DurationFormatter DURATION_FORMAT = new DurationFormatter();
 
     private static class ProfilePageRenderer extends TabbedPageRenderer<BuildProfile> {
+        private static final URL STYLE_URL = ProfilePageRenderer.class.getResource("style.css");
+
         @Override
         protected String getTitle() {
             return "Profile report";
         }
 
         @Override
+        protected URL getStyleUrl() {
+            return STYLE_URL;
+        }
+
+        @Override
         protected ReportRenderer<BuildProfile, SimpleHtmlWriter> getHeaderRenderer() {
             return new ReportRenderer<BuildProfile, SimpleHtmlWriter>() {
                 @Override
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/CodePanelRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/CodePanelRenderer.java
index b233147..a4b2fef 100644
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/CodePanelRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/CodePanelRenderer.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.reporting;
 
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 
 import java.io.IOException;
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlPageBuilder.java b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlPageBuilder.java
new file mode 100644
index 0000000..978b29d
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlPageBuilder.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.reporting;
+
+import java.net.URL;
+import java.util.Date;
+
+public interface HtmlPageBuilder<T> {
+    /**
+     * Registers a resource that is required by this page.
+     *
+     * @return A relative URL that refers to the resource's output location.
+     */
+    String requireResource(URL resource);
+
+    String formatDate(Date date);
+
+    T getOutput();
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportBuilder.java b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportBuilder.java
new file mode 100644
index 0000000..8a2b191
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.reporting;
+
+import org.gradle.internal.html.SimpleHtmlWriter;
+
+import java.io.Writer;
+import java.net.URL;
+
+public interface HtmlReportBuilder {
+    void requireResource(URL resource);
+
+    <T> void renderHtmlPage(String name, T model, ReportRenderer<T, HtmlPageBuilder<SimpleHtmlWriter>> renderer);
+
+    <T> void renderRawHtmlPage(String name, T model, ReportRenderer<T, HtmlPageBuilder<Writer>> renderer);
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportRenderer.java
index ef3624f..3b9523b 100644
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/HtmlReportRenderer.java
@@ -16,54 +16,160 @@
 package org.gradle.reporting;
 
 import org.apache.commons.lang.StringUtils;
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.ErroringAction;
+import org.gradle.internal.IoActions;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.Writer;
 import java.net.URL;
-import java.util.HashSet;
-import java.util.Set;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 
 public class HtmlReportRenderer {
-    private final Set<URL> resources = new HashSet<URL>();
-
-    public void requireResource(URL resource) {
-        resources.add(resource);
+    /**
+     * Renders a multi-page HTML report from the given model, into the given directory.
+     */
+    public <T> void render(T model, ReportRenderer<T, HtmlReportBuilder> renderer, File outputDirectory) {
+        try {
+            outputDirectory.mkdirs();
+            DefaultHtmlReportContext context = new DefaultHtmlReportContext(outputDirectory);
+            renderer.render(model, context);
+            for (Resource resource : context.resources.values()) {
+                File destFile = new File(outputDirectory, resource.path);
+                if (!destFile.exists()) {
+                    GFileUtils.copyURLToFile(resource.source, destFile);
+                }
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
     }
 
-    public <T> TextReportRenderer<T> renderer(final ReportRenderer<T, SimpleHtmlWriter> renderer) {
-        return renderer(new TextReportRenderer<T>() {
+    /**
+     * Renders a single page HTML report from the given model, into the given output file.
+     */
+    public <T> void renderSinglePage(T model, final ReportRenderer<T, HtmlPageBuilder<SimpleHtmlWriter>> renderer, final File outputFile) {
+        render(model, new ReportRenderer<T, HtmlReportBuilder>() {
             @Override
-            protected void writeTo(T model, Writer writer) throws Exception {
-                SimpleHtmlWriter htmlWriter = new SimpleHtmlWriter(writer, "");
-                htmlWriter.startElement("html");
-                renderer.render(model, htmlWriter);
-                htmlWriter.endElement();
+            public void render(T model, HtmlReportBuilder output) throws IOException {
+                output.renderHtmlPage(outputFile.getName(), model, renderer);
             }
-        });
+        }, outputFile.getParentFile());
     }
 
-    public <T> TextReportRenderer<T> renderer(final TextReportRenderer<T> renderer) {
-        return new TextReportRenderer<T>() {
+    /**
+     * Renders a single page HTML report from the given model, into the given output file.
+     */
+    public <T> void renderRawSinglePage(T model, final ReportRenderer<T, HtmlPageBuilder<Writer>> renderer, final File outputFile) {
+        render(model, new ReportRenderer<T, HtmlReportBuilder>() {
             @Override
-            protected void writeTo(T model, Writer out) throws Exception {
-                renderer.writeTo(model, out);
+            public void render(T model, HtmlReportBuilder output) throws IOException {
+                output.renderRawHtmlPage(outputFile.getName(), model, renderer);
             }
+        }, outputFile.getParentFile());
+    }
 
-            @Override
-            public void writeTo(T model, File file) {
-                super.writeTo(model, file);
-                for (URL resource : resources) {
-                    String name = StringUtils.substringAfterLast(resource.getPath(), "/");
-                    String type = StringUtils.substringAfterLast(resource.getPath(), ".");
-                    File destFile = new File(file.getParentFile(), String.format("%s/%s", type, name));
-                    if (!destFile.exists()) {
-                        destFile.getParentFile().mkdirs();
-                        GFileUtils.copyURLToFile(resource, destFile);
-                    }
+    private static class Resource {
+        final URL source;
+        final String path;
+
+        private Resource(URL source, String path) {
+            this.source = source;
+            this.path = path;
+        }
+    }
+
+    private static class DefaultHtmlReportContext implements HtmlReportBuilder {
+        private final File outputDirectory;
+        private final Map<String, Resource> resources = new HashMap<String, Resource>();
+
+        public DefaultHtmlReportContext(File outputDirectory) {
+            this.outputDirectory = outputDirectory;
+        }
+
+        Resource addResource(URL source) {
+            String name = StringUtils.substringAfterLast(source.getPath(), "/");
+            String type = StringUtils.substringAfterLast(source.getPath(), ".");
+            if (type.equalsIgnoreCase("png") || type.equalsIgnoreCase("gif")) {
+                type = "images";
+            }
+            String path = String.format("%s/%s", type, name);
+            Resource resource = resources.get(path);
+            if (resource == null) {
+                resource = new Resource(source, path);
+                resources.put(path, resource);
+            }
+            return resource;
+        }
+
+        public void requireResource(URL source) {
+            addResource(source);
+        }
+
+        public <T> void renderHtmlPage(final String name, final T model, final ReportRenderer<T, HtmlPageBuilder<SimpleHtmlWriter>> renderer) {
+            File outputFile = new File(outputDirectory, name);
+            IoActions.writeTextFile(outputFile, "utf-8", new ErroringAction<Writer>() {
+                @Override
+                protected void doExecute(Writer writer) throws Exception {
+                    SimpleHtmlWriter htmlWriter = new SimpleHtmlWriter(writer, "");
+                    htmlWriter.startElement("html");
+                    renderer.render(model, new DefaultHtmlPageBuilder<SimpleHtmlWriter>(prefix(name), htmlWriter));
+                    htmlWriter.endElement();
                 }
+            });
+        }
+
+        public <T> void renderRawHtmlPage(final String name, final T model, final ReportRenderer<T, HtmlPageBuilder<Writer>> renderer) {
+            File outputFile = new File(outputDirectory, name);
+            IoActions.writeTextFile(outputFile, "utf-8", new ErroringAction<Writer>() {
+                @Override
+                protected void doExecute(Writer writer) throws Exception {
+                    renderer.render(model, new DefaultHtmlPageBuilder<Writer>(prefix(name), writer));
+                }
+            });
+        }
+
+        private String prefix(String name) {
+            StringBuilder builder = new StringBuilder();
+            int pos = 0;
+            while (pos < name.length()) {
+                int next = name.indexOf('/', pos);
+                if (next < 0) {
+                    break;
+                }
+                builder.append("../");
+                pos = next + 1;
+            }
+            return builder.toString();
+        }
+
+        private class DefaultHtmlPageBuilder<D> implements HtmlPageBuilder<D> {
+            private final String prefix;
+            private final D output;
+
+            public DefaultHtmlPageBuilder(String prefix, D output) {
+                this.prefix = prefix;
+                this.output = output;
+            }
+
+            public String requireResource(URL source) {
+                Resource resource = addResource(source);
+                return prefix + resource.path;
+            }
+
+            public String formatDate(Date date) {
+                return DateFormat.getDateTimeInstance().format(date);
+            }
+
+            public D getOutput() {
+                return output;
             }
-        };
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/TabbedPageRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/TabbedPageRenderer.java
index 2260eb8..253f248 100644
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/TabbedPageRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/TabbedPageRenderer.java
@@ -15,14 +15,17 @@
  */
 package org.gradle.reporting;
 
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 import org.gradle.util.GradleVersion;
 
 import java.io.IOException;
-import java.text.DateFormat;
+import java.net.URL;
 import java.util.Date;
 
-public abstract class TabbedPageRenderer<T> extends ReportRenderer<T, SimpleHtmlWriter> {
+public abstract class TabbedPageRenderer<T> extends ReportRenderer<T, HtmlPageBuilder<SimpleHtmlWriter>> {
+    private static final URL BASE_STYLE_URL = TabbedPageRenderer.class.getResource("/org/gradle/reporting/base-style.css");
+    private static final URL REPORT_JS_URL = TabbedPageRenderer.class.getResource("/org/gradle/reporting/report.js");
+
     private T model;
 
     protected T getModel() {
@@ -39,26 +42,53 @@ public abstract class TabbedPageRenderer<T> extends ReportRenderer<T, SimpleHtml
         return getTitle();
     }
 
+    protected abstract URL getStyleUrl();
+
     @Override
-    public void render(final T model, SimpleHtmlWriter htmlWriter) throws IOException {
+    public void render(final T model, HtmlPageBuilder<SimpleHtmlWriter> builder) throws IOException {
         this.model = model;
+
+        String baseStyleLink = builder.requireResource(BASE_STYLE_URL);
+        String reportJsLink = builder.requireResource(REPORT_JS_URL);
+        String styleLink = builder.requireResource(getStyleUrl());
+
+        SimpleHtmlWriter htmlWriter = builder.getOutput();
+
         htmlWriter.startElement("head")
             .startElement("meta").attribute("http-equiv", "Content-Type").attribute("content", "text/html; charset=utf-8").endElement()
+            .startElement("meta").attribute("http-equiv", "x-ua-compatible").attribute("content", "IE=edge").endElement()
             .startElement("title").characters(getPageTitle()).endElement()
-            .startElement("link").attribute("href", "css/base-style.css").attribute("rel", "stylesheet").attribute("type", "text/css").endElement()
-            .startElement("link").attribute("href", "css/style.css").attribute("rel", "stylesheet").attribute("type", "text/css").endElement()
-            .startElement("script").attribute("src", "js/report.js").attribute("type", "text/javascript").characters("").endElement() //html does not like <a name="..."/>
+            .startElement("link").attribute("href", baseStyleLink).attribute("rel", "stylesheet").attribute("type", "text/css").endElement()
+            .startElement("link").attribute("href", styleLink).attribute("rel", "stylesheet").attribute("type", "text/css").endElement()
+            .startElement("script").attribute("src", reportJsLink).attribute("type", "text/javascript").characters("").endElement() //html does not like <a name="..."/>
         .endElement();
 
         htmlWriter.startElement("body")
             .startElement("div").attribute("id", "content")
-            .startElement("h1").characters(getTitle()).endElement();
-            getHeaderRenderer().render(model, htmlWriter);
-            getContentRenderer().render(model, htmlWriter);
-            htmlWriter.startElement("div").attribute("id", "footer")
-                .startElement("p").characters("Generated by ")
-                    .startElement("a").attribute("href", "http://www.gradle.org").characters(String.format("Gradle %s", GradleVersion.current().getVersion())).endElement()
-                    .characters(String.format(" at %s", DateFormat.getDateTimeInstance().format(new Date())))
+                .startElement("h1").characters(getTitle()).endElement();
+
+                getHeaderRenderer().render(model, htmlWriter);
+                getContentRenderer().render(model, htmlWriter);
+
+                htmlWriter.startElement("div").attribute("id", "footer")
+                    .startElement("p")
+                        .startElement("div")
+                            .startElement("label")
+                                .attribute("class", "hidden")
+                                .attribute("id", "label-for-line-wrapping-toggle")
+                                .attribute("for", "line-wrapping-toggle")
+                                .characters("Wrap lines")
+                                .startElement("input")
+                                    .attribute("id", "line-wrapping-toggle")
+                                    .attribute("type", "checkbox")
+                                    .attribute("autocomplete", "off")
+                                .endElement()
+                            .endElement()
+                        .endElement()
+                    .characters("Generated by ")
+                        .startElement("a").attribute("href", "http://www.gradle.org").characters(String.format("Gradle %s", GradleVersion.current().getVersion())).endElement()
+                        .characters(String.format(" at %s", builder.formatDate(new Date())))
+                    .endElement()
                 .endElement()
             .endElement()
         .endElement();
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/TabsRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/TabsRenderer.java
index 6583475..dc47e28 100644
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/TabsRenderer.java
+++ b/subprojects/core/src/main/groovy/org/gradle/reporting/TabsRenderer.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.reporting;
 
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/subprojects/core/src/main/groovy/org/gradle/reporting/TextReportRenderer.java b/subprojects/core/src/main/groovy/org/gradle/reporting/TextReportRenderer.java
deleted file mode 100644
index ffbe215..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/reporting/TextReportRenderer.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.reporting;
-
-import org.gradle.internal.ErroringAction;
-import org.gradle.internal.IoActions;
-
-import java.io.File;
-import java.io.Writer;
-
-public abstract class TextReportRenderer<T> {
-    /**
-     * Renders the report for the given model to a writer.
-     */
-    protected abstract void writeTo(T model, Writer out) throws Exception;
-
-    /**
-     * Renders the report for the given model to a file.
-     */
-    public void writeTo(final T model, File file) {
-        IoActions.writeTextFile(file, "utf-8", new ErroringAction<Writer>() {
-            @Override
-            protected void doExecute(Writer writer) throws Exception {
-                writeTo(model, writer);
-            }
-        });
-    }
-}
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 da0d2ee..e8d5134 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
@@ -15,13 +15,13 @@
  */
 package org.gradle.testfixtures.internal;
 
-import org.gradle.CacheUsage;
+import com.google.common.collect.Maps;
 import org.gradle.api.Action;
 import org.gradle.cache.*;
 import org.gradle.cache.internal.CacheFactory;
 import org.gradle.cache.internal.filelock.LockOptions;
 import org.gradle.internal.Factory;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.Serializer;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
@@ -30,10 +30,10 @@ import java.util.Map;
 
 public class InMemoryCacheFactory implements CacheFactory {
     public PersistentCache openStore(File storeDir, String displayName, LockOptions lockOptions, Action<? super PersistentCache> initializer) throws CacheOpenException {
-        return open(storeDir, displayName, CacheUsage.ON, null, Collections.<String, Object>emptyMap(), lockOptions, initializer);
+        return open(storeDir, displayName, null, Collections.<String, Object>emptyMap(), lockOptions, initializer);
     }
 
-    public PersistentCache open(File cacheDir, String displayName, CacheUsage usage, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) {
+    public PersistentCache open(File cacheDir, String displayName, CacheValidator cacheValidator, Map<String, ?> properties, LockOptions lockOptions, Action<? super PersistentCache> initializer) {
         GFileUtils.mkdirs(cacheDir);
         InMemoryCache cache = new InMemoryCache(cacheDir);
         if (initializer != null) {
@@ -42,33 +42,58 @@ public class InMemoryCacheFactory implements CacheFactory {
         return cache;
     }
 
-    public <K, V> PersistentIndexedCache<K, V> openIndexedCache(File cacheDir, CacheUsage usage, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Serializer<V> serializer) {
+    public <K, V> PersistentIndexedCache<K, V> openIndexedCache(File cacheDir, CacheValidator validator, Map<String, ?> properties, LockOptions lockOptions, Serializer<V> serializer) {
         return new InMemoryIndexedCache<K, V>(serializer);
     }
 
-    private static class InMemoryCache implements PersistentCache {
+    public static class InMemoryCache implements PersistentCache {
+
+        final Map<String, PersistentIndexedCache<?, ?>> caches = Maps.newLinkedHashMap();
+
         private final File cacheDir;
+        private boolean closed;
 
         public InMemoryCache(File cacheDir) {
             this.cacheDir = cacheDir;
         }
 
         public void close() {
+            closed = true;
+        }
+
+        public boolean isClosed() {
+            return closed;
         }
 
         public File getBaseDir() {
             return cacheDir;
         }
 
+        private void assertNotClosed() {
+            if (closed) {
+                throw new IllegalStateException("cache is closed");
+            }
+        }
+
         public <K, V> PersistentIndexedCache<K, V> createCache(String name, Class<K> keyType, Serializer<V> valueSerializer) {
-            return new InMemoryIndexedCache<K, V>(valueSerializer);
+            assertNotClosed();
+            return createCache(name, valueSerializer);
         }
 
         public <K, V> PersistentIndexedCache<K, V> createCache(PersistentIndexedCacheParameters<K, V> parameters) {
-            return new InMemoryIndexedCache<K, V>(parameters.getValueSerializer());
+            assertNotClosed();
+            return createCache(parameters.getCacheName(), parameters.getValueSerializer());
+        }
+
+        private <K, V> PersistentIndexedCache<K, V> createCache(String name, Serializer<V> valueSerializer) {
+            assertNotClosed();
+            InMemoryIndexedCache<K, V> cache = new InMemoryIndexedCache<K, V>(valueSerializer);
+            caches.put(name, cache);
+            return cache;
         }
 
         public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
+            assertNotClosed();
             // The contract of useCache() means we have to provide some basic synchronization.
             synchronized (this) {
                 return action.create();
@@ -76,14 +101,17 @@ public class InMemoryCacheFactory implements CacheFactory {
         }
 
         public void useCache(String operationDisplayName, Runnable action) {
+            assertNotClosed();
             action.run();
         }
 
         public <T> T longRunningOperation(String operationDisplayName, Factory<? extends T> action) {
+            assertNotClosed();
             return action.create();
         }
 
         public void longRunningOperation(String operationDisplayName, Runnable action) {
+            assertNotClosed();
             action.run();
         }
     }
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
index cdea768..353c948 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryIndexedCache.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/InMemoryIndexedCache.java
@@ -17,9 +17,9 @@ 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 org.gradle.internal.serialize.InputStreamBackedDecoder;
+import org.gradle.internal.serialize.OutputStreamBackedEncoder;
+import org.gradle.internal.serialize.Serializer;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff --git a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java
index f703397..89fd653 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/NoOpLoggingManager.java
@@ -17,9 +17,12 @@ package org.gradle.testfixtures.internal;
 
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.ConsoleOutput;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.internal.OutputEventListener;
 
+import java.io.OutputStream;
+
 public class NoOpLoggingManager implements LoggingManagerInternal {
     private LogLevel level = LogLevel.LIFECYCLE;
     private LogLevel stdoutLevel = LogLevel.LIFECYCLE;
@@ -76,9 +79,21 @@ public class NoOpLoggingManager implements LoggingManagerInternal {
     public void removeOutputEventListener(OutputEventListener listener) {
     }
 
-    public void attachConsole(boolean colorOutput) {
+    public void removeAllOutputEventListeners() {
+    }
+
+    public void attachProcessConsole(ConsoleOutput consoleOutput) {
+    }
+
+    public void attachAnsiConsole(OutputStream outputStream) {
+    }
+
+    public void addStandardOutputListener(OutputStream outputStream) {
+    }
+
+    public void addStandardErrorListener(OutputStream outputStream) {
     }
 
-    public void addStandardOutputAndError() {
+    public void attachSystemOutAndErr() {
     }
 }
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 047c0af..c6ffe72 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
@@ -23,13 +23,14 @@ import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.TemporaryFileProvider;
 import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.api.internal.project.IProjectFactory;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.initialization.DefaultProjectDescriptor;
 import org.gradle.initialization.DefaultProjectDescriptorRegistry;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
@@ -39,13 +40,7 @@ import org.gradle.util.GFileUtils;
 import java.io.File;
 
 public class ProjectBuilderImpl {
-    private static final ServiceRegistry GLOBAL_SERVICES = ServiceRegistryBuilder
-            .builder()
-            .displayName("global services")
-            .parent(new TestGlobalScopeServices.TestLoggingServices())
-            .parent(NativeServices.getInstance())
-            .provider(new TestGlobalScopeServices())
-            .build();
+    private static ServiceRegistry globalServices;
     private static final AsmBackedClassGenerator CLASS_GENERATOR = new AsmBackedClassGenerator();
 
     public Project createChildProject(String name, Project parent, File projectDir) {
@@ -58,7 +53,8 @@ public class ProjectBuilderImpl {
                 new StringScriptSource("test build file", null),
                 parentProject.getGradle(),
                 parentProject.getGradle().getServiceRegistryFactory(),
-                parentProject.getClassLoaderScope().createChild()
+                parentProject.getClassLoaderScope().createChild("project-" + name),
+                parentProject.getBaseClassLoaderScope()
         );
         parentProject.addChildProject(project);
         parentProject.getProjectRegistry().addProject(project);
@@ -71,14 +67,19 @@ public class ProjectBuilderImpl {
         final File homeDir = new File(projectDir, "gradleHome");
 
         StartParameter startParameter = new StartParameter();
-        startParameter.setGradleUserHomeDir(new File(projectDir, "userHome"));
+        File userHomeDir = new File(projectDir, "userHome");
+        startParameter.setGradleUserHomeDir(userHomeDir);
 
-        ServiceRegistry topLevelRegistry = new TestBuildScopeServices(GLOBAL_SERVICES, startParameter, homeDir);
-        GradleInternal gradle = new DefaultGradle(null, startParameter, topLevelRegistry.get(ServiceRegistryFactory.class));
+        NativeServices.initialize(userHomeDir);
+
+        ServiceRegistry topLevelRegistry = new TestBuildScopeServices(getGlobalServices(), startParameter, homeDir);
+        GradleInternal gradle = CLASS_GENERATOR.newInstance(DefaultGradle.class, null, startParameter, topLevelRegistry.get(ServiceRegistryFactory.class));
 
         DefaultProjectDescriptor projectDescriptor = new DefaultProjectDescriptor(null, name, projectDir, new DefaultProjectDescriptorRegistry(),
                 topLevelRegistry.get(FileResolver.class));
-        ProjectInternal project = topLevelRegistry.get(IProjectFactory.class).createProject(projectDescriptor, null, gradle, gradle.getClassLoaderScope().createSibling());
+        ClassLoaderScope baseScope = gradle.getClassLoaderScope();
+        ClassLoaderScope rootProjectScope = baseScope.createChild("root-project");
+        ProjectInternal project = topLevelRegistry.get(IProjectFactory.class).createProject(projectDescriptor, null, gradle, rootProjectScope, baseScope);
 
         gradle.setRootProject(project);
         gradle.setDefaultProject(project);
@@ -86,6 +87,19 @@ public class ProjectBuilderImpl {
         return project;
     }
 
+    private ServiceRegistry getGlobalServices() {
+        if (globalServices == null) {
+            globalServices = ServiceRegistryBuilder
+                    .builder()
+                    .displayName("global services")
+                    .parent(new TestGlobalScopeServices.TestLoggingServices())
+                    .parent(NativeServices.getInstance())
+                    .provider(new TestGlobalScopeServices())
+                    .build();
+        }
+        return globalServices;
+    }
+
     public File prepareProjectDir(File projectDir) {
         if (projectDir == null) {
             TemporaryFileProvider temporaryFileProvider = new TmpDirTemporaryFileProvider();
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
index f66de1f..d7c62e4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestBuildScopeServices.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestBuildScopeServices.java
@@ -18,7 +18,9 @@ package org.gradle.testfixtures.internal;
 import org.gradle.StartParameter;
 import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.initialization.FixedBuildCancellationToken;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.BuildScopeServices;
 
@@ -32,6 +34,10 @@ public class TestBuildScopeServices extends BuildScopeServices {
         this.homeDir = homeDir;
     }
 
+    protected BuildCancellationToken createBuildCancellationToken() {
+        return new FixedBuildCancellationToken();
+    }
+
     protected BuildClientMetaData createClientMetaData() {
         return new GradleLauncherMetaData();
     }
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
index bb7f288..4ccfb4e 100644
--- a/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
+++ b/subprojects/core/src/main/groovy/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
@@ -21,8 +21,8 @@ import org.gradle.internal.Factory;
 import org.gradle.internal.TrueTimeProvider;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.scopes.GlobalScopeServices;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.DefaultListenerManager;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.logging.StyledTextOutputFactory;
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 adf17dd..d8bc5ce 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/Clock.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/Clock.java
@@ -62,7 +62,7 @@ public class Clock {
     }
 
     public static String prettyTime(long timeInMs) {
-        StringBuffer result = new StringBuffer();
+        StringBuilder result = new StringBuilder();
         if (timeInMs > MS_PER_HOUR) {
             result.append(timeInMs / MS_PER_HOUR).append(" hrs ");
         }
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 1e13ab1..b93d837 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/GFileUtils.java
@@ -28,13 +28,15 @@ import java.nio.charset.Charset;
 import java.util.*;
 import java.util.zip.Checksum;
 
+import static org.gradle.internal.concurrent.CompositeStoppable.stoppable;
+
 public class GFileUtils {
 
     public static FileInputStream openInputStream(File file) {
         try {
             return FileUtils.openInputStream(file);
         } catch (IOException e) {
-            throw new UncheckedIOException(e);
+            throw new RuntimeException("Problems opening file input stream for file: " + file, e);
         }
     }
 
@@ -82,6 +84,21 @@ public class GFileUtils {
         }
     }
 
+    /**
+     * Reads and returns file contents. If some exception is triggered the method returns information about this exception.
+     * Useful for including file contents in debug trace / exception messages.
+     *
+     * @param file to read
+     * @return content of the file or the problem description in case file cannot be read.
+     */
+    public static String readFileQuietly(File file) {
+        try {
+            return readFile(file);
+        } catch (Exception e) {
+            return "Unable to read file '" + file + "' due to: " + e.toString();
+        }
+    }
+
     public static void writeFile(String content, File destination) {
         writeFile(content, destination, Charset.defaultCharset().name());
     }
@@ -138,6 +155,10 @@ public class GFileUtils {
         return FileUtils.deleteQuietly(file);
     }
 
+    public static void closeInputStream(InputStream input) {
+        stoppable(input).stop();
+    }
+
     public static class TailReadingException extends RuntimeException {
         public TailReadingException(Throwable throwable) {
             super(throwable);
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java b/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
deleted file mode 100644
index c87eb2c..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/GradleVersion.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 groovy.lang.GroovySystem;
-import org.apache.ivy.Ivy;
-import org.apache.tools.ant.Main;
-import org.gradle.api.GradleException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.jvm.Jvm;
-import org.gradle.internal.os.OperatingSystem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Properties;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class GradleVersion implements Comparable<GradleVersion> {
-    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;
-    private static final GradleVersion CURRENT;
-
-    public static final String RESOURCE_NAME = "/org/gradle/build-receipt.properties";
-
-    // TODO - get rid of this static initialiser nonsense
-    static {
-        URL resource = GradleVersion.class.getResource(RESOURCE_NAME);
-
-        InputStream inputStream = null;
-        try {
-            URLConnection connection = resource.openConnection();
-            inputStream = connection.getInputStream();
-            Properties properties = new Properties();
-            properties.load(inputStream);
-
-            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, buildNumber, commitId);
-        } catch (Exception e) {
-            throw new GradleException(String.format("Could not load version details from resource '%s'.", resource), e);
-        } finally {
-            if (inputStream != null) {
-                try {
-                    inputStream.close();
-                } catch (IOException e) {
-                    throw new UncheckedIOException(e);
-                }
-            }
-        }
-    }
-
-
-    public static GradleVersion current() {
-        return CURRENT;
-    }
-
-    /**
-     * 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, 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()) {
-            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(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(5).equals("rc")) {
-                stageNumber = 3;
-            } else {
-                stageNumber = 1;
-            }
-            String stageString = matcher.group(6);
-            stage = new Stage(stageNumber, stageString);
-        } else {
-            stage = null;
-        }
-
-        if (matcher.group(8) != null) {
-            try {
-                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(8)).getTime();
-                }
-            } catch (ParseException e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        } else {
-            snapshot = null;
-        }
-    }
-
-    private String formatBuildTime(Date buildTime) {
-        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
-        format.setTimeZone(TimeZone.getTimeZone("UTC"));
-        return format.format(buildTime);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Gradle %s", version);
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public String getBuildTime() {
-        return buildTime;
-    }
-
-    public boolean isSnapshot() {
-        return snapshot != null;
-    }
-
-    /**
-     * 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
-     */
-    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 GradleVersion getNextMajor() {
-        if (stage != null && stage.stage == STAGE_MILESTONE) {
-            return version(majorPart + ".0");
-        }
-        return version((majorPart + 1) + ".0");
-    }
-
-    public int compareTo(GradleVersion gradleVersion) {
-        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]);
-
-            if (part > otherPart) {
-                return 1;
-            }
-            if (otherPart > part) {
-                return -1;
-            }
-        }
-        if (majorVersionParts.length > otherMajorVersionParts.length) {
-            return 1;
-        }
-        if (majorVersionParts.length < otherMajorVersionParts.length) {
-            return -1;
-        }
-
-        if (stage != null && gradleVersion.stage != null) {
-            int diff = stage.compareTo(gradleVersion.stage);
-            if (diff != 0) {
-                return diff;
-            }
-        }
-        if (stage == null && gradleVersion.stage != null) {
-            return 1;
-        }
-        if (stage != null && gradleVersion.stage == null) {
-            return -1;
-        }
-
-        if (snapshot != null && gradleVersion.snapshot != null) {
-            return snapshot.compareTo(gradleVersion.snapshot);
-        }
-        if (snapshot == null && gradleVersion.snapshot != null) {
-            return 1;
-        }
-        if (snapshot != null && gradleVersion.snapshot == null) {
-            return -1;
-        }
-
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
-        if (o == null || o.getClass() != getClass()) {
-            return false;
-        }
-        GradleVersion other = (GradleVersion) o;
-        return version.equals(other.version);
-    }
-
-    @Override
-    public int hashCode() {
-        return version.hashCode();
-    }
-
-    public String prettyPrint() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("\n------------------------------------------------------------\nGradle ");
-        sb.append(getVersion());
-        sb.append("\n------------------------------------------------------------\n\nBuild time:   ");
-        sb.append(getBuildTime());
-        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(Main.getAntVersion());
-        sb.append("\nIvy:          ");
-        sb.append(Ivy.getIvyVersion());
-        sb.append("\nJVM:          ");
-        sb.append(Jvm.current());
-        sb.append("\nOS:           ");
-        sb.append(OperatingSystem.current());
-        sb.append("\n");
-        return sb.toString();
-    }
-
-    public boolean isValid() {
-        return versionPart != null;
-    }
-
-    static final class Stage implements Comparable<Stage> {
-        final int stage;
-        final int number;
-        final Character patchNo;
-
-        Stage(int stage, String number) {
-            this.stage = stage;
-            Matcher m = Pattern.compile("(\\d+)([a-z])?").matcher(number);
-            try {
-                m.matches();
-                this.number = Integer.parseInt(m.group(1));
-            } catch (Exception e) {
-                throw new RuntimeException("Invalid stage small number: " + number, e);
-            }
-
-            if (m.groupCount() == 2 && m.group(2) != null) {
-                this.patchNo = m.group(2).charAt(0);
-            } else {
-                this.patchNo = '_';
-            }
-        }
-
-        public int compareTo(Stage other) {
-            if (stage > other.stage) {
-                return 1;
-            }
-            if (stage < other.stage) {
-                return -1;
-            }
-            if (number > other.number) {
-                return 1;
-            }
-            if (number < other.number) {
-                return -1;
-            }
-            if (patchNo > other.patchNo) {
-                return 1;
-            }
-            if (patchNo < other.patchNo) {
-                return -1;
-            }
-            return 0;
-        }
-    }
-}
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java b/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java
deleted file mode 100644
index ff0dcab..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/Jvm.java
+++ /dev/null
@@ -1,93 +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.internal.jvm.JavaHomeException;
-import org.gradle.internal.jvm.JavaInfo;
-
-import java.io.File;
-import java.util.Map;
-
- at Deprecated
-public class Jvm implements JavaInfo {
-    private final org.gradle.internal.jvm.Jvm jvm;
-
-    public Jvm(org.gradle.internal.jvm.Jvm current) {
-        this.jvm = current;
-    }
-
-    public static Jvm current() {
-        DeprecationLogger.nagUserOfDeprecated("The class org.gradle.util.Jvm");
-        return new Jvm(org.gradle.internal.jvm.Jvm.current());
-    }
-
-    public File getJavaExecutable() throws JavaHomeException {
-        return jvm.getJavaExecutable();
-    }
-
-    public File getJavadocExecutable() throws JavaHomeException {
-        return jvm.getJavadocExecutable();
-    }
-
-    public File getExecutable(String name) throws JavaHomeException {
-        return jvm.getExecutable(name);
-    }
-
-    public boolean isJava5() {
-        return jvm.getJavaVersion().isJava5();
-    }
-
-    public boolean isJava6() {
-        return jvm.getJavaVersion().isJava6();
-    }
-
-    public boolean isJava7() {
-        return jvm.getJavaVersion().isJava7();
-    }
-
-    public boolean isJava5Compatible() {
-        return jvm.getJavaVersion().isJava5Compatible();
-    }
-
-    public boolean isJava6Compatible() {
-        return jvm.getJavaVersion().isJava6Compatible();
-    }
-
-    public File getJavaHome() {
-        return jvm.getJavaHome();
-    }
-
-    public File getRuntimeJar() {
-        return jvm.getRuntimeJar();
-    }
-
-    public File getToolsJar() {
-        return jvm.getToolsJar();
-    }
-
-    public Map<String, ?> getInheritableEnvironmentVariables(Map<String, ?> envVars) {
-        return jvm.getInheritableEnvironmentVariables(envVars);
-    }
-
-    public boolean isIbmJvm() {
-        return jvm.isIbmJvm();
-    }
-    
-    public String toString(){
-        return jvm.toString();
-    }
-}
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 5a1b47e..3d0ecb4 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
@@ -41,7 +41,7 @@ public class LineBufferingOutputStream extends OutputStream {
         bufferIncrement = bufferLength;
         buf = new byte[bufferLength];
         count = 0;
-        lineSeparator = SystemProperties.getLineSeparator().getBytes();
+        lineSeparator = SystemProperties.getInstance().getLineSeparator().getBytes();
     }
 
     /**
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 b58e0ec..8104a10 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/SingleMessageLogger.java
@@ -17,7 +17,6 @@
 package org.gradle.util;
 
 import net.jcip.annotations.ThreadSafe;
-import org.apache.commons.lang.StringUtils;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.Factory;
@@ -34,7 +33,6 @@ import java.util.concurrent.locks.ReentrantLock;
 @ThreadSafe
 public class SingleMessageLogger {
     private static final Logger LOGGER = Logging.getLogger(DeprecationLogger.class);
-    private static final Set<String> DYNAMIC_PROPERTIES = 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>() {
@@ -69,7 +67,6 @@ public class SingleMessageLogger {
     }
 
     public static void reset() {
-        DYNAMIC_PROPERTIES.clear();
         FEATURES.clear();
         LOCK.lock();
         try {
@@ -94,6 +91,12 @@ public class SingleMessageLogger {
                 pluginName, getDeprecationMessage(), replacement));
     }
 
+    public static void nagUserOfReplacedTask(String taskName, String replacement) {
+        nagUserWith(String.format(
+                "The %s task %s. Please use the %s task instead.",
+                taskName, getDeprecationMessage(), replacement));
+    }
+
     public static void nagUserOfReplacedTaskType(String taskName, String replacement) {
         nagUserWith(String.format(
                 "The %s task type %s. Please use the %s instead.",
@@ -122,11 +125,6 @@ public class SingleMessageLogger {
                 propertyName, getDeprecationMessage(), advice));
     }
 
-    public static void nagUserOfDiscontinuedConfiguration(String configurationName, String advice) {
-        nagUserWith(String.format("The %s configuration %s. %s",
-                configurationName, getDeprecationMessage(), advice));
-    }
-
     public static void nagUserOfReplacedNamedParameter(String parameterName, String replacement) {
         nagUserWith(String.format(
                 "The %s named parameter %s. Please use the %s named parameter instead.",
@@ -184,22 +182,6 @@ public class SingleMessageLogger {
         return ENABLED.get();
     }
 
-    public static void nagUserAboutDynamicProperty(String propertyName, Object target, Object value) {
-        if (!isEnabled()) {
-            return;
-        }
-        nagUserOfDeprecated("Creating properties on demand (a.k.a. dynamic properties)", "Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties");
-
-        String propertyWithClass = target.getClass().getName() + "." + propertyName;
-        if (DYNAMIC_PROPERTIES.add(propertyWithClass)) {
-            String propertyWithTarget = String.format("\"%s\" on \"%s\"", propertyName, target);
-            String theValue = (value==null)? "null" : StringUtils.abbreviate(value.toString(), 25);
-            nagUserWith(String.format("Deprecated dynamic property: %s, value: \"%s\".", propertyWithTarget, theValue));
-        } else {
-            nagUserWith(String.format("Deprecated dynamic property \"%s\" created in multiple locations.", propertyName));
-        }
-    }
-
     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
deleted file mode 100644
index 90e3186..0000000
--- a/subprojects/core/src/main/groovy/org/gradle/util/TextUtil.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util;
-
-import org.apache.commons.lang.StringEscapeUtils;
-import org.gradle.internal.SystemProperties;
-
-import java.io.File;
-import java.util.regex.Pattern;
-
-public class TextUtil {
-    private static final Pattern WHITESPACE = Pattern.compile("\\s*");
-
-    /**
-     * Returns the line separator for Windows.
-     */
-    public static String getWindowsLineSeparator() {
-        return "\r\n";
-    }
-
-    /**
-     * Returns the line separator for Unix.
-     */
-    public static String getUnixLineSeparator() {
-        return "\n";
-    }
-
-    /**
-     * Returns the line separator for this platform.
-     */
-    public static String getPlatformLineSeparator() {
-        return SystemProperties.getLineSeparator();
-    }
-
-    /**
-     * Converts all line separators in the specified string to the specified line separator.
-     */
-    public static String convertLineSeparators(String str, String sep) {
-        return str == null ? null : str.replaceAll("\r\n|\r|\n", sep);
-    }
-
-    /**
-     * Converts all line separators in the specified string to the the platform's line separator.
-     */
-    public static String toPlatformLineSeparators(String str) {
-        return str == null ? null : convertLineSeparators(str, getPlatformLineSeparator());
-    }
-
-    /**
-     * 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) {
-        return path.replaceAll(Pattern.quote(File.separator), "/");
-    }
-
-    /**
-     * Escapes the toString() representation of {@code obj} for use in a literal string.
-     * This is useful for interpolating variables into script strings, as well as in other situations.
-     */
-    public static String escapeString(Object obj) {
-        return obj == null ? null : StringEscapeUtils.escapeJava(obj.toString());
-    }
-
-    /**
-     * Tells whether the specified string contains any whitespace characters.
-     */
-    public static boolean containsWhitespace(String str) {
-        for (int i = 0; i < str.length(); i++) {
-            if (Character.isWhitespace(str.charAt(i))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Indents every line of {@code text} by {@code indent}. Empty lines
-     * and lines that only contain whitespace are not indented.
-     */
-    public static String indent(String text, String indent) {
-        StringBuilder builder = new StringBuilder();
-        String[] lines = text.split("\n");
-
-        for (int i = 0; i < lines.length; i++) {
-            String line = lines[i];
-            if (!WHITESPACE.matcher(line).matches()) {
-                builder.append(indent);
-            }
-            builder.append(line);
-            if (i < lines.length - 1) {
-                builder.append('\n');
-            }
-        }
-
-        return builder.toString();
-    }
-}
\ No newline at end of file
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 10255ff..ea6e5bd 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/VersionNumber.java
@@ -18,38 +18,47 @@ package org.gradle.util;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.Ordering;
-
 import org.gradle.api.Nullable;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
- * Represents, parses, and compares version numbers following a major.minor.micro-qualifier format.
- * The {@link #parse} method handles missing parts and allows "." to be used instead of "-".
+ * Represents, parses, and compares version numbers. Supports a couple of different schemes: <ul> <li>MAJOR.MINOR.MICRO-QUALIFIER (the default).</li> <li>MAJOR.MINOR.MICRO.PATCH-QUALIFIER.</li> </ul>
+ *
+ * <p>The {@link #parse} method handles missing parts and allows "." to be used instead of "-", and "_" to be used instead of "." for the patch number.
+ *
+ * <p>This class considers missing parts to be 0, so that "1.0" == "1.0.0" == "1.0.0_0".</p>
  *
- * <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>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}.
+ * <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);
-
-    private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)(?:\\.(\\d+))?+(?:\\.(\\d+))?+(?:[-\\.](.+))?");
-    private static final String VERSION_TEMPLATE = "%d.%d.%d%s";
+    private static final DefaultScheme DEFAULT_SCHEME = new DefaultScheme();
+    private static final SchemeWithPatchVersion PATCH_SCHEME = new SchemeWithPatchVersion();
+    public static final VersionNumber UNKNOWN = version(0);
 
     private final int major;
     private final int minor;
     private final int micro;
+    private final int patch;
     private final String qualifier;
+    private final AbstractScheme scheme;
 
     public VersionNumber(int major, int minor, int micro, @Nullable String qualifier) {
+        this(major, minor, micro, 0, qualifier, DEFAULT_SCHEME);
+    }
+
+    public VersionNumber(int major, int minor, int micro, int patch, @Nullable String qualifier) {
+        this(major, minor, micro, patch, qualifier, PATCH_SCHEME);
+    }
+
+    private VersionNumber(int major, int minor, int micro, int patch, @Nullable String qualifier, AbstractScheme scheme) {
         this.major = major;
         this.minor = minor;
         this.micro = micro;
+        this.patch = patch;
         this.qualifier = qualifier;
+        this.scheme = scheme;
     }
 
     public int getMajor() {
@@ -64,54 +73,214 @@ public class VersionNumber implements Comparable<VersionNumber> {
         return micro;
     }
 
+    public int getPatch() {
+        return patch;
+    }
+
     public String getQualifier() {
         return qualifier;
     }
 
     public VersionNumber getBaseVersion() {
-        return new VersionNumber(major, minor, micro, null);
+        return new VersionNumber(major, minor, micro, patch, null, scheme);
     }
 
     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; }
+        if (major != other.major) {
+            return major - other.major;
+        }
+        if (minor != other.minor) {
+            return minor - other.minor;
+        }
+        if (micro != other.micro) {
+            return micro - other.micro;
+        }
+        if (patch != other.patch) {
+            return patch - other.patch;
+        }
         return Ordering.natural().nullsLast().compare(toLowerCase(qualifier), toLowerCase(other.qualifier));
     }
 
     public boolean equals(Object other) {
-        return other instanceof VersionNumber && compareTo((VersionNumber)other) == 0;
+        return other instanceof VersionNumber && compareTo((VersionNumber) other) == 0;
     }
 
     public int hashCode() {
         int result = major;
         result = 31 * result + minor;
         result = 31 * result + micro;
+        result = 31 * result + patch;
         result = 31 * result + Objects.hashCode(qualifier);
         return result;
     }
 
     public String toString() {
-        return String.format(VERSION_TEMPLATE, major, minor, micro, qualifier == null ? "" : "-" + qualifier);
+        return scheme.format(this);
     }
 
-    public static VersionNumber parse(String versionString) {
-        if (versionString == null) { return UNKNOWN; }
-        Matcher m = VERSION_PATTERN.matcher(versionString);
-        if (!m.matches()) { return UNKNOWN; }
+    public static VersionNumber version(int major) {
+        return new VersionNumber(major, 0, 0, 0, null, DEFAULT_SCHEME);
+    }
 
-        int major = Integer.valueOf(m.group(1));
-        String minorString = m.group(2);
-        int minor = minorString == null ? 0 : Integer.valueOf(minorString);
-        String microString = m.group(3);
-        int micro = microString == null ? 0 : Integer.valueOf(microString);
-        String qualifier = m.group(4);
+    /**
+     * Returns the default MAJOR.MINOR.MICRO-QUALIFIER scheme.
+     */
+    public static Scheme scheme() {
+        return DEFAULT_SCHEME;
+    }
+
+    /**
+     * Returns the MAJOR.MINOR.MICRO.PATCH-QUALIFIER scheme.
+     */
+    public static Scheme withPatchNumber() {
+        return PATCH_SCHEME;
+    }
 
-        return new VersionNumber(major, minor, micro, qualifier);
+    public static VersionNumber parse(String versionString) {
+        return DEFAULT_SCHEME.parse(versionString);
     }
 
     private String toLowerCase(@Nullable String string) {
         return string == null ? null : string.toLowerCase();
     }
+
+    public interface Scheme {
+        public VersionNumber parse(String value);
+
+        public String format(VersionNumber versionNumber);
+    }
+
+    private abstract static class AbstractScheme implements Scheme {
+        final int depth;
+
+        protected AbstractScheme(int depth) {
+            this.depth = depth;
+        }
+
+        public VersionNumber parse(String versionString) {
+            if (versionString == null || versionString.length() == 0) {
+                return UNKNOWN;
+            }
+            Scanner scanner = new Scanner(versionString);
+
+            int major = 0;
+            int minor = 0;
+            int micro = 0;
+            int patch = 0;
+
+            if (!scanner.hasDigit()) {
+                return UNKNOWN;
+            }
+            major = scanner.scanDigit();
+            if (scanner.isSeparatorAndDigit('.')) {
+                scanner.skipSeparator();
+                minor = scanner.scanDigit();
+                if (scanner.isSeparatorAndDigit('.')) {
+                    scanner.skipSeparator();
+                    micro = scanner.scanDigit();
+                    if (depth > 3 && scanner.isSeparatorAndDigit('.', '_')) {
+                        scanner.skipSeparator();
+                        patch = scanner.scanDigit();
+                    }
+                }
+            }
+
+            if (scanner.isEnd()) {
+                return new VersionNumber(major, minor, micro, patch, null, this);
+            }
+
+            if (scanner.isQualifier()) {
+                scanner.skipSeparator();
+                return new VersionNumber(major, minor, micro, patch, scanner.remainder(), this);
+            }
+
+            return UNKNOWN;
+        }
+
+        private static class Scanner {
+            int pos;
+            final String str;
+
+            private Scanner(String string) {
+                this.str = string;
+            }
+
+            boolean hasDigit() {
+                return pos < str.length() && Character.isDigit(str.charAt(pos));
+            }
+
+            boolean isSeparatorAndDigit(char... separators) {
+                return pos < str.length() - 1 && oneOf(separators) && Character.isDigit(str.charAt(pos + 1));
+            }
+
+            private boolean oneOf(char... separators) {
+                char current = str.charAt(pos);
+                for (int i = 0; i < separators.length; i++) {
+                    char separator = separators[i];
+                    if (current == separator) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            boolean isQualifier() {
+                return pos < str.length() - 1 && oneOf('.', '-');
+            }
+
+            int scanDigit() {
+                int start = pos;
+                while (hasDigit()) {
+                    pos++;
+                }
+                return Integer.parseInt(str.substring(start, pos));
+            }
+
+            public boolean isEnd() {
+                return pos == str.length();
+            }
+
+            private boolean skip(char ch) {
+                if (pos < str.length() && str.charAt(pos) == ch) {
+                    pos++;
+                    return true;
+                }
+                return false;
+            }
+
+            public void skipSeparator() {
+                pos++;
+            }
+
+            public String remainder() {
+                return pos == str.length() ? null : str.substring(pos);
+            }
+        }
+    }
+
+    private static class DefaultScheme extends AbstractScheme {
+        private static final String VERSION_TEMPLATE = "%d.%d.%d%s";
+
+        public DefaultScheme() {
+            super(3);
+        }
+
+        public String format(VersionNumber versionNumber) {
+            return String.format(VERSION_TEMPLATE, versionNumber.major, versionNumber.minor, versionNumber.micro, versionNumber.qualifier == null ? "" : "-" + versionNumber.qualifier);
+        }
+    }
+
+    private static class SchemeWithPatchVersion extends AbstractScheme {
+        private static final String VERSION_TEMPLATE = "%d.%d.%d.%d%s";
+
+        private SchemeWithPatchVersion() {
+            super(4);
+        }
+
+        public String format(VersionNumber versionNumber) {
+            return String.format(VERSION_TEMPLATE, versionNumber.major, versionNumber.minor, versionNumber.micro, versionNumber.patch, versionNumber.qualifier == null ? "" : "-" + versionNumber.qualifier);
+        }
+    }
+
 }
 
diff --git a/subprojects/core/src/main/groovy/org/gradle/util/WrapUtil.java b/subprojects/core/src/main/groovy/org/gradle/util/WrapUtil.java
index 84588e6..62e8e7c 100644
--- a/subprojects/core/src/main/groovy/org/gradle/util/WrapUtil.java
+++ b/subprojects/core/src/main/groovy/org/gradle/util/WrapUtil.java
@@ -16,7 +16,11 @@
 package org.gradle.util;
 
 import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Named;
+import org.gradle.api.NamedDomainObjectSet;
 import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.DefaultNamedDomainObjectSet;
+import org.gradle.internal.reflect.DirectInstantiator;
 
 import java.util.*;
 
@@ -42,6 +46,15 @@ public class WrapUtil {
     }
 
     /**
+     * Wraps the given items in a named domain object set.
+     */
+    public static <T extends Named> NamedDomainObjectSet<T> toNamedDomainObjectSet(Class<T> type, T... items) {
+        DefaultNamedDomainObjectSet<T> domainObjectSet = new DefaultNamedDomainObjectSet<T>(type, DirectInstantiator.INSTANCE);
+        CollectionUtils.addAll(domainObjectSet, items);
+        return domainObjectSet;
+    }
+
+    /**
      * Wraps the given items in a mutable ordered set.
      */
     public static <T> Set<T> toLinkedSet(T... items) {
diff --git a/subprojects/core/src/main/groovy/org/slf4j/impl/StaticLoggerBinder.java b/subprojects/core/src/main/groovy/org/slf4j/impl/StaticLoggerBinder.java
new file mode 100644
index 0000000..6fc2cb7
--- /dev/null
+++ b/subprojects/core/src/main/groovy/org/slf4j/impl/StaticLoggerBinder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.slf4j.impl;
+
+import org.gradle.internal.TrueTimeProvider;
+import org.gradle.logging.internal.slf4j.OutputEventListenerBackedLoggerContext;
+import org.slf4j.ILoggerFactory;
+import org.slf4j.spi.LoggerFactoryBinder;
+
+ at SuppressWarnings("UnusedDeclaration")
+public class StaticLoggerBinder implements LoggerFactoryBinder {
+
+    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
+    private static final String LOGGER_FACTORY_CLASS_STR = OutputEventListenerBackedLoggerContext.class.getName();
+
+    private final OutputEventListenerBackedLoggerContext factory = new OutputEventListenerBackedLoggerContext(System.out, System.err, new TrueTimeProvider());
+
+    public static StaticLoggerBinder getSingleton() {
+        return SINGLETON;
+    }
+
+    /**
+     * Declare the version of the SLF4J API this implementation is compiled against.
+     * The value of this field is usually modified with each release.
+     */
+    public static final String REQUESTED_API_VERSION = "1.7.7";
+
+    @Override
+    public ILoggerFactory getLoggerFactory() {
+        return factory;
+    }
+
+    @Override
+    public String getLoggerFactoryClassStr() {
+        return LOGGER_FACTORY_CLASS_STR;
+    }
+}
diff --git a/subprojects/core/src/main/resources/org/gradle/reporting/base-style.css b/subprojects/core/src/main/resources/org/gradle/reporting/base-style.css
index 8ddb790..4afa73e 100644
--- a/subprojects/core/src/main/resources/org/gradle/reporting/base-style.css
+++ b/subprojects/core/src/main/resources/org/gradle/reporting/base-style.css
@@ -32,6 +32,14 @@ body, a, a:visited {
     color: #a0a0a0;
 }
 
+#line-wrapping-toggle {
+    vertical-align: middle;
+}
+
+#label-for-line-wrapping-toggle {
+    vertical-align: middle;
+}
+
 ul {
     margin-left: 0;
 }
@@ -68,7 +76,6 @@ ul.tabLinks li {
     margin-right: 25px;
     border: solid 1px #d4d4d4;
     background-color: #f0f0f0;
-    behavior: url(htc/css3-pie-1.0beta3.htc);
 }
 
 ul.tabLinks li:hover {
@@ -160,3 +167,13 @@ span.code pre {
     width: auto !important;
     width: 700px;
 }
+
+span.wrapped pre {
+    word-wrap: break-word;
+    white-space: pre-wrap;
+    word-break: break-all;
+}
+
+label.hidden {
+    display: none;
+}
\ No newline at end of file
diff --git a/subprojects/core/src/main/resources/org/gradle/reporting/report.js b/subprojects/core/src/main/resources/org/gradle/reporting/report.js
index a4455e4..83bab4a 100644
--- a/subprojects/core/src/main/resources/org/gradle/reporting/report.js
+++ b/subprojects/core/src/main/resources/org/gradle/reporting/report.js
@@ -1,101 +1,194 @@
-var tabs = new Object();
-
-function initTabs() {
-    var container = document.getElementById('tabs');
-    tabs.tabs = findTabs(container);
-    tabs.titles = findTitles(tabs.tabs);
-    tabs.headers = findHeaders(container);
-    tabs.select = select;
-    tabs.deselectAll = deselectAll;
-    tabs.select(0);
-    return true;
-}
-
-window.onload = initTabs;
-
-function switchTab() {
-    var id = this.id.substr(1);
-    for (var i = 0; i < tabs.tabs.length; i++) {
-        if (tabs.tabs[i].id == id) {
-            tabs.select(i);
-            break;
+(function (window, document) {
+    "use strict";
+
+    var tabs = {};
+
+    function changeElementClass(element, classValue) {
+        if (element.getAttribute("className")) {
+            element.setAttribute("className", classValue);
+        } else {
+            element.setAttribute("class", classValue);
+        }
+    }
+
+    function getClassAttribute(element) {
+        if (element.getAttribute("className")) {
+            return element.getAttribute("className");
+        } else {
+            return element.getAttribute("class");
+        }
+    }
+
+    function addClass(element, classValue) {
+        changeElementClass(element, getClassAttribute(element) + " " + classValue);
+    }
+
+    function removeClass(element, classValue) {
+        changeElementClass(element, getClassAttribute(element).replace(classValue, ""));
+    }
+
+    function initTabs() {
+        var container = document.getElementById("tabs");
+
+        tabs.tabs = findTabs(container);
+        tabs.titles = findTitles(tabs.tabs);
+        tabs.headers = findHeaders(container);
+        tabs.select = select;
+        tabs.deselectAll = deselectAll;
+        tabs.select(0);
+
+        return true;
+    }
+
+    function getCheckBox() {
+        return document.getElementById("line-wrapping-toggle");
+    }
+
+    function getLabelForCheckBox() {
+        return document.getElementById("label-for-line-wrapping-toggle");
+    }
+
+    function findCodeBlocks() {
+        var spans = document.getElementById("tabs").getElementsByTagName("span");
+        var codeBlocks = [];
+        for (var i = 0; i < spans.length; ++i) {
+            if (spans[i].className.indexOf("code") >= 0) {
+                codeBlocks.push(spans[i]);
+            }
+        }
+        return codeBlocks;
+    }
+
+    function forAllCodeBlocks(operation) {
+        var codeBlocks = findCodeBlocks();
+
+        for (var i = 0; i < codeBlocks.length; ++i) {
+            operation(codeBlocks[i], "wrapped");
+        }
+    }
+
+    function toggleLineWrapping() {
+        var checkBox = getCheckBox();
+
+        if (checkBox.checked) {
+            forAllCodeBlocks(addClass);
+        } else {
+            forAllCodeBlocks(removeClass);
+        }
+    }
+
+    function initControls() {
+        if (findCodeBlocks().length > 0) {
+            var checkBox = getCheckBox();
+            var label = getLabelForCheckBox();
+
+            checkBox.onclick = toggleLineWrapping;
+            checkBox.checked = false;
+
+            removeClass(label, "hidden");
+         }
+    }
+
+    function switchTab() {
+        var id = this.id.substr(1);
+
+        for (var i = 0; i < tabs.tabs.length; i++) {
+            if (tabs.tabs[i].id === id) {
+                tabs.select(i);
+                break;
+            }
         }
+
+        return false;
     }
-    return false;
-}
-
-function select(i) {
-    this.deselectAll();
-    changeElementClass(this.tabs[i], 'tab selected');
-    changeElementClass(this.headers[i], 'selected');
-    while (this.headers[i].firstChild) {
-        this.headers[i].removeChild(this.headers[i].firstChild);
-    }
-    var h2 = document.createElement('H2');
-    h2.appendChild(document.createTextNode(this.titles[i]));
-    this.headers[i].appendChild(h2);
-}
-
-function deselectAll() {
-    for (var i = 0; i < this.tabs.length; i++) {
-        changeElementClass(this.tabs[i], 'tab deselected');
-        changeElementClass(this.headers[i], 'deselected');
+
+    function select(i) {
+        this.deselectAll();
+
+        changeElementClass(this.tabs[i], "tab selected");
+        changeElementClass(this.headers[i], "selected");
+
         while (this.headers[i].firstChild) {
             this.headers[i].removeChild(this.headers[i].firstChild);
         }
-        var a = document.createElement('A');
-        a.setAttribute('id', 'ltab' + i);
-        a.setAttribute('href', '#tab' + i);
-        a.onclick = switchTab;
-        a.appendChild(document.createTextNode(this.titles[i]));
-        this.headers[i].appendChild(a);
-    }
-}
-
-function changeElementClass(element, classValue) {
-    if (element.getAttribute('className')) {
-        /* IE */
-        element.setAttribute('className', classValue)
-    } else {
-        element.setAttribute('class', classValue)
-    }
-}
-
-function findTabs(container) {
-    return findChildElements(container, 'DIV', 'tab');
-}
-
-function findHeaders(container) {
-    var owner = findChildElements(container, 'UL', 'tabLinks');
-    return findChildElements(owner[0], 'LI', null);
-}
-
-function findTitles(tabs) {
-    var titles = new Array();
-    for (var i = 0; i < tabs.length; i++) {
-        var tab = tabs[i];
-        var header = findChildElements(tab, 'H2', null)[0];
-        header.parentNode.removeChild(header);
-        if (header.innerText) {
-            titles.push(header.innerText)
-        } else {
-            titles.push(header.textContent)
+
+        var h2 = document.createElement("H2");
+
+        h2.appendChild(document.createTextNode(this.titles[i]));
+        this.headers[i].appendChild(h2);
+    }
+
+    function deselectAll() {
+        for (var i = 0; i < this.tabs.length; i++) {
+            changeElementClass(this.tabs[i], "tab deselected");
+            changeElementClass(this.headers[i], "deselected");
+
+            while (this.headers[i].firstChild) {
+                this.headers[i].removeChild(this.headers[i].firstChild);
+            }
+
+            var a = document.createElement("A");
+
+            a.setAttribute("id", "ltab" + i);
+            a.setAttribute("href", "#tab" + i);
+            a.onclick = switchTab;
+            a.appendChild(document.createTextNode(this.titles[i]));
+
+            this.headers[i].appendChild(a);
         }
     }
-    return titles;
-}
-
-function findChildElements(container, name, targetClass) {
-    var elements = new Array();
-    var children = container.childNodes;
-    for (var i = 0; i < children.length; i++) {
-        var child = children.item(i);
-        if (child.nodeType == 1 && child.nodeName == name) {
-            if (targetClass && child.className.indexOf(targetClass) < 0) {
-                continue;
+
+    function findTabs(container) {
+        return findChildElements(container, "DIV", "tab");
+    }
+
+    function findHeaders(container) {
+        var owner = findChildElements(container, "UL", "tabLinks");
+        return findChildElements(owner[0], "LI", null);
+    }
+
+    function findTitles(tabs) {
+        var titles = [];
+
+        for (var i = 0; i < tabs.length; i++) {
+            var tab = tabs[i];
+            var header = findChildElements(tab, "H2", null)[0];
+
+            header.parentNode.removeChild(header);
+
+            if (header.innerText) {
+                titles.push(header.innerText);
+            } else {
+                titles.push(header.textContent);
+            }
+        }
+
+        return titles;
+    }
+
+    function findChildElements(container, name, targetClass) {
+        var elements = [];
+        var children = container.childNodes;
+
+        for (var i = 0; i < children.length; i++) {
+            var child = children.item(i);
+
+            if (child.nodeType === 1 && child.nodeName === name) {
+                if (targetClass && child.className.indexOf(targetClass) < 0) {
+                    continue;
+                }
+
+                elements.push(child);
             }
-            elements.push(child);
         }
+
+        return elements;
     }
-    return elements;
-}
+
+    // Entry point.
+
+    window.onload = function() {
+        initTabs();
+        initControls();
+    };
+} (window, window.document));
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
index d1e2ff7..39eb2e4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
@@ -16,11 +16,11 @@
 package org.gradle
 
 import org.gradle.api.GradleException
-import org.gradle.internal.exceptions.AbstractMultiCauseException
-import org.gradle.internal.exceptions.LocationAwareException
 import org.gradle.api.logging.LogLevel
 import org.gradle.execution.MultipleBuildFailures
 import org.gradle.initialization.BuildClientMetaData
+import org.gradle.internal.exceptions.DefaultMultiCauseException
+import org.gradle.internal.exceptions.LocationAwareException
 import org.gradle.logging.LoggingConfiguration
 import org.gradle.logging.ShowStacktrace
 import org.gradle.logging.StyledTextOutputFactory
@@ -299,25 +299,6 @@ org.gradle.api.GradleException: <message>
 '''
     }
 
-    def reportsBuildFailureWhenDebugLoggingEnabled() {
-        configuration.logLevel = LogLevel.DEBUG
-
-        GradleException exception = new GradleException('<message>')
-
-        expect:
-        reporter.buildFinished(result(exception))
-        output.value == '''
-{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
-
-* What went wrong:
-<message>
-
-* Exception is:
-org.gradle.api.GradleException: <message>
-{stacktrace}
-'''
-    }
-
     def result(Throwable failure) {
         BuildResult result = Mock()
         result.failure >> failure
@@ -351,7 +332,7 @@ org.gradle.api.GradleException: <message>
     }
 }
 
-class TestException extends AbstractMultiCauseException {
+class TestException extends DefaultMultiCauseException {
     TestException(String message, Throwable... causes) {
         super(message, causes)
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
index 2b4ade3..872298e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/StartParameterTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle
 
 import org.gradle.api.logging.LogLevel
+import org.gradle.internal.DefaultTaskExecutionRequest
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.SetSystemProperties
 import org.junit.Rule
@@ -41,7 +42,6 @@ class StartParameterTest extends Specification {
         parameter.systemPropertiesArgs = [b: 'b']
         parameter.gradleUserHomeDir = new File('b')
         parameter.initScripts = [new File('init script'), new File("/path/to/another init script")]
-        parameter.cacheUsage = CacheUsage.ON
         parameter.logLevel = LogLevel.WARN
         parameter.colorOutput = false
         parameter.continueOnFailure = true
@@ -102,12 +102,12 @@ class StartParameterTest extends Specification {
         parameter.logLevel == LogLevel.LIFECYCLE
         parameter.colorOutput
         parameter.taskNames.empty
+        parameter.taskRequests.empty
         parameter.excludedTaskNames.empty
         parameter.projectProperties.isEmpty()
         parameter.systemPropertiesArgs.isEmpty()
         !parameter.dryRun
         !parameter.continueOnFailure
-        parameter.refreshOptions == RefreshOptions.NONE
         !parameter.rerunTasks
         !parameter.recompileScripts
         !parameter.refreshDependencies
@@ -235,12 +235,27 @@ class StartParameterTest extends Specification {
         assertThat(parameter, isSerializable())
     }
 
+    void "considers system properties for null user home dir"() {
+        def gradleUserHome = tmpDir.file("someGradleUserHomePath")
+        System.setProperty(StartParameter.GRADLE_USER_HOME_PROPERTY_KEY, gradleUserHome.absolutePath)
+
+        given:
+        StartParameter parameter = new StartParameter()
+        parameter.gradleUserHomeDir = tmpDir.file("ignore-me")
+
+        when:
+        parameter.gradleUserHomeDir = null
+
+        then:
+        parameter.gradleUserHomeDir == gradleUserHome
+        assertThat(parameter, isSerializable())
+    }
+
     void "creates parameter for new build"() {
         StartParameter parameter = new StartParameter()
 
         // Copied properties
         parameter.gradleUserHomeDir = new File("home")
-        parameter.cacheUsage = CacheUsage.REBUILD
         parameter.logLevel = LogLevel.DEBUG
         parameter.colorOutput = false
         parameter.configureOnDemand = true
@@ -267,7 +282,6 @@ class StartParameterTest extends Specification {
 
         newParameter.configureOnDemand == parameter.configureOnDemand
         newParameter.gradleUserHomeDir == parameter.gradleUserHomeDir
-        newParameter.cacheUsage == parameter.cacheUsage
         newParameter.logLevel == parameter.logLevel
         newParameter.colorOutput == parameter.colorOutput
         newParameter.continueOnFailure == parameter.continueOnFailure
@@ -276,6 +290,7 @@ class StartParameterTest extends Specification {
         newParameter.recompileScripts == parameter.recompileScripts
 
         newParameter.buildFile == null
+        newParameter.taskRequests.empty
         newParameter.taskNames.empty
         newParameter.excludedTaskNames.empty
         newParameter.currentDir == new File(System.getProperty("user.dir")).getCanonicalFile()
@@ -314,4 +329,51 @@ class StartParameterTest extends Specification {
         then:
         parameter.allInitScripts == [userMainInit, userInit1, userInit2, distroInit1, distroInit2]
     }
+
+    def 'taskNames getter defaults to taskParameters'() {
+        def parameter = new StartParameter()
+        def requests = [new DefaultTaskExecutionRequest(['a']), new DefaultTaskExecutionRequest(['b'])]
+
+        when:
+        parameter.taskRequests = requests
+
+        then:
+        parameter.taskNames == [ 'a', 'b' ]
+        parameter.taskRequests == requests
+    }
+
+    def 'taskNames setter defaults to taskParameters'() {
+        StartParameter parameter = new StartParameter()
+
+        when:
+        parameter.taskNames = [ 'a', 'b' ]
+
+        then:
+        parameter.taskNames == [ 'a', 'b' ]
+        parameter.taskRequests == [ new DefaultTaskExecutionRequest(['a', 'b']) ]
+
+        when:
+        parameter.taskNames = null
+
+        then:
+        parameter.taskNames == []
+        parameter.taskRequests == []
+    }
+
+
+    def 'parallel project execution linked to number of parallel threads'() {
+        StartParameter parameter = new StartParameter()
+
+        when:
+        parameter.parallelThreadCount = 10
+
+        then:
+        parameter.parallelProjectExecutionEnabled
+
+        when:
+        parameter.parallelProjectExecutionEnabled = false
+
+        then:
+        parameter.parallelThreadCount == 0
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/file/MicroBenchmarkPerformanceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/file/MicroBenchmarkPerformanceTest.groovy
new file mode 100644
index 0000000..3ca7b85
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/file/MicroBenchmarkPerformanceTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.GFileUtils
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+import spock.lang.Unroll
+
+/**
+ * Created by Rene on 28/01/15.
+ * Temporary tests for nailing down root cause of slow java 8 builds on windows
+ *
+ */
+class MicroBenchmarkPerformanceTest extends Specification{
+
+    final def project = TestUtil.createRootProject()
+
+    @Unroll
+    def "creating #number of files"() {
+        expect:
+        number.times {
+            touch("src/test/dummy${it}.s")
+        }
+        where:
+        number << [10, 100, 1000]
+    }
+
+
+
+    def touch(String filePath) {
+        GFileUtils.touch(project.file(filePath))
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractClassGeneratorTestGroovy.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractClassGeneratorTestGroovy.groovy
index 26c24dd..6cbf038 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractClassGeneratorTestGroovy.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractClassGeneratorTestGroovy.groovy
@@ -21,6 +21,19 @@ class AbstractClassGeneratorTestGroovy {
     public static class BeanWithGroovyBoolean {
         boolean smallB
         Boolean bigB
+        Boolean mixedB
+
+        boolean getMixedB() { mixedB }
+
+        Boolean isMixedB() { mixedB }
+
+        void setMixedB(Boolean mixedB) {
+            this.mixedB = mixedB
+        }
+
+        void setMixedB(boolean mixedB) {
+            this.mixedB = mixedB
+        }
     }
 
 }
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 9640f27..44cda14 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
@@ -19,10 +19,11 @@ import org.gradle.api.Action
 import org.gradle.api.InvalidUserDataException
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
+import spock.lang.Issue
 import spock.lang.Specification
 
 class AbstractNamedDomainObjectContainerTest extends Specification {
-    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
     AbstractNamedDomainObjectContainer container = instantiator.newInstance(TestContainer.class, instantiator)
 
     def "is dynamic object aware"() {
@@ -103,7 +104,11 @@ class AbstractNamedDomainObjectContainerTest extends Specification {
 
         when:
         container.configure {
-            someObj { unknown { anotherUnknown(2) } }
+            someObj {
+                unknown {
+                    anotherUnknown(2)
+                }
+            }
         }
 
         then:
@@ -180,7 +185,9 @@ class AbstractNamedDomainObjectContainerTest extends Specification {
         container.configure {
             someObj {
                 children {
-                    child1 { prop = 'child1' }
+                    child1 {
+                        prop = 'child1'
+                    }
                     child2
                 }
             }
@@ -202,6 +209,24 @@ class AbstractNamedDomainObjectContainerTest extends Specification {
         container.list1.prop == 'list1'
         container.list2.prop == 'list2'
     }
+
+    static class Owner {
+        void thing(Closure closure) {}
+    }
+
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3126")
+    def "can create element when owner scope has item with same name"() {
+        when:
+        new Owner().with {
+            container.configure {
+                thing {}
+            }
+        }
+
+        then:
+        container.names.toList() == ["thing"]
+    }
 }
 
 class DynamicOwner {
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
deleted file mode 100644
index c8d5ae1..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskSpec.groovy
+++ /dev/null
@@ -1,67 +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
-
-import org.gradle.api.Action
-import org.gradle.api.Task
-import org.gradle.api.internal.project.AbstractProject
-import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory
-import org.gradle.api.internal.project.taskfactory.TaskFactory
-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.TestUtil
-import org.junit.Test
-import spock.lang.Specification
-
-import static org.junit.Assert.assertTrue
-
-class AbstractTaskSpec extends Specification {
-
-    private DefaultServiceRegistry serviceRegistry = new DefaultServiceRegistry();
-    private Instantiator instantiator = new DependencyInjectingInstantiator(serviceRegistry);
-    private final AnnotationProcessingTaskFactory rootFactory = new AnnotationProcessingTaskFactory(new TaskFactory(new AsmBackedClassGenerator()));
-
-    public static class TestTask extends AbstractTask {
-
-    }
-
-    public Task createTask(String name) {
-        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));
-        assertTrue(TestTask.class.isAssignableFrom(task.getClass()));
-        return TestTask.class.cast(task);
-    }
-
-    @Test
-    def "can add action to a task via Task.getActions() List"() {
-        setup:
-        TestTask task = createTask("task")
-        when:
-        def actions = task.getActions()
-        and:
-        def action = Mock(Action)
-
-        actions.add(action)
-        then:
-        task.actions.size() == 1
-        actions.size() == 1
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskTest.groovy
new file mode 100644
index 0000000..5a29f23
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/AbstractTaskTest.groovy
@@ -0,0 +1,84 @@
+/*
+ * 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
+
+import org.gradle.api.Action
+import org.gradle.api.Task
+import org.gradle.api.internal.project.AbstractProject
+import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory
+import org.gradle.api.internal.project.taskfactory.TaskFactory
+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.TestUtil
+import spock.lang.Specification
+
+import static org.junit.Assert.assertTrue
+
+class AbstractTaskTest extends Specification {
+
+    private DefaultServiceRegistry serviceRegistry = new DefaultServiceRegistry()
+    private Instantiator instantiator = new DependencyInjectingInstantiator(serviceRegistry)
+    private final AnnotationProcessingTaskFactory rootFactory = new AnnotationProcessingTaskFactory(new TaskFactory(new AsmBackedClassGenerator()))
+
+    public static class TestTask extends AbstractTask {
+    }
+
+    public TaskInternal createTask(String name) {
+        AbstractProject project = TestUtil.createRootProject()
+        DefaultServiceRegistry registry = new DefaultServiceRegistry()
+        registry.add(Instantiator, DirectInstantiator.INSTANCE)
+        TaskInternal task = rootFactory.createChild(project, instantiator).createTask(GUtil.map(Task.TASK_TYPE, TestTask, Task.TASK_NAME, name))
+        assertTrue(TestTask.isAssignableFrom(task.getClass()))
+        return task
+    }
+
+    def "can add action to a task via Task.getActions() List"() {
+        setup:
+        TestTask task = createTask("task")
+        when:
+        def actions = task.actions
+        and:
+        def action = Mock(Action)
+
+        actions.add(action)
+        then:
+        task.actions.size() == 1
+        actions.size() == 1
+    }
+
+    def "can detect tasks with custom actions added"() {
+        when:
+        def task = createTask("task")
+
+        then:
+        !task.hasCustomActions
+
+        when:
+        task.prependParallelSafeAction {}
+
+        then:
+        !task.hasCustomActions
+
+        when:
+        task.doFirst {}
+
+        then:
+        task.hasCustomActions
+    }
+}
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 02f5d4c..ae1f6a4 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
@@ -20,15 +20,18 @@ import org.gradle.api.Action
 import org.gradle.api.NonExtensible
 import org.gradle.api.plugins.ExtensionAware
 import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.service.ServiceRegistry
 import org.gradle.internal.typeconversion.TypeConversionException
 import org.gradle.util.ConfigureUtil
 import spock.lang.Issue
 import spock.lang.Specification
 
+import javax.inject.Inject
+
 class AsmBackedClassGeneratorGroovyTest extends Specification {
 
     def generator = new AsmBackedClassGenerator()
-    def instantiator = new ClassGeneratorBackedInstantiator(generator, new DirectInstantiator())
+    def instantiator = new ClassGeneratorBackedInstantiator(generator, DirectInstantiator.INSTANCE)
 
     private <T> T create(Class<T> clazz, Object... args) {
         instantiator.newInstance(clazz, *args)
@@ -41,7 +44,7 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
 
         when:
         conf(thing) {
-            m1(1,2,3)
+            m1(1, 2, 3)
             p1 = 1
             p1 = p1 + 1
         }
@@ -139,6 +142,20 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         tester.lastArgs.size() == 2
         tester.lastArgs.first() == "1"
         tester.lastArgs.last().is(closure)
+
+        expect: // can return values
+        tester.oneActionReturnsString({}) == "string"
+        tester.lastArgs.last() instanceof ClosureBackedAction
+        tester.twoArgsReturnsString("foo", {}) == "string"
+        tester.lastArgs.last() instanceof ClosureBackedAction
+        tester.oneActionReturnsInt({}) == 1
+        tester.lastArgs.last() instanceof ClosureBackedAction
+        tester.twoArgsReturnsInt("foo", {}) == 1
+        tester.lastArgs.last() instanceof ClosureBackedAction
+        tester.oneActionReturnsArray({}) == [] as Object[]
+        tester.lastArgs.last() instanceof ClosureBackedAction
+        tester.twoArgsReturnsArray("foo", {}) == [] as Object[]
+        tester.lastArgs.last() instanceof ClosureBackedAction
     }
 
     def "can coerce enum values"() {
@@ -196,6 +213,14 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         i.setAtFieldInit == i.class
     }
 
+    def "can use inherited properties during construction"() {
+        when:
+        def i = create(UsesInheritedPropertiesDuringConstruction)
+
+        then:
+        i.someValue == 'value'
+    }
+
     def "can call private methods internally"() {
         /*
             We have to specially handle private methods in our dynamic protocol.
@@ -228,7 +253,6 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         i.calledWith == Integer
     }
 
-
     def "can use non extensible objects"() {
         def i = create(NonExtensibleObject)
 
@@ -254,16 +278,16 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         ConfigureUtil.configure(c, o)
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2863")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2863")
     def "checked exceptions from private methods are thrown"() {
-            when:
+        when:
         create(CallsPrivateMethods).callsPrivateThatThrowsCheckedException("1")
 
         then:
         thrown IOException
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2863")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2863")
     def "private methods are called with Groovy semantics"() {
         when:
         def foo = "bar"
@@ -272,6 +296,64 @@ class AsmBackedClassGeneratorGroovyTest extends Specification {
         then:
         obj.callsPrivateStringMethodWithGString("$foo") == "BAR"
     }
+
+    def "can inject service using a service getter method"() {
+        given:
+        def services = Mock(ServiceRegistry)
+        def service = Mock(Runnable)
+        _ * services.get(Runnable) >> service
+
+        when:
+        def obj = create(BeanWithServices, services)
+
+        then:
+        obj.thing == service
+        obj.getThing() == service
+        obj.getProperty("thing") == service
+    }
+
+    def "can optionally set injected service using a service setter method"() {
+        given:
+        def services = Mock(ServiceRegistry)
+        def service = Mock(Runnable)
+
+        when:
+        def obj = create(BeanWithMutableServices, services)
+        obj.thing = service
+
+        then:
+        obj.thing == service
+        obj.getThing() == service
+        obj.getProperty("thing") == service
+
+        and:
+        0 * services._
+    }
+
+    def "service lookup is lazy and the result is cached"() {
+        given:
+        def services = Mock(ServiceRegistry)
+        def service = Mock(Runnable)
+
+        when:
+        def obj = create(BeanWithServices, services)
+
+        then:
+        0 * services._
+
+        when:
+        obj.thing
+
+        then:
+        1 * services.get(Runnable) >> service
+        0 * services._
+
+        when:
+        obj.thing
+
+        then:
+        0 * services._
+    }
 }
 
 enum TestEnum {
@@ -367,15 +449,72 @@ class ActionsTester {
         lastMethod = "hasClosure"
         lastArgs = [s, closure]
     }
+
+    String oneActionReturnsString(Action action) {
+        lastMethod = "oneAction"
+        lastArgs = [action]
+        action.execute(subject)
+        "string"
+    }
+
+    String twoArgsReturnsString(String first, Action action) {
+        lastMethod = "twoArgs"
+        lastArgs = [first, action]
+        action.execute(subject)
+        "string"
+    }
+
+    int oneActionReturnsInt(Action action) {
+        lastMethod = "oneAction"
+        lastArgs = [action]
+        action.execute(subject)
+        1
+    }
+
+    int twoArgsReturnsInt(String first, Action action) {
+        lastMethod = "twoArgs"
+        lastArgs = [first, action]
+        action.execute(subject)
+        1
+    }
+
+    Object[] oneActionReturnsArray(Action action) {
+        lastMethod = "oneAction"
+        lastArgs = [action]
+        action.execute(subject)
+        [] as Object[]
+    }
+
+    Object[] twoArgsReturnsArray(String first, Action action) {
+        lastMethod = "twoArgs"
+        lastArgs = [first, action]
+        action.execute(subject)
+        [] as Object[]
+    }
+
 }
 
 class CallsMethodDuringConstruction {
 
     Class setAtFieldInit = getClass()
+    Map<String, String> someMap = [:]
     Class setDuringConstructor
 
     CallsMethodDuringConstruction() {
-        setDuringConstructor = getClass()
+        setDuringConstructor = setAtFieldInit
+        someMap['a'] = 'b'
+        assert setDuringConstructor
+    }
+}
+
+class UsesInheritedPropertiesDuringConstruction extends TestJavaObject {
+    UsesInheritedPropertiesDuringConstruction() {
+        assert metaClass != null
+        assert getMetaClass() != null
+        assert metaClass.getProperty(this, "someValue") == "value"
+        assert asDynamicObject.getProperty("someValue") == "value"
+        assert getProperty("someValue") == "value"
+        assert someValue == "value"
     }
 }
 
@@ -404,7 +543,7 @@ class CallsPrivateMethods {
     }
 
     // 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
+    // See https://issues.gradle.org/browse/GRADLE-2863
     def callsPrivateThatThrowsCheckedException(s) {
         try {
             throwsCheckedException(s)
@@ -426,3 +565,22 @@ class CallsPrivateMethods {
         str.toUpperCase()
     }
 }
+
+class BeanWithServices {
+    ServiceRegistry services
+
+    BeanWithServices(ServiceRegistry services) {
+        this.services = services
+    }
+
+    @Inject
+    Runnable getThing() { throw new UnsupportedOperationException() }
+}
+
+class BeanWithMutableServices extends BeanWithServices {
+    BeanWithMutableServices(ServiceRegistry services) {
+        super(services)
+    }
+
+    void setThing(Runnable runnnable) { throw new UnsupportedOperationException() }
+}
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 844b607..3fa5d3d 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
@@ -32,19 +32,24 @@ import spock.lang.Issue;
 
 import javax.inject.Inject;
 import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.lang.reflect.*;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Callable;
 
+import static org.gradle.api.internal.AbstractClassGeneratorTestGroovy.BeanWithGroovyBoolean;
+import static org.gradle.util.Matchers.isEmpty;
 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.*;
 import static org.junit.Assert.*;
 
-import static org.gradle.api.internal.AbstractClassGeneratorTestGroovy.*;
-
 public class AsmBackedClassGeneratorTest {
     private final AbstractClassGenerator generator = new AsmBackedClassGenerator();
 
@@ -319,7 +324,7 @@ public class AsmBackedClassGeneratorTest {
     }
 
     @Test
-    public void appliesConventionMappingToEachGetter() throws Exception {
+    public void appliesConventionMappingToEachProperty() throws Exception {
         Class<? extends Bean> generatedClass = generator.generate(Bean.class);
         assertTrue(IConventionAware.class.isAssignableFrom(generatedClass));
         Bean bean = generatedClass.newInstance();
@@ -343,6 +348,85 @@ public class AsmBackedClassGeneratorTest {
     }
 
     @Test
+    public void appliesConventionMappingToPropertyWithMultipleSetters() throws Exception {
+        BeanWithVariousGettersAndSetters bean = generator.newInstance(BeanWithVariousGettersAndSetters.class);
+        new DslObject(bean).getConventionMapping().map("overloaded", new Callable<String>() {
+            public String call() {
+                return "conventionValue";
+            }
+        });
+
+        assertThat(bean.getOverloaded(), equalTo("conventionValue"));
+
+        bean.setOverloaded("value");
+        assertThat(bean.getOverloaded(), equalTo("chars = value"));
+
+        bean = generator.newInstance(BeanWithVariousGettersAndSetters.class);
+        new DslObject(bean).getConventionMapping().map("overloaded", new Callable<String>() {
+            public String call() {
+                return "conventionValue";
+            }
+        });
+
+        assertThat(bean.getOverloaded(), equalTo("conventionValue"));
+
+        bean.setOverloaded(12);
+        assertThat(bean.getOverloaded(), equalTo("number = 12"));
+
+        bean = generator.newInstance(BeanWithVariousGettersAndSetters.class);
+        new DslObject(bean).getConventionMapping().map("overloaded", new Callable<String>() {
+            public String call() {
+                return "conventionValue";
+            }
+        });
+
+        assertThat(bean.getOverloaded(), equalTo("conventionValue"));
+
+        bean.setOverloaded(true);
+        assertThat(bean.getOverloaded(), equalTo("object = true"));
+    }
+
+    @Test
+    public void appliesConventionMappingToPropertyWithGetterCovariantType() throws Exception {
+        CovariantPropertyTypes bean = generator.newInstance(CovariantPropertyTypes.class);
+
+        new DslObject(bean).getConventionMapping().map("value", new Callable<String>() {
+            public String call() {
+                return "conventionValue";
+            }
+        });
+
+        assertThat(bean.getValue(), equalTo("conventionValue"));
+
+        bean.setValue(12);
+        assertThat(bean.getValue(), equalTo("12"));
+    }
+
+    @Test
+    public void appliesConventionMappingToProtectedMethods() throws Exception {
+        BeanWithNonPublicProperties bean = generator.newInstance(BeanWithNonPublicProperties.class);
+
+        assertThat(bean.getPackageProtected(), equalTo("package-protected"));
+        assertThat(bean.getProtected(), equalTo("protected"));
+        assertThat(bean.getPrivate(), equalTo("private"));
+
+        IConventionAware conventionAware = (IConventionAware) bean;
+        conventionAware.getConventionMapping().map("packageProtected", new Callable<String>() {
+            public String call() {
+                return "1";
+            }
+        });
+        conventionAware.getConventionMapping().map("protected", new Callable<String>() {
+            public String call() {
+                return "2";
+            }
+        });
+
+        assertThat(bean.getPackageProtected(), equalTo("1"));
+        assertThat(bean.getProtected(), equalTo("2"));
+    }
+
+    @Test
     @Issue("GRADLE-2163")
     public void appliesConventionMappingToGroovyBoolean() throws Exception {
         BeanWithGroovyBoolean bean = generator.generate(BeanWithGroovyBoolean.class).newInstance();
@@ -350,6 +434,7 @@ public class AsmBackedClassGeneratorTest {
         assertTrue(bean instanceof IConventionAware);
         assertThat(bean.getSmallB(), equalTo(false));
         assertThat(bean.getBigB(), nullValue());
+        assertThat(bean.getMixedB(), equalTo(false));
 
         IConventionAware conventionAware = (IConventionAware) bean;
 
@@ -375,6 +460,15 @@ public class AsmBackedClassGeneratorTest {
         assertThat(bean.getBigB(), equalTo(Boolean.TRUE));
         bean.setBigB(Boolean.FALSE);
         assertThat(bean.getBigB(), equalTo(Boolean.FALSE));
+
+        conventionAware.getConventionMapping().map("mixedB", new Callable<Object>() {
+            public Object call() throws Exception {
+                return Boolean.TRUE;
+            }
+        });
+
+        assertThat(bean.getMixedB(), equalTo(true));
+        assertThat(bean.isMixedB(), equalTo(Boolean.TRUE));
     }
 
     @Test
@@ -544,8 +638,9 @@ public class AsmBackedClassGeneratorTest {
         bean.getAsDynamicObject().setProperty("prop", "value2");
         assertThat(call("{ it.prop }", bean), equalTo((Object) "value2"));
 
-        bean.getAsDynamicObject().setProperty("dynamicProp", "value");
-        assertThat(call("{ it.dynamicProp }", bean), equalTo((Object) "value"));
+        call("{ it.ext.anotherProp = 12 }", bean);
+        assertThat(bean.getAsDynamicObject().getProperty("anotherProp"), equalTo((Object) 12));
+        assertThat(call("{ it.anotherProp }", bean), equalTo((Object) 12));
     }
 
     @Test
@@ -570,6 +665,18 @@ public class AsmBackedClassGeneratorTest {
 
         call("{ it.primitive 12}", bean);
         assertThat(bean.getPrimitive(), equalTo(12));
+
+        call("{ it.bool true}", bean);
+        assertThat(bean.isBool(), equalTo(true));
+
+        call("{ it.overloaded 'value'}", bean);
+        assertThat(bean.getOverloaded(), equalTo("chars = value"));
+
+        call("{ it.overloaded 12}", bean);
+        assertThat(bean.getOverloaded(), equalTo("number = 12"));
+
+        call("{ it.overloaded true}", bean);
+        assertThat(bean.getOverloaded(), equalTo("object = true"));
     }
 
     @Test
@@ -666,6 +773,10 @@ public class AsmBackedClassGeneratorTest {
         bean.prop = "value";
 
         call("{def value; it.doStuff { value = it }; assert value == \'value\' }", bean);
+
+        BeanWithOverriddenMethods subBean = generator.generate(BeanWithOverriddenMethods.class).newInstance();
+
+        call("{def value; it.doStuff { value = it }; assert value == \'overloaded\' }", subBean);
     }
 
     @Test
@@ -680,6 +791,24 @@ public class AsmBackedClassGeneratorTest {
         new DslObject(generator.generate(Bean.class).newInstance());
     }
 
+    @Test
+    public void includesNotInheritedTypeAnnotations() throws IllegalAccessException, InstantiationException {
+        Class<? extends AnnotatedBean> generatedClass = generator.generate(AnnotatedBean.class);
+
+        BeanAnnotation annotation = generatedClass.getAnnotation(BeanAnnotation.class);
+        assertThat(annotation, notNullValue());
+        assertThat(annotation.value(), equalTo("test"));
+        assertThat(annotation.values(), equalTo(new String[] {"1", "2"}));
+        assertThat(annotation.enumValue(), equalTo(AnnotationEnum.A));
+        assertThat(annotation.enumValues(), equalTo(new AnnotationEnum[] {AnnotationEnum.A, AnnotationEnum.B}));
+        assertThat(annotation.number(), equalTo(1));
+        assertThat(annotation.numbers(), equalTo(new int[] {1, 2}));
+        assertThat(annotation.clazz().equals(Integer.class), equalTo(true));
+        assertThat(annotation.classes(), equalTo(new Class<?>[] {Integer.class}));
+        assertThat(annotation.annotation().value(), equalTo("nested"));
+        assertThat(annotation.annotations()[0].value(), equalTo("nested array"));
+    }
+
     public static class Bean {
         private String prop;
 
@@ -700,12 +829,67 @@ public class AsmBackedClassGeneratorTest {
         }
     }
 
+    public static class BeanWithOverriddenMethods extends Bean {
+        @Override
+        public String getProp() {
+            return super.getProp();
+        }
+
+        @Override
+        public void setProp(String prop) {
+            super.setProp(prop);
+        }
+
+        @Override
+        public String doStuff(String value) {
+            return super.doStuff(value);
+        }
+
+        @Override
+        public void doStuff(Action<String> action) {
+            action.execute("overloaded");
+        }
+    }
+
+    public static class ParentBean {
+        Object value;
+
+        public Object getValue() {
+            return value;
+        }
+
+        public void setValue(Object value) {
+            this.value = value;
+        }
+    }
+
+    public static class CovariantPropertyTypes extends ParentBean {
+        @Override
+        public String getValue() {
+            return String.valueOf(super.getValue());
+        }
+    }
+
     public static class BeanWithReadOnlyProperties {
         public String getProp() {
             return "value";
         }
     }
 
+    public static class BeanWithNonPublicProperties {
+        String getPackageProtected() {
+            return "package-protected";
+        }
+
+        protected String getProtected() {
+            return "protected";
+        }
+
+        private String getPrivate() {
+            return "private";
+        }
+    }
+
     public static class CollectionBean {
         private List<String> prop = new ArrayList<String>();
 
@@ -954,6 +1138,7 @@ public class AsmBackedClassGeneratorTest {
         private boolean bool;
         private String finalGetter;
         private Integer writeOnly;
+        private String overloaded;
 
         public int getPrimitive() {
             return primitive;
@@ -963,6 +1148,14 @@ public class AsmBackedClassGeneratorTest {
             this.primitive = primitive;
         }
 
+        public boolean isBool() {
+            return bool;
+        }
+
+        public void setBool(boolean bool) {
+            this.bool = bool;
+        }
+
         public final String getFinalGetter() {
             return finalGetter;
         }
@@ -974,6 +1167,22 @@ public class AsmBackedClassGeneratorTest {
         public void setWriteOnly(Integer value) {
             writeOnly = value;
         }
+
+        public String getOverloaded() {
+            return overloaded;
+        }
+
+        public void setOverloaded(Number overloaded) {
+            this.overloaded = String.format("number = %s", overloaded);
+        }
+
+        public void setOverloaded(CharSequence overloaded) {
+            this.overloaded = String.format("chars = %s", overloaded);
+        }
+
+        public void setOverloaded(Object overloaded) {
+            this.overloaded = String.format("object = %s", overloaded);
+        }
     }
 
     public interface SomeType {
@@ -1040,4 +1249,44 @@ public class AsmBackedClassGeneratorTest {
 
     private static class PrivateBean {
     }
+
+    public enum AnnotationEnum {
+        A, B
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    public static @interface NestedBeanAnnotation {
+        String value();
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    public static @interface BeanAnnotation {
+        String value();
+        String[] values();
+        AnnotationEnum enumValue();
+        AnnotationEnum[] enumValues();
+        int number();
+        int[] numbers();
+        Class<?> clazz();
+        Class<?>[] classes();
+        NestedBeanAnnotation annotation();
+        NestedBeanAnnotation[] annotations();
+    }
+
+    @BeanAnnotation(
+            value = "test",
+            values = {"1", "2"},
+            enumValue = AnnotationEnum.A,
+            enumValues = {AnnotationEnum.A, AnnotationEnum.B},
+            number = 1,
+            numbers = {1, 2},
+            clazz = Integer.class,
+            classes = {Integer.class},
+            annotation = @NestedBeanAnnotation("nested"),
+            annotations = {@NestedBeanAnnotation("nested array")}
+    )
+    public static class AnnotatedBean {
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/ClosureBackedActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/ClosureBackedActionTest.groovy
index 7a1fb4c..aa3cb24 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/ClosureBackedActionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/ClosureBackedActionTest.groovy
@@ -66,7 +66,6 @@ class ClosureBackedActionTest extends Specification {
         then:
         def e = thrown InvalidActionClosureException
         e.closure.is(closure)
-        e.argument.is(arg)
         e.message == "The closure '${closure.toString()}' is not valid as an action for argument '1'. It should accept no parameters, or one compatible with type 'java.lang.String'. It accepts (java.util.Map)."
     }
 
@@ -81,7 +80,6 @@ class ClosureBackedActionTest extends Specification {
         then:
         def e = thrown InvalidActionClosureException
         e.closure.is(closure)
-        e.argument.is(arg)
         e.message == "The closure '${closure.toString()}' is not valid as an action for argument '1'. It should accept no parameters, or one compatible with type 'java.lang.String'. It accepts (java.util.Map, java.util.List)."
     }
 
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 1a8a981..7e8de89 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
@@ -16,8 +16,7 @@
 package org.gradle.api.internal
 
 import org.gradle.api.DomainObjectCollection
-
-import spock.lang.*
+import spock.lang.Specification
 
 class CompositeDomainObjectSetTest extends Specification {
 
@@ -30,7 +29,7 @@ class CompositeDomainObjectSetTest extends Specification {
     }
 
     protected composite(DomainObjectCollection... collections) {
-        new CompositeDomainObjectSet(type, *collections)
+        CompositeDomainObjectSet.create(type, *collections)
     }
 
     def "empty composite contains no elements"() {
@@ -274,7 +273,7 @@ class CompositeDomainObjectSetTest extends Specification {
         calledFor == ["j", "k", "l", "m"]
 
         and:
-        superComposite.toList() == ["a", "j", "b", "k", "c", "l", "d", "m"]
+        superComposite.toSet() == ["a", "j", "b", "k", "c", "l", "d", "m"].toSet()
 
         when:
         superComposite.whenObjectRemoved { calledFor << it }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectListTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectListTest.groovy
index 531e5b9..9c56eb3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectListTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultNamedDomainObjectListTest.groovy
@@ -27,7 +27,7 @@ class DefaultNamedDomainObjectListTest extends Specification {
             return object.toString()
         }
     }
-    final DefaultNamedDomainObjectList<String> list = new DefaultNamedDomainObjectList<String>(String, new DirectInstantiator(), toStringNamer)
+    final DefaultNamedDomainObjectList<String> list = new DefaultNamedDomainObjectList<String>(String, DirectInstantiator.INSTANCE, toStringNamer)
 
     def "can add element at given index"() {
         given:
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 3ea532d..89e54b4 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
@@ -20,10 +20,11 @@ import groovy.lang.MissingPropertyException;
 import org.gradle.api.*;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
+import org.gradle.internal.reflect.DirectInstantiator;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.GUtil;
-import org.gradle.util.TestUtil;
 import org.gradle.util.TestClosure;
+import org.gradle.util.TestUtil;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -41,8 +42,12 @@ import static org.junit.Assert.*;
 
 @RunWith(JMock.class)
 public class DefaultNamedDomainObjectSetTest {
-    private final org.gradle.internal.reflect.Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new org.gradle.internal.reflect.DirectInstantiator());
-    private final Namer<Bean> namer = new Namer<Bean>() { public String determineName(Bean bean) { return bean.name; } };
+    private final org.gradle.internal.reflect.Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE);
+    private final Namer<Bean> namer = new Namer<Bean>() {
+        public String determineName(Bean bean) {
+            return bean.name;
+        }
+    };
     @SuppressWarnings("unchecked")
     private final DefaultNamedDomainObjectSet<Bean> container = instantiator.newInstance(DefaultNamedDomainObjectSet.class, Bean.class, instantiator, namer);
     private final JUnit4Mockery context = new JUnit4Mockery();
@@ -63,11 +68,11 @@ public class DefaultNamedDomainObjectSetTest {
         Bean bean1 = new Bean("a");
         Bean bean2 = new Bean("b");
         Bean bean3 = new Bean("c");
-    
+
         container.add(bean2);
         container.add(bean1);
         container.add(bean3);
-    
+
         assertThat(toList(container), equalTo(toList(bean1, bean2, bean3)));
     }
 
@@ -82,11 +87,11 @@ public class DefaultNamedDomainObjectSetTest {
         Bean bean1 = new Bean("a");
         Bean bean2 = new Bean("b");
         Bean bean3 = new Bean("c");
-    
+
         container.add(bean2);
         container.add(bean1);
         container.add(bean3);
-    
+
         Iterator<Bean> iterator = container.iterator();
         assertThat(iterator.next(), sameInstance(bean1));
         assertThat(iterator.next(), sameInstance(bean2));
@@ -199,7 +204,9 @@ public class DefaultNamedDomainObjectSetTest {
             public OtherBean(String name) {
                 super(name);
             }
-            public OtherBean() {}
+
+            public OtherBean() {
+            }
         }
         final Action<OtherBean> action = context.mock(Action.class);
         Bean bean1 = new Bean("b1");
@@ -208,7 +215,7 @@ public class DefaultNamedDomainObjectSetTest {
         container.add(bean1);
         container.add(bean2);
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(action).execute(bean2);
         }});
 
@@ -221,7 +228,9 @@ public class DefaultNamedDomainObjectSetTest {
             public OtherBean(String name) {
                 super(name);
             }
-            public OtherBean() {}
+
+            public OtherBean() {
+            }
         }
         final TestClosure closure = context.mock(TestClosure.class);
         Bean bean1 = new Bean("b1");
@@ -230,7 +239,7 @@ public class DefaultNamedDomainObjectSetTest {
         container.add(bean1);
         container.add(bean2);
 
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(closure).call(bean2);
         }});
 
@@ -662,7 +671,7 @@ public class DefaultNamedDomainObjectSetTest {
 
         Bean withType = new Bean("withType");
         container.add(withType);
-        
+
         // Try with an element with the same name as a method
         ConfigureUtil.configure(toClosure("{ withType.beanProperty = 'value 6' }"), container);
         assertThat(withType.getBeanProperty(), equalTo("value 6"));
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
index cc0f716..721f56e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerBaseTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultPolymorphicDomainObjectContainerBaseTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.api.internal
 
 import org.gradle.internal.reflect.Instantiator
+import org.gradle.model.internal.core.NamedEntityInstantiator
 
 class DefaultPolymorphicDomainObjectContainerBaseTest extends AbstractNamedDomainObjectContainerTest {
     def setup() {
@@ -29,6 +30,11 @@ class PolymorphicTestContainer extends AbstractPolymorphicDomainObjectContainer<
     }
 
     @Override
+    NamedEntityInstantiator<TestObject> getEntityInstantiator() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
     protected TestObject doCreate(String name) {
         def testObject = new TestObject(instantiator)
         testObject.name = name
@@ -39,4 +45,9 @@ class PolymorphicTestContainer extends AbstractPolymorphicDomainObjectContainer<
     protected <U extends TestObject> U doCreate(String name, Class<U> type) {
         throw new UnsupportedOperationException("doCreate")
     }
+
+    @Override
+    Set<? extends Class<? extends TestObject>> getCreateableTypes() {
+        Collections.singleton(TestObject)
+    }
 }
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 cc42e6f..7c8025d 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
@@ -15,7 +15,9 @@
  */
 package org.gradle.api.internal
 
+import com.google.common.collect.Sets
 import org.gradle.api.Action
+import org.gradle.api.GradleException
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Named
 import org.gradle.api.NamedDomainObjectFactory
@@ -29,7 +31,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
     def agedFred = new DefaultAgeAwarePerson(name: "fred", age: 42)
     def agedBarney = new DefaultAgeAwarePerson(name: "barney", age: 42)
 
-    def container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, new DirectInstantiator())
+    def container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, DirectInstantiator.INSTANCE)
 
     interface Person extends Named {}
 
@@ -97,6 +99,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
         container.findByName("barney") == barney
         container.asDynamicObject.getProperty("fred") == fred
         container.asDynamicObject.getProperty("barney") == barney
+        container.createableTypes == Collections.singleton(Person)
     }
 
     def "maybe create elements without specifying type"() {
@@ -113,7 +116,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
     }
 
     def "throws meaningful exception if it doesn't support creating domain objects without specifying a type"() {
-        container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, new DirectInstantiator())
+        container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, DirectInstantiator.INSTANCE)
 
         when:
         container.create("fred")
@@ -157,7 +160,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
     }
 
     def "create elements with specified type based on type binding"() {
-        container = new DefaultPolymorphicDomainObjectContainer<?>(Object, new DirectInstantiator(),
+        container = new DefaultPolymorphicDomainObjectContainer<?>(Object, DirectInstantiator.INSTANCE,
                 { it instanceof Named ? it.name : "unknown" } as Named.Namer)
 
         container.registerBinding(UnnamedPerson, DefaultUnnamedPerson)
@@ -177,6 +180,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
             it.getClass() == DefaultCtorNamedPerson
             name == "barney"
         }
+        container.createableTypes == Sets.newHashSet(UnnamedPerson, CtorNamedPerson)
     }
 
     def "maybe create elements with specified type"() {
@@ -190,6 +194,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
         container.size() == 1
         container.findByName("fred") == fred
         first == second
+        container.createableTypes == Sets.newHashSet(Person)
     }
 
     def "throws meaningful exception if element with same name exists with incompatible type"() {
@@ -205,7 +210,7 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
     }
 
     def "throws meaningful exception if it doesn't support creating domain objects with the specified type"() {
-        container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, new DirectInstantiator())
+        container = new DefaultPolymorphicDomainObjectContainer<Person>(Person, DirectInstantiator.INSTANCE)
 
         when:
         container.create("fred", Person)
@@ -247,4 +252,15 @@ class DefaultPolymorphicDomainObjectContainerTest extends Specification {
         expect:
         container.findAll { it != fred } == [barney] as Set
     }
+
+    def "cannot register factory for already registered type"() {
+        given:
+        container.registerFactory(Person, { new DefaultPerson(name: it) })
+        when:
+        container.registerFactory(Person, { new DefaultPerson(name: it) })
+
+        then:
+        def e = thrown(GradleException)
+        e.message == "Cannot register a factory for type Person because a factory for this type is already registered."
+    }
 }
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 bb57cf7..cdd7523 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
@@ -23,7 +23,7 @@ import org.gradle.api.tasks.TaskDependency
 import org.gradle.api.tasks.TaskExecutionException
 import org.gradle.api.tasks.TaskInstantiationException
 import org.gradle.internal.Actions
-import org.gradle.listener.ListenerManager
+import org.gradle.internal.event.ListenerManager
 import org.gradle.util.WrapUtil
 import org.jmock.Expectations
 import org.junit.After
@@ -85,8 +85,8 @@ class DefaultTaskTest extends AbstractTaskTest {
     public void testDependsOn() {
         Task dependsOnTask = createTask(project, "somename");
         Task task = createTask(project, TEST_TASK_NAME);
-        project.getTasks().add("path1");
-        project.getTasks().add("path2");
+        project.getTasks().create("path1");
+        project.getTasks().create("path2");
 
         task.dependsOn(Project.PATH_SEPARATOR + "path1");
         assertThat(task, dependsOn("path1"));
@@ -420,7 +420,7 @@ class DefaultTaskTest extends AbstractTaskTest {
     }
 
     @Test
-    @Issue("http://issues.gradle.org/browse/GRADLE-2022")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2022")
     public void testGoodErrorMessageWhenTaskInstantiatedDirectly() {
         try {
             new DefaultTask();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyClassPathProviderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyClassPathProviderTest.groovy
index 815ba2b..1dc69a3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyClassPathProviderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyClassPathProviderTest.groovy
@@ -31,11 +31,12 @@ class DependencyClassPathProviderTest extends Specification {
         def classpath = provider.findClassPath("GRADLE_API")
 
         then:
-        classpath.asFiles.collect{it.name} == ["gradle-core-runtime", "gradle-cli-runtime", "gradle-core-impl-runtime", "gradle-tooling-api-impl", "plugin1-runtime", "plugin2-runtime"]
+        classpath.asFiles.collect{it.name} == ["gradle-core-runtime", "gradle-cli-runtime", "gradle-dependency-management-runtime", "gradle-plugin-use-runtime", "gradle-tooling-api-runtime", "plugin1-runtime", "plugin2-runtime"]
 
         and:
         1 * moduleRegistry.getModule("gradle-core") >> module("gradle-core", module("gradle-cli"))
-        1 * moduleRegistry.getModule("gradle-core-impl") >> module("gradle-core-impl")
+        1 * moduleRegistry.getModule("gradle-dependency-management") >> module("gradle-dependency-management")
+        1 * moduleRegistry.getModule("gradle-plugin-use") >> module("gradle-plugin-use")
         1 * moduleRegistry.getModule("gradle-tooling-api") >> module("gradle-tooling-api")
         1 * pluginModuleRegistry.getPluginModules() >> ([module("plugin1"), module("plugin2")] as LinkedHashSet)
     }
@@ -43,7 +44,6 @@ class DependencyClassPathProviderTest extends Specification {
     def module(String name, Module ... requiredModules) {
         Module module = Mock()
         _ * module.classpath >> new DefaultClassPath(new File("$name-runtime"))
-        _ * module.implementationClasspath >> new DefaultClassPath(new File("$name-impl"))
         _ * module.allRequiredModules >> (([module] + (requiredModules as List)) as LinkedHashSet)
         return module
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyInjectingInstantiatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyInjectingInstantiatorTest.groovy
index 8c6080d..4e4f8a0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyInjectingInstantiatorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DependencyInjectingInstantiatorTest.groovy
@@ -16,25 +16,23 @@
 
 package org.gradle.api.internal
 
-import spock.lang.Specification
-import org.gradle.internal.service.ServiceRegistry
-import javax.inject.Inject
-import org.gradle.api.Action
 import org.gradle.internal.reflect.ObjectInstantiationException
+import org.gradle.internal.service.ServiceRegistry
 import org.gradle.internal.service.UnknownServiceException
+import spock.lang.Specification
+
+import javax.inject.Inject
 
 class DependencyInjectingInstantiatorTest extends Specification {
     final ServiceRegistry services = Mock()
-    final Action warning = Mock()
-    final DependencyInjectingInstantiator instantiator = new DependencyInjectingInstantiator(services, warning)
+    final DependencyInjectingInstantiator instantiator = new DependencyInjectingInstantiator(services)
 
     def "creates instance that has default constructor"() {
         when:
         def result = instantiator.newInstance(HasDefaultConstructor)
 
         then:
-        result != null
-        0 * warning._
+        result instanceof HasDefaultConstructor
     }
 
     def "injects provided parameters into constructor"() {
@@ -44,7 +42,6 @@ class DependencyInjectingInstantiatorTest extends Specification {
         then:
         result.param1 == "string"
         result.param2 == 12
-        0 * warning._
     }
 
     def "injects missing parameters from provided service registry"() {
@@ -57,7 +54,6 @@ class DependencyInjectingInstantiatorTest extends Specification {
         then:
         result.param1 == "string"
         result.param2 == 12
-        0 * warning._
     }
 
     def "unboxes primitive types"() {
@@ -67,7 +63,6 @@ class DependencyInjectingInstantiatorTest extends Specification {
         then:
         result.param1 == 12
         result.param2
-        0 * warning._
     }
 
     def "constructors do not need to be public"() {
@@ -75,73 +70,17 @@ class DependencyInjectingInstantiatorTest extends Specification {
         instantiator.newInstance(HasPrivateConstructor, "param") != null
     }
 
-    def "prefers exact match constructor when class has multiple constructors and none are annotated"() {
-        when:
-        def result = instantiator.newInstance(HasNoInjectConstructor, "param")
-
-        then:
-        result != null
-        1 * warning.execute("Class $HasNoInjectConstructor.name has multiple constructors and no constructor is annotated with @Inject. In Gradle 2.0 this will be treated as an error.")
-        0 * warning._
-    }
-
-    def "prefers exact match constructor when class has multiple constructors and another is annotated"() {
-        when:
-        def result = instantiator.newInstance(HasOneInjectConstructor, "param")
-
-        then:
-        result != null
-        1 * warning.execute("Class $HasOneInjectConstructor.name has @Inject annotation on an unexpected constructor. In Gradle 2.0 the constructor annotated with @Inject will be used instead of the current default constructor.")
-        0 * warning._
-    }
-
-    def "prefers exact match constructor when class has multiple annotated constructors"() {
-        when:
-        def result = instantiator.newInstance(HasMultipleInjectConstructors, "param")
-
-        then:
-        result != null
-        1 * warning.execute("Class $HasMultipleInjectConstructors.name has multiple constructors with @Inject annotation. In Gradle 2.0 this will be treated as an error.")
-        0 * warning._
-    }
-
-    def "warns when class has exactly one constructor that takes parameters parameters and is not annotated"() {
-        when:
-        def result = instantiator.newInstance(HasNonAnnotatedConstructor, "param")
-
-        then:
-        result != null
-        1 * warning.execute("Constructor for class $HasNonAnnotatedConstructor.name is not annotated with @Inject. In Gradle 2.0 this will be treated as an error.")
-        0 * warning._
-
-        when:
-        result = instantiator.newInstance(HasNonAnnotatedConstructor)
-
-        then:
-        result != null
-        1 * warning.execute("Constructor for class $HasNonAnnotatedConstructor.name is not annotated with @Inject. In Gradle 2.0 this will be treated as an error.")
-        0 * warning._
+    def "class can be package scoped"() {
+        expect:
+        instantiator.newInstance(PackageScopedClass) != null
     }
 
-    def "does not warn when class has multiple constructors and exact match constructor is annotated"() {
+    def "selects annotated constructor when class has multiple constructors and only one is annotated"() {
         when:
         def result = instantiator.newInstance(HasOneInjectConstructor, 12)
 
         then:
         result != null
-        0 * warning._
-    }
-
-    def "does not warn when class has multiple constructors and there is no exact match and one constructor is annotated"() {
-        given:
-        _ * services.get(Number) >> 12
-
-        when:
-        def result = instantiator.newInstance(HasOneInjectConstructor)
-
-        then:
-        result != null
-        0 * warning._
     }
 
     def "propagates constructor failure"() {
@@ -180,43 +119,48 @@ class DependencyInjectingInstantiatorTest extends Specification {
         _ * services.get(String) >> { throw failure }
 
         when:
-        instantiator.newInstance(HasNonAnnotatedConstructor)
+        instantiator.newInstance(HasInjectConstructor, 12)
 
         then:
         ObjectInstantiationException e = thrown()
         e.cause == failure
     }
 
-    def "fails when class has multiple matching constructors"() {
+    def "fails when class has multiple constructors and none are annotated"() {
         when:
-        instantiator.newInstance(HasMultipleCompatibleConstructor, "param")
+        instantiator.newInstance(HasNoInjectConstructor, new StringBuilder("param"))
 
         then:
         ObjectInstantiationException e = thrown()
-        e.cause.message == "Class $HasMultipleCompatibleConstructor.name has multiple constructors that accept parameters [param]."
+        e.cause.message == "Class $HasNoInjectConstructor.name has no constructor that is annotated with @Inject."
     }
 
-    def "fails when class has no matching constructors and none are annotated"() {
+    def "fails when class has multiple constructor that are annotated"() {
         when:
-        instantiator.newInstance(HasNoInjectConstructor, new StringBuilder("param"))
+        instantiator.newInstance(HasMultipleInjectConstructors, new StringBuilder("param"))
 
         then:
         ObjectInstantiationException e = thrown()
-        e.cause.message == "Class $HasNoInjectConstructor.name has no constructor that accepts parameters [param] or that is annotated with @Inject."
+        e.cause.message == "Class $HasMultipleInjectConstructors.name has multiple constructors that are annotated with @Inject."
     }
 
-    def "fails when class has no matching constructor and multiple are annotated"() {
+    def "fails when class has non-public zero args constructor that is not annotated"() {
         when:
-        instantiator.newInstance(HasMultipleInjectConstructors, new StringBuilder("param"))
+        instantiator.newInstance(HasNonPublicNoArgsConstructor, new StringBuilder("param"))
 
         then:
         ObjectInstantiationException e = thrown()
-        e.cause.message == "Class $HasMultipleInjectConstructors.name has multiple constructors with @Inject annotation."
+        e.cause.message == "Class $HasNonPublicNoArgsConstructor.name has no constructor that is annotated with @Inject."
     }
 
     public static class HasDefaultConstructor {
     }
 
+    public static class HasNonPublicNoArgsConstructor {
+        protected HasNonPublicNoArgsConstructor() {
+        }
+    }
+
     public static class HasBrokenConstructor {
         static def failure = new RuntimeException()
 
@@ -256,11 +200,6 @@ class DependencyInjectingInstantiatorTest extends Specification {
         }
     }
 
-    public static class HasNonAnnotatedConstructor {
-        HasNonAnnotatedConstructor(String param1) {
-        }
-    }
-
     public static class HasNoInjectConstructor {
         HasNoInjectConstructor(String param1) {
         }
@@ -274,14 +213,6 @@ class DependencyInjectingInstantiatorTest extends Specification {
         }
     }
 
-    public static class HasMultipleCompatibleConstructor {
-        HasMultipleCompatibleConstructor(String param1) {
-        }
-
-        HasMultipleCompatibleConstructor(Object param1) {
-        }
-    }
-
     public static class HasPrivateConstructor {
         @Inject
         private HasPrivateConstructor(String param1) {
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 bb69e62..9c45b08 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
@@ -30,4 +30,14 @@ class DocumentationRegistryTest extends Specification {
         expect:
         registry.getDocumentationFor('gradle_daemon') == "http://gradle.org/docs/${gradleVersion.version}/userguide/gradle_daemon.html"
     }
+
+    def "points users at the gradle docs web site with section"() {
+        expect:
+        registry.getDocumentationFor('gradle_daemon', 'reusing_daemons') == "http://gradle.org/docs/${gradleVersion.version}/userguide/gradle_daemon.html#reusing_daemons"
+    }
+
+    def "points users at the gradle dsl web site"() {
+        expect:
+        registry.getDslRefForProperty(org.gradle.api.Project, 'name') == "http://gradle.org/docs/${gradleVersion.version}/dsl/org.gradle.api.Project.html#org.gradle.api.Project:name"
+    }
 }
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 85a7135..848c550 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
@@ -163,8 +163,6 @@ public class ExtensibleDynamicObjectTest {
         assertThat(bean.getProperty("readWriteProperty"), equalTo((Object) "value"));
         bean.setProperty("groovyProperty", "value");
         assertThat(bean.getProperty("groovyProperty"), equalTo((Object) "value"));
-        bean.setProperty("additional", "value");
-        assertThat(bean.getProperty("additional"), equalTo((Object) "value"));
     }
 
     @Test
@@ -204,7 +202,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void hasPropertyDefinedByParent() {
         Bean parent = new Bean();
-        parent.setProperty("parentProperty", "value");
+        parent.defineProperty("parentProperty", "value");
 
         Bean bean = new Bean();
         assertFalse(bean.hasProperty("parentProperty"));
@@ -216,7 +214,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void canGetPropertyDefinedByParent() {
         Bean parent = new Bean();
-        parent.setProperty("parentProperty", "value");
+        parent.defineProperty("parentProperty", "value");
 
         Bean bean = new Bean();
         bean.setParent(parent.getAsDynamicObject());
@@ -225,12 +223,12 @@ public class ExtensibleDynamicObjectTest {
     }
 
     @Test
-    public void cannotSetPropertyDefinedByParent() {
+    public void extraPropertyIsNotVisibleToParent() {
         Bean parent = new Bean();
 
         Bean bean = new Bean();
         bean.setParent(parent.getAsDynamicObject());
-        bean.setProperty("parentProperty", "value");
+        bean.defineProperty("parentProperty", "value");
 
         assertFalse(parent.hasProperty("parentProperty"));
     }
@@ -241,7 +239,7 @@ public class ExtensibleDynamicObjectTest {
 
         assertFalse(bean.hasProperty("additional"));
 
-        bean.setProperty("additional", "value");
+        bean.defineProperty("additional", "value");
         assertTrue(bean.hasProperty("additional"));
 
         bean.setProperty("additional", null);
@@ -249,17 +247,20 @@ public class ExtensibleDynamicObjectTest {
     }
 
     @Test
-    public void canGetAndSetAdditionalProperty() {
+    public void canGetAndSetExtraProperty() {
         Bean bean = new Bean();
 
-        bean.setProperty("additional", "value");
-        assertThat(bean.getProperty("additional"), equalTo((Object) "value"));
+        bean.defineProperty("additional", "value 1");
+        assertThat(bean.getProperty("additional"), equalTo((Object) "value 1"));
+
+        bean.setProperty("additional", "value 2");
+        assertThat(bean.getProperty("additional"), equalTo((Object) "value 2"));
     }
 
     @Test
     public void canGetAndSetPropertyDefinedByAdditionalObject() {
         Bean otherObject = new Bean();
-        otherObject.setProperty("otherObject", "value");
+        otherObject.defineProperty("otherObject", "value");
 
         Bean bean = new Bean();
         bean.extensibleDynamicObject.addObject(otherObject.getAsDynamicObject(), ExtensibleDynamicObject.Location.BeforeConvention);
@@ -287,9 +288,9 @@ public class ExtensibleDynamicObjectTest {
     }
 
     @Test
-    public void additionalPropertyTakesPrecedenceOverConventionProperty() {
+    public void extraPropertyTakesPrecedenceOverConventionProperty() {
         Bean bean = new Bean();
-        bean.setProperty("conventionProperty", "value");
+        bean.defineProperty("conventionProperty", "value");
 
         Convention convention = bean.extensibleDynamicObject.getConvention();
         ConventionBean conventionBean = new ConventionBean();
@@ -307,7 +308,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void conventionPropertyTakesPrecedenceOverParentProperty() {
         Bean parent = new Bean();
-        parent.setProperty("conventionProperty", "parent");
+        parent.defineProperty("conventionProperty", "parent");
 
         Bean bean = new Bean();
         bean.setParent(parent.getAsDynamicObject());
@@ -323,14 +324,14 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void canGetAllProperties() {
         Bean parent = new Bean();
-        parent.setProperty("parentProperty", "parentProperty");
+        parent.defineProperty("parentProperty", "parentProperty");
         parent.setReadWriteProperty("ignore me");
         parent.doSetReadOnlyProperty("ignore me");
         Convention parentConvention = parent.extensibleDynamicObject.getConvention();
         parentConvention.getPlugins().put("parent", new ConventionBean());
 
         GroovyBean bean = new GroovyBean();
-        bean.setProperty("additional", "additional");
+        bean.defineProperty("additional", "additional");
         bean.setReadWriteProperty("readWriteProperty");
         bean.doSetReadOnlyProperty("readOnlyProperty");
         bean.setGroovyProperty("groovyProperty");
@@ -385,9 +386,9 @@ public class ExtensibleDynamicObjectTest {
     }
 
     @Test
-    public void additionalPropertyWithNullValueIsNotTreatedAsUnknown() {
+    public void extraPropertyWithNullValueIsNotTreatedAsUnknown() {
         Bean bean = new Bean();
-        bean.setProperty("additional", null);
+        bean.defineProperty("additional", null);
         assertThat(bean.getProperty("additional"), nullValue());
     }
 
@@ -484,7 +485,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void canInvokeClosurePropertyAsAMethod() {
         Bean bean = new Bean();
-        bean.setProperty("someMethod", TestUtil.toClosure("{ param -> param.toLowerCase() }"));
+        bean.defineProperty("someMethod", TestUtil.toClosure("{ param -> param.toLowerCase() }"));
         assertThat(bean.invokeMethod("someMethod", "Param"), equalTo((Object) "param"));
     }
 
@@ -551,9 +552,9 @@ public class ExtensibleDynamicObjectTest {
     }
 
     @Test
-    public void additionalPropertiesAreInherited() {
+    public void extraPropertiesAreInherited() {
         Bean bean = new Bean();
-        bean.setProperty("additional", "value");
+        bean.defineProperty("additional", "value");
 
         DynamicObject inherited = bean.getInheritable();
         assertTrue(inherited.hasProperty("additional"));
@@ -568,7 +569,7 @@ public class ExtensibleDynamicObjectTest {
         DynamicObject inherited = bean.getInheritable();
         assertFalse(inherited.hasProperty("additional"));
 
-        bean.setProperty("additional", "value");
+        bean.defineProperty("additional", "value");
         assertTrue(inherited.hasProperty("additional"));
         assertThat(inherited.getProperty("additional"), equalTo((Object) "value"));
     }
@@ -576,7 +577,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void additionalObjectPropertiesAreInherited() {
         Bean other = new Bean();
-        other.setProperty("other", "value");
+        other.defineProperty("other", "value");
         Bean bean = new Bean();
         bean.extensibleDynamicObject.addObject(other.getAsDynamicObject(), ExtensibleDynamicObject.Location.BeforeConvention);
 
@@ -589,7 +590,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void inheritedAdditionalObjectPropertiesTrackChanges() {
         Bean other = new Bean();
-        other.setProperty("other", "value");
+        other.defineProperty("other", "value");
         Bean bean = new Bean();
 
         DynamicObject inherited = bean.getInheritable();
@@ -634,7 +635,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void parentPropertiesAreInherited() {
         Bean parent = new Bean();
-        parent.setProperty("parentProperty", "value");
+        parent.defineProperty("parentProperty", "value");
         Bean bean = new Bean();
         bean.setParent(parent.getAsDynamicObject());
 
@@ -657,7 +658,7 @@ public class ExtensibleDynamicObjectTest {
     @Test
     public void cannotSetInheritedProperties() {
         Bean bean = new Bean();
-        bean.setProperty("additional", "value");
+        bean.defineProperty("additional", "value");
 
         DynamicObject inherited = bean.getInheritable();
         try {
@@ -848,6 +849,10 @@ public class ExtensibleDynamicObjectTest {
         public DynamicObject getInheritable() {
             return extensibleDynamicObject.getInheritable();
         }
+
+        public void defineProperty(String name, Object value) {
+            extensibleDynamicObject.getConvention().getExtraProperties().set(name, value);
+        }
     }
 
     private static class DynamicJavaBean extends GroovyBean {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTestHelper.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTestHelper.groovy
index e2da665..18774ac 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTestHelper.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/ExtensibleDynamicObjectTestHelper.groovy
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue
 public class ExtensibleDynamicObjectTestHelper {
     public static void assertCanGetAllProperties (ExtensibleDynamicObjectTest.Bean bean) {
         bean.readWriteProperty = 'readWrite'
-        bean.setProperty('additional', 'additional')
+        bean.defineProperty('additional', 'additional')
         assertEquals(bean.getProperties().readWriteProperty, 'readWrite')
         assertEquals(bean.getProperties().additional, 'additional')
     }
@@ -33,11 +33,8 @@ public class ExtensibleDynamicObjectTestHelper {
         bean.doSetReadOnlyProperty('value')
         assertEquals(bean.readOnlyProperty, 'value')
 
-        bean.extensibleDynamicObject.dynamicProperties.set('additional', 'value')
+        bean.defineProperty('additional', 'value')
         assertEquals(bean.additional, 'value')
-
-        bean.setProperty 'another', 'value'
-        assertEquals(bean.another, 'value')
     }
     
     public static void assertCanGetAndSetProperties (ExtensibleDynamicObjectTest.Bean bean) {
@@ -47,11 +44,8 @@ public class ExtensibleDynamicObjectTestHelper {
         bean.doSetReadOnlyProperty('value')
         assertEquals(bean.readOnlyProperty, 'value')
 
-        bean.additional = 'value'
+        bean.ext.additional = 'value'
         assertEquals(bean.additional, 'value')
-
-        bean.setProperty 'another', 'value'
-        assertEquals(bean.another, 'value')
     }
 
     public static void assertCanCallMethods (ExtensibleDynamicObjectTest.Bean bean) {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/NestedConfigureAutoCreateNamedDomainObjectContainerSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/NestedConfigureAutoCreateNamedDomainObjectContainerSpec.groovy
index 421ff58..73fdc9f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/NestedConfigureAutoCreateNamedDomainObjectContainerSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/NestedConfigureAutoCreateNamedDomainObjectContainerSpec.groovy
@@ -20,13 +20,13 @@ import org.gradle.internal.reflect.DirectInstantiator
 
 class NestedConfigureAutoCreateNamedDomainObjectContainerSpec extends Specification {
 
-    def instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    def instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
 
     static class Container extends FactoryNamedDomainObjectContainer {
         String parentName
         String name
         Container(String parentName, String name, Closure factory) {
-            super(Object, new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator()), new DynamicPropertyNamer(), factory)
+            super(Object, new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE), new DynamicPropertyNamer(), factory)
             this.parentName = parentName
             this.name = name
         }
@@ -103,7 +103,7 @@ class NestedConfigureAutoCreateNamedDomainObjectContainerSpec extends Specificat
         e.method == "somethingThatDoesntExist"
         parent.c1.m1.prop == "c1c1m1"
         
-        // make sure the somethingThatDoesntExist() call didn't resolve against any of the parent containers, creating an entry
+        // make sure the somethingThatDoesntExist() call didn't resolve against any of the root containers, creating an entry
         parent.size() == 1
         parent.c1.size() == 1
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/PackageScopedClass.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/PackageScopedClass.java
new file mode 100644
index 0000000..315f847
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/PackageScopedClass.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal;
+
+class PackageScopedClass {
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/TestJavaObject.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/TestJavaObject.java
new file mode 100644
index 0000000..4c3d93b
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/TestJavaObject.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal;
+
+public class TestJavaObject {
+    public String getSomeValue() {
+        return "value";
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapperTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapperTest.groovy
new file mode 100644
index 0000000..a0a03d7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/TypedDomainObjectContainerWrapperTest.groovy
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal
+
+import org.gradle.api.Action
+import org.gradle.api.Named
+import org.gradle.internal.reflect.DirectInstantiator
+import spock.lang.Specification
+
+class TypedDomainObjectContainerWrapperTest extends Specification {
+
+    DefaultPolymorphicDomainObjectContainer<Type> parent = new DefaultPolymorphicDomainObjectContainer<Type>(Type, DirectInstantiator.INSTANCE)
+
+    def setup() {
+        parent.add(type("typeOne"))
+        parent.add(type("typeTwo"))
+        parent.add(subtype("subTypeOne"))
+        parent.add(subtype("subTypeTwo"))
+        parent.add(otherSubtype("otherSubType"))
+
+        parent.registerFactory(CreatedSubType, { String name ->
+            return new DefaultCreatedSubType(name)
+        })
+    }
+
+    def "test returns subtype elements"() {
+        when:
+        def container = parent.containerWithType(SubType)
+
+        then:
+        containerHas container, "subTypeOne", "subTypeTwo"
+    }
+
+    def "returns all elements when filtered by parent type"() {
+        when:
+        def container = parent.containerWithType(Type)
+
+        then:
+        containerHas container, "otherSubType", "subTypeOne", "subTypeTwo", "typeOne", "typeTwo"
+    }
+
+    def "created elements are typed"() {
+        when:
+        def container = parent.containerWithType(CreatedSubType)
+        def action = { CreatedSubType subType ->
+            subType.value = "actioned"
+        } as Action<CreatedSubType>
+
+        and:
+        container.create("created1")
+        container.create("created2", {
+            it.value = "changed"
+        })
+        container.create("created3", action)
+
+        then:
+        containerHas container, "created1", "created2", "created3"
+        parent.collect({it.name}).containsAll(["created1", "created2", "created3"])
+
+        and:
+        container.getByName("created1").value == "original"
+        container.getByName("created2").value == "changed"
+        container.getByName("created3").value == "actioned"
+    }
+
+    def "can create and configure via DSL"() {
+        when:
+        def container = parent.containerWithType(CreatedSubType)
+
+        and:
+        container.configure {
+            createdOne
+            createdTwo {
+                value = "changed"
+            }
+        }
+
+        then:
+        containerHas container, "createdOne", "createdTwo"
+        parent.collect({it.name}).containsAll(["createdOne", "createdTwo"])
+
+        and:
+        container.getByName("createdOne").value == "original"
+        container.getByName("createdTwo").value == "changed"
+    }
+
+    def "can configure existing via DSL"() {
+        given:
+        def container = parent.containerWithType(CreatedSubType)
+        def created = container.create("createdOne")
+        assert container.asList() == [created]
+
+        when:
+        container.configure {
+            createdOne {
+                value = "changed"
+            }
+        }
+
+        then:
+        container.asList() == [created]
+        created.value == "changed"
+    }
+
+    def containerHas(def container, String... names) {
+        assert container.toList().collect {it.name} == names as List
+        true
+    }
+
+    def Type type(def name) {
+        Stub(Type) {
+            getName() >> name
+        }
+    }
+
+    def SubType subtype(def name) {
+        Stub(SubType) {
+            getName() >> name
+        }
+    }
+
+    def OtherSubType otherSubtype(def name) {
+        Stub(OtherSubType) {
+            getName() >> name
+        }
+    }
+    interface Type extends Named {}
+    interface SubType extends Type {}
+    interface OtherSubType extends Type {}
+    interface CreatedSubType extends Type {
+        String getValue()
+        void setValue(String value)
+    }
+
+    class DefaultCreatedSubType implements CreatedSubType {
+        final String name
+        String value = "original";
+
+        DefaultCreatedSubType(String name) {
+            this.name = name
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainerTest.groovy
index 2723ceb..9707898 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactRepositoryContainerTest.groovy
@@ -16,145 +16,23 @@
 
 package org.gradle.api.internal.artifacts
 
-import org.apache.ivy.plugins.resolver.DependencyResolver
 import org.gradle.api.Action
 import org.gradle.api.artifacts.ArtifactRepositoryContainer
 import org.gradle.api.artifacts.UnknownRepositoryException
 import org.gradle.api.artifacts.repositories.ArtifactRepository
-import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
 import spock.lang.Specification
 
 class DefaultArtifactRepositoryContainerTest extends Specification {
-
-    BaseRepositoryFactory repositoryFactory
     DefaultArtifactRepositoryContainer container
 
     def setup() {
-        repositoryFactory = Mock(BaseRepositoryFactory)
         container = createResolverContainer()
     }
 
-    ArtifactRepositoryContainer createResolverContainer(
-            BaseRepositoryFactory repositoryFactory = repositoryFactory,
-            Instantiator instantiator = new DirectInstantiator()
-    ) {
-        new DefaultArtifactRepositoryContainer(repositoryFactory, instantiator)
-    }
-
-    List setupNotation(int i, repositoryFactory = repositoryFactory) {
-        setupNotation("repoNotation$i", i == 1 ? "repository" : "repository${i-1}", "resolver$i", repositoryFactory)
-    }
-
-    List setupNotation(notation, repoName, resolverName, repositoryFactory = repositoryFactory) {
-        def repo = Mock(ArtifactRepositoryInternal) { getName() >> repoName }
-        def resolver = Mock(DependencyResolver)
-        def resolverRepo = Mock(ArtifactRepositoryInternal)
-
-        interaction {
-            1 * repositoryFactory.createRepository(notation) >> repo
-            1 * repositoryFactory.toResolver(repo) >> resolver
-            1 * repositoryFactory.createResolverBackedRepository(resolver) >> resolverRepo
-            1 * resolverRepo.setName(repoName)
-            _ * resolverRepo.getName() >> repoName
-            1 * resolverRepo.onAddToContainer(container)
-        }
-
-        [notation, repo, resolver, resolverRepo]
-    }
-
-    def "can add resolver"() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-
-        expect:
-        container.addLast(repo1Notation).is resolver1
-        assert container.findByName("repository") != null
-        container.addLast(repo2Notation)
-        container == [resolverRepo1, resolverRepo2]
-    }
-
-    def "can add repositories with duplicate names"() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-
-        when:
-        container.addLast(repo1Notation)
-        container.addLast(repo2Notation)
-
-        then:
-        container*.name == ["repository", "repository1"]
-    }
-
-    def testAddResolverWithClosure() {
-        given:
-        def repo = Mock(ArtifactRepositoryInternal) { getName() >> "name" }
-        def resolver = Mock(DependencyResolver)
-        def resolverRepo = Mock(ArtifactRepositoryInternal)
-
-        interaction {
-            1 * repositoryFactory.createRepository(resolver) >> repo
-            1 * repositoryFactory.toResolver(repo) >> resolver
-            1 * repositoryFactory.createResolverBackedRepository(resolver) >> resolverRepo
-            _ * resolverRepo.name >> "bar"
-            1 * resolverRepo.onAddToContainer(container)
-        }
-
-        when:
-        container.add(resolver) {
-            name = "bar"
-        }
-
-        then:
-        1 * resolver.setName("bar")
-    }
-
-    def testAddBefore() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-
-        when:
-        container.addLast(repo1Notation)
-        container.addBefore(repo2Notation, "repository")
-
-        then:
-        container == [resolverRepo2, resolverRepo1]
-    }
-
-    def testAddAfter() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-        def (repo3Notation, repo3, resolver3, resolverRepo3) = setupNotation(3)
-
-        when:
-        container.addLast(repo1Notation)
-        container.addAfter(repo2Notation, "repository")
-        container.addAfter(repo3Notation, "repository")
-
-        then:
-        container == [resolverRepo1, resolverRepo3, resolverRepo2]
-    }
-
-
-    def testAddBeforeWithUnknownResolver() {
-        when:
-        container.addBefore("asdfasd", 'unknownName')
-
-        then:
-        thrown(UnknownRepositoryException)
-    }
-
-    def testAddAfterWithUnknownResolver() {
-        when:
-        container.addAfter("asdfasd", 'unknownName')
-
-        then:
-        thrown(UnknownRepositoryException)
+    ArtifactRepositoryContainer createResolverContainer(Instantiator instantiator = DirectInstantiator.INSTANCE) {
+        new DefaultArtifactRepositoryContainer(instantiator)
     }
 
     def testAddFirst() {
@@ -186,32 +64,6 @@ class DefaultArtifactRepositoryContainerTest extends Specification {
         container == [repo1, repo2]
     }
 
-    def testAddFirstUsingUserDescription() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-
-        when:
-        container.addFirst(repo1Notation)
-        container.addFirst(repo2Notation)
-
-        then:
-        container == [resolverRepo2, resolverRepo1]
-    }
-
-    def testAddLastUsingUserDescription() {
-        given:
-        def (repo1Notation, repo1, resolver1, resolverRepo1) = setupNotation(1)
-        def (repo2Notation, repo2, resolver2, resolverRepo2) = setupNotation(2)
-
-        when:
-        container.addLast(repo1Notation)
-        container.addLast(repo2Notation)
-
-        then:
-        container == [resolverRepo1, resolverRepo2]
-    }
-
     def testGetThrowsExceptionForUnknownResolver() {
         when:
         container.getByName("unknown")
@@ -223,7 +75,7 @@ class DefaultArtifactRepositoryContainerTest extends Specification {
 
     def notificationsAreFiredWhenRepositoryIsAdded() {
         Action<ArtifactRepository> action = Mock(Action)
-        ArtifactRepository repository = Mock(ArtifactRepository)
+        ArtifactRepository repository = Mock(ArtifactRepository) { getName() >> "name" }
 
         when:
         container.all(action)
@@ -235,7 +87,7 @@ class DefaultArtifactRepositoryContainerTest extends Specification {
 
     def notificationsAreFiredWhenRepositoryIsAddedToTheHead() {
         Action<ArtifactRepository> action = Mock(Action)
-        ArtifactRepository repository = Mock(ArtifactRepository)
+        ArtifactRepository repository = Mock(ArtifactRepository) { getName() >> "name" }
 
         when:
         container.all(action)
@@ -247,7 +99,7 @@ class DefaultArtifactRepositoryContainerTest extends Specification {
 
     def notificationsAreFiredWhenRepositoryIsAddedToTheTail() {
         Action<ArtifactRepository> action = Mock(Action)
-        ArtifactRepository repository = Mock(ArtifactRepository)
+        ArtifactRepository repository = Mock(ArtifactRepository) { getName() >> "name" }
 
         when:
         container.all(action)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverterTest.groovy
new file mode 100644
index 0000000..e5aae85
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationConverterTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ExcludeRule
+import org.gradle.internal.typeconversion.NotationParserBuilder
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+
+class ExcludeRuleNotationConverterTest extends Specification {
+    def parser = NotationParserBuilder.toType(ExcludeRule).converter(new ExcludeRuleNotationConverter()).toComposite()
+
+    def "with group"() {
+        when:
+        ExcludeRule d = parser.parseNotation([group: 'aGroup']);
+        then:
+        d != null;
+        d instanceof DefaultExcludeRule
+        d.group == 'aGroup'
+        !d.module
+    }
+
+    def "with module"() {
+        when:
+        ExcludeRule d = parser.parseNotation([module: 'aModule']);
+        then:
+        d != null;
+        d instanceof DefaultExcludeRule
+        d.module == 'aModule'
+        !d.group
+    }
+
+    def "with group and module"() {
+        when:
+        ExcludeRule d = parser.parseNotation([group: 'aGroup', module: 'aModule']);
+        then:
+        d != null;
+        d instanceof DefaultExcludeRule
+        d.group == 'aGroup'
+        d.module == 'aModule'
+    }
+
+    def "with no group and no module InvalidUserDataException is thrown"() {
+        when:
+        parser.parseNotation([invalidKey1: 'aGroup', invalidKey2: 'aModule']);
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "throw exception with a valid group but invalid second key"() {
+        when:
+        parser.parseNotation([group: 'aGroup', invalidKey1:"invalidValue"]);
+        then:
+        thrown(MissingPropertyException)
+    }
+
+    def "checkValidExcludeRuleMap is true if group or module is defined"() {
+        expect:
+        parser.parseNotation(WrapUtil.toMap(ExcludeRule.GROUP_KEY, "aGroup"));
+        parser.parseNotation(WrapUtil.toMap(ExcludeRule.MODULE_KEY, "aModule"));
+
+        when:
+        parser.parseNotation(WrapUtil.toMap("unknownKey", "someValue"))
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParserTest.groovy
deleted file mode 100644
index 7014a95..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/ExcludeRuleNotationParserTest.groovy
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.artifacts
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.ExcludeRule
-import org.gradle.util.WrapUtil
-import spock.lang.Specification
-
-class ExcludeRuleNotationParserTest extends Specification {
-    def parser = new ExcludeRuleNotationParser<DefaultExcludeRule>();
-
-    def "with group"() {
-        when:
-        ExcludeRule d = parser.parseNotation([group: 'aGroup']);
-        then:
-        d != null;
-        d instanceof DefaultExcludeRule
-        d.group == 'aGroup'
-        !d.module
-    }
-
-    def "with module"() {
-        when:
-        ExcludeRule d = parser.parseNotation([module: 'aModule']);
-        then:
-        d != null;
-        d instanceof DefaultExcludeRule
-        d.module == 'aModule'
-        !d.group
-    }
-
-    def "with group and module"() {
-        when:
-        ExcludeRule d = parser.parseNotation([group: 'aGroup', module: 'aModule']);
-        then:
-        d != null;
-        d instanceof DefaultExcludeRule
-        d.group == 'aGroup'
-        d.module == 'aModule'
-    }
-
-    def "with no group and no module InvalidUserDataException is thrown"() {
-        when:
-        parser.parseNotation([invalidKey1: 'aGroup', invalidKey2: 'aModule']);
-        then:
-        thrown(InvalidUserDataException)
-    }
-
-    def "throw exception with a valid group but invalid second key"() {
-        when:
-        parser.parseNotation([group: 'aGroup', invalidKey1:"invalidValue"]);
-        then:
-        thrown(MissingPropertyException)
-    }
-
-    def "checkValidExcludeRuleMap is true if group or module is defined"() {
-        expect:
-        parser.parseNotation(WrapUtil.toMap(ExcludeRule.GROUP_KEY, "aGroup"));
-        parser.parseNotation(WrapUtil.toMap(ExcludeRule.MODULE_KEY, "aModule"));
-
-        when:
-        parser.parseNotation(WrapUtil.toMap("unknownKey", "someValue"))
-
-        then:
-        thrown(InvalidUserDataException)
-    }
-}
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 e5a1bca..ddec9dd 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
@@ -14,35 +14,49 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.artifacts.dependencies
-
+import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.DependencyArtifact
+import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.internal.artifacts.DefaultExcludeRule
-import org.gradle.util.TestUtil
 import org.gradle.util.WrapUtil
 import spock.lang.Specification
 
-class AbstractModuleDependencySpec extends Specification {
+abstract class AbstractModuleDependencySpec extends Specification {
 
     private dependency = createDependency("org.gradle", "gradle-core", "4.4-beta2")
 
-    private static createDependency(String group, String name, String version) {
-        createDependency(group, name, version, null)
+    def init() {
+        dependency = createDependency("org.gradle", "gradle-core", "4.4-beta2")
     }
 
-    private static createDependency(String group, String name, String version, String configuration) {
-        new DefaultExternalModuleDependency(group, name, version, configuration)
+    protected createDependency(String group, String name, String version) {
+        createDependency(group, name, version, null)
     }
 
+    protected abstract ExternalModuleDependency createDependency(String group, String name, String version, String configuration);
+
     void "has reasonable default values"() {
         expect:
+        dependency.group == "org.gradle"
+        dependency.name == "gradle-core"
+        dependency.version == "4.4-beta2"
         dependency.transitive
         dependency.artifacts.isEmpty()
         dependency.excludeRules.isEmpty()
         dependency.configuration == Dependency.DEFAULT_CONFIGURATION
     }
 
+    def "cannot create with null name"() {
+        when:
+        createDependency("group", null, "version")
+
+        then:
+        def e = thrown InvalidUserDataException
+        e.message == "Name must not be null!"
+    }
+
     void "can exclude dependencies"() {
         def excludeArgs1 = WrapUtil.toMap("group", "aGroup")
         def excludeArgs2 = WrapUtil.toMap("module", "aModule")
@@ -58,8 +72,8 @@ class AbstractModuleDependencySpec extends Specification {
     }
 
     void "can add artifacts"() {
-        DependencyArtifact artifact1 = createAnonymousArtifact()
-        DependencyArtifact artifact2 = createAnonymousArtifact()
+        def artifact1 = Mock(DependencyArtifact)
+        def artifact2 = Mock(DependencyArtifact)
 
         when:
         dependency.addArtifact(artifact1)
@@ -82,8 +96,12 @@ class AbstractModuleDependencySpec extends Specification {
         createDependency("group1", "name1", "version1", "depConf1") != createDependency("group1", "name1", "version1", "depConf2")
     }
 
-    private DependencyArtifact createAnonymousArtifact() {
-        return new DefaultDependencyArtifact(TestUtil.createUniqueId(), "type", "org", "classifier", "url")
+    def "creates deep copy"() {
+        when:
+        def copy = dependency.copy()
+
+        then:
+        assertDeepCopy(dependency, copy)
     }
 
     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
deleted file mode 100644
index 118ea7b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/AbstractModuleDependencyTest.java
+++ /dev/null
@@ -1,110 +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.dependencies;
-
-import org.gradle.api.artifacts.Dependency;
-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.TestUtil;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.WrapUtil;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Map;
-
-import static org.gradle.util.Matchers.isEmpty;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
- at RunWith(JMock.class)
-abstract public class AbstractModuleDependencyTest {
-    //TODO SF rework the remaining coverage of this hierarchy in the spirit of AbstractModuleDependencySpec and DefaultProjectDependencyTest
-
-    protected abstract AbstractModuleDependency getDependency();
-
-    protected abstract AbstractModuleDependency createDependency(String group, String name, String version);
-
-    protected abstract AbstractModuleDependency createDependency(String group, String name, String version, String configuration);
-
-    protected JUnit4Mockery context = new JUnit4GroovyMockery();
-
-    @Test
-    public void defaultValues() {
-        assertTrue(getDependency().isTransitive());
-        assertThat(getDependency().getArtifacts(), isEmpty());
-        assertThat(getDependency().getExcludeRules(), isEmpty());
-        assertThat(getDependency().getConfiguration(), equalTo(Dependency.DEFAULT_CONFIGURATION));
-    }
-
-    @Test
-    public void exclude() {
-        Map<String, String> excludeArgs1 = WrapUtil.toMap("group", "aGroup");
-        Map<String, String> excludeArgs2 = WrapUtil.toMap("module", "aModule");
-
-        getDependency().exclude(excludeArgs1);
-        getDependency().exclude(excludeArgs2);
-
-        assertThat(getDependency().getExcludeRules().size(), equalTo(2));
-        assertThat(getDependency().getExcludeRules(), hasItem((ExcludeRule) new DefaultExcludeRule("aGroup", null)));
-        assertThat(getDependency().getExcludeRules(), hasItem((ExcludeRule) new DefaultExcludeRule(null, "aModule")));
-    }
-
-    @Test
-    public void addArtifact() {
-        DependencyArtifact artifact1 = createAnonymousArtifact();
-        DependencyArtifact artifact2 = createAnonymousArtifact();
-
-        getDependency().addArtifact(artifact1);
-        getDependency().addArtifact(artifact2);
-
-        assertThat(getDependency().getArtifacts().size(), equalTo(2));
-        assertThat(getDependency().getArtifacts(), hasItem(artifact1));
-        assertThat(getDependency().getArtifacts(), hasItem(artifact2));
-    }
-
-    private DependencyArtifact createAnonymousArtifact() {
-        return new DefaultDependencyArtifact(TestUtil.createUniqueId(), "type", "org", "classifier", "url");
-    }
-
-    @Test
-    public void equality() {
-        assertThat(createDependency("group1", "name1", "version1"), equalTo(createDependency("group1", "name1", "version1")));
-        assertThat(createDependency("group1", "name1", "version1").hashCode(), equalTo(createDependency("group1", "name1", "version1").hashCode()));
-        assertThat(createDependency("group1", "name1", "version1"), not(equalTo(createDependency("group1", "name1", "version2"))));
-        assertThat(createDependency("group1", "name1", "version1"), not(equalTo(createDependency("group1", "name2", "version1"))));
-        assertThat(createDependency("group1", "name1", "version1"), not(equalTo(createDependency("group2", "name1", "version1"))));
-        assertThat(createDependency("group1", "name1", "version1"), not(equalTo(createDependency("group2", "name1", "version1"))));
-        assertThat(createDependency("group1", "name1", "version1", "depConf1"), not(equalTo(createDependency("group1", "name1", "version1", "depConf2"))));
-    }
-
-    protected void assertDeepCopy(ModuleDependency dependency, ModuleDependency copiedDependency) {
-        assertThat(copiedDependency.getGroup(), equalTo(dependency.getGroup()));
-        assertThat(copiedDependency.getName(), equalTo(dependency.getName()));
-        assertThat(copiedDependency.getVersion(), equalTo(dependency.getVersion()));
-        assertThat(copiedDependency.getConfiguration(), equalTo(dependency.getConfiguration()));
-        assertThat(copiedDependency.isTransitive(), equalTo(dependency.isTransitive()));
-        assertThat(copiedDependency.getArtifacts(), equalTo(dependency.getArtifacts()));
-        assertThat(copiedDependency.getArtifacts(), not(sameInstance(dependency.getArtifacts())));
-        assertThat(copiedDependency.getExcludeRules(), equalTo(dependency.getExcludeRules()));
-        assertThat(copiedDependency.getExcludeRules(), not(sameInstance(dependency.getExcludeRules())));
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy
new file mode 100644
index 0000000..25b49e8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/ClientModuleDependencySpec.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ExternalModuleDependency
+import org.gradle.api.artifacts.ModuleDependency
+
+class ClientModuleDependencySpec extends AbstractModuleDependencySpec {
+
+    protected ExternalModuleDependency createDependency(String group, String name, String version, String configuration) {
+        new DefaultClientModule(group, name, version, configuration)
+    }
+
+    def "equals but content not equal with different module dependencies"() {
+        when:
+        def dep1 = createDependency("group1", "name1", "version1", null)
+        def dep2 = createDependency("group1", "name1", "version1", null)
+        dep2.addDependency(Mock(ModuleDependency))
+
+        then:
+        dep1 == dep2
+        !dep1.contentEquals(dep2)
+        !dep2.contentEquals(dep1)
+    }
+}
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
deleted file mode 100644
index e99073c..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultClientModuleTest.java
+++ /dev/null
@@ -1,94 +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.dependencies;
-
-import org.gradle.api.InvalidUserDataException;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-
-public class DefaultClientModuleTest extends AbstractModuleDependencyTest {
-    private static final String TEST_GROUP = "org.gradle";
-    private static final String TEST_NAME = "gradle-core";
-    private static final String TEST_VERSION = "4.4-beta2";
-
-    DefaultClientModule clientModule;
-
-    protected AbstractModuleDependency getDependency() {
-        return clientModule;
-    }
-
-    protected AbstractModuleDependency createDependency(String group, String name, String version) {
-        return new DefaultClientModule(group, name, version);
-    }
-
-    protected AbstractModuleDependency createDependency(String group, String name, String version, String configuration) {
-        return new DefaultClientModule(group, name, version, configuration);
-    }
-
-    @Before
-    public void setUp() {
-        clientModule = new DefaultClientModule(TEST_GROUP, TEST_NAME, TEST_VERSION);
-    }
-
-    @Test
-    public void init() {
-        assertThat(clientModule.getGroup(), equalTo(TEST_GROUP));
-        assertThat(clientModule.getName(), equalTo(TEST_NAME));
-        assertThat(clientModule.getVersion(), equalTo(TEST_VERSION));
-        assertThat(clientModule.isForce(), equalTo(false));
-        assertThat(clientModule.isTransitive(), equalTo(true));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void initWithNullNameShouldThrowInvalidUserDataEx() {
-        new DefaultClientModule(TEST_GROUP, null, TEST_VERSION);
-    }
-
-    @Test
-    public void contentEqualsWithEqualDependencies() {
-        DefaultClientModule clientModule1 = createModule();
-        DefaultClientModule clientModule2 = createModule();
-        assertThat(clientModule1.contentEquals(clientModule2), equalTo(true));
-    }
-
-    @Test
-    public void contentEqualsWithNonEqualDependencies() {
-        DefaultClientModule clientModule1 = createModule();
-        DefaultClientModule clientModule2 = createModule();
-        clientModule2.setGroup(clientModule1.getGroup() + "delta");
-        assertThat(clientModule1.contentEquals(clientModule2), equalTo(false));
-    }
-
-    @Test
-    public void copy() {
-        DefaultClientModule clientModule = createModule();
-        DefaultClientModule copiedClientModule = (DefaultClientModule) clientModule.copy();
-        assertThat(clientModule.contentEquals(copiedClientModule), equalTo(true));
-        assertDeepCopy(clientModule, copiedClientModule);
-        assertThat(copiedClientModule.getDependencies().iterator().next(), not(sameInstance(clientModule.getDependencies().iterator().next())));
-    }
-
-    private DefaultClientModule createModule() {
-        DefaultClientModule clientModule =  new DefaultClientModule("group", "name", "version", "conf");
-        clientModule.addArtifact(new DefaultDependencyArtifact("name", "type", "ext", "classifier", "url"));
-        clientModule.addDependency(new DefaultExternalModuleDependency("org", "name", "version"));
-        return clientModule;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencySpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencySpec.groovy
new file mode 100644
index 0000000..a727f97
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencySpec.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ExternalModuleDependency
+
+class DefaultExternalModuleDependencySpec extends AbstractModuleDependencySpec {
+
+    protected ExternalModuleDependency createDependency(String group, String name, String version, String configuration) {
+        new DefaultExternalModuleDependency(group, name, version, configuration)
+    }
+}
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
deleted file mode 100644
index 1019446..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultExternalModuleDependencyTest.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.internal.artifacts.dependencies;
-
-import org.gradle.api.InvalidUserDataException;
-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.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-
- at RunWith(JMock.class)
-public class DefaultExternalModuleDependencyTest extends AbstractModuleDependencyTest {
-    private static final String TEST_GROUP = "org.gradle";
-    private static final String TEST_NAME = "gradle-core";
-    private static final String TEST_VERSION = "4.4-beta2";
-
-    private DefaultExternalModuleDependency moduleDependency;
-
-    public AbstractModuleDependency getDependency() {
-        return moduleDependency;
-    }
-
-    protected AbstractModuleDependency createDependency(String group, String name, String version) {
-        return new DefaultExternalModuleDependency(group, name, version);
-    }
-
-    protected AbstractModuleDependency createDependency(String group, String name, String version, String configuration) {
-        return new DefaultExternalModuleDependency(group, name, version, configuration);
-    }
-
-    @Before public void setUp() {
-        moduleDependency = new DefaultExternalModuleDependency(TEST_GROUP, TEST_NAME, TEST_VERSION);
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-    }
-
-    @Test
-    public void init() {
-        assertThat(moduleDependency.getGroup(), equalTo(TEST_GROUP));
-        assertThat(moduleDependency.getName(), equalTo(TEST_NAME));
-        assertThat(moduleDependency.getVersion(), equalTo(TEST_VERSION));
-        assertThat(moduleDependency.isChanging(), equalTo(false));
-        assertThat(moduleDependency.isForce(), equalTo(false));
-        assertThat(moduleDependency.isTransitive(), equalTo(true));
-        assertThat(moduleDependency.getVersion(), equalTo(TEST_VERSION));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void initWithNullNameShouldThrowInvalidUserDataEx() {
-        new DefaultExternalModuleDependency(TEST_GROUP, null, TEST_VERSION);
-    }
-    
-    @Test
-    public void contentEqualsWithEqualDependencies() {
-        DefaultExternalModuleDependency dependency1 = createModuleDependency();
-        DefaultExternalModuleDependency dependency2 = createModuleDependency();
-        assertThat(dependency1.contentEquals(dependency2), equalTo(true));
-    }
-
-    @Test
-    public void contentEqualsWithNonEqualDependencies() {
-        DefaultExternalModuleDependency dependency1 = createModuleDependency();
-        DefaultExternalModuleDependency dependency2 = createModuleDependency();
-        dependency2.setTransitive(!dependency1.isTransitive());
-        assertThat(dependency1.contentEquals(dependency2), equalTo(false));
-    }
-
-    @Test
-    public void copy() {
-        DefaultExternalModuleDependency dependency = createModuleDependency();
-        DefaultExternalModuleDependency copiedDependency = dependency.copy();
-        assertDeepCopy(dependency, copiedDependency);
-    }
-
-    private DefaultExternalModuleDependency createModuleDependency() {
-        DefaultExternalModuleDependency moduleDependency = new DefaultExternalModuleDependency("group", "name", "version", "conf");
-        moduleDependency.addArtifact(new DefaultDependencyArtifact("name", "type", "ext", "classifier", "url"));
-        return moduleDependency;
-    }
-}
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
index bc7afa0..4539ac0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerTest.groovy
@@ -16,19 +16,17 @@
 
 package org.gradle.api.internal.artifacts.dsl
 
-import org.apache.ivy.plugins.resolver.DependencyResolver
 import org.gradle.api.Action
 import org.gradle.api.artifacts.ArtifactRepositoryContainer
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository
 import org.gradle.api.internal.ThreadGlobalInstantiator
 import org.gradle.api.internal.artifacts.BaseRepositoryFactory
 import org.gradle.api.internal.artifacts.DefaultArtifactRepositoryContainerTest
-import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal
 import org.gradle.internal.reflect.Instantiator
 import org.junit.Test
 
 class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTest {
-
+    BaseRepositoryFactory repositoryFactory = Mock()
     DefaultRepositoryHandler handler
 
     def setup() {
@@ -44,7 +42,7 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
 
     def testFlatDirWithClosure() {
         given:
-        def repository = Mock(TestFlatDirectoryArtifactRepository)
+        def repository = Mock(TestFlatDirectoryArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createFlatDirRepository() >> repository
 
         expect:
@@ -53,7 +51,7 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
 
     def testFlatDirWithMap() {
         given:
-        def repository = Mock(TestFlatDirectoryArtifactRepository)
+        def repository = Mock(TestFlatDirectoryArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createFlatDirRepository() >> repository
 
         expect:
@@ -62,7 +60,7 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
 
     public void testMavenCentralWithNoArgs() {
         when:
-        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
+        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createMavenCentralRepository() >> repository
         repository.getName() >> "name"
 
@@ -72,7 +70,7 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
 
     public void testMavenCentralWithMap() {
         when:
-        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
+        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createMavenCentralRepository() >> repository
         1 * repository.setArtifactUrls(["abc"])
         repository.getName() >> "name"
@@ -91,84 +89,18 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
         handler.mavenLocal().is(repository)
     }
 
-    def testMavenRepoWithNameAndUrls() {
-        when:
-        String testUrl1 = 'http://www.gradle1.org'
-        String testUrl2 = 'http://www.gradle2.org'
-        String repoRoot = 'http://www.reporoot.org'
-        String repoName = 'mavenRepoName'
-
-        TestMavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
-        repositoryFactory.createMavenRepository() >> repository
-        1 * repository.setName(repoName)
-        1 * repository.setUrl(repoRoot)
-        1 * repository.setArtifactUrls([testUrl1, testUrl2])
-        DependencyResolver resolver = Mock()
-        1 * repositoryFactory.toResolver(repository) >> resolver
-        ArtifactRepositoryInternal fixedRepo = Mock(ArtifactRepositoryInternal)
-        1 * repositoryFactory.createResolverBackedRepository(resolver) >> fixedRepo
-        fixedRepo.getName() >> repoName
-
-        then:
-        handler.mavenRepo([name: repoName, url: repoRoot, artifactUrls: [testUrl1, testUrl2]]).is(resolver)
-        handler.size() == 1
-        handler.first() == fixedRepo
-    }
-
-    @Test
-    public void testMavenRepoWithNameAndRootUrlOnly() {
-        when:
-        String repoRoot = 'http://www.reporoot.org'
-        String repoName = 'mavenRepoName'
-
-        TestMavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
-        repositoryFactory.createMavenRepository() >> repository
-        1 * repository.setName(repoName)
-        1 * repository.setUrl(repoRoot)
-        DependencyResolver resolver = Mock()
-        1 * repositoryFactory.toResolver(repository) >> resolver
-        ArtifactRepositoryInternal fixedRepo = Mock(ArtifactRepositoryInternal)
-        1 * repositoryFactory.createResolverBackedRepository(resolver) >> fixedRepo
-        fixedRepo.getName() >> repoName
-
-        then:
-        handler.mavenRepo([name: repoName, url: repoRoot]).is(resolver)
-        handler.size() == 1
-        handler.first() == fixedRepo
-    }
-
-    @Test
-    public void testMavenRepoWithoutName() {
-        when:
-        String repoRoot = 'http://www.reporoot.org'
-
-        TestMavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
-        repositoryFactory.createMavenRepository() >> repository
-        1 * repository.setUrl(repoRoot)
-        DependencyResolver resolver = Mock()
-        1 * repositoryFactory.toResolver(repository) >> resolver
-        ArtifactRepositoryInternal fixedRepo = Mock(ArtifactRepositoryInternal)
-        1 * repositoryFactory.createResolverBackedRepository(resolver) >> fixedRepo
-        1 * fixedRepo.setName("mavenRepo")
-
-        then:
-        handler.mavenRepo([url: repoRoot]).is(resolver)
-        handler.size() == 1
-        handler.first() == fixedRepo
-    }
-
     public void createIvyRepositoryUsingClosure() {
         when:
-        def repository = Mock(TestIvyArtifactRepository)
+        def repository = Mock(TestIvyArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createIvyRepository() >> repository
 
         then:
-        handler.ivy { }.is repository
+        handler.ivy {}.is repository
     }
 
     def createIvyRepositoryUsingAction() {
         when:
-        def repository = Mock(TestIvyArtifactRepository)
+        def repository = Mock(TestIvyArtifactRepository) { getName() >> "name" }
         def action = Mock(Action)
         1 * repositoryFactory.createIvyRepository() >> repository
 
@@ -197,9 +129,9 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
         repositoryFactory.createIvyRepository() >>> [repo1, repo2, repo3]
 
         when:
-        handler.ivy { }
-        handler.ivy { }
-        handler.ivy { }
+        handler.ivy {}
+        handler.ivy {}
+        handler.ivy {}
 
         then:
         repo1Name == "ivy"
@@ -209,16 +141,16 @@ class DefaultRepositoryHandlerTest extends DefaultArtifactRepositoryContainerTes
 
     public void createMavenRepositoryUsingClosure() {
         when:
-        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
+        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository) { getName() >> "name" }
         1 * repositoryFactory.createMavenRepository() >> repository
 
         then:
-        handler.maven { }.is repository
+        handler.maven {}.is repository
     }
 
     public void createMavenRepositoryUsingAction() {
         when:
-        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository)
+        MavenArtifactRepository repository = Mock(TestMavenArtifactRepository) { getName() >> "name" }
         def action = Mock(Action)
         1 * repositoryFactory.createMavenRepository() >> repository
 
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 3875a21..41709ef 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
@@ -17,6 +17,8 @@ package org.gradle.api.internal.artifacts.dsl.dependencies
 
 import org.gradle.api.artifacts.*
 import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
+import org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler
+import org.gradle.api.internal.artifacts.query.ArtifactResolutionQueryFactory
 import spock.lang.Specification
 
 class DefaultDependencyHandlerTest extends Specification {
@@ -28,7 +30,7 @@ class DefaultDependencyHandlerTest extends Specification {
     private DependencySet dependencySet = Mock()
 
     private DefaultDependencyHandler dependencyHandler = new DefaultDependencyHandler(
-            configurationContainer, dependencyFactory, projectFinder, Stub(ComponentMetadataHandler), Stub(ArtifactResolutionQueryFactory))
+            configurationContainer, dependencyFactory, projectFinder, Stub(ComponentMetadataHandler), Stub(ComponentModuleMetadataHandler), Stub(ArtifactResolutionQueryFactory))
 
     void setup() {
         _ * configurationContainer.findByName(TEST_CONF_NAME) >> configuration
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 2a91119..cfb0595 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
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.internal.artifacts.publish;
 
-import org.apache.ivy.core.module.id.ModuleId;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -34,7 +32,6 @@ public abstract class AbstractPublishArtifactTest {
     private static final String TEST_TYPE = "type";
     private static final String TEST_CLASSIFIER = "classifier";
     private static final Date TEST_DATE = new Date();
-    private static final ModuleRevisionId TEST_MODULE_REVISION_ID = new ModuleRevisionId(new ModuleId("group", "name"), "version");
 
     protected File getTestFile() {
         return TEST_FILE;
@@ -56,10 +53,6 @@ public abstract class AbstractPublishArtifactTest {
         return TEST_CLASSIFIER;
     }
 
-    protected ModuleRevisionId getTestModuleRevisionId() {
-        return TEST_MODULE_REVISION_ID;
-    }
-
     protected Date getDate() {
         return TEST_DATE;
     }
@@ -68,8 +61,6 @@ public abstract class AbstractPublishArtifactTest {
         setImposteriser(ClassImposteriser.INSTANCE);
     }};
 
-    abstract PublishArtifact createPublishArtifact(String classifier);
-
     protected void assertCommonPropertiesAreSet(PublishArtifact artifact, boolean shouldHaveClassifier) {
         assertEquals(getTestName(), artifact.getName());
         assertEquals(getTestType(), artifact.getType());
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 681f65a..1f59f46 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
@@ -17,7 +17,6 @@
 package org.gradle.api.internal.artifacts.publish;
 
 import org.gradle.api.Task;
-import org.gradle.api.artifacts.PublishArtifact;
 import org.jmock.integration.junit4.JMock;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -30,9 +29,6 @@ import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class DefaultPublishArtifactTest extends AbstractPublishArtifactTest {
-    protected PublishArtifact createPublishArtifact(String classifier) {
-        return new DefaultPublishArtifact(getTestName(), getTestExt(), getTestType(), classifier, getDate(), getTestFile());
-    }
 
     @Test
     public void init() {
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
deleted file mode 100644
index 1e09674..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/repositories/ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest.groovy
+++ /dev/null
@@ -1,67 +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
-
-import org.gradle.api.Project
-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.TestUtil
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class ArtifactRepositoryChangingNameAfterContainerInclusionDeprecationTest extends Specification {
-
-    TestAppender appender = new TestAppender()
-    @Rule ConfigureLogging logging = new ConfigureLogging(appender)
-    Project project
-
-    def setup() {
-        project = TestUtil.createRootProject()
-        DeprecationLogger.reset()
-    }
-
-    def cleanup() {
-        DeprecationLogger.reset()
-    }
-
-    /**
-     * This is a bit of a weird test. We are assuming that repository impls are extending AbstractArtifactRepository.
-     * Also, we are relying on DefaultReportContainerTest testing that we inform repositories when they are
-     */
-    @Unroll
-    def "logs deprecation warning on name change of #name repo"() {
-        given:
-        ArtifactRepository artifactRepository = repoNotation.call(project.repositories)
-
-        when:
-        artifactRepository.name = "changed"
-
-        then:
-        appender.toString().contains("Changing the name of an ArtifactRepository that is part of a container has been deprecated")
-
-        where:
-        name           | repoNotation
-        "flatDir"      | { it.flatDir {} }
-        "ivy"          | { it.ivy {} }
-        "maven"        | { it.maven {} }
-        "mavenCentral" | { it.mavenCentral() }
-        "mavenLocal"   | { it.mavenLocal() }
-    }
-}
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
index 8284894..56cfc2a 100644
--- 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
@@ -16,7 +16,6 @@
 
 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
@@ -29,8 +28,8 @@ import org.gradle.cache.internal.CacheScopeMapping
 import org.gradle.cache.internal.DefaultCacheRepository
 import org.gradle.internal.id.RandomLongIdGenerator
 import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.messaging.serialize.DefaultSerializerRegistry
-import org.gradle.messaging.serialize.SerializerRegistry
+import org.gradle.internal.serialize.DefaultSerializerRegistry
+import org.gradle.internal.serialize.SerializerRegistry
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.testfixtures.internal.InMemoryCacheFactory
@@ -69,7 +68,7 @@ public class DefaultTaskArtifactStateRepositoryTest extends Specification {
     DefaultTaskArtifactStateRepository repository
 
     def setup() {
-        CacheRepository cacheRepository = new DefaultCacheRepository(mapping, CacheUsage.ON, new InMemoryCacheFactory())
+        CacheRepository cacheRepository = new DefaultCacheRepository(mapping, new InMemoryCacheFactory())
         TaskArtifactStateCacheAccess cacheAccess = new DefaultTaskArtifactStateCacheAccess(gradle, cacheRepository, new NoOpDecorator())
         FileCollectionSnapshotter inputFilesSnapshotter = new DefaultFileCollectionSnapshotter(new CachingFileSnapshotter(new DefaultHasher(), cacheAccess), cacheAccess)
         FileCollectionSnapshotter outputFilesSnapshotter = new OutputFilesCollectionSnapshotter(inputFilesSnapshotter, new RandomLongIdGenerator(), cacheAccess)
@@ -77,7 +76,7 @@ public class DefaultTaskArtifactStateRepositoryTest extends Specification {
         inputFilesSnapshotter.registerSerializers(serializerRegistry);
         outputFilesSnapshotter.registerSerializers(serializerRegistry);
         TaskHistoryRepository taskHistoryRepository = new CacheBackedTaskHistoryRepository(cacheAccess, new CacheBackedFileSnapshotRepository(cacheAccess, serializerRegistry.build(), new RandomLongIdGenerator()))
-        repository = new DefaultTaskArtifactStateRepository(taskHistoryRepository, new DirectInstantiator(), outputFilesSnapshotter, inputFilesSnapshotter)
+        repository = new DefaultTaskArtifactStateRepository(taskHistoryRepository, DirectInstantiator.INSTANCE, outputFilesSnapshotter, inputFilesSnapshotter)
     }
 
     def artifactsAreNotUpToDateWhenCacheIsEmpty() {
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
index 86723cb..4a90b93 100755
--- 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
@@ -27,7 +27,7 @@ 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)
+    ShortCircuitTaskArtifactStateRepository repository = new ShortCircuitTaskArtifactStateRepository(startParameter, DirectInstantiator.INSTANCE, delegate)
     TaskArtifactState taskArtifactState = Mock(TaskArtifactState)
     TaskInternal task = Mock(TaskInternal)
     TaskOutputsInternal outputs = Mock(TaskOutputsInternal)
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
index 16740e3..e26f675 100644
--- 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
@@ -15,12 +15,9 @@
  */
 
 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.FileCollectionSnapshotter
 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
 
@@ -30,20 +27,10 @@ public class InputFilesStateChangeRuleTest extends Specification {
     FileCollectionSnapshot.ChangeIterator<String> changeIterator = Mock()
 
     TaskStateChanges createStateChanges() {
-        def taskInputs = Stub(TaskInputs) {
-            getFiles() >> new SimpleFileCollection()
-        }
-        def task = Stub(TaskInternal) {
-            getInputs() >> taskInputs
-        }
-        def snapshotter = Stub(FileCollectionSnapshotter) {
-            snapshot(_) >> inputSnapshot
-        }
-
         def previousExecution = Stub(TaskExecution) {
             getInputFilesSnapshot() >> previousInputSnapshot
         }
-        return InputFilesStateChangeRule.create(task, previousExecution, Mock(TaskExecution), snapshotter)
+        return InputFilesStateChangeRule.create(previousExecution, Mock(TaskExecution), inputSnapshot)
     }
 
     def "emits change for no previous input snapshot"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateStateTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateStateTest.groovy
new file mode 100644
index 0000000..17f7ccc
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/rules/TaskUpToDateStateTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.UncheckedIOException
+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.FileCollectionSnapshotter
+import org.gradle.api.internal.changedetection.state.FilesSnapshotSet
+import org.gradle.api.internal.changedetection.state.TaskHistoryRepository
+import org.gradle.api.tasks.TaskInputs
+import spock.lang.Issue
+import spock.lang.Specification
+
+class TaskUpToDateStateTest extends Specification {
+    private TaskInternal stubTask
+    private TaskHistoryRepository.History stubHistory
+    private FileCollectionSnapshotter stubOutputFileSnapshotter
+    private FileCollectionSnapshotter stubInputFileSnapshotter
+
+    def setup() {
+        TaskInputs stubInputs = Stub(TaskInputs)
+        TaskOutputsInternal stubOutputs = Stub(TaskOutputsInternal)
+        this.stubTask = Stub(TaskInternal) {
+            _ * getName() >> { "testTask" }
+            _ * getInputs() >> stubInputs
+            _ * getOutputs() >> stubOutputs
+        }
+        this.stubHistory = Stub(TaskHistoryRepository.History)
+        this.stubOutputFileSnapshotter = Stub(FileCollectionSnapshotter)
+        this.stubInputFileSnapshotter = Stub(FileCollectionSnapshotter)
+    }
+
+    def "constructor invokes snapshots" () {
+        setup:
+        FileCollectionSnapshot stubSnapshot = Stub(FileCollectionSnapshot) {
+            _ * getSnapshot() >> Stub(FilesSnapshotSet)
+        }
+        FileCollectionSnapshotter mockOutputFileSnapshotter = Mock(FileCollectionSnapshotter)
+        FileCollectionSnapshotter mockInputFileSnapshotter = Mock(FileCollectionSnapshotter)
+
+        when:
+        new TaskUpToDateState(stubTask, stubHistory, mockOutputFileSnapshotter, mockInputFileSnapshotter)
+
+        then:
+        noExceptionThrown()
+        1 * mockOutputFileSnapshotter.snapshot(_)
+        1 * mockInputFileSnapshotter.snapshot(_) >> stubSnapshot
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2967")
+    def "constructor adds context when input snapshot throws UncheckedIOException" () {
+        setup:
+        def cause = new UncheckedIOException("thrown from stub")
+        _ * stubInputFileSnapshotter.snapshot(_) >> { throw cause }
+
+        when:
+        new TaskUpToDateState(stubTask, stubHistory, stubOutputFileSnapshotter, stubInputFileSnapshotter)
+
+        then:
+        def e = thrown(UncheckedIOException)
+        e.message.contains(stubTask.getName())
+        e.message.contains("up-to-date")
+        e.message.contains("input")
+        e.cause == cause
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2967")
+    def "constructor adds context when output snapshot throws UncheckedIOException" () {
+        setup:
+        def cause = new UncheckedIOException("thrown from stub")
+         _ * stubOutputFileSnapshotter.snapshot(_) >> { throw cause }
+
+        when:
+        new TaskUpToDateState(stubTask, stubHistory, stubOutputFileSnapshotter, stubInputFileSnapshotter)
+
+        then:
+        def e = thrown(UncheckedIOException)
+        e.message.contains(stubTask.getName())
+        e.message.contains("up-to-date")
+        e.message.contains("output")
+        e.cause == cause
+    }
+}
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
index 7e81421..19fbfd2 100644
--- 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
@@ -17,7 +17,7 @@ package org.gradle.api.internal.changedetection.state
 
 import org.gradle.cache.PersistentIndexedCache
 import org.gradle.internal.id.IdGenerator
-import org.gradle.messaging.serialize.Serializer
+import org.gradle.internal.serialize.Serializer
 import spock.lang.Specification
 
 class CacheBackedFileSnapshotRepositoryTest extends Specification {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotterTest.groovy
index b66dcbd..6f96986 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/DefaultFileCollectionSnapshotterTest.groovy
@@ -35,7 +35,7 @@ public class DefaultFileCollectionSnapshotterTest extends Specification {
 
     def setup() {
         fileSnapshotter.snapshot(_) >> { File file ->
-            return Stub(FileSnapshotter.FileSnapshot) {
+            return Stub(FileSnapshot) {
                 getHash() >> HashUtil.sha1(file).asByteArray()
             }
         }
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
index 607e47d..6492f4e 100644
--- 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
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.changedetection.state
 
-import org.gradle.messaging.serialize.SerializerSpec
+import org.gradle.internal.serialize.SerializerSpec
 
 class DefaultFileSnapshotterSerializerTest extends SerializerSpec {
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializerTest.groovy
new file mode 100644
index 0000000..587b738
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/state/InputPropertiesSerializerTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.GradleException
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.serialize.MapSerializer
+import org.gradle.internal.serialize.OutputStreamBackedEncoder
+import spock.lang.Specification
+import spock.lang.Subject
+
+class InputPropertiesSerializerTest extends Specification {
+
+    def output = new ByteArrayOutputStream()
+    def encoder = new OutputStreamBackedEncoder(output)
+
+    @Subject serializer = new InputPropertiesSerializer(this.class.getClassLoader())
+
+    def "serializes empty properties"() {
+        write [:]
+        expect:
+        [:] == written
+    }
+
+    def "serializes properties"() {
+        write([a: "x", b: "y"])
+        expect:
+        [a: "x", b: "y"] == written
+    }
+
+    def "serializes properties with custom classes"() {
+        write([a: new SomeSerializableObject(x:10)])
+        expect:
+        written["a"].x == 10
+    }
+
+    def "informs which properties are not serializable"() {
+        when: write([a: 'x', b: new SomeNotSerializableObject()])
+        then:
+        def ex = thrown(GradleException)
+        ex.message == "Unable to store task input properties. Property 'b' with value 'I'm not serializable' cannot be serialized."
+        ex.cause.class == MapSerializer.EntrySerializationException
+    }
+
+    static class SomeSerializableObject implements Serializable {
+        int x
+    }
+
+    static class SomeNotSerializableObject {
+        public String toString() { "I'm not serializable" }
+    }
+
+    private Map<String, Object> getWritten() {
+        serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(output.toByteArray())))
+    }
+
+    private void write(Map map) {
+        serializer.write(encoder, map)
+    }
+}
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
index c569af8..c5d3da2 100644
--- 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
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.changedetection.state
 
-import org.gradle.messaging.serialize.Serializer
-import org.gradle.messaging.serialize.SerializerSpec
+import org.gradle.internal.serialize.Serializer
+import org.gradle.internal.serialize.SerializerSpec
 
 class OutputFilesSnapshotSerializerTest extends SerializerSpec {
     def targetSerializer = Mock(Serializer)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistryTest.groovy
index c5a2ed6..1c865b5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/classpath/DefaultModuleRegistryTest.groovy
@@ -32,6 +32,7 @@ class DefaultModuleRegistryTest extends Specification {
 
         distDir.createDir("lib")
         distDir.createDir("lib/plugins")
+        distDir.createDir("lib/plugins/sonar")
         runtimeDep = distDir.createZip("lib/dep-1.2.jar")
 
         resourcesDir = tmpDir.createDir("classes")
@@ -234,4 +235,18 @@ class DefaultModuleRegistryTest extends Specification {
         module.implementationClasspath.asFiles == [runtimeDep]
         module.runtimeClasspath.empty
     }
+
+    def "also looks in subdirectories of plugins directory when searching for external modules"() {
+        given:
+        def cl = new URLClassLoader([] as URL[])
+        def registry = new DefaultModuleRegistry(cl, distDir)
+
+        when:
+        def sonarDependency = distDir.createZip("lib/plugins/sonar/sonar-dependency-1.2.jar")
+
+        then:
+        def module = registry.getExternalModule("sonar-dependency")
+        module.implementationClasspath.asFiles == [sonarDependency]
+        module.runtimeClasspath.empty
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/StringToEnumTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/StringToEnumTransformerTest.groovy
new file mode 100644
index 0000000..09e2604
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/StringToEnumTransformerTest.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.coerce
+
+import org.gradle.internal.typeconversion.TypeConversionException
+import spock.lang.Specification
+import spock.lang.Unroll
+
+ at SuppressWarnings("GroovyUnusedDeclaration")
+class StringToEnumTransformerTest extends Specification {
+
+    def transformer = new StringToEnumTransformer()
+
+    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 TypeConversionException
+        e.message.contains TestEnum.values().toString() // error message shows valid values
+    }
+
+}
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
deleted file mode 100644
index 2110ce7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/coerce/TypeCoercingMethodArgumentsTransformerTest.groovy
+++ /dev/null
@@ -1,68 +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.coerce
-
-import org.gradle.internal.typeconversion.TypeConversionException
-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 TypeConversionException
-        e.message.contains TestEnum.values().toString() // error message shows valid values
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistryTest.groovy
new file mode 100644
index 0000000..c787e03
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/component/DefaultComponentTypeRegistryTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.component
+
+import org.gradle.api.component.Artifact
+import org.gradle.api.component.Component
+import spock.lang.Specification
+
+class DefaultComponentTypeRegistryTest extends Specification {
+
+    def registry = new DefaultComponentTypeRegistry()
+
+    def "fails getting registration for unregistered component"() {
+        when:
+        registry.getComponentRegistration(MyComponentType)
+
+        then:
+        def e = thrown IllegalArgumentException
+        e.message == "Not a registered component type: ${MyComponentType.name}."
+    }
+
+    def "fails getting type registration for unregistered type"() {
+        given:
+        registry.maybeRegisterComponentType(MyComponentType)
+
+        when:
+        registry.getComponentRegistration(MyComponentType).getArtifactType(MyArtifactType)
+
+        then:
+        def e = thrown IllegalArgumentException
+        e.message == "Artifact type ${MyArtifactType.name} is not registered for component type ${MyComponentType.name}."
+    }
+
+    def "cannot register same artifact type twice"() {
+        given:
+        def componentRegistration = registry.maybeRegisterComponentType(MyComponentType)
+        componentRegistration.registerArtifactType(MyArtifactType, ArtifactType.SOURCES)
+
+        when:
+        componentRegistration.registerArtifactType(MyArtifactType, ArtifactType.SOURCES)
+
+        then:
+        def e = thrown IllegalStateException
+        e.message == "Artifact type ${MyArtifactType.name} is already registered for component type ${MyComponentType.name}."
+    }
+
+    def "returns registered type for component"() {
+        when:
+        registry.maybeRegisterComponentType(MyComponentType).registerArtifactType(MyArtifactType, ArtifactType.SOURCES)
+
+        then:
+        registry.getComponentRegistration(MyComponentType).getArtifactType(MyArtifactType) == ArtifactType.SOURCES
+    }
+
+    def "can separately register multiple artifact types for component type"() {
+        when:
+        registry.maybeRegisterComponentType(MyComponentType).registerArtifactType(MyArtifactType, ArtifactType.IVY_DESCRIPTOR)
+        registry.maybeRegisterComponentType(MyComponentType).registerArtifactType(MyOtherArtifactType, ArtifactType.MAVEN_POM)
+
+        then:
+        def registration = registry.getComponentRegistration(MyComponentType)
+        registration.getArtifactType(MyArtifactType) == ArtifactType.IVY_DESCRIPTOR
+        registration.getArtifactType(MyOtherArtifactType) == ArtifactType.MAVEN_POM
+    }
+
+    interface MyComponentType extends Component {}
+    interface MyArtifactType extends Artifact {}
+    interface MyOtherArtifactType extends Artifact {}
+}
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 b0ca056..493c4fc 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
@@ -24,12 +24,14 @@ import org.gradle.api.tasks.StopExecutionException;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.GUtil;
 import org.gradle.util.TestUtil;
 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.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,6 +52,11 @@ public class AbstractFileCollectionTest {
     final JUnit4Mockery context = new JUnit4GroovyMockery();
     final TaskDependency dependency = context.mock(TaskDependency.class);
 
+    @Before
+    public void setUp() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void usesDisplayNameAsToString() {
         TestFileCollection collection = new TestFileCollection();
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeElementTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeElementTest.java
index 0f39bac..ac5fa68 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeElementTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/AbstractFileTreeElementTest.java
@@ -16,8 +16,8 @@
 package org.gradle.api.internal.file;
 
 import org.gradle.api.file.RelativePath;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.filesystem.Chmod;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.GFileUtils;
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
index 9bea03d..2ab50ab 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
@@ -15,13 +15,18 @@
  */
 package org.gradle.api.internal.file
 
+import org.gradle.internal.typeconversion.UnsupportedNotationException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+ at UsesNativeServices
 class BaseDirFileResolverSpec extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
@@ -173,6 +178,20 @@ class BaseDirFileResolverSpec extends Specification {
         normalize("../../..", root) == root
     }
 
+    def "cannot resolve file using unsupported notation"() {
+        when:
+        resolver().resolve(12)
+
+        then:
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators("""Cannot convert the provided notation to a File or URI: 12.
+The following types/formats are supported:
+  - A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
+  - A String or CharSequence URI, for example 'file:/usr/include'.
+  - A File instance.
+  - A URI or URL instance.""")
+    }
+
     def createLink(File link, File target) {
         createLink(link, target.absolutePath)
     }
@@ -188,7 +207,11 @@ class BaseDirFileResolverSpec extends Specification {
     }
 
     def normalize(Object path, File baseDir = tmpDir.testDirectory) {
-        new BaseDirFileResolver(TestFiles.fileSystem(), baseDir).resolve(path)
+        resolver(baseDir).resolve(path)
+    }
+
+    private BaseDirFileResolver resolver(File baseDir = tmpDir.testDirectory) {
+        new BaseDirFileResolver(TestFiles.fileSystem(), baseDir)
     }
 
     private File[] getFsRoots() {
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 ddd2826..ee6d6ae 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
@@ -141,6 +141,7 @@ class BaseDirFileResolverTest {
     @Test public void testResolveRelativePath() {
         String relativeFileName = "relative"
         assertEquals(new File(baseDir, relativeFileName), baseDirConverter.resolve(relativeFileName))
+        assertEquals(new File(baseDir, relativeFileName), baseDirConverter.resolve(new StringBuffer(relativeFileName)))
         assertEquals(baseDir, baseDirConverter.resolve("."))
     }
 
@@ -149,10 +150,6 @@ class BaseDirFileResolverTest {
         assertEquals(absoluteFile, baseDirConverter.resolve(absoluteFile))
     }
 
-    @Test public void testResolveRelativeObject() {
-        assertEquals(new File(baseDir, "12"), baseDirConverter.resolve(12))
-    }
-
     @Test public void testResolveFileWithRelativePath() {
         File relativeFile = new File('relative')
         assertEquals(new File(baseDir, 'relative'), baseDirConverter.resolve(relativeFile))
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BasicFileResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BasicFileResolverTest.groovy
new file mode 100644
index 0000000..9627aad
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BasicFileResolverTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.file
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BasicFileResolverTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def baseDir = tmpDir.createDir("base-dir")
+    def resolver = new BasicFileResolver(baseDir)
+
+    def "converts relative path"() {
+        expect:
+        resolver.transform("some-file") == baseDir.file("some-file")
+        resolver.transform("../other-file") == baseDir.file("../other-file")
+        resolver.transform(".") == baseDir
+    }
+
+    def "converts absolute path"() {
+        def target = tmpDir.file("some-file")
+
+        expect:
+        resolver.transform(target.absolutePath) == target
+    }
+
+    def "converts file URI"() {
+        def target = tmpDir.file("some-file")
+
+        expect:
+        resolver.transform(target.toURI().toString()) == target
+    }
+
+    def "does not convert http URI"() {
+        when:
+        resolver.transform("http://127.0.0.1")
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == "Cannot convert URL 'http://127.0.0.1' to a file."
+    }
+}
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 1ce86c3..2fb77ad 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,11 +21,13 @@ 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.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.TestUtil;
 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;
 
@@ -44,6 +46,11 @@ public class CompositeFileCollectionTest {
     private final AbstractFileCollection source2 = context.mock(AbstractFileCollection.class, "source2");
     private final TestCompositeFileCollection collection = new TestCompositeFileCollection(source1, source2);
 
+    @Before
+    public void setUp() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void containsUnionOfAllSourceCollections() {
         final File file1 = new File("1");
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 057e82b..59c4542 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,6 +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.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.TestUtil;
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
@@ -29,6 +30,8 @@ import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import static org.junit.Assert.*;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -50,6 +53,11 @@ public class CompositeFileTreeTest {
         }
     };
 
+    @Before
+    public void setUp() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void matchingWithClosureReturnsUnionOfFilteredSets() {
         final Closure closure = TestUtil.TEST_CLOSURE;
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 bb96f53..6c33076 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
@@ -22,6 +22,8 @@ import org.gradle.api.InvalidUserDataException
 import org.gradle.api.PathValidation
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileTree
+import org.gradle.api.internal.AsmBackedClassGenerator
+import org.gradle.api.internal.ClassGeneratorBackedInstantiator
 import org.gradle.api.internal.file.archive.TarFileTree
 import org.gradle.api.internal.file.archive.ZipFileTree
 import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
@@ -32,23 +34,28 @@ import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.process.ExecResult
-import org.gradle.process.internal.DefaultExecAction
 import org.gradle.process.internal.ExecException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
-import org.junit.Test
 import spock.lang.Specification
 
+ at UsesNativeServices
 public class DefaultFileOperationsTest extends Specification {
     private final FileResolver resolver = Mock()
     private final TaskResolver taskResolver = Mock()
     private final TemporaryFileProvider temporaryFileProvider = Mock()
-    private final Instantiator instantiator = new DirectInstantiator()
+    private final Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
     private final FileLookup fileLookup = Mock()
-    private DefaultFileOperations fileOperations = new DefaultFileOperations(resolver, taskResolver, temporaryFileProvider, instantiator, fileLookup)
+    private DefaultFileOperations fileOperations = instance()
+
+    private DefaultFileOperations instance(FileResolver resolver = resolver) {
+        instantiator.newInstance(DefaultFileOperations, resolver, taskResolver, temporaryFileProvider, instantiator, fileLookup)
+    }
+
     @Rule
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
@@ -99,22 +106,6 @@ public class DefaultFileOperationsTest extends Specification {
         fileTree.resolver.is(resolver)
     }
 
-    def createsAndConfiguresFileTree() {
-        given:
-        TestFile baseDir = expectPathResolved('base')
-        
-        when:
-        def fileTree = fileOperations.fileTree('base') {
-            builtBy 1
-        }
-        
-        then:
-        fileTree instanceof FileTree
-        fileTree.dir == baseDir
-        fileTree.resolver.is(resolver)
-        fileTree.builtBy == [1] as Set
-    }
-    
     def createsFileTreeFromMap() {
         TestFile baseDir = expectPathResolved('base')
 
@@ -127,19 +118,6 @@ public class DefaultFileOperationsTest extends Specification {
         fileTree.resolver.is(resolver)
     }
 
-    @Test
-    public void createsFileTreeFromClosure() {
-        TestFile baseDir = expectPathResolved('base')
-
-        when:
-        def fileTree = fileOperations.fileTree { from 'base' }
-
-        then:
-        fileTree instanceof FileTree
-        fileTree.dir == baseDir
-        fileTree.resolver.is(resolver)
-    }
-
     def createsZipFileTree() {
         expectPathResolved('path')
         expectTempFileCreated()
@@ -217,7 +195,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     def createsCopySpec() {
         when:
-        def spec = fileOperations.copySpec { include 'pattern'}
+        def spec = fileOperations.copySpec { include 'pattern' }
 
         then:
         spec instanceof DefaultCopySpec
@@ -244,7 +222,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     def javaexec() {
         File testFile = tmpDir.file("someFile")
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
         List files = ClasspathUtil.getClasspath(getClass().classLoader)
 
         when:
@@ -260,7 +238,7 @@ public class DefaultFileOperationsTest extends Specification {
     }
 
     def javaexecWithNonZeroExitValueShouldThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
 
         when:
         fileOperations.javaexec {
@@ -272,7 +250,7 @@ public class DefaultFileOperationsTest extends Specification {
     }
 
     def javaexecWithNonZeroExitValueAndIgnoreExitValueShouldNotThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
 
         when:
         ExecResult result = fileOperations.javaexec {
@@ -286,7 +264,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     @Requires(TestPrecondition.NOT_WINDOWS)
     def exec() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
         File testFile = tmpDir.file("someFile")
 
         when:
@@ -303,7 +281,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     @Requires(TestPrecondition.NOT_WINDOWS)
     def execWithNonZeroExitValueShouldThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
 
         when:
         fileOperations.exec {
@@ -318,7 +296,7 @@ public class DefaultFileOperationsTest extends Specification {
 
     @Requires(TestPrecondition.NOT_WINDOWS)
     def execWithNonZeroExitValueAndIgnoreExitValueShouldNotThrowException() {
-        fileOperations = new DefaultFileOperations(resolver(), taskResolver, temporaryFileProvider, instantiator, fileLookup)
+        fileOperations = instance(resolver())
 
         when:
         ExecResult result = fileOperations.exec {
@@ -332,11 +310,6 @@ public class DefaultFileOperationsTest extends Specification {
         result.exitValue != 0
     }
 
-    def createsExecAction() {
-        expect:
-        fileOperations.newExecAction() instanceof DefaultExecAction
-    }
-
     def resolver() {
         return TestFiles.resolver(tmpDir.testDirectory)
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileTreeElementTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileTreeElementTest.groovy
index af4a1d9..19a570d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileTreeElementTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileTreeElementTest.groovy
@@ -16,8 +16,8 @@
 package org.gradle.api.internal.file
 
 import org.gradle.api.file.FileTreeElement
-import org.gradle.internal.nativeplatform.filesystem.Chmod
-import org.gradle.internal.nativeplatform.filesystem.Stat
+import org.gradle.internal.nativeintegration.filesystem.Chmod
+import org.gradle.internal.nativeintegration.filesystem.Stat
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
index c069291..a8319c6 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultSourceDirectorySetTest.groovy
@@ -22,12 +22,14 @@ import org.gradle.api.tasks.StopExecutionException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.GFileUtils
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
 import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes
 import static org.hamcrest.Matchers.equalTo
 
+ at UsesNativeServices
 public class DefaultSourceDirectorySetTest extends Specification {
     @Rule public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     private final TestFile testDir = tmpDir.testDirectory
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationConverterTest.groovy
new file mode 100644
index 0000000..689b3b7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationConverterTest.groovy
@@ -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
+
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Issue
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class FileOrUriNotationConverterTest extends Specification {
+
+    @Rule public TestNameTestDirectoryProvider folder = new TestNameTestDirectoryProvider();
+
+    def "with File returns this File"() {
+        setup:
+        def testFile = folder.createFile("test1")
+        when:
+        def object = parse(testFile)
+        then:
+        object instanceof File
+        testFile == object
+    }
+
+    def "with file path as String"() {
+        setup:
+        def testFile = folder.createFile("test1")
+        when:
+        def object = parse(testFile.getAbsolutePath())
+        then:
+        object instanceof File
+        testFile.getAbsolutePath() == object.getAbsolutePath()
+    }
+
+    def "with file URI"() {
+        setup:
+        def testFileURI = folder.createFile("test1").toURI()
+        when:
+        def object = parse(testFileURI)
+        then:
+        object instanceof File
+        object.toURI() == testFileURI
+    }
+
+    def "with URI as CharSequence"() {
+        setup:
+        def uriString = folder.createFile("test1").toURI().toString()
+        when:
+        def object = parse(uriString)
+        then:
+        object instanceof File
+        object.toURI().toString() == uriString
+    }
+
+    def "with URL"() {
+        setup:
+        def testFileURL = folder.createFile("test1").toURI().toURL()
+        when:
+        def object = parse(testFileURL)
+        then:
+        object instanceof File
+        object.toURI().toURL() == testFileURL
+    }
+
+    def "with non File URI URI instance is returned"() {
+        setup:
+        def unsupportedURI = URI.create("http://gradle.org")
+        when:
+        def parsed = parse(unsupportedURI)
+        then:
+        parsed instanceof URI
+    }
+
+    def "with non File URI String URI is returned"() {
+        setup:
+        def unsupportedURIString = "http://gradle.org"
+        when:
+        def parsed = parse(unsupportedURIString)
+        then:
+        parsed instanceof URI
+    }
+
+    @Issue("GRADLE-2072")
+    def "parsing unknown types causes UnsupportedNotationException"() {
+        when:
+        parse(12)
+
+        then:
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators("""Cannot convert the provided notation to a File or URI: 12.
+The following types/formats are supported:
+  - A String or CharSequence path, for example 'src/main/java' or '/usr/include'.
+  - A String or CharSequence URI, for example 'file:/usr/include'.
+  - A File instance.
+  - A URI or URL instance.""")
+    }
+
+    def parse(def value) {
+        return FileOrUriNotationConverter.parser(TestFiles.fileSystem()).parseNotation(value)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationParserTest.groovy
deleted file mode 100644
index f2edccf..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/FileOrUriNotationParserTest.groovy
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.file
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class FileOrUriNotationParserTest extends Specification {
-
-    @Rule public TestNameTestDirectoryProvider folder = new TestNameTestDirectoryProvider();
-
-    final FileOrUriNotationParser<Serializable> parser = new FileOrUriNotationParser<Serializable>(TestFiles.fileSystem())
-
-    def "with File returns this File"() {
-        setup:
-        def testFile = folder.createFile("test1")
-        when:
-        def object = parser.parseNotation(testFile)
-        then:
-        object instanceof File
-        testFile == object
-    }
-
-    def "with file path as String"() {
-        setup:
-        def testFile = folder.createFile("test1")
-        when:
-        def object = parser.parseNotation(testFile.getAbsolutePath())
-        then:
-        object instanceof File
-        testFile.getAbsolutePath() == object.getAbsolutePath()
-    }
-
-    def "with file URI"() {
-        setup:
-        def testFileURI = folder.createFile("test1").toURI()
-        when:
-        def object = parser.parseNotation(testFileURI)
-        then:
-        object instanceof File
-        object.toURI() == testFileURI
-    }
-
-    def "with URI as CharSequence"() {
-        setup:
-        def uriString = folder.createFile("test1").toURI().toString()
-        when:
-        def object = parser.parseNotation(uriString)
-        then:
-        object instanceof File
-        object.toURI().toString() == uriString
-    }
-
-    def "with URL"() {
-        setup:
-        def testFileURL = folder.createFile("test1").toURI().toURL()
-        when:
-        def object = parser.parseNotation(testFileURL)
-        then:
-        object instanceof File
-        object.toURI().toURL() == testFileURL
-    }
-
-    def "with non File URI URI instance is returned"() {
-        setup:
-        def unsupportedURI = URI.create("http://gradle.org")
-        when:
-        def parsed = parser.parseNotation(unsupportedURI)
-        then:
-        parsed instanceof URI
-    }
-
-    def "with non File URI String URI is returned"() {
-        setup:
-        def unsupportedURIString = "http://gradle.org"
-        when:
-        def parsed = parser.parseNotation(unsupportedURIString)
-        then:
-        parsed instanceof URI
-    }
-
-//    @Issue("GRADLE-2072")
-//    def "parsing unknown types causes UnsupportedNotationException"() {
-//        setup:
-//        def taskInternalMock = Mock(TaskInternal)
-//        def fileResolverMock = Mock(FileResolver)
-//        when:
-//        parser.parseNotation(new DefaultTaskOutputs(fileResolverMock, taskInternalMock))
-//        then:
-//        thrown(UnsupportedNotationException)
-//    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/RelativeFileTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/RelativeFileTest.groovy
new file mode 100644
index 0000000..b15ab2a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/RelativeFileTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.RelativePath
+import spock.lang.Specification
+
+class RelativeFileTest extends Specification {
+    def "can get base directory of relative file" () {
+        File file = new File("/some/relatively/long/path/to/a/file")
+        RelativePath relativePath = RelativePath.parse(true, "to/a/file")
+        RelativeFile relativeFile = new RelativeFile(file, relativePath)
+
+        expect:
+        relativeFile.getBaseDir() == new File("/some/relatively/long/path")
+    }
+
+    def "base directory is null if either file or relativepath are null" () {
+        RelativeFile relativeFile = new RelativeFile(file, relativePath)
+
+        expect:
+        relativeFile.getBaseDir() == null
+
+        where:
+        file                   | relativePath
+        new File("/some/path") | null
+        null                   | RelativePath.parse(true, "some/path")
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
index 0143acd..e581924 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileCollectionTest.java
@@ -16,9 +16,11 @@
 package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileCollection;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 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;
 
@@ -34,6 +36,11 @@ public class UnionFileCollectionTest {
     private final FileCollection source1 = context.mock(FileCollection.class, "source1");
     private final FileCollection source2 = context.mock(FileCollection.class, "source2");
 
+    @Before
+    public void setup() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void containsUnionOfAllSourceCollections() {
         final File file1 = new File("1");
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
index 25229e7..817ce74 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/UnionFileTreeTest.java
@@ -17,8 +17,10 @@ package org.gradle.api.internal.file;
 
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 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;
 
@@ -32,6 +34,11 @@ public class UnionFileTreeTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
     private final UnionFileTree set = new UnionFileTree("<display name>");
 
+    @Before
+    public void setUp() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void canAddFileTree() {
         FileTree set1 = context.mock(FileTree.class, "set1");
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
index 8c09b19..48af36e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java
@@ -19,6 +19,7 @@ import org.gradle.api.GradleException;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.file.FileResource;
 import org.gradle.api.internal.file.MaybeCompressedFileResource;
+import org.gradle.api.resources.MissingResourceException;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.Resources;
@@ -28,13 +29,11 @@ import org.junit.Test;
 import java.util.HashMap;
 import java.util.Map;
 
-import static java.util.Collections.EMPTY_LIST;
 import static org.gradle.api.file.FileVisitorUtil.*;
 import static org.gradle.api.internal.file.TestFiles.fileSystem;
 import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes;
 import static org.gradle.util.WrapUtil.toList;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
@@ -99,9 +98,14 @@ public class TarFileTreeTest {
     }
 
     @Test
-    public void isEmptyWhenTarFileDoesNotExist() {
-        assertVisits(tree, EMPTY_LIST, EMPTY_LIST);
-        assertSetContainsForAllTypes(tree, EMPTY_LIST);
+    public void failsWhenTarFileDoesNotExist() {
+        try {
+            tree.visit(null);
+            fail();
+        } catch (InvalidUserDataException e) {
+            assertThat(e.getMessage(), containsString("Cannot expand TAR '" + tarFile + "'."));
+            assertThat(e.getCause(), instanceOf(MissingResourceException.class));
+        }
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
index 106f621..de47b40 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java
@@ -26,7 +26,6 @@ import org.junit.Test;
 import java.util.HashMap;
 import java.util.Map;
 
-import static java.util.Collections.EMPTY_LIST;
 import static org.gradle.api.file.FileVisitorUtil.*;
 import static org.gradle.api.internal.file.TestFiles.fileSystem;
 import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes;
@@ -68,9 +67,13 @@ public class ZipFileTreeTest {
     }
 
     @Test
-    public void isEmptyWhenZipFileDoesNotExist() {
-        assertVisits(tree, EMPTY_LIST, EMPTY_LIST);
-        assertSetContainsForAllTypes(tree, EMPTY_LIST);
+    public void failsWhenZipFileDoesNotExist() {
+        try {
+            tree.visit(null);
+            fail();
+        } catch (InvalidUserDataException e) {
+            assertThat(e.getMessage(), equalTo("Cannot expand ZIP '" + zipFile + "' as it does not exist."));
+        }
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy
index 4490299..2c521b1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/BuildDependenciesOnlyFileCollectionResolveContextTest.groovy
@@ -17,10 +17,12 @@ package org.gradle.api.internal.file.collections
 
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.tasks.TaskDependency
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 import org.gradle.api.file.FileTree
 import org.gradle.api.file.FileCollection
 
+ at UsesNativeServices
 class BuildDependenciesOnlyFileCollectionResolveContextTest extends Specification {
     final BuildDependenciesOnlyFileCollectionResolveContext context = new BuildDependenciesOnlyFileCollectionResolveContext()
 
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 ad4e016..6fb3d95 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,11 +22,13 @@ 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.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.TestUtil;
 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.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +54,11 @@ public class DefaultConfigurableFileCollectionTest {
     private final DefaultConfigurableFileCollection collection = new DefaultConfigurableFileCollection(resolverMock,
             taskResolverStub);
 
+    @Before
+    public void setUp() {
+        NativeServicesTestFixture.initialize();
+    }
+
     @Test
     public void resolvesSpecifiedFilesUseFileResolver() {
         final File file1 = new File("1");
@@ -331,7 +338,7 @@ public class DefaultConfigurableFileCollectionTest {
 
         collection.from(fileCollectionMock);
         collection.from("f");
-        collection.builtBy('b');
+        collection.builtBy("b");
 
         final Task taskA = context.mock(Task.class, "a");
         final Task taskB = context.mock(Task.class, "b");
@@ -343,7 +350,7 @@ public class DefaultConfigurableFileCollectionTest {
             will(returnValue(dependency));
             allowing(dependency).getDependencies(null);
             will(returnValue(toSet(taskA)));
-            allowing(taskResolverStub).resolveTask('b');
+            allowing(taskResolverStub).resolveTask("b");
             will(returnValue(taskB));
         }});
 
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 01c7006..1583d53 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
@@ -28,6 +28,7 @@ import org.gradle.api.tasks.util.AbstractTestForPatternSet
 import org.gradle.api.tasks.util.PatternFilterable
 import org.gradle.api.tasks.util.PatternSet
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.gradle.util.JUnit4GroovyMockery
 import org.gradle.util.WrapUtil
 import org.jmock.integration.junit4.JUnit4Mockery
@@ -57,6 +58,7 @@ class DefaultConfigurableFileTreeTest extends AbstractTestForPatternSet {
 
     @Before public void setUp() {
         super.setUp()
+        NativeServicesTestFixture.initialize()
         fileSet = new DefaultConfigurableFileTree(testDir, fileResolverStub, taskResolverStub, fileCopier)
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy
index eea1f05..cfe6fe4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/DefaultFileCollectionResolveContextTest.groovy
@@ -15,6 +15,8 @@
  */
 package org.gradle.api.internal.file.collections
 
+import org.gradle.util.UsesNativeServices
+
 import java.util.concurrent.Callable
 
 import org.gradle.api.internal.file.FileResolver
@@ -25,6 +27,7 @@ import org.gradle.api.file.FileCollection
 import org.gradle.api.Task
 import org.gradle.api.tasks.TaskOutputs
 
+ at UsesNativeServices
 class DefaultFileCollectionResolveContextTest extends Specification {
     final FileResolver resolver = Mock()
     final DefaultFileCollectionResolveContext context = new DefaultFileCollectionResolveContext(resolver)
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 11a4694..13818ef 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
@@ -22,6 +22,7 @@ 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.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -55,6 +56,7 @@ public class DirectoryFileTreeTest {
 
     @Before
     public void setUp() {
+        NativeServicesTestFixture.initialize();
         visitor = context.mock(FileVisitor.class);
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy
index 6ece488..178e9fe 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/FileTreeAdapterTest.groovy
@@ -20,8 +20,10 @@ import org.gradle.api.file.FileVisitDetails
 import org.gradle.api.file.FileVisitor
 import org.gradle.api.tasks.TaskDependency
 import org.gradle.api.tasks.util.PatternFilterable
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
+ at UsesNativeServices
 class FileTreeAdapterTest extends Specification {
     def toStringUsesDisplayName() {
         MinimalFileTree tree = Mock()
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 a11c29a..d19bbf7 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
@@ -15,14 +15,16 @@
  */
 package org.gradle.api.internal.file.collections;
 
-import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.file.TestFiles;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.gradle.util.TestUtil;
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.List;
 
 import static org.gradle.api.file.FileVisitorUtil.assertCanStopVisiting;
@@ -46,8 +48,8 @@ public class MapFileTreeTest {
     
     @Test
     public void canAddAnElementUsingAClosureToGeneratedContent() {
-        Closure closure = TestUtil.toClosure("{it.write('content'.getBytes())}");
-        tree.add("path/file.txt", closure);
+        Action<OutputStream> action = getAction();
+        tree.add("path/file.txt", action);
 
         assertVisits(tree, toList("path/file.txt"), toList("path"));
         assertSetContainsForAllTypes(tree, toList("path/file.txt"));
@@ -58,10 +60,10 @@ public class MapFileTreeTest {
 
     @Test
     public void canAddMultipleElementsInDifferentDirs() {
-        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);
+        Action<OutputStream> action = getAction();
+        tree.add("path/file.txt", action);
+        tree.add("file.txt", action);
+        tree.add("path/subdir/file.txt", action);
 
         assertVisits(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"), toList("path", "path/subdir"));
         assertSetContainsForAllTypes(tree, toList("path/file.txt", "file.txt", "path/subdir/file.txt"));
@@ -69,9 +71,21 @@ public class MapFileTreeTest {
 
     @Test
     public void canStopVisitingElements() {
-        Closure closure = TestUtil.toClosure("{it.write('content'.getBytes())}");
+        Action<OutputStream> closure = getAction();
         tree.add("path/file.txt", closure);
         tree.add("file.txt", closure);
         assertCanStopVisiting(tree);
     }
+
+    private Action<OutputStream> getAction() {
+        return new Action<OutputStream>() {
+            public void execute(OutputStream outputStream) {
+                try {
+                    outputStream.write("content".getBytes());
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        };
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTreeSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTreeSpec.groovy
index e979a39..48e5373 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTreeSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingleIncludePatternFileTreeSpec.groovy
@@ -19,10 +19,12 @@ import org.gradle.api.file.FileVisitDetails
 import org.gradle.api.file.FileVisitor
 import org.gradle.api.specs.Spec
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import org.junit.ClassRule
 import spock.lang.Shared
 import spock.lang.Specification
 
+ at UsesNativeServices
 class SingleIncludePatternFileTreeSpec extends Specification {
     @Shared @ClassRule TestNameTestDirectoryProvider tempDir
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy
index 89f8b5c..0ef3610 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/collections/SingletonFileTreeTest.groovy
@@ -15,10 +15,12 @@
  */
 package org.gradle.api.internal.file.collections
 
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 import org.gradle.api.file.FileVisitor
 import org.gradle.api.file.FileVisitDetails
 
+ at UsesNativeServices
 class SingletonFileTreeTest extends Specification {
     def hasUsefulDisplayName() {
         File f = new File('test-file')
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
index 57416ea..c4aee2d 100644
--- 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
@@ -36,7 +36,7 @@ class CopyActionExecuterTest extends WorkspaceTest {
         }
 
         def resolver = TestFiles.resolver(testDirectory)
-        def copySpec = new DestinationRootCopySpec(resolver, new DefaultCopySpec(resolver, new DirectInstantiator()))
+        def copySpec = new DestinationRootCopySpec(resolver, new DefaultCopySpec(resolver, DirectInstantiator.INSTANCE))
         copySpec.with {
             into "out"
             from "a", {
@@ -56,7 +56,7 @@ class CopyActionExecuterTest extends WorkspaceTest {
                 new SimpleWorkResult(workResult)
             }
         }
-        def executer = new CopyActionExecuter(new DirectInstantiator(), TestFiles.fileSystem())
+        def executer = new CopyActionExecuter(DirectInstantiator.INSTANCE, TestFiles.fileSystem())
 
         when:
         executer.execute(copySpec, copyAction)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImplTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImplTest.groovy
index 83c6f3f..256f08a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImplTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopyFileVisitorImplTest.groovy
@@ -21,12 +21,12 @@ 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.nativeintegration.filesystem.FileSystem
 import org.gradle.internal.reflect.Instantiator
 import spock.lang.Specification
 
 class CopyFileVisitorImplTest extends Specification {
-    CopySpecInternal spec = Mock()
+    CopySpecResolver specResolver = Mock()
     CopyActionProcessingStreamAction action = Mock()
     Instantiator instantiator = Mock()
     FileSystem fileSystem = Mock()
@@ -34,19 +34,19 @@ class CopyFileVisitorImplTest extends Specification {
     FileVisitor copyFileVisitorImpl
 
     def setup() {
-        copyFileVisitorImpl = new CopyFileVisitorImpl(spec, action, instantiator, fileSystem)
+        copyFileVisitorImpl = new CopyFileVisitorImpl(specResolver, action, instantiator, fileSystem)
     }
 
     def "visit directory"() {
         given:
         FileVisitDetails dirDetails = Mock()
-        DefaultFileCopyDetails defaultFileCopyDetails = new DefaultFileCopyDetails(dirDetails, spec, fileSystem)
+        DefaultFileCopyDetails defaultFileCopyDetails = new DefaultFileCopyDetails(dirDetails, specResolver, fileSystem)
 
         when:
         copyFileVisitorImpl.visitDir(dirDetails)
 
         then:
-        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, spec, fileSystem) >> defaultFileCopyDetails
+        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, specResolver, fileSystem) >> defaultFileCopyDetails
         1 * action.processFile(defaultFileCopyDetails)
         0 * defaultFileCopyDetails.excluded
     }
@@ -54,14 +54,14 @@ class CopyFileVisitorImplTest extends Specification {
     def "visit file if no action are defined in copy spec"() {
         given:
         FileVisitDetails dirDetails = Mock()
-        DefaultFileCopyDetails defaultFileCopyDetails = new DefaultFileCopyDetails(dirDetails, spec, fileSystem)
+        DefaultFileCopyDetails defaultFileCopyDetails = new DefaultFileCopyDetails(dirDetails, specResolver, fileSystem)
 
         when:
         copyFileVisitorImpl.visitFile(dirDetails)
 
         then:
-        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, spec, fileSystem) >> defaultFileCopyDetails
-        1 * spec.getAllCopyActions() >> []
+        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, specResolver, fileSystem) >> defaultFileCopyDetails
+        1 * specResolver.getAllCopyActions() >> []
         1 * action.processFile(defaultFileCopyDetails)
         0 * defaultFileCopyDetails.excluded
     }
@@ -77,8 +77,8 @@ class CopyFileVisitorImplTest extends Specification {
         copyFileVisitorImpl.visitFile(dirDetails)
 
         then:
-        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, spec, fileSystem) >> defaultFileCopyDetails
-        1 * spec.getAllCopyActions() >> [fileCopyAction1, fileCopyAction2]
+        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, specResolver, fileSystem) >> defaultFileCopyDetails
+        1 * specResolver.getAllCopyActions() >> [fileCopyAction1, fileCopyAction2]
         1 * action.processFile(defaultFileCopyDetails)
         1 * fileCopyAction1.execute(defaultFileCopyDetails)
         1 * fileCopyAction2.execute(defaultFileCopyDetails)
@@ -96,8 +96,8 @@ class CopyFileVisitorImplTest extends Specification {
         copyFileVisitorImpl.visitFile(dirDetails)
 
         then:
-        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, spec, fileSystem) >> defaultFileCopyDetails
-        1 * spec.getAllCopyActions() >> [fileCopyAction1, fileCopyAction2]
+        1 * instantiator.newInstance(DefaultFileCopyDetails.class, dirDetails, specResolver, fileSystem) >> defaultFileCopyDetails
+        1 * specResolver.getAllCopyActions() >> [fileCopyAction1, fileCopyAction2]
         0 * action.processFile(defaultFileCopyDetails)
         1 * fileCopyAction1.execute(defaultFileCopyDetails)
         1 * defaultFileCopyDetails.excluded >> true
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecActionImplTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecActionImplTest.groovy
index 19f6855..6e10507 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecActionImplTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecActionImplTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.api.internal.file.copy
 import org.gradle.api.Action
 import org.gradle.api.file.FileTree
 import org.gradle.api.internal.file.CopyActionProcessingStreamAction
-import org.gradle.internal.nativeplatform.filesystem.FileSystem
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
 import org.gradle.internal.reflect.Instantiator
 import spock.lang.Specification
 
@@ -26,7 +26,7 @@ class CopySpecActionImplTest extends Specification {
     CopyActionProcessingStreamAction action = Mock()
     Instantiator instantiator = Mock()
     FileSystem fileSystem = Mock()
-    CopySpecInternal copySpecInternal = Mock()
+    CopySpecResolver copySpecResolver = Mock()
     FileTree source = Mock()
     Action<CopySpecInternal> copySpecInternalAction
 
@@ -36,10 +36,10 @@ class CopySpecActionImplTest extends Specification {
 
     def "can visit spec source"() {
         when:
-        copySpecInternalAction.execute(copySpecInternal)
+        copySpecInternalAction.execute(copySpecResolver)
 
         then:
-        1 * copySpecInternal.getSource() >> source
+        1 * copySpecResolver.getSource() >> source
         1 * source.visit(_ as CopyFileVisitorImpl)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStreamTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStreamTest.groovy
index 467ee64..ed5d325 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStreamTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecBackedCopyActionProcessingStreamTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.file.copy
 
 import org.gradle.api.internal.file.CopyActionProcessingStreamAction
-import org.gradle.internal.nativeplatform.filesystem.FileSystem
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
 import org.gradle.internal.reflect.Instantiator
 import spock.lang.Specification
 
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
index f2f006c..91d34e4 100644
--- 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
@@ -27,7 +27,7 @@ import spock.lang.Specification
 
 class CopySpecMatchingTest extends Specification {
 
-    DefaultCopySpec copySpec = new DefaultCopySpec(TestFiles.resolver(), new DirectInstantiator(), null)
+    DefaultCopySpec copySpec = new DefaultCopySpec(TestFiles.resolver(), DirectInstantiator.INSTANCE)
 
     FileTree fileTree = Mock()
 
@@ -37,14 +37,14 @@ class CopySpecMatchingTest extends Specification {
         FileCopyDetails details1 = Mock()
         FileCopyDetails details2 = Mock()
 
-        details1.relativePath >>  RelativePath.parse(true, 'path/abc.txt')
-        details2.relativePath >> RelativePath.parse(true, 'path/bcd.txt')
+        details1.relativeSourcePath >>  RelativePath.parse(true, 'path/abc.txt')
+        details2.relativeSourcePath >> RelativePath.parse(true, 'path/bcd.txt')
 
         Action matchingAction = Mock()
 
         when:
         copySpec.filesMatching("**/a*", matchingAction)
-        copySpec.allCopyActions.each { copyAction ->
+        copySpec.copyActions.each { copyAction ->
             copyAction.execute(details1)
             copyAction.execute(details2)
         }
@@ -60,14 +60,14 @@ class CopySpecMatchingTest extends Specification {
         FileCopyDetails details1 = Mock()
         FileCopyDetails details2 = Mock()
 
-        details1.relativePath >>  RelativePath.parse(true, 'path/abc.txt')
-        details2.relativePath >> RelativePath.parse(true, 'path/bcd.txt')
+        details1.relativeSourcePath >>  RelativePath.parse(true, 'path/abc.txt')
+        details2.relativeSourcePath >> RelativePath.parse(true, 'path/bcd.txt')
 
         Action matchingAction = Mock()
 
         when:
         copySpec.filesNotMatching("**/a*", matchingAction)
-        copySpec.allCopyActions.each { copyAction ->
+        copySpec.copyActions.each { copyAction ->
             copyAction.execute(details1)
             copyAction.execute(details2)
         }
@@ -78,11 +78,12 @@ class CopySpecMatchingTest extends Specification {
 
     def matchingSpecInherited() {
         given:
-        DefaultCopySpec childSpec = copySpec.addChild()
+        DefaultCopySpec childSpec = new DefaultCopySpec(TestFiles.resolver(), DirectInstantiator.INSTANCE)
+        CopySpecResolver childResolver = childSpec.buildResolverRelativeToParent(copySpec.buildRootResolver())
         when:
         copySpec.filesMatching("**/*.java", Actions.doNothing())
         then:
-        1 == childSpec.allCopyActions.size()
-        childSpec.allCopyActions[0] instanceof MatchingCopyAction
+        1 == childResolver.allCopyActions.size()
+        childResolver.allCopyActions[0] instanceof MatchingCopyAction
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecResolutionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecResolutionTest.groovy
new file mode 100644
index 0000000..5aad73a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DefaultCopySpecResolutionTest.groovy
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.DuplicatesStrategy
+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.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.Assert
+import org.junit.Rule
+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
+
+ at RunWith(JMock)
+public class DefaultCopySpecResolutionTest {
+
+    @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 = DirectInstantiator.INSTANCE
+    private final DefaultCopySpec parentSpec = new DefaultCopySpec(fileResolver, instantiator)
+
+    @Test
+    public void testSpecHasRootPathAsDestinationByDefault() {
+        assertThat(parentSpec.buildRootResolver().getDestPath(), equalTo(new RelativePath(false)))
+    }
+
+    @Test
+    public void testChildResolvesUsingParentDestinationPathAsDefault() {
+        parentSpec.into 'parent'
+        CopySpecResolver parentContext = parentSpec.buildRootResolver()
+        assertThat(parentContext.destPath, equalTo(new RelativePath(false, 'parent')))
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        CopySpecResolver childResolver = child.buildResolverRelativeToParent(parentContext)
+        assertThat(childResolver.destPath, equalTo(new RelativePath(false, 'parent')))
+    }
+
+    @Test
+    public void testChildDestinationPathIsResolvedAsNestedWithinParent() {
+        parentSpec.into 'parent'
+        CopySpecResolver parentContext = parentSpec.buildRootResolver()
+        assertThat(parentContext.destPath, equalTo(new RelativePath(false, 'parent')))
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        child.into 'child'
+        CopySpecResolver childResolver = child.buildResolverRelativeToParent(parentContext)
+        assertThat(childResolver.destPath, equalTo(new RelativePath(false, 'parent', 'child')))
+    }
+
+    @Test
+    public void testChildUsesParentPatternsAsDefault() {
+
+        Spec specInclude = [:] as Spec
+        Spec specExclude = [:] as Spec
+
+        parentSpec.include('parent-include')
+        parentSpec.exclude('parent-exclude')
+        parentSpec.include(specInclude)
+        parentSpec.exclude(specExclude)
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+
+
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        PatternSet patterns = childResolver.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 testChildUsesPatternsFromParent() {
+        Spec specInclude = [:] as Spec
+        Spec specExclude = [:] as Spec
+
+
+        parentSpec.include('parent-include')
+        parentSpec.exclude('parent-exclude')
+        parentSpec.include(specInclude)
+        parentSpec.exclude(specExclude)
+
+        Spec childInclude = [:] as Spec
+        Spec childExclude = [:] as Spec
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        child.include('child-include')
+        child.exclude('child-exclude')
+        child.include(childInclude)
+        child.exclude(childExclude)
+
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        PatternSet patterns = childResolver.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 testResolvesSourceUsingOwnSourceFilteredByPatternset() {
+        //Does not get source from root
+        parentSpec.from 'x'
+        parentSpec.from 'y'
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        child.from 'a'
+        child.from 'b'
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+
+        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(parentSpec.patternSet)))
+            will(returnValue(filteredTree))
+        }
+
+        assertThat(childResolver.source, sameInstance(filteredTree))
+    }
+
+
+
+    @Test
+    public void duplicatesStrategyDefaultsToInclude() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        assert childResolver.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+    }
+
+    @Test
+    public void childInheritsDuplicatesStrategyFromParent() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+
+        assert childResolver.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+
+        parentSpec.duplicatesStrategy = 'EXCLUDE'
+        assert childResolver.duplicatesStrategy == DuplicatesStrategy.EXCLUDE
+
+        child.duplicatesStrategy = 'INCLUDE'
+        assert childResolver.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+    }
+
+    @Test
+    public void caseSensitiveFlagDefaultsToTrue() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        assert childResolver.caseSensitive
+        assert childResolver.patternSet.caseSensitive
+    }
+
+    @Test
+    public void childUsesCaseSensitiveFlagFromParentAsDefault() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+
+        assert childResolver.caseSensitive
+        assert childResolver.patternSet.caseSensitive
+
+        parentSpec.caseSensitive = false
+        assert !childResolver.caseSensitive
+        assert !childResolver.patternSet.caseSensitive
+
+        child.caseSensitive = true
+        assert childResolver.caseSensitive
+        assert childResolver.patternSet.caseSensitive
+    }
+
+    @Test
+    public void includeEmptyDirsFlagDefaultsToTrue() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        assert childResolver.includeEmptyDirs
+
+    }
+
+    @Test
+    public void childUsesIncludeEmptyDirsFlagFromParentAsDefault() {
+
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+
+        assert childResolver.includeEmptyDirs
+
+        parentSpec.includeEmptyDirs = false
+        assert !childResolver.includeEmptyDirs
+
+        child.includeEmptyDirs = true
+        assert childResolver.includeEmptyDirs
+    }
+
+
+    @Test
+    public void testSpecInheritsActionsFromParent() {
+        Action parentAction = context.mock(Action, 'parent')
+        parentSpec.eachFile parentAction
+
+        Action childAction = context.mock(Action, 'child')
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        child.eachFile childAction
+
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+
+        assertThat(childResolver.allCopyActions, equalTo([parentAction, childAction]))
+    }
+
+    @Test
+    public void testHasNoPermissionsByDefault() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        assert childResolver.fileMode == null
+        assert childResolver.dirMode == null
+    }
+
+    @Test
+    public void testInheritsPermissionsFromParent() {
+        DefaultCopySpec child = new DefaultCopySpec(fileResolver, instantiator)
+        DefaultCopySpec.DefaultCopySpecResolver childResolver = child.buildResolverRelativeToParent(parentSpec.buildRootResolver())
+        parentSpec.fileMode = 0x1
+        parentSpec.dirMode = 0x2
+
+        Assert.assertEquals(0x1, childResolver.fileMode)
+        Assert.assertEquals(0x2, childResolver.dirMode)
+
+        child.fileMode = 0x3
+        child.dirMode = 0x4
+
+        Assert.assertEquals(0x3, childResolver.fileMode)
+        Assert.assertEquals(0x4, childResolver.dirMode)
+    }
+
+    @Test
+    public void canWalkDownTreeCreatedUsingFromIntegrationTest() {
+
+        CopySpec child = parentSpec.from('somedir') { into 'child' }
+        child.from('somedir') { into 'grandchild' }
+        child.from('somedir') { into '/grandchild' }
+
+        List<RelativePath> paths = new ArrayList<RelativePath>()
+        parentSpec.buildRootResolver().walk(new Action<CopySpecResolver>() {
+            void execute(CopySpecResolver csr) {
+                paths.add(csr.destPath)
+            }
+        })
+
+        assertThat(paths.size(), equalTo(4))
+        assertThat(paths.get(0), equalTo(new RelativePath(false)))
+        assertThat(paths.get(1), equalTo(new RelativePath(false, 'child')))
+        assertThat(paths.get(2), equalTo(new RelativePath(false, 'child', 'grandchild')))
+        assertThat(paths.get(3), equalTo(new RelativePath(false, 'grandchild')))
+    }
+
+    @Test
+    public void canWalkDownTreeCreatedUsingWithIntegrationTest() {
+
+        DefaultCopySpec childOne = new DefaultCopySpec(fileResolver, instantiator)
+        childOne.into("child_one");
+        parentSpec.with(childOne);
+
+        DefaultCopySpec childTwo = new DefaultCopySpec(fileResolver, instantiator)
+         childTwo.into("child_two");
+        parentSpec.with( childTwo);
+
+        DefaultCopySpec grandchild = new DefaultCopySpec(fileResolver, instantiator)
+        grandchild.into("grandchild");
+        childOne.with(grandchild);
+         childTwo.with(grandchild);
+
+        List<RelativePath> paths = new ArrayList<RelativePath>()
+        parentSpec.buildRootResolver().walk(new Action<CopySpecResolver>() {
+            void execute(CopySpecResolver csr) {
+                paths.add(csr.destPath)
+            }
+        })
+
+        assertThat(paths.size(), equalTo(5))
+        assertThat(paths.get(0), equalTo(new RelativePath(false)))
+        assertThat(paths.get(1), equalTo(new RelativePath(false, 'child_one')))
+        assertThat(paths.get(2), equalTo(new RelativePath(false, 'child_one', 'grandchild')))
+        assertThat(paths.get(3), equalTo(new RelativePath(false, 'child_two')))
+        assertThat(paths.get(4), equalTo(new RelativePath(false, 'child_two', 'grandchild')))
+    }
+
+
+}
+
+
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
index b956679..3b0e10a 100644
--- 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
@@ -20,11 +20,9 @@ import org.apache.tools.ant.filters.StripJavaComments
 import org.gradle.api.Action
 import org.gradle.api.file.CopySpec
 import org.gradle.api.file.DuplicatesStrategy
-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.internal.Actions
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
@@ -32,7 +30,6 @@ 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.Assert
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,7 +45,7 @@ public class DefaultCopySpecTest {
     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 Instantiator instantiator = DirectInstantiator.INSTANCE
     private final DefaultCopySpec spec = new DefaultCopySpec(fileResolver, instantiator)
 
     private List<String> getTestSourceFileNames() {
@@ -93,23 +90,23 @@ public class DefaultCopySpecTest {
 
     @Test
     public void testDefaultDestinationPathForRootSpec() {
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+        assertThat(spec.buildRootResolver().destPath, equalTo(new RelativePath(false)))
     }
 
     @Test
     public void testInto() {
         spec.into 'spec'
-        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+        assertThat(spec.buildRootResolver().destPath, equalTo(new RelativePath(false, 'spec')))
         spec.into '/'
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
+        assertThat(spec.buildRootResolver().destPath, equalTo(new RelativePath(false)))
     }
 
     @Test
     public void testIntoWithAClosure() {
         spec.into { 'spec' }
-        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+        assertThat(spec.buildRootResolver().destPath, equalTo(new RelativePath(false, 'spec')))
         spec.into { return { 'spec' } }
-        assertThat(spec.destPath, equalTo(new RelativePath(false, 'spec')))
+        assertThat(spec.buildRootResolver().destPath, equalTo(new RelativePath(false, 'spec')))
     }
 
     @Test
@@ -123,171 +120,30 @@ public class DefaultCopySpecTest {
     }
 
     @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() {
         CopySpec child = spec.into('target') {
         }
 
         assertThat(child, not(sameInstance(spec as CopySpec)))
-        assertThat(unpackWrapper(child).destPath, equalTo(new RelativePath(false, 'target')))
-    }
-
-    @Test
-    public void testRootSpecHasRootPathAsDestination() {
-        assertThat(spec.destPath, equalTo(new RelativePath(false)))
-    }
-
-    @Test
-    public void testChildSpecResolvesIntoArgRelativeToParentDestinationDir() {
-        CopySpec child = spec.from('somedir') { into 'child' }
-        assertThat(unpackWrapper(child).destPath, equalTo(new RelativePath(false, 'child')))
-
-        CopySpec grandchild = child.from('somedir') { into 'grandchild' }
-        assertThat(unpackWrapper(grandchild).destPath, equalTo(new RelativePath(false, 'child', 'grandchild')))
-
-        grandchild.into '/grandchild'
-        assertThat(unpackWrapper(grandchild).destPath, equalTo(new RelativePath(false, 'grandchild')))
-    }
-
-    @Test
-    public void testChildSpecUsesParentDestinationPathAsDefault() {
-        CopySpec child = spec.from('somedir') {}
-        assertThat(unpackWrapper(child).destPath, equalTo(spec.destPath))
-
-        child.into 'child'
-
-        CopySpec grandchild = child.from('somedir') {}
-        assertThat(unpackWrapper(grandchild).destPath, equalTo(unpackWrapper(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() {
-        CopySpec 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 = unpackWrapper(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() {
-        CopySpec 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 = unpackWrapper(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') {}
-        def realChild = (child as CopySpecWrapper).delegate as DefaultCopySpec
-
-        assert child.caseSensitive
-        assert realChild.patternSet.caseSensitive
-
-        spec.caseSensitive = false
-        assert !child.caseSensitive
-        assert !realChild.patternSet.caseSensitive
-
-        child.caseSensitive = true
-        assert child.caseSensitive
-        assert realChild.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
+        assertThat(unpackWrapper(child).buildRootResolver().destPath, equalTo(new RelativePath(false, 'target')))
     }
 
     @Test
     public void testNoArgFilter() {
         spec.filter(StripJavaComments)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.copyActions.size(), equalTo(1))
     }
 
     @Test
     public void testArgFilter() {
         spec.filter(HeadFilter, lines: 15, skip: 2)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.copyActions.size(), equalTo(1))
     }
 
     @Test
     public void testExpand() {
         spec.expand(version: '1.2', skip: 2)
-        assertThat(spec.allCopyActions.size(), equalTo(1))
+        assertThat(spec.copyActions.size(), equalTo(1))
     }
 
     @Test
@@ -295,33 +151,33 @@ public class DefaultCopySpecTest {
         spec.filter(StripJavaComments)
         spec.filter(HeadFilter, lines: 15, skip: 2)
 
-        assertThat(spec.allCopyActions.size(), equalTo(2))
+        assertThat(spec.copyActions.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))
+        assertThat(spec.copyActions.size(), equalTo(1))
+        assertThat(spec.copyActions[0], instanceOf(RenamingCopyAction))
+        assertThat(spec.copyActions[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))
+        assertThat(spec.copyActions.size(), equalTo(1))
+        assertThat(spec.copyActions[0], instanceOf(RenamingCopyAction))
+        assertThat(spec.copyActions[0].transformer, instanceOf(RegExpNameMapper))
     }
 
     @Test
     public void testAddsClosureToActions() {
         spec.rename {}
 
-        assertThat(spec.allCopyActions.size(), equalTo(1))
-        assertThat(spec.allCopyActions[0], instanceOf(RenamingCopyAction))
+        assertThat(spec.copyActions.size(), equalTo(1))
+        assertThat(spec.copyActions[0], instanceOf(RenamingCopyAction))
     }
 
     @Test
@@ -329,7 +185,7 @@ public class DefaultCopySpecTest {
         def action = context.mock(Action)
         spec.eachFile(action)
 
-        assertThat(spec.allCopyActions, equalTo([action]))
+        assertThat(spec.copyActions, equalTo([action]))
     }
 
     @Test
@@ -337,37 +193,9 @@ public class DefaultCopySpecTest {
         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
-        CopySpec childSpec = spec.from('src') {
-            eachFile childAction
-        }
-
-        assertThat(unpackWrapper(childSpec).allCopyActions, equalTo([parentAction, childAction]))
-    }
-
-    @Test
-    public void testHasNoPermissionsByDefault() {
-        assert spec.fileMode == null
-        assert spec.dirMode == null
+        assertThat(spec.copyActions.size(), equalTo(1))
     }
 
-    @Test
-    public void testInheritsPermissionsFromParent() {
-        spec.fileMode = 0x1
-        spec.dirMode = 0x2
-
-        CopySpec child = spec.from('src') {}
-        Assert.assertEquals(0x1, child.fileMode)
-        Assert.assertEquals(0x2, child.dirMode)
-    }
 
     @Test
     public void testHasNoSourceByDefault() {
@@ -386,31 +214,14 @@ public class DefaultCopySpecTest {
         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))
+        assertEquals(1, spec.copyActions.size())
+        assertThat(spec.copyActions[0], instanceOf(MatchingCopyAction))
 
-        Spec<RelativePath> matchSpec = spec.allCopyActions[0].matchSpec
+        Spec<RelativePath> matchSpec = spec.copyActions[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')))
@@ -422,10 +233,10 @@ public class DefaultCopySpecTest {
     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))
+        assertEquals(1, spec.copyActions.size())
+        assertThat(spec.copyActions[0], instanceOf(MatchingCopyAction))
 
-        Spec<RelativePath> matchSpec = spec.allCopyActions[0].matchSpec
+        Spec<RelativePath> matchSpec = spec.copyActions[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')))
@@ -498,6 +309,77 @@ public class DefaultCopySpecTest {
         assert spec.childSpecs[2] == child3
     }
 
+    @Test
+    void "properties accessed directly have defaults"() {
+
+        assert spec.caseSensitive == true;
+        assert spec.getIncludeEmptyDirs() == true;
+        assert spec.duplicatesStrategy == DuplicatesStrategy.INCLUDE
+        assert spec.fileMode == null
+        assert spec.dirMode == null
+
+        spec.caseSensitive = false
+        spec.includeEmptyDirs = false
+        spec.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+        spec.fileMode = 1
+        spec.dirMode = 2
+
+        assert spec.caseSensitive == false;
+        assert spec.getIncludeEmptyDirs() == false;
+        assert spec.duplicatesStrategy == DuplicatesStrategy.EXCLUDE
+        assert spec.fileMode == 1
+        assert spec.dirMode == 2
+
+
+    }
+
+    @Test
+    void "properties accessed directly on specs created using from inherit from parents"() {
+
+        //set non defaults on root
+        spec.caseSensitive = false
+        spec.includeEmptyDirs = false
+        spec.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+        spec.fileMode = 1
+        spec.dirMode = 2
+
+        DefaultCopySpec child = unpackWrapper(spec.from("child") {
+
+        })
+
+        //children still have defaults
+        assert child.caseSensitive == false;
+        assert child.getIncludeEmptyDirs() == false;
+        assert child.duplicatesStrategy == DuplicatesStrategy.EXCLUDE
+        assert child.fileMode == 1
+        assert child.dirMode == 2
+
+    }
+
+    @Test
+    void "properties accessed directly on specs created using into inherit from parents"() {
+
+        //set non defaults on root
+        spec.caseSensitive = false
+        spec.includeEmptyDirs = false
+        spec.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+        spec.fileMode = 1
+        spec.dirMode = 2
+
+        DefaultCopySpec child = unpackWrapper(spec.into("child") {
+
+        })
+
+        //children still have defaults
+        assert child.caseSensitive == false;
+        assert child.getIncludeEmptyDirs() == false;
+        assert child.duplicatesStrategy == DuplicatesStrategy.EXCLUDE
+        assert child.fileMode == 1
+        assert child.dirMode == 2
+
+    }
+
+
     DefaultCopySpec unpackWrapper(CopySpec copySpec) {
         (copySpec as CopySpecWrapper).delegate as DefaultCopySpec
     }
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
deleted file mode 100644
index 7fbb8c7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecoratorTest.groovy
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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/DuplicateHandlingCopyActionExecutorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionExecutorTest.groovy
new file mode 100644
index 0000000..579102c
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionExecutorTest.groovy
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.nativeintegration.filesystem.FileSystem
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestOutputEventListener
+import org.junit.Rule
+import spock.lang.Shared
+import spock.lang.Specification
+
+class DuplicateHandlingCopyActionExecutorTest 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 outputEventListener = new TestOutputEventListener()
+    @Rule ConfigureLogging logging = new ConfigureLogging(outputEventListener)
+
+    @Shared Instantiator instantiator = ThreadGlobalInstantiator.getOrCreate()
+    def executer = new CopyActionExecuter(instantiator, fileSystem)
+    def copySpec = Mock(MyCopySpec) {
+        getChildren() >> []
+    }
+    def copySpecResolver = Mock(CopySpecResolver)
+
+    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 {}
+        copySpecResolver.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 {}
+        copySpecResolver.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 {}
+        copySpecResolver.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' })
+        outputEventListener.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' })
+        outputEventListener.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) {
+        copySpecResolver.destPath >> new RelativePath(false, '/root')
+        def fileTree = Mock(FileTree)
+        copySpecResolver.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(copySpecResolver) }
+    }
+
+    void actions(Closure... actions) {
+        copySpecResolver.allCopyActions >> actions.collect { new ClosureBackedAction<>(it) }
+    }
+
+    void visit() {
+        executer.execute(copySpec, delegate)
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
index ddd94ed..15423c1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/LineFilterTest.groovy
@@ -78,6 +78,6 @@ class LineFilterTest {
     }
 
     private String lines(String ... lines) {
-        (lines as List).join(SystemProperties.lineSeparator)
+        (lines as List).join(SystemProperties.instance.lineSeparator)
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationConverterTest.groovy
new file mode 100644
index 0000000..1371084
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationConverterTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * 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.file.copy
+
+import org.gradle.internal.typeconversion.NotationParser
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+
+import java.util.concurrent.Callable
+import org.gradle.api.internal.artifacts.DefaultExcludeRule
+import spock.lang.Specification
+
+class PathNotationConverterTest extends Specification {
+    NotationParser<Object, String> pathNotationParser = PathNotationConverter.parser();
+
+    def "with null"() {
+        expect:
+        null == pathNotationParser.parseNotation(null);
+    }
+
+    def "with CharSequence"() {
+        expect:
+        pathToParse == pathNotationParser.parseNotation(pathToParse);
+        where:
+        pathToParse << ["this/is/a/path", 'this/is/a/path', "this/is/a/${'path'}"]
+    }
+
+    def "with File"() {
+        def file = new File("a.txt")
+
+        expect:
+        pathNotationParser.parseNotation(file) == file.path
+    }
+
+    def "with Number"() {
+        expect:
+        expected == pathNotationParser.parseNotation(input);
+        where:
+        input << [1, 1.5, -1]
+        expected << ["1", "1.5", "-1"]
+    }
+
+    def "with Boolean"() {
+        expect:
+        expected == pathNotationParser.parseNotation(input);
+        where:
+        input << [true, false, Boolean.TRUE, Boolean.FALSE]
+        expected << ["true", "false", "true", "false"]
+    }
+
+    def "with unsupported class"() {
+        def customObj = new DefaultExcludeRule();
+
+        when:
+        pathNotationParser.parseNotation(customObj);
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "with closure "() {
+        expect:
+        "closure/path" == pathNotationParser.parseNotation({ "closure/path"});
+    }
+
+    def "with double nested closure "() {
+        expect:
+        "closure/path" == pathNotationParser.parseNotation({ "closure/path" });
+    }
+
+    def "with closure with null return value"() {
+        expect:
+        null == pathNotationParser.parseNotation({ null });
+    }
+
+    def "with closure of unsupported return value"() {
+        def customObj = new DefaultExcludeRule()
+
+        when:
+        pathNotationParser.parseNotation({ customObj });
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "with Callable that throws exception"() {
+        Callable callable = new Callable<String>() {
+            String call() {
+                return "callable/path";
+            }
+        };
+        expect:
+        "callable/path" == pathNotationParser.parseNotation(callable);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationParserTest.groovy
deleted file mode 100644
index 3b4010a..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/copy/PathNotationParserTest.groovy
+++ /dev/null
@@ -1,90 +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.file.copy
-
-import java.util.concurrent.Callable
-import org.gradle.api.internal.artifacts.DefaultExcludeRule
-import spock.lang.Specification
-
-class PathNotationParserTest extends Specification {
-    PathNotationParser<String> pathNotationParser = new PathNotationParser<String>();
-
-    def "with null"() {
-        expect:
-        null == pathNotationParser.parseNotation(null);
-    }
-
-    def "with CharSequence"() {
-        expect:
-        pathToParse == pathNotationParser.parseNotation(pathToParse);
-        where:
-        pathToParse << ["this/is/a/path", 'this/is/a/path', "this/is/a/${'path'}"]
-    }
-
-    def "with Number"() {
-        expect:
-        expected == pathNotationParser.parseNotation(input);
-        where:
-        input << [1, 1.5, -1]
-        expected << ["1", "1.5", "-1"]
-    }
-
-    def "with Boolean"() {
-        expect:
-        expected == pathNotationParser.parseNotation(input);
-        where:
-        input << [true, false, Boolean.TRUE, Boolean.FALSE]
-        expected << ["true", "false", "true", "false"]
-    }
-
-    def "with unsupported class"() {
-        def customObj = new DefaultExcludeRule();
-        expect:
-        customObj.toString() == pathNotationParser.parseNotation(customObj);
-    }
-
-    def "with closure "() {
-        expect:
-        "closure/path" == pathNotationParser.parseNotation({ "closure/path"});
-    }
-
-    def "with double nested closure "() {
-        expect:
-        "closure/path" == pathNotationParser.parseNotation({ "closure/path" });
-    }
-
-    def "with closure with null return value"() {
-        expect:
-        null == pathNotationParser.parseNotation({ null });
-    }
-
-    def "with closure of unsupported return value"() {
-        def customObj = new DefaultExcludeRule()
-        expect:
-        customObj.toString() == pathNotationParser.parseNotation({ customObj });
-    }
-
-    def "with Callable that throws exception"() {
-        Callable callable = new Callable<String>() {
-            String call() {
-                return "callable/path";
-            }
-        };
-        expect:
-        "callable/path" == pathNotationParser.parseNotation(callable);
-    }
-}
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
index 7844dbb..7b7a14c 100644
--- 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
@@ -25,7 +25,7 @@ class SyncCopyActionDecoratorTest extends WorkspaceTest {
     FileCopier copier
 
     def setup() {
-        copier = new FileCopier(new DirectInstantiator(), TestFiles.resolver(testDirectory), TestFiles.fileLookup())
+        copier = new FileCopier(DirectInstantiator.INSTANCE, TestFiles.resolver(testDirectory), TestFiles.fileLookup())
     }
 
     void deletesExtraFilesFromDestinationDirectoryAtTheEndOfVisit() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathKeyFileStoreTest.groovy
deleted file mode 100644
index 3dfbc15..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathKeyFileStoreTest.groovy
+++ /dev/null
@@ -1,198 +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.api.Action
-import org.gradle.api.GradleException
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class PathKeyFileStoreTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
-    TestFile fsBase
-    PathKeyFileStore store
-
-    def pathCounter = 0
-
-    def setup() {
-        fsBase = dir.file("fs")
-        store = new PathKeyFileStore(fsBase)
-    }
-
-    def "move vs copy"() {
-        given:
-        createFile("a", "a").exists()
-        createFile("b", "b").exists()
-
-        when:
-        store.move("a", dir.file("a"))
-        store.copy("b", dir.file("b"))
-
-        then:
-        !dir.file("a").exists()
-        dir.file("b").exists()
-    }
-
-    def "can move to filestore"() {
-        when:
-        store.move("a", createFile("abc"))
-        store.move("b", createFile("def"))
-
-        then:
-        fsBase.file("a").text == "abc"
-        fsBase.file("b").text == "def"
-    }
-
-    def "can add to filestore"() {
-        when:
-        store.add("a", { File f -> f.text = "abc"} as Action<File>)
-        store.add("b", { File f -> f.text = "def"} as Action<File>)
-        then:
-        fsBase.file("a").text == "abc"
-        fsBase.file("b").text == "def"
-    }
-
-    def "add throws GradleException if exception in action occured"() {
-        when:
-        store.add("a", { File f -> throw new Exception("TestException")} as Action<File>)
-        then:
-        thrown(GradleException)
-        !fsBase.file("a").exists()
-        !fsBase.file("a.fslock").exists()
-    }
-
-    def "can get from filestore"() {
-        when:
-        createFile("abc", "fs/a")
-        then:
-        store.get("a") != null
-        store.get("b") == null
-    }
-
-    def "get cleans up filestore"() {
-        when:
-        createFile("abc", "fs/a").exists()
-        createFile("lock", "fs/a.fslck").exists()
-        then:
-        store.get("a") == null
-        store.get("a.fslock") == null
-    }
-
-    def "can overwrite stale files "() {
-        given:
-        createFile("abc", "fs/a").exists()
-        createFile("lock", "fs/a.fslck").exists()
-        when:
-        store.add("a", { File f -> f.text = "def"} as Action<File>)
-        then:
-        store.get("a").file.text == "def"
-    }
-
-    def "get on stale file with marker removes file from filestore"() {
-        when:
-        createFile("abc", "fs/a")
-        createFile("def", "fs/b")
-        then:
-        store.get("a").file.text == "abc"
-        store.get("b").file.text == "def"
-    }
-
-    def "can overwrite entry"() {
-        when:
-        store.move("a", createFile("abc"))
-        store.move("a", createFile("def"))
-
-        then:
-        fsBase.file("a").text == "def"
-    }
-
-    def "creates intermediary directories"() {
-        when:
-        store.move("a/b/c", createFile("abc"))
-        store.move("a/b/d", createFile("abd"))
-        store.move("a/c/a", createFile("aca"))
-
-        then:
-        fsBase.file("a/b").directory
-        fsBase.file("a/b/c").text == "abc"
-        fsBase.file("a/c/a").text == "aca"
-    }
-
-    def "can search via globs"() {
-        when:
-        store.move("a/a/a", createFile("a"))
-        store.move("a/a/b", createFile("b"))
-        store.move("a/b/a", createFile("c"))
-
-        then:
-        store.search("**/a").size() == 2
-        store.search("*/b/*").size() == 1
-        store.search("a/b/a").size() == 1
-    }
-
-    def "search ignores stale entries with marker file"() {
-        when:
-        store.move("a/a/a", createFile("a"))
-        store.move("a/b/b", createFile("b"))
-        store.move("a/c/c", createFile("c"))
-        createFile("lock", "fs/a/b/b.fslck")
-        def search = store.search("**/*")
-        then:
-        search.size() == 2
-        search.collect {entry -> entry.file.name}.sort() == ["a", "c"]
-    }
-
-    def "move filestore"() {
-        given:
-        def a = store.move("a", createFile("abc"))
-        def b = store.move("b", createFile("def"))
-
-        expect:
-        a.file == dir.file("fs/a")
-        b.file == dir.file("fs/b")
-
-        when:
-        store.moveFilestore(dir.file("new-store"))
-
-        then:
-        store.baseDir == dir.file("new-store")
-
-        and:
-        a.file == dir.file("new-store/a")
-        b.file == dir.file("new-store/b")
-
-        !dir.file("fs").exists()
-    }
-
-    def "can move filestore that doesn't exist yet"() {
-        expect:
-        !store.baseDir.exists()
-
-        when:
-        store.moveFilestore(dir.file("new-filestore"))
-
-        then:
-        notThrown Exception
-    }
-
-    def createFile(String content, String path = "f${pathCounter++}") {
-        dir.createFile(path) << content
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStoreTest.groovy
deleted file mode 100644
index b02bdeb..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/PathNormalisingKeyFileStoreTest.groovy
+++ /dev/null
@@ -1,94 +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.api.Action
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class PathNormalisingKeyFileStoreTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
-    TestFile fsBase
-    PathNormalisingKeyFileStore store
-
-    def pathCounter = 0
-
-    def setup() {
-        fsBase = dir.createDir("fs")
-        store = new PathNormalisingKeyFileStore(new PathKeyFileStore(fsBase))
-    }
-
-    def "can move to filestore"() {
-        when:
-        store.move("!.zip", file("abc"))
-        store.move("  ", file("def"))
-
-        then:
-        fsBase.file("_.zip").text == "abc"
-        fsBase.file("__").text == "def"
-    }
-
-    def "can add to filestore"() {
-        when:
-        store.add("!.zip", {File file -> file.text = "abc"} as Action<File>)
-        store.add("  ", {File file -> file.text = "def"} as Action<File>)
-        then:
-        fsBase.file("_.zip").text == "abc"
-        fsBase.file("__").text == "def"
-    }
-
-    def "can overwrite entry"() {
-        when:
-        store.move("!", file("abc"))
-        store.move(" ", file("def"))
-
-        then:
-        fsBase.file("_").text == "def"
-    }
-
-    def "creates intermediary directories"() {
-        when:
-        store.move("a/!/c", file("abc"))
-        store.move("a/ /d", file("abd"))
-        store.move("a/c/(", file("aca"))
-
-        then:
-        fsBase.file("a/_").directory
-        fsBase.file("a/_/c").text == "abc"
-        fsBase.file("a/c/_").text == "aca"
-    }
-
-    def "can search via globs"() {
-        when:
-        store.move("a/!/a", file("a"))
-        store.move("a/ /b", file("b"))
-        store.move("a/b/&", file("c"))
-
-        then:
-        store.search("**/a").size() == 1
-        store.search("*/ /*").size() == 2
-        store.search("a/b/_").size() == 1
-    }
-
-    def file(String content, String path = "f${pathCounter++}") {
-        dir.createFile(path) << content
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStoreTest.groovy
deleted file mode 100644
index c1ec9cb..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/filestore/UniquePathKeyFileStoreTest.groovy
+++ /dev/null
@@ -1,112 +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.api.Action
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class UniquePathKeyFileStoreTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider();
-    Action<File> action = Mock()
-
-    UniquePathKeyFileStore uniquePathKeyFileStore
-
-    def setup() {
-        uniquePathKeyFileStore = new UniquePathKeyFileStore(temporaryFolder.createDir("fsbase"))
-    }
-
-    def "add executes action if file does not exist"() {
-        def file = temporaryFolder.file("fsbase/a/a");
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.add("a/a", action)
-
-        then:
-        fileInStore.file == file
-        1 * action.execute(file) >> { File f -> f.text = 'hi' }
-    }
-
-    def "add skips action if file already exists"() {
-        setup:
-        def file = temporaryFolder.createFile("fsbase/a/a");
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.add("a/a", action)
-
-        then:
-        fileInStore.file == file
-        0 * action.execute(_)
-    }
-
-    def "copy returns existing file it it already exists"() {
-        setup:
-        def source = temporaryFolder.createFile("some-file")
-        def file = temporaryFolder.createFile("fsbase/a/a");
-        file.text = 'existing content'
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.copy("a/a", source)
-
-        then:
-        fileInStore.file == file
-        file.text == 'existing content'
-    }
-
-    def "copy adds file it it does not exist"() {
-        setup:
-        def source = temporaryFolder.createFile("some-file")
-        def file = temporaryFolder.file("fsbase/a/a");
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.copy("a/a", source)
-
-        then:
-        fileInStore.file == file
-        file.assertIsCopyOf(source)
-    }
-
-    def "move returns existing file it it already exists"() {
-        setup:
-        def source = temporaryFolder.createFile("some-file")
-        def file = temporaryFolder.createFile("fsbase/a/a");
-        file.text = 'existing content'
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.move("a/a", source)
-
-        then:
-        fileInStore.file == file
-        file.text == 'existing content'
-        !source.exists()
-    }
-
-    def "move adds file it it does not exist"() {
-        setup:
-        def source = temporaryFolder.createFile("some-file")
-        def file = temporaryFolder.file("fsbase/a/a");
-
-        when:
-        def fileInStore = uniquePathKeyFileStore.move("a/a", source)
-
-        then:
-        fileInStore.file == file
-        !source.exists()
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/html/SimpleHtmlWriterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/html/SimpleHtmlWriterTest.groovy
deleted file mode 100644
index 9d5218b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/html/SimpleHtmlWriterTest.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.html
-
-import org.jsoup.Jsoup
-import spock.lang.Specification
-
-class SimpleHtmlWriterTest extends Specification {
-
-    def "creates html output"() {
-        given:
-        StringWriter sw = new StringWriter();
-
-        when:
-        SimpleHtmlWriter htmlWriter = new SimpleHtmlWriter(sw)
-        htmlWriter.startElement("html")
-                .startElement("head").endElement()
-                .startElement("body")
-                .startElement("h1").characters("Test Header").endElement()
-                .endElement()
-                .endElement()
-        sw.close()
-
-        then:
-        Jsoup.parse(sw.toString()).select("h1").text() == "Test Header"
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderIdsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderIdsTest.groovy
new file mode 100644
index 0000000..fe815f4
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderIdsTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization
+
+import spock.lang.Specification
+
+import static org.gradle.api.internal.initialization.ClassLoaderIds.buildScript
+import static org.gradle.api.internal.initialization.ClassLoaderIds.testTaskClasspath
+import static org.gradle.util.Matchers.strictlyEquals
+
+class ClassLoaderIdsTest extends Specification {
+
+    def "equality"() {
+        expect:
+        strictlyEquals(buildScript("x", "x"), buildScript("x", "x"))
+        buildScript("x", "x") != buildScript("x", "y")
+        buildScript("x", "x") != buildScript("y", "y")
+
+        testTaskClasspath("x") != buildScript("y", "y")
+        buildScript("y", "y") != testTaskClasspath("x")
+
+        strictlyEquals(testTaskClasspath("x"), testTaskClasspath("x"))
+        testTaskClasspath("x") != testTaskClasspath("y")
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifierTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifierTest.groovy
new file mode 100644
index 0000000..86bbf59
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/ClassLoaderScopeIdentifierTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization
+
+import spock.lang.Specification
+
+class ClassLoaderScopeIdentifierTest extends Specification {
+    private ClassLoaderScopeIdentifier root = new ClassLoaderScopeIdentifier(null, "root")
+
+    def "equality"() {
+        expect:
+        root.localId() == root.localId()
+        root.localId() != root.exportId()
+        root.exportId() != root.localId()
+        root.exportId() == root.exportId()
+    }
+
+    def "creates child"() {
+        def child = root.child(root.name)
+
+        expect:
+        child.localId() == child.localId()
+        child.localId() != child.exportId()
+        child.exportId() != child.localId()
+        child.exportId() == child.exportId()
+    }
+
+    def "children with same name are equivalent"() {
+        def child1 = root.child(root.name)
+        def child2 = root.child(root.name)
+
+        expect:
+        child1.localId() == child2.localId()
+        child1.exportId() == child2.exportId()
+    }
+
+    def "children with different name are not equal"() {
+        def child1 = root.child(root.name)
+        def child2 = root.child(root.name + "changed")
+
+        expect:
+        child1.localId() != child2.localId()
+        child1.exportId() != child2.exportId()
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCacheTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCacheTest.groovy
deleted file mode 100644
index 5df9838..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderCacheTest.groovy
+++ /dev/null
@@ -1,80 +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.initialization
-
-import com.google.common.cache.CacheBuilder
-import org.gradle.internal.classloader.FilteringClassLoader
-import org.gradle.internal.classpath.ClassPath
-import org.gradle.internal.classpath.DefaultClassPath
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class DefaultClassLoaderCacheTest extends Specification {
-
-    def backingCache = CacheBuilder.newBuilder().build()
-    def cache = new DefaultClassLoaderCache(backingCache)
-
-    @Rule TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-
-    TestFile file(String path) {
-        testDirectoryProvider.testDirectory.file(path)
-    }
-
-    ClassPath classPath(String... paths) {
-        new DefaultClassPath(paths.collect { file(it) } as Iterable<File>)
-    }
-
-    ClassLoader classLoader(ClassPath classPath) {
-        new URLClassLoader(classPath.asURLArray)
-    }
-
-    def "class loaders are reused"() {
-        expect:
-        def root = classLoader(classPath("root"))
-        cache.get(root, classPath("c1"), null).is cache.get(root, classPath("c1"), null)
-    }
-
-    def "parents are respected"() {
-        expect:
-        def root1 = classLoader(classPath("root1"))
-        def root2 = classLoader(classPath("root2"))
-        cache.get(root1, classPath("c1"), null) != cache.get(root2, classPath("c1"), null)
-    }
-
-    def "filters are respected"() {
-        expect:
-        def root = classLoader(classPath("root"))
-        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [])
-        def f2 = new FilteringClassLoader.Spec(["2"], [], [], [], [], [])
-        cache.get(root, classPath("c1"), f1).is(cache.get(root, classPath("c1"), f1))
-        !cache.get(root, classPath("c1"), f1).is(cache.get(root, classPath("c1"), f2))
-        backingCache.size() == 3
-    }
-
-    def "non filtered classloaders are reused"() {
-        expect:
-        def root = classLoader(classPath("root"))
-        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [])
-        cache.get(root, classPath("c1"), f1)
-        backingCache.size() == 2
-        cache.get(root, classPath("c1"), null)
-        backingCache.size() == 2
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScopeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScopeTest.groovy
index 2ec75a8..dd2ee3b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScopeTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultClassLoaderScopeTest.groovy
@@ -16,8 +16,9 @@
 
 package org.gradle.api.internal.initialization
 
-import com.google.common.cache.Cache
-import com.google.common.cache.CacheBuilder
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache
+import org.gradle.api.internal.initialization.loadercache.DefaultClassLoaderCache
+import org.gradle.api.internal.initialization.loadercache.FileClassPathSnapshotter
 import org.gradle.internal.classloader.CachingClassLoader
 import org.gradle.internal.classpath.ClassPath
 import org.gradle.internal.classpath.DefaultClassPath
@@ -28,24 +29,21 @@ import spock.lang.Specification
 
 class DefaultClassLoaderScopeTest extends Specification {
 
-    ClassLoader parentClassLoader
-    ClassLoader baseClassLoader
+    ClassLoader rootClassLoader
 
-    ClassLoaderScope parent
-    ClassLoaderScope base
+    ClassLoaderScope root
     ClassLoaderScope scope
 
-    Cache<DefaultClassLoaderCache.Key, ClassLoader> backingCache = CacheBuilder.newBuilder().build();
-    ClassLoaderCache classLoaderCache = new DefaultClassLoaderCache(backingCache);
+    ClassLoaderCache classLoaderCache = new DefaultClassLoaderCache(new FileClassPathSnapshotter())
 
-    @Rule TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
 
     def setup() {
-        parentClassLoader = new URLClassLoader(classPath("root").asURLArray)
-        baseClassLoader = new URLClassLoader(classPath("base").asURLArray)
-        parent = new RootClassLoaderScope(parentClassLoader, classLoaderCache)
-        base = new RootClassLoaderScope(baseClassLoader, classLoaderCache)
-        scope = new DefaultClassLoaderScope(parent, base, classLoaderCache)
+        file("root/root") << "root"
+        rootClassLoader = new URLClassLoader(classPath("root").asURLArray, getClass().classLoader.parent)
+        root = new RootClassLoaderScope(rootClassLoader, rootClassLoader, classLoaderCache)
+        scope = root.createChild("child")
     }
 
     TestFile file(String path) {
@@ -61,70 +59,167 @@ class DefaultClassLoaderScopeTest extends Specification {
         scope.lock()
 
         then:
-        scope.childClassLoader.is parentClassLoader
-        scope.scopeClassLoader.is parentClassLoader
+        scope.localClassLoader.is rootClassLoader
+        scope.exportClassLoader.is rootClassLoader
     }
 
-    def "locked scope with only exports only exports exports"() {
+    def "locked empty scope does not define any classes"() {
         when:
-        file("export/foo") << "bar"
-        def exportClassLoader = scope.export(classPath("export"))
         scope.lock()
 
         then:
-        exportClassLoader.getResource("foo").text == "bar"
-        scope.childClassLoader.is scope.scopeClassLoader
-        scope.scopeClassLoader.getResource("foo").text == "bar"
+        !scope.defines(String)
     }
 
-    def "locked scope with only local exports parent loader to children"() {
+    def "ignores empty classpaths"() {
         when:
-        file("local/foo") << "bar"
-        def localClassLoader = scope.addLocal(classPath("local"))
+        scope.export(classPath())
+        scope.local(classPath())
         scope.lock()
 
         then:
-        localClassLoader.getResource("foo").text == "bar"
-        scope.childClassLoader.is parentClassLoader
-        scope.scopeClassLoader.getResource("foo").text == "bar"
+        scope.localClassLoader.is rootClassLoader
+        scope.exportClassLoader.is rootClassLoader
+    }
+
+    def "locked scope with only exports uses same export and local loader"() {
+        when:
+        file("export/foo") << "foo"
+        scope.export(classPath("export")).lock()
+
+        then:
+        scope.exportClassLoader.getResource("root").text == "root"
+        scope.exportClassLoader.getResource("foo").text == "foo"
+        scope.exportClassLoader instanceof URLClassLoader
+        scope.exportClassLoader.parent.is rootClassLoader
+        scope.localClassLoader.is scope.exportClassLoader
+    }
+
+    def "can export more than one classpath"() {
+        when:
+        file("export/1/foo") << "foo"
+        file("export/2/bar") << "bar"
+        scope.
+                export(classPath("export/1")).
+                export(classPath("export/2")).
+                lock()
+
+        then:
+        scope.exportClassLoader.getResource("root").text == "root"
+        scope.exportClassLoader.getResource("foo").text == "foo"
+        scope.exportClassLoader.getResource("bar").text == "bar"
+        scope.localClassLoader.is scope.exportClassLoader
+    }
+
+    def "locked scope with only local exports parent loader to children"() {
+        when:
+        file("local/1/foo") << "foo"
+        file("local/2/bar") << "bar"
+        scope.
+                local(classPath("local/1")).
+                local(classPath("local/2")).
+                lock()
+
+        then:
+        scope.localClassLoader.getResource("root").text == "root"
+        scope.localClassLoader.getResource("foo").text == "foo"
+        scope.localClassLoader.getResource("bar").text == "bar"
+        scope.localClassLoader != scope.exportClassLoader
+        scope.exportClassLoader.is rootClassLoader
+    }
+
+    def "locked scope with only one local exports parent loader to children and uses loader as local loader"() {
+        when:
+        file("local/foo") << "foo"
+        scope.local(classPath("local")).lock()
+
+        then:
+        scope.localClassLoader.getResource("root").text == "root"
+        scope.localClassLoader.getResource("foo").text == "foo"
+        scope.localClassLoader instanceof URLClassLoader
+        scope.localClassLoader.parent.is rootClassLoader
+        scope.exportClassLoader.is rootClassLoader
     }
 
-    def "locked scope with local and exports exports custom classloader to children"() {
+    def "locked scope with local and exports exports custom ClassLoader to children"() {
         when:
         file("local/local") << "bar"
         file("export/export") << "bar"
-        def localClassLoader = scope.addLocal(classPath("local"))
-        def exportClassLoader = scope.export(classPath("export"))
-        scope.lock()
+        scope.
+                local(classPath("local")).
+                export(classPath("export")).
+                lock()
+
+        then:
+        scope.exportClassLoader.getResource("export").text == "bar"
+        scope.exportClassLoader.getResource("local") == null
+        scope.exportClassLoader instanceof URLClassLoader
+        scope.exportClassLoader.parent == rootClassLoader
+
+        scope.localClassLoader instanceof CachingClassLoader
+        scope.localClassLoader.getResource("export").text == "bar"
+        scope.localClassLoader.getResource("local").text == "bar"
+    }
+
+    def "locked scope with local and exports defines local and exported classes"() {
+        when:
+        copyTo(TestClass1, file("local"))
+        copyTo(TestClass2, file("export"))
+        scope.
+                local(classPath("local")).
+                export(classPath("export")).
+                lock()
 
         then:
-        localClassLoader.getResource("local").text == "bar"
-        exportClassLoader.getResource("export").text == "bar"
-        scope.childClassLoader.getResource("export").text == "bar"
-        scope.childClassLoader.getResource("local") == null
-        scope.scopeClassLoader instanceof CachingClassLoader
-        scope.scopeClassLoader.getResource("export").text == "bar"
-        scope.scopeClassLoader.getResource("local").text == "bar"
+        def local = scope.localClassLoader.loadClass(TestClass1.name)
+        def exported = scope.exportClassLoader.loadClass(TestClass2.name)
+        scope.defines(local)
+        scope.defines(exported)
+        !scope.defines(TestClass1)
+        !scope.defines(TestClass2)
     }
 
     def "requesting loaders before locking creates pessimistic setup"() {
         given:
-        scope.scopeClassLoader // trigger
+        scope.localClassLoader // trigger
 
         when:
         file("local/local") << "bar"
         file("export/export") << "bar"
-        def localClassLoader = scope.addLocal(classPath("local"))
-        def exportClassLoader = scope.export(classPath("export"))
+        scope.
+                local(classPath("local")).
+                export(classPath("export"))
 
         then:
-        localClassLoader.getResource("local").text == "bar"
-        exportClassLoader.getResource("export").text == "bar"
-        scope.childClassLoader.getResource("export").text == "bar"
-        scope.childClassLoader.getResource("local") == null
-        scope.scopeClassLoader instanceof CachingClassLoader
-        scope.scopeClassLoader.getResource("export").text == "bar"
-        scope.scopeClassLoader.getResource("local").text == "bar"
+        scope.exportClassLoader instanceof CachingClassLoader
+        scope.exportClassLoader.getResource("root").text == "root"
+        scope.exportClassLoader.getResource("export").text == "bar"
+        scope.exportClassLoader.getResource("local") == null
+
+        scope.localClassLoader instanceof CachingClassLoader
+        scope.localClassLoader.getResource("root").text == "root"
+        scope.localClassLoader.getResource("export").text == "bar"
+        scope.localClassLoader.getResource("local").text == "bar"
+    }
+
+    def "pessimistic scope with local and exports defines local and exported classes"() {
+        given:
+        scope.localClassLoader // trigger
+
+        when:
+        copyTo(TestClass1, file("local"))
+        copyTo(TestClass2, file("export"))
+        scope.
+                local(classPath("local")).
+                export(classPath("export"))
+
+        then:
+        def local = scope.localClassLoader.loadClass(TestClass1.name)
+        def exported = scope.exportClassLoader.loadClass(TestClass2.name)
+        scope.defines(local)
+        scope.defines(exported)
+        !scope.defines(TestClass1)
+        !scope.defines(TestClass2)
     }
 
     def "cannot modify after locking"() {
@@ -132,7 +227,7 @@ class DefaultClassLoaderScopeTest extends Specification {
         scope.lock()
 
         when:
-        scope.addLocal(classPath("local"))
+        scope.local(classPath("local"))
 
         then:
         thrown IllegalStateException
@@ -148,94 +243,96 @@ class DefaultClassLoaderScopeTest extends Specification {
         when:
         file("local/local") << "bar"
         file("export/export") << "bar"
-        scope.addLocal(classPath("local"))
+        scope.local(classPath("local"))
         scope.export(classPath("export"))
-        scope.lock()
-        def child = scope.createChild()
-        child.lock()
+        def child = scope.lock().createChild("child").lock()
 
         then:
-        child.scopeClassLoader.getResource("export").text == "bar"
-        child.scopeClassLoader.getResource("local") == null
-        child.childClassLoader.getResource("export").text == "bar"
-        child.childClassLoader.getResource("local") == null
+        child.localClassLoader.getResource("root").text == "root"
+        child.localClassLoader.getResource("export").text == "bar"
+        child.localClassLoader.getResource("local") == null
+        child.exportClassLoader.getResource("root").text == "root"
+        child.exportClassLoader.getResource("export").text == "bar"
+        child.exportClassLoader.getResource("local") == null
     }
 
-    def "sibling scopes can not access exported or local"() {
+    def "class loaders are reused"() {
+        given:
+        def c1 = classPath("c1")
+        def c2 = classPath("c2")
+
         when:
-        file("local/local") << "bar"
-        file("export/export") << "bar"
-        scope.addLocal(classPath("local"))
-        scope.export(classPath("export"))
-        scope.lock()
-        def sibling = scope.createSibling()
-        sibling.lock()
+        def scope1 = root.createChild("child1").export(c1).local(c2).lock()
+        scope1.exportClassLoader
+
+        def scope2 = root.createChild("child2").export(c1).local(c2).lock()
+        scope2.exportClassLoader
 
         then:
-        sibling.scopeClassLoader.getResource("export") == null
-        sibling.scopeClassLoader.getResource("local") == null
-        sibling.childClassLoader.getResource("export") == null
-        sibling.childClassLoader.getResource("local") == null
-    }
+        scope1.exportClassLoader.is scope2.exportClassLoader
 
-    def "rebased children local additions can access parent exported"() {
         when:
-        file("local/local") << "bar"
-        file("export/export") << "bar"
-        scope.addLocal(classPath("local"))
-        scope.export(classPath("export"))
-        scope.lock()
-        def child = scope.createRebasedChild()
-        file("childLocal/childLocal") << "bar"
-        def childLocalClassLoader = child.addLocal(classPath("childLocal"))
-        child.lock()
+        def child = scope1.createChild("child").export(c1).local(c2).lock()
+        child.exportClassLoader
 
         then:
-        childLocalClassLoader.getResource("local") == null
-        childLocalClassLoader.getResource("export").text == "bar"
-        childLocalClassLoader.getResource("childLocal").text == "bar"
+        child.exportClassLoader != scope.exportClassLoader // classpath is the same, but root is different
     }
 
-    def "class loaders are reused"() {
+    def "pessimistic structure has parent visibility"() {
         expect:
-        backingCache.size() == 0
+        scope.localClassLoader.getResource("root").text == "root"
+    }
+
+    def "optimised structure has parent visibility"() {
+        expect:
+        scope.lock().localClassLoader.getResource("root").text == "root"
+    }
+
+    def "manages cache"() {
+        expect:
+        classLoaderCache.size() == 0
+
+        file("c1/f") << "c1"
+        file("c2/f") << "c2"
+        def c1 = classPath("c1")
+        def c2 = classPath("c2")
 
         when:
-        file("c1/c1") << "bar"
-        file("c2/c2") << "bar"
-        def c1ExportLoader = scope.export(classPath("c1"))
-        def c2Local = scope.addLocal(classPath("c2"))
-        scope.lock()
+        root.createChild("c").local(c1).export(c2).lock().exportClassLoader
+
+        then:
+        classLoaderCache.size() == 2
 
-        def sibling = scope.createSibling()
-        def child = scope.createChild()
+        when:
+        root.createChild("d").local(c1).export(c2).lock().exportClassLoader
 
         then:
-        backingCache.size() == 2
-        sibling.export(classPath("c1")).is c1ExportLoader
-        backingCache.size() == 2
+        classLoaderCache.size() == 2
 
-        !child.export(classPath("c1")).is(c1ExportLoader) // classpath is the same, but parent is different
-        backingCache.size() == 3
+        when:
+        root.createChild("c").local(c1).lock().exportClassLoader
 
-        sibling.addLocal(classPath("c2")).is c2Local
-        child.addLocal(classPath("c2")).is c2Local
-        backingCache.size() == 3
-    }
+        then:
+        classLoaderCache.size() == 2
 
-    def "pessimistic structure has parent visibility"() {
         when:
-        file("root/root") << "foo"
+        root.createChild("d").lock().exportClassLoader
 
         then:
-        scope.scopeClassLoader.getResource("root").text == "foo"
-    }
+        classLoaderCache.size() == 1
 
-    def "optimised structure has parent visibility"() {
         when:
-        file("root/root") << "foo"
+        root.createChild("c").lock().exportClassLoader
 
         then:
-        scope.lock().scopeClassLoader.getResource("root").text == "foo"
+        classLoaderCache.size() == 0
+    }
+
+    void copyTo(Class<?> clazz, TestFile destDir) {
+        def fileName = clazz.name.replace('.', '/') + ".class"
+        def dest = destDir.file(fileName)
+        def classFile = clazz.classLoader.getResource(fileName)
+        dest.copyFrom(classFile)
     }
 }
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 ee7748a..b35df5b 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
@@ -15,11 +15,11 @@
  */
 package org.gradle.api.internal.initialization
 
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.RepositoryHandler
 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.ConfigurationInternal
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.groovy.scripts.ScriptSource
@@ -28,10 +28,10 @@ import spock.lang.Specification
 class DefaultScriptHandlerFactoryTest extends Specification {
     private final DependencyMetaDataProvider metaDataProvider = Mock()
     private final ClassLoaderScope parentScope = Stub() {
-        getScopeClassLoader() >> Stub(ClassLoader)
+        getLocalClassLoader() >> Stub(ClassLoader)
     }
     private final RepositoryHandler repositoryHandler = Mock()
-    private final ConfigurationContainerInternal configurationContainer = Mock()
+    private final ConfigurationContainer configurationContainer = Mock()
     private final FileResolver fileResolver = Mock()
     private final DependencyManagementServices dependencyManagementServices = Mock()
     private final DefaultScriptHandlerFactory scriptHandlerFactory = new DefaultScriptHandlerFactory(dependencyManagementServices, fileResolver, metaDataProvider)
@@ -82,7 +82,7 @@ class DefaultScriptHandlerFactoryTest extends Specification {
         _ * dependencyManagementServices.create(fileResolver, metaDataProvider, _, _) >> dependencyResolutionServices
         _ * dependencyResolutionServices.resolveRepositoryHandler >> repositoryHandler
         _ * dependencyResolutionServices.configurationContainer >> configurationContainer
-        _ * configurationContainer.create(_) >> Stub(ConfigurationInternal)
+        _ * configurationContainer.create(_) >> Stub(Configuration)
     }
 
     private def scriptSource(String className = 'script') {
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 5334414..9e93232 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
@@ -31,7 +31,7 @@ class DefaultScriptHandlerTest extends Specification {
     def scriptSource = Stub(ScriptSource)
     def baseClassLoader = new ClassLoader() {}
     def classLoaderScope = Stub(ClassLoaderScope) {
-        getScopeClassLoader() >> baseClassLoader
+        getLocalClassLoader() >> baseClassLoader
     }
 
     def "adds classpath configuration"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass1.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass1.java
new file mode 100644
index 0000000..84a4ea9
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass1.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization;
+
+public class TestClass1 {
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass2.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass2.java
new file mode 100644
index 0000000..d0d6dc1
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/TestClass2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization;
+
+public class TestClass2 {
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCacheTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCacheTest.groovy
new file mode 100644
index 0000000..d200847
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DefaultClassLoaderCacheTest.groovy
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache
+
+import org.gradle.internal.classloader.FilteringClassLoader
+import org.gradle.internal.classpath.ClassPath
+import org.gradle.internal.classpath.DefaultClassPath
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultClassLoaderCacheTest extends Specification {
+
+    def cache = new DefaultClassLoaderCache(new FileClassPathSnapshotter())
+    def id1 = new ClassLoaderId() {}
+    def id2 = new ClassLoaderId() {}
+
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+
+    TestFile file(String path) {
+        testDirectoryProvider.testDirectory.file(path)
+    }
+
+    ClassPath classPath(String... paths) {
+        new DefaultClassPath(paths.collect { file(it) } as Iterable<File>)
+    }
+
+    ClassLoader classLoader(ClassPath classPath) {
+        new URLClassLoader(classPath.asURLArray)
+    }
+
+    def "class loaders are reused when parent and class path are the same"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        cache.get(id1, classPath("c1"), root, null) == cache.get(id1, classPath("c1"), root, null)
+        cache.get(id1, classPath("c1"), root, null) != cache.get(id1, classPath("c1", "c2"), root, null)
+    }
+
+    def "class loaders with different ids are reused"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        cache.get(id1, classPath("c1"), root, null).is cache.get(id2, classPath("c1"), root, null)
+    }
+
+    def "parents are respected"() {
+        expect:
+        def root1 = classLoader(classPath("root1"))
+        def root2 = classLoader(classPath("root2"))
+        cache.get(id1, classPath("c1"), root1, null) != cache.get(id2, classPath("c1"), root2, null)
+    }
+
+    def "null parents are respected"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        cache.get(id1, classPath("c1"), null, null) == cache.get(id1, classPath("c1"), null, null)
+        cache.get(id1, classPath("c1"), null, null) != cache.get(id1, classPath("c1"), root, null)
+    }
+
+    def "filters are respected"() {
+        def root = classLoader(classPath("root"))
+        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [], [])
+        def f2 = new FilteringClassLoader.Spec(["2"], [], [], [], [], [], [])
+
+        expect:
+        cache.get(id1, classPath("c1"), root, f1).is(cache.get(id1, classPath("c1"), root, f1))
+        cache.size() == 2
+        !cache.get(id1, classPath("c1"), root, f1).is(cache.get(id1, classPath("c1"), root, f2))
+        cache.size() == 2
+    }
+
+    def "non filtered classloaders are reused"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [], [])
+        cache.get(id1, classPath("c1"), root, f1)
+        cache.size() == 2
+        cache.get(id1, classPath("c1"), root, null)
+        cache.size() == 1
+    }
+
+    def "filtered classloaders are reused if they have multiple ids"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [], [])
+        cache.get(id1, classPath("c1"), root, f1)
+        cache.get(id2, classPath("c1"), root, f1)
+        cache.size() == 2
+        cache.get(id1, classPath("c1"), root, null)
+        cache.size() == 2
+    }
+
+    def "unfiltered base is released when there are no more references to it"() {
+        expect:
+        def root = classLoader(classPath("root"))
+        def f1 = new FilteringClassLoader.Spec(["1"], [], [], [], [], [], [])
+        def f2 = new FilteringClassLoader.Spec(["2"], [], [], [], [], [], [])
+        def cp1 = classPath("c1")
+        def cp2 = classPath("c2")
+
+        cache.get(id1, cp1, root, f1)
+        cache.get(id2, cp1, root, f2)
+        cache.size() == 3
+        cache.get(id1, cp2, root, f1)
+        cache.size() == 4
+        cache.get(id1, cp1, root, null)
+        cache.size() == 2
+        cache.get(id1, cp2, root, null)
+        cache.get(id2, cp2, root, null)
+        cache.size() == 1
+    }
+
+    def "removes stale classloader"() {
+        def root = classLoader(classPath("root"))
+        cache.get(id1, classPath("c1"), root, null)
+        def c2 = cache.get(id1, classPath("c2"), root, null)
+        expect:
+        cache.size() == 1
+        c2.is cache.get(id1, classPath("c2"), root, null)
+    }
+
+    def "can remove loaders"() {
+        expect:
+        cache.size() == 0
+
+        when:
+        cache.remove(id1)
+
+        then:
+        noExceptionThrown()
+        cache.size() == 0
+
+        when:
+        def root = classLoader(classPath("root"))
+        cache.get(id1, classPath("c2"), root, null)
+        cache.get(id2, classPath("c2"), root, null)
+
+        then:
+        cache.size() == 1 // both are the same
+
+        when:
+        cache.remove(id1)
+
+        then:
+        cache.size() == 1 // still used by id2
+
+        when:
+        cache.remove(id2)
+
+        then:
+        cache.size() == 0
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DummyClassLoaderCache.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DummyClassLoaderCache.java
new file mode 100644
index 0000000..1024761
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/DummyClassLoaderCache.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.classloader.FilteringClassLoader;
+import org.gradle.internal.classpath.ClassPath;
+
+import java.net.URLClassLoader;
+
+public class DummyClassLoaderCache implements ClassLoaderCache {
+    public ClassLoader get(ClassLoaderId id, ClassPath classPath, ClassLoader parent, @Nullable FilteringClassLoader.Spec filterSpec) {
+        return new URLClassLoader(classPath.getAsURLArray(), parent);
+    }
+
+    @Override
+    public void remove(ClassLoaderId id) {
+
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotterTest.groovy
new file mode 100644
index 0000000..b4dae59
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/loadercache/HashClassPathSnapshotterTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.initialization.loadercache
+
+import org.gradle.api.internal.changedetection.state.CachingFileSnapshotter
+import org.gradle.api.internal.hash.DefaultHasher
+import org.gradle.cache.internal.NonThreadsafeInMemoryStore
+import org.gradle.internal.classpath.DefaultClassPath
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+class HashClassPathSnapshotterTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+    @Subject snapshotter = new HashClassPathSnapshotter(new CachingFileSnapshotter(new DefaultHasher(), new NonThreadsafeInMemoryStore()))
+
+    def "classpaths are different if file hashes are different"() {
+        def file = temp.file("a.txt")
+
+        file << "a"; def a = snapshotter.snapshot(new DefaultClassPath(file))
+        file << "b"; def b = snapshotter.snapshot(new DefaultClassPath(file))
+
+        expect:
+        a != b
+        a.hashCode() != b.hashCode()
+    }
+
+    def "classpaths are different when file names don't match"() {
+        def fa = temp.file("a.txt") << "a"
+        def fb = temp.file("b.txt") << "a" //same content
+
+        def a = snapshotter.snapshot(new DefaultClassPath(fa))
+        def b = snapshotter.snapshot(new DefaultClassPath(fb))
+
+        expect:
+        a != b
+        a.hashCode() != b.hashCode()
+    }
+
+    def "classpaths are different when files have different order"() {
+        def fa = temp.file("a.txt") << "a"
+        def fb = temp.file("b.txt") << "a"
+
+        def a = snapshotter.snapshot(new DefaultClassPath(fa, fb))
+        def b = snapshotter.snapshot(new DefaultClassPath(fb, fa))
+
+        expect:
+        a != b
+        a.hashCode() != b.hashCode()
+    }
+
+    def "classpaths are equal if all files are the same"() {
+        def files = [temp.file("a.txt") << "a", temp.file("b.txt") << "b"]
+        def a = snapshotter.snapshot(new DefaultClassPath(files))
+        def b = snapshotter.snapshot(new DefaultClassPath(files))
+
+        expect:
+        a == b
+        a.hashCode() == b.hashCode()
+    }
+
+    def "classpaths are equal if dirs are the same"() {
+        temp.file("dir/a.txt") << "a"; temp.file("dir/b.txt") << "b"; temp.file("dir/dir2/c.txt") << "c"
+        def a = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir")))
+        def b = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir")))
+
+        expect:
+        a == b
+        a.hashCode() == b.hashCode()
+    }
+
+    def "classpaths are different if dir names are different"() {
+        temp.file("dir1/a.txt") << "a"; temp.file("dir2/a.txt") << "a"
+        def a = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir1")))
+        def b = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir2")))
+
+        expect:
+        a != b
+        a.hashCode() != b.hashCode()
+    }
+
+    def "classpaths are the same for 2 empty dirs"() {
+        def a = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir1")))
+        def b = snapshotter.snapshot(new DefaultClassPath(temp.createDir("dir2")))
+
+        expect:
+        a == b
+        a.hashCode() == b.hashCode()
+    }
+
+    def "empty snapshots are the same"() {
+        when:
+        def s1 = snapshotter.snapshot(new DefaultClassPath(new File(temp.createDir("dir1"), "missing")));
+        def s2 = snapshotter.snapshot(new DefaultClassPath(new File(temp.createDir("dir2"), "missing")));
+
+        then:
+        s1 == s2
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationActionTest.groovy
index c2e9129..626902f 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationActionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultObjectConfigurationActionTest.groovy
@@ -19,7 +19,6 @@ import org.gradle.api.initialization.dsl.ScriptHandler
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
-
 import org.gradle.configuration.ScriptPlugin
 import org.gradle.configuration.ScriptPluginFactory
 import org.gradle.groovy.scripts.DefaultScript
@@ -35,9 +34,10 @@ class DefaultObjectConfigurationActionTest extends Specification {
     def scriptHandlerFactory = Mock(ScriptHandlerFactory)
     def scriptHandler = Mock(ScriptHandler)
     def scriptCompileScope = Mock(ClassLoaderScope)
+    def parentCompileScope = Mock(ClassLoaderScope)
     def configurer = Mock(ScriptPlugin)
 
-    DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(resolver, scriptPluginFactory, scriptHandlerFactory, scriptCompileScope, target)
+    DefaultObjectConfigurationAction action = new DefaultObjectConfigurationAction(resolver, scriptPluginFactory, scriptHandlerFactory, parentCompileScope, target)
 
     void doesNothingWhenNothingSpecified() {
         expect:
@@ -48,8 +48,9 @@ class DefaultObjectConfigurationActionTest extends Specification {
     public void appliesScriptsToDefaultTargetObject() {
         given:
         1 * resolver.resolveUri('script') >> file
+        1 * parentCompileScope.createChild("script-$file") >> scriptCompileScope
         1 * scriptHandlerFactory.create(_, scriptCompileScope) >> scriptHandler
-        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, "buildscript", DefaultScript) >> configurer
+        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, parentCompileScope, "buildscript", DefaultScript, false) >> configurer
 
         when:
         action.from('script')
@@ -65,9 +66,10 @@ class DefaultObjectConfigurationActionTest extends Specification {
         Object target2 = new Object()
         1 * resolver.resolveUri('script') >> file
         1 * scriptHandlerFactory.create(_, scriptCompileScope) >> scriptHandler
-        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, "buildscript", DefaultScript) >> configurer
+        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, parentCompileScope,  "buildscript", DefaultScript, false) >> configurer
         1 * configurer.apply(target1)
         1 * configurer.apply(target2)
+        1 * parentCompileScope.createChild("script-$file") >> scriptCompileScope
 
         then:
         action.from('script')
@@ -83,9 +85,10 @@ class DefaultObjectConfigurationActionTest extends Specification {
         Object target2 = new Object()
         1 * resolver.resolveUri('script') >> file
         1 * scriptHandlerFactory.create(_, scriptCompileScope) >> scriptHandler
-        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, "buildscript", DefaultScript) >> configurer
+        1 * scriptPluginFactory.create(_, scriptHandler, scriptCompileScope, parentCompileScope, "buildscript", DefaultScript, false) >> configurer
         1 * configurer.apply(target1)
         1 * configurer.apply(target2)
+        1 * parentCompileScope.createChild("script-$file") >> scriptCompileScope
 
         then:
         action.from('script')
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.groovy
new file mode 100644
index 0000000..7e18183
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.groovy
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.initialization.ClassLoaderScope
+import org.gradle.api.internal.project.TestRuleSource
+import org.gradle.api.plugins.UnknownPluginException
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+public class DefaultPluginContainerTest extends Specification {
+
+    def PluginInspector pluginInspector = new PluginInspector(new ModelRuleSourceDetector())
+    def classLoader = new GroovyClassLoader(getClass().classLoader)
+    def pluginRegistry = new DefaultPluginRegistry(pluginInspector, scope(classLoader))
+    def applicator = Mock(PluginApplicator)
+    def instantiator = DirectInstantiator.INSTANCE
+    def pluginManager = new DefaultPluginManager(pluginRegistry, instantiator, applicator)
+
+    @Subject
+    def container = pluginManager.pluginContainer
+
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    private Class<?> plugin1Class = classLoader.parseClass("""
+        import org.gradle.api.Plugin
+        import org.gradle.api.Project
+        class TestPlugin1 implements Plugin<Project> {
+          void apply(Project project) {}
+        }
+    """)
+
+    private Class<?> plugin2Class = classLoader.parseClass("""
+        import org.gradle.api.Plugin
+        import org.gradle.api.Project
+        class TestPlugin2 implements Plugin<Project> {
+          void apply(Project project) {}
+        }
+    """)
+
+    def setup() {
+        classLoader.addURL(testDirectoryProvider.testDirectory.toURI().toURL())
+        testDirectoryProvider.file("META-INF/gradle-plugins/plugin.properties") << "implementation-class=${plugin1Class.name}"
+    }
+
+    def "offers plugin management via plugin id"() {
+        when:
+        def p = container.apply(plugin1Class)
+
+        then:
+        p.is(container.apply("plugin"))
+        p.is(container.apply(plugin1Class))
+
+        p.is(container.findPlugin(plugin1Class))
+        p.is(container.findPlugin("plugin"))
+
+        !container.findPlugin(UnknownPlugin)
+        !container.findPlugin("unknown")
+
+        container.hasPlugin("plugin")
+        container.hasPlugin(plugin1Class)
+
+        !container.hasPlugin("unknown")
+        !container.hasPlugin(UnknownPlugin)
+    }
+
+    private class UnknownPlugin implements Plugin<Project> {
+        void apply(Project project) {}
+    }
+
+    def "offers plugin management via plugin type"() {
+        when:
+        def p = container.apply(plugin1Class)
+
+        then:
+        p.is(container.apply(plugin1Class))
+        p.is(container.findPlugin(plugin1Class))
+        container.hasPlugin(plugin1Class)
+
+        !p.is(container.findPlugin(plugin2Class))
+        !container.hasPlugin(plugin2Class)
+    }
+
+    def "does not find plugin by unknown id"() {
+        expect:
+        !container.hasPlugin("x")
+        !container.findPlugin("x")
+    }
+
+    def "fails when getting unknown plugin"() {
+        when:
+        container.getPlugin("unknown")
+
+        then:
+        thrown(UnknownPluginException)
+    }
+
+    def "fails when getting plugin of unknown type"() {
+        when:
+        container.getPlugin(plugin1Class)
+
+        then:
+        thrown(UnknownPluginException)
+    }
+
+    def "executes action for plugin with given id"() {
+        def plugin = plugin1Class.newInstance()
+        def plugins = []
+        container.add(plugin)
+
+        when:
+        container.withId("plugin") { plugins << it }
+
+        then:
+        plugins == [plugin]
+    }
+
+    def "executes action when plugin with given id is added later"() {
+        given:
+        def groovyLoader = new GroovyClassLoader(getClass().classLoader)
+        def classPathAdditions = testDirectoryProvider.createDir("resources")
+        groovyLoader.addURL(classPathAdditions.toURL())
+
+        def pluginClass = groovyLoader.parseClass """
+            package test
+
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class TestPlugin implements Plugin<Project> {
+                void apply(Project project) {
+
+                }
+            }
+        """
+        classPathAdditions.file("META-INF/gradle-plugins/plugin.properties") << "implementation-class=${pluginClass.name}"
+
+        def pluginRegistry = new DefaultPluginRegistry(pluginInspector, scope(groovyLoader))
+        def container = new DefaultPluginContainer(pluginRegistry, pluginManager)
+        def plugin = pluginClass.newInstance()
+        def plugins = []
+
+        when:
+        container.withId("plugin") { plugins << it }
+
+        then:
+        plugins.empty
+
+        when:
+        container.add(plugin)
+
+        then:
+        plugins == [plugin]
+    }
+
+    def "executes action when plugin with given id, of plugin not in registry, is added later"() {
+        given:
+        def groovyLoader = new GroovyClassLoader(getClass().classLoader)
+        def classPathAdditions = testDirectoryProvider.createDir("resources")
+        groovyLoader.addURL(classPathAdditions.toURL())
+
+        def pluginClass = groovyLoader.parseClass """
+            package test
+
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class TestPlugin implements Plugin<Project> {
+                void apply(Project project) {
+
+                }
+            }
+        """
+        classPathAdditions.file("META-INF/gradle-plugins/plugin.properties") << "implementation-class=${pluginClass.name}"
+
+        def pluginRegistry = new DefaultPluginRegistry(pluginInspector, scope(groovyLoader.parent))
+        def container = new DefaultPluginContainer(pluginRegistry, pluginManager)
+        def plugin = pluginClass.newInstance()
+        def plugins = []
+
+        when:
+        container.apply("plugin")
+
+        then:
+        thrown UnknownPluginException
+
+        when:
+        container.withId("plugin") { plugins << it }
+
+        then:
+        plugins.empty
+
+        when:
+        container.add(plugin)
+
+        then:
+        plugins == [plugin]
+    }
+
+    def "no error when withId used and plugin with no id"() {
+        given:
+        def groovyLoader = new GroovyClassLoader(getClass().classLoader)
+
+        def pluginClass = groovyLoader.parseClass """
+            package test
+
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class TestPlugin implements Plugin<Project> {
+                void apply(Project project) {
+
+                }
+            }
+        """
+
+        def pluginRegistry = new DefaultPluginRegistry(pluginInspector, scope(groovyLoader.parent))
+        def container = new DefaultPluginContainer(pluginRegistry, pluginManager)
+        def plugin = pluginClass.newInstance()
+        def plugins = []
+
+        when:
+        container.apply("plugin")
+
+        then:
+        thrown UnknownPluginException
+
+        when:
+        container.withId("plugin") { plugins << it }
+
+        then:
+        plugins.empty
+
+        when:
+        container.add(plugin)
+
+        then:
+        plugins == []
+    }
+
+    def "calls applicator for type only"() {
+        when:
+        container.apply(plugin1Class)
+
+        then:
+        1 * applicator.applyImperative(null, { plugin1Class.isInstance(it) })
+    }
+
+    def "calls applicator for id"() {
+        when:
+        container.apply("plugin")
+
+        then:
+        1 * applicator.applyImperative("plugin", { plugin1Class.isInstance(it) })
+    }
+
+    def "a useful error message is set when a plain rule source type is passed to withType"() {
+        when:
+        container.withType(TestRuleSource)
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "'$TestRuleSource.name' does not implement the Plugin interface."
+    }
+
+    def scope(ClassLoader classLoader) {
+        return Stub(ClassLoaderScope) {
+            getLocalClassLoader() >> classLoader
+        }
+    }
+}
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
deleted file mode 100644
index 5f2e756..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginContainerTest.java
+++ /dev/null
@@ -1,128 +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.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/DefaultPluginManagerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginManagerTest.groovy
new file mode 100644
index 0000000..9e3ae10
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginManagerTest.groovy
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action
+import org.gradle.api.Plugin
+import org.gradle.api.internal.initialization.ClassLoaderScope
+import org.gradle.api.plugins.AppliedPlugin
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultPluginManagerTest extends Specification {
+
+    def classLoader = new GroovyClassLoader(getClass().classLoader)
+    def classLoaderScope = Stub(ClassLoaderScope) {
+        getLocalClassLoader() >> classLoader
+    }
+    def registry = new DefaultPluginRegistry(new PluginInspector(new ModelRuleSourceDetector()), classLoaderScope)
+    def applicator = Mock(PluginApplicator)
+    def manager = new DefaultPluginManager(registry, DirectInstantiator.INSTANCE, applicator)
+
+    Class<?> rulesClass
+    Class<? extends Plugin> hybridClass
+    Class<? extends Plugin> imperativeClass
+
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+
+    def setup() {
+        rulesClass = classLoader.parseClass("""
+            class Rules extends org.gradle.model.RuleSource {
+
+            }
+        """)
+
+        hybridClass = classLoader.parseClass("""
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class Hybrid implements Plugin<Project> {
+                @Override
+                void apply(Project target) {
+
+                }
+
+                static class Rules extends org.gradle.model.RuleSource {}
+            }
+        """)
+
+        imperativeClass = classLoader.parseClass("""
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class Imperative implements Plugin<Project> {
+                @Override
+                void apply(Project target) {
+
+                }
+            }
+        """)
+
+        classLoader.addURL(testDirectoryProvider.testDirectory.toURI().toURL())
+    }
+
+    def "empty manager ops"() {
+        expect:
+        manager.pluginContainer.empty
+        !manager.hasPlugin("foo")
+        manager.findPlugin("foo") == null
+    }
+
+    def "can apply rules plugin with no id"() {
+        when:
+        manager.apply(rulesClass)
+
+        then:
+        1 * applicator.applyRules(null, rulesClass)
+
+        and:
+        manager.pluginContainer.isEmpty()
+    }
+
+    def "can apply rules plugin by class with id"() {
+        given:
+        addPluginId("foo", rulesClass)
+
+        when:
+        manager.apply(rulesClass)
+
+        then:
+        1 * applicator.applyRules(null, rulesClass)
+
+        and:
+        manager.hasPlugin("foo")
+    }
+
+    def "action is notified when rules plugin with id is applied by class"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", rulesClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply(rulesClass)
+
+        then:
+        1 * action.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "foo"
+            assert p.namespace == null
+            assert p.name == "foo"
+        }
+        0 * action._
+    }
+
+    def "rules plugin is applied at most once"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", rulesClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply(rulesClass)
+        manager.apply("foo")
+        manager.apply(rulesClass)
+
+        then:
+        1 * applicator.applyRules(null, rulesClass)
+        1 * action.execute(_)
+        0 * applicator._
+        0 * action._
+
+        and:
+        manager.hasPlugin("foo")
+    }
+
+    def "can apply rules plugin by id"() {
+        given:
+        addPluginId("foo", rulesClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        1 * applicator.applyRules("foo", rulesClass)
+
+        and:
+        manager.pluginContainer.isEmpty()
+        manager.hasPlugin("foo")
+    }
+
+    def "action is notified when rules plugin with id is applied by id"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", rulesClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        1 * action.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "foo"
+            assert p.namespace == null
+            assert p.name == "foo"
+        }
+        0 * action._
+    }
+
+    def "rules plugin with id does not appear in plugin container"() {
+        given:
+        addPluginId("foo", rulesClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        manager.pluginContainer.isEmpty()
+        manager.pluginContainer.findPlugin(rulesClass) == null
+        manager.pluginContainer.findPlugin("foo") == null
+        !manager.pluginContainer.hasPlugin(rulesClass)
+        !manager.pluginContainer.hasPlugin("foo")
+    }
+
+    def "can apply hybrid plugin with no id"() {
+        when:
+        manager.apply(hybridClass)
+
+        then:
+        1 * applicator.applyImperativeRulesHybrid(null, { hybridClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+    }
+
+    def "can apply hybrid plugin by class with id"() {
+        given:
+        addPluginId("foo", hybridClass)
+
+        when:
+        manager.apply(hybridClass)
+
+        then:
+        1 * applicator.applyImperativeRulesHybrid(null, { hybridClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.hasPlugin("foo")
+        def called = false
+        manager.withPlugin("foo") {
+            assert it.id == "foo"
+            assert it.namespace == null
+            assert it.name == "foo"
+            called = true
+        }
+        called
+    }
+
+    def "can apply hybrid plugin by id"() {
+        given:
+        addPluginId("foo", hybridClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        1 * applicator.applyImperativeRulesHybrid("foo", { hybridClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.hasPlugin("foo")
+        def called = false
+        manager.withPlugin("foo") {
+            assert it.id == "foo"
+            assert it.namespace == null
+            assert it.name == "foo"
+            called = true
+        }
+        called
+    }
+
+    def "hybrid plugin is applied at most once"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", hybridClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply(hybridClass)
+        manager.apply("foo")
+        manager.apply(hybridClass)
+
+        then:
+        1 * applicator.applyImperativeRulesHybrid(null, { hybridClass.isInstance(it) })
+        0 * applicator._
+        1 * action.execute(_)
+        0 * action._
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.hasPlugin("foo")
+    }
+
+    def "hybrid plugin with id appears in plugins container"() {
+        given:
+        addPluginId("foo", hybridClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        manager.pluginContainer.size() == 1
+        manager.pluginContainer.findPlugin(hybridClass) != null
+        manager.pluginContainer.findPlugin("foo") != null
+        manager.pluginContainer.hasPlugin(hybridClass)
+        manager.pluginContainer.hasPlugin("foo")
+    }
+
+    def "can apply imperative plugin with no id"() {
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        1 * applicator.applyImperative(null, { imperativeClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+    }
+
+    def "can apply imperative plugin by class with id"() {
+        given:
+        addPluginId("foo", imperativeClass)
+
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        1 * applicator.applyImperative(null, { imperativeClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.hasPlugin("foo")
+    }
+
+    def "can apply imperative plugin by id"() {
+        given:
+        addPluginId("foo", imperativeClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        1 * applicator.applyImperative("foo", { imperativeClass.isInstance(it) })
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.hasPlugin("foo")
+        def called = false
+        manager.withPlugin("foo") {
+            assert it.id == "foo"
+            assert it.namespace == null
+            assert it.name == "foo"
+            called = true
+        }
+        called
+    }
+
+    def "imperative plugin applied via plugins container is visible via plugins manager"() {
+        given:
+        addPluginId("foo", imperativeClass)
+
+        when:
+        manager.pluginContainer.apply(imperativeClass)
+
+        then:
+        1 * applicator.applyImperative(null, { imperativeClass.isInstance(it) })
+        0 * applicator._
+
+        and:
+        manager.pluginContainer.size() == 1
+        manager.pluginContainer.hasPlugin("foo")
+        manager.pluginContainer.hasPlugin(imperativeClass)
+        manager.hasPlugin("foo")
+    }
+
+    def "imperative plugin is applied at most once"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", imperativeClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply(imperativeClass)
+        manager.apply("foo")
+        manager.apply(imperativeClass)
+
+        then:
+        1 * applicator.applyImperative(null, { imperativeClass.isInstance(it) })
+        0 * applicator._
+        1 * action.execute(_)
+        0 * action._
+    }
+
+    def "imperative plugin with id appears in plugins container"() {
+        given:
+        addPluginId("foo", imperativeClass)
+
+        when:
+        manager.apply("foo")
+
+        then:
+        manager.pluginContainer.size() == 1
+        manager.pluginContainer.findPlugin("foo") != null
+        manager.pluginContainer.findPlugin(imperativeClass) != null
+        manager.pluginContainer.hasPlugin(imperativeClass)
+        manager.pluginContainer.hasPlugin("foo")
+    }
+
+    def "action is notified when imperative plugin with id is applied"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", imperativeClass)
+        manager.withPlugin("foo", action)
+        manager.pluginContainer.withId("foo", action)
+
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        1 * action.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "foo"
+        }
+        1 * action.execute(_) >> { Plugin p ->
+            assert p.class == imperativeClass
+        }
+        0 * action._
+    }
+
+    def "plugin with multiple ids visible with all ids"() {
+        given:
+        addPluginId("foo", imperativeClass)
+        addPluginId("bar", imperativeClass)
+
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        manager.hasPlugin("foo")
+        manager.hasPlugin("bar")
+        manager.pluginContainer.size() == 1
+        manager.pluginContainer.hasPlugin("foo")
+        manager.pluginContainer.hasPlugin("bar")
+        manager.pluginContainer.hasPlugin(imperativeClass)
+    }
+
+    def "plugin with org.gradle id is visible with unqualified id"() {
+        given:
+        addPluginId("org.gradle.foo", imperativeClass)
+
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        manager.hasPlugin("foo")
+        manager.hasPlugin("org.gradle.foo")
+
+        manager.findPlugin("foo") != null
+        manager.findPlugin("org.gradle.foo") != null
+
+        manager.pluginContainer.size() == 1
+        manager.pluginContainer.hasPlugin("foo")
+        manager.pluginContainer.hasPlugin("org.gradle.foo")
+        manager.pluginContainer.findPlugin("foo") != null
+        manager.pluginContainer.hasPlugin("org.gradle.foo") != null
+    }
+
+    def "action is notified for plugin with multiple ids"() {
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+
+        given:
+        addPluginId("foo", imperativeClass)
+        addPluginId("bar", imperativeClass)
+
+        when:
+        manager.withPlugin("foo", action1)
+        manager.withPlugin("bar", action2)
+        manager.apply(imperativeClass)
+        manager.withPlugin("foo", action1)
+        manager.withPlugin("bar", action2)
+
+        then:
+        2 * action1.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "foo"
+        }
+        2 * action2.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "bar"
+        }
+        0 * action1._
+        0 * action2._
+    }
+
+    def "action is notified for plugin with org.gradle id"() {
+        def action1 = Mock(Action)
+        def action2 = Mock(Action)
+
+        given:
+        addPluginId("org.gradle.foo", imperativeClass)
+
+        when:
+        manager.withPlugin("foo", action1)
+        manager.withPlugin("org.gradle.foo", action2)
+        manager.apply(imperativeClass)
+        manager.withPlugin("foo", action1)
+        manager.withPlugin("org.gradle.foo", action2)
+
+        then:
+        2 * action1.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "foo"
+        }
+        2 * action2.execute(_) >> { AppliedPlugin p ->
+            assert p.id == "org.gradle.foo"
+        }
+        0 * action1._
+        0 * action2._
+    }
+
+    def "plugin with multiple ids is applied at most once"() {
+        def action = Mock(Action)
+
+        given:
+        addPluginId("foo", imperativeClass)
+        addPluginId("bar", imperativeClass)
+        manager.withPlugin("foo", action)
+
+        when:
+        manager.apply("bar")
+        manager.apply("foo")
+        manager.apply(imperativeClass)
+
+        then:
+        1 * applicator.applyImperative("bar", { imperativeClass.isInstance(it) })
+        0 * applicator._
+        1 * action.execute(_)
+        0 * action._
+
+        and:
+        manager.pluginContainer.size() == 1
+    }
+
+    def "with plugin fires for each plugin known by that id"() {
+        given:
+        def applied = []
+        def groovyLoader = new GroovyClassLoader(getClass().classLoader)
+        def pluginClass = groovyLoader.parseClass """
+            package test
+
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+
+            class TestPlugin implements Plugin<Project> {
+                void apply(Project project) {
+
+                }
+            }
+        """
+
+        when:
+        addPluginId("plugin", imperativeClass)
+        manager.withPlugin("plugin") { applied << it }
+
+        then:
+        applied.empty
+
+        when:
+        def dir = testDirectoryProvider.createDir("other")
+        groovyLoader.addURL(dir.toURI().toURL())
+        addPluginId(dir, "plugin", pluginClass)
+        manager.apply(pluginClass)
+
+        then:
+        applied.size() == 1
+
+        when:
+        manager.apply(imperativeClass)
+
+        then:
+        applied.size() == 2
+    }
+
+    void addPluginId(String id, Class<?> pluginClass) {
+        addPluginId(testDirectoryProvider.testDirectory, id, pluginClass)
+    }
+
+    static void addPluginId(TestFile dir, String id, Class<?> pluginClass) {
+        dir.file("META-INF/gradle-plugins/${id}.properties") << "implementation-class=$pluginClass.name"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistryTest.groovy
index 0117655..b73c443 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/DefaultPluginRegistryTest.groovy
@@ -16,15 +16,13 @@
 
 package org.gradle.api.internal.plugins
 
-import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Plugin
 import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.api.internal.project.TestPlugin1
-import org.gradle.api.internal.project.TestPlugin2
-import org.gradle.api.plugins.PluginInstantiationException
-import org.gradle.api.plugins.UnknownPluginException
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.reflect.ObjectInstantiationException
+import org.gradle.api.internal.project.TestRuleSource
+import org.gradle.api.plugins.InvalidPluginException
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.plugin.internal.PluginId
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.GUtil
 import org.junit.Rule
@@ -33,181 +31,287 @@ import spock.lang.Specification
 class DefaultPluginRegistryTest extends Specification {
     @Rule
     final TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider()
-    final Instantiator instantiator = Mock()
-    final ClassLoader classLoader = Mock()
-    private DefaultPluginRegistry pluginRegistry = new DefaultPluginRegistry(classLoader, instantiator)
+    def classLoader = Mock(ClassLoader)
+    def classLoaderScope = Stub(ClassLoaderScope) {
+        getLocalClassLoader() >> classLoader
+    }
+    def pluginInspector = new PluginInspector(new ModelRuleSourceDetector())
+    private DefaultPluginRegistry pluginRegistry = new DefaultPluginRegistry(pluginInspector, classLoaderScope)
 
-    public void canLoadPluginByType() {
-        def plugin = new TestPlugin2()
+    def "can locate imperative plugin implementation given an id"() {
+        def url = writePluginProperties(TestPlugin1)
 
-        when:
-        def result = pluginRegistry.loadPlugin(TestPlugin2.class)
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
 
-        then:
-        result == plugin
+        expect:
+        def plugin = pluginRegistry.lookup(PluginId.of("somePlugin"))
+        plugin.pluginId == PluginId.of("somePlugin")
+        plugin.type == PotentialPlugin.Type.IMPERATIVE_CLASS
+        plugin.displayName == "id 'somePlugin'"
+        plugin.asClass() == TestPlugin1
+    }
 
-        and:
-        1* instantiator.newInstance(TestPlugin2.class, new Object[0]) >> plugin
+    def "can locate rule source plugin implementation given an id"() {
+        def ruleUrl = writePluginProperties(TestRuleSource)
+
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/someRuleSource.properties") >> ruleUrl
+        classLoader.loadClass(TestRuleSource.name) >> TestRuleSource
+
+        expect:
+        def plugin = pluginRegistry.lookup(PluginId.of("someRuleSource"))
+        plugin.pluginId == PluginId.of("someRuleSource")
+        plugin.type == PotentialPlugin.Type.PURE_RULE_SOURCE_CLASS
+        plugin.displayName == "id 'someRuleSource'"
+        plugin.asClass() == TestRuleSource
+    }
+
+    def "locate returns null for unknown id"() {
+        expect:
+        pluginRegistry.lookup(PluginId.of("unknownId")) == null
     }
 
-    public void canLookupPluginTypeById() {
-        def url = writePluginProperties("somePlugin", TestPlugin1)
+    def "can locate plugin implementation in org.gradle namespace using unqualified id"() {
+        def url = writePluginProperties(TestPlugin1)
 
         given:
-        _ * classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
-        _ * classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+        classLoader.getResource("META-INF/gradle-plugins/org.gradle.somePlugin.properties") >> url
+        classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> { throw new RuntimeException() }
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
 
         expect:
-        pluginRegistry.getTypeForId("somePlugin") == TestPlugin1
+        def unqualified = pluginRegistry.lookup(PluginId.of("somePlugin"))
+        unqualified.pluginId == PluginId.of("org.gradle.somePlugin")
+        unqualified.displayName == "id 'org.gradle.somePlugin'"
+        unqualified.asClass() == TestPlugin1
+
+        def qualified = pluginRegistry.lookup(PluginId.of("org.gradle.somePlugin"))
+        qualified.pluginId == PluginId.of("org.gradle.somePlugin")
+        unqualified.displayName == "id 'org.gradle.somePlugin'"
+        qualified.asClass() == TestPlugin1
     }
 
-    public void failsForUnknownId() {
-        when:
-        pluginRegistry.getTypeForId("unknownId")
+    def "does not search in org.gradle namespace when id is qualified with some other namespace"() {
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/org.gradle.thing.somePlugin.properties") >> { throw new RuntimeException() }
 
-        then:
-        UnknownPluginException e = thrown()
-        e.message == "Plugin with id 'unknownId' not found."
+        expect:
+        pluginRegistry.lookup(PluginId.of("thing.somePlugin")) == null
     }
 
-    public void failsWhenClassDoesNotImplementPlugin() {
-        when:
-        pluginRegistry.loadPlugin((Class)String.class)
+    def "plugin implementation with id in org.gradle namespace is also known by unqualified id"() {
+        def url = writePluginProperties(TestPlugin1)
 
-        then:
-        InvalidUserDataException e = thrown()
-        e.message == "Cannot create plugin of type 'String' as it does not implement the Plugin interface."
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/org.gradle.somePlugin.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def qualified = pluginRegistry.lookup(PluginId.of("org.gradle.somePlugin"))
+        qualified.isAlsoKnownAs(PluginId.of("org.gradle.somePlugin"))
+        qualified.isAlsoKnownAs(PluginId.of("somePlugin"))
+
+        def unqualified = pluginRegistry.lookup(PluginId.of("somePlugin"))
+        unqualified.isAlsoKnownAs(PluginId.of("org.gradle.somePlugin"))
+        unqualified.isAlsoKnownAs(PluginId.of("somePlugin"))
     }
 
-    public void failsWhenNoImplementationClassSpecifiedInPropertiesFile() {
-        def properties = new Properties()
-        def propertiesFile = testDir.file("prop")
-        GUtil.saveProperties(properties, propertiesFile)
-        def url = propertiesFile.toURI().toURL()
+    def "plugin implementation is also known by all id mappings that reference that implementation"() {
+        def url = writePluginProperties(TestPlugin1)
 
         given:
-        _ * classLoader.getResource("META-INF/gradle-plugins/noImpl.properties") >> url
+        classLoader.getResource("META-INF/gradle-plugins/plugin-1.properties") >> url
+        classLoader.getResource("META-INF/gradle-plugins/plugin-2.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
 
-        when:
-        pluginRegistry.getTypeForId("noImpl")
+        expect:
+        def plugin1 = pluginRegistry.lookup(PluginId.of("plugin-1"))
+        def plugin2 = pluginRegistry.lookup(PluginId.of("plugin-2"))
 
-        then:
-        PluginInstantiationException e = thrown()
-        e.message == "No implementation class specified for plugin 'noImpl' in $url."
+        plugin1.isAlsoKnownAs(PluginId.of("plugin-1"))
+        plugin1.isAlsoKnownAs(PluginId.of("plugin-2"))
+        plugin2.isAlsoKnownAs(PluginId.of("plugin-1"))
+        plugin2.isAlsoKnownAs(PluginId.of("plugin-2"))
+    }
+
+    def "plugin implementation is not known by unknown id"() {
+        def url = writePluginProperties(TestPlugin1)
+
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/thing.somePlugin.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def plugin = pluginRegistry.lookup(PluginId.of("thing.somePlugin"))
+        !plugin.isAlsoKnownAs(PluginId.of("somePlugin"))
+        !plugin.isAlsoKnownAs(PluginId.of("thing.other"))
+        !plugin.isAlsoKnownAs(PluginId.of("org.gradle.thing.somePlugin"))
+    }
+
+    def "plugin implementation is not known by id that maps to another implementation"() {
+        def url1 = writePluginProperties(TestPlugin1)
+        def url2 = writePluginProperties(BrokenPlugin)
+
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/plugin-1.properties") >> url1
+        classLoader.getResource("META-INF/gradle-plugins/plugin-2.properties") >> url2
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+        classLoader.loadClass(BrokenPlugin.name) >> BrokenPlugin
+
+        expect:
+        def plugin1 = pluginRegistry.lookup(PluginId.of("plugin-1"))
+        def plugin2 = pluginRegistry.lookup(PluginId.of("plugin-2"))
+
+        !plugin1.isAlsoKnownAs(PluginId.of("plugin-2"))
+        !plugin2.isAlsoKnownAs(PluginId.of("plugin-1"))
     }
 
-    public void failsWhenImplementationClassSpecifiedInPropertiesFileDoesNotImplementPlugin() {
+    def "inspects imperative plugin implementation"() {
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def plugin = pluginRegistry.inspect(TestPlugin1.class)
+        plugin.type == PotentialPlugin.Type.IMPERATIVE_CLASS
+        plugin.pluginId == null
+        plugin.displayName == "class '${TestPlugin1.name}'"
+    }
+
+    def "inspects imperative plugin implementation that has no id mapping"() {
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def plugin = pluginRegistry.inspect(TestPlugin1.class)
+        !plugin.isAlsoKnownAs(PluginId.of("org.gradle.some-plugin"))
+    }
+
+    def "inspects class that has multiple id mappings"() {
+        def url = writePluginProperties(TestPlugin1)
+
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/plugin-1.properties") >> url
+        classLoader.getResource("META-INF/gradle-plugins/plugin-2.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def plugin = pluginRegistry.lookup(PluginId.of("plugin-1"), classLoader)
+        plugin.isAlsoKnownAs(PluginId.of("plugin-1"))
+        plugin.isAlsoKnownAs(PluginId.of("plugin-2"))
+    }
+
+    def "inspects class that has id mapping in org.gradle namespace"() {
+        def url = writePluginProperties(TestPlugin1)
+
+        given:
+        classLoader.getResource("META-INF/gradle-plugins/org.gradle.somePlugin.properties") >> url
+        classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
+
+        expect:
+        def plugin = pluginRegistry.lookup(PluginId.of("org.gradle.somePlugin"), classLoader)
+        plugin.isAlsoKnownAs(PluginId.of("somePlugin"))
+        plugin.isAlsoKnownAs(PluginId.of("org.gradle.somePlugin"))
+    }
+
+    def "inspects class that is not a plugin implementation"() {
+        classLoader.loadClass(String.name) >> String
+
+        expect:
+        pluginRegistry.inspect(String.class).type == PotentialPlugin.Type.UNKNOWN
+    }
+
+    def "fails when no implementation class specified in properties file"() {
         def properties = new Properties()
         def propertiesFile = testDir.file("prop")
-        properties.setProperty("implementation-class", "java.lang.String")
         GUtil.saveProperties(properties, propertiesFile)
         def url = propertiesFile.toURI().toURL()
 
         given:
-        _ * classLoader.getResource("META-INF/gradle-plugins/brokenImpl.properties") >> url
-        _ * classLoader.loadClass("java.lang.String") >> String
+        _ * classLoader.getResource("META-INF/gradle-plugins/noImpl.properties") >> url
 
         when:
-        pluginRegistry.getTypeForId("brokenImpl")
+        pluginRegistry.lookup(PluginId.of("noImpl"))
 
         then:
-        PluginInstantiationException e = thrown()
-        e.message == "Implementation class 'java.lang.String' specified for plugin 'brokenImpl' does not implement the Plugin interface. Specified in $url."
-    }
-
-    public void wrapsPluginInstantiationFailure() {
-        def failure = new RuntimeException();
-
-        given:
-        _ * instantiator.newInstance(BrokenPlugin, new Object[0]) >> { throw new ObjectInstantiationException(BrokenPlugin.class, failure) }
+        InvalidPluginException e = thrown()
+        e.message == "No implementation class specified for plugin 'noImpl' in $url."
 
         when:
-        pluginRegistry.loadPlugin(BrokenPlugin.class)
+        pluginRegistry.lookup(PluginId.of("noImpl"))
 
         then:
-        PluginInstantiationException e = thrown()
-        e.message == "Could not create plugin of type 'BrokenPlugin'."
-        e.cause == failure
+        e = thrown()
+        e.message == "No implementation class specified for plugin 'noImpl' in $url."
     }
 
-    public void wrapsFailureToLoadImplementationClass() {
-        def url = writePluginProperties("somePlugin", TestPlugin1)
+    def "can locate a plugin implementation that is neither imperative or rule source"() {
+        def url = writePluginProperties(String)
 
         given:
-        _ * classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
-        _ * classLoader.loadClass(TestPlugin1.name) >> { throw new ClassNotFoundException() }
+        classLoader.getResource("META-INF/gradle-plugins/brokenImpl.properties") >> url
+        classLoader.loadClass(String.name) >> String
 
-        when:
-        pluginRegistry.getTypeForId("somePlugin")
-
-        then:
-        PluginInstantiationException e = thrown()
-        e.message == "Could not find implementation class '$TestPlugin1.name' for plugin 'somePlugin' specified in $url."
-        e.cause instanceof ClassNotFoundException
+        expect:
+        pluginRegistry.lookup(PluginId.of("brokenImpl")).type == PotentialPlugin.Type.UNKNOWN
     }
 
-    public void childUsesItsOwnInstantiatorToCreatePlugin() {
-        def lookupScope = Mock(ClassLoaderScope)
-        def childInstantiator = Mock(Instantiator)
-        def plugin = new TestPlugin1()
+    def "wraps failure to load implementation class"() {
+        def url = writePluginProperties(TestPlugin1)
 
         given:
-        PluginRegistry child = pluginRegistry.createChild(lookupScope, childInstantiator)
+        _ * classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
+        _ * classLoader.loadClass(TestPlugin1.name) >> { throw new ClassNotFoundException() }
 
         when:
-        def result = child.loadPlugin(TestPlugin1)
+        pluginRegistry.lookup(PluginId.of("somePlugin"))
 
         then:
-        result == plugin
-
-        and:
-        1 * childInstantiator.newInstance(TestPlugin1, new Object[0]) >> plugin
-        0 * instantiator._
+        InvalidPluginException e = thrown()
+        e.message == "Could not find implementation class '$TestPlugin1.name' for plugin 'somePlugin' specified in $url."
+        e.cause instanceof ClassNotFoundException
     }
 
-    public void childDelegatesToParentRegistryToLookupPluginType() throws Exception {
+    def "child delegates to parent registry to locate implementation from an id"() throws Exception {
         def lookupScope = Mock(ClassLoaderScope)
-        def childInstantiator = Mock(Instantiator)
-        def url = writePluginProperties("somePlugin", TestPlugin1)
+        def url = writePluginProperties(TestPlugin1)
 
         given:
-        PluginRegistry child = pluginRegistry.createChild(lookupScope, childInstantiator)
+        PluginRegistry child = pluginRegistry.createChild(lookupScope)
         _ * classLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
         _ * classLoader.loadClass(TestPlugin1.name) >> TestPlugin1
 
         when:
-        def type = child.getTypeForId("somePlugin")
+        def plugin = child.lookup(PluginId.of("somePlugin"))
 
         then:
-        type == TestPlugin1
+        plugin.asClass() == TestPlugin1
 
         and:
         0 * lookupScope._
     }
 
-    public void childClasspathCanContainAdditionalMappingsForPlugins() throws Exception {
+    def "child classpath can container additional mappings"() throws Exception {
         def childClassLoader = Mock(ClassLoader)
         def lookupScope = Mock(ClassLoaderScope)
-        def childInstantiator = Mock(Instantiator)
-        def url = writePluginProperties("somePlugin", TestPlugin1)
+        def url = writePluginProperties(TestPlugin1)
 
         given:
-        PluginRegistry child = pluginRegistry.createChild(lookupScope, childInstantiator)
-        _ * lookupScope.scopeClassLoader >> childClassLoader
+        PluginRegistry child = pluginRegistry.createChild(lookupScope)
+        _ * lookupScope.localClassLoader >> childClassLoader
         _ * childClassLoader.getResource("META-INF/gradle-plugins/somePlugin.properties") >> url
         _ * childClassLoader.loadClass(TestPlugin1.name) >> TestPlugin1
 
         when:
-        def type = child.getTypeForId("somePlugin")
+        def type = child.lookup(PluginId.of("somePlugin")).asClass()
 
         then:
         type == TestPlugin1
     }
 
-    private URL writePluginProperties(String pluginId, Class implClass) {
+    private URL writePluginProperties(Class implClass) {
         def props = new Properties()
         props.setProperty("implementation-class", implClass.name)
-        def propertiesFile = testDir.file("${pluginId}.properties")
+        def propertiesFile = testDir.file("${implClass}.properties")
         propertiesFile.getParentFile().mkdirs()
         GUtil.saveProperties(props, propertiesFile)
         return propertiesFile.toURI().toURL()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapterTest.groovy
index e40f683..c6b8bb7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/ExtraPropertiesDynamicObjectAdapterTest.groovy
@@ -16,15 +16,12 @@
 
 package org.gradle.api.internal.plugins
 
-import org.gradle.api.internal.BeanDynamicObject
 import org.gradle.api.plugins.ExtraPropertiesExtension
 import spock.lang.Specification
 
 public class ExtraPropertiesDynamicObjectAdapterTest extends Specification {
-
-    Object delegate = new Object()
     ExtraPropertiesExtension extension = new DefaultExtraPropertiesExtension()
-    ExtraPropertiesDynamicObjectAdapter adapter =  new ExtraPropertiesDynamicObjectAdapter(delegate, new BeanDynamicObject(delegate), extension)
+    ExtraPropertiesDynamicObjectAdapter adapter =  new ExtraPropertiesDynamicObjectAdapter(String.class, extension)
 
     def "can get and set properties"() {
         given:
@@ -84,15 +81,15 @@ public class ExtraPropertiesDynamicObjectAdapterTest extends Specification {
         thrown(groovy.lang.MissingMethodException)
     }
 
-    static class NamedExtraPropertiesExtension extends DefaultExtraPropertiesExtension {
-        String name
-    }
-
     def "has property 'properties'"() {
         expect:
         adapter.hasProperty("properties")
 
         and:
-        new ExtraPropertiesDynamicObjectAdapter(delegate, new BeanDynamicObject(delegate), new NamedExtraPropertiesExtension()).hasProperty("name")
+        new ExtraPropertiesDynamicObjectAdapter(String.class, new NamedExtraPropertiesExtension()).hasProperty("name")
     }
 }
+
+class NamedExtraPropertiesExtension extends DefaultExtraPropertiesExtension {
+    String name
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/RuleSourceApplicationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/RuleSourceApplicationTest.groovy
new file mode 100644
index 0000000..3c9d603
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/plugins/RuleSourceApplicationTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.plugins
+
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.plugins.InvalidPluginException
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.type.ModelType
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.CustomRuleSource
+import org.gradle.testfixtures.ProjectBuilder
+import org.junit.Rule
+import spock.lang.Specification
+
+class RuleSourceApplicationTest extends Specification {
+
+    @Rule
+    public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    private ProjectInternal buildProject() {
+        ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+    }
+
+    def "can apply a rule source by id"() {
+        when:
+        def project = buildProject()
+        project.apply plugin: 'custom-rule-source'
+
+        then:
+        project.modelRegistry.realize(ModelPath.path("foo"), ModelType.of(String)) == "bar"
+    }
+
+    def "can apply a rule source by type"() {
+        when:
+        def project = buildProject()
+        project.apply type: CustomRuleSource
+
+        then:
+        project.modelRegistry.realize(ModelPath.path("foo"), ModelType.of(String)) == "bar"
+    }
+
+    def "cannot apply a type that is neither a plugin nor a rule source"() {
+        when:
+        def project = buildProject()
+        project.apply type: String
+
+        then:
+        PluginApplicationException e = thrown()
+        e.cause instanceof InvalidPluginException
+        e.cause.message == "'${String.name}' is neither a plugin or a rule source and cannot be applied."
+    }
+
+    def "cannot apply a rule source to a non model rule scope element"() {
+        when:
+        def project = buildProject()
+        project.gradle.apply plugin: "custom-rule-source"
+
+        then:
+        PluginApplicationException e = thrown()
+        e.cause instanceof UnsupportedOperationException
+        e.cause.message == "Cannot apply model rules of plugin '${CustomRuleSource.name}' as the target 'build 'test'' is not model rule aware"
+    }
+
+    def "useful message is presented when applying rule source only type as a plugin"() {
+        when:
+        def project = buildProject()
+        project.apply plugin: CustomRuleSource
+
+        then:
+        project.modelRegistry.realize(ModelPath.path("foo"), ModelType.of(String)) == "bar"
+    }
+
+    def "can use id to check for applied plugins and rule sources"() {
+        when:
+        def project = buildProject()
+
+        then:
+        !project.pluginManager.hasPlugin("custom-plugin")
+        !project.pluginManager.hasPlugin("custom-rule-source")
+
+        when:
+        project.apply plugin: "custom-plugin"
+        project.apply plugin: "custom-rule-source"
+
+        then:
+        project.pluginManager.hasPlugin("custom-plugin")
+        project.pluginManager.hasPlugin("custom-rule-source")
+    }
+}
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 37025bc..3e10295 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
@@ -16,16 +16,19 @@
 package org.gradle.api.internal.project
 
 import groovy.xml.MarkupBuilder
-import org.junit.Test
+import org.apache.tools.ant.Target
+import org.apache.tools.ant.Task
 import org.gradle.api.Project
-import org.gradle.util.TestUtil
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-import static org.gradle.util.Matchers.*
 import org.gradle.api.tasks.ant.AntTarget
+import org.gradle.util.TestUtil
+import org.junit.Test
+
 import java.lang.reflect.Field
-import org.apache.tools.ant.Target
-import org.apache.tools.ant.Task
+
+import static org.gradle.util.Matchers.isEmpty
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.fail
 
 class DefaultAntBuilderTest {
     private final Project project = TestUtil.createRootProject()
@@ -71,7 +74,7 @@ class DefaultAntBuilderTest {
     @Test
     public void importAddsTaskForEachAntTarget() {
         File buildFile = new File(project.projectDir, 'build.xml')
-        buildFile.withWriter {Writer writer ->
+        buildFile.withWriter { Writer writer ->
             def xml = new MarkupBuilder(writer)
             xml.project {
                 target(name: 'target1', depends: 'target2, target3')
@@ -118,7 +121,7 @@ class DefaultAntBuilderTest {
         ant.test()
         Thread.currentThread().setContextClassLoader(original)
     }
-    
+
     @Test
     public void discardsTasksAfterExecution() {
         ant.echo(message: 'message')
@@ -135,6 +138,30 @@ class DefaultAntBuilderTest {
         List children = field.get(target)
         assertThat(children, isEmpty())
     }
+
+    @Test
+    public void testTaskRename() {
+        File buildFile = new File(project.projectDir, 'build.xml')
+        buildFile.withWriter { Writer writer ->
+            def xml = new MarkupBuilder(writer)
+            xml.project {
+                target(name: 'target1', depends: 'target2, target3')
+                target(name: 'target2')
+                target(name: 'target3')
+            }
+        }
+
+        ant.importBuild(buildFile) { taskName ->
+            'a-' + taskName
+        }
+
+        def task = project.tasks.'a-target1'
+        assertThat(task, instanceOf(AntTarget))
+        assertThat(task.target.name, equalTo('target1'))
+        assert task.taskDependencies.getDependencies(task).name.sort() == ["a-target2", "a-target3"]
+    }
+
+
 }
 
 public class TestTask extends Task {
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 c4082c8..dbdd3a9 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
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.internal.project
 
-import ch.qos.logback.classic.Level
-import org.apache.tools.ant.BuildException
 import org.apache.tools.ant.Project
 import org.apache.tools.ant.taskdefs.ConditionTask
 import org.gradle.api.GradleException
@@ -25,60 +23,32 @@ 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.project.ant.BasicAntBuilder
-import org.gradle.logging.ConfigureLogging
-import org.gradle.logging.TestAppender
+import org.gradle.api.logging.LogLevel
+import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.internal.classloader.DefaultClassLoaderFactory
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestOutputEventListener
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+
 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.internal.classloader.ClasspathUtil
 
 class DefaultIsolatedAntBuilderTest {
     private final ModuleRegistry moduleRegistry = new DefaultModuleRegistry()
     private final ClassPathRegistry registry = new DefaultClassPathRegistry(new DefaultClassPathProvider(moduleRegistry))
     private final DefaultIsolatedAntBuilder builder = new DefaultIsolatedAntBuilder(registry, new DefaultClassLoaderFactory())
-    private final TestAppender appender = new TestAppender()
-    @Rule public final ConfigureLogging logging = new ConfigureLogging(appender)
+    private final TestOutputEventListener outputEventListener = new TestOutputEventListener()
+    @Rule
+    public final ConfigureLogging logging = new ConfigureLogging(outputEventListener)
     private Collection<File> classpath
 
     @Before
     public void attachAppender() {
         classpath = registry.getClassPath("GROOVY").asFiles
-        logging.setLevel(Level.INFO);
-    }
-
-    @Test
-    public void executesClosureAgainstDifferentVersionOfAntAndGroovy() {
-        Object antBuilder = null
-        Object antProject = null
-        builder.withGroovy(classpath).execute {
-            antBuilder = delegate.builder
-            antProject = delegate.antProject
-        }
-        assertThat(antBuilder, notNullValue())
-        assertThat(antProject, notNullValue())
-
-        ClassLoader loader = antBuilder.class.classLoader
-        assertThat(loader, not(sameInstance(AntBuilder.classLoader)))
-
-        assertThat(antBuilder, not(instanceOf(BasicAntBuilder)))
-        assertThat(antBuilder, instanceOf(loader.loadClass(BasicAntBuilder.name)))
-
-        assertThat(antBuilder, not(instanceOf(AntBuilder)))
-        assertThat(antBuilder, instanceOf(loader.loadClass(AntBuilder.name)))
-
-        assertThat(antProject, not(instanceOf(Project)))
-        assertThat(antProject, instanceOf(loader.loadClass(Project.name)))
-
-        assertThat(loader.loadClass(AntBuilder.name).classLoader, sameInstance(loader.loadClass(Project.name).classLoader))
-
-        assertThat(loader.loadClass(BuildException.name), not(sameInstance(BuildException)))
-        assertThat(loader.loadClass(Closure.name), not(sameInstance(Closure)))
+        logging.setLevel(LogLevel.INFO);
     }
 
     @Test
@@ -102,7 +72,7 @@ class DefaultIsolatedAntBuilderTest {
     public void canAccessAntBuilderFromWithinClosures() {
         builder.execute {
             assertThat(ant, sameInstance(delegate))
-            
+
             ant.property(name: 'prop', value: 'a message')
             assertThat(project.properties.prop, equalTo('a message'))
         }
@@ -115,7 +85,7 @@ class DefaultIsolatedAntBuilderTest {
             echo('${message}')
         }
 
-        assertThat(appender.toString(), equalTo('[WARN [ant:echo] a message]'))
+        assertThat(outputEventListener.toString(), equalTo('[WARN [ant:echo] a message]'))
     }
 
     @Test
@@ -127,9 +97,9 @@ class DefaultIsolatedAntBuilderTest {
             loggingTask()
         }
 
-        assertThat(appender.toString(), containsString('[INFO a jcl log message]'))
-        assertThat(appender.toString(), containsString('[INFO an slf4j log message]'))
-        assertThat(appender.toString(), containsString('[INFO a log4j log message]'))
+        assertThat(outputEventListener.toString(), containsString('[INFO a jcl log message]'))
+        assertThat(outputEventListener.toString(), containsString('[INFO an slf4j log message]'))
+        assertThat(outputEventListener.toString(), containsString('[INFO a log4j log message]'))
     }
 
     @Test
@@ -140,21 +110,7 @@ class DefaultIsolatedAntBuilderTest {
     }
 
     @Test
-    public void cachesClassloaderForGivenClassPath() {
-        Object antBuilder1 = null
-        builder.execute {
-            antBuilder1 = delegate.builder
-        }
-        Object antBuilder2 = null
-        builder.withGroovy(classpath).execute {
-            antBuilder2 = delegate.builder
-        }
-
-        assertThat(antBuilder1.class, sameInstance(antBuilder2.class))
-    }
-
-    @Test
-    public void cachesClassloaderForGivenAntAndGroovyImplementationClassPath() {
+    public void reusesAntGroovyClassloader() {
         ClassLoader antClassLoader = null
         builder.withClasspath([new File("no-existo.jar")]).execute {
             antClassLoader = project.class.classLoader
@@ -168,6 +124,29 @@ class DefaultIsolatedAntBuilderTest {
     }
 
     @Test
+    public void reusesClassloaderForImplementation() {
+        ClassLoader loader1 = null
+        def classpath = [new File("no-existo.jar")]
+        builder.withClasspath(classpath).execute {
+            loader1 = delegate.antlibClassLoader
+        }
+        ClassLoader loader2 = null
+        builder.withClasspath(classpath).execute {
+            loader2 = delegate.antlibClassLoader
+        }
+
+        assertThat(loader1, sameInstance(loader2))
+
+
+        ClassLoader loader3 = null
+        builder.withClasspath(classpath + [new File("unknown.jar")]).execute {
+            loader3 = delegate.antlibClassLoader
+        }
+
+        assertThat(loader1, not(sameInstance(loader3)))
+    }
+
+    @Test
     public void setsContextClassLoader() {
         ClassLoader originalLoader = Thread.currentThread().contextClassLoader
         ClassLoader contextLoader = null
@@ -197,12 +176,3 @@ class DefaultIsolatedAntBuilderTest {
         }
     }
 }
-
-class TestAntTask extends Task {
-    @Override
-    void execute() {
-        org.apache.commons.logging.LogFactory.getLog('ant-test').info("a jcl log message")
-        org.slf4j.LoggerFactory.getLogger('ant-test').info("an slf4j log message")
-        org.apache.log4j.Logger.getLogger('ant-test').info("a log4j log message")
-    }
-}
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 66e15b1..e268e8b 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
@@ -18,6 +18,7 @@ package org.gradle.api.internal.project
 
 import org.apache.tools.ant.types.FileSet
 import org.gradle.api.*
+import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
@@ -27,32 +28,32 @@ import org.gradle.api.initialization.dsl.ScriptHandler
 import org.gradle.api.internal.*
 import org.gradle.api.internal.artifacts.ModuleInternal
 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.file.FileOperations
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ClassLoaderScope
-import org.gradle.api.internal.initialization.DefaultClassLoaderCache
 import org.gradle.api.internal.initialization.RootClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
+import org.gradle.api.internal.initialization.loadercache.DummyClassLoaderCache
+import org.gradle.api.internal.plugins.PluginManagerInternal
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
 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.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.initialization.ProjectAccessListener
 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.model.ModelRules
-import org.gradle.model.internal.ModelRegistry
-import org.gradle.model.internal.ModelRegistryBackedModelRules
+import org.gradle.model.internal.core.ModelCreatorFactory
+import org.gradle.model.internal.manage.schema.ModelSchemaStore
+import org.gradle.model.internal.registry.ModelRegistry
 import org.gradle.util.JUnit4GroovyMockery
 import org.gradle.util.TestClosure
 import org.gradle.util.TestUtil
@@ -62,6 +63,7 @@ import org.junit.Test
 import org.junit.runner.RunWith
 
 import java.awt.*
+import java.lang.reflect.Type
 import java.text.FieldPosition
 
 import static org.hamcrest.Matchers.*
@@ -93,11 +95,10 @@ class DefaultProjectTest {
     Factory<AntBuilder> antBuilderFactoryMock = context.mock(Factory.class)
     AntBuilder testAntBuilder
 
-    ConfigurationContainerInternal configurationContainerMock = context.mock(ConfigurationContainerInternal.class)
+    ConfigurationContainer configurationContainerMock = context.mock(ConfigurationContainer.class)
     RepositoryHandler repositoryHandlerMock = context.mock(RepositoryHandler.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)
     Gradle build = context.mock(GradleInternal)
@@ -107,8 +108,11 @@ class DefaultProjectTest {
     Instantiator instantiatorMock = context.mock(Instantiator)
     SoftwareComponentContainer softwareComponentsMock = context.mock(SoftwareComponentContainer.class)
     ProjectConfigurationActionContainer configureActions = context.mock(ProjectConfigurationActionContainer.class)
+    PluginManagerInternal pluginManager = context.mock(PluginManagerInternal.class)
+    PluginContainer pluginContainer = context.mock(PluginContainer.class)
 
-    ClassLoaderScope rootProjectClassLoaderScope = new RootClassLoaderScope(getClass().classLoader, new DefaultClassLoaderCache()).createChild()
+    ClassLoaderScope baseClassLoaderScope = new RootClassLoaderScope(getClass().classLoader, getClass().classLoader, new DummyClassLoaderCache())
+    ClassLoaderScope rootProjectClassLoaderScope = baseClassLoaderScope.createChild("root-project")
 
     @Before
     void setUp() {
@@ -137,52 +141,73 @@ class DefaultProjectTest {
             allowing(taskContainerMock).getTasksAsDynamicObject(); will(returnValue(new BeanDynamicObject(new TaskContainerDynamicObject(someTask: testTask))))
             allowing(taskContainerMock).all(withParam(notNullValue()))
             allowing(taskContainerMock).whenObjectRemoved(withParam(notNullValue()))
-            allowing(serviceRegistryMock).get(RepositoryHandler); will(returnValue(repositoryHandlerMock))
-            allowing(serviceRegistryMock).get(ConfigurationContainerInternal); will(returnValue(configurationContainerMock))
+            allowing(serviceRegistryMock).get((Type) RepositoryHandler); will(returnValue(repositoryHandlerMock))
+            allowing(serviceRegistryMock).get(ConfigurationContainer); will(returnValue(configurationContainerMock))
             allowing(serviceRegistryMock).get(ArtifactHandler); will(returnValue(context.mock(ArtifactHandler)))
             allowing(serviceRegistryMock).get(DependencyHandler); will(returnValue(dependencyHandlerMock))
-            allowing(serviceRegistryMock).get(ComponentMetadataHandler); will(returnValue(moduleHandlerMock))
-            allowing(serviceRegistryMock).get(SoftwareComponentContainer); will(returnValue(softwareComponentsMock))
+            allowing(serviceRegistryMock).get((Type) ComponentMetadataHandler); will(returnValue(moduleHandlerMock))
+            allowing(serviceRegistryMock).get((Type) SoftwareComponentContainer); will(returnValue(softwareComponentsMock))
             allowing(serviceRegistryMock).get(ProjectEvaluator); will(returnValue(projectEvaluator))
             allowing(serviceRegistryMock).getFactory(AntBuilder); will(returnValue(antBuilderFactoryMock))
-            allowing(serviceRegistryMock).get(PluginContainer); will(returnValue(pluginContainerMock))
-            allowing(serviceRegistryMock).get(ScriptHandler); will(returnValue(scriptHandlerMock))
-            allowing(serviceRegistryMock).get(LoggingManagerInternal); will(returnValue(loggingManagerMock))
-            allowing(serviceRegistryMock).get(StandardOutputCapture); will(returnValue(context.mock(StandardOutputCapture)))
-            allowing(serviceRegistryMock).get(ProjectRegistry); will(returnValue(projectRegistry))
+            allowing(serviceRegistryMock).get((Type) ScriptHandler); will(returnValue(scriptHandlerMock))
+            allowing(serviceRegistryMock).get((Type) LoggingManagerInternal); will(returnValue(loggingManagerMock))
+            allowing(serviceRegistryMock).get(projectRegistryType); will(returnValue(projectRegistry))
             allowing(serviceRegistryMock).get(DependencyMetaDataProvider); will(returnValue(dependencyMetaDataProviderMock))
-            allowing(serviceRegistryMock).get(FileResolver); will(returnValue([toString: {-> "file resolver" }] as FileResolver))
+            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(ScriptHandlerFactory); will(returnValue([toString: {-> "script plugin factory" }] as ScriptHandlerFactory))
-            allowing(serviceRegistryMock).get(ProjectConfigurationActionContainer); will(returnValue(configureActions))
+            allowing(serviceRegistryMock).get((Type) FileOperations); will(returnValue(fileOperationsMock))
+            allowing(serviceRegistryMock).get((Type) ProcessOperations); will(returnValue(processOperationsMock))
+            allowing(serviceRegistryMock).get((Type) ScriptPluginFactory); will(returnValue([toString: { -> "script plugin factory" }] as ScriptPluginFactory))
+            allowing(serviceRegistryMock).get((Type) ScriptHandlerFactory); will(returnValue([toString: { -> "script plugin factory" }] as ScriptHandlerFactory))
+            allowing(serviceRegistryMock).get((Type) ProjectConfigurationActionContainer); will(returnValue(configureActions))
+            allowing(serviceRegistryMock).get((Type) PluginManagerInternal); will(returnValue(pluginManager))
+            allowing(pluginManager).getPluginContainer(); will(returnValue(pluginContainer))
+
+            allowing(serviceRegistryMock).get((Type) DeferredProjectConfiguration); will(returnValue(context.mock(DeferredProjectConfiguration)))
+            allowing(serviceRegistryMock).get((Type) ProjectAccessListener); will(returnValue(context.mock(ProjectAccessListener)))
+
+            ITaskFactory taskFactoryMock = context.mock(ITaskFactory)
+            allowing(serviceRegistryMock).get(ITaskFactory); will(returnValue(taskFactoryMock))
+
             ModelRegistry modelRegistry = context.mock(ModelRegistry)
             ignoring(modelRegistry)
+            allowing(serviceRegistryMock).get((Type) ModelRegistry); will(returnValue(modelRegistry))
             allowing(serviceRegistryMock).get(ModelRegistry); will(returnValue(modelRegistry))
-            allowing(serviceRegistryMock).get(ModelRules); will(returnValue(new ModelRegistryBackedModelRules(modelRegistry)))
+
+            ModelSchemaStore modelSchemaStore = context.mock(ModelSchemaStore)
+            ignoring(modelSchemaStore)
+            allowing(serviceRegistryMock).get((Type) ModelSchemaStore); will(returnValue(modelSchemaStore))
+            allowing(serviceRegistryMock).get(ModelSchemaStore); will(returnValue(modelSchemaStore))
+
+            ModelCreatorFactory modelCreatorFactory = context.mock(ModelCreatorFactory)
+            ignoring(modelCreatorFactory)
+            allowing(serviceRegistryMock).get((Type) ModelCreatorFactory); will(returnValue(modelCreatorFactory))
+            allowing(serviceRegistryMock).get(ModelCreatorFactory); will(returnValue(modelCreatorFactory))
+
             Object listener = context.mock(ProjectEvaluationListener)
             ignoring(listener)
             allowing(build).getProjectEvaluationBroadcaster();
             will(returnValue(listener))
         }
 
-        // TODO - don't decorate the project objects
         AsmBackedClassGenerator classGenerator = new AsmBackedClassGenerator()
-        project = classGenerator.newInstance(DefaultProject.class, 'root', null, rootDir, script, build, projectServiceRegistryFactoryMock, rootProjectClassLoaderScope);
-        def child1ClassLoaderScope = rootProjectClassLoaderScope.createChild()
-        child1 = classGenerator.newInstance(DefaultProject.class, "child1", project, new File("child1"), script, build, projectServiceRegistryFactoryMock, child1ClassLoaderScope)
+        project = classGenerator.newInstance(DefaultProject.class, 'root', null, rootDir, script, build, projectServiceRegistryFactoryMock, rootProjectClassLoaderScope, baseClassLoaderScope);
+        def child1ClassLoaderScope = rootProjectClassLoaderScope.createChild("project-child1")
+        child1 = classGenerator.newInstance(DefaultProject.class, "child1", project, new File("child1"), script, build, projectServiceRegistryFactoryMock, child1ClassLoaderScope, baseClassLoaderScope)
         project.addChildProject(child1)
-        childchild = classGenerator.newInstance(DefaultProject.class, "childchild", child1, new File("childchild"), script, build, projectServiceRegistryFactoryMock, child1ClassLoaderScope.createChild())
+        childchild = classGenerator.newInstance(DefaultProject.class, "childchild", child1, new File("childchild"), script, build, projectServiceRegistryFactoryMock, child1ClassLoaderScope.createChild("project-childchild"), baseClassLoaderScope)
         child1.addChildProject(childchild)
-        child2 = classGenerator.newInstance(DefaultProject.class, "child2", project, new File("child2"), script, build, projectServiceRegistryFactoryMock, rootProjectClassLoaderScope.createChild())
+        child2 = classGenerator.newInstance(DefaultProject.class, "child2", project, new File("child2"), script, build, projectServiceRegistryFactoryMock, rootProjectClassLoaderScope.createChild("project-child2"), baseClassLoaderScope)
         project.addChildProject(child2)
         [project, child1, childchild, child2].each {
             projectRegistry.addProject(it)
         }
     }
 
+    Type getProjectRegistryType() {
+        return AbstractProject.class.getDeclaredMethod("getProjectRegistry").getGenericReturnType()
+    }
+
     //TODO please move more coverage to NewDefaultProjectTest
 
     @Test
@@ -299,22 +324,6 @@ class DefaultProjectTest {
     }
 
     @Test
-    void testUsePluginWithString() {
-        context.checking {
-            one(pluginContainerMock).apply('someplugin'); will(returnValue([:] as Plugin))
-        }
-        project.apply(plugin: 'someplugin')
-    }
-
-    @Test
-    void testUsePluginWithClass() {
-        context.checking {
-            one(pluginContainerMock).apply(Plugin); will(returnValue([:] as Plugin))
-        }
-        project.apply(plugin: Plugin)
-    }
-
-    @Test
     void testEvaluationDependsOn() {
         boolean mockReader2Finished = false
         boolean mockReader1Called = false
@@ -393,92 +402,9 @@ class DefaultProjectTest {
     }
 
     @Test
-    void testDependsOnWithNoEvaluation() {
-        boolean mockReaderCalled = false
-        final ProjectEvaluator mockReader = [evaluateProject: { DefaultProject project ->
-            mockReaderCalled = true
-            testScript
-        }] as ProjectEvaluator
-        child1.projectEvaluator = mockReader
-        project.dependsOn(child1.name, false)
-        assertFalse mockReaderCalled
-        assertEquals([child1] as Set, project.dependsOnProjects)
-        project.dependsOn(child2.path, false)
-        assertEquals([child1, child2] as Set, project.dependsOnProjects)
-    }
-
-    @Test
-    void testDependsOn() {
-        boolean mockReaderCalled = false
-        final ProjectEvaluator mockReader = [evaluate: { DefaultProject project, state ->
-            mockReaderCalled = true
-            testScript
-        }] as ProjectEvaluator
-        child1.projectEvaluator = mockReader
-        project.dependsOn(child1.name)
-        assertTrue mockReaderCalled
-        assertEquals([child1] as Set, project.dependsOnProjects)
-
-    }
-
-    @Test
-    void testChildrenDependsOnMe() {
-        project.childrenDependOnMe()
-        assertTrue(child1.dependsOnProjects.contains(project))
-        assertTrue(child2.dependsOnProjects.contains(project))
-        assertEquals(1, child1.dependsOnProjects.size())
-        assertEquals(1, child2.dependsOnProjects.size())
-    }
-
-    @Test
-    void testDependsOnChildren() {
-        context.checking {
-            never(projectEvaluator).evaluate(child1, child1.state)
-        }
-
-        project.dependsOnChildren()
-        context.assertIsSatisfied()
-        assertTrue(project.dependsOnProjects.contains(child1))
-        assertTrue(project.dependsOnProjects.contains(child2))
-        assertEquals(2, project.dependsOnProjects.size())
-    }
-
-    @Test
-    void testDependsOnChildrenIncludingEvaluate() {
-        context.checking {
-            one(projectEvaluator).evaluate(child1, child1.state)
-            one(projectEvaluator).evaluate(child2, child2.state)
-        }
-        project.dependsOnChildren(true)
-        assertTrue(project.dependsOnProjects.contains(child1))
-        assertTrue(project.dependsOnProjects.contains(child2))
-        assertEquals(2, project.dependsOnProjects.size())
-    }
-
-    @Test(expected = InvalidUserDataException)
-    void testDependsOnWithNullPath() {
-        project.dependsOn(null)
-    }
-
-    @Test(expected = InvalidUserDataException)
-    void testDependsOnWithEmptyPath() {
-        project.dependsOn('')
-    }
-
-    @Test(expected = UnknownProjectException)
-    void testDependsOnWithUnknownParentPath() {
-        project.dependsOn(child1.path + 'XXX')
-    }
-
-    @Test(expected = UnknownProjectException)
-    void testDependsOnWithUnknownProjectPath() {
-        project.dependsOn(child1.name + 'XXX')
-    }
-
-    @Test
     void testAddAndGetChildProject() {
-        ProjectInternal child1 = ['getName': {-> 'child1' }] as ProjectInternal
-        ProjectInternal child2 = ['getName': {-> 'child2' }] as ProjectInternal
+        ProjectInternal child1 = ['getName': { -> 'child1' }] as ProjectInternal
+        ProjectInternal child2 = ['getName': { -> 'child2' }] as ProjectInternal
 
         project.addChildProject(child1)
         assertEquals(2, project.childProjects.size())
@@ -599,111 +525,12 @@ class DefaultProjectTest {
     void testGetProjectWithClosure() {
         String newPropValue = 'someValue'
         assert child1.is(project.project("child1") {
-            newProp = newPropValue
+            ext.newProp = newPropValue
         })
         assertEquals(child1.newProp, newPropValue)
     }
 
     @Test
-    void testGetAllTasksRecursive() {
-        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
-        expectedMap[child1] = [child1Task] as TreeSet
-        expectedMap[child2] = [child2Task] as TreeSet
-        expectedMap[childchild] = [] as TreeSet
-
-        context.checking {
-            atMost(1).of(taskContainerMock).size(); will(returnValue(1))
-            one(taskContainerMock).iterator(); will(returnValue(([projectTask] as Set).iterator()))
-            atMost(1).of(taskContainerMock).size(); will(returnValue(1))
-            one(taskContainerMock).iterator(); will(returnValue(([child1Task] as Set).iterator()))
-            atMost(1).of(taskContainerMock).size(); will(returnValue(1))
-            one(taskContainerMock).iterator(); will(returnValue(([child2Task] as Set).iterator()))
-            atMost(1).of(taskContainerMock).size(); will(returnValue(0))
-            one(taskContainerMock).iterator(); will(returnValue(([] as Set).iterator()))
-        }
-
-        assertEquals(expectedMap, project.getAllTasks(true))
-    }
-
-    @Test
-    void testGetAllTasksNonRecursive() {
-        Task projectTask = TestUtil.createTask(DefaultTask.class)
-
-        Map expectedMap = new TreeMap()
-        expectedMap[project] = [projectTask] as TreeSet
-
-        context.checking {
-            allowing(taskContainerMock).size(); will(returnValue(1))
-            one(taskContainerMock).iterator(); will(returnValue(([projectTask] as Set).iterator()))
-        }
-
-        assertEquals(expectedMap, project.getAllTasks(false))
-    }
-
-    @Test
-    void testGetTasksByNameRecursive() {
-        Task projectTask = TestUtil.createTask(DefaultTask.class)
-        Task child1Task = TestUtil.createTask(DefaultTask.class)
-
-        context.checking {
-            one(taskContainerMock).findByName('task'); will(returnValue(projectTask))
-            one(taskContainerMock).findByName('task'); will(returnValue(child1Task))
-            one(taskContainerMock).findByName('task'); will(returnValue(null))
-            one(taskContainerMock).findByName('task'); will(returnValue(null))
-        }
-
-        assertEquals([projectTask, child1Task] as Set, project.getTasksByName('task', true))
-    }
-
-    @Test
-    void testGetTasksByNameNonRecursive() {
-        Task projectTask = TestUtil.createTask(DefaultTask.class)
-
-        context.checking {
-            one(taskContainerMock).findByName('task'); will(returnValue(projectTask))
-        }
-
-        assertEquals([projectTask] as Set, project.getTasksByName('task', false))
-    }
-
-    @Test(expected = InvalidUserDataException)
-    void testGetTasksWithEmptyName() {
-        project.getTasksByName('', true)
-    }
-
-    @Test(expected = InvalidUserDataException)
-    void testGetTasksWithNullName() {
-        project.getTasksByName(null, true)
-    }
-
-    @Test
-    void testGetTasksWithUnknownName() {
-        context.checking {
-            allowing(taskContainerMock).findByName('task'); will(returnValue(null))
-        }
-
-        assertEquals([] as Set, project.getTasksByName('task', true))
-        assertEquals([] as Set, project.getTasksByName('task', false))
-    }
-
-    private List addTestTaskToAllProjects(String name) {
-        List tasks = []
-        project.allprojects.each { Project project ->
-            tasks << addTestTask(project, name)
-        }
-        tasks
-    }
-
-    private Task addTestTask(Project project, String name) {
-        new DefaultTask(project, name)
-    }
-
-    @Test
     void testMethodMissing() {
         boolean closureCalled = false
         Closure testConfigureClosure = { closureCalled = true }
@@ -731,7 +558,7 @@ def scriptMethod(Closure closure) {
         String propertyName = 'propName'
         String expectedValue = 'somevalue'
 
-        project."$propertyName" = expectedValue
+        project.ext."$propertyName" = expectedValue
         assertEquals(expectedValue, project."$propertyName")
         assertEquals(expectedValue, child1."$propertyName")
     }
@@ -762,7 +589,7 @@ def scriptMethod(Closure closure) {
         String propertyName = 'archivesBaseName'
         String expectedValue = 'somename'
 
-        project.archivesBaseName = expectedValue
+        project.ext.archivesBaseName = expectedValue
         project.convention.plugins.test = new TestConvention()
         project.convention.archivesBaseName = 'someothername'
         project."$propertyName" = expectedValue
@@ -772,7 +599,7 @@ def scriptMethod(Closure closure) {
 
     @Test
     void testPropertyMissingWithNullProperty() {
-        project.nullProp = null
+        project.ext.nullProp = null
         assertNull(project.nullProp)
         assert project.hasProperty('nullProp')
     }
@@ -803,7 +630,7 @@ def scriptMethod(Closure closure) {
             ignoring(taskContainerMock)
             allowing(serviceRegistryMock).get(ServiceRegistryFactory); will(returnValue({} as ServiceRegistryFactory))
         }
-        project.additional = 'additional'
+        project.ext.additional = 'additional'
 
         Map properties = project.properties
         assertEquals(properties.name, 'root')
@@ -812,8 +639,8 @@ def scriptMethod(Closure closure) {
     }
 
     @Test
-    void testAdditionalPropertiesAreInheritable() {
-        project.somename = 'somevalue'
+    void testExtraPropertiesAreInheritable() {
+        project.ext.somename = 'somevalue'
         assertTrue(project.inheritedScope.hasProperty('somename'))
         assertEquals(project.inheritedScope.getProperty('somename'), 'somevalue')
     }
@@ -828,7 +655,7 @@ def scriptMethod(Closure closure) {
 
     @Test
     void testInheritedPropertiesAreInheritable() {
-        project.somename = 'somevalue'
+        project.ext.somename = 'somevalue'
         assertTrue(child1.inheritedScope.hasProperty('somename'))
         assertEquals(child1.inheritedScope.getProperty('somename'), 'somevalue')
     }
@@ -866,59 +693,6 @@ def scriptMethod(Closure closure) {
     }
 
     @Test
-    public void testDir() {
-        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).create('dir1', Directory); will(returnValue(dirTask1))
-            one(taskContainerMock).findByName('dir1/dir2'); will(returnValue(null))
-            one(taskContainerMock).create('dir1/dir2', Directory); will(returnValue(dirTask12))
-            one(taskContainerMock).findByName('dir1/dir2/dir3'); will(returnValue(null))
-            one(taskContainerMock).create('dir1/dir2/dir3', Directory); will(returnValue(dirTask123))
-        }
-        assertSame(dirTask123, project.dir('dir1/dir2/dir3'));
-    }
-
-    @Test
-    public void testDirWithExistingParentDirTask() {
-        Task dirTask1 = TestUtil.createTask(Directory.class)
-        context.checking {
-            one(taskContainerMock).findByName('dir1'); will(returnValue(null))
-            one(taskContainerMock).create('dir1', Directory); will(returnValue(dirTask1))
-        }
-        project.dir('dir1')
-
-        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).create('dir1/dir4', Directory); will(returnValue(dirTask14))
-        }
-        assertSame(dirTask14, project.dir('dir1/dir4'))
-    }
-
-    @Test
-    public void testDirWithConflictingNonDirTask() {
-        Task dirTask14 = TestUtil.createTask(DefaultTask.class)
-
-        Task dirTask1 = TestUtil.createTask(Directory.class)
-        context.checking {
-            one(taskContainerMock).findByName('dir1'); will(returnValue(null))
-            one(taskContainerMock).create('dir1', Directory); will(returnValue(dirTask1))
-            one(taskContainerMock).findByName('dir1/dir4'); will(returnValue(dirTask14))
-        }
-
-        try {
-            project.dir('dir1/dir4')
-            fail()
-        } catch (InvalidUserDataException e) {
-            assertThat(e.message, equalTo("Cannot add directory task 'dir1/dir4' as a non-directory task with this name already exists."))
-        }
-    }
-
-    @Test
     void testCachingOfAnt() {
         assertSame(testAntBuilder, project.ant)
         assert project.ant.is(project.ant)
@@ -986,12 +760,12 @@ def scriptMethod(Closure closure) {
         if (configureMethod == 'configure') {
             project."$configureMethod" projectsToCheck as java.util.List,
                     {
-                        testSubProp = propValue
+                        ext.testSubProp = propValue
                     }
         } else {
             project."$configureMethod"(
                     {
-                        testSubProp = propValue
+                        ext.testSubProp = propValue
                     })
         }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DeferredProjectConfigurationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DeferredProjectConfigurationTest.groovy
new file mode 100644
index 0000000..fac41b1
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DeferredProjectConfigurationTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.SetSystemProperties
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class DeferredProjectConfigurationTest extends Specification {
+
+    def project = TestUtil.createRootProject()
+    def config = new DeferredProjectConfiguration(project)
+
+    @Rule
+    SetSystemProperties setSystemProperties
+
+    def "can add config and fire"() {
+        given:
+        def events = []
+
+        when:
+        config.add { events << "a" }
+        config.add { events << "b" }
+
+        and:
+        config.fire()
+
+        then:
+        events == ["a", "b"]
+    }
+
+    def "fire is idempotent"() {
+        given:
+        def events = []
+
+        when:
+        config.add { events << "a" }
+        config.add { events << "b" }
+
+        and:
+        3.times { config.fire() }
+
+        then:
+        events == ["a", "b"]
+    }
+
+    def "cannot add actions once fired"() {
+        when:
+        config.fire()
+
+        and:
+        config.add {}
+
+        then:
+        def e = thrown IllegalStateException
+        e.cause == null
+    }
+
+    def "can get trace info"() {
+        given:
+        System.setProperty("org.gradle.trace.deferred.project.configuration", "true")
+
+        when:
+        config.fire()
+
+        and:
+        config.add {}
+
+        then:
+        def e = thrown IllegalStateException
+        e.cause instanceof Exception
+    }
+
+}
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 3c9a0bb..1411a9e 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
@@ -15,23 +15,25 @@
  */
 
 package org.gradle.api.internal.project
-
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ConfigurationContainer
 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.TestUtil
 import spock.lang.Specification
 
+import static org.gradle.util.TestUtil.createChildProject
+
 class NewDefaultProjectTest extends Specification {
 
-    def project = TestUtil.createRootProject()
+    DefaultProject root = TestUtil.createRootProject()
 
     void "delegates to artifacts handler"() {
         def handler = Mock(ArtifactHandler)
-        project.artifactHandler = handler
+        root.artifactHandler = handler
 
         when:
-        project.artifacts {
+        root.artifacts {
             add('conf', 'art')
         }
 
@@ -41,10 +43,10 @@ class NewDefaultProjectTest extends Specification {
 
     void "delegates to dependency handler"() {
         def handler = Mock(DependencyHandler)
-        project.dependencyHandler = handler
+        root.dependencyHandler = handler
 
         when:
-        project.dependencies {
+        root.dependencies {
             add('conf', 'dep')
         }
 
@@ -54,13 +56,92 @@ class NewDefaultProjectTest extends Specification {
 
     void "delegates to configuration container"() {
         Closure cl = {}
-        def container = Mock(ConfigurationContainerInternal)
-        project.configurationContainer = container
+        def container = Mock(ConfigurationContainer)
+        root.configurationContainer = container
 
         when:
-        project.configurations cl
+        root.configurations cl
 
         then:
         1 * container.configure(cl)
     }
+
+    def "provides all tasks recursively"() {
+        def a = createChildProject(root, "a")
+
+        [root, a].each { it.task "foo"; it.task "bar" }
+
+        when:
+        def rootTasks = root.getAllTasks(true)
+        def aTasks = a.getAllTasks(true)
+
+        then:
+        rootTasks.size() == 2
+        rootTasks[root]*.path as Set == [":bar", ":foo"] as Set
+        rootTasks[a]*.path as Set == [":a:bar", ":a:foo"] as Set
+
+        aTasks.size() == 1
+        aTasks[a]*.path as Set == [":a:bar", ":a:foo"] as Set
+    }
+
+    def "provides all tasks non-recursively"() {
+        def a = createChildProject(root, "a")
+
+        [root, a].each { it.task "foo"; it.task "bar" }
+
+        when:
+        def rootTasks = root.getAllTasks(false)
+        def aTasks = a.getAllTasks(false)
+
+        then:
+        rootTasks.size() == 1
+        rootTasks[root]*.path as Set == [":bar", ":foo"] as Set
+
+        aTasks.size() == 1
+        aTasks[a]*.path as Set == [":a:bar", ":a:foo"] as Set
+    }
+
+    def "provides task by name recursively"() {
+        def a = createChildProject(root, "a")
+
+        [root, a].each { it.task "foo"; it.task "bar" }
+
+        when:
+        def rootTasks = root.getTasksByName("foo", true)
+        def aTasks = a.getTasksByName("bar", true)
+
+        then:
+        rootTasks*.path as Set == [":a:foo", ":foo"] as Set
+        aTasks*.path == [":a:bar"]
+    }
+
+    def "provides task by name non-recursively"() {
+        def a = createChildProject(root, "a")
+
+        [root, a].each { it.task "foo"; it.task "bar" }
+
+        when:
+        def rootTasks = root.getTasksByName("foo", false)
+        def aTasks = a.getTasksByName("bar", false)
+
+        then:
+        rootTasks*.path == [":foo"]
+        aTasks*.path == [":a:bar"]
+    }
+
+    def "does not allow asking for tasks using empty name"() {
+        when: root.getTasksByName('', true)
+        then: thrown(InvalidUserDataException)
+
+        when: root.getTasksByName(null, true)
+        then: thrown(InvalidUserDataException)
+    }
+
+    def "allows asking for unknown tasks"() {
+        root.task "bar"
+
+        expect:
+        root.getTasksByName('foo', true).empty
+        root.getTasksByName('foo', false).empty
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.groovy
index 60e5e07..47ca84a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.groovy
@@ -19,7 +19,7 @@ package org.gradle.api.internal.project
 import org.gradle.api.initialization.ProjectDescriptor
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.initialization.ClassLoaderScope
-import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.groovy.scripts.NonExistentFileScriptSource
 import org.gradle.groovy.scripts.UriScriptSource
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.scopes.ServiceRegistryFactory
@@ -28,7 +28,8 @@ import org.junit.Rule
 import spock.lang.Specification
 
 class ProjectFactoryTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     def instantiator = Mock(Instantiator)
     def projectDescriptor = Stub(ProjectDescriptor)
     def gradle = Stub(GradleInternal)
@@ -36,7 +37,8 @@ class ProjectFactoryTest extends Specification {
     def projectRegistry = Mock(ProjectRegistry)
     def project = Stub(DefaultProject)
     def factory = new ProjectFactory(instantiator, projectRegistry)
-    def classLoaderScope = Mock(ClassLoaderScope)
+    def rootProjectScope = Mock(ClassLoaderScope)
+    def baseScope = Mock(ClassLoaderScope)
 
     def setup() {
         gradle.serviceRegistryFactory >> serviceRegistryFactory
@@ -52,11 +54,11 @@ class ProjectFactoryTest extends Specification {
         projectDescriptor.buildFile >> buildFile
 
         when:
-        def result = factory.createProject(projectDescriptor, null, gradle, classLoaderScope)
+        def result = factory.createProject(projectDescriptor, null, gradle, rootProjectScope, baseScope)
 
         then:
         result == project
-        1 * instantiator.newInstance(DefaultProject, "name", null, projectDir, { it instanceof UriScriptSource }, gradle, serviceRegistryFactory, classLoaderScope) >> project
+        1 * instantiator.newInstance(DefaultProject, "name", null, projectDir, { it instanceof UriScriptSource }, gradle, serviceRegistryFactory, rootProjectScope, baseScope) >> project
         1 * projectRegistry.addProject(project)
     }
 
@@ -70,11 +72,11 @@ class ProjectFactoryTest extends Specification {
         projectDescriptor.buildFile >> buildFile
 
         when:
-        def result = factory.createProject(projectDescriptor, null, gradle, classLoaderScope)
+        def result = factory.createProject(projectDescriptor, null, gradle, rootProjectScope, baseScope)
 
         then:
         result == project
-        1 * instantiator.newInstance(DefaultProject, "name", null, projectDir, { it instanceof StringScriptSource }, gradle, serviceRegistryFactory, classLoaderScope) >> project
+        1 * instantiator.newInstance(DefaultProject, "name", null, projectDir, { it instanceof NonExistentFileScriptSource }, gradle, serviceRegistryFactory, rootProjectScope, baseScope) >> project
         1 * projectRegistry.addProject(project)
     }
 
@@ -89,11 +91,11 @@ class ProjectFactoryTest extends Specification {
         projectDescriptor.buildFile >> buildFile
 
         when:
-        def result = factory.createProject(projectDescriptor, parent, gradle, classLoaderScope)
+        def result = factory.createProject(projectDescriptor, parent, gradle, rootProjectScope, baseScope)
 
         then:
         result == project
-        1 * instantiator.newInstance(DefaultProject, "name", parent, projectDir, _, gradle, serviceRegistryFactory, classLoaderScope) >> project
+        1 * instantiator.newInstance(DefaultProject, "name", parent, projectDir, _, gradle, serviceRegistryFactory, rootProjectScope, baseScope) >> project
         1 * parent.addChildProject(project)
         1 * projectRegistry.addProject(project)
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestAntTask.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestAntTask.java
new file mode 100644
index 0000000..0fa3bda
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestAntTask.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.apache.tools.ant.Task;
+
+public class TestAntTask extends Task {
+    @Override
+    public void execute() {
+        org.apache.commons.logging.LogFactory.getLog("ant-test").info("a jcl log message");
+        org.slf4j.LoggerFactory.getLogger("ant-test").info("an slf4j log message");
+        org.apache.log4j.Logger.getLogger("ant-test").info("a log4j log message");
+    }
+}
\ No newline at end of file
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
deleted file mode 100644
index 87bde21..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestPlugin2.groovy
+++ /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.project
-
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-
-class TestPlugin2 implements Plugin<Project> {
-    void apply(Project project) {
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestRuleSource.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestRuleSource.groovy
new file mode 100644
index 0000000..63fe4d2
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/TestRuleSource.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.model.RuleSource
+
+class TestRuleSource extends RuleSource {
+}
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 f5f6fe0..26856eb 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
@@ -22,9 +22,21 @@ import org.gradle.api.GradleException;
 import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.internal.ClassGenerator;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.DefaultProject;
-import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputDirectories;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.SkipWhenEmpty;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.TaskValidationException;
 import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
 import org.gradle.internal.UncheckedException;
 import org.gradle.test.fixtures.file.TestFile;
@@ -42,7 +54,12 @@ import spock.lang.Issue;
 
 import java.io.File;
 import java.lang.reflect.Field;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Callable;
 
 import static org.gradle.util.Matchers.isEmpty;
@@ -82,12 +99,13 @@ public class AnnotationProcessingTaskFactoryTest {
 
     private <T extends Task> T expectTaskCreated(final Class<T> type, final Object... params) {
         DefaultProject project = TestUtil.createRootProject();
+        final Class<? extends T> decorated = project.getServices().get(ClassGenerator.class).generate(type);
         T task = AbstractTask.injectIntoNewInstance(project, "task", new Callable<T>() {
             public T call() throws Exception {
                 if (params.length > 0) {
-                    return type.cast(type.getConstructors()[0].newInstance(params));
+                    return type.cast(decorated.getConstructors()[0].newInstance(params));
                 } else {
-                    return type.newInstance();
+                    return decorated.newInstance();
                 }
             }
         });
@@ -593,7 +611,7 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     @Test
-    @Issue("http://issues.gradle.org/browse/GRADLE-2815")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2815")
     public void registersSpecifiedBooleanInputValue() {
         TaskWithBooleanInput task = expectTaskCreated(TaskWithBooleanInput.class, true);
         assertThat(task.getInputs().getProperties().get("inputValue"), equalTo((Object) true));
@@ -681,6 +699,30 @@ public class AnnotationProcessingTaskFactoryTest {
                 "No value has been specified for property 'bean.inputFile'.");
     }
 
+    @Test
+    public void taskActionsRegisteredByProcessingAnnotationsAreNotConsideredCustom() {
+        TaskInternal task = expectTaskCreated(TestTask.class, new Object[]{null});
+        assertThat(task.isHasCustomActions(), equalTo(false));
+    }
+
+    @Test
+    public void validationActionsAreNotConsideredCustom() {
+        TaskInternal task = expectTaskCreated(TaskWithInputFile.class, new Object[]{null});
+        assertThat(task.isHasCustomActions(), equalTo(false));
+    }
+
+    @Test
+    public void directoryCreationActionsAreNotConsideredCustom() {
+        TaskInternal task = expectTaskCreated(TaskWithOutputDir.class, new Object[]{null});
+        assertThat(task.isHasCustomActions(), equalTo(false));
+    }
+
+    @Test
+    public void fileCreationActionsAreNotConsideredCustom() {
+        TaskInternal task = expectTaskCreated(TaskWithOutputFile.class, new Object[]{null});
+        assertThat(task.isHasCustomActions(), equalTo(false));
+    }
+
     private void assertValidationFails(TaskInternal task, String... expectedErrorMessages) {
         try {
             task.execute();
@@ -919,14 +961,15 @@ public class AnnotationProcessingTaskFactoryTest {
     }
     
     public static class TaskWithOptionalOutputFile extends DefaultTask {
-        @OutputFile @Optional
+        @OutputFile @org.gradle.api.tasks.Optional
         public File getOutputFile() {
             return null;
         }
     }
 
     public static class TaskWithOptionalOutputFiles extends DefaultTask {
-        @OutputFiles @Optional
+        @OutputFiles
+        @org.gradle.api.tasks.Optional
         public List<File> getOutputFiles() {
             return null;
         }
@@ -959,14 +1002,14 @@ public class AnnotationProcessingTaskFactoryTest {
     }
     
     public static class TaskWithOptionalOutputDir extends DefaultTask {
-        @OutputDirectory @Optional
+        @OutputDirectory @org.gradle.api.tasks.Optional
         public File getOutputDir() {
             return null;
         }
     }
 
     public static class TaskWithOptionalOutputDirs extends DefaultTask {
-        @OutputDirectories @Optional
+        @OutputDirectories @org.gradle.api.tasks.Optional
         public File getOutputDirs() {
             return null;
         }
@@ -1002,7 +1045,7 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     public static class TaskWithOptionalInputFile extends DefaultTask {
-        @InputFile @Optional
+        @InputFile @org.gradle.api.tasks.Optional
         public File getInputFile() {
             return null;
         }
@@ -1048,7 +1091,7 @@ public class AnnotationProcessingTaskFactoryTest {
     }
 
     public static class TaskWithOptionalNestedBean extends DefaultTask {
-        @Nested @Optional
+        @Nested @org.gradle.api.tasks.Optional
         public Bean getBean() {
             return null;
         }
@@ -1057,7 +1100,7 @@ public class AnnotationProcessingTaskFactoryTest {
     public static class TaskWithOptionalNestedBeanWithPrivateType extends DefaultTask {
         Bean2 bean = new Bean2();
 
-        @Nested @Optional
+        @Nested @org.gradle.api.tasks.Optional
         public Bean getBean() {
             return null;
         }
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 f5517b7..aff351c 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
@@ -20,7 +20,9 @@ import org.gradle.api.Action
 import org.gradle.api.DefaultTask
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Task
+import org.gradle.api.internal.AbstractTask
 import org.gradle.api.internal.ClassGenerator
+import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.tasks.TaskInstantiationException
 import org.gradle.internal.reflect.Instantiator
@@ -73,6 +75,17 @@ class TaskFactoryTest extends Specification {
         task instanceof TestDefaultTask
     }
 
+    public void testCreateTaskWhereSuperTypeOfDefaultImplementationRequested() {
+        when:
+        Task task = taskFactory.createTask([name: 'task', type: type])
+
+        then:
+        task instanceof DefaultTask
+
+        where:
+        type << [Task, TaskInternal, AbstractTask, DefaultTask]
+    }
+
     public void instantiatesAnInstanceOfTheDecoratedTaskType() {
         when:
         Task task = taskFactory.createTask([name: 'task', type: TestDefaultTask.class])
@@ -108,7 +121,6 @@ class TaskFactoryTest extends Specification {
         then:
         exception = thrown()
         exception.message == "Could not create task 'task': Unknown argument(s) in task definition: [Type]"
-
     }
 
     public void testCreateTaskWithAction() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/CachingResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/CachingResourceTest.groovy
deleted file mode 100644
index f8ae030..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/CachingResourceTest.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.resource
-
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
- at RunWith(JMock.class)
-class CachingResourceTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final Resource target = context.mock(Resource.class)
-    private final CachingResource resource = new CachingResource(target)
-
-    @Test
-    public void fetchesAndCachesContentWhenExistenceIsChecked() {
-        context.checking {
-            one(target).getText()
-            will(returnValue('content'))
-        }
-
-        assertTrue(resource.exists)
-        assertThat(resource.text, equalTo('content'))
-    }
-
-    @Test
-    public void fetchesAndCachesContentWhenContentIsRead() {
-        context.checking {
-            one(target).getText()
-            will(returnValue('content'))
-        }
-
-        assertThat(resource.text, equalTo('content'))
-        assertTrue(resource.exists)
-    }
-    
-    @Test
-    public void fetchesAndCachesContentForResourceThatDoesNotExist() {
-        context.checking {
-            one(target).getText()
-            will(returnValue(null))
-        }
-
-        assertThat(resource.text, nullValue())
-        assertFalse(resource.exists)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/StringResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/StringResourceTest.groovy
deleted file mode 100644
index 9cd4ba0..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/StringResourceTest.groovy
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.gradle.api.internal.resource
-
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-import org.junit.Test
-
-class StringResourceTest {
-    private final StringResource resource = new StringResource('displayname', 'text')
-
-    @Test
-    public void hasTextContent() {
-         assertThat(resource.text, equalTo('text'))
-    }
-
-    @Test
-    public void exists() {
-         assertTrue(resource.exists)
-    }
-
-    @Test
-    public void hasNoFile() {
-         assertThat(resource.file, nullValue())
-    }
-
-    @Test
-    public void hasNoURI() {
-        assertThat(resource.URI, nullValue())
-    }
-}
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
deleted file mode 100644
index beb8119..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/resource/UriResourceTest.groovy
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.gradle.api.internal.resource
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TestPrecondition
-import org.junit.Assume
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-
-import static org.hamcrest.Matchers.equalTo
-import static org.hamcrest.Matchers.nullValue
-import static org.junit.Assert.*
-
-class UriResourceTest {
-    private TestFile testDir;
-    private File file;
-    private URI fileUri;
-    @Rule
-    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-
-    @Before
-    public void setUp() throws URISyntaxException {
-        testDir = tmpDir.createDir('dir');
-        file = new File(testDir, 'build.script');
-        fileUri = file.toURI();
-    }
-
-    private URI createJar() throws URISyntaxException {
-        TestFile jarFile = tmpDir.testDirectory.file('test.jar');
-        testDir.file('ignoreme').write('content');
-        testDir.zipTo(jarFile);
-        return new URI("jar:${jarFile.toURI()}!/build.script")
-    }
-
-    @Test
-    public void canConstructResourceFromFile() {
-        UriResource resource = new UriResource('<display-name>', file);
-        assertThat(resource.file, equalTo(file));
-        assertThat(resource.URI, equalTo(fileUri));
-    }
-
-    @Test
-    public void canConstructResourceFromFileURI() {
-        UriResource resource = new UriResource('<display-name>', fileUri);
-        assertThat(resource.file, equalTo(file));
-        assertThat(resource.URI, equalTo(fileUri));
-    }
-
-    @Test
-    public void canConstructResourceFromJarURI() {
-        URI jarUri = createJar()
-        UriResource resource = new UriResource('<display-name>', jarUri);
-        assertThat(resource.file, nullValue());
-        assertThat(resource.URI, equalTo(jarUri));
-    }
-
-    @Test
-    public void readsFileContentWhenFileExists() throws IOException {
-        file.text = '<content>'
-
-        UriResource resource = new UriResource('<display-name>', file);
-        assertTrue(resource.exists)
-        assertThat(resource.text, equalTo('<content>'));
-    }
-
-    @Test
-    public void hasNoContentWhenFileDoesNotExist() {
-        UriResource resource = new UriResource('<display-name>', file);
-        assertFalse(resource.exists)
-        try {
-            resource.text
-            fail()
-        } catch (ResourceNotFoundException e) {
-            assertThat(e.message, equalTo("Could not read <display-name> '$file' as it does not exist." as String))
-        }
-    }
-
-    @Test
-    public void hasNoContentWhenFileIsADirectory() {
-        TestFile dir = testDir.file('somedir').createDir()
-        UriResource resource = new UriResource('<display-name>', dir);
-        assertTrue(resource.exists)
-        try {
-            resource.text
-            fail()
-        } catch (ResourceException e) {
-            assertThat(e.message, equalTo("Could not read <display-name> '$dir' as it is a directory." as String))
-        }
-    }
-    
-    @Test
-    public void readsFileContentUsingFileUriWhenFileExists() {
-        file.text = '<content>'
-
-        UriResource resource = new UriResource('<display-name>', fileUri);
-        assertTrue(resource.exists)
-        assertThat(resource.text, equalTo('<content>'));
-    }
-
-    @Test
-    public void hasNoContentWhenUsingFileUriAndFileDoesNotExist() {
-        UriResource resource = new UriResource('<display-name>', fileUri);
-        assertFalse(resource.exists)
-        try {
-            resource.text
-            fail()
-        } catch (ResourceNotFoundException e) {
-            assertThat(e.message, equalTo("Could not read <display-name> '$file' as it does not exist." as String))
-        }
-    }
-
-    @Test
-    public void readsFileContentUsingJarUriWhenFileExists() {
-        file.text = '<content>'
-
-        UriResource resource = new UriResource('<display-name>', createJar());
-        assertTrue(resource.exists)
-        assertThat(resource.text, equalTo('<content>'));
-    }
-
-    @Test
-    public void hasNoContentWhenUsingJarUriAndFileDoesNotExistInJar() {
-        URI jarUri = createJar()
-        UriResource resource = new UriResource('<display-name>', jarUri);
-        assertFalse(resource.exists)
-        try {
-            resource.text
-            fail()
-        } catch (ResourceNotFoundException e) {
-            assertThat(e.message, equalTo("Could not read <display-name> '$jarUri' as it does not exist." as String))
-        }
-    }
-
-    @Test
-    public void hasNoContentWhenUsingHttpUriAndFileDoesNotExist() {
-        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)
-        try {
-            resource.text
-            fail()
-        } catch (ResourceNotFoundException e) {
-            assertThat(e.message, equalTo("Could not read <display-name> 'http://www.gradle.org/unknown.txt' as it does not exist." as String))
-        }
-    }
-
-    @Test
-    public void usesFilePathToBuildDisplayNameWhenUsingFile() {
-        UriResource resource = new UriResource("<file-type>", file);
-        assertThat(resource.displayName, equalTo(String.format("<file-type> '%s'", file.absolutePath)));
-    }
-
-    @Test
-    public void usesFilePathToBuildDisplayNameWhenUsingFileUri() {
-        UriResource resource = new UriResource("<file-type>", fileUri);
-        assertThat(resource.displayName, equalTo(String.format("<file-type> '%s'", file.absolutePath)));
-    }
-
-    @Test
-    public void usesUriToBuildDisplayNameWhenUsingHttpUri() {
-        UriResource resource = new UriResource("<file-type>", new URI("http://www.gradle.org/unknown.txt"));
-        assertThat(resource.displayName, equalTo('<file-type> \'http://www.gradle.org/unknown.txt\''))
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/AbstractTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/AbstractTextResourceTest.groovy
new file mode 100644
index 0000000..c388007
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/AbstractTextResourceTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import com.google.common.base.Charsets
+
+import org.gradle.api.resources.TextResource
+
+import spock.lang.Specification
+
+abstract class AbstractTextResourceTest extends Specification {
+    TextResource resource
+
+    def "read as string"() {
+        expect:
+        resource.asString() == "contents"
+    }
+
+    def "read as reader"() {
+        expect:
+        resource.asReader().readLine() == "contents"
+    }
+
+    def "read as file"() {
+        expect:
+        resource.asFile().text == "contents"
+    }
+
+    def "read as file with different encoding"() {
+        expect:
+        resource.asFile(Charsets.UTF_16.name()).getText(Charsets.UTF_16.name()) == "contents"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResourceTest.groovy
new file mode 100644
index 0000000..539fa02
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/CharSourceBackedTextResourceTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import com.google.common.io.CharSource
+import spock.lang.Specification
+
+class CharSourceBackedTextResourceTest extends Specification {
+
+    def "can use char source text resource"() {
+        when:
+        def r = new CharSourceBackedTextResource(CharSource.wrap("foo"))
+
+        then:
+        r.asString() == "foo"
+        r.asReader().text == "foo"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTarArchiveTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTarArchiveTextResourceTest.groovy
new file mode 100644
index 0000000..a5ed9c4
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTarArchiveTextResourceTest.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import com.google.common.base.Charsets
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.util.TestUtil
+
+class FileCollectionBackedTarArchiveTextResourceTest extends AbstractTextResourceTest {
+    def setup() {
+        def project = TestUtil.createRootProject()
+        def archive = project.file("archive.tar.gz")
+        def archiveEntry = project.file("archive/path/to/text")
+        archiveEntry.parentFile.mkdirs()
+        archiveEntry.text = "contents"
+        project.ant.tar(basedir: project.file("archive"), destfile: archive, compression: "gzip")
+        resource = new FileCollectionBackedArchiveTextResource(project.services.get(FileOperations),
+                project.services.get(TemporaryFileProvider), project.files(archive), "path/to/text", Charsets.UTF_8)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResourceTest.groovy
new file mode 100644
index 0000000..74c7d3f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedTextResourceTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import com.google.common.base.Charsets
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.util.TestUtil
+
+class FileCollectionBackedTextResourceTest extends AbstractTextResourceTest {
+    def setup() {
+        def project = TestUtil.createRootProject()
+        def file = project.file("file.txt")
+        file.text = "contents"
+        resource = new FileCollectionBackedTextResource(project.services.get(TemporaryFileProvider), project.files(file), Charsets.UTF_8)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedZipArchiveTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedZipArchiveTextResourceTest.groovy
new file mode 100644
index 0000000..0b815d4
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/FileCollectionBackedZipArchiveTextResourceTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import com.google.common.base.Charsets
+
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.util.TestUtil
+
+class FileCollectionBackedZipArchiveTextResourceTest extends AbstractTextResourceTest {
+    def setup() {
+        def project = TestUtil.createRootProject()
+        def archive = project.file("archive.zip")
+        def archiveEntry = project.file("archive/path/to/text")
+        archiveEntry.parentFile.mkdirs()
+        archiveEntry.text = "contents"
+        project.ant.zip(basedir: project.file("archive"), destfile: archive)
+        resource = new FileCollectionBackedArchiveTextResource(project.services.get(FileOperations),
+                project.services.get(TemporaryFileProvider), project.files(archive), "path/to/text", Charsets.UTF_8)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/StringBackedTextResourceTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/StringBackedTextResourceTest.groovy
new file mode 100644
index 0000000..ca08d66
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/resources/StringBackedTextResourceTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.resources
+
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.util.TestUtil
+
+class StringBackedTextResourceTest extends AbstractTextResourceTest {
+    def setup() {
+        def project = TestUtil.createRootProject()
+        resource = new StringBackedTextResource(project.services.get(TemporaryFileProvider), "contents")
+    }
+}
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 7d08c1b..71d1098 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
@@ -22,7 +22,8 @@ import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.taskfactory.ITaskFactory
 import org.gradle.api.tasks.TaskDependency
 import org.gradle.initialization.ProjectAccessListener
-import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.model.internal.registry.DefaultModelRegistry
 import org.gradle.util.GUtil
 import spock.lang.Specification
 
@@ -31,58 +32,45 @@ import static java.util.Collections.singletonMap
 public class DefaultTaskContainerTest extends Specification {
 
     private taskFactory = Mock(ITaskFactory)
-    private project = Mock(ProjectInternal, name: "<project>")
+    def modelRegistry = new DefaultModelRegistry(null)
+    private project = Mock(ProjectInternal, name: "<project>") {
+        getModelRegistry() >> modelRegistry
+    }
     private taskCount = 1;
     private accessListener = Mock(ProjectAccessListener)
-    private container = new DefaultTaskContainer(project, Mock(Instantiator), taskFactory, accessListener)
+    private container = new DefaultTaskContainerFactory(modelRegistry, DirectInstantiator.INSTANCE, taskFactory, project, accessListener).create()
 
-    void "adds by Map"() {
+    void "creates by Map"() {
         def options = singletonMap("option", "value")
         def task = task("task")
         taskFactory.createTask(options) >> task
 
         when:
-        def added = container.add(options)
+        def added = container.create(options)
 
         then:
         added == task
         container.getByName("task") == task
     }
 
-    void "adds by name"() {
+    void "creates by name"() {
         given:
         def options = singletonMap(Task.TASK_NAME, "task")
         def task = task("task")
         taskFactory.createTask(options) >> task
 
         expect:
-        container.add("task") == task
+        container.create("task") == task
     }
 
-    void "adds by name and type"() {
+    void "creates by name and type"() {
         given:
         def options = GUtil.map(Task.TASK_NAME, "task", Task.TASK_TYPE, Task.class)
         def task = task("task")
         taskFactory.createTask(options) >> task
 
         expect:
-        container.add("task", Task.class) == task
-    }
-
-    void "adds 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.add("task", action)
-
-        then:
-        added == task
-        1 * task.configure(action) >> task
+        container.create("task", Task.class) == task
     }
 
     void "creates by name and closure"() {
@@ -150,7 +138,7 @@ public class DefaultTaskContainerTest extends Specification {
         taskFactory.createTask(options) >> task
 
         when:
-        container.add("task")
+        container.create("task")
 
         then:
         0 * rule._
@@ -162,11 +150,11 @@ public class DefaultTaskContainerTest extends Specification {
         taskFactory.createTask(singletonMap(Task.TASK_NAME, "task")) >> { this.task("task") }
 
         when:
-        container.add("task")
+        container.create("task")
 
         then:
         def ex = thrown(InvalidUserDataException)
-        ex.message == "Cannot add Mock for type 'TaskInternal' named '[task2]' as a task with that name already exists."
+        ex.message == "Cannot add Mock for type 'TaskInternal' named '[task1]' as a task with that name already exists."
         container.getByName("task") == task
     }
 
@@ -279,7 +267,7 @@ public class DefaultTaskContainerTest extends Specification {
         expectTaskLookupInOtherProject(":", "task", task)
 
         then:
-        container.resolveTask(new StringBuilder(":task")) == task
+        container.resolveTask(":task") == task
     }
 
     void "actualizes task graph"() {
@@ -296,15 +284,21 @@ public class DefaultTaskContainerTest extends Specification {
 
         bTaskDependency.getDependencies(b) >> Collections.emptySet()
 
-        aTaskDependency.getDependencies(task) >> { container.add("b"); Collections.singleton(b) }
+        aTaskDependency.getDependencies(task) >> { container.create("b"); Collections.singleton(b) }
         task.dependsOn("b")
 
         addPlaceholderTask("c")
+        def cTask = this.task("c", DefaultTask)
+        def cTaskDependency = Mock(TaskDependencyInternal)
+        cTask.getTaskDependencies() >> cTaskDependency
+        cTaskDependency.getDependencies(_) >> []
+
+        1 * taskFactory.create("c", DefaultTask) >> { cTask }
 
         assert container.size() == 1
 
         when:
-        container.actualize()
+        container.realize()
 
         then:
         container.size() == 3
@@ -313,29 +307,43 @@ public class DefaultTaskContainerTest extends Specification {
     void "can add task via placeholder action"() {
         when:
         addPlaceholderTask("task")
+        1 * taskFactory.create("task", DefaultTask) >> { task(it[0], it[1]) }
+
         then:
         container.getByName("task") != null
     }
 
-    void "task priotized over placeholders"() {
+    void "placeholder is ignored when task already exists"() {
         given:
         Task task = addTask("task")
-        Runnable placeholderAction = addPlaceholderTask("task")
+        def placeholderAction = addPlaceholderTask("task")
 
         when:
         container.getByName("task") == task
 
         then:
-        0 * placeholderAction.run()
+        0 * placeholderAction.execute(_)
+    }
+
+    void "placeholder is ignored when task later defined"() {
+        given:
+        def placeholderAction = addPlaceholderTask("task")
+        Task task = addTask("task")
+
+        when:
+        container.getByName("task") == task
+
+        then:
+        0 * placeholderAction.execute(_)
     }
 
     void "getNames contains task and placeholder action names"() {
         when:
         addTask("task1")
-        Runnable placeholderAction = addPlaceholderTask("task2")
-        0 * placeholderAction.run()
+        def placeholderAction = addPlaceholderTask("task2")
+        0 * placeholderAction.execute(_)
         then:
-        container.names ==  ['task1', 'task2'] as SortedSet
+        container.names == ['task1', 'task2'] as SortedSet
     }
 
     void "maybeCreate creates new task"() {
@@ -399,16 +407,16 @@ public class DefaultTaskContainerTest extends Specification {
     }
 
     private <U extends TaskInternal> U task(final String name, Class<U> type) {
-        Mock(type, name: "[task" + ++taskCount + "]") {
+        Mock(type, name: "[task" + taskCount++ + "]") {
             getName() >> name
+            getTaskDependency() >> Mock(TaskDependency)
         }
     }
 
-    private Runnable addPlaceholderTask(String placeholderName) {
-        Runnable runnable = Mock(Runnable)
-        runnable.run() >> { addTask(placeholderName) }
-        container.addPlaceholderAction(placeholderName, runnable)
-        runnable
+    private Action addPlaceholderTask(String placeholderName) {
+        def action = Mock(Action)
+        container.addPlaceholderAction(placeholderName, DefaultTask, action)
+        action
     }
 
     private Task addTask(String name) {
@@ -428,4 +436,4 @@ public class DefaultTaskContainerTest extends Specification {
     }
 
     interface CustomTask extends TaskInternal {}
-}
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskDependencyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskDependencyTest.groovy
index e739c6f..36c4d75 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskDependencyTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskDependencyTest.groovy
@@ -15,6 +15,8 @@
  */
 package org.gradle.api.internal.tasks
 
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+
 import java.util.concurrent.Callable
 import org.gradle.api.Buildable
 import org.gradle.api.Task
@@ -52,6 +54,19 @@ public class DefaultTaskDependencyTest {
     }
 
     @Test
+    public void canDependOnACharSequence() {
+        def input = new StringBuilder("other")
+        dependency.add(input);
+
+        context.checking({
+            one(resolver).resolveTask("other");
+            will(returnValue(otherTask));
+        })
+
+        assertThat(dependency.getDependencies(task), equalTo(toSet(otherTask)));
+    }
+
+    @Test
     public void canDependOnATaskInstance() {
         dependency.add(otherTask);
 
@@ -144,32 +159,32 @@ public class DefaultTaskDependencyTest {
     }
 
     @Test
-    public void failsForOtherObjectsWhenNoResolverProvided() {
-        StringBuffer dep = new StringBuffer("task")
-
-        DefaultTaskDependency dependency = new DefaultTaskDependency()
-        dependency.add(dep)
+    public void failsForOtherTypes() {
+        dependency.add(12)
 
         try {
             dependency.getDependencies(task)
             fail()
         } catch (GradleException e) {
-            assertThat(e.cause, instanceOf(IllegalArgumentException))
-            assertThat(e.cause.message, equalTo("Cannot convert $dep to a task." as String))
+            assertThat(e.cause, instanceOf(UnsupportedNotationException))
+            assertThat(e.cause.message, startsWith("Cannot convert 12 to a task." as String))
         }
     }
-    
-    @Test
-    public void resolvesOtherObjects() {
 
-        dependency.add(9);
+    @Test
+    public void failsForCharSequencesWhenNoResolverProvided() {
+        StringBuffer dep = new StringBuffer("task")
 
-        context.checking({
-            one(resolver).resolveTask(9);
-            will(returnValue(otherTask));
-        });
+        DefaultTaskDependency dependency = new DefaultTaskDependency()
+        dependency.add(dep)
 
-        assertThat(dependency.getDependencies(task), equalTo(toSet(otherTask)));
+        try {
+            dependency.getDependencies(task)
+            fail()
+        } catch (GradleException e) {
+            assertThat(e.cause, instanceOf(UnsupportedNotationException))
+            assertThat(e.cause.message, startsWith("Cannot convert $dep to a task." as String))
+        }
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
index 8aba401..e370274 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskInputsTest.groovy
@@ -19,10 +19,12 @@ import org.gradle.api.file.FileCollection
 import org.gradle.api.file.FileTree
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
 import java.util.concurrent.Callable
 
+ at UsesNativeServices
 class DefaultTaskInputsTest extends Specification {
     private final File treeFile = new File('tree')
     private final FileTree tree = [getFiles: { [treeFile] as Set}] as FileTree
@@ -31,7 +33,9 @@ class DefaultTaskInputsTest extends Specification {
             resolveFilesAsTree: {tree}
     ] as FileResolver
 
-    private TaskStatusNagger taskStatusNagger = Mock()
+    private TaskMutator taskStatusNagger = Stub() {
+        mutate(_, _) >> { String method, Runnable action -> action.run() }
+    }
     private final DefaultTaskInputs inputs = new DefaultTaskInputs(resolver, {} as TaskInternal, taskStatusNagger)
 
     def defaultValues() {
@@ -105,6 +109,19 @@ class DefaultTaskInputsTest extends Specification {
         inputs.properties == [a: files]
     }
 
+    def "GString input property values are evaluated to avoid serialization issues"() {
+        when:
+        inputs.property('a', { "hey ${new NotSerializable()}" })
+
+        then:
+        inputs.properties == [a: "hey Joe"]
+        String.is inputs.properties.a.class
+    }
+
+    class NotSerializable {
+        String toString() { "Joe" }
+    }
+
     def canRegisterSourceFile() {
         when:
         inputs.source('file')
@@ -182,57 +199,4 @@ class DefaultTaskInputsTest extends Specification {
         inputs.hasInputs
         inputs.hasSourceFiles
     }
-
-    public void callsTaskStatusNaggerWhenFileMethodCalled() {
-        when:
-        inputs.file("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.file(Object)")
-    }
-
-    public void callsTaskStatusNaggerWhenFilesMethodCalled() {
-        when:
-        inputs.files("aFile", "bFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.files(Object...)")
-    }
-
-    public void callsTaskStatusNaggerWhenDirMethodCalled() {
-        when:
-        inputs.dir("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.dir(Object)")
-    }
-
-    public void callsTaskStatusNaggerWhenSourceDirMethodCalled() {
-        when:
-        inputs.sourceDir("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.sourceDir(Object)")
-    }
-
-    public void callsTaskStatusNaggerWhenSourceMethodCalled() {
-        when:
-        inputs.source("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.source(Object)")
-        when:
-        inputs.source("aFile", "bFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.source(Object...)")
-    }
-
-    public void callsTaskStatusNaggerWhenPropertyMethodCalled() {
-        when:
-        inputs.property("name", "value")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.property(String, Object)")
-    }
-
-    public void callsTaskStatusNaggerWhenPropertiesMethodCalled() {
-        when:
-        inputs.properties(name:"value")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskInputs.properties(Map)")
-    }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputsTest.groovy
index 25f3587..c75eed4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputsTest.groovy
@@ -19,11 +19,15 @@ import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.TaskExecutionHistory
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
+ at UsesNativeServices
 class DefaultTaskOutputsTest extends Specification {
 
-    private TaskStatusNagger taskStatusNagger = Mock()
+    private TaskMutator taskStatusNagger = Stub() {
+        mutate(_, _) >> { String method, Runnable action -> action.run() }
+    }
     private final TaskInternal task = [toString: {'task'}] as TaskInternal
     private final DefaultTaskOutputs outputs = new DefaultTaskOutputs({new File(it)} as FileResolver, task, taskStatusNagger)
 
@@ -101,28 +105,6 @@ class DefaultTaskOutputsTest extends Specification {
         1 * history.outputFiles >> outputFiles
     }
 
-    public void callsTaskStatusNaggerWhenFileMethodCalled() {
-        when:
-        outputs.file("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.file(Object)")
-    }
-
-    public void callsTaskStatusNaggerWhenFilesMethodCalled() {
-        when:
-        outputs.files("aFile", "bFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.files(Object...)")
-    }
-
-    public void callsTaskStatusNaggerWhenDirMethodCalled() {
-        when:
-        outputs.dir("aFile")
-        then:
-        1 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.dir(Object)")
-        0 * taskStatusNagger.nagIfTaskNotInConfigurableState("TaskOutputs.files(Object...)");
-    }
-
     public void getPreviousFilesFailsWhenNoTaskHistoryAvailable() {
         when:
         outputs.previousFiles
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/PublicTaskSpecificationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/PublicTaskSpecificationTest.groovy
new file mode 100644
index 0000000..1541c30
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/PublicTaskSpecificationTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Task
+import spock.lang.Specification
+
+class PublicTaskSpecificationTest extends Specification {
+
+    def publicTaskSpec = PublicTaskSpecification.INSTANCE
+
+    def "task with null group is private task"() {
+        given:
+        def privateTask = Mock(Task)
+        privateTask.getGroup() >> { null }
+
+        when:
+        def isPublic = publicTaskSpec.isSatisfiedBy(privateTask)
+
+        then:
+        !isPublic
+    }
+
+    def "task with empty group is private task"() {
+        given:
+        def privateTask = Mock(Task)
+        privateTask.getGroup() >> { "" }
+
+        when:
+        def isPublic = publicTaskSpec.isSatisfiedBy(privateTask)
+
+        then:
+        !isPublic
+    }
+
+    def "task with non-null group is public task"() {
+        given:
+        def publicTask = Mock(Task)
+        publicTask.getGroup() >> { 'build' }
+
+        when:
+        def isPublic = publicTaskSpec.isSatisfiedBy(publicTask)
+
+        then:
+        isPublic
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/TaskMutatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/TaskMutatorTest.groovy
new file mode 100644
index 0000000..cefdc46
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/TaskMutatorTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks
+
+import org.gradle.api.internal.TaskInternal
+import spock.lang.Specification
+
+class TaskMutatorTest extends Specification {
+    def state = Stub(TaskStateInternal)
+    def task = Stub(TaskInternal) {
+        getState() >> state
+        toString() >> "<task>"
+    }
+    def nagger = new TaskMutator(task)
+
+    def "executes mutation action when in configurable state"() {
+        def action = Mock(Runnable)
+
+        given:
+        state.configurable >> true
+
+        when:
+        nagger.mutate("Task.thing()", action)
+
+        then:
+        1 * action.run()
+    }
+
+    def "mutation action fails when not in configurable state"() {
+        def action = Mock(Runnable)
+
+        given:
+        state.configurable >> false
+
+        when:
+        nagger.mutate("Task.thing()", action)
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot call Task.thing() on <task> after task has started execution.'
+    }
+
+    def "includes hint when executing an action added using left shift"() {
+        def action = Mock(Runnable)
+        def taskAction = Mock(ContextAwareTaskAction)
+
+        given:
+        state.configurable >> false
+        taskAction.execute(_) >> {
+            nagger.mutate("Task.thing()", action)
+        }
+
+        and:
+        def wrappedAction = nagger.leftShift(taskAction)
+
+        when:
+        wrappedAction.execute(task)
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot call Task.thing() on <task> after task has started execution. Check the configuration of <task> as you may have misused \'<<\' at task declaration.'
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactorySpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactorySpec.groovy
index 63622a6..abca05d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactorySpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionNotationParserFactorySpec.groovy
@@ -36,7 +36,6 @@ class OptionNotationParserFactorySpec extends Specification {
         when:
         def parser = factory.toComposite(TestEnum.class);
         then:
-        parser.parseNotation(TestEnum.ABC) == TestEnum.ABC
         parser.parseNotation("ABC") == TestEnum.ABC
     }
 
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 9141221..600175a 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
@@ -59,7 +59,7 @@ public class DefaultJavaForkOptionsTest {
         assertThat(options.bootstrapClasspath.files, isEmpty())
         assertFalse(options.enableAssertions)
         assertFalse(options.debug)
-        assertThat(options.allJvmArgs, equalTo([fileEncodingProperty()]))
+        assert options.allJvmArgs == [fileEncodingProperty(), *localeProperties()]
     }
 
     @Test
@@ -92,7 +92,7 @@ public class DefaultJavaForkOptionsTest {
         options.systemProperties(key: 12, key2: null, "key3": 'value')
         options.jvmArgs('arg1')
 
-        assertThat(options.allJvmArgs, equalTo(['-Dkey=12', '-Dkey2', '-Dkey3=value', 'arg1', fileEncodingProperty()]))
+        assert options.allJvmArgs == ['-Dkey=12', '-Dkey2', '-Dkey3=value', 'arg1', fileEncodingProperty(), *localeProperties()]
     }
 
     @Test
@@ -115,16 +115,14 @@ public class DefaultJavaForkOptionsTest {
     public void allJvmArgsIncludeMinHeapSize() {
         options.minHeapSize = '64m'
         options.jvmArgs('arg1')
-
-        assertThat(options.allJvmArgs, equalTo(['arg1', '-Xms64m', fileEncodingProperty()]))
+        assert options.allJvmArgs == ['arg1', '-Xms64m', fileEncodingProperty(), *localeProperties()]
     }
 
     @Test
     public void allJvmArgsIncludeMaxHeapSize() {
         options.maxHeapSize = '1g'
         options.jvmArgs('arg1')
-
-        assertThat(options.allJvmArgs, equalTo(['arg1', '-Xmx1g', fileEncodingProperty()]))
+        assert options.allJvmArgs == ['arg1', '-Xmx1g', fileEncodingProperty(), *localeProperties()]
     }
 
     @Test
@@ -161,11 +159,9 @@ public class DefaultJavaForkOptionsTest {
 
     @Test
     public void allJvmArgsIncludeAssertionsEnabled() {
-        assertThat(options.allJvmArgs, equalTo([fileEncodingProperty()]))
-
+        assert options.allJvmArgs == [fileEncodingProperty(), *localeProperties()]
         options.enableAssertions = true
-
-        assertThat(options.allJvmArgs, equalTo([fileEncodingProperty(), '-ea']))
+        assert options.allJvmArgs == [fileEncodingProperty(), *localeProperties(), '-ea']
     }
 
     @Test
@@ -186,11 +182,9 @@ public class DefaultJavaForkOptionsTest {
 
     @Test
     public void allJvmArgsIncludeDebugArgs() {
-        assertThat(options.allJvmArgs, equalTo([fileEncodingProperty()]))
-
+        assert options.allJvmArgs == [fileEncodingProperty(), *localeProperties()]
         options.debug = true
-
-        assertThat(options.allJvmArgs, equalTo([fileEncodingProperty(), '-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005']))
+        assert options.allJvmArgs  == [fileEncodingProperty(), *localeProperties(), '-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005']
     }
 
     @Test
@@ -254,7 +248,7 @@ public class DefaultJavaForkOptionsTest {
             will(returnValue([isEmpty: {false}, getAsPath: {'<classpath>'}] as FileCollection))
         }
 
-        assertThat(options.allJvmArgs, equalTo(['-Xbootclasspath:' + files.join(System.properties['path.separator']), fileEncodingProperty()]))
+        assert options.allJvmArgs  == ['-Xbootclasspath:' + files.join(System.properties['path.separator']), fileEncodingProperty(), *localeProperties()]
     }
 
     @Test
@@ -292,9 +286,17 @@ public class DefaultJavaForkOptionsTest {
         options.copyTo(target)
     }
 
-    private String fileEncodingProperty(String encoding = Charset.defaultCharset().name()) {
+    private static String fileEncodingProperty(String encoding = Charset.defaultCharset().name()) {
         return "-Dfile.encoding=$encoding"
     }
+
+    private static List<String> localeProperties(Locale locale = Locale.default) {
+        ["country", "language", "variant"].sort().collectEntries {
+            ["user.$it", locale."$it"]
+        }.collect {
+            it.value ? "-D$it.key=$it.value" : "-D$it.key"
+        }
+    }
 }
 
 
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
deleted file mode 100644
index b98f65e..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/SimpleXmlWriterSpec.groovy
+++ /dev/null
@@ -1,414 +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.xml
-
-import org.gradle.util.TextUtil
-import spock.lang.Specification
-
-import javax.xml.parsers.DocumentBuilderFactory
-
-class SimpleXmlWriterSpec extends Specification {
-
-    private sw = new ByteArrayOutputStream()
-    private writer = new SimpleXmlWriter(sw)
-
-    String getXml() {
-        def text = sw.toString("UTF-8")
-        println "TEXT {$text}"
-        def document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(sw.toByteArray()))
-        assert document
-        return text
-    }
-
-    def "writes basic XML"() {
-        when:
-        writer.startElement("root").attribute("items", "9")
-        writer.startElement("item").endElement()
-        writer.startElement("item").attribute("size", "10m")
-        writer.characters("some chars")
-        writer.characters(" and some other".toCharArray())
-        writer.characters("x  chars.x".toCharArray(), 2, 7)
-        writer.startElement("foo").characters(" ")
-        writer.endElement()
-        writer.endElement()
-        writer.endElement()
-
-        then:
-        xml == '<?xml version="1.0" encoding="UTF-8"?><root items="9"><item/><item size="10m">some chars and some other chars.<foo> </foo></item></root>'
-    }
-
-    def "escapes reserved characters in text content"() {
-        when:
-        writer.startElement("root")
-        writer.characters("chars with interesting stuff: < < > ' \" ]]> \r\n \t")
-        writer.endElement()
-
-        then:
-        xml.contains('<root>chars with interesting stuff: &lt; < > \' " ]]> \r\n \t</root>')
-    }
-
-    def "escapes reserved characters in attribute values"() {
-        when:
-        writer.startElement("root")
-        writer.startElement("item").attribute("description", "encoded: \t < < > ' \n\r\"  ")
-        writer.endElement()
-        writer.endElement()
-
-        then:
-        xml.contains('<item description="encoded: 	 &lt; < > \' 

"  "/>')
-
-        and:
-        def item = new XmlSlurper().parseText(xml).item
-        item. at description.text() == "encoded: \t < < > ' \n\r\"  "
-    }
-
-    def "writes CDATA"() {
-        when:
-        writer.startElement("root")
-        writer.startElement("stuff")
-
-        writer.startCDATA()
-        writer.characters('x hey x'.toCharArray(), 2, 4)
-        writer.characters('joe'.toCharArray())
-        writer.characters("!")
-        writer.endCDATA()
-
-        writer.endElement()
-
-        writer.startCDATA()
-        writer.characters('encodes: ]]> ')
-        writer.characters('does not encode: ]] ')
-        writer.characters('html allowed: <> &')
-        writer.endCDATA()
-
-        writer.endElement()
-
-        then:
-        xml.contains('<stuff><![CDATA[hey joe!]]></stuff><![CDATA[encodes: ]]]]><![CDATA[> does not encode: ]] html allowed: <> &]]>')
-    }
-
-    def "encodes CDATA when token on the border"() {
-        when:
-        //the end token is on the border of both char arrays
-        writer.startElement('root')
-        writer.startCDATA()
-        writer.characters('stuff ]]')
-        writer.characters('> more stuff')
-        writer.endCDATA()
-        writer.endElement()
-
-        then:
-        xml.contains('<![CDATA[stuff ]]]]><![CDATA[> more stuff]]>')
-    }
-
-    def "does not encode CDATA when token separated in different CDATAs"() {
-        when:
-        //the end token is on the border of both char arrays
-
-        writer.startElement('root')
-
-        writer.startCDATA();
-        writer.characters('stuff ]]')
-        writer.endCDATA();
-
-        writer.startCDATA()
-        writer.characters('> more stuff')
-        writer.endCDATA();
-
-        writer.endElement()
-
-        then:
-        xml.contains('<root><![CDATA[stuff ]]]]><![CDATA[> more stuff]]></root>')
-    }
-
-    def "encodes non-ASCII characters"() {
-        when:
-        writer.startElement("\u0200").attribute("\u0201", "\u0202")
-        writer.characters("\u0203")
-        writer.startCDATA().characters("\u0204").endCDATA()
-        writer.endElement()
-
-        then:
-        xml.contains('<\u0200 \u0201="\u0202">\u0203<![CDATA[\u0204]]></\u0200>')
-    }
-
-    def "escapes restricted characters in text content"() {
-        when:
-        writer.startElement("root")
-        writer.attribute("name", "\u0084\u009f")
-        writer.characters("\u0084\u009f")
-        writer.startCDATA().characters("\u0084\u009f").endCDATA()
-        writer.endElement()
-
-        then:
-        xml.contains('<root name="&#x84;&#x9f;">&#x84;&#x9f;<![CDATA[]]>&#x84;<![CDATA[]]>&#x9f;<![CDATA[]]></root>')
-    }
-
-    def "replaces illegal characters in text content"() {
-        given:
-
-        when:
-        writer.startElement("root")
-        writer.characters(chars)
-        writer.startElement("broken").attribute("name", chars)
-        writer.startCDATA().characters(chars).endCDATA()
-        writer.endElement()
-        writer.endElement()
-
-        then:
-        xml.contains('<root>?<broken name="?"><![CDATA[?]]></broken></root>')
-
-        where:
-        chars << ["\u0000", "\ud800", "\udfff", "\ufffe"]
-    }
-
-    def "is a Writer implementation that escapes characters"() {
-        when:
-        writer.startElement("root")
-        writer.write("some <chars>")
-        writer.write(" and ".toCharArray())
-        writer.write("x some x".toCharArray(), 2, 4)
-        writer.write(' ')
-        writer.startCDATA()
-        writer.write("cdata")
-        writer.endCDATA()
-        writer.endElement()
-
-        then:
-        xml.contains("<root>some <chars> and some <![CDATA[cdata]]></root>")
-    }
-
-    def "cannot end element when stack is empty"() {
-        writer.startElement("root")
-        writer.endElement()
-
-        when:
-        writer.endElement()
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot end element, as there are no started elements.'
-    }
-
-    def "cannot write characters when stack is empty"() {
-        when:
-        writer.characters("text")
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot write text, as there are no started elements.'
-
-        given:
-
-        when:
-        writer.startElement("root")
-        writer.endElement()
-        writer.characters("text")
-
-        then:
-        e = thrown()
-        e.message == 'Cannot write text, as there are no started elements.'
-    }
-
-    def "cannot end element when CDATA node is open"() {
-        writer.startElement("root")
-        writer.startCDATA()
-
-        when:
-        writer.endElement()
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot end element, as current CDATA node has not been closed.'
-    }
-
-    def "cannot start element when CDATA node is open"() {
-        writer.startElement("root")
-        writer.startCDATA()
-
-        when:
-        writer.startElement("nested")
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot start element, as current CDATA node has not been closed.'
-    }
-
-    def "cannot start CDATA node when CDATA node is open"() {
-        writer.startElement("root")
-        writer.startCDATA()
-
-        when:
-        writer.startCDATA()
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot start CDATA node, as current CDATA node has not been closed.'
-    }
-
-    def "cannot end CDATA node when not in a CDATA node"() {
-        writer.startElement("root")
-
-        when:
-        writer.endCDATA()
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot end CDATA node, as not currently in a CDATA node.'
-    }
-
-    def "closes tags"() {
-        when:
-        writer.startElement("root")
-        action.call(writer)
-        writer.endElement()
-
-        then:
-        sw.toString().contains("<root>") //is closed with '>'
-
-        where:
-        action << [{ it.startElement("foo"); it.endElement() },
-                { it.startCDATA(); it.endCDATA() },
-                { it.characters("bar") },
-                { it.write("close") }]
-    }
-
-    def "closes attributed tags"() {
-        when:
-        writer.startElement("root")
-        writer.attribute("foo", '115')
-        action.call(writer)
-        writer.endElement()
-
-        then:
-        sw.toString().contains('<root foo="115">') //is closed with '>'
-
-        where:
-        action << [{ it.startElement("foo"); it.endElement() },
-                { it.startCDATA(); it.endCDATA() },
-                { it.characters("bar") },
-                { it.write("close") }]
-    }
-
-    def "outputs empty element when element has no content"() {
-        when:
-        writer.startElement("root")
-        writer.startElement("empty").endElement()
-        writer.startElement("empty").attribute("property", "value").endElement()
-        writer.endElement()
-
-        then:
-        xml.contains('<root><empty/><empty property="value"/></root>')
-    }
-
-    def "writes indented XML when enabled"() {
-        sw.reset()
-        def writer = new SimpleXmlWriter(sw, "    ")
-
-        when:
-        writer.startElement("root").attribute("items", "9")
-        writer.startElement("item").endElement()
-        writer.startElement("item").characters("some text").endElement()
-        writer.startElement("item")
-        writer.startElement("nested-1")
-        writer.startElement("nested-2").characters(" ").endElement()
-        writer.endElement()
-        writer.endElement()
-        writer.startElement("item")
-        writer.startElement("thing").characters("some text").endElement()
-        writer.startElement("thing").startCDATA().characters("some text").endCDATA().endElement()
-        writer.endElement()
-        writer.startElement("mixed")
-        writer.characters("text")
-        writer.startElement("mixed-1").endElement()
-        writer.characters("text")
-        writer.startElement("mixed-2").characters("123").endElement()
-        writer.startElement("mixed-3").startElement("empty").endElement().endElement()
-        writer.characters("text")
-        writer.endElement()
-        writer.endElement()
-
-        then:
-        xml == TextUtil.toPlatformLineSeparators('''<?xml version="1.0" encoding="UTF-8"?>
-<root items="9">
-    <item/>
-    <item>some text</item>
-    <item>
-        <nested-1>
-            <nested-2> </nested-2>
-        </nested-1>
-    </item>
-    <item>
-        <thing>some text</thing>
-        <thing><![CDATA[some text]]></thing>
-    </item>
-    <mixed>text
-        <mixed-1/>text
-        <mixed-2>123</mixed-2>
-        <mixed-3>
-            <empty/>
-        </mixed-3>text</mixed>
-</root>
-''')
-    }
-
-    def "allows valid tag names"() {
-        when:
-        writer.startElement(name)
-
-        then:
-        notThrown(IllegalArgumentException)
-
-        where:
-        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300"]
-    }
-
-    def "validates tag names"() {
-        when:
-        writer.startElement(name)
-
-        then:
-        def ex = thrown(IllegalArgumentException)
-        ex.message == "Invalid element name: '$name'"
-
-        where:
-        name << ["tag with space", "", "-invalid-start-char", "  ", "912", "\u00d7"]
-    }
-
-    def "allows valid attribute names"() {
-        when:
-        writer.startElement("foo").attribute(name, "foo")
-
-        then:
-        notThrown(IllegalArgumentException)
-
-        where:
-        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300"]
-    }
-
-    def "validates attribute names"() {
-        when:
-        writer.startElement("foo").attribute(name, "foo")
-
-        then:
-        def ex = thrown(IllegalArgumentException)
-        ex.message == "Invalid attribute name: '$name'"
-
-        where:
-        name << ["attribute with space", "", "-invalid-start-char", "  ", "912", "\u00d7"]
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/XmlTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/XmlTransformerTest.groovy
deleted file mode 100644
index a6be29b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/xml/XmlTransformerTest.groovy
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.xml
-
-import org.gradle.api.Action
-import org.gradle.api.XmlProvider
-import org.gradle.api.internal.DomNode
-import org.gradle.util.TextUtil
-import spock.lang.Specification
-import org.junit.Rule
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import javax.xml.parsers.DocumentBuilderFactory
-
-class XmlTransformerTest extends Specification {
-    final XmlTransformer transformer = new XmlTransformer()
-    @Rule TestNameTestDirectoryProvider tmpDir
-
-    def "returns original string when no actions are provided"() {
-        expect:
-        looksLike '<root/>', transformer.transform('<root/>')
-    }
-
-    def "action can access XML as StringBuilder"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-
-        when:
-        def result = transformer.transform('<root/>')
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            def builder = provider.asString()
-            builder.insert(builder.indexOf("root"), 'some-')
-        }
-        looksLike '<some-root/>', result
-    }
-
-    def "action can access XML as Node"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-
-        when:
-        def result = transformer.transform('<root/>')
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child1')
-        }
-        looksLike '<root>\n  <child1/>\n</root>\n', result
-    }
-
-    def "action can access XML as DOM element"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-
-        when:
-        def result = transformer.transform('<root/>')
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            def document = provider.asElement().ownerDocument
-            provider.asElement().appendChild(document.createElement('child1'))
-        }
-        looksLike '<root>\n  <child1/>\n</root>\n', result
-    }
-
-    def "can transform String to a Writer"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-        StringWriter writer = new StringWriter()
-
-        when:
-        transformer.transform('<root/>', writer)
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child1')
-        }
-        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
-    }
-
-    def "can transform String to an OutputStream"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-        def outputStream = new ByteArrayOutputStream()
-
-        when:
-        transformer.transform('<root/>', outputStream)
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child\u03b1')
-        }
-        looksLike '<root>\n  <child\u03b1/>\n</root>\n', outputStream.toByteArray()
-    }
-
-    def "can transform Node to a Writer"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-        StringWriter writer = new StringWriter()
-        Node node = new XmlParser().parseText('<root/>')
-
-        when:
-        transformer.transform(node, writer)
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child1')
-        }
-        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
-    }
-
-    def "can transform Node to an OutputStream"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-        def outputStream = new ByteArrayOutputStream()
-        Node node = new XmlParser().parseText('<root/>')
-
-        when:
-        transformer.transform(node, outputStream)
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child\u03b1')
-        }
-        looksLike '<root>\n  <child\u03b1/>\n</root>\n', outputStream.toByteArray()
-    }
-
-    def "can transform Node to a File"() {
-        Action<XmlProvider> action = Mock()
-        transformer.addAction(action)
-        File file = tmpDir.file("out.xml")
-        Node node = new XmlParser().parseText('<root/>')
-
-        when:
-        transformer.transform(node, file)
-
-        then:
-        action.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child\u03b1')
-        }
-        looksLike '<root>\n  <child\u03b1/>\n</root>\n', file.bytes
-    }
-
-    def "can use a closure as an action"() {
-        transformer.addAction { provider ->
-            provider.asNode().appendNode('child1')
-        }
-        StringWriter writer = new StringWriter()
-
-        when:
-        transformer.transform('<root/>', writer)
-
-        then:
-        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
-    }
-
-    def "can chain actions"() {
-        Action<XmlProvider> stringAction = Mock()
-        Action<XmlProvider> nodeAction = Mock()
-        Action<XmlProvider> elementAction = Mock()
-        Action<XmlProvider> stringAction2 = Mock()
-        transformer.addAction(stringAction)
-        transformer.addAction(elementAction)
-        transformer.addAction(nodeAction)
-        transformer.addAction(stringAction2)
-
-        when:
-        def result = transformer.transform('<root/>')
-
-        then:
-        stringAction.execute(_) >> { XmlProvider provider ->
-            def builder = provider.asString()
-            builder.insert(builder.indexOf("root"), 'some-')
-        }
-        nodeAction.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child2')
-        }
-        elementAction.execute(_) >> { XmlProvider provider ->
-            def document = provider.asElement().ownerDocument
-            provider.asElement().appendChild(document.createElement('child1'))
-        }
-        stringAction2.execute(_) >> { XmlProvider provider ->
-            provider.asString().append('<!-- end -->')
-        }
-
-        looksLike '<some-root>\n  <child1/>\n  <child2/>\n</some-root>\n<!-- end -->', result
-    }
-
-    def "can chain node actions"() {
-        Action<XmlProvider> nodeAction = Mock()
-        Action<XmlProvider> nodeAction2 = Mock()
-        transformer.addAction(nodeAction)
-        transformer.addAction(nodeAction2)
-
-        when:
-        def result = transformer.transform('<root/>')
-
-        then:
-        nodeAction.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child1')
-        }
-        nodeAction2.execute(_) >> { XmlProvider provider ->
-            provider.asNode().appendNode('child2')
-        }
-        looksLike '<root>\n  <child1/>\n  <child2/>\n</root>\n', result
-    }
-
-    def "indentation correct when writing out Node"() {
-        transformer.indentation = "\t"
-        transformer.addAction { XmlProvider provider -> provider.asNode().children()[0].appendNode("grandchild") }
-
-        when:
-        def result = transformer.transform("<root>\n  <child/>\n</root>\n")
-
-        then:
-        looksLike "<root>\n\t<child>\n\t\t<grandchild/>\n\t</child>\n</root>\n", result
-    }
-
-    def "can add DOCTYPE along with nodes"() {
-        transformer.addAction { it.asNode().appendNode('someChild') }
-        transformer.addAction {
-            def s = it.asString()
-            s.insert(s.indexOf("?>") + 2, '\n<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">')
-        }
-
-        when:
-        def result = transformer.transform("<root></root>")
-
-        then:
-        looksLike "<!DOCTYPE application PUBLIC \"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN\" \"http://java.sun.com/dtd/application_1_3.dtd\">\n<root>\n  <someChild/>\n</root>\n", result
-    }
-
-    def "can specify DOCTYPE when using DomNode"() {
-        StringWriter writer = new StringWriter()
-        def node = new DomNode('root')
-        node.publicId = 'public-id'
-        node.systemId = 'system-id'
-
-        when:
-        transformer.transform(node, writer)
-
-        then:
-        looksLike '''<!DOCTYPE root PUBLIC "public-id" "system-id">
-<root/>
-''', writer.toString()
-    }
-
-    def "DOCTYPE is preserved when transformed as a Node"() {
-        StringWriter writer = new StringWriter()
-        def node = new DomNode('root')
-        node.publicId = 'public-id'
-        node.systemId = 'system-id'
-        transformer.addAction { it.asNode().appendNode('someChild') }
-
-        when:
-        transformer.transform(node, writer)
-
-        then:
-        looksLike '''<!DOCTYPE root PUBLIC "public-id" "system-id">
-<root>
-  <someChild/>
-</root>
-''', writer.toString()
-    }
-
-    def "DOCTYPE is preserved when transformed as a DOM element"() {
-        StringWriter writer = new StringWriter()
-        def node = new DomNode('root')
-        node.publicId = 'public-id'
-        node.systemId = getClass().getResource("xml-transformer-test.dtd")
-        transformer.addAction { it.asElement().appendChild(it.asElement().ownerDocument.createElement('someChild')) }
-
-        when:
-        transformer.transform(node, writer)
-
-        then:
-        looksLike """<!DOCTYPE root PUBLIC "public-id" "${node.getSystemId()}">
-<root>
-  <someChild/>
-</root>
-""", writer.toString()
-    }
-
-    def "indentation correct when writing out DOM element (only) if indenting with spaces"() {
-        transformer.indentation = expected
-        transformer.addAction { XmlProvider provider ->
-            def document = provider.asElement().ownerDocument
-            document.getElementsByTagName("child").item(0).appendChild(document.createElement("grandchild"))
-        }
-
-        when:
-        def result = transformer.transform("<root>\n  <child/>\n</root>\n")
-
-        then:
-        looksLike("<root>\n$actual<child>\n$actual$actual<grandchild/>\n$actual</child>\n</root>\n", result)
-
-        where:
-        expected | actual
-        "    "   | "    "
-        "\t"     | "  " // tabs not supported, two spaces used instead
-    }
-
-    def "can use with action api"() {
-        given:
-        def writer = new StringWriter()
-        def input = "<things><thing/></things>"
-        def generator = new Action<Writer>() {
-            void execute(Writer t) {
-                t.write(input)
-            }
-        }
-
-        when:
-        transformer.transform(writer, generator)
-
-        then:
-        looksLike(input, writer.toString())
-
-        when:
-        writer.buffer.setLength(0)
-        transformer.addAction(new Action<XmlProvider>() {
-            void execute(XmlProvider xml) {
-                xml.asNode().thing[0]. at foo = "bar"
-            }
-        })
-        transformer.transform(writer, generator)
-
-        then:
-        looksLike('<things>\n  <thing foo="bar"/>\n</things>', writer.toString())
-    }
-
-    private void looksLike(String expected, String actual) {
-        assert removeTrailingWhitespace(actual) == removeTrailingWhitespace(TextUtil.toPlatformLineSeparators("<?xml version=\"1.0\"?>\n" + expected))
-    }
-
-    private void looksLike(String expected, byte[] actual) {
-        DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(actual))
-        assert removeTrailingWhitespace(new String(actual, "utf-8")) == removeTrailingWhitespace(TextUtil.toPlatformLineSeparators("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + expected))
-    }
-
-    private String removeTrailingWhitespace(String value) {
-        return value.replaceFirst('(?s)\\s+$', "")
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/logging/LoggingTest.java b/subprojects/core/src/test/groovy/org/gradle/api/logging/LoggingTest.java
index 8203cac..a35a639 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/logging/LoggingTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/api/logging/LoggingTest.java
@@ -16,11 +16,9 @@
 
 package org.gradle.api.logging;
 
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.classic.spi.LoggingEvent;
-import ch.qos.logback.core.Appender;
 import org.gradle.logging.ConfigureLogging;
+import org.gradle.logging.internal.LogEvent;
+import org.gradle.logging.internal.OutputEventListener;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -32,63 +30,70 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.slf4j.Marker;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 @RunWith(JMock.class)
 public class LoggingTest {
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final Appender<ILoggingEvent> appender = context.mock(Appender.class);
-    private final ConfigureLogging logging = new ConfigureLogging(appender);
+    private final OutputEventListener outputEventListener = context.mock(OutputEventListener.class);
+    private final ConfigureLogging logging = new ConfigureLogging(outputEventListener);
 
     @Before
-    public void attachAppender() {
-        logging.attachAppender();
+    public void attachListener() {
+        logging.attachListener();
     }
 
     @After
-    public void detachAppender() {
-        logging.detachAppender();
+    public void resetLogging() {
+        logging.resetLogging();
     }
 
     @Test
     public void routesLogMessagesViaSlf4j() {
         Logger logger = Logging.getLogger(LoggingTest.class);
 
-        expectLogMessage(Level.TRACE, null, "trace");
-        logger.trace("trace");
-
-        expectLogMessage(Level.DEBUG, null, "debug");
+        expectLogMessage(LogLevel.DEBUG, "debug");
         logger.debug("debug");
 
-        expectLogMessage(Level.INFO, null, "info");
+        expectLogMessage(LogLevel.INFO, "info");
         logger.info("info");
 
-        expectLogMessage(Level.WARN, null, "warn");
+        expectLogMessage(LogLevel.WARN, "warn");
         logger.warn("warn");
 
-        expectLogMessage(Level.INFO, Logging.LIFECYCLE, "lifecycle");
+        expectLogMessage(LogLevel.LIFECYCLE, "lifecycle");
         logger.lifecycle("lifecycle");
 
-        expectLogMessage(Level.ERROR, null, "error");
+        expectLogMessage(LogLevel.ERROR, "error");
         logger.error("error");
 
-        expectLogMessage(Level.INFO, Logging.QUIET, "quiet");
+        expectLogMessage(LogLevel.QUIET, "quiet");
         logger.quiet("quiet");
 
-        expectLogMessage(Level.INFO, Logging.LIFECYCLE, "lifecycle via level");
+        expectLogMessage(LogLevel.LIFECYCLE, "lifecycle via level");
         logger.log(LogLevel.LIFECYCLE, "lifecycle via level");
     }
 
     @Test
+    public void ignoresTraceLevelLogging() {
+        Logger logger = Logging.getLogger(LoggingTest.class);
+
+        context.checking(new Expectations() {{
+            never(outputEventListener);
+        }});
+        logger.trace("trace");
+    }
+
+    @Test
     public void delegatesLevelIsEnabledToSlf4j() {
-        logging.setLevel(Level.WARN);
+        logging.setLevel(LogLevel.WARN);
 
         Logger logger = Logging.getLogger(LoggingTest.class);
         assertTrue(logger.isErrorEnabled());
+        assertTrue(logger.isQuietEnabled());
         assertTrue(logger.isWarnEnabled());
-        assertFalse(logger.isQuietEnabled());
         assertFalse(logger.isLifecycleEnabled());
         assertFalse(logger.isInfoEnabled());
         assertFalse(logger.isDebugEnabled());
@@ -98,22 +103,21 @@ public class LoggingTest {
         assertFalse(logger.isEnabled(LogLevel.INFO));
     }
 
-    private void expectLogMessage(final Level level, final Marker marker, final String text) {
-        final Matcher<LoggingEvent> matcher = new BaseMatcher<LoggingEvent>() {
+    private void expectLogMessage(final LogLevel level, final String text) {
+        final Matcher<LogEvent> matcher = new BaseMatcher<LogEvent>() {
 
             public void describeTo(Description description) {
-                description.appendText("level: ").appendValue(level).appendText(", marker: ").appendValue(marker)
-                        .appendText(", text:").appendValue(text);
+                description.appendText("level: ").appendValue(level).appendText(", text:").appendValue(text);
             }
 
             public boolean matches(Object o) {
-                LoggingEvent event = (LoggingEvent) o;
-                return event.getLevel().equals(level) && event.getMessage().equals(text) && event.getMarker() == marker;
+                LogEvent event = (LogEvent) o;
+                return event.getLogLevel() == level && event.getMessage().equals(text);
             }
         };
 
         context.checking(new Expectations() {{
-            one(appender).doAppend(with(matcher));
+            one(outputEventListener).onOutput(with(matcher));
         }});
     }
 }
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
index ad7f4a8..6601bbf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/AbstractCopyTaskTest.groovy
@@ -15,8 +15,6 @@
  */
 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
@@ -30,34 +28,13 @@ class AbstractCopyTaskTest extends WorkspaceTest {
         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()
+        task.rootSpec.hasSource() == false
 
         when:
         task.from testDirectory.absolutePath
@@ -65,12 +42,11 @@ class AbstractCopyTaskTest extends WorkspaceTest {
 
         then:
         task.mainSpec.getIncludes() == ["include"].toSet()
-        task.mainSpec.source.files == task.project.fileTree(testDirectory).files
+        task.mainSpec.buildRootResolver().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/DirectoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy
deleted file mode 100644
index f8e3842..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DirectoryTest.groovy
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks
-
-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.*
-
-class DirectoryTest extends AbstractTaskTest {
-    static final String TASK_DIR_NAME = 'parent/child'
-    Directory directoryForAbstractTest
-    Directory directory
-
-    public AbstractTask getTask() {
-        return directoryForAbstractTest
-    }
-
-    @Before public void setUp() {
-        directoryForAbstractTest = createTask(Directory.class)
-        directory = createTask(Directory.class, project, TASK_DIR_NAME)
-    }
-
-    @Test public void testInit() {
-        assertEquals(new File(project.projectDir, TASK_DIR_NAME).absoluteFile, directory.dir)
-    }
-
-    @Test public void testInitWithAbsolutePathName() {
-        try {
-            createTask(Directory.class, project, new File('nonRelative').absolutePath)
-            fail()
-        } catch (GradleException e) {
-            assertThat(e.cause, instanceOf(InvalidUserDataException.class))
-        }
-    }
-
-    @Test public void testExecute() {
-        directory.execute()
-        assert new File(project.projectDir, TASK_DIR_NAME).isDirectory()
-    }
-
-    @Test public void testWithExistingDir() {
-        File dir = new File(project.projectDir, TASK_DIR_NAME)
-        dir.mkdirs()
-        // create new file to check later that dir has not been recreated 
-        File file = new File(dir, 'somefile')
-        file.createNewFile()
-        directory.execute()
-        assert dir.isDirectory()
-        assert file.isFile()
-    }
-
-    @Test (expected = UncheckedIOException) public void testWithExistingFile() {
-        File file = new File(project.projectDir, 'testname')
-        file.createNewFile()
-        directory = createTask(Directory.class, project, 'testname')
-        directory.mkdir()
-    }
-}
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 676313a..2619749 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
@@ -1,56 +1,57 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks
-
-import org.gradle.BuildResult
-import org.gradle.GradleLauncher
-import org.gradle.initialization.GradleLauncherFactory
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-public class GradleBuildTest extends Specification {
-    GradleLauncherFactory launcherFactory = Mock()
-    GradleBuild task = TestUtil.createTask(GradleBuild, [gradleLauncherFactory: launcherFactory])
-
-    void usesCopyOfCurrentBuildsStartParams() {
-        def expectedStartParameter = task.project.gradle.startParameter.newBuild()
-        expectedStartParameter.currentDir = task.project.projectDir
-
-        expect:
-        task.startParameter == expectedStartParameter
-
-        when:
-        task.tasks = ['a', 'b']
-
-        then:
-        task.tasks == ['a', 'b']
-        task.startParameter.taskNames == ['a', 'b']
-    }
-
-    void executesBuild() {
-        GradleLauncher launcher = Mock()
-        BuildResult resultMock = Mock()
-
-        when:
-        task.build()
-
-        then:
-        1 * launcherFactory.newInstance(task.startParameter) >> launcher
-        1 * launcher.run() >> resultMock
-        1 * resultMock.rethrowFailure()
-        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.BuildResult
+import org.gradle.initialization.GradleLauncher
+import org.gradle.initialization.GradleLauncherFactory
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+public class GradleBuildTest extends Specification {
+    GradleLauncherFactory launcherFactory = Mock()
+    GradleBuild task = TestUtil.createTask(GradleBuild, [gradleLauncherFactory: launcherFactory])
+
+    void usesCopyOfCurrentBuildsStartParams() {
+        def expectedStartParameter = task.project.gradle.startParameter.newBuild()
+        expectedStartParameter.currentDir = task.project.projectDir
+
+        expect:
+        task.startParameter == expectedStartParameter
+
+        when:
+        task.tasks = ['a', 'b']
+
+        then:
+        task.tasks == ['a', 'b']
+        task.startParameter.taskNames == ['a', 'b']
+    }
+
+    void executesBuild() {
+        GradleLauncher launcher = Mock()
+        BuildResult resultMock = Mock()
+
+        when:
+        task.build()
+
+        then:
+        1 * launcherFactory.newInstance(task.startParameter) >> launcher
+        1 * launcher.run() >> resultMock
+        1 * resultMock.rethrowFailure()
+        1 * launcher.stop()
+        0 * _._
+    }
+}
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 685ccfa..d74942e 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
@@ -18,7 +18,6 @@ package org.gradle.api.tasks.ant;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
 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.TestUtil;
@@ -27,9 +26,7 @@ import org.junit.Rule;
 import org.junit.Test;
 
 import java.io.File;
-import java.util.Set;
 
-import static org.gradle.util.WrapUtil.toSet;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -52,26 +49,13 @@ public class AntTargetTest {
         TestTask testTask = new TestTask();
         testTask.setProject(antTarget.getProject());
         antTarget.addTask(testTask);
-
         task.setTarget(antTarget);
         task.setBaseDir(baseDir);
         task.executeAntTarget();
-
         assertTrue(testTask.executed);
     }
 
     @Test
-    public void dependsOnTargetDependencies() {
-        Task a = project.getTasks().create("a");
-        Task b = project.getTasks().create("b");
-        antTarget.setDepends("a, b");
-
-        task.setTarget(antTarget);
-        Set dependencies = task.getTaskDependencies().getDependencies(task);
-        assertThat(dependencies, equalTo((Set) toSet(a, b)));
-    }
-
-    @Test
     public void delegatesDescriptionToTarget() {
         antTarget.setDescription("description");
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
index 3df74b2..11d90de 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/util/PatternSetTest.groovy
@@ -16,9 +16,11 @@
  
 package org.gradle.api.tasks.util
 
+import org.apache.tools.ant.DirectoryScanner
 import org.gradle.api.file.FileTreeElement
 import org.gradle.api.file.RelativePath
 import org.gradle.api.specs.Spec
+import org.junit.After
 import org.junit.Test
 import spock.lang.Issue
 
@@ -29,6 +31,10 @@ import static org.junit.Assert.*
 class PatternSetTest extends AbstractTestForPatternSet {
     PatternSet patternSet = new PatternSet()
 
+    @After void resetDefaultExcludes() {
+        DirectoryScanner.resetDefaultExcludes()
+    }
+
     @Test void testConstructionFromMap() {
         Map map = [includes: [TEST_PATTERN_1], excludes: [TEST_PATTERN_2]]
         PatternFilterable patternSet = new PatternSet(map)
@@ -66,151 +72,151 @@ class PatternSetTest extends AbstractTestForPatternSet {
     }
 
     @Test void createsSpecForEmptyPatternSet() {
-        Spec<FileTreeElement> spec = patternSet.asSpec
+        included file('a')
+        included file('b')
+    }
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertTrue(spec.isSatisfiedBy(file('b')))
+    @Test void usesDefaultGlobalExcludes() {
+        excluded dir('.svn')
+        excluded file('.svn', 'abc')
+        excluded dir('a', 'b', '.svn')
+        excluded file('a', 'b', '.svn', 'c')
+        excluded file('foo', '.DS_Store')
+    }
+
+    @Test void takesGlobalExcludesFromAnt() {
+        DirectoryScanner.defaultExcludes.each {
+            DirectoryScanner.removeDefaultExclude(it)
+        }
+        included dir('.svn')
+        included file('.svn', 'abc')
+        included file('foo', '.DS_Store')
+
+        DirectoryScanner.addDefaultExclude('*X*')
+
+        excluded file('X')
     }
 
     @Test void createsSpecForIncludePatterns() {
         patternSet.include '*a*'
         patternSet.include '*b*'
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertTrue(spec.isSatisfiedBy(file('b')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
+        included file('a')
+        included file('b')
+        excluded file('c')
     }
 
     @Test void createsSpecForExcludePatterns() {
         patternSet.exclude '*b*'
         patternSet.exclude '*c*'
-        Spec<FileTreeElement> spec = patternSet.asSpec
-
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
+        
+        included file('a')
+        excluded file('b')
+        excluded file('c')
     }
 
     @Test void createsSpecForIncludeAndExcludePatterns() {
         patternSet.include '*a*'
         patternSet.exclude '*b*'
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('ab')))
-        assertFalse(spec.isSatisfiedBy(file('ba')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
+        included file('a')
+        excluded file('ab')
+        excluded file('ba')
+        excluded file('c')
+        excluded file('b')
     }
 
     @Test void createsSpecForIncludeSpecs() {
         patternSet.include({ FileTreeElement element -> element.file.name.contains('a') } as Spec)
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
+        included file('a')
+        excluded file('b')
     }
 
     @Test void createsSpecForExcludeSpecs() {
         patternSet.exclude({ FileTreeElement element -> element.file.name.contains('b') } as Spec)
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
+        included file('a')
+        excluded file('b')
     }
 
     @Test void createsSpecForIncludeAndExcludeSpecs() {
         patternSet.include({ FileTreeElement element -> element.file.name.contains('a') } as Spec)
         patternSet.exclude({ FileTreeElement element -> element.file.name.contains('b') } as Spec)
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('ab')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
+        included file('a')
+        excluded file('ab')
+        excluded file('b')
+        excluded file('c')
     }
 
     @Test void createsSpecForIncludeClosure() {
         patternSet.include { FileTreeElement element -> element.file.name.contains('a') }
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
+        included file('a')
+        excluded file('b')
     }
 
     @Test void createsSpecForExcludeClosure() {
         patternSet.exclude { FileTreeElement element -> element.file.name.contains('b') }
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('b')))
+        included file('a')
+        excluded file('b')
     }
 
     @Test void createsSpecForIncludeAndExcludeClosures() {
         patternSet.include { FileTreeElement element -> element.file.name.contains('a') }
         patternSet.exclude { FileTreeElement element -> element.file.name.contains('b') }
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('ab')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
+        included file('a')
+        excluded file('ab')
+        excluded file('c')
     }
 
     @Test void isCaseSensitiveByDefault() {
         patternSet.include '*a*'
         patternSet.exclude '*b*'
-        Spec<FileTreeElement> spec = patternSet.asSpec
-
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('A')))
-        assertFalse(spec.isSatisfiedBy(file('Ab')))
-        assertTrue(spec.isSatisfiedBy(file('aB')))
+        
+        included file('a')
+        excluded file('A')
+        excluded file('Ab')
+        included file('aB')
     }
 
     @Test void createsSpecForCaseInsensitivePatternSet() {
         patternSet.include '*a*'
         patternSet.exclude '*b*'
         patternSet.caseSensitive = false
-        Spec<FileTreeElement> spec = patternSet.asSpec
 
-        assertTrue(spec.isSatisfiedBy(file('A')))
-        assertTrue(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('AB')))
-        assertFalse(spec.isSatisfiedBy(file('bA')))
+        included file('A')
+        included file('a')
+        excluded file('AB')
+        excluded file('bA')
     }
 
     @Test void createIntersectPatternSet() {
-        patternSet.include '*a*'
-        patternSet.include { FileTreeElement element -> element.file.name.contains('1') }
-        patternSet.exclude '*b*'
-        patternSet.exclude { FileTreeElement element -> element.file.name.contains('2') }
-        PatternSet intersection = patternSet.intersect()
-        intersection.include '*c*'
-        intersection.include { FileTreeElement element -> element.file.name.contains('3') }
-        intersection.exclude '*d*'
-        intersection.exclude { FileTreeElement element -> element.file.name.contains('4') }
-        Spec<FileTreeElement> spec = intersection.asSpec
-
-        assertTrue(spec.isSatisfiedBy(file('ac')))
-        assertTrue(spec.isSatisfiedBy(file('13')))
-        assertFalse(spec.isSatisfiedBy(file('a')))
-        assertFalse(spec.isSatisfiedBy(file('1')))
-        assertFalse(spec.isSatisfiedBy(file('c')))
-        assertFalse(spec.isSatisfiedBy(file('3')))
-        assertFalse(spec.isSatisfiedBy(file('acb')))
-        assertFalse(spec.isSatisfiedBy(file('acd')))
-        assertFalse(spec.isSatisfiedBy(file('132')))
-        assertFalse(spec.isSatisfiedBy(file('132')))
-    }
-
-    @Test void globalExcludes() {
-        Spec<FileTreeElement> spec = patternSet.asSpec
-
-        assertFalse(spec.isSatisfiedBy(dir('.svn')))
-        assertFalse(spec.isSatisfiedBy(file('.svn', 'abc')))
-        assertFalse(spec.isSatisfiedBy(dir('a', 'b', '.svn')))
-        assertFalse(spec.isSatisfiedBy(file('a', 'b', '.svn', 'c')))
+        PatternSet basePatternSet = new PatternSet()
+        basePatternSet.include '*a*'
+        basePatternSet.include { FileTreeElement element -> element.file.name.contains('1') }
+        basePatternSet.exclude '*b*'
+        basePatternSet.exclude { FileTreeElement element -> element.file.name.contains('2') }
+
+        patternSet = basePatternSet.intersect()
+        patternSet.include '*c*'
+        patternSet.include { FileTreeElement element -> element.file.name.contains('3') }
+        patternSet.exclude '*d*'
+        patternSet.exclude { FileTreeElement element -> element.file.name.contains('4') }
+
+        included file('ac')
+        included file('13')
+        excluded file('a')
+        excluded file('1')
+        excluded file('c')
+        excluded file('3')
+        excluded file('acb')
+        excluded file('acd')
+        excluded file('132')
+        excluded file('132')
     }
 
     @Issue("GRADLE-2566")
@@ -221,11 +227,9 @@ class PatternSetTest extends AbstractTestForPatternSet {
         patternSet.includes = ["$a"]
         patternSet.include("$b")
 
-        Spec<FileTreeElement> spec = patternSet.asSpec
-
-        assertTrue(spec.isSatisfiedBy(file("aaa")))
-        assertTrue(spec.isSatisfiedBy(file("bbb")))
-        assertFalse(spec.isSatisfiedBy(file("ccc")))
+        included file("aaa")
+        included file("bbb")
+        excluded file("ccc")
     }
 
     @Issue("GRADLE-2566")
@@ -236,11 +240,17 @@ class PatternSetTest extends AbstractTestForPatternSet {
         patternSet.excludes = ["${a}*"]
         patternSet.exclude("${b}*")
 
-        Spec<FileTreeElement> spec = patternSet.asSpec
+        excluded file("aaa")
+        excluded file("bbb")
+        included file("ccc")
+    }
+
+    void included(FileTreeElement file) {
+        assertTrue(patternSet.asSpec.isSatisfiedBy(file))
+    }
 
-        assertFalse(spec.isSatisfiedBy(file("aaa")))
-        assertFalse(spec.isSatisfiedBy(file("bbb")))
-        assertTrue(spec.isSatisfiedBy(file("ccc")))
+    void excluded(FileTreeElement file) {
+        assertFalse(patternSet.asSpec.isSatisfiedBy(file))
     }
 
     private FileTreeElement element(boolean isFile, String... elements) {
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 1ca4464..63ba856 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
@@ -18,7 +18,7 @@ package org.gradle.cache.internal
 import org.gradle.cache.PersistentIndexedCacheParameters
 import org.gradle.cache.internal.btree.BTreePersistentIndexedCache
 import org.gradle.internal.Factory
-import org.gradle.messaging.serialize.Serializer
+import org.gradle.internal.serialize.Serializer
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
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 6006cf8..983e509 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
@@ -15,7 +15,6 @@
  */
 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
@@ -66,7 +65,7 @@ class DefaultCacheFactoryTest extends Specification {
 
     public void "creates directory backed cache instance"() {
         when:
-        def cache = factory.open(tmpDir.testDirectory, "<display>", CacheUsage.ON, null, [prop: 'value'], mode(Shared), null)
+        def cache = factory.open(tmpDir.testDirectory, "<display>", null, [prop: 'value'], mode(Shared), null)
 
         then:
         cache.reference.cache instanceof DefaultPersistentDirectoryCache
@@ -76,8 +75,8 @@ class DefaultCacheFactoryTest extends Specification {
 
     public void "reuses directory backed cache instances"() {
         when:
-        def ref1 = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
-        def ref2 = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        def ref1 = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
+        def ref2 = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         then:
         ref1.reference.cache.is(ref2.reference.cache)
@@ -104,7 +103,7 @@ class DefaultCacheFactoryTest extends Specification {
         def implementation
 
         when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         then:
         1 * opened.execute(_) >> { DefaultPersistentDirectoryStore s -> implementation = s }
@@ -122,8 +121,8 @@ class DefaultCacheFactoryTest extends Specification {
         def implementation
 
         when:
-        def cache1 = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
-        def cache2 = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        def cache1 = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
+        def cache2 = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         then:
         1 * opened.execute(_) >> { DefaultPersistentDirectoryStore s -> implementation = s }
@@ -147,7 +146,7 @@ class DefaultCacheFactoryTest extends Specification {
         def implementation
 
         when:
-        def cache = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        def cache = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         then:
         1 * opened.execute(_) >> { DefaultPersistentDirectoryStore s -> implementation = s }
@@ -166,7 +165,7 @@ class DefaultCacheFactoryTest extends Specification {
         def implementation
 
         when:
-        def cache = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        def cache = factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         then:
         1 * opened.execute(_) >> { DefaultPersistentDirectoryStore s -> implementation = s }
@@ -183,57 +182,22 @@ class DefaultCacheFactoryTest extends Specification {
 
     public void "fails when directory cache is already open with different properties"() {
         given:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
+        factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Exclusive), null)
 
         when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'other'], mode(Exclusive), null)
+        factory.open(tmpDir.testDirectory, null, null, [prop: 'other'], mode(Exclusive), null)
 
         then:
         IllegalStateException e = thrown()
         e.message == "Cache '${tmpDir.testDirectory}' is already open with different state."
     }
 
-    public void "fails when directory cache is already open when rebuild is requested"() {
-        given:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Exclusive), null)
-
-        when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.REBUILD, null, [prop: 'value'], mode(Exclusive), null)
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == "Cannot rebuild cache '${tmpDir.testDirectory}' as it is already open."
-    }
-
-    public void "can open directory cache when rebuild is requested and cache was rebuilt in same session"() {
-        given:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.REBUILD, null, [prop: 'value'], mode(Exclusive), null)
-
-        when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.REBUILD, null, [prop: 'value'], mode(Exclusive), null)
-
-        then:
-        notThrown(RuntimeException)
-    }
-
-    public void "can open directory cache when rebuild is requested and has been closed"() {
-        given:
-        def cache = factory.open(tmpDir.testDirectory, null, CacheUsage.REBUILD, null, [prop: 'value'], mode(Exclusive), null)
-        cache.close()
-
-        when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.REBUILD, null, [prop: 'value'], mode(Exclusive), null)
-
-        then:
-        notThrown(RuntimeException)
-    }
-
     public void "fails when directory cache when cache is already open with different lock mode"() {
         given:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'value'], mode(Shared), null)
+        factory.open(tmpDir.testDirectory, null, null, [prop: 'value'], mode(Shared), null)
 
         when:
-        factory.open(tmpDir.testDirectory, null, CacheUsage.ON, null, [prop: 'other'], mode(Exclusive), null)
+        factory.open(tmpDir.testDirectory, null, null, [prop: 'other'], mode(Exclusive), null)
 
         then:
         IllegalStateException e = thrown()
@@ -245,7 +209,7 @@ class DefaultCacheFactoryTest extends Specification {
         CacheValidator validator = Mock()
 
         when:
-        def cache = factory.open(tmpDir.testDirectory, null, CacheUsage.ON, validator, [prop: 'value'], mode(Shared), null)
+        def cache = factory.open(tmpDir.testDirectory, null, validator, [prop: 'value'], mode(Shared), null)
 
         then:
         validator.isValid() >>> [false, true]
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheRepositoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheRepositoryTest.groovy
index 7cbd017..f7b68b0 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheRepositoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultCacheRepositoryTest.groovy
@@ -15,7 +15,6 @@
  */
 package org.gradle.cache.internal
 
-import org.gradle.CacheUsage
 import org.gradle.api.Action
 import org.gradle.cache.CacheBuilder
 import org.gradle.cache.CacheValidator
@@ -40,7 +39,7 @@ class DefaultCacheRepositoryTest extends Specification {
     private final CacheFactory cacheFactory = Mock()
     private final PersistentCache cache = Mock()
     private final CacheScopeMapping scopeMapping = Mock()
-    private final DefaultCacheRepository repository = new DefaultCacheRepository(scopeMapping, CacheUsage.ON, cacheFactory)
+    private final DefaultCacheRepository repository = new DefaultCacheRepository(scopeMapping, cacheFactory)
 
     public void createsGlobalDirectoryBackedStore() {
         when:
@@ -60,7 +59,7 @@ class DefaultCacheRepositoryTest extends Specification {
         then:
         result == cache
         1 * scopeMapping.getBaseDirectory(null, "a/b/c", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(Shared), null) >> cache
         0 * cacheFactory._
     }
 
@@ -70,7 +69,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory(null, "a/b/c", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, properties, mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, properties, mode(Shared), null) >> cache
     }
 
     public void createsScopedCache() {
@@ -79,7 +78,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory("scope", "a/b/c", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(Shared), null) >> cache
     }
 
     public void createsCacheWithBaseDirectory() {
@@ -87,7 +86,7 @@ class DefaultCacheRepositoryTest extends Specification {
         repository.cache(sharedCacheDir).open()
 
         then:
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(Shared), null) >> cache
     }
 
     public void createsCrossVersionCache() {
@@ -96,7 +95,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory("scope", "a/b/c", CacheBuilder.VersionStrategy.SharedCache) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(Shared), null) >> cache
     }
 
     public void canSpecifyInitializerActionForDirectoryCache() {
@@ -107,7 +106,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory(null, "a", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(Shared), action) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(Shared), action) >> cache
     }
 
     public void canSpecifyLockModeForDirectoryCache() {
@@ -116,7 +115,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory(null, "a", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, null, [:], mode(None), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, null, [:], mode(None), null) >> cache
     }
 
     public void canSpecifyDisplayNameForDirectoryCache() {
@@ -125,7 +124,7 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory(null, "a", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, "<cache>", CacheUsage.ON, null, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, "<cache>", null, [:], mode(Shared), null) >> cache
     }
 
     public void canSpecifyCacheValidatorForDirectoryCache() {
@@ -135,6 +134,6 @@ class DefaultCacheRepositoryTest extends Specification {
 
         then:
         1 * scopeMapping.getBaseDirectory(null, "a", CacheBuilder.VersionStrategy.CachePerVersion) >> sharedCacheDir
-        1 * cacheFactory.open(sharedCacheDir, null, CacheUsage.ON, validator, [:], mode(Shared), null) >> cache
+        1 * cacheFactory.open(sharedCacheDir, null, validator, [:], mode(Shared), null) >> cache
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheSpec.groovy
index b4c1a66..ea79581 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryCacheSpec.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.cache.internal
 
-import org.gradle.CacheUsage
 import org.gradle.api.Action
 import org.gradle.cache.CacheValidator
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -38,7 +37,7 @@ class DefaultPersistentDirectoryCacheSpec extends Specification {
         def init = { initd = true } as Action
         unlockUncleanly(new File(dir, "cache.properties"))
         def cache = new DefaultPersistentDirectoryCache(
-                dir, "test", CacheUsage.ON, { true } as CacheValidator, [:], mode(FileLockManager.LockMode.Exclusive), init, createDefaultFileLockManager()
+                dir, "test", { true } as CacheValidator, [:], mode(FileLockManager.LockMode.Exclusive), init, createDefaultFileLockManager()
         )
         
         when:
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 b5b09dd..6e489f9 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
@@ -15,7 +15,6 @@
  */
 package org.gradle.cache.internal;
 
-import org.gradle.CacheUsage;
 import org.gradle.api.Action;
 import org.gradle.cache.CacheOpenException;
 import org.gradle.cache.CacheValidator;
@@ -74,7 +73,7 @@ public class DefaultPersistentDirectoryCacheTest {
             one(action).execute(with(notNullValue(PersistentCache.class)));
         }});
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(emptyDir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(emptyDir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         assertThat(loadProperties(emptyDir.file("cache.properties")), equalTo(properties));
     }
@@ -87,7 +86,7 @@ public class DefaultPersistentDirectoryCacheTest {
             one(action).execute(with(notNullValue(PersistentCache.class)));
         }});
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         assertThat(loadProperties(dir.file("cache.properties")), equalTo(properties));
     }
@@ -100,25 +99,12 @@ public class DefaultPersistentDirectoryCacheTest {
             one(action).execute(with(notNullValue(PersistentCache.class)));
         }});
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         assertThat(loadProperties(dir.file("cache.properties")), equalTo(properties));
     }
 
     @Test
-    public void rebuildsCacheWhenCacheRebuildRequested() {
-        TestFile dir = createCacheDir();
-
-        context.checking(new Expectations() {{
-            one(action).execute(with(notNullValue(PersistentCache.class)));
-        }});
-
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.REBUILD, validator, properties, mode(LockMode.Shared), action, lockManager);
-        cache.open();
-        assertThat(loadProperties(dir.file("cache.properties")), equalTo(properties));
-    }
-    
-    @Test
     public void rebuildsCacheWhenCacheValidatorReturnsFalse() {
         TestFile dir = createCacheDir();
         final CacheValidator invalidator = context.mock(CacheValidator.class);
@@ -132,7 +118,7 @@ public class DefaultPersistentDirectoryCacheTest {
 
         }});
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, invalidator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", invalidator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         assertThat(loadProperties(dir.file("cache.properties")), equalTo(properties));
     }
@@ -148,7 +134,7 @@ public class DefaultPersistentDirectoryCacheTest {
         }});
 
         try {
-            new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager).open();
+            new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager).open();
             fail();
         } catch (CacheOpenException e) {
             assertThat(e.getCause(), sameInstance((Throwable) failure));
@@ -158,7 +144,7 @@ public class DefaultPersistentDirectoryCacheTest {
             one(action).execute(with(notNullValue(PersistentCache.class)));
         }});
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         assertThat(loadProperties(dir.file("cache.properties")), equalTo(properties));
     }
@@ -167,7 +153,7 @@ public class DefaultPersistentDirectoryCacheTest {
     public void doesNotInitializeCacheWhenCacheDirExistsAndIsNotInvalid() {
         TestFile dir = createCacheDir();
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), action, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), action, lockManager);
         cache.open();
         dir.file("cache.properties").assertIsFile();
         dir.file("some-file").assertIsFile();
@@ -189,7 +175,7 @@ public class DefaultPersistentDirectoryCacheTest {
         properties.putAll(this.properties);
         properties.putAll(GUtil.map((Object[]) extraProps));
 
-        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", CacheUsage.ON, validator, properties, mode(LockMode.Shared), null, lockManager);
+        DefaultPersistentDirectoryCache cache = new DefaultPersistentDirectoryCache(dir, "<display-name>", validator, properties, mode(LockMode.Shared), null, lockManager);
         cache.open();
         dir.file("some-file").touch();
         cache.close();
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStoreConcurrencyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStoreConcurrencyTest.groovy
new file mode 100644
index 0000000..8009dba
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultPersistentDirectoryStoreConcurrencyTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.cache.internal.locklistener.NoOpFileLockContentionHandler
+import org.gradle.internal.nativeintegration.ProcessEnvironment
+import org.gradle.internal.serialize.NullSafeStringSerializer
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.junit.Rule
+import spock.lang.Issue
+
+import static org.gradle.cache.internal.FileLockManager.LockMode.None
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode
+
+class DefaultPersistentDirectoryStoreConcurrencyTest extends ConcurrentSpec {
+
+    @Rule
+    def TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def cacheDir = tmpDir.file("dir")
+    def metaDataProvider = new DefaultProcessMetaDataProvider(NativeServicesTestFixture.getInstance().get(ProcessEnvironment));
+    def lockManager = new DefaultFileLockManager(metaDataProvider, new NoOpFileLockContentionHandler())
+
+
+    @Issue("GRADLE-3206")
+    def "can create new caches and access them in parallel"() {
+        def store = new DefaultPersistentDirectoryStore(cacheDir, "<display>", mode(None), lockManager)
+        store.open()
+
+        when:
+        async {
+            200.times { index ->
+                start {
+                    store.createCache(index.toString(), String, new NullSafeStringSerializer())
+                    store.useCache(index.toString()) {}
+                }
+            }
+        }
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProviderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProviderTest.groovy
index 4a62257..470eef3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProviderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/DefaultProcessMetaDataProviderTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.cache.internal
 
 import spock.lang.Specification
-import org.gradle.internal.nativeplatform.ProcessEnvironment
+import org.gradle.internal.nativeintegration.ProcessEnvironment
 
 class DefaultProcessMetaDataProviderTest extends Specification {
     final ProcessEnvironment processEnvironment = Mock()
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/SimpleStateCacheTest.groovy b/subprojects/core/src/test/groovy/org/gradle/cache/internal/SimpleStateCacheTest.groovy
index a9d4097..f6efbcb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/SimpleStateCacheTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/SimpleStateCacheTest.groovy
@@ -16,8 +16,8 @@
 package org.gradle.cache.internal
 
 import org.gradle.cache.PersistentStateCache
-import org.gradle.messaging.serialize.DefaultSerializer
-import org.gradle.messaging.serialize.Serializer
+import org.gradle.internal.serialize.DefaultSerializer
+import org.gradle.internal.serialize.Serializer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
diff --git a/subprojects/core/src/test/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCacheTest.java b/subprojects/core/src/test/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCacheTest.java
index 272a629..69e4ca7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCacheTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/cache/internal/btree/BTreePersistentIndexedCacheTest.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.cache.internal.btree;
 
-import org.gradle.messaging.serialize.DefaultSerializer;
-import org.gradle.messaging.serialize.Serializer;
+import org.gradle.internal.serialize.DefaultSerializer;
+import org.gradle.internal.serialize.Serializer;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.junit.Before;
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 7a7b531..c7c39de 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
@@ -18,13 +18,15 @@ package org.gradle.configuration
 import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.execution.ProjectConfigurer
 import spock.lang.Specification
 
 class DefaultBuildConfigurerTest extends Specification {
     private startParameter = Mock(StartParameter)
     private gradle = Mock(GradleInternal)
     private rootProject = Mock(ProjectInternal)
-    private configurer = new DefaultBuildConfigurer()
+    private projectConfigurer = Mock(ProjectConfigurer)
+    private configurer = new DefaultBuildConfigurer(projectConfigurer)
 
     def setup() {
         gradle.startParameter >> startParameter
@@ -32,19 +34,11 @@ 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 * rootProject.evaluate()
-        1 * child1.evaluate()
-        1 * child2.evaluate()
+        1 * projectConfigurer.configureHierarchy(rootProject)
     }
 
     def "configures build for on demand mode"() {
@@ -53,7 +47,6 @@ class DefaultBuildConfigurerTest extends Specification {
 
         then:
         startParameter.isConfigureOnDemand() >> true
-        1 * rootProject.evaluate()
-        0 * rootProject._
+        1 * projectConfigurer.configure(rootProject)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultImportsReaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultImportsReaderTest.groovy
new file mode 100644
index 0000000..a4ef056
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultImportsReaderTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.configuration
+
+import org.gradle.util.Resources
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultImportsReaderTest extends Specification {
+    @Rule
+    public Resources resources = new Resources()
+    DefaultImportsReader reader = new DefaultImportsReader()
+
+    public void testReadImportPackagesFromResource() {
+        expect:
+        reader.importPackages.contains('org.gradle.api')
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultInitScriptProcessorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultInitScriptProcessorTest.groovy
index 85d7059..322d9eb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultInitScriptProcessorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultInitScriptProcessorTest.groovy
@@ -21,6 +21,7 @@ import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.initialization.InitScript
+import org.gradle.internal.resource.Resource
 import spock.lang.Specification
 
 class DefaultInitScriptProcessorTest extends Specification {
@@ -29,18 +30,23 @@ class DefaultInitScriptProcessorTest extends Specification {
         when:
         def scriptPluginFactory = Mock(ScriptPluginFactory)
         def scriptHandlerFactory = Mock(ScriptHandlerFactory)
-        def compileScope = Mock(ClassLoaderScope)
-        def initScriptMock = Mock(ScriptSource)
+        def gradleScope = Mock(ClassLoaderScope)
+        def uri = new URI("file:///foo")
+        def initScriptMock = Mock(ScriptSource) {
+            getResource() >> Mock(Resource) {
+                getURI() >> uri
+            }
+        }
         def gradleMock = Mock(GradleInternal)
+        def siblingScope = Mock(ClassLoaderScope)
         def scriptHandler = Mock(ScriptHandler)
         def scriptPlugin = Mock(ScriptPlugin)
 
-        1 * gradleMock.getClassLoaderScope() >> Mock(ClassLoaderScope) {
-            createSibling() >> compileScope
-        }
+        1 * gradleMock.getClassLoaderScope() >> gradleScope
+        1 * gradleScope.createChild("init-$uri") >> siblingScope
 
-        1 * scriptHandlerFactory.create(initScriptMock, compileScope) >> scriptHandler
-        1 * scriptPluginFactory.create(initScriptMock, scriptHandler, compileScope, "initscript", InitScript) >> scriptPlugin
+        1 * scriptHandlerFactory.create(initScriptMock, siblingScope) >> scriptHandler
+        1 * scriptPluginFactory.create(initScriptMock, scriptHandler, siblingScope, gradleScope, "initscript", InitScript, false) >> scriptPlugin
         1 * scriptPlugin.apply(gradleMock)
 
         DefaultInitScriptProcessor processor = new DefaultInitScriptProcessor(scriptPluginFactory, scriptHandlerFactory)
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.groovy
index 57aabeb..aaf84e5 100755
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.groovy
@@ -18,42 +18,49 @@ package org.gradle.configuration
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.initialization.dsl.ScriptHandler
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.file.FileLookup
 import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
 import org.gradle.groovy.scripts.*
-import org.gradle.groovy.scripts.internal.StatementExtractingScriptTransformer
+import org.gradle.groovy.scripts.internal.CompiledScript
+import org.gradle.groovy.scripts.internal.FactoryBackedCompileOperation
 import org.gradle.internal.Factory
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.logging.LoggingManagerInternal
-import org.gradle.plugin.internal.PluginResolverFactory
+import org.gradle.model.dsl.internal.transform.ClosureCreationInterceptingVerifier
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.plugin.use.internal.PluginRequestApplicator
 import spock.lang.Specification
 
 public class DefaultScriptPluginFactoryTest extends Specification {
 
     def scriptCompilerFactory = Mock(ScriptCompilerFactory)
-    def importsReader = Mock(ImportsReader)
     def scriptCompiler = Mock(ScriptCompiler)
     def scriptSource = Mock(ScriptSource)
     def scriptRunner = Mock(ScriptRunner)
     def script = Mock(BasicScript)
     def instantiator = Mock(Instantiator)
-    def classLoaderScope = Mock(ClassLoaderScope)
-    def baseClassLoaderScope = Mock(ClassLoaderScope)
+    def targetScope = Mock(ClassLoaderScope)
+    def baseScope = Mock(ClassLoaderScope)
     def scopeClassLoader = Mock(ClassLoader)
     def baseChildClassLoader = Mock(ClassLoader)
     def scriptHandlerFactory = Mock(ScriptHandlerFactory)
-    def pluginResolverFactory = Mock(PluginResolverFactory)
+    def pluginRequestApplicator = Mock(PluginRequestApplicator)
     def scriptHandler = Mock(ScriptHandler)
     def classPathScriptRunner = Mock(ScriptRunner)
     def classPathScript = Mock(BasicScript)
     def loggingManagerFactory = Mock(Factory) as Factory<LoggingManagerInternal>
-    def sourceWithImports = Mock(ScriptSource)
     def loggingManager = Mock(LoggingManagerInternal)
     def fileLookup = Mock(FileLookup)
+    def documentationRegistry = Mock(DocumentationRegistry)
+    def classpathClosureName = "buildscript"
+    def compiledScript = Mock(CompiledScript)
+    def classpathCompiledScript = Mock(CompiledScript)
 
-    def factory = new DefaultScriptPluginFactory(scriptCompilerFactory, importsReader, loggingManagerFactory, instantiator, scriptHandlerFactory, pluginResolverFactory, fileLookup)
+    def factory = new DefaultScriptPluginFactory(scriptCompilerFactory, loggingManagerFactory, instantiator, scriptHandlerFactory, pluginRequestApplicator, fileLookup,
+            documentationRegistry, new ModelRuleSourceDetector())
 
     def setup() {
         def configurations = Mock(ConfigurationContainer)
@@ -61,10 +68,9 @@ public class DefaultScriptPluginFactoryTest extends Specification {
         def configuration = Mock(Configuration)
         configurations.getByName(ScriptHandler.CLASSPATH_CONFIGURATION) >> configuration
         configuration.getFiles() >> Collections.emptySet()
-        classLoaderScope.getBase() >> baseClassLoaderScope
-        baseClassLoaderScope.getChildClassLoader() >> baseChildClassLoader
+        baseScope.getExportClassLoader() >> baseChildClassLoader
 
-        1 * classLoaderScope.getScopeClassLoader() >> scopeClassLoader
+        1 * targetScope.getLocalClassLoader() >> scopeClassLoader
     }
 
     void configuresATargetObjectUsingScript() {
@@ -72,23 +78,21 @@ public class DefaultScriptPluginFactoryTest extends Specification {
         final Object target = new Object()
 
         1 * loggingManagerFactory.create() >> loggingManager
-        1 * importsReader.withImports(scriptSource) >> sourceWithImports
-        1 * scriptCompilerFactory.createCompiler(sourceWithImports) >> scriptCompiler
-        1 * scriptCompiler.setClassloader(baseChildClassLoader)
-        1 * scriptCompiler.setTransformer(_ as StatementExtractingScriptTransformer)
-        1 * scriptCompiler.compile(DefaultScript) >> classPathScriptRunner
+        1 * scriptCompilerFactory.createCompiler(scriptSource) >> scriptCompiler
+        1 * scriptCompiler.compile(DefaultScript, _ as FactoryBackedCompileOperation, baseChildClassLoader, classpathClosureName, _) >> classPathScriptRunner
         1 * classPathScriptRunner.getScript() >> classPathScript
         1 * classPathScript.init(target, _ as ServiceRegistry)
         1 * classPathScriptRunner.run()
-        1 * scriptCompiler.setClassloader(scopeClassLoader)
-        1 * scriptCompiler.setTransformer(!null)
-        1 * scriptCompiler.compile(DefaultScript) >> scriptRunner
+        1 * classPathScriptRunner.getCompiledScript() >> classpathCompiledScript
+        1 * scriptCompiler.compile(DefaultScript, { it.transformer != null }, scopeClassLoader, classpathClosureName, ClosureCreationInterceptingVerifier.INSTANCE) >> scriptRunner
         1 * scriptRunner.getScript() >> script
+        1 * scriptRunner.compiledScript >> compiledScript
+        1 * compiledScript.data >> true
         1 * script.init(target, _ as ServiceRegistry)
         1 * scriptRunner.run()
 
         then:
-        ScriptPlugin configurer = factory.create(scriptSource, scriptHandler, classLoaderScope, "buildscript", DefaultScript)
+        ScriptPlugin configurer = factory.create(scriptSource, scriptHandler, targetScope, baseScope, "buildscript", DefaultScript, false)
         configurer.apply(target)
     }
 
@@ -97,23 +101,21 @@ public class DefaultScriptPluginFactoryTest extends Specification {
         def target = Mock(ScriptAware)
 
         1 * loggingManagerFactory.create() >> loggingManager
-        1 * importsReader.withImports(scriptSource) >> sourceWithImports
-        1 * scriptCompilerFactory.createCompiler(sourceWithImports) >> scriptCompiler
-        1 * scriptCompiler.setClassloader(baseChildClassLoader)
-        1 * scriptCompiler.setTransformer(_ as StatementExtractingScriptTransformer)
-        1 * scriptCompiler.compile(DefaultScript) >> classPathScriptRunner
+        1 * scriptCompilerFactory.createCompiler(scriptSource) >> scriptCompiler
+        1 * scriptCompiler.compile(DefaultScript, _ as FactoryBackedCompileOperation, baseChildClassLoader, classpathClosureName, _) >> classPathScriptRunner
         1 * classPathScriptRunner.getScript() >> classPathScript
         1 * classPathScript.init(target, _ as ServiceRegistry)
         1 * classPathScriptRunner.run()
-        1 * scriptCompiler.setClassloader(scopeClassLoader)
-        1 * scriptCompiler.setTransformer(!null)
-        1 * scriptCompiler.compile(DefaultScript) >> scriptRunner
+        1 * classPathScriptRunner.getCompiledScript() >> classpathCompiledScript
+        1 * scriptCompiler.compile(DefaultScript, { it.transformer != null }, scopeClassLoader, classpathClosureName, ClosureCreationInterceptingVerifier.INSTANCE) >> scriptRunner
         1 * scriptRunner.getScript() >> script
+        1 * scriptRunner.compiledScript >> compiledScript
+        1 * compiledScript.data >> true
         1 * script.init(target, _ as ServiceRegistry)
         1 * scriptRunner.run()
 
         then:
-        ScriptPlugin configurer = factory.create(scriptSource, scriptHandler, classLoaderScope, "buildscript", DefaultScript)
+        ScriptPlugin configurer = factory.create(scriptSource, scriptHandler, targetScope, baseScope, "buildscript", DefaultScript, false)
         configurer.apply(target)
     }
 }
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy
deleted file mode 100644
index e75959a..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsReaderTest.groovy
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.configuration
-
-import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.util.Resources
-import org.junit.Rule
-import spock.lang.Specification
-
-class ImportsReaderTest extends Specification {
-    @Rule public Resources resources = new Resources()
-    ImportsReader reader = new ImportsReader()
-
-    public void testReadImportsFromResource() {
-        expect:
-        reader.imports.contains('import org.gradle.api.*')
-    }
-
-    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/ImportsScriptSourceTest.java b/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsScriptSourceTest.java
deleted file mode 100644
index 0760af7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/ImportsScriptSourceTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.configuration;
-
-import org.gradle.api.internal.resource.Resource;
-import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.util.JUnit4GroovyMockery;
-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.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class ImportsScriptSourceTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private ScriptSource backingSource;
-    private ImportsReader importsReader;
-    private ImportsScriptSource source;
-    private Resource resource;
-
-    @Before
-    public void setUp() {
-        backingSource = context.mock(ScriptSource.class);
-        importsReader = context.mock(ImportsReader.class);
-        resource = context.mock(Resource.class);
-        source = new ImportsScriptSource(backingSource, importsReader);
-    }
-    
-    @Test
-    public void prependsImportsToScriptText() {
-        context.checking(new Expectations() {{
-            one(backingSource).getResource();
-            will(returnValue(resource));
-
-            one(resource).getText();
-            will(returnValue("<content>"));
-
-            one(importsReader).getImports();
-            will(returnValue("<imports>"));
-        }});
-
-        assertThat(source.getResource().getText(), equalTo("<content>\n<imports>"));
-    }
-
-    @Test
-    public void doesNotPrependImportsWhenScriptHasNoText() {
-        context.checking(new Expectations(){{
-            one(backingSource).getResource();
-            will(returnValue(resource));
-
-            one(resource).getText();
-            will(returnValue(""));
-        }});
-
-        assertThat(source.getResource().getText(), equalTo(""));
-    }
-
-    @Test
-    public void doesNotPrependImportsWhenScriptContainsOnlyWhitespace() {
-        context.checking(new Expectations(){{
-            one(backingSource).getResource();
-            will(returnValue(resource));
-
-            one(resource).getText();
-            will(returnValue(" \n\t"));
-        }});
-
-        assertThat(source.getResource().getText(), equalTo(" \n\t"));
-    }
-
-    @Test
-    public void delegatesAllOtherMethodsToBackingScriptSource() {
-        context.checking(new Expectations(){{
-            one(backingSource).getClassName();
-            will(returnValue("classname"));
-
-            one(backingSource).getDisplayName();
-            will(returnValue("description"));
-        }});
-
-        assertThat(source.getClassName(), equalTo("classname"));
-        assertThat(source.getDisplayName(), equalTo("description"));
-    }
-}
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
index 40044b3..eedfd4a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/project/BuildScriptProcessorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/BuildScriptProcessorTest.groovy
@@ -29,7 +29,8 @@ public class BuildScriptProcessorTest extends Specification {
     def scriptSource = Mock(ScriptSource)
     def configurerFactory = Mock(ScriptPluginFactory)
     def scriptPlugin = Mock(ScriptPlugin)
-    def classLoaderScope = Mock(ClassLoaderScope)
+    def targetScope = Mock(ClassLoaderScope)
+    def baseScope = Mock(ClassLoaderScope)
     def BuildScriptProcessor buildScriptProcessor = new BuildScriptProcessor(configurerFactory)
     private ScriptHandler scriptHandler;
 
@@ -37,7 +38,8 @@ public class BuildScriptProcessorTest extends Specification {
         _ * project.buildScriptSource >> scriptSource
         scriptHandler = Mock(ScriptHandler)
         project.getBuildscript() >> scriptHandler
-        project.getClassLoaderScope() >> classLoaderScope
+        project.getClassLoaderScope() >> targetScope
+        project.getBaseClassLoaderScope() >> baseScope
     }
 
     def configuresProjectUsingBuildScript() {
@@ -45,7 +47,7 @@ public class BuildScriptProcessorTest extends Specification {
         buildScriptProcessor.execute(project)
 
         then:
-        1 * configurerFactory.create(scriptSource, scriptHandler, classLoaderScope, "buildscript", ProjectScript) >> scriptPlugin
+        1 * configurerFactory.create(scriptSource, scriptHandler, targetScope, baseScope, "buildscript", ProjectScript, true) >> scriptPlugin
         1 * scriptPlugin.apply(project)
     }
 }
\ No newline at end of file
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
index ffa970c..fccba29 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/project/LifecycleProjectEvaluatorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/LifecycleProjectEvaluatorTest.groovy
@@ -26,8 +26,8 @@ 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)
+    private state = Mock(ProjectStateInternal)
 
     void setup() {
         project.getProjectEvaluationBroadcaster() >> listener
@@ -86,6 +86,9 @@ public class LifecycleProjectEvaluatorTest extends Specification {
         })
         1 * state.setExecuting(false)
         1 * listener.afterEvaluate(project, state)
+
+        and:
+        _ * state.hasFailure() >> true
     }
 
     void "updates state and does not delegate when beforeEvaluate action fails"() {
@@ -121,6 +124,9 @@ public class LifecycleProjectEvaluatorTest extends Specification {
         1 * state.executed({
             assertIsConfigurationFailure(it, failure)
         })
+
+        and:
+        state.hasFailure() >>> [false, true]
     }
 
     def assertIsConfigurationFailure(def it, def cause) {
@@ -146,7 +152,8 @@ public class LifecycleProjectEvaluatorTest extends Specification {
 
         then:
         1 * listener.afterEvaluate(project, state) >> { throw new RuntimeException("afterEvaluate") }
-        1 * state.hasFailure() >> true
+        _ * 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
index d4ec1aa..74745f2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/project/PluginsProjectConfigureActionsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/configuration/project/PluginsProjectConfigureActionsTest.groovy
@@ -20,18 +20,23 @@ import spock.lang.Specification
 
 class PluginsProjectConfigureActionsTest extends Specification {
     final def pluginsClassLoader = Mock(ClassLoader)
-    final def evaluator = new PluginsProjectConfigureActions(pluginsClassLoader)
+
+    private PluginsProjectConfigureActions createActions() {
+        new PluginsProjectConfigureActions(pluginsClassLoader)
+    }
 
     def "executes all implicit configuration actions"() {
         def project = Mock(ProjectInternal)
 
         when:
+        def evaluator = createActions()
+        evaluator.execute(project)
         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)
+        2 * project.setVersion(12)
         0 * _._
     }
 
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
deleted file mode 100644
index 9be41ed..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/configuration/project/ProjectDependencies2TaskResolverTest.groovy
+++ /dev/null
@@ -1,37 +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.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/DefaultTasksBuildExecutionActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/DefaultTasksBuildExecutionActionTest.groovy
index 2b28cff..7404a2b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/DefaultTasksBuildExecutionActionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/DefaultTasksBuildExecutionActionTest.groovy
@@ -15,16 +15,18 @@
  */
 package org.gradle.execution
 
-import spock.lang.Specification
 import org.gradle.StartParameter
-import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.DefaultTaskExecutionRequest
+import spock.lang.Specification
 
 class DefaultTasksBuildExecutionActionTest extends Specification {
-    final DefaultTasksBuildExecutionAction action = new DefaultTasksBuildExecutionAction()
-    final BuildExecutionContext context = Mock()
-    final StartParameter startParameter = Mock()
-    final ProjectInternal defaultProject = Mock()
+    final projectConfigurer = Mock(ProjectConfigurer)
+    final DefaultTasksBuildExecutionAction action = new DefaultTasksBuildExecutionAction(projectConfigurer)
+    final context = Mock(BuildExecutionContext)
+    final startParameter = Mock(StartParameter)
+    final defaultProject = Mock(ProjectInternal)
 
     def setup() {
         GradleInternal gradle = Mock()
@@ -35,7 +37,7 @@ class DefaultTasksBuildExecutionActionTest extends Specification {
 
     def "proceeds when task names specified in StartParameter"() {
         given:
-        _ * startParameter.taskNames >> ['a']
+        _ * startParameter.taskRequests >> [ new DefaultTaskExecutionRequest(['a']) ]
 
         when:
         action.configure(context)
@@ -44,9 +46,9 @@ class DefaultTasksBuildExecutionActionTest extends Specification {
         1 * context.proceed()
     }
 
-    def "sets task names to project defaults when none specified in StartParameter"() {
+    def "sets task names to project defaults when no requests specified in StartParameter"() {
         given:
-        _ * startParameter.taskNames >> []
+        _ * startParameter.taskRequests >> []
         _ * defaultProject.defaultTasks >> ['a', 'b']
 
         when:
@@ -59,7 +61,7 @@ class DefaultTasksBuildExecutionActionTest extends Specification {
 
     def "uses the help task if no tasks specified in StartParameter or project"() {
         given:
-        _ * startParameter.taskNames >> []
+        _ * startParameter.taskRequests >> []
         _ * defaultProject.defaultTasks >> []
 
         when:
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationActionTest.groovy
index a0a38b5..ce52d97 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationActionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/ExcludedTaskFilteringBuildConfigurationActionTest.groovy
@@ -17,26 +17,21 @@ package org.gradle.execution
 
 import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
-import org.gradle.internal.service.ServiceRegistry
+import org.gradle.api.specs.Spec
 import spock.lang.Specification
 
-import static java.util.Collections.emptySet
-
 class ExcludedTaskFilteringBuildConfigurationActionTest extends Specification {
     final BuildExecutionContext context = Mock()
     final StartParameter startParameter = Mock()
     final TaskGraphExecuter taskGraph = Mock()
     final TaskSelector selector = Mock()
     final GradleInternal gradle = Mock()
-    final ServiceRegistry services = Mock()
-    final action = new ExcludedTaskFilteringBuildConfigurationAction()
+    final action = new ExcludedTaskFilteringBuildConfigurationAction(selector)
 
     def setup() {
         _ * context.gradle >> gradle
         _ * gradle.startParameter >> startParameter
         _ * gradle.taskGraph >> taskGraph
-        _ * gradle.services >> services
-        _ * services.get(TaskSelector) >> selector
     }
 
     def "calls proceed when there are no excluded tasks defined"() {
@@ -51,6 +46,8 @@ class ExcludedTaskFilteringBuildConfigurationActionTest extends Specification {
     }
 
     def "applies a filter for excluded tasks before proceeding"() {
+        def filter = Stub(Spec)
+
         given:
         _ * startParameter.excludedTaskNames >> ['a']
 
@@ -58,8 +55,8 @@ class ExcludedTaskFilteringBuildConfigurationActionTest extends Specification {
         action.configure(context)
 
         then:
-        1 * selector.getSelection('a') >> new TaskSelector.TaskSelection('a', emptySet())
-        1 * taskGraph.useFilter(!null)
+        1 * selector.getFilter('a') >> filter
+        1 * taskGraph.useFilter({it.specs == [filter]})
         1 * context.proceed()
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy
deleted file mode 100644
index abd3159..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/execution/ProjectEvaluatingActionTest.groovy
+++ /dev/null
@@ -1,66 +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.execution
-
-import org.gradle.StartParameter
-import org.gradle.api.internal.GradleInternal
-import org.gradle.api.internal.project.DefaultProject
-import spock.lang.Specification
-
-class ProjectEvaluatingActionTest extends Specification {
-
-    private evaluator = Mock(TaskPathProjectEvaluator)
-    private context = Mock(BuildExecutionContext)
-    private startParameter = Mock(StartParameter)
-    private gradle = Mock(GradleInternal)
-    private project = Mock(DefaultProject)
-
-    private action = new ProjectEvaluatingAction(evaluator)
-
-    def setup() {
-        context.gradle >> gradle
-        gradle.startParameter >> startParameter
-        gradle.defaultProject >> project
-    }
-
-    def "evaluates projects by task paths and proceeds"() {
-        when:
-        action.configure(context)
-
-        then:
-        startParameter.taskNames >> ['foo', "bar:baz"]
-
-        1 * context.proceed()
-        1 * evaluator.evaluateByPath(project, 'foo')
-        1 * evaluator.evaluateByPath(project, 'bar:baz')
-        0 * project.evaluate()
-        0 * evaluator._
-    }
-
-    def "evaluates the default project when the task names are empty"() {
-        when:
-        action.configure(context)
-
-        then:
-        startParameter.taskNames >> []
-
-        1 * context.proceed()
-        1 * project.evaluate()
-        0 * project._
-        0 * evaluator._
-    }
-}
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 f9dda3a..6a9ddd3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
@@ -15,168 +15,273 @@
  */
 package org.gradle.execution
 
+import org.gradle.api.Action
 import org.gradle.api.Task
+import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.TaskContainerInternal
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.DefaultCollectionBuilder
+import org.gradle.model.internal.core.ModelCreators
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.model.internal.core.NamedEntityInstantiator
+import org.gradle.model.internal.fixture.ModelRegistryHelper
 import spock.lang.Specification
 
+import static org.gradle.model.internal.core.ModelNode.State.*
+import static org.gradle.model.internal.core.ModelPath.path
+
 class TaskNameResolverTest extends Specification {
-    private final TaskNameResolver resolver = new TaskNameResolver()
+    def tasks = Mock(TaskContainerInternal)
+    def registry = new ModelRegistryHelper()
+    def project = Mock(ProjectInternal)
+    def resolver = new TaskNameResolver()
+
+    def setup() {
+        _ * project.getTasks() >> Mock(TaskContainerInternal) {
+            _ * findByName(_)
+            _ * getTasks() >> tasks
+            _ * getNames() >> tasks.getNames()
+            0 * _
+        }
+        _ * project.getModelRegistry() >> registry
+
+        createTasksCollection(registry, "root")
+    }
 
-    def selectsTaskForSingleProjectWhenThereIsAnExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        _ * project.tasks >> tasks
-        Task task = task('task')
-        1 * tasks.getByName('task') >> task
+    private final TaskNameResolver resolver = new TaskNameResolver()
 
+    def "eagerly locates task with given name for single project"() {
         when:
-        def candidates = resolver.select('task', project)
+        tasks(registry) { it.create("task")}
+        def candidates = resolver.selectWithName('task', project, false)
 
         then:
-        1 * tasks.findByName('task') >> task
-        asTasks(candidates.get("task")) == [task] as Set
+        registry.state(path("tasks")) == SelfClosed
+        registry.state(path("tasks.task")) == Known
+
+        and:
+        asTasks(candidates).size() == 1
+        registry.state(path("tasks.task")) == GraphClosed
+    }
+
+    def "returns null when no task with given name for single project"() {
+        expect:
+        resolver.selectWithName('task', project, false) == null
+        registry.state(path("tasks")) == SelfClosed
     }
 
-    def selectsImplicitTaskForSingleProjectWhenThereIsAnExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        TaskContainerInternal implicitTasks = Mock()
-        _ * project.tasks >> tasks
-        _ * project.implicitTasks >> implicitTasks
+    def "eagerly locates tasks with given name for multiple projects"() {
+        given:
+        def childRegistry = new ModelRegistryHelper()
+
+        def childProject = Mock(ProjectInternal) {
+            _ * getModelRegistry() >> childRegistry
+            _ * getTasks() >> Mock(TaskContainerInternal) {
+                _ * findByName(_)
+                0 * _
+            }
+            _ * getChildProjects() >> [:]
+        }
 
-        Task task = task('task')
-        1 * implicitTasks.getByName('task') >> task
+        _ * project.getChildProjects() >> [child: childProject]
+        createTasksCollection(childRegistry, "child")
 
         when:
-        def candidates = resolver.select('task', project)
+        tasks(registry) { it.create("task") }
+        tasks(childRegistry) { it.create("task") }
+        def results = resolver.selectWithName('task', project, true)
 
         then:
-        1 * tasks.findByName('task') >> null
-        1 * implicitTasks.findByName('task') >> task
-        asTasks(candidates.get('task')) == [task] as Set
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+        registry.state(path("tasks.task")) == GraphClosed
+        childRegistry.state(path("tasks.task")) == GraphClosed
+
+        and:
+        asTasks(results)*.description == ["root", "child"]
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+        registry.state(path("tasks.task")) == GraphClosed
+        childRegistry.state(path("tasks.task")) == GraphClosed
     }
 
-    def selectsAllTasksForSingleProjectWhenThereIsNoExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        TaskContainerInternal implicitTasks = Mock()
-        _ * project.tasks >> tasks
-        _ * project.implicitTasks >> implicitTasks
+    def "does not select tasks in sub projects when task implies sub projects"() {
+        given:
+        def childRegistry = new ModelRegistryHelper()
+        def childProject = Mock(ProjectInternal) {
+            _ * getModelRegistry() >> childRegistry
+            _ * getTasks() >> Mock(TaskContainerInternal) {
+                _ * findByName(_)
+                0 * _
+            }
+            _ * getChildProjects() >> [:]
+        }
 
-        Task task1 = task('task1')
-        Task task2 = task('task2')
-        1 * tasks.getByName('task1') >> task1
-        1 * implicitTasks.getByName('task2') >> task2
+        _ * project.getChildProjects() >> [child: childProject]
+        createTasksCollection(childRegistry, "child")
 
         when:
-        def candidates = resolver.select('task', project)
+        tasks(registry) { it.create("task") { _ * it.getImpliesSubProjects() >> true } }
+        def results = resolver.selectWithName('task', project, true)
 
         then:
-        1 * tasks.findByName('task') >> null
-        1 * implicitTasks.findByName('task') >> null
-        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
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == Known
+        registry.state(path("tasks.task")) == GraphClosed
+
+        and:
+        asTasks(results)*.description == ["root"]
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == Known
+        registry.state(path("tasks.task")) == GraphClosed
     }
 
+    def "locates tasks in child projects with given name when missing in starting project"() {
+        given:
+        def childRegistry = new ModelRegistryHelper()
+        def childProject = Mock(ProjectInternal) {
+            _ * getModelRegistry() >> childRegistry
+            _ * getTasks() >> Mock(TaskContainerInternal) {
+                _ * findByName(_)
+                0 * _
+            }
+            _ * getChildProjects() >> [:]
+        }
 
-    def selectsTasksForMultipleProjectsWhenThereIsAnExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        ProjectInternal childProject = Mock()
-        TaskContainerInternal childProjectTasks = Mock()
-        _ * project.tasks >> tasks
-        _ * project.subprojects >> ([childProject] as Set)
-        _ * childProject.tasks >> childProjectTasks
+        _ * project.getChildProjects() >> [child: childProject]
+        createTasksCollection(childRegistry, "child")
+
+        when:
+        tasks(childRegistry) { it.create("task") }
+        def result = resolver.selectWithName('task', project, true)
+
+        then:
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks.task")) == GraphClosed
 
-        Task task1 = task('task')
-        _ * tasks.getByName('task') >> task1
-        Task task2 = task('task')
-        _ * childProjectTasks.getByName('task') >> task2
+        and:
+        asTasks(result)*.description == ["child"]
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks.task")) == GraphClosed
+    }
 
+    def "lazily locates all tasks for a single project"() {
         when:
-        def candidates = resolver.selectAll('task', project)
+        tasks(registry) { it.create("task1"); it.create("task2") }
 
         then:
-        1 * tasks.findByName('task') >> task1
-        1 * childProjectTasks.findByName('task') >> task2
-        asTasks(candidates.get('task')) == [task1, task2] as Set
+        resolver.selectAll(project, false).keySet() == ["task1", "task2"].toSet()
+        registry.state(path("tasks.task1")) == Known
+        registry.state(path("tasks.task2")) == Known
     }
 
-    def selectsImplicitTaskForMultipleProjectsWhenThereIsAnExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        TaskContainerInternal implicitTasks = Mock()
-        ProjectInternal childProject = Mock()
-        TaskContainerInternal childProjectTasks = Mock()
-        _ * project.tasks >> tasks
-        _ * project.implicitTasks >> implicitTasks
-        _ * project.subprojects >> ([childProject] as Set)
-        _ * childProject.tasks >> childProjectTasks
-
-        Task task1 = task('task')
-        _ * implicitTasks.getByName('task') >> task1
-        Task task2 = task('task')
-        _ * childProjectTasks.getByName('task') >> task2
+    def "lazily locates all tasks for multiple projects"() {
+        def childRegistry = new ModelRegistryHelper()
+        def childProject = Mock(ProjectInternal) {
+            _ * getModelRegistry() >> childRegistry
+            _ * getTasks() >> Mock(TaskContainerInternal) {
+                _ * findByName(_)
+                0 * _
+            }
+            _ * getChildProjects() >> [:]
+        }
+
+        _ * project.getChildProjects() >> [child: childProject]
+        createTasksCollection(childRegistry, "child")
+
+        tasks(registry) { it.create("name1"); it.create("name2") }
+        tasks(childRegistry) { it.create("name1"); it.create("name3") }
 
         when:
-        def candidates = resolver.selectAll('task', project)
+        def candidates = resolver.selectAll(project, true)
 
         then:
-        1 * tasks.findByName('task') >> null
-        1 * implicitTasks.findByName('task') >> task1
-        1 * childProjectTasks.findByName('task') >> task2
-        asTasks(candidates.get('task')) == [task1, task2] as Set
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+
+        registry.state(path("tasks.name1")) == Known
+        registry.state(path("tasks.name2")) == Known
+        childRegistry.state(path("tasks.name1")) == Known
+        childRegistry.state(path("tasks.name3")) == Known
+
+        and:
+        asTasks(candidates.get('name1'))*.description == ["root", "child"]
+        registry.state(path("tasks.name1")) == GraphClosed
+        childRegistry.state(path("tasks.name1")) == GraphClosed
+        registry.state(path("tasks.name2")) == Known
+        childRegistry.state(path("tasks.name3")) == Known
+
+        asTasks(candidates.get('name2'))*.description == ["root"]
+        registry.state(path("tasks.name2")) == GraphClosed
+        childRegistry.state(path("tasks.name3")) == Known
+
+        asTasks(candidates.get('name3'))*.description == ["child"]
+        childRegistry.state(path("tasks.name3")) == GraphClosed
     }
 
-    def selectsAllTasksForMultipleProjectsWhenThereIsNoExactMatchOnName() {
-        ProjectInternal project = Mock()
-        TaskContainerInternal tasks = Mock()
-        TaskContainerInternal implicitTasks = Mock()
-        ProjectInternal childProject = Mock()
-        TaskContainerInternal childProjectTasks = Mock()
-        _ * project.tasks >> tasks
-        _ * project.implicitTasks >> implicitTasks
-        _ * project.subprojects >> ([childProject] as Set)
-        _ * childProject.tasks >> childProjectTasks
-
-        Task task1 = task('name1')
-        Task task2 = task('name2')
-        Task task3 = task('name1')
-        Task task4 = task('name2')
-        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
+    def "does not visit sub-projects when task implies sub-projects"() {
+        def childRegistry = new ModelRegistryHelper()
+        def childProject = Mock(ProjectInternal) {
+            _ * getModelRegistry() >> childRegistry
+            _ * getTasks() >> Mock(TaskContainerInternal) {
+                _ * findByName(_)
+                0 * _
+            }
+            _ * getChildProjects() >> [:]
+        }
+
+        _ * project.getChildProjects() >> [child: childProject]
+        createTasksCollection(childRegistry, "child")
+
+        tasks(registry) { it.create("name1") { it.getImpliesSubProjects() >> true }; it.create("name2") }
+        tasks(childRegistry) { it.create("name1"); it.create("name3") }
 
         when:
-        def candidates = resolver.selectAll('task', project)
+        def candidates = resolver.selectAll(project, true)
 
         then:
-        1 * tasks.findByName('task') >> null
-        1 * implicitTasks.findByName('task') >> null
-        1 * childProjectTasks.findByName('task') >> null
-        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
+        registry.state(path("tasks")) == SelfClosed
+        childRegistry.state(path("tasks")) == SelfClosed
+
+        registry.state(path("tasks.name1")) == Known
+        registry.state(path("tasks.name2")) == Known
+        childRegistry.state(path("tasks.name1")) == Known
+        childRegistry.state(path("tasks.name3")) == Known
+
+        and:
+        asTasks(candidates.get('name1'))*.description == ["root"]
+        registry.state(path("tasks.name1")) == GraphClosed
+        childRegistry.state(path("tasks.name1")) == Known
+        registry.state(path("tasks.name2")) == Known
+        childRegistry.state(path("tasks.name3")) == Known
+    }
+
+    def task(String name, String description = "") {
+        Stub(TaskInternal) { TaskInternal task ->
+            _ * task.getName() >> name
+            _ * task.getDescription() >> description
+        }
+    }
+
+    def tasks(ModelRegistryHelper registry, Action<? super CollectionBuilder<TaskInternal>> action) {
+        registry.mutateCollection("tasks", TaskInternal, action)
     }
 
-    def task(String name) {
-        Task task = Mock()
-        _ * task.name >> name
-        return task
+    Set<Task> asTasks(TaskSelectionResult taskSelectionResult) {
+        def result = []
+        taskSelectionResult.collectTasks(result)
+        return result
     }
 
-    Set<Task> asTasks(Set<TaskSelectionResult> taskSelectionResults) {
-        taskSelectionResults.collect { it.getTask() }.toSet()
+    private ModelRegistryHelper createTasksCollection(ModelRegistryHelper registry, String description) {
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(TaskInternal)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> task(name, description) } as NamedEntityInstantiator).build())
+                .collection("tasks", TaskInternal, iRef)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionSpec.groovy
new file mode 100644
index 0000000..f1bcc0e
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionSpec.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.StartParameter
+import org.gradle.TaskExecutionRequest
+import org.gradle.api.Task
+import org.gradle.api.internal.GradleInternal
+import org.gradle.execution.commandline.CommandLineTaskParser
+import spock.lang.Specification
+
+class TaskNameResolvingBuildConfigurationActionSpec extends Specification {
+    GradleInternal gradle
+    BuildExecutionContext context
+    CommandLineTaskParser parser
+    def TaskNameResolvingBuildConfigurationAction action
+
+    def setup() {
+        gradle = Mock(GradleInternal)
+        context = Mock(BuildExecutionContext)
+        parser = Mock(CommandLineTaskParser)
+        action = new TaskNameResolvingBuildConfigurationAction(parser)
+    }
+
+    def "empty task parameters are no-op action"() {
+        given:
+        def startParameters = Mock(StartParameter)
+
+        when:
+        _ * context.getGradle() >> gradle
+        _ * gradle.getStartParameter() >> startParameters
+        _ * startParameters.getTaskRequests() >> []
+
+        action.configure(context)
+
+        then:
+        1 * context.proceed()
+        0 * context._()
+        0 * startParameters._()
+    }
+
+    def "expand task parameters to tasks"() {
+        def startParameters = Mock(StartParameter)
+        def executer = Mock(TaskGraphExecuter)
+        TaskExecutionRequest request1 = Stub(TaskExecutionRequest)
+        TaskExecutionRequest request2 = Stub(TaskExecutionRequest)
+        def task1 = Stub(Task)
+        def task2 = Stub(Task)
+        def task3 = Stub(Task)
+        def selection1 = Stub(TaskSelector.TaskSelection)
+        def selection2 = Stub(TaskSelector.TaskSelection)
+
+        given:
+        _ * gradle.startParameter >> startParameters
+        _ * startParameters.taskRequests >> [request1, request2]
+        _ * gradle.taskGraph >> executer
+
+        def tasks1 = [task1, task2] as Set
+        _ * selection1.tasks >> tasks1
+
+        def tasks2 = [task3] as Set
+        _ * selection2.tasks >> tasks2
+
+        when:
+        action.configure(context)
+
+        then:
+        1 * parser.parseTasks(request1) >> [selection1]
+        1 * parser.parseTasks(request2) >> [selection2]
+        1 * executer.addTasks(tasks1)
+        1 * executer.addTasks(tasks2)
+        1 * context.proceed()
+        _ * context.gradle >> gradle
+        0 * context._()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java b/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java
deleted file mode 100644
index ee0abe1..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildConfigurationActionTest.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
-import org.gradle.StartParameter;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Task;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.AbstractProject;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.options.Option;
-import org.gradle.api.internal.tasks.options.OptionReader;
-import org.gradle.execution.commandline.CommandLineTaskConfigurer;
-import org.gradle.execution.commandline.CommandLineTaskParser;
-import org.gradle.util.GUtil;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.jmock.Expectations;
-import org.jmock.Sequence;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.gradle.util.WrapUtil.*;
-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)
-public class TaskNameResolvingBuildConfigurationActionTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final ProjectInternal project = context.mock(AbstractProject.class, "[project]");
-    private final ProjectInternal otherProject = context.mock(AbstractProject.class, "[otherProject]");
-    private final ProjectInternal rootProject = context.mock(ProjectInternal.class);
-    private final GradleInternal gradle = context.mock(GradleInternal.class);
-    private final TaskGraphExecuter taskExecuter = context.mock(TaskGraphExecuter.class);
-    private final TaskNameResolver resolver = context.mock(TaskNameResolver.class);
-    private final BuildExecutionContext executionContext = context.mock(BuildExecutionContext.class);
-    private final StartParameter startParameter = context.mock(StartParameter.class);
-    private final OptionReader optionReader = new OptionReader();
-    private final CommandLineTaskParser parser = new CommandLineTaskParser(new CommandLineTaskConfigurer(optionReader));
-    private final TaskSelector selector = new TaskSelector(gradle, resolver);
-    private final TaskNameResolvingBuildConfigurationAction action = new TaskNameResolvingBuildConfigurationAction(parser, selector);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(executionContext).getGradle();
-            will(returnValue(gradle));
-            allowing(gradle).getDefaultProject();
-            will(returnValue(project));
-            allowing(gradle).getTaskGraph();
-            will(returnValue(taskExecuter));
-            allowing(gradle).getStartParameter();
-            will(returnValue(startParameter));
-        }});
-    }
-
-    @Test
-    public void selectsAllTasksWithTheProvidedNameInCurrentProjectAndSubprojects() {
-        final Task task1 = task("name");
-        final Task task2 = task("name");
-        final Task task3 = task("other");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("name")));
-
-            one(resolver).selectAll("name", project);
-            will(returnValue(tasks(task1, task2, task3)));
-            one(taskExecuter).addTasks(toSet(task1, task2));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void usesCamelCaseAbbreviationToSelectTasksWhenNoExactMatch() {
-        assertMatches("soTaWN", "someTaskWithName", "saTaWN");
-        assertMatches("ta1", "task1", "Task1", "T1", "t2");
-        assertMatches("t1", "t1extra");
-        assertMatches("t1", "t12");
-        assertMatches("t1", "task1extra", "task2extra");
-        assertMatches("ABC", "AbcBbcCdc", "aabbcc");
-        assertMatches("s-t", "some-task");
-        assertMatches("s t", "some task");
-        assertMatches("s.t", "some.task");
-        assertMatches("a\\De(", "abc\\Def(", "a\\Df(");
-    }
-
-    private void assertMatches(final String pattern, final String matches, String... otherNames) {
-        final Set<Task> tasks = new HashSet<Task>();
-        final Task task1 = task(matches);
-        tasks.add(task1);
-        final Task task2 = task(matches);
-        tasks.add(task2);
-        for (String name : otherNames) {
-            tasks.add(task(name));
-        }
-        tasks.add(task("."));
-        tasks.add(task("other"));
-
-        context.checking(new Expectations() {{
-            one(startParameter).getTaskNames();
-            will(returnValue(toList(pattern)));
-
-            one(resolver).selectAll(pattern, project);
-            will(returnValue(tasks(tasks)));
-            one(taskExecuter).addTasks(toSet(task1, task2));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void selectsTaskWithMatchingRelativePath() {
-        final Task task1 = task("b");
-        final Task task2 = task("a");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("a:b")));
-
-            one(project).getChildProjects();
-            will(returnValue(toMap("a", otherProject)));
-            one(resolver).select("b", otherProject);
-            will(returnValue(tasks(task1, task2)));
-            one(taskExecuter).addTasks(toSet(task1));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void selectsTaskWithMatchingTaskInRootProject() {
-        final Task task1 = task("b");
-        final Task task2 = task("a");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList(":b")));
-
-            one(project).getRootProject();
-            will(returnValue(rootProject));
-            one(resolver).select("b", rootProject);
-            will(returnValue(tasks(task1, task2)));
-            one(taskExecuter).addTasks(toSet(task1));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void selectsTaskWithMatchingAbsolutePath() {
-        final Task task1 = task("b");
-        final Task task2 = task("a");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList(":a:b")));
-
-            one(project).getRootProject();
-            will(returnValue(rootProject));
-            one(rootProject).getChildProjects();
-            will(returnValue(toMap("a", otherProject)));
-            one(resolver).select("b", otherProject);
-            will(returnValue(tasks(task1, task2)));
-            one(taskExecuter).addTasks(toSet(task1));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void usesCamelCaseAbbreviationToSelectTasksWhenNoExactMatchAndPathProvided() {
-        final Task task1 = task("someTask");
-        final Task task2 = task("other");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("anotherProject:soTa")));
-
-            one(project).getChildProjects();
-            will(returnValue(toMap("anotherProject", otherProject)));
-            one(resolver).select("soTa", otherProject);
-            will(returnValue(tasks(task1, task2)));
-            one(taskExecuter).addTasks(toSet(task1));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void usesCamelCaseAbbreviationToSelectProjectWhenPathProvided() {
-        final Task task1 = task("someTask");
-        final Task task2 = task("other");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("anPr:soTa")));
-
-            one(project).getChildProjects();
-            will(returnValue(toMap("anotherProject", otherProject)));
-            one(resolver).select("soTa", otherProject);
-            will(returnValue(tasks(task1, task2)));
-            one(taskExecuter).addTasks(toSet(task1));
-            one(executionContext).proceed();
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void failsWhenProvidedTaskNameIsAmbiguous() {
-        final Task task1 = task("someTask");
-        final Task task2 = task("someTasks");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("soTa")));
-
-            one(resolver).selectAll("soTa", project);
-            will(returnValue(tasks(task1, task2)));
-        }});
-
-        try {
-            action.configure(executionContext);
-            fail();
-        } catch (TaskSelectionException e) {
-            assertThat(e.getMessage(), equalTo("Task 'soTa' is ambiguous in [project]. Candidates are: 'someTask', 'someTasks'."));
-        }
-    }
-
-    @Test
-    public void reportsTyposInTaskName() {
-        final Task task1 = task("someTask");
-        final Task task2 = task("someTasks");
-        final Task task3 = task("sometask");
-        final Task task4 = task("other");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("ssomeTask")));
-
-            one(resolver).selectAll("ssomeTask", project);
-            will(returnValue(tasks(task1, task2, task3, task4)));
-        }});
-
-        try {
-            action.configure(executionContext);
-            fail();
-        } catch (TaskSelectionException e) {
-            assertThat(e.getMessage(), equalTo("Task 'ssomeTask' not found in [project]. Some candidates are: 'someTask', 'someTasks', 'sometask'."));
-        }
-    }
-
-    @Test
-    public void treatsEachProvidedNameAsASeparateGroup() {
-        final Task task1 = task("name1");
-        final Task task2 = task("name2");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("child:name1", "name2")));
-
-            one(project).getChildProjects();
-            will(returnValue(toMap("child", otherProject)));
-            one(resolver).select("name1", otherProject);
-            will(returnValue(tasks(task1)));
-            one(resolver).selectAll("name2", project);
-            will(returnValue(tasks(task2)));
-
-            Sequence sequence = context.sequence("tasks");
-
-            one(taskExecuter).addTasks(toSet(task1));
-            inSequence(sequence);
-
-            one(taskExecuter).addTasks(toSet(task2));
-            inSequence(sequence);
-
-            ignoring(executionContext);
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void canConfigureSingleTaskUsingCommandLineOptions() {
-        final TaskWithBooleanProperty task1 = task("name1", TaskWithBooleanProperty.class);
-        final Task task2 = task("name2");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("name1", "--all", "name2")));
-
-            one(resolver).selectAll("name1", project);
-            will(returnValue(tasks(task1)));
-            one(resolver).selectAll("name2", project);
-            will(returnValue(tasks(task2)));
-
-            Sequence sequence = context.sequence("tasks");
-
-            one(task1).setSomeFlag(true);
-
-            one(taskExecuter).addTasks(toSet(task1));
-            inSequence(sequence);
-
-            one(taskExecuter).addTasks(toSet(task2));
-            inSequence(sequence);
-
-            ignoring(executionContext);
-        }});
-
-        action.configure(executionContext);
-    }
-
-    @Test
-    public void failsWhenUnknownTaskNameIsProvided() {
-        final Task task1 = task("t1");
-        final Task task2 = task("t2");
-
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("b3")));
-
-            one(resolver).selectAll("b3", project);
-            will(returnValue(tasks(task1, task2)));
-        }});
-
-        try {
-            action.configure(executionContext);
-            fail();
-        } catch (TaskSelectionException e) {
-            assertThat(e.getMessage(), equalTo("Task 'b3' not found in [project]."));
-        }
-    }
-
-    @Test
-    public void failsWhenCannotFindProjectInPath() {
-        context.checking(new Expectations() {{
-            allowing(startParameter).getTaskNames();
-            will(returnValue(toList("a:b", "name2")));
-
-            one(project).getChildProjects();
-            will(returnValue(GUtil.map("aa", otherProject, "ab", otherProject)));
-        }});
-
-        try {
-            action.configure(executionContext);
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), equalTo("Project 'a' is ambiguous in [project]. Candidates are: 'aa', 'ab'."));
-        }
-    }
-
-    private Task task(String name) {
-        return task(name, Task.class);
-    }
-
-    private <T extends Task> T task(final String name, Class<T> taskType) {
-        final T task = context.mock(taskType);
-        context.checking(new Expectations() {{
-            allowing(task).getName();
-            will(returnValue(name));
-        }});
-        return task;
-    }
-
-    private Multimap<String, TaskSelectionResult> tasks(Task... tasks) {
-        return tasks(Arrays.asList(tasks));
-    }
-
-    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 {
-        @Option(option = "all", description = "Some 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 214d9bb..80e201b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskPathProjectEvaluatorTest.groovy
@@ -16,49 +16,48 @@
 
 package org.gradle.execution
 
+import org.gradle.api.BuildCancelledException
 import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.execution.taskpath.ResolvedTaskPath
-import org.gradle.execution.taskpath.TaskPathResolver
+import org.gradle.initialization.BuildCancellationToken
 import spock.lang.Specification
 
 class TaskPathProjectEvaluatorTest extends Specification {
-
-    private resolver = Mock(TaskPathResolver)
+    private cancellationToken = Mock(BuildCancellationToken)
     private project = Mock(ProjectInternal)
+    private evaluator = new TaskPathProjectEvaluator(cancellationToken)
 
-    private evaluator = new TaskPathProjectEvaluator(resolver)
-
-    def "evaluates project by task path"() {
-        def path = Mock(ResolvedTaskPath)
-        def fooProject = Mock(ProjectInternal)
+    def "project configuration fails when cancelled"() {
+        given:
+        cancellationToken.cancellationRequested >> true
 
         when:
-        evaluator.evaluateByPath(project, ":foo:bar")
+        evaluator.configure(project)
 
         then:
-        1 * resolver.resolvePath(":foo:bar", project) >> path
-        1 * path.isQualified() >> true
-        1 * path.getProject() >> fooProject
-        1 * fooProject.evaluate()
-        0 * _._
+        thrown(BuildCancelledException)
+
+        and:
+        0 * project._
     }
 
-    def "evaluates all projects"() {
-        def path = Mock(ResolvedTaskPath)
-        def subprojects = [Mock(ProjectInternal), Mock(ProjectInternal)]
+    def "project hierarchy configuration fails when cancelled"() {
+        def child1 = Mock(ProjectInternal)
+        def child2 = Mock(ProjectInternal)
+        def subprojects = [child1, child2]
+
+        given:
+        project.subprojects >> subprojects
+        cancellationToken.cancellationRequested >>> [false, false, true]
 
         when:
-        evaluator.evaluateByPath(project, "someTask")
+        evaluator.configureHierarchy(project)
 
         then:
-        1 * resolver.resolvePath("someTask", project) >> path
-        1 * path.isQualified() >> false
+        BuildCancelledException e = thrown()
 
         and:
         1 * project.evaluate()
-        1 * project.subprojects >> subprojects
-        1 * subprojects[0].evaluate()
-        1 * subprojects[1].evaluate()
-        0 * _._
+        1 * child1.evaluate()
+        0 * child2._
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/TaskSelectorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/TaskSelectorTest.groovy
new file mode 100644
index 0000000..f132e06
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/TaskSelectorTest.groovy
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.execution
+
+import org.gradle.api.Task
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class TaskSelectorTest extends Specification {
+    def rootProject = TestUtil.createRootProject()
+    def projectA = TestUtil.createChildProject(rootProject, "a")
+    def projectB = TestUtil.createChildProject(rootProject, "b")
+    def projectBChild = TestUtil.createChildProject(projectB, "child")
+    def gradle = Stub(GradleInternal) {
+        getRootProject() >> rootProject
+        getDefaultProject() >> projectB
+    }
+    def resolver = Mock(TaskNameResolver)
+    def projectConfigurer = Mock(ProjectConfigurer)
+    def selector = new TaskSelector(gradle, resolver, projectConfigurer)
+
+    def "qualified exclude filter configures target project and selects exact match on task name"() {
+        def excluded = Stub(Task)
+        def notExcluded = Stub(Task)
+        def selectionResult = Stub(TaskSelectionResult)
+
+        when:
+        def filter = selector.getFilter(":a:b")
+
+        then:
+        1 * projectConfigurer.configure(projectA)
+        1 * resolver.selectWithName("b", projectA, false) >> selectionResult
+        _ * selectionResult.collectTasks(_) >> { it[0] << excluded }
+        0 * _
+
+        and:
+        !filter.isSatisfiedBy(excluded)
+        filter.isSatisfiedBy(notExcluded)
+    }
+
+    def "qualified exclude filter configures target project and selects matching tasks from all candidates"() {
+        def excluded = Stub(Task)
+        def notExcluded = Stub(Task)
+        def selectionResult = Stub(TaskSelectionResult)
+
+        when:
+        def filter = selector.getFilter(":a:b")
+
+        then:
+        1 * projectConfigurer.configure(projectA)
+        1 * resolver.selectWithName("b", projectA, false) >> null
+        1 * resolver.selectAll(projectA, false) >> [b1: selectionResult]
+        _ * selectionResult.collectTasks(_) >> { it[0] << excluded }
+        0 * _
+
+        and:
+        !filter.isSatisfiedBy(excluded)
+        filter.isSatisfiedBy(notExcluded)
+    }
+
+    def "unqualified exclude filter configures default project only and filters tasks by path"() {
+        when:
+        def filter = selector.getFilter("a")
+
+        then:
+        1 * projectConfigurer.configure(projectB)
+        1 * resolver.tryFindUnqualifiedTaskCheaply("a", projectB) >> true
+        0 * _
+
+        and:
+        !filter.isSatisfiedBy(task(projectB, "a"))
+        !filter.isSatisfiedBy(task(projectBChild, "a"))
+        filter.isSatisfiedBy(task(projectA, "a"))
+        filter.isSatisfiedBy(task(rootProject, "a"))
+        filter.isSatisfiedBy(task(projectB, "other"))
+    }
+
+    def "unqualified exclude filter configures all subprojects of the default project when exact match on task name not found"() {
+        def excluded = Stub(Task)
+        def notExcluded = Stub(Task)
+        def selectionResult = Stub(TaskSelectionResult)
+
+        when:
+        def filter = selector.getFilter("a")
+
+        then:
+        1 * projectConfigurer.configure(projectB)
+        1 * resolver.tryFindUnqualifiedTaskCheaply("a", projectB) >> false
+        1 * projectConfigurer.configureHierarchy(projectB)
+        1 * resolver.selectWithName("a", projectB, true) >> selectionResult
+        _ * selectionResult.collectTasks(_) >> { it[0] << excluded }
+        0 * _
+
+        and:
+        !filter.isSatisfiedBy(excluded)
+        filter.isSatisfiedBy(notExcluded)
+    }
+
+    def "unqualified exclude filter selects tasks and filters by instance when no exact match on name found"() {
+        def excluded = Stub(Task)
+        def notExcluded = Stub(Task)
+        def selectionResult = Stub(TaskSelectionResult)
+
+        when:
+        def filter = selector.getFilter("a")
+
+        then:
+        1 * projectConfigurer.configure(projectB)
+        1 * resolver.tryFindUnqualifiedTaskCheaply("a", projectB) >> false
+        1 * projectConfigurer.configureHierarchy(projectB)
+        1 * resolver.selectWithName("a", projectB, true) >> null
+        1 * resolver.selectAll(projectB, true) >> [a1: selectionResult]
+        _ * selectionResult.collectTasks(_) >> { it[0] << excluded }
+        0 * _
+
+        and:
+        !filter.isSatisfiedBy(excluded)
+        filter.isSatisfiedBy(notExcluded)
+    }
+
+    def task(ProjectInternal project, String name) {
+        def task = Stub(TaskInternal) {
+            getProject() >> project
+            getName() >> name
+        }
+        return task
+    }
+}
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 b485483..c874669 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
@@ -145,9 +145,8 @@ class CommandLineTaskConfigurerSpec extends Specification {
     }
 
     def "fails on unknown option"() {
-        def args = ['--xxx']
         when:
-        configurer.configureTasks([task, task2], args)
+        configurer.configureTasks([task, task2], ['--xxx'])
 
         then:
         def ex = thrown(TaskConfigurationException)
@@ -155,9 +154,8 @@ class CommandLineTaskConfigurerSpec extends Specification {
     }
 
     def "fails neatly when short option used"() {
-        def args = ['--someFlag', '-c']
         when:
-        configurer.configureTasks([task], args)
+        configurer.configureTasks([task], ['--someFlag', '-c'])
 
         then:
         def ex = thrown(TaskConfigurationException)
@@ -217,6 +215,7 @@ class CommandLineTaskConfigurerSpec extends Specification {
         }
     }
 
+    @SuppressWarnings('FieldName')
     enum TestEnum {
         value1, value2
     }
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 a93383d..6412469 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
@@ -16,102 +16,77 @@
 
 package org.gradle.execution.commandline
 
-import org.gradle.api.DefaultTask
-import org.gradle.api.Project
-import org.gradle.api.tasks.TaskAction
+import org.gradle.api.Task
 import org.gradle.execution.TaskSelectionResult
 import org.gradle.execution.TaskSelector
-import org.gradle.testfixtures.ProjectBuilder
+import org.gradle.internal.DefaultTaskExecutionRequest
 import spock.lang.Specification
 
 import static com.google.common.collect.Sets.newHashSet
-import static java.util.Collections.emptyList
 
 class CommandLineTaskParserSpec extends Specification {
-
-    Project project = new ProjectBuilder().build()
-    TaskSelector selector = Mock()
-    SomeTask task = project.task('someTask', type: SomeTask)
-    SomeTask task2 = project.task('someTask2', type: SomeTask)
-    SomeTask task3 = project.task('someTask3', type: SomeTask)
-
-    CommandLineTaskParser parser
+    def selector = Mock(TaskSelector)
+    def taskConfigurer = Mock(CommandLineTaskConfigurer)
+    def task = Mock(Task)
+    def task2 = Mock(Task)
+    def task3 = Mock(Task)
+    def parser = new CommandLineTaskParser(taskConfigurer, selector)
 
     def setup() {
-        CommandLineTaskConfigurer taskConfigurer = Mock(CommandLineTaskConfigurer)
         taskConfigurer.configureTasks(_, _) >> { args -> args[1] }
-        parser = new CommandLineTaskParser(taskConfigurer)
-    }
-
-    def "deals with empty input"() {
-        expect:
-        parser.parseTasks(emptyList(), selector).empty
     }
 
-    def "parses a single task"() {
+    def "parses a single task selector"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task))
+        def request = new DefaultTaskExecutionRequest(['foo'], 'project')
+        def selection = new TaskSelector.TaskSelection(':project', ':foo', asTaskSelectionResults(task))
 
-        when:
-        def out = parser.parseTasks(['foo'], selector)
-
-        then:
-        out.size() == 1
-        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', asTaskSelectionResults(task, task2))
+        selector.getSelection('project', 'foo') >> selection
 
         when:
-        def out = parser.parseTasks(['foo'], selector)
+        def out = parser.parseTasks(request)
 
         then:
-        out.size() == 2
-        out.get('foo task') == [task, task2] as Set
+        out == [selection]
     }
 
-    def "parses multiple matching tasks"() {
+    def "parses multiple tasks selectors"() {
         given:
-        selector.getSelection('foo') >> new TaskSelector.TaskSelection('foo task', asTaskSelectionResults(task, task2))
-        selector.getSelection('bar') >> new TaskSelector.TaskSelection('bar task', asTaskSelectionResults(task3))
+        def request = new DefaultTaskExecutionRequest(['foo', 'bar'])
+        def selection1 = new TaskSelector.TaskSelection(':project', ':foo', asTaskSelectionResults(task, task2))
+        def selection2 = new TaskSelector.TaskSelection(':project', ':bar', asTaskSelectionResults(task3))
+
+        selector.getSelection(null, 'foo') >> selection1
+        selector.getSelection(null, 'bar') >> selection2
 
         when:
-        def out = parser.parseTasks(['foo', 'bar'], selector)
+        def out = parser.parseTasks(request)
 
         then:
-        out.size() == 3
-        out.get('foo task') == [task, task2] as Set
-        out.get('bar task') == [task3] as Set
+        out == [selection1, selection2]
     }
 
     def "configures tasks if configuration options specified"() {
         given:
-        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))
+        def request = new DefaultTaskExecutionRequest(['foo', '--all', 'bar', '--include', 'stuff', 'lastTask'])
+        selector.getSelection(null, 'foo') >> new TaskSelector.TaskSelection(':project', 'foo task', asTaskSelectionResults(task, task2))
+        selector.getSelection(null, 'bar') >> new TaskSelector.TaskSelection(':project', 'bar task', asTaskSelectionResults(task3))
+        selector.getSelection(null, 'lastTask') >> new TaskSelector.TaskSelection(':project', 'last task', asTaskSelectionResults(task3))
 
         when:
-        def out = parser.parseTasks(['foo', '--all', 'bar', '--include', 'stuff', 'lastTask'], selector)
+        def out = parser.parseTasks(request)
 
         then:
-        out.size() == 4
-        1 * parser.taskConfigurer.configureTasks(newHashSet(task, task2), ['--all', 'bar', '--include', 'stuff', 'lastTask']) >> ['bar', '--include', 'stuff', 'lastTask']
-        1 * parser.taskConfigurer.configureTasks(newHashSet(task3), ['--include', 'stuff', 'lastTask']) >> ['lastTask']
-        1 * parser.taskConfigurer.configureTasks(newHashSet(task3), []) >> []
-        0 * parser.taskConfigurer._
+        out.size() == 3
+        1 * taskConfigurer.configureTasks(newHashSet(task, task2), ['--all', 'bar', '--include', 'stuff', 'lastTask']) >> ['bar', '--include', 'stuff', 'lastTask']
+        1 * taskConfigurer.configureTasks(newHashSet(task3), ['--include', 'stuff', 'lastTask']) >> ['lastTask']
+        1 * taskConfigurer.configureTasks(newHashSet(task3), []) >> []
+        0 * taskConfigurer._
     }
 
-    public static class SomeTask extends DefaultTask {
-        @TaskAction public void dummy() {}
+    TaskSelectionResult asTaskSelectionResults(Task... someTasks) {
+        TaskSelectionResult mock = Mock(TaskSelectionResult)
+        _ * mock.collectTasks(_) >> { Object args -> args[0].addAll(someTasks) }
+        mock
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanParallelTaskHandlingTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanParallelTaskHandlingTest.groovy
new file mode 100644
index 0000000..410c02f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlanParallelTaskHandlingTest.groovy
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.tasks.Delete
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.ParallelizableTask
+import org.gradle.initialization.BuildCancellationToken
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
+
+import static org.gradle.util.TestUtil.createChildProject
+import static org.gradle.util.TestUtil.createRootProject
+
+class DefaultTaskExecutionPlanParallelTaskHandlingTest extends Specification {
+
+    @Rule
+    TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
+    FileSystem fs = NativeServicesTestFixture.instance.get(FileSystem)
+
+    DefaultTaskExecutionPlan executionPlan = new DefaultTaskExecutionPlan(Stub(BuildCancellationToken), true)
+    DefaultProject root = createRootProject()
+
+    List<TaskInfo> startedTasks = []
+    List<Thread> blockedThreads = []
+
+    void cleanup() {
+        completeAllStartedTasks()
+        allBlockedThreadsFinish()
+    }
+
+    private void addToGraphAndPopulate(Task... tasks) {
+        executionPlan.addToTaskGraph(Arrays.asList(tasks))
+        executionPlan.determineExecutionPlan()
+    }
+
+    void startTasks(int num) {
+        num.times { startedTasks << executionPlan.getTaskToExecute() }
+    }
+
+    void noMoreTasksCurrentlyAvailableForExecution() {
+        blockedThreads << blockedThread { executionPlan.taskComplete(executionPlan.getTaskToExecute()) }
+    }
+
+    void completeAllStartedTasks() {
+        startedTasks.each { executionPlan.taskComplete(it) }
+        startedTasks.clear()
+    }
+
+    void allBlockedThreadsFinish() {
+        blockedThreads*.join()
+        blockedThreads.clear()
+    }
+
+    TestFile file(String path) {
+        tmp.file(path)
+    }
+
+    @ParallelizableTask
+    static class Parallel extends DefaultTask {}
+
+    static class ParallelChild extends Parallel {}
+
+    Thread blockedThread(Runnable target) {
+        def conditions = new PollingConditions(timeout: 3, delay: 0.01)
+        def thread = new Thread(target)
+
+        thread.start()
+        conditions.eventually {
+            assert thread.state == Thread.State.WAITING
+        }
+        thread
+    }
+
+    void requestedTasksBecomeAvailableForExecution() {
+        allBlockedThreadsFinish()
+    }
+
+    def "tasks arent parallelized unless toggle is on"() {
+        given:
+        executionPlan = new DefaultTaskExecutionPlan(Stub(BuildCancellationToken), false)
+        Task a = root.task("a")
+        Task b = root.task("b")
+
+        when:
+        addToGraphAndPopulate(a, b)
+
+        then:
+        startTasks(1)
+
+        and:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "two dependent parallelizable tasks are not executed in parallel"() {
+        given:
+        Task a = root.task("a", type: Parallel)
+        Task b = root.task("b", type: Parallel).dependsOn(a)
+
+        when:
+        addToGraphAndPopulate(b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "two parallelizable tasks with must run after ordering are not executed in parallel"() {
+        given:
+        Task a = root.task("a", type: Parallel)
+        Task b = root.task("b", type: Parallel).mustRunAfter(a)
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "task that extend a parallelizable task are not parallelizable by default"() {
+        given:
+        Task a = root.task("a", type: ParallelChild)
+        Task b = root.task("b", type: ParallelChild)
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "task is not available for execution until all of its dependencies that are executed in parallel complete"() {
+        given:
+        Task a = root.task("a", type: Parallel)
+        Task b = root.task("b", type: Parallel)
+        Task c = root.task("c", type: Parallel).dependsOn(a, b)
+
+        when:
+        addToGraphAndPopulate(c)
+        startTasks(2)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+
+        when:
+        completeAllStartedTasks()
+
+        then:
+        requestedTasksBecomeAvailableForExecution()
+    }
+
+    def "a parallelizable task with custom actions is not run in parallel"() {
+        given:
+        Task a = root.task("a", type: Parallel)
+        Task b = root.task("b", type: Parallel).doLast {}
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "DefaultTask is parallelizable"() {
+        given:
+        Task a = root.task("a")
+        Task b = root.task("b")
+
+        when:
+        addToGraphAndPopulate(a, b)
+
+        then:
+        startTasks(2)
+    }
+
+    def "Delete tasks are not parallelizable"() {
+        given:
+        Task clean = root.task("clean", type: Delete)
+        Task parallelizable = root.task("parallelizable", type: Parallel)
+
+        when:
+        addToGraphAndPopulate(clean, parallelizable)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    @ParallelizableTask
+    static class ParallelWithOutputFile extends DefaultTask {
+        @OutputFile
+        File outputFile
+    }
+
+    Task taskWithOutputFile(Project project = root, String taskName, File file) {
+        project.task(taskName, type: ParallelWithOutputFile) {
+            outputFile = file
+        }
+    }
+
+    def "two parallelizable tasks that have the same file in outputs are not executed in parallel"() {
+        given:
+        Task a = taskWithOutputFile("a", file("output"))
+        Task b = taskWithOutputFile("b", file("output"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    @ParallelizableTask
+    static class ParallelWithOutputDirectory extends DefaultTask {
+        @OutputDirectory
+        File outputDirectory
+    }
+
+    Task taskWithOutputDirectory(Project project = root, String taskName, File directory) {
+        project.task(taskName, type: ParallelWithOutputDirectory) {
+            outputDirectory = directory
+        }
+    }
+
+    def "a task that writes into a directory that is an output of currently running task is not started"() {
+        given:
+        Task a = taskWithOutputDirectory("a", file("outputDir"))
+        Task b = taskWithOutputFile("b", file("outputDir").file("outputSubdir").file("output"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "a task that writes into an ancestor directory of a file that is an output of currently running task is not started"() {
+        given:
+        Task a = taskWithOutputFile("a", file("outputDir").file("outputSubdir").file("output"))
+        Task b = taskWithOutputDirectory("b", file("outputDir"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    @Requires(TestPrecondition.SYMLINKS)
+    def "a task that writes into a symlink that overlaps with output of currently running task is not started"() {
+        given:
+        def taskOutput = file("outputDir").createDir()
+        def symlink = file("symlink")
+        fs.createSymbolicLink(symlink, taskOutput)
+
+        and:
+        Task a = taskWithOutputDirectory("a", taskOutput)
+        Task b = taskWithOutputFile("b", symlink.file("fileUnderSymlink"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    @Requires(TestPrecondition.SYMLINKS)
+    def "a task that writes into a symlink of a shared output dir of currently running task is not started"() {
+        given:
+        def taskOutput = file("outputDir").createDir()
+        def symlink = file("symlink")
+        fs.createSymbolicLink(symlink, taskOutput)
+
+        // Deleting any file clears the internal canonicalisation cache.
+        // This allows the created symlink to be actually resolved.
+        // See java.io.UnixFileSystem#cache.
+        file("tmp").createFile().delete()
+
+        and:
+        Task a = taskWithOutputDirectory("a", taskOutput)
+        Task b = taskWithOutputDirectory("b", symlink)
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "tasks from two different projects that have the same file in outputs are not executed in parallel"() {
+        given:
+        Task a = taskWithOutputFile(createChildProject(root, "a"), "a", file("output"))
+        Task b = taskWithOutputFile(createChildProject(root, "b"), "b", file("output"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+
+    def "a task from different project that writes into a directory that is an output of currently running task is not started"() {
+        given:
+        Task a = taskWithOutputDirectory(createChildProject(root, "a"), "a", file("outputDir"))
+        Task b = taskWithOutputFile(createChildProject(root, "b"), "b", file("outputDir").file("outputSubdir").file("output"))
+
+        when:
+        addToGraphAndPopulate(a, b)
+        startTasks(1)
+
+        then:
+        noMoreTasksCurrentlyAvailableForExecution()
+    }
+}
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 b2fd9ba..51d3260 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
@@ -16,14 +16,17 @@
 
 package org.gradle.execution.taskgraph
 
+import org.gradle.api.BuildCancelledException
 import org.gradle.api.CircularReferenceException
 import org.gradle.api.Task
 import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.TaskOutputsInternal
 import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.internal.tasks.TaskStateInternal
 import org.gradle.api.specs.Spec
 import org.gradle.api.tasks.TaskDependency
-import org.gradle.api.tasks.TaskState
 import org.gradle.execution.TaskFailureHandler
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.util.TextUtil
 import spock.lang.Issue
 import spock.lang.Specification
@@ -31,16 +34,18 @@ import spock.lang.Unroll
 
 import static org.gradle.util.TestUtil.createChildProject
 import static org.gradle.util.TestUtil.createRootProject
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
 import static org.gradle.util.WrapUtil.toList
 
 public class DefaultTaskExecutionPlanTest extends Specification {
 
     DefaultTaskExecutionPlan executionPlan
     DefaultProject root;
+    def cancellationHandler = Mock(BuildCancellationToken)
 
     def setup() {
         root = createRootProject();
-        executionPlan = new DefaultTaskExecutionPlan()
+        executionPlan = new DefaultTaskExecutionPlan(cancellationHandler)
     }
 
     private void addToGraphAndPopulate(List tasks) {
@@ -355,6 +360,25 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         executes(finalized, f2, d, f1)
     }
 
+    @Issue("GRADLE-2957")
+    def "task with a dependency and a finalizer both having a common finalizer"() {
+        // Finalizer task
+        Task finalTask = task('finalTask')
+
+        // Task with this finalizer
+        Task dependency = task('dependency', finalizedBy: [finalTask])
+        Task finalizer = task('finalizer', finalizedBy: [finalTask])
+
+        // Task to call, with the same finalizer than one of its dependencies
+        Task requestedTask = task('requestedTask', dependsOn: [dependency], finalizedBy: [finalizer])
+
+        when:
+        addToGraphAndPopulate([requestedTask])
+
+        then:
+        executes(dependency, requestedTask, finalizer, finalTask)
+    }
+
     @Issue("GRADLE-2983")
     def "multiple finalizer tasks with relationships via other tasks scheduled from multiple tasks"() {
         //finalizers with a relationship via a dependency
@@ -495,6 +519,23 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         executedTasks == [a, d, e, b, c, f, g, h]
     }
 
+    @Issue("GRADLE-3166")
+    def "multiple should run after declarations are removed if causing circular reference"() {
+        Task a = createTask("a")
+        Task b = createTask("b")
+        Task c = createTask("c")
+
+        relationships(a, dependsOn: [c])
+        relationships(b, dependsOn: [a, c])
+        relationships(c, shouldRunAfter: [b, a])
+
+        when:
+        addToGraphAndPopulate([b])
+
+        then:
+        executedTasks == [c, a, b]
+    }
+
     def "should run after ordering is ignored if it is at the end of a circular reference"() {
         Task a = createTask("a")
         Task b = task("b", dependsOn: [a])
@@ -508,6 +549,29 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         executedTasks == [a, b, c]
     }
 
+    @Issue("GRADLE-3127")
+    def "circular dependency detected with shouldRunAfter dependencies in the graph"() {
+        Task a = createTask("a")
+        Task b = task("b")
+        Task c = createTask("c")
+        Task d = task("d", dependsOn: [a, b, c])
+        relationships(a, shouldRunAfter: [b])
+        relationships(c, dependsOn: [d])
+
+        when:
+        addToGraphAndPopulate([d])
+
+        then:
+        CircularReferenceException e = thrown()
+        e.message == toPlatformLineSeparators("""Circular dependency between the following tasks:
+:c
+\\--- :d
+     \\--- :c (*)
+
+(*) - details omitted (listed previously)
+""")
+    }
+
     def "stops returning tasks on task execution failure"() {
         RuntimeException failure = new RuntimeException("failure");
         Task a = task("a");
@@ -530,6 +594,25 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         e == failure
     }
 
+    def "stops returning tasks when build is cancelled"() {
+        2 * cancellationHandler.cancellationRequested >>> [false, true]
+        Task a = task("a");
+        Task b = task("b");
+
+        when:
+        addToGraphAndPopulate([a, b])
+
+        then:
+        executedTasks == [a]
+
+        when:
+        executionPlan.awaitCompletion()
+
+        then:
+        BuildCancelledException e = thrown()
+        e.message == 'Build cancelled.'
+    }
+
     protected TaskInfo getTaskToExecute() {
         executionPlan.getTaskToExecute()
     }
@@ -751,17 +834,17 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         executes(c)
     }
 
-    def "one parallel task per project is allowed"() {
+    def "one non parallelizable parallel task per project is allowed"() {
         given:
-        //2 projects, 2 tasks each
+        //2 projects, 2 non parallelizable tasks each
         def projectA = createChildProject(root, "a")
         def projectB = createChildProject(root, "b")
 
-        def fooA = projectA.task("foo")
-        def barA = projectA.task("bar")
+        def fooA = projectA.task("foo").doLast {}
+        def barA = projectA.task("bar").doLast {}
 
-        def fooB = projectB.task("foo")
-        def barB = projectB.task("bar")
+        def fooB = projectB.task("foo").doLast {}
+        def barB = projectB.task("bar").doLast {}
 
         addToGraphAndPopulate([fooA, barA, fooB, barB])
 
@@ -834,6 +917,12 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         task([:], name)
     }
 
+    private TaskOutputsInternal emptyTaskOutputs() {
+        Mock(TaskOutputsInternal) {
+            getFiles() >> root.files()
+        }
+    }
+
     private TaskInternal task(Map options, final String name) {
         def task = createTask(name)
         relationships(options, task)
@@ -841,6 +930,7 @@ public class DefaultTaskExecutionPlanTest extends Specification {
             failure(task, options.failure)
         }
         task.getDidWork() >> (options.containsKey('didWork') ? options.didWork : true)
+        task.getOutputs() >> emptyTaskOutputs()
         return task
     }
 
@@ -862,7 +952,7 @@ public class DefaultTaskExecutionPlanTest extends Specification {
 
     private TaskInternal createTask(final String name) {
         TaskInternal task = Mock()
-        TaskState state = Mock()
+        TaskStateInternal state = Mock()
         task.getProject() >> root
         task.name >> name
         task.path >> ':' + name
@@ -871,6 +961,7 @@ public class DefaultTaskExecutionPlanTest extends Specification {
         task.compareTo(_ as TaskInternal) >> { TaskInternal taskInternal ->
             return name.compareTo(taskInternal.getName());
         }
+        task.getOutputs() >> emptyTaskOutputs()
         return task;
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterSpec.groovy
new file mode 100644
index 0000000..a9b2d89
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskGraphExecuterSpec.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.BuildCancelledException
+import org.gradle.api.Task
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.TaskOutputsInternal
+import org.gradle.api.internal.tasks.TaskStateInternal
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.initialization.BuildCancellationToken
+import org.gradle.internal.event.ListenerBroadcast
+import org.gradle.internal.event.ListenerManager
+import org.gradle.testfixtures.ProjectBuilder
+import spock.lang.Specification
+
+class DefaultTaskGraphExecuterSpec extends Specification {
+    def cancellationToken = Mock(BuildCancellationToken)
+    def project = ProjectBuilder.builder().build()
+    def listenerManager = Stub(ListenerManager) {
+        _ * createAnonymousBroadcaster(_) >> { Class cl -> new ListenerBroadcast(cl) }
+    }
+    def taskExecuter = new DefaultTaskGraphExecuter(listenerManager, new DefaultTaskPlanExecutor(), cancellationToken)
+
+    def "stops running tasks and fails with exception when build is cancelled"() {
+        def a = task("a")
+        def b = task("b")
+
+        given:
+        cancellationToken.cancellationRequested >>> [false, true]
+
+        when:
+        taskExecuter.addTasks([a, b])
+        taskExecuter.execute()
+
+        then:
+        BuildCancelledException e = thrown()
+        e.message == 'Build cancelled.'
+
+        and:
+        1 * a.executeWithoutThrowingTaskFailure()
+        0 * b.executeWithoutThrowingTaskFailure()
+    }
+
+    def "does not fail with exception when build is cancelled after last task has started"() {
+        def a = task("a")
+        def b = task("b")
+
+        given:
+        cancellationToken.cancellationRequested >>> [false, false, true]
+
+        when:
+        taskExecuter.addTasks([a, b])
+        taskExecuter.execute()
+
+        then:
+        1 * a.executeWithoutThrowingTaskFailure()
+        1 * b.executeWithoutThrowingTaskFailure()
+    }
+
+    def "does not fail with exception when build is cancelled and no tasks scheduled"() {
+        given:
+        cancellationToken.cancellationRequested >>> [true]
+
+        when:
+        taskExecuter.addTasks([])
+        taskExecuter.execute()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def task(String name) {
+        def mock = Mock(TaskInternal)
+        _ * mock.name >> name
+        _ * mock.project >> project
+        _ * mock.state >> Stub(TaskStateInternal) {
+            getFailure() >> null
+        }
+        _ * mock.taskDependencies >> Stub(TaskDependency)
+        _ * mock.finalizedBy >> Stub(TaskDependency)
+        _ * mock.mustRunAfter >> Stub(TaskDependency)
+        _ * mock.shouldRunAfter >> Stub(TaskDependency)
+        _ * mock.compareTo(_) >> { Task t -> name.compareTo(t.name) }
+        _ * mock.outputs >> Stub(TaskOutputsInternal) {
+            getFiles() >> project.files()
+        }
+        return mock
+    }
+}
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 9f1b67a..6563446 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
@@ -24,13 +24,16 @@ import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.internal.tasks.DefaultTaskOutputs;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.TaskOutputs;
 import org.gradle.api.tasks.TaskState;
 import org.gradle.execution.TaskFailureHandler;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.TestClosure;
 import org.hamcrest.Description;
@@ -56,6 +59,7 @@ import static org.junit.Assert.*;
 public class DefaultTaskGraphExecuterTest {
     final JUnit4Mockery context = new JUnit4GroovyMockery();
     final ListenerManager listenerManager = context.mock(ListenerManager.class);
+    final BuildCancellationToken cancellationToken = context.mock(BuildCancellationToken.class);
     DefaultTaskGraphExecuter taskExecuter;
     ProjectInternal root;
     List<Task> executedTasks = new ArrayList<Task>();
@@ -68,8 +72,9 @@ public class DefaultTaskGraphExecuterTest {
             will(returnValue(new ListenerBroadcast<TaskExecutionGraphListener>(TaskExecutionGraphListener.class)));
             one(listenerManager).createAnonymousBroadcaster(TaskExecutionListener.class);
             will(returnValue(new ListenerBroadcast<TaskExecutionListener>(TaskExecutionListener.class)));
+            allowing(cancellationToken).isCancellationRequested();
         }});
-        taskExecuter = new DefaultTaskGraphExecuter(listenerManager, new DefaultTaskPlanExecutor());
+        taskExecuter = new DefaultTaskGraphExecuter(listenerManager, new DefaultTaskPlanExecutor(), cancellationToken);
     }
 
     @Test
@@ -480,36 +485,47 @@ public class DefaultTaskGraphExecuterTest {
     }
     
     private Task brokenTask(String name, final RuntimeException failure, final Task... dependsOn) {
-        final TaskInternal task = createTask(name);
+        final TaskInternal task = context.mock(TaskInternal.class);
+        final TaskStateInternal state = context.mock(TaskStateInternal.class);
+        final TaskOutputs outputs = context.mock(DefaultTaskOutputs.class);
+        setExpectations(name, task, state, outputs);
         dependsOn(task, dependsOn);
         context.checking(new Expectations() {{
             atMost(1).of(task).executeWithoutThrowingTaskFailure();
             will(new ExecuteTaskAction(task));
-            allowing(task.getState()).getFailure();
+            allowing(state).getFailure();
             will(returnValue(failure));
-            allowing(task.getState()).rethrowFailure();
+            allowing(state).rethrowFailure();
             will(throwException(failure));
         }});
         return task;
     }
     
     private Task task(final String name, final Task... dependsOn) {
-        final TaskInternal task = createTask(name);
+        final TaskInternal task = context.mock(TaskInternal.class);
+        final TaskStateInternal state = context.mock(TaskStateInternal.class);
+        final TaskOutputs outputs = context.mock(DefaultTaskOutputs.class);
+        setExpectations(name, task, state, outputs);
         dependsOn(task, dependsOn);
         context.checking(new Expectations() {{
             atMost(1).of(task).executeWithoutThrowingTaskFailure();
             will(new ExecuteTaskAction(task));
-            allowing(task.getState()).getFailure();
+            allowing(state).getFailure();
             will(returnValue(null));
         }});
         return task;
     }
     
     private TaskInternal createTask(final String name) {
-        final TaskInternal task = context.mock(TaskInternal.class);
-        context.checking(new Expectations() {{
-            TaskStateInternal state = context.mock(TaskStateInternal.class);
+        TaskInternal task = context.mock(TaskInternal.class);
+        TaskStateInternal state = context.mock(TaskStateInternal.class);
+        final TaskOutputs outputs = context.mock(DefaultTaskOutputs.class);
+        setExpectations(name, task, state, outputs);
+        return task;
+    }
 
+    private void setExpectations(final String name, final TaskInternal task, final TaskStateInternal state, final TaskOutputs outputs) {
+        context.checking(new Expectations() {{
             allowing(task).getProject();
             will(returnValue(root));
             allowing(task).getName();
@@ -518,6 +534,8 @@ public class DefaultTaskGraphExecuterTest {
             will(returnValue(":" + name));
             allowing(task).getState();
             will(returnValue(state));
+            allowing((Task) task).getState();
+            will(returnValue(state));
             allowing(task).getMustRunAfter();
             will(returnValue(new DefaultTaskDependency()));
             allowing(task).getFinalizedBy();
@@ -536,9 +554,11 @@ public class DefaultTaskGraphExecuterTest {
                     description.appendText("compare to");
                 }
             });
+            allowing(task).getOutputs();
+            will(returnValue(outputs));
+            allowing(outputs).getFiles();
+            will(returnValue(root.files()));
         }});
-
-        return task;
     }
 
     private class ExecuteTaskAction implements org.jmock.api.Action {
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/CachingScriptSourceTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/CachingScriptSourceTest.java
index d6c6f4d..75371d8 100755
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/CachingScriptSourceTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/CachingScriptSourceTest.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.CachingResource;
-import org.gradle.api.internal.resource.Resource;
+import org.gradle.internal.resource.CachingResource;
+import org.gradle.internal.resource.Resource;
 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/groovy/scripts/DefaultScriptCompilerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactoryTest.groovy
index 0b6d153..4fd8cc4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/DefaultScriptCompilerFactoryTest.groovy
@@ -15,32 +15,45 @@
  */
 package org.gradle.groovy.scripts
 
-import spock.lang.Specification
-import org.gradle.logging.StandardOutputCapture
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.groovy.scripts.internal.ScriptRunnerFactory
+import org.gradle.api.Action
+import org.gradle.api.internal.initialization.ClassLoaderIds
+import org.gradle.groovy.scripts.internal.CompileOperation
+import org.gradle.groovy.scripts.internal.CompiledScript
 import org.gradle.groovy.scripts.internal.ScriptClassCompiler
+import org.gradle.groovy.scripts.internal.ScriptRunnerFactory
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.logging.StandardOutputCapture
+import spock.lang.Specification
 
 class DefaultScriptCompilerFactoryTest extends Specification {
     final ScriptRunnerFactory scriptRunnerFactory = Mock()
     final ScriptClassCompiler scriptClassCompiler = Mock()
-    final ScriptSource source = Mock()
-    final ScriptRunner<TestScript> runner = Mock()
+    final ScriptSource source = Mock() {
+        getFileName() >> "script.file"
+    }
+    final ScriptRunner<TestScript, ?> runner = Mock()
     final ClassLoader classLoader = Mock()
-    final Transformer transformer = Mock()
+    final CompileOperation<?> operation = Mock() {
+        getId() >> "id"
+    }
+    final CompiledScript<TestScript, ?> compiledScript = Mock() {
+        loadClass() >> TestScript
+    }
+    final verifier = Mock(Action)
     final DefaultScriptCompilerFactory factory = new DefaultScriptCompilerFactory(scriptClassCompiler, scriptRunnerFactory)
 
     def "compiles script into class and wraps instance in script runner"() {
         when:
         def compiler = factory.createCompiler(source)
-        compiler.classloader = classLoader
-        compiler.transformer = transformer
-        def result = compiler.compile(Script)
+        def result = compiler.compile(Script, operation, classLoader, "buildscript", verifier)
 
         then:
         result == runner
-        1 * scriptClassCompiler.compile({it instanceof CachingScriptSource}, classLoader, transformer, Script) >> TestScript
-        1 * scriptRunnerFactory.create({it instanceof TestScript}) >> runner
+
+        1 * scriptClassCompiler.compile({
+            it instanceof CachingScriptSource
+        }, classLoader, ClassLoaderIds.buildScript(source.fileName, operation.id), operation, "buildscript", Script, verifier) >> compiledScript
+        1 * scriptRunnerFactory.create(compiledScript, { it instanceof CachingScriptSource }, classLoader) >> runner
         0 * scriptRunnerFactory._
         0 * scriptClassCompiler._
     }
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 fc1fc83..c890bae 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
@@ -55,12 +55,12 @@ class DefaultScriptTest {
 
         DefaultScript script = new GroovyShell(createBaseCompilerConfiguration()).parse(testScriptText)
         DefaultProject testProject = TestUtil.createRootProject()
-        testProject.custom = 'true'
+        testProject.ext.custom = 'true'
         script.setScriptSource(new StringScriptSource('script', '//'))
         script.init(testProject, serviceRegistryMock)
         script.run();
         assertEquals("scriptMethod", script.scriptMethod())
-        assertEquals(testProject.path + "mySuffix", script.scriptProperty)
+        assertEquals("a", script.newProperty)
     }
 
     private CompilerConfiguration createBaseCompilerConfiguration() {
@@ -76,10 +76,9 @@ getName() // call a project method
 assert hasProperty('custom')
 repositories { } 
 def scriptMethod() { 'scriptMethod' }
-scriptProperty = project.path + 'mySuffix'
 String internalProp = 'a'
 assert internalProp == 'a'
-newProperty = 'a'
+ext.newProperty = 'a'
 assert newProperty == 'a'
 assert newProperty == project.newProperty
 '''
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/StringScriptSourceTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/StringScriptSourceTest.java
index 65dce34..c100368 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/StringScriptSourceTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/StringScriptSourceTest.java
@@ -19,7 +19,7 @@ import static org.gradle.util.Matchers.*;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
-import org.gradle.api.internal.resource.StringResource;
+import org.gradle.internal.resource.StringResource;
 import org.junit.Test;
 
 public class StringScriptSourceTest {
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/UriScriptSourceTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/UriScriptSourceTest.java
index f5942d6..a423ebc 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/UriScriptSourceTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/UriScriptSourceTest.java
@@ -16,7 +16,7 @@
 
 package org.gradle.groovy.scripts;
 
-import org.gradle.api.internal.resource.UriResource;
+import org.gradle.internal.resource.UriResource;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.junit.Before;
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformerSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformerSpec.groovy
new file mode 100644
index 0000000..0184a34
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/BuildScriptTransformerSpec.groovy
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.groovy.scripts.internal
+
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId
+import org.gradle.api.internal.initialization.loadercache.DummyClassLoaderCache
+import org.gradle.api.internal.project.ProjectScript
+import org.gradle.configuration.ImportsReader
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.internal.Actions
+import org.gradle.internal.serialize.BaseSerializerFactory
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildScriptTransformerSpec extends Specification {
+
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+
+    def importsReader = Mock(ImportsReader) {
+        getImportPackages() >> ([] as String[])
+    }
+
+    final DefaultScriptCompilationHandler scriptCompilationHandler = new DefaultScriptCompilationHandler(new AsmBackedEmptyScriptGenerator(), new DummyClassLoaderCache(), importsReader)
+    final String classpathClosureName = "buildscript"
+
+    File scriptCacheDir
+    File metadataCacheDir
+    private classLoaderId = Mock(ClassLoaderId)
+
+    def setup() {
+        File testProjectDir = tmpDir.createDir("projectDir");
+        scriptCacheDir = new File(testProjectDir, "cache");
+        metadataCacheDir = new File(testProjectDir, "metadata");
+    }
+
+    private boolean containsImperativeStatements(String script) {
+        def source = new StringScriptSource("test script", script)
+        def loader = getClass().getClassLoader()
+        def transformer = new BuildScriptTransformer(classpathClosureName, source)
+        def operation = new FactoryBackedCompileOperation("id", transformer, transformer, BaseSerializerFactory.BOOLEAN_SERIALIZER)
+        scriptCompilationHandler.compileToDir(source, loader, scriptCacheDir, metadataCacheDir, operation, classpathClosureName, ProjectScript, Actions.doNothing())
+        scriptCompilationHandler.loadFromDir(source, loader, scriptCacheDir, metadataCacheDir, operation, ProjectScript, classLoaderId).data
+    }
+
+    def "empty script does not contain imperative code"() {
+        expect:
+        !containsImperativeStatements("")
+        !containsImperativeStatements("//ignore me")
+    }
+
+    def "class, method and property declarations are not considered imperative code"() {
+        expect:
+        !containsImperativeStatements("""
+            class SomeClass {}
+            String a
+        """)
+    }
+
+    def "non-imperative script blocks are not considered imperative code"() {
+        expect:
+        !containsImperativeStatements("plugins {}; buildscript {}; model {}")
+    }
+
+    def "imports are not considered imperative code"() {
+        !containsImperativeStatements("import java.lang.String")
+    }
+
+    def "method declarations are considered imperative code"() {
+        expect:
+        containsImperativeStatements("def method() { println 'hi' }")
+    }
+
+    def "imperative code is detected"() {
+        expect:
+        containsImperativeStatements("foo = 'bar'")
+        containsImperativeStatements("foo")
+        containsImperativeStatements("println 'hi!'")
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompilerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompilerTest.groovy
index 8364cae..d9269ec 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompilerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/CachingScriptClassCompilerTest.groovy
@@ -15,29 +15,35 @@
  */
 package org.gradle.groovy.scripts.internal
 
-import spock.lang.Specification
-import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.groovy.scripts.Transformer
+import org.gradle.api.Action
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId
 import org.gradle.groovy.scripts.Script
+import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.groovy.scripts.TestScript
+import org.gradle.groovy.scripts.Transformer
+import spock.lang.Specification
 
 class CachingScriptClassCompilerTest extends Specification {
     private final ScriptClassCompiler target = Mock()
     private final CachingScriptClassCompiler compiler = new CachingScriptClassCompiler(target)
+    private final CompiledScript<?, ?> compiledScript = Mock(CompiledScript)
+    private final String classpathClosureName = "buildscript"
+    final verifier = Mock(Action)
+    def classLoaderId = Mock(ClassLoaderId)
 
     def "caches the script class for a given script class and classloader and transformer and baseclass"() {
         ScriptSource script1 = scriptSource('script')
         ScriptSource script2 = scriptSource('script')
         ClassLoader parentClassLoader = Mock()
-        Transformer transformer = transformer()
+        CompileOperation<?> transformer = operation()
 
         when:
-        def c1 = compiler.compile(script1, parentClassLoader, transformer, Script.class)
-        def c2 = compiler.compile(script2, parentClassLoader, transformer, Script.class)
+        def c1 = compiler.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        def c2 = compiler.compile(script2, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
 
         then:
         c1 == c2
-        1 * target.compile(script1, parentClassLoader, transformer, Script.class) >> Script.class
+        1 * target.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier) >> compiledScript
         0 * target._
     }
 
@@ -45,31 +51,31 @@ class CachingScriptClassCompilerTest extends Specification {
         ScriptSource script1 = scriptSource('script')
         ScriptSource script2 = scriptSource('other')
         ClassLoader parentClassLoader = Mock()
-        Transformer transformer = transformer()
+        CompileOperation<?> transformer = operation()
 
         when:
-        def c1 = compiler.compile(script1, parentClassLoader, transformer, Script.class)
-        def c2 = compiler.compile(script2, parentClassLoader, transformer, Script.class)
+        compiler.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        compiler.compile(script2, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
 
         then:
-        1 * target.compile(script1, parentClassLoader, transformer, Script.class) >> Script.class
-        1 * target.compile(script2, parentClassLoader, transformer, Script.class) >> Script.class
+        1 * target.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        1 * target.compile(script2, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
     }
 
     def "does not cache script class for different transformers"() {
         ScriptSource script1 = scriptSource('script')
         ScriptSource script2 = scriptSource('script')
         ClassLoader parentClassLoader = Mock()
-        Transformer transformer1 = transformer('t1')
-        Transformer transformer2 = transformer('t2')
+        CompileOperation<?> transformer1 = operation('t1')
+        CompileOperation<?> transformer2 = operation('t2')
 
         when:
-        def c1 = compiler.compile(script1, parentClassLoader, transformer1, Script.class)
-        def c2 = compiler.compile(script2, parentClassLoader, transformer2, Script.class)
+        compiler.compile(script1, parentClassLoader, classLoaderId, transformer1, classpathClosureName, Script.class, verifier)
+        compiler.compile(script2, parentClassLoader, classLoaderId, transformer2, classpathClosureName, Script.class, verifier)
 
         then:
-        1 * target.compile(script1, parentClassLoader, transformer1, Script.class) >> Script.class
-        1 * target.compile(script2, parentClassLoader, transformer2, Script.class) >> Script.class
+        1 * target.compile(script1, parentClassLoader, classLoaderId, transformer1, classpathClosureName, Script.class, verifier)
+        1 * target.compile(script2, parentClassLoader, classLoaderId, transformer2, classpathClosureName, Script.class, verifier)
     }
 
     def "does not cache script class for different classloaders"() {
@@ -77,30 +83,30 @@ class CachingScriptClassCompilerTest extends Specification {
         ScriptSource script2 = scriptSource('script')
         ClassLoader parentClassLoader1 = Mock()
         ClassLoader parentClassLoader2 = Mock()
-        Transformer transformer = transformer()
+        CompileOperation<?> transformer = operation()
 
         when:
-        def c1 = compiler.compile(script1, parentClassLoader1, transformer, Script.class)
-        def c2 = compiler.compile(script2, parentClassLoader2, transformer, Script.class)
+        compiler.compile(script1, parentClassLoader1, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        compiler.compile(script2, parentClassLoader2, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
 
         then:
-        1 * target.compile(script1, parentClassLoader1, transformer, Script.class) >> Script.class
-        1 * target.compile(script2, parentClassLoader2, transformer, Script.class) >> Script.class
+        1 * target.compile(script1, parentClassLoader1, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        1 * target.compile(script2, parentClassLoader2, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
     }
 
     def "does not cache script class for different base classes"() {
         ScriptSource script1 = scriptSource('script')
         ScriptSource script2 = scriptSource('script')
         ClassLoader parentClassLoader = Mock()
-        Transformer transformer = transformer()
+        CompileOperation<?> transformer = operation()
 
         when:
-        def c1 = compiler.compile(script1, parentClassLoader, transformer, Script.class)
-        def c2 = compiler.compile(script2, parentClassLoader, transformer, TestScript.class)
+        compiler.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        compiler.compile(script2, parentClassLoader, classLoaderId, transformer, classpathClosureName, TestScript.class, verifier)
 
         then:
-        1 * target.compile(script1, parentClassLoader, transformer, Script.class) >> Script.class
-        1 * target.compile(script2, parentClassLoader, transformer, TestScript.class) >> TestScript.class
+        1 * target.compile(script1, parentClassLoader, classLoaderId, transformer, classpathClosureName, Script.class, verifier)
+        1 * target.compile(script2, parentClassLoader, classLoaderId, transformer, classpathClosureName, TestScript.class, verifier)
     }
 
     def scriptSource(String className = 'script') {
@@ -109,9 +115,11 @@ class CachingScriptClassCompilerTest extends Specification {
         script
     }
 
-    def transformer(String id = 'id') {
+    def operation(String id = 'id') {
+        CompileOperation<?> operation = Mock()
         Transformer transformer = Mock()
-        _ * transformer.id >> id
-        transformer
+        operation.id >> id
+        operation.transformer >> transformer
+        operation
     }
 }
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 3ced662..33c1533 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
@@ -18,6 +18,7 @@ package org.gradle.groovy.scripts.internal;
 
 import groovy.lang.Script;
 import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
@@ -26,12 +27,20 @@ import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.control.CompilationFailedException;
 import org.codehaus.groovy.control.Phases;
 import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.api.Action;
 import org.gradle.api.GradleException;
-import org.gradle.api.internal.resource.Resource;
+import org.gradle.api.internal.initialization.ClassLoaderIds;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderId;
+import org.gradle.api.internal.initialization.loadercache.DummyClassLoaderCache;
+import org.gradle.configuration.ImportsReader;
 import org.gradle.groovy.scripts.ScriptCompilationException;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.Transformer;
+import org.gradle.internal.Actions;
+import org.gradle.internal.resource.Resource;
+import org.gradle.internal.serialize.BaseSerializerFactory;
+import org.gradle.internal.serialize.Serializer;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -59,32 +68,46 @@ public class DefaultScriptCompilationHandlerTest {
     private DefaultScriptCompilationHandler scriptCompilationHandler;
 
     private File scriptCacheDir;
+    private File metadataCacheDir;
     private File cachedFile;
 
     private ScriptSource scriptSource;
     private String scriptText;
     private String scriptClassName;
     private String scriptFileName;
+    private String classpathClosureName;
 
     private ClassLoader classLoader;
 
+    private Action<ClassNode> verifier = Actions.doNothing();
+
     private Class<? extends Script> expectedScriptClass;
 
+    private ImportsReader importsReader;
+
     private JUnit4Mockery context = new JUnit4Mockery();
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+    private final ClassLoaderId classLoaderId = ClassLoaderIds.buildScript("foo", "bar");
 
     @Before
     public void setUp() throws IOException, ClassNotFoundException {
         File testProjectDir = tmpDir.createDir("projectDir");
         classLoader = getClass().getClassLoader();
-        scriptCompilationHandler = new DefaultScriptCompilationHandler(new AsmBackedEmptyScriptGenerator());
+        importsReader = context.mock(ImportsReader.class);
+        context.checking(new Expectations() {{
+            allowing(importsReader).getImportPackages();
+            will(returnValue(new String[0]));
+        }});
+        scriptCompilationHandler = new DefaultScriptCompilationHandler(new AsmBackedEmptyScriptGenerator(), new DummyClassLoaderCache(), importsReader);
         scriptCacheDir = new File(testProjectDir, "cache");
+        metadataCacheDir = new File(testProjectDir, "metadata");
         scriptText = "System.setProperty('" + TEST_EXPECTED_SYSTEMPROP_KEY + "', '" + TEST_EXPECTED_SYSTEMPROP_VALUE
                 + "')";
 
         scriptClassName = "ScriptClassName";
         scriptFileName = "script-file-name";
+        classpathClosureName = "buildscript";
         scriptSource = scriptSource();
         cachedFile = new File(scriptCacheDir, scriptClassName + ".class");
         expectedScriptClass = TestBaseScript.class;
@@ -96,7 +119,7 @@ public class DefaultScriptCompilationHandlerTest {
 
     private ScriptSource scriptSource(final String scriptText) {
         final ScriptSource source = context.mock(ScriptSource.class, scriptText);
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             Resource resource = context.mock(Resource.class, scriptText + "resource");
 
             allowing(source).getClassName();
@@ -120,12 +143,13 @@ public class DefaultScriptCompilationHandlerTest {
 
     @Test
     public void testCompileScriptToDir() throws Exception {
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkScriptClassesInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         evaluateScript(script);
     }
 
@@ -134,7 +158,7 @@ public class DefaultScriptCompilationHandlerTest {
         final ScriptSource scriptSource = scriptSource("package org.gradle.test\n" + scriptText);
 
         try {
-            scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+            scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
             fail();
         } catch (UnsupportedOperationException e) {
             assertThat(e.getMessage(), equalTo("Script-display-name should not contain a package statement."));
@@ -144,69 +168,73 @@ public class DefaultScriptCompilationHandlerTest {
     @Test
     public void testCompileScriptToDirWithWhitespaceOnly() throws Exception {
         final ScriptSource scriptSource = scriptSource("// ignore me\n");
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkEmptyScriptInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         assertThat(script, isA(expectedScriptClass));
     }
 
     @Test
     public void testCompileScriptToDirWithEmptyScript() throws Exception {
         final ScriptSource scriptSource = scriptSource("");
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkEmptyScriptInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         assertThat(script, isA(expectedScriptClass));
     }
 
     @Test
     public void testCompileScriptToDirWithClassDefinitionOnlyScript() throws Exception {
         final ScriptSource scriptSource = scriptSource("class SomeClass {}");
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkEmptyScriptInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         assertThat(script, isA(expectedScriptClass));
     }
 
     @Test
     public void testCompileScriptToDirWithMethodOnlyScript() throws Exception {
         final ScriptSource scriptSource = scriptSource("def method() { println 'hi' }");
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkScriptClassesInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         assertThat(script, isA(expectedScriptClass));
     }
 
     @Test
     public void testCompileScriptToDirWithPropertiesOnlyScript() throws Exception {
         final ScriptSource scriptSource = scriptSource("String a");
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, expectedScriptClass);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
 
         checkScriptClassesInCache();
 
-        Script script = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                expectedScriptClass).newInstance();
+        CompiledScript<? extends Script, Void> compiledScript = scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId);
+
+        Script script = compiledScript.loadClass().newInstance();
         assertThat(script, isA(expectedScriptClass));
     }
 
     @Test
     public void testLoadFromDirWhenNotAssignableToBaseClass() {
-        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, null, Script.class);
+        scriptCompilationHandler.compileToDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, Script.class, verifier);
         try {
-            scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir,
-                    expectedScriptClass);
+            scriptCompilationHandler.loadFromDir(scriptSource, classLoader, scriptCacheDir, metadataCacheDir, null, expectedScriptClass, classLoaderId).loadClass();
             fail();
         } catch (GradleException e) {
             assertThat(e.getMessage(), containsString("Could not load compiled classes for script-display-name from cache."));
@@ -218,7 +246,7 @@ public class DefaultScriptCompilationHandlerTest {
     public void testCompileToDirWithSyntaxError() {
         ScriptSource source = new StringScriptSource("script.gradle", "\n\nnew HHHHJSJSJ jsj");
         try {
-            scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, null, expectedScriptClass);
+            scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
             fail();
         } catch (ScriptCompilationException e) {
             assertThat(e.getScriptSource(), sameInstance(source));
@@ -231,11 +259,7 @@ public class DefaultScriptCompilationHandlerTest {
 
     @Test
     public void testCanVisitAndTransformScriptClass() throws Exception {
-        Transformer visitor = new AbstractScriptTransformer() {
-            public String getId() {
-                return "id";
-            }
-
+        final Transformer visitor = new AbstractScriptTransformer() {
             protected int getPhase() {
                 return Phases.CANONICALIZATION;
             }
@@ -255,12 +279,45 @@ public class DefaultScriptCompilationHandlerTest {
             }
         };
 
+        CompileOperation<?> transformer = new CompileOperation<Boolean>() {
+            @Override
+            public String getId() {
+                return "id";
+            }
+
+            @Override
+            public Transformer getTransformer() {
+                return visitor;
+            }
+
+            @Override
+            public Boolean getExtractedData() {
+                return true;
+            }
+
+            @Override
+            public Serializer<Boolean> getDataSerializer() {
+                return BaseSerializerFactory.BOOLEAN_SERIALIZER;
+            }
+        };
+
         ScriptSource source = scriptSource("transformMe()");
-        scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, visitor, expectedScriptClass);
-        Script script = scriptCompilationHandler.loadFromDir(source, classLoader, scriptCacheDir, expectedScriptClass).newInstance();
+        scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, metadataCacheDir, transformer, classpathClosureName, expectedScriptClass, verifier);
+        Script script = scriptCompilationHandler.loadFromDir(source, classLoader, scriptCacheDir, metadataCacheDir, transformer, expectedScriptClass, classLoaderId).loadClass().newInstance();
         evaluateScript(script);
     }
 
+    @Test
+    public void testCanVisitAndTransformGeneratedClasses() throws Exception {
+        final Action<ClassNode> verifier = context.mock(Action.class);
+        context.checking(new Expectations() {{
+            one(verifier).execute(with(notNullValue(ClassNode.class)));
+        }});
+
+        ScriptSource source = scriptSource("transformMe()");
+        scriptCompilationHandler.compileToDir(source, classLoader, scriptCacheDir, metadataCacheDir, null, classpathClosureName, expectedScriptClass, verifier);
+    }
+
     private void checkScriptClassesInCache() {
         assertTrue(scriptCacheDir.isDirectory());
         assertTrue(cachedFile.isFile());
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactoryTest.java b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactoryTest.java
index f922418..cf93f5d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactoryTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/DefaultScriptRunnerFactoryTest.java
@@ -20,6 +20,7 @@ import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptExecutionListener;
 import org.gradle.groovy.scripts.ScriptRunner;
 import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.logging.StandardOutputCapture;
 import org.hamcrest.Description;
 import org.jmock.Expectations;
@@ -34,42 +35,51 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 @RunWith(JMock.class)
 public class DefaultScriptRunnerFactoryTest {
     private final JUnit4Mockery context = new JUnit4Mockery() {{
         setImposteriser(ClassImposteriser.INSTANCE);
     }};
+    private final CompiledScript<? extends Script, Void> compiledScriptMock = context.mock(CompiledScript.class);
     private final Script scriptMock = context.mock(Script.class, "<script-to-string>");
     private final StandardOutputCapture standardOutputCaptureMock = context.mock(StandardOutputCapture.class);
     private final ClassLoader classLoaderDummy = context.mock(ClassLoader.class);
     private final ScriptSource scriptSourceDummy = context.mock(ScriptSource.class);
     private final ScriptExecutionListener scriptExecutionListenerMock = context.mock(ScriptExecutionListener.class);
-    private final DefaultScriptRunnerFactory factory = new DefaultScriptRunnerFactory(scriptExecutionListenerMock);
+    private final Instantiator instantiatorMock = context.mock(Instantiator.class);
+    private final DefaultScriptRunnerFactory factory = new DefaultScriptRunnerFactory(scriptExecutionListenerMock, instantiatorMock);
 
     @Before
     public void setUp() {
         context.checking(new Expectations() {{
+            allowing(compiledScriptMock).loadClass();
+            will(returnValue(Script.class));
+            allowing(instantiatorMock).newInstance(Script.class);
+            will(returnValue(scriptMock));
             allowing(scriptMock).getStandardOutputCapture();
             will(returnValue(standardOutputCaptureMock));
             allowing(scriptMock).getScriptSource();
             will(returnValue(scriptSourceDummy));
             allowing(scriptMock).getContextClassloader();
             will(returnValue(classLoaderDummy));
+            allowing(scriptMock).setScriptSource(scriptSourceDummy);
+            allowing(scriptMock).setContextClassloader(classLoaderDummy);
             ignoring(scriptSourceDummy);
         }});
     }
 
     @Test
     public void createsScriptRunner() {
-        ScriptRunner<Script> scriptRunner = factory.create(scriptMock);
-        assertThat(scriptRunner.getScript(), sameInstance(scriptMock));
+        ScriptRunner<?, Void> scriptRunner = factory.create(compiledScriptMock, scriptSourceDummy, classLoaderDummy);
+        assertThat(scriptRunner.getScript(), sameInstance(scriptRunner.getScript()));
     }
 
     @Test
     public void redirectsStandardOutputAndSetsContextClassLoaderWhenScriptIsRun() {
-        ScriptRunner<Script> scriptRunner = factory.create(scriptMock);
+        ScriptRunner<?, Void> scriptRunner = factory.create(compiledScriptMock, scriptSourceDummy, classLoaderDummy);
 
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("seq");
@@ -112,7 +122,7 @@ public class DefaultScriptRunnerFactoryTest {
     public void wrapsExecutionExceptionAndRestoresStateWhenScriptFails() {
         final RuntimeException failure = new RuntimeException();
 
-        ScriptRunner<Script> scriptRunner = factory.create(scriptMock);
+        ScriptRunner<?, Void> scriptRunner = factory.create(compiledScriptMock, scriptSourceDummy, classLoaderDummy);
 
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("seq");
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompilerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompilerTest.groovy
index d533a3e..d637283 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompilerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompilerTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.groovy.scripts.internal
 
 import org.gradle.api.Action
-import org.gradle.api.internal.resource.Resource
+import org.gradle.api.internal.initialization.ClassLoaderIds
 import org.gradle.cache.CacheBuilder
 import org.gradle.cache.CacheRepository
 import org.gradle.cache.CacheValidator
@@ -24,6 +24,7 @@ import org.gradle.cache.PersistentCache
 import org.gradle.groovy.scripts.Script
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.groovy.scripts.Transformer
+import org.gradle.internal.resource.Resource
 import org.gradle.logging.ProgressLogger
 import org.gradle.logging.ProgressLoggerFactory
 import spock.lang.Specification
@@ -37,8 +38,17 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
     final ScriptSource source = Mock()
     final ClassLoader classLoader = Mock()
     final Transformer transformer = Mock()
+    final CompileOperation<?> operation = Mock()
     final File cacheDir = new File("base-dir")
+    final File classesDir = new File(cacheDir, "classes")
+    final File metadataDir = new File(cacheDir, "metadata")
     final FileCacheBackedScriptClassCompiler compiler = new FileCacheBackedScriptClassCompiler(cacheRepository, validator, scriptCompilationHandler, Stub(ProgressLoggerFactory))
+    final Action verifier = Stub()
+    final String classpathClosureName = "buildscript"
+    final CompiledScript compiledScript = Stub() {
+        loadClass() >> Script
+    }
+    def classLoaderId = ClassLoaderIds.buildScript("foo", "bar")
 
     def setup() {
         Resource resource = Mock()
@@ -46,14 +56,15 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
         _ * resource.text >> 'this is the script'
         _ * source.className >> 'ScriptClassName'
         _ * source.fileName >> 'ScriptFileName'
-        _ * transformer.id >> 'TransformerId'
+        _ * operation.id >> 'TransformerId'
+        _ * operation.transformer >> transformer
         _ * cache.baseDir >> cacheDir
         _ * validator.isValid() >> true
     }
 
     def "loads classes from cache directory"() {
         when:
-        def result = compiler.compile(source, classLoader, transformer, Script)
+        def result = compiler.compile(source, classLoader, classLoaderId, operation, classpathClosureName, Script, verifier).loadClass()
 
         then:
         result == Script
@@ -67,7 +78,7 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
         1 * cacheBuilder.withDisplayName(!null) >> cacheBuilder
         1 * cacheBuilder.withValidator(!null) >> cacheBuilder
         1 * cacheBuilder.open() >> cache
-        1 * scriptCompilationHandler.loadFromDir(source, classLoader, new File(cacheDir, "classes"), Script) >> Script
+        1 * scriptCompilationHandler.loadFromDir(source, classLoader, classesDir, metadataDir, operation, Script, classLoaderId) >> compiledScript
         0 * scriptCompilationHandler._
     }
 
@@ -78,10 +89,10 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
         cacheBuilder.withInitializer(!null) >> cacheBuilder
         cacheBuilder.withDisplayName(!null) >> cacheBuilder
         cacheBuilder.open() >> cache
-        scriptCompilationHandler.loadFromDir(source, classLoader, new File(cacheDir, "classes"), Script) >> Script
+        scriptCompilationHandler.loadFromDir(source, classLoader, classesDir, metadataDir, operation, Script, classLoaderId) >> compiledScript
 
         when:
-        compiler.compile(source, classLoader, transformer, Script)
+        compiler.compile(source, classLoader, classLoaderId, operation, classpathClosureName, Script, verifier)
 
         then:
         1 * cacheBuilder.withValidator(validator) >> cacheBuilder
@@ -91,9 +102,11 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
 
     def "compiles classes to cache directory when cache is invalid"() {
         def initializer
+        def classesDir = classesDir
+        def metadataDir = new File(cacheDir, "metadata")
 
         when:
-        def result = compiler.compile(source, classLoader, transformer, Script)
+        def result = compiler.compile(source, classLoader, classLoaderId, operation, classpathClosureName, Script, verifier).loadClass()
 
         then:
         result == Script
@@ -101,10 +114,10 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
         1 * cacheBuilder.withProperties(!null) >> cacheBuilder
         1 * cacheBuilder.withDisplayName(!null) >> cacheBuilder
         1 * cacheBuilder.withValidator(!null) >> cacheBuilder
-        1 * cacheBuilder.withInitializer(!null) >> {args -> initializer = args[0]; return cacheBuilder}
-        1 * cacheBuilder.open() >> {initializer.execute(cache); return cache}
-        1 * scriptCompilationHandler.compileToDir(source, classLoader, new File(cacheDir, "classes"), transformer, Script)
-        1 * scriptCompilationHandler.loadFromDir(source, classLoader, new File(cacheDir, "classes"), Script) >> Script
+        1 * cacheBuilder.withInitializer(!null) >> { args -> initializer = args[0]; return cacheBuilder }
+        1 * cacheBuilder.open() >> { initializer.execute(cache); return cache }
+        1 * scriptCompilationHandler.compileToDir(source, classLoader, classesDir, metadataDir, operation, classpathClosureName, Script, verifier)
+        1 * scriptCompilationHandler.loadFromDir(source, classLoader, classesDir, metadataDir, operation, Script, classLoaderId) >> compiledScript
         0 * scriptCompilationHandler._
     }
 
@@ -127,7 +140,7 @@ class FileCacheBackedScriptClassCompilerTest extends Specification {
         1 * logger.start("Compile script into cache", "Compiling script into cache") >> logger
 
         then:
-        1 * delegate.execute(cache) >> { throw new RuntimeException("Boo!")} //stress it a bit with a failure
+        1 * delegate.execute(cache) >> { throw new RuntimeException("Boo!") } //stress it a bit with a failure
 
         then:
         1 * logger.completed()
diff --git a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompilerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompilerTest.groovy
index 1719427..deff095 100644
--- a/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompilerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/groovy/scripts/internal/ShortCircuitEmptyScriptCompilerTest.groovy
@@ -15,12 +15,14 @@
  */
 package org.gradle.groovy.scripts.internal
 
-import spock.lang.Specification
-import org.gradle.api.internal.resource.Resource
-import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.groovy.scripts.Transformer
+import org.gradle.api.Action
+import org.gradle.api.internal.initialization.ClassLoaderIds
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache
 import org.gradle.groovy.scripts.Script
+import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.groovy.scripts.TestScript
+import org.gradle.internal.resource.Resource
+import spock.lang.Specification
 
 class ShortCircuitEmptyScriptCompilerTest extends Specification {
     final EmptyScriptGenerator emptyScriptGenerator = Mock()
@@ -28,8 +30,12 @@ class ShortCircuitEmptyScriptCompilerTest extends Specification {
     final ScriptSource source = Mock()
     final Resource resource = Mock()
     final ClassLoader classLoader = Mock()
-    final Transformer transformer = Mock()
-    final ShortCircuitEmptyScriptCompiler compiler = new ShortCircuitEmptyScriptCompiler(target, emptyScriptGenerator)
+    final CompileOperation<?> operation = Mock()
+    final Action verifier = Mock()
+    final classLoaderCache = Mock(ClassLoaderCache)
+    final ShortCircuitEmptyScriptCompiler compiler = new ShortCircuitEmptyScriptCompiler(target, emptyScriptGenerator, classLoaderCache)
+    String classpathClosureName = "buildscript"
+    def loaderId = ClassLoaderIds.buildScript(source.getFileName(), operation.getId())
 
     def setup() {
         _ * source.resource >> resource
@@ -37,29 +43,37 @@ class ShortCircuitEmptyScriptCompilerTest extends Specification {
 
     def "returns empty script object when script contains only whitespace"() {
         given:
+        def metadata = "metadata"
         _ * resource.text >> '  \n\t'
+        _ * operation.extractedData >> metadata
+
 
         when:
-        def result = compiler.compile(source, classLoader, transformer, Script)
+        def compiledScript = compiler.compile(source, classLoader, loaderId, operation, classpathClosureName, Script, verifier)
+        def scriptClass = compiledScript.loadClass()
 
         then:
-        result == TestScript
+        scriptClass == TestScript
+        compiledScript.data == metadata
         1 * emptyScriptGenerator.generate(Script) >> TestScript
         0 * emptyScriptGenerator._
         0 * target._
+        1 * classLoaderCache.remove(loaderId)
     }
 
     def "compiles script when script contains anything other than whitespace"() {
         given:
         _ * resource.text >> 'some script'
+        CompiledScript<?> compiledScript = Mock()
 
         when:
-        def result = compiler.compile(source, classLoader, transformer, Script)
+        def result = compiler.compile(source, classLoader, ClassLoaderIds.buildScript(source.getFileName(), operation.getId()), operation, classpathClosureName, Script, verifier)
 
         then:
-        result == TestScript
-        1 * target.compile(source, classLoader, transformer, Script) >> TestScript
+        result == compiledScript
+        1 * target.compile(source, classLoader, ClassLoaderIds.buildScript(source.getFileName(), operation.getId()), operation, classpathClosureName, Script, verifier) >> compiledScript
         0 * emptyScriptGenerator._
         0 * target._
+        0 * classLoaderCache._
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
index 4954dc8..35ae20b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
@@ -34,8 +34,10 @@ class BuildLayoutParametersTest extends Specification {
         expect:
         def params = new BuildLayoutParameters()
         params.searchUpwards
-        params.gradleUserHomeDir == canonicalise(StartParameter.DEFAULT_GRADLE_USER_HOME)
-        params.projectDir == canonicalise(SystemProperties.getCurrentDir())
+        params.gradleUserHomeDir == canonicalise(BuildLayoutParameters.DEFAULT_GRADLE_USER_HOME)
+        params.currentDir == canonicalise(SystemProperties.instance.getCurrentDir())
+        params.projectDir == null
+        params.searchDir == params.currentDir
     }
 
     def "reads gradle user home dir from system property"() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultBuildCancellationTokenSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultBuildCancellationTokenSpec.groovy
new file mode 100644
index 0000000..8938998
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultBuildCancellationTokenSpec.groovy
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.internal.exceptions.DefaultMultiCauseException
+import spock.lang.Specification
+
+class DefaultBuildCancellationTokenSpec extends Specification {
+    def 'can cancel token'() {
+        when:
+        def token = new DefaultBuildCancellationToken()
+
+        then:
+        !token.cancellationRequested
+
+        when:
+        token.doCancel()
+
+        then:
+        token.cancellationRequested
+    }
+
+    def 'cancel notifies callbacks'() {
+        def token = new DefaultBuildCancellationToken()
+
+        def callback1 = Mock(Runnable)
+        def callback2 = Mock(Runnable)
+        token.addCallback(callback1)
+        token.addCallback(callback2)
+
+        when:
+        token.doCancel()
+
+        then:
+        token.cancellationRequested
+        1 * callback1.run()
+        1 * callback2.run()
+    }
+
+    def 'addCallback after cancel notifies'() {
+        def token = new DefaultBuildCancellationToken()
+
+        def callback = Mock(Runnable)
+        token.doCancel()
+
+        when:
+        token.addCallback(callback)
+
+        then:
+        token.cancellationRequested
+        1 * callback.run()
+    }
+
+    def 'cancel drops references'() {
+        def token = new DefaultBuildCancellationToken()
+
+        def callback1 = Mock(Runnable)
+        token.addCallback(callback1)
+
+        when:
+        token.doCancel()
+
+        then:
+        token.cancellationRequested
+        1 * callback1.run()
+        token.callbacks.empty
+    }
+
+    def 'cancel notifies callbacks even if exception is thrown'() {
+        def token = new DefaultBuildCancellationToken()
+        def ex = new IllegalStateException('testing')
+
+        def callback1 = Mock(Runnable)
+        def callback2 = Mock(Runnable)
+        token.addCallback(callback1)
+        token.addCallback(callback2)
+
+        when:
+        token.doCancel()
+
+        then:
+        RuntimeException e = thrown()
+        e.cause == ex
+        token.cancellationRequested
+
+        and:
+        1 * callback1.run() >> { throw ex }
+        1 * callback2.run()
+    }
+
+    def 'cancel notifies callbacks and preserves exceptions'() {
+        def token = new DefaultBuildCancellationToken()
+        def ex1 = new IllegalStateException('testing', new IOException('something happened'))
+        def ex2 = new IllegalStateException('testing')
+
+        def callback1 = Mock(Runnable)
+        def callback2 = Mock(Runnable)
+        def callback3 = Mock(Runnable)
+        token.addCallback(callback1)
+        token.addCallback(callback2)
+        token.addCallback(callback3)
+
+        when:
+        token.doCancel()
+
+        then:
+        DefaultMultiCauseException e = thrown()
+        e.causes == [ex1, ex2]
+        token.cancellationRequested
+
+        and:
+        1 * callback1.run() >> { throw ex1 }
+        1 * callback2.run() >> { throw ex2 }
+        1 * callback3.run()
+    }
+
+    def 'removed callback is not notified'() {
+        def token = new DefaultBuildCancellationToken()
+
+        def callback = Mock(Runnable)
+        token.addCallback(callback)
+        token.removeCallback(callback)
+
+        when:
+        token.doCancel()
+
+        then:
+        token.cancellationRequested
+        0 * callback.run()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.groovy
new file mode 100644
index 0000000..eb9420b
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.groovy
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.logging.LogLevel
+import org.gradle.cli.CommandLineArgumentException
+import org.gradle.logging.ConsoleOutput
+import org.gradle.logging.ShowStacktrace
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import org.junit.Test
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static java.util.Arrays.asList
+import static org.gradle.util.WrapUtil.*
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertThat
+
+public class DefaultCommandLineConverterTest extends Specification {
+    @Rule
+    public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
+
+    private TestFile currentDir = testDir.file("current-dir");
+    private File expectedBuildFile;
+    private File expectedGradleUserHome = new BuildLayoutParameters().getGradleUserHomeDir();
+    private File expectedCurrentDir = currentDir;
+    private File expectedProjectDir;
+    private List<String> expectedTaskNames = toList();
+    private Set<String> expectedExcludedTasks = toSet();
+    private boolean buildProjectDependencies = true;
+    private Map<String, String> expectedSystemProperties = new HashMap<String, String>();
+    private Map<String, String> expectedProjectProperties = new HashMap<String, String>();
+    private List<File> expectedInitScripts = new ArrayList<File>();
+    private boolean expectedSearchUpwards = true;
+    private boolean expectedDryRun;
+    private ShowStacktrace expectedShowStackTrace = ShowStacktrace.INTERNAL_EXCEPTIONS;
+    private LogLevel expectedLogLevel = LogLevel.LIFECYCLE;
+    private boolean expectedColorOutput = true;
+    private ConsoleOutput expectedConsoleOutput = ConsoleOutput.Auto;
+    private StartParameter actualStartParameter;
+    private boolean expectedProfile;
+    private File expectedProjectCacheDir;
+    private boolean expectedRefreshDependencies;
+    private boolean expectedRerunTasks;
+    private final DefaultCommandLineConverter commandLineConverter = new DefaultCommandLineConverter();
+    private boolean expectedContinue;
+    private boolean expectedOffline;
+    private boolean expectedRecompileScripts;
+    private boolean expectedParallelProjectExecution;
+    private int expectedParallelExecutorCount;
+    private int expectedMaxWorkersCount = Runtime.getRuntime().availableProcessors();
+    private boolean expectedConfigureOnDemand;
+
+    @Test
+    public void withoutAnyOptions() {
+        checkConversion();
+    }
+
+    private void checkConversion(String... args) {
+        actualStartParameter = new StartParameter();
+        actualStartParameter.setCurrentDir(currentDir);
+        commandLineConverter.convert(asList(args), actualStartParameter);
+        // We check the params passed to the build factory
+        checkStartParameter(actualStartParameter);
+    }
+
+    private void checkStartParameter(StartParameter startParameter) {
+        assertEquals(expectedBuildFile, startParameter.getBuildFile());
+        assertEquals(expectedTaskNames, startParameter.getTaskNames());
+        assertEquals(buildProjectDependencies, startParameter.isBuildProjectDependencies());
+        assertEquals(expectedCurrentDir.getAbsoluteFile(), startParameter.getCurrentDir().getAbsoluteFile());
+        assertEquals(expectedProjectDir, startParameter.getProjectDir());
+        assertEquals(expectedSearchUpwards, startParameter.isSearchUpwards());
+        assertEquals(expectedProjectProperties, startParameter.getProjectProperties());
+        assertEquals(expectedSystemProperties, startParameter.getSystemPropertiesArgs());
+        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
+        assertEquals(expectedLogLevel, startParameter.getLogLevel());
+        assertEquals(expectedColorOutput, startParameter.isColorOutput());
+        assertEquals(expectedConsoleOutput, startParameter.getConsoleOutput());
+        assertEquals(expectedDryRun, startParameter.isDryRun());
+        assertEquals(expectedShowStackTrace, startParameter.getShowStacktrace());
+        assertEquals(expectedExcludedTasks, startParameter.getExcludedTaskNames());
+        assertEquals(expectedInitScripts, startParameter.getInitScripts());
+        assertEquals(expectedProfile, startParameter.isProfile());
+        assertEquals(expectedContinue, startParameter.isContinueOnFailure());
+        assertEquals(expectedOffline, startParameter.isOffline());
+        assertEquals(expectedRecompileScripts, startParameter.isRecompileScripts());
+        assertEquals(expectedRerunTasks, startParameter.isRerunTasks());
+        assertEquals(expectedRefreshDependencies, startParameter.isRefreshDependencies());
+        assertEquals(expectedProjectCacheDir, startParameter.getProjectCacheDir());
+        assertEquals(expectedParallelExecutorCount, startParameter.getParallelThreadCount());
+        assertEquals(expectedConfigureOnDemand, startParameter.isConfigureOnDemand());
+        assertEquals(expectedMaxWorkersCount, startParameter.getMaxWorkerCount());
+    }
+
+    @Test
+    public void withSpecifiedGradleUserHomeDirectory() {
+        expectedGradleUserHome = testDir.file("home");
+        checkConversion("-g", expectedGradleUserHome.getAbsolutePath());
+
+        expectedGradleUserHome = currentDir.file("home");
+        checkConversion("-g", "home");
+    }
+
+    @Test
+    public void withSpecifiedProjectCacheDir() {
+        expectedProjectCacheDir = new File(currentDir, ".foo");
+        checkConversion("--project-cache-dir", ".foo");
+    }
+
+    @Test
+    public void withSpecifiedProjectDirectory() {
+        expectedCurrentDir = testDir.file("project-dir");
+        expectedProjectDir = expectedCurrentDir;
+        checkConversion("-p", expectedCurrentDir.getAbsolutePath());
+
+        expectedCurrentDir = currentDir.file("project-dir");
+        expectedProjectDir = expectedCurrentDir;
+        checkConversion("-p", "project-dir");
+    }
+
+    @Test
+    public void withSpecifiedBuildFileName() throws IOException {
+        expectedBuildFile = testDir.file("somename");
+        expectedCurrentDir = expectedBuildFile.getParentFile();
+        expectedProjectDir = expectedCurrentDir;
+        checkConversion("-b", expectedBuildFile.getAbsolutePath());
+
+        expectedBuildFile = currentDir.file("somename");
+        expectedCurrentDir = expectedBuildFile.getParentFile();
+        expectedProjectDir = expectedCurrentDir;
+        checkConversion("-b", "somename");
+    }
+
+    @Test
+    public void withSpecifiedSettingsFileName() throws IOException {
+        File expectedSettingsFile = currentDir.file("somesettings");
+        expectedCurrentDir = expectedSettingsFile.getParentFile();
+
+        checkConversion("-c", "somesettings");
+
+        assertThat(actualStartParameter.getSettingsFile(), equalTo(expectedSettingsFile));
+    }
+
+    @Test
+    public void withInitScripts() {
+        File script1 = currentDir.file("init1.gradle");
+        expectedInitScripts.add(script1);
+        checkConversion("-Iinit1.gradle");
+
+        File script2 = currentDir.file("init2.gradle");
+        expectedInitScripts.add(script2);
+        checkConversion("-Iinit1.gradle", "-Iinit2.gradle");
+    }
+
+    @Test
+    public void withSystemProperties() {
+        final String prop1 = "gradle.prop1";
+        final String valueProp1 = "value1";
+        final String prop2 = "gradle.prop2";
+        final String valueProp2 = "value2";
+        expectedSystemProperties = toMap(prop1, valueProp1);
+        expectedSystemProperties.put(prop2, valueProp2);
+        checkConversion("-D", prop1 + "=" + valueProp1, "-D", prop2 + "=" + valueProp2);
+    }
+
+    @Test
+    public void withSpecifiedGradleUserHomeDirectoryBySystemProperty() {
+        expectedGradleUserHome = testDir.file("home");
+        String propName = "gradle.user.home";
+        String propValue = expectedGradleUserHome.getAbsolutePath();
+        expectedSystemProperties = toMap(propName, propValue);
+        checkConversion("-D", propName + "=" + propValue);
+    }
+
+    @Test
+    public void privilegeCmdLineOptionOverSystemPrefForGradleUserHome() {
+        expectedGradleUserHome = testDir.file("home");
+        String propName = "gradle.user.home";
+        String propValue = "home2";
+        expectedSystemProperties = toMap(propName, propValue);
+        checkConversion("-D", propName + "=" + propValue, "-g", expectedGradleUserHome.getAbsolutePath());
+    }
+
+    @Test
+    public void withStartProperties() {
+        final String prop1 = "prop1";
+        final String valueProp1 = "value1";
+        final String prop2 = "prop2";
+        final String valueProp2 = "value2";
+        expectedProjectProperties = toMap(prop1, valueProp1);
+        expectedProjectProperties.put(prop2, valueProp2);
+        checkConversion("-P", prop1 + "=" + valueProp1, "-P", prop2 + "=" + valueProp2);
+    }
+
+    @Test
+    public void withTaskNames() {
+        expectedTaskNames = toList("a", "b");
+        checkConversion("a", "b");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUnknownCacheFlags() {
+        checkConversion("-C", "unknown");
+    }
+
+    @Test
+    public void withSearchUpwardsFlagSet() {
+        expectedSearchUpwards = false;
+        checkConversion("-u");
+    }
+
+    @Test
+    public void withShowFullStacktrace() {
+        expectedShowStackTrace = ShowStacktrace.ALWAYS_FULL;
+        checkConversion("-S");
+    }
+
+    @Test
+    public void withShowStacktrace() {
+        expectedShowStackTrace = ShowStacktrace.ALWAYS;
+        checkConversion("-s");
+    }
+
+    @Test
+    public void withRerunTasks() {
+        expectedRerunTasks = true;
+        checkConversion("--rerun-tasks");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withShowStacktraceAndShowFullStacktraceShouldThrowCommandLineArgumentEx() {
+        checkConversion("-sf");
+    }
+
+    @Test
+    public void withDryRunFlagSet() {
+        expectedDryRun = true;
+        checkConversion("-m");
+    }
+
+    @Test
+    public void withExcludeTask() {
+        expectedExcludedTasks.add("excluded");
+        checkConversion("-x", "excluded");
+        expectedExcludedTasks.add("excluded2");
+        checkConversion("-x", "excluded", "-x", "excluded2");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingNoSearchUpwardsOption() {
+        checkConversion("-e", "someScript", "-u", "clean");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingSpecifyBuildFileOption() {
+        checkConversion("-e", "someScript", "-bsomeFile", "clean");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingSpecifySettingsFileOption() {
+        checkConversion("-e", "someScript", "-csomeFile", "clean");
+    }
+
+    @Test
+    public void withNoProjectDependencyRebuild() {
+        buildProjectDependencies = false;
+        checkConversion("-a");
+    }
+
+    @Test
+    public void withQuietLoggingOptions() {
+        expectedLogLevel = LogLevel.QUIET;
+        checkConversion("-q");
+    }
+
+    @Test
+    public void withInfoLoggingOptions() {
+        expectedLogLevel = LogLevel.INFO;
+        checkConversion("-i");
+    }
+
+    @Test
+    public void withDebugLoggingOptions() {
+        expectedLogLevel = LogLevel.DEBUG;
+        checkConversion("-d");
+    }
+
+    @Test
+    public void withNoColor() {
+        expectedColorOutput = false;
+        expectedConsoleOutput = ConsoleOutput.Plain;
+        checkConversion("--no-color");
+    }
+
+    @Test
+    public void withColor() {
+        expectedConsoleOutput = ConsoleOutput.Rich;
+        checkConversion("--console", "rich");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withLowerPParameterWithoutArgument() {
+        checkConversion("-p");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withAParameterWithoutArgument() {
+        checkConversion("-A");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUpperAAndLowerAParameter() {
+        checkConversion("-a", "-Atask1");
+    }
+
+    @Test
+    public void withProfile() {
+        expectedProfile = true;
+        checkConversion("--profile");
+    }
+
+    @Test
+    public void withContinue() {
+        expectedContinue = true;
+        checkConversion("--continue");
+    }
+
+    @Test
+    public void withOffline() {
+        expectedOffline = true;
+        checkConversion("--offline");
+        checkConversion("-offline");
+    }
+
+    @Test
+    public void withRefreshDependencies() {
+        expectedRefreshDependencies = true;
+        checkConversion("--refresh-dependencies");
+        checkConversion("-refresh-dependencies");
+    }
+
+    @Test
+    public void withRecompileScripts() {
+        expectedRecompileScripts = true;
+        checkConversion("--recompile-scripts");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUnknownOption() {
+        checkConversion("--unknown");
+    }
+
+    @Test
+    public void withTaskAndTaskOption() {
+        expectedTaskNames = toList("someTask", "--some-task-option");
+        checkConversion("someTask", "--some-task-option");
+    }
+
+    @Test
+    public void withParallelExecutor() {
+        expectedParallelProjectExecution = true;
+        expectedParallelExecutorCount = expectedMaxWorkersCount;
+        checkConversion("--parallel");
+    }
+
+    @Test
+    public void withParallelExecutorThreads() {
+        expectedParallelProjectExecution = true;
+        expectedMaxWorkersCount = expectedParallelExecutorCount = 5;
+        checkConversion("--parallel-threads", "5");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withInvalidParallelExecutorThreads() {
+        checkConversion("--parallel-threads", "foo");
+    }
+
+
+    @Test
+    public void withMaxWorkers() {
+        expectedMaxWorkersCount = 5;
+        checkConversion("--max-workers", "5");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withInvalidMaxWorkers() {
+        checkConversion("--max-workers", "foo");
+    }
+
+
+    @Test
+    public void withConfigureOnDemand() {
+        expectedConfigureOnDemand = true;
+        checkConversion("--configure-on-demand");
+    }
+
+    final static int NUM_OF_PROCS = Runtime.getRuntime().availableProcessors()
+    final static int N = 3
+    final static int M = 5
+
+    @Test
+    @Unroll("check combinations using #args")
+    public void checkCombinationsOfWorkersAndParallelOptions(List args, int maxWorkers, int parallelThreads, boolean isParallel) {
+        given:
+        expectedMaxWorkersCount = maxWorkers
+        expectedParallelExecutorCount = parallelThreads
+        expectedParallelProjectExecution = isParallel
+
+        expect:
+        checkConversion(*args)
+
+        where:
+        args                                          | maxWorkers   | parallelThreads | isParallel
+        []                                            | NUM_OF_PROCS | 0               | false
+        ["--parallel"]                                | NUM_OF_PROCS | NUM_OF_PROCS    | true
+        ["--parallel-threads=$N"]                     | N            | N               | true
+        ["--max-workers=$N"]                          | N            | 0               | false
+        ["--parallel", "--max-workers=$N"]            | N            | N               | true
+        ["--parallel-threads=$N", "--max-workers=$M"] | M            | M               | true
+        ["--max-workers=$N", "--parallel-threads=$M"] | M            | M               | true
+        ["--parallel-threads=-1"]                     | NUM_OF_PROCS | NUM_OF_PROCS    | true
+        ["--parallel-threads=0"]                      | NUM_OF_PROCS | 0               | false
+        ["--parallel-threads=1"]                      | 1            | 1               | true
+        ["--parallel", "--max-workers=1"]             | 1            | 1               | true
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
deleted file mode 100644
index 231e265..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.initialization;
-
-import org.gradle.CacheUsage;
-import org.gradle.RefreshOptions;
-import org.gradle.StartParameter;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.cli.CommandLineArgumentException;
-import org.gradle.logging.ShowStacktrace;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-
-import static java.util.Arrays.asList;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class DefaultCommandLineConverterTest {
-    @Rule
-    public TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider();
-
-    private TestFile currentDir = testDir.file("current-dir");
-    private File expectedBuildFile;
-    private File expectedGradleUserHome = StartParameter.DEFAULT_GRADLE_USER_HOME;
-    private File expectedProjectDir = currentDir;
-    private List<String> expectedTaskNames = toList();
-    private Set<String> expectedExcludedTasks = toSet();
-    private boolean buildProjectDependencies = true;
-    private Map<String, String> expectedSystemProperties = new HashMap<String, String>();
-    private Map<String, String> expectedProjectProperties = new HashMap<String, String>();
-    private List<File> expectedInitScripts = new ArrayList<File>();
-    private CacheUsage expectedCacheUsage = CacheUsage.ON;
-    private boolean expectedSearchUpwards = true;
-    private boolean expectedDryRun;
-    private ShowStacktrace expectedShowStackTrace = ShowStacktrace.INTERNAL_EXCEPTIONS;
-    private LogLevel expectedLogLevel = LogLevel.LIFECYCLE;
-    private boolean expectedColorOutput = true;
-    private StartParameter actualStartParameter;
-    private boolean expectedProfile;
-    private File expectedProjectCacheDir;
-    private boolean expectedRefreshDependencies;
-    private boolean expectedRerunTasks;
-    private final DefaultCommandLineConverter commandLineConverter = new DefaultCommandLineConverter();
-    private boolean expectedContinue;
-    private boolean expectedOffline;
-    private RefreshOptions expectedRefreshOptions = RefreshOptions.NONE;
-    private boolean expectedRecompileScripts;
-    private int expectedParallelExecutorCount;
-    private boolean expectedConfigureOnDemand;
-
-    @Test
-    public void withoutAnyOptions() {
-        checkConversion();
-    }
-
-    private void checkConversion(String... args) {
-        actualStartParameter = new StartParameter();
-        actualStartParameter.setCurrentDir(currentDir);
-        commandLineConverter.convert(asList(args), actualStartParameter);
-        // We check the params passed to the build factory
-        checkStartParameter(actualStartParameter);
-    }
-
-    private void checkStartParameter(StartParameter startParameter) {
-        assertEquals(expectedBuildFile, startParameter.getBuildFile());
-        assertEquals(expectedTaskNames, startParameter.getTaskNames());
-        assertEquals(buildProjectDependencies, startParameter.isBuildProjectDependencies());
-        assertEquals(expectedProjectDir.getAbsoluteFile(), startParameter.getCurrentDir().getAbsoluteFile());
-        assertEquals(expectedCacheUsage, startParameter.getCacheUsage());
-        assertEquals(expectedSearchUpwards, startParameter.isSearchUpwards());
-        assertEquals(expectedProjectProperties, startParameter.getProjectProperties());
-        assertEquals(expectedSystemProperties, startParameter.getSystemPropertiesArgs());
-        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
-        assertEquals(expectedLogLevel, startParameter.getLogLevel());
-        assertEquals(expectedColorOutput, startParameter.isColorOutput());
-        assertEquals(expectedDryRun, startParameter.isDryRun());
-        assertEquals(expectedShowStackTrace, startParameter.getShowStacktrace());
-        assertEquals(expectedExcludedTasks, startParameter.getExcludedTaskNames());
-        assertEquals(expectedInitScripts, startParameter.getInitScripts());
-        assertEquals(expectedProfile, startParameter.isProfile());
-        assertEquals(expectedContinue, startParameter.isContinueOnFailure());
-        assertEquals(expectedOffline, startParameter.isOffline());
-        assertEquals(expectedRecompileScripts, startParameter.isRecompileScripts());
-        assertEquals(expectedRerunTasks, startParameter.isRerunTasks());
-        assertEquals(expectedRefreshOptions, startParameter.getRefreshOptions());
-        assertEquals(expectedRefreshDependencies, startParameter.isRefreshDependencies());
-        assertEquals(expectedProjectCacheDir, startParameter.getProjectCacheDir());
-        assertEquals(expectedParallelExecutorCount, startParameter.getParallelThreadCount());
-        assertEquals(expectedConfigureOnDemand, startParameter.isConfigureOnDemand());
-    }
-
-    @Test
-    public void withSpecifiedGradleUserHomeDirectory() {
-        expectedGradleUserHome = testDir.file("home");
-        checkConversion("-g", expectedGradleUserHome.getAbsolutePath());
-
-        expectedGradleUserHome = currentDir.file("home");
-        checkConversion("-g", "home");
-    }
-
-    @Test
-    public void withSpecifiedProjectCacheDir() {
-        expectedProjectCacheDir = new File(currentDir, ".foo");
-        checkConversion("--project-cache-dir", ".foo");
-    }
-
-    @Test
-    public void withSpecifiedProjectDirectory() {
-        expectedProjectDir = testDir.file("project-dir");
-        checkConversion("-p", expectedProjectDir.getAbsolutePath());
-
-        expectedProjectDir = currentDir.file("project-dir");
-        checkConversion("-p", "project-dir");
-    }
-
-    @Test
-    public void withSpecifiedBuildFileName() throws IOException {
-        expectedBuildFile = testDir.file("somename");
-        expectedProjectDir = expectedBuildFile.getParentFile();
-        checkConversion("-b", expectedBuildFile.getAbsolutePath());
-
-        expectedBuildFile = currentDir.file("somename");
-        expectedProjectDir = expectedBuildFile.getParentFile();
-        checkConversion("-b", "somename");
-    }
-
-    @Test
-    public void withSpecifiedSettingsFileName() throws IOException {
-        File expectedSettingsFile = currentDir.file("somesettings");
-        expectedProjectDir = expectedSettingsFile.getParentFile();
-
-        checkConversion("-c", "somesettings");
-
-        assertThat(actualStartParameter.getSettingsFile(), equalTo(expectedSettingsFile));
-    }
-
-    @Test
-    public void withInitScripts() {
-        File script1 = currentDir.file("init1.gradle");
-        expectedInitScripts.add(script1);
-        checkConversion("-Iinit1.gradle");
-
-        File script2 = currentDir.file("init2.gradle");
-        expectedInitScripts.add(script2);
-        checkConversion("-Iinit1.gradle", "-Iinit2.gradle");
-    }
-    
-    @Test
-    public void withSystemProperties() {
-        final String prop1 = "gradle.prop1";
-        final String valueProp1 = "value1";
-        final String prop2 = "gradle.prop2";
-        final String valueProp2 = "value2";
-        expectedSystemProperties = toMap(prop1, valueProp1);
-        expectedSystemProperties.put(prop2, valueProp2);
-        checkConversion("-D", prop1 + "=" + valueProp1, "-D", prop2 + "=" + valueProp2);
-    }
-    
-    @Test
-    public void withSpecifiedGradleUserHomeDirectoryBySystemProperty() {
-        expectedGradleUserHome = testDir.file("home");
-        String propName = "gradle.user.home";
-        String propValue = expectedGradleUserHome.getAbsolutePath();
-        expectedSystemProperties = toMap(propName, propValue);
-        checkConversion("-D", propName+"="+propValue);
-    }
-
-    @Test
-    public void privilegeCmdLineOptionOverSystemPrefForGradleUserHome() {
-        expectedGradleUserHome = testDir.file("home");
-        String propName = "gradle.user.home";
-        String propValue = "home2";
-        expectedSystemProperties = toMap(propName, propValue);
-        checkConversion("-D", propName+"="+propValue, "-g", expectedGradleUserHome.getAbsolutePath());
-    }
-
-    @Test
-    public void withStartProperties() {
-        final String prop1 = "prop1";
-        final String valueProp1 = "value1";
-        final String prop2 = "prop2";
-        final String valueProp2 = "value2";
-        expectedProjectProperties = toMap(prop1, valueProp1);
-        expectedProjectProperties.put(prop2, valueProp2);
-        checkConversion("-P", prop1 + "=" + valueProp1, "-P", prop2 + "=" + valueProp2);
-    }
-
-    @Test
-    public void withTaskNames() {
-        expectedTaskNames = toList("a", "b");
-        checkConversion("a", "b");
-    }
-
-    @Test
-    public void withRebuildCacheFlagSet() {
-        expectedCacheUsage = CacheUsage.REBUILD;
-        checkConversion("-C", "rebuild");
-    }
-
-    @Test
-    public void withCacheOnFlagSet() {
-        checkConversion("-C", "on");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUnknownCacheFlags() {
-        checkConversion("-C", "unknown");
-    }
-
-    @Test
-    public void withSearchUpwardsFlagSet() {
-        expectedSearchUpwards = false;
-        checkConversion("-u");
-    }
-
-    @Test
-    public void withShowFullStacktrace() {
-        expectedShowStackTrace = ShowStacktrace.ALWAYS_FULL;
-        checkConversion("-S");
-    }
-
-    @Test
-    public void withShowStacktrace() {
-        expectedShowStackTrace = ShowStacktrace.ALWAYS;
-        checkConversion("-s");
-    }
-
-    @Test
-    public void withRerunTasks() {
-        expectedRerunTasks = true;
-        checkConversion("--rerun-tasks");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withShowStacktraceAndShowFullStacktraceShouldThrowCommandLineArgumentEx() {
-        checkConversion("-sf");
-    }
-
-    @Test
-    public void withDryRunFlagSet() {
-        expectedDryRun = true;
-        checkConversion("-m");
-    }
-
-    @Test
-    public void withExcludeTask() {
-        expectedExcludedTasks.add("excluded");
-        checkConversion("-x", "excluded");
-        expectedExcludedTasks.add("excluded2");
-        checkConversion("-x", "excluded", "-x", "excluded2");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingNoSearchUpwardsOption() {
-        checkConversion("-e", "someScript", "-u", "clean");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingSpecifyBuildFileOption() {
-        checkConversion("-e", "someScript", "-bsomeFile", "clean");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingSpecifySettingsFileOption() {
-        checkConversion("-e", "someScript", "-csomeFile", "clean");
-    }
-
-    @Test
-    public void withNoProjectDependencyRebuild() {
-        buildProjectDependencies = false;
-        checkConversion("-a");
-    }
-
-    @Test
-    public void withQuietLoggingOptions() {
-        expectedLogLevel = LogLevel.QUIET;
-        checkConversion("-q");
-    }
-
-    @Test
-    public void withInfoLoggingOptions() {
-        expectedLogLevel = LogLevel.INFO;
-        checkConversion("-i");
-    }
-
-    @Test
-    public void withDebugLoggingOptions() {
-        expectedLogLevel = LogLevel.DEBUG;
-        checkConversion("-d");
-    }
-
-    @Test
-    public void withNoColor() {
-        expectedColorOutput = false;
-        checkConversion("--no-color");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withLowerPParameterWithoutArgument() {
-        checkConversion("-p");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withAParameterWithoutArgument() {
-        checkConversion("-A");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUpperAAndLowerAParameter() {
-        checkConversion("-a", "-Atask1");
-    }
-
-    @Test
-    public void withProfile() {
-        expectedProfile = true;
-        checkConversion("--profile");
-    }
-
-    @Test
-    public void withContinue() {
-        expectedContinue = true;
-        checkConversion("--continue");
-    }
-
-    @Test
-    public void withOffline() {
-        expectedOffline = true;
-        checkConversion("--offline");
-        checkConversion("-offline");
-    }
-
-    @Test
-    public void withRefreshDependencies() {
-        expectedRefreshDependencies = true;
-        expectedRefreshOptions = new RefreshOptions(asList(RefreshOptions.Option.DEPENDENCIES));
-        checkConversion("--refresh-dependencies");
-        checkConversion("-refresh-dependencies");
-    }
-
-    @Test
-    public void withRecompileScripts() {
-        expectedRecompileScripts = true;
-        checkConversion("--recompile-scripts");
-    }
-
-    @Test
-    public void withRefreshDependenciesSet() {
-        expectedRefreshDependencies = true;
-        expectedRefreshOptions = new RefreshOptions(Arrays.asList(RefreshOptions.Option.DEPENDENCIES));
-        checkConversion("--refresh", "dependencies");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUnknownRefreshOption() {
-        checkConversion("--refresh", "unknown");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUnknownOption() {
-        checkConversion("--unknown");
-    }
-
-    @Test
-    public void withTaskAndTaskOption() {
-        expectedTaskNames = toList("someTask", "--some-task-option");
-        checkConversion("someTask", "--some-task-option");
-    }
-
-    @Test
-    public void withParallelExecutor() {
-        expectedParallelExecutorCount = -1;
-        checkConversion("--parallel");
-    }
-
-    @Test
-    public void withParallelExecutorThreads() {
-        expectedParallelExecutorCount = 5;
-        checkConversion("--parallel-threads", "5");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withInvalidParallelExecutorThreads() {
-        checkConversion("--parallel-threads", "foo");
-    }
-
-    @Test
-    public void withConfigureOnDemand() {
-        expectedConfigureOnDemand = true;
-        checkConversion("--configure-on-demand");
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
index c14dc08..4e9ffec 100755
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultExceptionAnalyserTest.java
@@ -22,7 +22,7 @@ import org.gradle.internal.exceptions.MultiCauseException;
 import org.gradle.api.tasks.TaskExecutionException;
 import org.gradle.groovy.scripts.Script;
 import org.gradle.groovy.scripts.ScriptSource;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -47,6 +47,8 @@ public class DefaultExceptionAnalyserTest {
     private final StackTraceElement element = new StackTraceElement("class", "method", "filename", 7);
     private final StackTraceElement callerElement = new StackTraceElement("class", "method", "filename", 11);
     private final StackTraceElement otherElement = new StackTraceElement("class", "method", "otherfile", 11);
+    private final StackTraceElement elementWithNoSourceFile = new StackTraceElement("class", "method", null, 11);
+    private final StackTraceElement elementWithNoLineNumber = new StackTraceElement("class", "method", "filename", -1);
     private final ScriptSource source = context.mock(ScriptSource.class);
 
     @Before
@@ -54,6 +56,8 @@ public class DefaultExceptionAnalyserTest {
         context.checking(new Expectations() {{
             allowing(source).getFileName();
             will(returnValue("filename"));
+            allowing(source).getDisplayName();
+            will(returnValue("build file filename"));
         }});
     }
 
@@ -100,9 +104,9 @@ public class DefaultExceptionAnalyserTest {
     }
 
     @Test
-    public void addsLocationInfoFromDeepestStackFrame() {
+    public void addsLocationInfoFromDeepestStackFrameWithMatchingSourceFileAndLineInformation() {
         Throwable failure = new ContextualException();
-        failure.setStackTrace(toArray(element, otherElement, callerElement));
+        failure.setStackTrace(toArray(elementWithNoSourceFile, elementWithNoLineNumber, otherElement, element, callerElement));
 
         DefaultExceptionAnalyser analyser = analyser();
         notifyAnalyser(analyser, source);
@@ -111,7 +115,7 @@ public class DefaultExceptionAnalyserTest {
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
 
         LocationAwareException gse = (LocationAwareException) transformedFailure;
-        assertThat(gse.getScriptSource(), sameInstance(source));
+        assertThat(gse.getSourceDisplayName(), equalTo(source.getDisplayName()));
         assertThat(gse.getLineNumber(), equalTo(7));
     }
 
@@ -129,7 +133,7 @@ public class DefaultExceptionAnalyserTest {
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
 
         LocationAwareException gse = (LocationAwareException) transformedFailure;
-        assertThat(gse.getScriptSource(), sameInstance(source));
+        assertThat(gse.getSourceDisplayName(), equalTo(source.getDisplayName()));
         assertThat(gse.getLineNumber(), equalTo(7));
     }
 
@@ -140,7 +144,7 @@ public class DefaultExceptionAnalyserTest {
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
 
         LocationAwareException gse = (LocationAwareException) transformedFailure;
-        assertThat(gse.getScriptSource(), nullValue());
+        assertThat(gse.getSourceDisplayName(), nullValue());
         assertThat(gse.getLineNumber(), nullValue());
     }
 
@@ -224,7 +228,7 @@ public class DefaultExceptionAnalyserTest {
         assertThat(transformedFailure, instanceOf(LocationAwareException.class));
 
         LocationAwareException gse = (LocationAwareException) transformedFailure;
-        assertThat(gse.getScriptSource(), sameInstance(source));
+        assertThat(gse.getSourceDisplayName(), equalTo(source.getDisplayName()));
         assertThat(gse.getLineNumber(), equalTo(7));
         assertThat(gse.getCause(), sameInstance(failure));
     }
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 933fae0..ca3e369 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.groovy
@@ -15,65 +15,59 @@
  */
 package org.gradle.initialization
 
-import org.gradle.GradleLauncher
 import org.gradle.StartParameter
-import org.gradle.cli.CommandLineConverter
-import org.gradle.internal.nativeplatform.services.NativeServices
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.internal.service.scopes.GlobalScopeServices
 import org.gradle.logging.LoggingServiceRegistry
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import spock.lang.Specification
 
 class DefaultGradleLauncherFactoryTest extends Specification {
-    final CommandLineConverter<StartParameter> parameterConverter = Mock()
-    final ServiceRegistry sharedServices = new DefaultServiceRegistry(LoggingServiceRegistry.newEmbeddableLogging(), NativeServices.getInstance()).addProvider(new GlobalScopeServices(false))
+    final ServiceRegistry sharedServices = new DefaultServiceRegistry(LoggingServiceRegistry.newEmbeddableLogging(), NativeServicesTestFixture.getInstance()).addProvider(new GlobalScopeServices(false))
     final DefaultGradleLauncherFactory factory = new DefaultGradleLauncherFactory(sharedServices)
 
-    def setup() {
-        factory.setCommandLineConverter(parameterConverter);
-    }
-
-    def cleanup() {
-        GradleLauncher.injectCustomFactory(null);
-    }
-
-    def registersSelfWithGradleLauncher() {
-        StartParameter startParameter = new StartParameter();
-
-        when:
-        def result = GradleLauncher.createStartParameter('a')
-
-        then:
-        result == startParameter
-        1 * parameterConverter.convert(['a']) >> startParameter
-    }
-
-    def newInstanceWithStartParameterAndRequestMetaData() {
-        StartParameter startParameter = new StartParameter();
-        BuildRequestMetaData metaData = new DefaultBuildRequestMetaData(System.currentTimeMillis());
+    def "makes services from build context available as build scoped services"() {
+        def startParameter = new StartParameter()
+        def cancellationToken = Stub(BuildCancellationToken)
+        def eventConsumer = Stub(BuildEventConsumer)
+        def requestContext = Stub(BuildRequestContext) {
+            getCancellationToken() >> cancellationToken
+            getEventConsumer() >> eventConsumer
+        }
 
         expect:
-        def launcher = factory.newInstance(startParameter, metaData)
+        def launcher = factory.newInstance(startParameter, requestContext)
         launcher.gradle.parent == null
         launcher.gradle.startParameter == startParameter
-        launcher.gradle.services.get(BuildRequestMetaData) == metaData
+        launcher.gradle.services.get(BuildRequestMetaData) == requestContext
+        launcher.gradle.services.get(BuildCancellationToken) == cancellationToken
+        launcher.gradle.services.get(BuildEventConsumer) == eventConsumer
     }
 
-    def newInstanceWithStartParameterWhenNoBuildRunning() {
-        StartParameter startParameter = new StartParameter();
+    def "provides default build context when no outer build is running"() {
+        def startParameter = new StartParameter()
 
         expect:
         def launcher = factory.newInstance(startParameter)
         launcher.gradle.parent == null
         launcher.gradle.services.get(BuildRequestMetaData) instanceof DefaultBuildRequestMetaData
+        launcher.gradle.services.get(BuildCancellationToken) instanceof FixedBuildCancellationToken
+        launcher.gradle.services.get(BuildEventConsumer) instanceof NoOpBuildEventConsumer
     }
 
-    def newInstanceWithStartParameterWhenBuildRunning() {
-        StartParameter startParameter = new StartParameter();
-        BuildClientMetaData clientMetaData = Mock()
-        BuildRequestMetaData requestMetaData = new DefaultBuildRequestMetaData(clientMetaData, 90)
-        DefaultGradleLauncher parent = factory.newInstance(startParameter, requestMetaData);
+    def "reuses build context services for nested build"() {
+        def startParameter = new StartParameter()
+        def cancellationToken = Stub(BuildCancellationToken)
+        def clientMetaData = Stub(BuildClientMetaData)
+        def eventConsumer = Stub(BuildEventConsumer)
+        def requestContext = Stub(BuildRequestContext) {
+            getCancellationToken() >> cancellationToken
+            getClient() >> clientMetaData
+            getEventConsumer() >> eventConsumer
+        }
+
+        def parent = factory.newInstance(startParameter, requestContext);
         parent.buildListener.buildStarted(parent.gradle)
 
         expect:
@@ -81,19 +75,9 @@ class DefaultGradleLauncherFactoryTest extends Specification {
         launcher.gradle.parent == parent.gradle
         def request = launcher.gradle.services.get(BuildRequestMetaData)
         request instanceof DefaultBuildRequestMetaData
-        request != requestMetaData
+        request != requestContext
         request.client == clientMetaData
+        launcher.gradle.services.get(BuildCancellationToken) == cancellationToken
+        launcher.gradle.services.get(BuildEventConsumer) == eventConsumer
     }
-
-    def createStartParameter() {
-        StartParameter startParameter = new StartParameter();
-
-        when:
-        def result = factory.createStartParameter('a')
-
-        then:
-        result == startParameter
-        1 * parameterConverter.convert(['a']) >> startParameter
-    }
-
 }
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 57138da..fe005db 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
@@ -18,7 +18,6 @@ package org.gradle.initialization;
 
 import org.gradle.BuildListener;
 import org.gradle.BuildResult;
-import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.ExceptionAnalyser;
@@ -40,13 +39,15 @@ import org.hamcrest.Matcher;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.Closeable;
 import java.io.File;
+import java.io.IOException;
 
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertThat;
 
 @RunWith(org.jmock.integration.junit4.JMock.class)
@@ -69,16 +70,17 @@ public class DefaultGradleLauncherTest {
 
     private ProjectDescriptor expectedRootProjectDescriptor;
 
+    private ProjectDescriptor expectedDefaultProjectDescriptor;
+
     private JUnit4Mockery context = new JUnit4GroovyMockery();
 
-    private ClassLoaderScope settingsClassLoaderScope = context.mock(ClassLoaderScope.class);
-    private ClassLoaderScope rootProjectClassLoaderScope = context.mock(ClassLoaderScope.class);
+    private ClassLoaderScope baseClassLoaderScope = context.mock(ClassLoaderScope.class);
     private ExceptionAnalyser exceptionAnalyserMock = context.mock(ExceptionAnalyser.class);
     private LoggingManagerInternal loggingManagerMock = context.mock(LoggingManagerInternal.class);
     private ModelConfigurationListener modelListenerMock = context.mock(ModelConfigurationListener.class);
     private TasksCompletionListener tasksCompletionListener = context.mock(TasksCompletionListener.class);
-
-    @Rule
+    private BuildCompletionListener buildCompletionListener = context.mock(BuildCompletionListener.class);
+    private Closeable buildServices = context.mock(Closeable.class);
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
 
     @Before
@@ -100,6 +102,8 @@ public class DefaultGradleLauncherTest {
         expectedRootProjectDescriptor = new DefaultProjectDescriptor(null, "someName", new File("somedir"), new DefaultProjectDescriptorRegistry(),
                 TestFiles.resolver(expectedRootDir));
         expectedRootProject = TestUtil.createRootProject(expectedRootDir);
+        expectedDefaultProjectDescriptor = new DefaultProjectDescriptor(null, "default", new File("default"), new DefaultProjectDescriptorRegistry(),
+                TestFiles.resolver(expectedCurrentDir));
         expectedCurrentProject = TestUtil.createRootProject(expectedCurrentDir);
 
         expectedStartParams = new StartParameter();
@@ -109,16 +113,16 @@ public class DefaultGradleLauncherTest {
 
         gradleLauncher = new DefaultGradleLauncher(gradleMock, initscriptHandlerMock, settingsHandlerMock,
                 buildLoaderMock, buildConfigurerMock, buildBroadcaster, exceptionAnalyserMock, loggingManagerMock,
-                modelListenerMock, tasksCompletionListener, buildExecuter);
+                modelListenerMock, tasksCompletionListener, buildExecuter, buildCompletionListener, buildServices);
 
         context.checking(new Expectations() {
             {
                 allowing(settingsMock).getRootProject();
                 will(returnValue(expectedRootProjectDescriptor));
-                allowing(settingsMock).getClassLoaderScope();
-                will(returnValue(settingsClassLoaderScope));
-                allowing(settingsClassLoaderScope).createSibling();
-                will(returnValue(rootProjectClassLoaderScope));
+                allowing(settingsMock).getDefaultProject();
+                will(returnValue(expectedRootProjectDescriptor));
+                allowing(settingsMock).getRootClassLoaderScope();
+                will(returnValue(baseClassLoaderScope));
                 allowing(gradleMock).getRootProject();
                 will(returnValue(expectedRootProject));
                 allowing(gradleMock).getDefaultProject();
@@ -133,7 +137,7 @@ public class DefaultGradleLauncherTest {
 
     @Test
     public void testRun() {
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         expectDagBuilt();
@@ -145,25 +149,13 @@ public class DefaultGradleLauncherTest {
     }
 
     @Test
-    public void testGetBuildAndRunAnalysis() {
-        expectLoggingStartedAndStoped();
-        expectInitScriptsExecuted();
-        expectSettingsBuilt();
-        expectDagBuilt();
-        expectBuildListenerCallbacks();
-        BuildResult buildResult = gradleLauncher.getBuildAndRunAnalysis();
-        assertThat(buildResult.getGradle(), sameInstance((Object) gradleMock));
-        assertThat(buildResult.getFailure(), nullValue());
-    }
-
-    @Test
     public void testGetBuildAnalysis() {
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         expectBuildListenerCallbacks();
         context.checking(new Expectations() {{
-            one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, rootProjectClassLoaderScope);
+            one(buildLoaderMock).load(expectedRootProjectDescriptor, expectedDefaultProjectDescriptor, gradleMock, baseClassLoaderScope);
             one(buildConfigurerMock).configure(gradleMock);
         }});
         BuildResult buildResult = gradleLauncher.getBuildAnalysis();
@@ -175,12 +167,12 @@ public class DefaultGradleLauncherTest {
     public void testGetBuildAnalysisWithFailure() {
         final RuntimeException exception = new RuntimeException();
         final RuntimeException transformedException = new RuntimeException();
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         context.checking(new Expectations() {{
             one(buildBroadcaster).buildStarted(gradleMock);
-            one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, rootProjectClassLoaderScope);
+            one(buildLoaderMock).load(expectedRootProjectDescriptor, expectedDefaultProjectDescriptor, gradleMock, baseClassLoaderScope);
             will(throwException(exception));
             one(exceptionAnalyserMock).transform(exception);
             will(returnValue(transformedException));
@@ -193,12 +185,12 @@ public class DefaultGradleLauncherTest {
 
     @Test
     public void testNotifiesListenerOfBuildAnalysisStages() {
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         expectBuildListenerCallbacks();
         context.checking(new Expectations() {{
-            one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, rootProjectClassLoaderScope);
+            one(buildLoaderMock).load(expectedRootProjectDescriptor, expectedDefaultProjectDescriptor, gradleMock, baseClassLoaderScope);
             one(buildConfigurerMock).configure(gradleMock);
         }});
 
@@ -207,7 +199,7 @@ public class DefaultGradleLauncherTest {
 
     @Test
     public void testNotifiesListenerOfBuildStages() {
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         expectDagBuilt();
@@ -221,7 +213,7 @@ public class DefaultGradleLauncherTest {
     public void testNotifiesListenerOnSettingsInitWithFailure() {
         final RuntimeException failure = new RuntimeException();
         final RuntimeException transformedException = new RuntimeException();
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         context.checking(new Expectations() {{
             one(buildBroadcaster).buildStarted(gradleMock);
@@ -240,7 +232,7 @@ public class DefaultGradleLauncherTest {
     public void testNotifiesListenerOnBuildCompleteWithFailure() {
         final RuntimeException failure = new RuntimeException();
         final RuntimeException transformedException = new RuntimeException();
-        expectLoggingStartedAndStoped();
+        expectLoggingStarted();
         expectInitScriptsExecuted();
         expectSettingsBuilt();
         expectDagBuilt();
@@ -259,10 +251,20 @@ public class DefaultGradleLauncherTest {
         assertThat(buildResult.getFailure(), sameInstance((Throwable) transformedException));
     }
 
-    private void expectLoggingStartedAndStoped() {
+    @Test
+    public void testCleansUpOnStop() throws IOException {
         context.checking(new Expectations() {{
-            one(loggingManagerMock).start();
             one(loggingManagerMock).stop();
+            one(buildServices).close();
+            one(buildCompletionListener).completed();
+        }});
+
+        gradleLauncher.stop();
+    }
+
+    private void expectLoggingStarted() {
+        context.checking(new Expectations() {{
+            one(loggingManagerMock).start();
         }});
     }
 
@@ -295,7 +297,7 @@ public class DefaultGradleLauncherTest {
     private void expectDagBuilt() {
         context.checking(new Expectations() {
             {
-                one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, rootProjectClassLoaderScope);
+                one(buildLoaderMock).load(expectedRootProjectDescriptor, expectedDefaultProjectDescriptor, gradleMock, baseClassLoaderScope);
                 one(buildConfigurerMock).configure(gradleMock);
                 one(buildExecuter).select(gradleMock);
             }
@@ -320,14 +322,6 @@ public class DefaultGradleLauncherTest {
         });
     }
 
-    // todo: This test is rather weak. Make it stronger.
-    @Test
-    public void testNewInstanceFactory() {
-        StartParameter startParameter = new StartParameter();
-        GradleLauncher gradleLauncher = GradleLauncher.newInstance(startParameter);
-        assertThat(gradleLauncher, notNullValue());
-    }
-
     private Matcher<BuildResult> result(final Matcher<? extends Throwable> exceptionMatcher) {
         return new BaseMatcher<BuildResult>() {
             public void describeTo(Description description) {
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 aeef7f2..1c65e1c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSettingsTest.groovy
@@ -21,12 +21,12 @@ 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.AsmBackedClassGenerator
 import org.gradle.api.internal.GradleInternal
-import org.gradle.api.internal.ThreadGlobalInstantiator
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
-import org.gradle.api.plugins.PluginContainer
+import org.gradle.api.internal.plugins.DefaultPluginManager
 import org.gradle.configuration.ScriptPluginFactory
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.internal.service.ServiceRegistry
@@ -38,12 +38,15 @@ import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
+import java.lang.reflect.Type
+
 import static org.junit.Assert.*
 
 @RunWith(JMock)
 class DefaultSettingsTest {
     File settingsDir
     StartParameter startParameter
+    ClassLoaderScope rootClassLoaderScope
     ClassLoaderScope classLoaderScope
     Map gradleProperties
     ScriptSource scriptSourceMock
@@ -52,10 +55,10 @@ class DefaultSettingsTest {
     JUnit4GroovyMockery context = new JUnit4GroovyMockery()
     ProjectDescriptorRegistry projectDescriptorRegistry
     ServiceRegistryFactory serviceRegistryFactory
-    PluginContainer pluginContainer
     FileResolver fileResolver
     ScriptPluginFactory scriptPluginFactory
     ScriptHandlerFactory scriptHandlerFactory
+    DefaultPluginManager pluginManager
 
     @Before
     public void setUp() {
@@ -63,35 +66,37 @@ class DefaultSettingsTest {
         settingsDir = new File('/somepath/root').absoluteFile
         gradleProperties = [someGradleProp: 'someValue']
         startParameter = new StartParameter(currentDir: new File(settingsDir, 'current'), gradleUserHomeDir: new File('gradleUserHomeDir'))
+        rootClassLoaderScope = context.mock(ClassLoaderScope)
         classLoaderScope = context.mock(ClassLoaderScope)
+        pluginManager = context.mock(DefaultPluginManager)
 
         scriptSourceMock = context.mock(ScriptSource)
         gradleMock = context.mock(GradleInternal)
         serviceRegistryFactory = context.mock(ServiceRegistryFactory.class)
-        pluginContainer = context.mock(PluginContainer.class)
         scriptPluginFactory = context.mock(ScriptPluginFactory.class)
         scriptHandlerFactory = context.mock(ScriptHandlerFactory.class)
         fileResolver = context.mock(FileResolver.class)
         projectDescriptorRegistry = new DefaultProjectDescriptorRegistry()
 
         def settingsServices = context.mock(ServiceRegistry.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(ScriptHandlerFactory.class);
-                will(returnValue(scriptHandlerFactory));
-                one(settingsServices).get(ProjectDescriptorRegistry.class);
-                will(returnValue(projectDescriptorRegistry));
+        context.checking {
+            one(serviceRegistryFactory).createFor(with(any(Settings.class)));
+            will(returnValue(settingsServices));
+            allowing(settingsServices).get((Type)FileResolver.class);
+            will(returnValue(fileResolver));
+            allowing(settingsServices).get((Type)ScriptPluginFactory.class);
+            will(returnValue(scriptPluginFactory));
+            allowing(settingsServices).get((Type)ScriptHandlerFactory.class);
+            will(returnValue(scriptHandlerFactory));
+            allowing(settingsServices).get((Type)ProjectDescriptorRegistry.class);
+            will(returnValue(projectDescriptorRegistry));
+            allowing(settingsServices).get((Type)DefaultPluginManager.class);
+            will(returnValue(pluginManager));
         }
-        settings = ThreadGlobalInstantiator.orCreate.newInstance(DefaultSettings, serviceRegistryFactory,
-                    gradleMock, classLoaderScope, settingsDir, scriptSourceMock, startParameter);
 
+        AsmBackedClassGenerator classGenerator = new AsmBackedClassGenerator()
+        settings = classGenerator.newInstance(DefaultSettings, serviceRegistryFactory,
+                gradleMock, classLoaderScope, rootClassLoaderScope, settingsDir, scriptSourceMock, startParameter);
     }
 
     @Test
@@ -189,10 +194,6 @@ class DefaultSettingsTest {
         return settings.createProjectDescriptor(settings.getRootProject(), testName, testDir)
     }
 
-    private Map createTestRepoArgs() {
-        return [name: 'someName']
-    }
-
     @Test
     public void testCreateClassLoader() {
         StartParameter expectedStartParameter = settings.startParameter.newInstance()
@@ -203,7 +204,7 @@ class DefaultSettingsTest {
 
     @Test
     public void testCanGetAndSetDynamicProperties() {
-        settings.dynamicProp = 'value'
+        settings.ext.dynamicProp = 'value'
         assertEquals('value', settings.dynamicProp)
     }
 
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 a012945..fb35358 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/InstantiatingBuildLoaderTest.groovy
@@ -44,6 +44,9 @@ class InstantiatingBuildLoaderTest extends Specification {
     ProjectInternal childProject
     GradleInternal build
     def rootProjectClassLoaderScope = Mock(ClassLoaderScope)
+    def baseClassLoaderScope = Mock(ClassLoaderScope) {
+        1 * createChild("root-project") >> rootProjectClassLoaderScope
+    }
 
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
@@ -63,52 +66,37 @@ class InstantiatingBuildLoaderTest extends Specification {
         build.getStartParameter() >> startParameter
     }
 
-    def createsBuildWithRootProject() {
+    def createsBuildWithRootProjectAsTheDefaultOne() {
         when:
         ProjectDescriptor rootDescriptor = descriptor('root', null, rootProjectDir)
         ProjectInternal rootProject = project(rootDescriptor, null)
 
-        projectFactory.createProject(rootDescriptor, null, !null, rootProjectClassLoaderScope) >> rootProject
+        projectFactory.createProject(rootDescriptor, null, !null, rootProjectClassLoaderScope, baseClassLoaderScope) >> rootProject
         1 * build.setRootProject(rootProject)
         build.getRootProject() >> rootProject
         1 * build.setDefaultProject(rootProject)
 
         then:
-        buildLoader.load(rootDescriptor, build, rootProjectClassLoaderScope)
+        buildLoader.load(rootDescriptor, rootDescriptor, build, baseClassLoaderScope)
     }
 
-    def createsBuildWithMultipleProjects() {
+    def createsBuildWithMultipleProjectsAndNotRootDefaultProject() {
         when:
         expectProjectsCreated()
-        buildLoader.load(rootDescriptor, build, rootProjectClassLoaderScope)
+        1 * build.setDefaultProject(childProject)
+        buildLoader.load(rootDescriptor, childDescriptor, build, baseClassLoaderScope)
 
         then:
         rootProject.childProjects['child'].is childProject
     }
 
-    def createsBuildWithMultipleProjectsAndSetsDefaultFromPath() {
-        when:
-        expectProjectsCreatedNoDefaultProject()
-        startParameter.projectPath = ':child'
-        buildLoader.load(rootDescriptor, build, rootProjectClassLoaderScope)
-
-        then:
-        1 * build.setDefaultProject(childProject)
-    }
-
-    def expectProjectsCreatedNoDefaultProject() {
-        1 * projectFactory.createProject(rootDescriptor, null, !null, rootProjectClassLoaderScope) >> rootProject
-        1 * projectFactory.createProject(childDescriptor, rootProject, !null, _ as ClassLoaderScope) >> childProject
+    def expectProjectsCreated() {
+        1 * projectFactory.createProject(rootDescriptor, null, !null, rootProjectClassLoaderScope, baseClassLoaderScope) >> rootProject
+        1 * projectFactory.createProject(childDescriptor, rootProject, !null, _ as ClassLoaderScope, baseClassLoaderScope) >> childProject
         1 * build.setRootProject(rootProject)
         build.getRootProject() >> rootProject
     }
 
-    def expectProjectsCreated() {
-        expectProjectsCreatedNoDefaultProject()
-
-        1 * build.setDefaultProject(rootProject)
-    }
-
     ProjectDescriptor descriptor(String name, ProjectDescriptor parent, File projectDir) {
         new DefaultProjectDescriptor(parent, name, projectDir, projectDescriptorRegistry, TestFiles.resolver(rootProjectDir))
     }
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 3841fb1..2d600cf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
@@ -16,8 +16,6 @@
 
 package org.gradle.initialization
 
-import org.gradle.StartParameter
-import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.SystemProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
@@ -27,17 +25,18 @@ import static org.gradle.util.GFileUtils.canonicalise
 
 class LayoutCommandLineConverterTest extends Specification {
 
-    def converter = new LayoutCommandLineConverter(TestFiles.fileLookup())
+    def converter = new LayoutCommandLineConverter()
     @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
 
     def convert(String... args) {
-        converter.convert(Arrays.asList(args))
+        converter.convert(Arrays.asList(args), new BuildLayoutParameters())
     }
 
     def "has reasonable defaults"() {
         expect:
-        convert().projectDir == canonicalise(SystemProperties.getCurrentDir())
-        convert().gradleUserHomeDir == canonicalise(StartParameter.DEFAULT_GRADLE_USER_HOME)
+        convert().currentDir == canonicalise(SystemProperties.instance.getCurrentDir())
+        convert().projectDir == null
+        convert().gradleUserHomeDir == canonicalise(BuildLayoutParameters.DEFAULT_GRADLE_USER_HOME)
         convert().searchUpwards
     }
 
@@ -51,7 +50,7 @@ class LayoutCommandLineConverterTest extends Specification {
     def "converts relatively to the target dir"() {
         given:
         def root = temp.createDir('root')
-        def target = new BuildLayoutParameters().setProjectDir(root)
+        def target = new BuildLayoutParameters().setCurrentDir(root)
 
         when:
         converter.convert(['-p', 'projectDir', '-g', 'gradleDir'], target)
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPathProjectSpecTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPathProjectSpecTest.java
deleted file mode 100644
index 784b9e7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPathProjectSpecTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.InvalidUserDataException;
-import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.api.internal.project.ProjectRegistry;
-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.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.gradle.util.WrapUtil.toSet;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class ProjectPathProjectSpecTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    @Rule
-    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-    private final String path = ":subproject";
-    private final ProjectPathProjectSpec spec = new ProjectPathProjectSpec(path);
-    private int counter;
-
-    @Test
-    public void containsMatchWhenAtLeastOneProjectHasSpecifiedProjectDir() {
-        assertFalse(spec.containsProject(registry()));
-        assertFalse(spec.containsProject(registry(project(":other"))));
-
-        assertTrue(spec.containsProject(registry(project(path))));
-        assertTrue(spec.containsProject(registry(project(path), project(":"), project(":other"))));
-        assertTrue(spec.containsProject(registry(project(path), project(path))));
-    }
-
-    @Test
-    public void selectsSingleProjectWhichHasSpecifiedProjectDir() {
-        ProjectIdentifier project = project(path);
-        assertThat(spec.selectProject(registry(project, project(":other"))), sameInstance(project));
-    }
-
-    @Test
-    public void selectProjectFailsWhenNoProjectHasSpecifiedProjectDir() {
-        try {
-            spec.selectProject(registry());
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), equalTo("No projects in this build have path '" + path + "'."));
-        }
-    }
-
-    @Test
-    public void selectProjectFailsWhenMultipleProjectsHaveSpecifiedProjectDir() {
-        // this is really artificial
-        ProjectIdentifier project1 = project(path);
-        ProjectIdentifier project2 = project(path);
-        try {
-            spec.selectProject(registry(project1, project2));
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), startsWith("Multiple projects in this build have path '" + path + "':"));
-        }
-    }
-
-    @Test
-    public void cannotSelectProjectWhenBuildFileIsNotAFile() {
-
-        try {
-            new ProjectPathProjectSpec(null).containsProject(registry());
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), equalTo("Project path must not be empty."));
-        }
-
-        try {
-            new ProjectPathProjectSpec("").containsProject(registry());
-            fail();
-        } catch (InvalidUserDataException e) {
-            assertThat(e.getMessage(), equalTo("Project path must not be empty."));
-        }
-    }
-
-    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)));
-        }});
-        return registry;
-    }
-
-    private ProjectIdentifier project(final String path) {
-        final ProjectIdentifier projectIdentifier = context.mock(ProjectIdentifier.class, String.valueOf(counter++));
-        context.checking(new Expectations(){{
-            allowing(projectIdentifier).getPath();
-            will(returnValue(path));
-        }});
-        return projectIdentifier;
-    }
-}
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 5f169bd..69baa80 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectPropertySettingBuildLoaderTest.groovy
@@ -29,7 +29,8 @@ import spock.lang.Specification
 class ProjectPropertySettingBuildLoaderTest extends Specification {
     @Rule public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     final BuildLoader target = Mock()
-    final ProjectDescriptor projectDescriptor = Mock()
+    final ProjectDescriptor rootProjectDescriptor = Mock()
+    final ProjectDescriptor defaultProjectDescriptor = Mock()
     final GradleInternal gradle = Mock()
     final ProjectInternal rootProject = Mock()
     final ProjectInternal childProject = Mock()
@@ -60,10 +61,10 @@ class ProjectPropertySettingBuildLoaderTest extends Specification {
         _ * propertiesLoader.mergeProperties(!null) >> [:]
         
         when:
-        loader.load(projectDescriptor, gradle, classLoaderScope)
+        loader.load(rootProjectDescriptor, defaultProjectDescriptor, gradle, classLoaderScope)
 
         then:
-        1 * target.load(projectDescriptor, gradle, classLoaderScope)
+        1 * target.load(rootProjectDescriptor, defaultProjectDescriptor, gradle, classLoaderScope)
         0 * target._
     }
 
@@ -72,11 +73,23 @@ class ProjectPropertySettingBuildLoaderTest extends Specification {
         2 * propertiesLoader.mergeProperties([:]) >> [prop: 'value']
 
         when:
-        loader.load(projectDescriptor, gradle, classLoaderScope)
+        loader.load(rootProjectDescriptor, defaultProjectDescriptor, gradle, classLoaderScope)
 
         then:
+        1 * rootProject.setProperty('prop', 'value')
+        1 * childProject.setProperty('prop', 'value')
+    }
+
+    def "defines extra property for unknown property"() {
+        given:
+        2 * propertiesLoader.mergeProperties([:]) >> [prop: 'value']
+
+        when:
+        loader.load(rootProjectDescriptor, defaultProjectDescriptor, gradle, classLoaderScope)
+
+        then:
+        1 * rootProject.setProperty('prop', 'value') >> { throw new MissingPropertyException('prop', rootProject.class) }
         1 * rootProperties.set('prop', 'value')
-        1 * childProperties.set('prop', 'value')
     }
 
     def "loads project properties from gradle.properties file in project dir"() {
@@ -85,12 +98,12 @@ class ProjectPropertySettingBuildLoaderTest extends Specification {
         GUtil.saveProperties(new Properties([prop: 'childValue']), new File(childProjectDir, Project.GRADLE_PROPERTIES))
 
         when:
-        loader.load(projectDescriptor, gradle, classLoaderScope)
+        loader.load(rootProjectDescriptor, defaultProjectDescriptor, gradle, classLoaderScope)
 
         then:
         1 * propertiesLoader.mergeProperties([prop: 'rootValue']) >> [prop: 'rootValue']
         1 * propertiesLoader.mergeProperties([prop: 'childValue']) >> [prop: 'childValue']
-        1 * rootProperties.set('prop', 'rootValue')
-        1 * childProperties.set('prop', 'childValue')
+        1 * rootProject.setProperty('prop', 'rootValue')
+        1 * childProject.setProperty('prop', 'childValue')
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectSpecsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectSpecsTest.groovy
index 28bb87e..7b6d619 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectSpecsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/ProjectSpecsTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.initialization
 
 import org.gradle.StartParameter
+import org.gradle.api.internal.SettingsInternal
 import spock.lang.Specification
 
 class ProjectSpecsTest extends Specification {
@@ -35,18 +36,6 @@ class ProjectSpecsTest extends Specification {
 
     }
 
-    def "path based spec"() {
-        given:
-        StartParameter parameter = new StartParameter()
-        parameter.setProjectPath(':a')
-        parameter.setBuildFile(buildFile)
-        parameter.setProjectDir(projectDir)
-        parameter.setCurrentDir(currentDir)
-
-        expect:
-        ProjectSpecs.forStartParameter(parameter).class == ProjectPathProjectSpec
-    }
-
     def "build file based spec"() {
         given:
         StartParameter parameter = new StartParameter()
@@ -55,7 +44,7 @@ class ProjectSpecsTest extends Specification {
         parameter.setCurrentDir(currentDir)
 
         expect:
-        ProjectSpecs.forStartParameter(parameter).class == BuildFileProjectSpec
+        ProjectSpecs.forStartParameter(parameter, Stub(SettingsInternal)).class == BuildFileProjectSpec
     }
 
     def "project dir based spec"() {
@@ -65,7 +54,7 @@ class ProjectSpecsTest extends Specification {
         parameter.setCurrentDir(currentDir)
 
         expect:
-        ProjectSpecs.forStartParameter(parameter).class == ProjectDirectoryProjectSpec
+        ProjectSpecs.forStartParameter(parameter, Stub(SettingsInternal)).class == ProjectDirectoryProjectSpec
     }
 
     def "current dir based spec"() {
@@ -74,6 +63,6 @@ class ProjectSpecsTest extends Specification {
         parameter.setCurrentDir(currentDir)
 
         expect:
-        ProjectSpecs.forStartParameter(parameter).class == DefaultProjectSpec
+        ProjectSpecs.forStartParameter(parameter, Stub(SettingsInternal)).class == DefaultProjectSpec
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessorTest.java b/subprojects/core/src/test/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessorTest.java
deleted file mode 100644
index 177da68..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/PropertiesLoadingSettingsProcessorTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.initialization;
-
-import org.gradle.StartParameter;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.SettingsInternal;
-import org.gradle.api.internal.initialization.ClassLoaderScope;
-import org.gradle.api.internal.initialization.DefaultClassLoaderCache;
-import org.gradle.api.internal.initialization.RootClassLoaderScope;
-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.net.URL;
-import java.net.URLClassLoader;
-
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class PropertiesLoadingSettingsProcessorTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-
-    @Test
-    public void loadsPropertiesThenDelegatesToBackingSettingsProcessor() {
-        final SettingsProcessor delegate = context.mock(SettingsProcessor.class);
-        final URLClassLoader urlClassLoader = new URLClassLoader(new URL[0]);
-        final IGradlePropertiesLoader propertiesLoader = context.mock(IGradlePropertiesLoader.class);
-        final StartParameter startParameter = new StartParameter();
-        final SettingsInternal settings = context.mock(SettingsInternal.class);
-        final File settingsDir = new File("root");
-        final GradleInternal gradle = context.mock(GradleInternal.class);
-        final SettingsLocation settingsLocation = new SettingsLocation(settingsDir, new File("foo"));
-
-        PropertiesLoadingSettingsProcessor processor = new PropertiesLoadingSettingsProcessor(delegate, propertiesLoader);
-
-        final ClassLoaderScope classLoaderScope = new RootClassLoaderScope(urlClassLoader, new DefaultClassLoaderCache());
-
-        context.checking(new Expectations() {{
-            one(propertiesLoader).loadProperties(settingsDir);
-            one(delegate).process(gradle, settingsLocation, classLoaderScope, startParameter);
-            will(returnValue(settings));
-        }});
-
-        assertThat(processor.process(gradle, settingsLocation, classLoaderScope, startParameter), sameInstance(settings));
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.groovy
index ee7a08c..deccc08 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsFactoryTest.groovy
@@ -20,10 +20,9 @@ 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.internal.initialization.DefaultClassLoaderCache
 import org.gradle.api.internal.initialization.RootClassLoaderScope
 import org.gradle.api.internal.initialization.ScriptHandlerFactory
-import org.gradle.api.plugins.PluginContainer
+import org.gradle.api.internal.initialization.loadercache.DummyClassLoaderCache
 import org.gradle.configuration.ScriptPluginFactory
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.internal.service.ServiceRegistry
@@ -41,14 +40,12 @@ class SettingsFactoryTest extends Specification {
         def startParameter = new StartParameter()
         def serviceRegistryFactory = Mock(ServiceRegistryFactory)
         def settingsServices = Mock(ServiceRegistry)
-        def pluginContainer = Mock(PluginContainer)
         def fileResolver = Mock(FileResolver)
         def scriptPluginFactory = Mock(ScriptPluginFactory)
         def scriptHandlerFactory = Mock(ScriptHandlerFactory)
         def projectDescriptorRegistry = Mock(ProjectDescriptorRegistry)
 
         1 * serviceRegistryFactory.createFor(_ as Settings) >> settingsServices
-        1 * settingsServices.get(PluginContainer) >> pluginContainer
         1 * settingsServices.get(FileResolver) >> fileResolver
         1 * settingsServices.get(ScriptPluginFactory) >> scriptPluginFactory
         1 * settingsServices.get(ScriptHandlerFactory) >> scriptHandlerFactory
@@ -60,7 +57,7 @@ class SettingsFactoryTest extends Specification {
         GradleInternal gradle = Mock(GradleInternal)
 
         DefaultSettings settings = (DefaultSettings) settingsFactory.createSettings(gradle,
-                settingsDir, scriptSource, expectedGradleProperties, startParameter, new RootClassLoaderScope(getClass().classLoader, new DefaultClassLoaderCache()));
+                settingsDir, scriptSource, expectedGradleProperties, startParameter, new RootClassLoaderScope(getClass().classLoader, getClass().classLoader, new DummyClassLoaderCache()));
 
         then:
         gradle.is(settings.gradle)
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.groovy
index 6bc529f..a8785aa 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/SettingsHandlerTest.groovy
@@ -33,7 +33,6 @@ class SettingsHandlerTest extends Specification {
     def settingsLocation = new SettingsLocation(GFileUtils.canonicalise(new File("someDir")), null);
     def startParameter = new StartParameter();
     def classLoaderScope = Mock(ClassLoaderScope)
-    def settingsClassLoaderScope = Mock(ClassLoaderScope)
     def settingsFinder = Mock(ISettingsFinder)
     def settingsProcessor = Mock(SettingsProcessor)
     def buildSourceBuilder = Mock(BuildSourceBuilder)
@@ -54,8 +53,7 @@ class SettingsHandlerTest extends Specification {
         gradle.getServices() >> services
         settingsFinder.find(startParameter) >> settingsLocation
         1 * buildSourceBuilder.buildAndCreateClassLoader({ StartParameter sp -> sp.currentDir == new File(settingsLocation.getSettingsDir(), BaseSettings.DEFAULT_BUILD_SRC_DIR) }) >> classLoaderScope
-        1 * classLoaderScope.createRebasedChild() >> settingsClassLoaderScope
-        1 * settingsProcessor.process(gradle, settingsLocation, settingsClassLoaderScope, startParameter) >> settings
+        1 * settingsProcessor.process(gradle, settingsLocation, classLoaderScope, startParameter) >> settings
 
         then:
         settingsHandler.findAndLoadSettings(gradle).is(settings)
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
index 753ac9d..1cfd3c5 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSourceBuilderTest.groovy
@@ -19,6 +19,7 @@ import org.gradle.StartParameter
 import org.gradle.api.internal.initialization.ClassLoaderScope
 import org.gradle.cache.CacheRepository
 import org.gradle.cache.PersistentCache
+import org.gradle.initialization.GradleLauncher
 import org.gradle.initialization.GradleLauncherFactory
 import org.gradle.internal.classpath.ClassPath
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -46,6 +47,8 @@ class BuildSourceBuilderTest extends Specification {
     void "creates classpath when build src exists"() {
         def cache = Mock(PersistentCache)
         def classpath = Mock(ClassPath)
+        def launcher = Mock(GradleLauncher)
+        launcherFactory.newInstance(_) >> launcher
         buildSourceBuilder.createCache(parameter) >> cache
         cache.useCache(_ as String, _ as BuildSrcUpdateFactory) >> 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
index 8e0c913..889d949 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy
@@ -16,22 +16,25 @@
 
 package org.gradle.initialization.buildsrc
 
-import spock.lang.Specification
+import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.component.BuildableJavaComponent
+import org.gradle.api.internal.component.ComponentRegistry
 import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.plugins.Convention
-import org.gradle.api.internal.plugins.EmbeddableJavaProject
-import org.gradle.StartParameter
+import org.gradle.internal.service.ServiceRegistry
+import spock.lang.Specification
 
 class BuildSrcBuildListenerFactoryTest extends Specification {
 
     def startParameter = Mock(StartParameter)
-    def plugin = Stub(EmbeddableJavaProject)
-    def convention = Mock(Convention) {
-        getPlugin(EmbeddableJavaProject) >> plugin
+    def component = Stub(BuildableJavaComponent)
+    def services = Mock(ServiceRegistry) {
+        get(ComponentRegistry) >> Stub(ComponentRegistry) {
+            getMainComponent() >> component
+        }
     }
     def project = Mock(ProjectInternal) {
-        getConvention() >> convention
+        getServices() >> services
     }
     def gradle = Mock(GradleInternal) {
         getStartParameter() >> startParameter
@@ -40,7 +43,7 @@ class BuildSrcBuildListenerFactoryTest extends Specification {
 
     def "configures task names when rebuild on"() {
         def listener = new BuildSrcBuildListenerFactory().create(true)
-        plugin.getRebuildTasks() >> ['fooBuild']
+        component.getRebuildTasks() >> ['fooBuild']
 
         when:
         listener.onConfigure(gradle)
@@ -51,7 +54,7 @@ class BuildSrcBuildListenerFactoryTest extends Specification {
 
     def "configures task names when rebuild off"() {
         def listener = new BuildSrcBuildListenerFactory().create(false)
-        plugin.getBuildTasks() >> ['barBuild']
+        component.getBuildTasks() >> ['barBuild']
 
         when:
         listener.onConfigure(gradle)
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
index ce52264..776f9c1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.initialization.buildsrc
 
-import org.gradle.GradleLauncher
+import org.gradle.initialization.GradleLauncher
 import org.gradle.cache.PersistentCache
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/layout/BuildLayoutFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/layout/BuildLayoutFactoryTest.groovy
index c9c8775..0697ff6 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/layout/BuildLayoutFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/layout/BuildLayoutFactoryTest.groovy
@@ -17,14 +17,14 @@ package org.gradle.initialization.layout
 
 import org.gradle.StartParameter
 import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.groovy.scripts.StringScriptSource
 import org.gradle.groovy.scripts.UriScriptSource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
 
 class BuildLayoutFactoryTest extends Specification {
-    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     final BuildLayoutFactory locator = new BuildLayoutFactory()
 
     def "returns current directory when it contains a settings file"() {
@@ -140,7 +140,7 @@ class BuildLayoutFactoryTest extends Specification {
         def currentDir = tmpDir.createDir("current")
         currentDir.createFile("settings.gradle")
         def rootDir = tmpDir.createDir("root")
-        def settingsFile = rootDir.createDir("some-settings.gradle")
+        def settingsFile = rootDir.createFile("some-settings.gradle")
         def startParameter = new StartParameter()
         startParameter.currentDir = currentDir
         startParameter.settingsFile = settingsFile
@@ -174,7 +174,6 @@ class BuildLayoutFactoryTest extends Specification {
     }
 
     void isEmpty(ScriptSource scriptSource) {
-        assert scriptSource instanceof StringScriptSource
-        assert scriptSource.resource.contents == ''
+        assert scriptSource.resource.text == ''
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/AbstractMultiCauseExceptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/AbstractMultiCauseExceptionTest.groovy
deleted file mode 100644
index e1f7524..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/AbstractMultiCauseExceptionTest.groovy
+++ /dev/null
@@ -1,125 +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.internal.exceptions
-
-import org.gradle.util.GUtil
-import spock.lang.Specification
-
-
-class AbstractMultiCauseExceptionTest extends Specification {
-    def getCauseReturnsTheFirstCause() {
-        def cause1 = new RuntimeException()
-        def cause2 = new RuntimeException()
-        def failure = new TestMultiCauseException('message', [cause1, cause2])
-
-        expect:
-        failure.cause == cause1
-        failure.causes == [cause1, cause2]
-    }
-
-    def getCauseReturnsNullWhenThereAreNoCauses() {
-        def failure = new TestMultiCauseException('message', [])
-
-        expect:
-        failure.cause == null
-        failure.causes == []
-    }
-
-    def canUseInitCauseToProvideCause() {
-        def cause1 = new RuntimeException()
-        def failure = new TestMultiCauseException('message', [])
-        failure.initCause(cause1)
-
-        expect:
-        failure.cause == cause1
-        failure.causes == [cause1]
-    }
-
-    def canUseInitCausesToProvideMultipleCause() {
-        def cause1 = new RuntimeException()
-        def cause2 = new RuntimeException()
-        def failure = new TestMultiCauseException('message', [])
-        failure.initCauses([cause1, cause2])
-
-        expect:
-        failure.cause == cause1
-        failure.causes == [cause1, cause2]
-    }
-
-    def printStackTraceWithMultipleCauses() {
-        RuntimeException cause1 = new RuntimeException('cause1')
-        RuntimeException cause2 = new RuntimeException('cause2')
-        def failure = new TestMultiCauseException('message', [cause1, cause2])
-        def outstr = new StringWriter()
-
-        when:
-        outstr.withPrintWriter { writer ->
-            failure.printStackTrace(writer)
-        }
-
-        then:
-        outstr.toString().contains("${TestMultiCauseException.name}: message")
-        outstr.toString().contains("Cause 1: ${RuntimeException.name}: cause1")
-        outstr.toString().contains("Cause 2: ${RuntimeException.name}: cause2")
-    }
-
-    def printStackTraceWithSingleCause() {
-        RuntimeException cause1 = new RuntimeException('cause1')
-        def failure = new TestMultiCauseException('message', [cause1])
-        def outstr = new StringWriter()
-
-        when:
-        outstr.withPrintWriter { writer ->
-            failure.printStackTrace(writer)
-        }
-
-        then:
-        outstr.toString().contains("${TestMultiCauseException.name}: message")
-        outstr.toString().contains("Caused by: ${RuntimeException.name}: cause1")
-    }
-
-    def canSerializeAndDeserializeException() {
-        def cause1 = new RuntimeException("cause1")
-        def cause2 = new RuntimeException("cause2")
-        def failure = new TestMultiCauseException("message", [cause1, cause2])
-
-        when:
-        def bytes = GUtil.serialize(failure)
-        def result = new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject()
-
-        then:
-        result instanceof TestMultiCauseException
-        result.message == "message"
-        result.causes.size() == 2
-        result.causes*.message == ["cause1", "cause2"]
-
-        when:
-        def outstr = new StringWriter()
-        outstr.withPrintWriter { result.printStackTrace(it) }
-
-        then:
-        outstr.toString().contains("${TestMultiCauseException.name}: message")
-        outstr.toString().contains("Cause 1: ${RuntimeException.name}: cause1")
-        outstr.toString().contains("Cause 2: ${RuntimeException.name}: cause2")
-    }
-}
-
-class TestMultiCauseException extends AbstractMultiCauseException {
-    TestMultiCauseException(String message, Iterable<? extends Throwable> causes) {
-        super(message, causes)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/LocationAwareExceptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/LocationAwareExceptionTest.groovy
index a8595fb..9527899 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/LocationAwareExceptionTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/exceptions/LocationAwareExceptionTest.groovy
@@ -120,7 +120,7 @@ class LocationAwareExceptionTest extends Specification {
         TreeVisitor visitor = Mock()
         def childCause1 = new RuntimeException()
         def childCause2 = new RuntimeException()
-        def cause = new AbstractMultiCauseException("broken", childCause1, childCause2)
+        def cause = new DefaultMultiCauseException("broken", childCause1, childCause2)
         def e = new LocationAwareException(cause, null, 100)
 
         when:
@@ -153,7 +153,7 @@ class LocationAwareExceptionTest extends Specification {
         def childCause2 = new RuntimeException()
         def childCause3 = new TestContextualException(childCause2)
         def childCause4 = new TestContextualException(childCause3)
-        def cause = new AbstractMultiCauseException("broken", childCause1, childCause4)
+        def cause = new DefaultMultiCauseException("broken", childCause1, childCause4)
         def e = new LocationAwareException(cause, null, 100)
 
         when:
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
index 353eaa6..abd4673 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandlerTest.groovy
@@ -17,15 +17,15 @@
 package org.gradle.internal.featurelifecycle
 
 import org.gradle.logging.ConfigureLogging
-import org.gradle.logging.TestAppender
+import org.gradle.logging.TestOutputEventListener
 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)
+    final outputEventListener = new TestOutputEventListener()
+    @Rule final ConfigureLogging logging = new ConfigureLogging(outputEventListener)
     @Rule SetSystemProperties systemProperties = new SetSystemProperties()
     final locationReporter = Mock(UsageLocationReporter)
     final handler = new LoggingDeprecatedFeatureHandler(locationReporter)
@@ -37,7 +37,7 @@ class LoggingDeprecatedFeatureHandlerTest extends Specification {
         handler.deprecatedFeatureUsed(new DeprecatedFeatureUsage("feature2", LoggingDeprecatedFeatureHandlerTest))
 
         then:
-        appender.toString() == '[WARN feature1][WARN feature2]'
+        outputEventListener.toString() == '[WARN feature1][WARN feature2]'
     }
 
     def "location reporter can prepend text"() {
@@ -52,6 +52,6 @@ class LoggingDeprecatedFeatureHandlerTest extends Specification {
         }
 
         and:
-        appender.toString() == TextUtil.toPlatformLineSeparators('[WARN location\nfeature]')
+        outputEventListener.toString() == TextUtil.toPlatformLineSeparators('[WARN location\nfeature]')
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/html/SimpleHtmlWriterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/html/SimpleHtmlWriterTest.groovy
new file mode 100644
index 0000000..afd8026
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/html/SimpleHtmlWriterTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.html
+
+import org.jsoup.Jsoup
+import spock.lang.Specification
+
+class SimpleHtmlWriterTest extends Specification {
+
+    def "creates html output"() {
+        given:
+        StringWriter sw = new StringWriter();
+
+        when:
+        SimpleHtmlWriter htmlWriter = new SimpleHtmlWriter(sw)
+        htmlWriter.startElement("html")
+                .startElement("head").endElement()
+                .startElement("body")
+                .startElement("h1").characters("Test Header").endElement()
+                .endElement()
+                .endElement()
+        sw.close()
+
+        then:
+        Jsoup.parse(sw.toString()).select("h1").text() == "Test Header"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactoryTest.groovy
new file mode 100644
index 0000000..cda2988
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerFactoryTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging
+
+import org.gradle.api.logging.Logger
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultBuildOperationLoggerFactoryTest extends Specification {
+    @Rule
+    final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    Logger logger = Mock()
+    def outputDir = tmpDirProvider.testDirectory.file("logs")
+    DefaultBuildOperationLoggerFactory factory = new DefaultBuildOperationLoggerFactory(logger)
+
+    @Unroll
+    def "enabling debug causes all failures to be logged"() {
+        given:
+        logger.isDebugEnabled() >> debugEnabled
+        def outputFile = outputDir.file("test.out")
+        when:
+        def config = factory.createLogInfo("testTask", outputFile, 10)
+        then:
+        config.maximumFailedOperationsShown == maximumFailures
+        where:
+        debugEnabled | maximumFailures
+        false        | 10
+        true         | Integer.MAX_VALUE
+    }
+
+    def "creates path to output directory"() {
+        when:
+        def outputFile = factory.createOutputFile(outputDir)
+        then:
+        outputDir.exists()
+        outputFile.name == "output.txt"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerTest.groovy
new file mode 100644
index 0000000..a691774
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/operations/logging/DefaultBuildOperationLoggerTest.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.operations.logging
+
+import org.gradle.api.logging.Logger
+import org.gradle.logging.ConsoleRenderer
+
+import static org.gradle.api.logging.LogLevel.*
+
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class DefaultBuildOperationLoggerTest extends Specification {
+    Logger logger = Mock()
+
+    StringWriter logOutput = new StringWriter()
+    PrintWriter logWriter = new PrintWriter(logOutput)
+
+    def maxOperationsShown = 5
+    def pathToLog = new File("path/to/log/file")
+    def pathToLogStr = new ConsoleRenderer().asClickableFileUrl(pathToLog)
+    BuildOperationLogInfo configuration = new BuildOperationLogInfo("<testTask>", pathToLog, maxOperationsShown)
+    BuildOperationLogger log = new DefaultBuildOperationLogger(configuration, logger, logWriter)
+
+    /**
+     * @return output that would appear in the log file
+     */
+    def logOutput() {
+        TextUtil.normaliseLineSeparators(logOutput.toString())
+    }
+
+    def "logs start of overall operation"() {
+        when:
+        log.start()
+        then:
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+"""
+    }
+
+    def "logs completion of operation"() {
+        given:
+        log.start()
+        when:
+        log.operationSuccess("<operation>", "<output>")
+        then:
+        1 * logger.log(DEBUG, "<operation> successful.")
+        1 * logger.log(INFO, "<output>")
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+<operation> successful.
+<output>
+"""
+    }
+
+    def "logs failure of operation"() {
+        given:
+        log.start()
+        when:
+        log.operationFailed("<operation>", "<output>")
+        then:
+        1 * logger.log(DEBUG, "<operation> failed.")
+        1 * logger.log(ERROR, "<output>")
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+<operation> failed.
+<output>
+"""
+    }
+    def "logs output from multiple operations"() {
+        when:
+        log.start()
+        4.times { log.operationFailed("<operation>", "<output>") }
+        log.done()
+
+        then:
+        1 * logger.log(INFO, "See $pathToLogStr for all output for <testTask>.")
+        4 * logger.log(DEBUG, "<operation> failed.")
+        4 * logger.log(ERROR, "<output>")
+        1 * logger.log(INFO, "Finished <testTask>, see full log $pathToLogStr.")
+
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+Finished <testTask>, see full log $pathToLogStr.
+"""
+    }
+
+    def "logs continued message at end of overall operation"() {
+        when:
+        log.start()
+        10.times { log.operationFailed("<operation>", "<output>") }
+        log.done()
+
+        then:
+        1 * logger.log(INFO, "See $pathToLogStr for all output for <testTask>.")
+        10 * logger.log(DEBUG, "<operation> failed.")
+        5 * logger.log(ERROR, "<output>")
+        1 * logger.log(ERROR, "...output for 5 more failed operation(s) continued in $pathToLogStr.")
+        1 * logger.log(INFO, "Finished <testTask>, see full log $pathToLogStr.")
+
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+<operation> failed.
+<output>
+Finished <testTask>, see full log $pathToLogStr.
+"""
+    }
+
+    def "logs end of overall operation"() {
+        when:
+        log.start()
+        log.done()
+        then:
+        logOutput() == """See $pathToLogStr for all output for <testTask>.
+Finished <testTask>, see full log $pathToLogStr.
+"""
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathKeyFileStoreTest.groovy
new file mode 100644
index 0000000..f19ab02
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathKeyFileStoreTest.groovy
@@ -0,0 +1,199 @@
+/*
+ * 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.resource.local
+
+import org.gradle.api.Action
+import org.gradle.api.GradleException
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
+import org.junit.Rule
+import spock.lang.Specification
+
+ at UsesNativeServices
+class PathKeyFileStoreTest extends Specification {
+    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
+    TestFile fsBase
+    PathKeyFileStore store
+
+    def pathCounter = 0
+
+    def setup() {
+        fsBase = dir.file("fs")
+        store = new PathKeyFileStore(fsBase)
+    }
+
+    def "move vs copy"() {
+        given:
+        createFile("a", "a").exists()
+        createFile("b", "b").exists()
+
+        when:
+        store.move("a", dir.file("a"))
+        store.copy("b", dir.file("b"))
+
+        then:
+        !dir.file("a").exists()
+        dir.file("b").exists()
+    }
+
+    def "can move to filestore"() {
+        when:
+        store.move("a", createFile("abc"))
+        store.move("b", createFile("def"))
+
+        then:
+        fsBase.file("a").text == "abc"
+        fsBase.file("b").text == "def"
+    }
+
+    def "can add to filestore"() {
+        when:
+        store.add("a", { File f -> f.text = "abc"} as Action<File>)
+        store.add("b", { File f -> f.text = "def"} as Action<File>)
+        then:
+        fsBase.file("a").text == "abc"
+        fsBase.file("b").text == "def"
+    }
+
+    def "add throws GradleException if exception in action occurred"() {
+        when:
+        store.add("a", { File f -> throw new Exception("TestException")} as Action<File>)
+        then:
+        thrown(GradleException)
+        !fsBase.file("a").exists()
+        !fsBase.file("a.fslock").exists()
+    }
+
+    def "can get from filestore"() {
+        when:
+        createFile("abc", "fs/a")
+        then:
+        store.get("a") != null
+        store.get("b") == null
+    }
+
+    def "get cleans up filestore"() {
+        when:
+        createFile("abc", "fs/a").exists()
+        createFile("lock", "fs/a.fslck").exists()
+        then:
+        store.get("a") == null
+        store.get("a.fslock") == null
+    }
+
+    def "can overwrite stale files "() {
+        given:
+        createFile("abc", "fs/a").exists()
+        createFile("lock", "fs/a.fslck").exists()
+        when:
+        store.add("a", { File f -> f.text = "def"} as Action<File>)
+        then:
+        store.get("a").file.text == "def"
+    }
+
+    def "get on stale file with marker removes file from filestore"() {
+        when:
+        createFile("abc", "fs/a")
+        createFile("def", "fs/b")
+        then:
+        store.get("a").file.text == "abc"
+        store.get("b").file.text == "def"
+    }
+
+    def "can overwrite entry"() {
+        when:
+        store.move("a", createFile("abc"))
+        store.move("a", createFile("def"))
+
+        then:
+        fsBase.file("a").text == "def"
+    }
+
+    def "creates intermediary directories"() {
+        when:
+        store.move("a/b/c", createFile("abc"))
+        store.move("a/b/d", createFile("abd"))
+        store.move("a/c/a", createFile("aca"))
+
+        then:
+        fsBase.file("a/b").directory
+        fsBase.file("a/b/c").text == "abc"
+        fsBase.file("a/c/a").text == "aca"
+    }
+
+    def "can search via globs"() {
+        when:
+        store.move("a/a/a", createFile("a"))
+        store.move("a/a/b", createFile("b"))
+        store.move("a/b/a", createFile("c"))
+
+        then:
+        store.search("**/a").size() == 2
+        store.search("*/b/*").size() == 1
+        store.search("a/b/a").size() == 1
+    }
+
+    def "search ignores stale entries with marker file"() {
+        when:
+        store.move("a/a/a", createFile("a"))
+        store.move("a/b/b", createFile("b"))
+        store.move("a/c/c", createFile("c"))
+        createFile("lock", "fs/a/b/b.fslck")
+        def search = store.search("**/*")
+        then:
+        search.size() == 2
+        search.collect {entry -> entry.file.name}.sort() == ["a", "c"]
+    }
+
+    def "move filestore"() {
+        given:
+        def a = store.move("a", createFile("abc"))
+        def b = store.move("b", createFile("def"))
+
+        expect:
+        a.file == dir.file("fs/a")
+        b.file == dir.file("fs/b")
+
+        when:
+        store.moveFilestore(dir.file("new-store"))
+
+        then:
+        store.baseDir == dir.file("new-store")
+
+        and:
+        a.file == dir.file("new-store/a")
+        b.file == dir.file("new-store/b")
+
+        !dir.file("fs").exists()
+    }
+
+    def "can move filestore that doesn't exist yet"() {
+        expect:
+        !store.baseDir.exists()
+
+        when:
+        store.moveFilestore(dir.file("new-filestore"))
+
+        then:
+        notThrown Exception
+    }
+
+    def createFile(String content, String path = "f${pathCounter++}") {
+        dir.createFile(path) << content
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStoreTest.groovy
new file mode 100644
index 0000000..a16b8c9
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/PathNormalisingKeyFileStoreTest.groovy
@@ -0,0 +1,96 @@
+/*
+ * 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.resource.local
+
+import org.gradle.api.Action
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
+import org.junit.Rule
+import spock.lang.Specification
+
+ at UsesNativeServices
+class PathNormalisingKeyFileStoreTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
+    TestFile fsBase
+    PathNormalisingKeyFileStore store
+
+    def pathCounter = 0
+
+    def setup() {
+        fsBase = dir.createDir("fs")
+        store = new PathNormalisingKeyFileStore(new PathKeyFileStore(fsBase))
+    }
+
+    def "can move to filestore"() {
+        when:
+        store.move("!.zip", file("abc"))
+        store.move("  ", file("def"))
+
+        then:
+        fsBase.file("_.zip").text == "abc"
+        fsBase.file("__").text == "def"
+    }
+
+    def "can add to filestore"() {
+        when:
+        store.add("!.zip", { File file -> file.text = "abc" } as Action<File>)
+        store.add("  ", { File file -> file.text = "def" } as Action<File>)
+        then:
+        fsBase.file("_.zip").text == "abc"
+        fsBase.file("__").text == "def"
+    }
+
+    def "can overwrite entry"() {
+        when:
+        store.move("!", file("abc"))
+        store.move(" ", file("def"))
+
+        then:
+        fsBase.file("_").text == "def"
+    }
+
+    def "creates intermediary directories"() {
+        when:
+        store.move("a/!/c", file("abc"))
+        store.move("a/ /d", file("abd"))
+        store.move("a/c/(", file("aca"))
+
+        then:
+        fsBase.file("a/_").directory
+        fsBase.file("a/_/c").text == "abc"
+        fsBase.file("a/c/_").text == "aca"
+    }
+
+    def "can search via globs"() {
+        when:
+        store.move("a/!/a", file("a"))
+        store.move("a/ /b", file("b"))
+        store.move("a/b/&", file("c"))
+
+        then:
+        store.search("**/a").size() == 1
+        store.search("*/ /*").size() == 2
+        store.search("a/b/_").size() == 1
+    }
+
+    def file(String content, String path = "f${pathCounter++}") {
+        dir.createFile(path) << content
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStoreTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStoreTest.groovy
new file mode 100644
index 0000000..937605e
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/resource/local/UniquePathKeyFileStoreTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * 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.resource.local
+
+import org.gradle.api.Action
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
+import org.junit.Rule
+import spock.lang.Specification
+
+ at UsesNativeServices
+class UniquePathKeyFileStoreTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider();
+    Action<File> action = Mock()
+
+    UniquePathKeyFileStore uniquePathKeyFileStore
+
+    def setup() {
+        uniquePathKeyFileStore = new UniquePathKeyFileStore(temporaryFolder.createDir("fsbase"))
+    }
+
+    def "add executes action if file does not exist"() {
+        def file = temporaryFolder.file("fsbase/a/a");
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.add("a/a", action)
+
+        then:
+        fileInStore.file == file
+        1 * action.execute(file) >> { File f -> f.text = 'hi' }
+    }
+
+    def "add skips action if file already exists"() {
+        setup:
+        def file = temporaryFolder.createFile("fsbase/a/a");
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.add("a/a", action)
+
+        then:
+        fileInStore.file == file
+        0 * action.execute(_)
+    }
+
+    def "copy returns existing file it it already exists"() {
+        setup:
+        def source = temporaryFolder.createFile("some-file")
+        def file = temporaryFolder.createFile("fsbase/a/a");
+        file.text = 'existing content'
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.copy("a/a", source)
+
+        then:
+        fileInStore.file == file
+        file.text == 'existing content'
+    }
+
+    def "copy adds file it it does not exist"() {
+        setup:
+        def source = temporaryFolder.createFile("some-file")
+        def file = temporaryFolder.file("fsbase/a/a");
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.copy("a/a", source)
+
+        then:
+        fileInStore.file == file
+        file.assertIsCopyOf(source)
+    }
+
+    def "move returns existing file it it already exists"() {
+        setup:
+        def source = temporaryFolder.createFile("some-file")
+        def file = temporaryFolder.createFile("fsbase/a/a");
+        file.text = 'existing content'
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.move("a/a", source)
+
+        then:
+        fileInStore.file == file
+        file.text == 'existing content'
+        !source.exists()
+    }
+
+    def "move adds file it it does not exist"() {
+        setup:
+        def source = temporaryFolder.createFile("some-file")
+        def file = temporaryFolder.file("fsbase/a/a");
+
+        when:
+        def fileInStore = uniquePathKeyFileStore.move("a/a", source)
+
+        then:
+        fileInStore.file == file
+        !source.exists()
+    }
+}
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
index d4079c2..ae1d284 100644
--- 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
@@ -24,6 +24,7 @@ import org.gradle.api.internal.classpath.ModuleRegistry
 import org.gradle.api.internal.classpath.PluginModuleRegistry
 import org.gradle.api.internal.file.FileLookup
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache
 import org.gradle.api.internal.project.*
 import org.gradle.cache.CacheRepository
 import org.gradle.cache.internal.CacheFactory
@@ -31,19 +32,24 @@ 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.ImportsReader
 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.classloader.ClassLoaderFactory
+import org.gradle.internal.event.DefaultListenerManager
+import org.gradle.internal.event.ListenerManager
+import org.gradle.internal.operations.logging.BuildOperationLoggerFactory
+import org.gradle.internal.operations.logging.DefaultBuildOperationLoggerFactory
 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.logging.ProgressLoggerFactory
 import org.gradle.messaging.remote.MessagingServer
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.plugin.use.internal.PluginRequestApplicator
 import org.gradle.process.internal.DefaultWorkerProcessFactory
 import org.gradle.process.internal.WorkerProcessBuilder
 import org.gradle.profile.ProfileEventAdapter
@@ -81,6 +87,11 @@ public class BuildScopeServicesTest extends Specification {
         parent.get(CacheFactory) >> Stub(CacheFactory)
         parent.get(DocumentationRegistry) >> new DocumentationRegistry()
         parent.get(FileLookup) >> Stub(FileLookup)
+        parent.get(PluginRequestApplicator) >> Mock(PluginRequestApplicator)
+        parent.get(BuildCancellationToken) >> Mock(BuildCancellationToken)
+        parent.get(ModelRuleSourceDetector) >> Mock(ModelRuleSourceDetector)
+        parent.get(ClassLoaderCache) >> Mock(ClassLoaderCache)
+        parent.get(ImportsReader) >> Mock(ImportsReader)
     }
 
     def delegatesToParentForUnknownService() {
@@ -173,7 +184,6 @@ public class BuildScopeServicesTest extends Specification {
 
     def providesAnInitScriptHandler() {
         setup:
-        allowGetCoreImplClassLoader()
         expectListenerManagerCreated()
         allowGetGradleDistributionLocator()
 
@@ -184,7 +194,6 @@ public class BuildScopeServicesTest extends Specification {
 
     def providesAScriptObjectConfigurerFactory() {
         setup:
-        allowGetCoreImplClassLoader()
         expectListenerManagerCreated()
         expect:
         assertThat(registry.get(ScriptPluginFactory), instanceOf(DefaultScriptPluginFactory))
@@ -193,7 +202,6 @@ public class BuildScopeServicesTest extends Specification {
 
     def providesASettingsProcessor() {
         setup:
-        allowGetCoreImplClassLoader()
         expectListenerManagerCreated()
         expect:
         assertThat(registry.get(SettingsProcessor), instanceOf(PropertiesLoadingSettingsProcessor))
@@ -204,15 +212,15 @@ public class BuildScopeServicesTest extends Specification {
         setup:
         expectListenerManagerCreated()
         expect:
-        assertThat(registry.get(ExceptionAnalyser), instanceOf(MultipleBuildFailuresExceptionAnalyser))
-        assertThat(registry.get(ExceptionAnalyser).delegate, instanceOf(DefaultExceptionAnalyser))
+        assertThat(registry.get(ExceptionAnalyser), instanceOf(StackTraceSanitizingExceptionAnalyser))
+        assertThat(registry.get(ExceptionAnalyser).analyser, instanceOf(MultipleBuildFailuresExceptionAnalyser))
+        assertThat(registry.get(ExceptionAnalyser).analyser.delegate, instanceOf(DefaultExceptionAnalyser))
         assertThat(registry.get(ExceptionAnalyser), sameInstance(registry.get(ExceptionAnalyser)))
     }
 
     def providesAWorkerProcessFactory() {
         setup:
         expectParentServiceLocated(MessagingServer)
-        allowGetCoreImplClassLoader()
 
         expect:
         assertThat(registry.getFactory(WorkerProcessBuilder), instanceOf(DefaultWorkerProcessFactory))
@@ -221,7 +229,6 @@ public class BuildScopeServicesTest extends Specification {
     def providesAnIsolatedAntBuilder() {
         setup:
         expectParentServiceLocated(ClassLoaderFactory)
-        allowGetCoreImplClassLoader()
         expect:
 
         assertThat(registry.get(IsolatedAntBuilder), instanceOf(DefaultIsolatedAntBuilder))
@@ -277,6 +284,14 @@ public class BuildScopeServicesTest extends Specification {
         projectRegistry sameInstance(secondRegistry)
     }
 
+    def "provides an build operation logger factory"() {
+        when:
+        def operationLoggerFactory = registry.get(BuildOperationLoggerFactory)
+
+        then:
+        operationLoggerFactory instanceof DefaultBuildOperationLoggerFactory
+    }
+
     private <T> T expectParentServiceLocated(Class<T> type) {
         T t = Mock(type)
         parent.get(type) >> t
@@ -291,10 +306,6 @@ public class BuildScopeServicesTest extends Specification {
         listenerManager
     }
 
-    private void allowGetCoreImplClassLoader() {
-        classLoaderRegistry.getCoreImplClassLoader() >> new ClassLoader() {}
-    }
-
     private void allowGetGradleDistributionLocator() {
         parent.get(GradleDistributionLocator) >> Mock(GradleDistributionLocator)
     }
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
index 8b02f93..2ec9eb0 100755
--- 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
@@ -25,6 +25,7 @@ import org.gradle.api.internal.file.DefaultFileLookup;
 import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.IdentityFileResolver;
+import org.gradle.api.internal.initialization.loadercache.*;
 import org.gradle.cache.internal.CacheFactory;
 import org.gradle.cache.internal.DefaultCacheFactory;
 import org.gradle.cache.internal.DefaultFileLockManager;
@@ -35,138 +36,158 @@ import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.classloader.DefaultClassLoaderFactory;
 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.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
 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.internal.event.DefaultListenerManager;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
 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.testfixtures.internal.NativeServicesTestFixture;
 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 ServiceRegistry registry = new DefaultServiceRegistry(LoggingServiceRegistry.newEmbeddableLogging(), NativeServices.getInstance()).addProvider(new GlobalScopeServices(false));
+
+    private ServiceRegistry registry(boolean longLiving) {
+        return new DefaultServiceRegistry(LoggingServiceRegistry.newEmbeddableLogging(), NativeServicesTestFixture.getInstance()).addProvider(new GlobalScopeServices(longLiving));
+    }
+
+    private ServiceRegistry longLivingProcessRegistry() {
+        return registry(true);
+    }
+
+    private ServiceRegistry registry() {
+        return registry(false);
+    }
 
     @Test
     public void providesAGradleLauncherFactory() {
-        assertThat(registry.get(GradleLauncherFactory.class), instanceOf(DefaultGradleLauncherFactory.class));
+        assertThat(registry().get(GradleLauncherFactory.class), instanceOf(DefaultGradleLauncherFactory.class));
     }
 
     @Test
     public void providesCommandLineArgsConverter() {
-        assertThat(registry.get(CommandLineConverter.class), instanceOf(
+        assertThat(registry().get(CommandLineConverter.class), instanceOf(
                 DefaultCommandLineConverter.class));
     }
 
     @Test
     public void providesACacheFactory() {
-        assertThat(registry.get(CacheFactory.class), instanceOf(DefaultCacheFactory.class));
+        assertThat(registry().get(CacheFactory.class), instanceOf(DefaultCacheFactory.class));
     }
 
     @Test
     public void providesAModuleRegistry() {
-        assertThat(registry.get(ModuleRegistry.class), instanceOf(DefaultModuleRegistry.class));
+        assertThat(registry().get(ModuleRegistry.class), instanceOf(DefaultModuleRegistry.class));
     }
 
     @Test
     public void providesAPluginModuleRegistry() {
-        assertThat(registry.get(PluginModuleRegistry.class), instanceOf(DefaultPluginModuleRegistry.class));
+        assertThat(registry().get(PluginModuleRegistry.class), instanceOf(DefaultPluginModuleRegistry.class));
     }
 
     @Test
     public void providesAClassPathRegistry() {
-        assertThat(registry.get(ClassPathRegistry.class), instanceOf(DefaultClassPathRegistry.class));
+        assertThat(registry().get(ClassPathRegistry.class), instanceOf(DefaultClassPathRegistry.class));
     }
 
     @Test
     public void providesAClassLoaderRegistry() {
-        assertThat(registry.get(ClassLoaderRegistry.class), instanceOf(DefaultClassLoaderRegistry.class));
+        assertThat(registry().get(ClassLoaderRegistry.class), instanceOf(DefaultClassLoaderRegistry.class));
     }
 
     @Test
     public void providesALoggingManagerFactory() {
-        assertThat(registry.getFactory(LoggingManagerInternal.class), instanceOf(DefaultLoggingManagerFactory.class));
+        assertThat(registry().getFactory(LoggingManagerInternal.class), instanceOf(DefaultLoggingManagerFactory.class));
     }
     
     @Test
     public void providesAListenerManager() {
-        assertThat(registry.get(ListenerManager.class), instanceOf(DefaultListenerManager.class));
+        assertThat(registry().get(ListenerManager.class), instanceOf(DefaultListenerManager.class));
     }
     
     @Test
     public void providesAProgressLoggerFactory() {
-        assertThat(registry.get(ProgressLoggerFactory.class), instanceOf(DefaultProgressLoggerFactory.class));
+        assertThat(registry().get(ProgressLoggerFactory.class), instanceOf(DefaultProgressLoggerFactory.class));
     }
     
     @Test
     public void providesAGradleDistributionLocator() {
-        assertThat(registry.get(GradleDistributionLocator.class), instanceOf(DefaultModuleRegistry.class));
+        assertThat(registry().get(GradleDistributionLocator.class), instanceOf(DefaultModuleRegistry.class));
     }
     
     @Test
     public void providesAClassLoaderFactory() {
-        assertThat(registry.get(ClassLoaderFactory.class), instanceOf(DefaultClassLoaderFactory.class));
+        assertThat(registry().get(ClassLoaderFactory.class), instanceOf(DefaultClassLoaderFactory.class));
     }
 
     @Test
     public void providesAMessagingServer() {
-        assertThat(registry.get(MessagingServer.class), instanceOf(MessagingServer.class));
+        assertThat(registry().get(MessagingServer.class), instanceOf(MessagingServer.class));
     }
 
     @Test
     public void providesAClassGenerator() {
-        assertThat(registry.get(ClassGenerator.class), instanceOf(AsmBackedClassGenerator.class));
+        assertThat(registry().get(ClassGenerator.class), instanceOf(AsmBackedClassGenerator.class));
     }
 
     @Test
     public void providesAnInstantiator() {
-        assertThat(registry.get(org.gradle.internal.reflect.Instantiator.class), instanceOf(ClassGeneratorBackedInstantiator.class));
+        assertThat(registry().get(org.gradle.internal.reflect.Instantiator.class), instanceOf(ClassGeneratorBackedInstantiator.class));
     }
 
     @Test
     public void providesAnExecutorFactory() {
-        assertThat(registry.get(ExecutorFactory.class), instanceOf(DefaultExecutorFactory.class));
+        assertThat(registry().get(ExecutorFactory.class), instanceOf(DefaultExecutorFactory.class));
     }
 
     @Test
     public void providesAFileLockManager() {
-        assertThat(registry.get(FileLockManager.class), instanceOf(DefaultFileLockManager.class));
+        assertThat(registry().get(FileLockManager.class), instanceOf(DefaultFileLockManager.class));
     }
 
     @Test
     public void providesAProcessEnvironment() {
-        assertThat(registry.get(ProcessEnvironment.class), notNullValue());
+        assertThat(registry().get(ProcessEnvironment.class), notNullValue());
     }
 
     @Test
     public void providesAFileSystem() {
-        assertThat(registry.get(FileSystem.class), notNullValue());
+        assertThat(registry().get(FileSystem.class), notNullValue());
     }
 
     @Test
     public void providesAFileResolver() {
-        assertThat(registry.get(FileResolver.class), instanceOf(IdentityFileResolver.class));
+        assertThat(registry().get(FileResolver.class), instanceOf(IdentityFileResolver.class));
     }
 
     @Test
     public void providesAFileLookup() {
-        assertThat(registry.get(FileLookup.class), instanceOf(DefaultFileLookup.class));
+        assertThat(registry().get(FileLookup.class), instanceOf(DefaultFileLookup.class));
     }
 
     @Test
     public void providesADocumentationRegistry() throws Exception {
-        assertThat(registry.get(DocumentationRegistry.class), instanceOf(DocumentationRegistry.class));
+        assertThat(registry().get(DocumentationRegistry.class), instanceOf(DocumentationRegistry.class));
     }
+
+    @Test
+    public void providesAClasspathSnapshotter() throws Exception {
+        assertThat(registry().get(ClassPathSnapshotter.class), instanceOf(FileClassPathSnapshotter.class));
+        assertThat(longLivingProcessRegistry().get(ClassPathSnapshotter.class), instanceOf(HashClassPathSnapshotter.class));
+    }
+    @Test
+    public void providesAClassloaderCache() throws Exception {
+        assertThat(registry().get(ClassLoaderCache.class), instanceOf(DefaultClassLoaderCache.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
index d09ba90..885e726 100644
--- 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
@@ -19,21 +19,18 @@ import org.gradle.StartParameter
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.artifacts.DependencyManagementServices
 import org.gradle.api.internal.changedetection.state.InMemoryTaskArtifactCache
-import org.gradle.api.internal.plugins.DefaultPluginContainer
 import org.gradle.api.internal.plugins.PluginRegistry
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.options.OptionReader
-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.TaskSelector
+import org.gradle.execution.*
 import org.gradle.execution.taskgraph.DefaultTaskGraphExecuter
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.internal.concurrent.ExecutorFactory
 import org.gradle.internal.environment.GradleBuildEnvironment
 import org.gradle.internal.service.ServiceRegistry
-import org.gradle.listener.ListenerManager
+import org.gradle.internal.event.ListenerManager
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
 import spock.lang.Specification
 
 import static org.hamcrest.Matchers.sameInstance
@@ -57,8 +54,11 @@ public class GradleScopeServicesTest extends Specification {
         parent.get(PluginRegistry) >> pluginRegistryParent
         parent.get(DependencyManagementServices) >> Stub(DependencyManagementServices)
         parent.get(ExecutorFactory) >> Stub(ExecutorFactory)
+        parent.get(BuildCancellationToken) >> Stub(BuildCancellationToken)
+        parent.get(ProjectConfigurer) >> Stub(ProjectConfigurer)
+        parent.get(ModelRuleSourceDetector) >> Stub(ModelRuleSourceDetector)
         gradle.getStartParameter() >> startParameter
-        pluginRegistryParent.createChild(_, _) >> pluginRegistryChild
+        pluginRegistryParent.createChild(_, _, _) >> pluginRegistryChild
     }
 
     def "can create services for a project instance"() {
@@ -91,16 +91,6 @@ public class GradleScopeServicesTest extends Specification {
         serviceRegistry2.closed
     }
 
-    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)
@@ -111,16 +101,6 @@ public class GradleScopeServicesTest extends Specification {
         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)
@@ -150,4 +130,19 @@ public class GradleScopeServicesTest extends Specification {
         optionReader instanceof OptionReader
         secondOptionReader sameInstance(optionReader)
     }
+
+    def "adds all plugin gradle scope services"() {
+        def plugin1 = Mock(PluginServiceRegistry)
+        def plugin2 = Mock(PluginServiceRegistry)
+
+        given:
+        parent.getAll(PluginServiceRegistry) >> [plugin1, plugin2]
+
+        when:
+        new GradleScopeServices(parent, gradle)
+
+        then:
+        1 * plugin1.registerGradleServices(_)
+        1 * plugin2.registerGradleServices(_)
+    }
 }
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
index 6ffe1f7..96d80f3 100644
--- 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
@@ -18,16 +18,18 @@ package org.gradle.internal.service.scopes
 
 import org.gradle.api.AntBuilder
 import org.gradle.api.RecordingAntBuildListener
+import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.initialization.dsl.ScriptHandler
-import org.gradle.api.internal.*
+import org.gradle.api.internal.ClassGenerator
+import org.gradle.api.internal.ClassGeneratorBackedInstantiator
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.TaskInternal
 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.ClassLoaderScope
 import org.gradle.api.internal.initialization.DefaultScriptHandler
-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
@@ -35,18 +37,21 @@ 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.nativeplatform.filesystem.FileSystem
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
 import org.gradle.internal.reflect.DirectInstantiator
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.ServiceRegistration
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.logging.LoggingManagerInternal
+import org.gradle.model.internal.inspect.ModelRuleExtractor
+import org.gradle.model.internal.inspect.ModelRuleSourceDetector
+import org.gradle.model.internal.registry.ModelRegistry
+import org.gradle.model.internal.persist.ModelRegistryStore
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 import org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry
@@ -55,14 +60,21 @@ import spock.lang.Specification
 
 class ProjectScopeServicesTest extends Specification {
     ProjectInternal project = Mock()
-    ConfigurationContainerInternal configurationContainer = Mock()
+    ConfigurationContainer configurationContainer = Mock()
     GradleInternal gradle = Mock()
     DependencyManagementServices dependencyManagementServices = Mock()
     ITaskFactory taskFactory = Mock()
     DependencyFactory dependencyFactory = Mock()
     ServiceRegistry parent = Stub()
     ProjectScopeServices registry
-    PluginRegistry pluginRegistry = Mock()
+    PluginRegistry pluginRegistry = Mock() {
+        createChild(_) >> Mock(PluginRegistry)
+    }
+    ModelRegistry modelRegistry = Mock()
+    ModelRegistryStore modelRegistryStore = Mock() {
+        get(_) >> modelRegistry
+    }
+    ModelRuleSourceDetector modelRuleSourceDetector = Mock()
     def classLoaderScope = Mock(ClassLoaderScope)
     DependencyResolutionServices dependencyResolutionServices = Stub()
 
@@ -74,17 +86,20 @@ class ProjectScopeServicesTest extends Specification {
         project.projectDir >> testDirectoryProvider.file("project-dir").createDir().absoluteFile
         project.buildScriptSource >> Stub(ScriptSource)
         project.getClassLoaderScope() >> classLoaderScope
-        project.getClassLoaderScope().createChild() >> classLoaderScope
+        project.getClassLoaderScope().createChild(_) >> classLoaderScope
         project.getClassLoaderScope().lock() >> classLoaderScope
         parent.get(ITaskFactory) >> taskFactory
         parent.get(DependencyFactory) >> dependencyFactory
         parent.get(PluginRegistry) >> pluginRegistry
         parent.get(DependencyManagementServices) >> dependencyManagementServices
-        parent.get(Instantiator) >> new DirectInstantiator()
+        parent.get(Instantiator) >> DirectInstantiator.INSTANCE
         parent.get(FileSystem) >> Stub(FileSystem)
         parent.get(ClassGenerator) >> Stub(ClassGenerator)
         parent.get(ProjectAccessListener) >> Stub(ProjectAccessListener)
         parent.get(FileLookup) >> Stub(FileLookup)
+        parent.get(ModelRegistryStore) >> modelRegistryStore
+        parent.get(ModelRuleSourceDetector) >> modelRuleSourceDetector
+        parent.get(ModelRuleExtractor) >> Stub(ModelRuleExtractor)
         registry = new ProjectScopeServices(parent, project)
     }
 
@@ -115,13 +130,6 @@ class ProjectScopeServicesTest extends Specification {
         registry.getFactory(TaskContainerInternal) instanceof DefaultTaskContainerFactory
     }
 
-    def "provides a PluginContainer"() {
-        1 * pluginRegistry.createChild(classLoaderScope, _ as DependencyInjectingInstantiator) >> Stub(PluginRegistry)
-
-        expect:
-        provides(PluginContainer, DefaultPluginContainer)
-    }
-
     def "provides a ToolingModelBuilderRegistry"() {
         expect:
         provides(ToolingModelBuilderRegistry, DefaultToolingModelBuilderRegistry)
@@ -214,6 +222,6 @@ class ProjectScopeServicesTest extends Specification {
     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)
+        dependencyResolutionServices.configurationContainer >> Mock(ConfigurationContainer)
     }
 }
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
index 6a0d578..45ab69c 100644
--- 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
@@ -19,10 +19,9 @@ 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 org.gradle.model.internal.inspect.ModelRuleSourceDetector
 import spock.lang.Specification
 
 import static org.hamcrest.Matchers.sameInstance
@@ -36,20 +35,10 @@ class SettingsScopeServicesTest extends Specification {
 
     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(org.gradle.internal.nativeintegration.filesystem.FileSystem) >> Stub(org.gradle.internal.nativeintegration.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)
+        parent.get(ModelRuleSourceDetector) >> Stub(ModelRuleSourceDetector)
+        pluginRegistryParent.createChild(_, _, _) >> pluginRegistryChild
     }
 
     def "provides a file resolver"() {
@@ -62,14 +51,4 @@ class SettingsScopeServicesTest extends Specification {
         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
index 6236df1..2d79615 100644
--- 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
@@ -23,11 +23,14 @@ import org.gradle.api.invocation.Gradle
 import org.gradle.cache.CacheBuilder
 import org.gradle.cache.CacheRepository
 import org.gradle.cache.PersistentCache
+import org.gradle.internal.concurrent.ExecutorFactory
 import org.gradle.internal.environment.GradleBuildEnvironment
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.operations.DefaultBuildOperationProcessor
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.internal.service.ServiceRegistry
-import org.gradle.listener.ListenerManager
+import org.gradle.internal.event.ListenerManager
 import spock.lang.Specification
 
 class TaskExecutionServicesTest extends Specification {
@@ -55,4 +58,14 @@ class TaskExecutionServicesTest extends Specification {
         services.get(TaskExecuter) instanceof ExecuteAtMostOnceTaskExecuter
         services.get(TaskExecuter).is(services.get(TaskExecuter))
     }
+
+    def "makes a BuildOperationProcessor available"() {
+        given:
+        _ * parent.get(StartParameter) >> Mock(StartParameter)
+        _ * parent.get(ExecutorFactory) >> Mock(ExecutorFactory)
+
+        expect:
+        services.get(BuildOperationProcessor) instanceof DefaultBuildOperationProcessor
+        services.get(BuildOperationProcessor).is(services.get(BuildOperationProcessor))
+    }
 }
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
index eda529d..d12ab85 100644
--- 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
@@ -22,7 +22,7 @@ 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.internal.tasks.TaskMutator;
 import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.tasks.TaskInputs;
 import org.gradle.internal.Factory;
@@ -70,8 +70,8 @@ public class TaskScopeServicesTest {
 
     @Test
     public void createsATaskStatusNaggerInstance() {
-        TaskStatusNagger nagger = registry.get(TaskStatusNagger.class);
-        assertSame(nagger, registry.get(TaskStatusNagger.class));
+        TaskMutator nagger = registry.get(TaskMutator.class);
+        assertSame(nagger, registry.get(TaskMutator.class));
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverterTest.groovy
new file mode 100644
index 0000000..8baac73
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationConverterTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class CharSequenceNotationConverterTest extends Specification {
+    def target = Mock(NotationConverter)
+    def result = Mock(NotationConvertResult)
+    def converter = new CharSequenceNotationConverter<Object, Number>(target)
+
+    def "ignores notations that are not a CharSequence instance"() {
+        when:
+        converter.convert(12, result)
+
+        then:
+        0 * _._
+    }
+
+    @Unroll
+    def "delegates to target converter when notation is an instance of #type"() {
+        when:
+        converter.convert(value, result)
+
+        then:
+        1 * target.convert("12", result)
+
+        where:
+        type          | value
+        String        | "12"
+        StringBuilder | new StringBuilder("12")
+        GString       | "${6 * 2}"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParserTest.groovy
deleted file mode 100644
index 0d4c3dd..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/CharSequenceNotationParserTest.groovy
+++ /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.internal.typeconversion
-
-import spock.lang.Specification
-
-class CharSequenceNotationParserTest extends Specification {
-    def parser = new CharSequenceNotationParser()
-
-    def "handles Strings"() {
-        expect:
-        converts("abc", "abc")
-    }
-
-    def "handles GStrings"() {
-        expect:
-        def foo = "abc"
-        converts("$foo", "abc")
-    }
-
-    def "handles StringBuilders"() {
-        def builder = new StringBuilder()
-        builder.append("abc")
-
-        expect:
-        converts(builder, "abc")
-    }
-
-    void converts(from, to) {
-        assert parser.parseNotation(from).getClass() == String
-        assert parser.parseNotation(from) == to
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverterTest.groovy
new file mode 100644
index 0000000..82a9833
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationConverterTest.groovy
@@ -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.internal.typeconversion
+
+import org.gradle.api.specs.Spec
+import spock.lang.Specification
+
+class ClosureToSpecNotationConverterTest extends Specification {
+    private ClosureToSpecNotationConverter converter = new ClosureToSpecNotationConverter(String)
+
+    def "converts closures"() {
+        given:
+        def spec = parse({ it == 'foo' })
+
+        expect:
+        spec.isSatisfiedBy("foo")
+        !spec.isSatisfiedBy("FOO")
+        !spec.isSatisfiedBy("bar")
+    }
+
+    def parse(def value) {
+        return NotationParserBuilder.toType(Spec).fromType(Closure, converter).toComposite().parseNotation(value)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParserTest.groovy
deleted file mode 100644
index f73bd91..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ClosureToSpecNotationParserTest.groovy
+++ /dev/null
@@ -1,36 +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.typeconversion
-
-import spock.lang.Specification
-
-class ClosureToSpecNotationParserTest extends Specification {
-
-    private ClosureToSpecNotationParser parser = new ClosureToSpecNotationParser()
-
-    def "converts closures"() {
-        expect:
-        parser.parseNotation({ it == 'foo' }).isSatisfiedBy("foo")
-        !parser.parseNotation({ it == 'foo' }).isSatisfiedBy("bar")
-
-        when:
-        parser.parseNotation("oups")
-
-        then:
-        thrown(UnsupportedNotationException)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParserSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParserSpec.groovy
index 3a84eee..b383bf7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParserSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/EnumFromCharSequenceNotationParserSpec.groovy
@@ -33,6 +33,7 @@ class EnumFromCharSequenceNotationParserSpec extends Specification{
     def "throws decent error for non convertable strings"(){
         when:
         parser.parseNotation("notKnown")
+
         then:
         def e = thrown(TypeConversionException)
         e.message == "Cannot coerce string value 'notKnown' to an enum value of type 'org.gradle.internal.typeconversion.EnumFromCharSequenceNotationParserSpec\$TestEnum' (valid case insensitive values: [ENUM1, ENUM2, ENUM3])"
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParserTest.groovy
index 2ab5c5e..7d68246 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParserTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/ErrorHandlingNotationParserTest.groovy
@@ -15,45 +15,47 @@
  */
 package org.gradle.internal.typeconversion
 
-import org.gradle.api.InvalidUserDataException
+import org.gradle.internal.exceptions.DiagnosticsVisitor
 import spock.lang.Specification
 
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
 
 class ErrorHandlingNotationParserTest extends Specification {
     def NotationParser<String, String> target = Mock()
-    def parser = new ErrorHandlingNotationParser<String, String>("String", "<broken>", target)
+    def parser = new ErrorHandlingNotationParser<String, String>("a thing", "<broken>", false, target)
 
     def "reports unable to parse null"() {
         when:
         parser.parseNotation(null)
 
         then:
-        InvalidUserDataException e = thrown()
-        e.message == toPlatformLineSeparators('''Cannot convert a null value to an object of type String.
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators('''Cannot convert a null value to a thing.
 The following types/formats are supported:
   - format 1
   - format 2
+
 <broken>''')
 
-        1 * target.describe(!null) >> { args -> args[0].add("format 1"); args[0].add("format 2") }
+        1 * target.describe(!null) >> { DiagnosticsVisitor visitor -> visitor.candidate("format 1"); visitor.candidate("format 2") }
         0 * target._  //no parsing
     }
 
     def "reports unable to parse non-null"() {
         given:
         target.parseNotation("bad") >> { throw new UnsupportedNotationException("broken-part") }
-        target.describe(!null) >> { args -> args[0].add("format 1"); args[0].add("format 2") }
+        target.describe(!null) >> { DiagnosticsVisitor visitor -> visitor.candidate("format 1"); visitor.candidate("format 2") }
 
         when:
         parser.parseNotation("bad")
 
         then:
-        InvalidUserDataException e = thrown()
-        e.message == toPlatformLineSeparators('''Cannot convert the provided notation to an object of type String: broken-part.
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators('''Cannot convert the provided notation to a thing: broken-part.
 The following types/formats are supported:
   - format 1
   - format 2
+
 <broken>''')
 
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationConverterTest.groovy
new file mode 100644
index 0000000..45ab72e
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationConverterTest.groovy
@@ -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.typeconversion
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.tasks.Optional
+import spock.lang.Specification
+
+class MapNotationConverterTest extends Specification {
+    final NotationParser parser = NotationParserBuilder.toType(TargetObject).converter(new DummyConverter()).toComposite()
+    
+    def "parses map with required keys"() {
+        expect:
+        def object = parser.parseNotation([name: 'name', version: 'version'])
+        object.key1 == 'name'
+        object.key2 == 'version'
+        object.prop1 == null
+    }
+
+    def "parses map with required and optional keys"() {
+        expect:
+        def object = parser.parseNotation([name: 'name', version: 'version', optional: '1.2'])
+        object.key1 == 'name'
+        object.key2 == 'version'
+        object.optional == '1.2'
+        object.prop1 == null
+    }
+
+    def "configures properties of converted object using extra properties"() {
+        expect:
+        def object = parser.parseNotation([name: 'name', version: 'version', prop1: 'prop1', optional: '1.2'])
+        object.key1 == 'name'
+        object.key2 == 'version'
+        object.prop1 == 'prop1'
+    }
+
+    def "does not mutate original map"() {
+        def source = [name: 'name', version: 'version', prop1: 'prop1', optional: '1.2']
+        def copy = new HashMap<String, Object>(source)
+        
+        when:
+        parser.parseNotation(source)
+        
+        then:
+        source == copy
+    }
+
+    def "does not parse map with missing keys"() {
+        when:
+        parser.parseNotation([name: 'name'])
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == 'Required keys [version] are missing from map {name=name}.'
+    }
+
+    def "treats empty strings and null values as missing"() {
+        when:
+        parser.parseNotation([name: null, version: ''])
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message.startsWith 'Required keys [name, version] are missing from map '
+    }
+
+    def "does not parse map with unknown extra properties"() {
+        when:
+        parser.parseNotation([name: 'name', version: 1.2, unknown: 'unknown'])
+
+        then:
+        MissingPropertyException e = thrown()
+        e.property == 'unknown'
+        e.type == TargetObject
+    }
+
+    def "does not parse notation that is not a map"() {
+        when:
+        parser.parseNotation('string')
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+    
+    static class DummyConverter extends MapNotationConverter<TargetObject> {
+        protected TargetObject parseMap(@MapKey('name') String name,
+                                        @MapKey('version') String version,
+                                        @Optional @MapKey('optional') optional) {
+            return new TargetObject(key1:  name, key2:  version, optional:  optional)
+        }
+    }
+}
+
+class TargetObject {
+    String key1;
+    String key2;
+    String optional;
+    String prop1;
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationParserTest.groovy
deleted file mode 100644
index 474a474..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/MapNotationParserTest.groovy
+++ /dev/null
@@ -1,109 +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.internal.typeconversion
-
-import spock.lang.Specification
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.tasks.Optional
-
-class MapNotationParserTest extends Specification {
-    final DummyParser parser = new DummyParser()
-    
-    def "parses map with required keys"() {
-        expect:
-        def object = parser.parseNotation([name: 'name', version: 'version'])
-        object.key1 == 'name'
-        object.key2 == 'version'
-        object.prop1 == null
-    }
-
-    def "parses map with required and optional keys"() {
-        expect:
-        def object = parser.parseNotation([name: 'name', version: 'version', optional: '1.2'])
-        object.key1 == 'name'
-        object.key2 == 'version'
-        object.optional == '1.2'
-        object.prop1 == null
-    }
-
-    def "configures properties of converted object using extra properties"() {
-        expect:
-        def object = parser.parseNotation([name: 'name', version: 'version', prop1: 'prop1', optional: '1.2'])
-        object.key1 == 'name'
-        object.key2 == 'version'
-        object.prop1 == 'prop1'
-    }
-
-    def "does not mutate original map"() {
-        def source = [name: 'name', version: 'version', prop1: 'prop1', optional: '1.2']
-        def copy = new HashMap<String, Object>(source)
-        
-        when:
-        parser.parseNotation(source)
-        
-        then:
-        source == copy
-    }
-
-    def "does not parse map with missing keys"() {
-        when:
-        parser.parseNotation([name: 'name'])
-
-        then:
-        InvalidUserDataException e = thrown()
-        e.message == 'Required keys [version] are missing from map {name=name}.'
-    }
-
-    def "treats empty strings and null values as missing"() {
-        when:
-        parser.parseNotation([name: null, version: ''])
-
-        then:
-        InvalidUserDataException e = thrown()
-        e.message.startsWith 'Required keys [name, version] are missing from map '
-    }
-
-    def "does not parse map with unknown extra properties"() {
-        when:
-        parser.parseNotation([name: 'name', version: 1.2, unknown: 'unknown'])
-
-        then:
-        MissingFieldException e = thrown()
-    }
-
-    def "does not parse notation that is not a map"() {
-        when:
-        parser.parseNotation('string')
-
-        then:
-        thrown(UnsupportedNotationException)
-    }
-    
-    static class DummyParser extends MapNotationParser<TargetObject> {
-        protected TargetObject parseMap(@MapKey('name') String name,
-                                        @MapKey('version') String version,
-                                        @Optional @MapKey('optional') optional) {
-            return new TargetObject(key1:  name, key2:  version, optional:  optional)
-        }
-    }
-
-    static class TargetObject {
-        String key1;
-        String key2;
-        String optional;
-        String prop1;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapterTest.groovy
new file mode 100644
index 0000000..c0e69af
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationConverterToNotationParserAdapterTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion
+
+import spock.lang.Specification
+
+class NotationConverterToNotationParserAdapterTest extends Specification {
+    def converter = Mock(NotationConverter)
+    def parser = new NotationConverterToNotationParserAdapter(converter)
+
+    def "converts notation"() {
+        given:
+        converter.convert(12, _) >> { Object notation, NotationConvertResult result -> result.converted("12") }
+
+        expect:
+        parser.parseNotation(12) == "12"
+    }
+
+    def "can convert to null value"() {
+        given:
+        converter.convert(12, _) >> { Object notation, NotationConvertResult result -> result.converted(null) }
+
+        expect:
+        parser.parseNotation(12) == null
+    }
+
+    def "fails when the notation cannot be converted"() {
+        given:
+        converter.convert(12, _) >> { }
+
+        when:
+        parser.parseNotation(12)
+
+        then:
+        UnsupportedNotationException e = thrown()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationParserBuilderSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationParserBuilderSpec.groovy
index 727e6e0..5c916d7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationParserBuilderSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/NotationParserBuilderSpec.groovy
@@ -16,32 +16,107 @@
 
 package org.gradle.internal.typeconversion
 
-import org.gradle.api.InvalidUserDataException
 import spock.lang.Specification
 
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
 
 class NotationParserBuilderSpec extends Specification {
 
-    def "adds just return parser as default"(){
+    def "adds just return parser as default"() {
         when:
-        def parser = new NotationParserBuilder<String>()
-                .resultingType(String.class).toComposite()
+        def parser = NotationParserBuilder.toType(String.class).toComposite()
+
         then:
         "Some String" == parser.parseNotation("Some String")
     }
 
-    def "can build with no just return parser"(){
-        setup:
-        def parser = new NotationParserBuilder<String>()
-                .resultingType(String.class)
-                .withDefaultJustReturnParser(false).toComposite()
+    def "can add a converter"() {
+        def converter = Mock(NotationConverter)
+        given:
+        converter.convert(_, _) >> { Object n, NotationConvertResult result -> result.converted("[${n}]") }
+
+        and:
+        def parser = NotationParserBuilder.toType(String.class).converter(converter).toComposite()
+
+        expect:
+        parser.parseNotation(12) == "[12]"
+    }
+
+    def "can add a converter that converts notations of a given type"() {
+        def converter = Mock(NotationConverter)
+
+        given:
+        converter.convert(_, _) >> { Number n, NotationConvertResult result -> result.converted("[${n}]") }
+
+        and:
+        def parser = NotationParserBuilder.toType(String.class).fromType(Number, converter).toComposite()
+
+        expect:
+        parser.parseNotation(12) == "[12]"
+    }
+
+    def "can add a converter that converts CharSequence notations"() {
+        def converter = Mock(NotationConverter)
+
+        given:
+        converter.convert(_, _) >> { String n, NotationConvertResult result -> result.converted("[${n}]") }
+
+        and:
+        def parser = NotationParserBuilder.toType(String.class).fromCharSequence(converter).toComposite()
+
+        expect:
+        parser.parseNotation(new StringBuilder("12")) == "[12]"
+    }
+
+    def "can add a converter that converts CharSequence notations when the target type is String"() {
+        def parser = NotationParserBuilder.toType(String.class).fromCharSequence().toComposite()
+
+        expect:
+        parser.parseNotation(new StringBuilder("12")) == "12"
+        parser.parseNotation("12") == "12"
+    }
+
+    def "can opt in to allow null as input"() {
+        def converter = Mock(NotationConverter)
+        def parser = NotationParserBuilder
+                .toType(String.class)
+                .allowNullInput()
+                .converter(converter)
+                .toComposite()
+
+
+        given:
+        converter.convert(null, _) >> { Object n, NotationConvertResult result -> result.converted("[null]") }
+
+        expect:
+        parser.parseNotation(null) == "[null]"
+    }
+
+    def "can tweak the conversion error messages"() {
+        given:
+        def parser = NotationParserBuilder.toType(String.class).typeDisplayName("a thing").toComposite()
+
         when:
-        parser.parseNotation("Some String")
+        parser.parseNotation(12)
+
         then:
-        def e = thrown(InvalidUserDataException)
-        e.message == toPlatformLineSeparators("""Cannot convert the provided notation to an object of type String: Some String.
-The following types/formats are supported:""")
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators("""Cannot convert the provided notation to a thing: 12.
+The following types/formats are supported:
+  - Instances of String.""")
     }
 
+    def "uses nice display name for String target type"() {
+        given:
+        def parser = NotationParserBuilder.toType(String.class).toComposite()
+
+        when:
+        parser.parseNotation(12)
+
+        then:
+        UnsupportedNotationException e = thrown()
+        e.message == toPlatformLineSeparators("""Cannot convert the provided notation to a String: 12.
+The following types/formats are supported:
+  - Instances of String.""")
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverterTest.groovy
new file mode 100644
index 0000000..b64cf6f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypeFilteringNotationConverterTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.typeconversion
+
+import spock.lang.Specification
+
+class TypeFilteringNotationConverterTest extends Specification {
+    def target = Mock(NotationConverter)
+    def result = Mock(NotationConvertResult)
+    def converter = new TypeFilteringNotationConverter<Number, BigDecimal, String>(BigDecimal, target)
+
+    def "ignores notation when it is not an instance of the target type"() {
+        when:
+        converter.convert(12L, result)
+
+        then:
+        0 * _._
+    }
+
+    def "delegates to target when notation is an instance of the target type"() {
+        when:
+        converter.convert(12.0, result)
+
+        then:
+        1 * target.convert(12.0, result)
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationConverterTest.groovy
new file mode 100644
index 0000000..16f3e96
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationConverterTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.typeconversion;
+
+
+import spock.lang.Specification
+
+public class TypedNotationConverterTest extends Specification {
+
+    def parser = NotationParserBuilder.toType(Integer).converter(new DummyConverter()).toComposite()
+
+    def "parses object of source type"(){
+        expect:
+        parser.parseNotation("100") == 100
+    }
+
+    def "throws meaningful exception on parse attempt"(){
+        when:
+        parser.parseNotation(new Object())
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    class DummyConverter extends TypedNotationConverter<String, Integer> {
+
+        DummyConverter() {
+            super(String.class)
+        }
+
+        Integer parseType(String notation) {
+            return Integer.valueOf(notation);
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationParserTest.groovy
deleted file mode 100644
index beaebd9..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/internal/typeconversion/TypedNotationParserTest.groovy
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.internal.typeconversion;
-
-
-import spock.lang.Specification
-
-public class TypedNotationParserTest extends Specification {
-
-    def parser = new DummyParser();
-
-    def "parses object of source type"(){
-        expect:
-        parser.parseNotation("100") == 100
-    }
-
-    def "throws meaningful exception on parse attempt"(){
-        when:
-        parser.parseNotation(new Object())
-
-        then:
-        thrown(UnsupportedNotationException)
-    }
-
-    class DummyParser extends TypedNotationParser<String, Integer> {
-
-        DummyParser() {
-            super(String.class)
-        }
-
-        Integer parseType(String notation) {
-            return Integer.valueOf(notation);
-        }
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/xml/SimpleXmlWriterSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/xml/SimpleXmlWriterSpec.groovy
new file mode 100644
index 0000000..3772f63
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/xml/SimpleXmlWriterSpec.groovy
@@ -0,0 +1,413 @@
+/*
+ * 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.xml
+
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+import javax.xml.parsers.DocumentBuilderFactory
+
+class SimpleXmlWriterSpec extends Specification {
+
+    private sw = new ByteArrayOutputStream()
+    private writer = new SimpleXmlWriter(sw)
+
+    String getXml() {
+        def text = sw.toString("UTF-8")
+        def document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(sw.toByteArray()))
+        assert document
+        return text
+    }
+
+    def "writes basic XML"() {
+        when:
+        writer.startElement("root").attribute("items", "9")
+        writer.startElement("item").endElement()
+        writer.startElement("item").attribute("size", "10m")
+        writer.characters("some chars")
+        writer.characters(" and some other".toCharArray())
+        writer.characters("x  chars.x".toCharArray(), 2, 7)
+        writer.startElement("foo").characters(" ")
+        writer.endElement()
+        writer.endElement()
+        writer.endElement()
+
+        then:
+        xml == '<?xml version="1.0" encoding="UTF-8"?><root items="9"><item/><item size="10m">some chars and some other chars.<foo> </foo></item></root>'
+    }
+
+    def "escapes reserved characters in text content"() {
+        when:
+        writer.startElement("root")
+        writer.characters("chars with interesting stuff: < < > ' \" ]]> \r\n \t")
+        writer.endElement()
+
+        then:
+        xml.contains('<root>chars with interesting stuff: &lt; < > \' " ]]> \r\n \t</root>')
+    }
+
+    def "escapes reserved characters in attribute values"() {
+        when:
+        writer.startElement("root")
+        writer.startElement("item").attribute("description", "encoded: \t < < > ' \n\r\"  ")
+        writer.endElement()
+        writer.endElement()
+
+        then:
+        xml.contains('<item description="encoded: 	 &lt; < > \' 

"  "/>')
+
+        and:
+        def item = new XmlSlurper().parseText(xml).item
+        item. at description.text() == "encoded: \t < < > ' \n\r\"  "
+    }
+
+    def "writes CDATA"() {
+        when:
+        writer.startElement("root")
+        writer.startElement("stuff")
+
+        writer.startCDATA()
+        writer.characters('x hey x'.toCharArray(), 2, 4)
+        writer.characters('joe'.toCharArray())
+        writer.characters("!")
+        writer.endCDATA()
+
+        writer.endElement()
+
+        writer.startCDATA()
+        writer.characters('encodes: ]]> ')
+        writer.characters('does not encode: ]] ')
+        writer.characters('html allowed: <> &')
+        writer.endCDATA()
+
+        writer.endElement()
+
+        then:
+        xml.contains('<stuff><![CDATA[hey joe!]]></stuff><![CDATA[encodes: ]]]]><![CDATA[> does not encode: ]] html allowed: <> &]]>')
+    }
+
+    def "encodes CDATA when token on the border"() {
+        when:
+        //the end token is on the border of both char arrays
+        writer.startElement('root')
+        writer.startCDATA()
+        writer.characters('stuff ]]')
+        writer.characters('> more stuff')
+        writer.endCDATA()
+        writer.endElement()
+
+        then:
+        xml.contains('<![CDATA[stuff ]]]]><![CDATA[> more stuff]]>')
+    }
+
+    def "does not encode CDATA when token separated in different CDATAs"() {
+        when:
+        //the end token is on the border of both char arrays
+
+        writer.startElement('root')
+
+        writer.startCDATA();
+        writer.characters('stuff ]]')
+        writer.endCDATA();
+
+        writer.startCDATA()
+        writer.characters('> more stuff')
+        writer.endCDATA();
+
+        writer.endElement()
+
+        then:
+        xml.contains('<root><![CDATA[stuff ]]]]><![CDATA[> more stuff]]></root>')
+    }
+
+    def "encodes non-ASCII characters"() {
+        when:
+        writer.startElement("\u0200").attribute("\u0201", "\u0202")
+        writer.characters("\u0203")
+        writer.startCDATA().characters("\u0204").endCDATA()
+        writer.endElement()
+
+        then:
+        xml.contains('<\u0200 \u0201="\u0202">\u0203<![CDATA[\u0204]]></\u0200>')
+    }
+
+    def "escapes restricted characters in text content"() {
+        when:
+        writer.startElement("root")
+        writer.attribute("name", "\u0084\u009f")
+        writer.characters("\u0084\u009f")
+        writer.startCDATA().characters("\u0084\u009f").endCDATA()
+        writer.endElement()
+
+        then:
+        xml.contains('<root name="&#x84;&#x9f;">&#x84;&#x9f;<![CDATA[]]>&#x84;<![CDATA[]]>&#x9f;<![CDATA[]]></root>')
+    }
+
+    def "replaces illegal characters in text content"() {
+        given:
+
+        when:
+        writer.startElement("root")
+        writer.characters(chars)
+        writer.startElement("broken").attribute("name", chars)
+        writer.startCDATA().characters(chars).endCDATA()
+        writer.endElement()
+        writer.endElement()
+
+        then:
+        xml.contains('<root>?<broken name="?"><![CDATA[?]]></broken></root>')
+
+        where:
+        chars << ["\u0000", "\ud800", "\udfff", "\ufffe"]
+    }
+
+    def "is a Writer implementation that escapes characters"() {
+        when:
+        writer.startElement("root")
+        writer.write("some <chars>")
+        writer.write(" and ".toCharArray())
+        writer.write("x some x".toCharArray(), 2, 4)
+        writer.write(' ')
+        writer.startCDATA()
+        writer.write("cdata")
+        writer.endCDATA()
+        writer.endElement()
+
+        then:
+        xml.contains("<root>some <chars> and some <![CDATA[cdata]]></root>")
+    }
+
+    def "cannot end element when stack is empty"() {
+        writer.startElement("root")
+        writer.endElement()
+
+        when:
+        writer.endElement()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot end element, as there are no started elements.'
+    }
+
+    def "cannot write characters when stack is empty"() {
+        when:
+        writer.characters("text")
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot write text, as there are no started elements.'
+
+        given:
+
+        when:
+        writer.startElement("root")
+        writer.endElement()
+        writer.characters("text")
+
+        then:
+        e = thrown()
+        e.message == 'Cannot write text, as there are no started elements.'
+    }
+
+    def "cannot end element when CDATA node is open"() {
+        writer.startElement("root")
+        writer.startCDATA()
+
+        when:
+        writer.endElement()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot end element, as current CDATA node has not been closed.'
+    }
+
+    def "cannot start element when CDATA node is open"() {
+        writer.startElement("root")
+        writer.startCDATA()
+
+        when:
+        writer.startElement("nested")
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot start element, as current CDATA node has not been closed.'
+    }
+
+    def "cannot start CDATA node when CDATA node is open"() {
+        writer.startElement("root")
+        writer.startCDATA()
+
+        when:
+        writer.startCDATA()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot start CDATA node, as current CDATA node has not been closed.'
+    }
+
+    def "cannot end CDATA node when not in a CDATA node"() {
+        writer.startElement("root")
+
+        when:
+        writer.endCDATA()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot end CDATA node, as not currently in a CDATA node.'
+    }
+
+    def "closes tags"() {
+        when:
+        writer.startElement("root")
+        action.call(writer)
+        writer.endElement()
+
+        then:
+        sw.toString().contains("<root>") //is closed with '>'
+
+        where:
+        action << [{ it.startElement("foo"); it.endElement() },
+                { it.startCDATA(); it.endCDATA() },
+                { it.characters("bar") },
+                { it.write("close") }]
+    }
+
+    def "closes attributed tags"() {
+        when:
+        writer.startElement("root")
+        writer.attribute("foo", '115')
+        action.call(writer)
+        writer.endElement()
+
+        then:
+        sw.toString().contains('<root foo="115">') //is closed with '>'
+
+        where:
+        action << [{ it.startElement("foo"); it.endElement() },
+                { it.startCDATA(); it.endCDATA() },
+                { it.characters("bar") },
+                { it.write("close") }]
+    }
+
+    def "outputs empty element when element has no content"() {
+        when:
+        writer.startElement("root")
+        writer.startElement("empty").endElement()
+        writer.startElement("empty").attribute("property", "value").endElement()
+        writer.endElement()
+
+        then:
+        xml.contains('<root><empty/><empty property="value"/></root>')
+    }
+
+    def "writes indented XML when enabled"() {
+        sw.reset()
+        def writer = new SimpleXmlWriter(sw, "    ")
+
+        when:
+        writer.startElement("root").attribute("items", "9")
+        writer.startElement("item").endElement()
+        writer.startElement("item").characters("some text").endElement()
+        writer.startElement("item")
+        writer.startElement("nested-1")
+        writer.startElement("nested-2").characters(" ").endElement()
+        writer.endElement()
+        writer.endElement()
+        writer.startElement("item")
+        writer.startElement("thing").characters("some text").endElement()
+        writer.startElement("thing").startCDATA().characters("some text").endCDATA().endElement()
+        writer.endElement()
+        writer.startElement("mixed")
+        writer.characters("text")
+        writer.startElement("mixed-1").endElement()
+        writer.characters("text")
+        writer.startElement("mixed-2").characters("123").endElement()
+        writer.startElement("mixed-3").startElement("empty").endElement().endElement()
+        writer.characters("text")
+        writer.endElement()
+        writer.endElement()
+
+        then:
+        xml == TextUtil.toPlatformLineSeparators('''<?xml version="1.0" encoding="UTF-8"?>
+<root items="9">
+    <item/>
+    <item>some text</item>
+    <item>
+        <nested-1>
+            <nested-2> </nested-2>
+        </nested-1>
+    </item>
+    <item>
+        <thing>some text</thing>
+        <thing><![CDATA[some text]]></thing>
+    </item>
+    <mixed>text
+        <mixed-1/>text
+        <mixed-2>123</mixed-2>
+        <mixed-3>
+            <empty/>
+        </mixed-3>text</mixed>
+</root>
+''')
+    }
+
+    def "allows valid tag names"() {
+        when:
+        writer.startElement(name)
+
+        then:
+        notThrown(IllegalArgumentException)
+
+        where:
+        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300", "ns:foo"]
+    }
+
+    def "validates tag names"() {
+        when:
+        writer.startElement(name)
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Invalid element name: '$name'"
+
+        where:
+        name << ["tag with space", "", "-invalid-start-char", "  ", "912", "\u00d7", "ns:foo:bar"]
+    }
+
+    def "allows valid attribute names"() {
+        when:
+        writer.startElement("foo").attribute(name, "foo")
+
+        then:
+        notThrown(IllegalArgumentException)
+
+        where:
+        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300", "ns:foo"]
+    }
+
+    def "validates attribute names"() {
+        when:
+        writer.startElement("foo").attribute(name, "foo")
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Invalid attribute name: '$name'"
+
+        where:
+        name << ["attribute with space", "", "-invalid-start-char", "  ", "912", "\u00d7", "ns:foo:bar"]
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlTransformerTest.groovy
new file mode 100644
index 0000000..313f060
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlTransformerTest.groovy
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.xml
+
+import org.gradle.api.Action
+import org.gradle.api.XmlProvider
+import org.gradle.api.internal.DomNode
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import javax.xml.parsers.DocumentBuilderFactory
+
+class XmlTransformerTest extends Specification {
+    final XmlTransformer transformer = new XmlTransformer()
+    @Rule TestNameTestDirectoryProvider tmpDir
+
+    def "returns original string when no actions are provided"() {
+        expect:
+        looksLike '<root/>', transformer.transform('<root/>')
+    }
+
+    def "action can access XML as StringBuilder"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<root/>')
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            def builder = provider.asString()
+            builder.insert(builder.indexOf("root"), 'some-')
+        }
+        looksLike '<some-root/>', result
+    }
+
+    def "action can access XML as Node"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<root/>')
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child1')
+        }
+        looksLike '<root>\n  <child1/>\n</root>\n', result
+    }
+
+    def "action can access XML as DOM element"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<root/>')
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            def document = provider.asElement().ownerDocument
+            provider.asElement().appendChild(document.createElement('child1'))
+        }
+        looksLike '<root>\n  <child1/>\n</root>\n', result
+    }
+
+    def "can transform String to a Writer"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        StringWriter writer = new StringWriter()
+
+        when:
+        transformer.transform('<root/>', writer)
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child1')
+        }
+        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
+    }
+
+    def "can transform String to an OutputStream"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        def outputStream = new ByteArrayOutputStream()
+
+        when:
+        transformer.transform('<root/>', outputStream)
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child\u03b1')
+        }
+        looksLike '<root>\n  <child\u03b1/>\n</root>\n', outputStream.toByteArray()
+    }
+
+    def "can transform Node to a Writer"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        StringWriter writer = new StringWriter()
+        Node node = new XmlParser().parseText('<root/>')
+
+        when:
+        transformer.transform(node, writer)
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child1')
+        }
+        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
+    }
+
+    def "can transform Node to an OutputStream"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        def outputStream = new ByteArrayOutputStream()
+        Node node = new XmlParser().parseText('<root/>')
+
+        when:
+        transformer.transform(node, outputStream)
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child\u03b1')
+        }
+        looksLike '<root>\n  <child\u03b1/>\n</root>\n', outputStream.toByteArray()
+    }
+
+    def "can transform Node to a File"() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        File file = tmpDir.file("out.xml")
+        Node node = new XmlParser().parseText('<root/>')
+
+        when:
+        transformer.transform(node, file)
+
+        then:
+        action.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child\u03b1')
+        }
+        looksLike '<root>\n  <child\u03b1/>\n</root>\n', file.bytes
+    }
+
+    def "can use a closure as an action"() {
+        transformer.addAction { provider ->
+            provider.asNode().appendNode('child1')
+        }
+        StringWriter writer = new StringWriter()
+
+        when:
+        transformer.transform('<root/>', writer)
+
+        then:
+        looksLike '<root>\n  <child1/>\n</root>\n', writer.toString()
+    }
+
+    def "can chain actions"() {
+        Action<XmlProvider> stringAction = Mock()
+        Action<XmlProvider> nodeAction = Mock()
+        Action<XmlProvider> elementAction = Mock()
+        Action<XmlProvider> stringAction2 = Mock()
+        transformer.addAction(stringAction)
+        transformer.addAction(elementAction)
+        transformer.addAction(nodeAction)
+        transformer.addAction(stringAction2)
+
+        when:
+        def result = transformer.transform('<root/>')
+
+        then:
+        stringAction.execute(_) >> { XmlProvider provider ->
+            def builder = provider.asString()
+            builder.insert(builder.indexOf("root"), 'some-')
+        }
+        nodeAction.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child2')
+        }
+        elementAction.execute(_) >> { XmlProvider provider ->
+            def document = provider.asElement().ownerDocument
+            provider.asElement().appendChild(document.createElement('child1'))
+        }
+        stringAction2.execute(_) >> { XmlProvider provider ->
+            provider.asString().append('<!-- end -->')
+        }
+
+        looksLike '<some-root>\n  <child1/>\n  <child2/>\n</some-root>\n<!-- end -->', result
+    }
+
+    def "can chain node actions"() {
+        Action<XmlProvider> nodeAction = Mock()
+        Action<XmlProvider> nodeAction2 = Mock()
+        transformer.addAction(nodeAction)
+        transformer.addAction(nodeAction2)
+
+        when:
+        def result = transformer.transform('<root/>')
+
+        then:
+        nodeAction.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child1')
+        }
+        nodeAction2.execute(_) >> { XmlProvider provider ->
+            provider.asNode().appendNode('child2')
+        }
+        looksLike '<root>\n  <child1/>\n  <child2/>\n</root>\n', result
+    }
+
+    def "indentation correct when writing out Node"() {
+        transformer.indentation = "\t"
+        transformer.addAction { XmlProvider provider -> provider.asNode().children()[0].appendNode("grandchild") }
+
+        when:
+        def result = transformer.transform("<root>\n  <child/>\n</root>\n")
+
+        then:
+        looksLike "<root>\n\t<child>\n\t\t<grandchild/>\n\t</child>\n</root>\n", result
+    }
+
+    def "can add DOCTYPE along with nodes"() {
+        transformer.addAction { it.asNode().appendNode('someChild') }
+        transformer.addAction {
+            def s = it.asString()
+            s.insert(s.indexOf("?>") + 2, '\n<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">')
+        }
+
+        when:
+        def result = transformer.transform("<root></root>")
+
+        then:
+        looksLike "<!DOCTYPE application PUBLIC \"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN\" \"http://java.sun.com/dtd/application_1_3.dtd\">\n<root>\n  <someChild/>\n</root>\n", result
+    }
+
+    def "can specify DOCTYPE when using DomNode"() {
+        StringWriter writer = new StringWriter()
+        def node = new DomNode('root')
+        node.publicId = 'public-id'
+        node.systemId = 'system-id'
+
+        when:
+        transformer.transform(node, writer)
+
+        then:
+        looksLike '''<!DOCTYPE root PUBLIC "public-id" "system-id">
+<root/>
+''', writer.toString()
+    }
+
+    def "DOCTYPE is preserved when transformed as a Node"() {
+        StringWriter writer = new StringWriter()
+        def node = new DomNode('root')
+        node.publicId = 'public-id'
+        node.systemId = 'system-id'
+        transformer.addAction { it.asNode().appendNode('someChild') }
+
+        when:
+        transformer.transform(node, writer)
+
+        then:
+        looksLike '''<!DOCTYPE root PUBLIC "public-id" "system-id">
+<root>
+  <someChild/>
+</root>
+''', writer.toString()
+    }
+
+    def "DOCTYPE is preserved when transformed as a DOM element"() {
+        StringWriter writer = new StringWriter()
+        def node = new DomNode('root')
+        node.publicId = 'public-id'
+        node.systemId = tmpDir.createFile("thing.dtd").toURI()
+        transformer.addAction { it.asElement().appendChild(it.asElement().ownerDocument.createElement('someChild')) }
+
+        when:
+        transformer.transform(node, writer)
+
+        then:
+        looksLike """<!DOCTYPE root PUBLIC "public-id" "${node.getSystemId()}">
+<root>
+  <someChild/>
+</root>
+""", writer.toString()
+    }
+
+    def "indentation correct when writing out DOM element (only) if indenting with spaces"() {
+        transformer.indentation = expected
+        transformer.addAction { XmlProvider provider ->
+            def document = provider.asElement().ownerDocument
+            document.getElementsByTagName("child").item(0).appendChild(document.createElement("grandchild"))
+        }
+
+        when:
+        def result = transformer.transform("<root>\n  <child/>\n</root>\n")
+
+        then:
+        looksLike("<root>\n$actual<child>\n$actual$actual<grandchild/>\n$actual</child>\n</root>\n", result)
+
+        where:
+        expected | actual
+        "    "   | "    "
+        "\t"     | "  " // tabs not supported, two spaces used instead
+    }
+
+    def "can use with action api"() {
+        given:
+        def writer = new StringWriter()
+        def input = "<things><thing/></things>"
+        def generator = new Action<Writer>() {
+            void execute(Writer t) {
+                t.write(input)
+            }
+        }
+
+        when:
+        transformer.transform(writer, generator)
+
+        then:
+        looksLike(input, writer.toString())
+
+        when:
+        writer.buffer.setLength(0)
+        transformer.addAction(new Action<XmlProvider>() {
+            void execute(XmlProvider xml) {
+                xml.asNode().thing[0]. at foo = "bar"
+            }
+        })
+        transformer.transform(writer, generator)
+
+        then:
+        looksLike('<things>\n  <thing foo="bar"/>\n</things>', writer.toString())
+    }
+
+    private void looksLike(String expected, String actual) {
+        assert removeTrailingWhitespace(actual) == removeTrailingWhitespace(TextUtil.toPlatformLineSeparators("<?xml version=\"1.0\"?>\n" + expected))
+    }
+
+    private void looksLike(String expected, byte[] actual) {
+        DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(actual))
+        assert removeTrailingWhitespace(new String(actual, "utf-8")) == removeTrailingWhitespace(TextUtil.toPlatformLineSeparators("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + expected))
+    }
+
+    private String removeTrailingWhitespace(String value) {
+        return value.replaceFirst('(?s)\\s+$', "")
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlValidationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlValidationTest.groovy
new file mode 100644
index 0000000..894d880
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/xml/XmlValidationTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.xml
+
+import spock.lang.Specification
+
+class XmlValidationTest extends Specification {
+    def "catches poorly formed xml names" () {
+        expect:
+        ! XmlValidation.isValidXmlName(name)
+
+        where:
+        name        | _
+        ''          | _
+        'foo\t'     | _
+        'foo\\n'    | _
+        'foo/'      | _
+        'foo\\'     | _
+        'foo<'      | _
+        'foo>'      | _
+        'foo='      | _
+        'foo;'      | _
+        'foo⿰'     | _
+        'foo÷'      | _
+        'foo`'      | _
+        'foo\u2000' | _
+        'foo\u200e' | _
+        'foo\u2190' | _
+        'foo\u2ff0' | _
+        'foo\uf8ff' | _
+        'foo\ufdd0' | _
+        'foo\ufffe' | _
+        '-foo'      | _
+        '1foo'      | _
+        '.foo'      | _
+        '\u0300foo' | _
+        '\u203ffoo' | _
+    }
+
+    def "allows well formed xml names" () {
+        expect:
+        XmlValidation.isValidXmlName(name)
+
+        where:
+        name             | _
+        'foo'            | _
+        'FOO'            | _
+        'foo-dash'       | _
+        'foo.dot'        | _
+        'foo123'         | _
+        'ns:foo'         | _
+        'foo_underscore' | _
+        ':foo'           | _
+        '_foo'           | _
+        '\u00c0foo'      | _
+        '\u00d8foo'      | _
+        '\u00f8foo'      | _
+        '\u0370foo'      | _
+        '\u037ffoo'      | _
+        '\u200cfoo'      | _
+        '\u2070foo'      | _
+        '\u2c00foo'      | _
+        '\u3001foo'      | _
+        '\uf900foo'      | _
+        '\ufdf0foo'      | _
+        'foo\u0300'      | _
+        'foo\u203f'      | _
+    }
+
+    def "identifies illegal character" () {
+        expect:
+        ! XmlValidation.isLegalCharacter(character as char)
+
+        where:
+        character | _
+        0x0019    | _
+        0xd800    | _
+        0xdfff    | _
+        0xfffe    | _
+        0x10000   | _
+    }
+
+    def "identifies legal character" () {
+        expect:
+        XmlValidation.isLegalCharacter(character as char)
+
+        where:
+        character | _
+        0x0009    | _
+        0x000a    | _
+        0x000d    | _
+        0x0020    | _
+        0xd7ff    | _
+        0xe000    | _
+        0xfffd    | _
+    }
+}
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 54fd2dc..75b3e98 100644
--- a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
@@ -23,22 +23,25 @@ import org.gradle.api.Action;
 import org.gradle.api.Project;
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.internal.AsmBackedClassGenerator;
 import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
-import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.plugins.PluginManager;
+import org.gradle.api.internal.plugins.DefaultPluginManager;
 import org.gradle.api.internal.project.ProjectInternal;
 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.initialization.ClassLoaderScopeRegistry;
 import org.gradle.internal.classloader.MultiParentClassLoader;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.util.GradleVersion;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.TestUtil;
@@ -51,6 +54,7 @@ import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Type;
 
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.sameInstance;
@@ -64,7 +68,6 @@ public class DefaultGradleTest {
     private final ScriptHandler scriptHandlerMock = context.mock(ScriptHandler.class);
     private final ServiceRegistryFactory serviceRegistryFactoryMock = context.mock(ServiceRegistryFactory.class, "parent");
     private final ServiceRegistry gradleServiceRegistryMock = context.mock(ServiceRegistry.class, "gradle");
-    private final PluginRegistry pluginRegistry = context.mock(PluginRegistry.class);
     private final TaskGraphExecuter taskExecuter = context.mock(TaskGraphExecuter.class);
     private final ListenerManager listenerManager = context.mock(ListenerManager.class);
     private final Gradle parent = context.mock(Gradle.class, "parentBuild");
@@ -76,7 +79,9 @@ public class DefaultGradleTest {
     private final PluginContainer pluginContainer = context.mock(PluginContainer.class);
     private final ScriptPluginFactory scriptPluginFactory = context.mock(ScriptPluginFactory.class);
     private final ScriptHandlerFactory scriptHandlerFactory = context.mock(ScriptHandlerFactory.class);
+    private final ClassLoaderScopeRegistry classLoaderScopeRegistry = context.mock(ClassLoaderScopeRegistry.class);
     private final ClassLoaderScope classLoaderScope = context.mock(ClassLoaderScope.class);
+    private final PluginManager pluginManager = context.mock(DefaultPluginManager.class);
 
     private DefaultGradle gradle;
 
@@ -85,34 +90,38 @@ public class DefaultGradleTest {
         context.checking(new Expectations() {{
             one(serviceRegistryFactoryMock).createFor(with(any(DefaultGradle.class)));
             will(returnValue(gradleServiceRegistryMock));
-            allowing(gradleServiceRegistryMock).get(ScriptHandler.class);
+            allowing(gradleServiceRegistryMock).get((Type)ScriptHandler.class);
             will(returnValue(scriptHandlerMock));
-            allowing(gradleServiceRegistryMock).get(ClassLoaderScope.class);
+            allowing(gradleServiceRegistryMock).get(ClassLoaderScopeRegistry.class);
+            will(returnValue(classLoaderScopeRegistry));
+            allowing(classLoaderScopeRegistry).getCoreAndPluginsScope();
             will(returnValue(classLoaderScope));
-            allowing(gradleServiceRegistryMock).get(PluginRegistry.class);
-            will(returnValue(pluginRegistry));
-            allowing(gradleServiceRegistryMock).get(TaskGraphExecuter.class);
+            allowing(gradleServiceRegistryMock).get((Type)DefaultPluginManager.class);
+            will(returnValue(pluginManager));
+            allowing(gradleServiceRegistryMock).get((Type)TaskGraphExecuter.class);
             will(returnValue(taskExecuter));
-            allowing(gradleServiceRegistryMock).get(ListenerManager.class);
+            allowing(gradleServiceRegistryMock).get((Type)ListenerManager.class);
             will(returnValue(listenerManager));
             allowing(gradleServiceRegistryMock).get(MultiParentClassLoader.class);
             will(returnValue(scriptClassLoaderMock));
-            allowing(gradleServiceRegistryMock).get(GradleDistributionLocator.class);
+            allowing(gradleServiceRegistryMock).get((Type)GradleDistributionLocator.class);
             will(returnValue(gradleDistributionLocatorMock));
             allowing(gradleServiceRegistryMock).get(PluginContainer.class);
             will(returnValue(pluginContainer));
-            allowing(gradleServiceRegistryMock).get(FileResolver.class);
+            allowing(gradleServiceRegistryMock).get((Type)FileResolver.class);
             will(returnValue(fileResolverMock));
-            allowing(gradleServiceRegistryMock).get(ScriptPluginFactory.class);
+            allowing(gradleServiceRegistryMock).get((Type)ScriptPluginFactory.class);
             will(returnValue(scriptPluginFactory));
-            allowing(gradleServiceRegistryMock).get(ScriptHandlerFactory.class);
+            allowing(gradleServiceRegistryMock).get((Type)ScriptHandlerFactory.class);
             will(returnValue(scriptHandlerFactory));
             allowing(listenerManager).createAnonymousBroadcaster(BuildListener.class);
             will(returnValue(buildListenerBroadcast));
             allowing(listenerManager).createAnonymousBroadcaster(ProjectEvaluationListener.class);
             will(returnValue(projectEvaluationListenerBroadcast));
         }});
-        gradle = new DefaultGradle(parent, parameter, serviceRegistryFactoryMock);
+
+        AsmBackedClassGenerator classGenerator = new AsmBackedClassGenerator();
+        gradle = classGenerator.newInstance(DefaultGradle.class, parent, parameter, serviceRegistryFactoryMock);
     }
 
     @Test
diff --git a/subprojects/core/src/test/groovy/org/gradle/listener/DefaultListenerManagerTest.java b/subprojects/core/src/test/groovy/org/gradle/listener/DefaultListenerManagerTest.java
deleted file mode 100644
index e150cea..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/listener/DefaultListenerManagerTest.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.listener;
-
-import org.jmock.Expectations;
-import org.jmock.Sequence;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
- at RunWith(JMock.class)
-public class DefaultListenerManagerTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final ListenerManager manager = new DefaultListenerManager();
-
-    private final TestFooListener fooListener1 = context.mock(TestFooListener.class, "foo listener 1");
-    private final TestFooListener fooListener2 = context.mock(TestFooListener.class, "foo listener 2");
-    private final TestFooListener fooListener3 = context.mock(TestFooListener.class, "foo listener 3");
-    private final TestFooListener fooListener4 = context.mock(TestFooListener.class, "foo listener 4");
-    private final TestBarListener barListener1 = context.mock(TestBarListener.class, "bar listener 1");
-
-    @Test
-    public void canAddListenerBeforeObtainingBroadcaster() {
-        manager.addListener(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        manager.getBroadcaster(TestFooListener.class).foo("param");
-    }
-
-    @Test
-    public void canAddListenerAfterObtainingBroadcaster() {
-        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
-
-        manager.addListener(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        broadcaster.foo("param");
-    }
-
-    @Test
-    public void canAddLoggerBeforeObtainingBroadcaster() {
-        manager.useLogger(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        manager.getBroadcaster(TestFooListener.class).foo("param");
-    }
-
-    @Test
-    public void canAddLoggerAfterObtainingBroadcaster() {
-        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
-
-        manager.useLogger(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        broadcaster.foo("param");
-    }
-
-    @Test
-    public void addedListenersGetMessagesInOrderAdded() {
-        context.checking(new Expectations() {{
-            Sequence sequence = context.sequence("sequence");
-            one(fooListener1).foo("param"); inSequence(sequence);
-            one(fooListener2).foo("param"); inSequence(sequence);
-            one(fooListener3).foo("param"); inSequence(sequence);
-            one(fooListener4).foo("param"); inSequence(sequence);
-        }});
-
-        manager.addListener(fooListener1);
-        manager.addListener(barListener1);
-        manager.addListener(fooListener2);
-        manager.addListener(fooListener3);
-
-        // get the broadcaster and then add more listeners (because broadcasters
-        // are cached and so must be maintained correctly after getting defined
-        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
-
-        manager.addListener(fooListener4);
-
-        broadcaster.foo("param");
-    }
-
-    @Test
-    public void cachesBroadcasters() {
-        assertSame(manager.getBroadcaster(TestFooListener.class), manager.getBroadcaster(TestFooListener.class));
-    }
-
-    @Test
-    public void removedListenersDontGetMessages() {
-        manager.addListener(fooListener1);
-        manager.addListener(fooListener2);
-
-        manager.removeListener(fooListener2);
-
-        TestFooListener testFooListener = manager.getBroadcaster(TestFooListener.class);
-
-        manager.removeListener(fooListener1);
-
-        testFooListener.foo("param");
-    }
-
-    @Test
-    public void replacedLoggersDontGetMessages() {
-        context.checking(new Expectations() {{
-            one(fooListener4).foo("param");
-        }});
-
-        manager.useLogger(fooListener1);
-        manager.useLogger(fooListener2);
-
-        TestFooListener testFooListener = manager.getBroadcaster(TestFooListener.class);
-
-        manager.useLogger(fooListener3);
-        manager.useLogger(fooListener4);
-
-        testFooListener.foo("param");
-    }
-
-    @Test
-    public void listenerReceivesEventsFromAnonymousBroadcasters() {
-        manager.addListener(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        manager.createAnonymousBroadcaster(TestFooListener.class).getSource().foo("param");
-    }
-
-    @Test
-    public void listenerReceivesEventsFromChildren() {
-        manager.addListener(fooListener1);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-
-        manager.createChild().getBroadcaster(TestFooListener.class).foo("param");
-    }
-    
-    @Test
-    public void listenerDoesNotReceiveEventsFromParent() {
-        manager.createChild().addListener(fooListener1);
-
-        manager.getBroadcaster(TestFooListener.class).foo("param");
-    }
-
-    @Test
-    public void loggerReceivesEventsFromChildren() {
-        manager.useLogger(fooListener1);
-
-        ListenerManager child = manager.createChild();
-        TestFooListener broadcaster = child.getBroadcaster(TestFooListener.class);
-
-        context.checking(new Expectations() {{
-            one(fooListener1).foo("param");
-        }});
-        broadcaster.foo("param");
-
-        manager.useLogger(fooListener2);
-
-        context.checking(new Expectations() {{
-            one(fooListener2).foo("param");
-        }});
-        broadcaster.foo("param");
-    }
-
-    @Test
-    public void loggerDoesNotReceiveEventsFromParent() {
-        manager.createChild().useLogger(fooListener1);
-
-        manager.getBroadcaster(TestFooListener.class).foo("param");
-    }
-
-    @Test
-    public void loggerInChildHasPrecedenceOverLoggerInParent() {
-        manager.useLogger(fooListener1);
-
-        ListenerManager child = manager.createChild();
-        TestFooListener broadcaster = child.getBroadcaster(TestFooListener.class);
-
-        child.useLogger(fooListener2);
-
-        context.checking(new Expectations() {{
-            one(fooListener2).foo("param");
-        }});
-
-        broadcaster.foo("param");
-    }
-
-    public interface TestFooListener {
-        void foo(String param);
-    }
-
-    public interface TestBarListener {
-        void bar(int value);
-    }
-}
-
diff --git a/subprojects/core/src/test/groovy/org/gradle/listener/LazyCreationProxyTest.groovy b/subprojects/core/src/test/groovy/org/gradle/listener/LazyCreationProxyTest.groovy
deleted file mode 100644
index e757aa0..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/listener/LazyCreationProxyTest.groovy
+++ /dev/null
@@ -1,90 +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.listener
-
-import spock.lang.Specification
-import org.gradle.internal.Factory
-import java.util.concurrent.Callable
-
-class LazyCreationProxyTest extends Specification {
-    final Factory<Callable<String>> factory = Mock()
-    final Callable<String> callable = Mock()
-
-    def "instantiates and caches object on first method invocation on source"() {
-        when:
-        def proxy = new LazyCreationProxy<Callable<String>>(Callable, factory)
-        def source = proxy.source
-
-        then:
-        0 * factory._
-
-        when:
-        def result = source.call()
-
-        then:
-        result == 'a'
-        1 * factory.create() >> callable
-        1 * callable.call() >> 'a'
-        0 * factory._
-        0 * callable._
-
-        when:
-        result = source.call()
-
-        then:
-        result == 'b'
-        1 * callable.call() >> 'b'
-        0 * factory._
-        0 * callable._
-    }
-
-    def "rethrows exception thrown by factory on creation"() {
-        def failure = new RuntimeException()
-        
-        given:
-        def proxy = new LazyCreationProxy<Callable<String>>(Callable, factory)
-        def source = proxy.source
-
-        when:
-        source.call()
-
-        then:
-        Exception e = thrown(Exception)
-        e == failure
-
-        and:
-        _ * factory.create() >> { throw failure }
-    }
-
-    def "rethrows checked exception thrown by method call on target object"() {
-        def failure = new IOException()
-
-        given:
-        def proxy = new LazyCreationProxy<Callable<String>>(Callable, factory)
-        def source = proxy.source
-        _ * factory.create() >> callable
-
-        when:
-        source.call()
-
-        then:
-        Exception e = thrown(Exception)
-        e == failure
-
-        and:
-        _ * callable.call() >> { throw failure }
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/listener/ListenerBroadcastTest.java b/subprojects/core/src/test/groovy/org/gradle/listener/ListenerBroadcastTest.java
deleted file mode 100644
index 2332720..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/listener/ListenerBroadcastTest.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.listener;
-
-import org.gradle.api.Action;
-import org.gradle.messaging.dispatch.Dispatch;
-import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.util.JUnit4GroovyMockery;
-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.Test;
-import org.junit.runner.RunWith;
-
-import static org.gradle.util.Matchers.strictlyEqual;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class ListenerBroadcastTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private final ListenerBroadcast<TestListener> broadcast = new ListenerBroadcast<TestListener>(TestListener.class);
-
-    @Test
-    public void createsSourceObject() {
-        assertThat(broadcast.getSource(), notNullValue());
-        assertThat(broadcast.getSource(), strictlyEqual(broadcast.getSource()));
-        assertFalse(broadcast.getSource().equals(new ListenerBroadcast<TestListener>(TestListener.class).getSource()));
-        assertEquals(broadcast.getSource().hashCode(), broadcast.getSource().hashCode());
-        assertThat(broadcast.getSource().toString(), equalTo("TestListener broadcast"));
-    }
-
-    @Test
-    public void getTypeIsCorrect() {
-        assertThat(broadcast.getType(), equalTo(TestListener.class));
-    }
-
-    @Test
-    public void sourceObjectDoesNothingWhenNoListenersAdded() {
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void sourceObjectNotifiesEachListenerInOrderAdded() {
-        final TestListener listener1 = context.mock(TestListener.class, "listener1");
-        final TestListener listener2 = context.mock(TestListener.class, "listener2");
-
-        context.checking(new Expectations() {{
-            one(listener1).event1("param");
-            one(listener2).event1("param");
-        }});
-
-        broadcast.add(listener1);
-        broadcast.add(listener2);
-
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void canDispatchEventToListeners() throws NoSuchMethodException {
-        final TestListener listener1 = context.mock(TestListener.class, "listener1");
-        final TestListener listener2 = context.mock(TestListener.class, "listener2");
-
-        context.checking(new Expectations() {{
-            one(listener1).event1("param");
-            one(listener2).event1("param");
-        }});
-
-        broadcast.add(listener1);
-        broadcast.add(listener2);
-
-        MethodInvocation invocation = new MethodInvocation(TestListener.class.getMethod("event1", String.class), new Object[]{"param"});
-        broadcast.dispatch(invocation);
-    }
-
-    @Test
-    public void listenerIsNotUsedAfterItIsRemoved() {
-        TestListener listener = context.mock(TestListener.class);
-
-        broadcast.add(listener);
-        broadcast.remove(listener);
-
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void canUseDispatchToReceiveNotifications() throws NoSuchMethodException {
-        final Dispatch<MethodInvocation> dispatch1 = context.mock(Dispatch.class, "listener1");
-        final Dispatch<MethodInvocation> dispatch2 = context.mock(Dispatch.class, "listener2");
-        final MethodInvocation invocation = new MethodInvocation(TestListener.class.getMethod("event1", String.class), new Object[]{"param"});
-
-        context.checking(new Expectations() {{
-            one(dispatch1).dispatch(invocation);
-            one(dispatch2).dispatch(invocation);
-        }});
-
-        broadcast.add(dispatch1);
-        broadcast.add(dispatch2);
-
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void dispatchIsNotUsedAfterItIsRemoved() {
-        Dispatch<MethodInvocation> dispatch = context.mock(Dispatch.class);
-
-        broadcast.add(dispatch);
-        broadcast.remove(dispatch);
-
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void canUseActionForSingleEventMethod() {
-        final Action<String> action = context.mock(Action.class);
-        context.checking(new Expectations() {{
-            one(action).execute("param");
-        }});
-
-        broadcast.add("event1", action);
-        broadcast.getSource().event1("param");
-    }
-
-    @Test
-    public void doesNotNotifyActionForOtherEventMethods() {
-        final Action<String> action = context.mock(Action.class);
-
-        broadcast.add("event1", action);
-        broadcast.getSource().event2(9, "param");
-    }
-
-    @Test
-    public void actionCanHaveFewerParametersThanEventMethod() {
-        final Action<Integer> action = context.mock(Action.class);
-        context.checking(new Expectations() {{
-            one(action).execute(1);
-            one(action).execute(2);
-        }});
-        broadcast.add("event2", action);
-        broadcast.getSource().event2(1, "param");
-        broadcast.getSource().event2(2, null);
-    }
-
-    @Test
-    public void listenerCanAddAnotherListener() {
-        final TestListener listener1 = context.mock(TestListener.class, "listener1");
-        final TestListener listener2 = context.mock(TestListener.class, "listener2");
-        final TestListener listener3 = context.mock(TestListener.class, "listener3");
-
-        broadcast.add(listener1);
-        broadcast.add(listener2);
-
-        context.checking(new Expectations() {{
-            ignoring(listener2);
-            one(listener1).event1("event");
-            will(new org.jmock.api.Action() {
-                public void describeTo(Description description) {
-                    description.appendText("add listener");
-                }
-
-                public Object invoke(Invocation invocation) throws Throwable {
-                    broadcast.add(listener3);
-                    return null;
-                }
-            });
-        }});
-
-        broadcast.getSource().event1("event");
-    }
-
-    @Test
-    public void wrapsCheckedExceptionThrownByListener() throws Exception {
-        final TestListener listener = context.mock(TestListener.class);
-        final Exception failure = new Exception();
-
-        context.checking(new Expectations() {{
-            one(listener).event3();
-            will(throwException(failure));
-        }});
-
-        broadcast.add(listener);
-
-        try {
-            broadcast.getSource().event3();
-            fail();
-        } catch (ListenerNotificationException e) {
-            assertThat(e.getMessage(), equalTo("Failed to notify test listener."));
-            assertThat(e.getCause(), sameInstance((Throwable) failure));
-        }
-    }
-
-    @Test
-    public void attemptsToNotifyAllOtherListenersWhenOneThrowsException() {
-        final TestListener listener1 = context.mock(TestListener.class);
-        final TestListener listener2 = context.mock(TestListener.class);
-        final RuntimeException failure = new RuntimeException();
-
-        context.checking(new Expectations() {{
-            one(listener1).event1("param");
-            will(throwException(failure));
-            one(listener2).event1("param");
-        }});
-
-        broadcast.add(listener1);
-        broadcast.add(listener2);
-
-        try {
-            broadcast.getSource().event1("param");
-            fail();
-        } catch (RuntimeException e) {
-            assertThat(e, sameInstance(failure));
-        }
-    }
-
-    @Test
-    public void attemptsToNotifyAllOtherListenersWhenMultipleThrowException() {
-        final TestListener listener1 = context.mock(TestListener.class);
-        final TestListener listener2 = context.mock(TestListener.class);
-        final TestListener listener3 = context.mock(TestListener.class);
-        final RuntimeException failure1 = new RuntimeException();
-        final RuntimeException failure2 = new RuntimeException();
-
-        context.checking(new Expectations() {{
-            one(listener1).event1("param");
-            will(throwException(failure1));
-            one(listener2).event1("param");
-            will(throwException(failure2));
-            one(listener3).event1("param");
-        }});
-
-        broadcast.add(listener1);
-        broadcast.add(listener2);
-        broadcast.add(listener3);
-
-        try {
-            broadcast.getSource().event1("param");
-            fail();
-        } catch (ListenerNotificationException e) {
-            assertThat(e.getCauses().size(), equalTo(2));
-            assertThat(e.getCauses().get(0), sameInstance((Throwable) failure1));
-            assertThat(e.getCauses().get(1), sameInstance((Throwable) failure2));
-        }
-    }
-
-    public interface TestListener {
-        void event1(String param);
-
-        void event2(int value, String other);
-
-        void event3() throws Exception;
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/ConfigureLogging.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/ConfigureLogging.groovy
deleted file mode 100644
index 601981f..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/logging/ConfigureLogging.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging
-
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.core.Appender
-import ch.qos.logback.classic.Logger
-import ch.qos.logback.classic.Level
-import org.junit.rules.ExternalResource
-import org.slf4j.LoggerFactory
-import ch.qos.logback.classic.LoggerContext
-
-import java.util.logging.LogManager
-
-class ConfigureLogging extends ExternalResource {
-    private final Appender<ILoggingEvent> appender;
-    private Logger logger;
-
-    def ConfigureLogging(appender) {
-        this.appender = appender;
-    }
-
-    @Override
-    protected void before() {
-        attachAppender()
-    }
-
-    @Override
-    protected void after() {
-        detachAppender()
-    }
-
-    public void attachAppender() {
-        logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("ROOT");
-        logger.detachAndStopAllAppenders()
-        logger.addAppender(appender)
-        logger.setLevel(Level.ALL)
-    }
-
-    public void detachAppender() {
-        logger.detachAppender(appender)
-        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory()
-        lc.reset()
-        LogManager.getLogManager().reset()
-    }
-
-    public void setLevel(Level level) {
-        logger.setLevel(level)
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
index c1b9570..a20e950 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
@@ -32,8 +32,8 @@ import spock.lang.Specification
 import java.util.logging.Logger
 
 class LoggingServiceRegistryTest extends Specification {
-    final TestAppender appender = new TestAppender()
-    @Rule ConfigureLogging logging = new ConfigureLogging(appender)
+    final TestOutputEventListener outputEventListener = new TestOutputEventListener()
+    @Rule ConfigureLogging logging = new ConfigureLogging(outputEventListener)
     @Rule RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
 
     def providesALoggingManagerFactory() {
@@ -82,7 +82,7 @@ class LoggingServiceRegistryTest extends Specification {
         logger.warn("before")
 
         then:
-        appender.toString() == '[WARN before]'
+        outputEventListener.toString() == '[WARN before]'
 
         when:
         loggingManager.level = LogLevel.INFO
@@ -91,7 +91,7 @@ class LoggingServiceRegistryTest extends Specification {
         logger.warn("warning")
 
         then:
-        appender.toString() == '[WARN before]'
+        outputEventListener.toString() == '[WARN before]'
     }
 
     def routesSlf4jToListenersWhenStarted() {
@@ -192,6 +192,30 @@ class LoggingServiceRegistryTest extends Specification {
         0 * listener._
     }
 
+    def routesStyledTextToListenersWhenStarted() {
+        StandardOutputListener listener = Mock()
+
+        when:
+        def registry = LoggingServiceRegistry.newCommandLineProcessLogging()
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+
+        then:
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+
+        when:
+        loggingManager.addStandardOutputListener(listener)
+        loggingManager.addStandardErrorListener(listener)
+        loggingManager.start()
+
+        def textOutput = registry.get(StyledTextOutputFactory).create("category")
+        textOutput.println("info")
+
+        then:
+        1 * listener.onOutput(TextUtil.toPlatformLineSeparators("info\n"))
+        0 * listener._
+    }
+
     def routesLoggingOutputToOriginalSystemOutAndErrWhenStarted() {
         given:
         def logger = LoggerFactory.getLogger("category")
@@ -263,12 +287,53 @@ class LoggingServiceRegistryTest extends Specification {
         System.err == outputs.stdErrPrintStream
     }
 
-    def canCreateANestedRegistry() {
+    def doesNotMessWithJavaUtilLoggingWhenNested() {
         given:
-        def registry = LoggingServiceRegistry.newEmbeddableLogging()
+        def registry = LoggingServiceRegistry.newNestedLogging()
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+        loggingManager.level = LogLevel.WARN
+        loggingManager.start()
+        def logger = Logger.getLogger("category")
 
-        expect:
-        def child = registry.newLogging()
-        child != null
+        when:
+        logger.warning("warning")
+        logger.severe("error")
+
+        then:
+        outputs.stdOut == ''
+        outputs.stdErr == ''
+    }
+
+    def doesNotMessWithSystemOutputAndErrorWhenNested() {
+        when:
+        def registry = LoggingServiceRegistry.newNestedLogging()
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+        loggingManager.level = LogLevel.WARN
+        loggingManager.start()
+
+        then:
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+
+    def doesNotRouteToSystemOutAndErrorWhenNested() {
+        StandardOutputListener listener = Mock()
+
+        when:
+        def registry = LoggingServiceRegistry.newNestedLogging()
+        def loggingManager = registry.newInstance(LoggingManagerInternal)
+        loggingManager.addStandardOutputListener(listener)
+        loggingManager.start()
+
+        def textOutput = registry.get(StyledTextOutputFactory).create("category")
+        textOutput.println("info")
+
+        then:
+        1 * listener.onOutput(TextUtil.toPlatformLineSeparators("info\n"))
+        0 * listener._
+
+        and:
+        outputs.stdOut == ''
+        outputs.stdErr == ''
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/TestAppender.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/TestAppender.groovy
deleted file mode 100644
index e62fce0..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/logging/TestAppender.groovy
+++ /dev/null
@@ -1,40 +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.logging
-
-import ch.qos.logback.core.AppenderBase
-
-class TestAppender<LoggingEvent> extends AppenderBase<LoggingEvent> {
-    final StringWriter writer = new StringWriter()
-
-    synchronized void doAppend(LoggingEvent e) {
-        append(e)
-    }
-
-    @Override
-    String toString() {
-        return writer.toString()
-    }
-
-    protected void append(LoggingEvent e) {
-        writer.append("[")
-        writer.append(e.level.toString())
-        writer.append(' ')
-        writer.append(e.formattedMessage)
-        writer.append("]")
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutputTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutputTest.groovy
index 8e38ee2..f62c6ab 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutputTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractLineChoppingStyledTextOutputTest.groovy
@@ -24,7 +24,7 @@ import spock.lang.Specification
 class AbstractLineChoppingStyledTextOutputTest extends Specification {
     @Rule final SetSystemProperties systemProperties = new SetSystemProperties()
     final StringBuilder result = new StringBuilder()
-    final String eol = SystemProperties.getLineSeparator()
+    final String eol = SystemProperties.instance.getLineSeparator()
 
     def "appends text to current line"() {
         def output = output()
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
index 5914f55..97ad093 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
@@ -43,7 +43,7 @@ class AbstractStyledTextOutputTest extends OutputSpecification {
         output.println()
 
         then:
-        output.rawValue == SystemProperties.lineSeparator
+        output.rawValue == SystemProperties.instance.lineSeparator
     }
 
     def appendsCharacter() {
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
index 34a18fb..af81b08 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
@@ -27,7 +27,7 @@ import org.junit.runner.RunWith
 
 @RunWith(JMock.class)
 class AnsiConsoleTest {
-    private static final String EOL = SystemProperties.lineSeparator
+    private static final String EOL = SystemProperties.instance.lineSeparator
 
     private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
     private final Ansi ansi = context.mock(Ansi.class)
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
index 4394931..a582e85 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.logging.internal
 
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData
 import spock.lang.Subject
 
 class ConsoleBackedProgressRendererTest extends OutputSpecification {
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
index 355b1a9..cd805be 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
@@ -22,7 +22,7 @@ import org.junit.Rule
 import spock.lang.Specification
 
 class DefaultStandardOutputRedirectorTest extends Specification {
-    private static final String EOL = SystemProperties.lineSeparator
+    private static final String EOL = SystemProperties.instance.lineSeparator
 
     @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
     private final DefaultStandardOutputRedirector redirector = new DefaultStandardOutputRedirector()
@@ -70,7 +70,31 @@ class DefaultStandardOutputRedirectorTest extends Specification {
         System.out == outputs.stdOutPrintStream
         System.err == outputs.stdErrPrintStream
     }
-    
+
+    def receivesPartialOutput() {
+        when:
+        redirector.redirectStandardOutputTo(stdOutListener)
+        redirector.start()
+        System.out.print('this is stdout')
+        redirector.stop()
+
+        then:
+        1 * stdOutListener.onOutput('this is stdout')
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+
+    def receivesPartialOutputOnFlush() {
+        when:
+        redirector.redirectStandardOutputTo(stdOutListener)
+        redirector.start()
+        System.out.print('this is stdout')
+        System.out.flush()
+
+        then:
+        1 * stdOutListener.onOutput('this is stdout')
+    }
+
     def canRedirectMultipleTimes() {
         when:
         redirector.redirectStandardErrorTo(stdErrListener)
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStatusBarFormatterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStatusBarFormatterTest.groovy
index 09ca716..ebd6c37 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStatusBarFormatterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/DefaultStatusBarFormatterTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.logging.internal
 
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData
 import org.gradle.logging.internal.progress.ProgressOperation
 import spock.lang.Specification
 import spock.lang.Subject
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy
index f9e4a79..1ffc82c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy
@@ -18,15 +18,15 @@ package org.gradle.logging.internal
 
 import org.gradle.api.logging.LogLevel
 import org.gradle.logging.ConfigureLogging
-import org.gradle.logging.TestAppender
+import org.gradle.logging.TestOutputEventListener
 import org.junit.Rule
 import spock.lang.Specification
 
 import java.util.logging.Logger
 
 class JavaUtilLoggingConfigurerTest extends Specification {
-    final TestAppender appender = new TestAppender()
-    @Rule final ConfigureLogging logging = new ConfigureLogging(appender)
+    final TestOutputEventListener outputEventListener = new TestOutputEventListener()
+    @Rule final ConfigureLogging logging = new ConfigureLogging(outputEventListener)
     private final JavaUtilLoggingConfigurer configurer = new JavaUtilLoggingConfigurer()
 
     def routesJulToSlf4j() {
@@ -35,6 +35,6 @@ class JavaUtilLoggingConfigurerTest extends Specification {
         Logger.getLogger('test').info('info message')
 
         then:
-        appender.toString() == '[INFO info message]'
+        outputEventListener.toString() == '[INFO info message]'
     }
 }
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 e77d3cf..ea3ee5d 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
@@ -16,6 +16,8 @@
 package org.gradle.logging.internal
 
 import org.gradle.api.logging.LogLevel
+import org.gradle.cli.CommandLineArgumentException
+import org.gradle.logging.ConsoleOutput
 import org.gradle.logging.LoggingConfiguration
 import org.gradle.logging.ShowStacktrace
 import spock.lang.Specification
@@ -60,6 +62,29 @@ class LoggingCommandLineConverterTest extends Specification {
         checkConversion(['--no-color'])
     }
 
+    def convertsColor() {
+        expectedConfig.consoleOutput = consoleOutput
+
+        expect:
+        checkConversion([arg])
+
+        where:
+        arg               | consoleOutput
+        "--console=plain" | ConsoleOutput.Plain
+        "--console=auto"  | ConsoleOutput.Auto
+        "--console=AUTO"  | ConsoleOutput.Auto
+        "--console=rich"  | ConsoleOutput.Rich
+    }
+
+    def reportsUnknownColorOption() {
+        when:
+        converter.convert(["--console", "unknown"], new LoggingConfiguration())
+
+        then:
+        CommandLineArgumentException e = thrown()
+        e.message == /Unrecognized value 'unknown' for console./
+    }
+
     def convertsShowStacktrace() {
         expectedConfig.showStacktrace = ShowStacktrace.ALWAYS
 
@@ -91,9 +116,9 @@ class LoggingCommandLineConverterTest extends Specification {
     }
 
     void checkConversion(List<String> args) {
-        def actual = converter.convert(args)
+        def actual = converter.convert(args, new LoggingConfiguration())
         assert actual.logLevel == expectedConfig.logLevel
-        assert actual.colorOutput == expectedConfig.colorOutput
+        assert actual.consoleOutput == expectedConfig.consoleOutput
         assert actual.showStacktrace == expectedConfig.showStacktrace
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
index b390b5d..6c5ad8f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
@@ -20,7 +20,7 @@ import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.StandardOutputListener
 import org.gradle.util.RedirectStdOutAndErr
 import org.junit.Rule
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData
+import org.gradle.internal.nativeintegration.console.ConsoleMetaData
 
 class OutputEventRendererTest extends OutputSpecification {
     @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
@@ -36,7 +36,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def rendersLogEventsToStdOut() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.onOutput(event('message', LogLevel.INFO))
 
         then:
@@ -46,7 +46,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def rendersErrorLogEventsToStdErr() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.onOutput(event('message', LogLevel.ERROR))
 
         then:
@@ -69,7 +69,7 @@ class OutputEventRendererTest extends OutputSpecification {
     def rendersLogEventsToStdOutandStdErrWhenLogLevelIsDebug() {
         when:
         renderer.configure(LogLevel.DEBUG)
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.onOutput(event(tenAm, 'info', LogLevel.INFO))
         renderer.onOutput(event(tenAm, 'error', LogLevel.ERROR))
 
@@ -184,7 +184,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def rendersProgressEvents() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.onOutput(start(loggingHeader: 'description'))
         renderer.onOutput(complete('status'))
 
@@ -195,7 +195,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def doesNotRendersProgressEventsForLogLevelQuiet() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.configure(LogLevel.QUIET)
         renderer.onOutput(start('description'))
         renderer.onOutput(complete('status'))
@@ -258,7 +258,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def attachesConsoleWhenStdOutAndStdErrAreAttachedToConsole() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.addConsole(console, true, true, metaData)
         renderer.onOutput(event('info', LogLevel.INFO))
         renderer.onOutput(event('error', LogLevel.ERROR))
@@ -271,7 +271,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def attachesConsoleWhenOnlyStdOutIsAttachedToConsole() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.addConsole(console, true, false, metaData)
         renderer.onOutput(event('info', LogLevel.INFO))
         renderer.onOutput(event('error', LogLevel.ERROR))
@@ -284,7 +284,7 @@ class OutputEventRendererTest extends OutputSpecification {
 
     def attachesConsoleWhenOnlyStdErrIsAttachedToConsole() {
         when:
-        renderer.addStandardOutputAndError()
+        renderer.attachSystemOutAndErr()
         renderer.addConsole(console, false, true, metaData)
         renderer.onOutput(event('info', LogLevel.INFO))
         renderer.onOutput(event('error', LogLevel.ERROR))
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurerTest.groovy
deleted file mode 100644
index 0f302b0..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/logging/internal/logback/LogbackLoggingConfigurerTest.groovy
+++ /dev/null
@@ -1,249 +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.logging.internal.logback
-
-import ch.qos.logback.classic.LoggerContext
-import ch.qos.logback.classic.Level
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.logging.Logging
-import org.gradle.logging.internal.OutputEventListener
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import spock.lang.Specification
-
-class LogbackLoggingConfigurerTest extends Specification {
-    Logger logger = LoggerFactory.getLogger("cat1")
-    OutputEventListener listener = Mock()
-    LogbackLoggingConfigurer configurer = new LogbackLoggingConfigurer(listener)
-    
-    def cleanup() {
-        def context = (LoggerContext) LoggerFactory.getILoggerFactory()
-        context.reset()
-    }
-
-    def routesSlf4jLogEventsToOutputEventListener() {
-        when:
-        configurer.configure(LogLevel.INFO)
-        logger.info('message')
-
-        then:
-        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == null})
-        0 * listener._
-    }
-
-    def includesThrowableInLogEvent() {
-        def failure = new RuntimeException()
-
-        when:
-        configurer.configure(LogLevel.INFO)
-        logger.info('message', failure)
-
-        then:
-        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == failure})
-        0 * listener._
-    }
-    
-    def mapsSlf4jLogLevelsToGradleLogLevels() {
-        when:
-        configurer.configure(LogLevel.DEBUG)
-
-        logger.debug('debug')
-        logger.info('info')
-        logger.info(Logging.LIFECYCLE, 'lifecycle')
-        logger.info(Logging.QUIET, 'quiet')
-        logger.warn('warn')
-        logger.error('error')
-
-        then:
-        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
-        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
-        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
-        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
-        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
-        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
-        0 * listener._
-    }
-
-    def formatsLogMessage() {
-        when:
-        configurer.configure(LogLevel.INFO)
-        logger.info('message {} {}', 'arg1', 'arg2')
-
-        then:
-        1 * listener.onOutput({it.message == 'message arg1 arg2'})
-        0 * listener._
-    }
-
-    def attachesATimestamp() {
-        when:
-        configurer.configure(LogLevel.INFO)
-        logger.info('message')
-
-        then:
-        1 * listener.onOutput({it.timestamp >= System.currentTimeMillis() - 300})
-        0 * listener._
-    }
-
-    def filtersLifecycleAndLowerWhenConfiguredAtQuietLevel() {
-        when:
-        configurer.configure(LogLevel.QUIET)
-
-        logger.trace('trace')
-        logger.debug('debug')
-        logger.info('info')
-        logger.info(Logging.LIFECYCLE, 'lifecycle')
-        logger.info(Logging.QUIET, 'quiet')
-        logger.warn('warn')
-        logger.error('error')
-
-        then:
-        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
-        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
-        0 * listener._
-    }
-
-    def filtersInfoAndLowerWhenConfiguredAtLifecycleLevel() {
-        when:
-        configurer.configure(LogLevel.LIFECYCLE)
-
-        logger.trace('trace')
-        logger.debug('debug')
-        logger.info('info')
-        logger.info(Logging.LIFECYCLE, 'lifecycle')
-        logger.info(Logging.QUIET, 'quiet')
-        logger.warn('warn')
-        logger.error('error')
-
-        then:
-        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
-        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
-        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
-        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
-        0 * listener._
-    }
-
-    def filtersDebugAndLowerWhenConfiguredAtInfoLevel() {
-        when:
-        configurer.configure(LogLevel.INFO)
-
-        logger.trace('trace')
-        logger.debug('debug')
-        logger.info('info')
-        logger.info(Logging.LIFECYCLE, 'lifecycle')
-        logger.info(Logging.QUIET, 'quiet')
-        logger.warn('warn')
-        logger.error('error')
-
-        then:
-        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
-        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
-        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
-        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
-        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
-        0 * listener._
-    }
-
-    def filtersTraceWhenConfiguredAtDebugLevel() {
-        when:
-        configurer.configure(LogLevel.DEBUG)
-
-        logger.trace('trace')
-        logger.debug('debug')
-        logger.info('info')
-        logger.info(Logging.LIFECYCLE, 'lifecycle')
-        logger.info(Logging.QUIET, 'quiet')
-        logger.warn('warn')
-        logger.error('error')
-
-        then:
-        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
-        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
-        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
-        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
-        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
-        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
-        0 * listener._
-    }
-
-    def "turns off Apache HTTP wire logging"() {
-        def wireLogger = LoggerFactory.getLogger("org.apache.http.wire")
-        configurer.configure(LogLevel.DEBUG)
-
-        when:
-        wireLogger.debug("debug message")
-
-        then:
-        0 * _
-
-        // check that changing log level doesn't reactivate wire logging
-        when:
-        configurer.configure(LogLevel.INFO)
-        wireLogger.info("info message")
-
-        then:
-        0 * _
-    }
-
-    def "respects per-logger log level if either logger log level or event log level is one of OFF, ERROR, DEBUG, TRACE"() {
-        configurer.configure(LogLevel.INFO)
-        logger.level = Level.DEBUG
-
-        when:
-        logger.debug("message")
-
-        then:
-        1 * listener._
-
-        when:
-        logger.level = Level.ERROR
-        logger.warn("message")
-
-        then:
-        0 * listener._
-    }
-
-    def "respects per-logger log level if both logger log level and event log level are WARN"() {
-        configurer.configure(LogLevel.ERROR)
-        logger.level = Level.WARN
-
-        when:
-        logger.warn("message")
-
-        then:
-        1 * listener._
-    }
-
-    // More a limitation than a feature, although it might not matter much for Gradle, where individual
-    // loggers usually don't have a log level set.
-    def "does not respect per-logger log level if either logger log level or event log level is one of INFO, LIFECYCLE, WARN, QUIET"() {
-        configurer.configure(LogLevel.INFO)
-        logger.level = Level.WARN
-
-        when:
-        logger.info("message")
-
-        then:
-        1 * listener._
-
-        when:
-        configurer.configure(LogLevel.WARN)
-        logger.level = Level.INFO
-        logger.info("message")
-
-        then:
-        0 * listener._
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContextTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContextTest.groovy
new file mode 100644
index 0000000..aba584a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerContextTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j
+
+import org.gradle.internal.TrueTimeProvider
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.api.logging.LogLevel.LIFECYCLE
+import static org.slf4j.Logger.ROOT_LOGGER_NAME
+
+ at Unroll
+class OutputEventListenerBackedLoggerContextTest extends Specification {
+
+    OutputEventListenerBackedLoggerContext context = new OutputEventListenerBackedLoggerContext(System.out, System.err, new TrueTimeProvider());
+
+    private OutputEventListenerBackedLogger logger(String name) {
+        context.getLogger(name)
+    }
+
+    def "can retrieve logger named #name"() {
+        expect:
+        logger(name).name == name
+
+        where:
+        name << [ROOT_LOGGER_NAME, "foo", "foo.bar"]
+    }
+
+    def "logger instances are cached"() {
+        expect:
+        logger(name).is(logger(name))
+
+        where:
+        name << [ROOT_LOGGER_NAME, "foo", "foo.bar"]
+    }
+
+    def "default log level is LIFECYCLE"() {
+        expect:
+        context.level == LIFECYCLE
+    }
+
+    def "cannot set global level to null"() {
+        when:
+        context.level = null
+
+        then:
+        IllegalArgumentException e = thrown()
+        e.message == "Global log level cannot be set to null"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerDefaultConfigurationTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerDefaultConfigurationTest.groovy
new file mode 100644
index 0000000..0763d65
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerDefaultConfigurationTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j
+
+import org.gradle.api.logging.Logging
+import org.gradle.internal.TrueTimeProvider
+import org.gradle.util.TextUtil
+import org.slf4j.Logger
+import spock.lang.Specification
+
+class OutputEventListenerBackedLoggerDefaultConfigurationTest extends Specification {
+
+    def outStream = new ByteArrayOutputStream()
+    def errStream = new ByteArrayOutputStream()
+
+    def context = new OutputEventListenerBackedLoggerContext(new PrintStream(outStream), new PrintStream(errStream), new TrueTimeProvider())
+
+    String getOut() {
+        outStream.toString()
+    }
+
+    String getErr() {
+        errStream.toString()
+    }
+
+    Logger logger() {
+        context.getLogger("foo")
+    }
+
+    def "messages logged below LIFECYCLE level are ignored"() {
+        when:
+        logger().trace("debug")
+        logger().debug("debug")
+        logger().info("debug")
+
+        then:
+        out.empty
+        err.empty
+    }
+
+    def "messages logged at LIFECYCLE, WARN and QUIET levels are directed to default output stream"() {
+        when:
+        logger().info(Logging.LIFECYCLE, "lifecycle")
+        logger().warn("warn")
+        logger().info(Logging.QUIET, "quiet")
+
+        then:
+        err.empty
+        out == TextUtil.toPlatformLineSeparators("""lifecycle
+warn
+quiet
+""")
+    }
+
+    def "messages logged at ERROR level are directed to default error stream"() {
+        when:
+        logger().error("error")
+
+        then:
+        out.empty
+        err == TextUtil.toPlatformLineSeparators("""error
+""")
+    }
+
+    private String stacktrace(Exception e) {
+        def stream = new ByteArrayOutputStream()
+        e.printStackTrace(new PrintStream(stream))
+        stream.toString()
+    }
+
+    def "can log stacktraces"() {
+        given:
+        Exception e = new Exception();
+
+        when:
+        logger().warn("warn stacktrace coming", e)
+        logger().error("error stacktrace coming", e)
+
+        then:
+        out == TextUtil.toPlatformLineSeparators("""warn stacktrace coming
+""" + stacktrace(e))
+        err == TextUtil.toPlatformLineSeparators("""error stacktrace coming
+""" + stacktrace(e))
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerTest.groovy
new file mode 100644
index 0000000..ffdbd26
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/OutputEventListenerBackedLoggerTest.groovy
@@ -0,0 +1,907 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal.slf4j
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.logging.Logger
+import org.gradle.api.logging.Logging
+import org.gradle.logging.internal.LogEvent
+import org.gradle.logging.internal.OutputEventListener
+import org.slf4j.Marker
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.api.logging.LogLevel.*
+import static org.slf4j.Logger.ROOT_LOGGER_NAME
+
+ at Unroll
+class OutputEventListenerBackedLoggerTest extends Specification {
+
+    final List<LogEvent> events = []
+    final long now = System.currentTimeMillis()
+    final OutputEventListenerBackedLoggerContext context = new OutputEventListenerBackedLoggerContext(System.out, System.err, { now })
+
+    def setup() {
+        context.outputEventListener = Mock(OutputEventListener) {
+            onOutput(_) >> { LogEvent event -> events << event }
+        }
+    }
+
+    def cleanup() {
+        assert !events
+    }
+
+    private SingleLogEventSpecificationBuilder singleLogEvent() {
+        new SingleLogEventSpecificationBuilder()
+    }
+
+    private class SingleLogEventSpecificationBuilder {
+        private String category = ROOT_LOGGER_NAME
+        private String message
+        private long timestamp
+        private Throwable throwable
+        private LogLevel logLevel
+        private boolean eventExpected = true
+
+        SingleLogEventSpecificationBuilder message(String message) {
+            this.message = message
+            this
+        }
+
+        SingleLogEventSpecificationBuilder timestamp(long timestamp) {
+            this.timestamp = timestamp
+            this
+        }
+
+        SingleLogEventSpecificationBuilder throwable(Throwable throwable) {
+            this.throwable = throwable
+            this
+        }
+
+        SingleLogEventSpecificationBuilder logLevel(LogLevel logLevel) {
+            this.logLevel = logLevel
+            this
+        }
+
+        SingleLogEventSpecificationBuilder eventExpected(boolean eventExpected) {
+            this.eventExpected = eventExpected
+            this
+        }
+
+        boolean asBoolean() {
+            if (!eventExpected) {
+                assert events.size() == 0
+                return true
+            }
+
+            assert events.size() == 1
+            LogEvent event = events.remove(0)
+            assert event.category == category
+            assert event.message == message
+            assert event.timestamp == now
+            assert event.throwable == throwable
+            assert event.logLevel == logLevel
+            return true
+        }
+    }
+
+    private Logger logger(String name) {
+        context.getLogger(name)
+    }
+
+    private Logger logger() {
+        logger(ROOT_LOGGER_NAME)
+    }
+
+    private void setGlobalLevel(LogLevel level) {
+        context.level = level
+    }
+
+    def "isTraceEnabled returns false when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        !logger().traceEnabled
+        !logger().isTraceEnabled(null)
+
+        where:
+        level << LogLevel.values()
+    }
+
+    def "isDebugEnabled returns #enabled when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().debugEnabled == enabled
+        logger().isDebugEnabled(null) == enabled
+        logger().isEnabled(DEBUG) == enabled
+
+
+        where:
+        level     | enabled
+        DEBUG     | true
+        INFO      | false
+        LIFECYCLE | false
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+    }
+
+    def "isInfoEnabled returns #enabled when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().infoEnabled == enabled
+        logger().isInfoEnabled(null) == enabled
+        logger().isEnabled(INFO) == enabled
+
+        where:
+        level     | enabled
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | false
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+    }
+
+    def "isInfoEnabled with LIFECYCLE marker returns #enabled when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().isInfoEnabled(Logging.LIFECYCLE) == enabled
+        logger().lifecycleEnabled == enabled
+        logger().isEnabled(LIFECYCLE) == enabled
+
+        where:
+        level     | enabled
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+    }
+
+    def "isInfoEnabled with QUIET marker returns #enabled when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().isInfoEnabled(Logging.QUIET) == enabled
+        logger().quietEnabled == enabled
+        logger().isEnabled(QUIET) == enabled
+
+
+        where:
+        level     | enabled
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | true
+        QUIET     | true
+        ERROR     | false
+    }
+
+    def "isWarnEnabled returns #enabled when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().warnEnabled == enabled
+        logger().isWarnEnabled(null) == enabled
+        logger().isEnabled(WARN) == enabled
+
+
+        where:
+        level     | enabled
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | true
+        QUIET     | false
+        ERROR     | false
+    }
+
+    def "isErrorEnabled returns true when level is #level"() {
+        when:
+        globalLevel = level
+
+        then:
+        logger().errorEnabled
+        logger().isErrorEnabled(null)
+        logger().isEnabled(ERROR)
+
+
+        where:
+        level << LogLevel.values()
+    }
+
+    def "trace calls do nothing when level is #level"() {
+        given:
+        context.outputEventListener = Mock(OutputEventListener)
+
+        and:
+        globalLevel = level
+
+        when:
+        logger().trace(message)
+        logger().trace(message, new Exception())
+        logger().trace(message, arg1)
+        logger().trace(message, arg1, arg2)
+        logger().trace(message, arg1, arg2, arg3)
+        logger().trace((Marker) null, message)
+        logger().trace((Marker) null, message, new Exception())
+        logger().trace((Marker) null, message, arg1)
+        logger().trace((Marker) null, message, arg1, arg2)
+        logger().trace((Marker) null, message, arg1, arg2, arg3)
+
+        then:
+        0 * context.outputEventListener._
+
+
+        where:
+        level << LogLevel.values()
+
+        message = "message"
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "debug calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().debug("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().log(DEBUG, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().log(DEBUG, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().log(DEBUG, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().debug((Marker)null, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug((Marker)null, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug((Marker)null, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug((Marker)null, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(DEBUG).eventExpected(eventExpected)
+
+        when:
+        logger().debug((Marker)null, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(DEBUG).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | false
+        LIFECYCLE | false
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "info calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().info("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().log(INFO, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().log(INFO, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().log(INFO, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().info((Marker)null, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info((Marker)null, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info((Marker)null, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info((Marker)null, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(INFO).eventExpected(eventExpected)
+
+        when:
+        logger().info((Marker)null, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(INFO).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | false
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "info calls with LIFECYCLE marker work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().info(Logging.LIFECYCLE, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.LIFECYCLE, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.LIFECYCLE, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.LIFECYCLE, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.LIFECYCLE, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "lifecycle calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().lifecycle("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().log(LIFECYCLE, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().lifecycle("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().log(LIFECYCLE, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().lifecycle("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().lifecycle("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(LIFECYCLE).eventExpected(eventExpected)
+
+        when:
+        logger().lifecycle("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().log(LIFECYCLE, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(LIFECYCLE).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | false
+        QUIET     | false
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "info calls with QUIET marker work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().info(Logging.QUIET, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.QUIET, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.QUIET, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.QUIET, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().info(Logging.QUIET, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | true
+        QUIET     | true
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "quiet calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().quiet("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().log(QUIET, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().quiet("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().log(QUIET, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().quiet("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().quiet("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(QUIET).eventExpected(eventExpected)
+
+        when:
+        logger().quiet("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().log(QUIET, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(QUIET).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | true
+        QUIET     | true
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "warn calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().warn("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().log(WARN, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().log(WARN, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().log(WARN, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).throwable(throwable).eventExpected(eventExpected)
+
+        when:
+        logger().warn((Marker)null, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn((Marker)null, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn((Marker)null, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn((Marker)null, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(WARN).eventExpected(eventExpected)
+
+        when:
+        logger().warn((Marker)null, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(WARN).throwable(throwable).eventExpected(eventExpected)
+
+        where:
+        level     | eventExpected
+        DEBUG     | true
+        INFO      | true
+        LIFECYCLE | true
+        WARN      | true
+        QUIET     | false
+        ERROR     | false
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    def "error calls work as expected when level is #level"() {
+        given:
+        globalLevel = level
+
+        when:
+        logger().error("message")
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR)
+
+        when:
+        logger().log(ERROR, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR)
+
+        when:
+        logger().error("{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(ERROR)
+
+        when:
+        logger().log(ERROR, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(ERROR)
+
+        when:
+        logger().error("{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(ERROR)
+
+        when:
+        logger().error("{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(ERROR)
+
+        when:
+        logger().error("message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR).throwable(throwable)
+
+        when:
+        logger().log(ERROR, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR).throwable(throwable)
+
+        when:
+        logger().error((Marker)null, "message")
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR)
+
+        when:
+        logger().error((Marker)null, "{}", arg1)
+
+        then:
+        singleLogEvent().message("arg1").logLevel(ERROR)
+
+        when:
+        logger().error((Marker)null, "{} {}", arg1, arg2)
+
+        then:
+        singleLogEvent().message("arg1 arg2").logLevel(ERROR)
+
+        when:
+        logger().error((Marker)null, "{} {} {}", arg1, arg2, arg3)
+
+        then:
+        singleLogEvent().message("arg1 arg2 arg3").logLevel(ERROR)
+
+        when:
+        logger().error((Marker)null, "message", throwable)
+
+        then:
+        singleLogEvent().message("message").logLevel(ERROR).throwable(throwable)
+
+        where:
+        level << LogLevel.values()
+
+        throwable = new Throwable()
+        arg1 = "arg1"
+        arg2 = "arg2"
+        arg3 = "arg3"
+    }
+
+    private String stacktrace(Exception e) {
+        def stream = new ByteArrayOutputStream()
+        e.printStackTrace(new PrintStream(stream))
+        stream.toString()
+    }
+
+    def "logging from Apache HTTP wire logger is suppressed"() {
+        when:
+        logger(OutputEventListenerBackedLoggerContext.HTTP_CLIENT_WIRE_LOGGER_NAME).error("message")
+
+        then:
+        singleLogEvent().eventExpected(false)
+    }
+
+    def "logging from MetaInfExtensionModule logger is suppressed"() {
+        when:
+        logger(OutputEventListenerBackedLoggerContext.META_INF_EXTENSION_MODULE_LOGGER_NAME).error("message")
+
+        then:
+        singleLogEvent().eventExpected(false)
+    }
+
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurerTest.groovy
new file mode 100644
index 0000000..37b508a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/logging/internal/slf4j/Slf4jLoggingConfigurerTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * 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.logging.internal.slf4j
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.logging.Logging
+import org.gradle.logging.internal.OutputEventListener
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import spock.lang.Specification
+
+class Slf4jLoggingConfigurerTest extends Specification {
+    Logger logger = LoggerFactory.getLogger("cat1")
+    OutputEventListener listener = Mock()
+    Slf4jLoggingConfigurer configurer = new Slf4jLoggingConfigurer(listener)
+    
+    def cleanup() {
+        def context = (OutputEventListenerBackedLoggerContext) LoggerFactory.getILoggerFactory()
+        context.reset()
+    }
+
+    def routesSlf4jLogEventsToOutputEventListener() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message')
+
+        then:
+        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == null})
+        0 * listener._
+    }
+
+    def includesThrowableInLogEvent() {
+        def failure = new RuntimeException()
+
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message', failure)
+
+        then:
+        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == failure})
+        0 * listener._
+    }
+    
+    def mapsSlf4jLogLevelsToGradleLogLevels() {
+        when:
+        configurer.configure(LogLevel.DEBUG)
+
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def formatsLogMessage() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message {} {}', 'arg1', 'arg2')
+
+        then:
+        1 * listener.onOutput({it.message == 'message arg1 arg2'})
+        0 * listener._
+    }
+
+    def attachesATimestamp() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message')
+
+        then:
+        1 * listener.onOutput({it.timestamp >= System.currentTimeMillis() - 300})
+        0 * listener._
+    }
+
+    def filtersLifecycleAndLowerWhenConfiguredAtQuietLevel() {
+        when:
+        configurer.configure(LogLevel.QUIET)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersInfoAndLowerWhenConfiguredAtLifecycleLevel() {
+        when:
+        configurer.configure(LogLevel.LIFECYCLE)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersDebugAndLowerWhenConfiguredAtInfoLevel() {
+        when:
+        configurer.configure(LogLevel.INFO)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersTraceWhenConfiguredAtDebugLevel() {
+        when:
+        configurer.configure(LogLevel.DEBUG)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/model/dsl/internal/GroovyModelDslTest.groovy b/subprojects/core/src/test/groovy/org/gradle/model/dsl/internal/GroovyModelDslTest.groovy
deleted file mode 100644
index c286708..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/model/dsl/internal/GroovyModelDslTest.groovy
+++ /dev/null
@@ -1,101 +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.model.dsl.internal
-
-import org.gradle.model.internal.DefaultModelRegistry
-import org.gradle.model.internal.ModelRegistryBackedModelRules
-import spock.lang.Specification
-
-class GroovyModelDslTest extends Specification {
-
-    def modelRegistry = new DefaultModelRegistry()
-    def modelRules = new ModelRegistryBackedModelRules(modelRegistry)
-    def modelDsl = new GroovyModelDsl(modelRules)
-
-    def "can add rules via dsl"() {
-        given:
-        modelRules.register("foo", [])
-
-        when:
-        modelDsl.configure {
-            foo {
-                add 1
-            }
-        }
-
-        then:
-        modelRegistry.get("foo", List) == [1]
-    }
-
-    def "can use property accessors in DSL to build model object path"() {
-        given:
-        modelRules.register("foo.bar", [])
-
-        when:
-        modelDsl.configure {
-            foo.bar {
-                add 1
-            }
-        }
-
-        then:
-        modelRegistry.get("foo.bar", List) == [1]
-    }
-
-    def "does not add rules when not configuring"() {
-        given:
-        modelRules.register("foo", new TestObject())
-        modelRules.register("bah", new TestObject())
-
-        when:
-        modelDsl.configure {
-            foo {
-                defineSomeThing {
-                    unknown
-                }
-            }
-        }
-        modelRegistry.get("foo", Object)
-
-        then:
-        MissingPropertyException missingProp = thrown()
-        missingProp.property == 'unknown'
-
-        when:
-        modelDsl.configure {
-            bah {
-                defineSomeThing {
-                    unknown { }
-                }
-            }
-        }
-        modelRegistry.get("bah", Object)
-
-        then:
-        MissingMethodException missingMethod = thrown()
-        missingMethod.method == 'unknown'
-    }
-}
-
-class TestObject {
-    String prop
-
-    def defineSomeThing(Closure cl) {
-        cl.delegate = this
-        cl.call()
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/model/internal/ModelRegistryBackedModelRulesTest.groovy b/subprojects/core/src/test/groovy/org/gradle/model/internal/ModelRegistryBackedModelRulesTest.groovy
deleted file mode 100644
index 0ca5b31..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/model/internal/ModelRegistryBackedModelRulesTest.groovy
+++ /dev/null
@@ -1,102 +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.model.internal
-
-import org.gradle.api.Action
-import org.gradle.model.ModelFinalizer
-import org.gradle.model.ModelRule
-import spock.lang.Specification
-
-class ModelRegistryBackedModelRulesTest extends Specification {
-
-    static class ModelElement {
-        List<String> names = []
-    }
-
-    static class DerivedThing {
-        String name
-    }
-
-    def modelRegistry = new DefaultModelRegistry()
-    def rules = new ModelRegistryBackedModelRules(modelRegistry)
-
-    def "can configure by rules"() {
-        when:
-        rules.register("element", new ModelElement())
-        rules.register("things", [] as List<DerivedThing>)
-
-        3.times { int i ->
-            rules.rule(new ModelRule() {
-                void addName(ModelElement modelElement) {
-                    modelElement.names << "name$i"
-                }
-            })
-        }
-
-        rules.rule(new ModelRule() {
-            public void registerThings(List<DerivedThing> things, ModelElement element) {
-                element.names.each {
-                    things << new DerivedThing(name: it)
-                }
-            }
-        })
-
-        then:
-        List<DerivedThing> things = modelRegistry.get("things", List)
-        things*.name == ["name0", "name1", "name2"]
-    }
-
-    def "can configure by action"() {
-        when:
-        rules.register("element", new ModelElement())
-
-        3.times { int i ->
-            rules.config("element", { ModelElement it ->
-                    it.names << "name$i"
-            } as Action)
-        }
-
-
-        def element = modelRegistry.get("element", ModelElement)
-        then:
-        element
-        element.names == ["name0", "name1", "name2"]
-    }
-
-    def "can finalize"() {
-        when:
-        rules.register("element", new ModelElement())
-
-        rules.rule(new ModelFinalizer() {
-            void addFinal(ModelElement modelElement) {
-                modelElement.names << "final"
-            }
-        })
-
-        3.times { int i ->
-            rules.config("element", { ModelElement it ->
-                it.names << "name$i"
-            } as Action)
-        }
-
-
-        def element = modelRegistry.get("element", ModelElement)
-        then:
-        element
-        element.names == ["name0", "name1", "name2", "final"]
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/plugin/bintray/JCenterPluginMapperSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/plugin/bintray/JCenterPluginMapperSpec.groovy
deleted file mode 100644
index 3ef797b..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/plugin/bintray/JCenterPluginMapperSpec.groovy
+++ /dev/null
@@ -1,80 +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.plugin.bintray
-
-import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.dsl.DependencyHandler
-import org.gradle.cache.PersistentIndexedCache
-import org.gradle.internal.Factories
-import org.gradle.internal.Supplier
-import org.gradle.internal.Suppliers
-import org.gradle.messaging.serialize.BaseSerializerFactory
-import org.gradle.plugin.resolve.internal.DefaultPluginRequest
-import org.gradle.plugin.resolve.internal.InvalidPluginRequestException
-import org.gradle.plugin.resolve.internal.JCenterPluginMapper
-import org.gradle.plugin.resolve.internal.PluginRequest
-import org.gradle.testfixtures.internal.InMemoryIndexedCache
-import spock.lang.Specification
-
-class JCenterPluginMapperSpec extends Specification {
-
-    public static final String TEST_PLUGIN_MAVEN_GROUP_ID = 'com.bintray.gradle.test'
-    public static final String TEST_PLUGIN_MAVEN_ARTIFACT_ID = 'test-plugin'
-    public static final String TEST_PLUGIN_EXPLICIT_VERSION = '1.0'
-    public static final String TEST_PLUGIN_ID = 'gradle-test-plugin'
-
-    private DependencyHandler getMockForVersion(String version) {
-        Mock(DependencyHandler) {
-            create("$TEST_PLUGIN_MAVEN_GROUP_ID:$TEST_PLUGIN_MAVEN_ARTIFACT_ID:$version") >> Stub(Dependency) {
-                getGroup() >> TEST_PLUGIN_MAVEN_GROUP_ID
-                getName() >> TEST_PLUGIN_MAVEN_ARTIFACT_ID
-                getVersion() >> version
-            }
-            0 * _ //fail if create called with any other string
-        }
-    }
-
-    PersistentIndexedCache<PluginRequest, String> cache = new InMemoryIndexedCache<PluginRequest, String>(BaseSerializerFactory.STRING_SERIALIZER)
-    Supplier<PersistentIndexedCache<PluginRequest, String>> cacheSupplier = Suppliers.of(Factories.constant(cache))
-    JCenterPluginMapper mapper = new JCenterPluginMapper(cacheSupplier)
-
-    def 'Latest version of plugin maps correctly from Bintray'() {
-        when:
-        def dependencyHandler = Mock(DependencyHandler)
-        0 * dependencyHandler._
-        mapper.map(new DefaultPluginRequest(TEST_PLUGIN_ID), dependencyHandler)
-
-        then:
-        def e = thrown InvalidPluginRequestException
-        e.message.startsWith "No version number supplied for plugin '$TEST_PLUGIN_ID'"
-    }
-
-    def 'Explicit version of plugin maps correctly from Bintray'() {
-        when:
-        Dependency dependency = mapper.map(new DefaultPluginRequest(TEST_PLUGIN_ID, TEST_PLUGIN_EXPLICIT_VERSION), getMockForVersion(TEST_PLUGIN_EXPLICIT_VERSION))
-
-        then:
-        dependency.group == TEST_PLUGIN_MAVEN_GROUP_ID
-        dependency.name == TEST_PLUGIN_MAVEN_ARTIFACT_ID
-        dependency.version == TEST_PLUGIN_EXPLICIT_VERSION
-    }
-
-    def 'Query for non-existing plugin returns null'() {
-        expect:
-        mapper.map(new DefaultPluginRequest("not-exist"), getMockForVersion(TEST_PLUGIN_EXPLICIT_VERSION)) == null
-    }
-}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/plugin/internal/DefaultPluginHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/plugin/internal/DefaultPluginHandlerTest.groovy
deleted file mode 100644
index 9c8d86f..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/plugin/internal/DefaultPluginHandlerTest.groovy
+++ /dev/null
@@ -1,62 +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.plugin.internal
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.plugin.resolve.internal.DefaultPluginRequest
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class DefaultPluginHandlerTest extends Specification {
-
-    def requests = []
-    def handler = new DefaultPluginHandler(requests)
-
-    @Unroll
-    def "errors on invalid notation - #map"() {
-        when:
-        handler.apply(map)
-
-        then:
-        thrown InvalidUserDataException
-
-        where:
-        map << [
-                [:],
-                [foo: "bar"],
-                [version: "1.0"],
-                [version: "1"]
-        ]
-    }
-
-    @Unroll
-    def "accepts valid notation and applies when resolved - #map"() {
-        when:
-        handler.apply(map)
-
-        then:
-        requests.first() == request
-
-        where:
-        map                             | request
-        [plugin: "foo"]                 | new DefaultPluginRequest("foo")
-        [plugin: "foo", version: "bar"] | new DefaultPluginRequest("foo", "bar")
-        [plugin: "foo", version: 1]     | new DefaultPluginRequest("foo", "1")
-        [plugin: "foo", version: []]    | new DefaultPluginRequest("foo", "[]")
-    }
-
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/plugin/internal/PluginIdTest.groovy b/subprojects/core/src/test/groovy/org/gradle/plugin/internal/PluginIdTest.groovy
new file mode 100644
index 0000000..deffc9d
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/plugin/internal/PluginIdTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.internal
+
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+import static org.gradle.plugin.internal.PluginId.validate
+
+class PluginIdTest extends Specification {
+
+    def "test validation matcher"() {
+        expect:
+        PluginId.INVALID_PLUGIN_ID_CHAR_MATCHER.indexIn(input) == index
+
+        where:
+        input     | index
+        "foo"     | -1
+        "f o"     | 1
+        "foo.bür" | 5
+        "123"     | -1
+        "FOO.bar" | -1
+    }
+
+    def "validate valid"() {
+        when:
+        validate("foo")
+        validate("Foo")
+        validate("foo.bar")
+        validate("foo.Bar")
+        validate("1")
+        validate("1.1")
+        validate("_._")
+        validate("-.-")
+        validate("-")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "is qualified"() {
+        expect:
+        !new PluginId("foo").qualified
+        new PluginId("foo.bar").qualified
+    }
+
+    def "qualify if unqualified"() {
+        expect:
+        new PluginId("foo").maybeQualify("bar").toString() == "bar.foo"
+        new PluginId("foo.bar").maybeQualify("bar").toString() == "foo.bar"
+    }
+
+    def "equality"() {
+        expect:
+        new PluginId("foo") Matchers.strictlyEqual(new PluginId("foo"))
+        new PluginId("foo.bar") Matchers.strictlyEqual(new PluginId("foo.bar"))
+        def qualified = new PluginId("foo").maybeQualify("some.org")
+        qualified Matchers.strictlyEqual(new PluginId("some.org.foo"))
+        new PluginId("foo") != new PluginId("foo.bar")
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestCollectorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestCollectorTest.groovy
new file mode 100644
index 0000000..e3af442
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestCollectorTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal
+
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.internal.exceptions.LocationAwareException
+import org.gradle.plugin.use.PluginDependenciesSpec
+import spock.lang.Specification
+
+class PluginRequestCollectorTest extends Specification {
+
+    final scriptSource = new StringScriptSource("d", "c")
+    static final int LINE_NUMBER = 10
+
+    List<PluginRequest> plugins(@DelegatesTo(PluginDependenciesSpec) Closure<?> closure) {
+        new PluginRequestCollector(scriptSource).with {
+            createSpec(LINE_NUMBER).with(closure)
+            getRequests()
+        }
+    }
+
+    List<PluginRequest> requests(Map<String, String> requests) {
+        requests.collect { new DefaultPluginRequest(it.key, it.value, LINE_NUMBER, scriptSource) }
+    }
+
+    def "can use spec dsl to build one request"() {
+        expect:
+        requests(foo: "bar") == plugins {
+            id "foo" version "bar"
+        }
+    }
+
+    def "version is optional"() {
+        expect:
+        requests(foo: null) == plugins {
+            id "foo"
+        }
+    }
+
+    def "returns empty list if none specified"() {
+        expect:
+        plugins {}.isEmpty()
+    }
+
+    def "can specify multiple"() {
+        expect:
+        requests(foo: "1.0", "bar": "2.0") == plugins {
+            id "foo" version "1.0"
+            id "bar" version "2.0"
+        }
+    }
+
+    def "prevents duplicate ids"() {
+        when:
+        plugins {
+            id "foo" version "1.0"
+            id "foo" version "1.0"
+        }
+
+        then:
+        def e = thrown(LocationAwareException)
+        e.cause instanceof InvalidPluginRequestException
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializerTest.groovy
new file mode 100644
index 0000000..930a446
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/plugin/use/internal/PluginRequestsSerializerTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal
+
+import org.gradle.internal.serialize.SerializerSpec
+import org.gradle.plugin.internal.PluginId
+
+class PluginRequestsSerializerTest extends SerializerSpec {
+
+    def serializer = new PluginRequestsSerializer()
+
+    def "empty"() {
+        when:
+        def serialized = serialize(new DefaultPluginRequests([]), serializer)
+
+        then:
+        serialized.empty
+    }
+
+    def "non empty"() {
+        when:
+        def serialized = serialize(new DefaultPluginRequests([
+                new DefaultPluginRequest("java", null, 1, "buildscript"),
+                new DefaultPluginRequest("groovy", null, 2, "buildscript"),
+                new DefaultPluginRequest("custom", "1.0", 3, "initscript")
+        ]), serializer)
+
+        then:
+        serialized*.id == ["java", "groovy", "custom"].collect { PluginId.of(it) }
+        serialized*.version == [null, null, "1.0"]
+        serialized*.lineNumber == [1, 2, 3]
+        serialized*.scriptDisplayName == ["buildscript", "buildscript", "initscript"]
+    }
+}
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 93f9470..5af1ac2 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
@@ -16,11 +16,12 @@
 
 package org.gradle.process.internal
 
+import org.gradle.internal.jvm.Jvm
 import org.gradle.process.ExecResult
 import org.gradle.process.internal.streams.StreamsHandler
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.GUtil
-import org.gradle.util.Jvm
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Ignore
 import spock.lang.Specification
@@ -28,6 +29,7 @@ import spock.lang.Timeout
 
 import java.util.concurrent.Callable
 
+ at UsesNativeServices
 @Timeout(60)
 class DefaultExecHandleSpec extends Specification {
     @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
@@ -143,7 +145,7 @@ class DefaultExecHandleSpec extends Specification {
         execHandle.abort()
     }
 
-    @Ignore //TODO SF not yet implemented, as following @Ignores
+    @Ignore //not yet implemented
     void "aborts daemon"() {
         def output = new ByteArrayOutputStream()
         def execHandle = handle().setDaemon(true).setStandardOutput(output).args(args(SlowDaemonApp.class)).build();
@@ -182,7 +184,7 @@ class DefaultExecHandleSpec extends Specification {
         execHandle.abort()
     }
 
-    @Ignore
+    @Ignore //not yet implemented
     void "can detach from long daemon and then wait for finish"() {
         def out = new ByteArrayOutputStream()
         def execHandle = handle().setStandardOutput(out).args(args(SlowDaemonApp.class, "200")).build();
@@ -201,7 +203,7 @@ class DefaultExecHandleSpec extends Specification {
         execHandle.state == ExecHandleState.SUCCEEDED
     }
 
-    @Ignore
+    @Ignore //not yet implemented
     void "can detach from fast app then wait for finish"() {
         def out = new ByteArrayOutputStream()
         def execHandle = handle().setStandardOutput(out).args(args(TestApp.class)).build();
@@ -215,7 +217,7 @@ class DefaultExecHandleSpec extends Specification {
         execHandle.state == ExecHandleState.SUCCEEDED
     }
 
-    @Ignore
+    @Ignore //not yet implemented
     //it may not be easily testable
     void "detach detects when process did not start or died prematurely"() {
         def execHandle = handle().args(args(BrokenApp.class)).build();
@@ -260,7 +262,7 @@ class DefaultExecHandleSpec extends Specification {
     }
 
     @Timeout(2)
-    @Ignore
+    @Ignore //not yet implemented
     void "exec handle can detach with timeout"() {
         given:
         def execHandle = handle().args(args(SlowApp.class)).setTimeout(1).build();
@@ -274,7 +276,7 @@ class DefaultExecHandleSpec extends Specification {
         //the timeout does not hit
     }
 
-    @Ignore
+    @Ignore //not yet implemented
     void "exec handle can wait with timeout"() {
         given:
         def execHandle = handle().args(args(SlowApp.class)).setTimeout(1).build();
@@ -308,13 +310,6 @@ class DefaultExecHandleSpec extends Specification {
         GUtil.flattenElements("-cp", System.getProperty("java.class.path"), mainClass.getName(), args);
     }
 
-    public static class TestApp {
-        public static void main(String[] args) {
-            System.out.print("output args: " + Arrays.asList(args));
-            System.err.print("error args: " + Arrays.asList(args));
-        }
-    }
-
     public static class BrokenApp {
         public static void main(String[] args) {
             System.exit(72);
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/ExecHandleBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/ExecHandleBuilderTest.groovy
index 8269b3a..bc3c6af 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/ExecHandleBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/ExecHandleBuilderTest.groovy
@@ -16,8 +16,10 @@
 
 package org.gradle.process.internal
 
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
+ at UsesNativeServices
 class ExecHandleBuilderTest extends Specification {
     private final ExecHandleBuilder builder = new ExecHandleBuilder()
 
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 db05100..5da45d8 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
@@ -53,15 +53,14 @@ public class JavaExecHandleBuilderTest extends Specification {
         List jvmArgs = builder.getAllJvmArgs()
 
         then:
-        jvmArgs == ['-Dprop=value', 'jvm1', 'jvm2', '-Xms64m', '-Xmx1g', fileEncodingProperty(expectedEncoding), '-cp', "$jar1$File.pathSeparator$jar2"]
+        jvmArgs == ['-Dprop=value', 'jvm1', 'jvm2', '-Xms64m', '-Xmx1g', fileEncodingProperty(expectedEncoding), *localeProperties(), '-cp', "$jar1$File.pathSeparator$jar2"]
 
         when:
         List commandLine = builder.getCommandLine()
 
         then:
         String executable = Jvm.current().getJavaExecutable().getAbsolutePath()
-        commandLine == [executable,  '-Dprop=value', 'jvm1', 'jvm2', '-Xms64m', '-Xmx1g', fileEncodingProperty(expectedEncoding),
-                '-cp', "$jar1$File.pathSeparator$jar2", 'mainClass', 'arg1', 'arg2']
+        commandLine == [executable,  '-Dprop=value', 'jvm1', 'jvm2', '-Xms64m', '-Xmx1g', fileEncodingProperty(expectedEncoding), *localeProperties(), '-cp', "$jar1$File.pathSeparator$jar2", 'mainClass', 'arg1', 'arg2']
         
         where:
         inputEncoding | expectedEncoding
@@ -77,4 +76,13 @@ public class JavaExecHandleBuilderTest extends Specification {
     private String fileEncodingProperty(String encoding = Charset.defaultCharset().name()) {
         return "-Dfile.encoding=$encoding"
     }
+
+    private static List<String> localeProperties(Locale locale = Locale.default) {
+        ["country", "language", "variant"].sort().collectEntries {
+            ["user.$it", locale."$it"]
+        }.collect {
+            it.value ? "-D$it.key=$it.value" : "-D$it.key"
+        }
+    }
+
 }
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 016a3f0..371853e 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
@@ -69,7 +69,7 @@ class JvmOptionsTest extends Specification {
 
     def "system properties are always before the symbolic arguments"() {
         expect:
-        parse("-Xms1G -Dfile.encoding=UTF-8 -Dfoo.encoding=blah -Dfile.encoding=UTF-16").allJvmArgs == ["-Dfoo.encoding=blah", "-Xms1G", "-Dfile.encoding=UTF-16"]
+        parse("-Xms1G -Dfile.encoding=UTF-8 -Dfoo.encoding=blah -Dfile.encoding=UTF-16").allJvmArgs == ["-Dfoo.encoding=blah", "-Xms1G", "-Dfile.encoding=UTF-16", *localePropertyStrings()]
     }
 
     def "debug option can be set via allJvmArgs"() {
@@ -89,18 +89,18 @@ class JvmOptionsTest extends Specification {
 
     def "managed jvm args includes heap settings"() {
         expect:
-        parse("-Xms1G -XX:-PrintClassHistogram -Xmx2G -Dfoo.encoding=blah").managedJvmArgs == ["-Xms1G", "-Xmx2G", "-Dfile.encoding=${defaultCharset}"]
+        parse("-Xms1G -XX:-PrintClassHistogram -Xmx2G -Dfoo.encoding=blah").managedJvmArgs == ["-Xms1G", "-Xmx2G", "-Dfile.encoding=${defaultCharset}", *localePropertyStrings()]
     }
 
     def "managed jvm args includes file encoding"() {
         expect:
-        parse("-XX:-PrintClassHistogram -Dfile.encoding=klingon-16 -Dfoo.encoding=blah").managedJvmArgs == ["-Dfile.encoding=klingon-16"]
-        parse("-XX:-PrintClassHistogram -Dfoo.encoding=blah").managedJvmArgs == ["-Dfile.encoding=${defaultCharset}"]
+        parse("-XX:-PrintClassHistogram -Dfile.encoding=klingon-16 -Dfoo.encoding=blah").managedJvmArgs == ["-Dfile.encoding=klingon-16", *localePropertyStrings()]
+        parse("-XX:-PrintClassHistogram -Dfoo.encoding=blah").managedJvmArgs == ["-Dfile.encoding=${defaultCharset}", *localePropertyStrings()]
     }
 
     def "managed jvm args includes JMX settings"() {
         expect:
-        parse("-Dfile.encoding=utf-8 -Dcom.sun.management.jmxremote").managedJvmArgs == ["-Dcom.sun.management.jmxremote", "-Dfile.encoding=utf-8"]
+        parse("-Dfile.encoding=utf-8 -Dcom.sun.management.jmxremote").managedJvmArgs == ["-Dcom.sun.management.jmxremote", "-Dfile.encoding=utf-8", *localePropertyStrings()]
     }
 
     def "file encoding can be set as systemproperty"() {
@@ -154,7 +154,9 @@ class JvmOptionsTest extends Specification {
         when:
         parse("-Dfile.encoding=UTF-8 -Dfoo.encoding=blah -Dfile.encoding=UTF-16").copyTo(target)
         then:
-        1 * target.systemProperties({it == ["file.encoding": "UTF-16"]})
+        1 * target.systemProperties({
+            it == new TreeMap(["file.encoding": "UTF-16"] + localeProperties())
+        })
     }
 
     def "can enter debug mode"() {
@@ -212,4 +214,17 @@ class JvmOptionsTest extends Specification {
         opts.jvmArgs(JvmOptions.fromString(optsString))
         opts
     }
+
+    private static List<String> localePropertyStrings(Locale locale = Locale.default) {
+        localeProperties(locale).collect {
+            it.value ? "-D$it.key=$it.value" : "-D$it.key"
+        }*.toString()
+    }
+
+    private static Map<String, String> localeProperties(Locale locale = Locale.default) {
+        ["country", "language", "variant"].sort().collectEntries {
+            ["user.$it".toString(), locale."$it".toString()]
+        }
+    }
+
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/TestApp.java b/subprojects/core/src/test/groovy/org/gradle/process/internal/TestApp.java
new file mode 100644
index 0000000..74c425a
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/TestApp.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.Arrays;
+
+public class TestApp {
+    public static void main(String[] args) {
+        System.out.print("output args: " + Arrays.asList(args));
+        System.err.print("error args: " + Arrays.asList(args));
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ActionExecutionWorkerTest.java b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ActionExecutionWorkerTest.java
index d601f1f..7a6562a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ActionExecutionWorkerTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/ActionExecutionWorkerTest.java
@@ -22,10 +22,12 @@ import org.gradle.messaging.remote.MessagingClient;
 import org.gradle.messaging.remote.ObjectConnection;
 import org.gradle.messaging.remote.internal.MessagingServices;
 import org.gradle.process.internal.WorkerProcessContext;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,6 +38,9 @@ import static org.junit.Assert.fail;
 
 @RunWith(JMock.class)
 public class ActionExecutionWorkerTest {
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider();
+
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final Action<WorkerProcessContext> action = context.mock(Action.class);
     private final ObjectConnection connection = context.mock(ObjectConnection.class);
@@ -45,7 +50,7 @@ public class ActionExecutionWorkerTest {
     private final Address serverAddress = context.mock(Address.class);
     private final ClassLoader appClassLoader = new ClassLoader() {
     };
-    private final ActionExecutionWorker main = new ActionExecutionWorker(action, 12, "<display name>", serverAddress) {
+    private final ActionExecutionWorker main = new ActionExecutionWorker(action, 12, "<display name>", serverAddress, testDirectoryProvider.getTestDirectory()) {
         @Override
         MessagingServices createClient() {
             return messagingServices;
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/BootstrapSecurityManagerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/BootstrapSecurityManagerTest.groovy
index 48bff24..8b39dd2 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/child/BootstrapSecurityManagerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/child/BootstrapSecurityManagerTest.groovy
@@ -64,11 +64,10 @@ class BootstrapSecurityManagerTest extends Specification {
     }
 
     def "installs custom SecurityManager"() {
-        System.setProperty("org.gradle.security.manager", TestSecurityManager.class.name)
         URLClassLoader cl = new URLClassLoader([] as URL[], getClass().classLoader)
 
         given:
-        System.in = createStdInContent()
+        System.in = createStdInContent(TestSecurityManager.class)
 
         when:
         def securityManager = new BootstrapSecurityManager(cl)
@@ -78,11 +77,12 @@ class BootstrapSecurityManagerTest extends Specification {
         System.securityManager instanceof TestSecurityManager
     }
 
-    def createStdInContent(File... classpath) {
+    def createStdInContent(Class securityManager = null, File... classpath) {
         def out = new ByteArrayOutputStream()
         def dataOut = new DataOutputStream(new EncodedStream.EncodedOutput(out))
         dataOut.writeInt(classpath.length)
         classpath.each { dataOut.writeUTF(it.absolutePath) }
+        dataOut.writeUTF(securityManager ? securityManager.name : "")
         return new ByteArrayInputStream(out.toByteArray())
     }
 
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 42ebb24..40cb417 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
@@ -18,9 +18,10 @@ package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
+import org.gradle.internal.classloader.MutableURLClassLoader;
 import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.util.GUtil;
 import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.internal.classloader.MutableURLClassLoader;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -66,7 +67,7 @@ public class ImplementationClassLoaderWorkerTest {
     private class TestImplementationClassLoaderWorker extends ImplementationClassLoaderWorker {
         private TestImplementationClassLoaderWorker(LogLevel logLevel, Collection<String> sharedPackages,
                                                     Collection<URL> implementationClassPath, Action<WorkerContext> workerAction) {
-            super(logLevel, sharedPackages, implementationClassPath, workerAction);
+            super(logLevel, sharedPackages, implementationClassPath, GUtil.serialize(workerAction));
         }
 
         @Override
diff --git a/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
index 8fa5b15..1dfb56a 100644
--- a/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
@@ -60,12 +60,11 @@ class ProfileReportRendererTest extends Specification {
         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">
+        file.text.contains(toPlatformLineSeparators("""<!DOCTYPE html>
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta http-equiv="x-ua-compatible" content="IE=edge"/>
 <title>Profile report</title>
 <link href="css/base-style.css" rel="stylesheet" type="text/css"/>
 <link href="css/style.css" rel="stylesheet" type="text/css"/>
diff --git a/subprojects/core/src/test/groovy/org/gradle/reporting/HtmlReportRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/reporting/HtmlReportRendererTest.groovy
index 5d058be..b12ca78 100644
--- a/subprojects/core/src/test/groovy/org/gradle/reporting/HtmlReportRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/reporting/HtmlReportRendererTest.groovy
@@ -15,46 +15,147 @@
  */
 package org.gradle.reporting
 
-import org.gradle.api.internal.html.SimpleHtmlWriter
+import org.gradle.internal.html.SimpleHtmlWriter
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.TextUtil
 import org.junit.Rule
 import spock.lang.Specification
 
 class HtmlReportRendererTest extends Specification {
-    ReportRenderer<String, SimpleHtmlWriter> abstractHtmlReportRenderer = new ReportRenderer<String, SimpleHtmlWriter>() {
-        @Override
-        void render(String model, SimpleHtmlWriter htmlWriter) {
-            htmlWriter.startElement("pre").characters(model).endElement()
-        }
-    }
     @Rule
     final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     final HtmlReportRenderer renderer = new HtmlReportRenderer()
 
-    def "renders report to stream"() {
-        StringWriter writer = new StringWriter()
+    def "renders HTML to file encoded with UTF-8"() {
+        def destDir = tmpDir.file("out")
+        def reportRenderer = Mock(ReportRenderer)
+        def pageRenderer = Mock(ReportRenderer)
+
+        when:
+        renderer.render("test: \u03b1\u03b2", reportRenderer, destDir)
+
+        then:
+        1 * reportRenderer.render(_, _) >> { String model, HtmlReportBuilder builder ->
+            builder.renderHtmlPage("index.html", model, pageRenderer)
+        }
+        1 * pageRenderer.render(_, _) >> { String model, HtmlPageBuilder<SimpleHtmlWriter> builder ->
+            builder.output.startElement("pre").characters(model).endElement()
+        }
+
+        and:
+        destDir.file("index.html").getText("utf-8") == TextUtil.toPlatformLineSeparators('''<!DOCTYPE html>
+<html>
+<pre>test: \u03b1\u03b2</pre>
+</html>
+''')
+    }
+
+    def "can use writer to render multi-page HTML report"() {
+        def destDir = tmpDir.file("out")
+        def reportRenderer = Mock(ReportRenderer)
+        def pageRenderer = Mock(ReportRenderer)
+
+        when:
+        renderer.render("test: \u03b1\u03b2", reportRenderer, destDir)
+
+        then:
+        1 * reportRenderer.render(_, _) >> { String model, HtmlReportBuilder builder ->
+            builder.renderRawHtmlPage("index.html", model, pageRenderer)
+            builder.renderRawHtmlPage("child/other.html", "[${model}]" as String, pageRenderer)
+        }
+        2 * pageRenderer.render(_, _) >> { String model, HtmlPageBuilder<Writer> builder ->
+            builder.output.write("<html>" + model + "</html>")
+        }
+
+        and:
+        destDir.file("index.html").getText("utf-8") == TextUtil.toPlatformLineSeparators("<html>test: \u03b1\u03b2</html>")
+        destDir.file("child/other.html").getText("utf-8") == TextUtil.toPlatformLineSeparators("<html>[test: \u03b1\u03b2]</html>")
+    }
+
+    def "can use writer to render single page HTML report"() {
+        def destFile = tmpDir.file("out")
+        def pageRenderer = Mock(ReportRenderer)
+
+        when:
+        renderer.renderRawSinglePage("test: \u03b1\u03b2", pageRenderer, destFile)
+
+        then:
+        1 * pageRenderer.render(_, _) >> { String model, HtmlPageBuilder<Writer> builder ->
+            builder.output.write("<html>" + model + "</html>")
+        }
+
+        and:
+        destFile.getText("utf-8") == TextUtil.toPlatformLineSeparators("<html>test: \u03b1\u03b2</html>")
+    }
+
+    def "renders single page HTML to file"() {
+        def destFile = tmpDir.file("out.html")
+        def pageRenderer = Mock(ReportRenderer)
+
         when:
-        renderer.renderer(abstractHtmlReportRenderer).writeTo("test", writer)
+        renderer.renderSinglePage("test: \u03b1\u03b2", pageRenderer, destFile)
 
         then:
-        writer.toString() == TextUtil.toPlatformLineSeparators('''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+        1 * pageRenderer.render(_, _) >> { String model, HtmlPageBuilder<SimpleHtmlWriter> builder ->
+            builder.output.startElement("pre").characters(model).endElement()
+        }
+
+        and:
+        destFile.getText("utf-8") == TextUtil.toPlatformLineSeparators('''<!DOCTYPE html>
 <html>
-<pre>test</pre>
+<pre>test: \u03b1\u03b2</pre>
 </html>
 ''')
     }
 
     def "copies resources into output directory"() {
-        File destFile = tmpDir.file('report.txt')
+        def destDir = tmpDir.file("out")
+        def reportRenderer = Mock(ReportRenderer)
 
-        given:
-        renderer.requireResource(getClass().getResource("base-style.css"))
+        when:
+        renderer.render("model", reportRenderer, destDir)
+
+        then:
+        1 * reportRenderer.render(_, _) >> { String model, HtmlReportBuilder builder ->
+            builder.requireResource(resource("base-style.css"))
+            builder.requireResource(resource("script.js"))
+            builder.requireResource(resource("thing.png"))
+            builder.requireResource(resource("thing.gif"))
+        }
+
+        and:
+        destDir.file("css/base-style.css").file
+        destDir.file("js/script.js").file
+        destDir.file("images/thing.png").file
+        destDir.file("images/thing.gif").file
+    }
+
+    def "copies page resources into output directory"() {
+        def destDir = tmpDir.file("out")
+        def reportRenderer = Mock(ReportRenderer)
+        def pageRenderer = Mock(ReportRenderer)
 
         when:
-        renderer.renderer(abstractHtmlReportRenderer).writeTo("test", destFile)
+        renderer.render("model", reportRenderer, destDir)
 
         then:
-        tmpDir.file("css/base-style.css").file
+        1 * reportRenderer.render(_, _) >> { String model, HtmlReportBuilder builder ->
+            builder.renderHtmlPage("child/page.html", model, pageRenderer)
+        }
+        1 * pageRenderer.render(_, _) >> { String model, HtmlPageBuilder<SimpleHtmlWriter> builder ->
+            def link = builder.requireResource(getClass().getResource("base-style.css"))
+            builder.output.startElement("pre").characters(link).endElement()
+        }
+
+        and:
+        destDir.file("child/page.html").getText("utf-8").contains("<pre>../css/base-style.css</pre>")
+        destDir.file("css/base-style.css").file
+    }
+
+    def resource(String name) {
+        def file = tmpDir.file("tmp", name)
+        file.parentFile.mkdirs()
+        file.text = "not empty"
+        file.toURI().toURL()
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/reporting/TabsRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/reporting/TabsRendererTest.groovy
index 032f3af..c3ce2bb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/reporting/TabsRendererTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/reporting/TabsRendererTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.reporting
 
-import org.gradle.api.internal.html.SimpleHtmlWriter
+import org.gradle.internal.html.SimpleHtmlWriter
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
 import spock.lang.Specification
diff --git a/subprojects/core/src/test/groovy/org/gradle/reporting/TextReportRendererTest.groovy b/subprojects/core/src/test/groovy/org/gradle/reporting/TextReportRendererTest.groovy
deleted file mode 100644
index ee003f7..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/reporting/TextReportRendererTest.groovy
+++ /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.reporting
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class TextReportRendererTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    final TextReportRenderer<String> renderer = new TextReportRenderer<String>() {
-        @Override protected void writeTo(String model, Writer out) {
-            out.write("[")
-            out.write(model)
-            out.write("]")
-        }
-    }
-
-    def "writes report to output file"() {
-        def reportFile = tmpDir.file("dir/report.txt")
-
-        when:
-        renderer.writeTo("test", reportFile)
-
-        then:
-        reportFile.text == "[test]"
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/testfixtures/ProjectBuilderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/testfixtures/ProjectBuilderTest.groovy
index 37fa260..d12c39d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/testfixtures/ProjectBuilderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/testfixtures/ProjectBuilderTest.groovy
@@ -21,14 +21,22 @@ import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.tasks.TaskAction
+import org.gradle.model.Model
+import org.gradle.model.RuleSource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Resources
 import org.junit.Rule
+import spock.lang.Ignore
+import spock.lang.Issue
 import spock.lang.Specification
 
+import java.util.concurrent.atomic.AtomicBoolean
+
 class ProjectBuilderTest extends Specification {
-    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-    @Rule public final Resources resources = new Resources()
+    @Rule
+    public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    @Rule
+    public final Resources resources = new Resources()
 
     def canCreateARootProject() {
 
@@ -46,9 +54,13 @@ class ProjectBuilderTest extends Specification {
         project.gradle.gradleUserHomeDir == project.file('userHome')
     }
 
+    private Project buildProject() {
+        ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+    }
+
     def canCreateARootProjectWithAGivenProjectDir() {
         when:
-        def project = ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+        def project = buildProject()
 
         then:
         project.projectDir == temporaryFolder.testDirectory
@@ -56,9 +68,9 @@ class ProjectBuilderTest extends Specification {
         project.gradle.gradleUserHomeDir == project.file('userHome')
     }
 
-    def canApplyACustomPluginByType() {
+    def canApplyACustomPluginUsingClass() {
         when:
-        def project = ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+        def project = buildProject()
         project.apply plugin: CustomPlugin
 
         then:
@@ -67,16 +79,25 @@ class ProjectBuilderTest extends Specification {
 
     def canApplyACustomPluginById() {
         when:
-        def project = ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+        def project = buildProject()
         project.apply plugin: 'custom-plugin'
 
         then:
         project.tasks.hello instanceof DefaultTask
     }
 
+    def canApplyACustomPluginByType() {
+        when:
+        def project = buildProject()
+        project.pluginManager.apply(CustomPlugin)
+
+        then:
+        project.tasks.hello instanceof DefaultTask
+    }
+
     def canCreateAndExecuteACustomTask() {
         when:
-        def project = ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+        def project = buildProject()
         def task = project.task('custom', type: CustomTask)
         task.doStuff()
 
@@ -86,12 +107,52 @@ class ProjectBuilderTest extends Specification {
 
     def canApplyABuildScript() {
         when:
-        def project = ProjectBuilder.builder().withProjectDir(temporaryFolder.testDirectory).build()
+        def project = buildProject()
         project.apply from: resources.getResource('ProjectBuilderTest.gradle')
 
         then:
         project.tasks.hello instanceof DefaultTask
     }
+
+    def "Can trigger afterEvaluate programmatically"() {
+        setup:
+        def latch = new AtomicBoolean(false)
+
+        when:
+        def project = buildProject()
+
+        project.afterEvaluate {
+            latch.getAndSet(true)
+        }
+
+        project.evaluate()
+
+        then:
+        noExceptionThrown()
+        latch.get()
+    }
+
+    @Ignore
+    @Issue("GRADLE-3136")
+    def "Can trigger afterEvaluate programmatically after calling getTasksByName"() {
+        setup:
+        def latch = new AtomicBoolean(false)
+
+        when:
+        def project = buildProject()
+
+        project.getTasksByName('myTask', true)
+
+        project.afterEvaluate {
+            latch.getAndSet(true)
+        }
+
+        project.evaluate()
+
+        then:
+        noExceptionThrown()
+        latch.get()
+    }
 }
 
 public class CustomTask extends DefaultTask {
@@ -107,4 +168,12 @@ public class CustomPlugin implements Plugin<Project> {
     void apply(Project target) {
         target.task('hello');
     }
+}
+
+public class CustomRuleSource extends RuleSource {
+
+    @Model
+    String foo() {
+        "bar"
+    }
 }
\ No newline at end of file
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 03a221c..e69798f 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/GFileUtilsTest.groovy
@@ -21,8 +21,7 @@ import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
 
-import static org.gradle.util.GFileUtils.mkdirs
-import static org.gradle.util.GFileUtils.parentMkdirs
+import static org.gradle.util.GFileUtils.*
 
 class GFileUtilsTest extends Specification {
 
@@ -118,4 +117,12 @@ three
         ex.message == "Cannot create parent directory '$c' when creating directory '$e' as '$b' is not a directory"
     }
 
-}
+    def "reads file quietly"() {
+        temp.file("foo.txt") << "hey"
+
+        expect:
+        readFileQuietly(temp.file("foo.txt")) == "hey"
+        readFileQuietly(new File("missing")) == "Unable to read file 'missing' due to: org.gradle.api.UncheckedIOException: java.io.FileNotFoundException: File 'missing' does not exist"
+        readFileQuietly(temp.createDir("dir")).startsWith "Unable to read file"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
deleted file mode 100644
index 21b25af..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util
-
-import org.apache.ivy.Ivy
-import org.apache.tools.ant.Main
-import org.codehaus.groovy.runtime.InvokerHelper
-import org.gradle.internal.jvm.Jvm
-import org.gradle.internal.os.OperatingSystem
-import spock.lang.Issue
-import spock.lang.Specification
-
-class GradleVersionTest extends Specification {
-    final GradleVersion version = GradleVersion.current()
-
-    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 "current version has non-null parts"() {
-        expect:
-        version.version
-        version.buildTime
-        version.nextMajor
-        version.baseVersion
-    }
-
-    @Issue("http://issues.gradle.org/browse/GRADLE-1892")
-    def "build time should always print in UTC"() {
-        expect:
-        version.buildTime.endsWith("UTC")
-    }
-
-    def equalsAndHashCode() {
-        expect:
-        Matchers.strictlyEquals(GradleVersion.version('0.9'), GradleVersion.version('0.9'))
-        GradleVersion.version('0.9') != GradleVersion.version('1.0')
-    }
-
-    def canConstructVersionFromString(String version) {
-        expect:
-        def gradleVersion = GradleVersion.version(version)
-        gradleVersion.version == version
-        gradleVersion.toString() == "Gradle ${version}"
-
-        where:
-        version << [
-                '1.0',
-                '12.4.5.67',
-                '1.0-milestone-5',
-                '1.0-milestone-5a',
-                '3.2-rc-2',
-        ]
-    }
-
-    def versionsWithTimestampAreConsideredSnapshots(String version) {
-        expect:
-        def gradleVersion = GradleVersion.version(version)
-        gradleVersion.version == version
-        gradleVersion.snapshot
-
-        where:
-        version << [
-                '0.9-20101220110000+1100',
-                '0.9-20101220110000-0800',
-                '1.2-20120501110000'
-        ]
-    }
-
-    def versionsWithoutTimestampAreNotConsideredSnapshots(String version) {
-        expect:
-        !GradleVersion.version(version).snapshot
-
-        where:
-        version << [
-                '0.9-milestone-5',
-                '2.1-rc-1',
-                '1.2',
-                '1.2.1']
-    }
-
-    def canCompareMajorVersions() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a      | b
-        '0.9'  | '0.8'
-        '1.0'  | '0.10'
-        '10.0' | '2.1'
-        '2.5'  | '2.4'
-    }
-
-    def canComparePointVersions() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a                   | b
-        '0.9.2'             | '0.9.1'
-        '0.10.1'            | '0.9.2'
-        '1.2.3.40'          | '1.2.3.8'
-        '1.2.3.1'           | '1.2.3'
-        '1.2.3.1.4.12.9023' | '1.2.3'
-    }
-
-    def canComparePointVersionAndMajorVersions() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a       | b
-        '0.9.1' | '0.9'
-        '0.10'  | '0.9.1'
-    }
-
-    def canComparePreviewsMilestonesAndRCVersions() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a                 | b
-        '1.0-milestone-2' | '1.0-milestone-1'
-        '1.0-preview-2'   | '1.0-preview-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'
-    }
-
-    def canComparePatchVersion() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a                  | b
-        '1.0-milestone-2a' | '1.0-milestone-2'
-        '1.0-milestone-2b' | '1.0-milestone-2a'
-        '1.0-milestone-3'  | '1.0-milestone-2b'
-        '1.0'              | '1.0-milestone-2b'
-    }
-
-    def canCompareSnapshotVersions() {
-        expect:
-        GradleVersion.version(a) > GradleVersion.version(b)
-        GradleVersion.version(b) < GradleVersion.version(a)
-        GradleVersion.version(a) == GradleVersion.version(a)
-        GradleVersion.version(b) == GradleVersion.version(b)
-
-        where:
-        a                         | b
-        '0.9-20101220110000+1100' | '0.9-20101220100000+1100'
-        '0.9-20101220110000+1000' | '0.9-20101220100000+1100'
-        '0.9-20101220110000-0100' | '0.9-20101220100000+0000'
-        '0.9-20101220110000'      | '0.9-20101220100000'
-        '0.9-20101220110000'      | '0.9-20101220110000+0100'
-        '0.9-20101220110000-0100' | '0.9-20101220110000'
-        '0.9'                     | '0.9-20101220100000+1000'
-        '0.9'                     | '0.9-20101220100000'
-    }
-
-    def "can get version base"() {
-        expect:
-        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
-
-        where:
-        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 "milestones are treated as base versions"() {
-        expect:
-        GradleVersion.version(v).baseVersion == GradleVersion.version(base)
-
-        where:
-        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() {
-        String expectedText = """
-------------------------------------------------------------
-Gradle $version.version
-------------------------------------------------------------
-
-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/LineBufferingOutputStreamTest.java b/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
index 7eaa4b7..9117770 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
+++ b/subprojects/core/src/test/groovy/org/gradle/util/LineBufferingOutputStreamTest.java
@@ -35,7 +35,7 @@ public class LineBufferingOutputStreamTest {
 
     @Before
     public void setUp() {
-        eol = SystemProperties.getLineSeparator();
+        eol = SystemProperties.getInstance().getLineSeparator();
     }
 
     @After
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 81da2f9..aef5694 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/SingleMessageLoggerTest.groovy
@@ -18,13 +18,13 @@ package org.gradle.util
 
 import org.gradle.internal.Factory
 import org.gradle.logging.ConfigureLogging
-import org.gradle.logging.TestAppender
+import org.gradle.logging.TestOutputEventListener
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.junit.Rule
 
 class SingleMessageLoggerTest extends ConcurrentSpec {
-    final TestAppender appender = new TestAppender()
-    @Rule final ConfigureLogging logging = new ConfigureLogging(appender)
+    final TestOutputEventListener outputEventListener = new TestOutputEventListener()
+    @Rule final ConfigureLogging logging = new ConfigureLogging(outputEventListener)
 
     def cleanup() {
         SingleMessageLogger.reset()
@@ -36,14 +36,14 @@ class SingleMessageLoggerTest extends ConcurrentSpec {
         SingleMessageLogger.nagUserWith("nag")
 
         then:
-        appender.toString() == '[WARN nag]'
+        outputEventListener.toString() == '[WARN nag]'
 
         when:
         SingleMessageLogger.reset()
         SingleMessageLogger.nagUserWith("nag")
 
         then:
-        appender.toString() == '[WARN nag][WARN nag]'
+        outputEventListener.toString() == '[WARN nag][WARN nag]'
     }
 
     def "does not log warning while disabled with factory"() {
@@ -60,10 +60,10 @@ class SingleMessageLoggerTest extends ConcurrentSpec {
             SingleMessageLogger.nagUserWith("nag")
             return "result"
         }
-        0 * _._
+        0 * _
 
         and:
-        appender.toString().length() == 0
+        outputEventListener.toString().length() == 0
     }
 
     def "does not log warning while disabled with action"() {
@@ -74,10 +74,10 @@ class SingleMessageLoggerTest extends ConcurrentSpec {
 
         then:
         1 * action.run()
-        0 * _._
+        0 * _
 
         and:
-        appender.toString().length() == 0
+        outputEventListener.toString().length() == 0
     }
 
     def "warnings are disabled for the current thread only"() {
@@ -98,7 +98,7 @@ class SingleMessageLoggerTest extends ConcurrentSpec {
         }
 
         then:
-        appender.toString() == '[WARN nag]'
+        outputEventListener.toString() == '[WARN nag]'
     }
 
     def "deprecation message has next major version"() {
@@ -109,6 +109,6 @@ class SingleMessageLoggerTest extends ConcurrentSpec {
         SingleMessageLogger.nagUserOfDeprecated("foo", "bar")
 
         then:
-        appender.toString() == "[WARN foo has been deprecated and is scheduled to be removed in Gradle ${major.version}. bar.]"
+        outputEventListener.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/TextUtilTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
index dffd28e..e3ec19c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/TextUtilTest.groovy
@@ -85,4 +85,13 @@ class TextUtilTest extends Specification {
         "abc\ndef\nghi"     | " "    | " abc\n def\n ghi"
         "abc\n\t\n   \nghi" | "X"    | "Xabc\n\t\n   \nXghi"
     }
+
+    def shorterOf() {
+        expect:
+        TextUtil.shorterOf("a", "b") == "a"
+        TextUtil.shorterOf("aa", "b") == "b"
+        TextUtil.shorterOf("a", "bb") == "a"
+        TextUtil.shorterOf("", "bb") == ""
+        TextUtil.shorterOf("", "") == ""
+    }
 }
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 d3f4b3f..3abaf73 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/VersionNumberTest.groovy
@@ -16,9 +16,14 @@
 
 package org.gradle.util
 
-import spock.lang.*
+import spock.lang.Specification
 
 class VersionNumberTest extends Specification {
+    def "construction"() {
+        expect:
+        VersionNumber.version(5) == new VersionNumber(5, 0, 0, null)
+    }
+
     def "parsing"() {
         expect:
         VersionNumber.parse("1") == new VersionNumber(1, 0, 0, null)
@@ -36,53 +41,107 @@ class VersionNumberTest extends Specification {
         VersionNumber.parse("1.2.rc1-SNAPSHOT") == new VersionNumber(1, 2, 0, "rc1-SNAPSHOT")
         VersionNumber.parse("1.2.3.rc1-SNAPSHOT") == new VersionNumber(1, 2, 3, "rc1-SNAPSHOT")
 
-        VersionNumber.parse("11.22.33.44") == new VersionNumber(11, 22, 33, "44")
-        VersionNumber.parse("11.44") == new VersionNumber(11, 44, 0, null)
+        VersionNumber.parse("11.22") == new VersionNumber(11, 22, 0, null)
+        VersionNumber.parse("11.22.33") == new VersionNumber(11, 22, 33, null)
+        VersionNumber.parse("11.22.33-eap") == new VersionNumber(11, 22, 33, "eap")
+
         VersionNumber.parse("11.fortyfour") == new VersionNumber(11, 0, 0, "fortyfour")
+
+        VersionNumber.parse("1.0.0.0") == new VersionNumber(1, 0, 0, "0")
+        VersionNumber.parse("1.0.0.0.0.0.0") == new VersionNumber(1, 0, 0, "0.0.0.0")
+        VersionNumber.parse("1.2.3.4-rc1-SNAPSHOT") == new VersionNumber(1, 2, 3, "4-rc1-SNAPSHOT")
+        VersionNumber.parse("1.2.3.4.rc1-SNAPSHOT") == new VersionNumber(1, 2, 3, "4.rc1-SNAPSHOT")
     }
 
-    def "unparseable version number is represented as UNKNOWN (0.0.0)"() {
+    def "parsing with patch number"() {
+        expect:
+        def defaultScheme = VersionNumber.scheme()
+        defaultScheme.parse("1") == new VersionNumber(1, 0, 0, null)
+        defaultScheme.parse("1.2") == new VersionNumber(1, 2, 0, null)
+        defaultScheme.parse("1.2.3") == new VersionNumber(1, 2, 3, null)
+        defaultScheme.parse("1.2.3-qualifier") == new VersionNumber(1, 2, 3, "qualifier")
+        defaultScheme.parse("1.2.3.4") == new VersionNumber(1, 2, 3, "4")
+
+        def patchScheme = VersionNumber.withPatchNumber()
+        patchScheme.parse("1") == new VersionNumber(1, 0, 0, null)
+        patchScheme.parse("1.2") == new VersionNumber(1, 2, 0, null)
+        patchScheme.parse("1.2.3") == new VersionNumber(1, 2, 3, null)
+        patchScheme.parse("1.2.3.4") == new VersionNumber(1, 2, 3, 4, null)
+        patchScheme.parse("1.2.3_4") == new VersionNumber(1, 2, 3, 4, null)
+        patchScheme.parse("1.2.3.4-qualifier") == new VersionNumber(1, 2, 3, 4, "qualifier")
+        patchScheme.parse("1.2.3.4.qualifier") == new VersionNumber(1, 2, 3, 4, "qualifier")
+        patchScheme.parse("1.2.3.4.5.6") == new VersionNumber(1, 2, 3, 4, "5.6")
+    }
+
+    def "unparseable version number is represented as UNKNOWN (0.0.0.0)"() {
         expect:
         VersionNumber.parse(null) == VersionNumber.UNKNOWN
         VersionNumber.parse("") == VersionNumber.UNKNOWN
         VersionNumber.parse("foo") == VersionNumber.UNKNOWN
         VersionNumber.parse("1.") == VersionNumber.UNKNOWN
         VersionNumber.parse("1.2.3-") == VersionNumber.UNKNOWN
+        VersionNumber.parse(".") == VersionNumber.UNKNOWN
+        VersionNumber.parse("_") == VersionNumber.UNKNOWN
+        VersionNumber.parse("-") == VersionNumber.UNKNOWN
+        VersionNumber.parse(".1") == VersionNumber.UNKNOWN
+        VersionNumber.parse("a.1") == VersionNumber.UNKNOWN
+        VersionNumber.parse("1_2") == VersionNumber.UNKNOWN
+        VersionNumber.parse("1_2_2") == VersionNumber.UNKNOWN
+        VersionNumber.parse("1.2.3_4") == VersionNumber.UNKNOWN
     }
 
     def "accessors"() {
         when:
-        def version = new VersionNumber(1, 2, 3, "foo")
+        def version = new VersionNumber(1, 2, 3, 4, "foo")
 
         then:
         version.major == 1
         version.minor == 2
         version.micro == 3
+        version.patch == 4
         version.qualifier == "foo"
     }
 
     def "string representation"() {
         expect:
-        new VersionNumber(1, 0, 0, null).toString() == "1.0.0"
-        new VersionNumber(1, 2, 3, "foo").toString() == "1.2.3-foo"
+        VersionNumber.parse("1.0").toString() == "1.0.0"
+        VersionNumber.parse("1.2.3").toString() == "1.2.3"
+        VersionNumber.parse("1.2.3.4").toString() == "1.2.3-4"
+        VersionNumber.parse("1-rc-1").toString() == "1.0.0-rc-1"
+        VersionNumber.parse("1.2.3-rc-1").toString() == "1.2.3-rc-1"
+
+        def patchScheme = VersionNumber.withPatchNumber()
+        patchScheme.parse("1").toString() == "1.0.0.0"
+        patchScheme.parse("1.2").toString() == "1.2.0.0"
+        patchScheme.parse("1.2.3").toString() == "1.2.3.0"
+        patchScheme.parse("1.2.3.4").toString() == "1.2.3.4"
+        patchScheme.parse("1.2-rc-1").toString() == "1.2.0.0-rc-1"
     }
 
     def "equality"() {
+        def version = new VersionNumber(1, 1, 1, 1, null)
+        def qualified = new VersionNumber(1, 1, 1, 1, "beta-2")
+
         expect:
-        new VersionNumber(1, 1, 1, null) == 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, "rc") != new VersionNumber(1, 1, 1, null)
+        new VersionNumber(1, 1, 1, 1, null) Matchers.strictlyEqual(version)
+        new VersionNumber(2, 1, 1, 1, null) != version
+        new VersionNumber(1, 2, 1, 1, null) != version
+        new VersionNumber(1, 1, 2, 1, null) != version
+        new VersionNumber(1, 1, 1, 2, null) != version
+        new VersionNumber(1, 1, 1, 1, "rc") != version
+        new VersionNumber(1, 1, 1, 1, "beta-2") Matchers.strictlyEqual(qualified)
+        new VersionNumber(1, 1, 1, 1, "beta-3") != qualified
     }
 
     def "comparison"() {
         expect:
         (new VersionNumber(1, 1, 1, null) <=> new VersionNumber(1, 1, 1, null)) == 0
+        (new VersionNumber(1, 1, 1, null) <=> new VersionNumber(1, 1, 1, 0, null)) == 0
 
         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, 2, null) > new VersionNumber(1, 1, 1, null)
         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")
diff --git a/subprojects/core/src/test/resources/META-INF/gradle-plugins/custom-plugin.properties b/subprojects/core/src/test/resources/META-INF/gradle-plugins/org.gradle.custom-plugin.properties
similarity index 100%
rename from subprojects/core/src/test/resources/META-INF/gradle-plugins/custom-plugin.properties
rename to subprojects/core/src/test/resources/META-INF/gradle-plugins/org.gradle.custom-plugin.properties
diff --git a/subprojects/core/src/test/resources/META-INF/gradle-plugins/org.gradle.custom-rule-source.properties b/subprojects/core/src/test/resources/META-INF/gradle-plugins/org.gradle.custom-rule-source.properties
new file mode 100644
index 0000000..f90f3d8
--- /dev/null
+++ b/subprojects/core/src/test/resources/META-INF/gradle-plugins/org.gradle.custom-rule-source.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.testfixtures.CustomRuleSource
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
old mode 100644
new mode 100755
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 20ffb37..2bbb8b2 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,13 +15,13 @@
  */
 package org.gradle.api.internal.file;
 
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 
 import java.io.File;
 
 public class TestFiles {
-    private static final FileSystem FILE_SYSTEM = NativeServices.getInstance().get(FileSystem.class);
+    private static final FileSystem FILE_SYSTEM = NativeServicesTestFixture.getInstance().get(FileSystem.class);
     private static final DefaultFileLookup FILE_LOOKUP = new DefaultFileLookup(FILE_SYSTEM);
 
     public static FileLookup fileLookup() {
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 e1ac786..47ea3c5 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
@@ -63,7 +63,7 @@ public abstract class AbstractSpockTaskTest extends Specification {
     }
 
     public <T extends AbstractTask> T createTask(Class<T> type, Project project, String name) {
-        Task task = TASK_FACTORY.createChild(project, new DirectInstantiator()).createTask(
+        Task task = TASK_FACTORY.createChild(project, DirectInstantiator.INSTANCE).createTask(
                 GUtil.map(Task.TASK_TYPE, type,
                         Task.TASK_NAME, name))
         assert type.isAssignableFrom(task.getClass())
@@ -112,8 +112,8 @@ public abstract class AbstractSpockTaskTest extends Specification {
     def testDependsOn() {
         Task dependsOnTask = createTask(project, "somename");
         Task task = createTask(project, TEST_TASK_NAME);
-        project.getTasks().add("path1");
-        project.getTasks().add("path2");
+        project.getTasks().create("path1");
+        project.getTasks().create("path2");
 
         when:
         task.dependsOn(Project.PATH_SEPARATOR + "path1");
@@ -144,6 +144,18 @@ public abstract class AbstractSpockTaskTest extends Specification {
         new ArrayList() ==  getTask().getActions()
     }
 
+    def testSetActions() {
+        when:
+        Action action1 = Actions.doNothing();
+        getTask().actions = [action1]
+
+        Action action2 = Actions.doNothing();
+        getTask().actions = [action2]
+
+        then:
+        [new AbstractTask.TaskActionWrapper(action2)] ==  getTask().actions
+    }
+
     def testAddActionWithNull() {
         when:
         getTask().doLast((Closure) null)
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 0e6f261..31e9201 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
@@ -16,13 +16,13 @@
 
 package org.gradle.api.tasks;
 
+import com.google.common.collect.Lists;
 import groovy.lang.Closure;
 import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.internal.AbstractTask;
-import org.gradle.internal.Actions;
 import org.gradle.api.internal.DependencyInjectingInstantiator;
 import org.gradle.api.internal.project.AbstractProject;
 import org.gradle.api.internal.project.DefaultProject;
@@ -32,18 +32,20 @@ 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.Actions;
 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.TestUtil;
 import org.gradle.util.JUnit4GroovyMockery;
+import org.gradle.util.TestUtil;
 import org.jmock.Expectations;
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.hamcrest.Matchers.notNullValue;
@@ -130,6 +132,20 @@ public abstract class AbstractTaskTest {
         assertTrue(getTask().getActions().isEmpty());
     }
 
+    @Test
+    public void testSetActions() {
+        getTask().deleteAllActions();
+        getTask().getActions().add(Actions.doNothing());
+        getTask().getActions().add(Actions.doNothing());
+        assertEquals(2, getTask().getActions().size());
+
+        List<Action<? super Task>> actions = Lists.newArrayList();
+        actions.add(Actions.doNothing());
+
+        getTask().setActions(actions);
+        assertEquals(getTask().getActions().size(), 1);
+    }
+
     @Test(expected = InvalidUserDataException.class)
     public void testAddActionWithNull() {
         getTask().doLast((Closure) null);
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/logging/ConfigureLogging.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/ConfigureLogging.groovy
new file mode 100644
index 0000000..c2ff7b4
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/ConfigureLogging.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.logging.internal.OutputEventListener
+import org.gradle.logging.internal.slf4j.OutputEventListenerBackedLogger
+import org.gradle.logging.internal.slf4j.OutputEventListenerBackedLoggerContext
+import org.junit.rules.ExternalResource
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import java.util.logging.LogManager
+
+class ConfigureLogging extends ExternalResource {
+    private final OutputEventListener listener
+    private final OutputEventListenerBackedLoggerContext context
+    private final OutputEventListenerBackedLogger logger
+
+    ConfigureLogging(OutputEventListener listener) {
+        this.listener = listener
+        context = LoggerFactory.ILoggerFactory as OutputEventListenerBackedLoggerContext
+        logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as OutputEventListenerBackedLogger
+    }
+
+    @Override
+    protected void before() {
+        attachListener()
+    }
+
+    public void attachListener() {
+        context.outputEventListener = listener
+        context.level = LogLevel.DEBUG
+    }
+
+    @Override
+    protected void after() {
+        resetLogging()
+    }
+
+    public void resetLogging() {
+        context.reset()
+        LogManager.getLogManager().reset()
+    }
+
+    public void setLevel(LogLevel level) {
+        context.level = level
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestOutputEventListener.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestOutputEventListener.groovy
new file mode 100644
index 0000000..6e2a048
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestOutputEventListener.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging
+
+import org.gradle.logging.internal.LogEvent
+import org.gradle.logging.internal.OutputEvent
+import org.gradle.logging.internal.OutputEventListener
+
+class TestOutputEventListener implements OutputEventListener {
+    final StringWriter writer = new StringWriter()
+
+    @Override
+    String toString() {
+        return writer.toString()
+    }
+
+    void reset() {
+        writer
+    }
+
+    @Override
+    synchronized void onOutput(OutputEvent event) {
+        LogEvent logEvent = event as LogEvent
+        writer.append("[")
+        writer.append(logEvent.logLevel.toString())
+        writer.append(' ')
+        writer.append(logEvent.message)
+        writer.append("]")
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestStyledTextOutput.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestStyledTextOutput.groovy
index 892fa24..35a8563 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestStyledTextOutput.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/logging/TestStyledTextOutput.groovy
@@ -48,7 +48,7 @@ class TestStyledTextOutput extends AbstractStyledTextOutput {
     String getValue() {
         StringBuilder normalised = new StringBuilder()
 
-        String eol = SystemProperties.lineSeparator
+        String eol = SystemProperties.instance.lineSeparator
         boolean inStackTrace = false
         new StringTokenizer(result.toString().replaceAll(eol, '\n'), '\n', true).each { String line ->
             if (line == '\n') {
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
index d16ee8d..54c3a2e 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
@@ -21,6 +21,7 @@ import org.junit.rules.ExternalResource
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
+import java.util.concurrent.AbstractExecutorService
 import java.util.concurrent.CopyOnWriteArraySet
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
@@ -67,6 +68,7 @@ class ConcurrentTestUtil extends ExternalResource {
     //simplistic polling assertion. attempts asserting every x millis up to some max timeout
     static void poll(int timeout = 10, Closure assertion) {
         def expiry = System.currentTimeMillis() + timeout * 1000 // convert to ms
+        long sleepTime = 100
         while(true) {
             try {
                 assertion()
@@ -75,7 +77,8 @@ class ConcurrentTestUtil extends ExternalResource {
                 if (System.currentTimeMillis() > expiry) {
                     throw t
                 }
-                Thread.sleep(100);
+                sleepTime = Math.min(250, (long) (sleepTime * 1.2))
+                Thread.sleep(sleepTime);
             }
         }
     }
@@ -89,6 +92,11 @@ class ConcurrentTestUtil extends ExternalResource {
             StoppableExecutor create(String displayName) {
                 return new StoppableExecutorStub(ConcurrentTestUtil.this)
             }
+
+            StoppableExecutor create(String displayName, int fixedSize) {
+                // Ignores size of thread pool
+                return new StoppableExecutorStub(ConcurrentTestUtil.this)
+            }
         }
     }
 
@@ -166,7 +174,7 @@ class ConcurrentTestUtil extends ExternalResource {
         }
     }
 
-    private void onFailure(Throwable t) {
+    private void failed(Throwable t) {
         lock.lock()
         try {
             if (failureHandler != null) {
@@ -189,7 +197,7 @@ class ConcurrentTestUtil extends ExternalResource {
             LOG.info("Waiting for test threads to complete.")
             while (!threads.isEmpty()) {
                 if (!threadsChanged.awaitUntil(timeout)) {
-                    onFailure(new IllegalStateException("Timeout waiting for test threads to complete."))
+                    failed(new IllegalStateException("Timeout waiting for test threads to complete."))
                     break;
                 }
             }
@@ -235,7 +243,7 @@ class ConcurrentTestUtil extends ExternalResource {
         try {
             threads.remove(thread)
             if (failure) {
-                onFailure(failure)
+                failed(failure)
             }
             threadsChanged.signalAll()
         } finally {
@@ -426,7 +434,7 @@ class CompositeTestParticipant extends AbstractTestParticipant {
     }
 }
 
-class StoppableExecutorStub implements StoppableExecutor {
+class StoppableExecutorStub extends AbstractExecutorService implements StoppableExecutor {
     final ConcurrentTestUtil owner
     final Set<TestThread> threads = new CopyOnWriteArraySet<TestThread>()
 
@@ -450,6 +458,26 @@ class StoppableExecutorStub implements StoppableExecutor {
     void execute(Runnable runnable) {
         threads.add(owner.startThread(runnable))
     }
+
+    void shutdown() {
+        throw new UnsupportedOperationException()
+    }
+
+    List<Runnable> shutdownNow() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean isShutdown() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean isTerminated() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        throw new UnsupportedOperationException()
+    }
 }
 
 class AbstractAsyncAction {
@@ -493,7 +521,7 @@ class StartAsyncAction extends AbstractAsyncAction {
     }
 
     /**
-     * Runs the given action, and then waits until another another thread calls {@link #done()}.  Asserts that the start action does not block waiting for
+     * Runs the given action, and then waits until another thread calls {@link #done()}.  Asserts that the start action does not block waiting for
      * the async action to complete.
      *
      * @param action The start action
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy
index 572f624..203f47b 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestUtil.groovy
@@ -135,7 +135,7 @@ class TestUtil {
      static String createUniqueId() {
          return new UID().toString();
      }
- }
+}
 
 
 interface TestClosure {
diff --git a/subprojects/cpp/cpp.gradle b/subprojects/cpp/cpp.gradle
deleted file mode 100644
index 5881e21..0000000
--- a/subprojects/cpp/cpp.gradle
+++ /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.
- */
-
-dependencies {
-    compile libraries.groovy
-    compile project(':core')
-    compile project(":plugins")
-    compile project(":ide")
-    compile libraries.commons_io
-    integTestRuntime project(":maven")
-}
-
-useTestFixtures()
-useTestFixtures(project: ":messaging")
-useTestFixtures(sourceSet: "testFixtures")
-
-//useClassycle()
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.groovy
deleted file mode 100755
index 05af27d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.groovy
+++ /dev/null
@@ -1,207 +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.ide.visualstudio
-
-import org.gradle.ide.visualstudio.fixtures.FiltersFile
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-
-class VisualStudioFileCustomizationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    def app = new CppHelloWorldApp()
-
-    def setup() {
-        app.writeSources(file("src/main"))
-        buildFile << """
-    apply plugin: 'cpp'
-    apply plugin: 'visual-studio'
-
-    model {
-        platforms {
-            win32 {
-                architecture "i386"
-            }
-        }
-        buildTypes {
-            debug
-            release
-        }
-    }
-    executables {
-        main {}
-    }
-"""
-    }
-
-    def "can specify location of generated files"() {
-        when:
-        file("gradlew.bat") << "dummy wrapper"
-        buildFile << '''
-    model {
-        visualStudio {
-            projects.all { project ->
-                projectFile.location = "very/deeply/nested/${project.name}.vcxproj"
-                filtersFile.location = "other/filters.vcxproj.filters"
-            }
-            solutions.all {
-                solutionFile.location = "vs/${it.name}.solution"
-            }
-        }
-    }
-'''
-        and:
-        run "mainVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainExeVisualStudio"
-
-        and:
-        final projectFile = projectFile("very/deeply/nested/mainExe.vcxproj")
-        assert projectFile.headerFiles == app.headerFiles*.withPath("../../../src/main").sort()
-        assert projectFile.sourceFiles == ['../../../build.gradle'] + app.sourceFiles*.withPath("../../../src/main").sort()
-        projectFile.projectConfigurations.values().each {
-            assert it.buildCommand == "../../../gradlew.bat -p \"../../..\" :install${it.name.capitalize()}MainExecutable"
-            assert it.outputFile == OperatingSystem.current().getExecutableName("../../../build/install/mainExecutable/${it.name}/lib/main")
-        }
-        def filtersFile = filtersFile("other/filters.vcxproj.filters")
-
-        and:
-        final mainSolution = solutionFile("vs/mainExe.solution")
-        mainSolution.assertHasProjects("mainExe")
-        mainSolution.assertReferencesProject(projectFile, ['debug', 'release'])
-
-        // Ensure that clean handles custom file locations
-        when:
-        run "cleanVisualStudio"
-
-        then:
-        projectFile.projectFile.assertDoesNotExist()
-        filtersFile.file.assertDoesNotExist()
-        mainSolution.file.assertDoesNotExist()
-    }
-
-    def "can add xml configuration to generated project files"() {
-        when:
-        buildFile << """
-    model {
-        visualStudio {
-            projects.all { project ->
-                projectFile.withXml { xml ->
-                    Node globals = xml.asNode().PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
-                    globals.appendNode("ExtraInfo", "Some extra info")
-                    globals.appendNode("ProjectName", project.name)
-                }
-            }
-        }
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.globals.ExtraInfo[0].text() == "Some extra info"
-        projectFile.globals.ProjectName[0].text() == "mainExe"
-    }
-
-    def "can add xml configuration to generated filter files"() {
-        when:
-        buildFile << '''
-    model {
-        visualStudio {
-            projects.all { project ->
-                filtersFile.withXml { xml ->
-                    xml.asNode().appendNode("ExtraContent", "Filter - ${project.name}")
-                }
-            }
-        }
-    }
-'''
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final filtersFile = filtersFile("mainExe.vcxproj.filters")
-        filtersFile.xml.ExtraContent[0].text() == "Filter - mainExe"
-    }
-
-    def "can add text content to generated solution files"() {
-        when:
-        buildFile << '''
-    model {
-        visualStudio {
-            solutions.all { solution ->
-                solution.solutionFile.withContent { content ->
-                    String projectList = solution.projects.collect({it.name}).join(',')
-                    int insertPos = text.lastIndexOf("EndGlobal")
-                    content.text = content.text.replace("EndGlobal", """
-    GlobalSection(MyGlobalSection)
-       Project-list: ${projectList}
-    EndGlobalSection
-EndGlobal
-""")
-                }
-            }
-        }
-    }
-'''
-
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final solutionFile = solutionFile("mainExe.sln")
-        solutionFile.content.contains "GlobalSection(MyGlobalSection)"
-        solutionFile.content.contains "Project-list: mainExe"
-    }
-
-    def "can configure gradle command line"() {
-        when:
-        buildFile << """
-    executables {
-        main {}
-    }
-    tasks.withType(GenerateProjectFileTask) {
-        it.gradleExe "myCustomGradleExe"
-        it.gradleArgs "--configure-on-demand --another"
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.projectConfigurations.values().each {
-            assert it.buildCommand == "myCustomGradleExe --configure-on-demand --another :install${it.name.capitalize()}MainExecutable"
-        }
-    }
-
-    private SolutionFile solutionFile(String path) {
-        return new SolutionFile(file(path))
-    }
-
-    private ProjectFile projectFile(String path) {
-        return new ProjectFile(file(path))
-    }
-
-    private FiltersFile filtersFile(String path) {
-        return new FiltersFile(file(path))
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
deleted file mode 100755
index 6cea134..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,316 +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.ide.visualstudio
-
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-
-class VisualStudioMultiProjectIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    private final Set<String> projectConfigurations = ['debug', 'release'] as Set
-
-    def app = new CppHelloWorldApp()
-
-    def setup() {
-        buildFile << """
-    subprojects {
-        apply plugin: 'cpp'
-        apply plugin: 'visual-studio'
-
-        model {
-            platforms {
-                win32 {
-                    architecture "i386"
-                }
-            }
-            buildTypes {
-                debug
-                release
-            }
-        }
-    }
-
-"""
-    }
-
-    def "create visual studio solution for executable that depends on static library in another project"() {
-        when:
-        app.executable.writeSources(file("exe/src/main"))
-        app.library.writeSources(file("lib/src/hello"))
-
-        settingsFile.text = "include ':exe', ':lib'"
-        file("exe", "build.gradle") << """
-    executables {
-        main {}
-    }
-    sources.main.cpp.lib project: ':lib', library: 'hello', linkage: 'static'
-"""
-        file("lib", "build.gradle") << """
-    libraries {
-        hello {}
-    }
-"""
-        and:
-        run ":exe:mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
-        exeProject.assertHasComponentSources(app.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations.values().each {
-            assert it.includePath == filePath("src/main/headers", "../lib/src/hello/headers")
-            assert it.buildCommand == "gradle -p \"..\" :exe:install${it.name.capitalize()}MainExecutable"
-        }
-
-        and:
-        final libProject = projectFile("lib/lib_helloLib.vcxproj")
-        libProject.assertHasComponentSources(app.library, "src/hello")
-        libProject.projectConfigurations.keySet() == projectConfigurations
-        libProject.projectConfigurations.values().each {
-            assert it.includePath == filePath("src/hello/headers")
-            assert it.buildCommand == "gradle -p \"..\" :lib:${it.name}HelloStaticLibrary"
-        }
-
-        and:
-        final mainSolution = solutionFile("exe/exe_mainExe.sln")
-        mainSolution.assertHasProjects("exe_mainExe", "lib_helloLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(libProject, projectConfigurations)
-    }
-
-    def "create visual studio solution for executable that transitively depends on multiple projects"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("greet/src/greetings"))
-
-        and:
-        settingsFile.text = "include ':exe', ':lib', ':greet'"
-        buildFile << """
-        project(":exe") {
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'hello'
-        }
-        project(":lib") {
-            apply plugin: "cpp"
-            libraries {
-                hello {}
-            }
-            sources.hello.cpp.lib project: ':greet', library: 'greetings', linkage: 'static'
-        }
-        project(":greet") {
-            apply plugin: "cpp"
-            libraries {
-                greetings {}
-            }
-        }
-        """
-
-        when:
-        succeeds ":exe:mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
-        final helloProject = projectFile("lib/lib_helloDll.vcxproj")
-        final greetProject = projectFile("greet/greet_greetingsLib.vcxproj")
-        final mainSolution = solutionFile("exe/exe_mainExe.sln")
-
-        and:
-        mainSolution.assertHasProjects("exe_mainExe", "lib_helloDll", "greet_greetingsLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
-
-        and:
-        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/hello/headers")
-        helloProject.projectConfigurations['debug'].includePath == filePath("src/hello/headers", "../greet/src/greetings/headers")
-        greetProject.projectConfigurations['debug'].includePath == filePath("src/greetings/headers")
-    }
-
-    def "create visual studio solution where multiple components have same name"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.writeSources(file("exe/src/main"), file("lib/src/main"), file("greet/src/main"))
-
-        and:
-        settingsFile.text = "include ':exe', ':lib', ':greet'"
-        buildFile << """
-        project(":exe") {
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'main'
-        }
-        project(":lib") {
-            apply plugin: "cpp"
-            libraries {
-                main {}
-            }
-            sources.main.cpp.lib project: ':greet', library: 'main', linkage: 'static'
-        }
-        project(":greet") {
-            apply plugin: "cpp"
-            libraries {
-                main {}
-            }
-        }
-        """
-
-        when:
-        succeeds ":exe:mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
-        final helloProject = projectFile("lib/lib_mainDll.vcxproj")
-        final greetProject = projectFile("greet/greet_mainLib.vcxproj")
-        final mainSolution = solutionFile("exe/exe_mainExe.sln")
-
-        and:
-        mainSolution.assertHasProjects("exe_mainExe", "lib_mainDll", "greet_mainLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
-
-        and:
-        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/main/headers")
-        helloProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../greet/src/main/headers")
-        greetProject.projectConfigurations['debug'].includePath == filePath("src/main/headers")
-    }
-
-    def "create visual studio solution for executable with project dependency cycle"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("exe/src/greetings"))
-
-        and:
-        settingsFile.text = "include ':exe', ':lib'"
-        buildFile << """
-        project(":exe") {
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            libraries {
-                greetings {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'hello'
-        }
-        project(":lib") {
-            apply plugin: "cpp"
-            libraries {
-                hello {}
-            }
-            sources.hello.cpp.lib project: ':exe', library: 'greetings', linkage: 'static'
-        }
-        """
-
-        when:
-        succeeds ":exe:mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
-        final helloProject = projectFile("lib/lib_helloDll.vcxproj")
-        final greetProject = projectFile("exe/exe_greetingsLib.vcxproj")
-        final mainSolution = solutionFile("exe/exe_mainExe.sln")
-
-        and:
-        mainSolution.assertHasProjects("exe_mainExe", "lib_helloDll", "exe_greetingsLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
-
-        and:
-        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/hello/headers")
-        helloProject.projectConfigurations['debug'].includePath == filePath("src/hello/headers", "../exe/src/greetings/headers")
-        greetProject.projectConfigurations['debug'].includePath == filePath("src/greetings/headers")
-    }
-
-    def "detects gradle wrapper and uses in vs project"() {
-        when:
-        def gradlew = file("gradlew.bat") << "dummy wrapper"
-
-        settingsFile.text = "include ':exe'"
-        buildFile << """
-    project(':exe') {
-        executables {
-            main {}
-        }
-    }
-"""
-        and:
-        run ":exe:mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
-        exeProject.projectConfigurations.values().each {
-            assert it.buildCommand == "../gradlew.bat -p \"..\" :exe:install${it.name.capitalize()}MainExecutable"
-        }
-    }
-
-    def "cleanVisualStudio removes all generated visual studio files"() {
-        when:
-        settingsFile.text = "include ':exe', ':lib'"
-        buildFile << """
-    project(':exe') {
-        executables {
-            main {}
-        }
-        sources.main.cpp.lib project: ':lib', library: 'main', linkage: 'static'
-    }
-    project(':lib') {
-        libraries {
-            main {}
-        }
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        def generatedFiles = [
-                file("exe/exe_mainExe.sln"),
-                file("exe/exe_mainExe.vcxproj"),
-                file("exe/exe_mainExe.vcxproj.filters"),
-                file("lib/lib_mainDll.sln"),
-                file("lib/lib_mainDll.vcxproj"),
-                file("lib/lib_mainDll.vcxproj.filters")
-        ]
-        generatedFiles*.assertExists()
-
-        when:
-        run "cleanVisualStudio"
-
-        then:
-        generatedFiles*.assertDoesNotExist()
-    }
-
-    private SolutionFile solutionFile(String path) {
-        return new SolutionFile(file(path))
-    }
-
-    private ProjectFile projectFile(String path) {
-        return new ProjectFile(file(path))
-    }
-
-    private static String filePath(String... paths) {
-        return (paths as List).join(";")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
deleted file mode 100755
index fe9e651..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,840 +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.ide.visualstudio
-
-import org.gradle.ide.visualstudio.fixtures.FiltersFile
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.*
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-
-class VisualStudioSingleProjectIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    private final Set<String> projectConfigurations = ['win32Debug', 'win32Release', 'x64Debug', 'x64Release'] as Set
-
-    def app = new CppHelloWorldApp()
-
-    def setup() {
-        buildFile << """
-    apply plugin: 'cpp'
-    apply plugin: 'visual-studio'
-
-    model {
-        platforms {
-            win32 {
-                architecture "i386"
-            }
-            x64 {
-                architecture "amd64"
-            }
-        }
-        buildTypes {
-            debug
-            release
-        }
-    }
-
-"""
-    }
-
-    def "create visual studio solution for single executable"() {
-        when:
-        app.writeSources(file("src/main"))
-        buildFile << """
-    executables {
-        main {}
-    }
-    binaries.all {
-        cppCompiler.define "TEST"
-        cppCompiler.define "foo", "bar"
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainExeVisualStudio"
-
-        and:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.assertHasComponentSources(app, "src/main")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        projectFile.projectConfigurations.values().each {
-            assert it.macros == "TEST;foo=bar"
-            assert it.includePath == filePath("src/main/headers")
-            assert it.buildCommand == "gradle :install${it.name.capitalize()}MainExecutable"
-            assert it.outputFile == OperatingSystem.current().getExecutableName("build/install/mainExecutable/${it.name}/lib/main")
-        }
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe")
-        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
-    }
-
-    def "create visual studio solution for shared and library"() {
-        when:
-        app.library.writeSources(file("src/main"))
-        buildFile << """
-    libraries {
-        main {}
-    }
-"""
-        and:
-        run "mainDllVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainDllVisualStudio"
-
-        and:
-        final projectFile = projectFile("mainDll.vcxproj")
-        projectFile.assertHasComponentSources(app.library, "src/main")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        projectFile.projectConfigurations.values().each {
-            assert it.includePath == filePath("src/main/headers")
-            assert it.buildCommand == "gradle :${it.name}MainSharedLibrary"
-            assert it.outputFile == OperatingSystem.current().getSharedLibraryName("build/binaries/mainSharedLibrary/${it.name}/main")
-        }
-
-        and:
-        final mainSolution = solutionFile("mainDll.sln")
-        mainSolution.assertHasProjects("mainDll")
-        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
-    }
-
-    def "create visual studio solution for static library"() {
-        when:
-        app.library.writeSources(file("src/main"))
-        buildFile << """
-    libraries {
-        main {
-        }
-    }
-"""
-        and:
-        run "mainLibVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainLibVisualStudio"
-
-        and:
-        final projectFile = projectFile("mainLib.vcxproj")
-        projectFile.assertHasComponentSources(app.library, "src/main")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        projectFile.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers")
-
-        and:
-        final mainSolution = solutionFile("mainLib.sln")
-        mainSolution.assertHasProjects("mainLib")
-        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
-    }
-
-    def "lifecycle task creates visual studio solution for buildable static and shared libraries"() {
-        when:
-        app.library.writeSources(file("src/main"))
-        buildFile << """
-    libraries {
-        both {}
-        staticOnly {
-            binaries.withType(SharedLibraryBinary) {
-                buildable false
-            }
-        }
-    }
-"""
-        and:
-        run "bothVisualStudio", "staticOnlyVisualStudio"
-
-        then:
-        executedAndNotSkipped ":bothDllVisualStudio", ":bothLibVisualStudio", ":staticOnlyLibVisualStudio"
-
-        and:
-        file("staticOnlyLib.sln").assertExists()
-        file("staticOnlyDll.sln").assertDoesNotExist()
-    }
-
-    def "create visual studio solution for defined static library"() {
-        when:
-        app.library.writeSources(file("src/main"))
-        buildFile << """
-    libraries {
-        main {
-            binaries.withType(SharedLibraryBinary) {
-                buildable = false
-            }
-        }
-    }
-"""
-        and:
-        run "mainLibVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainLibVisualStudio"
-
-        and:
-        final projectFile = projectFile("mainLib.vcxproj")
-        projectFile.assertHasComponentSources(app.library, "src/main")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        projectFile.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers")
-
-        and:
-        final mainSolution = solutionFile("mainLib.sln")
-        mainSolution.assertHasProjects("mainLib")
-        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
-    }
-
-    def "create visual studio solution for executable that depends on static library"() {
-        when:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        buildFile << """
-    libraries {
-        hello {}
-    }
-    executables {
-        main {}
-    }
-    sources.main.cpp.lib libraries.hello.static
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        exeProject.assertHasComponentSources(app.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
-
-        and:
-        final libProject = projectFile("helloLib.vcxproj")
-        libProject.assertHasComponentSources(app.library, "src/hello")
-        libProject.projectConfigurations.keySet() == projectConfigurations
-        libProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(libProject, projectConfigurations)
-    }
-
-    def "create visual studio solution for executable that depends on shared library"() {
-        when:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        buildFile << """
-    libraries {
-        hello {}
-    }
-    executables {
-        main {}
-    }
-    sources.main.cpp.lib libraries.hello
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        exeProject.assertHasComponentSources(app.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
-
-        and:
-        final dllProject = projectFile("helloDll.vcxproj")
-        dllProject.assertHasComponentSources(app.library, "src/hello")
-        dllProject.projectConfigurations.keySet() == projectConfigurations
-        dllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloDll")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(dllProject, projectConfigurations)
-    }
-
-    def "create visual studio solution for executable that depends on library that depends on another library"() {
-        given:
-        def testApp = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-
-        buildFile << """
-            apply plugin: "cpp"
-            libraries {
-                greetings {}
-                hello {}
-            }
-            executables {
-                main {}
-            }
-            sources {
-                hello.cpp.lib libraries.greetings.static
-                main.cpp.lib libraries.hello
-            }
-        """
-        when:
-        run "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        exeProject.assertHasComponentSources(testApp.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
-
-        and:
-        final helloDllProject = projectFile("helloDll.vcxproj")
-        helloDllProject.assertHasComponentSources(testApp.library, "src/hello")
-        helloDllProject.projectConfigurations.keySet() == projectConfigurations
-        helloDllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers", "src/greetings/headers")
-
-        and:
-        final greetingsLibProject = projectFile("greetingsLib.vcxproj")
-        greetingsLibProject.assertHasComponentSources(testApp.greetingsLibrary, "src/greetings")
-        greetingsLibProject.projectConfigurations.keySet() == projectConfigurations
-        greetingsLibProject.projectConfigurations['win32Debug'].includePath == filePath("src/greetings/headers")
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloDllProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetingsLibProject, projectConfigurations)
-    }
-
-    def "create visual studio solutions for 2 executables that depend on different linkages of the same library"() {
-        when:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        buildFile << """
-    libraries {
-        hello {}
-    }
-    executables {
-        main {}
-        mainStatic {}
-    }
-    sources.main.cpp.lib libraries.hello
-    sources.mainStatic.cpp.source.srcDirs "src/main/cpp"
-    sources.mainStatic.cpp.lib libraries.hello.static
-"""
-        and:
-        run "mainVisualStudio", "mainStaticVisualStudio"
-
-        then:
-        solutionFile("mainExe.sln").assertHasProjects("mainExe", "helloDll")
-        solutionFile("mainStaticExe.sln").assertHasProjects("mainStaticExe", "helloLib")
-
-        and:
-        final exeProject = projectFile("mainExe.vcxproj")
-        final staticExeProject = projectFile("mainStaticExe.vcxproj")
-        exeProject.sourceFiles == staticExeProject.sourceFiles
-        exeProject.headerFiles == []
-        staticExeProject.headerFiles == []
-
-        and:
-        final dllProject = projectFile("helloDll.vcxproj")
-        final libProject = projectFile("helloLib.vcxproj")
-        dllProject.sourceFiles == libProject.sourceFiles
-        dllProject.headerFiles == libProject.headerFiles
-    }
-
-    def "create visual studio solutions for 2 executables that depend on different build types of the same library"() {
-        when:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        buildFile << """
-    libraries {
-        hello {}
-    }
-    executables {
-        main {}
-        mainRelease {
-            targetBuildTypes "release"
-        }
-    }
-    sources.main.cpp.lib libraries.hello
-
-    sources.mainRelease.cpp.source.srcDirs "src/main/cpp"
-    sources.mainRelease.cpp.lib libraries.hello
-"""
-        and:
-        run "mainVisualStudio", "mainReleaseVisualStudio"
-
-        then:
-        solutionFile("mainExe.sln").assertHasProjects("mainExe", "helloDll")
-        solutionFile("mainReleaseExe.sln").assertHasProjects("mainReleaseExe", "helloDll")
-
-        and:
-        final helloProjectFile = projectFile("helloDll.vcxproj")
-        helloProjectFile.projectConfigurations.keySet() == projectConfigurations
-        final mainProjectFile = projectFile("mainExe.vcxproj")
-        mainProjectFile.projectConfigurations.keySet() == projectConfigurations
-        final mainReleaseProjectFile = projectFile("mainReleaseExe.vcxproj")
-        mainReleaseProjectFile.projectConfigurations.keySet() == ['win32', 'x64'] as Set
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertReferencesProject(helloProjectFile, projectConfigurations)
-        final mainReleaseSolution = solutionFile("mainReleaseExe.sln")
-        mainReleaseSolution.assertReferencesProject(helloProjectFile, [win32: 'win32Release', x64: 'x64Release'])
-    }
-
-    def "create visual studio project for executable that targets multiple platforms with the same architecture"() {
-        when:
-        app.writeSources(file("src/main"))
-        buildFile << """
-    model {
-        platforms {
-            otherWin32 {
-                architecture "i386"
-            }
-        }
-    }
-    executables {
-        main {
-            targetPlatforms "win32", "otherWin32"
-        }
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final mainProjectFile = projectFile("mainExe.vcxproj")
-        mainProjectFile.projectConfigurations.keySet() == ['win32Debug', 'otherWin32Debug', 'win32Release', 'otherWin32Release'] as Set
-    }
-
-    def "create visual studio solution for executable that has diamond dependency"() {
-        def testApp = new ExeWithDiamondDependencyHelloWorldApp()
-        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib libraries.hello.shared
-            sources.main.cpp.lib libraries.greetings.static
-            sources.hello.cpp.lib libraries.greetings.static
-        """
-
-        when:
-        succeeds "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        final helloProject = projectFile("helloDll.vcxproj")
-        final greetProject = projectFile("greetingsLib.vcxproj")
-        final mainSolution = solutionFile("mainExe.sln")
-
-        and:
-        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
-
-        and:
-        exeProject.assertHasComponentSources(testApp.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers", "src/greetings/headers")
-    }
-
-    def "create visual studio solution for executable that depends on both static and shared linkage of library"() {
-        given:
-        def testApp = new ExeWithDiamondDependencyHelloWorldApp()
-        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib libraries.hello.shared
-            sources.main.cpp.lib libraries.greetings.shared
-            sources.hello.cpp.lib libraries.greetings.static
-        """
-
-        when:
-        succeeds "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        final helloProject = projectFile("helloDll.vcxproj")
-        final greetDllProject = projectFile("greetingsDll.vcxproj")
-        final greetLibProject = projectFile("greetingsLib.vcxproj")
-        final mainSolution = solutionFile("mainExe.sln")
-
-        and:
-        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib", "greetingsDll")
-        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
-        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetDllProject, projectConfigurations)
-        mainSolution.assertReferencesProject(greetLibProject, projectConfigurations)
-
-        and:
-        exeProject.assertHasComponentSources(testApp.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == projectConfigurations
-        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers", "src/greetings/headers")
-
-        and:
-        helloProject.assertHasComponentSources(testApp.library, "src/hello")
-        helloProject.projectConfigurations.keySet() == projectConfigurations
-        helloProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers", "src/greetings/headers")
-    }
-
-    def "generate visual studio solution for executable with mixed sources"() {
-        given:
-        def testApp = new MixedLanguageHelloWorldApp(toolChain)
-        testApp.writeSources(file("src/main"))
-
-        and:
-        buildFile << """
-            apply plugin: 'c'
-            apply plugin: 'assembler'
-            executables {
-                main {}
-            }
-        """
-
-        when:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.assertHasComponentSources(testApp, "src/main")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        with (projectFile.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/main/headers")
-        }
-
-        and:
-        solutionFile("mainExe.sln").assertHasProjects("mainExe")
-    }
-
-    @RequiresInstalledToolChain(VisualCpp)
-    def "generate visual studio solution for executable with windows resource files"() {
-        given:
-        def resourceApp = new WindowsResourceHelloWorldApp()
-        resourceApp.writeSources(file("src/main"))
-
-        and:
-        buildFile << """
-            apply plugin: 'windows-resources'
-            executables {
-                main {}
-            }
-            binaries.all {
-                rcCompiler.define "TEST"
-                rcCompiler.define "foo", "bar"
-            }
-        """
-
-        when:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        final List<SourceFile> resources = resourceApp.resourceSources
-        final List<SourceFile> sources = resourceApp.sourceFiles - resources
-        assert projectFile.headerFiles == resourceApp.headerFiles*.withPath("src/main").sort()
-        assert projectFile.sourceFiles == ['build.gradle'] + sources*.withPath("src/main").sort()
-        assert projectFile.resourceFiles == resources*.withPath("src/main").sort()
-
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        with (projectFile.projectConfigurations['win32Debug']) {
-            macros == "TEST;foo=bar"
-            includePath == filePath("src/main/headers")
-        }
-
-        and:
-        solutionFile("mainExe.sln").assertHasProjects("mainExe")
-    }
-
-    def "builds solution for component with no source"() {
-        given:
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        when:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.sourceFiles == ['build.gradle']
-        projectFile.headerFiles == []
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-        with (projectFile.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/main/headers")
-        }
-
-        and:
-        solutionFile("mainExe.sln").assertHasProjects("mainExe")
-    }
-
-    def "visual studio project includes headers co-located with sources"() {
-        when:
-        // Write headers so they sit with sources
-        app.allFiles.each {
-            it.writeToFile(file("src/main/cpp/${it.name}"))
-        }
-        buildFile << """
-    executables {
-        main {}
-    }
-    sources.main.cpp.source.include "**/*.cpp"
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainExeVisualStudio"
-
-        and:
-        final projectFile = projectFile("mainExe.vcxproj")
-        assert projectFile.sourceFiles == ['build.gradle'] + app.sourceFiles.collect({"src/main/cpp/${it.name}"}).sort()
-        assert projectFile.headerFiles == app.headerFiles.collect({"src/main/cpp/${it.name}"}).sort()
-    }
-
-    def "visual studio solution with header-only library"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-
-        app.library.headerFiles*.writeToDir(file("src/helloApi"))
-        app.library.sourceFiles*.writeToDir(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                helloApi {}
-                hello {}
-            }
-            sources.main.cpp.lib library: 'helloApi', linkage: 'api' // TODO:DAZ This should not be needed
-            sources.main.cpp.lib library: 'hello'
-            sources.hello.cpp.lib library: 'helloApi', linkage: 'api'
-        """
-
-        when:
-        succeeds "mainVisualStudio"
-
-        then:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloDll", "helloApiDll")
-
-        and:
-        final mainExeProject = projectFile("mainExe.vcxproj")
-        with (mainExeProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/main/headers", "src/helloApi/headers", "src/hello/headers")
-        }
-
-        and:
-        final helloDllProject = projectFile("helloDll.vcxproj")
-        with (helloDllProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/hello/headers", "src/helloApi/headers")
-        }
-
-        and:
-        final helloApiDllProject = projectFile("helloApiDll.vcxproj")
-        with (helloApiDllProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/helloApi/headers")
-        }
-    }
-
-    def "create visual studio solution for executable with variant conditional sources"() {
-        when:
-        app.writeSources(file("src/win32"))
-        app.alternate.writeSources(file("src/x64"))
-        buildFile << """
-    sources {
-        win32 {}
-        x64 {}
-    }
-    executables {
-        main {}
-    }
-    binaries.all {
-        source sources[it.targetPlatform.name]
-    }
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final projectFile = projectFile("mainExe.vcxproj")
-        projectFile.assertHasComponentSources(app, "src/win32", app.alternate, "src/x64")
-        projectFile.projectConfigurations.keySet() == projectConfigurations
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe")
-        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
-    }
-
-    def "visual studio solution with pre-built library"() {
-        given:
-        app.writeSources(file("src/main"))
-        buildFile << """
-    model {
-        repositories {
-            libs(PrebuiltLibraries) {
-                test {
-                    headers.srcDir "libs/test/include"
-                }
-            }
-        }
-    }
-    executables {
-        main {}
-    }
-    sources.main.cpp.lib library: 'test', linkage: 'api'
-"""
-
-        when:
-        run "mainVisualStudio"
-
-        then:
-        executedAndNotSkipped ":mainExeVisualStudio"
-        and:
-
-        then:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe")
-
-        and:
-        final mainExeProject = projectFile("mainExe.vcxproj")
-        with (mainExeProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/main/headers", "libs/test/include")
-        }
-    }
-
-    def "visual studio solution for component graph with library dependency cycle"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        app.greetingsHeader.writeToDir(file("src/hello"))
-        app.greetingsSources*.writeToDir(file("src/greetings"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib library: 'hello'
-            sources.hello.cpp.lib library: 'greetings', linkage: 'static'
-            sources.greetings.cpp.lib library: 'hello', linkage: 'api'
-        """
-
-        when:
-        succeeds "mainVisualStudio"
-
-        then:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
-
-        and:
-        final mainExeProject = projectFile("mainExe.vcxproj")
-        with (mainExeProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/main/headers", "src/hello/headers")
-        }
-
-        and:
-        final helloDllProject = projectFile("helloDll.vcxproj")
-        with (helloDllProject.projectConfigurations['win32Debug']) {
-            includePath == filePath( "src/hello/headers", "src/greetings/headers")
-        }
-
-        and:
-        final greetingsLibProject = projectFile("greetingsLib.vcxproj")
-        with (greetingsLibProject.projectConfigurations['win32Debug']) {
-            includePath == filePath("src/greetings/headers", "src/hello/headers")
-        }
-    }
-
-    def "create visual studio solution where referenced projects have different configurations"() {
-        when:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        buildFile << """
-    libraries {
-        hello {}
-    }
-    executables {
-        main {
-            targetPlatforms "win32"
-            targetBuildTypes "release"
-        }
-    }
-    sources.main.cpp.lib libraries.hello
-"""
-        and:
-        run "mainVisualStudio"
-
-        then:
-        final exeProject = projectFile("mainExe.vcxproj")
-        exeProject.assertHasComponentSources(app.executable, "src/main")
-        exeProject.projectConfigurations.keySet() == ['release'] as Set
-        exeProject.projectConfigurations['release'].includePath == filePath("src/main/headers", "src/hello/headers")
-
-        and:
-        final dllProject = projectFile("helloDll.vcxproj")
-        dllProject.assertHasComponentSources(app.library, "src/hello")
-        dllProject.projectConfigurations.keySet() == projectConfigurations
-        dllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
-
-        and:
-        final mainSolution = solutionFile("mainExe.sln")
-        mainSolution.assertHasProjects("mainExe", "helloDll")
-        mainSolution.assertReferencesProject(exeProject, ['release'])
-        mainSolution.assertReferencesProject(dllProject, [release: 'win32Release'])
-    }
-
-    private SolutionFile solutionFile(String path) {
-        return new SolutionFile(file(path))
-    }
-
-    private ProjectFile projectFile(String path) {
-        return new ProjectFile(file(path))
-    }
-
-    private FiltersFile filtersFile(String path) {
-        return new FiltersFile(file(path))
-    }
-
-    private static String filePath(String... paths) {
-        return (paths as List).join(';')
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.groovy
deleted file mode 100644
index e2b281d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.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.ide.visualstudio.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class VisualStudioPluginIntegrationTest extends WellBehavedPluginTest {
-    @Override
-    String getPluginId() {
-        "visual-studio"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/CppAutoTestedSamplesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/CppAutoTestedSamplesIntegrationTest.groovy
deleted file mode 100644
index bc6edf9..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/CppAutoTestedSamplesIntegrationTest.groovy
+++ /dev/null
@@ -1,31 +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.nativebinaries.language
-
-import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
-import org.junit.Test
-
-class CppAutoTestedSamplesIntegrationTest extends AbstractAutoTestedSamplesTest{
-
-    @Test
-    void runSamples() {
-        runSamplesFrom("subprojects/cpp/src/main")
-    }
-
-}
-
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy
deleted file mode 100644
index cbffb4f..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,22 +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.nativebinaries.language.assembler.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class AssemblerPluginIntegrationTest extends WellBehavedPluginTest {
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/c/plugins/CPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/c/plugins/CPluginIntegrationTest.groovy
deleted file mode 100644
index 4a28638..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/c/plugins/CPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,22 +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.nativebinaries.language.c.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class CPluginIntegrationTest extends WellBehavedPluginTest {
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100755
index e506efb..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,513 +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.nativebinaries.language.cpp
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.GUtil
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Assume
-import spock.lang.Ignore
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-import static org.gradle.util.TextUtil.escapeString
-
-abstract class AbstractLanguageIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    static boolean multiPlatformsAvailable = true
-
-    IncrementalHelloWorldApp app
-    String mainCompileTask
-    String libraryCompileTask
-    TestFile sourceFile
-    TestFile headerFile
-    List<TestFile> librarySourceFiles = []
-
-    abstract IncrementalHelloWorldApp getHelloWorldApp();
-
-    String getCompilerTool() {
-        "${app.sourceType}Compiler"
-    }
-
-    String getSourceType() {
-        GUtil.toCamelCase(app.sourceType)
-    }
-
-    def "setup"() {
-        app = getHelloWorldApp()
-        mainCompileTask = ":compileMainExecutableMain${sourceType}"
-        libraryCompileTask = ":compileHelloSharedLibraryHello${sourceType}"
-
-        buildFile << app.pluginScript
-        buildFile << app.extraConfiguration
-
-        buildFile << """
-            executables {
-                main {
-                    binaries.all {
-                        lib libraries.hello
-                    }
-                }
-            }
-            libraries {
-                hello {
-                    binaries.withType(SharedLibraryBinary) {
-                        ${app.compilerDefine("DLL_EXPORT")}
-                    }
-                }
-            }
-        """
-        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"))
-        }
-    }
-
-    def "does not re-execute build with no change"() {
-        given:
-        run "installMainExecutable"
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        nonSkippedTasks.empty
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "rebuilds executable with source file change"() {
-        given:
-        run "installMainExecutable"
-
-        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
-    }
-
-    def "recompiles but does not relink executable with source comment change"() {
-        given:
-        run "installMainExecutable"
-        maybeWait()
-
-        when:
-        sourceFile.text = sourceFile.text.replaceFirst("// Simple hello world app", "// Comment is changed")
-        run "mainExecutable"
-
-        then:
-        skipped libraryCompileTask
-        skipped ":linkHelloSharedLibrary"
-        skipped ":helloSharedLibrary"
-
-        executedAndNotSkipped mainCompileTask
-
-        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
-        if (toolChain.visualCpp) {
-            executedAndNotSkipped ":linkMainExecutable"
-            executedAndNotSkipped ":mainExecutable"
-        } else if(objectiveCWithAslr()){
-            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            executed ":linkMainExecutable", ":mainExecutable"
-        } else {
-            skipped ":linkMainExecutable"
-            skipped ":mainExecutable"
-        }
-    }
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "recompiles library and relinks executable with library source file change"() {
-        given:
-        run "installMainExecutable"
-        maybeWait()
-        def install = installation("build/install/mainExecutable")
-
-        when:
-        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"() {
-        given:
-        run "installMainExecutable"
-        maybeWait()
-
-        when:
-        headerFile << """
-            int unused();
-"""
-
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped libraryCompileTask
-        executedAndNotSkipped mainCompileTask
-
-        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
-        if (toolChain.visualCpp) {
-            executedAndNotSkipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            executedAndNotSkipped ":linkMainExecutable", ":mainExecutable"
-        } else if(objectiveCWithAslr()){
-            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            executed ":linkMainExecutable", ":mainExecutable"
-        } else {
-            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            skipped ":linkMainExecutable", ":mainExecutable"
-        }
-    }
-
-    def "recompiles binary when header file changes in a way that does not affect the object files"() {
-        given:
-        run "installMainExecutable"
-        maybeWait()
-
-        when:
-        headerFile << """
-// Comment added to the end of the header file
-"""
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped libraryCompileTask
-        executedAndNotSkipped mainCompileTask
-
-        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
-        if (toolChain.visualCpp) {
-            executedAndNotSkipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            executedAndNotSkipped ":linkMainExecutable", ":mainExecutable"
-        } else if(objectiveCWithAslr()){
-             executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
-             executed ":linkMainExecutable", ":mainExecutable"
-        } else {
-            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            skipped ":linkMainExecutable", ":mainExecutable"
-        }
-    }
-
-    // compiling Objective-C and Objective-Cpp with clang generates
-    // random different object files (related to ASLR settings) 
-    // We saw this behaviour only on linux so far. 
-    boolean objectiveCWithAslr() {
-        return (sourceType == "Objc" || sourceType == "Objcpp") &&
-                OperatingSystem.current().isLinux() && 
-                toolChain.displayName == "clang"
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "rebuilds binary with compiler option change"() {
-        given:
-        run "installMainExecutable"
-
-        def install = installation("build/install/mainExecutable")
-
-        when:
-        buildFile << """
-            libraries {
-                hello {
-                    binaries.all {
-                        ${helloWorldApp.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
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "rebuilds binary with target platform change"() {
-        Assume.assumeTrue(multiPlatformsAvailable)
-        given:
-        buildFile << """
-    model {
-        platforms {
-            arch {
-                // Tool chain defaults
-            }
-        }
-    }
-"""
-        run "mainExecutable"
-
-        when:
-        buildFile.text = buildFile.text.replace("// Tool chain defaults", "architecture 'i386'")
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped libraryCompileTask, mainCompileTask
-        executedAndNotSkipped ":linkHelloSharedLibrary"
-        executedAndNotSkipped ":helloSharedLibrary", ":mainExecutable"
-    }
-
-    def "relinks binary when set of input libraries changes"() {
-        given:
-        run "mainExecutable", "helloStaticLibrary"
-
-        def executable = executable("build/binaries/mainExecutable/main")
-        def snapshot = executable.snapshot()
-
-        when:
-        buildFile.text = buildFile.text.replaceFirst("lib libraries.hello", "lib libraries.hello.static")
-        run "mainExecutable"
-
-        then:
-        skipped ":helloStaticLibrary"
-        skipped mainCompileTask
-        executedAndNotSkipped ":linkMainExecutable"
-        executedAndNotSkipped ":mainExecutable"
-
-        and:
-        executable.assertHasChangedSince(snapshot)
-    }
-
-    def "relinks binary but does not recompile when linker option changed"() {
-        given:
-        run "installMainExecutable"
-
-        when:
-        def executable = executable("build/binaries/mainExecutable/main")
-        def snapshot = executable.snapshot()
-
-        and:
-        def linkerArgs =
-            toolChain.isVisualCpp() ? "'/DEBUG'" : OperatingSystem.current().isMacOsX() ? "'-Xlinker', '-no_pie'" : "'-Xlinker', '-q'";
-        linkerArgs = escapeString(linkerArgs)
-        buildFile << """
-            executables {
-                main {
-                    binaries.all {
-                        linker.args ${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 "cleans up stale object files when executable source file renamed"() {
-        given:
-        run "installMainExecutable"
-
-        def oldObjFile = objectFileFor(sourceFile)
-        def newObjFile = objectFileFor(sourceFile.getParentFile().file("changed_${sourceFile.name}"))
-        assert oldObjFile.file
-        assert !newObjFile.file
-
-        final source = sourceFile
-
-        when:
-        rename(source)
-        run "mainExecutable"
-
-        then:
-        skipped libraryCompileTask
-        skipped ":linkHelloSharedLibrary"
-        skipped ":helloSharedLibrary"
-        executedAndNotSkipped mainCompileTask
-        executedAndNotSkipped ":linkMainExecutable"
-        executedAndNotSkipped ":mainExecutable"
-
-        and:
-        !oldObjFile.file
-        newObjFile.file
-    }
-
-    def "cleans up stale object files when library source file renamed"() {
-        when:
-        run "helloStaticLibrary"
-
-        then:
-        String objectFilesPath = "build/objectFiles/helloStaticLibrary/hello${sourceType}"
-        def oldObjFile = objectFileFor(librarySourceFiles[0], objectFilesPath)
-        def newObjFile = objectFileFor( librarySourceFiles[0].getParentFile().file("changed_${librarySourceFiles[0].name}"), objectFilesPath)
-        assert oldObjFile.file
-        assert !newObjFile.file
-
-        assert staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(oldObjFile.name)
-
-        when:
-        librarySourceFiles.each { rename(it) }
-        run "helloStaticLibrary"
-
-        then:
-        executedAndNotSkipped libraryCompileTask.replace("Shared", "Static")
-        executedAndNotSkipped ":createHelloStaticLibrary"
-        executedAndNotSkipped ":helloStaticLibrary"
-
-        and:
-        !oldObjFile.file
-        newObjFile.file
-
-        and:
-        assert staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(newObjFile.name)
-        assert !staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(oldObjFile.name)
-    }
-
-    @RequiresInstalledToolChain(VisualCpp)
-    def "cleans up stale debug files when changing from debug to non-debug"() {
-
-        given:
-        buildFile << """
-            binaries.all { ${compilerTool}.args '/Zi'; linker.args '/DEBUG'; }
-        """
-        run "mainExecutable"
-
-        def executable = executable("build/binaries/mainExecutable/main")
-        executable.assertDebugFileExists()
-
-        when:
-        buildFile << """
-            binaries.all { ${compilerTool}.args.clear(); linker.args.clear(); }
-        """
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped libraryCompileTask
-        executedAndNotSkipped ":helloSharedLibrary"
-        executedAndNotSkipped mainCompileTask
-        executedAndNotSkipped ":linkMainExecutable"
-        executedAndNotSkipped ":mainExecutable"
-
-        and:
-        executable.assertDebugFileDoesNotExist()
-    }
-
-    @Ignore("Test demonstrates missing functionality in incremental build with C++")
-    def "recompiles binary when header file with relative path changes"() {
-        when:
-        buildFile << """
-            apply plugin: 'cpp'
-            executables {
-                main {}
-            }
-"""
-
-        file("src/main/cpp/main.cpp") << """
-            #include "../not_included/hello.h"
-
-            int main () {
-              sayHello();
-              return 0;
-            }
-"""
-
-        def headerFile = file("src/main/not_included/hello.h") << """
-            void sayHello();
-"""
-
-        file("src/main/cpp/hello.cpp") << """
-            #include <iostream>
-
-            void sayHello() {
-                std::cout << "HELLO" << std::endl;
-            }
-"""
-        then:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == "HELLO\n"
-
-        when:
-        headerFile.text = """
-            NOT A VALID HEADER FILE
-"""
-        then:
-        fails "mainExecutable"
-        and:
-        executedAndNotSkipped "compileMainExecutableMainCpp"
-    }
-
-    private void maybeWait() {
-        if (toolChain.visualCpp) {
-            def now = System.currentTimeMillis()
-            def nextSecond = now % 1000
-            Thread.sleep(1200 - nextSecond)
-        }
-    }
-
-    private static boolean rename(TestFile sourceFile) {
-        final newFile = new File(sourceFile.getParentFile(), "changed_${sourceFile.name}")
-        newFile << sourceFile.text
-        sourceFile.delete()
-    }
-
-
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalCompileIntegrationTest.groovy
deleted file mode 100755
index 8c8f9d2..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIncrementalCompileIntegrationTest.groovy
+++ /dev/null
@@ -1,529 +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.nativebinaries.language.cpp
-
-import groovy.io.FileType
-import org.apache.commons.io.FilenameUtils
-import org.gradle.internal.hash.HashUtil
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.GUtil
-import spock.lang.Unroll
-
-abstract class AbstractLanguageIncrementalCompileIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    IncrementalHelloWorldApp app
-    String compileTask
-    TestFile sourceFile
-    TestFile sharedHeaderFile
-    TestFile otherHeaderFile
-    List<TestFile> otherSourceFiles = []
-    TestFile objectFileDir
-
-    abstract IncrementalHelloWorldApp getHelloWorldApp();
-
-    String getSourceType() {
-        GUtil.toCamelCase(app.sourceType)
-    }
-
-    def "setup"() {
-        app = getHelloWorldApp()
-        compileTask = ":compileMainExecutableMain${sourceType}"
-
-        buildFile << app.pluginScript
-        buildFile << app.extraConfiguration
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        sourceFile = app.mainSource.writeToDir(file("src/main"))
-        sharedHeaderFile = app.libraryHeader.writeToDir(file("src/main"))
-        app.librarySources.each {
-            otherSourceFiles << it.writeToDir(file("src/main"))
-        }
-        otherHeaderFile = file("src/main/headers/other.h") << """
-            // Dummy header file
-"""
-        objectFileDir = file("build/objectFiles/mainExecutable")
-    }
-
-    def "recompiles changed source file only"() {
-        given:
-        initialCompile()
-
-        when:
-        sourceFile << """
-// Changed source file
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "recompiles all source files that include changed header file"() {
-        given:
-        initialCompile()
-
-        when:
-        sharedHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-    }
-
-    def "recompiles only source file that includes changed header file"() {
-        given:
-        sourceFile << """
-            #include "${otherHeaderFile.name}"
-"""
-        and:
-        initialCompile()
-
-        when:
-        otherHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "source is always recompiled if it includes header via macro"() {
-        given:
-        sourceFile << """
-            #define MY_HEADER "${otherHeaderFile.name}"
-            #include MY_HEADER
-"""
-
-        and:
-        initialCompile()
-
-        when:
-        otherHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-
-        // TODO:DAZ Remove this behaviour
-        when: "Header that is NOT included is changed"
-        file("src/main/headers/notIncluded.h") << """
-            // Dummy header file
-"""
-        and:
-        run "mainExecutable"
-
-        then: "Source is still recompiled"
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "recompiles source file when transitively included header file is changed"() {
-        given:
-        def transitiveHeaderFile = file("src/main/headers/transitive.h") << """
-           // Dummy header file
-"""
-        otherHeaderFile << """
-            #include "${transitiveHeaderFile.name}"
-"""
-        sourceFile << """
-            #include "${otherHeaderFile.name}"
-"""
-
-        and:
-        initialCompile()
-
-        when:
-        transitiveHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "recompiles source file when an included header file is renamed"() {
-        given:
-        initialCompile()
-
-        and:
-        final newFile = file("src/main/headers/changed.h")
-        newFile << sharedHeaderFile.text
-        sharedHeaderFile.delete()
-
-        when:
-        fails "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-        failure.assertHasDescription("Execution failed for task '${compileTask}'.");
-    }
-
-    def "does not recompile any sources when unused header file is changed"() {
-        given:
-        initialCompile()
-
-        when:
-        otherHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        skipped compileTask
-        noneRecompiled()
-    }
-
-    @Unroll
-    def "does not recompile when include path has #testCase"() {
-        given:
-        initialCompile()
-
-        file("src/additional-headers/other.h") << """
-    // extra header file that is not included in source
-"""
-        file("src/replacement-headers/${sharedHeaderFile.name}") << """
-    // replacement header file that is included in source
-"""
-
-        when:
-        buildFile << """
-            sources.main.${app.sourceType} {
-                exportedHeaders {
-                    srcDirs ${headerDirs}
-                }
-            }
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        skipped compileTask
-        noneRecompiled()
-
-        where:
-        testCase                       | headerDirs
-        "extra header dir after"       | '"src/main/headers", "src/additional-headers"'
-        "extra header dir before"      | '"src/additional-headers", "src/main/headers"'
-        "replacement header dir after" | '"src/main/headers", "src/replacement-headers"'
-    }
-
-    def "recompiles when include path is changed so that replacement header file occurs before previous header"() {
-        given:
-        initialCompile()
-
-        file("src/replacement-headers/${sharedHeaderFile.name}") << sharedHeaderFile.text
-
-        when:
-        buildFile << """
-            sources.main.${app.sourceType}  {
-                exportedHeaders {
-                    srcDirs "src/replacement-headers", "src/main/headers"
-                }
-            }
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-    }
-
-    def "recompiles when replacement header file is added before previous header to existing include path"() {
-        given:
-        buildFile << """
-            sources.main.${app.sourceType} {
-                exportedHeaders {
-                    srcDirs "src/replacement-headers", "src/main/headers"
-                }
-            }
-"""
-        initialCompile()
-
-        when:
-        file("src/replacement-headers/${sharedHeaderFile.name}") << sharedHeaderFile.text
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-    }
-
-    def "recompiles when replacement header file is added to source directory"() {
-        given:
-        initialCompile()
-
-        when:
-        sourceFile.parentFile.file(sharedHeaderFile.name) << sharedHeaderFile.text
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-    }
-
-    def "recompiles all source files and removes stale outputs when compiler arg changes"() {
-        given:
-        def extraSource = file("src/main/${app.sourceType}/extra.${app.sourceExtension}")
-        extraSource << sourceFile.text.replaceAll("main", "main2")
-
-        initialCompile()
-
-        outputFile(extraSource).assertExists()
-
-        when:
-        sourceFile << """
-            // Changed source file
-"""
-        buildFile << """
-            executables {
-                main {
-                    binaries.all {
-                        ${helloWorldApp.compilerDefine("MY_DEF")}
-                    }
-                }
-            }
-"""
-        extraSource.delete()
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-        outputFile(extraSource).assertDoesNotExist()
-    }
-
-    def "recompiles all source files when generated object files are removed"() {
-        given:
-        initialCompile()
-
-        when:
-        objectFileDir.deleteDir()
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled allSources
-    }
-
-    def "removes output file when source file is renamed"() {
-        given:
-        initialCompile()
-
-        when:
-        final newFile = file("src/main/${app.sourceType}/changed.${app.sourceExtension}")
-        newFile << sourceFile.text
-        sourceFile.delete()
-
-        and:
-        run "mainExecutable"
-
-        then:
-        recompiled newFile
-        outputFile(sourceFile).assertDoesNotExist()
-    }
-
-    def "removes output file when source file is removed"() {
-        given:
-        def extraSource = file("src/main/${app.sourceType}/extra.${app.sourceExtension}")
-        extraSource << sourceFile.text.replaceAll("main", "main2")
-
-        initialCompile()
-
-        and:
-        outputFile(extraSource).assertExists()
-
-        when:
-        extraSource.delete()
-
-        and:
-        run "mainExecutable"
-
-        then:
-        outputFile(extraSource).assertDoesNotExist()
-        noneRecompiled()
-    }
-
-    def "removes output files when all source files are removed"() {
-        given:
-        initialCompile()
-
-        def executable = executable("build/binaries/mainExecutable/main")
-        executable.assertExists()
-
-        when:
-        file("src/main").eachFileRecurse(FileType.FILES) {
-            println "deleting ${it}"
-            it.delete()
-        }
-
-        and:
-        run "mainExecutable"
-
-        then: "linker output file is removed"
-        executable.assertDoesNotExist()
-
-        // Stale object files are removed when a new file is added to the source set
-        when:
-        def newSource = file("src/main/${app.sourceType}/newfile.${app.sourceExtension}") << """
-            #include <stdio.h>
-
-            int main () {
-                printf("hello");
-                return 0;
-            }
-"""
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executable.exec().out == "hello"
-        outputFile(newSource).assertExists()
-
-        and: "Previous object files are removed"
-        outputFile(sourceFile).assertDoesNotExist()
-        otherSourceFiles.each {
-            outputFile(it).assertDoesNotExist()
-        }
-    }
-
-    def "incremental compile is not effected by other compile tasks"() {
-        given:
-        buildFile << """
-            executables {
-                other
-            }
-"""
-        app.writeSources(file("src/other"))
-
-        and:
-        initialCompile()
-
-        and:
-        // Completely independent compile task (state should be independent)
-        run "otherExecutable"
-
-        when:
-        sourceFile << """
-// Changed source file
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-
-    def initialCompile() {
-        run "mainExecutable"
-
-        // Set the last modified timestamp to zero for all object files
-        objectFileDir.eachFileRecurse(FileType.FILES) { it.lastModified = 0 }
-    }
-
-    def getRecompiledFiles() {
-        // Get all of the object files that do not have a zero last modified timestamp
-        def recompiled = []
-        objectFileDir.eachFileRecurse(FileType.FILES) {
-            if (it.lastModified() > 0) {
-                recompiled << FilenameUtils.removeExtension(it.name)
-            }
-        }
-        return recompiled as Set
-    }
-
-    def getAllSources() {
-        return [sourceFile] + otherSourceFiles
-    }
-
-    def noneRecompiled() {
-        recompiled([])
-    }
-
-    def recompiled(TestFile file) {
-        recompiled([file])
-    }
-
-    def recompiled(List<TestFile> files) {
-        def expectedNames = files.collect({ FilenameUtils.removeExtension(it.name) }) as Set
-        assert getRecompiledFiles() == expectedNames
-        return true
-    }
-
-    def outputFile(TestFile sourceFile) {
-        final baseName = FilenameUtils.removeExtension(sourceFile.name)
-        String compactMD5 = HashUtil.createCompactMD5(sourceFile.getAbsolutePath());
-        return objectFile("build/objectFiles/mainExecutable/main${sourceType}/$compactMD5/${baseName}")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIntegrationTest.groovy
deleted file mode 100755
index 94cb126..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AbstractLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,198 +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.nativebinaries.language.cpp
-
-import org.apache.commons.lang.RandomStringUtils
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Ignore
-
-abstract class AbstractLanguageIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    abstract HelloWorldApp getHelloWorldApp()
-
-    def "setup"() {
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-    }
-
-    def "compile and link executable"() {
-        given:
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        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 << """
-            executables {
-                main {
-                    binaries.all {
-                        ${helloWorldApp.compilerArgs("-DFRENCH")}
-                    }
-                }
-            }
-        """
-
-        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 << """
-            executables {
-                main {
-                    binaries.all {
-                        ${helloWorldApp.compilerDefine("FRENCH")}
-                    }
-                }
-            }
-        """
-
-        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 << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.${helloWorldApp.sourceType}.lib libraries.hello
-        """
-
-        and:
-        helloWorldApp.executable.writeSources(file("src/main"))
-        helloWorldApp.library.writeSources(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 << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {
-                    binaries.withType(StaticLibraryBinary) {
-                        ${helloWorldApp.compilerDefine("FRENCH")}
-                    }
-                }
-            }
-            sources.main.${helloWorldApp.sourceType}.lib libraries.hello.static
-        """
-
-        and:
-        helloWorldApp.executable.writeSources(file("src/main"))
-        helloWorldApp.library.writeSources(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
-    }
-
-    @Ignore
-    def "can run project in extended nested file paths"() {
-        // windows can't handle a path up to 260 characters
-        // we create a path that ends up with build folder longer than is 260
-        def projectPathOffset = 180 - testDirectory.getAbsolutePath().length()
-        def nestedProjectPath = RandomStringUtils.randomAlphanumeric(projectPathOffset-10) + "/123456789"
-
-        setup:
-        def deepNestedProjectFolder = file(nestedProjectPath)
-        executer.usingProjectDirectory(deepNestedProjectFolder)
-        def TestFile buildFile = deepNestedProjectFolder.file("build.gradle")
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("$nestedProjectPath/src/main"));
-
-        expect:
-        succeeds "mainExecutable"
-        def mainExecutable = executable("$nestedProjectPath/build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.englishOutput
-    }
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100755
index cf0c21e..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,136 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-class AssemblyLanguageIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    HelloWorldApp app = new MixedLanguageHelloWorldApp(toolChain)
-    TestFile asmSourceFile
-    def install
-
-    def "setup"() {
-        buildFile << """
-            apply plugin: 'assembler'
-            apply plugin: 'c'
-            apply plugin: 'cpp'
-
-            $app.extraConfiguration
-
-            libraries {
-                hello {}
-            }
-            executables {
-                main {
-                    binaries.all {
-                        lib libraries.hello
-                    }
-                }
-            }
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        asmSourceFile = file("src/hello/asm/sum.s")
-
-        run "installMainExecutable"
-
-        install = installation("build/install/mainExecutable")
-    }
-
-    def "does not re-execute build with no change"() {
-        when:
-        run "mainExecutable"
-
-        then:
-        nonSkippedTasks.empty
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "reassembles binary with assembler option change"() {
-        when:
-        buildFile << """
-            libraries {
-                hello {
-                    binaries.all {
-                        if (toolChain in VisualCpp) {
-                            assembler.args '/Zf'
-                        } else {
-                            assembler.args '-W'
-                        }
-                    }
-                }
-            }
-"""
-
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
-
-        and:
-        install.exec().out == app.englishOutput
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "reassembles binary with target platform change"() {
-        when:
-        buildFile.text = buildFile.text.replace("i386", "x86")
-
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
-
-        and:
-        install.exec().out == app.englishOutput
-    }
-
-    def "cleans up stale object files when source file renamed"() {
-        def oldObjFile = objectFile("build/objectFiles/helloSharedLibrary/helloAsm/${hashFor(asmSourceFile)}/sum")
-        def newObjFile = objectFile("build/objectFiles/helloSharedLibrary/helloAsm/${hashFor(file('src/hello/asm/changed_sum.s'))}/changed_sum")
-        assert oldObjFile.file
-        assert !newObjFile.file
-
-        when:
-        asmSourceFile.renameTo(file("src/hello/asm/changed_sum.s"))
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
-
-        and:
-        !oldObjFile.file
-        newObjFile.file
-    }
-
-    def "reassembles binary with source comment change"() {
-        when:
-        asmSourceFile << "# A comment at the end of the file\n"
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
-    }
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIntegrationTest.groovy
deleted file mode 100755
index 74b0adb..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/AssemblyLanguageIntegrationTest.groovy
+++ /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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.SourceFile
-
-class AssemblyLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-
-    HelloWorldApp helloWorldApp = new AssemblerWithCHelloWorldApp(toolChain)
-
-    def "build fails when assemble fails"() {
-        given:
-        buildFile << """
-            executables {
-                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.")
-    }
-
-    def "can manually define Assembler source sets"() {
-        given:
-        helloWorldApp.mainSource.writeToDir(file("src/main"))
-        helloWorldApp.getLibraryHeader().writeToDir(file("src/main"))
-        helloWorldApp.librarySources[0].writeToDir(file("src/main"))
-        file("src/main/sum-sources/sum.s") << helloWorldApp.librarySources[1].content
-
-        and:
-        buildFile << """
-            sources {
-                main {
-                    sumAsm(AssemblerSourceSet) {
-                        source {
-                            srcDir "src/main/sum-sources"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-"""
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.englishOutput
-    }
-
-
-    static class AssemblerWithCHelloWorldApp extends MixedLanguageHelloWorldApp {
-        AssemblerWithCHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
-            super(toolChain)
-        }
-
-        @Override
-        List<String> getPluginList() {
-            return ['c', 'assembler']
-        }
-
-        @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/nativebinaries/language/cpp/BinaryBuildTypesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryBuildTypesIntegrationTest.groovy
deleted file mode 100755
index 1a9b153..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryBuildTypesIntegrationTest.groovy
+++ /dev/null
@@ -1,206 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-class BinaryBuildTypesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def helloWorldApp = new CppHelloWorldApp()
-
-    def "creates debug and release variants"() {
-        when:
-        helloWorldApp.writeSources(file("src/main"))
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                buildTypes {
-                    debug {
-                        ext.debug = true
-                    }
-                    integration {
-                        ext.debug = true
-                    }
-                    release {
-                        ext.debug = false
-                    }
-                }
-            }
-            binaries.all { binary ->
-                if (toolChain in Gcc && buildType.debug) {
-                    cppCompiler.args "-g"
-                }
-                if (toolChain in VisualCpp) {
-                    // Apply to all debug build types: 'debug' and 'integration'
-                    if (buildType.debug) {
-                        cppCompiler.args '/Zi'
-                        cppCompiler.define 'DEBUG'
-                        linker.args '/DEBUG'
-                    }
-                }
-                // Apply to 'integration' type binaries only
-                if (buildType == buildTypes['integration']) {
-                    cppCompiler.define "FRENCH"
-                }
-            }
-            executables {
-                main {}
-            }
-        """
-        and:
-        succeeds "debugMainExecutable", "integrationMainExecutable", "releaseMainExecutable"
-
-        then:
-        with(executable("build/binaries/mainExecutable/debug/main")) {
-            it.assertExists()
-            it.assertDebugFileExists()
-            it.exec().out == helloWorldApp.englishOutput
-        }
-        with (executable("build/binaries/mainExecutable/integration/main")) {
-            it.assertExists()
-            it.assertDebugFileExists()
-            it.exec().out == helloWorldApp.frenchOutput
-        }
-        with (executable("build/binaries/mainExecutable/release/main")) {
-            it.assertExists()
-            it.assertDebugFileDoesNotExist()
-            it.exec().out == helloWorldApp.englishOutput
-        }
-    }
-
-    def "configure component for a single build type"() {
-        when:
-        helloWorldApp.writeSources(file("src/main"))
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                buildTypes {
-                    debug
-                    release
-                }
-            }
-            executables {
-                main {
-                    targetBuildTypes "release"
-                }
-            }
-            binaries.all { binary ->
-                if (buildType == buildTypes.release) {
-                    cppCompiler.define "FRENCH"
-                }
-            }
-"""
-
-        and:
-        succeeds "mainExecutable"
-
-        then:
-        // Build type dimension is flattened since there is only one possible value
-        executedAndNotSkipped(":mainExecutable")
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
-    }
-
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "executable with build type depends on library with matching build type"() {
-        when:
-        helloWorldApp.executable.writeSources(file("src/main"))
-        helloWorldApp.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                buildTypes {
-                    debug
-                    release
-                }
-            }
-            binaries.all {
-                if (buildType == buildTypes.debug) {
-                    cppCompiler.define "FRENCH" // Equate 'debug' to 'french' for this test
-                }
-            }
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.cpp.lib libraries.hello.static
-        """
-        and:
-        succeeds "installDebugMainExecutable", "installReleaseMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/debug").exec().out == helloWorldApp.frenchOutput
-        installation("build/install/mainExecutable/release").exec().out == helloWorldApp.englishOutput
-    }
-
-    def "fails with reasonable error message when trying to target an unknown build type"() {
-        when:
-        settingsFile << "rootProject.name = 'bad-build-type'"
-        buildFile << """
-            model {
-                buildTypes {
-                    debug
-                }
-            }
-            executables {
-                main {
-                    targetBuildTypes "unknown"
-                }
-            }
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("A problem occurred configuring root project 'bad-build-type'.")
-        failure.assertHasCause("Invalid BuildType: 'unknown'")
-    }
-
-    def "fails with reasonable error message when depended on library has no variant with matching build type"() {
-        when:
-        settingsFile << "rootProject.name = 'no-matching-build-type'"
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                buildTypes {
-                    debug
-                    release
-                }
-            }
-            executables {
-                main {}
-            }
-            libraries {
-                hello {
-                    targetBuildTypes "debug"
-                }
-            }
-            sources.main.cpp.lib libraries.hello.static
-"""
-
-        and:
-        fails "releaseMainExecutable"
-
-        then:
-        failure.assertHasDescription("No static library binary available for library 'hello' with [flavor: 'default', platform: 'current', buildType: 'release']")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryFlavorsIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryFlavorsIntegrationTest.groovy
deleted file mode 100755
index f5d3bc5..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryFlavorsIntegrationTest.groovy
+++ /dev/null
@@ -1,227 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Ignore
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class BinaryFlavorsIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    static final DEFAULT = HelloWorldApp.HELLO_WORLD
-    static final FRENCH = HelloWorldApp.HELLO_WORLD_FRENCH
-
-    def helloWorldApp = new ExeWithLibraryUsingLibraryHelloWorldApp()
-
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-
-        buildFile << """
-            apply plugin: "cpp"
-            model {
-                flavors {
-                    english
-                    french
-                    german
-                }
-            }
-            libraries {
-                greetings {
-                    binaries.all {
-                        if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) {
-                            cppCompiler.args("-fPIC");
-                        }
-                    }
-                }
-                hello {
-                    binaries.all {
-                        lib libraries.greetings.static
-                    }
-                }
-            }
-            executables {
-                main {
-                    binaries.all {
-                        lib libraries.hello
-                    }
-                }
-            }
-        """
-
-        helloWorldApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-    }
-
-    def "can configure components for a single flavor"() {
-        given:
-        buildFile << """
-    binaries.all {
-        if (flavor == flavors.french) {
-            cppCompiler.define "FRENCH"
-        }
-    }
-    executables.main.targetFlavors "french"
-    libraries.hello.targetFlavors "french"
-    libraries.greetings.targetFlavors "french"
-"""
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == FRENCH + " " + FRENCH
-    }
-
-    def "builds executable for each defined flavor when not configured for component"() {
-        when:
-        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable", "installGermanMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/english").assertInstalled()
-        installation("build/install/mainExecutable/french").assertInstalled()
-        installation("build/install/mainExecutable/german").assertInstalled()
-    }
-
-    def "executable with flavors depends on library with matching flavors"() {
-        when:
-        buildFile << """
-            executables {
-                main {
-                    targetFlavors "english", "french"
-                    binaries.all {
-                        if (flavor == flavors.french) {
-                            cppCompiler.define "FRENCH"
-                        }
-                    }
-                }
-            }
-            libraries.all {
-                targetFlavors "english", "french"
-                binaries.all {
-                    if (flavor == flavors.french) {
-                        cppCompiler.define "FRENCH"
-                    }
-                }
-            }
-        """
-
-        and:
-        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/english").exec().out == DEFAULT + " " + DEFAULT
-        installation("build/install/mainExecutable/french").exec().out == FRENCH + " " + FRENCH
-    }
-
-    // TODO:DAZ Un-ignore
-    @Ignore("Requires proper dependency resolution")
-    def "executable with flavors depends on library with no defined flavor"() {
-        when:
-        buildFile << """
-            executables {
-                main {
-                    targetFlavors "english", "french"
-                    binaries.all {
-                        if (flavor == flavors.french) {
-                            cppCompiler.define "FRENCH"
-                        }
-                    }
-                }
-            }
-        """
-
-        and:
-        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/english").exec().out == DEFAULT + " " + DEFAULT
-        installation("build/install/mainExecutable/french").exec().out == FRENCH + " " + DEFAULT
-    }
-
-    // TODO:DAZ Un-ignore
-    @Ignore("Library resolution does not yet handle this case")
-    def "executable with flavors depends on a library with a single flavor which depends on a library with flavors"() {
-        when:
-        buildFile << """
-            executables {
-                main {
-                    targetFlavors "english", "french"
-                    binaries.all {
-                        if (flavor == flavors.french) {
-                            cppCompiler.define "FRENCH"
-                        }
-                    }
-                }
-            }
-            libraries {
-                greetings {
-                    targetFlavors "english", "french"
-                    binaries.all {
-                        if (flavor == flavors.french) {
-                            cppCompiler.define "FRENCH"
-                        }
-                    }
-                }
-            }
-        """
-
-        and:
-        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/english").exec().out == DEFAULT + " " + DEFAULT
-        installation("build/install/mainExecutable/french").exec().out == FRENCH + " " + FRENCH
-    }
-
-    def "build fails when library has no matching flavour"() {
-        when:
-        buildFile << """
-            apply plugin: "cpp"
-            libraries {
-                hello {
-                    targetFlavors "english", "french"
-                }
-            }
-            executables {
-                main {
-                    targetFlavors "english", "german"
-                    binaries.all {
-                        lib libraries.hello
-                    }
-                }
-            }
-        """
-
-        then:
-        fails "germanMainExecutable"
-        failure.assertHasDescription("No shared library binary available for library 'hello' with [flavor: 'german', platform: 'current', buildType: 'debug']")
-    }
-
-    def "fails with reasonable error message when trying to target an unknown flavor"() {
-        when:
-        buildFile << """
-            executables.main.targetFlavors "unknown"
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("A problem occurred configuring root project 'test'.")
-        failure.assertHasCause("Invalid Flavor: 'unknown'")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryPlatformIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryPlatformIntegrationTest.groovy
deleted file mode 100755
index f3c8189..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/BinaryPlatformIntegrationTest.groovy
+++ /dev/null
@@ -1,349 +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.nativebinaries.language.cpp
-
-import net.rubygrapefruit.platform.Native
-import net.rubygrapefruit.platform.SystemInfo
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement
-import org.gradle.nativebinaries.language.cpp.fixtures.app.PlatformDetectingTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.DumpbinBinaryInfo
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.OtoolBinaryInfo
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.ReadelfBinaryInfo
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.gradle.util.TextUtil
-import spock.lang.Unroll
-
- at Requires(TestPrecondition.NOT_UNKNOWN_OS)
-class BinaryPlatformIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def testApp = new PlatformDetectingTestApp()
-    def os = OperatingSystem.current()
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'cpp'
-
-            executables {
-                main {}
-            }
-        """
-
-        testApp.writeSources(file("src/main"))
-    }
-
-    def "build binary for a default target platform"() {
-        given:
-        def arch =  [name: "x86_64", altName: "amd64"]
-        // Tool chains on Windows currently build for i386 by default, even on amd64
-        if (OperatingSystem.current().windows || Native.get(SystemInfo).architecture == SystemInfo.Architecture.i386) {
-            arch = [name: "x86", altName: "i386"]
-        }
-
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executedAndNotSkipped(":mainExecutable")
-        executable("build/binaries/mainExecutable/main").binaryInfo.arch.name == arch.name
-        executable("build/binaries/mainExecutable/main").exec().out == "${arch.altName} ${os.familyName}" * 2
-        binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objectFiles/mainExecutable/mainCpp")).arch.name == arch.name
-    }
-
-    def "configure component for a single target platform"() {
-        when:
-        buildFile << """
-            model {
-                platforms {
-                    x86 {
-                        architecture "x86"
-                    }
-                    x86_64 {
-                        architecture "x86_64"
-                    }
-                }
-            }
-            task buildExecutables {
-                dependsOn binaries.withType(ExecutableBinary).matching {
-                    it.buildable
-                }
-            }
-            executables.main.targetPlatforms "x86"
-"""
-
-        and:
-        succeeds "buildExecutables"
-
-        then:
-        // Platform dimension is flattened since there is only one possible value
-        executedAndNotSkipped(":mainExecutable")
-        executable("build/binaries/mainExecutable/main").binaryInfo.arch.name == "x86"
-        executable("build/binaries/mainExecutable/main").exec().out == "i386 ${os.familyName}" * 2
-    }
-
-    def "library with matching platform is chosen by dependency resolution"() {
-        given:
-        testApp.executable.writeSources(file("src/exe"))
-        testApp.library.writeSources(file("src/hello"))
-        when:
-        buildFile << """
-            model {
-                platforms {
-                    x86 {
-                        architecture "x86"
-                    }
-                    x86_64 {
-                        architecture "x86_64"
-                    }
-                }
-            }
-            executables {
-                exe {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.exe.cpp.lib libraries.hello.static
-            executables.exe.targetPlatforms "x86"
-"""
-
-        and:
-        succeeds "exeExecutable"
-
-        then:
-        // Platform dimension is flattened since there is only one possible value
-        executedAndNotSkipped(":exeExecutable")
-        executable("build/binaries/exeExecutable/exe").binaryInfo.arch.name == "x86"
-        executable("build/binaries/exeExecutable/exe").exec().out == "i386 ${os.familyName}" * 2
-    }
-
-    def "build binary for multiple target architectures"() {
-        when:
-        buildFile << """
-            model {
-                platforms {
-                    x86 {
-                        architecture "x86"
-                    }
-                    x86_64 {
-                        architecture "x86_64"
-                    }
-                    itanium {
-                        architecture "ia-64"
-                    }
-                    arm {
-                        architecture "arm"
-                    }
-                }
-            }
-            task buildExecutables {
-                dependsOn binaries.withType(ExecutableBinary).matching {
-                    it.buildable
-                }
-            }
-"""
-
-        and:
-        succeeds "buildExecutables"
-
-        then:
-        executable("build/binaries/mainExecutable/x86/main").binaryInfo.arch.name == "x86"
-        executable("build/binaries/mainExecutable/x86/main").exec().out == "i386 ${os.familyName}" * 2
-        binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objectFiles/mainExecutable/x86/mainCpp")).arch.name == "x86"
-
-        // x86_64 binaries not supported on MinGW or cygwin
-        if (toolChain.id == "mingw" || toolChain.id == "gcccygwin") {
-            executable("build/binaries/mainExecutable/x86_64/main").assertDoesNotExist()
-        } else {
-            executable("build/binaries/mainExecutable/x86_64/main").binaryInfo.arch.name == "x86_64"
-            executable("build/binaries/mainExecutable/x86_64/main").exec().out == "amd64 ${os.familyName}" * 2
-            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objectFiles/mainExecutable/x86_64/mainCpp")).arch.name == "x86_64"
-        }
-
-        // Itanium only supported on visualCpp
-        if (toolChain.visualCpp) {
-            executable("build/binaries/mainExecutable/itanium/main").binaryInfo.arch.name == "ia-64"
-            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"),"build/objectFiles/mainExecutable/itanium/mainCpp")).arch.name == "ia-64"
-        } else {
-            executable("build/binaries/mainExecutable/itanium/main").assertDoesNotExist()
-        }
-
-        // ARM only supported on visualCpp 2013
-        if (toolChain.meets(ToolChainRequirement.VisualCpp2013)) {
-            executable("build/binaries/mainExecutable/arm/main").binaryInfo.arch.name == "arm"
-            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objectFiles/mainExecutable/arm/mainCpp")).arch.name == "arm"
-        } else {
-            executable("build/binaries/mainExecutable/arm/main").assertDoesNotExist()
-        }
-    }
-
-    def "can configure binary for multiple target operating systems"() {
-        when:
-        buildFile << """
-            model {
-                platforms {
-                    windows {
-                        operatingSystem "windows"
-                    }
-                    linux {
-                        operatingSystem "linux"
-                    }
-                    osx {
-                        operatingSystem "osx"
-                    }
-                }
-            }
-
-            binaries.matching({ it.targetPlatform.operatingSystem.windows }).all {
-                cppCompiler.define "FRENCH"
-            }
-            task buildExecutables {
-                dependsOn binaries.withType(ExecutableBinary).matching {
-                    it.buildable
-                }
-            }
-        """
-
-        and:
-        succeeds "buildExecutables"
-
-        then:
-        if (os.windows) {
-            executable("build/binaries/mainExecutable/windows/main").exec().out == "amd64 windows" * 2
-        } else if (os.linux) {
-            executable("build/binaries/mainExecutable/linux/main").exec().out == "amd64 linux" * 2
-        } else if (os.macOsX) {
-            executable("build/binaries/mainExecutable/osx/main").exec().out == "amd64 os x" * 2
-        } else {
-            throw new AssertionError("Unexpected operating system")
-        }
-    }
-
-    @Unroll
-    def "fails with reasonable error message when trying to build for an unavailable #type"() {
-        when:
-        buildFile << """
-            model {
-                platforms {
-                    unavailable {
-                        ${config}
-                    }
-                }
-            }
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainCpp'.")
-        failure.assertHasCause(TextUtil.toPlatformLineSeparators("""No tool chain is available to build for platform 'unavailable':
-  - ${toolChain.instanceDisplayName}: Don't know how to build for platform 'unavailable'."""))
-
-        where:
-        type               | config
-        "architecture"     | "architecture 'sparc'"
-        "operating system" | "operatingSystem 'solaris'"
-    }
-
-    @Unroll
-    def "fails with reasonable error message when trying to build for an unknown #type"() {
-        when:
-        settingsFile << """rootProject.name = 'bad'"""
-        buildFile << """
-            model {
-                platforms {
-                    bad {
-                        ${badConfig}
-                    }
-                }
-            }
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("A problem occurred configuring root project 'bad'.")
-        failure.assertHasCause("Cannot convert the provided notation to an object of type ${type}: bad.")
-
-        where:
-        type               | badConfig
-        "Architecture"     | "architecture 'bad'"
-        "OperatingSystem" | "operatingSystem 'bad'"
-    }
-
-    def "fails with reasonable error message when trying to target an unknown platform"() {
-        when:
-        settingsFile << "rootProject.name = 'bad-platform'"
-        buildFile << """
-            model {
-                platforms {
-                    main
-                }
-            }
-            executables.main.targetPlatforms "unknown"
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("A problem occurred configuring root project 'bad-platform'.")
-        failure.assertHasCause("Invalid Platform: 'unknown'")
-    }
-
-    def "fails with reasonable error message when depended on library has no variant with matching platform"() {
-        when:
-        settingsFile << "rootProject.name = 'no-matching-platform'"
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                platforms {
-                    one
-                    two
-                }
-            }
-            libraries {
-                hello {
-                    targetPlatforms "two"
-                }
-            }
-            sources.main.cpp.lib libraries.hello
-"""
-
-        and:
-        fails "oneMainExecutable"
-
-        then:
-        failure.assertHasDescription("No shared library binary available for library 'hello' with [flavor: 'default', platform: 'one', buildType: 'debug']")
-    }
-
-    def binaryInfo(TestFile file) {
-        file.assertIsFile()
-        if (os.macOsX) {
-            return new OtoolBinaryInfo(file)
-        }
-        if (os.windows) {
-            return new DumpbinBinaryInfo(file, toolChain)
-        }
-        return new ReadelfBinaryInfo(file)
-    }
-
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CCallingMixedCAndCppLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CCallingMixedCAndCppLanguageIntegrationTest.groovy
deleted file mode 100755
index 07f8a1a..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CCallingMixedCAndCppLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,25 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CCallingMixedCAndCppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-
-class CCallingMixedCAndCppLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-    HelloWorldApp helloWorldApp = new CCallingMixedCAndCppHelloWorldApp()
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100755
index d4d7677..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,27 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-
-class CLanguageIncrementalBuildIntegrationTest extends AbstractLanguageIncrementalBuildIntegrationTest {
-    @Override
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        new CHelloWorldApp()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalCompileIntegrationTest.groovy
deleted file mode 100644
index db9c190..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIncrementalCompileIntegrationTest.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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-
-class CLanguageIncrementalCompileIntegrationTest extends AbstractLanguageIncrementalCompileIntegrationTest {
-     @Override
-     IncrementalHelloWorldApp getHelloWorldApp() {
-         return new CHelloWorldApp()
-     }
- }
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIntegrationTest.groovy
deleted file mode 100755
index d8e5149..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,201 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CCompilerDetectingTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import spock.lang.Issue
-import spock.lang.Unroll
-
-// TODO:DAZ Some of these tests should apply to all single-language integration tests
-class CLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-
-    HelloWorldApp helloWorldApp = new CHelloWorldApp()
-
-    def "sources are compiled with C compiler"() {
-        def app = new CCompilerDetectingTestApp()
-
-        given:
-        app.writeSources(file('src/main'))
-
-        and:
-        buildFile << """
-             executables {
-                 main {}
-             }
-         """
-
-        expect:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == app.expectedOutput(toolChain)
-    }
-
-    def "can manually define C source sets"() {
-        given:
-        helloWorldApp.getLibraryHeader().writeToDir(file("src/shared"))
-
-        file("src/main/c/main.c") << helloWorldApp.mainSource.content
-        file("src/main/c2/hello.c") << helloWorldApp.librarySources[0].content
-        file("src/main/sum-sources/sum.c") << helloWorldApp.librarySources[1].content
-
-
-        and:
-        buildFile << """
-            sources {
-                main {
-                    c {
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                    c2(CSourceSet) {
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                    c3(CSourceSet) {
-                        source {
-                            srcDir "src/main/sum-sources"
-                        }
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-"""
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.englishOutput
-    }
-
-    def "uses headers co-located with sources"() {
-        given:
-        // Write headers so they sit with sources
-        helloWorldApp.allFiles.each {
-            it.writeToFile(file("src/main/c/${it.name}"))
-        }
-        buildFile << """
-    executables {
-        main {}
-    }
-    sources.main.c.source.include "**/*.c"
-"""
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.englishOutput
-    }
-
-    @Issue("GRADLE-2943")
-    @Unroll
-    def "can define macro #output"() {
-        given:
-        buildFile << """
-            executables {
-                main {
-                    binaries.all {
-                        ${helloWorldApp.compilerDefine('CUSTOM', inString)}
-                    }
-                }
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.getCustomOutput(output)
-
-        where:
-        inString                           | output
-        '"quoted"'                         | 'quoted'
-        '"with space"'                     | 'with space'
-        '"with\\\\"quote\\\\"internal"'    | 'with"quote"internal'
-        '"with \\\\"quote\\\\" and space"' | 'with "quote" and space'
-    }
-
-    def "compiler and linker args can contain quotes and spaces"() {
-        given:
-        buildFile << '''
-            executables {
-                main {
-                    binaries.all {
-                        // These are just some dummy arguments to test we don't blow up. Their effects are not verified.
-                        if (toolChain in VisualCpp) {
-                            cCompiler.args '/DVERSION="The version is \\'1.0\\'"'
-                            linker.args '/MANIFESTUAC:level=\\'asInvoker\\' uiAccess=\\'false\\''
-                        } else if (toolChain in Clang) {
-                            cCompiler.args '-frandom-seed="here is the \\'random\\' seed"'
-                            // TODO:DAZ Find something that works here (for all our CI machines)
-                            // linker.args '-Wl,-client_name,"a \\'client\\' name"'
-                        } else {
-                            cCompiler.args '-frandom-seed="here is the \\'random\\' seed"'
-                            // TODO:DAZ Find something that works on linux
-                            // linker.args '-Wl,--auxiliary,"an \\'auxiliary\\' name"'
-                        }
-                    }
-                }
-            }
-        '''
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        expect:
-        succeeds "mainExecutable"
-    }
-
-    def "build fails when compilation fails"() {
-        given:
-        buildFile << """
-             executables {
-                 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/nativebinaries/language/cpp/CUnitIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest.groovy
deleted file mode 100755
index dfd0eec..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest.groovy
+++ /dev/null
@@ -1,387 +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.nativebinaries.language.cpp
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.test.cunit.CUnitTestResults
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-
-import static org.gradle.util.TextUtil.normaliseLineSeparators
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class CUnitIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-    def app = new CHelloWorldApp()
-
-    def setup() {
-        buildFile << """
-            apply plugin: "c"
-            apply plugin: "cunit"
-
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        cunit {
-                            headers.srcDir "libs/cunit/2.1-2/include"
-                            binaries.withType(StaticLibraryBinary) {
-                                staticLibraryFile = file("libs/cunit/2.1-2/lib/${cunitPlatform}/${cunitLibName}")
-                            }
-                        }
-                    }
-                }
-            }
-        """
-        settingsFile << "rootProject.name = 'test'"
-    }
-
-    private void useStandardConfig() {
-        buildFile << """
-            libraries {
-                hello {}
-            }
-            binaries.withType(TestSuiteExecutableBinary) {
-                lib library: "cunit", linkage: "static"
-            }
-"""
-    }
-
-    private def getCunitPlatform() {
-        if (OperatingSystem.current().isMacOsX()) {
-            return "osx"
-        }
-        if (OperatingSystem.current().isLinux()) {
-            return "linux"
-        }
-        if (toolChain.displayName == "mingw") {
-            return "mingw"
-        }
-        if (toolChain.displayName == "gcc cygwin") {
-            return "cygwin"
-        }
-        if (toolChain.visualCpp) {
-            def vcVersion = (toolChain as AvailableToolChains.InstalledVisualCpp).version
-            switch (vcVersion.major) {
-                case "12":
-                    return "vs2013"
-                case "10":
-                    return "vs2010"
-            }
-        }
-        throw new IllegalStateException("No cunit binary available for ${toolChain.displayName}")
-    }
-
-    private def getCunitLibName() {
-        return OperatingSystem.current().getStaticLibraryName("cunit")
-    }
-
-    def "can build and run cunit test suite"() {
-        given:
-        useConventionalSourceLocations()
-        useStandardConfig()
-
-        when:
-        run "runHelloTestCUnitExe"
-
-        then:
-        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestCunit",
-                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-
-        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
-        testResults.suiteNames == ['hello test']
-        testResults.suites['hello test'].passingTests == ['test_sum']
-        testResults.suites['hello test'].failingTests == []
-        testResults.checkTestCases(1, 1, 0)
-        testResults.checkAssertions(3, 3, 0)
-    }
-
-    def "can configure via testSuite component"() {
-        given:
-        useConventionalSourceLocations()
-
-        buildFile << """
-            libraries {
-                hello {}
-            }
-            testSuites {
-                helloTest {
-                    binaries.all {
-                        lib library: "cunit", linkage: "static"
-                    }
-                }
-            }
-"""
-
-        when:
-        run "runHelloTestCUnitExe"
-
-        then:
-        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestCunit",
-                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-
-        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
-        testResults.suiteNames == ['hello test']
-        testResults.suites['hello test'].passingTests == ['test_sum']
-        testResults.suites['hello test'].failingTests == []
-        testResults.checkTestCases(1, 1, 0)
-        testResults.checkAssertions(3, 3, 0)
-    }
-
-    def "can supply cCompiler macro to cunit sources"() {
-        given:
-        useConventionalSourceLocations()
-        useStandardConfig()
-
-        when:
-        buildFile << """
-            binaries.withType(TestSuiteExecutableBinary) {
-                cCompiler.define "ONE_TEST"
-            }
-"""
-        and:
-        run "runHelloTestCUnitExe"
-
-        then:
-        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
-        testResults.checkAssertions(1, 1, 0)
-    }
-
-    def "can configure location of cunit test sources"() {
-        given:
-        useStandardConfig()
-        app.library.writeSources(file("src/hello"))
-        app.cunitTests.writeSources(file("src/alternateHelloTest"))
-
-        when:
-        buildFile << """
-            sources {
-                helloTest {
-                    cunit {
-                        source.srcDir "src/alternateHelloTest/cunit"
-                    }
-                }
-            }
-"""
-
-        then:
-        succeeds "runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "can configure location of cunit test sources before component is declared"() {
-        given:
-        app.library.writeSources(file("src/hello"))
-        app.cunitTests.writeSources(file("src/alternateHelloTest"))
-
-        when:
-        buildFile << """
-            sources {
-                helloTest {
-                    cunit(CSourceSet) {
-                        source.srcDir "src/alternateHelloTest/cunit"
-                    }
-                }
-            }
-"""
-        useStandardConfig()
-
-        then:
-        succeeds "runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "variant-dependent sources are included in test binary"() {
-        given:
-        useStandardConfig()
-        app.library.headerFiles*.writeToDir(file("src/hello"))
-        app.cunitTests.writeSources(file("src/helloTest"))
-        app.library.sourceFiles*.writeToDir(file("src/variant"))
-
-        when:
-        buildFile << """
-            sources {
-                variant {
-                    c {
-                        lib sources.hello.c
-                    }
-                }
-            }
-            binaries.withType(LibraryBinary) {
-                source sources.variant
-            }
-"""
-
-        then:
-        succeeds "runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "can configure variant-dependent test sources"() {
-        given:
-        useStandardConfig()
-        app.library.writeSources(file("src/hello"))
-        app.cunitTests.writeSources(file("src/variantTest"))
-
-        when:
-        buildFile << """
-            sources {
-                variantTest {
-                    cunit(CSourceSet) {
-                        lib sources.hello.c
-                        lib sources.helloTest.cunitLauncher
-                    }
-                }
-            }
-            binaries.withType(TestSuiteExecutableBinary) {
-                source sources.variantTest.cunit
-            }
-"""
-
-        then:
-        succeeds "runHelloTestCUnitExe"
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "test suite skipped after successful run"() {
-        given:
-        useStandardConfig()
-        useConventionalSourceLocations()
-        run "runHelloTestCUnitExe"
-
-        when:
-        run "runHelloTestCUnitExe"
-
-        then:
-        skipped ":helloTestCUnitExe", ":runHelloTestCUnitExe"
-    }
-
-    def "can build and run cunit failing test suite"() {
-        when:
-        useStandardConfig()
-        useFailingTestSources()
-        fails "runHelloTestCUnitExe"
-
-        then:
-        failure.assertHasDescription("Execution failed for task ':runHelloTestCUnitExe'.")
-        failure.assertHasCause("There were failing tests. See the results at: ")
-
-        and:
-        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestCunit",
-                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
-        output.contains """
-There were test failures:
-"""
-        and:
-        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
-        testResults.suiteNames == ['hello test']
-        testResults.suites['hello test'].passingTests == []
-        testResults.suites['hello test'].failingTests == ['test_sum']
-        testResults.checkTestCases(1, 0, 1)
-        testResults.checkAssertions(3, 1, 2)
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "build does not break for failing tests if ignoreFailures is true"() {
-        when:
-        useStandardConfig()
-        useFailingTestSources()
-        buildFile << """
-    tasks.withType(RunTestExecutable) {
-        it.ignoreFailures = true
-    }
-"""
-        succeeds "runHelloTestCUnitExe"
-
-        then:
-        output.contains """
-There were test failures:
-"""
-        output.contains "There were failing tests. See the results at: "
-
-        and:
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml").assertExists()
-        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
-    }
-
-    def "test suite not skipped after failing run"() {
-        given:
-        useStandardConfig()
-        useFailingTestSources()
-        fails "runHelloTestCUnitExe"
-
-        when:
-        fails "runHelloTestCUnitExe"
-
-        then:
-        executedAndNotSkipped ":runHelloTestCUnitExe"
-    }
-
-    def "creates visual studio solution and project for cunit test suite"() {
-        given:
-        useStandardConfig()
-        useConventionalSourceLocations()
-        buildFile.text = "apply plugin: 'visual-studio'\n" + buildFile.text
-
-        when:
-        succeeds "helloTestVisualStudio"
-
-        then:
-        final mainSolution = new SolutionFile(file("helloTestExe.sln"))
-        mainSolution.assertHasProjects("helloTestExe")
-
-        and:
-        final projectFile = new ProjectFile(file("helloTestExe.vcxproj"))
-        projectFile.sourceFiles as Set == [
-                "build.gradle",
-                "build/src/helloTestCUnitLauncher/cunit/gradle_cunit_main.c",
-                "src/helloTest/cunit/test.c",
-                "src/hello/c/hello.c",
-                "src/hello/c/sum.c"
-        ] as Set
-        projectFile.headerFiles == [
-                "build/src/helloTestCUnitLauncher/headers/gradle_cunit_register.h",
-                "src/hello/headers/hello.h"
-        ]
-        projectFile.projectConfigurations.keySet() == ['debug'] as Set
-        with (projectFile.projectConfigurations['debug']) {
-            includePath == "build/src/helloTestCUnitLauncher/headers;src/helloTest/headers;src/hello/headers;libs/cunit/2.1-2/include"
-        }
-    }
-
-    private useConventionalSourceLocations() {
-        app.library.writeSources(file("src/hello"))
-        app.cunitTests.writeSources(file("src/helloTest"))
-    }
-
-    private useFailingTestSources() {
-        useConventionalSourceLocations()
-        file("src/hello/c/sum.c").text = file("src/hello/c/sum.c").text.replace("return a + b;", "return 2;")
-    }
-
-    @Override
-    String getOutput() {
-        return normaliseLineSeparators(super.getOutput())
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppBinariesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppBinariesIntegrationTest.groovy
deleted file mode 100755
index ee59943..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppBinariesIntegrationTest.groovy
+++ /dev/null
@@ -1,371 +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.nativebinaries.language.cpp
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.hamcrest.Matchers
-import spock.lang.IgnoreIf
-import spock.lang.Issue
-import spock.lang.Unroll
-
-class CppBinariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def "can configure the binaries of a C++ application"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-
-            executables {
-                main {
-                    binaries.all {
-                        cppCompiler.define 'ENABLE_GREETING'
-                    }
-                }
-            }
-        """
-
-        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/binaries/mainExecutable/main")
-        executable.exec().out == "Hello!"
-    }
-
-    def "can build debug binaries for a C++ executable"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-
-            executables {
-                main {
-                    binaries.all {
-                        if (toolChain in VisualCpp) {
-                            cppCompiler.args '/Zi'
-                            linker.args '/DEBUG'
-                        } else {
-                            cppCompiler.args '-g'
-                        }
-                    }
-                }
-            }
-        """
-
-        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/main")
-        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"
-
-            libraries {
-                hello {
-                    binaries.all {
-                        cppCompiler.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/binaries/helloStaticLibrary/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 {
-                    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!"
-    }
-
-    def "can customize binaries before and after linking"() {
-        def helloWorldApp = new CppHelloWorldApp()
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-            executables {
-                main {}
-            }
-
-            binaries.withType(ExecutableBinary) { binary ->
-                def preLink = task("\${binary.name}PreLink") {
-                    dependsOn binary.tasks.withType(CppCompile)
-
-                    doLast {
-                        println "Pre Link"
-                    }
-                }
-                binary.tasks.link.dependsOn preLink
-
-                def postLink = task("\${binary.name}PostLink") {
-                    dependsOn binary.tasks.link
-
-                    doLast {
-                        println "Post Link"
-                    }
-                }
-
-                binary.builtBy postLink
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executedTasks.tail() == [":compileMainExecutableMainCpp", ":mainExecutablePreLink", ":linkMainExecutable", ":mainExecutablePostLink", ":mainExecutable"]
-    }
-
-    @Issue("GRADLE-2973")
-    @IgnoreIf({ !GradleContextualExecuter.isParallel() })
-    def "releases cache lock when compilation fails with --parallel"() {
-        def helloWorldApp = new CppHelloWorldApp()
-        given:
-        settingsFile << "include ':a', ':b'"
-        buildFile << """
-            subprojects {
-                apply plugin: 'cpp'
-                executables {
-                    main {}
-                }
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("a/src/main"))
-        helloWorldApp.writeSources(file("b/src/main"))
-
-        file("b/src/main/cpp/broken.cpp") << """
-    A broken C++ file
-"""
-
-        expect:
-        fails "mainExecutable"
-        failure.assertThatCause(Matchers.not(Matchers.containsString("Could not stop")))
-    }
-
-    def "can configure output file for binaries"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            executables {
-                main {
-                    binaries.all {
-                        executableFile = modPath(executableFile)
-                    }
-                }
-            }
-            libraries {
-                hello {
-                    binaries.withType(SharedLibraryBinary) {
-                        sharedLibraryFile = modPath(sharedLibraryFile)
-                        sharedLibraryLinkFile = modPath(sharedLibraryLinkFile)
-                    }
-                    binaries.withType(StaticLibraryBinary) {
-                        staticLibraryFile = modPath(staticLibraryFile)
-                    }
-                }
-            }
-            //sources.main.cpp.lib libraries.hello
-
-            def modPath(File file) {
-                new File("\${file.parentFile}/new_output/_\${file.name}")
-            }
-"""
-
-        when:
-        succeeds "mainExecutable", "helloSharedLibrary", "helloStaticLibrary"
-
-        then:
-        def modPath = {TestFile file -> new TestFile("${file.parentFile}/new_output/_${file.name}")}
-        modPath(executable("build/binaries/mainExecutable/main").file).assertExists()
-        modPath(sharedLibrary("build/binaries/helloSharedLibrary/hello").file).assertExists()
-        modPath(staticLibrary("build/binaries/helloStaticLibrary/hello").file).assertExists()
-    }
-
-    @Unroll
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "can link to #linkage library binary with custom output file"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            executables {
-                main {}
-            }
-            libraries {
-                hello {
-                    binaries.withType(SharedLibraryBinary) {
-                        sharedLibraryFile = modPath(sharedLibraryFile)
-                        sharedLibraryLinkFile = modPath(sharedLibraryLinkFile)
-                    }
-                    binaries.withType(StaticLibraryBinary) {
-                        staticLibraryFile = modPath(staticLibraryFile)
-                    }
-                }
-            }
-            sources.main.cpp.lib libraries.hello.${linkage}
-
-            def modPath(File file) {
-                new File("\${file.parentFile}/new_output/_\${file.name}")
-            }
-"""
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        where:
-        linkage << ["static", "shared"]
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppCallingCLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppCallingCLanguageIntegrationTest.groovy
deleted file mode 100755
index bdb89f5..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppCallingCLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,25 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppCallingCHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-
-class CppCallingCLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-    HelloWorldApp helloWorldApp = new CppCallingCHelloWorldApp()
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100755
index 74521e2..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,25 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-
-class CppLanguageIncrementalBuildIntegrationTest extends AbstractLanguageIncrementalBuildIntegrationTest {
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        new CppHelloWorldApp()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalCompileIntegrationTest.groovy
deleted file mode 100644
index 753d0db..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIncrementalCompileIntegrationTest.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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-
-class CppLanguageIncrementalCompileIntegrationTest extends AbstractLanguageIncrementalCompileIntegrationTest {
-     @Override
-     IncrementalHelloWorldApp getHelloWorldApp() {
-         return new CppHelloWorldApp()
-     }
- }
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIntegrationTest.groovy
deleted file mode 100755
index cc8b508..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,114 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppCompilerDetectingTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-
-class CppLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-
-    HelloWorldApp helloWorldApp = new CppHelloWorldApp()
-
-    def "build fails when compilation fails"() {
-        given:
-        buildFile << """
-             executables {
-                 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.")
-    }
-
-    def "sources are compiled with C++ compiler"() {
-        def app = new CppCompilerDetectingTestApp()
-
-        given:
-        app.writeSources(file('src/main'))
-
-        and:
-        buildFile << """
-             executables {
-                 main {}
-             }
-         """
-
-        expect:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == app.expectedOutput(toolChain)
-    }
-
-    def "can manually define C++ source sets"() {
-        given:
-        helloWorldApp.getLibraryHeader().writeToDir(file("src/shared"))
-
-        file("src/main/cpp/main.cpp") << helloWorldApp.mainSource.content
-        file("src/main/cpp2/hello.cpp") << helloWorldApp.librarySources[0].content
-        file("src/main/sum-sources/sum.cpp") << helloWorldApp.librarySources[1].content
-
-
-        and:
-        buildFile << """
-            sources {
-                main {
-                    cpp {
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                    cpp2(CppSourceSet) {
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                    cpp3(CppSourceSet) {
-                        source {
-                            srcDir "src/main/sum-sources"
-                        }
-                        exportedHeaders {
-                            srcDirs "src/shared/headers"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-"""
-
-        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/nativebinaries/language/cpp/CppPluginGoodBehaviourTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppPluginGoodBehaviourTest.groovy
deleted file mode 100644
index fe3c020..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/CppPluginGoodBehaviourTest.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.nativebinaries.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/nativebinaries/language/cpp/DuplicateBaseNamesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/DuplicateBaseNamesIntegrationTest.groovy
deleted file mode 100644
index 00cdbd7..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/DuplicateBaseNamesIntegrationTest.groovy
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateAssemblerBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateCBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateCppBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateMixedSameBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateObjectiveCBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateObjectiveCppBaseNamesTestApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.DuplicateWindowsResourcesBaseNamesTestApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-
-// TODO add coverage for mixed sources
-class DuplicateBaseNamesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    def "can have sourcefiles with same base name but different directories"() {
-        setup:
-        testApp.writeSources(file("src/main"))
-        buildFile.text = ""
-        testApp.plugins.each{ plugin ->
-            buildFile << "apply plugin: '$plugin'\n"
-        }
-
-        buildFile << """
-        binaries.all{
-            linker.args "-v"
-        }
-        """
-        buildFile << """
-            model {
-                platforms {
-                    x86 {
-                        architecture "i386"
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-
-            """
-        expect:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == expectedOutput
-        where:
-        testApp                                              |   expectedOutput
-        new DuplicateCBaseNamesTestApp()                     |    "foo1foo2"
-        new DuplicateCppBaseNamesTestApp()                   |    "foo1foo2"
-        new DuplicateAssemblerBaseNamesTestApp(toolChain)    |    "foo1foo2"
-        new DuplicateMixedSameBaseNamesTestApp(toolChain)    |    "fooFromC\nfooFromCpp\nfooFromAsm\n"
-    }
-
-    //TODO Rene: inline with testcase above once we got coverage for objective-c and objective-cpp on windows
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "can have objectiveC and objectiveCpp source files with same name in different directories"(){
-        setup:
-        testApp.writeSources(file("src/main"))
-        buildFile.text = ""
-        testApp.plugins.each{ plugin ->
-            buildFile << "apply plugin: '$plugin'\n"
-        }
-        buildFile << testApp.extraConfiguration
-
-        buildFile << """
-            executables {
-                main {}
-            }
-
-            """
-        expect:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == "foo1foo2"
-        where:
-        testApp << [ new DuplicateObjectiveCBaseNamesTestApp(), new DuplicateObjectiveCppBaseNamesTestApp() ]
-    }
-
-    @RequiresInstalledToolChain(VisualCpp)
-    def "windows-resources can have sourcefiles with same base name but different directories"() {
-        setup:
-        def testApp = new DuplicateWindowsResourcesBaseNamesTestApp();
-        testApp.writeSources(file("src/main"))
-        buildFile.text = ""
-        testApp.plugins.each{ plugin ->
-            buildFile << "apply plugin: '$plugin'\n"
-        }
-        buildFile <<"""
-            binaries.all {
-                linker.args "user32.lib"
-            }
-            executables {
-                main {}
-            }
-            """
-        expect:
-        succeeds "mainExecutable"
-        executable("build/binaries/mainExecutable/main").exec().out == "foo1foo2"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainCustomisationIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainCustomisationIntegrationTest.groovy
deleted file mode 100755
index 2e2bbe9..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainCustomisationIntegrationTest.groovy
+++ /dev/null
@@ -1,193 +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.nativebinaries.language.cpp
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.GccCompatible
-
- at RequiresInstalledToolChain(GccCompatible)
-class GccToolChainCustomisationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def helloWorldApp = new CHelloWorldApp()
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'c'
-
-            model {
-                toolChains {
-                    ${toolChain.buildScriptConfig}
-                }
-            }
-
-            executables {
-                main {
-                    binaries.all {
-                        lib libraries.hello.static
-                    }
-                }
-            }
-            libraries {
-                hello {}
-            }
-"""
-
-        helloWorldApp.executable.writeSources(file("src/main"))
-        helloWorldApp.library.writeSources(file("src/hello"))
-    }
-
-    def "can add binary configuration to target a platform"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        addPlatformConfiguration(new ArmArchitecture())
-                    }
-                }
-                platforms {
-                    arm {
-                        architecture "arm"
-                    }
-                    i386 {
-                        architecture "i386"
-                    }
-                }
-            }
-
-            class ArmArchitecture implements TargetPlatformConfiguration {
-                boolean supportsPlatform(Platform element) {
-                    return element.getArchitecture().name == "arm"
-                }
-
-                List<String> getCppCompilerArgs() {
-                    []
-                }
-
-                List<String> getCCompilerArgs() {
-                    ["-m32", "-DFRENCH"]
-                }
-
-                List<String> getObjectiveCCompilerArgs() {
-                    []
-                }
-
-                List<String> getObjectiveCppCompilerArgs() {
-                    []
-                }
-
-                List<String> getAssemblerArgs() {
-                    []
-                }
-
-                List<String> getLinkerArgs() {
-                    ["-m32"]
-                }
-
-                List<String> getStaticLibraryArchiverArgs() {
-                    []
-                }
-            }
-"""
-
-        and:
-        succeeds "armMainExecutable", "i386MainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/arm/main").binaryInfo.arch.name == "x86"
-        executable("build/binaries/mainExecutable/arm/main").exec().out == helloWorldApp.frenchOutput
-
-        executable("build/binaries/mainExecutable/i386/main").binaryInfo.arch.name == "x86"
-        executable("build/binaries/mainExecutable/i386/main").exec().out == helloWorldApp.englishOutput
-    }
-
-    def "can add action to tool chain that modifies tool arguments prior to execution"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        cCompiler.withArguments { args ->
-                            Collections.replaceAll(args, "CUSTOM", "-DFRENCH")
-                        }
-                        linker.withArguments { args ->
-                            args.remove "CUSTOM"
-                        }
-                        staticLibArchiver.withArguments { args ->
-                            args.remove "CUSTOM"
-                        }
-                    }
-                }
-            }
-            binaries.all {
-                cCompiler.args "CUSTOM"
-                linker.args "CUSTOM"
-            }
-            binaries.withType(StaticLibraryBinary) {
-                staticLibArchiver.args "CUSTOM"
-            }
-"""
-        then:
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
-    }
-
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "can configure tool executables"() {
-        def binDir = testDirectory.createDir("bin")
-        wrapperTool(binDir, "c-compiler", toolChain.CCompiler, "-DFRENCH")
-        wrapperTool(binDir, "static-lib", toolChain.staticLibArchiver)
-        wrapperTool(binDir, "linker", toolChain.linker)
-
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        path file('${binDir.toURI()}')
-                        cCompiler.executable = 'c-compiler'
-                        staticLibArchiver.executable = 'static-lib'
-                        linker.executable = 'linker'
-                    }
-                }
-            }
-"""
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
-    }
-
-    def wrapperTool(TestFile binDir, String wrapperName, String executable, String... additionalArgs) {
-        def script = binDir.file(OperatingSystem.current().getExecutableName(wrapperName))
-        if (OperatingSystem.current().windows) {
-            script.text = "${executable} ${additionalArgs.join(' ')} %*"
-        } else {
-            script.text = "${executable} ${additionalArgs.join(' ')} \"\$@\""
-            script.permissions = "rwxr--r--"
-        }
-        return script
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainDiscoveryIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainDiscoveryIntegrationTest.groovy
deleted file mode 100755
index 7354288..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GccToolChainDiscoveryIntegrationTest.groovy
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.hamcrest.Matchers
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.GccCompatible
-
- at RequiresInstalledToolChain(GccCompatible)
-class GccToolChainDiscoveryIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def helloWorldApp = new CHelloWorldApp()
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'c'
-
-            model {
-                toolChains {
-                    ${toolChain.buildScriptConfig}
-                }
-            }
-
-            executables {
-                main {
-                    binaries.all {
-                        lib libraries.hello.static
-                    }
-                }
-            }
-            libraries {
-                hello {}
-            }
-"""
-
-        helloWorldApp.executable.writeSources(file("src/main"))
-        helloWorldApp.library.writeSources(file("src/hello"))
-    }
-
-    def "can build when language tools that are not required are not available"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        cppCompiler.executable = 'does-not-exist'
-                    }
-                }
-            }
-"""
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
-    }
-
-    def "does not break when compiler not available and not building"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        cCompiler.executable = 'does-not-exist'
-                    }
-                }
-            }
-"""
-
-        then:
-        succeeds "help"
-    }
-
-    def "fails when required language tool is not available"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        cCompiler.executable = 'does-not-exist'
-                    }
-                }
-            }
-"""
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.")
-        failure.assertThatCause(Matchers.startsWith("Could not find C compiler 'does-not-exist'"))
-    }
-
-    def "fails when required linker tool is not available"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    ${toolChain.id} {
-                        linker.executable = 'does-not-exist'
-                    }
-                }
-            }
-"""
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.")
-        failure.assertThatCause(Matchers.startsWith("Could not find Linker 'does-not-exist'"))
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GeneratedSourcesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GeneratedSourcesIntegrationTest.groovy
deleted file mode 100755
index c0115a8..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/GeneratedSourcesIntegrationTest.groovy
+++ /dev/null
@@ -1,383 +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.nativebinaries.language.cpp
-import org.apache.commons.io.FileUtils
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.WindowsResourceHelloWorldApp
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-// TODO:DAZ Test incremental
-class GeneratedSourcesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    def setup() {
-        settingsFile << "rootProject.name = 'test'"
-        buildFile << """
-    class GenerateSources extends DefaultTask {
-        @InputDirectory File inputDir
-        @OutputDirectory File sourceDir
-        @OutputDirectory @Optional File headerDir
-
-        @TaskAction
-        void processInputFiles() {
-            project.copy {
-                from inputDir
-                into sourceDir.parentFile
-                filter { String line ->
-                    line.replaceAll('REMOVE_ME', '')
-                }
-            }
-        }
-    }
-    task generateCSources(type: GenerateSources) {
-        inputDir project.file("src/input")
-        headerDir project.file("build/src/generated/headers")
-        sourceDir project.file("build/src/generated/c")
-    }
-"""
-    }
-
-    private void degenerateInputSources() {
-        FileUtils.listFiles(file("src/input"), null, true).each { File file ->
-            file.text = "REMOVE_ME\n" + file.text
-        }
-    }
-
-    def "generator task produces c sources and headers"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    sources.main.c.generatedBy tasks.generateCSources
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "generator task produces sources for dependent source set with headers only"() {
-        given:
-        // Write sources to src/main, headers to src/input
-        def app = new CHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.sourceFiles*.writeToDir(file("src/main"))
-        app.library.headerFiles*.writeToDir(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    sources {
-        generated {
-            cHeaders(CSourceSet) {
-                generatedBy tasks.generateCSources
-            }
-        }
-    }
-    sources.main.c.lib sources.generated.cHeaders
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "generator task produces sources for dependent source set"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    sources {
-        generated {
-            c {
-                generatedBy tasks.generateCSources
-            }
-        }
-    }
-    sources.main.c.lib sources.generated.c
-    executables.main.source sources.generated.c
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "can have library composed of generated sources"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    libraries {
-        hello {}
-    }
-    sources {
-        hello {
-            c {
-                generatedBy tasks.generateCSources
-            }
-        }
-    }
-    sources.main.c.lib library: 'hello', linkage: 'static'
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "generator task produces cpp sources"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'cpp'
-
-    task generateCppSources(type: GenerateSources) {
-        inputDir project.file("src/input")
-        headerDir project.file("build/src/generated/headers")
-        sourceDir project.file("build/src/generated/cpp")
-    }
-
-    executables {
-        main {}
-    }
-    sources.main.cpp.generatedBy tasks.generateCppSources
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "generator task produces assembler sources"() {
-        given:
-        def app = new MixedLanguageHelloWorldApp(toolChain)
-        def asmSources = app.sourceFiles.findAll {it.path == 'asm'}
-        def mainSources = app.headerFiles + app.sourceFiles - asmSources
-        mainSources*.writeToDir(file("src/main"))
-        asmSources*.writeToDir(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << app.pluginScript
-        buildFile << app.extraConfiguration
-        buildFile << """
-    task generateAsmSources(type: GenerateSources) {
-        inputDir project.file("src/input")
-        sourceDir project.file("build/src/generated/asm")
-    }
-
-    executables {
-        main {}
-    }
-    sources.main.asm.generatedBy tasks.generateAsmSources
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    @RequiresInstalledToolChain(VisualCpp)
-    def "generator task produces windows resources"() {
-        given:
-        def app = new WindowsResourceHelloWorldApp()
-        def rcSources = app.sourceFiles.findAll {it.path == 'rc'}
-        def mainSources = app.headerFiles + app.sourceFiles - rcSources
-        mainSources*.writeToDir(file("src/main"))
-        rcSources*.writeToDir(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << app.pluginScript
-        buildFile << app.extraConfiguration
-        buildFile << """
-    task generateRcSources(type: GenerateSources) {
-        inputDir project.file("src/input")
-        sourceDir project.file("build/src/generated/rc")
-    }
-
-    executables {
-        main {}
-    }
-    sources.main.rc.generatedBy tasks.generateRcSources
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "produces reasonable error message when generator task does not have sourceDir property"() {
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    task generateSources {
-    }
-
-    executables {
-        main {}
-    }
-    sources.main.c.generatedBy tasks.generateSources
-"""
-
-        and:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription "A problem occurred configuring root project 'test'."
-        failure.assertHasCause "Could not find property 'sourceDir' on task ':generateSources'."
-    }
-
-    def "can explicitly configure source and header directories from generator task"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    sources {
-        main {
-            c {
-                builtBy tasks.generateCSources
-                source {
-                    srcDirs tasks.generateCSources.sourceDir
-                }
-                exportedHeaders {
-                    srcDirs tasks.generateCSources.headerDir
-                }
-            }
-        }
-    }
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "can configure generator task properties after wiring"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        when:
-        buildFile << """
-    apply plugin: 'c'
-
-    task lateConfiguredGenerator(type: GenerateSources)
-
-    executables {
-        main {}
-    }
-    sources.main.c.generatedBy tasks.lateConfiguredGenerator
-
-    lateConfiguredGenerator {
-        inputDir project.file("src/input")
-        headerDir project.file("build/src/generated/headers")
-        sourceDir project.file("build/src/generated/c")
-    }
-"""
-
-        then:
-        executableBuilt(app)
-    }
-
-    def "creates visual studio project including generated sources"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/input"))
-        degenerateInputSources()
-
-        and:
-        buildFile << """
-    apply plugin: 'visual-studio'
-    apply plugin: 'c'
-
-    executables {
-        main {}
-    }
-    sources.main.c.generatedBy tasks.generateCSources
-"""
-
-        when:
-        succeeds "mainVisualStudio"
-
-        then:
-        final mainSolution = new SolutionFile(file("mainExe.sln"))
-        mainSolution.assertHasProjects("mainExe")
-
-        and:
-        final projectFile = new ProjectFile(file("mainExe.vcxproj"))
-        projectFile.sourceFiles as Set == [
-                "build.gradle",
-                "build/src/generated/c/hello.c",
-                "build/src/generated/c/main.c",
-                "build/src/generated/c/sum.c"
-        ] as Set
-        projectFile.headerFiles == [ "build/src/generated/headers/hello.h" ]
-        projectFile.projectConfigurations.keySet() == ['debug'] as Set
-        with (projectFile.projectConfigurations['debug']) {
-            includePath == "build/src/generated/headers"
-        }
-    }
-
-    def executableBuilt(def app) {
-        succeeds "mainExecutable"
-        assert executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
-        true
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryApiDependenciesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryApiDependenciesIntegrationTest.groovy
deleted file mode 100755
index e060551..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryApiDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,237 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Unroll
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class LibraryApiDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-        buildFile << """
-            apply plugin: "cpp"
-            // Allow static libraries to be linked into shared
-            binaries.withType(StaticLibraryBinary) {
-                if (toolChain in Gcc || toolChain in Clang) {
-                    cppCompiler.args '-fPIC'
-                }
-            }
-"""
-    }
-
-    @Unroll
-    def "can use api linkage via #notationName notation"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-
-        app.library.headerFiles*.writeToDir(file("src/helloApi"))
-        app.library.sourceFiles*.writeToDir(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                helloApi {}
-                hello {}
-            }
-            sources.main.cpp.lib ${notation}
-            sources.main.cpp.lib library: 'hello'
-            sources.hello.cpp.lib ${notation}
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        where:
-        notationName | notation
-        "direct"     | "libraries.helloApi.api"
-        "map"        | "library: 'helloApi', linkage: 'api'"
-    }
-
-    def "executable compiles using functions defined in header-only utility library"() {
-        given:
-        file("src/util/headers/util.h") << """
-            const char *message = "Hello from the utility library";
-"""
-        file("src/main/cpp/main.cpp") << """
-            #include "util.h"
-            #include <iostream>
-
-            int main () {
-                std::cout << message;
-                return 0;
-            }
-"""
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                util {}
-            }
-            sources.main.cpp.lib library: 'util'
-"""
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == "Hello from the utility library"
-    }
-
-    def "executable compiles using functions defined in utility library with build type variants"() {
-        given:
-        file("src/util/debug/util.h") << """
-            const char *message = "Hello from the debug library";
-"""
-        file("src/util/release/util.h") << """
-            const char *message = "Hello from the release library";
-"""
-        file("src/main/cpp/main.cpp") << """
-            #include "util.h"
-            #include <iostream>
-
-            int main () {
-                std::cout << message;
-                return 0;
-            }
-"""
-        buildFile << """
-            model {
-                buildTypes {
-                    debug
-                    release
-                }
-            }
-            executables {
-                main
-            }
-            libraries {
-                util {
-                    binaries.all { binary ->
-                        binary.source sources[binary.buildType.name]
-                    }
-                }
-            }
-            sources {
-                main.cpp.lib library: 'util'
-                debug.cpp {
-                    exportedHeaders.srcDir "src/util/debug"
-                }
-                release.cpp {
-                    exportedHeaders.srcDir "src/util/release"
-                }
-            }
-"""
-        when:
-        succeeds "installDebugMainExecutable", "installReleaseMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable/debug").exec().out == "Hello from the debug library"
-        installation("build/install/mainExecutable/release").exec().out == "Hello from the release library"
-    }
-
-    def "can choose alternative library implementation of api"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        app.alternateLibrarySources*.writeToDir(file("src/hello2"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                hello2 {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'api'
-            sources.main.cpp.lib library: 'hello2'
-            sources.hello2.cpp.lib library: 'hello', linkage: 'api'
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.alternateLibraryOutput
-    }
-
-    def "can use api linkage for component graph with library dependency cycle"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-        app.greetingsHeader.writeToDir(file("src/hello"))
-        app.greetingsSources*.writeToDir(file("src/greetings"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib library: 'hello'
-            sources.hello.cpp.lib library: 'greetings', linkage: 'static'
-            sources.greetings.cpp.lib library: 'hello', linkage: 'api'
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-    }
-
-    def "can compile but not link when executable depends on api of library required for linking"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'api'
-        """
-
-        when:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryBinariesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryBinariesIntegrationTest.groovy
deleted file mode 100755
index 45bf094..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryBinariesIntegrationTest.groovy
+++ /dev/null
@@ -1,298 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Issue
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class LibraryBinariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-    }
-
-    def "executable can use a mix of static and shared libraries"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            libraries {
-                helloStatic {}
-                helloShared {}
-            }
-            sources.main.cpp.lib libraries.helloStatic.static
-            sources.main.cpp.lib libraries.helloShared
-        """
-
-        and:
-        file("src/helloStatic/cpp/hellostatic.cpp") << """
-            #include <iostream>
-
-            void helloStatic() {
-                std::cout << "Hello static";
-            }
-        """
-
-        and:
-        file("src/helloStatic/headers/hellostatic.h") << """
-            void helloStatic();
-        """
-
-        and:
-        file("src/helloShared/cpp/helloshared.cpp") << """
-            #include <iostream>
-            #include "helloshared.h"
-
-            void DLL_FUNC helloShared() {
-                std::cout << "Hello shared";
-            }
-        """
-
-        and:
-        file("src/helloShared/headers/helloshared.h") << """
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC helloShared();
-        """
-
-        and:
-        file("src/main/cpp/main.cpp") << """
-            #include "hellostatic.h"
-            #include "helloshared.h"
-
-            int main () {
-                helloStatic();
-                helloShared();
-                return 0;
-            }
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        staticLibrary("build/binaries/helloStaticStaticLibrary/helloStatic").assertExistsAndDelete()
-        sharedLibrary("build/binaries/helloSharedSharedLibrary/helloShared").assertExistsAndDelete()
-        installation("build/install/mainExecutable")
-                .assertIncludesLibraries("helloShared")
-                .exec().out == "Hello staticHello shared"
-    }
-
-    def "executable can use a combination of libraries from the same and other projects"() {
-        given:
-        settingsFile << """
-include 'exe', 'lib'
-"""
-        buildFile << """
-            project('lib') {
-                apply plugin: "cpp"
-                libraries {
-                    helloLib {}
-                }
-            }
-            project('exe') {
-                evaluationDependsOn(":lib")
-                apply plugin: "cpp"
-                executables {
-                    main {}
-                }
-                libraries {
-                    helloMain {}
-                }
-                sources.main.cpp {
-                    lib libraries.helloMain
-                    lib project(":lib").libraries.helloLib
-                }
-            }
-        """
-
-        and:
-        file("lib/src/helloLib/cpp/hellolib.cpp") << """
-            #include <iostream>
-            #include "hellolib.h"
-
-            void DLL_FUNC helloLib() {
-                std::cout << "Hello lib";
-            }
-        """
-
-        and:
-        file("lib/src/helloLib/headers/hellolib.h") << """
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC helloLib();
-        """
-
-        and:
-        file("exe/src/helloMain/cpp/hellomain.cpp") << """
-            #include <iostream>
-            #include "hellomain.h"
-
-            void DLL_FUNC helloMain() {
-                std::cout << "Hello main" << std::endl;
-            }
-        """
-
-        and:
-        file("exe/src/helloMain/headers/hellomain.h") << """
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC helloMain();
-        """
-
-        and:
-        file("exe/src/main/cpp/main.cpp") << """
-            #include "hellolib.h"
-            #include "hellomain.h"
-
-            int main () {
-                helloMain();
-                helloLib();
-                return 0;
-            }
-        """
-
-        when:
-        succeeds "exe:installMainExecutable"
-
-        then:
-        sharedLibrary("lib/build/binaries/helloLibSharedLibrary/helloLib").assertExistsAndDelete()
-        sharedLibrary("exe/build/binaries/helloMainSharedLibrary/helloMain").assertExistsAndDelete()
-        installation("exe/build/install/mainExecutable")
-                .assertIncludesLibraries("helloLib", "helloMain")
-                .exec().out == "Hello main\nHello lib"
-    }
-
-    def "source set library dependencies are not shared with other source sets"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-            apply plugin: "c"
-            executables {
-                main {}
-            }
-            libraries {
-                libCpp {}
-                libC {}
-            }
-            sources.main.cpp.lib libraries.libCpp.static
-            sources.main.c.lib libraries.libC.static
-        """
-
-        and:
-        file("src/main/headers/head.h") << """
-            void cppOut();
-
-            extern "C" {
-                void cOut();
-            }
-"""
-
-        file("src/main/cpp/main.cpp") << """
-            #include "head.h"
-
-            int main () {
-                cppOut();
-                cOut();
-                return 0;
-            }
-"""
-        and: "C and CPP sources sets depend on header file with same name"
-
-        file("src/main/cpp/test.cpp") << """
-            #include <iostream>
-            #include "output.h"
-
-            void cppOut() {
-                std::cout << OUTPUT << "_";
-            }
-"""
-
-        file("src/main/c/test.c") << """
-            #include <stdio.h>
-            #include "output.h"
-
-            void cOut() {
-                printf(OUTPUT);
-            }
-"""
-
-        and: "Library header files define different OUTPUT values"
-
-        file("src/libCpp/headers/output.h") << """
-            #define OUTPUT "CPP"
-"""
-
-        file("src/libC/headers/output.h") << """
-            #define OUTPUT "C"
-"""
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == "CPP_C"
-    }
-
-    @Issue("GRADLE-2925")
-    def "headers for source set added to library binary are available to consuming binary"() {
-        def app = new CppHelloWorldApp()
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-            sources {
-                helloLib {}
-            }
-            executables {
-                main {}
-            }
-            libraries {
-                hello {
-                    binaries.all {
-                        source sources.helloLib.cpp
-                    }
-                }
-            }
-            sources.main.cpp.lib libraries.hello
-        """
-
-        and:
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/helloLib"))
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryDependenciesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryDependenciesIntegrationTest.groovy
deleted file mode 100755
index 740b7a9..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/LibraryDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,350 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ExeWithDiamondDependencyHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Unroll
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class LibraryDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-        buildFile << """
-            allprojects {
-                apply plugin: "cpp"
-                // Allow static libraries to be linked into shared
-                binaries.withType(StaticLibraryBinary) {
-                    if (toolChain in Gcc || toolChain in Clang) {
-                        cppCompiler.args '-fPIC'
-                    }
-                }
-            }
-"""
-    }
-
-    @Unroll
-    def "produces reasonable error message when referenced library #label"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("exe/src/main"))
-        app.library.writeSources(file("lib/src/hello"))
-
-        and:
-        settingsFile.text = "include ':exe', ':other'"
-        buildFile << """
-        project(":exe") {
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.cpp.lib ${dependencyNotation}
-        }
-        project(":other") {
-            libraries {
-                hello {}
-            }
-        }
-        """
-
-        when:
-        fails ":exe:mainExecutable"
-
-        then:
-        failure.assertHasDescription(description)
-        failure.assertHasCause(cause)
-
-        where:
-        label                                  | dependencyNotation                      | description                                                | cause
-        "does not exist"                       | "library: 'unknown'"                    | "Could not locate library 'unknown'."                      | "Library with name 'unknown' not found."
-        "project that does not exist"          | "project: ':unknown', library: 'hello'" | "Could not locate library 'hello' for project ':unknown'." | "Project with path ':unknown' could not be found in project ':exe'."
-        "does not exist in referenced project" | "project: ':other', library: 'unknown'" | "Could not locate library 'unknown' for project ':other'." | "Library with name 'unknown' not found."
-    }
-
-    @Unroll
-    def "can use #notationName notation to reference library in same project"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.cpp.lib ${notation}
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        where:
-        notationName | notation
-        "direct"     | "libraries.hello"
-        "map"        | "library: 'hello'"
-    }
-
-    @Unroll
-    def "can use map #notationName notation to reference library dependency of binary"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {
-                    binaries.all { binary ->
-                        binary.lib ${notation}
-                    }
-                }
-            }
-            libraries {
-                hello {}
-            }
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        where:
-        notationName | notation
-        "direct"     | "libraries.hello"
-        "map"        | "library: 'hello'"
-    }
-
-    def "can use map notation to reference static library in same project"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/hello"))
-
-        and:
-        buildFile << """
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'static'
-            libraries {
-                hello {}
-            }
-        """
-
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
-    }
-
-    @Unroll
-    def "can use map notation to reference library in different project#label"() {
-        given:
-        def app = new CppHelloWorldApp()
-        app.executable.writeSources(file("exe/src/main"))
-        app.library.writeSources(file("lib/src/hello"))
-
-        and:
-        settingsFile.text = "include ':lib', ':exe'"
-        buildFile << """
-        project(":exe") {
-            ${explicitEvaluation}
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'hello'
-        }
-        project(":lib") {
-            libraries {
-                hello {}
-            }
-        }
-        """
-
-        when:
-        if (configureOnDemand) {
-            executer.withArgument('--configure-on-demand')
-        }
-        succeeds ":exe:installMainExecutable"
-
-        then:
-        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
-
-        where:
-        label                       | configureOnDemand | explicitEvaluation
-        ""                          | false             | ""
-        " with configure-on-demand" | true              | ""
-//        " with evaluationDependsOn" | false             | "evaluationDependsOn(':lib')"
-        " with afterEvaluate"       | false             | """
-project.afterEvaluate {
-    binaries*.libs*.linkFiles.files.each { println it }
-}
-"""
-    }
-
-    def "can use map notation to transitively reference libraries in different projects"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("greet/src/greetings"))
-
-        and:
-        settingsFile.text = "include ':exe', ':lib', ':greet'"
-        buildFile << """
-        project(":exe") {
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'hello'
-        }
-        project(":lib") {
-            libraries {
-                hello {}
-            }
-            sources.hello.cpp.lib project: ':greet', library: 'greetings', linkage: 'static'
-        }
-        project(":greet") {
-            libraries {
-                greetings {}
-            }
-        }
-        """
-
-        when:
-        succeeds ":exe:installMainExecutable"
-
-        then:
-        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
-    }
-
-    def "can have component graph with project dependency cycle"() {
-        given:
-        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
-        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("exe/src/greetings"))
-
-        and:
-        settingsFile.text = "include ':exe', ':lib'"
-        buildFile << """
-        project(":exe") {
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            libraries {
-                greetings {}
-            }
-            sources.main.cpp.lib project: ':lib', library: 'hello'
-        }
-        project(":lib") {
-            apply plugin: "cpp"
-            libraries {
-                hello {}
-            }
-            sources.hello.cpp.lib project: ':exe', library: 'greetings', linkage: 'static'
-        }
-        """
-
-        when:
-        succeeds ":exe:installMainExecutable"
-
-        then:
-        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
-    }
-
-    def "can have component graph with diamond dependency"() {
-        given:
-        def app = new ExeWithDiamondDependencyHelloWorldApp()
-        app.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-
-        and:
-        buildFile << """
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib libraries.hello.shared
-            sources.main.cpp.lib libraries.greetings.static
-            sources.hello.cpp.lib libraries.greetings.static
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        and:
-        notExecuted ":greetingsSharedLibrary"
-        sharedLibrary("build/binaries/greetingsSharedLibrary/greetings").assertDoesNotExist()
-    }
-
-    def "can have component graph with both static and shared variants of same library"() {
-        given:
-        def app = new ExeWithDiamondDependencyHelloWorldApp()
-        app.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
-
-        and:
-        buildFile << """
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-                greetings {}
-            }
-            sources.main.cpp.lib libraries.hello.shared
-            sources.main.cpp.lib libraries.greetings.shared
-            sources.hello.cpp.lib libraries.greetings.static
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.englishOutput
-
-        and:
-        executedAndNotSkipped ":greetingsSharedLibrary", ":greetingsStaticLibrary"
-        sharedLibrary("build/binaries/greetingsSharedLibrary/greetings").assertExists()
-        staticLibrary("build/binaries/greetingsStaticLibrary/greetings").assertExists()
-
-        // TODO:DAZ Investigate this output and parse to ensure that greetings is dynamically linked into mainExe but not helloShared
-        and:
-        println executable("build/binaries/mainExecutable/main").binaryInfo.listLinkedLibraries()
-        println sharedLibrary("build/binaries/helloSharedLibrary/hello").binaryInfo.listLinkedLibraries()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MixedLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MixedLanguageIntegrationTest.groovy
deleted file mode 100755
index 3a4bf5f..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MixedLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,168 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.MixedLanguageHelloWorldApp
-import org.gradle.nativebinaries.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 << """
-            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 {}
-            }
-        """
-
-        and:
-        helloWorldApp.allFiles.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
-    }
-
-    def "build and execute program with non-conventional source layout"() {
-        given:
-        buildFile << """
-            sources {
-                main {
-                    cpp {
-                        source {
-                            srcDirs "source"
-                            include "**/*.cpp"
-                        }
-                        exportedHeaders {
-                            srcDirs "source/hello", "include"
-                        }
-                    }
-                    c {
-                        source {
-                            srcDirs "source", "include"
-                            include "**/*.c"
-                        }
-                        exportedHeaders {
-                            srcDirs "source/hello", "include"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-        """
-        settingsFile << "rootProject.name = 'test'"
-
-        and:
-        file("source", "hello", "hello.cpp") << """
-            #include <iostream>
-
-            void hello () {
-              std::cout << "${HelloWorldApp.HELLO_WORLD}";
-            }
-        """
-
-        and:
-        file("source", "hello", "french", "bonjour.c") << """
-            #include <stdio.h>
-            #include "otherProject/bonjour.h"
-
-            void bonjour() {
-                printf("${HelloWorldApp.HELLO_WORLD_FRENCH}");
-            }
-        """
-
-        and:
-        file("source", "hello", "hello.h") << """
-            void hello();
-        """
-
-        and:
-        file("source", "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
-        """
-        file("src", "main", "cpp", "bad.cpp") << """
-            THIS FILE WON'T BE COMPILED
-        """
-        file("src", "main", "headers", "hello.h") << """
-            THIS FILE WON'T BE INCLUDED
-        """
-
-        when:
-        run "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == HelloWorldApp.HELLO_WORLD + HelloWorldApp.HELLO_WORLD_FRENCH
-    }
-
-
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MultipleToolChainIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MultipleToolChainIntegrationTest.groovy
deleted file mode 100755
index b0abeb9..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/MultipleToolChainIntegrationTest.groovy
+++ /dev/null
@@ -1,115 +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.nativebinaries.language.cpp
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppCompilerDetectingTestApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.gradle.util.TextUtil
-import spock.lang.Ignore
-
- at RequiresInstalledToolChain
-class MultipleToolChainIntegrationTest extends AbstractIntegrationSpec {
-    def helloWorld = new CppCompilerDetectingTestApp()
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'cpp'
-
-            executables {
-                main {}
-            }
-            libraries {
-                hello {}
-            }
-            sources.main.cpp.lib libraries.hello
-        """
-
-        helloWorld.executable.writeSources(file("src/main"))
-        helloWorld.library.writeSources(file("src/hello"))
-    }
-
-    // TODO:DAZ Test building for 2 platforms that require different tool chains
-    @Ignore
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-    def "can build with all multiple 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 << """
-            model {
-                toolChains {
-                    ${toolChainConfig}
-                    unavailable(Gcc) {
-                        linker.executable = "does_not_exist"
-                    }
-                }
-            }
-
-"""
-
-        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)
-        }
-    }
-
-    def "exception when building with unavailable tool chain"() {
-        when:
-        buildFile << """
-            model {
-                toolChains {
-                    bad(Gcc) {
-                        cCompiler.executable = "does_not_exist"
-                        cppCompiler.executable = "does_not_exist"
-                    }
-                }
-            }
-"""
-
-        helloWorld.writeSources(file("src/main"))
-
-        then:
-        fails "mainExecutable"
-
-        and:
-        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainCpp'.")
-        failure.assertHasCause(TextUtil.toPlatformLineSeparators("""No tool chain is available to build for platform 'current':
-  - Tool chain 'bad' (GNU GCC): """))
-    }
-
-    def checkInstall(String path, AvailableToolChains.InstalledToolChain toolChain) {
-        def executable = file(OperatingSystem.current().getScriptName(path))
-        executable.assertExists()
-        assert executable.execute([], toolChain.runtimeEnv).out == helloWorld.expectedOutput(toolChain)
-        return true
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/NativeBinariesPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/NativeBinariesPluginIntegrationTest.groovy
deleted file mode 100755
index 76da4f6..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/NativeBinariesPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,226 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.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: "c"
-            apply plugin: "cpp"
-            sources {
-                test {}
-            }
-            executables {
-                main {
-                    source sources.test.cpp
-                    source sources.test.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: "c"
-            apply plugin: "cpp"
-            sources {
-                test {}
-            }
-            executables {
-                main {}
-            }
-            binaries.all {
-                source sources.test.cpp
-                source sources.test.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: "c"
-            apply plugin: "cpp"
-            sources {
-                test {}
-            }
-            executables {
-                main {}
-            }
-            binaries.all {
-                source sources.test
-            }
-        """
-        
-        then:
-        succeeds "mainExecutable"
-
-        and:
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
-    }
-
-    def "ignores java sources added to binary"() {
-        given:
-        useMixedSources()
-        file("src/test/java/HelloWorld.java") << """
-    This would not compile
-"""
-
-        when:
-        buildFile << """
-            apply plugin: "c"
-            apply plugin: "cpp"
-            apply plugin: "java"
-
-            executables {
-                main {
-                    source sources.test.cpp
-                    source sources.test.c
-                    source sources.test.java
-                }
-            }
-         """
-
-        then:
-        succeeds "mainExecutable"
-
-        and:
-        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
-    }
-
-    private def useMixedSources() {
-        helloWorldApp.writeSources(file("src/test"))
-    }
-
-    def "build fails when link executable fails"() {
-        given:
-        buildFile << """
-            apply plugin: "cpp"
-            executables {
-                main {}
-            }
-        """
-
-        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"
-            libraries { main {} }
-        """
-
-        and:
-        file("src/main/cpp/hello1.cpp") << """
-            void hello() {
-            }
-"""
-
-        and:
-        file("src/main/cpp/hello2.cpp") << """
-            void hello() {
-            }
-"""
-
-        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"
-            libraries { main {} }
-            binaries.withType(StaticLibraryBinary) {
-                staticLibArchiver.args "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/nativebinaries/language/cpp/NativeSamplesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/NativeSamplesIntegrationTest.groovy
deleted file mode 100755
index a716e3e..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/NativeSamplesIntegrationTest.groovy
+++ /dev/null
@@ -1,409 +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.nativebinaries.language.cpp
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.test.cunit.CUnitTestResults
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class NativeSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    @Rule public final Sample c = new Sample(temporaryFolder, 'native-binaries/c')
-    @Rule public final Sample assembler = new Sample(temporaryFolder, 'native-binaries/assembler')
-    @Rule public final Sample cpp = new Sample(temporaryFolder, 'native-binaries/cpp')
-    @Rule public final Sample cppLib = new Sample(temporaryFolder, 'native-binaries/cpp-lib')
-    @Rule public final Sample cppExe = new Sample(temporaryFolder, 'native-binaries/cpp-exe')
-    @Rule public final Sample objectiveC = new Sample(temporaryFolder, 'native-binaries/objective-c')
-    @Rule public final Sample objectiveCpp = new Sample(temporaryFolder, 'native-binaries/objective-cpp')
-    @Rule public final Sample customLayout = new Sample(temporaryFolder, 'native-binaries/custom-layout')
-    @Rule public final Sample multiProject = new Sample(temporaryFolder, 'native-binaries/multi-project')
-    @Rule public final Sample flavors = new Sample(temporaryFolder, 'native-binaries/flavors')
-    @Rule public final Sample variants = new Sample(temporaryFolder, 'native-binaries/variants')
-    @Rule public final Sample toolChains = new Sample(temporaryFolder, 'native-binaries/tool-chains')
-    @Rule public final Sample windowsResources = new Sample(temporaryFolder, 'native-binaries/windows-resources')
-    @Rule public final Sample visualStudio = new Sample(temporaryFolder, 'native-binaries/visual-studio')
-    @Rule public final Sample prebuilt = new Sample(temporaryFolder, 'native-binaries/prebuilt')
-    @Rule public final Sample idl = new Sample(temporaryFolder, 'native-binaries/idl')
-    @Rule public final Sample cunit = new Sample(temporaryFolder, 'native-binaries/cunit')
-
-    def "assembler"() {
-        given:
-        sample assembler
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        nonSkippedTasks.count { it.startsWith(":assembleMainExecutable") } == 1
-        executedAndNotSkipped ":compileMainExecutableMainC", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/assembler/build/install/mainExecutable").exec().out == "5 + 7 = 12\n"
-    }
-
-    def "c"() {
-        given:
-        sample c
-        
-        when:
-        run "installMainExecutable"
-        
-        then:
-        executedAndNotSkipped ":compileHelloSharedLibraryHelloC", ":linkHelloSharedLibrary", ":helloSharedLibrary",
-                              ":compileMainExecutableMainC", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/c/build/install/mainExecutable").exec().out == "Hello world!"
-    }
-
-    def "cpp"() {
-        given:
-        sample cpp
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileHelloSharedLibraryHelloCpp", ":linkHelloSharedLibrary", ":helloSharedLibrary",
-                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/cpp/build/install/mainExecutable").exec().out == "Hello world!\n"
-    }
-
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "objectiveC"() {
-        given:
-        sample objectiveC
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainObjc", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        executable("native-binaries/objective-c/build/binaries/mainExecutable/main").exec().out == "Hello world!\n"
-    }
-
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "objectiveCpp"() {
-        given:
-        sample objectiveCpp
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainObjcpp", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        executable("native-binaries/objective-cpp/build/binaries/mainExecutable/main").exec().out == "Hello world!\n"
-    }
-
-    def "exe"() {
-        given:
-        // Need to PATH to be set to find the 'strip' executable
-        toolChain.initialiseEnvironment()
-
-        and:
-        sample cppExe
-
-        when:
-        run "installMain"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainCpp", ":linkMainExecutable", ":stripMainExecutable", ":mainExecutable"
-
-        and:
-        executable("native-binaries/cpp-exe/build/binaries/mainExecutable/main").exec().out == "Hello, World!\n"
-        installation("native-binaries/cpp-exe/build/install/mainExecutable").exec().out == "Hello, World!\n"
-
-        cleanup:
-        toolChain.resetEnvironment()
-    }
-
-    def "lib"() {
-        given:
-        sample cppLib
-
-        when:
-        run "mainSharedLibrary"
-
-        then:
-        executedAndNotSkipped ":compileMainSharedLibraryMainCpp", ":linkMainSharedLibrary", ":mainSharedLibrary"
-
-        and:
-        sharedLibrary("native-binaries/cpp-lib/build/binaries/mainSharedLibrary/main").assertExists()
-
-        when:
-        sample cppLib
-        run "mainStaticLibrary"
-
-        then:
-        executedAndNotSkipped ":compileMainStaticLibraryMainCpp", ":createMainStaticLibrary", ":mainStaticLibrary"
-
-        and:
-        staticLibrary("native-binaries/cpp-lib/build/binaries/mainStaticLibrary/main").assertExists()
-    }
-
-
-    @RequiresInstalledToolChain(VisualCpp)
-    def "win rc"() {
-        given:
-        sample windowsResources
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileHelloSharedLibraryHelloCpp", ":compileHelloSharedLibraryHelloRc",
-                              ":linkHelloSharedLibrary", ":helloSharedLibrary",
-                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/windows-resources/build/install/mainExecutable").exec().out == "Hello world!\n"
-
-        when:
-        executer.usingBuildScript(windowsResources.dir.file('build-resource-only-dll.gradle'))
-        run "helloResSharedLibrary"
-
-        then:
-        file("native-binaries/windows-resources/build/binaries/helloResSharedLibrary/helloRes.dll").assertExists()
-    }
-
-    def "custom layout"() {
-        given:
-        sample customLayout
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileHelloStaticLibraryHelloC", ":createHelloStaticLibrary", ":helloStaticLibrary",
-                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/custom-layout/build/install/mainExecutable").exec().out == "Hello world!"
-    }
-
-    def flavors() {
-        when:
-        sample flavors
-        run "installEnglishMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileEnglishHelloSharedLibraryLibCpp", ":linkEnglishHelloSharedLibrary", ":englishHelloSharedLibrary"
-        executedAndNotSkipped ":compileEnglishMainExecutableExeCpp", ":linkEnglishMainExecutable", ":englishMainExecutable"
-
-        and:
-        executable("native-binaries/flavors/build/binaries/mainExecutable/english/main").assertExists()
-        sharedLibrary("native-binaries/flavors/build/binaries/helloSharedLibrary/english/hello").assertExists()
-
-        and:
-        installation("native-binaries/flavors/build/install/mainExecutable/english").exec().out == "Hello world!\n"
-
-        when:
-        sample flavors
-        run "installFrenchMainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileFrenchHelloSharedLibraryLibCpp", ":linkFrenchHelloSharedLibrary", ":frenchHelloSharedLibrary"
-        executedAndNotSkipped ":compileFrenchMainExecutableExeCpp", ":linkFrenchMainExecutable", ":frenchMainExecutable"
-
-        and:
-        executable("native-binaries/flavors/build/binaries/mainExecutable/french/main").assertExists()
-        sharedLibrary("native-binaries/flavors/build/binaries/helloSharedLibrary/french/hello").assertExists()
-
-        and:
-        installation("native-binaries/flavors/build/install/mainExecutable/french").exec().out == "Bonjour monde!\n"
-    }
-
-    def variants() {
-        when:
-        sample variants
-        run "buildExecutables"
-
-        then:
-        final debugX86 = executable("native-binaries/variants/build/binaries/mainExecutable/x86Debug/main")
-        final releaseX86 = executable("native-binaries/variants/build/binaries/mainExecutable/x86Release/main")
-        final debugX64 = executable("native-binaries/variants/build/binaries/mainExecutable/x64Debug/main")
-        final releaseX64 = executable("native-binaries/variants/build/binaries/mainExecutable/x64Release/main")
-        final debugIA64 = executable("native-binaries/variants/build/binaries/mainExecutable/itaniumDebug/main")
-        final releaseIA64 = executable("native-binaries/variants/build/binaries/mainExecutable/itaniumRelease/main")
-
-        debugX86.binaryInfo.arch.name == "x86"
-        debugX86.assertDebugFileExists()
-        debugX86.exec().out == "Hello world!\n"
-
-        releaseX86.binaryInfo.arch.name == "x86"
-        releaseX86.assertDebugFileDoesNotExist()
-        releaseX86.exec().out == "Hello world!\n"
-
-        // x86_64 binaries not supported on MinGW or cygwin
-        if (toolChain.id == "mingw" || toolChain.id == "gcccygwin") {
-            debugX64.assertDoesNotExist()
-            releaseX64.assertDoesNotExist()
-        } else {
-            debugX64.binaryInfo.arch.name == "x86_64"
-            releaseX64.binaryInfo.arch.name == "x86_64"
-        }
-
-        // Itanium not built
-        debugIA64.assertDoesNotExist()
-        releaseIA64.assertDoesNotExist()
-    }
-
-    def "tool chains"() {
-        given:
-        sample toolChains
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        executable("native-binaries/tool-chains/build/binaries/mainExecutable/main").exec().out == "Hello from ${toolChain.typeDisplayName}!\n"
-    }
-
-    def multiProject() {
-        given:
-        sample multiProject
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        ":exe:mainExecutable" in executedTasks
-
-        and:
-        sharedLibrary("native-binaries/multi-project/lib/build/binaries/mainSharedLibrary/main").assertExists()
-        executable("native-binaries/multi-project/exe/build/binaries/mainExecutable/main").assertExists()
-        installation("native-binaries/multi-project/exe/build/install/mainExecutable").exec().out == "Hello, World!\n"
-    }
-
-    def "visual studio"() {
-        given:
-        sample visualStudio
-
-        when:
-        run "mainVisualStudio"
-
-        then:
-        final solutionFile = new SolutionFile(visualStudio.dir.file("vs/mainExe.sln"))
-        solutionFile.assertHasProjects("mainExe", "helloDll")
-        solutionFile.content.contains "GlobalSection(SolutionNotes) = postSolution"
-        solutionFile.content.contains "Text2 = The projects in this solution are [mainExe, helloDll]."
-
-        final projectFile = new ProjectFile(visualStudio.dir.file("vs/helloDll.vcxproj"))
-        projectFile.projectXml.PropertyGroup.find({it.'@Label' == 'Custom'}).ProjectDetails[0].text() == "Project is named helloDll"
-    }
-
-    def prebuilt() {
-        given:
-        inDirectory(prebuilt.dir.file("3rd-party-lib/util"))
-        run "buildLibraries"
-
-        and:
-        sample prebuilt
-
-        when:
-        succeeds "buildExecutables"
-
-        then:
-
-        executable(prebuilt.dir.file("build/binaries/mainExecutable/debug/main")).exec().out ==
-"""Built with Boost version: 1_55
-Util build type: DEBUG
-"""
-        executable(prebuilt.dir.file("build/binaries/mainExecutable/release/main")).exec().out ==
-"""Built with Boost version: 1_55
-Util build type: RELEASE
-"""
-    }
-
-    def "idl"() {
-        given:
-        sample idl
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        executedAndNotSkipped ":idl", ":compileMainExecutableMainC", ":compileMainExecutableMainIdlOutput",
-                              ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        installation("native-binaries/idl/build/install/mainExecutable").exec().out == "Hello from generated source!!\n"
-    }
-
-    def "cunit"() {
-        given:
-        // CUnit prebuilt library only works for VS2010 on windows
-        if (OperatingSystem.current().windows && !isVisualCpp2010()) {
-            return
-        }
-
-        when:
-        sample cunit
-        succeeds "runPassing"
-
-        then:
-        executedAndNotSkipped ":operatorsTestCUnitLauncher",
-                              ":compilePassingOperatorsTestCUnitExeOperatorsTestCunit", ":compilePassingOperatorsTestCUnitExeOperatorsTestCunitLauncher",
-                              ":linkPassingOperatorsTestCUnitExe", ":passingOperatorsTestCUnitExe",
-                              ":installPassingOperatorsTestCUnitExe", ":runPassingOperatorsTestCUnitExe"
-
-        and:
-        def passingResults = new CUnitTestResults(file("native-binaries/cunit/build/test-results/operatorsTestCUnitExe/passing/CUnitAutomated-Results.xml"))
-        passingResults.suiteNames == ['operator tests']
-        passingResults.suites['operator tests'].passingTests == ['test_plus', 'test_minus']
-        passingResults.suites['operator tests'].failingTests == []
-        passingResults.checkTestCases(2, 2, 0)
-        passingResults.checkAssertions(6, 6, 0)
-
-        when:
-        sample cunit
-        fails "runFailing"
-
-        then:
-        skipped ":operatorsTestCUnitLauncher"
-        executedAndNotSkipped ":compileFailingOperatorsTestCUnitExeOperatorsTestCunit", ":compileFailingOperatorsTestCUnitExeOperatorsTestCunitLauncher",
-                              ":linkFailingOperatorsTestCUnitExe", ":failingOperatorsTestCUnitExe",
-                              ":installFailingOperatorsTestCUnitExe", ":runFailingOperatorsTestCUnitExe"
-
-        and:
-        def failingResults = new CUnitTestResults(file("native-binaries/cunit/build/test-results/operatorsTestCUnitExe/failing/CUnitAutomated-Results.xml"))
-        failingResults.suiteNames == ['operator tests']
-        failingResults.suites['operator tests'].passingTests == ['test_minus']
-        failingResults.suites['operator tests'].failingTests == ['test_plus']
-        failingResults.checkTestCases(2, 1, 1)
-        failingResults.checkAssertions(6, 4, 2)
-    }
-
-    private static boolean isVisualCpp2010() {
-        return (toolChain.visualCpp && (toolChain as AvailableToolChains.InstalledVisualCpp).version.major == "10")
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/PrebuiltLibrariesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/PrebuiltLibrariesIntegrationTest.groovy
deleted file mode 100755
index 11a3075..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/PrebuiltLibrariesIntegrationTest.groovy
+++ /dev/null
@@ -1,349 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
-class PrebuiltLibrariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    final app = new CppHelloWorldApp()
-
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("libs/src/hello"))
-
-        file("libs/build.gradle") << """
-            apply plugin: 'cpp'
-            model {
-                flavors {
-                    english
-                    french
-                }
-            }
-            libraries {
-                hello {
-                    binaries.all {
-                        if (flavor == flavors.french) {
-                            cppCompiler.define "FRENCH"
-                        }
-                    }
-                }
-            }
-            task buildAll {
-                dependsOn binaries.matching {
-                    it.buildable
-                }
-            }
-"""
-    }
-
-    private void preBuildLibrary() {
-        executer.inDirectory(file("libs"))
-        run "buildAll"
-    }
-
-    def "can link to a prebuilt header-only library with api linkage"() {
-        given:
-        app.alternateLibrarySources*.writeToDir(file("src/main"))
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        hello {
-                            headers.srcDir "libs/src/hello/headers"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'api'
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.alternateLibraryOutput
-    }
-
-    def "can link to a prebuilt library with static and shared linkage"() {
-        given:
-        preBuildLibrary()
-
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        hello {
-                            headers.srcDir "libs/src/hello/headers"
-                            binaries.withType(StaticLibraryBinary) {
-                                def libName = targetPlatform.operatingSystem.windows ? 'hello.lib' : 'libhello.a'
-                                staticLibraryFile = file("libs/build/binaries/helloStaticLibrary/english/\${libName}")
-                            }
-                            binaries.withType(SharedLibraryBinary) {
-                                def os = targetPlatform.operatingSystem
-                                def baseDir = "libs/build/binaries/helloSharedLibrary/french"
-                                if (os.windows) {
-                                    // Windows uses a .dll file, and a different link file if it exists (not Cygwin or MinGW)
-                                    sharedLibraryFile = file("\${baseDir}/hello.dll")
-                                    if (file("\${baseDir}/hello.lib").exists()) {
-                                        sharedLibraryLinkFile = file("\${baseDir}/hello.lib")
-                                    }
-                                } else if (os.macOsX) {
-                                    sharedLibraryFile = file("\${baseDir}/libhello.dylib")
-                                } else {
-                                    sharedLibraryFile = file("\${baseDir}/libhello.so")
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-                mainStatic {}
-            }
-            sources {
-                main.cpp {
-                    lib library: 'hello'
-                }
-                mainStatic.cpp {
-                    source.srcDir "src/main/cpp"
-                    lib library: 'hello', linkage: 'static'
-                }
-            }
-        """
-
-        when:
-        succeeds "installMainExecutable", "installMainStaticExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.frenchOutput
-        installation("build/install/mainStaticExecutable").exec().out == app.englishOutput
-    }
-
-    def "searches all prebuilt library repositories"() {
-        given:
-        preBuildLibrary()
-
-        and:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs1(PrebuiltLibraries) {
-                        nope {
-                            headers.srcDir "not/here"
-                        }
-                    }
-                    libs2(PrebuiltLibraries) {
-                        hello {
-                            headers.srcDir "libs/src/hello/headers"
-                            binaries.withType(StaticLibraryBinary) {
-                                def libName = targetPlatform.operatingSystem.windows ? 'hello.lib' : 'libhello.a'
-                                staticLibraryFile = file("libs/build/binaries/helloStaticLibrary/french/\${libName}")
-                            }
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'static'
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("build/install/mainExecutable").exec().out == app.frenchOutput
-    }
-
-    def "locates prebuilt library in another project"() {
-        given:
-        app.executable.writeSources(file("projectA/src/main"))
-        app.librarySources*.writeToDir(file("projectA/src/main"))
-        app.libraryHeader.writeToDir(file("projectB/libs/src/hello"))
-
-        and:
-        settingsFile.text = "include ':projectA', ':projectB'"
-        buildFile << """
-            project(':projectA') {
-                apply plugin: 'cpp'
-                executables {
-                    main {}
-                }
-                sources.main.cpp.lib project: ':projectB', library: 'hello', linkage: 'api'
-            }
-            project(':projectB') {
-                apply plugin: 'cpp'
-                model {
-                    repositories {
-                        libs(PrebuiltLibraries) {
-                            hello {
-                                headers.srcDir "../libs/src/hello/headers"
-                            }
-                        }
-                    }
-                }
-            }
-        """
-
-        when:
-        succeeds "installMainExecutable"
-
-        then:
-        installation("projectA/build/install/mainExecutable").exec().out == app.englishOutput
-    }
-
-    def "produces reasonable error message when no output file is defined for binary"() {
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        hello {
-                            headers.srcDir "libs/src/hello/headers"
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'static'
-        """
-
-        when:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Static library file not set for prebuilt library 'hello'.")
-    }
-
-    def "produces reasonable error message when prebuilt library output file does not exist"() {
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        hello {
-                            headers.srcDir "libs/src/hello/headers"
-                            binaries.withType(StaticLibraryBinary) { binary ->
-                                staticLibraryFile = file("does_not_exist")
-                            }
-                        }
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'hello', linkage: 'static'
-        """
-
-        when:
-        succeeds "tasks"
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Static library file ${file("does_not_exist").absolutePath} does not exist for prebuilt library 'hello'.")
-    }
-
-    def "produces reasonable error message when prebuilt library does not exist"() {
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-            model {
-                repositories {
-                    libs(PrebuiltLibraries) {
-                        hello
-                    }
-                    libs2(PrebuiltLibraries) {
-                        hello2
-                    }
-                }
-            }
-            executables {
-                main {}
-            }
-            sources.main.cpp.lib library: 'other'
-        """
-
-        when:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Could not locate library 'other'.")
-        failure.assertHasCause("Library with name 'other' not found.")
-        failure.assertHasCause("Prebuilt library with name 'other' not found in repositories '[libs, libs2]'.")
-    }
-
-    def "produces reasonable error message when prebuilt library does not exist in a different project"() {
-        given:
-        settingsFile.text = "include ':projectA', ':projectB'"
-        buildFile << """
-            project(':projectA') {
-                apply plugin: 'cpp'
-                model {
-                    repositories {
-                        libs(PrebuiltLibraries) {
-                            hello {
-                                headers.srcDir "libs/src/hello/headers"
-                            }
-                        }
-                    }
-                }
-                executables {
-                    main {}
-                }
-                sources.main.cpp.lib project: ':projectB', library: 'hello', linkage: 'api'
-            }
-            project(':projectB') {
-                apply plugin: 'cpp'
-                model {
-                    repositories {
-                        libs(PrebuiltLibraries) {
-                            hello1
-                        }
-                        libs2(PrebuiltLibraries) {
-                            hello2
-                        }
-                    }
-                }
-            }
-        """
-
-        when:
-        fails "mainExecutable"
-
-        then:
-        failure.assertHasDescription("Could not locate library 'hello' for project ':projectB'.")
-        failure.assertHasCause("Library with name 'hello' not found.")
-        failure.assertHasCause("Prebuilt library with name 'hello' not found in repositories '[libs, libs2]'.")
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SharedLibrarySoNameIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SharedLibrarySoNameIntegrationTest.groovy
deleted file mode 100755
index 595374d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SharedLibrarySoNameIntegrationTest.groovy
+++ /dev/null
@@ -1,80 +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.nativebinaries.language.cpp
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class SharedLibrarySoNameIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-    def "setup"() {
-        settingsFile << "rootProject.name = 'test'"
-
-        def app = new CppHelloWorldApp()
-        app.library.writeSources(file("src/hello"))
-
-        buildFile << """
-            apply plugin: 'cpp'
-            libraries {
-                hello {}
-            }
-        """
-    }
-
-    def "library soname is file name when installName is not set"() {
-        when:
-        succeeds "helloSharedLibrary"
-
-        then:
-        final sharedLibrary = sharedLibrary("build/binaries/helloSharedLibrary/hello")
-        sharedLibrary.soName == sharedLibrary.file.name
-    }
-
-    def "library soname uses specified installName"() {
-        given:
-        buildFile << """
-            tasks.withType(LinkSharedLibrary) {
-                it.installName = 'hello-install-name'
-            }
-        """
-
-        when:
-        succeeds "helloSharedLibrary"
-
-        then:
-        sharedLibrary("build/binaries/helloSharedLibrary/hello").soName == "hello-install-name"
-    }
-
-    def "library soname defaults when installName is null"() {
-        given:
-        buildFile << """
-            tasks.withType(LinkSharedLibrary) {
-                it.installName = null
-            }
-        """
-
-        when:
-        succeeds "helloSharedLibrary"
-
-        then:
-        final library = sharedLibrary("build/binaries/helloSharedLibrary/hello")
-        def expectedSoName = OperatingSystem.current().macOsX ? library.file.absolutePath : null
-        library.soName == expectedSoName
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SourceSetDependenciesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SourceSetDependenciesIntegrationTest.groovy
deleted file mode 100755
index 534fcce..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/SourceSetDependenciesIntegrationTest.groovy
+++ /dev/null
@@ -1,153 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppCallingCHelloWorldApp
-
-// TODO:DAZ Test incremental
-// TODO:DAZ Test dependency on functional source set
-// TODO:DAZ Test dependency on source set that is not HeaderExportingSourceSet
-// TODO:DAZ Sad day tests
-class SourceSetDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    def "source dependency on source set of same type"() {
-        def app = new CHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/library"))
-
-        buildFile << """
-    apply plugin: 'c'
-    sources {
-        main {}
-        library {}
-    }
-    sources.main.c.lib sources.library.c
-    executables {
-        main {
-            source sources.library
-        }
-    }
-"""
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
-    }
-
-    def "source dependency on source set of headers"() {
-        def app = new CHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.sourceFiles*.writeToDir(file("src/main"))
-        app.library.headerFiles*.writeToDir(file("src/library"))
-
-        buildFile << """
-    apply plugin: 'c'
-    // library not required in executable: only headers are used
-    executables {
-        main {}
-    }
-    sources {
-        library {}
-    }
-    sources.main.c.lib sources.library.c
-"""
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
-    }
-
-    def "source dependency on source set of different type"() {
-        def app = new CppCallingCHelloWorldApp()
-        app.executable.writeSources(file("src/main"))
-        app.library.writeSources(file("src/library"))
-
-        buildFile << """
-    apply plugin: 'cpp'
-    apply plugin: 'c'
-    sources {
-        main {}
-        library {}
-    }
-    sources.main.cpp.lib sources.library.c
-    executables {
-        main {
-            source sources.library
-        }
-    }
-"""
-        when:
-        succeeds "mainExecutable"
-
-        then:
-        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
-    }
-
-    def "source files in depended-on source set are not included"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/main"))
-
-        file("src/extra/cpp/bad.cpp") << """
-    FILE WILL BE IGNORED: source set dependency set only considers headers
-"""
-
-        buildFile << """
-    apply plugin: 'cpp'
-    executables {
-        main {}
-    }
-    sources {
-        extra {}
-    }
-    sources.main.cpp.lib sources.extra.cpp
-"""
-        expect:
-        succeeds "mainExecutable"
-    }
-
-    def "binary depending on source set has no effect"() {
-        given:
-        def app = new CHelloWorldApp()
-        app.writeSources(file("src/main"))
-
-        file("src/extra/headers/hello.h") << """
-    FILE WILL BE IGNORED: source set dependency added to binary
-"""
-
-        buildFile << """
-    apply plugin: 'cpp'
-    sources {
-        extra {}
-    }
-    executables {
-        main {
-            binaries.all {
-                lib sources.extra.cpp
-            }
-        }
-    }
-"""
-        expect:
-        succeeds "mainExecutable"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/ToolChainDiscoveryIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/ToolChainDiscoveryIntegrationTest.groovy
deleted file mode 100755
index 91bbf69..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/ToolChainDiscoveryIntegrationTest.groovy
+++ /dev/null
@@ -1,89 +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.nativebinaries.language.cpp
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppCompilerDetectingTestApp
-
-/**
- * Test that each available tool chain can be discovered and used without configuration, assuming it is in the path.
- */
-class ToolChainDiscoveryIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    def helloWorldApp = new CppCompilerDetectingTestApp()
-
-    def setup() {
-        // Discard init script content generated by superclass
-        initScript.text = ""
-    }
-
-    def "can discover tool chain in environment"() {
-        given:
-        toolChain.initialiseEnvironment();
-
-        and:
-        // We explicitly apply a single tool chain plugin here, to avoid using an alternative tool chain
-        buildFile << """
-            apply plugin: CppNativeBinariesPlugin
-            apply plugin: ${toolChain.pluginClass}
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.expectedOutput(toolChain)
-
-        cleanup:
-        toolChain.resetEnvironment();
-    }
-
-    def "uses correct tool chain when explicitly configured"() {
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-
-            model {
-                toolChains {
-                    ${toolChain.buildScriptConfig}
-                }
-            }
-
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        run "mainExecutable"
-
-        then:
-        def mainExecutable = executable("build/binaries/mainExecutable/main")
-        mainExecutable.assertExists()
-        mainExecutable.exec().out == helloWorldApp.expectedOutput(toolChain)
-    }
-
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIncrementalBuildIntegrationTest.groovy
deleted file mode 100644
index 09f6e0d..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,162 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.ExecutableFixture
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.WindowsResourceHelloWorldApp
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-
- at RequiresInstalledToolChain(VisualCpp)
-class WindowsResourcesIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    HelloWorldApp helloWorldApp = new WindowsResourceHelloWorldApp()
-    ExecutableFixture mainExe
-    def mainResourceFile
-    def unusedHeaderFile
-
-    def "setup"() {
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        helloWorldApp.writeSources(file("src/main"))
-        unusedHeaderFile = file("src/main/headers/unused.h") << """
-    #define DUMMY_HEADER_FILE
-"""
-
-        run "mainExecutable"
-
-        mainExe = executable("build/binaries/mainExecutable/main")
-        mainResourceFile = file("src/main/rc/resources.rc")
-    }
-
-    def "does not re-compile sources with no change"() {
-        when:
-        run "mainExecutable"
-
-        then:
-        nonSkippedTasks.empty
-    }
-
-    def "compiles and links when resource source changes"() {
-        when:
-        file("src/main/rc/resources.rc").text = """
-#include "hello.h"
-
-STRINGTABLE
-{
-    IDS_HELLO, "Goodbye"
-}
-"""
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainRc", ":linkMainExecutable", ":mainExecutable"
-
-        and:
-        mainExe.exec().out == "Goodbye"
-    }
-
-    def "compiles and but does not link when resource source changes with comment only"() {
-        when:
-        file("src/main/rc/resources.rc") << """
-// Comment added to the end of the resource file
-"""
-
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainRc"
-        skipped ":linkMainExecutable", ":mainExecutable"
-    }
-
-    def "compiles and links when resource compiler arg changes"() {
-        when:
-        buildFile << """
-            executables {
-                main {
-                    binaries.all {
-                        // Use a compiler arg that will change the generated .res file
-                        rcCompiler.args "-DFRENCH"
-                    }
-                }
-            }
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainRc", ":linkMainExecutable", ":mainExecutable"
-    }
-
-    def "stale .res files are removed when a resource source file is renamed"() {
-        given:
-        def oldResFile = file("build/objectFiles/mainExecutable/mainRc/${hashFor(mainResourceFile)}/resources.res")
-        def newResFile = file("build/objectFiles/mainExecutable/mainRc/${hashFor(file('src/main/rc/changed_resources.rc'))}/changed_resources.res")
-        assert oldResFile.file
-        assert !newResFile.file
-
-        when:
-        mainResourceFile.renameTo(file("src/main/rc/changed_resources.rc"))
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped ":compileMainExecutableMainRc"
-
-        and:
-        !oldResFile.file
-        newResFile.file
-    }
-
-    def "recompiles resource when included header is changed"() {
-        given: "set the generated res file timestamp to zero"
-        def resourceFile = file("build/objectFiles/mainExecutable/mainRc/${hashFor(mainResourceFile)}/resources.res")
-        resourceFile.lastModified = 0
-        when: "Unused header is changed"
-        unusedHeaderFile << """
-    #define EXTRA_DEFINE
-"""
-        and:
-        run "mainExecutable"
-
-        then: "No resource compilation"
-        skipped ":compileMainExecutableMainRc"
-        resourceFile.lastModified() == 0
-
-        when:
-        file("src/main/headers/hello.h") << """
-    #define EXTRA_DEFINE
-"""
-        and:
-        run "mainExecutable"
-
-        then: "Resource is recompiled"
-        executedAndNotSkipped ":compileMainExecutableMainRc"
-        resourceFile.lastModified() > 0
-    }
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIntegrationTest.groovy
deleted file mode 100755
index df4bac6..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesIntegrationTest.groovy
+++ /dev/null
@@ -1,143 +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.nativebinaries.language.cpp
-
-import org.apache.commons.lang.RandomStringUtils
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.WindowsResourceHelloWorldApp
-import org.gradle.test.fixtures.file.TestFile
-import spock.lang.Ignore
-
-import static org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement.VisualCpp
-
- at RequiresInstalledToolChain(VisualCpp)
-class WindowsResourcesIntegrationTest extends AbstractLanguageIntegrationTest {
-
-    HelloWorldApp helloWorldApp = new WindowsResourceHelloWorldApp()
-
-    def "user receives a reasonable error message when resource compilation fails"() {
-        given:
-        buildFile << """
-             executables {
-                 main {}
-             }
-         """
-
-        and:
-        file("src/main/rc/broken.rc") << """
-        #include <stdio.h>
-
-        NOT A VALID RESOURCE
-"""
-
-        expect:
-        fails "mainExecutable"
-        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainRc'.");
-        failure.assertHasCause("Windows resource compiler failed; see the error output for details.")
-    }
-
-    def "can create resources-only shared library"() {
-        given:
-        buildFile << """
-            executables {
-                main {}
-            }
-            libraries {
-                resources {
-                    binaries.all {
-                        linker.args "/noentry", "/machine:x86"
-                    }
-                }
-            }
-            sources.main.cpp.lib libraries.resources
-"""
-
-        and:
-        file("src/resources/rc/resources.rc") << """
-            #include "resources.h"
-
-            STRINGTABLE
-            {
-                IDS_HELLO, "Hello!"
-            }
-        """
-
-        and:
-        file("src/resources/headers/resources.h") << """
-            #define IDS_HELLO    111
-        """
-
-        and:
-        file("src/main/cpp/main.cpp") << """
-            #include <iostream>
-            #include <windows.h>
-            #include <string>
-            #include "resources.h"
-
-            std::string LoadStringFromResource(UINT stringID)
-            {
-                HMODULE instance = LoadLibraryEx("resources.dll", NULL, LOAD_LIBRARY_AS_DATAFILE);
-                WCHAR * pBuf = NULL;
-                int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
-                std::wstring wide = std::wstring(pBuf, len);
-                return std::string(wide.begin(), wide.end());
-            }
-
-            int main() {
-                std::string hello = LoadStringFromResource(IDS_HELLO);
-                std::cout << hello;
-                return 0;
-            }
-        """
-
-        when:
-        run "installMainExecutable"
-
-        then:
-        resourceOnlyLibrary("build/binaries/resourcesSharedLibrary/resources").assertExists()
-        installation("build/install/mainExecutable").exec().out == "Hello!"
-    }
-
-    @Ignore
-    def "windows resources compiler can use long file paths"() {
-        // windows can't handle a path up to 260 characters
-        // we create a project path that is ~180 characters to end up
-        // with a path for the compiled resources.res > 260 chars
-        def projectPathOffset = 180 - testDirectory.getAbsolutePath().length()
-        def nestedProjectPath = RandomStringUtils.randomAlphanumeric(projectPathOffset-10) + "/123456789"
-
-        setup:
-        def deepNestedProjectFolder = file(nestedProjectPath)
-        executer.usingProjectDirectory(deepNestedProjectFolder)
-        def TestFile buildFile = deepNestedProjectFolder.file("build.gradle")
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("$nestedProjectPath/src/main"));
-
-        expect:
-        // this test is just for verifying explicitly the behaviour of the windows resource compiler
-        // that's why we explicitly trigger this task instead of main.
-        succeeds "mainExecutable"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesUnsupportedIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesUnsupportedIntegrationTest.groovy
deleted file mode 100755
index 616daa7..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/WindowsResourcesUnsupportedIntegrationTest.groovy
+++ /dev/null
@@ -1,55 +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.nativebinaries.language.cpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.app.CppHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class WindowsResourcesUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
-
-    HelloWorldApp helloWorldApp = new CppHelloWorldApp()
-
-    def "resource files are ignored on unsupported platforms"() {
-        given:
-        buildFile << """
-            apply plugin: 'cpp'
-            apply plugin: 'windows-resources'
-
-            executables {
-                main {}
-            }
-         """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-        file("src/main/rc/broken.rc") << """
-        #include <stdio.h>
-
-        NOT A VALID RESOURCE
-"""
-
-        when:
-        run "mainExecutable"
-
-        then:
-        !executedTasks.contains(":compileMainExecutableMainRc")
-    }
-}
-
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
deleted file mode 100755
index 34a1d33..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
+++ /dev/null
@@ -1,81 +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.nativebinaries.language.cpp.fixtures
-
-import org.apache.commons.io.FilenameUtils
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.internal.hash.HashUtil
-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
-    File initScript
-
-    def setup() {
-        initScript = file("init.gradle") << """
-allprojects {
-    apply plugin: ${toolChain.pluginClass}
-    model {
-        toolChains {
-            ${toolChain.buildScriptConfig}
-        }
-    }
-}
-"""
-        executer.beforeExecute({
-            usingInitScript(initScript)
-        })
-    }
-
-    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) {
-        return toolChain.objectFile(file(path))
-    }
-
-    def SharedLibraryFixture sharedLibrary(Object path) {
-        return toolChain.sharedLibrary(file(path))
-    }
-
-    def StaticLibraryFixture staticLibrary(Object path) {
-        return toolChain.staticLibrary(file(path))
-    }
-
-    def NativeBinaryFixture resourceOnlyLibrary(Object path) {
-        return toolChain.resourceOnlyLibrary(file(path))
-    }
-
-    def objectFileFor(TestFile sourceFile, String rootObjectFilesDir = "build/objectFiles/mainExecutable/main${sourceType}") {
-        final baseName = FilenameUtils.removeExtension(sourceFile.name)
-        String compactMD5 = HashUtil.createCompactMD5(sourceFile.getAbsolutePath());
-        return objectFile("$rootObjectFilesDir/$compactMD5/${baseName}")
-    }
-
-    String hashFor(File inputFile){
-        HashUtil.createCompactMD5(inputFile.getAbsolutePath());
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SingleToolChainTestRunner.java b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SingleToolChainTestRunner.java
deleted file mode 100755
index dd626d4..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SingleToolChainTestRunner.java
+++ /dev/null
@@ -1,77 +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.nativebinaries.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();
-        boolean someToolChainAvailable = false;
-        for (AvailableToolChains.ToolChainCandidate toolChain : toolChains) {
-            boolean enabled = enableAllToolChains || (toolChain.isAvailable() && !someToolChainAvailable);
-            someToolChainAvailable |= enabled;
-            add(new ToolChainExecution(toolChain, 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) {
-            if (enabled) {
-                RequiresInstalledToolChain toolChainRestriction = testDetails.getAnnotation(RequiresInstalledToolChain.class);
-                return toolChainRestriction == null
-                        || toolChain.meets(toolChainRestriction.value());
-            }
-            return false;
-        }
-
-        @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()));
-            AbstractInstalledToolChainIntegrationSpec.setToolChain((AvailableToolChains.InstalledToolChain) toolChain);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPluginIntegrationTest.groovy
deleted file mode 100644
index 3758071..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,22 +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.nativebinaries.language.cpp.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class CppPluginIntegrationTest extends WellBehavedPluginTest {
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/MixedObjectiveCIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/MixedObjectiveCIntegrationTest.groovy
deleted file mode 100644
index 5d84e68..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/MixedObjectiveCIntegrationTest.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivec
-
-import org.gradle.nativebinaries.language.cpp.AbstractLanguageIntegrationTest
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.MixedObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-//TODO find a better name
- at Requires(TestPrecondition.NOT_WINDOWS)
-class MixedObjectiveCIntegrationTest extends AbstractLanguageIntegrationTest{
-
-    @Override
-    HelloWorldApp getHelloWorldApp() {
-        return new MixedObjectiveCHelloWorldApp()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100644
index 6f986dd..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,86 +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.nativebinaries.language.objectivec
-
-import org.gradle.internal.hash.HashUtil
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.AbstractLanguageIncrementalBuildIntegrationTest
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Ignore
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCLanguageIncrementalBuildIntegrationTest extends AbstractLanguageIncrementalBuildIntegrationTest{
-
-    def setupSpec(){
-        multiPlatformsAvailable = OperatingSystem.current().isMacOsX();
-    }
-
-    @Ignore("Demos a problem with clang on ubuntu creating randomly differerent object files")
-    def "generates always exactly same object file"() {
-        setup:
-        def recordings = []
-        def invocation =  10
-        when:
-        invocation.times{
-            run "cleanCompileHelloSharedLibraryHello$sourceType", "compileHelloSharedLibraryHello$sourceType"
-            def oldHash= HashUtil.sha1(objectFileFor(librarySourceFiles[0], "build/objectFiles/helloSharedLibrary/hello$sourceType")).asCompactString()
-
-            //to ensure it's not a timestamp issue
-            sleep(1000)
-            run "cleanCompileHelloSharedLibraryHello$sourceType", "compileHelloSharedLibraryHello$sourceType"
-            def newHash = HashUtil.sha1(objectFileFor(librarySourceFiles[0], "build/objectFiles/helloSharedLibrary/hello$sourceType")).asCompactString()
-            recordings << (oldHash == newHash)
-        }
-        then:
-        recordings.findAll{ it }.size() != 0 // not everytime the .o file differs -> not a timestamp issue
-        recordings.findAll{ it }.size() == invocation
-    }
-
-    def "recompiles binary when imported header file changes"() {
-        sourceFile.text = sourceFile.text.replaceFirst('#include "hello.h"', "#import \"hello.h\"")
-
-        given:
-        run "installMainExecutable"
-
-
-        when:
-        headerFile << """
-            int unused();
-"""
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped libraryCompileTask
-        executedAndNotSkipped mainCompileTask
-
-        if(objectiveCWithAslr()){
-            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            executed ":linkMainExecutable", ":mainExecutable"
-        } else {
-            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
-            skipped ":linkMainExecutable", ":mainExecutable"
-        }
-    }
-
-    @Override
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCHelloWorldApp()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
deleted file mode 100644
index cea1480..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivec
-
-import org.gradle.nativebinaries.language.cpp.AbstractLanguageIncrementalCompileIntegrationTest
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCLanguageIncrementalCompileIntegrationTest extends AbstractLanguageIncrementalCompileIntegrationTest {
-    @Override
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCHelloWorldApp()
-    }
-
-    def "recompiles only source file that imported changed header file"() {
-        given:
-        sourceFile << """
-            #import "${otherHeaderFile.name}"
-"""
-        and:
-        initialCompile()
-
-        when:
-        otherHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "source is always recompiled if it imported header via macro"() {
-        given:
-        sourceFile << """
-            #define MY_HEADER "${otherHeaderFile.name}"
-            #import MY_HEADER
-"""
-
-        and:
-        initialCompile()
-
-        when:
-        otherHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-
-        // TODO:DAZ Remove this behaviour
-        when: "Header that is NOT included is changed"
-        file("src/main/headers/notIncluded.h") << """
-            // Dummy header file
-"""
-        and:
-        run "mainExecutable"
-
-        then: "Source is still recompiled"
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-
-    def "recompiles source file when transitively imported header file is changed"() {
-        given:
-        def transitiveHeaderFile = file("src/main/headers/transitive.h") << """
-           // Dummy header file
-"""
-        otherHeaderFile << """
-            #import "${transitiveHeaderFile.name}"
-"""
-        sourceFile << """
-            #import "${otherHeaderFile.name}"
-"""
-
-        and:
-        initialCompile()
-
-        when:
-        transitiveHeaderFile << """
-            // Some extra content
-"""
-        and:
-        run "mainExecutable"
-
-        then:
-        executedAndNotSkipped compileTask
-
-        and:
-        recompiled sourceFile
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
deleted file mode 100644
index ad4eb51..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,32 +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.nativebinaries.language.objectivec
-
-import org.gradle.nativebinaries.language.cpp.AbstractLanguageIntegrationTest
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCLanguageIntegrationTest extends AbstractLanguageIntegrationTest{
-
-    @Override
-    HelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCHelloWorldApp()
-    }
-
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy
deleted file mode 100644
index 108d6d6..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivec
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCHelloWorldApp
-
-import static org.hamcrest.CoreMatchers.containsString
-
- at RequiresInstalledToolChain(ToolChainRequirement.VisualCpp)
-class ObjectiveCUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec{
-
-    def helloWorldApp = new ObjectiveCHelloWorldApp();
-
-    def "setup"() {
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-    }
-
-    def "fails with decent error message with visual studio toolchain"() {
-        given:
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        fails "compileMainExecutableMainObjc"
-
-        then:
-        failure.assertThatCause(containsString("Objective-C is not available on the Visual C++ toolchain"))
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.groovy
deleted file mode 100644
index 59c1c09..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.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.nativebinaries.language.objectivec.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class ObjectiveCPluginIntegrationTest extends WellBehavedPluginTest {
-    @Override
-    String getPluginId() {
-        "objective-c"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
deleted file mode 100644
index 6fd838e..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivecpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.nativebinaries.language.objectivec.ObjectiveCLanguageIncrementalBuildIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCppLanguageIncrementalBuildIntegrationTest  extends ObjectiveCLanguageIncrementalBuildIntegrationTest{
-    @Override
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCppHelloWorldApp()
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
deleted file mode 100644
index 53c2228..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivecpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.nativebinaries.language.objectivec.ObjectiveCLanguageIncrementalCompileIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCppLanguageIncrementalCompileIntegrationTest extends ObjectiveCLanguageIncrementalCompileIntegrationTest {
-    @Override
-    IncrementalHelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCppHelloWorldApp()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
deleted file mode 100644
index 98fdf85..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
+++ /dev/null
@@ -1,32 +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.nativebinaries.language.objectivecpp
-
-import org.gradle.nativebinaries.language.cpp.AbstractLanguageIntegrationTest
-import org.gradle.nativebinaries.language.cpp.fixtures.app.HelloWorldApp
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class ObjectiveCppLanguageIntegrationTest extends AbstractLanguageIntegrationTest {
-
-    @Override
-    HelloWorldApp getHelloWorldApp() {
-        return new ObjectiveCppHelloWorldApp()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy
deleted file mode 100644
index 440927c..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.objectivecpp
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.nativebinaries.language.cpp.fixtures.RequiresInstalledToolChain
-import org.gradle.nativebinaries.language.cpp.fixtures.ToolChainRequirement
-import org.gradle.nativebinaries.language.cpp.fixtures.app.ObjectiveCppHelloWorldApp
-
-import static org.hamcrest.CoreMatchers.containsString
-
-
- at RequiresInstalledToolChain(ToolChainRequirement.VisualCpp)
-class ObjectiveCppUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec{
-
-    def helloWorldApp = new ObjectiveCppHelloWorldApp();
-
-    def "setup"() {
-        buildFile << helloWorldApp.pluginScript
-        buildFile << helloWorldApp.extraConfiguration
-    }
-
-    def "fails with decent error message with visual studio toolchain"() {
-        given:
-        buildFile << """
-            executables {
-                main {}
-            }
-        """
-
-        and:
-        helloWorldApp.writeSources(file("src/main"))
-
-        when:
-        fails "compileMainExecutableMainObjcpp"
-
-        then:
-        failure.assertThatCause(containsString("Objective-C++ is not available on the Visual C++ toolchain"))
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.groovy
deleted file mode 100644
index cbbe5f8..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.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.nativebinaries.language.objectivecpp.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class ObjectiveCppPluginIntegrationTest extends WellBehavedPluginTest {
-    @Override
-    String getPluginId() {
-        "objective-cpp"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPluginIntegrationTest.groovy
deleted file mode 100644
index 5bc3a03..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPluginIntegrationTest.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.nativebinaries.language.rc.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class WindowsResourcesPluginIntegrationTest extends WellBehavedPluginTest {
-    @Override
-    String getPluginId() {
-        "windows-resources"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginIntegrationTest.groovy
deleted file mode 100644
index 58fd599..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginIntegrationTest.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.nativebinaries.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class NativeBinariesPluginIntegrationTest extends WellBehavedPluginTest {
-    @Override
-    String getPluginId() {
-        "native-binaries"
-    }
-}
diff --git a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPluginIntegrationTest.groovy b/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPluginIntegrationTest.groovy
deleted file mode 100644
index cc47aa7..0000000
--- a/subprojects/cpp/src/integTest/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,22 +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.nativebinaries.test.cunit.plugins
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class CUnitPluginIntegrationTest extends WellBehavedPluginTest {
-}
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/linux/libcunit.a b/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/linux/libcunit.a
deleted file mode 100644
index 8cc73ef..0000000
Binary files a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/linux/libcunit.a and /dev/null differ
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/osx/libcunit.a b/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/osx/libcunit.a
deleted file mode 100644
index 16a84bb..0000000
Binary files a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/osx/libcunit.a and /dev/null differ
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy
deleted file mode 100644
index cccb6ab..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.ide.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.ide.cdt.model.ProjectSettings
-import org.gradle.ide.cdt.model.ProjectDescriptor
-import org.gradle.ide.cdt.model.CprojectSettings
-import org.gradle.ide.cdt.model.CprojectDescriptor
-
-import org.gradle.ide.cdt.tasks.GenerateMetadataFileTask
-
- at Incubating
-class CdtIdePlugin implements Plugin<Project> {
-
-    void apply(Project project) {
-        project.apply(plugin: "native-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/ide/cdt/model/CprojectDescriptor.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/CprojectDescriptor.groovy
deleted file mode 100644
index ce8903b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/CprojectDescriptor.groovy
+++ /dev/null
@@ -1,117 +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.ide.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/ide/cdt/model/CprojectSettings.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/CprojectSettings.groovy
deleted file mode 100644
index 625a2fc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/CprojectSettings.groovy
+++ /dev/null
@@ -1,103 +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.ide.cdt.model
-
-import org.gradle.api.Incubating
-import org.gradle.api.file.ConfigurableFileCollection
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.nativebinaries.*
-
-/**
- * Exposes a more logical view of the actual .cproject descriptor file
- */
- at Incubating
-// TODO:DAZ I'm sure this is now broken
-class CprojectSettings {
-
-    ProjectNativeComponent binary
-    private final ConfigurableFileCollection includeRoots
-    private final ConfigurableFileCollection libs
-
-    CprojectSettings(ProjectNativeComponent 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/ide/cdt/model/ProjectDescriptor.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/ProjectDescriptor.groovy
deleted file mode 100644
index af499bc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/ProjectDescriptor.groovy
+++ /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.ide.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/ide/visualstudio/VisualStudioExtension.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioExtension.java
deleted file mode 100644
index 924869d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioExtension.java
+++ /dev/null
@@ -1,36 +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.ide.visualstudio;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectSet;
-
-/**
- * The configuration for mapping a set of {@link org.gradle.nativebinaries.ProjectNativeComponent}s to a Visual Studio project.
- */
- at Incubating
-public interface VisualStudioExtension {
-    /**
-     * The {@link VisualStudioProject}s generated.
-     */
-    NamedDomainObjectSet<? extends VisualStudioProject> getProjects();
-
-    /**
-     * The {@link VisualStudioSolution}s generated.
-     */
-    NamedDomainObjectSet<? extends VisualStudioSolution> getSolutions();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.java
deleted file mode 100644
index 22c1726..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.java
+++ /dev/null
@@ -1,62 +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.ide.visualstudio;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.language.base.BuildableModelElement;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-/**
- * A visual studio project, created from one or more {@link org.gradle.nativebinaries.NativeBinary} instances.
- *
- * <p/>
- *
- * The content and location of the generate project file can be modified by the supplied methods:
- *
- * <pre autoTested="true">
- *  apply plugin: "visual-studio"
- *  model {
- *      visualStudio {
- *          projects.all {
- *              projectFile.location = "vs/${name}.vcxproj"
- *              projectFile.withXml {
- *                  asNode().appendNode('PropertyGroup', [Label: 'Custom'])
- *                          .appendNode('ProjectDetails', "Project is named ${project.name}")
- *              }
- *          }
- *      }
- *  }
- * </pre>
- */
- at Incubating
-public interface VisualStudioProject extends Named, BuildableModelElement {
-    /**
-     * The component that this project represents.
-     */
-    ProjectNativeComponent getComponent();
-
-    /**
-     * Configuration for the generated project file.
-     */
-    XmlConfigFile getProjectFile();
-
-    /**
-     * Configuration for the generated filters file.
-     */
-    XmlConfigFile getFiltersFile();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.java
deleted file mode 100644
index 9c62135..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.java
+++ /dev/null
@@ -1,64 +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.ide.visualstudio;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.language.base.BuildableModelElement;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-import java.util.Set;
-
-/**
- * A visual studio solution, representing one or more {@link org.gradle.nativebinaries.ProjectNativeBinary} instances
- * from the same {@link ProjectNativeComponent}.
- * <p/>
- *
- * The content and location of the generate solution file can be modified by the supplied methods:
- *
- * <pre autoTested="true">
- *  apply plugin: "visual-studio"
- *  model {
- *      visualStudio {
- *          solutions.all {
- *              solutionFile.location = "vs/${name}.sln"
- *              solutionFile.withContent { TextProvider content ->
- *                  content.asBuilder().insert(0, "# GENERATED FILE: DO NOT EDIT\n")
- *                  content.text = content.text.replaceAll("HideSolutionNode = FALSE", "HideSolutionNode = TRUE")
- *              }
- *          }
- *      }
- *  }
- * </pre>
- */
- at Incubating
-public interface VisualStudioSolution extends Named, BuildableModelElement {
-    /**
-     * The set of projects included in this solution.
-     */
-    Set<VisualStudioProject> getProjects();
-
-    /**
-     * The component that this solution represents.
-     */
-    ProjectNativeComponent getComponent();
-
-    /**
-     * Configuration for the generated solution file.
-     */
-    TextConfigFile getSolutionFile();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.java
deleted file mode 100644
index 3a99def..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.java
+++ /dev/null
@@ -1,52 +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.ide.visualstudio.internal;
-
-import org.gradle.api.NamedDomainObjectSet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.ide.visualstudio.VisualStudioExtension;
-import org.gradle.ide.visualstudio.VisualStudioProject;
-import org.gradle.ide.visualstudio.VisualStudioSolution;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.internal.resolve.ProjectLocator;
-
-public class DefaultVisualStudioExtension implements VisualStudioExtension {
-    private final VisualStudioProjectRegistry projectRegistry;
-    private final VisualStudioSolutionRegistry solutionRegistry;
-
-    public DefaultVisualStudioExtension(Instantiator instantiator, ProjectLocator projectLocator, FileResolver fileResolver) {
-        VisualStudioProjectMapper projectMapper = new VisualStudioProjectMapper();
-        projectRegistry = new VisualStudioProjectRegistry(fileResolver, projectMapper, instantiator);
-        VisualStudioProjectResolver projectResolver = new VisualStudioProjectResolver(projectLocator);
-        solutionRegistry = new VisualStudioSolutionRegistry(fileResolver, projectResolver, instantiator);
-    }
-
-    public NamedDomainObjectSet<? extends VisualStudioProject> getProjects() {
-        return projectRegistry;
-    }
-
-    public VisualStudioProjectRegistry getProjectRegistry() {
-        return projectRegistry;
-    }
-
-    public NamedDomainObjectSet<? extends VisualStudioSolution> getSolutions() {
-        return solutionRegistry;
-    }
-
-    public VisualStudioSolutionRegistry getSolutionRegistry() {
-        return solutionRegistry;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy
deleted file mode 100644
index 929e256..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy
+++ /dev/null
@@ -1,154 +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.ide.visualstudio.internal
-import org.gradle.api.Action
-import org.gradle.api.XmlProvider
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.ide.visualstudio.VisualStudioProject
-import org.gradle.ide.visualstudio.XmlConfigFile
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.AbstractBuildableModelElement
-import org.gradle.language.rc.WindowsResourceSet
-import org.gradle.nativebinaries.NativeBinary
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.ProjectNativeComponent
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import org.gradle.util.CollectionUtils
-/**
- * A VisualStudio project represents a set of binaries for a component that may vary in build type and target platform.
- */
-class DefaultVisualStudioProject extends AbstractBuildableModelElement implements VisualStudioProject {
-    private final String name
-    private final DefaultConfigFile projectFile
-    private final DefaultConfigFile filtersFile
-    private final ProjectNativeComponent component
-    private final List<File> additionalFiles = []
-    final Set<LanguageSourceSet> sources = new LinkedHashSet<LanguageSourceSet>()
-    private final Map<NativeBinary, VisualStudioProjectConfiguration> configurations = [:]
-
-    DefaultVisualStudioProject(String name, ProjectNativeComponent component, FileResolver fileResolver, Instantiator instantiator) {
-        this.name = name
-        this.component = component
-        projectFile = instantiator.newInstance(DefaultConfigFile, fileResolver, "${name}.vcxproj" as String)
-        filtersFile = instantiator.newInstance(DefaultConfigFile, fileResolver, "${name}.vcxproj.filters" as String)
-    }
-
-    String getName() {
-        return name
-    }
-
-    DefaultConfigFile getProjectFile() {
-        return projectFile
-    }
-
-    DefaultConfigFile getFiltersFile() {
-        return filtersFile
-    }
-
-    ProjectNativeComponent getComponent() {
-        return component
-    }
-
-    void addSourceFile(File sourceFile) {
-        additionalFiles << sourceFile
-    }
-
-    String getUuid() {
-        String projectPath = (component as ProjectNativeComponentInternal).projectPath
-        String vsComponentPath = "${projectPath}:${name}"
-        return '{' + UUID.nameUUIDFromBytes(vsComponentPath.bytes).toString().toUpperCase() + '}'
-    }
-
-    void source(Collection<LanguageSourceSet> sources) {
-        this.sources.addAll(sources)
-        builtBy(sources)
-    }
-
-    List<File> getSourceFiles() {
-        def allSource = [] as Set
-        allSource.addAll additionalFiles
-        sources.each { LanguageSourceSet sourceSet ->
-            if (!(sourceSet instanceof WindowsResourceSet)) {
-                allSource.addAll sourceSet.source.files
-            }
-        }
-        return allSource as List
-    }
-
-    List<File> getResourceFiles() {
-        def allResources = [] as Set
-        sources.each { LanguageSourceSet sourceSet ->
-            if (sourceSet instanceof WindowsResourceSet) {
-                allResources.addAll sourceSet.source.files
-            }
-        }
-        return allResources as List
-    }
-
-    List<File> getHeaderFiles() {
-        def allHeaders = [] as Set
-        sources.each { LanguageSourceSet sourceSet ->
-            if (sourceSet instanceof HeaderExportingSourceSet) {
-                allHeaders.addAll sourceSet.exportedHeaders.files
-                allHeaders.addAll sourceSet.implicitHeaders.files
-            }
-        }
-        return allHeaders as List
-    }
-
-    List<VisualStudioProjectConfiguration> getConfigurations() {
-        return CollectionUtils.toList(configurations.values())
-    }
-
-    void addConfiguration(ProjectNativeBinary nativeBinary, VisualStudioProjectConfiguration configuration) {
-        configurations[nativeBinary] = configuration
-        source nativeBinary.source
-    }
-
-    VisualStudioProjectConfiguration getConfiguration(ProjectNativeBinary nativeBinary) {
-        return configurations[nativeBinary]
-    }
-
-    public static class DefaultConfigFile implements XmlConfigFile {
-        private final List<Action<? super XmlProvider>> actions = new ArrayList<Action<? super XmlProvider>>();
-        private final FileResolver fileResolver
-        private Object location
-
-        DefaultConfigFile(FileResolver fileResolver, String defaultLocation) {
-            this.fileResolver = fileResolver
-            this.location = defaultLocation
-        }
-
-        File getLocation() {
-            return fileResolver.resolve(location)
-        }
-
-        void setLocation(Object location) {
-            this.location = location
-        }
-
-        void withXml(Action<? super XmlProvider> action) {
-            actions.add(action)
-        }
-
-        List<Action<? super XmlProvider>> getXmlActions() {
-            return actions
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.groovy
deleted file mode 100644
index 634c497..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.groovy
+++ /dev/null
@@ -1,114 +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.ide.visualstudio.internal
-import org.gradle.api.Action
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.ide.visualstudio.TextConfigFile
-import org.gradle.ide.visualstudio.TextProvider
-import org.gradle.ide.visualstudio.VisualStudioProject
-import org.gradle.ide.visualstudio.VisualStudioSolution
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.language.base.internal.AbstractBuildableModelElement
-import org.gradle.nativebinaries.LibraryBinary
-import org.gradle.nativebinaries.ProjectNativeComponent
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-
-class DefaultVisualStudioSolution extends AbstractBuildableModelElement implements VisualStudioSolution {
-    final DefaultVisualStudioProject rootProject
-    private final String name
-    private final SolutionFile solutionFile
-    private final VisualStudioProjectResolver vsProjectResolver
-
-    DefaultVisualStudioSolution(DefaultVisualStudioProject rootProject, FileResolver fileResolver,
-                                VisualStudioProjectResolver vsProjectResolver, Instantiator instantiator) {
-        this.rootProject = rootProject
-        this.name = rootProject.name
-        this.vsProjectResolver = vsProjectResolver
-        this.solutionFile = instantiator.newInstance(SolutionFile, fileResolver, "${name}.sln" as String)
-    }
-
-    String getName() {
-        return name
-    }
-
-    SolutionFile getSolutionFile() {
-        return solutionFile
-    }
-
-    ProjectNativeComponent getComponent() {
-        return rootProject.component
-    }
-
-    Set<VisualStudioProject> getProjects() {
-        def projects = [] as Set
-        solutionConfigurations.each { solutionConfig ->
-            getProjectConfigurations(solutionConfig).each { projectConfig ->
-                projects << projectConfig.project
-            }
-        }
-        return projects
-    }
-
-    List<VisualStudioProjectConfiguration> getSolutionConfigurations() {
-        return rootProject.configurations
-    }
-
-    List<VisualStudioProjectConfiguration> getProjectConfigurations(VisualStudioProjectConfiguration solutionConfiguration) {
-        def configurations = [] as Set
-        configurations << solutionConfiguration
-        addDependentConfigurations(configurations, solutionConfiguration)
-        return configurations as List
-    }
-
-    private void addDependentConfigurations(Set configurations, VisualStudioProjectConfiguration configuration) {
-        for (LibraryBinary library : configuration.binary.dependentBinaries) {
-            if (library instanceof ProjectNativeBinaryInternal) {
-                VisualStudioProjectConfiguration libraryConfiguration = vsProjectResolver.lookupProjectConfiguration(library);
-                if (configurations.add(libraryConfiguration)) {
-                    addDependentConfigurations(configurations, libraryConfiguration)
-                }
-            }
-        }
-    }
-
-    static class SolutionFile implements TextConfigFile {
-        private final List<Action<? super TextProvider>> actions = new ArrayList<Action<? super TextProvider>>();
-        private final FileResolver fileResolver
-        private Object location
-
-        SolutionFile(FileResolver fileResolver, String defaultLocation) {
-            this.fileResolver = fileResolver
-            this.location = defaultLocation
-        }
-
-        File getLocation() {
-            return fileResolver.resolve(location)
-        }
-
-        void setLocation(Object location) {
-            this.location = location
-        }
-
-        void withContent(Action<? super TextProvider> action) {
-            actions.add(action)
-        }
-
-        List<Action<? super TextProvider>> getTextActions() {
-            return actions
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.groovy
deleted file mode 100644
index 570a69c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.groovy
+++ /dev/null
@@ -1,46 +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.ide.visualstudio.internal
-
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.tasks.InstallExecutable
-
-class ExecutableVisualStudioProjectConfiguration extends VisualStudioProjectConfiguration {
-    ExecutableVisualStudioProjectConfiguration(DefaultVisualStudioProject vsProject, String configurationName, String platformName, ProjectNativeBinary binary) {
-        super(vsProject, configurationName, platformName, binary)
-    }
-
-    @Override
-    String getBuildTask() {
-        InstallExecutable installTask = getInstallTask()
-        return installTask == null ? super.getBuildTask() : installTask.path
-    }
-
-    @Override
-    File getOutputFile() {
-        InstallExecutable installTask = getInstallTask()
-        if (installTask == null) {
-            return super.getOutputFile()
-        } else {
-            return new File(installTask.destinationDir, "lib/" + installTask.executable.name)
-        }
-    }
-
-    private InstallExecutable getInstallTask() {
-        return binary.tasks.withType(InstallExecutable).iterator().next()
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy
deleted file mode 100644
index 7cab44c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy
+++ /dev/null
@@ -1,109 +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.ide.visualstudio.internal
-
-import org.gradle.api.plugins.ExtensionAware
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.PreprocessingTool
-import org.gradle.nativebinaries.toolchain.internal.MacroArgsConverter
-
-class VisualStudioProjectConfiguration {
-    private final DefaultVisualStudioProject vsProject
-    private final String configurationName
-    private final String platformName
-    final ProjectNativeBinaryInternal binary
-    final String type = "Makefile"
-
-    VisualStudioProjectConfiguration(DefaultVisualStudioProject vsProject, String configurationName, String platformName, ProjectNativeBinary binary) {
-        this.vsProject = vsProject
-        this.configurationName = configurationName
-        this.platformName = platformName
-        this.binary = binary as ProjectNativeBinaryInternal
-    }
-
-    String getName() {
-        return "${configurationName}|${platformName}"
-    }
-
-    String getConfigurationName() {
-        return configurationName
-    }
-
-    String getPlatformName() {
-        return platformName
-    }
-
-    String getBuildTask() {
-        return taskPath(binary.name)
-    }
-
-    String getCleanTask() {
-        return taskPath("clean")
-    }
-
-    // TODO:DAZ Make it easier to find the lifecycle task and clean task for a binary and use task.path.
-    private String taskPath(String taskName) {
-        String projectPath = binary.component.projectPath
-        if (projectPath == ":") {
-            return ":${taskName}"
-        }
-        return "${projectPath}:${taskName}"
-    }
-
-    File getOutputFile() {
-        return binary.primaryOutput
-    }
-
-    boolean isDebug() {
-        return binary.buildType.name != 'release'
-    }
-
-    List<String> getCompilerDefines() {
-        List<String> defines = []
-        defines.addAll getDefines('cCompiler')
-        defines.addAll getDefines('cppCompiler')
-        defines.addAll getDefines('rcCompiler')
-        return defines
-    }
-
-    private List<String> getDefines(String tool) {
-        PreprocessingTool rcCompiler = findCompiler(tool)
-        return rcCompiler == null ? [] : new MacroArgsConverter().transform(rcCompiler.macros)
-    }
-
-    private PreprocessingTool findCompiler(String tool) {
-        ExtensionAware extendedBinary = binary as ExtensionAware;
-        return extendedBinary.extensions.findByName(tool) as PreprocessingTool
-    }
-
-    List<File> getIncludePaths() {
-        def includes = [] as Set
-        binary.source.withType(HeaderExportingSourceSet).each { HeaderExportingSourceSet sourceSet ->
-            includes.addAll sourceSet.exportedHeaders.srcDirs
-        }
-        binary.libs*.includeRoots.each {
-            includes.addAll it.files
-        }
-        return includes as List
-    }
-
-    DefaultVisualStudioProject getProject() {
-        return vsProject
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java
deleted file mode 100644
index 6cdd7e5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java
+++ /dev/null
@@ -1,90 +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.ide.visualstudio.internal;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.nativebinaries.ExecutableBinary;
-import org.gradle.nativebinaries.LibraryBinary;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-import org.gradle.nativebinaries.SharedLibraryBinary;
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal;
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal;
-
-import java.util.List;
-
-public class VisualStudioProjectMapper {
-
-    public ProjectConfigurationNames mapToConfiguration(ProjectNativeBinary nativeBinary) {
-        String projectName = projectPrefix(nativeBinary) + componentName(nativeBinary) + projectSuffix(nativeBinary);
-        String configurationName = getConfigurationName(nativeBinary);
-        return new ProjectConfigurationNames(projectName, configurationName, "Win32");
-    }
-
-    private String getConfigurationName(ProjectNativeBinary nativeBinary) {
-        List<String> dimensions = ((ProjectNativeBinaryInternal) nativeBinary).getNamingScheme().getVariantDimensions();
-        if (dimensions.isEmpty()) {
-            return nativeBinary.getBuildType().getName();
-        }
-        return makeName(dimensions);
-    }
-
-    private String projectPrefix(ProjectNativeBinary nativeBinary) {
-        ProjectNativeComponentInternal component = (ProjectNativeComponentInternal) nativeBinary.getComponent();
-        String projectPath = component.getProjectPath();
-        if (":".equals(projectPath)) {
-            return "";
-        }
-        return projectPath.substring(1).replace(":", "_") + "_";
-    }
-
-    private String componentName(ProjectNativeBinary nativeBinary) {
-        return nativeBinary.getComponent().getName();
-    }
-
-    private String projectSuffix(ProjectNativeBinary nativeBinary) {
-        return nativeBinary instanceof SharedLibraryBinary ? "Dll"
-                : nativeBinary instanceof LibraryBinary ? "Lib"
-                : nativeBinary instanceof ExecutableBinary ? "Exe"
-                : "";
-    }
-
-    private static String makeName(Iterable<String> components) {
-        StringBuilder builder = new StringBuilder();
-        for (String component : components) {
-            if (component != null && component.length() > 0) {
-                if (builder.length() == 0) {
-                    builder.append(component);
-                } else {
-                    builder.append(StringUtils.capitalize(component));
-                }
-            }
-        }
-        return builder.toString();
-    }
-
-    static class ProjectConfigurationNames {
-        public final String project;
-        public final String configuration;
-        public final String platform;
-
-        ProjectConfigurationNames(String project, String configuration, String platform) {
-            this.project = project;
-            this.configuration = configuration;
-            this.platform = platform;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.java
deleted file mode 100644
index 192fedf..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.java
+++ /dev/null
@@ -1,65 +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.ide.visualstudio.internal;
-
-import org.gradle.api.internal.DefaultNamedDomainObjectSet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.*;
-
-public class VisualStudioProjectRegistry extends DefaultNamedDomainObjectSet<DefaultVisualStudioProject> {
-    private final FileResolver fileResolver;
-    private final VisualStudioProjectMapper projectMapper;
-
-    public VisualStudioProjectRegistry(FileResolver fileResolver, VisualStudioProjectMapper projectMapper, Instantiator instantiator) {
-        super(DefaultVisualStudioProject.class, instantiator);
-        this.fileResolver = fileResolver;
-        this.projectMapper = projectMapper;
-    }
-
-    public VisualStudioProjectConfiguration getProjectConfiguration(ProjectNativeBinary nativeBinary) {
-        String projectName = projectName(nativeBinary);
-        return getByName(projectName).getConfiguration(nativeBinary);
-    }
-
-    public VisualStudioProjectConfiguration addProjectConfiguration(ProjectNativeBinary nativeBinary) {
-        VisualStudioProjectMapper.ProjectConfigurationNames names = projectMapper.mapToConfiguration(nativeBinary);
-        DefaultVisualStudioProject project = getOrCreateProject(nativeBinary.getComponent(), names.project);
-        VisualStudioProjectConfiguration configuration = createVisualStudioProjectConfiguration(nativeBinary, project, names.configuration, names.platform);
-        project.addConfiguration(nativeBinary, configuration);
-        return configuration;
-    }
-
-    private VisualStudioProjectConfiguration createVisualStudioProjectConfiguration(ProjectNativeBinary nativeBinary, DefaultVisualStudioProject project, String configuration, String platform) {
-        Class<? extends VisualStudioProjectConfiguration> type =
-                nativeBinary instanceof ExecutableBinary ? ExecutableVisualStudioProjectConfiguration.class : VisualStudioProjectConfiguration.class;
-        return getInstantiator().newInstance(type, project, configuration, platform, nativeBinary);
-    }
-
-    private DefaultVisualStudioProject getOrCreateProject(ProjectNativeComponent nativeComponent, String projectName) {
-        DefaultVisualStudioProject vsProject = findByName(projectName);
-        if (vsProject == null) {
-            vsProject = getInstantiator().newInstance(DefaultVisualStudioProject.class, projectName, nativeComponent, fileResolver, getInstantiator());
-            add(vsProject);
-        }
-        return vsProject;
-    }
-
-    private String projectName(ProjectNativeBinary nativeBinary) {
-        return projectMapper.mapToConfiguration(nativeBinary).project;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.java
deleted file mode 100644
index c2d82d9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.java
+++ /dev/null
@@ -1,44 +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.ide.visualstudio.internal;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal;
-import org.gradle.nativebinaries.internal.resolve.ProjectLocator;
-
-public class VisualStudioProjectResolver {
-    private final ProjectLocator projectLocator;
-
-    public VisualStudioProjectResolver(ProjectLocator projectLocator) {
-        this.projectLocator = projectLocator;
-    }
-
-    public VisualStudioProjectConfiguration lookupProjectConfiguration(ProjectNativeBinary nativeBinary) {
-        // Looks in the correct project registry for this binary
-        ProjectInternal componentProject = getComponentProject(nativeBinary);
-        DefaultVisualStudioExtension visualStudioExtension = componentProject.getModelRegistry().get("visualStudio", DefaultVisualStudioExtension.class);
-        VisualStudioProjectRegistry projectRegistry = visualStudioExtension.getProjectRegistry();
-        return projectRegistry.getProjectConfiguration(nativeBinary);
-    }
-
-    private ProjectInternal getComponentProject(ProjectNativeBinary nativeBinary) {
-        String projectPath = ((ProjectNativeComponentInternal) nativeBinary.getComponent()).getProjectPath();
-        return projectLocator.locateProject(projectPath);
-    }
-}
-
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioModel.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioModel.java
deleted file mode 100644
index 0a1fdfa..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioModel.java
+++ /dev/null
@@ -1,39 +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.ide.visualstudio.internal.rules;
-
-import org.gradle.ide.visualstudio.internal.DefaultVisualStudioExtension;
-import org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject;
-import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.model.ModelRule;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-
-public class CreateVisualStudioModel extends ModelRule {
-    @SuppressWarnings("UnusedDeclaration")
-    public void createVisualStudioModelForBinaries(DefaultVisualStudioExtension visualStudioExtension, BinaryContainer binaryContainer) {
-        for (ProjectNativeBinary binary : binaryContainer.withType(ProjectNativeBinary.class)) {
-            VisualStudioProjectConfiguration configuration = visualStudioExtension.getProjectRegistry().addProjectConfiguration(binary);
-
-            // Only create a solution if one of the binaries is buildable
-            if (binary.isBuildable()) {
-                DefaultVisualStudioProject visualStudioProject = configuration.getProject();
-                visualStudioExtension.getSolutionRegistry().addSolution(visualStudioProject);
-            }
-        }
-    }
-}
-
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioTasks.java b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioTasks.java
deleted file mode 100644
index 2614100..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/rules/CreateVisualStudioTasks.java
+++ /dev/null
@@ -1,90 +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.ide.visualstudio.internal.rules;
-
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Delete;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.ide.visualstudio.VisualStudioExtension;
-import org.gradle.ide.visualstudio.VisualStudioProject;
-import org.gradle.ide.visualstudio.VisualStudioSolution;
-import org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask;
-import org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask;
-import org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask;
-import org.gradle.model.ModelRule;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-public class CreateVisualStudioTasks extends ModelRule {
-
-    @SuppressWarnings("UnusedDeclaration")
-    public void createTasksForVisualStudio(TaskContainer tasks, VisualStudioExtension visualStudioExtension) {
-        for (VisualStudioProject vsProject : visualStudioExtension.getProjects()) {
-            vsProject.builtBy(createProjectsFileTask(tasks, vsProject));
-            vsProject.builtBy(createFiltersFileTask(tasks, vsProject));
-        }
-
-        for (VisualStudioSolution vsSolution : visualStudioExtension.getSolutions()) {
-            Task solutionTask = tasks.create(vsSolution.getName() + "VisualStudio");
-            solutionTask.setDescription(String.format("Generates the '%s' Visual Studio solution file.", vsSolution.getName()));
-            vsSolution.setLifecycleTask(solutionTask);
-            vsSolution.builtBy(createSolutionTask(tasks, vsSolution));
-
-            // Lifecycle task for component
-            ProjectNativeComponent component = vsSolution.getComponent();
-            Task lifecycleTask = tasks.maybeCreate(component.getName() + "VisualStudio");
-            lifecycleTask.dependsOn(vsSolution);
-            lifecycleTask.setGroup("IDE");
-            lifecycleTask.setDescription(String.format("Generates the Visual Studio solution for %s.", component));
-        }
-
-        addCleanTask(tasks);
-    }
-
-    private void addCleanTask(TaskContainer tasks) {
-        Delete cleanTask = tasks.create("cleanVisualStudio", Delete.class);
-        for (Task task : tasks.withType(GenerateSolutionFileTask.class)) {
-            cleanTask.delete(task.getOutputs().getFiles());
-        }
-        for (Task task : tasks.withType(GenerateFiltersFileTask.class)) {
-            cleanTask.delete(task.getOutputs().getFiles());
-        }
-        for (Task task : tasks.withType(GenerateProjectFileTask.class)) {
-            cleanTask.delete(task.getOutputs().getFiles());
-        }
-        cleanTask.setGroup("IDE");
-        cleanTask.setDescription("Removes all generated Visual Studio project and solution files");
-    }
-
-    private Task createSolutionTask(TaskContainer tasks, VisualStudioSolution solution) {
-        GenerateSolutionFileTask solutionFileTask = tasks.create(solution.getName() + "VisualStudioSolution", GenerateSolutionFileTask.class);
-        solutionFileTask.setVisualStudioSolution(solution);
-        return solutionFileTask;
-    }
-
-    private Task createProjectsFileTask(TaskContainer tasks, VisualStudioProject vsProject) {
-        GenerateProjectFileTask task = tasks.create(vsProject.getName() + "VisualStudioProject", GenerateProjectFileTask.class);
-        task.setVisualStudioProject(vsProject);
-        task.initGradleCommand();
-        return task;
-    }
-
-    private Task createFiltersFileTask(TaskContainer tasks, VisualStudioProject vsProject) {
-        GenerateFiltersFileTask task = tasks.create(vsProject.getName() + "VisualStudioFilters", GenerateFiltersFileTask.class);
-        task.setVisualStudioProject(vsProject);
-        return task;
-    }
-}
-
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.groovy
deleted file mode 100644
index 5535b2a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.groovy
+++ /dev/null
@@ -1,73 +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.ide.visualstudio.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.ide.visualstudio.VisualStudioExtension
-import org.gradle.ide.visualstudio.internal.DefaultVisualStudioExtension
-import org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject
-import org.gradle.ide.visualstudio.internal.rules.CreateVisualStudioModel
-import org.gradle.ide.visualstudio.internal.rules.CreateVisualStudioTasks
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.model.ModelRules
-import org.gradle.nativebinaries.internal.resolve.ProjectLocator
-import org.gradle.nativebinaries.plugins.NativeBinariesModelPlugin
-
-import javax.inject.Inject
-
- at Incubating
-class VisualStudioPlugin implements Plugin<ProjectInternal> {
-    private final Instantiator instantiator
-    private final ModelRules modelRules
-    private final ProjectLocator projectLocator
-    private final FileResolver fileResolver
-
-    @Inject
-    VisualStudioPlugin(Instantiator instantiator, ModelRules modelRules, ProjectLocator projectLocator, FileResolver fileResolver) {
-        this.instantiator = instantiator
-        this.modelRules = modelRules
-        this.projectLocator = projectLocator
-        this.fileResolver = fileResolver
-    }
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesModelPlugin)
-
-        modelRules.register("visualStudio", instantiator.newInstance(DefaultVisualStudioExtension, instantiator, projectLocator, fileResolver))
-        modelRules.config("visualStudio", new IncludeBuildFileInProject(project))
-        modelRules.rule(new CreateVisualStudioModel())
-        modelRules.rule(new CreateVisualStudioTasks())
-    }
-
-    private class IncludeBuildFileInProject implements Action<VisualStudioExtension> {
-        private final ProjectInternal project;
-
-        IncludeBuildFileInProject(ProjectInternal project) {
-            this.project = project
-        }
-
-        void execute(VisualStudioExtension extension) {
-            extension.projects.all {
-                if (project.getBuildFile() != null) {
-                    ((DefaultVisualStudioProject) it).addSourceFile(project.getBuildFile())
-                }
-            }
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
deleted file mode 100644
index 188f4f0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
+++ /dev/null
@@ -1,58 +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.ide.visualstudio.tasks.internal
-
-import org.gradle.api.Transformer
-import org.gradle.api.internal.xml.XmlTransformer
-import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
-
-class VisualStudioFiltersFile extends XmlPersistableConfigurationObject {
-    private final Transformer<String, File> fileLocationResolver
-
-    VisualStudioFiltersFile(XmlTransformer xmlTransformer, Transformer<String, File> fileLocationResolver) {
-        super(xmlTransformer)
-        this.fileLocationResolver = fileLocationResolver
-    }
-
-    protected String getDefaultResourceName() {
-        'default.vcxproj.filters'
-    }
-
-    def addSource(File sourceFile) {
-        sources.appendNode("ClCompile", [Include: toPath(sourceFile)]).appendNode('Filter', 'Source Files')
-    }
-
-    def addHeader(File headerFile) {
-        headers.appendNode("ClInclude", [Include: toPath(headerFile)]).appendNode('Filter', 'Header Files')
-    }
-
-    def getFilters() {
-        return xml.ItemGroup.findAll({ it.'@Label' == 'Filters' })[0]
-    }
-
-    private Node getSources() {
-        return xml.ItemGroup.find({ it.'@Label' == 'Sources' }) as Node
-    }
-
-    private Node getHeaders() {
-        return xml.ItemGroup.find({ it.'@Label' == 'Headers' }) as Node
-    }
-
-    private String toPath(File it) {
-        fileLocationResolver.transform(it)
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.groovy
deleted file mode 100644
index ca27bc5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.groovy
+++ /dev/null
@@ -1,98 +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.ide.visualstudio.tasks.internal
-import org.gradle.api.Transformer
-import org.gradle.api.internal.xml.XmlTransformer
-import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
-import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
-
-class VisualStudioProjectFile extends XmlPersistableConfigurationObject {
-    private final Transformer<String, File> fileLocationResolver
-    String gradleCommand = 'gradle'
-
-    VisualStudioProjectFile(XmlTransformer xmlTransformer, Transformer<String, File> fileLocationResolver) {
-        super(xmlTransformer)
-        this.fileLocationResolver = fileLocationResolver
-    }
-
-    protected String getDefaultResourceName() {
-        'default.vcxproj'
-    }
-
-    def setProjectUuid(String uuid) {
-        Node globals = xml.PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
-        globals.appendNode("ProjectGUID", uuid)
-    }
-
-    def addSourceFile(File it) {
-        def sources = xml.ItemGroup.find({ it.'@Label' == 'Sources' }) as Node
-        sources.appendNode("ClCompile", [Include: toPath(it)])
-    }
-
-    def addResource(File it) {
-        def resources = xml.ItemGroup.find({ it.'@Label' == 'References' }) as Node
-        resources.appendNode("ResourceCompile", [Include: toPath(it)])
-    }
-
-    def addHeaderFile(File it) {
-        def headers = xml.ItemGroup.find({ it.'@Label' == 'Headers' }) as Node
-        headers.appendNode("ClInclude", [Include: toPath(it)])
-    }
-
-    def addConfiguration(VisualStudioProjectConfiguration configuration) {
-        def configNode = configurations.appendNode("ProjectConfiguration", [Include: configuration.name])
-        configNode.appendNode("Configuration", configuration.configurationName)
-        configNode.appendNode("Platform", configuration.platformName)
-        final configCondition = "'\$(Configuration)|\$(Platform)'=='${configuration.name}'"
-
-        def vsOutputDir = ".vs\\${configuration.project.name}\\\$(Configuration)"
-        Node defaultProps = xml.Import.find({ it.'@Project' == '$(VCTargetsPath)\\Microsoft.Cpp.Default.props'}) as Node
-        defaultProps + {
-            PropertyGroup(Label: "Configuration", Condition: configCondition) {
-                ConfigurationType(configuration.type)
-                UseDebugLibraries(configuration.debug)
-                OutDir(vsOutputDir)
-                IntDir(vsOutputDir)
-            }
-        }
-
-        final includePath = toPath(configuration.includePaths).join(";")
-        Node userMacros = xml.PropertyGroup.find({ it.'@Label' == 'UserMacros'}) as Node
-        userMacros + {
-            PropertyGroup(Label: "NMakeConfiguration", Condition: configCondition) {
-                NMakeBuildCommandLine("${gradleCommand} ${configuration.buildTask}")
-                NMakeCleanCommandLine("${gradleCommand} ${configuration.cleanTask}")
-                NMakeReBuildCommandLine("${gradleCommand} ${configuration.cleanTask} ${configuration.buildTask}")
-                NMakePreprocessorDefinitions(configuration.compilerDefines.join(";"))
-                NMakeIncludeSearchPath(includePath)
-                NMakeOutput(toPath(configuration.outputFile))
-            }
-        }
-    }
-
-    private Node getConfigurations() {
-        return xml.ItemGroup.find({ it.'@Label' == 'ProjectConfigurations' }) as Node
-    }
-
-    private List<String> toPath(List<File> files) {
-        return files.collect({toPath(it)})
-    }
-
-    private String toPath(File it) {
-        fileLocationResolver.transform(it)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/DependentSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/DependentSourceSet.java
deleted file mode 100644
index 168b2b1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/DependentSourceSet.java
+++ /dev/null
@@ -1,57 +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.language;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.LanguageSourceSet;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * A source set that depends on one or more {@link org.gradle.nativebinaries.NativeDependencySet}s to be built.
- */
- at Incubating
-public interface DependentSourceSet extends LanguageSourceSet {
-    /**
-     * 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 org.gradle.nativebinaries.Library}</li>
-     *     <li>A {@link org.gradle.nativebinaries.NativeDependencySet}</li>
-     *     <li>A {@link java.util.Map} containing the library selector.</li>
-     * </ul>
-     *
-     * The Map notation supports the following String attributes:
-     *
-     * <ul>
-     *     <li>project: the path to the project containing the library (optional, defaults to current project)</li>
-     *     <li>library: the name of the library (required)</li>
-     *     <li>linkage: the library linkage required ['shared'/'static'] (optional, defaults to 'shared')</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/language/HeaderExportingSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/HeaderExportingSourceSet.java
deleted file mode 100644
index e27d234..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/HeaderExportingSourceSet.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.language;
-
-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<? super SourceDirectorySet> config);
-
-    /**
-     * The headers as a directory set.
-     */
-    SourceDirectorySet getExportedHeaders();
-
-    /**
-     * The headers that are private to this source set and implicitly available. These are not explicitly made available for compilation.
-     *
-     * TODO:DAZ This is temporary to get 'implicit' headers working in Visual Studio. The plan is to model these better soon.
-     */
-    SourceDirectorySet getImplicitHeaders();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/AssemblerSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/AssemblerSourceSet.java
deleted file mode 100644
index 48ea483..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/AssemblerSourceSet.java
+++ /dev/null
@@ -1,42 +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.language.assembler;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * A set of assembly language sources.
- *
- * <pre autoTested="true">
- * apply plugin: "assembler"
- *
- * sources {
- *     main {
- *         // Configure an existing AssemblerSourceSet
- *         asm {
- *             source {
- *                 srcDirs "src/main/i386", "src/shared/asm"
- *                 include "**{@literal /}*.s"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- at Incubating
-public interface AssemblerSourceSet extends LanguageSourceSet {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.java
deleted file mode 100644
index 0f988da..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.java
+++ /dev/null
@@ -1,29 +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.language.assembler.internal;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.assembler.AssemblerSourceSet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.internal.AbstractLanguageSourceSet;
-
-public class DefaultAssemblerSourceSet extends AbstractLanguageSourceSet implements AssemblerSourceSet {
-    public DefaultAssemblerSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, "assembler source", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/plugins/AssemblerLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/plugins/AssemblerLangPlugin.groovy
deleted file mode 100644
index b393bb8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/plugins/AssemblerLangPlugin.groovy
+++ /dev/null
@@ -1,62 +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.language.assembler.plugins
-import org.gradle.api.Action
-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.assembler.AssemblerSourceSet
-import org.gradle.language.assembler.internal.DefaultAssemblerSourceSet
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-
-import javax.inject.Inject
-/**
- * Adds core Assembler language support.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link AssemblerSourceSet} called 'asm'.</li>
- *     <li>Establishes a convention for all {@link AssemblerSourceSet}s so that sources are located in 'src/<name>/asm'.</li>
- *     <li>
- * </ul>
- */
- 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);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(AssemblerSourceSet) { name ->
-                    instantiator.newInstance(DefaultAssemblerSourceSet, name, functionalSourceSet, project)
-                }
-
-                // Create a single assembler source set
-                functionalSourceSet.create "asm", AssemblerSourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/c/CSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/c/CSourceSet.java
deleted file mode 100644
index 17269ab..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/c/CSourceSet.java
+++ /dev/null
@@ -1,49 +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.language.c;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.HeaderExportingSourceSet;
-
-/**
- * A set of C source files.
- *
- * <p>A C source set contains a set of source files, together with an optional set of exported header files.</p>
- *
- * <pre autoTested="true">
- * apply plugin: "c"
- *
- * sources {
- *     main {
- *         // Configure an existing CSourceSet
- *         c {
- *             source {
- *                 srcDirs "src/main/cpp", "src/shared/c++"
- *                 include "**{@literal /}*.c"
- *             }
- *             exportedHeaders {
- *                 srcDirs "src/main/include"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- 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/language/c/internal/DefaultCSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/c/internal/DefaultCSourceSet.java
deleted file mode 100644
index 5035f2d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/c/internal/DefaultCSourceSet.java
+++ /dev/null
@@ -1,32 +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.language.c.internal;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.c.CSourceSet;
-import org.gradle.language.internal.AbstractHeaderExportingDependentSourceSet;
-
-import javax.inject.Inject;
-
-public class DefaultCSourceSet extends AbstractHeaderExportingDependentSourceSet implements CSourceSet {
-    @Inject
-    public DefaultCSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, project, "C source", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/c/plugins/CLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/c/plugins/CLangPlugin.groovy
deleted file mode 100644
index d46a449..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/c/plugins/CLangPlugin.groovy
+++ /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.language.c.plugins
-import org.gradle.api.Action
-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.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.c.internal.DefaultCSourceSet
-
-import javax.inject.Inject
-/**
- * Adds core C language support.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link CSourceSet} called 'cpp'.</li>
- *     <li>Establishes a convention for all {@link CSourceSet}s so that sources are located in 'src/<name>/c' and
- *         headers are located in 'src/<name>/headers'.</li>
- *     <li>
- * </ul>
- */
- 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);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(CSourceSet) { name ->
-                    instantiator.newInstance(DefaultCSourceSet, name, functionalSourceSet, project)
-                }
-
-                // Create a single C source set
-                functionalSourceSet.create "c", CSourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/CppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/CppSourceSet.java
deleted file mode 100644
index 20d2ea1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/CppSourceSet.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.language.cpp;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.HeaderExportingSourceSet;
-
-/**
- * A set of C++ source files.
- *
- * <p>A C++ source set contains a set of source files, together with an optional set of exported header files.</p>
- *
- * <pre autoTested="true">
- * apply plugin: "cpp"
- *
- * sources {
- *     main {
- *         // Configure an existing CppSourceSet
- *         cpp {
- *             source {
- *                 srcDirs "src/main/cpp", "src/shared/c++"
- *                 include "**{@literal /}*.cpp"
- *             }
- *             exportedHeaders {
- *                 srcDirs "src/main/include", "src/shared/include"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- 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/language/cpp/internal/DefaultCppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/internal/DefaultCppSourceSet.java
deleted file mode 100644
index 146049b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/internal/DefaultCppSourceSet.java
+++ /dev/null
@@ -1,29 +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.language.cpp.internal;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.internal.AbstractHeaderExportingDependentSourceSet;
-import org.gradle.language.cpp.CppSourceSet;
-
-public class DefaultCppSourceSet extends AbstractHeaderExportingDependentSourceSet implements CppSourceSet {
-    public DefaultCppSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, project, "C++ source", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/plugins/CppLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/plugins/CppLangPlugin.groovy
deleted file mode 100644
index 036912d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/plugins/CppLangPlugin.groovy
+++ /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.language.cpp.plugins
-import org.gradle.api.Action
-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.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.language.cpp.internal.DefaultCppSourceSet
-
-import javax.inject.Inject
-/**
- * Adds core C++ language support.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link CppSourceSet} called 'cpp'.</li>
- *     <li>Establishes a convention for all {@link CppSourceSet}s so that sources are located in 'src/<name>/cpp' and
- *         headers are located in 'src/<name>/headers'.</li>
- *     <li>
- * </ul>
- */
- 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);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(CppSourceSet) { name ->
-                    instantiator.newInstance(DefaultCppSourceSet, name, functionalSourceSet, project)
-                }
-                // Add a single C++ source set
-                functionalSourceSet.create "cpp", CppSourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/internal/AbstractHeaderExportingDependentSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/internal/AbstractHeaderExportingDependentSourceSet.java
deleted file mode 100644
index dcae0fb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/internal/AbstractHeaderExportingDependentSourceSet.java
+++ /dev/null
@@ -1,64 +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.language.internal;
-
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.util.CollectionUtils;
-
-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 with dependencies and exported headers.
- */
-public abstract class AbstractHeaderExportingDependentSourceSet extends AbstractHeaderExportingSourceSet
-        implements HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
-
-    private final List<Object> libs = new ArrayList<Object>();
-    private final ConfigurationBasedNativeDependencySet configurationDependencySet;
-
-    public AbstractHeaderExportingDependentSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project, String typeName, SourceDirectorySet source) {
-        super(name, parent, project, typeName, source);
-
-        this.configurationDependencySet = new ConfigurationBasedNativeDependencySet(project, getFullName());
-
-        libs.add(configurationDependencySet);
-    }
-
-    public Collection<?> getLibs() {
-        return libs;
-    }
-
-    public void lib(Object library) {
-        if (library instanceof Iterable<?>) {
-            Iterable<?> iterable = (Iterable) library;
-            CollectionUtils.addAll(libs, iterable);
-        } else {
-            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/language/internal/AbstractHeaderExportingSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/internal/AbstractHeaderExportingSourceSet.java
deleted file mode 100644
index 982189f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/internal/AbstractHeaderExportingSourceSet.java
+++ /dev/null
@@ -1,53 +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.language.internal;
-
-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.language.HeaderExportingSourceSet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.AbstractLanguageSourceSet;
-
-/**
- * A convenience base class for implementing language source sets with dependencies and exported headers.
- */
-public abstract class AbstractHeaderExportingSourceSet extends AbstractLanguageSourceSet
-        implements HeaderExportingSourceSet, LanguageSourceSet {
-
-    private final DefaultSourceDirectorySet exportedHeaders;
-    private final DefaultSourceDirectorySet implicitHeaders;
-
-    public AbstractHeaderExportingSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project, String typeName, SourceDirectorySet source) {
-        super(name, parent, typeName, source);
-        this.exportedHeaders = new DefaultSourceDirectorySet("exported headers", project.getFileResolver());
-        this.implicitHeaders = new DefaultSourceDirectorySet("implicit headers", project.getFileResolver());
-    }
-
-    public SourceDirectorySet getExportedHeaders() {
-        return exportedHeaders;
-    }
-
-    public void exportedHeaders(Action<? super SourceDirectorySet> config) {
-        config.execute(getExportedHeaders());
-    }
-
-    public SourceDirectorySet getImplicitHeaders() {
-        return implicitHeaders;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/internal/ConfigurationBasedNativeDependencySet.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/internal/ConfigurationBasedNativeDependencySet.groovy
deleted file mode 100644
index 2b9b0a9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/internal/ConfigurationBasedNativeDependencySet.groovy
+++ /dev/null
@@ -1,90 +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.language.internal
-
-import org.gradle.nativebinaries.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/language/objectivec/ObjectiveCSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/ObjectiveCSourceSet.java
deleted file mode 100644
index d86c297..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/ObjectiveCSourceSet.java
+++ /dev/null
@@ -1,52 +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.language.objectivec;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-
-
-/**
- * A set of Objective-C source files.
- *
- * <p>An ObjectiveC source set contains a set of source files, together with an optional set of exported header files.</p>
- *
- * <pre autoTested="true">
- * apply plugin: "objective-c"
- *
- * sources {
- *     main {
- *         // Configure an existing ObjectiveCSourceSet
- *         objc {
- *             source {
- *                 srcDirs "src/main/objectiveC", "src/shared/objectiveC"
- *                 include "**{@literal /}*.m"
- *             }
- *             exportedHeaders {
- *                 srcDirs "src/main/include"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- at Incubating
-public interface ObjectiveCSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.java
deleted file mode 100644
index 7e8b5f3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.java
+++ /dev/null
@@ -1,32 +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.language.objectivec.internal;
-
-import javax.inject.Inject;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.internal.AbstractHeaderExportingDependentSourceSet;
-import org.gradle.language.objectivec.ObjectiveCSourceSet;
-
-public class DefaultObjectiveCSourceSet extends AbstractHeaderExportingDependentSourceSet implements ObjectiveCSourceSet {
-    @Inject
-    public DefaultObjectiveCSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, project, "Objective-C source", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.groovy
deleted file mode 100644
index e2d78d0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.groovy
+++ /dev/null
@@ -1,62 +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.language.objectivec.plugins
-import org.gradle.api.Action
-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.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.language.objectivec.ObjectiveCSourceSet
-import org.gradle.language.objectivec.internal.DefaultObjectiveCSourceSet
-
-import javax.inject.Inject
-/**
- * Adds core Objective-C language support.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link ObjectiveCSourceSet} called 'objectiveC'.</li>
- *     <li>Establishes a convention for all {@link ObjectiveCSourceSet}s so that sources are located in 'src/<name>/objectiveC' and
- *         headers are located in 'src/<name>/headers'.</li>
- *     <li>
- * </ul>
- */
- at Incubating
-class ObjectiveCLangPlugin implements Plugin<ProjectInternal> {
-    private final Instantiator instantiator;
-
-    @Inject
-    public ObjectiveCLangPlugin(Instantiator instantiator) {
-        this.instantiator = instantiator;
-    }
-
-    void apply(ProjectInternal project) {
-        project.getPlugins().apply(LanguageBasePlugin.class);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(ObjectiveCSourceSet) { name ->
-                    instantiator.newInstance(DefaultObjectiveCSourceSet, name, functionalSourceSet, project)
-                }
-                // Add a single Objective-C source set
-                functionalSourceSet.create "objc", ObjectiveCSourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.java
deleted file mode 100644
index 3db6a7c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.java
+++ /dev/null
@@ -1,51 +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.language.objectivecpp;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * A set of Objective-C++ source files.
- *
- * <p>An Objective-C++ source set contains a set of source files, together with an optional set of exported header files.</p>
- *
- * <pre autoTested="true">
- * apply plugin: "objective-cpp"
- *
- * sources {
- *     main {
- *         // Configure an existing ObjectiveCppSourceSet
- *         objcpp {
- *             source {
- *                 srcDirs "src/main/objectiveCpp", "src/shared/objectiveCpp"
- *                 include "**{@literal /}*.mm"
- *             }
- *             exportedHeaders {
- *                 srcDirs "src/main/include"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- at Incubating
-public interface ObjectiveCppSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.java
deleted file mode 100644
index 677450b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.java
+++ /dev/null
@@ -1,32 +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.language.objectivecpp.internal;
-
-import javax.inject.Inject;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.internal.AbstractHeaderExportingDependentSourceSet;
-import org.gradle.language.objectivecpp.ObjectiveCppSourceSet;
-
-public class DefaultObjectiveCppSourceSet extends AbstractHeaderExportingDependentSourceSet implements ObjectiveCppSourceSet {
-    @Inject
-    public DefaultObjectiveCppSourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, project, "Objective-C++ source", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.groovy
deleted file mode 100644
index f538428..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.groovy
+++ /dev/null
@@ -1,62 +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.language.objectivecpp.plugins
-import org.gradle.api.Action
-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.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
-import org.gradle.language.objectivecpp.internal.DefaultObjectiveCppSourceSet
-
-import javax.inject.Inject
-/**
- * Adds core Objective-C++ language support.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link ObjectiveCppSourceSet} called 'objcpp'.</li>
- *     <li>Establishes a convention for all {@link ObjectiveCppSourceSet}s so that sources are located in 'src/<name>/objcpp' and
- *         headers are located in 'src/<name>/headers'.</li>
- *     <li>
- * </ul>
- */
- at Incubating
-class ObjectiveCppLangPlugin implements Plugin<ProjectInternal> {
-    private final Instantiator instantiator;
-
-    @Inject
-    public ObjectiveCppLangPlugin(Instantiator instantiator) {
-        this.instantiator = instantiator;
-    }
-
-    void apply(ProjectInternal project) {
-        project.getPlugins().apply(LanguageBasePlugin.class);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(ObjectiveCppSourceSet) { name ->
-                    instantiator.newInstance(DefaultObjectiveCppSourceSet, name, functionalSourceSet, project)
-                }
-                // Add a single Objective-C++ source set
-                functionalSourceSet.create "objcpp", ObjectiveCppSourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/WindowsResourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/rc/WindowsResourceSet.java
deleted file mode 100644
index 09cce70..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/WindowsResourceSet.java
+++ /dev/null
@@ -1,48 +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.language.rc;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * A set of Windows Resource definition files.
- *
- * <p>A Windows Resource set contains a set of script files, together with an optional set of header files.</p>
- *
- * <pre autoTested="true">
- * apply plugin: "windows-resources"
- *
- * sources {
- *     main {
- *         // Configure an existing WindowsResourceSet
- *         rc {
- *             source {
- *                 srcDirs "src/main/rc"
- *                 include "**{@literal /}*.rc"
- *             }
- *             exportedHeaders {
- *                 srcDirs "src/main/include"
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
- at Incubating
-public interface WindowsResourceSet extends LanguageSourceSet, HeaderExportingSourceSet {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/internal/DefaultWindowsResourceSet.java b/subprojects/cpp/src/main/groovy/org/gradle/language/rc/internal/DefaultWindowsResourceSet.java
deleted file mode 100644
index 90acc28..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/internal/DefaultWindowsResourceSet.java
+++ /dev/null
@@ -1,28 +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.language.rc.internal;
-
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.internal.AbstractHeaderExportingSourceSet;
-import org.gradle.language.rc.WindowsResourceSet;
-
-public class DefaultWindowsResourceSet extends AbstractHeaderExportingSourceSet implements WindowsResourceSet {
-    public DefaultWindowsResourceSet(String name, FunctionalSourceSet parent, ProjectInternal project) {
-        super(name, parent, project, "windows resources", new DefaultSourceDirectorySet("source", project.getFileResolver()));
-     }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.groovy
deleted file mode 100644
index 3bb029d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.groovy
+++ /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.language.rc.plugins
-import org.gradle.api.Action
-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.FunctionalSourceSet
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.language.rc.WindowsResourceSet
-import org.gradle.language.rc.internal.DefaultWindowsResourceSet
-
-import javax.inject.Inject
-/**
- * Adds core language support for Windows resource script files.
- *
- * <ul>
- *     <li>For any {@link FunctionalSourceSet}, adds a conventional {@link WindowsResourceSet} called 'rc'.</li>
- *     <li>Establishes a convention for all {@link WindowsResourceSet}s so that sources are
- *         located in 'src/<name>/rc' and headers are located in 'src/<name>/headers'.</li>
- *     <li>
- * </ul>
- */
- at Incubating
-class WindowsResourceScriptPlugin implements Plugin<ProjectInternal> {
-    private final Instantiator instantiator;
-
-    @Inject
-    public WindowsResourceScriptPlugin(Instantiator instantiator) {
-        this.instantiator = instantiator;
-    }
-
-    void apply(ProjectInternal project) {
-        project.getPlugins().apply(LanguageBasePlugin.class);
-
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(WindowsResourceSet) { name ->
-                    instantiator.newInstance(DefaultWindowsResourceSet, name, functionalSourceSet, project)
-                }
-
-                // Create a single windows resource set
-                functionalSourceSet.create "rc", WindowsResourceSet
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildType.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildType.java
deleted file mode 100644
index f70765f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildType.java
+++ /dev/null
@@ -1,30 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-
-/**
- * Specifies a build-type for a native binary. Common build types are 'debug' and 'release', but others may be defined.
- */
- at Incubating
-public interface BuildType extends Named {
-    /**
-     * Returns a human-consumable name for this build type.
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildTypeContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildTypeContainer.java
deleted file mode 100644
index a18f249..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/BuildTypeContainer.java
+++ /dev/null
@@ -1,27 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectContainer;
-
-/**
- * A container of {@link BuildType}s.
- */
- at Incubating
-public interface BuildTypeContainer extends NamedDomainObjectContainer<BuildType> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Executable.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Executable.java
deleted file mode 100644
index 6bda7a2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Executable.java
+++ /dev/null
@@ -1,26 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-/**
- * An executable native component that is built by Gradle.
- */
- at Incubating
-public interface Executable extends ProjectNativeComponent, TargetedNativeComponent {
-    
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableBinary.java
deleted file mode 100644
index 875f594..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableBinary.java
+++ /dev/null
@@ -1,36 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-import java.io.File;
-
-/**
- * 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 {
-    /**
-     * The executable file.
-     */
-    File getExecutableFile();
-
-    /**
-     * The executable file.
-     */
-    void setExecutableFile(File executableFile);
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableContainer.java
deleted file mode 100644
index 34bed7b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ExecutableContainer.java
+++ /dev/null
@@ -1,27 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectSet;
-
-/**
- * A container of native executables.
- */
- at Incubating
-public interface ExecutableContainer extends NamedDomainObjectSet<Executable> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Flavor.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Flavor.java
deleted file mode 100644
index e147653..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Flavor.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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-
-/**
- * Defines a custom variant that differentiate a {@link NativeBinary}.
- */
- at Incubating
-public interface Flavor extends Named {
-    /**
-     * Returns a human-consumable display name for this flavor.
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/FlavorContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/FlavorContainer.java
deleted file mode 100644
index 143fe6d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/FlavorContainer.java
+++ /dev/null
@@ -1,30 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectContainer;
-
-/**
- * A container of {@link Flavor}s.
- * <p/>
- * If no flavor is explicitly configured, will contain a single {@link Flavor} named 'default'.
- * Any flavors explicitly configured will overwrite the default flavor.
- */
- at Incubating
-public interface FlavorContainer extends NamedDomainObjectContainer<Flavor> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Library.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Library.java
deleted file mode 100644
index a933b8f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Library.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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-/**
- * A library component that is built by a gradle project.
- */
- at Incubating
-public interface Library extends ProjectNativeComponent, TargetedNativeComponent {
-    /**
-     * Converts this library to a native library requirement that uses the shared library variant. This is the default.
-     */
-    NativeLibraryRequirement getShared();
-
-    /**
-     * Converts this library to a native library requirement that uses the static library variant.
-     */
-    NativeLibraryRequirement getStatic();
-
-    /**
-     * Converts this library to a native library requirement that uses the api library linkage.
-     */
-    NativeLibraryRequirement getApi();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryBinary.java
deleted file mode 100644
index 2072ade..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryBinary.java
+++ /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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-/**
- * A physical representation of a {@link Library} component.
- */
- at Incubating
-public interface LibraryBinary extends NativeBinary {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryContainer.java
deleted file mode 100644
index 628f987..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/LibraryContainer.java
+++ /dev/null
@@ -1,27 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectSet;
-
-/**
- * A container of native libraries.
- */
- at Incubating
-public interface LibraryContainer extends NamedDomainObjectSet<Library> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinary.java
deleted file mode 100644
index 0c99b57..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinary.java
+++ /dev/null
@@ -1,42 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.Binary;
-import org.gradle.nativebinaries.platform.Platform;
-
-/**
- * Represents a particular binary artifact.
- */
- at Incubating
-public interface NativeBinary extends Binary {
-    /**
-     * The flavor that this binary was built with.
-     */
-    Flavor getFlavor();
-
-    /**
-     * Returns the {@link org.gradle.nativebinaries.platform.Platform} that this binary is targeted to run on.
-     */
-    Platform getTargetPlatform();
-
-    /**
-     * Returns the {@link BuildType} used to construct this binary.
-     */
-    BuildType getBuildType();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinaryTasks.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinaryTasks.java
deleted file mode 100644
index d66bb95..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeBinaryTasks.java
+++ /dev/null
@@ -1,43 +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.nativebinaries;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Incubating;
-import org.gradle.api.Task;
-import org.gradle.nativebinaries.tasks.BuildBinaryTask;
-
-/**
- * Provides access to key tasks used for building the binary.
- */
- at Incubating
-public interface NativeBinaryTasks extends DomainObjectSet<Task> {
-    /**
-     * The link task, if one is present. Null otherwise.
-     */
-    Task getLink();
-
-    /**
-     * The create static library task if present. Null otherwise.
-     */
-    Task getCreateStaticLib();
-
-    /**
-     * The task that builds the binary out of object files: either the link task or create static library task as appropriate.
-     */
-    BuildBinaryTask getBuilder();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeDependencySet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeDependencySet.java
deleted file mode 100644
index b79f25c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeDependencySet.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.nativebinaries;
-
-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/nativebinaries/NativeLibraryRequirement.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeLibraryRequirement.java
deleted file mode 100644
index 2493b9a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/NativeLibraryRequirement.java
+++ /dev/null
@@ -1,40 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-/**
- * A dependency on a native-binaries library within the build.
- */
- at Incubating
-public interface NativeLibraryRequirement {
-    /**
-     * The path to the project containing the library.
-     */
-    String getProjectPath();
-
-    /**
-     * The name of the required library.
-     */
-    String getLibraryName();
-
-    /**
-     * The required linkage.
-     */
-    // TODO:DAZ Type this
-    String getLinkage();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibraries.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibraries.java
deleted file mode 100644
index 9f3d605..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibraries.java
+++ /dev/null
@@ -1,29 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectSet;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
-
-/**
- * A container of {@link PrebuiltLibrary} instances.
- */
- at Incubating
-public interface PrebuiltLibraries extends ArtifactRepository, NamedDomainObjectSet<PrebuiltLibrary> {
-    PrebuiltLibrary resolveLibrary(String name);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibrary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibrary.java
deleted file mode 100644
index 330f92b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/PrebuiltLibrary.java
+++ /dev/null
@@ -1,38 +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.nativebinaries;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.api.file.SourceDirectorySet;
-
-/**
- * A library component that is not built by gradle.
- */
- at Incubating
-public interface PrebuiltLibrary extends Named {
-    /**
-     * The binaries that are built for this component. You can use this to configure the binaries for this component.
-     */
-    DomainObjectSet<NativeBinary> getBinaries();
-
-    /**
-     * The headers exported by this library. These headers will be added to all binaries for this library.
-     */
-    SourceDirectorySet getHeaders();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeBinary.java
deleted file mode 100644
index 332e29b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeBinary.java
+++ /dev/null
@@ -1,106 +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.nativebinaries;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Incubating;
-import org.gradle.internal.HasInternalProtocol;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-
-import java.util.Collection;
-
-/**
- * Represents a particular binary artifact that is the result of building a native component.
- */
- at Incubating @HasInternalProtocol
-public interface ProjectNativeBinary extends NativeBinary {
-    /**
-     * The component that this binary was built from.
-     */
-    ProjectNativeComponent getComponent();
-
-    /**
-     * Can this binary be built in the current environment?
-     */
-    boolean isBuildable();
-
-    /**
-     * The source sets used to compile this binary.
-     */
-    DomainObjectSet<LanguageSourceSet> getSource();
-
-    /**
-     * Adds one or more {@link org.gradle.language.base.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 org.gradle.language.base.LanguageSourceSet}</li>
-     *     <li>A Collection of {@link org.gradle.language.base.LanguageSourceSet}s</li>
-     * </ul>
-     */
-    void source(Object source);
-
-    /**
-     * 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 NativeDependencySet}</li>
-     *     <li>A {@link java.util.Map} containing the library selector.</li>
-     * </ul>
-     *
-     * The Map notation supports the following String attributes:
-     *
-     * <ul>
-     *     <li>project: the path to the project containing the library (optional, defaults to current project)</li>
-     *     <li>library: the name of the library (required)</li>
-     *     <li>linkage: the library linkage required ['shared'/'static'] (optional, defaults to 'shared')</li>
-     * </ul>
-     */
-    void lib(Object library);
-
-    /**
-     * Returns the {@link org.gradle.nativebinaries.toolchain.ToolChain} that will be used to build this binary.
-     */
-    ToolChain getToolChain();
-
-    // TODO:DAZ Add these tools as extensions: linker for ExecutableBinary and SharedLibraryBinary, staticLibArchiver for StaticLibraryBinary
-    /**
-     * The settings used for linking this binary.
-     */
-    Tool getLinker();
-
-    /**
-     * The static archiver settings used for creating this binary.
-     */
-    Tool getStaticLibArchiver();
-
-    /**
-     * The set of tasks associated with this binary.
-     */
-    NativeBinaryTasks getTasks();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeComponent.java
deleted file mode 100644
index 94774f4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/ProjectNativeComponent.java
+++ /dev/null
@@ -1,66 +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.nativebinaries;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.internal.HasInternalProtocol;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * Represents a logical software component, which may be built in a number of variant binaries.
- */
- at Incubating @HasInternalProtocol
-public interface ProjectNativeComponent extends Named {
-    /**
-     * Returns a human-consumable display name for this component.
-     */
-    String getDisplayName();
-
-    /**
-     * The binaries that are built for this component. You can use this to configure the binaries for this component.
-     */
-    DomainObjectSet<NativeBinary> getBinaries();
-
-    /**
-     * The source sets that are used to build this component.
-     */
-    DomainObjectSet<LanguageSourceSet> getSource();
-
-    /**
-     * Adds one or more {@link org.gradle.language.base.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 org.gradle.language.base.LanguageSourceSet}</li>
-     *     <li>A Collection of {@link org.gradle.language.base.LanguageSourceSet}s</li>
-     * </ul>
-     */
-    void source(Object source);
-
-    /**
-     * 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);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Repositories.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Repositories.java
deleted file mode 100644
index 26b904e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Repositories.java
+++ /dev/null
@@ -1,28 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.PolymorphicDomainObjectContainer;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
-
-/**
- * The repositories that Gradle will search for prebuilt libraries.
- */
- at Incubating
-public interface Repositories extends PolymorphicDomainObjectContainer<ArtifactRepository> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/SharedLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/SharedLibraryBinary.java
deleted file mode 100644
index 21e80f2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/SharedLibraryBinary.java
+++ /dev/null
@@ -1,47 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-import java.io.File;
-
-/**
- * A {@link Library} that has been compiled and linked as a shared library.
- */
- at Incubating
-public interface SharedLibraryBinary extends LibraryBinary {
-
-    /**
-     * The shared library file.
-     */
-    File getSharedLibraryFile();
-
-    /**
-     * The shared library link file.
-     */
-    File getSharedLibraryLinkFile();
-
-    /**
-     * The shared library file.
-     */
-    void setSharedLibraryFile(File sharedLibraryFile);
-
-    /**
-     * The shared library file.
-     */
-    void setSharedLibraryLinkFile(File sharedLibraryLinkFile);
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/StaticLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/StaticLibraryBinary.java
deleted file mode 100644
index d0306d9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/StaticLibraryBinary.java
+++ /dev/null
@@ -1,40 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.internal.HasInternalProtocol;
-
-import java.io.File;
-
-/**
- * A {@link Library} that has been compiled and archived into a static library.
- */
- at Incubating
- at HasInternalProtocol
-public interface StaticLibraryBinary extends LibraryBinary {
-
-    /**
-     * The static library file.
-     */
-    File getStaticLibraryFile();
-
-    /**
-     * The static library binary file.
-     */
-    void setStaticLibraryFile(File staticLibraryFile);
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/TargetedNativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/TargetedNativeComponent.java
deleted file mode 100644
index 3b4639b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/TargetedNativeComponent.java
+++ /dev/null
@@ -1,42 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A native component that can be configured to target certain variant dimensions.
- */
- at Incubating @HasInternalProtocol
-public interface TargetedNativeComponent {
-
-    /**
-     * Specifies the names of one or more {@link Flavor}s that this component should be built for.
-     */
-    void targetFlavors(String... flavorSelectors);
-
-    /**
-     * Specifies the names of one or more {@link org.gradle.nativebinaries.platform.Platform}s that this component should be built for.
-     */
-    void targetPlatforms(String... platformSelectors);
-
-    /**
-     * Specifies the names of one or more {@link BuildType}s that this component should be built for.
-     */
-    void targetBuildTypes(String... platformSelectors);
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Tool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Tool.java
deleted file mode 100644
index a4d8810..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/Tool.java
+++ /dev/null
@@ -1,39 +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.nativebinaries;
-
-import org.gradle.api.Incubating;
-
-import java.util.List;
-
-// TODO:DAZ Possibly merge with org.gradle.nativecode.toolchain.GccTool
-// Need to work out if it makes sense to set the args when configuring a tool chain, or set the tool exe for a binary.
-// Seems like merging the 2 might work.
-/**
- * Configuration of the arguments of a ToolChain executable.
- */
- at Incubating
-public interface Tool {
-    /**
-     * The arguments passed when executing this tool.
-     */
-    List<String> getArgs();
-
-    /**
-     * Adds a number of arguments to be passed to the tool.
-     */
-    void args(String... args);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractBinaryToolSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractBinaryToolSpec.java
deleted file mode 100644
index b7dfcd5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractBinaryToolSpec.java
+++ /dev/null
@@ -1,59 +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.nativebinaries.internal;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class AbstractBinaryToolSpec implements BinaryToolSpec {
-    private List<String> args = new ArrayList<String>();
-    private List<String> systemArgs = new ArrayList<String>();
-    private File tempDir;
-
-    public File getTempDir() {
-        return tempDir;
-    }
-
-    public void setTempDir(File tempDir) {
-        this.tempDir = tempDir;
-    }
-
-    public List<String> getArgs() {
-        return args;
-    }
-
-    public void args(List<String> args) {
-        this.args.addAll(args);
-    }
-
-    public List<String> getSystemArgs() {
-        return systemArgs;
-    }
-
-    public void systemArgs(List<String> args) {
-       if(!systemArgs.containsAll(args)){
-           systemArgs.addAll(args);
-       }
-    }
-
-    public List<String> getAllArgs() {
-        List<String> allArgs = new ArrayList<String>(systemArgs.size() + args.size());
-        allArgs.addAll(systemArgs);
-        allArgs.addAll(args);
-        return allArgs;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectLibraryBinary.java
deleted file mode 100644
index ca29374..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectLibraryBinary.java
+++ /dev/null
@@ -1,104 +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.nativebinaries.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.AbstractFileCollection;
-import org.gradle.api.internal.tasks.DefaultTaskDependency;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public abstract class AbstractProjectLibraryBinary extends AbstractProjectNativeBinary implements LibraryBinaryInternal {
-
-    protected AbstractProjectLibraryBinary(Library library, Flavor flavor, ToolChainInternal toolChain, Platform targetPlatform, BuildType buildType,
-                                           BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        super(library, flavor, toolChain, targetPlatform, buildType, namingScheme, resolver);
-    }
-
-    protected boolean hasSources() {
-        for (LanguageSourceSet sourceSet : getSource()) {
-            if (!sourceSet.getSource().isEmpty()) {
-                return true;
-            }
-            if (sourceSet.hasBuildDependencies()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public FileCollection getHeaderDirs() {
-        return new AbstractFileCollection() {
-            public String getDisplayName() {
-                return String.format("Headers for %s", getName());
-            }
-
-            public Set<File> getFiles() {
-                Set<File> headerDirs = new LinkedHashSet<File>();
-                for (HeaderExportingSourceSet sourceSet : getSource().withType(HeaderExportingSourceSet.class)) {
-                    headerDirs.addAll(sourceSet.getExportedHeaders().getSrcDirs());
-                }
-                return headerDirs;
-            }
-
-            @Override
-            public TaskDependency getBuildDependencies() {
-                DefaultTaskDependency dependency = new DefaultTaskDependency();
-                for (HeaderExportingSourceSet sourceSet : getSource().withType(HeaderExportingSourceSet.class)) {
-                    dependency.add(sourceSet.getBuildDependencies());
-                }
-                return dependency;
-            }
-        };
-    }
-
-    protected abstract class LibraryOutputs extends AbstractFileCollection {
-        public final Set<File> getFiles() {
-            if (hasOutputs()) {
-                return getOutputs();
-            }
-            return Collections.emptySet();
-        }
-
-        public final String getDisplayName() {
-            return AbstractProjectLibraryBinary.this.toString();
-        }
-
-        public final TaskDependency getBuildDependencies() {
-            if (hasOutputs()) {
-                return AbstractProjectLibraryBinary.this.getBuildDependencies();
-            }
-            return new DefaultTaskDependency();
-        }
-
-        protected abstract boolean hasOutputs();
-
-        protected abstract Set<File> getOutputs();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeBinary.java
deleted file mode 100644
index 0f0cc9a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeBinary.java
+++ /dev/null
@@ -1,161 +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.nativebinaries.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.AbstractBuildableModelElement;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.resolve.NativeBinaryResolveResult;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public abstract class AbstractProjectNativeBinary extends AbstractBuildableModelElement implements ProjectNativeBinaryInternal {
-    private final ProjectNativeComponent component;
-    private final NotationParser<Object, Set<LanguageSourceSet>> sourcesNotationParser = SourceSetNotationParser.parser();
-    private final Set<? super Object> libs = new LinkedHashSet<Object>();
-    private final DomainObjectSet<LanguageSourceSet> source = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
-    private final DefaultTool linker = new DefaultTool();
-    private final DefaultTool staticLibArchiver = new DefaultTool();
-    private final NativeBinaryTasks tasks = new DefaultNativeBinaryTasks();
-    private final BinaryNamingScheme namingScheme;
-    private final Flavor flavor;
-    private final ToolChainInternal toolChain;
-    private final Platform targetPlatform;
-    private final BuildType buildType;
-    private final NativeDependencyResolver resolver;
-    private boolean buildable;
-
-    protected AbstractProjectNativeBinary(ProjectNativeComponent owner, Flavor flavor, ToolChainInternal toolChain, Platform targetPlatform, BuildType buildType,
-                                          BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        this.component = owner;
-        this.namingScheme = namingScheme;
-        this.flavor = flavor;
-        this.toolChain = toolChain;
-        this.targetPlatform = targetPlatform;
-        this.buildType = buildType;
-        this.buildable = true;
-        this.resolver = resolver;
-        owner.getSource().all(new Action<LanguageSourceSet>() {
-            public void execute(LanguageSourceSet sourceSet) {
-                source.add(sourceSet);
-            }
-        });
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getDisplayName() {
-        return namingScheme.getDescription();
-    }
-
-    public String getName() {
-        return namingScheme.getLifecycleTaskName();
-    }
-
-    public ProjectNativeComponent getComponent() {
-        return component;
-    }
-
-    public Flavor getFlavor() {
-        return flavor;
-    }
-
-    public ToolChainInternal getToolChain() {
-        return toolChain;
-    }
-
-    public Platform getTargetPlatform() {
-        return targetPlatform;
-    }
-
-    public BuildType getBuildType() {
-        return buildType;
-    }
-
-    public DomainObjectSet<LanguageSourceSet> getSource() {
-        return source;
-    }
-
-    public void source(Object sources) {
-        source.addAll(sourcesNotationParser.parseNotation(sources));
-    }
-
-    public Tool getLinker() {
-        return linker;
-    }
-
-    public Tool getStaticLibArchiver() {
-        return staticLibArchiver;
-    }
-
-    public NativeBinaryTasks getTasks() {
-        return tasks;
-    }
-
-    public BinaryNamingScheme getNamingScheme() {
-        return namingScheme;
-    }
-
-    public Collection<NativeDependencySet> getLibs() {
-        return resolve(source.withType(DependentSourceSet.class)).getAllResults();
-    }
-
-    public Collection<NativeDependencySet> getLibs(DependentSourceSet sourceSet) {
-        return resolve(Collections.singleton(sourceSet)).getAllResults();
-    }
-
-    public void lib(Object notation) {
-        libs.add(notation);
-    }
-
-    public Collection<LibraryBinary> getDependentBinaries() {
-        return resolve(source.withType(DependentSourceSet.class)).getAllLibraryBinaries();
-    }
-
-    private NativeBinaryResolveResult resolve(Collection<? extends DependentSourceSet> sourceSets) {
-        Set<? super Object> allLibs = new LinkedHashSet<Object>(libs);
-        for (DependentSourceSet dependentSourceSet : sourceSets) {
-            allLibs.addAll(dependentSourceSet.getLibs());
-        }
-        NativeBinaryResolveResult resolution = new NativeBinaryResolveResult(this, allLibs);
-        resolver.resolve(resolution);
-        return resolution;
-    }
-
-    public boolean isBuildable() {
-        return buildable;
-    }
-
-    public void setBuildable(boolean buildable) {
-        this.buildable = buildable;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeComponent.java
deleted file mode 100644
index 140b980..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractProjectNativeComponent.java
+++ /dev/null
@@ -1,73 +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.nativebinaries.internal;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.nativebinaries.NativeBinary;
-import org.gradle.util.GUtil;
-
-import java.util.Set;
-
-public abstract class AbstractProjectNativeComponent implements ProjectNativeComponentInternal {
-    private final NotationParser<Object, Set<LanguageSourceSet>> sourcesNotationParser = SourceSetNotationParser.parser();
-    private final NativeProjectComponentIdentifier id;
-    private final DomainObjectSet<LanguageSourceSet> sourceSets;
-    private final DefaultDomainObjectSet<NativeBinary> binaries;
-
-    private String baseName;
-
-    public AbstractProjectNativeComponent(NativeProjectComponentIdentifier id) {
-        this.id = id;
-        this.sourceSets = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
-        binaries = new DefaultDomainObjectSet<NativeBinary>(NativeBinary.class);
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getProjectPath() {
-        return id.getProjectPath();
-    }
-
-    public String getName() {
-        return id.getName();
-    }
-
-    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, getName());
-    }
-
-    public void setBaseName(String baseName) {
-        this.baseName = baseName;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractTargetedProjectNativeComponent.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractTargetedProjectNativeComponent.java
deleted file mode 100644
index 333db6f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/AbstractTargetedProjectNativeComponent.java
+++ /dev/null
@@ -1,82 +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.nativebinaries.internal;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Named;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public abstract class AbstractTargetedProjectNativeComponent extends AbstractProjectNativeComponent implements TargetedNativeComponentInternal {
-
-    private final Set<String> targetPlatforms = new HashSet<String>();
-    private final Set<String> buildTypes = new HashSet<String>();
-    private final Set<String> flavors = new HashSet<String>();
-
-    public AbstractTargetedProjectNativeComponent(NativeProjectComponentIdentifier id) {
-        super(id);
-    }
-
-    public void targetFlavors(String... flavorSelectors) {
-        Collections.addAll(flavors, flavorSelectors);
-    }
-
-    public void targetPlatforms(String... platformSelectors) {
-        Collections.addAll(targetPlatforms, platformSelectors);
-    }
-
-    public void targetBuildTypes(String... buildTypeSelectors) {
-        Collections.addAll(buildTypes, buildTypeSelectors);
-    }
-
-    public Set<Flavor> chooseFlavors(Set<? extends Flavor> candidates) {
-        return chooseElements(Flavor.class, candidates, flavors);
-    }
-
-    public Set<BuildType> chooseBuildTypes(Set<? extends BuildType> candidates) {
-        return chooseElements(BuildType.class, candidates, buildTypes);
-    }
-
-    public Set<Platform> choosePlatforms(Set<? extends Platform> candidates) {
-        return chooseElements(Platform.class, candidates, targetPlatforms);
-    }
-
-    private <T extends Named> Set<T> chooseElements(Class<T> type, Set<? extends T> candidates, final Set<String> names) {
-        if (names.isEmpty()) {
-            return new LinkedHashSet<T>(candidates);
-        }
-
-        Set<String> unusedNames = new HashSet<String>(names);
-        Set<T> chosen = new LinkedHashSet<T>();
-        for (T candidate : candidates) {
-            if (unusedNames.remove(candidate.getName())) {
-                chosen.add(candidate);
-            }
-        }
-
-        if (!unusedNames.isEmpty()) {
-            throw new InvalidUserDataException(String.format("Invalid %s: '%s'", type.getSimpleName(), unusedNames.iterator().next()));
-        }
-
-        return chosen;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/BinaryToolSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/BinaryToolSpec.java
deleted file mode 100644
index 50485c3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/BinaryToolSpec.java
+++ /dev/null
@@ -1,39 +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.nativebinaries.internal;
-
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-
-import java.io.File;
-import java.util.List;
-
-public interface BinaryToolSpec extends CompileSpec {
-    File getTempDir();
-
-    void setTempDir(File tempDir);
-
-    List<String> getArgs();
-
-    void args(List<String> args);
-
-    List<String> getSystemArgs();
-
-    void systemArgs(List<String> args);
-
-    List<String> getAllArgs();
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildType.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildType.java
deleted file mode 100644
index 279cdcc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildType.java
+++ /dev/null
@@ -1,39 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.BuildType;
-
-public class DefaultBuildType implements BuildType {
-    private final String name;
-
-    public DefaultBuildType(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getDisplayName() {
-        return String.format("build type '%s'", name);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeContainer.java
deleted file mode 100644
index c718868..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeContainer.java
+++ /dev/null
@@ -1,33 +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.nativebinaries.internal;
-
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.BuildTypeContainer;
-
-public class DefaultBuildTypeContainer extends AbstractNamedDomainObjectContainer<BuildType> implements BuildTypeContainer {
-    public DefaultBuildTypeContainer(Instantiator instantiator) {
-        super(BuildType.class, instantiator);
-    }
-
-    @Override
-    protected BuildType doCreate(String name) {
-        return getInstantiator().newInstance(DefaultBuildType.class, name);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutable.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutable.java
deleted file mode 100755
index 928fa14..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutable.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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.Executable;
-
-public class DefaultExecutable extends AbstractTargetedProjectNativeComponent implements Executable {
-    public DefaultExecutable(NativeProjectComponentIdentifier id) {
-        super(id);
-    }
-
-    public String getDisplayName() {
-        return String.format("executable '%s'", getName());
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutableContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutableContainer.java
deleted file mode 100644
index 4adb2c4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultExecutableContainer.java
+++ /dev/null
@@ -1,38 +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.nativebinaries.internal;
-
-import org.gradle.api.Project;
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.Executable;
-import org.gradle.nativebinaries.ExecutableContainer;
-
-public class DefaultExecutableContainer extends AbstractNamedDomainObjectContainer<Executable> implements ExecutableContainer {
-    private final Project project;
-
-    public DefaultExecutableContainer(Instantiator instantiator, Project project) {
-        super(Executable.class, instantiator);
-        this.project = project;
-    }
-
-    @Override
-    protected Executable doCreate(String name) {
-        NativeProjectComponentIdentifier id = new NativeProjectComponentIdentifier(project.getPath(), name);
-        return getInstantiator().newInstance(DefaultExecutable.class, id);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavor.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavor.java
deleted file mode 100644
index 1117813..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavor.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.Flavor;
-
-public class DefaultFlavor implements Flavor {
-    public static final String DEFAULT = "default";
-    private final String name;
-
-    public DefaultFlavor(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getDisplayName() {
-        return String.format("flavor '%s'", name);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavorContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavorContainer.java
deleted file mode 100644
index 1e62fc9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultFlavorContainer.java
+++ /dev/null
@@ -1,33 +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.nativebinaries.internal;
-
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.FlavorContainer;
-
-public class DefaultFlavorContainer extends AbstractNamedDomainObjectContainer<Flavor> implements FlavorContainer {
-    public DefaultFlavorContainer(Instantiator instantiator) {
-        super(Flavor.class, instantiator);
-    }
-
-    @Override
-    protected Flavor doCreate(String name) {
-        return getInstantiator().newInstance(DefaultFlavor.class, name);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibrary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibrary.java
deleted file mode 100755
index 2ee1fb3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibrary.java
+++ /dev/null
@@ -1,42 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-public class DefaultLibrary extends AbstractTargetedProjectNativeComponent implements Library {
-    public DefaultLibrary(NativeProjectComponentIdentifier id) {
-        super(id);
-    }
-
-    public String getDisplayName() {
-        return String.format("library '%s'", getName());
-    }
-
-    public NativeLibraryRequirement getShared() {
-        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "shared");
-    }
-
-    public NativeLibraryRequirement getStatic() {
-        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "static");
-    }
-
-    public NativeLibraryRequirement getApi() {
-        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "api");
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibraryContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibraryContainer.java
deleted file mode 100644
index 90c573a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLibraryContainer.java
+++ /dev/null
@@ -1,38 +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.nativebinaries.internal;
-
-import org.gradle.api.Project;
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.LibraryContainer;
-
-public class DefaultLibraryContainer extends AbstractNamedDomainObjectContainer<Library> implements LibraryContainer {
-    private final Project project;
-
-    public DefaultLibraryContainer(Instantiator instantiator, Project project) {
-        super(Library.class, instantiator);
-        this.project = project;
-    }
-
-    @Override
-    protected Library doCreate(String name) {
-        NativeProjectComponentIdentifier id = new NativeProjectComponentIdentifier(project.getPath(), name);
-        return getInstantiator().newInstance(DefaultLibrary.class, id);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLinkerSpec.java
deleted file mode 100644
index 990f025..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultLinkerSpec.java
+++ /dev/null
@@ -1,68 +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.nativebinaries.internal;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class DefaultLinkerSpec extends AbstractBinaryToolSpec implements LinkerSpec {
-
-    private final List<File> objectFiles = new ArrayList<File>();
-    private final List<File> libraries = new ArrayList<File>();
-    private final List<File> libraryPath = new ArrayList<File>();
-    private File outputFile;
-
-    public List<File> getObjectFiles() {
-        return objectFiles;
-    }
-
-    public void objectFiles(Iterable<File> objectFiles) {
-        addAll(this.objectFiles, objectFiles);
-    }
-
-    public List<File> getLibraries() {
-        return libraries;
-    }
-
-    public void libraries(Iterable<File> libraries) {
-        addAll(this.libraries, libraries);
-    }
-
-    public List<File> getLibraryPath() {
-        return libraryPath;
-    }
-
-    public void libraryPath(File... libraryPath) {
-        Collections.addAll(this.libraryPath, libraryPath);
-    }
-
-    public File getOutputFile() {
-        return outputFile;
-    }
-
-    public void setOutputFile(File outputFile) {
-        this.outputFile = outputFile;
-    }
-
-    private void addAll(List<File> list, Iterable<File> iterable) {
-        for (File file : iterable) {
-            list.add(file);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasks.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasks.java
deleted file mode 100644
index 8783210..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasks.java
+++ /dev/null
@@ -1,56 +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.nativebinaries.internal;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Task;
-import org.gradle.api.UnknownDomainObjectException;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.nativebinaries.NativeBinaryTasks;
-import org.gradle.nativebinaries.tasks.AbstractLinkTask;
-import org.gradle.nativebinaries.tasks.BuildBinaryTask;
-import org.gradle.nativebinaries.tasks.CreateStaticLibrary;
-
-public class DefaultNativeBinaryTasks extends DefaultDomainObjectSet<Task> implements NativeBinaryTasks {
-    public DefaultNativeBinaryTasks() {
-        super(Task.class);
-    }
-
-    public AbstractLinkTask getLink() {
-        return findOnlyWithType(AbstractLinkTask.class);
-    }
-
-    public CreateStaticLibrary getCreateStaticLib() {
-        return findOnlyWithType(CreateStaticLibrary.class);
-    }
-
-    public BuildBinaryTask getBuilder() {
-        BuildBinaryTask link = getLink();
-        return link == null ? getCreateStaticLib() : link;
-    }
-
-    private <T extends Task> T findOnlyWithType(Class<T> type) {
-        DomainObjectSet<T> tasks = withType(type);
-        if (tasks.size() == 0) {
-            return null;
-        }
-        if (tasks.size() > 1) {
-            throw new UnknownDomainObjectException(String.format("Multiple task with type '%s' found", type.getSimpleName()));
-        }
-        return tasks.iterator().next();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultStaticLibraryArchiverSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultStaticLibraryArchiverSpec.java
deleted file mode 100644
index 416c788..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultStaticLibraryArchiverSpec.java
+++ /dev/null
@@ -1,45 +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.nativebinaries.internal;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DefaultStaticLibraryArchiverSpec extends AbstractBinaryToolSpec implements StaticLibraryArchiverSpec {
-
-    private final List<File> objectFiles = new ArrayList<File>();
-    private File outputFile;
-
-    public List<File> getObjectFiles() {
-        return objectFiles;
-    }
-
-    public void objectFiles(Iterable<File> objectFiles) {
-        for (File objectFile : objectFiles) {
-            this.objectFiles.add(objectFile);
-        }
-    }
-
-    public File getOutputFile() {
-        return outputFile;
-    }
-
-    public void setOutputFile(File outputFile) {
-        this.outputFile = outputFile;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultTool.java
deleted file mode 100644
index 1669408..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/DefaultTool.java
+++ /dev/null
@@ -1,37 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.Tool;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A tool that is part of a tool chain (compiler, linker, assembler, etc).
- */
-public class DefaultTool implements Tool {
-    private final ArrayList<String> args = new ArrayList<String>();
-
-    public List<String> getArgs() {
-        return args;
-    }
-
-    public void args(String... args) {
-        Collections.addAll(this.args, args);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LibraryBinaryInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LibraryBinaryInternal.java
deleted file mode 100644
index 6f1f59c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LibraryBinaryInternal.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.nativebinaries.LibraryBinary;
-
-public interface LibraryBinaryInternal extends LibraryBinary {
-
-    FileCollection getHeaderDirs();
-
-    FileCollection getLinkFiles();
-
-    FileCollection getRuntimeFiles();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LinkerSpec.java
deleted file mode 100644
index 202800c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/LinkerSpec.java
+++ /dev/null
@@ -1,44 +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.nativebinaries.internal;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A high level interface to the linker, specifying what is to be linked and how.
- */
-public interface LinkerSpec extends BinaryToolSpec {
-
-    List<File> getObjectFiles();
-
-    void objectFiles(Iterable<File> objectFiles);
-
-    List<File> getLibraries();
-
-    void libraries(Iterable<File> libraries);
-
-    // TODO:DAZ Work out how to test this
-    List<File> getLibraryPath();
-
-    void libraryPath(File... libraryPath);
-
-    File getOutputFile();
-
-    void setOutputFile(File outputFile);
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeBinaryServices.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeBinaryServices.java
deleted file mode 100644
index 66150f5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeBinaryServices.java
+++ /dev/null
@@ -1,37 +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.nativebinaries.internal;
-
-import org.gradle.internal.service.ServiceRegistration;
-import org.gradle.internal.service.scopes.PluginServiceRegistry;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolverServices;
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.DefaultVisualStudioLocator;
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.DefaultWindowsSdkLocator;
-
-public class NativeBinaryServices implements PluginServiceRegistry {
-    public void registerGlobalServices(ServiceRegistration registration) {
-    }
-
-    public void registerBuildServices(ServiceRegistration registration) {
-        registration.add(DefaultVisualStudioLocator.class);
-        registration.add(DefaultWindowsSdkLocator.class);
-    }
-
-    public void registerProjectServices(ServiceRegistration registration) {
-        registration.addProvider(new NativeDependencyResolverServices());
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeProjectComponentIdentifier.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeProjectComponentIdentifier.java
deleted file mode 100644
index 5d9c294..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/NativeProjectComponentIdentifier.java
+++ /dev/null
@@ -1,64 +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.nativebinaries.internal;
-
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-
-/**
- * An identifier for a native component that is built as part of the current build.
- */
-public class NativeProjectComponentIdentifier implements ProjectComponentIdentifier {
-    private final String projectPath;
-    private final String name;
-
-    public NativeProjectComponentIdentifier(String projectPath, String name) {
-        this.projectPath = projectPath;
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getProjectPath() {
-        return projectPath;
-    }
-
-    public String getDisplayName() {
-        return String.format("Project native component: %s.%s", projectPath, name);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof NativeProjectComponentIdentifier)) {
-            return false;
-        }
-
-        NativeProjectComponentIdentifier that = (NativeProjectComponentIdentifier) o;
-        return name.equals(that.name) && projectPath.equals(that.projectPath);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = projectPath.hashCode();
-        result = 31 * result + name.hashCode();
-        return result;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectExecutableBinary.java
deleted file mode 100644
index 185697b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectExecutableBinary.java
+++ /dev/null
@@ -1,49 +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.nativebinaries.internal;
-
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Executable;
-import org.gradle.nativebinaries.ExecutableBinary;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-
-public class ProjectExecutableBinary extends AbstractProjectNativeBinary implements ExecutableBinary {
-    private File executableFile;
-
-    public ProjectExecutableBinary(Executable executable, Flavor flavor, ToolChainInternal toolChain, Platform platform, BuildType buildType,
-                                   BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        super(executable, flavor, toolChain, platform, buildType, namingScheme, resolver);
-    }
-
-    public File getExecutableFile() {
-        return executableFile;
-    }
-
-    public void setExecutableFile(File executableFile) {
-        this.executableFile = executableFile;
-    }
-
-    public File getPrimaryOutput() {
-        return getExecutableFile();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryInternal.java
deleted file mode 100644
index a9c976d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryInternal.java
+++ /dev/null
@@ -1,36 +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.nativebinaries.internal;
-
-import org.gradle.language.DependentSourceSet;
-import org.gradle.language.base.internal.BinaryInternal;
-import org.gradle.nativebinaries.LibraryBinary;
-import org.gradle.nativebinaries.NativeDependencySet;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-
-import java.io.File;
-import java.util.Collection;
-
-public interface ProjectNativeBinaryInternal extends ProjectNativeBinary, BinaryInternal {
-    File getPrimaryOutput();
-
-    Collection<NativeDependencySet> getLibs(DependentSourceSet sourceSet);
-
-    void setBuildable(boolean buildable);
-
-    Collection<LibraryBinary> getDependentBinaries();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeComponentInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeComponentInternal.java
deleted file mode 100644
index 833e311..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeComponentInternal.java
+++ /dev/null
@@ -1,24 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-public interface ProjectNativeComponentInternal extends ProjectNativeComponent {
-
-    // TODO:DAZ Use BuildComponentIdentifier
-    String getProjectPath();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeLibraryRequirement.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeLibraryRequirement.java
deleted file mode 100644
index fdfb92f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectNativeLibraryRequirement.java
+++ /dev/null
@@ -1,48 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-public class ProjectNativeLibraryRequirement implements NativeLibraryRequirement {
-    private final String projectPath;
-    private final String libraryName;
-    private final String linkage;
-
-    public ProjectNativeLibraryRequirement(String libraryName, String linkage) {
-        this.projectPath = null;
-        this.libraryName = libraryName;
-        this.linkage = linkage;
-    }
-
-    public ProjectNativeLibraryRequirement(String projectPath, String libraryName, String linkage) {
-        this.projectPath = projectPath;
-        this.libraryName = libraryName;
-        this.linkage = linkage;
-    }
-
-    public String getProjectPath() {
-        return projectPath;
-    }
-
-    public String getLibraryName() {
-        return libraryName;
-    }
-
-    public String getLinkage() {
-        return linkage;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinary.java
deleted file mode 100644
index 53b65b4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinary.java
+++ /dev/null
@@ -1,120 +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.nativebinaries.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.rc.WindowsResourceSet;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.SharedLibraryBinary;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Set;
-
-public class ProjectSharedLibraryBinary extends AbstractProjectLibraryBinary implements SharedLibraryBinary {
-    private File sharedLibraryFile;
-    private File sharedLibraryLinkFile;
-
-    public ProjectSharedLibraryBinary(Library library, Flavor flavor, ToolChainInternal toolChain, Platform platform, BuildType buildType,
-                                      BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        super(library, flavor, toolChain, platform, buildType, namingScheme, resolver);
-    }
-
-    public File getSharedLibraryFile() {
-        return sharedLibraryFile;
-    }
-
-    public void setSharedLibraryFile(File sharedLibraryFile) {
-        this.sharedLibraryFile = sharedLibraryFile;
-    }
-
-    public File getSharedLibraryLinkFile() {
-        return sharedLibraryLinkFile;
-    }
-
-    public void setSharedLibraryLinkFile(File sharedLibraryLinkFile) {
-        this.sharedLibraryLinkFile = sharedLibraryLinkFile;
-    }
-
-    public File getPrimaryOutput() {
-        return getSharedLibraryFile();
-    }
-
-    public FileCollection getLinkFiles() {
-        return new SharedLibraryLinkOutputs();
-    }
-
-    public FileCollection getRuntimeFiles() {
-        return new SharedLibraryRuntimeOutputs();
-    }
-
-    private class SharedLibraryLinkOutputs extends LibraryOutputs {
-        @Override
-        protected boolean hasOutputs() {
-            return hasSources() && !isResourceOnly();
-        }
-
-        @Override
-        protected Set<File> getOutputs() {
-            return Collections.singleton(getSharedLibraryLinkFile());
-        }
-
-        private boolean isResourceOnly() {
-            return hasResources() && !hasExportedSymbols();
-        }
-
-        private boolean hasResources() {
-            for (WindowsResourceSet windowsResourceSet : getSource().withType(WindowsResourceSet.class)) {
-                if (!windowsResourceSet.getSource().isEmpty()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private boolean hasExportedSymbols() {
-            // TODO:DAZ This is a very rough approximation: actually inspect the binary to determine if there are exported symbols
-            for (LanguageSourceSet languageSourceSet : getSource()) {
-                if (!(languageSourceSet instanceof WindowsResourceSet)) {
-                    if (!languageSourceSet.getSource().isEmpty()) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-    }
-
-    private class SharedLibraryRuntimeOutputs extends LibraryOutputs {
-        @Override
-        protected boolean hasOutputs() {
-            return hasSources();
-        }
-
-        @Override
-        protected Set<File> getOutputs() {
-            return Collections.singleton(getSharedLibraryFile());
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinary.java
deleted file mode 100644
index 9a08bc7..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinary.java
+++ /dev/null
@@ -1,86 +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.nativebinaries.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ProjectStaticLibraryBinary extends AbstractProjectLibraryBinary implements StaticLibraryBinaryInternal {
-    private final List<FileCollection> additionalLinkFiles = new ArrayList<FileCollection>();
-    private File staticLibraryFile;
-
-    public ProjectStaticLibraryBinary(Library library, Flavor flavor, ToolChainInternal toolChain, Platform platform, BuildType buildType,
-                                      BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        super(library, flavor, toolChain, platform, buildType, namingScheme, resolver);
-    }
-
-    public File getStaticLibraryFile() {
-        return staticLibraryFile;
-    }
-
-    public void setStaticLibraryFile(File staticLibraryFile) {
-        this.staticLibraryFile = staticLibraryFile;
-    }
-
-    public File getPrimaryOutput() {
-        return getStaticLibraryFile();
-    }
-
-    public void additionalLinkFiles(FileCollection files) {
-        this.additionalLinkFiles.add(files);
-    }
-
-    public FileCollection getLinkFiles() {
-        return new StaticLibraryLinkOutputs();
-    }
-
-    public FileCollection getRuntimeFiles() {
-        return new SimpleFileCollection();
-    }
-
-    private class StaticLibraryLinkOutputs extends LibraryOutputs {
-        @Override
-        protected boolean hasOutputs() {
-            return hasSources() || !additionalLinkFiles.isEmpty();
-        }
-
-        @Override
-        protected Set<File> getOutputs() {
-            Set<File> allFiles = new LinkedHashSet<File>();
-            if (hasSources()) {
-                allFiles.add(getStaticLibraryFile());
-            }
-            for (FileCollection resourceSet : additionalLinkFiles) {
-                allFiles.addAll(resourceSet.getFiles());
-            }
-            return allFiles;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SharedLibraryLinkerSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SharedLibraryLinkerSpec.java
deleted file mode 100644
index 09fb3f7..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SharedLibraryLinkerSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.internal;
-
-public interface SharedLibraryLinkerSpec extends LinkerSpec {
-    String getInstallName();
-
-    void setInstallName(String path);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParser.java
deleted file mode 100644
index f01a520..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParser.java
+++ /dev/null
@@ -1,73 +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.nativebinaries.internal;
-
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.TypeInfo;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class SourceSetNotationParser {
-    public static NotationParser<Object, 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 LinkedHashSet<LanguageSourceSet>(notation);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryArchiverSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryArchiverSpec.java
deleted file mode 100644
index 67983ae..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryArchiverSpec.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.nativebinaries.internal;
-
-import java.io.File;
-import java.util.List;
-
-public interface StaticLibraryArchiverSpec extends BinaryToolSpec {
-
-    File getOutputFile();
-
-    void setOutputFile(File outputFile);
-
-    List<File> getObjectFiles();
-
-    void objectFiles(Iterable<File> source);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryBinaryInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryBinaryInternal.java
deleted file mode 100644
index dcaeac6..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/StaticLibraryBinaryInternal.java
+++ /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.nativebinaries.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.nativebinaries.StaticLibraryBinary;
-
-public interface StaticLibraryBinaryInternal extends StaticLibraryBinary {
-    /**
-     * Add some additional files required at link time.
-     */
-    void additionalLinkFiles(FileCollection files);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/TargetedNativeComponentInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/TargetedNativeComponentInternal.java
deleted file mode 100644
index a6a6111..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/TargetedNativeComponentInternal.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal;
-
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.TargetedNativeComponent;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.util.Set;
-
-public interface TargetedNativeComponentInternal extends TargetedNativeComponent {
-    Set<Flavor> chooseFlavors(Set<? extends Flavor> candidates);
-    Set<Platform> choosePlatforms(Set<? extends Platform> candidates);
-    Set<BuildType> chooseBuildTypes(Set<? extends BuildType> candidates);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ApplySourceSetConventions.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ApplySourceSetConventions.java
deleted file mode 100644
index 1448d97..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ApplySourceSetConventions.java
+++ /dev/null
@@ -1,50 +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.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-
-/**
- * Applies source set conventions when no source directories are explicitly configured.
- */
-public class ApplySourceSetConventions implements Action<ProjectInternal> {
-    public void execute(ProjectInternal project) {
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        for (FunctionalSourceSet functionalSourceSet : projectSourceSet) {
-            for (LanguageSourceSet languageSourceSet : functionalSourceSet) {
-                // Only apply default locations when none explicitly configured
-                if (languageSourceSet.getSource().getSrcDirs().isEmpty()) {
-                    languageSourceSet.getSource().srcDir(String.format("src/%s/%s", functionalSourceSet.getName(), languageSourceSet.getName()));
-                }
-            }
-            for (HeaderExportingSourceSet headerSourceSet : functionalSourceSet.withType(HeaderExportingSourceSet.class)) {
-                // Only apply default locations when none explicitly configured
-                if (headerSourceSet.getExportedHeaders().getSrcDirs().isEmpty()) {
-                    headerSourceSet.getExportedHeaders().srcDir(String.format("src/%s/headers", functionalSourceSet.getName()));
-                }
-
-                headerSourceSet.getImplicitHeaders().setSrcDirs(headerSourceSet.getSource().getSrcDirs());
-                headerSourceSet.getImplicitHeaders().include("**/*.h");
-            }
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ConfigureGeneratedSourceSets.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ConfigureGeneratedSourceSets.java
deleted file mode 100644
index 7bfa909..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ConfigureGeneratedSourceSets.java
+++ /dev/null
@@ -1,54 +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.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.api.Task;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.language.base.internal.LanguageSourceSetInternal;
-
-/**
- * Wires generator tasks into language source sets.
- */
-public class ConfigureGeneratedSourceSets implements Action<ProjectInternal> {
-    public void execute(ProjectInternal project) {
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        for (FunctionalSourceSet functionalSourceSet : projectSourceSet) {
-            for (LanguageSourceSetInternal languageSourceSet : functionalSourceSet.withType(LanguageSourceSetInternal.class)) {
-                Task generatorTask = languageSourceSet.getGeneratorTask();
-                if (generatorTask != null) {
-                    languageSourceSet.builtBy(generatorTask);
-                    maybeSetSourceDir(languageSourceSet.getSource(), generatorTask, "sourceDir");
-                    if (languageSourceSet instanceof HeaderExportingSourceSet) {
-                        maybeSetSourceDir(((HeaderExportingSourceSet) languageSourceSet).getExportedHeaders(), generatorTask, "headerDir");
-                    }
-                }
-            }
-        }
-    }
-
-    private void maybeSetSourceDir(SourceDirectorySet sourceSet, Task task, String propertyName) {
-        // TODO:DAZ Handle multiple output directories
-        Object value = task.property(propertyName);
-        if (value != null) {
-            sourceSet.srcDir(value);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypes.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypes.java
deleted file mode 100644
index 50841f9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypes.java
+++ /dev/null
@@ -1,30 +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.nativebinaries.internal.configure;
-
-import org.gradle.model.ModelFinalizer;
-import org.gradle.nativebinaries.BuildTypeContainer;
-
-public class CreateDefaultBuildTypes extends ModelFinalizer {
-
-    @SuppressWarnings("UnusedDeclaration")
-    void createDefaultPlatforms(BuildTypeContainer buildTypes) {
-        if (buildTypes.isEmpty()) {
-            buildTypes.create("debug");
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavors.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavors.java
deleted file mode 100644
index 0f3a319..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavors.java
+++ /dev/null
@@ -1,32 +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.nativebinaries.internal.configure;
-
-import org.gradle.model.ModelFinalizer;
-import org.gradle.nativebinaries.FlavorContainer;
-import org.gradle.nativebinaries.internal.DefaultFlavor;
-
-public class CreateDefaultFlavors extends ModelFinalizer {
-
-    @SuppressWarnings("UnusedDeclaration")
-    void createDefaultFlavor(FlavorContainer flavors) {
-        if (flavors.isEmpty()) {
-            flavors.create(DefaultFlavor.DEFAULT);
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatform.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatform.java
deleted file mode 100644
index cc05654..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatform.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal.configure;
-
-import org.gradle.model.ModelFinalizer;
-import org.gradle.nativebinaries.platform.PlatformContainer;
-
-public class CreateDefaultPlatform extends ModelFinalizer {
-    @SuppressWarnings("UnusedDeclaration")
-    void createDefaultPlatforms(PlatformContainer platforms) {
-        if (platforms.isEmpty()) {
-            platforms.create("current");
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateNativeBinaries.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateNativeBinaries.java
deleted file mode 100644
index 14b6379..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/CreateNativeBinaries.java
+++ /dev/null
@@ -1,79 +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.nativebinaries.internal.configure;
-
-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.language.base.internal.BinaryNamingSchemeBuilder;
-import org.gradle.language.base.internal.DefaultBinaryNamingSchemeBuilder;
-import org.gradle.model.ModelRule;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.PlatformContainer;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class CreateNativeBinaries extends ModelRule {
-    private final Instantiator instantiator;
-    private final ProjectInternal project;
-    private final NativeDependencyResolver resolver;
-
-    public CreateNativeBinaries(Instantiator instantiator, ProjectInternal project, NativeDependencyResolver resolver) {
-        this.instantiator = instantiator;
-        this.project = project;
-        this.resolver = resolver;
-    }
-
-    public void create(BinaryContainer binaries, ToolChainRegistryInternal toolChains,
-                       PlatformContainer platforms, BuildTypeContainer buildTypes, FlavorContainer flavors) {
-        // TODO:DAZ Work out the right way to make these containers available to binaries.all
-        project.getExtensions().add("platforms", platforms);
-        project.getExtensions().add("buildTypes", buildTypes);
-        project.getExtensions().add("flavors", flavors);
-
-        Action<ProjectNativeBinary> configureBinaryAction = new ProjectNativeBinaryInitializer(project);
-        NativeBinariesFactory factory = new DefaultNativeBinariesFactory(instantiator, configureBinaryAction, resolver);
-        BinaryNamingSchemeBuilder namingSchemeBuilder = new DefaultBinaryNamingSchemeBuilder();
-        Action<ProjectNativeComponent> createBinariesAction =
-                new ProjectNativeComponentInitializer(factory, namingSchemeBuilder, toolChains, platforms, buildTypes, flavors);
-
-        for (ProjectNativeComponent component : allComponents()) {
-            createBinariesAction.execute(component);
-            binaries.addAll(component.getBinaries());
-        }
-    }
-
-    private Collection<ProjectNativeComponent> allComponents() {
-        ExecutableContainer executables = project.getExtensions().getByType(ExecutableContainer.class);
-        LibraryContainer libraries = project.getExtensions().getByType(LibraryContainer.class);
-
-        List<ProjectNativeComponent> components = new ArrayList<ProjectNativeComponent>();
-        for (Library library : libraries) {
-            components.add(library);
-        }
-        for (Executable executable : executables) {
-            components.add(executable);
-        }
-        return components;
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactory.java
deleted file mode 100644
index 7d3282b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactory.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.base.internal.BinaryNamingSchemeBuilder;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.ProjectExecutableBinary;
-import org.gradle.nativebinaries.internal.ProjectSharedLibraryBinary;
-import org.gradle.nativebinaries.internal.ProjectStaticLibraryBinary;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-
-class DefaultNativeBinariesFactory implements NativeBinariesFactory {
-    private final Instantiator instantiator;
-    private final Action<ProjectNativeBinary> configureAction;
-    private final NativeDependencyResolver resolver;
-
-    DefaultNativeBinariesFactory(Instantiator instantiator, Action<ProjectNativeBinary> configureAction, NativeDependencyResolver resolver) {
-        this.configureAction = configureAction;
-        this.instantiator = instantiator;
-        this.resolver = resolver;
-    }
-
-    public void createNativeBinaries(ProjectNativeComponent component, BinaryNamingSchemeBuilder namingScheme, ToolChain toolChain, Platform platform, BuildType buildType, Flavor flavor) {
-        if (component instanceof Library) {
-            createNativeBinary(ProjectSharedLibraryBinary.class, component, namingScheme.withTypeString("SharedLibrary").build(), toolChain, platform, buildType, flavor);
-            createNativeBinary(ProjectStaticLibraryBinary.class, component, namingScheme.withTypeString("StaticLibrary").build(), toolChain, platform, buildType, flavor);
-        } else {
-            createNativeBinary(ProjectExecutableBinary.class, component, namingScheme.withTypeString("Executable").build(), toolChain, platform, buildType, flavor);
-        }
-    }
-
-    private void createNativeBinary(Class<? extends ProjectNativeBinary> type, ProjectNativeComponent component, BinaryNamingScheme namingScheme,
-                            ToolChain toolChain, Platform platform, BuildType buildType, Flavor flavor) {
-        ProjectNativeBinary nativeBinary = instantiator.newInstance(type, component, flavor, toolChain, platform, buildType, namingScheme, resolver);
-        setupDefaults(nativeBinary);
-        component.getBinaries().add(nativeBinary);
-    }
-
-    private void setupDefaults(ProjectNativeBinary nativeBinary) {
-        configureAction.execute(nativeBinary);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/NativeBinariesFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/NativeBinariesFactory.java
deleted file mode 100644
index c90d65d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/NativeBinariesFactory.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.configure;
-
-import org.gradle.language.base.internal.BinaryNamingSchemeBuilder;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-
-public interface NativeBinariesFactory {
-    void createNativeBinaries(ProjectNativeComponent component, BinaryNamingSchemeBuilder namingScheme, ToolChain toolChain, Platform platform, BuildType buildType, Flavor flavor);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializer.java
deleted file mode 100644
index e0da6c2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.api.Project;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.ExecutableBinary;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-import org.gradle.nativebinaries.SharedLibraryBinary;
-import org.gradle.nativebinaries.StaticLibraryBinary;
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-
-class ProjectNativeBinaryInitializer implements Action<ProjectNativeBinary> {
-    private final File binariesOutputDir;
-
-    ProjectNativeBinaryInitializer(Project project) {
-        binariesOutputDir = new File(project.getBuildDir(), "binaries");
-    }
-
-    public void execute(ProjectNativeBinary nativeBinary) {
-        ToolChainInternal tc = (ToolChainInternal) nativeBinary.getToolChain();
-        BinaryNamingScheme namingScheme = ((ProjectNativeBinaryInternal) nativeBinary).getNamingScheme();
-        File binaryOutputDir = new File(binariesOutputDir, namingScheme.getOutputDirectoryBase());
-        String baseName = nativeBinary.getComponent().getBaseName();
-
-        if (nativeBinary instanceof ExecutableBinary) {
-            ((ExecutableBinary) nativeBinary).setExecutableFile(new File(binaryOutputDir, tc.getExecutableName(baseName)));
-        } else if (nativeBinary instanceof SharedLibraryBinary) {
-            ((SharedLibraryBinary) nativeBinary).setSharedLibraryFile(new File(binaryOutputDir, tc.getSharedLibraryName(baseName)));
-            ((SharedLibraryBinary) nativeBinary).setSharedLibraryLinkFile(new File(binaryOutputDir, tc.getSharedLibraryLinkFileName(baseName)));
-        } else if (nativeBinary instanceof StaticLibraryBinary) {
-            ((StaticLibraryBinary) nativeBinary).setStaticLibraryFile(new File(binaryOutputDir, tc.getStaticLibraryName(baseName)));
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializer.java
deleted file mode 100644
index 92e1a17..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializer.java
+++ /dev/null
@@ -1,90 +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.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.language.base.internal.BinaryNamingSchemeBuilder;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-import org.gradle.nativebinaries.internal.TargetedNativeComponentInternal;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal;
-
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class ProjectNativeComponentInitializer implements Action<ProjectNativeComponent> {
-    private final NativeBinariesFactory factory;
-    private final ToolChainRegistryInternal toolChainRegistry;
-    private final Set<Platform> allPlatforms = new LinkedHashSet<Platform>();
-    private final Set<BuildType> allBuildTypes = new LinkedHashSet<BuildType>();
-    private final Set<Flavor> allFlavors = new LinkedHashSet<Flavor>();
-    private final BinaryNamingSchemeBuilder namingSchemeBuilder;
-
-    public ProjectNativeComponentInitializer(NativeBinariesFactory factory, BinaryNamingSchemeBuilder namingSchemeBuilder, ToolChainRegistryInternal toolChainRegistry,
-                                             Collection<? extends Platform> allPlatforms, Collection<? extends BuildType> allBuildTypes, Collection<? extends Flavor> allFlavors) {
-        this.factory = factory;
-        this.namingSchemeBuilder = namingSchemeBuilder;
-        this.toolChainRegistry = toolChainRegistry;
-        this.allPlatforms.addAll(allPlatforms);
-        this.allBuildTypes.addAll(allBuildTypes);
-        this.allFlavors.addAll(allFlavors);
-    }
-
-    public void execute(ProjectNativeComponent projectNativeComponent) {
-        TargetedNativeComponentInternal targetedComponent = (TargetedNativeComponentInternal) projectNativeComponent;
-        for (Platform platform : targetedComponent.choosePlatforms(allPlatforms)) {
-            ToolChain toolChain = toolChainRegistry.getForPlatform(platform);
-            for (BuildType buildType : targetedComponent.chooseBuildTypes(allBuildTypes)) {
-                for (Flavor flavor : targetedComponent.chooseFlavors(allFlavors)) {
-                    BinaryNamingSchemeBuilder namingScheme = initializeNamingScheme(targetedComponent, projectNativeComponent.getName(), platform, buildType, flavor);
-                    factory.createNativeBinaries(projectNativeComponent, namingScheme, toolChain, platform, buildType, flavor);
-                }
-            }
-        }
-    }
-
-    private BinaryNamingSchemeBuilder initializeNamingScheme(TargetedNativeComponentInternal component, String name, Platform platform, BuildType buildType, Flavor flavor) {
-        BinaryNamingSchemeBuilder builder = namingSchemeBuilder.withComponentName(name);
-        if (usePlatformDimension(component)) {
-            builder = builder.withVariantDimension(platform.getName());
-        }
-        if (useBuildTypeDimension(component)) {
-            builder = builder.withVariantDimension(buildType.getName());
-        }
-        if (useFlavorDimension(component)) {
-            builder = builder.withVariantDimension(flavor.getName());
-        }
-        return builder;
-    }
-
-    private boolean usePlatformDimension(TargetedNativeComponentInternal component) {
-        return component.choosePlatforms(allPlatforms).size() > 1;
-    }
-
-    private boolean useBuildTypeDimension(TargetedNativeComponentInternal component) {
-        return component.chooseBuildTypes(allBuildTypes).size() > 1;
-    }
-
-    private boolean useFlavorDimension(TargetedNativeComponentInternal component) {
-        return component.chooseFlavors(allFlavors).size() > 1;
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/RepositoriesFactory.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/RepositoriesFactory.java
deleted file mode 100644
index 496e0c6..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/configure/RepositoriesFactory.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.configure;
-
-import org.gradle.api.Action;
-import org.gradle.api.NamedDomainObjectFactory;
-import org.gradle.api.Namer;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
-import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.model.internal.Inputs;
-import org.gradle.model.internal.ModelCreator;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.prebuilt.DefaultPrebuiltLibraries;
-import org.gradle.nativebinaries.internal.prebuilt.PrebuiltLibraryInitializer;
-import org.gradle.nativebinaries.platform.PlatformContainer;
-
-public class RepositoriesFactory implements ModelCreator<Repositories> {
-    private final Instantiator instantiator;
-    private final FileResolver fileResolver;
-
-    public RepositoriesFactory(Instantiator instantiator, FileResolver fileResolver) {
-        this.instantiator = instantiator;
-        this.fileResolver = fileResolver;
-    }
-
-    public Repositories create(Inputs inputs) {
-        FlavorContainer flavors = inputs.get(0, FlavorContainer.class);
-        PlatformContainer platforms = inputs.get(1, PlatformContainer.class);
-        BuildTypeContainer buildTypes = inputs.get(2, BuildTypeContainer.class);
-        Action<PrebuiltLibrary> initializer = new PrebuiltLibraryInitializer(instantiator, platforms, buildTypes, flavors);
-        return new DefaultRepositories(instantiator, fileResolver, initializer);
-    }
-
-    public Class<Repositories> getType() {
-        return Repositories.class;
-    }
-
-    private static class DefaultRepositories extends DefaultPolymorphicDomainObjectContainer<ArtifactRepository> implements Repositories {
-        private DefaultRepositories(final Instantiator instantiator, final FileResolver fileResolver, final Action<PrebuiltLibrary> binaryFactory) {
-            super(ArtifactRepository.class, instantiator, new ArtifactRepositoryNamer());
-            registerFactory(PrebuiltLibraries.class, new NamedDomainObjectFactory<PrebuiltLibraries>() {
-                public PrebuiltLibraries create(String name) {
-                    return instantiator.newInstance(DefaultPrebuiltLibraries.class, name, instantiator, fileResolver, binaryFactory);
-                }
-            });
-        }
-    }
-
-    private static class ArtifactRepositoryNamer implements Namer<ArtifactRepository> {
-        public String determineName(ArtifactRepository object) {
-            return object.getName();
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/AbstractPrebuiltLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/AbstractPrebuiltLibraryBinary.java
deleted file mode 100644
index 4172551..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/AbstractPrebuiltLibraryBinary.java
+++ /dev/null
@@ -1,107 +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.nativebinaries.internal.prebuilt;
-
-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.language.base.internal.AbstractBuildableModelElement;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-import org.gradle.nativebinaries.internal.LibraryBinaryInternal;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Set;
-
-public abstract class AbstractPrebuiltLibraryBinary extends AbstractBuildableModelElement implements LibraryBinaryInternal {
-    private final String name;
-    private final PrebuiltLibrary library;
-    private final BuildType buildType;
-    private final Platform targetPlatform;
-    private final Flavor flavor;
-
-    public AbstractPrebuiltLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, Platform targetPlatform, Flavor flavor) {
-        this.name = name;
-        this.library = library;
-        this.buildType = buildType;
-        this.targetPlatform = targetPlatform;
-        this.flavor = flavor;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public PrebuiltLibrary getComponent() {
-        return library;
-    }
-
-    public BuildType getBuildType() {
-        return buildType;
-    }
-
-    public Flavor getFlavor() {
-        return flavor;
-    }
-
-    public Platform getTargetPlatform() {
-        return targetPlatform;
-    }
-
-    public FileCollection getHeaderDirs() {
-        return new SimpleFileCollection(library.getHeaders().getSrcDirs());
-    }
-
-    protected FileCollection createFileCollection(File file, String fileDescription) {
-        return new FileCollectionAdapter(new ValidatingFileSet(file, getComponent().getName(), fileDescription));
-    }
-
-    private static class ValidatingFileSet implements MinimalFileSet {
-        private final File file;
-        private final String libraryName;
-        private final String fileDescription;
-
-        private ValidatingFileSet(File file, String libraryName, String fileDescription) {
-            this.file = file;
-            this.libraryName = libraryName;
-            this.fileDescription = fileDescription;
-        }
-
-        public String getDisplayName() {
-            return String.format("%s for prebuilt library '%s'", fileDescription, libraryName);
-        }
-
-        public Set<File> getFiles() {
-            if (file == null) {
-                throw new PrebuiltLibraryResolveException(String.format("%s not set for prebuilt library '%s'.", fileDescription, libraryName));
-            }
-            if (!file.exists() || !file.isFile()) {
-                throw new PrebuiltLibraryResolveException(String.format("%s %s does not exist for prebuilt library '%s'.", fileDescription, file.getAbsolutePath(), libraryName));
-            }
-            return Collections.singleton(file);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibraries.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibraries.java
deleted file mode 100644
index b1719c8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibraries.java
+++ /dev/null
@@ -1,59 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.PrebuiltLibraries;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-
-public class DefaultPrebuiltLibraries extends AbstractNamedDomainObjectContainer<PrebuiltLibrary> implements PrebuiltLibraries {
-    private final FileResolver fileResolver;
-    private final Action<PrebuiltLibrary> libraryInitializer;
-    private String name;
-
-    public DefaultPrebuiltLibraries(String name, Instantiator instantiator, FileResolver fileResolver, Action<PrebuiltLibrary> libraryInitializer) {
-        super(PrebuiltLibrary.class, instantiator);
-        this.name = name;
-        this.fileResolver = fileResolver;
-        this.libraryInitializer = libraryInitializer;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    @Override
-    protected PrebuiltLibrary doCreate(String name) {
-        return getInstantiator().newInstance(DefaultPrebuiltLibrary.class, name, fileResolver);
-    }
-
-    public PrebuiltLibrary resolveLibrary(String name) {
-        PrebuiltLibrary library = findByName(name);
-        if (library != null && library.getBinaries().isEmpty()) {
-            libraryInitializer.execute(library);
-        }
-        return library;
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibrary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibrary.java
deleted file mode 100644
index 1327e86..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltLibrary.java
+++ /dev/null
@@ -1,50 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.nativebinaries.NativeBinary;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-
-public class DefaultPrebuiltLibrary implements PrebuiltLibrary {
-
-    private final String name;
-    private final SourceDirectorySet headers;
-    private final DomainObjectSet<NativeBinary> binaries;
-
-    public DefaultPrebuiltLibrary(String name, FileResolver fileResolver) {
-        this.name = name;
-        headers = new DefaultSourceDirectorySet("headers", fileResolver);
-        binaries = new DefaultDomainObjectSet<NativeBinary>(NativeBinary.class);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public SourceDirectorySet getHeaders() {
-        return headers;
-    }
-
-    public DomainObjectSet<NativeBinary> getBinaries() {
-        return binaries;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.java
deleted file mode 100644
index 8f43be9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.java
+++ /dev/null
@@ -1,66 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-import org.gradle.nativebinaries.SharedLibraryBinary;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.io.File;
-
-public class DefaultPrebuiltSharedLibraryBinary extends AbstractPrebuiltLibraryBinary implements SharedLibraryBinary {
-    private File sharedLibraryFile;
-    private File sharedLibraryLinkFile;
-
-    public DefaultPrebuiltSharedLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, Platform targetPlatform, Flavor flavor) {
-        super(name, library, buildType, targetPlatform, flavor);
-    }
-
-    public String getDisplayName() {
-        return String.format("shared library '%s'", getName());
-    }
-
-    public void setSharedLibraryFile(File sharedLibraryFile) {
-        this.sharedLibraryFile = sharedLibraryFile;
-    }
-
-    public File getSharedLibraryFile() {
-        return sharedLibraryFile;
-    }
-
-    public void setSharedLibraryLinkFile(File sharedLibraryLinkFile) {
-        this.sharedLibraryLinkFile = sharedLibraryLinkFile;
-    }
-
-    public File getSharedLibraryLinkFile() {
-        if (sharedLibraryLinkFile != null) {
-            return sharedLibraryLinkFile;
-        }
-        return sharedLibraryFile;
-    }
-
-    public FileCollection getLinkFiles() {
-        return createFileCollection(getSharedLibraryLinkFile(), "Shared library link file");
-    }
-
-    public FileCollection getRuntimeFiles() {
-        return createFileCollection(getSharedLibraryFile(), "Shared library runtime file");
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java
deleted file mode 100644
index 434df2e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java
+++ /dev/null
@@ -1,55 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-import org.gradle.nativebinaries.StaticLibraryBinary;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.io.File;
-
-public class DefaultPrebuiltStaticLibraryBinary extends AbstractPrebuiltLibraryBinary implements StaticLibraryBinary {
-    private File staticLibraryFile;
-
-    public DefaultPrebuiltStaticLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, Platform targetPlatform, Flavor flavor) {
-        super(name, library, buildType, targetPlatform, flavor);
-    }
-
-    public String getDisplayName() {
-        return String.format("static library '%s'", getName());
-    }
-
-    public void setStaticLibraryFile(File staticLibraryFile) {
-        this.staticLibraryFile = staticLibraryFile;
-    }
-
-    public File getStaticLibraryFile() {
-        return staticLibraryFile;
-    }
-
-    public FileCollection getLinkFiles() {
-        return createFileCollection(getStaticLibraryFile(), "Static library file");
-    }
-
-    public FileCollection getRuntimeFiles() {
-        return new SimpleFileCollection();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryBinaryLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryBinaryLocator.java
deleted file mode 100644
index 6694b52..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryBinaryLocator.java
+++ /dev/null
@@ -1,58 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.NamedDomainObjectSet;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.resolve.LibraryBinaryLocator;
-import org.gradle.nativebinaries.internal.resolve.ProjectLocator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PrebuiltLibraryBinaryLocator implements LibraryBinaryLocator {
-    private final ProjectLocator projectLocator;
-
-    public PrebuiltLibraryBinaryLocator(ProjectLocator projectLocator) {
-        this.projectLocator = projectLocator;
-    }
-
-    public DomainObjectSet<NativeBinary> getBinaries(NativeLibraryRequirement requirement) {
-        ProjectInternal project = projectLocator.locateProject(requirement.getProjectPath());
-        NamedDomainObjectSet<PrebuiltLibraries> repositories = project.getModelRegistry().get("repositories", Repositories.class).withType(PrebuiltLibraries.class);
-        if (repositories.isEmpty()) {
-            throw new PrebuiltLibraryResolveException("Project does not have any prebuilt library repositories.");
-        }
-        PrebuiltLibrary prebuiltLibrary = getPrebuiltLibrary(repositories, requirement.getLibraryName());
-        return prebuiltLibrary.getBinaries();
-    }
-
-    private PrebuiltLibrary getPrebuiltLibrary(NamedDomainObjectSet<PrebuiltLibraries> repositories, String libraryName) {
-        List<String> repositoryNames = new ArrayList<String>();
-        for (PrebuiltLibraries prebuiltLibraries : repositories) {
-            repositoryNames.add(prebuiltLibraries.getName());
-            PrebuiltLibrary prebuiltLibrary = prebuiltLibraries.resolveLibrary(libraryName);
-            if (prebuiltLibrary != null) {
-                return prebuiltLibrary;
-            }
-        }
-        throw new PrebuiltLibraryResolveException(
-                String.format("Prebuilt library with name '%s' not found in repositories '%s'.", libraryName, repositoryNames));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryInitializer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryInitializer.java
deleted file mode 100644
index 3c4eecc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryInitializer.java
+++ /dev/null
@@ -1,77 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.Action;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.internal.BinaryNamingSchemeBuilder;
-import org.gradle.language.base.internal.DefaultBinaryNamingSchemeBuilder;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.LibraryBinary;
-import org.gradle.nativebinaries.PrebuiltLibrary;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class PrebuiltLibraryInitializer implements Action<PrebuiltLibrary> {
-    private final Instantiator instantiator;
-    private final Set<Platform> allPlatforms = new LinkedHashSet<Platform>();
-    private final Set<BuildType> allBuildTypes = new LinkedHashSet<BuildType>();
-    private final Set<Flavor> allFlavors = new LinkedHashSet<Flavor>();
-
-    public PrebuiltLibraryInitializer(Instantiator instantiator,
-                                      Collection<? extends Platform> allPlatforms, Collection<? extends BuildType> allBuildTypes, Collection<? extends Flavor> allFlavors) {
-        this.instantiator = instantiator;
-        this.allPlatforms.addAll(allPlatforms);
-        this.allBuildTypes.addAll(allBuildTypes);
-        this.allFlavors.addAll(allFlavors);
-    }
-
-    public void execute(PrebuiltLibrary prebuiltLibrary) {
-        for (Platform platform : allPlatforms) {
-            for (BuildType buildType : allBuildTypes) {
-                for (Flavor flavor : allFlavors) {
-                    createNativeBinaries(prebuiltLibrary, platform, buildType, flavor);
-                }
-            }
-        }
-    }
-
-    public void createNativeBinaries(PrebuiltLibrary library, Platform platform, BuildType buildType, Flavor flavor) {
-        createNativeBinary(DefaultPrebuiltSharedLibraryBinary.class, library, platform, buildType, flavor);
-        createNativeBinary(DefaultPrebuiltStaticLibraryBinary.class, library, platform, buildType, flavor);
-    }
-
-    public <T extends LibraryBinary> void createNativeBinary(Class<T> type, PrebuiltLibrary library, Platform platform, BuildType buildType, Flavor flavor) {
-        String name = getName(type, library, platform, buildType, flavor);
-        T nativeBinary = instantiator.newInstance(type, name, library, buildType, platform, flavor);
-        library.getBinaries().add(nativeBinary);
-    }
-
-    private <T extends LibraryBinary> String getName(Class<T> type, PrebuiltLibrary library, Platform platform, BuildType buildType, Flavor flavor) {
-        BinaryNamingSchemeBuilder namingScheme = new DefaultBinaryNamingSchemeBuilder()
-                .withComponentName(library.getName())
-                .withTypeString(type.getSimpleName())
-                .withVariantDimension(platform.getName())
-                .withVariantDimension(buildType.getName())
-                .withVariantDimension(flavor.getName());
-        return namingScheme.build().getLifecycleTaskName();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryResolveException.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryResolveException.java
deleted file mode 100644
index b08966e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/prebuilt/PrebuiltLibraryResolveException.java
+++ /dev/null
@@ -1,27 +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.nativebinaries.internal.prebuilt;
-
-import org.gradle.api.GradleException;
-import org.gradle.internal.exceptions.Contextual;
-
- at Contextual
-public class PrebuiltLibraryResolveException extends GradleException {
-    public PrebuiltLibraryResolveException(String message) {
-        super(message);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ApiRequirementNativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ApiRequirementNativeDependencyResolver.java
deleted file mode 100644
index b5e512b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ApiRequirementNativeDependencyResolver.java
+++ /dev/null
@@ -1,104 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.nativebinaries.NativeDependencySet;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-/**
- * Adapts an 'api' library requirement to a default linkage, and then wraps the result so that only headers are provided.
- */
-public class ApiRequirementNativeDependencyResolver implements NativeDependencyResolver {
-    private final NativeDependencyResolver delegate;
-
-    public ApiRequirementNativeDependencyResolver(NativeDependencyResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
-        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getAllResolutions()) {
-            String linkage = getLinkage(resolution);
-            if ("api".equals(linkage)) {
-                resolution.setRequirement(new ApiAdaptedNativeLibraryRequirement(resolution.getRequirement()));
-            }
-        }
-
-        delegate.resolve(nativeBinaryResolveResult);
-
-        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getAllResolutions()) {
-            if (resolution.getRequirement() instanceof ApiAdaptedNativeLibraryRequirement) {
-                ApiAdaptedNativeLibraryRequirement adaptedRequirement = (ApiAdaptedNativeLibraryRequirement) resolution.getRequirement();
-                resolution.setRequirement(adaptedRequirement.getOriginal());
-//                resolution.setLibraryBinary(null);
-                resolution.setNativeDependencySet(new ApiNativeDependencySet(resolution.getNativeDependencySet()));
-            }
-        }
-    }
-
-    private String getLinkage(NativeBinaryRequirementResolveResult resolution) {
-        if (resolution.getRequirement() == null) {
-            return null;
-        }
-        return resolution.getRequirement().getLinkage();
-    }
-
-    private static class ApiAdaptedNativeLibraryRequirement implements NativeLibraryRequirement {
-        private final NativeLibraryRequirement original;
-        public ApiAdaptedNativeLibraryRequirement(NativeLibraryRequirement original) {
-            this.original = original;
-        }
-
-        public NativeLibraryRequirement getOriginal() {
-            return original;
-        }
-
-        public String getProjectPath() {
-            return original.getProjectPath();
-        }
-
-        public String getLibraryName() {
-            return original.getLibraryName();
-        }
-
-        public String getLinkage() {
-            // Rely on the default linkage for providing the headers
-            return null;
-        }
-    }
-
-    private static class ApiNativeDependencySet implements NativeDependencySet {
-        private final NativeDependencySet delegate;
-
-        public ApiNativeDependencySet(NativeDependencySet delegate) {
-            this.delegate = delegate;
-        }
-
-        public FileCollection getIncludeRoots() {
-            return delegate.getIncludeRoots();
-        }
-
-        public FileCollection getLinkFiles() {
-            return new SimpleFileCollection();
-        }
-
-        public FileCollection getRuntimeFiles() {
-            return new SimpleFileCollection();
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ChainedLibraryBinaryLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ChainedLibraryBinaryLocator.java
deleted file mode 100644
index 890037f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ChainedLibraryBinaryLocator.java
+++ /dev/null
@@ -1,51 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.nativebinaries.NativeBinary;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ChainedLibraryBinaryLocator implements LibraryBinaryLocator {
-    private final List<LibraryBinaryLocator> locators = new ArrayList<LibraryBinaryLocator>();
-
-    public ChainedLibraryBinaryLocator(List<? extends LibraryBinaryLocator> locators) {
-        this.locators.addAll(locators);
-    }
-
-    public DomainObjectSet<NativeBinary> getBinaries(NativeLibraryRequirement requirement) {
-        List<Exception> failures = new ArrayList<Exception>();
-        for (LibraryBinaryLocator locator : locators) {
-            try {
-                return locator.getBinaries(requirement);
-            } catch (Exception e) {
-                failures.add(e);
-            }
-        }
-        throw new LibraryResolveException(getFailureMessage(requirement), failures);
-    }
-
-    private String getFailureMessage(NativeLibraryRequirement requirement) {
-        return requirement.getProjectPath() == null
-                ? String.format("Could not locate library '%s'.", requirement.getLibraryName())
-                : String.format("Could not locate library '%s' for project '%s'.", requirement.getLibraryName(), requirement.getProjectPath());
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultLibraryResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultLibraryResolver.java
deleted file mode 100644
index ee64467..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultLibraryResolver.java
+++ /dev/null
@@ -1,107 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.nativebinaries.*;
-import org.gradle.nativebinaries.internal.LibraryBinaryInternal;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.util.GUtil;
-
-import java.util.Set;
-
-class DefaultLibraryResolver {
-    private final NativeLibraryRequirement requirement;
-    private final NativeBinary context;
-    private final LibraryBinaryLocator libraryBinaryLocator;
-
-    public DefaultLibraryResolver(LibraryBinaryLocator libraryBinaryLocator, NativeLibraryRequirement requirement, NativeBinary context) {
-        this.requirement = requirement;
-        this.context = context;
-        this.libraryBinaryLocator = libraryBinaryLocator;
-    }
-
-    public LibraryBinaryInternal resolveLibraryBinary() {
-        return new LibraryResolution()
-                .withFlavor(context.getFlavor())
-                .withPlatform(context.getTargetPlatform())
-                .withBuildType(context.getBuildType())
-                .resolveLibrary(libraryBinaryLocator.getBinaries(requirement));
-    }
-
-    private class LibraryResolution {
-        private Flavor flavor;
-        private Platform platform;
-        private BuildType buildType;
-
-        public LibraryResolution withFlavor(Flavor flavor) {
-            this.flavor = flavor;
-            return this;
-        }
-
-        public LibraryResolution withPlatform(Platform platform) {
-            this.platform = platform;
-            return this;
-        }
-
-        public LibraryResolution withBuildType(BuildType buildType) {
-            this.buildType = buildType;
-            return this;
-        }
-
-        public NativeDependencySet resolve(DomainObjectSet<NativeBinary> allBinaries) {
-            LibraryBinaryInternal resolve = resolveLibrary(allBinaries);
-            return new DefaultNativeDependencySet(resolve);
-        }
-
-        public LibraryBinaryInternal resolveLibrary(DomainObjectSet<NativeBinary> allBinaries) {
-            Class<? extends LibraryBinary> type = getTypeForLinkage(requirement.getLinkage());
-            DomainObjectSet<? extends LibraryBinary> candidateBinaries = allBinaries.withType(type);
-            return resolve(candidateBinaries);
-        }
-
-        private Class<? extends LibraryBinary> getTypeForLinkage(String linkage) {
-            if ("static".equals(linkage)) {
-                return StaticLibraryBinary.class;
-            }
-            if ("shared".equals(linkage) || linkage == null) {
-                return SharedLibraryBinary.class;
-            }
-            throw new InvalidUserDataException("Not a valid linkage: " + linkage);
-        }
-
-        private LibraryBinaryInternal resolve(Set<? extends LibraryBinary> candidates) {
-            for (LibraryBinary candidate : candidates) {
-                if (flavor != null && !flavor.getName().equals(candidate.getFlavor().getName())) {
-                    continue;
-                }
-                if (platform != null && !platform.getName().equals(candidate.getTargetPlatform().getName())) {
-                    continue;
-                }
-                if (buildType != null && !buildType.getName().equals(candidate.getBuildType().getName())) {
-                    continue;
-                }
-
-                return (LibraryBinaryInternal) candidate;
-            }
-
-            String typeName = GUtil.elvis(requirement.getLinkage(), "shared");
-            throw new LibraryResolveException(String.format("No %s library binary available for library '%s' with [flavor: '%s', platform: '%s', buildType: '%s']",
-                    typeName, requirement.getLibraryName(), flavor.getName(), platform.getName(), buildType.getName()));
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultNativeDependencySet.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultNativeDependencySet.java
deleted file mode 100644
index 9005f6d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultNativeDependencySet.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.nativebinaries.NativeDependencySet;
-import org.gradle.nativebinaries.internal.LibraryBinaryInternal;
-
-public class DefaultNativeDependencySet implements NativeDependencySet {
-    private final LibraryBinaryInternal binary;
-
-    public DefaultNativeDependencySet(LibraryBinaryInternal binary) {
-        this.binary = binary;
-    }
-
-    public FileCollection getIncludeRoots() {
-        return binary.getHeaderDirs();
-    }
-
-    public FileCollection getLinkFiles() {
-        return binary.getLinkFiles();
-    }
-
-    public FileCollection getRuntimeFiles() {
-        return binary.getRuntimeFiles();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultProjectLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultProjectLocator.java
deleted file mode 100644
index 1c47df9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/DefaultProjectLocator.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.project.ProjectInternal;
-
-public class DefaultProjectLocator implements ProjectLocator {
-    private final String projectPath;
-    private final ProjectFinder delegate;
-
-    public DefaultProjectLocator(String projectPath, ProjectFinder delegate) {
-        this.projectPath = projectPath;
-        this.delegate = delegate;
-    }
-
-    public ProjectInternal locateProject(String path) {
-        if (path == null || path.length() == 0) {
-            return delegate.getProject(projectPath);
-        }
-
-        ProjectInternal referencedProject = delegate.getProject(path);
-        // TODO:DAZ This is a brain-dead way to ensure that the reference project's model is ready to access
-        referencedProject.evaluate();
-        return referencedProject;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/InputHandlingNativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/InputHandlingNativeDependencyResolver.java
deleted file mode 100644
index 6f96cbc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/InputHandlingNativeDependencyResolver.java
+++ /dev/null
@@ -1,36 +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.nativebinaries.internal.resolve;
-
-import org.gradle.nativebinaries.NativeDependencySet;
-
-public class InputHandlingNativeDependencyResolver implements NativeDependencyResolver {
-    private final NativeDependencyResolver delegate;
-
-    public InputHandlingNativeDependencyResolver(NativeDependencyResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
-        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
-            if (resolution.getInput() instanceof NativeDependencySet) {
-                resolution.setNativeDependencySet((NativeDependencySet) resolution.getInput());
-            }
-        }
-        delegate.resolve(nativeBinaryResolveResult);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryBinaryLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryBinaryLocator.java
deleted file mode 100644
index a39c104..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryBinaryLocator.java
+++ /dev/null
@@ -1,25 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.nativebinaries.NativeBinary;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-public interface LibraryBinaryLocator {
-    DomainObjectSet<NativeBinary> getBinaries(NativeLibraryRequirement requirement);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryNativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryNativeDependencyResolver.java
deleted file mode 100644
index aac1ed5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryNativeDependencyResolver.java
+++ /dev/null
@@ -1,36 +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.nativebinaries.internal.resolve;
-
-import org.gradle.nativebinaries.internal.LibraryBinaryInternal;
-
-public class LibraryNativeDependencyResolver implements NativeDependencyResolver {
-    private final LibraryBinaryLocator libraryBinaryLocator;
-
-    public LibraryNativeDependencyResolver(final LibraryBinaryLocator locator) {
-        libraryBinaryLocator = locator;
-    }
-
-    public void resolve(NativeBinaryResolveResult resolution) {
-        for (NativeBinaryRequirementResolveResult requirementResolution : resolution.getPendingResolutions()) {
-            DefaultLibraryResolver libraryResolver = new DefaultLibraryResolver(libraryBinaryLocator, requirementResolution.getRequirement(), resolution.getTarget());
-            LibraryBinaryInternal libraryBinary = libraryResolver.resolveLibraryBinary();
-            requirementResolution.setLibraryBinary(libraryBinary);
-            requirementResolution.setNativeDependencySet(new DefaultNativeDependencySet(libraryBinary));
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryResolveException.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryResolveException.java
deleted file mode 100644
index 8bb556b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/LibraryResolveException.java
+++ /dev/null
@@ -1,32 +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.nativebinaries.internal.resolve;
-
-import org.gradle.internal.exceptions.AbstractMultiCauseException;
-import org.gradle.internal.exceptions.Contextual;
-
- at Contextual
-class LibraryResolveException extends AbstractMultiCauseException {
-
-    public LibraryResolveException(String message) {
-        super(message);
-    }
-
-    LibraryResolveException(String message, Iterable<? extends Throwable> causes) {
-        super(message, causes);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryRequirementResolveResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryRequirementResolveResult.java
deleted file mode 100644
index 9619178..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryRequirementResolveResult.java
+++ /dev/null
@@ -1,65 +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.nativebinaries.internal.resolve;
-
-import org.gradle.nativebinaries.LibraryBinary;
-import org.gradle.nativebinaries.NativeDependencySet;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-import org.gradle.nativebinaries.internal.LibraryBinaryInternal;
-
-public class NativeBinaryRequirementResolveResult {
-    private Object input;
-    private NativeLibraryRequirement requirement;
-    private LibraryBinaryInternal libraryBinary;
-    private NativeDependencySet nativeDependencySet;
-
-    public NativeBinaryRequirementResolveResult(Object input) {
-        this.input = input;
-    }
-
-    public Object getInput() {
-        return input;
-    }
-
-    public void setRequirement(NativeLibraryRequirement requirement) {
-        this.requirement = requirement;
-    }
-
-    public NativeLibraryRequirement getRequirement() {
-        return requirement;
-    }
-
-    public LibraryBinary getLibraryBinary() {
-        return libraryBinary;
-    }
-
-    public void setLibraryBinary(LibraryBinaryInternal libraryBinary) {
-        this.libraryBinary = libraryBinary;
-    }
-
-    public NativeDependencySet getNativeDependencySet() {
-        return nativeDependencySet;
-    }
-
-    public void setNativeDependencySet(NativeDependencySet nativeDependencySet) {
-        this.nativeDependencySet = nativeDependencySet;
-    }
-
-    public boolean isComplete() {
-        return nativeDependencySet != null;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryResolveResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryResolveResult.java
deleted file mode 100644
index df7a268..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeBinaryResolveResult.java
+++ /dev/null
@@ -1,74 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.specs.Spec;
-import org.gradle.nativebinaries.LibraryBinary;
-import org.gradle.nativebinaries.NativeDependencySet;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-import org.gradle.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class NativeBinaryResolveResult {
-    private final ProjectNativeBinary target;
-    private final List<NativeBinaryRequirementResolveResult> resolutions = new ArrayList<NativeBinaryRequirementResolveResult>();
-
-    public NativeBinaryResolveResult(ProjectNativeBinary target, Collection<?> libs) {
-        this.target = target;
-        for (Object lib : libs) {
-            resolutions.add(new NativeBinaryRequirementResolveResult(lib));
-        }
-    }
-
-    public ProjectNativeBinary getTarget() {
-        return target;
-    }
-
-    public List<NativeBinaryRequirementResolveResult> getAllResolutions() {
-        return resolutions;
-    }
-
-    public List<NativeDependencySet> getAllResults() {
-        return CollectionUtils.collect(getAllResolutions(), new Transformer<NativeDependencySet, NativeBinaryRequirementResolveResult>() {
-            public NativeDependencySet transform(NativeBinaryRequirementResolveResult original) {
-                return original.getNativeDependencySet();
-            }
-        });
-    }
-
-    public List<LibraryBinary> getAllLibraryBinaries() {
-        List<LibraryBinary> result = new ArrayList<LibraryBinary>();
-        for (NativeBinaryRequirementResolveResult resolution : getAllResolutions()) {
-            if (resolution.getLibraryBinary() != null) {
-                result.add(resolution.getLibraryBinary());
-            }
-        }
-        return result;
-    }
-
-    public List<NativeBinaryRequirementResolveResult> getPendingResolutions() {
-        return CollectionUtils.filter(resolutions, new Spec<NativeBinaryRequirementResolveResult>() {
-            public boolean isSatisfiedBy(NativeBinaryRequirementResolveResult element) {
-                return !element.isComplete();
-            }
-        });
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParser.java
deleted file mode 100644
index 16ae8b1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParser.java
+++ /dev/null
@@ -1,59 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.tasks.Optional;
-import org.gradle.internal.typeconversion.*;
-import org.gradle.nativebinaries.Library;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-import org.gradle.nativebinaries.internal.ProjectNativeLibraryRequirement;
-
-import java.util.Collection;
-
-class NativeDependencyNotationParser {
-    public static NotationParser<Object, NativeLibraryRequirement> parser() {
-        return new NotationParserBuilder<NativeLibraryRequirement>()
-                .resultingType(NativeLibraryRequirement.class)
-                .parser(new LibraryConverter())
-                .parser(new NativeLibraryRequirementMapNotationParser())
-                .toComposite();
-    }
-
-    private static class LibraryConverter extends TypedNotationParser<Library, NativeLibraryRequirement> {
-        private LibraryConverter() {
-            super(Library.class);
-        }
-
-        @Override
-        protected NativeLibraryRequirement parseType(Library notation) {
-            return notation.getShared();
-        }
-    }
-
-    private static class NativeLibraryRequirementMapNotationParser extends MapNotationParser<NativeLibraryRequirement> {
-
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Map with mandatory 'library' and optional 'project' and 'linkage' keys, e.g. [project: ':someProj', library: 'mylib', linkage: 'static']");
-        }
-
-        @SuppressWarnings("unused")
-        protected NativeLibraryRequirement parseMap(@MapKey("library") String libraryName, @Optional @MapKey("project") String projectPath, @Optional @MapKey("linkage") String linkage) {
-            return new ProjectNativeLibraryRequirement(projectPath, libraryName, linkage);
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolver.java
deleted file mode 100644
index bc6a3da..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolver.java
+++ /dev/null
@@ -1,20 +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.nativebinaries.internal.resolve;
-
-public interface NativeDependencyResolver {
-    void resolve(NativeBinaryResolveResult resolution);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolverServices.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolverServices.java
deleted file mode 100644
index 7786b5f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyResolverServices.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.resolve;
-
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.nativebinaries.internal.prebuilt.PrebuiltLibraryBinaryLocator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class NativeDependencyResolverServices {
-
-    public ProjectLocator createProjectLocator(ProjectFinder projectFinder, DependencyMetaDataProvider metaDataProvider) {
-        String currentProjectPath = metaDataProvider.getModule().getProjectPath();
-        return new DefaultProjectLocator(currentProjectPath, projectFinder);
-    }
-
-    public LibraryBinaryLocator createLibraryBinaryLocator(ProjectLocator projectLocator) {
-        List<LibraryBinaryLocator> locators = new ArrayList<LibraryBinaryLocator>();
-        locators.add(new ProjectLibraryBinaryLocator(projectLocator));
-        locators.add(new PrebuiltLibraryBinaryLocator(projectLocator));
-        return new ChainedLibraryBinaryLocator(locators);
-    }
-
-    public NativeDependencyResolver createResolver(LibraryBinaryLocator locator) {
-        NativeDependencyResolver resolver = new LibraryNativeDependencyResolver(locator);
-        resolver = new ApiRequirementNativeDependencyResolver(resolver);
-        resolver = new RequirementParsingNativeDependencyResolver(resolver);
-        resolver = new SourceSetNativeDependencyResolver(resolver);
-        return new InputHandlingNativeDependencyResolver(resolver);
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocator.java
deleted file mode 100644
index 5763005..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocator.java
+++ /dev/null
@@ -1,44 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.DomainObjectSet;
-import org.gradle.api.Project;
-import org.gradle.nativebinaries.LibraryContainer;
-import org.gradle.nativebinaries.NativeBinary;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-public class ProjectLibraryBinaryLocator implements LibraryBinaryLocator {
-    private final ProjectLocator projectLocator;
-
-    public ProjectLibraryBinaryLocator(ProjectLocator projectLocator) {
-        this.projectLocator = projectLocator;
-    }
-
-    public DomainObjectSet<NativeBinary> getBinaries(NativeLibraryRequirement requirement) {
-        Project project = findProject(requirement);
-        LibraryContainer libraryContainer = (LibraryContainer) project.getExtensions().findByName("libraries");
-        if (libraryContainer == null) {
-            throw new LibraryResolveException(String.format("Project does not have a libraries container: '%s'", project.getPath()));
-        }
-        return libraryContainer.getByName(requirement.getLibraryName()).getBinaries();
-    }
-
-    private Project findProject(NativeLibraryRequirement requirement) {
-        return projectLocator.locateProject(requirement.getProjectPath());
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLocator.java
deleted file mode 100644
index 23b8a34..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLocator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.internal.resolve;
-
-import org.gradle.api.internal.project.ProjectInternal;
-
-public interface ProjectLocator {
-    ProjectInternal locateProject(String path);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/RequirementParsingNativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/RequirementParsingNativeDependencyResolver.java
deleted file mode 100644
index a24010c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/RequirementParsingNativeDependencyResolver.java
+++ /dev/null
@@ -1,38 +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.nativebinaries.internal.resolve;
-
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.nativebinaries.NativeLibraryRequirement;
-
-public class RequirementParsingNativeDependencyResolver implements NativeDependencyResolver {
-    private final NotationParser<Object, NativeLibraryRequirement> parser = NativeDependencyNotationParser.parser();
-
-    private final NativeDependencyResolver delegate;
-
-    public RequirementParsingNativeDependencyResolver(NativeDependencyResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
-        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
-            NativeLibraryRequirement requirement = parser.parseNotation(resolution.getInput());
-            resolution.setRequirement(requirement);
-        }
-        delegate.resolve(nativeBinaryResolveResult);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/SourceSetNativeDependencyResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/SourceSetNativeDependencyResolver.java
deleted file mode 100644
index 8fcf6fc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/internal/resolve/SourceSetNativeDependencyResolver.java
+++ /dev/null
@@ -1,97 +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.nativebinaries.internal.resolve;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.AbstractFileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.language.HeaderExportingSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.nativebinaries.NativeDependencySet;
-
-import java.io.File;
-import java.util.Set;
-
-public class SourceSetNativeDependencyResolver implements NativeDependencyResolver {
-    private final NativeDependencyResolver delegate;
-
-    public SourceSetNativeDependencyResolver(NativeDependencyResolver delegate) {
-        this.delegate = delegate;
-    }
-
-    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
-        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
-            if (resolution.getInput() instanceof LanguageSourceSet) {
-                LanguageSourceSet input = (LanguageSourceSet) resolution.getInput();
-                resolution.setNativeDependencySet(createNativeDependencySet(input));
-            }
-        }
-        delegate.resolve(nativeBinaryResolveResult);
-    }
-
-    private NativeDependencySet createNativeDependencySet(LanguageSourceSet sourceSet) {
-        if (sourceSet instanceof HeaderExportingSourceSet) {
-            return new LanguageSourceSetNativeDependencySet((HeaderExportingSourceSet) sourceSet);
-        }
-        return new EmptyNativeDependencySet();
-    }
-
-    private static class EmptyNativeDependencySet implements NativeDependencySet {
-        public FileCollection getIncludeRoots() {
-            return empty();
-        }
-
-        public FileCollection getLinkFiles() {
-            return empty();
-        }
-
-        public FileCollection getRuntimeFiles() {
-            return empty();
-        }
-
-        private FileCollection empty() {
-            return new SimpleFileCollection();
-        }
-    }
-
-    private static class LanguageSourceSetNativeDependencySet extends EmptyNativeDependencySet {
-        private final HeaderExportingSourceSet sourceSet;
-
-        private LanguageSourceSetNativeDependencySet(HeaderExportingSourceSet sourceSet) {
-            this.sourceSet = sourceSet;
-        }
-
-        public FileCollection getIncludeRoots() {
-            return new AbstractFileCollection() {
-                @Override
-                public String getDisplayName() {
-                    return "Include roots of " + sourceSet.getName();
-                }
-
-                public Set<File> getFiles() {
-                    return sourceSet.getExportedHeaders().getSrcDirs();
-                }
-
-                @Override
-                public TaskDependency getBuildDependencies() {
-                    return sourceSet.getBuildDependencies();
-                }
-            };
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/PreprocessingTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/PreprocessingTool.java
deleted file mode 100644
index 4d3928c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/PreprocessingTool.java
+++ /dev/null
@@ -1,45 +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.nativebinaries.language;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.Tool;
-
-import java.util.Map;
-
-/**
- * A tool that permits configuration of the C preprocessor.
- */
- at Incubating
-public interface PreprocessingTool extends Tool {
-    /**
-     * The set of preprocessor macros to define when compiling this binary.
-     */
-    Map<String, String> getMacros();
-
-    /**
-     * Defines a named preprocessor macros to use when compiling this binary.
-     * The macro will be supplied to the compiler as '-D name'.
-     */
-    void define(String name);
-
-    /**
-     * Defines a named preprocessor macro with a value, which will be used when compiling this binary.
-     * The macro will be supplied to the compiler as '-D name=definition'.
-     */
-    void define(String name, String definition);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/AssembleSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/AssembleSpec.java
deleted file mode 100644
index 8d667ac..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/AssembleSpec.java
+++ /dev/null
@@ -1,35 +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.nativebinaries.language.assembler.internal;
-
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * 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);
-
-    List<File> getSourceFiles();
-
-    void source(Iterable<File> sources);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/DefaultAssembleSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/DefaultAssembleSpec.java
deleted file mode 100644
index 8f97629..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/internal/DefaultAssembleSpec.java
+++ /dev/null
@@ -1,47 +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.nativebinaries.language.assembler.internal;
-
-import org.gradle.nativebinaries.internal.AbstractBinaryToolSpec;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DefaultAssembleSpec extends AbstractBinaryToolSpec implements AssembleSpec {
-
-    private List<File> source = new ArrayList<File>();
-    private File objectFileDir;
-
-    public List<File> getSourceFiles() {
-        return source;
-    }
-
-    public void source(Iterable<File> sources) {
-        for (File file : sources) {
-            this.source.add(file);
-        }
-    }
-
-    public File getObjectFileDir() {
-        return objectFileDir;
-    }
-
-    public void setObjectFileDir(File objectFileDir) {
-        this.objectFileDir = objectFileDir;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/package-info.java
deleted file mode 100644
index 036c922..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for building Assembler sources for a native runtime.
- */
-package org.gradle.nativebinaries.language.assembler;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPlugin.groovy
deleted file mode 100644
index 7822937..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPlugin.groovy
+++ /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.nativebinaries.language.assembler.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.assembler.AssemblerSourceSet
-import org.gradle.language.assembler.plugins.AssemblerLangPlugin
-import org.gradle.nativebinaries.Executable
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.ProjectNativeComponent
-import org.gradle.nativebinaries.internal.DefaultTool
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.assembler.tasks.Assemble
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Assembly language sources.
- *
- * <p>Automatically includes the {@link AssemblerLangPlugin} for core Assembler support and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link Assemble} task for each {@link AssemblerSourceSet} to assemble the sources.</li>
- */
- at Incubating
-class AssemblerNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(AssemblerLangPlugin)
-
-        // TODO:DAZ Clean this up (see CppNativeBinariesPlugin)
-        project.executables.all { Executable executable ->
-            addLanguageExtensionsToComponent(executable)
-        }
-        project.libraries.all { Library library ->
-            addLanguageExtensionsToComponent(library)
-        }
-
-        project.binaries.withType(ProjectNativeBinary) { ProjectNativeBinaryInternal binary ->
-            binary.source.withType(AssemblerSourceSet).all { AssemblerSourceSet sourceSet ->
-                if (sourceSet.mayHaveSources) {
-                    def assembleTask = createAssembleTask(project, binary, sourceSet)
-                    assembleTask.dependsOn sourceSet
-                    binary.tasks.add assembleTask
-                    binary.tasks.builder.source assembleTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
-                }
-            }
-        }
-    }
-
-    private def addLanguageExtensionsToComponent(ProjectNativeComponent component) {
-        component.binaries.all { binary ->
-            binary.extensions.create("assembler", DefaultTool)
-        }
-    }
-
-    private def createAssembleTask(ProjectInternal project, ProjectNativeBinaryInternal binary, def sourceSet) {
-        def assembleTask = project.task(binary.namingScheme.getTaskName("assemble", sourceSet.fullName), type: Assemble) {
-            description = "Assembles the $sourceSet of $binary"
-        }
-
-        assembleTask.toolChain = binary.toolChain
-        assembleTask.targetPlatform = binary.targetPlatform
-
-        assembleTask.source sourceSet.source
-
-        assembleTask.objectFileDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        assembleTask.assemblerArgs = binary.assembler.args
-
-        assembleTask
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPlugin.groovy
deleted file mode 100644
index 9b38fd5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerPlugin.groovy
+++ /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.nativebinaries.language.assembler.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Assembly language sources.
- *
- * <p>Adds core tool chain support to the {@link AssemblerNativeBinariesPlugin}.</p>
- */
- at Incubating
-class AssemblerPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-        project.plugins.apply(AssemblerNativeBinariesPlugin)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/tasks/Assemble.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/tasks/Assemble.groovy
deleted file mode 100644
index 2984112..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/assembler/tasks/Assemble.groovy
+++ /dev/null
@@ -1,95 +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.nativebinaries.language.assembler.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.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.language.assembler.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 platform being targeted.
-     */
-    Platform targetPlatform
-
-    /**
-     * 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}:${targetPlatform.compatibilityString}"
-    }
-
-    /**
-     * 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.target(targetPlatform).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/nativebinaries/language/c/internal/CCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/CCompileSpec.java
deleted file mode 100644
index 4ddb79b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/CCompileSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.c.internal;
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-public interface CCompileSpec extends NativeCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/DefaultCCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/DefaultCCompileSpec.java
deleted file mode 100644
index 72866c7..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/DefaultCCompileSpec.java
+++ /dev/null
@@ -1,22 +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.nativebinaries.language.c.internal;
-
-import org.gradle.nativebinaries.language.internal.AbstractNativeCompileSpec;
-
-public class DefaultCCompileSpec extends AbstractNativeCompileSpec implements CCompileSpec {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/AbstractIncrementalNativeCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/AbstractIncrementalNativeCompiler.java
deleted file mode 100644
index cd43291..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/AbstractIncrementalNativeCompiler.java
+++ /dev/null
@@ -1,86 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.state.FileSnapshotter;
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.cache.PersistentIndexedCache;
-import org.gradle.cache.PersistentStateCache;
-import org.gradle.internal.Factory;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-import org.gradle.util.CollectionUtils;
-
-import java.io.File;
-
-abstract class AbstractIncrementalNativeCompiler implements Compiler<NativeCompileSpec> {
-    private final TaskInternal task;
-    private final SourceIncludesParser sourceIncludesParser;
-    private final TaskArtifactStateCacheAccess cacheAccess;
-    private final FileSnapshotter fileSnapshotter;
-    private Iterable<File> includes;
-
-    protected AbstractIncrementalNativeCompiler(TaskInternal task, SourceIncludesParser sourceIncludesParser, Iterable<File> includes,
-                                                TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter) {
-        this.task = task;
-        this.includes = includes;
-        this.sourceIncludesParser = sourceIncludesParser;
-        this.cacheAccess = cacheAccess;
-        this.fileSnapshotter = fileSnapshotter;
-    }
-
-    public WorkResult execute(final NativeCompileSpec spec) {
-        return cacheAccess.useCache("incremental compile", new Factory<WorkResult>() {
-            public WorkResult create() {
-                IncrementalCompileProcessor processor = createProcessor(includes);
-                return doIncrementalCompile(processor, spec);
-            }
-        });
-    }
-
-    protected abstract WorkResult doIncrementalCompile(IncrementalCompileProcessor processor, NativeCompileSpec spec);
-
-    protected TaskInternal getTask() {
-        return task;
-    }
-
-    private IncrementalCompileProcessor createProcessor(Iterable<File> includes) {
-        PersistentStateCache<CompilationState> compileStateCache = createCompileStateCache(task.getPath());
-
-        DefaultSourceIncludesResolver dependencyParser = new DefaultSourceIncludesResolver(CollectionUtils.toList(includes));
-
-        return new IncrementalCompileProcessor(compileStateCache, dependencyParser, sourceIncludesParser, fileSnapshotter);
-    }
-
-    private PersistentStateCache<CompilationState> createCompileStateCache(final String taskPath) {
-        final PersistentIndexedCache<String, CompilationState> stateIndexedCache = cacheAccess.createCache("compilationState", String.class, new CompilationStateSerializer());
-        return new PersistentStateCache<CompilationState>() {
-            public CompilationState get() {
-                return stateIndexedCache.get(taskPath);
-            }
-
-            public void set(CompilationState newValue) {
-                stateIndexedCache.put(taskPath, newValue);
-            }
-
-            public void update(UpdateAction<CompilationState> updateAction) {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompiler.java
deleted file mode 100644
index 9eef667..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompiler.java
+++ /dev/null
@@ -1,57 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.state.FileSnapshotter;
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-
-public class CleanCompilingNativeCompiler extends AbstractIncrementalNativeCompiler {
-    private final Compiler<NativeCompileSpec> delegateCompiler;
-
-    public CleanCompilingNativeCompiler(TaskInternal task, SourceIncludesParser sourceIncludesParser, Iterable<File> includes,
-                                        TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter,
-                                        Compiler<NativeCompileSpec> delegateCompiler) {
-        super(task, sourceIncludesParser, includes, cacheAccess, fileSnapshotter);
-        this.delegateCompiler = delegateCompiler;
-    }
-
-    @Override
-    protected WorkResult doIncrementalCompile(IncrementalCompileProcessor processor, NativeCompileSpec spec) {
-        processor.processSourceFiles(spec.getSourceFiles());
-        boolean deleted = cleanPreviousOutputs(spec);
-        WorkResult compileResult = delegateCompiler.execute(spec);
-        if (deleted && !compileResult.getDidWork()) {
-            return new SimpleWorkResult(deleted);
-        }
-        return compileResult;
-    }
-
-    private boolean cleanPreviousOutputs(NativeCompileSpec spec) {
-        SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(getTask().getOutputs());
-        cleaner.setDestinationDir(spec.getObjectFileDir());
-        cleaner.execute();
-        return cleaner.getDidWork();
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationFileState.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationFileState.java
deleted file mode 100644
index a5295b7..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationFileState.java
+++ /dev/null
@@ -1,50 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.Serializable;
-import java.util.HashSet;
-import java.util.Set;
-
-public class CompilationFileState implements Serializable {
-    private byte[] hash;
-    private SourceIncludes sourceIncludes = new DefaultSourceIncludes();
-    private Set<ResolvedInclude> resolvedIncludes = new HashSet<ResolvedInclude>();
-
-    public CompilationFileState(byte[] hash) {
-        this.hash = hash;
-    }
-
-    public byte[] getHash() {
-        return hash;
-    }
-
-    public SourceIncludes getSourceIncludes() {
-        return sourceIncludes;
-    }
-
-    public void setSourceIncludes(SourceIncludes sourceIncludes) {
-        this.sourceIncludes = sourceIncludes;
-    }
-
-    public Set<ResolvedInclude> getResolvedIncludes() {
-        return resolvedIncludes;
-    }
-
-    public void setResolvedIncludes(Set<ResolvedInclude> resolvedIncludes) {
-        this.resolvedIncludes = resolvedIncludes;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationState.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationState.java
deleted file mode 100644
index 15ea4c5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationState.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.*;
-
-public class CompilationState implements Serializable {
-    List<File> sourceInputs = new ArrayList<File>();
-    Map<File, CompilationFileState> fileStates = new HashMap<File, CompilationFileState>();
-
-    public List<File> getSourceInputs() {
-        return sourceInputs;
-    }
-
-    public void addSourceInput(File file) {
-        sourceInputs.add(file);
-    }
-
-    public CompilationFileState getState(File file) {
-        return fileStates.get(file);
-    }
-
-    public void setState(File file, CompilationFileState compilationFileState) {
-        fileStates.put(file, compilationFileState);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializer.java
deleted file mode 100644
index 121d66c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializer.java
+++ /dev/null
@@ -1,127 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.messaging.serialize.*;
-
-import java.io.File;
-import java.util.Set;
-
-public class CompilationStateSerializer implements Serializer<CompilationState> {
-    
-    private static final int SERIAL_VERSION = 1;
-    private final BaseSerializerFactory serializerFactory = new BaseSerializerFactory();
-    private final Serializer<File> fileSerializer;
-    private final ListSerializer<File> fileListSerializer;
-    private final MapSerializer<File, CompilationFileState> stateMapSerializer;
-
-    public CompilationStateSerializer() {
-        fileSerializer = serializerFactory.getSerializerFor(File.class);
-        fileListSerializer = new ListSerializer<File>(fileSerializer);
-        stateMapSerializer = new MapSerializer<File, CompilationFileState>(fileSerializer, new CompilationFileStateSerializer());
-    }
-
-    public CompilationState read(Decoder decoder) throws Exception {
-        CompilationState compilationState = new CompilationState();
-        int version = decoder.readInt();
-        if (version != SERIAL_VERSION) {
-            return compilationState;
-        }
-
-        compilationState.sourceInputs.addAll(fileListSerializer.read(decoder));
-        compilationState.fileStates.putAll(stateMapSerializer.read(decoder));
-        return compilationState;
-    }
-
-    public void write(Encoder encoder, CompilationState value) throws Exception {
-        encoder.writeInt(SERIAL_VERSION);
-        fileListSerializer.write(encoder, value.sourceInputs);
-        stateMapSerializer.write(encoder, value.fileStates);
-    }
-
-    private class CompilationFileStateSerializer implements Serializer<CompilationFileState> {
-        private final Serializer<byte[]> hashSerializer = new HashSerializer();
-        private final Serializer<Set<ResolvedInclude>> resolveIncludesSerializer = new SetSerializer<ResolvedInclude>(new ResolvedIncludeSerializer());
-        private final Serializer<SourceIncludes> sourceIncludesSerializer = new SourceIncludesSerializer();
-
-        public CompilationFileState read(Decoder decoder) throws Exception {
-            CompilationFileState fileState = new CompilationFileState(hashSerializer.read(decoder));
-            fileState.setResolvedIncludes(resolveIncludesSerializer.read(decoder));
-            fileState.setSourceIncludes(sourceIncludesSerializer.read(decoder));
-            return fileState;
-        }
-
-        public void write(Encoder encoder, CompilationFileState value) throws Exception {
-            hashSerializer.write(encoder, value.getHash());
-            resolveIncludesSerializer.write(encoder, value.getResolvedIncludes());
-            sourceIncludesSerializer.write(encoder, value.getSourceIncludes());
-        }
-    }
-
-    private class HashSerializer implements Serializer<byte[]> {
-        public byte[] read(Decoder decoder) throws Exception {
-            int size = decoder.readSmallInt();
-            byte[] value = new byte[size];
-            decoder.readBytes(value);
-            return value;
-        }
-
-        public void write(Encoder encoder, byte[] value) throws Exception {
-            encoder.writeSmallInt(value.length);
-            encoder.writeBytes(value);
-        }
-    }
-
-    private class ResolvedIncludeSerializer implements Serializer<ResolvedInclude> {
-        public ResolvedInclude read(Decoder decoder) throws Exception {
-            String include = decoder.readString();
-            File included = null;
-            if (decoder.readBoolean()) {
-                included = fileSerializer.read(decoder);
-            }
-            return new ResolvedInclude(include, included);
-        }
-
-        public void write(Encoder encoder, ResolvedInclude value) throws Exception {
-            encoder.writeString(value.getInclude());
-            if (value.getFile() == null) {
-                encoder.writeBoolean(false);
-            } else {
-                encoder.writeBoolean(true);
-                fileSerializer.write(encoder, value.getFile());
-            }
-        }
-    }
-
-    private class SourceIncludesSerializer implements Serializer<SourceIncludes> {
-        private final Serializer<String> stringSerializer = serializerFactory.getSerializerFor(String.class);
-        private final ListSerializer<String> stringListSerializer = new ListSerializer<String>(stringSerializer);
-
-        public SourceIncludes read(Decoder decoder) throws Exception {
-            SourceIncludes sourceIncludes = new DefaultSourceIncludes();
-            sourceIncludes.getQuotedIncludes().addAll(stringListSerializer.read(decoder));
-            sourceIncludes.getSystemIncludes().addAll(stringListSerializer.read(decoder));
-            sourceIncludes.getMacroIncludes().addAll(stringListSerializer.read(decoder));
-            return sourceIncludes;
-        }
-
-        public void write(Encoder encoder, SourceIncludes value) throws Exception {
-            stringListSerializer.write(encoder, value.getQuotedIncludes());
-            stringListSerializer.write(encoder, value.getSystemIncludes());
-            stringListSerializer.write(encoder, value.getMacroIncludes());
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultIncrementalCompilation.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultIncrementalCompilation.java
deleted file mode 100644
index cabd8ed..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultIncrementalCompilation.java
+++ /dev/null
@@ -1,37 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-import java.util.List;
-
-public class DefaultIncrementalCompilation implements IncrementalCompilation {
-    private final List<File> recompile;
-    private final List<File> removed;
-
-    public DefaultIncrementalCompilation(List<File> recompile, List<File> removed) {
-        this.recompile = recompile;
-        this.removed = removed;
-    }
-
-    public List<File> getRecompile() {
-        return recompile;
-    }
-
-    public List<File> getRemoved() {
-        return removed;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludes.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludes.java
deleted file mode 100644
index e4ff737..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludes.java
+++ /dev/null
@@ -1,79 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-class DefaultSourceIncludes implements SourceIncludes, Serializable {
-    private final List<String> quotedIncludes = new ArrayList<String>();
-    private final List<String> systemIncludes = new ArrayList<String>();
-    private final List<String> macroIncludes = new ArrayList<String>();
-
-    public void addAll(List<String> includes) {
-        for (String value : includes) {
-            if (value.startsWith("<") && value.endsWith(">")) {
-                systemIncludes.add(strip(value));
-            } else if (value.startsWith("\"") && value.endsWith("\"")) {
-                quotedIncludes.add(strip(value));
-            } else {
-                macroIncludes.add(value);
-            }
-        }
-    }
-
-    private String strip(String include) {
-        return include.substring(1, include.length() - 1);
-    }
-
-    public List<String> getQuotedIncludes() {
-        return quotedIncludes;
-    }
-
-    public List<String> getSystemIncludes() {
-        return systemIncludes;
-    }
-
-    public List<String> getMacroIncludes() {
-        return macroIncludes;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof DefaultSourceIncludes)) {
-            return false;
-        }
-
-        DefaultSourceIncludes that = (DefaultSourceIncludes) o;
-
-        return macroIncludes.equals(that.macroIncludes)
-                && quotedIncludes.equals(that.quotedIncludes)
-                && systemIncludes.equals(that.systemIncludes);
-
-    }
-
-    @Override
-    public int hashCode() {
-        int result = quotedIncludes.hashCode();
-        result = 31 * result + systemIncludes.hashCode();
-        result = 31 * result + macroIncludes.hashCode();
-        return result;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParser.java
deleted file mode 100644
index 33a1c86..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParser.java
+++ /dev/null
@@ -1,43 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.nativebinaries.language.c.internal.incremental.sourceparser.CSourceParser;
-
-import java.io.File;
-
-public class DefaultSourceIncludesParser implements SourceIncludesParser {
-    private final CSourceParser sourceParser;
-    private final boolean importAware;
-
-    public DefaultSourceIncludesParser(CSourceParser sourceParser, boolean importAware) {
-        this.sourceParser = sourceParser;
-        this.importAware = importAware;
-    }
-
-    public SourceIncludes parseIncludes(File sourceFile) {
-        CSourceParser.SourceDetails sourceDetails = sourceParser.parseSource(sourceFile);
-        DefaultSourceIncludes defaultIncludes = new DefaultSourceIncludes();
-        defaultIncludes.addAll(sourceDetails.getIncludes());
-        if (importAware) {
-            defaultIncludes.addAll(sourceDetails.getImports());
-        }
-
-        return defaultIncludes;
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolver.java
deleted file mode 100644
index 6c93b35..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolver.java
+++ /dev/null
@@ -1,67 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-public class DefaultSourceIncludesResolver implements SourceIncludesResolver {
-    private final List<File> includePaths;
-
-    public DefaultSourceIncludesResolver(List<File> includePaths) {
-        this.includePaths = includePaths;
-    }
-
-    public Set<ResolvedInclude> resolveIncludes(File sourceFile, SourceIncludes includes) {
-        Set<ResolvedInclude> dependencies = new LinkedHashSet<ResolvedInclude>();
-        searchForDependencies(dependencies, prependSourceDir(sourceFile, includePaths), includes.getQuotedIncludes());
-        searchForDependencies(dependencies, includePaths, includes.getSystemIncludes());
-        if (!includes.getMacroIncludes().isEmpty()) {
-            dependencies.add(new ResolvedInclude(includes.getMacroIncludes().get(0), null));
-        }
-
-        return dependencies;
-    }
-
-    private List<File> prependSourceDir(File sourceFile, List<File> includePaths) {
-        List<File> quotedSearchPath = new ArrayList<File>(includePaths.size() + 1);
-        quotedSearchPath.add(sourceFile.getParentFile());
-        quotedSearchPath.addAll(includePaths);
-        return quotedSearchPath;
-    }
-
-    private void searchForDependencies(Set<ResolvedInclude> dependencies, List<File> searchPath, List<String> includes) {
-        for (String include : includes) {
-            searchForDependency(dependencies, searchPath, include);
-        }
-    }
-
-    private void searchForDependency(Set<ResolvedInclude> dependencies, List<File> searchPath, String include) {
-        for (File searchDir : searchPath) {
-            File candidate = new File(searchDir, include);
-            // TODO:DAZ This means that we'll never detect changed files if they are not on the defined include path
-            if (candidate.isFile()) {
-                dependencies.add(new ResolvedInclude(include, GFileUtils.canonicalise(candidate)));
-                return;
-            }
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilation.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilation.java
deleted file mode 100644
index 4a22885..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilation.java
+++ /dev/null
@@ -1,25 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-import java.util.List;
-
-public interface IncrementalCompilation {
-    List<File> getRecompile();
-
-    List<File> getRemoved();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessor.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessor.java
deleted file mode 100644
index d65c606..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessor.java
+++ /dev/null
@@ -1,148 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.api.internal.changedetection.state.FileSnapshotter;
-import org.gradle.cache.PersistentStateCache;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.*;
-
-public class IncrementalCompileProcessor {
-    private static final Logger LOGGER = LoggerFactory.getLogger(IncrementalCompileProcessor.class);
-
-    private final PersistentStateCache<CompilationState> previousCompileStateCache;
-    private final SourceIncludesParser sourceIncludesParser;
-    private final SourceIncludesResolver sourceIncludesResolver;
-    private final FileSnapshotter snapshotter;
-
-    public IncrementalCompileProcessor(PersistentStateCache<CompilationState> previousCompileStateCache, SourceIncludesResolver sourceIncludesResolver, SourceIncludesParser sourceIncludesParser,
-                                       FileSnapshotter snapshotter) {
-        this.previousCompileStateCache = previousCompileStateCache;
-        this.sourceIncludesResolver = sourceIncludesResolver;
-        this.sourceIncludesParser = sourceIncludesParser;
-        this.snapshotter = snapshotter;
-    }
-
-    public IncrementalCompilation processSourceFiles(Collection<File> sourceFiles) {
-        CompilationState previousCompileState = previousCompileStateCache.get();
-        final IncrementalCompileFiles result = new IncrementalCompileFiles(previousCompileState);
-
-        for (File sourceFile : sourceFiles) {
-            result.processSource(sourceFile);
-        }
-
-        previousCompileStateCache.set(result.current);
-
-        return new DefaultIncrementalCompilation(result.getModifiedSources(), result.getRemovedSources());
-    }
-
-    private class IncrementalCompileFiles {
-
-        private final List<File> recompile = new ArrayList<File>();
-
-        private final CompilationState previous;
-        private final CompilationState current = new CompilationState();
-        private final Map<File, Boolean> processed = new HashMap<File, Boolean>();
-
-        public IncrementalCompileFiles(CompilationState previousCompileState) {
-            this.previous = previousCompileState == null ? new CompilationState() : previousCompileState;
-        }
-
-        public void processSource(File sourceFile) {
-            current.addSourceInput(sourceFile);
-            if (checkChangedAndUpdateState(sourceFile) || !previous.getSourceInputs().contains(sourceFile)) {
-                recompile.add(sourceFile);
-            }
-        }
-
-        public boolean checkChangedAndUpdateState(File file) {
-            boolean changed = false;
-
-            if (processed.containsKey(file)) {
-                return processed.get(file);
-            }
-
-            if (!file.exists()) {
-                return true;
-            }
-
-            // Assume unchanged if we recurse to the same file due to dependency cycle
-            processed.put(file, false);
-
-            CompilationFileState previousState = previous.getState(file);
-            CompilationFileState newState = new CompilationFileState(snapshotter.snapshot(file).getHash());
-
-            // TODO:DAZ Keep a separate, build-scoped cache of file -> parsed includes. This would prevent need for reparsing per-variant and per-component.
-            if (!sameHash(previousState, newState)) {
-                changed = true;
-                newState.setSourceIncludes(sourceIncludesParser.parseIncludes(file));
-            } else {
-                newState.setSourceIncludes(previousState.getSourceIncludes());
-            }
-
-            newState.setResolvedIncludes(resolveIncludes(file, newState.getSourceIncludes()));
-            // Compare the previous resolved includes with resolving now.
-            if (!sameResolved(previousState, newState)) {
-                changed = true;
-            }
-
-            current.setState(file, newState);
-
-            for (ResolvedInclude dep : newState.getResolvedIncludes()) {
-                if (dep.isUnknown()) {
-                    LOGGER.info(String.format("Cannot determine changed state of included '%s' in source file '%s'. Assuming changed.", dep.getInclude(), file.getName()));
-                    changed = true;
-                } else {
-                    boolean depChanged = checkChangedAndUpdateState(dep.getFile());
-                    changed = changed || depChanged;
-                }
-            }
-
-            processed.put(file, changed);
-
-            return changed;
-        }
-
-        private boolean sameHash(CompilationFileState previousState, CompilationFileState newState) {
-            return previousState != null && Arrays.equals(newState.getHash(), previousState.getHash());
-        }
-
-        private boolean sameResolved(CompilationFileState previousState, CompilationFileState newState) {
-            return previousState != null && newState.getResolvedIncludes().equals(previousState.getResolvedIncludes());
-        }
-
-        private Set<ResolvedInclude> resolveIncludes(File file, SourceIncludes sourceIncludes) {
-            return sourceIncludesResolver.resolveIncludes(file, sourceIncludes);
-        }
-
-        public List<File> getModifiedSources() {
-            return recompile;
-        }
-
-        public List<File> getRemovedSources() {
-            List<File> removed = new ArrayList<File>();
-            for (File previousSource : previous.getSourceInputs()) {
-                if (!current.getSourceInputs().contains(previousSource)) {
-                    removed.add(previousSource);
-                }
-            }
-            return removed;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilerBuilder.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilerBuilder.java
deleted file mode 100644
index c08e454..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompilerBuilder.java
+++ /dev/null
@@ -1,75 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.state.FileSnapshotter;
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.nativebinaries.language.c.internal.incremental.sourceparser.CSourceParser;
-import org.gradle.nativebinaries.language.c.internal.incremental.sourceparser.RegexBackedCSourceParser;
-import org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile;
-import org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-
-public class IncrementalCompilerBuilder {
-    private final TaskInternal task;
-    private final TaskArtifactStateCacheAccess cacheAccess;
-    private final SourceIncludesParser sourceIncludesParser;
-    private final FileSnapshotter fileSnapshotter;
-    private boolean cleanCompile;
-    private Iterable<File> includes;
-
-    public IncrementalCompilerBuilder(TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter, TaskInternal task) {
-        this.task = task;
-        this.sourceIncludesParser = createIncludesParser(task);
-        this.cacheAccess = cacheAccess;
-        this.fileSnapshotter = fileSnapshotter;
-    }
-
-    private static SourceIncludesParser createIncludesParser(TaskInternal task) {
-        CSourceParser sourceParser = new RegexBackedCSourceParser();
-        boolean importsAreIncludes = ObjectiveCCompile.class.isAssignableFrom(task.getClass()) || ObjectiveCppCompile.class.isAssignableFrom(task.getClass());
-        return new DefaultSourceIncludesParser(sourceParser, importsAreIncludes);
-    }
-
-    public IncrementalCompilerBuilder withCleanCompile() {
-        this.cleanCompile = true;
-        return this;
-    }
-
-    public IncrementalCompilerBuilder withIncludes(Iterable<File> includes) {
-        this.includes = includes;
-        return this;
-    }
-
-    public Compiler<NativeCompileSpec> createIncrementalCompiler(Compiler<NativeCompileSpec> compiler) {
-        if (cleanCompile) {
-            return createCleaningCompiler(compiler, task, includes);
-        }
-        return createIncrementalCompiler(compiler, task, includes);
-    }
-
-    private Compiler<NativeCompileSpec> createIncrementalCompiler(Compiler<NativeCompileSpec> compiler, TaskInternal task, Iterable<File> includes) {
-        return new IncrementalNativeCompiler(task, sourceIncludesParser, includes, cacheAccess, fileSnapshotter, compiler);
-    }
-
-    private Compiler<NativeCompileSpec> createCleaningCompiler(Compiler<NativeCompileSpec> compiler, TaskInternal task, Iterable<File> includes) {
-        return new CleanCompilingNativeCompiler(task, sourceIncludesParser, includes, cacheAccess, fileSnapshotter, compiler);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompiler.java
deleted file mode 100644
index 1c8fa09..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompiler.java
+++ /dev/null
@@ -1,45 +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.nativebinaries.language.c.internal.incremental;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.changedetection.state.FileSnapshotter;
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-
-public class IncrementalNativeCompiler extends AbstractIncrementalNativeCompiler {
-    private final Compiler<NativeCompileSpec> delegateCompiler;
-
-    public IncrementalNativeCompiler(TaskInternal task, SourceIncludesParser sourceIncludesParser, Iterable<File> includes,
-                                     TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter, Compiler<NativeCompileSpec> delegateCompiler) {
-        super(task, sourceIncludesParser, includes, cacheAccess, fileSnapshotter);
-        this.delegateCompiler = delegateCompiler;
-    }
-
-    @Override
-    protected WorkResult doIncrementalCompile(IncrementalCompileProcessor processor, NativeCompileSpec spec) {
-        IncrementalCompilation compilation = processor.processSourceFiles(spec.getSourceFiles());
-
-        // Determine the actual sources to clean/compile
-        spec.setSourceFiles(compilation.getRecompile());
-        spec.setRemovedSourceFiles(compilation.getRemoved());
-        return delegateCompiler.execute(spec);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/ResolvedInclude.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/ResolvedInclude.java
deleted file mode 100644
index ae27164..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/ResolvedInclude.java
+++ /dev/null
@@ -1,67 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-import java.io.Serializable;
-
-public class ResolvedInclude implements Serializable {
-    private final String include;
-    private final File dependencyFile;
-
-    public ResolvedInclude(String include, File dependencyFile) {
-        this.include = include;
-        this.dependencyFile = dependencyFile;
-    }
-
-    public boolean isUnknown() {
-        return dependencyFile == null;
-    }
-
-    public String getInclude() {
-        return include;
-    }
-
-    public File getFile() {
-        return dependencyFile;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Resolved include '%s'", include);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof ResolvedInclude)) {
-            return false;
-        }
-
-        ResolvedInclude that = (ResolvedInclude) o;
-
-        return include.equals(that.include)
-                && (dependencyFile == null ? that.dependencyFile == null : dependencyFile.equals(that.dependencyFile));
-
-    }
-
-    @Override
-    public int hashCode() {
-        return include.hashCode();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludes.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludes.java
deleted file mode 100644
index cb8e170..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludes.java
+++ /dev/null
@@ -1,24 +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.nativebinaries.language.c.internal.incremental;
-
-import java.util.List;
-
-public interface SourceIncludes {
-    List<String> getQuotedIncludes();
-    List<String> getSystemIncludes();
-    List<String> getMacroIncludes();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesParser.java
deleted file mode 100644
index 906fee9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesParser.java
+++ /dev/null
@@ -1,24 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-
-public interface SourceIncludesParser {
-
-    SourceIncludes parseIncludes(File sourceFile);
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesResolver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesResolver.java
deleted file mode 100644
index 30fc7f0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/SourceIncludesResolver.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.c.internal.incremental;
-
-import java.io.File;
-import java.util.Set;
-
-public interface SourceIncludesResolver {
-    Set<ResolvedInclude> resolveIncludes(File sourceFile, SourceIncludes includes);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/CSourceParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/CSourceParser.java
deleted file mode 100644
index 8098d45..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/CSourceParser.java
+++ /dev/null
@@ -1,33 +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.nativebinaries.language.c.internal.incremental.sourceparser;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A parser to extract information from C-compatible source files.
- */
-public interface CSourceParser {
-
-    SourceDetails parseSource(File sourceFile);
-
-    interface SourceDetails {
-        List<String> getIncludes();
-        List<String> getImports();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReader.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReader.java
deleted file mode 100644
index 6e38997..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReader.java
+++ /dev/null
@@ -1,145 +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.nativebinaries.language.c.internal.incremental.sourceparser;
-
-import org.apache.tools.ant.filters.BaseFilterReader;
-
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * Replaces c-style comments with a single space, and removes line-continuation characters.
- * This code is largely adopted from org.apache.tools.ant.filters.StripJavaComments.
- *
- * This avoids the synchronisation overhead of PushbackReader and is _not_ threadsafe.
- */
-public class PreprocessingReader extends BaseFilterReader {
-    /**
-     * The read-ahead characters, used for reading ahead up to 2 characters and pushing back into stream.
-     * A value of -1 indicates that no character is in the buffer.
-     */
-    private int[] readAheadChars = new int[2];
-
-    /**
-     * Whether or not the parser is currently in the middle of a string literal.
-     */
-    private boolean inString;
-
-    /**
-     * Whether or not the last char has been a backslash.
-     */
-    private boolean quoted;
-
-    public PreprocessingReader(Reader in) {
-        super(in);
-    }
-
-    /**
-     * Returns the next character in the filtered stream:
-     * <ul>
-     *     <li>Comments will be replaced by a single space</li>
-     *     <li>Line continuation (backslash-newline) will be removed</li>
-     * </ul>
-     */
-    public int read() throws IOException {
-        int ch = next();
-
-        if (ch == '\\') {
-            if (discardNewLine()) {
-                return read();
-            }
-        }
-
-        if (ch == '"' && !quoted) {
-            inString = !inString;
-            quoted = false;
-        } else if (ch == '\\') {
-            quoted = !quoted;
-        } else {
-            quoted = false;
-            if (!inString) {
-                if (ch == '/') {
-                    ch = next();
-                    if (ch == '/') {
-                        while (ch != '\n' && ch != -1 && ch != '\r') {
-                            ch = next();
-                        }
-                    } else if (ch == '*') {
-                        while (ch != -1) {
-                            ch = next();
-                            if (ch == '*') {
-                                ch = next();
-                                while (ch == '*') {
-                                    ch = next();
-                                }
-
-                                if (ch == '/') {
-                                    ch = ' ';
-                                    break;
-                                }
-                            }
-                        }
-                    } else {
-                        pushBack(ch);
-                        ch = '/';
-                    }
-                }
-            }
-        }
-
-        return ch;
-    }
-
-    private boolean discardNewLine() throws IOException {
-        int nextChar = next();
-        if (nextChar == '\n') {
-            return true; // '\\\n' discarded from stream
-        } else if (nextChar == '\r') {
-            int followingChar = next();
-            if (followingChar == '\n') {
-                return true; // '\\\r\n' discarded from stream
-            }
-            pushBack(nextChar);
-            pushBack(followingChar);
-            return false;
-        } else {
-            pushBack(nextChar);
-            return false;
-        }
-    }
-
-    private int next() throws IOException {
-        if (readAheadChars[0] != -1) {
-            int ch = readAheadChars[0];
-            readAheadChars[0] = readAheadChars[1];
-            readAheadChars[1] = -1;
-            return ch;
-        }
-
-        return in.read();
-    }
-
-    private void pushBack(int ch) {
-        if (readAheadChars[1] != -1) {
-            throw new IllegalStateException();
-        }
-        if (readAheadChars[0] != -1) {
-            readAheadChars[1] = ch;
-        } else {
-            readAheadChars[0] = ch;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParser.java
deleted file mode 100644
index 8d051c3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParser.java
+++ /dev/null
@@ -1,82 +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.nativebinaries.language.c.internal.incremental.sourceparser;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.api.UncheckedIOException;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-// TODO:DAZ We might be better with a hand-crafter parser
-public class RegexBackedCSourceParser implements CSourceParser {
-    private static final String INCLUDE_IMPORT_PATTERN = "\\s*#\\s*(include|import)\\s+((<[^>]+>)|(\"[^\"]+\")|(\\w+))";
-    private final Pattern includePattern;
-
-    public RegexBackedCSourceParser() {
-        this.includePattern = Pattern.compile(INCLUDE_IMPORT_PATTERN, Pattern.CASE_INSENSITIVE);
-    }
-
-    public SourceDetails parseSource(File sourceFile) {
-        DefaultSourceDetails sourceDetails = new DefaultSourceDetails();
-        parseFile(sourceFile, sourceDetails);
-        return sourceDetails;
-    }
-
-    private void parseFile(File file, DefaultSourceDetails sourceDetails) {
-        try {
-            BufferedReader bf = new BufferedReader(new PreprocessingReader(new BufferedReader(new FileReader(file))));
-
-            try {
-                String line;
-                while ((line = bf.readLine()) != null) {
-                    Matcher m = includePattern.matcher(line);
-
-                    if (m.lookingAt()) {
-                        boolean isImport = "import".equals(m.group(1));
-                        String value = m.group(2);
-                        if (isImport) {
-                            sourceDetails.getImports().add(value);
-                        } else {
-                            sourceDetails.getIncludes().add(value);
-                        }
-                    }
-                }
-            } finally {
-                IOUtils.closeQuietly(bf);
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private static class DefaultSourceDetails implements SourceDetails {
-        private final List<String> includes = new ArrayList<String>();
-        private final List<String> imports = new ArrayList<String>();
-
-        public List<String> getIncludes() {
-            return includes;
-        }
-
-        public List<String> getImports() {
-            return imports;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/package-info.java
deleted file mode 100644
index 74f4ebb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for compiling and linking C sources for a native runtime.
- */
-package org.gradle.nativebinaries.language.c;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPlugin.groovy
deleted file mode 100644
index 53427c9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPlugin.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.nativebinaries.language.c.plugins
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.c.plugins.CLangPlugin
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.c.tasks.CCompile
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-/**
- * A plugin for projects wishing to build native binary components from C sources.
- *
- * <p>Automatically includes the {@link CLangPlugin} for core C++ support and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link CCompile} task for each {@link CSourceSet} to compile the C sources.</li>
- */
- at Incubating
-class CNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(CLangPlugin)
-
-        // TODO:DAZ Clean this up (see CppNativeBinariesPlugin)
-        project.executables.all { Executable executable ->
-            addLanguageExtensionsToComponent(executable)
-        }
-        project.libraries.all { Library library ->
-            addLanguageExtensionsToComponent(library)
-        }
-
-        project.binaries.withType(ProjectNativeBinary) { ProjectNativeBinaryInternal binary ->
-            binary.source.withType(CSourceSet).all { CSourceSet sourceSet ->
-                if (sourceSet.mayHaveSources) {
-                    def compileTask = createCompileTask(project, binary, sourceSet)
-                    compileTask.dependsOn sourceSet
-                    binary.tasks.add compileTask
-                    binary.tasks.builder.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
-                }
-            }
-        }
-    }
-
-    private def addLanguageExtensionsToComponent(ProjectNativeComponent component) {
-        component.binaries.all { binary ->
-            binary.extensions.create("cCompiler", DefaultPreprocessingTool)
-        }
-    }
-
-    private def createCompileTask(ProjectInternal project, ProjectNativeBinaryInternal binary, CSourceSet sourceSet) {
-        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: CCompile) {
-            description = "Compiles the $sourceSet of $binary"
-        }
-
-        compileTask.toolChain = binary.toolChain
-        compileTask.targetPlatform = binary.targetPlatform
-        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
-
-        compileTask.includes {
-            sourceSet.exportedHeaders.srcDirs
-        }
-        compileTask.includes {
-            binary.getLibs(sourceSet)*.includeRoots
-        }
-        compileTask.source sourceSet.source
-
-        compileTask.objectFileDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        compileTask.macros = binary.cCompiler.macros
-        compileTask.compilerArgs = binary.cCompiler.args
-
-        compileTask
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CPlugin.groovy
deleted file mode 100644
index bcefa85..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/plugins/CPlugin.groovy
+++ /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.nativebinaries.language.c.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from C sources.
- *
- * <p>Adds core tool chain support to the {@link CNativeBinariesPlugin}.</p>
- */
- at Incubating
-class CPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-        project.plugins.apply(CNativeBinariesPlugin)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/tasks/AbstractNativeCompileTask.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/tasks/AbstractNativeCompileTask.groovy
deleted file mode 100644
index 0a09f66..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/tasks/AbstractNativeCompileTask.groovy
+++ /dev/null
@@ -1,145 +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.nativebinaries.language.c.tasks
-import org.gradle.api.DefaultTask
-import org.gradle.api.Incubating
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.changedetection.state.FileSnapshotter
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-import org.gradle.nativebinaries.language.c.internal.incremental.IncrementalCompilerBuilder
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-
-import javax.inject.Inject
-/**
- * Compiles native source files into object files.
- */
- at Incubating
-abstract class AbstractNativeCompileTask extends DefaultTask {
-    private final IncrementalCompilerBuilder incrementalCompilerBuilder
-
-    /**
-     * The tool chain used for compilation.
-     */
-    ToolChain toolChain
-
-    /**
-     * The platform being targeted.
-     */
-    Platform targetPlatform
-
-    /**
-     * Should the compiler generate position independent code?
-     */
-    @Input
-    boolean positionIndependentCode
-
-    /**
-     * The directory where object files will be generated.
-     */
-    @OutputDirectory
-    File objectFileDir
-
-    /**
-     * Returns the header directories to be used for compilation.
-     */
-    @InputFiles
-    FileCollection includes
-
-    /**
-     * Returns the source files to be compiled.
-     */
-    @InputFiles
-    FileCollection source
-
-    // Invalidate output when the tool chain output changes
-    @Input
-    def getOutputType() {
-        return "${toolChain.outputType}:${targetPlatform.compatibilityString}"
-    }
-
-    /**
-     * Macros that should be defined for the compiler.
-     */
-    @Input
-    Map<String, String> macros
-
-    /**
-     * Additional arguments to provide to the compiler.
-     */
-    @Input
-    List<String> compilerArgs
-
-    @Inject
-    AbstractNativeCompileTask() {
-        incrementalCompilerBuilder = new IncrementalCompilerBuilder(services.get(TaskArtifactStateCacheAccess),
-                                                                    services.get(FileSnapshotter), this)
-        includes = project.files()
-        source = project.files()
-    }
-
-    @TaskAction
-    void compile(IncrementalTaskInputs inputs) {
-
-        def spec = createCompileSpec()
-        spec.tempDir = getTemporaryDir()
-        spec.objectFileDir = getObjectFileDir()
-        spec.include getIncludes()
-        spec.source getSource()
-        spec.macros = getMacros()
-        spec.args getCompilerArgs()
-        spec.positionIndependentCode = isPositionIndependentCode()
-
-        PlatformToolChain platformToolChain = toolChain.target(targetPlatform)
-        if (!inputs.incremental) {
-            incrementalCompilerBuilder.withCleanCompile()
-        }
-        incrementalCompilerBuilder.withIncludes(includes)
-        final compiler = createCompiler(platformToolChain)
-        final incrementalCompiler = incrementalCompilerBuilder.createIncrementalCompiler(compiler)
-
-        def result = incrementalCompiler.execute(spec)
-        didWork = result.didWork
-    }
-
-    protected abstract NativeCompileSpec createCompileSpec();
-
-    protected abstract Compiler<NativeCompileSpec> createCompiler(PlatformToolChain toolChain)
-
-    /**
-     * Add directories where the compiler should search for header files.
-     */
-    void includes(Object 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/nativebinaries/language/c/tasks/CCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/tasks/CCompile.groovy
deleted file mode 100644
index e0f3f58..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/c/tasks/CCompile.groovy
+++ /dev/null
@@ -1,36 +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.nativebinaries.language.c.tasks
-import org.gradle.api.Incubating
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.nativebinaries.language.c.internal.DefaultCCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-/**
- * Compiles C source files into object files.
- */
- at Incubating
-class CCompile extends AbstractNativeCompileTask {
-    @Override
-    protected NativeCompileSpec createCompileSpec() {
-        new DefaultCCompileSpec()
-    }
-
-    protected Compiler<NativeCompileSpec> createCompiler(PlatformToolChain toolChain) {
-        toolChain.createCCompiler()
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/CppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/CppCompileSpec.java
deleted file mode 100644
index 03d6217..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/CppCompileSpec.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.nativebinaries.language.cpp.internal;
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-public interface CppCompileSpec extends NativeCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppCompileSpec.java
deleted file mode 100644
index 2a7d4c9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppCompileSpec.java
+++ /dev/null
@@ -1,22 +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.nativebinaries.language.cpp.internal;
-
-import org.gradle.nativebinaries.language.internal.AbstractNativeCompileSpec;
-
-public class DefaultCppCompileSpec extends AbstractNativeCompileSpec implements CppCompileSpec {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/package-info.java
deleted file mode 100644
index 696f621..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for compiling and linking C++ sources for a native runtime.
- */
-package org.gradle.nativebinaries.language.cpp;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPlugin.groovy
deleted file mode 100644
index 6d68b09..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPlugin.groovy
+++ /dev/null
@@ -1,89 +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.nativebinaries.language.cpp.plugins
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.language.cpp.plugins.CppLangPlugin
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.cpp.tasks.CppCompile
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-/**
- * A plugin for projects wishing to build native binary components from C++ sources.
- *
- * <p>Automatically includes the {@link org.gradle.language.cpp.plugins.CppLangPlugin} for core C++ support and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link CppCompile} task for each {@link CppSourceSet} to compile the C++ sources.</li>
- */
- at Incubating
-class CppNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(CppLangPlugin)
-
-        // TODO:DAZ It's ugly that we can't do this as project.binaries.all, but this is the way I could
-        // add the cppCompiler in time to allow it to be configured within the component.binaries.all block.
-        project.executables.all { Executable executable ->
-            executable.binaries.all { binary ->
-                binary.extensions.create("cppCompiler", DefaultPreprocessingTool)
-            }
-        }
-        project.libraries.all { Library library ->
-            library.binaries.all { binary ->
-                binary.extensions.create("cppCompiler", DefaultPreprocessingTool)
-            }
-        }
-
-        project.binaries.withType(ProjectNativeBinary) { ProjectNativeBinaryInternal binary ->
-            binary.source.withType(CppSourceSet).all { CppSourceSet sourceSet ->
-                if (sourceSet.mayHaveSources) {
-                    def compileTask = createCompileTask(project, binary, sourceSet)
-                    compileTask.dependsOn sourceSet
-                    binary.tasks.add compileTask
-                    binary.tasks.builder.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
-                }
-            }
-        }
-    }
-
-    private def createCompileTask(ProjectInternal project, ProjectNativeBinaryInternal binary, CppSourceSet sourceSet) {
-        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: CppCompile) {
-            description = "Compiles the $sourceSet of $binary"
-        }
-
-        compileTask.toolChain = binary.toolChain
-        compileTask.targetPlatform = binary.targetPlatform
-        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
-
-        compileTask.includes {
-            sourceSet.exportedHeaders.srcDirs
-        }
-        compileTask.includes {
-            binary.getLibs(sourceSet)*.includeRoots
-        }
-        compileTask.source sourceSet.source
-
-        compileTask.objectFileDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        compileTask.macros = binary.cppCompiler.macros
-        compileTask.compilerArgs = binary.cppCompiler.args
-
-        compileTask
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPlugin.groovy
deleted file mode 100644
index 7f2c322..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppPlugin.groovy
+++ /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.nativebinaries.language.cpp.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from C++ sources.
- *
- * <p>Adds core tool chain support to the {@link CppNativeBinariesPlugin}.</p>
- */
- at Incubating
-class CppPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-        project.plugins.apply(CppNativeBinariesPlugin)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/package-info.java
deleted file mode 100644
index 4780b1d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/plugins/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Plugins for buildings C++ projects.
- */
-package org.gradle.nativebinaries.language.cpp.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompile.groovy
deleted file mode 100644
index 7aa2fe5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompile.groovy
+++ /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.nativebinaries.language.cpp.tasks
-import org.gradle.api.Incubating
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.nativebinaries.language.c.tasks.AbstractNativeCompileTask
-import org.gradle.nativebinaries.language.cpp.internal.DefaultCppCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-/**
- * Compiles C++ source files into object files.
- */
- at Incubating
-class CppCompile extends AbstractNativeCompileTask {
-    @Override
-    protected NativeCompileSpec createCompileSpec() {
-        new DefaultCppCompileSpec()
-    }
-
-    @Override
-    protected Compiler<NativeCompileSpec> createCompiler(PlatformToolChain toolChain) {
-        return toolChain.createCppCompiler()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/package-info.java
deleted file mode 100644
index 55609c5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/cpp/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for building C++ projects.
- */
-package org.gradle.nativebinaries.language.cpp.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/AbstractNativeCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/AbstractNativeCompileSpec.java
deleted file mode 100644
index 74112f3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/AbstractNativeCompileSpec.java
+++ /dev/null
@@ -1,109 +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.nativebinaries.language.internal;
-
-import org.gradle.nativebinaries.internal.AbstractBinaryToolSpec;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-import java.util.*;
-
-public abstract class AbstractNativeCompileSpec extends AbstractBinaryToolSpec implements NativeCompileSpec {
-
-    private List<File> includeRoots = new ArrayList<File>();
-    private List<File> sourceFiles = new ArrayList<File>();
-    private List<File> removedSourceFiles = new ArrayList<File>();
-    private Map<String, String> macros = new LinkedHashMap<String, String>();
-    private File objectFileDir;
-    private boolean positionIndependentCode;
-
-    public List<File> getIncludeRoots() {
-        return includeRoots;
-    }
-
-    public void include(File... includeRoots) {
-        Collections.addAll(this.includeRoots, includeRoots);
-    }
-
-    public void include(Iterable<File> includeRoots) {
-        addAll(this.includeRoots, includeRoots);
-    }
-
-    public List<File> getSourceFiles() {
-        return sourceFiles;
-    }
-
-    public void source(Iterable<File> sources) {
-        addAll(sourceFiles, sources);
-    }
-
-    public void setSourceFiles(Collection<File> sources) {
-        sourceFiles.clear();
-        sourceFiles.addAll(sources);
-    }
-
-    public List<File> getRemovedSourceFiles() {
-        return removedSourceFiles;
-    }
-
-    public void removedSource(Iterable<File> sources) {
-        addAll(removedSourceFiles, sources);
-    }
-
-    public void setRemovedSourceFiles(Collection<File> sources) {
-        removedSourceFiles.clear();
-        removedSourceFiles.addAll(sources);
-    }
-
-    public File getObjectFileDir() {
-        return objectFileDir;
-    }
-
-    public void setObjectFileDir(File objectFileDir) {
-        this.objectFileDir = objectFileDir;
-    }
-
-    public Map<String, String> getMacros() {
-        return macros;
-    }
-
-    public void setMacros(Map<String, String> macros) {
-        this.macros = macros;
-    }
-
-    public void define(String name) {
-        macros.put(name, null);
-    }
-
-    public void define(String name, String value) {
-        macros.put(name, value);
-    }
-
-    public boolean isPositionIndependentCode() {
-        return positionIndependentCode;
-    }
-
-    public void setPositionIndependentCode(boolean positionIndependentCode) {
-        this.positionIndependentCode = positionIndependentCode;
-    }
-
-    private void addAll(List<File> list, Iterable<File> iterable) {
-        for (File file : iterable) {
-            list.add(file);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/DefaultPreprocessingTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/DefaultPreprocessingTool.java
deleted file mode 100644
index 74e34ee..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/internal/DefaultPreprocessingTool.java
+++ /dev/null
@@ -1,39 +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.nativebinaries.language.internal;
-
-import org.gradle.nativebinaries.internal.DefaultTool;
-import org.gradle.nativebinaries.language.PreprocessingTool;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class DefaultPreprocessingTool extends DefaultTool implements PreprocessingTool {
-    private final Map<String, String> definitions = new LinkedHashMap<String, String>();
-
-    public Map<String, String> getMacros() {
-        return definitions;
-    }
-
-    public void define(String name) {
-        definitions.put(name, null);
-    }
-
-    public void define(String name, String definition) {
-        definitions.put(name, definition);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/DefaultObjectiveCCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/DefaultObjectiveCCompileSpec.java
deleted file mode 100644
index 68ef153..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/DefaultObjectiveCCompileSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.objectivec.internal;
-
-import org.gradle.nativebinaries.language.internal.AbstractNativeCompileSpec;
-
-public class DefaultObjectiveCCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/ObjectiveCCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/ObjectiveCCompileSpec.java
deleted file mode 100644
index 88bd223..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/internal/ObjectiveCCompileSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.objectivec.internal;
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-public interface ObjectiveCCompileSpec extends NativeCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/package-info.java
deleted file mode 100644
index 16fda4a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for compiling and linking Objective-C sources for a native runtime.
- */
-package org.gradle.nativebinaries.language.objectivec;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPlugin.groovy
deleted file mode 100644
index ea340e8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPlugin.groovy
+++ /dev/null
@@ -1,89 +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.nativebinaries.language.objectivec.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.objectivec.ObjectiveCSourceSet
-import org.gradle.language.objectivec.plugins.ObjectiveCLangPlugin
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-/**
- * A plugin for projects wishing to build native binary components from Objective-C sources.
- *
- * <p>Automatically includes the {@link org.gradle.language.objectivec.plugins.ObjectiveCLangPlugin} for core Objective-C support and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link ObjectiveCCompile} task for each {@link ObjectiveCSourceSet} to compile the Objective-C sources.</li>
- */
- at Incubating
-class ObjectiveCNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(ObjectiveCLangPlugin)
-
-        project.executables.all { Executable executable ->
-            executable.binaries.all { binary ->
-                binary.extensions.create("objcCompiler", DefaultPreprocessingTool)
-            }
-        }
-
-        project.libraries.all { Library library ->
-            library.binaries.all { binary ->
-                binary.extensions.create("objcCompiler", DefaultPreprocessingTool)
-            }
-        }
-
-        project.binaries.withType(ProjectNativeBinary) { ProjectNativeBinary binary ->
-            binary.source.withType(ObjectiveCSourceSet).all { ObjectiveCSourceSet sourceSet ->
-                if (sourceSet.mayHaveSources) {
-                    def compileTask = createCompileTask(project, binary, sourceSet)
-                    binary.tasks.add compileTask
-                    binary.tasks.builder.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
-                }
-            }
-        }
-    }
-
-    private def createCompileTask(ProjectInternal project, ProjectNativeBinaryInternal binary, ObjectiveCSourceSet sourceSet) {
-        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: ObjectiveCCompile) {
-            description = "Compiles the $sourceSet of $binary"
-        }
-
-        compileTask.toolChain = binary.toolChain
-        compileTask.targetPlatform = binary.targetPlatform
-        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
-
-        compileTask.includes {
-            sourceSet.exportedHeaders.srcDirs
-        }
-        compileTask.includes {
-            binary.getLibs(sourceSet)*.includeRoots
-        }
-
-        compileTask.source sourceSet.source
-
-        compileTask.objectFileDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        compileTask.macros = binary.objcCompiler.macros
-        compileTask.compilerArgs = binary.objcCompiler.args
-
-        compileTask
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPlugin.groovy
deleted file mode 100644
index 14feee5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCPlugin.groovy
+++ /dev/null
@@ -1,36 +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.nativebinaries.language.objectivec.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Objective-C sources.
- *
- * <p>Adds core tool chain support to the {@link ObjectiveCNativeBinariesPlugin}.</p>
- */
- at Incubating
-class ObjectiveCPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-        project.plugins.apply(ObjectiveCNativeBinariesPlugin)
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/package-info.java
deleted file mode 100644
index 9760195..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Plugins for building Objective-C projects.
- */
-package org.gradle.nativebinaries.language.objectivec.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/ObjectiveCCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/ObjectiveCCompile.groovy
deleted file mode 100644
index 0d7a046..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/ObjectiveCCompile.groovy
+++ /dev/null
@@ -1,38 +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.nativebinaries.language.objectivec.tasks
-import org.gradle.api.Incubating
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.nativebinaries.language.c.tasks.AbstractNativeCompileTask
-import org.gradle.nativebinaries.language.objectivec.internal.DefaultObjectiveCCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-/**
- * Compiles Objective-C source files into object files.
- */
- at Incubating
-class ObjectiveCCompile extends AbstractNativeCompileTask {
-    @Override
-    protected NativeCompileSpec createCompileSpec() {
-        new DefaultObjectiveCCompileSpec()
-    }
-
-    @Override
-    protected Compiler<NativeCompileSpec> createCompiler(PlatformToolChain toolChain) {
-        return toolChain.createObjectiveCCompiler()
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/package-info.java
deleted file mode 100644
index 555e538..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivec/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for building Objective-C projects.
- */
-package org.gradle.nativebinaries.language.objectivec.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.java
deleted file mode 100644
index 8125b84..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.objectivecpp.internal;
-
-import org.gradle.nativebinaries.language.internal.AbstractNativeCompileSpec;
-
-public class DefaultObjectiveCppCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCppCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/ObjectiveCppCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/ObjectiveCppCompileSpec.java
deleted file mode 100644
index 4ad9a26..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/internal/ObjectiveCppCompileSpec.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.language.objectivecpp.internal;
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-public interface ObjectiveCppCompileSpec extends NativeCompileSpec {
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/package-info.java
deleted file mode 100644
index 082204c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for compiling and linking Objective-C++ sources for a native runtime.
- */
-package org.gradle.nativebinaries.language.objectivecpp;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPlugin.groovy
deleted file mode 100644
index 11a3ab4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPlugin.groovy
+++ /dev/null
@@ -1,90 +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.nativebinaries.language.objectivecpp.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
-import org.gradle.language.objectivecpp.plugins.ObjectiveCppLangPlugin
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Objective-C++ sources.
- *
- * <p>Automatically includes the {@link org.gradle.language.objectivecpp.plugins.ObjectiveCppLangPlugin} for core Objective-C++ support and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link ObjectiveCppCompile} task for each {@link ObjectiveCppSourceSet} to compile the Objective-C++ sources.</li>
- */
- at Incubating
-class ObjectiveCppNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(ObjectiveCppLangPlugin)
-
-        project.executables.all { Executable executable ->
-            executable.binaries.all { binary ->
-                binary.extensions.create("objcppCompiler", DefaultPreprocessingTool)
-            }
-        }
-
-        project.libraries.all { Library library ->
-            library.binaries.all { binary ->
-                binary.extensions.create("objcppCompiler", DefaultPreprocessingTool)
-            }
-        }
-
-        project.binaries.withType(NativeBinary) { ProjectNativeBinaryInternal binary ->
-            binary.source.withType(ObjectiveCppSourceSet).all { ObjectiveCppSourceSet sourceSet ->
-                if (sourceSet.mayHaveSources) {
-                    def compileTask = createCompileTask(project, binary, sourceSet)
-                    binary.tasks.add compileTask
-                    binary.tasks.builder.source compileTask.outputs.files.asFileTree.matching { include '**/*.obj', '**/*.o' }
-                }
-            }
-        }
-    }
-
-    private def createCompileTask(ProjectInternal project, ProjectNativeBinaryInternal binary, ObjectiveCppSourceSet sourceSet) {
-        def compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: ObjectiveCppCompile) {
-            description = "Compiles the $sourceSet of $binary"
-        }
-
-        compileTask.toolChain = binary.toolChain
-        compileTask.targetPlatform = binary.targetPlatform
-        compileTask.positionIndependentCode = binary instanceof SharedLibraryBinary
-
-        compileTask.includes {
-            sourceSet.exportedHeaders.srcDirs
-        }
-
-        compileTask.source sourceSet.source
-        binary.getLibs(sourceSet).each { deps ->
-            compileTask.includes deps.includeRoots
-        }
-
-        compileTask.objectFileDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        compileTask.macros = binary.objcppCompiler.macros
-        compileTask.compilerArgs = binary.objcppCompiler.args
-
-        compileTask
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPlugin.groovy
deleted file mode 100644
index d871662..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppPlugin.groovy
+++ /dev/null
@@ -1,36 +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.nativebinaries.language.objectivecpp.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Objective-C++ sources.
- *
- * <p>Adds core tool chain support to the {@link ObjectiveCppNativeBinariesPlugin}.</p>
- */
- at Incubating
-class ObjectiveCppPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-        project.plugins.apply(ObjectiveCppNativeBinariesPlugin)
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/package-info.java
deleted file mode 100644
index 70315a5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Plugins for building Objective-C++ projects.
- */
-package org.gradle.nativebinaries.language.objectivecpp.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/ObjectiveCppCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/ObjectiveCppCompile.groovy
deleted file mode 100644
index 528c4ec..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/ObjectiveCppCompile.groovy
+++ /dev/null
@@ -1,38 +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.nativebinaries.language.objectivecpp.tasks
-import org.gradle.api.Incubating
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.nativebinaries.language.c.tasks.AbstractNativeCompileTask
-import org.gradle.nativebinaries.language.objectivecpp.internal.DefaultObjectiveCppCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-/**
- * Compiles Objective-C++ source files into object files.
- */
- at Incubating
-class ObjectiveCppCompile extends AbstractNativeCompileTask {
-    @Override
-    protected NativeCompileSpec createCompileSpec() {
-        new DefaultObjectiveCppCompileSpec()
-    }
-
-    @Override
-    protected Compiler<NativeCompileSpec> createCompiler(PlatformToolChain toolChain) {
-        return toolChain.createObjectiveCppCompiler()
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/package-info.java
deleted file mode 100644
index cdd90f3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/objectivecpp/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for building Objective-C++ projects.
- */
-package org.gradle.nativebinaries.language.objectivecpp.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/package-info.java
deleted file mode 100644
index 6b6cb32..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes for building sources for a native runtime.
- */
-package org.gradle.nativebinaries.language;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/DefaultWindowsResourceCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/DefaultWindowsResourceCompileSpec.java
deleted file mode 100644
index 801e90f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/DefaultWindowsResourceCompileSpec.java
+++ /dev/null
@@ -1,21 +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.nativebinaries.language.rc.internal;
-
-import org.gradle.nativebinaries.language.internal.AbstractNativeCompileSpec;
-
-public class DefaultWindowsResourceCompileSpec extends AbstractNativeCompileSpec implements WindowsResourceCompileSpec {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/WindowsResourceCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/WindowsResourceCompileSpec.java
deleted file mode 100644
index 6c288a1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/internal/WindowsResourceCompileSpec.java
+++ /dev/null
@@ -1,21 +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.nativebinaries.language.rc.internal;
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-public interface WindowsResourceCompileSpec extends NativeCompileSpec {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesNativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesNativeBinariesPlugin.groovy
deleted file mode 100644
index b0dd256..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesNativeBinariesPlugin.groovy
+++ /dev/null
@@ -1,103 +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.nativebinaries.language.rc.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.language.rc.WindowsResourceSet
-import org.gradle.language.rc.plugins.WindowsResourceScriptPlugin
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.internal.StaticLibraryBinaryInternal
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool
-import org.gradle.nativebinaries.language.rc.tasks.WindowsResourceCompile
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Windows Resource sources.
- *
- * <p>Automatically includes the {@link WindowsResourceScriptPlugin} for core Windows Resource source support
- * and the {@link NativeBinariesPlugin} for native binary support.</p>
- *
- * <li>Creates a {@link WindowsResourceCompile} task for each {@link WindowsResourceSet} to compile the sources.</li>
- */
- at Incubating
-class WindowsResourcesNativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(NativeBinariesPlugin)
-        project.plugins.apply(WindowsResourceScriptPlugin)
-
-        // TODO:DAZ Clean this up (see CppNativeBinariesPlugin)
-        project.executables.all { Executable executable ->
-            addLanguageExtensionsToComponent(executable)
-        }
-        project.libraries.all { Library library ->
-            addLanguageExtensionsToComponent(library)
-        }
-
-        project.binaries.withType(ProjectNativeBinary) { ProjectNativeBinaryInternal binary ->
-            if (shouldProcessResources(binary)) {
-                binary.source.withType(WindowsResourceSet).all { WindowsResourceSet resources ->
-                    if (resources.mayHaveSources) {
-                        def resourceCompileTask = createResourceCompileTask(project, binary, resources)
-                        resourceCompileTask.dependsOn resources
-                        binary.tasks.add resourceCompileTask
-                        final resourceOutputs = resourceCompileTask.outputs.files.asFileTree.matching { include '**/*.res' }
-                        binary.tasks.builder.source resourceOutputs
-                        if (binary instanceof StaticLibraryBinaryInternal) {
-                            binary.additionalLinkFiles resourceOutputs
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private def addLanguageExtensionsToComponent(ProjectNativeComponent component) {
-        component.binaries.all { NativeBinary binary ->
-            if (shouldProcessResources(binary)) {
-                binary.extensions.create("rcCompiler", DefaultPreprocessingTool)
-            }
-        }
-    }
-
-    private boolean shouldProcessResources(NativeBinary binary) {
-        binary.targetPlatform.operatingSystem.windows
-    }
-
-    private def createResourceCompileTask(ProjectInternal project, ProjectNativeBinaryInternal binary, WindowsResourceSet sourceSet) {
-        WindowsResourceCompile compileTask = project.task(binary.namingScheme.getTaskName("compile", sourceSet.fullName), type: WindowsResourceCompile) {
-            description = "Compiles resources of the $sourceSet of $binary"
-        }
-
-        compileTask.toolChain = binary.toolChain
-        compileTask.targetPlatform = binary.targetPlatform
-
-        compileTask.includes {
-            sourceSet.exportedHeaders.srcDirs
-        }
-        compileTask.source sourceSet.source
-
-        compileTask.outputDir = project.file("${project.buildDir}/objectFiles/${binary.namingScheme.outputDirectoryBase}/${sourceSet.fullName}")
-        compileTask.macros = binary.rcCompiler.macros
-        compileTask.compilerArgs = binary.rcCompiler.args
-
-        compileTask
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPlugin.groovy
deleted file mode 100644
index 2e0ac11..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/plugins/WindowsResourcesPlugin.groovy
+++ /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.nativebinaries.language.rc.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.toolchain.internal.plugins.StandardToolChainsPlugin
-
-/**
- * A plugin for projects wishing to build native binary components from Windows Resource sources.
- *
- * <p>Adds core tool chain support to the {@link WindowsResourcesNativeBinariesPlugin}.</p>
- */
- at Incubating
-class WindowsResourcesPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.plugins.apply(StandardToolChainsPlugin)
-
-        project.plugins.apply(WindowsResourcesNativeBinariesPlugin)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/tasks/WindowsResourceCompile.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/tasks/WindowsResourceCompile.groovy
deleted file mode 100644
index 4d0bd62..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/language/rc/tasks/WindowsResourceCompile.groovy
+++ /dev/null
@@ -1,131 +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.nativebinaries.language.rc.tasks
-import org.gradle.api.DefaultTask
-import org.gradle.api.Incubating
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.changedetection.state.FileSnapshotter
-import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-import org.gradle.nativebinaries.language.c.internal.incremental.IncrementalCompilerBuilder
-import org.gradle.nativebinaries.language.rc.internal.DefaultWindowsResourceCompileSpec
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-
-import javax.inject.Inject
-/**
- * Compiles Windows Resource scripts into .res files.
- */
- at Incubating
-class WindowsResourceCompile extends DefaultTask {
-    private final IncrementalCompilerBuilder incrementalCompilerBuilder
-
-    @Inject
-    WindowsResourceCompile() {
-        incrementalCompilerBuilder = new IncrementalCompilerBuilder(services.get(TaskArtifactStateCacheAccess),
-                                                                    services.get(FileSnapshotter), this)
-        includes = project.files()
-        source = project.files()
-    }
-
-     /**
-      * The tool chain used for compilation.
-      */
-     ToolChain toolChain
-
-     /**
-      * The platform being targeted.
-      */
-     Platform targetPlatform
-
-     /**
-      * The directory where object files will be generated.
-      */
-     @OutputDirectory
-     File outputDir
-
-     /**
-      * Returns the header directories to be used for compilation.
-      */
-     @InputFiles
-     FileCollection includes
-
-     /**
-      * Returns the source files to be compiled.
-      */
-     @InputFiles
-     FileCollection source
-
-     // Invalidate output when the tool chain output changes
-     @Input
-     def getOutputType() {
-         return "${toolChain.outputType}:${targetPlatform.compatibilityString}"
-     }
-
-     /**
-      * Macros that should be defined for the compiler.
-      */
-     @Input
-     Map<String, String> macros = [:]
-
-     /**
-      * Additional arguments to provide to the compiler.
-      */
-     @Input
-     List<String> compilerArgs = []
-
-     @TaskAction
-     void compile(IncrementalTaskInputs inputs) {
-         def spec = new DefaultWindowsResourceCompileSpec()
-         spec.tempDir = getTemporaryDir()
-         spec.objectFileDir = getOutputDir()
-         spec.include getIncludes()
-         spec.source getSource()
-         spec.macros = getMacros()
-         spec.args getCompilerArgs()
-
-         PlatformToolChain platformToolChain = toolChain.target(targetPlatform)
-         final compiler = platformToolChain.createWindowsResourceCompiler()
-         if (!inputs.incremental) {
-             incrementalCompilerBuilder.withCleanCompile()
-         }
-         incrementalCompilerBuilder.withIncludes(includes)
-         final incrementalCompiler = incrementalCompilerBuilder.createIncrementalCompiler(compiler)
-         def result = incrementalCompiler.execute(spec)
-         didWork = result.didWork
-     }
-
-    /**
-     * Add directories where the compiler should search for header files.
-     */
-    void includes(Object 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/nativebinaries/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/package-info.java
deleted file mode 100644
index 5911ef4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Classes that model aspects of native component projects.
- */
-package org.gradle.nativebinaries;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Architecture.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Architecture.java
deleted file mode 100644
index 9f6de2b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Architecture.java
+++ /dev/null
@@ -1,32 +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.nativebinaries.platform;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A cpu architecture.
- */
- at Incubating
- at HasInternalProtocol
-public interface Architecture extends Named {
-    /**
-     * Returns a human-consumable display name for this architecture.
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/OperatingSystem.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/OperatingSystem.java
deleted file mode 100644
index b87b33d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/OperatingSystem.java
+++ /dev/null
@@ -1,60 +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.nativebinaries.platform;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-
-/**
- * A machine operating system.
- */
- at Incubating
-public interface OperatingSystem extends Named {
-    /**
-     * Returns a human-consumable display name for this operating system.
-     */
-    String getDisplayName();
-
-    /**
-     * Is this the current OS?
-     */
-    boolean isCurrent();
-
-    /**
-     * Is it Windows?
-     */
-    boolean isWindows();
-
-    /**
-     * Is it Mac OS X?
-     */
-    boolean isMacOsX();
-
-    /**
-     * Is it Linux?
-     */
-    boolean isLinux();
-
-    /**
-     * Is it Solaris?
-     */
-    boolean isSolaris();
-
-    /**
-     * Is it FreeBSD?
-     */
-    boolean isFreeBSD();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Platform.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Platform.java
deleted file mode 100644
index 0d944c8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/Platform.java
+++ /dev/null
@@ -1,125 +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.nativebinaries.platform;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A target platform for building native binaries. Each target platform is given a name, and may optionally be given
- * a specific {@link Architecture} and/or {@link OperatingSystem} to target.
- *
- * <pre>
- *     model {
- *         platforms {
- *             windows_x86 {
- *                 architecture "i386"
- *                 operatingSystem "windows"
- *             }
- *         }
- *     }
- * </pre>
- */
- at Incubating
- at HasInternalProtocol
-public interface Platform extends Named {
-    /**
-     * Returns a human-consumable display name for this platform.
-     */
-    String getDisplayName();
-
-    /**
-     * The cpu architecture being targeted. Defaults to the default architecture produced by the tool chain.
-     */
-    Architecture getArchitecture();
-
-    /**
-     * Sets the cpu architecture being targeted.
-     * The architecture is provided as a string name, which is translated into one of the supported architecture types.
-     *
-     * <table>
-     *     <tr>
-     *         <th>Instruction Set</th>
-     *         <th>32-bit names</th>
-     *         <th>64-bit names</th>
-     *     </tr>
-     *     <tr>
-     *         <td>Intel x86</td>
-     *         <td>"x86", "i386", "ia-32"</td>
-     *         <td>"x86_64", "amd64", "x64", "x86-64"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>Intel Itanium</td>
-     *         <td></td>
-     *         <td>"ia-64"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>Power PC</td>
-     *         <td>"ppc"</td>
-     *         <td>"ppc64"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>Sparc</td>
-     *         <td>"sparc", "sparc32", "sparc-v7", "sparc-v8"</td>
-     *         <td>"sparc64", "ultrasparc", "sparc-v9"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>ARM</td>
-     *         <td>"arm"</td>
-     *         <td></td>
-     *     </tr>
-     * </table>
-     */
-    void architecture(Object notation);
-
-    /**
-     * The operating system being targeted.
-     * Defaults to the default operating system targeted by the tool chain (normally the current operating system).
-     */
-    OperatingSystem getOperatingSystem();
-
-    /**
-     * Sets the operating system being targeted.
-     * The operating system is provided as a string name, which is translated into one of the supported operating system types.
-     *
-     * <table>
-     *     <tr>
-     *         <th>Operating System</th>
-     *         <th>Aliases</th>
-     *     </tr>
-     *     <tr>
-     *         <td>Windows</td>
-     *         <td>"windows"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>GNU/Linux</td>
-     *         <td>"linux"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>Mac OS X</td>
-     *         <td>"osx", "mac os x", "darwin"</td>
-     *     </tr>
-     *     <tr>
-     *         <td>Solaris</td>
-     *         <td>"solaris", "sunos"</td>
-     *     </tr>
-     * </table>
-     */
-    void operatingSystem(Object notation);
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/PlatformContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/PlatformContainer.java
deleted file mode 100644
index 86718b3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/PlatformContainer.java
+++ /dev/null
@@ -1,27 +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.nativebinaries.platform;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectContainer;
-
-/**
- * A container of {@link Platform}s.
- */
- at Incubating
-public interface PlatformContainer extends NamedDomainObjectContainer<Platform> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureInternal.java
deleted file mode 100644
index d3fc1ca..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureInternal.java
+++ /dev/null
@@ -1,38 +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.nativebinaries.platform.internal;
-
-import org.gradle.nativebinaries.platform.Architecture;
-
-public interface ArchitectureInternal extends Architecture {
-    static final ArchitectureInternal TOOL_CHAIN_DEFAULT = new DefaultArchitecture("default", null, 0);
-
-    enum InstructionSet { X86, ITANIUM, PPC, SPARC, ARM }
-
-    InstructionSet getInstructionSet();
-
-    int getRegisterSize();
-
-    boolean isI386();
-
-    boolean isAmd64();
-
-    boolean isIa64();
-
-    boolean isArm();
-
-    boolean isArmv8();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParser.java
deleted file mode 100644
index d71d730..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParser.java
+++ /dev/null
@@ -1,91 +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.nativebinaries.platform.internal;
-
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
-import org.gradle.nativebinaries.platform.Architecture;
-import org.gradle.util.CollectionUtils;
-import org.gradle.util.GUtil;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-public class ArchitectureNotationParser {
-
-    private static final List<String> X86_ALIASES = Arrays.asList("x86", "i386", "ia-32");
-    private static final List<String> X86_64_ALIASES = Arrays.asList("x86_64", "amd64", "x64", "x86-64");
-    private static final List<String> ITANIUM_ALIASES = Arrays.asList("ia-64");
-    private static final List<String> PPC_32_ALIASES = Arrays.asList("ppc");
-    private static final List<String> PPC_64_ALIASES = Arrays.asList("ppc64");
-    private static final List<String> SPARC_32_ALIASES = Arrays.asList("sparc", "sparc32", "sparc-v7", "sparc-v8");
-    private static final List<String> SPARC_64_ALIASES = Arrays.asList("sparc64", "ultrasparc", "sparc-v9");
-    private static final List<String> ARM_ALIASES = Arrays.asList("arm");
-
-    public static NotationParser<Object, Architecture> parser() {
-        return new NotationParserBuilder<Architecture>()
-                .resultingType(Architecture.class)
-                .parser(new Parser())
-                .toComposite();
-    }
-
-    private static final class Parser extends TypedNotationParser<String, Architecture> {
-        private Parser() {
-            super(String.class);
-        }
-
-        @Override
-        protected Architecture parseType(String notation) {
-            if (X86_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.X86, 32);
-            }
-            if (X86_64_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.X86, 64);
-            }
-            if (ITANIUM_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.ITANIUM, 64);
-            }
-            if (PPC_32_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.PPC, 32);
-            }
-            if (PPC_64_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.PPC, 64);
-            }
-            if (SPARC_32_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.SPARC, 32);
-            }
-            if (SPARC_64_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.SPARC, 64);
-            }
-            if (ARM_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultArchitecture(notation, ArchitectureInternal.InstructionSet.ARM, 32);
-            }
-            throw new UnsupportedNotationException(notation);
-        }
-
-        @Override
-        public void describe(Collection<String> candidateFormats) {
-            List<String> validList = CollectionUtils.flattenCollections(String.class,
-                    X86_ALIASES, X86_64_ALIASES, ITANIUM_ALIASES, PPC_32_ALIASES, PPC_64_ALIASES, SPARC_32_ALIASES, SPARC_64_ALIASES, ARM_ALIASES
-            );
-            candidateFormats.add("One of the following values: " + GUtil.toString(validList));
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitecture.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitecture.java
deleted file mode 100644
index 27c28f0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitecture.java
+++ /dev/null
@@ -1,100 +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.nativebinaries.platform.internal;
-
-public class DefaultArchitecture implements ArchitectureInternal {
-    private final String name;
-    private final InstructionSet instructionSet;
-    private final int registerSize;
-
-    public DefaultArchitecture(String name, InstructionSet instructionSet, int registerSize) {
-        this.name = name;
-        this.instructionSet = instructionSet;
-        this.registerSize = registerSize;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getDisplayName() {
-        return String.format("architecture '%s'", name);
-    }
-
-    public InstructionSet getInstructionSet() {
-        return instructionSet;
-    }
-
-    public int getRegisterSize() {
-        return registerSize;
-    }
-
-    public boolean isI386() {
-        return instructionSet == InstructionSet.X86 && registerSize == 32;
-    }
-
-    public boolean isAmd64() {
-        return instructionSet == InstructionSet.X86 && registerSize == 64;
-    }
-
-    public boolean isIa64() {
-        return instructionSet == InstructionSet.ITANIUM && registerSize == 64;
-    }
-
-    public boolean isArm() {
-        return instructionSet == InstructionSet.ARM && registerSize == 32;
-    }
-
-    public boolean isArmv8() {
-        return instructionSet == InstructionSet.ARM && registerSize == 64;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                + ((instructionSet == null) ? 0 : instructionSet.hashCode());
-        result = prime * result + registerSize;
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        DefaultArchitecture other = (DefaultArchitecture) obj;
-        if (instructionSet != other.instructionSet) {
-            return false;
-        }
-        if (registerSize != other.registerSize) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystem.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystem.java
deleted file mode 100644
index 26745de..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystem.java
+++ /dev/null
@@ -1,68 +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.nativebinaries.platform.internal;
-
-import org.gradle.nativebinaries.platform.OperatingSystem;
-
-public class DefaultOperatingSystem implements OperatingSystem {
-    private static final org.gradle.internal.os.OperatingSystem CURRENT_OS = org.gradle.internal.os.OperatingSystem.current();
-    public static final OperatingSystem TOOL_CHAIN_DEFAULT = new DefaultOperatingSystem("default", CURRENT_OS);
-
-    private final String name;
-    private final org.gradle.internal.os.OperatingSystem internalOs;
-
-    public DefaultOperatingSystem(String name, org.gradle.internal.os.OperatingSystem internalOs) {
-        this.name = name;
-        this.internalOs = internalOs;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDisplayName() {
-        return String.format("operating system '%s'", name);
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public boolean isCurrent() {
-        return internalOs == CURRENT_OS;
-    }
-
-    public boolean isWindows() {
-        return internalOs.isWindows();
-    }
-
-    public boolean isLinux() {
-        return internalOs.isLinux();
-    }
-
-    public boolean isMacOsX() {
-        return internalOs.isMacOsX();
-    }
-
-    public boolean isSolaris() {
-        return internalOs == org.gradle.internal.os.OperatingSystem.SOLARIS;
-    }
-
-    public boolean isFreeBSD() {
-        return internalOs == org.gradle.internal.os.OperatingSystem.FREE_BSD;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatform.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatform.java
deleted file mode 100644
index f8142ac..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatform.java
+++ /dev/null
@@ -1,74 +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.nativebinaries.platform.internal;
-
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.nativebinaries.platform.Architecture;
-import org.gradle.nativebinaries.platform.OperatingSystem;
-
-public class DefaultPlatform implements PlatformInternal {
-    private final NotationParser<Object, Architecture> archParser;
-    private final NotationParser<Object, OperatingSystem> osParser;
-    private final String name;
-    private Architecture architecture;
-    private OperatingSystem operatingSystem;
-
-    public DefaultPlatform(String name, NotationParser<Object, Architecture> archParser, NotationParser<Object, OperatingSystem> osParser) {
-        this.name = name;
-        this.architecture = ArchitectureInternal.TOOL_CHAIN_DEFAULT;
-        this.operatingSystem = DefaultOperatingSystem.TOOL_CHAIN_DEFAULT;
-        this.archParser = archParser;
-        this.osParser = osParser;
-    }
-
-    public DefaultPlatform(String name) {
-        this(name, ArchitectureNotationParser.parser(), OperatingSystemNotationParser.parser());
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getDisplayName() {
-        return String.format("platform '%s'", name);
-    }
-
-    public Architecture getArchitecture() {
-        return architecture;
-    }
-
-    public void architecture(Object notation) {
-        architecture = archParser.parseNotation(notation);
-    }
-
-    public OperatingSystem getOperatingSystem() {
-        return operatingSystem;
-    }
-
-    public void operatingSystem(Object notation) {
-        operatingSystem = osParser.parseNotation(notation);
-    }
-
-    public String getCompatibilityString() {
-        return String.format("%s:%s", architecture.getName(), operatingSystem.getName());
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformContainer.java
deleted file mode 100644
index 5b9707e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformContainer.java
+++ /dev/null
@@ -1,39 +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.nativebinaries.platform.internal;
-
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.nativebinaries.platform.Architecture;
-import org.gradle.nativebinaries.platform.OperatingSystem;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.platform.PlatformContainer;
-
-public class DefaultPlatformContainer extends AbstractNamedDomainObjectContainer<Platform> implements PlatformContainer {
-    private final NotationParser<Object, Architecture> archParser = ArchitectureNotationParser.parser();
-    private final NotationParser<Object, OperatingSystem> osParser = OperatingSystemNotationParser.parser();
-
-    public DefaultPlatformContainer(Instantiator instantiator) {
-        super(Platform.class, instantiator);
-    }
-
-    @Override
-    protected Platform doCreate(String name) {
-        return getInstantiator().newInstance(DefaultPlatform.class, name, archParser, osParser);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParser.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParser.java
deleted file mode 100644
index 6ebf6ad..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParser.java
+++ /dev/null
@@ -1,80 +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.nativebinaries.platform.internal;
-
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.TypedNotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
-import org.gradle.nativebinaries.platform.OperatingSystem;
-import org.gradle.util.GUtil;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-public class OperatingSystemNotationParser {
-
-    private static final List<String> WINDOWS_ALIASES = Arrays.asList("windows");
-    private static final List<String> OSX_ALIASES = Arrays.asList("osx", "mac os x");
-    private static final List<String> LINUX_ALIASES = Arrays.asList("linux");
-    private static final List<String> SOLARIS_ALIASES = Arrays.asList("solaris", "sunos");
-    private static final List<String> FREEBSD_ALIASES = Arrays.asList("freebsd");
-
-    public static NotationParser<Object, OperatingSystem> parser() {
-        return new NotationParserBuilder<OperatingSystem>()
-                .resultingType(OperatingSystem.class)
-                .parser(new Parser())
-                .toComposite();
-    }
-
-    private static final class Parser extends TypedNotationParser<String, OperatingSystem> {
-        private Parser() {
-            super(String.class);
-        }
-
-        @Override
-        protected OperatingSystem parseType(String notation) {
-            if (WINDOWS_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultOperatingSystem(notation, org.gradle.internal.os.OperatingSystem.WINDOWS);
-            }
-            if (OSX_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultOperatingSystem(notation, org.gradle.internal.os.OperatingSystem.MAC_OS);
-            }
-            if (LINUX_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultOperatingSystem(notation, org.gradle.internal.os.OperatingSystem.LINUX);
-            }
-            if (SOLARIS_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultOperatingSystem(notation, org.gradle.internal.os.OperatingSystem.SOLARIS);
-            }
-            if (FREEBSD_ALIASES.contains(notation.toLowerCase())) {
-                return new DefaultOperatingSystem(notation, org.gradle.internal.os.OperatingSystem.FREE_BSD);
-            }
-            throw new UnsupportedNotationException(notation);
-        }
-
-        @Override
-        public void describe(Collection<String> candidateFormats) {
-            List<String> allValues = new ArrayList<String>();
-            allValues.addAll(WINDOWS_ALIASES);
-            allValues.addAll(OSX_ALIASES);
-            allValues.addAll(LINUX_ALIASES);
-            allValues.addAll(SOLARIS_ALIASES);
-            candidateFormats.add("One of the following values: " + GUtil.toString(allValues));
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/PlatformInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/PlatformInternal.java
deleted file mode 100644
index 74abf88..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/internal/PlatformInternal.java
+++ /dev/null
@@ -1,22 +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.nativebinaries.platform.internal;
-
-import org.gradle.nativebinaries.platform.Platform;
-
-public interface PlatformInternal extends Platform {
-    String getCompatibilityString();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/package-info.java
deleted file mode 100644
index c5d0e06..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/platform/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes that allow defining a native binary platform.
- */
-package org.gradle.nativebinaries.platform;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPlugin.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPlugin.java
deleted file mode 100644
index ac52682..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPlugin.java
+++ /dev/null
@@ -1,142 +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.nativebinaries.plugins;
-
-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.api.plugins.BasePlugin;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.configuration.project.ProjectConfigurationActionContainer;
-import org.gradle.internal.Actions;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.plugins.LanguageBasePlugin;
-import org.gradle.model.ModelFinalizer;
-import org.gradle.model.ModelRule;
-import org.gradle.model.ModelRules;
-import org.gradle.nativebinaries.BuildTypeContainer;
-import org.gradle.nativebinaries.FlavorContainer;
-import org.gradle.nativebinaries.internal.DefaultBuildTypeContainer;
-import org.gradle.nativebinaries.internal.DefaultExecutableContainer;
-import org.gradle.nativebinaries.internal.DefaultFlavorContainer;
-import org.gradle.nativebinaries.internal.DefaultLibraryContainer;
-import org.gradle.nativebinaries.internal.configure.*;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.PlatformContainer;
-import org.gradle.nativebinaries.platform.internal.DefaultPlatformContainer;
-import org.gradle.nativebinaries.toolchain.internal.DefaultToolChainRegistry;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal;
-
-import javax.inject.Inject;
-import java.util.Arrays;
-
-/**
- * A plugin that sets up the infrastructure for defining native binaries.
- */
- at Incubating
-public class NativeBinariesModelPlugin implements Plugin<ProjectInternal> {
-
-    private final Instantiator instantiator;
-    private final ProjectConfigurationActionContainer configurationActions;
-    private final ModelRules modelRules;
-    private final NativeDependencyResolver resolver;
-    private final FileResolver fileResolver;
-
-    @Inject
-    public NativeBinariesModelPlugin(Instantiator instantiator, ProjectConfigurationActionContainer configurationActions, ModelRules modelRules,
-                                     NativeDependencyResolver resolver, FileResolver fileResolver) {
-        this.instantiator = instantiator;
-        this.configurationActions = configurationActions;
-        this.modelRules = modelRules;
-        this.resolver = resolver;
-        this.fileResolver = fileResolver;
-    }
-
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(BasePlugin.class);
-        project.getPlugins().apply(LanguageBasePlugin.class);
-
-        modelRules.register("toolChains", ToolChainRegistryInternal.class, factory(DefaultToolChainRegistry.class));
-        modelRules.register("platforms", PlatformContainer.class, factory(DefaultPlatformContainer.class));
-        modelRules.register("buildTypes", BuildTypeContainer.class, factory(DefaultBuildTypeContainer.class));
-        modelRules.register("flavors", FlavorContainer.class, factory(DefaultFlavorContainer.class));
-
-        project.getModelRegistry().create("repositories", Arrays.asList("flavors", "platforms", "buildTypes"), new RepositoriesFactory(instantiator, fileResolver));
-
-        modelRules.rule(new CreateDefaultPlatform());
-        modelRules.rule(new CreateDefaultBuildTypes());
-        modelRules.rule(new CreateDefaultFlavors());
-        modelRules.rule(new AddDefaultToolChainsIfRequired());
-        modelRules.rule(new CreateNativeBinaries(instantiator, project, resolver));
-        modelRules.rule(new CloseBinariesForTasks());
-
-        project.getExtensions().create(
-                "executables",
-                DefaultExecutableContainer.class,
-                instantiator,
-                project
-        );
-        project.getExtensions().create(
-                "libraries",
-                DefaultLibraryContainer.class,
-                instantiator,
-                project
-        );
-
-        // TODO:DAZ Lazy configuration actions: need a better way to accomplish these.
-        configurationActions.add(Actions.composite(
-                new ConfigureGeneratedSourceSets(),
-                new ApplySourceSetConventions()
-        ));
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    private static class CloseBinariesForTasks extends ModelRule {
-        void closeBinariesForTasks(TaskContainer tasks, BinaryContainer binaries) {
-            // nothing needed here
-        }
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    private static class AddDefaultToolChainsIfRequired extends ModelFinalizer {
-        void createDefaultToolChain(ToolChainRegistryInternal toolChains) {
-            if (toolChains.isEmpty()) {
-                toolChains.addDefaultToolChains();
-            }
-        }
-    }
-
-    private <T> Factory<T> factory(Class<T> type) {
-        return new InstantiatingFactory<T>(type, instantiator);
-    }
-
-    private static class InstantiatingFactory<T> implements Factory<T> {
-        private final Class<T> type;
-        private final Instantiator instantiator;
-
-        public InstantiatingFactory(Class<T> type, Instantiator instantiator) {
-            this.type = type;
-            this.instantiator = instantiator;
-        }
-
-        public T create() {
-            return instantiator.newInstance(type, instantiator);
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPlugin.groovy
deleted file mode 100644
index cdbcfce..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPlugin.groovy
+++ /dev/null
@@ -1,138 +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.nativebinaries.plugins
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.language.base.BinaryContainer
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.tasks.CreateStaticLibrary
-import org.gradle.nativebinaries.tasks.InstallExecutable
-import org.gradle.nativebinaries.tasks.LinkExecutable
-import org.gradle.nativebinaries.tasks.LinkSharedLibrary
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-/**
- * A plugin that creates tasks used for constructing native binaries.
- */
- at Incubating
-public class NativeBinariesPlugin implements Plugin<ProjectInternal> {
-
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(NativeBinariesModelPlugin.class);
-
-        // Create a functionalSourceSet for each native component, with the same name
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        project.getExtensions().getByType(ExecutableContainer).all { Executable exe ->
-            exe.source projectSourceSet.maybeCreate(exe.name)
-        }
-        project.getExtensions().getByType(LibraryContainer).all { Library lib ->
-            lib.source projectSourceSet.maybeCreate(lib.name)
-        }
-
-        final BinaryContainer binaries = project.getExtensions().getByType(BinaryContainer.class);
-        binaries.withType(ProjectNativeBinary) { ProjectNativeBinaryInternal binary ->
-            binary.conventionMapping.buildable = { isBuildableBinary(binary) }
-            createTasks(project, binary)
-        }
-    }
-
-    static boolean isBuildableBinary(ProjectNativeBinaryInternal binary) {
-        final chain = binary.toolChain as ToolChainInternal
-        chain.target(binary.getTargetPlatform()).available
-    }
-
-    def createTasks(ProjectInternal project, ProjectNativeBinaryInternal binary) {
-        def builderTask
-        if (binary instanceof ExecutableBinary) {
-            builderTask = createLinkExecutableTask(project, binary as ExecutableBinary)
-            binary.tasks.add createInstallTask(project, binary as ExecutableBinary);
-        } else if (binary instanceof SharedLibraryBinary) {
-            builderTask = createLinkSharedLibraryTask(project, binary)
-        } else if (binary instanceof StaticLibraryBinary) {
-            builderTask = createStaticLibraryTask(project, binary)
-        } else {
-            throw new RuntimeException("Not a valid binary type for building: " + binary)
-        }
-        binary.tasks.add builderTask
-        binary.builtBy builderTask
-    }
-
-    private LinkExecutable createLinkExecutableTask(ProjectInternal project, ExecutableBinary executable) {
-        def binary = executable as ProjectNativeBinaryInternal
-        LinkExecutable linkTask = project.task(binary.namingScheme.getTaskName("link"), type: LinkExecutable) {
-             description = "Links ${executable}"
-         }
-
-        linkTask.toolChain = binary.toolChain
-        linkTask.targetPlatform = executable.targetPlatform
-
-        linkTask.lib { binary.libs*.linkFiles }
-
-        linkTask.conventionMapping.outputFile = { executable.executableFile }
-        linkTask.linkerArgs = binary.linker.args
-        return linkTask
-    }
-
-    private LinkSharedLibrary createLinkSharedLibraryTask(ProjectInternal project, SharedLibraryBinary sharedLibrary) {
-        def binary = sharedLibrary as ProjectNativeBinaryInternal
-        LinkSharedLibrary linkTask = project.task(binary.namingScheme.getTaskName("link"), type: LinkSharedLibrary) {
-             description = "Links ${sharedLibrary}"
-         }
-
-        linkTask.toolChain = binary.toolChain
-        linkTask.targetPlatform = binary.targetPlatform
-
-        linkTask.lib { binary.libs*.linkFiles }
-
-        linkTask.conventionMapping.outputFile = { sharedLibrary.sharedLibraryFile }
-        linkTask.conventionMapping.installName = { sharedLibrary.sharedLibraryFile.name }
-        linkTask.linkerArgs = binary.linker.args
-        return linkTask
-    }
-
-    private CreateStaticLibrary createStaticLibraryTask(ProjectInternal project, StaticLibraryBinary staticLibrary) {
-        def binary = staticLibrary as ProjectNativeBinaryInternal
-        CreateStaticLibrary task = project.task(binary.namingScheme.getTaskName("create"), type: CreateStaticLibrary) {
-             description = "Creates ${staticLibrary}"
-         }
-
-        task.toolChain = binary.toolChain
-        task.targetPlatform = staticLibrary.targetPlatform
-        task.conventionMapping.outputFile = { staticLibrary.staticLibraryFile }
-        task.staticLibArgs = binary.staticLibArchiver.args
-        return task
-    }
-
-    def createInstallTask(ProjectInternal project, ExecutableBinary executable) {
-        def binary = executable as ProjectNativeBinaryInternal
-        InstallExecutable installTask = project.task(binary.namingScheme.getTaskName("install"), type: InstallExecutable) {
-            description = "Installs a development image of $executable"
-            group = BasePlugin.BUILD_GROUP
-        }
-
-        installTask.toolChain = binary.toolChain
-        installTask.conventionMapping.destinationDir = { project.file("${project.buildDir}/install/${binary.namingScheme.outputDirectoryBase}") }
-
-        installTask.conventionMapping.executable = { executable.executableFile }
-        installTask.lib { binary.libs*.runtimeFiles }
-
-        installTask.dependsOn(executable)
-        return installTask
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/package-info.java
deleted file mode 100644
index 161301d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Plugins for building native component projects.
- */
-package org.gradle.nativebinaries.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/AbstractLinkTask.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/AbstractLinkTask.groovy
deleted file mode 100644
index d9a4064..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/AbstractLinkTask.groovy
+++ /dev/null
@@ -1,120 +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.nativebinaries.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.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.internal.LinkerSpec
-
-import javax.inject.Inject
-
- at Incubating
-abstract class AbstractLinkTask extends DefaultTask implements BuildBinaryTask {
-    @Inject
-    AbstractLinkTask() {
-        libs = project.files()
-        source = project.files()
-    }
-
-    /**
-     * The tool chain used for linking.
-     */
-    ToolChain toolChain
-    Platform targetPlatform
-
-    // Invalidate output when the tool chain output changes
-    @Input
-    def getOutputType() {
-        return "${toolChain.outputType}:${targetPlatform.compatibilityString}"
-    }
-
-    // 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
-    FileCollection 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()
-
-        if (source.empty) {
-            didWork = false
-            return
-        }
-
-        def spec = createLinkerSpec()
-        spec.tempDir = getTemporaryDir()
-        spec.outputFile = getOutputFile()
-
-        spec.objectFiles getSource()
-        spec.libraries getLibs()
-        spec.args getLinkerArgs()
-
-        def result = toolChain.target(targetPlatform).createLinker().execute(spec)
-        didWork = result.didWork
-    }
-
-    protected abstract LinkerSpec createLinkerSpec();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/BuildBinaryTask.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/BuildBinaryTask.java
deleted file mode 100644
index c64a633..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/BuildBinaryTask.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.nativebinaries.tasks;
-
-import org.gradle.api.Incubating;
-
-/**
- * A task that combines a set of object files into a single binary.
- */
- at Incubating
-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/nativebinaries/tasks/CreateStaticLibrary.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/CreateStaticLibrary.groovy
deleted file mode 100644
index 81b3784..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/CreateStaticLibrary.groovy
+++ /dev/null
@@ -1,96 +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.nativebinaries.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.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.internal.DefaultStaticLibraryArchiverSpec
-
-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
-
-    /**
-     * The platform being targeted.
-     */
-    Platform targetPlatform
-
-    // Invalidate output when the tool chain output changes
-    @Input
-    def getOutputType() {
-        return "${toolChain.outputType}:${targetPlatform.compatibilityString}"
-    }
-
-    /**
-     * 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 DefaultStaticLibraryArchiverSpec()
-        spec.tempDir = getTemporaryDir()
-        spec.outputFile = getOutputFile()
-        spec.objectFiles getSource()
-        spec.args getStaticLibArgs()
-
-        def result = toolChain.target(targetPlatform).createStaticLibraryArchiver().execute(spec)
-        didWork = result.didWork
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/InstallExecutable.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/InstallExecutable.groovy
deleted file mode 100644
index 7efdb91..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/InstallExecutable.groovy
+++ /dev/null
@@ -1,156 +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.nativebinaries.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.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.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
-    }
-
-    /**
-     * Returns the script file that can be used to run the install image.
-     */
-    File getRunScript() {
-        new File(getDestinationDir(), os.getScriptName(getExecutable().name))
-    }
-
-    // TODO:DAZ Allow this to be configured
-    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 pathEntry : ((Gcc) toolChain).path) {
-                toolChainPath.append(pathEntry.absolutePath).append(";")
-            }
-            toolChainPath.append("%PATH%")
-        }
-
-        runScript.text = """
- at echo off
-SETLOCAL
-$toolChainPath
-CALL "%~dp0lib\\${executable.name}"
-EXIT /B %ERRORLEVEL%
-
-ENDLOCAL
-"""
-    }
-
-    private void installUnix() {
-        final destination = getDestinationDir()
-        final executable = getExecutable()
-
-        installToDir(new File(destination, "lib"))
-
-        runScript.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(runScript, 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/nativebinaries/tasks/LinkExecutable.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/LinkExecutable.groovy
deleted file mode 100644
index bbe156e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/LinkExecutable.groovy
+++ /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.nativebinaries.tasks
-import org.gradle.api.Incubating
-import org.gradle.nativebinaries.internal.DefaultLinkerSpec
-import org.gradle.nativebinaries.internal.LinkerSpec
-/**
- * Links a binary executable from object files and libraries.
- */
- at Incubating
-class LinkExecutable extends AbstractLinkTask {
-
-    @Override
-    protected LinkerSpec createLinkerSpec() {
-        return new DefaultLinkerSpec()
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/LinkSharedLibrary.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/LinkSharedLibrary.groovy
deleted file mode 100644
index 219427d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/LinkSharedLibrary.groovy
+++ /dev/null
@@ -1,43 +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.nativebinaries.tasks
-import org.gradle.api.Incubating
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
-import org.gradle.nativebinaries.internal.DefaultLinkerSpec
-import org.gradle.nativebinaries.internal.LinkerSpec
-import org.gradle.nativebinaries.internal.SharedLibraryLinkerSpec
-
-/**
- * Links a binary shared library from object files and imported libraries.
- */
- at Incubating
-class LinkSharedLibrary extends AbstractLinkTask {
-    @Input @Optional
-    String installName;
-
-    @Override
-    protected LinkerSpec createLinkerSpec() {
-        final spec = new Spec()
-        spec.installName = getInstallName()
-        return spec
-    }
-
-    private static class Spec extends DefaultLinkerSpec implements SharedLibraryLinkerSpec {
-        String installName;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/package-info.java
deleted file mode 100644
index ec6daa2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for building native component projects.
- */
-package org.gradle.nativebinaries.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/ProjectComponentTestSuite.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/ProjectComponentTestSuite.java
deleted file mode 100644
index b280fc5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/ProjectComponentTestSuite.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-/**
- * A suite of tests for a project component.
- */
- at Incubating
-public interface ProjectComponentTestSuite extends TestSuite {
-
-    /**
-     * The tested component.
-     */
-    ProjectNativeComponent getTestedComponent();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuite.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuite.java
deleted file mode 100644
index 687a584..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuite.java
+++ /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.nativebinaries.test;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-
-/**
- * A component representing a suite of tests that will be executed together.
- */
- at Incubating
-public interface TestSuite extends ProjectNativeComponent {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteContainer.java
deleted file mode 100644
index d950f5d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteContainer.java
+++ /dev/null
@@ -1,27 +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.nativebinaries.test;
-
-import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
-import org.gradle.api.Incubating;
-
-/**
- * A polymorphic container of {@link TestSuite} instances.
- */
- at Incubating
-public interface TestSuiteContainer extends ExtensiblePolymorphicDomainObjectContainer<TestSuite> {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteExecutableBinary.java
deleted file mode 100644
index fbcdd49..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/TestSuiteExecutableBinary.java
+++ /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.nativebinaries.test;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.ExecutableBinary;
-
-/**
- * An executable which runs a suite of tests.
- */
- at Incubating
-public interface TestSuiteExecutableBinary extends ExecutableBinary {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestSuite.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestSuite.java
deleted file mode 100644
index bd746a5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestSuite.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test.cunit;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.test.ProjectComponentTestSuite;
-
-/**
- * Test suite of CUnit tests.
- */
- at Incubating
-public interface CUnitTestSuite extends ProjectComponentTestSuite {
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/ConfigureCUnitTestSources.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/ConfigureCUnitTestSources.java
deleted file mode 100644
index c47c38c..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/ConfigureCUnitTestSources.java
+++ /dev/null
@@ -1,66 +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.nativebinaries.test.cunit.internal;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.language.c.CSourceSet;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-import org.gradle.nativebinaries.test.cunit.CUnitTestSuite;
-import org.gradle.nativebinaries.test.cunit.tasks.GenerateCUnitLauncher;
-
-import java.io.File;
-
-public class ConfigureCUnitTestSources {
-    private final ProjectInternal project;
-
-    public ConfigureCUnitTestSources(ProjectInternal project) {
-        this.project = project;
-    }
-
-    public void apply(CUnitTestSuite cUnitTestSuite) {
-        FunctionalSourceSet suiteSourceSet = createSuiteSources(cUnitTestSuite);
-
-        CSourceSet launcherSources = suiteSourceSet.maybeCreate("cunitLauncher", CSourceSet.class);
-        cUnitTestSuite.source(launcherSources);
-        createCUnitLauncherTask(cUnitTestSuite, launcherSources);
-
-        CSourceSet testSources = suiteSourceSet.maybeCreate("cunit", CSourceSet.class);
-        cUnitTestSuite.source(testSources);
-        testSources.lib(launcherSources);
-
-        ProjectNativeComponent testedComponent = cUnitTestSuite.getTestedComponent();
-        cUnitTestSuite.source(testedComponent.getSource());
-
-        testSources.lib(testedComponent.getSource().withType(CSourceSet.class));
-    }
-
-    private FunctionalSourceSet createSuiteSources(CUnitTestSuite cUnitTestSuite) {
-        ProjectSourceSet projectSourceSet = project.getExtensions().getByType(ProjectSourceSet.class);
-        return projectSourceSet.maybeCreate(cUnitTestSuite.getName());
-    }
-
-    private void createCUnitLauncherTask(CUnitTestSuite suite, CSourceSet cunitSourceSet) {
-        String taskName = suite.getName() + "CUnitLauncher";
-        GenerateCUnitLauncher skeletonTask = project.getTasks().create(taskName, GenerateCUnitLauncher.class);
-
-        File baseDir = new File(project.getBuildDir(), "src/" + taskName);
-        skeletonTask.setSourceDir(new File(baseDir, "cunit"));
-        skeletonTask.setHeaderDir(new File(baseDir, "headers"));
-        cunitSourceSet.generatedBy(skeletonTask);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/CreateCUnitBinaries.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/CreateCUnitBinaries.java
deleted file mode 100644
index c9374c6..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/CreateCUnitBinaries.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test.cunit.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.plugins.ExtensionAware;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.base.internal.DefaultBinaryNamingSchemeBuilder;
-import org.gradle.nativebinaries.ProjectNativeBinary;
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.language.internal.DefaultPreprocessingTool;
-import org.gradle.nativebinaries.test.TestSuiteExecutableBinary;
-import org.gradle.nativebinaries.test.cunit.CUnitTestSuite;
-import org.gradle.nativebinaries.test.internal.DefaultTestSuiteExecutableBinary;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-
-public class CreateCUnitBinaries {
-    private final ProjectInternal project;
-    private final Instantiator instantiator;
-    private final NativeDependencyResolver resolver;
-
-    public CreateCUnitBinaries(ProjectInternal project, Instantiator instantiator, NativeDependencyResolver resolver) {
-        this.project = project;
-        this.instantiator = instantiator;
-        this.resolver = resolver;
-    }
-
-    public void apply(final CUnitTestSuite cUnitTestSuite, final BinaryContainer binaries) {
-        cUnitTestSuite.getTestedComponent().getBinaries().withType(ProjectNativeBinaryInternal.class).all(new Action<ProjectNativeBinaryInternal>() {
-            public void execute(ProjectNativeBinaryInternal testedBinary) {
-                final ProjectNativeBinary cunitExe = createTestBinary(cUnitTestSuite, testedBinary, project);
-                ((ExtensionAware) cunitExe).getExtensions().create("cCompiler", DefaultPreprocessingTool.class);
-
-                cUnitTestSuite.getBinaries().add(cunitExe);
-                binaries.add(cunitExe);
-
-                testedBinary.getSource().all(new Action<LanguageSourceSet>() {
-                    public void execute(LanguageSourceSet languageSourceSet) {
-                        cunitExe.source(languageSourceSet);
-                    }
-                });
-            }
-        });
-    }
-
-    public ProjectNativeBinary createTestBinary(CUnitTestSuite cUnitTestSuite, ProjectNativeBinaryInternal testedBinary, ProjectInternal project) {
-        BinaryNamingScheme namingScheme = new DefaultBinaryNamingSchemeBuilder(testedBinary.getNamingScheme())
-                .withComponentName(cUnitTestSuite.getBaseName())
-                .withTypeString("CUnitExe").build();
-
-        ProjectNativeBinary testBinary = instantiator.newInstance(DefaultTestSuiteExecutableBinary.class,
-                cUnitTestSuite, testedBinary.getFlavor(), testedBinary.getToolChain(),
-                testedBinary.getTargetPlatform(), testedBinary.getBuildType(), namingScheme, resolver);
-
-        setupDefaults(testBinary, project);
-        return testBinary;
-    }
-
-    private void setupDefaults(ProjectNativeBinary nativeBinary, ProjectInternal project) {
-        BinaryNamingScheme namingScheme = ((ProjectNativeBinaryInternal) nativeBinary).getNamingScheme();
-        File binaryOutputDir = new File(new File(project.getBuildDir(), "binaries"), namingScheme.getOutputDirectoryBase());
-        String baseName = nativeBinary.getComponent().getBaseName();
-
-        ToolChainInternal tc = (ToolChainInternal) nativeBinary.getToolChain();
-        ((TestSuiteExecutableBinary) nativeBinary).setExecutableFile(new File(binaryOutputDir, tc.getExecutableName(baseName)));
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/DefaultCUnitTestSuite.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/DefaultCUnitTestSuite.java
deleted file mode 100644
index 5b6d34d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/internal/DefaultCUnitTestSuite.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test.cunit.internal;
-
-import org.gradle.nativebinaries.ProjectNativeComponent;
-import org.gradle.nativebinaries.internal.AbstractProjectNativeComponent;
-import org.gradle.nativebinaries.internal.NativeProjectComponentIdentifier;
-import org.gradle.nativebinaries.test.ProjectComponentTestSuite;
-import org.gradle.nativebinaries.test.cunit.CUnitTestSuite;
-
-public class DefaultCUnitTestSuite extends AbstractProjectNativeComponent implements CUnitTestSuite, ProjectComponentTestSuite {
-    private final ProjectNativeComponent testedComponent;
-
-    public DefaultCUnitTestSuite(NativeProjectComponentIdentifier id, ProjectNativeComponent testedComponent) {
-        super(id);
-        this.testedComponent = testedComponent;
-    }
-
-    public String getDisplayName() {
-        return String.format("cunit tests '%s'", getName());
-    }
-
-    public ProjectNativeComponent getTestedComponent() {
-        return testedComponent;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/package-info.java
deleted file mode 100644
index eb425bc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * API classes for cunit integration.
- */
-package org.gradle.nativebinaries.test.cunit;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPlugin.groovy
deleted file mode 100644
index 4044e3f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/CUnitPlugin.groovy
+++ /dev/null
@@ -1,72 +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.nativebinaries.test.cunit.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.BinaryContainer
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.NativeProjectComponentIdentifier
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.test.TestSuiteContainer
-import org.gradle.nativebinaries.test.cunit.CUnitTestSuite
-import org.gradle.nativebinaries.test.cunit.internal.ConfigureCUnitTestSources
-import org.gradle.nativebinaries.test.cunit.internal.CreateCUnitBinaries
-import org.gradle.nativebinaries.test.cunit.internal.DefaultCUnitTestSuite
-import org.gradle.nativebinaries.test.plugins.NativeBinariesTestPlugin
-
-import javax.inject.Inject
-/**
- * A plugin that sets up the infrastructure for testing native binaries with CUnit.
- */
- at Incubating
-public class CUnitPlugin implements Plugin<ProjectInternal> {
-
-    private final Instantiator instantiator
-    private final NativeDependencyResolver resolver;
-
-    @Inject
-    public CUnitPlugin(Instantiator instantiator, NativeDependencyResolver resolver) {
-        this.instantiator = instantiator;
-        this.resolver = resolver;
-    }
-
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(NativeBinariesTestPlugin.class)
-
-        TestSuiteContainer testSuites = project.getExtensions().getByType(TestSuiteContainer)
-        BinaryContainer binaries = project.getExtensions().getByType(BinaryContainer)
-        project.getExtensions().getByType(ExecutableContainer).all { Executable executable ->
-            testSuites.add createCUnitTestSuite(executable, binaries, project)
-        }
-        project.getExtensions().getByType(LibraryContainer).all { Library library ->
-            testSuites.add createCUnitTestSuite(library, binaries, project)
-        }
-    }
-
-    private CUnitTestSuite createCUnitTestSuite(ProjectNativeComponent testedComponent, BinaryContainer binaries, ProjectInternal project) {
-        String suiteName = "${testedComponent.name}Test"
-        String path = (testedComponent as ProjectNativeComponentInternal).projectPath
-        NativeProjectComponentIdentifier id = new NativeProjectComponentIdentifier(path, suiteName);
-        CUnitTestSuite cUnitTestSuite = instantiator.newInstance(DefaultCUnitTestSuite, id, testedComponent);
-
-        new ConfigureCUnitTestSources(project).apply(cUnitTestSuite)
-        new CreateCUnitBinaries(project, instantiator, resolver).apply(cUnitTestSuite, binaries);
-        return cUnitTestSuite;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/package-info.java
deleted file mode 100644
index 1b9661b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Plugins for cunit testing.
- */
-package org.gradle.nativebinaries.test.cunit.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/GenerateCUnitLauncher.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/GenerateCUnitLauncher.groovy
deleted file mode 100644
index 4820825..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/GenerateCUnitLauncher.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test.cunit.tasks
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-
-/**
- * Generated the Gradle CUnit launcher: main method and header.
- */
-class GenerateCUnitLauncher extends DefaultTask {
-    @OutputDirectory File sourceDir
-    @OutputDirectory File headerDir
-
-    @TaskAction
-    void generate() {
-        writeToFile(sourceDir, "gradle_cunit_main.c")
-        writeToFile(headerDir, "gradle_cunit_register.h")
-    }
-
-    private void writeToFile(File directory, String fileName) {
-        final File file = new File(directory, fileName)
-        file.text = ''
-        file << getClass().getResourceAsStream(fileName)
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/package-info.java
deleted file mode 100644
index 92abed7..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/cunit/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for cunit integration.
- */
-package org.gradle.nativebinaries.test.cunit.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteContainer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteContainer.java
deleted file mode 100644
index dcf907d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteContainer.java
+++ /dev/null
@@ -1,30 +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.nativebinaries.test.internal;
-
-import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.test.TestSuite;
-import org.gradle.nativebinaries.test.TestSuiteContainer;
-
-// TODO:DAZ Add a 'components' container (polymorphic), and then a writable container filtered by type that looks like a non-polymorphic container
-// Then 'executables', 'libraries' and 'testSuites' would all be filtered containers, not separate.
-public class DefaultTestSuiteContainer extends DefaultPolymorphicDomainObjectContainer<TestSuite> implements TestSuiteContainer {
-    public DefaultTestSuiteContainer(Instantiator instantiator) {
-        super(TestSuite.class, instantiator);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteExecutableBinary.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteExecutableBinary.java
deleted file mode 100644
index 1b0ba06..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/internal/DefaultTestSuiteExecutableBinary.java
+++ /dev/null
@@ -1,48 +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.nativebinaries.test.internal;
-
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.nativebinaries.BuildType;
-import org.gradle.nativebinaries.Flavor;
-import org.gradle.nativebinaries.ProjectNativeComponent;
-import org.gradle.nativebinaries.test.TestSuiteExecutableBinary;
-import org.gradle.nativebinaries.internal.AbstractProjectNativeBinary;
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal;
-
-import java.io.File;
-
-public class DefaultTestSuiteExecutableBinary extends AbstractProjectNativeBinary implements TestSuiteExecutableBinary {
-    private File executableFile;
-
-    public DefaultTestSuiteExecutableBinary(ProjectNativeComponent owner, Flavor flavor, ToolChainInternal toolChain, Platform targetPlatform, BuildType buildType, BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-        super(owner, flavor, toolChain, targetPlatform, buildType, namingScheme, resolver);
-    }
-
-    public File getExecutableFile() {
-        return executableFile;
-    }
-
-    public void setExecutableFile(File executableFile) {
-        this.executableFile = executableFile;
-    }
-
-    public File getPrimaryOutput() {
-        return getExecutableFile();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/package-info.java
deleted file mode 100644
index fa0a484..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * API classes for testing native binaries.
- */
-package org.gradle.nativebinaries.test;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/NativeBinariesTestPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/NativeBinariesTestPlugin.groovy
deleted file mode 100644
index fbacfc4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/NativeBinariesTestPlugin.groovy
+++ /dev/null
@@ -1,75 +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.nativebinaries.test.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.BinaryContainer
-import org.gradle.model.ModelRules
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-import org.gradle.nativebinaries.tasks.InstallExecutable
-import org.gradle.nativebinaries.test.TestSuiteExecutableBinary
-import org.gradle.nativebinaries.test.internal.DefaultTestSuiteContainer
-import org.gradle.nativebinaries.test.tasks.RunTestExecutable
-
-import javax.inject.Inject
-
-/**
- * A plugin that sets up the infrastructure for testing native binaries with CUnit.
- */
- at Incubating
-public class NativeBinariesTestPlugin implements Plugin<ProjectInternal> {
-
-    private final Instantiator instantiator
-    private final ModelRules modelRules
-    private final NativeDependencyResolver resolver
-
-    @Inject
-    public NativeBinariesTestPlugin(Instantiator instantiator, ModelRules modelRules, NativeDependencyResolver resolver) {
-        this.instantiator = instantiator
-        this.modelRules = modelRules
-        this.resolver = resolver
-    }
-
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(NativeBinariesPlugin.class)
-
-        project.getExtensions().create(
-                "testSuites",
-                DefaultTestSuiteContainer.class,
-                instantiator
-        );
-
-        final BinaryContainer binaries = project.getExtensions().getByType(BinaryContainer.class);
-        binaries.withType(TestSuiteExecutableBinary).all { testBinary ->
-            def binary = testBinary as ProjectNativeBinaryInternal
-            def namingScheme = binary.namingScheme
-
-            // TODO:DAZ Need a better model for accessing tasks related to binary
-            def installTask = binary.tasks.withType(InstallExecutable).find()
-
-            def runTask = project.tasks.create(namingScheme.getTaskName("run"), RunTestExecutable)
-            runTask.setDescription("Runs the " + binary.getDisplayName())
-            runTask.inputs.files(installTask.outputs.files)
-
-            runTask.conventionMapping.testExecutable = { installTask.runScript }
-            runTask.conventionMapping.outputDir = { project.file("${project.buildDir}/test-results/${namingScheme.outputDirectoryBase}") }
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/package-info.java
deleted file mode 100644
index 34b66d1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Plugin classes for generic support for testing native binaries.
- */
-package org.gradle.nativebinaries.test.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/RunTestExecutable.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/RunTestExecutable.groovy
deleted file mode 100644
index 698a55f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/RunTestExecutable.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.test.tasks
-import org.gradle.api.GradleException
-import org.gradle.api.Incubating
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-import org.gradle.logging.ConsoleRenderer
-import org.gradle.process.ExecResult
-import org.gradle.process.internal.ExecAction
-import org.gradle.process.internal.ExecActionFactory
-/**
- * Runs a compiled and installed test executable.
- */
- at Incubating
-public class RunTestExecutable extends ConventionTask {
-    /**
-     * The executable binary to run.
-     */
-    @InputFile File testExecutable
-
-    /**
-     * The directory where the results should be generated.
-     */
-    @OutputDirectory File outputDir
-
-    /**
-     * Should the build continue if a test fails, or should the build break?
-     */
-    @Input boolean ignoreFailures
-
-    private ExecAction execAction;
-    private ExecResult execResult;
-
-    public RunTestExecutable() {
-        execAction = getServices().get(ExecActionFactory.class).newExecAction();
-    }
-
-    @TaskAction
-    void exec() {
-        execAction.setExecutable(getTestExecutable())
-        execAction.setWorkingDir(getOutputDir())
-        try {
-            execResult = execAction.execute();
-        } catch (Exception e) {
-            handleTestFailures(e);
-        }
-    }
-
-    private void handleTestFailures(Exception e) {
-        String message = "There were failing tests";
-        String resultsUrl = new ConsoleRenderer().asClickableFileUrl(getOutputDir());
-        message = message.concat(". See the results at: " + resultsUrl);
-
-        if (isIgnoreFailures()) {
-            getLogger().warn(message);
-        } else {
-            throw new GradleException(message, e);
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/package-info.java
deleted file mode 100644
index 2601087..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/test/tasks/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Tasks for test execution.
- */
-package org.gradle.nativebinaries.test.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Clang.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Clang.java
deleted file mode 100644
index 0601249..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Clang.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * The <a href="http://clang.llvm.org">Clang</a> tool chain.
- */
- at Incubating
-public interface Clang extends PlatformConfigurableToolChain {
-    /**
-     * The paths setting required for executing the tool chain.
-     * These are used to locate tools for this tool chain, and are prepended to the system PATH when executing these tools.
-     */
-    List<File> getPath();
-
-    /**
-     * Append an entry or entries to the tool chain path.
-     *
-     * @param pathEntries The path values to append. These are evaluated as per {@link org.gradle.api.Project#files(Object...)}
-     */
-    void path(Object... pathEntries);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Gcc.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Gcc.java
deleted file mode 100644
index 1c65bf9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/Gcc.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * The <a href="http://gcc.gnu.org/">GNU GCC</a> tool chain.
- */
- at Incubating
-public interface Gcc extends PlatformConfigurableToolChain {
-    /**
-     * The path required for executing the tool chain.
-     * These are used to locate tools for this tool chain, and are prepended to the system PATH when executing these tools.
-     */
-    List<File> getPath();
-
-    /**
-     * Append an entry or entries to the tool chain path.
-     *
-     * @param pathEntries The path values to append. These are evaluated as per {@link org.gradle.api.Project#files(Object...)}
-     */
-    void path(Object... pathEntries);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/GccTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/GccTool.java
deleted file mode 100644
index be4642f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/GccTool.java
+++ /dev/null
@@ -1,42 +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.nativebinaries.toolchain;
-
-import groovy.lang.Closure;
-import org.gradle.api.Incubating;
-
-/**
- * An executable tool that forms part of a tool chain.
- */
- at Incubating
-public interface GccTool {
-    /**
-     * The name of the executable file for this tool.
-     */
-    String getExecutable();
-
-    /**
-     * Set the name of the executable file for this tool.
-     * The executable will be located in the tool chain path.
-     */
-    void setExecutable(String file);
-
-    /**
-     * Adds an action that will be applied to the command-line arguments prior to execution.
-     */
-    void withArguments(Closure arguments);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/PlatformConfigurableToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/PlatformConfigurableToolChain.java
deleted file mode 100644
index a74c869..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/PlatformConfigurableToolChain.java
+++ /dev/null
@@ -1,55 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-
-/**
- * A ToolChain that can handle additional platforms simply by configuring the NativeBinary.
- */
- at Incubating
-public interface PlatformConfigurableToolChain extends ToolChain {
-
-    /**
-     * Add configuration for a target platform.
-     */
-    void addPlatformConfiguration(TargetPlatformConfiguration platformConfig);
-
-    /**
-     * The C++ compiler.
-     */
-    GccTool getCCompiler();
-
-    /**
-     * The C compiler.
-     */
-    GccTool getCppCompiler();
-
-    /**
-     * The assembler.
-     */
-    GccTool getAssembler();
-
-    /**
-     * The linker.
-     */
-    GccTool getLinker();
-
-    /**
-     * The static library archiver.
-     */
-    GccTool getStaticLibArchiver();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/TargetPlatformConfiguration.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/TargetPlatformConfiguration.java
deleted file mode 100644
index eebc167..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/TargetPlatformConfiguration.java
+++ /dev/null
@@ -1,67 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-import org.gradle.nativebinaries.platform.Platform;
-
-import java.util.List;
-
-/**
- * Configuration to add support for a target platform to a {@link PlatformConfigurableToolChain}.
- */
- at Incubating
-public interface TargetPlatformConfiguration {
-    /**
-     * Matches the platform that this configuration supports.
-     */
-    boolean supportsPlatform(Platform targetPlatform);
-
-    /**
-     * The additional args supplied to the C++ compiler to target the platform.
-     */
-    List<String> getCppCompilerArgs();
-
-    /**
-     * The additional args supplied to the C compiler to target the platform.
-     */
-    List<String> getCCompilerArgs();
-
-    /**
-     * The additional args supplied to the Objective-C++ compiler to target the platform.
-     */
-    List<String> getObjectiveCppCompilerArgs();
-
-    /**
-     * The additional args supplied to the Objective-C compiler to target the platform.
-     */
-    List<String> getObjectiveCCompilerArgs();
-
-    /**
-     * The additional args supplied to the Assembler to target the platform.
-     */
-    List<String> getAssemblerArgs();
-
-    /**
-     * The additional args supplied to the Static Library Archiver to target the platform.
-     */
-    List<String> getStaticLibraryArchiverArgs();
-
-    /**
-     * The additional args supplied to the Linker to target the platform.
-     */
-    List<String> getLinkerArgs();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChain.java
deleted file mode 100644
index c5259fb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChain.java
+++ /dev/null
@@ -1,35 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A set of compilers and linkers that are used together to construct a native binary.
- */
- at Incubating
- at HasInternalProtocol
-public interface ToolChain extends Named {
-    /**
-     * Returns a human consumable name for this tool chain.
-     *
-     * @since 1.11
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChainRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChainRegistry.java
deleted file mode 100644
index 6679cdd..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/ToolChainRegistry.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.nativebinaries.toolchain;
-
-import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
-import org.gradle.api.Incubating;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A container for {@link ToolChain}s.
- */
- at Incubating
- at HasInternalProtocol
-public interface ToolChainRegistry extends ExtensiblePolymorphicDomainObjectContainer<ToolChain> {
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/VisualCpp.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/VisualCpp.java
deleted file mode 100644
index d432620..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/VisualCpp.java
+++ /dev/null
@@ -1,47 +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.nativebinaries.toolchain;
-
-import org.gradle.api.Incubating;
-
-import java.io.File;
-
-/**
- * The Visual C++ tool chain.
- */
- at Incubating
-public interface VisualCpp extends ToolChain {
-    /**
-     * The directory where Visual Studio or Visual C++ is installed.
-     */
-    File getInstallDir();
-
-    /**
-     * The directory where Visual Studio or Visual C++ is installed.
-     */
-    void setInstallDir(Object installDir);
-
-    /**
-     * The directory where Windows SDK is installed.
-     */
-    File getWindowsSdkDir();
-
-    /**
-     * The directory where Windows SDK is installed.
-     */
-    void setWindowsSdkDir(Object installDir);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/AbstractToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/AbstractToolChain.java
deleted file mode 100644
index 0a78c31..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/AbstractToolChain.java
+++ /dev/null
@@ -1,73 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.os.OperatingSystem;
-
-import java.io.File;
-
-public abstract class AbstractToolChain implements ToolChainInternal {
-    private final String name;
-    protected final OperatingSystem operatingSystem;
-    private final FileResolver fileResolver;
-
-    protected AbstractToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver) {
-        this.name = name;
-        this.operatingSystem = operatingSystem;
-        this.fileResolver = fileResolver;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    protected abstract String getTypeName();
-
-    public String getDisplayName() {
-        return String.format("Tool chain '%s' (%s)", getName(), getTypeName());
-    }
-
-    @Override
-    public String toString() {
-        return getDisplayName();
-    }
-
-    public String getOutputType() {
-        return String.format("%s-%s", getName(), operatingSystem.getName());
-    }
-
-    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);
-    }
-
-    protected File resolve(Object path) {
-        return fileResolver.resolve(path);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ArgsTransformer.java
deleted file mode 100644
index a7cf5fb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ArgsTransformer.java
+++ /dev/null
@@ -1,25 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.Transformer;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-
-import java.util.List;
-
-public interface ArgsTransformer<T extends BinaryToolSpec> extends Transformer<List<String>, T> {
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CommandLineTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CommandLineTool.java
deleted file mode 100755
index b06d42e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CommandLineTool.java
+++ /dev/null
@@ -1,113 +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.nativebinaries.toolchain.internal;
-
-import com.google.common.base.Joiner;
-import org.gradle.api.GradleException;
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.process.internal.ExecException;
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-import java.util.*;
-
-public class CommandLineTool<T extends BinaryToolSpec> {
-    private final String action;
-    private final File executable;
-    private final ExecActionFactory execActionFactory;
-    private final Map<String, String> environment = new HashMap<String, String>();
-    private final List<File> path = new ArrayList<File>();
-    private ArgsTransformer<T> specToArgs;
-    private Transformer<T, T> specTransformer = new IdentityTransformer<T>();
-    private File workDir;
-
-    public CommandLineTool(String action, File executable, ExecActionFactory 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> withSpecTransformer(Transformer<T, T> specAction) {
-        this.specTransformer = specAction;
-        return this;
-    }
-
-    public CommandLineTool<T> withPath(List<File> pathEntries) {
-        path.addAll(pathEntries);
-        return this;
-    }
-
-    public CommandLineTool<T> withPath(File... pathEntries) {
-        Collections.addAll(path, pathEntries);
-        return this;
-    }
-
-    public CommandLineTool<T> withEnvironmentVar(String name, String value) {
-        environment.put(name, value);
-        return this;
-    }
-
-    public CommandLineTool<T> withArguments(ArgsTransformer<T> arguments) {
-        this.specToArgs = arguments;
-        return this;
-    }
-
-    public WorkResult execute(T spec) {
-        ExecAction compiler = execActionFactory.newExecAction();
-        compiler.executable(executable);
-        if (workDir != null) {
-            compiler.workingDir(workDir);
-        }
-
-        List<String> args = specToArgs.transform(specTransformer.transform(spec));
-        compiler.args(args);
-
-        if (!path.isEmpty()) {
-            String pathVar = OperatingSystem.current().getPathVar();
-            String compilerPath = Joiner.on(File.pathSeparator).join(path);
-            compilerPath = compilerPath + 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);
-    }
-
-    static class IdentityTransformer<T> implements Transformer<T, T> {
-        public T transform(T original) {
-            return original;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CompileSpecToArgsTransformerChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CompileSpecToArgsTransformerChain.java
deleted file mode 100644
index f107cfe..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/CompileSpecToArgsTransformerChain.java
+++ /dev/null
@@ -1,44 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.Transformer;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CompileSpecToArgsTransformerChain<T extends BinaryToolSpec> implements Transformer<List<String>, T> {
-    private final Transformer<List<String>, T> delegate;
-    private final List<Transformer<List<String>, List<String>>> transformers = new ArrayList<Transformer<List<String>, List<String>>>();
-
-    public CompileSpecToArgsTransformerChain(Transformer<List<String>, T> delegate) {
-        this.delegate = delegate;
-    }
-
-    public List<String> transform(T spec) {
-        List<String> args = delegate.transform(spec);
-        for (Transformer<List<String>, List<String>> transformer : transformers) {
-            args = transformer.transform(args);
-        }
-        return args;
-    }
-
-    public CompileSpecToArgsTransformerChain<T> withTransformation(Transformer<List<String>, List<String>> transformer) {
-        transformers.add(transformer);
-        return this;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistry.java
deleted file mode 100755
index 3c57b70..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistry.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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-import org.gradle.util.TreeVisitor;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class DefaultToolChainRegistry extends DefaultPolymorphicDomainObjectContainer<ToolChain> implements ToolChainRegistryInternal {
-    private final Map<String, Class<? extends ToolChain>> registeredDefaults = new LinkedHashMap<String, Class<? extends ToolChain>>();
-    private final List<ToolChainInternal> searchOrder = new ArrayList<ToolChainInternal>();
-
-    public DefaultToolChainRegistry(Instantiator instantiator) {
-        super(ToolChain.class, instantiator);
-        whenObjectAdded(new Action<ToolChain>() {
-            public void execute(ToolChain toolChain) {
-                searchOrder.add((ToolChainInternal) toolChain);
-            }
-        });
-        whenObjectRemoved(new Action<ToolChain>() {
-            public void execute(ToolChain toolChain) {
-                searchOrder.remove(toolChain);
-            }
-        });
-    }
-
-    @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) {
-        registeredDefaults.put(name, type);
-    }
-
-    public void addDefaultToolChains() {
-        for (String name : registeredDefaults.keySet()) {
-            create(name, registeredDefaults.get(name));
-        }
-    }
-
-    public ToolChain getForPlatform(Platform targetPlatform) {
-        for (ToolChainInternal toolChain : searchOrder) {
-            if (toolChain.target(targetPlatform).isAvailable()) {
-                return toolChain;
-            }
-        }
-
-        // No tool chains can build for this platform. Assemble a description of why
-        Map<String, PlatformToolChain> candidates = new LinkedHashMap<String, PlatformToolChain>();
-        for (ToolChainInternal toolChain : searchOrder) {
-            candidates.put(toolChain.getDisplayName(), toolChain.target(targetPlatform));
-        }
-
-        return new UnavailableToolChain(new UnavailableToolChainDescription(targetPlatform, candidates));
-    }
-
-    private static class UnavailableToolChainDescription implements ToolSearchResult {
-        private final Platform targetPlatform;
-        private final Map<String, PlatformToolChain> candidates;
-
-        private UnavailableToolChainDescription(Platform targetPlatform, Map<String, PlatformToolChain> candidates) {
-            this.targetPlatform = targetPlatform;
-            this.candidates = candidates;
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            visitor.node(String.format("No tool chain is available to build for platform '%s'", targetPlatform.getName()));
-            visitor.startChildren();
-            for (Map.Entry<String, PlatformToolChain> entry : candidates.entrySet()) {
-                visitor.node(entry.getKey());
-                visitor.startChildren();
-                entry.getValue().explain(visitor);
-                visitor.endChildren();
-            }
-            if (candidates.isEmpty()) {
-                visitor.node("No tool chain plugin applied.");
-            }
-            visitor.endChildren();
-        }
-    }
-    private static class UnavailableToolChain implements ToolChainInternal {
-        private final OperatingSystem operatingSystem = OperatingSystem.current();
-        private final ToolSearchResult failure;
-
-        UnavailableToolChain(ToolSearchResult failure) {
-            this.failure = failure;
-        }
-
-        public String getDisplayName() {
-            return getName();
-        }
-
-        public String getName() {
-            return "unavailable";
-        }
-
-        public PlatformToolChain target(Platform targetPlatform) {
-            return new UnavailablePlatformToolChain(failure);
-        }
-
-        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() {
-            return "unavailable";
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/MacroArgsConverter.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/MacroArgsConverter.java
deleted file mode 100644
index be90e2f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/MacroArgsConverter.java
+++ /dev/null
@@ -1,35 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.Transformer;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class MacroArgsConverter implements Transformer<List<String>, Map<String, String>> {
-    public List<String> transform(Map<String, String> original) {
-        List<String> macroList = new ArrayList<String>(original.size());
-        for (String macroName : original.keySet()) {
-            String macroDef = original.get(macroName);
-            String arg = macroDef == null ? macroName : String.format("%s=%s", macroName, macroDef);
-            macroList.add(arg);
-        }
-        return macroList;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/NativeCompileSpec.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/NativeCompileSpec.java
deleted file mode 100644
index 4efbccc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/NativeCompileSpec.java
+++ /dev/null
@@ -1,63 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 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);
-
-    List<File> getIncludeRoots();
-
-    void include(Iterable<File> includeRoots);
-
-    void include(File... includeRoots);
-
-    List<File> getSourceFiles();
-
-    void setSourceFiles(Collection<File> sources);
-
-    void source(Iterable<File> sources);
-
-    List<File> getRemovedSourceFiles();
-
-    void setRemovedSourceFiles(Collection<File> sources);
-
-    void removedSource(Iterable<File> sources);
-
-    Map<String, String> getMacros();
-
-    void setMacros(Map<String, String> macros);
-
-    void define(String name);
-
-    void define(String name, String value);
-
-    boolean isPositionIndependentCode();
-
-    void setPositionIndependentCode(boolean flag);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OptionsFileArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OptionsFileArgsTransformer.java
deleted file mode 100644
index 88f4393..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OptionsFileArgsTransformer.java
+++ /dev/null
@@ -1,64 +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.nativebinaries.toolchain.internal;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.api.Transformer;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.tasks.compile.ArgWriter;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class OptionsFileArgsTransformer<T extends BinaryToolSpec> implements ArgsTransformer<T> {
-    private final Transformer<ArgWriter, PrintWriter> argWriterFactory;
-    private final ArgsTransformer<T> delegate;
-
-    public OptionsFileArgsTransformer(Transformer<ArgWriter, PrintWriter> argWriterFactory, ArgsTransformer<T> delegate) {
-        this.argWriterFactory = argWriterFactory;
-        this.delegate = delegate;
-    }
-
-    public List<String> transform(T spec) {
-        List<String> output = new ArrayList<String>();
-        transformArgs(delegate.transform(spec), output, spec.getTempDir());
-        return output;
-    }
-
-    protected void transformArgs(List<String> input, List<String> output, File tempDir) {
-        GFileUtils.mkdirs(tempDir);
-        File optionsFile = new File(tempDir, "options.txt");
-        try {
-            PrintWriter writer = new PrintWriter(optionsFile);
-            try {
-                ArgWriter argWriter = argWriterFactory.transform(writer);
-                argWriter.args(input);
-            } finally {
-                IOUtils.closeQuietly(writer);
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(String.format("Could not write compiler options file '%s'.", optionsFile.getAbsolutePath()), e);
-        }
-
-        output.add(String.format("@%s", optionsFile.getAbsolutePath()));
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompiler.java
deleted file mode 100755
index 5b51ce8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompiler.java
+++ /dev/null
@@ -1,67 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.hash.HashUtil;
-
-import java.io.File;
-
-public class OutputCleaningCompiler<T extends NativeCompileSpec> implements Compiler<T> {
-
-    private final Compiler<T> compiler;
-    private final String outputFileSuffix;
-
-    public OutputCleaningCompiler(Compiler<T> compiler, String outputFileSuffix) {
-        this.compiler = compiler;
-        this.outputFileSuffix = outputFileSuffix;
-    }
-
-    public WorkResult execute(T spec) {
-        boolean didRemove = deleteOutputsForRemovedSources(spec);
-        boolean didCompile = compileSources(spec);
-        return new SimpleWorkResult(didRemove || didCompile);
-    }
-
-    private boolean compileSources(T spec) {
-        if (spec.getSourceFiles().isEmpty()) {
-            return false;
-        }
-        return compiler.execute(spec).getDidWork();
-    }
-
-    private boolean deleteOutputsForRemovedSources(NativeCompileSpec spec) {
-        boolean didRemove = false;
-        for (File removedSource : spec.getRemovedSourceFiles()) {
-            File objectFile = getObjectFile(spec.getObjectFileDir(), removedSource);
-            if (objectFile.delete()) {
-                didRemove = true;
-                objectFile.getParentFile().delete();
-            }
-        }
-        return didRemove;
-    }
-
-    private File getObjectFile(File objectFileRoot, File sourceFile) {
-        String objectFileName = sourceFile.getName().replaceFirst("\\.[^\\.]+$", outputFileSuffix);
-        String compactMD5 = HashUtil.createCompactMD5(sourceFile.getAbsolutePath());
-        File objectFileDir = new File(objectFileRoot, compactMD5);
-        return new File(objectFileDir, objectFileName);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/PlatformToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/PlatformToolChain.java
deleted file mode 100644
index a3c8fa0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/PlatformToolChain.java
+++ /dev/null
@@ -1,40 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.StaticLibraryArchiverSpec;
-
-public interface PlatformToolChain extends ToolSearchResult {
-    <T extends BinaryToolSpec> Compiler<T> createCppCompiler();
-
-    <T extends BinaryToolSpec> Compiler<T> createCCompiler();
-
-    <T extends BinaryToolSpec> Compiler<T> createObjectiveCppCompiler();
-
-    <T extends BinaryToolSpec> Compiler<T> createObjectiveCCompiler();
-
-    <T extends BinaryToolSpec> Compiler<T> createAssembler();
-
-    <T extends BinaryToolSpec> Compiler<T> createWindowsResourceCompiler();
-
-    <T extends LinkerSpec> Compiler<T> createLinker();
-
-    <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/SingleSourceCompileArgTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/SingleSourceCompileArgTransformer.java
deleted file mode 100644
index 44ed06d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/SingleSourceCompileArgTransformer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal;
-
-import org.gradle.internal.FileUtils;
-import org.gradle.internal.hash.HashUtil;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class SingleSourceCompileArgTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T> {
-    private final ArgsTransformer<T> delegate;
-    private final String objectFileName;
-    private final File sourceFile;
-    private final boolean usingVisualCToolChain;
-    private final boolean windowsPathLengthLimitation;
-
-    public SingleSourceCompileArgTransformer(File sourceFile, String objectFileName, ArgsTransformer<T> delegate, boolean windowsPathLengthLimitation, boolean usingVisualCToolChain) {
-        this.sourceFile = sourceFile;
-        this.delegate = delegate;
-        this.objectFileName = objectFileName;
-        this.usingVisualCToolChain = usingVisualCToolChain;
-        this.windowsPathLengthLimitation = windowsPathLengthLimitation;
-    }
-
-    public List<String> transform(T spec) {
-        List<String> args = new ArrayList<String>();
-        File outputFilePath = getOutputFileDir(sourceFile, spec.getObjectFileDir());
-
-        args.addAll(delegate.transform(spec));
-        args.add(sourceFile.getAbsolutePath());
-
-        if (usingVisualCToolChain) {
-            Collections.addAll(args, "/Fo" + outputFilePath.getAbsolutePath());
-        } else {
-            Collections.addAll(args, "-o", outputFilePath.getAbsolutePath());
-        }
-        return args;
-    }
-
-    protected File getOutputFileDir(File sourceFile, File objectFileDir) {
-        String compactMD5 = HashUtil.createCompactMD5(sourceFile.getAbsolutePath());
-        File outputFileDir = new File(objectFileDir, compactMD5);
-        if(!outputFileDir.exists()){
-            outputFileDir.mkdir();
-        }
-        File outputFile = new File(outputFileDir, objectFileName);
-        return windowsPathLengthLimitation ? FileUtils.assertInWindowsPathLengthLimitation(outputFile) : outputFile;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailability.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailability.java
deleted file mode 100644
index 81bc204..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailability.java
+++ /dev/null
@@ -1,68 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.internal.text.TreeFormatter;
-import org.gradle.util.TreeVisitor;
-
-public class ToolChainAvailability implements ToolSearchResult {
-    private ToolSearchResult reason;
-
-    public boolean isAvailable() {
-        return reason == null;
-    }
-
-    public String getUnavailableMessage() {
-        TreeFormatter formatter = new TreeFormatter();
-        this.explain(formatter);
-        return formatter.toString();
-    }
-
-    public void explain(TreeVisitor<? super String> visitor) {
-        reason.explain(visitor);
-    }
-
-    public ToolChainAvailability unavailable(String unavailableMessage) {
-        if (reason == null) {
-            reason = new FixedMessageToolSearchResult(unavailableMessage);
-        }
-        return this;
-    }
-
-    public ToolChainAvailability mustBeAvailable(ToolSearchResult tool) {
-        if (!tool.isAvailable() && reason == null) {
-            reason = tool;
-        }
-        return this;
-    }
-
-    private static class FixedMessageToolSearchResult implements ToolSearchResult {
-        private final String message;
-
-        private FixedMessageToolSearchResult(String message) {
-            this.message = message;
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            visitor.node(message);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainInternal.java
deleted file mode 100644
index 2e53bcf..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainInternal.java
+++ /dev/null
@@ -1,41 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-
-public interface ToolChainInternal extends ToolChain {
-    /**
-     * Locates the tools that can target the given platform.
-     */
-    PlatformToolChain target(Platform targetPlatform);
-
-    // TODO:DAZ These are platform-specific
-    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/nativebinaries/toolchain/internal/ToolChainRegistryInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainRegistryInternal.java
deleted file mode 100644
index 2db978a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainRegistryInternal.java
+++ /dev/null
@@ -1,34 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.ToolChain;
-import org.gradle.nativebinaries.toolchain.ToolChainRegistry;
-
-public interface ToolChainRegistryInternal extends ToolChainRegistry {
-    /**
-     * Registers a default ToolChain, which may later be added to the registry via {@link #addDefaultToolChains()}.
-     */
-    void registerDefaultToolChain(String name, Class<? extends ToolChain> type);
-
-    /**
-     * Adds default tool chains to the registry.
-     */
-    void addDefaultToolChains();
-
-    ToolChain getForPlatform(Platform targetPlatform);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolSearchResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolSearchResult.java
deleted file mode 100644
index 134101f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolSearchResult.java
+++ /dev/null
@@ -1,28 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.util.TreeVisitor;
-
-public interface ToolSearchResult {
-    boolean isAvailable();
-
-    /**
-     * Writes some diagnostics about why the tool is not available.
-     */
-    void explain(TreeVisitor<? super String> visitor);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolType.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolType.java
deleted file mode 100644
index a197514..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/ToolType.java
+++ /dev/null
@@ -1,44 +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.nativebinaries.toolchain.internal;
-
-import org.gradle.util.GUtil;
-
-public enum ToolType {
-    CPP_COMPILER("C++ compiler"),
-    C_COMPILER("C compiler"),
-    OBJECTIVECPP_COMPILER("Objective-C++ compiler"),
-    OBJECTIVEC_COMPILER("Objective-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/nativebinaries/toolchain/internal/UnavailablePlatformToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/UnavailablePlatformToolChain.java
deleted file mode 100644
index fe4d0f2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/UnavailablePlatformToolChain.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.text.TreeFormatter;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.StaticLibraryArchiverSpec;
-import org.gradle.util.TreeVisitor;
-
-public class UnavailablePlatformToolChain implements PlatformToolChain {
-    private final ToolSearchResult failure;
-
-    public UnavailablePlatformToolChain(ToolSearchResult failure) {
-        this.failure = failure;
-    }
-
-    public boolean isAvailable() {
-        return false;
-    }
-
-    public void explain(TreeVisitor<? super String> visitor) {
-        failure.explain(visitor);
-    }
-
-    private RuntimeException failure() {
-        TreeFormatter formatter = new TreeFormatter();
-        this.explain(formatter);
-        return new GradleException(formatter.toString());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
-        throw failure();
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
-        throw failure();
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
-        throw failure();
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createObjectiveCppCompiler() {
-        throw failure();
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createObjectiveCCompiler() {
-        throw failure();
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createWindowsResourceCompiler() {
-        throw failure();
-    }
-
-    public <T extends LinkerSpec> Compiler<T> createLinker() {
-        throw failure();
-    }
-
-    public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
-        throw failure();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/clang/ClangToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/clang/ClangToolChain.java
deleted file mode 100644
index 0ef94a8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/clang/ClangToolChain.java
+++ /dev/null
@@ -1,47 +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.nativebinaries.toolchain.internal.clang;
-
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.toolchain.Clang;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-import org.gradle.nativebinaries.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
-import org.gradle.nativebinaries.toolchain.internal.tools.ToolSearchPath;
-import org.gradle.process.internal.ExecActionFactory;
-
-public class ClangToolChain extends AbstractGccCompatibleToolChain implements Clang {
-    public static final String DEFAULT_NAME = "clang";
-
-    public ClangToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory) {
-        super(name, operatingSystem, fileResolver, execActionFactory, new ToolSearchPath(operatingSystem));
-
-        registerTool(ToolType.CPP_COMPILER, "clang++");
-        registerTool(ToolType.C_COMPILER, "clang");
-        registerTool(ToolType.OBJECTIVECPP_COMPILER, "clang++");
-        registerTool(ToolType.OBJECTIVEC_COMPILER, "clang");
-        registerTool(ToolType.ASSEMBLER, "as");
-        registerTool(ToolType.LINKER, "clang++");
-        registerTool(ToolType.STATIC_LIB_ARCHIVER, "ar");
-    }
-
-    @Override
-    protected String getTypeName() {
-        return "Clang";
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java
deleted file mode 100644
index 5a7575b..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java
+++ /dev/null
@@ -1,267 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal;
-import org.gradle.nativebinaries.toolchain.GccTool;
-import org.gradle.nativebinaries.toolchain.PlatformConfigurableToolChain;
-import org.gradle.nativebinaries.toolchain.TargetPlatformConfiguration;
-import org.gradle.nativebinaries.toolchain.internal.*;
-import org.gradle.nativebinaries.toolchain.internal.tools.*;
-import org.gradle.process.internal.ExecActionFactory;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
-/**
- * A tool chain that has GCC semantics, where all platform variants are produced by varying the tool args.
- */
-public abstract class AbstractGccCompatibleToolChain extends AbstractToolChain implements PlatformConfigurableToolChain {
-    private final ExecActionFactory execActionFactory;
-    private final ToolSearchPath toolSearchPath;
-    private final DefaultToolRegistry toolRegistry = new DefaultToolRegistry();
-
-    private final List<TargetPlatformConfiguration> platformConfigs = new ArrayList<TargetPlatformConfiguration>();
-    private int configInsertLocation;
-
-    public AbstractGccCompatibleToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, ToolSearchPath toolSearchPath) {
-        super(name, operatingSystem, fileResolver);
-        this.execActionFactory = execActionFactory;
-        this.toolSearchPath = toolSearchPath;
-
-        addPlatformConfiguration(new ToolChainDefaultArchitecture());
-        addPlatformConfiguration(new Intel32Architecture());
-        addPlatformConfiguration(new Intel64Architecture());
-        configInsertLocation = 0;
-    }
-
-    protected GccTool getTool(ToolType toolType) {
-        return toolRegistry.getTool(toolType);
-    }
-
-    protected void registerTool(ToolType type, String defaultExecutable) {
-        toolRegistry.register(type, defaultExecutable);
-    }
-
-    protected CommandLineToolSearchResult locate(ToolType type) {
-        GccTool gccTool = getTool(type);
-        return toolSearchPath.locate(type, gccTool.getExecutable());
-    }
-
-    public List<File> getPath() {
-        return toolSearchPath.getPath();
-    }
-
-    public void path(Object... pathEntries) {
-        for (Object path : pathEntries) {
-            toolSearchPath.path(resolve(path));
-        }
-    }
-
-    protected void initTools(ToolChainAvailability availability) {
-        for (ToolType type : ToolType.values()) {
-            locate(type);
-        }
-        boolean found = false;
-        for (ToolType type : Arrays.asList(ToolType.C_COMPILER, ToolType.CPP_COMPILER, ToolType.OBJECTIVEC_COMPILER, ToolType.OBJECTIVECPP_COMPILER)) {
-            found |= locate(type).isAvailable();
-        }
-        if (!found) {
-            availability.mustBeAvailable(locate(ToolType.C_COMPILER));
-        }
-    }
-
-    public void addPlatformConfiguration(TargetPlatformConfiguration platformConfig) {
-        platformConfigs.add(configInsertLocation, platformConfig);
-        configInsertLocation++;
-    }
-
-    public PlatformToolChain target(Platform targetPlatform) {
-        TargetPlatformConfiguration platformConfiguration = getPlatformConfiguration(targetPlatform);
-        ToolChainAvailability result = new ToolChainAvailability();
-        if (platformConfiguration == null) {
-            result.unavailable(String.format("Don't know how to build for platform '%s'.", targetPlatform.getName()));
-            return new UnavailablePlatformToolChain(result);
-        }
-        initTools(result);
-        if (!result.isAvailable()) {
-            return new UnavailablePlatformToolChain(result);
-        }
-
-        // Target the tools for the platform
-        ToolRegistry platformTools = new PlatformToolRegistry(toolRegistry, platformConfiguration);
-        return new GccPlatformToolChain(toolSearchPath, platformTools, execActionFactory, canUseCommandFile());
-    }
-
-    protected TargetPlatformConfiguration getPlatformConfiguration(Platform targetPlatform) {
-        for (TargetPlatformConfiguration platformConfig : platformConfigs) {
-            if (platformConfig.supportsPlatform(targetPlatform)) {
-                return platformConfig;
-            }
-        }
-        return null;
-    }
-
-    protected boolean canUseCommandFile() {
-        return true;
-    }
-
-    public GccTool getCppCompiler() {
-        return getTool(ToolType.CPP_COMPILER);
-    }
-
-    public GccTool getCCompiler() {
-        return getTool(ToolType.C_COMPILER);
-    }
-
-    // This is here to allow using this property from Groovy as `cCompiler`
-    public GccTool getcCompiler() {
-        return getTool(ToolType.C_COMPILER);
-    }
-
-    public GccTool getAssembler() {
-        return getTool(ToolType.ASSEMBLER);
-    }
-
-    public GccTool getLinker() {
-        return getTool(ToolType.LINKER);
-    }
-
-    public GccTool getStaticLibArchiver() {
-        return getTool(ToolType.STATIC_LIB_ARCHIVER);
-    }
-    private static class ToolChainDefaultArchitecture implements TargetPlatformConfiguration {
-        public boolean supportsPlatform(Platform targetPlatform) {
-            return targetPlatform.getOperatingSystem().isCurrent()
-                && targetPlatform.getArchitecture() == ArchitectureInternal.TOOL_CHAIN_DEFAULT;
-        }
-
-        public List<String> getAssemblerArgs() {
-            return emptyList();
-        }
-
-        public List<String> getCppCompilerArgs() {
-            return emptyList();
-        }
-
-        public List<String> getCCompilerArgs() {
-            return emptyList();
-        }
-
-        public List<String> getObjectiveCppCompilerArgs() {
-            return emptyList();
-        }
-
-        public List<String> getObjectiveCCompilerArgs() {
-            return emptyList();
-        }
-
-        public List<String> getStaticLibraryArchiverArgs() {
-            return emptyList();
-        }
-
-        public List<String> getLinkerArgs() {
-            return emptyList();
-        }
-    }
-
-    private static class Intel32Architecture implements TargetPlatformConfiguration {
-        public boolean supportsPlatform(Platform targetPlatform) {
-            return targetPlatform.getOperatingSystem().isCurrent()
-                    && ((ArchitectureInternal) targetPlatform.getArchitecture()).isI386();
-        }
-
-        public List<String> getCppCompilerArgs() {
-            return asList("-m32");
-        }
-
-        public List<String> getCCompilerArgs() {
-            return asList("-m32");
-        }
-
-        public List<String> getObjectiveCppCompilerArgs() {
-            return asList("-m32");
-        }
-
-        public List<String> getObjectiveCCompilerArgs() {
-            return asList("-m32");
-        }
-
-        public List<String> getAssemblerArgs() {
-            if (OperatingSystem.current().isMacOsX()) {
-                return asList("-arch", "i386");
-            } else {
-                return asList("--32");
-            }
-        }
-
-        public List<String> getLinkerArgs() {
-            return asList("-m32");
-        }
-
-        public List<String> getStaticLibraryArchiverArgs() {
-            return emptyList();
-        }
-    }
-
-    private static class Intel64Architecture implements TargetPlatformConfiguration {
-        public boolean supportsPlatform(Platform targetPlatform) {
-            return targetPlatform.getOperatingSystem().isCurrent()
-                    && !OperatingSystem.current().isWindows() // Currently don't support building 64-bit binaries on GCC/Windows
-                    && ((ArchitectureInternal) targetPlatform.getArchitecture()).isAmd64();
-        }
-        public List<String> getCppCompilerArgs() {
-            return asList("-m64");
-        }
-
-        public List<String> getCCompilerArgs() {
-            return asList("-m64");
-        }
-
-        public List<String> getObjectiveCppCompilerArgs() {
-            return asList("-m64");
-        }
-
-        public List<String> getObjectiveCCompilerArgs() {
-            return asList("-m64");
-        }
-
-        public List<String> getAssemblerArgs() {
-            if (OperatingSystem.current().isMacOsX()) {
-                return asList("-arch", "x86_64");
-            } else {
-                return asList("--64");
-            }
-        }
-
-        public List<String> getLinkerArgs() {
-            return asList("-m64");
-        }
-
-        public List<String> getStaticLibraryArchiverArgs() {
-            return emptyList();
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ArStaticLibraryArchiver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ArStaticLibraryArchiver.java
deleted file mode 100755
index b867e0d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ArStaticLibraryArchiver.java
+++ /dev/null
@@ -1,73 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.nativebinaries.internal.StaticLibraryArchiverSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A static library archiver based on the GNU 'ar' utility
- */
-class ArStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
-    private final CommandLineTool<StaticLibraryArchiverSpec> commandLineTool;
-
-    public ArStaticLibraryArchiver(CommandLineTool<StaticLibraryArchiverSpec> commandLineTool, Action<List<String>> argsAction) {
-        ArgsTransformer<StaticLibraryArchiverSpec> arguments = new ArchiverSpecToArguments();
-        arguments = new PostTransformActionArgsTransformer<StaticLibraryArchiverSpec>(arguments, argsAction);
-        this.commandLineTool = commandLineTool.withArguments(arguments);
-    }
-
-    public WorkResult execute(StaticLibraryArchiverSpec spec) {
-        deletePreviousOutput(spec);
-        return commandLineTool.execute(spec);
-    }
-
-    private void deletePreviousOutput(StaticLibraryArchiverSpec spec) {
-        // Need to delete the previous archive, otherwise stale object files will remain
-        if (!spec.getOutputFile().isFile()) {
-            return;
-        }
-        if (!(spec.getOutputFile().delete())) {
-            throw new GradleException("Create static archive failed: could not delete previous archive");
-        }
-    }
-
-    private static class ArchiverSpecToArguments implements ArgsTransformer<StaticLibraryArchiverSpec> {
-        public List<String> transform(StaticLibraryArchiverSpec spec) {
-            List<String> args = new ArrayList<String>();
-            // -r : Add files to static archive, creating if required
-            // -c : Don't write message to standard error when creating archive
-            // -s : Create an object file index (equivalent to running 'ranlib')
-            args.add("-rcs");
-            args.addAll(spec.getAllArgs());
-            args.add(spec.getOutputFile().getAbsolutePath());
-            for (File file : spec.getObjectFiles()) {
-                args.add(file.getAbsolutePath());
-            }
-            return args;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/Assembler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/Assembler.java
deleted file mode 100755
index 8799dbb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/Assembler.java
+++ /dev/null
@@ -1,80 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.Action;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-class Assembler implements Compiler<AssembleSpec> {
-
-    private final CommandLineTool<AssembleSpec> commandLineTool;
-    private final Action<List<String>> argsAction;
-    private String outputFileSuffix;
-
-    public Assembler(CommandLineTool<AssembleSpec> commandLineTool, Action<List<String>> argsAction, String outputFileSuffix) {
-        this.commandLineTool = commandLineTool;
-        this.argsAction = argsAction;
-        this.outputFileSuffix = outputFileSuffix;
-    }
-
-    public WorkResult execute(AssembleSpec spec) {
-        boolean didWork = false;
-        CommandLineTool<AssembleSpec> commandLineAssembler = commandLineTool.inWorkDirectory(spec.getObjectFileDir());
-        for (File sourceFile : spec.getSourceFiles()) {
-            ArgsTransformer<AssembleSpec> arguments = new AssembleSpecToArgsList(sourceFile, spec.getObjectFileDir(), outputFileSuffix);
-            arguments = new PostTransformActionArgsTransformer<AssembleSpec>(arguments, argsAction);
-            WorkResult result = commandLineAssembler.withArguments(arguments).execute(spec);
-            didWork = didWork || result.getDidWork();
-        }
-        return new SimpleWorkResult(didWork);
-    }
-
-    private static class AssembleSpecToArgsList implements ArgsTransformer<AssembleSpec> {
-        private final File inputFile;
-        private final File outputFile;
-
-        public AssembleSpecToArgsList(File inputFile, File objectFileRootDir, String outputFileSuffix) {
-            this.inputFile = inputFile;
-            String outputFileName = FilenameUtils.removeExtension(inputFile.getName())+ outputFileSuffix;
-            File currentObjectOutputDir = new File(objectFileRootDir, HashUtil.createCompactMD5(inputFile.getAbsolutePath()));
-            this.outputFile = new File(currentObjectOutputDir, outputFileName);
-        }
-
-        public List<String> transform(AssembleSpec spec) {
-            List<String> args = new ArrayList<String>();
-            args.addAll(spec.getAllArgs());
-            if(!outputFile.getParentFile().exists()){
-                outputFile.getParentFile().mkdir();
-            }
-            Collections.addAll(args, "-o", outputFile.getAbsolutePath());
-            args.add(inputFile.getAbsolutePath());
-            return args;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompiler.java
deleted file mode 100755
index 1c5fce8..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompiler.java
+++ /dev/null
@@ -1,36 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.util.List;
-
-class CCompiler extends NativeCompiler<CCompileSpec> {
-
-    public CCompiler(CommandLineTool<CCompileSpec> commandLineTool, Action<List<String>> toolChainArgsAction, boolean useCommandFile) {
-        super(commandLineTool, toolChainArgsAction, new CCompileArgsTransformer(), useCommandFile);
-    }
-
-    private static class CCompileArgsTransformer extends GccCompilerArgsTransformer<CCompileSpec> {
-        protected String getLanguage() {
-            return "c";
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CommandLineToolSearchResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CommandLineToolSearchResult.java
deleted file mode 100644
index 694b96f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CommandLineToolSearchResult.java
+++ /dev/null
@@ -1,25 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult;
-
-import java.io.File;
-
-public interface CommandLineToolSearchResult extends ToolSearchResult {
-    File getTool();
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CppCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CppCompiler.java
deleted file mode 100755
index 5ff0885..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CppCompiler.java
+++ /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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.util.List;
-
-public class CppCompiler extends NativeCompiler<CppCompileSpec> {
-
-    public CppCompiler(CommandLineTool<CppCompileSpec> commandLineTool, Action<List<String>> toolChainArgsAction, boolean useCommandFile) {
-        super(commandLineTool, toolChainArgsAction, new CppCompileArgsTransformer(), useCommandFile);
-
-    }
-
-    private static class CppCompileArgsTransformer extends GccCompilerArgsTransformer<CppCompileSpec> {
-        protected String getLanguage() {
-            return "c++";
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccCompilerArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccCompilerArgsTransformer.java
deleted file mode 100644
index 4fac79d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccCompilerArgsTransformer.java
+++ /dev/null
@@ -1,58 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.MacroArgsConverter;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Maps common options for C/C++ compiling with GCC
- */
-abstract class GccCompilerArgsTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T> {
-    public List<String> transform(T spec) {
-        List<String> args = new ArrayList<String>();
-        Collections.addAll(args, "-x", getLanguage());
-
-        for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
-            args.add("-D" + macroArg);
-        }
-
-        args.addAll(spec.getAllArgs());
-        args.add("-c");
-        if (spec.isPositionIndependentCode()) {
-            if (!OperatingSystem.current().isWindows()) {
-                args.add("-fPIC");
-            }
-        }
-
-        for (File file : spec.getIncludeRoots()) {
-            args.add("-I");
-            args.add(file.getAbsolutePath());
-        }
-        return args;
-    }
-
-    protected abstract String getLanguage();
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinker.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinker.java
deleted file mode 100755
index 7c84bed..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinker.java
+++ /dev/null
@@ -1,94 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.SharedLibraryLinkerSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-class GccLinker implements Compiler<LinkerSpec> {
-
-    private final CommandLineTool<LinkerSpec> commandLineTool;
-
-    public GccLinker(CommandLineTool<LinkerSpec> commandLineTool, Action<List<String>> argsAction, boolean useCommandFile) {
-        ArgsTransformer<LinkerSpec> argsTransformer = new GccLinkerArgsTransformer();
-        argsTransformer = new PostTransformActionArgsTransformer<LinkerSpec>(argsTransformer, argsAction);
-        if (useCommandFile) {
-            argsTransformer = new GccOptionsFileArgTransformer<LinkerSpec>(argsTransformer);
-        }
-        this.commandLineTool = commandLineTool.withArguments(argsTransformer);
-    }
-
-    public WorkResult execute(LinkerSpec spec) {
-        return commandLineTool.execute(spec);
-    }
-
-    private static class GccLinkerArgsTransformer implements ArgsTransformer<LinkerSpec> {
-        public List<String> transform(LinkerSpec spec) {
-            List<String> args = new ArrayList<String>();
-            
-            args.addAll(spec.getSystemArgs());
-
-            if (spec instanceof SharedLibraryLinkerSpec) {
-                args.add("-shared");
-                maybeSetInstallName((SharedLibraryLinkerSpec) spec, args);
-            }
-            args.add("-o");
-            args.add(spec.getOutputFile().getAbsolutePath());
-            for (File file : spec.getObjectFiles()) {
-                args.add(file.getAbsolutePath());
-            }
-            for (File file : spec.getLibraries()) {
-                args.add(file.getAbsolutePath());
-            }
-            for (File pathEntry : spec.getLibraryPath()) {
-                // TODO:DAZ It's not clear to me what the correct meaning of this should be for GCC
-//                args.add("-L" + pathEntry.getAbsolutePath());
-//                args.add("-Wl,-L" + pathEntry.getAbsolutePath());
-//                args.add("-Wl,-rpath," + pathEntry.getAbsolutePath());
-                throw new UnsupportedOperationException("Library Path not yet supported on GCC");
-            }
-
-            for (String userArg : spec.getArgs()) {
-                args.add(userArg);
-            }
-
-            return args;
-        }
-
-        private void maybeSetInstallName(SharedLibraryLinkerSpec spec, List<String> args) {
-            String installName = spec.getInstallName();
-            if (installName == null || OperatingSystem.current().isWindows()) {
-                return;
-            }
-            if (OperatingSystem.current().isMacOsX()) {
-                args.add("-Wl,-install_name," + installName);
-            } else {
-                args.add("-Wl,-soname," + installName);
-            }
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccOptionsFileArgTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccOptionsFileArgTransformer.java
deleted file mode 100644
index 6065ab6..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccOptionsFileArgTransformer.java
+++ /dev/null
@@ -1,52 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.internal.tasks.compile.ArgWriter;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.OptionsFileArgsTransformer;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Uses an option file for arguments passed to GCC if possible.
- * Certain GCC options do not function correctly when included in an option file, so include these directly on the command line as well.
- */
-class GccOptionsFileArgTransformer<T extends BinaryToolSpec> extends OptionsFileArgsTransformer<T> {
-    private static final List<String> CLI_ONLY_ARGS = Arrays.asList("-m32", "-m64");
-
-    public GccOptionsFileArgTransformer(ArgsTransformer<T> delegate) {
-        super(ArgWriter.unixStyleFactory(), delegate);
-    }
-
-    @Override
-    protected void transformArgs(List<String> input, List<String> output, File tempDir) {
-        List<String> commandLineOnlyArgs = getCommandLineOnlyArgs(input);
-        output.addAll(commandLineOnlyArgs);
-        super.transformArgs(input, output, tempDir);
-    }
-
-    private List<String> getCommandLineOnlyArgs(List<String> allArgs) {
-        List<String> commandLineOnlyArgs = new ArrayList<String>(allArgs);
-        commandLineOnlyArgs.retainAll(CLI_ONLY_ARGS);
-        return commandLineOnlyArgs;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccPlatformToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccPlatformToolChain.java
deleted file mode 100644
index 82be2d0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccPlatformToolChain.java
+++ /dev/null
@@ -1,114 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.StaticLibraryArchiverSpec;
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec;
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec;
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec;
-import org.gradle.nativebinaries.language.objectivec.internal.ObjectiveCCompileSpec;
-import org.gradle.nativebinaries.language.objectivecpp.internal.ObjectiveCppCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-import org.gradle.nativebinaries.toolchain.internal.OutputCleaningCompiler;
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-import org.gradle.nativebinaries.toolchain.internal.tools.ToolRegistry;
-import org.gradle.nativebinaries.toolchain.internal.tools.ToolSearchPath;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.util.TreeVisitor;
-
-class GccPlatformToolChain implements PlatformToolChain {
-    private final ToolSearchPath toolSearchPath;
-    private final ToolRegistry toolRegistry;
-    private final ExecActionFactory execActionFactory;
-    private final boolean useCommandFile;
-
-    GccPlatformToolChain(ToolSearchPath toolSearchPath, ToolRegistry toolRegistry, ExecActionFactory execActionFactory, boolean useCommandFile) {
-        this.toolRegistry = toolRegistry;
-        this.toolSearchPath = toolSearchPath;
-        this.execActionFactory = execActionFactory;
-        this.useCommandFile = useCommandFile;
-    }
-
-    public boolean isAvailable() {
-        return true;
-    }
-
-    public void explain(TreeVisitor<? super String> visitor) {
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
-        CommandLineTool<CppCompileSpec> commandLineTool = commandLineTool(ToolType.CPP_COMPILER);
-        CppCompiler cppCompiler = new CppCompiler(commandLineTool, toolRegistry.getTool(ToolType.CPP_COMPILER).getArgAction(), useCommandFile);
-        return (Compiler<T>) new OutputCleaningCompiler<CppCompileSpec>(cppCompiler, getOutputFileSuffix());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
-        CommandLineTool<CCompileSpec> commandLineTool = commandLineTool(ToolType.C_COMPILER);
-        CCompiler cCompiler = new CCompiler(commandLineTool, toolRegistry.getTool(ToolType.C_COMPILER).getArgAction(), useCommandFile);
-        return (Compiler<T>) new OutputCleaningCompiler<CCompileSpec>(cCompiler, getOutputFileSuffix());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createObjectiveCppCompiler() {
-        CommandLineTool<ObjectiveCppCompileSpec> commandLineTool = commandLineTool(ToolType.OBJECTIVECPP_COMPILER);
-        ObjectiveCppCompiler objectiveCppCompiler = new ObjectiveCppCompiler(commandLineTool, toolRegistry.getTool(ToolType.OBJECTIVECPP_COMPILER).getArgAction(), useCommandFile);
-        return (Compiler<T>) new OutputCleaningCompiler<ObjectiveCppCompileSpec>(objectiveCppCompiler, getOutputFileSuffix());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createObjectiveCCompiler() {
-        CommandLineTool<ObjectiveCCompileSpec> commandLineTool = commandLineTool(ToolType.OBJECTIVEC_COMPILER);
-        ObjectiveCCompiler objectiveCCompiler = new ObjectiveCCompiler(commandLineTool, toolRegistry.getTool(ToolType.OBJECTIVEC_COMPILER).getArgAction(), useCommandFile);
-        return (Compiler<T>) new OutputCleaningCompiler<ObjectiveCCompileSpec>(objectiveCCompiler, getOutputFileSuffix());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
-        CommandLineTool<AssembleSpec> commandLineTool = commandLineTool(ToolType.ASSEMBLER);
-        return (Compiler<T>) new Assembler(commandLineTool, toolRegistry.getTool(ToolType.ASSEMBLER).getArgAction(), getOutputFileSuffix());
-    }
-
-    public <T extends BinaryToolSpec> Compiler<T> createWindowsResourceCompiler() {
-        throw new UnsupportedOperationException();
-    }
-
-    public <T extends LinkerSpec> Compiler<T> createLinker() {
-        CommandLineTool<LinkerSpec> commandLineTool = commandLineTool(ToolType.LINKER);
-        return (Compiler<T>) new GccLinker(commandLineTool, toolRegistry.getTool(ToolType.LINKER).getArgAction(), useCommandFile);
-    }
-
-    public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
-        CommandLineTool<StaticLibraryArchiverSpec> commandLineTool = commandLineTool(ToolType.STATIC_LIB_ARCHIVER);
-            return (Compiler<T>) new ArStaticLibraryArchiver(commandLineTool, toolRegistry.getTool(ToolType.STATIC_LIB_ARCHIVER).getArgAction());
-    }
-
-
-    private String getOutputFileSuffix() {
-        return OperatingSystem.current().isWindows() ? ".obj" : ".o";
-    }
-
-    private <T extends BinaryToolSpec> CommandLineTool<T> commandLineTool(ToolType key) {
-        String exeName = toolRegistry.getTool(key).getExecutable();
-        CommandLineTool<T> commandLineTool = new CommandLineTool<T>(key.getToolName(), toolSearchPath.locate(key, exeName).getTool(), execActionFactory);
-        // MinGW requires the path to be set
-        commandLineTool.withPath(toolSearchPath.getPath());
-        commandLineTool.withEnvironmentVar("CYGWIN", "nodosfilewarning");
-        return commandLineTool;
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChain.java
deleted file mode 100755
index bc8f4ba..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChain.java
+++ /dev/null
@@ -1,90 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.toolchain.Gcc;
-import org.gradle.nativebinaries.toolchain.internal.ToolChainAvailability;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-import org.gradle.nativebinaries.toolchain.internal.gcc.version.GccVersionDeterminer;
-import org.gradle.nativebinaries.toolchain.internal.gcc.version.GccVersionResult;
-import org.gradle.process.internal.ExecActionFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * Compiler adapter for GCC.
- */
-public class GccToolChain extends AbstractGccCompatibleToolChain implements Gcc {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(GccToolChain.class);
-
-    public static final String DEFAULT_NAME = "gcc";
-
-    private final Transformer<GccVersionResult, File> versionDeterminer;
-
-    private GccVersionResult versionResult;
-
-    public GccToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory) {
-        super(name, operatingSystem, fileResolver, execActionFactory, new GccToolSearchPath(operatingSystem));
-        this.versionDeterminer = new GccVersionDeterminer(execActionFactory);
-
-        registerTool(ToolType.CPP_COMPILER, "g++");
-        registerTool(ToolType.C_COMPILER, "gcc");
-        registerTool(ToolType.OBJECTIVECPP_COMPILER, "g++");
-        registerTool(ToolType.OBJECTIVEC_COMPILER, "gcc");
-        registerTool(ToolType.ASSEMBLER, "as");
-        registerTool(ToolType.LINKER, "g++");
-        registerTool(ToolType.STATIC_LIB_ARCHIVER, "ar");
-    }
-
-    @Override
-    protected String getTypeName() {
-        return "GNU GCC";
-    }
-
-    @Override
-    protected void initTools(ToolChainAvailability availability) {
-        if (versionResult == null) {
-            CommandLineToolSearchResult compiler = locate(ToolType.C_COMPILER);
-            if (!compiler.isAvailable()) {
-                compiler = locate(ToolType.CPP_COMPILER);
-            }
-            availability.mustBeAvailable(compiler);
-            if (!compiler.isAvailable()) {
-                return;
-            }
-            versionResult = versionDeterminer.transform(compiler.getTool());
-            LOGGER.debug("Found {} with version {}", ToolType.C_COMPILER.getToolName(), versionResult);
-        }
-        availability.mustBeAvailable(versionResult);
-    }
-
-    protected boolean canUseCommandFile() {
-        String[] components = versionResult.getVersion().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.", versionResult), e);
-        }
-        return majorVersion >= 4;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolSearchPath.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolSearchPath.java
deleted file mode 100644
index 91e506e..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolSearchPath.java
+++ /dev/null
@@ -1,49 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.toolchain.internal.tools.ToolSearchPath;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-
-class GccToolSearchPath extends ToolSearchPath {
-    public GccToolSearchPath(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/nativebinaries/toolchain/internal/gcc/NativeCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/NativeCompiler.java
deleted file mode 100644
index 56f7c8d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/NativeCompiler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal.gcc;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.Action;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.SingleSourceCompileArgTransformer;
-
-import java.io.File;
-import java.util.List;
-
-abstract public class NativeCompiler<T extends NativeCompileSpec> implements Compiler<T> {
-
-    private final CommandLineTool<T> commandLineTool;
-    private final ArgsTransformer<T> argsTransfomer;
-
-    public NativeCompiler(CommandLineTool<T> commandLineTool, Action<List<String>> toolChainArgsAction, ArgsTransformer<T> argsTransformer, boolean useCommandFile) {
-        argsTransformer = new PostTransformActionArgsTransformer<T>(argsTransformer, toolChainArgsAction);
-        if (useCommandFile) {
-            argsTransformer = new GccOptionsFileArgTransformer<T>(argsTransformer);
-        }
-        this.argsTransfomer = argsTransformer;
-        this.commandLineTool = commandLineTool;
-    }
-
-    public WorkResult execute(T spec) {
-        boolean didWork = false;
-        boolean windowsPathLimitation = OperatingSystem.current().isWindows();
-
-        String objectFileExtension = OperatingSystem.current().isWindows() ? ".obj" : ".o";
-        for (File sourceFile : spec.getSourceFiles()) {
-            String objectFileName = FilenameUtils.removeExtension(sourceFile.getName()) + objectFileExtension;
-            WorkResult result = commandLineTool.inWorkDirectory(spec.getObjectFileDir())
-                    .withArguments(new SingleSourceCompileArgTransformer<T>(sourceFile,
-                                            objectFileName,
-                                            new ShortCircuitArgsTransformer(argsTransfomer),
-                                            windowsPathLimitation,
-                                            false))
-                    .execute(spec);
-            didWork = didWork || result.getDidWork();
-        }
-        return new SimpleWorkResult(didWork);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCCompiler.java
deleted file mode 100644
index f13affe..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCCompiler.java
+++ /dev/null
@@ -1,38 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.language.objectivec.internal.ObjectiveCCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.util.List;
-
-public class ObjectiveCCompiler extends NativeCompiler<ObjectiveCCompileSpec> {
-
-    public ObjectiveCCompiler(CommandLineTool<ObjectiveCCompileSpec> commandLineTool, Action<List<String>> toolChainArgsAction, boolean useCommandFile) {
-        super(commandLineTool, toolChainArgsAction, new ObjectiveCCompileArgsTransformer(), useCommandFile);
-    }
-
-    private static class ObjectiveCCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCCompileSpec> {
-        protected String getLanguage() {
-            return "objective-c";
-        }
-    }
-
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCppCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCppCompiler.java
deleted file mode 100644
index eaba4dd..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ObjectiveCppCompiler.java
+++ /dev/null
@@ -1,37 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.language.objectivecpp.internal.ObjectiveCppCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.util.List;
-
-public class ObjectiveCppCompiler extends NativeCompiler<ObjectiveCppCompileSpec> {
-
-    public ObjectiveCppCompiler(CommandLineTool<ObjectiveCppCompileSpec> commandLineTool, Action<List<String>> toolChainArgsAction, boolean useCommandFile) {
-        super(commandLineTool, toolChainArgsAction, new ObjectiveCppCompileArgsTransformer(), useCommandFile);
-    }
-
-    private static class ObjectiveCppCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCppCompileSpec> {
-        protected String getLanguage() {
-            return "objective-c++";
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/PostTransformActionArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/PostTransformActionArgsTransformer.java
deleted file mode 100644
index 0e62a78..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/PostTransformActionArgsTransformer.java
+++ /dev/null
@@ -1,39 +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.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class PostTransformActionArgsTransformer<T extends BinaryToolSpec> implements ArgsTransformer<T> {
-    private final ArgsTransformer<T> delegate;
-    private final Action<List<String>> userTransformer;
-
-    PostTransformActionArgsTransformer(ArgsTransformer<T> delegate, Action<List<String>> userTransformer) {
-        this.delegate = delegate;
-        this.userTransformer = userTransformer;
-    }
-
-    public List<String> transform(T spec) {
-        List<String> args = new ArrayList<String>(delegate.transform(spec));
-        userTransformer.execute(args);
-        return args;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformer.java
deleted file mode 100644
index 41c14a3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal.gcc;
-
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.util.List;
-
-public class ShortCircuitArgsTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T>  {
-    private ArgsTransformer<T> delegate;
-    private List<String> cachedArguments;
-    private T cachedSpec;
-
-    public ShortCircuitArgsTransformer(ArgsTransformer<T> delegate) {
-        this.delegate = delegate;
-    }
-
-    public List<String> transform(T spec) {
-        if(spec.equals(cachedSpec)){
-            return cachedArguments;
-        }
-        cachedArguments = delegate.transform(spec);
-        this.cachedSpec = spec;
-        return cachedArguments;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminer.java
deleted file mode 100644
index b1bebbb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminer.java
+++ /dev/null
@@ -1,142 +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.nativebinaries.toolchain.internal.gcc.version;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.process.ExecResult;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.util.TreeVisitor;
-
-import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
-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 GccVersionDeterminer implements Transformer<GccVersionResult, File> {
-    private static final Pattern DEFINE_PATTERN = Pattern.compile("\\s*#define\\s+(\\S+)\\s+(.*)");
-
-    private final Transformer<String, File> outputProducer;
-
-    public GccVersionDeterminer(ExecActionFactory execActionFactory) {
-        this.outputProducer = new GccVersionOutputProducer(execActionFactory);
-    }
-
-    public GccVersionResult transform(File gccBinary) {
-        String output = outputProducer.transform(gccBinary);
-        if (output == null) {
-            return new BrokenResult(String.format("Could not determine GCC version: failed to execute %s -v.", gccBinary.getName()));
-        }
-        return transform(output, gccBinary);
-    }
-
-    private GccVersionResult transform(String output, File gccBinary) {
-        BufferedReader reader = new BufferedReader(new StringReader(output));
-        String line;
-        Map<String, String> defines = new HashMap<String, String>();
-        try {
-            while ((line = reader.readLine()) != null) {
-                Matcher matcher = DEFINE_PATTERN.matcher(line);
-                if (!matcher.matches()) {
-                    return new BrokenResult(String.format("Could not determine GCC version: %s produced unexpected output.", gccBinary.getName()));
-                }
-                defines.put(matcher.group(1), matcher.group(2));
-            }
-        } catch (IOException e) {
-            // Should not happen reading from a StringReader
-            throw new UncheckedIOException(e);
-        }
-        if (!defines.containsKey("__GNUC__")) {
-            return new BrokenResult(String.format("Could not determine GCC version: %s produced unexpected output.", gccBinary.getName()));
-        }
-        if (defines.containsKey("__clang__")) {
-            return new BrokenResult(String.format("XCode %s is a wrapper around Clang. Treating it as Clang and not GCC.", gccBinary.getName()));
-        }
-        return new DefaultGccVersionResult(defines.get("__GNUC__"));
-    }
-
-    private static class DefaultGccVersionResult implements GccVersionResult {
-        private final String scrapedVersion;
-
-        public DefaultGccVersionResult(String scrapedVersion) {
-            this.scrapedVersion = scrapedVersion;
-        }
-
-        public String getVersion() {
-            return scrapedVersion;
-        }
-
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-        }
-    }
-
-    static class GccVersionOutputProducer implements Transformer<String, File> {
-        
-        private final ExecActionFactory execActionFactory;
-
-        GccVersionOutputProducer(ExecActionFactory execActionFactory) {
-            this.execActionFactory = execActionFactory;
-        }
-
-        public String transform(File gccBinary) {
-            ExecAction exec = execActionFactory.newExecAction();
-            exec.executable(gccBinary.getAbsolutePath());
-            exec.setWorkingDir(gccBinary.getParentFile());
-            exec.args("-dM", "-E", "-");
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            exec.setStandardOutput(baos);
-            exec.setIgnoreExitValue(true);
-            ExecResult result = exec.execute();
-
-            int exitValue = result.getExitValue();
-            if (exitValue == 0) {
-                return new String(baos.toByteArray());
-            } else {
-                return null;
-            }
-        }
-    }
-
-    private static class BrokenResult implements GccVersionResult {
-        private final String message;
-
-        private BrokenResult(String message) {
-            this.message = message;
-        }
-
-        public String getVersion() {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            visitor.node(message);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionResult.java
deleted file mode 100644
index e771c19..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionResult.java
+++ /dev/null
@@ -1,23 +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.nativebinaries.toolchain.internal.gcc.version;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult;
-
-public interface GccVersionResult extends ToolSearchResult {
-    String getVersion();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/Assembler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/Assembler.java
deleted file mode 100755
index b9587d9..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/Assembler.java
+++ /dev/null
@@ -1,80 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.nativebinaries.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
-
-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.getSourceFiles()) {
-            WorkResult result = commandLineAssembler.withArguments(new AssemblerArgsTransformer(sourceFile)).execute(spec);
-            didWork = didWork || result.getDidWork();
-        }
-        return new SimpleWorkResult(didWork);
-    }
-
-
-    private static class AssemblerArgsTransformer implements ArgsTransformer<AssembleSpec> {
-        private final File inputFile;
-
-        public AssemblerArgsTransformer(File inputFile) {
-            this.inputFile = inputFile;
-        }
-
-        public List<String> transform(AssembleSpec spec) {
-            List<String> args = new ArrayList<String>();
-            args.addAll(escapeUserArgs(spec.getAllArgs()));
-            args.add("/nologo");
-            args.add("/c");
-            File outputFile = getOutputFilePath(spec);
-            if(!outputFile.getParentFile().exists()){
-                outputFile.getParentFile().mkdir();
-            }
-            args.add("/Fo"+ outputFile);
-            args.add(inputFile.getAbsolutePath());
-            return args;
-        }
-
-        public File getOutputFilePath(AssembleSpec spec) {
-            String compactMD5 = HashUtil.createCompactMD5(inputFile.getAbsolutePath());
-            File currentObjectOutputDir = new File(spec.getObjectFileDir(), compactMD5);
-            return new File(currentObjectOutputDir, FilenameUtils.removeExtension(inputFile.getName())+ ".obj");
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CCompiler.java
deleted file mode 100755
index 7120bad..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CCompiler.java
+++ /dev/null
@@ -1,33 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-class CCompiler extends NativeCompiler<CCompileSpec> {
-
-    CCompiler(CommandLineTool<CCompileSpec> commandLineTool) {
-        super(commandLineTool, new CCompilerArgsTransformer());
-    }
-
-    private static class CCompilerArgsTransformer extends VisualCppCompilerArgsTransformer<CCompileSpec> {
-        protected String getLanguageOption() {
-            return "/TC";
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CppCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CppCompiler.java
deleted file mode 100755
index 892c485..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/CppCompiler.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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-class CppCompiler extends NativeCompiler<CppCompileSpec> {
-
-    CppCompiler(CommandLineTool<CppCompileSpec> commandLineTool) {
-        super(commandLineTool, new CppCompilerArgsTransformer());
-    }
-
-    private static class CppCompilerArgsTransformer extends VisualCppCompilerArgsTransformer<CppCompileSpec> {
-        protected String getLanguageOption() {
-            return "/TP";
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java
deleted file mode 100644
index 0e088f5..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java
+++ /dev/null
@@ -1,369 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import net.rubygrapefruit.platform.MissingRegistryEntryException;
-import net.rubygrapefruit.platform.SystemInfo;
-import net.rubygrapefruit.platform.WindowsRegistry;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.nativebinaries.platform.Architecture;
-import org.gradle.nativebinaries.platform.internal.ArchitectureNotationParser;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.TreeVisitor;
-import org.gradle.util.VersionNumber;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.*;
-
-public class DefaultVisualStudioLocator implements VisualStudioLocator {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultVisualStudioLocator.class);
-    private static final String[] REGISTRY_BASEPATHS = {
-        "SOFTWARE\\",
-        "SOFTWARE\\Wow6432Node\\"
-    };
-    private static final String REGISTRY_ROOTPATH_VC = "Microsoft\\VisualStudio\\SxS\\VC7";
-    private static final String PATH_COMMON = "Common7/";
-    private static final String PATH_COMMONTOOLS = PATH_COMMON + "Tools/";
-    private static final String PATH_COMMONIDE = PATH_COMMON + "IDE/";
-    private static final String PATH_BIN = "bin/";
-    private static final String PATH_INCLUDE = "include/";
-    private static final String COMPILER_FILENAME = "cl.exe";
-
-    private static final String ARCHITECTURE_AMD64 = "amd64";
-    private static final String ARCHITECTURE_X86 = "x86";
-    private static final String ARCHITECTURE_ARM = "arm";
-    private static final String ARCHITECTURE_IA64 = "ia-64";
-    private static final String BINPATH_AMD64_AMD64 = "bin/amd64";
-    private static final String BINPATH_AMD64_ARM = "bin/amd64_arm";
-    private static final String BINPATH_AMD64_X86 = "bin/amd64_x86";
-    private static final String BINPATH_X86_AMD64 = "bin/x86_amd64";
-    private static final String BINPATH_X86_ARM = "bin/x86_arm";
-    private static final String BINPATH_X86_IA64 = "bin/x86_ia64";
-    private static final String BINPATH_X86_X86 = "bin";
-    private static final String LIBPATH_AMD64 = "lib/amd64";
-    private static final String LIBPATH_ARM = "lib/arm";
-    private static final String LIBPATH_IA64 = "lib/ia64";
-    private static final String LIBPATH_X86 = "lib";
-    private static final String ASSEMBLER_FILENAME_AMD64 = "ml64.exe";
-    private static final String ASSEMBLER_FILENAME_ARM = "armasm.exe";
-    private static final String ASSEMBLER_FILENAME_IA64 = "ias.exe";
-    private static final String ASSEMBLER_FILENAME_X86 = "ml.exe";
-    private static final String DEFINE_ARMPARTITIONAVAILABLE = "_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE";
-
-    private final Map<File, VisualStudioInstall> foundInstalls = new HashMap<File, VisualStudioInstall>();
-    private final OperatingSystem os;
-    private final WindowsRegistry windowsRegistry;
-    private final SystemInfo systemInfo;
-    private VisualStudioInstall pathInstall;
-    private boolean initialised;
-
-    public DefaultVisualStudioLocator(OperatingSystem os, WindowsRegistry windowsRegistry, SystemInfo systemInfo) {
-        this.os = os;
-        this.windowsRegistry = windowsRegistry;
-        this.systemInfo = systemInfo;
-    }
-
-    public SearchResult locateVisualStudioInstalls(File candidate) {
-        if (!initialised) {
-            locateInstallsInRegistry();
-            locateInstallInPath();
-            initialised = true;
-        }
-
-        if (candidate != null) {
-            return locateUserSpecifiedInstall(candidate);
-        }
-
-        return determineDefaultInstall();
-    }
-
-    private void locateInstallsInRegistry() {
-        for (String baseKey : REGISTRY_BASEPATHS) {
-            locateInstallsInRegistry(baseKey);
-        }
-    }
-
-    private void locateInstallsInRegistry(String baseKey) {
-        List<String> visualCppVersions;
-        try {
-            visualCppVersions = windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_VC);
-        } catch (MissingRegistryEntryException e) {
-            // No Visual Studio information available in the registry
-            return;
-        }
-
-        for (String valueName : visualCppVersions) {
-            if (!valueName.matches("\\d+\\.\\d+")) {
-                // Ignore the other values
-                continue;
-            }
-            File visualCppDir = new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_VC, valueName));
-            visualCppDir = GFileUtils.canonicalise(visualCppDir);
-            File visualStudioDir = visualCppDir.getParentFile();
-
-            if (isVisualCpp(visualCppDir) && isVisualStudio(visualStudioDir)) {
-                LOGGER.debug("Found Visual C++ {} at {}", valueName, visualCppDir);
-                VersionNumber version = VersionNumber.parse(valueName);
-                VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ " + valueName, visualStudioDir, visualCppDir, version);
-                VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
-                foundInstalls.put(visualStudioDir, visualStudio);
-            } else {
-                LOGGER.debug("Ignoring candidate Visual C++ directory {} as it does not look like a Visual C++ installation.", visualCppDir);
-            }
-        }
-    }
-
-    private void locateInstallInPath() {
-        File compilerInPath = os.findInPath(COMPILER_FILENAME);
-        if (compilerInPath == null) {
-            LOGGER.debug("No visual c++ compiler found in system path.");
-            return;
-        }
-
-        File visualCppDir = GFileUtils.canonicalise(compilerInPath.getParentFile().getParentFile());
-        if (!isVisualCpp(visualCppDir)) {
-            visualCppDir = visualCppDir.getParentFile();
-            if (!isVisualCpp(visualCppDir)) {
-                LOGGER.debug("Ignoring candidate Visual C++ install for {} as it does not look like a Visual C++ installation.", compilerInPath);
-                return;
-            }
-        }
-        LOGGER.debug("Found Visual C++ install {} using system path", visualCppDir);
-
-        File visualStudioDir = visualCppDir.getParentFile();
-        if (!foundInstalls.containsKey(visualStudioDir)) {
-            VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ from system path", visualStudioDir, visualCppDir, VersionNumber.UNKNOWN);
-            VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
-            foundInstalls.put(visualStudioDir, visualStudio);
-        }
-        pathInstall = foundInstalls.get(visualStudioDir);
-    }
-
-    private SearchResult locateUserSpecifiedInstall(File candidate) {
-        File visualStudioDir = GFileUtils.canonicalise(candidate);
-        File visualCppDir = new File(visualStudioDir, "VC");
-        if (!isVisualStudio(visualStudioDir) || !isVisualCpp(visualCppDir)) {
-            LOGGER.debug("Ignoring candidate Visual C++ install for {} as it does not look like a Visual C++ installation.", candidate);
-            return new InstallNotFound(String.format("The specified installation directory '%s' does not appear to contain a Visual Studio installation.", candidate));
-        }
-
-        if (!foundInstalls.containsKey(visualStudioDir)) {
-            VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ from user provided path", visualStudioDir, visualCppDir, VersionNumber.UNKNOWN);
-            VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
-            foundInstalls.put(visualStudioDir, visualStudio);
-        }
-        return new InstallFound(foundInstalls.get(visualStudioDir));
-    }
-
-    private VisualCppInstall buildVisualCppInstall(String name, File vsPath, File basePath, VersionNumber version) {
-        boolean isNativeAmd64 = systemInfo.getArchitecture() == SystemInfo.Architecture.amd64;
-        Map<Architecture, List<File>> paths = new HashMap<Architecture, List<File>>();
-        Map<Architecture, File> binaryPaths = new HashMap<Architecture, File>();
-        Map<Architecture, File> libraryPaths = new HashMap<Architecture, File>();
-        Map<Architecture, File> includePaths = new HashMap<Architecture, File>();
-        Map<Architecture, String> assemblerFilenames = new HashMap<Architecture, String>();
-        Map<Architecture, Map<String, String>> definitions = new HashMap<Architecture, Map<String, String>>();
-        NotationParser<Object, Architecture> architectureParser = ArchitectureNotationParser.parser();
-        Architecture amd64 = architectureParser.parseNotation(ARCHITECTURE_AMD64);
-        Architecture x86 = architectureParser.parseNotation(ARCHITECTURE_X86);
-        Architecture arm = architectureParser.parseNotation(ARCHITECTURE_ARM);
-        Architecture ia64 = architectureParser.parseNotation(ARCHITECTURE_IA64);
-        File includePath = new File(basePath, PATH_INCLUDE);
-        File commonTools = new File(vsPath, PATH_COMMONTOOLS);
-        File commonIde = new File(vsPath, PATH_COMMONIDE);
-
-        if (isNativeAmd64) {
-            Architecture[] architectures = {
-                amd64,
-                x86,
-                arm
-            };
-            String[] binPaths = {
-                BINPATH_AMD64_AMD64,
-                BINPATH_AMD64_X86,
-                BINPATH_AMD64_ARM
-            };
-            String[] libPaths = {
-                LIBPATH_AMD64,
-                LIBPATH_X86,
-                LIBPATH_ARM
-            };
-            String[] asmFilenames = {
-                ASSEMBLER_FILENAME_AMD64,
-                ASSEMBLER_FILENAME_X86,
-                ASSEMBLER_FILENAME_ARM
-            };
-
-            for (int i = 0; i != architectures.length; ++i) {
-                Architecture architecture = architectures[i];
-                File binPath = new File(basePath, binPaths[i]);
-                File libPath = new File(basePath, libPaths[i]);
-
-                if (binPath.isDirectory() && libPath.isDirectory()) {
-                    Map<String, String> definitionsList = new LinkedHashMap<String, String>();
-                    List<File> pathsList = new ArrayList<File>();
-
-                    pathsList.add(commonTools);
-                    pathsList.add(commonIde);
-
-                    // For cross-compilers, add the native compiler to the path as well
-                    if (architecture != amd64) {
-                        pathsList.add(new File(basePath, binPaths[0]));
-                    }
-
-                    if (architecture == arm) {
-                        definitionsList.put(DEFINE_ARMPARTITIONAVAILABLE, "1");
-                    }
-
-                    binaryPaths.put(architecture, binPath);
-                    libraryPaths.put(architecture, libPath);
-                    includePaths.put(architecture, includePath);
-                    assemblerFilenames.put(architecture, asmFilenames[i]);
-                    paths.put(architecture, pathsList);
-                    definitions.put(architecture, definitionsList);
-                }
-            }
-        }
-
-        Architecture[] architectures = {
-            x86,
-            amd64,
-            ia64,
-            arm
-        };
-        String[] binPaths = {
-            BINPATH_X86_X86,
-            BINPATH_X86_AMD64,
-            BINPATH_X86_IA64,
-            BINPATH_X86_ARM
-        };
-        String[] libPaths = {
-            LIBPATH_X86,
-            LIBPATH_AMD64,
-            LIBPATH_IA64,
-            LIBPATH_ARM
-        };
-        String[] asmFilenames = {
-            ASSEMBLER_FILENAME_X86,
-            ASSEMBLER_FILENAME_AMD64,
-            ASSEMBLER_FILENAME_IA64,
-            ASSEMBLER_FILENAME_ARM
-        };
-
-        for (int i = 0; i != architectures.length; ++i) {
-            Architecture architecture = architectures[i];
-
-            if (!binaryPaths.containsKey(architecture)) {
-                File binPath = new File(basePath, binPaths[i]);
-                File libPath = new File(basePath, libPaths[i]);
-    
-                if (binPath.isDirectory() && libPath.isDirectory()) {
-                    Map<String, String> definitionsList = new LinkedHashMap<String, String>();
-                    List<File> pathsList = new ArrayList<File>();
-
-                    pathsList.add(commonTools);
-                    pathsList.add(commonIde);
-
-                    // For cross-compilers, add the native compiler to the path as well
-                    if (architecture != x86) {
-                        pathsList.add(new File(basePath, binPaths[0]));
-                    }
-
-                    if (architecture == arm) {
-                        definitionsList.put(DEFINE_ARMPARTITIONAVAILABLE, "1");
-                    }
-
-                    binaryPaths.put(architecture, binPath);
-                    libraryPaths.put(architecture, libPath);
-                    includePaths.put(architecture, includePath);
-                    assemblerFilenames.put(architecture, asmFilenames[i]);
-                    paths.put(architecture, pathsList);
-                    definitions.put(architecture, definitionsList);
-                }
-            }
-        }
-
-        // TODO:MPUT - use x64 as the default architecture on x64 systems (same for winsdk)? (isNativeAmd64 && binaryPaths.containsKey(amd64)) ? amd64 : x86
-        return new VisualCppInstall(name, version, x86, paths, binaryPaths, libraryPaths, includePaths, assemblerFilenames, definitions);
-    }
-
-    private SearchResult determineDefaultInstall() {
-        if (pathInstall != null) {
-            return new InstallFound(pathInstall);
-        }
-
-        VisualStudioInstall candidate = null;
-
-        for (VisualStudioInstall visualStudio : foundInstalls.values()) {
-            if (candidate == null || visualStudio.getVersion().compareTo(candidate.getVersion()) > 0) {
-                candidate = visualStudio;
-            }
-        }
-
-        return candidate == null ? new InstallNotFound("Could not locate a Visual Studio installation, using the Windows registry and system path.") : new InstallFound(candidate);
-    }
-
-    private static boolean isVisualStudio(File candidate) {
-        return new File(candidate, PATH_COMMON).isDirectory() && isVisualCpp(new File(candidate, "VC"));
-    }
-
-    private static boolean isVisualCpp(File candidate) {
-        return new File(candidate, PATH_BIN + COMPILER_FILENAME).isFile();
-    }
-
-    private static class InstallFound implements SearchResult {
-        private final VisualStudioInstall install;
-
-        public InstallFound(VisualStudioInstall install) {
-            this.install = install;
-        }
-
-        public VisualStudioInstall getVisualStudio() {
-            return install;
-        }
-
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-        }
-    }
-
-    private static class InstallNotFound implements SearchResult {
-        private final String message;
-
-        private InstallNotFound(String message) {
-            this.message = message;
-        }
-
-        public VisualStudioInstall getVisualStudio() {
-            return null;
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            visitor.node(message);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java
deleted file mode 100644
index 929bf49..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java
+++ /dev/null
@@ -1,275 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import net.rubygrapefruit.platform.MissingRegistryEntryException;
-import net.rubygrapefruit.platform.WindowsRegistry;
-import org.apache.commons.lang.StringUtils;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.TreeVisitor;
-import org.gradle.util.VersionNumber;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class DefaultWindowsSdkLocator implements WindowsSdkLocator {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultWindowsSdkLocator.class);
-    private static final String REGISTRY_BASEPATHS[] = {
-        "SOFTWARE\\",
-        "SOFTWARE\\Wow6432Node\\"
-    };
-    private static final String REGISTRY_ROOTPATH_SDK = "Microsoft\\Microsoft SDKs\\Windows";
-    private static final String REGISTRY_ROOTPATH_KIT = "Microsoft\\Windows Kits\\Installed Roots";
-    private static final String REGISTRY_FOLDER = "InstallationFolder";
-    private static final String REGISTRY_VERSION = "ProductVersion";
-    private static final String REGISTRY_NAME = "ProductName";
-    private static final String REGISTRY_KIT_8 = "KitsRoot";
-    private static final String REGISTRY_KIT_81 = "KitsRoot81";
-    private static final String VERSION_KIT_8 = "8.0";
-    private static final String VERSION_KIT_81 = "8.1";
-    private static final String VERSION_USER = "user";
-
-    private static final String NAME_USER = "User-provided Windows SDK";
-    private static final String NAME_KIT = "Windows Kit";
-
-    private static final String RESOURCE_PATHS[] = {
-        "bin/x86/",
-        "bin/"
-    };
-
-    private static final String KERNEL32_PATHS[] = {
-        "lib/winv6.3/um/x86/",
-        "lib/win8/um/x86/",
-        "lib/"
-    };
-
-    private static final String RESOURCE_FILENAME = "rc.exe";
-    private static final String KERNEL32_FILENAME = "kernel32.lib";
-
-    private final Map<File, WindowsSdk> foundSdks = new HashMap<File, WindowsSdk>();
-    private final OperatingSystem os;
-    private final WindowsRegistry windowsRegistry;
-    private WindowsSdk pathSdk;
-    private boolean initialised;
-
-    public DefaultWindowsSdkLocator(OperatingSystem os, WindowsRegistry windowsRegistry) {
-        this.os = os;
-        this.windowsRegistry = windowsRegistry;
-    }
-
-    public SearchResult locateWindowsSdks(File candidate) {
-        if (!initialised) {
-            locateSdksInRegistry();
-            locateKitsInRegistry();
-            locateSdkInPath();
-            initialised = true;
-        }
-
-        if (candidate != null) {
-            return locateUserSpecifiedSdk(candidate);
-        }
-
-        return locateDefaultSdk();
-    }
-
-    private void locateSdksInRegistry() {
-        for (String baseKey : REGISTRY_BASEPATHS) {
-            locateSdksInRegistry(baseKey);
-        }
-    }
-
-    private void locateSdksInRegistry(String baseKey) {
-        try {
-            List<String> subkeys = windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_SDK);
-            for (String subkey : subkeys) {
-                try {
-                    String basePath = baseKey + REGISTRY_ROOTPATH_SDK + "\\" + subkey;
-                    File sdkDir = GFileUtils.canonicalise(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_FOLDER)));
-                    String version = formatVersion(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_VERSION));
-                    String name = windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_NAME);
-
-                    if (isWindowsSdk(sdkDir)) {
-                        LOGGER.debug("Found Windows SDK {} at {}", version, sdkDir);
-                        addSdk(sdkDir, version, name);
-                    } else {
-                        LOGGER.debug("Ignoring candidate Windows SDK directory {} as it does not look like a Windows SDK installation.", sdkDir);
-                    }
-                } catch (MissingRegistryEntryException e) {
-                    // Ignore the subkey if it doesn't have a folder and version
-                }
-            }
-        } catch (MissingRegistryEntryException e) {
-            // No SDK information available in the registry
-        }
-    }
-
-    private void locateKitsInRegistry() {
-        for (String baseKey : REGISTRY_BASEPATHS) {
-            locateKitsInRegistry(baseKey);
-        }
-    }
-
-    private void locateKitsInRegistry(String baseKey) {
-        String[] versions = {
-                VERSION_KIT_8,
-                VERSION_KIT_81
-        };
-        String[] keys = {
-                REGISTRY_KIT_8,
-                REGISTRY_KIT_81
-        };
-
-        for (int i = 0; i != keys.length; ++i) {
-            try {
-                File kitDir = GFileUtils.canonicalise(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_KIT, keys[i])));
-                if (isWindowsSdk(kitDir)) {
-                    LOGGER.debug("Found Windows Kit {} at {}", versions[i], kitDir);
-                    addSdk(kitDir, versions[i], NAME_KIT + " " + versions[i]);
-                } else {
-                    LOGGER.debug("Ignoring candidate Windows Kit directory {} as it does not look like a Windows Kit installation.", kitDir);
-                }
-            } catch (MissingRegistryEntryException e) {
-                // Ignore the version if the string cannot be read
-            }
-        }
-    }
-
-    private void locateSdkInPath() {
-        File resourceCompiler = os.findInPath(RESOURCE_FILENAME);
-        if (resourceCompiler == null) {
-            LOGGER.debug("Could not find Windows resource compiler in system path.");
-            return;
-        }
-        File sdkDir = GFileUtils.canonicalise(resourceCompiler.getParentFile().getParentFile());
-        if (!isWindowsSdk(sdkDir)) {
-            sdkDir = sdkDir.getParentFile();
-            if (!isWindowsSdk(sdkDir)) {
-                LOGGER.debug("Ignoring candidate Windows SDK for {} as it does not look like a Windows SDK installation.", resourceCompiler);
-            }
-        }
-        LOGGER.debug("Found Windows SDK {} using system path", sdkDir);
-
-        if (!foundSdks.containsKey(sdkDir)) {
-            addSdk(sdkDir, "path", "Path-resolved Windows SDK");
-        }
-        pathSdk = foundSdks.get(sdkDir);
-    }
-
-    private SearchResult locateUserSpecifiedSdk(File candidate) {
-        File sdkDir = GFileUtils.canonicalise(candidate);
-        if (!isWindowsSdk(sdkDir)) {
-            return new SdkNotFound(String.format("The specified installation directory '%s' does not appear to contain a Windows SDK installation.", candidate));
-        }
-
-        if (!foundSdks.containsKey(sdkDir)) {
-            addSdk(sdkDir, VERSION_USER, NAME_USER);
-        }
-        return new SdkFound(foundSdks.get(sdkDir));
-    }
-
-    private SearchResult locateDefaultSdk() {
-        if (pathSdk != null) {
-            return new SdkFound(pathSdk);
-        }
-
-        WindowsSdk candidate = null;
-        for (WindowsSdk windowsSdk : foundSdks.values()) {
-            if (candidate == null || windowsSdk.getVersion().compareTo(candidate.getVersion()) > 0) {
-                candidate = windowsSdk;
-            }
-        }
-        return candidate == null ? new SdkNotFound("Could not locate a Windows SDK installation, using the Windows registry and system path.") : new SdkFound(candidate);
-    }
-
-    private void addSdk(File path, String version, String name) {
-        foundSdks.put(path, new WindowsSdk(path, VersionNumber.parse(version), name));
-    }
-
-    private static boolean isWindowsSdk(File candidate) {
-        boolean hasResourceCompiler = false;
-        boolean hasKernel32Lib = false;
-
-        for (String path : RESOURCE_PATHS) {
-            if (new File(candidate, path + RESOURCE_FILENAME).isFile()) {
-                hasResourceCompiler = true;
-                break;
-            }
-        }
-
-        for (String path : KERNEL32_PATHS) {
-            if (new File(candidate, path + KERNEL32_FILENAME).isFile()) {
-                hasKernel32Lib = true;
-                break;
-            }
-        }
-
-        return hasResourceCompiler && hasKernel32Lib;
-    }
-
-    private static String formatVersion(String version) {
-        int index = StringUtils.ordinalIndexOf(version, ".", 2);
-
-        if (index != -1) {
-            version = version.substring(0, index);
-        }
-
-        return version;
-    }
-
-    private static class SdkFound implements SearchResult {
-        private final WindowsSdk sdk;
-
-        public SdkFound(WindowsSdk sdk) {
-            this.sdk = sdk;
-        }
-
-        public WindowsSdk getSdk() {
-            return sdk;
-        }
-
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-        }
-    }
-
-    private static class SdkNotFound implements SearchResult {
-        private final String message;
-
-        private SdkNotFound(String message) {
-            this.message = message;
-        }
-
-        public WindowsSdk getSdk() {
-            return null;
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            visitor.node(message);
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/EscapeUserArgs.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/EscapeUserArgs.java
deleted file mode 100644
index 238d244..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/EscapeUserArgs.java
+++ /dev/null
@@ -1,40 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.api.Transformer;
-import org.gradle.util.CollectionUtils;
-
-import java.util.List;
-
-class EscapeUserArgs implements Transformer<String, String> {
-    public static String escapeUserArg(String original) {
-        return new EscapeUserArgs().transform(original);
-    }
-
-    public static List<String> escapeUserArgs(List<String> original) {
-        return new EscapeUserArgs().transform(original);
-    }
-
-    public String transform(String original) {
-        return original.replace("\\", "\\\\").replace("\"", "\\\"");
-    }
-
-    public List<String> transform(List<String> args) {
-        return CollectionUtils.collect(args, this);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/InstallationSearchResult.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/InstallationSearchResult.java
deleted file mode 100644
index 3dfcae0..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/InstallationSearchResult.java
+++ /dev/null
@@ -1,27 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult;
-
-import java.io.File;
-
-public interface InstallationSearchResult extends ToolSearchResult {
-    String getVersion();
-
-    File getResult();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java
deleted file mode 100755
index 2b6d38f..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java
+++ /dev/null
@@ -1,58 +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.nativebinaries.toolchain.internal.msvcpp;
-
-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.nativebinaries.internal.StaticLibraryArchiverSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.OptionsFileArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.nativebinaries.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
-
-class LibExeStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
-    private final CommandLineTool<StaticLibraryArchiverSpec> commandLineTool;
-
-    public LibExeStaticLibraryArchiver(CommandLineTool<StaticLibraryArchiverSpec> commandLineTool) {
-        this.commandLineTool = commandLineTool
-                .withArguments(new OptionsFileArgsTransformer<StaticLibraryArchiverSpec>(ArgWriter.windowsStyleFactory(), new LibExeSpecToArguments()
-        ));
-    }
-
-    public WorkResult execute(StaticLibraryArchiverSpec spec) {
-        return commandLineTool.execute(spec);
-    }
-
-    private static class LibExeSpecToArguments implements ArgsTransformer<StaticLibraryArchiverSpec> {
-        public List<String> transform(StaticLibraryArchiverSpec spec) {
-            List<String> args = new ArrayList<String>();
-            args.add("/OUT:" + spec.getOutputFile().getAbsolutePath());
-            args.add("/NOLOGO");
-            args.addAll(escapeUserArgs(spec.getAllArgs()));
-            for (File file : spec.getObjectFiles()) {
-                args.add(file.getAbsolutePath());
-            }
-            return args;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LinkExeLinker.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LinkExeLinker.java
deleted file mode 100755
index 4f9bb4a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/LinkExeLinker.java
+++ /dev/null
@@ -1,70 +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.nativebinaries.toolchain.internal.msvcpp;
-
-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.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.SharedLibraryLinkerSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.OptionsFileArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.nativebinaries.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
-
-class LinkExeLinker implements Compiler<LinkerSpec> {
-
-    private final CommandLineTool<LinkerSpec> commandLineTool;
-
-    public LinkExeLinker(CommandLineTool<LinkerSpec> commandLineTool) {
-        this.commandLineTool = commandLineTool
-                .withArguments(new OptionsFileArgsTransformer<LinkerSpec>(
-                ArgWriter.windowsStyleFactory(), new LinkerArgsTransformer()
-        ));
-    }
-
-    public WorkResult execute(LinkerSpec spec) {
-        return commandLineTool.execute(spec);
-    }
-
-    private static class LinkerArgsTransformer implements ArgsTransformer<LinkerSpec> {
-        public List<String> transform(LinkerSpec spec) {
-            List<String> args = new ArrayList<String>();
-            args.addAll(escapeUserArgs(spec.getAllArgs()));
-            args.add("/OUT:" + spec.getOutputFile().getAbsolutePath());
-            args.add("/NOLOGO");
-            if (spec instanceof SharedLibraryLinkerSpec) {
-                args.add("/DLL");
-            }
-            for (File pathEntry : spec.getLibraryPath()) {
-                args.add("/LIBPATH:" + pathEntry.getAbsolutePath());
-            }
-            for (File file : spec.getObjectFiles()) {
-                args.add(file.getAbsolutePath());
-            }
-            for (File file : spec.getLibraries()) {
-                args.add(file.getAbsolutePath());
-            }
-            return args;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/NativeCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/NativeCompiler.java
deleted file mode 100644
index 819a933..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/NativeCompiler.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal.msvcpp;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.ArgWriter;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.OptionsFileArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.SingleSourceCompileArgTransformer;
-import org.gradle.nativebinaries.toolchain.internal.gcc.ShortCircuitArgsTransformer;
-
-import java.io.File;
-
-abstract public class NativeCompiler<T extends NativeCompileSpec> implements org.gradle.api.internal.tasks.compile.Compiler<T> {
-
-    private final CommandLineTool<T> commandLineTool;
-    private final OptionsFileArgsTransformer<T> argsTransFormer;
-
-    NativeCompiler(CommandLineTool<T> commandLineTool, ArgsTransformer<T> argsTransFormer) {
-        this.argsTransFormer = new OptionsFileArgsTransformer<T>(
-                        ArgWriter.windowsStyleFactory(),
-                        argsTransFormer);
-        this.commandLineTool = commandLineTool;
-    }
-
-    public WorkResult execute(T spec) {
-        boolean didWork = false;
-        for (File sourceFile : spec.getSourceFiles()) {
-            String objectFileName = FilenameUtils.removeExtension(sourceFile.getName()) + ".obj";
-            WorkResult result = commandLineTool.inWorkDirectory(spec.getObjectFileDir())
-                    .withArguments(new SingleSourceCompileArgTransformer<T>(sourceFile,
-                                                                            objectFileName,
-                                                                            new ShortCircuitArgsTransformer<T>(argsTransFormer),
-                                                                            true,
-                                                                            true))
-                    .execute(spec);
-            didWork = didWork || result.getDidWork();
-        }
-        return new SimpleWorkResult(didWork);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java
deleted file mode 100644
index 2871dc2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java
+++ /dev/null
@@ -1,50 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.MacroArgsConverter;
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.nativebinaries.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArg;
-import static org.gradle.nativebinaries.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
-
-abstract class VisualCppCompilerArgsTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T> {
-    public List<String> transform(T spec) {
-        List<String> args = new ArrayList<String>();
-        args.add(getLanguageOption());
-        args.add("/nologo");
-
-        for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
-            args.add(escapeUserArg("/D" + macroArg));
-        }
-        args.addAll(escapeUserArgs(spec.getAllArgs()));
-
-        args.add("/c");
-        for (File file : spec.getIncludeRoots()) {
-            args.add("/I" + file.getAbsolutePath());
-        }
-
-        return args;
-    }
-
-    protected abstract String getLanguageOption();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppInstall.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppInstall.java
deleted file mode 100644
index d6c1e00..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppInstall.java
+++ /dev/null
@@ -1,116 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-import org.gradle.api.Named;
-import org.gradle.nativebinaries.platform.Architecture;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal;
-import org.gradle.util.VersionNumber;
-
-public class VisualCppInstall implements Named {
-    private static final String COMPILER_FILENAME = "cl.exe";
-    private static final String LINKER_FILENAME = "link.exe";
-    private static final String ARCHIVER_FILENAME = "lib.exe";
-
-    private final Map<Architecture, List<File>> paths;
-    private final Map<Architecture, File> binaryPaths;
-    private final Map<Architecture, File> libraryPaths;
-    private final Map<Architecture, File> includePaths;
-    private final Map<Architecture, String> assemblerFilenames;
-    private final Map<Architecture, Map<String, String>> definitions;
-    private final Architecture defaultArchitecture;
-    private final String name;
-    private final VersionNumber version;
-
-    public VisualCppInstall(String name, VersionNumber version, Architecture defaultArchitecture,
-            Map<Architecture, List<File>> paths, Map<Architecture, File> binaryPaths, Map<Architecture, File> libraryPaths,
-            Map<Architecture, File> includePaths, Map<Architecture, String> assemblerFilenames,
-            Map<Architecture, Map<String, String>> definitions) {
-        this.paths = paths;
-        this.name = name;
-        this.version = version;
-        this.defaultArchitecture = defaultArchitecture;
-        this.binaryPaths = binaryPaths;
-        this.libraryPaths = libraryPaths;
-        this.includePaths = includePaths;
-        this.assemblerFilenames = assemblerFilenames;
-        this.definitions = definitions;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public VersionNumber getVersion() {
-        return version;
-    }
-
-    public boolean isSupportedPlatform(Platform targetPlatform) {
-        // TODO:ADAM - ARM only if the target OS is Windows 8 or later
-        // TODO:MPUT - ARM also if the target OS is Windows RT or Windows Phone/Mobile/CE
-        // TODO:ADAM - IA64 only if the target OS is Windows 2008 or earlier
-        return targetPlatform.getOperatingSystem().isWindows()
-                && (binaryPaths.containsKey(getPlatformArchitecture(targetPlatform)));
-    }
-
-    public List<File> getPath(Platform targetPlatform) {
-        return paths.get(getPlatformArchitecture(targetPlatform));
-    }
-
-    public File getCompiler(Platform targetPlatform) {
-        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), COMPILER_FILENAME);
-    }
-
-    public File getLinker(Platform targetPlatform) {
-        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), LINKER_FILENAME);
-    }
-
-    public File getArchiver(Platform targetPlatform) {
-        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), ARCHIVER_FILENAME);
-    }
-
-    public File getAssembler(Platform targetPlatform) {
-        Architecture architecture = getPlatformArchitecture(targetPlatform);
-        return new File(binaryPaths.get(architecture), assemblerFilenames.get(architecture));
-    }
-
-    public File getBinaryPath(Platform targetPlatform) {
-        return binaryPaths.get(getPlatformArchitecture(targetPlatform));
-    }
-
-    public File getLibraryPath(Platform targetPlatform) {
-        return libraryPaths.get(getPlatformArchitecture(targetPlatform));
-    }
-
-    public Map<String, String> getDefinitions(Platform targetPlatform) {
-        return definitions.get(getPlatformArchitecture(targetPlatform));
-    }
-
-    public File getIncludePath(Platform targetPlatform) {
-        return includePaths.get(getPlatformArchitecture(targetPlatform));
-    }
-
-    private Architecture getPlatformArchitecture(Platform targetPlatform) {
-        ArchitectureInternal architecture = (ArchitectureInternal) targetPlatform.getArchitecture();
-        return (architecture == ArchitectureInternal.TOOL_CHAIN_DEFAULT) ? defaultArchitecture : architecture;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChain.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChain.java
deleted file mode 100755
index fe70d8d..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChain.java
+++ /dev/null
@@ -1,245 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.api.Transformer;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.jvm.Jvm;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.internal.BinaryToolSpec;
-import org.gradle.nativebinaries.internal.LinkerSpec;
-import org.gradle.nativebinaries.internal.StaticLibraryArchiverSpec;
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec;
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec;
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec;
-import org.gradle.nativebinaries.language.rc.internal.WindowsResourceCompileSpec;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.toolchain.VisualCpp;
-import org.gradle.nativebinaries.toolchain.internal.*;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.util.TreeVisitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Map.Entry;
-
-public class VisualCppToolChain extends AbstractToolChain implements VisualCpp {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(VisualCppToolChain.class);
-
-    public static final String DEFAULT_NAME = "visualCpp";
-
-    private final ExecActionFactory execActionFactory;
-    private final VisualStudioLocator visualStudioLocator;
-    private final WindowsSdkLocator windowsSdkLocator;
-    private File installDir;
-    private File windowsSdkDir;
-    private VisualCppInstall visualCpp;
-    private WindowsSdk windowsSdk;
-    private ToolChainAvailability availability;
-
-    public VisualCppToolChain(String name, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory,
-                              VisualStudioLocator visualStudioLocator, WindowsSdkLocator windowsSdkLocator) {
-        super(name, operatingSystem, fileResolver);
-        this.execActionFactory = execActionFactory;
-        this.visualStudioLocator = visualStudioLocator;
-        this.windowsSdkLocator = windowsSdkLocator;
-    }
-
-    @Override
-    protected String getTypeName() {
-        return "Visual Studio";
-    }
-
-    private void checkAvailable(ToolChainAvailability availability) {
-        if (!operatingSystem.isWindows()) {
-            availability.unavailable("Visual Studio is not available on this operating system.");
-            return;
-        }
-        VisualStudioLocator.SearchResult visualStudioSearchResult = visualStudioLocator.locateVisualStudioInstalls(installDir);
-        availability.mustBeAvailable(visualStudioSearchResult);
-        if (visualStudioSearchResult.isAvailable()) {
-            visualCpp = visualStudioSearchResult.getVisualStudio().getVisualCpp();
-        }
-        WindowsSdkLocator.SearchResult windowsSdkSearchResult = windowsSdkLocator.locateWindowsSdks(windowsSdkDir);
-        availability.mustBeAvailable(windowsSdkSearchResult);
-        if (windowsSdkSearchResult.isAvailable()) {
-            windowsSdk = windowsSdkSearchResult.getSdk();
-        }
-    }
-
-    public File getInstallDir() {
-        return installDir;
-    }
-
-    public void setInstallDir(Object installDirPath) {
-        this.installDir = resolve(installDirPath);
-    }
-
-    public File getWindowsSdkDir() {
-        return windowsSdkDir;
-    }
-
-    public void setWindowsSdkDir(Object windowsSdkDirPath) {
-        this.windowsSdkDir = resolve(windowsSdkDirPath);
-    }
-
-    private ToolChainAvailability getAvailability() {
-        if (availability == null) {
-            availability = new ToolChainAvailability();
-            checkAvailable(availability);
-        }
-        return availability;
-    }
-
-    public PlatformToolChain target(Platform targetPlatform) {
-        ToolChainAvailability result = new ToolChainAvailability();
-        result.mustBeAvailable(getAvailability());
-        if (visualCpp != null && !visualCpp.isSupportedPlatform(targetPlatform)) {
-            result.unavailable(String.format("Don't know how to build for platform '%s'.", targetPlatform.getName()));
-        }
-        if (!result.isAvailable()) {
-            return new UnavailablePlatformToolChain(result);
-        }
-        return new VisualCppPlatformToolChain(visualCpp, windowsSdk, targetPlatform);
-    }
-
-    @Override
-    public String getSharedLibraryLinkFileName(String libraryName) {
-        return getSharedLibraryName(libraryName).replaceFirst("\\.dll$", ".lib");
-    }
-
-    private class VisualCppPlatformToolChain implements PlatformToolChain {
-        private final WindowsSdk sdk;
-        private final Platform targetPlatform;
-        private final VisualCppInstall visualCpp;
-
-        private VisualCppPlatformToolChain(VisualCppInstall visualCpp, WindowsSdk sdk, Platform targetPlatform) {
-            this.visualCpp = visualCpp;
-            this.sdk = sdk;
-            this.targetPlatform = targetPlatform;
-        }
-
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createCppCompiler() {
-            CommandLineTool<CppCompileSpec> commandLineTool = commandLineTool("C++ compiler", visualCpp.getCompiler(targetPlatform));
-            Transformer<CppCompileSpec, CppCompileSpec> specTransformer = addIncludePathAndDefinitions();
-            commandLineTool.withSpecTransformer(specTransformer);
-            CppCompiler cppCompiler = new CppCompiler(commandLineTool);
-            return (Compiler<T>) new OutputCleaningCompiler<CppCompileSpec>(cppCompiler, ".obj");
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createCCompiler() {
-            CommandLineTool<CCompileSpec> commandLineTool = commandLineTool("C compiler", visualCpp.getCompiler(targetPlatform));
-            Transformer<CCompileSpec, CCompileSpec> specTransformer = addIncludePathAndDefinitions();
-            commandLineTool.withSpecTransformer(specTransformer);
-            CCompiler cCompiler = new CCompiler(commandLineTool);
-            return (Compiler<T>) new OutputCleaningCompiler<CCompileSpec>(cCompiler, ".obj");
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createObjectiveCppCompiler() {
-            throw new RuntimeException("Objective-C++ is not available on the Visual C++ toolchain");
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createObjectiveCCompiler() {
-            throw new RuntimeException("Objective-C is not available on the Visual C++ toolchain");
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createAssembler() {
-            CommandLineTool<AssembleSpec> commandLineTool = commandLineTool("Assembler", visualCpp.getAssembler(targetPlatform));
-            return (Compiler<T>) new Assembler(commandLineTool);
-        }
-
-        public <T extends BinaryToolSpec> Compiler<T> createWindowsResourceCompiler() {
-            CommandLineTool<WindowsResourceCompileSpec> commandLineTool = commandLineTool("Windows resource compiler", sdk.getResourceCompiler(targetPlatform));
-            Transformer<WindowsResourceCompileSpec, WindowsResourceCompileSpec> specTransformer = addIncludePathAndDefinitions();
-            commandLineTool.withSpecTransformer(specTransformer);
-            WindowsResourceCompiler windowsResourceCompiler = new WindowsResourceCompiler(commandLineTool);
-            return (Compiler<T>) new OutputCleaningCompiler<WindowsResourceCompileSpec>(windowsResourceCompiler, ".res");
-        }
-
-        public <T extends LinkerSpec> Compiler<T> createLinker() {
-            CommandLineTool<LinkerSpec> commandLineTool = commandLineTool("Linker", visualCpp.getLinker(targetPlatform));
-            commandLineTool.withSpecTransformer(addLibraryPath());
-            return (Compiler<T>) new LinkExeLinker(commandLineTool);
-        }
-
-        public <T extends StaticLibraryArchiverSpec> Compiler<T> createStaticLibraryArchiver() {
-            CommandLineTool<StaticLibraryArchiverSpec> commandLineTool = commandLineTool("Static library archiver", visualCpp.getArchiver(targetPlatform));
-            return (Compiler<T>) new LibExeStaticLibraryArchiver(commandLineTool);
-        }
-
-        private <T extends BinaryToolSpec> CommandLineTool<T> commandLineTool(String toolName, File exe) {
-            CommandLineTool<T> tool = new CommandLineTool<T>(toolName, exe, execActionFactory);
-
-            // The visual C++ tools use the path to find other executables
-            // TODO:ADAM - restrict this to the specific path for the target tool
-            tool.withPath(visualCpp.getPath(targetPlatform));
-            tool.withPath(sdk.getBinDir(targetPlatform));
-
-            // Clear environment variables that might effect cl.exe & link.exe
-            clearEnvironmentVars(tool, "INCLUDE", "CL", "LIBPATH", "LINK", "LIB");
-            return tool;
-        }
-
-        private <T extends BinaryToolSpec> void clearEnvironmentVars(CommandLineTool<T> tool, String... names) {
-            Map<String, ?> environmentVariables = Jvm.current().getInheritableEnvironmentVariables(System.getenv());
-            for (String name : names) {
-                Object value = environmentVariables.get(name);
-                if (value != null) {
-                    LOGGER.warn("Ignoring value '{}' set for environment variable '{}'.", value, name);
-                    tool.withEnvironmentVar(name, "");
-                }
-            }
-        }
-
-        public String getOutputType() {
-            return String.format("%s-%s", getName(), operatingSystem.getName());
-        }
-
-        private <T extends NativeCompileSpec> Transformer<T, T> addIncludePathAndDefinitions() {
-            return new Transformer<T, T>() {
-                public T transform(T original) {
-                    original.include(visualCpp.getIncludePath(targetPlatform));
-                    original.include(sdk.getIncludeDirs());
-                    for (Entry<String, String> definition : visualCpp.getDefinitions(targetPlatform).entrySet()) {
-                        original.define(definition.getKey(), definition.getValue());
-                    }
-                    return original;
-                }
-            };
-        }
-
-        private Transformer<LinkerSpec, LinkerSpec> addLibraryPath() {
-            return new Transformer<LinkerSpec, LinkerSpec>() {
-                public LinkerSpec transform(LinkerSpec original) {
-                    original.libraryPath(visualCpp.getLibraryPath(targetPlatform), sdk.getLibDir(targetPlatform));
-                    return original;
-                }
-            };
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioInstall.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioInstall.java
deleted file mode 100644
index ac59ddd..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioInstall.java
+++ /dev/null
@@ -1,48 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.api.Named;
-import org.gradle.util.VersionNumber;
-
-import java.io.File;
-
-public class VisualStudioInstall implements Named {
-    private final VisualCppInstall visualCppInstall;
-    private final File baseDir;
-
-    public VisualStudioInstall(File baseDir,  VisualCppInstall visualCppInstall) {
-        this.baseDir = baseDir;
-        this.visualCppInstall = visualCppInstall;
-    }
-
-    public String getName() {
-        return visualCppInstall.getName();
-    }
-
-    public VersionNumber getVersion() {
-        return visualCppInstall.getVersion();
-    }
-
-    public File getVisualStudioDir() {
-        return baseDir;
-    }
-
-    public VisualCppInstall getVisualCpp() {
-        return visualCppInstall;
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioLocator.java
deleted file mode 100644
index e7ad130..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualStudioLocator.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult;
-
-import java.io.File;
-
-public interface VisualStudioLocator {
-
-    SearchResult locateVisualStudioInstalls(File candidate);
-
-    interface SearchResult extends ToolSearchResult {
-        VisualStudioInstall getVisualStudio();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsResourceCompiler.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsResourceCompiler.java
deleted file mode 100644
index 93adae2..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsResourceCompiler.java
+++ /dev/null
@@ -1,90 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.FileUtils;
-import org.gradle.internal.hash.HashUtil;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.language.rc.internal.WindowsResourceCompileSpec;
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer;
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool;
-import org.gradle.nativebinaries.toolchain.internal.MacroArgsConverter;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class WindowsResourceCompiler implements Compiler<WindowsResourceCompileSpec> {
-
-    private final CommandLineTool<WindowsResourceCompileSpec> commandLineTool;
-
-    WindowsResourceCompiler(CommandLineTool<WindowsResourceCompileSpec> commandLineTool) {
-        this.commandLineTool = commandLineTool;
-    }
-
-    public WorkResult execute(WindowsResourceCompileSpec spec) {
-        boolean didWork = false;
-        boolean windowsPathLimitation = OperatingSystem.current().isWindows();
-        CommandLineTool<WindowsResourceCompileSpec> commandLineAssembler = commandLineTool.inWorkDirectory(spec.getObjectFileDir());
-        for (File sourceFile : spec.getSourceFiles()) {
-            WorkResult result = commandLineAssembler.withArguments(new RcCompilerArgsTransformer(sourceFile, windowsPathLimitation)).execute(spec);
-            didWork |= result.getDidWork();
-        }
-        return new SimpleWorkResult(didWork);
-    }
-
-    private static class RcCompilerArgsTransformer implements ArgsTransformer<WindowsResourceCompileSpec> {
-        private final File inputFile;
-        private boolean windowsPathLengthLimitation;
-
-        public RcCompilerArgsTransformer(File inputFile, boolean windowsPathLengthLimitation) {
-            this.inputFile = inputFile;
-            this.windowsPathLengthLimitation = windowsPathLengthLimitation;
-        }
-
-        public List<String> transform(WindowsResourceCompileSpec spec) {
-            List<String> args = new ArrayList<String>();
-            args.add("/nologo");
-            args.add("/fo");
-            args.add(getOutputFile(spec).getAbsolutePath());
-            for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
-                args.add("/D" + macroArg);
-            }
-            args.addAll(spec.getAllArgs());
-            for (File file : spec.getIncludeRoots()) {
-                args.add("/I" + file.getAbsolutePath());
-            }
-            args.add(inputFile.getAbsolutePath());
-
-            return args;
-        }
-
-        private File getOutputFile(WindowsResourceCompileSpec spec) {
-            String outputFileName = FilenameUtils.getBaseName(inputFile.getName()) + ".res";
-            String compactMD5 = HashUtil.createCompactMD5(inputFile.getAbsolutePath());
-            File outputDirectory = new File(spec.getObjectFileDir(), compactMD5);
-            if(!outputDirectory.exists()){
-                outputDirectory.mkdir();
-            }
-            File outputFile = new File(outputDirectory, outputFileName);
-            return windowsPathLengthLimitation ? FileUtils.assertInWindowsPathLengthLimitation(outputFile) : outputFile;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdk.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdk.java
deleted file mode 100644
index b5ba454..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdk.java
+++ /dev/null
@@ -1,138 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.api.Named;
-import org.gradle.nativebinaries.platform.Platform;
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal;
-import org.gradle.util.VersionNumber;
-
-import java.io.File;
-
-public class WindowsSdk implements Named {
-    private static final String[] BINPATHS_X86 = {
-        "bin/x86",
-        "Bin"
-    };
-    private static final String[] BINPATHS_AMD64 = {
-        "bin/x64"
-    };
-    private static final String[] BINPATHS_IA64 = {
-        "bin/IA64"
-    };
-    private static final String[] BINPATHS_ARM = {
-        "bin/arm"
-    };
-    private static final String LIBPATH_SDK8 = "Lib/win8/";
-    private static final String LIBPATH_SDK81 = "Lib/winv6.3/um/";
-    private static final String[] LIBPATHS_X86 = {
-        LIBPATH_SDK81 + "x86",
-        LIBPATH_SDK8 + "x86",
-        "lib"
-    };
-    private static final String[] LIBPATHS_AMD64 = {
-        LIBPATH_SDK81 + "x64",
-        LIBPATH_SDK8 + "x64",
-        "lib/x64"
-    };
-    private static final String[] LIBPATHS_IA64 = {
-        "lib/IA64"
-    };
-    private static final String[] LIBPATHS_ARM = {
-        LIBPATH_SDK81 + "arm",
-        LIBPATH_SDK8 + "arm"
-    };
-
-    private final File baseDir;
-    private final VersionNumber version;
-    private final String name;
-
-    public WindowsSdk(File baseDir, VersionNumber version, String name) {
-        this.baseDir = baseDir;
-        this.version = version;
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public VersionNumber getVersion() {
-        return version;
-    }
-
-    public File getResourceCompiler(Platform platform) {
-        return new File(getBinDir(platform), "rc.exe");
-    }
-
-    public File getBinDir(Platform platform) {
-        if (architecture(platform).isAmd64()) {
-            return getAvailableFile(BINPATHS_AMD64);
-        }
-        if (architecture(platform).isIa64()) {
-            return getAvailableFile(BINPATHS_IA64);
-        }
-        if (architecture(platform).isArm()) {
-            return getAvailableFile(BINPATHS_ARM);
-        }
-        return getAvailableFile(BINPATHS_X86);
-    }
-
-    public File[] getIncludeDirs() {
-        File[] includesSdk8 = new File[] {
-            new File(baseDir, "Include/shared"),
-            new File(baseDir, "Include/um")
-        };
-        for (File file : includesSdk8) {
-            if (!file.isDirectory()) {
-                return new File[] {
-                    new File(baseDir, "Include")
-                };
-            }
-        }
-        return includesSdk8;
-    }
-
-    public File getLibDir(Platform platform) {
-        if (architecture(platform).isAmd64()) {
-            return getAvailableFile(LIBPATHS_AMD64);
-        }
-        if (architecture(platform).isIa64()) {
-            return getAvailableFile(LIBPATHS_IA64);
-        }
-        if (architecture(platform).isArm()) {
-            return getAvailableFile(LIBPATHS_ARM);
-        }
-        return getAvailableFile(LIBPATHS_X86);
-    }
-
-    private ArchitectureInternal architecture(Platform platform) {
-        return (ArchitectureInternal) platform.getArchitecture();
-    }
-
-    private File getAvailableFile(String... candidates) {
-        for (String candidate : candidates) {
-            File file = new File(baseDir, candidate);
-            if (file.isDirectory()) {
-                return file;
-            }
-        }
-
-        return new File(baseDir, candidates[0]);
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdkLocator.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdkLocator.java
deleted file mode 100644
index 9a223aa..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/WindowsSdkLocator.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.toolchain.internal.msvcpp;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult;
-
-import java.io.File;
-
-public interface WindowsSdkLocator {
-
-    SearchResult locateWindowsSdks(File candidate);
-
-    interface SearchResult extends ToolSearchResult {
-        WindowsSdk getSdk();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/plugins/StandardToolChainsPlugin.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/plugins/StandardToolChainsPlugin.java
deleted file mode 100644
index 157e2b3..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/plugins/StandardToolChainsPlugin.java
+++ /dev/null
@@ -1,34 +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.nativebinaries.toolchain.internal.plugins;
-
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.nativebinaries.toolchain.plugins.ClangCompilerPlugin;
-import org.gradle.nativebinaries.toolchain.plugins.GccCompilerPlugin;
-import org.gradle.nativebinaries.toolchain.plugins.MicrosoftVisualCppPlugin;
-
-/**
- * Registers the standard tool chains.
- */
-public class StandardToolChainsPlugin implements Plugin<Project> {
-    public void apply(Project project) {
-        project.getPlugins().apply(MicrosoftVisualCppPlugin.class);
-        project.getPlugins().apply(GccCompilerPlugin.class);
-        project.getPlugins().apply(ClangCompilerPlugin.class);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultTool.java
deleted file mode 100644
index e2feb02..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultTool.java
+++ /dev/null
@@ -1,58 +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.nativebinaries.toolchain.internal.tools;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.internal.Actions;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class DefaultTool implements GccToolInternal {
-    private final ToolType toolType;
-    private String executable;
-    private List<Action<? super List<String>>> argActions = new ArrayList<Action<? super List<String>>>();
-
-    public DefaultTool(ToolType toolType, String defaultExecutable) {
-        this.toolType = toolType;
-        this.executable = defaultExecutable;
-    }
-
-    public ToolType getToolType() {
-        return toolType;
-    }
-
-    public String getExecutable() {
-        return executable;
-    }
-
-    public void setExecutable(String file) {
-        executable = file;
-    }
-
-    // TODO:DAZ Decorate class and use an action parameter
-    public void withArguments(Closure arguments) {
-        Action<List<String>> action = new ClosureBackedAction<List<String>>(arguments);
-        argActions.add(action);
-    }
-
-    public Action<List<String>> getArgAction() {
-        return Actions.composite(argActions);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultToolRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultToolRegistry.java
deleted file mode 100644
index 7e2f7ad..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/DefaultToolRegistry.java
+++ /dev/null
@@ -1,35 +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.nativebinaries.toolchain.internal.tools;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class DefaultToolRegistry implements ToolRegistry {
-    private final Map<ToolType, GccToolInternal> gccTools = new HashMap<ToolType, GccToolInternal>();
-
-    // TODO:DAZ Should not need default executable here?
-    public void register(ToolType toolType, String defaultExecutable) {
-        DefaultTool tool = new DefaultTool(toolType, defaultExecutable);
-        gccTools.put(toolType, tool);
-    }
-
-    public GccToolInternal getTool(ToolType toolType) {
-        return gccTools.get(toolType);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/GccToolInternal.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/GccToolInternal.java
deleted file mode 100644
index c803ddb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/GccToolInternal.java
+++ /dev/null
@@ -1,29 +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.nativebinaries.toolchain.internal.tools;
-
-import org.gradle.api.Action;
-import org.gradle.nativebinaries.toolchain.GccTool;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-import java.util.List;
-
-public interface GccToolInternal extends GccTool {
-
-    ToolType getToolType();
-
-    Action<List<String>> getArgAction();
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformGccTool.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformGccTool.java
deleted file mode 100644
index efee0bc..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformGccTool.java
+++ /dev/null
@@ -1,58 +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.nativebinaries.toolchain.internal.tools;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.internal.Actions;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-import java.util.List;
-
-public class PlatformGccTool implements GccToolInternal {
-    private final GccToolInternal baseTool;
-    private final List<String> platformArgs;
-
-    public PlatformGccTool(GccToolInternal baseTool, List<String> platformArgs) {
-        this.baseTool = baseTool;
-        this.platformArgs = platformArgs;
-    }
-
-    public ToolType getToolType() {
-        return baseTool.getToolType();
-    }
-
-    public String getExecutable() {
-        return baseTool.getExecutable();
-    }
-
-    public void setExecutable(String file) {
-        throw new UnsupportedOperationException();
-    }
-
-    public Action<List<String>> getArgAction() {
-        Action<? super List<String>> platformArgsAction = new Action<List<String>>() {
-            public void execute(List<String> strings) {
-                strings.addAll(0, platformArgs);
-            }
-        };
-        return Actions.composite(baseTool.getArgAction(), platformArgsAction);
-    }
-
-    public void withArguments(Closure arguments) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformToolRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformToolRegistry.java
deleted file mode 100644
index ea32d95..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/PlatformToolRegistry.java
+++ /dev/null
@@ -1,48 +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.nativebinaries.toolchain.internal.tools;
-
-import org.gradle.nativebinaries.toolchain.GccTool;
-import org.gradle.nativebinaries.toolchain.TargetPlatformConfiguration;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class PlatformToolRegistry implements ToolRegistry {
-    private final Map<ToolType, GccToolInternal> gccTools = new HashMap<ToolType, GccToolInternal>();
-
-    public PlatformToolRegistry(ToolRegistry defaultTools, TargetPlatformConfiguration configuration) {
-        // TODO:DAZ Replace TargetPlatformConfiguration with an action that applies to the entire registry.
-        // This will involve exposing the tools by their 'name' in a container, and applying the action to that container.
-        register(defaultTools.getTool(ToolType.CPP_COMPILER), configuration.getCppCompilerArgs());
-        register(defaultTools.getTool(ToolType.C_COMPILER), configuration.getCCompilerArgs());
-        register(defaultTools.getTool(ToolType.OBJECTIVECPP_COMPILER), configuration.getObjectiveCppCompilerArgs());
-        register(defaultTools.getTool(ToolType.OBJECTIVEC_COMPILER), configuration.getObjectiveCCompilerArgs());
-        register(defaultTools.getTool(ToolType.ASSEMBLER), configuration.getAssemblerArgs());
-        register(defaultTools.getTool(ToolType.LINKER), configuration.getLinkerArgs());
-        register(defaultTools.getTool(ToolType.STATIC_LIB_ARCHIVER), configuration.getStaticLibraryArchiverArgs());
-    }
-
-    private GccTool register(GccToolInternal baseTool, List<String> platformArgs) {
-        return gccTools.put(baseTool.getToolType(), new PlatformGccTool(baseTool, platformArgs));
-    }
-
-    public GccToolInternal getTool(ToolType toolType) {
-        return gccTools.get(toolType);
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolRegistry.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolRegistry.java
deleted file mode 100644
index 4edfa3a..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolRegistry.java
+++ /dev/null
@@ -1,22 +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.nativebinaries.toolchain.internal.tools;
-
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-
-public interface ToolRegistry {
-    GccToolInternal getTool(ToolType toolType);
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPath.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPath.java
deleted file mode 100644
index 8a54fa1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPath.java
+++ /dev/null
@@ -1,135 +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.nativebinaries.toolchain.internal.tools;
-
-import org.gradle.api.GradleException;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.internal.text.TreeFormatter;
-import org.gradle.nativebinaries.toolchain.internal.ToolType;
-import org.gradle.nativebinaries.toolchain.internal.gcc.CommandLineToolSearchResult;
-import org.gradle.util.TreeVisitor;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ToolSearchPath {
-    private final Map<String, CommandLineToolSearchResult> executables = new HashMap<String, CommandLineToolSearchResult>();
-    private final List<File> pathEntries = new ArrayList<File>();
-
-    private final OperatingSystem operatingSystem;
-
-    public ToolSearchPath(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 CommandLineToolSearchResult locate(ToolType key, String exeName) {
-        CommandLineToolSearchResult result = executables.get(exeName);
-        if (result == null) {
-            File exe = findExecutable(operatingSystem, exeName);
-            result = exe == null || !exe.isFile() ? new MissingTool(key, exeName, pathEntries) : new FoundTool(exe);
-            executables.put(exeName, result);
-        }
-        return result;
-    }
-
-    protected File findExecutable(OperatingSystem operatingSystem, String name) {
-        if (!pathEntries.isEmpty()) {
-            String exeName = operatingSystem.getExecutableName(name);
-            for (File pathEntry : pathEntries) {
-                File candidate = new File(pathEntry, exeName);
-                if (candidate.isFile()) {
-                    return candidate;
-                }
-            }
-            return null;
-        } else {
-            return operatingSystem.findInPath(name);
-        }
-    }
-
-    private static class FoundTool implements CommandLineToolSearchResult {
-        private final File tool;
-
-        private FoundTool(File tool) {
-            this.tool = tool;
-        }
-
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public File getTool() {
-            return tool;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-        }
-    }
-
-    private static class MissingTool implements CommandLineToolSearchResult {
-        private final ToolType type;
-        private final String exeName;
-        private final List<File> path;
-
-        private MissingTool(ToolType type, String exeName, List<File> path) {
-            this.type = type;
-            this.exeName = exeName;
-            this.path = path;
-        }
-
-        public void explain(TreeVisitor<? super String> visitor) {
-            if (path.isEmpty()) {
-                visitor.node(String.format("Could not find %s '%s' in system path.", type.getToolName(), exeName));
-            } else {
-                visitor.node(String.format("Could not find %s '%s'. Searched in", type.getToolName(), exeName));
-                visitor.startChildren();
-                for (File location : path) {
-                    visitor.node(location.toString());
-                }
-                visitor.endChildren();
-            }
-        }
-
-        public File getTool() {
-            TreeFormatter formatter = new TreeFormatter();
-            explain(formatter);
-            throw new GradleException(formatter.toString());
-        }
-
-        public boolean isAvailable() {
-            return false;
-        }
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/package-info.java
deleted file mode 100644
index 6e0a248..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Classes that allow C++ tool chains to be configured.
- */
-package org.gradle.nativebinaries.toolchain;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPlugin.groovy
deleted file mode 100644
index 36da872..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPlugin.groovy
+++ /dev/null
@@ -1,62 +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.nativebinaries.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.os.OperatingSystem
-import org.gradle.model.ModelRule
-import org.gradle.model.ModelRules
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-import org.gradle.nativebinaries.toolchain.Clang
-import org.gradle.nativebinaries.toolchain.internal.clang.ClangToolChain
-import org.gradle.process.internal.ExecActionFactory
-
-import javax.inject.Inject
-/**
- * A {@link Plugin} which makes the <a href="http://clang.llvm.org">Clang</a> compiler available for compiling C/C++ code.
- */
- at Incubating
-class ClangCompilerPlugin implements Plugin<Project> {
-    private final FileResolver fileResolver
-    private final ExecActionFactory execActionFactory
-    private final Instantiator instantiator
-    private final ModelRules modelRules
-
-    @Inject
-    ClangCompilerPlugin(FileResolver fileResolver, ExecActionFactory execActionFactory, ModelRules modelRules, Instantiator instantiator) {
-        this.execActionFactory = execActionFactory
-        this.fileResolver = fileResolver
-        this.instantiator = instantiator
-        this.modelRules = modelRules
-    }
-
-    void apply(Project project) {
-        project.plugins.apply(NativeBinariesPlugin)
-
-        modelRules.rule(new ModelRule() {
-            void addToolChain(ToolChainRegistryInternal toolChainRegistry) {
-                toolChainRegistry.registerFactory(Clang, { String name ->
-                    return instantiator.newInstance(ClangToolChain, name, OperatingSystem.current(), fileResolver, execActionFactory)
-                })
-                toolChainRegistry.registerDefaultToolChain(ClangToolChain.DEFAULT_NAME, Clang)
-            }
-        })
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPlugin.groovy
deleted file mode 100644
index 89a6ce1..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPlugin.groovy
+++ /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.nativebinaries.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.os.OperatingSystem
-import org.gradle.model.ModelRule
-import org.gradle.model.ModelRules
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-import org.gradle.nativebinaries.toolchain.Gcc
-import org.gradle.nativebinaries.toolchain.internal.gcc.GccToolChain
-import org.gradle.process.internal.ExecActionFactory
-
-import javax.inject.Inject
-/**
- * A {@link Plugin} which makes the <a href="http://gcc.gnu.org/">GNU GCC/G++ compiler</a> available for compiling C/C++ code.
- */
- at Incubating
-class GccCompilerPlugin implements Plugin<Project> {
-    private final FileResolver fileResolver
-    private final ExecActionFactory execActionFactory
-    private final Instantiator instantiator
-    private final ModelRules modelRules;
-
-    @Inject
-    GccCompilerPlugin(FileResolver fileResolver, ExecActionFactory execActionFactory, ModelRules modelRules, Instantiator instantiator) {
-        this.execActionFactory = execActionFactory
-        this.fileResolver = fileResolver
-        this.modelRules = modelRules
-        this.instantiator = instantiator
-    }
-
-    void apply(Project project) {
-        project.plugins.apply(NativeBinariesPlugin)
-
-        modelRules.rule(new ModelRule() {
-            void addGccToolChain(ToolChainRegistryInternal toolChainRegistry) {
-                toolChainRegistry.registerFactory(Gcc, { String name ->
-                    return instantiator.newInstance(GccToolChain, name, OperatingSystem.current(), fileResolver, execActionFactory)
-                })
-                toolChainRegistry.registerDefaultToolChain(GccToolChain.DEFAULT_NAME, Gcc)
-            }
-        })
-    }
-
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPlugin.groovy b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPlugin.groovy
deleted file mode 100755
index f61abfb..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPlugin.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.nativebinaries.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.os.OperatingSystem
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.model.ModelRule
-import org.gradle.model.ModelRules
-import org.gradle.nativebinaries.plugins.NativeBinariesPlugin
-import org.gradle.nativebinaries.toolchain.VisualCpp
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualCppToolChain
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualStudioLocator
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.WindowsSdkLocator
-import org.gradle.process.internal.ExecActionFactory
-
-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;
-    private final ExecActionFactory execActionFactory
-    private final Instantiator instantiator
-    private final ModelRules modelRules
-    private final OperatingSystem operatingSystem
-    private final VisualStudioLocator visualStudioLocator
-    private final WindowsSdkLocator windowsSdkLocator
-
-    @Inject
-    MicrosoftVisualCppPlugin(FileResolver fileResolver, ExecActionFactory execActionFactory, ModelRules modelRules, Instantiator instantiator, OperatingSystem operatingSystem,
-                             VisualStudioLocator visualStudioLocator, WindowsSdkLocator windowsSdkLocator) {
-        this.windowsSdkLocator = windowsSdkLocator
-        this.visualStudioLocator = visualStudioLocator
-        this.operatingSystem = operatingSystem
-        this.execActionFactory = execActionFactory
-        this.fileResolver = fileResolver
-        this.instantiator = instantiator
-        this.modelRules = modelRules
-    }
-
-    void apply(Project project) {
-        project.plugins.apply(NativeBinariesPlugin)
-
-        modelRules.rule(new ModelRule() {
-            void addToolChain(ToolChainRegistryInternal toolChainRegistry) {
-                toolChainRegistry.registerFactory(VisualCpp, { String name ->
-                    return instantiator.newInstance(VisualCppToolChain, name, operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator)
-                })
-                toolChainRegistry.registerDefaultToolChain(VisualCppToolChain.DEFAULT_NAME, VisualCpp)
-            }
-        })
-    }
-}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/package-info.java b/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/package-info.java
deleted file mode 100644
index 3b868d4..0000000
--- a/subprojects/cpp/src/main/groovy/org/gradle/nativebinaries/toolchain/plugins/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Built-in tool chain support.
- */
-package org.gradle.nativebinaries.toolchain.plugins;
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/assembler.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/assembler.properties
deleted file mode 100644
index 4746f21..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/assembler.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.assembler.plugins.AssemblerPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/c.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/c.properties
deleted file mode 100644
index dc3fd3c..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/c.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.c.plugins.CPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cpp.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cpp.properties
deleted file mode 100644
index 9108bc0..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cpp.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.cpp.plugins.CppPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cunit.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cunit.properties
deleted file mode 100644
index 8b6f37e..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/cunit.properties
+++ /dev/null
@@ -1,17 +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.
-#
-
-implementation-class=org.gradle.nativebinaries.test.cunit.plugins.CUnitPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/native-binaries.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/native-binaries.properties
deleted file mode 100644
index 838bbdd..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/native-binaries.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.plugins.NativeBinariesPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-c.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-c.properties
deleted file mode 100644
index a190690..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-c.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.objectivec.plugins.ObjectiveCPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-cpp.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-cpp.properties
deleted file mode 100644
index 9e9112d..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/objective-cpp.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.objectivecpp.plugins.ObjectiveCppPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/windows-resources.properties b/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/windows-resources.properties
deleted file mode 100644
index b41a378..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/windows-resources.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.nativebinaries.language.rc.plugins.WindowsResourcesPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/cpp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
deleted file mode 100644
index 71acde7..0000000
--- a/subprojects/cpp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
+++ /dev/null
@@ -1 +0,0 @@
-org.gradle.nativebinaries.internal.NativeBinaryServices
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy
deleted file mode 100644
index a76d9fd..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy
+++ /dev/null
@@ -1,58 +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.ide.cdt.model
-
-import spock.lang.*
-
-import org.gradle.api.Project
-import org.gradle.util.TestUtil
-
-// very loose test, but I'm not expecting it to stay around
- at Ignore
-class CprojectSettingsSpec extends Specification {
-
-    Project project = TestUtil.createRootProject()
-
-    def descriptor = new CprojectDescriptor()
-    def settings = new CprojectSettings()
-
-    def "wire in includes"() {
-        given:
-        project.apply plugin: 'cpp-exe'
-        settings.binary = project.executables.main
-        descriptor.loadDefaults()
-
-        expect:
-        descriptor.getRootCppCompilerTools().each { compiler ->
-            def includePathsOption = descriptor.getOrCreateIncludePathsOption(compiler)
-            assert includePathsOption.listOptionValue.size() == 0
-        }
-
-        when:
-        settings.applyTo(descriptor)
-        def baos = new ByteArrayOutputStream()
-        descriptor.store(baos)
-        descriptor.load(new ByteArrayInputStream(baos.toByteArray()))
-
-        then:
-        descriptor.getRootCppCompilerTools().each { compiler ->
-            def includePathsOption = descriptor.getOrCreateIncludePathsOption(compiler)
-            assert includePathsOption.listOptionValue.size() == 1
-        }
-    }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy
deleted file mode 100644
index 4927569..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy
+++ /dev/null
@@ -1,115 +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.ide.visualstudio.internal
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import spock.lang.Specification
-
-class DefaultVisualStudioProjectTest extends Specification {
-    private DirectInstantiator instantiator = new DirectInstantiator()
-    def component = Mock(ProjectNativeComponentInternal)
-    def fileResolver = Mock(FileResolver)
-    def vsProject = new DefaultVisualStudioProject("projectName", component, fileResolver, instantiator)
-
-    def "names"() {
-        final projectFile = new File("project")
-        final filtersFile = new File("filters")
-        when:
-        fileResolver.resolve("projectName.vcxproj") >> projectFile
-        fileResolver.resolve("projectName.vcxproj.filters") >> filtersFile
-
-        then:
-        vsProject.name == "projectName"
-        vsProject.projectFile.location == projectFile
-        vsProject.filtersFile.location == filtersFile
-    }
-
-    def "includes source files from all source sets"() {
-        when:
-        def file1 = Mock(File)
-        def file2 = Mock(File)
-        def file3 = Mock(File)
-        def sourceSet1 = sourceSet(file1, file2)
-        def sourceSet2 = sourceSet(file3)
-        vsProject.source([sourceSet1, sourceSet2])
-
-        then:
-        vsProject.sourceFiles == [file1, file2, file3]
-    }
-
-    def "includes header files from all source sets"() {
-        when:
-        def file1 = Mock(File)
-        def file2 = Mock(File)
-        def file3 = Mock(File)
-        def file4 = Mock(File)
-        def sourceSet1 = headerSourceSet([file1, file2])
-        def sourceSet2 = headerSourceSet([file3], [file4])
-        vsProject.source([sourceSet1, sourceSet2])
-
-        then:
-        vsProject.headerFiles == [file1, file2, file3, file4]
-    }
-
-    def "has consistent uuid for same mapped component"() {
-        when:
-        def sameComponent = Mock(ProjectNativeComponentInternal)
-        def otherComponent = Mock(ProjectNativeComponentInternal)
-
-        def sameProject = new DefaultVisualStudioProject("projectName", component, fileResolver, instantiator)
-        def samePath = new DefaultVisualStudioProject("projectName", sameComponent, fileResolver, instantiator)
-        def differentPath = new DefaultVisualStudioProject("projectName", otherComponent, fileResolver, instantiator)
-        def differentName = new DefaultVisualStudioProject("otherProject", component, fileResolver, instantiator)
-
-        and:
-        component.projectPath >> ":projectPath"
-        sameComponent.projectPath >> ":projectPath"
-        otherComponent.projectPath >> ":otherProjectPath"
-
-        then:
-        vsProject.uuid == sameProject.uuid
-        vsProject.uuid == samePath.uuid
-        vsProject.uuid != differentPath.uuid
-        vsProject.uuid != differentName.uuid
-    }
-
-    private LanguageSourceSet sourceSet(File... files) {
-        def allFiles = files as Set
-        def sourceSet = Mock(LanguageSourceSet)
-        def sourceDirs = Mock(SourceDirectorySet)
-        1 * sourceSet.source >> sourceDirs
-        1 * sourceDirs.files >> allFiles
-        return sourceSet
-    }
-
-    private HeaderExportingSourceSet headerSourceSet(List<File> exportedHeaders, List<File> implicitHeaders = []) {
-        def exportedHeaderFiles = exportedHeaders as Set
-        def implicitHeaderFiles = implicitHeaders as Set
-        def sourceSet = Mock(HeaderExportingSourceSet)
-        def sourceDirs = Mock(SourceDirectorySet)
-        1 * sourceSet.exportedHeaders >> sourceDirs
-        1 * sourceDirs.files >> exportedHeaderFiles
-        def implicitHeaderSet = Mock(SourceDirectorySet)
-        1 * sourceSet.implicitHeaders >> implicitHeaderSet
-        1 * implicitHeaderSet.files >> implicitHeaderFiles
-        return sourceSet
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy
deleted file mode 100644
index bcf2c85..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy
+++ /dev/null
@@ -1,196 +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.ide.visualstudio.internal
-
-import org.gradle.api.file.FileCollection
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.plugins.ExtensionAware
-import org.gradle.api.plugins.ExtensionContainer
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.nativebinaries.Executable
-import org.gradle.nativebinaries.ExecutableBinary
-import org.gradle.nativebinaries.NativeDependencySet
-import org.gradle.nativebinaries.internal.DefaultFlavor
-import org.gradle.nativebinaries.internal.DefaultFlavorContainer
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import org.gradle.nativebinaries.language.PreprocessingTool
-import org.gradle.nativebinaries.platform.Platform
-import spock.lang.Specification
-
-class VisualStudioProjectConfigurationTest extends Specification {
-    final flavor = new DefaultFlavor("flavor1")
-    def flavors = new DefaultFlavorContainer(new DirectInstantiator())
-    def exe = Mock(ExecutableInternal) {
-        getFlavors() >> flavors
-    }
-    def extensions = Mock(ExtensionContainer)
-    def platform = Mock(Platform)
-    def exeBinary = Mock(TestExecutableBinary) {
-        getExtensions() >> extensions
-        getFlavor() >> flavor
-        getComponent() >> exe
-        getTargetPlatform() >> platform
-    }
-    def configuration = new VisualStudioProjectConfiguration(null, "configName", "platformName", exeBinary)
-    def cppCompiler = Mock(PreprocessingTool)
-    def cCompiler = Mock(PreprocessingTool)
-    def rcCompiler = Mock(PreprocessingTool)
-
-    def "setup"() {
-        flavors.add(flavor)
-    }
-
-    def "configuration has supplied names"() {
-        expect:
-        configuration.configurationName == "configName"
-        configuration.platformName == "platformName"
-        configuration.name == "configName|platformName"
-    }
-
-    def "configuration tasks are binary tasks"() {
-        when:
-        exeBinary.name >> "exeBinary"
-        exeBinary.component >> exe
-        exe.projectPath >> ":project-path"
-
-        then:
-        configuration.buildTask == ":project-path:exeBinary"
-        configuration.cleanTask == ":project-path:clean"
-    }
-
-    def "compiler defines are taken from cpp compiler configuration"() {
-        when:
-        1 * extensions.findByName('cCompiler') >> null
-        1 * extensions.findByName('cppCompiler') >> cppCompiler
-        1 * extensions.findByName('rcCompiler') >> null
-        cppCompiler.macros >> [foo: "bar", empty: null]
-
-        then:
-        configuration.compilerDefines == ["foo=bar", "empty"]
-    }
-
-    def "compiler defines are taken from c compiler configuration"() {
-        when:
-        1 * extensions.findByName('cCompiler') >> cCompiler
-        1 * extensions.findByName('cppCompiler') >> null
-        1 * extensions.findByName('rcCompiler') >> null
-        cCompiler.macros >> [foo: "bar", another: null]
-
-        then:
-        configuration.compilerDefines == ["foo=bar", "another"]
-    }
-
-    def "resource defines are taken from rcCompiler config"() {
-        when:
-        1 * extensions.findByName('cCompiler') >> null
-        1 * extensions.findByName('cppCompiler') >> null
-        1 * extensions.findByName('rcCompiler') >> rcCompiler
-        rcCompiler.macros >> [foo: "bar", empty: null]
-
-        then:
-        configuration.compilerDefines == ["foo=bar", "empty"]
-    }
-
-    def "compiler defines are taken from cpp, c and rc compiler configurations combined"() {
-        when:
-        1 * extensions.findByName('cppCompiler') >> null
-        1 * extensions.findByName('cCompiler') >> null
-        1 * extensions.findByName('rcCompiler') >> null
-
-        then:
-        configuration.compilerDefines == []
-
-        when:
-        1 * extensions.findByName('cCompiler') >> cCompiler
-        1 * extensions.findByName('cppCompiler') >> cppCompiler
-        1 * extensions.findByName('rcCompiler') >> rcCompiler
-        cCompiler.macros >> [_c: null]
-        cppCompiler.macros >> [foo: "bar", _cpp: null]
-        rcCompiler.macros >> [rc: "defined", rc_empty: null]
-
-        then:
-        configuration.compilerDefines == ["_c", "foo=bar", "_cpp", "rc=defined", "rc_empty"]
-    }
-
-    def "include paths include component headers"() {
-        final sources = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
-
-        when:
-        exeBinary.source >> sources
-        exeBinary.libs >> []
-
-        then:
-        configuration.includePaths == []
-
-        when:
-        def file1 = Mock(File)
-        def file2 = Mock(File)
-        def file3 = Mock(File)
-        def sourceSet = Mock(LanguageSourceSet)
-        def sourceSet1 = headerSourceSet(file1, file2)
-        def sourceSet2 = headerSourceSet(file3)
-        sources.addAll(sourceSet, sourceSet1, sourceSet2)
-
-        and:
-        exeBinary.source >> sources
-        exeBinary.libs >> []
-
-        then:
-        configuration.includePaths == [file1, file2, file3]
-    }
-
-    def "include paths include library headers"() {
-        when:
-        def file1 = Mock(File)
-        def file2 = Mock(File)
-        def file3 = Mock(File)
-
-        def deps1 = dependencySet(file1, file2)
-        def deps2 = dependencySet(file3)
-
-        exeBinary.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
-        exeBinary.libs >> [deps1, deps2]
-
-        then:
-        configuration.includePaths == [file1, file2, file3]
-    }
-
-    private HeaderExportingSourceSet headerSourceSet(File... files) {
-        def allFiles = files as Set
-        def sourceSet = Mock(HeaderExportingSourceSet)
-        def sourceDirs = Mock(SourceDirectorySet)
-        1 * sourceSet.exportedHeaders >> sourceDirs
-        1 * sourceDirs.srcDirs >> allFiles
-        return sourceSet
-    }
-
-    private NativeDependencySet dependencySet(File... files) {
-        def deps = Mock(NativeDependencySet)
-        def fileCollection = Mock(FileCollection)
-        deps.includeRoots >> fileCollection
-        fileCollection.files >> (files as Set)
-        return deps
-    }
-
-    interface ExecutableInternal extends Executable, ProjectNativeComponentInternal {}
-    interface TestExecutableBinary extends ProjectNativeBinaryInternal, ExecutableBinary, ExtensionAware {}
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy
deleted file mode 100644
index 90e07dd..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy
+++ /dev/null
@@ -1,142 +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.ide.visualstudio.internal
-import org.gradle.language.base.internal.BinaryNamingScheme
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import org.gradle.nativebinaries.platform.Architecture
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.platform.internal.ArchitectureNotationParser
-import spock.lang.Specification
-
-class VisualStudioProjectMapperTest extends Specification {
-    def mapper = new VisualStudioProjectMapper()
-
-    def executable = Mock(ExecutableInternal)
-    ExecutableBinaryInternal executableBinary
-    def library = Mock(LibraryInternal)
-    def namingScheme = Mock(BinaryNamingScheme)
-
-    def flavorOne = Mock(Flavor)
-    def buildTypeOne = Mock(BuildType)
-    def buildTypeTwo = Mock(BuildType)
-    def platformOne = Mock(Platform)
-
-    def setup() {
-        executableBinary = createExecutableBinary("exeBinaryName", buildTypeOne, platformOne)
-        executableBinary.namingScheme >> namingScheme
-
-        executable.name >> "exeName"
-        library.name >> "libName"
-
-        flavorOne.name >> "flavorOne"
-        buildTypeOne.name >> "buildTypeOne"
-        buildTypeTwo.name >> "buildTypeTwo"
-        platformOne.name >> "platformOne"
-        platformOne.architecture >> arch("i386")
-    }
-
-    def "maps executable binary to visual studio project"() {
-        when:
-        executable.projectPath >> ":"
-        namingScheme.variantDimensions >> []
-
-        then:
-        checkNames executableBinary, "exeNameExe", 'buildTypeOne', 'Win32'
-    }
-
-    def "maps library binary types to visual studio projects"() {
-        when:
-        def sharedLibraryBinary = libraryBinary(SharedLibraryBinaryInternal)
-        def staticLibraryBinary = libraryBinary(StaticLibraryBinaryInternal)
-
-        library.projectPath >> ":"
-        namingScheme.variantDimensions >> []
-
-        then:
-        checkNames sharedLibraryBinary, "libNameDll", 'buildTypeOne', 'Win32'
-        checkNames staticLibraryBinary, "libNameLib", 'buildTypeOne', 'Win32'
-    }
-
-    def "includes project path in visual studio project name"() {
-        when:
-        executable.projectPath >> ":subproject:name"
-
-        and:
-        namingScheme.variantDimensions >> []
-
-        then:
-        checkNames executableBinary, "subproject_name_exeNameExe", 'buildTypeOne', 'Win32'
-    }
-
-    def "uses single variant dimension for configuration name where not empty"() {
-        when:
-        executable.projectPath >> ":"
-        namingScheme.variantDimensions >> ["flavorOne"]
-
-        then:
-        checkNames executableBinary, "exeNameExe", 'flavorOne', 'Win32'
-    }
-
-    def "includes variant dimensions in configuration where component has multiple dimensions"() {
-        when:
-        executable.projectPath >> ":"
-        namingScheme.variantDimensions >> ["platformOne", "buildTypeOne", "flavorOne"]
-
-        then:
-        checkNames executableBinary, "exeNameExe", 'platformOneBuildTypeOneFlavorOne', 'Win32'
-    }
-
-    private def createExecutableBinary(String binaryName, def buildType, def platform) {
-        def binary = Mock(ExecutableBinaryInternal)
-        binary.name >> binaryName
-        binary.component >> executable
-        binary.buildType >> buildType
-        binary.flavor >> flavorOne
-        binary.targetPlatform >> platform
-        return binary
-    }
-
-    private checkNames(def binary, def projectName, def configurationName, def platformName) {
-        def names = mapper.mapToConfiguration(binary)
-        assert names.project == projectName
-        assert names.configuration == configurationName
-        assert names.platform == platformName
-        true
-    }
-
-    private static Architecture arch(String name) {
-        return ArchitectureNotationParser.parser().parseNotation(name)
-    }
-
-    private libraryBinary(Class<? extends LibraryBinary> type) {
-        def binary = Mock(type)
-        binary.component >> library
-        binary.flavor >> flavorOne
-        binary.targetPlatform >> platformOne
-        binary.buildType >> buildTypeOne
-        binary.namingScheme >> namingScheme
-        return binary
-    }
-
-    interface ExecutableInternal extends Executable, ProjectNativeComponentInternal {}
-    interface LibraryInternal extends Library, ProjectNativeComponentInternal {}
-    interface ExecutableBinaryInternal extends ExecutableBinary, ProjectNativeBinaryInternal {}
-    interface SharedLibraryBinaryInternal extends SharedLibraryBinary, ProjectNativeBinaryInternal {}
-    interface StaticLibraryBinaryInternal extends StaticLibraryBinary, ProjectNativeBinaryInternal {}
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy
deleted file mode 100644
index 5444cdc..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy
+++ /dev/null
@@ -1,109 +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.ide.visualstudio.internal
-
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import spock.lang.Specification
-
-class VisualStudioProjectRegistryTest extends Specification {
-    private DefaultDomainObjectSet<LanguageSourceSet> sources = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
-    def fileResolver = Mock(FileResolver)
-    def visualStudioProjectMapper = Mock(VisualStudioProjectMapper)
-    def registry = new VisualStudioProjectRegistry(fileResolver, visualStudioProjectMapper, new DirectInstantiator())
-
-    def executable = Mock(Executable)
-
-    def "creates a matching visual studio project configuration for NativeBinary"() {
-        def executableBinary = Mock(ExecutableInternal)
-        when:
-        visualStudioProjectMapper.mapToConfiguration(executableBinary) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig", "vsPlatform")
-        executableBinary.component >> executable
-        executableBinary.source >> sources
-
-        and:
-        registry.addProjectConfiguration(executableBinary)
-
-        then:
-        def vsConfig = registry.getProjectConfiguration(executableBinary)
-        vsConfig.project.component == executable
-        vsConfig.type == "Makefile"
-        vsConfig.project.name == "vsProject"
-        vsConfig.configurationName == "vsConfig"
-        vsConfig.platformName == "vsPlatform"
-    }
-
-    def "returns same visual studio project configuration for native binaries that share project name"() {
-        def executableBinary1 = Mock(ExecutableInternal)
-        def executableBinary2 = Mock(ExecutableInternal)
-
-        when:
-        visualStudioProjectMapper.mapToConfiguration(executableBinary1) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig1", "vsPlatform")
-        visualStudioProjectMapper.mapToConfiguration(executableBinary2) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig2", "vsPlatform")
-        executableBinary1.source >> sources
-        executableBinary2.source >> sources
-
-        and:
-        registry.addProjectConfiguration(executableBinary1)
-        registry.addProjectConfiguration(executableBinary2)
-
-        then:
-        def vsConfig1 = registry.getProjectConfiguration(executableBinary1)
-        def vsConfig2 = registry.getProjectConfiguration(executableBinary2)
-        vsConfig1.project == vsConfig2.project
-
-        and:
-        vsConfig1.type == "Makefile"
-        vsConfig1.project.name == "vsProject"
-        vsConfig1.configurationName == "vsConfig1"
-        vsConfig1.platformName == "vsPlatform"
-
-        and:
-        vsConfig2.type == "Makefile"
-        vsConfig2.project.name == "vsProject"
-        vsConfig2.configurationName == "vsConfig2"
-        vsConfig2.platformName == "vsPlatform"
-    }
-
-    def "visual studio project contains sources for native binaries for all configurations"() {
-        def executableBinary1 = Mock(ExecutableInternal)
-        def executableBinary2 = Mock(ExecutableInternal)
-        def sourceCommon = Mock(LanguageSourceSet)
-        def source1 = Mock(LanguageSourceSet)
-        def source2 = Mock(LanguageSourceSet)
-
-        when:
-        visualStudioProjectMapper.mapToConfiguration(executableBinary1) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig1", "vsPlatform")
-        visualStudioProjectMapper.mapToConfiguration(executableBinary2) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig2", "vsPlatform")
-        executableBinary1.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet, [sourceCommon, source1])
-        executableBinary2.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet, [sourceCommon, source2])
-
-        and:
-        registry.addProjectConfiguration(executableBinary1)
-        registry.addProjectConfiguration(executableBinary2)
-
-        then:
-        def vsProject = registry.getProjectConfiguration(executableBinary1).project
-        vsProject.sources as List == [sourceCommon, source1, source2]
-    }
-
-    interface ExecutableInternal extends ExecutableBinary, ProjectNativeBinaryInternal {}
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.groovy
deleted file mode 100644
index 3da512a..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.groovy
+++ /dev/null
@@ -1,97 +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.ide.visualstudio.tasks.internal
-import org.gradle.api.Transformer
-import org.gradle.api.internal.xml.XmlTransformer
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import spock.lang.Specification
-
-class VisualStudioFiltersFileTest extends Specification {
-    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-    Transformer<String, File> fileNameTransformer = { it.name } as Transformer<String, File>
-    def filtersFile = new VisualStudioFiltersFile(new XmlTransformer(), fileNameTransformer)
-
-    def "empty filters file"() {
-        when:
-        filtersFile.loadDefaults()
-
-        then:
-        Node sourceFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Source Files'}) as Node
-        sourceFiles.Extensions[0].text() == 'cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx'
-
-        Node headerFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Header Files'}) as Node
-        headerFiles.Extensions[0].text() == 'h;hpp;hxx;hm;inl;inc;xsd'
-
-        Node resourceFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Resource Files'}) as Node
-        resourceFiles.Extensions[0].text() == 'rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav'
-
-        and:
-        itemGroup('Sources').children().isEmpty()
-        itemGroup('Headers').children().isEmpty()
-    }
-
-    def "adds sources and header files"() {
-        when:
-        filtersFile.loadDefaults()
-
-        and:
-        filtersFile.addSource(file("sourceOne"))
-        filtersFile.addSource(file("sourceTwo"))
-
-        filtersFile.addHeader(file("headerOne"))
-        filtersFile.addHeader(file("headerTwo"))
-
-        then:
-        assert sourceFile(0) == "sourceOne"
-        assert sourceFile(1) == "sourceTwo"
-
-        assert headerFile(0) == "headerOne"
-        assert headerFile(1) == "headerTwo"
-    }
-
-    private String sourceFile(int index) {
-        def source = itemGroup('Sources').ClCompile[index]
-        assert source.Filter[0].text() == 'Source Files'
-        return source.'@Include'
-    }
-
-    private String headerFile(int index) {
-        def header = itemGroup('Headers').ClInclude[index]
-        assert header.Filter[0].text() == 'Header Files'
-        return header.'@Include'
-    }
-
-    private Node itemGroup(String label) {
-        return filtersXml.ItemGroup.find({it.'@Label' == label}) as Node
-    }
-
-    private def getFiltersXml() {
-        return new XmlParser().parse(filtersFileContent)
-    }
-
-    private TestFile getFiltersFileContent() {
-        def file = testDirectoryProvider.testDirectory.file("filters.xml")
-        filtersFile.store(file)
-        return file
-    }
-
-    private TestFile file(String name) {
-        testDirectoryProvider.testDirectory.file(name)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy
deleted file mode 100644
index a864b6e..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy
+++ /dev/null
@@ -1,118 +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.ide.visualstudio.tasks.internal
-import org.gradle.api.Transformer
-import org.gradle.api.internal.xml.XmlTransformer
-import org.gradle.ide.visualstudio.fixtures.ProjectFile
-import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import spock.lang.Specification
-
-class VisualStudioProjectFileTest extends Specification {
-    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-    Transformer<String, File> fileNameTransformer = { it.name } as Transformer<String, File>
-    def generator = new VisualStudioProjectFile(new XmlTransformer(), fileNameTransformer)
-
-    def "setup"() {
-        generator.loadDefaults()
-    }
-
-    def "empty project file"() {
-        expect:
-        projectFile.projectConfigurations.isEmpty()
-        projectFile.sourceFiles == []
-        projectFile.headerFiles == []
-    }
-
-    def "set project uuid"() {
-        when:
-        generator.setProjectUuid("THE_PROJECT_UUID")
-
-        then:
-        projectFile.projectGuid == "THE_PROJECT_UUID"
-    }
-
-    def "add source and headers"() {
-        when:
-        generator.addSourceFile(file("sourceOne"))
-        generator.addSourceFile(file("sourceTwo"))
-
-        generator.addHeaderFile(file("headerOne"))
-        generator.addHeaderFile(file("headerTwo"))
-
-        then:
-        projectFile.sourceFiles == ["sourceOne", "sourceTwo"]
-        projectFile.headerFiles == ["headerOne", "headerTwo"]
-    }
-
-    def "add configurations"() {
-        when:
-        generator.gradleCommand = 'GRADLE'
-        generator.addConfiguration(configuration("debugWin32", "Win32", ["foo", "bar"], ["include1", "include2"]))
-        generator.addConfiguration(configuration("releaseWin32", "Win32", ["foo", "bar"], ["include1", "include2", "include3"]))
-        generator.addConfiguration(configuration("debugX64", "x64", ["foo", "bar"], ["include1", "include2"]))
-
-        then:
-        final configurations = projectFile.projectConfigurations
-        configurations.size() == 3
-        with (configurations['debugWin32']) {
-            name == 'debugWin32'
-            platformName == 'Win32'
-            macros == "foo;bar"
-            includePath == "include1;include2"
-            buildCommand == "GRADLE debugWin32Build"
-        }
-        with (configurations['releaseWin32']) {
-            name == 'releaseWin32'
-            platformName == 'Win32'
-            macros == "foo;bar"
-            includePath == "include1;include2;include3"
-            buildCommand == "GRADLE releaseWin32Build"
-        }
-        with (configurations['debugX64']) {
-            name == 'debugX64'
-            platformName == 'x64'
-            macros == "foo;bar"
-            includePath == "include1;include2"
-            buildCommand == "GRADLE debugX64Build"
-        }
-    }
-
-    private VisualStudioProjectConfiguration configuration(def configName, def platformName, def defines, def includes) {
-        return Stub(VisualStudioProjectConfiguration) {
-            getName() >> "${configName}|${platformName}"
-            getConfigurationName() >> configName
-            getPlatformName() >> platformName
-            getBuildTask() >> "${configName}Build"
-            getCleanTask() >> "${configName}Clean"
-            getCompilerDefines() >> defines
-            getIncludePaths() >> includes.collect { file(it) }
-        }
-    }
-
-    private ProjectFile getProjectFile() {
-        def file = testDirectoryProvider.testDirectory.file("project.xml")
-        generator.store(file)
-        return new ProjectFile(file)
-    }
-
-    private TestFile file(String name) {
-        testDirectoryProvider.testDirectory.file(name)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy
deleted file mode 100644
index d3cbd28..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy
+++ /dev/null
@@ -1,190 +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.ide.visualstudio.tasks.internal
-import org.gradle.api.Action
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.ide.visualstudio.TextProvider
-import org.gradle.ide.visualstudio.fixtures.SolutionFile
-import org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject
-import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.internal.ProjectNativeComponentInternal
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import spock.lang.Specification
-
-class VisualStudioSolutionFileTest extends Specification {
-    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-    def fileResolver = Mock(FileResolver)
-    def instantiator = new DirectInstantiator()
-    def solutionFile = new VisualStudioSolutionFile()
-    def binary1 = binary("one")
-
-    def "setup"() {
-        solutionFile.loadDefaults()
-    }
-
-    def "empty solution file"() {
-        expect:
-        generatedSolution.content ==
-"""Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual C++ Express 2010
-
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
-"""
-    }
-
-    def "create for single project configuration"() {
-        when:
-        def project = createProject("project1")
-        def configuration1 = createProjectConfiguration(project, "projectConfig")
-        solutionFile.addSolutionConfiguration("solutionConfig", [configuration1])
-        solutionFile.mainProject = project
-
-        then:
-        generatedSolution.content ==
-"""Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual C++ Express 2010
-
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project1", "${project.projectFile.location.absolutePath}", "${project.uuid}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		solutionConfig=solutionConfig
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		${project.uuid}.solutionConfig.ActiveCfg = projectConfig|Win32
-		${project.uuid}.solutionConfig.Build.0 = projectConfig|Win32
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
-"""
-    }
-
-    def "create for multiple configurations"() {
-        when:
-        def project1 = createProject("project1")
-        def project2 = createProject("project2")
-        solutionFile.mainProject = project1
-        solutionFile.addSolutionConfiguration("solutionConfig1", [
-                createProjectConfiguration(project1, "config1"),
-                createProjectConfiguration(project1, "config2"),
-                createProjectConfiguration(project2, "configA")
-        ])
-        solutionFile.addSolutionConfiguration("solutionConfig2", [
-                createProjectConfiguration(project2, "configA")
-        ])
-
-        then:
-        generatedSolution.content ==
-"""Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual C++ Express 2010
-
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project1", "${project1.projectFile.location.absolutePath}", "${project1.uuid}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project2", "${project2.projectFile.location.absolutePath}", "${project2.uuid}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		solutionConfig1=solutionConfig1
-		solutionConfig2=solutionConfig2
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		${project1.uuid}.solutionConfig1.ActiveCfg = config1|Win32
-		${project1.uuid}.solutionConfig1.Build.0 = config1|Win32
-		${project1.uuid}.solutionConfig1.ActiveCfg = config2|Win32
-		${project1.uuid}.solutionConfig1.Build.0 = config2|Win32
-		${project2.uuid}.solutionConfig1.ActiveCfg = configA|Win32
-		${project2.uuid}.solutionConfig2.ActiveCfg = configA|Win32
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
-"""
-    }
-
-    def "applies multiple text actions"() {
-        when:
-        solutionFile.actions << ({ TextProvider text ->
-            text.setText("foo")
-        } as Action)
-        solutionFile.actions << ({ TextProvider text ->
-            text.asBuilder().append("bar")
-        } as Action)
-
-        then:
-        generatedSolutionFile.text == "foobar"
-    }
-
-    def "can get and set text with actions"() {
-        when:
-        solutionFile.actions << ({ TextProvider text ->
-            text.text = "test"
-        } as Action)
-        solutionFile.actions << ({ TextProvider text ->
-            text.text = text.text.reverse()
-        } as Action)
-
-        then:
-        generatedSolutionFile.text == "tset"
-    }
-
-    private VisualStudioProjectConfiguration createProjectConfiguration(DefaultVisualStudioProject project1, String configName) {
-        return new VisualStudioProjectConfiguration(project1, configName, "Win32", binary1)
-    }
-
-    private DefaultVisualStudioProject createProject(String projectName) {
-        final project1File = new File(projectName)
-        fileResolver.resolve("${projectName}.vcxproj") >> project1File
-        return new DefaultVisualStudioProject(projectName, binary1.component, fileResolver, instantiator)
-    }
-
-    private ProjectNativeBinary binary(def name) {
-        def component = Mock(ProjectNativeComponentInternal)
-        def binary = Mock(ProjectNativeBinaryInternal)
-        component.name >> "${name}Component"
-        component.projectPath >> "project-path"
-        binary.name >> name
-        binary.component >> component
-        return binary
-    }
-
-    private SolutionFile getGeneratedSolution() {
-        TestFile file = getGeneratedSolutionFile()
-        return new SolutionFile(file)
-    }
-
-    private TestFile getGeneratedSolutionFile() {
-        def file = testDirectoryProvider.testDirectory.file("solution.txt")
-        solutionFile.store(file)
-        file
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.groovy
deleted file mode 100644
index 3ea8783..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.groovy
+++ /dev/null
@@ -1,42 +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.language.assembler.plugins
-
-import org.gradle.language.assembler.AssemblerSourceSet
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class AssemblerLangPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def "adds support for custom AssemblerSourceSets"() {
-        when:
-        project.plugins.apply(AssemblerLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.create("test_asm", AssemblerSourceSet) in AssemblerSourceSet
-    }
-
-    def "adds conventional AssemblerSourceSet"() {
-        when:
-        project.plugins.apply(AssemblerLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.asm in AssemblerSourceSet
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.groovy
deleted file mode 100644
index eade031..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.groovy
+++ /dev/null
@@ -1,42 +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.language.c.plugins
-
-import org.gradle.language.c.CSourceSet
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class CLangPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def "adds support for custom CSourceSets"() {
-        when:
-        project.plugins.apply(CLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.create("test_c", CSourceSet) in CSourceSet
-    }
-
-    def "adds conventional CSourceSet"() {
-        when:
-        project.plugins.apply(CLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.c in CSourceSet
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.groovy
deleted file mode 100644
index 7369554..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.groovy
+++ /dev/null
@@ -1,42 +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.language.cpp.plugins
-
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class CppLangPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def "adds support for custom CppSourceSets"() {
-        when:
-        project.plugins.apply(CppLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.create("test_cpp", CppSourceSet) in CppSourceSet
-    }
-
-    def "adds conventional CppSourceSet"() {
-        when:
-        project.plugins.apply(CppLangPlugin)
-        project.sources.create "test"
-
-        then:
-        project.sources.test.cpp in CppSourceSet
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeTest.groovy
deleted file mode 100644
index 82699d0..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultBuildTypeTest.groovy
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal
-
-import spock.lang.Specification
-
-class DefaultBuildTypeTest extends Specification {
-    def "has useful string representation"() {
-        def buildType = new DefaultBuildType("release")
-
-        expect:
-        buildType.toString() == "build type 'release'"
-        buildType.displayName == "build type 'release'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableBinaryTest.groovy
deleted file mode 100644
index 13ca2de..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableBinaryTest.groovy
+++ /dev/null
@@ -1,39 +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.nativebinaries.internal
-import org.gradle.language.base.internal.DefaultBinaryNamingScheme
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Executable
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import spock.lang.Specification
-
-class DefaultExecutableBinaryTest extends Specification {
-    def namingScheme = new DefaultBinaryNamingScheme("bigOne", "executable", [])
-
-    def "has useful string representation"() {
-        given:
-        def executable = Stub(Executable)
-
-        when:
-        def binary = new ProjectExecutableBinary(executable, new DefaultFlavor("flavorOne"), Stub(ToolChainInternal), Stub(Platform), Stub(BuildType), namingScheme, Mock(NativeDependencyResolver))
-
-        then:
-        binary.toString() == "executable 'bigOne:executable'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableTest.groovy
deleted file mode 100644
index 4347607..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultExecutableTest.groovy
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal
-
-import spock.lang.Specification
-
-class DefaultExecutableTest extends Specification {
-    def executable = new DefaultExecutable(new NativeProjectComponentIdentifier("project-path", "someExe"))
-
-    def "has useful string representation"() {
-        expect:
-        executable.toString() == "executable 'someExe'"
-        executable.displayName == "executable 'someExe'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultFlavorTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultFlavorTest.groovy
deleted file mode 100644
index 210b9ab..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultFlavorTest.groovy
+++ /dev/null
@@ -1,29 +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.nativebinaries.internal
-
-import spock.lang.Specification
-
-class DefaultFlavorTest extends Specification {
-    def "has useful string representation"() {
-        def flavor = new DefaultFlavor("someFlavor")
-
-        expect:
-        flavor.toString() == "flavor 'someFlavor'"
-        flavor.displayName == "flavor 'someFlavor'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultLibraryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultLibraryTest.groovy
deleted file mode 100644
index 9d1dcc6..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultLibraryTest.groovy
+++ /dev/null
@@ -1,59 +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.nativebinaries.internal
-
-import spock.lang.Specification
-
-class DefaultLibraryTest extends Specification {
-    final library = new DefaultLibrary(new NativeProjectComponentIdentifier("project-path", "someLib"))
-
-    def "has useful string representation"() {
-        expect:
-        library.toString() == "library 'someLib'"
-        library.displayName == "library 'someLib'"
-    }
-
-    def "can use shared variant as requirement"() {
-        when:
-        def requirement = library.shared
-
-        then:
-        requirement.projectPath == 'project-path'
-        requirement.libraryName == 'someLib'
-        requirement.linkage == 'shared'
-    }
-
-    def "can use static variant as requirement"() {
-        when:
-        def requirement = library.static
-
-        then:
-        requirement.projectPath == 'project-path'
-        requirement.libraryName == 'someLib'
-        requirement.linkage == 'static'
-    }
-
-    def "can use api linkage as requirement"() {
-        when:
-        def requirement = library.api
-
-        then:
-        requirement.projectPath == 'project-path'
-        requirement.libraryName == 'someLib'
-        requirement.linkage == 'api'
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasksTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasksTest.groovy
deleted file mode 100644
index 9e2d31a..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeBinaryTasksTest.groovy
+++ /dev/null
@@ -1,55 +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.nativebinaries.internal
-
-import org.gradle.nativebinaries.tasks.CreateStaticLibrary
-import org.gradle.nativebinaries.tasks.LinkExecutable
-import org.gradle.util.TestUtil
-import spock.lang.Specification;
-
-class DefaultNativeBinaryTasksTest extends Specification {
-    def tasks = new DefaultNativeBinaryTasks()
-
-    def "returns null for link, createStaticLib and builder when none defined"() {
-        expect:
-        tasks.link == null
-        tasks.createStaticLib == null
-        tasks.builder == null
-    }
-
-    def "returns link task when defined"() {
-        when:
-        final linkTask = TestUtil.createTask(LinkExecutable)
-        tasks.add(linkTask)
-
-        then:
-        tasks.link == linkTask
-        tasks.createStaticLib == null
-        tasks.builder == linkTask
-    }
-
-    def "returns create task when defined"() {
-        when:
-        final createTask = TestUtil.createTask(CreateStaticLibrary)
-        tasks.add(createTask)
-
-        then:
-        tasks.link == null
-        tasks.createStaticLib == createTask
-        tasks.builder == createTask
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeComponentTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeComponentTest.groovy
deleted file mode 100644
index d43d2df..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/DefaultNativeComponentTest.groovy
+++ /dev/null
@@ -1,77 +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.nativebinaries.internal
-
-import org.gradle.api.internal.AsmBackedClassGenerator
-import org.gradle.api.internal.ClassGeneratorBackedInstantiator
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.DefaultFunctionalSourceSet
-import spock.lang.Specification
-
-class DefaultNativeComponentTest extends Specification {
-    def instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
-    def id = new NativeProjectComponentIdentifier("project", "name")
-    def component = new TestProjectNativeComponent(id)
-
-    def "uses all source sets from a functional source set"() {
-        given:
-        def functionalSourceSet = new DefaultFunctionalSourceSet("func", instantiator)
-        def sourceSet1 = Stub(LanguageSourceSet) {
-            getName() >> "ss1"
-        }
-        def sourceSet2 = Stub(LanguageSourceSet) {
-            getName() >> "ss2"
-        }
-
-        when:
-        functionalSourceSet.add(sourceSet1)
-        functionalSourceSet.add(sourceSet2)
-
-        and:
-        component.source functionalSourceSet
-
-        then:
-        component.source.contains(sourceSet1)
-        component.source.contains(sourceSet2)
-    }
-
-    def "flavors can be chosen and will replace default flavor"() {
-        when:
-        component.targetFlavors "flavor1", "flavor2"
-
-        and:
-        component.targetFlavors("flavor3")
-
-        then:
-        component.chooseFlavors([flavor("flavor1"), flavor("flavor2"), flavor("flavor3"), flavor("flavor4")] as Set)*.name == ["flavor1", "flavor2", "flavor3"]
-    }
-
-    class TestProjectNativeComponent extends AbstractTargetedProjectNativeComponent {
-        TestProjectNativeComponent(NativeProjectComponentIdentifier id) {
-            super(id)
-        }
-
-        String getDisplayName() {
-            return "test component"
-        }
-    }
-
-    def flavor(String name) {
-        new DefaultFlavor(name)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryTest.groovy
deleted file mode 100644
index 955eab9..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectNativeBinaryTest.groovy
+++ /dev/null
@@ -1,210 +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.nativebinaries.internal
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.DependentSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.BinaryNamingScheme
-import org.gradle.language.base.internal.DefaultBinaryNamingScheme
-import org.gradle.language.base.internal.DefaultFunctionalSourceSet
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.resolve.NativeBinaryResolveResult
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.platform.internal.DefaultArchitecture
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import spock.lang.Specification
-
-class ProjectNativeBinaryTest extends Specification {
-    def flavor1 = new DefaultFlavor("flavor1")
-    def id = new NativeProjectComponentIdentifier("project", "name")
-    def component = new TestProjectNativeComponent(id)
-    def toolChain1 = Stub(ToolChainInternal) {
-        getName() >> "ToolChain1"
-    }
-    def platform1 = Stub(Platform) {
-        getArchitecture() >> new DefaultArchitecture("i386", ArchitectureInternal.InstructionSet.X86, 64)
-    }
-    def buildType1 = Stub(BuildType) {
-        getName() >> "BuildType1"
-    }
-    def resolver = Mock(NativeDependencyResolver)
-
-    def "binary uses source from its owner component"() {
-        given:
-        def binary = testBinary(component)
-        def sourceSet = Stub(LanguageSourceSet)
-
-        when:
-        component.source(sourceSet)
-
-        then:
-        binary.source.contains(sourceSet)
-    }
-
-    def "binary uses all source sets from a functional source set"() {
-        given:
-        def binary = testBinary(component)
-        def functionalSourceSet = new DefaultFunctionalSourceSet("func", new DirectInstantiator())
-        def sourceSet1 = Stub(LanguageSourceSet) {
-            getName() >> "ss1"
-        }
-        def sourceSet2 = Stub(LanguageSourceSet) {
-            getName() >> "ss2"
-        }
-
-        when:
-        functionalSourceSet.add(sourceSet1)
-        functionalSourceSet.add(sourceSet2)
-
-        and:
-        binary.source functionalSourceSet
-
-        then:
-        binary.source.contains(sourceSet1)
-        binary.source.contains(sourceSet2)
-    }
-
-    def "uses resolver to resolve lib to dependency"() {
-        def binary = testBinary(component, flavor1)
-        def lib = new Object()
-        def dependency = Stub(NativeDependencySet)
-
-        when:
-        binary.lib(lib)
-
-        and:
-        1 * resolver.resolve({NativeBinaryResolveResult result ->
-            result.allResolutions*.input == [lib]
-        }) >> { NativeBinaryResolveResult result ->
-            result.allResolutions[0].nativeDependencySet = dependency
-        }
-
-        then:
-        binary.libs// == [dependency]
-    }
-
-    def "binary libs include source set dependencies"() {
-        def binary = testBinary(component)
-        def lib = new Object()
-        def dependency = Stub(NativeDependencySet)
-
-        when:
-        def sourceSet = Stub(DependentSourceSet) {
-            getLibs() >> [lib]
-        }
-        binary.source sourceSet
-
-        1 * resolver.resolve({NativeBinaryResolveResult result ->
-            result.allResolutions*.input == [lib]
-        }) >> { NativeBinaryResolveResult result ->
-            result.allResolutions[0].nativeDependencySet = dependency
-        }
-
-        then:
-        binary.getLibs(sourceSet) == [dependency]
-    }
-
-    def "order of libraries is maintained"() {
-        def binary = testBinary(component)
-        def libraryBinary = Mock(LibraryBinary)
-        def dependency1 = Stub(NativeDependencySet)
-        def dependency2 = Stub(NativeDependencySet)
-        def dependency3 = Stub(NativeDependencySet)
-
-        when:
-        binary.lib(dependency1)
-        binary.lib(libraryBinary)
-        binary.lib(dependency3)
-
-        and:
-        1 * resolver.resolve({NativeBinaryResolveResult result ->
-            result.allResolutions*.input == [dependency1, libraryBinary, dependency3]
-        }) >> { NativeBinaryResolveResult result ->
-            result.allResolutions[0].nativeDependencySet = dependency1
-            result.allResolutions[1].nativeDependencySet = dependency2
-            result.allResolutions[2].nativeDependencySet = dependency3
-        }
-
-        then:
-        binary.libs as List == [dependency1, dependency2, dependency3]
-    }
-
-    def "library added to binary is ordered before library for source set"() {
-        def binary = testBinary(component)
-        def lib1 = new Object()
-        def dep1 = Stub(NativeDependencySet)
-        def lib2 = new Object()
-        def dep2 = Stub(NativeDependencySet)
-        def sourceLib = new Object()
-        def sourceDep = Stub(NativeDependencySet)
-
-        when:
-        binary.lib(lib1)
-        def sourceSet = Stub(DependentSourceSet) {
-            getLibs() >> [sourceLib]
-        }
-        binary.source sourceSet
-        binary.lib(lib2)
-
-        and:
-        1 * resolver.resolve({NativeBinaryResolveResult result ->
-            result.allResolutions*.input == [lib1, lib2, sourceLib]
-        }) >> { NativeBinaryResolveResult result ->
-            result.allResolutions[0].nativeDependencySet = dep1
-            result.allResolutions[1].nativeDependencySet = dep2
-            result.allResolutions[2].nativeDependencySet = sourceDep
-        }
-
-        then:
-        binary.libs as List == [dep1, dep2, sourceDep]
-    }
-
-    def testBinary(ProjectNativeComponent owner, Flavor flavor = new DefaultFlavor(DefaultFlavor.DEFAULT)) {
-        return new TestProjectNativeBinary(owner, flavor, toolChain1, platform1, buildType1, new DefaultBinaryNamingScheme("baseName", "", []), resolver)
-    }
-
-    class TestProjectNativeComponent extends AbstractProjectNativeComponent {
-        TestProjectNativeComponent(NativeProjectComponentIdentifier id) {
-            super(id)
-        }
-
-        String getDisplayName() {
-            return "test component"
-        }
-    }
-
-    class TestProjectNativeBinary extends AbstractProjectNativeBinary {
-        def owner
-
-        TestProjectNativeBinary(ProjectNativeComponent owner, Flavor flavor, ToolChainInternal toolChain, Platform targetPlatform, BuildType buildType,
-                   BinaryNamingScheme namingScheme, NativeDependencyResolver resolver) {
-            super(owner, flavor, toolChain, targetPlatform, buildType, namingScheme, resolver)
-            this.owner = owner
-        }
-
-        String getOutputFileName() {
-            return null
-        }
-
-        File getPrimaryOutput() {
-            File binaryOutputDir = getBinaryOutputDir();
-            return new File(binaryOutputDir, getOutputFileName());
-        }
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinaryTest.groovy
deleted file mode 100644
index 21f7527..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectSharedLibraryBinaryTest.groovy
+++ /dev/null
@@ -1,123 +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.nativebinaries.internal
-import org.gradle.api.Task
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.base.internal.DefaultBinaryNamingScheme
-import org.gradle.language.rc.WindowsResourceSet
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ProjectSharedLibraryBinaryTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir
-    def namingScheme = new DefaultBinaryNamingScheme("main", "sharedLibrary", [])
-    final toolChain = Stub(ToolChainInternal)
-    final platform = Stub(Platform)
-    final buildType = Stub(BuildType)
-    final library = Stub(Library)
-    final resolver = Stub(NativeDependencyResolver)
-    def sharedLibraryFile = Mock(File)
-    def sharedLibraryLinkFile = Mock(File)
-
-    def "has useful string representation"() {
-        expect:
-        sharedLibrary.toString() == "shared library 'main:sharedLibrary'"
-    }
-
-    def "can set output files"() {
-        given:
-        def binary = sharedLibrary
-
-        when:
-        binary.sharedLibraryFile = sharedLibraryFile
-        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
-
-        then:
-        binary.sharedLibraryFile == sharedLibraryFile
-        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
-    }
-
-    def "can convert binary to a native dependency"() {
-        given:
-        def binary = sharedLibrary
-        binary.sharedLibraryFile = sharedLibraryFile
-        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
-        def lifecycleTask = Stub(Task)
-        binary.setLifecycleTask(lifecycleTask)
-        binary.builtBy(Stub(Task))
-
-        and: "has at least one header exporting source set"
-        final headerDir = tmpDir.createDir("headerDir")
-        def headerDirSet = Stub(SourceDirectorySet) {
-            getSrcDirs() >> [headerDir]
-        }
-        def sourceDirSet = Stub(SourceDirectorySet) {
-            getFiles() >> [tmpDir.createFile("input.src")]
-        }
-        def sourceSet = Stub(HeaderExportingSourceSet) {
-            getSource() >> sourceDirSet
-            getExportedHeaders() >> headerDirSet
-        }
-        binary.source sourceSet
-
-        expect:
-        binary.sharedLibraryFile == sharedLibraryFile
-        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
-
-        binary.headerDirs.files == [headerDir] as Set
-
-        and:
-        binary.linkFiles.files == [binary.sharedLibraryLinkFile] as Set
-        binary.linkFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
-        binary.linkFiles.toString() == "shared library 'main:sharedLibrary'"
-
-        and:
-        binary.runtimeFiles.files == [binary.sharedLibraryFile] as Set
-        binary.runtimeFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
-        binary.runtimeFiles.toString() == "shared library 'main:sharedLibrary'"
-    }
-
-    def "has empty link files when has resources and no symbols are exported from library"() {
-        when:
-        def binary = sharedLibrary
-        def sourceDirSet = Stub(SourceDirectorySet) {
-            getFiles() >> [tmpDir.createFile("input.rc")]
-        }
-        def resourceSet = Stub(WindowsResourceSet) {
-            getSource() >> sourceDirSet
-        }
-        binary.source resourceSet
-
-        def binaryFile = tmpDir.createFile("binary.run")
-        def linkFile = tmpDir.createFile("binary.link")
-        toolChain.getSharedLibraryLinkFileName(binaryFile.path) >> linkFile.path
-
-        then:
-        binary.linkFiles.files == [] as Set
-    }
-
-    private ProjectSharedLibraryBinary getSharedLibrary() {
-        new ProjectSharedLibraryBinary(library, new DefaultFlavor("flavorOne"), toolChain, platform, buildType, namingScheme, resolver)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinaryTest.groovy
deleted file mode 100644
index 77a544b..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/ProjectStaticLibraryBinaryTest.groovy
+++ /dev/null
@@ -1,125 +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.nativebinaries.internal
-import org.gradle.api.Task
-import org.gradle.api.file.FileCollection
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.language.HeaderExportingSourceSet
-import org.gradle.language.base.internal.DefaultBinaryNamingScheme
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ProjectStaticLibraryBinaryTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir
-    def namingScheme = new DefaultBinaryNamingScheme("main", "staticLibrary", [])
-    def library = Stub(Library)
-    def toolChain = Stub(ToolChainInternal)
-    def platform = Stub(Platform)
-    def buildType = Stub(BuildType)
-    final resolver = Stub(NativeDependencyResolver)
-    final outputFile = Mock(File)
-
-    def "has useful string representation"() {  
-        expect:
-        staticLibrary.toString() == "static library 'main:staticLibrary'"
-    }
-
-    def getStaticLibrary() {
-        new ProjectStaticLibraryBinary(library, new DefaultFlavor("flavorOne"), toolChain, platform, buildType, namingScheme, resolver)
-    }
-
-    def "can set output file"() {
-        given:
-        final binary = staticLibrary
-        def outputFile = Mock(File)
-
-        when:
-        binary.staticLibraryFile = outputFile
-
-        then:
-        binary.staticLibraryFile == outputFile
-    }
-
-    def "can convert binary to a native dependency"() {
-        final binary = staticLibrary
-        given:
-        def lifecycleTask = Stub(Task)
-        binary.lifecycleTask = lifecycleTask
-        binary.builtBy(Stub(Task))
-
-        and:
-        binary.staticLibraryFile = outputFile
-
-        and:
-        final headerDir = tmpDir.createDir("headerDir")
-        addSources(binary, headerDir)
-
-        expect:
-        binary.headerDirs.files == [headerDir] as Set
-        binary.staticLibraryFile == outputFile
-
-        and:
-        binary.linkFiles.files == [binary.staticLibraryFile] as Set
-        binary.linkFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
-        binary.linkFiles.toString() == "static library 'main:staticLibrary'"
-
-        and:
-        binary.runtimeFiles.files.isEmpty()
-        binary.runtimeFiles.buildDependencies.getDependencies(Stub(Task)) == [] as Set
-    }
-
-    def "includes additional link files in native dependency"() {
-        final binary = staticLibrary
-        given:
-        binary.staticLibraryFile = outputFile
-        def linkFile1 = Mock(File)
-        def linkFile2 = Mock(File)
-        def additionalLinkFiles = Stub(FileCollection) {
-            getFiles() >> [linkFile1, linkFile2]
-        }
-        binary.additionalLinkFiles(additionalLinkFiles)
-
-        and:
-        addSources(binary, tmpDir.createDir("headerDir"))
-
-        expect:
-        binary.staticLibraryFile == outputFile
-        binary.linkFiles.files == [binary.staticLibraryFile, linkFile1, linkFile2] as Set
-    }
-
-    private TestFile addSources(ProjectStaticLibraryBinary binary, def headerDir) {
-        def headerDirSet = Stub(SourceDirectorySet) {
-            getSrcDirs() >> [headerDir]
-        }
-        def sourceDirSet = Stub(SourceDirectorySet) {
-            getFiles() >> [tmpDir.createFile("input.src")]
-        }
-        def sourceSet = Stub(HeaderExportingSourceSet) {
-            getSource() >> sourceDirSet
-            getExportedHeaders() >> headerDirSet
-        }
-        binary.source sourceSet
-        headerDir
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParserTest.groovy
deleted file mode 100644
index a4ea850..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/SourceSetNotationParserTest.groovy
+++ /dev/null
@@ -1,53 +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.nativebinaries.internal
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.DefaultFunctionalSourceSet
-import spock.lang.Specification
-
-class SourceSetNotationParserTest extends Specification {
-    def parser = SourceSetNotationParser.parser()
-    def languageSourceSet1 = languageSourceSet("lss1")
-    def languageSourceSet2 = languageSourceSet("lss2")
-
-    def "translates single LanguageSourceSet"() {
-        expect:
-        parser.parseNotation(languageSourceSet1) as List == [languageSourceSet1]
-    }
-
-    def "collects all LanguageSourceSets for a FunctionalSourceSet"() {
-        when:
-        def functionalSourceSet = new DefaultFunctionalSourceSet("func", new DirectInstantiator())
-        functionalSourceSet.add(languageSourceSet1)
-        functionalSourceSet.add(languageSourceSet2)
-
-        then:
-        parser.parseNotation(functionalSourceSet) as List == [languageSourceSet1, languageSourceSet2]
-    }
-
-    def "collects all LanguageSourceSets in a collection"() {
-        expect:
-        parser.parseNotation([languageSourceSet1, languageSourceSet2]) as List == [languageSourceSet1, languageSourceSet2]
-        parser.parseNotation([languageSourceSet2, languageSourceSet1]) as List == [languageSourceSet2, languageSourceSet1]
-    }
-
-    private LanguageSourceSet languageSourceSet(def name) {
-        Stub(LanguageSourceSet) {
-            getName() >> name
-        }
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypesTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypesTest.groovy
deleted file mode 100644
index df9c45e..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultBuildTypesTest.groovy
+++ /dev/null
@@ -1,44 +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.nativebinaries.internal.configure
-
-import org.gradle.nativebinaries.BuildTypeContainer
-import spock.lang.Specification
-
-class CreateDefaultBuildTypesTest extends Specification {
-    def buildTypes = Mock(BuildTypeContainer)
-    def rule = new CreateDefaultBuildTypes()
-
-    def "adds a default build type when none configured"() {
-        when:
-        rule.createDefaultPlatforms(buildTypes)
-
-        then:
-        1 * buildTypes.empty >> true
-        1 * buildTypes.create("debug")
-        0 * buildTypes._
-    }
-
-    def "does not add default build type when some configured"() {
-        when:
-        rule.createDefaultPlatforms(buildTypes)
-
-        then:
-        1 * buildTypes.empty >> false
-        0 * buildTypes._
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavorsTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavorsTest.groovy
deleted file mode 100644
index d11a912..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultFlavorsTest.groovy
+++ /dev/null
@@ -1,67 +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.nativebinaries.internal.configure
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.nativebinaries.internal.DefaultFlavor
-import org.gradle.nativebinaries.internal.DefaultFlavorContainer
-import spock.lang.Specification
-
-class CreateDefaultFlavorsTest extends Specification {
-    def flavorContainer = new DefaultFlavorContainer(new DirectInstantiator())
-    def rule = new CreateDefaultFlavors()
-
-    def "has a single default flavor when not configured"() {
-        when:
-        rule.createDefaultFlavor(flavorContainer)
-
-        then:
-        flavorContainer.size() == 1
-        flavorNames == [DefaultFlavor.DEFAULT] as Set
-    }
-
-    def "configured flavors overwrite default flavor"() {
-        when:
-        flavorContainer.configure {
-            flavor1 {}
-            flavor2 {}
-        }
-        and:
-        rule.createDefaultFlavor(flavorContainer)
-
-        then:
-        flavorNames == ["flavor1", "flavor2"] as Set
-    }
-
-    def "can explicitly add flavor named 'default'"() {
-        when:
-        flavorContainer.configure {
-            flavor1 {}
-            it.'default' {}
-            flavor2 {}
-        }
-        and:
-        rule.createDefaultFlavor(flavorContainer)
-
-        then:
-        flavorNames == [DefaultFlavor.DEFAULT, "flavor1", "flavor2"] as Set
-
-    }
-
-    def getFlavorNames() {
-        return flavorContainer.collect { it.name } as Set
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatformTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatformTest.groovy
deleted file mode 100644
index cfa14ae..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/CreateDefaultPlatformTest.groovy
+++ /dev/null
@@ -1,44 +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.nativebinaries.internal.configure
-
-import org.gradle.nativebinaries.platform.PlatformContainer
-import spock.lang.Specification
-
-class CreateDefaultPlatformTest extends Specification {
-    def platforms = Mock(PlatformContainer)
-    def action = new CreateDefaultPlatform()
-
-    def "adds a default platform when none configured"() {
-        when:
-        action.createDefaultPlatforms(platforms)
-
-        then:
-        1 * platforms.empty >> true
-        1 * platforms.create("current")
-        0 * platforms._
-    }
-
-    def "does not add default platform when some configured"() {
-        when:
-        action.createDefaultPlatforms(platforms)
-
-        then:
-        1 * platforms.empty >> false
-        0 * platforms._
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactoryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactoryTest.groovy
deleted file mode 100644
index 47803da..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/DefaultNativeBinariesFactoryTest.groovy
+++ /dev/null
@@ -1,93 +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.nativebinaries.internal.configure
-import org.gradle.api.Action
-import org.gradle.internal.reflect.DirectInstantiator
-import org.gradle.language.base.internal.DefaultBinaryNamingSchemeBuilder
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Flavor
-import org.gradle.nativebinaries.ProjectNativeBinary
-import org.gradle.nativebinaries.SharedLibraryBinary
-import org.gradle.nativebinaries.internal.DefaultExecutable
-import org.gradle.nativebinaries.internal.DefaultLibrary
-import org.gradle.nativebinaries.internal.NativeProjectComponentIdentifier
-import org.gradle.nativebinaries.internal.resolve.NativeDependencyResolver
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import spock.lang.Specification
-
-class DefaultNativeBinariesFactoryTest extends Specification {
-    def resolver = Mock(NativeDependencyResolver)
-    Action<ProjectNativeBinary> configAction = Mock(Action)
-
-    def toolChain = Mock(ToolChainInternal)
-    def platform = Mock(Platform)
-    def buildType = Mock(BuildType)
-    def flavor = Mock(Flavor)
-
-    def id = new NativeProjectComponentIdentifier("project", "name")
-
-    def namingSchemeBuilder = new DefaultBinaryNamingSchemeBuilder().withComponentName("test")
-    def factory = new DefaultNativeBinariesFactory(new DirectInstantiator(), configAction, resolver)
-
-    def "creates binaries for executable"() {
-        given:
-        def executable = new DefaultExecutable(id)
-
-        when:
-        1 * configAction.execute(_)
-
-        and:
-        factory.createNativeBinaries(executable, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-
-        then:
-        executable.binaries.size() == 1
-        def binary = (executable.binaries as List)[0] as ProjectNativeBinary
-        binary.name == "testExecutable"
-        binary.toolChain == toolChain
-        binary.targetPlatform == platform
-        binary.buildType == buildType
-        binary.flavor == flavor
-    }
-
-    def "creates binaries for library"() {
-        given:
-        def library = new DefaultLibrary(id)
-
-        when:
-        2 * configAction.execute(_)
-
-        and:
-        factory.createNativeBinaries(library, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-
-        then:
-        library.binaries.size() == 2
-        def sharedLibrary = (library.binaries.withType(SharedLibraryBinary) as List)[0] as ProjectNativeBinary
-        sharedLibrary.name == "testSharedLibrary"
-        sharedLibrary.toolChain == toolChain
-        sharedLibrary.targetPlatform == platform
-        sharedLibrary.buildType == buildType
-        sharedLibrary.flavor == flavor
-
-        def staticLibrary = (library.binaries.withType(SharedLibraryBinary) as List)[0] as ProjectNativeBinary
-        staticLibrary.name == "testSharedLibrary"
-        staticLibrary.toolChain == toolChain
-        staticLibrary.targetPlatform == platform
-        staticLibrary.buildType == buildType
-        staticLibrary.flavor == flavor
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializerTest.groovy
deleted file mode 100644
index 9065895..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeBinaryInitializerTest.groovy
+++ /dev/null
@@ -1,99 +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.nativebinaries.internal.configure
-
-import org.gradle.api.Project
-import org.gradle.language.base.internal.BinaryNamingScheme
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.ProjectNativeBinaryInternal
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ProjectNativeBinaryInitializerTest extends Specification {
-    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-
-    def component = Mock(ProjectNativeComponent)
-    def project = Mock(Project)
-    def configAction
-
-    def namingScheme = Mock(BinaryNamingScheme)
-    def toolChain = Mock(ToolChainInternal)
-
-    def setup() {
-        project.buildDir >> tmpDir.testDirectory
-        configAction = new ProjectNativeBinaryInitializer(project)
-    }
-
-    def "test executable"() {
-        def binary = initBinary(ExecutableBinaryInternal)
-
-        when:
-        toolChain.getExecutableName("base_name") >> "exe_name"
-
-        and:
-        configAction.execute(binary)
-
-        then:
-        1 * binary.setExecutableFile(tmpDir.testDirectory.file("binaries", "output_dir", "exe_name"))
-    }
-
-    def "test shared library"() {
-        def binary = initBinary(SharedLibraryBinaryInternal)
-
-        when:
-        toolChain.getSharedLibraryName("base_name") >> "shared_library_name"
-        toolChain.getSharedLibraryLinkFileName("base_name") >> "shared_library_link_name"
-
-        and:
-        configAction.execute(binary)
-
-        then:
-        1 * binary.setSharedLibraryFile(tmpDir.testDirectory.file("binaries", "output_dir", "shared_library_name"))
-        1 * binary.setSharedLibraryLinkFile(tmpDir.testDirectory.file("binaries", "output_dir", "shared_library_link_name"))
-    }
-
-    def "test static library"() {
-        def binary = initBinary(StaticLibraryBinaryInternal)
-
-        when:
-        toolChain.getStaticLibraryName("base_name") >> "static_library_name"
-
-        and:
-        configAction.execute(binary)
-
-        then:
-        1 * binary.setStaticLibraryFile(tmpDir.testDirectory.file("binaries", "output_dir", "static_library_name"))
-    }
-
-    private <T extends ProjectNativeBinaryInternal> T initBinary(Class<T> type) {
-        def binary = Mock(type)
-        binary.component >> component
-        binary.toolChain >> toolChain
-        binary.namingScheme >> namingScheme
-
-        namingScheme.outputDirectoryBase >> "output_dir"
-        component.baseName >> "base_name"
-        return binary
-    }
-
-    interface ExecutableBinaryInternal extends ExecutableBinary, ProjectNativeBinaryInternal {}
-    interface SharedLibraryBinaryInternal extends SharedLibraryBinary, ProjectNativeBinaryInternal {}
-    interface StaticLibraryBinaryInternal extends StaticLibraryBinary, ProjectNativeBinaryInternal {}
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializerTest.groovy
deleted file mode 100644
index 689f9ae..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/configure/ProjectNativeComponentInitializerTest.groovy
+++ /dev/null
@@ -1,145 +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.nativebinaries.internal.configure
-
-import org.gradle.api.Named
-import org.gradle.language.base.internal.BinaryNamingSchemeBuilder
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Flavor
-import org.gradle.nativebinaries.internal.DefaultExecutable
-import org.gradle.nativebinaries.internal.NativeProjectComponentIdentifier
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.nativebinaries.toolchain.internal.ToolChainRegistryInternal
-import spock.lang.Specification
-
-class ProjectNativeComponentInitializerTest extends Specification {
-    def toolChains = Mock(ToolChainRegistryInternal)
-    def toolChain = Mock(ToolChainInternal)
-    def nativeBinariesFactory = Mock(NativeBinariesFactory)
-    def namingSchemeBuilder = Mock(BinaryNamingSchemeBuilder)
-
-    def platform = createStub(Platform, "platform1")
-    def buildType = createStub(BuildType, "buildType1")
-    def flavor = createStub(Flavor, "flavor1")
-
-    def id = new NativeProjectComponentIdentifier("project", "name")
-    def component = new DefaultExecutable(id)
-
-    def "does not use variant dimension names for single valued dimensions"() {
-        when:
-        def factory = new ProjectNativeComponentInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains, [platform], [buildType], [flavor])
-        factory.execute(component)
-
-        then:
-        1 * toolChains.getForPlatform(platform) >> toolChain
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-        0 * namingSchemeBuilder._
-    }
-
-    def "does not use variant dimension names when component targets a single point on dimension"() {
-        when:
-        def factory = new ProjectNativeComponentInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
-                [platform, Mock(Platform)], [buildType, Mock(BuildType)], [flavor, Mock(Flavor)])
-        component.targetPlatforms("platform1")
-        component.targetBuildTypes("buildType1")
-        component.targetFlavors("flavor1")
-        factory.execute(component)
-
-        then:
-        1 * toolChains.getForPlatform(platform) >> toolChain
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-        0 * namingSchemeBuilder._
-    }
-
-    def "includes platform in name for when multiple platforms"() {
-        final Platform platform2 = createStub(Platform, "platform2")
-        when:
-        def factory = new ProjectNativeComponentInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
-                [platform, platform2], [buildType], [flavor])
-        factory.execute(component)
-
-        then:
-        1 * toolChains.getForPlatform(platform) >> toolChain
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("platform1") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-        0 * _
-
-        then:
-        1 * toolChains.getForPlatform(platform2) >> toolChain
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("platform2") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform2, buildType, flavor)
-        0 * _
-    }
-
-    def "includes buildType in name for when multiple buildTypes"() {
-        final BuildType buildType2 = createStub(BuildType, "buildType2")
-        when:
-        def factory = new ProjectNativeComponentInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
-                [platform], [buildType, buildType2], [flavor])
-        factory.execute(component)
-
-        then:
-        1 * toolChains.getForPlatform(platform) >> toolChain
-
-        then:
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("buildType1") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-        0 * _
-
-        then:
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("buildType2") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType2, flavor)
-        0 * _
-    }
-
-    def "includes flavor in name for when multiple flavors"() {
-        final Flavor flavor2 = createStub(Flavor, "flavor2")
-        when:
-        def factory = new ProjectNativeComponentInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
-                [platform], [buildType], [flavor, flavor2])
-        factory.execute(component)
-
-        then:
-        1 * toolChains.getForPlatform(platform) >> toolChain
-
-        then:
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("flavor1") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor)
-        0 * _
-
-        then:
-        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
-        1 * namingSchemeBuilder.withVariantDimension("flavor2") >> namingSchemeBuilder
-        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, platform, buildType, flavor2)
-        0 * _
-    }
-
-    private <T extends Named> T createStub(Class<T> type, def name) {
-        def stub = Stub(type) {
-            getName() >> name
-        }
-        return stub
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy
deleted file mode 100644
index 97c5ed2..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy
+++ /dev/null
@@ -1,74 +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.nativebinaries.internal.prebuilt
-
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Flavor
-import org.gradle.nativebinaries.PrebuiltLibrary
-import org.gradle.nativebinaries.platform.Platform
-import spock.lang.Specification
-
-class DefaultPrebuiltSharedLibraryBinaryTest extends Specification {
-    def binary = new DefaultPrebuiltSharedLibraryBinary("name", Stub(PrebuiltLibrary), Stub(BuildType), Stub(Platform), Stub(Flavor))
-
-    def "has useful string representation"() {
-        expect:
-        binary.toString() == "shared library 'name'"
-        binary.displayName == "shared library 'name'"
-    }
-
-    def "uses library file when link file not set"() {
-        given:
-        def sharedLibraryFile = Mock(File)
-        def sharedLibraryLinkFile = Mock(File)
-
-        when:
-        binary.sharedLibraryFile = sharedLibraryFile
-
-        then:
-        binary.sharedLibraryFile == sharedLibraryFile
-        binary.sharedLibraryLinkFile == sharedLibraryFile
-
-        when:
-        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
-
-        then:
-        binary.sharedLibraryFile == sharedLibraryFile
-        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
-    }
-
-    def "uses specified linke file and library file"() {
-        given:
-        def sharedLibraryFile = createFile()
-        def sharedLibraryLinkFile = createFile()
-
-        when:
-        binary.sharedLibraryFile = sharedLibraryFile
-        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
-
-        then:
-        binary.linkFiles.files == [sharedLibraryLinkFile] as Set
-        binary.runtimeFiles.files == [sharedLibraryFile] as Set
-    }
-
-    def createFile() {
-        def file = Stub(File) {
-            exists() >> true
-            isFile() >> true
-        }
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.groovy
deleted file mode 100644
index d0e4ae6..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.groovy
+++ /dev/null
@@ -1,56 +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.nativebinaries.internal.prebuilt
-
-import org.gradle.nativebinaries.BuildType
-import org.gradle.nativebinaries.Flavor
-import org.gradle.nativebinaries.PrebuiltLibrary
-import org.gradle.nativebinaries.platform.Platform
-import spock.lang.Specification
-
-class DefaultPrebuiltStaticLibraryBinaryTest extends Specification {
-    def binary = new DefaultPrebuiltStaticLibraryBinary("name", Stub(PrebuiltLibrary), Stub(BuildType), Stub(Platform), Stub(Flavor))
-
-    def "has useful string representation"() {
-        expect:
-        binary.toString() == "static library 'name'"
-        binary.displayName == "static library 'name'"
-    }
-
-    def "can set static library file"() {
-        given:
-        def file = createFile()
-
-        when:
-        binary.staticLibraryFile = file
-
-        then:
-        binary.staticLibraryFile == file
-        binary.linkFiles.files == [file] as Set
-
-        and:
-        binary.runtimeFiles.empty
-    }
-
-    def createFile() {
-        def file = Stub(File) {
-            exists() >> true
-            isFile() >> true
-        }
-    }
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParserTest.groovy
deleted file mode 100644
index 489be6f..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/NativeDependencyNotationParserTest.groovy
+++ /dev/null
@@ -1,72 +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.nativebinaries.internal.resolve
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.NativeLibraryRequirement
-import spock.lang.Specification
-
-class NativeDependencyNotationParserTest extends Specification {
-    def parser = NativeDependencyNotationParser.parser()
-    def requirement = Mock(NativeLibraryRequirement)
-    def library = Mock(Library)
-    def project = Mock(ProjectInternal)
-
-    def "uses shared variant of library"() {
-        when:
-        def input = library
-
-        and:
-        library.shared >> requirement
-
-        then:
-        parser.parseNotation(input) == requirement
-    }
-
-    def "parses map notation for library in same project"() {
-        when:
-        def input = [library: 'libName']
-        def dependency = parser.parseNotation(input)
-
-        then:
-        dependency.projectPath == null
-        dependency.libraryName == "libName"
-        dependency.linkage == null
-    }
-
-    def "parses map notation for library in other project"() {
-        when:
-        def input = [project: 'other', library: 'libName']
-        def dependency = parser.parseNotation(input)
-
-
-        then:
-        dependency.projectPath == "other"
-        dependency.libraryName == "libName"
-        dependency.linkage == null
-    }
-
-    def "parses map notation for library with defined linkage"() {
-        when:
-        def input = [project: 'other', library: 'libName', linkage: 'static']
-        def dependency = parser.parseNotation(input)
-
-        then:
-        dependency.projectPath == "other"
-        dependency.libraryName == "libName"
-        dependency.linkage == "static"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy
deleted file mode 100644
index 1ee846e..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy
+++ /dev/null
@@ -1,136 +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.nativebinaries.internal.resolve
-import org.gradle.api.DomainObjectSet
-import org.gradle.api.UnknownDomainObjectException
-import org.gradle.api.UnknownProjectException
-import org.gradle.api.internal.plugins.ExtensionContainerInternal
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.LibraryContainer
-import org.gradle.nativebinaries.NativeLibraryRequirement
-import org.gradle.nativebinaries.internal.ProjectNativeLibraryRequirement
-import spock.lang.Specification
-
-class ProjectLibraryBinaryLocatorTest extends Specification {
-    def project = Mock(ProjectInternal)
-    def projectLocator = Mock(ProjectLocator)
-    def requirement = Mock(NativeLibraryRequirement)
-    def library = Mock(Library)
-    def binaries = Mock(DomainObjectSet)
-    def locator = new ProjectLibraryBinaryLocator(projectLocator)
-
-    def setup() {
-        library.binaries >> binaries
-    }
-
-    def "locates binaries for library in same project"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("libName", null)
-
-        and:
-        projectLocator.locateProject(null) >> project
-        findLibraryInProject()
-
-        then:
-        locator.getBinaries(requirement) == binaries
-    }
-
-    def "locates binaries for library in other project"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("other", "libName", null)
-
-        and:
-        projectLocator.locateProject("other") >> project
-        findLibraryInProject()
-
-        then:
-        locator.getBinaries(requirement) == binaries
-    }
-
-    def "parses map notation for library with static linkage"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("other", "libName", "static")
-
-        and:
-        projectLocator.locateProject("other") >> project
-        findLibraryInProject()
-
-        then:
-        locator.getBinaries(requirement) == binaries
-    }
-
-    def "fails for unknown project"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("unknown", "libName", "static")
-
-        and:
-        projectLocator.locateProject("unknown") >> { throw new UnknownProjectException("unknown")}
-
-        and:
-        locator.getBinaries(requirement)
-
-        then:
-        thrown(UnknownProjectException)
-    }
-
-    def "fails for unknown library"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("other", "unknown", "static")
-
-        and:
-        projectLocator.locateProject("other") >> project
-        def libraries = findLibraryContainer(project)
-        libraries.getByName("unknown") >> { throw new UnknownDomainObjectException("unknown") }
-
-        and:
-        locator.getBinaries(requirement)
-
-        then:
-        thrown(UnknownDomainObjectException)
-    }
-
-    def "fails when project does not have libraries"() {
-        when:
-        requirement = new ProjectNativeLibraryRequirement("other", "libName", "static")
-
-        and:
-        projectLocator.locateProject("other") >> project
-        def extensions = Mock(ExtensionContainerInternal)
-        project.getExtensions() >> extensions
-        extensions.findByName("libraries") >> null
-        project.path >> "project-path"
-
-        and:
-        locator.getBinaries(requirement)
-
-        then:
-        def e = thrown(LibraryResolveException)
-        e.message == "Project does not have a libraries container: 'project-path'"
-    }
-
-    private void findLibraryInProject() {
-        def libraries = findLibraryContainer(project)
-        libraries.getByName("libName") >> library
-    }
-    private LibraryContainer findLibraryContainer(ProjectInternal project) {
-        def extensions = Mock(ExtensionContainerInternal)
-        def libraries = Mock(LibraryContainer)
-        project.getExtensions() >> extensions
-        extensions.findByName("libraries") >> libraries
-        return libraries
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/AbstractNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/AbstractNativeBinariesPluginTest.groovy
deleted file mode 100644
index 96c7c6b..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/AbstractNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,160 +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.nativebinaries.language
-
-import org.apache.commons.lang.StringUtils
-import org.gradle.api.Plugin
-import org.gradle.api.tasks.TaskDependencyMatchers
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.nativebinaries.ExecutableBinary
-import org.gradle.nativebinaries.NativeBinary
-import org.gradle.util.GFileUtils
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-abstract class AbstractNativeBinariesPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    abstract Class<? extends Plugin> getPluginClass();
-    abstract Class<? extends LanguageSourceSet> getSourceSetClass();
-    abstract Class<? extends Plugin> getCompileTaskClass();
-    abstract String getPluginName();
-
-    def "creates source set with conventional locations for components"() {
-        when:
-        dsl {
-            apply plugin: pluginClass
-            executables {
-                exe {}
-            }
-            libraries {
-                lib {}
-            }
-        }
-
-        then:
-        def sourceSets = project.sources
-        sourceSets.size() == 2
-        sourceSets*.name == ["exe", "lib"]
-
-        and:
-        sourceSets.exe instanceof FunctionalSourceSet
-        sourceSetClass.isInstance(sourceSets.exe."$pluginName")
-        sourceSets.exe."$pluginName".source.srcDirs == [project.file("src/exe/$pluginName")] as Set
-        sourceSets.exe."$pluginName".exportedHeaders.srcDirs == [project.file("src/exe/headers")] as Set
-        project.executables.exe.source == [sourceSets.exe."$pluginName"] as Set
-
-        and:
-        sourceSets.lib instanceof FunctionalSourceSet
-        sourceSetClass.isInstance(sourceSets.lib."$pluginName")
-        sourceSets.lib."$pluginName".source.srcDirs == [project.file("src/lib/$pluginName")] as Set
-        sourceSets.lib."$pluginName".exportedHeaders.srcDirs == [project.file("src/lib/headers")] as Set
-        project.libraries.lib.source == [sourceSets.lib."$pluginName"] as Set
-    }
-
-    def "can configure source set locations"() {
-        given:
-        dsl {
-            apply plugin: pluginClass
-            sources {
-                exe {
-                    "$pluginName" {
-                        source {
-                            srcDirs "d1", "d2"
-                        }
-                        exportedHeaders {
-                            srcDirs "h1", "h2"
-                        }
-                    }
-                }
-                lib {
-                    "$pluginName" {
-                        source {
-                            srcDirs "d3"
-                        }
-                        exportedHeaders {
-                            srcDirs "h3"
-                        }
-                    }
-                }
-            }
-        }
-
-        expect:
-        def sourceSets = project.sources
-        with (sourceSets.exe."$pluginName") {
-            source.srcDirs*.name == ["d1", "d2"]
-            exportedHeaders.srcDirs*.name == ["h1", "h2"]
-        }
-
-        with (sourceSets.lib."$pluginName") {
-            source.srcDirs*.name == ["d3"]
-            exportedHeaders.srcDirs*.name == ["h3"]
-        }
-    }
-
-    def "creates compile tasks for each non-empty executable source set"() {
-        when:
-        touch("src/test/$pluginName/file.o")
-        touch("src/test/anotherOne/file.o")
-        dsl {
-            apply plugin: pluginClass
-            sources {
-                test {
-                    anotherOne(sourceSetClass) {}
-                    emptyOne(sourceSetClass) {}
-                }
-            }
-            executables {
-                test {
-                    binaries.all { NativeBinary binary ->
-                        binary."${pluginName}Compiler".define "NDEBUG"
-                        binary."${pluginName}Compiler".define "LEVEL", "1"
-                        binary."${pluginName}Compiler".args "ARG1", "ARG2"
-                    }
-                }
-            }
-        }
-
-        then:
-        ExecutableBinary binary = project.binaries.testExecutable
-        binary.tasks.withType(compileTaskClass)*.name == ["compileTestExecutableTestAnotherOne", "compileTestExecutableTest${StringUtils.capitalize(pluginName)}"]
-
-        and:
-        binary.tasks.withType(compileTaskClass).each { compile ->
-            compile.toolChain == binary.toolChain
-            compile.macros == [NDEBUG:null, LEVEL:"1"]
-            compile.compilerArgs == ["ARG1", "ARG2"]
-        }
-
-        and:
-        def linkTask = binary.tasks.link
-        linkTask TaskDependencyMatchers.dependsOn("compileTestExecutableTestAnotherOne", "compileTestExecutableTest${StringUtils.capitalize(pluginName)}")
-    }
-
-
-    def touch(String filePath) {
-        GFileUtils.touch(project.file(filePath))
-    }
-
-    def dsl(Closure closure) {
-        closure.delegate = project
-        closure()
-        project.evaluate()
-    }
-}
\ No newline at end of file
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPluginTest.groovy
deleted file mode 100644
index dbb720e..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/plugins/AssemblerNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,183 +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.nativebinaries.language.assembler.plugins
-import org.gradle.api.tasks.TaskDependencyMatchers
-import org.gradle.language.assembler.AssemblerSourceSet
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.nativebinaries.ExecutableBinary
-import org.gradle.nativebinaries.NativeBinary
-import org.gradle.nativebinaries.SharedLibraryBinary
-import org.gradle.nativebinaries.StaticLibraryBinary
-import org.gradle.nativebinaries.language.assembler.tasks.Assemble
-import org.gradle.util.GFileUtils
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class AssemblerNativeBinariesPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def "creates asm source set with conventional locations for components"() {
-        when:
-        dsl {
-            apply plugin: AssemblerNativeBinariesPlugin
-            executables {
-                exe {}
-            }
-            libraries {
-                lib {}
-            }
-        }
-
-        then:
-        def sourceSets = project.sources
-        sourceSets.size() == 2
-        sourceSets*.name == ["exe", "lib"]
-
-        and:
-        sourceSets.exe.asm instanceof AssemblerSourceSet
-        sourceSets.exe.asm.source.srcDirs == [project.file("src/exe/asm")] as Set
-        project.executables.exe.source == [sourceSets.exe.asm] as Set
-
-        and:
-        sourceSets.lib instanceof FunctionalSourceSet
-        sourceSets.lib.asm instanceof AssemblerSourceSet
-        sourceSets.lib.asm.source.srcDirs == [project.file("src/lib/asm")] as Set
-        project.libraries.lib.source == [sourceSets.lib.asm] as Set
-    }
-
-    def "can configure source set locations"() {
-        given:
-        dsl {
-            apply plugin: AssemblerNativeBinariesPlugin
-            sources {
-                exe {
-                    asm {
-                        source {
-                            srcDirs "d1", "d2"
-                        }
-                    }
-                }
-                lib {
-                    asm {
-                        source {
-                            srcDirs "d3"
-                        }
-                    }
-                }
-            }
-        }
-
-        expect:
-        project.sources.exe.asm.source.srcDirs*.name == ["d1", "d2"]
-        project.sources.lib.asm.source.srcDirs*.name == ["d3"]
-    }
-
-    def "creates assemble tasks for each non-empty executable source set "() {
-        when:
-        touch("src/test/asm/dummy.s")
-        touch("src/test/anotherOne/dummy.s")
-        dsl {
-            apply plugin: AssemblerNativeBinariesPlugin
-            sources {
-                test {
-                    anotherOne(AssemblerSourceSet) {}
-                    emptyOne(AssemblerSourceSet) {}
-                }
-            }
-            executables {
-                test {
-                    binaries.all { NativeBinary binary ->
-                        binary.assembler.args "ARG1", "ARG2"
-                    }
-                }
-            }
-        }
-
-        then:
-        ExecutableBinary binary = project.binaries.testExecutable
-        binary.tasks.withType(Assemble)*.name == ["assembleTestExecutableTestAnotherOne", "assembleTestExecutableTestAsm"]
-
-        and:
-        binary.tasks.withType(Assemble).each { compile ->
-            compile instanceof Assemble
-            compile.toolChain == binary.toolChain
-            compile.assemblerArgs == ["ARG1", "ARG2"]
-        }
-
-        and:
-        def linkTask = binary.tasks.link
-        linkTask TaskDependencyMatchers.dependsOn("assembleTestExecutableTestAnotherOne", "assembleTestExecutableTestAsm")
-    }
-
-    def "creates assemble tasks for each library source set"() {
-        when:
-        touch("src/test/asm/dummy.s")
-        touch("src/test/anotherOne/dummy.s")
-        dsl {
-            apply plugin: AssemblerNativeBinariesPlugin
-            sources {
-                test {
-                    anotherOne(AssemblerSourceSet) {}
-                    emptyOne(AssemblerSourceSet) {}
-                }
-            }
-            libraries {
-                test {
-                    binaries.all {
-                        assembler.args "ARG1", "ARG2"
-                    }
-                    binaries.withType(SharedLibraryBinary) {
-                        assembler.args "SHARED1", "SHARED2"
-                    }
-                    binaries.withType(StaticLibraryBinary) {
-                        assembler.args "STATIC1", "STATIC2"
-                    }
-                }
-            }
-        }
-
-        then:
-        SharedLibraryBinary sharedLib = project.binaries.testSharedLibrary
-        sharedLib.tasks.withType(Assemble)*.name == ["assembleTestSharedLibraryTestAnotherOne", "assembleTestSharedLibraryTestAsm"]
-        sharedLib.tasks.withType(Assemble).each { compile ->
-            compile.toolChain == sharedLib.toolChain
-            compile.assemblerArgs == ["ARG1", "ARG2", "SHARED1", "SHARED2"]
-        }
-        def sharedLinkTask = sharedLib.tasks.link
-        sharedLinkTask TaskDependencyMatchers.dependsOn("assembleTestSharedLibraryTestAnotherOne", "assembleTestSharedLibraryTestAsm")
-
-        and:
-        StaticLibraryBinary staticLib = project.binaries.testStaticLibrary
-        staticLib.tasks.withType(Assemble)*.name == ["assembleTestStaticLibraryTestAnotherOne", "assembleTestStaticLibraryTestAsm"]
-        staticLib.tasks.withType(Assemble).each { compile ->
-            compile.toolChain == sharedLib.toolChain
-            compile.assemblerArgs == ["ARG1", "ARG2", "STATIC1", "STATIC2"]
-        }
-        def staticLibTask = staticLib.tasks.createStaticLib
-        staticLibTask TaskDependencyMatchers.dependsOn("assembleTestStaticLibraryTestAnotherOne", "assembleTestStaticLibraryTestAsm")
-    }
-
-    def touch(String filePath) {
-        GFileUtils.touch(project.file(filePath))
-    }
-
-    def dsl(Closure closure) {
-        closure.delegate = project
-        closure()
-        project.evaluate()
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/tasks/AssemblerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/tasks/AssemblerTest.groovy
deleted file mode 100644
index 4c6b8da..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/assembler/tasks/AssemblerTest.groovy
+++ /dev/null
@@ -1,65 +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.nativebinaries.language.assembler.tasks
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.api.tasks.WorkResult
-import org.gradle.nativebinaries.platform.internal.PlatformInternal
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class AssemblerTest extends Specification {
-    def testDir = new TestNameTestDirectoryProvider().testDirectory
-    Assemble assembleTask = TestUtil.createTask(Assemble)
-    def toolChain = Mock(ToolChainInternal)
-    def platform = Mock(PlatformInternal)
-    def platformToolChain = Mock(PlatformToolChain)
-    Compiler<AssembleSpec> assembler = Mock(Compiler)
-
-    def "executes using the Assembler"() {
-        def inputDir = testDir.file("sourceFile")
-        def result = Mock(WorkResult)
-        when:
-        assembleTask.toolChain = toolChain
-        assembleTask.targetPlatform = platform
-        assembleTask.assemblerArgs = ["arg"]
-        assembleTask.objectFileDir = testDir.file("outputFile")
-        assembleTask.source inputDir
-        assembleTask.execute()
-
-        then:
-        _ * toolChain.outputType >> "c"
-        _ * platform.compatibilityString >> "p"
-        1 * toolChain.target(platform) >> platformToolChain
-        1 * platformToolChain.createAssembler() >> assembler
-        1 * assembler.execute({ AssembleSpec spec ->
-            assert spec.sourceFiles*.name == ["sourceFile"]
-            assert spec.args == ['arg']
-            assert spec.allArgs == ['arg']
-            assert spec.objectFileDir.name == "outputFile"
-            true
-        }) >> result
-        1 * result.didWork >> true
-        0 * _._
-
-        and:
-        assembleTask.didWork
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompilerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompilerTest.groovy
deleted file mode 100644
index 269cc9d..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CleanCompilingNativeCompilerTest.groovy
+++ /dev/null
@@ -1,68 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.TaskOutputsInternal
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-import org.gradle.api.internal.tasks.SimpleWorkResult
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class CleanCompilingNativeCompilerTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-
-    def delegateCompiler = Mock(org.gradle.api.internal.tasks.compile.Compiler)
-    def incrementalCompileProcessor = Mock(IncrementalCompileProcessor)
-    def task = Mock(TaskInternal)
-    def outputs = Mock(TaskOutputsInternal)
-    def includesParser = Mock(SourceIncludesParser);
-    def compiler = new CleanCompilingNativeCompiler(task, includesParser, null, null, null, delegateCompiler)
-
-    def "cleans outputs and delegates spec for compilation"() {
-        def spec = Mock(NativeCompileSpec)
-        def existingSource = temporaryFolder.file("existing")
-        def newSource = temporaryFolder.file("new")
-        def outputFile = temporaryFolder.createFile("output", "previous")
-
-        def sources = [existingSource, newSource]
-        def compilation = Mock(IncrementalCompilation)
-
-        when:
-        outputFile.assertExists()
-
-        and:
-        spec.getSourceFiles() >> sources
-        incrementalCompileProcessor.processSourceFiles(_) >> compilation
-        0 * compilation._
-
-        and:
-        def result = compiler.doIncrementalCompile(incrementalCompileProcessor, spec)
-
-        then:
-        1 * spec.getObjectFileDir() >> outputFile.parentFile
-        1 * task.getOutputs() >> outputs
-        1 * outputs.previousFiles >> new SimpleFileCollection(outputFile)
-        0 * spec._
-        1 * delegateCompiler.execute(spec) >> new SimpleWorkResult(false)
-
-        and:
-        result.didWork
-        outputFile.assertDoesNotExist()
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializerTest.groovy
deleted file mode 100644
index d5529a3..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/CompilationStateSerializerTest.groovy
+++ /dev/null
@@ -1,90 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.messaging.serialize.SerializerSpec
-
-class CompilationStateSerializerTest extends SerializerSpec {
-    def state = new CompilationState()
-    private CompilationStateSerializer serializer = new CompilationStateSerializer()
-
-    def "serializes empty state"() {
-        expect:
-        with (serialized) {
-            sourceInputs.empty
-            fileStates.isEmpty()
-        }
-    }
-
-    def "serializes source inputs"() {
-        when:
-        def fileOne = new File("one")
-        def fileTwo = new File("two")
-        state.sourceInputs << fileOne << fileTwo
-
-        then:
-        with (serialized) {
-            sourceInputs == [fileOne, fileTwo]
-            fileStates.isEmpty()
-        }
-    }
-
-    def "serializes file state"() {
-        when:
-        def fileEmpty = new File("empty")
-        state.fileStates.put(fileEmpty, new CompilationFileState(new byte[0]))
-
-        def fileTwo = new File("two")
-        def stateTwo = new CompilationFileState("FooBar".getBytes())
-        stateTwo.sourceIncludes = createSourceIncludes("<system>", '"quoted"', "MACRO")
-        stateTwo.resolvedIncludes = [resolvedInclude("ONE"), resolvedInclude("TWO")]
-        state.fileStates.put(fileTwo, stateTwo)
-
-        then:
-        def newState = serialized
-        newState.sourceInputs.empty
-        newState.fileStates.size() == 2
-
-        def emptyCompileState = newState.getState(fileEmpty)
-        emptyCompileState.hash.length == 0
-        emptyCompileState.sourceIncludes.macroIncludes.empty
-        emptyCompileState.sourceIncludes.quotedIncludes.empty
-        emptyCompileState.sourceIncludes.systemIncludes.empty
-        emptyCompileState.resolvedIncludes.empty
-
-        def otherCompileState = newState.getState(fileTwo)
-        new String(otherCompileState.hash) == "FooBar"
-        otherCompileState.sourceIncludes.systemIncludes == ["system"]
-        otherCompileState.sourceIncludes.quotedIncludes == ["quoted"]
-        otherCompileState.sourceIncludes.macroIncludes == ["MACRO"]
-        otherCompileState.resolvedIncludes == [resolvedInclude("ONE"), resolvedInclude("TWO")] as Set
-    }
-
-    private static DefaultSourceIncludes createSourceIncludes(String... strings) {
-        final DefaultSourceIncludes sourceIncludes = new DefaultSourceIncludes()
-        sourceIncludes.addAll(strings as List<String>)
-        sourceIncludes
-    }
-
-    private static ResolvedInclude resolvedInclude(String value) {
-        return new ResolvedInclude(value, new File(value))
-    }
-
-    private CompilationState getSerialized() {
-        serialize(state, serializer) as CompilationState
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParserTest.groovy
deleted file mode 100644
index d5b8c82..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesParserTest.groovy
+++ /dev/null
@@ -1,68 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.nativebinaries.language.c.internal.incremental.sourceparser.CSourceParser
-import spock.lang.Specification
-
-class DefaultSourceIncludesParserTest extends Specification {
-    def sourceParser = Mock(CSourceParser)
-    def sourceDetails = Mock(CSourceParser.SourceDetails)
-
-    def "imports are not included in includes"() {
-        given:
-        def file = new File("test")
-
-        when:
-        def includesParser = new DefaultSourceIncludesParser(sourceParser, false)
-
-        1 * sourceParser.parseSource(file) >> sourceDetails
-        1 * sourceDetails.includes >> ['"quoted"', '<system>', 'DEFINED']
-        0 * sourceDetails._
-
-        and:
-        def includes = includesParser.parseIncludes(file)
-
-        then:
-        includes.quotedIncludes == ["quoted"]
-        includes.systemIncludes == ["system"]
-        includes.macroIncludes == ["DEFINED"]
-    }
-
-
-    def "imports are included in includes"() {
-        given:
-        def file = new File("test")
-
-        when:
-        def includesParser = new DefaultSourceIncludesParser(sourceParser, true)
-
-        1 * sourceParser.parseSource(file) >> sourceDetails
-        1 * sourceDetails.includes >> ['"quoted"', '<system>', 'DEFINED']
-        1 * sourceDetails.imports >> ['"quotedImport"', '<systemImport>', 'DEFINED_IMPORT']
-        0 * sourceDetails._
-
-        and:
-        def includes = includesParser.parseIncludes(file)
-
-        then:
-        includes.quotedIncludes == ["quoted", "quotedImport"]
-        includes.systemIncludes == ["system", "systemImport"]
-        includes.macroIncludes == ["DEFINED", "DEFINED_IMPORT"]
-    }
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolverTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolverTest.groovy
deleted file mode 100644
index 4bc6fd8..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/DefaultSourceIncludesResolverTest.groovy
+++ /dev/null
@@ -1,154 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class DefaultSourceIncludesResolverTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-
-    def testDirectory = temporaryFolder.testDirectory
-    def sourceDirectory = testDirectory.createDir("sources")
-    def quotedIncludes = []
-    def systemIncludes = []
-    def macroIncludes = []
-    def includesParser = Mock(SourceIncludesParser)
-    def includes
-    def includePaths = []
-
-    def setup() {
-        includes = Mock(SourceIncludes)
-        includesParser.parseIncludes(sourceFile) >> includes
-        includes.getQuotedIncludes() >> quotedIncludes
-        includes.getSystemIncludes() >> systemIncludes
-        includes.getMacroIncludes() >> macroIncludes
-    }
-
-    protected TestFile getSourceFile() {
-        sourceDirectory.file('source.c')
-    }
-
-    def getDependencies() {
-        return new DefaultSourceIncludesResolver(includePaths).resolveIncludes(sourceFile, includes) as List
-    }
-
-    def "handles source file with no includes"() {
-        expect:
-        dependencies == []
-    }
-
-    def "ignores include files that do not exist"() {
-        when:
-        quotedIncludes << "test.h"
-        systemIncludes << "system"
-
-        then:
-        dependencies == []
-    }
-
-    def "locates quoted includes in same directory"() {
-        when:
-        final header1 = sourceDirectory.createFile("test1.h")
-        final header2 = sourceDirectory.createFile("test2.h")
-
-        and:
-        quotedIncludes << "test1.h" << "test2.h"
-
-        then:
-        dependencies == deps(header1, header2)
-    }
-
-    def "locates quoted includes relative to source directory"() {
-        when:
-        final header1 = sourceDirectory.createFile("test1.h")
-        final header2 = sourceDirectory.file("nested", "test2.h").createFile()
-        final header3 = sourceDirectory.file("..", "sibling", "test3.h").createFile()
-
-        and:
-        quotedIncludes << "test1.h" << "nested/test2.h" << "../sibling/test3.h"
-
-        then:
-        dependencies.collect {it.file} == [header1, header2, header3]
-    }
-
-    def "does not locate system includes in same directory"() {
-        when:
-        sourceDirectory.file("system.h").createFile()
-
-        and:
-        systemIncludes << "system.h"
-
-        then:
-        dependencies == []
-    }
-
-    def "locates includes in path"() {
-        when:
-        def includeDir1 = testDirectory.file("include1")
-        final header11 = includeDir1.file("test11.h").createFile()
-        final header12 = includeDir1.file("test12.h").createFile()
-        def includeDir2 = testDirectory.file("include2")
-        final header21 = includeDir1.file("test21.h").createFile()
-        final header22 = includeDir1.file("test22.h").createFile()
-
-        and:
-        includePaths << includeDir1 << includeDir2
-        quotedIncludes << "test11.h" << "test21.h"
-        systemIncludes << "test12.h" << "test22.h"
-
-        then:
-        dependencies == deps(header11, header21, header12, header22)
-    }
-
-    def "searches relative before searching include path"() {
-        when:
-        final relativeHeader = sourceDirectory.createFile("test.h")
-        final includeDir = testDirectory.file("include")
-        includeDir.createFile("test.h")
-        final otherHeader = includeDir.createFile("other.h")
-
-        and:
-        includePaths << includeDir
-        quotedIncludes << "test.h" << "other.h"
-
-        then:
-        dependencies == deps(relativeHeader, otherHeader)
-    }
-
-    def "includes unknown source dependency for first macro include"() {
-        when:
-        macroIncludes << 'DEFINE_1' << 'DEFINE_2'
-
-        then:
-        dependencies.size() == 1
-        with (dependencies[0]) {
-            unknown
-            include == 'DEFINE_1'
-            file == null
-        }
-    }
-
-    def deps(File... files) {
-        return files.collect {dep(it)}
-    }
-
-    def dep(File dependencyFile) {
-        return new ResolvedInclude(dependencyFile.name, dependencyFile)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessorTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessorTest.groovy
deleted file mode 100644
index 58a44f9..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalCompileProcessorTest.groovy
+++ /dev/null
@@ -1,422 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.api.internal.changedetection.state.FileSnapshotter
-import org.gradle.cache.PersistentStateCache
-import org.gradle.internal.hash.HashUtil
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class IncrementalCompileProcessorTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    def includesParser = Mock(SourceIncludesParser)
-    def dependencyParser = Mock(SourceIncludesResolver)
-    def fileSnapshotter = Stub(FileSnapshotter)
-    def stateCache = new DummyPersistentStateCache()
-    def incrementalCompileProcessor = new IncrementalCompileProcessor(stateCache, dependencyParser, includesParser, fileSnapshotter)
-
-    def source1 = sourceFile("source1")
-    def source2 = sourceFile("source2")
-    def dep1 = sourceFile("dep1")
-    def dep2 = sourceFile("dep2")
-    def dep3 = sourceFile("dep3")
-    def dep4 = sourceFile("dep4")
-    def sourceFiles
-
-    Map<TestFile, List<ResolvedInclude>> graph = [:]
-    List<TestFile> modified = []
-
-    def setup() {
-        fileSnapshotter.snapshot(_) >> { File file ->
-            return Stub(FileSnapshotter.FileSnapshot) {
-                getHash() >> HashUtil.sha1(file).asByteArray()
-            }
-        }
-
-        // S1 - D1 \
-        //    \ D2  \
-        //           D3
-        // S2 ------/
-        //    \ D4
-
-        graph[source1] = deps(dep1, dep2)
-        graph[source2] = deps(dep3, dep4)
-        graph[dep1] = deps(dep3)
-        graph[dep2] = []
-        graph[dep3] = []
-        graph[dep4] = []
-    }
-
-    def initialFiles() {
-
-        graph.keySet().each { TestFile sourceFile ->
-            parse(sourceFile)
-            resolve(sourceFile)
-        }
-
-        sourceFiles = [source1, source2]
-        with (state) {
-            assert recompile == [source1, source2]
-            assert removed == []
-        }
-    }
-
-    def parse(TestFile sourceFile) {
-        final Set<ResolvedInclude> deps = graph[sourceFile]
-        SourceIncludes includes = includes(deps)
-        1 * includesParser.parseIncludes(sourceFile) >> includes
-    }
-
-    def resolve(TestFile sourceFile) {
-        Set<ResolvedInclude> deps = graph[sourceFile]
-        SourceIncludes includes = includes(deps)
-        1 * dependencyParser.resolveIncludes(sourceFile, includes) >> deps
-    }
-
-    private static SourceIncludes includes(Set<ResolvedInclude> deps) {
-        def includes = new DefaultSourceIncludes()
-        includes.addAll(deps.collect { '<' + it.file.name + '>' })
-        return includes
-    }
-
-    def added(TestFile sourceFile) {
-        modified << sourceFile
-        graph[sourceFile] = []
-    }
-
-    def sourceAdded(TestFile sourceFile, def deps = []) {
-        sourceFiles << sourceFile
-        modified << sourceFile
-        graph[sourceFile] = deps
-    }
-
-    def modified(TestFile sourceFile, def deps = null) {
-        modified << sourceFile
-        sourceFile << "More text"
-        if (deps != null) {
-            graph[sourceFile] = deps
-        }
-    }
-
-    def sourceRemoved(TestFile sourceFile) {
-        sourceFiles.remove(sourceFile)
-        graph.remove(sourceFile)
-    }
-
-    def dependencyRemoved(TestFile sourceFile) {
-        graph.remove(sourceFile)
-        sourceFile.delete()
-    }
-
-    def "detects unchanged source files"() {
-        given:
-        initialFiles()
-
-        expect:
-        checkCompile recompiled: [], removed: []
-    }
-
-    def "detects new source files"() {
-        given:
-        initialFiles()
-
-        when:
-        def file3 = sourceFile("file3")
-        sourceAdded(file3)
-
-        then:
-        checkCompile recompiled: [file3], removed: []
-    }
-
-    def "detects removed source file"() {
-        given:
-        initialFiles()
-
-        when:
-        sourceRemoved(source2)
-        graph.remove(dep4)
-
-        then:
-        checkCompile recompiled: [], removed: [source2]
-    }
-
-    def "detects removed source file with multiple dependencies"() {
-        given:
-        initialFiles()
-
-        when:
-        sourceRemoved(source1)
-        graph.remove(dep1)
-        graph.remove(dep2)
-
-        then:
-        checkCompile recompiled: [], removed: [source1]
-    }
-
-    def "detects source file changed"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(source2)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "detects dependency file changed"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(dep4)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "detects dependency file removed"() {
-        given:
-        initialFiles()
-
-        when:
-        dependencyRemoved(dep4)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "detects shared dependency file changed"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(dep3)
-
-        then:
-        checkCompile recompiled: [source1, source2], removed: []
-    }
-
-    def "detects source file change with new dependencies"() {
-        given:
-        initialFiles()
-
-        when:
-        def dep5 = sourceFile("dep5")
-        added(dep5)
-        modified(source2, deps(dep3, dep4, dep5))
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-
-        when:
-        modified(dep5)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "detects unchanged source file change with different resolved dependencies"() {
-        given:
-        initialFiles()
-
-        when:
-        def dep5 = sourceFile("dep5")
-        graph[dep5] = []
-
-        resolve(source1)
-        resolve(dep1)
-        resolve(dep2)
-        resolve(dep3)
-        parse(dep5)
-        resolve(dep5)
-
-        1 * dependencyParser.resolveIncludes(source2, includes(deps(dep3, dep4))) >> deps(dep3, dep5)
-
-        then:
-        with (state) {
-            recompile == [source2]
-            removed == []
-        }
-    }
-
-    def "detects dependency file change with new dependencies"() {
-        given:
-        initialFiles()
-
-        when:
-        def dep5 = sourceFile("dep5")
-        modified(dep4, deps(dep5))
-        added(dep5)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-
-        when:
-        modified(dep5)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "detects dependency file change adding dependency cycle"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(dep3, deps(dep1))
-
-        then:
-        checkCompile recompiled: [source1, source2], removed: []
-    }
-
-    def "detects shared dependency file changed with new dependency"() {
-        given:
-        initialFiles()
-
-        when:
-        def dep5 = sourceFile("dep5")
-        modified(dep3, deps(dep5))
-        added(dep5)
-
-        then:
-        checkCompile recompiled: [source1, source2], removed: []
-
-        when:
-        modified(dep5)
-
-        then:
-        checkCompile recompiled: [source1, source2], removed: []
-    }
-
-    def "detects changed dependency with new source file including that dependency"() {
-        given:
-        initialFiles()
-
-        when:
-        def file3 = sourceFile("file3")
-        sourceAdded(file3, deps(dep4))
-        modified(dep4)
-
-        then:
-        checkCompile recompiled: [source2, file3], removed: []
-    }
-
-    def "detects source file removed then readded"() {
-        given:
-        initialFiles()
-
-        when:
-        sourceRemoved(source2)
-        graph.remove(dep4)
-
-        then:
-        checkCompile recompiled: [], removed: [source2]
-
-        when:
-        sourceAdded(source2, [])
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def "handles source file that is also a dependency"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(dep2, deps(source2))
-
-        then:
-        checkCompile recompiled: [source1], removed: []
-
-        when:
-        modified(dep4)
-
-        then:
-        checkCompile recompiled: [source1, source2], removed: []
-    }
-
-    def "reports source file changed to dependency as removed"() {
-        given:
-        initialFiles()
-
-        when:
-        modified(dep2, deps(source2))
-        sourceFiles.remove(source2)
-
-        then:
-        checkCompile recompiled: [source1], removed: [source2]
-
-        when:
-        sourceFiles.add(source2)
-
-        then:
-        checkCompile recompiled: [source2], removed: []
-    }
-
-    def checkCompile(Map<String, List<File>> args) {
-        parseAndResolve()
-        with (state) {
-            assert recompile == args['recompiled']
-            assert removed == args['removed']
-        }
-        return true
-    }
-
-    def parseAndResolve() {
-        modified.each {
-            parse(it)
-        }
-        modified.clear()
-        graph.keySet().each { TestFile sourceFile ->
-            resolve(sourceFile)
-        }
-        true
-    }
-
-    def getState() {
-        incrementalCompileProcessor.processSourceFiles(sourceFiles)
-    }
-
-    def sourceFile(def name) {
-        tmpDir.createFile(name) << "initial text"
-    }
-
-    Set<ResolvedInclude> deps(File... dep) {
-        dep.collect {new ResolvedInclude(it.name, it)} as Set
-    }
-
-    class DummyPersistentStateCache implements PersistentStateCache<CompilationState> {
-        private CompilationState compilationState
-
-        CompilationState get() {
-            return compilationState
-        }
-
-        void set(CompilationState newValue) {
-            this.compilationState = newValue
-        }
-
-        void update(PersistentStateCache.UpdateAction<CompilationState> updateAction) {
-
-        }
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompilerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompilerTest.groovy
deleted file mode 100644
index 8930690..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/IncrementalNativeCompilerTest.groovy
+++ /dev/null
@@ -1,54 +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.nativebinaries.language.c.internal.incremental
-
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class IncrementalNativeCompilerTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-
-    def delegateCompiler = Mock(org.gradle.api.internal.tasks.compile.Compiler)
-    def incrementalCompileProcessor = Mock(IncrementalCompileProcessor)
-    def includesParser = Mock(SourceIncludesParser);
-    def compiler = new IncrementalNativeCompiler(null, includesParser, null, null, null, delegateCompiler)
-
-    def "updates spec for incremental compilation"() {
-        def spec = Mock(NativeCompileSpec)
-        def existingSource = temporaryFolder.file("existing")
-        def newSource = temporaryFolder.file("new")
-        def removedSource = temporaryFolder.file("removed")
-
-        def sources = [existingSource, newSource]
-        def compilation = Mock(IncrementalCompilation)
-
-        when:
-        spec.getSourceFiles() >> sources
-        incrementalCompileProcessor.processSourceFiles(_) >> compilation
-        compilation.getRecompile() >> [newSource]
-        compilation.getRemoved() >> [removedSource]
-
-        and:
-        compiler.doIncrementalCompile(incrementalCompileProcessor, spec)
-
-        then:
-        1 * spec.setSourceFiles([newSource])
-        1 * spec.setRemovedSourceFiles([removedSource])
-        1 * delegateCompiler.execute(spec)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReaderTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReaderTest.groovy
deleted file mode 100644
index 7b03685..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/PreprocessingReaderTest.groovy
+++ /dev/null
@@ -1,72 +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.nativebinaries.language.c.internal.incremental.sourceparser
-
-import spock.lang.Specification
-
-class PreprocessingReaderTest extends Specification {
-    private static final String BN = "\\" + System.getProperty("line.separator")
-    String input
-
-    def getOutput() {
-        def reader = new PreprocessingReader(new StringReader(input))
-        return reader.text.trim()
-    }
-
-    def "removes line continuation characters"() {
-        when:
-        input = """Here is a ${BN}single line ${BN}with continuations \\${BN}and \\ slashes\\\t\n too\\ \n."""
-
-        then:
-        output == "Here is a single line with continuations \\and \\ slashes\\\t\n too\\ \n."
-    }
-
-    def "replaces inline comments with space"() {
-        when:
-        input = """
-Here/* comment */is a string/*
-multiline
-comment
-here */that contains/**
- comment /* containing ** / ${BN} characters */several inline comments.
-"""
-
-        then:
-        output == "Here is a string that contains several inline comments."
-    }
-
-    def "replaces line comments with space"() {
-        when:
-        input = """
-Here is a // comment
-string
-// Line comment
-with interspersed // comment /* with inline comment */ and // other comment */ chars
-line comments.
-"""
-
-        then:
-        output == "Here is a \nstring\n\nwith interspersed \nline comments."
-    }
-
-    def "can cope with multiple unescaped and escaped \\r characters"() {
-        when:
-        input =  "Here \r\r\\\r\\\r${BN}\\\r\\\r\\\r\\\r."
-        then:
-        output == "Here \r\r\\\r\\\r\\\r\\\r\\\r\\\r."
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy
deleted file mode 100644
index c0a0a3d..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy
+++ /dev/null
@@ -1,386 +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.nativebinaries.language.c.internal.incremental.sourceparser
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class RegexBackedCSourceParserTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-    CSourceParser parser = new RegexBackedCSourceParser()
-
-    protected TestFile getSourceFile() {
-        testDirectory.file('source.c')
-    }
-
-    TestFile getTestDirectory() {
-        temporaryFolder.testDirectory
-    }
-
-    def getParsedSource() {
-        parser.parseSource(sourceFile)
-    }
-
-    def getIncludes() {
-        return parsedSource.includes
-    }
-
-    def getImports() {
-        return parsedSource.imports
-    }
-
-    def getFound() {
-        return includes + imports
-    }
-    
-    def noIncludes() {
-        assert includes == []
-        true
-    }
-    
-    def noImports() {
-        assert imports == []
-        true
-    }
-
-    def useDirective(String directive) {
-        sourceFile.text = sourceFile.text.replace("include", directive)
-    }
-
-    def "parses file with no includes"() {
-        when:
-        sourceFile << ""
-
-        then:
-        noIncludes()
-        noImports()
-    }
-
-    def "finds quoted include"() {
-        when:
-        sourceFile << """
-    #include "test.h"
-"""
-
-        then:
-        includes == ['"test.h"']
-
-        and:
-        noImports()
-    }
-
-    def "finds system include"() {
-        when:
-        sourceFile << """
-    #include <test.h>
-"""
-
-        then:
-        includes == ['<test.h>']
-        
-        and:
-        noImports()
-    }
-
-    def "finds defined include"() {
-        when:
-        sourceFile << """
-    #include DEFINED
-"""
-
-        then:
-        includes == ['DEFINED']
-
-        and:
-        noImports()
-    }
-
-    def "finds multiple includes"() {
-        when:
-        sourceFile << """
-    #include "test1"
-    #include "test2"
-    #include <system1>
-    #include <system2>
-    #include DEFINED
-"""
-        then:
-        includes == ['"test1"', '"test2"', '<system1>', '<system2>', 'DEFINED']
-        
-        and:
-        noImports()
-    }
-
-    def "finds quoted import"() {
-        when:
-        sourceFile << """
-            #import "test.h"
-        """
-
-        then:
-        imports == ['"test.h"']
-
-        and:
-        noIncludes()
-    }
-
-    def "finds system import"() {
-        when:
-        sourceFile << """
-            #import <test.h>
-        """
-
-        then:
-        imports == ['<test.h>']
-        
-        and:
-        noIncludes()
-    }
-
-    def "finds defined import"() {
-        when:
-        sourceFile << """
-            #import DEFINED
-        """
-
-        then:
-        imports == ['DEFINED']
-
-        and:
-        noIncludes()
-    }
-
-    def "finds multiple imports"() {
-        when:
-        sourceFile << """
-    #import "test1"
-    #import "test2"
-    #import <system1>
-    #import <system2>
-    #import DEFINED
-"""
-        then:
-        imports == ['"test1"', '"test2"', '<system1>', '<system2>', 'DEFINED']
-
-        and:
-        noIncludes()
-    }
-
-    def "finds mixed import include statement imports"() {
-        when:
-        sourceFile << """
-    #import "test1"
-    #include "test2"
-    #import "test3"
-    #include "test4"
-    #import <system1>
-    #import <system2>
-    #include <system3>
-    #import <system4>
-    #include DEFINED1
-    #import DEFINED2
-"""
-        then:
-        includes == ['"test2"', '"test4"', '<system3>', 'DEFINED1']
-        imports == ['"test1"', '"test3"', '<system1>', '<system2>', '<system4>', 'DEFINED2']
-    }
-
-    @Unroll
-    def "finds #directive surrounded by different whitespace"() {
-        when:
-        sourceFile << """
-#include     "test1"
-#include\t"test2"\t
-\t#include\t"test3"
-#include     <system1>
-#include\t<system2>\t
-\t#include\t<system3>
-"""
-        and:
-        useDirective(directive)
-
-        then:
-        found == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', '<system3>']
-
-        where:
-        directive << ["include", "import"]
-    }
-
-    @Unroll
-    def "finds #directive where whitespace surrounds the # character"() {
-        when:
-        sourceFile << """
-  #  include   "test1"
-\t#\tinclude "test2"
-
-  #  include   <system1>
-\t#\tinclude <system2>
-"""
-        and:
-        useDirective(directive)
-
-        then:
-        found == ['"test1"', '"test2"', '<system1>', '<system2>']
-
-        where:
-        directive << ["include", "import"]
-    }
-
-    def "ignores comment after directive"() {
-        when:
-        sourceFile << """
-#include "test1"  // A comment here
-#include "test2" /* A comment here */
-#include "test3" /*
-   A comment here
-*/
-#include <system1>  // A comment here
-#include <system2> /* A comment here */
-#include <system3> /*
-   A comment here
-*/
-"""
-        then:
-        includes == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', '<system3>']
-    }
-
-    @Unroll
-    def "finds #directive where comment in place of whitespace"() {
-        when:
-        sourceFile << """
-#include/* a comment here*/"test1"
-#/* a
-    comment
-    here*/include "test2"
-/* a comment here*/#include/* a comment here*/"test3"
-#include/* a comment here*/<system1>
-#/* a comment here*/include <system2>
-/* a comment here*/#include/* a comment here*/DEFINED
-"""
-        useDirective(directive)
-
-        then:
-        found == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', 'DEFINED']
-
-        where:
-        directive << ["include", "import"]
-    }
-
-    def "find quoted include with special characters"() {
-        when:
-        sourceFile << """
-    #include "$included"
-    #import "$included"
-"""
-        then:
-        includes == ['"' + included + '"']
-        imports == ['"' + included + '"']
-
-        where:
-        included << ["test'file", "testfile'", "'testfile'", "test<>file", "test>file", "<testFile>", "test<file", "test file"]
-    }
-
-    def "find system include with special characters"() {
-        when:
-        sourceFile << """
-    #include <$included>
-    #import <$included>
-"""
-        then:
-        includes == ['<' + included + '>']
-        imports == ['<' + included + '>']
-
-        where:
-        included << ["test'file", "testfile'", "'testfile'", "test<file", "test\"file", "\"testFile\"", "test file"]
-    }
-
-    def "find various defined includes"() {
-        when:
-        sourceFile << """
-    #include $included
-    #import $included
-"""
-        then:
-        includes == [included]
-        imports == [included]
-
-        where:
-        included << ["DEFINED", "mixedDefined", "DEF_INED", "_DEFINED", "__DEFINED__"]
-    }
-
-    @Unroll
-    def "ignores #directive inside a quoted string"() {
-        when:
-        sourceFile << """
-    printf("use #include <stdio.h>");
-    printf("use #include \\"test1\\");
-"""
-        and:
-        useDirective(directive)
-
-        then:
-        noIncludes()
-        noImports()
-
-        where:
-        directive << ["include", "import"]
-    }
-
-    @Unroll
-    def "ignores #directive that is commented out"() {
-        when:
-        sourceFile << """
-/*
-    #include "test1"
-    #include <system1>
-*/
-/* #include "test2" */
-/* #include <system3> */
-
-//    #include "test3"
-//    #include <system3>
-"""
-        and:
-        useDirective(directive)
-
-        then:
-        noIncludes()
-        noImports()
-
-        where:
-        directive << ["include", "import"]
-    }
-
-    def "detects imports with line=continuation"() {
-        when:
-        sourceFile << """
-#include \\
-"test1"
-#\\
-include\\
- "test2"
-#incl\\
-ude "te\\
-st3"
-"""
-
-        then:
-        includes == ['"test1"', '"test2"', '"test3"']
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPluginTest.groovy
deleted file mode 100644
index 08e28c3..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/plugins/CNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,48 +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.nativebinaries.language.c.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.c.CSourceSet
-import org.gradle.nativebinaries.language.AbstractNativeBinariesPluginTest
-import org.gradle.nativebinaries.language.c.tasks.CCompile
-import org.gradle.util.TestUtil
-
-class CNativeBinariesPluginTest extends AbstractNativeBinariesPluginTest{
-    final def project = TestUtil.createRootProject()
-
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        return CNativeBinariesPlugin
-    }
-
-    @Override
-    Class<? extends LanguageSourceSet> getSourceSetClass() {
-        return CSourceSet
-    }
-
-    @Override
-    Class<? extends Plugin> getCompileTaskClass() {
-        return CCompile
-    }
-
-    @Override
-    String getPluginName() {
-        return "c"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/tasks/CCompileTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/tasks/CCompileTest.groovy
deleted file mode 100644
index 5524eb0..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/c/tasks/CCompileTest.groovy
+++ /dev/null
@@ -1,68 +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.nativebinaries.language.c.tasks
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.api.tasks.WorkResult
-import org.gradle.nativebinaries.platform.internal.PlatformInternal
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class CCompileTest extends Specification {
-    def testDir = new TestNameTestDirectoryProvider().testDirectory
-    CCompile cCompile = TestUtil.createTask(CCompile)
-    def toolChain = Mock(ToolChainInternal)
-    def platform = Mock(PlatformInternal)
-    def platformToolChain = Mock(PlatformToolChain)
-    Compiler<CppCompileSpec> cCompiler = Mock(Compiler)
-
-    def "executes using the C Compiler"() {
-        def sourceFile = testDir.createFile("sourceFile")
-        def result = Mock(WorkResult)
-        when:
-        cCompile.toolChain = toolChain
-        cCompile.targetPlatform = platform
-        cCompile.compilerArgs = ["arg"]
-        cCompile.macros = [def: "value"]
-        cCompile.objectFileDir = testDir.file("outputFile")
-        cCompile.source sourceFile
-        cCompile.execute()
-
-        then:
-        _ * toolChain.outputType >> "c"
-        _ * platform.compatibilityString >> "p"
-        1 * toolChain.target(platform) >> platformToolChain
-        1 * platformToolChain.createCCompiler() >> cCompiler
-        1 * cCompiler.execute({ CCompileSpec spec ->
-            assert spec.sourceFiles*.name== ["sourceFile"]
-            assert spec.args == ['arg']
-            assert spec.allArgs == ['arg']
-            assert spec.macros == [def: 'value']
-            assert spec.objectFileDir.name == "outputFile"
-            true
-        }) >> result
-        1 * result.didWork >> true
-        0 * _._
-
-        and:
-        cCompile.didWork
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfoTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfoTest.groovy
deleted file mode 100644
index 867471c..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfoTest.groovy
+++ /dev/null
@@ -1,56 +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.nativebinaries.language.cpp.fixtures.binaryinfo
-
-import spock.lang.Specification
-
-class ReadelfBinaryInfoTest extends Specification {
-    def "reads soname value"() {
-        when:
-        def input = """
-Dynamic section at offset 0xdf8 contains 24 entries:
-  Tag        Type                         Name/Value
- 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
- 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
- 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
- 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
- 0x000000000000000e (SONAME)             Library soname: [heythere]
- 0x000000000000000c (INIT)               0x668
-"""
-        def inputLines = input.readLines()
-
-        then:
-        ReadelfBinaryInfo.readSoName(inputLines) == 'heythere'
-    }
-
-    def "returns null for no soname value"() {
-        when:
-        def input = """
-Dynamic section at offset 0xdf8 contains 24 entries:
-  Tag        Type                         Name/Value
- 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
- 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
- 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
- 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
- 0x000000000000000c (INIT)               0x668
-"""
-        def inputLines = input.readLines()
-
-        then:
-        ReadelfBinaryInfo.readSoName(inputLines) == null
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppSourceSetTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppSourceSetTest.groovy
deleted file mode 100644
index 96f12fe..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/internal/DefaultCppSourceSetTest.groovy
+++ /dev/null
@@ -1,66 +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.nativebinaries.language.cpp.internal
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.cpp.internal.DefaultCppSourceSet
-import org.gradle.nativebinaries.Library
-import org.gradle.nativebinaries.LibraryBinary
-import org.gradle.nativebinaries.NativeDependencySet
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class DefaultCppSourceSetTest extends Specification {
-    def parent = Stub(FunctionalSourceSet) {
-        getName() >> "main"
-    }
-    def sourceSet = new DefaultCppSourceSet("cpp", parent, TestUtil.createRootProject())
-
-    def "has useful string representation"() {
-        expect:
-        sourceSet.toString() == "C++ source 'main:cpp'"
-    }
-
-    def "can add a library as a dependency of the source set"() {
-        def library = Mock(Library)
-
-        when:
-        sourceSet.lib(library)
-
-        then:
-        sourceSet.libs.contains(library)
-    }
-
-    def "can add a library binary as a dependency of the binary"() {
-        def library = Mock(LibraryBinary)
-
-        when:
-        sourceSet.lib(library)
-
-        then:
-        sourceSet.libs.contains(library)
-    }
-
-    def "can add a native dependency as a dependency of the binary"() {
-        def dependency = Stub(NativeDependencySet)
-
-        when:
-        sourceSet.lib(dependency)
-
-        then:
-        sourceSet.libs.contains(dependency)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPluginTest.groovy
deleted file mode 100644
index 4f010a7..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/plugins/CppNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,206 +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.nativebinaries.language.cpp.plugins
-
-import org.gradle.api.tasks.TaskDependencyMatchers
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.nativebinaries.ExecutableBinary
-import org.gradle.nativebinaries.NativeBinary
-import org.gradle.nativebinaries.SharedLibraryBinary
-import org.gradle.nativebinaries.StaticLibraryBinary
-import org.gradle.nativebinaries.language.cpp.tasks.CppCompile
-import org.gradle.util.GFileUtils
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class CppNativeBinariesPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def "creates cpp source set with conventional locations for components"() {
-        when:
-        dsl {
-            apply plugin: CppNativeBinariesPlugin
-            executables {
-                exe {}
-            }
-            libraries {
-                lib {}
-            }
-        }
-
-        then:
-        def sourceSets = project.sources
-        sourceSets.size() == 2
-        sourceSets*.name == ["exe", "lib"]
-
-        and:
-        sourceSets.exe instanceof FunctionalSourceSet
-        sourceSets.exe.cpp instanceof CppSourceSet
-        sourceSets.exe.cpp.source.srcDirs == [project.file("src/exe/cpp")] as Set
-        sourceSets.exe.cpp.exportedHeaders.srcDirs == [project.file("src/exe/headers")] as Set
-        project.executables.exe.source == [sourceSets.exe.cpp] as Set
-
-        and:
-        sourceSets.lib instanceof FunctionalSourceSet
-        sourceSets.lib.cpp instanceof CppSourceSet
-        sourceSets.lib.cpp.source.srcDirs == [project.file("src/lib/cpp")] as Set
-        sourceSets.lib.cpp.exportedHeaders.srcDirs == [project.file("src/lib/headers")] as Set
-        project.libraries.lib.source == [sourceSets.lib.cpp] as Set
-    }
-
-    def "can configure source set locations"() {
-        given:
-        dsl {
-            apply plugin: CppNativeBinariesPlugin
-            sources {
-                exe {
-                    cpp {
-                        source {
-                            srcDirs "d1", "d2"
-                        }
-                        exportedHeaders {
-                            srcDirs "h1", "h2"
-                        }
-                    }
-                }
-                lib {
-                    cpp {
-                        source {
-                            srcDirs "d3"
-                        }
-                        exportedHeaders {
-                            srcDirs "h3"
-                        }
-                    }
-                }
-            }
-        }
-
-        expect:
-        def sourceSets = project.sources
-        with (sourceSets.exe.cpp) {
-            source.srcDirs*.name == ["d1", "d2"]
-            exportedHeaders.srcDirs*.name == ["h1", "h2"]
-        }
-
-        with (sourceSets.lib.cpp) {
-            source.srcDirs*.name == ["d3"]
-            exportedHeaders.srcDirs*.name == ["h3"]
-        }
-    }
-
-    def "creates compile tasks for each non-empty executable source set"() {
-        when:
-        touch("src/test/cpp/file.cpp")
-        touch("src/test/anotherCpp/file.cpp")
-        dsl {
-            apply plugin: CppNativeBinariesPlugin
-            sources {
-                test {
-                    anotherCpp(CppSourceSet) {}
-                    emptyOne(CppSourceSet) {}
-                }
-            }
-            executables {
-                test {
-                    binaries.all { NativeBinary binary ->
-                        binary.cppCompiler.define "NDEBUG"
-                        binary.cppCompiler.define "LEVEL", "1"
-                        binary.cppCompiler.args "ARG1", "ARG2"
-                    }
-                }
-            }
-        }
-
-        then:
-        ExecutableBinary binary = project.binaries.testExecutable
-        binary.tasks.withType(CppCompile)*.name == ["compileTestExecutableTestAnotherCpp", "compileTestExecutableTestCpp"]
-
-        and:
-        binary.tasks.withType(CppCompile).each { compile ->
-            compile.toolChain == binary.toolChain
-            compile.macros == [NDEBUG:null, LEVEL:"1"]
-            compile.compilerArgs == ["ARG1", "ARG2"]
-        }
-
-        and:
-        def linkTask = binary.tasks.link
-        linkTask TaskDependencyMatchers.dependsOn("compileTestExecutableTestAnotherCpp", "compileTestExecutableTestCpp")
-    }
-
-    def "creates compile task for each library source set"() {
-        when:
-        touch("src/test/cpp/file.cpp")
-        touch("src/test/anotherCpp/file.cpp")
-        dsl {
-            apply plugin: CppNativeBinariesPlugin
-            sources {
-                test {
-                    anotherCpp(CppSourceSet) {}
-                }
-            }
-            libraries {
-                test {
-                    binaries.all {
-                        cppCompiler.define "NDEBUG"
-                        cppCompiler.define "LEVEL", "1"
-                        cppCompiler.args "ARG1", "ARG2"
-                    }
-                    binaries.withType(SharedLibraryBinary) {
-                        cppCompiler.args "SHARED1", "SHARED2"
-                    }
-                    binaries.withType(StaticLibraryBinary) {
-                        cppCompiler.args "STATIC1", "STATIC2"
-                    }
-                }
-            }
-        }
-
-        then:
-        SharedLibraryBinary sharedLib = project.binaries.testSharedLibrary
-        sharedLib.tasks.withType(CppCompile)*.name == ["compileTestSharedLibraryTestAnotherCpp", "compileTestSharedLibraryTestCpp"]
-        sharedLib.tasks.withType(CppCompile).each { compile ->
-            compile.toolChain == sharedLib.toolChain
-            compile.macros == [NDEBUG:null, LEVEL:"1"]
-            compile.compilerArgs == ["ARG1", "ARG2", "SHARED1", "SHARED2"]
-        }
-        def sharedLinkTask = sharedLib.tasks.link
-        sharedLinkTask TaskDependencyMatchers.dependsOn("compileTestSharedLibraryTestAnotherCpp", "compileTestSharedLibraryTestCpp")
-
-        and:
-        StaticLibraryBinary staticLib = project.binaries.testStaticLibrary
-        staticLib.tasks.withType(CppCompile)*.name == ["compileTestStaticLibraryTestAnotherCpp", "compileTestStaticLibraryTestCpp"]
-        staticLib.tasks.withType(CppCompile).each { compile ->
-            compile.toolChain == sharedLib.toolChain
-            compile.macros == [NDEBUG:null, LEVEL:"1"]
-            compile.compilerArgs == ["ARG1", "ARG2", "STATIC1", "STATIC2"]
-        }
-        def staticLibTask = staticLib.tasks.createStaticLib
-        staticLibTask TaskDependencyMatchers.dependsOn("compileTestStaticLibraryTestAnotherCpp", "compileTestStaticLibraryTestCpp")
-    }
-
-    def touch(String filePath) {
-        GFileUtils.touch(project.file(filePath))
-    }
-
-    def dsl(Closure closure) {
-        closure.delegate = project
-        closure()
-        project.evaluate()
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompileTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompileTest.groovy
deleted file mode 100644
index ae50f01..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/cpp/tasks/CppCompileTest.groovy
+++ /dev/null
@@ -1,67 +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.nativebinaries.language.cpp.tasks
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.api.tasks.WorkResult
-import org.gradle.nativebinaries.platform.internal.PlatformInternal
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.nativebinaries.language.cpp.internal.CppCompileSpec
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class CppCompileTest extends Specification {
-    def testDir = new TestNameTestDirectoryProvider().testDirectory
-    CppCompile cppCompile = TestUtil.createTask(CppCompile)
-    def toolChain = Mock(ToolChainInternal)
-    def platform = Mock(PlatformInternal)
-    def platformToolChain = Mock(PlatformToolChain)
-    Compiler<CppCompileSpec> cppCompiler = Mock(Compiler)
-
-    def "executes using the CppCompiler"() {
-        def sourceFile = testDir.createFile("sourceFile")
-        def result = Mock(WorkResult)
-        when:
-        cppCompile.toolChain = toolChain
-        cppCompile.targetPlatform = platform
-        cppCompile.compilerArgs = ["arg"]
-        cppCompile.macros = [def: "value"]
-        cppCompile.objectFileDir = testDir.file("outputFile")
-        cppCompile.source sourceFile
-        cppCompile.execute()
-
-        then:
-        _ * toolChain.outputType >> "cpp"
-        _ * platform.compatibilityString >> "p"
-        1 * toolChain.target(platform) >> platformToolChain
-        1 * platformToolChain.createCppCompiler() >> cppCompiler
-        1 * cppCompiler.execute({ CppCompileSpec spec ->
-            assert spec.sourceFiles*.name == ["sourceFile"]
-            assert spec.args == ['arg']
-            assert spec.allArgs == ['arg']
-            assert spec.macros == [def: 'value']
-            assert spec.objectFileDir.name == "outputFile"
-            true
-        }) >> result
-        1 * result.didWork >> true
-        0 * _._
-
-        and:
-        cppCompile.didWork
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPluginTest.groovy
deleted file mode 100644
index e3a5264..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivec/plugins/ObjectiveCNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,46 +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.nativebinaries.language.objectivec.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.objectivec.ObjectiveCSourceSet
-import org.gradle.nativebinaries.language.AbstractNativeBinariesPluginTest
-import org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile
-
-class ObjectiveCNativeBinariesPluginTest extends AbstractNativeBinariesPluginTest{
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        return ObjectiveCNativeBinariesPlugin
-    }
-
-    @Override
-    Class<? extends LanguageSourceSet> getSourceSetClass() {
-        return ObjectiveCSourceSet
-    }
-
-    @Override
-    Class<? extends Plugin> getCompileTaskClass() {
-        return ObjectiveCCompile
-    }
-
-    @Override
-    String getPluginName() {
-        return "objc"
-    }
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPluginTest.groovy
deleted file mode 100644
index 148a65e..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/language/objectivecpp/plugins/ObjectiveCppNativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,45 +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.nativebinaries.language.objectivecpp.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
-import org.gradle.nativebinaries.language.AbstractNativeBinariesPluginTest
-import org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile
-
-class ObjectiveCppNativeBinariesPluginTest extends AbstractNativeBinariesPluginTest{
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        return ObjectiveCppNativeBinariesPlugin
-    }
-
-    @Override
-    Class<? extends LanguageSourceSet> getSourceSetClass() {
-        return ObjectiveCppSourceSet
-    }
-
-    @Override
-    Class<? extends Plugin> getCompileTaskClass() {
-        return ObjectiveCppCompile
-    }
-
-    @Override
-    String getPluginName() {
-        return "objcpp"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParserTest.groovy
deleted file mode 100644
index 3206e0d..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/ArchitectureNotationParserTest.groovy
+++ /dev/null
@@ -1,115 +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.nativebinaries.platform.internal
-
-import org.gradle.api.InvalidUserDataException
-import spock.lang.Specification
-
-class ArchitectureNotationParserTest extends Specification {
-    def parser = ArchitectureNotationParser.parser()
-
-    def "fails when parsing unknown architecture"() {
-        when:
-        parseNotation("bad")
-
-        then:
-        def e = thrown(InvalidUserDataException)
-        e.message.contains("One of the following values: 'x86', 'i386', 'ia-32', 'x86_64', 'amd64', 'x64', 'x86-64',")
-    }
-
-    def "parses intel architectures"() {
-        when:
-        def arch = parseNotation(archString)
-
-        then:
-        arch.instructionSet == ArchitectureInternal.InstructionSet.X86
-        arch.registerSize == registerSize
-        arch.name == archString
-
-        where:
-        archString | registerSize
-        "x86"      | 32
-        "X86"      | 32
-        "i386"     | 32
-        "ia-32"    | 32
-        "IA-32"    | 32
-        "x86_64"   | 64
-        "x86-64"   | 64
-        "amd64"    | 64
-        "AMD64"    | 64
-        "x64"      | 64
-    }
-
-    def "parses itanium architecture"() {
-        when:
-        def arch = parseNotation("ia-64")
-        then:
-        arch.instructionSet == ArchitectureInternal.InstructionSet.ITANIUM
-        arch.registerSize == 64
-        arch.name == "ia-64"
-    }
-
-    def "parses ppc architecture"() {
-        when:
-        def arch = parseNotation(archString)
-
-        then:
-        arch.instructionSet == ArchitectureInternal.InstructionSet.PPC
-        arch.registerSize == registerSize
-        arch.name == archString
-
-        where:
-        archString | registerSize
-        "ppc"      | 32
-        "PPC"      | 32
-        "ppc64"    | 64
-        "PPC64"    | 64
-    }
-
-    def "parses sparc architectures"() {
-        when:
-        def arch = parseNotation(archString)
-
-        then:
-        arch.instructionSet == ArchitectureInternal.InstructionSet.SPARC
-        arch.registerSize == registerSize
-        arch.name == archString
-
-        where:
-        archString   | registerSize
-        "sparc"      | 32
-        "SPARC"      | 32
-        "sparc32"    | 32
-        "sparc-v7"   | 32
-        "sparc-v8"   | 32
-        "sparc64"    | 64
-        "ultrasparc" | 64
-        "sparc-v9"   | 64
-    }
-
-    def "parses arm architecture"() {
-        when:
-        def arch = parseNotation("arm")
-        then:
-        arch.instructionSet == ArchitectureInternal.InstructionSet.ARM
-        arch.registerSize == 32
-        arch.name == "arm"
-    }
-
-    private ArchitectureInternal parseNotation(String archString) {
-        parser.parseNotation(archString) as ArchitectureInternal
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitectureTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitectureTest.groovy
deleted file mode 100644
index 37d75ca..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultArchitectureTest.groovy
+++ /dev/null
@@ -1,29 +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.nativebinaries.platform.internal
-
-import spock.lang.Specification
-
-class DefaultArchitectureTest extends Specification {
-    def "has useful string representation"() {
-        def architecture = new DefaultArchitecture("arch", ArchitectureInternal.InstructionSet.ARM, 32)
-
-        expect:
-        architecture.toString() == "architecture 'arch'"
-        architecture.displayName == "architecture 'arch'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystemTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystemTest.groovy
deleted file mode 100644
index 64b0ede..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultOperatingSystemTest.groovy
+++ /dev/null
@@ -1,30 +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.nativebinaries.platform.internal
-
-import org.gradle.internal.os.OperatingSystem
-import spock.lang.Specification
-
-class DefaultOperatingSystemTest extends Specification {
-    def "has useful string representation"() {
-        def os = new DefaultOperatingSystem("windows", Stub(OperatingSystem))
-
-        expect:
-        os.toString() == "operating system 'windows'"
-        os.displayName == "operating system 'windows'"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformTest.groovy
deleted file mode 100644
index 347dd91..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/DefaultPlatformTest.groovy
+++ /dev/null
@@ -1,61 +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.nativebinaries.platform.internal
-import org.gradle.internal.typeconversion.NotationParser
-import org.gradle.nativebinaries.platform.OperatingSystem
-import spock.lang.Specification
-
-class DefaultPlatformTest extends Specification {
-    def archParser = Mock(NotationParser)
-    def osParser = Mock(NotationParser)
-    def platform = new DefaultPlatform("platform", archParser, osParser)
-
-    def "has useful string representation"() {
-        expect:
-        platform.displayName == "platform 'platform'"
-        platform.toString() == "platform 'platform'"
-    }
-
-    def "has default architecture and operating system"() {
-        expect:
-        platform.architecture == ArchitectureInternal.TOOL_CHAIN_DEFAULT
-        platform.operatingSystem == DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-    }
-
-    def "can configure architecture"() {
-        def arch = Mock(ArchitectureInternal)
-        when:
-        platform.architecture "ppc64"
-
-        then:
-        1 * archParser.parseNotation("ppc64") >> arch
-
-        and:
-        platform.architecture == arch
-    }
-
-    def "can configure operating system"() {
-        def os = Mock(OperatingSystem)
-        when:
-        platform.operatingSystem "the-os"
-
-        then:
-        1 * osParser.parseNotation("the-os") >> os
-
-        and:
-        platform.operatingSystem == os
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParserTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParserTest.groovy
deleted file mode 100644
index ab69961..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/platform/internal/OperatingSystemNotationParserTest.groovy
+++ /dev/null
@@ -1,100 +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.nativebinaries.platform.internal
-
-import org.gradle.api.InvalidUserDataException
-import spock.lang.Specification
-
-class OperatingSystemNotationParserTest extends Specification {
-    def parser = OperatingSystemNotationParser.parser()
-
-    def "fails when parsing unknown operating system"() {
-        when:
-        parser.parseNotation("bad")
-
-        then:
-        def e = thrown(InvalidUserDataException)
-        e.message.contains("One of the following values: 'windows', 'osx', 'mac os x', 'linux', 'solaris', 'sunos'")
-    }
-
-    def "parses windows"() {
-        when:
-        def os = parser.parseNotation("windows")
-        then:
-        os.windows
-        !os.macOsX
-        !os.linux
-        !os.solaris
-        !os.freeBSD
-    }
-
-    def "parses osx"() {
-        when:
-        def os = parser.parseNotation(osString)
-
-        then:
-        !os.windows
-        os.macOsX
-        !os.linux
-        !os.solaris
-        !os.freeBSD
-
-        where:
-        osString << ["osx", "mac os x"]
-    }
-
-    def "parses linux"() {
-        when:
-        def os = parser.parseNotation("linux")
-
-        then:
-        !os.windows
-        !os.macOsX
-        os.linux
-        !os.solaris
-        !os.freeBSD
-    }
-
-    def "parses solaris"() {
-        when:
-        def os = parser.parseNotation(osString)
-
-        then:
-        !os.windows
-        !os.macOsX
-        !os.linux
-        os.solaris
-        !os.freeBSD
-
-        where:
-        osString << ["solaris", "sunos"]
-    }
-
-    def "parses FreeBSD"() {
-        when:
-        def os = parser.parseNotation(osString)
-
-        then:
-        !os.windows
-        !os.macOsX
-        !os.linux
-        !os.solaris
-        os.freeBSD
-
-        where:
-        osString << ["FreeBSD", "freebsd"]
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPluginTest.groovy
deleted file mode 100644
index ca33573..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesModelPluginTest.groovy
+++ /dev/null
@@ -1,238 +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.nativebinaries.plugins
-
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Task
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.api.tasks.TaskDependency
-import org.gradle.nativebinaries.*
-import org.gradle.nativebinaries.internal.DefaultFlavor
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.platform.PlatformContainer
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.toolchain.ToolChainRegistry
-import org.gradle.nativebinaries.toolchain.internal.PlatformToolChain
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class NativeBinariesModelPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def setup() {
-        project.plugins.apply(NativeBinariesModelPlugin)
-    }
-
-    def "adds model extensions"() {
-        expect:
-        project.executables instanceof NamedDomainObjectContainer
-        project.libraries instanceof NamedDomainObjectContainer
-        project.modelRegistry.get("toolChains", ToolChainRegistry) != null
-        project.modelRegistry.get("platforms", PlatformContainer) != null
-        project.modelRegistry.get("buildTypes", BuildTypeContainer) != null
-        project.modelRegistry.get("flavors", FlavorContainer) != null
-    }
-
-    def "adds default target platform, build type and flavor"() {
-        expect:
-        with (one(project.modelRegistry.get("platforms", PlatformContainer))) {
-            name == 'current'
-            architecture == ArchitectureInternal.TOOL_CHAIN_DEFAULT
-        }
-        one(project.modelRegistry.get("buildTypes", BuildTypeContainer)).name == 'debug'
-        one(project.modelRegistry.get("flavors", FlavorContainer)).name == 'default'
-    }
-
-    def "does not provide a default tool chain"() {
-        expect:
-        project.modelRegistry.get("toolChains", ToolChainRegistry).isEmpty()
-    }
-
-    def "adds default flavor to every binary"() {
-        when:
-        project.executables.create "exe"
-        project.libraries.create "lib"
-        project.evaluate()
-
-        then:
-        one(project.binaries.withType(ExecutableBinary)).flavor.name == DefaultFlavor.DEFAULT
-        one(project.binaries.withType(SharedLibraryBinary)).flavor.name == DefaultFlavor.DEFAULT
-    }
-
-    def "does not add defaults when domain is explicitly configured"() {
-        when:
-        project.model {
-            toolChains {
-                add named(ToolChainInternal, "tc")
-            }
-            platforms {
-                add named(Platform, "platform")
-            }
-            buildTypes {
-                add named(BuildType, "bt")
-            }
-            flavors {
-                add named(Flavor, "flavor1")
-            }
-        }
-
-        and:
-        project.evaluate()
-
-        then:
-        one(project.modelRegistry.get("toolChains", ToolChainRegistry)).name == 'tc'
-        one(project.modelRegistry.get("platforms", PlatformContainer)).name == 'platform'
-        one(project.modelRegistry.get("buildTypes", BuildTypeContainer)).name == 'bt'
-        one(project.modelRegistry.get("flavors", FlavorContainer)).name == 'flavor1'
-    }
-
-    def "creates binaries for executable"() {
-        when:
-        project.plugins.apply(NativeBinariesModelPlugin)
-        project.model {
-            toolChains {
-                add toolChain("tc")
-            }
-            platforms {
-                add named(Platform, "platform")
-            }
-            buildTypes {
-                add named(BuildType, "bt")
-            }
-            flavors {
-                add named(Flavor, "flavor1")
-            }
-        }
-        def executable = project.executables.create "test"
-        project.evaluate()
-
-        then:
-        ExecutableBinary executableBinary = one(project.binaries) as ExecutableBinary
-        with (executableBinary) {
-            name == 'testExecutable'
-            component == executable
-            toolChain.name == "tc"
-            targetPlatform.name == "platform"
-            buildType.name == "bt"
-            flavor.name == "flavor1"
-        }
-
-        and:
-        executable.binaries == [executableBinary] as Set
-    }
-
-    def "creates binaries for library"() {
-        when:
-        project.plugins.apply(NativeBinariesModelPlugin)
-        project.model {
-            toolChains {
-                add toolChain("tc")
-            }
-            platforms {
-                add named(Platform, "platform")
-            }
-            buildTypes {
-                add named(BuildType, "bt")
-            }
-            flavors {
-                add named(Flavor, "flavor1")
-            }
-        }
-        def library = project.libraries.create "test"
-        project.evaluate()
-
-        then:
-        SharedLibraryBinary sharedLibraryBinary = project.binaries.testSharedLibrary as SharedLibraryBinary
-        with (sharedLibraryBinary) {
-            name == 'testSharedLibrary'
-            component == library
-
-            toolChain.name == "tc"
-            targetPlatform.name == "platform"
-            buildType.name == "bt"
-            flavor.name == "flavor1"
-        }
-
-        and:
-        StaticLibraryBinary staticLibraryBinary = project.binaries.testStaticLibrary as StaticLibraryBinary
-        with (staticLibraryBinary) {
-            name == 'testStaticLibrary'
-            component == library
-
-            toolChain.name == "tc"
-            targetPlatform.name == "platform"
-            buildType.name == "bt"
-            flavor.name == "flavor1"
-        }
-
-        and:
-        library.binaries.contains(sharedLibraryBinary)
-        library.binaries.contains(staticLibraryBinary)
-    }
-
-    def "creates lifecycle task for each binary"() {
-        when:
-        project.plugins.apply(NativeBinariesModelPlugin)
-        def executable = project.executables.create "exe"
-        def library = project.libraries.create "lib"
-        project.evaluate()
-
-        then:
-        ExecutableBinary executableBinary = project.binaries.exeExecutable as ExecutableBinary
-        with (oneTask(executableBinary.buildDependencies)) {
-            name == executableBinary.name
-            group == BasePlugin.BUILD_GROUP
-        }
-        SharedLibraryBinary sharedLibraryBinary = project.binaries.libSharedLibrary as SharedLibraryBinary
-        with (oneTask(sharedLibraryBinary.buildDependencies)) {
-            name == sharedLibraryBinary.name
-            group == BasePlugin.BUILD_GROUP
-        }
-        StaticLibraryBinary staticLibraryBinary = project.binaries.libStaticLibrary as StaticLibraryBinary
-        with (oneTask(staticLibraryBinary.buildDependencies)) {
-            name == staticLibraryBinary.name
-            group == BasePlugin.BUILD_GROUP
-        }
-    }
-
-    static <T> T one(Collection<T> collection) {
-        assert collection.size() == 1
-        return collection.iterator().next()
-    }
-
-    def named(Class type, def name) {
-        Stub(type) {
-            getName() >> name
-        }
-    }
-
-    def toolChain(def name) {
-        Stub(ToolChainInternal) {
-            getName() >> name
-            target(_) >> Stub(PlatformToolChain) {
-                isAvailable() >> true
-            }
-        }
-    }
-
-    Task oneTask(TaskDependency dependencies) {
-        def tasks = dependencies.getDependencies(Stub(Task))
-        assert tasks.size() == 1
-        return tasks.asList()[0]
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginTest.groovy
deleted file mode 100644
index 5a509b0..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/plugins/NativeBinariesPluginTest.groovy
+++ /dev/null
@@ -1,126 +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.nativebinaries.plugins
-
-import org.gradle.api.tasks.TaskDependencyMatchers
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.nativebinaries.tasks.CreateStaticLibrary
-import org.gradle.nativebinaries.tasks.InstallExecutable
-import org.gradle.nativebinaries.tasks.LinkExecutable
-import org.gradle.nativebinaries.tasks.LinkSharedLibrary
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class NativeBinariesPluginTest extends Specification {
-    final def project = TestUtil.createRootProject()
-
-    def setup() {
-        project.plugins.apply(NativeBinariesPlugin)
-    }
-
-    def "creates link and install task for executable"() {
-        when:
-        project.executables.create "test"
-        project.evaluate()
-
-        then:
-        def testExecutable = project.binaries.testExecutable
-        with (project.tasks.linkTestExecutable) {
-            it instanceof LinkExecutable
-            it == testExecutable.tasks.link
-            it.toolChain == testExecutable.toolChain
-            it.targetPlatform == testExecutable.targetPlatform
-            it.linkerArgs == testExecutable.linker.args
-        }
-        testExecutable.tasks.createStaticLib == null
-
-        and:
-        def lifecycleTask = project.tasks.testExecutable
-        lifecycleTask TaskDependencyMatchers.dependsOn("linkTestExecutable")
-
-        and:
-        project.tasks.installTestExecutable instanceof InstallExecutable
-    }
-
-    def "creates link task and static archive task for library"() {
-        when:
-        project.libraries.create "test"
-        project.evaluate()
-
-        then:
-        def sharedLibraryBinary = project.binaries.testSharedLibrary
-        with (project.tasks.linkTestSharedLibrary) {
-            it instanceof LinkSharedLibrary
-            it == sharedLibraryBinary.tasks.link
-            it.toolChain == sharedLibraryBinary.toolChain
-            it.targetPlatform == sharedLibraryBinary.targetPlatform
-            it.linkerArgs == sharedLibraryBinary.linker.args
-        }
-        sharedLibraryBinary.tasks.createStaticLib == null
-
-        and:
-        def sharedLibTask = project.tasks.testSharedLibrary
-        sharedLibTask TaskDependencyMatchers.dependsOn("linkTestSharedLibrary")
-
-        and:
-        def staticLibraryBinary = project.binaries.testStaticLibrary
-        with (project.tasks.createTestStaticLibrary) {
-            it instanceof  CreateStaticLibrary
-            it == staticLibraryBinary.tasks.createStaticLib
-            it.toolChain == staticLibraryBinary.toolChain
-            it.targetPlatform == staticLibraryBinary.targetPlatform
-            it.staticLibArgs == staticLibraryBinary.staticLibArchiver.args
-        }
-        staticLibraryBinary.tasks.link == null
-
-        and:
-        def staticLibTask = project.tasks.testStaticLibrary
-        staticLibTask TaskDependencyMatchers.dependsOn("createTestStaticLibrary")
-    }
-
-    def "attaches existing functional source set with same name to component"() {
-        def languageSourceSet = Stub(LanguageSourceSet) {
-            getName() >> "languageSourceSet"
-        }
-
-        when:
-        project.sources.create "testExe"
-        project.sources.testExe.add languageSourceSet
-
-        project.executables.create "testExe"
-
-        then:
-        project.executables.testExe.source == [languageSourceSet] as Set
-    }
-
-    def "creates and attaches functional source set with same name to component"() {
-        def languageSourceSet = Stub(LanguageSourceSet) {
-            getName() >> "languageSourceSet"
-        }
-
-        when:
-        // Ensure that any created functional source set has a language source set
-        project.sources.all { functionalSourceSet ->
-            functionalSourceSet.add languageSourceSet
-        }
-
-        project.executables.create "testExe"
-
-        then:
-        project.executables.testExe.source == [languageSourceSet] as Set
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistryTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistryTest.groovy
deleted file mode 100644
index 9ffa628..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/DefaultToolChainRegistryTest.groovy
+++ /dev/null
@@ -1,182 +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.nativebinaries.toolchain.internal
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectFactory
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.nativebinaries.platform.internal.DefaultPlatform
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-
-class DefaultToolChainRegistryTest extends Specification {
-    def project = TestUtil.createRootProject()
-    def instantiator = project.services.get(Instantiator)
-    def registry = instantiator.newInstance(DefaultToolChainRegistry, instantiator)
-    def NamedDomainObjectFactory<TestToolChain> factory = Mock(NamedDomainObjectFactory)
-    def platform = new DefaultPlatform("platform")
-
-    def "setup"() {
-        project.extensions.add("toolChains", registry)
-        registry.registerFactory(TestToolChain, factory)
-    }
-
-    def "adds default tool chains"() {
-        def defaultToolChain1 = unavailableToolChain("test1")
-        def defaultToolChain2 = availableToolChain("test2")
-        def defaultToolChain3 = availableToolChain("test3")
-
-        when:
-        registry.registerDefaultToolChain("test1", TestToolChain)
-        registry.registerDefaultToolChain("test2", TestToolChain)
-        registry.registerDefaultToolChain("test3", TestToolChain)
-        registry.addDefaultToolChains()
-
-        then:
-        registry.asList() == [defaultToolChain1, defaultToolChain2, defaultToolChain3]
-    }
-
-    def "can add default tool chain when some configured"() {
-        def defaultToolChain = availableToolChain("default")
-        def configuredToolChain = unavailableToolChain("configured")
-
-        when:
-        registry.registerDefaultToolChain("default", TestToolChain)
-
-        and:
-        registry.create("configured", TestToolChain)
-
-        and:
-        registry.addDefaultToolChains()
-
-        then:
-        registry.asList() == [configuredToolChain, defaultToolChain]
-    }
-
-    def "provides unavailable tool chain when no tool chain available for requested  platform"() {
-        unavailableToolChain("test", "nope")
-        unavailableToolChain("test2", "not me")
-        unavailableToolChain("test3", "not me either")
-
-        given:
-        registry.registerDefaultToolChain("test", TestToolChain)
-        registry.registerDefaultToolChain("test2", TestToolChain)
-        registry.registerDefaultToolChain("test3", TestToolChain)
-        registry.addDefaultToolChains()
-
-        and:
-        def tc = registry.getForPlatform(platform)
-        def result = tc.target(platform)
-
-        when:
-        result.createCCompiler()
-
-        then:
-        GradleException e = thrown()
-        e.message == toPlatformLineSeparators("""No tool chain is available to build for platform 'platform':
-  - Tool chain 'test': nope
-  - Tool chain 'test2': not me
-  - Tool chain 'test3': not me either""")
-    }
-
-    def "can use DSL to configure toolchains"() {
-        def defaultToolChain = availableToolChain("test")
-        def anotherToolChain = unavailableToolChain("another")
-
-        when:
-        registry.registerDefaultToolChain("test", TestToolChain)
-        registry.addDefaultToolChains()
-
-        and:
-        project.toolChains {
-            test {
-                baseDir = "foo"
-            }
-            another(TestToolChain) {
-                baseDir = "bar"
-            }
-        }
-
-        then:
-        1 * defaultToolChain.setBaseDir("foo")
-        1 * anotherToolChain.setBaseDir("bar")
-
-        and:
-        registry.asList() == [anotherToolChain, defaultToolChain]
-    }
-
-    def "tool chains are returned in name order"() {
-        def test = unavailableToolChain("test")
-        def tc2 = availableToolChain("test2")
-        def tcFirst = availableToolChain("first")
-
-        when:
-        registry.create("test", TestToolChain)
-        registry.create("test2", TestToolChain)
-        registry.create("first", TestToolChain)
-
-        then:
-        registry.toList() == [tcFirst, test, tc2]
-    }
-
-    def "uses first available tool chain that can target platform"() {
-        def defaultToolChain1 = unavailableToolChain("test1")
-        def defaultToolChain2 = availableToolChain("test2")
-        def defaultToolChain3 = availableToolChain("test3")
-
-        given:
-        registry.add(defaultToolChain1)
-        registry.add(defaultToolChain2)
-        registry.add(defaultToolChain3)
-
-        expect:
-        registry.getForPlatform(platform) == defaultToolChain2
-    }
-
-    def availableToolChain(String name) {
-        PlatformToolChain platformToolChain = Stub(PlatformToolChain) {
-            _ * isAvailable() >> true
-        }
-        TestToolChain testToolChain = Mock(TestToolChain) {
-            _ * getName() >> name
-            _ * target(platform) >> platformToolChain
-        }
-        factory.create(name) >> testToolChain
-        return testToolChain
-    }
-
-    def unavailableToolChain(String name, String message = "Not available") {
-        PlatformToolChain platformToolChain = Stub(PlatformToolChain) {
-            _ * isAvailable() >> false
-            _ * explain(_) >> { it[0].node(message) }
-        }
-        TestToolChain testToolChain = Mock(TestToolChain) {
-            _ * getName() >> name
-            _ * getDisplayName() >> "Tool chain '$name'"
-            _ * target(platform) >> platformToolChain
-        }
-        factory.create(name) >> testToolChain
-        return testToolChain
-    }
-
-    interface TestToolChain extends ToolChainInternal
-    {
-        void setBaseDir(String value);
-    }
-
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompilerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompilerTest.groovy
deleted file mode 100644
index c695676..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/OutputCleaningCompilerTest.groovy
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal
-
-import org.gradle.api.tasks.WorkResult
-import org.gradle.internal.hash.HashUtil
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.compile.Compiler;
-
-class OutputCleaningCompilerTest extends Specification {
-
-    @Rule
-    final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-
-
-    NativeCompileSpec spec = Mock(NativeCompileSpec);
-    Compiler delegateCompiler = Mock(Compiler)
-    OutputCleaningCompiler cleanCompiler = new OutputCleaningCompiler<NativeCompileSpec>(delegateCompiler, ".o");
-
-    TestFile outputDir = tmpDirProvider.createDir("objectFiles")
-
-    WorkResult workResult = Mock(WorkResult)
-    List<TestFile> sourceFiles
-
-    def setup() {
-        _ * spec.objectFileDir >> outputDir
-        _ * delegateCompiler.execute(_) >> { NativeCompileSpec spec ->
-            List<File> sourceFiles = spec.getSourceFiles()
-            sourceFiles.each{ inputFile ->
-                createObjDummy(inputFile)
-            }
-            _ * workResult.getDidWork() >> !sourceFiles.isEmpty();
-            return workResult
-        }
-    }
-
-    def "deletes output files and according hash directory"() {
-        setup:
-        sourceFiles = Arrays.asList(tmpDirProvider.file("src/main/c/main.c"), tmpDirProvider.file("src/main/c/foo/main2.c"))
-        when:
-        compile(sourceFiles[0], sourceFiles[1])
-        then:
-        outputDir.listFiles().size() == 2
-
-        when:
-        compile(sourceFiles[0])
-        then:
-        objectFile(sourceFiles[0])
-        !objectFile(sourceFiles[1])
-
-
-        when:
-        compile(sourceFiles[1])
-        then:
-        !objectFile(sourceFiles[0])
-        objectFile(sourceFiles[1])
-    }
-
-    def "removes stale output when source file is moved"() {
-        setup:
-        def orgFile = tmpDirProvider.file("src/main/c/org/main.c")
-        def movedFile = tmpDirProvider.file("src/main/c/moved/main.c")
-        sourceFiles = [orgFile, movedFile]
-
-        when:
-        compile(orgFile)
-
-        then:
-        objectFile(orgFile)
-        !objectFile(movedFile)
-
-        when:
-        compile(movedFile)
-        then:
-        !objectFile(orgFile)
-        objectFile(movedFile)
-    }
-
-    def objectFile(TestFile testFile) {
-        assert outputDir.listFiles().size() == 1
-        assert outputDir.listFiles()[0].listFiles().size() == 1
-        outputDir.listFiles()[0].listFiles()[0].text == testFile.absolutePath
-    }
-
-    def compile(TestFile... sourceToCompile) {
-        List<TestFile> toCompile = Arrays.asList(sourceToCompile)
-        List<TestFile> toRemove = sourceFiles - toCompile
-        2 * spec.getSourceFiles() >> toCompile
-        1 * spec.getRemovedSourceFiles() >> toRemove
-        cleanCompiler.execute(spec)
-    }
-
-    def createObjDummy(File sourceFile) {
-        TestFile objectFile = outputDir.file("${HashUtil.createCompactMD5(sourceFile.absolutePath)}/${sourceFile.name - ".c" + ".o" }")
-        objectFile.touch()
-        objectFile.text = sourceFile.absolutePath
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailabilityTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailabilityTest.groovy
deleted file mode 100644
index 41e9306..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/ToolChainAvailabilityTest.groovy
+++ /dev/null
@@ -1,67 +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.nativebinaries.toolchain.internal
-
-import org.gradle.util.TreeVisitor
-import spock.lang.Specification
-
-class ToolChainAvailabilityTest extends Specification {
-    def "visits message"() {
-        def visitor = Mock(TreeVisitor)
-
-        given:
-        def availability = new ToolChainAvailability()
-        availability.unavailable("some reason")
-
-        when:
-        availability.explain(visitor)
-
-        then:
-        visitor.node("some reason")
-    }
-
-    def "ignores available tool"() {
-        def searchResult = Mock(ToolSearchResult)
-
-        given:
-        searchResult.available >> true
-
-        when:
-        def availability = new ToolChainAvailability()
-        availability.mustBeAvailable(searchResult)
-
-        then:
-        availability.available
-    }
-
-    def "visits missing tool"() {
-        def visitor = Mock(TreeVisitor)
-        def searchResult = Mock(ToolSearchResult)
-
-        given:
-        searchResult.available >> false
-
-        and:
-        def availability = new ToolChainAvailability()
-        availability.mustBeAvailable(searchResult)
-
-        when:
-        availability.explain(visitor)
-
-        then:
-        1 * searchResult.explain(visitor)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/UnavailablePlatformToolChainTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/UnavailablePlatformToolChainTest.groovy
deleted file mode 100644
index f1da702..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/UnavailablePlatformToolChainTest.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal
-
-import org.gradle.api.GradleException
-import org.gradle.util.TreeVisitor
-import spock.lang.Specification
-
-class UnavailablePlatformToolChainTest extends Specification {
-    def reason = new ToolChainAvailability().unavailable("broken")
-    def toolChain = new UnavailablePlatformToolChain(reason)
-
-    def "is not available"() {
-        expect:
-        !toolChain.available
-
-        def visitor = Mock(TreeVisitor)
-
-        when:
-        toolChain.explain(visitor)
-
-        then:
-        1 * visitor.node("broken")
-    }
-
-    def "throws failure when attempting to create C compiler"() {
-        when:
-        toolChain.createCCompiler()
-
-        then:
-        GradleException e = thrown()
-        e.message == "broken"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
deleted file mode 100644
index 2191f03..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
+++ /dev/null
@@ -1,237 +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.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.internal.text.TreeFormatter
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.platform.internal.DefaultArchitecture
-import org.gradle.nativebinaries.platform.internal.DefaultOperatingSystem
-import org.gradle.nativebinaries.toolchain.TargetPlatformConfiguration
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult
-import org.gradle.nativebinaries.toolchain.internal.ToolType
-import org.gradle.nativebinaries.toolchain.internal.tools.ToolSearchPath
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.gradle.util.TreeVisitor
-import spock.lang.Specification
-
-import static ArchitectureInternal.InstructionSet.X86
-
-class AbstractGccCompatibleToolChainTest extends Specification {
-    def fileResolver = Mock(FileResolver)
-    def execActionFactory = Mock(ExecActionFactory)
-    def toolSearchPath = Stub(ToolSearchPath)
-    def tool = Stub(CommandLineToolSearchResult) {
-        isAvailable() >> true
-    }
-    def toolChain = new TestToolChain("test", fileResolver, execActionFactory, toolSearchPath)
-    def platform = Stub(Platform)
-
-    def "is unavailable when platform is not known and is not the default platform"() {
-        given:
-        platform.name >> 'unknown'
-
-        expect:
-        def platformToolChain = toolChain.target(platform)
-        !platformToolChain.available
-        getMessage(platformToolChain) == "Don't know how to build for platform 'unknown'."
-    }
-
-    def "is unavailable when no language tools can be found and building for default platform"() {
-        def missing = Stub(CommandLineToolSearchResult) {
-            isAvailable() >> false
-            explain(_) >> { TreeVisitor<String> visitor -> visitor.node("c compiler not found") }
-        }
-
-        given:
-        platform.operatingSystem >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.architecture >> ArchitectureInternal.TOOL_CHAIN_DEFAULT
-
-        and:
-        toolSearchPath.locate(ToolType.C_COMPILER, "gcc") >> missing
-        toolSearchPath.locate(ToolType.CPP_COMPILER, "g++") >> missing
-        toolSearchPath.locate(ToolType.OBJECTIVEC_COMPILER, "gcc") >> missing
-        toolSearchPath.locate(ToolType.OBJECTIVECPP_COMPILER, "g++") >> missing
-
-        expect:
-        def platformToolChain = toolChain.target(platform)
-        !platformToolChain.available
-        getMessage(platformToolChain) == "c compiler not found"
-    }
-
-    def "is available when any language tool can be found and building for default platform"() {
-        def missing = Stub(CommandLineToolSearchResult) {
-            isAvailable() >> false
-        }
-
-        given:
-        platform.operatingSystem >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.architecture >> ArchitectureInternal.TOOL_CHAIN_DEFAULT
-
-        and:
-        toolSearchPath.locate(ToolType.CPP_COMPILER, "g++") >> missing
-        toolSearchPath.locate(_, _) >> tool
-
-        expect:
-        toolChain.target(platform).available
-    }
-
-    def "is available when any language tool can be found and platform configuration registered for platform"() {
-        def platformConfig = Mock(TargetPlatformConfiguration)
-
-        given:
-        toolSearchPath.locate(_, _) >> tool
-        platformConfig.supportsPlatform(platform) >> true
-
-        and:
-        toolChain.addPlatformConfiguration(platformConfig)
-
-        expect:
-        toolChain.target(platform).available
-    }
-
-    def "supplies no additional arguments to target native binary for tool chain default"() {
-        when:
-        toolSearchPath.locate(_, _) >> tool
-        platform.getOperatingSystem() >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.getArchitecture() >> ArchitectureInternal.TOOL_CHAIN_DEFAULT
-
-        then:
-        with(toolChain.getPlatformConfiguration(platform)) {
-            linkerArgs == []
-            cppCompilerArgs == []
-            CCompilerArgs == []
-            assemblerArgs == []
-            staticLibraryArchiverArgs == []
-        }
-    }
-
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "supplies args for supported architecture"() {
-        when:
-        toolSearchPath.locate(_, _) >> tool
-        platform.operatingSystem >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.architecture >> new DefaultArchitecture(arch, instructionSet, registerSize)
-
-        then:
-        toolChain.target(platform).available
-
-        with(toolChain.getPlatformConfiguration(platform)) {
-            linkerArgs == [linkerArg]
-            cppCompilerArgs == [compilerArg]
-            CCompilerArgs == [compilerArg]
-            if (OperatingSystem.current().isMacOsX()) {
-                assemblerArgs == osxAssemblerArgs
-            } else {
-                assemblerArgs == [assemblerArg]
-            }
-            staticLibraryArchiverArgs == []
-        }
-
-        where:
-        arch     | instructionSet | registerSize | linkerArg | compilerArg | assemblerArg | osxAssemblerArgs
-        "i386"   | X86            | 32           | "-m32"    | "-m32"      | "--32"       | ["-arch", "i386"]
-        "x86_64" | X86            | 64           | "-m64"    | "-m64"      | "--64"       | ["-arch", "x86_64"]
-    }
-
-    @Requires(TestPrecondition.WINDOWS)
-    def "supplies args for supported architecture for i386 architecture on windows"() {
-        when:
-        toolSearchPath.locate(_, _) >> tool
-        platform.operatingSystem >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.architecture >> new DefaultArchitecture("i386", X86, 32)
-
-        then:
-        toolChain.target(platform).available
-
-        with(toolChain.getPlatformConfiguration(platform)) {
-            linkerArgs == ["-m32"]
-            cppCompilerArgs == ["-m32"]
-            CCompilerArgs == ["-m32"]
-            assemblerArgs == ["--32"]
-            staticLibraryArchiverArgs == []
-        }
-    }
-
-    @Requires(TestPrecondition.WINDOWS)
-    def "cannot target x86_64 architecture on windows"() {
-        given:
-        toolSearchPath.locate(_, _) >> tool
-
-        and:
-        platform.getName() >> "x64"
-        platform.operatingSystem >> DefaultOperatingSystem.TOOL_CHAIN_DEFAULT
-        platform.architecture >> new DefaultArchitecture("x64", X86, 64)
-
-        when:
-        def platformToolChain = toolChain.target(platform)
-
-        then:
-        !platformToolChain.available
-        getMessage(platformToolChain) == "Don't know how to build for platform 'x64'."
-    }
-
-    def "uses supplied platform configurations in order to target binary"() {
-        def platformConfig1 = Mock(TargetPlatformConfiguration)
-        def platformConfig2 = Mock(TargetPlatformConfiguration)
-        when:
-        toolSearchPath.locate(_, _) >> tool
-        platform.getOperatingSystem() >> new DefaultOperatingSystem("other", OperatingSystem.SOLARIS)
-
-        and:
-        toolChain.addPlatformConfiguration(platformConfig1)
-        toolChain.addPlatformConfiguration(platformConfig2)
-
-        and:
-        platformConfig1.supportsPlatform(platform) >> false
-        platformConfig2.supportsPlatform(platform) >> true
-
-        then:
-        toolChain.target(platform).available
-
-        and:
-        toolChain.getPlatformConfiguration(platform) == platformConfig2
-    }
-
-    def getMessage(ToolSearchResult result) {
-        def formatter = new TreeFormatter()
-        result.explain(formatter)
-        return formatter.toString()
-    }
-
-    static class TestToolChain extends AbstractGccCompatibleToolChain {
-        TestToolChain(String name, FileResolver fileResolver, ExecActionFactory execActionFactory, ToolSearchPath tools) {
-            super(name, OperatingSystem.current(), fileResolver, execActionFactory, tools)
-
-            registerTool(ToolType.CPP_COMPILER, "g++");
-            registerTool(ToolType.C_COMPILER, "gcc");
-            registerTool(ToolType.OBJECTIVECPP_COMPILER, "g++");
-            registerTool(ToolType.OBJECTIVEC_COMPILER, "gcc");
-            registerTool(ToolType.ASSEMBLER, "as");
-            registerTool(ToolType.LINKER, "ld");
-            registerTool(ToolType.STATIC_LIB_ARCHIVER, "ar");
-        }
-
-        @Override
-        protected String getTypeName() {
-            return "Test"
-        }
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AssemblerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AssemblerTest.groovy
deleted file mode 100644
index 41292f5..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/AssemblerTest.groovy
+++ /dev/null
@@ -1,83 +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.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.Action
-import org.gradle.internal.hash.HashUtil
-import org.gradle.nativebinaries.language.assembler.internal.AssembleSpec
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool
-import org.gradle.process.internal.ExecAction
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class AssemblerTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-
-    def executable = new File("executable")
-    def execActionFactory = Mock(ExecActionFactory)
-    Action<List<String>> argAction = Mock(Action)
-    CommandLineTool<AssembleSpec> commandLineTool = new CommandLineTool<AssembleSpec>("assembler", executable, execActionFactory)
-    Assembler assembler = new Assembler(commandLineTool, argAction, ".o");
-
-    def "assembles each source file independently"() {
-        given:
-        def testDir = tmpDirProvider.testDirectory
-        def objectFileDir = testDir.file("output/objects")
-
-        def execAction1 = Mock(ExecAction)
-        def execAction2 = Mock(ExecAction)
-
-        def sourceOne = testDir.file("one.s")
-        def sourceTwo = testDir.file("two.s")
-
-        when:
-        AssembleSpec assembleSpec = Mock(AssembleSpec)
-        assembleSpec.getObjectFileDir() >> objectFileDir
-        assembleSpec.getAllArgs() >> ["-firstArg", "-secondArg"]
-        assembleSpec.getSourceFiles() >> [sourceOne, sourceTwo]
-
-        and:
-        assembler.execute(assembleSpec)
-
-        then:
-        1 * argAction.execute(["-firstArg", "-secondArg", "-o", outputFilePathFor(objectFileDir, sourceOne), sourceOne.absolutePath])
-        1 * execActionFactory.newExecAction() >> execAction1
-        1 * execAction1.executable(executable)
-        1 * execAction1.workingDir(objectFileDir)
-        1 * execAction1.args(["-firstArg", "-secondArg", "-o", outputFilePathFor(objectFileDir, sourceOne), sourceOne.absolutePath])
-        1 * execAction1.environment([:])
-        1 * execAction1.execute()
-        0 * execAction1._
-
-        1 * argAction.execute(["-firstArg", "-secondArg", "-o", outputFilePathFor(objectFileDir, sourceTwo), sourceTwo.absolutePath])
-        1 * execActionFactory.newExecAction() >> execAction2
-        1 * execAction2.executable(executable)
-        1 * execAction2.workingDir(objectFileDir)
-        1 * execAction2.args(["-firstArg", "-secondArg", "-o", outputFilePathFor(objectFileDir, sourceTwo), sourceTwo.absolutePath])
-        1 * execAction2.environment([:])
-        1 * execAction2.execute()
-        0 * execAction2._
-    }
-
-    String outputFilePathFor(File objectFileRoot, File sourceFile) {
-        String relativeObjectFilePath = "${HashUtil.createCompactMD5(sourceFile.absolutePath)}/${sourceFile.name - ".s"}.o"
-        String outputFilePath = new File(objectFileRoot, relativeObjectFilePath).absolutePath;
-        outputFilePath
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompilerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompilerTest.groovy
deleted file mode 100644
index 7b21b75..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/CCompilerTest.groovy
+++ /dev/null
@@ -1,98 +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.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.Action
-import org.gradle.internal.hash.HashUtil
-import org.gradle.nativebinaries.language.c.internal.CCompileSpec
-import org.gradle.nativebinaries.language.c.internal.DefaultCCompileSpec
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool
-import org.gradle.process.internal.ExecAction
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-import org.gradle.internal.os.OperatingSystem
-
-class CCompilerTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-
-    def executable = new File("executable")
-    def execActionFactory = Mock(ExecActionFactory)
-    Action<List<String>> argAction = Mock(Action)
-    CommandLineTool<CCompileSpec> commandLineTool = new CommandLineTool<CCompileSpec>("cCompiler", executable, execActionFactory)
-    CCompiler compiler = new CCompiler(commandLineTool, argAction, false);
-    String objectFileExtension = OperatingSystem.current().isWindows() ? ".obj" : ".o";
-
-    def "compiles all source files in separate executions"() {
-        given:
-        def testDir = tmpDirProvider.testDirectory
-        def objectFileDir = testDir.file("output/objects")
-
-        def execAction = Mock(ExecAction)
-
-        when:
-        CCompileSpec compileSpec = Spy(DefaultCCompileSpec)
-        compileSpec.getMacros() >> [foo: "bar", empty: null]
-        compileSpec.getObjectFileDir() >> objectFileDir
-        compileSpec.getAllArgs() >> ["-firstArg", "-secondArg"]
-        compileSpec.getIncludeRoots() >> [testDir.file("include.h")]
-        compileSpec.setSourceFiles([testDir.file("one.c"), testDir.file("two.c")])
-
-        and:
-        compiler.execute(compileSpec)
-
-        then:
-        2 * argAction.execute([
-                "-x", "c",
-                "-Dfoo=bar", "-Dempty",
-                "-firstArg", "-secondArg",
-                "-c",
-                "-I", testDir.file("include.h").absolutePath])
-
-        ["one.c", "two.c"].each{ sourceFileName ->
-
-            TestFile sourceFile = testDir.file(sourceFileName)
-            String objectOutputPath = outputFilePathFor(objectFileDir, sourceFile)
-
-            1 * execAction.args([
-                    "-x", "c",
-                    "-Dfoo=bar", "-Dempty",
-                    "-firstArg", "-secondArg",
-                    "-c",
-                    "-I", testDir.file("include.h").absolutePath,
-                    testDir.file(sourceFileName).absolutePath,
-                    "-o", objectOutputPath])
-
-        }
-        2 * execActionFactory.newExecAction() >> execAction
-        2 * execAction.executable(executable)
-        2 * execAction.workingDir(_)
-        2 * execAction.environment([:])
-        2 * execAction.execute()
-        0 * execAction._
-        0 * argAction._
-    }
-
-    String outputFilePathFor(File objectFileRoot, TestFile testFile) {
-        String relativeObjectFilePath = "${HashUtil.createCompactMD5(testFile.absolutePath)}/${testFile.name - ".c"}$objectFileExtension"
-        String outputFilePath = new File(objectFileRoot, relativeObjectFilePath).absolutePath;
-        outputFilePath
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ClangToolChainTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ClangToolChainTest.groovy
deleted file mode 100644
index b2ef9a0..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ClangToolChainTest.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.toolchain.internal.clang.ClangToolChain
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ClangToolChainTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-    final FileResolver fileResolver = Mock(FileResolver)
-    final toolChain = new ClangToolChain("clang", OperatingSystem.current(), fileResolver, Stub(ExecActionFactory))
-
-    def "has default tool names"() {
-        expect:
-        toolChain.cppCompiler.executable == "clang++"
-        toolChain.cCompiler.executable == "clang"
-        toolChain.assembler.executable == "as"
-        toolChain.linker.executable == "clang++"
-        toolChain.staticLibArchiver.executable == "ar"
-    }
-
-    def "can update tool names"() {
-        when:
-        toolChain.assembler.executable = "foo"
-
-        then:
-        toolChain.assembler.executable == "foo"
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinkerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinkerTest.groovy
deleted file mode 100644
index 71a7bfd..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccLinkerTest.groovy
+++ /dev/null
@@ -1,86 +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.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.Action
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.internal.LinkerSpec
-import org.gradle.nativebinaries.internal.SharedLibraryLinkerSpec
-import org.gradle.nativebinaries.toolchain.internal.CommandLineTool
-import org.gradle.process.internal.ExecAction
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class GccLinkerTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-
-    def executable = new File("executable")
-    def execActionFactory = Mock(ExecActionFactory)
-    Action<List<String>> argAction = Mock(Action)
-    CommandLineTool<LinkerSpec> commandLineTool = new CommandLineTool<LinkerSpec>("linker", executable, execActionFactory)
-    GccLinker linker = new GccLinker(commandLineTool, argAction, false);
-
-    def "compiles all source files in a single execution"() {
-        given:
-        def testDir = tmpDirProvider.testDirectory
-        def outputFile = testDir.file("output/lib")
-
-        def execAction = Mock(ExecAction)
-        final expectedArgs = [
-                "-sys1", "-sys2",
-                "-shared",
-                getSoNameProp("installName"),
-                "-o", outputFile.absolutePath,
-                testDir.file("one.o").absolutePath,
-                testDir.file("two.o").absolutePath,
-                "-arg1", "-arg2"].flatten()
-
-        when:
-        LinkerSpec spec = Mock(SharedLibraryLinkerSpec)
-        spec.getSystemArgs() >> ['-sys1', '-sys2']
-        spec.getArgs() >> ['-arg1', '-arg2']
-        spec.getOutputFile() >> outputFile
-        spec.getLibraries() >> []
-        spec.getLibraryPath() >> []
-        spec.getInstallName() >> "installName"
-        spec.getObjectFiles() >> [testDir.file("one.o"), testDir.file("two.o")]
-
-        and:
-        linker.execute(spec)
-
-        then:
-        1 * argAction.execute(expectedArgs)
-        1 * execActionFactory.newExecAction() >> execAction
-        1 * execAction.executable(executable)
-        1 * execAction.args(expectedArgs)
-        1 * execAction.environment([:])
-        1 * execAction.execute()
-        0 * execAction._
-    }
-
-    List<String> getSoNameProp(def value) {
-        if (OperatingSystem.current().isWindows()) {
-            return []
-        }
-        if (OperatingSystem.current().isMacOsX()) {
-            return ["-Wl,-install_name,${value}"]
-        }
-        return ["-Wl,-soname,${value}"]
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChainTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChainTest.groovy
deleted file mode 100644
index e250b94..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/GccToolChainTest.groovy
+++ /dev/null
@@ -1,68 +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.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class GccToolChainTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
-    final FileResolver fileResolver = Mock(FileResolver)
-    final toolChain = new GccToolChain("gcc", OperatingSystem.current(), fileResolver, Stub(ExecActionFactory))
-
-    def "uses shared library binary at link time"() {
-        expect:
-        toolChain.getSharedLibraryLinkFileName("test") == toolChain.getSharedLibraryName("test")
-    }
-
-    def "has default tool names"() {
-        expect:
-        toolChain.cppCompiler.executable == "g++"
-        toolChain.cCompiler.executable == "gcc"
-        toolChain.assembler.executable == "as"
-        toolChain.linker.executable == "g++"
-        toolChain.staticLibArchiver.executable == "ar"
-    }
-
-    def "can update tool names"() {
-        when:
-        toolChain.assembler.executable = "foo"
-
-        then:
-        toolChain.assembler.executable == "foo"
-    }
-
-    def "resolves path entries"() {
-        def testDir = tmpDirProvider.testDirectory
-
-        when:
-        toolChain.path "The Path"
-        toolChain.path "Path1", "Path2"
-
-        then:
-        fileResolver.resolve("The Path") >> testDir.file("one")
-        fileResolver.resolve("Path1") >> testDir.file("two")
-        fileResolver.resolve("Path2") >> testDir.file("three")
-
-        and:
-        toolChain.path == [testDir.file("one"), testDir.file("two"), testDir.file("three")]
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformerTest.groovy
deleted file mode 100644
index 67a8607..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/ShortCircuitArgsTransformerTest.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.toolchain.internal.gcc
-
-import org.gradle.nativebinaries.toolchain.internal.ArgsTransformer
-import org.gradle.nativebinaries.toolchain.internal.NativeCompileSpec
-import spock.lang.Specification
-
-class ShortCircuitArgsTransformerTest extends Specification {
-
-    def "can short circuit args transformation"(){
-        given:
-        def spec = Mock(NativeCompileSpec)
-        def expectedArgs = Arrays.asList("some", "options");
-        def delegateArgsTransformer = Mock(ArgsTransformer)
-        def ShortCircuitArgsTransformer transformer = new ShortCircuitArgsTransformer(delegateArgsTransformer)
-
-        when:
-        def transformedArgs = transformer.transform(spec)
-        then:
-        transformedArgs == expectedArgs
-        1 * delegateArgsTransformer.transform(spec) >> expectedArgs
-
-        when:
-        transformedArgs = transformer.transform(spec)
-        then:
-        transformedArgs == expectedArgs
-        0 * delegateArgsTransformer.transform(spec)
-
-    }
-
-    def "does not short circuit when spec has changed"(){
-        given:
-        def spec = Mock(NativeCompileSpec)
-        def changedSpec = Mock(NativeCompileSpec)
-        def delegateArgsTransformer = Mock(ArgsTransformer)
-        def ShortCircuitArgsTransformer transformer = new ShortCircuitArgsTransformer(delegateArgsTransformer)
-
-        when:
-        transformer.transform(spec)
-        then:
-        1 * delegateArgsTransformer.transform(spec)
-
-        when:
-        transformer.transform(changedSpec)
-        then:
-        1 * delegateArgsTransformer.transform(changedSpec)
-        0 * delegateArgsTransformer.transform(spec)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy
deleted file mode 100644
index b1cb4d5..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy
+++ /dev/null
@@ -1,139 +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.nativebinaries.toolchain.internal.gcc.version
-
-import org.gradle.api.Transformer
-import org.gradle.process.ExecResult
-import org.gradle.process.internal.ExecAction
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.util.TreeVisitor
-import spock.lang.Specification
-import spock.lang.Unroll
-
-class GccVersionDeterminerTest extends Specification {
-    def execActionFactory = Mock(ExecActionFactory)
-
-    @Unroll
-    "can scrape ok output for #version"() {
-        expect:
-        def result = output(output)
-        result.available
-        result.version == version
-
-        where:
-        [version, output] << OUTPUTS.collect { [it.value, it.key] }
-    }
-
-    def "handles gcc output that cannot be parsed"() {
-        def visitor = Mock(TreeVisitor)
-
-        expect:
-        def result = output(output)
-        !result.available
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not determine GCC version: g++ produced unexpected output.")
-
-        where:
-        output << [ "not sure about this", "" ]
-    }
-
-    def "g++ execution error ok"() {
-        given:
-        def visitor = Mock(TreeVisitor)
-        def action = Mock(ExecAction)
-        def execResult = Mock(ExecResult)
-
-        and:
-        def determiner = new GccVersionDeterminer(execActionFactory)
-        def binary = new File("g++")
-        
-        when:
-        def result = determiner.transform(binary)
-        
-        then:
-        1 * execActionFactory.newExecAction() >> action
-        1 * action.execute() >> execResult
-        1 * execResult.getExitValue() >> 1
-
-        and:
-        !result.available
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not determine GCC version: failed to execute g++ -v.")
-    }
-
-    def "detects clang pretending to be gcc"() {
-        def visitor = Mock(TreeVisitor)
-
-        expect:
-        def result = output """#define __GNUC_MINOR__ 2
-#define __GNUC_PATCHLEVEL__ 1
-#define __GNUC__ 4
-#define __VERSION__ "4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)"
-#define __clang__ 1
-#define __clang_major__ 5
-#define __clang_minor__ 0
-#define __clang_patchlevel__ 0
-#define __clang_version__ "5.0 (clang-500.2.79)"
-"""
-        !result.available
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("XCode g++ is a wrapper around Clang. Treating it as Clang and not GCC.")
-    }
-
-    GccVersionResult output(String output) {
-        def action = Mock(ExecAction)
-        def result = Mock(ExecResult)
-        1 * execActionFactory.newExecAction() >> action
-        1 * action.setStandardOutput(_) >> { OutputStream outstr -> outstr << output; action }
-        1 * action.execute() >> result
-        new GccVersionDeterminer(execActionFactory).transform(new File("g++"))
-    }
-
-    Transformer transformer(constant) {
-        transformer { constant }
-    }
-
-    Transformer transformer(Closure closure) {
-        new Transformer() {
-            String transform(original) {
-                closure.call(original)
-            }
-        }
-    }
-
-    static final OUTPUTS = [
-        """#define __GNUC_MINOR__ 2
-#define __GNUC_PATCHLEVEL__ 1
-#define __GNUC__ 4
-#define __INTMAX_C(c) c ## LL
-#define __REGISTER_PREFIX__ """: "4",
-        """#define __gnu_linux__ 1
-#define __GNUC__ 3""": "3",
-    ]
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy
deleted file mode 100644
index 3157ea9..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy
+++ /dev/null
@@ -1,202 +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.nativebinaries.toolchain.internal.msvcpp
-
-import net.rubygrapefruit.platform.MissingRegistryEntryException
-import net.rubygrapefruit.platform.SystemInfo
-import net.rubygrapefruit.platform.WindowsRegistry
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TreeVisitor
-import org.gradle.util.VersionNumber
-import org.junit.Rule
-
-import spock.lang.Specification
-
-class DefaultVisualStudioLocatorTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    final WindowsRegistry windowsRegistry =  Stub(WindowsRegistry)
-    final SystemInfo systemInfo =  Stub(SystemInfo)
-    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
-        isWindows() >> true
-        getExecutableName(_ as String) >> { String exeName -> exeName }
-    }
-    final VisualStudioLocator visualStudioLocator = new DefaultVisualStudioLocator(operatingSystem, windowsRegistry, systemInfo)
-
-    def "use highest visual studio version found in the registry"() {
-        def dir1 = vsDir("vs1");
-        def dir2 = vsDir("vs2");
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["", "11.0", "12.0", "ignore-me"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "11.0") >> dir1.absolutePath + "/VC"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> dir2.absolutePath + "/VC"
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(null)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ 12.0"
-        result.visualStudio.version == VersionNumber.parse("12.0")
-        result.visualStudio.baseDir == dir2
-        result.visualStudio.visualCpp
-    }
-
-    def "visual studio not available when nothing in registry and executable not found in path"() {
-        def visitor = Mock(TreeVisitor)
-
-        given:
-        windowsRegistry.getValueNames(_, _) >> { throw new MissingRegistryEntryException("not found") }
-        operatingSystem.findInPath(_) >> null
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(null)
-
-        then:
-        !result.available
-        result.visualStudio == null
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not locate a Visual Studio installation, using the Windows registry and system path.")
-    }
-
-    def "locates visual studio installation based on executables in path"() {
-        def vsDir = vsDir("vs")
-
-        given:
-        windowsRegistry.getValueNames(_, _) >> { throw new MissingRegistryEntryException("not found") }
-        operatingSystem.findInPath("cl.exe") >> vsDir.file("VC/bin/cl.exe")
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(null)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ from system path"
-        result.visualStudio.version == VersionNumber.UNKNOWN
-        result.visualStudio.baseDir == vsDir
-    }
-
-    def "uses visual studio using specified install dir"() {
-        def vsDir1 = vsDir("vs")
-        def vsDir2 = vsDir("vs-2")
-        def ignored = vsDir("vs-3")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> ignored.absolutePath + "/VC"
-        assert visualStudioLocator.locateVisualStudioInstalls(null).available
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(vsDir1)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ from user provided path"
-        result.visualStudio.version == VersionNumber.UNKNOWN
-        result.visualStudio.baseDir == vsDir1
-
-        when:
-        result = visualStudioLocator.locateVisualStudioInstalls(vsDir2)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ from user provided path"
-        result.visualStudio.version == VersionNumber.UNKNOWN
-        result.visualStudio.baseDir == vsDir2
-    }
-
-    def "visual studio not found when specified directory does not look like an install"() {
-        def visitor = Mock(TreeVisitor)
-        def providedDir = tmpDir.createDir("vs")
-        def ignoredDir = vsDir("vs-2")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> ignoredDir.absolutePath + "/VC"
-        assert visualStudioLocator.locateVisualStudioInstalls(null).available
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(providedDir)
-
-        then:
-        !result.available
-        result.visualStudio == null
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("The specified installation directory '$providedDir' does not appear to contain a Visual Studio installation.")
-    }
-
-    def "fills in meta-data from registry for install discovered using the system path"() {
-        def vsDir = vsDir("vs")
-
-        given:
-        operatingSystem.findInPath("cl.exe") >> vsDir.file("VC/bin/cl.exe")
-
-        and:
-        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> vsDir.absolutePath + "/VC"
-        
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(null)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ 12.0"
-        result.visualStudio.version == VersionNumber.parse("12.0")
-        result.visualStudio.baseDir == vsDir
-    }
-
-    def "fills in meta-data from registry for user specified install"() {
-        def vsDir = vsDir("vs")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-
-        and:
-        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> vsDir.absolutePath + "/VC"
-
-        when:
-        def result = visualStudioLocator.locateVisualStudioInstalls(vsDir)
-
-        then:
-        result.available
-        result.visualStudio.name == "Visual C++ 12.0"
-        result.visualStudio.version == VersionNumber.parse("12.0")
-        result.visualStudio.baseDir == vsDir
-    }
-
-    def vsDir(String name) {
-        def dir = tmpDir.createDir(name)
-        dir.createDir("Common7")
-        dir.createFile("VC/bin/cl.exe")
-        dir.createDir("VC/lib")
-        return dir
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy
deleted file mode 100644
index 55bc49c..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy
+++ /dev/null
@@ -1,256 +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.nativebinaries.toolchain.internal.msvcpp
-
-import net.rubygrapefruit.platform.MissingRegistryEntryException
-import net.rubygrapefruit.platform.WindowsRegistry
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TreeVisitor
-import org.gradle.util.VersionNumber
-import org.junit.Rule
-import spock.lang.Specification
-
-class DefaultWindowsSdkLocatorTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    final WindowsRegistry windowsRegistry = Stub(WindowsRegistry)
-    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
-        isWindows() >> true
-        getExecutableName(_ as String) >> { String exeName -> exeName }
-    }
-    final WindowsSdkLocator windowsSdkLocator = new DefaultWindowsSdkLocator(operatingSystem, windowsRegistry)
-
-    def "uses highest version SDK found in registry"() {
-        def dir1 = sdkDir("sdk1")
-        def dir2 = sdkDir("sdk2")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1", "v2"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> dir1.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "sdk 1"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "InstallationFolder") >> dir2.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "ProductVersion") >> "7.1"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "ProductName") >> "sdk 2"
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        result.available
-        result.sdk.name == "sdk 2"
-        result.sdk.version == VersionNumber.parse("7.1")
-        result.sdk.baseDir == dir2
-    }
-
-    def "uses windows kit if version is higher than windows SDK"() {
-        def dir1 = sdkDir("sdk1")
-        def dir2 = kitDir("sdk2")
-        def dir3 = kitDir("sdk3")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\SDKs\Windows/) >> ["v1"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> dir1.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.1"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "sdk 1"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot") >> dir2.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot81") >> dir3.absolutePath
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        result.available
-        result.sdk.name == "Windows Kit 8.1"
-        result.sdk.version == VersionNumber.parse("8.1")
-        result.sdk.baseDir == dir3
-    }
-
-    def "handles missing SDKs and Kits"() {
-        def dir = sdkDir("sdk1")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\SDKs\Windows/) >> { throw new MissingRegistryEntryException("missing") }
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot") >> { throw new MissingRegistryEntryException("missing") }
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot81") >> dir.absolutePath
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        result.available
-        result.sdk.name == "Windows Kit 8.1"
-        result.sdk.version == VersionNumber.parse("8.1")
-        result.sdk.baseDir == dir
-    }
-
-    def "locates windows SDK based on executables in path"() {
-        def sdkDir = sdkDir("sdk")
-
-        given:
-        operatingSystem.findInPath("rc.exe") >> sdkDir.file("bin/rc.exe")
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        result.available
-        result.sdk.name == "Path-resolved Windows SDK"
-        result.sdk.version == VersionNumber.UNKNOWN
-        result.sdk.baseDir == sdkDir
-    }
-
-    def "SDK not available when not found in registry or system path"() {
-        def visitor = Mock(TreeVisitor)
-
-        given:
-        operatingSystem.findInPath(_) >> null
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        !result.available
-        result.sdk == null
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not locate a Windows SDK installation, using the Windows registry and system path.")
-    }
-
-    def "uses windows SDK using specified install dir"() {
-        def sdkDir1 = this.sdkDir("sdk-1")
-        def sdkDir2 = this.sdkDir("sdk-2")
-        def ignoredDir = sdkDir("ignored")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> ignoredDir.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
-        assert windowsSdkLocator.locateWindowsSdks(null).available
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(sdkDir1)
-
-        then:
-        result.available
-        result.sdk.name == "User-provided Windows SDK"
-        result.sdk.version == VersionNumber.UNKNOWN
-        result.sdk.baseDir == sdkDir1
-
-        when:
-        result = windowsSdkLocator.locateWindowsSdks(sdkDir2)
-
-        then:
-        result.available
-        result.sdk.name == "User-provided Windows SDK"
-        result.sdk.version == VersionNumber.UNKNOWN
-        result.sdk.baseDir == sdkDir2
-    }
-
-    def "SDK not available when specified install dir does not look like an SDK"() {
-        def sdkDir1 = tmpDir.createDir("dir")
-        def ignoredDir = sdkDir("ignored")
-        def visitor = Mock(TreeVisitor)
-
-        given:
-        operatingSystem.findInPath(_) >> null
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> ignoredDir.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
-        assert windowsSdkLocator.locateWindowsSdks(null).available
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(sdkDir1)
-
-        then:
-        !result.available
-        result.sdk == null
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("The specified installation directory '$sdkDir1' does not appear to contain a Windows SDK installation.")
-    }
-
-    def "fills in meta-data from registry for SDK discovered using the path"() {
-        def sdkDir = sdkDir("sdk1")
-
-        given:
-        operatingSystem.findInPath("rc.exe") >> sdkDir.file("bin/rc.exe")
-
-        and:
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> sdkDir.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(null)
-
-        then:
-        result.available
-        result.sdk.name == "installed sdk"
-        result.sdk.version == VersionNumber.parse("7.0")
-        result.sdk.baseDir == sdkDir
-    }
-
-    def "fills in meta-data from registry for SDK specified by user"() {
-        def sdkDir = sdkDir("sdk1")
-
-        given:
-        operatingSystem.findInPath(_) >> null
-
-        and:
-        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> sdkDir.absolutePath
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
-        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
-
-        when:
-        def result = windowsSdkLocator.locateWindowsSdks(sdkDir)
-
-        then:
-        result.available
-        result.sdk.name == "installed sdk"
-        result.sdk.version == VersionNumber.parse("7.0")
-        result.sdk.baseDir == sdkDir
-    }
-
-    def sdkDir(String name) {
-        def dir = tmpDir.createDir(name)
-        dir.createFile("bin/rc.exe")
-        dir.createFile("lib/kernel32.lib")
-        return dir
-    }
-
-    def kitDir(String name) {
-        def dir = tmpDir.createDir(name)
-        dir.createFile("bin/x86/rc.exe")
-        dir.createFile("lib/win8/um/x86/kernel32.lib")
-        return dir
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy
deleted file mode 100644
index d926cf1..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy
+++ /dev/null
@@ -1,206 +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.nativebinaries.toolchain.internal.msvcpp
-
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.internal.text.TreeFormatter
-import org.gradle.nativebinaries.platform.Platform
-import org.gradle.nativebinaries.toolchain.internal.ToolChainAvailability
-import org.gradle.nativebinaries.toolchain.internal.ToolSearchResult
-import org.gradle.process.internal.ExecActionFactory
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TreeVisitor
-import spock.lang.Specification
-
-class VisualCppToolChainTest extends Specification {
-    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-    final FileResolver fileResolver = Mock(FileResolver)
-    final ExecActionFactory execActionFactory = Mock(ExecActionFactory)
-    final VisualStudioLocator.SearchResult visualStudioLookup = Stub(VisualStudioLocator.SearchResult)
-    final WindowsSdkLocator.SearchResult windowsSdkLookup = Stub(WindowsSdkLocator.SearchResult)
-    final VisualStudioLocator visualStudioLocator = Stub(VisualStudioLocator) {
-        locateVisualStudioInstalls(_) >> visualStudioLookup
-    }
-    final WindowsSdkLocator windowsSdkLocator = Stub(WindowsSdkLocator) {
-        locateWindowsSdks(_) >> windowsSdkLookup
-    }
-    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
-        isWindows() >> true
-    }
-    final toolChain = new VisualCppToolChain("visualCpp", operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator)
-
-    def "uses .lib file for shared library at link time"() {
-        given:
-        operatingSystem.getSharedLibraryName("test") >> "test.dll"
-
-        expect:
-        toolChain.getSharedLibraryLinkFileName("test") == "test.lib"
-    }
-
-    def "uses .dll file for shared library at runtime time"() {
-        given:
-        operatingSystem.getSharedLibraryName("test") >> "test.dll"
-
-        expect:
-        toolChain.getSharedLibraryName("test") == "test.dll"
-    }
-
-    def "installs an unavailable tool chain when not windows"() {
-        given:
-        def operatingSystem = Stub(OperatingSystem)
-        operatingSystem.isWindows() >> false
-        def toolChain = new VisualCppToolChain("visualCpp", operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator)
-
-        when:
-        def availability = new ToolChainAvailability()
-        toolChain.checkAvailable(availability)
-
-        then:
-        !availability.available
-        availability.unavailableMessage == 'Visual Studio is not available on this operating system.'
-    }
-
-    def "is not available when visual studio installation cannot be located"() {
-        when:
-        visualStudioLookup.available >> false
-        visualStudioLookup.explain(_) >> { TreeVisitor<String> visitor -> visitor.node("vs install not found anywhere") }
-        windowsSdkLookup.available >> false
-
-        and:
-        def result = toolChain.target(Stub(Platform))
-
-        then:
-        !result.available
-        getMessage(result) == "vs install not found anywhere"
-    }
-
-    def "is not available when windows SDK cannot be located"() {
-        when:
-        visualStudioLookup.available >> true
-
-        windowsSdkLookup.available >> false
-        windowsSdkLookup.explain(_) >> { TreeVisitor<String> visitor -> visitor.node("sdk not found anywhere") }
-
-        and:
-        def result = toolChain.target(Stub(Platform))
-
-        then:
-        !result.available
-        getMessage(result) == "sdk not found anywhere"
-    }
-
-    def "is not available when visual studio installation and windows SDK can be located and visual studio install does not support target platform"() {
-        when:
-        def visualStudio = Stub(VisualStudioInstall)
-        def visualCpp = Stub(VisualCppInstall)
-        def platform = Stub(Platform) { getName() >> 'platform' }
-        visualStudioLookup.available >> true
-        windowsSdkLookup.available >> true
-        visualStudioLookup.visualStudio >> visualStudio
-        visualStudioLookup.visualStudio >> Stub(VisualStudioInstall)
-        visualStudio.visualCpp >> visualCpp
-        visualCpp.isSupportedPlatform(platform) >> false
-
-        and:
-        def result = toolChain.target(platform)
-
-        then:
-        !result.available
-        getMessage(result) == "Don't know how to build for platform 'platform'."
-    }
-
-    def "is available when visual studio installation and windows SDK can be located and visual studio install supports target platform"() {
-        when:
-        def visualStudio = Stub(VisualStudioInstall)
-        def visualCpp = Stub(VisualCppInstall)
-        def platform = Stub(Platform)
-        visualStudioLookup.available >> true
-        windowsSdkLookup.available >> true
-        visualStudioLookup.visualStudio >> visualStudio
-        visualStudioLookup.visualStudio >> Stub(VisualStudioInstall)
-        visualStudio.visualCpp >> visualCpp
-        visualCpp.isSupportedPlatform(platform) >> true
-
-        and:
-        def platformToolChain = toolChain.target(platform)
-
-        then:
-        platformToolChain.available
-    }
-
-    def "uses provided installDir and windowsSdkDir for location"() {
-        when:
-        toolChain.installDir = "install-dir"
-        toolChain.windowsSdkDir = "windows-sdk-dir"
-
-        and:
-        fileResolver.resolve("install-dir") >> file("vs")
-        visualStudioLocator.locateVisualStudioInstalls(file("vs")) >> visualStudioLookup
-        visualStudioLookup.available >> true
-
-        and:
-        fileResolver.resolve("windows-sdk-dir") >> file("win-sdk")
-        windowsSdkLocator.locateWindowsSdks(file("win-sdk")) >> windowsSdkLookup
-        windowsSdkLookup.available >> true
-
-        and:
-        0 * _._
-
-        then:
-        def availability = new ToolChainAvailability()
-        toolChain.checkAvailable(availability);
-        availability.available
-    }
-
-    def "resolves install directory"() {
-        when:
-        toolChain.installDir = "The Path"
-
-        then:
-        fileResolver.resolve("The Path") >> file("one")
-
-        and:
-        toolChain.installDir == file("one")
-    }
-
-    def "resolves windows sdk directory"() {
-        when:
-        toolChain.windowsSdkDir = "The Path"
-
-        then:
-        fileResolver.resolve("The Path") >> file("one")
-
-        and:
-        toolChain.windowsSdkDir == file("one")
-    }
-
-    def file(String name) {
-        testDirectoryProvider.testDirectory.file(name)
-    }
-
-    def createFile(String name) {
-        file(name).createFile()
-    }
-
-    def getMessage(ToolSearchResult result) {
-        def formatter = new TreeFormatter()
-        result.explain(formatter)
-        return formatter.toString()
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPathTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPathTest.groovy
deleted file mode 100644
index 9e5cf82..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/internal/tools/ToolSearchPathTest.groovy
+++ /dev/null
@@ -1,125 +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.nativebinaries.toolchain.internal.tools
-
-import org.gradle.api.GradleException
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.toolchain.internal.ToolType
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TreeVisitor
-import org.junit.Rule
-import spock.lang.Specification
-
-class ToolSearchPathTest extends Specification {
-    @Rule def TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    def os = Stub(OperatingSystem)
-    def registry = new ToolSearchPath(os)
-
-    def "finds executable in system path"() {
-        def file = tmpDir.createFile("cc.bin")
-
-        given:
-        os.findInPath("cc") >> file
-
-        when:
-        def result = registry.locate(ToolType.C_COMPILER, "cc")
-
-        then:
-        result.available
-        result.tool == file
-    }
-
-    def "finds executable in provided path"() {
-        def file = tmpDir.createFile("cc.bin")
-
-        given:
-        os.getExecutableName("cc") >> "cc.bin"
-        registry.setPath([file.parentFile])
-
-        when:
-        def result = registry.locate(ToolType.C_COMPILER, "cc")
-
-        then:
-        result.available
-        result.tool == file
-    }
-
-    def "executable is unavailable when not found in path"() {
-        def visitor = Mock(TreeVisitor)
-        def dir1 = tmpDir.createDir("some-dir")
-        def dir2 = tmpDir.createDir("some-dir-2")
-
-        given:
-        os.getExecutableName("cc") >> "cc.bin"
-        registry.setPath([dir1, dir2])
-
-        when:
-        def result = registry.locate(ToolType.C_COMPILER, "cc")
-
-        then:
-        !result.available
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not find C compiler 'cc'. Searched in")
-        1 * visitor.startChildren()
-        1 * visitor.node(dir1.toString())
-        1 * visitor.node(dir2.toString())
-        1 * visitor.endChildren()
-        0 * visitor._
-    }
-
-    def "executable is unavailable when not found in system path"() {
-        def visitor = Mock(TreeVisitor)
-
-        given:
-        os.findInPath("cc") >> null
-
-        when:
-        def result = registry.locate(ToolType.C_COMPILER, "cc")
-
-        then:
-        !result.available
-
-        when:
-        result.explain(visitor)
-
-        then:
-        1 * visitor.node("Could not find C compiler 'cc' in system path.")
-        0 * visitor._
-    }
-
-    def "cannot use an unavailable tool"() {
-        given:
-        os.findInPath("cc") >> null
-
-        when:
-        def result = registry.locate(ToolType.C_COMPILER, "cc")
-
-        then:
-        !result.available
-
-        when:
-        result.getTool()
-
-        then:
-        GradleException e = thrown()
-        e.message == "Could not find C compiler 'cc' in system path."
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPluginTest.groovy
deleted file mode 100644
index 5fbb6db..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ClangCompilerPluginTest.groovy
+++ /dev/null
@@ -1,57 +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.nativebinaries.toolchain.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.Clang
-import org.gradle.nativebinaries.toolchain.internal.clang.ClangToolChain
-
-class ClangCompilerPluginTest extends ToolChainPluginTest {
-
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        ClangCompilerPlugin
-    }
-
-    @Override
-    Class<? extends ToolChain> getToolchainClass() {
-        Clang
-    }
-
-    @Override
-    String getToolchainName() {
-        "clang"
-    }
-
-    def "makes a Clang tool chain available"() {
-        when:
-        register()
-
-        then:
-        toolchain instanceof ClangToolChain
-        toolchain.displayName == "Tool chain 'clang' (Clang)"
-    }
-
-    def "registers default Clang tool chain"() {
-        when:
-        addDefaultToolchain()
-
-        then:
-        toolchain instanceof ClangToolChain
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPluginTest.groovy
deleted file mode 100644
index d67cb54..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/GccCompilerPluginTest.groovy
+++ /dev/null
@@ -1,59 +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.nativebinaries.toolchain.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.Gcc
-import org.gradle.nativebinaries.toolchain.internal.gcc.GccToolChain
-import org.gradle.util.TestUtil
-
-class GccCompilerPluginTest extends ToolChainPluginTest {
-    def project = TestUtil.createRootProject()
-
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        GccCompilerPlugin
-    }
-
-    @Override
-    Class<? extends ToolChain> getToolchainClass() {
-        Gcc
-    }
-
-    @Override
-    String getToolchainName() {
-        "gcc"
-    }
-
-    def "makes a Gcc tool chain available"() {
-        when:
-        register()
-
-        then:
-        toolchain instanceof GccToolChain
-        toolchain.displayName == "Tool chain 'gcc' (GNU GCC)"
-    }
-
-    def "registers default Gcc tool chain"() {
-        when:
-        addDefaultToolchain()
-
-        then:
-        toolchain instanceof GccToolChain
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy
deleted file mode 100644
index cfa6812..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy
+++ /dev/null
@@ -1,64 +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.nativebinaries.toolchain.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.VisualCpp
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualCppToolChain
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-
-class MicrosoftVisualCppPluginTest extends ToolChainPluginTest {
-    @Rule
-    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-
-    @Override
-    Class<? extends Plugin> getPluginClass() {
-        MicrosoftVisualCppPlugin
-    }
-
-    @Override
-    Class<? extends ToolChain> getToolchainClass() {
-        VisualCpp
-    }
-
-    @Override
-    String getToolchainName() {
-        VisualCppToolChain.DEFAULT_NAME
-    }
-
-    def "makes a VisualCpp tool chain available"() {
-        when:
-        register()
-
-        then:
-        toolchain instanceof VisualCppToolChain
-    }
-
-    def "registers default VisualCpp tool chain"() {
-        when:
-        addDefaultToolchain()
-
-        then:
-        toolchain instanceof VisualCppToolChain
-    }
-
-    def file(String name) {
-        testDirectoryProvider.testDirectory.file(name)
-    }
-}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ToolchainPluginTest.groovy b/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ToolchainPluginTest.groovy
deleted file mode 100644
index 89ba5d1..0000000
--- a/subprojects/cpp/src/test/groovy/org/gradle/nativebinaries/toolchain/plugins/ToolchainPluginTest.groovy
+++ /dev/null
@@ -1,70 +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.nativebinaries.toolchain.plugins
-
-import org.gradle.api.Plugin
-import org.gradle.api.plugins.ExtensionAware
-import org.gradle.api.plugins.ExtraPropertiesExtension
-import org.gradle.nativebinaries.toolchain.ToolChain
-import org.gradle.nativebinaries.toolchain.ToolChainRegistry
-import org.gradle.nativebinaries.toolchain.internal.ToolChainInternal
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-abstract class ToolChainPluginTest extends Specification {
-
-    def project = TestUtil.createRootProject()
-
-    def setup() {
-        project.plugins.apply(getPluginClass())
-    }
-
-    abstract Class<? extends Plugin> getPluginClass()
-
-    abstract Class<? extends ToolChain> getToolchainClass()
-
-    String getToolchainName() {
-        "toolchain"
-    }
-
-    ToolChainInternal getToolchain() {
-        project.modelRegistry.get("toolChains", ToolChainRegistry).getByName(getToolchainName()) as ToolChainInternal
-    }
-
-    void register() {
-        project.model {
-            toolChains {
-                create(getToolchainName(), getToolchainClass())
-            }
-        }
-    }
-
-    void addDefaultToolchain() {
-        project.model { toolChains { addDefaultToolChains() } }
-    }
-
-    def "tool chain is extended"() {
-        when:
-        register()
-
-        then:
-        with (toolchain) {
-            it instanceof ExtensionAware
-            it.ext instanceof ExtraPropertiesExtension
-        }
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy
deleted file mode 100644
index 86d36be..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy
+++ /dev/null
@@ -1,128 +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.ide.visualstudio.fixtures
-
-import org.gradle.nativebinaries.language.cpp.fixtures.app.SourceFile
-import org.gradle.nativebinaries.language.cpp.fixtures.app.TestComponent
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.TextUtil
-
-class ProjectFile {
-    String name
-    TestFile projectFile
-    Node projectXml
-
-    ProjectFile(TestFile projectFile) {
-        assert projectFile.exists()
-        this.projectFile = projectFile
-        this.name = projectFile.name.replace(".vcxproj", "")
-        this.projectXml = new XmlParser().parse(projectFile)
-    }
-
-    public Map<String, Configuration> getProjectConfigurations() {
-        def configs = itemGroup("ProjectConfigurations").collect {
-            new Configuration(it.Configuration[0].text(), it.Platform[0].text())
-        }
-        return configs.collectEntries {
-            [it.name, it]
-        }
-    }
-
-    public String getProjectGuid() {
-        return globals.ProjectGUID[0].text()
-    }
-
-    public Node getGlobals() {
-        return projectXml.PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
-    }
-
-    public List<String> getSourceFiles() {
-        def sources = itemGroup('Sources').ClCompile
-        return normalise(sources*.'@Include')
-    }
-
-    public List<String> getResourceFiles() {
-        def sources = itemGroup('References').ResourceCompile
-        return normalise(sources*.'@Include')
-    }
-
-    public List<String> getHeaderFiles() {
-        def sources = itemGroup('Headers').ClInclude
-        return normalise(sources*.'@Include')
-    }
-
-    private static List<String> normalise(List<String> files) {
-        return files.collect({ TextUtil.normaliseFileSeparators(it)}).sort()
-    }
-
-    private Node itemGroup(String label) {
-        return projectXml.ItemGroup.find({it.'@Label' == label}) as Node
-    }
-
-    class Configuration {
-        String name
-        String platformName
-
-        Configuration(String name, String platformName) {
-            this.name = name
-            this.platformName = platformName
-        }
-
-        ProjectFile getProject() {
-            return ProjectFile.this
-        }
-
-        String getMacros() {
-            buildConfiguration.NMakePreprocessorDefinitions[0].text()
-        }
-
-        String getIncludePath() {
-            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeIncludeSearchPath[0].text())
-        }
-
-        String getBuildCommand() {
-            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeBuildCommandLine[0].text())
-        }
-
-        String getOutputFile() {
-            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeOutput[0].text())
-        }
-
-        private Node getBuildConfiguration() {
-            projectXml.PropertyGroup.find({ it.'@Label' == 'NMakeConfiguration' && it.'@Condition' == condition}) as Node
-        }
-
-        private String getCondition() {
-            "'\$(Configuration)|\$(Platform)'=='${name}|${platformName}'"
-        }
-    }
-
-    void assertHasComponentSources(TestComponent component, String basePath) {
-        assert sourceFiles == ['build.gradle'] + sourceFiles(component.sourceFiles, basePath)
-        assert headerFiles == sourceFiles(component.headerFiles, basePath)
-    }
-
-    void assertHasComponentSources(TestComponent component, String basePath, TestComponent component2, String basePath2) {
-        assert sourceFiles == ['build.gradle'] + sourceFiles(component.sourceFiles, basePath) + sourceFiles(component2.sourceFiles, basePath2)
-        assert headerFiles == sourceFiles(component.headerFiles, basePath) + sourceFiles(component2.headerFiles, basePath2)
-    }
-
-    private static List<String> sourceFiles(List<SourceFile> files, String path) {
-        return files*.withPath(path).sort()
-    }
-
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AvailableToolChains.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AvailableToolChains.java
deleted file mode 100755
index d7bec41..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/AvailableToolChains.java
+++ /dev/null
@@ -1,503 +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.nativebinaries.language.cpp.fixtures;
-
-import com.google.common.base.Joiner;
-import net.rubygrapefruit.platform.SystemInfo;
-import net.rubygrapefruit.platform.WindowsRegistry;
-import org.gradle.api.internal.file.TestFiles;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.nativebinaries.platform.internal.DefaultPlatform;
-import org.gradle.nativebinaries.toolchain.Clang;
-import org.gradle.nativebinaries.toolchain.Gcc;
-import org.gradle.nativebinaries.toolchain.VisualCpp;
-import org.gradle.nativebinaries.toolchain.internal.gcc.version.GccVersionDeterminer;
-import org.gradle.nativebinaries.toolchain.internal.gcc.version.GccVersionResult;
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.DefaultVisualStudioLocator;
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualStudioInstall;
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualStudioLocator;
-import org.gradle.nativebinaries.toolchain.plugins.ClangCompilerPlugin;
-import org.gradle.nativebinaries.toolchain.plugins.GccCompilerPlugin;
-import org.gradle.nativebinaries.toolchain.plugins.MicrosoftVisualCppPlugin;
-import org.gradle.process.internal.DefaultExecAction;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.util.VersionNumber;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class AvailableToolChains {
-    /**
-     * @return A list of all tool chains installed on the system, with the default tool chain listed first (if installed).
-     */
-    public static List<InstalledToolChain> getAvailableToolChains() {
-        List<ToolChainCandidate> allToolChains = getToolChains();
-        List<InstalledToolChain> installedToolChains = new ArrayList<InstalledToolChain>();
-        for (ToolChainCandidate candidate : allToolChains) {
-            if (candidate.isAvailable()) {
-                installedToolChains.add((InstalledToolChain) candidate);
-            }
-        }
-        return installedToolChains;
-    }
-
-    /**
-     * @return The tool chain with the given name.
-     */
-    public static ToolChainCandidate getToolChain(ToolChainRequirement requirement) {
-        for (ToolChainCandidate toolChainCandidate : getToolChains()) {
-            if (toolChainCandidate.meets(requirement)) {
-                return toolChainCandidate;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @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(findGcc("4", null));
-
-            // Clang must be on the path
-            // TODO:ADAM Also check on windows
-            compilers.add(findClang());
-
-            // TODO:DAZ Make a GCC3 install available for testing
-        }
-        return compilers;
-    }
-
-    static private ToolChainCandidate findClang() {
-        File compilerExe = OperatingSystem.current().findInPath("clang");
-        if (compilerExe != null) {
-            return new InstalledClang();
-        }
-        return new UnavailableToolChain("clang");
-    }
-
-    static private ToolChainCandidate findVisualCpp() {
-        // Search in the standard installation locations
-        VisualStudioLocator vsLocator = new DefaultVisualStudioLocator(OperatingSystem.current(), NativeServices.getInstance().get(WindowsRegistry.class), NativeServices.getInstance().get(SystemInfo.class));
-        VisualStudioLocator.SearchResult searchResult = vsLocator.locateVisualStudioInstalls(null);
-        if (searchResult.isAvailable()) {
-            VisualStudioInstall install = searchResult.getVisualStudio();
-            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 InstalledWindowsGcc("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 InstalledWindowsGcc("gcc cygwin").inPath(compilerExe.getParentFile());
-        }
-
-        return new UnavailableToolChain("gcc cygwin");
-    }
-
-    static private ToolChainCandidate findGcc(String versionPrefix, String hardcodedFallback) {
-        String name = String.format("gcc %s", versionPrefix);
-        GccVersionDeterminer versionDeterminer = new GccVersionDeterminer(new ExecActionFactory() {
-            public ExecAction newExecAction() {
-                return new DefaultExecAction(TestFiles.resolver());
-            }
-        });
-
-        List<File> gppCandidates = OperatingSystem.current().findAllInPath("g++");
-        for (int i = 0; i < gppCandidates.size(); i++) {
-            File candidate = gppCandidates.get(i);
-            GccVersionResult version = versionDeterminer.transform(candidate);
-            if (version.isAvailable() && version.getVersion().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 boolean meets(ToolChainRequirement requirement);
-
-        public abstract void initialiseEnvironment();
-
-        public abstract void resetEnvironment();
-
-   }
-    
-    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>();
-        private final String displayName;
-        protected final String pathVarName;
-        private final String objectFileNameSuffix;
-
-        private String originalPath;
-
-        public InstalledToolChain(String displayName) {
-            this.displayName = displayName;
-            this.pathVarName = OperatingSystem.current().getPathVar();
-            this.objectFileNameSuffix = OperatingSystem.current().isWindows() ? ".obj" : ".o";
-        }
-
-        InstalledToolChain inPath(File... pathEntries) {
-            Collections.addAll(this.pathEntries, pathEntries);
-            return this;
-        }
-
-        @Override
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return true;
-        }
-
-        public String getTypeDisplayName() {
-            return getDisplayName().replaceAll("\\s+\\d+(\\.\\d+)*$", "");
-        }
-
-        public abstract String getInstanceDisplayName();
-
-        public ExecutableFixture executable(Object path) {
-            return new ExecutableFixture(new TestFile(OperatingSystem.current().getExecutableName(path.toString())), this);
-        }
-
-        public TestFile objectFile(Object path) {
-            return new TestFile(path.toString() + objectFileNameSuffix);
-        }
-
-        public SharedLibraryFixture sharedLibrary(Object path) {
-            return new SharedLibraryFixture(new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
-        }
-
-        public StaticLibraryFixture staticLibrary(Object path) {
-            return new StaticLibraryFixture(new TestFile(OperatingSystem.current().getStaticLibraryName(path.toString())), this);
-        }
-
-        public NativeBinaryFixture resourceOnlyLibrary(Object path) {
-            return new NativeBinaryFixture(new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
-        }
-
-        /**
-         * Initialise the process environment so that this tool chain is visible to the default discovery mechanism that the
-         * plugin uses (eg add the compiler to the PATH).
-         */
-        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);
-            }
-        }
-
-        public void resetEnvironment() {
-            if (originalPath != null) {
-                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
-            }
-        }
-
-        public abstract String getBuildScriptConfig();
-
-        public abstract String getImplementationClass();
-
-        public abstract String getPluginClass();
-
-        public boolean isVisualCpp() {
-            return false;
-        }
-
-        public List<File> getPathEntries() {
-            return pathEntries;
-        }
-
-        /**
-         * The environment required to execute a binary created by this toolchain.
-         */
-        public List<String> getRuntimeEnv() {
-            // Toolchains should be linking against stuff in the standard locations
-            return Collections.emptyList();
-        }
-
-        public String getId() {
-            return displayName.replaceAll("\\W", "");
-        }
-    }
-
-    public static abstract class GccCompatibleToolChain extends InstalledToolChain {
-        protected GccCompatibleToolChain(String displayName) {
-            super(displayName);
-        }
-
-        protected String find(String tool) {
-            if (getPathEntries().isEmpty()) {
-                return tool;
-            }
-            return new File(getPathEntries().get(0), tool).getAbsolutePath();
-        }
-
-        public String getLinker() {
-            return getCCompiler();
-        }
-
-        public String getStaticLibArchiver() {
-            return find("ar");
-        }
-
-        public abstract String getCCompiler();
-    }
-
-    public static class InstalledGcc extends GccCompatibleToolChain {
-        public InstalledGcc(String name) {
-            super(name);
-        }
-
-        @Override
-        public boolean meets(ToolChainRequirement requirement) {
-            return requirement == ToolChainRequirement.Gcc || requirement == ToolChainRequirement.GccCompatible || requirement == ToolChainRequirement.Available;
-        }
-
-        @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(), pathEntry.toURI());
-            }
-            return config;
-        }
-
-        @Override
-        public String getCCompiler() {
-            return find("gcc");
-        }
-
-        public String getInstanceDisplayName() {
-            return String.format("Tool chain '%s' (GNU GCC)", getId());
-        }
-
-        public String getImplementationClass() {
-            return Gcc.class.getSimpleName();
-        }
-
-        @Override
-        public String getPluginClass() {
-            return GccCompilerPlugin.class.getSimpleName();
-        }
-    }
-
-    public static class InstalledWindowsGcc extends InstalledGcc {
-        public InstalledWindowsGcc(String name) {
-            super(name);
-        }
-
-        /**
-         * The environment required to execute a binary created by this toolchain.
-         */
-        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 static class InstalledVisualCpp extends InstalledToolChain {
-        private VersionNumber version;
-        private File installDir;
-
-        public InstalledVisualCpp(String name) {
-            super(name);
-        }
-
-        public InstalledVisualCpp withInstall(VisualStudioInstall install) {
-            DefaultPlatform targetPlatform = new DefaultPlatform("default");
-            installDir = install.getVisualStudioDir();
-            version = install.getVersion();
-            pathEntries.addAll(install.getVisualCpp().getPath(targetPlatform));
-            return this;
-        }
-
-        @Override
-        public boolean meets(ToolChainRequirement requirement) {
-            switch (requirement) {
-                case Available:
-                case VisualCpp:
-                    return true;
-                case VisualCpp2013:
-                    return version.compareTo(VersionNumber.parse("12.0")) >= 0;
-                default:
-                    return false;
-            }
-        }
-
-        @Override
-        public String getBuildScriptConfig() {
-            String config = String.format("%s(%s)\n", getId(), getImplementationClass());
-            if (installDir != null) {
-                config += String.format("%s.installDir = file('%s')", getId(), installDir.toURI());
-            }
-            return config;
-        }
-
-        public String getImplementationClass() {
-            return VisualCpp.class.getSimpleName();
-        }
-
-        public String getInstanceDisplayName() {
-            return String.format("Tool chain '%s' (Visual Studio)", getId());
-        }
-
-        @Override
-        public String getPluginClass() {
-            return MicrosoftVisualCppPlugin.class.getSimpleName();
-        }
-
-        public boolean isVisualCpp() {
-            return true;
-        }
-
-        public VersionNumber getVersion() {
-            return version;
-        }
-
-        @Override
-        public TestFile objectFile(Object path) {
-            return new TestFile(path.toString() + ".obj");
-        }
-    }
-
-    public static class InstalledClang extends GccCompatibleToolChain {
-        public InstalledClang() {
-            super("clang");
-        }
-
-        @Override
-        public boolean meets(ToolChainRequirement requirement) {
-            return requirement == ToolChainRequirement.GccCompatible || requirement == ToolChainRequirement.Available;
-        }
-
-        @Override
-        public String getBuildScriptConfig() {
-            return "clang(Clang)";
-        }
-
-        @Override
-        public String getCCompiler() {
-            return find("clang");
-        }
-
-        public String getInstanceDisplayName() {
-            return String.format("Tool chain '%s' (Clang)", getId());
-        }
-
-        @Override
-        public String getImplementationClass() {
-            return Clang.class.getSimpleName();
-        }
-
-        @Override
-        public String getPluginClass() {
-            return ClangCompilerPlugin.class.getSimpleName();
-        }
-    }
-
-    public static class UnavailableToolChain extends ToolChainCandidate {
-        private final String name;
-
-        public UnavailableToolChain(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public boolean meets(ToolChainRequirement requirement) {
-            return false;
-        }
-
-        @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/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ExecutableFixture.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ExecutableFixture.groovy
deleted file mode 100644
index 530bb92..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ExecutableFixture.groovy
+++ /dev/null
@@ -1,35 +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.nativebinaries.language.cpp.fixtures
-
-import org.gradle.test.fixtures.file.ExecOutput
-import org.gradle.test.fixtures.file.TestFile
-
-class ExecutableFixture extends NativeBinaryFixture {
-    ExecutableFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
-        super(file, toolChain)
-    }
-
-    public ExecOutput exec(Object... args) {
-        assertExists()
-        return file.execute(args as List, toolChain.runtimeEnv)
-    }
-
-    public List<String> listLinkedLibraries() {
-        return binaryInfo.listLinkedLibraries()
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeBinaryFixture.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeBinaryFixture.groovy
deleted file mode 100644
index a149496..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeBinaryFixture.groovy
+++ /dev/null
@@ -1,89 +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.nativebinaries.language.cpp.fixtures
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.BinaryInfo
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.DumpbinBinaryInfo
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.OtoolBinaryInfo
-import org.gradle.nativebinaries.language.cpp.fixtures.binaryinfo.ReadelfBinaryInfo
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestFile.Snapshot
-
-class NativeBinaryFixture {
-    final TestFile file
-    protected final AvailableToolChains.InstalledToolChain toolChain
-
-    NativeBinaryFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
-        this.file = file
-        this.toolChain = toolChain
-    }
-
-    URI toURI() {
-        file.toURI()
-    }
-
-    Snapshot snapshot() {
-        file.snapshot()
-    }
-
-    void assertHasChangedSince(Snapshot snapshot) {
-        file.assertContentsHaveChangedSince(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")
-    }
-
-    boolean assertExistsAndDelete() {
-        assertExists()
-        file.delete()
-    }
-
-    BinaryInfo getBinaryInfo() {
-        file.assertExists()
-        if (OperatingSystem.current().isMacOsX()) {
-            return new OtoolBinaryInfo(file);
-        }
-        if (OperatingSystem.current().isWindows()) {
-            return new DumpbinBinaryInfo(file, toolChain);
-        }
-        return new ReadelfBinaryInfo(file);
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeInstallationFixture.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeInstallationFixture.groovy
deleted file mode 100644
index 315c4cb..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/NativeInstallationFixture.groovy
+++ /dev/null
@@ -1,73 +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.nativebinaries.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/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChain.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChain.groovy
deleted file mode 100644
index ada654d..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChain.groovy
+++ /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.nativebinaries.language.cpp.fixtures
-
-import org.spockframework.runtime.extension.ExtensionAnnotation
-
-import java.lang.annotation.ElementType
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import java.lang.annotation.Target
-
- at Retention(RetentionPolicy.RUNTIME)
- at Target([ElementType.METHOD, ElementType.TYPE])
- at ExtensionAnnotation(RequiresInstalledToolChainExtension.class)
-public @interface RequiresInstalledToolChain {
-    ToolChainRequirement value() default ToolChainRequirement.Available;
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChainExtension.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChainExtension.groovy
deleted file mode 100644
index 92567b2..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/RequiresInstalledToolChainExtension.groovy
+++ /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.nativebinaries.language.cpp.fixtures
-
-import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
-import org.spockframework.runtime.model.FeatureInfo
-import org.spockframework.runtime.model.SpecInfo
-
-class RequiresInstalledToolChainExtension extends AbstractAnnotationDrivenExtension<RequiresInstalledToolChain> {
-    @Override
-    void visitSpecAnnotation(RequiresInstalledToolChain annotation, SpecInfo spec) {
-        final available = isToolChainAvailable(annotation)
-        spec.skipped |= !available
-    }
-
-    @Override
-    void visitFeatureAnnotation(RequiresInstalledToolChain annotation, FeatureInfo feature) {
-        final available = isToolChainAvailable(annotation)
-        feature.skipped |= !available
-    }
-
-    private static boolean isToolChainAvailable(RequiresInstalledToolChain annotation) {
-        def requiredToolChain = AvailableToolChains.getToolChain(annotation.value())
-        return requiredToolChain != null
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SharedLibraryFixture.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SharedLibraryFixture.groovy
deleted file mode 100644
index 05a70c5..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/SharedLibraryFixture.groovy
+++ /dev/null
@@ -1,47 +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.nativebinaries.language.cpp.fixtures
-
-import org.gradle.test.fixtures.file.TestFile
-
-class SharedLibraryFixture extends NativeBinaryFixture {
-    SharedLibraryFixture(TestFile file, AvailableToolChains.InstalledToolChain 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()
-        }
-    }
-
-    String getSoName() {
-        return binaryInfo.soName
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/StaticLibraryFixture.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/StaticLibraryFixture.groovy
deleted file mode 100644
index 63fffa6..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/StaticLibraryFixture.groovy
+++ /dev/null
@@ -1,29 +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.nativebinaries.language.cpp.fixtures
-
-import org.gradle.test.fixtures.file.TestFile
-
-class StaticLibraryFixture extends NativeBinaryFixture {
-    StaticLibraryFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
-        super(file, toolChain)
-    }
-
-    List<String> listObjectFiles() {
-        getBinaryInfo().listObjectFiles()
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ToolChainRequirement.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ToolChainRequirement.java
deleted file mode 100644
index 5250199..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/ToolChainRequirement.java
+++ /dev/null
@@ -1,30 +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.nativebinaries.language.cpp.fixtures;
-
-public enum ToolChainRequirement {
-    // Any available tool chain
-    Available,
-    // Any available Visual Studio implementation
-    VisualCpp,
-    // Any available Visual Studio >= 2013
-    VisualCpp2013,
-    // Any available GCC implementation (including mingw, cygwin, but not clang)
-    Gcc,
-    // Any available GCC compatible implementation (including mingw, cygwin, and clang)
-    GccCompatible
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCallingMixedCAndCppHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCallingMixedCAndCppHelloWorldApp.groovy
deleted file mode 100644
index 12a7d21..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCallingMixedCAndCppHelloWorldApp.groovy
+++ /dev/null
@@ -1,83 +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.nativebinaries.language.cpp.fixtures.app;
-
-public class CCallingMixedCAndCppHelloWorldApp extends HelloWorldApp {
-    @Override
-    List<String> getPluginList() {
-        return ['c', 'cpp']
-    }
-
-    @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
-            }
-"""),
-        sourceFile("c", "sum.c", """
-            #include "hello.h"
-
-            int DLL_FUNC sum(int a, int b) {
-                return a + b;
-            }
-""")
-    ]
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCompilerDetectingTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCompilerDetectingTestApp.groovy
deleted file mode 100644
index 480d68a..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CCompilerDetectingTestApp.groovy
+++ /dev/null
@@ -1,80 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains.InstalledToolChain
-
-class CCompilerDetectingTestApp extends TestApp {
-    String expectedOutput(InstalledToolChain toolChain) {
-        "C ${toolChain.typeDisplayName}"
-    }
-
-    @Override
-    SourceFile getLibraryHeader() {
-        sourceFile("headers", "c-detector.h", """
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC detectCCompiler();
-        """);
-    }
-
-    @Override
-    List<SourceFile> getLibrarySources() {
-        return [
-            sourceFile("c", "c-detector.c", """
-                #include <stdio.h>
-                #include "c-detector.h"
-
-                void detectCCompiler() {
-                #if !defined(__cplusplus)
-                    printf("C ");
-                #endif
-                #if defined(__clang__)
-                    printf("clang");
-                #elif defined(__GNUC__) && defined(__MINGW32__)
-                    printf("mingw");
-                #elif defined(__GNUC__) && defined(__CYGWIN__)
-                    printf("gcc cygwin");
-                #elif defined(__GNUC__)
-                    printf("gcc");
-                #elif defined(_MSC_VER)
-                    printf("visual c++");
-                #else
-                    printf("unknown");
-                #endif
-                }
-        """)
-        ]
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        return new SourceFile("c", "main.c", """
-#include <stdio.h>
-#include "c-detector.h"
-
-int main () {
-    detectCCompiler();
-    return 0;
-}
-""")
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CHelloWorldApp.groovy
deleted file mode 100644
index 69d1582..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CHelloWorldApp.groovy
+++ /dev/null
@@ -1,167 +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.nativebinaries.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
-            #ifdef CUSTOM
-            char* greeting() {
-                return CUSTOM;
-            }
-            #endif
-            void DLL_FUNC sayHello() {
-                #if defined(FRENCH) || defined(CUSTOM)
-                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);
-                }
-
-                // Extra function to ensure library has different size
-                int anotherFunction() {
-                    return 1000;
-                }
-            """),
-            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"
-
-    TestComponent getCunitTests() {
-        return new TestComponent() {
-            List<SourceFile> sourceFiles = [
-                    sourceFile("cunit", "test.c", """
-#include <CUnit/Basic.h>
-#include "hello.h"
-#include "gradle_cunit_register.h"
-
-int init_test(void) {
-    return 0;
-}
-
-int clean_test(void) {
-    return 0;
-}
-
-void test_sum(void) {
-  CU_ASSERT(sum(0, 2) == 2);
-#ifndef ONE_TEST
-  CU_ASSERT(sum(0, -2) == -2);
-  CU_ASSERT(sum(2, 2) == 4);
-#endif
-}
-
-void gradle_cunit_register() {
-    CU_pSuite pSuiteMath = CU_add_suite("hello test", init_test, clean_test);
-    CU_add_test(pSuiteMath, "test_sum", test_sum);
-}
-                    """),
-            ]
-            List<SourceFile> headerFiles = [
-            ]
-
-            String testOutput = """
-Suite: hello test
-  Test: test of sum ...passed
-
-Run Summary:    Type  Total    Ran Passed Failed Inactive
-              suites      1      1    n/a      0        0
-               tests      1      1      1      0        0
-             asserts      3      3      3      0      n/a
-"""
-        };
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy
deleted file mode 100644
index 41a1cd2..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCallingCHelloWorldApp.groovy
+++ /dev/null
@@ -1,75 +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.nativebinaries.language.cpp.fixtures.app;
-
-public class CppCallingCHelloWorldApp extends HelloWorldApp {
-    @Override
-    List<String> getPluginList() {
-        return ['c', 'cpp']
-    }
-
-    @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/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCompilerDetectingTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCompilerDetectingTestApp.groovy
deleted file mode 100644
index 01fd267..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppCompilerDetectingTestApp.groovy
+++ /dev/null
@@ -1,80 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains.InstalledToolChain
-
-class CppCompilerDetectingTestApp extends TestApp {
-    String expectedOutput(InstalledToolChain toolChain) {
-        "C++ ${toolChain.typeDisplayName}"
-    }
-
-    @Override
-    SourceFile getLibraryHeader() {
-        sourceFile("headers", "cpp-detector.h", """
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            void DLL_FUNC detectCppCompiler();
-        """);
-    }
-
-    @Override
-    List<SourceFile> getLibrarySources() {
-        return [
-            sourceFile("cpp", "cpp-detector.cpp", """
-                #include <stdio.h>
-                #include "cpp-detector.h"
-
-                void detectCppCompiler() {
-                #if defined(__cplusplus)
-                    printf("C++ ");
-                #endif
-                #if defined(__clang__)
-                    printf("clang");
-                #elif defined(__GNUC__) && defined(__MINGW32__)
-                    printf("mingw");
-                #elif defined(__GNUC__) && defined(__CYGWIN__)
-                    printf("gcc cygwin");
-                #elif defined(__GNUC__)
-                    printf("gcc");
-                #elif defined(_MSC_VER)
-                    printf("visual c++");
-                #else
-                    printf("unknown");
-                #endif
-                }
-        """)
-        ]
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        return new SourceFile("cpp", "main.cpp", """
-#include <stdio.h>
-#include "cpp-detector.h"
-
-int main () {
-    detectCppCompiler();
-    return 0;
-}
-""")
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppHelloWorldApp.groovy
deleted file mode 100644
index c0398a4..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/CppHelloWorldApp.groovy
+++ /dev/null
@@ -1,116 +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.nativebinaries.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;
-            }
-
-            // Extra function to ensure library has different size
-            int anotherFunction() {
-                return 1000;
-            }
-        """),
-        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/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy
deleted file mode 100644
index b3a816b..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,88 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-
-class DuplicateAssemblerBaseNamesTestApp extends TestComponent{
-
-    AvailableToolChains.InstalledToolChain toolChain
-
-    DuplicateAssemblerBaseNamesTestApp(AvailableToolChains.InstalledToolChain toolChain) {
-        this.toolChain = toolChain
-    }
-
-    def plugins = ["c", "assembler"]
-
-    @Override
-    List<SourceFile> getSourceFiles() {
-        return  [
-            sourceFile("c", "main.c", """
-            #include <stdio.h>
-
-            // Ensure consistent asm name mapping on all platforms
-            #if !defined(_MSC_VER)
-            extern int sum1(int a, int b) asm("_sum1");
-            extern int sum2(int a, int b) asm("_sum2");
-            #endif
-
-            int main () {
-                printf("foo%dfoo%d", sum1(1, 0), sum2(1, 1));
-                fflush(stdout);
-                return 0;
-            }
-
-            """),
-            sourceFile("asm", "foo1/sum.s", getAsmSource("sum1")),
-            sourceFile("asm", "foo2/sum.s", getAsmSource("sum2"))
-        ]
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        []
-    }
-
-    protected def getAsmSource(String methodName) {
-        if (toolChain.isVisualCpp()) {
-            return """
-.386
-.model    flat
-
-PUBLIC    _${methodName}
-_TEXT     SEGMENT
-_${methodName}    PROC
-mov    eax, DWORD PTR 4[esp]
-add    eax, DWORD PTR 8[esp]
-ret    0
-_${methodName}    ENDP
-_TEXT   ENDS
-END
-"""
-        }else{
-            return """
-.text
-.globl  _${methodName}
-_${methodName}:
-movl    8(%esp), %eax
-addl    4(%esp), %eax
-ret
-"""
-
-        }
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCBaseNamesTestApp.groovy
deleted file mode 100644
index 871f8fa..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-class DuplicateCBaseNamesTestApp extends TestComponent {
-
-    def plugins = ["c"]
-
-    @Override
-    List<SourceFile> getSourceFiles() {
-        [sourceFile("c", "main.c", """
-            #include <stdio.h>
-            #include "foo.h"
-            int main () {
-               foo1();
-               foo2();
-               return 0;
-            }
-        """),
-
-        sourceFile("c/foo1", "foo.c", """
-            #include <stdio.h>
-            #include "foo.h"
-
-            void foo1() {
-                printf("foo1");
-            }
-        """),
-
-        sourceFile("c/foo2", "foo.c", """
-            #include <stdio.h>
-            #include "foo.h"
-
-            void foo2() {
-                printf("foo2");
-            }
-        """)]
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        [sourceFile("headers", "foo.h", """
-           void foo1();
-           void foo2();
-           """)]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCppBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCppBaseNamesTestApp.groovy
deleted file mode 100644
index 061b547..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateCppBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-class DuplicateCppBaseNamesTestApp extends TestComponent {
-
-    def plugins = ["cpp"]
-
-    @Override
-    List<SourceFile> getSourceFiles() {
-        [sourceFile("cpp", "main.cpp", """
-            #include <iostream>
-            #include "foo.h"
-            using namespace std;
-            int main () {
-               foo1();
-               foo2();
-               return 0;
-            }
-        """),
-         sourceFile("cpp/foo1", "foo.cpp", """
-            #include <iostream>
-            #include "foo.h"
-            using namespace std;
-
-            void foo1() {
-                cout << "foo1";
-            }
-        """),
-
-         sourceFile("cpp/foo2", "foo.cpp", """
-            #include <iostream>
-            #include "foo.h"
-            using namespace std;
-
-            void foo2() {
-                cout << "foo2";
-            }
-        """)]
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        [sourceFile("headers", "foo.h", """
-           void foo1();
-           void foo2();
-           """)
-         ]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy
deleted file mode 100644
index 3d8c6f3..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains;
-
-
-// TODO integrate objective-c/cpp we have coverage on windows
-public class DuplicateMixedSameBaseNamesTestApp extends TestComponent {
-
-    AvailableToolChains.InstalledToolChain toolChain
-
-    public DuplicateMixedSameBaseNamesTestApp(AvailableToolChains.InstalledToolChain toolChain) {
-        this.toolChain = toolChain
-    }
-
-    def plugins = ["assembler", "c", "cpp"]
-
-    @Override
-    public List<SourceFile> getSourceFiles() {
-        [sourceFile("c", "main.c", """
-            #include <stdio.h>
-            #include "hello.h"
-
-            // Ensure consistent asm name mapping on all platforms
-            #if !defined(_MSC_VER)
-            extern void sayFooFromAsm() asm("_sayFooFromAsm");
-            #endif
-
-            int main () {
-                sayFooFromC();
-                sayFooFromCpp();
-                //sayFooFromObjectiveC //TODO RG
-                //sayFooFromObjectiveCpp //TODO RG
-                sayFooFromAsm();
-                return 0;
-            }"""),
-
-                sourceFile("c", "foo.c", """
-            #include <stdio.h>
-            #include "hello.h"
-
-            void sayFooFromC() {
-                printf("fooFromC\\n");
-            }
-        """),
-           sourceFile("cpp", "foo.cpp", """
-            #include "hello.h"
-            #include <iostream>
-            using namespace std;
-
-            void sayFooFromCpp() {
-                cout << "fooFromCpp" << std::endl;;
-            }
-
-        """),
-                sourceFile("asm", "foo.s", asmSource())
-        ];
-    }
-
-    String asmSource() {
-        if (toolChain.isVisualCpp()) {
-            return """
-.386
-;.model flat
-.model small,c
-.data
-msg db "fooFromAsm", 10, 0
-.code
-includelib MSVCRT
-extrn printf:near
-extrn exit:NEAR
-public sayFooFromAsm
-sayFooFromAsm proc
-push    offset msg
-call    printf
-mov eax,0
-push eax
-call exit
-sayFooFromAsm endp
-end
-"""
-        } else {
-            return """
-.text
-
-LC0:
-.ascii "fooFromAsm\\12\\0"
-.globl _sayFooFromAsm
-
-_sayFooFromAsm:
-        pushl   %ebp
-        movl    %esp, %ebp
-        subl    \$8, %esp
-        andl    \$-16, %esp
-        movl    \$0, %eax
-        movl    %eax, -4(%ebp)
-        movl    -4(%ebp), %eax
-        movl    \$LC0, (%esp)
-        call    ${(OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) ? '_printf' : 'printf'}
-        movl    \$0, %eax
-        leave
-        ret
-        """
-        }
-    }
-
-    @Override
-    public List<SourceFile> getHeaderFiles() {
-        [sourceFile("headers", "hello.h", """
-            #ifdef __cplusplus
-            extern "C" {
-            #endif
-            void sayFooFromCpp();
-            #ifdef __cplusplus
-            }
-            #endif
-            void sayFooFromC();
-            void sayFooFromAsm();
-            //void sayFooFromObjectiveC();
-            //void sayFooFromObjectiveCpp();
-           """)
-        ]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy
deleted file mode 100644
index 5fc2372..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-
-class DuplicateObjectiveCBaseNamesTestApp extends TestComponent{
-
-    def plugins = ["objective-c"]
-    @Override
-    List<SourceFile> getSourceFiles() {
-        [sourceFile("objc", "main.m", """
-            #import <Foundation/Foundation.h>
-            #import "foo.h"
-
-            int main (int argc, const char * argv[])
-            {
-                sayFoo1();
-                sayFoo2();
-                return 0;
-            }
-        """),
-          fooSource(1),
-          fooSource(2)]
-    }
-
-    public String getExtraConfiguration() {
-        def linkerArgs = OperatingSystem.current().isMacOsX() ? '"-framework", "Foundation"' : '"-lgnustep-base", "-lobjc"'
-        return """
-            binaries.all {
-                if (toolChain in Gcc) {
-                    objcCompiler.args "-I/usr/include/GNUstep", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                }
-
-                if (toolChain in Clang) {
-                    objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                }
-
-                linker.args $linkerArgs
-            }
-        """
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        [sourceFile("headers", "foo.h", """
-            void sayFoo1();
-            void sayFoo2();
-           """)
-        ]
-    }
-
-    SourceFile fooSource(int index) {
-        sourceFile("objc/foo$index", "foo.m", """
-            #import <Foundation/Foundation.h>
-            #import "foo.h"
-
-            void sayFoo$index()
-            {
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [@"foo$index" dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-            }
-        """)
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy
deleted file mode 100644
index d8be9c2..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-
-class DuplicateObjectiveCppBaseNamesTestApp extends TestComponent{
-    def plugins = ["objective-cpp"]
-    @Override
-    List<SourceFile> getSourceFiles() {
-        [sourceFile("objcpp", "main.mm", """
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #import <Foundation/Foundation.h>
-            #import "foo.h"
-
-            int main (int argc, const char * argv[])
-            {
-                sayFoo1();
-                sayFoo2();
-                return 0;
-            }
-        """),
-            sourceFile("objcpp/foo1", "foo.mm", """
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #import <Foundation/Foundation.h>
-            #import "foo.h"
-
-            void sayFoo1()
-            {
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [@"foo1" dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-            }
-        """),
-                sourceFile("objcpp/foo2", "foo.mm", """
-            #import <iostream>
-            #import "foo.h"
-
-            void sayFoo2()
-            {
-                std::cout << "foo2";
-            }
-        """)]
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        [sourceFile("headers", "foo.h", """
-            void sayFoo1();
-            void sayFoo2();
-           """)
-        ]
-    }
-
-    public String getExtraConfiguration() {
-        def linkerArgs = OperatingSystem.current().isMacOsX() ? '"-framework", "Foundation"' : '"-lgnustep-base", "-lobjc"'
-        return """
-            binaries.all {
-                objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                linker.args $linkerArgs
-            }
-        """
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy
deleted file mode 100644
index 8d7d3b4..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-class DuplicateWindowsResourcesBaseNamesTestApp extends TestComponent {
-
-    def plugins = ["cpp","windows-resources"]
-
-    @Override
-    List<SourceFile> getSourceFiles() {
-        [sourceFile("cpp", "main.cpp", """
-#include "hello.h"
-
-int main () {
-    hello();
-    return 0;
-}
-"""),
-         sourceFile("cpp", "hello.cpp", """
-#include <iostream>
-#include <windows.h>
-#include <string>
-#include "hello.h"
-
-std::string LoadStringFromResource(UINT stringID)
-{
-    HINSTANCE instance = GetModuleHandle("hello");
-    WCHAR * pBuf = NULL;
-    int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
-    std::wstring wide = std::wstring(pBuf, len);
-    return std::string(wide.begin(), wide.end());
-}
-
-void DLL_FUNC hello() {
-    std::string foo1 = LoadStringFromResource(IDS_FOO1);
-    std::string foo2 = LoadStringFromResource(IDS_FOO2);
-    std::cout << foo1;
-    std::cout << foo2;
-}
-"""),
-        sourceFile("rc/dir1", "resources.rc", """
-#include "hello.h"
-
-STRINGTABLE
-{
-    IDS_FOO1, "foo1"
-}
-"""),
-        sourceFile("rc/dir2", "resources.rc", """
-#include "hello.h"
-
-STRINGTABLE
-{
-    IDS_FOO2, "foo2"
-}
-""")]
-
-    }
-
-    @Override
-    List<SourceFile> getHeaderFiles() {
-        return [sourceFile("headers", "hello.h", """
-#define IDS_FOO1    111
-#define IDS_FOO2    1000
-
-#ifdef DLL_EXPORT
-#define DLL_FUNC __declspec(dllexport)
-#define MODULE_HANDLE GetModuleHandle("hello")
-#else
-#define DLL_FUNC
-#define MODULE_HANDLE null
-#endif
-
-void DLL_FUNC hello();
-""")]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.groovy
deleted file mode 100644
index 76beee6..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.groovy
+++ /dev/null
@@ -1,56 +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.nativebinaries.language.cpp.fixtures.app
-
-public class ExeWithDiamondDependencyHelloWorldApp extends ExeWithLibraryUsingLibraryHelloWorldApp {
-
-    @Override
-    String getEnglishOutput() {
-        return HELLO_WORLD + " " + HELLO_WORLD + " " + HELLO_WORLD
-    }
-
-    @Override
-    String getFrenchOutput() {
-        return HELLO_WORLD_FRENCH + "\n"
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        sourceFile("cpp", "main.cpp", """
-            #include <iostream>
-            #include "hello.h"
-            #include "greetings.h"
-
-            const char* getExeHello() {
-                #ifdef FRENCH
-                return "${HELLO_WORLD_FRENCH}";
-                #else
-                return "${HELLO_WORLD}";
-                #endif
-            }
-
-            int main () {
-                std::cout << getExeHello() << " ";
-                std::cout << getHello() << " ";
-                sayHello();
-                return 0;
-            }
-        """)
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy
deleted file mode 100644
index 10efa62..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy
+++ /dev/null
@@ -1,132 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.test.fixtures.file.TestFile;
-
-public class ExeWithLibraryUsingLibraryHelloWorldApp extends HelloWorldApp {
-
-    void writeSources(TestFile mainSourceDir, TestFile librarySourceDir, TestFile greetingsLibrarySourceDir) {
-        getExecutable().writeSources(mainSourceDir)
-        getLibrary().writeSources(librarySourceDir)
-        getGreetingsHeader().writeToDir(greetingsLibrarySourceDir);
-        for (SourceFile sourceFile : greetingsSources) {
-            sourceFile.writeToDir(greetingsLibrarySourceDir);
-        }
-    }
-
-
-    public TestComponent getGreetingsLibrary() {
-        return new TestComponent() {
-            @Override
-            public List<SourceFile> getHeaderFiles() {
-                return Arrays.asList(getGreetingsHeader())
-            }
-
-            @Override
-            public List<SourceFile> getSourceFiles() {
-                return greetingsSources
-            }
-        };
-    }
-
-    @Override
-    String getEnglishOutput() {
-        return HELLO_WORLD + " " + HELLO_WORLD
-    }
-
-    @Override
-    String getFrenchOutput() {
-        return HELLO_WORLD_FRENCH + "\n"
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        sourceFile("cpp", "main.cpp", """
-            #include <iostream>
-            #include "hello.h"
-
-            const char* getExeHello() {
-                #ifdef FRENCH
-                return "${HELLO_WORLD_FRENCH}";
-                #else
-                return "${HELLO_WORLD}";
-                #endif
-            }
-
-            int main () {
-                std::cout << getExeHello() << " ";
-                sayHello();
-                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();
-        """);
-    }
-
-    List<SourceFile> librarySources = [
-        sourceFile("cpp", "hello.cpp", """
-            #include <iostream>
-            #include "hello.h"
-            #include "greetings.h"
-
-            void DLL_FUNC sayHello() {
-                std::cout << getHello();
-            }
-        """)
-    ]
-
-    SourceFile getGreetingsHeader() {
-        sourceFile("headers", "greetings.h", """
-            #include <string>
-
-            #ifdef _WIN32
-            #define DLL_FUNC __declspec(dllexport)
-            #else
-            #define DLL_FUNC
-            #endif
-
-            std::string DLL_FUNC getHello();
-        """);
-    }
-
-    List<SourceFile> greetingsSources = [
-        sourceFile("cpp", "greetings.cpp", """
-            #include "greetings.h"
-
-            std::string DLL_FUNC getHello() {
-                #ifdef FRENCH
-                return "${HELLO_WORLD_FRENCH}";
-                #else
-                return "${HELLO_WORLD}";
-                #endif
-            }
-        """)
-    ]
-
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/HelloWorldApp.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/HelloWorldApp.java
deleted file mode 100644
index ba7f9fc..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/HelloWorldApp.java
+++ /dev/null
@@ -1,117 +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.nativebinaries.language.cpp.fixtures.app;
-
-import org.apache.commons.io.FilenameUtils;
-import org.gradle.api.Transformer;
-import org.gradle.util.CollectionUtils;
-
-import java.util.Collections;
-import java.util.List;
-
-public abstract class HelloWorldApp extends TestApp {
-    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 getCustomOutput(String value) {
-        return value + "\n12";
-    }
-
-    public String getExtraConfiguration() {
-        return "";
-    }
-
-    public String getSourceType() {
-        return getMainSource().getPath();
-    }
-
-    public String getSourceExtension() {
-        return FilenameUtils.getExtension(getMainSource().getName());
-    }
-
-    public List<String> getPluginList() {
-        return Collections.singletonList(getNormalizedPluginName());
-    }
-
-    private String getNormalizedPluginName() {
-        return getSourceType().replaceAll("([a-z])([A-Z])", "$1-$2").toLowerCase();
-    }
-
-    public String getPluginScript() {
-        StringBuilder builder = new StringBuilder();
-        for (String plugin : getPluginList()) {
-            builder.append("apply plugin: '").append(plugin).append("'\n");
-        }
-        return builder.toString();
-    }
-
-    public String compilerArgs(String arg) {
-        return compilerConfig("args", arg);
-    }
-
-    public String compilerDefine(String define) {
-        return compilerConfig("define", define);
-    }
-
-    public String compilerDefine(String define, String value) {
-        return compilerConfig("define", define, value);
-    }
-
-    private String compilerConfig(String action, String... args) {
-        String quotedArgs = CollectionUtils.join(",", CollectionUtils.collect(args, new SingleQuotingTransformer()));
-        StringBuilder builder = new StringBuilder();
-        for (String plugin : getPluginList()) {
-            String compilerPrefix = getCompilerPrefix(plugin);
-            if (compilerPrefix == null) {
-                continue;
-            }
-            builder.append(compilerPrefix).append("Compiler.").append(action).append(" ").append(quotedArgs).append("\n");
-        }
-
-        return builder.toString();
-    }
-
-    private String getCompilerPrefix(String plugin) {
-        if (plugin.equals("c")) {
-            return "c";
-        }
-        if (plugin.equals("cpp")) {
-            return "cpp";
-        }
-        if (plugin.equals("objective-c")) {
-            return "objc";
-        }
-        if (plugin.equals("objective-cpp")) {
-            return "objcpp";
-        }
-        return null;
-    }
-
-    private static class SingleQuotingTransformer implements Transformer<Object, String> {
-        public Object transform(String original) {
-            return "'" + original + "'";
-        }
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/IncrementalHelloWorldApp.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/IncrementalHelloWorldApp.java
deleted file mode 100644
index 6783006..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/IncrementalHelloWorldApp.java
+++ /dev/null
@@ -1,49 +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.nativebinaries.language.cpp.fixtures.app;
-
-import java.util.List;
-
-public abstract class IncrementalHelloWorldApp extends HelloWorldApp {
-    public TestApp getAlternate() {
-        return new TestApp() {
-            @Override
-            public SourceFile getMainSource() {
-                return getAlternateMainSource();
-            }
-
-            @Override
-            public SourceFile getLibraryHeader() {
-                return getAlternateLibraryHeader();
-            }
-
-            @Override
-            public List<SourceFile> getLibrarySources() {
-                return getAlternateLibrarySources();
-            }
-        };
-    }
-    private SourceFile getAlternateLibraryHeader() {
-        return getLibraryHeader();
-    }
-
-    public abstract SourceFile getAlternateMainSource();
-    public abstract String getAlternateOutput();
-
-    public abstract List<SourceFile> getAlternateLibrarySources();
-    public abstract String getAlternateLibraryOutput();
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy
deleted file mode 100644
index 914ca88..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedLanguageHelloWorldApp.groovy
+++ /dev/null
@@ -1,143 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains
-
-public class MixedLanguageHelloWorldApp extends HelloWorldApp {
-    private final AvailableToolChains.InstalledToolChain toolChain
-
-    MixedLanguageHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
-        this.toolChain = toolChain
-    }
-
-    @Override
-    List<String> getPluginList() {
-        return ['c', 'cpp', 'assembler']
-    }
-
-    String getExtraConfiguration() {
-        return """
-            model {
-                platforms {
-                    x86 {
-                        architecture "i386"
-                    }
-                }
-            }
-"""
-    }
-
-    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);
-            }
-
-            // Ensure consistent asm name mapping on all platforms
-            #if !defined(_MSC_VER)
-            extern int sumx(int a, int b) asm("_sumx");
-            #endif
-
-            int DLL_FUNC sum(int a, int b) {
-                return sumx(a, b);
-            }
-"""),
-            new SourceFile("asm", "sum.s", getAsmSource())
-        ]
-    }
-
-    protected def getAsmSource() {
-        if (toolChain.isVisualCpp()) {
-            return windowsMasmSource
-        }
-        return i386GnuAsmSource
-    }
-
-    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
-'''
-
-    // TODO:DAZ test 64 bit assembler generation (on all tool chains)
-    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/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedObjectiveCHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedObjectiveCHelloWorldApp.groovy
deleted file mode 100644
index 865d737..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/MixedObjectiveCHelloWorldApp.groovy
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-
-class MixedObjectiveCHelloWorldApp extends HelloWorldApp {
-
-    List pluginList = ["objective-c", "objective-cpp", "c", "cpp"]
-
-    public String getExtraConfiguration() {
-        def linkerArgs = OperatingSystem.current().isMacOsX() ? '"-framework", "Foundation"' : '"-lgnustep-base", "-lobjc"'
-        return """
-            binaries.all {
-                [objcCompiler, objcppCompiler].each{compiler ->
-                    compiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                }
-                linker.args $linkerArgs
-            }
-        """
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        return sourceFile("objc", "main.m", """
-            // Simple hello world app
-            #import "hello.h"
-
-            int main (int argc, const char * argv[]) {
-                doGreeting();
-                printf("%d", sum(7, 5));
-                return 0;
-            }
-
-        """);
-    }
-
-    @Override
-    SourceFile getLibraryHeader() {
-        return sourceFile("headers", "hello.h", """
-            #ifdef __cplusplus
-            extern "C" {
-            #endif
-            int sum(int a, int b);
-            void world();
-            #ifdef __cplusplus
-            }
-            #endif
-            void doGreeting();
-            void hello();
-            void bonjour();
-           """)
-    }
-
-    @Override
-    List<SourceFile> getLibrarySources() {
-        return [
-                sourceFile("objc", "objclib.c", """
-            #import <Foundation/Foundation.h>
-            #include <stdio.h>
-            #include "hello.h"
-
-            void doGreeting()
-            {
-                #ifdef FRENCH
-                bonjour();
-                #else
-                hello();
-                world();
-                #endif
-            }
-
-            void hello()
-            {
-                NSString *hello = @"Hello";
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [hello dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-            }"""),
-
-                sourceFile("c", "clib.c", """
-            #include <stdio.h>
-            #include "hello.h"
-
-            void bonjour() {
-                printf("Bonjour, Monde!\\n");
-            }
-        """),
-                sourceFile("cpp", "cpplib.cpp", """
-            #include "hello.h"
-            #include <iostream>
-            using namespace std;
-
-            void world() {
-                cout << ", World!" << std::endl;
-            }
-        """),
-                sourceFile("objcpp", "sum.mm", """
-            #import "hello.h"
-            int sum(int a, int b) {
-                return a + b;
-            }
-            """)
-        ]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCHelloWorldApp.groovy
deleted file mode 100644
index a1a8cf2..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCHelloWorldApp.groovy
+++ /dev/null
@@ -1,139 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-
-class ObjectiveCHelloWorldApp extends IncrementalHelloWorldApp {
-
-    @Override
-    SourceFile getMainSource() {
-        return sourceFile("objc", "main.m", """
-            // Simple hello world app
-            #import <Foundation/Foundation.h>
-            #include "hello.h"
-
-            int main (int argc, const char * argv[])
-            {
-                sayHello();
-                printf("%d", sum(7, 5));
-                return 0;
-            }
-        """);
-    }
-
-    @Override
-    SourceFile getAlternateMainSource() {
-        return sourceFile("objc", "main.m", """
-            #import <Foundation/Foundation.h>
-            #import "hello.h"
-
-            int main (int argc, const char * argv[])
-            {
-                sayHello();
-                return 0;
-            }
-        """);
-    }
-
-    String alternateOutput = "$HELLO_WORLD\n"
-
-
-    @Override
-    SourceFile getLibraryHeader() {
-        return sourceFile("headers", "hello.h", """
-            int main (int argc, const char * argv[]);
-            int sum(int a, int b);
-            void sayHello();
-        """);
-    }
-
-    @Override
-    List<SourceFile> getLibrarySources() {
-        return [
-                sourceFile("objc", "hello.m", """
-            #import <Foundation/Foundation.h>
-            #import "hello.h"
-
-            void sayHello()
-            {
-                NSString *helloWorld = @"${HELLO_WORLD}\\n";
-                #ifdef FRENCH
-                helloWorld = @"${HELLO_WORLD_FRENCH}\\n";
-                #endif
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-            }
-        """),
-                sourceFile("objc", "sum.m", """
-            #import "hello.h"
-
-            int sum (int a, int b)
-            {
-                return a + b;
-            }
-        """)]
-    }
-
-    @Override
-    List<SourceFile> getAlternateLibrarySources() {
-        return [
-                sourceFile("objc", "hello.m", """
-            #import <Foundation/Foundation.h>
-            #import "hello.h"
-
-            void sayHello()
-            {
-                NSString *helloWorld = @"${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}\\n";
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-            }
-
-            // Extra function to ensure library has different size
-            int anotherFunction() {
-                return 1000;
-            }
-        """),
-                sourceFile("objc", "sum.m", """
-            #import "hello.h"
-
-            int sum (int a, int b)
-            {
-                return a + b;
-            }
-        """)]
-    }
-
-    String alternateLibraryOutput = "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}\n12"
-
-    public String getExtraConfiguration() {
-        def linkerArgs = OperatingSystem.current().isMacOsX() ? '"-framework", "Foundation"' : '"-lgnustep-base", "-lobjc"'
-        return """
-            binaries.all {
-                objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                linker.args $linkerArgs
-            }
-        """
-    }
-
-    @Override
-    List<String> getPluginList() {
-        ['objective-c']
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCppHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCppHelloWorldApp.groovy
deleted file mode 100644
index 2ceb795..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/ObjectiveCppHelloWorldApp.groovy
+++ /dev/null
@@ -1,146 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.internal.os.OperatingSystem
-
-
-class ObjectiveCppHelloWorldApp extends IncrementalHelloWorldApp {
-    @Override
-    SourceFile getMainSource() {
-        return sourceFile("objcpp", "main.mm", """
-            // Simple hello world app
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #import <Foundation/Foundation.h>
-            #include "hello.h"
-
-            int main (int argc, const char * argv[])
-            {
-                sayHello();
-                printf("%d", sum(7, 5));
-                return 0;
-            }
-        """);
-    }
-
-    @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("objcpp", "hello.mm", """
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #include "hello.h"
-            #import <Foundation/Foundation.h>
-
-            #include <iostream>
-
-            #ifdef FRENCH
-            const char* greeting() {
-                return "${HELLO_WORLD_FRENCH}";
-            }
-            #endif
-
-            void DLL_FUNC sayHello() {
-                #ifdef FRENCH
-                std::cout << greeting() << std::endl;
-                #else
-                NSString *helloWorld = @"${HELLO_WORLD}\\n";
-                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-                NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
-                [stdout writeData: strData];
-                #endif
-            }
-        """),
-            sourceFile("objcpp", "sum.mm", """
-            #include "hello.h"
-            int DLL_FUNC sum(int a, int b) {
-                return a + b;
-            }
-        """)
-    ]
-
-    @Override
-    SourceFile getAlternateMainSource() {
-        return sourceFile("objcpp", "main.mm", """
-            // Simple hello world app
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #import <Foundation/Foundation.h>
-            #import <iostream>
-            #import "hello.h"
-
-            int main (int argc, const char * argv[])
-            {
-                std::cout << "${HELLO_WORLD} ${HELLO_WORLD}" << std::endl;
-                return 0;
-            }
-        """);
-    }
-
-    String alternateOutput = "${HELLO_WORLD} ${HELLO_WORLD}\n"
-
-
-    @Override
-    List<SourceFile> getAlternateLibrarySources() {
-        return [
-            sourceFile("objcpp", "hello.mm", """
-            #define __STDC_LIMIT_MACROS
-            #include <stdint.h>
-            #include <iostream>
-            #include "hello.h"
-
-            void DLL_FUNC sayHello() {
-                std::cout << "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}" << std::endl;
-            }
-        """),
-        sourceFile("objcpp", "sum.mm", """
-            #include "hello.h"
-            int DLL_FUNC sum(int a, int b) {
-                return a + b;
-            }
-        """)]
-    }
-
-    String alternateLibraryOutput = "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}\n12"
-
-    public String getExtraConfiguration() {
-        def linkerArgs = OperatingSystem.current().isMacOsX() ? '"-framework", "Foundation"' : '"-lgnustep-base", "-lobjc"'
-        return """
-            binaries.all {
-                objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-                linker.args $linkerArgs
-            }
-        """
-    }
-    @Override
-    List<String> getPluginList() {
-        ['objective-cpp']
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/PlatformDetectingTestApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/PlatformDetectingTestApp.groovy
deleted file mode 100644
index f86522c..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/PlatformDetectingTestApp.groovy
+++ /dev/null
@@ -1,85 +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.nativebinaries.language.cpp.fixtures.app
-
-class PlatformDetectingTestApp extends TestApp {
-    @Override
-    SourceFile getMainSource() {
-        sourceFile("cpp", "main.cpp", """
-#include <iostream>
-using namespace std;
-#include "hello.h"
-
-int main () {
-    ${outputPlatform()}
-
-    outputLibraryPlatform();
-}
-""")
-    }
-
-    @Override
-    SourceFile getLibraryHeader() {
-        sourceFile("headers", "hello.h", """
-#ifdef _WIN32
-#define DLL_FUNC __declspec(dllexport)
-#else
-#define DLL_FUNC
-#endif
-
-void DLL_FUNC outputLibraryPlatform();
-        """);
-    }
-
-
-    List<SourceFile> librarySources = [
-        sourceFile("cpp", "hello.cpp", """
-#include <iostream>
-using namespace std;
-#include "hello.h"
-
-void DLL_FUNC outputLibraryPlatform() {
-    ${outputPlatform()}
-}
-        """)
-    ]
-
-    def outputPlatform() {
-        return """
-    #if defined(__x86_64__) || defined(_M_X64)
-    cout << "amd64";
-    #elif defined(__i386) || defined(_M_IX86)
-    cout << "i386";
-    #elif defined(_M_IA64)
-    cout << "itanium";
-    #else
-    cout << "unknown";
-    #endif
-    cout << " ";
-
-    #if defined(__linux__)
-    cout << "linux";
-    #elif defined(__APPLE__) && defined(__MACH__)
-    cout << "os x";
-    #elif defined(_WIN32) || defined (_WIN64) || defined (__CYGWIN__)
-    cout << "windows";
-    #else
-    cout << "unknown";
-    #endif
-"""
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/SourceFile.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/SourceFile.java
deleted file mode 100644
index eaf42a9..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/SourceFile.java
+++ /dev/null
@@ -1,61 +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.nativebinaries.language.cpp.fixtures.app;
-
-import com.google.common.base.Joiner;
-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);
-        writeToFile(file);
-        return file;
-    }
-
-    public void writeToFile(TestFile file) {
-        if (file.exists()) {
-            file.write("");
-        }
-        file.write(content);
-    }
-
-    public String withPath(String basePath) {
-        return Joiner.on('/').join(basePath, path, name);
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestApp.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestApp.java
deleted file mode 100644
index 8984ae3..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestApp.java
+++ /dev/null
@@ -1,72 +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.nativebinaries.language.cpp.fixtures.app;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-public abstract class TestApp extends TestComponent {
-    public abstract SourceFile getMainSource();
-    public abstract SourceFile getLibraryHeader();
-    public abstract List<SourceFile> getLibrarySources();
-
-    public TestComponent getLibrary() {
-        return new TestComponent() {
-            @Override
-            public List<SourceFile> getSourceFiles() {
-                return getLibrarySources();
-            }
-
-            @Override
-            public List<SourceFile> getHeaderFiles() {
-                return Arrays.asList(getLibraryHeader());
-            }
-        };
-    }
-
-    public TestComponent getExecutable() {
-        return new TestComponent() {
-            @Override
-            public List<SourceFile> getHeaderFiles() {
-                return Collections.emptyList();
-            }
-
-            @Override
-            public List<SourceFile> getSourceFiles() {
-                return Arrays.asList(getMainSource());
-            }
-        };
-    }
-
-    @Override
-    public List<SourceFile> getHeaderFiles() {
-        ArrayList<SourceFile> headerFiles = new ArrayList<SourceFile>();
-        headerFiles.addAll(getExecutable().getHeaderFiles());
-        headerFiles.addAll(getLibrary().getHeaderFiles());
-        return headerFiles;
-    }
-
-    @Override
-    public List<SourceFile> getSourceFiles() {
-        ArrayList<SourceFile> sourceFiles = new ArrayList<SourceFile>();
-        sourceFiles.addAll(getExecutable().getSourceFiles());
-        sourceFiles.addAll(getLibrary().getSourceFiles());
-        return sourceFiles;
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestComponent.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestComponent.groovy
deleted file mode 100644
index a8579bb..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/TestComponent.groovy
+++ /dev/null
@@ -1,39 +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.nativebinaries.language.cpp.fixtures.app
-
-import org.gradle.test.fixtures.file.TestFile
-
-abstract class TestComponent {
-    List<SourceFile> getAllFiles() {
-        return sourceFiles + headerFiles
-    }
-
-    abstract List<SourceFile> getSourceFiles()
-
-    abstract List<SourceFile> getHeaderFiles()
-
-    protected SourceFile sourceFile(String path, String name, String content) {
-        return new SourceFile(path, name, content);
-    }
-
-    public void writeSources(TestFile sourceDir) {
-        for (SourceFile srcFile : allFiles) {
-            srcFile.writeToDir(sourceDir)
-        }
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/WindowsResourceHelloWorldApp.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/WindowsResourceHelloWorldApp.groovy
deleted file mode 100644
index 2c9b0ab..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/app/WindowsResourceHelloWorldApp.groovy
+++ /dev/null
@@ -1,129 +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.nativebinaries.language.cpp.fixtures.app
-
-class WindowsResourceHelloWorldApp extends HelloWorldApp {
-    @Override
-    String getEnglishOutput() {
-        return HELLO_WORLD
-    }
-
-    @Override
-    String getFrenchOutput() {
-        return HELLO_WORLD_FRENCH
-    }
-
-    @Override
-    List<String> getPluginList() {
-        ['cpp', 'windows-resources']
-    }
-
-    @Override
-    String getExtraConfiguration() {
-        return """
-            binaries.all {
-                linker.args "user32.lib"
-            }
-            binaries.withType(SharedLibraryBinary) {
-                cppCompiler.define "DLL_EXPORT"
-            }
-"""
-    }
-
-    @Override
-    String compilerArgs(String arg) {
-        "rcCompiler.args '${arg}'"
-    }
-
-    @Override
-    String compilerDefine(String define) {
-        "rcCompiler.define '${define}'"
-    }
-
-    @Override
-    String compilerDefine(String define, String value) {
-        "rcCompiler.define '${define}', '${value}'"
-    }
-
-    @Override
-    SourceFile getMainSource() {
-        return sourceFile("cpp", "main.cpp", """
-#include "hello.h"
-
-int main () {
-    hello();
-    return 0;
-}
-""");
-    }
-
-    @Override
-    SourceFile getLibraryHeader() {
-        return sourceFile("headers", "hello.h", """
-#define IDS_HELLO    111
-
-#ifdef DLL_EXPORT
-#define DLL_FUNC __declspec(dllexport)
-#define MODULE_HANDLE GetModuleHandle("hello")
-#else
-#define DLL_FUNC
-#define MODULE_HANDLE null
-#endif
-
-void DLL_FUNC hello();
-""");
-    }
-
-    List<SourceFile> librarySources = [
-        sourceFile("cpp", "hello.cpp", """
-#include <iostream>
-#include <windows.h>
-#include <string>
-#include "hello.h"
-
-std::string LoadStringFromResource(UINT stringID)
-{
-    HINSTANCE instance = GetModuleHandle("hello");
-    WCHAR * pBuf = NULL;
-    int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
-    std::wstring wide = std::wstring(pBuf, len);
-    return std::string(wide.begin(), wide.end());
-}
-
-void DLL_FUNC hello() {
-    std::string hello = LoadStringFromResource(IDS_HELLO);
-    std::cout << hello;
-}
-"""),
-        sourceFile("rc", "resources.rc", """
-#include "hello.h"
-
-STRINGTABLE
-{
-    #ifdef FRENCH
-    IDS_HELLO, "${HELLO_WORLD_FRENCH}"
-    #else
-    IDS_HELLO, "${HELLO_WORLD}"
-    #endif
-}
-""")
-    ]
-
-    List<SourceFile> getResourceSources() {
-        getLibrarySources().subList(1, 2)
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/BinaryInfo.java b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/BinaryInfo.java
deleted file mode 100644
index 8959726..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/BinaryInfo.java
+++ /dev/null
@@ -1,28 +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.nativebinaries.language.cpp.fixtures.binaryinfo;
-
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal;
-
-import java.util.List;
-
-public interface BinaryInfo {
-    ArchitectureInternal getArch();
-    List<String> listObjectFiles();
-    List<String> listLinkedLibraries();
-    String getSoName();
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/DumpbinBinaryInfo.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/DumpbinBinaryInfo.groovy
deleted file mode 100644
index 0405181..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/DumpbinBinaryInfo.groovy
+++ /dev/null
@@ -1,99 +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.nativebinaries.language.cpp.fixtures.binaryinfo
-
-import net.rubygrapefruit.platform.SystemInfo
-import net.rubygrapefruit.platform.WindowsRegistry
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.nativebinaries.language.cpp.fixtures.AvailableToolChains.InstalledToolChain
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.platform.internal.DefaultArchitecture
-import org.gradle.nativebinaries.platform.internal.DefaultPlatform
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.DefaultVisualStudioLocator
-import org.gradle.nativebinaries.toolchain.internal.msvcpp.VisualStudioInstall
-
-class DumpbinBinaryInfo implements BinaryInfo {
-    final File binaryFile
-    final File vcBin
-    final String vcPath
-
-    DumpbinBinaryInfo(File binaryFile, InstalledToolChain tc) {
-        this.binaryFile = binaryFile
-
-        VisualStudioInstall vsInstall = findVisualStudio()
-        DefaultPlatform targetPlatform = new DefaultPlatform("default");
-        vcBin = vsInstall.getVisualCpp().getBinaryPath(targetPlatform)
-        vcPath = vsInstall.getVisualCpp().getPath(targetPlatform).join(';')
-    }
-
-    static VisualStudioInstall findVisualStudio() {
-        def vsLocator = new DefaultVisualStudioLocator(OperatingSystem.current(), NativeServices.instance.get(WindowsRegistry), NativeServices.instance.get(SystemInfo))
-        return vsLocator.locateVisualStudioInstalls(null).visualStudio
-    }
-
-    private findExe(String exe) {
-        final candidate = new File(vcBin, exe)
-        if (candidate.exists()) {
-            return candidate
-        }
-        throw new RuntimeException("dumpbin.exe not found")
-    }
-
-    private String getDumpbinHeaders() {
-        def dumpbin = findExe("dumpbin.exe")
-        def process = [dumpbin.absolutePath, '/HEADERS', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
-        return process.inputStream.text
-    }
-
-    def static readArch(def input) {
-        def pattern = /(?m)^.* machine \((.*)\).*$/
-        return (input =~ pattern).findResult { line, group ->
-            group.trim()
-        }
-    }
-
-    ArchitectureInternal getArch() {
-        def archString = readArch(dumpbinHeaders)
-        switch (archString) {
-            case "x86":
-                return new DefaultArchitecture("x86", ArchitectureInternal.InstructionSet.X86, 32)
-            case "x64":
-                return new DefaultArchitecture("x86_64", ArchitectureInternal.InstructionSet.X86, 64)
-            case "IA64":
-                return new DefaultArchitecture("ia-64", ArchitectureInternal.InstructionSet.ITANIUM, 64)
-            default:
-                throw new RuntimeException("Cannot determine architecture for ${archString}")
-        }
-    }
-
-    List<String> listObjectFiles() {
-        def dumpbin = findExe("lib.exe")
-        def process = [dumpbin.absolutePath, '/LIST', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
-        return process.inputStream.readLines().drop(3).collect { new File(it).name }
-    }
-
-    List<String> listLinkedLibraries() {
-        def dumpbin = findExe("dumpbin.exe")
-        def process = [dumpbin.absolutePath, '/IMPORTS', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
-        return process.inputStream.readLines()
-    }
-
-    String getSoName() {
-        throw new UnsupportedOperationException("soname is not relevant on windows")
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/OtoolBinaryInfo.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/OtoolBinaryInfo.groovy
deleted file mode 100644
index 6152c2d..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/OtoolBinaryInfo.groovy
+++ /dev/null
@@ -1,59 +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.nativebinaries.language.cpp.fixtures.binaryinfo
-
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.platform.internal.DefaultArchitecture
-
-class OtoolBinaryInfo implements BinaryInfo {
-    def binaryFile
-
-    OtoolBinaryInfo(File binaryFile) {
-        this.binaryFile = binaryFile    }
-
-    ArchitectureInternal getArch() {
-        def process = ['otool', '-hv', binaryFile.absolutePath].execute()
-        def lines = process.inputStream.readLines()
-        def archString = lines[3].split()[1]
-
-        switch (archString) {
-            case "I386":
-                return new DefaultArchitecture("x86", ArchitectureInternal.InstructionSet.X86, 32)
-            case "X86_64":
-                return new DefaultArchitecture("x86_64", ArchitectureInternal.InstructionSet.X86, 64)
-            default:
-                throw new RuntimeException("Cannot determine architecture for ${archString}")
-        }
-    }
-
-    List<String> listObjectFiles() {
-        def process = ['ar', '-t', binaryFile.getAbsolutePath()].execute()
-        return process.inputStream.readLines().drop(1)
-    }
-
-    List<String> listLinkedLibraries() {
-        def process = ['otool', '-L', binaryFile.absolutePath].execute()
-        def lines = process.inputStream.readLines()
-        return lines
-    }
-
-    String getSoName() {
-        def process = ['otool', '-D', binaryFile.absolutePath].execute()
-        def lines = process.inputStream.readLines()
-        return lines[1]
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfo.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfo.groovy
deleted file mode 100644
index 20b9505..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/language/cpp/fixtures/binaryinfo/ReadelfBinaryInfo.groovy
+++ /dev/null
@@ -1,82 +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.nativebinaries.language.cpp.fixtures.binaryinfo
-import org.gradle.nativebinaries.platform.internal.ArchitectureInternal
-import org.gradle.nativebinaries.platform.internal.DefaultArchitecture
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-class ReadelfBinaryInfo implements BinaryInfo {
-
-    private final File binaryFile
-
-    ReadelfBinaryInfo(File binaryFile) {
-        this.binaryFile = binaryFile
-    }
-
-    public static String readHeaderValue(List<String> lines, String header) {
-        String matchingLine = lines.find {
-            it.trim().startsWith(header)
-        }
-        return matchingLine != null ? matchingLine.replaceFirst(header, "").trim() : null
-    }
-
-    ArchitectureInternal getArch() {
-        def process = ['readelf', '-h', binaryFile.absolutePath].execute()
-        List<String> lines = process.inputStream.readLines()
-        def archString = readHeaderValue(lines, "Machine:")
-        switch (archString) {
-            case "Intel 80386":
-                return new DefaultArchitecture("x86", ArchitectureInternal.InstructionSet.X86, 32)
-            case "Advanced Micro Devices X86-64":
-                return new DefaultArchitecture("x86_64", ArchitectureInternal.InstructionSet.X86, 64)
-            default:
-                throw new RuntimeException("Cannot determine architecture for ${archString}")
-        }
-    }
-
-    List<String> listObjectFiles() {
-        def process = ['ar', '-t', binaryFile.getAbsolutePath()].execute()
-        return process.inputStream.readLines()
-    }
-
-    List<String> listLinkedLibraries() {
-        def process = ['readelf', '-d', binaryFile.absolutePath].execute()
-        def lines = process.inputStream.readLines()
-        return lines
-    }
-
-    String getSoName() {
-        def process = ['readelf', '-d', binaryFile.absolutePath].execute()
-        List<String> lines = process.inputStream.readLines()
-        return readSoName(lines)
-    }
-
-    static String readSoName(List<String> lines) {
-        final Pattern pattern = ~/^.*\(SONAME\)\s+Library soname\: \[(.*)\]$/
-        String matchingLine = lines.find {
-            pattern.matcher(it).matches()
-        }
-        if (matchingLine == null) {
-            return null;
-        }
-        final Matcher matcher = pattern.matcher(matchingLine)
-        assert matcher.matches()
-        return matcher.group(1)
-    }
-}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestResults.groovy b/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestResults.groovy
deleted file mode 100644
index ce4f3a3..0000000
--- a/subprojects/cpp/src/testFixtures/groovy/org/gradle/nativebinaries/test/cunit/CUnitTestResults.groovy
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.nativebinaries.test.cunit
-
-import org.gradle.test.fixtures.file.TestFile
-
-class CUnitTestResults {
-    TestFile testResultsFile
-    Node resultsNode
-    Map<String, Suite> suites = [:]
-    Map<String, SummaryRecord> summaryRecords = [:]
-
-    CUnitTestResults(TestFile testResultsFile) {
-        assert testResultsFile.exists()
-        this.testResultsFile = testResultsFile
-        final XmlParser parser = new XmlParser(false, false)
-        parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
-        parser.setFeature("http://xml.org/sax/features/namespaces", false)
-        this.resultsNode = parser.parse(testResultsFile)
-
-        suiteNames.each { String name ->
-            Node suiteNode = resultsNode.getAt('CUNIT_RESULT_LISTING').CUNIT_RUN_SUITE.CUNIT_RUN_SUITE_SUCCESS.find({it.SUITE_NAME.text() == name}) as Node
-            suites.put(name, new Suite(suiteNode))
-        }
-
-        summaryRecords['Suites'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Suites'}) as Node)
-        summaryRecords['Test Cases'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Test Cases'}) as Node)
-        summaryRecords['Assertions'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Assertions'}) as Node)
-    }
-
-    List<String> getSuiteNames() {
-        resultsNode.getAt('CUNIT_RESULT_LISTING').CUNIT_RUN_SUITE.CUNIT_RUN_SUITE_SUCCESS.SUITE_NAME*.text()
-    }
-
-    def checkTestCases(int total, int succeeded, int failed) {
-        checkSummaryRecord('Test Cases', total, succeeded, failed)
-    }
-
-    def checkAssertions(int total, int succeeded, int failed) {
-        checkSummaryRecord('Assertions', total, succeeded, failed)
-    }
-
-    private def checkSummaryRecord(String name, int total, int succeeded, int failed) {
-        def recordNode = resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == name}) as Node
-        assert recordNode.RUN.text() as int == total
-        assert recordNode.SUCCEEDED.text() as int == succeeded
-        assert recordNode.FAILED.text() as int == failed
-        return true
-    }
-
-    class Suite {
-        final Node suiteNode
-
-        Suite(Node suiteNode) {
-            this.suiteNode = suiteNode
-        }
-
-        String getName() {
-            return suiteNode.SUITE_NAME.text()
-        }
-
-        List<String> getPassingTests() {
-            suiteNode.CUNIT_RUN_TEST_RECORD.CUNIT_RUN_TEST_SUCCESS.TEST_NAME*.text()
-        }
-
-        List<String> getFailingTests() {
-            suiteNode.CUNIT_RUN_TEST_RECORD.CUNIT_RUN_TEST_FAILURE.TEST_NAME*.text().unique()
-        }
-    }
-
-    class SummaryRecord {
-        final Node recordNode
-
-        SummaryRecord(Node recordNode) {
-            this.recordNode = recordNode
-        }
-
-        int getRun() {
-            recordNode.RUN.text() as int
-        }
-
-        int getSucceeded() {
-            recordNode.SUCCEEDED.text() as int
-        }
-
-        int getFailed() {
-            recordNode.FAILED.text() as int
-        }
-
-    }
-}
diff --git a/subprojects/dependency-management/dependency-management.gradle b/subprojects/dependency-management/dependency-management.gradle
new file mode 100644
index 0000000..d3e50de
--- /dev/null
+++ b/subprojects/dependency-management/dependency-management.gradle
@@ -0,0 +1,88 @@
+apply plugin: "groovy"
+
+import org.gradle.build.JarJar
+
+configurations {
+    mvn3Input
+}
+
+repositories {
+    jcenter()
+    mavenCentral()
+}
+
+dependencies {
+    compile project(":core")
+    compile project(":resources")
+    compile libraries.commons_lang
+    compile libraries.commons_io
+    compile libraries.ivy
+    compile libraries.slf4j_api
+    compile libraries.gson
+    runtime libraries.xbean //maven3 classes dependency
+    runtime libraries.bouncycastle_provider
+
+    compile fileTree("$buildDir/libs/jarjar") {
+        builtBy 'jarJarMaven3'
+    }
+
+    mvn3Input libraries.maven3
+
+    testCompile libraries.groovy
+
+    integTestRuntime project(":ivy")
+    integTestRuntime project(":maven")
+    integTestRuntime project(":resourcesHttp")
+    integTestRuntime project(":resourcesS3")
+    integTestRuntime project(":resourcesSftp")
+    //this dependency is necessary to run IvySFtpResolverIntegrationTest on ibm jdk
+    //integTestRuntime "org.bouncycastle:bcprov-jdk15:1.46 at jar"
+
+    testFixturesCompile project(":internalIntegTesting")
+}
+
+task jarJarMaven3(type: JarJar) {
+    inputJars = configurations.mvn3Input
+    outputDir = file("$buildDir/libs/jarjar")
+
+    //unfortunately, all those need to be jarjarred.
+    // Even if some library (like aether) is not included in maven-ant-tasks it has
+    // META-INF/plexus/components.xml that to jarjarred components.
+    rule('org.apache.maven.**', 'org.gradle.mvn3.org.apache.maven. at 1')
+    rule('org.codehaus.**', 'org.gradle.mvn3.org.codehaus. at 1')
+    rule('org.sonatype.**', 'org.gradle.mvn3.org.sonatype. at 1')
+
+    avoidConflictingPlexusComponents(it)
+}
+
+if (isWindows && javaVersion.java5) {
+    compileTestGroovy.options.fork(memoryMaximumSize: '512m')
+}
+
+classpathManifest.dependsOn jarJarMaven3 //see GRADLE-2521
+
+//adding explicit task dependencies due to https://issues.gradle.org/browse/GRADLE-2481
+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).
+    //The implementation is the same but the 'hint' is different and this makes plexus fail to start.
+    //I'm removing the components.xml file from the sec-dispatcher jar.
+    //This file contains only single component so I think we can remove it.
+    task.doLast {
+        def plexusSec = "$outputDir/jarjar-plexus-sec-dispatcher-1.3.jar"
+        def plexusSecNoComps = "$plexusSec-noComps"
+        ant {
+            zip(destfile: plexusSecNoComps, update: true) {
+                zipfileset(src: plexusSec) {
+                    exclude(name: 'META-INF/plexus/components.xml')
+                }
+            }
+            move(file: plexusSecNoComps, tofile: plexusSec)
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDeclarationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDeclarationIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDeclarationIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDeclarationIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..76ba9ac
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
@@ -0,0 +1,691 @@
+/*
+ * 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.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.file.TestFile
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import spock.lang.Issue
+
+import static org.hamcrest.Matchers.containsString
+
+class ArtifactDependenciesIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources(testDirectoryProvider)
+
+    @Before
+    public void setup() {
+        executer.requireOwnGradleUserHomeDir()
+    }
+
+    @Test
+    public void canHaveConfigurationHierarchy() {
+        File buildFile = testFile("projectWithConfigurationHierarchy.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void dependencyReportWithConflicts() {
+        File buildFile = testFile("projectWithConflicts.gradle");
+        usingBuildFile(buildFile).run();
+        usingBuildFile(buildFile).withDependencyList().run();
+    }
+
+    @Test
+    public void canNestModules() throws IOException {
+        File buildFile = testFile("projectWithNestedModules.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canHaveCycleInDependencyGraph() throws IOException {
+        File buildFile = testFile("projectWithCyclesInDependencyGraph.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void canUseDynamicVersions() throws IOException {
+        File buildFile = testFile("projectWithDynamicVersions.gradle");
+        usingBuildFile(buildFile).run();
+    }
+
+    @Test
+    public void resolutionFailsWhenProjectHasNoRepositoriesEvenWhenArtifactIsCachedLocally() {
+        testFile('settings.gradle') << 'include "a", "b"'
+        testFile('build.gradle') << """
+subprojects {
+    configurations {
+        compile
+    }
+    task listDeps << { configurations.compile.each { } }
+}
+project(':a') {
+    repositories {
+        maven { url '${repo.uri}' }
+    }
+    dependencies {
+        compile 'org.gradle.test:external1:1.0'
+    }
+}
+project(':b') {
+    dependencies {
+        compile 'org.gradle.test:external1:1.0'
+    }
+}
+"""
+        repo.module('org.gradle.test', 'external1', '1.0').publish()
+
+        inTestDirectory().withTasks('a:listDeps').run()
+        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
+        result.assertHasCause('Cannot resolve external dependency org.gradle.test:external1:1.0 because no repositories are defined.')
+    }
+
+    @Test
+    public void resolutionFailsForMissingArtifact() {
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    compile; missingExt; missingClassifier
+}
+dependencies {
+    compile "org.gradle.test:lib:1.0"
+    missingExt "org.gradle.test:lib:1.0 at zip"
+    missingClassifier "org.gradle.test:lib:1.0:classifier1"
+}
+task listJar << { configurations.compile.each { } }
+task listMissingExt << { configurations.missingExt.each { } }
+task listMissingClassifier << { configurations.missingClassifier.each { } }
+"""
+
+        def module = repo.module('org.gradle.test', 'lib', '1.0')
+        module.publish()
+
+        inTestDirectory().withTasks('listJar').run()
+
+        def result = inTestDirectory().withTasks('listMissingExt').runWithFailure()
+        result.assertHasCause("""Could not find lib.zip (org.gradle.test:lib:1.0).
+Searched in the following locations:
+    ${module.artifactFile(type: 'zip').toURL()}""")
+
+        result = inTestDirectory().withTasks('listMissingClassifier').runWithFailure()
+        result.assertHasCause("""Could not find lib-classifier1.jar (org.gradle.test:lib:1.0).
+Searched in the following locations:
+    ${module.artifactFile(classifier: 'classifier1').toURL()}""")
+    }
+
+    @Test
+    @Issue("GRADLE-1342")
+    public void resolutionDoesNotUseCachedArtifactFromDifferentRepository() {
+        def repo1 = maven('repo1')
+        repo1.module('org.gradle.test', 'external1', '1.0').publish()
+        def repo2 = maven('repo2')
+
+        testFile('settings.gradle') << 'include "a", "b"'
+        testFile('build.gradle') << """
+subprojects {
+    configurations {
+        compile
+    }
+    task listDeps << { configurations.compile.each { } }
+}
+project(':a') {
+    repositories {
+        maven { url '${repo1.uri}' }
+    }
+    dependencies {
+        compile 'org.gradle.test:external1:1.0'
+    }
+}
+project(':b') {
+    repositories {
+        maven { url '${repo2.uri}' }
+    }
+    dependencies {
+        compile 'org.gradle.test:external1:1.0'
+    }
+}
+"""
+
+        inTestDirectory().withTasks('a:listDeps').run()
+        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
+        result.assertThatCause(containsString('Could not find org.gradle.test:external1:1.0.'))
+    }
+
+    @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')
+        module.artifact(classifier: 'classifier')
+        module.publish()
+        repo.module('org.gradle.test', 'dist', '1.0').hasType('zip').publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile "org.gradle.test:lib:1.0"
+    compile "org.gradle.test:lib:1.0:classifier"
+    compile "org.gradle.test:lib:1.0 at zip"
+    compile "org.gradle.test:dist:1.0"
+}
+task test << {
+    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'
+    assert artifacts[0].type == 'jar'
+    assert artifacts[0].extension == 'jar'
+    assert artifacts[0].classifier == null
+    assert artifacts[1].name == 'lib'
+    assert artifacts[1].type == 'jar'
+    assert artifacts[1].extension == 'jar'
+    assert artifacts[1].classifier == 'classifier'
+    assert artifacts[2].name == 'lib'
+    assert artifacts[2].type == 'zip'
+    assert artifacts[2].extension == 'zip'
+    assert artifacts[2].classifier == null
+    assert artifacts[3].name == 'dist'
+    assert artifacts[3].type == 'zip'
+    assert artifacts[3].extension == 'zip'
+    assert artifacts[3].classifier == null
+}
+"""
+
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    @Issue("GRADLE-1567")
+    public void resolutionDifferentiatesBetweenArtifactsThatDifferOnlyInClassifier() {
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.artifact(classifier: 'classifier1')
+        module.artifact(classifier: 'classifier2')
+        module.publish()
+
+        testFile('settings.gradle') << 'include "a", "b", "c"'
+        testFile('build.gradle') << """
+subprojects {
+    repositories {
+        maven { url '${repo.uri}' }
+    }
+    configurations {
+        compile
+    }
+}
+project(':a') {
+    dependencies {
+        compile 'org.gradle.test:external1:1.0:classifier1'
+    }
+    task test(dependsOn: configurations.compile) << {
+        assert configurations.compile.collect { it.name } == ['external1-1.0-classifier1.jar']
+        assert configurations.compile.resolvedConfiguration.resolvedArtifacts.collect { "\${it.name}-\${it.classifier}" } == ['external1-classifier1']
+    }
+}
+project(':b') {
+    dependencies {
+        compile 'org.gradle.test:external1:1.0:classifier2'
+    }
+    task test(dependsOn: configurations.compile) << {
+        assert configurations.compile.collect { it.name } == ['external1-1.0-classifier2.jar']
+        assert configurations.compile.resolvedConfiguration.resolvedArtifacts.collect { "\${it.name}-\${it.classifier}" } == ['external1-classifier2']
+    }
+}
+"""
+
+        inTestDirectory().withTasks('a:test').run()
+        inTestDirectory().withTasks('b:test').run()
+    }
+
+    @Test
+    @Issue("GRADLE-739")
+    public void singleConfigurationCanContainMultipleArtifactsThatOnlyDifferByClassifier() {
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.artifact(classifier: 'baseClassifier')
+        module.artifact(classifier: 'extendedClassifier')
+        module.publish()
+        repo.module('org.gradle.test', 'other', '1.0').publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    base
+    extendedWithClassifier.extendsFrom base
+    extendedWithOther.extendsFrom base
+    justDefault
+    justClassifier
+    rawBase
+    rawExtended.extendsFrom rawBase
+    cBase
+    cExtended.extendsFrom cBase
+}
+dependencies {
+    base 'org.gradle.test:external1:1.0'
+    base 'org.gradle.test:external1:1.0:baseClassifier'
+    extendedWithClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
+    extendedWithOther 'org.gradle.test:other:1.0'
+    justDefault 'org.gradle.test:external1:1.0'
+    justClassifier 'org.gradle.test:external1:1.0:baseClassifier'
+    justClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
+    rawBase 'org.gradle.test:external1:1.0'
+    rawExtended 'org.gradle.test:external1:1.0:extendedClassifier'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config.collect({ it.name }) as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar']
+    checkDeps configurations.extendedWithOther, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
+    checkDeps configurations.extendedWithClassifier, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+    checkDeps configurations.justDefault, ['external1-1.0.jar']
+    checkDeps configurations.justClassifier, ['external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+    checkDeps configurations.rawBase, ['external1-1.0.jar']
+    checkDeps configurations.rawExtended, ['external1-1.0.jar', 'external1-1.0-extendedClassifier.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    @Issue("GRADLE-739")
+    public void canUseClassifiersCombinedWithArtifactWithNonStandardPackaging() {
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.artifact(type: 'txt')
+        module.artifact(classifier: 'baseClassifier', type: 'jar')
+        module.artifact(classifier: 'extendedClassifier', type: 'jar')
+        module.hasType('zip')
+        module.publish()
+        repo.module('org.gradle.test', 'other', '1.0').publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    base
+    extended.extendsFrom base
+    extendedWithClassifier.extendsFrom base
+    extendedWithType.extendsFrom base
+}
+dependencies {
+    base 'org.gradle.test:external1:1.0'
+    base 'org.gradle.test:external1:1.0:baseClassifier'
+    extended 'org.gradle.test:other:1.0'
+    extendedWithClassifier 'org.gradle.test:external1:1.0:extendedClassifier'
+    extendedWithType 'org.gradle.test:external1:1.0 at txt'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config.collect({ it.name }) as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.base, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar']
+    checkDeps configurations.extended, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
+    checkDeps configurations.extendedWithClassifier, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+    checkDeps configurations.extendedWithType, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0.txt']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    @Issue("GRADLE-739")
+    public void configurationCanContainMultipleArtifactsThatOnlyDifferByType() {
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.artifact(type: 'zip')
+        module.artifact(classifier: 'classifier')
+        module.artifact(classifier: 'classifier', type: 'bin')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    base
+    extended.extendsFrom base
+    extended2.extendsFrom base
+}
+dependencies {
+    base 'org.gradle.test:external1:1.0'
+    base 'org.gradle.test:external1:1.0 at zip'
+    extended 'org.gradle.test:external1:1.0:classifier'
+    extended2 'org.gradle.test:external1:1.0:classifier at bin'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config.collect({ it.name }) as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0.zip']
+    checkDeps configurations.extended, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.jar']
+    checkDeps configurations.extended2, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.bin']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void "dependencies that are excluded by a dependency are not retrieved"() {
+        repo.module('org.gradle.test', 'one', '1.0').publish()
+        repo.module('org.gradle.test', 'two', '1.0').publish()
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.dependsOn('org.gradle.test', 'one', '1.0')
+        module.artifact(classifier: 'classifier')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    reference
+    excluded
+    extendedExcluded.extendsFrom excluded
+    excludedWithClassifier
+}
+dependencies {
+    reference 'org.gradle.test:external1:1.0'
+    excluded 'org.gradle.test:external1:1.0', { exclude module: 'one' }
+    extendedExcluded 'org.gradle.test:two:1.0'
+    excludedWithClassifier 'org.gradle.test:external1:1.0', { exclude module: 'one' }
+    excludedWithClassifier 'org.gradle.test:external1:1.0:classifier', { exclude module: 'one' }
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config*.name as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.reference, ['external1-1.0.jar', 'one-1.0.jar']
+    checkDeps configurations.excluded, ['external1-1.0.jar']
+    checkDeps configurations.extendedExcluded, ['external1-1.0.jar', 'two-1.0.jar']
+    checkDeps configurations.excludedWithClassifier, ['external1-1.0.jar', 'external1-1.0-classifier.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void "dependencies that are globally excluded are not retrieved"() {
+        repo.module('org.gradle.test', 'direct', '1.0').publish()
+        repo.module('org.gradle.test', 'transitive', '1.0').publish()
+        def module = repo.module('org.gradle.test', 'external', '1.0')
+        module.dependsOn('org.gradle.test', 'transitive', '1.0')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    excluded {
+        exclude module: 'direct'
+        exclude module: 'transitive'
+    }
+    extendedExcluded.extendsFrom excluded
+}
+dependencies {
+    excluded 'org.gradle.test:external:1.0'
+    excluded 'org.gradle.test:direct:1.0'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config*.name as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.excluded, ['external-1.0.jar']
+    checkDeps configurations.extendedExcluded, ['external-1.0.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void "does not attempt to resolve an excluded dependency"() {
+        def module = repo.module('org.gradle.test', 'external', '1.0')
+        module.dependsOn('org.gradle.test', 'unknown1', '1.0')
+        module.dependsOn('org.gradle.test', 'unknown2', '1.0')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    excluded {
+        exclude module: 'unknown2'
+    }
+}
+dependencies {
+    excluded 'org.gradle.test:external:1.0', { exclude module: 'unknown1' }
+    excluded 'org.gradle.test:unknown2:1.0'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config*.name as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.excluded, ['external-1.0.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    @Issue("GRADLE-3124")
+    public void "typo in configuration excludes is detected"() {
+        testFile("build.gradle") << """
+            configurations { foo }
+            configurations.foo.exclude group: 'kafka', modue: 'kafka'
+        """
+
+        expect:
+        def failure = inTestDirectory().runWithFailure()
+        failure.assertHasCause("No such property: modue")
+    }
+
+    @Test
+    public void nonTransitiveDependenciesAreNotRetrieved() {
+        repo.module('org.gradle.test', 'one', '1.0').publish()
+        repo.module('org.gradle.test', 'two', '1.0').publish()
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.dependsOn('org.gradle.test', 'one', '1.0')
+        module.artifact(classifier: 'classifier')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    transitive
+    nonTransitive
+    extendedNonTransitive.extendsFrom nonTransitive
+    extendedBoth.extendsFrom transitive, nonTransitive
+    mergedNonTransitive
+}
+dependencies {
+    transitive 'org.gradle.test:external1:1.0'
+    nonTransitive 'org.gradle.test:external1:1.0', { transitive = false }
+    extendedNonTransitive 'org.gradle.test:two:1.0'
+    mergedNonTransitive 'org.gradle.test:external1:1.0', {transitive = false }
+    mergedNonTransitive 'org.gradle.test:external1:1.0:classifier', { transitive = false }
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config.collect({ it.name }) as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.transitive, ['external1-1.0.jar', 'one-1.0.jar']
+    checkDeps configurations.nonTransitive, ['external1-1.0.jar']
+    checkDeps configurations.extendedNonTransitive, ['external1-1.0.jar', 'two-1.0.jar']
+    checkDeps configurations.extendedBoth, ['external1-1.0.jar', 'one-1.0.jar']
+    checkDeps configurations.mergedNonTransitive, ['external1-1.0.jar', 'external1-1.0-classifier.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void "configuration transitive = false overrides dependency transitive flag"() {
+        repo.module('org.gradle.test', 'one', '1.0').publish()
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.dependsOn('org.gradle.test', 'one', '1.0')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    override { transitive = false }
+}
+dependencies {
+    override 'org.gradle.test:external1:1.0'
+}
+
+task test << {
+    assert configurations.override.collect { it.name } == ['external1-1.0.jar']
+}
+"""
+
+        inTestDirectory().withTasks('test').run()
+    }
+
+    /*
+     * Originally, we were aliasing dependency descriptors that were identical. This caused alias errors when we subsequently modified one of these descriptors.
+     */
+
+    @Test
+    public void addingClassifierToDuplicateDependencyDoesNotAffectOriginal() {
+        def module = repo.module('org.gradle.test', 'external1', '1.0')
+        module.artifact(classifier: 'withClassifier')
+        module.publish()
+
+        testFile('build.gradle') << """
+repositories {
+    maven { url '${repo.uri}' }
+}
+configurations {
+    a
+    b
+}
+dependencies {
+    a 'org.gradle.test:external1:1.0'
+    b 'org.gradle.test:external1:1.0', 'org.gradle.test:external1:1.0:withClassifier'
+}
+
+def checkDeps(config, expectedDependencies) {
+    assert config.collect({ it.name }) as Set == expectedDependencies as Set
+}
+
+task test << {
+    checkDeps configurations.a, ['external1-1.0.jar']
+    checkDeps configurations.b, ['external1-1.0-withClassifier.jar', 'external1-1.0.jar']
+}
+"""
+        inTestDirectory().withTasks('test').run()
+    }
+
+    @Test
+    public void projectCanDependOnItself() {
+        TestFile buildFile = testFile("build.gradle");
+        buildFile << '''
+            configurations { compile; create('default') }
+            dependencies { compile project(':') }
+            task jar1(type: Jar) { destinationDir = buildDir; baseName = '1' }
+            task jar2(type: Jar) { destinationDir = buildDir; baseName = '2' }
+            artifacts { compile jar1; 'default' jar2 }
+            task listJars << {
+                assert configurations.compile.collect { it.name } == ['2.jar']
+            }
+'''
+
+        inTestDirectory().withTasks("listJars").run()
+    }
+
+    @Test
+    public void canSpecifyProducerTasksForFileDependency() {
+        testFile("settings.gradle").write("include 'sub'");
+        testFile("build.gradle") << '''
+            configurations { compile }
+            dependencies { compile project(path: ':sub', configuration: 'compile') }
+            task test(dependsOn: configurations.compile) << {
+                assert file('sub/sub.jar').isFile()
+            }
+'''
+        testFile("sub/build.gradle") << '''
+            configurations { compile }
+            dependencies { compile files('sub.jar') { builtBy 'jar' } }
+            task jar << { file('sub.jar').text = 'content' }
+'''
+
+        inTestDirectory().withTasks("test").run().assertTasksExecuted(":sub:jar", ":test");
+    }
+
+    def getRepo() {
+        return maven(testFile('repo'))
+    }
+}
+
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
new file mode 100644
index 0000000..acf8a78
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+public class CacheResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    public void "cache handles manual deletion of cached artifacts"() {
+        given:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        def cacheDir = executer.gradleUserHomeDir.file('caches').toURI()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+task deleteCacheFiles(type: Delete) {
+    delete fileTree(dir: '${cacheDir}', includes: ['**/projectA/**'])
+}
+"""
+
+        and:
+        module.allowAll()
+
+        and:
+        succeeds('listJars')
+        succeeds('deleteCacheFiles')
+        
+        when:
+        server.resetExpectations()
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds('listJars')
+    }
+
+    public void "cache entries are segregated between different repositories"() {
+        given:
+        def repo1 = ivyHttpRepo('ivy-repo-a')
+        def module1 = repo1.module('org.gradle', 'testproject', '1.0').publish()
+        def repo2 = ivyHttpRepo('ivy-repo-b')
+        def module2 = repo2.module('org.gradle', 'testproject', '1.0').publishWithChangedContent()
+
+        and:
+        settingsFile << "include 'a','b'"
+        buildFile << """
+subprojects {
+    configurations {
+        test
+    }
+    dependencies {
+        test "org.gradle:testproject:1.0"
+    }
+    task retrieve(type: Sync) {
+        into 'build'
+        from configurations.test
+    }
+}
+project('a') {
+    repositories {
+        ivy { url "${repo1.uri}" }
+    }
+}
+project('b') {
+    repositories {
+        ivy { url "${repo2.uri}" }
+    }
+    retrieve.dependsOn(':a:retrieve')
+}
+"""
+
+        when:
+        module1.ivy.expectGet()
+        module1.jar.expectGet()
+
+        module2.ivy.expectHead()
+        module2.ivy.sha1.expectGet()
+        module2.ivy.expectGet()
+        module2.jar.expectHead()
+        module2.jar.sha1.expectGet()
+        module2.jar.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('a/build/testproject-1.0.jar').assertIsCopyOf(module1.jarFile)
+        file('b/build/testproject-1.0.jar').assertIsCopyOf(module2.jarFile)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
new file mode 100644
index 0000000..8b565b3
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ClientModuleDependenciesResolveIntegrationTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+public class ClientModuleDependenciesResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    public void "uses metadata from Client Module and looks up artifact in declared repositories"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+        def projectAInRepo1 = repo1.module('group', 'projectA', '1.2')
+        def projectAInRepo2 = repo2.module('group', 'projectA', '1.2').publish()
+        def projectB = repo1.module('group', 'projectB', '1.3').publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}" }
+    maven { url "${repo2.uri}" }
+}
+configurations { compile }
+dependencies {
+    compile module("group:projectA:1.2") {
+       dependency("group:projectB:1.3")
+    }
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-1.3.jar']
+}
+"""
+
+        when:
+        projectB.ivy.expectGet()
+        projectB.jar.expectGet()
+        projectAInRepo1.ivy.expectGetMissing()
+        projectAInRepo1.jar.expectHeadMissing()
+        projectAInRepo2.pom.expectGet()
+        projectAInRepo2.artifact.expectGet()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds('listJars')
+    }
+
+    def "client module dependency ignores published artifact listing and resolves single jar file"() {
+        def projectA = ivyHttpRepo.module('group', 'projectA', '1.2')
+                .artifact()
+                .artifact(classifier: "extra")
+                .publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+configurations {
+    regular
+    clientModule
+}
+dependencies {
+    regular "group:projectA:1.2"
+    clientModule module("group:projectA:1.2")
+}
+task listJars << {
+    assert configurations.regular.collect { it.name } == ['projectA-1.2.jar', 'projectA-1.2-extra.jar']
+}
+task listClientModuleJars << {
+    assert configurations.clientModule.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        projectA.ivy.expectGet()
+        projectA.jar.expectGet()
+
+        then:
+        succeeds('listClientModuleJars')
+
+        when:
+        server.resetExpectations()
+        projectA.getArtifact(classifier: "extra").expectGet()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds('listClientModuleJars')
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy
new file mode 100644
index 0000000..a50670a
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesChangingModulesIntegrationTest.groovy
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.HttpRepository
+
+abstract class ComponentMetadataRulesChangingModulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    abstract HttpRepository getRepo()
+    abstract String getRepoDeclaration()
+
+    def moduleA = getRepo().module('org.test', 'moduleA', '1.0')
+
+    def setup() {
+        moduleA.publish()
+        moduleA.allowAll()
+    }
+
+    def "changing dependency doesn't affect changing flag"() {
+        buildFile <<
+"""
+$repoDeclaration
+configurations {
+    modules
+}
+dependencies {
+    modules("org.test:moduleA:1.0") { changing = true }
+    components {
+        all { details ->
+            assert !details.changing
+        }
+    }
+}
+task resolve << {
+    configurations.modules.resolve()
+}
+"""
+
+        expect:
+        succeeds("resolve")
+    }
+
+    def "static and dynamic dependencies have changing flag initialized to false"() {
+        buildFile <<
+"""
+$repoDeclaration
+configurations {
+    modules
+}
+dependencies {
+    modules "org.test:moduleA:$version"
+    components {
+        all { details ->
+            assert !details.changing
+        }
+    }
+}
+task resolve << {
+    configurations.modules.resolve()
+}
+"""
+
+        expect:
+        succeeds("resolve")
+
+        where:
+        version << ["1.0", "[1.0,2.0]"]
+    }
+
+
+    def "rule can make a component changing"() {
+        buildFile <<
+"""
+$repoDeclaration
+configurations {
+    modules {
+        resolutionStrategy.cacheChangingModulesFor 0, "seconds"
+    }
+}
+dependencies {
+    modules("org.test:moduleA:1.0")
+    components {
+        all { details -> details.changing = true }
+    }
+}
+task resolve << {
+    copy {
+        from configurations.modules
+        into "modules"
+    }
+}
+"""
+
+        when:
+        run("resolve")
+        def artifact = file("modules/moduleA-1.0.jar")
+        def snapshot = artifact.snapshot()
+
+        and:
+        moduleA.publishWithChangedContent()
+        run("resolve")
+
+        then:
+        artifact.assertContentsHaveChangedSince(snapshot)
+
+        when:
+        buildFile << """
+dependencies.components.all { details -> details.changing = false }
+"""
+        snapshot = artifact.snapshot()
+        server.resetExpectations()
+
+        and:
+        run("resolve")
+
+        then:
+        artifact.assertContentsHaveNotChangedSince(snapshot)
+    }
+
+    def "rule cannot make a dependency non-changing"() {
+        buildFile <<
+"""
+$repoDeclaration
+configurations {
+    modules {
+        resolutionStrategy.cacheChangingModulesFor 0, "seconds"
+    }
+}
+dependencies {
+    modules("org.test:moduleA:1.0") { changing = true }
+    components {
+        all { details -> details.changing = false }
+    }
+}
+task resolve << {
+    copy {
+        from configurations.modules
+        into "modules"
+    }
+}
+"""
+
+        when:
+        run("resolve")
+        def artifact = file("modules/moduleA-1.0.jar")
+        def snapshot = artifact.snapshot()
+
+        and:
+        moduleA.publishWithChangedContent()
+        run("resolve")
+
+        then:
+        artifact.assertContentsHaveChangedSince(snapshot)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesErrorHandlingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesErrorHandlingIntegrationTest.groovy
new file mode 100644
index 0000000..22fdec5
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesErrorHandlingIntegrationTest.groovy
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+
+class ComponentMetadataRulesErrorHandlingIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def setup() {
+        ivyRepo.module('org.test', 'projectA', '1.0').publish()
+        buildFile << """
+            repositories {
+                ivy {
+                    url "$ivyRepo.uri"
+                }
+            }
+
+            configurations { compile }
+
+            dependencies {
+                compile 'org.test:projectA:1.0'
+            }
+
+            task resolve {
+                doLast {
+                    configurations.compile.files
+                }
+            }
+        """
+    }
+
+    def "produces sensible error when bad code is supplied in component metadata rule" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all { metadata ->
+                        foo()
+                    }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failure.assertHasDescription("Execution failed for task ':resolve'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(23)
+        failure.assertHasCause("There was an error while evaluating a component metadata rule for org.test:projectA:1.0.")
+        failure.assertHasCause("No signature of method: org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataDetailsAdapter_Decorated.foo()")
+    }
+
+    def "produces sensible error for invalid component metadata rule" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all { ${parameters} }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(22)
+        failureHasCause("The closure provided is not valid as a rule for 'ComponentMetadataHandler'.")
+        failureHasCause(message)
+
+        where:
+        parameters                           | message
+        "String vs ->"                       | "First parameter of rule action closure must be of type 'ComponentMetadataDetails'."
+        "ComponentMetadata cm, String s ->"  | "Rule may not have an input parameter of type: java.lang.String. " +
+                                               "Second parameter must be of type: " +
+                                               "org.gradle.api.artifacts.ivy.IvyModuleDescriptor."
+    }
+
+    def "produces sensible error when closure rule throws an exception" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all { throw new Exception('From Test') }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failure.assertHasDescription("Execution failed for task ':resolve'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(22)
+        failure.assertHasCause("There was an error while evaluating a component metadata rule for org.test:projectA:1.0.")
+        failure.assertHasCause("From Test")
+    }
+
+    def "produces sensible error for invalid module target id" () {
+        buildFile << """
+            dependencies {
+                components {
+                    withModule('org.test') { }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(22)
+        failureHasCause("Could not add a component metadata rule for module 'org.test'.")
+        failureHasCause("Cannot convert the provided notation to an object of type ModuleIdentifier: org.test")
+    }
+
+    def "produces sensible error when @Mutate method doesn't provide ComponentSelection as the first parameter" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all(new BadRuleSource())
+                }
+            }
+
+            class BadRuleSource {
+                @org.gradle.model.Mutate
+                void process(String s) { }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(22)
+        failureHasCause("Type BadRuleSource is not a valid model rule source: \n- first parameter of rule method 'process' must be of type org.gradle.api.artifacts.ComponentMetadataDetails")
+
+    }
+
+    def "produces sensible error when rule source throws an exception" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all(new ExceptionRuleSource())
+                }
+            }
+
+            class ExceptionRuleSource {
+                def candidates = []
+
+                @org.gradle.model.Mutate
+                void process(ComponentMetadataDetails cmd) {
+                    throw new Exception("thrown from rule")
+                }
+            }
+        """
+
+        expect:
+        fails 'resolve'
+        failure.assertHasDescription("Execution failed for task ':resolve'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(31)
+        failure.assertHasCause("There was an error while evaluating a component metadata rule for org.test:projectA:1.0.")
+        failure.assertHasCause("java.lang.Exception: thrown from rule")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..3779dce
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.HttpRepository
+
+abstract class ComponentMetadataRulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    abstract HttpRepository getRepo()
+    abstract String getRepoDeclaration()
+    abstract String getDefaultStatus()
+
+    def setup() {
+        buildFile <<
+"""
+$repoDeclaration
+
+configurations { compile }
+
+dependencies {
+    compile 'org.test:projectA:1.0'
+}
+
+// implement Sync manually to make sure that task is never up-to-date
+task resolve {
+    doLast {
+        delete 'libs'
+        copy {
+            from configurations.compile
+            into 'libs'
+        }
+    }
+}
+"""
+    }
+
+    def "rule receives correct metadata"() {
+        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
+        buildFile <<
+"""
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            assert details.id.group == "org.test"
+            assert details.id.name == "projectA"
+            assert details.id.version == "1.0"
+            assert details.status == "$defaultStatus"
+            assert details.statusScheme == ["integration", "milestone", "release"]
+            assert !details.changing
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+    }
+
+    def "changes made by a rule are visible to subsequent rules"() {
+        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
+
+        buildFile <<
+                """
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            details.status "integration.changed" // verify that 'details' is enhanced
+            details.statusScheme = ["integration.changed", "milestone.changed", "release.changed"]
+            details.changing = true
+        }
+        all { ComponentMetadataDetails details ->
+            assert details.status == "integration.changed"
+            assert details.statusScheme == ["integration.changed", "milestone.changed", "release.changed"]
+            assert details.changing
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+    }
+
+    def "changes made by a rule are not cached"() {
+        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
+
+        buildFile <<
+                """
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            assert !details.changing
+            assert details.status == "$defaultStatus"
+            assert details.statusScheme == ["integration", "milestone", "release"]
+
+            details.changing = true
+            details.status = "release.changed"
+            details.statusScheme = ["integration.changed", "milestone.changed", "release.changed"]
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+        succeeds 'resolve'
+    }
+
+    def "can apply all rule types to all modules" () {
+        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
+        buildFile << """
+            ext.rulesInvoked = []
+            dependencies {
+                components {
+                    all { ComponentMetadataDetails details ->
+                        rulesInvoked << details.id.version
+                    }
+                    all {
+                        rulesInvoked << id.version
+                    }
+                    all { details ->
+                        rulesInvoked << details.id.version
+                    }
+                    all(new ActionRule('rulesInvoked': rulesInvoked))
+                    all(new RuleObject('rulesInvoked': rulesInvoked))
+                }
+            }
+
+            class ActionRule implements Action<ComponentMetadataDetails> {
+                List rulesInvoked
+
+                void execute(ComponentMetadataDetails details) {
+                    rulesInvoked << details.id.version
+                }
+            }
+
+            class RuleObject {
+                List rulesInvoked
+
+                @org.gradle.model.Mutate
+                void execute(ComponentMetadataDetails details) {
+                    rulesInvoked << details.id.version
+                }
+            }
+
+            resolve.doLast { assert rulesInvoked == [ '1.0', '1.0', '1.0', '1.0', '1.0' ] }
+        """
+
+        expect:
+        succeeds 'resolve'
+    }
+
+    def "can apply all rule types by module" () {
+        repo.module('org.test', 'projectA', '1.0').publish().allowAll()
+        buildFile << """
+            ext.rulesInvoked = []
+            ext.rulesUninvoked = []
+            dependencies {
+                components {
+                    withModule('org.test:projectA') { ComponentMetadataDetails details ->
+                        assert details.id.group == 'org.test'
+                        assert details.id.name == 'projectA'
+                        rulesInvoked << 1
+                    }
+                    withModule('org.test:projectA', new ActionRule('rulesInvoked': rulesInvoked))
+                    withModule('org.test:projectA', new RuleObject('rulesInvoked': rulesInvoked))
+
+                    withModule('org.test:projectB') { ComponentMetadataDetails details ->
+                        rulesUninvoked << 1
+                    }
+                    withModule('org.test:projectB', new ActionRule('rulesInvoked': rulesUninvoked))
+                    withModule('org.test:projectB', new RuleObject('rulesInvoked': rulesUninvoked))
+                }
+            }
+
+            class ActionRule implements Action<ComponentMetadataDetails> {
+                List rulesInvoked
+
+                void execute(ComponentMetadataDetails details) {
+                    rulesInvoked << 2
+                }
+            }
+
+            class RuleObject {
+                List rulesInvoked
+
+                @org.gradle.model.Mutate
+                void execute(ComponentMetadataDetails details) {
+                    rulesInvoked << 3
+                }
+            }
+
+            resolve.doLast {
+                assert rulesInvoked.sort() == [ 1, 2, 3 ]
+                assert rulesUninvoked.empty
+            }
+        """
+
+        expect:
+        succeeds 'resolve'
+    }
+
+    String sq(String input) {
+        return escapeForSingleQuoting(input)
+    }
+
+    String escapeForSingleQuoting(String input) {
+        return input.replace('\\', '\\\\').replace('\'', '\\\'')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy
new file mode 100644
index 0000000..aae872a
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentMetadataRulesStatusIntegrationTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.HttpRepository
+
+abstract class ComponentMetadataRulesStatusIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    abstract HttpRepository getRepo()
+
+    abstract String getRepoDeclaration()
+
+    def setup() {
+        buildFile <<
+"""
+$repoDeclaration
+
+configurations { compile }
+
+dependencies {
+    compile 'org.test:projectA:1.0'
+}
+
+task resolve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentReplacementIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentReplacementIntegrationTest.groovy
new file mode 100644
index 0000000..c3d9a97
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ComponentReplacementIntegrationTest.groovy
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.TestDependency
+import spock.lang.Ignore
+
+class ComponentReplacementIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+            configurations { conf }
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            task resolvedFiles {
+                dependsOn 'dependencies'
+                doLast {
+                    copy {
+                        from configurations.conf
+                        into 'resolved-files'
+                    }
+                    println "All files:"
+                    configurations.conf.each { println it.name }
+                }
+            }
+        """
+    }
+
+    //publishes and declares the dependencies
+    void declaredDependencies(String ... deps) {
+        publishedMavenModules(deps)
+        def content = ''
+        deps.each {
+            content += "dependencies.conf '${new TestDependency(it).notation}'\n"
+        }
+        buildFile << """
+            $content
+        """
+    }
+
+    void declaredReplacements(String ... reps) {
+        def content = ''
+        reps.each {
+            def d = new TestDependency(it)
+            content +=  "dependencies.modules.module('${d.group}:${d.name}') { replacedBy '${d.pointsTo.group}:${d.pointsTo.name}' }\n"
+        }
+        buildFile << """
+            $content
+        """
+    }
+
+    void resolvedFiles(String ... files) {
+        run("resolvedFiles")
+        assert file('resolved-files').listFiles()*.name as Set == files as Set
+    }
+
+    void resolvedModules(String ... modules) {
+        resolvedFiles(modules.collect { new TestDependency(it).jarName } as String[])
+    }
+
+    def "ignores replacement if not in graph"() {
+        declaredDependencies 'a'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'a'
+    }
+
+    def "ignores replacement if org does not match"() {
+        declaredDependencies 'a', 'com:b'
+        declaredReplacements 'a->org:b'
+        expect: resolvedModules 'a', 'com:b'
+    }
+
+    def "just uses replacement if source not in graph"() {
+        declaredDependencies 'b'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b'
+    }
+
+    def "replaces already resolved module"() {
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b'
+    }
+
+    def "replaces not yet resolved module"() {
+        declaredDependencies 'b', 'a'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b'
+    }
+
+    def "uses highest when it is last"() {
+        declaredDependencies 'b', 'a', 'b:2'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b:2'
+    }
+
+    def "uses highest when it is last following replacedBy"() {
+        declaredDependencies 'a', 'b', 'b:2'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b:2'
+    }
+
+    def "uses highest when it is first"() {
+        declaredDependencies 'b:2', 'b', 'a'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b:2'
+    }
+
+    def "uses highest when it is first followed by replacedBy"() {
+        declaredDependencies 'b:2', 'b', 'a'
+        declaredReplacements 'a->b'
+        expect: resolvedModules 'b:2'
+    }
+
+    def "evicts transitive dependencies of replaced module"() {
+        declaredDependencies 'a', 'c'
+        declaredReplacements 'a->e'
+        //resolution sequence: a,c,b,d,e!
+        publishedMavenModules 'a->b', 'c->d', 'd->e'
+        expect: resolvedModules 'c', 'd', 'e' //'b' is evicted
+    }
+
+    def "replaces transitive module"() {
+        declaredDependencies 'a', 'c'
+        declaredReplacements 'b->d'
+        publishedMavenModules 'a->b', 'c->d'
+        expect: resolvedModules 'a', 'd', 'c'
+    }
+
+    def "replaces module even if it was already conflict-resolved"() {
+        declaredDependencies 'a:1', 'a:2'
+        declaredReplacements 'a->c'
+        //resolution sequence: a1,a2,!,b,c,!
+        publishedMavenModules 'a:2->b', 'b->c'
+        expect: resolvedModules 'c'
+    }
+
+    def "uses already resolved highest version"() {
+        declaredDependencies 'a:1', 'a:2'
+        declaredReplacements 'c->a'
+        //resolution sequence: a1,a2,!,b,c,!
+        publishedMavenModules 'a:2->b', 'b->c'
+        expect: resolvedModules 'a:2', 'b'
+    }
+
+    def "latest replacement wins"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->b', 'a->c' //2 replacements for the same source module
+        expect: resolvedModules 'c', 'b'
+    }
+
+    def "supports consecutive replacements"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->b', 'b->c'
+        expect: resolvedModules 'c'
+    }
+
+    def "reports replacement cycles early"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->b', 'b->c', 'c->a'
+        expect:
+        def failure = fails()
+        failure.assertHasCause("Cannot declare module replacement org:c->org:a because it introduces a cycle: org:c->org:a->org:b->org:c")
+    }
+
+    def "replacement target unresolved"() {
+        publishedMavenModules('a')
+        buildFile << "dependencies { conf 'org:a:1', 'org:b:1' }\n"
+        declaredReplacements 'a->b'
+
+        expect:
+        fails("resolvedFiles").assertResolutionFailure(":conf")
+    }
+
+    def "replacement source unresolved"() {
+        publishedMavenModules('a')
+        buildFile << "dependencies { conf 'org:a:1', 'org:b:1' }\n"
+        declaredReplacements 'a->b'
+
+        expect:
+        fails("resolvedFiles").assertResolutionFailure(":conf")
+    }
+
+    def "human error in declaring replacements is neatly reported"() {
+        buildFile << """
+            dependencies.modules.module('org:foo') { replacedBy('org:bar:2.0') }
+        """
+
+        expect:
+        fails().assertHasCause("Cannot convert the provided notation to an object of type ModuleIdentifier: org:bar:2.0")
+    }
+
+    def "human error in referring to component module metadata is neatly reported"() {
+        buildFile << """
+            dependencies.modules.module('org:foo:1.0') {}
+        """
+
+        expect:
+        fails().assertHasCause("Cannot convert the provided notation to an object of type ModuleIdentifier: org:foo:1.0")
+    }
+
+    def "replacement target is not used if it is excluded"() {
+        declaredDependencies 'a', 'b->c'
+        declaredReplacements 'a->c'
+        buildFile << "configurations.all { exclude module: 'c' }"
+        expect: resolvedModules 'a', 'b'
+    }
+
+    def "replacement target is used if replacement source is excluded"() {
+        declaredDependencies 'a', 'b->c'
+        declaredReplacements 'a->c'
+        buildFile << "configurations.all { exclude module: 'a' }"
+        expect: resolvedModules 'c', 'b'
+    }
+
+    def "replacement is not used when it replaced by resolve rule"() {
+        publishedMavenModules('d')
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'b') {
+                    dep.useTarget 'org:d:1'
+                }
+            }}
+        """
+        expect: resolvedModules 'a', 'd'
+    }
+
+    def "replacement and resolve rule have exactly the same target"() {
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'a') {
+                    dep.useTarget 'org:b:1'
+                }
+            }}
+        """
+        expect: resolvedModules 'b'
+    }
+
+    def "replacement is used when it is pulled to the graph via resolve rule"() {
+        publishedMavenModules('d')
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->d'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'b') {
+                    dep.useTarget 'org:d:1'
+                }
+            }}
+        """
+        expect: resolvedModules 'd'
+    }
+
+    def "both source and replacement target are pulled to the graph via resolve rule"() {
+        publishedMavenModules('a', 'b')
+        declaredDependencies 'c', 'd'
+        declaredReplacements 'a->b'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'c') { dep.useTarget 'org:a:1' }
+                if (dep.requested.module == 'd') { dep.useTarget 'org:b:1' }
+            }}
+        """
+        expect: resolvedModules 'b'
+    }
+
+    def "replacement target is manipulated by resolve rule and then replaced again by different module replacement declaration"() {
+        publishedMavenModules 'd'
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->b', 'c->d'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'b') { dep.useTarget 'org:d:1' }
+            }}
+        """
+        expect: resolvedModules 'a', 'd'
+    }
+
+    def "replacement target forms a legal cycle with resolve rule"() {
+        publishedMavenModules 'd'
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b', 'd->a'
+        buildFile << """
+            configurations.all { resolutionStrategy.dependencySubstitution.eachModule { dep ->
+                if (dep.requested.module == 'b') { dep.useTarget 'org:d:1' }
+            }}
+        """
+        //a->b->d->a
+        expect: resolvedModules 'a'
+    }
+
+    def "supports multiple replacement targets"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->b', 'a->c'
+        expect: resolvedModules 'b', 'c'
+    }
+
+    def "multiple source modules have the same replacement target"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->c', 'b->c'
+        expect: resolvedModules 'c'
+    }
+
+    def "multiple source modules but only some are included in graph"() {
+        declaredDependencies 'a', 'c'
+        declaredReplacements 'a->c', 'b->c'
+        expect: resolvedModules 'c'
+    }
+
+    def "declared modules coexist with forced versions"() {
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b'
+        buildFile << "configurations.all { resolutionStrategy.force 'org:a:1', 'org:b:1'} "
+        expect: resolvedModules 'b'
+    }
+
+    @Ignore //maybe, as a way to 'clear' any replacements
+    def "allow replacing with self"() {
+        declaredDependencies 'a', 'b', 'c'
+        declaredReplacements 'a->c', 'a->b', 'a->a'
+        expect: resolvedModules 'a', 'b', 'c'
+    }
+
+    @Ignore
+    def "pulls extra dependency to graph if multiple replacement targets declared"() {
+        publishedMavenModules 'c'
+        declaredDependencies 'a', 'b'
+        declaredReplacements 'a->b', 'a->c'
+        expect: resolvedModules 'b', 'c'
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CredentialsDslIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CredentialsDslIntegrationTest.groovy
new file mode 100644
index 0000000..79dfad6
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CredentialsDslIntegrationTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.resolve.support.RepositoryDslSupport
+import spock.lang.Unroll
+
+ at Mixin(RepositoryDslSupport)
+class CredentialsDslIntegrationTest extends AbstractIntegrationSpec {
+
+    @Unroll
+    def "can configure repositories with [#scenario] DSL "() {
+        expect:
+        buildFile << dsl
+        succeeds 'help'
+
+        where:
+        scenario          | dsl
+        'simple'          | simplestDsl()
+        'awsCredentials'  | awsCredentials()
+        'ivyWithPassword' | ivyPasswordCredentials()
+        'passwordTyped'   | passwordCredentialsTyped()
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyExcludeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..0bebf8e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyExcludeResolveIntegrationTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Unroll
+
+class DependencyExcludeResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    /**
+     * Dependency exclude rules defined through Gradle DSL.
+     *
+     * Dependency graph:
+     *
+     * org.gradle:test:1.45 -> org.gradle:foo:2.0, org.gradle:bar:3.0, com.company:company:4.0, com.company:other-company:4.0
+     * com.company:company:4.0 -> com.enterprise:enterprise:5.0, org.gradle:baz:6.0
+     */
+    @Unroll
+    def "dependency exclude rule for #condition"() {
+        given:
+        final String orgGradleGroupId = 'org.gradle'
+        def testModule = mavenRepo().module(orgGradleGroupId, 'test', '1.45')
+        def fooModule = mavenRepo().module(orgGradleGroupId, 'foo', '2.0')
+        fooModule.publish()
+        def barModule = mavenRepo().module(orgGradleGroupId, 'bar', '3.0')
+        barModule.publish()
+        def bazModule = mavenRepo().module(orgGradleGroupId, 'baz', '6.0')
+        bazModule.publish()
+
+        def enterpriseModule = mavenRepo().module('com.enterprise', 'enterprise', '5.0')
+        enterpriseModule.publish()
+
+        def companyModule = mavenRepo().module('com.company', 'company', '4.0')
+        companyModule.dependsOn(enterpriseModule)
+        companyModule.dependsOn(bazModule)
+        companyModule.publish()
+
+        def otherCompanyModule = mavenRepo().module('com.company', 'other-company', '4.0')
+        otherCompanyModule.publish()
+
+        testModule.dependsOn(fooModule)
+        testModule.dependsOn(barModule)
+        testModule.dependsOn(companyModule)
+        testModule.dependsOn(otherCompanyModule)
+        testModule.publish()
+
+        and:
+        buildFile << """
+repositories { maven { url "${mavenRepo().uri}" } }
+configurations { compile }
+dependencies {
+    compile('${testModule.groupId}:${testModule.artifactId}:${testModule.version}') {
+        exclude ${excludeAttributes.collect { key, value -> "$key: '$value'" }.join(', ')}
+    }
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == [${resolvedJars.collect { "'$it'" }.join(", ")}]
+}
+"""
+
+        expect:
+        succeeds "check"
+
+        where:
+        condition                                          | excludeAttributes                         | resolvedJars
+        'excluding by group'                               | [group: 'com.company']                    | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar']
+        'excluding by module and group'                    | [group: 'com.company', module: 'company'] | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar', 'other-company-4.0.jar']
+        'excluding group of declared module'               | [group: 'org.gradle']                     | ['test-1.45.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'enterprise-5.0.jar']
+        'excluding other module in same group as declared' | [group: 'org.gradle', module: 'foo']      | ['test-1.45.jar', 'bar-3.0.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'enterprise-5.0.jar', 'baz-6.0.jar']
+        'excluding transitive module by group'             | [group: 'com.enterprise']                 | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'baz-6.0.jar']
+        'non-matching group attribute'                     | [group: 'some.other']                     | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'enterprise-5.0.jar', 'baz-6.0.jar']
+        'non-matching module attribute'                    | [module: 'unknown']                       | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'enterprise-5.0.jar', 'baz-6.0.jar']
+        'attempting to exclude declared module'            | [group: 'org.gradle', module: 'test']     | ['test-1.45.jar', 'foo-2.0.jar', 'bar-3.0.jar', 'company-4.0.jar', 'other-company-4.0.jar', 'enterprise-5.0.jar', 'baz-6.0.jar']
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
new file mode 100644
index 0000000..757b3cd
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
@@ -0,0 +1,211 @@
+/*
+ * 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 org.hamcrest.Matchers
+import spock.lang.Issue
+
+class DependencyNotationIntegrationSpec extends AbstractIntegrationSpec {
+
+    def "understands dependency notations"() {
+        when:
+        buildFile <<  """
+import org.gradle.api.internal.artifacts.dependencies.*
+configurations {
+    conf
+    gradleStuff
+    allowsCollections
+}
+
+def someDependency = new DefaultSelfResolvingDependency(files('foo.txt'))
+dependencies {
+    conf someDependency
+    conf "org.mockito:mockito-core:1.8"
+    conf group: 'org.spockframework', name: 'spock-core', version: '1.0'
+    conf module('org.foo:moduleOne:1.0'), module('org.foo:moduleTwo:1.0')
+
+    gradleStuff gradleApi()
+
+    allowsCollections "org.mockito:mockito-core:1.8", someDependency
+}
+
+task checkDeps << {
+    def deps = configurations.conf.incoming.dependencies
+    assert deps.contains(someDependency)
+    assert deps.find { it instanceof ExternalDependency && it.group == 'org.mockito' && it.name == 'mockito-core' && it.version == '1.8'  }
+    assert deps.find { it instanceof ExternalDependency && it.group == 'org.spockframework' && it.name == 'spock-core' && it.version == '1.0'  }
+    assert deps.find { it instanceof ClientModule && it.name == 'moduleOne' && it.group == 'org.foo' }
+    assert deps.find { it instanceof ClientModule && it.name == 'moduleTwo' && it.version == '1.0' }
+
+    deps = configurations.gradleStuff.dependencies
+    assert deps.findAll { it instanceof SelfResolvingDependency }.size() > 0 : "should include gradle api jars"
+
+    deps = configurations.allowsCollections.dependencies
+    assert deps.size() == 2
+    assert deps.find { it instanceof ExternalDependency && it.group == 'org.mockito' }
+    assert deps.contains(someDependency)
+}
+"""
+        then:
+        succeeds 'checkDeps'
+    }
+
+    def "understands project notations"() {
+        when:
+        settingsFile << "include 'otherProject'"
+
+        buildFile <<  """
+configurations {
+    conf
+    confTwo
+}
+
+project(':otherProject') {
+    configurations {
+        otherConf
+    }
+}
+
+dependencies {
+    conf project(':otherProject')
+    confTwo project(path: ':otherProject', configuration: 'otherConf')
+}
+
+task checkDeps << {
+    def deps = configurations.conf.incoming.dependencies
+    assert deps.size() == 1
+    assert deps.find { it.dependencyProject.path == ':otherProject' }
+
+    deps = configurations.confTwo.incoming.dependencies
+    assert deps.size() == 1
+    assert deps.find { it.dependencyProject.path == ':otherProject' && it.projectConfiguration.name == 'otherConf' }
+}
+"""
+        then:
+        succeeds 'checkDeps'
+    }
+
+    def "understands client module notation with dependencies"() {
+        when:
+        buildFile <<  """
+configurations {
+    conf
+}
+
+dependencies {
+    conf module('org.foo:moduleOne:1.0') {
+        dependency 'org.foo:bar:1.0'
+        dependencies ('org.foo:one:1', 'org.foo:two:1')
+        dependency ('high:five:5') { transitive = false }
+    }
+}
+
+task checkDeps << {
+    def deps = configurations.conf.incoming.dependencies
+    assert deps.size() == 1
+    def dep = deps.find { it instanceof ClientModule && it.name == 'moduleOne' }
+    assert dep
+    assert dep.dependencies.size() == 4
+    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'bar' && it.version == '1.0' && it.transitive == true }
+    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'one' && it.version == '1' }
+    assert dep.dependencies.find { it.group == 'org.foo' && it.name == 'two' && it.version == '1' }
+    assert dep.dependencies.find { it.group == 'high' && it.name == 'five' && it.version == '5' && it.transitive == false }
+}
+"""
+        then:
+        succeeds 'checkDeps'
+    }
+
+    def "fails gracefully for invalid notations"() {
+        when:
+        buildFile <<  """
+configurations {
+    conf
+}
+
+dependencies {
+    conf 100
+}
+
+task checkDeps
+"""
+        then:
+        fails 'checkDeps'
+        failure.assertThatCause(Matchers.startsWith("Cannot convert the provided notation to an object of type Dependency: 100."))
+    }
+
+    def "fails gracefully for single null notation"() {
+        when:
+        buildFile <<  """
+configurations {
+    conf
+}
+
+dependencies {
+    conf null
+}
+
+task checkDeps
+"""
+        then:
+        fails 'checkDeps'
+        failure.assertThatCause(Matchers.startsWith("Cannot convert a null value to an object of type Dependency"))
+    }
+
+    def "fails gracefully for null notation in list"() {
+        when:
+        buildFile <<  """
+configurations {
+    conf
+}
+
+dependencies {
+    conf "a:b:c", null, "d:e:f"
+}
+
+task checkDeps
+"""
+        then:
+        fails 'checkDeps'
+        failure.assertThatCause(Matchers.startsWith("Cannot convert a null value to an object of type Dependency"))
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3271")
+    def "gradleApi dependency implements contentEquals"() {
+        when:
+        buildFile << """
+            configurations {
+              conf
+            }
+
+            dependencies {
+              conf gradleApi()
+            }
+
+            task check << {
+              assert dependencies.gradleApi().contentEquals(dependencies.gradleApi())
+              assert dependencies.gradleApi().is(dependencies.gradleApi())
+              assert dependencies.gradleApi() == dependencies.gradleApi()
+              assert configurations.conf.dependencies.contains(dependencies.gradleApi())
+            }
+        """
+
+        then:
+        succeeds "check"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy
new file mode 100644
index 0000000..bba7f39
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolutionEventsIntegrationTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve
+
+import org.gradle.integtests.fixtures.*
+import spock.lang.Issue
+
+class DependencyResolutionEventsIntegrationTest extends AbstractIntegrationSpec {
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2047")
+    def "can access resolved files from afterResolve hook"() {
+        given:
+        file("thing.txt") << "stuff"
+        buildFile << """
+            configurations {
+                things.incoming.afterResolve { incoming ->
+                    incoming.files.files
+                    println "accessed files"
+                }
+            }
+            dependencies {
+                things files("thing.txt")
+            }
+
+            task resolveIt(type: Copy, dependsOn: configurations.things) {
+                from configurations.things
+                into buildDir
+            }
+        """
+
+        when:
+        run "resolveIt"
+
+        then:
+        output.contains "accessed files"
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
new file mode 100644
index 0000000..3f6bfdb
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyResolveRulesIntegrationTest.groovy
@@ -0,0 +1,840 @@
+/*
+ * 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 static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class DependencyResolveRulesIntegrationTest extends AbstractIntegrationSpec {
+
+    /**
+     * Test demonstrating current (not necessarily desired) behaviour
+     */
+    void "can replace project dependency with external dependency"() {
+        mavenRepo.module("org.gradle.test", "a", '1.3').publish()
+
+        settingsFile << "include 'a', 'b'"
+        buildFile << """
+            project(':a') {
+                apply plugin: 'base'
+                group = 'org.gradle.test'
+                version = '1.2'
+                configurations { 'default' }
+            }
+
+            project(':b') {
+                $common
+                dependencies {
+                    conf project(':a')
+                }
+                configurations.conf.resolutionStrategy {
+                    eachDependency {
+                        assert it.requested.toString() == 'org.gradle.test:a:1.2'
+                        assert it.target.toString() == 'org.gradle.test:a:1.2'
+                        it.useVersion('1.3')
+                        assert it.target.toString() == 'org.gradle.test:a:1.3'
+                    }
+                }
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0].selected.id instanceof ModuleComponentIdentifier
+                    assert deps[0].selected.id.version == '1.3'
+                }
+            }
+"""
+        expect:
+        // Force resolve to catch failures
+        succeeds("resolveConf")
+        succeeds("check")
+    }
+
+    void "forces multiple modules by rule"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5') publish()
+        mavenRepo.module("org.utils", "optional-lib", '5.0').publish()
+
+        //above models the scenario where org.utils:api and org.utils:impl are libraries that must be resolved with the same version
+        //however due to the conflict resolution, org.utils:api:1.5 and org.utils.impl:1.3 are resolved.
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.stuff:foo:2.0', 'org.utils:impl:1.3', 'org.utils:optional-lib:5.0'
+            }
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    if (it.requested.group == 'org.utils' && it.requested.name != 'optional-lib') {
+                        it.useVersion '1.5'
+                    }
+                }
+                failOnVersionConflict()
+            }
+"""
+
+        when:
+        run("resolveConf")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "module forced by rule has correct selection reason"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'impl', '1.3') publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.stuff:foo:2.0'
+            }
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    if (it.requested.group == 'org.utils') {
+                        it.useVersion '1.5'
+                    }
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps*.selected.id.module == ['foo', 'impl', 'api']
+                assert deps*.selected.id.version == ['2.0', '1.5', '1.5']
+                assert deps*.selected.selectionReason.forced         == [false, false, false]
+                assert deps*.selected.selectionReason.selectedByRule == [false, true, true]
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "all rules are executed orderly and last one wins"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    assert it.target == it.requested
+                    it.useVersion '1.4'
+                }
+                eachDependency {
+                    assert it.target.version == '1.4'
+                    assert it.target.name == it.requested.name
+                    assert it.target.group == it.requested.group
+                    it.useVersion '1.5'
+                }
+                eachDependency {
+                    assert it.target.version == '1.5'
+                    //don't change the version
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+                    assert it.selected.id.version == '1.5'
+                    assert it.selected.selectionReason.selectedByRule
+                    assert it.selected.selectionReason.description == 'selected by rule'
+                }
+            }
+"""
+
+        when:
+        succeeds("resolveConf")
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can unforce the version"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5", "org.utils:api:1.5")
+
+                eachDependency {
+                    it.useVersion it.requested.version
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+                    assert it.selected.id.version == '1.3'
+                    def reason = it.selected.selectionReason
+                    assert !reason.forced
+                    assert reason.selectedByRule
+                }
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule are applied after forced modules"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5", "org.utils:api:1.5")
+
+                eachDependency {
+                    assert it.target.version == '1.5'
+                    it.useVersion '1.3'
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+                    assert it.selected.id.version == '1.3'
+                    def reason = it.selected.selectionReason
+                    assert !reason.forced
+                    assert reason.selectedByRule
+                }
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "forced modules and rules coexist"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5")
+
+                eachDependency {
+                    if (it.requested.name == 'api') {
+                        assert it.target == it.requested
+                        it.useVersion '1.5'
+                    }
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.find {
+                    it.selected.id.module == 'impl' &&
+                    it.selected.id.version == '1.5' &&
+                    it.selected.selectionReason.forced &&
+                    !it.selected.selectionReason.selectedByRule
+                }
+
+                assert deps.find {
+                    it.selected.id.module == 'api' &&
+                    it.selected.id.version == '1.5' &&
+                    !it.selected.selectionReason.forced &&
+                    it.selected.selectionReason.selectedByRule
+                }
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule selects a dynamic version"()
+    {
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.4').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                it.useVersion '1.+'
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                assert deps[0].requested.version == '1.3'
+                assert deps[0].selected.id.version == '1.5'
+                assert !deps[0].selected.selectionReason.forced
+                assert deps[0].selected.selectionReason.selectedByRule
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can blacklist a version"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.4').publish()
+        mavenRepo.module("org.utils", "a",  '1.3').publish()
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                // a:1.2 is blacklisted, 1.4 should be used instead:
+                if (it.requested.name == 'a' && it.requested.version == '1.2') {
+                    it.useVersion '1.4'
+                }
+            }
+
+            task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                def a = modules.find { it.id.module == 'a' }
+                assert a.id.version == '1.4'
+                assert a.selectionReason.conflictResolution
+                assert a.selectionReason.selectedByRule
+                assert !a.selectionReason.forced
+                assert a.selectionReason.description == 'selected by rule and conflict resolution'
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can blacklist a version that is not used"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.3').publish()
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                // a:1.2 is blacklisted, 1.2.1 should be used instead:
+                if (it.requested.name == 'a' && it.requested.version == '1.2') {
+                    it.useVersion '1.2.1'
+                }
+            }
+
+            task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                def a = modules.find { it.id.module == 'a' }
+                assert a.id.version == '1.3'
+                assert a.selectionReason.conflictResolution
+                assert !a.selectionReason.selectedByRule
+                assert !a.selectionReason.forced
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can use custom versioning scheme"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:default'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.version == 'default') {
+                    it.useVersion '1.3'
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                deps[0].requested.version == 'default'
+                deps[0].selected.id.version == '1.3'
+                deps[0].selected.selectionReason.selectedByRule
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can use custom versioning scheme for transitive dependencies"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', 'default').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.version == 'default') {
+                    it.useVersion '1.3'
+                }
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 2
+                def api = deps.find { it.requested.module == 'api' }
+                api.requested.version == 'default'
+                api.selected.id.version == '1.3'
+                api.selected.selectionReason.selectedByRule
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule selects unavailable version"()
+    {
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                it.useVersion '1.123.15' //does not exist
+            }
+
+            task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                assert deps[0].attempted.group == 'org.utils'
+                assert deps[0].attempted.module == 'api'
+                assert deps[0].attempted.version == '1.123.15'
+                assert deps[0].attemptedReason.selectedByRule
+                assert deps[0].failure.message.contains('1.123.15')
+                assert deps[0].requested.version == '1.3'
+            }
+"""
+
+        when:
+        def failure = runAndFail("check", "resolveConf")
+
+        then:
+        failure.assertResolutionFailure(":conf")
+            .assertHasCause("Could not find org.utils:api:1.123.15")
+    }
+
+    void "rules triggered exactly once per the same dependency"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5').publish()
+        mavenRepo.module("org.stuff", "bar", '2.0').dependsOn('org.utils', 'impl', '1.3').publish()
+
+        /*
+        dependencies:
+
+        impl:1.3->api:1.3
+        foo->api:1.5
+        bar->impl:1.3(*)->api:1.3(*)
+
+        * - should be excluded as it was already visited
+        */
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3', 'org.stuff:foo:2.0', 'org.stuff:bar:2.0'
+            }
+
+            List requested = []
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    requested << "\$it.requested.name:\$it.requested.version"
+                }
+            }
+
+            task check << {
+                configurations.conf.resolve()
+                assert requested == ['impl:1.3', 'foo:2.0', 'bar:2.0', 'api:1.3', 'api:1.5']
+            }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "runtime exception when evaluating rule yields decent exception"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+            version = 1.0
+
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    it.useVersion '1.3' //happy
+                }
+                eachDependency {
+                    throw new RuntimeException("Unhappy :(")
+                }
+            }
+"""
+
+        when:
+        def failure = runAndFail("resolveConf")
+
+        then:
+        failure.assertResolutionFailure(":conf")
+                .assertHasCause("Could not resolve org.utils:impl:1.3.")
+                .assertHasCause("Unhappy :(")
+                .assertFailedDependencyRequiredBy(":root:1.0")
+    }
+
+    void "can substitute module name and resolve conflict"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b",  '2.0').publish()
+        mavenRepo.module("org.utils", "b",  '2.1').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:2.0'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.name == 'a') {
+                    it.useTarget(it.requested.group + ':b:2.1')
+                }
+            }
+
+            task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                assert !modules.find { it.id.module == 'a' }
+                def b = modules.find { it.id.module == 'b' }
+                assert b.id.version == '2.1'
+                assert b.selectionReason.conflictResolution
+                assert b.selectionReason.selectedByRule
+                assert !b.selectionReason.forced
+                assert b.selectionReason.description == 'selected by rule and conflict resolution'
+            }
+"""
+
+        when:
+        run("check", "dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""conf
++--- org.utils:a:1.2 -> org.utils:b:2.1
+\\--- org.utils:b:2.0 -> 2.1"""))
+    }
+
+    def "can substitute module group"()
+    {
+        mavenRepo.module("org", "a", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "c").publish()
+        //a1
+        //b->a2->c
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:b:1.0'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.group == 'foo') {
+                    it.useTarget('org:' + it.requested.name + ':' + it.requested.version)
+                }
+            }
+"""
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
++--- org:a:1.0 -> 2.0
+|    \\--- org:c:1.0
+\\--- foo:b:1.0 -> org:b:1.0
+     \\--- org:a:2.0 (*)"""))
+    }
+
+    def "can substitute module group, name and version"()
+    {
+        mavenRepo.module("org", "a", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "c").publish()
+        //a1
+        //b->a2->c
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:bar:baz'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                if (it.requested.group == 'foo') {
+                    it.useTarget group: 'org', name: 'b', version: '1.0'
+                }
+            }
+"""
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
++--- org:a:1.0 -> 2.0
+|    \\--- org:c:1.0
+\\--- foo:bar:baz -> org:b:1.0
+     \\--- org:a:2.0 (*)"""))
+    }
+
+    def "provides decent feedback when target module incorrectly specified"()
+    {
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:bar:baz'
+            }
+
+            configurations.conf.resolutionStrategy.eachDependency {
+                it.useTarget "foobar"
+            }
+"""
+
+        when:
+        runAndFail("dependencies")
+
+        then:
+        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()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "b", "2.5").publish()
+        mavenRepo.module("org", "b", "3.0").publish()
+        mavenRepo.module("org", "b", "4.0").publish()
+
+        /*
+        I agree this dependency set is awkward but it is the simplest reproducible scenario
+        a:1.0
+        a:2.0 -> b:2.5
+        b:3.0
+        b:4.0
+
+        the conflict resolution of b:
+        1st pass: b:3 vs b:4(wins)
+        2nd pass: b:2.5 vs b:4(wins *again*)
+        */
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:b:3.0', 'org:b:4.0', 'org:a:1.0', 'org:a:2.0'
+            }
+
+            task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                assert modules.find { it.id.module == 'b' && it.id.version == '4.0' && it.selectionReason.conflictResolution }
+            }
+"""
+
+        expect:
+        run("check")
+    }
+
+    String getCommon() {
+        """configurations { conf }
+        repositories {
+            maven { url "${mavenRepo.uri}" }
+        }
+        task resolveConf << { configurations.conf.files }
+
+        //resolving the configuration at the end:
+        gradle.startParameter.taskNames += 'resolveConf'
+        """
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy
new file mode 100644
index 0000000..350b51e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy
@@ -0,0 +1,1417 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Ignore
+import spock.lang.Unroll
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class DependencySubstitutionRulesIntegrationTest extends AbstractIntegrationSpec {
+
+    void "forces multiple modules by rule"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5') publish()
+        mavenRepo.module("org.utils", "optional-lib", '5.0').publish()
+
+        //above models the scenario where org.utils:api and org.utils:impl are libraries that must be resolved with the same version
+        //however due to the conflict resolution, org.utils:api:1.5 and org.utils.impl:1.3 are resolved.
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.stuff:foo:2.0', 'org.utils:impl:1.3', 'org.utils:optional-lib:5.0'
+            }
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        if (it.requested.group == 'org.utils' && it.requested.module != 'optional-lib') {
+                            it.useVersion '1.5'
+                        }
+                    }
+                }
+	            failOnVersionConflict()
+	        }
+"""
+
+        when:
+        run("resolveConf")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "module forced by rule has correct selection reason"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'impl', '1.3') publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.stuff:foo:2.0'
+            }
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        if (it.requested.group == 'org.utils') {
+                            it.useVersion '1.5'
+                        }
+                    }
+                }
+	        }
+
+	        task check << {
+	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
+	            assert deps*.selected.id.module == ['foo', 'impl', 'api']
+	            assert deps*.selected.id.version == ['2.0', '1.5', '1.5']
+	            assert deps*.selected.selectionReason.forced         == [false, false, false]
+	            assert deps*.selected.selectionReason.selectedByRule == [false, true, true]
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    @Ignore("Deprecation not yet added")
+    void "warns about using deprecated resolution rules"()
+    {
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                eachDependency {
+                    it.useVersion "1.5"
+                }
+	        }
+"""
+
+        executer.withDeprecationChecksDisabled()
+
+        when:
+        succeeds()
+
+        then:
+        output.contains("The ResolutionStrategy.eachDependency() method has been deprecated and is scheduled to be removed in Gradle 3.0. Please use the DependencySubstitution.eachModule() method instead.")
+    }
+
+    void "all rules are executed in order and last one wins"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        assert it.target == it.requested
+                        it.useVersion '1.4'
+                    }
+                    eachModule {
+                        assert it.target.version == '1.4'
+                        assert it.target.module == it.requested.module
+                        assert it.target.group == it.requested.group
+                        it.useVersion '1.5'
+                    }
+                    eachModule {
+                        assert it.target.version == '1.5'
+                        //don't change the version
+                    }
+                }
+	        }
+
+	        task check << {
+	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+	                assert it.selected.id.version == '1.5'
+	                assert it.selected.selectionReason.selectedByRule
+	                assert it.selected.selectionReason.description == 'selected by rule'
+	            }
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "all rules are executed in order and last one wins, including deprecated resolution rules"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        assert it.target == it.requested
+                        it.useVersion '1.4'
+                    }
+                }
+                eachDependency {
+                    assert it.target.version == '1.4'
+                    assert it.target.name == it.requested.name
+                    assert it.target.group == it.requested.group
+                    it.useVersion '1.5'
+                }
+                dependencySubstitution {
+                    eachModule {
+                        assert it.target.version == '1.5'
+                        //don't change the version
+                    }
+                }
+	        }
+
+	        task check << {
+	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+	                assert it.selected.id.version == '1.5'
+	                assert it.selected.selectionReason.selectedByRule
+	                assert it.selected.selectionReason.description == 'selected by rule'
+	            }
+	        }
+"""
+        executer.withDeprecationChecksDisabled()
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can unforce the version"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5", "org.utils:api:1.5")
+
+                dependencySubstitution {
+    	            eachModule {
+                        it.useVersion it.requested.version
+	                }
+                }
+	        }
+
+	        task check << {
+	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+	                assert it.selected.id.version == '1.3'
+                    def reason = it.selected.selectionReason
+                    assert !reason.forced
+                    assert reason.selectedByRule
+	            }
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule are applied after forced modules"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5", "org.utils:api:1.5")
+
+                dependencySubstitution {
+                    eachModule {
+                        assert it.target.version == '1.5'
+                        it.useVersion '1.3'
+                    }
+                }
+	        }
+
+	        task check << {
+	            def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.size() == 2
+                deps.each {
+	                assert it.selected.id.version == '1.3'
+                    def reason = it.selected.selectionReason
+                    assert !reason.forced
+                    assert reason.selectedByRule
+	            }
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "forced modules and rules coexist"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                force("org.utils:impl:1.5")
+
+                dependencySubstitution {
+                    withModule("org.utils:api") {
+                        assert it.target == it.requested
+                        it.useVersion '1.5'
+                    }
+                }
+	        }
+
+	        task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies
+                assert deps.find {
+                    it.selected.id.module == 'impl' &&
+                    it.selected.id.version == '1.5' &&
+                    it.selected.selectionReason.forced &&
+                    !it.selected.selectionReason.selectedByRule
+                }
+
+                assert deps.find {
+	                it.selected.id.module == 'api' &&
+                    it.selected.id.version == '1.5' &&
+                    !it.selected.selectionReason.forced &&
+                    it.selected.selectionReason.selectedByRule
+	            }
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule selects a dynamic version"()
+    {
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.4').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                it.useVersion '1.+'
+	        }
+
+	        task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                assert deps[0].requested.version == '1.3'
+                assert deps[0].selected.id.version == '1.5'
+                assert !deps[0].selected.selectionReason.forced
+                assert deps[0].selected.selectionReason.selectedByRule
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can replace external dependency with project dependency"()
+    {
+        settingsFile << 'include "api", "impl"'
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf group: "org.utils", name: "api", version: "1.5", configuration: "conf"
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule(group: "org.utils", name: "api") {
+                    it.useTarget project(":api")
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                    assert deps[0].selected.componentId == projectId(":api")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert deps[0].selected.selectionReason.selectedByRule
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can access built artifacts from substituted project dependency"()
+    {
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":api") {
+                task build << {
+                    mkdir(projectDir)
+                    file("artifact.txt") << "Lajos"
+                }
+
+                artifacts {
+                    conf (file("artifact.txt")) {
+                        builtBy build
+                    }
+                }
+            }
+
+            project(":impl") {
+                dependencies {
+                    conf group: "org.utils", name: "api", version: "1.5"
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule(group: "org.utils", name: "api") {
+                    it.useTarget project(":api")
+                }
+
+                task check(dependsOn: configurations.conf) << {
+                    def files = configurations.conf.files
+                    assert files*.name.sort() == ["artifact.txt"]
+                    assert files*.text.sort() == ["Lajos"]
+                }
+                // TODO:PREZI Remove this when PR#412 is merged
+                check.dependsOn project(":api").build
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can replace project dependency with external dependency"()
+    {
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf project(path: ":api", configuration: "default")
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withProject(":api") {
+                    it.useTarget group: "org.utils", name: "api", version: "1.5"
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(projectId(":api"))
+                    assert deps[0].selected.componentId == moduleId("org.utils", "api", "1.5")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert deps[0].selected.selectionReason.selectedByRule
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can replace transitive external dependency with project dependency"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+        settingsFile << 'include "api", "test"'
+
+        buildFile << """
+            $common
+
+            project(":test") {
+                dependencies {
+                    conf group: "org.utils", name: "impl", version: "1.5"
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule("org.utils:api") {
+                    it.useTarget project(":api")
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 2
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.selected.componentId == moduleId("org.utils", "impl", "1.5")
+                        !it.selected.selectionReason.forced &&
+                        !it.selected.selectionReason.selectedByRule
+                    }
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                        it.selected.componentId == projectId(":api")
+                        !it.selected.selectionReason.forced &&
+                        it.selected.selectionReason.selectedByRule
+                    }
+                }
+            }
+"""
+
+        expect:
+        succeeds("test:check")
+    }
+
+    void "can replace client module dependency with project dependency"()
+    {
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf module(group: "org.utils", name: "api", version: "1.5")
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule("org.utils:api") {
+                    it.useTarget project(":api")
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                    assert deps[0].selected.componentId == projectId(":api")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert deps[0].selected.selectionReason.selectedByRule
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can replace client module's transitive dependency with project dependency"()
+    {
+        settingsFile << 'include "api", "impl"'
+        mavenRepo.module("org.utils", "bela", '1.5').publish()
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf module(group: "org.utils", name: "bela", version: "1.5") {
+                        dependencies group: "org.utils", name: "api", version: "1.5"
+                    }
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule("org.utils:api") {
+                    it.useTarget project(":api")
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 2
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.selected.componentId == moduleId("org.utils", "bela", "1.5") &&
+                        !it.selected.selectionReason.forced &&
+                        !it.selected.selectionReason.selectedByRule
+                    }
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "api", "1.5")) &&
+                        it.selected.componentId == projectId(":api") &&
+                        !it.selected.selectionReason.forced &&
+                        it.selected.selectionReason.selectedByRule
+                    }
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can replace external dependency declared in extended configuration with project dependency"()
+    {
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                configurations {
+                    testConf.extendsFrom conf
+                }
+
+                dependencies {
+                    conf group: "org.utils", name: "api", version: "1.5"
+                }
+
+                configurations.testConf.resolutionStrategy.dependencySubstitution.withModule("org.utils:api") {
+                    it.useTarget project(":api")
+                }
+
+                task checkConf << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                    assert deps[0].selected.componentId == moduleId("org.utils", "api", "1.5")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert !deps[0].selected.selectionReason.selectedByRule
+                }
+
+                task checkTestConf << {
+                    def deps = configurations.testConf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                    assert deps[0].selected.componentId == projectId(":api")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert deps[0].selected.selectionReason.selectedByRule
+                }
+
+                task check(dependsOn: [ checkConf, checkTestConf ])
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "can replace forced external dependency with project dependency"()
+    {
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf group: "org.utils", name: "api", version: "1.5"
+                }
+
+                configurations.conf.resolutionStrategy {
+                    force("org.utils:api:1.3")
+
+                    dependencySubstitution.withModule("org.utils:api") {
+                        it.useTarget project(":api")
+                    }
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps[0] instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult
+
+                    assert deps[0].requested.matchesStrictly(moduleId("org.utils", "api", "1.5"))
+                    assert deps[0].selected.componentId == projectId(":api")
+
+                    assert !deps[0].selected.selectionReason.forced
+                    assert deps[0].selected.selectionReason.selectedByRule
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "replacing external module dependency with project dependency keeps the original configuration"()
+    {
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":api") {
+                configurations {
+                    archives
+                }
+
+                artifacts {
+                    archives file("archives.txt")
+                    conf file("conf.txt")
+                }
+            }
+
+            project(":impl") {
+                configurations {
+                    compile
+                }
+
+                dependencies {
+                    compile group: "org.utils", name: "api", version: "1.5", configuration: "conf"
+                }
+
+                configurations.compile.resolutionStrategy.dependencySubstitution.withModule("org.utils:api") {
+                    it.useTarget project(":api")
+                }
+
+                task check << {
+                    def files = configurations.compile.files
+                    assert files*.name.sort() == ["conf.txt"]
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    void "replacing external module dependency with project dependency keeps the original transitivity"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.5').dependsOn('org.utils', 'api', '1.5').publish()
+        settingsFile << 'include "impl", "test"'
+
+        buildFile << """
+            $common
+
+            project(":test") {
+                dependencies {
+                    conf (group: "org.utils", name: "impl", version: "1.5") { transitive = false }
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withModule("org.utils:impl") {
+                    it.useTarget project(":impl")
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 1
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "impl", "1.5"))
+                        it.selected.componentId == projectId(":impl")
+                        !it.selected.selectionReason.forced &&
+                        it.selected.selectionReason.selectedByRule
+                    }
+                }
+            }
+"""
+
+        expect:
+        succeeds("test:check")
+    }
+
+    void "external dependency substituted for a project dependency participates in conflict resolution"()
+    {
+        mavenRepo.module("org.utils", "api", '2.0').publish()
+
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":impl") {
+                dependencies {
+                    conf project(":api")
+                    conf "org.utils:api:2.0"
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.withProject(":api") {
+                    it.useTarget "org.utils:api:1.6"
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 2
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(projectId(":api")) &&
+                        it.selected.componentId == moduleId("org.utils", "api", "2.0") &&
+                        !it.selected.selectionReason.forced &&
+                        !it.selected.selectionReason.selectedByRule &&
+                        it.selected.selectionReason.conflictResolution
+                    }
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "api", "2.0")) &&
+                        it.selected.componentId == moduleId("org.utils", "api", "2.0") &&
+                        !it.selected.selectionReason.forced &&
+                        !it.selected.selectionReason.selectedByRule &&
+                        it.selected.selectionReason.conflictResolution
+                    }
+
+                    def resolvedDeps = configurations.conf.resolvedConfiguration.firstLevelModuleDependencies
+                    resolvedDeps.size() == 1
+                    resolvedDeps[0].module.id == moduleId("org.utils", "api", "2.0")
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+    }
+
+    @Unroll
+    void "project dependency substituted for an external dependency participates in conflict resolution (version #apiProjectVersion)"()
+    {
+        mavenRepo.module("org.utils", "api", '2.0').publish()
+        settingsFile << 'include "api", "impl"'
+
+        buildFile << """
+            $common
+
+            project(":api") {
+                group "org.utils"
+                version = $apiProjectVersion
+            }
+
+            project(":impl") {
+                dependencies {
+                    conf "org.utils:api:1.5"
+                    conf "org.utils:api:2.0"
+                }
+
+                configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                    if (it.requested.module == "api" && it.requested.version == "1.5") {
+                        it.useTarget project(":api")
+                    }
+                }
+
+                task check << {
+                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                    assert deps.size() == 2
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "api", "1.5")) &&
+                        it.selected.componentId == $winner &&
+                        !it.selected.selectionReason.forced &&
+                        it.selected.selectionReason.selectedByRule == $selectedByRule &&
+                        it.selected.selectionReason.conflictResolution
+                    }
+                    assert deps.find {
+                        it instanceof org.gradle.api.artifacts.result.ResolvedDependencyResult &&
+                        it.requested.matchesStrictly(moduleId("org.utils", "api", "2.0")) &&
+                        it.selected.componentId == $winner &&
+                        !it.selected.selectionReason.forced &&
+                        it.selected.selectionReason.selectedByRule == $selectedByRule &&
+                        it.selected.selectionReason.conflictResolution
+                    }
+                }
+            }
+"""
+
+        expect:
+        succeeds("impl:check")
+
+        where:
+        apiProjectVersion | winner                                | selectedByRule
+        "1.6"             | 'moduleId("org.utils", "api", "2.0")' | false
+        "3.0"             | 'projectId(":api")'                   | true
+    }
+
+    void "can blacklist a version"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.4').publish()
+        mavenRepo.module("org.utils", "a",  '1.3').publish()
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                // a:1.2 is blacklisted, 1.4 should be used instead:
+                if (it.requested.module == 'a' && it.requested.version == '1.2') {
+                    it.useVersion '1.4'
+                }
+	        }
+
+	        task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                def a = modules.find { it.id.module == 'a' }
+                assert a.id.version == '1.4'
+                assert a.selectionReason.conflictResolution
+                assert a.selectionReason.selectedByRule
+                assert !a.selectionReason.forced
+                assert a.selectionReason.description == 'selected by rule and conflict resolution'
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "can blacklist a version that is not used"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.3').publish()
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b", '1.3').dependsOn("org.utils", "a", "1.3").publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                // a:1.2 is blacklisted, 1.2.1 should be used instead:
+                if (it.requested.module == 'a' && it.requested.version == '1.2') {
+                    it.useVersion '1.2.1'
+                }
+	        }
+
+	        task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                def a = modules.find { it.id.module == 'a' }
+                assert a.id.version == '1.3'
+                assert a.selectionReason.conflictResolution
+                assert !a.selectionReason.selectedByRule
+                assert !a.selectionReason.forced
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can use custom versioning scheme"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:default'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                if (it.requested.version == 'default') {
+                    it.useVersion '1.3'
+                }
+	        }
+
+	        task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                deps[0].requested.version == 'default'
+                deps[0].selected.id.version == '1.3'
+                deps[0].selected.selectionReason.selectedByRule
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can use custom versioning scheme for transitive dependencies"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', 'default').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                if (it.requested.version == 'default') {
+                    it.useVersion '1.3'
+                }
+	        }
+
+	        task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 2
+                def api = deps.find { it.requested.module == 'api' }
+                api.requested.version == 'default'
+                api.selected.id.version == '1.3'
+                api.selected.selectionReason.selectedByRule
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "rule selects unavailable version"()
+    {
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:api:1.3'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                it.useVersion '1.123.15' //does not exist
+	        }
+
+	        task check << {
+                def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
+                assert deps.size() == 1
+                assert deps[0].attempted.group == 'org.utils'
+                assert deps[0].attempted.module == 'api'
+                assert deps[0].attempted.version == '1.123.15'
+                assert deps[0].attemptedReason.selectedByRule
+                assert deps[0].failure.message.contains('1.123.15')
+                assert deps[0].requested.version == '1.3'
+	        }
+"""
+
+        when:
+        def failure = runAndFail("check", "resolveConf")
+
+        then:
+        failure.assertResolutionFailure(":conf")
+            .assertHasCause("Could not find org.utils:api:1.123.15")
+    }
+
+    void "rules triggered exactly once per the same dependency"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.5').publish()
+
+        mavenRepo.module("org.stuff", "foo", '2.0').dependsOn('org.utils', 'api', '1.5').publish()
+        mavenRepo.module("org.stuff", "bar", '2.0').dependsOn('org.utils', 'impl', '1.3').publish()
+
+        /*
+        dependencies:
+
+        impl:1.3->api:1.3
+        foo->api:1.5
+        bar->impl:1.3(*)->api:1.3(*)
+
+        * - should be excluded as it was already visited
+        */
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3', 'org.stuff:foo:2.0', 'org.stuff:bar:2.0'
+            }
+
+            List requested = []
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        requested << "\$it.requested.module:\$it.requested.version"
+                    }
+                }
+	        }
+
+	        task check << {
+                configurations.conf.resolve()
+                assert requested == ['impl:1.3', 'foo:2.0', 'bar:2.0', 'api:1.3', 'api:1.5']
+	        }
+"""
+
+        when:
+        run("check")
+
+        then:
+        noExceptionThrown()
+    }
+
+    void "runtime exception when evaluating rule yields decent exception"()
+    {
+        mavenRepo.module("org.utils", "impl", '1.3').dependsOn('org.utils', 'api', '1.3').publish()
+        mavenRepo.module("org.utils", "api", '1.3').publish()
+
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+            version = 1.0
+
+            $common
+
+            dependencies {
+                conf 'org.utils:impl:1.3'
+            }
+
+            configurations.conf.resolutionStrategy {
+                dependencySubstitution {
+                    eachModule {
+                        it.useVersion '1.3' //happy
+                    }
+                    eachModule {
+                        throw new RuntimeException("Unhappy :(")
+                    }
+                }
+	        }
+"""
+
+        when:
+        def failure = runAndFail("resolveConf")
+
+        then:
+        failure.assertResolutionFailure(":conf")
+                .assertHasCause("Could not resolve org.utils:impl:1.3.")
+                .assertHasCause("Unhappy :(")
+                .assertFailedDependencyRequiredBy(":root:1.0")
+    }
+
+    void "can substitute module name and resolve conflict"()
+    {
+        mavenRepo.module("org.utils", "a",  '1.2').publish()
+        mavenRepo.module("org.utils", "b",  '2.0').publish()
+        mavenRepo.module("org.utils", "b",  '2.1').publish()
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org.utils:a:1.2', 'org.utils:b:2.0'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.withModule("org.utils:a") {
+                it.useTarget(it.requested.group + ':b:2.1')
+	        }
+
+	        task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                assert !modules.find { it.id.module == 'a' }
+                def b = modules.find { it.id.module == 'b' }
+                assert b.id.version == '2.1'
+                assert b.selectionReason.conflictResolution
+                assert b.selectionReason.selectedByRule
+                assert !b.selectionReason.forced
+                assert b.selectionReason.description == 'selected by rule and conflict resolution'
+	        }
+"""
+
+        when:
+        run("check", "dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""conf
++--- org.utils:a:1.2 -> org.utils:b:2.1
+\\--- org.utils:b:2.0 -> 2.1"""))
+    }
+
+    def "can substitute module group"()
+    {
+        mavenRepo.module("org", "a", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "c").publish()
+        //a1
+        //b->a2->c
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:b:1.0'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                if (it.requested.group == 'foo') {
+                    it.useTarget('org:' + it.requested.module + ':' + it.requested.version)
+                }
+	        }
+"""
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
++--- org:a:1.0 -> 2.0
+|    \\--- org:c:1.0
+\\--- foo:b:1.0 -> org:b:1.0
+     \\--- org:a:2.0 (*)"""))
+    }
+
+    def "can substitute module group, name and version"()
+    {
+        mavenRepo.module("org", "a", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "c").publish()
+        //a1
+        //b->a2->c
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:bar:baz'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                if (it.requested.group == 'foo') {
+                    it.useTarget group: 'org', name: 'b', version: '1.0'
+                }
+	        }
+"""
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
++--- org:a:1.0 -> 2.0
+|    \\--- org:c:1.0
+\\--- foo:bar:baz -> org:b:1.0
+     \\--- org:a:2.0 (*)"""))
+    }
+
+    def "provides decent feedback when target module incorrectly specified"()
+    {
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:a:1.0', 'foo:bar:baz'
+            }
+
+            configurations.conf.resolutionStrategy.dependencySubstitution.eachModule {
+                it.useTarget "foobar"
+	        }
+"""
+
+        when:
+        runAndFail("dependencies")
+
+        then:
+        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.dependencySubstitution.eachModule {
+                if (it.requested.module == '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()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "b", "2.5").publish()
+        mavenRepo.module("org", "b", "3.0").publish()
+        mavenRepo.module("org", "b", "4.0").publish()
+
+        /*
+        I agree this dependency set is awkward but it is the simplest reproducible scenario
+        a:1.0
+        a:2.0 -> b:2.5
+        b:3.0
+        b:4.0
+
+        the conflict resolution of b:
+        1st pass: b:3 vs b:4(wins)
+        2nd pass: b:2.5 vs b:4(wins *again*)
+        */
+
+        buildFile << """
+            $common
+
+            dependencies {
+                conf 'org:b:3.0', 'org:b:4.0', 'org:a:1.0', 'org:a:2.0'
+            }
+
+            task check << {
+                def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
+                assert modules.find { it.id.module == 'b' && it.id.version == '4.0' && it.selectionReason.conflictResolution }
+            }
+"""
+
+        expect:
+        run("check")
+    }
+
+    String getCommon() {
+        """
+        allprojects {
+            configurations {
+                conf
+            }
+            configurations.create("default").extendsFrom(configurations.conf)
+
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+
+            task resolveConf << { configurations.conf.files }
+        }
+
+        //resolving the configuration at the end:
+        gradle.startParameter.taskNames += 'resolveConf'
+
+        def moduleId(String group, String name, String version) {
+            return org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier.newId(group, name, version)
+        }
+
+        def projectId(String projectPath) {
+            return org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier.newId(projectPath)
+        }
+        """
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ExtendingConfigurationsIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..0caecc3
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class FlatDirJvmLibraryArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
+    JvmLibraryArtifactResolveTestFixture fixture
+
+    def setup() {
+        buildFile << """
+repositories {
+    flatDir { dir 'repo' }
+}
+"""
+        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
+    }
+
+    def "resolves and does not cache source and javadoc artifacts"() {
+        publishModule()
+        fixture.expectSourceArtifact("sources")
+                .expectJavadocArtifact("javadoc")
+                .prepare()
+
+        when:
+        succeeds("verify")
+
+        and:
+        def snapshot = file("sources/some-artifact-1.0-sources.jar").snapshot()
+
+        and:
+        publishChanged()
+
+        then:
+        succeeds("verify")
+        file("sources/some-artifact-1.0-sources.jar").assertHasChangedSince(snapshot)
+    }
+
+    def "resolves artifacts of non-existing component"() {
+        def location1 = file("repo/some-artifact-1.0.jar").toURL()
+        def location2 = file("repo/some-artifact.jar").toURL()
+
+        fixture.expectComponentNotFound().prepare()
+
+        expect:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${location1}
+    ${location2}""")
+    }
+
+    def "resolve missing source and javadoc artifacts"() {
+        file("repo/some-artifact-1.0.jar").createFile()
+
+        fixture.prepare()
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "resolve partially missing artifacts"() {
+        file("repo/some-artifact-1.0.jar").createFile()
+        file("repo/some-artifact-1.0-sources.jar").createFile()
+
+        fixture.expectSourceArtifact("sources")
+                .prepare()
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "can only resolve component if main artifact exists"() {
+        file("repo/some-artifact-1.0-sources.jar").createFile()
+        file("repo/some-artifact-1.0-javadoc.jar").createFile()
+        def location1 = file("repo/some-artifact-1.0.jar").toURL().toString()
+        def location2 = file("repo/some-artifact.jar").toURL().toString()
+
+        fixture.expectComponentNotFound().prepare()
+
+        expect:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${location1}
+    ${location2}""")
+    }
+
+    private publishModule() {
+        file("repo/some-artifact-1.0.jar").createFile()
+        file("repo/some-artifact-1.0-sources.jar").createFile()
+        file("repo/some-artifact-1.0-javadoc.jar").createFile()
+    }
+
+    private publishChanged() {
+        file("repo/some-artifact-1.0.jar") << "more"
+        file("repo/some-artifact-1.0-sources.jar") << "more"
+        file("repo/some-artifact-1.0-javadoc.jar") << "more"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirResolveIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirResolveIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirResolveIntegrationTest.groovy
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy
new file mode 100644
index 0000000..dd24052
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/JvmLibraryArtifactResolveTestFixture.groovy
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.test.fixtures.file.TestFile
+
+/**
+ * A test fixture that injects a task into a build that uses the Artifact Query API to download some artifacts, validating the results.
+ */
+class JvmLibraryArtifactResolveTestFixture {
+    private final TestFile buildFile
+    private final String config
+    private ModuleComponentIdentifier id = DefaultModuleComponentIdentifier.newId("some.group", "some-artifact", "1.0")
+    private artifactTypes = []
+    private expectedSources = []
+    private expectedJavadoc = []
+    private boolean unresolvedComponentFailure
+
+    JvmLibraryArtifactResolveTestFixture(TestFile buildFile, String config = "compile") {
+        this.buildFile = buildFile
+        this.config = config
+    }
+
+    JvmLibraryArtifactResolveTestFixture withComponentVersion(String group, String module, String version) {
+        this.id = DefaultModuleComponentIdentifier.newId(group, module, version)
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture requestingSource() {
+        this.artifactTypes << "SourcesArtifact"
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture requestingJavadoc() {
+        this.artifactTypes << "JavadocArtifact"
+        this
+    }
+
+
+    JvmLibraryArtifactResolveTestFixture clearExpectations() {
+        unresolvedComponentFailure = false
+        this.expectedSources = []
+        this.expectedJavadoc = []
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectComponentNotFound() {
+        unresolvedComponentFailure = true
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectComponentResolutionFailure() {
+        unresolvedComponentFailure = true
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectSourceArtifact(String classifier) {
+        expectedSources << "${id.module}-${id.version}-${classifier}.jar"
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectSourceArtifactNotFound(String artifactClassifier) {
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectSourceArtifactFailure() {
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectJavadocArtifact(String classifier) {
+        expectedJavadoc << "${id.module}-${id.version}-${classifier}.jar"
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectJavadocArtifactNotFound(String artifactClassifier) {
+        this
+    }
+
+    JvmLibraryArtifactResolveTestFixture expectJavadocArtifactFailure() {
+        this
+    }
+
+    /**
+     * Injects the appropriate stuff into the build script.
+     */
+    void prepare() {
+        buildFile << """
+configurations {
+    ${config}
+}
+dependencies {
+    ${config} "${id.group}:${id.module}:${id.version}"
+}
+
+ at org.gradle.internal.exceptions.Contextual
+class VerificationException extends org.gradle.internal.exceptions.DefaultMultiCauseException {
+    public VerificationException(String message, Iterable<? extends Throwable> causes) {
+        super(message, causes)
+    }
+}
+"""
+        if (unresolvedComponentFailure) {
+            prepareComponentNotFound()
+        } else {
+            createVerifyTask("verify")
+        }
+    }
+
+    void createVerifyTask(String taskName) {
+        buildFile << """
+task $taskName << {
+    def deps = configurations.${config}.incoming.resolutionResult.allDependencies as List
+    assert deps.size() == 1
+    def componentId = deps[0].selected.id
+
+    def result = dependencies.createArtifactResolutionQuery()
+        .forComponents(componentId)
+        .withArtifacts(JvmLibrary, $artifactTypesString)
+        .execute()
+
+    assert result.components.size() == 1
+
+    // Check generic component result
+    def componentResult = result.components.iterator().next()
+    assert componentResult.id.displayName == "${id.displayName}"
+    assert componentResult.id.group == "${id.group}"
+    assert componentResult.id.module == "${id.module}"
+    assert componentResult.id.version == "${id.version}"
+    assert componentResult instanceof ComponentArtifactsResult
+
+    def failures = []
+
+    ${checkComponentResultArtifacts("componentResult", "sources", expectedSources)}
+    ${checkComponentResultArtifacts("componentResult", "javadoc", expectedJavadoc)}
+
+    if (!failures.empty) {
+        throw new VerificationException("Artifact resolution failed", failures)
+    }
+}
+"""
+    }
+
+    void prepareComponentNotFound() {
+        buildFile << """
+task verify << {
+    def componentId = new org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier("${id.group}", "${id.module}", "${id.version}")
+
+    def result = dependencies.createArtifactResolutionQuery()
+        .forComponents(componentId)
+        .withArtifacts(JvmLibrary, $artifactTypesString)
+        .execute()
+
+    assert result.components.size() == 1
+
+    // Check generic component result
+    def componentResult = result.components.iterator().next()
+    assert componentResult.id.displayName == "${id.displayName}"
+    assert componentResult.id.group == "${id.group}"
+    assert componentResult.id.module == "${id.module}"
+    assert componentResult.id.version == "${id.version}"
+    assert componentResult instanceof UnresolvedComponentResult
+
+    throw componentResult.failure
+}
+"""
+    }
+
+    private static String checkComponentResultArtifacts(String componentResult, String type, def expectedFiles) {
+        """
+    def ${type}ArtifactResultFiles = []
+    ${componentResult}.getArtifacts(${type.capitalize()}Artifact).each { artifactResult ->
+        if (artifactResult instanceof ResolvedArtifactResult) {
+            copy {
+                from artifactResult.file
+                into "${type}"
+            }
+            ${type}ArtifactResultFiles << artifactResult.file.name
+        } else {
+            failures << artifactResult.failure
+        }
+    }
+    assert ${type}ArtifactResultFiles as Set == ${toQuotedList(expectedFiles)} as Set
+"""
+    }
+
+    private static String toQuotedList(def values) {
+        return values.collect({"\"$it\""}).toListString()
+    }
+
+    private String getArtifactTypesString() {
+        if (artifactTypes.empty) {
+            return "SourcesArtifact,JavadocArtifact"
+        }
+        return artifactTypes.join(',')
+    }
+}
+
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/MetadataArtifactResolveTestFixture.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/MetadataArtifactResolveTestFixture.groovy
new file mode 100644
index 0000000..b1312ae
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/MetadataArtifactResolveTestFixture.groovy
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.artifacts.result.ComponentArtifactsResult
+import org.gradle.api.artifacts.result.ComponentResult
+import org.gradle.api.artifacts.result.UnresolvedComponentResult
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.test.fixtures.file.TestFile
+
+class MetadataArtifactResolveTestFixture {
+    private final TestFile buildFile
+    final String config
+    final ModuleComponentIdentifier id = DefaultModuleComponentIdentifier.newId('some.group', 'some-artifact', '1.0')
+    private String requestedComponent
+    private String requestedArtifact
+    private Class<? extends ComponentResult> expectedComponentResult
+    private Throwable expectedException
+    private Set<File> expectedMetadataFiles
+    private Class<? extends Throwable> expectedArtifactFailure
+    private String expectedArtifactFailureMessage
+
+    MetadataArtifactResolveTestFixture(TestFile buildFile, String config = "compile") {
+        this.buildFile = buildFile
+        this.config = config
+    }
+
+    void basicSetup() {
+        buildFile << """
+configurations {
+    $config
+}
+
+dependencies {
+    $config '$id.displayName'
+}
+"""
+    }
+
+    void configureChangingModule() {
+        buildFile << """
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            details.changing = true
+        }
+    }
+}
+
+if (project.hasProperty('nocache')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+"""
+    }
+
+    MetadataArtifactResolveTestFixture requestComponent(String component) {
+        this.requestedComponent = component
+        this
+    }
+
+    MetadataArtifactResolveTestFixture requestArtifact(String artifact) {
+        this.requestedArtifact = artifact
+        this
+    }
+
+    MetadataArtifactResolveTestFixture expectResolvedComponentResult() {
+        this.expectedComponentResult = ComponentArtifactsResult
+        this
+    }
+
+    MetadataArtifactResolveTestFixture expectUnresolvedComponentResult(Throwable expectedException) {
+        this.expectedComponentResult = UnresolvedComponentResult
+        this.expectedException = expectedException
+        this
+    }
+
+    MetadataArtifactResolveTestFixture expectMetadataFiles(File... metadataFiles) {
+        this.expectedMetadataFiles = metadataFiles as Set
+        this
+    }
+
+    MetadataArtifactResolveTestFixture expectNoMetadataFiles() {
+        expectMetadataFiles()
+    }
+
+    MetadataArtifactResolveTestFixture expectUnresolvedArtifactResult(Class<? extends Throwable> failure, String message) {
+        expectedArtifactFailure = failure
+        expectedArtifactFailureMessage = message
+        this
+    }
+
+    void createVerifyTaskModuleComponentIdentifier() {
+        buildFile << """
+task verify {
+    doLast {
+        def deps = configurations.${config}.incoming.resolutionResult.allDependencies as List
+        assert deps.size() == 1
+        def componentId = deps[0].selected.id
+
+        def result = dependencies.createArtifactResolutionQuery()
+            .forComponents(deps[0].selected.id)
+            .withArtifacts($requestedComponent, $requestedArtifact)
+            .execute()
+
+        assert result.components.size() == 1
+
+        ${createComponentResultVerificationCode()}
+"""
+
+        if(expectedComponentResult == UnresolvedComponentResult) {
+            buildFile << createUnresolvedComponentResultVerificationCode()
+        }
+
+        buildFile << """
+        def expectedMetadataFileNames = ${expectedMetadataFiles.collect { "'" + it.name + "'" }} as Set
+
+        for(component in result.resolvedComponents) {
+            def resolvedArtifacts = component.getArtifacts($requestedArtifact).findAll { it instanceof ResolvedArtifactResult }
+            assert expectedMetadataFileNames.size() == resolvedArtifacts.size()
+
+            ${createUnresolvedArtifactResultVerificationCode()}
+
+            if(expectedMetadataFileNames.size() > 0) {
+                def resolvedArtifactFileNames = resolvedArtifacts*.file.name as Set
+                assert resolvedArtifactFileNames == expectedMetadataFileNames
+            }
+        }
+    }
+}
+"""
+    }
+
+    private String createUnresolvedArtifactResultVerificationCode() {
+        if (expectedArtifactFailure != null) {
+            return """
+                def unResolvedArtifacts = component.getArtifacts($requestedArtifact).findAll { it instanceof UnresolvedArtifactResult }
+                assert unResolvedArtifacts.size() == 1
+                assert unResolvedArtifacts[0].failure instanceof ${expectedArtifactFailure.name}
+                assert unResolvedArtifacts[0].failure.message.startsWith("${expectedArtifactFailureMessage}")
+            """
+        }
+    }
+
+    private String createComponentResultVerificationCode() {
+        """
+        // Check generic component result
+        def componentResult = result.components.iterator().next()
+        assert componentResult.id.displayName == '$id.displayName'
+        assert componentResult instanceof $expectedComponentResult.name
+"""
+    }
+
+    private String createUnresolvedComponentResultVerificationCode() {
+        """
+        // Check unresolved component result
+        UnresolvedComponentResult unresolvedComponentResult = (UnresolvedComponentResult)componentResult
+        assert unresolvedComponentResult.failure instanceof ${expectedException.getClass().name}
+        assert unresolvedComponentResult.failure.message == "$expectedException.message"
+"""
+}
+
+    void createVerifyTaskForProjectComponentIdentifier() {
+        buildFile << """
+task verify {
+    doLast {
+        def rootId = configurations.${config}.incoming.resolutionResult.root.id
+        assert rootId instanceof ProjectComponentIdentifier
+
+        dependencies.createArtifactResolutionQuery()
+            .forComponents(rootId)
+            .withArtifacts($requestedComponent, $requestedArtifact)
+            .execute()
+    }
+}
+"""
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..e53f148
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.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.integtests.resolve
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import spock.lang.Issue
+
+class ProjectDependenciesIntegrationTest extends AbstractDependencyResolutionTest {
+
+    @Issue("GRADLE-2477") //this is a feature on its own but also covers one of the reported issues
+    def "resolving project dependency triggers configuration of the target project"() {
+        settingsFile << "include 'impl'"
+        buildFile << """
+            apply plugin: 'java'
+            dependencies {
+                compile project(":impl")
+            }
+            repositories {
+                //resolving project must declare the repo
+                maven { url '${mavenRepo.uri}' }
+            }
+            println "Resolved at configuration time: " + configurations.compile.files*.name
+        """
+
+        mavenRepo.module("org", "foo").publish()
+        file("impl/build.gradle") << """
+            apply plugin: 'java'
+            dependencies {
+                compile "org:foo:1.0"
+            }
+        """
+
+        when:
+        run()
+
+        then:
+        result.output.contains "Resolved at configuration time: [impl.jar, foo-1.0.jar]"
+    }
+
+    def "configuring project dependencies by map is validated"() {
+        settingsFile << "include 'impl'"
+        buildFile << """
+            allprojects { configurations.create('conf') }
+            task extraKey << {
+                dependencies.project(path: ":impl", configuration: ":conf", foo: "bar")
+            }
+            task missingPath << {
+                dependencies.project(paths: ":impl", configuration: ":conf")
+            }
+            task missingConfiguration << {
+                dependencies.project(path: ":impl")
+            }
+        """
+
+        when:
+        runAndFail("extraKey")
+
+        then:
+        failureHasCause("No such property: foo for class: ")
+
+        when:
+        run("missingConfiguration")
+
+        then:
+        noExceptionThrown()
+
+        when:
+        runAndFail("missingPath")
+
+        then:
+        failureHasCause("Required keys [path] are missing from map")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..a5a9b84
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
@@ -0,0 +1,500 @@
+/*
+ * 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 org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+
+class ProjectDependencyResolveIntegrationTest extends AbstractIntegrationSpec {
+
+    public void "project dependency includes artifacts and transitive dependencies of default configuration in target project"() {
+        given:
+        mavenRepo.module("org.other", "externalA", 1.2).publish()
+        mavenRepo.module("org.other", "externalB", 2.1).publish()
+
+        and:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        buildFile << """
+allprojects {
+    repositories { maven { url '$mavenRepo.uri' } }
+}
+project(":a") {
+    configurations {
+        api
+        'default' { extendsFrom api }
+    }
+    dependencies {
+        api "org.other:externalA:1.2"
+        'default' "org.other:externalB:2.1"
+    }
+    task jar(type: Jar) { baseName = 'a' }
+    artifacts { api jar }
+}
+project(":b") {
+    group = 'org.gradle'
+    version = '1.0'
+
+    configurations {
+        compile
+    }
+    dependencies {
+        compile project(':a')
+    }
+
+    task check(dependsOn: configurations.compile) << {
+        assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar', 'externalB-2.1.jar']
+        def result = configurations.compile.incoming.resolutionResult
+
+         // Check root component
+        def rootId = result.root.id
+        assert rootId instanceof ProjectComponentIdentifier
+        def rootPublishedAs = result.root.moduleVersion
+        assert rootPublishedAs.group == 'org.gradle'
+        assert rootPublishedAs.name == 'b'
+        assert rootPublishedAs.version == '1.0'
+
+        // Check project components
+        def projectComponents = result.root.dependencies.selected.findAll { it.id instanceof ProjectComponentIdentifier }
+        assert projectComponents.size() == 1
+        def projectA = projectComponents[0]
+        assert projectA.id.projectPath == ':a'
+        assert projectA.moduleVersion.group != null
+        assert projectA.moduleVersion.name == 'a'
+        assert projectA.moduleVersion.version == 'unspecified'
+
+        // Check project dependencies
+        def projectDependencies = result.root.dependencies.requested.findAll { it instanceof ProjectComponentSelector }
+        assert projectDependencies.size() == 1
+        def projectDependency = projectDependencies[0]
+        assert projectDependency.projectPath == ':a'
+
+        // Check external module components
+        def externalComponents = result.allDependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
+        assert externalComponents.size() == 2
+        def externalA = externalComponents[0]
+        assert externalA.id.group == 'org.other'
+        assert externalA.id.module == 'externalA'
+        assert externalA.id.version == '1.2'
+        assert externalA.moduleVersion.group == 'org.other'
+        assert externalA.moduleVersion.name == 'externalA'
+        assert externalA.moduleVersion.version == '1.2'
+        def externalB = externalComponents[1]
+        assert externalB.id.group == 'org.other'
+        assert externalB.id.module == 'externalB'
+        assert externalB.id.version == '2.1'
+        assert externalB.moduleVersion.group == 'org.other'
+        assert externalB.moduleVersion.name == 'externalB'
+        assert externalB.moduleVersion.version == '2.1'
+    }
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    public void "project dependency that specifies a target configuration includes artifacts and transitive dependencies of selected configuration"() {
+        given:
+        mavenRepo.module("org.other", "externalA", 1.2).publish()
+
+        and:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        buildFile << """
+allprojects {
+    repositories { maven { url '$mavenRepo.uri' } }
+}
+project(":a") {
+    configurations {
+        api
+        runtime { extendsFrom api }
+    }
+    dependencies {
+        api "org.other:externalA:1.2"
+    }
+    task jar(type: Jar) { baseName = 'a' }
+    artifacts { api jar }
+}
+project(":b") {
+    configurations {
+        compile
+    }
+    dependencies {
+        compile project(path: ':a', configuration: 'runtime')
+    }
+    task check(dependsOn: configurations.compile) << {
+        assert configurations.compile.collect { it.name } == ['a.jar', 'externalA-1.2.jar']
+    }
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    @Issue("GRADLE-2899")
+    public void "consuming project can refer to multiple configurations of target project"() {
+        given:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        buildFile << """
+project(':a') {
+    configurations {
+        configA1
+        configA2
+    }
+    task A1jar(type: Jar) {
+        archiveName = 'A1.jar'
+    }
+    task A2jar(type: Jar) {
+        archiveName = 'A2.jar'
+    }
+    artifacts {
+        configA1 A1jar
+        configA2 A2jar
+    }
+}
+
+project(':b') {
+    configurations {
+        configB1
+        configB2
+    }
+    dependencies {
+        configB1 project(path:':a', configuration:'configA1')
+        configB2 project(path:':a', configuration:'configA2')
+    }
+    task check << {
+        assert configurations.configB1.collect { it.name } == ['A1.jar']
+        assert configurations.configB2.collect { it.name } == ['A2.jar']
+    }
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    public void "resolved project artifacts reflect late changes in project attributes"() {
+        given:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        file('a/build.gradle') << '''
+            apply plugin: 'base'
+            configurations { compile }
+            task aJar(type: Jar) { }
+            artifacts { compile aJar }
+'''
+        file('b/build.gradle') << '''
+            apply plugin: 'base'
+            version = 'early'
+            configurations { compile }
+            task bJar(type: Jar) { }
+            gradle.taskGraph.whenReady { project.version = 'late' }
+            artifacts { compile bJar }
+'''
+        file('build.gradle') << '''
+            configurations { compile }
+            dependencies { compile project(path: ':a', configuration: 'compile'), project(path: ':b', configuration: 'compile') }
+            task test(dependsOn: configurations.compile) << {
+                assert configurations.compile.collect { it.name } == ['a.jar', 'b-late.jar']
+            }
+'''
+
+        expect:
+        succeeds "test"
+    }
+
+    public void "resolved project artifact can be changed by configuration task"() {
+        given:
+        file('settings.gradle') << "include 'a'"
+
+        and:
+        file('a/build.gradle') << '''
+            apply plugin: 'base'
+            configurations { compile }
+            task configureJar << {
+                tasks.aJar.archiveName = "modified-artifact.txt"
+            }
+            task aJar(type: Jar) {
+                dependsOn configureJar
+            }
+            artifacts { compile aJar }
+'''
+        file('build.gradle') << '''
+            configurations {
+                compile
+                testCompile { extendsFrom compile }
+            }
+            dependencies { compile project(path: ':a', configuration: 'compile') }
+            task test(dependsOn: [configurations.compile, configurations.testCompile]) << {
+                assert configurations.compile.collect { it.name } == ['modified-artifact.txt']
+                assert configurations.testCompile.collect { it.name } == ['modified-artifact.txt']
+            }
+'''
+
+        expect:
+        succeeds "test"
+    }
+
+
+    public void "set of resolved project artifacts can be changed after task graph is resolved"() {
+        given:
+        file('settings.gradle') << "include 'a'"
+
+        and:
+        file('a/build.gradle') << '''
+            apply plugin: 'base'
+            configurations { compile }
+            task jar1(type: Jar) {
+                classifier '1'
+            }
+            task jar2(type: Jar) {
+                classifier '2'
+            }
+            artifacts { compile tasks.jar1 }
+            gradle.taskGraph.whenReady {
+                configurations.compile.artifacts.clear()
+                artifacts { compile tasks.jar2 }
+            }
+'''
+        file('build.gradle') << '''
+            configurations { compile }
+            dependencies { compile project(path: ':a', configuration: 'compile') }
+            task test(dependsOn: configurations.compile) << {
+                assert configurations.compile.collect { it.name } == ['a-2.jar']
+            }
+'''
+
+        expect:
+        succeeds "test"
+    }
+
+    public void "project dependency that references an artifact includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
+        given:
+        mavenRepo.module("group", "externalA", 1.5).publish()
+
+        and:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        buildFile << """
+allprojects {
+    apply plugin: 'base'
+    repositories { maven { url '${mavenRepo.uri}' } }
+}
+
+project(":a") {
+    configurations { 'default' {} }
+    dependencies { 'default' 'group:externalA:1.5' }
+    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 = 'b'; type = 'jar' } } }
+    task test {
+        inputs.files configurations.compile
+        doFirst {
+            assert configurations.compile.files.collect { it.name } == ['b.jar', 'externalA-1.5.jar']
+        }
+    }
+}
+"""
+
+        expect:
+        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("Could not find b.jar (test:a:unspecified).")
+    }
+
+    public void "non-transitive project dependency includes only the artifacts of the target configuration"() {
+        given:
+        mavenRepo.module("group", "externalA", 1.5).publish()
+
+        and:
+        file('settings.gradle') << "include 'a', 'b'"
+
+        and:
+        buildFile << '''
+allprojects {
+    apply plugin: 'java'
+    repositories { maven { url rootProject.uri('repo') } }
+}
+project(':a') {
+    dependencies {
+        compile 'group:externalA:1.5'
+        compile files('libs/externalB.jar')
+    }
+}
+project(':b') {
+    dependencies {
+        compile project(':a'), { transitive = false }
+    }
+    task listJars << {
+        assert configurations.compile.collect { it.name } == ['a.jar']
+    }
+}
+'''
+
+        expect:
+        succeeds "listJars"
+    }
+
+    public void "can have cycle in project dependencies"() {
+        given:
+        file('settings.gradle') << "include 'a', 'b', 'c'"
+
+        and:
+        buildFile << """
+
+subprojects {
+    apply plugin: 'base'
+    configurations {
+        'default'
+        other
+    }
+    task jar(type: Jar)
+    artifacts {
+        'default' jar
+    }
+}
+
+project('a') {
+    dependencies {
+        'default' project(':b')
+        other project(':b')
+    }
+    task listJars {
+        dependsOn configurations.default
+        dependsOn configurations.other
+        doFirst {
+            def jars = configurations.default.collect { it.name } as Set
+            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
+
+            jars = configurations.other.collect { it.name } as Set
+            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
+
+            // Check type of root component
+            def defaultResult = configurations.default.incoming.resolutionResult
+            def defaultRootId = defaultResult.root.id
+            assert defaultRootId instanceof ProjectComponentIdentifier
+
+            def otherResult = configurations.default.incoming.resolutionResult
+            def otherRootId = otherResult.root.id
+            assert otherRootId instanceof ProjectComponentIdentifier
+        }
+    }
+}
+
+project('b') {
+    dependencies {
+        'default' project(':c')
+    }
+}
+
+project('c') {
+    dependencies {
+        'default' project(':a')
+    }
+}
+"""
+
+        expect:
+        succeeds "listJars"
+    }
+
+    // this test is largely covered by other tests, but does ensure that there is nothing special about
+    // project dependencies that are “built” by built in plugins like the Java plugin's created jars
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "can use zip files as project dependencies"() {
+        given:
+        file("settings.gradle") << "include 'a'; include 'b'"
+        file("a/some.txt") << "foo"
+        file("a/build.gradle") << """
+            group = "g"
+            version = 1.0
+            
+            apply plugin: 'base'
+            task zip(type: Zip) {
+                from "some.txt"
+            }
+
+            artifacts {
+                delegate.default zip
+            }
+        """
+        file("b/build.gradle") << """
+            configurations { conf }
+            dependencies {
+                conf project(":a")
+            }
+            
+            task copyZip(type: Copy) {
+                from configurations.conf
+                into "\$buildDir/copied"
+            }
+        """
+        
+        when:
+        succeeds ":b:copyZip"
+        
+        then:
+        ":b:copyZip" in nonSkippedTasks
+        
+        and:
+        file("b/build/copied/a-1.0.zip").exists()
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionResultApiIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
new file mode 100644
index 0000000..b53130a
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolutionStrategySamplesIntegrationTest.groovy
@@ -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.integtests.resolve
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Rule
+
+public class ResolutionStrategySamplesIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule public final Sample sample = new Sample(temporaryFolder, 'userguide/artifacts/resolutionStrategy')
+
+    void "can resolve dependencies"()
+    {
+        mavenRepo.module("org", "foo").publish()
+        mavenRepo.module("org", "bar").publish()
+        mavenRepo.module("org.gradle", "gradle-core", "1.4").publish()
+        mavenRepo.module("org.software", "some-library", "1.2.1").publish()
+        mavenRepo.module("org.codehaus", "groovy", "2.3.10").publish()
+        mavenRepo.module("org.slf4j", "log4j-over-slf4j", "1.7.10").publish()
+
+        sample.dir.file("build.gradle") << """
+            configurations { conf }
+            repositories { maven { url "${mavenRepo.uri}" } }
+            dependencies {
+                conf "org:foo:1.0"
+                conf "org.gradle:gradle-core:1.0"
+                conf "org:bar:default"
+                conf "org.software:some-library:1.2"
+                conf "org.codehaus:groovy-all:2.3.10"
+                conf "log4j:log4j:1.2"
+            }
+            task resolveConf << { configurations.conf.files }
+        """
+
+        when:
+        inDirectory(sample.dir)
+        //smoke testing if dependency resolution works fine
+        run("resolveConf")
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
new file mode 100644
index 0000000..2cf89c5
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveCrossVersionIntegrationTest.groovy
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.CrossVersionIntegrationSpec
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.junit.Rule
+import spock.lang.Issue
+
+class ResolveCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
+    @Rule
+    HttpServer server = new HttpServer()
+    def mavenHttpRepo = new MavenHttpRepository(server, mavenRepo)
+    def ivyHttpRepo = new IvyHttpRepository(server, new IvyFileRepository(file("ivy-repo")))
+
+    def setup() {
+        requireOwnGradleUserHomeDir()
+        server.start()
+    }
+
+    def "can upgrade and downgrade Gradle version"() {
+        given:
+        mavenHttpRepo.module("test", "io", "1.4").publish()
+        mavenHttpRepo.module("test", "lang", "2.4").publish()
+        mavenHttpRepo.module("test", "lang", "2.6").publish()
+
+        and:
+        server.allowGetOrHead("/repo", mavenRepo.rootDir)
+
+        and:
+        buildFile << """
+repositories {
+    if (repositories.metaClass.respondsTo(repositories, 'maven')) {
+        maven { url "${mavenHttpRepo.uri}" }
+    } else {
+        mavenRepo urls: "${mavenHttpRepo.uri}"
+    }
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile 'test:io:1.4'
+    compile 'test:lang:2.+'
+}
+
+task check << {
+    assert configurations.compile*.name as Set == ['io-1.4.jar', 'lang-2.6.jar'] as Set
+}
+"""
+
+        expect:
+        version previous withTasks 'check' run()
+        version current withTasks 'check' run()
+        version previous withTasks 'check' run()
+    }
+
+    @Issue("GRADLE-3153")
+    def "can upgrade when ivy.xml contains namespaced extra info elements"() {
+        given:
+        def module = ivyHttpRepo.module("test", "io", "1.4").publish()
+        module.ivyFile.text = """
+<ivy-module version="1.0">
+    <info module="io" organisation="test" publication="20080831111344" revision="1.4" status="release">
+        <description homepage="http://some-thing/" />
+        <ns0:properties__organization.logo xmlns:ns0="http://ant.apache.org/ivy/maven">http://www.apache.org/images/asf_logo_wide.gif</ns0:properties__organization.logo>
+    </info>
+    <configurations>
+        <conf name="default" visibility="public"/>
+    </configurations>
+    <publications>
+        <artifact conf="*" ext="jar" name="io" type="jar"/>
+    </publications>
+</ivy-module>"""
+
+        and:
+        module.allowAll()
+
+        and:
+        buildFile << """
+repositories {
+    if (gradle.gradleVersion == '${current.version.version}' || ${previous.fullySupportsIvyRepository}) {
+        ivy { url "${ivyHttpRepo.uri}" }
+    } else {
+        add(Class.forName('org.apache.ivy.plugins.resolver.URLResolver').newInstance()) {
+            name = 'repo'
+            addIvyPattern("${ivyHttpRepo.uri}/[organisation]/[module]/[revision]/ivy-[revision].xml")
+            addArtifactPattern("${ivyHttpRepo.uri}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]")
+            descriptor = 'required'
+            checkmodified = true
+        }
+    }
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile 'test:io:1.4'
+}
+
+task check << {
+    assert configurations.compile*.name as Set == ['io-1.4.jar'] as Set
+}
+"""
+
+        expect:
+        version previous withTasks 'check' run()
+        version current withTasks 'check' run()
+        version current withTasks 'check' run()
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy
new file mode 100644
index 0000000..e700507
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveTestFixture.groovy
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api.DefaultTask
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.result.ComponentSelectionReason
+import org.gradle.api.artifacts.result.ResolvedComponentResult
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.classloader.ClasspathUtil
+import org.gradle.test.fixtures.file.TestFile
+
+/**
+ * A test fixture that injects a task into a build that resolves a dependency configuration and does some validation of the resulting graph, to
+ * ensure that the old and new dependency graphs plus the artifacts and files are as expected and well-formed.
+ */
+class ResolveTestFixture {
+    private final TestFile buildFile
+    private final String config
+
+    ResolveTestFixture(TestFile buildFile, String config = "compile") {
+        this.config = config
+        this.buildFile = buildFile
+    }
+
+    /**
+     * Injects the appropriate stuff into the build script.
+     */
+    void prepare() {
+        buildFile << """
+buildscript {
+    dependencies.classpath files("${ClasspathUtil.getClasspathForClass(GenerateGraphTask).toURI()}")
+}
+
+allprojects {
+    tasks.addPlaceholderAction("checkDeps", ${GenerateGraphTask.name}) {
+        it.dependsOn configurations.${config}
+        it.outputFile = rootProject.file("\${rootProject.buildDir}/${config}.txt")
+        it.configuration = configurations.$config
+    }
+}
+"""
+    }
+
+    /**
+     * Verifies the result of executing the task injected by {@link #prepare()}. The closure delegates to a {@link GraphBuilder} instance.
+     */
+    void expectGraph(@DelegatesTo(GraphBuilder) Closure closure) {
+        def graph = new GraphBuilder()
+        closure.resolveStrategy = Closure.DELEGATE_ONLY
+        closure.delegate = graph
+        closure.call()
+
+        if (graph.root == null) {
+            throw new IllegalArgumentException("No root node defined")
+        }
+
+        def configDetailsFile = buildFile.parentFile.file("build/${config}.txt")
+        def configDetails = configDetailsFile.text.readLines()
+
+        def actualArtifacts = configDetails.findAll { it.startsWith('artifact:') }.collect { it.substring(9) }
+        def expectedArtifacts = graph.artifactNodes.collect { "[${it.moduleVersionId}][${it.module}.jar]" }
+        assert actualArtifacts == expectedArtifacts
+
+        def actualFiles = configDetails.findAll { it.startsWith('file:') }.collect { it.substring(5) }
+        def expectedFiles = graph.artifactNodes.collect { it.fileName }
+        assert actualFiles == expectedFiles
+
+        def actualFirstLevel = configDetails.findAll { it.startsWith('first-level:') }.collect { it.substring(12) } as LinkedHashSet
+        def expectedFirstLevel = graph.root.deps.collect { "[${it.selected.moduleVersionId}:default]" } as LinkedHashSet
+        assert actualFirstLevel == expectedFirstLevel
+
+        def actualRoot = configDetails.find { it.startsWith('root:') }.substring(5)
+        def expectedRoot = "[id:${graph.root.id}][mv:${graph.root.moduleVersionId}][reason:${graph.root.reason}]"
+        assert actualRoot == expectedRoot
+
+        def actualNodes = configDetails.findAll { it.startsWith('component:') }.collect { it.substring(10) }
+        def expectedNodes = graph.nodes.values().collect { "[id:${it.id}][mv:${it.moduleVersionId}][reason:${it.reason}]" }
+        assert actualNodes == expectedNodes
+
+        def actualEdges = configDetails.findAll { it.startsWith('dependency:') }.collect { it.substring(11) }
+        def expectedEdges = graph.edges.collect { "[from:${it.from.id}][${it.requested}->${it.selected.id}]" }
+        assert actualEdges == expectedEdges
+    }
+
+    public static class GraphBuilder {
+        final Map<String, NodeBuilder> nodes = new LinkedHashMap<>()
+        NodeBuilder root
+
+        private getArtifactNodes() {
+            Set<NodeBuilder> result = new LinkedHashSet()
+            visitNodes(root, result)
+            return result
+        }
+
+        private void visitNodes(NodeBuilder node, Set<NodeBuilder> result) {
+            Set<NodeBuilder> nodesToVisit = []
+            for (EdgeBuilder edge: node.deps) {
+                if (result.add(edge.selected)) {
+                    nodesToVisit << edge.selected
+                }
+            }
+            for(NodeBuilder child: nodesToVisit) {
+                visitNodes(child, result)
+            }
+        }
+
+        private getEdges() {
+            Set<EdgeBuilder> result = new LinkedHashSet<>()
+            Set<NodeBuilder> seen = []
+            visitEdges(root, seen, result)
+            return result
+        }
+
+        private visitEdges(NodeBuilder node, Set<NodeBuilder> seenNodes, Set<EdgeBuilder> edges) {
+            for (EdgeBuilder edge: node.deps) {
+                edges.add(edge)
+                if (seenNodes.add(edge.selected)) {
+                    visitEdges(edge.selected, seenNodes, edges)
+                }
+            }
+        }
+
+        /**
+         * Defines the root node of the graph. The closure delegates to a {@link NodeBuilder} instance that represents the root node.
+         */
+        def root(String value, @DelegatesTo(NodeBuilder) Closure cl) {
+            if (root != null) {
+                throw new IllegalStateException("Root node is already defined")
+            }
+            root = node(value, value)
+            cl.resolveStrategy = Closure.DELEGATE_ONLY
+            cl.delegate = root
+            cl.call()
+            return root
+        }
+
+        /**
+         * Defines the root node of the graph. The closure delegates to a {@link NodeBuilder} instance that represents the root node.
+         */
+        def root(String path, String value, @DelegatesTo(NodeBuilder) Closure cl) {
+            if (root != null) {
+                throw new IllegalStateException("Root node is already defined")
+            }
+            root = node("project $path", value)
+            cl.resolveStrategy = Closure.DELEGATE_ONLY
+            cl.delegate = root
+            cl.call()
+            return root
+        }
+
+        def node(Map attrs) {
+            def id = "${attrs.group}:${attrs.module}:${attrs.version}"
+            return node(id, id, attrs)
+        }
+
+        def node(String id, String moduleVersionId) {
+            def attrs
+            if (moduleVersionId.matches(':\\w+:')) {
+                def parts = moduleVersionId.split(':')
+                attrs = [group: null, module: parts[1], version:null]
+                moduleVersionId = ":${attrs.module}:unspecified"
+            } else if (moduleVersionId.matches('\\w+:\\w+:')) {
+                def parts = moduleVersionId.split(':')
+                attrs = [group: parts[0], module: parts[1], version: null]
+                moduleVersionId = "${attrs.group}:${attrs.module}:unspecified"
+            } else {
+                def parts = moduleVersionId.split(':')
+                assert parts.length == 3
+                attrs = [group: parts[1], module: parts[1], version: parts[2]]
+            }
+            return node(id, moduleVersionId, attrs)
+        }
+
+        def node(String id, String moduleVersion, Map attrs) {
+            def node = nodes[moduleVersion]
+            if (!node) {
+                node = new NodeBuilder(id, moduleVersion, attrs, this)
+                nodes[moduleVersion] = node
+            }
+            return node
+        }
+    }
+
+    public static class EdgeBuilder {
+        final String requested
+        final NodeBuilder from
+        NodeBuilder selected
+
+        EdgeBuilder(NodeBuilder from, String requested, NodeBuilder selected) {
+            this.from = from
+            this.requested = requested
+            this.selected = selected
+        }
+
+        EdgeBuilder selects(Map selectedModule) {
+            selected = from.graph.node(selectedModule)
+            return this
+        }
+    }
+
+    public static class NodeBuilder {
+        final List<EdgeBuilder> deps = []
+        private final GraphBuilder graph
+        final String id
+        final String moduleVersionId
+        final String group
+        final String module
+        final String version
+        private String reason
+
+        NodeBuilder(String id, String moduleVersionId, Map attrs, GraphBuilder graph) {
+            this.graph = graph
+            this.group = attrs.group
+            this.module = attrs.module
+            this.version = attrs.version
+            this.moduleVersionId = moduleVersionId
+            this.id = id
+        }
+
+        private def getReason() {
+            reason ?: (this == graph.root ? 'root:' : 'requested:')
+        }
+
+        private def getFileName() {
+            "$module${version ? '-' + version : ''}.jar"
+        }
+
+        private NodeBuilder addNode(String id, String moduleVersionId = id) {
+            def node = graph.node(id, moduleVersionId)
+            deps << new EdgeBuilder(this, node.id, node)
+            return node
+        }
+
+        /**
+         * Defines a dependency on the given external module.
+         */
+        NodeBuilder module(String moduleVersionId) {
+            return addNode(moduleVersionId)
+        }
+
+        /**
+         * Defines a dependency on the given external module. The closure delegates to a {@link NodeBuilder} instance that represents the target node.
+         */
+        NodeBuilder module(String moduleVersionId, @DelegatesTo(NodeBuilder) Closure cl) {
+            def node = module(moduleVersionId)
+            cl.resolveStrategy = Closure.DELEGATE_ONLY
+            cl.delegate = node
+            cl.call()
+            return node
+        }
+
+        /**
+         * Defines a dependency on the given project. The closure delegates to a {@link NodeBuilder} instance that represents the target node.
+         */
+        NodeBuilder project(String path, String value, @DelegatesTo(NodeBuilder) Closure cl) {
+            def node = addNode("project $path", value)
+            cl.resolveStrategy = Closure.DELEGATE_ONLY
+            cl.delegate = node
+            cl.call()
+            return node
+        }
+
+        /**
+         * Defines a dependency from the current node to the given node.
+         */
+        NodeBuilder edge(String requested, String selectedModuleVersionId) {
+            def node = graph.node(selectedModuleVersionId, selectedModuleVersionId)
+            deps << new EdgeBuilder(this, requested, node)
+            return node
+        }
+
+        /**
+         * Defines a dependency of the current node.
+         */
+        EdgeBuilder dependency(Map requested) {
+            def edge = new EdgeBuilder(this, "${requested.group}:${requested.module}:${requested.version}", null)
+            deps << edge
+            return edge
+        }
+
+        /**
+         * Marks that this node was selected due to conflict resolution.
+         */
+        NodeBuilder byConflictResolution() {
+            reason = 'conflict resolution:conflict'
+            this
+        }
+    }
+}
+
+public class GenerateGraphTask extends DefaultTask {
+    File outputFile
+    Configuration configuration
+
+    @TaskAction
+    def generateOutput() {
+        outputFile.parentFile.mkdirs()
+
+        outputFile.withPrintWriter { writer ->
+            configuration.resolvedConfiguration.firstLevelModuleDependencies.each {
+                writer.println("first-level:[${it.moduleGroup}:${it.moduleName}:${it.moduleVersion}:${it.configuration}]")
+            }
+            configuration.resolvedConfiguration.resolvedArtifacts.each {
+                writer.println("artifact:[${it.moduleVersion.id}][${it.name}${it.classifier ? "-" + it.classifier : ""}.${it.extension}]")
+            }
+            def root = configuration.incoming.resolutionResult.root
+            writer.println("root:${formatComponent(root)}")
+            configuration.incoming.resolutionResult.allComponents.each {
+                writer.println("component:${formatComponent(it)}")
+            }
+            configuration.incoming.resolutionResult.allDependencies.each {
+                writer.println("dependency:[from:${it.from.id}][${it.requested}->${it.selected.id}]")
+            }
+            configuration.files.each {
+                writer.println("file:${it.name}")
+            }
+        }
+    }
+
+    def formatComponent(ResolvedComponentResult result) {
+        return "[id:${result.id}][mv:${result.moduleVersion}][reason:${formatReason(result.selectionReason)}]"
+    }
+
+    def formatReason(ComponentSelectionReason reason) {
+        def reasons = []
+        if (reason.conflictResolution) {
+            reasons << "conflict"
+        }
+        if (reason.forced) {
+            reasons << "forced"
+        }
+        if (reason.selectedByRule) {
+            reasons << "selectedByRule"
+        }
+        return "${reason.description}:${reasons.join(',')}"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
new file mode 100644
index 0000000..e12949f
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve
+
+import org.gradle.api.Project
+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.TestUtil
+import org.junit.Before
+import org.junit.Test
+
+public class ResolvedConfigurationIntegrationTest extends AbstractIntegrationTest {
+    def DefaultProject project = TestUtil.createRootProject()
+    def Project childProject = TestUtil.createChildProject(project, "child", new File("."))
+
+    @Before
+    public void boringSetup() {
+        project.allprojects { apply plugin: 'java' }
+
+        project.repositories {
+            maven { url mavenRepo.uri }
+        }
+    }
+
+    @Test
+    public void "resolves leniently"() {
+        mavenRepo.module('org.foo', 'hiphop').publish()
+        mavenRepo.module('org.foo', 'rock').dependsOn("some unresolved dependency").publish()
+
+        project.dependencies {
+            compile 'org.foo:hiphop:1.0'
+            compile 'unresolved.org:hiphopxx:3.0' //does not exist
+            compile childProject
+
+            compile 'org.foo:rock:1.0' //contains unresolved transitive dependency
+        }
+
+        LenientConfiguration compile = project.configurations.compile.resolvedConfiguration.lenientConfiguration
+
+        def unresolved = compile.getUnresolvedModuleDependencies()
+        def resolved = compile.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
+
+        assert resolved.size() == 3
+        assert resolved.find { it.moduleName == 'hiphop' }
+        assert resolved.find { it.moduleName == 'rock' }
+        assert resolved.find { it.moduleName == 'child' }
+
+        assert unresolved.size() == 2
+        assert unresolved.find { it.selector.group == 'unresolved.org' && it.selector.name == 'hiphopxx' && it.selector.version == '3.0' }
+        assert unresolved.find { it.selector.name == 'some unresolved dependency' }
+    }
+
+    @Test
+    public void "resolves leniently from mixed confs"() {
+        mavenRepo.module('org.foo', 'hiphop').publish()
+        mavenRepo.module('org.foo', 'rock').dependsOn("some unresolved dependency").publish()
+
+        project.configurations {
+            someConf
+        }
+
+        project.dependencies {
+            compile 'org.foo:hiphop:1.0'
+            someConf 'org.foo:hiphopxx:1.0' //does not exist
+        }
+
+        LenientConfiguration compile = project.configurations.compile.resolvedConfiguration.lenientConfiguration
+
+        def unresolved = compile.getUnresolvedModuleDependencies()
+        def resolved = compile.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
+
+        assert resolved.size() == 1
+        assert resolved.find { it.moduleName == 'hiphop' }
+        assert unresolved.size() == 0
+
+        LenientConfiguration someConf = project.configurations.someConf.resolvedConfiguration.lenientConfiguration
+
+        unresolved = someConf.getUnresolvedModuleDependencies()
+        resolved = someConf.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
+
+        assert resolved.size() == 0
+        assert unresolved.size() == 1
+        assert unresolved.find { it.selector.name == 'hiphopxx' }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..9394298
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class ScriptDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def "root component identifier has the correct type when resolving a script classpath"() {
+        given:
+        def module = mavenRepo().module("org.gradle", "test", "1.45")
+        module.dependsOn("org.gradle", "other", "preview-1")
+        module.artifact(classifier: 'classifier')
+        module.publish()
+        mavenRepo().module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        settingsFile << """
+rootProject.name = 'testproject'
+"""
+
+        buildFile << """
+group = 'org.gradle'
+version = '1.0'
+
+buildscript {
+    repositories { maven { url "${mavenRepo().uri}" } }
+    dependencies {
+        classpath "org.gradle:test:1.45"
+    }
+}
+
+task check << {
+    assert buildscript.configurations.classpath.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
+    def result = buildscript.configurations.classpath.incoming.resolutionResult
+
+    // Check root component
+    def rootId = result.root.id
+    assert rootId instanceof ProjectComponentIdentifier
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsupportedConfigurationMutationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsupportedConfigurationMutationTest.groovy
new file mode 100644
index 0000000..a112295
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsupportedConfigurationMutationTest.groovy
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+// TODO - report on the configuration that was actually changed
+// TODO - warn about configurations resolved via a project dependency
+// TODO - verify line number is included in deprecation message
+// TODO - warn about changes to artifacts
+// TODO - warn about changes to resolution strategy and other mutations
+class UnsupportedConfigurationMutationTest extends AbstractIntegrationSpec {
+
+    def "does not allow adding dependencies to a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            dependencies { a files("some.jar") }
+        """
+        when: fails()
+        then: failure.assertHasCause("Cannot change configuration ':a' after it has been resolved.")
+    }
+
+    def "does not allow adding artifacts to a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            artifacts { a file("some.jar") }
+        """
+        when: fails()
+        then: failure.assertHasCause("Cannot change configuration ':a' after it has been resolved.")
+    }
+
+    def "does not allow changing excludes on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.exclude group: 'someGroup'
+        """
+        when: fails()
+        then: failure.assertHasCause("Cannot change configuration ':a' after it has been resolved.")
+    }
+
+    def "warns about changing conflict resolution on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.failOnVersionConflict()
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warns about changing forced versions on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.force "org.utils:api:1.3"
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warns about changing cache policy on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.cacheChangingModulesFor 0, "seconds"
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warns about changing resolution rules on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.eachDependency {}
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warns about changing substitution rules on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.dependencySubstitution.all {}
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warns about changing component selection rules on a configuration that has been resolved"() {
+        buildFile << """
+            configurations { a }
+            configurations.a.resolve()
+            configurations.a.resolutionStrategy.componentSelection.all {}
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been resolved. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    @Issue("GRADLE-3155")
+    def "warns about adding dependencies to a configuration whose child has been resolved"() {
+        buildFile << """
+            configurations {
+                a
+                b.extendsFrom a
+                c.extendsFrom b
+            }
+            configurations.c.resolve()
+            dependencies { a files("some.jar") }
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    @Issue("GRADLE-3155")
+    def "warns about adding artifacts to a configuration whose child has been resolved"() {
+        buildFile << """
+            configurations {
+                a
+                b.extendsFrom a
+                c.extendsFrom b
+            }
+            configurations.c.resolve()
+            artifacts { a file("some.jar") }
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    @Issue("GRADLE-3155")
+    def "warns about changing a configuration whose child has been resolved"() {
+        buildFile << """
+            configurations {
+                a
+                b.extendsFrom a
+                c.extendsFrom b
+            }
+            configurations.c.resolve()
+            configurations.a.exclude group: 'someGroup'
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: succeeds()
+        then: output.contains("Attempting to change configuration ':a' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+    }
+
+    def "warning is upgraded to an error when configuration is resolved"() {
+        buildFile << """
+            configurations {
+                a
+                b.extendsFrom a
+            }
+            configurations.b.resolve()
+            configurations.a.exclude group: 'someGroup'
+            configurations.a.resolve()
+            configurations.a.exclude group: 'otherGroup'
+        """
+        executer.withDeprecationChecksDisabled()
+
+        when: fails()
+        then: output.contains("Attempting to change configuration ':a' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0")
+        and: failure.assertHasCause("Cannot change configuration ':a' after it has been resolved.")
+    }
+
+    @Issue("GRADLE-3155")
+    def "allows changing a configuration when the change does not affect a resolved child configuration"() {
+        buildFile << """
+            configurations {
+                a
+                b.extendsFrom a
+                b.resolve()
+                a.description = 'some conf'
+            }
+        """
+        expect: succeeds()
+    }
+
+    @Issue("GRADLE-3155")
+    def "allows changing a configuration that does not affect a resolved configuration"() {
+        buildFile << """
+            configurations {
+                a
+                b
+                b.resolve()
+            }
+            dependencies { a "a:b:c" }
+        """
+        expect: succeeds()
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..036b34d
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
@@ -0,0 +1,727 @@
+/*
+ * 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
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+import static org.hamcrest.Matchers.containsString
+
+public class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
+
+    void "strict conflict resolution should fail due to conflict"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.4.4')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		compile project(':api')
+		compile project(':impl')
+	}
+
+	configurations.compile.resolutionStrategy.failOnVersionConflict()
+}
+"""
+
+        expect:
+        runAndFail("tool:dependencies")
+        failure.assertThatCause(containsString('A conflict was found between the following modules:'))
+    }
+
+    void "strict conflict resolution should pass when no conflicts"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		compile project(':api')
+		compile project(':impl')
+	}
+
+	configurations.all { resolutionStrategy.failOnVersionConflict() }
+}
+"""
+
+        expect:
+        run("tool:dependencies")
+    }
+
+    void "resolves to the latest version by default"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+
+        settingsFile << """
+rootProject.name = 'test'
+include 'api', 'impl', 'tool'
+"""
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		compile (group: 'org', name: 'foo', version:'1.4.4')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		compile project(':api')
+		compile project(':impl')
+	}
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        run("tool:checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":tool", "test:tool:") {
+                project(":api", "test:api:") {
+                    edge("org:foo:1.3.3", "org:foo:1.4.4")
+                }
+                project(":impl", "test:impl:") {
+                    module("org:foo:1.4.4").byConflictResolution()
+                }
+            }
+        }
+    }
+
+    void "does not attempt to resolve an evicted dependency"() {
+        mavenRepo.module("org", "external", "1.2").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.0").publish()
+
+        settingsFile << "rootProject.name = 'test'"
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:external:1.2").byConflictResolution()
+                module("org:dep:2.2") {
+                    edge("org:external:1.0", "org:external:1.2")
+                }
+            }
+        }
+    }
+
+    @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.allComponents*.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()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.+").publish()
+
+        def buildFile = file("build.gradle")
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+
+task checkDeps << {
+    assert configurations.compile*.name == ['dep-2.2.jar', 'external-1.4.jar']
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
+    void "fails when version selected by conflict resolution does not exist"() {
+        mavenRepo.module("org", "external", "1.2").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+
+task checkDeps << {
+    assert configurations.compile*.name == ['external-1.2.jar', 'dep-2.2.jar']
+}
+"""
+
+        expect:
+        runAndFail("checkDeps")
+        failure.assertHasCause("Could not find org:external:1.4.")
+    }
+
+    void "does not fail when evicted version does not exist"() {
+        mavenRepo.module("org", "external", "1.4").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+
+task checkDeps << {
+    assert configurations.compile*.name == ['dep-2.2.jar', 'external-1.4.jar']
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
+    void "takes newest dynamic version when dynamic version forced"() {
+        mavenRepo.module("org", "foo", '1.3.0').publish()
+
+        mavenRepo.module("org", "foo", '1.4.1').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+        mavenRepo.module("org", "foo", '1.4.9').publish()
+
+        mavenRepo.module("org", "foo", '1.6.0').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		compile 'org:foo:1.4.4'
+	}
+}
+
+project(':impl') {
+	dependencies {
+		compile 'org:foo:1.4.1'
+	}
+}
+
+project(':tool') {
+
+	dependencies {
+		compile project(':api'), project(':impl'), 'org:foo:1.3.0'
+	}
+
+	configurations.all {
+	    resolutionStrategy {
+	        force 'org:foo:1.4+'
+	        failOnVersionConflict()
+	    }
+	}
+
+	task checkDeps << {
+        assert configurations.compile*.name.contains('foo-1.4.9.jar')
+    }
+}
+
+"""
+
+        expect:
+        run("tool:checkDeps")
+    }
+
+    void "parent pom does not participate in forcing mechanism"() {
+        mavenRepo.module("org", "foo", '1.3.0').publish()
+        mavenRepo.module("org", "foo", '2.4.0').publish()
+
+        def parent = mavenRepo.module("org", "someParent", "1.0")
+        parent.type = 'pom'
+        parent.dependsOn("org", "foo", "1.3.0")
+        parent.publish()
+
+        def otherParent = mavenRepo.module("org", "someParent", "2.0")
+        otherParent.type = 'pom'
+        otherParent.dependsOn("org", "foo", "2.4.0")
+        otherParent.publish()
+
+        def module = mavenRepo.module("org", "someArtifact", '1.0')
+        module.parent("org", "someParent", "1.0")
+        module.publish()
+
+        buildFile << """
+apply plugin: 'java'
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    compile 'org:someArtifact:1.0'
+}
+
+configurations.all {
+    resolutionStrategy {
+        force 'org:someParent:2.0'
+        failOnVersionConflict()
+    }
+}
+
+task checkDeps << {
+    def deps = configurations.compile*.name
+    assert deps.contains('someArtifact-1.0.jar')
+    assert deps.contains('foo-1.3.0.jar')
+    assert deps.size() == 2
+}
+"""
+
+        expect:
+        run("checkDeps")
+    }
+
+    void "previously evicted nodes should contain correct target version"() {
+        /*
+        a1->b1
+        a2->b2->a1
+
+        resolution process:
+
+        1. stop resolution, resolve conflict a1 vs a2
+        2. select a2, restart resolution
+        3. stop, resolve b1 vs b2
+        4. select b2, restart
+        5. resolve b2 dependencies, a1 has been evicted previously but it should show correctly on the report
+           ('dependencies' report pre 1.2 would not show the a1 dependency leaf for this scenario)
+        */
+
+        ivyRepo.module("org", "b", '1.0').publish()
+        ivyRepo.module("org", "a", '1.0').dependsOn("org", "b", '1.0').publish()
+        ivyRepo.module("org", "b", '2.0').dependsOn("org", "a", "1.0").publish()
+        ivyRepo.module("org", "a", '2.0').dependsOn("org", "b", '2.0').publish()
+
+        buildFile << """
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+            }
+
+            configurations {
+                conf
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:a:2.0'
+            }
+            task checkDeps << {
+                assert configurations.conf*.name == ['a-2.0.jar', 'b-2.0.jar']
+                def result = configurations.conf.incoming.resolutionResult
+                assert result.allComponents.size() == 3
+                def root = result.root
+                assert root.dependencies*.toString() == ['org:a:1.0 -> org:a:2.0', 'org:a:2.0']
+                def a = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
+                assert a.dependencies*.toString() == ['org:b:2.0']
+                def b = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b' }
+                assert b.dependencies*.toString() == ['org:a:1.0 -> org:a:2.0']
+            }
+        """
+
+        expect:
+        run("checkDeps")
+    }
+
+    @Issue("GRADLE-2555")
+    void "can deal with transitive with parent in conflict"() {
+        /*
+            Graph looks like…
+
+            \--- org:a:1.0
+                 \--- org:in-conflict:1.0 -> 2.0
+                      \--- org:target:1.0
+                           \--- org:target-child:1.0
+            \--- org:b:1.0
+                 \--- org:b-child:1.0
+                      \--- org:in-conflict:2.0 (*)
+
+            This is the simplest structure I could boil it down to that produces the error.
+            - target *must* have a child
+            - Having "b" depend directly on "in-conflict" does not produce the error, needs to go through "b-child"
+         */
+
+        mavenRepo.module("org", "target-child", "1.0").publish()
+        mavenRepo.module("org", "target", "1.0").dependsOn("org", "target-child", "1.0").publish()
+        mavenRepo.module("org", "in-conflict", "1.0").dependsOn("org", "target", "1.0").publish()
+        mavenRepo.module("org", "in-conflict", "2.0").dependsOn("org", "target", "1.0").publish()
+
+        mavenRepo.module("org", "a", '1.0').dependsOn("org", "in-conflict", "1.0").publish()
+
+        mavenRepo.module("org", "b-child", '1.0').dependsOn("org", "in-conflict", "2.0").publish()
+
+        mavenRepo.module("org", "b", '1.0').dependsOn("org", "b-child", "1.0").publish()
+
+        buildFile << """
+            repositories { maven { url "${mavenRepo.uri}" } }
+
+            configurations { conf }
+
+            dependencies {
+                conf "org:a:1.0", "org:b:1.0"
+            }
+
+        task checkDeps << {
+            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.allComponents.size() == 7
+            def a = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
+            assert a.dependencies*.toString() == ['org:in-conflict:1.0 -> org:in-conflict:2.0']
+            def bChild = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b-child' }
+            assert bChild.dependencies*.toString() == ['org:in-conflict:2.0']
+            def target = result.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'target' }
+            assert target.dependents*.from*.toString() == ['org:in-conflict:2.0']
+        }
+        """
+
+        expect:
+        run("checkDeps")
+    }
+
+    @Issue("GRADLE-2555")
+    void "batched up conflicts with conflicted parent and child"() {
+        /*
+        Dependency tree:
+
+        a->c1
+        b->c2->x1
+        d->x1
+        f->x2
+
+        Everything is resolvable but not x2
+
+        Scenario:
+         - We have batched up conflicts
+         - root of one conflicted version is also conflicted
+         - conflicted root is positioned on the conflicts queue after the conflicted child (the order of declaring dependencies matters)
+         - winning root depends on a child that previously was evicted
+         - finally, the winning child is an unresolved dependency
+        */
+        mavenRepo.module("org", "c", '1.0').publish()
+        mavenRepo.module("org", "x", '1.0').publish()
+        mavenRepo.module("org", "c", '2.0').dependsOn("org", "x", '1.0').publish()
+        mavenRepo.module("org", "a").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "c", "2.0").publish()
+        mavenRepo.module("org", "d").dependsOn("org", "x", "1.0").publish()
+        mavenRepo.module("org", "f").dependsOn("org", "x", "2.0").publish()
+
+        buildFile << """
+            repositories { maven { url "${mavenRepo.uri}" } }
+            configurations {
+                childFirst
+                parentFirst
+            }
+            dependencies {
+                //conflicted child is resolved first
+                childFirst 'org:d:1.0', 'org:f:1.0', 'org:a:1.0', 'org:b:1.0'
+                //conflicted parent is resolved first
+                parentFirst 'org:a:1.0', 'org:b:1.0', 'org:d:1.0', 'org:f:1.0'
+            }
+        """
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+childFirst
++--- org:d:1.0
+|    \\--- org:x:1.0 -> 2.0 FAILED
++--- org:f:1.0
+|    \\--- org:x:2.0 FAILED
++--- org:a:1.0
+|    \\--- org:c:1.0 -> 2.0
+|         \\--- org:x:1.0 -> 2.0 FAILED
+\\--- org:b:1.0
+     \\--- org:c:2.0 (*)
+
+parentFirst
++--- org:a:1.0
+|    \\--- org:c:1.0 -> 2.0
+|         \\--- org:x:1.0 -> 2.0 FAILED
++--- org:b:1.0
+|    \\--- org:c:2.0 (*)
++--- org:d:1.0
+|    \\--- org:x:1.0 -> 2.0 FAILED
+\\--- org:f:1.0
+     \\--- org:x:2.0 FAILED"""))
+    }
+
+    @Issue("GRADLE-2752")
+    void "selects root module when earlier version of module 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"
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org:test:1.3") {
+                module("org:other:1.7") {
+                    edge("org:test:1.2", "org:test:1.3")
+                }
+            }
+        }
+    }
+
+    @Issue("GRADLE-2920")
+    void "selects later version of root module when requested"() {
+        mavenRepo.module("org", "test", "2.1").publish()
+        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "2.1").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"
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org:test:1.3") {
+                module("org:other:1.7") {
+                    module("org:test:2.1").byConflictResolution()
+                }
+            }
+        }
+    }
+
+    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.allComponents.find { it.id instanceof ModuleComponentIdentifier && it.id.module == '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/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AbstractCacheReuseCrossVersionIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..8ebf2b7
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/AliasedArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve.artifactreuse
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import spock.lang.Ignore
+
+class AliasedArtifactResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def mavenRepo1 = mavenHttpRepo("maven1")
+    def mavenRepo2 = mavenHttpRepo("maven2")
+    def ivyRepo1 = ivyHttpRepo("ivy1")
+    def ivyRepo2 = ivyHttpRepo("ivy2")
+
+    def setup() {
+        buildFile << """
+            repositories {
+                if (project.hasProperty('mavenRepository1')) {
+                    maven { url '${mavenRepo1.uri}' }
+                } else if (project.hasProperty('mavenRepository2')) {
+                    maven { url '${mavenRepo2.uri}' }
+                } else if (project.hasProperty('ivyRepository1')) {
+                    ivy { url '${ivyRepo1.uri}' }
+                } else if (project.hasProperty('ivyRepository2')) {
+                    ivy { url '${ivyRepo2.uri}' }
+                } else if (project.hasProperty('fileRepository')) {
+                    maven { url '${mavenRepo.uri}' }
+                }
+            }
+            configurations { compile }
+            dependencies {
+                compile 'org.name:projectB:1.0'
+            }
+
+            task retrieve(type: Sync) {
+                into 'libs'
+                from configurations.compile
+            }
+        """
+    }
+
+    def "does not re-download maven artifact downloaded from a different maven repository when sha1 matches"() {
+        when:
+        def projectBModuleRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBModuleRepo1.pom.expectGet()
+        projectBModuleRepo1.artifact.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository1'
+
+        when:
+        def projectBModuleRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
+        projectBModuleRepo2.pom.expectHead()
+        projectBModuleRepo2.pom.sha1.expectGet()
+        projectBModuleRepo2.artifact.expectHead()
+        projectBModuleRepo2.artifact.sha1.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository2'
+    }
+
+    def "does not re-download ivy artifact downloaded from a different ivy repository when sha1 matches"() {
+        when:
+        def projectBRepo1 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo1.ivy.expectGet()
+        projectBRepo1.jar.expectGet()
+
+        then:
+        succeedsWith 'ivyRepository1'
+
+        when:
+        def projectBRepo2 = ivyRepo2.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo2.ivy.expectHead()
+        projectBRepo2.ivy.sha1.expectGet()
+        projectBRepo2.jar.expectHead()
+        projectBRepo2.jar.sha1.expectGet()
+
+        then:
+        succeedsWith 'ivyRepository2'
+    }
+
+    def "does not re-download ivy artifact downloaded from a maven repository when sha1 matches"() {
+        when:
+        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo1.pom.expectGet()
+        projectBRepo1.artifact.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository1'
+
+        when:
+        def projectBRepo2 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo2.ivy.expectGet()
+        projectBRepo2.jar.expectHead()
+        projectBRepo2.jar.sha1.expectGet()
+
+        then:
+        succeedsWith 'ivyRepository1'
+    }
+
+    def "does not re-download maven artifact downloaded from a ivy repository when sha1 matches"() {
+        when:
+        def projectBRepo1 = ivyRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo1.ivy.expectGet()
+        projectBRepo1.jar.expectGet()
+
+        then:
+        succeedsWith 'ivyRepository1'
+
+        when:
+        def projectBRepo2 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo2.pom.expectGet()
+        projectBRepo2.artifact.expectHead()
+        projectBRepo2.artifact.sha1.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository1'
+    }
+
+    @Ignore("File repository does not cache artifacts locally, so they are not used to prevent download")
+    def "does not download artifact previously accessed from a file uri when sha1 matches"() {
+        given:
+        succeedsWith 'fileRepository'
+
+        when:
+        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo2.pom.expectHead()
+        projectBRepo2.pom.sha1.expectGet()
+        projectBRepo2.artifact.expectHead()
+        projectBRepo2.artifact.sha1.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository2'
+    }
+
+    def "does re-download maven artifact downloaded from a different URI when sha1 not found"() {
+        when:
+        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo1.pom.expectGet()
+        projectBRepo1.artifact.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository1'
+
+        when:
+        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo2.pom.expectHead()
+        projectBRepo2.pom.sha1.expectGetMissing()
+        projectBRepo2.pom.expectGet()
+        projectBRepo2.artifact.expectHead()
+        projectBRepo2.artifact.sha1.expectGetMissing()
+        projectBRepo2.getArtifact().expectGet()
+
+        then:
+        succeedsWith 'mavenRepository2'
+    }
+
+    def "does re-download maven artifact downloaded from a different URI when sha1 does not match"() {
+        when:
+        def projectBRepo1 = mavenRepo1.module('org.name', 'projectB', '1.0').publish()
+        projectBRepo1.pom.expectGet()
+        projectBRepo1.artifact.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository1'
+
+        when:
+        def projectBRepo2 = mavenRepo2.module('org.name', 'projectB', '1.0').publishWithChangedContent()
+        projectBRepo2.pom.expectHead()
+        projectBRepo2.pom.sha1.expectGet()
+        projectBRepo2.pom.expectGet()
+
+        projectBRepo2.artifact.expectHead()
+        projectBRepo2.artifact.sha1.expectGet()
+        projectBRepo2.artifact.expectGet()
+
+        then:
+        succeedsWith 'mavenRepository2'
+    }
+
+    def succeedsWith(repository) {
+        executer.withArguments('-i', "-P${repository}")
+        def result = succeeds 'retrieve'
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        server.resetExpectations()
+        return result
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy
new file mode 100644
index 0000000..4a332d6
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/CacheReuseCrossVersionIntegrationTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve.artifactreuse
+
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.junit.Rule
+
+import org.gradle.integtests.fixtures.IgnoreVersions
+import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
+
+ at TargetVersions('1.0-milestone-6+')
+ at IgnoreVersions({ it.artifactCacheLayoutVersion == DefaultCacheLockingManager.CACHE_LAYOUT_VERSION || it.version.version == "1.9-rc-1" })
+class CacheReuseCrossVersionIntegrationTest extends AbstractCacheReuseCrossVersionIntegrationTest {
+    @Rule public final HttpServer server = new HttpServer()
+    final MavenHttpRepository httpRepo = new MavenHttpRepository(server, new MavenFileRepository(file("maven-repo")))
+
+    @Override
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "uses cached artifacts from previous Gradle version when no sha1 header"() {
+        given:
+        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
+        server.sendSha1Header = false
+        server.start()
+        buildFile << """
+repositories {
+    maven { url '${httpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectB:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        def userHome = file('user-home')
+
+        when:
+        projectB.allowAll()
+
+        and:
+        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        def snapshot = file('libs/projectB-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        projectB.pom.expectHead()
+        projectB.pom.sha1.expectGet()
+        projectB.artifact.expectHead()
+        projectB.artifact.sha1.expectGet()
+
+        and:
+        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
+    }
+
+    def "uses cached artifacts from previous Gradle version with sha1 header"() {
+        given:
+        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
+        server.sendSha1Header = true
+        server.start()
+        buildFile << """
+repositories {
+    maven { url '${httpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectB:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        def userHome = file('user-home')
+
+        when:
+        projectB.allowAll()
+
+        and:
+        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        def snapshot = file('libs/projectB-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        projectB.pom.expectHead()
+        projectB.artifact.expectHead()
+
+        and:
+        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
+    }
+
+    def "uses cached artifacts from previous Gradle version that match dynamic version"() {
+        given:
+        def projectB = httpRepo.module('org.name', 'projectB', '1.1').publish()
+        server.start()
+
+        buildFile << """
+repositories {
+    maven { url '${httpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectB:[1.0,2.0]'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        def userHome = file('user-home')
+
+        when:
+        httpRepo.getModuleMetaData("org.name", "projectB").expectGet()
+        projectB.allowAll()
+
+        and:
+        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.1.jar')
+        def snapshot = file('libs/projectB-1.1.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        httpRepo.getModuleMetaData("org.name", "projectB").expectGet()
+        projectB.pom.expectHead()
+        projectB.pom.sha1.expectGet()
+        projectB.artifact.expectHead()
+        projectB.artifact.sha1.expectGet()
+
+        and:
+        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.1.jar')
+        file('libs/projectB-1.1.jar').assertContentsHaveNotChangedSince(snapshot)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy
new file mode 100644
index 0000000..c58dded
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/M3CacheReuseCrossVersionIntegrationTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve.artifactreuse
+
+import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.junit.Rule
+
+// Special case for milestone-3 since that version (and earlier versions) do not include POM reuse.
+ at TargetVersions('1.0-milestone-3')
+class M3CacheReuseCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
+    @Rule public final HttpServer server = new HttpServer()
+    final MavenHttpRepository remoteRepo = new MavenHttpRepository(server, mavenRepo)
+
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "uses cached artifacts from previous Gradle version"() {
+        given:
+        def projectB = remoteRepo.module('org.name', 'projectB').publish()
+
+        server.start()
+        buildFile << """
+repositories {
+    if (repositories.metaClass.respondsTo(repositories, 'maven')) {
+        maven { url "${remoteRepo.uri}" }
+    } else {
+        mavenRepo urls: ["${remoteRepo.uri}"]
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectB:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        def userHome = file('user-home')
+
+        when:
+        projectB.allowAll()
+
+        and:
+        version previous withGradleUserHomeDir userHome withTasks 'retrieve' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        def snapshot = file('libs/projectB-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        projectB.pom.expectGet()
+        projectB.artifact.expectHead()
+        projectB.artifact.sha1.expectGet()
+
+        and:
+        version current withGradleUserHomeDir userHome withTasks 'retrieve' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.groovy
new file mode 100644
index 0000000..4d7c795
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/MavenM2CacheReuseIntegrationTest.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.integtests.resolve.artifactreuse
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+class MavenM2CacheReuseIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def "uses cached artifacts from maven local cache"() {
+        given:
+        def remoteModule = mavenHttpRepo.module('gradletest.maven.local.cache.test', "foo", "1.0").publish()
+        m2Installation.generateGlobalSettingsFile()
+        def m2Module = m2Installation.mavenRepo().module('gradletest.maven.local.cache.test', "foo", "1.0").publish()
+
+        buildFile.text = """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations { compile }
+dependencies {
+    compile 'gradletest.maven.local.cache.test:foo:1.0'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'build'
+}
+"""
+        and:
+        remoteModule.pom.expectHead()
+        remoteModule.pom.sha1.expectGet()
+        remoteModule.artifact.expectHead()
+        remoteModule.artifact.sha1.expectGet()
+
+        when:
+        using m2Installation
+        run 'retrieve'
+
+        then:
+        file('build/foo-1.0.jar').assertIsCopyOf(m2Module.artifactFile)
+    }
+
+    def "does not reuse cached artifacts from maven local cache when they are different to those in the remote repository"() {
+        given:
+        def remoteModule = mavenHttpRepo.module('gradletest.maven.local.cache.test', "foo", "1.0").publish()
+        m2Installation.generateGlobalSettingsFile()
+        m2Installation.mavenRepo().module('gradletest.maven.local.cache.test', "foo", "1.0").publishWithChangedContent()
+
+        buildFile.text = """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations { compile }
+dependencies {
+    compile 'gradletest.maven.local.cache.test:foo:1.0'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'build'
+}
+"""
+        and:
+        remoteModule.pom.expectHead()
+        remoteModule.pom.sha1.expectGet()
+        remoteModule.pom.expectGet()
+        remoteModule.artifact.expectHead()
+        remoteModule.artifact.sha1.expectGet()
+        remoteModule.artifact.expectGet()
+
+        when:
+        using m2Installation
+        run 'retrieve'
+
+        then:
+        file('build/foo-1.0.jar').assertIsCopyOf(remoteModule.artifactFile)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
new file mode 100644
index 0000000..4f84d55
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.artifactreuse
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+class ResolutionOverrideIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    public void "will refresh non-changing module when run with --refresh-dependencies"() {
+        given:
+        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
+
+        and:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations { compile }
+dependencies { compile 'org.name:projectA:1.2' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        module.allowAll()
+
+        when:
+        succeeds 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar')
+        def snapshot = file('libs/projectA-1.2.jar').snapshot()
+
+        when:
+        module.publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+        module.pom.expectHead()
+        module.pom.sha1.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectHead()
+        module.artifact.sha1.expectGet()
+        module.artifact.expectGet()
+
+        and:
+        executer.withArguments('--refresh-dependencies')
+        succeeds 'retrieve'
+
+        then:
+        file('libs/projectA-1.2.jar').assertIsCopyOf(module.artifactFile).assertHasChangedSince(snapshot)
+    }
+
+    public void "will recover from missing module when run with --refresh-dependencies"() {
+        given:
+        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
+        def artifact = module.artifact
+
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+    }
+}
+configurations { missing }
+dependencies {
+    missing 'org.name:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        module.pom.expectGetMissing()
+        artifact.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+
+        when:
+        server.resetExpectations()
+        module.pom.expectGet()
+        module.getArtifact().expectGet()
+
+        then:
+        executer.withArguments("--refresh-dependencies")
+        succeeds('showMissing')
+    }
+
+    public void "will recover from missing artifact when run with --refresh-dependencies"() {
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectA:1.2'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        def module = mavenHttpRepo.module('org.name', 'projectA', '1.2').publish()
+        def artifact = module.artifact
+
+        when:
+        module.pom.expectGet()
+        artifact.expectGetMissing()
+
+        then:
+        fails "retrieve"
+
+        when:
+        server.resetExpectations()
+        module.pom.expectHead()
+        artifact.expectGet()
+
+        then:
+        executer.withArguments("--refresh-dependencies")
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants('projectA-1.2.jar')
+    }
+
+    public void "will not expire cache entries when run with offline flag"() {
+
+        given:
+        def module = mavenHttpRepo.module("org.name", "unique", "1.0-SNAPSHOT").publish()
+
+        and:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations { compile }
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+dependencies {
+    compile "org.name:unique:1.0-SNAPSHOT"
+}
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when: "Server handles requests"
+        module.allowAll()
+
+        and: "We resolve dependencies"
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar')
+        def snapshot = file('libs/unique-1.0-SNAPSHOT.jar').snapshot()
+
+        when:
+        module.publishWithChangedContent()
+
+        and: "We resolve again, offline"
+        server.resetExpectations()
+        executer.withArguments('--offline')
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar')
+        file('libs/unique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshot)
+    }
+
+    public void "does not attempt to contact server when run with offline flag"() {
+        given:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations { compile }
+dependencies { compile 'org.name:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        executer.withArguments("--offline")
+
+        then:
+        fails 'listJars'
+
+        and:
+        failure.assertHasDescription('Execution failed for task \':listJars\'.')
+        failure.assertResolutionFailure(":compile")
+            .assertHasCause('No cached version of org.name:projectA:1.2 available for offline mode')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy
new file mode 100644
index 0000000..529b19d
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/SameCacheUsageCrossVersionIntegrationTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * 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.artifactreuse
+
+import org.gradle.api.internal.artifacts.ivyservice.DefaultCacheLockingManager
+import org.gradle.integtests.fixtures.IgnoreVersions
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.junit.Rule
+
+ at TargetVersions('1.0-milestone-6+')
+ at IgnoreVersions({ it.artifactCacheLayoutVersion != DefaultCacheLockingManager.CACHE_LAYOUT_VERSION })
+class SameCacheUsageCrossVersionIntegrationTest extends AbstractCacheReuseCrossVersionIntegrationTest {
+    @Rule public final HttpServer server = new HttpServer()
+    final MavenHttpRepository httpRepo = new MavenHttpRepository(server, new MavenFileRepository(file("maven-repo")))
+
+    @Override
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "incurs zero remote requests when cache version not upgraded"() {
+        given:
+        def projectB = httpRepo.module('org.name', 'projectB', '1.0').publish()
+        server.sendSha1Header = false
+        server.start()
+        buildFile << """
+repositories {
+    maven { url '${httpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org.name:projectB:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+        and:
+        def userHome = file('user-home')
+
+        when:
+        projectB.allowAll()
+
+        and:
+        version previous withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        def snapshot = file('libs/projectB-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        //expect no http requests
+
+        and:
+        version current withGradleUserHomeDir userHome withTasks 'retrieve' withArguments '-i' run()
+
+        then:
+        file('libs').assertHasDescendants('projectB-1.0.jar')
+        file('libs/projectB-1.0.jar').assertContentsHaveNotChangedSince(snapshot)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy
new file mode 100644
index 0000000..e134d7e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedChangingModulesIntegrationTest.groovy
@@ -0,0 +1,229 @@
+/*
+ * 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.caching
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+public class CachedChangingModulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    def "can cache and refresh unique versioned maven artifacts with a classifier"() {
+        given:
+        def repo = mavenHttpRepo("repo")
+        def module = repo.module("group", "projectA", "1.0-SNAPSHOT")
+        def sourceArtifact = module.artifact(classifier: "source")
+
+        module.publish()
+        buildFile << """
+        repositories {
+            maven {
+                name 'repo'
+                url '${repo.uri}'
+            }
+        }
+        configurations {
+            compile
+        }
+
+        configurations.all {
+            resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+        }
+
+        dependencies {
+            compile 'group:projectA:1.0-SNAPSHOT:source'
+        }
+
+        task retrieve(type: Sync) {
+            into 'libs'
+            from configurations.compile
+        }
+        """
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectGet()
+        module.metaData.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        module.metaData.expectGet()
+        sourceArtifact.expectHead()
+        module.pom.expectHead()
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+
+        module.metaData.expectGet()
+        module.pom.sha1.expectGet()
+        module.pom.expectHead()
+        module.pom.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+        sourceArtifact.sha1.expectGet()
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+    }
+
+    def "can cache and refresh non unique versioned maven artifacts with a classifier"() {
+        given:
+        def repo = mavenHttpRepo("repo")
+        def module = repo.module("group", "projectA", "1.0-SNAPSHOT").withNonUniqueSnapshots()
+        def sourceArtifact = module.artifact(classifier: "source")
+
+        module.publish()
+        buildFile << """
+        repositories {
+            maven {
+                name 'repo'
+                url '${repo.uri}'
+            }
+        }
+        configurations {
+            compile
+        }
+
+        configurations.all {
+            resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+        }
+
+        dependencies {
+            compile 'group:projectA:1.0-SNAPSHOT:source'
+        }
+
+        task retrieve(type: Sync) {
+            into 'libs'
+            from configurations.compile
+        }
+        """
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectGet()
+        module.metaData.expectGetMissing()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        module.metaData.expectGetMissing()
+        sourceArtifact.expectHead()
+        module.pom.expectHead()
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+
+        module.metaData.expectGetMissing()
+        module.pom.sha1.expectGet()
+        module.pom.expectHead()
+        module.pom.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+        sourceArtifact.sha1.expectGet()
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+    }
+
+    def "can cache and refresh ivy changing artifacts with a classifier"() {
+        given:
+        def repo = ivyHttpRepo("repo")
+        def module = repo.module("group", "projectA", "1.0")
+        module.artifact(classifier: "source")
+
+        module.publish()
+        buildFile << """
+          repositories {
+              ivy {
+                  name 'repo'
+                  url '${repo.uri}'
+              }
+          }
+          configurations {
+              compile
+          }
+
+          configurations.all {
+              resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+          }
+
+          dependencies {
+                compile group: "group", name: "projectA", version: "1.0", classifier: "source", changing: true
+          }
+
+          task retrieve(type: Sync) {
+              into 'libs'
+              from configurations.compile
+          }
+          """
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "source").expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.getArtifact(classifier: 'source').expectHead()
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.getArtifact(classifier: 'source').expectHead()
+
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.getArtifact(classifier: 'source').expectGet()
+        module.getArtifact(classifier: 'source').sha1.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..e3e24cb
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedDependencyResolutionIntegrationTest.groovy
@@ -0,0 +1,238 @@
+/*
+ * 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.caching
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import org.gradle.test.fixtures.server.http.HttpServer
+import spock.lang.Issue
+
+/**
+ * We are using Ivy here, but the strategy is the same for any kind of repository.
+ */
+class CachedDependencyResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    IvyHttpModule module
+
+    TestFile downloaded
+    TestFile.Snapshot lastState
+
+    def setup() {
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.1", changing: true
+}
+
+task retrieve(type: Sync) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        downloaded = file('build/projectA-1.1.jar')
+    }
+
+    void initialResolve() {
+        module.ivy.expectGet()
+        module.jar.expectGet()
+        resolve()
+    }
+
+    void resolve() {
+        if (downloaded.exists()) {
+            lastState = downloaded.snapshot()
+        }
+
+        succeeds ":retrieve"
+    }
+
+    void headOnlyRequests() {
+        module.ivy.expectHead()
+        module.jar.expectHead()
+    }
+
+    void headSha1ThenGetRequests() {
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+    }
+
+    void sha1OnlyRequests() {
+        module.ivy.sha1.expectGet()
+        module.jar.sha1.expectGet()
+    }
+
+    void sha1ThenGetRequests() {
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+    }
+
+    void headThenSha1Requests() {
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+    }
+
+    void headThenGetRequests() {
+        module.ivy.expectHead()
+        module.ivy.expectGet()
+
+        module.jar.expectHead()
+        module.jar.expectGet()
+    }
+
+    void unchangedResolve() {
+        resolve()
+        downloaded.assertHasNotChangedSince(lastState)
+    }
+
+    void changedResolve() {
+        resolve()
+        downloaded.assertHasChangedSince(lastState)
+    }
+
+    void change() {
+        module.publishWithChangedContent()
+    }
+
+    def "etags are used to determine changed"() {
+        given:
+        server.etags = HttpServer.EtagStrategy.RAW_SHA1_HEX
+        server.sendLastModified = false
+        initialResolve()
+
+        expect:
+        headOnlyRequests()
+        unchangedResolve()
+
+        when:
+        change()
+
+        then:
+        headSha1ThenGetRequests()
+        changedResolve()
+    }
+
+    def "last modified and content length are used to determine changed"() {
+        given:
+        server.etags = null
+        initialResolve()
+
+        expect:
+        headOnlyRequests()
+        unchangedResolve()
+
+        when:
+        change()
+
+        then:
+        headSha1ThenGetRequests()
+        changedResolve()
+    }
+
+    def "checksum is used when last modified and content length can't be used"() {
+        given:
+        server.etags = null
+        server.sendLastModified = false
+        initialResolve()
+
+        expect:
+        headThenSha1Requests()
+        unchangedResolve()
+
+        when:
+        change()
+
+        then:
+        headSha1ThenGetRequests()
+        changedResolve()
+    }
+
+    def "no need for sha1 request if we get it in the metadata"() {
+        given:
+        server.sendSha1Header = true
+        initialResolve()
+
+        expect:
+        headOnlyRequests()
+        unchangedResolve()
+
+        when:
+        change()
+
+        then:
+        headThenGetRequests()
+        changedResolve()
+    }
+
+    def "no need for sha1 request if we know the etag is sha1"() {
+        given:
+        server.etags = HttpServer.EtagStrategy.NEXUS_ENCODED_SHA1
+        initialResolve()
+
+        expect:
+        headOnlyRequests()
+        unchangedResolve()
+
+        when:
+        change()
+
+        then:
+        headThenGetRequests()
+        changedResolve()
+    }
+
+    @Issue("GRADLE-2781")
+    def "no leading zeros in sha1 checksums supported"() {
+        given:
+        server.etags = null
+        server.sendLastModified = false
+        byte[] jarBytes = [0, 0, 0, 5]
+        module.jarFile.bytes = jarBytes
+        initialResolve()
+        expect:
+        headThenSha1Requests()
+        trimLeadingZerosFromSHA1()
+        unchangedResolve()
+    }
+
+    def trimLeadingZerosFromSHA1() {
+        //remove leading zeros from sha1 checksum
+        new File("${module.jarFile.absolutePath}.sha1").text = "e14c6ef59816760e2c9b5a57157e8ac9de4012"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
new file mode 100644
index 0000000..ddf711e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
@@ -0,0 +1,500 @@
+/*
+ * 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.caching
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
+class CachedMissingModulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    public void "caches missing module when module found in another repository"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+        def moduleInRepo1 = repo1.module("group", "projectA", "1.2")
+        def moduleInRepo2 = repo2.module('group', 'projectA', '1.2').publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}"}
+    ivy { url "${repo2.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        moduleInRepo1.ivy.expectGetMissing()
+        moduleInRepo1.jar.expectHeadMissing()
+        moduleInRepo2.ivy.expectGet()
+        moduleInRepo2.jar.expectGet()
+
+        then:
+        succeeds("showMissing")
+
+        when:
+        server.resetExpectations() // Missing status in repo1 is cached
+
+        then:
+        succeeds('showMissing')
+    }
+
+    def "checks for missing modules in each repository when run with --refresh-dependencies"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+        def moduleInRepo1 = repo1.module("group", "projectA", "1.2")
+        def moduleInRepo2 = repo2.module('group', 'projectA', '1.2').publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}"}
+    ivy { url "${repo2.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        moduleInRepo1.ivy.expectGetMissing()
+        moduleInRepo1.jar.expectHeadMissing()
+        moduleInRepo2.ivy.expectGet()
+        moduleInRepo2.jar.expectGet()
+
+        then:
+        succeeds("showMissing")
+
+        when:
+        moduleInRepo1.publish()
+        server.resetExpectations()
+        then:
+        succeeds("showMissing")
+
+        when:
+        executer.withArgument("--refresh-dependencies")
+        moduleInRepo1.ivy.expectHead()
+        moduleInRepo1.ivy.sha1.expectGet()
+        moduleInRepo1.jar.expectHead()
+        moduleInRepo1.jar.sha1.expectGet()
+
+        then:
+        succeeds("showMissing")
+    }
+
+    def "cached empty version list is ignored when no module for dynamic version is available in any repo"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        buildFile << """
+            repositories {
+                maven {
+                    name 'repo1'
+                    url '${repo1.uri}'
+                }
+                maven {
+                    name 'repo2'
+                    url '${repo2.uri}'
+                }
+            }
+            configurations { compile }
+            dependencies {
+                compile 'group:projectA:latest.integration'
+            }
+
+            task retrieve(type: Sync) {
+                into 'libs'
+                from configurations.compile
+            }
+            """
+
+        when:
+        def repo1MetaData = repo1.getModuleMetaData("group", "projectA")
+        def repo1DirList = repo1.directory("group", "projectA")
+        def repo2MetaData = repo2.getModuleMetaData("group", "projectA")
+        def repo2DirLib = repo2.directory("group", "projectA")
+        def repo2Module = repo2.module("group", "projectA", "1.0")
+
+        repo1MetaData.expectGetMissing()
+        repo1DirList.expectGet()
+        repo2MetaData.expectGetMissing()
+        repo2DirLib.expectGet()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find any matches for group:projectA:latest.integration as no versions of group:projectA are available.
+Searched in the following locations:
+    ${repo1MetaData.uri}
+    ${repo1DirList.uri}
+    ${repo2MetaData.uri}
+    ${repo2DirLib.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        repo1MetaData.expectGetMissing()
+        repo1DirList.expectGet()
+        repo2MetaData.expectGetMissing()
+        repo2DirLib.expectGet()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find any matches for group:projectA:latest.integration as no versions of group:projectA are available.
+Searched in the following locations:
+    ${repo1MetaData.uri}
+    ${repo1DirList.uri}
+    ${repo2MetaData.uri}
+    ${repo2DirLib.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        repo2Module.publish()
+        repo1MetaData.expectGetMissing()
+        repo1DirList.expectGet()
+        repo2MetaData.expectGet()
+        repo2Module.pom.expectGet()
+        repo2Module.getArtifact().expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+
+        then:
+        run 'retrieve'
+    }
+
+    def "cached missing module is ignored if module is not available in any repo"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo1Module = repo1.module("group", "projectA", "1.0")
+        def repo1Artifact = repo1Module.artifact
+
+        def repo2 = mavenHttpRepo("repo2")
+        def repo2Module = repo2.module("group", "projectA", "1.0")
+        def repo2Artifact = repo2Module.artifact
+
+        buildFile << """
+    repositories {
+        maven {
+            name 'repo1'
+            url '${repo1.uri}'
+        }
+        maven {
+            name 'repo2'
+            url '${repo2.uri}'
+        }
+    }
+    configurations { compile }
+    dependencies {
+        compile 'group:projectA:1.0'
+    }
+
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.compile
+    }
+    """
+
+        when:
+        repo1Module.pom.expectGetMissing()
+        repo1Artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Artifact.expectHeadMissing()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find group:projectA:1.0.
+Searched in the following locations:
+    ${repo1Module.pom.uri}
+    ${repo1Module.artifact.uri}
+    ${repo2Module.pom.uri}
+    ${repo2Module.artifact.uri}
+Required by:
+""")
+
+        when:
+        repo1Module.pom.expectGetMissing()
+        repo1Artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Artifact.expectHeadMissing()
+
+        then:
+        fails 'retrieve'
+
+        failure.assertHasCause("""Could not find group:projectA:1.0.
+Searched in the following locations:
+    ${repo1Module.pom.uri}
+    ${repo1Module.artifact.uri}
+    ${repo2Module.pom.uri}
+    ${repo2Module.artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        repo1Module.pom.expectGetMissing()
+        repo1Artifact.expectHeadMissing()
+        repo2Module.publish()
+        repo2Module.pom.expectGet()
+        repo2Artifact.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+
+        then:
+        run 'retrieve'
+    }
+
+    def "cached missing module is ignored when no module for dynamic version is available in any repo"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo1Module = repo1.module("group", "projectA", "1.0").publish()
+        def repo2 = mavenHttpRepo("repo2")
+        def repo2Module = repo2.module("group", "projectA", "1.0").publish()
+
+        buildFile << """
+    repositories {
+        maven {
+            name 'repo1'
+            url '${repo1.uri}'
+        }
+        maven {
+            name 'repo2'
+            url '${repo2.uri}'
+        }
+    }
+    configurations { conf1; conf2 }
+    dependencies {
+        conf1 'group:projectA:1.0'
+        conf2 'group:projectA:1.+'
+    }
+
+    task cache << { configurations.conf1.files }
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.conf2
+    }
+    """
+
+        and:
+        repo1Module.pom.expectGetMissing()
+        repo1Module.artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Module.artifact.expectHeadMissing()
+        fails 'cache'
+        failure.assertHasCause("Could not find group:projectA:1.0.")
+
+        when:
+        server.resetExpectations()
+        repo1Module.rootMetaData.expectGet()
+        repo2Module.rootMetaData.expectGet()
+        repo1Module.pom.expectGetMissing()
+        repo1Module.artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Module.artifact.expectHeadMissing()
+
+        then:
+        fails 'retrieve'
+        failure.assertHasCause("""Could not find any matches for group:projectA:1.+ as no versions of group:projectA are available.
+Searched in the following locations:
+    ${repo1Module.rootMetaData.uri}
+    ${repo1Module.pom.uri}
+    ${repo1Module.artifact.uri}
+    ${repo2Module.rootMetaData.uri}
+    ${repo2Module.pom.uri}
+    ${repo2Module.artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        repo1Module.rootMetaData.expectGet()
+        repo1Module.pom.expectGetMissing()
+        repo1Module.artifact.expectHeadMissing()
+        repo2Module.rootMetaData.expectGet()
+        repo2Module.pom.expectGetMissing()
+        repo2Module.artifact.expectHeadMissing()
+
+        then:
+        fails 'retrieve'
+        failure.assertHasCause("""Could not find any matches for group:projectA:1.+ as no versions of group:projectA are available.
+Searched in the following locations:
+    ${repo1Module.rootMetaData.uri}
+    ${repo1Module.pom.uri}
+    ${repo1Module.artifact.uri}
+    ${repo2Module.rootMetaData.uri}
+    ${repo2Module.pom.uri}
+    ${repo2Module.artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        repo1Module.pom.expectGetMissing()
+        repo1Module.artifact.expectHeadMissing()
+        repo2Module.pom.expectGet()
+        repo2Module.artifact.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        // TODO - should not need to do this
+        repo1Module.pom.expectHeadMissing()
+        repo1Module.artifact.expectHeadMissing()
+
+        then:
+        run 'retrieve'
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.isParallel() })
+    def "hit each remote repo only once per build and missing module"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo1Module = repo1.module("group", "projectA", "1.0")
+        def repo1Artifact = repo1Module.artifact
+        def repo2 = mavenHttpRepo("repo2")
+        def repo2Module = repo2.module("group", "projectA", "1.0")
+        def repo2Artifact = repo2Module.artifact
+
+        settingsFile << "include 'subproject'"
+        buildFile << """
+            allprojects{
+                repositories {
+                    maven {
+                        name 'repo1'
+                        url '${repo1.uri}'
+                    }
+                    maven {
+                        name 'repo2'
+                        url '${repo2.uri}'
+                    }
+                }
+            }
+            configurations {
+                config1
+            }
+            dependencies {
+                config1 'group:projectA:1.0'
+            }
+
+            task resolveConfig1 << {
+                   configurations.config1.incoming.resolutionResult.allDependencies{
+                        it instanceof UnresolvedDependencyResult
+                   }
+            }
+
+            project(":subproject"){
+                configurations{
+                    config2
+                }
+                dependencies{
+                    config2 'group:projectA:1.0'
+                }
+                task resolveConfig2 << {
+                    configurations.config2.incoming.resolutionResult.allDependencies{
+                        it instanceof UnresolvedDependencyResult
+                    }
+                }
+            }
+        """
+        when:
+        repo1Module.pom.expectGetMissing()
+        repo1Artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Artifact.expectHeadMissing()
+
+        then:
+        run('resolveConfig1')
+
+        when:
+        server.resetExpectations()
+        repo1Module.pom.expectGetMissing()
+        repo1Artifact.expectHeadMissing()
+        repo2Module.pom.expectGetMissing()
+        repo2Artifact.expectHeadMissing()
+
+        then:
+        run "resolveConfig1", "resolveConfig2"
+    }
+
+    def "does not hit remote repositories if version is available in local repo and missing from remote repo"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo1Module = repo1.module("group", "projectA", "1.0")
+        def repo2 = mavenRepo("repo2")
+        def repo2Module = repo2.module("group", "projectA", "1.0")
+
+        buildFile << """
+        repositories {
+           maven {
+               name 'repo1'
+               url '${repo1.uri}'
+           }
+           maven {
+               name 'repo2'
+               url '${repo2.uri}'
+           }
+       }
+       configurations { compile }
+       dependencies {
+           compile 'group:projectA:1.0'
+       }
+
+       task retrieve(type: Sync) {
+           into 'libs'
+           from configurations.compile
+       }
+       """
+
+        when:
+        repo2Module.publish()
+        repo1Module.pom.expectGetMissing()
+        repo1Module.artifact.expectHeadMissing()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+
+        then:
+        run 'retrieve'
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
new file mode 100644
index 0000000..1e039bc
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 "version list, 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.+'
+                two 'org:lib:1.+'
+            }
+            //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 { 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/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/RecoverFromBrokenResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/RecoverFromBrokenResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..80bed13
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/RecoverFromBrokenResolutionIntegrationTest.groovy
@@ -0,0 +1,354 @@
+/*
+ * 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.caching
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.hamcrest.Matchers
+
+class RecoverFromBrokenResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    MavenHttpRepository repo
+    MavenHttpModule module
+
+    private void buildFileWithSnapshotDependency() {
+        buildFile << """
+            configurations {
+                compile
+            }
+
+            configurations.all {
+                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+            }
+
+            dependencies {
+                compile 'group:projectA:1.0-SNAPSHOT'
+            }
+
+            task retrieve(type: Sync) {
+                into 'libs'
+                from configurations.compile
+            }
+            """
+    }
+
+    def "can run offline mode after hitting broken repo url"() {
+        given:
+        buildFileWithSnapshotDependency()
+        and:
+        noAuthorizationRepo()
+
+        publishedMavenModule()
+        when:
+        moduleAvailableViaHttp()
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        server.addBroken("/")
+        then:
+        fails 'retrieve'
+
+        and:
+        //TODO should expose the failed task in the error message like
+        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
+        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertThatCause(Matchers.containsString("Received status code 500 from server: broken"))
+
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
+    }
+
+    def "can run offline mode after connection problem with repo url using unique snapshot version"() {
+        given:
+        buildFileWithSnapshotDependency()
+        noAuthorizationRepo()
+        publishedMavenModule()
+
+        when:
+        moduleAvailableViaHttp()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        int port = server.port
+        server.stop()
+        then:
+        fails 'retrieve'
+
+        and:
+        //TODO should expose the failed task in the error message like
+        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
+        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
+    }
+
+    def "can run offline mode after connection problem with repo url using non unique snapshot version"() {
+        given:
+        buildFileWithSnapshotDependency()
+        noAuthorizationRepo()
+        publishedMavenModule(true)
+
+        when:
+        moduleAvailableViaHttpWithoutMetaData()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        int port = server.port
+        server.stop()
+        then:
+        fails 'retrieve'
+
+        and:
+        //TODO should expose the failed task in the error message like
+        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
+        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
+    }
+
+    def "can run offline mode after authentication fails on remote repo"() {
+        given:
+        buildFileWithSnapshotDependency()
+        and:
+        authorizationRepo()
+        when:
+        publishedMavenModule()
+        server.resetExpectations()
+        module.metaData.expectGet('username', 'password')
+        module.pom.expectGet('username', 'password')
+        module.artifact.expectGet('username', 'password')
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        server.allowGetOrHead('/repo/group/projectA/1.0-SNAPSHOT/maven-metadata.xml', 'bad_username', 'password', module.metaDataFile)
+
+        then:
+        fails 'retrieve'
+
+        and:
+        //TODO should expose the failed task in the error message like
+        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
+        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertThatCause(Matchers.containsString("Received status code 401 from server: Unauthorized"))
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifact.file)
+    }
+
+    def "can run offline mode after connection problem with repo when using ivy changing modules"() {
+        given:
+        def ivyRepo = ivyHttpRepo("ivyRepo")
+        def ivyModule = ivyRepo.module("group", "projectA", "1.0")
+        ivyModule.publish()
+        and:
+        buildFile.text = """
+                  repositories {
+                       ivy {
+                           name 'repo'
+                           url '${ivyRepo.uri}'
+                       }
+                  }
+                  configurations {
+                      compile
+                  }
+
+                  configurations.all {
+                      resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+                  }
+
+                  dependencies {
+                      compile group:'group', name:'projectA', version:'1.0', changing:true
+                  }
+
+                  task retrieve(type: Sync) {
+                      into 'libs'
+                      from configurations.compile
+                  }"""
+        when:
+        ivyModule.ivy.expectGet()
+        ivyModule.jar.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        int port = server.port
+        server.stop()
+        then:
+        fails 'retrieve'
+
+        and:
+        //TODO should expose the failed task in the error message like
+        //failure.assertHasDescription('Execution failed for task \':retrieve\'.')
+        //failure.assertHasCause('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertThatCause(Matchers.containsString("Connection to http://localhost:${port} refused"))
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.0.jar').assertIsCopyOf(ivyModule.jarFile)
+    }
+
+    def "can run offline mode after connection problem with repo when using ivy dynamic version"() {
+        given:
+        def ivyRepo = ivyHttpRepo("ivyRepo")
+        def ivyModule = ivyRepo.module("group", "projectA", "1.1")
+        ivyModule.publish()
+        and:
+        buildFile << """
+                  repositories {
+                       ivy { url '${ivyRepo.uri}' }
+                  }
+                  configurations {
+                      compile
+                  }
+                  configurations.all {
+                      resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
+                  }
+                  dependencies {
+                      compile group:'group', name:'projectA', version:'1.+'
+                  }
+
+                  task retrieve(type: Sync) {
+                      into 'libs'
+                      from configurations.compile
+                  }"""
+        when:
+        ivyModule.repository.directoryList('group', 'projectA').expectGet()
+        ivyModule.ivy.expectGet()
+        ivyModule.jar.expectGet()
+
+        then:
+        run 'retrieve'
+
+        when:
+        server.resetExpectations()
+        int port = server.port
+        server.stop()
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasDescription('Could not resolve all dependencies for configuration \':compile\'.')
+        failure.assertHasCause("Could not list versions using Ivy pattern 'http://localhost:${port}/ivyRepo/[organisation]/[module]/[revision]/ivy-[revision].xml")
+        failure.assertHasCause("Connection to http://localhost:${port} refused")
+
+        when:
+        server.resetExpectations()
+        then:
+        executer.withArgument("--offline")
+        run 'retrieve'
+        and:
+        file('libs/projectA-1.1.jar').assertIsCopyOf(ivyModule.jarFile)
+    }
+
+    def moduleAvailableViaHttp() {
+        module.metaData.expectGet()
+        module.pom.expectGet()
+        module.getArtifact().expectGet()
+    }
+
+    def moduleAvailableViaHttpWithoutMetaData() {
+        module.metaData.expectGetMissing()
+        module.pom.expectGet()
+        module.getArtifact().expectGet()
+    }
+
+
+    private MavenHttpModule publishedMavenModule(withNonUniqueVersion = false) {
+        module = repo.module("group", "projectA", "1.0-SNAPSHOT")
+        if (withNonUniqueVersion) {
+            module.withNonUniqueSnapshots()
+        }
+        module.publish()
+        module
+    }
+
+    private noAuthorizationRepo() {
+        repo = mavenHttpRepo("repo")
+
+        buildFile << """
+        repositories {
+            maven {
+                name 'repo'
+                url '${repo.uri}'
+            }
+        } """
+    }
+
+    private authorizationRepo() {
+        repo = mavenHttpRepo("repo")
+        buildFile << """
+        repositories {
+            maven {
+                url '${repo.uri}'
+                credentials {
+                    password 'password'
+                    username 'username'
+                }
+            }
+        }
+        """
+    }
+
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..c069548
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * 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.http
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.keystore.TestKeyStore
+import org.junit.Rule
+
+import static org.gradle.util.Matchers.containsText
+
+abstract class AbstractHttpsRepoResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    @Rule TestResources resources = new TestResources(temporaryFolder)
+    TestKeyStore keyStore
+
+    abstract protected String setupRepo()
+
+    def "resolve with server certificate"() {
+        keyStore = TestKeyStore.init(resources.dir)
+        keyStore.enableSslWithServerCert(server)
+
+        def repoType = setupRepo()
+        setupBuildFile(repoType)
+
+        when:
+        keyStore.configureServerCert(executer)
+        succeeds "libs"
+
+        then:
+        file('libs').assertHasDescendants('my-module-1.0.jar')
+    }
+
+    def "resolve with server and client certificate"() {
+        keyStore = TestKeyStore.init(resources.dir)
+        keyStore.enableSslWithServerAndClientCerts(server)
+
+        def repoType = setupRepo()
+        setupBuildFile(repoType)
+
+        when:
+        keyStore.configureServerAndClientCerts(executer)
+        succeeds "libs"
+
+        then:
+        file('libs').assertHasDescendants('my-module-1.0.jar')
+    }
+
+    def "decent error message when client can't authenticate server"() {
+        keyStore = TestKeyStore.init(resources.dir)
+        keyStore.enableSslWithServerCert(server)
+
+        def repoType = setupRepo()
+        setupBuildFile(repoType)
+
+        when:
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        keyStore.configureIncorrectServerCert(executer)
+        fails "libs"
+
+        then:
+        failure.assertThatCause(containsText("Could not GET '${server.uri}/repo1/my-group/my-module/1.0/"))
+        failure.assertHasCause("peer not authenticated")
+    }
+
+    def "decent error message when server can't authenticate client"() {
+        keyStore = TestKeyStore.init(resources.dir)
+        keyStore.enableSslWithServerAndBadClientCert(server)
+
+        def repoType = setupRepo()
+        setupBuildFile(repoType)
+
+        when:
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        keyStore.configureServerAndClientCerts(executer)
+        fails "libs"
+
+        then:
+        failure.assertThatCause(containsText("Could not GET '${server.uri}/repo1/my-group/my-module/1.0/"))
+        failure.assertHasCause("peer not authenticated")
+    }
+
+    private void setupBuildFile(String repoType) {
+        buildFile << """
+repositories {
+    $repoType { url '${server.uri}/repo1' }
+}
+configurations { compile }
+dependencies {
+    compile 'my-group:my-module:1.0'
+}
+task libs(type: Copy) {
+    into 'libs'
+    from configurations.compile
+}
+        """
+    }
+}
+
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..726d414
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpAuthenticationDependencyResolutionIntegrationTest.groovy
@@ -0,0 +1,215 @@
+/*
+ * 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.http
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.hamcrest.Matchers
+import spock.lang.Unroll
+
+class HttpAuthenticationDependencyResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    static String badCredentials = "credentials{username 'testuser'; password 'bad'}"
+
+    @Unroll
+    public void "can resolve dependencies from #authScheme authenticated HTTP ivy repository"() {
+        given:
+        def moduleA = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+        ivyHttpRepo.module('group', 'projectB', '2.1').publish()
+        ivyHttpRepo.module('group', 'projectB', '2.2').publish()
+        def moduleB = ivyHttpRepo.module('group', 'projectB', '2.3').publish()
+        ivyHttpRepo.module('group', 'projectB', '3.0').publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+
+        credentials {
+            password 'password'
+            username 'username'
+        }
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+    compile 'group:projectB:2.+'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar','projectB-2.3.jar']
+}
+"""
+
+        when:
+        server.authenticationScheme = authScheme
+
+        and:
+        moduleA.ivy.expectGet('username', 'password')
+        moduleA.jar.expectGet('username', 'password')
+        server.expectGetDirectoryListing("/repo/group/projectB/", 'username', 'password', moduleB.backingModule.moduleDir.parentFile)
+        moduleB.ivy.expectGet('username', 'password')
+        moduleB.jar.expectGet('username', 'password')
+
+        then:
+        succeeds('listJars')
+
+        where:
+        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
+    }
+
+    @Unroll
+    public void "can resolve dependencies from #authScheme authenticated HTTP maven repository"() {
+        given:
+        def moduleA = mavenHttpRepo.module('group', 'projectA', '1.2').publish()
+        mavenHttpRepo.module('group', 'projectB', '2.0').publish()
+        mavenHttpRepo.module('group', 'projectB', '2.2').publish()
+        def moduleB = mavenHttpRepo.module('group', 'projectB', '2.3').publish()
+        mavenHttpRepo.module('group', 'projectB', '3.0').publish()
+        def moduleC = mavenHttpRepo.module('group', 'projectC', '3.1-SNAPSHOT').publish()
+        def moduleD = mavenHttpRepo.module('group', 'projectD', '4-SNAPSHOT').withNonUniqueSnapshots().publish()
+        and:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+
+        credentials {
+            password 'password'
+            username 'username'
+        }
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+    compile 'group:projectB:2.+'
+    compile 'group:projectC:3.1-SNAPSHOT'
+    compile 'group:projectD:4-SNAPSHOT'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-2.3.jar', 'projectC-3.1-SNAPSHOT.jar', 'projectD-4-SNAPSHOT.jar']
+}
+"""
+
+        when:
+        server.authenticationScheme = authScheme
+
+        and:
+        moduleA.pom.expectGet('username', 'password')
+        moduleA.artifact.expectGet('username', 'password')
+        moduleB.rootMetaData.expectGet('username', 'password')
+        moduleB.pom.expectGet('username', 'password')
+        moduleB.artifact.expectGet('username', 'password')
+
+        moduleC.metaData.expectGet('username', 'password')
+        moduleC.pom.expectGet('username', 'password')
+        moduleC.artifact.expectGet('username', 'password')
+
+        moduleD.metaData.expectGet('username', 'password')
+        moduleD.pom.expectGet('username', 'password')
+        moduleD.artifact.expectGet('username', 'password')
+
+        then:
+        succeeds('listJars')
+
+        where:
+        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
+    }
+
+    @Unroll
+    def "reports failure resolving with #credsName credentials from #authScheme authenticated HTTP ivy repository"() {
+        given:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+        when:
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+   repositories {
+       ivy {
+            url "${ivyHttpRepo.uri}"
+            $creds
+        }
+   }
+   configurations { compile }
+   dependencies {
+       compile 'group:projectA:1.2'
+   }
+   task listJars << {
+       assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+   }
+   """
+
+        and:
+        server.authenticationScheme = authScheme
+        server.allowGetOrHead('/repo/group/projectA/1.2/ivy-1.2.xml', 'username', 'password', module.ivyFile)
+
+        then:
+        fails 'listJars'
+
+        and:
+        failure
+            .assertHasDescription('Execution failed for task \':listJars\'.')
+            .assertResolutionFailure(':compile')
+            .assertThatCause(Matchers.containsString('Received status code 401 from server: Unauthorized'))
+
+        where:
+        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST, HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
+        credsName << ['empty', 'empty', 'bad', 'bad']
+        creds << ['', '', badCredentials, badCredentials]
+    }
+
+    @Unroll
+    def "reports failure resolving with #credsName credentials from #authScheme authenticated HTTP maven repository"() {
+        given:
+        def module = mavenHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+ repositories {
+     maven {
+         url "${mavenHttpRepo.uri}"
+        $creds
+     }
+ }
+ configurations { compile }
+ dependencies {
+     compile 'group:projectA:1.2'
+ }
+ task listJars << {
+     assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+ }
+ """
+
+        and:
+        server.authenticationScheme = authScheme
+        module.pom.allowGetOrHead('username', 'password')
+
+        then:
+        fails 'listJars'
+
+        and:
+        failure
+            .assertHasDescription('Execution failed for task \':listJars\'.')
+            .assertResolutionFailure(':compile')
+            .assertThatCause(Matchers.containsString('Received status code 401 from server: Unauthorized'))
+
+        where:
+        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST, HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
+        credsName << ['empty', 'empty', 'bad', 'bad']
+        creds << ['', '', badCredentials, badCredentials]
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..e506348
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpEncodingDependencyResolutionIntegrationTest.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.integtests.resolve.http
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+public class HttpEncodingDependencyResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    public void "handles gzip encoded content"() {
+        given:
+        def repo = ivyRepo()
+        def module = repo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "http://localhost:${server.port}/repo" }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        server.expectGetGZipped('/repo/group/projectA/1.2/ivy-1.2.xml', module.ivyFile)
+        server.expectGetGZipped('/repo/group/projectA/1.2/projectA-1.2.jar', module.jarFile)
+
+        then:
+        succeeds('listJars')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..cf8a200
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpProxyResolveIntegrationTest.groovy
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.http
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.TestProxyServer
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Unroll
+
+class HttpProxyResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    @Rule TestProxyServer proxyServer = new TestProxyServer(server)
+    @Rule SetSystemProperties systemProperties = new SetSystemProperties()
+
+    public void "uses configured proxy to access remote HTTP repository"() {
+        proxyServer.start()
+
+        given:
+        def repo = ivyHttpRepo
+        def module = repo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "http://not.a.real.domain/repo" }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}")
+
+        and:
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds('listJars')
+
+        and:
+        proxyServer.requestCount == 2
+    }
+
+    public void "uses authenticated proxy to access remote HTTP repository"() {
+        proxyServer.start()
+
+        given:
+        def repo = ivyHttpRepo
+        def module = repo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "http://not.a.real.domain/repo"
+    }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}", "-Dhttp.nonProxyHosts=foo",
+                               "-Dhttp.proxyUser=proxyUser", "-Dhttp.proxyPassword=proxyPassword")
+
+        and:
+        proxyServer.requireAuthentication('proxyUser', 'proxyPassword')
+
+        and:
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds('listJars')
+
+        and:
+        proxyServer.requestCount == 2
+    }
+
+    @Unroll
+    public void "passes target credentials to #authScheme authenticated server via proxy"() {
+        proxyServer.start()
+
+        given:
+        def repo = ivyHttpRepo
+        def module = repo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "http://not.a.real.domain/repo"
+        credentials {
+            username 'targetUser'
+            password 'targetPassword'
+        }
+    }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.2' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+}
+"""
+
+        when:
+        server.authenticationScheme = authScheme
+        executer.withArguments("-Dhttp.proxyHost=localhost", "-Dhttp.proxyPort=${proxyServer.port}", "-Dhttp.proxyUser=proxyUser", "-Dhttp.proxyPassword=proxyPassword")
+
+        and:
+        proxyServer.requireAuthentication('proxyUser', 'proxyPassword')
+
+        and:
+        module.ivy.expectGet('targetUser', 'targetPassword')
+        module.jar.expectGet('targetUser', 'targetPassword')
+
+        then:
+        succeeds('listJars')
+
+        and:
+        // 1 extra request for authentication
+        proxyServer.requestCount == 3
+
+        where:
+        authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy
new file mode 100644
index 0000000..d61bd18
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpRedirectResolveIntegrationTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * 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.http
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Issue
+
+class HttpRedirectResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    @Rule SetSystemProperties systemProperties = new SetSystemProperties()
+    @Rule public final HttpServer server2 = new HttpServer()
+
+    public void "resolves module artifacts via HTTP redirect"() {
+        server2.start()
+
+        given:
+        def module = ivyRepo().module('group', 'projectA').publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "http://localhost:${server.port}/repo" }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.0' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar']
+}
+"""
+
+        when:
+        server.expectGetRedirected('/repo/group/projectA/1.0/ivy-1.0.xml', "http://localhost:${server2.port}/redirected/group/projectA/1.0/ivy-1.0.xml")
+        server2.expectGet('/redirected/group/projectA/1.0/ivy-1.0.xml', module.ivyFile)
+        server.expectGetRedirected('/repo/group/projectA/1.0/projectA-1.0.jar', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.jar")
+        server2.expectGet('/redirected/group/projectA/1.0/projectA-1.0.jar', module.jarFile)
+
+        then:
+        succeeds('listJars')
+    }
+
+    @Issue('GRADLE-2196')
+    public void "resolves artifact-only module via HTTP redirect"() {
+        server2.start()
+
+        given:
+        def module = ivyRepo().module('group', 'projectA').publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "http://localhost:${server.port}/repo" }
+}
+configurations { compile }
+dependencies { compile 'group:projectA:1.0 at zip' }
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.0.zip']
+}
+"""
+
+        when:
+        server.expectGetMissing('/repo/group/projectA/1.0/ivy-1.0.xml')
+        server.expectHeadRedirected('/repo/group/projectA/1.0/projectA-1.0.zip', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.zip")
+        server2.expectHead('/redirected/group/projectA/1.0/projectA-1.0.zip', module.jarFile)
+        server.expectGetRedirected('/repo/group/projectA/1.0/projectA-1.0.zip', "http://localhost:${server2.port}/redirected/group/projectA/1.0/projectA-1.0.zip")
+        server2.expectGet('/redirected/group/projectA/1.0/projectA-1.0.zip', module.jarFile)
+
+        then:
+        succeeds('listJars')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractComponentSelectionRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractComponentSelectionRulesIntegrationTest.groovy
new file mode 100644
index 0000000..e9db45d
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractComponentSelectionRulesIntegrationTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+
+abstract class AbstractComponentSelectionRulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    Map<String, IvyHttpModule> modules = [:]
+
+    def setup() {
+        modules['1.0'] = ivyHttpRepo.module("org.utils", "api", "1.0").publish()
+        modules['1.1'] = ivyHttpRepo.module("org.utils", "api", "1.1").withBranch("test").withStatus("milestone").publish()
+        modules['1.2'] = ivyHttpRepo.module("org.utils", "api", "1.2").publish()
+        modules['2.0'] = ivyHttpRepo.module("org.utils", "api", "2.0").withBranch("test").withStatus("milestone").publish()
+        modules['2.1'] = ivyHttpRepo.module("org.utils", "api", "2.1").publish()
+        modules['1.0-lib'] = ivyHttpRepo.module("org.utils", "lib", "1.0").publish()
+        modules['1.1-lib'] = ivyHttpRepo.module("org.utils", "lib", "1.1").withBranch("test").withStatus("milestone").publish()
+    }
+
+    String getBaseBuildFile() {
+        """
+        def candidates = []
+        configurations { conf }
+        repositories {
+            ivy { url "${ivyRepo.uri}" }
+        }
+        task resolveConf << { configurations.conf.files }
+        """
+    }
+
+    String getHttpBaseBuildFile() {
+        """
+        def candidates = []
+        configurations { conf }
+        repositories {
+            ivy { url "${ivyHttpRepo.uri}" }
+        }
+        task resolveConf << { configurations.conf.files }
+        """
+    }
+
+    static def rules = [
+            "reject all": """{ ComponentSelection selection ->
+                selection.reject("rejecting everything")
+                candidates << selection.candidate.version
+            }
+            """,
+            "reject all with metadata": """{ ComponentSelection selection, ComponentMetadata metadata ->
+                selection.reject("rejecting everything")
+                candidates << selection.candidate.version
+            }
+            """,
+            "select 1.1": """{ ComponentSelection selection ->
+                if (selection.candidate.version != '1.1') {
+                    selection.reject("not 1.1")
+                }
+                candidates << selection.candidate.version
+            }
+            """,
+            "select 2.0": """{ ComponentSelection selection ->
+                if (selection.candidate.version != '2.0') {
+                    selection.reject("not 2.0")
+                }
+                candidates << selection.candidate.version
+            }
+            """,
+            "select 2.1": """{ ComponentSelection selection ->
+                if (selection.candidate.version != '2.1') {
+                    selection.reject("not 2.1")
+                }
+                candidates << selection.candidate.version
+            }
+            """,
+            "select branch": """{ ComponentSelection selection, IvyModuleDescriptor ivy ->
+                if (ivy.branch != 'test') {
+                    selection.reject("not branch")
+                }
+                candidates << selection.candidate.version
+            }
+            """,
+            "select status": """{ ComponentSelection selection, ComponentMetadata metadata ->
+                if (metadata.status != 'milestone') {
+                    selection.reject("not milestone")
+                }
+                candidates << selection.candidate.version
+            }
+            """
+    ]
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyDescriptorExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyDescriptorExcludeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..8015fa9
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyDescriptorExcludeResolveIntegrationTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class AbstractIvyDescriptorExcludeResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    protected static final EXCLUDE_ATTRIBUTE = 'exclude'
+
+    def setup() {
+        buildFile << """
+repositories {
+    ivy {
+        url '${ivyRepo.uri}'
+    }
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile 'org.gradle.test:a:1.0'
+}
+
+task check(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+    }
+
+    protected void succeedsDependencyResolution() {
+        succeeds 'check'
+    }
+
+    protected void assertResolvedFiles(List<String> files) {
+        file('libs').assertHasDescendants(files as String[])
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy
new file mode 100644
index 0000000..d7199d5
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 spock.lang.Issue
+import spock.lang.Unroll
+
+class ComponentSelectionRulesDependencyResolveIntegTest extends AbstractComponentSelectionRulesIntegrationTest {
+    @Unroll
+    def "uses '#rule' rule to choose component for #selector" () {
+
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:${selector}"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ${rules[rule]}
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                def artifacts = configurations.conf.resolvedConfiguration.resolvedArtifacts
+                assert artifacts.size() == 1
+                assert artifacts[0].moduleVersion.id.version == '${chosen}'
+                assert candidates == ${candidates}
+            }
+"""
+
+        when:
+        if (selector != "1.1") {
+            ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        }
+        downloadedMetadata.each {
+            modules[it].ivy.expectGet()
+        }
+        modules[chosen].artifact.expectGet()
+
+        then:
+        succeeds 'resolveConf'
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds 'resolveConf'
+
+        where:
+        selector             | rule            | chosen | candidates                            | downloadedMetadata
+        "1.+"                | "select 1.1"    | "1.1"  | '["1.2", "1.1"]'                      | ['1.1']
+        "1.+"                | "select status" | "1.1"  | '["1.2", "1.1"]'                      | ['1.2', '1.1']
+        "1.+"                | "select branch" | "1.1"  | '["1.2", "1.1"]'                      | ['1.2', '1.1']
+        "latest.integration" | "select 2.1"    | "2.1"  | '["2.1"]'                             | ['2.1']
+        "latest.milestone"   | "select 2.0"    | "2.0"  | '["2.0"]'                             | ['2.1', '2.0']
+        "latest.milestone"   | "select status" | "2.0"  | '["2.0"]'                             | ['2.1', '2.0']
+        "latest.milestone"   | "select branch" | "2.0"  | '["2.0"]'                             | ['2.1', '2.0']
+        "1.1"                | "select 1.1"    | "1.1"  | '["1.1"]'                             | ['1.1']
+        "1.1"                | "select status" | "1.1"  | '["1.1"]'                             | ['1.1']
+        "1.1"                | "select branch" | "1.1"  | '["1.1"]'                             | ['1.1']
+    }
+
+    @Unroll
+    def "uses '#rule' rule to reject all candidates for dynamic version #selector" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:${selector}"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ${rules[rule]}
+                    }
+                }
+            }
+
+            task checkConf << {
+                def artifacts = configurations.conf.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL)
+                assert artifacts.size() == 0
+                assert candidates == ${candidates}
+            }
+"""
+
+        when:
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        downloadedMetadata.each {
+            modules[it].ivy.expectGet()
+        }
+
+        then:
+        succeeds 'checkConf'
+
+        when:
+        server.resetExpectations()
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+
+        then:
+        fails 'resolveConf'
+        failureHasCause("Could not find any version that matches org.utils:api:${selector}.")
+
+        when:
+        server.resetExpectations()
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+
+        then:
+        fails 'resolveConf'
+        failureHasCause("Could not find any version that matches org.utils:api:${selector}.")
+
+        where:
+        selector             | rule                       | candidates                            | downloadedMetadata
+        "1.+"                | "reject all"               | '["1.2", "1.1", "1.0"]'               | []
+        "latest.integration" | "reject all"               | '["2.1"]'                             | ['2.1']
+        "latest.milestone"   | "reject all"               | '["2.0"]'                             | ['2.1', '2.0']
+        "1.+"                | "reject all with metadata" | '["1.2", "1.1", "1.0"]'               | ['1.2', '1.1', '1.0']
+        "latest.integration" | "reject all with metadata" | '["2.1"]'                             | ['2.1']
+        "latest.milestone"   | "reject all with metadata" | '["2.0"]'                             | ['2.1', '2.0']
+        // latest.milestone is 2.0, but since the rule rejects it, we should never reach version 1.1
+        "latest.milestone"   | "select 1.1"               | '["2.0"]'                             | ['2.1', '2.0']
+    }
+
+    def "reports all candidates rejected by rule" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.+"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ${rules["reject all with metadata"]}
+                    }
+                }
+            }
+"""
+
+        when:
+        def dirList = ivyHttpRepo.directoryList("org.utils", "api")
+        dirList.expectGet()
+        modules["1.2"].ivy.expectGet()
+        modules["1.1"].ivy.expectGet()
+        modules["1.0"].ivy.expectGet()
+
+        then:
+        fails 'resolveConf'
+
+        and:
+        failureHasCause("""Could not find any version that matches org.utils:api:1.+.
+Versions that do not match:
+    2.1
+    2.0
+Versions rejected by component selection rules:
+    1.2
+    1.1
+    1.0
+Searched in the following locations:
+    ${dirList.uri}
+    ${modules["1.2"].ivy.uri}
+    ${modules["1.1"].ivy.uri}
+    ${modules["1.0"].ivy.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        dirList.expectGet()
+
+        then:
+        fails 'resolveConf'
+
+        and:
+        // TODO - this failure and the previous failure should report the same urls (whatever that happens to be)
+        failureHasCause("""Could not find any version that matches org.utils:api:1.+.
+Versions that do not match:
+    2.1
+    2.0
+Versions rejected by component selection rules:
+    1.2
+    1.1
+    1.0
+Searched in the following locations:
+    ${dirList.uri}
+Required by:
+""")
+    }
+
+    @Unroll
+    def "uses '#rule' rule to reject candidate for static version #selector" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:${selector}"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ${rules[rule]}
+                    }
+                }
+            }
+
+            task checkConf << {
+                def artifacts = configurations.conf.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL)
+                assert artifacts.size() == 0
+                assert candidates == ${candidates}
+            }
+"""
+
+        when:
+        downloadedMetadata.each {
+            modules[it].ivy.expectGet()
+        }
+
+        then:
+        succeeds 'checkConf'
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails 'resolveConf'
+        failureHasCause("Could not find org.utils:api:${selector}.")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails 'resolveConf'
+        failureHasCause("Could not find org.utils:api:${selector}.")
+
+        where:
+        selector             | rule            | candidates                            | downloadedMetadata
+        "1.0"                | "reject all"    | '["1.0"]'                             | ['1.0']
+        "1.0"                | "select 1.1"    | '["1.0"]'                             | ['1.0']
+        "1.0"                | "select status" | '["1.0"]'                             | ['1.0']
+        "1.0"                | "select branch" | '["1.0"]'                             | ['1.0']
+        "1.1"                | "reject all"    | '["1.1"]'                             | ['1.1']
+    }
+
+    @Unroll
+    def "can use component selection rule to choose component from different repository for #selector"() {
+        def ivyRepo2 = ivyRepo("repo2")
+        def module2 = ivyRepo2.module("org.utils", "api", "1.1").withBranch("other").publishWithChangedContent()
+
+        buildFile << """
+            configurations { conf }
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+                ivy { url "${ivyRepo2.uri}" }
+            }
+
+            dependencies {
+                conf "org.utils:api:${selector}"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection, IvyModuleDescriptor ivy ->
+                            if (ivy.branch != "other") {
+                                selection.reject("looking for other")
+                            }
+                        }
+                    }
+                }
+            }
+
+            task retrieve(type: Copy) {
+                from configurations.conf
+                into "libs"
+            }
+"""
+        when:
+        succeeds "retrieve"
+
+        then:
+        file("libs").assertHasDescendants("api-1.1.jar")
+        file("libs/api-1.1.jar").assertIsDifferentFrom(modules['1.1'].jarFile)
+        file("libs/api-1.1.jar").assertIsCopyOf(module2.jarFile)
+
+        where:
+        selector << ["1.1", "1.+"]
+    }
+
+    @Unroll
+    def "can control selection of components by module for #selector" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:${selector}"
+                conf "org.utils:lib:1.+"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        withModule("org.utils:api") ${rules[rule]}
+                        withModule("org.utils:api") { ComponentSelection cs ->
+                            assert cs.candidate.group == "org.utils"
+                            assert cs.candidate.module == "api"
+                        }
+                        withModule("some.other:module") { ComponentSelection cs ->
+                            throw new RuntimeException()
+                        }
+                        withModule("some.other:module") { ComponentSelection cs, IvyModuleDescriptor descriptor, ComponentMetadata metadata ->
+                            throw new RuntimeException()
+                        }
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                def artifacts = configurations.conf.resolvedConfiguration.resolvedArtifacts
+                assert artifacts.size() == 2
+                assert artifacts[0].moduleVersion.id.version == '${chosen}'
+                assert candidates == ${candidates}
+            }
+        """
+
+        when:
+        if (selector != "1.1") {
+            ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        }
+        downloadedMetadata.each {
+            modules[it].ivy.expectGet()
+        }
+        modules[chosen].artifact.expectGet()
+
+        ivyHttpRepo.directoryList("org.utils", "lib").expectGet()
+        modules["1.1-lib"].ivy.expectGet()
+        modules["1.1-lib"].artifact.expectGet()
+
+        then:
+        succeeds 'resolveConf'
+
+        where:
+        selector             | rule            | chosen | candidates                            | downloadedMetadata
+        "1.+"                | "select 1.1"    | "1.1"  | '["1.2", "1.1"]'                      | ['1.1']
+        "latest.milestone"   | "select status" | "2.0"  | '["2.0"]'                             | ['2.1', '2.0']
+        "1.1"                | "select branch" | "1.1"  | '["1.1"]'                             | ['1.1']
+    }
+
+    @Issue("GRADLE-3236")
+    def "can select a different component for the same selector in different configurations" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            configurations {
+                modules
+                modulesA {
+                    extendsFrom modules
+                    resolutionStrategy {
+                        componentSelection {
+                            all { ComponentSelection selection, IvyModuleDescriptor ivy ->
+                                println "A is evaluating \$selection.candidate"
+                                if (selection.candidate.version != "1.1") { selection.reject("Rejected by A") }
+                            }
+                        }
+                    }
+                }
+                modulesB {
+                    extendsFrom modules
+                    resolutionStrategy {
+                        componentSelection {
+                            all { ComponentSelection selection, IvyModuleDescriptor ivy ->
+                                println "B is evaluating \$selection.candidate"
+                                if (selection.candidate.version != "1.0") { selection.reject("Rejected by B") }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                modules "org.utils:api:1.+"
+            }
+
+            task verify {
+                doLast {
+                    assert configurations.modulesA.files.collect { it.name } == [ 'api-1.1.jar']
+                    assert configurations.modulesB.files.collect { it.name } == [ 'api-1.0.jar']
+                }
+            }
+        """
+
+        when:
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        modules['1.2'].ivy.expectGet()
+        modules['1.1'].ivy.expectGet()
+        modules['1.0'].ivy.expectGet()
+        modules['1.1'].artifact.expectGet()
+        modules['1.0'].artifact.expectGet()
+
+        then:
+        succeeds "verify"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds "verify"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesErrorHandlingIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesErrorHandlingIntegTest.groovy
new file mode 100644
index 0000000..89a2eab
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesErrorHandlingIntegTest.groovy
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class ComponentSelectionRulesErrorHandlingIntegTest extends AbstractComponentSelectionRulesIntegrationTest {
+    def "produces sensible error when bad code is supplied in component selection rule" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.2"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection ->
+                            foo()
+                        }
+                    }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failure.assertHasDescription("Execution failed for task ':resolveConf'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(19)
+        failure.assertHasCause("There was an error while evaluating a component selection rule for org.utils:api:1.2.")
+        failure.assertHasCause("Could not find method foo()")
+    }
+
+    def "produces sensible error for invalid component selection rule" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.2"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ${parameters} }
+                    }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(18)
+        failureHasCause("The closure provided is not valid as a rule for 'ComponentSelectionRules'.")
+        failureHasCause(message)
+
+        where:
+        parameters                           | message
+        "String vs ->"                       | "First parameter of rule action closure must be of type 'ComponentSelection'."
+        "ComponentSelection vs, String s ->" | "Rule may not have an input parameter of type: java.lang.String. " +
+                                               "Valid types (for the second and subsequent parameters) are: " +
+                                               "[org.gradle.api.artifacts.ComponentMetadata, org.gradle.api.artifacts.ivy.IvyModuleDescriptor]."
+    }
+
+    def "produces sensible error when closure rule throws an exception" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.2"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        withModule("org.utils:api") { ComponentSelection cs -> throw new Exception("From test") }
+                    }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failure.assertHasDescription("Execution failed for task ':resolveConf'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(18)
+        failure.assertHasCause("There was an error while evaluating a component selection rule for org.utils:api:1.2.")
+        failure.assertHasCause("From test")
+    }
+
+    def "produces sensible error for invalid module target id" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.2"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        withModule("org.utils") { ComponentSelection cs -> }
+                    }
+                }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(18)
+        failureHasCause("Could not add a component selection rule for module 'org.utils'.")
+        failureHasCause("Cannot convert the provided notation to an object of type ModuleIdentifier: org.utils")
+    }
+
+    def "produces sensible error when @Mutate method doesn't provide ComponentSelection as the first parameter" () {
+        buildFile << """
+            $baseBuildFile
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all(new BadRuleSource())
+                    }
+                }
+            }
+
+            class BadRuleSource {
+                def candidates = []
+
+                @org.gradle.model.Mutate
+                void select(String s) { }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(13)
+        failureHasCause("Type BadRuleSource is not a valid model rule source: \n- first parameter of rule method 'select' must be of type org.gradle.api.artifacts.ComponentSelection")
+    }
+
+    def "produces sensible error when rule source throws an exception" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.2"
+            }
+
+            def ruleSource = new ExceptionRuleSource()
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ruleSource
+                    }
+                }
+            }
+
+            class ExceptionRuleSource {
+                def candidates = []
+
+                @org.gradle.model.Mutate
+                void select(ComponentSelection cs) {
+                    throw new Exception("thrown from rule")
+                }
+            }
+        """
+
+        expect:
+        fails 'resolveConf'
+        failure.assertHasDescription("Execution failed for task ':resolveConf'.")
+        failure.assertHasFileName("Build file '$buildFile.path'")
+        failure.assertHasLineNumber(30)
+        failure.assertHasCause("There was an error while evaluating a component selection rule for org.utils:api:1.2.")
+        failure.assertHasCause("java.lang.Exception: thrown from rule")
+    }
+
+    def "reports missing module when component selection rule requires meta-data"() {
+        buildFile << """
+${httpBaseBuildFile}
+configurations {
+    conf {
+        resolutionStrategy.componentSelection {
+            all { ComponentSelection selection, ComponentMetadata metadata ->
+            }
+        }
+    }
+}
+dependencies {
+    conf "org.utils:api:+"
+}
+"""
+
+        when:
+        def dirList = ivyHttpRepo.directoryList("org.utils", "api")
+        def module21 = ivyHttpRepo.module("org.utils", "api", "2.1")
+        dirList.expectGet()
+        module21.ivy.expectGetMissing()
+        module21.jar.expectHeadMissing()
+
+        then:
+        fails "resolveConf"
+        failure.assertHasCause("""Could not find any matches for org.utils:api:+ as no versions of org.utils:api are available.
+Searched in the following locations:
+    ${dirList.uri}
+    ${module21.ivy.uri}
+    ${module21.jar.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        module21.ivy.expectGet()
+        module21.jar.expectGet()
+
+        then:
+        succeeds "resolveConf"
+    }
+
+    def "reports broken module when component selection rule requires meta-data"() {
+        buildFile << """
+${httpBaseBuildFile}
+configurations {
+    conf {
+        resolutionStrategy.componentSelection {
+            all { ComponentSelection selection, ComponentMetadata metadata ->
+            }
+        }
+    }
+}
+dependencies {
+    conf "org.utils:api:+"
+}
+"""
+
+        when:
+        def dirList = ivyHttpRepo.directoryList("org.utils", "api")
+        def module21 = ivyHttpRepo.module("org.utils", "api", "2.1")
+        dirList.expectGet()
+        module21.ivy.expectGetBroken()
+
+        then:
+        fails "resolveConf"
+        failure.assertHasCause("Could not resolve org.utils:api:+.")
+        failure.assertHasCause("Could not resolve org.utils:api:2.1.")
+        failure.assertHasCause("Could not GET '${module21.ivy.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module21.ivy.expectGet()
+        module21.jar.expectGetBroken()
+
+        then:
+        fails "resolveConf"
+        failure.assertHasCause("Could not download api.jar (org.utils:api:2.1)")
+        failure.assertHasCause("Could not GET '${module21.jar.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module21.jar.expectGet()
+
+        then:
+        succeeds "resolveConf"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy
new file mode 100644
index 0000000..27a510a
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy
@@ -0,0 +1,451 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class ComponentSelectionRulesProcessingIntegTest extends AbstractComponentSelectionRulesIntegrationTest {
+
+    def "rules are not fired when no candidate matches selector"() {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:3.+"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection ->
+                            candidates << selection.candidate.version
+                        }
+                    }
+                }
+            }
+
+            task checkConf << {
+                def artifacts = configurations.conf.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL)
+                assert artifacts.size() == 0
+                assert candidates.empty
+            }
+"""
+        expect:
+        succeeds 'checkConf'
+    }
+
+    def "further rules are not fired when any rule rejects candidate"() {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.+"
+            }
+
+            def extraRuleCandidates = []
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection, ComponentMetadata metadata ->
+                            extraRuleCandidates << selection.candidate.version
+                        }
+                        all ${rules['select 1.1']}
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                assert extraRuleCandidates == ['1.1']
+            }
+"""
+        expect:
+        succeeds 'resolveConf'
+    }
+
+    def "maven module is not affected by rule requiring ivy module descriptor input"() {
+        def mavenModule = mavenRepo.module("org.utils", "api", "1.1").publishWithChangedContent()
+
+        buildFile << """
+            configurations { conf }
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+                maven { url "${mavenRepo.uri}" }
+            }
+
+            dependencies {
+                conf "org.utils:api:1.1"
+            }
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection, IvyModuleDescriptor ivy ->
+                            selection.reject("rejecting all ivy modules")
+                        }
+                    }
+                }
+            }
+
+            task retrieve(type: Copy) {
+                from configurations.conf
+                into "libs"
+            }
+"""
+        when:
+        succeeds "retrieve"
+
+        then:
+        file("libs").assertHasDescendants("api-1.1.jar")
+        file("libs/api-1.1.jar").assertIsDifferentFrom(modules['1.1'].jarFile)
+        file("libs/api-1.1.jar").assertIsCopyOf(mavenModule.artifactFile)
+    }
+
+    def "maven parent pom is not affected by selection rules" () {
+        mavenRepo.module("org", "parent_dep", "1.2").publish()
+        mavenRepo.module("org", "child_dep", "1.7").publish()
+
+        def parent = mavenRepo.module("org", "parent", "1.0")
+        parent.hasPackaging('pom')
+        parent.dependsOn("org", "parent_dep", "1.2")
+        parent.publish()
+
+        def child = mavenRepo.module("org", "child", "1.0")
+        child.dependsOn("org", "child_dep", "1.7")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+            configurations { conf }
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+
+            def fired = []
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection ->
+                            logger.warn("fired for \${selection.candidate.module}")
+                            fired << "\${selection.candidate.module}"
+                        }
+
+                        withModule('org:parent') { ComponentSelection selection ->
+                            logger.warn("rejecting parent")
+                            selection.reject("Rejecting parent")
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                conf "org:child:1.0"
+            }
+
+            task resolveConf << {
+                configurations.conf.files
+                assert fired.sort() == [ 'child', 'child_dep', 'parent_dep' ]
+            }
+        """
+
+        expect:
+        succeeds "resolveConf"
+    }
+
+    def "ivy extension module is not affected by selection rules" () {
+        ivyRepo.module("org", "parent_dep", "1.2").publish()
+        ivyRepo.module("org", "child_dep", "1.7").publish()
+
+        def parent = ivyRepo.module("org", "parent", "1.0").dependsOn("org", "parent_dep", "1.2").publish()
+        def child = ivyRepo.module("org", "child", "1.0")
+        child.dependsOn("org", "child_dep", "1.7")
+        child.extendsFrom(organisation: "org", module: "parent", revision: "1.0")
+        child.publish()
+
+        buildFile << """
+            configurations { conf }
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+            }
+
+            def fired = []
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection ->
+                            logger.warn("fired for \${selection.candidate.module}")
+                            fired << "\${selection.candidate.module}"
+                        }
+
+                        withModule('org:parent') { ComponentSelection selection ->
+                            logger.warn("rejecting parent")
+                            selection.reject("Rejecting parent")
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                conf "org:child:1.0"
+            }
+
+            task resolveConf << {
+                configurations.conf.files
+                assert fired.sort() == [ 'child', 'child_dep', 'parent_dep' ]
+            }
+        """
+
+        expect:
+        succeeds "resolveConf"
+    }
+
+    def "component metadata is requested only once for rules that do require it" () {
+        buildFile << """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:2.0"
+            }
+
+            def rule1candidates = []
+            def rule2candidates = []
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection vs, IvyModuleDescriptor imd, ComponentMetadata cm ->
+                            rule1candidates << vs.candidate.version
+                        }
+                        all { ComponentSelection vs, ComponentMetadata cm ->
+                            rule2candidates << vs.candidate.version
+                        }
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                assert rule1candidates == ['2.0']
+                assert rule2candidates == ['2.0']
+            }
+        """
+
+        when:
+        modules['2.0'].ivy.expectDownload()
+        modules['2.0'].artifact.expectDownload()
+
+        then:
+        succeeds 'resolveConf'
+
+        when:
+        // Should use cache second time
+        server.resetExpectations()
+
+        then:
+        succeeds 'resolveConf'
+    }
+
+    def "changed component metadata becomes visible when module is refreshed" () {
+
+        def commonBuildFile = """
+            $httpBaseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.+"
+            }
+
+            def status11 = null
+            def branch11 = null
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all { ComponentSelection selection, IvyModuleDescriptor descriptor, ComponentMetadata metadata ->
+                            if (selection.candidate.version == '1.1') {
+                                status11 = metadata.status
+                                branch11 = descriptor.branch
+                            } else {
+                                selection.reject('not 1.1')
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        buildFile << """
+            $commonBuildFile
+
+            resolveConf.doLast {
+                assert status11 == 'milestone'
+                assert branch11 == 'test'
+            }
+        """
+
+        and:
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        modules["1.2"].ivy.expectDownload()
+        modules["1.1"].ivy.expectDownload()
+        modules["1.1"].artifact.expectDownload()
+
+        then:
+        succeeds 'resolveConf'
+
+        when:
+        modules["1.1"].withBranch('master').withStatus('release').publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+
+        then:
+        // Everything should come from cache
+        succeeds 'resolveConf'
+
+        when:
+        buildFile.text = """
+            $commonBuildFile
+
+            resolveConf.doLast {
+                assert status11 == 'release'
+                assert branch11 == 'master'
+            }
+
+            def var = "here to change length of the bytecode"
+        """
+
+        and:
+        server.resetExpectations()
+        ivyHttpRepo.directoryList("org.utils", "api").expectGet()
+        modules["1.2"].ivy.expectHead()
+        modules["1.1"].ivy.expectHead()
+        modules["1.1"].ivy.sha1.expectGet()
+        modules["1.1"].ivy.expectDownload()
+        modules["1.1"].artifact.expectMetadataRetrieve()
+        modules["1.1"].artifact.sha1.expectGet()
+        modules["1.1"].artifact.expectDownload()
+
+        then:
+        args("--refresh-dependencies")
+        succeeds 'resolveConf'
+    }
+
+    def "copies selection rules when configuration is copied" () {
+        buildFile << """
+            $baseBuildFile
+
+            configurations {
+                notCopy 
+            }
+
+            dependencies {
+                conf "org.utils:api:1.+"
+                notCopy "org.utils:api:1.+"
+            }
+
+            configurations.conf {
+                resolutionStrategy {
+                    componentSelection {
+                        all ${rules['select 1.1']}
+                    }
+                }
+            }
+            configurations.add(configurations.conf.copy())
+
+            task('checkConf') << {
+                assert configurations.conf.files*.name == ['api-1.1.jar']
+                assert configurations.confCopy.files*.name == ['api-1.1.jar']
+                assert configurations.notCopy.files*.name == ['api-1.2.jar']
+            }
+        """
+
+        expect:
+        succeeds 'checkConf'
+    }
+
+    def "can provide component selection rule as closure" () {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.+"
+            }
+
+            configurations.conf {
+                resolutionStrategy {
+                    componentSelection {
+                        all {
+                            candidates << candidate.version
+                        }
+                        all { details ->
+                            candidates << details.candidate.version
+                        }
+                        all { def details ->
+                            candidates << details.candidate.version
+                        }
+                        all { ComponentSelection details ->
+                            candidates << details.candidate.version
+                        }
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                assert candidates == ['1.2', '1.2', '1.2', '1.2']
+            }
+        """
+
+        expect:
+        succeeds 'resolveConf'
+    }
+
+    def "can provide component selection rule as rule source"() {
+        buildFile << """
+            $baseBuildFile
+
+            dependencies {
+                conf "org.utils:api:1.+"
+            }
+
+            def ruleSource = new Select11()
+
+            configurations.all {
+                resolutionStrategy {
+                    componentSelection {
+                        all ruleSource
+                    }
+                }
+            }
+
+            resolveConf.doLast {
+                def artifacts = configurations.conf.resolvedConfiguration.resolvedArtifacts
+                assert artifacts.size() == 1
+                assert artifacts[0].moduleVersion.id.version == '1.1'
+                assert ruleSource.candidates == ['1.2', '1.1']
+            }
+
+            class Select11 {
+                def candidates = []
+
+                @Mutate
+                void select(ComponentSelection selection) {
+                    if (selection.candidate.version != '1.1') {
+                        selection.reject("not 1.1")
+                    }
+                    candidates << selection.candidate.version
+                }
+            }
+        """
+
+        expect:
+        succeeds "resolveConf"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy
new file mode 100644
index 0000000..3b73e16
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenDescriptorIntegrationTest.groovy
@@ -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.integtests.resolve.ivy
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+class IvyBrokenDescriptorIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def "reports Ivy descriptor that cannot be parsed"() {
+        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.ivy.expectGet()
+
+        then:
+        fails "showBroken"
+        failure
+            .assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
+            .assertHasCause("invalid version null")
+    }
+
+    def "reports Ivy descriptor with configuration that extends unknown configuration"() {
+        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').configuration('conf', extendsFrom: ['unknown']).publish()
+
+        when:
+        module.ivy.expectGet()
+
+        then:
+        fails "showBroken"
+        failure
+            .assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
+            .assertHasCause("unknown configuration 'unknown'. It is extended by conf")
+    }
+
+    def "reports missing parent descriptor"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task showBroken << { println configurations.compile.files }
+"""
+
+        and:
+        def parent = ivyHttpRepo.module('group', 'parent', 'a')
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').extendsFrom(organisation: 'group', module: 'parent', revision: 'a').publish()
+
+        when:
+        module.ivy.expectGet()
+        parent.ivy.expectGetMissing()
+        parent.jar.expectHeadMissing()
+
+        then:
+        fails "showBroken"
+        failure
+            .assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
+            .assertHasCause("""Could not find group:parent:a.
+Searched in the following locations:
+    ${parent.ivy.uri}
+    ${parent.jar.uri}""")
+    }
+
+    def "reports parent descriptor that cannot be parsed"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task showBroken << { println configurations.compile.files }
+"""
+
+        and:
+        def parent = ivyHttpRepo.module('group', 'parent', 'a').publish()
+        parent.ivyFile.text = '<ivy-module/>'
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').extendsFrom(organisation: 'group', module: 'parent', revision: 'a').publish()
+
+        when:
+        module.ivy.expectGet()
+        parent.ivy.expectGet()
+
+        then:
+        fails "showBroken"
+        failure
+            .assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse Ivy file ${module.ivy.uri}")
+            .assertHasCause("Could not parse Ivy file ${parent.ivy.uri}")
+            .assertHasCause("invalid version null")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
new file mode 100644
index 0000000..9bb89f8
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+
+class IvyBrokenRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    public void "reports and recovers from missing module"() {
+        given:
+        def repo = ivyHttpRepo("repo1")
+        def module = repo.module("group", "projectA", "1.2").publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${repo.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        module.ivy.expectGetMissing()
+        module.jar.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertResolutionFailure(':missing')
+                .assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${module.jar.uri}
+Required by:
+""")
+
+        when:
+        module.ivy.expectGetMissing()
+        module.jar.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertResolutionFailure(':missing')
+                .assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${module.jar.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds('showMissing')
+    }
+
+    public void "reports and recovers from multiple missing modules"() {
+        given:
+        def repo = ivyHttpRepo("repo1")
+        def moduleA = repo.module("group", "projectA", "1.2").publish()
+        def moduleB = repo.module("group", "projectB", "1.0-milestone-9").publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${repo.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+    missing 'group:projectB:1.0-milestone-9'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        moduleA.ivy.expectGetMissing()
+        moduleA.jar.expectHeadMissing()
+        moduleB.ivy.expectGetMissing()
+        moduleB.jar.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertResolutionFailure(':missing')
+                .assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${moduleA.ivy.uri}
+    ${moduleA.jar.uri}
+Required by:
+""")
+                .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
+Searched in the following locations:
+    ${moduleB.ivy.uri}
+    ${moduleB.jar.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        moduleA.ivy.expectGet()
+        moduleA.jar.expectGet()
+        moduleB.ivy.expectGet()
+        moduleB.jar.expectGet()
+
+        then:
+        succeeds('showMissing')
+    }
+
+    public void "reports and recovers from missing module when dependency declaration references an artifact"() {
+        given:
+        def repo = ivyHttpRepo("repo1")
+        def module = repo.module("group", "projectA", "1.2").artifact(classifier: 'thing').publish()
+
+        buildFile << """
+repositories {
+    ivy { url "${repo.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2:thing'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        module.ivy.expectGetMissing()
+        def artifact = module.getArtifact(classifier: 'thing')
+        artifact.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertHasCause('Could not resolve all dependencies for configuration \':missing\'.')
+                .assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectGet()
+        artifact.expectGet()
+
+        then:
+        succeeds('showMissing')
+    }
+
+    public void "reports and recovers from module missing from multiple repositories"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+        def moduleInRepo1 = repo1.module("group", "projectA", "1.2").publish()
+        def moduleInRepo2 = repo2.module("group", "projectA", "1.2")
+
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}"}
+    ivy { url "${repo2.uri}"}
+}
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        when:
+        moduleInRepo1.ivy.expectGetMissing()
+        moduleInRepo1.jar.expectHeadMissing()
+        moduleInRepo2.ivy.expectGetMissing()
+        moduleInRepo2.jar.expectHeadMissing()
+
+        then:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertHasCause('Could not resolve all dependencies for configuration \':missing\'.')
+                .assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${moduleInRepo1.ivy.uri}
+    ${moduleInRepo1.jar.uri}
+    ${moduleInRepo2.ivy.uri}
+    ${moduleInRepo2.jar.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        moduleInRepo1.ivy.expectGet()
+        moduleInRepo1.jar.expectGet()
+
+        then:
+        succeeds('showMissing')
+    }
+
+    public void "reports and recovers from missing module when no repositories defined"() {
+        given:
+        buildFile << """
+configurations { missing }
+dependencies {
+    missing 'group:projectA:1.2'
+}
+task showMissing << { println configurations.missing.files }
+"""
+
+        expect:
+        fails("showMissing")
+        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
+                .assertResolutionFailure(':missing')
+                .assertHasCause("Cannot resolve external dependency group:projectA:1.2 because no repositories are defined.")
+
+        when:
+        def module = ivyHttpRepo.module("group", "projectA", "1.2").publish()
+
+        and:
+        buildFile << "repositories { ivy { url '${ivyHttpRepo.uri}' } }"
+
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds('showMissing')
+    }
+
+    public void "reports and recovers from failed Ivy descriptor download"() {
+        given:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.3').publish()
+
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { broken }
+dependencies {
+    broken 'group:projectA:1.3'
+}
+task showBroken << { println configurations.broken.files }
+"""
+
+        when:
+        module.ivy.expectGetBroken()
+        fails("showBroken")
+
+        then:
+        failure
+                .assertHasDescription('Execution failed for task \':showBroken\'.')
+                .assertResolutionFailure(':broken')
+                .assertHasCause('Could not resolve group:projectA:1.3.')
+                .assertHasCause("Could not GET '${module.ivy.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        then:
+        succeeds("showBroken")
+    }
+
+    public void "reports and caches missing artifact"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        module.ivy.expectGet()
+        module.jar.expectGetMissing()
+
+        then:
+        fails "retrieve"
+
+        failure.assertHasCause("""Could not find projectA.jar (group:projectA:1.2).
+Searched in the following locations:
+    ${module.jar.uri}""")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails "retrieve"
+
+        failure.assertHasCause("""Could not find projectA.jar (group:projectA:1.2).
+Searched in the following locations:
+    ${module.jar.uri}""")
+    }
+
+    public void "reports and recovers from failed artifact download"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        module.ivy.expectGet()
+        module.jar.expectGetBroken()
+
+        then:
+        fails "retrieve"
+        failure.assertHasCause("Could not download projectA.jar (group:projectA:1.2)")
+        failure.assertHasCause("Could not GET '${module.jar.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module.jar.expectGet()
+
+        then:
+        succeeds "retrieve"
+        file('libs').assertHasDescendants('projectA-1.2.jar')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
new file mode 100644
index 0000000..6778837
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy
@@ -0,0 +1,406 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+
+class IvyChangingModuleRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    def "detects changed module descriptor when flagged as changing"() {
+        given:
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.1", changing: true
+}
+
+task retrieve(type: Copy) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        when: "Version 1.1 is published"
+        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        and: "Server handles requests"
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        and: "We request 1.1 (changing)"
+        run 'retrieve'
+
+        then: "Version 1.1 jar is downloaded"
+        file('build').assertHasDescendants('projectA-1.1.jar')
+
+        when: "Module meta-data is changed (new artifact)"
+        module.artifact([name: 'other'])
+        module.dependsOn("group", "projectB", "2.0")
+        module.publish()
+        def moduleB = ivyHttpRepo.module("group", "projectB", "2.0").publish()
+
+        and: "Server handles requests"
+        server.resetExpectations()
+        // Server will be hit to get updated versions
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.getArtifact(name: 'other').expectGet()
+        moduleB.ivy.expectGet()
+        moduleB.jar.expectGet()
+
+        and: "We request 1.1 again"
+        run 'retrieve'
+
+        then: "We get all artifacts, including the new ones"
+        file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar', 'projectB-2.0.jar')
+    }
+
+    def "can mark a module as changing after first retrieval"() {
+        given:
+        buildFile << """
+def isChanging = project.hasProperty('isChanging') ? true : false
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.1", changing: isChanging
+}
+
+task retrieve(type: Copy) {
+    into 'build'
+    from configurations.compile
+}
+"""
+        and:
+        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+        module.allowAll()
+
+        when: 'original retrieve'
+        run 'retrieve'
+
+        then:
+        def jarSnapshot = file('build/projectA-1.1.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        module.publishWithChangedContent()
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+
+        and:
+        executer.withArguments('-PisChanging')
+        run 'retrieve'
+
+        then:
+        file('build/projectA-1.1.jar').assertHasChangedSince(jarSnapshot)
+    }
+
+    def "detects changed artifact when flagged as changing"() {
+        given:
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.1", changing: true
+}
+
+task retrieve(type: Copy) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        and:
+        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        when:
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        run 'retrieve'
+
+        then:
+        def jarFile = file('build/projectA-1.1.jar')
+        jarFile.assertIsCopyOf(module.jarFile)
+        def snapshot = jarFile.snapshot()
+
+        when:
+        module.publishWithChangedContent()
+
+        server.resetExpectations()
+        // Server will be hit to get updated versions
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+
+        run 'retrieve'
+
+        then:
+        def changedJarFile = file('build/projectA-1.1.jar')
+        changedJarFile.assertHasChangedSince(snapshot)
+        changedJarFile.assertIsCopyOf(module.jarFile)
+    }
+
+    def "caches changing module descriptor and artifacts until cache expiry"() {
+        given:
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+
+if (project.hasProperty('doNotCacheChangingModules')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.1", changing: true
+}
+
+task retrieve(type: Copy) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        when: "Version 1.1 is published"
+        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        and: "Server handles requests"
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        and: "We request 1.1 (changing)"
+        run 'retrieve'
+
+        then: "Version 1.1 jar is downloaded"
+        file('build').assertHasDescendants('projectA-1.1.jar')
+        def jarFile = file('build/projectA-1.1.jar')
+        jarFile.assertIsCopyOf(module.jarFile)
+        def snapshot = jarFile.snapshot()
+
+        when: "Module meta-data is changed and artifacts are modified"
+        server.resetExpectations()
+        module.artifact([name: 'other'])
+        module.publishWithChangedContent()
+
+        and: "We request 1.1 (changing), with module meta-data cached. No server requests."
+        run 'retrieve'
+
+        then: "Original module meta-data and artifacts are used"
+        file('build').assertHasDescendants('projectA-1.1.jar')
+        jarFile.assertHasNotChangedSince(snapshot)
+
+        when: "Server handles requests"
+        server.resetExpectations()
+        // Server will be hit to get updated versions
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+        module.getArtifact(name: 'other').expectGet()
+
+        and: "We request 1.1 (changing) again, with zero expiry for dynamic revision cache"
+        executer.withArguments("-PdoNotCacheChangingModules")
+        run 'retrieve'
+
+        then: "We get new artifacts based on the new meta-data"
+        file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar')
+        jarFile.assertHasChangedSince(snapshot)
+        jarFile.assertIsCopyOf(module.jarFile)
+    }
+
+    def "can use cache-control DSL to mimic changing pattern for ivy repository"() {
+        given:
+        buildFile << """
+repositories {
+    ivy { url "${ivyHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+import static java.util.concurrent.TimeUnit.SECONDS
+configurations.all {
+    resolutionStrategy.resolutionRules.with {
+        eachModule({ moduleResolve ->
+            if (moduleResolve.request.version.endsWith('-CHANGING')) {
+                moduleResolve.cacheFor(0, SECONDS)
+            }
+        } as Action)
+
+        eachArtifact({ artifactResolve ->
+            if (artifactResolve.request.moduleVersionIdentifier.version.endsWith('-CHANGING')) {
+                artifactResolve.cacheFor(0, SECONDS)
+            }
+        } as Action)
+    }
+}
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1-CHANGING"
+}
+
+task retrieve(type: Copy) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        when: "Version 1-CHANGING is published"
+        def module = ivyHttpRepo.module("group", "projectA", "1-CHANGING").publish()
+
+        and: "Server handles requests"
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        and: "We request 1-CHANGING"
+        run 'retrieve'
+
+        then: "Version 1-CHANGING jar is used"
+        file('build').assertHasDescendants('projectA-1-CHANGING.jar')
+        def jarFile = file('build/projectA-1-CHANGING.jar')
+        jarFile.assertIsCopyOf(module.jarFile)
+        def snapshot = jarFile.snapshot()
+
+        when: "Module meta-data is changed and artifacts are modified"
+        module.artifact([name: 'other'])
+        module.publishWithChangedContent()
+
+        and: "Server handles requests"
+        server.resetExpectations()
+        // Server will be hit to get updated versions
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.jar.sha1.expectGet()
+        module.jar.expectGet()
+        module.getArtifact(name: 'other').expectGet()
+
+        and: "We request 1-CHANGING again"
+        executer.withArguments()
+        run 'retrieve'
+
+        then: "We get new artifacts based on the new meta-data"
+        file('build').assertHasDescendants('projectA-1-CHANGING.jar', 'other-1-CHANGING.jar')
+        jarFile.assertHasChangedSince(snapshot)
+        jarFile.assertIsCopyOf(module.jarFile)
+    }
+
+    def "avoid redownload unchanged artifact when no checksum available"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy { url "${ivyHttpRepo.uri}" }
+            }
+
+            configurations { compile }
+
+            configurations.all {
+                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+            }
+
+            dependencies {
+                compile group: "group", name: "projectA", version: "1.1", changing: true
+            }
+
+            task retrieve(type: Copy) {
+                into 'build'
+                from configurations.compile
+            }
+        """
+
+        and:
+        def module = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        when:
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        def downloadedJar = file('build/projectA-1.1.jar')
+        downloadedJar.assertIsCopyOf(module.jarFile)
+        def snapshot = downloadedJar.snapshot()
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.jar.expectHead()
+
+        and:
+        run 'retrieve'
+
+        then:
+        downloadedJar.assertHasNotChangedSince(snapshot)
+
+        when:
+        // Do change the jar, so we can check that the new version wasn't downloaded
+        module.publishWithChangedContent()
+
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGetMissing()
+        module.ivy.expectGet()
+        module.jar.expectHead()
+        module.jar.sha1.expectGetMissing()
+        module.jar.expectGet()
+
+        run 'retrieve'
+
+        then:
+        downloadedJar.assertHasChangedSince(snapshot)
+        downloadedJar.assertIsCopyOf(module.jarFile)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy
new file mode 100644
index 0000000..d3ff7b1
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesChangingModulesIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesChangingModulesIntegrationTest
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+
+class IvyComponentMetadataRulesChangingModulesIntegrationTest extends ComponentMetadataRulesChangingModulesIntegrationTest {
+    IvyHttpRepository getRepo() {
+        ivyHttpRepo
+    }
+
+    String getRepoDeclaration() {
+"""
+repositories {
+    ivy {
+        url "$repo.uri"
+    }
+}
+"""
+    }
+
+    def setup() {
+        repo.directoryList("org.test", "moduleA").allowGet()
+    }
+}
+
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..d5c9e24
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,315 @@
+/*
+ * 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.api.internal.artifacts.ivyservice.NamespaceId
+import org.gradle.integtests.resolve.ComponentMetadataRulesIntegrationTest
+import org.gradle.test.fixtures.encoding.Identifier
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+import spock.lang.Unroll
+
+class IvyComponentMetadataRulesIntegrationTest extends ComponentMetadataRulesIntegrationTest {
+    @Override
+    IvyHttpRepository getRepo() {
+        ivyHttpRepo
+    }
+
+    @Override
+    String getRepoDeclaration() {
+"""
+repositories {
+    ivy {
+        url "$ivyHttpRepo.uri"
+    }
+}
+"""
+    }
+
+    @Override
+    String getDefaultStatus() {
+        "integration"
+    }
+
+    def "can access Ivy metadata by accepting parameter of type IvyModuleDescriptor"() {
+        def module = repo.module('org.test', 'projectA', '1.0')
+                .withExtraInfo((ns('foo')): "fooValue", (ns('bar')): "barValue")
+                .withBranch('someBranch')
+                .withStatus('release')
+                .publish()
+        module.ivy.expectDownload()
+        module.artifact.expectDownload()
+
+        buildFile <<
+"""
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            assert descriptor.extraInfo.asMap() == [${declareNS('foo')}: "fooValue", ${declareNS('bar')}: "barValue"]
+            assert descriptor.extraInfo.get('foo') == 'fooValue'
+            assert descriptor.extraInfo.get('${ns('foo').namespace}', 'foo') == 'fooValue'
+            assert descriptor.branch == 'someBranch'
+            assert descriptor.ivyStatus == 'release'
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        expect:
+        succeeds 'resolve'
+        // also works when already cached
+        succeeds 'resolve'
+    }
+
+    def "produces sensible error when accessing non-unique extra info element name" () {
+        def module = repo.module('org.test', 'projectA', '1.0')
+                .withExtraInfo((ns('foo')): "fooValue", (new NamespaceId('http://some.other.ns', 'foo')): "barValue")
+                .publish()
+        module.ivy.expectDownload()
+
+        buildFile <<
+                """
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            descriptor.extraInfo.get('foo')
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        when:
+        fails 'resolve'
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':resolve'.")
+        failure.assertHasLineNumber(33)
+        failure.assertHasCause("Could not resolve all dependencies for configuration ':compile'.")
+        failure.assertHasCause("Cannot get extra info element named 'foo' by name since elements with this name were found from multiple namespaces (http://my.extra.info/foo, http://some.other.ns).  Use get(String namespace, String name) instead.")
+    }
+
+    @Unroll
+    def "can access Ivy metadata with #identifier characters" () {
+        def branch = identifier.safeForBranch().decorate("branch")
+        def status = identifier.safeForFileName().decorate("status")
+        def module = repo.module('org.test', 'projectA', '1.0')
+                .withBranch(branch)
+                .withStatus(status)
+                .publish()
+        module.ivy.expectDownload()
+        module.artifact.expectDownload()
+
+        buildFile <<
+                """
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            assert descriptor.branch == '${sq(branch)}'
+            details.statusScheme = [ '${sq(status)}' ]
+            assert descriptor.ivyStatus == '${sq(status)}'
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        expect:
+        succeeds 'resolve'
+
+        where:
+        identifier << Identifier.all
+    }
+
+    def "rule that doesn't initially access Ivy metadata can be changed to get access at any time"() {
+        def module = repo.module('org.test', 'projectA', '1.0')
+                .withExtraInfo((ns('foo')): "fooValue", (ns('bar')): "barValue")
+                .withBranch("someBranch")
+                .withStatus("release")
+                .publish()
+        module.ivy.expectDownload()
+        module.artifact.expectDownload()
+
+        def baseScript = buildFile.text
+
+        when:
+        buildFile.text = baseScript +
+"""
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            ruleInvoked = true
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        then:
+        succeeds 'resolve'
+
+        when:
+        buildFile.text = baseScript +
+"""
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            assert descriptor.extraInfo.asMap() == [${declareNS('foo')}: "fooValue", ${declareNS('bar')}: "barValue"]
+            assert descriptor.branch == 'someBranch'
+            assert descriptor.ivyStatus == 'release'
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        then:
+        succeeds 'resolve'
+    }
+
+    def "changed Ivy metadata becomes visible once module is refreshed"() {
+        def baseScript = buildFile.text
+
+        when:
+        def module = repo.module('org.test', 'projectA', '1.0')
+                .withExtraInfo((ns('foo')): "fooValue", (ns('bar')): "barValue")
+                .withBranch('someBranch')
+                .withStatus('release')
+                .publish()
+        module.ivy.expectDownload()
+        module.artifact.expectDownload()
+
+        buildFile.text = baseScript +
+                """
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            assert descriptor.extraInfo.asMap() == [${declareNS('foo')}: "fooValue", ${declareNS('bar')}: "barValue"]
+            assert descriptor.branch == 'someBranch'
+            assert descriptor.ivyStatus == 'release'
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        then:
+        succeeds 'resolve'
+
+        when:
+        repo.module('org.test', 'projectA', '1.0')
+                .withExtraInfo((ns('foo')): "fooValueChanged", (ns('bar')): "barValueChanged")
+                .withBranch('differentBranch')
+                .withStatus('milestone')
+                .publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+
+        then:
+        succeeds 'resolve'
+
+        when:
+        args("--refresh-dependencies")
+        buildFile.text = baseScript +
+"""
+def ruleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ruleInvoked = true
+            file("metadata").delete()
+            file("metadata") << descriptor.extraInfo.asMap().toString()
+            file("metadata") << "\\n"
+            file("metadata") << descriptor.branch
+            file("metadata") << "\\n"
+            file("metadata") << descriptor.ivyStatus
+        }
+    }
+}
+
+resolve.doLast { assert ruleInvoked }
+"""
+
+        and:
+        server.resetExpectations()
+        module.ivy.expectMetadataRetrieve()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectDownload()
+        module.artifact.expectMetadataRetrieve()
+        module.artifact.sha1.expectGet()
+        module.artifact.expectDownload()
+
+        then:
+        succeeds 'resolve'
+        assert file("metadata").text == "{{http://my.extra.info/bar}bar=barValueChanged, {http://my.extra.info/foo}foo=fooValueChanged}\ndifferentBranch\nmilestone"
+    }
+
+    def "produces sensible error when @Mutate method does not have ComponentMetadata as first parameter" () {
+        buildFile << """
+            dependencies {
+                components {
+                    all(new BadRuleSource())
+                }
+            }
+
+            class BadRuleSource {
+                @org.gradle.model.Mutate
+                void doSomething(String s) { }
+            }
+        """
+
+        when:
+        fails "resolve"
+
+        then:
+        fails 'resolveConf'
+        failureDescriptionStartsWith("A problem occurred evaluating root project")
+        failure.assertHasCause("Type BadRuleSource is not a valid model rule source: \n- first parameter of rule method 'doSomething' must be of type org.gradle.api.artifacts.ComponentMetadataDetails")
+    }
+
+    def ns(String name) {
+        return new NamespaceId("http://my.extra.info/${name}", name)
+    }
+
+    def declareNS(String name) {
+        return "(new javax.xml.namespace.QName('http://my.extra.info/${name}', '${name}'))"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy
new file mode 100644
index 0000000..668ecf5
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyComponentMetadataRulesStatusIntegrationTest.groovy
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesStatusIntegrationTest
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+
+class IvyComponentMetadataRulesStatusIntegrationTest extends ComponentMetadataRulesStatusIntegrationTest {
+    @Override
+    IvyHttpRepository getRepo() {
+        ivyHttpRepo
+    }
+
+    @Override
+    String getRepoDeclaration() {
+"""
+repositories {
+    ivy {
+        url "$ivyHttpRepo.uri"
+    }
+}
+"""
+    }
+
+    def setup() {
+        repo.module('org.test', 'projectA', '1.0').withStatus("silver").publish().allowAll()
+    }
+
+    def "module with custom status can be resolved by adapting status scheme"() {
+        buildFile <<
+                """
+dependencies {
+    components {
+        all { details ->
+            assert details.status == "silver"
+            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"() {
+        expect:
+        fails 'resolve'
+        failure.assertHasCause(/Unexpected status 'silver' specified for org.test:projectA:1.0. Expected one of: [integration, milestone, release]/)
+    }
+
+    def "resolve fails if status doesn't match custom status scheme"() {
+        buildFile <<
+                """
+dependencies {
+    components {
+        all { details ->
+            details.statusScheme = ["gold", "bronze"]
+        }
+    }
+}
+"""
+
+        expect:
+        fails 'resolve'
+        failure.assertHasCause(/Unexpected status 'silver' specified for org.test:projectA:1.0. Expected one of: [gold, bronze]/)
+    }
+
+    def "rule can change status"() {
+        buildFile <<
+                """
+dependencies {
+    components {
+        all { details ->
+            details.status = "milestone"
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy
new file mode 100644
index 0000000..9b1b19b
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyCustomStatusLatestVersionIntegrationTest.groovy
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Issue
+
+class IvyCustomStatusLatestVersionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    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 {
+        all { ComponentMetadataDetails 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"
+    }
+
+    def "uses status provided by component metadata rule for latest.xyz"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'org.test:projectA:latest.release'
+    components {
+        all { ComponentMetadataDetails details ->
+            if (details.id.version == project.properties['releaseVersion']) {
+                details.status = 'release'
+            }
+        }
+    }
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        ivyHttpRepo.directoryList('org.test', 'projectA').allowGet()
+        ivyHttpRepo.module('org.test', 'projectA', '1.0').withStatus("release").publish().allowAll()
+        ivyHttpRepo.module('org.test', 'projectA', '1.1').withStatus("integration").publish().allowAll()
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.0.jar")
+
+        when:
+        executer.withArgument("-PreleaseVersion=1.1")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.1.jar")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3216")
+    def "uses changing provided by component metadata rule for latest.xyz"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'org.test:projectA:latest.release'
+    components {
+        all { ComponentMetadataDetails details ->
+            if (details.status == 'snapshot') {
+                details.changing = true
+            }
+
+            details.statusScheme = ['snapshot', 'release']
+        }
+    }
+}
+
+configurations.all {
+    resolutionStrategy {
+        cacheChangingModulesFor 0, 'seconds'
+    }
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+
+        and:
+        def directoryList = ivyHttpRepo.directoryList('org.test', 'projectA')
+        ivyHttpRepo.module('org.test', 'projectA', '1.1').withStatus("snapshot").publish()
+        def project2 = ivyHttpRepo.module('org.test', 'projectA', '1.2').withStatus("release").publish()
+        def project3 = ivyHttpRepo.module('org.test', 'projectA', '1.3').withStatus("snapshot").publish()
+
+        and:
+        directoryList.allowGet()
+        project3.ivy.expectGet()
+        project2.ivy.expectGet()
+        project2.jar.expectGet()
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.2.jar")
+
+        when:
+        server.resetExpectations()
+        directoryList.allowGet()
+        project3.ivy.expectHead()
+        project2.ivy.expectHead()
+        project2.jar.expectHead()
+
+        and:
+        executer.withArgument("--refresh-dependencies")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.2.jar")
+
+        when:
+        server.resetExpectations()
+        directoryList.allowGet()
+        project3.ivy.expectHead()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.2.jar")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3216")
+    def "handles changing module with latest.release"() {
+        given:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile group: "org.test", name: "projectA", version: "latest.release", changing: true
+}
+
+configurations.all {
+    resolutionStrategy {
+        cacheChangingModulesFor 0, 'seconds'
+    }
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        ivyHttpRepo.directoryList('org.test', 'projectA').expectGet()
+        def module = ivyHttpRepo.module('org.test', 'projectA', '1.2').withStatus("release").publish()
+        module.ivy.expectGet()
+        module.jar.expectGet()
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.2.jar")
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.jar.expectHead()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.2.jar")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorDependencyExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorDependencyExcludeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..5964a5d
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorDependencyExcludeResolveIntegrationTest.groovy
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.test.fixtures.ivy.IvyModule
+import spock.lang.Issue
+import spock.lang.Unroll
+
+/**
+ * Demonstrates the use of Ivy dependency excludes.
+ *
+ * @see <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile/artifact-exclude.html">Ivy reference documentation</a>
+ */
+class IvyDescriptorDependencyExcludeResolveIntegrationTest extends AbstractIvyDescriptorExcludeResolveIntegrationTest {
+    /**
+     * Dependency exclude for a single artifact by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     */
+    @Unroll
+    def "dependency exclude having single artifact with #name"() {
+        given:
+        ivyRepo.module('b').publish()
+        ivyRepo.module('c').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeAttributes | resolvedJars
+        'non-matching module'   | [module: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'non-matching artifact' | [name: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching module'       | [module: 'b']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar'] // Module exclude does not apply to declaring module
+        'matching artifact'     | [name: 'b']       | ['a-1.0.jar', 'c-1.0.jar'] // Artifact exclude does apply to declaring module
+    }
+
+    /**
+     * Exclude of transitive dependency with a single artifact by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> e
+     *
+     * Exclude is applied to dependency a->b
+     */
+    @Unroll
+    def "transitive dependency exclude having single artifact with #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeAttributes | resolvedJars
+        'non-matching module'   | [module: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+        'non-matching artifact' | [name: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+        'matching all modules'  | [module: '*']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching module'       | [module: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching artifact'     | [name: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+    }
+
+    /**
+     * Exclude of transitive dependency with a single artifact does not exclude its transitive module by using a combination of name exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d -> f
+     * c -> e
+     */
+    @Unroll
+    def "transitive dependency exclude having single artifact with #name does not exclude its transitive module"() {
+        given:
+        ivyRepo.module('f').publish()
+        ivyRepo.module('d').dependsOn('f').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeAttributes | resolvedJars
+        'non-matching artifact' | [name: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'matching artifact'     | [name: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+    }
+
+    /**
+     * Exclude of transitive dependency with multiple artifacts by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> e
+     */
+    @Unroll
+    def "transitive dependency exclude having multiple artifacts with #name"() {
+        given:
+        ivyRepo.module('d')
+                .artifact([:])
+                .artifact([type: 'sources', classifier: 'sources', ext: 'jar'])
+                .artifact([type: 'javadoc', classifier: 'javadoc', ext: 'jar'])
+                .publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                     | excludeAttributes        | resolvedJars
+        'non-matching module'    | [module: 'other']        | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+        'non-matching artifact'  | [name: 'other']          | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+        'matching all modules'   | [module: '*']            | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching module'        | [module: 'd']            | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching artifact'      | [name: 'd']              | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching name and type' | [name: 'd', type: 'jar'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+    }
+
+    /**
+     * Transitive dependency exclude for a module reachable via alternative path using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module excluding #name is not excluded if reachable via path that does not exclude it"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('c').dependsOn('d').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeAttributes | resolvedJars
+        'non-matching module'   | [module: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'non-matching artifact' | [name: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching all modules'  | [module: '*']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching module'       | [module: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching artifact'     | [name: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+    }
+
+    /**
+     * Transitive dependency exclude for a module reachable via alternative path using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    def "artifact is not excluded if reachable via path that does not exclude it"() {
+        given:
+        ivyRepo.module('d').artifact([type: 'war']).artifact([type: 'ear']) publish()
+        def moduleB = ivyRepo.module('b').dependsOn('d')
+        addExcludeRuleToModuleDependency(moduleB, 'd', [type: 'war'])
+        moduleB.publish()
+        def moduleC = ivyRepo.module('c').dependsOn('d')
+        addExcludeRuleToModuleDependency(moduleC, 'd', [type: 'ear'])
+        moduleC.publish()
+        def moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.ear', 'd-1.0.war'])
+    }
+
+    /**
+     * Transitive dependency exclude for module reachable by multiple paths for all paths by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module reachable by multiple paths excluded for all paths with #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('c').dependsOn('d').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        addExcludeRuleToModuleDependency(moduleA, 'c', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeAttributes | resolvedJars
+        'non-matching module'   | [module: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'non-matching artifact' | [name: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching module'       | [module: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching artifact'     | [name: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+    }
+
+    /**
+     * Transitive dependency exclude for module reachable by multiple paths for all paths by intersection of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module reachable by multiple paths excluded for all paths with intersection of #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('c').dependsOn('d').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+
+        excludeRulesPath1.each { excludeAttributes ->
+            addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        }
+
+        excludeRulesPath2.each { excludeAttributes ->
+            addExcludeRuleToModuleDependency(moduleA, 'c', excludeAttributes)
+        }
+
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeRulesPath1                                                              | excludeRulesPath2                       | resolvedJars
+        'non-matching module'   | [[org: 'org.company', module: 'd'], [org: 'org.gradle.test', module: 'e']]     | [[org: 'org.company', module: 'd']]     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'non-matching artifact' | [[org: 'org.company', name: 'd'], [org: 'org.gradle.test', name: 'e']]         | [[org: 'org.company', name: 'd']]       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching module'       | [[org: 'org.gradle.test', module: 'd'], [org: 'org.gradle.test', module: 'e']] | [[org: 'org.gradle.test', module: 'd']] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching artifact'     | [[org: 'org.gradle.test', name: 'd'], [org: 'org.gradle.test', name: 'e']]     | [[org: 'org.gradle.test', name: 'd']]   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+    }
+
+    /**
+     * Exclude of transitive dependency for union of multiple rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d -> f
+     * c -> e
+     */
+    @Unroll
+    def "transitive dependency exclude for union of multiple rules with #name"() {
+        given:
+        ivyRepo.module('f').publish()
+        ivyRepo.module('d').dependsOn('f').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+
+        excludeRules.each { excludeAttributes ->
+            addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        }
+
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name               | excludeRules                                      | resolvedJars
+        'no match'         | [[name: 'other'], [name: 'some'], [name: 'more']] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'all matches'      | [[name: 'b'], [name: 'd'], [name: 'f']]           | ['a-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'partial match'    | [[name: 'other'], [name: 'd'], [name: 'more']]    | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'duplicated match' | [[name: 'f'], [name: 'some'], [name: 'f']]        | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+    }
+
+    /**
+     * Exclude of transitive dependency without provided group or module attribute does not exclude its transitive module by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d -> f
+     * c -> e
+     */
+    @Issue("https://issues.gradle.org/browse/GRADLE-2674")
+    @Unroll
+    def "transitive dependency exclude without provided group or module attribute but matching #name does not exclude its transitive module"() {
+        given:
+        ivyRepo.module('f')
+                .artifact([:])
+                .artifact([type: 'war'])
+                .publish()
+        ivyRepo.module('d')
+                .artifact([:])
+                .artifact([type: 'war'])
+                .artifact([type: 'ear'])
+                .dependsOn('f')
+                .publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModuleDependency(moduleA, 'b', excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                            | excludeAttributes              | resolvedJars
+        "type 'war'"                    | [type: 'war']                  | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0.ear', 'e-1.0.jar', 'f-1.0.jar']
+        "ext 'war'"                     | [ext: 'war']                   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0.ear', 'e-1.0.jar', 'f-1.0.jar']
+        "type 'war' and conf 'default'" | [type: 'war', conf: 'default'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0.ear', 'e-1.0.jar', 'f-1.0.jar']
+        "ext 'jar'"                     | [ext: 'jar']                   | ['a-1.0.jar', 'c-1.0.jar', 'd-1.0.war', 'd-1.0.ear', 'e-1.0.jar', 'f-1.0.war']
+    }
+
+    private void addExcludeRuleToModuleDependency(IvyModule module, String dependencyName, Map<String, String> excludeAttributes) {
+        module.withXml {
+            Node moduleDependency = asNode().dependencies[0].dependency.find { it. at name == dependencyName }
+            assert moduleDependency, "Failed to find module dependency with name '$dependencyName'"
+            moduleDependency.appendNode(EXCLUDE_ATTRIBUTE, excludeAttributes)
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorModuleExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorModuleExcludeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..3aa0bd0
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorModuleExcludeResolveIntegrationTest.groovy
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.test.fixtures.ivy.IvyModule
+import spock.lang.Issue
+import spock.lang.Unroll
+
+/**
+ * Demonstrates the use of Ivy module excludes.
+ *
+ * @see <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile/exclude.html">Ivy reference documentation</a>
+ */
+ at Issue("https://issues.gradle.org/browse/GRADLE-3147")
+class IvyDescriptorModuleExcludeResolveIntegrationTest extends AbstractIvyDescriptorExcludeResolveIntegrationTest {
+    /**
+     * Module exclude for dependencies having single artifact by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     */
+    @Unroll
+    def "module exclude having single artifact with #name"() {
+        given:
+        ivyRepo.module('b').publish()
+        ivyRepo.module('c').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModule(moduleA, excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                     | excludeAttributes   | resolvedJars
+        'non-matching module'    | [module: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'non-matching artifact'  | [artifact: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching all modules'   | [module: '*']       | ['a-1.0.jar']
+        'matching module'        | [module: 'b']       | ['a-1.0.jar', 'c-1.0.jar']
+        'matching all artifacts' | [artifact: '*']     | ['a-1.0.jar']
+        'matching artifact'      | [artifact: 'b']     | ['a-1.0.jar', 'c-1.0.jar']
+    }
+
+    /**
+     * Module exclude of transitive dependency with a single artifact by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> e
+     */
+    @Unroll
+    def "module exclude for transitive dependency having single artifact with #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModule(moduleA, excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                     | excludeAttributes   | resolvedJars
+        'non-matching module'    | [module: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+        'non-matching artifact'  | [artifact: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+        'matching all modules'   | [module: '*']       | ['a-1.0.jar']
+        'matching module'        | [module: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching all artifacts' | [artifact: '*']     | ['a-1.0.jar']
+        'matching artifact'      | [artifact: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+    }
+
+    /**
+     * Module exclude of transitive dependency with a single artifact does not exclude its transitive module by using a combination of artifact exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d -> f
+     * c -> e
+     */
+    @Unroll
+    def "module exclude for transitive dependency having single artifact with #name does not exclude its transitive module"() {
+        given:
+        ivyRepo.module('f').publish()
+        ivyRepo.module('d').dependsOn('f').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModule(moduleA, excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                     | excludeAttributes   | resolvedJars
+        'non-matching artifact'  | [artifact: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'matching all artifacts' | [artifact: '*']     | ['a-1.0.jar']
+        'matching artifact'      | [artifact: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+    }
+
+    /**
+     * Module exclude of transitive dependency with multiple artifacts by using a combination of artifact exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> e
+     */
+    @Unroll
+    def "module exclude for transitive dependency having multiple artifacts with #name"() {
+        given:
+        ivyRepo.module('d')
+                .artifact([:])
+                .artifact([type: 'sources', classifier: 'sources', ext: 'jar'])
+                .artifact([type: 'javadoc', classifier: 'javadoc', ext: 'jar'])
+                .publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModule(moduleA, excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                         | excludeAttributes            | resolvedJars
+        'non-matching module'        | [module: 'other']            | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+        'non-matching artifact'      | [artifact: 'other']          | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+        'matching all modules'       | [module: '*']                | ['a-1.0.jar']
+        'matching module'            | [module: 'd']                | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching all artifacts'     | [artifact: '*']              | ['a-1.0.jar']
+        'matching artifact'          | [artifact: 'd']              | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'matching artifact and type' | [artifact: 'd', type: 'jar'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0-javadoc.jar', 'd-1.0-sources.jar', 'e-1.0.jar']
+    }
+
+    /**
+     * Transitive module exclude for a module reachable via alternative path using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module with #name is not excluded if reachable via alternate path"() {
+        given:
+        ivyRepo.module('d').publish()
+        IvyModule moduleB = ivyRepo.module('b').dependsOn('d')
+        addExcludeRuleToModule(moduleB, excludeAttributes)
+        moduleB.publish()
+        ivyRepo.module('c').dependsOn('d').publish()
+        ivyRepo.module('a').dependsOn('b').dependsOn('c').publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar'])
+
+        where:
+        name                     | excludeAttributes
+        'non-matching module'    | [module: 'other']
+        'non-matching artifact'  | [artifact: 'other']
+        'matching all modules'   | [module: '*']
+        'matching module'        | [module: 'd']
+        'matching all artifacts' | [artifact: '*']
+        'matching artifact'      | [artifact: 'd']
+    }
+
+    /**
+     * Transitive module exclude for module reachable by multiple paths for all paths by using a combination of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module reachable by multiple paths excluded for all paths with #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('c').dependsOn('d').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+        addExcludeRuleToModule(moduleA, excludeAttributes)
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                     | excludeAttributes   | resolvedJars
+        'non-matching module'    | [module: 'other']   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'non-matching artifact'  | [artifact: 'other'] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching all modules'   | [module: '*']       | ['a-1.0.jar']
+        'matching all artifacts' | [artifact: '*']     | ['a-1.0.jar']
+        'matching module'        | [module: 'd']       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching artifact'      | [artifact: 'd']     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+    }
+
+    /**
+     * Transitive module exclude for module reachable by multiple paths for all paths intersection of exclude rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d
+     * c -> d
+     */
+    @Unroll
+    def "module reachable by multiple paths excluded for all paths with intersection of #name"() {
+        given:
+        ivyRepo.module('d').publish()
+        IvyModule moduleB = ivyRepo.module('b').dependsOn('d')
+        IvyModule moduleC = ivyRepo.module('c').dependsOn('d')
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+
+        excludeRulesPath1.each { excludeAttributes ->
+            addExcludeRuleToModule(moduleB, excludeAttributes)
+        }
+
+        excludeRulesPath2.each { excludeAttributes ->
+            addExcludeRuleToModule(moduleC, excludeAttributes)
+        }
+
+        moduleB.publish()
+        moduleC.publish()
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name                    | excludeRulesPath1                                                                  | excludeRulesPath2                         | resolvedJars
+        'non-matching module'   | [[org: 'org.company', module: 'd'], [org: 'org.gradle.test', module: 'e']]         | [[org: 'org.company', module: 'd']]       | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'non-matching artifact' | [[org: 'org.company', artifact: 'd'], [org: 'org.gradle.test', artifact: 'e']]     | [[org: 'org.company', artifact: 'd']]     | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar']
+        'matching module'       | [[org: 'org.gradle.test', module: 'd'], [org: 'org.gradle.test', module: 'e']]     | [[org: 'org.gradle.test', module: 'd']]   | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+        'matching artifact'     | [[org: 'org.gradle.test', artifact: 'd'], [org: 'org.gradle.test', artifact: 'e']] | [[org: 'org.gradle.test', artifact: 'd']] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar']
+    }
+
+    /**
+     * Module exclude of transitive dependency for union of multiple rules.
+     *
+     * Dependency graph:
+     * a -> b, c
+     * b -> d -> f
+     * c -> e
+     */
+    @Unroll
+    def "transitive module exclude for union of multiple rules with #name"() {
+        given:
+        ivyRepo.module('f').publish()
+        ivyRepo.module('d').dependsOn('f').publish()
+        ivyRepo.module('b').dependsOn('d').publish()
+        ivyRepo.module('e').publish()
+        ivyRepo.module('c').dependsOn('e').publish()
+        IvyModule moduleA = ivyRepo.module('a').dependsOn('b').dependsOn('c')
+
+        excludeRules.each { excludeAttributes ->
+            addExcludeRuleToModule(moduleA, excludeAttributes)
+        }
+
+        moduleA.publish()
+
+        when:
+        succeedsDependencyResolution()
+
+        then:
+        assertResolvedFiles(resolvedJars)
+
+        where:
+        name               | excludeRules                                                  | resolvedJars
+        'no match'         | [[artifact: 'other'], [artifact: 'some'], [artifact: 'more']] | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'all matches'      | [[artifact: 'b'], [artifact: 'd'], [artifact: 'f']]           | ['a-1.0.jar', 'c-1.0.jar', 'e-1.0.jar']
+        'partial match'    | [[artifact: 'other'], [artifact: 'd'], [artifact: 'more']]    | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'e-1.0.jar', 'f-1.0.jar']
+        'duplicated match' | [[artifact: 'f'], [artifact: 'some'], [artifact: 'f']]        | ['a-1.0.jar', 'b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'e-1.0.jar']
+    }
+
+    private void addExcludeRuleToModule(IvyModule module, Map<String, String> excludeAttributes) {
+        module.withXml {
+            asNode().dependencies[0].appendNode(EXCLUDE_ATTRIBUTE, excludeAttributes)
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
new file mode 100644
index 0000000..d2bae08
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorResolveIntegrationTest.groovy
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Unroll
+
+class IvyDescriptorResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def "substitutes system properties into ivy descriptor"() {
+        given:
+        ivyRepo.module("org.gradle", "test", "1.45")
+                .dependsOn('org.gradle.${sys_prop}', 'module_${sys_prop}', 'v_${sys_prop}')
+                .publish()
+
+        ivyRepo.module("org.gradle.111", "module_111", "v_111").publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each {
+        it.children.each { transitive ->
+            assert transitive.moduleGroup == "org.gradle.111"
+            assert transitive.moduleName == "module_111"
+            assert transitive.moduleVersion == "v_111"
+        }
+    }
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'module_111-v_111.jar']
+}
+"""
+
+        when:
+        executer.withArgument("-Dsys_prop=111")
+
+        then:
+        succeeds "check"
+    }
+
+    def "merges values from parent descriptor file that is available locally"() {
+        given:
+        def parentModule = ivyHttpRepo.module("org.gradle.parent", "parent_module", "1.1").dependsOn("org.gradle.dep", "dep_module", "1.1").publish()
+        def depModule = ivyHttpRepo.module("org.gradle.dep", "dep_module", "1.1").publish()
+
+        def module = ivyHttpRepo.module("org.gradle", "test", "1.45")
+        module.extendsFrom(organisation: "org.gradle.parent", module: "parent_module", revision: "1.1", location: parentModule.ivyFile.toURI().toURL())
+        parentModule.publish()
+        module.publish()
+
+        when:
+        buildFile << """
+repositories { ivy { url "${ivyHttpRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'dep_module-1.1.jar']
+}
+"""
+
+        and:
+        module.ivy.expectGet()
+        depModule.ivy.expectGet()
+        module.jar.expectGet()
+        depModule.jar.expectGet()
+
+        then:
+        succeeds "check"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds "check"
+    }
+
+    def "merges values from parent descriptor file"() {
+        given:
+        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 = ivyHttpRepo.module("org.gradle", "test", "1.45")
+        final extendAttributes = [organisation: "org.gradle.parent", module: "parent_module", revision: "1.1"]
+        module.extendsFrom(extendAttributes)
+        parentModule.publish()
+        module.publish()
+
+        when:
+        buildFile << """
+repositories { ivy { url "${ivyHttpRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'dep_module-1.1.jar']
+}
+"""
+
+        and:
+        module.ivy.expectGet()
+        parentModule.ivy.expectGet()
+        depModule.ivy.expectGet()
+        module.jar.expectGet()
+        depModule.jar.expectGet()
+
+        then:
+        succeeds "check"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds "check"
+    }
+
+    @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'] + transitiveJars
+        file("libs").assertHasDescendants(jars.toArray(new String[0]))
+
+        where:
+        name                       | excludeAttributes                          | transitiveJars
+        "empty exclude"            | [:]                                        | ['dep_module-1.134.jar'] // Does not exclude the depended-on module itself
+        "unmatched exclude"        | [module: "different"]                      | ['dep_module-1.134.jar', '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"]                        | ['dep_module-1.134.jar', 'mod_two-2.2.jar']
+        "org exclude"              | [org: "org.gradle.two"]                    | ['dep_module-1.134.jar', 'mod_one-1.1.jar', 'mod_one-1.1.war']
+        "module and org exclude"   | [org: "org.gradle.two", module: "mod_one"] | ['dep_module-1.134.jar', 'mod_one-1.1.jar', 'mod_one-1.1.war', 'mod_two-2.2.jar']
+        "regex module exclude"     | [module: "mod.*"]                          | ['dep_module-1.134.jar']
+        "matching config exclude"  | [module: "mod_one", conf: "default,other"] | ['dep_module-1.134.jar', 'mod_two-2.2.jar']
+        "unmatched config exclude" | [module: "mod_one", conf: "other"]         | ['dep_module-1.134.jar', 'mod_one-1.1.jar', 'mod_one-1.1.war', 'mod_one-2.1.jar', 'mod_two-2.2.jar']
+        "type exclude"             | [type: "war"]                              | ['dep_module-1.134.jar', 'mod_one-1.1.jar', 'mod_one-2.1.jar', 'mod_two-2.2.jar']
+        "extension exclude"        | [ext: "jar"]                               | ['mod_one-1.1.war']
+        "name exclude"             | [name: "dep_module-*"]                     | ['mod_one-1.1.jar', 'mod_one-1.1.war', '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/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorValidationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorValidationIntegrationTest.groovy
new file mode 100644
index 0000000..b39afe4
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDescriptorValidationIntegrationTest.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 IvyDescriptorValidationIntegrationTest extends AbstractDependencyResolutionTest {
+    def "incorrect value for revision attribute is flagged"() {
+        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/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
new file mode 100644
index 0000000..86ce267
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
@@ -0,0 +1,1233 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.ResolveTestFixture
+import org.gradle.test.fixtures.Repository
+import org.gradle.test.fixtures.encoding.Identifier
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class IvyDynamicRevisionRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    ResolveTestFixture resolve
+
+    def setup() {
+        settingsFile << "rootProject.name = 'test' "
+
+        resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+    }
+
+    @Issue("GRADLE-3264")
+    def "resolves latest.milestone from when same dependency has a range constraint transitively"() {
+        given:
+        useRepository ivyHttpRepo
+
+        buildFile << """
+configurations { compile }
+
+dependencies {
+    compile group: "group", name: "projectA", version: "1.+"
+}
+"""
+        when:
+        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.0.0").
+                dependsOn("group", "projectB", "[1.0,1.2)").
+                dependsOn("group", "projectC", "1.0.0").
+                publish()
+        def projectB11 = ivyHttpRepo.module("group", "projectB", "1.1").withStatus("milestone").publish()
+        def projectB12 = ivyHttpRepo.module("group", "projectB", "1.2").withStatus("milestone").publish()
+
+        def projectC1 = ivyHttpRepo.module("group", "projectC", "1.0.0").dependsOn("group", "projectB", "latest.milestone").publish()
+
+        and:
+        expectGetDynamicRevision(projectA1)
+        expectGetDynamicRevision(projectB12)
+        projectB11.ivy.expectGet()
+        projectC1.ivy.expectGet()
+        projectC1.jar.expectGet()
+
+        then:
+        assert succeeds('checkDeps')
+    }
+
+    def "uses latest version from version range and latest status"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+if (project.hasProperty('refreshDynamicVersions')) {
+    configurations.all {
+        resolutionStrategy.cacheDynamicVersionsFor 0, "seconds"
+    }
+}
+dependencies {
+    compile group: "group", name: "projectA", version: "1.+"
+    compile group: "group", name: "projectB", version: "latest.integration"
+}
+"""
+        when:
+        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+        ivyHttpRepo.module("group", "projectA", "2.0").publish()
+        def projectB1 = ivyHttpRepo.module("group", "projectB", "1.1").publish()
+
+        and:
+        expectGetDynamicRevision(projectA1)
+        expectGetDynamicRevision(projectB1)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.1",
+                     "group:projectB:latest.integration": "group:projectB:1.1"
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
+        def projectB2 = ivyHttpRepo.module("group", "projectB", "2.2").publish()
+
+        and:
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA2)
+        expectGetDynamicRevision(projectB2)
+
+        then:
+        executer.withArgument("-PrefreshDynamicVersions")
+        checkResolve "group:projectA:1.+": "group:projectA:1.2", "group:projectB:latest.integration": "group:projectB:2.2"
+    }
+
+    @Unroll
+    def "uses latest version from version range with #identifier characters"() {
+        given:
+        def name = identifier.safeForFileName().decorate("name")
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+configurations.all {
+    resolutionStrategy.cacheDynamicVersionsFor 0, "seconds"
+}
+
+dependencies {
+    compile group: /${name}/, name: /${name}/, version: "latest.integration"
+}
+"""
+        when:
+        def projectA1 = ivyHttpRepo.module(name, name, name).publish()
+
+        and:
+        expectGetDynamicRevision(projectA1)
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                dependency(group: name, module: name, version: 'latest.integration').selects(group: name, module: name, version: name)
+            }
+        }
+
+        where:
+        identifier << Identifier.all
+    }
+
+    def "determines latest version with jar only"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+  compile group: "group", name: "projectA", version: "1.+"
+  compile group: "group", name: "projectB", version: "latest.integration"
+}
+"""
+
+        when:
+        ivyHttpRepo.module("group", "projectA", "1.1").withNoMetaData().publish()
+        def projectA12 = ivyHttpRepo.module("group", "projectA", "1.2").withNoMetaData().publish()
+        ivyHttpRepo.module("group", "projectA", "2.0").withNoMetaData().publish()
+        ivyHttpRepo.module("group", "projectB", "1.1").withNoMetaData().publish()
+        def projectB12 = ivyHttpRepo.module("group", "projectB", "1.2").withNoMetaData().publish()
+
+        and:
+        ivyHttpRepo.directoryList("group", "projectA").expectGet()
+        projectA12.ivy.expectGetMissing()
+        projectA12.jar.expectHead()
+        projectA12.jar.expectGet()
+        ivyHttpRepo.directoryList("group", "projectB").expectGet()
+        projectB12.ivy.expectGetMissing()
+        projectB12.jar.expectHead()
+        projectB12.jar.expectGet()
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2",
+                     "group:projectB:latest.integration": "group:projectB:1.2"
+
+        when: "result is cached"
+        server.resetExpectations()
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2",
+                     "group:projectB:latest.integration": "group:projectB:1.2"
+    }
+
+    def "uses latest version with correct status for latest.release and latest.milestone"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+def latestRevision = project.getProperty('latestRevision')
+configurations { compile }
+
+dependencies {
+    compile group: "group", name: "projectA", version: "latest.\${latestRevision}"
+}
+"""
+
+        when:
+        ivyHttpRepo.module("group", "projectA", "1.0").withStatus('release').publish()
+        ivyHttpRepo.module('group', 'projectA', '1.1').withStatus('milestone').publish()
+        ivyHttpRepo.module('group', 'projectA', '1.2').withStatus('integration').publish()
+        def release = ivyHttpRepo.module("group", "projectA", "2.0").withStatus('release').publish()
+        def milestone = ivyHttpRepo.module('group', 'projectA', '2.1').withStatus('milestone').publish()
+        def integration = ivyHttpRepo.module('group', 'projectA', '2.2').withStatus('integration').publish()
+
+        and:
+        ivyHttpRepo.directoryList("group", "projectA").expectGet()
+        integration.ivy.expectGet()
+        milestone.ivy.expectGet()
+        release.ivy.expectGet()
+        release.jar.expectGet()
+
+        and:
+        executer.withArgument('-PlatestRevision=release')
+
+        then:
+        checkResolve "group:projectA:latest.release": "group:projectA:2.0"
+
+        when:
+        server.resetExpectations()
+        milestone.jar.expectGet()
+        executer.withArgument('-PlatestRevision=milestone')
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:2.1"
+
+        when:
+        server.resetExpectations()
+        executer.withArgument('-PlatestRevision=milestone')
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:2.1"
+    }
+
+    def "reuses cached meta-data when resolving latest.status"() {
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+
+        given:
+        useRepository repo1, repo2
+        buildFile << """
+configurations {
+    staticVersions {
+        // Force load the metadata
+        resolutionStrategy.componentSelection.all { ComponentSelection s, ComponentMetadata d -> if (d.status != 'release') { s.reject('nope') } }
+    }
+    compile
+}
+dependencies {
+    staticVersions group: "group", name: "projectA", version: "1.1"
+    compile group: "group", name: "projectA", version: "latest.milestone"
+}
+task cache << { configurations.staticVersions.files }
+"""
+
+        and:
+        def repo1ProjectA1 = repo1.module("group", "projectA", "1.1").withStatus("milestone").publish()
+        def repo2ProjectA1 = repo2.module("group", "projectA", "1.1").withStatus("release").publishWithChangedContent()
+        def repo2ProjectA2 = repo2.module("group", "projectA", "1.2").publish()
+        repo1ProjectA1.ivy.expectGet()
+        repo2ProjectA1.ivy.expectHead()
+        repo2ProjectA1.ivy.sha1.expectGet()
+        repo2ProjectA1.ivy.expectGet()
+        repo2ProjectA1.jar.expectGet()
+        succeeds "cache"
+
+        when:
+        repo1.directoryList("group", "projectA").expectGet()
+        repo2.directoryList("group", "projectA").expectGet()
+        repo2ProjectA2.ivy.expectGet()
+        repo1ProjectA1.jar.expectHead()
+        repo1ProjectA1.jar.sha1.expectGet()
+        repo1ProjectA1.jar.expectGet()
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
+    }
+
+    def "can use latest version from different remote repositories"() {
+        def repo1 = ivyHttpRepo("ivy1")
+        def repo2 = ivyHttpRepo("ivy2")
+
+        given:
+        useRepository repo1, repo2
+        buildFile << """
+    configurations { compile }
+    dependencies {
+        compile group: "group", name: "projectA", version: "latest.milestone"
+    }
+    """
+
+        when:
+        def version11 = repo1.module('group', 'projectA', '1.1').withStatus('milestone').publish()
+        def version12 = repo2.module('group', 'projectA', '1.2').withStatus('integration').publish()
+
+        and:
+        expectGetDynamicRevision(version11)
+
+        repo2.directoryList("group", "projectA").expectGet()
+        version12.ivy.expectGet()
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        checkResolve "group:projectA:latest.milestone": "group:projectA:1.1"
+    }
+
+    def "checks new repositories before returning any cached value"() {
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+
+        given:
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}" }
+}
+
+if (project.hasProperty('addRepo2')) {
+    repositories {
+        ivy { url "${repo2.uri}" }
+    }
+}
+
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "1.+"
+}
+"""
+
+        when:
+        def projectA11 = repo1.module("group", "projectA", "1.1").publish()
+        def projectA12 = repo2.module("group", "projectA", "1.2").publish()
+
+        and:
+        expectGetDynamicRevision(projectA11)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.1"
+
+        when:
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA12)
+
+        then:
+        executer.withArguments("-PaddRepo2")
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        executer.withArguments("-PaddRepo2")
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+    }
+
+    def "recovers from broken directory listing in subsequent resolution"() {
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+
+        given:
+        useRepository repo1, repo2
+        buildFile << """
+    configurations { compile }
+    dependencies {
+        compile group: "group", name: "projectA", version: "1.+"
+    }
+    """
+
+        when:
+        def projectA12 = repo1.module("group", "projectA", "1.2").publish()
+        def projectA11 = repo2.module("group", "projectA", "1.1").publish()
+
+        and: "projectA is broken in repo1"
+        repo1.directoryList("group", "projectA").expectGetBroken()
+        expectGetDynamicRevision(projectA11)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.1"
+
+        when:
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA12)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+    }
+
+    def "uses and caches latest of versions obtained from multiple HTTP repositories"() {
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+        def repo3 = ivyHttpRepo("repo3")
+
+        given:
+        useRepository repo1, repo2, repo3
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "1.+"
+}
+"""
+
+        when:
+        def projectA11 = repo1.module("group", "projectA", "1.1").publish()
+        def projectA12 = repo3.module("group", "projectA", "1.2").publish()
+
+        and:
+        repo1.directoryList("group", "projectA").expectGet()
+        // TODO Should not need to get this
+        projectA11.ivy.expectGet()
+        repo2.directoryList("group", "projectA").expectGet()
+        expectGetDynamicRevision(projectA12)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+
+        when:
+        server.resetExpectations()
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+    }
+
+    def "reuses cached artifacts that match multiple dynamic versions"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "org.test", name: "projectA", version: "1.+"
+    compile group: "org.test", name: "projectA", version: "latest.integration"
+}
+"""
+
+        when:
+        ivyHttpRepo.module("org.test", "projectA", "1.1").publish()
+        def projectA12 = ivyHttpRepo.module("org.test", "projectA", "1.2").publish()
+
+        and:
+        expectGetDynamicRevision(projectA12)
+
+        then:
+        succeeds "checkDeps"
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge "org.test:projectA:1.+", "org.test:projectA:1.2"
+                edge "org.test:projectA:latest.integration", "org.test:projectA:1.2"
+            }
+        }
+
+        when:
+        server.resetExpectations()
+
+        and:
+        buildFile << """
+dependencies {
+    compile group: "org.test", name: "projectA", version: "[1.0,2.0)"
+}
+"""
+
+        then:
+        succeeds "checkDeps"
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge "org.test:projectA:1.+", "org.test:projectA:1.2"
+                edge "org.test:projectA:latest.integration", "org.test:projectA:1.2"
+                edge "org.test:projectA:[1.0,2.0)", "org.test:projectA:1.2"
+            }
+        }
+    }
+
+    def "reuses cached version lists unless no matches"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "org.test", name: "projectA", version: "1.+"
+}
+"""
+
+        when:
+        ivyHttpRepo.module("org.test", "projectA", "1.1").publish()
+        def projectA21 = ivyHttpRepo.module("org.test", "projectA", "2.1").publish()
+        def projectA12 = ivyHttpRepo.module("org.test", "projectA", "1.2").publish()
+
+        and:
+        ivyHttpRepo.directoryList("org.test", "projectA").expectGet()
+        projectA12.ivy.expectGet()
+        projectA12.jar.expectGet()
+
+        then:
+        succeeds "checkDeps"
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge "org.test:projectA:1.+", "org.test:projectA:1.2"
+            }
+        }
+
+        when:
+        server.resetExpectations()
+        projectA21.ivy.expectGet()
+        projectA21.jar.expectGet()
+
+        and:
+        buildFile << """
+dependencies {
+    compile group: "org.test", name: "projectA", version: "2.+"
+}
+"""
+
+        then:
+        succeeds "checkDeps"
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.+", "org.test:projectA:2.1").byConflictResolution()
+                edge("org.test:projectA:2.+", "org.test:projectA:2.1").byConflictResolution()
+            }
+        }
+
+        when:
+        def projectA30 = ivyHttpRepo.module("org.test", "projectA", "3.0").publish()
+        server.resetExpectations()
+        ivyHttpRepo.directoryList("org.test", "projectA").expectGet()
+        projectA30.ivy.expectGet()
+        projectA30.jar.expectGet()
+
+        and:
+        buildFile << """
+dependencies {
+    compile group: "org.test", name: "projectA", version: "3.+"
+}
+"""
+
+        then:
+        succeeds "checkDeps"
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.+", "org.test:projectA:3.0").byConflictResolution()
+                edge("org.test:projectA:2.+", "org.test:projectA:3.0").byConflictResolution()
+                edge("org.test:projectA:3.+", "org.test:projectA:3.0").byConflictResolution()
+            }
+        }
+    }
+
+    def "caches resolved revisions until cache expiry"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "1.+"
+}
+if (project.hasProperty('noDynamicRevisionCache')) {
+    configurations.all {
+        resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
+    }
+}
+"""
+
+        when: "Version 1.1 is published"
+        def version1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+
+        and:
+        expectGetDynamicRevision(version1)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.1"
+
+        when: "Version 1.2 is published"
+        server.resetExpectations()
+        def version2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
+
+        then: "Version 1.1 is still used, as the 1.+ -> 1.1 mapping is cached"
+        checkResolve "group:projectA:1.+": "group:projectA:1.1"
+
+        when: "zero expiry for dynamic revision cache"
+        executer.withArguments("-PnoDynamicRevisionCache")
+
+        and:
+        expectGetDynamicRevision(version2)
+
+        then: "Version 1.2 is used"
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+    }
+
+    def "uses and caches dynamic revisions for transitive dependencies"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "main", version: "1.0"
+}
+
+if (project.hasProperty('noDynamicRevisionCache')) {
+    configurations.all {
+        resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
+    }
+}
+"""
+
+        when:
+        def mainProject = ivyHttpRepo.module("group", "main", "1.0")
+        mainProject.dependsOn("group", "projectA", "1.+")
+        mainProject.dependsOn("group", "projectB", "latest.integration")
+        mainProject.publish()
+
+        and:
+        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+        def projectB1 = ivyHttpRepo.module("group", "projectB", "1.1").publish()
+
+        and:
+        mainProject.ivy.expectGet()
+        mainProject.jar.expectGet()
+        expectGetDynamicRevision(projectA1)
+        expectGetDynamicRevision(projectB1)
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("group:main:1.0") {
+                    edge("group:projectA:1.+", "group:projectA:1.1")
+                    edge("group:projectB:latest.integration", "group:projectB:1.1")
+                }
+            }
+        }
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
+        def projectB2 = ivyHttpRepo.module("group", "projectB", "2.2").publish()
+
+        and:
+        server.resetExpectations()
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("group:main:1.0") {
+                    edge("group:projectA:1.+", "group:projectA:1.1")
+                    edge("group:projectB:latest.integration", "group:projectB:1.1")
+                }
+            }
+        }
+
+        when: "Server handles requests"
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA2)
+        expectGetDynamicRevision(projectB2)
+
+        and: "DynamicRevisionCache is bypassed"
+        executer.withArguments("-PnoDynamicRevisionCache")
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("group:main:1.0") {
+                    edge("group:projectA:1.+", "group:projectA:1.2")
+                    edge("group:projectB:latest.integration", "group:projectB:2.2")
+                }
+            }
+        }
+    }
+
+    public void "resolves dynamic version with 2 repositories where first repo results in 404 for directory listing"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def repo2 = ivyHttpRepo("repo2")
+        def moduleA = repo2.module('group', 'projectA').publish()
+
+        and:
+        useRepository repo1, repo2
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.+'
+}
+"""
+
+        when:
+        repo1.directoryList("group", "projectA").expectGetMissing()
+        expectGetDynamicRevision(moduleA)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.0"
+
+        when:
+        server.resetExpectations()
+        // No extra calls for cached dependencies
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.0"
+    }
+
+    def "reuses cached artifacts across repository types"() {
+        def ivyRepo = ivyHttpRepo('repo1')
+        def mavenRepo = mavenHttpRepo('repo2')
+        def ivyModule = ivyRepo.module("org.test", "a", "1.1").publish()
+        def mavenModule = mavenRepo.module("org.test", "a", "1.1").publish()
+        assert ivyModule.jarFile.bytes == mavenModule.artifactFile.bytes
+
+        given:
+        useRepository ivyRepo
+        buildFile << """
+configurations { compile }
+
+dependencies {
+    compile 'org.test:a:1+'
+}
+"""
+
+        when:
+        expectGetDynamicRevision(ivyModule)
+
+        then:
+        checkResolve "org.test:a:1+": "org.test:a:1.1"
+
+        when:
+        buildFile.text = """
+repositories {
+    maven { url '${mavenRepo.uri}' }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org.test:a:[1.0,2.0)'
+}
+"""
+        resolve.prepare()
+
+        and:
+        mavenRepo.getModuleMetaData("org.test", "a").expectGet()
+        mavenModule.pom.expectGet()
+        mavenModule.artifact.expectHead()
+        mavenModule.artifact.sha1.expectGet()
+
+        then:
+        checkResolve "org.test:a:[1.0,2.0)": "org.test:a:1.1"
+    }
+
+    def "can resolve dynamic versions from repository with multiple ivy patterns"() {
+        given:
+        def repo1versions = [:]
+        def repo1 = ivyHttpRepo("ivyRepo1")
+        def repo2versions = [:]
+        def repo2 = ivyHttpRepo("ivyRepo2")
+        repo1versions.A1 = repo1.module('org.test', 'projectA', '1.1').publish()
+        repo1versions.A2 = repo1.module('org.test', 'projectA', '1.2').publish()
+        repo1versions.A3 = repo1.module('org.test', 'projectA', '1.3') // unpublished
+
+        repo2versions.A1 = repo2.module('org.test', 'projectA', '1.1').publish()
+        repo2versions.A3 = repo2.module('org.test', 'projectA', '1.3').publish()
+
+        repo1versions.B1 = repo1.module('org.test', 'projectB', '1.1').withStatus("integration").publish()
+        repo1versions.B2 = repo1.module('org.test', 'projectB', '1.2').withStatus("milestone").publish()
+        repo1versions.B3 = repo1.module('org.test', 'projectB', '1.3') // unpublished
+
+        repo2versions.B1 = repo2.module('org.test', 'projectB', '1.1').withStatus("milestone").publish()
+        repo2versions.B3 = repo2.module('org.test', 'projectB', '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 { compile }
+dependencies {
+  compile 'org.test:projectA:1.+'
+  compile 'org.test:projectB:latest.milestone'
+}
+"""
+
+        when:
+        repo1.directoryList("org.test", "projectA").expectGet()
+        // TODO Should not be looking in repo1, since A3 was not included in the version listing
+        repo1versions.A3.ivy.expectGetMissing()
+        repo1versions.A3.jar.expectGetMissing()
+        expectGetDynamicRevision(repo2versions.A3)
+
+        and:
+        // TODO Should not be looking in repo1, since B3 was not included in the version listing
+        repo1versions.B3.ivy.expectGetMissing()
+        repo2.directoryList("org.test", "projectB").expectGet()
+        repo2versions.B3.ivy.expectGet()
+        expectGetDynamicRevision(repo1versions.B2)
+
+        then:
+        checkResolve "org.test:projectA:1.+": "org.test:projectA:1.3",
+                     "org.test:projectB:latest.milestone": "org.test:projectB:1.2"
+
+        when: "resolve a second time"
+        server.resetExpectations()
+
+        then:
+        checkResolve "org.test:projectA:1.+": "org.test:projectA:1.3",
+                     "org.test:projectB:latest.milestone": "org.test:projectB:1.2"
+
+    }
+
+    def "versions are listed once only per resolve"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "main", version: "1.0"
+    compile group: "group", name: "projectA", version: "latest.integration"
+}
+configurations.all {
+    resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
+}
+"""
+
+        when:
+        def projectA0 = ivyHttpRepo.module("group", "projectA", "1.0").publish()
+        def projectA1 = ivyHttpRepo.module("group", "projectA", "1.1").publish()
+        def mainProject = ivyHttpRepo.module("group", "main", "1.0")
+        mainProject.dependsOn("group", "projectA", "1.+")
+        mainProject.publish()
+
+        and:
+        mainProject.ivy.expectGet()
+        mainProject.jar.expectGet()
+        ivyHttpRepo.directoryList("group", "projectA").expectGet()
+        projectA1.ivy.expectGet()
+        projectA1.jar.expectGet()
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("group:main:1.0") {
+                    edge("group:projectA:1.+", "group:projectA:1.1")
+                }
+                edge("group:projectA:latest.integration", "group:projectA:1.1")
+            }
+        }
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "1.2").publish()
+
+        and:
+        server.resetExpectations()
+        ivyHttpRepo.directoryList("group", "projectA").expectGet()
+        projectA2.ivy.expectGet()
+        projectA2.jar.expectGet()
+
+        then:
+        succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("group:main:1.0") {
+                    edge("group:projectA:1.+", "group:projectA:1.2")
+                }
+                edge("group:projectA:latest.integration", "group:projectA:1.2")
+            }
+        }
+    }
+
+    def "reports and recovers from no matching version for dynamic version"() {
+        def repo2 = ivyHttpRepo("repo-2")
+
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "2.+"
+}
+"""
+
+        when:
+        ivyHttpRepo.module("group", "projectA", "1.1").publish()
+        ivyHttpRepo.module("group", "projectA", "1.2").publish()
+        ivyHttpRepo.module("group", "projectA", "3.0").publish()
+        def dirListRepo1 = ivyHttpRepo.directoryList("group", "projectA")
+        dirListRepo1.expectGet()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any version that matches group:projectA:2.+.
+Versions that do not match:
+    3.0
+    1.2
+    1.1
+Searched in the following locations:
+    ${dirListRepo1.uri}
+Required by:
+""")
+
+        when:
+        useRepository repo2
+        repo2.module("group", "projectA", "3.0").publish()
+        repo2.module("group", "projectA", "4.4").publish()
+        def dirListRepo2 = repo2.directoryList("group", "projectA")
+
+        and:
+        server.resetExpectations()
+        dirListRepo1.expectGet()
+        dirListRepo2.expectGet()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any version that matches group:projectA:2.+.
+Versions that do not match:
+    3.0
+    1.2
+    1.1
+    4.4
+Searched in the following locations:
+    ${dirListRepo1.uri}
+    ${dirListRepo2.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        dirListRepo1.expectGet()
+        dirListRepo2.expectGet()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any version that matches group:projectA:2.+.
+Versions that do not match:
+    3.0
+    1.2
+    1.1
+    4.4
+Searched in the following locations:
+    ${dirListRepo1.uri}
+    ${dirListRepo2.uri}
+Required by:
+""")
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "2.2").publish()
+
+        and:
+        server.resetExpectations()
+        dirListRepo1.allowGet()
+        dirListRepo2.allowGet()
+        projectA2.ivy.expectGet()
+        projectA2.jar.expectGet()
+
+        then:
+        checkResolve "group:projectA:2.+": "group:projectA:2.2"
+    }
+
+    def "reports and recovers from missing directory available for dynamic version"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "2.+"
+}
+"""
+
+        when: "no versions"
+        def directoryList = ivyHttpRepo.directoryList("group", "projectA")
+        directoryList.expectGetMissing()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any matches for group:projectA:2.+ as no versions of group:projectA are available.
+Searched in the following locations:
+    ${directoryList.uri}
+Required by:
+""")
+
+        when: "no versions"
+        server.resetExpectations()
+        directoryList.expectGetMissing()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any matches for group:projectA:2.+ as no versions of group:projectA are available.
+Searched in the following locations:
+    ${directoryList.uri}
+Required by:
+""")
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "2.2").publish()
+
+        and:
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA2)
+
+        then:
+        checkResolve "group:projectA:2.+": "group:projectA:2.2"
+    }
+
+    def "reports and recovers from missing dynamic version when no repositories defined"() {
+        given:
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "2.+"
+}
+"""
+
+        expect:
+        fails "checkDeps"
+        failure.assertHasCause("Cannot resolve external dependency group:projectA:2.+ because no repositories are defined.")
+
+        when:
+        useRepository ivyHttpRepo
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "2.2").publish()
+        expectGetDynamicRevision(projectA2)
+
+        then:
+        checkResolve "group:projectA:2.+": "group:projectA:2.2"
+    }
+
+    def "reports and recovers from broken directory available for dynamic version"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "2.+"
+}
+"""
+
+        when: "no version > 2"
+        def directoryList = ivyHttpRepo.directoryList("group", "projectA")
+        directoryList.expectGetBroken()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("Could not resolve group:projectA:2.+")
+        failure.assertHasCause("Could not list versions using Ivy pattern '${ivyHttpRepo.ivyPattern}'.")
+        failure.assertHasCause("Could not GET '${directoryList.uri}'. Received status code 500 from server")
+
+        when:
+        def projectA2 = ivyHttpRepo.module("group", "projectA", "2.2").publish()
+
+        and:
+        server.resetExpectations()
+        expectGetDynamicRevision(projectA2)
+
+        then:
+        checkResolve "group:projectA:2.+": "group:projectA:2.2"
+    }
+
+    def "reports and recovers from missing module for dynamic version that requires meta-data"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "latest.release"
+}
+"""
+
+        when:
+        def directoryList = ivyHttpRepo.directoryList("group", "projectA")
+        def projectA = ivyHttpRepo.module("group", "projectA", "1.2").withStatus("release").publish()
+        directoryList.expectGet()
+        projectA.ivy.expectGetMissing()
+        projectA.jar.expectHeadMissing()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("""Could not find any matches for group:projectA:latest.release as no versions of group:projectA are available.
+Searched in the following locations:
+    ${directoryList.uri}
+    ${projectA.ivy.uri}
+    ${projectA.jar.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        projectA.ivy.expectGet()
+        projectA.jar.expectGet()
+
+        then:
+        checkResolve "group:projectA:latest.release": "group:projectA:1.2"
+    }
+
+    def "reports and recovers from broken module for dynamic version that requires meta-data"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile group: "group", name: "projectA", version: "latest.release"
+}
+"""
+
+        when:
+        def directoryList = ivyHttpRepo.directoryList("group", "projectA")
+        def projectA = ivyHttpRepo.module("group", "projectA", "1.2").withStatus("release").publish()
+        directoryList.expectGet()
+        projectA.ivy.expectGetBroken()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("Could not resolve group:projectA:latest.release")
+        failure.assertHasCause("Could not GET '${projectA.ivy.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        projectA.ivy.expectGet()
+        projectA.jar.expectGetBroken()
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause("Could not download projectA.jar (group:projectA:1.2)")
+        failure.assertHasCause("Could not GET '${projectA.jar.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        projectA.jar.expectGet()
+
+        then:
+        checkResolve "group:projectA:latest.release": "group:projectA:1.2"
+    }
+
+    @Unroll
+    def "finds best matching version in local and remote repository with #order"() {
+        given:
+        def fileRepo = ivyRepo("fileRepo")
+        def httpModule = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        and:
+        if (localFirst) {
+            useRepository fileRepo, ivyHttpRepo
+        } else {
+            useRepository ivyHttpRepo, fileRepo
+        }
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.+'
+}
+configurations.all {
+    resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
+}
+"""
+        when: "missing from local"
+        expectGetDynamicRevision(httpModule)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.2"
+
+        when: "missing from remote"
+        fileRepo.module('group', 'projectA', '1.1').publish()
+        ivyHttpRepo.directoryList("group", "projectA").expectGetMissing()
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.1"
+
+        when: "present in both"
+        server.resetExpectations()
+        httpModule = ivyHttpRepo.module('group', 'projectA', '1.3').publish()
+        expectGetDynamicRevision(httpModule)
+
+        then:
+        checkResolve "group:projectA:1.+": "group:projectA:1.3"
+
+        where:
+        order          | localFirst
+        "local first"  | true
+        "remote first" | false
+    }
+
+    def "fails with reasonable error message when no cached version list in offline mode"() {
+        given:
+        useRepository ivyHttpRepo
+        buildFile << """
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.+'
+}
+"""
+        when:
+        executer.withArgument "--offline"
+
+        then:
+        fails "checkDeps"
+        failure.assertHasCause "Could not resolve all dependencies for configuration ':compile'."
+        failure.assertHasCause "No cached version listing for group:projectA:1.+ available for offline mode."
+    }
+
+    def checkResolve(Map edges) {
+        assert succeeds('checkDeps')
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edges.each {from, to ->
+                    edge(from, to)
+                }
+            }
+        }
+        true
+    }
+
+    def expectGetDynamicRevision(IvyHttpModule module) {
+        module.repository.directoryList(module.organisation, module.module).expectGet()
+        module.ivy.expectGet()
+        module.jar.expectGet()
+    }
+
+    def useRepository(Repository... repo) {
+        buildFile << """
+repositories {
+"""
+        repo.each {
+            buildFile << "ivy { url '${it.uri}' }\n"
+        }
+        buildFile << """
+}
+"""
+    }
+
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
new file mode 100644
index 0000000..01d2d22
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
@@ -0,0 +1,449 @@
+/*
+ * 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 org.gradle.integtests.resolve.ResolveTestFixture
+import spock.lang.Issue
+
+class IvyDynamicRevisionResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def setup() {
+        settingsFile << "rootProject.name = 'test' "
+    }
+
+    @Issue("GRADLE-2502")
+    def "latest.integration selects highest version regardless of status"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:latest.integration'
+  }
+  """
+
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause 'Could not find any matches for org.test:projectA:latest.integration as no versions of org.test:projectA are available.'
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.0').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.integration", "org.test:projectA:1.0")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('integration').publish()
+        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('integration').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.integration", "org.test:projectA:1.2")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('release').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.integration", "org.test:projectA:1.3")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.4').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.integration", "org.test:projectA:1.4")
+            }
+        }
+    }
+
+    @Issue("GRADLE-2502")
+    def "latest.milestone selects highest version with milestone or release status"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:latest.milestone'
+  }
+  """
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause 'Could not find any matches for org.test:projectA:latest.milestone as no versions of org.test:projectA are available.'
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '2.0').withNoMetaData().publish()
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause '''Could not find any version that matches org.test:projectA:latest.milestone.
+Versions that do not match:
+    2.0
+Searched in the following locations:
+'''
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause '''Could not find any version that matches org.test:projectA:latest.milestone.
+Versions that do not match:
+    2.0
+    1.3
+Searched in the following locations:
+'''
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus('milestone').publish()
+        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('milestone').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('release').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2")
+            }
+        }
+    }
+
+    @Issue("GRADLE-2502")
+    public void "latest.release selects highest version with release status"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:latest.release'
+  }
+  """
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause 'Could not find any matches for org.test:projectA:latest.release as no versions of org.test:projectA are available.'
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '2.0').withNoMetaData().publish()
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause '''Could not find any version that matches org.test:projectA:latest.release.
+Versions that do not match:
+    2.0
+Searched in the following locations:
+'''
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.3').withStatus('integration').publish()
+        ivyRepo.module('org.test', 'projectA', '1.2').withStatus('milestone').publish()
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause '''Could not find any version that matches org.test:projectA:latest.release.
+Versions that do not match:
+    2.0
+    1.3
+    1.2
+Searched in the following locations:
+'''
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.0').withStatus('release').publish()
+        ivyRepo.module('org.test', 'projectA', '1.1').withStatus('release').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.release", "org.test:projectA:1.1")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.1.1').withStatus('milestone').publish()
+        ivyRepo.module('org.test', 'projectA', '1.1-beta2').withStatus('integration').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.release", "org.test:projectA:1.1")
+            }
+        }
+    }
+
+    @Issue(["GRADLE-2502", "GRADLE-2794"])
+    def "version selector ending in + selects highest matching version"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:1.2+'
+  }
+  """
+        and:
+        ivyRepo.module('org.test', 'projectA', '1.1.2').publish()
+        ivyRepo.module('org.test', 'projectA', '2.0').publish()
+
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause 'Could not find any version that matches org.test:projectA:1.2+'
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.1")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2.9').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.9")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2.10').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.10")
+            }
+        }
+    }
+
+    @Issue("GRADLE-2502")
+    def "version range selects highest matching version"() {
+        given:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${ivyRepo.uri}"
+      }
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:[1.2,2.0]'
+  }
+  """
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        and:
+        ivyRepo.module('org.test', 'projectA', '1.1.2').publish()
+        ivyRepo.module('org.test', 'projectA', '2.1').publish()
+
+        when:
+        runAndFail 'checkDeps'
+
+        then:
+        failureHasCause 'Could not find any version that matches org.test:projectA:[1.2,2.0]'
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.2.1').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2.1")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.3').publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.3")
+            }
+        }
+
+        when:
+        ivyRepo.module('org.test', 'projectA', '1.4').withNoMetaData().publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.4")
+            }
+        }
+    }
+
+    @Issue("GRADLE-2502")
+    def "can resolve dynamic version from different repositories"() {
+        given:
+        def repo1 = ivyRepo("ivyRepo1")
+        def repo2 = ivyRepo("ivyRepo2")
+
+        and:
+        buildFile << """
+  repositories {
+      ivy {
+          url "${repo1.uri}"
+      }
+      ivy {
+          url "${repo2.uri}"
+      }
+
+  }
+  configurations { compile }
+  dependencies {
+      compile 'org.test:projectA:latest.milestone'
+  }
+  """
+        and:
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        repo1.module('org.test', 'projectA', '1.1').withStatus("milestone").publish()
+        repo2.module('org.test', 'projectA', '1.2').withStatus("integration").publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1")
+            }
+        }
+
+        when:
+        repo2.module('org.test', 'projectA', '1.3').withStatus("milestone").publish()
+        run 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.3")
+            }
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyFileRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyFileRepoResolveIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyFileRepoResolveIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyFileRepoResolveIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..81dc6db
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpRepoResolveIntegrationTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.resolve.ivy
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.gradle.test.fixtures.server.http.RepositoryHttpServer
+import org.junit.Rule
+
+class IvyHttpRepoResolveIntegrationTest extends AbstractIvyRemoteRepoResolveIntegrationTest {
+
+    @Rule
+    final RepositoryHttpServer server = new RepositoryHttpServer(this)
+
+    @Override
+    RepositoryServer getServer() {
+        return server
+    }
+
+    void "fails when configured with AwsCredentials"() {
+        given:
+        def remoteIvyRepo = server.remoteIvyRepo
+        def module = remoteIvyRepo.module('org.group.name', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${remoteIvyRepo.uri}"
+                    credentials(AwsCredentials) {
+                        accessKey "someKey"
+                        secretKey "someSecret"
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+
+        fails 'retrieve'
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Credentials must be an instance of: org.gradle.api.artifacts.repositories.PasswordCredentials')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..12daa3c
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyHttpsRepoResolveIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * 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.resolve.http.AbstractHttpsRepoResolveIntegrationTest
+
+class IvyHttpsRepoResolveIntegrationTest extends AbstractHttpsRepoResolveIntegrationTest {
+    protected String setupRepo() {
+        def module = ivyHttpRepo('repo1').module('my-group', 'my-module').publish()
+        module.ivy.allowGetOrHead()
+        module.jar.allowGetOrHead()
+        "ivy"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..0c31d71
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyJvmLibraryArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.JvmLibraryArtifactResolveTestFixture
+import org.gradle.test.fixtures.ivy.IvyRepository
+import spock.lang.Unroll
+
+class IvyJvmLibraryArtifactResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def fileRepo = ivyRepo
+    def httpRepo = ivyHttpRepo
+    def module = httpRepo.module("some.group", "some-artifact", "1.0")
+    JvmLibraryArtifactResolveTestFixture fixture
+
+    def setup() {
+        initBuild(httpRepo)
+
+        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
+
+        publishModule()
+    }
+
+    def initBuild(IvyRepository repo) {
+        buildFile.text = """
+repositories {
+    ivy { url '$repo.uri' }
+}
+"""
+    }
+
+    def "resolve sources artifacts"() {
+        fixture.requestingSource()
+                .expectSourceArtifact("my-sources")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-sources").expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolve javadoc artifacts"() {
+        fixture.requestingJavadoc()
+                .expectJavadocArtifact("my-javadoc")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-javadoc").expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolve all artifacts"() {
+        fixture.expectSourceArtifact("my-sources")
+                .expectJavadocArtifact("my-javadoc")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-sources").expectGet()
+        module.getArtifact(classifier: "my-javadoc").expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolves multiple artifacts of the same type"() {
+        given:
+        module.artifact(type: "source", classifier: "other-sources", ext: "jar", conf: "sources")
+        module.artifact(type: "javadoc", classifier: "other-javadoc", ext: "jar", conf: "javadoc")
+        module.publish()
+
+        fixture.expectSourceArtifact("my-sources")
+                .expectSourceArtifact("other-sources")
+                .expectJavadocArtifact("my-javadoc")
+                .expectJavadocArtifact("other-javadoc")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-sources").expectGet()
+        module.getArtifact(classifier: "other-sources").expectGet()
+        module.getArtifact(classifier: "my-javadoc").expectGet()
+        module.getArtifact(classifier: "other-javadoc").expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolves when configurations are present and empty"() {
+        given:
+        def module1 = httpRepo.module("some.group", "some-artifact", "1.1")
+        module1.configuration("sources")
+        module1.configuration("javadoc")
+        // Add an artifact to prevent the default artifact being added with conf='*'
+        module1.artifact([conf: 'default'])
+        module1.publish()
+
+        and:
+        fixture.withComponentVersion("some.group", "some-artifact", "1.1").prepare()
+
+        when:
+        module1.ivy.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "fetches missing artifacts for module #condition"() {
+        fixture.requestingSource()
+                .expectSourceArtifactNotFound("my-sources")
+                .prepare()
+        buildFile << """
+dependencies {
+    components {
+        all { details ->
+            details.changing = true
+        }
+    }
+}
+
+if (project.hasProperty('nocache')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+"""
+
+        when:
+        module.ivy.expectGet()
+        def sourceArtifact = module.getArtifact(classifier: "my-sources")
+        sourceArtifact.expectGetMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-sources.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${sourceArtifact.uri}""")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-sources.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${sourceArtifact.uri}""")
+
+        when:
+        module.publishWithChangedContent()
+        fixture.clearExpectations()
+                .expectSourceArtifact("my-sources")
+                .createVerifyTask("verifyRefresh")
+
+        and:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        sourceArtifact.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verifyRefresh")
+
+        where:
+        condition                     | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when ivy descriptor changes" | "-Pnocache"
+    }
+
+    @Unroll
+    def "updates artifacts for module #condition"() {
+        buildFile << """
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            details.changing = true
+        }
+    }
+}
+
+if (project.hasProperty('nocache')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+"""
+
+        final sourceArtifact = module.getArtifact(classifier: "my-sources")
+        fixture.requestingSource()
+                .expectSourceArtifact("my-sources")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        sourceArtifact.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        when:
+        def snapshot = file("sources/some-artifact-1.0-my-sources.jar").snapshot()
+        module.publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.sha1.expectGet()
+        sourceArtifact.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verify")
+        file("sources/some-artifact-1.0-my-sources.jar").assertHasChangedSince(snapshot)
+
+        where:
+        condition                     | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when ivy descriptor changes" | "-Pnocache"
+    }
+
+    def "reports failure to resolve artifacts of non-existing component"() {
+        fixture.expectComponentNotFound().prepare()
+
+        when:
+        module.ivy.expectGetMissing()
+        module.jar.expectHeadMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${module.jar.uri}""")
+
+        when:
+        server.resetExpectations()
+        module.ivy.expectGetMissing()
+        module.jar.expectHeadMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${module.jar.uri}""")
+    }
+
+    def "reports failure to resolve missing artifacts"() {
+        fixture.expectSourceArtifactNotFound("my-sources")
+                .expectJavadocArtifactNotFound("my-javadoc")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        def sourceArtifact = module.getArtifact(classifier: "my-sources")
+        sourceArtifact.expectGetMissing()
+        def javadocArtifact = module.getArtifact(classifier: "my-javadoc")
+        javadocArtifact.expectGetMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-sources.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${sourceArtifact.uri}""")
+        failure.assertHasCause("""Could not find some-artifact-my-javadoc.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${javadocArtifact.uri}""")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-sources.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${sourceArtifact.uri}""")
+        failure.assertHasCause("""Could not find some-artifact-my-javadoc.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${javadocArtifact.uri}""")
+    }
+
+    def "resolves when some artifacts are missing"() {
+        fixture.expectSourceArtifact("my-sources")
+                .expectJavadocArtifactNotFound("my-javadoc")
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-sources").expectGet()
+        def javadocArtifact = module.getArtifact(classifier: "my-javadoc")
+        javadocArtifact.expectGetMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-javadoc.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${javadocArtifact.uri}""")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some-artifact-my-javadoc.jar (some.group:some-artifact:1.0).
+Searched in the following locations:
+    ${javadocArtifact.uri}""")
+    }
+
+    def "resolves and recovers from broken artifacts"() {
+        given:
+        module.artifact(type: "source", classifier: "broken-sources", ext: "jar", conf: "sources")
+        module.publish()
+
+        fixture.expectSourceArtifact("my-sources")
+                .expectSourceArtifactFailure()
+                .expectJavadocArtifactFailure()
+                .prepare()
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(classifier: "my-sources").expectGet()
+        def brokenSources = module.getArtifact(classifier: "broken-sources")
+        brokenSources.expectGetBroken()
+        def brokenJavadoc = module.getArtifact(classifier: "my-javadoc")
+        brokenJavadoc.expectGetBroken()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("Could not download some-artifact-broken-sources.jar (some.group:some-artifact:1.0)")
+        failure.assertHasCause("Could not get resource '${brokenSources.uri}'.")
+        failure.assertHasCause("Could not GET '${brokenSources.uri}'. Received status code 500 from server: broken")
+        failure.assertHasCause("Could not download some-artifact-my-javadoc.jar (some.group:some-artifact:1.0)")
+        failure.assertHasCause("Could not get resource '${brokenJavadoc.uri}'.")
+        failure.assertHasCause("Could not GET '${brokenJavadoc.uri}'. Received status code 500 from server: broken")
+
+        when:
+        fixture.clearExpectations()
+                .expectSourceArtifact("my-sources")
+                .expectSourceArtifact("broken-sources")
+                .expectJavadocArtifact("my-javadoc")
+                .createVerifyTask("verifyFixed")
+
+        and:
+        server.resetExpectations()
+        // Only the broken artifacts are not cached
+        brokenSources.expectGet()
+        brokenJavadoc.expectGet()
+
+        then:
+        succeeds("verifyFixed")
+    }
+
+    def "resolve and does not cache artifacts from local repository"() {
+        initBuild(fileRepo)
+
+        fixture.expectSourceArtifact("my-sources")
+                .expectJavadocArtifact("my-javadoc")
+                .prepare()
+
+        when:
+        succeeds("verify")
+
+        and:
+        def snapshot = file("sources/some-artifact-1.0-my-sources.jar").snapshot()
+
+        and:
+        module.publishWithChangedContent()
+
+        then:
+        succeeds("verify")
+        file("sources/some-artifact-1.0-my-sources.jar").assertHasChangedSince(snapshot)
+    }
+
+    def "can resolve artifacts with maven scheme from ivy repository"() {
+        // Published with no configurations, and a source artifact only
+        def moduleWithMavenScheme = httpRepo.module("some.group", "some-artifact", "1.1")
+        moduleWithMavenScheme.artifact(classifier: "sources")
+        moduleWithMavenScheme.publish()
+
+        fixture.withComponentVersion("some.group", "some-artifact", "1.1")
+                .requestingSource().requestingJavadoc()
+                .expectSourceArtifact("sources")
+                .prepare()
+
+        when:
+        moduleWithMavenScheme.ivy.expectGet()
+        moduleWithMavenScheme.getArtifact(classifier: "sources").expectHead()
+        moduleWithMavenScheme.getArtifact(classifier: "sources").expectGet()
+        moduleWithMavenScheme.getArtifact(classifier: "javadoc").expectHeadMissing()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def checkArtifactsResolvedAndCached() {
+        assert succeeds("verify")
+        server.resetExpectations()
+        assert succeeds("verify")
+        true
+    }
+
+    private publishModule() {
+        module.configuration("sources")
+        module.configuration("javadoc")
+        // use uncommon classifiers that are different from those used by maven, 
+        // in order to prove that artifact names don't matter
+        module.artifact(type: "source", classifier: "my-sources", ext: "jar", conf: "sources")
+        module.artifact(type: "javadoc", classifier: "my-javadoc", ext: "jar", conf: "javadoc")
+        module.publish()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..a79b352
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.MetadataArtifactResolveTestFixture
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.test.fixtures.ivy.IvyRepository
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import spock.lang.Unroll
+
+class IvyModuleArtifactResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    private MetadataArtifactResolveTestFixture fixture
+    def httpRepo = ivyHttpRepo
+
+    def setup() {
+        initBuild(httpRepo)
+        fixture = new MetadataArtifactResolveTestFixture(buildFile)
+        fixture.basicSetup()
+    }
+
+    def initBuild(IvyRepository repo) {
+        buildFile << """
+repositories {
+    ivy { url '$repo.uri' }
+}
+"""
+    }
+
+    def "successfully resolve existing Ivy module artifact"() {
+        given:
+        IvyHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent('IvyModule').requestArtifact('IvyDescriptorArtifact')
+               .expectResolvedComponentResult().expectMetadataFiles(module.ivy.file)
+               .createVerifyTaskModuleComponentIdentifier()
+
+        module.ivy.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "invalid component type and artifact type (#reason)"() {
+        given:
+        IvyHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent(component).requestArtifact(artifactType)
+               .expectUnresolvedComponentResult(exception)
+               .expectNoMetadataFiles()
+               .createVerifyTaskModuleComponentIdentifier()
+        module.ivy.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        where:
+        component     | artifactType       | reason                                                                    | exception
+        'IvyModule'   | 'MavenPomArtifact' | 'cannot mix IvyModule with Maven metadata artifact type MavenPomArtifact' | new IllegalArgumentException('Artifact type org.gradle.maven.MavenPomArtifact is not registered for component type org.gradle.ivy.IvyModule.')
+        'MavenModule' | 'MavenPomArtifact' | 'cannot retrieve Maven component and metadata artifact for Ivy module'    | new ArtifactResolveException("Could not determine artifacts for some.group:some-artifact:1.0: Cannot locate 'maven pom' artifacts for 'some.group:some-artifact:1.0' in repository 'ivy'")
+    }
+
+    def "requesting IvyModule for a project component"() {
+        given:
+        IvyHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent('IvyModule').requestArtifact('IvyDescriptorArtifact')
+               .expectUnresolvedComponentResult(new IllegalArgumentException("Cannot query artifacts for a project component (project :)"))
+               .expectNoMetadataFiles()
+               .createVerifyTaskForProjectComponentIdentifier()
+
+        module.ivy.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "request an ivy descriptor for an ivy module with no descriptor"() {
+        given:
+        IvyHttpModule module = publishModuleWithoutMetadata()
+
+        when:
+        fixture.requestComponent('IvyModule').requestArtifact('IvyDescriptorArtifact')
+               .expectResolvedComponentResult()
+               .expectNoMetadataFiles()
+               .expectUnresolvedArtifactResult(ArtifactResolveException, "Could not find ivy.xml (some.group:some-artifact:1.0).")
+               .createVerifyTaskModuleComponentIdentifier()
+
+        // TODO - should do single request
+        module.ivy.expectGetMissing()
+        module.ivy.expectGetMissing()
+        module.jar.expectHead()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "updates artifacts for module #condition"() {
+        given:
+        IvyHttpModule module = publishModule()
+
+        fixture.configureChangingModule()
+        fixture.requestComponent('IvyModule').requestArtifact('IvyDescriptorArtifact')
+               .expectResolvedComponentResult().expectMetadataFiles(module.ivyFile)
+               .createVerifyTaskModuleComponentIdentifier()
+
+        when:
+        module.ivy.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        when:
+        module.publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+        module.ivy.expectHead()
+        module.ivy.sha1.expectGet()
+        module.ivy.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verify")
+
+        where:
+        condition                     | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when ivy descriptor changes" | "-Pnocache"
+    }
+
+    private IvyHttpModule publishModule() {
+        def module = createModule()
+        module.publish()
+    }
+
+    private IvyHttpModule publishModuleWithoutMetadata() {
+        IvyHttpModule module = createModule()
+        module.withNoMetaData()
+        module.publish()
+    }
+
+    private IvyHttpModule createModule() {
+        httpRepo.module(fixture.id.group, fixture.id.module, fixture.id.version)
+    }
+
+    def checkArtifactsResolvedAndCached() {
+        assert succeeds('verify')
+        server.resetExpectations()
+        assert succeeds('verify')
+        true
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy
new file mode 100644
index 0000000..df46560
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyModuleResolveIntegrationTest.groovy
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Unroll
+
+class IvyModuleResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    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 "${ivyHttpRepo.uri}" }
+    }
+    compile group: 'ivy.configuration', name: 'projectA', version: '1.2', configuration: 'a'
+}
+task retrieve(type: Sync) {
+  from configurations.compile
+  into 'libs'
+}
+"""
+        def projectA = ivyHttpRepo.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()
+
+        def projectB = ivyHttpRepo.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()
+
+        def projectC = ivyHttpRepo.module('ivy.configuration', 'projectC', '1.7').publish()
+        def projectD = ivyHttpRepo.module('ivy.configuration', 'projectD', '1.7').publish()
+
+        projectA.allowAll()
+        projectB.allowAll()
+        projectC.allowAll()
+        projectD.allowAll()
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants(*(['projectA-1.2.jar'] + jars))
+
+        when:
+        server.resetExpectations()
+        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')
+    }
+
+    def "prefers module with metadata to module with no metadata"() {
+        given:
+        def repo1 = ivyHttpRepo("repo1")
+        def moduleWithNoMetaData = repo1.module("org.gradle", "test", "1.45").withNoMetaData().publish()
+        def repo2 = ivyHttpRepo("repo2")
+        def moduleWithMetaData = repo2.module("org.gradle", "test", "1.45").publishWithChangedContent()
+        assert moduleWithNoMetaData.jarFile.text != moduleWithMetaData.jarFile.text
+
+        and:
+        buildFile << """
+repositories {
+    ivy { url "${repo1.uri}" }
+    ivy { url "${repo2.uri}" }
+}
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into "libs"
+}
+"""
+
+        when:
+        moduleWithNoMetaData.ivy.expectGetMissing()
+        moduleWithNoMetaData.jar.expectHead()
+        moduleWithMetaData.ivy.expectGet()
+        moduleWithMetaData.jar.expectGet()
+        succeeds "retrieve"
+
+        then:
+        file("libs").assertHasDescendants("test-1.45.jar")
+        file("libs/test-1.45.jar").assertIsCopyOf(moduleWithMetaData.jarFile)
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..dc92e66
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+
+class IvyResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def "a dependency on an ivy module includes all artifacts and transitive dependencies of referenced configuration"() {
+        given:
+        ivyRepo.module("org.gradle", "test", "1.45")
+                .dependsOn("org.gradle", "other", "preview-1")
+                .artifact()
+                .artifact(classifier: "classifier")
+                .artifact(name: "test-extra")
+                .publish()
+
+        ivyRepo.module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        settingsFile << """
+rootProject.name = 'testproject'
+"""
+
+        buildFile << """
+group = 'org.gradle'
+version = '1.0'
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'test-1.45-classifier.jar', 'test-extra-1.45.jar', 'other-preview-1.jar']
+    def result = configurations.compile.incoming.resolutionResult
+
+    // Check root component
+    def rootId = result.root.id
+    assert rootId instanceof ProjectComponentIdentifier
+    def rootPublishedAs = result.root.moduleVersion
+    assert rootPublishedAs.group == 'org.gradle'
+    assert rootPublishedAs.name == 'testproject'
+    assert rootPublishedAs.version == '1.0'
+
+    // Check external module components
+    def externalComponents = result.root.dependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
+    assert externalComponents.size() == 1
+    def selectedExternalComponent = externalComponents[0]
+    assert selectedExternalComponent.id.group == 'org.gradle'
+    assert selectedExternalComponent.id.module == 'test'
+    assert selectedExternalComponent.id.version == '1.45'
+    assert selectedExternalComponent.moduleVersion.group == 'org.gradle'
+    assert selectedExternalComponent.moduleVersion.name == 'test'
+    assert selectedExternalComponent.moduleVersion.version == '1.45'
+
+    // Check external dependencies
+    def externalDependencies = result.root.dependencies.requested.findAll { it instanceof ModuleComponentSelector }
+    assert externalDependencies.size() == 1
+    def requestedExternalDependency = externalDependencies[0]
+    assert requestedExternalDependency.group == 'org.gradle'
+    assert requestedExternalDependency.module == 'test'
+    assert requestedExternalDependency.version == '1.45'
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    def "dependency includes only the artifacts of the default configuration"() {
+        given:
+        server.start()
+        def module = ivyHttpRepo.module("org.gradle", "test", "1.45")
+                .configuration('archives')
+                .configuration('default', extendsFrom: ['archives'])
+                .configuration('source')
+                .configuration('javadoc')
+                .artifact(conf: 'archives')
+                .artifact(classifier: 'source', conf: 'source')
+                .artifact(classifier: 'javadoc', conf: 'javadoc')
+                .publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyHttpRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar']
+}
+"""
+
+        expect:
+        module.ivy.expectGet()
+        module.getArtifact().expectGet()
+        succeeds "check"
+
+        // Need to check twice to use the cached version too
+        succeeds "check"
+    }
+
+    def "dependency that references a classifier includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
+        given:
+        ivyRepo.module("org.gradle", "test", "1.45")
+                .dependsOn("org.gradle", "other", "preview-1")
+                .artifact(classifier: "classifier")
+                .artifact(name: "test-extra")
+                .publish()
+        ivyRepo.module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45:classifier"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    def "dependency that references a classifier can resolve module with no metadata"() {
+        given:
+        ivyRepo.module("org.gradle", "test", "1.45").withNoMetaData().artifact(classifier: "classifier").publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45:classifier"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar']
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    def "dependency that references an artifact includes the matching artifact only plus the transitive dependencies of referenced configuration"() {
+        given:
+        def module = ivyHttpRepo.module("org.gradle", "test", "1.45")
+                .dependsOn("org.gradle", "other", "preview-1")
+                .artifact(classifier: "classifier")
+                .artifact(name: "test-extra")
+                .publish()
+        def module2 = ivyHttpRepo.module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyHttpRepo.uri}" } }
+configurations { compile }
+dependencies {
+    compile ("org.gradle:test:1.45") {
+        artifact {
+            name = 'test-extra'
+            type = 'jar'
+        }
+    }
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-extra-1.45.jar', 'other-preview-1.jar']
+}
+"""
+
+        when:
+        module.ivy.expectGet()
+        module.getArtifact(name: 'test-extra').expectGet()
+        module2.ivy.expectGet()
+        module2.jar.expectGet()
+
+        then:
+        succeeds "check"
+    }
+
+    def "uses correct artifact name for module with no metadata where artifact name does not match module name"() {
+        given:
+        def module = ivyHttpRepo.module("org.gradle", "test", "1.45")
+                .withNoMetaData()
+                .artifact(name: 'my-test-artifact')
+                .publish()
+
+        and:
+        buildFile << """
+repositories {
+    ivy {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile ("org.gradle:test:1.45") {
+        artifact {
+            name = 'my-test-artifact'
+            extension = 'jar'
+            type = 'jar'
+        }
+    }
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['my-test-artifact-1.45.jar']
+}
+"""
+
+        when:
+        module.ivy.expectGetMissing()
+        module.getArtifact(name: 'my-test-artifact').expectHead()
+        module.getArtifact(name: 'my-test-artifact').expectGet()
+
+        then:
+        succeeds "check"
+    }
+
+    def "transitive flag of referenced configuration affects its transitive dependencies only"() {
+        given:
+        ivyRepo.module("org.gradle", "test", "1.45")
+                .dependsOn("org.gradle", "other", "preview-1")
+                .nonTransitive('default')
+                .publish()
+        ivyRepo.module("org.gradle", "other", "preview-1").dependsOn("org.gradle", "other2", "7").publish()
+        ivyRepo.module("org.gradle", "other2", "7").publish()
+
+        and:
+        buildFile << """
+repositories { ivy { url "${ivyRepo.uri}" } }
+configurations {
+    compile
+    runtime.extendsFrom compile
+}
+dependencies {
+    compile "org.gradle:test:1.45"
+    runtime "org.gradle:other:preview-1"
+}
+
+task check << {
+    def spec = { it.name == 'test' } as Spec
+
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
+    assert configurations.compile.resolvedConfiguration.getFiles(spec).collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
+
+    assert configurations.runtime.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar', 'other2-7.jar']
+    assert configurations.compile.resolvedConfiguration.getFiles(spec).collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
new file mode 100644
index 0000000..8656848
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/BadPomFileResolveIntegrationTest.groovy
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Issue
+
+class BadPomFileResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    @Issue("https://issues.gradle.org/browse/GRADLE-1005")
+    def "can handle self referencing dependency"() {
+        given:
+        mavenRepo().module('group', 'artifact', '1.0').dependsOn('group', 'artifact', '1.0').publish()
+
+        and:
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo().uri}" }
+            }
+            configurations { compile }
+            dependencies {
+                compile "group:artifact:1.0"
+            }
+            task libs << { assert configurations.compile.files.collect {it.name} == ['artifact-1.0.jar'] }
+        """
+
+        expect:
+        succeeds ":libs"
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2861")
+    def "can handle pom with placeholders in dependency management"() {
+        given:
+        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"
+    }
+
+    public void "reports POM that cannot be parsed"() {
+        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}")
+            .assertHasCause("null name not allowed")
+    }
+
+    def "reports missing parent POM"() {
+        given:
+        def parent = mavenHttpRepo.module("org", "parent", "1.0")
+
+        def child = mavenHttpRepo.module("org", "child", "1.0")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task showBroken << { println configurations.compile.files }
+"""
+
+        when:
+        child.pom.expectGet()
+        parent.pom.expectGetMissing()
+
+        // Will always check for a default artifact with a module with 'pom' packaging
+        // TODO - should not make this request
+        parent.artifact.expectHeadMissing()
+
+        and:
+        fails 'showBroken'
+
+        then:
+        failure.assertResolutionFailure(':compile')
+                .assertHasCause("Could not parse POM ${child.pom.uri}")
+                .assertHasCause("""Could not find org:parent:1.0.
+Searched in the following locations:
+    ${parent.pom.uri}
+    ${parent.artifact.uri}""")
+    }
+
+    def "reports parent POM that cannot be parsed"() {
+        given:
+        def parent = mavenHttpRepo.module("org", "parent", "1.0").publish()
+        parent.pomFile.text = "<project/>"
+
+        def child = mavenHttpRepo.module("org", "child", "1.0")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task showBroken << { println configurations.compile.files }
+"""
+
+        when:
+        child.pom.expectGet()
+        parent.pom.expectGet()
+
+        and:
+        fails 'showBroken'
+
+        then:
+        failure.assertResolutionFailure(":compile")
+            .assertHasCause("Could not parse POM ${child.pom.uri}")
+            .assertHasCause("Could not parse POM ${parent.pom.uri}")
+            .assertHasCause("null name not allowed")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
new file mode 100644
index 0000000..fff00ca
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.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.integtests.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+class MavenBrokenRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    public void "reports and recovers from failed POM download"() {
+        given:
+        def module = mavenHttpRepo.module('group', 'projectA', '1.3').publish()
+
+        buildFile << """
+repositories {
+    maven {
+        url "${ivyHttpRepo.uri}"
+    }
+}
+configurations { broken }
+dependencies {
+    broken 'group:projectA:1.3'
+}
+task showBroken << { println configurations.broken.files }
+"""
+
+        when:
+        module.pom.expectGetBroken()
+        fails("showBroken")
+
+        then:
+        failure
+            .assertHasDescription('Execution failed for task \':showBroken\'.')
+            .assertResolutionFailure(':broken')
+            .assertHasCause('Could not resolve group:projectA:1.3.')
+            .assertHasCause("Could not GET '${module.pom.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+
+        then:
+        succeeds("showBroken")
+    }
+
+    public void "reports and recovers from failed artifact download"() {
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.2'
+}
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        and:
+        def module = mavenHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        module.pom.expectGet()
+        module.artifact.expectGetBroken()
+
+        then:
+        fails "retrieve"
+        failure.assertHasCause("Could not download projectA.jar (group:projectA:1.2)")
+        failure.assertHasCause("Could not GET '${module.artifact.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        module.artifact.expectGet()
+
+        then:
+        succeeds "retrieve"
+        file('libs').assertHasDescendants('projectA-1.2.jar')
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy
new file mode 100644
index 0000000..d0b6777
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesChangingModulesIntegrationTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesChangingModulesIntegrationTest
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+
+class MavenComponentMetadataRulesChangingModulesIntegrationTest extends ComponentMetadataRulesChangingModulesIntegrationTest {
+    MavenHttpRepository getRepo() {
+        mavenHttpRepo
+    }
+
+    String getRepoDeclaration() {
+"""
+repositories {
+    maven {
+        url "$repo.uri"
+    }
+}
+"""
+    }
+
+    def setup() {
+        moduleA.rootMetaData.allowGetOrHead()
+    }
+
+    def "snapshot dependencies have changing flag initialized to true"() {
+        def moduleB = repo.module("org.test", "moduleB", "1.0-SNAPSHOT").publish()
+        moduleB.allowAll()
+
+        buildFile <<
+"""
+$repoDeclaration
+configurations {
+    modules
+}
+dependencies {
+    modules "org.test:moduleB:1.0-SNAPSHOT"
+    components {
+        all { details ->
+            file(details.id.name).text = details.changing
+        }
+    }
+}
+task resolve << {
+    configurations.modules.files
+}
+"""
+
+        when:
+        run("resolve")
+
+        then:
+        file("moduleB").text == "true"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..85b5943
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesIntegrationTest.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.integtests.resolve.maven
+
+import org.gradle.integtests.resolve.ComponentMetadataRulesIntegrationTest
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+
+class MavenComponentMetadataRulesIntegrationTest extends ComponentMetadataRulesIntegrationTest {
+    @Override
+    MavenHttpRepository getRepo() {
+        mavenHttpRepo
+    }
+
+    @Override
+    String getRepoDeclaration() {
+"""
+repositories {
+    maven {
+        url "$repo.uri"
+    }
+}
+"""
+    }
+
+    @Override
+    String getDefaultStatus() {
+        "release"
+    }
+
+    def "rule that accepts IvyModuleDescriptor isn't invoked for Maven component"() {
+        def module = repo.module('org.test', 'projectA', '1.0').publish()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+
+        buildFile <<
+"""
+def plainRuleInvoked = false
+def ivyRuleInvoked = false
+
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            plainRuleInvoked = true
+        }
+        all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            ivyRuleInvoked = true
+        }
+    }
+}
+
+resolve.doLast {
+    assert plainRuleInvoked
+    assert !ivyRuleInvoked
+}
+"""
+
+        expect:
+        succeeds 'resolve'
+        // also works when already cached
+        succeeds 'resolve'
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
new file mode 100644
index 0000000..ed13e0d
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.ComponentMetadataRulesStatusIntegrationTest
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+
+class MavenComponentMetadataRulesStatusIntegrationTest extends ComponentMetadataRulesStatusIntegrationTest {
+    MavenHttpRepository getRepo() {
+        mavenHttpRepo
+    }
+
+    String getRepoDeclaration() {
+"""
+repositories {
+    maven {
+        url "$repo.uri"
+    }
+}
+"""
+    }
+
+    def "snapshot and release versions have correct status"() {
+        given:
+        repo.module('group1', 'projectA', '1.0').publish().allowAll()
+        repo.module('group2', 'projectB', '2.0-SNAPSHOT').publish().allowAll()
+        def c = repo.module('group2', 'projectC', '12-SNAPSHOT').publish().allowAll()
+
+        and:
+        buildFile.text =
+"""
+$repoDeclaration
+configurations { compile }
+dependencies {
+    compile 'group1:projectA:1.0'
+    compile 'group2:projectB:2.0-SNAPSHOT'
+    compile 'group2:projectC:${c.publishArtifactVersion}'
+    components {
+        all {
+            assert it.status == it.id.name == "projectA" ? "release" : "integration"
+            assert it.statusScheme == ['integration', 'milestone', 'release']
+        }
+    }
+}
+
+task resolve << {
+    configurations.compile.resolve()
+}
+"""
+
+        expect:
+        succeeds "resolve"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.groovy
new file mode 100644
index 0000000..f0b0b04
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenCustomPackagingResolveIntegrationTest.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.integtests.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import spock.lang.Issue
+
+class MavenCustomPackagingResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2984")
+    def "can resolve dependency with custom packaging"() {
+        def extension = "aar"
+        m2Installation.mavenRepo().module("local", "local", "1.0").hasType(extension).hasPackaging(extension).publish()
+        def remote = mavenHttpRepo.module("remote", "remote", "1.0").hasType(extension).hasPackaging(extension).publish()
+        remote.pom.expectGet()
+        remote.artifact.expectHead()
+        remote.artifact.expectGet()
+
+        when:
+        buildScript """
+            configurations {
+                local
+                remote
+            }
+            repositories {
+                mavenLocal() // there's a different code path for mavenLocal() so test it explicitly
+                maven { url "$mavenHttpRepo.uri" }
+            }
+            dependencies {
+                local "local:local:1.0"
+                remote "remote:remote:1.0"
+            }
+
+            task local(type: Sync) {
+                into 'local'
+                from configurations.local
+            }
+            task remote(type: Sync) {
+                into 'remote'
+                from configurations.remote
+            }
+        """
+
+        then:
+        succeeds("local", "remote")
+
+        and:
+        file("local").assertHasDescendants("local-1.0.aar")
+        file("remote").assertHasDescendants("remote-1.0.aar")
+    }
+
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
new file mode 100644
index 0000000..d9b4738
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyResolveIntegrationTest.groovy
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+class MavenDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
+    def "dependency includes main artifact and runtime dependencies of referenced module"() {
+        given:
+        def module = mavenRepo().module("org.gradle", "test", "1.45")
+        module.dependsOn("org.gradle", "other", "preview-1")
+        module.artifact(classifier: 'classifier')
+        module.publish()
+        mavenRepo().module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        settingsFile << """
+rootProject.name = 'testproject'
+"""
+
+        buildFile << """
+group = 'org.gradle'
+version = '1.0'
+repositories { maven { url "${mavenRepo().uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45.jar', 'other-preview-1.jar']
+    def result = configurations.compile.incoming.resolutionResult
+
+    // Check root component
+    def rootId = result.root.id
+    assert rootId instanceof ProjectComponentIdentifier
+    def rootPublishedAs = result.root.moduleVersion
+    assert rootPublishedAs.group == 'org.gradle'
+    assert rootPublishedAs.name == 'testproject'
+    assert rootPublishedAs.version == '1.0'
+
+    // Check external module components
+    def externalComponents = result.root.dependencies.selected.findAll { it.id instanceof ModuleComponentIdentifier }
+    assert externalComponents.size() == 1
+    def selectedExternalComponent = externalComponents[0]
+    assert selectedExternalComponent.id.group == 'org.gradle'
+    assert selectedExternalComponent.id.module == 'test'
+    assert selectedExternalComponent.id.version == '1.45'
+    assert selectedExternalComponent.moduleVersion.group == 'org.gradle'
+    assert selectedExternalComponent.moduleVersion.name == 'test'
+    assert selectedExternalComponent.moduleVersion.version == '1.45'
+
+    // Check external dependencies
+    def externalDependencies = result.root.dependencies.requested.findAll { it instanceof ModuleComponentSelector }
+    assert externalDependencies.size() == 1
+    def requestedExternalDependency = externalDependencies[0]
+    assert requestedExternalDependency.group == 'org.gradle'
+    assert requestedExternalDependency.module == 'test'
+    assert requestedExternalDependency.version == '1.45'
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    def "dependency that references a classifier includes the matching artifact only plus the runtime dependencies of referenced module"() {
+        given:
+        def module = mavenRepo().module("org.gradle", "test", "1.45")
+        module.dependsOn("org.gradle", "other", "preview-1")
+        module.artifact(classifier: 'classifier')
+        module.artifact(classifier: 'some-other')
+        module.publish()
+        mavenRepo().module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url "${mavenRepo().uri}" } }
+configurations { compile }
+dependencies {
+    compile "org.gradle:test:1.45:classifier"
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    def "dependency that references an artifact includes the matching artifact only plus the runtime dependencies of referenced module"() {
+        given:
+        def module = mavenRepo().module("org.gradle", "test", "1.45")
+        module.dependsOn("org.gradle", "other", "preview-1")
+        module.artifact(classifier: 'classifier')
+        module.publish()
+        mavenRepo().module("org.gradle", "other", "preview-1").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url "${mavenRepo().uri}" } }
+configurations { compile }
+dependencies {
+    compile ("org.gradle:test:1.45") {
+        artifact {
+            name = 'test'
+            type = 'jar'
+            classifier = 'classifier'
+        }
+    }
+}
+
+task check << {
+    assert configurations.compile.collect { it.name } == ['test-1.45-classifier.jar', 'other-preview-1.jar']
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+
+    @Requires(TestPrecondition.ONLINE)
+    def "resolves dependencies on real projects"() {
+        // Hibernate core brings in conflicts, exclusions and root poms
+        // Add a direct dependency on an earlier version of commons-collection than required by hibernate core
+        // Logback classic depends on a later version of slf4j-api than required by hibernate core
+
+        given:
+        buildFile << """
+repositories {
+    mavenCentral()
+}
+
+configurations {
+    compile
+}
+
+dependencies {
+    compile "commons-collections:commons-collections:3.0"
+    compile "ch.qos.logback:logback-classic:0.9.30"
+    compile "org.hibernate:hibernate-core:3.6.7.Final"
+}
+
+task check << {
+    def compile = configurations.compile
+    assert compile.resolvedConfiguration.firstLevelModuleDependencies.collect { it.name } == [
+        'ch.qos.logback:logback-classic:0.9.30',
+        'org.hibernate:hibernate-core:3.6.7.Final',
+        'commons-collections:commons-collections:3.1'
+    ]
+
+    def filteredDependencies = compile.resolvedConfiguration.getFirstLevelModuleDependencies({ it.name == 'logback-classic' } as Spec)
+    assert filteredDependencies.collect { it.name } == [
+        'ch.qos.logback:logback-classic:0.9.30'
+    ]
+
+    def filteredFiles = compile.resolvedConfiguration.getFiles({ it.name == 'logback-classic' } as Spec)
+    assert filteredFiles.collect { it.name } == [
+        'logback-classic-0.9.30.jar',
+         'logback-core-0.9.30.jar',
+          'slf4j-api-1.6.2.jar'
+    ]
+
+    assert compile.collect { it.name } == [
+        'logback-classic-0.9.30.jar',
+        'hibernate-core-3.6.7.Final.jar',
+        'logback-core-0.9.30.jar',
+        'slf4j-api-1.6.2.jar',
+        'antlr-2.7.6.jar',
+        'dom4j-1.6.1.jar',
+        'hibernate-commons-annotations-3.2.0.Final.jar',
+        'hibernate-jpa-2.0-api-1.0.1.Final.jar',
+        'jta-1.1.jar',
+        'commons-collections-3.1.jar'
+    ]
+
+    assert compile.resolvedConfiguration.resolvedArtifacts.collect { it.file.name } == [
+        'logback-classic-0.9.30.jar',
+        'hibernate-core-3.6.7.Final.jar',
+        'logback-core-0.9.30.jar',
+        'slf4j-api-1.6.2.jar',
+        'antlr-2.7.6.jar',
+        'dom4j-1.6.1.jar',
+        'hibernate-commons-annotations-3.2.0.Final.jar',
+        'hibernate-jpa-2.0-api-1.0.1.Final.jar',
+        'jta-1.1.jar',
+        'commons-collections-3.1.jar'
+    ]
+}
+"""
+
+        expect:
+        succeeds "check"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy
new file mode 100644
index 0000000..fa9bbfb
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDynamicResolveIntegrationTest.groovy
@@ -0,0 +1,401 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+
+class MavenDynamicResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    def "can resolve snapshot versions with version range"() {
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenHttpRepo.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile group: "org.test", name: "projectA", version: "1.+"
+    compile group: "org.test", name: "projectB", version: "1.+"
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        when:
+        mavenHttpRepo.module("org.test", "projectA", "1.0").publish()
+        mavenHttpRepo.module("org.test", "projectA", "1.1-SNAPSHOT").publish()
+        def matchingA = mavenHttpRepo.module("org.test", "projectA", "1.1").publish()
+        mavenHttpRepo.module("org.test", "projectA", "2.0").publish()
+
+        mavenHttpRepo.module("org.test", "projectB", "1.1").publish()
+        def matchingB = mavenHttpRepo.module("org.test", "projectB", "1.2-SNAPSHOT").publish()
+        mavenHttpRepo.module("org.test", "projectB", "2.0").publish()
+
+        and:
+        mavenHttpRepo.getModuleMetaData("org.test", "projectA").expectGet()
+        matchingA.pom.expectGet()
+        matchingA.artifact.expectGet()
+
+        mavenHttpRepo.getModuleMetaData("org.test", "projectB").expectGet()
+        matchingB.metaData.expectGet()
+        matchingB.pom.expectGet()
+        matchingB.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.1.jar', 'projectB-1.2-SNAPSHOT.jar')
+        file('libs/projectA-1.1.jar').assertIsCopyOf(matchingA.artifactFile)
+        file('libs/projectB-1.2-SNAPSHOT.jar').assertIsCopyOf(matchingB.artifactFile)
+
+        when:
+        server.resetExpectations()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.1.jar', 'projectB-1.2-SNAPSHOT.jar')
+    }
+
+    def "can resolve dynamic version declared in pom as transitive dependency from HTTP Maven repository"() {
+        given:
+        mavenHttpRepo.module('org.test', 'projectC', '1.1').publish()
+        def projectC = mavenHttpRepo.module('org.test', 'projectC', '1.5').publish()
+        mavenHttpRepo.module('org.test', 'projectC', '2.0').publish()
+        def projectB = mavenHttpRepo.module('org.test', 'projectB', '1.0').dependsOn("org.test", 'projectC', '[1.0, 2.0)').publish()
+        def projectA = mavenHttpRepo.module('org.test', 'projectA', '1.0').dependsOn('org.test', 'projectB', '1.0').publish()
+
+        buildFile << """
+    repositories {
+        maven { url '${mavenHttpRepo.uri}' }
+    }
+    configurations { compile }
+    dependencies {
+        compile 'org.test:projectA:1.0'
+    }
+
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.compile
+    }
+    """
+
+        when:
+        projectA.pom.expectGet()
+        projectA.getArtifact().expectGet()
+        projectB.pom.expectGet()
+        projectB.getArtifact().expectGet()
+        mavenHttpRepo.getModuleMetaData("org.test", "projectC").expectGet()
+        projectC.pom.expectGet()
+        projectC.getArtifact().expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar', 'projectC-1.5.jar')
+        def snapshot = file('libs/projectA-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
+    }
+
+    def "falls back to directory listing when maven-metadata.xml is missing"() {
+        given:
+        mavenHttpRepo.module('org.test', 'projectA', '1.0').publish()
+        def projectA = mavenHttpRepo.module('org.test', 'projectA', '1.5').publish()
+
+        buildFile << """
+    repositories {
+        maven { url '${mavenHttpRepo.uri}' }
+    }
+    configurations { compile }
+    dependencies {
+        compile 'org.test:projectA:1.+'
+    }
+
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.compile
+    }
+    """
+
+        when:
+        mavenHttpRepo.getModuleMetaData("org.test", "projectA").expectGetMissing()
+        mavenHttpRepo.directory("org.test", "projectA").expectGet()
+        projectA.pom.expectGet()
+        projectA.getArtifact().expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.5.jar')
+        def snapshot = file('libs/projectA-1.5.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs/projectA-1.5.jar').assertHasNotChangedSince(snapshot)
+    }
+
+    def "reports and recovers from broken maven-metadata.xml and directory listing"() {
+        given:
+        mavenHttpRepo.module('org.test', 'projectA', '1.0').publish()
+        def projectA = mavenHttpRepo.module('org.test', 'projectA', '1.5').publish()
+
+        buildFile << """
+    repositories {
+        maven { url '${mavenHttpRepo.uri}' }
+    }
+    configurations { compile }
+    dependencies {
+        compile 'org.test:projectA:1.+'
+    }
+
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.compile
+    }
+    """
+
+        when:
+        def metaData = mavenHttpRepo.getModuleMetaData("org.test", "projectA")
+        metaData.expectGetBroken()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause('Could not resolve org.test:projectA:1.+.')
+        failure.assertHasCause('Failed to list versions for org.test:projectA.')
+        failure.assertHasCause("Unable to load Maven meta-data from ${metaData.uri}.")
+        failure.assertHasCause("Could not GET '${metaData.uri}'. Received status code 500 from server")
+
+        when:
+        metaData.expectGetMissing()
+
+        def moduleDir = mavenHttpRepo.directory("org.test", "projectA")
+        moduleDir.expectGetBroken()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause('Could not resolve org.test:projectA:1.+.')
+        failure.assertHasCause('Failed to list versions for org.test:projectA.')
+        failure.assertHasCause("Could not list versions using M2 pattern '${mavenHttpRepo.uri}")
+        failure.assertHasCause("Could not GET '${moduleDir.uri}'. Received status code 500 from server")
+
+        when:
+        server.resetExpectations()
+        metaData.expectGet()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.5.jar')
+    }
+
+    def "dynamic version reports and recovers from broken module"() {
+        given:
+        def repo = mavenHttpRepo("repo1")
+        def projectA = repo.module('group', 'projectA', '1.1').publish()
+
+        buildFile << """
+        repositories {
+            maven { url '${repo.uri}' }
+        }
+        configurations { compile }
+        dependencies {
+            compile 'group:projectA:1.+'
+        }
+
+        task retrieve(type: Sync) {
+            into 'libs'
+            from configurations.compile
+        }
+        """
+
+        when:
+        repo.getModuleMetaData("group", "projectA").expectGet()
+        projectA.pom.expectGetBroken()
+
+        and:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasCause("Could not resolve group:projectA:1.+.")
+        failure.assertHasCause("Could not GET '${projectA.pom.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGetBroken()
+
+        and:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasCause("Could not download projectA.jar (group:projectA:1.1)")
+        failure.assertHasCause("Could not GET '${projectA.artifact.uri}'. Received status code 500 from server: broken")
+
+        when:
+        server.resetExpectations()
+        projectA.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.1.jar')
+    }
+
+    def "dynamic version reports and recovers from missing module"() {
+        given:
+        def repo = mavenHttpRepo("repo1")
+        def projectA = repo.module('group', 'projectA', '1.1').publish()
+
+        buildFile << """
+        repositories {
+            maven { url '${repo.uri}' }
+        }
+        configurations { compile }
+        dependencies {
+            compile 'group:projectA:1.+'
+        }
+
+        task retrieve(type: Sync) {
+            into 'libs'
+            from configurations.compile
+        }
+        """
+
+        when:
+        repo.getModuleMetaData("group", "projectA").expectGet()
+        projectA.pom.expectGetMissing()
+        projectA.artifact.expectHeadMissing()
+
+        and:
+        fails 'retrieve'
+
+        then:
+        // TODO - this error message isn't right: it found a version, it just happened to be missing. should really choose another version
+        failure.assertHasCause("""Could not find any matches for group:projectA:1.+ as no versions of group:projectA are available.
+Searched in the following locations:
+    ${repo.getModuleMetaData("group", "projectA").uri}
+    ${projectA.pom.uri}
+    ${projectA.artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGetMissing()
+
+        and:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasCause("""Could not find projectA.jar (group:projectA:1.1).
+Searched in the following locations:
+    ${projectA.artifact.uri}""")
+
+        when:
+        server.resetExpectations()
+        repo.getModuleMetaData("group", "projectA").expectGet()
+        projectA.pom.expectHead()
+        projectA.artifact.expectGet()
+
+        and:
+        args("--refresh-dependencies")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.1.jar')
+    }
+
+    def "dynamic version ignores broken module in one repository when available in another repository"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+        def projectA1 = repo1.module('group', 'projectA', '1.1').publish()
+        def projectA2 = repo2.module('group', 'projectA', '1.5').publish()
+
+        buildFile << """
+        repositories {
+            maven { url '${repo1.uri}' }
+            maven { url '${repo2.uri}' }
+        }
+        configurations { compile }
+        dependencies {
+            compile 'group:projectA:1.+'
+        }
+
+        task retrieve(type: Sync) {
+            into 'libs'
+            from configurations.compile
+        }
+        """
+
+        when:
+        repo1.getModuleMetaData("group", "projectA").expectGet()
+        projectA1.pom.expectGet()
+        projectA1.getArtifact().expectGet()
+
+        repo2.getModuleMetaData("group", "projectA").expectGet()
+        projectA2.pom.expectGetBroken()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.1.jar')
+
+        when:
+        server.resetExpectations()
+        projectA2.pom.expectGet()
+        projectA2.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.5.jar')
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenFileRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenFileRepoResolveIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenFileRepoResolveIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenFileRepoResolveIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..fa8f653
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpRepoResolveIntegrationTest.groovy
@@ -0,0 +1,391 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
+import org.gradle.test.fixtures.encoding.Identifier
+import org.junit.Rule
+import spock.lang.Unroll
+
+class MavenHttpRepoResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
+
+    def "can resolve and cache dependencies from HTTP Maven repository"() {
+        given:
+        def projectB = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
+        def projectA = mavenHttpRepo.module('group', 'projectA').dependsOn('group', 'projectB', '1.0').publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+    }
+}
+dependencies {
+    compile 'group:projectA:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+        projectB.pom.expectGet()
+        projectB.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar')
+        def snapshot = file('libs/projectA-1.0.jar').snapshot()
+
+        and:
+        progressLogging.downloadProgressLogged(projectA.pom.uri)
+        progressLogging.downloadProgressLogged(projectA.artifact.uri)
+        progressLogging.downloadProgressLogged(projectB.pom.uri)
+        progressLogging.downloadProgressLogged(projectB.artifact.uri)
+
+        when:
+        server.resetExpectations()
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
+    }
+
+    @Unroll
+    def "can resolve with GAV containing #identifier characters"() {
+        def value = identifier.safeForFileName().decorate("name")
+
+        given:
+        def projectB = mavenHttpRepo.module(value, value, value).publish()
+        def projectA = mavenHttpRepo.module('group', 'projectA').dependsOn(value, value, value).publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+    }
+}
+dependencies {
+    compile 'group:projectA:1.0'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+        projectB.pom.expectGet()
+        projectB.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0.jar', "${value}-${value}.jar")
+
+        and:
+        progressLogging.downloadProgressLogged(projectA.pom.uri)
+        progressLogging.downloadProgressLogged(projectA.artifact.uri)
+        progressLogging.downloadProgressLogged(projectB.pom.uri)
+        progressLogging.downloadProgressLogged(projectB.artifact.uri)
+
+        where:
+        identifier << Identifier.all
+    }
+
+    def "can resolve and cache artifact-only dependencies from an HTTP Maven repository"() {
+        given:
+        def projectA = mavenHttpRepo.module('group', 'projectA', '1.2')
+        projectA.dependsOn('group', 'projectC', '1.2')
+        projectA.publish()
+        def projectB = mavenHttpRepo.module('group', 'projectB', '1.2')
+        projectB.artifact(classifier: 'classy')
+        projectB.dependsOn('group', 'projectC', '1.2')
+        projectB.publish()
+
+        and:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+    }
+}
+dependencies {
+    compile 'group:projectA:1.2 at jar'
+    compile 'group:projectB:1.2:classy at jar'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-1.2-classy.jar']
+}
+"""
+
+        when:
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+        projectB.pom.expectGet()
+        projectB.artifact(classifier: 'classy').expectGet()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+        // No extra calls for cached dependencies
+
+        then:
+        succeeds('listJars')
+    }
+
+    def "can resolve and cache artifact-only dependencies with no pom from an HTTP Maven repository"() {
+        given:
+        def projectA = mavenHttpRepo.module('group', 'projectA', '1.2')
+        projectA.dependsOn('group', 'projectC', '1.2')
+        projectA.publish()
+        def projectB = mavenHttpRepo.module('group', 'projectB', '1.2')
+        projectB.artifact(classifier: 'classy')
+        projectB.dependsOn('group', 'projectC', '1.2')
+        projectB.publish()
+
+        and:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+    }
+}
+dependencies {
+    compile 'group:projectA:1.2 at jar'
+    compile 'group:projectB:1.2:classy at jar'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.2.jar', 'projectB-1.2-classy.jar']
+}
+"""
+
+        when:
+        projectA.pom.expectGetMissing()
+        projectA.artifact.expectHead()
+        projectA.artifact.expectGet()
+        projectB.pom.expectGetMissing()
+        projectB.artifact(classifier: 'classy').expectHead()
+        projectB.artifact(classifier: 'classy').expectGet()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+        // No extra calls for cached dependencies
+
+        then:
+        succeeds('listJars')
+    }
+
+    def "can resolve and cache dependencies from multiple HTTP Maven repositories"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        buildFile << """
+repositories {
+    maven { url '${repo1.uri}' }
+    maven { url '${repo2.uri}' }
+}
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+    }
+}
+dependencies {
+    compile 'group:projectA:1.0', 'group:projectB:1.0'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar']
+}
+"""
+
+        def projectA = repo1.module('group', 'projectA').publish()
+        def missingProjectB = repo1.module('group', 'projectB')
+        def projectB = repo2.module('group', 'projectB').publish()
+
+        when:
+        projectA.pom.expectGet()
+
+        // Looks for POM and JAR in repo1 before looking in repo2 (jar is an attempt to handle publication without module descriptor)
+        missingProjectB.pom.expectGetMissing()
+        missingProjectB.artifact.expectHeadMissing()
+        projectB.pom.expectGet()
+
+        projectA.artifact.expectGet()
+        projectB.artifact.expectGet()
+
+        then:
+        succeeds 'listJars'
+
+        when:
+        server.resetExpectations()
+        // No server requests when all jars cached
+
+        then:
+        succeeds 'listJars'
+    }
+
+    def "uses artifactsUrl to resolve artifacts"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        buildFile << """
+repositories {
+    maven {
+        url '${repo1.uri}'
+        artifactUrls '${repo2.uri}'
+    }
+}
+configurations { compile }
+dependencies {
+    compile 'group:projectA:1.0', 'group:projectB:1.0'
+}
+task listJars << {
+    assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar']
+}
+"""
+
+        def projectA = repo1.module('group', 'projectA').publish()
+        def projectB = repo1.module('group', 'projectB').publish()
+        def projectBArtifacts = repo2.module('group', 'projectB').publish()
+
+        when:
+        projectA.pom.expectGet()
+        projectB.pom.expectGet()
+
+        projectA.artifact.expectGet()
+        projectB.artifact.expectGetMissing()
+        projectBArtifacts.artifact.expectGet()
+
+        then:
+        succeeds 'listJars'
+    }
+
+    def "can resolve and cache dependencies from HTTP Maven repository with invalid settings.xml"() {
+        given:
+        def projectB = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
+        def projectA = mavenHttpRepo.module('group', 'projectA').dependsOn('group', 'projectB', '1.0').publish()
+
+        buildFile << """
+    repositories {
+        maven { url '${mavenHttpRepo.uri}' }
+    }
+    configurations {
+        compile {
+            resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+        }
+    }
+    dependencies {
+        compile 'group:projectA:1.0'
+    }
+
+    task retrieve(type: Sync) {
+        into 'libs'
+        from configurations.compile
+    }
+    """
+
+        def m2Home = file("M2_REPO")
+        def settingsFile = m2Home.file("conf/settings.xml")
+        settingsFile << "invalid content... blabla"
+
+        when:
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+        projectB.pom.expectGet()
+        projectB.artifact.expectGet()
+
+        and:
+        executer.withEnvironmentVars(M2_HOME: m2Home.absolutePath)
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar')
+        def snapshot = file('libs/projectA-1.0.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs/projectA-1.0.jar').assertHasNotChangedSince(snapshot)
+    }
+
+    void "fails when configured with AwsCredentials"() {
+        given:
+        mavenHttpRepo.module('group', 'projectA', '1.2').publish()
+
+        and:
+        buildFile << """
+            repositories {
+                maven {
+                    url '${mavenHttpRepo.uri}'
+                    credentials(AwsCredentials) {
+                        accessKey "someKey"
+                        secretKey "someSecret"
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+
+        fails 'retrieve'
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Credentials must be an instance of: org.gradle.api.artifacts.repositories.PasswordCredentials')
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpsRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpsRepoResolveIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpsRepoResolveIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenHttpsRepoResolveIntegrationTest.groovy
diff --git a/subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy
rename to subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJcenterDependencyResolveIntegrationTest.groovy
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..5ed3aec
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.JvmLibraryArtifactResolveTestFixture
+import org.gradle.test.fixtures.maven.MavenRepository
+import spock.lang.Unroll
+
+class MavenJvmLibraryArtifactResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def repo = mavenHttpRepo
+    def fileRepo = mavenRepo
+    def module = repo.module("some.group", "some-artifact", "1.0")
+    def sourceArtifact = module.artifact(classifier: "sources")
+    def javadocArtifact = module.artifact(classifier: "javadoc")
+    JvmLibraryArtifactResolveTestFixture fixture
+
+    def setup() {
+        initBuild(repo)
+
+        fixture = new JvmLibraryArtifactResolveTestFixture(buildFile)
+
+        module.publish()
+    }
+
+    def initBuild(MavenRepository repo) {
+        buildFile.text = """
+repositories {
+    maven { url '$repo.uri' }
+}
+"""
+    }
+
+    def "resolves and caches source artifacts"() {
+        fixture.requestingSource()
+                .expectSourceArtifact("sources")
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolve javadoc artifacts"() {
+        fixture.requestingJavadoc()
+                .expectJavadocArtifact("javadoc")
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        javadocArtifact.expectHead()
+        javadocArtifact.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolves and caches all artifacts"() {
+        fixture.expectSourceArtifact("sources")
+                .expectJavadocArtifact("javadoc")
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+        javadocArtifact.expectHead()
+        javadocArtifact.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "fetches missing snapshot artifacts #condition"() {
+        buildFile << """
+if (project.hasProperty('nocache')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+"""
+
+        def snapshotModule = repo.module("some.group", "some-artifact", "1.0-SNAPSHOT")
+        def snapshotSources = snapshotModule.artifact(classifier: "sources")
+        snapshotModule.publish()
+
+        fixture.withComponentVersion("some.group", "some-artifact", "1.0-SNAPSHOT")
+                .requestingSource()
+                .prepare()
+
+        when:
+        snapshotModule.metaData.expectGet()
+        snapshotModule.pom.expectGet()
+        snapshotSources.expectHeadMissing()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        when:
+        snapshotModule.publishWithChangedContent()
+        fixture.clearExpectations()
+                .expectSourceArtifact("sources")
+                .createVerifyTask("verifyRefresh")
+
+        and:
+        server.resetExpectations()
+        snapshotModule.metaData.expectGet()
+        snapshotModule.pom.expectHead()
+        snapshotModule.pom.sha1.expectGet()
+        snapshotModule.pom.expectGet()
+        snapshotSources.expectHead()
+        snapshotSources.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verifyRefresh")
+
+        where:
+        condition | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when snapshot pom changes" | "-Pnocache"
+    }
+
+    @Unroll
+    def "updates snapshot artifacts #condition"() {
+        buildFile << """
+if (project.hasProperty('nocache')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+"""
+
+        def snapshotModule = repo.module("some.group", "some-artifact", "1.0-SNAPSHOT")
+        def snapshotSources = snapshotModule.artifact(classifier: "sources")
+        snapshotModule.publish()
+
+        fixture.withComponentVersion("some.group", "some-artifact", "1.0-SNAPSHOT")
+                .requestingSource()
+                .expectSourceArtifact("sources")
+                .prepare()
+
+        when:
+        snapshotModule.metaData.expectGet()
+        snapshotModule.pom.expectGet()
+        snapshotSources.expectHead()
+        snapshotSources.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        when:
+        def snapshot = file("sources/some-artifact-1.0-SNAPSHOT-sources.jar").snapshot()
+        snapshotModule.publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+        snapshotModule.metaData.expectGet()
+        snapshotModule.pom.expectHead()
+        snapshotModule.pom.sha1.expectGet()
+        snapshotModule.pom.expectGet()
+        snapshotSources.expectHead()
+        // TODO Extra head request should not be required
+        snapshotSources.expectHead()
+        snapshotSources.sha1.expectGet()
+        snapshotSources.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verify")
+        file("sources/some-artifact-1.0-SNAPSHOT-sources.jar").assertHasChangedSince(snapshot)
+
+        where:
+        condition | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when snapshot pom changes" | "-Pnocache"
+    }
+
+    def "reports failure to resolve artifacts of non-existing component"() {
+        fixture.expectComponentNotFound().prepare()
+
+        when:
+        module.pom.expectGetMissing()
+        module.artifact.expectHeadMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${module.pom.uri}
+    ${module.artifact.uri}""")
+
+        when:
+        server.resetExpectations()
+        module.pom.expectGetMissing()
+        module.artifact.expectHeadMissing()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("""Could not find some.group:some-artifact:1.0.
+Searched in the following locations:
+    ${module.pom.uri}
+    ${module.artifact.uri}""")
+    }
+
+    def "resolve and caches missing artifacts of existing component"() {
+        fixture.prepare()
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectHeadMissing()
+        javadocArtifact.expectHeadMissing()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "resolves and caches artifacts where some are present"() {
+        fixture.expectSourceArtifact("sources")
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+        javadocArtifact.expectHeadMissing()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "reports on failure to list artifacts and recovers on subsequent resolve"() {
+        fixture.requestingSource()
+                .expectComponentResolutionFailure()
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        sourceArtifact.expectHeadBroken()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("Could not determine artifacts for some.group:some-artifact:1.0")
+        failure.assertHasCause("Could not HEAD '${sourceArtifact.uri}'. Received status code 500 from server: broken")
+
+        when:
+        fixture.clearExpectations()
+                .expectSourceArtifact("sources")
+                .createVerifyTask("verifyFixed")
+
+        and:
+        server.resetExpectations()
+        sourceArtifact.expectHead()
+        sourceArtifact.expectGet()
+
+        then:
+        succeeds("verifyFixed")
+    }
+
+    def "resolves and recovers from broken artifacts"() {
+        fixture.requestingJavadoc()
+                .expectJavadocArtifactFailure()
+                .prepare()
+
+        when:
+        module.pom.expectGet()
+        javadocArtifact.expectHead()
+        javadocArtifact.expectGetBroken()
+
+        then:
+        fails("verify")
+        failure.assertHasCause("Could not download some-artifact-javadoc.jar (some.group:some-artifact:1.0)")
+        failure.assertHasCause("Could not get resource '${javadocArtifact.uri}'")
+        failure.assertHasCause("Could not GET '${javadocArtifact.uri}'. Received status code 500 from server: broken")
+
+        when:
+        fixture.clearExpectations()
+                .expectJavadocArtifact("javadoc")
+                .createVerifyTask("verifyFixed")
+
+        and:
+        server.resetExpectations()
+        javadocArtifact.expectGet()
+
+        then:
+        succeeds("verifyFixed")
+    }
+
+    def "resolve and does not cache artifacts from local repository"() {
+        initBuild(fileRepo)
+
+        fixture.requestingSource()
+                .expectSourceArtifact("sources")
+                .prepare()
+
+        when:
+        succeeds("verify")
+
+        and:
+        def snapshot = file("sources/some-artifact-1.0-sources.jar").snapshot()
+
+        and:
+        module.publishWithChangedContent()
+
+        then:
+        succeeds("verify")
+        file("sources/some-artifact-1.0-sources.jar").assertHasChangedSince(snapshot)
+    }
+
+    def checkArtifactsResolvedAndCached() {
+        assert succeeds("verify")
+        server.resetExpectations()
+        assert succeeds("verify")
+        true
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy
new file mode 100644
index 0000000..e7e6538
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLatestResolveIntegrationTest.groovy
@@ -0,0 +1,120 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Unroll
+
+class MavenLatestResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def setup(){
+        buildFile << """
+            repositories {
+                maven { url '${mavenRepo().uri}' }
+            }
+            configurations { compile }
+
+            task retrieve(type: Sync) {
+                into 'build'
+                from configurations.compile
+            }
+        """
+    }
+
+    def "latest selector works correctly when no snapshot 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 << " dependencies { compile 'group:projectA:latest.$status' }"
+
+        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 << "dependencies { compile 'group:projectA:latest.foo' }"
+
+        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.")
+    }
+
+    def "snapshot versions are considered integration status when using latest selector"() {
+        given:
+        mavenHttpRepo.getModuleMetaData('group', 'projectA').allowGetOrHead()
+        mavenHttpRepo.module('group', 'projectA', '1.0').publish().allowAll()
+        mavenHttpRepo.module('group', 'projectA', '1.2-SNAPSHOT').publish().allowAll()
+
+        and:
+        buildFile << """
+            repositories { maven { url "${mavenHttpRepo.uri}" } }
+            dependencies { compile 'group:projectA:latest.${status}' }
+        """
+
+        when:
+        run "retrieve"
+
+        then:
+        file("build").assertHasDescendants(latest)
+
+        where:
+        status        | latest
+        "release"     | "projectA-1.0.jar"
+        "milestone"   | "projectA-1.0.jar"
+        "integration" | "projectA-1.2-SNAPSHOT.jar"
+    }
+
+    @Unroll
+    def "can resolve dynamic #versionDefinition version declared in pom as transitive dependency"() {
+        given:
+        mavenRepo().module('org.test', 'projectC', '1.1').publish()
+        mavenRepo().module('org.test', 'projectC', '1.5').publish()
+        mavenRepo().module('org.test', 'projectC', '2.0').publish()
+        // We use a non-unique snapshot here because we are using a file repository, and we want the file name to be projectC-2.1-SNAPSHOT.jar
+        // For a file repository, the resolved artifact name would be the unique snapshot version (since we use artifact in-place)
+        mavenRepo().module('org.test', 'projectC', '2.1-SNAPSHOT').withNonUniqueSnapshots().publish()
+        mavenRepo().module('org.test', 'projectB', '1.0').dependsOn("org.test", 'projectC', versionDefinition).publish()
+        mavenRepo().module('org.test', 'projectA', '1.0').dependsOn('org.test', 'projectB', '1.0').publish()
+
+        buildFile << """
+            dependencies {
+                compile 'org.test:projectA:1.0'
+            }"""
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('build').assertHasDescendants('projectA-1.0.jar', 'projectB-1.0.jar', "projectC-${resolvedVersion}.jar")
+
+        where:
+        versionDefinition | resolvedVersion
+        "RELEASE"         | "2.0"
+        "LATEST"          | "2.1-SNAPSHOT"
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..661cf49
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.test.fixtures.maven.MavenModule
+import spock.lang.Issue
+
+import static org.hamcrest.Matchers.containsString
+
+class MavenLocalRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
+
+    def setup() {
+        buildFile << """
+                repositories {
+                    mavenLocal()
+                }
+                configurations { compile }
+                dependencies {
+                    compile 'group:projectA:1.2'
+                }
+
+                task retrieve(type: Sync) {
+                    from configurations.compile
+                    into 'build'
+                }"""
+    }
+
+    def "can resolve artifacts from local m2 when user settings.xml does not exist"() {
+        given:
+        def moduleA = m2Installation.mavenRepo().module('group', 'projectA', '1.2').publish()
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "can resolve artifacts from local m2 with custom local repository defined in user settings.xml"() {
+        given:
+        def artifactRepo = mavenLocal("artifactrepo")
+        m2Installation.generateUserSettingsFile(artifactRepo)
+        def moduleA = artifactRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "can resolve artifacts from local m2 with custom local repository defined in global settings.xml"() {
+        given:
+        def sysPropRepo = mavenLocal("artifactrepo")
+        m2Installation.generateGlobalSettingsFile(sysPropRepo)
+        def moduleA = sysPropRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "can resolve artifacts from local m2 with custom local repository defined by system-property"() {
+        given:
+        def artifactRepo = mavenLocal("artifactrepo")
+        def moduleA = artifactRepo.module('group', 'projectA', '1.2').publish()
+
+        when:
+        args "-Dmaven.repo.local=${artifactRepo.rootDir.getAbsolutePath()}"
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "local repository in user settings take precedence over the local repository global settings"() {
+        given:
+        def globalRepo = mavenLocal("globalArtifactRepo")
+        def userRepo = mavenLocal("userArtifactRepo")
+        m2Installation.generateGlobalSettingsFile(globalRepo).generateUserSettingsFile(userRepo)
+        def moduleA = userRepo.module('group', 'projectA', '1.2').publish()
+        globalRepo.module('group', 'projectA', '1.2').publishWithChangedContent()
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "local repository in System Property take precedence over the local repository user settings"() {
+        given:
+        def userRepo = mavenLocal("userArtifactRepo")
+        m2Installation.generateUserSettingsFile(userRepo)
+        def sysPropRepo = mavenLocal("artifactrepo")
+        def moduleA = sysPropRepo.module('group', 'projectA', '1.2').publish()
+        userRepo.module('group', 'projectA', '1.2').publishWithChangedContent()
+
+        when:
+        args "-Dmaven.repo.local=${sysPropRepo.rootDir.getAbsolutePath()}"
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    def "fail with meaningful error message if settings.xml is invalid"() {
+        given:
+        m2Installation.userSettingsFile << "invalid content"
+
+        when:
+        runAndFail 'retrieve'
+
+        then:
+        failure.assertThatCause(containsString(String.format("Non-parseable settings %s:", m2Installation.userSettingsFile.absolutePath)));
+    }
+
+    def "mavenLocal is ignored if no local maven repository exists"() {
+        given:
+        def anotherRepo = maven("another-local-repo")
+        def moduleA = anotherRepo.module('group', 'projectA', '1.2').publishWithChangedContent()
+
+        and:
+        buildFile << """
+        repositories{
+            maven { url "${anotherRepo.uri}" }
+        }
+        """
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleA)
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal fails to resolve artifact if contains pom but not artifact"() {
+        given:
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2').publishPom()
+
+        when:
+        runAndFail 'retrieve'
+
+        then:
+        failure.assertHasCause('Could not find group:projectA:1.2')
+    }
+
+    def "mavenLocal reports and recovers from missing module"() {
+        def module = m2Installation.mavenRepo().module('group', 'projectA', '1.2')
+
+        when:
+        runAndFail 'retrieve'
+
+        then:
+        failure.assertHasCause("""Could not find group:projectA:1.2.
+Searched in the following locations:
+    ${module.pomFile.toURL()}
+    ${module.artifactFile.toURL()}
+Required by:
+""")
+
+        when:
+        module.publish()
+
+        then:
+        succeeds 'retrieve'
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal skipped if contains pom but no artifact and there is another repository available"() {
+        given:
+        def anotherRepo = maven("another-local-repo")
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2').publishPom()
+        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2').publish()
+
+        and:
+        buildFile << """
+        repositories{
+            maven { url "${anotherRepo.uri}" }
+        }
+        """
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleARemote)
+    }
+
+    def "mavenLocal includes jar for module with packaging 'pom'"() {
+        given:
+        m2Installation.mavenRepo().module('group', 'projectB', '1.2').publish()
+        def pomModule = m2Installation.mavenRepo().module('group', 'projectA', '1.2')
+        pomModule.packaging = 'pom'
+        pomModule.dependsOn('group', 'projectB', '1.2')
+        pomModule.artifact(type: "jar")
+        pomModule.publish()
+
+        when:
+        run 'retrieve'
+
+        then:
+        def buildDir = file('build')
+        buildDir.assertHasDescendants("projectA-1.2.jar", "projectB-1.2.jar")
+    }
+
+    def "mavenLocal ignores missing jar for module with packaging 'pom'"() {
+        given:
+        m2Installation.mavenRepo().module('group', 'projectB', '1.2').publish()
+        def pomModule = m2Installation.mavenRepo().module('group', 'projectA', '1.2')
+        pomModule.packaging = 'pom'
+        pomModule.dependsOn('group', 'projectB', '1.2')
+        pomModule.publishPom()
+
+        when:
+        run 'retrieve'
+
+        then:
+        def buildDir = file('build')
+        buildDir.assertHasDescendants("projectB-1.2.jar")
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal fails to resolve snapshot artifact if contains pom but not artifact"() {
+        given:
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').publishPom()
+
+        and:
+        buildFile.text = """
+                repositories {
+                    mavenLocal()
+                }
+                configurations { compile }
+                dependencies {
+                    compile 'group:projectA:1.2-SNAPSHOT'
+                }
+
+                task retrieve(type: Sync) {
+                    from configurations.compile
+                    into 'build'
+                }"""
+
+        when:
+        runAndFail 'retrieve'
+
+        then:
+        failure.assertHasCause('Could not find group:projectA:1.2-SNAPSHOT')
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal fails to resolve non-unique snapshot artifact if contains pom but not artifact"() {
+        given:
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publishPom()
+
+        and:
+        buildFile.text = """
+                repositories {
+                    mavenLocal()
+                }
+                configurations { compile }
+                dependencies {
+                    compile 'group:projectA:1.2-SNAPSHOT'
+                }
+
+                task retrieve(type: Sync) {
+                    from configurations.compile
+                    into 'build'
+                }"""
+
+        when:
+        runAndFail 'retrieve'
+
+        then:
+        failure.assertHasCause('Could not find group:projectA:1.2-SNAPSHOT')
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal skipped if contains pom but no artifact for snapshot"() {
+        given:
+        def anotherRepo = maven("another-local-repo")
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').publishPom()
+        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2-SNAPSHOT').publish()
+
+        and:
+        buildFile.text = """
+                repositories {
+                    mavenLocal()
+                    maven { url "${anotherRepo.uri}" }
+                }
+                configurations { compile }
+                dependencies {
+                    compile 'group:projectA:1.2-SNAPSHOT'
+                }
+
+                task retrieve(type: Sync) {
+                    from configurations.compile
+                    into 'build'
+                }
+        """
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleARemote)
+    }
+
+    @Issue('GRADLE-2034')
+    def "mavenLocal skipped if contains pom but no artifact for non-unique snapshot"() {
+        given:
+        def anotherRepo = maven("another-local-repo")
+        m2Installation.mavenRepo().module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publishPom()
+        def moduleARemote = anotherRepo.module('group', 'projectA', '1.2-SNAPSHOT').withNonUniqueSnapshots().publish()
+
+        and:
+        buildFile.text = """
+                repositories {
+                    mavenLocal()
+                    maven { url "${anotherRepo.uri}" }
+                }
+                configurations { compile }
+                dependencies {
+                    compile 'group:projectA:1.2-SNAPSHOT'
+                }
+
+                task retrieve(type: Sync) {
+                    from configurations.compile
+                    into 'build'
+                }
+        """
+
+        when:
+        run 'retrieve'
+
+        then:
+        hasArtifact(moduleARemote)
+    }
+
+    def hasArtifact(MavenModule module) {
+        def buildDir = file('build')
+        def artifactName = module.artifactFile.name
+        buildDir.assertHasDescendants(artifactName)
+        buildDir.file(artifactName).assertIsCopyOf(module.artifactFile)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenModuleArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenModuleArtifactResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..b5fef22
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenModuleArtifactResolutionIntegrationTest.groovy
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.MetadataArtifactResolveTestFixture
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.test.fixtures.maven.MavenRepository
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import spock.lang.Unroll
+
+class MavenModuleArtifactResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    private MetadataArtifactResolveTestFixture fixture
+    def httpRepo = mavenHttpRepo
+
+    def setup() {
+        initBuild(httpRepo)
+        fixture = new MetadataArtifactResolveTestFixture(buildFile)
+        fixture.basicSetup()
+    }
+
+    def initBuild(MavenRepository repo) {
+        buildFile << """
+repositories {
+    maven { url '$repo.uri' }
+}
+"""
+    }
+
+    def "successfully resolve existing Maven module artifact"() {
+        given:
+        MavenHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent('MavenModule').requestArtifact('MavenPomArtifact')
+                .expectResolvedComponentResult().expectMetadataFiles(module.pom.file)
+                .createVerifyTaskModuleComponentIdentifier()
+
+        module.pom.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "invalid component type and artifact type (#reason)"() {
+        given:
+        MavenHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent(component).requestArtifact(artifactType)
+                .expectUnresolvedComponentResult(exception)
+                .expectNoMetadataFiles()
+                .createVerifyTaskModuleComponentIdentifier()
+        module.pom.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        where:
+        component     | artifactType            | reason                                                                           | exception
+        'MavenModule' | 'IvyDescriptorArtifact' | 'cannot mix MavenModule with Maven metadata artifact type IvyDescriptorArtifact' | new IllegalArgumentException('Artifact type org.gradle.ivy.IvyDescriptorArtifact is not registered for component type org.gradle.maven.MavenModule.')
+        'IvyModule'   | 'IvyDescriptorArtifact' | 'cannot retrieve Ivy component and metadata artifact for Maven module'           | new ArtifactResolveException("Could not determine artifacts for some.group:some-artifact:1.0: Cannot locate 'ivy descriptor' artifacts for 'some.group:some-artifact:1.0' in repository 'maven'")
+    }
+
+    def "requesting MavenModule for a project component"() {
+        MavenHttpModule module = publishModule()
+
+        when:
+        fixture.requestComponent('MavenModule').requestArtifact('MavenPomArtifact')
+               .expectUnresolvedComponentResult(new IllegalArgumentException("Cannot query artifacts for a project component (project :)"))
+               .expectNoMetadataFiles()
+               .createVerifyTaskForProjectComponentIdentifier()
+
+        module.pom.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    def "request an Maven POM for a Maven module with no metadata"() {
+        given:
+        MavenHttpModule module = publishModuleWithoutMetadata()
+
+        when:
+        fixture.requestComponent('MavenModule').requestArtifact('MavenPomArtifact')
+               .expectResolvedComponentResult()
+               .expectNoMetadataFiles()
+               .expectUnresolvedArtifactResult(ArtifactResolveException, "Could not find some-artifact.pom (some.group:some-artifact:1.0).")
+               .createVerifyTaskModuleComponentIdentifier()
+
+        // TODO - should make a single request
+        module.pom.expectGetMissing()
+        module.pom.expectGetMissing()
+        module.artifact.expectHead()
+
+        then:
+        checkArtifactsResolvedAndCached()
+    }
+
+    @Unroll
+    def "updates artifacts for module #condition"() {
+        given:
+        MavenHttpModule module = publishModule()
+
+        fixture.configureChangingModule()
+        fixture.requestComponent('MavenModule').requestArtifact('MavenPomArtifact')
+               .expectResolvedComponentResult().expectMetadataFiles(module.pomFile)
+               .createVerifyTaskModuleComponentIdentifier()
+
+        when:
+        module.pom.expectGet()
+
+        then:
+        checkArtifactsResolvedAndCached()
+
+        when:
+        module.publishWithChangedContent()
+
+        and:
+        server.resetExpectations()
+        module.pom.expectHead()
+        module.pom.sha1.expectGet()
+        module.pom.expectGet()
+
+        then:
+        executer.withArgument(execArg)
+        succeeds("verify")
+
+        where:
+        condition                     | execArg
+        "with --refresh-dependencies" | "--refresh-dependencies"
+        "when maven pom changes"      | "-Pnocache"
+    }
+
+    private MavenHttpModule publishModule() {
+        def module = createModule()
+        module.publish()
+    }
+
+    private MavenHttpModule publishModuleWithoutMetadata() {
+        MavenHttpModule module = createModule()
+        module.withNoMetaData()
+        module.publish()
+    }
+
+    private MavenHttpModule createModule() {
+        httpRepo.module(fixture.id.group, fixture.id.module, fixture.id.version)
+    }
+
+    def checkArtifactsResolvedAndCached() {
+        assert succeeds('verify')
+        server.resetExpectations()
+        assert succeeds('verify')
+        true
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
new file mode 100644
index 0000000..d8c6346
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenParentPomResolveIntegrationTest.groovy
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Issue
+
+class MavenParentPomResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    def "includes dependencies from parent pom"() {
+        given:
+        def parentDep = mavenHttpRepo.module("org", "parent_dep", "1.2").publish()
+        def childDep = mavenHttpRepo.module("org", "child_dep", "1.7").publish()
+
+        def parent = mavenHttpRepo.module("org", "parent", "1.0")
+        parent.hasPackaging('pom')
+        parent.dependsOn("org", "parent_dep", "1.2")
+        parent.publish()
+
+        def child = mavenHttpRepo.module("org", "child", "1.0")
+        child.dependsOn("org", "child_dep", "1.7")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        child.pom.expectGet()
+        parent.pom.expectGet()
+
+        child.artifact.expectGet()
+
+        parentDep.pom.expectGet()
+        parentDep.artifact.expectGet()
+        childDep.pom.expectGet()
+        childDep.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        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")
+    def "can handle parent pom with SNAPSHOT version"() {
+        given:
+        def parent = mavenHttpRepo.module("org", "parent", "1.0-SNAPSHOT")
+        parent.hasPackaging('pom')
+        parent.publish()
+
+        def child = mavenHttpRepo.module("org", "child", "1.0")
+        child.parent("org", "parent", "1.0-SNAPSHOT")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        child.pom.expectGet()
+        parent.metaData.expectGet()
+        parent.pom.expectGet()
+
+        child.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('child-1.0.jar')
+    }
+
+    def "looks for parent pom in different repository"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        def parentInRepo1 = repo1.module("org", "parent")
+
+        def parentInRepo2 = repo2.module("org", "parent")
+        parentInRepo2.hasPackaging('pom')
+        parentInRepo2.publish()
+
+        def child = repo1.module("org", "child")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${repo1.uri}' }
+    maven { url '${repo2.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        child.pom.expectGet()
+        child.artifact.expectGet()
+
+        parentInRepo1.pom.expectGetMissing()
+        parentInRepo1.artifact.expectHeadMissing()
+
+        parentInRepo2.pom.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('child-1.0.jar')
+    }
+
+    def "fails with reasonable message if parent module is an ivy module"() {
+        given:
+        def child = mavenHttpRepo.module("org", "child")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        def missingChild = ivyHttpRepo.module("org", "child")
+        def parent = ivyHttpRepo.module("org", "parent").publish()
+
+        buildFile << """
+repositories {
+    ivy { url '${ivyHttpRepo.uri}' }
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies { compile 'org:child:1.0' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        missingChild.ivy.expectGetMissing()
+        missingChild.jar.expectHeadMissing()
+        child.pom.expectGet()
+        parent.ivy.expectGet()
+
+        and:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasCause "Could not resolve org:child:1.0."
+        failure.assertHasCause "Could not determine artifacts for org:parent:1.0: Cannot locate 'maven pom' artifacts for 'org:parent:1.0' in repository 'ivy'"
+    }
+
+    def "uses cached parent pom located in a different repository"() {
+        given:
+        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()
+
+        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')
+    }
+
+    @Issue("GRADLE-2861")
+    def "two modules that share common parent"() {
+        given:
+        def parent = mavenHttpRepo.module("org", "parent", "1.0")
+        parent.hasPackaging('pom')
+        parent.publish()
+
+        def child1 = mavenHttpRepo.module("org", "child1", "1.0")
+        child1.parent("org", "parent", "1.0")
+        child1.publish()
+        def child2 = mavenHttpRepo.module("org", "child2", "1.0")
+        child2.parent("org", "parent", "1.0")
+        child2.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org:child1:1.0'
+    compile 'org:child2:1.0'
+}
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        child1.pom.expectGet()
+        child2.pom.expectGet()
+        parent.pom.expectGet()
+
+        child1.artifact.expectGet()
+        child2.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('child1-1.0.jar', 'child2-1.0.jar')
+    }
+
+    @Issue("GRADLE-2861")
+    def "module that has a parent and grandparent module"() {
+        given:
+        def grandParent = mavenHttpRepo.module("org", "grandparent", "1.0")
+        grandParent.hasPackaging('pom')
+        grandParent.publish()
+
+        def parent = mavenHttpRepo.module("org", "parent", "1.0")
+        parent.hasPackaging('pom')
+        parent.parent("org", "grandparent", "1.0")
+        parent.publish()
+
+        def child = mavenHttpRepo.module("org", "child", "1.0")
+        child.parent("org", "parent", "1.0")
+        child.publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations { compile }
+dependencies {
+    compile 'org:child:1.0'
+}
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        child.pom.expectGet()
+        parent.pom.expectGet()
+        grandParent.pom.expectGet()
+
+        child.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('child-1.0.jar')
+    }
+
+    def "parent pom parsing with custom properties for dependency coordinates"() {
+        given:
+        def parent = mavenHttpRepo.module('group', 'parent', '1.0').publish()
+        parent.pomFile.text = parent.pomFile.text.replace("</project>", """
+<properties>
+    <some.group>my.group</some.group>
+    <some.artifact>myartifact</some.artifact>
+    <some.version>1.1</some.version>
+</properties>
+<dependencies>
+    <dependency>
+        <groupId>\${some.group}</groupId>
+        <artifactId>\${some.artifact}</artifactId>
+        <version>\${some.version}</version>
+    </dependency>
+</dependencies>
+</project>
+""")
+
+        def parentDepModule = mavenHttpRepo.module('my.group', 'myartifact', '1.1').publish()
+        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', 'myartifact-1.1.jar'] }
+        """
+
+        and:
+        parent.pom.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+        parentDepModule.pom.expectGet()
+        parentDepModule.artifact.expectGet()
+
+        expect:
+        // have to run twice to trigger the failure, to parse the descriptor from the cache
+        succeeds ":libs"
+        succeeds ":libs"
+    }
+
+    def "dependency with same group ID and artifact ID defined in child and parent is used from child"() {
+        given:
+        def parent = mavenHttpRepo.module('group', 'parent', '1.0').dependsOn('my.group', 'myartifact', '1.1').publish()
+        mavenHttpRepo.module('my.group', 'myartifact', '1.1').publish()
+        def depModule = mavenHttpRepo.module('my.group', 'myartifact', '1.3').publish()
+        def module = mavenHttpRepo.module('group', 'artifact', '1.0').parent('group', 'parent', '1.0').dependsOn('my.group', 'myartifact', '1.3').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', 'myartifact-1.3.jar'] }
+        """
+
+        and:
+        parent.pom.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+        depModule.pom.expectGet()
+        depModule.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/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomExcludeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..a9f9b74
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomExcludeResolveIntegrationTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import spock.lang.Issue
+
+class MavenPomExcludeResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    @Issue("https://issues.gradle.org/browse/GRADLE-3243")
+    def "Wildcard exclude of groupId and artifactId does not exclude itself"() {
+        given:
+        mavenHttpRepo.module("org.gradle", "bar", "2.0").publish()
+        def childDep = mavenHttpRepo.module("com.company", "foo", "1.5")
+        childDep.dependsOn("org.gradle", "bar", "2.0")
+        childDep.publish()
+        def parentDep = mavenHttpRepo.module("groupA", "projectA", "1.2")
+        parentDep.dependsOn("com.company", "foo", "1.5")
+        parentDep.publish()
+        parentDep.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>projectA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>com.company</groupId>
+            <artifactId>foo</artifactId>
+            <version>1.5</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:projectA:1.2' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        and:
+        parentDep.pom.expectGet()
+        parentDep.artifact.expectGet()
+        childDep.pom.expectGet()
+        childDep.artifact.expectGet()
+
+        when:
+        run "retrieve"
+
+        then:
+        file("libs").assertHasDescendants("projectA-1.2.jar", "foo-1.5.jar")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomPackagingResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomPackagingResolveIntegrationTest.groovy
new file mode 100644
index 0000000..b3605f1
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomPackagingResolveIntegrationTest.groovy
@@ -0,0 +1,360 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import spock.lang.Issue
+
+class MavenPomPackagingResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    MavenHttpRepository repo1
+    MavenHttpRepository repo2
+    MavenHttpModule projectARepo1
+    MavenHttpModule projectARepo2
+
+    public setup() {
+        repo1 = mavenHttpRepo("repo1")
+        repo2 = mavenHttpRepo("repo2")
+        projectARepo1 = repo1.module('group', 'projectA')
+        projectARepo2 = repo2.module('group', 'projectA')
+    }
+
+    private void buildWithDependencies(def dependencies) {
+        buildFile << """
+repositories {
+    maven { url '${repo1.uri}' }
+    maven { url '${repo2.uri}' }
+}
+configurations { compile }
+dependencies {
+    $dependencies
+}
+task deleteDir(type: Delete) {
+    delete 'libs'
+}
+task retrieve(type: Copy, dependsOn: deleteDir) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+    }
+
+    def "includes jar artifact if present for pom with packaging of type 'pom'"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo2.hasPackaging("pom").publish()
+
+        and:
+        // First attempts to resolve in repo1
+        projectARepo1.pom.expectGetMissing()
+        projectARepo1.artifact.expectHeadMissing()
+
+        projectARepo2.pom.expectGet()
+        projectARepo2.artifact.expectHead()
+        projectARepo2.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0.jar')
+
+        when:
+        server.resetExpectations()
+        run 'retrieve'
+
+        then: // Uses cached artifacts
+        file('libs').assertHasDescendants('projectA-1.0.jar')
+    }
+
+    def "ignores missing jar artifact for pom with packaging of type 'pom'"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo1.hasPackaging("pom").publishPom()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact.expectHeadMissing()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertIsEmptyDir()
+
+        when:
+        server.resetExpectations()
+        run 'retrieve'
+
+        then: // Uses cached artifacts
+        file('libs').assertIsEmptyDir()
+    }
+
+    def "for a snapshot module with packaging of type 'pom', will check for jar artifact that was previously missing on cache expiry"() {
+        when:
+        def snapshotA = repo1.module('group', 'projectA', '1.1-SNAPSHOT')
+        snapshotA.hasPackaging("pom").publish()
+
+        and:
+        buildWithDependencies("compile 'group:projectA:1.1-SNAPSHOT'")
+        buildFile << """
+if (project.hasProperty('skipCache')) {
+    configurations.compile.resolutionStrategy.cacheChangingModulesFor(0, 'seconds')
+}
+"""
+
+        and:
+        snapshotA.metaData.expectGet()
+        snapshotA.pom.expectGet()
+        snapshotA.artifact.expectHeadMissing()
+
+        and:
+        run 'retrieve'
+
+        then:
+        skipped ':retrieve'
+
+        // Jar artifact presence is cached
+        when:
+        server.resetExpectations()
+
+        and:
+        run 'retrieve'
+
+        then:
+        skipped ':retrieve'
+
+        // New artifact is detected
+        when:
+        server.resetExpectations()
+        snapshotA.metaData.expectGet()
+        snapshotA.pom.expectHead()
+        snapshotA.artifact.expectHead()
+        snapshotA.artifact.expectGet()
+
+        and:
+        executer.withArgument('-PskipCache')
+        run 'retrieve'
+
+        then:
+        executed ':retrieve'
+        file('libs').assertHasDescendants('projectA-1.1-SNAPSHOT.jar')
+
+        // Jar artifact removal is detected
+        when:
+        server.resetExpectations()
+        snapshotA.metaData.expectGet()
+        snapshotA.pom.expectHead()
+        snapshotA.artifact.expectHeadMissing()
+
+        and:
+        executer.withArgument('-PskipCache')
+        run 'retrieve'
+
+        then:
+        skipped ':retrieve'
+    }
+
+    def "will use jar artifact for pom with packaging that maps to jar"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo1.hasPackaging(packaging).publish()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.jar')
+        file('libs/projectA-1.0.jar').assertIsCopyOf(projectARepo1.artifactFile)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+
+        where:
+        packaging << ['', 'jar', 'eclipse-plugin', 'bundle']
+    }
+
+
+    @Issue('GRADLE-2188')
+    def "will use jar artifact for pom with packaging 'orbit'"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo1.hasPackaging('orbit').publish()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact(type: 'orbit').expectHeadMissing()
+        projectARepo1.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.jar')
+        file('libs/projectA-1.0.jar').assertIsCopyOf(projectARepo1.artifactFile)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+    }
+
+    @Issue('GRADLE-2188')
+    def "where 'module.custom' exists, will use it as main artifact for pom with packaging 'custom'"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo1.hasPackaging("custom").hasType("custom").publish()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact.expectHead()
+        projectARepo1.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.custom')
+        file('libs/projectA-1.0.custom').assertIsCopyOf(projectARepo1.artifactFile)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+    }
+
+    def "fails and reports type-based location if neither packaging-based or type-based artifact can be located"() {
+        when:
+        buildWithDependencies("compile 'group:projectA:1.0'")
+        projectARepo1.hasPackaging("custom").publishPom()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact(type: 'custom').expectHeadMissing()
+        projectARepo1.artifact(type: 'jar').expectGetMissing()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        // TODO - should report both locations as missing
+        failure.assertHasCause("Could not find projectA.jar (group:projectA:1.0).")
+    }
+
+    def "will use non-jar dependency type to determine jar artifact location"() {
+        when:
+        buildWithDependencies("""
+compile('group:projectA:1.0') {
+    artifact {
+        name = 'projectA'
+        type = 'zip'
+    }
+}
+""")
+        projectARepo1.hasPackaging("custom").hasType("custom")
+        projectARepo1.artifact(type: 'zip')
+        projectARepo1.publish()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact(type: 'zip').expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.zip')
+        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifact(type: 'zip').file)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+    }
+
+    def "will use non-jar maven dependency type to determine artifact location"() {
+        when:
+        buildWithDependencies("""
+compile 'group:mavenProject:1.0'
+""")
+        def mavenProject = repo1.module('group', 'mavenProject', '1.0').hasPackaging('pom').dependsOn('group', 'projectA', '1.0', 'zip').publishPom()
+        projectARepo1.hasPackaging("custom")
+        projectARepo1.artifact(type: 'zip')
+        projectARepo1.publish()
+
+        and:
+        mavenProject.pom.expectGet()
+        mavenProject.artifact.expectHeadMissing()
+
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact(type: 'zip').expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.zip')
+        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifact(type: 'zip').file)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+    }
+
+    def "will use dependency type to locate artifact, even when custom packaging matches artifact type"() {
+        when:
+        buildWithDependencies("""
+compile('group:projectA:1.0') {
+    artifact {
+        name = 'projectA'
+        type = 'zip'
+    }
+}
+""")
+        projectARepo1.hasPackaging("zip").hasType("zip").publish()
+
+        and:
+        projectARepo1.pom.expectGet()
+        projectARepo1.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('projectA-1.0.zip')
+        file('libs/projectA-1.0.zip').assertIsCopyOf(projectARepo1.artifactFile)
+
+        // Check caching
+        when:
+        server.resetExpectations()
+        then:
+        succeeds 'retrieve'
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
new file mode 100644
index 0000000..1aea8d4
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
@@ -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.integtests.resolve.maven
+
+import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+
+class MavenPomResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    def "follows relocation to another group"() {
+        given:
+        def original = mavenHttpRepo.module("groupA", "projectA", "1.2").publishPom()
+        original.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>projectA</artifactId>
+    <version>1.2</version>
+    <distributionManagement>
+        <relocation>
+            <groupId>newGroupA</groupId>
+        </relocation>
+    </distributionManagement>
+</project>
+"""
+
+        def newModule = mavenHttpRepo.module("newGroupA", "projectA", "1.2").publish()
+        newModule.publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:projectA:1.2' }
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        and:
+        original.pom.expectGet()
+        original.artifact.expectHead()
+        newModule.pom.expectGet()
+        newModule.artifact.expectGet()
+
+        when:
+        run "retrieve"
+
+        then:
+        file("libs").assertHasDescendants("projectA-1.2.jar")
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy
new file mode 100644
index 0000000..15c9049
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenProfileResolveIntegrationTest.groovy
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.resolve.ResolveTestFixture
+import org.gradle.internal.id.UUIDGenerator
+
+class MavenProfileResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    ResolveTestFixture resolve
+
+    def setup() {
+        settingsFile << "rootProject.name = 'test' "
+        resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+    }
+
+    def "uses properties from active profile to resolve dependency"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>groupB</groupId.prop>
+                <artifactId.prop>artifactB</artifactId.prop>
+                <version.prop>1.4</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+
+    def "uses dependency management defaults from active profile to resolve dependency"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>groupB</groupId>
+                        <artifactId>artifactB</artifactId>
+                        <version>1.4</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+
+    def "resolves dependency from active profile"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+            <version>1.3</version>
+        </dependency>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+            <version>1.7</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>groupB</groupId>
+                    <artifactId>artifactB</artifactId>
+                    <version>1.4</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+
+    def "uses properties from profile activated by the absence of a property"() {
+        given:
+        String customPropertyName = new UUIDGenerator().generateId()
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <properties>
+                <groupId.prop>groupB</groupId.prop>
+                <artifactId.prop>artifactB</artifactId.prop>
+                <version.prop>1.4</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+
+    def "uses properties from profile activated by absence of system property over active by default to resolve dependency"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>groupB</groupId.prop>
+                <artifactId.prop>artifactB</artifactId.prop>
+                <version.prop>1.4</version.prop>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <properties>
+                <groupId.prop>groupB</groupId.prop>
+                <artifactId.prop>artifactB</artifactId.prop>
+                <version.prop>1.5</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.5").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.5")
+                }
+            }
+        }
+    }
+
+    def "uses dependency management defaults from profile activated by absence of system property to resolve dependency"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>groupB</groupId>
+                        <artifactId>artifactB</artifactId>
+                        <version>1.4</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+
+    def "resolves dependency from profile activated by absence of system property"() {
+        given:
+        def requestedModule = mavenHttpRepo.module("groupA", "artifactA", "1.2").publish()
+        requestedModule.pomFile.text = """
+<project>
+    <groupId>groupA</groupId>
+    <artifactId>artifactA</artifactId>
+    <version>1.2</version>
+    <dependencies>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+            <version>1.3</version>
+        </dependency>
+        <dependency>
+            <groupId>groupB</groupId>
+            <artifactId>artifactB</artifactId>
+            <version>1.7</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>groupB</groupId>
+                    <artifactId>artifactB</artifactId>
+                    <version>1.4</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def transitiveModule = mavenHttpRepo.module("groupB", "artifactB", "1.4").publish()
+
+        and:
+        buildFile << """
+repositories { maven { url '${mavenHttpRepo.uri}' } }
+configurations { compile }
+dependencies { compile 'groupA:artifactA:1.2' }
+"""
+
+        and:
+        requestedModule.pom.expectGet()
+        requestedModule.artifact.expectGet()
+        transitiveModule.pom.expectGet()
+        transitiveModule.artifact.expectGet()
+
+        when:
+        run "checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:unspecified") {
+                module("groupA:artifactA:1.2") {
+                    module("groupB:artifactB:1.4")
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
new file mode 100644
index 0000000..c1f280e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
@@ -0,0 +1,997 @@
+/*
+ * 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.AbstractHttpDependencyResolutionTest
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import spock.lang.Ignore
+import spock.lang.Issue
+
+class MavenSnapshotResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+
+    def "can find and cache snapshots in multiple Maven HTTP repositories"() {
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        given:
+        buildFile << """
+repositories {
+    maven { url "${repo1.uri}" }
+    maven { url "${repo2.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT"
+    compile "org.gradle.integtests.resolve:projectB:1.0-SNAPSHOT"
+    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        and: "snapshot modules are published"
+        def repo1ProjectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT").publish()
+        def repo1ProjectB = repo1.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT")
+        def repo2ProjectB = repo2.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
+        def repo1NonUnique = repo1.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots()
+        def repo2NonUnique = repo2.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
+
+        when: "Server provides projectA from repo1"
+        expectModuleServed(repo1ProjectA)
+
+        and: "Server provides projectB from repo2"
+        expectModuleMissing(repo1ProjectB)
+        expectModuleServed(repo2ProjectB)
+
+        and: "Server provides nonunique snapshot from repo2"
+        expectModuleMissing(repo1NonUnique)
+        expectModuleServed(repo2NonUnique)
+
+        and: "We resolve dependencies"
+        run 'retrieve'
+
+        then: "Snapshots are downloaded"
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
+        def snapshotA = file('libs/projectA-1.0-SNAPSHOT.jar').snapshot()
+        def snapshotNonUnique = file('libs/nonunique-1.0-SNAPSHOT.jar').snapshot()
+
+        when: "We resolve with snapshots cached: no server requests"
+        server.resetExpectations()
+        def result = run('retrieve')
+
+        then: "Everything is up to date"
+        result.assertTaskSkipped(':retrieve')
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotA);
+        file('libs/nonunique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotNonUnique);
+    }
+
+    def "can find and cache snapshots in Maven HTTP repository with additional artifact urls"() {
+        def repo1 = mavenHttpRepo("repo1")
+        def repo2 = mavenHttpRepo("repo2")
+
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${repo1.uri}"
+        artifactUrls "${repo2.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT"
+    compile "org.gradle.integtests.resolve:projectB:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        and: "snapshot modules are published"
+        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT").publish()
+        def repo1ProjectB = repo1.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
+        def repo2ProjectB = repo2.module("org.gradle.integtests.resolve", "projectB", "1.0-SNAPSHOT").publish()
+
+        when: "Server provides projectA from repo1"
+        expectModuleServed(projectA)
+
+        and: "Server provides projectB with artifact in repo2"
+        repo1ProjectB.metaData.expectGet()
+        repo1ProjectB.pom.expectGet()
+        repo1ProjectB.artifact.expectGetMissing()
+        repo2ProjectB.artifact.expectGet()
+
+        and: "We resolve dependencies"
+        run 'retrieve'
+
+        then: "Snapshots are downloaded"
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0-SNAPSHOT.jar')
+        def snapshotA = file('libs/projectA-1.0-SNAPSHOT.jar').snapshot()
+        def snapshotB = file('libs/projectB-1.0-SNAPSHOT.jar').snapshot()
+
+        when: "We resolve with snapshots cached: no server requests"
+        server.resetExpectations()
+        def result = run('retrieve')
+
+        then: "Everything is up to date"
+        result.assertTaskSkipped(':retrieve')
+        file('libs/projectA-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotA);
+        file('libs/projectB-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshotB);
+    }
+
+    def "can find and cache snapshots in Maven HTTP repository with artifact classifier"() {
+        def repo1 = mavenHttpRepo("repo1")
+
+        given:
+        buildFile << """
+repositories {
+    maven {
+        url "${repo1.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:1.0-SNAPSHOT:tests"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        and:
+        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT")
+        def classifierArtifact = projectA.artifact(classifier: "tests")
+        projectA.publish()
+
+        when:
+        projectA.metaData.expectGet()
+        projectA.pom.expectGet()
+        classifierArtifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT-tests.jar')
+        def snapshotA = file('libs/projectA-1.0-SNAPSHOT-tests.jar').snapshot()
+
+        when:
+        server.resetExpectations()
+        run 'retrieve'
+
+        then: "Everything is up to date"
+        skipped ':retrieve'
+        file('libs/projectA-1.0-SNAPSHOT-tests.jar').assertHasNotChangedSince(snapshotA);
+    }
+
+    def "will detect changed snapshot artifacts when pom has not changed"() {
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+
+configurations { compile }
+configurations.compile.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+
+dependencies { 
+    compile "org.gradle.integtests.resolve:unique:1.0-SNAPSHOT"
+    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when: "snapshot modules are published"
+        def uniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "unique", "1.0-SNAPSHOT").publish()
+        def nonUniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
+
+        and: "Server handles requests"
+        expectModuleServed(uniqueVersionModule)
+        expectModuleServed(nonUniqueVersionModule)
+
+        and: "We resolve dependencies"
+        run 'retrieve'
+
+        then: "Snapshots are downloaded"
+        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
+        def uniqueJarSnapshot = file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).snapshot()
+        def nonUniqueJarSnapshot = file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).snapshot()
+        server.resetExpectations()
+
+        when: "Change the snapshot artifacts directly: do not change the pom"
+        uniqueVersionModule.artifactFile << 'more content'
+        uniqueVersionModule.backingModule.sha1File(uniqueVersionModule.artifactFile)
+        nonUniqueVersionModule.artifactFile << 'more content'
+        nonUniqueVersionModule.backingModule.sha1File(nonUniqueVersionModule.artifactFile)
+
+        and: "No server requests"
+        expectChangedArtifactServed(uniqueVersionModule)
+        expectChangedArtifactServed(nonUniqueVersionModule)
+
+        and: "Resolve dependencies again"
+        run 'retrieve'
+
+        then:
+        file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).assertHasChangedSince(uniqueJarSnapshot)
+        file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).assertHasChangedSince(nonUniqueJarSnapshot)
+    }
+
+    def "uses cached snapshots from a Maven HTTP repository until the snapshot timeout is reached"() {
+        given:
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+if (project.hasProperty('noTimeout')) {
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+}
+
+dependencies {
+    compile "org.gradle.integtests.resolve:unique:1.0-SNAPSHOT"
+    compile "org.gradle.integtests.resolve:nonunique:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when: "snapshot modules are published"
+        def uniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "unique", "1.0-SNAPSHOT").publish()
+        def nonUniqueVersionModule = mavenHttpRepo.module("org.gradle.integtests.resolve", "nonunique", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
+
+        and: "Server handles requests"
+        expectModuleServed(uniqueVersionModule)
+        expectModuleServed(nonUniqueVersionModule)
+
+        and: "We resolve dependencies"
+        run 'retrieve'
+
+        then: "Snapshots are downloaded"
+        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
+        def uniqueJarSnapshot = file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).snapshot()
+        def nonUniqueJarSnapshot = file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).snapshot()
+
+        when: "Republish the snapshots"
+        uniqueVersionModule.publishWithChangedContent()
+        nonUniqueVersionModule.publishWithChangedContent()
+
+        and: "No server requests"
+        server.resetExpectations()
+
+        and: "Resolve dependencies again, with cached versions"
+        run 'retrieve'
+
+        then:
+        file('libs/unique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(uniqueJarSnapshot)
+        file('libs/nonunique-1.0-SNAPSHOT.jar').assertHasNotChangedSince(nonUniqueJarSnapshot)
+
+        when: "Server handles requests"
+        expectChangedModuleServed(uniqueVersionModule)
+        expectChangedModuleServed(nonUniqueVersionModule)
+
+        and: "Resolve dependencies with cache expired"
+        executer.withArguments("-PnoTimeout")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('unique-1.0-SNAPSHOT.jar', 'nonunique-1.0-SNAPSHOT.jar')
+        file('libs/unique-1.0-SNAPSHOT.jar').assertIsCopyOf(uniqueVersionModule.artifactFile).assertHasChangedSince(uniqueJarSnapshot)
+        file('libs/nonunique-1.0-SNAPSHOT.jar').assertIsCopyOf(nonUniqueVersionModule.artifactFile).assertHasChangedSince(nonUniqueJarSnapshot);
+    }
+
+    def "does not download snapshot artifacts after expiry when snapshot has not changed"() {
+        buildFile << """
+repositories {
+    maven { url "${mavenHttpRepo.uri}" }
+}
+
+configurations { compile }
+
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+dependencies {
+    compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'build'
+    from configurations.compile
+}
+"""
+
+        when: "Publish the first snapshot"
+        def module = mavenHttpRepo.module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").publish()
+
+        and: "Server handles requests"
+        expectModuleServed(module)
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
+        def snapshot = file('build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifactFile).snapshot()
+
+        when: "Server handles requests"
+        server.resetExpectations()
+        expectChangedProbe(module)
+
+        // Retrieve again with zero timeout should check for updated snapshot
+        and:
+        def result = run 'retrieve'
+
+        then:
+        result.assertTaskSkipped(':retrieve')
+        file('build/testproject-1.0-SNAPSHOT.jar').assertHasNotChangedSince(snapshot);
+    }
+
+    def "does not download snapshot artifacts more than once per build"() {
+        given:
+        def module = mavenHttpRepo.module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").publish()
+
+        and:
+        settingsFile << "include 'a', 'b'"
+        buildFile << """
+allprojects {
+    repositories {
+        maven { url "${mavenHttpRepo.uri}" }
+    }
+
+    configurations { compile }
+
+    configurations.all {
+        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+    }
+
+    dependencies {
+        compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
+    }
+
+    task retrieve(type: Sync) {
+        into 'build'
+        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)
+
+        then:
+        run 'retrieve'
+
+        and:
+        file('build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
+        file('a/build').assertHasDescendants('testproject-1.0-SNAPSHOT.jar')
+        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"() {
+        given:
+        def module = mavenHttpRepo("/repo", maven("repo1")).module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
+        def module2 = mavenHttpRepo("/repo", maven("repo2")).module("org.gradle.integtests.resolve", "testproject", "1.0-SNAPSHOT").withNonUniqueSnapshots().publish()
+        module2.pomFile << '    ' // ensure it's a different length to the first one
+        module2.backingModule.sha1File(module2.pomFile)
+        module2.artifactFile << module2.artifactFile.bytes // ensure it's a different length to the first one
+        module2.backingModule.sha1File(module2.artifactFile)
+        and:
+        settingsFile << "include 'first', 'second'"
+        buildFile << """
+def fileLocks = [:]
+subprojects {
+    repositories {
+        maven { url "http://localhost:${server.port}/repo" }
+    }
+
+    configurations { compile }
+
+    configurations.all {
+        resolutionStrategy.resolutionRules.eachArtifact({ artifact ->
+            artifact.refresh()
+        } as Action)
+    }
+
+    dependencies {
+        compile "org.gradle.integtests.resolve:testproject:1.0-SNAPSHOT"
+    }
+
+    task lock << {
+        configurations.compile.each { file ->
+            println "locking " + file
+            def lockFile = new RandomAccessFile(file.canonicalPath, 'r')
+            fileLocks[file] = lockFile
+        }
+    }
+
+    task retrieve(type: Sync) {
+        into 'build'
+        from configurations.compile
+    }
+    retrieve.dependsOn 'lock'
+}
+project('second') {
+    lock.dependsOn ':first:lock'
+    retrieve.dependsOn ':first:retrieve'
+
+    task cleanup << {
+        fileLocks.each { key, value ->
+            println "unlocking " + key
+            value.close()
+        }
+    }
+    cleanup.dependsOn 'retrieve'
+}
+"""
+        when: "Module is requested once"
+        module.metaData.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+
+        module2.artifact.expectHead()
+        module2.artifact.sha1.expectGet()
+        module2.artifact.expectGet()
+
+        then:
+        run 'cleanup'
+
+        and:
+        file('first/build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module.artifactFile)
+        file('second/build/testproject-1.0-SNAPSHOT.jar').assertIsCopyOf(module2.artifactFile)
+    }
+
+    def "avoid redownload unchanged artifact when no checksum available"() {
+        given:
+        buildFile << """
+            repositories {
+                maven { url "${mavenHttpRepo.uri}" }
+            }
+
+            configurations { compile }
+
+            configurations.all {
+                resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+            }
+
+            dependencies {
+                compile group: "group", name: "projectA", version: "1.1-SNAPSHOT"
+            }
+
+            task retrieve(type: Copy) {
+                into 'build'
+                from configurations.compile
+            }
+        """
+
+        and:
+        def module = mavenHttpRepo.module("group", "projectA", "1.1-SNAPSHOT").withNonUniqueSnapshots().publish()
+        // Set the last modified to something that's not going to be anything “else”.
+        // There are lots of dates floating around in a resolution and we want to make
+        // sure we use this.
+        module.artifactFile.setLastModified(2000)
+        module.pom.file.setLastModified(6000)
+        def artifact = module.artifact
+
+        when:
+        expectModuleServed(module)
+
+        run "retrieve"
+
+        then:
+        def downloadedJarFile = file("build/projectA-1.1-SNAPSHOT.jar")
+        downloadedJarFile.assertIsCopyOf(module.artifactFile)
+        def initialDownloadJarFileSnapshot = downloadedJarFile.snapshot()
+
+        when:
+        server.resetExpectations()
+        expectChangedProbe(module)
+
+        run "retrieve"
+
+        then:
+        downloadedJarFile.assertHasNotChangedSince(initialDownloadJarFileSnapshot)
+
+        when:
+        module.publishWithChangedContent()
+        server.resetExpectations()
+        module.metaData.expectGet()
+        module.pom.expectHead()
+        module.pom.sha1.expectGetMissing()
+        module.pom.expectGet()
+        artifact.expectHead()
+        artifact.sha1.expectGetMissing()
+        artifact.expectGet()
+
+        run "retrieve"
+
+        then:
+        downloadedJarFile.assertHasChangedSince(initialDownloadJarFileSnapshot)
+        downloadedJarFile.assertIsCopyOf(module.artifactFile)
+    }
+
+    @Issue("GRADLE-3017")
+    def "resolves changed metadata in snapshot dependency"() {
+        given:
+        def projectB1 = mavenHttpRepo.module('group', 'projectB', '1.0').publish()
+        def projectB2 = mavenHttpRepo.module('group', 'projectB', '2.0').publish()
+        def projectA = mavenHttpRepo.module('group', 'projectA', "1.0-SNAPSHOT").dependsOn('group', 'projectB', '1.0').publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile {
+        if (project.hasProperty('bypassCache')) {
+            resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+        }
+    }
+}
+dependencies {
+    compile 'group:projectA:1.0-SNAPSHOT'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        projectA.pom.expectGet()
+        projectA.metaData.expectGet()
+        projectA.artifact.expectGet()
+        projectB1.pom.expectGet()
+        projectB1.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0.jar')
+
+        when: "Project A is published with changed dependencies"
+        server.resetExpectations()
+        projectA = projectA.dependsOn('group', 'projectB', '2.0').publish()
+
+        and: "Resolve with caching"
+        run 'retrieve'
+
+        then: "Gets original ProjectA metadata from cache"
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-1.0.jar')
+
+        when: "Resolve without cache"
+        projectA.metaData.expectGet()
+        projectA.pom.expectHead()
+        projectA.pom.sha1.expectGet()
+        projectA.pom.expectGet()
+        projectA.artifact.expectHead()
+        projectB2.pom.expectGet()
+        projectB2.artifact.expectGet()
+
+        and:
+        executer.withArguments("-PbypassCache")
+        run 'retrieve'
+
+        then: "Gets updated metadata"
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-2.0.jar')
+
+        when: "Resolve with caching"
+        server.resetExpectations()
+        run 'retrieve'
+
+        then: "Gets updated metadata from cache"
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar', 'projectB-2.0.jar')
+    }
+
+    def "reports and recovers from missing snapshot"() {
+        given:
+        def projectA = mavenHttpRepo.module('group', 'projectA', "1.0-SNAPSHOT")
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile 'group:projectA:1.0-SNAPSHOT'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        projectA.metaData.expectGetMissing()
+        projectA.pom.expectGetMissing()
+        projectA.artifact.expectHeadMissing()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find group:projectA:1.0-SNAPSHOT.
+Searched in the following locations:
+    ${projectA.metaData.uri}
+    ${projectA.pom.uri}
+    ${projectA.artifact.uri}
+Required by:
+""")
+
+        when:
+        server.resetExpectations()
+        projectA.publish()
+        projectA.metaData.expectGet()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar')
+    }
+
+    def "reports missing unique snapshot artifact"() {
+        given:
+        def projectA = mavenHttpRepo.module('group', 'projectA', "1.0-SNAPSHOT").publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile 'group:projectA:1.0-SNAPSHOT'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        projectA.metaData.expectGet()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGetMissing()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find projectA.jar (group:projectA:1.0-SNAPSHOT).
+Searched in the following locations:
+    ${projectA.artifact.uri}""")
+
+        when:
+        server.resetExpectations()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause("""Could not find projectA.jar (group:projectA:1.0-SNAPSHOT).
+Searched in the following locations:
+    ${projectA.artifact.uri}""")
+    }
+
+    def "reports and recovers from broken maven-metadata.xml"() {
+        given:
+        def projectA = mavenHttpRepo.module('group', 'projectA', "1.0-SNAPSHOT").publish()
+
+        buildFile << """
+repositories {
+    maven { url '${mavenHttpRepo.uri}' }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile 'group:projectA:1.0-SNAPSHOT'
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        def metaData = projectA.metaData
+        metaData.expectGetBroken()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasCause('Could not resolve group:projectA:1.0-SNAPSHOT.')
+        failure.assertHasCause("Unable to load Maven meta-data from ${metaData.uri}.")
+        failure.assertHasCause("Could not GET '${metaData.uri}'. Received status code 500 from server")
+
+        when:
+        server.resetExpectations()
+        metaData.expectGet()
+        projectA.pom.expectGet()
+        projectA.artifact.expectGet()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants('projectA-1.0-SNAPSHOT.jar')
+    }
+
+    def "can find and cache a unique snapshot in a Maven HTTP repository"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT")
+        def published = projectA.publish()
+        buildFile << """
+repositories {
+    maven {
+        url "${repo1.uri}"
+    }
+}
+
+configurations {
+    compile {
+        resolutionStrategy.cacheChangingModulesFor 0, 'SECONDS'
+    }
+}
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:${published.publishArtifactVersion}"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        published.pom.expectGet()
+        published.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${published.publishArtifactVersion}.jar")
+
+        when:
+        server.resetExpectations()
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${published.publishArtifactVersion}.jar")
+    }
+
+    def "can find a unique snapshot in a Maven file repository"() {
+        given:
+        def fileRepo = maven("fileRepo")
+        def projectA = fileRepo.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT")
+        projectA.publish()
+        buildFile << """
+repositories {
+    maven {
+        url "${fileRepo.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:${projectA.publishArtifactVersion}"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${projectA.publishArtifactVersion}.jar")
+
+        when:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${projectA.publishArtifactVersion}.jar")
+    }
+
+    def "applies conflict resolution when unique snapshot is referenced by timestamp"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def projectA = repo1.module("group", "projectA", "1.0-SNAPSHOT")
+        def published = projectA.publish()
+        def timestamp1 = published.publishArtifactVersion
+        buildFile << """
+repositories {
+    maven {
+        url "${repo1.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "group:projectA:${timestamp1}"
+    compile "group:projectA:1.0-SNAPSHOT"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        published.metaData.expectGet()
+        published.pom.expectGet()
+        published.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${timestamp1}.jar")
+
+        when:
+        published.publishWithChangedContent()
+        def timestamp2 = published.publishArtifactVersion
+        buildFile << """
+dependencies {
+    compile "group:projectA:${timestamp2}"
+}
+"""
+        server.resetExpectations()
+        published.pom.expectGet()
+        published.artifact.expectGet()
+
+        and:
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-${timestamp2}.jar")
+
+        when:
+        server.resetExpectations()
+        def released = repo1.module("group", "projectA", "1.0").publish()
+        released.pom.expectGet()
+        released.artifact.expectGet()
+        buildFile << """
+dependencies {
+    compile "group:projectA:1.0"
+}
+"""
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants("projectA-1.0.jar")
+    }
+
+    def "reports failure to find a missing unique snapshot in a Maven HTTP repository"() {
+        given:
+        def repo1 = mavenHttpRepo("repo1")
+        def projectA = repo1.module("org.gradle.integtests.resolve", "projectA", "1.0-SNAPSHOT")
+        def published = projectA.publish()
+        buildFile << """
+repositories {
+    maven {
+        url "${repo1.uri}"
+    }
+}
+
+configurations { compile }
+
+dependencies {
+    compile "org.gradle.integtests.resolve:projectA:${published.publishArtifactVersion}"
+}
+
+task retrieve(type: Sync) {
+    into 'libs'
+    from configurations.compile
+}
+"""
+
+        when:
+        published.missing()
+
+        and:
+        fails('retrieve')
+
+        then:
+        failure.assertHasCause("""Could not find org.gradle.integtests.resolve:projectA:${published.publishArtifactVersion}.
+Searched in the following locations:
+    ${projectA.pom.uri}
+    ${projectA.artifact.uri}
+Required by:
+""")
+    }
+
+    private expectModuleServed(MavenHttpModule module) {
+        module.metaData.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectGet()
+    }
+
+    private expectChangedModuleServed(MavenHttpModule module) {
+        module.metaData.expectGet()
+        module.pom.expectHead()
+        module.pom.sha1.expectGet()
+        module.pom.expectGet()
+        module.artifact.expectHead()
+        module.artifact.sha1.expectGet()
+        module.artifact.expectGet()
+    }
+
+    private expectChangedArtifactServed(MavenHttpModule module) {
+        module.metaData.expectGet()
+        module.pom.expectHead()
+        def artifact = module.artifact
+        artifact.expectHead()
+        artifact.sha1.expectGet()
+        artifact.expectGet()
+    }
+
+    private expectChangedProbe(MavenHttpModule module) {
+        module.metaData.expectGet()
+        module.pom.expectHead()
+        module.artifact.expectHead()
+    }
+
+    private expectModuleMissing(MavenHttpModule module) {
+        module.metaData.expectGetMissing()
+        module.pom.expectGetMissing()
+        module.artifact.expectHeadMissing()
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/support/RepositoryDslSupport.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/support/RepositoryDslSupport.groovy
new file mode 100644
index 0000000..3c65da9
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/support/RepositoryDslSupport.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.support
+
+class RepositoryDslSupport {
+
+    def url = "http://somerepo"
+    def s3Url = "s3://somerepo"
+
+    def simplestDsl() {
+        """
+        repositories {
+            maven {
+                url "${url}"
+            }
+        }
+        """
+    }
+
+    def passwordCredentialsTyped() {
+        """
+        repositories {
+            maven {
+                url "${url}"
+                credentials(PasswordCredentials) {
+                    username "myUsername"
+                    password "myPassword"
+                }
+            }
+        }
+        """
+    }
+
+    def awsCredentials() {
+        """
+        repositories {
+            maven {
+                url "${s3Url}"
+                credentials(AwsCredentials) {
+                    accessKey "myAccessKey"
+                    secretKey "mySecret"
+                }
+            }
+        }
+        """
+
+    }
+
+    def ivyPasswordCredentials(){
+        """
+repositories {
+    ivy {
+        url "${url}"
+        credentials {
+            username 'targetUser'
+            password 'targetPassword'
+        }
+    }
+}
+"""
+    }
+}
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectA-1.2-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectB-1.5-ivy.xml
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
new file mode 100644
index 0000000..d6f6c62
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/projectWithConfigurationHierarchy.gradle
@@ -0,0 +1,57 @@
+configurations {
+    compile
+    runtime { extendsFrom compile }
+}
+dependencies {
+    repositories {
+        ivy {
+            artifactPattern projectDir.absolutePath + '/[artifact]-[revision].jar'
+            ivyPattern projectDir.absolutePath + '/[module]-[revision]-ivy.xml'
+        }
+    }
+    compile group: 'test', name: 'projectA', version: '1.2', configuration: 'api'
+    runtime group: 'test', name: 'projectA', version: '1.2'
+    runtime group: 'test', name: 'projectB', version: '1.5', configuration: 'extraRuntime'
+}
+
+file("projectA-1.2.jar").text = ''
+file("projectB-1.5.jar").text = ''
+file("projectB-api-1.5.jar").text = ''
+file("projectB-extraRuntime-1.5.jar").text = ''
+
+defaultTasks 'listJars'
+
+task listJars << {
+    def compile = configurations.compile
+
+    Set jars = compile.collect { it.name } as Set
+    assert ['projectA-1.2.jar', 'projectB-api-1.5.jar'] as Set == jars
+
+    def projectA = compile.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
+    def root = (projectA.parents as List)[0]
+    def artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
+    assert ['projectA', 'projectB-api'] as Set == artifacts
+
+    def projectB = projectA.children.find { it.moduleName == 'projectB' && it.configuration == 'compileTime' }
+    artifacts = projectB.getAllArtifacts(projectA).collect { it.name } as Set
+    assert ['projectB-api'] as Set == artifacts
+
+    def runtime = configurations.runtime
+
+    jars = runtime.collect { it.name } as Set
+    assert ['projectA-1.2.jar', 'projectB-api-1.5.jar', 'projectB-1.5.jar', 'projectB-extraRuntime-1.5.jar'] as Set == jars
+
+    projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
+    root = (projectA.parents as List)[0]
+    artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
+    assert ['projectA', 'projectB-api'] as Set == artifacts
+
+    projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'default' }
+    root = (projectA.parents as List)[0]
+    artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
+    assert ['projectA', 'projectB'] as Set == artifacts
+
+    projectB = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectB' && it.configuration == 'extraRuntime' }
+    artifacts = projectB.getAllArtifacts(root).collect { it.name } as Set
+    assert ['projectB', 'projectB-extraRuntime'] as Set == artifacts
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectA-1.2-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectB-1.5-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/projectWithCyclesInDependencyGraph.gradle
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canNestModules/projectWithNestedModules.gradle
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectA-1.2-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectB-1.5-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/projectWithDynamicVersions.gradle
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-1.2-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectA-2.0-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-1.5-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectB-2.1.5-ivy.xml
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
rename to subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
new file mode 100644
index 0000000..cdbadd3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactDependencyResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts;
+
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+
+import java.util.List;
+
+public interface ArtifactDependencyResolver {
+    void resolve(ConfigurationInternal configuration,
+                 List<? extends ResolutionAwareRepository> repositories,
+                 GlobalDependencyResolutionRules metadataHandler,
+                 ResolverResults results) throws ResolveException;
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactPublicationServices.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactPublisher.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ArtifactPublisher.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactPublisher.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentMetadataProcessor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentMetadataProcessor.java
new file mode 100644
index 0000000..7f6aa5e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentMetadataProcessor.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.external.model.MutableModuleComponentResolveMetaData;
+
+public interface ComponentMetadataProcessor {
+    void processMetadata(MutableModuleComponentResolveMetaData metadata);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentModuleMetadataProcessor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentModuleMetadataProcessor.java
new file mode 100644
index 0000000..0bde1fc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentModuleMetadataProcessor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.dsl.ModuleReplacementsData;
+
+public interface ComponentModuleMetadataProcessor {
+    ModuleReplacementsData getModuleReplacements();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionInternal.java
new file mode 100644
index 0000000..fac5874
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionInternal.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentSelection;
+
+public interface ComponentSelectionInternal extends ComponentSelection {
+    boolean isRejected();
+    
+    String getRejectionReason();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionRulesInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionRulesInternal.java
new file mode 100644
index 0000000..ae2fdd7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ComponentSelectionRulesInternal.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentSelection;
+import org.gradle.api.artifacts.ComponentSelectionRules;
+import org.gradle.internal.rules.SpecRuleAction;
+
+import java.util.Collection;
+
+public interface ComponentSelectionRulesInternal extends ComponentSelectionRules {
+    Collection<SpecRuleAction<? super ComponentSelection>> getRules();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ConfigurationResolver.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.java
new file mode 100644
index 0000000..04f105b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultArtifactIdentifier.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.api.internal.artifacts;
+
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier;
+
+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(DefaultModuleComponentArtifactIdentifier id) {
+        this(newId(id.getComponentIdentifier()), id.getName().getName(), id.getName().getType(), id.getName().getExtension(), id.getName().getClassifier());
+    }
+
+    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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelection.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelection.java
new file mode 100644
index 0000000..5e90335
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelection.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component.ModuleComponentIdentifier;
+
+public class DefaultComponentSelection implements ComponentSelectionInternal {
+    private ModuleComponentIdentifier candidate;
+    private boolean rejected;
+    private String rejectionReason;
+
+    public DefaultComponentSelection(ModuleComponentIdentifier candidate) {
+        this.candidate = candidate;
+    }
+
+    public ModuleComponentIdentifier getCandidate() {
+        return candidate;
+    }
+
+    public void reject(String reason) {
+        rejected = true;
+        rejectionReason = reason;
+    }
+
+    public boolean isRejected() {
+        return rejected;
+    }
+
+    public String getRejectionReason() {
+        return rejectionReason;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyFactory.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
new file mode 100644
index 0000000..bedc259
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Project;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.dsl.*;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer;
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.dsl.*;
+import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradlePomModuleDescriptorParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.query.ArtifactResolutionQueryFactory;
+import org.gradle.api.internal.artifacts.query.DefaultArtifactResolutionQueryFactory;
+import org.gradle.api.internal.artifacts.repositories.DefaultBaseRepositoryFactory;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore;
+import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.typeconversion.NotationParser;
+
+public class DefaultDependencyManagementServices implements DependencyManagementServices {
+
+    private final ServiceRegistry parent;
+
+    public DefaultDependencyManagementServices(ServiceRegistry parent) {
+        this.parent = parent;
+    }
+
+    public DependencyResolutionServices create(FileResolver fileResolver, DependencyMetaDataProvider dependencyMetaDataProvider, ProjectFinder projectFinder, DomainObjectContext domainObjectContext) {
+        DefaultServiceRegistry services = new DefaultServiceRegistry(parent);
+        services.add(FileResolver.class, fileResolver);
+        services.add(DependencyMetaDataProvider.class, dependencyMetaDataProvider);
+        services.add(ProjectFinder.class, projectFinder);
+        services.add(DomainObjectContext.class, domainObjectContext);
+        services.addProvider(new DependencyResolutionScopeServices());
+        return services.get(DependencyResolutionServices.class);
+    }
+
+    public void addDslServices(ServiceRegistration registration) {
+        registration.addProvider(new DependencyResolutionScopeServices());
+    }
+
+    private static class DependencyResolutionScopeServices {
+        BaseRepositoryFactory createBaseRepositoryFactory(LocalMavenRepositoryLocator localMavenRepositoryLocator, Instantiator instantiator, FileResolver fileResolver,
+                                                          RepositoryTransportFactory repositoryTransportFactory, LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                                          ResolverStrategy resolverStrategy, ArtifactIdentifierFileStore artifactIdentifierFileStore,
+                                                          VersionSelectorScheme versionSelectorScheme) {
+            return new DefaultBaseRepositoryFactory(
+                    localMavenRepositoryLocator,
+                    fileResolver,
+                    instantiator,
+                    repositoryTransportFactory,
+                    locallyAvailableResourceFinder,
+                    resolverStrategy,
+                    artifactIdentifierFileStore,
+                    new GradlePomModuleDescriptorParser(versionSelectorScheme)
+            );
+        }
+
+        RepositoryHandler createRepositoryHandler(Instantiator instantiator, BaseRepositoryFactory baseRepositoryFactory) {
+            return instantiator.newInstance(DefaultRepositoryHandler.class, baseRepositoryFactory, instantiator);
+        }
+
+        ConfigurationContainerInternal createConfigurationContainer(Instantiator instantiator, ConfigurationResolver configurationResolver, DomainObjectContext domainObjectContext,
+                                                                    ListenerManager listenerManager, DependencyMetaDataProvider metaDataProvider, ProjectAccessListener projectAccessListener) {
+            return instantiator.newInstance(DefaultConfigurationContainer.class,
+                    configurationResolver,
+                    instantiator,
+                    domainObjectContext,
+                    listenerManager,
+                    metaDataProvider,
+                    projectAccessListener);
+        }
+
+        DependencyHandler createDependencyHandler(Instantiator instantiator, ConfigurationContainerInternal configurationContainer, DependencyFactory dependencyFactory,
+                                                  ProjectFinder projectFinder, ComponentMetadataHandler componentMetadataHandler, ComponentModuleMetadataHandler componentModuleMetadataHandler, ArtifactResolutionQueryFactory resolutionQueryFactory) {
+            return instantiator.newInstance(DefaultDependencyHandler.class,
+                    configurationContainer,
+                    dependencyFactory,
+                    projectFinder,
+                    componentMetadataHandler,
+                    componentModuleMetadataHandler,
+                    resolutionQueryFactory);
+        }
+
+        DefaultComponentMetadataHandler createComponentMetadataHandler(Instantiator instantiator) {
+            return instantiator.newInstance(DefaultComponentMetadataHandler.class, instantiator);
+        }
+
+        DefaultComponentModuleMetadataHandler createComponentModuleMetadataHandler(Instantiator instantiator) {
+            return instantiator.newInstance(DefaultComponentModuleMetadataHandler.class);
+        }
+
+        ArtifactHandler createArtifactHandler(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, ConfigurationContainerInternal configurationContainer) {
+            NotationParser<Object, PublishArtifact> publishArtifactNotationParser = new PublishArtifactNotationParserFactory(instantiator, dependencyMetaDataProvider).create();
+            return instantiator.newInstance(DefaultArtifactHandler.class, configurationContainer, publishArtifactNotationParser);
+        }
+
+        GlobalDependencyResolutionRules createModuleMetadataHandler(ComponentMetadataProcessor componentMetadataProcessor, ComponentModuleMetadataProcessor moduleMetadataProcessor) {
+            return new DefaultGlobalDependencyResolutionRules(componentMetadataProcessor, moduleMetadataProcessor);
+        }
+
+        ConfigurationResolver createDependencyResolver(ArtifactDependencyResolver artifactDependencyResolver, RepositoryHandler repositories,
+                                                       GlobalDependencyResolutionRules metadataHandler) {
+            return new DefaultConfigurationResolver(artifactDependencyResolver, repositories, metadataHandler);
+        }
+
+        ArtifactPublicationServices createArtifactPublicationServices(ServiceRegistry services) {
+            return new DefaultArtifactPublicationServices(services);
+        }
+
+        DependencyResolutionServices createDependencyResolutionServices(ServiceRegistry services) {
+            return new DefaultDependencyResolutionServices(services);
+        }
+
+        ArtifactResolutionQueryFactory createArtifactResolutionQueryFactory(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
+                                                                            ResolveIvyFactory ivyFactory, GlobalDependencyResolutionRules metadataHandler,
+                                                                            CacheLockingManager cacheLockingManager, ComponentTypeRegistry componentTypeRegistry) {
+            return new DefaultArtifactResolutionQueryFactory(configurationContainer, repositoryHandler, ivyFactory, metadataHandler, cacheLockingManager, componentTypeRegistry);
+
+        }
+    }
+
+    private static class DependencyMetaDataProviderImpl implements DependencyMetaDataProvider {
+        public ModuleInternal getModule() {
+            return new DefaultModule("unspecified", "unspecified", Project.DEFAULT_VERSION, Project.DEFAULT_STATUS);
+        }
+    }
+
+    private static class DefaultDependencyResolutionServices implements DependencyResolutionServices {
+        private final ServiceRegistry services;
+
+        private DefaultDependencyResolutionServices(ServiceRegistry services) {
+            this.services = services;
+        }
+
+        public RepositoryHandler getResolveRepositoryHandler() {
+            return services.get(RepositoryHandler.class);
+        }
+
+        public ConfigurationContainerInternal getConfigurationContainer() {
+            return services.get(ConfigurationContainerInternal.class);
+        }
+
+        public DependencyHandler getDependencyHandler() {
+            return services.get(DependencyHandler.class);
+        }
+    }
+
+    private static class DefaultArtifactPublicationServices implements ArtifactPublicationServices {
+        private final ServiceRegistry services;
+
+        public DefaultArtifactPublicationServices(ServiceRegistry services) {
+            this.services = services;
+        }
+
+        public RepositoryHandler createRepositoryHandler() {
+            Instantiator instantiator = services.get(Instantiator.class);
+            BaseRepositoryFactory baseRepositoryFactory = services.get(BaseRepositoryFactory.class);
+            return instantiator.newInstance(DefaultRepositoryHandler.class, baseRepositoryFactory, instantiator);
+        }
+
+        public ArtifactPublisher createArtifactPublisher() {
+            return new IvyBackedArtifactPublisher(
+                    services.get(LocalComponentFactory.class),
+                    services.get(IvyContextManager.class),
+                    new DefaultIvyDependencyPublisher(),
+                    new IvyXmlModuleDescriptorWriter()
+            );
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultGlobalDependencyResolutionRules.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultGlobalDependencyResolutionRules.java
new file mode 100644
index 0000000..b4c8c11
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultGlobalDependencyResolutionRules.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+public class DefaultGlobalDependencyResolutionRules implements GlobalDependencyResolutionRules {
+    private final ComponentMetadataProcessor componentMetadataProcessor;
+    private final ComponentModuleMetadataProcessor moduleMetadataProcessor;
+
+    public DefaultGlobalDependencyResolutionRules(ComponentMetadataProcessor componentMetadataProcessor, ComponentModuleMetadataProcessor moduleMetadataProcessor) {
+        this.componentMetadataProcessor = componentMetadataProcessor;
+        this.moduleMetadataProcessor = moduleMetadataProcessor;
+    }
+
+    public ComponentMetadataProcessor getComponentMetadataProcessor() {
+        return componentMetadataProcessor;
+    }
+
+    public ComponentModuleMetadataProcessor getModuleMetadataProcessor() {
+        return moduleMetadataProcessor;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java
new file mode 100755
index 0000000..fa61412
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleIdentifier.java
@@ -0,0 +1,71 @@
+/*
+ * 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.ModuleIdentifier;
+
+public class DefaultModuleIdentifier implements ModuleIdentifier {
+    private final String group;
+    private final String name;
+
+    public DefaultModuleIdentifier(String group, String name) {
+        assert group != null : "group cannot be null";
+        assert name != null : "name cannot be null";
+        this.group = group;
+        this.name = name;
+    }
+
+    public static ModuleIdentifier newId(String group, String name) {
+        return new DefaultModuleIdentifier(group, name);
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s:%s", group, name);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultModuleIdentifier other = (DefaultModuleIdentifier) obj;
+        if (!group.equals(other.group)) {
+            return false;
+        }
+        if (!name.equals(other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return group.hashCode() ^ name.hashCode();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
new file mode 100755
index 0000000..06e48de
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifier.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.id.ModuleRevisionId;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+
+public class DefaultModuleVersionIdentifier implements ModuleVersionIdentifier {
+
+    private final DefaultModuleIdentifier id;
+    private final String version;
+
+    public DefaultModuleVersionIdentifier(String group, String name, String version) {
+        assert group != null : "group cannot be null";
+        assert name != null : "name cannot be null";
+        assert version != null : "version cannot be null";
+        this.id = new DefaultModuleIdentifier(group, name);
+        this.version = version;
+    }
+
+    public DefaultModuleVersionIdentifier(ModuleIdentifier id, String version) {
+        this.id = new DefaultModuleIdentifier(id.getGroup(), id.getName());
+        this.version = version;
+    }
+
+    public String getGroup() {
+        return id.getGroup();
+    }
+
+    public String getName() {
+        return id.getName();
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String toString() {
+        String group = id.getGroup();
+        String module = id.getName();
+        StringBuilder builder = new StringBuilder(group.length() + module.length() + version.length() + 2);
+        builder.append(group);
+        builder.append(":");
+        builder.append(module);
+        builder.append(":");
+        builder.append(version);
+        return builder.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultModuleVersionIdentifier other = (DefaultModuleVersionIdentifier) obj;
+        if (!id.equals(other.id)) {
+            return false;
+        }
+        if (!version.equals(other.version)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode() ^ version.hashCode();
+    }
+
+    public ModuleIdentifier getModule() {
+        return id;
+    }
+
+    public static ModuleVersionIdentifier newId(Module module) {
+        return new DefaultModuleVersionIdentifier(module.getGroup(), module.getName(), module.getVersion());
+    }
+
+    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());
+    }
+
+    public static ModuleVersionIdentifier newId(ModuleComponentIdentifier componentId) {
+        return new DefaultModuleVersionIdentifier(componentId.getGroup(), componentId.getModule(), componentId.getVersion());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultModuleVersionSelector.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultProjectDependencyFactory.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
new file mode 100644
index 0000000..5660d57
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolvedArtifact.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ResolvedArtifact;
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.Factory;
+
+import java.io.File;
+
+public class DefaultResolvedArtifact implements ResolvedArtifact {
+    private final ResolvedModuleVersion owner;
+    private final IvyArtifactName artifact;
+    private long id;
+    private Factory<File> artifactSource;
+    private File file;
+
+    public DefaultResolvedArtifact(ResolvedModuleVersion owner, IvyArtifactName artifact, Factory<File> artifactSource, long id) {
+        this.owner = owner;
+        this.artifact = artifact;
+        this.id = id;
+        this.artifactSource = artifactSource;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public ResolvedModuleVersion getModuleVersion() {
+        return owner;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("[ResolvedArtifact dependency:%s name:%s classifier:%s extension:%s type:%s]", owner, getName(), getClassifier(), getExtension(), getType());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultResolvedArtifact other = (DefaultResolvedArtifact) obj;
+        if (!other.owner.getId().equals(owner.getId())) {
+            return false;
+        }
+        if (!other.artifact.equals(artifact)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return owner.getId().hashCode() ^ getName().hashCode() ^ getType().hashCode() ^ getExtension().hashCode() ^ artifact.hashCode();
+    }
+
+    public String getName() {
+        return artifact.getName();
+    }
+
+    public String getType() {
+        return artifact.getType();
+    }
+
+    public String getExtension() {
+        return artifact.getExtension();
+    }
+
+    public String getClassifier() {
+        return artifact.getClassifier();
+    }
+    
+    public File getFile() {
+        if (file == null) {
+            file = artifactSource.create();
+            artifactSource = null;
+        }
+        return file;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolvedDependency.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
new file mode 100644
index 0000000..693f391
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.StartParameter;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
+import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.SingleFileBackedModuleVersionsCache;
+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.InMemoryCachedRepositoryFactory;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.*;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.DefaultModuleArtifactsCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.DefaultModuleMetaDataCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectComponentRegistry;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublicationRegistry;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
+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.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.file.FileLookup;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore;
+import org.gradle.api.internal.notations.ClientModuleNotationParserFactory;
+import org.gradle.api.internal.notations.DependencyNotationParser;
+import org.gradle.api.internal.notations.ProjectDependencyFactory;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectRegistry;
+import org.gradle.cache.CacheRepository;
+import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.cached.ByUrlCachedExternalResourceIndex;
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryCachedArtifactIndex;
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.gradle.internal.resource.local.UniquePathKeyFileStore;
+import org.gradle.internal.resource.local.ivy.LocallyAvailableResourceFinderFactory;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+/**
+ * The set of dependency management services that are created per build.
+ */
+class DependencyManagementBuildScopeServices {
+    InMemoryCachedRepositoryFactory createInMemoryDependencyMetadataCache() {
+        return new InMemoryCachedRepositoryFactory();
+    }
+
+    DependencyManagementServices createDependencyManagementServices(ServiceRegistry parent) {
+        return new DefaultDependencyManagementServices(parent);
+    }
+
+    DependencyFactory createDependencyFactory(Instantiator instantiator,
+                                              ProjectAccessListener projectAccessListener,
+                                              StartParameter startParameter,
+                                              ClassPathRegistry classPathRegistry,
+                                              FileLookup fileLookup) {
+        DefaultProjectDependencyFactory factory = new DefaultProjectDependencyFactory(
+                projectAccessListener, instantiator, startParameter.isBuildProjectDependencies());
+
+        ProjectDependencyFactory projectDependencyFactory = new ProjectDependencyFactory(factory);
+
+        return new DefaultDependencyFactory(
+                DependencyNotationParser.parser(instantiator, factory, classPathRegistry, fileLookup),
+                new ClientModuleNotationParserFactory(instantiator).create(),
+                projectDependencyFactory);
+    }
+
+    CacheLockingManager createCacheLockingManager(CacheRepository cacheRepository) {
+        return new DefaultCacheLockingManager(cacheRepository);
+    }
+
+    BuildCommencedTimeProvider createBuildTimeProvider() {
+        return new BuildCommencedTimeProvider();
+    }
+
+    ModuleVersionsCache createModuleVersionsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        return new SingleFileBackedModuleVersionsCache(
+                timeProvider,
+                cacheLockingManager
+        );
+    }
+
+    ModuleArtifactsCache createModuleArtifactsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        return new DefaultModuleArtifactsCache(
+                timeProvider,
+                cacheLockingManager
+        );
+    }
+
+    ModuleMetaDataCache createModuleDescriptorCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager, ResolverStrategy resolverStrategy) {
+        return new DefaultModuleMetaDataCache(
+                timeProvider,
+                cacheLockingManager,
+                resolverStrategy
+        );
+    }
+
+    ArtifactAtRepositoryCachedArtifactIndex createArtifactAtRepositoryCachedResolutionIndex(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        return new ArtifactAtRepositoryCachedArtifactIndex(
+                "artifact-at-repository",
+                timeProvider,
+                cacheLockingManager
+        );
+    }
+
+    ByUrlCachedExternalResourceIndex createArtifactUrlCachedResolutionIndex(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        return new ByUrlCachedExternalResourceIndex(
+                "artifact-at-url",
+                timeProvider,
+                cacheLockingManager
+        );
+    }
+
+    ArtifactIdentifierFileStore createArtifactRevisionIdFileStore(CacheLockingManager cacheLockingManager) {
+        return new ArtifactIdentifierFileStore(new UniquePathKeyFileStore(cacheLockingManager.getFileStoreDirectory()), new TmpDirTemporaryFileProvider());
+    }
+
+    MavenSettingsProvider createMavenSettingsProvider() {
+        return new DefaultMavenSettingsProvider(new DefaultMavenFileLocations());
+    }
+
+    LocalMavenRepositoryLocator createLocalMavenRepositoryLocator(MavenSettingsProvider mavenSettingsProvider) {
+        return new DefaultLocalMavenRepositoryLocator(mavenSettingsProvider);
+    }
+
+    LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> createArtifactRevisionIdLocallyAvailableResourceFinder(ArtifactCacheMetaData artifactCacheMetaData, LocalMavenRepositoryLocator localMavenRepositoryLocator, ArtifactIdentifierFileStore fileStore) {
+        LocallyAvailableResourceFinderFactory finderFactory = new LocallyAvailableResourceFinderFactory(
+                artifactCacheMetaData,
+                localMavenRepositoryLocator,
+                fileStore);
+        return finderFactory.create();
+    }
+
+    ResolverStrategy createResolverStrategy() {
+        return new ResolverStrategy();
+    }
+
+    VersionSelectorScheme createVersionSelectorScheme(ResolverStrategy resolverStrategy, VersionComparator versionComparator) {
+        return new DefaultVersionSelectorScheme(versionComparator);
+    }
+
+    VersionComparator createVersionComparator() {
+        return new DefaultVersionComparator();
+    }
+
+    RepositoryTransportFactory createRepositoryTransportFactory(ProgressLoggerFactory progressLoggerFactory,
+                                                                TemporaryFileProvider temporaryFileProvider,
+                                                                ByUrlCachedExternalResourceIndex externalResourceIndex,
+                                                                BuildCommencedTimeProvider buildCommencedTimeProvider,
+                                                                CacheLockingManager cacheLockingManager,
+                                                                ServiceRegistry serviceRegistry) {
+        return new RepositoryTransportFactory(
+                serviceRegistry.getAll(ResourceConnectorFactory.class),
+                progressLoggerFactory,
+                temporaryFileProvider,
+                externalResourceIndex,
+                buildCommencedTimeProvider,
+                cacheLockingManager
+        );
+    }
+
+    ResolveIvyFactory createResolveIvyFactory(StartParameter startParameter, ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache, ModuleArtifactsCache moduleArtifactsCache,
+                                              ArtifactAtRepositoryCachedArtifactIndex artifactAtRepositoryCachedArtifactIndex, CacheLockingManager cacheLockingManager,
+                                              BuildCommencedTimeProvider buildCommencedTimeProvider, InMemoryCachedRepositoryFactory inMemoryCachedRepositoryFactory,
+                                              VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+        StartParameterResolutionOverride startParameterResolutionOverride = new StartParameterResolutionOverride(startParameter);
+        return new ResolveIvyFactory(
+                moduleVersionsCache,
+                moduleMetaDataCache,
+                moduleArtifactsCache,
+                artifactAtRepositoryCachedArtifactIndex,
+                cacheLockingManager,
+                startParameterResolutionOverride,
+                buildCommencedTimeProvider,
+                inMemoryCachedRepositoryFactory,
+                versionSelectorScheme,
+                versionComparator);
+    }
+
+    ArtifactDependencyResolver createArtifactDependencyResolver(ResolveIvyFactory resolveIvyFactory, LocalComponentFactory publishModuleDescriptorConverter, DependencyDescriptorFactory dependencyDescriptorFactory,
+                                                                CacheLockingManager cacheLockingManager, IvyContextManager ivyContextManager, ResolutionResultsStoreFactory resolutionResultsStoreFactory,
+                                                                VersionComparator versionComparator, ProjectRegistry<ProjectInternal> projectRegistry, ComponentIdentifierFactory componentIdentifierFactory) {
+        ArtifactDependencyResolver resolver = new DefaultDependencyResolver(
+                resolveIvyFactory,
+                publishModuleDescriptorConverter,
+                dependencyDescriptorFactory,
+                new DefaultProjectComponentRegistry(
+                        publishModuleDescriptorConverter,
+                        projectRegistry),
+                cacheLockingManager,
+                ivyContextManager,
+                resolutionResultsStoreFactory,
+                versionComparator
+        );
+        return new ErrorHandlingArtifactDependencyResolver(
+                new ShortcircuitEmptyConfigsArtifactDependencyResolver(
+                        new SelfResolvingDependencyResolver(
+                                new CacheLockingArtifactDependencyResolver(
+                                        cacheLockingManager,
+                                        resolver)),
+                        componentIdentifierFactory));
+    }
+
+    ResolutionResultsStoreFactory createResolutionResultsStoreFactory(TemporaryFileProvider temporaryFileProvider) {
+        return new ResolutionResultsStoreFactory(temporaryFileProvider);
+    }
+
+    ProjectPublicationRegistry createProjectPublicationRegistry() {
+        return new DefaultProjectPublicationRegistry();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
new file mode 100644
index 0000000..65ce1fc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.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;
+
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
+import org.gradle.api.internal.artifacts.component.DefaultComponentIdentifierFactory;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyContextManager;
+import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager;
+import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.*;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.*;
+
+class DependencyManagementGlobalScopeServices {
+    IvyContextManager createIvyContextManager() {
+        return new DefaultIvyContextManager();
+    }
+
+    ExcludeRuleConverter createExcludeRuleConverter() {
+        return new DefaultExcludeRuleConverter();
+    }
+
+    ComponentIdentifierFactory createComponentIdentifierFactory() {
+        return new DefaultComponentIdentifierFactory();
+    }
+
+    ExternalModuleIvyDependencyDescriptorFactory createExternalModuleDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
+        return new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverter);
+    }
+
+    ConfigurationsToModuleDescriptorConverter createConfigurationsToModuleDescriptorConverter() {
+        return new DefaultConfigurationsToModuleDescriptorConverter();
+    }
+
+    DependencyDescriptorFactory createDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter, ExternalModuleIvyDependencyDescriptorFactory descriptorFactory) {
+        return new DefaultDependencyDescriptorFactory(
+                new ProjectIvyDependencyDescriptorFactory(
+                        excludeRuleConverter),
+                descriptorFactory);
+    }
+
+    LocalComponentFactory createPublishLocalComponentFactory(ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter,
+                                                             DependencyDescriptorFactory dependencyDescriptorFactory,
+                                                             ExcludeRuleConverter excludeRuleConverter,
+                                                             ComponentIdentifierFactory componentIdentifierFactory) {
+        return new ResolveLocalComponentFactory(
+                configurationsToModuleDescriptorConverter,
+                new DefaultDependenciesToModuleDescriptorConverter(
+                        dependencyDescriptorFactory,
+                        excludeRuleConverter),
+                componentIdentifierFactory,
+                new DefaultConfigurationsToArtifactsConverter());
+
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java
new file mode 100644
index 0000000..f78eef7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.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;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class DependencyServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new DependencyManagementGlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new DependencyManagementBuildScopeServices());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/GlobalDependencyResolutionRules.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/GlobalDependencyResolutionRules.java
new file mode 100644
index 0000000..bbcbfb3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/GlobalDependencyResolutionRules.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.artifacts;
+
+public interface GlobalDependencyResolutionRules {
+    ComponentMetadataProcessor getComponentMetadataProcessor();
+    ComponentModuleMetadataProcessor getModuleMetadataProcessor();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.java
new file mode 100644
index 0000000..efcf624
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionIdentifierSerializer.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.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.IOException;
+
+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());
+    }
+
+    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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java
new file mode 100644
index 0000000..a1ea734
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionPublisher.java
@@ -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.api.internal.artifacts;
+
+import org.gradle.internal.component.external.model.IvyModulePublishMetaData;
+
+import java.io.IOException;
+
+public interface ModuleVersionPublisher {
+    void publish(IvyModulePublishMetaData moduleVersion) throws IOException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializer.java
new file mode 100644
index 0000000..0ff6733
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.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/ResolvedConfigurationIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifier.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializer.java
new file mode 100644
index 0000000..f689b86
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java
new file mode 100644
index 0000000..5b22d63
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.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.api.internal.artifacts;
+
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.ResolvedConfiguration;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults;
+
+public class ResolverResults {
+    private ResolvedConfiguration resolvedConfiguration;
+    private ResolutionResult resolutionResult;
+    private ResolveException fatalFailure;
+    private ResolvedProjectConfigurationResults resolvedProjectConfigurationResults;
+
+    //old model, slowly being replaced by the new model
+    public ResolvedConfiguration getResolvedConfiguration() {
+        assertHasResult();
+        return resolvedConfiguration;
+    }
+
+    //new model
+    public ResolutionResult getResolutionResult() {
+        assertHasResult();
+        if (fatalFailure != null) {
+            throw fatalFailure;
+        }
+        return resolutionResult;
+    }
+
+    public ResolvedProjectConfigurationResults getResolvedProjectConfigurationResults() {
+        assertHasResult();
+        if (fatalFailure != null) {
+            throw fatalFailure;
+        }
+        return resolvedProjectConfigurationResults;
+    }
+
+    private void assertHasResult() {
+        if (resolvedConfiguration == null) {
+            throw new IllegalStateException("Resolution result has not been attached.");
+        }
+    }
+
+    public void withResolvedConfiguration(ResolvedConfiguration resolvedConfiguration) {
+        this.resolvedConfiguration = resolvedConfiguration;
+    }
+
+    public void resolved(ResolvedConfiguration resolvedConfiguration, ResolutionResult resolutionResult, ResolvedProjectConfigurationResults resolvedProjectConfigurationResults) {
+        this.resolvedConfiguration = resolvedConfiguration;
+        this.resolutionResult = resolutionResult;
+        this.resolvedProjectConfigurationResults = resolvedProjectConfigurationResults;
+        this.fatalFailure = null;
+    }
+
+    public void failed(ResolvedConfiguration resolvedConfiguration, ResolveException failure) {
+        this.resolvedConfiguration = resolvedConfiguration;
+        this.resolutionResult = null;
+        this.fatalFailure = failure;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
new file mode 100644
index 0000000..5a2fedd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.artifacts.ModuleInternal;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier;
+
+public class DefaultComponentIdentifierFactory implements ComponentIdentifierFactory {
+    public ComponentIdentifier createComponentIdentifier(ModuleInternal module) {
+        String projectPath = module.getProjectPath();
+
+        if(projectPath != null) {
+            return new DefaultProjectComponentIdentifier(projectPath);
+        }
+
+        return new DefaultModuleComponentIdentifier(module.getGroup(), module.getName(), module.getVersion());
+    }
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
new file mode 100644
index 0000000..20023fe
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.configurations;
+
+import org.gradle.api.artifacts.Configuration;
+
+public interface ConfigurationInternal extends Configuration, DependencyMetaDataProvider {
+    ResolutionStrategyInternal getResolutionStrategy();
+
+    String getPath();
+
+    void includedInResolveResult();
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/Configurations.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/Configurations.java
new file mode 100644
index 0000000..32dd816
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/Configurations.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.configurations;
+
+import org.gradle.api.artifacts.Configuration;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Configurations {
+    public static Set<String> getNames(Collection<Configuration> configurations) {
+        Set<String> names = new HashSet<String>(configurations.size());
+        for (Configuration configuration : configurations) {
+            names.add(configuration.getName());
+        }
+        return names;
+    }
+
+    public static String uploadTaskName(String configurationName) {
+        return "upload".concat(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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
new file mode 100644
index 0000000..e06b164
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.CompositeDomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.artifacts.*;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType;
+import org.gradle.api.internal.file.AbstractFileCollection;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.util.CollectionUtils;
+import org.gradle.util.ConfigureUtil;
+import org.gradle.util.DeprecationLogger;
+import org.gradle.util.WrapUtil;
+
+import java.io.File;
+import java.util.*;
+
+import static org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+
+public class DefaultConfiguration extends AbstractFileCollection implements ConfigurationInternal {
+
+    private final String path;
+    private final String name;
+
+    private Visibility visibility = Visibility.PUBLIC;
+    private boolean transitive = true;
+    private Set<Configuration> extendsFrom = new LinkedHashSet<Configuration>();
+    private String description;
+    private ConfigurationsProvider configurationsProvider;
+    private final ConfigurationResolver resolver;
+    private final ListenerManager listenerManager;
+    private final DependencyMetaDataProvider metaDataProvider;
+    private final DefaultDependencySet dependencies;
+    private final CompositeDomainObjectSet<Dependency> inheritedDependencies;
+    private final DefaultDependencySet allDependencies;
+    private final DefaultPublishArtifactSet artifacts;
+    private final CompositeDomainObjectSet<PublishArtifact> inheritedArtifacts;
+    private final DefaultPublishArtifactSet allArtifacts;
+    private final ConfigurationResolvableDependencies resolvableDependencies = new ConfigurationResolvableDependencies();
+    private final ListenerBroadcast<DependencyResolutionListener> resolutionListenerBroadcast;
+    private Set<ExcludeRule> excludeRules = new LinkedHashSet<ExcludeRule>();
+    private final ProjectAccessListener projectAccessListener;
+
+    // This lock only protects the following fields
+    private final Object lock = new Object();
+    private State state = State.UNRESOLVED;
+    private boolean includedInResult;
+    private ResolverResults cachedResolverResults;
+    private final ResolutionStrategyInternal resolutionStrategy;
+
+    public DefaultConfiguration(String path, String name, ConfigurationsProvider configurationsProvider,
+                                ConfigurationResolver resolver, ListenerManager listenerManager,
+                                DependencyMetaDataProvider metaDataProvider,
+                                ResolutionStrategyInternal resolutionStrategy,
+                                ProjectAccessListener projectAccessListener) {
+        this.path = path;
+        this.name = name;
+        this.configurationsProvider = configurationsProvider;
+        this.resolver = resolver;
+        this.listenerManager = listenerManager;
+        this.metaDataProvider = metaDataProvider;
+        this.resolutionStrategy = resolutionStrategy;
+        this.projectAccessListener = projectAccessListener;
+
+        resolutionListenerBroadcast = listenerManager.createAnonymousBroadcaster(DependencyResolutionListener.class);
+
+        RunnableMutationValidator veto = new RunnableMutationValidator(MutationType.CONTENT) {
+            @Override
+            public void validateMutation(MutationType type) {
+                DefaultConfiguration.this.validateMutation(type);
+            }
+        };
+
+        DefaultDomainObjectSet<Dependency> ownDependencies = new DefaultDomainObjectSet<Dependency>(Dependency.class);
+        ownDependencies.beforeChange(veto);
+
+        dependencies = new DefaultDependencySet(String.format("%s dependencies", getDisplayName()), ownDependencies);
+        inheritedDependencies = CompositeDomainObjectSet.create(Dependency.class, ownDependencies);
+        allDependencies = new DefaultDependencySet(String.format("%s all dependencies", getDisplayName()), inheritedDependencies);
+
+        DefaultDomainObjectSet<PublishArtifact> ownArtifacts = new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact.class);
+        ownArtifacts.beforeChange(veto);
+        artifacts = new DefaultPublishArtifactSet(String.format("%s artifacts", getDisplayName()), ownArtifacts);
+        inheritedArtifacts = CompositeDomainObjectSet.create(PublishArtifact.class, ownArtifacts);
+        allArtifacts = new DefaultPublishArtifactSet(String.format("%s all artifacts", getDisplayName()), inheritedArtifacts);
+
+        resolutionStrategy.beforeChange(veto);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public State getState() {
+        synchronized (lock) {
+            return state;
+        }
+    }
+
+    public ModuleInternal getModule() {
+        return metaDataProvider.getModule();
+    }
+
+    public boolean isVisible() {
+        return visibility == Visibility.PUBLIC;
+    }
+
+    public Configuration setVisible(boolean visible) {
+        validateMutation(MutationType.CONTENT);
+        this.visibility = visible ? Visibility.PUBLIC : Visibility.PRIVATE;
+        return this;
+    }
+
+    public Set<Configuration> getExtendsFrom() {
+        return Collections.unmodifiableSet(extendsFrom);
+    }
+
+    public Configuration setExtendsFrom(Iterable<Configuration> extendsFrom) {
+        validateMutation(MutationType.CONTENT);
+        for (Configuration configuration : this.extendsFrom) {
+            inheritedArtifacts.removeCollection(configuration.getAllArtifacts());
+            inheritedDependencies.removeCollection(configuration.getAllDependencies());
+        }
+        this.extendsFrom = new HashSet<Configuration>();
+        for (Configuration configuration : extendsFrom) {
+            extendsFrom(configuration);
+        }
+        return this;
+    }
+
+    public Configuration extendsFrom(Configuration... extendsFrom) {
+        validateMutation(MutationType.CONTENT);
+        for (Configuration configuration : extendsFrom) {
+            if (configuration.getHierarchy().contains(this)) {
+                throw new InvalidUserDataException(String.format(
+                        "Cyclic extendsFrom from %s and %s is not allowed. See existing hierarchy: %s", this,
+                        configuration, configuration.getHierarchy()));
+            }
+            this.extendsFrom.add(configuration);
+            inheritedArtifacts.addCollection(configuration.getAllArtifacts());
+            inheritedDependencies.addCollection(configuration.getAllDependencies());
+        }
+        return this;
+    }
+
+    public boolean isTransitive() {
+        return transitive;
+    }
+
+    public Configuration setTransitive(boolean transitive) {
+        validateMutation(MutationType.CONTENT);
+        this.transitive = transitive;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Configuration setDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public Set<Configuration> getHierarchy() {
+        Set<Configuration> result = WrapUtil.<Configuration>toLinkedSet(this);
+        collectSuperConfigs(this, result);
+        return result;
+    }
+
+    private void collectSuperConfigs(Configuration configuration, Set<Configuration> result) {
+        for (Configuration superConfig : configuration.getExtendsFrom()) {
+            if (result.contains(superConfig)) {
+                result.remove(superConfig);
+            }
+            result.add(superConfig);
+            collectSuperConfigs(superConfig, result);
+        }
+    }
+
+    public Set<Configuration> getAll() {
+        return configurationsProvider.getAll();
+    }
+
+    public Set<File> resolve() {
+        return getFiles();
+    }
+
+    public Set<File> getFiles() {
+        return fileCollection(Specs.SATISFIES_ALL).getFiles();
+    }
+
+    public Set<File> files(Dependency... dependencies) {
+        return fileCollection(dependencies).getFiles();
+    }
+
+    public Set<File> files(Closure dependencySpecClosure) {
+        return fileCollection(dependencySpecClosure).getFiles();
+    }
+
+    public Set<File> files(Spec<? super Dependency> dependencySpec) {
+        return fileCollection(dependencySpec).getFiles();
+    }
+
+    public FileCollection fileCollection(Spec<? super Dependency> dependencySpec) {
+        return new ConfigurationFileCollection(dependencySpec);
+    }
+
+    public FileCollection fileCollection(Closure dependencySpecClosure) {
+        return new ConfigurationFileCollection(dependencySpecClosure);
+    }
+
+    public FileCollection fileCollection(Dependency... dependencies) {
+        return new ConfigurationFileCollection(WrapUtil.toLinkedSet(dependencies));
+    }
+
+    public void includedInResolveResult() {
+        includedInResult = true;
+        for (Configuration configuration : extendsFrom) {
+            ((ConfigurationInternal) configuration).includedInResolveResult();
+        }
+    }
+
+    public ResolvedConfiguration getResolvedConfiguration() {
+        resolveNow();
+        return cachedResolverResults.getResolvedConfiguration();
+    }
+
+    private void resolveNow() {
+        synchronized (lock) {
+            if (state == State.UNRESOLVED) {
+                DependencyResolutionListener broadcast = getDependencyResolutionBroadcast();
+                ResolvableDependencies incoming = getIncoming();
+                broadcast.beforeResolve(incoming);
+                cachedResolverResults = resolver.resolve(this);
+                for (Configuration configuration : extendsFrom) {
+                    ((ConfigurationInternal) configuration).includedInResolveResult();
+                }
+                if (cachedResolverResults.getResolvedConfiguration().hasError()) {
+                    state = State.RESOLVED_WITH_FAILURES;
+                } else {
+                    state = State.RESOLVED;
+                }
+                broadcast.afterResolve(incoming);
+            }
+        }
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return allDependencies.getBuildDependencies();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TaskDependency getTaskDependencyFromProjectDependency(final boolean useDependedOn, final String taskName) {
+        if (useDependedOn) {
+            return new TasksFromProjectDependencies(taskName, getAllDependencies(), projectAccessListener);
+        } else {
+            return new TasksFromDependentProjects(taskName, getName());
+        }
+    }
+
+    public DependencySet getDependencies() {
+        return dependencies;
+    }
+
+    public DependencySet getAllDependencies() {
+        return allDependencies;
+    }
+
+    public PublishArtifactSet getArtifacts() {
+        return artifacts;
+    }
+
+    public PublishArtifactSet getAllArtifacts() {
+        return allArtifacts;
+    }
+
+    public Set<ExcludeRule> getExcludeRules() {
+        return Collections.unmodifiableSet(excludeRules);
+    }
+
+    public void setExcludeRules(Set<ExcludeRule> excludeRules) {
+        validateMutation(MutationType.CONTENT);
+        this.excludeRules = excludeRules;
+    }
+
+    public DefaultConfiguration exclude(Map<String, String> excludeRuleArgs) {
+        validateMutation(MutationType.CONTENT);
+        excludeRules.add(ExcludeRuleNotationConverter.parser().parseNotation(excludeRuleArgs)); //TODO SF try using ExcludeRuleContainer
+        return this;
+    }
+
+    public String getUploadTaskName() {
+        return Configurations.uploadTaskName(getName());
+    }
+
+    public String getDisplayName() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("configuration '");
+        builder.append(path);
+        builder.append("'");
+        return builder.toString();
+    }
+
+    public ResolvableDependencies getIncoming() {
+        return resolvableDependencies;
+    }
+
+    public Configuration copy() {
+        return createCopy(getDependencies(), false);
+    }
+
+    public Configuration copyRecursive() {
+        return createCopy(getAllDependencies(), true);
+    }
+
+    public Configuration copy(Spec<? super Dependency> dependencySpec) {
+        return createCopy(CollectionUtils.filter(getDependencies(), dependencySpec), false);
+    }
+
+    public Configuration copyRecursive(Spec<? super Dependency> dependencySpec) {
+        return createCopy(CollectionUtils.filter(getAllDependencies(), dependencySpec), true);
+    }
+
+    private DefaultConfiguration createCopy(Set<Dependency> dependencies, boolean recursive) {
+        DetachedConfigurationsProvider configurationsProvider = new DetachedConfigurationsProvider();
+        DefaultConfiguration copiedConfiguration = new DefaultConfiguration(path + "Copy", name + "Copy",
+                configurationsProvider, resolver, listenerManager, metaDataProvider, resolutionStrategy.copy(), projectAccessListener);
+        configurationsProvider.setTheOnlyConfiguration(copiedConfiguration);
+        // state, cachedResolvedConfiguration, and extendsFrom intentionally not copied - must re-resolve copy
+        // copying extendsFrom could mess up dependencies when copy was re-resolved
+
+        copiedConfiguration.visibility = visibility;
+        copiedConfiguration.transitive = transitive;
+        copiedConfiguration.description = description;
+
+        copiedConfiguration.getArtifacts().addAll(getAllArtifacts());
+
+        // todo An ExcludeRule is a value object but we don't enforce immutability for DefaultExcludeRule as strong as we
+        // should (we expose the Map). We should provide a better API for ExcludeRule (I don't want to use unmodifiable Map).
+        // As soon as DefaultExcludeRule is truly immutable, we don't need to create a new instance of DefaultExcludeRule.
+        Set<Configuration> excludeRuleSources = new LinkedHashSet<Configuration>();
+        excludeRuleSources.add(this);
+        if (recursive) {
+            excludeRuleSources.addAll(getHierarchy());
+        }
+
+        for (Configuration excludeRuleSource : excludeRuleSources) {
+            for (ExcludeRule excludeRule : excludeRuleSource.getExcludeRules()) {
+                copiedConfiguration.excludeRules.add(new DefaultExcludeRule(excludeRule.getGroup(), excludeRule.getModule()));
+            }
+        }
+
+        DomainObjectSet<Dependency> copiedDependencies = copiedConfiguration.getDependencies();
+        for (Dependency dependency : dependencies) {
+            copiedDependencies.add(dependency.copy());
+        }
+        return copiedConfiguration;
+    }
+
+    public Configuration copy(Closure dependencySpec) {
+        return copy(Specs.<Dependency>convertClosureToSpec(dependencySpec));
+    }
+
+    public Configuration copyRecursive(Closure dependencySpec) {
+        return copyRecursive(Specs.<Dependency>convertClosureToSpec(dependencySpec));
+    }
+
+    public DependencyResolutionListener getDependencyResolutionBroadcast() {
+        return resolutionListenerBroadcast.getSource();
+    }
+
+    public ResolutionStrategyInternal getResolutionStrategy() {
+        return resolutionStrategy;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public Configuration resolutionStrategy(Closure closure) {
+        ConfigureUtil.configure(closure, resolutionStrategy);
+        return this;
+    }
+
+    private void validateMutation(MutationType type) {
+        boolean userAlreadyNagged = false;
+        if (getState() != State.UNRESOLVED) {
+            if (type == MutationType.CONTENT) {
+                throw new InvalidUserDataException(String.format("Cannot change %s after it has been resolved.", getDisplayName()));
+            } else {
+                userAlreadyNagged = true;
+                DeprecationLogger.nagUserOfDeprecatedBehaviour(String.format("Attempting to change %s after it has been resolved", getDisplayName()));
+            }
+        }
+        if (!userAlreadyNagged && includedInResult) {
+            DeprecationLogger.nagUserOfDeprecatedBehaviour(String.format("Attempting to change %s after it has been included in dependency resolution", getDisplayName()));
+        }
+    }
+
+    class ConfigurationFileCollection extends AbstractFileCollection {
+        private Spec<? super Dependency> dependencySpec;
+
+        private ConfigurationFileCollection(Spec<? super Dependency> dependencySpec) {
+            this.dependencySpec = dependencySpec;
+        }
+
+        public ConfigurationFileCollection(Closure dependencySpecClosure) {
+            this.dependencySpec = Specs.convertClosureToSpec(dependencySpecClosure);
+        }
+
+        public ConfigurationFileCollection(final Set<Dependency> dependencies) {
+            this.dependencySpec = new Spec<Dependency>() {
+                public boolean isSatisfiedBy(Dependency element) {
+                    return dependencies.contains(element);
+                }
+            };
+        }
+
+        @Override
+        public TaskDependency getBuildDependencies() {
+            return DefaultConfiguration.this.getBuildDependencies();
+        }
+
+        public Spec<? super Dependency> getDependencySpec() {
+            return dependencySpec;
+        }
+
+        public String getDisplayName() {
+            return String.format("%s dependencies", DefaultConfiguration.this);
+        }
+
+        public Set<File> getFiles() {
+            synchronized (lock) {
+                ResolvedConfiguration resolvedConfiguration = getResolvedConfiguration();
+                if (getState() == State.RESOLVED_WITH_FAILURES) {
+                    resolvedConfiguration.rethrowFailure();
+                }
+                return resolvedConfiguration.getFiles(dependencySpec);
+            }
+        }
+    }
+
+    /**
+     * Print a formatted representation of a Configuration
+     */
+    public String dump() {
+        StringBuilder reply = new StringBuilder();
+
+        reply.append("\nConfiguration:");
+        reply.append("  class='" + this.getClass() + "'");
+        reply.append("  name='" + this.getName() + "'");
+        reply.append("  hashcode='" + this.hashCode() + "'");
+
+        reply.append("\nLocal Dependencies:");
+        if (getDependencies().size() > 0) {
+            for (Dependency d : getDependencies()) {
+                reply.append("\n   " + d);
+            }
+        } else {
+            reply.append("\n   none");
+        }
+
+        reply.append("\nLocal Artifacts:");
+        if (getArtifacts().size() > 0) {
+            for (PublishArtifact a : getArtifacts()) {
+                reply.append("\n   " + a);
+            }
+        } else {
+            reply.append("\n   none");
+        }
+
+        reply.append("\nAll Dependencies:");
+        if (getAllDependencies().size() > 0) {
+            for (Dependency d : getAllDependencies()) {
+                reply.append("\n   " + d);
+            }
+        } else {
+            reply.append("\n   none");
+        }
+
+
+        reply.append("\nAll Artifacts:");
+        if (getAllArtifacts().size() > 0) {
+            for (PublishArtifact a : getAllArtifacts()) {
+                reply.append("\n   " + a);
+            }
+        } else {
+            reply.append("\n   none");
+        }
+
+        return reply.toString();
+    }
+
+    private class ConfigurationResolvableDependencies implements ResolvableDependencies {
+        public String getName() {
+            return name;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("dependencies '%s'", path);
+        }
+
+        public FileCollection getFiles() {
+            return DefaultConfiguration.this.fileCollection(Specs.<Dependency>satisfyAll());
+        }
+
+        public DependencySet getDependencies() {
+            return getAllDependencies();
+        }
+
+        public void beforeResolve(Action<? super ResolvableDependencies> action) {
+            resolutionListenerBroadcast.add("beforeResolve", action);
+        }
+
+        public void beforeResolve(Closure action) {
+            resolutionListenerBroadcast.add(new ClosureBackedMethodInvocationDispatch("beforeResolve", action));
+        }
+
+        public void afterResolve(Action<? super ResolvableDependencies> action) {
+            resolutionListenerBroadcast.add("afterResolve", action);
+        }
+
+        public void afterResolve(Closure action) {
+            resolutionListenerBroadcast.add(new ClosureBackedMethodInvocationDispatch("afterResolve", action));
+        }
+
+        public ResolutionResult getResolutionResult() {
+            DefaultConfiguration.this.resolveNow();
+            return DefaultConfiguration.this.cachedResolverResults.getResolutionResult();
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
new file mode 100644
index 0000000..369605c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.configurations;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.UnknownDomainObjectException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.UnknownConfigurationException;
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy;
+import org.gradle.initialization.ProjectAccessListener;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.event.ListenerManager;
+
+import java.util.Collection;
+import java.util.Set;
+
+public class DefaultConfigurationContainer extends AbstractNamedDomainObjectContainer<Configuration>
+        implements ConfigurationContainerInternal, ConfigurationsProvider {
+    public static final String DETACHED_CONFIGURATION_DEFAULT_NAME = "detachedConfiguration";
+    
+    private final ConfigurationResolver resolver;
+    private final Instantiator instantiator;
+    private final DomainObjectContext context;
+    private final ListenerManager listenerManager;
+    private final DependencyMetaDataProvider dependencyMetaDataProvider;
+    private final ProjectAccessListener projectAccessListener;
+
+    private int detachedConfigurationDefaultNameCounter = 1;
+
+    public DefaultConfigurationContainer(ConfigurationResolver resolver,
+                                         Instantiator instantiator, DomainObjectContext context, ListenerManager listenerManager,
+                                         DependencyMetaDataProvider dependencyMetaDataProvider, ProjectAccessListener projectAccessListener) {
+        super(Configuration.class, instantiator, new Configuration.Namer());
+        this.resolver = resolver;
+        this.instantiator = instantiator;
+        this.context = context;
+        this.listenerManager = listenerManager;
+        this.dependencyMetaDataProvider = dependencyMetaDataProvider;
+        this.projectAccessListener = projectAccessListener;
+    }
+
+    @Override
+    protected Configuration doCreate(String name) {
+        return instantiator.newInstance(DefaultConfiguration.class, context.absoluteProjectPath(name), name, this, resolver,
+                listenerManager, dependencyMetaDataProvider, instantiator.newInstance(DefaultResolutionStrategy.class), projectAccessListener);
+    }
+
+    public Set<Configuration> getAll() {
+        return this;
+    }
+
+    @Override
+    public ConfigurationInternal getByName(String name) {
+        return (ConfigurationInternal) super.getByName(name);
+    }
+
+    @Override
+    public String getTypeDisplayName() {
+        return "configuration";
+    }
+
+    @Override
+    protected UnknownDomainObjectException createNotFoundException(String name) {
+        return new UnknownConfigurationException(String.format("Configuration with name '%s' not found.", name));
+    }
+
+    public ConfigurationInternal detachedConfiguration(Dependency... dependencies) {
+        String name = DETACHED_CONFIGURATION_DEFAULT_NAME + detachedConfigurationDefaultNameCounter++;
+        DetachedConfigurationsProvider detachedConfigurationsProvider = new DetachedConfigurationsProvider();
+        DefaultConfiguration detachedConfiguration = new DefaultConfiguration(
+                name, name, detachedConfigurationsProvider, resolver,
+                listenerManager, dependencyMetaDataProvider, new DefaultResolutionStrategy(), projectAccessListener);
+        DomainObjectSet<Dependency> detachedDependencies = detachedConfiguration.getDependencies();
+        for (Dependency dependency : dependencies) {
+            detachedDependencies.add(dependency.copy());
+        }
+        detachedConfigurationsProvider.setTheOnlyConfiguration(detachedConfiguration);
+        return detachedConfiguration;
+    }
+    
+    /**
+     * Build a formatted representation of all Configurations in this ConfigurationContainer.
+     * Configuration(s) being toStringed are likely derivations of DefaultConfiguration.
+     */
+    public String dump() {
+        StringBuilder reply = new StringBuilder();
+        
+        reply.append("Configuration of type: " + getTypeDisplayName());
+        Collection<Configuration> configs = getAll();
+        for (Configuration c : configs) {
+            reply.append("\n  " + c.toString());
+        }
+        
+        return reply.toString();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java
new file mode 100644
index 0000000..4a9bfda
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+/**
+ * Used to validate mutation of an object and its sub-parts.
+ */
+public interface MutationValidator {
+    enum MutationType {
+        /**
+         * The mutation of the resolution strategy of the configuration, i.e. caching, resolution rules etc.
+         */
+        STRATEGY,
+
+        /**
+         * The mutation of the content of the configuration, i.e. dependencies, artifacts, extended configurations etc.
+         */
+        CONTENT
+    }
+
+    /**
+     * Check if mutation is allowed.
+     *
+     * @param type the type of mutation to validate.
+     */
+    void validateMutation(MutationType type);
+
+    static final MutationValidator IGNORE = new MutationValidator() {
+        @Override
+        public void validateMutation(MutationType type) {
+        }
+    };
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
new file mode 100644
index 0000000..944fc1a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.configurations;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ConflictResolution;
+import org.gradle.api.artifacts.DependencySubstitution;
+import org.gradle.api.artifacts.ResolutionStrategy;
+import org.gradle.api.artifacts.cache.ResolutionRules;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DependencySubstitutionsInternal;
+
+public interface ResolutionStrategyInternal extends ResolutionStrategy {
+
+    /**
+     * Gets the current expiry policy for dynamic revisions.
+     *
+     * @return the expiry policy
+     */
+    CachePolicy getCachePolicy();
+
+    /**
+     * Until the feature 'settles' and we receive more feedback, it's internal
+     *
+     * @return conflict resolution
+     */
+    ConflictResolution getConflictResolution();
+
+    /**
+     * The nascent DSL for cache control, and possibly other per-module resolution overrides
+     * @return the resolution rules
+     */
+    ResolutionRules getResolutionRules();
+
+    /**
+     * @return the dependency substitution rule (may aggregate multiple rules)
+     */
+    Action<DependencySubstitution<ComponentSelector>> getDependencySubstitutionRule();
+
+    /**
+     * @return the version selection rules object
+     */
+    ComponentSelectionRulesInternal getComponentSelection();
+
+    DependencySubstitutionsInternal getDependencySubstitution();
+
+    /**
+     * @return copy of this resolution strategy. See the contract of {@link org.gradle.api.artifacts.Configuration#copy()}.
+     */
+    ResolutionStrategyInternal copy();
+
+    /**
+     * Sets the validator to invoke before mutation. Any exception thrown by the action will veto the mutation.
+     */
+    void beforeChange(MutationValidator action);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RunnableMutationValidator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RunnableMutationValidator.java
new file mode 100644
index 0000000..7468972
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RunnableMutationValidator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+/**
+ * A {@link MutationValidator} that can be used as a {@link Runnable} to be passed to
+ * {@link org.gradle.api.internal.DefaultDomainObjectCollection#beforeChange(Runnable)}.
+ */
+abstract public class RunnableMutationValidator implements MutationValidator, Runnable {
+    private final MutationType typeAsRunnable;
+
+    public RunnableMutationValidator(MutationType typeAsRunnable) {
+        this.typeAsRunnable = typeAsRunnable;
+    }
+
+    @Override
+    public void run() {
+        validateMutation(typeAsRunnable);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjects.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjects.java
new file mode 100644
index 0000000..255ad65
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjects.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.tasks.AbstractTaskDependency;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+
+import java.util.Set;
+
+class TasksFromDependentProjects extends AbstractTaskDependency {
+
+    private final String taskName;
+    private final String configurationName;
+    private final TaskDependencyChecker checker;
+
+    public TasksFromDependentProjects(String taskName, String configurationName) {
+        this(taskName, configurationName, new TaskDependencyChecker());
+    }
+
+    public TasksFromDependentProjects(String taskName, String configurationName, TaskDependencyChecker checker) {
+        this.taskName = taskName;
+        this.configurationName = configurationName;
+        this.checker = checker;
+    }
+
+    public void resolve(TaskDependencyResolveContext context) {
+        Project thisProject = context.getTask().getProject();
+        Set<Task> tasksWithName = thisProject.getRootProject().getTasksByName(taskName, true);
+        for (Task nextTask : tasksWithName) {
+            boolean isDependency = checker.isDependent(thisProject, configurationName, nextTask.getProject());
+            if (isDependency) {
+                context.add(nextTask);
+            }
+        }
+    }
+
+    static class TaskDependencyChecker {
+        //checks if candidate project is dependent of the origin project with given configuration
+        boolean isDependent(Project originProject, String configurationName, Project candidateProject) {
+            Configuration configuration = candidateProject.getConfigurations().findByName(configurationName);
+            return configuration != null && doesConfigurationDependOnProject(configuration, originProject);
+        }
+
+        private static boolean doesConfigurationDependOnProject(Configuration configuration, Project project) {
+            Set<ProjectDependency> projectDependencies = configuration.getAllDependencies().withType(ProjectDependency.class);
+            for (ProjectDependency projectDependency : projectDependencies) {
+                if (projectDependency.getDependencyProject().equals(project)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public String getTaskName() {
+        return taskName;
+    }
+
+    public String getConfigurationName() {
+        return configurationName;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependencies.java
new file mode 100644
index 0000000..f3a1bc9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependencies.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.DependencySet;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.AbstractTaskDependency;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+import org.gradle.initialization.ProjectAccessListener;
+
+import java.util.Set;
+
+class TasksFromProjectDependencies extends AbstractTaskDependency {
+    private final String taskName;
+    private final DependencySet dependencies;
+    private final ProjectAccessListener projectAccessListener;
+
+    public TasksFromProjectDependencies(String taskName, DependencySet dependencies, ProjectAccessListener projectAccessListener) {
+        this.taskName = taskName;
+        this.dependencies = dependencies;
+        this.projectAccessListener = projectAccessListener;
+    }
+
+    public void resolve(TaskDependencyResolveContext context) {
+        resolveProjectDependencies(context, dependencies.withType(ProjectDependency.class));
+    }
+
+    void resolveProjectDependencies(TaskDependencyResolveContext context, Set<ProjectDependency> projectDependencies) {
+        for (ProjectDependency projectDependency : projectDependencies) {
+            projectAccessListener.beforeResolvingProjectDependency((ProjectInternal) projectDependency.getDependencyProject());
+
+            Task nextTask = projectDependency.getDependencyProject().getTasks().findByName(taskName);
+            if (nextTask != null) {
+                context.add(nextTask);
+            }
+        }
+    }
+
+    public String getTaskName() {
+        return taskName;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainer.java
new file mode 100644
index 0000000..8f7563c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.base.Joiner;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ComponentModuleMetadataDetails;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.notations.ModuleIdentiferNotationConverter;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static java.lang.String.format;
+
+public class ComponentModuleMetadataContainer implements ModuleReplacementsData {
+
+    private final Map<ModuleIdentifier, ModuleIdentifier> replacements = newHashMap();
+
+    public ComponentModuleMetadataDetails module(final Object sourceModule) {
+        final NotationParser<Object, ModuleIdentifier> parser = parser();
+        final ModuleIdentifier source = parser.parseNotation(sourceModule);
+        return new ComponentModuleMetadataDetails() {
+            public void replacedBy(final Object targetModule) {
+                ModuleIdentifier target = parser.parseNotation(targetModule);
+                detectCycles(replacements, source, target);
+                replacements.put(source, target);
+            }
+
+            public ModuleIdentifier getId() {
+                return source;
+            }
+
+            public ModuleIdentifier getReplacedBy() {
+                return replacements.get(source);
+            }
+        };
+    }
+
+    public ModuleIdentifier getReplacementFor(ModuleIdentifier sourceModule) {
+        return replacements.get(sourceModule);
+    }
+
+    private static void detectCycles(Map<ModuleIdentifier, ModuleIdentifier> replacements, ModuleIdentifier source, ModuleIdentifier target) {
+        if (source.equals(target)) {
+            throw new InvalidUserDataException(String.format("Cannot declare module replacement that replaces self: %s->%s", source, target));
+        }
+
+        ModuleIdentifier m = replacements.get(target);
+        if (m == null) {
+            //target does not exist in the map, there's no cycle for sure
+            return;
+        }
+        Set<ModuleIdentifier> visited = new LinkedHashSet<ModuleIdentifier>();
+        visited.add(source);
+        visited.add(target);
+
+        while(m != null) {
+            if (!visited.add(m)) {
+                //module was already visited, there is a cycle
+                throw new InvalidUserDataException(
+                        format("Cannot declare module replacement %s->%s because it introduces a cycle: %s",
+                                source, target, Joiner.on("->").join(visited) + "->" + source));
+            }
+            m = replacements.get(m);
+        }
+    }
+
+    private static NotationParser<Object, ModuleIdentifier> parser() {
+        return NotationParserBuilder
+                .toType(ModuleIdentifier.class)
+                .converter(new ModuleIdentiferNotationConverter())
+                .toComposite();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsers.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsers.java
new file mode 100644
index 0000000..4d4a774
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsers.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.IllegalDependencyNotation;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+
+import java.util.Set;
+
+import static org.gradle.internal.component.external.model.DefaultModuleComponentSelector.newSelector;
+
+public class ComponentSelectorParsers {
+
+    private static final NotationParserBuilder<ComponentSelector> BUILDER = NotationParserBuilder
+            .toType(ComponentSelector.class)
+            .fromCharSequence(new StringConverter())
+            .converter(new MapConverter())
+            .fromType(Project.class, new ProjectConverter());
+
+    public static NotationParser<Object, Set<ComponentSelector>> multiParser() {
+        return builder().toFlatteningComposite();
+    }
+
+    public static NotationParser<Object, ComponentSelector> parser() {
+        return builder().toComposite();
+    }
+
+    private static NotationParserBuilder<ComponentSelector> builder() {
+        return BUILDER;
+    }
+
+    static class MapConverter extends MapNotationConverter<ComponentSelector> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("Maps, e.g. [group: 'org.gradle', name:'gradle-core', version: '1.0'].");
+        }
+
+        protected ModuleComponentSelector parseMap(@MapKey("group") String group, @MapKey("name") String name, @MapKey("version") String version) {
+            return newSelector(group, name, version);
+        }
+    }
+
+    static class StringConverter implements NotationConverter<String, ComponentSelector> {
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("String or CharSequence values, e.g. 'org.gradle:gradle-core:1.0'.");
+        }
+
+        public void convert(String notation, NotationConvertResult<? super ComponentSelector> result) throws TypeConversionException {
+            ParsedModuleStringNotation parsed;
+            try {
+                parsed = new ParsedModuleStringNotation(notation, null);
+            } catch (IllegalDependencyNotation e) {
+                throw new InvalidUserDataException(
+                        "Invalid format: '" + notation + "'. The correct notation is a 3-part group:name:version notation, "
+                                + "e.g: 'org.gradle:gradle-core:1.0'");
+            }
+
+            if (parsed.getGroup() == null || parsed.getName() == null || parsed.getVersion() == null) {
+                throw new InvalidUserDataException(
+                        "Invalid format: '" + notation + "'. Group, name and version cannot be empty. Correct example: "
+                                + "'org.gradle:gradle-core:1.0'");
+            }
+            result.converted(newSelector(parsed.getGroup(), parsed.getName(), parsed.getVersion()));
+        }
+    }
+
+    static class ProjectConverter implements NotationConverter<Project, ComponentSelector> {
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("Project objects, e.g. project(':api').");
+        }
+
+        @Override
+        public void convert(Project notation, NotationConvertResult<? super ComponentSelector> result) throws TypeConversionException {
+            result.converted(DefaultProjectComponentSelector.newSelector(notation.getPath()));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
new file mode 100644
index 0000000..d5dc32a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 groovy.lang.Closure;
+import groovy.lang.MissingMethodException;
+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.internal.typeconversion.NotationParser;
+import org.gradle.util.ConfigureUtil;
+import org.gradle.util.GUtil;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DefaultArtifactHandler implements ArtifactHandler {
+
+    private final ConfigurationContainer configurationContainer;
+    private final NotationParser<Object, PublishArtifact> publishArtifactFactory;
+
+    public DefaultArtifactHandler(ConfigurationContainer configurationContainer, NotationParser<Object, PublishArtifact> publishArtifactFactory) {
+        this.configurationContainer = configurationContainer;
+        this.publishArtifactFactory = publishArtifactFactory;
+    }
+
+    private PublishArtifact pushArtifact(org.gradle.api.artifacts.Configuration configuration, Object notation, Closure configureClosure) {
+        PublishArtifact publishArtifact = publishArtifactFactory.parseNotation(notation);
+        configuration.getArtifacts().add(publishArtifact);
+        ConfigureUtil.configure(configureClosure, publishArtifact);
+        return publishArtifact;
+    }
+
+    public PublishArtifact add(String configurationName, Object artifactNotation) {
+        return pushArtifact(configurationContainer.getByName(configurationName), artifactNotation, null);
+    }
+
+    public PublishArtifact add(String configurationName, Object artifactNotation, Closure configureClosure) {
+        return pushArtifact(configurationContainer.getByName(configurationName), artifactNotation, configureClosure);
+    }
+
+    public Object methodMissing(String name, Object arg) {
+        Object[] args = (Object[]) arg;
+        Configuration configuration = configurationContainer.findByName(name);
+        if (configuration == null) {
+            throw new MissingMethodException(name, this.getClass(), args);
+        }
+        List<Object> normalizedArgs = GUtil.flatten(Arrays.asList(args), false);
+        if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
+            return pushArtifact(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1));
+        }
+        for (Object notation : args) {
+            pushArtifact(configuration, notation, null);
+        }
+        return null;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java
new file mode 100644
index 0000000..c52742b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandler.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.api.artifacts.ComponentMetadataDetails;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataDetailsAdapter;
+import org.gradle.api.internal.notations.ModuleIdentiferNotationConverter;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.component.external.model.IvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.rules.*;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+import org.gradle.internal.typeconversion.UnsupportedNotationException;
+
+import java.util.List;
+import java.util.Set;
+
+public class DefaultComponentMetadataHandler implements ComponentMetadataHandler, ComponentMetadataProcessor {
+    private final Instantiator instantiator;
+
+    private static final String INVALID_SPEC_ERROR = "Could not add a component metadata rule for module '%s'.";
+    private final Set<SpecRuleAction<? super ComponentMetadataDetails>> rules = Sets.newLinkedHashSet();
+    private final RuleActionAdapter<ComponentMetadataDetails> ruleActionAdapter;
+    private final NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser;
+
+    public DefaultComponentMetadataHandler(Instantiator instantiator, RuleActionAdapter<ComponentMetadataDetails> ruleActionAdapter, NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser) {
+        this.instantiator = instantiator;
+        this.ruleActionAdapter = ruleActionAdapter;
+        this.moduleIdentifierNotationParser = moduleIdentifierNotationParser;
+    }
+
+    public DefaultComponentMetadataHandler(Instantiator instantiator) {
+        this(instantiator, createAdapter(), createModuleIdentifierNotationParser());
+    }
+
+    private static RuleActionAdapter<ComponentMetadataDetails> createAdapter() {
+        List<Class<?>> parameterTypes = Lists.newArrayList();
+        parameterTypes.add(IvyModuleDescriptor.class);
+        RuleActionValidator<ComponentMetadataDetails> ruleActionValidator = new DefaultRuleActionValidator<ComponentMetadataDetails>(parameterTypes);
+        return new DefaultRuleActionAdapter<ComponentMetadataDetails>(ruleActionValidator, ComponentMetadataHandler.class.getSimpleName());
+    }
+
+    private static NotationParser<Object, ModuleIdentifier> createModuleIdentifierNotationParser() {
+        return NotationParserBuilder
+                .toType(ModuleIdentifier.class)
+                .converter(new ModuleIdentiferNotationConverter())
+                .toComposite();
+    }
+
+    private ComponentMetadataHandler addRule(SpecRuleAction<? super ComponentMetadataDetails> ruleAction) {
+        rules.add(ruleAction);
+        return this;
+    }
+
+    private SpecRuleAction<? super ComponentMetadataDetails> createAllSpecRuleAction(RuleAction<? super ComponentMetadataDetails> ruleAction) {
+        return new SpecRuleAction<ComponentMetadataDetails>(ruleAction, Specs.<ComponentMetadataDetails>satisfyAll());
+    }
+
+    private SpecRuleAction<? super ComponentMetadataDetails> createSpecRuleActionForModule(Object id, RuleAction<? super ComponentMetadataDetails> ruleAction) {
+        ModuleIdentifier moduleIdentifier;
+
+        try {
+            moduleIdentifier = moduleIdentifierNotationParser.parseNotation(id);
+        } catch (UnsupportedNotationException e) {
+            throw new InvalidUserCodeException(String.format(INVALID_SPEC_ERROR, id == null ? "null" : id.toString()), e);
+        }
+
+        Spec<ComponentMetadataDetails> spec = new ComponentMetadataDetailsMatchingSpec(moduleIdentifier);
+        return new SpecRuleAction<ComponentMetadataDetails>(ruleAction, spec);
+    }
+
+    public ComponentMetadataHandler all(Action<? super ComponentMetadataDetails> rule) {
+        return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromAction(rule)));
+    }
+
+    public ComponentMetadataHandler all(Closure<?> rule) {
+        return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromClosure(ComponentMetadataDetails.class, rule)));
+    }
+
+    public ComponentMetadataHandler all(Object ruleSource) {
+        return addRule(createAllSpecRuleAction(ruleActionAdapter.createFromRuleSource(ComponentMetadataDetails.class, ruleSource)));
+    }
+
+    public ComponentMetadataHandler withModule(Object id, Action<? super ComponentMetadataDetails> rule) {
+        return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromAction(rule)));
+    }
+
+    public ComponentMetadataHandler withModule(Object id, Closure<?> rule) {
+        return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromClosure(ComponentMetadataDetails.class, rule)));
+    }
+
+    public ComponentMetadataHandler withModule(Object id, Object ruleSource) {
+        return addRule(createSpecRuleActionForModule(id, ruleActionAdapter.createFromRuleSource(ComponentMetadataDetails.class, ruleSource)));
+    }
+
+    public void processMetadata(MutableModuleComponentResolveMetaData metadata) {
+        ComponentMetadataDetails details = instantiator.newInstance(ComponentMetadataDetailsAdapter.class, metadata);
+        processAllRules(metadata, details);
+        if (!metadata.getStatusScheme().contains(metadata.getStatus())) {
+            throw new ModuleVersionResolveException(metadata.getId(), String.format("Unexpected status '%s' specified for %s. Expected one of: %s", metadata.getStatus(), metadata.getComponentId().getDisplayName(), metadata.getStatusScheme()));
+        }
+    }
+
+    private void processAllRules(ModuleComponentResolveMetaData metadata, ComponentMetadataDetails details) {
+        for (SpecRuleAction<? super ComponentMetadataDetails> rule : rules) {
+            processRule(rule, metadata, details);
+        }
+    }
+
+    private void processRule(SpecRuleAction<? super ComponentMetadataDetails> specRuleAction, ModuleComponentResolveMetaData metadata, ComponentMetadataDetails details) {
+        if (!specRuleAction.getSpec().isSatisfiedBy(details)) {
+            return;
+        }
+
+        List<Object> inputs = Lists.newArrayList();
+        for (Class<?> inputType : specRuleAction.getAction().getInputTypes()) {
+            if (inputType == IvyModuleDescriptor.class) {
+                // Ignore the rule if it expects Ivy metadata and this isn't an Ivy module
+                if (!(metadata instanceof IvyModuleResolveMetaData)) {
+                    return;
+                }
+
+                IvyModuleResolveMetaData ivyMetadata = (IvyModuleResolveMetaData) metadata;
+                inputs.add(new DefaultIvyModuleDescriptor(ivyMetadata.getExtraInfo(), ivyMetadata.getBranch(), ivyMetadata.getStatus()));
+                continue;
+            }
+
+            // We've already validated the inputs: should never get here.
+            throw new IllegalStateException();
+        }
+
+        try {
+            specRuleAction.getAction().execute(details, inputs);
+        } catch (Exception e) {
+            throw new InvalidUserCodeException(String.format("There was an error while evaluating a component metadata rule for %s.", details.getId()), e);
+        }
+    }
+
+    static class ComponentMetadataDetailsMatchingSpec implements Spec<ComponentMetadataDetails> {
+        private ModuleIdentifier target;
+
+        ComponentMetadataDetailsMatchingSpec(ModuleIdentifier target) {
+            this.target = target;
+        }
+
+        public boolean isSatisfiedBy(ComponentMetadataDetails componentMetadataDetails) {
+            return componentMetadataDetails.getId().getGroup().equals(target.getGroup()) && componentMetadataDetails.getId().getName().equals(target.getName());
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentModuleMetadataHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentModuleMetadataHandler.java
new file mode 100644
index 0000000..068799a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultComponentModuleMetadataHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentModuleMetadata;
+import org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler;
+import org.gradle.api.internal.artifacts.ComponentModuleMetadataProcessor;
+
+public class DefaultComponentModuleMetadataHandler implements ComponentModuleMetadataHandler, ComponentModuleMetadataProcessor {
+    private final ComponentModuleMetadataContainer moduleMetadataContainer = new ComponentModuleMetadataContainer();
+
+    public void module(Object moduleNotation, Action<? super ComponentModuleMetadata> rule) {
+        rule.execute(moduleMetadataContainer.module(moduleNotation));
+    }
+
+    public ModuleReplacementsData getModuleReplacements() {
+        return moduleMetadataContainer;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleReplacementsData.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleReplacementsData.java
new file mode 100644
index 0000000..0ab0603
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleReplacementsData.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.api.artifacts.ModuleIdentifier;
+
+public interface ModuleReplacementsData {
+    @Nullable ModuleIdentifier getReplacementFor(ModuleIdentifier sourceModule);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
new file mode 100644
index 0000000..3553acf
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsers.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.dsl;
+
+import org.gradle.api.IllegalDependencyNotation;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+
+import java.util.Set;
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector;
+
+public class ModuleVersionSelectorParsers {
+
+    private static final NotationParserBuilder<ModuleVersionSelector> BUILDER = NotationParserBuilder
+            .toType(ModuleVersionSelector.class)
+            .fromCharSequence(new StringConverter())
+            .converter(new MapConverter());
+
+    public static NotationParser<Object, Set<ModuleVersionSelector>> multiParser() {
+        return builder().toFlatteningComposite();
+    }
+
+    public static NotationParser<Object, ModuleVersionSelector> parser() {
+        return builder().toComposite();
+    }
+
+    private static NotationParserBuilder<ModuleVersionSelector> builder() {
+        return BUILDER;
+    }
+
+    static class MapConverter extends MapNotationConverter<ModuleVersionSelector> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Maps").example("[group: 'org.gradle', name:'gradle-core', version: '1.0']");
+        }
+
+        protected ModuleVersionSelector parseMap(@MapKey("group") String group, @MapKey("name") String name, @MapKey("version") String version) {
+            return newSelector(group, name, version);
+        }
+    }
+
+    static class StringConverter implements NotationConverter<String, ModuleVersionSelector> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("String or CharSequence values").example("'org.gradle:gradle-core:1.0'");
+        }
+
+        public void convert(String notation, NotationConvertResult<? super ModuleVersionSelector> result) throws TypeConversionException {
+            ParsedModuleStringNotation parsed;
+            try {
+                parsed = new ParsedModuleStringNotation(notation, null);
+            } catch (IllegalDependencyNotation e) {
+                throw new InvalidUserDataException(
+                        "Invalid format: '" + notation + "'. The correct notation is a 3-part group:name:version notation, "
+                                + "e.g: 'org.gradle:gradle-core:1.0'");
+            }
+
+            if (parsed.getGroup() == null || parsed.getName() == null || parsed.getVersion() == null) {
+                throw new InvalidUserDataException(
+                        "Invalid format: '" + notation + "'. Group, name and version cannot be empty. Correct example: "
+                                + "'org.gradle:gradle-core:1.0'");
+            }
+            result.converted(newSelector(parsed.getGroup(), parsed.getName(), parsed.getVersion()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
new file mode 100644
index 0000000..1657068
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ParsedModuleStringNotation.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dsl;
+
+import org.gradle.api.IllegalDependencyNotation;
+import org.gradle.util.GUtil;
+
+public class ParsedModuleStringNotation {
+    private String group;
+    private String name;
+    private String version;
+    private String classifier;
+    private String artifactType;
+
+    public ParsedModuleStringNotation(String moduleNotation, String artifactType) {
+        assignValuesFromModuleNotation(moduleNotation);
+        this.artifactType = artifactType;
+    }
+
+    private void assignValuesFromModuleNotation(String moduleNotation) {
+        String[] moduleNotationParts = moduleNotation.split(":");
+        if (moduleNotationParts.length < 2 || moduleNotationParts.length > 4) {
+            throw new IllegalDependencyNotation("Supplied String module notation '" + moduleNotation + "' is invalid. Example notations: 'org.gradle:gradle-core:2.2', 'org.mockito:mockito-core:1.9.5:javadoc'.");
+        }
+        group = GUtil.elvis(moduleNotationParts[0], null);
+        name = moduleNotationParts[1];
+        version = moduleNotationParts.length == 2 ? null : moduleNotationParts[2];
+        if (moduleNotationParts.length == 4) {
+            classifier = moduleNotationParts[3];
+        }
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getClassifier() {
+        return classifier;
+    }
+
+    public String getArtifactType() {
+        return artifactType;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
new file mode 100644
index 0000000..b9e8db8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationParserFactory.java
@@ -0,0 +1,94 @@
+/*
+ * 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.dsl;
+
+import org.apache.tools.ant.Task;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
+import org.gradle.api.tasks.bundling.AbstractArchiveTask;
+import org.gradle.internal.Factory;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.*;
+
+import java.io.File;
+
+public class PublishArtifactNotationParserFactory implements Factory<NotationParser<Object, PublishArtifact>> {
+    private final Instantiator instantiator;
+    private final DependencyMetaDataProvider metaDataProvider;
+
+    public PublishArtifactNotationParserFactory(Instantiator instantiator, DependencyMetaDataProvider metaDataProvider) {
+        this.instantiator = instantiator;
+        this.metaDataProvider = metaDataProvider;
+    }
+
+    public NotationParser<Object, PublishArtifact> create() {
+        FileNotationConverter fileConverter = new FileNotationConverter();
+        return NotationParserBuilder
+                .toType(PublishArtifact.class)
+                .converter(new ArchiveTaskNotationConverter())
+                .converter(new FileMapNotationConverter(fileConverter))
+                .converter(fileConverter)
+                .toComposite();
+    }
+
+    private class ArchiveTaskNotationConverter extends TypedNotationConverter<AbstractArchiveTask, PublishArtifact> {
+        private ArchiveTaskNotationConverter() {
+            super(AbstractArchiveTask.class);
+        }
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Instances of AbstractArchiveTask").example("jar.");
+        }
+
+        @Override
+        protected PublishArtifact parseType(AbstractArchiveTask notation) {
+            return instantiator.newInstance(ArchivePublishArtifact.class, notation);
+        }
+    }
+
+    private class FileMapNotationConverter extends MapNotationConverter<PublishArtifact> {
+        private final FileNotationConverter fileConverter;
+
+        private FileMapNotationConverter(FileNotationConverter fileConverter) {
+            this.fileConverter = fileConverter;
+        }
+
+        protected PublishArtifact parseMap(@MapKey("file") File file) {
+            return fileConverter.parseType(file);
+        }
+    }
+
+    private class FileNotationConverter extends TypedNotationConverter<File, PublishArtifact> {
+        private FileNotationConverter() {
+            super(File.class);
+        }
+
+        @Override
+        protected PublishArtifact parseType(File file) {
+            Module module = metaDataProvider.getModule();
+            ArtifactFile artifactFile = new ArtifactFile(file, module.getVersion());
+            return instantiator.newInstance(DefaultPublishArtifact.class, artifactFile.getName(), artifactFile.getExtension(),
+                                            artifactFile.getExtension() == null? "":artifactFile.getExtension(),
+                                            artifactFile.getClassifier(), null, file, new Task[0]);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/AbstractDependencySubstitution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/AbstractDependencySubstitution.java
new file mode 100644
index 0000000..2072fcb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/AbstractDependencySubstitution.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.dsl.ComponentSelectorParsers;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+
+public abstract class AbstractDependencySubstitution<T extends ComponentSelector> implements DependencySubstitutionInternal<T> {
+    private final T requested;
+    private final ModuleVersionSelector oldRequested;
+    private ComponentSelectionReason selectionReason;
+    private ComponentSelector target;
+
+    public AbstractDependencySubstitution(T requested, ModuleVersionSelector oldRequested) {
+        this.requested = requested;
+        this.target = requested;
+        this.oldRequested = oldRequested;
+    }
+
+    @Override
+    public T getRequested() {
+        return requested;
+    }
+
+    @Override
+    public ModuleVersionSelector getOldRequested() {
+        return oldRequested;
+    }
+
+    @Override
+    public void useTarget(Object notation) {
+        useTarget(notation, VersionSelectionReasons.SELECTED_BY_RULE);
+    }
+
+    @Override
+    public void useTarget(Object notation, ComponentSelectionReason selectionReason) {
+        this.target = ComponentSelectorParsers.parser().parseNotation(notation);
+        this.selectionReason = selectionReason;
+    }
+
+    @Override
+    public ComponentSelectionReason getSelectionReason() {
+        return selectionReason;
+    }
+
+    @Override
+    public ComponentSelector getTarget() {
+        return target;
+    }
+
+    @Override
+    public boolean isUpdated() {
+        return selectionReason != null;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactCacheMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCacheMetaData.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactCacheMetaData.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCacheMetaData.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
new file mode 100644
index 0000000..0be73ac
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.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;
+
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+
+public enum CacheLayout {
+    ROOT(null, "modules", 2),
+    FILE_STORE(ROOT, "files", 1),
+    META_DATA(ROOT, "metadata", 15);
+
+    // If you update the META_DATA version, also update DefaultGradleDistribution.getArtifactCacheLayoutVersion() (which is the historical record)
+    // If you update FILE_STORE, you may also need to update LocallyAvailableResourceFinderFactory
+
+    private final String name;
+    private final CacheLayout parent;
+    private final int version;
+
+    private CacheLayout(CacheLayout parent, String name, int version) {
+        this.parent = parent;
+        this.name = name;
+        this.version = version;
+    }
+
+    public VersionNumber getVersion() {
+        return VersionNumber.parse(getFormattedVersion());
+    }
+
+    public String getKey() {
+        StringBuilder key = new StringBuilder();
+        key.append(name);
+        key.append("-");
+        key.append(getFormattedVersion());
+        return key.toString();
+    }
+
+    public String getFormattedVersion() {
+        if (parent == null) {
+            return String.valueOf(version);
+        }
+        return parent.getFormattedVersion() + '.' + String.valueOf(version);
+    }
+
+    public File getPath(File parentDir) {
+        return new File(parentDir, getKey());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java
new file mode 100644
index 0000000..d45821c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+
+import java.util.List;
+
+public class CacheLockingArtifactDependencyResolver implements ArtifactDependencyResolver {
+    private final CacheLockingManager lockingManager;
+    private final ArtifactDependencyResolver resolver;
+
+    public CacheLockingArtifactDependencyResolver(CacheLockingManager lockingManager, ArtifactDependencyResolver resolver) {
+        this.lockingManager = lockingManager;
+        this.resolver = resolver;
+    }
+
+    public void resolve(final ConfigurationInternal configuration,
+                                   final List<? extends ResolutionAwareRepository> repositories,
+                                   final GlobalDependencyResolutionRules metadataHandler,
+                                   final ResolverResults results) throws ResolveException {
+        lockingManager.useCache(String.format("resolve %s", configuration), new Runnable() {
+            public void run() {
+                resolver.resolve(configuration, repositories, metadataHandler, results);
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java
new file mode 100644
index 0000000..1def84a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLockingManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.cache.CacheAccess;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.File;
+
+/**
+ * Provides synchronized access to the artifact cache.
+ */
+ at ThreadSafe
+public interface CacheLockingManager extends ArtifactCacheMetaData, CacheAccess {
+    /**
+     * Creates a cache implementation that is managed by this locking manager. This method may be used at any time.
+     *
+     * <p>The returned cache may only be used by an action being run from {@link #useCache(String, org.gradle.internal.Factory)}.
+     * In this instance, an exclusive lock will be held on the cache.
+     *
+     * <p>The returned cache may not be used by an action being run from {@link #longRunningOperation(String, org.gradle.internal.Factory)}.
+     */
+    <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer);
+
+    /**
+     * Returns the root directory for the file store.
+     *
+     * @return File store location
+     */
+    File getFileStoreDirectory();
+
+    /**
+     * Returns the root directory for the meta-data file store.
+     *
+     * @return Metadata store location
+     */
+    File createMetaDataStore();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.java
new file mode 100644
index 0000000..ecfc503
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ContextualArtifactResolver.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.api.internal.artifacts.ivyservice;
+
+import org.apache.ivy.Ivy;
+import org.gradle.api.Action;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ComponentUsage;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+
+public class ContextualArtifactResolver implements ArtifactResolver {
+    private final CacheLockingManager lockingManager;
+    private final IvyContextManager ivyContextManager;
+    private final ArtifactResolver delegate;
+
+    public ContextualArtifactResolver(CacheLockingManager lockingManager, IvyContextManager ivyContextManager, ArtifactResolver delegate) {
+        this.lockingManager = lockingManager;
+        this.ivyContextManager = ivyContextManager;
+        this.delegate = delegate;
+    }
+
+    public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ArtifactType artifactType, final BuildableArtifactSetResolveResult result) {
+        String description = "Resolve " + artifactType + " for " + component;
+        executeInContext(description, new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                delegate.resolveModuleArtifacts(component, artifactType, result);
+            }
+        });
+    }
+
+    public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ComponentUsage usage, final BuildableArtifactSetResolveResult result) {
+        String description = "Resolve " + usage + " for " + component;
+        executeInContext(description, new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                delegate.resolveModuleArtifacts(component, usage, result);
+            }
+        });
+    }
+
+    public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
+        executeInContext("Resolve ".concat(artifact.toString()), new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                delegate.resolveArtifact(artifact, moduleSource, result);
+            }
+        });
+    }
+
+    private void executeInContext(String description, final Action<Ivy> action) {
+        lockingManager.useCache(description, new Runnable() {
+            public void run() {
+                ivyContextManager.withIvy(action);
+            }
+        });
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java
new file mode 100644
index 0000000..7d5de72
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManager.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.cache.PersistentIndexedCacheParameters;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.internal.Factory;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.util.VersionNumber;
+
+import java.io.Closeable;
+import java.io.File;
+
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
+
+public class DefaultCacheLockingManager implements CacheLockingManager, Closeable {
+
+    public static final VersionNumber CACHE_LAYOUT_VERSION = CacheLayout.META_DATA.getVersion();
+
+    private final PersistentCache cache;
+
+    public DefaultCacheLockingManager(CacheRepository cacheRepository) {
+        cache = cacheRepository
+                .store(CacheLayout.ROOT.getKey())
+                .withCrossVersionCache()
+                .withDisplayName("artifact cache")
+                .withLockOptions(mode(FileLockManager.LockMode.None)) // Don't need to lock anything until we use the caches
+                .open();
+    }
+
+    public void close() {
+        cache.close();
+    }
+
+    public File getCacheDir() {
+        return cache.getBaseDir();
+    }
+
+    public void longRunningOperation(String operationDisplayName, final Runnable action) {
+        cache.longRunningOperation(operationDisplayName, action);
+    }
+
+    public <T> T useCache(String operationDisplayName, Factory<? extends T> action) {
+        return cache.useCache(operationDisplayName, action);
+    }
+
+    public void useCache(String operationDisplayName, Runnable action) {
+        cache.useCache(operationDisplayName, action);
+    }
+
+    public <T> T longRunningOperation(String operationDisplayName, Factory<? extends T> action) {
+        return cache.longRunningOperation(operationDisplayName, action);
+    }
+
+    public <K, V> PersistentIndexedCache<K, V> createCache(String cacheName, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
+        String cacheFileInMetaDataStore = CacheLayout.META_DATA.getKey() + "/" + cacheName;
+        return cache.createCache(new PersistentIndexedCacheParameters<K, V>(cacheFileInMetaDataStore, keySerializer, valueSerializer));
+    }
+
+    public File getFileStoreDirectory() {
+        return createCacheRelativeDir(CacheLayout.FILE_STORE);
+    }
+
+    public File createMetaDataStore() {
+        return new File(createCacheRelativeDir(CacheLayout.META_DATA), "descriptors");
+    }
+
+    private File createCacheRelativeDir(CacheLayout cacheLayout) {
+        return cacheLayout.getPath(cache.getBaseDir());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java
new file mode 100644
index 0000000..1136fcf
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.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.api.internal.artifacts.ivyservice;
+
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.internal.Transformers;
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
+import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+public class DefaultConfigurationResolver implements ConfigurationResolver {
+    private final ArtifactDependencyResolver resolver;
+    private final RepositoryHandler repositories;
+    private final GlobalDependencyResolutionRules metadataHandler;
+
+    public DefaultConfigurationResolver(ArtifactDependencyResolver resolver, RepositoryHandler repositories, GlobalDependencyResolutionRules metadataHandler) {
+        this.resolver = resolver;
+        this.repositories = repositories;
+        this.metadataHandler = metadataHandler;
+    }
+
+    public ResolverResults resolve(ConfigurationInternal configuration) throws ResolveException {
+        List<ResolutionAwareRepository> resolutionAwareRepositories = CollectionUtils.collect(repositories, Transformers.cast(ResolutionAwareRepository.class));
+        ResolverResults results = new ResolverResults();
+        resolver.resolve(configuration, resolutionAwareRepositories, metadataHandler, results);
+        return results;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
new file mode 100644
index 0000000..c12720a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetails.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
+import org.gradle.api.internal.artifacts.DependencyResolveDetailsInternal;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+
+public class DefaultDependencyResolveDetails implements DependencyResolveDetailsInternal {
+
+    private final DependencySubstitutionInternal<?> delegate;
+    private ModuleVersionSelector target;
+
+    public DefaultDependencyResolveDetails(DependencySubstitutionInternal<?> delegate) {
+        this.delegate = delegate;
+        target = determineTarget(delegate);
+    }
+
+    private static ModuleVersionSelector determineTarget(DependencySubstitutionInternal<?> delegate) {
+        // Temporary logic until we add DependencySubstitution back in
+        if (delegate.getTarget() instanceof ModuleComponentSelector) {
+            ModuleComponentSelector moduleComponentSelector = (ModuleComponentSelector) delegate.getTarget();
+            return DefaultModuleVersionSelector.newSelector(moduleComponentSelector.getGroup(), moduleComponentSelector.getModule(), moduleComponentSelector.getVersion());
+        }
+        // If the target is a project component, it must be unmodified from the requested
+        return delegate.getOldRequested();
+    }
+
+    @Override
+    public ModuleVersionSelector getRequested() {
+        return delegate.getOldRequested();
+    }
+
+    @Override
+    public void useVersion(String version) {
+        useVersion(version, VersionSelectionReasons.SELECTED_BY_RULE);
+    }
+
+    @Override
+    public void useVersion(String version, ComponentSelectionReason selectionReason) {
+        assert selectionReason != null;
+        if (version == null) {
+            throw new IllegalArgumentException("Configuring the dependency resolve details with 'null' version is not allowed.");
+        }
+
+        if (!version.equals(target.getVersion())) {
+            target = DefaultModuleVersionSelector.newSelector(target.getGroup(), target.getName(), version);
+            delegate.useTarget(DefaultModuleComponentSelector.newSelector(target), selectionReason);
+        } else {
+            // Still 'updated' with reason when version remains the same.
+            delegate.useTarget(delegate.getTarget(), selectionReason);
+        }
+    }
+
+    @Override
+    public void useTarget(Object notation) {
+        target = ModuleVersionSelectorParsers.parser().parseNotation(notation);
+        delegate.useTarget(DefaultModuleComponentSelector.newSelector(target), VersionSelectionReasons.SELECTED_BY_RULE);
+    }
+
+    @Override
+    public ComponentSelectionReason getSelectionReason() {
+        return delegate.getSelectionReason();
+    }
+
+    @Override
+    public ModuleVersionSelector getTarget() {
+        return target;
+    }
+
+    @Override
+    public boolean isUpdated() {
+        return delegate.isUpdated();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManager.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
new file mode 100644
index 0000000..db57012
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyPublisher.java
@@ -0,0 +1,73 @@
+/*
+ * 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.gradle.api.UncheckedIOException;
+import org.gradle.api.artifacts.PublishException;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.internal.component.external.model.BuildableIvyModulePublishMetaData;
+import org.gradle.internal.component.external.model.DefaultIvyModulePublishMetaData;
+import org.gradle.internal.component.external.model.IvyModuleArtifactPublishMetaData;
+import org.gradle.internal.component.external.model.IvyModulePublishMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class DefaultIvyDependencyPublisher implements IvyDependencyPublisher {
+    private static Logger logger = LoggerFactory.getLogger(DefaultIvyDependencyPublisher.class);
+
+    public void publish(List<ModuleVersionPublisher> publishResolvers,
+                        IvyModulePublishMetaData publishMetaData) {
+        try {
+            // Make a copy of the publication and filter missing artifacts
+            DefaultIvyModulePublishMetaData publication = new DefaultIvyModulePublishMetaData(publishMetaData.getId());
+            for (IvyModuleArtifactPublishMetaData artifact: publishMetaData.getArtifacts()) {
+                addPublishedArtifact(artifact, publication);
+            }
+            for (ModuleVersionPublisher publisher : publishResolvers) {
+                logger.info("Publishing to {}", publisher);
+                publisher.publish(publication);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void addPublishedArtifact(IvyModuleArtifactPublishMetaData artifact, BuildableIvyModulePublishMetaData publication) {
+        if (checkArtifactFileExists(artifact)) {
+            publication.addArtifact(artifact);
+        }
+    }
+
+    private boolean checkArtifactFileExists(IvyModuleArtifactPublishMetaData artifact) {
+        File artifactFile = artifact.getFile();
+        if (artifactFile.exists()) {
+            return true;
+        }
+        if (!isSigningArtifact(artifact.getArtifactName())) {
+            throw new PublishException(String.format("Cannot publish artifact '%s' (%s) as it does not exist.", artifact.getId(), artifactFile));
+        }
+        return false;
+    }
+
+    private boolean isSigningArtifact(IvyArtifactName artifact) {
+        return artifact.getType().endsWith(".asc") || artifact.getType().endsWith(".sig");
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfo.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfo.java
new file mode 100644
index 0000000..51d13d0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfo.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 javax.xml.namespace.QName;
+
+import com.google.common.base.Joiner;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.ivy.IvyExtraInfo;
+import org.gradle.util.CollectionUtils;
+
+import java.util.*;
+
+public class DefaultIvyExtraInfo implements IvyExtraInfo {
+    protected Map<NamespaceId, String> extraInfo;
+
+    public DefaultIvyExtraInfo() {
+        this.extraInfo = new LinkedHashMap<NamespaceId, String>();
+    }
+
+    public DefaultIvyExtraInfo(Map<NamespaceId, String> extraInfo) {
+        this.extraInfo = extraInfo;
+    }
+
+    public String get(String name) {
+        List<Map.Entry<NamespaceId, String>> foundEntries = new ArrayList<Map.Entry<NamespaceId, String>>();
+        for (Map.Entry<NamespaceId, String> entry : extraInfo.entrySet()) {
+            if (entry.getKey().getName().equals(name)) {
+                foundEntries.add(entry);
+            }
+        }
+        if (foundEntries.size() > 1) {
+            String allNamespaces = Joiner.on(", ").join(CollectionUtils.collect(foundEntries, new Transformer<String, Map.Entry<NamespaceId, String>>() {
+                public String transform(Map.Entry<NamespaceId, String> original) {
+                    return original.getKey().getNamespace();
+                }
+            }));
+            throw new InvalidUserDataException(String.format("Cannot get extra info element named '%s' by name since elements with this name were found from multiple namespaces (%s).  Use get(String namespace, String name) instead.", name, allNamespaces));
+        }
+        return foundEntries.size() == 0 ? null : foundEntries.get(0).getValue();
+    }
+
+    public String get(String namespace, String name) {
+        return extraInfo.get(new NamespaceId(namespace, name));
+    }
+
+    public Map<QName, String> asMap() {
+        Map<QName, String> map = new LinkedHashMap<QName, String>();
+        for (Map.Entry<NamespaceId, String> entry : extraInfo.entrySet()) {
+            map.put(new QName(entry.getKey().getNamespace(), entry.getKey().getName()), entry.getValue());
+        }
+        return Collections.unmodifiableMap(map);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptor.java
new file mode 100644
index 0000000..748ff82
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ivy.IvyExtraInfo;
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
+
+import java.util.Map;
+
+public class DefaultIvyModuleDescriptor implements IvyModuleDescriptor {
+    private final String branch;
+    private final String ivyStatus;
+    private final IvyExtraInfo extraInfo;
+
+    public DefaultIvyModuleDescriptor(Map<NamespaceId, String> extraInfo, String branch, String ivyStatus) {
+        this.extraInfo = new DefaultIvyExtraInfo(extraInfo);
+        this.branch = branch;
+        this.ivyStatus = ivyStatus;
+    }
+
+    public String getBranch() {
+        return branch;
+    }
+
+    public String getIvyStatus() {
+        return ivyStatus;
+    }
+
+    public IvyExtraInfo getExtraInfo() {
+        return extraInfo;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
new file mode 100644
index 0000000..5d5da17
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.*;
+import org.gradle.internal.resolve.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 LenientConfiguration {
+    private CacheLockingManager cacheLockingManager;
+    private final Configuration configuration;
+    private ResolvedConfigurationResults results;
+
+    public DefaultLenientConfiguration(Configuration configuration, ResolvedConfigurationResults results, CacheLockingManager cacheLockingManager) {
+        this.configuration = configuration;
+        this.results = results;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    public boolean hasError() {
+        return results.hasError();
+    }
+
+    public void rethrowFailure() throws ResolveException {
+        if (hasError()) {
+            List<Throwable> failures = new ArrayList<Throwable>();
+            for (UnresolvedDependency unresolvedDependency : results.getUnresolvedDependencies()) {
+                failures.add(unresolvedDependency.getProblem());
+            }
+            throw new ResolveException(configuration, failures);
+        }
+    }
+
+    public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
+        return results.getUnresolvedDependencies();
+    }
+
+    public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
+        return results.getArtifacts();
+    }
+
+    public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
+        Set<ResolvedDependency> matches = new LinkedHashSet<ResolvedDependency>();
+        for (Map.Entry<ModuleDependency, ResolvedDependency> entry : results.more().getFirstLevelDependencies().entrySet()) {
+            if (dependencySpec.isSatisfiedBy(entry.getKey())) {
+                matches.add(entry.getValue());
+            }
+        }
+        return matches;
+    }
+
+    public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+        Set<ResolvedArtifact> artifacts = getArtifacts(dependencySpec);
+        return getFiles(artifacts);
+    }
+
+    public Set<File> getFilesStrict(Spec<? super Dependency> dependencySpec) {
+        Set<ResolvedArtifact> artifacts = getAllArtifacts(dependencySpec);
+        return getFiles(artifacts);
+    }
+
+    /**
+     * Recursive but excludes unsuccessfully resolved artifacts.
+     *
+     * @param dependencySpec dependency spec
+     */
+    public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
+        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(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;
+    }
+
+    /**
+     * Recursive, includes unsuccessfully resolved artifacts
+     *
+     * @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(results.more().getRoot()));
+            walker.add(resolvedDependency);
+        }
+
+        artifacts.addAll(walker.findValues());
+        return artifacts;
+    }
+
+    public Configuration getConfiguration() {
+        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<? super ResolvedArtifact> values,
+                                  Collection<? super ResolvedDependency> connectedNodes) {
+            connectedNodes.addAll(node.getChildren());
+        }
+
+        public void getEdgeValues(ResolvedDependency from, ResolvedDependency to,
+                                  Collection<ResolvedArtifact> values) {
+            values.addAll(to.getParentArtifacts(from));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitution.java
new file mode 100644
index 0000000..46fb15c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitution.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.ModuleDependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+
+public class DefaultModuleDependencySubstitution extends AbstractDependencySubstitution<ModuleComponentSelector> implements ModuleDependencySubstitutionInternal {
+
+    public DefaultModuleDependencySubstitution(ModuleComponentSelector requested, ModuleVersionSelector oldRequested) {
+        super(requested, oldRequested);
+    }
+
+    @Override
+    public void useVersion(String version) {
+        useVersion(version, VersionSelectionReasons.SELECTED_BY_RULE);
+    }
+
+    @Override
+    public void useVersion(String version, ComponentSelectionReason selectionReason) {
+        assert selectionReason != null;
+        if (version == null) {
+            throw new IllegalArgumentException("Configuring the dependency resolve details with 'null' version is not allowed.");
+        }
+        ModuleComponentSelector moduleTarget = getRequested();
+        if (!version.equals(moduleTarget.getVersion())) {
+            useTarget(DefaultModuleComponentSelector.newSelector(moduleTarget.getGroup(), moduleTarget.getModule(), version), selectionReason);
+        } else {
+            useTarget(moduleTarget, selectionReason);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitution.java
new file mode 100644
index 0000000..6d0f78a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitution.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleVersionSelector;
+import org.gradle.api.artifacts.ProjectDependencySubstitution;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+
+public class DefaultProjectDependencySubstitution extends AbstractDependencySubstitution<ProjectComponentSelector> implements ProjectDependencySubstitution {
+
+    public DefaultProjectDependencySubstitution(ProjectComponentSelector requested, ModuleVersionSelector oldRequested) {
+        super(requested, oldRequested);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java
new file mode 100755
index 0000000..e9362ce
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependency.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.UnresolvedDependency;
+
+public class DefaultUnresolvedDependency implements UnresolvedDependency {
+    private final Throwable problem;
+    private final ModuleVersionSelector selector;
+
+    public DefaultUnresolvedDependency(ModuleVersionSelector selector, Throwable problem) {
+        this.selector = selector;
+        this.problem = problem;
+    }
+
+    public ModuleVersionSelector getSelector() {
+        return selector;
+    }
+
+    public Throwable getProblem() {
+        return problem;
+    }
+
+    public String toString() {
+        return selector.getGroup() + ":" + selector.getName() + ":" + selector.getVersion();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolver.java
new file mode 100644
index 0000000..bd55870
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolver.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Action;
+import org.gradle.api.artifacts.DependencySubstitution;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult;
+
+public class DependencySubstitutionResolver implements DependencyToComponentIdResolver {
+    private final DependencyToComponentIdResolver resolver;
+    private final Action<DependencySubstitution<ComponentSelector>> rule;
+
+    public DependencySubstitutionResolver(DependencyToComponentIdResolver resolver, Action<DependencySubstitution<ComponentSelector>> rule) {
+        this.resolver = resolver;
+        this.rule = rule;
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result) {
+        ComponentSelector selector = dependency.getSelector();
+        DependencySubstitutionInternal details;
+        if (selector instanceof ModuleComponentSelector) {
+            details = new DefaultModuleDependencySubstitution((ModuleComponentSelector) selector, dependency.getRequested());
+        } else if (selector instanceof ProjectComponentSelector) {
+            details = new DefaultProjectDependencySubstitution((ProjectComponentSelector) selector, dependency.getRequested());
+        } else {
+            throw new IllegalStateException("Unknown type of component selector: " + selector);
+        }
+        try {
+            rule.execute(details);
+        } catch (Throwable e) {
+            result.failed(new ModuleVersionResolveException(selector, e));
+            return;
+        }
+        if (details.isUpdated()) {
+            resolver.resolve(dependency.withTarget(details.getTarget()), result);
+            result.setSelectionReason(details.getSelectionReason());
+            return;
+        }
+        resolver.resolve(dependency, result);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
new file mode 100644
index 0000000..5f9a2c4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolver.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 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.ResolvedComponentResult;
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.specs.Spec;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+public class ErrorHandlingArtifactDependencyResolver implements ArtifactDependencyResolver {
+    private final ArtifactDependencyResolver dependencyResolver;
+
+    public ErrorHandlingArtifactDependencyResolver(ArtifactDependencyResolver dependencyResolver) {
+        this.dependencyResolver = dependencyResolver;
+    }
+
+    public void resolve(ConfigurationInternal configuration,
+                        List<? extends ResolutionAwareRepository> repositories,
+                        GlobalDependencyResolutionRules metadataHandler,
+                        ResolverResults results) throws ResolveException {
+        try {
+            dependencyResolver.resolve(configuration, repositories, metadataHandler, results);
+        } catch (final Throwable e) {
+            results.failed(new BrokenResolvedConfiguration(e, configuration), wrapException(e, configuration));
+            return;
+        }
+        ResolvedConfiguration wrappedConfiguration = new ErrorHandlingResolvedConfiguration(results.getResolvedConfiguration(), configuration);
+        ResolutionResult wrappedResult = new ErrorHandlingResolutionResult(results.getResolutionResult(), configuration);
+        results.resolved(wrappedConfiguration, wrappedResult, results.getResolvedProjectConfigurationResults());
+    }
+
+    private static ResolveException wrapException(Throwable e, Configuration configuration) {
+        if (e instanceof ResolveException) {
+            return (ResolveException) e;
+        }
+        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 ResolvedComponentResult 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<ResolvedComponentResult> getAllComponents() {
+            try {
+                return resolutionResult.getAllComponents();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public void allComponents(Action<? super ResolvedComponentResult> action) {
+            resolutionResult.allComponents(action);
+        }
+
+        public void allComponents(Closure closure) {
+            resolutionResult.allComponents(closure);
+        }
+    }
+    
+    private static class ErrorHandlingResolvedConfiguration implements ResolvedConfiguration {
+        private final ResolvedConfiguration resolvedConfiguration;
+        private final Configuration configuration;
+
+        public ErrorHandlingResolvedConfiguration(ResolvedConfiguration resolvedConfiguration,
+                                                  Configuration configuration) {
+            this.resolvedConfiguration = resolvedConfiguration;
+            this.configuration = configuration;
+        }
+
+        public boolean hasError() {
+            return resolvedConfiguration.hasError();
+        }
+
+        public LenientConfiguration getLenientConfiguration() {
+            try {
+                return new ErrorHandlingLenientConfiguration(resolvedConfiguration.getLenientConfiguration(), configuration);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public void rethrowFailure() throws ResolveException {
+            try {
+                resolvedConfiguration.rethrowFailure();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            try {
+                return resolvedConfiguration.getFiles(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
+            try {
+                return resolvedConfiguration.getFirstLevelModuleDependencies();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            try {
+                return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+
+        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
+            try {
+                return resolvedConfiguration.getResolvedArtifacts();
+            } catch (Throwable e) {
+                throw wrapException(e, configuration);
+            }
+        }
+    }
+
+    private static class BrokenResolvedConfiguration implements ResolvedConfiguration {
+        private final Throwable e;
+        private final Configuration configuration;
+
+        public BrokenResolvedConfiguration(Throwable e, Configuration configuration) {
+            this.e = e;
+            this.configuration = configuration;
+        }
+
+        public boolean hasError() {
+            return true;
+        }
+
+        public LenientConfiguration getLenientConfiguration() {
+            throw wrapException(e, configuration);
+        }
+
+        public void rethrowFailure() throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+
+        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
+            throw wrapException(e, configuration);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
new file mode 100644
index 0000000..f537a80
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyBackedArtifactPublisher.java
@@ -0,0 +1,83 @@
+/*
+ * 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.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.PublishException;
+import org.gradle.api.internal.artifacts.ArtifactPublisher;
+import org.gradle.api.internal.artifacts.ModuleInternal;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.internal.component.external.model.BuildableIvyModulePublishMetaData;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class IvyBackedArtifactPublisher implements ArtifactPublisher {
+    private final LocalComponentFactory publishLocalComponentFactory;
+    private final IvyContextManager ivyContextManager;
+    private final IvyDependencyPublisher dependencyPublisher;
+    private final IvyModuleDescriptorWriter ivyModuleDescriptorWriter;
+
+    public IvyBackedArtifactPublisher(LocalComponentFactory publishLocalComponentFactory,
+                                      IvyContextManager ivyContextManager,
+                                      IvyDependencyPublisher dependencyPublisher,
+                                      IvyModuleDescriptorWriter ivyModuleDescriptorWriter) {
+        this.publishLocalComponentFactory = publishLocalComponentFactory;
+        this.ivyContextManager = ivyContextManager;
+        this.dependencyPublisher = dependencyPublisher;
+        this.ivyModuleDescriptorWriter = ivyModuleDescriptorWriter;
+    }
+
+    public void publish(final Iterable<? extends PublicationAwareRepository> repositories, final ModuleInternal module, final Configuration configuration, final File descriptor) throws PublishException {
+        ivyContextManager.withIvy(new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                Set<Configuration> allConfigurations = configuration.getAll();
+                Set<Configuration> configurationsToPublish = configuration.getHierarchy();
+
+                MutableLocalComponentMetaData componentMetaData = publishLocalComponentFactory.convert(allConfigurations, module);
+                if (descriptor != null) {
+                    ModuleDescriptor moduleDescriptor = componentMetaData.getModuleDescriptor();
+                    ivyModuleDescriptorWriter.write(moduleDescriptor, descriptor);
+                }
+
+                // Need to convert a second time, to determine which artifacts to publish (and yes, this isn't a great way to do things...)
+                componentMetaData = publishLocalComponentFactory.convert(configurationsToPublish, module);
+                BuildableIvyModulePublishMetaData publishMetaData = componentMetaData.toPublishMetaData();
+                if (descriptor != null) {
+                    Artifact artifact = MDArtifact.newIvyArtifact(componentMetaData.getModuleDescriptor());
+                    publishMetaData.addArtifact(artifact, descriptor);
+                }
+
+                List<ModuleVersionPublisher> publishResolvers = new ArrayList<ModuleVersionPublisher>();
+                for (PublicationAwareRepository repository : repositories) {
+                    ModuleVersionPublisher publisher = repository.createPublisher();
+                    publishResolvers.add(publisher);
+                }
+
+                dependencyPublisher.publish(publishResolvers, publishMetaData);
+            }
+        });
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyContextManager.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.java
new file mode 100644
index 0000000..0083e10
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyDependencyPublisher.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.internal.component.external.model.IvyModulePublishMetaData;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+
+import java.util.List;
+
+public interface IvyDependencyPublisher {
+    void publish(List<ModuleVersionPublisher> publishResolvers,
+                 IvyModulePublishMetaData publishMetaData);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyModuleDescriptorWriter.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
new file mode 100644
index 0000000..178b5e7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyUtil.java
@@ -0,0 +1,90 @@
+/*
+ * 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.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+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.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.util.GUtil;
+
+import java.util.Map;
+
+import static java.util.Collections.emptyMap;
+
+public class IvyUtil {
+
+    private static final Object MODULE_ID_LOCK = new Object(); //see GRADLE-3027
+
+    public static ModuleRevisionId createModuleRevisionId(Module module) {
+        return createModuleRevisionId(module.getGroup(), module.getName(), module.getVersion());
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(Dependency dependency) {
+        return createModuleRevisionId(dependency.getGroup(), dependency.getName(), dependency.getVersion());
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(String group, String name, String version) {
+        return createModuleRevisionId(emptyStringIfNull(group), name, null, emptyStringIfNull(version), emptyMap());
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(ModuleVersionIdentifier id) {
+        return createModuleRevisionId(id.getGroup(), id.getName(), id.getVersion());
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(ModuleComponentIdentifier id) {
+        return createModuleRevisionId(id.getGroup(), id.getModule(), id.getVersion());
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(ModuleRevisionId revId, String version) {
+        return createModuleRevisionId(revId.getOrganisation(), revId.getName(), revId.getBranch(), version, revId.getQualifiedExtraAttributes());
+    }
+
+    private static String emptyStringIfNull(String value) {
+        return GUtil.elvis(value, "");
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(String org, String name, String branch, String rev, Map extraAttributes) {
+        return createModuleRevisionId(org, name, branch, rev, extraAttributes, true);
+    }
+
+    public static ModuleRevisionId createModuleRevisionId(String org, String name, String branch, String revConstraint, Map extraAttributes, boolean replaceNullBranchWithDefault) {
+        synchronized (MODULE_ID_LOCK) {
+            return ModuleRevisionId.newInstance(org, name, branch, revConstraint, extraAttributes, replaceNullBranchWithDefault);
+        }
+    }
+
+    public static ModuleId createModuleId(String org, String name) {
+        synchronized (MODULE_ID_LOCK) {
+            return ModuleId.newInstance(org, name);
+        }
+    }
+
+    public static ArtifactId createArtifactId(String org, String module, String name, String type, String ext) {
+        return new ArtifactId(createModuleId(org, module), name, type, ext);
+    }
+
+    public static DefaultModuleDescriptor createModuleDescriptor(DependencyDescriptor dependencyDescriptor) {
+        DefaultModuleDescriptor moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(dependencyDescriptor.getDependencyRevisionId(), dependencyDescriptor.getAllDependencyArtifacts());
+        moduleDescriptor.setStatus("integration");
+        return moduleDescriptor;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
new file mode 100644
index 0000000..c018779
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriter.java
@@ -0,0 +1,402 @@
+/*
+ * 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 com.google.common.base.Joiner;
+import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.extendable.ExtendableItem;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.xml.SimpleXmlWriter;
+import org.gradle.internal.UncheckedException;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class IvyXmlModuleDescriptorWriter implements IvyModuleDescriptorWriter {
+    public static final String IVY_DATE_PATTERN = "yyyyMMddHHmmss";
+    private final Field dependencyConfigField;
+
+    public IvyXmlModuleDescriptorWriter() {
+        try {
+            dependencyConfigField = DefaultDependencyDescriptor.class.getDeclaredField("confs");
+        } catch (NoSuchFieldException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        dependencyConfigField.setAccessible(true);
+    }
+
+    private void writeTo(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        writer.startElement("ivy-module");
+        writer.attribute("version", "2.0");
+
+        Map<String, String> namespaces = md.getExtraAttributesNamespaces();
+        for (Map.Entry<String, String> entry : namespaces.entrySet()) {
+            writer.attribute("xmlns:" + entry.getKey(), entry.getValue());
+        }
+
+        printInfoTag(md, writer);
+        printConfigurations(md, writer);
+        printPublications(md, writer);
+        printDependencies(md, writer);
+
+        writer.endElement();
+    }
+
+    public void write(ModuleDescriptor md, File output) {
+        try {
+            output.getParentFile().mkdirs();
+            OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
+            try {
+                SimpleXmlWriter xmlWriter = new SimpleXmlWriter(outputStream, "  ");
+                writeTo(md, xmlWriter);
+                xmlWriter.flush();
+            } finally {
+                outputStream.close();
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void printDependencies(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        DependencyDescriptor[] dds = md.getDependencies();
+        if (dds.length > 0) {
+            writer.startElement("dependencies");
+            for (int i = 0; i < dds.length; i++) {
+                DependencyDescriptor dep = dds[i];
+                printDependency(md, dep, writer);
+            }
+            printAllExcludes(md, writer);
+            writer.endElement();
+        }
+    }
+
+    protected void printDependency(ModuleDescriptor md, DependencyDescriptor dep,
+                                          SimpleXmlWriter writer) throws IOException {
+        writer.startElement("dependency");
+
+        ModuleRevisionId dependencyRevisionId = dep.getDependencyRevisionId();
+        writer.attribute("org", dependencyRevisionId.getOrganisation());
+        writer.attribute("name", dependencyRevisionId.getName());
+        if (dependencyRevisionId.getBranch() != null) {
+            writer.attribute("branch", dependencyRevisionId.getBranch());
+        }
+        writer.attribute("rev", dependencyRevisionId.getRevision());
+        if (!dep.getDynamicConstraintDependencyRevisionId().equals(dependencyRevisionId)) {
+            if (dep.getDynamicConstraintDependencyRevisionId().getBranch() != null) {
+                writer.attribute("branchConstraint", dep.getDynamicConstraintDependencyRevisionId().getBranch());
+            }
+            writer.attribute("revConstraint", dep.getDynamicConstraintDependencyRevisionId().getRevision());
+        }
+        if (dep.isForce()) {
+            writer.attribute("force", "true");
+        }
+        if (dep.isChanging()) {
+            writer.attribute("changing", "true");
+        }
+        if (!dep.isTransitive()) {
+            writer.attribute("transitive", "false");
+        }
+        writer.attribute("conf", getConfMapping(dep));
+
+        printExtraAttributes(dep, writer);
+
+        DependencyArtifactDescriptor[] depArtifacts = dep.getAllDependencyArtifacts();
+        printDependencyArtefacts(md, writer, depArtifacts);
+
+        IncludeRule[] includes = dep.getAllIncludeRules();
+        printDependencyIncludeRules(md, writer, includes);
+
+        ExcludeRule[] excludes = dep.getAllExcludeRules();
+        printDependencyExcludeRules(md, writer, excludes);
+
+        writer.endElement();
+    }
+
+    private String getConfMapping(DependencyDescriptor dep) {
+        StringBuilder confs = new StringBuilder();
+        String[] modConfs = dep.getModuleConfigurations();
+
+        Map<String, List<String>> configMappings;
+        if (dep instanceof DefaultDependencyDescriptor) {
+            // The `getDependencyConfigurations()` implementation for DefaultDependencyDescriptor does some interpretation of the RHS of the configuration
+            // mappings, and gets it wrong for mappings such as '*->@' pr '*->#'. So, instead, reach into the descriptor and get the raw mappings out.
+            try {
+                configMappings = (Map<String, List<String>>) dependencyConfigField.get(dep);
+            } catch (IllegalAccessException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        } else {
+            configMappings = new HashMap<String, List<String>>();
+            for (String modConf : modConfs) {
+                configMappings.put(modConf, Arrays.asList(dep.getDependencyConfigurations(modConfs)));
+            }
+        }
+
+        for (int j = 0; j < modConfs.length; j++) {
+            List<String> depConfs = configMappings.get(modConfs[j]);
+            confs.append(modConfs[j]).append("->");
+            for (int k = 0; k < depConfs.size(); k++) {
+                confs.append(depConfs.get(k));
+                if (k + 1 < depConfs.size()) {
+                    confs.append(",");
+                }
+            }
+            if (j + 1 < modConfs.length) {
+                confs.append(";");
+            }
+        }
+        return confs.toString();
+    }
+
+    private static void printAllExcludes(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        ExcludeRule[] excludes = md.getAllExcludeRules();
+        for (ExcludeRule exclude : excludes) {
+            writer.startElement("exclude");
+            writer.attribute("org", exclude.getId().getModuleId().getOrganisation());
+            writer.attribute("module", exclude.getId().getModuleId().getName());
+            writer.attribute("artifact", exclude.getId().getName());
+            writer.attribute("type", exclude.getId().getType());
+            writer.attribute("ext", exclude.getId().getExt());
+            String[] ruleConfs = exclude.getConfigurations();
+            if (!Arrays.asList(ruleConfs).equals(
+                    Arrays.asList(md.getConfigurationsNames()))) {
+                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
+            }
+            writer.attribute("matcher", exclude.getMatcher().getName());
+            writer.endElement();
+        }
+    }
+
+    private static void printDependencyExcludeRules(ModuleDescriptor md, SimpleXmlWriter writer,
+                                                    ExcludeRule[] excludes) throws IOException {
+        for (ExcludeRule exclude : excludes) {
+            writer.startElement("exclude");
+            writer.attribute("org", exclude.getId().getModuleId().getOrganisation());
+            writer.attribute("module", exclude.getId().getModuleId().getName());
+            writer.attribute("name", exclude.getId().getName());
+            writer.attribute("type", exclude.getId().getType());
+            writer.attribute("ext", exclude.getId().getExt());
+            String[] ruleConfs = exclude.getConfigurations();
+            if (!Arrays.asList(ruleConfs).equals(
+                    Arrays.asList(md.getConfigurationsNames()))) {
+                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
+            }
+            writer.attribute("matcher", exclude.getMatcher().getName());
+            writer.endElement();
+        }
+    }
+
+    private static void printDependencyIncludeRules(ModuleDescriptor md, SimpleXmlWriter writer,
+                                                    IncludeRule[] includes) throws IOException {
+        for (IncludeRule include : includes) {
+            writer.startElement("include");
+            writer.attribute("name", include.getId().getName());
+            writer.attribute("type", include.getId().getType());
+            writer.attribute("ext", include.getId().getExt());
+            String[] ruleConfs = include.getConfigurations();
+            if (!Arrays.asList(ruleConfs).equals(
+                    Arrays.asList(md.getConfigurationsNames()))) {
+                writer.attribute("conf", Joiner.on(',').join(ruleConfs));
+            }
+            writer.attribute("matcher", include.getMatcher().getName());
+            writer.endElement();
+        }
+    }
+
+    private static void printDependencyArtefacts(ModuleDescriptor md, SimpleXmlWriter writer,
+                                                 DependencyArtifactDescriptor[] depArtifacts) throws IOException {
+        for (DependencyArtifactDescriptor depArtifact : depArtifacts) {
+            writer.startElement("artifact");
+            writer.attribute("name", depArtifact.getName());
+            writer.attribute("type", depArtifact.getType());
+            writer.attribute("ext", depArtifact.getExt());
+            String[] dadconfs = depArtifact.getConfigurations();
+            if (!Arrays.asList(dadconfs).equals(
+                    Arrays.asList(md.getConfigurationsNames()))) {
+                writer.attribute("conf", Joiner.on(',').join(dadconfs));
+            }
+            printExtraAttributes(depArtifact, writer);
+            writer.endElement();
+        }
+    }
+
+    /**
+     * Writes the extra attributes of the given {@link org.apache.ivy.util.extendable.ExtendableItem} to the given <tt>PrintWriter</tt>.
+     *
+     * @param item the {@link org.apache.ivy.util.extendable.ExtendableItem}, cannot be <tt>null</tt>
+     * @param writer the writer to use
+     */
+    private static void printExtraAttributes(ExtendableItem item, SimpleXmlWriter writer) throws IOException {
+        printExtraAttributes(item.getQualifiedExtraAttributes(), writer);
+    }
+
+    /**
+     * Writes the specified <tt>Map</tt> containing the extra attributes to the given <tt>PrintWriter</tt>.
+     *
+     * @param extra the extra attributes, can be <tt>null</tt>
+     * @param writer the writer to use
+     */
+    private static void printExtraAttributes(Map<String, ?> extra, SimpleXmlWriter writer) throws IOException {
+        if (extra == null) {
+            return;
+        }
+        for (Map.Entry<String, ?> entry : extra.entrySet()) {
+            writer.attribute(entry.getKey(), entry.getValue().toString());
+        }
+    }
+
+    private static void printPublications(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        writer.startElement("publications");
+        Artifact[] artifacts = md.getAllArtifacts();
+        for (int i = 0; i < artifacts.length; i++) {
+            Artifact artifact = artifacts[i];
+            writer.startElement("artifact");
+            writer.attribute("name", artifact.getName());
+            writer.attribute("type", artifact.getType());
+            writer.attribute("ext", artifact.getExt());
+            writer.attribute("conf", getConfs(artifact));
+            printExtraAttributes(artifact, writer);
+            writer.endElement();
+        }
+        writer.endElement();
+    }
+
+    private static void printConfigurations(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        Configuration[] confs = md.getConfigurations();
+        if (confs.length > 0) {
+            writer.startElement("configurations");
+            for (Configuration conf : confs) {
+                printConfiguration(conf, writer);
+            }
+            writer.endElement();
+        }
+    }
+
+    private static void printConfiguration(Configuration conf, SimpleXmlWriter writer) throws IOException {
+        writer.startElement("conf");
+        writer.attribute("name", conf.getName());
+        writer.attribute("visibility", conf.getVisibility().toString());
+        String description = conf.getDescription();
+        if (description != null) {
+            writer.attribute("description", description);
+        }
+        String[] exts = conf.getExtends();
+        if (exts.length > 0) {
+            writer.attribute("extends", Joiner.on(',').join(exts));
+        }
+        if (!conf.isTransitive()) {
+            writer.attribute("transitive", "false");
+        }
+        if (conf.getDeprecated() != null) {
+            writer.attribute("deprecated", conf.getDeprecated());
+        }
+        printExtraAttributes(conf, writer);
+        writer.endElement();
+    }
+
+    private static void printInfoTag(ModuleDescriptor md, SimpleXmlWriter writer) throws IOException {
+        ModuleRevisionId moduleRevisionId = md.getModuleRevisionId();
+        writer.startElement("info");
+
+        writer.attribute("organisation", moduleRevisionId.getOrganisation());
+        writer.attribute("module", moduleRevisionId.getName());
+
+        ModuleRevisionId resolvedModuleRevisionId = md.getResolvedModuleRevisionId();
+        String branch = resolvedModuleRevisionId.getBranch();
+        if (branch != null) {
+            writer.attribute("branch", branch);
+        }
+        String revision = resolvedModuleRevisionId.getRevision();
+        if (revision != null) {
+            writer.attribute("revision", revision);
+        }
+        writer.attribute("status", md.getStatus());
+
+        SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_PATTERN);
+        Date publicationDate = md.getResolvedPublicationDate();
+        if (publicationDate != null) {
+            writer.attribute("publication", ivyDateFormat.format(publicationDate));
+        }
+        if (md.isDefault()) {
+            writer.attribute("default", "true");
+        }
+        if (md instanceof DefaultModuleDescriptor) {
+            DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
+            if (dmd.getNamespace() != null && !dmd.getNamespace().getName().equals("system")) {
+                writer.attribute("namespace", dmd.getNamespace().getName());
+            }
+        }
+        if (!md.getExtraAttributes().isEmpty()) {
+            printExtraAttributes(md, writer);
+        }
+
+        ExtendsDescriptor[] parents = md.getInheritedDescriptors();
+        if (parents.length != 0) {
+            throw new UnsupportedOperationException("Extends descriptors not supported.");
+        }
+
+        License[] licenses = md.getLicenses();
+        for (int i = 0; i < licenses.length; i++) {
+            License license = licenses[i];
+            writer.startElement("license");
+            if (license.getName() != null) {
+                writer.attribute("name", license.getName());
+            }
+            if (license.getUrl() != null) {
+                writer.attribute("url", license.getUrl());
+            }
+            writer.endElement();
+        }
+
+        if (md.getHomePage() != null || md.getDescription() != null) {
+            writer.startElement("description");
+            if (md.getHomePage() != null) {
+                writer.attribute("homepage", md.getHomePage());
+            }
+            if (md.getDescription() != null && md.getDescription().trim().length() > 0) {
+                writer.characters(md.getDescription());
+            }
+            writer.endElement();
+        }
+
+        for (Iterator it = md.getExtraInfo().entrySet().iterator(); it.hasNext();) {
+            Map.Entry extraDescr = (Map.Entry) it.next();
+            if (extraDescr.getValue() == null || ((String) extraDescr.getValue()).length() == 0) {
+                continue;
+            }
+            if (extraDescr.getKey() instanceof NamespaceId) {
+                NamespaceId id = (NamespaceId) extraDescr.getKey();
+                writer.startElement(String.format("ns:%s", id.getName()));
+                writer.attribute("xmlns:ns", id.getNamespace());
+            } else {
+                writer.startElement(extraDescr.getKey().toString());
+            }
+            writer.characters(extraDescr.getValue().toString());
+            writer.endElement();
+        }
+
+        writer.endElement();
+    }
+
+    private static String getConfs(Artifact artifact) {
+        return Joiner.on(",").join(artifact.getConfigurations());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.java
new file mode 100644
index 0000000..54324cb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/LocalComponentFactory.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.internal.artifacts.ModuleInternal;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+import java.util.Set;
+
+public interface LocalComponentFactory {
+    MutableLocalComponentMetaData convert(Set<? extends Configuration> configurations, ModuleInternal module);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/NamespaceId.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/NamespaceId.java
new file mode 100644
index 0000000..f110cc6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/NamespaceId.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.commons.lang.builder.HashCodeBuilder;
+import org.gradle.api.Incubating;
+
+import java.io.Serializable;
+
+/**
+ * Represents an identifier containing a tuple of namespace and name for use when
+ * consuming/producing namespaced elements in descriptors.
+ */
+ at Incubating
+public class NamespaceId implements Serializable {
+    private String namespace;
+    private String name;
+
+    public NamespaceId(String namespace, String name) {
+        this.namespace = namespace;
+        this.name = name;
+    }
+
+    /**
+     * Gets the namespace for this identifier.
+     *
+     * @return the namespace for this identifier
+     */
+    public String getNamespace() {
+        return namespace;
+    }
+
+    /**
+     * Sets the namespace for this identifier.
+     *
+     * @param namespace
+     */
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    /**
+     * Gets the name for this identifier.
+     *
+     * @return the name for this identifier
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the name for this identifier.
+     *
+     * @param name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o == null || o.getClass() != getClass()) {
+            return false;
+        }
+
+        NamespaceId other = (NamespaceId) o;
+        return other.getName().equals(name) && other.getNamespace().equals(namespace);
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 31)
+                .append(namespace)
+                .append(name)
+                .toHashCode();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
new file mode 100644
index 0000000..5c55884
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolver.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
+import org.gradle.api.internal.artifacts.CachingDependencyResolveContext;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.specs.Spec;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class SelfResolvingDependencyResolver implements ArtifactDependencyResolver {
+    private final ArtifactDependencyResolver resolver;
+
+    public SelfResolvingDependencyResolver(ArtifactDependencyResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void resolve(ConfigurationInternal configuration,
+                        List<? extends ResolutionAwareRepository> repositories,
+                        GlobalDependencyResolutionRules metadataHandler,
+                        ResolverResults results) throws ResolveException {
+        resolver.resolve(configuration, repositories, metadataHandler, results);
+        ResolvedConfiguration resolvedConfiguration = results.getResolvedConfiguration();
+        Set<Dependency> dependencies = configuration.getAllDependencies();
+        CachingDependencyResolveContext resolveContext = new CachingDependencyResolveContext(configuration.isTransitive());
+        SelfResolvingFilesProvider provider = new SelfResolvingFilesProvider(resolveContext, dependencies);
+
+        results.withResolvedConfiguration(new FilesAggregatingResolvedConfiguration(resolvedConfiguration, provider));
+    }
+
+    protected static class SelfResolvingFilesProvider {
+
+        final CachingDependencyResolveContext resolveContext;
+        final Set<Dependency> dependencies;
+
+        public SelfResolvingFilesProvider(CachingDependencyResolveContext resolveContext, Set<Dependency> dependencies) {
+            this.resolveContext = resolveContext;
+            this.dependencies = dependencies;
+        }
+
+        Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+            Set<Dependency> selectedDependencies = CollectionUtils.filter(dependencies, dependencySpec);
+            for (Dependency dependency : selectedDependencies) {
+                resolveContext.add(dependency);
+            }
+            return resolveContext.resolve().getFiles();
+        }
+    }
+
+    protected static class FilesAggregatingResolvedConfiguration implements ResolvedConfiguration {
+        final ResolvedConfiguration resolvedConfiguration;
+        final SelfResolvingFilesProvider selfResolvingFilesProvider;
+
+        FilesAggregatingResolvedConfiguration(ResolvedConfiguration resolvedConfiguration, SelfResolvingFilesProvider selfResolvingFilesProvider) {
+            this.resolvedConfiguration = resolvedConfiguration;
+            this.selfResolvingFilesProvider = selfResolvingFilesProvider;
+        }
+
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+            Set<File> files = new LinkedHashSet<File>();
+            files.addAll(selfResolvingFilesProvider.getFiles(dependencySpec));
+            files.addAll(resolvedConfiguration.getFiles(dependencySpec));
+            return files;
+        }
+
+        public Set<ResolvedArtifact> getResolvedArtifacts() {
+            return resolvedConfiguration.getResolvedArtifacts();
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
+            return resolvedConfiguration.getFirstLevelModuleDependencies();
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
+        }
+
+        public boolean hasError() {
+            return resolvedConfiguration.hasError();
+        }
+
+        public LenientConfiguration getLenientConfiguration() {
+            return resolvedConfiguration.getLenientConfiguration();
+        }
+
+        public void rethrowFailure() throws GradleException {
+            resolvedConfiguration.rethrowFailure();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
new file mode 100644
index 0000000..cd3a427
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolver.java
@@ -0,0 +1,107 @@
+/*
+ * 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.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+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.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.DefaultResolvedProjectConfigurationResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.DefaultResolutionResultBuilder;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.specs.Spec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class ShortcircuitEmptyConfigsArtifactDependencyResolver implements ArtifactDependencyResolver {
+    private final ArtifactDependencyResolver dependencyResolver;
+    private final ComponentIdentifierFactory componentIdentifierFactory;
+
+    public ShortcircuitEmptyConfigsArtifactDependencyResolver(ArtifactDependencyResolver dependencyResolver, ComponentIdentifierFactory componentIdentifierFactory) {
+        this.dependencyResolver = dependencyResolver;
+        this.componentIdentifierFactory = componentIdentifierFactory;
+    }
+
+    public void resolve(ConfigurationInternal configuration,
+                        List<? extends ResolutionAwareRepository> repositories,
+                        GlobalDependencyResolutionRules metadataHandler,
+                        ResolverResults results) throws ResolveException {
+        if (configuration.getAllDependencies().isEmpty()) {
+            ModuleVersionIdentifier id = DefaultModuleVersionIdentifier.newId(configuration.getModule());
+            ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(configuration.getModule());
+            ResolutionResult emptyResult = new DefaultResolutionResultBuilder().start(id, componentIdentifier).complete();
+            ResolvedProjectConfigurationResults emptyProjectResult = new DefaultResolvedProjectConfigurationResultBuilder().complete();
+            results.resolved(new EmptyResolvedConfiguration(), emptyResult, emptyProjectResult);
+        } else {
+            dependencyResolver.resolve(configuration, repositories, metadataHandler, results);
+        }
+    }
+
+    private static class EmptyResolvedConfiguration implements ResolvedConfiguration {
+
+        public boolean hasError() {
+            return false;
+        }
+
+        public LenientConfiguration getLenientConfiguration() {
+            return new LenientConfiguration() {
+                public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) {
+                    return Collections.emptySet();
+                }
+
+                public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
+                    return Collections.emptySet();
+                }
+
+                public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+                    return Collections.emptySet();
+                }
+
+                public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpec) {
+                    return Collections.emptySet();
+                }
+            };
+        }
+
+        public void rethrowFailure() throws ResolveException {
+        }
+
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
+            return Collections.emptySet();
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
+            return Collections.emptySet();
+        }
+
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            return Collections.emptySet();
+        }
+
+        public Set<ResolvedArtifact> getResolvedArtifacts() {
+            return Collections.emptySet();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
new file mode 100644
index 0000000..663c864
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.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.artifacts.ivyservice.clientmodule;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.gradle.api.artifacts.ClientModule;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+import java.util.List;
+
+public class ClientModuleResolver implements ComponentMetaDataResolver {
+    private final ComponentMetaDataResolver resolver;
+    private final DependencyDescriptorFactory dependencyDescriptorFactory;
+
+    public ClientModuleResolver(ComponentMetaDataResolver resolver, DependencyDescriptorFactory dependencyDescriptorFactory) {
+        this.resolver = resolver;
+        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+    }
+
+    public void resolve(DependencyMetaData dependency, ComponentIdentifier identifier, BuildableComponentResolveResult result) {
+        resolver.resolve(dependency, identifier, result);
+
+        if (result.getFailure() != null) {
+            return;
+        }
+        if (dependency instanceof DslOriginDependencyMetaData) {
+            ModuleDependency maybeClientModule = ((DslOriginDependencyMetaData) dependency).getSource();
+            if (maybeClientModule instanceof ClientModule) {
+                ClientModule clientModule = (ClientModule) maybeClientModule;
+
+                MutableModuleComponentResolveMetaData clientModuleMetaData = ((MutableModuleComponentResolveMetaData)result.getMetaData()).copy();
+                addClientModuleDependencies(clientModule, clientModuleMetaData);
+
+                setClientModuleArtifact(clientModuleMetaData);
+
+                result.setMetaData(clientModuleMetaData);
+            }
+        }
+    }
+
+    private void addClientModuleDependencies(ClientModule clientModule, MutableModuleComponentResolveMetaData clientModuleMetaData) {
+        List<DependencyMetaData> dependencies = Lists.newArrayList();
+        for (ModuleDependency moduleDependency : clientModule.getDependencies()) {
+            DependencyMetaData dependencyMetaData = dependencyDescriptorFactory.createDependencyDescriptor(moduleDependency.getConfiguration(), clientModuleMetaData.getDescriptor(), moduleDependency);
+            dependencies.add(dependencyMetaData);
+        }
+        clientModuleMetaData.setDependencies(dependencies);
+    }
+
+    private void setClientModuleArtifact(MutableModuleComponentResolveMetaData clientModuleMetaData) {
+        ModuleComponentArtifactMetaData artifact = clientModuleMetaData.artifact("jar", "jar", null);
+        clientModuleMetaData.setArtifacts(Sets.newHashSet(artifact));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.java
new file mode 100644
index 0000000..68a990c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultCachedModuleVersionList.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.api.internal.artifacts.ivyservice.dynamicversions;
+
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.util.Set;
+
+class DefaultCachedModuleVersionList implements ModuleVersionsCache.CachedModuleVersionList {
+    private final Set<String> moduleVersions;
+    private final long ageMillis;
+
+    public DefaultCachedModuleVersionList(ModuleVersionsCacheEntry entry, BuildCommencedTimeProvider timeProvider) {
+        this.moduleVersions = entry.moduleVersionListing;
+        ageMillis = timeProvider.getCurrentTime() - entry.createTimestamp;
+    }
+
+    public Set<String> getModuleVersions() {
+        return moduleVersions;
+    }
+
+    public long getAgeMillis() {
+        return ageMillis;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/DefaultResolvedModuleVersion.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java
new file mode 100644
index 0000000..33f4410
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCache.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+
+import java.util.Set;
+
+public interface ModuleVersionsCache {
+
+    void cacheModuleVersionList(ModuleComponentRepository repository, ModuleIdentifier moduleId, Set<String> listedVersions);
+
+    CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository repository, ModuleIdentifier moduleId);
+
+    interface CachedModuleVersionList {
+        Set<String> getModuleVersions();
+
+        long getAgeMillis();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.java
new file mode 100644
index 0000000..fbfc672
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/ModuleVersionsCacheEntry.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.dynamicversions;
+
+import java.util.Set;
+
+class ModuleVersionsCacheEntry {
+    public Set<String> moduleVersionListing;
+    public long createTimestamp;
+
+    ModuleVersionsCacheEntry(Set<String> moduleVersionListing, long createTimestamp) {
+        this.moduleVersionListing = moduleVersionListing;
+        this.createTimestamp = createTimestamp;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java
new file mode 100644
index 0000000..18f0a9e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dynamicversions/SingleFileBackedModuleVersionsCache.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.dynamicversions;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.util.BuildCommencedTimeProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class SingleFileBackedModuleVersionsCache implements ModuleVersionsCache {
+    private static final Logger LOGGER = LoggerFactory.getLogger(SingleFileBackedModuleVersionsCache.class);
+
+    private final BuildCommencedTimeProvider timeProvider;
+    private final CacheLockingManager cacheLockingManager;
+    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> cache;
+
+    public SingleFileBackedModuleVersionsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        this.timeProvider = timeProvider;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> getCache() {
+        if (cache == null) {
+            cache = initCache();
+        }
+        return cache;
+    }
+
+    private PersistentIndexedCache<ModuleKey, ModuleVersionsCacheEntry> initCache() {
+        return cacheLockingManager.createCache("module-versions", new ModuleKeySerializer(), new ModuleVersionsCacheEntrySerializer());
+    }
+
+    public void cacheModuleVersionList(ModuleComponentRepository repository, ModuleIdentifier moduleId, Set<String> listedVersions) {
+        LOGGER.debug("Caching version list in module versions cache: Using '{}' for '{}'", listedVersions, moduleId);
+        getCache().put(createKey(repository, moduleId), createEntry(listedVersions));
+    }
+
+    public CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository repository, ModuleIdentifier moduleId) {
+        ModuleVersionsCacheEntry moduleVersionsCacheEntry = getCache().get(createKey(repository, moduleId));
+        if (moduleVersionsCacheEntry == null) {
+            return null;
+        }
+        return new DefaultCachedModuleVersionList(moduleVersionsCacheEntry, timeProvider);
+    }
+
+    private ModuleKey createKey(ModuleComponentRepository repository, ModuleIdentifier moduleId) {
+        return new ModuleKey(repository.getId(), moduleId);
+    }
+
+    private ModuleVersionsCacheEntry createEntry(Set<String> listedVersions) {
+        return new ModuleVersionsCacheEntry(listedVersions, timeProvider.getCurrentTime());
+    }
+
+    private static class ModuleKey {
+        private final String repositoryId;
+        private final ModuleIdentifier moduleId;
+
+        private ModuleKey(String repositoryId, ModuleIdentifier moduleId) {
+            this.repositoryId = repositoryId;
+            this.moduleId = moduleId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || !(o instanceof ModuleKey)) {
+                return false;
+            }
+            ModuleKey other = (ModuleKey) o;
+            return repositoryId.equals(other.repositoryId) && moduleId.equals(other.moduleId);
+        }
+
+        @Override
+        public int hashCode() {
+            return repositoryId.hashCode() ^ moduleId.hashCode();
+        }
+    }
+
+    private static class ModuleKeySerializer implements Serializer<ModuleKey> {
+        public void write(Encoder encoder, ModuleKey value) throws Exception {
+            encoder.writeString(value.repositoryId);
+            encoder.writeString(value.moduleId.getGroup());
+            encoder.writeString(value.moduleId.getName());
+        }
+
+        public ModuleKey read(Decoder decoder) throws Exception {
+            String resolverId = decoder.readString();
+            String group = decoder.readString();
+            String module = decoder.readString();
+            return new ModuleKey(resolverId, new DefaultModuleIdentifier(group, module));
+        }
+    }
+
+    private static class ModuleVersionsCacheEntrySerializer implements Serializer<ModuleVersionsCacheEntry> {
+
+        public void write(Encoder encoder, ModuleVersionsCacheEntry value) throws Exception {
+            Set<String> versions = value.moduleVersionListing;
+            encoder.writeInt(versions.size());
+            for (String version : versions) {
+                encoder.writeString(version);
+            }
+            encoder.writeLong(value.createTimestamp);
+        }
+
+        public ModuleVersionsCacheEntry read(Decoder decoder) throws Exception {
+            int size = decoder.readInt();
+            Set<String> versions = new LinkedHashSet<String>();
+            for (int i = 0; i < size; i++) {
+                versions.add(decoder.readString());
+            }
+            long createTimestamp = decoder.readLong();
+            return new ModuleVersionsCacheEntry(versions, createTimestamp);
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java
new file mode 100644
index 0000000..c185ca7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 BaseModuleComponentRepository implements ModuleComponentRepository {
+    protected final ModuleComponentRepository delegate;
+    private final ModuleComponentRepositoryAccess localAccess;
+    private final ModuleComponentRepositoryAccess remoteAccess;
+
+    public BaseModuleComponentRepository(ModuleComponentRepository delegate, ModuleComponentRepositoryAccess localAccess, ModuleComponentRepositoryAccess remoteAccess) {
+        this.delegate = delegate;
+        this.localAccess = localAccess;
+        this.remoteAccess = remoteAccess;
+    }
+
+    public BaseModuleComponentRepository(ModuleComponentRepository delegate) {
+        this.delegate = delegate;
+        this.localAccess = delegate.getLocalAccess();
+        this.remoteAccess = delegate.getRemoteAccess();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    public String getId() {
+        return delegate.getId();
+    }
+
+    public String getName() {
+        return delegate.getName();
+    }
+
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return localAccess;
+    }
+
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return remoteAccess;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java
new file mode 100644
index 0000000..0c7cdf3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.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.api.internal.artifacts.ivyservice.ivyresolve;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.*;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+public class BaseModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess {
+    private final ModuleComponentRepositoryAccess delegate;
+
+    public BaseModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate) {
+        this.delegate = delegate;
+    }
+
+    public ModuleComponentRepositoryAccess getDelegate() {
+        return delegate;
+    }
+
+    public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+        delegate.listModuleVersions(dependency, result);
+    }
+
+    public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        delegate.resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        delegate.resolveModuleArtifacts(component, artifactType, result);
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+        delegate.resolveModuleArtifacts(component, componentUsage, result);
+    }
+
+    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        delegate.resolveArtifact(artifact, moduleSource, result);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockReleasingModuleComponentsRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockReleasingModuleComponentsRepository.java
new file mode 100644
index 0000000..88c7c35
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CacheLockReleasingModuleComponentsRepository.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.internal.component.model.*;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+/**
+ * A wrapper around a {@link ModuleComponentRepository} that handles releasing the cache lock before making remote calls.
+ */
+public class CacheLockReleasingModuleComponentsRepository extends BaseModuleComponentRepository {
+    private final ModuleComponentRepositoryAccess remoteAccess;
+
+    public CacheLockReleasingModuleComponentsRepository(ModuleComponentRepository repository, CacheLockingManager cacheLockingManager) {
+        super(repository);
+        this.remoteAccess = new LockReleasingRepositoryAccess(repository.getName(), repository.getRemoteAccess(), cacheLockingManager);
+    }
+
+    @Override
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return remoteAccess;
+    }
+
+    private static class LockReleasingRepositoryAccess implements ModuleComponentRepositoryAccess {
+        private final String name;
+        private final ModuleComponentRepositoryAccess delegate;
+        private final CacheLockingManager cacheLockingManager;
+
+        @Override
+        public String toString() {
+            return "unlocking > " + delegate.toString();
+        }
+
+        private LockReleasingRepositoryAccess(String name, ModuleComponentRepositoryAccess delegate, CacheLockingManager cacheLockingManager) {
+            this.name = name;
+            this.delegate = delegate;
+            this.cacheLockingManager = cacheLockingManager;
+        }
+
+        public void listModuleVersions(final DependencyMetaData dependency, final BuildableModuleVersionListingResolveResult result) {
+            cacheLockingManager.longRunningOperation(String.format("List %s using repository %s", dependency, name), new Runnable() {
+                public void run() {
+                    delegate.listModuleVersions(dependency, result);
+                }
+            });
+        }
+
+        public void resolveComponentMetaData(final DependencyMetaData dependency, final ModuleComponentIdentifier moduleComponentIdentifier, final BuildableModuleComponentMetaDataResolveResult result) {
+            cacheLockingManager.longRunningOperation(String.format("Resolve %s using repository %s", dependency, name), new Runnable() {
+                public void run() {
+                    delegate.resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+                }
+            });
+        }
+
+        public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ArtifactType artifactType, final BuildableArtifactSetResolveResult result) {
+            cacheLockingManager.longRunningOperation(String.format("Resolve %s for %s using repository %s", artifactType, component, name), new Runnable() {
+                public void run() {
+                    delegate.resolveModuleArtifacts(component, artifactType, result);
+                }
+            });
+        }
+
+        public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ComponentUsage componentUsage, final BuildableArtifactSetResolveResult result) {
+            cacheLockingManager.longRunningOperation(String.format("Resolve %s for %s using repository %s", componentUsage, component, name), new Runnable() {
+                public void run() {
+                    delegate.resolveModuleArtifacts(component, componentUsage, result);
+                }
+            });
+        }
+
+
+        public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
+            cacheLockingManager.longRunningOperation(String.format("Download %s using repository %s", artifact, name), new Runnable() {
+                public void run() {
+                    delegate.resolveArtifact(artifact, moduleSource, result);
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java
new file mode 100644
index 0000000..c307ed1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.Transformer;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.ArtifactNotFoundException;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+import org.gradle.internal.resource.cached.CachedArtifact;
+import org.gradle.internal.resource.cached.CachedArtifactIndex;
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryKey;
+import org.gradle.util.BuildCommencedTimeProvider;
+import org.gradle.util.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.util.Set;
+
+public class CachingModuleComponentRepository implements ModuleComponentRepository {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CachingModuleComponentRepository.class);
+
+    private final ModuleVersionsCache moduleVersionsCache;
+    private final ModuleMetaDataCache moduleMetaDataCache;
+    private final ModuleArtifactsCache moduleArtifactsCache;
+    private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
+
+    private final CachePolicy cachePolicy;
+
+    private final ModuleComponentRepository delegate;
+    private final BuildCommencedTimeProvider timeProvider;
+    private final ComponentMetadataProcessor metadataProcessor;
+    private LocateInCacheRepositoryAccess locateInCacheRepositoryAccess = new LocateInCacheRepositoryAccess();
+    private ResolveAndCacheRepositoryAccess resolveAndCacheRepositoryAccess = new ResolveAndCacheRepositoryAccess();
+
+    public CachingModuleComponentRepository(ModuleComponentRepository delegate, ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache,
+                                            ModuleArtifactsCache moduleArtifactsCache, CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
+                                            CachePolicy cachePolicy, BuildCommencedTimeProvider timeProvider,
+                                            ComponentMetadataProcessor metadataProcessor) {
+        this.delegate = delegate;
+        this.moduleMetaDataCache = moduleMetaDataCache;
+        this.moduleVersionsCache = moduleVersionsCache;
+        this.moduleArtifactsCache = moduleArtifactsCache;
+        this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
+        this.timeProvider = timeProvider;
+        this.cachePolicy = cachePolicy;
+        this.metadataProcessor = metadataProcessor;
+    }
+
+    public String getId() {
+        return delegate.getId();
+    }
+
+    public String getName() {
+        return delegate.getName();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return locateInCacheRepositoryAccess;
+    }
+
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return resolveAndCacheRepositoryAccess;
+    }
+
+    private DefaultModuleIdentifier getCacheKey(ModuleVersionSelector requested) {
+        return new DefaultModuleIdentifier(requested.getGroup(), requested.getName());
+    }
+
+    private class LocateInCacheRepositoryAccess implements ModuleComponentRepositoryAccess {
+        @Override
+        public String toString() {
+            return "cache lookup for " + delegate.toString();
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            delegate.getLocalAccess().listModuleVersions(dependency, result);
+            if (result.hasResult()) {
+                return;
+            }
+
+            listModuleVersionsFromCache(dependency, result);
+        }
+
+        private void listModuleVersionsFromCache(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            ModuleVersionSelector requested = dependency.getRequested();
+            final ModuleIdentifier moduleId = getCacheKey(requested);
+            ModuleVersionsCache.CachedModuleVersionList cachedModuleVersionList = moduleVersionsCache.getCachedModuleResolution(delegate, moduleId);
+            if (cachedModuleVersionList != null) {
+                Set<String> versionList = cachedModuleVersionList.getModuleVersions();
+                Set<ModuleVersionIdentifier> versions = CollectionUtils.collect(versionList, new Transformer<ModuleVersionIdentifier, String>() {
+                    public ModuleVersionIdentifier transform(String original) {
+                        return new DefaultModuleVersionIdentifier(moduleId, original);
+                    }
+                });
+                if (cachePolicy.mustRefreshVersionList(moduleId, versions, cachedModuleVersionList.getAgeMillis())) {
+                    LOGGER.debug("Version listing in dynamic revision cache is expired: will perform fresh resolve of '{}' in '{}'", requested, delegate.getName());
+                } else {
+                    result.listed(versionList);
+                    // When age == 0, verified since the start of this build, assume listing hasn't changed
+                    result.setAuthoritative(cachedModuleVersionList.getAgeMillis() == 0);
+                }
+            }
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            delegate.getLocalAccess().resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+            if (result.hasResult()) {
+                return;
+            }
+
+            resolveComponentMetaDataFromCache(dependency, moduleComponentIdentifier, result);
+        }
+
+        private void resolveComponentMetaDataFromCache(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            ModuleMetaDataCache.CachedMetaData cachedMetaData = moduleMetaDataCache.getCachedModuleDescriptor(delegate, moduleComponentIdentifier);
+            if (cachedMetaData == null) {
+                return;
+            }
+            if (cachedMetaData.isMissing()) {
+                if (cachePolicy.mustRefreshMissingModule(moduleComponentIdentifier, cachedMetaData.getAgeMillis())) {
+                    LOGGER.debug("Cached meta-data for missing module is expired: will perform fresh resolve of '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
+                    return;
+                }
+                LOGGER.debug("Detected non-existence of module '{}' in resolver cache '{}'", moduleComponentIdentifier, delegate.getName());
+                result.missing();
+                // When age == 0, verified since the start of this build, assume still missing
+                result.setAuthoritative(cachedMetaData.getAgeMillis() == 0);
+                return;
+            }
+            MutableModuleComponentResolveMetaData metaData = cachedMetaData.getMetaData();
+            metadataProcessor.processMetadata(metaData);
+            if (dependency.isChanging() || metaData.isChanging()) {
+                if (cachePolicy.mustRefreshChangingModule(moduleComponentIdentifier, cachedMetaData.getModuleVersion(), cachedMetaData.getAgeMillis())) {
+                    LOGGER.debug("Cached meta-data for changing module is expired: will perform fresh resolve of '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
+                    return;
+                }
+                LOGGER.debug("Found cached version of changing module '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
+            } else {
+                if (cachePolicy.mustRefreshModule(moduleComponentIdentifier, cachedMetaData.getModuleVersion(), cachedMetaData.getAgeMillis())) {
+                    LOGGER.debug("Cached meta-data for module must be refreshed: will perform fresh resolve of '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
+                    return;
+                }
+            }
+
+            LOGGER.debug("Using cached module metadata for module '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
+            metaData.setSource(new CachingModuleSource(cachedMetaData.getDescriptorHash(), metaData.isChanging(), metaData.getSource()));
+            result.resolved(metaData);
+            // When age == 0, verified since the start of this build, assume the meta-data hasn't changed
+            result.setAuthoritative(cachedMetaData.getAgeMillis() == 0);
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            final CachingModuleSource cachedModuleSource = (CachingModuleSource) component.getSource();
+
+            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            delegate.getLocalAccess().resolveModuleArtifacts(component.withSource(cachedModuleSource.getDelegate()), artifactType, result);
+            if (result.hasResult()) {
+                return;
+            }
+
+            resolveModuleArtifactsFromCache(cacheKey(artifactType), component, result, cachedModuleSource);
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            final CachingModuleSource cachedModuleSource = (CachingModuleSource) component.getSource();
+
+            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            delegate.getLocalAccess().resolveModuleArtifacts(component.withSource(cachedModuleSource.getDelegate()), componentUsage, result);
+            if (result.hasResult()) {
+                return;
+            }
+
+            resolveModuleArtifactsFromCache(cacheKey(componentUsage), component, result, cachedModuleSource);
+        }
+
+        private void resolveModuleArtifactsFromCache(String contextId, ComponentResolveMetaData component, BuildableArtifactSetResolveResult result, CachingModuleSource cachedModuleSource) {
+            ModuleArtifactsCache.CachedArtifacts cachedModuleArtifacts = moduleArtifactsCache.getCachedArtifacts(delegate, component.getId(), contextId);
+            BigInteger moduleDescriptorHash = cachedModuleSource.getDescriptorHash();
+
+            if (cachedModuleArtifacts != null) {
+                if (!cachePolicy.mustRefreshModuleArtifacts(component.getId(), null, cachedModuleArtifacts.getAgeMillis(),
+                        cachedModuleSource.isChangingModule(), moduleDescriptorHash.equals(cachedModuleArtifacts.getDescriptorHash()))) {
+                    Set<ModuleComponentArtifactMetaData> artifactMetaDataSet = CollectionUtils.collect(cachedModuleArtifacts.getArtifacts(), new ArtifactIdToMetaData());
+                    result.resolved(artifactMetaDataSet);
+                    return;
+                }
+
+                LOGGER.debug("Artifact listing has expired: will perform fresh resolve of '{}' for '{}' in '{}'", contextId, component.getId(), delegate.getName());
+            }
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            final CachingModuleSource cachedModuleSource = (CachingModuleSource) moduleSource;
+
+            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            delegate.getLocalAccess().resolveArtifact(artifact, cachedModuleSource.getDelegate(), result);
+            if (result.hasResult()) {
+                return;
+            }
+
+            resolveArtifactFromCache(artifact, cachedModuleSource, result);
+        }
+
+        private void resolveArtifactFromCache(ComponentArtifactMetaData artifact, CachingModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            CachedArtifact cached = artifactAtRepositoryCachedResolutionIndex.lookup(artifactCacheKey(artifact));
+            final BigInteger descriptorHash = moduleSource.getDescriptorHash();
+            if (cached != null) {
+                long age = timeProvider.getCurrentTime() - cached.getCachedAt();
+                final boolean isChangingModule = moduleSource.isChangingModule();
+                ArtifactIdentifier artifactIdentifier = ((ModuleComponentArtifactMetaData) artifact).toArtifactIdentifier();
+                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);
+                        for (String location : cached.attemptedLocations()) {
+                            result.attempted(location);
+                        }
+                        result.notFound(artifact.getId());
+                    }
+                } else {
+                    File cachedArtifactFile = cached.getCachedFile();
+                    if (!cachePolicy.mustRefreshArtifact(artifactIdentifier, cachedArtifactFile, age, isChangingModule, descriptorHash.equals(cached.getDescriptorHash()))) {
+                        LOGGER.debug("Found artifact '{}' in resolver cache: {}", artifact, cachedArtifactFile);
+                        result.resolved(cachedArtifactFile);
+                    }
+                }
+            }
+        }
+    }
+
+    private class ResolveAndCacheRepositoryAccess implements ModuleComponentRepositoryAccess {
+        @Override
+        public String toString() {
+            return "cache > " + delegate.getRemoteAccess().toString();
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            delegate.getRemoteAccess().listModuleVersions(dependency, result);
+            switch (result.getState()) {
+                case Listed:
+                    ModuleIdentifier moduleId = getCacheKey(dependency.getRequested());
+                    Set<String> versionList = result.getVersions();
+                    moduleVersionsCache.cacheModuleVersionList(delegate, moduleId, versionList);
+                    break;
+                case Failed:
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected state on listModuleVersions: " + result.getState());
+            }
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            DependencyMetaData forced = dependency.withChanging();
+            delegate.getRemoteAccess().resolveComponentMetaData(forced, moduleComponentIdentifier, result);
+            switch (result.getState()) {
+                case Missing:
+                    moduleMetaDataCache.cacheMissing(delegate, moduleComponentIdentifier);
+                    break;
+                case Resolved:
+                    MutableModuleComponentResolveMetaData metaData = result.getMetaData();
+                    ModuleSource moduleSource = metaData.getSource();
+                    ModuleMetaDataCache.CachedMetaData cachedMetaData = moduleMetaDataCache.cacheMetaData(delegate, metaData);
+                    metadataProcessor.processMetadata(metaData);
+                    moduleSource = new CachingModuleSource(cachedMetaData.getDescriptorHash(), dependency.isChanging() || metaData.isChanging(), moduleSource);
+                    metaData.setSource(moduleSource);
+                    result.resolved(metaData);
+                    break;
+                case Failed:
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected resolve state: " + result.getState());
+            }
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            final CachingModuleSource moduleSource = (CachingModuleSource) component.getSource();
+            delegate.getRemoteAccess().resolveModuleArtifacts(component.withSource(moduleSource.getDelegate()), artifactType, result);
+
+            maybeCache(component, result, moduleSource, cacheKey(artifactType));
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            final CachingModuleSource moduleSource = (CachingModuleSource) component.getSource();
+            delegate.getRemoteAccess().resolveModuleArtifacts(component.withSource(moduleSource.getDelegate()), componentUsage, result);
+
+            maybeCache(component, result, moduleSource, cacheKey(componentUsage));
+        }
+
+        private void maybeCache(ComponentResolveMetaData component, BuildableArtifactSetResolveResult result, CachingModuleSource moduleSource, String contextId) {
+            if (result.getFailure() == null) {
+                Set<ModuleComponentArtifactIdentifier> artifactIdentifierSet = CollectionUtils.collect(result.getArtifacts(), new ArtifactMetaDataToId());
+                moduleArtifactsCache.cacheArtifacts(delegate, component.getId(), contextId, moduleSource.getDescriptorHash(), artifactIdentifierSet);
+            }
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            final CachingModuleSource cachingModuleSource = (CachingModuleSource) moduleSource;
+
+            delegate.getRemoteAccess().resolveArtifact(artifact, cachingModuleSource.getDelegate(), result);
+            LOGGER.debug("Downloaded artifact '{}' from resolver: {}", artifact, delegate.getName());
+
+            ArtifactResolveException failure = result.getFailure();
+            if (failure == null) {
+                artifactAtRepositoryCachedResolutionIndex.store(artifactCacheKey(artifact), result.getFile(), cachingModuleSource.getDescriptorHash());
+            } else if (failure instanceof ArtifactNotFoundException) {
+                artifactAtRepositoryCachedResolutionIndex.storeMissing(artifactCacheKey(artifact), result.getAttempted(), cachingModuleSource.getDescriptorHash());
+            }
+        }
+    }
+
+    private String cacheKey(ArtifactType artifactType) {
+        return "artifacts:" + artifactType.name();
+    }
+
+    private String cacheKey(ComponentUsage context) {
+        return "configuration:" + context.getConfigurationName();
+    }
+
+    private ArtifactAtRepositoryKey artifactCacheKey(ComponentArtifactMetaData artifact) {
+        // TODO:ADAM - Don't assume this
+        ModuleComponentArtifactMetaData moduleComponentArtifactMetaData = (ModuleComponentArtifactMetaData) artifact;
+        return new ArtifactAtRepositoryKey(delegate.getId(), moduleComponentArtifactMetaData.getId());
+    }
+
+    static class CachingModuleSource implements ModuleSource {
+        private final BigInteger descriptorHash;
+        private final boolean changingModule;
+        private final ModuleSource delegate;
+
+        public CachingModuleSource(BigInteger descriptorHash, boolean changingModule, ModuleSource delegate) {
+            this.delegate = delegate;
+            this.descriptorHash = descriptorHash;
+            this.changingModule = changingModule;
+        }
+
+        public BigInteger getDescriptorHash() {
+            return descriptorHash;
+        }
+
+        public boolean isChangingModule() {
+            return changingModule;
+        }
+
+        public ModuleSource getDelegate() {
+            return delegate;
+        }
+    }
+
+    static class ArtifactIdToMetaData implements Transformer<ModuleComponentArtifactMetaData, ModuleComponentArtifactIdentifier> {
+        public ModuleComponentArtifactMetaData transform(ModuleComponentArtifactIdentifier original) {
+            return new DefaultModuleComponentArtifactMetaData(original);
+        }
+    }
+
+    static class ArtifactMetaDataToId implements Transformer<ModuleComponentArtifactIdentifier, ComponentArtifactMetaData> {
+        public ModuleComponentArtifactIdentifier transform(ComponentArtifactMetaData original) {
+            return ((ModuleComponentArtifactMetaData)original).getId();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java
new file mode 100644
index 0000000..63c0e2a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.DefaultBuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+
+class ComponentMetaDataResolveState {
+    private final DefaultBuildableModuleComponentMetaDataResolveResult resolveResult = new DefaultBuildableModuleComponentMetaDataResolveResult();
+    private final VersionedComponentChooser versionedComponentChooser;
+    private final DependencyMetaData dependency;
+    private final ModuleComponentIdentifier componentIdentifier;
+    final ModuleComponentRepository repository;
+
+    private boolean searchedLocally;
+    private boolean searchedRemotely;
+
+    public ComponentMetaDataResolveState(DependencyMetaData dependency, ModuleComponentIdentifier componentIdentifier, ModuleComponentRepository repository, VersionedComponentChooser versionedComponentChooser) {
+        this.dependency = dependency;
+        this.componentIdentifier = componentIdentifier;
+        this.repository = repository;
+        this.versionedComponentChooser = versionedComponentChooser;
+    }
+
+    BuildableModuleComponentMetaDataResolveResult resolve() {
+        if (!searchedLocally) {
+            searchedLocally = true;
+            process(repository.getLocalAccess());
+            if (resolveResult.hasResult()) {
+                if (resolveResult.isAuthoritative()) {
+                    // Don't bother searching remotely
+                    searchedRemotely = true;
+                }
+                return resolveResult;
+            }
+            // If unknown, try a remote search
+        }
+
+        if (!searchedRemotely) {
+            searchedRemotely = true;
+            process(repository.getRemoteAccess());
+            return resolveResult;
+        }
+
+        throw new IllegalStateException();
+    }
+
+    protected void process(ModuleComponentRepositoryAccess moduleAccess) {
+        moduleAccess.resolveComponentMetaData(dependency, componentIdentifier, resolveResult);
+        if (resolveResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
+            if (versionedComponentChooser.isRejectedComponent(componentIdentifier, new MetadataProvider(resolveResult))) {
+                resolveResult.missing();
+            }
+        }
+    }
+
+    protected void applyTo(ResourceAwareResolveResult result) {
+        resolveResult.applyTo(result);
+    }
+
+    public boolean canMakeFurtherAttempts() {
+        return !searchedRemotely;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessor.java
new file mode 100644
index 0000000..eb490d9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.InvalidUserCodeException;
+import org.gradle.api.artifacts.ComponentMetadata;
+import org.gradle.api.artifacts.ComponentSelection;
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.ComponentSelectionInternal;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.rules.SpecRuleAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ComponentSelectionRulesProcessor {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ComponentSelectionRulesProcessor.class);
+
+    private final Spec<SpecRuleAction<? super ComponentSelection>> withNoInputs = new Spec<SpecRuleAction<? super ComponentSelection>>() {
+        public boolean isSatisfiedBy(SpecRuleAction<? super ComponentSelection> element) {
+            return element.getAction().getInputTypes().isEmpty();
+        }
+    };
+    private final Spec<SpecRuleAction<? super ComponentSelection>> withInputs = Specs.not(withNoInputs);
+
+    public void apply(ComponentSelectionInternal selection, Collection<SpecRuleAction<? super ComponentSelection>> specRuleActions, MetadataProvider metadataProvider) {
+        if (processRules(specRuleActions, withNoInputs, selection, metadataProvider)) {
+            processRules(specRuleActions, withInputs, selection, metadataProvider);
+        }
+    }
+
+    private boolean processRules(Collection<SpecRuleAction<? super ComponentSelection>> specRuleActions, Spec<SpecRuleAction<? super ComponentSelection>> filter, ComponentSelectionInternal selection, MetadataProvider metadataProvider) {
+        for (SpecRuleAction<? super ComponentSelection> rule : specRuleActions) {
+            if (filter.isSatisfiedBy(rule)) {
+                processRule(rule, selection, metadataProvider);
+
+                if (selection.isRejected()) {
+                    LOGGER.info(String.format("Selection of %s rejected by component selection rule: %s", selection.getCandidate().getDisplayName(), selection.getRejectionReason()));
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private void processRule(SpecRuleAction<? super ComponentSelection> rule, ComponentSelection selection, MetadataProvider metadataProvider) {
+        if (!rule.getSpec().isSatisfiedBy(selection)) {
+            return;
+        }
+
+        List<Object> inputValues = getInputValues(rule.getAction().getInputTypes(), metadataProvider);
+
+        if (inputValues == null) {
+            // Broken meta-data, bail
+            return;
+        }
+
+        if (inputValues.contains(null)) {
+            // If any of the input values are not available for this selection, ignore the rule
+            return;
+        }
+
+        try {
+            rule.getAction().execute(selection, inputValues);
+        } catch (Exception e) {
+            throw new InvalidUserCodeException(String.format("There was an error while evaluating a component selection rule for %s.", selection.getCandidate().getDisplayName()), e);
+        }
+    }
+
+    private List<Object> getInputValues(List<Class<?>> inputTypes, MetadataProvider metadataProvider) {
+        if (inputTypes.size() == 0) {
+            return Collections.emptyList();
+        }
+
+        if (!metadataProvider.resolve()) {
+            return null;
+        }
+
+        List<Object> inputs = new ArrayList<Object>(inputTypes.size());
+        for (Class<?> inputType : inputTypes) {
+            if (inputType == ModuleComponentResolveMetaData.class) {
+                inputs.add(metadataProvider.getMetaData());
+                continue;
+            }
+            if (inputType == ComponentMetadata.class) {
+                inputs.add(metadataProvider.getComponentMetadata());
+                continue;
+            }
+            if (inputType == IvyModuleDescriptor.class) {
+                inputs.add(metadataProvider.getIvyModuleDescriptor());
+                continue;
+            }
+            // We've already validated the inputs: should never get here.
+            throw new IllegalStateException();
+        }
+        return inputs;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java
new file mode 100644
index 0000000..f7e8024
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.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 ConfiguredModuleComponentRepository extends ModuleComponentRepository {
+    boolean isDynamicResolveMode();
+
+    boolean isLocal();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java
new file mode 100644
index 0000000..3c8077d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.ComponentSelection;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ComponentSelectionInternal;
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.DefaultComponentSelection;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableComponentSelectionResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.rules.SpecRuleAction;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+class DefaultVersionedComponentChooser implements VersionedComponentChooser {
+    private final ComponentSelectionRulesProcessor rulesProcessor = new ComponentSelectionRulesProcessor();
+    private final VersionSelectorScheme versionSelectorScheme;
+    private final VersionComparator versionComparator;
+    private final ComponentSelectionRulesInternal componentSelectionRules;
+
+    DefaultVersionedComponentChooser(VersionComparator versionComparator, VersionSelectorScheme versionSelectorScheme, ComponentSelectionRulesInternal componentSelectionRules) {
+        this.versionComparator = versionComparator;
+        this.versionSelectorScheme = versionSelectorScheme;
+        this.componentSelectionRules = componentSelectionRules;
+    }
+
+    public ComponentResolveMetaData selectNewestComponent(ComponentResolveMetaData one, ComponentResolveMetaData two) {
+        if (one == null || two == null) {
+            return two == null ? one : two;
+        }
+
+        int comparison = versionComparator.compare(new VersionInfo(one.getId().getVersion()), new VersionInfo(two.getId().getVersion()));
+
+        if (comparison == 0) {
+            if (isGeneratedModuleDescriptor(one) && !isGeneratedModuleDescriptor(two)) {
+                return two;
+            }
+            return one;
+        }
+
+        return comparison < 0 ? two : one;
+    }
+
+    private boolean isGeneratedModuleDescriptor(ComponentResolveMetaData componentResolveMetaData) {
+        return componentResolveMetaData.isGenerated();
+    }
+
+    public void selectNewestMatchingComponent(Collection<? extends ModuleComponentResolveState> versions, DependencyMetaData dependency, BuildableComponentSelectionResult result) {
+        ModuleVersionSelector requestedModule = dependency.getRequested();
+        VersionSelector requestedVersion = versionSelectorScheme.parseSelector(requestedModule.getVersion());
+        Collection<SpecRuleAction<? super ComponentSelection>> rules = componentSelectionRules.getRules();
+
+        for (ModuleComponentResolveState candidate : sortLatestFirst(versions)) {
+            MetadataProvider metadataProvider = new MetadataProvider(candidate);
+
+            boolean versionMatches = versionMatches(requestedVersion, candidate, metadataProvider);
+            if (!metadataProvider.isUsable()) {
+                applyTo(metadataProvider, result);
+                return;
+            }
+            if (!versionMatches) {
+                result.notMatched(candidate.getVersion());
+                continue;
+            }
+
+            ModuleComponentIdentifier candidateIdentifier = candidate.getId();
+            boolean accepted = !isRejectedByRules(candidateIdentifier, rules, metadataProvider);
+            if (!metadataProvider.isUsable()) {
+                applyTo(metadataProvider, result);
+                return;
+            }
+
+            if (accepted) {
+                result.matches(candidateIdentifier);
+                return;
+            }
+
+            result.rejected(candidate.getVersion());
+            if (requestedVersion.matchesUniqueVersion()) {
+                // Only consider one candidate
+                break;
+            }
+        }
+
+        result.noMatchFound();
+    }
+
+    private void applyTo(MetadataProvider provider, BuildableComponentSelectionResult result) {
+        BuildableModuleComponentMetaDataResolveResult metaDataResult = provider.getResult();
+        switch (metaDataResult.getState()) {
+            case Unknown:
+                // For example, when using a local access to resolve something remote
+                result.noMatchFound();
+                break;
+            case Missing:
+                result.noMatchFound();
+                break;
+            case Failed:
+                result.failed(metaDataResult.getFailure());
+                break;
+            default:
+                throw new IllegalStateException("Unexpected meta-data resolution result.");
+        }
+    }
+
+    private boolean versionMatches(VersionSelector selector, ModuleComponentResolveState component, MetadataProvider metadataProvider) {
+        if (selector.requiresMetadata()) {
+            if (!metadataProvider.resolve()) {
+                return false;
+            }
+            return selector.accept(metadataProvider.getComponentMetadata());
+        } else {
+            return selector.accept(component.getVersion());
+        }
+    }
+
+    public boolean isRejectedComponent(ModuleComponentIdentifier candidateIdentifier, MetadataProvider metadataProvider) {
+        return isRejectedByRules(candidateIdentifier, componentSelectionRules.getRules(), metadataProvider);
+    }
+
+    private boolean isRejectedByRules(ModuleComponentIdentifier candidateIdentifier, Collection<SpecRuleAction<? super ComponentSelection>> rules, MetadataProvider metadataProvider) {
+        ComponentSelectionInternal selection = new DefaultComponentSelection(candidateIdentifier);
+        rulesProcessor.apply(selection, rules, metadataProvider);
+        return selection.isRejected();
+    }
+
+    private List<ModuleComponentResolveState> sortLatestFirst(Collection<? extends ModuleComponentResolveState> listing) {
+        return CollectionUtils.sort(listing, Collections.reverseOrder(versionComparator));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java
new file mode 100644
index 0000000..88be64e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifier.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
+
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
+import org.gradle.internal.hash.HashUtil;
+import org.gradle.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DependencyResolverIdentifier {
+    // 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 static String calculateId(List<String> parts) {
+        String idString = CollectionUtils.join("::", parts);
+        return HashUtil.createHash(idString, "MD5").asHexString();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java
new file mode 100644
index 0000000..c67bda1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.ModuleVersionNotFoundException;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.result.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+import static org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult.State.Failed;
+import static org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult.State.Resolved;
+
+public class DynamicVersionResolver implements DependencyToComponentIdResolver {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVersionResolver.class);
+
+    private final List<ModuleComponentRepository> repositories = new ArrayList<ModuleComponentRepository>();
+    private final List<String> repositoryNames = new ArrayList<String>();
+    private final VersionedComponentChooser versionedComponentChooser;
+    private final Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> metaDataFactory;
+
+    public DynamicVersionResolver(VersionedComponentChooser versionedComponentChooser, Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> metaDataFactory) {
+        this.versionedComponentChooser = versionedComponentChooser;
+        this.metaDataFactory = metaDataFactory;
+    }
+
+    public void add(ModuleComponentRepository repository) {
+        repositories.add(repository);
+        repositoryNames.add(repository.getName());
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result) {
+        ModuleVersionSelector requested = dependency.getRequested();
+        LOGGER.debug("Attempting to resolve {} using repositories {}", requested, repositoryNames);
+        List<Throwable> errors = new ArrayList<Throwable>();
+
+        List<RepositoryResolveState> resolveStates = new ArrayList<RepositoryResolveState>();
+        for (ModuleComponentRepository repository : repositories) {
+            resolveStates.add(new RepositoryResolveState(dependency, repository));
+        }
+
+        final RepositoryChainModuleResolution latestResolved = findLatestModule(resolveStates, errors);
+        if (latestResolved != null) {
+            LOGGER.debug("Using {} from {}", latestResolved.module.getId(), latestResolved.repository);
+            for (Throwable error : errors) {
+                LOGGER.debug("Discarding resolve failure.", error);
+            }
+
+            result.resolved(metaDataFactory.transform(latestResolved));
+            return;
+        }
+        if (!errors.isEmpty()) {
+            result.failed(new ModuleVersionResolveException(requested, errors));
+        } else {
+            notFound(result, requested, resolveStates);
+        }
+    }
+
+    private void notFound(BuildableComponentIdResolveResult result, ModuleVersionSelector requested, List<RepositoryResolveState> resolveStates) {
+        Set<String> unmatchedVersions = new LinkedHashSet<String>();
+        Set<String> rejectedVersions = new LinkedHashSet<String>();
+        for (RepositoryResolveState resolveState : resolveStates) {
+            resolveState.applyTo(result, unmatchedVersions, rejectedVersions);
+        }
+        result.failed(new ModuleVersionNotFoundException(requested, result.getAttempted(), unmatchedVersions, rejectedVersions));
+    }
+
+    private RepositoryChainModuleResolution findLatestModule(List<RepositoryResolveState> resolveStates, Collection<Throwable> failures) {
+        LinkedList<RepositoryResolveState> queue = new LinkedList<RepositoryResolveState>();
+        queue.addAll(resolveStates);
+
+        LinkedList<RepositoryResolveState> missing = new LinkedList<RepositoryResolveState>();
+
+        // A first pass to do local resolves only
+        RepositoryChainModuleResolution best = findLatestModule(queue, failures, missing);
+        if (best != null) {
+            return best;
+        }
+
+        // Nothing found - do a second pass
+        queue.addAll(missing);
+        missing.clear();
+        return findLatestModule(queue, failures, missing);
+    }
+
+    private RepositoryChainModuleResolution findLatestModule(LinkedList<RepositoryResolveState> queue, Collection<Throwable> failures, Collection<RepositoryResolveState> missing) {
+        RepositoryChainModuleResolution best = null;
+        while (!queue.isEmpty()) {
+            RepositoryResolveState request = queue.removeFirst();
+            try {
+                request.resolve();
+            } catch (Throwable t) {
+                failures.add(t);
+                continue;
+            }
+            switch (request.resolveResult.getState()) {
+                case Failed:
+                    failures.add(request.resolveResult.getFailure());
+                    break;
+                case Missing:
+                case Unknown:
+                    // Queue this up for checking again later
+                    if (request.canMakeFurtherAttempts()) {
+                        missing.add(request);
+                    }
+                    break;
+                case Resolved:
+                    RepositoryChainModuleResolution moduleResolution = new RepositoryChainModuleResolution(request.repository, request.resolveResult.getMetaData());
+                    best = chooseBest(best, moduleResolution);
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected state for resolution: " + request.resolveResult.getState());
+            }
+        }
+
+        return best;
+    }
+
+    private RepositoryChainModuleResolution chooseBest(RepositoryChainModuleResolution one, RepositoryChainModuleResolution two) {
+        if (one == null || two == null) {
+            return two == null ? one : two;
+        }
+        return versionedComponentChooser.selectNewestComponent(one.module, two.module) == one.module ? one : two;
+    }
+
+    private static class AttemptCollector implements Action<ResourceAwareResolveResult> {
+        private final List<String> attempts = new ArrayList<String>();
+
+        @Override
+        public void execute(ResourceAwareResolveResult resourceAwareResolveResult) {
+            attempts.addAll(resourceAwareResolveResult.getAttempted());
+        }
+
+        public void applyTo(ResourceAwareResolveResult result) {
+            for (String url : attempts) {
+                result.attempted(url);
+            }
+        }
+    }
+
+    private class RepositoryResolveState {
+        private final DefaultBuildableModuleComponentMetaDataResolveResult resolveResult = new DefaultBuildableModuleComponentMetaDataResolveResult();
+        private final DefaultBuildableComponentSelectionResult componentSelectionResult = new DefaultBuildableComponentSelectionResult();
+        private final Map<String, CandidateResult> candidateComponents = new LinkedHashMap<String, CandidateResult>();
+        private final VersionListResult versionListingResult;
+        private final ModuleComponentRepository repository;
+        private final AttemptCollector attemptCollector;
+        private final DependencyMetaData dependency;
+
+        public RepositoryResolveState(DependencyMetaData dependency, ModuleComponentRepository repository) {
+            this.dependency = dependency;
+            this.repository = repository;
+            this.attemptCollector = new AttemptCollector();
+            versionListingResult = new VersionListResult(dependency, repository);
+        }
+
+        public boolean canMakeFurtherAttempts() {
+            return versionListingResult.canMakeFurtherAttempts();
+        }
+
+        void resolve() {
+            versionListingResult.resolve();
+            switch (versionListingResult.result.getState()) {
+                case Failed:
+                    resolveResult.failed(versionListingResult.result.getFailure());
+                    break;
+                case Listed:
+                    selectMatchingVersionAndResolve();
+                    break;
+                case Unknown:
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected state for version list result.");
+            }
+        }
+
+        private void selectMatchingVersionAndResolve() {
+            // TODO - reuse metaData if it was already fetched to select the component from the version list
+            versionedComponentChooser.selectNewestMatchingComponent(candidates(), dependency, componentSelectionResult);
+            switch (componentSelectionResult.getState()) {
+                // No version matching list: component is missing
+                case NoMatch:
+                    resolveResult.missing();
+                    break;
+                // Found version matching in list: resolve component
+                case Match:
+                    ModuleComponentIdentifier selectedComponentId = componentSelectionResult.getMatch();
+                    CandidateResult candidateResult = candidateComponents.get(selectedComponentId.getVersion());
+                    candidateResult.resolve(resolveResult);
+                    break;
+                case Failed:
+                    resolveResult.failed(componentSelectionResult.getFailure());
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected state for component selection result.");
+            }
+        }
+
+        private List<CandidateResult> candidates() {
+            List<CandidateResult> candidates = new ArrayList<CandidateResult>();
+            for (String version : versionListingResult.result.getVersions()) {
+                CandidateResult candidateResult = candidateComponents.get(version);
+                if (candidateResult == null) {
+                    candidateResult = new CandidateResult(dependency, version, repository, attemptCollector);
+                    candidateComponents.put(version, candidateResult);
+                }
+                candidates.add(candidateResult);
+            }
+            return candidates;
+        }
+
+        protected void applyTo(ResourceAwareResolveResult target, Set<String> unmatchedVersions, Set<String> rejectedVersions) {
+            versionListingResult.applyTo(target);
+            attemptCollector.applyTo(target);
+            unmatchedVersions.addAll(componentSelectionResult.getUnmatchedVersions());
+            rejectedVersions.addAll(componentSelectionResult.getRejectedVersions());
+        }
+    }
+
+    private static class CandidateResult implements ModuleComponentResolveState {
+        private final ModuleComponentIdentifier identifier;
+        private final ModuleComponentRepository repository;
+        private final AttemptCollector attemptCollector;
+        private final DependencyMetaData dependencyMetaData;
+        private final String version;
+        private boolean searchedLocally;
+        private boolean searchedRemotely;
+        private final DefaultBuildableModuleComponentMetaDataResolveResult result = new DefaultBuildableModuleComponentMetaDataResolveResult();
+
+        public CandidateResult(DependencyMetaData dependencyMetaData, String version, ModuleComponentRepository repository, AttemptCollector attemptCollector) {
+            this.dependencyMetaData = dependencyMetaData;
+            this.version = version;
+            this.repository = repository;
+            this.attemptCollector = attemptCollector;
+            ModuleVersionSelector requested = dependencyMetaData.getRequested();
+            this.identifier = DefaultModuleComponentIdentifier.newId(requested.getGroup(), requested.getName(), version);
+        }
+
+        @Override
+        public ModuleComponentIdentifier getId() {
+            return identifier;
+        }
+
+        @Override
+        public String getVersion() {
+            return version;
+        }
+
+        @Override
+        public BuildableModuleComponentMetaDataResolveResult resolve() {
+            if (!searchedLocally) {
+                searchedLocally = true;
+                process(repository.getLocalAccess());
+                if (result.hasResult() && result.isAuthoritative()) {
+                    // Authoritative result means don't do remote search
+                    searchedRemotely = true;
+                }
+            }
+            if (result.getState() == Resolved || result.getState() == Failed) {
+                return result;
+            }
+            if (!searchedRemotely) {
+                searchedRemotely = true;
+                process(repository.getRemoteAccess());
+            }
+            return result;
+        }
+
+        private void process(ModuleComponentRepositoryAccess access) {
+            access.resolveComponentMetaData(dependencyMetaData.withRequestedVersion(version), identifier, result);
+            attemptCollector.execute(result);
+        }
+
+        public void resolve(DefaultBuildableModuleComponentMetaDataResolveResult target) {
+            resolve();
+            switch (result.getState()) {
+                case Resolved:
+                    target.resolved(result.getMetaData());
+                    break;
+                case Missing:
+                    result.applyTo(target);
+                    target.missing();
+                    break;
+                case Failed:
+                    target.failed(result.getFailure());
+                    break;
+                case Unknown:
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+        }
+    }
+
+    private static class VersionListResult {
+        private final DefaultBuildableModuleVersionListingResolveResult result = new DefaultBuildableModuleVersionListingResolveResult();
+        private final ModuleComponentRepository repository;
+        private final DependencyMetaData dependency;
+
+        private boolean searchedLocally;
+        private boolean searchedRemotely;
+
+        public VersionListResult(DependencyMetaData dependency, ModuleComponentRepository repository) {
+            this.dependency = dependency;
+            this.repository = repository;
+        }
+
+        void resolve() {
+            if (!searchedLocally) {
+                searchedLocally = true;
+                process(dependency, repository.getLocalAccess());
+                if (result.hasResult()) {
+                    if (result.isAuthoritative()) {
+                        // Authoritative result - don't need to try remote
+                        searchedRemotely = true;
+                    }
+                    return;
+                }
+                // Otherwise, try remotely
+            }
+            if (!searchedRemotely) {
+                searchedRemotely = true;
+                process(dependency, repository.getRemoteAccess());
+            }
+
+            // Otherwise, just reuse previous result
+        }
+
+        public boolean canMakeFurtherAttempts() {
+            return !searchedRemotely;
+        }
+
+        public void applyTo(ResourceAwareResolveResult target) {
+            result.applyTo(target);
+        }
+
+        private void process(DependencyMetaData dynamicVersionDependency, ModuleComponentRepositoryAccess moduleAccess) {
+            moduleAccess.listModuleVersions(dynamicVersionDependency, result);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
new file mode 100644
index 0000000..668fd8b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ComponentUsage;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+
+public class ErrorHandlingArtifactResolver implements ArtifactResolver {
+    private final ArtifactResolver resolver;
+
+    public ErrorHandlingArtifactResolver(ArtifactResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        try {
+            resolver.resolveModuleArtifacts(component, artifactType, result);
+        } catch (Throwable t) {
+            result.failed(new ArtifactResolveException(component.getComponentId(), t));
+        }
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage usage, BuildableArtifactSetResolveResult result) {
+        try {
+            resolver.resolveModuleArtifacts(component, usage, result);
+        } catch (Throwable t) {
+            result.failed(new ArtifactResolveException(component.getComponentId(), t));
+        }
+    }
+
+    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        try {
+            resolver.resolveArtifact(artifact, moduleSource, result);
+        } catch (Throwable t) {
+            result.failed(new ArtifactResolveException(artifact.getId(), t));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java
new file mode 100644
index 0000000..e2c2804
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+public class ErrorHandlingModuleComponentRepository implements ModuleComponentRepository {
+    private final ModuleComponentRepository delegate;
+    private final ErrorHandlingModuleComponentRepositoryAccess local;
+    private final ErrorHandlingModuleComponentRepositoryAccess remote;
+
+    public ErrorHandlingModuleComponentRepository(ModuleComponentRepository delegate) {
+        this.delegate = delegate;
+        local = new ErrorHandlingModuleComponentRepositoryAccess(delegate.getLocalAccess());
+        remote = new ErrorHandlingModuleComponentRepositoryAccess(delegate.getRemoteAccess());
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    @Override
+    public String getId() {
+        return delegate.getId();
+    }
+
+    @Override
+    public String getName() {
+        return delegate.getName();
+    }
+
+    @Override
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return local;
+    }
+
+    @Override
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return remote;
+    }
+
+    private static final class ErrorHandlingModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess {
+        private final ModuleComponentRepositoryAccess delegate;
+
+        public ErrorHandlingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public String toString() {
+            return "error handling > " + delegate.toString();
+        }
+
+        @Override
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            try {
+                delegate.listModuleVersions(dependency, result);
+            } catch (Throwable throwable) {
+                result.failed(new ModuleVersionResolveException(dependency.getSelector(), throwable));
+            }
+        }
+
+        @Override
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            try {
+                delegate.resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+            } catch (Throwable throwable) {
+                result.failed(new ModuleVersionResolveException(moduleComponentIdentifier, throwable));
+            }
+        }
+
+        @Override
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            try {
+                delegate.resolveModuleArtifacts(component, artifactType, result);
+            } catch (Throwable throwable) {
+                result.failed(new ArtifactResolveException(component.getComponentId(), throwable));
+            }
+        }
+
+        @Override
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            try {
+                delegate.resolveModuleArtifacts(component, componentUsage, result);
+            } catch (Throwable throwable) {
+                result.failed(new ArtifactResolveException(component.getComponentId(), throwable));
+            }
+        }
+
+        @Override
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            try {
+                delegate.resolveArtifact(artifact, moduleSource, result);
+            } catch (Throwable throwable) {
+                result.failed(new ArtifactResolveException(artifact.getId(), throwable));
+            }
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyContextualiser.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccess.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccess.java
new file mode 100644
index 0000000..71dd980
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccess.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;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class IvyDynamicResolveModuleComponentRepositoryAccess extends BaseModuleComponentRepositoryAccess {
+
+    public static ModuleComponentRepository wrap(ModuleComponentRepository delegate) {
+        final ModuleComponentRepositoryAccess localAccess = new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getLocalAccess());
+        final ModuleComponentRepositoryAccess remoteAccess = new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getRemoteAccess());
+        return new BaseModuleComponentRepository(delegate) {
+            @Override
+            public ModuleComponentRepositoryAccess getLocalAccess() {
+                return localAccess;
+            }
+
+            @Override
+            public ModuleComponentRepositoryAccess getRemoteAccess() {
+                return remoteAccess;
+            }
+        };
+    }
+
+    IvyDynamicResolveModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public String toString() {
+        return "Ivy dynamic resolve > " + getDelegate().toString();
+    }
+
+    public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        super.resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+        if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
+            transformDependencies(result);
+        }
+    }
+
+    private void transformDependencies(BuildableModuleComponentMetaDataResolveResult result) {
+        MutableModuleComponentResolveMetaData metaData = result.getMetaData();
+        List<DependencyMetaData> transformed = new ArrayList<DependencyMetaData>();
+        for (DependencyMetaData dependency : metaData.getDependencies()) {
+            transformed.add(dependency.withRequestedVersion(dependency.getDescriptor().getDynamicConstraintDependencyRevisionId().getRevision()));
+        }
+        metaData.setDependencies(transformed);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java
new file mode 100644
index 0000000..1e5c19d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java
@@ -0,0 +1,114 @@
+/*
+ * 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.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+public class LocalModuleComponentRepository extends BaseModuleComponentRepository {
+    private final ComponentMetadataProcessor metadataProcessor;
+    private final LocalAccess localAccess = new LocalAccess();
+    private final RemoteAccess remoteAccess = new RemoteAccess();
+
+    public LocalModuleComponentRepository(ModuleComponentRepository delegate, ComponentMetadataProcessor metadataProcessor) {
+        super(delegate);
+        this.metadataProcessor = metadataProcessor;
+    }
+
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return localAccess;
+    }
+
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return remoteAccess;
+    }
+
+    private class LocalAccess implements ModuleComponentRepositoryAccess {
+        @Override
+        public String toString() {
+            return "local adapter > " + delegate.toString();
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            delegate.getLocalAccess().listModuleVersions(dependency, result);
+            if (!result.hasResult()) {
+                delegate.getRemoteAccess().listModuleVersions(dependency, result);
+            }
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            delegate.getLocalAccess().resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+            if (!result.hasResult()) {
+                delegate.getRemoteAccess().resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+            }
+
+            if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
+                metadataProcessor.processMetadata(result.getMetaData());
+            }
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            delegate.getLocalAccess().resolveModuleArtifacts(component, artifactType, result);
+            if(!result.hasResult()) {
+                delegate.getRemoteAccess().resolveModuleArtifacts(component, artifactType, result);
+            }
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            delegate.getLocalAccess().resolveModuleArtifacts(component, componentUsage, result);
+            if(!result.hasResult()) {
+                delegate.getRemoteAccess().resolveModuleArtifacts(component, componentUsage, result);
+            }
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            delegate.getLocalAccess().resolveArtifact(artifact, moduleSource, result);
+            if(!result.hasResult()) {
+                delegate.getRemoteAccess().resolveArtifact(artifact, moduleSource, result);
+            }
+        }
+    }
+
+    private static class RemoteAccess implements ModuleComponentRepositoryAccess {
+        @Override
+        public String toString() {
+            return "empty";
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProvider.java
new file mode 100644
index 0000000..52e10c6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.Nullable;
+import org.gradle.api.artifacts.ComponentMetadata;
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataDetailsAdapter;
+import org.gradle.internal.component.external.model.IvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+
+public class MetadataProvider {
+    private final ModuleComponentResolveState resolveState;
+    private BuildableModuleComponentMetaDataResolveResult cachedResult;
+
+    public MetadataProvider(ModuleComponentResolveState resolveState) {
+        this.resolveState = resolveState;
+    }
+
+    public MetadataProvider(BuildableModuleComponentMetaDataResolveResult result) {
+        this.resolveState = null;
+        cachedResult = result;
+    }
+
+    public ComponentMetadata getComponentMetadata() {
+        return new ComponentMetadataDetailsAdapter(getMetaData());
+    }
+
+    @Nullable
+    public IvyModuleDescriptor getIvyModuleDescriptor() {
+        ModuleComponentResolveMetaData metaData = getMetaData();
+        if (metaData instanceof IvyModuleResolveMetaData) {
+            IvyModuleResolveMetaData ivyMetadata = (IvyModuleResolveMetaData) metaData;
+            return new DefaultIvyModuleDescriptor(ivyMetadata.getExtraInfo(), ivyMetadata.getBranch(), ivyMetadata.getStatus());
+        }
+        return null;
+    }
+
+    public boolean resolve() {
+        if (cachedResult == null) {
+            cachedResult = resolveState.resolve();
+        }
+        return cachedResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved;
+    }
+
+    public MutableModuleComponentResolveMetaData getMetaData() {
+        resolve();
+        return cachedResult.getMetaData();
+    }
+
+    public boolean isUsable() {
+        return cachedResult == null || cachedResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved;
+    }
+
+    @Nullable
+    public BuildableModuleComponentMetaDataResolveResult getResult() {
+        return cachedResult;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java
new file mode 100644
index 0000000..35a4197
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.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.artifacts.ivyservice.ivyresolve;
+
+/**
+ * A repository of module components.
+ *
+ * The plan is to sync this with {@link org.gradle.internal.resolve.resolver.DependencyToComponentResolver} and rename it
+ * to have 'resolver' instead of 'repository' in its name.
+ */
+public interface ModuleComponentRepository {
+    String getId();
+
+    String getName();
+
+    ModuleComponentRepositoryAccess getLocalAccess();
+
+    ModuleComponentRepositoryAccess getRemoteAccess();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java
new file mode 100644
index 0000000..073b99e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.*;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+/**
+ * Provides access to a repository of components that are identified by a ModuleComponentIdentifier.
+ */
+public interface ModuleComponentRepositoryAccess {
+    /**
+     * Resolves the given dependency to a list of module versions.
+     */
+    void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result);
+
+    /**
+     * Resolves the metadata for a module component.
+     */
+    void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result);
+
+    /**
+     * Resolves a set of artifacts belonging to the given component, based on the supplied usage. Any failures are packaged up in the result.
+     */
+    void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result);
+
+    /**
+     * Resolves a set of artifacts belonging to the given component, with the type specified. Any failures are packaged up in the result.
+     */
+    void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result);
+
+    /**
+     * Resolves the given artifact. Any failures are packaged up in the result.
+     */
+    void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java
new file mode 100644
index 0000000..23126f6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+
+public interface ModuleComponentResolveState extends Versioned {
+    ModuleComponentIdentifier getId();
+
+    BuildableModuleComponentMetaDataResolveResult resolve();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java
new file mode 100644
index 0000000..ec2f55a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.ModuleVersionNotFoundException;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+/**
+ * Used as a fallback when no repositories are defined for a given resolution.
+ */
+public class NoRepositoriesResolver implements RepositoryChain, DependencyToComponentIdResolver, ComponentMetaDataResolver, ArtifactResolver {
+    public DependencyToComponentIdResolver getComponentIdResolver() {
+        return this;
+    }
+
+    public ComponentMetaDataResolver getComponentMetaDataResolver() {
+        return this;
+    }
+
+    public DependencyToComponentResolver getDependencyResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ArtifactResolver getArtifactResolver() {
+        return this;
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result) {
+        result.failed(new ModuleVersionNotFoundException(dependency.getRequested(), String.format("Cannot resolve external dependency %s because no repositories are defined.", dependency.getRequested())));
+    }
+
+    public void resolve(DependencyMetaData dependency, ComponentIdentifier identifier, BuildableComponentResolveResult result) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage usage, BuildableArtifactSetResolveResult result) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java
new file mode 100644
index 0000000..f71916e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChain.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+
+public interface RepositoryChain {
+    public DependencyToComponentIdResolver getComponentIdResolver();
+
+    public ComponentMetaDataResolver getComponentMetaDataResolver();
+
+    public DependencyToComponentResolver getDependencyResolver();
+
+    public ArtifactResolver getArtifactResolver();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapter.java
new file mode 100644
index 0000000..ec4d5d3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+/**
+ * Takes a dependency->meta-data resolver and presents it as separate dependency->id and id->meta-data resolvers.
+ *
+ * Short-circuits the dependency->id resolution for static versions.
+ */
+public class RepositoryChainAdapter implements DependencyToComponentIdResolver, ComponentMetaDataResolver {
+    private final DependencyToComponentIdResolver dynamicRevisionResolver;
+    private final DependencyToComponentResolver metaDataResolver;
+    private final VersionSelectorScheme versionSelectorScheme;
+
+    public RepositoryChainAdapter(DependencyToComponentIdResolver dynamicRevisionResolver, DependencyToComponentResolver metaDataResolver, VersionSelectorScheme versionSelectorScheme) {
+        this.dynamicRevisionResolver = dynamicRevisionResolver;
+        this.metaDataResolver = metaDataResolver;
+        this.versionSelectorScheme = versionSelectorScheme;
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result) {
+        ModuleVersionSelector requested = dependency.getRequested();
+        if (versionSelectorScheme.parseSelector(requested.getVersion()).isDynamic()) {
+            dynamicRevisionResolver.resolve(dependency, result);
+        } else {
+            DefaultModuleComponentIdentifier id = new DefaultModuleComponentIdentifier(requested.getGroup(), requested.getName(), requested.getVersion());
+            DefaultModuleVersionIdentifier mvId = new DefaultModuleVersionIdentifier(requested.getGroup(), requested.getName(), requested.getVersion());
+            result.resolved(id, mvId);
+        }
+    }
+
+    public void resolve(DependencyMetaData dependency, ComponentIdentifier identifier, BuildableComponentResolveResult result) {
+        if (!(identifier instanceof ModuleComponentIdentifier)) {
+            throw new UnsupportedOperationException("Can resolve meta-data for module components only.");
+        }
+
+        // Force the requested version
+        ModuleComponentIdentifier moduleId = (ModuleComponentIdentifier) identifier;
+        dependency = dependency.withTarget(new DefaultModuleComponentSelector(moduleId.getGroup(), moduleId.getModule(), moduleId.getVersion()));
+
+        metaDataResolver.resolve(dependency, result);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
new file mode 100644
index 0000000..6fc1b05
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ComponentUsage;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.Transformers;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class RepositoryChainArtifactResolver implements ArtifactResolver {
+    private final Map<String, ModuleComponentRepository> repositories = new LinkedHashMap<String, ModuleComponentRepository>();
+
+    void add(ModuleComponentRepository repository) {
+        repositories.put(repository.getId(), repository);
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        ModuleComponentRepository sourceRepository = findSourceRepository(component.getSource());
+        ComponentResolveMetaData unpackedComponent = unpackSource(component);
+        // First try to determine the artifacts locally before going remote
+        sourceRepository.getLocalAccess().resolveModuleArtifacts(unpackedComponent, artifactType, result);
+        if (!result.hasResult()) {
+            sourceRepository.getRemoteAccess().resolveModuleArtifacts(unpackedComponent, artifactType, result);
+        }
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage usage, BuildableArtifactSetResolveResult result) {
+        ModuleComponentRepository sourceRepository = findSourceRepository(component.getSource());
+        ComponentResolveMetaData unpackedComponent = unpackSource(component);
+        // First try to determine the artifacts locally before going remote
+        sourceRepository.getLocalAccess().resolveModuleArtifacts(unpackedComponent, usage, result);
+        if (!result.hasResult()) {
+            sourceRepository.getRemoteAccess().resolveModuleArtifacts(unpackedComponent, usage, result);
+        }
+    }
+
+    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource source, BuildableArtifactResolveResult result) {
+        ModuleComponentRepository sourceRepository = findSourceRepository(source);
+        ModuleSource unpackedSource = unpackSource(source);
+
+        // First try to resolve the artifacts locally before going remote
+        sourceRepository.getLocalAccess().resolveArtifact(artifact, unpackedSource, result);
+        if (!result.hasResult()) {
+            sourceRepository.getRemoteAccess().resolveArtifact(artifact, unpackedSource, result);
+        }
+    }
+
+    private ModuleComponentRepository findSourceRepository(ModuleSource originalSource) {
+        ModuleComponentRepository moduleVersionRepository = repositories.get(repositorySource(originalSource).getRepositoryId());
+        if (moduleVersionRepository == null) {
+            throw new IllegalStateException("Attempting to resolve artifacts from invalid repository");
+        }
+        return moduleVersionRepository;
+    }
+
+    private RepositoryChainModuleSource repositorySource(ModuleSource original) {
+        return Transformers.cast(RepositoryChainModuleSource.class).transform(original);
+    }
+
+    private ModuleSource unpackSource(ModuleSource original) {
+        return repositorySource(original).getDelegate();
+    }
+
+    private ComponentResolveMetaData unpackSource(ComponentResolveMetaData component) {
+        return component.withSource(repositorySource(component.getSource()).getDelegate());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java
new file mode 100644
index 0000000..80c1c7a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolver.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.Transformer;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class RepositoryChainDependencyResolver implements DependencyToComponentResolver {
+    private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryChainDependencyResolver.class);
+
+    private final List<ModuleComponentRepository> repositories = new ArrayList<ModuleComponentRepository>();
+    private final List<String> repositoryNames = new ArrayList<String>();
+    private final VersionedComponentChooser versionedComponentChooser;
+    private final Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> metaDataFactory;
+
+    public RepositoryChainDependencyResolver(VersionedComponentChooser versionedComponentChooser, Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> metaDataFactory) {
+        this.versionedComponentChooser = versionedComponentChooser;
+        this.metaDataFactory = metaDataFactory;
+    }
+
+    public void add(ModuleComponentRepository repository) {
+        repositories.add(repository);
+        repositoryNames.add(repository.getName());
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentResolveResult result) {
+        ModuleVersionSelector requested = dependency.getRequested();
+        LOGGER.debug("Attempting to resolve {} using repositories {}", requested, repositoryNames);
+        ModuleComponentIdentifier moduleComponentIdentifier = new DefaultModuleComponentIdentifier(requested.getGroup(), requested.getName(), requested.getVersion());
+        ModuleVersionIdentifier moduleVersionIdentifier = new DefaultModuleVersionIdentifier(requested.getGroup(), requested.getName(), requested.getVersion());
+
+        List<Throwable> errors = new ArrayList<Throwable>();
+
+        List<ComponentMetaDataResolveState> resolveStates = new ArrayList<ComponentMetaDataResolveState>();
+        for (ModuleComponentRepository repository : repositories) {
+            resolveStates.add(new ComponentMetaDataResolveState(dependency, moduleComponentIdentifier, repository, versionedComponentChooser));
+        }
+
+        final RepositoryChainModuleResolution latestResolved = findBestMatch(resolveStates, errors);
+        if (latestResolved != null) {
+            LOGGER.debug("Using {} from {}", latestResolved.module.getId(), latestResolved.repository);
+            for (Throwable error : errors) {
+                LOGGER.debug("Discarding resolve failure.", error);
+            }
+
+            result.resolved(metaDataFactory.transform(latestResolved));
+            return;
+        }
+        if (!errors.isEmpty()) {
+            result.failed(new ModuleVersionResolveException(moduleComponentIdentifier, errors));
+        } else {
+            for (ComponentMetaDataResolveState resolveState : resolveStates) {
+                resolveState.applyTo(result);
+            }
+            result.notFound(moduleVersionIdentifier);
+        }
+    }
+
+    private RepositoryChainModuleResolution findBestMatch(List<ComponentMetaDataResolveState> resolveStates, Collection<Throwable> failures) {
+        LinkedList<ComponentMetaDataResolveState> queue = new LinkedList<ComponentMetaDataResolveState>();
+        queue.addAll(resolveStates);
+
+        LinkedList<ComponentMetaDataResolveState> missing = new LinkedList<ComponentMetaDataResolveState>();
+
+        // A first pass to do local resolves only
+        RepositoryChainModuleResolution best = findBestMatch(queue, failures, missing);
+        if (best != null) {
+            return best;
+        }
+
+        // Nothing found - do a second pass
+        queue.addAll(missing);
+        missing.clear();
+        return findBestMatch(queue, failures, missing);
+    }
+
+    private RepositoryChainModuleResolution findBestMatch(LinkedList<ComponentMetaDataResolveState> queue, Collection<Throwable> failures, Collection<ComponentMetaDataResolveState> missing) {
+        RepositoryChainModuleResolution best = null;
+        while (!queue.isEmpty()) {
+            ComponentMetaDataResolveState request = queue.removeFirst();
+            BuildableModuleComponentMetaDataResolveResult metaDataResolveResult;
+            try {
+                metaDataResolveResult = request.resolve();
+            } catch (Throwable t) {
+                failures.add(t);
+                continue;
+            }
+            switch (metaDataResolveResult.getState()) {
+                case Failed:
+                    failures.add(metaDataResolveResult.getFailure());
+                    break;
+                case Missing:
+                    // Queue this up for checking again later
+                    if (request.canMakeFurtherAttempts()) {
+                        missing.add(request);
+                    }
+                    break;
+                case Resolved:
+                    RepositoryChainModuleResolution moduleResolution = new RepositoryChainModuleResolution(request.repository, metaDataResolveResult.getMetaData());
+                    if (!metaDataResolveResult.getMetaData().isGenerated()) {
+                        return moduleResolution;
+                    }
+                    best = best != null ? best : moduleResolution;
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected state for resolution: " + metaDataResolveResult.getState());
+            }
+        }
+
+        return best;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
new file mode 100644
index 0000000..e446902
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+
+class RepositoryChainModuleResolution implements Versioned {
+    public final ModuleComponentRepository repository;
+    public final MutableModuleComponentResolveMetaData module;
+
+    public RepositoryChainModuleResolution(ModuleComponentRepository repository, MutableModuleComponentResolveMetaData module) {
+        this.repository = repository;
+        this.module = module;
+    }
+
+    public String getVersion() {
+        return module.getId().getVersion();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
new file mode 100644
index 0000000..3d51e88
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.component.model.ModuleSource;
+
+class RepositoryChainModuleSource implements ModuleSource {
+    private final String repositoryId;
+    private final ModuleSource delegate;
+
+    public RepositoryChainModuleSource(String repositoryId, ModuleSource delegate) {
+        this.repositoryId = repositoryId;
+        this.delegate = delegate;
+    }
+
+    public String getRepositoryId() {
+        return repositoryId;
+    }
+
+    public ModuleSource getDelegate() {
+        return delegate;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
new file mode 100644
index 0000000..023ac47
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.cache.ResolutionRules;
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
+import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.memcache.InMemoryCachedRepositoryFactory;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache;
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultComponentSelectionRules;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+import org.gradle.internal.resource.cached.CachedArtifactIndex;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.util.Collection;
+
+public class ResolveIvyFactory {
+    private final ModuleVersionsCache moduleVersionsCache;
+    private final ModuleMetaDataCache moduleMetaDataCache;
+    private final ModuleArtifactsCache moduleArtifactsCache;
+    private final CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex;
+    private final CacheLockingManager cacheLockingManager;
+    private final StartParameterResolutionOverride startParameterResolutionOverride;
+    private final BuildCommencedTimeProvider timeProvider;
+    private final InMemoryCachedRepositoryFactory inMemoryCache;
+    private final VersionSelectorScheme versionSelectorScheme;
+    private final VersionComparator versionComparator;
+
+    public ResolveIvyFactory(ModuleVersionsCache moduleVersionsCache, ModuleMetaDataCache moduleMetaDataCache, ModuleArtifactsCache moduleArtifactsCache,
+                             CachedArtifactIndex artifactAtRepositoryCachedResolutionIndex,
+                             CacheLockingManager cacheLockingManager, StartParameterResolutionOverride startParameterResolutionOverride,
+                             BuildCommencedTimeProvider timeProvider, InMemoryCachedRepositoryFactory inMemoryCache, VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+        this.moduleVersionsCache = moduleVersionsCache;
+        this.moduleMetaDataCache = moduleMetaDataCache;
+        this.moduleArtifactsCache = moduleArtifactsCache;
+        this.artifactAtRepositoryCachedResolutionIndex = artifactAtRepositoryCachedResolutionIndex;
+        this.cacheLockingManager = cacheLockingManager;
+        this.startParameterResolutionOverride = startParameterResolutionOverride;
+        this.timeProvider = timeProvider;
+        this.inMemoryCache = inMemoryCache;
+        this.versionSelectorScheme = versionSelectorScheme;
+        this.versionComparator = versionComparator;
+    }
+
+    public RepositoryChain create(ConfigurationInternal configuration,
+                                  Collection<? extends ResolutionAwareRepository> repositories,
+                                  ComponentMetadataProcessor metadataProcessor) {
+        if (repositories.isEmpty()) {
+            return new NoRepositoriesResolver();
+        }
+
+        ResolutionStrategyInternal resolutionStrategy = configuration.getResolutionStrategy();
+        ResolutionRules resolutionRules = resolutionStrategy.getResolutionRules();
+        CachePolicy cachePolicy = resolutionStrategy.getCachePolicy();
+
+        startParameterResolutionOverride.addResolutionRules(resolutionRules);
+
+        UserResolverChain moduleResolver = new UserResolverChain(versionSelectorScheme, versionComparator, resolutionStrategy.getComponentSelection());
+        ParentModuleLookupResolver parentModuleResolver = new ParentModuleLookupResolver(versionSelectorScheme, versionComparator, cacheLockingManager);
+
+        for (ResolutionAwareRepository repository : repositories) {
+            ConfiguredModuleComponentRepository baseRepository = repository.createResolver();
+
+            if (baseRepository instanceof ExternalResourceResolver) {
+                ((ExternalResourceResolver) baseRepository).setRepositoryChain(parentModuleResolver);
+            }
+
+            // TODO:DAZ In theory we could update this so that _all_ repositories are wrapped in a cache:
+            //     - would need to add local/remote pattern to artifact download
+            //     - This might help later when we integrate in-memory caching with file-backed caching.
+            ModuleComponentRepository moduleComponentRepository = baseRepository;
+            if (baseRepository.isLocal()) {
+                moduleComponentRepository = new LocalModuleComponentRepository(baseRepository, metadataProcessor);
+            } else {
+                moduleComponentRepository = new CacheLockReleasingModuleComponentsRepository(moduleComponentRepository, cacheLockingManager);
+                moduleComponentRepository = startParameterResolutionOverride.overrideModuleVersionRepository(moduleComponentRepository);
+                moduleComponentRepository = new CachingModuleComponentRepository(moduleComponentRepository, moduleVersionsCache, moduleMetaDataCache, moduleArtifactsCache, artifactAtRepositoryCachedResolutionIndex,
+                        cachePolicy, timeProvider, metadataProcessor);
+            }
+
+            if (baseRepository.isDynamicResolveMode()) {
+                moduleComponentRepository = IvyDynamicResolveModuleComponentRepositoryAccess.wrap(moduleComponentRepository);
+            }
+            moduleComponentRepository = inMemoryCache.cached(moduleComponentRepository);
+            moduleComponentRepository = new ErrorHandlingModuleComponentRepository(moduleComponentRepository);
+
+            moduleResolver.add(moduleComponentRepository);
+            parentModuleResolver.add(moduleComponentRepository);
+        }
+
+        return moduleResolver;
+    }
+
+    /**
+     * Provides access to the top-level resolver chain for looking up parent modules when parsing module descriptor files.
+     */
+    private static class ParentModuleLookupResolver implements RepositoryChain, DependencyToComponentResolver, ArtifactResolver {
+        private final CacheLockingManager cacheLockingManager;
+        private final UserResolverChain delegate;
+
+        public ParentModuleLookupResolver(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator, CacheLockingManager cacheLockingManager) {
+            this.delegate = new UserResolverChain(versionSelectorScheme, versionComparator, new DefaultComponentSelectionRules());
+            this.cacheLockingManager = cacheLockingManager;
+        }
+
+        public void add(ModuleComponentRepository moduleComponentRepository) {
+            delegate.add(moduleComponentRepository);
+        }
+
+        public ComponentMetaDataResolver getComponentMetaDataResolver() {
+            throw new UnsupportedOperationException();
+        }
+
+        public DependencyToComponentIdResolver getComponentIdResolver() {
+            throw new UnsupportedOperationException();
+        }
+
+        public ArtifactResolver getArtifactResolver() {
+            return this;
+        }
+
+        public DependencyToComponentResolver getDependencyResolver() {
+            return this;
+        }
+
+        public void resolve(final DependencyMetaData dependency, final BuildableComponentResolveResult result) {
+            cacheLockingManager.useCache(String.format("Resolve %s", dependency), new Runnable() {
+                public void run() {
+                    delegate.getDependencyResolver().resolve(dependency, result);
+                }
+            });
+        }
+
+        public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ArtifactType artifactType, final BuildableArtifactSetResolveResult result) {
+            cacheLockingManager.useCache(String.format("Resolve %s for %s", artifactType, component), new Runnable() {
+                public void run() {
+                    delegate.getArtifactResolver().resolveModuleArtifacts(component, artifactType, result);
+                }
+            });
+        }
+
+        public void resolveModuleArtifacts(final ComponentResolveMetaData component, final ComponentUsage usage, final BuildableArtifactSetResolveResult result) {
+            cacheLockingManager.useCache(String.format("Resolve %s for %s", usage, component), new Runnable() {
+                public void run() {
+                    delegate.getArtifactResolver().resolveModuleArtifacts(component, usage, result);
+                }
+            });
+        }
+
+        public void resolveArtifact(final ComponentArtifactMetaData artifact, final ModuleSource moduleSource, final BuildableArtifactResolveResult result) {
+            cacheLockingManager.useCache(String.format("Resolve %s", artifact), new Runnable() {
+                public void run() {
+                    delegate.getArtifactResolver().resolveArtifact(artifact, moduleSource, result);
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
new file mode 100644
index 0000000..d74e251
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
+
+import org.gradle.StartParameter;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.cache.ArtifactResolutionControl;
+import org.gradle.api.artifacts.cache.DependencyResolutionControl;
+import org.gradle.api.artifacts.cache.ModuleResolutionControl;
+import org.gradle.api.artifacts.cache.ResolutionRules;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.*;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+import java.util.concurrent.TimeUnit;
+
+public class StartParameterResolutionOverride {
+    private final StartParameter startParameter;
+
+    public StartParameterResolutionOverride(StartParameter startParameter) {
+        this.startParameter = startParameter;
+    }
+
+    public void addResolutionRules(ResolutionRules resolutionRules) {
+        if (startParameter.isOffline()) {
+            resolutionRules.eachDependency(new Action<DependencyResolutionControl>() {
+                public void execute(DependencyResolutionControl dependencyResolutionControl) {
+                    dependencyResolutionControl.useCachedResult();
+                }
+            });
+            resolutionRules.eachModule(new Action<ModuleResolutionControl>() {
+                public void execute(ModuleResolutionControl moduleResolutionControl) {
+                    moduleResolutionControl.useCachedResult();
+                }
+            });
+            resolutionRules.eachArtifact(new Action<ArtifactResolutionControl>() {
+                public void execute(ArtifactResolutionControl artifactResolutionControl) {
+                    artifactResolutionControl.useCachedResult();
+                }
+            });
+        } else if (startParameter.isRefreshDependencies()) {
+            resolutionRules.eachDependency(new Action<DependencyResolutionControl>() {
+                public void execute(DependencyResolutionControl dependencyResolutionControl) {
+                    dependencyResolutionControl.cacheFor(0, TimeUnit.SECONDS);
+                }
+            });
+            resolutionRules.eachModule(new Action<ModuleResolutionControl>() {
+                public void execute(ModuleResolutionControl moduleResolutionControl) {
+                    moduleResolutionControl.cacheFor(0, TimeUnit.SECONDS);
+                }
+            });
+            resolutionRules.eachArtifact(new Action<ArtifactResolutionControl>() {
+                public void execute(ArtifactResolutionControl artifactResolutionControl) {
+                    artifactResolutionControl.cacheFor(0, TimeUnit.SECONDS);
+                }
+            });
+        }
+    }
+
+    public ModuleComponentRepository overrideModuleVersionRepository(ModuleComponentRepository original) {
+        if (startParameter.isOffline()) {
+            return new OfflineModuleComponentRepository(original);
+        }
+        return original;
+    }
+
+    private static class OfflineModuleComponentRepository extends BaseModuleComponentRepository {
+
+        private final FailedRemoteAccess failedRemoteAccess = new FailedRemoteAccess();
+
+        public OfflineModuleComponentRepository(ModuleComponentRepository original) {
+            super(original);
+        }
+
+        @Override
+        public ModuleComponentRepositoryAccess getRemoteAccess() {
+            return failedRemoteAccess;
+        }
+    }
+
+    private static class FailedRemoteAccess implements ModuleComponentRepositoryAccess {
+        @Override
+        public String toString() {
+            return "offline remote";
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            result.failed(new ModuleVersionResolveException(dependency.getRequested(), String.format("No cached version listing for %s available for offline mode.", dependency.getRequested())));
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            result.failed(new ModuleVersionResolveException(moduleComponentIdentifier, String.format("No cached version of %s available for offline mode.", moduleComponentIdentifier.getDisplayName())));
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            result.failed(new ArtifactResolveException(component.getComponentId(), "No cached version available for offline mode"));
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            result.failed(new ArtifactResolveException(component.getComponentId(), "No cached version available for offline mode"));
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            result.failed(new ArtifactResolveException(artifact.getId(), "No cached version available for offline mode"));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
new file mode 100644
index 0000000..f912983
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+
+public class UserResolverChain implements RepositoryChain {
+    private final RepositoryChainDependencyResolver dependencyResolver;
+    private final RepositoryChainArtifactResolver artifactResolver = new RepositoryChainArtifactResolver();
+    private final RepositoryChainAdapter adapter;
+    private final DynamicVersionResolver dynamicVersionResolver;
+    private final ComponentSelectionRulesInternal componentSelectionRules;
+
+    public UserResolverChain(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator, ComponentSelectionRulesInternal componentSelectionRules) {
+        this.componentSelectionRules = componentSelectionRules;
+        DefaultVersionedComponentChooser componentChooser = new DefaultVersionedComponentChooser(versionComparator, versionSelectorScheme, componentSelectionRules);
+        ModuleTransformer metaDataFactory = new ModuleTransformer();
+        dependencyResolver = new RepositoryChainDependencyResolver(componentChooser, metaDataFactory);
+        dynamicVersionResolver = new DynamicVersionResolver(componentChooser, metaDataFactory);
+        adapter = new RepositoryChainAdapter(dynamicVersionResolver, dependencyResolver, versionSelectorScheme);
+    }
+
+    public DependencyToComponentIdResolver getComponentIdResolver() {
+        return adapter;
+    }
+
+    public ComponentMetaDataResolver getComponentMetaDataResolver() {
+        return adapter;
+    }
+
+    public DependencyToComponentResolver getDependencyResolver() {
+        return dependencyResolver;
+    }
+
+    public ArtifactResolver getArtifactResolver() {
+        return artifactResolver;
+    }
+
+    public ComponentSelectionRulesInternal getComponentSelectionRules() {
+        return componentSelectionRules;
+    }
+
+    public void add(ModuleComponentRepository repository) {
+        dependencyResolver.add(repository);
+        dynamicVersionResolver.add(repository);
+        artifactResolver.add(repository);
+    }
+
+    private static class ModuleTransformer implements Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> {
+        public ModuleComponentResolveMetaData transform(RepositoryChainModuleResolution original) {
+            RepositoryChainModuleSource moduleSource = new RepositoryChainModuleSource(original.repository.getId(), original.module.getSource());
+            original.module.setSource(moduleSource);
+            return original.module;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionInfo.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/Versioned.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java
new file mode 100644
index 0000000..a607dca
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.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;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableComponentSelectionResult;
+
+import java.util.Collection;
+
+public interface VersionedComponentChooser {
+    ComponentResolveMetaData selectNewestComponent(ComponentResolveMetaData one, ComponentResolveMetaData two);
+
+    void selectNewestMatchingComponent(Collection<? extends ModuleComponentResolveState> versions, DependencyMetaData dependency, BuildableComponentSelectionResult result);
+
+    boolean isRejectedComponent(ModuleComponentIdentifier candidateIdentifier, MetadataProvider metadataProvider);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.java
new file mode 100644
index 0000000..38710db
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResult.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.memcache;
+
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+
+import static org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult.State.Missing;
+import static org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult.State.Resolved;
+
+class CachedModuleVersionResult {
+    private final BuildableModuleComponentMetaDataResolveResult.State state;
+    private final boolean authoritative;
+    private final MutableModuleComponentResolveMetaData metaData;
+
+    public CachedModuleVersionResult(BuildableModuleComponentMetaDataResolveResult result) {
+        this.state = result.getState();
+        if (state == Resolved) {
+            this.metaData = result.getMetaData().copy();
+            this.authoritative = result.isAuthoritative();
+        } else if (state == Missing) {
+            this.metaData = null;
+            this.authoritative = result.isAuthoritative();
+        } else {
+            this.metaData = null;
+            this.authoritative = false;
+        }
+    }
+
+    public boolean isCacheable() {
+        return state == Missing || state == Resolved;
+    }
+
+    public void supply(BuildableModuleComponentMetaDataResolveResult result) {
+        assert isCacheable() : "Results are not cacheable, cannot supply the results.";
+        if (state == Resolved) {
+            MutableModuleComponentResolveMetaData metaData = this.metaData.copy();
+            result.resolved(metaData);
+            result.setAuthoritative(authoritative);
+        } else if (state == Missing) {
+            result.missing();
+            result.setAuthoritative(authoritative);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCache.java
new file mode 100644
index 0000000..cba4f26
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCache.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.artifacts.ivyservice.ivyresolve.memcache;
+
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+class InMemoryArtifactsCache {
+    private final Map<ComponentArtifactIdentifier, File> artifacts = new HashMap<ComponentArtifactIdentifier, File>();
+    private InMemoryCacheStats stats;
+
+    InMemoryArtifactsCache(InMemoryCacheStats stats) {
+        this.stats = stats;
+    }
+
+    public boolean supplyArtifact(ComponentArtifactIdentifier id, BuildableArtifactResolveResult result) {
+        File fromCache = artifacts.get(id);
+        if (fromCache != null) {
+            result.resolved(fromCache);
+            stats.artifactsServed++;
+            return true;
+        }
+        return false;
+    }
+
+    public void newArtifact(ComponentArtifactIdentifier id, BuildableArtifactResolveResult result) {
+        if (result.hasResult() && result.getFailure() == null) {
+            artifacts.put(id, result.getFile());
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCacheStats.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCacheStats.java
new file mode 100644
index 0000000..9521be4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCacheStats.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 InMemoryCacheStats {
+    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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepository.java
new file mode 100644
index 0000000..13c8460
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepository.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.memcache;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.*;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+// TODO:DAZ Should share local and remote caches, and use make in-memory caching a decorator over filesystem caching
+class InMemoryCachedModuleComponentRepository extends BaseModuleComponentRepository {
+    final InMemoryCacheStats stats;
+    private final ModuleComponentRepositoryAccess localAccess;
+    private final ModuleComponentRepositoryAccess remoteAccess;
+
+    public InMemoryCachedModuleComponentRepository(InMemoryModuleComponentRepositoryCaches cache, ModuleComponentRepository delegate) {
+        super(delegate);
+        this.stats = cache.stats;
+        this.localAccess = new CachedAccess(delegate.getLocalAccess(), cache.localArtifactsCache, cache.localMetaDataCache);
+        this.remoteAccess = new CachedAccess(delegate.getRemoteAccess(), cache.remoteArtifactsCache, cache.remoteMetaDataCache);
+    }
+
+    @Override
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return localAccess;
+    }
+
+    @Override
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return remoteAccess;
+    }
+
+    private class CachedAccess extends BaseModuleComponentRepositoryAccess {
+        private final InMemoryMetaDataCache metaDataCache;
+        private final InMemoryArtifactsCache artifactsCache;
+
+        public CachedAccess(ModuleComponentRepositoryAccess access, InMemoryArtifactsCache artifactsCache, InMemoryMetaDataCache metaDataCache) {
+            super(access);
+            this.artifactsCache = artifactsCache;
+            this.metaDataCache = metaDataCache;
+        }
+
+        @Override
+        public String toString() {
+            return "in-memory cache > " + getDelegate().toString();
+        }
+
+        public void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            if(!metaDataCache.supplyModuleVersions(dependency.getRequested(), result)) {
+                super.listModuleVersions(dependency, result);
+                metaDataCache.newModuleVersions(dependency.getRequested(), result);
+            }
+        }
+
+        public void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            if(!metaDataCache.supplyMetaData(moduleComponentIdentifier, result)) {
+                super.resolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+                metaDataCache.newDependencyResult(moduleComponentIdentifier, result);
+            }
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            if (!artifactsCache.supplyArtifact(artifact.getId(), result)) {
+                super.resolveArtifact(artifact, moduleSource, result);
+                artifactsCache.newArtifact(artifact.getId(), result);
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactory.java
new file mode 100644
index 0000000..5bad5e5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactory.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.ModuleComponentRepository;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.concurrent.Stoppable;
+
+import java.util.Map;
+
+/**
+ * Caches the dependency metadata (descriptors, artifact files) in memory.
+ */
+public class InMemoryCachedRepositoryFactory implements Stoppable {
+
+    public final static String TOGGLE_PROPERTY = "org.gradle.resolution.memorycache";
+
+    private final static Logger LOG = Logging.getLogger(InMemoryCachedRepositoryFactory.class);
+
+    Map<String, InMemoryModuleComponentRepositoryCaches> cachePerRepo = new MapMaker().makeMap();
+
+    final InMemoryCacheStats stats = new InMemoryCacheStats();
+
+    public ModuleComponentRepository cached(ModuleComponentRepository input) {
+        if ("false".equalsIgnoreCase(System.getProperty(TOGGLE_PROPERTY))) {
+            return input;
+        }
+
+        InMemoryModuleComponentRepositoryCaches caches = cachePerRepo.get(input.getId());
+        stats.reposWrapped++;
+        if (caches == null) {
+            LOG.debug("Creating new in-memory cache for repo '{}' [{}].", input.getName(), input.getId());
+            caches = new InMemoryModuleComponentRepositoryCaches(stats);
+            stats.cacheInstances++;
+            cachePerRepo.put(input.getId(), caches);
+        } else {
+            LOG.debug("Reusing in-memory cache for repo '{}' [{}].", input.getName(), input.getId());
+        }
+        return new InMemoryCachedModuleComponentRepository(caches, input);
+    }
+
+    public void stop() {
+        cachePerRepo.clear();
+        LOG.debug("In-memory dependency metadata cache closed. {}", stats);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCache.java
new file mode 100644
index 0000000..ebddf63
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCache.java
@@ -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.api.internal.artifacts.ivyservice.ivyresolve.memcache;
+
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult.State.Listed;
+
+class InMemoryMetaDataCache {
+    private final Map<ModuleVersionSelector, Set<String>> moduleVersionListing = new HashMap<ModuleVersionSelector, Set<String>>();
+    private final Map<ModuleComponentIdentifier, CachedModuleVersionResult> metaData = new HashMap<ModuleComponentIdentifier, CachedModuleVersionResult>();
+    private InMemoryCacheStats stats;
+
+    InMemoryMetaDataCache(InMemoryCacheStats stats) {
+        this.stats = stats;
+    }
+
+    public boolean supplyModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionListingResolveResult result) {
+        Set<String> versions = moduleVersionListing.get(requested);
+        if (versions == null) {
+            return false;
+        }
+        result.listed(versions);
+        return true;
+    }
+
+    public void newModuleVersions(ModuleVersionSelector requested, BuildableModuleVersionListingResolveResult result) {
+        if (result.getState() == Listed) {
+            moduleVersionListing.put(requested, result.getVersions());
+        }
+    }
+
+    boolean supplyMetaData(ModuleComponentIdentifier requested, BuildableModuleComponentMetaDataResolveResult result) {
+        CachedModuleVersionResult fromCache = metaData.get(requested);
+        if (fromCache == null) {
+            return false;
+        }
+        fromCache.supply(result);
+        stats.metadataServed++;
+        return true;
+    }
+
+    void newDependencyResult(ModuleComponentIdentifier requested, BuildableModuleComponentMetaDataResolveResult result) {
+        CachedModuleVersionResult cachedResult = new CachedModuleVersionResult(result);
+        if (cachedResult.isCacheable()) {
+            metaData.put(requested, cachedResult);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryModuleComponentRepositoryCaches.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryModuleComponentRepositoryCaches.java
new file mode 100644
index 0000000..65edd3c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryModuleComponentRepositoryCaches.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.memcache;
+
+public class InMemoryModuleComponentRepositoryCaches {
+    public final InMemoryMetaDataCache localMetaDataCache;
+    public final InMemoryMetaDataCache remoteMetaDataCache;
+    public final InMemoryArtifactsCache localArtifactsCache;
+    public final InMemoryArtifactsCache remoteArtifactsCache;
+    public final InMemoryCacheStats stats;
+
+    public InMemoryModuleComponentRepositoryCaches(InMemoryCacheStats stats) {
+        this(new InMemoryArtifactsCache(stats),
+                new InMemoryArtifactsCache(stats),
+                new InMemoryMetaDataCache(stats),
+                new InMemoryMetaDataCache(stats),
+                stats);
+    }
+
+    protected InMemoryModuleComponentRepositoryCaches(InMemoryArtifactsCache localArtifactsCache, InMemoryArtifactsCache remoteArtifactsCache,
+                                                      InMemoryMetaDataCache localMetaDataCache, InMemoryMetaDataCache remoteMetaDataCache,
+                                                      InMemoryCacheStats stats) {
+        this.localArtifactsCache = localArtifactsCache;
+        this.remoteArtifactsCache = remoteArtifactsCache;
+        this.localMetaDataCache = localMetaDataCache;
+        this.remoteMetaDataCache = remoteMetaDataCache;
+        this.stats = stats;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
new file mode 100644
index 0000000..0fbd54e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractModuleDescriptorParser.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+
+import java.io.File;
+
+public abstract class AbstractModuleDescriptorParser<T extends MutableModuleComponentResolveMetaData> implements MetaDataParser<T> {
+    public T parseMetaData(DescriptorParseContext ivySettings, File descriptorFile, boolean validate) throws MetaDataParseException {
+        LocallyAvailableResource localResource = new DefaultLocallyAvailableResource(descriptorFile);
+        LocallyAvailableExternalResource resource = new DefaultLocallyAvailableExternalResource(descriptorFile.toURI(), localResource);
+        return parseDescriptor(ivySettings, resource, validate);
+    }
+
+    public T parseMetaData(DescriptorParseContext ivySettings, File descriptorFile) throws MetaDataParseException {
+        return parseMetaData(ivySettings, descriptorFile, true);
+    }
+
+    public T parseMetaData(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource) throws MetaDataParseException {
+        return parseDescriptor(ivySettings, resource, true);
+    }
+
+    protected T 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 T doParseDescriptor(DescriptorParseContext ivySettings, LocallyAvailableExternalResource resource, boolean validate) throws Exception;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/BuildableIvyArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/BuildableIvyArtifact.java
new file mode 100644
index 0000000..8d38fff
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/BuildableIvyArtifact.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+class BuildableIvyArtifact {
+
+    private final IvyArtifactName ivyArtifactName;
+    private final Set<String> configurations = new LinkedHashSet<String>();
+
+    public BuildableIvyArtifact(String name, String type, String ext, Map<String, String> extraAttributes) {
+        this.ivyArtifactName = new DefaultIvyArtifactName(name, type, ext, extraAttributes);
+    }
+
+    public BuildableIvyArtifact addConfiguration(String confName) {
+        configurations.add(confName);
+        return this;
+    }
+
+    public IvyArtifactName getArtifact() {
+        return ivyArtifactName;
+    }
+
+    public Set<String> getConfigurations() {
+        return configurations;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.java
new file mode 100644
index 0000000..7edeff7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DescriptorParseContext.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.parser;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+public interface DescriptorParseContext {
+    LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, ArtifactType artifactType);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.java
new file mode 100644
index 0000000..7552701
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedDescriptorParseContext.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.artifacts.ivyservice.ivyresolve.parser;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+/**
+ * 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 implements DescriptorParseContext {
+
+    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, ArtifactType artifactType) {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.java
new file mode 100644
index 0000000..9460027
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParser.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.parser;
+
+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 org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Map;
+
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId;
+
+public class DisconnectedIvyXmlModuleDescriptorParser extends IvyXmlModuleDescriptorParser {
+    public DisconnectedIvyXmlModuleDescriptorParser(ResolverStrategy resolverStrategy) {
+        super(resolverStrategy);
+    }
+
+    @Override
+    protected Parser createParser(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, Map<String, String> properties, ResolverStrategy resolverStrategy) throws MalformedURLException {
+        return new DisconnectedParser(parseContext, resource, resource.getLocalResource().getFile().toURI().toURL(), properties, resolverStrategy);
+    }
+
+    private static class DisconnectedParser extends Parser {
+        public DisconnectedParser(DescriptorParseContext parseContext, ExternalResource res, URL descriptorURL, Map<String, String> properties, ResolverStrategy resolverStrategy) {
+            super(parseContext, res, descriptorURL, properties, resolverStrategy);
+        }
+
+        @Override
+        public Parser newParser(ExternalResource res, URL descriptorURL) {
+            Parser parser = new DisconnectedParser(getParseContext(), res, descriptorURL, properties, resolverStrategy);
+            parser.setValidate(isValidate());
+            return parser;
+        }
+
+        @Override
+        protected ModuleDescriptor parseOtherIvyFile(String parentOrganisation,
+                                                     String parentModule, String parentRevision) throws IOException, ParseException, SAXException {
+            ModuleRevisionId parentMrid = createModuleRevisionId(parentOrganisation, parentModule, parentRevision);
+            return new DefaultModuleDescriptor(parentMrid, "release", new Date());
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParser.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
new file mode 100644
index 0000000..f370eb7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorBuilder.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Ivy;
+import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+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.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader.PomDependencyData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This a straight copy of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder, with minor changes: 1) Do not create artifact for empty classifier. (Previously did so for all non-null
+ * classifiers)
+ */
+public class GradlePomModuleDescriptorBuilder {
+    public static final Configuration[] MAVEN2_CONFIGURATIONS = new Configuration[]{
+            new Configuration("default", Visibility.PUBLIC,
+                    "runtime dependencies and master artifact can be used with this conf",
+                    new String[]{"runtime", "master"}, true, null),
+            new Configuration("master", Visibility.PUBLIC,
+                    "contains only the artifact published by this module itself, "
+                            + "with no transitive dependencies",
+                    new String[0], true, null),
+            new Configuration("compile", Visibility.PUBLIC,
+                    "this is the default scope, used if none is specified. "
+                            + "Compile dependencies are available in all classpaths.",
+                    new String[0], true, null),
+            new Configuration("provided", Visibility.PUBLIC,
+                    "this is much like compile, but indicates you expect the JDK or a container "
+                            + "to provide it. "
+                            + "It is only available on the compilation classpath, and is not transitive.",
+                    new String[0], true, null),
+            new Configuration("runtime", Visibility.PUBLIC,
+                    "this scope indicates that the dependency is not required for compilation, "
+                            + "but is for execution. It is in the runtime and test classpaths, "
+                            + "but not the compile classpath.",
+                    new String[]{"compile"}, true, null),
+            new Configuration("test", Visibility.PRIVATE,
+                    "this scope indicates that the dependency is not required for normal use of "
+                            + "the application, and is only available for the test compilation and "
+                            + "execution phases.",
+                    new String[]{"runtime"}, true, null),
+            new Configuration("system", Visibility.PUBLIC,
+                    "this scope is similar to provided except that you have to provide the JAR "
+                            + "which contains it explicitly. The artifact is always available and is not "
+                            + "looked up in a repository.",
+                    new String[0], true, null),
+            new Configuration("sources", Visibility.PUBLIC,
+                    "this configuration contains the source artifact of this module, if any.",
+                    new String[0], true, null),
+            new Configuration("javadoc", Visibility.PUBLIC,
+                    "this configuration contains the javadoc artifact of this module, if any.",
+                    new String[0], true, null),
+            new Configuration("optional", Visibility.PUBLIC,
+                    "contains all optional dependencies", new String[0], true, null)
+    };
+
+    static final Map<String, ConfMapper> MAVEN2_CONF_MAPPING = new HashMap<String, ConfMapper>();
+    private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("(.+)-\\d{8}\\.\\d{6}-\\d+");
+    private static final String EXTRA_ATTRIBUTE_CLASSIFIER = "m:classifier";
+
+    static interface ConfMapper {
+        public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional);
+    }
+
+    static {
+        MAVEN2_CONF_MAPPING.put("compile", new ConfMapper() {
+            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+                if (isOptional) {
+                    dd.addDependencyConfiguration("optional", "compile(*)");
+                    //dd.addDependencyConfiguration("optional", "provided(*)");
+                    dd.addDependencyConfiguration("optional", "master(*)");
+
+                } else {
+                    dd.addDependencyConfiguration("compile", "compile(*)");
+                    //dd.addDependencyConfiguration("compile", "provided(*)");
+                    dd.addDependencyConfiguration("compile", "master(*)");
+                    dd.addDependencyConfiguration("runtime", "runtime(*)");
+                }
+            }
+        });
+        MAVEN2_CONF_MAPPING.put("provided", new ConfMapper() {
+            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+                if (isOptional) {
+                    dd.addDependencyConfiguration("optional", "compile(*)");
+                    dd.addDependencyConfiguration("optional", "provided(*)");
+                    dd.addDependencyConfiguration("optional", "runtime(*)");
+                    dd.addDependencyConfiguration("optional", "master(*)");
+                } else {
+                    dd.addDependencyConfiguration("provided", "compile(*)");
+                    dd.addDependencyConfiguration("provided", "provided(*)");
+                    dd.addDependencyConfiguration("provided", "runtime(*)");
+                    dd.addDependencyConfiguration("provided", "master(*)");
+                }
+            }
+        });
+        MAVEN2_CONF_MAPPING.put("runtime", new ConfMapper() {
+            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+                if (isOptional) {
+                    dd.addDependencyConfiguration("optional", "compile(*)");
+                    dd.addDependencyConfiguration("optional", "provided(*)");
+                    dd.addDependencyConfiguration("optional", "master(*)");
+
+                } else {
+                    dd.addDependencyConfiguration("runtime", "compile(*)");
+                    dd.addDependencyConfiguration("runtime", "runtime(*)");
+                    dd.addDependencyConfiguration("runtime", "master(*)");
+                }
+            }
+        });
+        MAVEN2_CONF_MAPPING.put("test", new ConfMapper() {
+            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+                //optional doesn't make sense in the test scope
+                dd.addDependencyConfiguration("test", "runtime(*)");
+                dd.addDependencyConfiguration("test", "master(*)");
+            }
+        });
+        MAVEN2_CONF_MAPPING.put("system", new ConfMapper() {
+            public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+                //optional doesn't make sense in the system scope
+                dd.addDependencyConfiguration("system", "master(*)");
+            }
+        });
+    }
+
+    private final VersionSelectorScheme defaultVersionSelectorScheme;
+    private final VersionSelectorScheme mavenVersionSelectorScheme;
+    private final DefaultModuleDescriptor ivyModuleDescriptor;
+
+    private ModuleRevisionId mrid;
+
+    private final PomReader pomReader;
+
+    public GradlePomModuleDescriptorBuilder(PomReader pomReader, VersionSelectorScheme gradleVersionSelectorScheme, VersionSelectorScheme mavenVersionSelectorScheme) {
+        this.defaultVersionSelectorScheme = gradleVersionSelectorScheme;
+        this.mavenVersionSelectorScheme = mavenVersionSelectorScheme;
+        ivyModuleDescriptor = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
+        ivyModuleDescriptor.setResolvedPublicationDate(new Date());
+        for (Configuration maven2Configuration : MAVEN2_CONFIGURATIONS) {
+            ivyModuleDescriptor.addConfiguration(maven2Configuration);
+        }
+        ivyModuleDescriptor.setMappingOverride(true);
+        ivyModuleDescriptor.addExtraAttributeNamespace("m", Ivy.getIvyHomeURL() + "maven");
+        this.pomReader = pomReader;
+    }
+
+    public DefaultModuleDescriptor getModuleDescriptor() {
+        return ivyModuleDescriptor;
+    }
+
+    public void setModuleRevId(String group, String module, String version) {
+        String effectiveVersion = version;
+        if (version != null) {
+            Matcher matcher = TIMESTAMP_PATTERN.matcher(version);
+            if (matcher.matches()) {
+                effectiveVersion = matcher.group(1) + "-SNAPSHOT";
+            }
+        }
+
+        this.mrid = ModuleRevisionId.newInstance(group, module, effectiveVersion);
+        ivyModuleDescriptor.setModuleRevisionId(mrid);
+
+        if (effectiveVersion != null && effectiveVersion.endsWith("SNAPSHOT")) {
+            ivyModuleDescriptor.setStatus("integration");
+        } else {
+            ivyModuleDescriptor.setStatus("release");
+        }
+    }
+
+    public void setHomePage(String homePage) {
+        ivyModuleDescriptor.setHomePage(homePage);
+    }
+
+    public void setDescription(String description) {
+        ivyModuleDescriptor.setDescription(description);
+    }
+
+    public void setLicenses(Iterable<License> licenses) {
+        for (License license : licenses) {
+            ivyModuleDescriptor.addLicense(license);
+        }
+    }
+
+    public void addDependency(PomDependencyData dep) {
+        String scope = dep.getScope();
+        if ((scope != null) && (scope.length() > 0) && !MAVEN2_CONF_MAPPING.containsKey(scope)) {
+            // unknown scope, defaulting to 'compile'
+            scope = "compile";
+        }
+
+        String version = determineVersion(dep);
+        String mappedVersion = convertVersionFromMavenSyntax(version);
+        ModuleRevisionId moduleRevId = IvyUtil.createModuleRevisionId(dep.getGroupId(), dep.getArtifactId(), mappedVersion);
+
+        // Some POMs depend on themselves, don't add this dependency: Ivy doesn't allow this!
+        // Example: http://repo2.maven.org/maven2/net/jini/jsk-platform/2.1/jsk-platform-2.1.pom
+        ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
+        if ((mRevId != null) && mRevId.getModuleId().equals(moduleRevId.getModuleId())) {
+            return;
+        }
+
+        DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(ivyModuleDescriptor, moduleRevId, true, false, true);
+        scope = (scope == null || scope.length() == 0) ? getDefaultScope(dep) : scope;
+        ConfMapper mapping = MAVEN2_CONF_MAPPING.get(scope);
+        mapping.addMappingConfs(dd, dep.isOptional());
+        Map<String, String> extraAtt = new HashMap<String, String>();
+        boolean hasClassifier = dep.getClassifier() != null && dep.getClassifier().length() > 0;
+        boolean hasNonJarType = dep.getType() != null && !"jar".equals(dep.getType());
+        if (hasClassifier || hasNonJarType) {
+            String type = "jar";
+            if (dep.getType() != null) {
+                type = dep.getType();
+            }
+            String ext = determineExtension(type);
+            handleSpecialTypes(type, extraAtt);
+
+            // we deal with classifiers by setting an extra attribute and forcing the
+            // dependency to assume such an artifact is published
+            if (dep.getClassifier() != null) {
+                extraAtt.put(EXTRA_ATTRIBUTE_CLASSIFIER, dep.getClassifier());
+            }
+            DefaultDependencyArtifactDescriptor depArtifact = new DefaultDependencyArtifactDescriptor(dd, dd.getDependencyId().getName(), type, ext, null, extraAtt);
+            // here we have to assume a type and ext for the artifact, so this is a limitation
+            // compared to how m2 behave with classifiers
+            String optionalizedScope = dep.isOptional() ? "optional" : scope;
+            dd.addDependencyArtifact(optionalizedScope, depArtifact);
+        }
+
+        // experimentation shows the following, excluded modules are
+        // inherited from parent POMs if either of the following is true:
+        // the <exclusions> element is missing or the <exclusions> element
+        // is present, but empty.
+        List /*<ModuleId>*/ excluded = dep.getExcludedModules();
+        if (excluded.isEmpty()) {
+            excluded = getDependencyMgtExclusions(dep);
+        }
+        for (Object anExcluded : excluded) {
+            ModuleId excludedModule = (ModuleId) anExcluded;
+            String[] confs = dd.getModuleConfigurations();
+            for (String conf : confs) {
+                dd.addExcludeRule(conf, new DefaultExcludeRule(new ArtifactId(
+                        excludedModule, PatternMatcher.ANY_EXPRESSION,
+                        PatternMatcher.ANY_EXPRESSION,
+                        PatternMatcher.ANY_EXPRESSION),
+                        ExactPatternMatcher.INSTANCE, null));
+            }
+        }
+
+        ivyModuleDescriptor.addDependency(dd);
+    }
+
+    // TODO:DAZ Would be better if we held onto the VersionSelector and only rendered it when required
+    private String convertVersionFromMavenSyntax(String version) {
+        VersionSelector versionSelector = mavenVersionSelectorScheme.parseSelector(version);
+        return defaultVersionSelectorScheme.renderSelector(versionSelector);
+    }
+
+    /**
+     * Determines extension of dependency.
+     *
+     * @param type Type
+     * @return Extension
+     */
+    private String determineExtension(String type) {
+        return JarDependencyType.isJarExtension(type) ? "jar" : type;
+    }
+
+    /**
+     * Handles special types of dependencies. If one of the following types matches, a specific type of classifier is set.
+     *
+     * - test-jar (see <a href="http://maven.apache.org/guides/mini/guide-attached-tests.html">Maven documentation</a>)
+     * - ejb-client (see <a href="http://maven.apache.org/plugins/maven-ejb-plugin/examples/ejb-client-dependency.html">Maven documentation</a>)
+     *
+     * @param type Type
+     * @param extraAttributes Extra attributes
+     */
+    private void handleSpecialTypes(String type, Map<String, String> extraAttributes) {
+        if(JarDependencyType.TEST_JAR.getName().equals(type)) {
+            extraAttributes.put(EXTRA_ATTRIBUTE_CLASSIFIER, "tests");
+        } else if(JarDependencyType.EJB_CLIENT.getName().equals(type)) {
+            extraAttributes.put(EXTRA_ATTRIBUTE_CLASSIFIER, "client");
+        }
+    }
+
+    private enum JarDependencyType {
+        TEST_JAR("test-jar"), EJB_CLIENT("ejb-client"), EJB("ejb"), BUNDLE("bundle"), MAVEN_PLUGIN("maven-plugin"), ECLIPSE_PLUGIN("eclipse-plugin");
+
+        private static final Map<String, JarDependencyType> TYPES;
+
+        static {
+            TYPES = new HashMap<String, JarDependencyType>();
+
+            for(JarDependencyType type : values()) {
+                TYPES.put(type.name, type);
+            }
+        }
+
+        private final String name;
+
+        private JarDependencyType(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public static boolean isJarExtension(String type) {
+            return TYPES.containsKey(type);
+        }
+    }
+
+    /**
+     * Determines the version of a dependency. Uses the specified version if declared for the as coordinate. If the version is not declared, try to resolve it from the dependency management section.
+     * In case the version cannot be resolved with any of these methods, throw an exception of type {@see org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.UnresolvedDependencyVersionException}.
+     *
+     * @param dependency Dependency
+     * @return Resolved dependency version
+     */
+    private String determineVersion(PomDependencyData dependency) {
+        String version = dependency.getVersion();
+        version = (version == null || version.length() == 0) ? getDefaultVersion(dependency) : version;
+
+        if (version == null) {
+            throw new UnresolvedDependencyVersionException(dependency.getId());
+        }
+
+        return version;
+    }
+
+    public void addDependency(DependencyDescriptor descriptor) {
+        // Some POMs depend on themselves 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();
+        ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
+        if ((mRevId != null) && mRevId.getModuleId().equals(dependencyId)) {
+            return;
+        }
+
+        ivyModuleDescriptor.addDependency(descriptor);
+    }
+
+    private String getDefaultVersion(PomDependencyData dep) {
+        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
+        if (pomDependencyMgt != null) {
+            return pomDependencyMgt.getVersion();
+        }
+        return null;
+    }
+
+    private String getDefaultScope(PomDependencyData dep) {
+        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
+        String result = null;
+        if (pomDependencyMgt != null) {
+            result = pomDependencyMgt.getScope();
+        }
+        if ((result == null) || !MAVEN2_CONF_MAPPING.containsKey(result)) {
+            result = "compile";
+        }
+        return result;
+    }
+
+    private List<ModuleId> getDependencyMgtExclusions(PomDependencyData dep) {
+        PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep);
+        if (pomDependencyMgt != null) {
+            return pomDependencyMgt.getExcludedModules();
+        }
+
+        return Collections.emptyList();
+    }
+
+    private PomDependencyMgt findDependencyDefault(PomDependencyData dependency) {
+        return pomReader.findDependencyDefaults(dependency.getId());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
new file mode 100644
index 0000000..87124b1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+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.parser.PomReader.PomDependencyData;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.MavenVersionSelectorScheme;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * This based on a copy of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser, but now heavily refactored.
+ */
+public final class GradlePomModuleDescriptorParser extends AbstractModuleDescriptorParser<DefaultMavenModuleResolveMetaData> {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GradlePomModuleDescriptorParser.class);
+    private static final String DEPENDENCY_IMPORT_SCOPE = "import";
+    private final VersionSelectorScheme gradleVersionSelectorScheme;
+    private final VersionSelectorScheme mavenVersionSelectorScheme;
+
+    public GradlePomModuleDescriptorParser(VersionSelectorScheme gradleVersionSelectorScheme) {
+        this.gradleVersionSelectorScheme = gradleVersionSelectorScheme;
+        mavenVersionSelectorScheme = new MavenVersionSelectorScheme(gradleVersionSelectorScheme);
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "POM";
+    }
+
+    public String toString() {
+        return "gradle pom parser";
+    }
+
+    protected DefaultMavenModuleResolveMetaData doParseDescriptor(DescriptorParseContext parserSettings, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException, SAXException {
+        PomReader pomReader = new PomReader(resource);
+        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(pomReader, gradleVersionSelectorScheme, mavenVersionSelectorScheme);
+
+        doParsePom(parserSettings, mdBuilder, pomReader);
+
+        DefaultModuleDescriptor moduleDescriptor = mdBuilder.getModuleDescriptor();
+        if(pomReader.getRelocation() != null) {
+            return new DefaultMavenModuleResolveMetaData(moduleDescriptor, "pom", true);
+        }
+        return new DefaultMavenModuleResolveMetaData(moduleDescriptor, pomReader.getPackaging(), false);
+    }
+
+    private void doParsePom(DescriptorParseContext parserSettings, GradlePomModuleDescriptorBuilder mdBuilder, PomReader pomReader) throws IOException, SAXException {
+        if (pomReader.hasParent()) {
+            //Is there any other parent properties?
+
+            ModuleVersionIdentifier parentId = DefaultModuleVersionIdentifier.newId(
+                    pomReader.getParentGroupId(),
+                    pomReader.getParentArtifactId(),
+                    pomReader.getParentVersion());
+            PomReader parentPomReader = parseOtherPom(parserSettings, parentId);
+            pomReader.setPomParent(parentPomReader);
+        }
+        pomReader.resolveGAV();
+
+        String groupId = pomReader.getGroupId();
+        String artifactId = pomReader.getArtifactId();
+        String version = pomReader.getVersion();
+        mdBuilder.setModuleRevId(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.");
+                PomReader relocatedModule = parseOtherPom(parserSettings, DefaultModuleVersionIdentifier.newId(relocation));
+
+                Collection<PomDependencyData> pomDependencyDataList = relocatedModule.getDependencies().values();
+                for(PomDependencyData pomDependencyData : pomDependencyDataList) {
+                    mdBuilder.addDependency(pomDependencyData);
+                }
+
+            } else {
+                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());
+                    }
+                }
+                mdBuilder.addDependency(dd);
+            }
+        } else {
+            overrideDependencyMgtsWithImported(parserSettings, pomReader);
+
+            for (PomDependencyData dependency : pomReader.getDependencies().values()) {
+                mdBuilder.addDependency(dependency);
+            }
+        }
+    }
+
+    /**
+     * Overrides existing dependency management information with imported ones if existing.
+     *
+     * @param parseContext Parse context
+     * @param pomReader POM reader
+     * @throws IOException
+     * @throws SAXException
+     */
+    private void overrideDependencyMgtsWithImported(DescriptorParseContext parseContext, PomReader pomReader) throws IOException, SAXException {
+        Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = parseImportedDependencyMgts(parseContext, pomReader.parseDependencyMgt());
+        pomReader.addImportedDependencyMgts(importedDependencyMgts);
+    }
+
+    /**
+     * Parses imported dependency management information.
+     *
+     * @param parseContext Parse context
+     * @param currentDependencyMgts Current dependency management information
+     * @return Imported dependency management information
+     * @throws IOException
+     * @throws SAXException
+     */
+    private Map<MavenDependencyKey, PomDependencyMgt> parseImportedDependencyMgts(DescriptorParseContext parseContext, Collection<PomDependencyMgt> currentDependencyMgts) throws IOException, SAXException {
+        Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
+
+        for(PomDependencyMgt currentDependencyMgt : currentDependencyMgts) {
+            if(isDependencyImportScoped(currentDependencyMgt)) {
+                PomReader importDescr = parseImportedPom(parseContext, currentDependencyMgt);
+                importedDependencyMgts.putAll(importDescr.getDependencyMgt());
+            }
+        }
+
+        return importedDependencyMgts;
+    }
+
+    /**
+     * Checks if dependency has scope "import".
+     *
+     * @param dependencyMgt Dependency management element
+     * @return Flag
+     */
+    private boolean isDependencyImportScoped(PomDependencyMgt dependencyMgt) {
+        return DEPENDENCY_IMPORT_SCOPE.equals(dependencyMgt.getScope());
+    }
+
+    /**
+     * Parses imported POM.
+     *
+     * @param parseContext Parse context
+     * @param pomDependencyMgt Dependency management information
+     * @return POM reader
+     * @throws IOException
+     * @throws SAXException
+     */
+    private PomReader parseImportedPom(DescriptorParseContext parseContext, PomDependencyMgt pomDependencyMgt) throws IOException, SAXException {
+        ModuleVersionIdentifier importedId = DefaultModuleVersionIdentifier.newId(pomDependencyMgt.getGroupId(), pomDependencyMgt.getArtifactId(), pomDependencyMgt.getVersion());
+        return parseOtherPom(parseContext, importedId);
+    }
+
+    /**
+     * Parses other POM.
+     *
+     * @param parseContext Parse context
+     * @param parentId Parent module revision ID
+     * @return POM reader
+     * @throws IOException
+     * @throws SAXException
+     */
+    private PomReader parseOtherPom(DescriptorParseContext parseContext, ModuleVersionIdentifier parentId) throws IOException, SAXException {
+        LocallyAvailableExternalResource localResource = parseContext.getMetaDataArtifact(parentId, ArtifactType.MAVEN_POM);
+        PomReader pomReader = new PomReader(localResource);
+        GradlePomModuleDescriptorBuilder mdBuilder = new GradlePomModuleDescriptorBuilder(pomReader, gradleVersionSelectorScheme, mavenVersionSelectorScheme);
+        doParsePom(parseContext, mdBuilder, pomReader);
+        return pomReader;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
new file mode 100644
index 0000000..bfc5a6b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParser.java
@@ -0,0 +1,1281 @@
+/*
+ * 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.parser;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Sets;
+import org.apache.ivy.core.IvyPatternHelper;
+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.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.util.extendable.DefaultExtendableItem;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.external.model.BuildableIvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.DefaultIvyModuleResolveMetaData;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.ResourceNotFoundException;
+import org.gradle.internal.resource.transfer.UrlExternalResource;
+import org.gradle.util.CollectionUtils;
+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 javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+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.*;
+
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId;
+
+/**
+ * Copied from org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser into Gradle codebase, and heavily modified.
+ */
+public class IvyXmlModuleDescriptorParser extends AbstractModuleDescriptorParser<DefaultIvyModuleResolveMetaData> {
+    static final String[] DEPENDENCY_REGULAR_ATTRIBUTES =
+            new String[] {"org", "name", "branch", "branchConstraint", "rev", "revConstraint", "force", "transitive", "changing", "conf"};
+
+    public static final String IVY_DATE_FORMAT_PATTERN = "yyyyMMddHHmmss";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(IvyXmlModuleDescriptorParser.class);
+    private final ResolverStrategy resolverStrategy;
+
+    public IvyXmlModuleDescriptorParser(ResolverStrategy resolverStrategy) {
+        this.resolverStrategy = resolverStrategy;
+    }
+
+    protected DefaultIvyModuleResolveMetaData doParseDescriptor(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, boolean validate) throws IOException, ParseException {
+        Parser parser = createParser(parseContext, resource, populateProperties(), resolverStrategy);
+        return doParseDescriptorWithProvidedParser(parser, validate);
+    }
+
+    protected Parser createParser(DescriptorParseContext parseContext, LocallyAvailableExternalResource resource, Map<String, String> properties, ResolverStrategy resolverStrategy) throws MalformedURLException {
+        return new Parser(parseContext, resource, resource.getLocalResource().getFile().toURI().toURL(), properties, resolverStrategy);
+    }
+
+    private DefaultIvyModuleResolveMetaData doParseDescriptorWithProvidedParser(Parser parser, boolean validate) throws ParseException {
+        parser.setValidate(validate);
+        parser.parse();
+        DefaultModuleDescriptor moduleDescriptor = parser.getModuleDescriptor();
+        postProcess(moduleDescriptor);
+
+        return parser.getMetaData();
+    }
+
+    protected void postProcess(DefaultModuleDescriptor moduleDescriptor) {
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "Ivy file";
+    }
+
+    private Map<String, String> populateProperties() {
+        HashMap<String, String> properties = new HashMap<String, String>();
+        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));
+        }
+        return properties;
+    }
+
+    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;
+        protected BuildableIvyModuleResolveMetaData metaData;
+
+        protected AbstractParser(ExternalResource resource) {
+            this.res = resource; // used for log and date only
+            md = new DefaultModuleDescriptor(XmlModuleDescriptorParser.getInstance(), null);
+        }
+
+        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(createModuleRevisionId("", "", ""), 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;
+        }
+
+        public DefaultIvyModuleResolveMetaData getMetaData() {
+            return metaData;
+        }
+
+        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 {
+        public enum State {
+            NONE,
+            INFO,
+            CONF,
+            PUB,
+            DEP,
+            DEP_ARTIFACT,
+            ARTIFACT_INCLUDE,
+            ARTIFACT_EXCLUDE,
+            CONFLICT,
+            EXCLUDE,
+            DEPS,
+            DESCRIPTION,
+            EXTRA_INFO
+        }
+
+        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 final DescriptorParseContext parseContext;
+        private final RelativeUrlResolver relativeUrlResolver = new NormalRelativeUrlResolver();
+        private final URL descriptorURL;
+        private boolean validate = true;
+
+        /* Parsing state */
+        private State state = State.NONE;
+        private PatternMatcher defaultMatcher;
+        private DefaultDependencyDescriptor dd;
+        private ConfigurationAware confAware;
+        private BuildableIvyArtifact artifact;
+        private String conf;
+        private boolean artifactsDeclared;
+        private StringBuffer buffer;
+        private String descriptorVersion;
+        private String[] publicationsDefaultConf;
+        final Map<String, String> properties;
+        final ResolverStrategy resolverStrategy;
+
+        public Parser(DescriptorParseContext parseContext, ExternalResource res, URL descriptorURL, Map<String, String> properties, ResolverStrategy resolverStrategy) {
+            super(res);
+            this.parseContext = parseContext;
+            this.descriptorURL = descriptorURL;
+            this.properties = properties;
+            this.resolverStrategy = resolverStrategy;
+        }
+
+        public Parser newParser(ExternalResource res, URL descriptorURL) {
+            Parser parser = new Parser(parseContext, res, descriptorURL, properties, resolverStrategy);
+            parser.setValidate(validate);
+            return parser;
+        }
+
+        public void setValidate(boolean validate) {
+            this.validate = validate;
+        }
+
+        public boolean isValidate() {
+            return validate;
+        }
+
+        public DescriptorParseContext getParseContext() {
+            return parseContext;
+        }
+
+        public void parse() throws ParseException {
+            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 {
+                        ParserHelper.parse(inSrc, schemaURL, Parser.this);
+                    } catch (Exception e) {
+                        throw new MetaDataParseException("Ivy file", getResource(), e);
+                    }
+                }
+            });
+            checkErrors();
+            checkConfigurations();
+            replaceConfigurationWildcards();
+            if (!artifactsDeclared) {
+                IvyArtifactName implicitArtifact = new DefaultIvyArtifactName(getMd().getModuleRevisionId().getName(), "jar", "jar");
+                Set<String> configurationNames = Sets.newHashSet(getMd().getConfigurationsNames());
+                metaData.addArtifact(implicitArtifact, configurationNames);
+            }
+            checkErrors();
+            getMd().check();
+        }
+
+        public void startElement(String uri, String localName, String qName, Attributes attributes)
+                throws SAXException {
+            try {
+                if (state == State.DESCRIPTION) {
+                    // make sure we don't interpret any tag while in description tag
+                    descriptionStarted(qName, attributes);
+                } else if ("ivy-module".equals(qName)) {
+                    ivyModuleStarted(attributes);
+                } else if ("info".equals(qName)) {
+                    infoStarted(attributes);
+                } else if (state == State.INFO && "extends".equals(qName)) {
+                    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 && "ivyauthor".equals(qName)) {
+                    // nothing to do, we don't store this
+                    return;
+                } else if (state == State.INFO && "repository".equals(qName)) {
+                    // nothing to do, we don't store this
+                    return;
+                } else if (state == State.INFO && "description".equals(qName)) {
+                    getMd().setHomePage(substitute(attributes.getValue("homepage")));
+                    state = State.DESCRIPTION;
+                    buffer = new StringBuffer();
+                } else if (state == State.INFO && isOtherNamespace(qName)) {
+                    buffer = new StringBuffer();
+                    state = State.EXTRA_INFO;
+                } else if ("configurations".equals(qName)) {
+                    configurationStarted(attributes);
+                } else if ("publications".equals(qName)) {
+                    publicationsStarted(attributes);
+                } else if ("dependencies".equals(qName)) {
+                    dependenciesStarted(attributes);
+                } else if ("conflicts".equals(qName)) {
+                    state = State.CONFLICT;
+                    checkConfigurations();
+                } else if ("artifact".equals(qName)) {
+                    artifactStarted(qName, attributes);
+                } else if ("include".equals(qName) && state == State.DEP) {
+                    addIncludeRule(qName, attributes);
+                } else if ("exclude".equals(qName) && state == State.DEP) {
+                    addExcludeRule(qName, attributes);
+                } else if ("exclude".equals(qName) && state == State.DEPS) {
+                    state = State.EXCLUDE;
+                    parseRule(qName, attributes);
+                    getMd().addExcludeRule((ExcludeRule) confAware);
+                } else if ("dependency".equals(qName)) {
+                    dependencyStarted(attributes);
+                } else if ("conf".equals(qName)) {
+                    confStarted(attributes);
+                } else if ("mapped".equals(qName)) {
+                    dd.addDependencyConfiguration(conf, substitute(attributes.getValue("name")));
+                } else if (("conflict".equals(qName) && state == State.DEPS) || "manager".equals(qName) && state == State.CONFLICT) {
+                    LOGGER.debug("Ivy.xml conflict managers are not supported by Gradle. Ignoring conflict manager declared in {}", getResource().getName());
+                } else if ("override".equals(qName) && state == State.DEPS) {
+                    LOGGER.debug("Ivy.xml dependency overrides are not supported by Gradle. Ignoring override declared in {}", getResource().getName());
+                } else if ("include".equals(qName) && state == State.CONF) {
+                    includeConfStarted(attributes);
+                } else if (validate && state != State.EXTRA_INFO && state != State.DESCRIPTION) {
+                    addError("unknown tag " + qName);
+                }
+            } catch (Exception ex) {
+                if (ex instanceof SAXException) {
+                    throw (SAXException) ex;
+                }
+                SAXException sax = new SAXException("Problem occurred while parsing ivy file: "
+                        + ex.getMessage(), ex);
+                sax.initCause(ex);
+                throw sax;
+            }
+        }
+
+        private void extendsStarted(Attributes attributes) throws ParseException {
+            String parentOrganisation = attributes.getValue("organisation");
+            String parentModule = attributes.getValue("module");
+            String parentRevision = attributes.getValue("revision");
+            String location = elvis(attributes.getValue("location"), "../ivy.xml");
+
+            String extendType = elvis(attributes.getValue("extendType"), "all").toLowerCase();
+            List<String> extendTypes = Arrays.asList(extendType.split(","));
+
+            ModuleDescriptor parent;
+            try {
+                LOGGER.debug("Trying to parse included ivy file :" + location);
+                parent = parseOtherIvyFileOnFileSystem(location);
+                if (parent != null) {
+                    //verify that the parsed descriptor is the correct parent module.
+                    ModuleId expected = IvyUtil.createModuleId(parentOrganisation, parentModule);
+                    ModuleId pid = parent.getModuleRevisionId().getModuleId();
+                    if (!expected.equals(pid)) {
+                        LOGGER.warn("Ignoring parent Ivy file " + location + "; expected " + expected + " but found " + pid);
+                        parent = null;
+                    }
+                }
+
+                // if the included ivy file is not found on file system, tries to resolve using
+                // repositories
+                if (parent == null) {
+                    LOGGER.debug("Trying to parse included ivy file by asking repository for module :"
+                            + parentOrganisation
+                            + "#"
+                            + parentModule
+                            + ";"
+                            + parentRevision);
+                    parent = parseOtherIvyFile(parentOrganisation, parentModule, parentRevision);
+                }
+            } catch(Exception e) {
+                throw (ParseException) new ParseException("Unable to parse included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision, 0).initCause(e);
+            }
+
+            if (parent == null) {
+                throw new ParseException("Unable to parse included ivy file for " + parentOrganisation + "#" + parentModule + ";" + parentRevision, 0);
+            }
+
+            mergeWithOtherModuleDescriptor(extendTypes, parent);
+        }
+
+        private void mergeWithOtherModuleDescriptor(List<String> extendTypes, ModuleDescriptor parent) {
+
+            if (extendTypes.contains("all")) {
+                mergeAll(parent);
+            } else {
+                if (extendTypes.contains("info")) {
+                    mergeInfo(parent);
+                }
+
+                if (extendTypes.contains("configurations")) {
+                    mergeConfigurations(parent.getModuleRevisionId(), parent.getConfigurations());
+                }
+
+                if (extendTypes.contains("dependencies")) {
+                    mergeDependencies(parent.getDependencies());
+                }
+
+                if (extendTypes.contains("description")) {
+                    mergeDescription(parent.getDescription());
+                }
+            }
+        }
+
+        private void mergeAll(ModuleDescriptor parent) {
+            ModuleRevisionId sourceMrid = parent.getModuleRevisionId();
+            mergeInfo(parent);
+            mergeConfigurations(sourceMrid, parent.getConfigurations());
+            mergeDependencies(parent.getDependencies());
+            mergeDescription(parent.getDescription());
+        }
+
+        private void mergeInfo(ModuleDescriptor parent) {
+            ModuleRevisionId parentMrid = parent.getModuleRevisionId();
+
+            DefaultModuleDescriptor descriptor = getMd();
+            ModuleRevisionId currentMrid = descriptor.getModuleRevisionId();
+
+            ModuleRevisionId mergedMrid = createModuleRevisionId(
+                    mergeValue(parentMrid.getOrganisation(), currentMrid.getOrganisation()),
+                    currentMrid.getName(),
+                    mergeValue(parentMrid.getBranch(), currentMrid.getBranch()),
+                    mergeValue(parentMrid.getRevision(), currentMrid.getRevision()),
+                    mergeValues(parentMrid.getQualifiedExtraAttributes(), currentMrid.getQualifiedExtraAttributes())
+            );
+
+            descriptor.setModuleRevisionId(mergedMrid);
+            descriptor.setResolvedModuleRevisionId(mergedMrid);
+
+            descriptor.setStatus(mergeValue(parent.getStatus(), descriptor.getStatus()));
+            if (descriptor.getNamespace() == null && parent instanceof DefaultModuleDescriptor) {
+                Namespace parentNamespace = ((DefaultModuleDescriptor) parent).getNamespace();
+                descriptor.setNamespace(parentNamespace);
+            }
+        }
+
+        private static String mergeValue(String inherited, String override) {
+            return override == null ? inherited : override;
+        }
+
+        private static Map mergeValues(Map inherited, Map overrides) {
+            LinkedHashMap dup = new LinkedHashMap(inherited.size() + overrides.size());
+            dup.putAll(inherited);
+            dup.putAll(overrides);
+            return dup;
+        }
+
+        private void mergeConfigurations(ModuleRevisionId sourceMrid, Configuration[] configurations) {
+            DefaultModuleDescriptor md = getMd();
+            for (Configuration configuration : configurations) {
+                LOGGER.debug("Merging configuration with: " + configuration.getName());
+                //copy configuration from parent descriptor
+                md.addConfiguration(new Configuration(configuration, sourceMrid));
+            }
+        }
+
+        private void mergeDependencies(DependencyDescriptor[] dependencies) {
+            DefaultModuleDescriptor md = getMd();
+            for (DependencyDescriptor dependencyDescriptor : dependencies) {
+                LOGGER.debug("Merging dependency with: " + dependencyDescriptor.getDependencyRevisionId().toString());
+                md.addDependency(dependencyDescriptor);
+            }
+        }
+
+        private void mergeDescription(String description) {
+            String current = getMd().getDescription();
+            if (current == null || current.trim().length() == 0) {
+                getMd().setDescription(description);
+            }
+        }
+
+        private ModuleDescriptor parseOtherIvyFileOnFileSystem(String location)
+                throws ParseException, IOException {
+            URL url = relativeUrlResolver.getURL(descriptorURL, location);
+            LOGGER.debug("Trying to load included ivy file from " + url.toString());
+            ExternalResource resource = UrlExternalResource.open(url);
+            try {
+                return parseModuleDescriptor(resource, url);
+            } catch (ResourceNotFoundException e) {
+                // Ignore
+                return null;
+            } finally {
+                resource.close();
+            }
+        }
+
+        protected ModuleDescriptor parseOtherIvyFile(String parentOrganisation, String parentModule, String parentRevision) throws IOException, ParseException, SAXException {
+            ModuleVersionIdentifier importedId = new DefaultModuleVersionIdentifier(parentOrganisation, parentModule, parentRevision);
+            LocallyAvailableExternalResource externalResource = parseContext.getMetaDataArtifact(importedId, ArtifactType.IVY_DESCRIPTOR);
+
+            return parseModuleDescriptor(externalResource, externalResource.getLocalResource().getFile().toURI().toURL());
+        }
+
+        private ModuleDescriptor parseModuleDescriptor(ExternalResource externalResource, URL descriptorURL) throws ParseException {
+            Parser parser = newParser(externalResource, descriptorURL);
+            parser.parse();
+            return parser.getModuleDescriptor();
+        }
+
+        private void publicationsStarted(Attributes attributes) {
+            state = State.PUB;
+            artifactsDeclared = true;
+            checkConfigurations();
+            String defaultConf = substitute(attributes.getValue("defaultconf"));
+            if (defaultConf != null) {
+                this.publicationsDefaultConf = defaultConf.split(",");
+            }
+        }
+
+        private boolean isOtherNamespace(String qName) {
+            return qName.indexOf(':') != -1;
+        }
+
+        private void includeConfStarted(Attributes attributes)
+                throws SAXException, IOException, ParserConfigurationException, ParseException {
+            URL url = relativeUrlResolver.getURL(descriptorURL, substitute(attributes.getValue("file")), substitute(attributes.getValue("url")));
+            if (url == null) {
+                throw new SAXException("include tag must have a file or an url attribute");
+            }
+
+            // create a new temporary parser to read the configurations from
+            // the specified file.
+            Parser parser = newParser(UrlExternalResource.open(url), url);
+            ParserHelper.parse(url , null, parser);
+
+            // add the configurations from this temporary parser to this module descriptor
+            Configuration[] configs = parser.getModuleDescriptor().getConfigurations();
+            for (Configuration config : configs) {
+                getMd().addConfiguration(config);
+            }
+            if (parser.getDefaultConfMapping() != null) {
+                LOGGER.debug("setting default conf mapping from imported configurations file: " + parser.getDefaultConfMapping());
+                setDefaultConfMapping(parser.getDefaultConfMapping());
+            }
+            if (parser.getDefaultConf() != null) {
+                LOGGER.debug("setting default conf from imported configurations file: " + parser.getDefaultConf());
+                setDefaultConf(parser.getDefaultConf());
+            }
+            if (parser.getMd().isMappingOverride()) {
+                LOGGER.debug("enabling mapping-override from imported configurations file");
+                getMd().setMappingOverride(true);
+            }
+        }
+
+        private void confStarted(Attributes attributes) {
+            String conf = substitute(attributes.getValue("name"));
+            switch (state) {
+                case CONF:
+                    Configuration.Visibility visibility = Configuration.Visibility.getVisibility(elvis(substitute(attributes.getValue("visibility")), "public"));
+                    String description = substitute(attributes.getValue("description"));
+                    String[] extend = substitute(attributes.getValue("extends")) == null ? null : substitute(attributes.getValue("extends")).split(",");
+                    String transitiveValue = attributes.getValue("transitive");
+                    boolean transitive = (transitiveValue == null) || Boolean.valueOf(attributes.getValue("transitive"));
+                    String deprecated = attributes.getValue("deprecated");
+                    Configuration configuration = new Configuration(conf, visibility, description, extend, transitive, deprecated);
+                    fillExtraAttributes(configuration, attributes,
+                            new String[]{"name", "visibility", "extends", "transitive", "description", "deprecated"});
+                    getMd().addConfiguration(configuration);
+                    break;
+                case PUB:
+                    if ("*".equals(conf)) {
+                        String[] confs = getMd().getConfigurationsNames();
+                        for (String confName : confs) {
+                            artifact.addConfiguration(confName);
+                        }
+                    } else {
+                        artifact.addConfiguration(conf);
+                    }
+                    break;
+                case DEP:
+                    this.conf = conf;
+                    String mappeds = substitute(attributes.getValue("mapped"));
+                    if (mappeds != null) {
+                        String[] mapped = mappeds.split(",");
+                        for (String depConf : mapped) {
+                            dd.addDependencyConfiguration(conf, depConf.trim());
+                        }
+                    }
+                    break;
+                case DEP_ARTIFACT:
+                case ARTIFACT_INCLUDE:
+                case ARTIFACT_EXCLUDE:
+                    addConfiguration(conf);
+                    break;
+                default:
+                    if (validate) {
+                        addError("conf tag found in invalid tag: " + state);
+                    }
+                    break;
+            }
+        }
+
+        private void dependencyStarted(Attributes attributes) {
+            state = State.DEP;
+            String org = substitute(attributes.getValue("org"));
+            if (org == null) {
+                org = getMd().getModuleRevisionId().getOrganisation();
+            }
+            boolean force = Boolean.valueOf(substitute(attributes.getValue("force")));
+            boolean changing = Boolean.valueOf(substitute(attributes.getValue("changing")));
+
+            String transitiveValue = substitute(attributes.getValue("transitive"));
+            boolean transitive = (transitiveValue == null) ? true : Boolean.valueOf(transitiveValue);
+
+            String name = substitute(attributes.getValue("name"));
+            String branch = substitute(attributes.getValue("branch"));
+            String branchConstraint = substitute(attributes.getValue("branchConstraint"));
+            String rev = substitute(attributes.getValue("rev"));
+            String revConstraint = substitute(attributes.getValue("revConstraint"));
+
+            String[] ignoredAttributeNames = DEPENDENCY_REGULAR_ATTRIBUTES;
+            Map<String, String> extraAttributes = getExtraAttributes(attributes, ignoredAttributeNames);
+
+            ModuleRevisionId revId = createModuleRevisionId(org, name, branch, rev, extraAttributes);
+            ModuleRevisionId dynamicId;
+            if ((revConstraint == null) && (branchConstraint == null)) {
+                // no dynamic constraints defined, so dynamicId equals revId
+                dynamicId = createModuleRevisionId(org, name, branch, rev, extraAttributes, false);
+            } else {
+                if (branchConstraint == null) {
+                    // this situation occurs when there was no branch defined
+                    // in the original dependency descriptor. So the dynamicId
+                    // shouldn't contain a branch neither
+                    dynamicId = createModuleRevisionId(org, name, null, revConstraint, extraAttributes, false);
+                } else {
+                    dynamicId = createModuleRevisionId(org, name, branchConstraint, revConstraint, extraAttributes);
+                }
+            }
+
+            dd = new DefaultDependencyDescriptor(getMd(), revId, dynamicId, force, changing, transitive);
+            getMd().addDependency(dd);
+            String confs = substitute(attributes.getValue("conf"));
+            if (confs != null && confs.length() > 0) {
+                parseDepsConfs(confs, dd);
+            }
+        }
+
+        private void artifactStarted(String qName, Attributes attributes)
+                throws MalformedURLException {
+            if (state == State.PUB) {
+                // this is a published artifact
+                String artName = elvis(substitute(attributes.getValue("name")), getMd().getModuleRevisionId().getName());
+                String type = elvis(substitute(attributes.getValue("type")), "jar");
+                String ext = elvis(substitute(attributes.getValue("ext")), type);
+                Map<String, String> extraAttributes = getExtraAttributes(attributes, new String[]{"ext", "type", "name", "conf"});
+                artifact = new BuildableIvyArtifact(artName, type, ext, extraAttributes);
+                String confs = substitute(attributes.getValue("conf"));
+                
+                // Only add confs if they are specified. if they aren't, endElement will handle this only if there are no conf defined in sub elements
+                if (confs != null && confs.length() > 0) {
+                    String[] conf;
+                    if ("*".equals(confs)) {
+                        conf = getMd().getConfigurationsNames();
+                    } else {
+                        conf = confs.split(",");
+                    }
+                    for (String confName : conf) {
+                        artifact.addConfiguration(confName.trim());
+                    }
+                }
+            } else if (state == State.DEP) {
+                // this is an artifact asked for a particular dependency
+                addDependencyArtifacts(qName, attributes);
+            } else if (validate) {
+                addError("artifact tag found in invalid tag: " + state);
+            }
+        }
+
+        private void dependenciesStarted(Attributes attributes) {
+            state = State.DEPS;
+            String defaultConf = substitute(attributes.getValue("defaultconf"));
+            if (defaultConf != null) {
+                setDefaultConf(defaultConf);
+            }
+            String defaultConfMapping = substitute(attributes.getValue("defaultconfmapping"));
+            if (defaultConfMapping != null) {
+                setDefaultConfMapping(defaultConfMapping);
+            }
+            String confMappingOverride = substitute(attributes.getValue("confmappingoverride"));
+            if (confMappingOverride != null) {
+                getMd().setMappingOverride(Boolean.valueOf(confMappingOverride));
+            }
+            checkConfigurations();
+        }
+
+        private void configurationStarted(Attributes attributes) {
+            state = State.CONF;
+            setDefaultConfMapping(substitute(attributes.getValue("defaultconfmapping")));
+            setDefaultConf(substitute(attributes.getValue("defaultconf")));
+            getMd().setMappingOverride(Boolean.valueOf(substitute(attributes.getValue("confmappingoverride"))));
+        }
+
+        private void infoStarted(Attributes attributes) {
+            state = State.INFO;
+            String org = substitute(attributes.getValue("organisation"));
+            String module = substitute(attributes.getValue("module"));
+            String revision = substitute(attributes.getValue("revision"));
+            String branch = substitute(attributes.getValue("branch"));
+            Map<String, String> extraAttributes = getExtraAttributes(attributes, new String[]{"organisation", "module", "revision", "status", "publication", "branch", "namespace", "default", "resolver"});
+            getMd().setModuleRevisionId(createModuleRevisionId(org, module, branch, revision, extraAttributes));
+
+            getMd().setStatus(elvis(substitute(attributes.getValue("status")), "integration"));
+            getMd().setDefault(Boolean.valueOf(substitute(attributes.getValue("default"))));
+            String pubDate = substitute(attributes.getValue("publication"));
+            if (pubDate != null && pubDate.length() > 0) {
+                try {
+                    final SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_FORMAT_PATTERN);
+                    getMd().setPublicationDate(ivyDateFormat.parse(pubDate));
+                } catch (ParseException e) {
+                    addError("invalid publication date format: " + pubDate);
+                }
+            }
+        }
+
+        private void ivyModuleStarted(Attributes attributes) throws SAXException {
+            descriptorVersion = attributes.getValue("version");
+            int versionIndex = ALLOWED_VERSIONS.indexOf(descriptorVersion);
+            if (versionIndex == -1) {
+                addError("invalid version " + descriptorVersion);
+                throw new SAXException("invalid version " + descriptorVersion);
+            }
+            if (versionIndex >= ALLOWED_VERSIONS.indexOf("1.3")) {
+                LOGGER.debug("post 1.3 ivy file: using " + PatternMatcher.EXACT + " as default matcher");
+                defaultMatcher = getMatcher(PatternMatcher.EXACT);
+            } else {
+                LOGGER.debug("pre 1.3 ivy file: using " + PatternMatcher.EXACT_OR_REGEXP + " as default matcher");
+                defaultMatcher = getMatcher(PatternMatcher.EXACT_OR_REGEXP);
+            }
+
+            for (int i = 0; i < attributes.getLength(); i++) {
+                if (attributes.getQName(i).startsWith("xmlns:")) {
+                    getMd().addExtraAttributeNamespace(attributes.getQName(i).substring("xmlns:".length()), attributes.getValue(i));
+                }
+            }
+        }
+
+        private void descriptionStarted(String qName, Attributes attributes) {
+            buffer.append("<").append(qName);
+            for (int i = 0; i < attributes.getLength(); i++) {
+                buffer.append(" ");
+                buffer.append(attributes.getQName(i));
+                buffer.append("=\"");
+                buffer.append(attributes.getValue(i));
+                buffer.append("\"");
+            }
+            buffer.append(">");
+        }
+
+        private void addDependencyArtifacts(String tag, Attributes attributes)
+                throws MalformedURLException {
+            state = State.DEP_ARTIFACT;
+            parseRule(tag, attributes);
+        }
+
+        private void addIncludeRule(String tag, Attributes attributes)
+                throws MalformedURLException {
+            state = State.ARTIFACT_INCLUDE;
+            parseRule(tag, attributes);
+        }
+
+        private void addExcludeRule(String tag, Attributes attributes)
+                throws MalformedURLException {
+            state = State.ARTIFACT_EXCLUDE;
+            parseRule(tag, attributes);
+        }
+
+        private void parseRule(String tag, Attributes attributes) throws MalformedURLException {
+            String name = substitute(attributes.getValue("name"));
+            if (name == null) {
+                name = substitute(attributes.getValue("artifact"));
+                if (name == null) {
+                    name = "artifact".equals(tag) ? dd.getDependencyId().getName()
+                            : PatternMatcher.ANY_EXPRESSION;
+                }
+            }
+            String type = substitute(attributes.getValue("type"));
+            if (type == null) {
+                type = "artifact".equals(tag) ? "jar" : PatternMatcher.ANY_EXPRESSION;
+            }
+            String ext = substitute(attributes.getValue("ext"));
+            ext = ext != null ? ext : type;
+            if (state == State.DEP_ARTIFACT) {
+                String url = substitute(attributes.getValue("url"));
+                Map<String, String> extraAttributes = getExtraAttributes(attributes, new String[]{"name", "type", "ext", "url", "conf"});
+                confAware = new DefaultDependencyArtifactDescriptor(dd, name, type, ext, url == null ? null : new URL(url), extraAttributes);
+            } else if (state == State.ARTIFACT_INCLUDE) {
+                PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
+                String org = elvis(substitute(attributes.getValue("org")), PatternMatcher.ANY_EXPRESSION);
+                String module = elvis(substitute(attributes.getValue("module")), PatternMatcher.ANY_EXPRESSION);
+                ArtifactId aid = new ArtifactId(IvyUtil.createModuleId(org, module), name, type, ext);
+                Map<String, String> extraAttributes = getExtraAttributes(attributes, new String[]{"org", "module", "name", "type", "ext", "matcher", "conf"});
+                confAware = new DefaultIncludeRule(aid, matcher, extraAttributes);
+            } else { // _state == ARTIFACT_EXCLUDE || EXCLUDE
+                PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
+                String org = elvis(substitute(attributes.getValue("org")), PatternMatcher.ANY_EXPRESSION);
+                String module = elvis(substitute(attributes.getValue("module")), PatternMatcher.ANY_EXPRESSION);
+                ArtifactId aid = new ArtifactId(IvyUtil.createModuleId(org, module), name, type, ext);
+                Map<String, String> extraAttributes = getExtraAttributes(attributes, new String[]{"org", "module", "name", "type", "ext", "matcher", "conf"});
+                confAware = new DefaultExcludeRule(aid, matcher, extraAttributes);
+            }
+            String confs = substitute(attributes.getValue("conf"));
+            // only add confs if they are specified. if they aren't, endElement will handle this
+            // only if there are no conf defined in sub elements
+            if (confs != null && confs.length() > 0) {
+                String[] conf;
+                if ("*".equals(confs)) {
+                    conf = getMd().getConfigurationsNames();
+                } else {
+                    conf = confs.split(",");
+                }
+                for (String confName : conf) {
+                    addConfiguration(confName.trim());
+                }
+            }
+        }
+
+        private void addConfiguration(String c) {
+            confAware.addConfiguration(c);
+            if (state != State.EXCLUDE) {
+                // we are currently adding a configuration to either an include, exclude or artifact
+                // element
+                // of a dependency. This means that we have to add this element to the corresponding
+                // conf
+                // of the current dependency descriptor
+                if (confAware instanceof DependencyArtifactDescriptor) {
+                    dd.addDependencyArtifact(c, (DependencyArtifactDescriptor) confAware);
+                } else if (confAware instanceof IncludeRule) {
+                    dd.addIncludeRule(c, (IncludeRule) confAware);
+                } else if (confAware instanceof ExcludeRule) {
+                    dd.addExcludeRule(c, (ExcludeRule) confAware);
+                }
+            }
+        }
+
+        private PatternMatcher getPatternMatcher(String m) {
+            String matcherName = substitute(m);
+            PatternMatcher matcher = matcherName == null ? defaultMatcher : getMatcher(matcherName);
+            if (matcher == null) {
+                throw new IllegalArgumentException("unknown matcher " + matcherName);
+            }
+            return matcher;
+        }
+
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            if (buffer != null) {
+                buffer.append(ch, start, length);
+            }
+        }
+
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            if (state == State.PUB && "artifact".equals(qName)) {
+                if (artifact.getConfigurations().isEmpty()) {
+                    String[] confs = publicationsDefaultConf == null ? getMd().getConfigurationsNames() : publicationsDefaultConf;
+                    for (String confName : confs) {
+                        artifact.addConfiguration(confName.trim());
+                    }
+                }
+                metaData.addArtifact(artifact.getArtifact(), artifact.getConfigurations());
+                artifact = null;
+            } else if ("configurations".equals(qName)) {
+                checkConfigurations();
+            } else if ((state == State.DEP_ARTIFACT && "artifact".equals(qName))
+                    || (state == State.ARTIFACT_INCLUDE && "include".equals(qName))
+                    || (state == State.ARTIFACT_EXCLUDE && "exclude".equals(qName))) {
+                state = State.DEP;
+                if (confAware.getConfigurations().length == 0) {
+                    String[] confs = getMd().getConfigurationsNames();
+                    for (String confName : confs) {
+                        addConfiguration(confName);
+                    }
+                }
+                confAware = null;
+            } else if ("exclude".equals(qName) && state == State.EXCLUDE) {
+                if (confAware.getConfigurations().length == 0) {
+                    String[] confs = getMd().getConfigurationsNames();
+                    for (String confName : confs) {
+                        addConfiguration(confName);
+                    }
+                }
+                confAware = null;
+                state = State.DEPS;
+            } else if ("dependency".equals(qName) && state == State.DEP) {
+                if (dd.getModuleConfigurations().length == 0) {
+                    parseDepsConfs(getDefaultConf(), dd);
+                }
+                state = State.DEPS;
+            } else if ("dependencies".equals(qName) && state == State.DEPS) {
+                state = State.NONE;
+            } else if (state == State.INFO && "info".equals(qName)) {
+                metaData = new BuildableIvyModuleResolveMetaData(getMd());
+                state = State.NONE;
+            } else if (state == State.DESCRIPTION && "description".equals(qName)) {
+                getMd().setDescription(buffer == null ? "" : buffer.toString().trim());
+                buffer = null;
+                state = State.INFO;
+            } else if (state == State.EXTRA_INFO) {
+                getMd().getExtraInfo().put(new NamespaceId(uri, localName), buffer == null ? "" : buffer.toString());
+                buffer = null;
+                state = State.INFO;
+            } else if (state == State.DESCRIPTION) {
+                if (buffer.toString().endsWith("<" + qName + ">")) {
+                    buffer.deleteCharAt(buffer.length() - 1);
+                    buffer.append("/>");
+                } else {
+                    buffer.append("</").append(qName).append(">");
+                }
+            }
+        }
+
+        private void checkConfigurations() {
+            if (getMd().getConfigurations().length == 0) {
+                getMd().addConfiguration(new Configuration("default"));
+            }
+        }
+
+        private void replaceConfigurationWildcards() {
+            Configuration[] configs = getMd().getConfigurations();
+            for (Configuration config : configs) {
+                config.replaceWildcards(getMd());
+            }
+        }
+
+        private URL getSchemaURL() {
+            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) {
+            return value != null ? value : defaultValue;
+        }
+
+        private String substitute(String value) {
+            return IvyPatternHelper.substituteVariables(value, properties);
+        }
+
+        private Map<String, String> getExtraAttributes(Attributes attributes, String[] ignoredAttributeNames) {
+            Map<String, String> ret = new HashMap<String, String>();
+            Collection ignored = Arrays.asList(ignoredAttributeNames);
+            for (int i = 0; i < attributes.getLength(); i++) {
+                if (!ignored.contains(attributes.getQName(i))) {
+                    ret.put(attributes.getQName(i), substitute(attributes.getValue(i)));
+                }
+            }
+            return ret;
+        }
+
+        private void fillExtraAttributes(DefaultExtendableItem item, Attributes attributes, String[] ignoredAttNames) {
+            Map<String, String> extraAttributes = getExtraAttributes(attributes, ignoredAttNames);
+            for (String name : extraAttributes.keySet()) {
+                item.setExtraAttribute(name, extraAttributes.get(name));
+            }
+        }
+
+        private PatternMatcher getMatcher(String matcherName) {
+            return resolverStrategy.getPatternMatcher(matcherName);
+        }
+    }
+
+    public static class ParserHelper {
+        static final String JAXP_SCHEMA_LANGUAGE
+                = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+        static final String JAXP_SCHEMA_SOURCE
+                = "http://java.sun.com/xml/jaxp/properties/schemaSource";
+
+        static final String XML_NAMESPACE_PREFIXES
+                = "http://xml.org/sax/features/namespace-prefixes";
+
+        static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
+
+        private static SAXParser newSAXParser(URL schema, InputStream schemaStream)
+                throws ParserConfigurationException, SAXException {
+            if (schema == null) {
+                SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+                parserFactory.setValidating(false);
+                parserFactory.setNamespaceAware(true);
+                SAXParser parser = parserFactory.newSAXParser();
+                parser.getXMLReader().setFeature(XML_NAMESPACE_PREFIXES, true);
+                return parser;
+            } else {
+                SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+                parserFactory.setValidating(true);
+                parserFactory.setNamespaceAware(true);
+
+                SAXParser parser = parserFactory.newSAXParser();
+                parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
+                parser.setProperty(JAXP_SCHEMA_SOURCE, schemaStream);
+                parser.getXMLReader().setFeature(XML_NAMESPACE_PREFIXES, true);
+                return parser;
+            }
+        }
+
+        public static void parse(
+                URL xmlURL, URL schema, DefaultHandler handler)
+                throws SAXException, IOException, ParserConfigurationException {
+            InputStream xmlStream = URLHandlerRegistry.getDefault().openStream(xmlURL);
+            try {
+                InputSource inSrc = new InputSource(xmlStream);
+                inSrc.setSystemId(xmlURL.toExternalForm());
+                parse(inSrc, schema, handler);
+            } finally {
+                try {
+                    xmlStream.close();
+                } catch (IOException e) {
+                    // ignored
+                }
+            }
+        }
+
+        public static void parse(
+                InputSource xmlStream, URL schema, DefaultHandler handler)
+                throws SAXException, IOException, ParserConfigurationException {
+            InputStream schemaStream = null;
+            try {
+                if (schema != null) {
+                    schemaStream = URLHandlerRegistry.getDefault().openStream(schema);
+                }
+                SAXParser parser = newSAXParser(schema, schemaStream);
+                parser.parse(xmlStream, handler);
+            } finally {
+                if (schemaStream != null) {
+                    try {
+                        schemaStream.close();
+                    } catch (IOException ex) {
+                        // ignored
+                    }
+                }
+            }
+        }
+    }
+
+    public String toString() {
+        return "ivy parser";
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParseException.java
new file mode 100644
index 0000000..3dbdd98
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.internal.exceptions.Contextual;
+import org.gradle.internal.resource.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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.java
new file mode 100644
index 0000000..a92e085
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/MetaDataParser.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.parser;
+
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+import java.io.File;
+
+public interface MetaDataParser<T extends MutableModuleComponentResolveMetaData> {
+    T parseMetaData(DescriptorParseContext context, LocallyAvailableExternalResource resource) throws MetaDataParseException;
+
+    T parseMetaData(DescriptorParseContext ivySettings, File descriptorFile) throws MetaDataParseException;
+
+    T parseMetaData(DescriptorParseContext ivySettings, File descriptorFile, boolean validate) throws MetaDataParseException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomDomParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomDomParser.java
new file mode 100644
index 0000000..622b04e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomDomParser.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.*;
+import java.util.LinkedList;
+import java.util.List;
+
+public final class PomDomParser {
+    private PomDomParser() {}
+
+    public 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();
+    }
+
+    public static String getFirstChildText(Element parentElem, String name) {
+        Element node = getFirstChildElement(parentElem, name);
+        if (node != null) {
+            return getTextContent(node);
+        } else {
+            return null;
+        }
+    }
+
+    public 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;
+    }
+
+    public 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;
+    }
+
+    public 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();
+
+        public 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/parser/PomParent.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomParent.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomParent.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomParent.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java
new file mode 100644
index 0000000..21b2510
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReader.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.XMLHelper;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomProfile;
+import org.gradle.internal.resource.local.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.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+import static org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomDomParser.*;
+
+/**
+ * Copied from org.apache.ivy.plugins.parser.m2.PomReader.
+ */
+public class PomReader implements PomParent {
+
+    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 TYPE = "type";
+    private static final String PROFILES = "profiles";
+    private static final String PROFILE = "profile";
+    private static final String PROFILE_ID = "id";
+    private static final String PROFILE_ACTIVATION = "activation";
+    private static final String PROFILE_ACTIVATION_ACTIVE_BY_DEFAULT = "activeByDefault";
+    private static final String PROFILE_ACTIVATION_PROPERTY = "property";
+
+    private PomParent pomParent = new RootPomParent();
+    private final Map<String, String> properties = new HashMap<String, String>();
+    private List<PomDependencyMgt> declaredDependencyMgts;
+    private List<PomProfile> declaredActivePomProfiles;
+    private Map<MavenDependencyKey, PomDependencyMgt> resolvedDependencyMgts;
+    private final Map<MavenDependencyKey, PomDependencyMgt> importedDependencyMgts = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
+    private Map<MavenDependencyKey, PomDependencyData> resolvedDependencies;
+
+    private final Element projectElement;
+    private final Element parentElement;
+
+    public PomReader(final LocallyAvailableExternalResource resource) throws 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);
+
+        setDefaultParentGavProperties();
+        setPomProperties();
+        setActiveProfileProperties();
+    }
+
+    public void setPomParent(PomParent pomParent) {
+        this.pomParent = pomParent;
+        setPomParentProperties();
+    }
+
+    private void setPomParentProperties() {
+        Map<String, String> parentPomProps = pomParent.getProperties();
+
+        for(Map.Entry<String, String> entry : parentPomProps.entrySet()) {
+            setProperty(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private void setPomProperties() {
+        for(Map.Entry<String, String> pomProperty : getPomProperties().entrySet()) {
+            setProperty(pomProperty.getKey(), pomProperty.getValue());
+        }
+    }
+
+    /**
+     * Sets properties for all active profiles. Properties from an active profile override existing POM properties.
+     */
+    private void setActiveProfileProperties() {
+        for(PomProfile activePomProfile : parseActivePomProfiles()) {
+            for(Map.Entry<String, String> property : activePomProfile.getProperties().entrySet()) {
+                properties.put(property.getKey(), property.getValue());
+            }
+        }
+    }
+
+    private void setDefaultParentGavProperties() {
+        setGavPropertyValueWithoutReplacement(GavProperty.PARENT_GROUP_ID, getParentGroupId());
+        setGavPropertyValueWithoutReplacement(GavProperty.PARENT_VERSION, getParentVersion());
+    }
+
+    private void setGavPropertyValueWithoutReplacement(GavProperty gavProperty, String propertyValue) {
+        for(String name : gavProperty.getNames()) {
+            setProperty(name, propertyValue);
+        }
+    }
+
+    private enum GavProperty {
+        PARENT_VERSION("parent.version", "project.parent.version"),
+        PARENT_GROUP_ID("parent.groupId", "project.parent.groupId"),
+        GROUP_ID("project.groupId", "pom.groupId", "groupId"),
+        ARTIFACT_ID("project.artifactId", "pom.artifactId", "artifactId"),
+        VERSION("project.version", "pom.version", "version");
+
+        private final String[] names;
+
+        private GavProperty(String... names) {
+            this.names = names;
+        }
+
+        public String[] getNames() {
+            return names;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return projectElement.getOwnerDocument().getDocumentURI();
+    }
+
+    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 guarantee 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 Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void addImportedDependencyMgts(Map<MavenDependencyKey, PomDependencyMgt> inherited) {
+        if (resolvedDependencyMgts != null) {
+            throw new IllegalStateException("Cannot add imported dependency management elements after dependency management elements have been resolved for this POM.");
+        }
+        importedDependencyMgts.putAll(inherited);
+    }
+
+    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 replaceProps(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 List<License> getLicenses() {
+        Element licenses = getFirstChildElement(projectElement, LICENSES);
+        if (licenses == null) {
+            return Collections.emptyList();
+        }
+        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;
+    }
+
+    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 IvyUtil.createModuleRevisionId(relocGroupId, relocArtId, relocVersion);
+        }
+    }
+
+    /**
+     * Returns all dependencies for this POM, including those inherited from parent POMs.
+     */
+    public Map<MavenDependencyKey, PomDependencyData> getDependencies() {
+        if (resolvedDependencies == null) {
+            resolvedDependencies = resolveDependencies();
+        }
+        return resolvedDependencies;
+    }
+
+    private Map<MavenDependencyKey, PomDependencyData> resolveDependencies() {
+        Map<MavenDependencyKey, PomDependencyData> dependencies = new LinkedHashMap<MavenDependencyKey, PomDependencyData>();
+
+        for(PomDependencyData dependency : getDependencyData(projectElement)) {
+            dependencies.put(dependency.getId(), dependency);
+        }
+
+        // Maven adds inherited dependencies last
+        for (Map.Entry<MavenDependencyKey, PomDependencyData> entry : pomParent.getDependencies().entrySet()) {
+            if (!dependencies.containsKey(entry.getKey())) {
+                dependencies.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        for(PomProfile pomProfile : parseActivePomProfiles()) {
+            for(PomDependencyData dependency : pomProfile.getDependencies()) {
+                dependencies.put(dependency.getId(), dependency);
+            }
+        }
+
+        return dependencies;
+    }
+
+    private List<PomDependencyData> getDependencyData(Element parentElement) {
+        List<PomDependencyData> depElements = new ArrayList<PomDependencyData>();
+        Element dependenciesElement = getFirstChildElement(parentElement, DEPENDENCIES);
+        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())) {
+                    depElements.add(new PomDependencyData((Element) node));
+                }
+            }
+        }
+
+        return depElements;
+    }
+
+    /**
+     * Returns all dependency management elements for this POM, including those inherited from parent and imported POMs.
+     */
+    public Map<MavenDependencyKey, PomDependencyMgt> getDependencyMgt() {
+        if(resolvedDependencyMgts == null) {
+            resolvedDependencyMgts = resolveDependencyMgt();
+        }
+        return resolvedDependencyMgts;
+    }
+
+    private Map<MavenDependencyKey, PomDependencyMgt> resolveDependencyMgt() {
+        Map<MavenDependencyKey, PomDependencyMgt> dependencies = new LinkedHashMap<MavenDependencyKey, PomDependencyMgt>();
+        dependencies.putAll(pomParent.getDependencyMgt());
+        dependencies.putAll(importedDependencyMgts);
+        for(PomDependencyMgt dependencyMgt : parseDependencyMgt()) {
+            dependencies.put(dependencyMgt.getId(), dependencyMgt);
+        }
+        return dependencies;
+    }
+
+    /**
+     * Parses the dependency management elements declared in this POM without removing the duplicates.
+     *
+     * @return Parsed dependency management elements
+     */
+    public List<PomDependencyMgt> parseDependencyMgt() {
+        if(declaredDependencyMgts == null) {
+            List<PomDependencyMgt> dependencyMgts = getDependencyMgt(projectElement);
+
+            for(PomProfile pomProfile : parseActivePomProfiles()) {
+                for(PomDependencyMgt dependencyMgt : pomProfile.getDependencyMgts()) {
+                    dependencyMgts.add(dependencyMgt);
+                }
+            }
+
+            declaredDependencyMgts = dependencyMgts;
+        }
+
+        return declaredDependencyMgts;
+    }
+
+    private List<PomDependencyMgt> getDependencyMgt(Element parentElement) {
+        List<PomDependencyMgt> depMgmtElements = new ArrayList<PomDependencyMgt>();
+        Element dependenciesElement = getFirstChildElement(parentElement, DEPENDENCY_MGT);
+        dependenciesElement = getFirstChildElement(dependenciesElement, DEPENDENCIES);
+
+        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())) {
+                    depMgmtElements.add(new PomDependencyMgtElement((Element) node));
+                }
+            }
+        }
+
+        return depMgmtElements;
+    }
+
+    public PomDependencyMgt findDependencyDefaults(MavenDependencyKey dependencyKey) {
+        return getDependencyMgt().get(dependencyKey);
+    }
+
+    public void resolveGAV() {
+        setGavPropertyValue(GavProperty.GROUP_ID, getGroupId());
+        setGavPropertyValue(GavProperty.ARTIFACT_ID, getArtifactId());
+        setGavPropertyValue(GavProperty.VERSION, getVersion());
+    }
+
+    private void setGavPropertyValue(GavProperty gavProperty, String propertyValue) {
+        for(String name : gavProperty.getNames()) {
+            properties.put(name, propertyValue);
+        }
+    }
+
+    public class PomDependencyMgtElement implements PomDependencyMgt {
+        private final Element depElement;
+
+        PomDependencyMgtElement(Element depElement) {
+            this.depElement = depElement;
+        }
+
+        public MavenDependencyKey getId() {
+            return new MavenDependencyKey(getGroupId(), getArtifactId(), getType(), getClassifier());
+        }
+
+        /* (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 String getType() {
+            String val = getFirstChildText(depElement , TYPE);
+            val = replaceProps(val);
+
+            if(val == null) {
+                val = "jar";
+            }
+
+            return val;
+        }
+
+        public String getClassifier() {
+            String val = getFirstChildText(depElement , CLASSIFIER);
+            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(IvyUtil.createModuleId(groupId, artifactId));
+                        }
+                    }
+                }
+            }
+            return exclusions;
+        }
+    }
+
+    public class PomDependencyData extends PomDependencyMgtElement {
+        private final Element depElement;
+        PomDependencyData(Element depElement) {
+            super(depElement);
+            this.depElement = depElement;
+        }
+
+        public boolean isOptional() {
+            Element e = getFirstChildElement(depElement, OPTIONAL);
+            return (e != null) && "true".equalsIgnoreCase(getTextContent(e));
+        }
+    }
+
+    public class PomProfileElement implements PomProfile {
+        private final Element element;
+        private List<PomDependencyMgt> declaredDependencyMgts;
+        private List<PomDependencyData> declaredDependencies;
+
+        PomProfileElement(Element element) {
+            this.element = element;
+        }
+
+        public String getId() {
+            return getFirstChildText(element, PROFILE_ID);
+        }
+
+        public Map<String, String> getProperties() {
+            return getPomProperties(element);
+        }
+
+        public List<PomDependencyMgt> getDependencyMgts() {
+            if(declaredDependencyMgts == null) {
+                declaredDependencyMgts = getDependencyMgt(element);
+            }
+
+            return declaredDependencyMgts;
+        }
+
+        public List<PomDependencyData> getDependencies() {
+            if(declaredDependencies == null) {
+                declaredDependencies = getDependencyData(element);
+            }
+
+            return declaredDependencies;
+        }
+    }
+
+    /**
+     * Parses all active profiles that can be found in POM.
+     *
+     * @return Active POM profiles
+     */
+    private List<PomProfile> parseActivePomProfiles() {
+        if(declaredActivePomProfiles == null) {
+            List<PomProfile> activeByDefaultPomProfiles = new ArrayList<PomProfile>();
+            List<PomProfile> activeByAbsenceOfPropertyPomProfiles = new ArrayList<PomProfile>();
+            Element profilesElement = getFirstChildElement(projectElement, PROFILES);
+
+            if(profilesElement != null) {
+                for(Element profileElement : getAllChilds(profilesElement)) {
+                    if(PROFILE.equals(profileElement.getNodeName())) {
+                        Element activationElement = getFirstChildElement(profileElement, PROFILE_ACTIVATION);
+
+                        if(activationElement != null) {
+                            String activeByDefault = getFirstChildText(activationElement, PROFILE_ACTIVATION_ACTIVE_BY_DEFAULT);
+
+                            if(activeByDefault != null && "true".equals(activeByDefault)) {
+                                activeByDefaultPomProfiles.add(new PomProfileElement(profileElement));
+                            } else {
+                                Element propertyElement = getFirstChildElement(activationElement, PROFILE_ACTIVATION_PROPERTY);
+
+                                if(propertyElement != null) {
+                                    if(isActivationPropertyActivated(propertyElement)) {
+                                        activeByAbsenceOfPropertyPomProfiles.add(new PomProfileElement(profileElement));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            declaredActivePomProfiles = determineActiveProfiles(activeByDefaultPomProfiles, activeByAbsenceOfPropertyPomProfiles);
+        }
+
+        return declaredActivePomProfiles;
+    }
+
+    /**
+     * If a profile is identified as active through any other activation method than activeByDefault, none of the existing
+     * profiles marked as activeByDefault apply.
+     *
+     * @param activeByDefaultPomProfiles Parsed profiles that are active by default
+     * @param activeByAbsenceOfPropertyPomProfiles Parsed profiles that are activated by absence of property
+     * @return List of active profiles that are not activeByDefault
+     */
+    private List<PomProfile> determineActiveProfiles(List<PomProfile> activeByDefaultPomProfiles, List<PomProfile> activeByAbsenceOfPropertyPomProfiles) {
+        return !activeByAbsenceOfPropertyPomProfiles.isEmpty() ? activeByAbsenceOfPropertyPomProfiles : activeByDefaultPomProfiles;
+    }
+
+    /**
+     * Checks if activation property is active through absence of system property.
+     *
+     * @param propertyElement Property element
+     * @return Activation indicator
+     * @see <a href="http://books.sonatype.com/mvnref-book/reference/profiles-sect-activation.html#profiles-sect-activation-config">Maven documentation</a>
+     */
+    private boolean isActivationPropertyActivated(Element propertyElement) {
+        String propertyName = getFirstChildText(propertyElement, "name");
+        return propertyName.startsWith("!");
+    }
+
+    /**
+     * @return the content of the properties tag into the pom.
+     */
+    public Map<String, String> getPomProperties() {
+        return getPomProperties(projectElement);
+    }
+
+    private Map<String, String> getPomProperties(Element parentElement) {
+        Map<String, String> pomProperties = new HashMap<String, String>();
+        Element propsEl = getFirstChildElement(parentElement, 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();
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/RootPomParent.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/RootPomParent.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/RootPomParent.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/RootPomParent.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/UnresolvedDependencyVersionException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/UnresolvedDependencyVersionException.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/UnresolvedDependencyVersionException.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/UnresolvedDependencyVersionException.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKey.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKey.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKey.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKey.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomDependencyMgt.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomDependencyMgt.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomDependencyMgt.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomDependencyMgt.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomProfile.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomProfile.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomProfile.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/PomProfile.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelector.java
new file mode 100644
index 0000000..d69bc56
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelector.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentMetadata;
+
+public abstract class AbstractVersionSelector implements VersionSelector {
+    private final String selector;
+
+    protected AbstractVersionSelector(String selector) {
+        this.selector = selector;
+    }
+
+    protected String getSelector() {
+        return selector;
+    }
+
+    public boolean accept(ComponentMetadata candidate) {
+        return accept(candidate.getId().getVersion());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparator.java
new file mode 100644
index 0000000..7dfd890
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparator.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.Versioned;
+
+import java.util.Comparator;
+
+public class DefaultVersionComparator implements VersionComparator {
+    private final Comparator<Version> baseComparator = new StaticVersionComparator();
+    private final VersionParser versionParser = new VersionParser();
+    private final Comparator<String> stringComparator = new Comparator<String>() {
+        @Override
+        public int compare(String string1, String string2) {
+            return baseComparator.compare(versionParser.transform(string1), versionParser.transform(string2));
+        }
+    };
+
+    public int compare(Versioned element1, Versioned element2) {
+        String version1 = element1.getVersion();
+        String version2 = element2.getVersion();
+        return stringComparator.compare(version1, version2);
+    }
+
+    @Override
+    public Comparator<Version> asVersionComparator() {
+        return baseComparator;
+    }
+
+    @Override
+    public Comparator<String> asStringComparator() {
+        return stringComparator;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorScheme.java
new file mode 100644
index 0000000..0e74e65
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorScheme.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+public class DefaultVersionSelectorScheme implements VersionSelectorScheme {
+    private final VersionComparator versionComparator;
+
+    public DefaultVersionSelectorScheme(VersionComparator versionComparator) {
+        this.versionComparator = versionComparator;
+    }
+
+    public VersionSelector parseSelector(String selectorString) {
+        if (VersionRangeSelector.ALL_RANGE.matcher(selectorString).matches()) {
+            return new VersionRangeSelector(selectorString, versionComparator.asStringComparator());
+        }
+
+        if (selectorString.endsWith("+")) {
+            return new SubVersionSelector(selectorString);
+        }
+
+        if (selectorString.startsWith("latest.")) {
+            return new LatestVersionSelector(selectorString);
+        }
+
+        return new ExactVersionSelector(selectorString);
+    }
+
+    public String renderSelector(VersionSelector selector) {
+        return ((AbstractVersionSelector) selector).getSelector();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.java
new file mode 100644
index 0000000..eb390dc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelector.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.ivyservice.ivyresolve.strategy;
+
+/**
+ * Version matcher for "static" version selectors (1.0, 1.2.3, etc.).
+ */
+public class ExactVersionSelector extends AbstractVersionSelector {
+    public ExactVersionSelector(String selector) {
+        super(selector);
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+    public boolean requiresMetadata() {
+        return false;
+    }
+
+    public boolean matchesUniqueVersion() {
+        return true;
+    }
+
+    public boolean accept(String candidate) {
+        return getSelector().equals(candidate);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelector.java
new file mode 100644
index 0000000..b4e3bfc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelector.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.artifacts.ivyservice.ivyresolve.strategy;
+
+import org.gradle.api.artifacts.ComponentMetadata;
+
+public class LatestVersionSelector extends AbstractVersionSelector {
+    private final String selectorStatus;
+
+    public LatestVersionSelector(String selector) {
+        super(selector);
+        selectorStatus = selector.substring("latest.".length());
+    }
+
+    public boolean isDynamic() {
+        return true;
+    }
+
+    public boolean requiresMetadata() {
+        return true;
+    }
+
+    public boolean matchesUniqueVersion() {
+        return true;
+    }
+
+    public boolean accept(String candidate) {
+        throw new UnsupportedOperationException("accept(String, String)");
+    }
+
+    public boolean accept(ComponentMetadata candidate) {
+        int selectorStatusIndex = candidate.getStatusScheme().indexOf(selectorStatus);
+        int candidateStatusIndex = candidate.getStatusScheme().indexOf(candidate.getStatus());
+        return selectorStatusIndex >=0 && selectorStatusIndex <= candidateStatusIndex;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorScheme.java
new file mode 100644
index 0000000..febbdc9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorScheme.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+public class MavenVersionSelectorScheme implements VersionSelectorScheme {
+
+    public static final String LATEST = "LATEST";
+    public static final String RELEASE = "RELEASE";
+    private static final String LATEST_INTEGRATION = "latest.integration";
+    private static final String LATEST_RELEASE = "latest.release";
+
+    private final VersionSelectorScheme defaultVersionSelectorScheme;
+
+    public MavenVersionSelectorScheme(VersionSelectorScheme defaultVersionSelectorScheme) {
+        this.defaultVersionSelectorScheme = defaultVersionSelectorScheme;
+    }
+
+    public VersionSelector parseSelector(String selectorString) {
+        if (selectorString.equals(RELEASE)) {
+            return new LatestVersionSelector(LATEST_RELEASE);
+        } else if (selectorString.equals(LATEST)) {
+            return new LatestVersionSelector(LATEST_INTEGRATION);
+        } else {
+            return defaultVersionSelectorScheme.parseSelector(selectorString);
+        }
+    }
+
+    public String renderSelector(VersionSelector selector) {
+        return toMavenSyntax(defaultVersionSelectorScheme.renderSelector(selector));
+    }
+
+    // TODO:DAZ VersionSelector should be more descriptive, so it can be directly translated
+    private String toMavenSyntax(String version) {
+        if (version.equals(LATEST_INTEGRATION)) {
+            return LATEST;
+        }
+        if (version.equals(LATEST_RELEASE)) {
+            return RELEASE;
+        }
+        if (version.startsWith("]")) {
+            version = '(' + version.substring(1);
+        }
+        if (version.endsWith("[")) {
+            version = version.substring(0, version.length() - 1) + ')';
+        }
+        return version;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.java
new file mode 100644
index 0000000..b0e4a8d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ResolverStrategy.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.ivyservice.ivyresolve.strategy;
+
+import com.google.common.collect.Maps;
+import org.apache.ivy.plugins.matcher.*;
+
+import java.util.Map;
+
+public class ResolverStrategy {
+    private final Map<String, PatternMatcher> matchers = Maps.newHashMap();
+
+    public ResolverStrategy() {
+        addMatcher(ExactPatternMatcher.INSTANCE);
+        addMatcher(RegexpPatternMatcher.INSTANCE);
+        addMatcher(ExactOrRegexpPatternMatcher.INSTANCE);
+        addMatcher(GlobPatternMatcher.INSTANCE);
+    }
+
+    private void addMatcher(PatternMatcher instance) {
+        matchers.put(instance.getName(), instance);
+    }
+
+    public PatternMatcher getPatternMatcher(String name) {
+        return matchers.get(name);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/StaticVersionComparator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/StaticVersionComparator.java
new file mode 100644
index 0000000..7e0cdd3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/StaticVersionComparator.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.util.Comparator;
+import java.util.Locale;
+import java.util.Map;
+
+class StaticVersionComparator implements Comparator<Version> {
+    private static final Map<String, Integer> SPECIAL_MEANINGS =
+            ImmutableMap.of("dev", new Integer(-1), "rc", new Integer(1), "final", new Integer(2));
+
+    /**
+     * Compares 2 versions. Algorithm is inspired by PHP version_compare one.
+     */
+    public int compare(Version version1, Version version2) {
+        if (version1.equals(version2)) {
+            return 0;
+        }
+
+        String[] parts1 = version1.getParts();
+        String[] parts2 = version2.getParts();
+
+        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+");
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelector.java
new file mode 100644
index 0000000..b5e8c99
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelector.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.api.internal.artifacts.ivyservice.ivyresolve.strategy;
+
+/**
+ * Version matcher for dynamic version selectors ending in '+'.
+ */
+public class SubVersionSelector extends AbstractVersionSelector {
+    private final String prefix;
+
+    public SubVersionSelector(String selector) {
+        super(selector);
+        prefix = selector.substring(0, selector.length() - 1);
+    }
+
+    public boolean isDynamic() {
+        return true;
+    }
+
+    public boolean requiresMetadata() {
+        return false;
+    }
+
+    public boolean matchesUniqueVersion() {
+        return false;
+    }
+
+    public boolean accept(String candidate) {
+        return candidate.startsWith(prefix);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/Version.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/Version.java
new file mode 100644
index 0000000..3a948ca
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/Version.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+/**
+ * A parsed version.
+ *
+ * This should be synced with {@link org.gradle.util.VersionNumber} and {@link org.gradle.util.GradleVersion} at some point.
+ */
+public interface Version {
+    /**
+     * Returns all the parts of this version. e.g. 1.2.3 returns [1,2,3] or 1.2-beta4 returns [1,2,beta,4].
+     */
+    String[] getParts();
+
+    /**
+     * Returns the base version for this version, which removes any qualifiers. Generally this is the first '.' separated parts of this version.
+     * e.g. 1.2.3-beta-4 returns 1.2.3, or 7.0.12beta5 returns 7.0.12.
+     */
+    Version getBaseVersion();
+
+    /**
+     * Returns true if this version is qualified in any way. For example, 1.2.3 is not qualified, 1.2-beta-3 is.
+     */
+    boolean isQualified();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionComparator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionComparator.java
new file mode 100644
index 0000000..65b3e7f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionComparator.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.strategy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
+
+import java.util.Comparator;
+
+public interface VersionComparator extends Comparator<Versioned> {
+    /**
+     * Compares two versioned elements to see which is the 'latest'.
+     */
+    int compare(Versioned element1, Versioned element2);
+
+    Comparator<Version> asVersionComparator();
+
+    Comparator<String> asStringComparator();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java
new file mode 100644
index 0000000..3d08bbb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParser.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Transformer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VersionParser implements Transformer<Version, String> {
+    @Override
+    public Version transform(String original) {
+        List<String> parts = new ArrayList<String>();
+        boolean digit = false;
+        int startPart = 0;
+        int pos = 0;
+        int endBase = 0;
+        int endBaseStr = 0;
+        for (; pos < original.length(); pos++) {
+            char ch = original.charAt(pos);
+            if (ch == '.' || ch == '_' || ch == '-' || ch == '+') {
+                parts.add(original.substring(startPart, pos));
+                startPart = pos + 1;
+                digit = false;
+                if (ch != '.' && endBaseStr == 0) {
+                    endBase = parts.size();
+                    endBaseStr = pos;
+                }
+            } else if (ch >= '0' && ch <= '9') {
+                if (!digit && pos > startPart) {
+                    if (endBaseStr == 0) {
+                        endBase = parts.size() + 1;
+                        endBaseStr = pos;
+                    }
+                    parts.add(original.substring(startPart, pos));
+                    startPart = pos;
+                }
+                digit = true;
+            } else {
+                if (digit) {
+                    if (endBaseStr == 0) {
+                        endBase = parts.size() + 1;
+                        endBaseStr = pos;
+                    }
+                    parts.add(original.substring(startPart, pos));
+                    startPart = pos;
+                }
+                digit = false;
+            }
+        }
+        if (pos > startPart) {
+            parts.add(original.substring(startPart, pos));
+        }
+        DefaultVersion base = null;
+        if (endBaseStr > 0) {
+            base = new DefaultVersion(original.substring(0, endBaseStr), parts.subList(0, endBase), null);
+        }
+        return new DefaultVersion(original, parts, base);
+    }
+
+    private static class DefaultVersion implements Version {
+        private final String source;
+        private final String[] parts;
+        private final DefaultVersion baseVersion;
+
+        public DefaultVersion(String source, List<String> parts, DefaultVersion baseVersion) {
+            this.source = source;
+            this.parts = parts.toArray(new String[parts.size()]);
+            this.baseVersion = baseVersion == null ? this : baseVersion;
+        }
+
+        @Override
+        public String toString() {
+            return source;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+            DefaultVersion other = (DefaultVersion) obj;
+            return source.equals(other.source);
+        }
+
+        @Override
+        public int hashCode() {
+            return source.hashCode();
+        }
+
+        @Override
+        public boolean isQualified() {
+            return baseVersion != this;
+        }
+
+        @Override
+        public Version getBaseVersion() {
+            return baseVersion;
+        }
+
+        public String[] getParts() {
+            return parts;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelector.java
new file mode 100644
index 0000000..5948edd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelector.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 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.
+ */
+public class VersionRangeSelector extends AbstractVersionSelector {
+    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);
+
+    public static final Pattern ALL_RANGE = Pattern.compile(FINITE_PATTERN + "|"
+            + LOWER_INFINITE_PATTERN + "|" + UPPER_INFINITE_PATTERN);
+
+    private final String upperBound;
+    private final boolean upperInclusive;
+    private final String lowerBound;
+    private final boolean lowerInclusive;
+    private final Comparator<String> comparator;
+
+    public VersionRangeSelector(String selector, Comparator<String> comparator) {
+        super(selector);
+        this.comparator = comparator;
+
+        Matcher matcher;
+        matcher = FINITE_RANGE.matcher(selector);
+        if (matcher.matches()) {
+            lowerBound = matcher.group(1);
+            lowerInclusive = selector.startsWith(OPEN_INC);
+            upperBound = matcher.group(2);
+            upperInclusive = selector.endsWith(CLOSE_INC);
+        } else {
+            matcher = LOWER_INFINITE_RANGE.matcher(selector);
+            if (matcher.matches()) {
+                lowerBound = null;
+                lowerInclusive = true;
+                upperBound = matcher.group(1);
+                upperInclusive = selector.endsWith(CLOSE_INC);
+            } else {
+                matcher = UPPER_INFINITE_RANGE.matcher(selector);
+                if (matcher.matches()) {
+                    lowerBound = matcher.group(1);
+                    lowerInclusive = selector.startsWith(OPEN_INC);
+                    upperBound = null;
+                    upperInclusive = true;
+                } else {
+                    throw new IllegalArgumentException("Not a version range selector: " + selector);
+                }
+            }
+        }
+    }
+
+    public boolean isDynamic() {
+        return true;
+    }
+
+    public boolean requiresMetadata() {
+        return false;
+    }
+
+    public boolean matchesUniqueVersion() {
+        return false;
+    }
+
+    public boolean accept(String candidate) {
+        if (lowerBound != null && !isHigher(candidate, lowerBound, lowerInclusive)) {
+            return false;
+        }
+        if (upperBound != null && !isLower(candidate, upperBound, upperInclusive)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Tells if version1 is lower than version2.
+     */
+    private boolean isLower(String version1, String version2, boolean inclusive) {
+        int result = comparator.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 = comparator.compare(version1, version2);
+        return result >= (inclusive ? 0 : 1);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelector.java
new file mode 100644
index 0000000..0ad68ba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelector.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentMetadata;
+
+public interface VersionSelector {
+    /**
+     * Indicates if the given version selector is dynamic.
+     */
+    public boolean isDynamic();
+
+    /**
+     * Indicates if module metadata is required to determine if the
+     * selector matches a candidate version.
+     */
+    public boolean requiresMetadata();
+
+    /**
+     * Indicates if the selector implies that it matches only a single version.
+     */
+    public boolean matchesUniqueVersion();
+
+    /**
+     * Indicates if the selector matches the given candidate version.
+     * Only called if {@link #requiresMetadata()} returned {@code false}.
+     *
+     * @param candidate the candidate version
+     */
+    public boolean accept(String candidate);
+
+    /**
+     * Indicates if the selector matches the given candidate version
+     * (whose metadata is provided). May also be called if {@link #isDynamic} returned
+     * {@code false}, in which case it should return the same result as
+     * {@code accept(candidate.getId().getVersion()}.
+     *
+     * @param candidate the metadata for the candidate version
+     */
+    public boolean accept(ComponentMetadata candidate);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelectorScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelectorScheme.java
new file mode 100644
index 0000000..6ab45d9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionSelectorScheme.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.strategy;
+
+/**
+ * Compares version selectors against candidate versions, indicating whether they match or not.
+ *
+ */
+public interface VersionSelectorScheme {
+    /**
+     * Returns an appropriate {@link VersionSelector} for the given selector string.
+     *
+     * @param selectorString - the string representation of the selector
+     * @return the {@link VersionSelector}
+     */
+    VersionSelector parseSelector(String selectorString);
+
+    /**
+     * Renders a {@link VersionSelector} to a selector string.
+     */
+    String renderSelector(VersionSelector selector);
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.java
new file mode 100644
index 0000000..ac4d14d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/CachedModuleDescriptorParseContext.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.modulecache;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+/**
+ * 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 implements DescriptorParseContext {
+    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, ArtifactType artifactType) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java
new file mode 100644
index 0000000..ffff228
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetaData.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.modulecache;
+
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.math.BigInteger;
+
+class DefaultCachedMetaData implements ModuleMetaDataCache.CachedMetaData {
+    private final ModuleSource moduleSource;
+    private final BigInteger descriptorHash;
+    private final long ageMillis;
+    private final MutableModuleComponentResolveMetaData metaData;
+
+    public DefaultCachedMetaData(ModuleDescriptorCacheEntry entry, MutableModuleComponentResolveMetaData metaData, BuildCommencedTimeProvider timeProvider) {
+        this.moduleSource = entry.moduleSource;
+        this.descriptorHash = entry.moduleDescriptorHash;
+        this.ageMillis = timeProvider.getCurrentTime() - entry.createTimestamp;
+        this.metaData = metaData;
+    }
+
+    public boolean isMissing() {
+        return metaData == null;
+    }
+
+    public ModuleSource getModuleSource() {
+        return moduleSource;
+    }
+
+    public ResolvedModuleVersion getModuleVersion() {
+        return isMissing() ? null : new DefaultResolvedModuleVersion(getMetaData().getId());
+    }
+
+    public MutableModuleComponentResolveMetaData getMetaData() {
+        return metaData;
+    }
+
+    public long getAgeMillis() {
+        return ageMillis;
+    }
+
+    public BigInteger getDescriptorHash() {
+        return descriptorHash;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java
new file mode 100644
index 0000000..adfac67
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleArtifactsCache.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifierSerializer;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.internal.serialize.SetSerializer;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.math.BigInteger;
+import java.util.Set;
+
+public class DefaultModuleArtifactsCache implements ModuleArtifactsCache {
+    private final BuildCommencedTimeProvider timeProvider;
+    private final CacheLockingManager cacheLockingManager;
+    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> cache;
+
+    public DefaultModuleArtifactsCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        this.timeProvider = timeProvider;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> getCache() {
+        if (cache == null) {
+            cache = initCache();
+        }
+        return cache;
+    }
+
+    private PersistentIndexedCache<ModuleArtifactsKey, ModuleArtifactsCacheEntry> initCache() {
+        return cacheLockingManager.createCache("module-artifacts", new ModuleArtifactsKeySerializer(), new ModuleArtifactsCacheEntrySerializer());
+    }
+
+    public CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context, BigInteger descriptorHash, Set<ModuleComponentArtifactIdentifier> artifacts) {
+        ModuleArtifactsKey key = new ModuleArtifactsKey(repository.getId(), moduleMetaDataId, context);
+        ModuleArtifactsCacheEntry entry = new ModuleArtifactsCacheEntry(artifacts, timeProvider.getCurrentTime(), descriptorHash);
+        getCache().put(key, entry);
+        return createCacheArtifacts(entry);
+    }
+
+    public CachedArtifacts getCachedArtifacts(ModuleComponentRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context) {
+        ModuleArtifactsKey key = new ModuleArtifactsKey(repository.getId(), moduleMetaDataId, context);
+        ModuleArtifactsCacheEntry entry = getCache().get(key);
+        if (entry == null) {
+            return null;
+        }
+        return createCacheArtifacts(entry);
+    }
+
+    private CachedArtifacts createCacheArtifacts(ModuleArtifactsCacheEntry entry) {
+        long entryAge = timeProvider.getCurrentTime() - entry.createTimestamp;
+        return new DefaultCachedArtifacts(entry.artifacts, entry.moduleDescriptorHash, entryAge);
+    }
+
+    private static class ModuleArtifactsKey {
+        private final String repositoryId;
+        private final ModuleVersionIdentifier moduleId;
+        private final String context;
+
+        private ModuleArtifactsKey(String repositoryId, ModuleVersionIdentifier moduleId, String context) {
+            this.repositoryId = repositoryId;
+            this.moduleId = moduleId;
+            this.context = context;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof ModuleArtifactsKey)) {
+                return false;
+            }
+
+            ModuleArtifactsKey that = (ModuleArtifactsKey) o;
+            return repositoryId.equals(that.repositoryId) && moduleId.equals(that.moduleId) && context.equals(that.context);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = repositoryId.hashCode();
+            result = 31 * result + moduleId.hashCode();
+            result = 31 * result + context.hashCode();
+            return result;
+        }
+    }
+
+    private static class ModuleArtifactsKeySerializer implements Serializer<ModuleArtifactsKey> {
+        private final ModuleVersionIdentifierSerializer identifierSerializer = new ModuleVersionIdentifierSerializer();
+
+        public void write(Encoder encoder, ModuleArtifactsKey value) throws Exception {
+            encoder.writeString(value.repositoryId);
+            identifierSerializer.write(encoder, value.moduleId);
+            encoder.writeString(value.context);
+        }
+
+        public ModuleArtifactsKey read(Decoder decoder) throws Exception {
+            String resolverId = decoder.readString();
+            ModuleVersionIdentifier moduleVersionIdentifier = identifierSerializer.read(decoder);
+            String context = decoder.readString();
+            return new ModuleArtifactsKey(resolverId, moduleVersionIdentifier, context);
+        }
+    }
+
+    private static class ModuleArtifactsCacheEntry {
+        private final Set<ModuleComponentArtifactIdentifier> artifacts;
+        public BigInteger moduleDescriptorHash;
+        public long createTimestamp;
+
+        ModuleArtifactsCacheEntry(Set<ModuleComponentArtifactIdentifier> artifacts, long createTimestamp, BigInteger moduleDescriptorHash) {
+            this.artifacts = artifacts;
+            this.createTimestamp = createTimestamp;
+            this.moduleDescriptorHash = moduleDescriptorHash;
+        }
+    }
+
+
+    private static class ModuleArtifactsCacheEntrySerializer implements Serializer<ModuleArtifactsCacheEntry> {
+        private final Serializer<Set<ModuleComponentArtifactIdentifier>> artifactsSerializer =
+                new SetSerializer<ModuleComponentArtifactIdentifier>(new ModuleVersionArtifactIdentifierSerializer());
+        public void write(Encoder encoder, ModuleArtifactsCacheEntry value) throws Exception {
+            encoder.writeLong(value.createTimestamp);
+            byte[] hash = value.moduleDescriptorHash.toByteArray();
+            encoder.writeBinary(hash);
+            artifactsSerializer.write(encoder, value.artifacts);
+        }
+
+        public ModuleArtifactsCacheEntry read(Decoder decoder) throws Exception {
+            long createTimestamp = decoder.readLong();
+            byte[] encodedHash = decoder.readBinary();
+            BigInteger hash = new BigInteger(encodedHash);
+            Set<ModuleComponentArtifactIdentifier> artifacts = artifactsSerializer.read(decoder);
+            return new ModuleArtifactsCacheEntry(artifacts, createTimestamp, hash);
+        }
+    }
+
+    private static class DefaultCachedArtifacts implements ModuleArtifactsCache.CachedArtifacts {
+        private final Set<ModuleComponentArtifactIdentifier> artifacts;
+        private final BigInteger descriptorHash;
+        private final long ageMillis;
+
+        private DefaultCachedArtifacts(Set<ModuleComponentArtifactIdentifier> artifacts, BigInteger descriptorHash, long ageMillis) {
+            this.ageMillis = ageMillis;
+            this.artifacts = artifacts;
+            this.descriptorHash = descriptorHash;
+        }
+
+        public Set<ModuleComponentArtifactIdentifier> getArtifacts() {
+            return artifacts;
+        }
+
+        public BigInteger getDescriptorHash() {
+            return descriptorHash;
+        }
+
+        public long getAgeMillis() {
+            return ageMillis;
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java
new file mode 100644
index 0000000..4c34294
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultModuleMetaDataCache.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleDescriptor;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.IvyXmlModuleDescriptorWriter;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentIdentifierSerializer;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.gradle.internal.resource.local.PathKeyFileStore;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.util.BuildCommencedTimeProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultModuleMetaDataCache implements ModuleMetaDataCache {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultModuleMetaDataCache.class);
+
+    private final BuildCommencedTimeProvider timeProvider;
+    private final CacheLockingManager cacheLockingManager;
+
+    private final ModuleDescriptorStore moduleDescriptorStore;
+    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> cache;
+
+    public DefaultModuleMetaDataCache(BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager, ResolverStrategy resolverStrategy) {
+        this.timeProvider = timeProvider;
+        this.cacheLockingManager = cacheLockingManager;
+
+        moduleDescriptorStore = new ModuleDescriptorStore(new PathKeyFileStore(cacheLockingManager.createMetaDataStore()), new IvyXmlModuleDescriptorWriter(), new IvyXmlModuleDescriptorParser(resolverStrategy));
+    }
+
+    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> getCache() {
+        if (cache == null) {
+            cache = initCache();
+        }
+        return cache;
+    }
+
+    private PersistentIndexedCache<RevisionKey, ModuleDescriptorCacheEntry> initCache() {
+        return cacheLockingManager.createCache("module-metadata", new RevisionKeySerializer(), new ModuleDescriptorCacheEntrySerializer());
+    }
+
+    public CachedMetaData getCachedModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier componentId) {
+        ModuleDescriptorCacheEntry entry = getCache().get(createKey(repository, componentId));
+        if (entry == null) {
+            return null;
+        }
+        if (entry.isMissing()) {
+            return new DefaultCachedMetaData(entry, null, timeProvider);
+        }
+        ModuleDescriptor descriptor = moduleDescriptorStore.getModuleDescriptor(repository, componentId);
+        if (descriptor == null) {
+            // Descriptor file has been deleted - ignore the entry
+            return null;
+        }
+        return new DefaultCachedMetaData(entry, entry.createMetaData(componentId, descriptor), timeProvider);
+    }
+
+    public CachedMetaData cacheMissing(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+        LOGGER.debug("Recording absence of module descriptor in cache: {} [changing = {}]", id, false);
+        ModuleDescriptorCacheEntry entry = ModuleDescriptorCacheEntry.forMissingModule(timeProvider.getCurrentTime());
+        getCache().put(createKey(repository, id), entry);
+        return new DefaultCachedMetaData(entry, null, timeProvider);
+    }
+
+    public CachedMetaData cacheMetaData(ModuleComponentRepository repository, ModuleComponentResolveMetaData metaData) {
+        ModuleDescriptor moduleDescriptor = metaData.getDescriptor();
+        LOGGER.debug("Recording module descriptor in cache: {} [changing = {}]", moduleDescriptor.getModuleRevisionId(), metaData.isChanging());
+        LocallyAvailableResource resource = moduleDescriptorStore.putModuleDescriptor(repository, metaData.getComponentId(), moduleDescriptor);
+        ModuleDescriptorCacheEntry entry = createEntry(metaData, resource.getSha1());
+        getCache().put(createKey(repository, metaData.getComponentId()), entry);
+        return new DefaultCachedMetaData(entry, null, timeProvider);
+    }
+
+    private RevisionKey createKey(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+        return new RevisionKey(repository.getId(), id);
+    }
+
+    private ModuleDescriptorCacheEntry createEntry(ModuleComponentResolveMetaData metaData, HashValue moduleDescriptorHash) {
+        return ModuleDescriptorCacheEntry.forMetaData(metaData, timeProvider.getCurrentTime(), moduleDescriptorHash.asBigInteger());
+    }
+
+    private static class RevisionKey {
+        private final String repositoryId;
+        private final ModuleComponentIdentifier componentId;
+
+        private RevisionKey(String repositoryId, ModuleComponentIdentifier componentId) {
+            this.repositoryId = repositoryId;
+            this.componentId = componentId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || !(o instanceof RevisionKey)) {
+                return false;
+            }
+            RevisionKey other = (RevisionKey) o;
+            return repositoryId.equals(other.repositoryId) && componentId.equals(other.componentId);
+        }
+
+        @Override
+        public int hashCode() {
+            return repositoryId.hashCode() ^ componentId.hashCode();
+        }
+    }
+
+    private static class RevisionKeySerializer implements Serializer<RevisionKey> {
+        private final ComponentIdentifierSerializer componentIdSerializer = new ComponentIdentifierSerializer();
+
+        public void write(Encoder encoder, RevisionKey value) throws Exception {
+            encoder.writeString(value.repositoryId);
+            componentIdSerializer.write(encoder, value.componentId);
+        }
+
+        public RevisionKey read(Decoder decoder) throws Exception {
+            String resolverId = decoder.readString();
+            ModuleComponentIdentifier identifier = (ModuleComponentIdentifier) componentIdSerializer.read(decoder);
+            return new RevisionKey(resolverId, identifier);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/IvyModuleCacheEntry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/IvyModuleCacheEntry.java
new file mode 100644
index 0000000..c8d2318
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/IvyModuleCacheEntry.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleDescriptor;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultIvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.math.BigInteger;
+
+class IvyModuleCacheEntry extends ModuleDescriptorCacheEntry {
+    public IvyModuleCacheEntry(boolean isChanging, long createTimestamp, BigInteger moduleDescriptorHash, ModuleSource moduleSource) {
+        super(TYPE_IVY, isChanging, createTimestamp, moduleDescriptorHash, moduleSource);
+    }
+
+    public MutableModuleComponentResolveMetaData createMetaData(ModuleComponentIdentifier componentIdentifier, ModuleDescriptor descriptor) {
+        return configure(new DefaultIvyModuleResolveMetaData(componentIdentifier, descriptor));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MavenModuleCacheEntry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MavenModuleCacheEntry.java
new file mode 100644
index 0000000..3a400fb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MavenModuleCacheEntry.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ModuleDescriptor;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.math.BigInteger;
+
+class MavenModuleCacheEntry extends ModuleDescriptorCacheEntry {
+    final String snapshotTimestamp;
+    final String packaging;
+
+    public MavenModuleCacheEntry(boolean isChanging, String packaging, String snapshotTimestamp, long createTimestamp, BigInteger moduleDescriptorHash, ModuleSource moduleSource) {
+        super(TYPE_MAVEN, isChanging, createTimestamp, moduleDescriptorHash, moduleSource);
+        this.packaging = packaging;
+        this.snapshotTimestamp = snapshotTimestamp;
+    }
+
+    public MutableModuleComponentResolveMetaData createMetaData(ModuleComponentIdentifier componentIdentifier, ModuleDescriptor descriptor) {
+        // TODO Relocation is not currently cached
+        return configure(new DefaultMavenModuleResolveMetaData(componentIdentifier, descriptor, packaging, false));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MissingModuleCacheEntry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MissingModuleCacheEntry.java
new file mode 100644
index 0000000..bbbcf8c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/MissingModuleCacheEntry.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 java.math.BigInteger;
+
+class MissingModuleCacheEntry extends ModuleDescriptorCacheEntry {
+    public MissingModuleCacheEntry(long createTimestamp) {
+        super(TYPE_MISSING, false, createTimestamp, BigInteger.ZERO, null);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.java
new file mode 100644
index 0000000..23063a1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleArtifactsCache.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.api.internal.artifacts.ivyservice.modulecache;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+
+import java.math.BigInteger;
+import java.util.Set;
+
+public interface ModuleArtifactsCache {
+    CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, ModuleVersionIdentifier moduleMetaDataId, String context, BigInteger descriptorHash, Set<ModuleComponentArtifactIdentifier> artifacts);
+
+    CachedArtifacts getCachedArtifacts(ModuleComponentRepository delegate, ModuleVersionIdentifier moduleMetaDataId, String context);
+
+    interface CachedArtifacts {
+        Set<ModuleComponentArtifactIdentifier> getArtifacts();
+
+        BigInteger getDescriptorHash();
+
+        long getAgeMillis();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.java
new file mode 100644
index 0000000..bae82c3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntry.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.modulecache;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.IvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.MavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.math.BigInteger;
+
+abstract class ModuleDescriptorCacheEntry {
+    static final byte TYPE_MISSING = 0;
+    static final byte TYPE_IVY = 1;
+    static final byte TYPE_MAVEN = 2;
+
+    final byte type;
+    final boolean isChanging;
+    final long createTimestamp;
+    final ModuleSource moduleSource;
+    final BigInteger moduleDescriptorHash;
+
+    ModuleDescriptorCacheEntry(byte type, boolean isChanging, long createTimestamp, BigInteger moduleDescriptorHash, ModuleSource moduleSource) {
+        this.type = type;
+        this.isChanging = isChanging;
+        this.createTimestamp = createTimestamp;
+        this.moduleSource = moduleSource;
+        this.moduleDescriptorHash = moduleDescriptorHash;
+    }
+
+    public static ModuleDescriptorCacheEntry forMissingModule(long createTimestamp) {
+        return new MissingModuleCacheEntry(createTimestamp);
+    }
+
+    public static ModuleDescriptorCacheEntry forMetaData(ModuleComponentResolveMetaData metaData, long createTimestamp, BigInteger moduleDescriptorHash) {
+        if (metaData instanceof IvyModuleResolveMetaData) {
+            return new IvyModuleCacheEntry(metaData.isChanging(), createTimestamp, moduleDescriptorHash, metaData.getSource());
+        }
+        if (metaData instanceof MavenModuleResolveMetaData) {
+            MavenModuleResolveMetaData mavenMetaData = (MavenModuleResolveMetaData) metaData;
+            String packaging = mavenMetaData.getPackaging();
+            String snapshotTimestamp = mavenMetaData.getSnapshotTimestamp();
+            return new MavenModuleCacheEntry(metaData.isChanging(), packaging, snapshotTimestamp, createTimestamp, moduleDescriptorHash, metaData.getSource());
+        }
+        throw new IllegalArgumentException("Not a valid module version type: " + metaData);
+    }
+
+    public boolean isMissing() {
+        return type == TYPE_MISSING;
+    }
+    
+    public MutableModuleComponentResolveMetaData createMetaData(ModuleComponentIdentifier componentIdentifier, ModuleDescriptor descriptor) {
+        throw new UnsupportedOperationException("Cannot create meta-data for entry " + this);
+    }
+
+    protected MutableModuleComponentResolveMetaData configure(MutableModuleComponentResolveMetaData input) {
+        input.setChanging(isChanging);
+        input.setSource(moduleSource);
+        return input;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntrySerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntrySerializer.java
new file mode 100644
index 0000000..97ccc9b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorCacheEntrySerializer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.DefaultSerializer;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.math.BigInteger;
+
+class ModuleDescriptorCacheEntrySerializer implements Serializer<ModuleDescriptorCacheEntry> {
+    private final DefaultSerializer<ModuleSource> moduleSourceSerializer = new DefaultSerializer<ModuleSource>(ModuleSource.class.getClassLoader());
+
+    public void write(Encoder encoder, ModuleDescriptorCacheEntry value) throws Exception {
+        encoder.writeByte(value.type);
+        switch (value.type) {
+            case ModuleDescriptorCacheEntry.TYPE_MISSING:
+                encoder.writeLong(value.createTimestamp);
+                break;
+            case ModuleDescriptorCacheEntry.TYPE_IVY:
+                encoder.writeBoolean(value.isChanging);
+                encoder.writeLong(value.createTimestamp);
+                moduleSourceSerializer.write(encoder, value.moduleSource);
+                byte[] hash = value.moduleDescriptorHash.toByteArray();
+                encoder.writeBinary(hash);
+                break;
+            case ModuleDescriptorCacheEntry.TYPE_MAVEN:
+                MavenModuleCacheEntry mavenCacheEntry = (MavenModuleCacheEntry) value;
+                encoder.writeBoolean(value.isChanging);
+                encoder.writeNullableString(mavenCacheEntry.packaging);
+                encoder.writeNullableString(mavenCacheEntry.snapshotTimestamp);
+                encoder.writeLong(value.createTimestamp);
+                moduleSourceSerializer.write(encoder, value.moduleSource);
+                hash = value.moduleDescriptorHash.toByteArray();
+                encoder.writeBinary(hash);
+                break;
+            default:
+                throw new IllegalArgumentException("Don't know how to serialize meta-data entry: " + value);
+        }
+    }
+
+    public ModuleDescriptorCacheEntry read(Decoder decoder) throws Exception {
+        byte type = decoder.readByte();
+        switch (type) {
+            case ModuleDescriptorCacheEntry.TYPE_MISSING:
+                long createTimestamp = decoder.readLong();
+                return new MissingModuleCacheEntry(createTimestamp);
+            case ModuleDescriptorCacheEntry.TYPE_IVY:
+                boolean isChanging = decoder.readBoolean();
+                createTimestamp = decoder.readLong();
+                ModuleSource moduleSource = moduleSourceSerializer.read(decoder);
+                byte[] encodedHash = decoder.readBinary();
+                BigInteger hash = new BigInteger(encodedHash);
+                return new IvyModuleCacheEntry(isChanging, createTimestamp, hash, moduleSource);
+            case ModuleDescriptorCacheEntry.TYPE_MAVEN:
+                isChanging = decoder.readBoolean();
+                String packaging = decoder.readNullableString();
+                String snapshotTimestamp = decoder.readNullableString();
+                createTimestamp = decoder.readLong();
+                moduleSource = moduleSourceSerializer.read(decoder);
+                encodedHash = decoder.readBinary();
+                hash = new BigInteger(encodedHash);
+                return new MavenModuleCacheEntry(isChanging, packaging, snapshotTimestamp, createTimestamp, hash, moduleSource);
+            default:
+                throw new IllegalArgumentException("Don't know how to deserialize meta-data entry of type " + type);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
new file mode 100644
index 0000000..65512fa
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStore.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.modulecache;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.gradle.internal.resource.local.PathKeyFileStore;
+
+import java.io.File;
+
+public class ModuleDescriptorStore {
+
+    public static final String FILE_PATH_PATTERN = "%s/%s/%s/%s/ivy.xml";
+    private final IvyXmlModuleDescriptorParser descriptorParser;
+    private final PathKeyFileStore metaDataStore;
+    private final IvyModuleDescriptorWriter descriptorWriter;
+
+    public ModuleDescriptorStore(PathKeyFileStore metaDataStore, IvyModuleDescriptorWriter descriptorWriter, IvyXmlModuleDescriptorParser ivyXmlModuleDescriptorParser) {
+        this.metaDataStore = metaDataStore;
+        this.descriptorWriter = descriptorWriter;
+        this.descriptorParser = ivyXmlModuleDescriptorParser;
+    }
+
+    public ModuleDescriptor getModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier moduleComponentIdentifier) {
+        String filePath = getFilePath(repository, moduleComponentIdentifier);
+        final LocallyAvailableResource resource = metaDataStore.get(filePath);
+        if (resource != null) {
+            return parseModuleDescriptorFile(resource.getFile());
+        }
+        return null;
+    }
+
+    public LocallyAvailableResource putModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier moduleComponentIdentifier, final ModuleDescriptor moduleDescriptor) {
+        String filePath = getFilePath(repository, moduleComponentIdentifier);
+        return metaDataStore.add(filePath, new Action<File>() {
+            public void execute(File moduleDescriptorFile) {
+                try {
+                    descriptorWriter.write(moduleDescriptor, moduleDescriptorFile);
+                } catch (Exception e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+        });
+    }
+
+    private ModuleDescriptor parseModuleDescriptorFile(File moduleDescriptorFile) {
+        DescriptorParseContext parserSettings = new CachedModuleDescriptorParseContext();
+        return descriptorParser.parseMetaData(parserSettings, moduleDescriptorFile, false).getDescriptor();
+    }
+
+    private String getFilePath(ModuleComponentRepository repository, ModuleComponentIdentifier moduleComponentIdentifier) {
+        return String.format(FILE_PATH_PATTERN, moduleComponentIdentifier.getGroup(), moduleComponentIdentifier.getModule(), moduleComponentIdentifier.getVersion(), repository.getId());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.java
new file mode 100644
index 0000000..e38fbd5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetaDataCache.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.api.internal.artifacts.ivyservice.modulecache;
+
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.math.BigInteger;
+
+public interface ModuleMetaDataCache {
+    CachedMetaData cacheMissing(ModuleComponentRepository repository, ModuleComponentIdentifier id);
+
+    CachedMetaData cacheMetaData(ModuleComponentRepository repository, ModuleComponentResolveMetaData metaData);
+
+    CachedMetaData getCachedModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier id);
+
+    interface CachedMetaData {
+        ResolvedModuleVersion getModuleVersion();
+
+        MutableModuleComponentResolveMetaData getMetaData();
+
+        long getAgeMillis();
+
+        BigInteger getDescriptorHash();
+
+        boolean isMissing();
+
+        ModuleSource getModuleSource();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java
new file mode 100644
index 0000000..548abd9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToArtifactsConverter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+public interface ConfigurationsToArtifactsConverter {
+    void addArtifacts(MutableLocalComponentMetaData metaData, Iterable<? extends Configuration> configurations);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
new file mode 100644
index 0000000..ef8d7d3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ConfigurationsToModuleDescriptorConverter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+public interface ConfigurationsToModuleDescriptorConverter {
+    void addConfigurations(MutableLocalComponentMetaData metaData, Iterable<? extends Configuration> configurations);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverter.java
new file mode 100644
index 0000000..875715d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+import org.gradle.util.GUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultConfigurationsToArtifactsConverter implements ConfigurationsToArtifactsConverter {
+
+    public void addArtifacts(MutableLocalComponentMetaData metaData, Iterable<? extends Configuration> configurations) {
+        ModuleVersionIdentifier id = metaData.getId();
+        for (Configuration configuration : configurations) {
+            for (PublishArtifact publishArtifact : configuration.getArtifacts()) {
+                IvyArtifactName ivyArtifact = createIvyArtifact(publishArtifact, id);
+                metaData.addArtifact(configuration.getName(), ivyArtifact, publishArtifact.getFile());
+            }
+        }
+    }
+
+    public IvyArtifactName createIvyArtifact(PublishArtifact publishArtifact, ModuleVersionIdentifier moduleVersionIdentifier) {
+        Map<String, String> extraAttributes = new HashMap<String, String>();
+        if (GUtil.isTrue(publishArtifact.getClassifier())) {
+            extraAttributes.put(Dependency.CLASSIFIER, publishArtifact.getClassifier());
+        }
+        String name = publishArtifact.getName();
+        if (!GUtil.isTrue(name)) {
+            name = moduleVersionIdentifier.getName();
+        }
+        return new DefaultIvyArtifactName(name, publishArtifact.getType(), publishArtifact.getExtension(), extraAttributes);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
new file mode 100644
index 0000000..bc4ab0b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.artifacts.configurations.Configurations;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+import java.util.Arrays;
+
+public class DefaultConfigurationsToModuleDescriptorConverter implements ConfigurationsToModuleDescriptorConverter {
+    public void addConfigurations(MutableLocalComponentMetaData metaData, Iterable<? extends Configuration> configurations) {
+        for (Configuration configuration : configurations) {
+            addConfiguration(metaData, configuration);
+        }
+    }
+
+    private void addConfiguration(MutableLocalComponentMetaData metaData, Configuration configuration) {
+        String[] superConfigs = Configurations.getNames(configuration.getExtendsFrom()).toArray(new String[configuration.getExtendsFrom().size()]);
+        Arrays.sort(superConfigs);
+        metaData.addConfiguration(configuration.getName(), configuration.isVisible(), configuration.getDescription(), superConfigs, configuration.isTransitive());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverter.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ExcludeRuleConverter.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java
new file mode 100644
index 0000000..491ed9b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactory.java
@@ -0,0 +1,62 @@
+/*
+ * 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.moduleconverter;
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.artifacts.ModuleInternal;
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter;
+import org.gradle.internal.component.local.model.DefaultLocalComponentMetaData;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+import java.util.Set;
+
+public class ResolveLocalComponentFactory implements LocalComponentFactory {
+    static final String IVY_MAVEN_NAMESPACE = "http://ant.apache.org/ivy/maven";
+    static final String IVY_MAVEN_NAMESPACE_PREFIX = "m";
+
+    private final ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter;
+    private final DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter;
+    private final ComponentIdentifierFactory componentIdentifierFactory;
+    private final ConfigurationsToArtifactsConverter configurationsToArtifactsConverter;
+
+    public ResolveLocalComponentFactory(ConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter,
+                                        DependenciesToModuleDescriptorConverter dependenciesToModuleDescriptorConverter,
+                                        ComponentIdentifierFactory componentIdentifierFactory,
+                                        ConfigurationsToArtifactsConverter configurationsToArtifactsConverter) {
+        this.configurationsToModuleDescriptorConverter = configurationsToModuleDescriptorConverter;
+        this.dependenciesToModuleDescriptorConverter = dependenciesToModuleDescriptorConverter;
+        this.componentIdentifierFactory = componentIdentifierFactory;
+        this.configurationsToArtifactsConverter = configurationsToArtifactsConverter;
+    }
+
+    public MutableLocalComponentMetaData convert(Set<? extends Configuration> configurations, ModuleInternal module) {
+        assert configurations.size() > 0 : "No configurations found for module: " + module.getName() + ". Configure them or apply a plugin that does it.";
+        DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(IvyUtil.createModuleRevisionId(module), module.getStatus(), null);
+        moduleDescriptor.addExtraAttributeNamespace(IVY_MAVEN_NAMESPACE_PREFIX, IVY_MAVEN_NAMESPACE);
+        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module);
+        DefaultLocalComponentMetaData metaData = new DefaultLocalComponentMetaData(moduleDescriptor, componentIdentifier);
+        configurationsToModuleDescriptorConverter.addConfigurations(metaData, configurations);
+        dependenciesToModuleDescriptorConverter.addDependencyDescriptors(metaData, configurations);
+        configurationsToArtifactsConverter.addArtifacts(metaData, configurations);
+        return metaData;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
new file mode 100644
index 0000000..1f108e8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
@@ -0,0 +1,77 @@
+/*
+ * 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.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
+import org.gradle.util.WrapUtil;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Set;
+
+public abstract class AbstractIvyDependencyDescriptorFactory implements IvyDependencyDescriptorFactory {
+    private ExcludeRuleConverter excludeRuleConverter;
+
+    public AbstractIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
+        this.excludeRuleConverter = excludeRuleConverter;
+    }
+
+    protected void addExcludesArtifactsAndDependencies(String configuration, ModuleDependency dependency,
+                                                       DefaultDependencyDescriptor dependencyDescriptor) {
+        addArtifacts(configuration, dependency.getArtifacts(), dependencyDescriptor);
+        addExcludes(configuration, dependency.getExcludeRules(), dependencyDescriptor);
+        addDependencyConfiguration(configuration, dependency, dependencyDescriptor);
+    }
+
+    private void addArtifacts(String configuration, Set<DependencyArtifact> artifacts,
+                              DefaultDependencyDescriptor dependencyDescriptor) {
+        for (DependencyArtifact artifact : artifacts) {
+            DefaultDependencyArtifactDescriptor artifactDescriptor;
+            try {
+                artifactDescriptor = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, artifact.getName(),
+                        artifact.getType(),
+                        artifact.getExtension() != null ? artifact.getExtension() : artifact.getType(),
+                        artifact.getUrl() != null ? new URL(artifact.getUrl()) : null,
+                        artifact.getClassifier() != null ? WrapUtil.toMap(Dependency.CLASSIFIER,
+                                artifact.getClassifier()) : null);
+            } catch (MalformedURLException e) {
+                throw new InvalidUserDataException("URL for artifact can't be parsed: " + artifact.getUrl(), e);
+            }
+            dependencyDescriptor.addDependencyArtifact(configuration, artifactDescriptor);
+        }
+    }
+
+    private void addDependencyConfiguration(String configuration, ModuleDependency dependency,
+                                            DefaultDependencyDescriptor dependencyDescriptor) {
+        dependencyDescriptor.addDependencyConfiguration(configuration, dependency.getConfiguration());
+    }
+
+    private void addExcludes(String configuration, Set<ExcludeRule> excludeRules,
+                             DefaultDependencyDescriptor dependencyDescriptor) {
+        for (ExcludeRule excludeRule : excludeRules) {
+            dependencyDescriptor.addExcludeRule(configuration, excludeRuleConverter.createExcludeRule(configuration,
+                    excludeRule));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
new file mode 100644
index 0000000..8d314f9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+import java.util.Collection;
+
+public class DefaultDependenciesToModuleDescriptorConverter implements DependenciesToModuleDescriptorConverter {
+    private DependencyDescriptorFactory dependencyDescriptorFactory;
+    private ExcludeRuleConverter excludeRuleConverter;
+
+    public DefaultDependenciesToModuleDescriptorConverter(DependencyDescriptorFactory dependencyDescriptorFactory,
+                                                          ExcludeRuleConverter excludeRuleConverter) {
+        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+        this.excludeRuleConverter = excludeRuleConverter;
+    }
+
+    public void addDependencyDescriptors(MutableLocalComponentMetaData metaData, Collection<? extends Configuration> configurations) {
+        assert !configurations.isEmpty();
+        addDependencies(metaData, configurations);
+        addExcludeRules(metaData, configurations);
+    }
+
+    private void addDependencies(MutableLocalComponentMetaData metaData, Collection<? extends Configuration> configurations) {
+        for (Configuration configuration : configurations) {
+            for (ModuleDependency dependency : configuration.getDependencies().withType(ModuleDependency.class)) {
+                metaData.addDependency(dependencyDescriptorFactory.createDependencyDescriptor(configuration.getName(), metaData.getModuleDescriptor(), dependency));
+            }
+        }
+    }
+
+    private void addExcludeRules(MutableLocalComponentMetaData metaData, Collection<? extends Configuration> configurations) {
+        for (Configuration configuration : configurations) {
+            for (ExcludeRule excludeRule : configuration.getExcludeRules()) {
+                org.apache.ivy.core.module.descriptor.ExcludeRule rule = excludeRuleConverter.createExcludeRule(
+                        configuration.getName(), excludeRule);
+                metaData.addExcludeRule(rule);
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
new file mode 100644
index 0000000..7f222dd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.util.WrapUtil;
+
+import java.util.List;
+
+public class DefaultDependencyDescriptorFactory implements DependencyDescriptorFactory {
+    private List<IvyDependencyDescriptorFactory> dependencyDescriptorFactories;
+
+    public DefaultDependencyDescriptorFactory(IvyDependencyDescriptorFactory... dependencyDescriptorFactories) {
+        this.dependencyDescriptorFactories = WrapUtil.toList(dependencyDescriptorFactories);
+    }
+
+    public DependencyMetaData createDependencyDescriptor(String configuration, ModuleDescriptor moduleDescriptor, ModuleDependency dependency) {
+        IvyDependencyDescriptorFactory factoryInternal = findFactoryForDependency(dependency);
+        return factoryInternal.createDependencyDescriptor(configuration, dependency, moduleDescriptor);
+    }
+
+    private IvyDependencyDescriptorFactory findFactoryForDependency(ModuleDependency dependency) {
+        for (IvyDependencyDescriptorFactory ivyDependencyDescriptorFactory : dependencyDescriptorFactories) {
+            if (ivyDependencyDescriptorFactory.canConvert(dependency)) {
+                return ivyDependencyDescriptorFactory;
+            }
+        }
+        throw new InvalidUserDataException("Can't map dependency of type: " + dependency.getClass());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
new file mode 100644
index 0000000..bef1e5c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependenciesToModuleDescriptorConverter.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+
+import java.util.Collection;
+
+public interface DependenciesToModuleDescriptorConverter {
+    void addDependencyDescriptors(MutableLocalComponentMetaData metaData, Collection<? extends Configuration> configurations);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
new file mode 100644
index 0000000..56c3d82
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.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.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+public interface DependencyDescriptorFactory {
+    DependencyMetaData createDependencyDescriptor(String configuration, ModuleDescriptor moduleDescriptor, ModuleDependency dependency);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
new file mode 100644
index 0000000..df8bd3b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+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.artifacts.ExternalModuleDependency;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaDataWrapper;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+import org.gradle.internal.component.model.DefaultDependencyMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+public class ExternalModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
+    public ExternalModuleIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
+        super(excludeRuleConverter);
+    }
+
+    private ModuleRevisionId createModuleRevisionId(ModuleDependency dependency) {
+        return IvyUtil.createModuleRevisionId(dependency);
+    }
+
+    public DslOriginDependencyMetaData createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor parent) {
+        ModuleRevisionId moduleRevisionId = createModuleRevisionId(dependency);
+        DefaultDependencyDescriptor dependencyDescriptor = new DefaultDependencyDescriptor(
+                parent,
+                moduleRevisionId,
+                getExternalModuleDependency(dependency).isForce(),
+                getExternalModuleDependency(dependency).isChanging(),
+                getExternalModuleDependency(dependency).isTransitive());
+        addExcludesArtifactsAndDependencies(configuration, getExternalModuleDependency(dependency), dependencyDescriptor);
+        DependencyMetaData dependencyMetaData = new DefaultDependencyMetaData(dependencyDescriptor);
+        return new DslOriginDependencyMetaDataWrapper(dependencyMetaData, dependency);
+    }
+
+    private ExternalModuleDependency getExternalModuleDependency(ModuleDependency dependency) {
+        return (ExternalModuleDependency) dependency;
+    }
+
+    public boolean canConvert(ModuleDependency dependency) {
+        return dependency instanceof ExternalModuleDependency;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java
new file mode 100644
index 0000000..2f51b47
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.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.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+
+public interface IvyDependencyDescriptorFactory {
+    DslOriginDependencyMetaData createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor moduleDescriptor);
+
+    boolean canConvert(ModuleDependency dependency);
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
new file mode 100644
index 0000000..0bbf6cb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+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.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;
+import org.gradle.internal.component.local.model.DefaultProjectDependencyMetaData;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaDataWrapper;
+
+public class ProjectIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
+    public ProjectIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
+        super(excludeRuleConverter);
+    }
+
+    public DslOriginDependencyMetaData createDependencyDescriptor(String configuration, ModuleDependency dependency, ModuleDescriptor parent) {
+        ProjectDependencyInternal projectDependency = (ProjectDependencyInternal) dependency;
+        projectDependency.beforeResolved();
+        ModuleRevisionId moduleRevisionId = createModuleRevisionId(dependency);
+        DefaultDependencyDescriptor dependencyDescriptor = new DefaultDependencyDescriptor(parent, moduleRevisionId, false, false, dependency.isTransitive());
+        addExcludesArtifactsAndDependencies(configuration, dependency, dependencyDescriptor);
+        DefaultProjectDependencyMetaData projectDependencyMetaData = new DefaultProjectDependencyMetaData(dependencyDescriptor, projectDependency.getDependencyProject().getPath());
+        return new DslOriginDependencyMetaDataWrapper(projectDependencyMetaData, projectDependency);
+    }
+
+    public boolean canConvert(ModuleDependency dependency) {
+        return dependency instanceof ProjectDependency;
+    }
+
+    private ModuleRevisionId createModuleRevisionId(ModuleDependency dependency) {
+        ProjectDependency projectDependency = (ProjectDependency) dependency;
+        Module module = ((ProjectInternal) projectDependency.getDependencyProject()).getModule();
+        return IvyUtil.createModuleRevisionId(module);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactory.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactory.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactory.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.java
new file mode 100644
index 0000000..b86f1f9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectComponentRegistry.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
+
+import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
+import org.gradle.internal.component.local.model.LocalComponentMetaData;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectRegistry;
+
+public class DefaultProjectComponentRegistry implements ProjectComponentRegistry {
+    private final LocalComponentFactory localComponentFactory;
+    private final ProjectRegistry<ProjectInternal> projectRegistry;
+
+    public DefaultProjectComponentRegistry(LocalComponentFactory localComponentFactory, ProjectRegistry<ProjectInternal> projectRegistry) {
+        this.localComponentFactory = localComponentFactory;
+        this.projectRegistry = projectRegistry;
+    }
+
+    public LocalComponentMetaData getProject(String projectPath) {
+        ProjectInternal project = projectRegistry.getProject(projectPath);
+        return localComponentFactory.convert(project.getConfigurations(), project.getModule());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java
new file mode 100644
index 0000000..fb41585
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectmodule;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+public class DefaultProjectPublication implements ProjectPublication {
+    private final ModuleVersionIdentifier id;
+
+    public DefaultProjectPublication(ModuleVersionIdentifier id) {
+        this.id = id;
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return id;
+    }
+
+    public boolean equals(Object other) {
+        if (!(other instanceof ProjectPublication)) {
+            return false;
+        }
+        return id.equals(((ProjectPublication) other).getId());
+    }
+
+    public int hashCode() {
+        return id.hashCode();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublicationRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublicationRegistry.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublicationRegistry.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectPublicationRegistry.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
new file mode 100644
index 0000000..0a4de6c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectmodule;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ComponentUsage;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.local.model.LocalArtifactMetaData;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+
+import java.util.Set;
+
+public class ProjectArtifactResolver implements ArtifactResolver {
+    private final ArtifactResolver delegate;
+
+    public ProjectArtifactResolver(ArtifactResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        if (isProjectModule(component.getComponentId())) {
+            throw new UnsupportedOperationException("Resolving artifacts by type is not yet supported for project modules");
+        }
+        delegate.resolveModuleArtifacts(component, artifactType, result);
+    }
+
+    public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage usage, BuildableArtifactSetResolveResult result) {
+        if (isProjectModule(component.getComponentId())) {
+            String configurationName = usage.getConfigurationName();
+            Set<ComponentArtifactMetaData> artifacts = component.getConfiguration(configurationName).getArtifacts();
+            result.resolved(artifacts);
+            return;
+        }
+        delegate.resolveModuleArtifacts(component, usage, result);
+    }
+
+    public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        if (isProjectModule(artifact.getComponentId())) {
+            LocalArtifactMetaData localArtifact = (LocalArtifactMetaData) artifact;
+            if (localArtifact.getFile() != null) {
+                result.resolved(localArtifact.getFile());
+            } else {
+                result.notFound(artifact.getId());
+            }
+        } else {
+            delegate.resolveArtifact(artifact, moduleSource, result);
+        }
+    }
+
+    private boolean isProjectModule(ComponentIdentifier componentId) {
+        return componentId instanceof ProjectComponentIdentifier;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.java
new file mode 100644
index 0000000..ae00bba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectComponentRegistry.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.artifacts.ivyservice.projectmodule;
+
+import org.gradle.internal.component.local.model.LocalComponentMetaData;
+
+public interface ProjectComponentRegistry {
+    LocalComponentMetaData getProject(String projectPath);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
new file mode 100644
index 0000000..d2478a9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.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.api.internal.artifacts.ivyservice.projectmodule;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+import org.gradle.api.internal.artifacts.ModuleInternal;
+import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory;
+import org.gradle.internal.component.local.model.LocalComponentMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.ModuleToComponentResolver;
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+import java.util.Set;
+
+public class ProjectDependencyResolver implements DependencyToComponentIdResolver, ModuleToComponentResolver {
+    private final ProjectComponentRegistry projectComponentRegistry;
+    private final DependencyToComponentIdResolver delegate;
+    private final LocalComponentFactory localComponentFactory;
+
+    public ProjectDependencyResolver(ProjectComponentRegistry projectComponentRegistry, LocalComponentFactory localComponentFactory, DependencyToComponentIdResolver delegate) {
+        this.projectComponentRegistry = projectComponentRegistry;
+        this.delegate = delegate;
+        this.localComponentFactory = localComponentFactory;
+    }
+
+    public void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result) {
+        if (dependency.getSelector() instanceof ProjectComponentSelector) {
+            ProjectComponentSelector selector = (ProjectComponentSelector) dependency.getSelector();
+            LocalComponentMetaData componentMetaData = projectComponentRegistry.getProject(selector.getProjectPath());
+            result.resolved(componentMetaData.toResolveMetaData());
+        } else {
+            delegate.resolve(dependency, result);
+        }
+    }
+
+    public void resolve(ModuleInternal module, Set<? extends Configuration> configurations, BuildableComponentResolveResult result) {
+        LocalComponentMetaData componentMetaData = localComponentFactory.convert(configurations, module);
+        result.resolved(componentMetaData.toResolveMetaData());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublication.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublication.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublication.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublication.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublicationRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublicationRegistry.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublicationRegistry.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectPublicationRegistry.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java
new file mode 100644
index 0000000..99cf4ae
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicy.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ResolvedModuleVersion;
+import org.gradle.api.artifacts.cache.*;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY;
+
+public class DefaultCachePolicy implements CachePolicy, ResolutionRules {
+    private static final int SECONDS_IN_DAY = 24 * 60 * 60;
+
+    final List<Action<? super DependencyResolutionControl>> dependencyCacheRules;
+    final List<Action<? super ModuleResolutionControl>> moduleCacheRules;
+    final List<Action<? super ArtifactResolutionControl>> artifactCacheRules;
+    private MutationValidator mutationValidator = MutationValidator.IGNORE;
+
+    public DefaultCachePolicy() {
+        this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>();
+        this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>();
+        this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>();
+
+        cacheDynamicVersionsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
+        cacheChangingModulesFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
+        cacheMissingArtifactsFor(SECONDS_IN_DAY, TimeUnit.SECONDS);
+    }
+
+    DefaultCachePolicy(DefaultCachePolicy policy) {
+        this.dependencyCacheRules = new ArrayList<Action<? super DependencyResolutionControl>>(policy.dependencyCacheRules);
+        this.moduleCacheRules = new ArrayList<Action<? super ModuleResolutionControl>>(policy.moduleCacheRules);
+        this.artifactCacheRules = new ArrayList<Action<? super ArtifactResolutionControl>>(policy.artifactCacheRules);
+    }
+
+    /**
+     * Sets the validator to invoke prior to each mutation.
+     */
+    public void beforeChange(MutationValidator validator) {
+        this.mutationValidator = validator;
+    }
+
+    public void eachDependency(Action<? super DependencyResolutionControl> rule) {
+        mutationValidator.validateMutation(STRATEGY);
+        dependencyCacheRules.add(0, rule);
+    }
+
+    public void eachModule(Action<? super ModuleResolutionControl> rule) {
+        mutationValidator.validateMutation(STRATEGY);
+        moduleCacheRules.add(0, rule);
+    }
+
+    public void eachArtifact(Action<? super ArtifactResolutionControl> rule) {
+        mutationValidator.validateMutation(STRATEGY);
+        artifactCacheRules.add(0, rule);
+    }
+
+    public void cacheDynamicVersionsFor(final int value, final TimeUnit unit) {
+        eachDependency(new Action<DependencyResolutionControl>() {
+            public void execute(DependencyResolutionControl dependencyResolutionControl) {
+                dependencyResolutionControl.cacheFor(value, unit);
+            }
+        });
+    }
+
+    public void cacheChangingModulesFor(final int value, final TimeUnit units) {
+        eachModule(new Action<ModuleResolutionControl>() {
+            public void execute(ModuleResolutionControl moduleResolutionControl) {
+                if (moduleResolutionControl.isChanging()) {
+                    moduleResolutionControl.cacheFor(value, units);
+                }
+            }
+        });
+        eachArtifact(new Action<ArtifactResolutionControl>() {
+            public void execute(ArtifactResolutionControl artifactResolutionControl) {
+                if (artifactResolutionControl.belongsToChangingModule()) {
+                    artifactResolutionControl.cacheFor(value, units);
+                }
+            }
+        });
+    }
+
+    private void cacheMissingArtifactsFor(final int value, final TimeUnit units) {
+        eachArtifact(new Action<ArtifactResolutionControl>() {
+            public void execute(ArtifactResolutionControl artifactResolutionControl) {
+                if (artifactResolutionControl.getCachedResult() == null) {
+                    artifactResolutionControl.cacheFor(value, units);
+                }
+            }
+        });
+    }
+
+    public boolean mustRefreshVersionList(final ModuleIdentifier moduleIdentifier, Set<ModuleVersionIdentifier> matchingVersions, long ageMillis) {
+        CachedDependencyResolutionControl dependencyResolutionControl = new CachedDependencyResolutionControl(moduleIdentifier, matchingVersions, ageMillis);
+
+        for (Action<? super DependencyResolutionControl> rule : dependencyCacheRules) {
+            rule.execute(dependencyResolutionControl);
+            if (dependencyResolutionControl.ruleMatch()) {
+                return dependencyResolutionControl.mustCheck();
+            }
+        }
+
+        return false;
+    }
+
+    public boolean mustRefreshMissingModule(ModuleComponentIdentifier component, long ageMillis) {
+        return mustRefreshModule(component, null, ageMillis, false);
+    }
+
+    public boolean mustRefreshModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis) {
+        return mustRefreshModule(component, resolvedModuleVersion, ageMillis, false);
+    }
+
+    public boolean mustRefreshChangingModule(ModuleComponentIdentifier component, ResolvedModuleVersion resolvedModuleVersion, long ageMillis) {
+        return mustRefreshModule(component, resolvedModuleVersion, ageMillis, true);
+    }
+
+    private boolean mustRefreshModule(ModuleComponentIdentifier component, ResolvedModuleVersion version, long ageMillis, boolean changingModule) {
+        return mustRefreshModule(DefaultModuleVersionIdentifier.newId(component), version, ageMillis, changingModule);
+    }
+
+    private boolean mustRefreshModule(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion version, long ageMillis, boolean changingModule) {
+        CachedModuleResolutionControl moduleResolutionControl = new CachedModuleResolutionControl(moduleVersionId, version, changingModule, ageMillis);
+
+        for (Action<? super ModuleResolutionControl> rule : moduleCacheRules) {
+            rule.execute(moduleResolutionControl);
+            if (moduleResolutionControl.ruleMatch()) {
+                return moduleResolutionControl.mustCheck();
+            }
+        }
+
+        return false;
+    }
+
+    public boolean mustRefreshModuleArtifacts(ModuleVersionIdentifier moduleVersionId, Set<ArtifactIdentifier> artifacts,
+                                              long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
+        if (belongsToChangingModule && !moduleDescriptorInSync) {
+            return true;
+        }
+        return mustRefreshModule(moduleVersionId, new DefaultResolvedModuleVersion(moduleVersionId), ageMillis, belongsToChangingModule);
+    }
+
+    public boolean mustRefreshArtifact(ArtifactIdentifier artifactIdentifier, File cachedArtifactFile, long ageMillis, boolean belongsToChangingModule, boolean moduleDescriptorInSync) {
+        CachedArtifactResolutionControl artifactResolutionControl = new CachedArtifactResolutionControl(artifactIdentifier, cachedArtifactFile, ageMillis, belongsToChangingModule);
+        if(belongsToChangingModule && !moduleDescriptorInSync){
+            return true;
+        }
+        for (Action<? super ArtifactResolutionControl> rule : artifactCacheRules) {
+            rule.execute(artifactResolutionControl);
+            if (artifactResolutionControl.ruleMatch()) {
+                return artifactResolutionControl.mustCheck();
+            }
+        }
+        return false;
+    }
+
+    DefaultCachePolicy copy() {
+        return new DefaultCachePolicy(this);
+    }
+
+    private abstract static class AbstractResolutionControl<A, B> implements ResolutionControl<A, B> {
+        private final A request;
+        private final B cachedResult;
+        private final long ageMillis;
+        private boolean ruleMatch;
+        private boolean mustCheck;
+
+        private AbstractResolutionControl(A request, B cachedResult, long ageMillis) {
+            this.request = request;
+            this.cachedResult = cachedResult;
+            this.ageMillis = ageMillis;
+        }
+
+        public A getRequest() {
+            return request;
+        }
+
+        public B getCachedResult() {
+            return cachedResult;
+        }
+
+        public void cacheFor(int value, TimeUnit units) {
+            long timeoutMillis = TimeUnit.MILLISECONDS.convert(value, units);
+            if (ageMillis <= timeoutMillis) {
+                setMustCheck(false);
+            } else {
+                setMustCheck(true);
+            }
+        }
+
+        public void useCachedResult() {
+            setMustCheck(false);
+        }
+
+        public void refresh() {
+            setMustCheck(true);
+        }
+
+        private void setMustCheck(boolean val) {
+            ruleMatch = true;
+            mustCheck = val;
+        }
+
+        public boolean ruleMatch() {
+            return ruleMatch;
+        }
+
+        public boolean mustCheck() {
+            return mustCheck;
+        }
+    }
+
+    private class CachedDependencyResolutionControl extends AbstractResolutionControl<ModuleIdentifier, Set<ModuleVersionIdentifier>> implements DependencyResolutionControl {
+        private CachedDependencyResolutionControl(ModuleIdentifier request, Set<ModuleVersionIdentifier> result, long ageMillis) {
+            super(request, result, ageMillis);
+        }
+    }
+
+    private class CachedModuleResolutionControl extends AbstractResolutionControl<ModuleVersionIdentifier, ResolvedModuleVersion> implements ModuleResolutionControl {
+        private final boolean changing;
+
+        private CachedModuleResolutionControl(ModuleVersionIdentifier moduleVersionId, ResolvedModuleVersion cachedVersion, boolean changing, long ageMillis) {
+            super(moduleVersionId, cachedVersion, ageMillis);
+            this.changing = changing;
+        }
+
+        public boolean isChanging() {
+            return changing;
+        }
+    }
+
+    private class CachedArtifactResolutionControl extends AbstractResolutionControl<ArtifactIdentifier, File> implements ArtifactResolutionControl {
+        private final boolean belongsToChangingModule;
+
+        private CachedArtifactResolutionControl(ArtifactIdentifier artifactIdentifier, File cachedResult, long ageMillis, boolean belongsToChangingModule) {
+            super(artifactIdentifier, cachedResult, ageMillis);
+            this.belongsToChangingModule = belongsToChangingModule;
+        }
+
+        public boolean belongsToChangingModule() {
+            return belongsToChangingModule;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRules.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRules.java
new file mode 100644
index 0000000..251b253
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRules.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.api.artifacts.ComponentMetadata;
+import org.gradle.api.artifacts.ComponentSelection;
+import org.gradle.api.artifacts.ComponentSelectionRules;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+import org.gradle.api.internal.notations.ModuleIdentiferNotationConverter;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.rules.*;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+import org.gradle.internal.typeconversion.UnsupportedNotationException;
+
+import java.util.Collection;
+import java.util.Set;
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY;
+
+public class DefaultComponentSelectionRules implements ComponentSelectionRulesInternal {
+    private static final String INVALID_SPEC_ERROR = "Could not add a component selection rule for module '%s'.";
+
+    private MutationValidator mutationValidator = MutationValidator.IGNORE;
+    private final Set<SpecRuleAction<? super ComponentSelection>> rules = Sets.newLinkedHashSet();
+
+    private final RuleActionAdapter<ComponentSelection> ruleActionAdapter;
+    private final NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser;
+
+    public DefaultComponentSelectionRules() {
+        this(createAdapter(), createModuleIdentifierNotationParser());
+    }
+
+    protected DefaultComponentSelectionRules(RuleActionAdapter<ComponentSelection> ruleActionAdapter, NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser) {
+        this.ruleActionAdapter = ruleActionAdapter;
+        this.moduleIdentifierNotationParser = moduleIdentifierNotationParser;
+    }
+
+    /**
+     * Sets the validator to invoke prior to each mutation.
+     */
+    public void beforeChange(MutationValidator mutationValidator) {
+        this.mutationValidator = mutationValidator;
+    }
+
+    private static NotationParser<Object, ModuleIdentifier> createModuleIdentifierNotationParser() {
+        return NotationParserBuilder
+                .toType(ModuleIdentifier.class)
+                .converter(new ModuleIdentiferNotationConverter())
+                .toComposite();
+    }
+
+    private static RuleActionAdapter<ComponentSelection> createAdapter() {
+        RuleActionValidator<ComponentSelection> ruleActionValidator = new DefaultRuleActionValidator<ComponentSelection>(Lists.newArrayList(ComponentMetadata.class, IvyModuleDescriptor.class));
+        return new DefaultRuleActionAdapter<ComponentSelection>(ruleActionValidator, ComponentSelectionRules.class.getSimpleName());
+    }
+
+    public Collection<SpecRuleAction<? super ComponentSelection>> getRules() {
+        return rules;
+    }
+
+    public ComponentSelectionRules all(Action<? super ComponentSelection> selectionAction) {
+        return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromAction(selectionAction)));
+    }
+
+    public ComponentSelectionRules all(Closure<?> closure) {
+        return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromClosure(ComponentSelection.class, closure)));
+    }
+
+    public ComponentSelectionRules all(Object ruleSource) {
+        return addRule(createAllSpecRulesAction(ruleActionAdapter.createFromRuleSource(ComponentSelection.class, ruleSource)));
+    }
+
+    public ComponentSelectionRules withModule(Object id, Action<? super ComponentSelection> selectionAction) {
+        return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromAction(selectionAction)));
+    }
+
+    public ComponentSelectionRules withModule(Object id, Closure<?> closure) {
+        return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromClosure(ComponentSelection.class, closure)));
+    }
+
+    public ComponentSelectionRules withModule(Object id, Object ruleSource) {
+        return addRule(createSpecRuleActionFromId(id, ruleActionAdapter.createFromRuleSource(ComponentSelection.class, ruleSource)));
+    }
+
+    private ComponentSelectionRules addRule(SpecRuleAction<? super ComponentSelection> specRuleAction) {
+        mutationValidator.validateMutation(STRATEGY);
+        rules.add(specRuleAction);
+        return this;
+    }
+
+    private SpecRuleAction<? super ComponentSelection> createSpecRuleActionFromId(Object id, RuleAction<? super ComponentSelection> ruleAction) {
+        final ModuleIdentifier moduleIdentifier;
+
+        try {
+            moduleIdentifier = moduleIdentifierNotationParser.parseNotation(id);
+        } catch (UnsupportedNotationException e) {
+            throw new InvalidUserCodeException(String.format(INVALID_SPEC_ERROR, id == null ? "null" : id.toString()), e);
+        }
+
+        Spec<ComponentSelection> spec = new ComponentSelectionMatchingSpec(moduleIdentifier);
+        return new SpecRuleAction<ComponentSelection>(ruleAction, spec);
+    }
+
+    private SpecRuleAction<? super ComponentSelection> createAllSpecRulesAction(RuleAction<? super ComponentSelection> ruleAction) {
+        return new SpecRuleAction<ComponentSelection>(ruleAction, Specs.<ComponentSelection>satisfyAll());
+    }
+
+    static class ComponentSelectionMatchingSpec implements Spec<ComponentSelection> {
+        final ModuleIdentifier target;
+
+        private ComponentSelectionMatchingSpec(ModuleIdentifier target) {
+            this.target = target;
+        }
+
+        public boolean isSatisfiedBy(ComponentSelection selection) {
+            return selection.getCandidate().getGroup().equals(target.getGroup()) && selection.getCandidate().getModule().equals(target.getName());
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutions.java
new file mode 100644
index 0000000..fe90db4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutions.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy;
+
+import com.google.common.base.Objects;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultDependencyResolveDetails;
+import org.gradle.api.internal.notations.ModuleIdentiferNotationConverter;
+import org.gradle.internal.Actions;
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultDependencySubstitutions implements DependencySubstitutionsInternal {
+    private final Set<Action<? super DependencySubstitution<? super ComponentSelector>>> substitutionRules;
+    private final NotationParser<Object, ModuleIdentifier> moduleIdentifierNotationParser;
+    private final NotationParser<Object, ProjectComponentIdentifier> projectIdentifierNotationParser;
+
+    private MutationValidator mutationValidator = MutationValidator.IGNORE;
+
+    public DefaultDependencySubstitutions() {
+        this(new LinkedHashSet<Action<? super DependencySubstitution<? super ComponentSelector>>>());
+    }
+
+    DefaultDependencySubstitutions(Set<Action<? super DependencySubstitution<? super ComponentSelector>>> substitutionRules) {
+        this.substitutionRules = substitutionRules;
+        this.moduleIdentifierNotationParser = createModuleIdentifierNotationParser();
+        this.projectIdentifierNotationParser = createProjectIdentifierNotationParser();
+    }
+
+    @Override
+    public Action<DependencySubstitution<ComponentSelector>> getDependencySubstitutionRule() {
+        return Actions.composite(substitutionRules);
+    }
+
+    private void addRule(Action<? super DependencySubstitution<? super ComponentSelector>> rule) {
+        mutationValidator.validateMutation(MutationValidator.MutationType.STRATEGY);
+        substitutionRules.add(rule);
+    }
+
+    @Override
+    public DependencySubstitutions all(Action<? super DependencySubstitution<? super ComponentSelector>> rule) {
+        addRule(rule);
+        return this;
+    }
+
+    @Override
+    public DependencySubstitutions allWithDependencyResolveDetails(Action<? super DependencyResolveDetails> rule) {
+        addRule(new DependencyResolveDetailsWrapperAction(rule));
+        return this;
+    }
+
+    @Override
+    public DependencySubstitutions all(Closure<?> action) {
+        return all(ClosureBackedAction.of(action));
+    }
+
+    @Override
+    public DependencySubstitutions eachModule(Action<? super ModuleDependencySubstitution> rule) {
+        return all(new TypeFilteringDependencySubstitutionAction<ModuleDependencySubstitution>(ModuleDependencySubstitution.class, rule));
+    }
+
+    @Override
+    public DependencySubstitutions eachModule(Closure<?> rule) {
+        return eachModule(ClosureBackedAction.of(rule));
+    }
+
+    @Override
+    public DependencySubstitutions withModule(Object id, Action<? super ModuleDependencySubstitution> rule) {
+        ModuleIdentifier moduleId = moduleIdentifierNotationParser.parseNotation(id);
+        return all(new ModuleIdFilteringDependencySubstitutionAction(moduleId, rule));
+    }
+
+    @Override
+    public DependencySubstitutions withModule(Object id, Closure<?> action) {
+        return withModule(id, ClosureBackedAction.of(action));
+    }
+
+    @Override
+    public DependencySubstitutions eachProject(Action<? super ProjectDependencySubstitution> rule) {
+        return all(new TypeFilteringDependencySubstitutionAction<ProjectDependencySubstitution>(ProjectDependencySubstitution.class, rule));
+    }
+
+    @Override
+    public DependencySubstitutions eachProject(Closure<?> rule) {
+        return eachProject(ClosureBackedAction.of(rule));
+    }
+
+    @Override
+    public DependencySubstitutions withProject(Object id, Action<? super ProjectDependencySubstitution> rule) {
+        ProjectComponentIdentifier componentId = projectIdentifierNotationParser.parseNotation(id);
+        return all(new ProjectIdFilteringDependencySubstitutionAction(componentId, rule));
+    }
+
+    @Override
+    public DependencySubstitutions withProject(Object id, Closure<?> rule) {
+        return withProject(id, ClosureBackedAction.of(rule));
+    }
+
+    @Override
+    public void beforeChange(MutationValidator validator) {
+        mutationValidator = validator;
+    }
+
+    @Override
+    public DependencySubstitutionsInternal copy() {
+        return new DefaultDependencySubstitutions(new LinkedHashSet<Action<? super DependencySubstitution<? super ComponentSelector>>>(substitutionRules));
+    }
+
+    private static NotationParser<Object, ModuleIdentifier> createModuleIdentifierNotationParser() {
+        return NotationParserBuilder
+                .toType(ModuleIdentifier.class)
+                .converter(new ModuleIdentiferNotationConverter())
+                .converter(ModuleIdentifierMapNotationConverter.getInstance())
+                .toComposite();
+    }
+
+    private static class ModuleIdentifierMapNotationConverter extends MapNotationConverter<ModuleIdentifier> {
+
+        private final static ModuleIdentifierMapNotationConverter INSTANCE = new ModuleIdentifierMapNotationConverter();
+
+        public static ModuleIdentifierMapNotationConverter getInstance() {
+            return INSTANCE;
+        }
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("Maps, e.g. [group: 'org.gradle', name:'gradle-core'].");
+        }
+
+        protected ModuleIdentifier parseMap(@MapKey("group") String group, @MapKey("name") String name) {
+            return DefaultModuleIdentifier.newId(group, name);
+        }
+    }
+
+    private static NotationParser<Object, ProjectComponentIdentifier> createProjectIdentifierNotationParser() {
+        return NotationParserBuilder
+                .toType(ProjectComponentIdentifier.class)
+                .fromCharSequence(new ProjectPathConverter())
+                .fromType(Project.class, new ProjectConverter())
+                .toComposite();
+    }
+
+    private static class ProjectPathConverter implements NotationConverter<String, ProjectComponentIdentifier> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("Project paths, e.g. ':api'.");
+        }
+
+        @Override
+        public void convert(String notation, NotationConvertResult<? super ProjectComponentIdentifier> result) throws TypeConversionException {
+            result.converted(DefaultProjectComponentIdentifier.newId(notation));
+        }
+    }
+
+    private static class ProjectConverter implements NotationConverter<Project, ProjectComponentIdentifier> {
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.example("Project objects, e.g. project(':api').");
+        }
+
+        @Override
+        public void convert(Project notation, NotationConvertResult<? super ProjectComponentIdentifier> result) throws TypeConversionException {
+            result.converted(DefaultProjectComponentIdentifier.newId(notation.getPath()));
+        }
+    }
+
+    private static class ModuleIdFilteringDependencySubstitutionAction implements Action<DependencySubstitution<ComponentSelector>> {
+        private final Action<? super ModuleDependencySubstitution> delegate;
+        private final ModuleIdentifier id;
+
+        public ModuleIdFilteringDependencySubstitutionAction(ModuleIdentifier id, Action<? super ModuleDependencySubstitution> delegate) {
+            this.id = id;
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void execute(DependencySubstitution substitution) {
+            ComponentSelector requested = substitution.getRequested();
+            if (requested instanceof ModuleComponentSelector) {
+                ModuleComponentSelector requestedModule = (ModuleComponentSelector) requested;
+                if (Objects.equal(requestedModule.getGroup(), id.getGroup())
+                        && Objects.equal(requestedModule.getModule(), id.getName())) {
+                    delegate.execute((ModuleDependencySubstitution) substitution);
+                }
+            }
+        }
+    }
+
+    private static class ProjectIdFilteringDependencySubstitutionAction implements Action<DependencySubstitution<ComponentSelector>> {
+        private final Action<? super ProjectDependencySubstitution> delegate;
+        private final ProjectComponentIdentifier id;
+
+        public ProjectIdFilteringDependencySubstitutionAction(ProjectComponentIdentifier id, Action<? super ProjectDependencySubstitution> delegate) {
+            this.id = id;
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void execute(DependencySubstitution substitution) {
+            ComponentSelector requested = substitution.getRequested();
+            if (requested.matchesStrictly(id)) {
+                delegate.execute((ProjectDependencySubstitution) substitution);
+            }
+        }
+    }
+
+    private static class TypeFilteringDependencySubstitutionAction<T extends DependencySubstitution<?>> implements Action<DependencySubstitution<ComponentSelector>> {
+        private final Class<T> type;
+        private final Action<? super T> delegate;
+
+        public TypeFilteringDependencySubstitutionAction(Class<T> type, Action<? super T> delegate) {
+            this.type = type;
+            this.delegate = delegate;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public void execute(DependencySubstitution<ComponentSelector> substitution) {
+            if (type.isAssignableFrom(substitution.getClass())) {
+                delegate.execute((T) substitution);
+            }
+        }
+    }
+
+    private static class DependencyResolveDetailsWrapperAction implements Action<DependencySubstitution<? extends ComponentSelector>> {
+        private final Action<? super DependencyResolveDetails> delegate;
+
+        public DependencyResolveDetailsWrapperAction(Action<? super DependencyResolveDetails> delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void execute(DependencySubstitution<? extends ComponentSelector> substitution) {
+            DefaultDependencyResolveDetails details = new DefaultDependencyResolveDetails((DependencySubstitutionInternal<?>) substitution);
+            delegate.execute(details);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultExternalResourceCachePolicy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultExternalResourceCachePolicy.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultExternalResourceCachePolicy.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultExternalResourceCachePolicy.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
new file mode 100644
index 0000000..009d778
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.cache.ResolutionRules;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
+import org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers;
+import org.gradle.internal.Actions;
+import org.gradle.internal.typeconversion.NormalizedTimeUnit;
+import org.gradle.internal.typeconversion.TimeUnitsParser;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY;
+import static org.gradle.util.GUtil.flattenElements;
+
+public class DefaultResolutionStrategy implements ResolutionStrategyInternal {
+    private final Set<ModuleVersionSelector> forcedModules = new LinkedHashSet<ModuleVersionSelector>();
+    private ConflictResolution conflictResolution = new LatestConflictResolution();
+    private final DefaultComponentSelectionRules componentSelectionRules = new DefaultComponentSelectionRules();
+
+    private final DefaultCachePolicy cachePolicy;
+    private final DependencySubstitutionsInternal dependencySubstitutions;
+    private MutationValidator mutationValidator = MutationValidator.IGNORE;
+
+    public DefaultResolutionStrategy() {
+        this(new DefaultCachePolicy(), new DefaultDependencySubstitutions());
+    }
+
+    DefaultResolutionStrategy(DefaultCachePolicy cachePolicy, DependencySubstitutionsInternal dependencySubstitutions) {
+        this.cachePolicy = cachePolicy;
+        this.dependencySubstitutions = dependencySubstitutions;
+    }
+
+    @Override
+    public void beforeChange(MutationValidator validator) {
+        mutationValidator = validator;
+        cachePolicy.beforeChange(validator);
+        componentSelectionRules.beforeChange(validator);
+        dependencySubstitutions.beforeChange(validator);
+    }
+
+    public Set<ModuleVersionSelector> getForcedModules() {
+        return Collections.unmodifiableSet(forcedModules);
+    }
+
+    public ResolutionStrategy failOnVersionConflict() {
+        mutationValidator.validateMutation(STRATEGY);
+        this.conflictResolution = new StrictConflictResolution();
+        return this;
+    }
+
+    public ConflictResolution getConflictResolution() {
+        return this.conflictResolution;
+    }
+
+    public ResolutionRules getResolutionRules() {
+        return cachePolicy;
+    }
+
+    public DefaultResolutionStrategy force(Object... moduleVersionSelectorNotations) {
+        mutationValidator.validateMutation(STRATEGY);
+        Set<ModuleVersionSelector> modules = ModuleVersionSelectorParsers.multiParser().parseNotation(moduleVersionSelectorNotations);
+        this.forcedModules.addAll(modules);
+        return this;
+    }
+
+    public ResolutionStrategy eachDependency(Action<? super DependencyResolveDetails> rule) {
+        mutationValidator.validateMutation(STRATEGY);
+        dependencySubstitutions.allWithDependencyResolveDetails(rule);
+        return this;
+    }
+
+    public Action<DependencySubstitution<ComponentSelector>> getDependencySubstitutionRule() {
+        Collection<Action<DependencySubstitution<ComponentSelector>>> allRules = flattenElements(new ModuleForcingResolveRule(forcedModules), dependencySubstitutions.getDependencySubstitutionRule());
+        return Actions.composite(allRules);
+    }
+
+    public DefaultResolutionStrategy setForcedModules(Object ... moduleVersionSelectorNotations) {
+        mutationValidator.validateMutation(STRATEGY);
+        Set<ModuleVersionSelector> modules = ModuleVersionSelectorParsers.multiParser().parseNotation(moduleVersionSelectorNotations);
+        this.forcedModules.clear();
+        this.forcedModules.addAll(modules);
+        return this;
+    }
+
+    public DefaultCachePolicy getCachePolicy() {
+        return cachePolicy;
+    }
+
+    public void cacheDynamicVersionsFor(int value, String units) {
+        NormalizedTimeUnit timeUnit = new TimeUnitsParser().parseNotation(units, value);
+        cacheDynamicVersionsFor(timeUnit.getValue(), timeUnit.getTimeUnit());
+    }
+
+    public void cacheDynamicVersionsFor(int value, TimeUnit units) {
+        this.cachePolicy.cacheDynamicVersionsFor(value, units);
+    }
+
+    public void cacheChangingModulesFor(int value, String units) {
+        NormalizedTimeUnit timeUnit = new TimeUnitsParser().parseNotation(units, value);
+        cacheChangingModulesFor(timeUnit.getValue(), timeUnit.getTimeUnit());
+    }
+
+    public void cacheChangingModulesFor(int value, TimeUnit units) {
+        this.cachePolicy.cacheChangingModulesFor(value, units);
+    }
+
+    public ComponentSelectionRulesInternal getComponentSelection() {
+        return componentSelectionRules;
+    }
+
+    public ResolutionStrategy componentSelection(Action<? super ComponentSelectionRules> action) {
+        action.execute(componentSelectionRules);
+        return this;
+    }
+
+    public DependencySubstitutionsInternal getDependencySubstitution() {
+        return dependencySubstitutions;
+    }
+
+    public ResolutionStrategy dependencySubstitution(Action<? super DependencySubstitutions> action) {
+        action.execute(dependencySubstitutions);
+        return this;
+    }
+
+    public DefaultResolutionStrategy copy() {
+        DefaultResolutionStrategy out = new DefaultResolutionStrategy(cachePolicy.copy(), dependencySubstitutions.copy());
+
+        if (conflictResolution instanceof StrictConflictResolution) {
+            out.failOnVersionConflict();
+        }
+        out.setForcedModules(getForcedModules());
+        out.getComponentSelection().getRules().addAll(componentSelectionRules.getRules());
+        return out;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DependencySubstitutionsInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DependencySubstitutionsInternal.java
new file mode 100644
index 0000000..126bc9a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DependencySubstitutionsInternal.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.DependencyResolveDetails;
+import org.gradle.api.artifacts.DependencySubstitution;
+import org.gradle.api.artifacts.DependencySubstitutions;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+
+public interface DependencySubstitutionsInternal extends DependencySubstitutions {
+    Action<DependencySubstitution<ComponentSelector>> getDependencySubstitutionRule();
+
+    DependencySubstitutions allWithDependencyResolveDetails(Action<? super DependencyResolveDetails> rule);
+
+    void beforeChange(MutationValidator validator);
+
+    DependencySubstitutionsInternal copy();
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ExternalResourceCachePolicy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ExternalResourceCachePolicy.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ExternalResourceCachePolicy.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ExternalResourceCachePolicy.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/LatestConflictResolution.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
new file mode 100644
index 0000000..68ed461
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRule.java
@@ -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.api.internal.artifacts.ivyservice.resolutionstrategy;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.ModuleDependencySubstitutionInternal;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ModuleForcingResolveRule implements Action<DependencySubstitutionInternal<? extends ComponentSelector>> {
+
+    private final Map<ModuleIdentifier, String> forcedModules;
+
+    public ModuleForcingResolveRule(Collection<? extends ModuleVersionSelector> forcedModules) {
+        if (!forcedModules.isEmpty()) {
+            this.forcedModules = new HashMap<ModuleIdentifier, String>();
+            for (ModuleVersionSelector module : forcedModules) {
+                this.forcedModules.put(new DefaultModuleIdentifier(module.getGroup(), module.getName()), module.getVersion());
+            }
+        } else {
+            this.forcedModules = null;
+        }
+    }
+
+    @Override
+    public void execute(DependencySubstitutionInternal<? extends ComponentSelector> details) {
+        if (forcedModules == null) {
+            return;
+        }
+        ModuleIdentifier key = new DefaultModuleIdentifier(details.getOldRequested().getGroup(), details.getOldRequested().getName());
+        if (forcedModules.containsKey(key) && details instanceof ModuleDependencySubstitutionInternal) {
+            ((ModuleDependencySubstitutionInternal) details).useVersion(forcedModules.get(key), VersionSelectionReasons.FORCED);
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/StrictConflictResolution.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.java
new file mode 100644
index 0000000..3ece65a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolutionState.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.api.internal.artifacts.ivyservice.resolveengine;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+
+public interface ComponentResolutionState extends Versioned {
+    ModuleVersionIdentifier getId();
+
+    /**
+     * Returns the meta-data for the component. Resolves if not already resolved.
+     *
+     * @return null if the meta-data is not available due to some failure.
+     */
+    @Nullable
+    ComponentResolveMetaData getMetaData();
+
+    ComponentSelectionReason getSelectionReason();
+
+    void setSelectionReason(ComponentSelectionReason componentSelectionReason);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
new file mode 100755
index 0000000..293dba4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyResolver.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Ivy;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.result.ResolvedComponentResult;
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.ResolverResults;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
+import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectComponentRegistry;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.StrictConflictResolution;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.ConflictHandler;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultConflictHandler;
+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.TransientConfigurationResultsBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.DefaultResolvedProjectConfigurationResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.StreamingResolutionResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.StoreSet;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.internal.cache.BinaryStore;
+import org.gradle.api.internal.cache.Store;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class DefaultDependencyResolver implements ArtifactDependencyResolver {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDependencyResolver.class);
+    private final LocalComponentFactory localComponentFactory;
+    private final DependencyDescriptorFactory dependencyDescriptorFactory;
+    private final ResolveIvyFactory ivyFactory;
+    private final ProjectComponentRegistry projectComponentRegistry;
+    private final CacheLockingManager cacheLockingManager;
+    private final IvyContextManager ivyContextManager;
+    private final ResolutionResultsStoreFactory storeFactory;
+    private final VersionComparator versionComparator;
+
+    public DefaultDependencyResolver(ResolveIvyFactory ivyFactory, LocalComponentFactory localComponentFactory, DependencyDescriptorFactory dependencyDescriptorFactory,
+                                     ProjectComponentRegistry projectComponentRegistry, CacheLockingManager cacheLockingManager, IvyContextManager ivyContextManager,
+                                     ResolutionResultsStoreFactory storeFactory, VersionComparator versionComparator) {
+        this.ivyFactory = ivyFactory;
+        this.localComponentFactory = localComponentFactory;
+        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+        this.projectComponentRegistry = projectComponentRegistry;
+        this.cacheLockingManager = cacheLockingManager;
+        this.ivyContextManager = ivyContextManager;
+        this.storeFactory = storeFactory;
+        this.versionComparator = versionComparator;
+    }
+
+    public void resolve(final ConfigurationInternal configuration,
+                        final List<? extends ResolutionAwareRepository> repositories,
+                        final GlobalDependencyResolutionRules metadataHandler,
+                        final ResolverResults results) throws ResolveException {
+        LOGGER.debug("Resolving {}", configuration);
+        ivyContextManager.withIvy(new Action<Ivy>() {
+            public void execute(Ivy ivy) {
+                RepositoryChain repositoryChain = ivyFactory.create(configuration, repositories, metadataHandler.getComponentMetadataProcessor());
+
+                ComponentMetaDataResolver metaDataResolver = new ClientModuleResolver(repositoryChain.getComponentMetaDataResolver(), dependencyDescriptorFactory);
+
+                ProjectDependencyResolver projectDependencyResolver = new ProjectDependencyResolver(projectComponentRegistry, localComponentFactory, repositoryChain.getComponentIdResolver());
+                ResolutionStrategyInternal resolutionStrategy = configuration.getResolutionStrategy();
+                DependencyToComponentIdResolver idResolver = new DependencySubstitutionResolver(projectDependencyResolver, resolutionStrategy.getDependencySubstitutionRule());
+
+                ArtifactResolver artifactResolver = createArtifactResolver(repositoryChain);
+
+                ModuleConflictResolver conflictResolver;
+                if (resolutionStrategy.getConflictResolution() instanceof StrictConflictResolution) {
+                    conflictResolver = new StrictConflictResolver();
+                } else {
+                    conflictResolver = new LatestModuleConflictResolver(versionComparator);
+                }
+                conflictResolver = new VersionSelectionReasonResolver(conflictResolver);
+                ConflictHandler conflictHandler = new DefaultConflictHandler(conflictResolver, metadataHandler.getModuleMetadataProcessor().getModuleReplacements());
+
+                DependencyGraphBuilder builder = new DependencyGraphBuilder(idResolver, metaDataResolver, projectDependencyResolver, artifactResolver, conflictHandler, new DefaultDependencyToConfigurationResolver());
+
+                StoreSet stores = storeFactory.createStoreSet();
+
+                BinaryStore newModelStore = stores.nextBinaryStore();
+                Store<ResolvedComponentResult> newModelCache = stores.oldModelStore();
+                ResolutionResultBuilder newModelBuilder = new StreamingResolutionResultBuilder(newModelStore, newModelCache);
+
+                BinaryStore oldModelStore = stores.nextBinaryStore();
+                Store<TransientConfigurationResults> oldModelCache = stores.newModelStore();
+                TransientConfigurationResultsBuilder oldTransientModelBuilder = new TransientConfigurationResultsBuilder(oldModelStore, oldModelCache);
+                DefaultResolvedConfigurationBuilder oldModelBuilder = new DefaultResolvedConfigurationBuilder(oldTransientModelBuilder);
+                ResolvedProjectConfigurationResultBuilder projectModelBuilder = new DefaultResolvedProjectConfigurationResultBuilder();
+
+                builder.resolve(configuration, newModelBuilder, oldModelBuilder, projectModelBuilder);
+                DefaultLenientConfiguration result = new DefaultLenientConfiguration(configuration, oldModelBuilder, cacheLockingManager);
+                results.resolved(new DefaultResolvedConfiguration(result), newModelBuilder.complete(), projectModelBuilder.complete());
+            }
+        });
+    }
+
+    private ArtifactResolver createArtifactResolver(RepositoryChain repositoryChain) {
+        ArtifactResolver artifactResolver = repositoryChain.getArtifactResolver();
+        artifactResolver = new ProjectArtifactResolver(artifactResolver);
+        artifactResolver = new ContextualArtifactResolver(cacheLockingManager, ivyContextManager, artifactResolver);
+        artifactResolver = new ErrorHandlingArtifactResolver(artifactResolver);
+        return artifactResolver;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java
new file mode 100644
index 0000000..16bde3c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultDependencyToConfigurationResolver.java
@@ -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.api.internal.artifacts.ivyservice.resolveengine;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ConfigurationMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+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, ComponentResolveMetaData targetComponent) {
+        // TODO - resolve directly to config meta data
+        ModuleDescriptor targetDescriptor = targetComponent.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.getComponent().getId(), fromConfiguration.getName(),
+                        targetConfigurationName, targetComponent.getId()));
+            }
+            ConfigurationMetaData targetConfiguration = targetComponent.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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilter.java
new file mode 100644
index 0000000..2785f6e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilter.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ExcludeRule;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.util.*;
+
+/**
+ * Manages sets of exclude rules, allowing union and intersection operations on the rules.
+ *
+ * <p>This class attempts to reduce execution time, by flattening union and intersection specs, at the cost of more analysis at construction time. This is taken advantage of by {@link
+ * org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder}, on the assumption that there are many more edges in the dependency graph than there are exclude rules (ie we evaluate the rules much more often that we construct them).
+ * </p>
+ *
+ * <p>Also, this class attempts to be quite accurate in determining if 2 specs will match exactly the same set of modules. {@link org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder} uses this to avoid traversing the
+ * dependency graph of a particular version that has already been traversed when a new incoming edge is added (eg a newly discovered dependency) and when an incoming edge is removed (eg a conflict
+ * evicts a version that depends on the given version). </p>
+ */
+public abstract class DefaultModuleResolutionFilter implements ModuleResolutionFilter {
+    private static final AcceptAllSpec ALL_SPEC = new AcceptAllSpec();
+    private static final String WILDCARD = "*";
+
+    /**
+     * Returns a spec that accepts everything.
+     */
+    public static ModuleResolutionFilter all() {
+        return ALL_SPEC;
+    }
+
+    /**
+     * Returns a spec that accepts only those module versions that do not match any of the given exclude rules.
+     */
+    public static ModuleResolutionFilter excludeAny(ExcludeRule... excludeRules) {
+        if (excludeRules.length == 0) {
+            return ALL_SPEC;
+        }
+        return new ExcludeRuleBackedSpec(Arrays.asList(excludeRules));
+    }
+
+    /**
+     * Returns a spec that accepts only those module versions that do not match any of the given exclude rules.
+     */
+    public static ModuleResolutionFilter excludeAny(Collection<ExcludeRule> excludeRules) {
+        if (excludeRules.isEmpty()) {
+            return ALL_SPEC;
+        }
+        return new ExcludeRuleBackedSpec(excludeRules);
+    }
+
+    private static boolean isWildcard(String attribute) {
+        return WILDCARD.equals(attribute);
+    }
+
+    public ModuleResolutionFilter union(ModuleResolutionFilter other) {
+        if (other == this) {
+            return this;
+        }
+        if (other == ALL_SPEC) {
+            return other;
+        }
+        if (this == ALL_SPEC) {
+            return this;
+        }
+        List<DefaultModuleResolutionFilter> specs = new ArrayList<DefaultModuleResolutionFilter>();
+        unpackUnion(specs);
+        ((DefaultModuleResolutionFilter) other).unpackUnion(specs);
+        for (int i = 0; i < specs.size();) {
+            DefaultModuleResolutionFilter spec = specs.get(i);
+            DefaultModuleResolutionFilter merged = null;
+            for (int j = i + 1; j < specs.size(); j++) {
+                merged = spec.doUnion(specs.get(j));
+                if (merged != null) {
+                    specs.remove(j);
+                    break;
+                }
+            }
+            if (merged != null) {
+                specs.set(i, merged);
+            } else {
+                i++;
+            }
+        }
+        if (specs.size() == 1) {
+            return specs.get(0);
+        }
+        return new UnionSpec(specs);
+    }
+
+    protected void unpackUnion(Collection<DefaultModuleResolutionFilter> specs) {
+        specs.add(this);
+    }
+
+    /**
+     * Returns the union of this filter and the given filter. Returns null if not recognized.
+     */
+    protected DefaultModuleResolutionFilter doUnion(DefaultModuleResolutionFilter other) {
+        return null;
+    }
+
+    public final boolean acceptsSameModulesAs(ModuleResolutionFilter filter) {
+        if (filter == this) {
+            return true;
+        }
+        DefaultModuleResolutionFilter other = (DefaultModuleResolutionFilter) filter;
+        boolean thisAcceptsEverything = acceptsAllModules();
+        boolean otherAcceptsEverything = other.acceptsAllModules();
+        if (thisAcceptsEverything && otherAcceptsEverything) {
+            return true;
+        }
+        if (thisAcceptsEverything ^ otherAcceptsEverything) {
+            return false;
+        }
+        if (!other.getClass().equals(getClass())) {
+            return false;
+        }
+        return doAcceptsSameModulesAs(other);
+    }
+
+    /**
+     * Only called when this and the other spec have the same class.
+     */
+    protected boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+        return false;
+    }
+
+    protected boolean acceptsAllModules() {
+        return false;
+    }
+
+    /**
+     * Returns a spec that accepts the intersection of those module versions that are accepted by this spec and the given spec.
+     */
+    public ModuleResolutionFilter intersect(ModuleResolutionFilter other) {
+        if (other == this) {
+            return this;
+        }
+        if (other == ALL_SPEC) {
+            return this;
+        }
+        if (this == ALL_SPEC) {
+            return other;
+        }
+
+        List<DefaultModuleResolutionFilter> specs = new ArrayList<DefaultModuleResolutionFilter>();
+        unpackIntersection(specs);
+        ((DefaultModuleResolutionFilter) other).unpackIntersection(specs);
+
+        return new ExcludeRuleBackedSpec(specs);
+    }
+
+    protected void unpackIntersection(Collection<DefaultModuleResolutionFilter> specs) {
+        specs.add(this);
+    }
+
+    private static class AcceptAllSpec extends DefaultModuleResolutionFilter {
+        @Override
+        public String toString() {
+            return "{accept-all}";
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            return true;
+        }
+
+        @Override
+        protected boolean acceptsAllModules() {
+            return true;
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return true;
+        }
+    }
+
+    private static abstract class CompositeSpec extends DefaultModuleResolutionFilter {
+        abstract Collection<DefaultModuleResolutionFilter> getSpecs();
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("{");
+            builder.append(getClass().getSimpleName());
+            for (DefaultModuleResolutionFilter spec : getSpecs()) {
+                builder.append(' ');
+                builder.append(spec);
+            }
+            builder.append("}");
+            return builder.toString();
+        }
+
+        @Override
+        protected boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            CompositeSpec spec = (CompositeSpec) other;
+            return implies(spec) && spec.implies(this);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+            CompositeSpec other = (CompositeSpec) obj;
+            return getSpecs().equals(other.getSpecs());
+        }
+
+        @Override
+        public int hashCode() {
+            return getSpecs().hashCode();
+        }
+
+        /**
+         * Returns true if for every spec in this spec, there is a corresponding spec in the given spec that acceptsSameModulesAs().
+         */
+        protected boolean implies(CompositeSpec spec) {
+            for (DefaultModuleResolutionFilter thisSpec : getSpecs()) {
+                boolean found = false;
+                for (DefaultModuleResolutionFilter otherSpec : spec.getSpecs()) {
+                    if (thisSpec.acceptsSameModulesAs(otherSpec)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private static class ExcludeRuleBackedSpec extends CompositeSpec {
+        private final Set<DefaultModuleResolutionFilter> excludeSpecs = new HashSet<DefaultModuleResolutionFilter>();
+
+        private ExcludeRuleBackedSpec(Iterable<ExcludeRule> excludeRules) {
+            for (ExcludeRule rule : excludeRules) {
+
+                if (!(rule.getMatcher() instanceof ExactPatternMatcher)) {
+                    excludeSpecs.add(new ExcludeRuleSpec(rule));
+                    continue;
+                }
+
+                ArtifactId artifactId = rule.getId();
+                ModuleId moduleId = artifactId.getModuleId();
+                boolean anyOrganisation = isWildcard(moduleId.getOrganisation());
+                boolean anyModule = isWildcard(moduleId.getName());
+                boolean anyArtifact = isWildcard(artifactId.getName()) && isWildcard(artifactId.getType()) && isWildcard(artifactId.getExt());
+
+                if (anyArtifact) {
+                    if (!anyOrganisation && !anyModule) {
+                        excludeSpecs.add(new ModuleIdSpec(moduleId.getOrganisation(), moduleId.getName()));
+                    } else if (!anyModule) {
+                        excludeSpecs.add(new ModuleNameSpec(moduleId.getName()));
+                    } else if (!anyOrganisation) {
+                        excludeSpecs.add(new GroupNameSpec(moduleId.getOrganisation()));
+                    } else {
+                        excludeSpecs.add(new ExcludeAllModulesSpec());
+                    }
+                } else {
+                    excludeSpecs.add(new ArtifactSpec(rule));
+                }
+            }
+        }
+
+        public ExcludeRuleBackedSpec(Collection<DefaultModuleResolutionFilter> specs) {
+            this.excludeSpecs.addAll(specs);
+        }
+
+        @Override
+        Collection<DefaultModuleResolutionFilter> getSpecs() {
+            return excludeSpecs;
+        }
+
+        @Override
+        protected void unpackIntersection(Collection<DefaultModuleResolutionFilter> specs) {
+            specs.addAll(excludeSpecs);
+        }
+
+        @Override
+        protected boolean acceptsAllModules() {
+            for (DefaultModuleResolutionFilter excludeSpec : excludeSpecs) {
+                if (!excludeSpec.acceptsAllModules()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            for (DefaultModuleResolutionFilter excludeSpec : excludeSpecs) {
+                if (!excludeSpec.acceptModule(element)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            for (DefaultModuleResolutionFilter excludeSpec : excludeSpecs) {
+                if (!excludeSpec.acceptArtifact(module, artifact)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        protected DefaultModuleResolutionFilter doUnion(DefaultModuleResolutionFilter other) {
+            if (!(other instanceof ExcludeRuleBackedSpec)) {
+                return super.doUnion(other);
+            }
+
+            ExcludeRuleBackedSpec excludeRuleBackedSpec = (ExcludeRuleBackedSpec) other;
+            if (excludeSpecs.equals(excludeRuleBackedSpec.excludeSpecs)) {
+                return this;
+            }
+
+            // Can only merge exact match rules, so don't try if this or the other spec contains any other type of rule
+            for (DefaultModuleResolutionFilter excludeSpec : excludeSpecs) {
+                if (excludeSpec instanceof ExcludeRuleSpec) {
+                    return super.doUnion(other);
+                }
+            }
+            for (DefaultModuleResolutionFilter excludeSpec : excludeRuleBackedSpec.excludeSpecs) {
+                if (excludeSpec instanceof ExcludeRuleSpec) {
+                    return super.doUnion(other);
+                }
+            }
+
+            // Calculate the intersection of the rules
+            List<DefaultModuleResolutionFilter> merged = new ArrayList<DefaultModuleResolutionFilter>();
+            for (DefaultModuleResolutionFilter thisSpec : excludeSpecs) {
+                for (DefaultModuleResolutionFilter otherSpec : excludeRuleBackedSpec.excludeSpecs) {
+                    intersect(thisSpec, otherSpec, merged);
+                }
+            }
+            if (merged.isEmpty()) {
+                return ALL_SPEC;
+            }
+            return new ExcludeRuleBackedSpec(merged);
+        }
+
+        private void intersect(DefaultModuleResolutionFilter spec1, DefaultModuleResolutionFilter spec2, List<DefaultModuleResolutionFilter> merged) {
+            if (spec1 instanceof ArtifactSpec) {
+                merged.add(spec1);
+            } else if (spec2 instanceof ArtifactSpec) {
+                merged.add(spec2);
+            } else if (spec1 instanceof GroupNameSpec) {
+                intersect((GroupNameSpec) spec1, spec2, merged);
+            } else if (spec2 instanceof GroupNameSpec) {
+                intersect((GroupNameSpec) spec2, spec1, merged);
+            } else if (spec1 instanceof ModuleNameSpec) {
+                intersect((ModuleNameSpec) spec1, spec2, merged);
+            } else if (spec2 instanceof ModuleNameSpec) {
+                intersect((ModuleNameSpec) spec2, spec1, merged);
+            } else if ((spec1 instanceof ModuleIdSpec) && (spec2 instanceof ModuleIdSpec)) {
+                ModuleIdSpec moduleSpec1 = (ModuleIdSpec) spec1;
+                ModuleIdSpec moduleSpec2 = (ModuleIdSpec) spec2;
+                if (moduleSpec1.moduleId.equals(moduleSpec2.moduleId)) {
+                    merged.add(moduleSpec1);
+                }
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        private void intersect(GroupNameSpec spec1, DefaultModuleResolutionFilter spec2, List<DefaultModuleResolutionFilter> merged) {
+            if (spec2 instanceof GroupNameSpec) {
+                GroupNameSpec groupNameSpec = (GroupNameSpec) spec2;
+                if (spec1.group.equals(groupNameSpec.group)) {
+                    merged.add(spec1);
+                }
+            } else if (spec2 instanceof ModuleNameSpec) {
+                ModuleNameSpec moduleNameSpec = (ModuleNameSpec) spec2;
+                merged.add(new ModuleIdSpec(spec1.group, moduleNameSpec.module));
+            } else if (spec2 instanceof ModuleIdSpec) {
+                ModuleIdSpec moduleIdSpec = (ModuleIdSpec) spec2;
+                if (moduleIdSpec.moduleId.getGroup().equals(spec1.group)) {
+                    merged.add(spec2);
+                }
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        private void intersect(ModuleNameSpec spec1, DefaultModuleResolutionFilter spec2, List<DefaultModuleResolutionFilter> merged) {
+            if (spec2 instanceof ModuleNameSpec) {
+                ModuleNameSpec moduleNameSpec = (ModuleNameSpec) spec2;
+                if (spec1.module.equals(moduleNameSpec.module)) {
+                    merged.add(spec1);
+                }
+            } else if (spec2 instanceof ModuleIdSpec) {
+                ModuleIdSpec moduleIdSpec = (ModuleIdSpec) spec2;
+                if (moduleIdSpec.moduleId.getName().equals(spec1.module)) {
+                    merged.add(spec2);
+                }
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    private static class UnionSpec extends CompositeSpec {
+        private final List<DefaultModuleResolutionFilter> specs;
+
+        public UnionSpec(List<DefaultModuleResolutionFilter> specs) {
+            this.specs = specs;
+        }
+
+        @Override
+        Collection<DefaultModuleResolutionFilter> getSpecs() {
+            return specs;
+        }
+
+        @Override
+        protected void unpackUnion(Collection<DefaultModuleResolutionFilter> specs) {
+            specs.addAll(this.specs);
+        }
+
+        @Override
+        protected boolean acceptsAllModules() {
+            for (DefaultModuleResolutionFilter excludeSpec : specs) {
+                if (excludeSpec.acceptsAllModules()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            for (DefaultModuleResolutionFilter spec : specs) {
+                if (spec.acceptModule(element)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            for (DefaultModuleResolutionFilter spec : specs) {
+                if (spec.acceptArtifact(module, artifact)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    private static class ModuleIdSpec extends DefaultModuleResolutionFilter {
+        private final ModuleIdentifier moduleId;
+
+        public ModuleIdSpec(String group, String name) {
+            this.moduleId = DefaultModuleIdentifier.newId(group, name);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{module-id %s}", moduleId);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || o.getClass() != getClass()) {
+                return false;
+            }
+            ModuleIdSpec other = (ModuleIdSpec) o;
+            return moduleId.equals(other.moduleId);
+        }
+
+        @Override
+        public int hashCode() {
+            return moduleId.hashCode();
+        }
+
+        @Override
+        protected boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            ModuleIdSpec moduleIdSpec = (ModuleIdSpec) other;
+            return moduleId.equals(moduleIdSpec.moduleId);
+        }
+
+        public boolean acceptModule(ModuleIdentifier module) {
+            return !module.equals(moduleId);
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return true;
+        }
+    }
+
+    private static class ModuleNameSpec extends DefaultModuleResolutionFilter {
+        private final String module;
+
+        private ModuleNameSpec(String module) {
+            this.module = module;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{module %s}", module);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || o.getClass() != getClass()) {
+                return false;
+            }
+            ModuleNameSpec other = (ModuleNameSpec) o;
+            return module.equals(other.module);
+        }
+
+        @Override
+        public int hashCode() {
+            return module.hashCode();
+        }
+
+        @Override
+        public boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            ModuleNameSpec moduleNameSpec = (ModuleNameSpec) other;
+            return module.equals(moduleNameSpec.module);
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            return !element.getName().equals(module);
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return true;
+        }
+    }
+
+    private static class GroupNameSpec extends DefaultModuleResolutionFilter {
+        private final String group;
+
+        private GroupNameSpec(String group) {
+            this.group = group;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{group %s}", group);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || o.getClass() != getClass()) {
+                return false;
+            }
+            GroupNameSpec other = (GroupNameSpec) o;
+            return group.equals(other.group);
+        }
+
+        @Override
+        public int hashCode() {
+            return group.hashCode();
+        }
+
+        @Override
+        public boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            GroupNameSpec groupNameSpec = (GroupNameSpec) other;
+            return group.equals(groupNameSpec.group);
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            return !element.getGroup().equals(group);
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return true;
+        }
+    }
+
+    private static class ExcludeAllModulesSpec extends DefaultModuleResolutionFilter {
+        @Override
+        public String toString() {
+            return "{all modules}";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o == this || !(o == null || o.getClass() != getClass());
+        }
+
+        @Override
+        public int hashCode() {
+            return 0;
+        }
+
+        @Override
+        public boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            return true;
+        }
+
+        public boolean acceptModule(ModuleIdentifier element) {
+            return false;
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return true;
+        }
+    }
+
+    private static class ExcludeRuleSpec extends DefaultModuleResolutionFilter {
+        private final ModuleIdentifier moduleId;
+        private final IvyArtifactName ivyArtifactName;
+        private final PatternMatcher matcher;
+        private final boolean isArtifactExclude;
+
+        private ExcludeRuleSpec(ExcludeRule rule) {
+            this.moduleId = DefaultModuleIdentifier.newId(rule.getId().getModuleId().getOrganisation(), rule.getId().getModuleId().getName());
+            this.ivyArtifactName = new DefaultIvyArtifactName(rule.getId().getName(), rule.getId().getType(), rule.getId().getExt());
+            this.matcher = rule.getMatcher();
+            isArtifactExclude = !isWildcard(ivyArtifactName.getName()) || !isWildcard(ivyArtifactName.getType()) || !isWildcard(ivyArtifactName.getExtension());
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{exclude-rule %s:%s with matcher %s}", moduleId, ivyArtifactName, matcher.getName());
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || o.getClass() != getClass()) {
+                return false;
+            }
+            ExcludeRuleSpec other = (ExcludeRuleSpec) o;
+            return doAcceptsSameModulesAs(other);
+        }
+
+        @Override
+        public int hashCode() {
+            return moduleId.hashCode() ^ ivyArtifactName.hashCode();
+        }
+
+        @Override
+        protected boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            ExcludeRuleSpec otherSpec = (ExcludeRuleSpec) other;
+            return moduleId.equals(otherSpec.moduleId)
+                    && ivyArtifactName.equals(otherSpec.ivyArtifactName)
+                    && matcher.getName().equals(otherSpec.matcher.getName());
+        }
+
+        @Override
+        protected boolean acceptsAllModules() {
+            return isArtifactExclude;
+        }
+
+        public boolean acceptModule(ModuleIdentifier module) {
+            return isArtifactExclude || !(matches(moduleId.getGroup(), module.getGroup()) && matches(moduleId.getName(), module.getName()));
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            if (isArtifactExclude) {
+                return !(matches(moduleId.getGroup(), module.getGroup())
+                        && matches(moduleId.getName(), module.getName())
+                        && matches(ivyArtifactName.getName(), artifact.getName())
+                        && matches(ivyArtifactName.getExtension(), artifact.getExtension())
+                        && matches(ivyArtifactName.getType(), artifact.getType()));
+            }
+            return true;
+        }
+
+        private boolean matches(String expression, String input) {
+            return matcher.getMatcher(expression).matches(input);
+        }
+    }
+
+    private static class ArtifactSpec extends DefaultModuleResolutionFilter {
+        private final ModuleIdentifier moduleId;
+        private final IvyArtifactName ivyArtifactName;
+        private final PatternMatcher matcher;
+
+        private ArtifactSpec(ExcludeRule rule) {
+            this.moduleId = DefaultModuleIdentifier.newId(rule.getId().getModuleId().getOrganisation(), rule.getId().getModuleId().getName());
+            this.ivyArtifactName = new DefaultIvyArtifactName(rule.getId().getName(), rule.getId().getType(), rule.getId().getExt());
+            this.matcher = rule.getMatcher();
+        }
+
+        @Override
+        public String toString() {
+            return String.format("{artifact %s:%s}", moduleId, ivyArtifactName);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || o.getClass() != getClass()) {
+                return false;
+            }
+            ArtifactSpec other = (ArtifactSpec) o;
+            return moduleId.equals(other.moduleId) && ivyArtifactName.equals(other.ivyArtifactName);
+        }
+
+        @Override
+        public int hashCode() {
+            return moduleId.hashCode() ^ ivyArtifactName.hashCode();
+        }
+
+        @Override
+        protected boolean doAcceptsSameModulesAs(DefaultModuleResolutionFilter other) {
+            return true;
+        }
+
+        @Override
+        protected boolean acceptsAllModules() {
+            return true;
+        }
+
+        public boolean acceptModule(ModuleIdentifier module) {
+            return true;
+        }
+
+        public boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact) {
+            return !(matches(moduleId.getGroup(), module.getGroup())
+                    && matches(moduleId.getName(), module.getName())
+                    && matches(ivyArtifactName.getName(), artifact.getName())
+                    && matches(ivyArtifactName.getExtension(), artifact.getExtension())
+                    && matches(ivyArtifactName.getType(), artifact.getType()));
+        }
+
+        private boolean matches(String expression, String input) {
+            return matcher.getMatcher(expression).matches(input);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyToConfigurationResolver.java
new file mode 100644
index 0000000..27b3440
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ConfigurationMetaData;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+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, ComponentResolveMetaData targetComponent);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
new file mode 100644
index 0000000..b126834
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/LatestModuleConflictResolver.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+
+import java.util.*;
+
+class LatestModuleConflictResolver implements ModuleConflictResolver {
+    private final Comparator<Version> versionComparator;
+    private final VersionParser versionParser = new VersionParser();
+
+    LatestModuleConflictResolver(VersionComparator versionComparator) {
+        this.versionComparator = versionComparator.asVersionComparator();
+    }
+
+    public <T extends ComponentResolutionState> T select(Collection<? extends T> candidates) {
+        // Find the candidates with the highest base version
+        Version baseVersion = null;
+        Map<Version, T> matches = new LinkedHashMap<Version, T>();
+        for (T candidate : candidates) {
+            Version version = versionParser.transform(candidate.getVersion());
+            if (baseVersion == null || versionComparator.compare(version.getBaseVersion(), baseVersion) > 0) {
+                matches.clear();
+                baseVersion = version.getBaseVersion();
+                matches.put(version, candidate);
+            } else if (version.getBaseVersion().equals(baseVersion)) {
+                matches.put(version, candidate);
+            }
+        }
+
+        if (matches.size() == 1) {
+            return matches.values().iterator().next();
+        }
+
+        // Work backwards from highest version, return the first candidate with qualified version and release status, or candidate with unqualified version
+        List<Version> sorted = new ArrayList<Version>(matches.keySet());
+        Collections.sort(sorted, Collections.reverseOrder(versionComparator));
+        for (Version version : sorted) {
+            T component = matches.get(version);
+            if (!version.isQualified()) {
+                return component;
+            }
+            ComponentResolveMetaData metaData = component.getMetaData();
+            if (metaData != null && "release".equals(metaData.getStatus())) {
+                return component;
+            }
+        }
+
+        // Nothing - just return the highest version
+        return matches.get(sorted.get(0));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
new file mode 100644
index 0000000..5d01ba1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleConflictResolver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
+
+import org.gradle.api.Nullable;
+
+import java.util.Collection;
+
+public interface ModuleConflictResolver {
+    /**
+     * Selects matching candidate. Returns null if this implementation of the resolver is not able to select a candidate.
+     *
+     * @param candidates
+     * @param <T>
+     */
+    @Nullable <T extends ComponentResolutionState> T select(Collection<? extends T> candidates);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleResolutionFilter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleResolutionFilter.java
new file mode 100644
index 0000000..191ccdc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ModuleResolutionFilter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.artifacts.ModuleIdentifier;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+/**
+ * Manages sets of exclude rules, allowing union and intersection operations on the rules.
+ */
+public interface ModuleResolutionFilter {
+    /**
+     * Should this module be included in the resolution result?
+     */
+    boolean acceptModule(ModuleIdentifier module);
+
+    /**
+     * Should this artifact be included in the resolution result?
+     */
+    boolean acceptArtifact(ModuleIdentifier module, IvyArtifactName artifact);
+
+    /**
+     * Determines if this filter accepts the same set of modules as the other.
+     *
+     * @return true if the filters accept the same set of modules. Returns false if they may not, or if it is unknown.
+     */
+    boolean acceptsSameModulesAs(ModuleResolutionFilter other);
+
+    /**
+     * Returns a filter that accepts the union of those module versions and artifacts that are accepted by this filter and the other.
+     * The union accepts if either of the inputs filters accepts.
+     */
+    ModuleResolutionFilter union(ModuleResolutionFilter other);
+
+    /**
+     * Returns a filter that accepts the union of those module versions and artifacts that are accepted by this filter and the other.
+     * The intersection accepts if both of the inputs filters accept.
+     */
+    ModuleResolutionFilter intersect(ModuleResolutionFilter other);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java
new file mode 100644
index 0000000..10722ba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/StrictConflictResolver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
+
+import java.util.Collection;
+import java.util.Formatter;
+
+class StrictConflictResolver implements ModuleConflictResolver {
+    public <T extends ComponentResolutionState> T select(Collection<? extends T> candidates) {
+        Formatter formatter = new Formatter();
+        formatter.format("A conflict was found between the following modules:");
+        for (ComponentResolutionState candidate : candidates) {
+            formatter.format("%n - %s", candidate.getId());
+        }
+        throw new RuntimeException(formatter.toString());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.java
new file mode 100644
index 0000000..56ff0e9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolver.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.resolveengine;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+
+import java.util.Collection;
+
+public class VersionSelectionReasonResolver implements ModuleConflictResolver {
+
+    private final ModuleConflictResolver delegate;
+
+    public VersionSelectionReasonResolver(ModuleConflictResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public <T extends ComponentResolutionState> T select(Collection<? extends T> candidates) {
+        T selected = delegate.select(candidates);
+        selected.setSelectionReason(VersionSelectionReasons.withConflictResolution(selected.getSelectionReason()));
+        return selected;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/CompositeDependencyGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/CompositeDependencyGraphVisitor.java
new file mode 100644
index 0000000..851041e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/CompositeDependencyGraphVisitor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+import java.util.Arrays;
+import java.util.List;
+
+class CompositeDependencyGraphVisitor implements DependencyGraphVisitor {
+    private final List<DependencyGraphVisitor> visitors;
+
+    CompositeDependencyGraphVisitor(DependencyGraphVisitor... visitors) {
+        this.visitors = Arrays.asList(visitors);
+    }
+
+    public void start(DependencyGraphBuilder.ConfigurationNode root) {
+        for (DependencyGraphVisitor visitor : visitors) {
+            visitor.start(root);
+        }
+    }
+
+    public void visitNode(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        for (DependencyGraphVisitor visitor : visitors) {
+            visitor.visitNode(resolvedConfiguration);
+        }
+    }
+
+    public void visitEdge(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        for (DependencyGraphVisitor visitor : visitors) {
+            visitor.visitEdge(resolvedConfiguration);
+        }
+    }
+
+    public void finish(DependencyGraphBuilder.ConfigurationNode root) {
+        for (DependencyGraphVisitor visitor : visitors) {
+            visitor.finish(root);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphBuilder.java
new file mode 100644
index 0000000..007f2ad
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphBuilder.java
@@ -0,0 +1,920 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+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.resolveengine.*;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.CandidateModule;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.ConflictHandler;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.ConflictResolutionResult;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.PotentialConflict;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.ResolvedConfigurationBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResultBuilder;
+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.ResolutionResultBuilder;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
+import org.gradle.internal.resolve.resolver.ModuleToComponentResolver;
+import org.gradle.internal.resolve.result.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class DependencyGraphBuilder {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DependencyGraphBuilder.class);
+    private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
+    private final ConflictHandler conflictHandler;
+    private final ModuleToComponentResolver moduleResolver;
+    private final ArtifactResolver artifactResolver;
+    private final DependencyToComponentIdResolver idResolver;
+    private final ComponentMetaDataResolver metaDataResolver;
+
+    public DependencyGraphBuilder(DependencyToComponentIdResolver idResolver,
+                                  ComponentMetaDataResolver metaDataResolver,
+                                  ModuleToComponentResolver moduleResolver,
+                                  ArtifactResolver artifactResolver,
+                                  ConflictHandler conflictHandler,
+                                  DependencyToConfigurationResolver dependencyToConfigurationResolver) {
+        this.idResolver = idResolver;
+        this.metaDataResolver = metaDataResolver;
+        this.moduleResolver = moduleResolver;
+        this.artifactResolver = artifactResolver;
+        this.conflictHandler = conflictHandler;
+        this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
+    }
+
+    public void resolve(ConfigurationInternal configuration,
+                        ResolutionResultBuilder newModelBuilder,
+                        ResolvedConfigurationBuilder oldModelBuilder,
+                        ResolvedProjectConfigurationResultBuilder projectModelBuilder) throws ResolveException {
+        DependencyGraphVisitor oldModelVisitor = new ResolvedConfigurationDependencyGraphVisitor(oldModelBuilder, artifactResolver);
+        DependencyGraphVisitor newModelVisitor = new ResolutionResultDependencyGraphVisitor(newModelBuilder);
+        DependencyGraphVisitor projectModelVisitor = new ResolvedProjectConfigurationResultGraphVisitor(projectModelBuilder);
+        DependencyGraphVisitor modelVisitor = new CompositeDependencyGraphVisitor(oldModelVisitor, newModelVisitor, projectModelVisitor);
+
+        resolveDependencyGraph(configuration, modelVisitor);
+    }
+
+    private void resolveDependencyGraph(ConfigurationInternal configuration, DependencyGraphVisitor modelVisitor) {
+        DefaultBuildableComponentResolveResult rootModule = new DefaultBuildableComponentResolveResult();
+        moduleResolver.resolve(configuration.getModule(), configuration.getAll(), rootModule);
+
+        ResolveState resolveState = new ResolveState(rootModule, configuration.getName(), idResolver, metaDataResolver, dependencyToConfigurationResolver, artifactResolver);
+        conflictHandler.registerResolver(new DirectDependencyForcingResolver(resolveState.root.moduleRevision));
+
+        traverseGraph(resolveState, conflictHandler);
+
+        assembleResult(resolveState, modelVisitor);
+    }
+
+    /**
+     * Traverses the dependency graph, resolving conflicts and building the paths from the root configuration.
+     */
+    private void traverseGraph(final ResolveState resolveState, final ConflictHandler conflictHandler) {
+        resolveState.onMoreSelected(resolveState.root);
+
+        List<DependencyEdge> dependencies = new ArrayList<DependencyEdge>();
+        while (resolveState.peek() != null || conflictHandler.hasConflicts()) {
+            if (resolveState.peek() != null) {
+                ConfigurationNode node = resolveState.pop();
+                LOGGER.debug("Visiting configuration {}.", node);
+
+                // Calculate the outgoing edges of this configuration
+                dependencies.clear();
+                node.visitOutgoingDependencies(dependencies);
+
+                for (DependencyEdge dependency : dependencies) {
+                    LOGGER.debug("Visiting dependency {}", dependency);
+
+                    // Resolve dependency to a particular revision
+                    ModuleVersionResolveState moduleRevision = dependency.resolveModuleRevisionId();
+                    if (moduleRevision == null) {
+                        // Failed to resolve.
+                        continue;
+                    }
+                    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
+                        PotentialConflict c = conflictHandler.registerModule(module);
+                        if (!c.conflictExists()) {
+                            // No conflict. Select it for now
+                            LOGGER.debug("Selecting new module version {}", moduleRevision);
+                            module.select(moduleRevision);
+                        } else {
+                            // We have a conflict
+                            LOGGER.debug("Found new conflicting module version {}", moduleRevision);
+
+                            // 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
+                            // For each module participating in the conflict (many times there is only one participating module that has multiple versions)
+                            c.withParticipatingModules(new Action<ModuleIdentifier>() {
+                                public void execute(ModuleIdentifier module) {
+                                    ModuleVersionResolveState previouslySelected = resolveState.getModule(module).clearSelection();
+                                    if (previouslySelected != null) {
+                                        for (ConfigurationNode configuration : previouslySelected.configurations) {
+                                            configuration.deselect();
+                                        }
+                                    }
+                                }
+                            });
+                        }
+                    }
+
+                    dependency.attachToTargetConfigurations();
+                }
+            } else {
+                // We have some batched up conflicts. Resolve the first, and continue traversing the graph
+                conflictHandler.resolveNextConflict(new Action<ConflictResolutionResult>() {
+                    public void execute(final ConflictResolutionResult result) {
+                        result.getConflict().withParticipatingModules(new Action<ModuleIdentifier>() {
+                            public void execute(ModuleIdentifier moduleIdentifier) {
+                                ModuleVersionResolveState selected = result.getSelected();
+                                // Restart each configuration. For the evicted configuration, this means moving incoming dependencies across to the
+                                // matching selected configuration. For the select configuration, this mean traversing its dependencies.
+                                resolveState.getModule(moduleIdentifier).restart(selected);
+                            }
+                        });
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Populates the result from the graph traversal state.
+     */
+    private void assembleResult(ResolveState resolveState, DependencyGraphVisitor listener) {
+        listener.start(resolveState.root);
+
+        // Visit the nodes
+        for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
+            if (resolvedConfiguration.isSelected()) {
+                resolvedConfiguration.validate();
+                listener.visitNode(resolvedConfiguration);
+            }
+        }
+        // Visit the edges
+        for (ConfigurationNode resolvedConfiguration : resolveState.getConfigurationNodes()) {
+            if (resolvedConfiguration.isSelected()) {
+                listener.visitEdge(resolvedConfiguration);
+            }
+        }
+
+        listener.finish(resolveState.root);
+    }
+
+    /**
+     * Represents the edges in the dependency graph.
+     */
+    static class DependencyEdge implements InternalDependencyResult {
+        public final ConfigurationNode from;
+        public final ModuleVersionSelectorResolveState selector;
+
+        private final DependencyMetaData dependencyMetaData;
+        private final DependencyDescriptor dependencyDescriptor;
+        private final ResolveState resolveState;
+        private final ModuleResolutionFilter resolutionFilter;
+        private final Set<ConfigurationNode> targetConfigurations = new LinkedHashSet<ConfigurationNode>();
+        private ModuleVersionResolveState targetModuleRevision;
+
+        public DependencyEdge(ConfigurationNode from, DependencyMetaData dependencyMetaData, ModuleResolutionFilter resolutionFilter, ResolveState resolveState) {
+            this.from = from;
+            this.dependencyMetaData = dependencyMetaData;
+            this.dependencyDescriptor = dependencyMetaData.getDescriptor();
+            this.resolutionFilter = resolutionFilter;
+            this.resolveState = resolveState;
+            selector = resolveState.getSelector(dependencyMetaData);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s -> %s(%s)", from.toString(), dependencyMetaData.getRequested(), dependencyDescriptor);
+        }
+
+        /**
+         * @return The resolved module version
+         */
+        public ModuleVersionResolveState resolveModuleRevisionId() {
+            if (targetModuleRevision == null) {
+                targetModuleRevision = selector.resolveModuleRevisionId();
+                selector.getSelectedModule().addUnattachedDependency(this);
+            }
+            return targetModuleRevision;
+        }
+
+        public boolean isTransitive() {
+            return from.isTransitive() && dependencyMetaData.isTransitive();
+        }
+
+        public void attachToTargetConfigurations() {
+            if (targetModuleRevision.state != ModuleState.Selected) {
+                return;
+            }
+            calculateTargetConfigurations();
+            for (ConfigurationNode targetConfiguration : targetConfigurations) {
+                targetConfiguration.addIncomingEdge(this);
+            }
+            if (!targetConfigurations.isEmpty()) {
+                selector.getSelectedModule().removeUnattachedDependency(this);
+            }
+        }
+
+        public void removeFromTargetConfigurations() {
+            for (ConfigurationNode targetConfiguration : targetConfigurations) {
+                targetConfiguration.removeIncomingEdge(this);
+            }
+            targetConfigurations.clear();
+            if (targetModuleRevision != null) {
+                selector.getSelectedModule().removeUnattachedDependency(this);
+            }
+        }
+
+        public void restart(ModuleVersionResolveState selected) {
+            targetModuleRevision = selected;
+            attachToTargetConfigurations();
+        }
+
+        private void calculateTargetConfigurations() {
+            targetConfigurations.clear();
+            ComponentResolveMetaData targetModuleVersion = targetModuleRevision.getMetaData();
+            if (targetModuleVersion == null) {
+                // Broken version
+                return;
+            }
+
+            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);
+            }
+        }
+
+        public ModuleResolutionFilter getSelector() {
+            String[] configurations = from.metaData.getHierarchy().toArray(new String[from.metaData.getHierarchy().size()]);
+            ModuleResolutionFilter selector = DefaultModuleResolutionFilter.excludeAny(dependencyDescriptor.getExcludeRules(configurations));
+            return selector.intersect(resolutionFilter);
+        }
+
+        public ComponentSelector getRequested() {
+            return dependencyMetaData.getSelector();
+        }
+
+        // TODO This should be replaced by getRequested()
+        public ModuleVersionSelector getRequestedModuleVersion() {
+            return dependencyMetaData.getRequested();
+        }
+
+        public ModuleVersionResolveException getFailure() {
+            return selector.getFailure();
+        }
+
+        public ModuleVersionIdentifier getSelected() {
+            return selector.getSelected().getId();
+        }
+
+        public ComponentSelectionReason getReason() {
+            return selector.getSelectionReason();
+        }
+
+        public ModuleDependency getModuleDependency() {
+            return ((DslOriginDependencyMetaData) dependencyMetaData).getSource();
+        }
+
+        public Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData metaData1) {
+            return dependencyMetaData.getArtifacts(from.metaData, metaData1);
+        }
+    }
+
+    /**
+     * Global resolution state.
+     */
+    private static class ResolveState {
+        private final Map<ModuleIdentifier, ModuleResolveState> modules = new LinkedHashMap<ModuleIdentifier, ModuleResolveState>();
+        private final Map<ResolvedConfigurationIdentifier, ConfigurationNode> nodes = new LinkedHashMap<ResolvedConfigurationIdentifier, ConfigurationNode>();
+        private final Map<ModuleVersionSelector, ModuleVersionSelectorResolveState> selectors = new LinkedHashMap<ModuleVersionSelector, ModuleVersionSelectorResolveState>();
+        private final RootConfigurationNode root;
+        private final DependencyToComponentIdResolver idResolver;
+        private final ComponentMetaDataResolver metaDataResolver;
+        private final DependencyToConfigurationResolver dependencyToConfigurationResolver;
+        private final ArtifactResolver artifactResolver;
+        private final Set<ConfigurationNode> queued = new HashSet<ConfigurationNode>();
+        private final LinkedList<ConfigurationNode> queue = new LinkedList<ConfigurationNode>();
+
+        public ResolveState(ComponentResolveResult rootResult, String rootConfigurationName, DependencyToComponentIdResolver idResolver,
+                            ComponentMetaDataResolver metaDataResolver, DependencyToConfigurationResolver dependencyToConfigurationResolver,
+                            ArtifactResolver artifactResolver) {
+            this.idResolver = idResolver;
+            this.metaDataResolver = metaDataResolver;
+            this.dependencyToConfigurationResolver = dependencyToConfigurationResolver;
+            this.artifactResolver = artifactResolver;
+            ModuleVersionResolveState rootVersion = getRevision(rootResult.getId());
+            rootVersion.setMetaData(rootResult.getMetaData());
+            root = new RootConfigurationNode(rootVersion, new ResolvedConfigurationIdentifier(rootVersion.id, rootConfigurationName), this);
+            nodes.put(root.id, root);
+            root.moduleRevision.module.select(root.moduleRevision);
+        }
+
+        public ModuleResolveState getModule(ModuleIdentifier id) {
+            ModuleResolveState module = modules.get(id);
+            if (module == null) {
+                module = new ModuleResolveState(id, this, metaDataResolver);
+                modules.put(id, module);
+            }
+            return module;
+        }
+
+        public ModuleVersionResolveState getRevision(ModuleVersionIdentifier id) {
+            return getModule(id.getModule()).getVersion(id);
+        }
+
+        public Collection<ConfigurationNode> getConfigurationNodes() {
+            return nodes.values();
+        }
+
+        public ConfigurationNode getConfigurationNode(ModuleVersionResolveState module, String configurationName) {
+            ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(module.id, configurationName);
+            ConfigurationNode configuration = nodes.get(id);
+            if (configuration == null) {
+                configuration = new ConfigurationNode(module, id, this);
+                nodes.put(id, configuration);
+            }
+            return configuration;
+        }
+
+        public ModuleVersionSelectorResolveState getSelector(DependencyMetaData dependencyMetaData) {
+            ModuleVersionSelector requested = dependencyMetaData.getRequested();
+            ModuleVersionSelectorResolveState resolveState = selectors.get(requested);
+            if (resolveState == null) {
+                resolveState = new ModuleVersionSelectorResolveState(dependencyMetaData, idResolver, this);
+                selectors.put(requested, resolveState);
+            }
+            return resolveState;
+        }
+
+        public ConfigurationNode peek() {
+            return queue.isEmpty() ? null : queue.getFirst();
+        }
+
+        public ConfigurationNode pop() {
+            ConfigurationNode next = queue.removeFirst();
+            queued.remove(next);
+            return next;
+        }
+
+        /**
+         * Called when a change is made to a configuration node, such that its dependency graph <em>may</em> now be larger than it previously was, and the node should be visited.
+         */
+        public void onMoreSelected(ConfigurationNode configuration) {
+            // Add to the end of the queue, so that we traverse the graph in breadth-wise order to pick up as many conflicts as
+            // possible before attempting to resolve them
+            if (queued.add(configuration)) {
+                queue.addLast(configuration);
+            }
+        }
+
+        /**
+         * Called when a change is made to a configuration node, such that its dependency graph <em>may</em> now be smaller than it previously was, and the node should be visited.
+         */
+        public void onFewerSelected(ConfigurationNode configuration) {
+            // Add to the front of the queue, to flush out configurations that are no longer required.
+            if (queued.add(configuration)) {
+                queue.addFirst(configuration);
+            }
+        }
+    }
+
+    enum ModuleState {
+        New,
+        Selected,
+        Conflict,
+        Evicted
+    }
+
+    /**
+     * Resolution state for a given module.
+     */
+    private static class ModuleResolveState implements CandidateModule {
+        final ComponentMetaDataResolver metaDataResolver;
+        final ModuleIdentifier id;
+        final Set<DependencyEdge> unattachedDependencies = new LinkedHashSet<DependencyEdge>();
+        final Map<ModuleVersionIdentifier, ModuleVersionResolveState> versions = new LinkedHashMap<ModuleVersionIdentifier, ModuleVersionResolveState>();
+        final Set<ModuleVersionSelectorResolveState> selectors = new HashSet<ModuleVersionSelectorResolveState>();
+        final ResolveState resolveState;
+        ModuleVersionResolveState selected;
+
+        private ModuleResolveState(ModuleIdentifier id, ResolveState resolveState, ComponentMetaDataResolver metaDataResolver) {
+            this.id = id;
+            this.resolveState = resolveState;
+            this.metaDataResolver = metaDataResolver;
+        }
+
+        @Override
+        public String toString() {
+            return id.toString();
+        }
+
+        public ModuleIdentifier getId() {
+            return id;
+        }
+
+        public Collection<ModuleVersionResolveState> getVersions() {
+            return versions.values();
+        }
+
+        public void select(ModuleVersionResolveState selected) {
+            assert this.selected == null;
+            this.selected = selected;
+            for (ModuleVersionResolveState version : versions.values()) {
+                version.state = ModuleState.Evicted;
+            }
+            selected.state = ModuleState.Selected;
+        }
+
+        public ModuleVersionResolveState clearSelection() {
+            ModuleVersionResolveState previousSelection = selected;
+            selected = null;
+            for (ModuleVersionResolveState version : versions.values()) {
+                version.state = ModuleState.Conflict;
+            }
+            return previousSelection;
+        }
+
+        public void restart(ModuleVersionResolveState selected) {
+            select(selected);
+            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);
+            }
+            unattachedDependencies.clear();
+        }
+
+        public void addUnattachedDependency(DependencyEdge edge) {
+            unattachedDependencies.add(edge);
+        }
+
+        public void removeUnattachedDependency(DependencyEdge edge) {
+            unattachedDependencies.remove(edge);
+        }
+
+        public ModuleVersionResolveState getVersion(ModuleVersionIdentifier id) {
+            ModuleVersionResolveState moduleRevision = versions.get(id);
+            if (moduleRevision == null) {
+                moduleRevision = new ModuleVersionResolveState(this, id, metaDataResolver);
+                versions.put(id, moduleRevision);
+            }
+
+            return moduleRevision;
+        }
+
+        public void addSelector(ModuleVersionSelectorResolveState selector) {
+            selectors.add(selector);
+        }
+    }
+
+    /**
+     * Resolution state for a given module version.
+     */
+    static class ModuleVersionResolveState implements ComponentResolutionState, ModuleVersionSelection {
+        public final ModuleVersionIdentifier id;
+        private final ComponentMetaDataResolver resolver;
+        private final Set<ConfigurationNode> configurations = new LinkedHashSet<ConfigurationNode>();
+        private final ModuleResolveState module;
+        private ComponentResolveMetaData metaData;
+        private ModuleState state = ModuleState.New;
+        private ComponentSelectionReason selectionReason = VersionSelectionReasons.REQUESTED;
+        private ModuleVersionResolveException failure;
+        private ModuleVersionSelectorResolveState firstReference;
+
+        private ModuleVersionResolveState(ModuleResolveState module, ModuleVersionIdentifier id, ComponentMetaDataResolver resolver) {
+            this.module = module;
+            this.id = id;
+            this.resolver = resolver;
+        }
+
+        @Override
+        public String toString() {
+            return id.toString();
+        }
+
+        public String getVersion() {
+            return id.getVersion();
+        }
+
+        public ModuleVersionIdentifier getId() {
+            return id;
+        }
+
+        public ModuleVersionResolveException getFailure() {
+            return failure;
+        }
+
+        public void restart(ModuleVersionResolveState selected) {
+            for (ConfigurationNode configuration : configurations) {
+                configuration.restart(selected);
+            }
+        }
+
+        public void addResolver(ModuleVersionSelectorResolveState resolver) {
+            if (firstReference == null) {
+                firstReference = resolver;
+            }
+        }
+
+        public void resolve() {
+            if (metaData != null || failure != null) {
+                return;
+            }
+
+            ComponentIdResolveResult idResolveResult = firstReference.idResolveResult;
+            if (idResolveResult.getFailure() != null) {
+                failure = idResolveResult.getFailure();
+                return;
+            }
+            if (idResolveResult.getMetaData() != null) {
+                metaData = idResolveResult.getMetaData();
+                return;
+            }
+
+            DefaultBuildableComponentResolveResult result = new DefaultBuildableComponentResolveResult();
+            resolver.resolve(firstReference.dependencyMetaData, idResolveResult.getId(), result);
+            if (result.getFailure() != null) {
+                failure = result.getFailure();
+                return;
+            }
+            metaData = result.getMetaData();
+        }
+
+        public ComponentResolveMetaData getMetaData() {
+            if (metaData == null) {
+                resolve();
+            }
+            return metaData;
+        }
+
+        public void setMetaData(ComponentResolveMetaData metaData) {
+            this.metaData = metaData;
+            this.failure = null;
+        }
+
+        public void addConfiguration(ConfigurationNode configurationNode) {
+            configurations.add(configurationNode);
+        }
+
+        public ComponentSelectionReason getSelectionReason() {
+            return selectionReason;
+        }
+
+        public void setSelectionReason(ComponentSelectionReason reason) {
+            this.selectionReason = reason;
+        }
+
+        public ComponentIdentifier getComponentId() {
+            return getMetaData().getComponentId();
+        }
+
+        public List<ModuleVersionResolveState> getIncoming() {
+            List<ModuleVersionResolveState> incoming = new ArrayList<ModuleVersionResolveState>();
+            for (DependencyGraphBuilder.ConfigurationNode configuration : configurations) {
+                for (DependencyGraphBuilder.DependencyEdge dependencyEdge : configuration.incomingEdges) {
+                    incoming.add(dependencyEdge.from.moduleRevision);
+                }
+            }
+            return incoming;
+        }
+    }
+
+    /**
+     * Represents a node in the dependency graph.
+     */
+    static class ConfigurationNode {
+        public final ModuleVersionResolveState moduleRevision;
+        public final ConfigurationMetaData metaData;
+        public final Set<DependencyEdge> incomingEdges = new LinkedHashSet<DependencyEdge>();
+        public final Set<DependencyEdge> outgoingEdges = new LinkedHashSet<DependencyEdge>();
+        public final ResolvedConfigurationIdentifier id;
+
+        private final ResolveState resolveState;
+        private ModuleResolutionFilter previousTraversal;
+        private Set<ComponentArtifactMetaData> artifacts;
+        private Map<IvyArtifactName, ResolvedArtifact> resolvedArtifacts = new HashMap<IvyArtifactName, ResolvedArtifact>();
+
+        private ConfigurationNode(ModuleVersionResolveState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState) {
+            this.moduleRevision = moduleRevision;
+            this.resolveState = resolveState;
+            this.metaData = moduleRevision.metaData.getConfiguration(id.getConfiguration());
+            this.id = id;
+            moduleRevision.addConfiguration(this);
+        }
+
+        public ModuleVersionIdentifier toId() {
+            return moduleRevision.id;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s(%s)", moduleRevision, metaData.getName());
+        }
+
+        public Set<ResolvedArtifact> getArtifacts(ResolvedConfigurationBuilder builder, ModuleResolutionFilter moduleResolutionFilter) {
+            if (artifacts == null) {
+                BuildableArtifactSetResolveResult result = new DefaultBuildableArtifactSetResolveResult();
+                resolveState.artifactResolver.resolveModuleArtifacts(metaData.getComponent(), new DefaultComponentUsage(metaData.getName()), result);
+                artifacts = result.getArtifacts();
+            }
+
+            Set<ResolvedArtifact> result = new LinkedHashSet<ResolvedArtifact>();
+            ModuleIdentifier moduleId = id.getId().getModule();
+            for (ComponentArtifactMetaData artifact : artifacts) {
+                IvyArtifactName artifactName = artifact.getName();
+                if (!moduleResolutionFilter.acceptArtifact(moduleId, artifactName)) {
+                    continue;
+                }
+                ResolvedArtifact resolvedArtifact = resolvedArtifacts.get(artifactName);
+                if (resolvedArtifact == null) {
+                    resolvedArtifact = builder.newArtifact(id, metaData.getComponent(), artifact, resolveState.artifactResolver);
+                    resolvedArtifacts.put(artifactName, resolvedArtifact);
+                }
+                result.add(resolvedArtifact);
+            }
+
+            return result;
+        }
+
+        public boolean isTransitive() {
+            return metaData.isTransitive();
+        }
+
+        public void visitOutgoingDependencies(Collection<DependencyEdge> target) {
+            // If this configuration's version is in conflict, don't do anything
+            // If not traversed before, add all selected outgoing edges
+            // 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 are transitive, then the node has no outgoing edges
+
+            if (moduleRevision.state != ModuleState.Selected) {
+                LOGGER.debug("version for {} is not selected. ignoring.", this);
+                return;
+            }
+
+            List<DependencyEdge> transitiveIncoming = new ArrayList<DependencyEdge>();
+            for (DependencyEdge edge : incomingEdges) {
+                if (edge.isTransitive()) {
+                    transitiveIncoming.add(edge);
+                }
+            }
+
+            if (transitiveIncoming.isEmpty() && this != resolveState.root) {
+                if (previousTraversal != null) {
+                    removeOutgoingEdges();
+                }
+                if (incomingEdges.isEmpty()) {
+                    LOGGER.debug("{} has no incoming edges. ignoring.", this);
+                } else {
+                    LOGGER.debug("{} has no transitive incoming edges. ignoring outgoing edges.", this);
+                }
+                return;
+            }
+
+            ModuleResolutionFilter resolutionFilter = getSelector(transitiveIncoming);
+            if (previousTraversal != null) {
+                if (previousTraversal.acceptsSameModulesAs(resolutionFilter)) {
+                    LOGGER.debug("Changed edges for {} selects same versions as previous traversal. ignoring", this);
+                    // Don't need to traverse again, but hang on to the new filter as the set of artifact may have changed
+                    previousTraversal = resolutionFilter;
+                    return;
+                }
+                removeOutgoingEdges();
+            }
+
+            for (DependencyMetaData dependency : metaData.getDependencies()) {
+                ModuleIdentifier targetModuleId = DefaultModuleIdentifier.newId(dependency.getRequested().getGroup(), dependency.getRequested().getName());
+                if (isExcluded(resolutionFilter, targetModuleId)) {
+                    continue;
+                }
+                DependencyEdge dependencyEdge = new DependencyEdge(this, dependency, resolutionFilter, resolveState);
+                outgoingEdges.add(dependencyEdge);
+                target.add(dependencyEdge);
+            }
+            previousTraversal = resolutionFilter;
+        }
+
+        private boolean isExcluded(ModuleResolutionFilter selector, ModuleIdentifier targetModuleId) {
+            if(!selector.acceptModule(targetModuleId)) {
+                LOGGER.debug("{} is excluded from {}.", targetModuleId, this);
+                return true;
+            }
+
+            return false;
+        }
+
+        public void addIncomingEdge(DependencyEdge dependencyEdge) {
+            incomingEdges.add(dependencyEdge);
+            resolveState.onMoreSelected(this);
+        }
+
+        public void removeIncomingEdge(DependencyEdge dependencyEdge) {
+            incomingEdges.remove(dependencyEdge);
+            resolveState.onFewerSelected(this);
+        }
+
+        public boolean isSelected() {
+            return moduleRevision.state == ModuleState.Selected;
+        }
+
+        private ModuleResolutionFilter getSelector(List<DependencyEdge> transitiveEdges) {
+            ModuleResolutionFilter resolutionFilter;
+            if (transitiveEdges.isEmpty()) {
+                resolutionFilter = DefaultModuleResolutionFilter.all();
+            } else {
+                resolutionFilter = transitiveEdges.get(0).getSelector();
+                for (int i = 1; i < transitiveEdges.size(); i++) {
+                    DependencyEdge dependencyEdge = transitiveEdges.get(i);
+                    resolutionFilter = resolutionFilter.union(dependencyEdge.getSelector());
+                }
+            }
+            resolutionFilter = resolutionFilter.intersect(DefaultModuleResolutionFilter.excludeAny(metaData.getExcludeRules()));
+            return resolutionFilter;
+        }
+
+        public void removeOutgoingEdges() {
+            for (DependencyEdge outgoingDependency : outgoingEdges) {
+                outgoingDependency.removeFromTargetConfigurations();
+            }
+            outgoingEdges.clear();
+            previousTraversal = null;
+        }
+
+        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 remove our incoming edges, which triggers them to be moved across to the selected configuration
+            if (moduleRevision == selected) {
+                resolveState.onMoreSelected(this);
+            } else {
+                for (DependencyEdge dependency : incomingEdges) {
+                    dependency.restart(selected);
+                }
+                incomingEdges.clear();
+            }
+        }
+
+        public void validate() {
+            for (DependencyEdge incomingEdge : incomingEdges) {
+                ConfigurationNode fromNode = incomingEdge.from;
+                if (!fromNode.isSelected()) {
+                    throw new IllegalStateException(String.format("Unexpected state %s for parent node for dependency from %s to %s.", fromNode.moduleRevision.state, fromNode, this));
+                }
+            }
+        }
+
+        public void deselect() {
+            removeOutgoingEdges();
+        }
+    }
+
+    private static class RootConfigurationNode extends ConfigurationNode {
+        private RootConfigurationNode(ModuleVersionResolveState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState) {
+            super(moduleRevision, id, resolveState);
+        }
+
+        @Override
+        public boolean isSelected() {
+            return true;
+        }
+
+        @Override
+        public void deselect() {
+        }
+    }
+
+    /**
+     * Resolution state for a given module version selector.
+     */
+    private static class ModuleVersionSelectorResolveState {
+        final DependencyMetaData dependencyMetaData;
+        final DependencyToComponentIdResolver resolver;
+        final ResolveState resolveState;
+        ModuleVersionResolveException failure;
+        ModuleResolveState targetModule;
+        ModuleVersionResolveState targetModuleRevision;
+        BuildableComponentIdResolveResult idResolveResult;
+
+        private ModuleVersionSelectorResolveState(DependencyMetaData dependencyMetaData, DependencyToComponentIdResolver resolver, ResolveState resolveState) {
+            this.dependencyMetaData = dependencyMetaData;
+            this.resolver = resolver;
+            this.resolveState = resolveState;
+            targetModule = resolveState.getModule(new DefaultModuleIdentifier(dependencyMetaData.getRequested().getGroup(), dependencyMetaData.getRequested().getName()));
+        }
+
+        @Override
+        public String toString() {
+            return dependencyMetaData.toString();
+        }
+
+        private ModuleVersionResolveException getFailure() {
+            return failure != null ? failure : targetModuleRevision.getFailure();
+        }
+
+        public ComponentSelectionReason 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 ModuleVersionResolveState resolveModuleRevisionId() {
+            if (targetModuleRevision != null) {
+                return targetModuleRevision;
+            }
+            if (failure != null) {
+                return null;
+            }
+
+            idResolveResult = new DefaultBuildableComponentIdResolveResult();
+            resolver.resolve(dependencyMetaData, idResolveResult);
+            if (idResolveResult.getFailure() != null) {
+                failure = idResolveResult.getFailure();
+                return null;
+            }
+
+            targetModuleRevision = resolveState.getRevision(idResolveResult.getModuleVersionId());
+            targetModuleRevision.addResolver(this);
+            targetModuleRevision.selectionReason = idResolveResult.getSelectionReason();
+            targetModule = targetModuleRevision.module;
+            targetModule.addSelector(this);
+
+            return targetModuleRevision;
+        }
+
+        public void restart(ModuleVersionResolveState moduleRevision) {
+            this.targetModuleRevision = moduleRevision;
+            this.targetModule = moduleRevision.module;
+        }
+    }
+
+    private static class DirectDependencyForcingResolver implements ModuleConflictResolver {
+        private final ModuleVersionResolveState root;
+
+        private DirectDependencyForcingResolver(ModuleVersionResolveState root) {
+            this.root = root;
+        }
+
+        public <T extends ComponentResolutionState> T select(Collection<? extends T> candidates) {
+            for (ConfigurationNode configuration : root.configurations) {
+                for (DependencyEdge outgoingEdge : configuration.outgoingEdges) {
+                    if (outgoingEdge.dependencyDescriptor.isForce() && candidates.contains(outgoingEdge.targetModuleRevision)) {
+                        outgoingEdge.targetModuleRevision.selectionReason = VersionSelectionReasons.FORCED;
+                        return (T) outgoingEdge.targetModuleRevision;
+                    }
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphVisitor.java
new file mode 100644
index 0000000..1c5e102
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphVisitor.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+interface DependencyGraphVisitor {
+    void start(DependencyGraphBuilder.ConfigurationNode root);
+    void visitNode(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration);
+    void visitEdge(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration);
+    void finish(DependencyGraphBuilder.ConfigurationNode root);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolutionResultDependencyGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolutionResultDependencyGraphVisitor.java
new file mode 100644
index 0000000..f8b1e8c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolutionResultDependencyGraphVisitor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultBuilder;
+
+class ResolutionResultDependencyGraphVisitor implements DependencyGraphVisitor {
+    private final ResolutionResultBuilder newModelBuilder;
+
+    ResolutionResultDependencyGraphVisitor(ResolutionResultBuilder newModelBuilder) {
+        this.newModelBuilder = newModelBuilder;
+    }
+
+    public void start(DependencyGraphBuilder.ConfigurationNode root) {
+        newModelBuilder.start(root.toId(), root.metaData.getComponent().getComponentId());
+    }
+
+    public void visitNode(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        newModelBuilder.resolvedModuleVersion(resolvedConfiguration.moduleRevision);
+    }
+
+    public void visitEdge(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        newModelBuilder.resolvedConfiguration(resolvedConfiguration.toId(), resolvedConfiguration.outgoingEdges);
+    }
+
+    public void finish(DependencyGraphBuilder.ConfigurationNode root) {
+
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedConfigurationDependencyGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedConfigurationDependencyGraphVisitor.java
new file mode 100644
index 0000000..c5674ba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedConfigurationDependencyGraphVisitor.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultUnresolvedDependency;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.ResolvedConfigurationBuilder;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+class ResolvedConfigurationDependencyGraphVisitor implements DependencyGraphVisitor {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ResolvedConfigurationDependencyGraphVisitor.class);
+
+    private final ResolvedConfigurationBuilder builder;
+    private final ArtifactResolver artifactResolver;
+    private final Map<ModuleVersionSelector, BrokenDependency> failuresByRevisionId = new LinkedHashMap<ModuleVersionSelector, BrokenDependency>();
+    private DependencyGraphBuilder.ConfigurationNode root;
+
+    ResolvedConfigurationDependencyGraphVisitor(ResolvedConfigurationBuilder builder, ArtifactResolver artifactResolver) {
+        this.builder = builder;
+        this.artifactResolver = artifactResolver;
+    }
+
+    public void start(DependencyGraphBuilder.ConfigurationNode root) {
+        this.root = root;
+    }
+
+    public void visitNode(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        builder.newResolvedDependency(resolvedConfiguration.id);
+        for (DependencyGraphBuilder.DependencyEdge dependency : resolvedConfiguration.outgoingEdges) {
+            ModuleVersionResolveException failure = dependency.getFailure();
+            if (failure != null) {
+                addUnresolvedDependency(dependency, dependency.getRequestedModuleVersion(), failure);
+            }
+        }
+    }
+
+    public void visitEdge(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        LOGGER.debug("Attaching {} to its parents.", resolvedConfiguration);
+        for (DependencyGraphBuilder.DependencyEdge dependency : resolvedConfiguration.incomingEdges) {
+            attachToParents(dependency, resolvedConfiguration, builder);
+        }
+    }
+
+    private void attachToParents(DependencyGraphBuilder.DependencyEdge dependency, DependencyGraphBuilder.ConfigurationNode childConfiguration, ResolvedConfigurationBuilder oldModelBuilder) {
+        ResolvedConfigurationIdentifier parent = dependency.from.id;
+        ResolvedConfigurationIdentifier child = childConfiguration.id;
+        oldModelBuilder.addChild(parent, child);
+        oldModelBuilder.addParentSpecificArtifacts(child, parent, getArtifacts(dependency, childConfiguration, oldModelBuilder));
+
+        if (parent == root.id) {
+            ModuleDependency moduleDependency = dependency.getModuleDependency();
+            oldModelBuilder.addFirstLevelDependency(moduleDependency, child);
+        }
+    }
+
+    private Set<ResolvedArtifact> getArtifacts(DependencyGraphBuilder.DependencyEdge dependency, DependencyGraphBuilder.ConfigurationNode childConfiguration, ResolvedConfigurationBuilder builder) {
+        Set<ComponentArtifactMetaData> dependencyArtifacts = dependency.getArtifacts(childConfiguration.metaData);
+        if (dependencyArtifacts.isEmpty()) {
+            return childConfiguration.getArtifacts(builder, dependency.getSelector());
+        }
+        Set<ResolvedArtifact> artifacts = new LinkedHashSet<ResolvedArtifact>();
+        for (ComponentArtifactMetaData artifact : dependencyArtifacts) {
+            artifacts.add(builder.newArtifact(childConfiguration.id, childConfiguration.metaData.getComponent(), artifact, artifactResolver));
+        }
+        return artifacts;
+    }
+
+    public void finish(DependencyGraphBuilder.ConfigurationNode root) {
+        attachFailures(builder);
+        builder.done(root.id);
+    }
+
+    private void attachFailures(ResolvedConfigurationBuilder result) {
+        for (Map.Entry<ModuleVersionSelector, BrokenDependency> entry : failuresByRevisionId.entrySet()) {
+            Collection<List<ModuleVersionIdentifier>> paths = calculatePaths(entry.getValue());
+            result.addUnresolvedDependency(new DefaultUnresolvedDependency(entry.getKey(), entry.getValue().failure.withIncomingPaths(paths)));
+        }
+    }
+
+    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<DependencyGraphBuilder.ModuleVersionResolveState, List<ModuleVersionIdentifier>> shortestPaths = new LinkedHashMap<DependencyGraphBuilder.ModuleVersionResolveState, List<ModuleVersionIdentifier>>();
+        List<ModuleVersionIdentifier> rootPath = new ArrayList<ModuleVersionIdentifier>();
+        rootPath.add(root.toId());
+        shortestPaths.put(root.moduleRevision, rootPath);
+
+        Set<DependencyGraphBuilder.ModuleVersionResolveState> directDependees = new LinkedHashSet<DependencyGraphBuilder.ModuleVersionResolveState>();
+        for (DependencyGraphBuilder.ConfigurationNode node : brokenDependency.requiredBy) {
+            directDependees.add(node.moduleRevision);
+        }
+
+        Set<DependencyGraphBuilder.ModuleVersionResolveState> seen = new HashSet<DependencyGraphBuilder.ModuleVersionResolveState>();
+        LinkedList<DependencyGraphBuilder.ModuleVersionResolveState> queue = new LinkedList<DependencyGraphBuilder.ModuleVersionResolveState>();
+        queue.addAll(directDependees);
+        while (!queue.isEmpty()) {
+            DependencyGraphBuilder.ModuleVersionResolveState version = queue.getFirst();
+            if (version == root.moduleRevision) {
+                queue.removeFirst();
+            } else if (seen.add(version)) {
+                for (DependencyGraphBuilder.ModuleVersionResolveState incomingVersion : version.getIncoming()) {
+                    queue.add(0, incomingVersion);
+                }
+            } else {
+                queue.remove();
+                List<ModuleVersionIdentifier> shortest = null;
+                for (DependencyGraphBuilder.ModuleVersionResolveState incomingVersion : version.getIncoming()) {
+                    List<ModuleVersionIdentifier> candidate = shortestPaths.get(incomingVersion);
+                    if (candidate == null) {
+                        continue;
+                    }
+                    if (shortest == null) {
+                        shortest = candidate;
+                    } else if (shortest.size() > candidate.size()) {
+                        shortest = candidate;
+                    }
+
+                }
+                if (shortest == null) {
+                    continue;
+                }
+                List<ModuleVersionIdentifier> path = new ArrayList<ModuleVersionIdentifier>();
+                path.addAll(shortest);
+                path.add(version.id);
+                shortestPaths.put(version, path);
+            }
+        }
+
+        List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
+        for (DependencyGraphBuilder.ModuleVersionResolveState version : directDependees) {
+            List<ModuleVersionIdentifier> path = shortestPaths.get(version);
+            paths.add(path);
+        }
+        return paths;
+    }
+
+    private void addUnresolvedDependency(DependencyGraphBuilder.DependencyEdge dependency, ModuleVersionSelector requested, ModuleVersionResolveException failure) {
+        BrokenDependency breakage = failuresByRevisionId.get(requested);
+        if (breakage == null) {
+            breakage = new BrokenDependency(failure);
+            failuresByRevisionId.put(requested, breakage);
+        }
+        breakage.requiredBy.add(dependency.from);
+    }
+
+    private static class BrokenDependency {
+        final ModuleVersionResolveException failure;
+        final List<DependencyGraphBuilder.ConfigurationNode> requiredBy = new ArrayList<DependencyGraphBuilder.ConfigurationNode>();
+
+        private BrokenDependency(ModuleVersionResolveException failure) {
+            this.failure = failure;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedProjectConfigurationResultGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedProjectConfigurationResultGraphVisitor.java
new file mode 100644
index 0000000..b6f459e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedProjectConfigurationResultGraphVisitor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResultBuilder;
+
+public class ResolvedProjectConfigurationResultGraphVisitor implements DependencyGraphVisitor {
+    private final ResolvedProjectConfigurationResultBuilder builder;
+
+    public ResolvedProjectConfigurationResultGraphVisitor(ResolvedProjectConfigurationResultBuilder builder) {
+        this.builder = builder;
+    }
+
+    @Override
+    public void start(DependencyGraphBuilder.ConfigurationNode root) {
+        builder.registerRoot(root.metaData.getComponent().getComponentId());
+    }
+
+    @Override
+    public void visitNode(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+        ComponentIdentifier componentId = resolvedConfiguration.metaData.getComponent().getComponentId();
+        if (componentId instanceof ProjectComponentIdentifier) {
+            builder.addProjectComponentResult((ProjectComponentIdentifier) componentId, resolvedConfiguration.id.getConfiguration());
+        }
+    }
+
+    @Override
+    public void visitEdge(DependencyGraphBuilder.ConfigurationNode resolvedConfiguration) {
+    }
+
+    @Override
+    public void finish(DependencyGraphBuilder.ConfigurationNode root) {
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CandidateModule.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CandidateModule.java
new file mode 100644
index 0000000..d8668aa
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CandidateModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+
+import java.util.Collection;
+
+/**
+ * Module that participates in conflict resolution. Contains id of the module and candidate versions.
+ */
+public interface CandidateModule {
+    /**
+     * Id of this module
+     */
+    ModuleIdentifier getId();
+
+    /**
+     * Candidate versions of this module. Many times, it has only single version.
+     */
+    Collection<? extends ComponentResolutionState> getVersions();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CompositeConflictResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CompositeConflictResolver.java
new file mode 100644
index 0000000..7b5fd75
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/CompositeConflictResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ModuleConflictResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+class CompositeConflictResolver implements ModuleConflictResolver {
+
+    private final List<ModuleConflictResolver> resolvers = new LinkedList<ModuleConflictResolver>();
+
+    public <T extends ComponentResolutionState> T select(Collection<? extends T> candidates) {
+        for (ModuleConflictResolver r : resolvers) {
+            T selection = r.select(candidates);
+            if (selection != null) {
+                return selection;
+            }
+        }
+        throw new IllegalStateException(this.getClass().getSimpleName()
+                + " was unable to select a candidate using resolvers: " + resolvers + ". Candidates: " + candidates);
+    }
+
+    void addFirst(ModuleConflictResolver conflictResolver) {
+        resolvers.add(0, conflictResolver);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainer.java
new file mode 100644
index 0000000..a59f063
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainer.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import org.gradle.api.Nullable;
+
+import java.util.*;
+
+import static com.google.common.collect.Lists.newLinkedList;
+import static com.google.common.collect.Maps.newHashMap;
+import static java.util.Arrays.asList;
+
+/**
+ * Generic container for conflicts. It's generic so that hopefully it's easier to comprehend (and test).
+ */
+class ConflictContainer<K, T> {
+
+    final LinkedList<Conflict> conflicts = newLinkedList();
+
+    private final Map<K, Collection<? extends T>> elements = newHashMap();
+    private final Multimap<K, K> targetToSource = LinkedHashMultimap.create();
+
+    /**
+     * Adds new element and returns a conflict instance if given element is conflicted. Element is conflicted when:
+     *  - has more than 1 candidate
+     *  - is in conflict with an existing element (via replacedBy relationship)
+     *
+     * @param target an element of some sort
+     * @param candidates candidates for given element
+     * @param replacedBy optional element that replaces the target
+     */
+    public Conflict newElement(K target, Collection<? extends T> candidates, @Nullable K replacedBy) {
+        elements.put(target, candidates);
+        if (replacedBy != null) {
+            targetToSource.put(replacedBy, target);
+            if (elements.containsKey(replacedBy)) {
+                //1) we've seen the replacement, register new conflict and return
+                return registerConflict(target, replacedBy);
+            }
+        }
+
+        Collection<K> replacementSource = targetToSource.get(target);
+        if (!replacementSource.isEmpty()) {
+            //2) new module is a replacement to a module we've seen already, register conflict and return
+            return registerConflict(replacementSource, target);
+        }
+
+        if (candidates.size() > 1) {
+            //3) new module has more than 1 version, register conflict and return
+            return registerConflict(target, target);
+        }
+        return null;
+    }
+
+    private Conflict registerConflict(Collection<K> targets, K replacedBy) {
+        assert !targets.isEmpty();
+
+        //replacement candidates are the only important candidates
+        Collection<? extends T> candidates = elements.get(replacedBy);
+        assert candidates != null;
+
+        Set<K> participants = new LinkedHashSet<K>();
+        participants.addAll(targets);
+        participants.add(replacedBy);
+
+        //We need to ensure that the conflict is orderly injected to the list of conflicts
+        //Brand new conflict goes to the end
+        //If we find any matching conflict we have to hook up with it
+
+        //Find an existing matching conflict
+        for (Conflict c : conflicts) {
+            if (!Sets.intersection(participants, c.participants).isEmpty()) {
+                //there is already registered conflict with at least one matching participant, hook up to this conflict
+                c.candidates = candidates;
+                c.participants.addAll(participants);
+                return c;
+            }
+        }
+
+        //No conflict with matching participants found, create new
+        Conflict c = new Conflict(participants, candidates);
+        conflicts.add(c);
+        return c;
+    }
+
+    private Conflict registerConflict(K target, K replacedBy) {
+        return registerConflict(asList(target), replacedBy);
+    }
+
+    public int getSize() {
+        return conflicts.size();
+    }
+
+    public Conflict popConflict() {
+        assert !conflicts.isEmpty();
+        return conflicts.pop();
+    }
+
+    class Conflict {
+        Set<K> participants;
+        Collection<? extends T> candidates;
+
+        public Conflict(Set<K> participants, Collection<? extends T> candidates) {
+            this.participants = participants;
+            this.candidates = candidates;
+        }
+
+        public String toString() {
+            return Joiner.on(",").join(participants) + ":" + Joiner.on(",").join(candidates);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictHandler.java
new file mode 100644
index 0000000..f2c40ad
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ModuleConflictResolver;
+
+public interface ConflictHandler {
+
+    /**
+     * Registers new module and returns information about any potential conflict
+     */
+    PotentialConflict registerModule(CandidateModule newModule);
+
+    /**
+     * Informs whether there is any conflict at present
+     */
+    boolean hasConflicts();
+
+    /**
+     * Resolves next conflict and trigger provided action after the resolution
+     */
+    void resolveNextConflict(Action<ConflictResolutionResult> resolutionAction);
+
+    /**
+     * Registers a conflict resolver that is used for resolving conflicts. It is possible to register multiple resolvers.
+     */
+    void registerResolver(ModuleConflictResolver conflictResolver);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictResolutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictResolutionResult.java
new file mode 100644
index 0000000..44874dd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictResolutionResult.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+
+public interface ConflictResolutionResult {
+
+    /**
+     * Conflict this result is about.
+     */
+    PotentialConflict getConflict();
+
+    /**
+     * The actual selected version.
+     */
+    <T extends ComponentResolutionState> T getSelected();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandler.java
new file mode 100644
index 0000000..30d5f0f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ModuleConflictResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.PotentialConflictFactory.potentialConflict;
+
+public class DefaultConflictHandler implements ConflictHandler {
+
+    private final static Logger LOGGER = Logging.getLogger(DefaultConflictHandler.class);
+
+    private final CompositeConflictResolver compositeResolver = new CompositeConflictResolver();
+    private final ConflictContainer<ModuleIdentifier, ComponentResolutionState> conflicts = new ConflictContainer<ModuleIdentifier, ComponentResolutionState>();
+    private final ModuleReplacementsData moduleReplacements;
+
+    public DefaultConflictHandler(ModuleConflictResolver conflictResolver, ModuleReplacementsData moduleReplacements) {
+        this.moduleReplacements = moduleReplacements;
+        this.compositeResolver.addFirst(conflictResolver);
+    }
+
+    /**
+     * Registers new newModule and returns an instance of a conflict if conflict exists.
+     */
+    @Nullable
+    public PotentialConflict registerModule(CandidateModule newModule) {
+        ModuleIdentifier replacedBy = moduleReplacements.getReplacementFor(newModule.getId());
+        return potentialConflict(conflicts.newElement(newModule.getId(), newModule.getVersions(), replacedBy));
+    }
+
+    /**
+     * Informs if there are any batched up conflicts.
+     */
+    public boolean hasConflicts() {
+        return conflicts.getSize() > 0;
+    }
+
+    /**
+     * Resolves the conflict by delegating to the conflict resolver who selects single version from given candidates. Executes provided action against the conflict resolution result object.
+     */
+    public void resolveNextConflict(Action<ConflictResolutionResult> resolutionAction) {
+        assert hasConflicts();
+        ConflictContainer.Conflict conflict = conflicts.popConflict();
+        ComponentResolutionState selected = compositeResolver.select(conflict.candidates);
+        ConflictResolutionResult result = new DefaultConflictResolutionResult(potentialConflict(conflict), selected);
+        resolutionAction.execute(result);
+        LOGGER.debug("Selected {} from conflicting modules {}.", selected, conflict.candidates);
+    }
+
+    public void registerResolver(ModuleConflictResolver conflictResolver) {
+        compositeResolver.addFirst(conflictResolver);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictResolutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictResolutionResult.java
new file mode 100644
index 0000000..0a55950
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictResolutionResult.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+
+class DefaultConflictResolutionResult implements ConflictResolutionResult {
+    private final PotentialConflict conflict;
+    private final ComponentResolutionState selected;
+
+    public DefaultConflictResolutionResult(PotentialConflict conflict, ComponentResolutionState selected) {
+        this.conflict = conflict;
+        this.selected = selected;
+    }
+
+    public PotentialConflict getConflict() {
+        return conflict;
+    }
+
+    public ComponentResolutionState getSelected() {
+        return selected;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflict.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflict.java
new file mode 100644
index 0000000..521a13f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflict.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ModuleIdentifier;
+
+public interface PotentialConflict {
+    /**
+     * Executes an action with each module that participates in the conflict.
+     * In a typical version conflict, e.g. org:foo:1.0 VS org:foo:2.0 there is only one participating module: org:foo.
+     */
+    void withParticipatingModules(Action<ModuleIdentifier> action);
+
+    /**
+     * informs whether the conflict exists
+     */
+    boolean conflictExists();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflictFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflictFactory.java
new file mode 100644
index 0000000..f348416
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/PotentialConflictFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
+
+class PotentialConflictFactory {
+
+    static PotentialConflict potentialConflict(final ConflictContainer<ModuleIdentifier, ? extends ComponentResolutionState>.Conflict conflict) {
+        return new PotentialConflict() {
+            public boolean conflictExists() {
+                return conflict != null;
+            }
+
+            public void withParticipatingModules(Action<ModuleIdentifier> action) {
+                assert conflictExists();
+                for (ModuleIdentifier participant : conflict.participants) {
+                    action.execute(participant);
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java
new file mode 100644
index 0000000..cf46273
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultResolvedConfigurationBuilder.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.artifacts.UnresolvedDependency;
+import org.gradle.api.internal.artifacts.DefaultResolvedArtifact;
+import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.DefaultBuildableArtifactResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.DefaultResolvedModuleVersion;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+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 final TransientConfigurationResultsBuilder builder;
+
+    public DefaultResolvedConfigurationBuilder(TransientConfigurationResultsBuilder builder) {
+        this.builder = builder;
+    }
+
+    public void addUnresolvedDependency(UnresolvedDependency unresolvedDependency) {
+        unresolvedDependencies.add(unresolvedDependency);
+    }
+
+    public void addFirstLevelDependency(ModuleDependency moduleDependency, ResolvedConfigurationIdentifier dependency) {
+        builder.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) {
+        builder.done(root);
+    }
+
+    public void addChild(ResolvedConfigurationIdentifier parent, ResolvedConfigurationIdentifier child) {
+        builder.parentChildMapping(parent, child);
+    }
+
+    public void addParentSpecificArtifacts(ResolvedConfigurationIdentifier child, ResolvedConfigurationIdentifier parent, Set<ResolvedArtifact> artifacts) {
+        for (ResolvedArtifact a : artifacts) {
+            builder.parentSpecificArtifact(child, parent, ((DefaultResolvedArtifact) a).getId());
+        }
+    }
+
+    public void newResolvedDependency(ResolvedConfigurationIdentifier id) {
+        builder.resolvedDependency(id);
+    }
+
+    public ResolvedArtifact newArtifact(ResolvedConfigurationIdentifier owner, ComponentResolveMetaData component, ComponentArtifactMetaData artifact, ArtifactResolver artifactResolver) {
+        Factory<File> artifactSource = new LazyArtifactSource(artifact, component.getSource(), artifactResolver);
+        long id = idGenerator.generateId();
+        ResolvedArtifact newArtifact = new DefaultResolvedArtifact(new DefaultResolvedModuleVersion(owner.getId()), artifact.getName(), artifactSource, id);
+        artifacts.put(id, newArtifact);
+        return newArtifact;
+    }
+
+    public boolean hasError() {
+        return !unresolvedDependencies.isEmpty();
+    }
+
+    public TransientConfigurationResults more() {
+        return builder.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 LazyArtifactSource implements Factory<File> {
+        private final ArtifactResolver artifactResolver;
+        private final ModuleSource moduleSource;
+        private final ComponentArtifactMetaData artifact;
+
+        private LazyArtifactSource(ComponentArtifactMetaData artifact, ModuleSource moduleSource, ArtifactResolver artifactResolver) {
+            this.artifact = artifact;
+            this.artifactResolver = artifactResolver;
+            this.moduleSource = moduleSource;
+        }
+
+        public File create() {
+            DefaultBuildableArtifactResolveResult result = new DefaultBuildableArtifactResolveResult();
+            artifactResolver.resolveArtifact(artifact, moduleSource, result);
+            return result.getFile();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.java
new file mode 100644
index 0000000..45f1a90
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/DefaultTransientConfigurationResults.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.oldresult;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedDependency;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultTransientConfigurationResults implements TransientConfigurationResults {
+
+    final Map<ModuleDependency, ResolvedDependency> firstLevelDependencies = new LinkedHashMap<ModuleDependency, ResolvedDependency>();
+    ResolvedDependency root;
+
+    public Map<ModuleDependency, ResolvedDependency> getFirstLevelDependencies() {
+        return firstLevelDependencies;
+    }
+
+    public ResolvedDependency getRoot() {
+        return root;
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.java
new file mode 100644
index 0000000..5fffd8e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationBuilder.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.artifacts.ivyservice.resolveengine.oldresult;
+
+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.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+
+import java.util.Set;
+
+//builds old model of resolved dependency graph based on the result events
+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, ComponentResolveMetaData component, ComponentArtifactMetaData artifact, ArtifactResolver artifactResolver);
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedConfigurationResults.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/ResolvedContentsMapping.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.java
new file mode 100644
index 0000000..cee01f3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResults.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.resolveengine.oldresult;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ResolvedDependency;
+
+import java.util.Map;
+
+public interface TransientConfigurationResults {
+
+    Map<ModuleDependency, ResolvedDependency> getFirstLevelDependencies();
+
+    ResolvedDependency getRoot();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResultsBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResultsBuilder.java
new file mode 100644
index 0000000..8b1206f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/oldresult/TransientConfigurationResultsBuilder.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.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.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.util.Clock;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
+//TODO SF unit coverage
+public class TransientConfigurationResultsBuilder {
+
+    private final static Logger LOG = Logging.getLogger(TransientConfigurationResultsBuilder.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 TransientConfigurationResultsBuilder(BinaryStore binaryStore, Store<TransientConfigurationResults> cache) {
+        this.binaryStore = binaryStore;
+        this.cache = cache;
+    }
+
+    private void writeId(final byte type, final ResolvedConfigurationIdentifier... ids) {
+        binaryStore.write(new BinaryStore.WriteAction() {
+            public void write(Encoder 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(Encoder encoder) throws IOException {
+                encoder.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(Decoder decoder) throws IOException {
+                                return deserialize(decoder, mapping);
+                            }
+                        });
+                    } finally {
+                        try {
+                            binaryData.close();
+                        } catch (IOException e) {
+                            throw throwAsUncheckedException(e);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    private TransientConfigurationResults deserialize(Decoder decoder, ResolvedContentsMapping mapping) {
+        Clock clock = new Clock();
+        Map<ResolvedConfigurationIdentifier, DefaultResolvedDependency> allDependencies = new HashMap<ResolvedConfigurationIdentifier, DefaultResolvedDependency>();
+        DefaultTransientConfigurationResults results = new DefaultTransientConfigurationResults();
+        int valuesRead = 0;
+        byte type = -1;
+        try {
+            while (true) {
+                type = decoder.readByte();
+                ResolvedConfigurationIdentifier id;
+                valuesRead++;
+                switch (type) {
+                    case NEW_DEP:
+                        id = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        allDependencies.put(id, new DefaultResolvedDependency(id.getId(), id.getConfiguration()));
+                        break;
+                    case ROOT:
+                        id = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        results.root = allDependencies.get(id);
+                        if (results.root == null) {
+                            throw new IllegalStateException(String.format("Unexpected root id %s. Seen ids: %s", id, 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 = allDependencies.get(id);
+                        if (dependency == null) {
+                            throw new IllegalStateException(String.format("Unexpected first level id %s. Seen ids: %s", id, 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 = allDependencies.get(parentId);
+                        DefaultResolvedDependency child = allDependencies.get(childId);
+                        if (parent == null) {
+                            throw new IllegalStateException(String.format("Unexpected parent dependency id %s. Seen ids: %s", parentId, allDependencies.keySet()));
+                        }
+                        if (child == null) {
+                            throw new IllegalStateException(String.format("Unexpected child dependency id %s. Seen ids: %s", childId, allDependencies.keySet()));
+                        }
+                        parent.addChild(child);
+                        break;
+                    case PARENT_ARTIFACT:
+                        ResolvedConfigurationIdentifier artifactParentId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        ResolvedConfigurationIdentifier artifactChildId = resolvedConfigurationIdentifierSerializer.read(decoder);
+                        DefaultResolvedDependency artifactParent = allDependencies.get(artifactParentId);
+                        DefaultResolvedDependency artifactChild = allDependencies.get(artifactChildId);
+                        if (artifactParent == null) {
+                            throw new IllegalStateException(String.format("Unexpected parent dependency id %s. Seen ids: %s", artifactParentId, allDependencies.keySet()));
+                        }
+                        if (artifactChild == null) {
+                            throw new IllegalStateException(String.format("Unexpected child dependency id %s. Seen ids: %s", artifactChildId, 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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResult.java
new file mode 100644
index 0000000..fe87fac
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultResolvedProjectConfigurationResult implements ResolvedProjectConfigurationResult {
+    private final ProjectComponentIdentifier id;
+    private final Set<String> targetConfigurations = new LinkedHashSet<String>();
+
+    public DefaultResolvedProjectConfigurationResult(ProjectComponentIdentifier id) {
+        this.id = id;
+    }
+
+    @Override
+    public ProjectComponentIdentifier getId() {
+        return id;
+    }
+
+    @Override
+    public Set<String> getTargetConfigurations() {
+        return targetConfigurations;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultResolvedProjectConfigurationResult that = (DefaultResolvedProjectConfigurationResult) o;
+
+        if (!id.equals(that.id)) {
+            return false;
+        }
+        if (!targetConfigurations.equals(that.targetConfigurations)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id.hashCode();
+        result = 31 * result + targetConfigurations.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return id + ":" + targetConfigurations;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResultBuilder.java
new file mode 100644
index 0000000..aa8349d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResultBuilder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+public class DefaultResolvedProjectConfigurationResultBuilder implements ResolvedProjectConfigurationResultBuilder {
+    private final Map<ProjectComponentIdentifier, DefaultResolvedProjectConfigurationResult> results = new LinkedHashMap<ProjectComponentIdentifier, DefaultResolvedProjectConfigurationResult>();
+    private ComponentIdentifier rootId;
+
+    @Override
+    public void registerRoot(ComponentIdentifier componentId) {
+        this.rootId = componentId;
+    }
+
+    @Override
+    public void addProjectComponentResult(ProjectComponentIdentifier componentId, String configurationName) {
+        if (rootId.equals(componentId)) {
+            return;
+        }
+        getOrCreate(componentId).getTargetConfigurations().add(configurationName);
+    }
+
+    private DefaultResolvedProjectConfigurationResult getOrCreate(ProjectComponentIdentifier componentId) {
+        DefaultResolvedProjectConfigurationResult result = results.get(componentId);
+        if (result == null) {
+            result = new DefaultResolvedProjectConfigurationResult(componentId);
+            results.put(componentId, result);
+        }
+        return result;
+    }
+
+    @Override
+    public ResolvedProjectConfigurationResults complete() {
+        return new DefaultResolvedProjectConfigurationResults(new LinkedHashSet<ResolvedProjectConfigurationResult>(results.values()));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResults.java
new file mode 100644
index 0000000..9895117
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/DefaultResolvedProjectConfigurationResults.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import java.util.Set;
+
+public class DefaultResolvedProjectConfigurationResults implements ResolvedProjectConfigurationResults {
+    private final Set<ResolvedProjectConfigurationResult> results;
+
+    public DefaultResolvedProjectConfigurationResults(Set<ResolvedProjectConfigurationResult> results) {
+        this.results = results;
+    }
+
+    @Override
+    public Set<ResolvedProjectConfigurationResult> getAllProjectConfigurationResults() {
+        return results;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResult.java
new file mode 100644
index 0000000..cc1d8f3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResult.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+
+import java.util.Set;
+
+public interface ResolvedProjectConfigurationResult {
+    ProjectComponentIdentifier getId();
+
+    Set<String> getTargetConfigurations();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResultBuilder.java
new file mode 100644
index 0000000..9d70bf6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResultBuilder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+
+public interface ResolvedProjectConfigurationResultBuilder {
+    void registerRoot(ComponentIdentifier componentId);
+    void addProjectComponentResult(ProjectComponentIdentifier componentId, String configurationName);
+    ResolvedProjectConfigurationResults complete();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResults.java
new file mode 100644
index 0000000..0a25524
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/projectresult/ResolvedProjectConfigurationResults.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.projectresult;
+
+import java.util.Set;
+
+public interface ResolvedProjectConfigurationResults {
+    Set<ResolvedProjectConfigurationResult> getAllProjectConfigurationResults();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
new file mode 100644
index 0000000..bbcb440
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.resolveengine.result;
+
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.artifacts.result.ResolvedComponentResult;
+import org.gradle.api.artifacts.result.ResolvedDependencyResult;
+import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult;
+import org.gradle.api.internal.artifacts.result.DefaultUnresolvedDependencyResult;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
+public class CachingDependencyResultFactory {
+
+    private final Map<List, DefaultUnresolvedDependencyResult> unresolvedDependencies = new HashMap<List, DefaultUnresolvedDependencyResult>();
+    private final Map<List, DefaultResolvedDependencyResult> resolvedDependencies = new HashMap<List, DefaultResolvedDependencyResult>();
+
+    public UnresolvedDependencyResult createUnresolvedDependency(ComponentSelector requested, ResolvedComponentResult from,
+                                                       ComponentSelectionReason reason, ModuleVersionResolveException failure) {
+        List<Object> key = asList(requested, from);
+        if (!unresolvedDependencies.containsKey(key)) {
+            unresolvedDependencies.put(key, new DefaultUnresolvedDependencyResult(requested, reason, from, failure));
+        }
+        return unresolvedDependencies.get(key);
+    }
+
+    public ResolvedDependencyResult createResolvedDependency(ComponentSelector requested, ResolvedComponentResult from, DefaultResolvedComponentResult selected) {
+        List<Object> key = asList(requested, from, selected);
+        if (!resolvedDependencies.containsKey(key)) {
+            resolvedDependencies.put(key, new DefaultResolvedDependencyResult(requested, selected, from));
+        }
+        return resolvedDependencies.get(key);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.java
new file mode 100644
index 0000000..9958ab2
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializer.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.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.IOException;
+
+public class ComponentIdentifierSerializer implements Serializer<ComponentIdentifier> {
+    public ComponentIdentifier read(Decoder decoder) throws IOException {
+        byte id = decoder.readByte();
+
+        if(Implementation.BUILD.getId() == id) {
+            return new DefaultProjectComponentIdentifier(decoder.readString());
+        } else if(Implementation.MODULE.getId() == id) {
+            return new DefaultModuleComponentIdentifier(decoder.readString(), decoder.readString(), decoder.readString());
+        }
+
+        throw new IllegalArgumentException("Unable to find component identifier with id: " + id);
+    }
+
+    public void write(Encoder encoder, ComponentIdentifier value) throws IOException {
+        if(value == null) {
+            throw new IllegalArgumentException("Provided component identifier may not be null");
+        }
+
+        if(value instanceof DefaultModuleComponentIdentifier) {
+            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier)value;
+            encoder.writeByte(Implementation.MODULE.getId());
+            encoder.writeString(moduleComponentIdentifier.getGroup());
+            encoder.writeString(moduleComponentIdentifier.getModule());
+            encoder.writeString(moduleComponentIdentifier.getVersion());
+        } else if(value instanceof DefaultProjectComponentIdentifier) {
+            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier)value;
+            encoder.writeByte(Implementation.BUILD.getId());
+            encoder.writeString(projectComponentIdentifier.getProjectPath());
+        } else {
+            throw new IllegalArgumentException("Unsupported component identifier class: " + value.getClass());
+        }
+    }
+
+    private static enum Implementation {
+        MODULE((byte) 1), BUILD((byte) 2);
+
+        private final byte id;
+
+        private Implementation(byte id) {
+            this.id = id;
+        }
+
+        private byte getId() {
+            return id;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.java
new file mode 100644
index 0000000..689abe8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializer.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.ComponentSelectionReason;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.IOException;
+
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.*;
+
+public class ComponentSelectionReasonSerializer implements Serializer<ComponentSelectionReason> {
+
+    private static final BiMap<Byte, ComponentSelectionReason> 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 ComponentSelectionReason read(Decoder decoder) throws IOException {
+        byte id = decoder.readByte();
+        ComponentSelectionReason 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, ComponentSelectionReason 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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.java
new file mode 100644
index 0000000..9265777
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializer.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.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.IOException;
+
+public class ComponentSelectorSerializer implements Serializer<ComponentSelector> {
+    public ComponentSelector read(Decoder decoder) throws IOException {
+        byte id = decoder.readByte();
+
+        if(Implementation.BUILD.getId() == id) {
+            return new DefaultProjectComponentSelector(decoder.readString());
+        } else if(Implementation.MODULE.getId() == id) {
+            return new DefaultModuleComponentSelector(decoder.readString(), decoder.readString(), decoder.readString());
+        }
+
+        throw new IllegalArgumentException("Unable to find component selector with id: " + id);
+    }
+
+    public void write(Encoder encoder, ComponentSelector value) throws IOException {
+        if(value == null) {
+            throw new IllegalArgumentException("Provided component selector may not be null");
+        }
+
+        if(value instanceof DefaultModuleComponentSelector) {
+            ModuleComponentSelector moduleComponentSelector = (ModuleComponentSelector)value;
+            encoder.writeByte(Implementation.MODULE.getId());
+            encoder.writeString(moduleComponentSelector.getGroup());
+            encoder.writeString(moduleComponentSelector.getModule());
+            encoder.writeString(moduleComponentSelector.getVersion());
+        } else if(value instanceof DefaultProjectComponentSelector) {
+            ProjectComponentSelector projectComponentSelector = (ProjectComponentSelector)value;
+            encoder.writeByte(Implementation.BUILD.getId());
+            encoder.writeString(projectComponentSelector.getProjectPath());
+        } else {
+            throw new IllegalArgumentException("Unsupported component selector class: " + value.getClass());
+        }
+    }
+
+    private static enum Implementation {
+        MODULE((byte) 1), BUILD((byte) 2);
+
+        private final byte id;
+
+        private Implementation(byte id) {
+            this.id = id;
+        }
+
+        private byte getId() {
+            return id;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultInternalDependencyResult.java
new file mode 100644
index 0000000..0c38b0a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public class DefaultInternalDependencyResult implements InternalDependencyResult {
+
+    private final ComponentSelector requested;
+    private final ModuleVersionIdentifier selected;
+    private final ComponentSelectionReason reason;
+    private ModuleVersionResolveException failure;
+
+    public DefaultInternalDependencyResult(ComponentSelector requested,
+                                           ModuleVersionIdentifier selected,
+                                           ComponentSelectionReason reason,
+                                           ModuleVersionResolveException failure) {
+        assert requested != null;
+        assert failure != null || selected != null;
+
+        this.requested = requested;
+        this.reason = reason;
+        this.selected = selected;
+        this.failure = failure;
+    }
+
+    public ComponentSelector getRequested() {
+        return requested;
+    }
+
+    public ModuleVersionIdentifier getSelected() {
+        return selected;
+    }
+
+    public ComponentSelectionReason getReason() {
+        return reason;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        return failure;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.java
new file mode 100644
index 0000000..f82f67e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultModuleVersionSelection.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.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+
+class DefaultModuleVersionSelection implements ModuleVersionSelection {
+    private ModuleVersionIdentifier id;
+    private ComponentSelectionReason reason;
+    private ComponentIdentifier componentIdentifier;
+
+    public DefaultModuleVersionSelection(ModuleVersionIdentifier id, ComponentSelectionReason reason, ComponentIdentifier componentIdentifier) {
+        this.id = id;
+        this.reason = reason;
+        this.componentIdentifier = componentIdentifier;
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return id;
+    }
+
+    public ComponentSelectionReason getSelectionReason() {
+        return reason;
+    }
+
+    public ComponentIdentifier getComponentId() {
+        return componentIdentifier;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
new file mode 100644
index 0000000..ca857f5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.*;
+import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult;
+import org.gradle.internal.Factory;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultResolutionResultBuilder implements ResolutionResultBuilder {
+
+    private DefaultResolvedComponentResult rootModule;
+
+    private Map<ModuleVersionIdentifier, DefaultResolvedComponentResult> modules
+            = new LinkedHashMap<ModuleVersionIdentifier, DefaultResolvedComponentResult>();
+
+    CachingDependencyResultFactory dependencyResultFactory = new CachingDependencyResultFactory();
+
+    public DefaultResolutionResultBuilder start(ModuleVersionIdentifier root, ComponentIdentifier componentIdentifier) {
+        rootModule = createOrGet(root, VersionSelectionReasons.ROOT, componentIdentifier);
+        return this;
+    }
+
+    public ResolutionResult complete() {
+        return new DefaultResolutionResult(new RootFactory(rootModule));
+    }
+
+    public void resolvedModuleVersion(ModuleVersionSelection moduleVersion) {
+        createOrGet(moduleVersion.getId(), moduleVersion.getSelectionReason(), moduleVersion.getComponentId());
+    }
+
+    public void resolvedConfiguration(ModuleVersionIdentifier id, Collection<? extends InternalDependencyResult> dependencies) {
+        for (InternalDependencyResult d : dependencies) {
+            DefaultResolvedComponentResult from = modules.get(id);
+            DependencyResult dependency;
+            if (d.getFailure() != null) {
+                dependency = dependencyResultFactory.createUnresolvedDependency(d.getRequested(), from, d.getReason(), d.getFailure());
+            } else {
+                DefaultResolvedComponentResult selected = modules.get(d.getSelected());
+                dependency = dependencyResultFactory.createResolvedDependency(d.getRequested(), from, selected);
+                selected.addDependent((ResolvedDependencyResult) dependency);
+            }
+            from.addDependency(dependency);
+        }
+    }
+
+    private DefaultResolvedComponentResult createOrGet(ModuleVersionIdentifier id, ComponentSelectionReason selectionReason, ComponentIdentifier componentId) {
+        if (!modules.containsKey(id)) {
+            modules.put(id, new DefaultResolvedComponentResult(id, selectionReason, componentId));
+        }
+        return modules.get(id);
+    }
+
+    private static class RootFactory implements Factory<ResolvedComponentResult> {
+        private DefaultResolvedComponentResult rootModule;
+
+        public RootFactory(DefaultResolvedComponentResult rootModule) {
+            this.rootModule = rootModule;
+        }
+
+        public ResolvedComponentResult create() {
+            return rootModule;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
new file mode 100644
index 0000000..8ffaba1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResult.java
@@ -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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public interface InternalDependencyResult {
+
+    ComponentSelector getRequested();
+
+    @Nullable
+    ModuleVersionResolveException getFailure();
+
+    @Nullable
+    ModuleVersionIdentifier getSelected();
+
+    /**
+     * Not null only when failure is not null.
+     */
+    @Nullable
+    ComponentSelectionReason getReason();
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.java
new file mode 100644
index 0000000..0d9d66f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializer.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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.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 ComponentSelectorSerializer componentSelectorSerializer = new ComponentSelectorSerializer();
+    private final ComponentSelectionReasonSerializer componentSelectionReasonSerializer = new ComponentSelectionReasonSerializer();
+    private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
+
+    public InternalDependencyResult read(Decoder decoder, Map<ComponentSelector, ModuleVersionResolveException> failures) throws IOException {
+        ComponentSelector requested = componentSelectorSerializer.read(decoder);
+        byte resultByte = decoder.readByte();
+        if (resultByte == SUCCESSFUL) {
+            ModuleVersionIdentifier selected = moduleVersionIdentifierSerializer.read(decoder);
+            return new DefaultInternalDependencyResult(requested, selected, null, null);
+        } else if (resultByte == FAILED) {
+            ComponentSelectionReason reason = componentSelectionReasonSerializer.read(decoder);
+            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 {
+        componentSelectorSerializer.write(encoder, value.getRequested());
+        if (value.getFailure() == null) {
+            encoder.writeByte(SUCCESSFUL);
+            moduleVersionIdentifierSerializer.write(encoder, value.getSelected());
+        } else {
+            encoder.writeByte(FAILED);
+            componentSelectionReasonSerializer.write(encoder, value.getReason());
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.java
new file mode 100644
index 0000000..9e3b5dc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelection.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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+
+public interface ModuleVersionSelection {
+
+    ModuleVersionIdentifier getId();
+
+    ComponentSelectionReason getSelectionReason();
+
+    ComponentIdentifier getComponentId();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.java
new file mode 100644
index 0000000..a5b6af1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializer.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.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.IOException;
+
+public class ModuleVersionSelectionSerializer implements Serializer<ModuleVersionSelection> {
+
+    private final ModuleVersionIdentifierSerializer idSerializer = new ModuleVersionIdentifierSerializer();
+    private final ComponentSelectionReasonSerializer reasonSerializer = new ComponentSelectionReasonSerializer();
+    private final ComponentIdentifierSerializer componentIdSerializer = new ComponentIdentifierSerializer();
+
+    public ModuleVersionSelection read(Decoder decoder) throws IOException {
+        ModuleVersionIdentifier id = idSerializer.read(decoder);
+        ComponentSelectionReason reason = reasonSerializer.read(decoder);
+        ComponentIdentifier componentId = componentIdSerializer.read(decoder);
+        return new DefaultModuleVersionSelection(id, reason, componentId);
+    }
+
+    public void write(Encoder encoder, ModuleVersionSelection value) throws IOException {
+        idSerializer.write(encoder, value.getId());
+        reasonSerializer.write(encoder, value.getSelectionReason());
+        componentIdSerializer.write(encoder, value.getComponentId());
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultBuilder.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
new file mode 100644
index 0000000..a647513
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.artifacts.result.ResolvedComponentResult;
+import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+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.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.util.Clock;
+
+import java.io.IOException;
+import java.util.*;
+
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
+public class StreamingResolutionResultBuilder implements ResolutionResultBuilder {
+
+    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<ComponentSelector, ModuleVersionResolveException> failures = new HashMap<ComponentSelector, ModuleVersionResolveException>();
+    private final BinaryStore store;
+    private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
+    private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
+    private final Store<ResolvedComponentResult> cache;
+    private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
+    private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
+
+    public StreamingResolutionResultBuilder(BinaryStore store, Store<ResolvedComponentResult> cache) {
+        this.store = store;
+        this.cache = cache;
+    }
+
+    public ResolutionResult complete() {
+        store.write(new BinaryStore.WriteAction() {
+            public void write(Encoder encoder) throws IOException {
+                encoder.writeByte(DONE);
+            }
+        });
+        BinaryStore.BinaryData data = store.done();
+        RootFactory rootSource = new RootFactory(data, failures, cache);
+        return new DefaultResolutionResult(rootSource);
+    }
+
+    public ResolutionResultBuilder start(final ModuleVersionIdentifier root, final ComponentIdentifier componentIdentifier) {
+        store.write(new BinaryStore.WriteAction() {
+            public void write(Encoder encoder) throws IOException {
+                encoder.writeByte(ROOT);
+                moduleVersionIdentifierSerializer.write(encoder, root);
+                componentIdentifierSerializer.write(encoder, componentIdentifier);
+            }
+        });
+        return this;
+    }
+
+    Set<ModuleVersionIdentifier> visitedModules = new HashSet<ModuleVersionIdentifier>();
+
+    public void resolvedModuleVersion(final ModuleVersionSelection moduleVersion) {
+        if (visitedModules.add(moduleVersion.getId())) {
+            store.write(new BinaryStore.WriteAction() {
+                public void write(Encoder 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 BinaryStore.WriteAction() {
+                public void write(Encoder encoder) throws IOException {
+                    encoder.writeByte(DEPENDENCY);
+                    moduleVersionIdentifierSerializer.write(encoder, from);
+                    encoder.writeSmallInt(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<ResolvedComponentResult> {
+
+        private final static Logger LOG = Logging.getLogger(RootFactory.class);
+        private final ModuleVersionSelectionSerializer moduleVersionSelectionSerializer = new ModuleVersionSelectionSerializer();
+
+        private final BinaryStore.BinaryData data;
+        private final Map<ComponentSelector, ModuleVersionResolveException> failures;
+        private final Store<ResolvedComponentResult> cache;
+        private final Object lock = new Object();
+        private final ModuleVersionIdentifierSerializer moduleVersionIdentifierSerializer = new ModuleVersionIdentifierSerializer();
+        private final InternalDependencyResultSerializer internalDependencyResultSerializer = new InternalDependencyResultSerializer();
+        private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
+
+        public RootFactory(BinaryStore.BinaryData data, Map<ComponentSelector, ModuleVersionResolveException> failures,
+                           Store<ResolvedComponentResult> cache) {
+            this.data = data;
+            this.failures = failures;
+            this.cache = cache;
+        }
+
+        public ResolvedComponentResult create() {
+            synchronized (lock) {
+                return cache.load(new Factory<ResolvedComponentResult>() {
+                    public ResolvedComponentResult create() {
+                        try {
+                            return data.read(new BinaryStore.ReadAction<ResolvedComponentResult>() {
+                                public ResolvedComponentResult read(Decoder decoder) throws IOException {
+                                    return deserialize(decoder);
+                                }
+                            });
+                        } finally {
+                            try {
+                                data.close();
+                            } catch (IOException e) {
+                                throw throwAsUncheckedException(e);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+
+        private ResolvedComponentResult deserialize(Decoder decoder) {
+            int valuesRead = 0;
+            byte type = -1;
+            Clock clock = new Clock();
+            try {
+                DefaultResolutionResultBuilder builder = new DefaultResolutionResultBuilder();
+                while (true) {
+                    type = decoder.readByte();
+                    valuesRead++;
+                    switch (type) {
+                        case ROOT:
+                            ModuleVersionIdentifier id = moduleVersionIdentifierSerializer.read(decoder);
+                            ComponentIdentifier componentIdentifier = componentIdentifierSerializer.read(decoder);
+                            builder.start(id, componentIdentifier);
+                            break;
+                        case MODULE:
+                            ModuleVersionSelection sel = moduleVersionSelectionSerializer.read(decoder);
+                            builder.resolvedModuleVersion(sel);
+                            break;
+                        case DEPENDENCY:
+                            id = moduleVersionIdentifierSerializer.read(decoder);
+                            int size = decoder.readSmallInt();
+                            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:
+                            ResolvedComponentResult 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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.java
new file mode 100644
index 0000000..20e2094
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasons.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.resolveengine.result;
+
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+
+public class VersionSelectionReasons {
+    public static final ComponentSelectionReason REQUESTED = new DefaultComponentSelectionReason(false, false, false, true, "requested");
+    public static final ComponentSelectionReason ROOT = new DefaultComponentSelectionReason(false, false, false, true, "root");
+    public static final ComponentSelectionReason FORCED = new DefaultComponentSelectionReason(true, false, false, false, "forced");
+    public static final ComponentSelectionReason CONFLICT_RESOLUTION = new DefaultComponentSelectionReason(false, true, false, false, "conflict resolution");
+    public static final ComponentSelectionReason SELECTED_BY_RULE = new DefaultComponentSelectionReason(false, false, true, false, "selected by rule");
+    public static final ComponentSelectionReason CONFLICT_RESOLUTION_BY_RULE = new DefaultComponentSelectionReason(false, true, true, false, "selected by rule and conflict resolution");
+
+    public static ComponentSelectionReason withConflictResolution(ComponentSelectionReason reason) {
+        if (reason.isConflictResolution()) {
+            return reason;
+        } else if (reason == SELECTED_BY_RULE) {
+            return CONFLICT_RESOLUTION_BY_RULE;
+        } else if (reason == REQUESTED) {
+            return CONFLICT_RESOLUTION;
+        } else if (reason == FORCED) {
+            return CONFLICT_RESOLUTION;
+        }
+        throw new IllegalArgumentException("Cannot create conflict resolution selection reason for input: " + reason);
+    }
+
+    private static class DefaultComponentSelectionReason implements ComponentSelectionReason {
+
+        private final boolean forced;
+        private final boolean conflictResolution;
+        private final boolean selectedByRule;
+        private final boolean expected;
+        private final String description;
+
+        private DefaultComponentSelectionReason(boolean forced, boolean conflictResolution, boolean selectedByRule, boolean expected, String description) {
+            this.forced = forced;
+            this.conflictResolution = conflictResolution;
+            this.selectedByRule = selectedByRule;
+            this.expected = expected;
+            assert description != null;
+            this.description = description;
+        }
+
+        public boolean isForced() {
+            return forced;
+        }
+
+        public boolean isConflictResolution() {
+            return conflictResolution;
+        }
+
+        public boolean isSelectedByRule() {
+            return selectedByRule;
+        }
+
+        public boolean isExpected() {
+            return expected;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String toString() {
+            return description;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.java
new file mode 100644
index 0000000..98f0c14
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactory.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 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.io.Closeable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.gradle.util.Clock.prettyTime;
+
+public class CachedStoreFactory<T> implements Closeable {
+
+    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).expireAfterAccess(10000, TimeUnit.MILLISECONDS).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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java
new file mode 100644
index 0000000..7b731fd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStore.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.io.RandomAccessFileInputStream;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
+
+import java.io.*;
+
+import static org.gradle.internal.UncheckedException.throwAsUncheckedException;
+
+class DefaultBinaryStore implements BinaryStore, Closeable {
+    private File file;
+    private KryoBackedEncoder encoder;
+    private int offset = -1;
+
+    public DefaultBinaryStore(File file) {
+        this.file = file;
+    }
+
+    public void write(WriteAction write) {
+        if (encoder == null) {
+            try {
+                encoder = new KryoBackedEncoder(new FileOutputStream(file));
+            } catch (FileNotFoundException e) {
+                throw throwAsUncheckedException(e);
+            }
+        }
+        if (offset == -1) {
+            offset = encoder.getWritePosition();
+            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(encoder);
+        } catch (Exception 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 (encoder != null) {
+                encoder.flush();
+            }
+            return new SimpleBinaryData(file, offset, diagnose());
+        } finally {
+            offset = -1;
+        }
+    }
+
+    public void close() {
+        try {
+            if (encoder != null) {
+                encoder.close();
+            }
+        } finally {
+            file.delete();
+            encoder = null;
+            file = null;
+        }
+    }
+
+    File getFile() {
+        return file;
+    }
+
+    long getSize() {
+        return file.length();
+    }
+
+    private static class SimpleBinaryData implements BinaryStore.BinaryData {
+        private final int offset;
+        private final File inputFile;
+        private final String sourceDescription;
+
+        private Decoder decoder;
+        private CompositeStoppable resources;
+
+        public SimpleBinaryData(File inputFile, int offset, String sourceDescription) {
+            this.inputFile = inputFile;
+            this.offset = offset;
+            this.sourceDescription = sourceDescription;
+        }
+
+        public <T> T read(BinaryStore.ReadAction<T> readAction) {
+            try {
+                if (decoder == null) {
+                    RandomAccessFile randomAccess = new RandomAccessFile(inputFile, "r");
+                    randomAccess.seek(offset);
+                    decoder = new KryoBackedDecoder(new RandomAccessFileInputStream(randomAccess));
+                    resources = new CompositeStoppable().add(randomAccess, decoder);
+                }
+                return readAction.read(decoder);
+            } catch (Exception e) {
+                throw new RuntimeException("Problems reading data from " + sourceDescription, e);
+            }
+        }
+
+        public void close() {
+            try {
+                if (resources != null) {
+                    resources.stop();
+                }
+            } catch (Exception e) {
+                throw new RuntimeException("Problems cleaning resources of " + sourceDescription, e);
+            } finally {
+                decoder = null;
+                resources = null;
+            }
+        }
+
+        public String toString() {
+            return sourceDescription;
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactory.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/StoreSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/StoreSet.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/StoreSet.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/StoreSet.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.java
new file mode 100644
index 0000000..5a01a5e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/metadata/ModuleVersionArtifactIdentifierSerializer.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.artifacts.metadata;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentIdentifierSerializer;
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.MapSerializer;
+import org.gradle.internal.serialize.Serializer;
+
+import java.util.Map;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.STRING_SERIALIZER;
+
+public class ModuleVersionArtifactIdentifierSerializer implements Serializer<ModuleComponentArtifactIdentifier> {
+    private final ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
+    private final MapSerializer<String, String> attributesSerializer = new MapSerializer<String, String>(STRING_SERIALIZER, STRING_SERIALIZER);
+
+    public void write(Encoder encoder, ModuleComponentArtifactIdentifier value) throws Exception {
+        DefaultModuleComponentArtifactIdentifier artifact = (DefaultModuleComponentArtifactIdentifier) value;
+        componentIdentifierSerializer.write(encoder, artifact.getComponentIdentifier());
+        IvyArtifactName ivyArtifactName = artifact.getName();
+        encoder.writeString(ivyArtifactName.getName());
+        encoder.writeString(ivyArtifactName.getType());
+        encoder.writeNullableString(ivyArtifactName.getExtension());
+        attributesSerializer.write(encoder, ivyArtifactName.getAttributes());
+    }
+
+    public ModuleComponentArtifactIdentifier read(Decoder decoder) throws Exception {
+        ModuleComponentIdentifier componentIdentifier = (ModuleComponentIdentifier) componentIdentifierSerializer.read(decoder);
+        String artifactName = decoder.readString();
+        String type = decoder.readString();
+        String extension = decoder.readNullableString();
+        Map<String, String> attributes = attributesSerializer.read(decoder);
+        return new DefaultModuleComponentArtifactIdentifier(componentIdentifier, artifactName, type, extension, attributes);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/CannotLocateLocalMavenRepositoryException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/CannotLocateLocalMavenRepositoryException.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/CannotLocateLocalMavenRepositoryException.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/CannotLocateLocalMavenRepositoryException.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
new file mode 100644
index 0000000..5952502
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocator.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.mvnsettings;
+
+import org.gradle.mvn3.org.apache.maven.settings.Settings;
+import org.gradle.mvn3.org.apache.maven.settings.building.SettingsBuildingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DefaultLocalMavenRepositoryLocator implements LocalMavenRepositoryLocator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLocalMavenRepositoryLocator.class);
+    private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{([^\\}]*)\\}");
+
+    private final MavenSettingsProvider settingsProvider;
+    private final SystemPropertyAccess system;
+    private String localRepoPathFromMavenSettings;
+
+    public DefaultLocalMavenRepositoryLocator(MavenSettingsProvider settingsProvider) {
+        this(settingsProvider, new CurrentSystemPropertyAccess());
+    }
+
+    protected DefaultLocalMavenRepositoryLocator(MavenSettingsProvider settingsProvider, SystemPropertyAccess system) {
+        this.settingsProvider = settingsProvider;
+        this.system = system;
+    }
+
+    public File getLocalMavenRepository() throws CannotLocateLocalMavenRepositoryException {
+        String localOverride = system.getProperty("maven.repo.local");
+        if (localOverride != null) {
+            return new File(localOverride);
+        }
+        try {
+            String repoPath = parseLocalRepoPathFromMavenSettings();
+            if (repoPath != null) {
+                return new File(resolvePlaceholders(repoPath.trim()));
+            } else {
+                File defaultLocation = new File(system.getProperty("user.home"), "/.m2/repository").getAbsoluteFile();
+                LOGGER.debug(String.format("No local repository in Settings file defined. Using default path: %s", defaultLocation));
+                return defaultLocation;
+            }
+        } catch (SettingsBuildingException e) {
+            throw new CannotLocateLocalMavenRepositoryException("Unable to parse local Maven settings.", e);
+        }
+    }
+
+    // We only cache the result of parsing the Maven settings files, but allow this value to be updated in-flight
+    // via system properties. This allows the local maven repo to be overridden when publishing to maven
+    // (see http://forums.gradle.org/gradle/topics/override_location_of_the_local_maven_repo).
+    private synchronized String parseLocalRepoPathFromMavenSettings() throws SettingsBuildingException {
+        if (localRepoPathFromMavenSettings == null) {
+            Settings settings = settingsProvider.buildSettings();
+            localRepoPathFromMavenSettings = settings.getLocalRepository();
+        }
+        return localRepoPathFromMavenSettings;
+    }
+
+    private String resolvePlaceholders(String value) {
+        StringBuffer result = new StringBuffer();
+        Matcher matcher = PLACEHOLDER_PATTERN.matcher(value);
+        while (matcher.find()) {
+            String placeholder = matcher.group(1);
+            String replacement = placeholder.startsWith("env.") ? system.getEnv(placeholder.substring(4)) : system.getProperty(placeholder);
+            if (replacement == null) {
+                throw new CannotLocateLocalMavenRepositoryException(String.format("Cannot resolve placeholder '%s' in value '%s'", placeholder, value));
+            }
+            matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
+        }
+        matcher.appendTail(result);
+        return result.toString();
+    }
+
+    public static interface SystemPropertyAccess {
+        String getProperty(String name);
+        String getEnv(String name);
+    }
+
+    public static class CurrentSystemPropertyAccess implements SystemPropertyAccess {
+        @Override
+        public String getProperty(String name) {
+            return System.getProperty(name);
+        }
+
+        @Override
+        public String getEnv(String name) {
+            return System.getenv(name);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java
new file mode 100644
index 0000000..e07e651
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenFileLocations.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.mvnsettings;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.SystemProperties;
+
+import java.io.File;
+
+public class DefaultMavenFileLocations implements MavenFileLocations {
+    public File getUserMavenDir() {
+        return new File(SystemProperties.getInstance().getUserHome(), ".m2");
+    }
+
+    @Nullable
+    public File getGlobalMavenDir() {
+        String m2Home = System.getenv("M2_HOME");
+        if (m2Home == null) {
+            return null;
+        }
+        return new File(m2Home);
+    }
+
+    public File getUserSettingsFile() {
+        return new File(getUserMavenDir(), "settings.xml");
+    }
+
+    @Nullable
+    public File getGlobalSettingsFile() {
+        File dir = getGlobalMavenDir();
+        if (dir == null) {
+            return null;
+        }
+        return new File(dir, "conf/settings.xml");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.java
new file mode 100644
index 0000000..cc39a17
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/DefaultMavenSettingsProvider.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.mvnsettings;
+
+import org.gradle.mvn3.org.apache.maven.settings.Settings;
+import org.gradle.mvn3.org.apache.maven.settings.building.*;
+
+public class DefaultMavenSettingsProvider implements MavenSettingsProvider {
+
+    private final MavenFileLocations mavenFileLocations;
+
+    public DefaultMavenSettingsProvider(MavenFileLocations mavenFileLocations) {
+        this.mavenFileLocations = mavenFileLocations;
+    }
+
+    public Settings buildSettings() throws SettingsBuildingException {
+        DefaultSettingsBuilderFactory factory = new DefaultSettingsBuilderFactory();
+        DefaultSettingsBuilder defaultSettingsBuilder = factory.newInstance();
+        DefaultSettingsBuildingRequest settingsBuildingRequest = new DefaultSettingsBuildingRequest();
+        settingsBuildingRequest.setSystemProperties(System.getProperties());
+        settingsBuildingRequest.setUserSettingsFile(mavenFileLocations.getUserSettingsFile());
+        settingsBuildingRequest.setGlobalSettingsFile(mavenFileLocations.getGlobalSettingsFile());
+        SettingsBuildingResult settingsBuildingResult = defaultSettingsBuilder.build(settingsBuildingRequest);
+        return settingsBuildingResult.getEffectiveSettings();
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/LocalMavenRepositoryLocator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/LocalMavenRepositoryLocator.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/LocalMavenRepositoryLocator.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/LocalMavenRepositoryLocator.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/MavenFileLocations.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/MavenFileLocations.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/MavenFileLocations.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/MavenFileLocations.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/MavenSettingsProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/MavenSettingsProvider.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/mvnsettings/MavenSettingsProvider.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/mvnsettings/MavenSettingsProvider.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java
new file mode 100644
index 0000000..f3f95ce
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.query;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.result.ArtifactResolutionResult;
+import org.gradle.api.artifacts.result.ComponentArtifactsResult;
+import org.gradle.api.artifacts.result.ComponentResult;
+import org.gradle.api.component.Artifact;
+import org.gradle.api.component.Component;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ivyservice.*;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.DefaultDependencyMetaData;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.internal.artifacts.result.*;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.internal.Factory;
+import org.gradle.internal.Transformers;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.result.*;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultArtifactResolutionQuery implements ArtifactResolutionQuery {
+    private final ConfigurationContainerInternal configurationContainer;
+    private final RepositoryHandler repositoryHandler;
+    private final ResolveIvyFactory ivyFactory;
+    private final GlobalDependencyResolutionRules metadataHandler;
+    private final CacheLockingManager lockingManager;
+    private final ComponentTypeRegistry componentTypeRegistry;
+
+    private Set<ComponentIdentifier> componentIds = Sets.newLinkedHashSet();
+    private Class<? extends Component> componentType;
+    private Set<Class<? extends Artifact>> artifactTypes = Sets.newLinkedHashSet();
+
+    public DefaultArtifactResolutionQuery(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
+                                          ResolveIvyFactory ivyFactory, GlobalDependencyResolutionRules metadataHandler, CacheLockingManager lockingManager,
+                                          ComponentTypeRegistry componentTypeRegistry) {
+        this.configurationContainer = configurationContainer;
+        this.repositoryHandler = repositoryHandler;
+        this.ivyFactory = ivyFactory;
+        this.metadataHandler = metadataHandler;
+        this.lockingManager = lockingManager;
+        this.componentTypeRegistry = componentTypeRegistry;
+    }
+
+    public ArtifactResolutionQuery forComponents(Iterable<? extends ComponentIdentifier> componentIds) {
+        CollectionUtils.addAll(this.componentIds, componentIds);
+        return this;
+    }
+
+    public ArtifactResolutionQuery forComponents(ComponentIdentifier... componentIds) {
+        CollectionUtils.addAll(this.componentIds, componentIds);
+        return this;
+    }
+
+    public ArtifactResolutionQuery withArtifacts(Class<? extends Component> componentType, Class<? extends Artifact>... artifactTypes) {
+        if (this.componentType != null) {
+            throw new IllegalStateException("Cannot specify component type multiple times.");
+        }
+        this.componentType = componentType;
+        this.artifactTypes.addAll(Arrays.asList(artifactTypes));
+        return this;
+    }
+
+    // TODO:DAZ This is ugly and needs a major cleanup and unit tests
+    public ArtifactResolutionResult execute() {
+        if (componentType == null) {
+            throw new IllegalStateException("Must specify component type and artifacts to query.");
+        }
+        List<ResolutionAwareRepository> repositories = CollectionUtils.collect(repositoryHandler, Transformers.cast(ResolutionAwareRepository.class));
+        ConfigurationInternal configuration = configurationContainer.detachedConfiguration();
+        final RepositoryChain repositoryChain = ivyFactory.create(configuration, repositories, metadataHandler.getComponentMetadataProcessor());
+        final ArtifactResolver artifactResolver = new ErrorHandlingArtifactResolver(repositoryChain.getArtifactResolver());
+
+        return lockingManager.useCache("resolve artifacts", new Factory<ArtifactResolutionResult>() {
+            public ArtifactResolutionResult create() {
+                Set<ComponentResult> componentResults = Sets.newHashSet();
+
+                for (ComponentIdentifier componentId : componentIds) {
+                    try {
+                        ModuleComponentIdentifier moduleComponentId = validateComponentIdentifier(componentId);
+                        componentResults.add(buildComponentResult(moduleComponentId, repositoryChain, artifactResolver));
+                    } catch (Throwable t) {
+                        componentResults.add(new DefaultUnresolvedComponentResult(componentId, t));
+                    }
+                }
+
+                return new DefaultArtifactResolutionResult(componentResults);
+            }
+
+            private ModuleComponentIdentifier validateComponentIdentifier(ComponentIdentifier componentId) {
+                if (componentId instanceof ModuleComponentIdentifier) {
+                    return (ModuleComponentIdentifier) componentId;
+                }
+                if(componentId instanceof ProjectComponentIdentifier) {
+                    throw new IllegalArgumentException(String.format("Cannot query artifacts for a project component (%s).", componentId.getDisplayName()));
+                }
+
+                throw new IllegalArgumentException(String.format("Cannot resolve the artifacts for component %s with unsupported type %s.", componentId.getDisplayName(), componentId.getClass().getName()));
+            }
+        });
+    }
+
+    private ComponentArtifactsResult buildComponentResult(ModuleComponentIdentifier moduleComponentId, RepositoryChain repositoryChain, ArtifactResolver artifactResolver) {
+        BuildableComponentResolveResult moduleResolveResult = new DefaultBuildableComponentResolveResult();
+        repositoryChain.getDependencyResolver().resolve(new DefaultDependencyMetaData(moduleComponentId), moduleResolveResult);
+        ComponentResolveMetaData component = moduleResolveResult.getMetaData();
+        DefaultComponentArtifactsResult componentResult = new DefaultComponentArtifactsResult(component.getComponentId());
+        for (Class<? extends Artifact> artifactType : artifactTypes) {
+            addArtifacts(componentResult, artifactType, component, artifactResolver);
+        }
+        return componentResult;
+    }
+
+    private <T extends Artifact> void addArtifacts(DefaultComponentArtifactsResult artifacts, Class<T> type, ComponentResolveMetaData component, ArtifactResolver artifactResolver) {
+        BuildableArtifactSetResolveResult artifactSetResolveResult = new DefaultBuildableArtifactSetResolveResult();
+        artifactResolver.resolveModuleArtifacts(component, convertType(type), artifactSetResolveResult);
+
+        for (ComponentArtifactMetaData artifactMetaData : artifactSetResolveResult.getArtifacts()) {
+            BuildableArtifactResolveResult resolveResult = new DefaultBuildableArtifactResolveResult();
+            artifactResolver.resolveArtifact(artifactMetaData, component.getSource(), resolveResult);
+            if (resolveResult.getFailure() != null) {
+                artifacts.addArtifact(new DefaultUnresolvedArtifactResult(type, resolveResult.getFailure()));
+            } else {
+                artifacts.addArtifact(new DefaultResolvedArtifactResult(type, resolveResult.getFile()));
+            }
+        }
+    }
+
+    private <T extends Artifact> ArtifactType convertType(Class<T> requestedType) {
+        return componentTypeRegistry.getComponentRegistration(componentType).getArtifactType(requestedType);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryFactory.java
new file mode 100644
index 0000000..5a542d5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.query;
+
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+
+public class DefaultArtifactResolutionQueryFactory implements ArtifactResolutionQueryFactory {
+    private final ConfigurationContainerInternal configurationContainer;
+    private final RepositoryHandler repositoryHandler;
+    private final ResolveIvyFactory ivyFactory;
+    private final GlobalDependencyResolutionRules metadataHandler;
+    private final CacheLockingManager cacheLockingManager;
+    private final ComponentTypeRegistry componentTypeRegistry;
+
+    public DefaultArtifactResolutionQueryFactory(ConfigurationContainerInternal configurationContainer, RepositoryHandler repositoryHandler,
+                                                 ResolveIvyFactory ivyFactory, GlobalDependencyResolutionRules metadataHandler,
+                                                 CacheLockingManager cacheLockingManager, ComponentTypeRegistry componentTypeRegistry) {
+        this.configurationContainer = configurationContainer;
+        this.repositoryHandler = repositoryHandler;
+        this.ivyFactory = ivyFactory;
+        this.metadataHandler = metadataHandler;
+        this.cacheLockingManager = cacheLockingManager;
+        this.componentTypeRegistry = componentTypeRegistry;
+    }
+
+    public ArtifactResolutionQuery createArtifactResolutionQuery() {
+        return new DefaultArtifactResolutionQuery(configurationContainer, repositoryHandler, ivyFactory, metadataHandler, cacheLockingManager, componentTypeRegistry);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.java
new file mode 100644
index 0000000..bd9fb4a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepository.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.internal.artifacts.repositories;
+
+import org.gradle.api.NamedDomainObjectCollection;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+
+public abstract class AbstractArtifactRepository implements ArtifactRepositoryInternal {
+
+    private String name;
+    private boolean isPartOfContainer;
+
+    public void onAddToContainer(NamedDomainObjectCollection<ArtifactRepository> container) {
+        isPartOfContainer = true;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        if (isPartOfContainer) {
+            throw new IllegalStateException("The name of an ArtifactRepository cannot be changed after it has been added to a repository container. You should set the name when creating the repository.");
+        }
+        this.name = name;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
new file mode 100644
index 0000000..4a8a866
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories;
+
+import org.gradle.api.Action;
+import org.gradle.api.artifacts.repositories.PasswordCredentials;
+import org.gradle.api.credentials.AwsCredentials;
+import org.gradle.api.credentials.Credentials;
+import org.gradle.internal.Cast;
+import org.gradle.internal.artifacts.repositories.AuthenticationSupportedInternal;
+import org.gradle.internal.credentials.DefaultAwsCredentials;
+import org.gradle.internal.reflect.Instantiator;
+
+public abstract class AbstractAuthenticationSupportedRepository extends AbstractArtifactRepository implements AuthenticationSupportedInternal {
+
+    private Credentials credentials;
+    private final Instantiator instantiator;
+
+    AbstractAuthenticationSupportedRepository(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    @Override
+    public PasswordCredentials getCredentials() {
+        if (credentials == null) {
+            return setCredentials(PasswordCredentials.class);
+        } else if (credentials instanceof PasswordCredentials) {
+            return Cast.uncheckedCast(credentials);
+        } else {
+            throw new IllegalStateException("Can not use getCredentials() method when not using PasswordCredentals; please use getCredentials(Class)");
+        }
+    }
+
+    @Override
+    public <T extends Credentials> T getCredentials(Class<T> credentialsType) {
+        if (credentials == null) {
+            return setCredentials(credentialsType);
+        } else if (credentialsType.isInstance(credentials)) {
+            return Cast.uncheckedCast(credentials);
+        } else {
+            throw new IllegalArgumentException(String.format("Given credentials type '%s' does not match actual type '%s'", credentialsType.getName(), getPublicType(credentials.getClass()).getName()));
+        }
+    }
+
+    public void credentials(Action<? super PasswordCredentials> action) {
+        if (credentials != null && !(credentials instanceof PasswordCredentials)) {
+            throw new IllegalStateException("Can not use credentials(Action) method when not using PasswordCredentals; please use credentials(Class, Action)");
+        }
+        credentials(PasswordCredentials.class, action);
+    }
+
+    public <T extends Credentials> void credentials(Class<T> credentialsType, Action<? super T> action) throws IllegalStateException {
+        action.execute(getCredentials(credentialsType));
+    }
+
+    private <T extends Credentials> T setCredentials(Class<T> clazz) {
+        T t = newCredentials(clazz);
+        credentials = t;
+        return t;
+    }
+
+    private <T extends Credentials> T newCredentials(Class<T> clazz) {
+        return instantiator.newInstance(getImplType(clazz));
+    }
+
+    public Credentials getConfiguredCredentials() {
+        return credentials;
+    }
+
+    // Mappings between public and impl types
+    // If the list of mappings grows we should move it to a data structure
+
+    private static <T extends Credentials> Class<? extends T> getImplType(Class<T> publicType) {
+        if (publicType == PasswordCredentials.class) {
+            return Cast.uncheckedCast(DefaultPasswordCredentials.class);
+        } else if (publicType == AwsCredentials.class) {
+            return Cast.uncheckedCast(DefaultAwsCredentials.class);
+        } else {
+            throw new IllegalArgumentException(String.format("Unknown credentials type: '%s' (supported types: %s and %s).", publicType.getName(), PasswordCredentials.class.getName(), AwsCredentials.class.getName()));
+        }
+    }
+
+    private static <T extends Credentials> Class<? super T> getPublicType(Class<T> implType) {
+        if (PasswordCredentials.class.isAssignableFrom(implType)) {
+            return Cast.uncheckedCast(PasswordCredentials.class);
+        } else if (AwsCredentials.class.isAssignableFrom(implType)) {
+            return Cast.uncheckedCast(AwsCredentials.class);
+        } else {
+            throw new IllegalArgumentException(String.format("Unknown credentials implementation type: '%s' (supported types: %s and %s).", implType.getName(), DefaultPasswordCredentials.class.getName(), DefaultAwsCredentials.class.getName()));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
new file mode 100644
index 0000000..cfacca1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactory.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
+import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.BaseRepositoryFactory;
+import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.io.File;
+
+public class DefaultBaseRepositoryFactory implements BaseRepositoryFactory {
+    private final LocalMavenRepositoryLocator localMavenRepositoryLocator;
+    private final FileResolver fileResolver;
+    private final Instantiator instantiator;
+    private final RepositoryTransportFactory transportFactory;
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final ResolverStrategy resolverStrategy;
+    private final FileStore<ModuleComponentArtifactMetaData> artifactFileStore;
+    private final MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser;
+
+    public DefaultBaseRepositoryFactory(LocalMavenRepositoryLocator localMavenRepositoryLocator,
+                                        FileResolver fileResolver,
+                                        Instantiator instantiator,
+                                        RepositoryTransportFactory transportFactory,
+                                        LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                        ResolverStrategy resolverStrategy,
+                                        FileStore<ModuleComponentArtifactMetaData> artifactFileStore, MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser) {
+        this.localMavenRepositoryLocator = localMavenRepositoryLocator;
+        this.fileResolver = fileResolver;
+        this.instantiator = instantiator;
+        this.transportFactory = transportFactory;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.resolverStrategy = resolverStrategy;
+        this.artifactFileStore = artifactFileStore;
+        this.pomParser = pomParser;
+    }
+
+    public FlatDirectoryArtifactRepository createFlatDirRepository() {
+        return instantiator.newInstance(DefaultFlatDirArtifactRepository.class, fileResolver, transportFactory,
+                locallyAvailableResourceFinder, resolverStrategy, artifactFileStore);
+    }
+
+    public MavenArtifactRepository createMavenLocalRepository() {
+        MavenArtifactRepository mavenRepository = instantiator.newInstance(DefaultMavenLocalArtifactRepository.class, fileResolver, transportFactory,
+                locallyAvailableResourceFinder, instantiator, artifactFileStore, pomParser);
+        final File localMavenRepository = localMavenRepositoryLocator.getLocalMavenRepository();
+        mavenRepository.setUrl(localMavenRepository);
+        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);
+        return mavenRepository;
+    }
+
+    public IvyArtifactRepository createIvyRepository() {
+        return instantiator.newInstance(DefaultIvyArtifactRepository.class, fileResolver, transportFactory,
+                locallyAvailableResourceFinder, instantiator, resolverStrategy, artifactFileStore);
+    }
+
+    public MavenArtifactRepository createMavenRepository() {
+        return instantiator.newInstance(DefaultMavenArtifactRepository.class, fileResolver, transportFactory,
+                locallyAvailableResourceFinder, instantiator, artifactFileStore, pomParser);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
new file mode 100644
index 0000000..a659689
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultFlatDirArtifactRepository extends AbstractArtifactRepository implements FlatDirectoryArtifactRepository, ResolutionAwareRepository, PublicationAwareRepository {
+    private final FileResolver fileResolver;
+    private List<Object> dirs = new ArrayList<Object>();
+    private final RepositoryTransportFactory transportFactory;
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final ResolverStrategy resolverStrategy;
+    private final FileStore<ModuleComponentArtifactMetaData> artifactFileStore;
+
+    public DefaultFlatDirArtifactRepository(FileResolver fileResolver,
+                                            RepositoryTransportFactory transportFactory,
+                                            LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                            ResolverStrategy resolverStrategy,
+                                            FileStore<ModuleComponentArtifactMetaData> artifactFileStore) {
+        this.fileResolver = fileResolver;
+        this.transportFactory = transportFactory;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.resolverStrategy = resolverStrategy;
+        this.artifactFileStore = artifactFileStore;
+    }
+
+    public Set<File> getDirs() {
+        return fileResolver.resolveFiles(dirs).getFiles();
+    }
+
+    public void setDirs(Iterable<?> dirs) {
+        this.dirs = Lists.newArrayList(dirs);
+    }
+
+    public void dir(Object dir) {
+        dirs(dir);
+    }
+
+    public void dirs(Object... dirs) {
+        this.dirs.addAll(Arrays.asList(dirs));
+    }
+
+    public ModuleVersionPublisher createPublisher() {
+        return createRealResolver();
+    }
+
+    public ConfiguredModuleComponentRepository createResolver() {
+        return createRealResolver();
+    }
+
+    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.");
+        }
+
+        IvyResolver resolver = new IvyResolver(getName(), transportFactory.createTransport("file", getName(), null), locallyAvailableResourceFinder, false, resolverStrategy, artifactFileStore);
+        for (File root : dirs) {
+            resolver.addArtifactLocation(root.toURI(), "/[artifact]-[revision](-[classifier]).[ext]");
+            resolver.addArtifactLocation(root.toURI(), "/[artifact](-[classifier]).[ext]");
+        }
+        return resolver;
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
new file mode 100644
index 0000000..5333273
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+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.RepositoryLayout;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+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.file.FileResolver;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.net.URI;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupportedRepository implements IvyArtifactRepository, ResolutionAwareRepository, PublicationAwareRepository {
+    private Object baseUrl;
+    private AbstractRepositoryLayout layout;
+    private final AdditionalPatternsRepositoryLayout additionalPatternsLayout;
+    private final FileResolver fileResolver;
+    private final RepositoryTransportFactory transportFactory;
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final MetaDataProvider metaDataProvider;
+    private final Instantiator instantiator;
+    private final ResolverStrategy resolverStrategy;
+    private final FileStore<ModuleComponentArtifactMetaData> artifactFileStore;
+
+    public DefaultIvyArtifactRepository(FileResolver fileResolver, RepositoryTransportFactory transportFactory,
+                                        LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder, Instantiator instantiator,
+                                        ResolverStrategy resolverStrategy, FileStore<ModuleComponentArtifactMetaData> artifactFileStore) {
+        super(instantiator);
+        this.fileResolver = fileResolver;
+        this.transportFactory = transportFactory;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.resolverStrategy = resolverStrategy;
+        this.artifactFileStore = artifactFileStore;
+        this.additionalPatternsLayout = new AdditionalPatternsRepositoryLayout(fileResolver);
+        this.layout = new GradleRepositoryLayout();
+        this.metaDataProvider = new MetaDataProvider();
+        this.instantiator = instantiator;
+    }
+
+    public ModuleVersionPublisher createPublisher() {
+        return createRealResolver();
+    }
+
+    public ConfiguredModuleComponentRepository createResolver() {
+        return createRealResolver();
+    }
+
+    protected IvyResolver createRealResolver() {
+        URI uri = getUrl();
+
+        Set<String> schemes = new LinkedHashSet<String>();
+        layout.addSchemes(uri, schemes);
+        additionalPatternsLayout.addSchemes(uri, schemes);
+
+        IvyResolver resolver = createResolver(schemes);
+
+        layout.apply(uri, resolver);
+        additionalPatternsLayout.apply(uri, resolver);
+
+        return resolver;
+    }
+
+    private IvyResolver createResolver(Set<String> schemes) {
+        if (schemes.isEmpty()) {
+            throw new InvalidUserDataException("You must specify a base url or at least one artifact pattern for an Ivy repository.");
+        }
+        return createResolver(transportFactory.createTransport(schemes, getName(), getConfiguredCredentials()));
+    }
+
+    private IvyResolver createResolver(RepositoryTransport transport) {
+        return new IvyResolver(
+                getName(), transport,
+                locallyAvailableResourceFinder,
+                metaDataProvider.dynamicResolve, resolverStrategy, artifactFileStore);
+    }
+
+    public URI getUrl() {
+        return baseUrl == null ? null : fileResolver.resolveUri(baseUrl);
+    }
+
+    public void setUrl(Object url) {
+        baseUrl = url;
+    }
+
+    public void artifactPattern(String pattern) {
+        additionalPatternsLayout.artifactPatterns.add(pattern);
+    }
+
+    public void ivyPattern(String pattern) {
+        additionalPatternsLayout.ivyPatterns.add(pattern);
+    }
+
+    public void layout(String layoutName) {
+        if ("ivy".equals(layoutName)) {
+            layout = instantiator.newInstance(IvyRepositoryLayout.class);
+        } else if ("maven".equals(layoutName)) {
+            layout = instantiator.newInstance(MavenRepositoryLayout.class);
+        } else if ("pattern".equals(layoutName)) {
+            layout = instantiator.newInstance(DefaultIvyPatternRepositoryLayout.class);
+        } else {
+            layout = instantiator.newInstance(GradleRepositoryLayout.class);
+        }
+    }
+
+    public void layout(String layoutName, Closure config) {
+        layout(layoutName, new ClosureBackedAction<RepositoryLayout>(config));
+    }
+
+    public void layout(String layoutName, Action<? extends RepositoryLayout> config) {
+        layout(layoutName);
+        ((Action) config).execute(layout);
+    }
+
+    public IvyArtifactRepositoryMetaDataProvider getResolve() {
+        return metaDataProvider;
+    }
+
+    /**
+     * Layout for applying additional patterns added via {@link #artifactPatterns} and {@link #ivyPatterns}.
+     */
+    private static class AdditionalPatternsRepositoryLayout extends AbstractRepositoryLayout {
+        private final FileResolver fileResolver;
+        private final Set<String> artifactPatterns = new LinkedHashSet<String>();
+        private final Set<String> ivyPatterns = new LinkedHashSet<String>();
+
+        public AdditionalPatternsRepositoryLayout(FileResolver fileResolver) {
+            this.fileResolver = fileResolver;
+        }
+
+        public void apply(URI baseUri, PatternBasedResolver resolver) {
+            for (String artifactPattern : artifactPatterns) {
+                ResolvedPattern resolvedPattern = new ResolvedPattern(artifactPattern, fileResolver);
+                resolver.addArtifactLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
+            }
+
+            Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
+            for (String ivyPattern : usedIvyPatterns) {
+                ResolvedPattern resolvedPattern = new ResolvedPattern(ivyPattern, fileResolver);
+                resolver.addDescriptorLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
+            }
+        }
+
+        @Override
+        public void addSchemes(URI baseUri, Set<String> schemes) {
+            for (String pattern : artifactPatterns) {
+                schemes.add(new ResolvedPattern(pattern, fileResolver).scheme);
+            }
+            for (String pattern : ivyPatterns) {
+                schemes.add(new ResolvedPattern(pattern, fileResolver).scheme);
+            }
+        }
+    }
+
+    private static class MetaDataProvider implements IvyArtifactRepositoryMetaDataProvider {
+        boolean dynamicResolve;
+
+        public boolean isDynamicMode() {
+            return dynamicResolve;
+        }
+
+        public void setDynamicMode(boolean mode) {
+            this.dynamicResolve = mode;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
new file mode 100644
index 0000000..798a74c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+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.file.FileResolver;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultMavenArtifactRepository extends AbstractAuthenticationSupportedRepository implements MavenArtifactRepository, ResolutionAwareRepository, PublicationAwareRepository {
+    private final FileResolver fileResolver;
+    private final RepositoryTransportFactory transportFactory;
+    private Object url;
+    private List<Object> additionalUrls = new ArrayList<Object>();
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final FileStore<ModuleComponentArtifactMetaData> artifactFileStore;
+    private final MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser;
+
+    public DefaultMavenArtifactRepository(FileResolver fileResolver, RepositoryTransportFactory transportFactory,
+                                          LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                          Instantiator instantiator,
+                                          FileStore<ModuleComponentArtifactMetaData> artifactFileStore,
+                                          MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser) {
+        super(instantiator);
+        this.fileResolver = fileResolver;
+        this.transportFactory = transportFactory;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.artifactFileStore = artifactFileStore;
+        this.pomParser = pomParser;
+    }
+
+    public URI getUrl() {
+        return url == null ? null : fileResolver.resolveUri(url);
+    }
+
+    public void setUrl(Object url) {
+        this.url = url;
+    }
+
+    public Set<URI> getArtifactUrls() {
+        Set<URI> result = new LinkedHashSet<URI>();
+        for (Object additionalUrl : additionalUrls) {
+            result.add(fileResolver.resolveUri(additionalUrl));
+        }
+        return result;
+    }
+
+    public void artifactUrls(Object... urls) {
+        additionalUrls.addAll(Lists.newArrayList(urls));
+    }
+
+    public void setArtifactUrls(Iterable<?> urls) {
+        additionalUrls = Lists.newArrayList(urls);
+    }
+
+    public ModuleVersionPublisher createPublisher() {
+        return createRealResolver();
+    }
+
+    public ConfiguredModuleComponentRepository createResolver() {
+        return createRealResolver();
+    }
+
+    protected MavenResolver createRealResolver() {
+        URI rootUri = getUrl();
+        if (rootUri == null) {
+            throw new InvalidUserDataException("You must specify a URL for a Maven repository.");
+        }
+
+        MavenResolver resolver = createResolver(rootUri);
+
+        for (URI repoUrl : getArtifactUrls()) {
+            resolver.addArtifactLocation(repoUrl);
+        }
+        return resolver;
+    }
+
+    private MavenResolver createResolver(URI rootUri) {
+        RepositoryTransport transport = getTransport(rootUri.getScheme());
+        return new MavenResolver(getName(), rootUri, transport, locallyAvailableResourceFinder, artifactFileStore, pomParser);
+    }
+
+    public MetaDataParser<DefaultMavenModuleResolveMetaData> getPomParser() {
+        return pomParser;
+    }
+
+    protected FileStore<ModuleComponentArtifactMetaData> getArtifactFileStore() {
+        return artifactFileStore;
+    }
+
+    protected RepositoryTransport getTransport(String scheme) {
+        return transportFactory.createTransport(scheme, getName(), getConfiguredCredentials());
+    }
+
+    protected LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> getLocallyAvailableResourceFinder() {
+        return locallyAvailableResourceFinder;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
new file mode 100644
index 0000000..c2fac9c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.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.repositories;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.repositories.resolver.MavenLocalResolver;
+import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.net.URI;
+
+public class DefaultMavenLocalArtifactRepository extends DefaultMavenArtifactRepository implements MavenArtifactRepository {
+    public DefaultMavenLocalArtifactRepository(FileResolver fileResolver, RepositoryTransportFactory transportFactory,
+                                        LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder, Instantiator instantiator,
+                                        FileStore<ModuleComponentArtifactMetaData> artifactFileStore, MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser) {
+        super(fileResolver, transportFactory, locallyAvailableResourceFinder, instantiator, artifactFileStore, pomParser);
+    }
+
+    protected MavenResolver createRealResolver() {
+        URI rootUri = getUrl();
+        if (rootUri == null) {
+            throw new InvalidUserDataException("You must specify a URL for a Maven repository.");
+        }
+
+        MavenResolver resolver = new MavenLocalResolver(getName(), rootUri, getTransport(rootUri.getScheme()), getLocallyAvailableResourceFinder(), getArtifactFileStore(), getPomParser());
+        for (URI repoUrl : getArtifactUrls()) {
+            resolver.addArtifactLocation(repoUrl);
+        }
+        return resolver;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultPasswordCredentials.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultPasswordCredentials.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultPasswordCredentials.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultPasswordCredentials.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/PublicationAwareRepository.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
new file mode 100644
index 0000000..7a5ae1f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/ResolutionAwareRepository.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+
+public interface ResolutionAwareRepository {
+    /**
+     * Creates a resolver for this repository.
+     */
+    ConfiguredModuleComponentRepository createResolver();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java
new file mode 100644
index 0000000..4e80fb2
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.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.artifacts.repositories.layout;
+
+import org.gradle.api.artifacts.repositories.RepositoryLayout;
+import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+
+import java.net.URI;
+import java.util.Set;
+
+/**
+ * Represents the directory structure for a repository.
+ */
+public abstract class AbstractRepositoryLayout implements RepositoryLayout {
+    /**
+     * Given the base URI, apply the patterns and other configuration for this layout to the supplied resolver.
+     *
+     * @param baseUri The base URI for the repository.
+     * @param resolver The ivy resolver that will be used to resolve this layout.
+     */
+    public abstract void apply(URI baseUri, PatternBasedResolver resolver);
+
+    /**
+     * Add any schemes registered as patterns in this layout, given the supplied base URI.
+     * These are used to determine which repository implementation can be used (local file, http, etc).
+     *
+     * @param baseUri The baseUri of the repository.
+     * @param schemes The set of schemes to add to.
+     */
+    public void addSchemes(URI baseUri, Set<String> schemes) {
+        if (baseUri != null) {
+            schemes.add(baseUri.getScheme());
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java
new file mode 100644
index 0000000..bc9d026
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.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.artifacts.repositories.layout;
+
+import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout;
+import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+
+import java.net.URI;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * A repository layout that uses user-supplied patterns. Each pattern will be appended to the base URI for the repository.
+ * At least one artifact pattern must be specified. If no Ivy patterns are specified, then the artifact patterns will be used.
+ * Optionally supports a Maven style layout for the 'organisation' part, replacing any dots with forward slashes.
+ */
+public class DefaultIvyPatternRepositoryLayout extends AbstractRepositoryLayout implements IvyPatternRepositoryLayout {
+    private final Set<String> artifactPatterns = new LinkedHashSet<String>();
+    private final Set<String> ivyPatterns = new LinkedHashSet<String>();
+    private boolean m2compatible;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void artifact(String pattern) {
+        artifactPatterns.add(pattern);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void ivy(String pattern) {
+        ivyPatterns.add(pattern);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getM2Compatible() {
+        return m2compatible;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setM2compatible(boolean m2compatible) {
+        this.m2compatible = m2compatible;
+    }
+
+    @Override
+    public void apply(URI baseUri, PatternBasedResolver resolver) {
+        if (baseUri == null) {
+            return;
+        }
+
+        resolver.setM2compatible(m2compatible);
+
+        for (String artifactPattern : artifactPatterns) {
+            resolver.addArtifactLocation(baseUri, artifactPattern);
+        }
+
+        Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
+        for (String ivyPattern : usedIvyPatterns) {
+            resolver.addDescriptorLocation(baseUri, ivyPattern);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
new file mode 100644
index 0000000..0230d87
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.layout;
+
+import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
+import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+
+import java.net.URI;
+
+/**
+ * A Repository Layout that applies the following patterns:
+ * <ul>
+ *     <li>Artifacts: $baseUri/{@value IvyArtifactRepository#GRADLE_ARTIFACT_PATTERN}</li>
+ *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#GRADLE_IVY_PATTERN}</li>
+ * </ul>
+ *
+ * Note the pattern is the same for both artifacts and ivy files.
+ */
+public class GradleRepositoryLayout extends AbstractRepositoryLayout {
+
+    public void apply(URI baseUri, PatternBasedResolver resolver) {
+        if (baseUri == null) {
+            return;
+        }
+
+        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
+        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.GRADLE_IVY_PATTERN);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java
new file mode 100644
index 0000000..8f31c11
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.layout;
+
+import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
+import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+
+import java.net.URI;
+
+/**
+ * A Repository Layout that applies the following patterns:
+ * <ul>
+ *     <li>Artifacts: $baseUri/{@value IvyArtifactRepository#IVY_ARTIFACT_PATTERN}</li>
+ *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#IVY_ARTIFACT_PATTERN}</li>
+ * </ul>
+ */
+public class IvyRepositoryLayout extends AbstractRepositoryLayout {
+
+    public void apply(URI baseUri, PatternBasedResolver resolver) {
+        if (baseUri == null) {
+            return;
+        }
+
+        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
new file mode 100644
index 0000000..2d6745f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.layout;
+
+import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
+import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+
+import java.net.URI;
+
+/**
+ * A Repository Layout that applies the following patterns:
+ * <ul>
+ *     <li>Artifacts: $baseUri/{@value IvyArtifactRepository#MAVEN_ARTIFACT_PATTERN}</li>
+ *     <li>Ivy: $baseUri/{@value IvyArtifactRepository#MAVEN_IVY_PATTERN}</li>
+ * </ul>
+ *
+ * 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 AbstractRepositoryLayout {
+
+    public void apply(URI baseUri, PatternBasedResolver resolver) {
+        if (baseUri == null) {
+            return;
+        }
+
+        resolver.setM2compatible(true); // Replace '.' with '/' in organisation
+
+        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
+        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.MAVEN_IVY_PATTERN);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/ResolvedPattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/ResolvedPattern.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/layout/ResolvedPattern.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/ResolvedPattern.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/AbstractResourcePattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/AbstractResourcePattern.java
new file mode 100644
index 0000000..fb6bbb6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/AbstractResourcePattern.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.repositories.resolver;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResourceName;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+abstract class AbstractResourcePattern implements ResourcePattern {
+    private final ExternalResourceName pattern;
+
+    public AbstractResourcePattern(String pattern) {
+        this.pattern = new ExternalResourceName(pattern);
+    }
+
+    public AbstractResourcePattern(URI baseUri, String pattern) {
+        this.pattern = new ExternalResourceName(baseUri, pattern);
+    }
+
+    public String getPattern() {
+        return pattern.getDecoded();
+    }
+
+    protected ExternalResourceName getBase() {
+        return pattern;
+    }
+
+    protected String substituteTokens(String pattern, Map<String, String> attributes) {
+        return IvyPatternHelper.substituteTokens(pattern, attributes);
+    }
+
+    protected Map<String, String> toAttributes(ModuleComponentArtifactMetaData artifact) {
+        Map<String, String> attributes = toAttributes(artifact.getId().getComponentIdentifier());
+        attributes.putAll(toAttributes(artifact.getName()));
+        return attributes;
+    }
+
+    protected Map<String, String> toAttributes(ModuleIdentifier module, IvyArtifactName ivyArtifactName) {
+        Map<String, String> attributes = toAttributes(module);
+        attributes.putAll(toAttributes(ivyArtifactName));
+        return attributes;
+    }
+
+    protected Map<String, String> toAttributes(IvyArtifactName ivyArtifact) {
+        HashMap<String, String> attributes = new HashMap<String, String>();
+        attributes.put(IvyPatternHelper.ARTIFACT_KEY, ivyArtifact.getName());
+        attributes.put(IvyPatternHelper.TYPE_KEY, ivyArtifact.getType());
+        attributes.put(IvyPatternHelper.EXT_KEY, ivyArtifact.getExtension());
+        attributes.put("classifier", ivyArtifact.getClassifier());
+        return attributes;
+    }
+
+    protected Map<String, String> toAttributes(ModuleIdentifier module) {
+        HashMap<String, String> attributes = new HashMap<String, String>();
+        attributes.put(IvyPatternHelper.ORGANISATION_KEY, module.getGroup());
+        attributes.put(IvyPatternHelper.MODULE_KEY, module.getName());
+        return attributes;
+    }
+
+    protected Map<String, String> toAttributes(ModuleComponentIdentifier componentIdentifier) {
+        HashMap<String, String> attributes = new HashMap<String, String>();
+        attributes.put(IvyPatternHelper.ORGANISATION_KEY, componentIdentifier.getGroup());
+        attributes.put(IvyPatternHelper.MODULE_KEY, componentIdentifier.getModule());
+        attributes.put(IvyPatternHelper.REVISION_KEY, componentIdentifier.getVersion());
+        return attributes;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
new file mode 100644
index 0000000..8b61eaf
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionLister.java
@@ -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.api.internal.artifacts.repositories.resolver;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.ResourceNotFoundException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class ChainedVersionLister implements VersionLister {
+    private final List<VersionLister> versionListers;
+
+    public ChainedVersionLister(VersionLister... delegates) {
+        this.versionListers = Arrays.asList(delegates);
+    }
+
+    public VersionPatternVisitor newVisitor(final ModuleIdentifier module, final Collection<String> dest, final ResourceAwareResolveResult result)  {
+        final List<VersionPatternVisitor> visitors = new ArrayList<VersionPatternVisitor>();
+        for (VersionLister lister : versionListers) {
+            visitors.add(lister.newVisitor(module, dest, result));
+        }
+        return new VersionPatternVisitor() {
+            public void visit(ResourcePattern pattern, IvyArtifactName artifact) throws ResourceException {
+                ResourceNotFoundException failure = null;
+                for (VersionPatternVisitor list : visitors) {
+                    try {
+                        list.visit(pattern, artifact);
+                        return;
+                    } catch (ResourceNotFoundException e) {
+                        // Try next
+                        if (failure == null) {
+                            failure = e;
+                        }
+                    } catch (Exception e) {
+                        throw new ResourceException(String.format("Failed to list versions for %s.", module), e);
+                    }
+                }
+                throw failure;
+            }
+        };
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapter.java
new file mode 100644
index 0000000..8e70d5c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/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.internal.component.external.model.MutableModuleComponentResolveMetaData;
+
+import java.util.List;
+
+public class ComponentMetadataDetailsAdapter implements ComponentMetadataDetails {
+    private final MutableModuleComponentResolveMetaData metadata;
+
+    public ComponentMetadataDetailsAdapter(MutableModuleComponentResolveMetaData 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/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/DefaultExternalResourceArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/DefaultExternalResourceArtifactResolver.java
new file mode 100644
index 0000000..acd949b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/DefaultExternalResourceArtifactResolver.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.repositories.resolver;
+
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.resource.ExternalResourceName;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResourceCandidates;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.List;
+
+class DefaultExternalResourceArtifactResolver implements ExternalResourceArtifactResolver {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExternalResourceArtifactResolver.class);
+
+    private final ExternalResourceRepository repository;
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final List<ResourcePattern> ivyPatterns;
+    private final List<ResourcePattern> artifactPatterns;
+    private final FileStore<ModuleComponentArtifactMetaData> fileStore;
+    private final CacheAwareExternalResourceAccessor resourceAccessor;
+
+    public DefaultExternalResourceArtifactResolver(ExternalResourceRepository repository, LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                                   List<ResourcePattern> ivyPatterns, List<ResourcePattern> artifactPatterns, FileStore<ModuleComponentArtifactMetaData> fileStore, CacheAwareExternalResourceAccessor resourceAccessor) {
+        this.repository = repository;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.ivyPatterns = ivyPatterns;
+        this.artifactPatterns = artifactPatterns;
+        this.fileStore = fileStore;
+        this.resourceAccessor = resourceAccessor;
+    }
+
+    public LocallyAvailableExternalResource resolveMetaDataArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return downloadStaticResource(ivyPatterns, artifact, result);
+    }
+
+    public LocallyAvailableExternalResource resolveArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return downloadStaticResource(artifactPatterns, artifact, result);
+    }
+
+    public boolean artifactExists(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return staticResourceExists(artifactPatterns, artifact, result);
+    }
+
+    private boolean staticResourceExists(List<ResourcePattern> patternList, ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        for (ResourcePattern resourcePattern : patternList) {
+            ExternalResourceName location = resourcePattern.getLocation(artifact);
+            result.attempted(location);
+            LOGGER.debug("Loading {}", location);
+            try {
+                if (repository.getResourceMetaData(location.getUri()) != null) {
+                    return true;
+                }
+            } catch (Exception e) {
+                throw ResourceException.failure(location.getUri(), String.format("Could not get resource '%s'.", location), e);
+            }
+        }
+        return false;
+    }
+
+    private LocallyAvailableExternalResource downloadStaticResource(List<ResourcePattern> patternList, final ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        for (ResourcePattern resourcePattern : patternList) {
+            ExternalResourceName location = resourcePattern.getLocation(artifact);
+            result.attempted(location);
+            LOGGER.debug("Loading {}", location);
+            LocallyAvailableResourceCandidates localCandidates = locallyAvailableResourceFinder.findCandidates(artifact);
+            try {
+                LocallyAvailableExternalResource resource = resourceAccessor.getResource(location.getUri(), new CacheAwareExternalResourceAccessor.ResourceFileStore() {
+                    public LocallyAvailableResource moveIntoCache(File downloadedResource) {
+                        return fileStore.move(artifact, downloadedResource);
+                    }
+                }, localCandidates);
+                if (resource != null) {
+                    return resource;
+                }
+            } catch (Exception e) {
+                throw ResourceException.failure(location.getUri(), String.format("Could not get resource '%s'.", location), e);
+            }
+        }
+        return null;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceArtifactResolver.java
new file mode 100644
index 0000000..80cf839
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceArtifactResolver.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.repositories.resolver;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+public interface ExternalResourceArtifactResolver {
+    @Nullable
+    LocallyAvailableExternalResource resolveMetaDataArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result);
+
+    @Nullable
+    LocallyAvailableExternalResource resolveArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result);
+
+    boolean artifactExists(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
new file mode 100644
index 0000000..97f521e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
@@ -0,0 +1,444 @@
+/*
+ * 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.resolver;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyResolverIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParseException;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.SystemProperties;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.component.external.model.*;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.resolve.result.*;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.ByteArrayLocalResource;
+import org.gradle.internal.resource.local.FileLocalResource;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.gradle.util.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.*;
+
+public abstract class ExternalResourceResolver implements ModuleVersionPublisher, ConfiguredModuleComponentRepository {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
+
+    private List<ResourcePattern> ivyPatterns = new ArrayList<ResourcePattern>();
+    private List<ResourcePattern> artifactPatterns = new ArrayList<ResourcePattern>();
+    private String name;
+    private RepositoryChain repositoryChain;
+
+    private final ExternalResourceRepository repository;
+    private final boolean local;
+    private final CacheAwareExternalResourceAccessor cachingResourceAccessor;
+    private final LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder;
+    private final FileStore<ModuleComponentArtifactMetaData> artifactFileStore;
+
+    private final VersionLister versionLister;
+
+    public ExternalResourceResolver(String name,
+                                    boolean local,
+                                    ExternalResourceRepository repository,
+                                    CacheAwareExternalResourceAccessor cachingResourceAccessor,
+                                    VersionLister versionLister,
+                                    LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                                    FileStore<ModuleComponentArtifactMetaData> artifactFileStore) {
+        this.name = name;
+        this.local = local;
+        this.cachingResourceAccessor = cachingResourceAccessor;
+        this.versionLister = versionLister;
+        this.repository = repository;
+        this.locallyAvailableResourceFinder = locallyAvailableResourceFinder;
+        this.artifactFileStore = artifactFileStore;
+    }
+
+    public String getId() {
+        return DependencyResolverIdentifier.forExternalResourceResolver(this);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isDynamicResolveMode() {
+        return false;
+    }
+
+    public void setRepositoryChain(RepositoryChain resolver) {
+        this.repositoryChain = resolver;
+    }
+
+    protected ExternalResourceRepository getRepository() {
+        return repository;
+    }
+
+    public boolean isLocal() {
+        return local;
+    }
+
+    private void doListModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+        ModuleIdentifier module  = new DefaultModuleIdentifier(dependency.getRequested().getGroup(), dependency.getRequested().getName());
+        Set<String> versions = new LinkedHashSet<String>();
+        VersionPatternVisitor visitor = versionLister.newVisitor(module, versions, result);
+
+        // List modules based on metadata files (artifact version is not considered in listVersionsForAllPatterns())
+        IvyArtifactName metaDataArtifact = getMetaDataArtifactName(dependency.getRequested().getName());
+        listVersionsForAllPatterns(ivyPatterns, metaDataArtifact, visitor);
+
+        // List modules with missing metadata files
+        for (IvyArtifactName otherArtifact : getDependencyArtifactNames(dependency)) {
+            listVersionsForAllPatterns(artifactPatterns, otherArtifact, visitor);
+        }
+        result.listed(versions);
+    }
+
+    private void listVersionsForAllPatterns(List<ResourcePattern> patternList, IvyArtifactName ivyArtifactName, VersionPatternVisitor visitor) {
+        for (ResourcePattern resourcePattern : patternList) {
+            visitor.visit(resourcePattern, ivyArtifactName);
+        }
+    }
+
+    protected void doResolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        resolveStaticDependency(dependency, moduleComponentIdentifier, result, createArtifactResolver());
+    }
+
+    protected final void resolveStaticDependency(DependencyMetaData dependency, ModuleComponentIdentifier moduleVersionIdentifier, BuildableModuleComponentMetaDataResolveResult result, ExternalResourceArtifactResolver artifactResolver) {
+        MutableModuleComponentResolveMetaData metaDataArtifactMetaData = parseMetaDataFromArtifact(moduleVersionIdentifier, artifactResolver, result);
+        if (metaDataArtifactMetaData != null) {
+            LOGGER.debug("Metadata file found for module '{}' in repository '{}'.", moduleVersionIdentifier, getName());
+            result.resolved(metaDataArtifactMetaData);
+            return;
+        }
+
+        MutableModuleComponentResolveMetaData metaDataFromDefaultArtifact = createMetaDataFromDefaultArtifact(moduleVersionIdentifier, dependency, artifactResolver, result);
+        if (metaDataFromDefaultArtifact != null) {
+            LOGGER.debug("Found artifact but no meta-data for module '{}' in repository '{}', using default meta-data.", moduleVersionIdentifier, getName());
+            result.resolved(metaDataFromDefaultArtifact);
+            return;
+        }
+
+        LOGGER.debug("No meta-data file or artifact found for module '{}' in repository '{}'.", moduleVersionIdentifier, getName());
+        result.missing();
+    }
+
+    @Nullable
+    protected MutableModuleComponentResolveMetaData parseMetaDataFromArtifact(ModuleComponentIdentifier moduleComponentIdentifier, ExternalResourceArtifactResolver artifactResolver, ResourceAwareResolveResult result) {
+        ModuleComponentArtifactMetaData artifact = getMetaDataArtifactFor(moduleComponentIdentifier);
+        LocallyAvailableExternalResource metaDataResource = artifactResolver.resolveMetaDataArtifact(artifact, result);
+        if (metaDataResource == null) {
+            return null;
+        }
+
+        ExternalResourceResolverDescriptorParseContext context = new ExternalResourceResolverDescriptorParseContext(repositoryChain);
+        return parseMetaDataFromResource(moduleComponentIdentifier, metaDataResource, context);
+    }
+
+    private MutableModuleComponentResolveMetaData createMetaDataFromDefaultArtifact(ModuleComponentIdentifier moduleVersionIdentifier, DependencyMetaData dependency, ExternalResourceArtifactResolver artifactResolver, ResourceAwareResolveResult result) {
+        for (IvyArtifactName artifact : getDependencyArtifactNames(dependency)) {
+            if (artifactResolver.artifactExists(new DefaultModuleComponentArtifactMetaData(moduleVersionIdentifier, artifact), result)) {
+                return createMetaDataForDependency(dependency);
+            }
+        }
+        return null;
+    }
+
+    protected abstract MutableModuleComponentResolveMetaData createMetaDataForDependency(DependencyMetaData dependency);
+
+    protected abstract MutableModuleComponentResolveMetaData parseMetaDataFromResource(ModuleComponentIdentifier moduleComponentIdentifier, LocallyAvailableExternalResource cachedResource, DescriptorParseContext context);
+
+    private Set<IvyArtifactName> getDependencyArtifactNames(DependencyMetaData dependency) {
+        String moduleName = dependency.getRequested().getName();
+        Set<IvyArtifactName> artifactSet = Sets.newLinkedHashSet();
+        artifactSet.addAll(dependency.getArtifacts());
+
+        if (artifactSet.isEmpty()) {
+            artifactSet.add(new DefaultIvyArtifactName(moduleName, "jar", "jar", Collections.<String, String>emptyMap()));
+        }
+
+        return artifactSet;
+    }
+
+    protected void checkMetadataConsistency(ModuleComponentIdentifier expectedId, ModuleComponentResolveMetaData metadata) throws MetaDataParseException {
+        List<String> errors = new ArrayList<String>();
+        if (!expectedId.getGroup().equals(metadata.getId().getGroup())) {
+            errors.add("bad group: expected='" + expectedId.getGroup() + "' found='" + metadata.getId().getGroup() + "'");
+        }
+        if (!expectedId.getModule().equals(metadata.getId().getName())) {
+            errors.add("bad module name: expected='" + expectedId.getModule() + "' found='" + metadata.getId().getName() + "'");
+        }
+        if (!expectedId.getVersion().equals(metadata.getId().getVersion())) {
+            errors.add("bad version: expected='" + expectedId.getVersion() + "' found='" + metadata.getId().getVersion() + "'");
+        }
+        if (errors.size() > 0) {
+            throw new MetaDataParseException(String.format("inconsistent module metadata found. Descriptor: %s Errors: %s",
+                    metadata.getId(), Joiner.on(SystemProperties.getInstance().getLineSeparator()).join(errors)));
+        }
+    }
+
+    protected abstract boolean isMetaDataArtifact(ArtifactType artifactType);
+
+    protected Set<ModuleComponentArtifactMetaData> findOptionalArtifacts(ModuleComponentResolveMetaData module, String type, String classifier) {
+        ModuleComponentArtifactMetaData artifact = module.artifact(type, "jar", classifier);
+        if (createArtifactResolver(module.getSource()).artifactExists(artifact, new DefaultResourceAwareResolveResult())) {
+            return ImmutableSet.of(artifact);
+        }
+        return Collections.emptySet();
+    }
+
+    private ModuleComponentArtifactMetaData getMetaDataArtifactFor(ModuleComponentIdentifier moduleComponentIdentifier) {
+        IvyArtifactName ivyArtifactName = getMetaDataArtifactName(moduleComponentIdentifier.getModule());
+        return new DefaultModuleComponentArtifactMetaData(moduleComponentIdentifier, ivyArtifactName);
+    }
+
+    protected abstract IvyArtifactName getMetaDataArtifactName(String moduleName);
+
+    protected void resolveArtifact(ComponentArtifactMetaData componentArtifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        ModuleComponentArtifactMetaData artifact = (ModuleComponentArtifactMetaData) componentArtifact;
+
+        File localFile;
+        try {
+            localFile = download(artifact, moduleSource, result);
+        } catch (Throwable e) {
+            result.failed(new ArtifactResolveException(artifact.getId(), e));
+            return;
+        }
+
+        if (localFile != null) {
+            result.resolved(localFile);
+        } else {
+            result.notFound(artifact.getId());
+        }
+    }
+
+    protected File download(ModuleComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+        LocallyAvailableExternalResource artifactResource = createArtifactResolver(moduleSource).resolveArtifact(artifact, result);
+        if (artifactResource == null) {
+            return null;
+        }
+
+        return artifactResource.getLocalResource().getFile();
+    }
+
+    protected ExternalResourceArtifactResolver createArtifactResolver() {
+        return createArtifactResolver(ivyPatterns, artifactPatterns);
+    }
+
+    protected ExternalResourceArtifactResolver createArtifactResolver(List<ResourcePattern> ivyPatterns, List<ResourcePattern> artifactPatterns) {
+        return new DefaultExternalResourceArtifactResolver(repository, locallyAvailableResourceFinder, ivyPatterns, artifactPatterns, artifactFileStore, cachingResourceAccessor);
+    }
+
+    protected ExternalResourceArtifactResolver createArtifactResolver(ModuleSource moduleSource) {
+        return createArtifactResolver();
+    }
+
+    public void publish(IvyModulePublishMetaData moduleVersion) throws IOException {
+        for (IvyModuleArtifactPublishMetaData artifact : moduleVersion.getArtifacts()) {
+            publish(new DefaultModuleComponentArtifactMetaData(artifact.getId()), artifact.getFile());
+        }
+    }
+
+    private void publish(ModuleComponentArtifactMetaData artifact, File src) throws IOException {
+        ResourcePattern destinationPattern;
+        if ("ivy".equals(artifact.getName().getType()) && !ivyPatterns.isEmpty()) {
+            destinationPattern = ivyPatterns.get(0);
+        } else if (!artifactPatterns.isEmpty()) {
+            destinationPattern = artifactPatterns.get(0);
+        } else {
+            throw new IllegalStateException("impossible to publish " + artifact + " using " + this + ": no artifact pattern defined");
+        }
+        URI destination = destinationPattern.getLocation(artifact).getUri();
+
+        put(src, destination);
+        LOGGER.info("Published {} to {}", artifact, destination);
+    }
+
+    private void put(File src, URI destination) throws IOException {
+        repository.withProgressLogging().put(new FileLocalResource(src), destination);
+        putChecksum(src, destination);
+    }
+
+    private void putChecksum(File source, URI destination) throws IOException {
+        byte[] checksumFile = createChecksumFile(source, "SHA1", 40);
+        URI checksumDestination = URI.create(destination + ".sha1");
+        repository.put(new ByteArrayLocalResource(checksumFile), checksumDestination);
+    }
+
+    private byte[] createChecksumFile(File src, String algorithm, int checksumLength) {
+        HashValue hash = HashUtil.createHash(src, algorithm);
+        String formattedHashString = hash.asZeroPaddedHexString(checksumLength);
+        try {
+            return formattedHashString.getBytes("US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    protected void addIvyPattern(ResourcePattern pattern) {
+        ivyPatterns.add(pattern);
+    }
+
+    protected void addArtifactPattern(ResourcePattern pattern) {
+        artifactPatterns.add(pattern);
+    }
+
+    public List<String> getIvyPatterns() {
+        return CollectionUtils.collect(ivyPatterns, new Transformer<String, ResourcePattern>() {
+            public String transform(ResourcePattern original) {
+                return original.getPattern();
+            }
+        });
+    }
+
+    public List<String> getArtifactPatterns() {
+        return CollectionUtils.collect(artifactPatterns, new Transformer<String, ResourcePattern>() {
+            public String transform(ResourcePattern original) {
+                return original.getPattern();
+            }
+        });
+    }
+
+    protected void setIvyPatterns(Iterable<? extends ResourcePattern> patterns) {
+        ivyPatterns.clear();
+        CollectionUtils.addAll(ivyPatterns, patterns);
+    }
+
+    protected void setArtifactPatterns(List<ResourcePattern> patterns) {
+        artifactPatterns = patterns;
+    }
+
+    public abstract boolean isM2compatible();
+
+    protected abstract class AbstractRepositoryAccess implements ModuleComponentRepositoryAccess {
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            ModuleComponentResolveMetaData moduleMetaData = (ModuleComponentResolveMetaData) component;
+
+            if (artifactType == ArtifactType.JAVADOC) {
+                resolveJavadocArtifacts(moduleMetaData, result);
+            } else if (artifactType == ArtifactType.SOURCES) {
+                resolveSourceArtifacts(moduleMetaData, result);
+            } else if (isMetaDataArtifact(artifactType)) {
+                resolveMetaDataArtifacts(moduleMetaData, result);
+            }
+        }
+
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            String configurationName = componentUsage.getConfigurationName();
+             ConfigurationMetaData configuration = component.getConfiguration(configurationName);
+             resolveConfigurationArtifacts((ModuleComponentResolveMetaData) component, configuration, result);
+        }
+
+        protected abstract void resolveConfigurationArtifacts(ModuleComponentResolveMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result);
+
+        protected abstract void resolveMetaDataArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result);
+
+        protected abstract void resolveJavadocArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result);
+
+        protected abstract void resolveSourceArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result);
+
+    }
+
+    protected abstract class LocalRepositoryAccess extends AbstractRepositoryAccess {
+        @Override
+        public String toString() {
+            return "local > " + ExternalResourceResolver.this.toString();
+        }
+
+        public final void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+        }
+
+        public final void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        }
+
+        protected final void resolveMetaDataArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            ModuleComponentArtifactMetaData artifact = getMetaDataArtifactFor(module.getComponentId());
+            result.resolved(Collections.singleton(artifact));
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+
+        }
+    }
+
+    protected abstract class RemoteRepositoryAccess extends AbstractRepositoryAccess {
+        @Override
+        public String toString() {
+            return "remote > " + ExternalResourceResolver.this.toString();
+        }
+
+        public final void listModuleVersions(DependencyMetaData dependency, BuildableModuleVersionListingResolveResult result) {
+            doListModuleVersions(dependency, result);
+        }
+
+        public final void resolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+            doResolveComponentMetaData(dependency, moduleComponentIdentifier, result);
+        }
+
+        @Override
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+            super.resolveModuleArtifacts(component, artifactType, result);
+            checkArtifactsResolved(component, artifactType, result);
+        }
+
+        @Override
+        public void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage componentUsage, BuildableArtifactSetResolveResult result) {
+            super.resolveModuleArtifacts(component, componentUsage, result);
+            checkArtifactsResolved(component, componentUsage, result);
+        }
+
+        private void checkArtifactsResolved(ComponentResolveMetaData component, Object context, BuildableArtifactSetResolveResult result) {
+            if (!result.hasResult()) {
+                result.failed(new ArtifactResolveException(component.getComponentId(),
+                        String.format("Cannot locate %s for '%s' in repository '%s'", context, component, name)));
+            }
+        }
+
+        protected final void resolveMetaDataArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            // Meta data  artifacts are determined locally
+        }
+
+        public void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result) {
+            ExternalResourceResolver.this.resolveArtifact(artifact, moduleSource, result);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
new file mode 100644
index 0000000..c3f36f5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.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.repositories.resolver;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.DefaultDependencyMetaData;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.resolver.ArtifactResolver;
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver;
+import org.gradle.internal.resolve.result.*;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+
+import java.io.File;
+
+/**
+ * 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 implements DescriptorParseContext {
+    private final RepositoryChain mainResolvers;
+
+    public ExternalResourceResolverDescriptorParseContext(RepositoryChain mainResolvers) {
+        this.mainResolvers = mainResolvers;
+    }
+
+    public LocallyAvailableExternalResource getMetaDataArtifact(ModuleVersionIdentifier moduleVersionIdentifier, ArtifactType artifactType) {
+        File resolvedArtifactFile = resolveMetaDataArtifactFile(moduleVersionIdentifier, mainResolvers.getDependencyResolver(), mainResolvers.getArtifactResolver(), artifactType);
+        LocallyAvailableResource localResource = new DefaultLocallyAvailableResource(resolvedArtifactFile);
+        return new DefaultLocallyAvailableExternalResource(resolvedArtifactFile.toURI(), localResource);
+    }
+
+    private File resolveMetaDataArtifactFile(ModuleVersionIdentifier moduleVersionIdentifier, DependencyToComponentResolver dependencyResolver,
+                                             ArtifactResolver artifactResolver, ArtifactType artifactType) {
+        BuildableComponentResolveResult moduleVersionResolveResult = new DefaultBuildableComponentResolveResult();
+        dependencyResolver.resolve(new DefaultDependencyMetaData(moduleVersionIdentifier), moduleVersionResolveResult);
+
+        BuildableArtifactSetResolveResult moduleArtifactsResolveResult = new DefaultBuildableArtifactSetResolveResult();
+        artifactResolver.resolveModuleArtifacts(moduleVersionResolveResult.getMetaData(), artifactType, moduleArtifactsResolveResult);
+
+        BuildableArtifactResolveResult artifactResolveResult = new DefaultBuildableArtifactResolveResult();
+        ComponentArtifactMetaData artifactMetaData = moduleArtifactsResolveResult.getArtifacts().iterator().next();
+        artifactResolver.resolveArtifact(artifactMetaData, moduleVersionResolveResult.getMetaData().getSource(), artifactResolveResult);
+        return artifactResolveResult.getFile();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
new file mode 100644
index 0000000..4f26b35
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.resolver;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.component.external.model.DefaultIvyModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DownloadedIvyModuleDescriptorParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+import org.gradle.internal.component.model.ConfigurationMetaData;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.net.URI;
+
+public class IvyResolver extends ExternalResourceResolver implements PatternBasedResolver {
+
+    private final boolean dynamicResolve;
+    private final MetaDataParser<DefaultIvyModuleResolveMetaData> metaDataParser;
+    private boolean m2Compatible;
+
+    public IvyResolver(String name, RepositoryTransport transport,
+                       LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                       boolean dynamicResolve, ResolverStrategy resolverStrategy, FileStore<ModuleComponentArtifactMetaData> artifactFileStore) {
+        super(name, transport.isLocal(), transport.getRepository(), transport.getResourceAccessor(), new ResourceVersionLister(transport.getRepository()), locallyAvailableResourceFinder, artifactFileStore);
+        this.metaDataParser = new DownloadedIvyModuleDescriptorParser(resolverStrategy);
+        this.dynamicResolve = dynamicResolve;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Ivy repository '%s'", getName());
+    }
+
+    @Override
+    public boolean isDynamicResolveMode() {
+        return dynamicResolve;
+    }
+
+    protected boolean isMetaDataArtifact(ArtifactType artifactType) {
+        return artifactType == ArtifactType.IVY_DESCRIPTOR;
+    }
+
+    @Override
+    protected IvyArtifactName getMetaDataArtifactName(String moduleName) {
+        return new DefaultIvyArtifactName("ivy", "ivy", "xml");
+    }
+
+    @Override
+    public boolean isM2compatible() {
+        return m2Compatible;
+    }
+
+    public void setM2compatible(boolean m2compatible) {
+        this.m2Compatible = m2compatible;
+    }
+
+    public void addArtifactLocation(URI baseUri, String pattern) {
+        addArtifactPattern(toResourcePattern(baseUri, pattern));
+    }
+
+    public void addDescriptorLocation(URI baseUri, String pattern) {
+        addIvyPattern(toResourcePattern(baseUri, pattern));
+    }
+
+    protected ResourcePattern toResourcePattern(URI baseUri, String pattern) {
+        return isM2compatible() ? new M2ResourcePattern(baseUri, pattern) : new IvyResourcePattern(baseUri, pattern);
+    }
+
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return new IvyLocalRepositoryAccess();
+    }
+
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return new IvyRemoteRepositoryAccess();
+    }
+
+    protected MutableModuleComponentResolveMetaData createMetaDataForDependency(DependencyMetaData dependency) {
+        return new DefaultIvyModuleResolveMetaData(dependency);
+    }
+
+    protected MutableModuleComponentResolveMetaData parseMetaDataFromResource(ModuleComponentIdentifier moduleComponentIdentifier, LocallyAvailableExternalResource cachedResource, DescriptorParseContext context) {
+        MutableModuleComponentResolveMetaData metaData = metaDataParser.parseMetaData(context, cachedResource);
+        checkMetadataConsistency(moduleComponentIdentifier, metaData);
+        return metaData;
+    }
+
+    private class IvyLocalRepositoryAccess extends LocalRepositoryAccess {
+
+        protected void resolveConfigurationArtifacts(ModuleComponentResolveMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result) {
+            result.resolved(configuration.getArtifacts());
+        }
+
+        @Override
+        protected void resolveJavadocArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            ConfigurationMetaData configuration = module.getConfiguration("javadoc");
+            if (configuration != null) {
+                result.resolved(configuration.getArtifacts());
+            }
+        }
+
+        @Override
+        protected void resolveSourceArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            ConfigurationMetaData configuration = module.getConfiguration("sources");
+            if (configuration != null) {
+                result.resolved(configuration.getArtifacts());
+            }
+        }
+    }
+
+    private class IvyRemoteRepositoryAccess extends RemoteRepositoryAccess {
+        @Override
+        protected void resolveConfigurationArtifacts(ModuleComponentResolveMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result) {
+            // Configuration artifacts are determined locally
+        }
+
+        @Override
+        protected void resolveJavadocArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            // Probe for artifact with classifier
+            result.resolved(findOptionalArtifacts(module, "javadoc", "javadoc"));
+        }
+
+        @Override
+        protected void resolveSourceArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            // Probe for artifact with classifier
+            result.resolved(findOptionalArtifacts(module, "source", "sources"));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java
new file mode 100644
index 0000000..f4344ed
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePattern.java
@@ -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.api.internal.artifacts.repositories.resolver;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResourceName;
+
+import java.net.URI;
+import java.util.Map;
+
+public class IvyResourcePattern extends AbstractResourcePattern implements ResourcePattern {
+
+    public IvyResourcePattern(String pattern) {
+        super(pattern);
+    }
+
+    public IvyResourcePattern(URI baseUri, String pattern) {
+        super(baseUri, pattern);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Ivy pattern '%s'", getPattern());
+    }
+
+    public ExternalResourceName getLocation(ModuleComponentArtifactMetaData artifact) {
+        Map<String, String> attributes = toAttributes(artifact);
+        return getBase().getRoot().resolve(substituteTokens(getBase().getPath(), attributes));
+    }
+
+    public ExternalResourceName toVersionListPattern(ModuleIdentifier module, IvyArtifactName artifact) {
+        Map<String, String> attributes = toAttributes(module, artifact);
+        return getBase().getRoot().resolve(substituteTokens(getBase().getPath(), attributes));
+    }
+
+    public ExternalResourceName toModulePath(ModuleIdentifier module) {
+        throw new UnsupportedOperationException("not implemented yet.");
+    }
+
+    public ExternalResourceName toModuleVersionPath(ModuleComponentIdentifier componentIdentifier) {
+        throw new UnsupportedOperationException("not implemented yet.");
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java
new file mode 100644
index 0000000..438cbf0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePattern.java
@@ -0,0 +1,89 @@
+/*
+ * 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.resolver;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResourceName;
+
+import java.net.URI;
+import java.util.Map;
+
+public class M2ResourcePattern extends AbstractResourcePattern {
+    public M2ResourcePattern(String pattern) {
+        super(pattern);
+    }
+
+    public M2ResourcePattern(URI baseUri, String pattern) {
+        super(baseUri, pattern);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("M2 pattern '%s'", getPattern());
+    }
+
+    public ExternalResourceName getLocation(ModuleComponentArtifactMetaData artifact) {
+        Map<String, String> attributes = toAttributes(artifact);
+        String pattern = maybeSubstituteTimestamp(artifact, getBase().getPath());
+        return getBase().getRoot().resolve(substituteTokens(pattern, attributes));
+    }
+
+    private String maybeSubstituteTimestamp(ModuleComponentArtifactMetaData artifact, String pattern) {
+        if (artifact.getComponentId() instanceof MavenUniqueSnapshotComponentIdentifier) {
+            MavenUniqueSnapshotComponentIdentifier snapshotId = (MavenUniqueSnapshotComponentIdentifier) artifact.getComponentId();
+            pattern = pattern
+                    .replaceFirst("\\-\\[revision\\]", "-" + snapshotId.getTimestampedVersion())
+                    .replace("[revision]", snapshotId.getSnapshotVersion());
+        }
+        return pattern;
+    }
+
+    public ExternalResourceName toVersionListPattern(ModuleIdentifier module, IvyArtifactName artifact) {
+        Map<String, String> attributes = toAttributes(module, artifact);
+        return getBase().getRoot().resolve(substituteTokens(getBase().getPath(), attributes));
+    }
+
+    public ExternalResourceName toModulePath(ModuleIdentifier module) {
+        String pattern = getBase().getPath();
+        if (!pattern.endsWith(MavenPattern.M2_PATTERN)) {
+            throw new UnsupportedOperationException("Cannot locate module for non-maven layout.");
+        }
+        String metaDataPattern = pattern.substring(0, pattern.length() - MavenPattern.M2_PER_MODULE_PATTERN.length() - 1);
+        return getBase().getRoot().resolve(substituteTokens(metaDataPattern, toAttributes(module)));
+    }
+
+    public ExternalResourceName toModuleVersionPath(ModuleComponentIdentifier componentIdentifier) {
+        String pattern = getBase().getPath();
+        if (!pattern.endsWith(MavenPattern.M2_PATTERN)) {
+            throw new UnsupportedOperationException("Cannot locate module version for non-maven layout.");
+        }
+        String metaDataPattern = pattern.substring(0, pattern.length() - MavenPattern.M2_PER_MODULE_VERSION_PATTERN.length() - 1);
+        return getBase().getRoot().resolve(substituteTokens(metaDataPattern, toAttributes(componentIdentifier)));
+    }
+
+    protected String substituteTokens(String pattern, Map<String, String> attributes) {
+        String org = attributes.get(IvyPatternHelper.ORGANISATION_KEY);
+        if (org != null) {
+            attributes.put(IvyPatternHelper.ORGANISATION_KEY, org.replace(".", "/"));
+        }
+        return super.substituteTokens(pattern, attributes);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java
new file mode 100644
index 0000000..5c8933b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenLocalResolver.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.internal.component.external.model.DefaultMavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.MavenModuleResolveMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.result.DefaultResourceAwareResolveResult;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+
+public class MavenLocalResolver extends MavenResolver {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MavenResolver.class);
+
+    public MavenLocalResolver(String name, URI rootUri, RepositoryTransport transport,
+                              LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                              FileStore<ModuleComponentArtifactMetaData> artifactFileStore,
+                              MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser) {
+        super(name, rootUri, transport, locallyAvailableResourceFinder, artifactFileStore, pomParser);
+    }
+
+    @Override
+    @Nullable
+    protected MutableModuleComponentResolveMetaData parseMetaDataFromArtifact(ModuleComponentIdentifier moduleComponentIdentifier, ExternalResourceArtifactResolver artifactResolver, ResourceAwareResolveResult result) {
+        MutableModuleComponentResolveMetaData metaData = super.parseMetaDataFromArtifact(moduleComponentIdentifier, artifactResolver, result);
+        if (metaData == null) {
+            return null;
+        }
+
+        if (isOrphanedPom(mavenMetaData(metaData), artifactResolver)) {
+            return null;
+        }
+        return metaData;
+    }
+
+    private boolean isOrphanedPom(MavenModuleResolveMetaData metaData, ExternalResourceArtifactResolver artifactResolver) {
+        if (metaData.isPomPackaging()) {
+            return false;
+        }
+
+        // check custom packaging
+        ModuleComponentArtifactMetaData artifact;
+        if (metaData.isKnownJarPackaging()) {
+            artifact = metaData.artifact("jar", "jar", null);
+        } else {
+            artifact = metaData.artifact(metaData.getPackaging(), metaData.getPackaging(), null);
+        }
+
+        if (artifactResolver.artifactExists(artifact, new DefaultResourceAwareResolveResult())) {
+            return false;
+        }
+
+        LOGGER.debug("POM file found for module '{}' in repository '{}' but no artifact found. Ignoring.", metaData.getId(), getName());
+        return true;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadata.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadata.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadata.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
new file mode 100644
index 0000000..5728c39
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenMetadataLoader.java
@@ -0,0 +1,90 @@
+/*
+ * 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.resolver;
+
+import org.apache.ivy.util.ContextualSAXHandler;
+import org.apache.ivy.util.XMLHelper;
+import org.gradle.internal.ErroringAction;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.ResourceNotFoundException;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+class MavenMetadataLoader {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MavenMetadataLoader.class);
+
+    private final ExternalResourceRepository repository;
+
+    public MavenMetadataLoader(ExternalResourceRepository repository) {
+        this.repository = repository;
+    }
+
+    public MavenMetadata load(URI metadataLocation) throws ResourceException {
+        MavenMetadata metadata = new MavenMetadata();
+        try {
+            parseMavenMetadataInfo(metadataLocation, metadata);
+        } catch (ResourceNotFoundException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ResourceException(metadataLocation, String.format("Unable to load Maven meta-data from %s.", metadataLocation), e);
+        }
+        return metadata;
+    }
+
+    private void parseMavenMetadataInfo(final URI metadataLocation, final MavenMetadata metadata) {
+        ExternalResource resource = repository.getResource(metadataLocation);
+        if (resource == null) {
+            throw new ResourceNotFoundException(metadataLocation, String.format("Maven meta-data not available: %s", metadataLocation));
+        }
+        try {
+            parseMavenMetadataInto(resource, metadata);
+        } finally {
+            resource.close();
+        }
+    }
+
+    private void parseMavenMetadataInto(ExternalResource metadataResource, final MavenMetadata mavenMetadata) {
+        LOGGER.debug("parsing maven-metadata: {}", metadataResource);
+        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);
+            }
+        });
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenPattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenPattern.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenPattern.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenPattern.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
new file mode 100644
index 0000000..b24e362
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
@@ -0,0 +1,280 @@
+/*
+ * 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.resolver;
+
+import com.google.common.collect.ImmutableSet;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.Transformers;
+import org.gradle.internal.component.external.model.*;
+import org.gradle.internal.component.model.*;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
+import org.gradle.internal.resolve.result.DefaultResourceAwareResolveResult;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.resource.ExternalResourceName;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.ResourceNotFoundException;
+import org.gradle.internal.resource.local.FileStore;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+
+import java.net.URI;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MavenResolver extends ExternalResourceResolver {
+    private final URI root;
+    private final List<URI> artifactRoots = new ArrayList<URI>();
+    private final MavenMetadataLoader mavenMetaDataLoader;
+    private final MetaDataParser<DefaultMavenModuleResolveMetaData> metaDataParser;
+    private static final Pattern UNIQUE_SNAPSHOT = Pattern.compile("(?:.+)-(\\d{8}\\.\\d{6}-\\d+)");
+
+    public MavenResolver(String name, URI rootUri, RepositoryTransport transport,
+                         LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder,
+                         FileStore<ModuleComponentArtifactMetaData> artifactFileStore, MetaDataParser<DefaultMavenModuleResolveMetaData> pomParser) {
+        super(name, transport.isLocal(),
+                transport.getRepository(),
+                transport.getResourceAccessor(),
+                new ChainedVersionLister(new MavenVersionLister(transport.getRepository()), new ResourceVersionLister(transport.getRepository())),
+                locallyAvailableResourceFinder,
+                artifactFileStore);
+        this.metaDataParser = pomParser;
+        this.mavenMetaDataLoader = new MavenMetadataLoader(transport.getRepository());
+        this.root = rootUri;
+
+        updatePatterns();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Maven repository '%s'", getName());
+    }
+
+    public URI getRoot() {
+        return root;
+    }
+
+    protected void doResolveComponentMetaData(DependencyMetaData dependency, ModuleComponentIdentifier moduleComponentIdentifier, BuildableModuleComponentMetaDataResolveResult result) {
+        if (isNonUniqueSnapshot(moduleComponentIdentifier)) {
+            MavenUniqueSnapshotModuleSource uniqueSnapshotVersion = findUniqueSnapshotVersion(moduleComponentIdentifier, result);
+            if (uniqueSnapshotVersion != null) {
+                MavenUniqueSnapshotComponentIdentifier snapshotIdentifier = composeSnapshotIdentifier(moduleComponentIdentifier, uniqueSnapshotVersion);
+                resolveUniqueSnapshotDependency(dependency, snapshotIdentifier, result, uniqueSnapshotVersion);
+                return;
+            }
+        } else {
+            MavenUniqueSnapshotModuleSource uniqueSnapshotVersion = composeUniqueSnapshotVersion(moduleComponentIdentifier);
+            if (uniqueSnapshotVersion != null) {
+                MavenUniqueSnapshotComponentIdentifier snapshotIdentifier = composeSnapshotIdentifier(moduleComponentIdentifier, uniqueSnapshotVersion);
+                resolveUniqueSnapshotDependency(dependency, snapshotIdentifier, result, uniqueSnapshotVersion);
+                return;
+            }
+        }
+
+        resolveStaticDependency(dependency, moduleComponentIdentifier, result, super.createArtifactResolver());
+    }
+
+    protected boolean isMetaDataArtifact(ArtifactType artifactType) {
+        return artifactType == ArtifactType.MAVEN_POM;
+    }
+
+    private MutableModuleComponentResolveMetaData processMetaData(MutableModuleComponentResolveMetaData metaData) {
+        if (isNonUniqueSnapshot(metaData.getComponentId())) {
+            metaData.setChanging(true);
+        }
+        return metaData;
+    }
+
+    private void resolveUniqueSnapshotDependency(DependencyMetaData dependency, MavenUniqueSnapshotComponentIdentifier module, BuildableModuleComponentMetaDataResolveResult result, MavenUniqueSnapshotModuleSource snapshotSource) {
+        resolveStaticDependency(dependency, module, result, createArtifactResolver(snapshotSource));
+        if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
+            result.getMetaData().setSource(snapshotSource);
+        }
+    }
+
+    @Override
+    protected ExternalResourceArtifactResolver createArtifactResolver(ModuleSource moduleSource) {
+
+        if (moduleSource instanceof MavenUniqueSnapshotModuleSource) {
+            final String timestamp = ((MavenUniqueSnapshotModuleSource) moduleSource).getTimestamp();
+            return new MavenUniqueSnapshotExternalResourceArtifactResolver(super.createArtifactResolver(moduleSource), timestamp);
+        }
+
+        return super.createArtifactResolver(moduleSource);
+    }
+
+    public void addArtifactLocation(URI baseUri) {
+        artifactRoots.add(baseUri);
+        updatePatterns();
+    }
+
+    private M2ResourcePattern getWholePattern() {
+        return new M2ResourcePattern(root, MavenPattern.M2_PATTERN);
+    }
+
+    private void updatePatterns() {
+        setIvyPatterns(Collections.singletonList(getWholePattern()));
+
+        List<ResourcePattern> artifactPatterns = new ArrayList<ResourcePattern>();
+        artifactPatterns.add(getWholePattern());
+        for (URI artifactRoot : artifactRoots) {
+            artifactPatterns.add(new M2ResourcePattern(artifactRoot, MavenPattern.M2_PATTERN));
+        }
+        setArtifactPatterns(artifactPatterns);
+    }
+
+    @Override
+    protected IvyArtifactName getMetaDataArtifactName(String moduleName) {
+        return new DefaultIvyArtifactName(moduleName, "pom", "pom");
+    }
+
+    private MavenUniqueSnapshotModuleSource findUniqueSnapshotVersion(ModuleComponentIdentifier module, ResourceAwareResolveResult result) {
+        ExternalResourceName metadataLocation = getWholePattern().toModuleVersionPath(module).resolve("maven-metadata.xml");
+        result.attempted(metadataLocation);
+        MavenMetadata mavenMetadata = parseMavenMetadata(metadataLocation.getUri());
+
+        if (mavenMetadata.timestamp != null) {
+            // we have found a timestamp, so this is a snapshot unique version
+            String timestamp = String.format("%s-%s", mavenMetadata.timestamp, mavenMetadata.buildNumber);
+            return new MavenUniqueSnapshotModuleSource(timestamp);
+        }
+        return null;
+    }
+
+    @Nullable
+    private MavenUniqueSnapshotModuleSource composeUniqueSnapshotVersion(ModuleComponentIdentifier moduleComponentIdentifier) {
+        Matcher matcher = UNIQUE_SNAPSHOT.matcher(moduleComponentIdentifier.getVersion());
+        if (!matcher.matches()) {
+            return null;
+        }
+        return new MavenUniqueSnapshotModuleSource(matcher.group(1));
+    }
+
+    private MavenMetadata parseMavenMetadata(URI metadataLocation) {
+        try {
+            return mavenMetaDataLoader.load(metadataLocation);
+        } catch (ResourceNotFoundException e) {
+            return new MavenMetadata();
+        }
+    }
+
+    @Override
+    public boolean isM2compatible() {
+        return true;
+    }
+
+    public ModuleComponentRepositoryAccess getLocalAccess() {
+        return new MavenLocalRepositoryAccess();
+    }
+
+    public ModuleComponentRepositoryAccess getRemoteAccess() {
+        return new MavenRemoteRepositoryAccess();
+    }
+
+    @Override
+    protected MutableModuleComponentResolveMetaData createMetaDataForDependency(DependencyMetaData dependency) {
+        return processMetaData(new DefaultMavenModuleResolveMetaData(dependency));
+    }
+
+    protected MutableModuleComponentResolveMetaData parseMetaDataFromResource(ModuleComponentIdentifier moduleComponentIdentifier, LocallyAvailableExternalResource cachedResource, DescriptorParseContext context) {
+        DefaultMavenModuleResolveMetaData metaData = metaDataParser.parseMetaData(context, cachedResource);
+        if (moduleComponentIdentifier instanceof MavenUniqueSnapshotComponentIdentifier) {
+            // Snapshot POMs use -SNAPSHOT instead of the timestamp as version, so validate against the expected id
+            MavenUniqueSnapshotComponentIdentifier snapshotComponentIdentifier = (MavenUniqueSnapshotComponentIdentifier) moduleComponentIdentifier;
+            checkMetadataConsistency(snapshotComponentIdentifier.getSnapshotComponent(), metaData);
+            // Use the requested id. Currently we're discarding the MavenUniqueSnapshotComponentIdentifier and replacing with DefaultModuleComponentIdentifier as pretty
+            // much every consumer of the meta-data is expecting a DefaultModuleComponentIdentifier.
+            ModuleComponentIdentifier lossyId = DefaultModuleComponentIdentifier.newId(moduleComponentIdentifier.getGroup(), moduleComponentIdentifier.getModule(), moduleComponentIdentifier.getVersion());
+            metaData.setComponentId(lossyId);
+            metaData.setSnapshotTimestamp(snapshotComponentIdentifier.getTimestamp());
+        } else {
+            checkMetadataConsistency(moduleComponentIdentifier, metaData);
+        }
+        return processMetaData(metaData);
+    }
+
+    protected static MavenModuleResolveMetaData mavenMetaData(ModuleComponentResolveMetaData metaData) {
+        return Transformers.cast(MavenModuleResolveMetaData.class).transform(metaData);
+    }
+
+    private class MavenLocalRepositoryAccess extends LocalRepositoryAccess {
+        @Override
+        protected void resolveConfigurationArtifacts(ModuleComponentResolveMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result) {
+            if (mavenMetaData(module).isKnownJarPackaging()) {
+                ModuleComponentArtifactMetaData artifact = module.artifact("jar", "jar", null);
+                result.resolved(ImmutableSet.of(artifact));
+            }
+        }
+
+        @Override
+        protected void resolveJavadocArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            // Javadoc artifacts are optional, so we need to probe for them remotely
+        }
+
+        @Override
+        protected void resolveSourceArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            // Javadoc artifacts are optional, so we need to probe for them remotely
+        }
+    }
+
+    private class MavenRemoteRepositoryAccess extends RemoteRepositoryAccess {
+        @Override
+        protected void resolveConfigurationArtifacts(ModuleComponentResolveMetaData module, ConfigurationMetaData configuration, BuildableArtifactSetResolveResult result) {
+            MavenModuleResolveMetaData mavenMetaData = mavenMetaData(module);
+            if (mavenMetaData.isPomPackaging()) {
+                Set<ComponentArtifactMetaData> artifacts = new LinkedHashSet<ComponentArtifactMetaData>();
+                artifacts.addAll(findOptionalArtifacts(module, "jar", null));
+                result.resolved(artifacts);
+            } else {
+                ModuleComponentArtifactMetaData artifactMetaData = module.artifact(mavenMetaData.getPackaging(), mavenMetaData.getPackaging(), null);
+
+                if (createArtifactResolver(module.getSource()).artifactExists(artifactMetaData, new DefaultResourceAwareResolveResult())) {
+                    result.resolved(ImmutableSet.of(artifactMetaData));
+                } else {
+                    ModuleComponentArtifactMetaData artifact = module.artifact("jar", "jar", null);
+                    result.resolved(ImmutableSet.of(artifact));
+                }
+            }
+        }
+
+        @Override
+        protected void resolveJavadocArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            result.resolved(findOptionalArtifacts(module, "javadoc", "javadoc"));
+        }
+
+        @Override
+        protected void resolveSourceArtifacts(ModuleComponentResolveMetaData module, BuildableArtifactSetResolveResult result) {
+            result.resolved(findOptionalArtifacts(module, "source", "sources"));
+        }
+    }
+
+    private boolean isNonUniqueSnapshot(ModuleComponentIdentifier moduleComponentIdentifier) {
+        return moduleComponentIdentifier.getVersion().endsWith("-SNAPSHOT");
+    }
+
+    private MavenUniqueSnapshotComponentIdentifier composeSnapshotIdentifier(ModuleComponentIdentifier moduleComponentIdentifier, MavenUniqueSnapshotModuleSource uniqueSnapshotVersion) {
+        return new MavenUniqueSnapshotComponentIdentifier(moduleComponentIdentifier.getGroup(),
+                moduleComponentIdentifier.getModule(),
+                moduleComponentIdentifier.getVersion(),
+                uniqueSnapshotVersion.getTimestamp());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifier.java
new file mode 100644
index 0000000..2eca07a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifier.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.api.internal.artifacts.repositories.resolver;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+
+/**
+ * A component identifier for a Maven unique snapshot module.
+ */
+public class MavenUniqueSnapshotComponentIdentifier extends DefaultModuleComponentIdentifier {
+    private final String timestamp;
+
+    public MavenUniqueSnapshotComponentIdentifier(String group, String module, String version, String timestamp) {
+        super(group, module, version);
+        this.timestamp = timestamp;
+    }
+
+    public MavenUniqueSnapshotComponentIdentifier(ModuleComponentIdentifier baseIdentifier, String timestamp) {
+        super(baseIdentifier.getGroup(), baseIdentifier.getModule(), baseIdentifier.getVersion());
+        this.timestamp = timestamp;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return super.equals(o) && ((MavenUniqueSnapshotComponentIdentifier) o).timestamp.equals(timestamp);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() + timestamp.hashCode();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return String.format("%s:%s:%s:%s", getGroup(), getModule(), getSnapshotVersion(), timestamp);
+    }
+
+    public String getTimestamp() {
+        return timestamp;
+    }
+
+    public String getSnapshotVersion() {
+        return getVersion().replace(timestamp, "SNAPSHOT");
+    }
+
+    public ModuleComponentIdentifier getSnapshotComponent() {
+        return DefaultModuleComponentIdentifier.newId(getGroup(), getModule(), getSnapshotVersion());
+    }
+
+    public String getTimestampedVersion() {
+        return getVersion().replace("SNAPSHOT", timestamp);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolver.java
new file mode 100644
index 0000000..2dbe366
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolver.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.artifacts.repositories.resolver;
+
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+
+class MavenUniqueSnapshotExternalResourceArtifactResolver implements ExternalResourceArtifactResolver {
+    private final ExternalResourceArtifactResolver delegate;
+    private final String timestamp;
+
+    public MavenUniqueSnapshotExternalResourceArtifactResolver(ExternalResourceArtifactResolver delegate, String timestamp) {
+        this.delegate = delegate;
+        this.timestamp = timestamp;
+    }
+
+    public boolean artifactExists(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return delegate.artifactExists(timestamp(artifact), result);
+    }
+
+    public LocallyAvailableExternalResource resolveArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return delegate.resolveArtifact(timestamp(artifact), result);
+    }
+
+    public LocallyAvailableExternalResource resolveMetaDataArtifact(ModuleComponentArtifactMetaData artifact, ResourceAwareResolveResult result) {
+        return delegate.resolveMetaDataArtifact(timestamp(artifact), result);
+    }
+
+    protected ModuleComponentArtifactMetaData timestamp(ModuleComponentArtifactMetaData artifact) {
+        MavenUniqueSnapshotComponentIdentifier snapshotComponentIdentifier =
+                new MavenUniqueSnapshotComponentIdentifier(artifact.getId().getComponentIdentifier(), timestamp);
+        return new DefaultModuleComponentArtifactMetaData(snapshotComponentIdentifier, artifact.getName());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotModuleSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotModuleSource.java
new file mode 100644
index 0000000..dfa0fb3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotModuleSource.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.repositories.resolver;
+
+import org.gradle.internal.component.model.ModuleSource;
+
+class MavenUniqueSnapshotModuleSource implements ModuleSource {
+    private final String timestamp;
+
+    MavenUniqueSnapshotModuleSource(String timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getTimestamp() {
+        return timestamp;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
new file mode 100644
index 0000000..05df945
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionLister.java
@@ -0,0 +1,54 @@
+/*
+ * 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.resolver;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResourceName;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class MavenVersionLister implements VersionLister {
+    private final MavenMetadataLoader mavenMetadataLoader;
+
+    public MavenVersionLister(ExternalResourceRepository repository) {
+        this.mavenMetadataLoader = new MavenMetadataLoader(repository);
+    }
+
+    public VersionPatternVisitor newVisitor(final ModuleIdentifier module, final Collection<String> dest, final ResourceAwareResolveResult result) {
+        return new VersionPatternVisitor() {
+            final Set<ExternalResourceName> searched = new HashSet<ExternalResourceName>();
+
+            public void visit(ResourcePattern pattern, IvyArtifactName artifact) throws ResourceException {
+                ExternalResourceName metadataLocation = pattern.toModulePath(module).resolve("maven-metadata.xml");
+                if (!searched.add(metadataLocation)) {
+                    return;
+                }
+                result.attempted(metadataLocation);
+                MavenMetadata mavenMetaData = mavenMetadataLoader.load(metadataLocation.getUri());
+                for (String version : mavenMetaData.versions) {
+                    dest.add(version);
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
new file mode 100644
index 0000000..f952dcc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.resolver;
+
+import java.net.URI;
+
+public interface PatternBasedResolver {
+    /**
+     * Must be called before any patterns are added.
+     */
+    void setM2compatible(boolean b);
+
+    void addArtifactLocation(URI baseUri, String pattern);
+
+    void addDescriptorLocation(URI baseUri, String pattern);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.java
new file mode 100644
index 0000000..6a03385
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourcePattern.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.api.internal.artifacts.repositories.resolver;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ExternalResourceName;
+
+public interface ResourcePattern {
+    /**
+     * Returns this pattern converted to a String.
+     */
+    String getPattern();
+
+    /**
+     * Returns the path for the given artifact.
+     */
+    ExternalResourceName getLocation(ModuleComponentArtifactMetaData artifact);
+
+    /**
+     * Returns the pattern which can be used to search for versions of the given artifact.
+     * The returned pattern should include at least one [revision] placeholder.
+     */
+    ExternalResourceName toVersionListPattern(ModuleIdentifier module, IvyArtifactName artifact);
+
+    /**
+     * Returns the path for the given module.
+     */
+    ExternalResourceName toModulePath(ModuleIdentifier moduleIdentifier);
+
+    /**
+     * Returns the path for the given component.
+     */
+    ExternalResourceName toModuleVersionPath(ModuleComponentIdentifier componentIdentifier);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
new file mode 100644
index 0000000..7169a57
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionLister.java
@@ -0,0 +1,149 @@
+/*
+ * 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.resolver;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+import org.gradle.internal.resource.ExternalResourceName;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ResourceVersionLister implements VersionLister {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceVersionLister.class);
+    private static final String REVISION_TOKEN = IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY);
+    public static final int REV_TOKEN_LENGTH = REVISION_TOKEN.length();
+
+    private final ExternalResourceRepository repository;
+    private final String fileSeparator = "/";
+
+    public ResourceVersionLister(ExternalResourceRepository repository) {
+        this.repository = repository;
+    }
+
+    public VersionPatternVisitor newVisitor(final ModuleIdentifier module, final Collection<String> dest, final ResourceAwareResolveResult result) {
+        return new VersionPatternVisitor() {
+            final Set<ExternalResourceName> directories = new HashSet<ExternalResourceName>();
+
+            public void visit(ResourcePattern pattern, IvyArtifactName artifact) throws ResourceException {
+                ExternalResourceName versionListPattern = pattern.toVersionListPattern(module, artifact);
+                LOGGER.debug("Listing all in {}", versionListPattern);
+                try {
+                    List<String> versionStrings = listRevisionToken(versionListPattern);
+                    for (String versionString : versionStrings) {
+                        dest.add(versionString);
+                    }
+                } catch (Exception e) {
+                    throw ResourceException.failure(versionListPattern.getUri(), String.format("Could not list versions using %s.", pattern), e);
+                }
+            }
+
+            // lists all the values a revision token listed by a given url lister
+            private List<String> listRevisionToken(ExternalResourceName versionListPattern) {
+                String pattern = versionListPattern.getPath();
+                if (!pattern.contains(REVISION_TOKEN)) {
+                    LOGGER.debug("revision token not defined in pattern {}.", pattern);
+                    return Collections.emptyList();
+                }
+                String prefix = pattern.substring(0, pattern.indexOf(REVISION_TOKEN));
+                if (revisionMatchesDirectoryName(pattern)) {
+                    ExternalResourceName parent = versionListPattern.getRoot().resolve(prefix);
+                    return listAll(parent);
+                } else {
+                    int parentFolderSlashIndex = prefix.lastIndexOf(fileSeparator);
+                    String revisionParentFolder = parentFolderSlashIndex == -1 ? "" : prefix.substring(0, parentFolderSlashIndex + 1);
+                    ExternalResourceName parent = versionListPattern.getRoot().resolve(revisionParentFolder);
+                    LOGGER.debug("using {} to list all in {} ", repository, revisionParentFolder);
+                    if (!directories.add(parent)) {
+                        return Collections.emptyList();
+                    }
+                    result.attempted(parent);
+                    List<String> all = repository.list(parent.getUri());
+                    if (all == null) {
+                        return Collections.emptyList();
+                    }
+                    LOGGER.debug("found {} urls", all.size());
+                    Pattern regexPattern = createRegexPattern(pattern, parentFolderSlashIndex);
+                    List<String> ret = filterMatchedValues(all, regexPattern);
+                    LOGGER.debug("{} matched {}" + pattern, ret.size(), pattern);
+                    return ret;
+                }
+            }
+
+            private List<String> filterMatchedValues(List<String> all, final Pattern p) {
+                List<String> ret = new ArrayList<String>(all.size());
+                for (String path : all) {
+                    Matcher m = p.matcher(path);
+                    if (m.matches()) {
+                        String value = m.group(1);
+                        ret.add(value);
+                    }
+                }
+                return ret;
+            }
+
+            private Pattern createRegexPattern(String pattern, int prefixLastSlashIndex) {
+                int endNameIndex = pattern.indexOf(fileSeparator, prefixLastSlashIndex + 1);
+                String namePattern;
+                if (endNameIndex != -1) {
+                    namePattern = pattern.substring(prefixLastSlashIndex + 1, endNameIndex);
+                } else {
+                    namePattern = pattern.substring(prefixLastSlashIndex + 1);
+                }
+                namePattern = namePattern.replaceAll("\\.", "\\\\.");
+
+                String acceptNamePattern = namePattern.replaceAll("\\[revision\\]", "(.+)");
+                return Pattern.compile(acceptNamePattern);
+            }
+
+            private boolean revisionMatchesDirectoryName(String pattern) {
+                int startToken = pattern.indexOf(REVISION_TOKEN);
+                if (startToken > 0 && !pattern.substring(startToken - 1, startToken).equals(fileSeparator)) {
+                    // previous character is not a separator
+                    return false;
+                }
+                int endToken = startToken + REV_TOKEN_LENGTH;
+                if (endToken < pattern.length() && !pattern.substring(endToken, endToken + 1).equals(fileSeparator)) {
+                    // next character is not a separator
+                    return false;
+                }
+                return true;
+            }
+
+            private List<String> listAll(ExternalResourceName parent)  {
+                if (!directories.add(parent)) {
+                    return Collections.emptyList();
+                }
+                LOGGER.debug("using {} to list all in {}", repository, parent);
+                result.attempted(parent.toString());
+                List<String> paths = repository.list(parent.getUri());
+                if (paths == null) {
+                    return Collections.emptyList();
+                }
+                LOGGER.debug("found {} resources", paths.size());
+                return paths;
+            }
+        };
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
new file mode 100644
index 0000000..4aa400d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionLister.java
@@ -0,0 +1,32 @@
+/*
+ * 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.resolver;
+
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
+
+import java.util.Collection;
+
+public interface VersionLister {
+    /**
+     * Creates a visitor for the given module. Call {@link VersionPatternVisitor#visit(ResourcePattern, org.gradle.internal.component.model.IvyArtifactName)} to search for versions.
+     *
+     * @param dest collection to add versions to
+     * @param result used to add candidate locations.
+     */
+    VersionPatternVisitor newVisitor(ModuleIdentifier module, Collection<String> dest, ResourceAwareResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionPatternVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionPatternVisitor.java
new file mode 100644
index 0000000..dca0eea
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/VersionPatternVisitor.java
@@ -0,0 +1,31 @@
+/*
+ * 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.resolver;
+
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.resource.ResourceException;
+
+public interface VersionPatternVisitor {
+    /**
+     * <p>Adds those versions available for the given pattern.</p>
+     *
+     * If no versions are listed with the given pattern, then no versions are added.
+     * 
+     * @throws ResourceException If information for versions cannot be loaded.
+     */
+    void visit(ResourcePattern pattern, IvyArtifactName artifact) throws ResourceException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java
new file mode 100644
index 0000000..48120ee
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransport.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.transport;
+
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+
+public interface RepositoryTransport {
+    ExternalResourceRepository getRepository();
+
+    CacheAwareExternalResourceAccessor getResourceAccessor();
+
+    boolean isLocal();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
new file mode 100644
index 0000000..878fc16
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactory.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories.transport;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.repositories.PasswordCredentials;
+import org.gradle.api.credentials.Credentials;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.resource.cached.CachedExternalResourceIndex;
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.resource.connector.ResourceConnectorSpecification;
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+import org.gradle.internal.resource.transport.ResourceConnectorRepositoryTransport;
+import org.gradle.internal.resource.transport.file.FileTransport;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class RepositoryTransportFactory {
+    private final List<ResourceConnectorFactory> registeredProtocols = Lists.newArrayList();
+
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final CachedExternalResourceIndex<String> cachedExternalResourceIndex;
+    private final ProgressLoggerFactory progressLoggerFactory;
+    private final BuildCommencedTimeProvider timeProvider;
+    private final CacheLockingManager cacheLockingManager;
+
+    public RepositoryTransportFactory(Collection<ResourceConnectorFactory> resourceConnectorFactory,
+                                      ProgressLoggerFactory progressLoggerFactory,
+                                      TemporaryFileProvider temporaryFileProvider,
+                                      CachedExternalResourceIndex<String> cachedExternalResourceIndex,
+                                      BuildCommencedTimeProvider timeProvider,
+                                      CacheLockingManager cacheLockingManager) {
+        this.progressLoggerFactory = progressLoggerFactory;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.cachedExternalResourceIndex = cachedExternalResourceIndex;
+        this.timeProvider = timeProvider;
+        this.cacheLockingManager = cacheLockingManager;
+
+        for (ResourceConnectorFactory connectorFactory : resourceConnectorFactory) {
+            register(connectorFactory);
+        }
+    }
+
+    public void register(ResourceConnectorFactory resourceConnectorFactory) {
+        registeredProtocols.add(resourceConnectorFactory);
+    }
+
+    public Set<String> getRegisteredProtocols() {
+        Set<String> validSchemes = Sets.newLinkedHashSet();
+        validSchemes.add("file");
+        for (ResourceConnectorFactory registeredProtocol : registeredProtocols) {
+            validSchemes.addAll(registeredProtocol.getSupportedProtocols());
+        }
+        return validSchemes;
+    }
+
+    public RepositoryTransport createTransport(String scheme, String name, Credentials credentials) {
+        return createTransport(Collections.singleton(scheme), name, credentials);
+    }
+
+    /**
+     * TODO René: why do we have two different PasswordCredentials
+     * */
+    private org.gradle.internal.resource.PasswordCredentials convertPasswordCredentials(Credentials credentials) {
+        if (!(credentials instanceof PasswordCredentials)) {
+            throw new IllegalArgumentException(String.format("Credentials must be an instance of: %s", PasswordCredentials.class.getCanonicalName()));
+        }
+        PasswordCredentials passwordCredentials = (PasswordCredentials) credentials;
+        return new org.gradle.internal.resource.PasswordCredentials(passwordCredentials.getUsername(), passwordCredentials.getPassword());
+    }
+
+    public RepositoryTransport createTransport(Set<String> schemes, String name, Credentials credentials) {
+        validateSchemes(schemes);
+
+        // File resources are handled slightly differently at present.
+        if (Collections.singleton("file").containsAll(schemes)) {
+            return new FileTransport(name);
+        }
+        ResourceConnectorSpecification connectionDetails = new DefaultResourceConnectorSpecification(credentials);
+        ExternalResourceConnector resourceConnector = findConnectorFactory(schemes).createResourceConnector(connectionDetails);
+        return new ResourceConnectorRepositoryTransport(name, progressLoggerFactory, temporaryFileProvider, cachedExternalResourceIndex, timeProvider, cacheLockingManager, resourceConnector);
+    }
+
+    private void validateSchemes(Set<String> schemes) {
+        Set<String> validSchemes = getRegisteredProtocols();
+        for (String scheme : schemes) {
+            if (!validSchemes.contains(scheme)) {
+                throw new InvalidUserDataException(String.format("Not a supported repository protocol '%s': valid protocols are %s", scheme, validSchemes));
+            }
+        }
+    }
+
+    private ResourceConnectorFactory findConnectorFactory(Set<String> schemes) {
+        for (ResourceConnectorFactory protocolRegistration : registeredProtocols) {
+            if (protocolRegistration.getSupportedProtocols().containsAll(schemes)) {
+                return protocolRegistration;
+            }
+        }
+        throw new InvalidUserDataException("You cannot mix different URL schemes for a single repository. Please declare separate repositories.");
+    }
+
+    private class DefaultResourceConnectorSpecification implements ResourceConnectorSpecification {
+        private final Credentials credentials;
+
+        private DefaultResourceConnectorSpecification(Credentials credentials) {
+            this.credentials = credentials;
+        }
+
+        @Override
+        public <T> T getCredentials(Class<T> type) {
+            if(credentials == null){
+                return null;
+            }
+            if (org.gradle.internal.resource.PasswordCredentials.class.isAssignableFrom(type)) {
+                return type.cast(convertPasswordCredentials(credentials));
+            }
+            if (type.isAssignableFrom(credentials.getClass())) {
+                return type.cast(credentials);
+            } else {
+                throw new IllegalArgumentException(String.format("Credentials must be an instance of '%s'.", type.getCanonicalName()));
+            }
+        }
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/AbstractDependencyResult.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResult.java
new file mode 100644
index 0000000..70c45a8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.artifacts.result.ArtifactResolutionResult;
+import org.gradle.api.artifacts.result.ComponentArtifactsResult;
+import org.gradle.api.artifacts.result.ComponentResult;
+
+import java.util.Set;
+
+public class DefaultArtifactResolutionResult implements ArtifactResolutionResult {
+    private final Set<ComponentResult> componentResults;
+
+    public DefaultArtifactResolutionResult(Set<ComponentResult> componentResults) {
+        this.componentResults = componentResults;
+    }
+
+    public Set<ComponentResult> getComponents() {
+        return componentResults;
+    }
+
+    public Set<ComponentArtifactsResult> getResolvedComponents() {
+        Set<ComponentArtifactsResult> resolvedComponentResults = Sets.newLinkedHashSet();
+        for (ComponentResult componentResult : componentResults) {
+            if (componentResult instanceof ComponentArtifactsResult) {
+                resolvedComponentResults.add((ComponentArtifactsResult) componentResult);
+            }
+        }
+        return resolvedComponentResults;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResult.java
new file mode 100644
index 0000000..b554aaf
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentArtifactsResult;
+import org.gradle.api.component.Artifact;
+import org.gradle.api.artifacts.result.ArtifactResult;
+
+import java.util.Set;
+
+public class DefaultComponentArtifactsResult implements ComponentArtifactsResult {
+    private final ComponentIdentifier componentIdentifier;
+    private final Set<ArtifactResult> artifactResults = Sets.newHashSet();
+
+    public DefaultComponentArtifactsResult(ComponentIdentifier componentIdentifier) {
+        this.componentIdentifier = componentIdentifier;
+    }
+
+    public ComponentIdentifier getId() {
+        return componentIdentifier;
+    }
+
+    public Set<ArtifactResult> getArtifacts(Class<? extends Artifact> type) {
+        Set<ArtifactResult> matching = Sets.newLinkedHashSet();
+        for (ArtifactResult artifactResult : artifactResults) {
+            if (type.isAssignableFrom(artifactResult.getType())) {
+                matching.add(artifactResult);
+            }
+        }
+        return matching;
+    }
+
+    public void addArtifact(ArtifactResult artifact) {
+        artifactResults.add(artifact);
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedArtifactResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedArtifactResult.java
new file mode 100644
index 0000000..e727760
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedArtifactResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result;
+
+import org.gradle.api.component.Artifact;
+import org.gradle.api.artifacts.result.ResolvedArtifactResult;
+
+import java.io.File;
+
+public class DefaultResolvedArtifactResult implements ResolvedArtifactResult {
+    private final Class<? extends Artifact> type;
+    private final File file;
+
+    public DefaultResolvedArtifactResult(Class<? extends Artifact> type, File file) {
+        this.type = type;
+        this.file = file;
+    }
+
+    public Class<? extends Artifact> getType() {
+        return type;
+    }
+
+    public File getFile() {
+        return file;
+    }
+}
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
diff --git a/subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
similarity index 100%
rename from subprojects/core-impl/src/main/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
rename to subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedDependencyResult.java
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedArtifactResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedArtifactResult.java
new file mode 100644
index 0000000..0b64406
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedArtifactResult.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result;
+
+import org.gradle.api.component.Artifact;
+import org.gradle.api.artifacts.result.UnresolvedArtifactResult;
+
+public class DefaultUnresolvedArtifactResult implements UnresolvedArtifactResult {
+    private final Class<? extends Artifact> type;
+    private final Throwable failure;
+
+    public DefaultUnresolvedArtifactResult(Class<? extends Artifact> type, Throwable failure) {
+        this.type = type;
+        this.failure = failure;
+    }
+
+    public Class<? extends Artifact> getType() {
+        return type;
+    }
+
+    public Throwable getFailure() {
+        return failure;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedComponentResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedComponentResult.java
new file mode 100644
index 0000000..3c0e8ab
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedComponentResult.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.UnresolvedComponentResult;
+
+public class DefaultUnresolvedComponentResult implements UnresolvedComponentResult {
+    private final ComponentIdentifier componentIdentifier;
+    private final Throwable failure;
+
+    public DefaultUnresolvedComponentResult(ComponentIdentifier componentIdentifier, Throwable failure) {
+        this.componentIdentifier = componentIdentifier;
+        this.failure = failure;
+    }
+
+    public ComponentIdentifier getId() {
+        return componentIdentifier;
+    }
+
+    public Throwable getFailure() {
+        return failure;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.java
new file mode 100644
index 0000000..d530817
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultUnresolvedDependencyResult.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.api.internal.artifacts.result;
+
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.artifacts.result.ResolvedComponentResult;
+import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public class DefaultUnresolvedDependencyResult extends AbstractDependencyResult implements UnresolvedDependencyResult {
+    private final ComponentSelectionReason reason;
+    private final ModuleVersionResolveException failure;
+
+    public DefaultUnresolvedDependencyResult(ComponentSelector requested, ComponentSelectionReason reason,
+                                             ResolvedComponentResult from, ModuleVersionResolveException failure) {
+        super(requested, from);
+        this.reason = reason;
+        this.failure = failure;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        return failure;
+    }
+
+    public ComponentSelector getAttempted() {
+        return failure.getSelector();
+    }
+
+    public ComponentSelectionReason getAttemptedReason() {
+        return reason;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s -> %s - %s", getRequested(), getAttempted(), failure.getMessage());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/jvm/AbstractArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/jvm/AbstractArtifact.java
new file mode 100644
index 0000000..8e64d76
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/jvm/AbstractArtifact.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result.jvm;
+
+import org.gradle.api.component.Artifact;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+
+public abstract class AbstractArtifact implements Artifact {
+    private final File file;
+    private final Throwable failure;
+
+    protected AbstractArtifact(File file) {
+        this.file = file;
+        failure = null;
+    }
+
+    protected AbstractArtifact(Throwable failure) {
+        file = null;
+        this.failure = failure;
+    }
+
+    public File getFile() {
+        assertNoFailure();
+        return file;
+    }
+
+    public Throwable getFailure() {
+        return failure;
+    }
+
+    private void assertNoFailure() {
+        if (failure != null) {
+            throw UncheckedException.throwAsUncheckedException(failure);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java
new file mode 100644
index 0000000..7e87a80
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/filestore/ivy/ArtifactIdentifierFileStore.java
@@ -0,0 +1,44 @@
+/*
+ * 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.ivy;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.resource.local.GroupedAndNamedUniqueFileStore;
+import org.gradle.internal.resource.local.PathKeyFileStore;
+
+public class ArtifactIdentifierFileStore extends GroupedAndNamedUniqueFileStore<ModuleComponentArtifactMetaData> {
+
+    private static final String GROUP_PATTERN = "[organisation]/[module](/[branch])/[revision]";
+    private static final String NAME_PATTERN = "[artifact]-[revision](-[classifier])(.[ext])";
+
+    public ArtifactIdentifierFileStore(PathKeyFileStore pathKeyFileStore, TemporaryFileProvider temporaryFileProvider) {
+        super(pathKeyFileStore, temporaryFileProvider, toTransformer(GROUP_PATTERN), toTransformer(NAME_PATTERN));
+    }
+
+    private static Transformer<String, ModuleComponentArtifactMetaData> toTransformer(final String pattern) {
+        final ResourcePattern resourcePattern = new IvyResourcePattern(pattern);
+        return new Transformer<String, ModuleComponentArtifactMetaData>() {
+             public String transform(ModuleComponentArtifactMetaData artifact) {
+                 return resourcePattern.getLocation(artifact).getPath();
+             }
+         };
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
new file mode 100644
index 0000000..130f70b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ClientModuleNotationParserFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations;
+
+import org.gradle.api.artifacts.ClientModule;
+import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule;
+import org.gradle.internal.Factory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+
+public class ClientModuleNotationParserFactory implements Factory<NotationParser<Object, ClientModule>> {
+
+    private final Instantiator instantiator;
+
+    public ClientModuleNotationParserFactory(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    public NotationParser<Object, ClientModule> create() {
+        return NotationParserBuilder.toType(ClientModule.class)
+                .fromCharSequence(new DependencyStringNotationConverter<DefaultClientModule>(instantiator, DefaultClientModule.class))
+                .converter(new DependencyMapNotationConverter<DefaultClientModule>(instantiator, DefaultClientModule.class))
+                .toComposite();
+
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyClassPathNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyClassPathNotationConverter.java
new file mode 100755
index 0000000..ee23175
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyClassPathNotationConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.artifacts.SelfResolvingDependency;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency;
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.NotationConvertResult;
+import org.gradle.internal.typeconversion.NotationConverter;
+import org.gradle.internal.typeconversion.TypeConversionException;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class DependencyClassPathNotationConverter implements NotationConverter<DependencyFactory.ClassPathNotation, SelfResolvingDependency> {
+
+    private final ClassPathRegistry classPathRegistry;
+    private final Instantiator instantiator;
+    private final FileResolver fileResolver;
+    private final Map<DependencyFactory.ClassPathNotation, SelfResolvingDependency> internCache = Maps.newEnumMap(DependencyFactory.ClassPathNotation.class);
+    private final Lock internCacheWriteLock = new ReentrantLock();
+
+    public DependencyClassPathNotationConverter(Instantiator instantiator, ClassPathRegistry classPathRegistry,
+                                                FileResolver fileResolver) {
+        this.instantiator = instantiator;
+        this.classPathRegistry = classPathRegistry;
+        this.fileResolver = fileResolver;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("ClassPathNotation").example("gradleApi()");
+    }
+
+    public void convert(DependencyFactory.ClassPathNotation notation, NotationConvertResult<? super SelfResolvingDependency> result) throws TypeConversionException {
+        SelfResolvingDependency dependency = internCache.get(notation);
+        if (dependency == null) {
+            internCacheWriteLock.lock();
+            try {
+                dependency = maybeCreateUnderLock(notation);
+            } finally {
+                internCacheWriteLock.unlock();
+            }
+        }
+
+        result.converted(dependency);
+    }
+
+    private SelfResolvingDependency maybeCreateUnderLock(DependencyFactory.ClassPathNotation notation) {
+        SelfResolvingDependency dependency = internCache.get(notation);
+        if (dependency == null) {
+            Collection<File> classpath = classPathRegistry.getClassPath(notation.name()).getAsFiles();
+            FileCollection files = fileResolver.resolveFiles(classpath);
+            dependency = instantiator.newInstance(DefaultSelfResolvingDependency.class, files);
+            internCache.put(notation, dependency);
+        }
+        return dependency;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyFilesNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyFilesNotationConverter.java
new file mode 100644
index 0000000..1349d21
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyFilesNotationConverter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations;
+
+import org.gradle.api.artifacts.SelfResolvingDependency;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.NotationConvertResult;
+import org.gradle.internal.typeconversion.NotationConverter;
+import org.gradle.internal.typeconversion.TypeConversionException;
+
+public class DependencyFilesNotationConverter implements NotationConverter<FileCollection, SelfResolvingDependency> {
+    private final Instantiator instantiator;
+
+    public DependencyFilesNotationConverter(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("FileCollections").example("files('some.jar', 'someOther.jar')");
+    }
+
+    public void convert(FileCollection notation, NotationConvertResult<? super SelfResolvingDependency> result) throws TypeConversionException {
+        result.converted(instantiator.newInstance(DefaultSelfResolvingDependency.class, notation));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyMapNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyMapNotationConverter.java
new file mode 100644
index 0000000..4066f34
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyMapNotationConverter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.notations;
+
+import org.gradle.api.artifacts.ExternalDependency;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ModuleFactoryHelper;
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.MapKey;
+import org.gradle.internal.typeconversion.MapNotationConverter;
+
+public class DependencyMapNotationConverter<T extends ExternalDependency> extends MapNotationConverter<T> {
+
+    private final Instantiator instantiator;
+    private final Class<T> resultingType;
+
+    public DependencyMapNotationConverter(Instantiator instantiator, Class<T> resultingType) {
+        this.instantiator = instantiator;
+        this.resultingType = resultingType;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("Maps").example("[group: 'org.gradle', name: 'gradle-core', version: '1.0']");
+    }
+
+    protected T parseMap(@MapKey("group") @Optional String group,
+                         @MapKey("name") @Optional String name,
+                         @MapKey("version") @Optional String version,
+                         @MapKey("configuration") @Optional String configuration,
+                         @MapKey("ext") @Optional String ext,
+                         @MapKey("classifier") @Optional String classifier) {
+        T dependency = instantiator.newInstance(resultingType, group, name, version, configuration);
+        ModuleFactoryHelper.addExplicitArtifactsIfDefined(dependency, ext, classifier);
+        return dependency;
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyNotationParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyNotationParser.java
new file mode 100644
index 0000000..10ba858
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyNotationParser.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.notations;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
+import org.gradle.api.internal.file.FileLookup;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+
+public class DependencyNotationParser {
+    public static NotationParser<Object, Dependency> parser(Instantiator instantiator, DefaultProjectDependencyFactory dependencyFactory, ClassPathRegistry classPathRegistry, FileLookup fileLookup) {
+        return NotationParserBuilder
+                .toType(Dependency.class)
+                .fromCharSequence(new DependencyStringNotationConverter<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class))
+                .converter(new DependencyMapNotationConverter<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class))
+                .fromType(FileCollection.class, new DependencyFilesNotationConverter(instantiator))
+                .fromType(Project.class, new DependencyProjectNotationConverter(dependencyFactory))
+                .fromType(DependencyFactory.ClassPathNotation.class, new DependencyClassPathNotationConverter(instantiator, classPathRegistry, fileLookup.getFileResolver()))
+                .invalidNotationMessage("Comprehensive documentation on dependency notations is available in DSL reference for DependencyHandler type.")
+                .toComposite();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyProjectNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyProjectNotationConverter.java
new file mode 100644
index 0000000..e05da45
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyProjectNotationConverter.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.notations;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.NotationConvertResult;
+import org.gradle.internal.typeconversion.NotationConverter;
+import org.gradle.internal.typeconversion.TypeConversionException;
+
+public class DependencyProjectNotationConverter implements NotationConverter<Project, ProjectDependency> {
+
+    private final DefaultProjectDependencyFactory factory;
+
+    public DependencyProjectNotationConverter(DefaultProjectDependencyFactory factory) {
+        this.factory = factory;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("Projects").example("project(':some:project:path')");
+    }
+
+    public void convert(Project notation, NotationConvertResult<? super ProjectDependency> result) throws TypeConversionException {
+        result.converted(factory.create(notation));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyStringNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyStringNotationConverter.java
new file mode 100644
index 0000000..6c6e27a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyStringNotationConverter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.notations;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ClientModule;
+import org.gradle.api.artifacts.ExternalDependency;
+import org.gradle.api.internal.artifacts.dsl.ParsedModuleStringNotation;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ModuleFactoryHelper;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.NotationConvertResult;
+import org.gradle.internal.typeconversion.NotationConverter;
+import org.gradle.internal.typeconversion.TypeConversionException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DependencyStringNotationConverter<T extends ExternalDependency> implements NotationConverter<String, T> {
+    private final Instantiator instantiator;
+    private final Class<T> wantedType;
+
+    public DependencyStringNotationConverter(Instantiator instantiator, Class<T> wantedType) {
+        this.instantiator = instantiator;
+        this.wantedType = wantedType;
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("String or CharSequence values").example("'org.gradle:gradle-core:1.0'");
+    }
+
+    public void convert(String notation, NotationConvertResult<? super T> result) throws TypeConversionException {
+        result.converted(createDependencyFromString(notation));
+    }
+
+    public static final Pattern EXTENSION_SPLITTER = Pattern.compile("^(.+)\\@([^:]+$)");
+
+    private T createDependencyFromString(String notation) {
+
+        ParsedModuleStringNotation parsedNotation = splitModuleFromExtension(notation);
+        T moduleDependency = instantiator.newInstance(wantedType,
+                parsedNotation.getGroup(), parsedNotation.getName(), parsedNotation.getVersion());
+        ModuleFactoryHelper.addExplicitArtifactsIfDefined(moduleDependency, parsedNotation.getArtifactType(), parsedNotation.getClassifier());
+
+        return moduleDependency;
+    }
+
+    private ParsedModuleStringNotation splitModuleFromExtension(String notation) {
+        Matcher matcher = EXTENSION_SPLITTER.matcher(notation);
+        boolean hasArtifactType = matcher.matches();
+        if (hasArtifactType && !ClientModule.class.isAssignableFrom(wantedType)) {
+            if (matcher.groupCount() != 2) {
+                throw new InvalidUserDataException("The dependency notation " + notation + " is invalid");
+            }
+            return new ParsedModuleStringNotation(matcher.group(1), matcher.group(2));
+        }
+        return new ParsedModuleStringNotation(notation, null);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ModuleIdentiferNotationConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ModuleIdentiferNotationConverter.java
new file mode 100644
index 0000000..183d68d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ModuleIdentiferNotationConverter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.notations;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.artifacts.ModuleIdentifier;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.TypedNotationConverter;
+import org.gradle.internal.typeconversion.UnsupportedNotationException;
+
+import java.util.List;
+
+import static org.gradle.api.internal.artifacts.DefaultModuleIdentifier.newId;
+
+public class ModuleIdentiferNotationConverter extends TypedNotationConverter<String, ModuleIdentifier> {
+    private final static List<Character> INVALID_SPEC_CHARS = Lists.newArrayList('*', '[', ']', '(', ')', ',', '+');
+
+    public ModuleIdentiferNotationConverter() {
+        super(String.class);
+    }
+
+    /**
+     * Empty String for either group or module name is not allowed.
+     */
+    protected ModuleIdentifier parseType(String notation) {
+        assert notation != null;
+        String[] split = notation.split(":");
+        if (split.length != 2) {
+            throw new UnsupportedNotationException(notation);
+        }
+        String group = split[0].trim();
+        String name = split[1].trim();
+        if (group.length() == 0 || name.length() == 0) {
+            throw new UnsupportedNotationException(notation);
+        }
+
+        for (char c : INVALID_SPEC_CHARS) {
+            if (group.indexOf(c) != -1 || name.indexOf(c) != -1) {
+                throw new UnsupportedNotationException(notation);
+            }
+        }
+
+        return newId(group, name);
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("String describing the module in 'group:name' format").example("'org.gradle:gradle-core'.");
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ProjectDependencyFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ProjectDependencyFactory.java
new file mode 100644
index 0000000..ca6ed5d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/ProjectDependencyFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations;
+
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.MapKey;
+import org.gradle.internal.typeconversion.MapNotationConverter;
+import org.gradle.internal.typeconversion.NotationParserBuilder;
+
+import java.util.Map;
+
+public class ProjectDependencyFactory {
+    private final DefaultProjectDependencyFactory factory;
+
+    public ProjectDependencyFactory(DefaultProjectDependencyFactory factory) {
+        this.factory = factory;
+    }
+
+    public ProjectDependency createFromMap(ProjectFinder projectFinder, Map<? extends String, ?> map) {
+        return NotationParserBuilder.toType(ProjectDependency.class)
+                .converter(new ProjectDependencyMapNotationConverter(projectFinder, factory)).toComposite().parseNotation(map);
+    }
+
+    static class ProjectDependencyMapNotationConverter extends MapNotationConverter<ProjectDependency> {
+
+        private final ProjectFinder projectFinder;
+        private final DefaultProjectDependencyFactory factory;
+
+        public ProjectDependencyMapNotationConverter(ProjectFinder projectFinder, DefaultProjectDependencyFactory factory) {
+            this.projectFinder = projectFinder;
+            this.factory = factory;
+        }
+
+        protected ProjectDependency parseMap(@MapKey("path") String path, @Optional @MapKey("configuration") String configuration) {
+            return factory.create(projectFinder.getProject(path), configuration);
+        }
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Map with mandatory 'path' and optional 'configuration' key").example("[path: ':someProj', configuration: 'someConf']");
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaData.java
new file mode 100644
index 0000000..02ee441
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaData.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.internal.component.external.model;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.internal.component.model.AbstractModuleDescriptorBackedMetaData;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.component.model.ConfigurationMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+abstract class AbstractModuleComponentResolveMetaData extends AbstractModuleDescriptorBackedMetaData implements MutableModuleComponentResolveMetaData {
+    private Set<ModuleComponentArtifactMetaData> artifacts;
+    private Multimap<String, ModuleComponentArtifactMetaData> artifactsByConfig;
+
+    public AbstractModuleComponentResolveMetaData(ModuleDescriptor moduleDescriptor) {
+        this(moduleVersionIdentifier(moduleDescriptor), moduleDescriptor, moduleComponentIdentifier(moduleDescriptor));
+    }
+    
+    private static ModuleVersionIdentifier moduleVersionIdentifier(ModuleDescriptor descriptor) {
+        return DefaultModuleVersionIdentifier.newId(descriptor.getModuleRevisionId());
+    }
+
+    private static ModuleComponentIdentifier moduleComponentIdentifier(ModuleDescriptor descriptor) {
+        return DefaultModuleComponentIdentifier.newId(moduleVersionIdentifier(descriptor));
+    }
+
+    public AbstractModuleComponentResolveMetaData(ModuleVersionIdentifier moduleVersionIdentifier, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier) {
+        super(moduleVersionIdentifier, moduleDescriptor, componentIdentifier);
+    }
+
+    protected void copyTo(AbstractModuleComponentResolveMetaData copy) {
+        super.copyTo(copy);
+        copy.artifacts = artifacts;
+        copy.artifactsByConfig = artifactsByConfig;
+    }
+
+    public abstract AbstractModuleComponentResolveMetaData copy();
+
+    public MutableModuleComponentResolveMetaData withSource(ModuleSource source) {
+        AbstractModuleComponentResolveMetaData copy = copy();
+        copy.setModuleSource(source);
+        return copy;
+    }
+
+    @Override
+    public ModuleComponentIdentifier getComponentId() {
+        return (ModuleComponentIdentifier) super.getComponentId();
+    }
+
+    @Override
+    public void setComponentId(ModuleComponentIdentifier componentId) {
+        super.setComponentId(componentId);
+        setId(DefaultModuleVersionIdentifier.newId(componentId));
+    }
+
+    public ModuleComponentArtifactMetaData artifact(Artifact artifact) {
+        return new DefaultModuleComponentArtifactMetaData(getComponentId(), artifact);
+    }
+
+    public ModuleComponentArtifactMetaData artifact(String type, @Nullable String extension, @Nullable String classifier) {
+        Map extraAttributes = classifier == null ? Collections.emptyMap() : Collections.singletonMap("m:classifier", classifier);
+        Artifact artifact = new DefaultArtifact(getDescriptor().getModuleRevisionId(), null, getId().getName(), type, extension, extraAttributes);
+        return new DefaultModuleComponentArtifactMetaData(getComponentId(), artifact);
+    }
+
+    public Set<ModuleComponentArtifactMetaData> getArtifacts() {
+        if (artifacts == null) {
+            populateArtifactsFromDescriptor();
+        }
+        return artifacts;
+    }
+
+    public void setArtifacts(Iterable<? extends ModuleComponentArtifactMetaData> artifacts) {
+        this.artifacts = Sets.newLinkedHashSet(artifacts);
+        this.artifactsByConfig = LinkedHashMultimap.create();
+        for (String config : getDescriptor().getConfigurationsNames()) {
+            artifactsByConfig.putAll(config, artifacts);
+        }
+    }
+
+    protected Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configurationMetaData) {
+        if (artifactsByConfig == null) {
+            populateArtifactsFromDescriptor();
+        }
+        Set<ComponentArtifactMetaData> artifactMetaData = new LinkedHashSet<ComponentArtifactMetaData>();
+        for (String ancestor : configurationMetaData.getHierarchy()) {
+            artifactMetaData.addAll(artifactsByConfig.get(ancestor));
+        }
+        return artifactMetaData;
+    }
+
+    private void populateArtifactsFromDescriptor() {
+        Map<Artifact, ModuleComponentArtifactMetaData> artifactToMetaData = Maps.newLinkedHashMap();
+        for (Artifact descriptorArtifact : getDescriptor().getAllArtifacts()) {
+            ModuleComponentArtifactMetaData artifact = artifact(descriptorArtifact);
+            artifactToMetaData.put(descriptorArtifact, artifact);
+        }
+
+        artifacts = Sets.newLinkedHashSet(artifactToMetaData.values());
+
+        this.artifactsByConfig = LinkedHashMultimap.create();
+        for (String configuration : getDescriptor().getConfigurationsNames()) {
+            Artifact[] configArtifacts = getDescriptor().getArtifacts(configuration);
+            for (Artifact configArtifact : configArtifacts) {
+                artifactsByConfig.put(configuration, artifactToMetaData.get(configArtifact));
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModulePublishMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModulePublishMetaData.java
new file mode 100644
index 0000000..8f3a6b4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModulePublishMetaData.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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+import java.io.File;
+
+public interface BuildableIvyModulePublishMetaData extends IvyModulePublishMetaData {
+    void addArtifact(IvyModuleArtifactPublishMetaData artifact);
+
+    void addArtifact(Artifact artifact, File file);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaData.java
new file mode 100644
index 0000000..43453a3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaData.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+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.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static java.util.Arrays.asList;
+
+public class BuildableIvyModuleResolveMetaData extends DefaultIvyModuleResolveMetaData {
+    private final DefaultModuleDescriptor module;
+
+    public BuildableIvyModuleResolveMetaData(DefaultModuleDescriptor module) {
+        super(module);
+        this.module = module;
+    }
+
+    public void addArtifact(IvyArtifactName newArtifact, Set<String> configurations) {
+        if (configurations.isEmpty()) {
+            throw new IllegalArgumentException("Artifact should be attached to at least one configuration.");
+        }
+
+        MDArtifact unattached = new MDArtifact(module, newArtifact.getName(), newArtifact.getType(), newArtifact.getExtension(), null, newArtifact.getAttributes());
+        //Adding the artifact will replace any existing artifact
+        //This potentially leads to loss of information - the configurations of the replaced artifact are lost (see GRADLE-123)
+        //Hence we attempt to find an existing artifact and merge the information
+        Artifact[] allArtifacts = module.getAllArtifacts();
+        for (Artifact existing : allArtifacts) {
+            // Can't just compare the raw IvyArtifactName, since creating MDArtifact creates a bunch of attributes
+            if (artifactsEqual(unattached, existing)) {
+                if (!(existing instanceof MDArtifact)) {
+                    throw new IllegalArgumentException("Cannot update an existing artifact (" + existing + ") in provided module descriptor (" + module + ")"
+                            + " because the artifact is not an instance of MDArtifact." + module);
+                }
+                attachArtifact((MDArtifact) existing, configurations, module);
+                return; //there is only one matching artifact
+            }
+        }
+        attachArtifact(unattached, configurations, module);
+    }
+
+    private boolean artifactsEqual(Artifact a, Artifact b) {
+        return new DefaultIvyArtifactName(a).equals(new DefaultIvyArtifactName(b));
+    }
+
+    private static void attachArtifact(MDArtifact artifact, Set<String> configurations, DefaultModuleDescriptor target) {
+        //The existing artifact configurations will be first
+        Set<String> existingConfigurations = newLinkedHashSet(asList(artifact.getConfigurations()));
+        for (String c : configurations) {
+            if (!existingConfigurations.contains(c)) {
+                artifact.addConfiguration(c);
+                target.addArtifact(c, artifact);
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaData.java
new file mode 100644
index 0000000..27de043
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaData.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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultIvyModulePublishMetaData implements BuildableIvyModulePublishMetaData {
+    private final ModuleVersionIdentifier id;
+    private final Map<ModuleComponentArtifactIdentifier, IvyModuleArtifactPublishMetaData> artifactsById = new LinkedHashMap<ModuleComponentArtifactIdentifier, IvyModuleArtifactPublishMetaData>();
+
+    public DefaultIvyModulePublishMetaData(ModuleVersionIdentifier id) {
+        this.id = id;
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return id;
+    }
+
+    public void addArtifact(Artifact artifact, File file) {
+        DefaultIvyModuleArtifactPublishMetaData publishMetaData = new DefaultIvyModuleArtifactPublishMetaData(id, artifact, file);
+        artifactsById.put(publishMetaData.getId(), publishMetaData);
+    }
+
+    public void addArtifact(IvyModuleArtifactPublishMetaData artifact) {
+        artifactsById.put(artifact.getId(), artifact);
+    }
+
+    public Collection<IvyModuleArtifactPublishMetaData> getArtifacts() {
+        return artifactsById.values();
+    }
+
+    public IvyModuleArtifactPublishMetaData getArtifact(ModuleComponentArtifactIdentifier artifactIdentifier) {
+        return artifactsById.get(artifactIdentifier);
+    }
+
+    private static class DefaultIvyModuleArtifactPublishMetaData implements IvyModuleArtifactPublishMetaData {
+        private final DefaultModuleComponentArtifactIdentifier id;
+        private final Artifact artifact;
+        private final File file;
+
+        private DefaultIvyModuleArtifactPublishMetaData(ModuleVersionIdentifier moduleVersionIdentifier, Artifact artifact, File file) {
+            this.id = new DefaultModuleComponentArtifactIdentifier(DefaultModuleComponentIdentifier.newId(moduleVersionIdentifier), artifact);
+            this.artifact = artifact;
+            this.file = file;
+        }
+
+        public IvyArtifactName getArtifactName() {
+            return id.getName();
+        }
+
+        public Artifact toIvyArtifact() {
+            return artifact;
+        }
+
+        public ModuleComponentArtifactIdentifier getId() {
+            return id;
+        }
+
+        public File getFile() {
+            return file;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaData.java
new file mode 100644
index 0000000..78cf6fc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaData.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+import java.util.Map;
+
+public class DefaultIvyModuleResolveMetaData extends AbstractModuleComponentResolveMetaData implements IvyModuleResolveMetaData {
+    private final Map<NamespaceId, String> extraInfo;
+
+    public DefaultIvyModuleResolveMetaData(ModuleDescriptor moduleDescriptor) {
+        super(moduleDescriptor);
+        this.extraInfo = moduleDescriptor.getExtraInfo();
+    }
+
+    public DefaultIvyModuleResolveMetaData(ModuleComponentIdentifier componentIdentifier, ModuleDescriptor moduleDescriptor) {
+        super(DefaultModuleVersionIdentifier.newId(componentIdentifier), moduleDescriptor, componentIdentifier);
+        this.extraInfo = moduleDescriptor.getExtraInfo();
+    }
+
+    private DefaultIvyModuleResolveMetaData(ModuleVersionIdentifier identifier, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier) {
+        super(identifier, moduleDescriptor, componentIdentifier);
+        this.extraInfo = moduleDescriptor.getExtraInfo();
+    }
+
+    public DefaultIvyModuleResolveMetaData(DependencyMetaData dependencyMetaData) {
+        this(IvyUtil.createModuleDescriptor(dependencyMetaData.getDescriptor()));
+    }
+
+    @Override
+    public DefaultIvyModuleResolveMetaData copy() {
+        // TODO:ADAM - need to make a copy of the descriptor (it's effectively immutable at this point so it's not a problem yet)
+        DefaultIvyModuleResolveMetaData copy = new DefaultIvyModuleResolveMetaData(getId(), getDescriptor(), getComponentId());
+        copyTo(copy);
+        return copy;
+    }
+
+    public String getBranch() {
+        return getDescriptor().getModuleRevisionId().getBranch();
+    }
+
+    public Map<NamespaceId, String> getExtraInfo() {
+        return extraInfo;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaData.java
new file mode 100644
index 0000000..9897a0c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaData.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+public class DefaultMavenModuleResolveMetaData extends AbstractModuleComponentResolveMetaData implements MavenModuleResolveMetaData {
+    private static final String POM_PACKAGING = "pom";
+    private static final Collection<String> JAR_PACKAGINGS = Arrays.asList("ejb", "bundle", "maven-plugin", "eclipse-plugin");
+    private final String packaging;
+    private final boolean relocated;
+    private String snapshotTimestamp;
+
+    public DefaultMavenModuleResolveMetaData(ModuleDescriptor moduleDescriptor, String packaging, boolean relocated) {
+        super(moduleDescriptor);
+        this.packaging = packaging;
+        this.relocated = relocated;
+    }
+
+    public DefaultMavenModuleResolveMetaData(ModuleComponentIdentifier componentId, ModuleDescriptor descriptor, String packaging, boolean relocated) {
+        super(DefaultModuleVersionIdentifier.newId(componentId), descriptor, componentId);
+        this.packaging = packaging;
+        this.relocated = relocated;
+    }
+
+    private DefaultMavenModuleResolveMetaData(ModuleVersionIdentifier id, ModuleDescriptor descriptor, ModuleComponentIdentifier componentId, String packaging, boolean relocated) {
+        super(id, descriptor, componentId);
+        this.packaging = packaging;
+        this.relocated = relocated;
+    }
+
+    public DefaultMavenModuleResolveMetaData(DependencyMetaData dependencyMetaData) {
+        this(IvyUtil.createModuleDescriptor(dependencyMetaData.getDescriptor()), "jar", false);
+    }
+
+    @Override
+    public DefaultMavenModuleResolveMetaData copy() {
+        // TODO:ADAM - need to make a copy of the descriptor (it's effectively immutable at this point so it's not a problem yet)
+        DefaultMavenModuleResolveMetaData copy = new DefaultMavenModuleResolveMetaData(getId(), getDescriptor(), getComponentId(), packaging, relocated);
+        copyTo(copy);
+        copy.snapshotTimestamp = snapshotTimestamp;
+        return copy;
+    }
+
+    public String getPackaging() {
+        return packaging;
+    }
+
+    public boolean isRelocated() {
+        return relocated;
+    }
+
+    public boolean isPomPackaging() {
+        return POM_PACKAGING.equals(packaging);
+    }
+
+    public boolean isKnownJarPackaging() {
+        return "jar".equals(packaging) || JAR_PACKAGINGS.contains(packaging);
+    }
+
+    public void setSnapshotTimestamp(@Nullable String snapshotTimestamp) {
+        this.snapshotTimestamp = snapshotTimestamp;
+    }
+
+    @Nullable
+    public String getSnapshotTimestamp() {
+        return snapshotTimestamp;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifier.java
new file mode 100644
index 0000000..3241ad9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifier.java
@@ -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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class DefaultModuleComponentArtifactIdentifier implements ModuleComponentArtifactIdentifier {
+    private final ModuleComponentIdentifier componentIdentifier;
+    private final IvyArtifactName name;
+
+    public DefaultModuleComponentArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, Artifact artifact) {
+        this(componentIdentifier, artifact.getName(), artifact.getType(), artifact.getExt(), artifact.getExtraAttributes());
+    }
+
+    public DefaultModuleComponentArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, String name, String type, @Nullable String extension) {
+        this(componentIdentifier, name, type, extension, Collections.<String, String>emptyMap());
+    }
+
+    public DefaultModuleComponentArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, String name, String type, @Nullable String extension, Map<String, String> attributes) {
+        this(componentIdentifier, new DefaultIvyArtifactName(name, type, extension, attributes));
+    }
+
+    public DefaultModuleComponentArtifactIdentifier(ModuleComponentIdentifier componentIdentifier, IvyArtifactName artifact) {
+        this.componentIdentifier = componentIdentifier;
+        this.name = artifact;
+    }
+
+    public String getDisplayName() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(name.toString());
+        builder.append(" (");
+        builder.append(componentIdentifier.toString());
+        builder.append(")");
+        return builder.toString();
+    }
+
+    public IvyArtifactName getName() {
+        return name;
+    }
+
+    public ModuleComponentIdentifier getComponentIdentifier() {
+        return componentIdentifier;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    @Override
+    public int hashCode() {
+        return componentIdentifier.hashCode() ^ name.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultModuleComponentArtifactIdentifier other = (DefaultModuleComponentArtifactIdentifier) obj;
+        return other.componentIdentifier.equals(componentIdentifier)
+                && other.name.equals(name);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaData.java
new file mode 100644
index 0000000..1e2c000
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaData.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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+public class DefaultModuleComponentArtifactMetaData implements ModuleComponentArtifactMetaData {
+    private final DefaultModuleComponentArtifactIdentifier id;
+
+    public DefaultModuleComponentArtifactMetaData(ModuleComponentIdentifier componentIdentifier, Artifact artifact) {
+        this(new DefaultModuleComponentArtifactIdentifier(componentIdentifier, artifact));
+    }
+
+    public DefaultModuleComponentArtifactMetaData(ModuleComponentIdentifier componentIdentifier, IvyArtifactName artifact) {
+        this(new DefaultModuleComponentArtifactIdentifier(componentIdentifier, artifact));
+    }
+
+    public DefaultModuleComponentArtifactMetaData(ModuleComponentArtifactIdentifier moduleComponentArtifactIdentifier) {
+        this.id = (DefaultModuleComponentArtifactIdentifier) moduleComponentArtifactIdentifier;
+    }
+
+    @Override
+    public String toString() {
+        return id.toString();
+    }
+
+    public ModuleComponentArtifactIdentifier getId() {
+        return id;
+    }
+
+    public ComponentIdentifier getComponentId() {
+        return id.getComponentIdentifier();
+    }
+
+    public ArtifactIdentifier toArtifactIdentifier() {
+        return new DefaultArtifactIdentifier(id);
+    }
+
+    public Artifact toIvyArtifact() {
+        IvyArtifactName ivyArtifactName = id.getName();
+        return new DefaultArtifact(IvyUtil.createModuleRevisionId(id.getComponentIdentifier()), null, ivyArtifactName.getName(), ivyArtifactName.getType(), ivyArtifactName.getExtension(), ivyArtifactName.getAttributes());
+    }
+
+    public IvyArtifactName getName() {
+        return id.getName();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java
new file mode 100644
index 0000000..385ea34
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifier.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+
+public class DefaultModuleComponentIdentifier implements ModuleComponentIdentifier {
+    private final String group;
+    private final String module;
+    private final String version;
+
+    public DefaultModuleComponentIdentifier(String group, String module, String version) {
+        assert group != null : "group cannot be null";
+        assert module != null : "module cannot be null";
+        assert version != null : "version cannot be null";
+        this.group = group;
+        this.module = module;
+        this.version = version;
+    }
+
+    public String getDisplayName() {
+        StringBuilder builder = new StringBuilder(group.length() + module.length() + version.length() + 2);
+        builder.append(group);
+        builder.append(":");
+        builder.append(module);
+        builder.append(":");
+        builder.append(version);
+        return builder.toString();
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultModuleComponentIdentifier that = (DefaultModuleComponentIdentifier) o;
+
+        if (!group.equals(that.group)) {
+            return false;
+        }
+        if (!module.equals(that.module)) {
+            return false;
+        }
+        if (!version.equals(that.version)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = group.hashCode();
+        result = 31 * result + module.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public static ModuleComponentIdentifier newId(String group, String name, String version) {
+        return new DefaultModuleComponentIdentifier(group, name, version);
+    }
+
+    public static ModuleComponentIdentifier newId(ModuleVersionIdentifier moduleVersionIdentifier) {
+        return new DefaultModuleComponentIdentifier(moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion());
+    }
+}
+
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java
new file mode 100644
index 0000000..416630e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentSelector.java
@@ -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.internal.component.external.model;
+
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+
+public class DefaultModuleComponentSelector implements ModuleComponentSelector {
+    private final String group;
+    private final String module;
+    private final String version;
+
+    public DefaultModuleComponentSelector(String group, String module, String version) {
+        assert group != null : "group cannot be null";
+        assert module != null : "module cannot be null";
+        assert version != null : "version cannot be null";
+        this.group = group;
+        this.module = module;
+        this.version = version;
+    }
+
+    public String getDisplayName() {
+        StringBuilder builder = new StringBuilder(group.length() + module.length() + version.length() + 2);
+        builder.append(group);
+        builder.append(":");
+        builder.append(module);
+        builder.append(":");
+        builder.append(version);
+        return builder.toString();
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public boolean matchesStrictly(ComponentIdentifier identifier) {
+        assert identifier != null : "identifier cannot be null";
+
+        if(identifier instanceof ModuleComponentIdentifier) {
+            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier)identifier;
+            return module.equals(moduleComponentIdentifier.getModule())
+                    && group.equals(moduleComponentIdentifier.getGroup())
+                    && version.equals(moduleComponentIdentifier.getVersion());
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultModuleComponentSelector that = (DefaultModuleComponentSelector) o;
+
+        if (!group.equals(that.group)) {
+            return false;
+        }
+        if (!module.equals(that.module)) {
+            return false;
+        }
+        if (!version.equals(that.version)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = group.hashCode();
+        result = 31 * result + module.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public static ModuleComponentSelector newSelector(String group, String name, String version) {
+        return new DefaultModuleComponentSelector(group, name, version);
+    }
+
+    public static ModuleComponentSelector newSelector(ModuleVersionSelector selector) {
+        return new DefaultModuleComponentSelector(selector.getGroup(), selector.getName(), selector.getVersion());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleArtifactPublishMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleArtifactPublishMetaData.java
new file mode 100644
index 0000000..fcab6d0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleArtifactPublishMetaData.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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.io.File;
+
+// TODO:ADAM - This is actually Ivy artifact publish meta data
+public interface IvyModuleArtifactPublishMetaData {
+    ModuleComponentArtifactIdentifier getId();
+
+    /**
+     * Converts this artifact to an Ivy artifact. This method is here while we transition away from the Ivy types.
+     */
+    Artifact toIvyArtifact();
+
+    IvyArtifactName getArtifactName();
+
+    File getFile();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModulePublishMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModulePublishMetaData.java
new file mode 100644
index 0000000..8a64377
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModulePublishMetaData.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.internal.component.external.model;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+import java.util.Collection;
+
+public interface IvyModulePublishMetaData {
+    ModuleVersionIdentifier getId();
+
+    Collection<IvyModuleArtifactPublishMetaData> getArtifacts();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleResolveMetaData.java
new file mode 100644
index 0000000..3b2831a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/IvyModuleResolveMetaData.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
+
+import java.util.Map;
+
+/**
+ * Meta-data for a module version resolved from an Ivy repository.
+ */
+public interface IvyModuleResolveMetaData extends ModuleComponentResolveMetaData {
+    /***
+     * Returns the branch attribute for the module.
+     *
+     * @return the branch attribute for the module
+     */
+    String getBranch();
+
+    /**
+     * Returns the extra info for the module.
+     *
+     * @return the extra info for the module
+     */
+    Map<NamespaceId, String> getExtraInfo();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MavenModuleResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MavenModuleResolveMetaData.java
new file mode 100644
index 0000000..3feb130
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MavenModuleResolveMetaData.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model;
+
+/**
+ * Meta-data for a module version resolved from a Maven repository.
+ */
+public interface MavenModuleResolveMetaData extends ModuleComponentResolveMetaData {
+    String getPackaging();
+    boolean isRelocated();
+    boolean isPomPackaging();
+    boolean isKnownJarPackaging();
+    String getSnapshotTimestamp();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactIdentifier.java
new file mode 100644
index 0000000..0ac5e5d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactIdentifier.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.internal.component.external.model;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+
+/**
+ * An immutable identifier for an artifact that belongs to some module version.
+ */
+public interface ModuleComponentArtifactIdentifier extends ComponentArtifactIdentifier {
+    /**
+     * Returns the id of the component that this artifact belongs to.
+     */
+    ModuleComponentIdentifier getComponentIdentifier();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactMetaData.java
new file mode 100644
index 0000000..e1a5dff
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentArtifactMetaData.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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.artifacts.ArtifactIdentifier;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+
+/**
+ * Meta-data for an artifact that belongs to some module version.
+ */
+public interface ModuleComponentArtifactMetaData extends ComponentArtifactMetaData {
+    ModuleComponentArtifactIdentifier getId();
+
+    /**
+     * Converts this artifact to an Ivy artifact. This method is here while we transition away from the Ivy types.
+     */
+    Artifact toIvyArtifact();
+
+    /**
+     * Produces an ArtifactIdentifier for this artifact (it's not actually an identifier - just a bucket of attributes).
+     * TODO:ADAM - remove this
+     */
+    ArtifactIdentifier toArtifactIdentifier();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetaData.java
new file mode 100644
index 0000000..0bfd60b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetaData.java
@@ -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.internal.component.external.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.util.Set;
+
+/**
+ * The meta-data for a module version that is required during dependency resolution.
+ */
+public interface ModuleComponentResolveMetaData extends ComponentResolveMetaData {
+    ModuleComponentIdentifier getComponentId();
+
+    ModuleComponentResolveMetaData withSource(ModuleSource source);
+
+    Set<ModuleComponentArtifactMetaData> getArtifacts();
+
+    ModuleComponentArtifactMetaData artifact(Artifact artifact);
+
+    ModuleComponentArtifactMetaData artifact(String type, @Nullable String extension, @Nullable String classifier);
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MutableModuleComponentResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MutableModuleComponentResolveMetaData.java
new file mode 100644
index 0000000..5efc209
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/MutableModuleComponentResolveMetaData.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.internal.component.external.model;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+
+import java.util.List;
+
+public interface MutableModuleComponentResolveMetaData extends ModuleComponentResolveMetaData {
+    /**
+     * Creates a deep copy of this meta-data.
+     */
+    MutableModuleComponentResolveMetaData copy();
+
+    /**
+     * Also sets the {@link #getId()} based on the provided component id.
+     */
+    void setComponentId(ModuleComponentIdentifier componentId);
+
+    void setChanging(boolean changing);
+    void setStatus(String status);
+    void setStatusScheme(List<String> statusScheme);
+
+    void setSource(ModuleSource source);
+
+    /**
+     * Replaces the dependencies of this module version.
+     */
+    void setDependencies(Iterable<? extends DependencyMetaData> dependencies);
+
+    /**
+     * Replaces the artifacts of this module version.
+     */
+    void setArtifacts(Iterable<? extends ModuleComponentArtifactMetaData> artifacts);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifier.java
new file mode 100644
index 0000000..e80a00d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifier.java
@@ -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.internal.component.local.model;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.util.Map;
+
+public class DefaultLocalArtifactIdentifier implements ComponentArtifactIdentifier {
+    private final ComponentIdentifier componentIdentifier;
+    private final String componentDisplayName;
+    private final IvyArtifactName name;
+
+    // The componentDisplayName parameter is temporary
+    public DefaultLocalArtifactIdentifier(ComponentIdentifier componentIdentifier, String componentDisplayName, String name, String type, @Nullable String extension, Map<String, String> attributes) {
+        this.componentIdentifier = componentIdentifier;
+        this.componentDisplayName = componentDisplayName;
+        this.name = new DefaultIvyArtifactName(name, type, extension, attributes);
+    }
+
+    public String getDisplayName() {
+        return String.format("%s (%s)", name, componentDisplayName);
+    }
+
+    public IvyArtifactName getName() {
+        return name;
+    }
+
+    public ComponentIdentifier getComponentIdentifier() {
+        return componentIdentifier;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    @Override
+    public int hashCode() {
+        return componentIdentifier.hashCode() ^ name.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultLocalArtifactIdentifier other = (DefaultLocalArtifactIdentifier) obj;
+        return other.componentIdentifier.equals(componentIdentifier) && other.name.equals(name);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetaData.java
new file mode 100644
index 0000000..cd8b39b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetaData.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.component.external.model.BuildableIvyModulePublishMetaData;
+import org.gradle.internal.component.external.model.DefaultIvyModulePublishMetaData;
+import org.gradle.internal.component.model.*;
+
+import java.io.File;
+import java.util.*;
+
+public class DefaultLocalComponentMetaData implements MutableLocalComponentMetaData {
+    private final Map<ComponentArtifactIdentifier, DefaultLocalArtifactMetaData> artifactsById = new LinkedHashMap<ComponentArtifactIdentifier, DefaultLocalArtifactMetaData>();
+    private final Map<ArtifactRevisionId, DefaultLocalArtifactMetaData> artifactsByIvyId = new LinkedHashMap<ArtifactRevisionId, DefaultLocalArtifactMetaData>();
+    private final Multimap<String, DefaultLocalArtifactMetaData> artifactsByConfig = LinkedHashMultimap.create();
+    private final List<DependencyMetaData> dependencies = new ArrayList<DependencyMetaData>();
+    private final DefaultModuleDescriptor moduleDescriptor;
+    private final ModuleVersionIdentifier id;
+    private final ComponentIdentifier componentIdentifier;
+
+    public DefaultLocalComponentMetaData(DefaultModuleDescriptor moduleDescriptor, ComponentIdentifier componentIdentifier) {
+        this.moduleDescriptor = moduleDescriptor;
+        id = DefaultModuleVersionIdentifier.newId(moduleDescriptor.getModuleRevisionId());
+        this.componentIdentifier = componentIdentifier;
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return id;
+    }
+
+    public DefaultModuleDescriptor getModuleDescriptor() {
+        return moduleDescriptor;
+    }
+
+    public void addArtifact(String configuration, IvyArtifactName artifact, File file) {
+        Artifact ivyArtifact = new MDArtifact(moduleDescriptor, artifact.getName(), artifact.getType(), artifact.getExtension(), null, artifact.getAttributes());
+
+        DefaultLocalArtifactMetaData artifactMetaData = new DefaultLocalArtifactMetaData(componentIdentifier, id.toString(), ivyArtifact, file);
+        if (artifactsById.containsKey(artifactMetaData.getId())) {
+            artifactMetaData = artifactsById.get(artifactMetaData.getId());
+        } else {
+            artifactsById.put(artifactMetaData.id, artifactMetaData);
+            artifactsByIvyId.put(ivyArtifact.getId(), artifactMetaData);
+        }
+        moduleDescriptor.addArtifact(configuration, ivyArtifact);
+        artifactsByConfig.put(configuration, artifactMetaData);
+        ((MDArtifact) artifactMetaData.artifact).addConfiguration(configuration);
+    }
+
+    public void addConfiguration(String name, boolean visible, String description, String[] superConfigs, boolean transitive) {
+        moduleDescriptor.addConfiguration(new Configuration(name, visible ? Configuration.Visibility.PUBLIC : Configuration.Visibility.PRIVATE, description, superConfigs, transitive, null));
+    }
+
+    public void addDependency(DependencyMetaData dependency) {
+        dependencies.add(dependency);
+        moduleDescriptor.addDependency(dependency.getDescriptor());
+    }
+
+    public void addExcludeRule(ExcludeRule excludeRule) {
+        moduleDescriptor.addExcludeRule(excludeRule);
+    }
+
+    public Collection<? extends LocalArtifactMetaData> getArtifacts() {
+        return artifactsById.values();
+    }
+
+    public LocalArtifactMetaData getArtifact(ComponentArtifactIdentifier artifactIdentifier) {
+        return artifactsById.get(artifactIdentifier);
+    }
+
+    public ComponentResolveMetaData toResolveMetaData() {
+        return new LocalComponentResolveMetaData();
+    }
+
+    public BuildableIvyModulePublishMetaData toPublishMetaData() {
+        DefaultIvyModulePublishMetaData publishMetaData = new DefaultIvyModulePublishMetaData(id);
+        for (DefaultLocalArtifactMetaData artifact : artifactsById.values()) {
+            publishMetaData.addArtifact(artifact.artifact, artifact.file);
+        }
+        return publishMetaData;
+    }
+
+    private static class DefaultLocalArtifactMetaData implements LocalArtifactMetaData {
+        private final ComponentIdentifier componentIdentifier;
+        private final DefaultLocalArtifactIdentifier id;
+        private final Artifact artifact;
+        private final File file;
+
+        private DefaultLocalArtifactMetaData(ComponentIdentifier componentIdentifier, String displayName, Artifact artifact, File file) {
+            this.componentIdentifier = componentIdentifier;
+            Map<String, String> attrs = new HashMap<String, String>();
+            attrs.putAll(artifact.getExtraAttributes());
+            attrs.put("file", file == null ? "null" : file.getAbsolutePath());
+            this.id = new DefaultLocalArtifactIdentifier(componentIdentifier, displayName, artifact.getName(), artifact.getType(), artifact.getExt(), attrs);
+            this.artifact = artifact;
+            this.file = file;
+        }
+
+        @Override
+        public String toString() {
+            return id.toString();
+        }
+
+        public IvyArtifactName getName() {
+            return id.getName();
+        }
+
+        public ComponentIdentifier getComponentId() {
+            return componentIdentifier;
+        }
+
+        public ComponentArtifactIdentifier getId() {
+            return id;
+        }
+
+        public File getFile() {
+            return file;
+        }
+    }
+
+    private class LocalComponentResolveMetaData extends AbstractModuleDescriptorBackedMetaData {
+        public LocalComponentResolveMetaData() {
+            // TODO:ADAM - need to clone the descriptor
+            super(id, moduleDescriptor, componentIdentifier);
+        }
+
+        public ModuleComponentResolveMetaData withSource(ModuleSource source) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected List<DependencyMetaData> populateDependenciesFromDescriptor() {
+            return dependencies;
+        }
+
+        public ComponentArtifactMetaData artifact(Artifact artifact) {
+            DefaultLocalArtifactMetaData candidate = artifactsByIvyId.get(artifact.getId());
+            return candidate != null ? candidate : new DefaultLocalArtifactMetaData(componentIdentifier, id.toString(), artifact, null);
+        }
+
+        public Set<ComponentArtifactMetaData> getArtifacts() {
+            return new LinkedHashSet<ComponentArtifactMetaData>(artifactsById.values());
+        }
+
+        @Override
+        protected Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configurationMetaData) {
+            Set<ComponentArtifactMetaData> result = new LinkedHashSet<ComponentArtifactMetaData>();
+            Set<ComponentArtifactIdentifier> seen = new HashSet<ComponentArtifactIdentifier>();
+            for (String configName : configurationMetaData.getHierarchy()) {
+                for (DefaultLocalArtifactMetaData localArtifactMetaData : artifactsByConfig.get(configName)) {
+                    if (seen.add(localArtifactMetaData.id)) {
+                        result.add(localArtifactMetaData);
+                    }
+                }
+            }
+            return result;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifier.java
new file mode 100644
index 0000000..2723bba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifier.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+
+public class DefaultProjectComponentIdentifier implements ProjectComponentIdentifier {
+    private final String projectPath;
+    private final String displayName;
+
+    public DefaultProjectComponentIdentifier(String projectPath) {
+        assert projectPath != null : "project path cannot be null";
+        this.projectPath = projectPath;
+        displayName = String.format("project %s", projectPath);
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public String getProjectPath() {
+        return projectPath;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultProjectComponentIdentifier that = (DefaultProjectComponentIdentifier) o;
+
+        if (!projectPath.equals(that.projectPath)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return projectPath.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return displayName;
+    }
+
+    public static ProjectComponentIdentifier newId(String projectPath) {
+        return new DefaultProjectComponentIdentifier(projectPath);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java
new file mode 100644
index 0000000..11abc2a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.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.component.local.model;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+
+public class DefaultProjectComponentSelector implements ProjectComponentSelector {
+    private final String projectPath;
+
+    public DefaultProjectComponentSelector(String projectPath) {
+        assert projectPath != null : "project path cannot be null";
+        this.projectPath = projectPath;
+    }
+
+    public String getDisplayName() {
+        return "project ".concat(projectPath);
+    }
+
+    public String getProjectPath() {
+        return projectPath;
+    }
+
+    public boolean matchesStrictly(ComponentIdentifier identifier) {
+        assert identifier != null : "identifier cannot be null";
+
+        if(identifier instanceof ProjectComponentIdentifier) {
+            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier)identifier;
+            return projectPath.equals(projectComponentIdentifier.getProjectPath());
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultProjectComponentSelector that = (DefaultProjectComponentSelector) o;
+
+        if (!projectPath.equals(that.projectPath)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return projectPath.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public static ProjectComponentSelector newSelector(String projectPath) {
+        return new DefaultProjectComponentSelector(projectPath);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetaData.java
new file mode 100644
index 0000000..4c67776
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetaData.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+import org.gradle.internal.component.model.DefaultDependencyMetaData;
+
+public class DefaultProjectDependencyMetaData extends DefaultDependencyMetaData {
+    private final String projectPath;
+
+    public DefaultProjectDependencyMetaData(DependencyDescriptor dependencyDescriptor, String projectPath) {
+        super(dependencyDescriptor);
+        this.projectPath = projectPath;
+    }
+
+    @Override
+    public ProjectComponentSelector getSelector() {
+        return new DefaultProjectComponentSelector(projectPath);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaData.java
new file mode 100644
index 0000000..a710c88
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaData.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.internal.component.model.DependencyMetaData;
+
+/**
+ * A dependency declared using the dependency DSL.
+ */
+public interface DslOriginDependencyMetaData extends DependencyMetaData {
+    /**
+     * Remove this.
+     */
+    ModuleDependency getSource();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaDataWrapper.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaDataWrapper.java
new file mode 100644
index 0000000..f619016
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetaDataWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.internal.component.model.*;
+
+import java.util.Set;
+
+public class DslOriginDependencyMetaDataWrapper implements DslOriginDependencyMetaData {
+    private final DependencyMetaData delegate;
+    private final ModuleDependency source;
+
+    public DslOriginDependencyMetaDataWrapper(DependencyMetaData delegate, ModuleDependency source) {
+        this.delegate = delegate;
+        this.source = source;
+    }
+
+    public ModuleDependency getSource() {
+        return source;
+    }
+
+    public ModuleVersionSelector getRequested() {
+        return delegate.getRequested();
+    }
+
+    public DependencyDescriptor getDescriptor() {
+        return delegate.getDescriptor();
+    }
+
+    public boolean isChanging() {
+        return delegate.isChanging();
+    }
+
+    public boolean isTransitive() {
+        return delegate.isTransitive();
+    }
+
+    public Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData fromConfiguration, ConfigurationMetaData toConfiguration) {
+        return delegate.getArtifacts(fromConfiguration, toConfiguration);
+    }
+
+    public Set<IvyArtifactName> getArtifacts() {
+        return delegate.getArtifacts();
+    }
+
+    public DependencyMetaData withRequestedVersion(String requestedVersion) {
+        return delegate.withRequestedVersion(requestedVersion);
+    }
+
+    public DependencyMetaData withTarget(ComponentSelector target) {
+        return delegate.withTarget(target);
+    }
+
+    public DependencyMetaData withChanging() {
+        return delegate.withChanging();
+    }
+
+    public ComponentSelector getSelector() {
+        return delegate.getSelector();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalArtifactMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalArtifactMetaData.java
new file mode 100644
index 0000000..601e49a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalArtifactMetaData.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.component.local.model;
+
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+
+import java.io.File;
+
+public interface LocalArtifactMetaData extends ComponentArtifactMetaData {
+    File getFile();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetaData.java
new file mode 100644
index 0000000..d885549
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetaData.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.internal.component.local.model;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.external.model.BuildableIvyModulePublishMetaData;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+
+public interface LocalComponentMetaData {
+    ModuleVersionIdentifier getId();
+
+    /**
+     * Converts this component to resolve meta-data.
+     */
+    ComponentResolveMetaData toResolveMetaData();
+
+    /**
+     * Converts this component to publication meta-data.
+     */
+    BuildableIvyModulePublishMetaData toPublishMetaData();
+
+    @Nullable
+    LocalArtifactMetaData getArtifact(ComponentArtifactIdentifier artifactIdentifier);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/MutableLocalComponentMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/MutableLocalComponentMetaData.java
new file mode 100644
index 0000000..230d92a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/MutableLocalComponentMetaData.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.internal.component.local.model;
+
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.component.model.IvyArtifactName;
+
+import java.io.File;
+
+public interface MutableLocalComponentMetaData extends LocalComponentMetaData {
+    ModuleDescriptor getModuleDescriptor();
+
+    void addArtifact(String configuration, IvyArtifactName artifact, File file);
+
+    void addConfiguration(String name, boolean visible, String description, String[] superConfigs, boolean transitive);
+
+    void addDependency(DependencyMetaData dependency);
+
+    void addExcludeRule(ExcludeRule excludeRule);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/OpaqueComponentIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/OpaqueComponentIdentifier.java
new file mode 100644
index 0000000..be5eda8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/OpaqueComponentIdentifier.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+public class OpaqueComponentIdentifier implements ComponentIdentifier {
+    private final String displayName;
+
+    public OpaqueComponentIdentifier(String displayName) {
+        assert displayName != null : "display name cannot be null";
+        this.displayName = displayName;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        OpaqueComponentIdentifier that = (OpaqueComponentIdentifier) o;
+
+        if (!displayName.equals(that.displayName)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return displayName.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return displayName;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractModuleDescriptorBackedMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractModuleDescriptorBackedMetaData.java
new file mode 100644
index 0000000..045ff4a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractModuleDescriptorBackedMetaData.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.model;
+
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.util.CollectionUtils;
+
+import java.util.*;
+
+public abstract class AbstractModuleDescriptorBackedMetaData implements ComponentResolveMetaData {
+    private static final List<String> DEFAULT_STATUS_SCHEME = Arrays.asList("integration", "milestone", "release");
+
+    private ModuleVersionIdentifier moduleVersionIdentifier;
+    private final ModuleDescriptor moduleDescriptor;
+    private ComponentIdentifier componentIdentifier;
+    private ModuleSource moduleSource;
+    private boolean changing;
+    private String status;
+    private List<String> statusScheme = DEFAULT_STATUS_SCHEME;
+    private List<DependencyMetaData> dependencies;
+    private Map<String, DefaultConfigurationMetaData> configurations = new HashMap<String, DefaultConfigurationMetaData>();
+
+    public AbstractModuleDescriptorBackedMetaData(ModuleVersionIdentifier moduleVersionIdentifier, ModuleDescriptor moduleDescriptor, ComponentIdentifier componentIdentifier) {
+        this.moduleVersionIdentifier = moduleVersionIdentifier;
+        this.moduleDescriptor = moduleDescriptor;
+        this.componentIdentifier = componentIdentifier;
+        status = moduleDescriptor.getStatus();
+    }
+
+    protected void copyTo(AbstractModuleDescriptorBackedMetaData copy) {
+        copy.dependencies = dependencies;
+        copy.changing = changing;
+        copy.status = status;
+        copy.statusScheme = statusScheme;
+        copy.moduleSource = moduleSource;
+    }
+
+    @Override
+    public String toString() {
+        return componentIdentifier.getDisplayName();
+    }
+
+    public ModuleVersionIdentifier getId() {
+        return moduleVersionIdentifier;
+    }
+
+    public void setId(ModuleVersionIdentifier moduleVersionIdentifier) {
+        this.moduleVersionIdentifier = moduleVersionIdentifier;
+    }
+
+    public ModuleSource getSource() {
+        return moduleSource;
+    }
+
+    public void setSource(ModuleSource source) {
+        this.moduleSource = source;
+    }
+
+    public void setModuleSource(ModuleSource moduleSource) {
+        this.moduleSource = moduleSource;
+    }
+
+    public ModuleDescriptor getDescriptor() {
+        return moduleDescriptor;
+    }
+
+    public boolean isGenerated() {
+        return moduleDescriptor.isDefault();
+    }
+
+    public boolean isChanging() {
+        return changing;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public List<String> getStatusScheme() {
+        return statusScheme;
+    }
+
+    public ComponentIdentifier getComponentId() {
+        return componentIdentifier;
+    }
+
+    public void setComponentId(ComponentIdentifier componentIdentifier) {
+        this.componentIdentifier = componentIdentifier;
+    }
+
+    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;
+    }
+
+    public List<DependencyMetaData> getDependencies() {
+        if (dependencies == null) {
+            dependencies = populateDependenciesFromDescriptor();
+        }
+        return dependencies;
+    }
+
+     protected List<DependencyMetaData> populateDependenciesFromDescriptor() {
+         List<DependencyMetaData> dependencies = new ArrayList<DependencyMetaData>();
+         for (final DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies()) {
+             dependencies.add(new DefaultDependencyMetaData(dependencyDescriptor));
+         }
+         return dependencies;
+    }
+
+    public void setDependencies(Iterable<? extends DependencyMetaData> dependencies) {
+        this.dependencies = CollectionUtils.toList(dependencies);
+        for (DefaultConfigurationMetaData configuration : configurations.values()) {
+            configuration.dependencies = null;
+        }
+    }
+
+    public DefaultConfigurationMetaData getConfiguration(final String name) {
+        DefaultConfigurationMetaData configuration = configurations.get(name);
+        if (configuration == null) {
+            configuration = populateConfigurationFromDescriptor(name);
+        }
+        return configuration;
+    }
+
+    private DefaultConfigurationMetaData populateConfigurationFromDescriptor(String name) {
+        Configuration descriptorConfiguration = moduleDescriptor.getConfiguration(name);
+        if (descriptorConfiguration == null) {
+            return null;
+        }
+        Set<String> hierarchy = new LinkedHashSet<String>();
+        hierarchy.add(name);
+        for (String parent : descriptorConfiguration.getExtends()) {
+            hierarchy.addAll(getConfiguration(parent).hierarchy);
+        }
+        DefaultConfigurationMetaData configuration = new DefaultConfigurationMetaData(name, descriptorConfiguration, hierarchy);
+        configurations.put(name, configuration);
+        return configuration;
+    }
+
+    protected abstract Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configuration);
+
+    private class DefaultConfigurationMetaData implements ConfigurationMetaData {
+        private final String name;
+        private final Configuration descriptor;
+        private final Set<String> hierarchy;
+        private List<DependencyMetaData> dependencies;
+        private Set<ComponentArtifactMetaData> artifacts;
+        private LinkedHashSet<ExcludeRule> excludeRules;
+
+        private DefaultConfigurationMetaData(String name, Configuration descriptor, Set<String> hierarchy) {
+            this.name = name;
+            this.descriptor = descriptor;
+            this.hierarchy = hierarchy;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s:%s", componentIdentifier.getDisplayName(), name);
+        }
+
+        public ComponentResolveMetaData getComponent() {
+            return AbstractModuleDescriptorBackedMetaData.this;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        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 : AbstractModuleDescriptorBackedMetaData.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) {
+                populateExcludeRulesFromDescriptor();
+            }
+            return excludeRules;
+        }
+
+        private void populateExcludeRulesFromDescriptor() {
+            excludeRules = new LinkedHashSet<ExcludeRule>();
+            for (ExcludeRule excludeRule : moduleDescriptor.getAllExcludeRules()) {
+                for (String config : excludeRule.getConfigurations()) {
+                    if (hierarchy.contains(config)) {
+                        excludeRules.add(excludeRule);
+                        break;
+                    }
+                }
+            }
+        }
+
+        public Set<ComponentArtifactMetaData> getArtifacts() {
+            if (artifacts == null) {
+                artifacts = getArtifactsForConfiguration(this);
+            }
+            return artifacts;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactIdentifier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactIdentifier.java
new file mode 100644
index 0000000..140f83f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactIdentifier.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.model;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+/**
+ * An immutable identifier for an artifact that belongs to some component instance.
+ */
+public interface ComponentArtifactIdentifier {
+    /**
+     * Returns the id of the component that this artifact belongs to.
+     */
+    ComponentIdentifier getComponentIdentifier();
+
+    /**
+     * Returns some human-consumable display name for this artifact.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactMetaData.java
new file mode 100644
index 0000000..0c6a257
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactMetaData.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.model;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+/**
+ * Meta-data for an artifact that belongs to some component.
+ */
+public interface ComponentArtifactMetaData {
+    /**
+     * Returns the identifier for this artifact.
+     */
+    ComponentArtifactIdentifier getId();
+
+    /**
+     * Returns the identifier for the component that this artifact belongs to.
+     */
+    ComponentIdentifier getComponentId();
+
+    /**
+     * Returns this artifact as an Ivy artifact. This method is here to allow the artifact to be exposed in a backward-compatible way.
+     */
+    IvyArtifactName getName();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetaData.java
new file mode 100644
index 0000000..e0bd6b6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetaData.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.model;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The meta-data for a component instance that is required during dependency resolution.
+ */
+public interface ComponentResolveMetaData {
+    /**
+     * Returns the identifier for this component.
+     */
+    ComponentIdentifier getComponentId();
+
+    /**
+     * Returns the module version identifier for this component. Currently this reflects the (group, module, version) that was used to request this component.
+     *
+     * <p>This is a legacy identifier and is here while we transition the meta-data away from ivy-like
+     * module versions to the more general component instances. Currently, the module version and component identifiers are used interchangeably. However, over
+     * time more things will use the component identifier. At some point, the module version identifier will become optional for a component.
+     */
+    ModuleVersionIdentifier getId();
+
+    /**
+     * Returns the source (eg location) for this component.
+     */
+    ModuleSource getSource();
+
+    /**
+     * Makes a copy of this meta-data with the given source.
+     */
+    ComponentResolveMetaData withSource(ModuleSource source);
+
+    /**
+     * Returns this module version as an Ivy ModuleDescriptor. This method is here to allow us to migrate away from the Ivy types
+     * and will be removed.
+     *
+     * <p>You should avoid using this method.
+     */
+    ModuleDescriptor getDescriptor();
+
+    List<DependencyMetaData> getDependencies();
+
+    /**
+     * Locates the configuration with the given name, if any.
+     */
+    @Nullable
+    ConfigurationMetaData getConfiguration(String name);
+
+    /**
+     * Converts the given Ivy artifact to the corresponding artifact meta-data. This method is here to allow us to migrate away from the Ivy types and
+     * will be removed.
+     */
+    ComponentArtifactMetaData artifact(Artifact artifact);
+
+    /**
+     * Returns the known artifacts for this component. There may be additional component available that are not included in this set.
+     */
+    Set<? extends ComponentArtifactMetaData> getArtifacts();
+
+    boolean isGenerated();
+
+    boolean isChanging();
+
+    String getStatus();
+
+    List<String> getStatusScheme();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentUsage.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentUsage.java
new file mode 100644
index 0000000..72961a6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentUsage.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.internal.component.model;
+
+public interface ComponentUsage {
+    String getConfigurationName();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetaData.java
new file mode 100644
index 0000000..22aac71
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetaData.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.internal.component.model;
+
+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();
+
+    ComponentResolveMetaData getComponent();
+
+    List<DependencyMetaData> getDependencies();
+
+    Set<ComponentArtifactMetaData> getArtifacts();
+
+    Set<ExcludeRule> getExcludeRules();
+
+    boolean isTransitive();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentUsage.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentUsage.java
new file mode 100644
index 0000000..f014010
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentUsage.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.internal.component.model;
+
+public class DefaultComponentUsage implements ComponentUsage {
+    private final String configurationName;
+
+    public DefaultComponentUsage(String configurationName) {
+        this.configurationName = configurationName;
+    }
+
+    public String getConfigurationName() {
+        return configurationName;
+    }
+
+    @Override
+    public String toString() {
+        return "artifacts for configuration '" + configurationName + "'";
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultDependencyMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultDependencyMetaData.java
new file mode 100644
index 0000000..3a8624c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultDependencyMetaData.java
@@ -0,0 +1,154 @@
+/*
+ * 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.component.model;
+
+import com.google.common.collect.Sets;
+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.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ReflectiveDependencyDescriptorFactory;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.component.local.model.DefaultProjectDependencyMetaData;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultDependencyMetaData implements DependencyMetaData {
+    private final DependencyDescriptor dependencyDescriptor;
+    private final DefaultModuleVersionSelector requested;
+
+    public DefaultDependencyMetaData(DependencyDescriptor dependencyDescriptor) {
+        this.dependencyDescriptor = dependencyDescriptor;
+        ModuleRevisionId dependencyRevisionId = dependencyDescriptor.getDependencyRevisionId();
+        requested = new DefaultModuleVersionSelector(dependencyRevisionId.getOrganisation(), dependencyRevisionId.getName(), dependencyRevisionId.getRevision());
+    }
+
+    public DefaultDependencyMetaData(ModuleVersionIdentifier moduleVersionIdentifier) {
+        dependencyDescriptor = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId(moduleVersionIdentifier), false);
+        requested = new DefaultModuleVersionSelector(moduleVersionIdentifier.getGroup(), moduleVersionIdentifier.getName(), moduleVersionIdentifier.getVersion());
+    }
+
+    public DefaultDependencyMetaData(ModuleComponentIdentifier componentIdentifier) {
+        dependencyDescriptor = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId(componentIdentifier), false);
+        requested = new DefaultModuleVersionSelector(componentIdentifier.getGroup(), componentIdentifier.getModule(), componentIdentifier.getVersion());
+    }
+
+    @Override
+    public String toString() {
+        return dependencyDescriptor.toString();
+    }
+
+    public ModuleVersionSelector getRequested() {
+        return requested;
+    }
+
+    public boolean isChanging() {
+        return dependencyDescriptor.isChanging();
+    }
+
+    public boolean isTransitive() {
+        return dependencyDescriptor.isTransitive();
+    }
+
+    public DependencyDescriptor getDescriptor() {
+        return dependencyDescriptor;
+    }
+
+    public Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData fromConfiguration, ConfigurationMetaData toConfiguration) {
+        String[] targetConfigurations = fromConfiguration.getHierarchy().toArray(new String[fromConfiguration.getHierarchy().size()]);
+        DependencyArtifactDescriptor[] dependencyArtifacts = dependencyDescriptor.getDependencyArtifacts(targetConfigurations);
+        if (dependencyArtifacts.length == 0) {
+            return Collections.emptySet();
+        }
+        Set<ComponentArtifactMetaData> artifacts = new LinkedHashSet<ComponentArtifactMetaData>();
+        for (DependencyArtifactDescriptor artifactDescriptor : dependencyArtifacts) {
+            ModuleRevisionId id = toConfiguration.getComponent().getDescriptor().getModuleRevisionId();
+            Artifact artifact = new DefaultArtifact(id, null, artifactDescriptor.getName(), artifactDescriptor.getType(), artifactDescriptor.getExt(), artifactDescriptor.getUrl(), artifactDescriptor.getQualifiedExtraAttributes());
+            artifacts.add(toConfiguration.getComponent().artifact(artifact));
+        }
+        return artifacts;
+    }
+
+    public Set<IvyArtifactName> getArtifacts() {
+        DependencyArtifactDescriptor[] dependencyArtifacts = dependencyDescriptor.getAllDependencyArtifacts();
+        if (dependencyArtifacts.length == 0) {
+            return Collections.emptySet();
+        }
+        Set<IvyArtifactName> artifactSet = Sets.newLinkedHashSet();
+        for (DependencyArtifactDescriptor artifact : dependencyArtifacts) {
+            artifactSet.add(new DefaultIvyArtifactName(artifact.getName(), artifact.getType(), artifact.getExt(), artifact.getExtraAttributes()));
+        }
+        return artifactSet;
+    }
+
+    public DependencyMetaData withRequestedVersion(String requestedVersion) {
+        if (requestedVersion.equals(requested.getVersion())) {
+            return this;
+        }
+        return new DefaultDependencyMetaData(dependencyDescriptor.clone(IvyUtil.createModuleRevisionId(dependencyDescriptor.getDependencyRevisionId(), requestedVersion)));
+    }
+
+    @Override
+    public DependencyMetaData withTarget(ComponentSelector target) {
+        if (target instanceof ModuleComponentSelector) {
+            ModuleComponentSelector moduleTarget = (ModuleComponentSelector) target;
+            ModuleVersionSelector requestedVersion = DefaultModuleVersionSelector.newSelector(moduleTarget.getGroup(), moduleTarget.getModule(), moduleTarget.getVersion());
+            if (requestedVersion.equals(requested)) {
+                return this;
+            }
+            ModuleRevisionId requestedId = IvyUtil.createModuleRevisionId(requestedVersion.getGroup(), requestedVersion.getName(), requestedVersion.getVersion());
+            DependencyDescriptor substitutedDescriptor = new ReflectiveDependencyDescriptorFactory().create(dependencyDescriptor, requestedId);
+            return new DefaultDependencyMetaData(substitutedDescriptor);
+        } else if (target instanceof ProjectComponentSelector) {
+            // TODO:Prezi what to do here?
+            ProjectComponentSelector projectTarget = (ProjectComponentSelector) target;
+            return new DefaultProjectDependencyMetaData(getDescriptor(), projectTarget.getProjectPath());
+        } else {
+            throw new AssertionError();
+        }
+    }
+
+    public DependencyMetaData withChanging() {
+        if (dependencyDescriptor.isChanging()) {
+            return this;
+        }
+
+        DependencyDescriptor forcedChanging = dependencyDescriptor.clone(dependencyDescriptor.getDependencyRevisionId());
+        try {
+            Field field = DefaultDependencyDescriptor.class.getDeclaredField("isChanging");
+            field.setAccessible(true);
+            field.set(forcedChanging, true);
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        return new DefaultDependencyMetaData(forcedChanging);
+    }
+
+    public ComponentSelector getSelector() {
+        return DefaultModuleComponentSelector.newSelector(requested.getGroup(), requested.getName(), requested.getVersion());
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultIvyArtifactName.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultIvyArtifactName.java
new file mode 100644
index 0000000..8a21c12
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultIvyArtifactName.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.component.model;
+
+import com.google.common.base.Objects;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.Nullable;
+import org.gradle.util.GUtil;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultIvyArtifactName implements IvyArtifactName {
+    private static final String CLASSIFIER = "classifier";
+    private final String name;
+    private final String type;
+    private final String extension;
+    private final Map<String, String> attributes;
+
+    public DefaultIvyArtifactName(Artifact a) {
+        this(a.getName(), a.getType(), a.getExt(), a.getAttributes());
+    }
+
+    public DefaultIvyArtifactName(String name, String type, @Nullable String extension, Map<String, String> attributes) {
+        this.name = name;
+        this.type = type;
+        this.extension = extension;
+        if (attributes.isEmpty()) {
+            this.attributes = Collections.emptyMap();
+        } else {
+            this.attributes = new HashMap<String, String>();
+            for (Map.Entry<String, String> entry : attributes.entrySet()) {
+                if (GUtil.isTrue(entry.getValue())) {
+                    this.attributes.put(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+    }
+
+    public DefaultIvyArtifactName(String name, String type, @Nullable String extension) {
+        this.name = name;
+        this.type = type;
+        this.extension = extension;
+        this.attributes = Collections.emptyMap();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append(name);
+        String classifier = attributes.get(CLASSIFIER);
+        if (GUtil.isTrue(classifier)) {
+            result.append("-");
+            result.append(classifier);
+        }
+        if (GUtil.isTrue(extension)) {
+            result.append(".");
+            result.append(extension);
+        }
+        return result.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode() ^ type.hashCode() ^ (extension == null ? 0 : extension.hashCode()) ^ attributes.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+        DefaultIvyArtifactName other = (DefaultIvyArtifactName) obj;
+        return other.name.equals(name)
+                && other.type.equals(type)
+                && Objects.equal(other.extension, extension)
+                && other.attributes.equals(attributes);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getExtension() {
+        return extension;
+    }
+
+    @Nullable
+    public String getClassifier() {
+        return attributes.get(CLASSIFIER);
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DependencyMetaData.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DependencyMetaData.java
new file mode 100644
index 0000000..5eaea6f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DependencyMetaData.java
@@ -0,0 +1,74 @@
+/*
+ * 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.component.model;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+
+import java.util.Set;
+
+public interface DependencyMetaData {
+    ModuleVersionSelector getRequested();
+
+    /**
+     * Returns this dependency as an Ivy DependencyDescriptor. This method is here to allow us to migrate away from the Ivy types
+     * and will be removed.
+     *
+     * <p>You should avoid using this method.
+     */
+    DependencyDescriptor getDescriptor();
+
+    boolean isChanging();
+
+    boolean isTransitive();
+
+    /**
+     * Returns the artifacts referenced by this dependency for the given combination of source and target configurations, if any. Returns an empty set if
+     * this dependency does not reference any specific artifacts - the defaults for the target configuration should be used in this case.
+     */
+    // TODO:ADAM - fromConfiguration should be implicit in this metadata
+    Set<ComponentArtifactMetaData> getArtifacts(ConfigurationMetaData fromConfiguration, ConfigurationMetaData toConfiguration);
+
+    /**
+     * Returns the artifacts referenced by this dependency, if any. Returns an empty set if this dependency does not reference any specific artifacts - the
+     * defaults should be used in this case.
+     */
+    Set<IvyArtifactName> getArtifacts();
+
+    /**
+     * Returns a copy of this dependency with the given requested version.
+     */
+    DependencyMetaData withRequestedVersion(String requestedVersion);
+
+    /**
+     * Returns a copy of this dependency with the given target.
+     */
+    DependencyMetaData withTarget(ComponentSelector target);
+
+    /**
+     * Returns a copy of this dependency with the changing flag set.
+     */
+    DependencyMetaData withChanging();
+
+    /**
+     * Returns the component selector for this dependency.
+     *
+     * @return Component selector
+     */
+    ComponentSelector getSelector();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/IvyArtifactName.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/IvyArtifactName.java
new file mode 100644
index 0000000..22e1969
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/IvyArtifactName.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.internal.component.model;
+
+import org.gradle.api.Nullable;
+
+import java.util.Map;
+
+/**
+ * Represents the 'name' part of an Ivy artifact, independent of which module version the artifact might belong to.
+ */
+public interface IvyArtifactName {
+    String getName();
+
+    String getType();
+
+    @Nullable
+    String getExtension();
+
+    String getClassifier();
+
+    Map<String, String> getAttributes();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ModuleSource.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ModuleSource.java
new file mode 100644
index 0000000..cf86931
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ModuleSource.java
@@ -0,0 +1,28 @@
+/*
+ * 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.component.model;
+
+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/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactNotFoundException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactNotFoundException.java
new file mode 100644
index 0000000..55f443f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactNotFoundException.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.internal.resolve;
+
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+
+import java.util.List;
+
+public class ArtifactNotFoundException extends ArtifactResolveException {
+    public ArtifactNotFoundException(ComponentArtifactIdentifier artifact, List<String> attemptedLocations) {
+        super(format(artifact, attemptedLocations));
+    }
+
+    private static String format(ComponentArtifactIdentifier artifact, List<String> locations) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(String.format("Could not find %s.", artifact.getDisplayName()));
+        if (!locations.isEmpty()) {
+            builder.append(String.format("%nSearched in the following locations:"));
+            for (String location : locations) {
+                builder.append(String.format("%n    %s", location.replace("%", "%%")));
+            }
+        }
+        return builder.toString();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactResolveException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactResolveException.java
new file mode 100644
index 0000000..0e17031
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ArtifactResolveException.java
@@ -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.internal.resolve;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.util.GUtil;
+
+ at Contextual
+public class ArtifactResolveException extends GradleException {
+    public ArtifactResolveException(String message) {
+        super(message);
+    }
+
+    public ArtifactResolveException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ArtifactResolveException(ComponentIdentifier component, Throwable cause) {
+        super(format(component, ""), cause);
+    }
+
+    public ArtifactResolveException(ComponentIdentifier component, String message) {
+        super(format(component, message));
+    }
+
+    public ArtifactResolveException(ComponentArtifactIdentifier artifact, Throwable cause) {
+        super(format(artifact, ""), cause);
+    }
+
+    public ArtifactResolveException(ComponentArtifactIdentifier artifact, String message) {
+        super(format(artifact, message));
+    }
+
+    private static String format(ComponentArtifactIdentifier artifact, String message) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Could not download ");
+        builder.append(artifact.getDisplayName());
+        if (GUtil.isTrue(message)) {
+            builder.append(": ");
+            builder.append(message);
+        }
+        return builder.toString();
+    }
+
+    private static String format(ComponentIdentifier component, String message) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Could not determine artifacts for ");
+        builder.append(component.getDisplayName());
+        if (GUtil.isTrue(message)) {
+            builder.append(": ");
+            builder.append(message);
+        }
+        return builder.toString();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java
new file mode 100755
index 0000000..403db86
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resolve;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public class ModuleVersionNotFoundException extends ModuleVersionResolveException {
+    /**
+     * This is used by {@link ModuleVersionResolveException#withIncomingPaths(java.util.Collection)}.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public ModuleVersionNotFoundException(ComponentSelector selector, String message) {
+        super(selector, message);
+    }
+
+    public ModuleVersionNotFoundException(ModuleVersionSelector selector, String message) {
+        super(selector, message);
+    }
+
+    public ModuleVersionNotFoundException(ModuleVersionSelector selector, Collection<String> attemptedLocations, Collection<String> unmatchedVersions, Collection<String> rejectedVersions) {
+        super(selector, format(selector, attemptedLocations, unmatchedVersions, rejectedVersions));
+    }
+
+    public ModuleVersionNotFoundException(ModuleVersionIdentifier id, Collection<String> attemptedLocations) {
+        super(id, format(id, attemptedLocations));
+    }
+
+    private static String format(ModuleVersionSelector selector, Collection<String> locations, Collection<String> unmatchedVersions, Collection<String> rejectedVersions) {
+        StringBuilder builder = new StringBuilder();
+        if (unmatchedVersions.isEmpty() && rejectedVersions.isEmpty()) {
+            builder.append(String.format("Could not find any matches for %s as no versions of %s:%s are available.", selector, selector.getGroup(), selector.getName()));
+        } else {
+            builder.append(String.format("Could not find any version that matches %s.", selector));
+            if (!unmatchedVersions.isEmpty()) {
+                builder.append(String.format("%nVersions that do not match:"));
+                appendSizeLimited(builder, unmatchedVersions);
+            }
+            if (!rejectedVersions.isEmpty()) {
+                builder.append(String.format("%nVersions rejected by component selection rules:"));
+                appendSizeLimited(builder, rejectedVersions);
+            }
+        }
+        addLocations(builder, locations);
+        return builder.toString();
+    }
+
+    private static String format(ModuleVersionIdentifier id, Collection<String> locations) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(String.format("Could not find %s.", id));
+        addLocations(builder, locations);
+        return builder.toString();
+    }
+
+    private static void appendSizeLimited(StringBuilder builder, Collection<String> values) {
+        Iterator<String> iterator = values.iterator();
+        int count = Math.min(5, values.size());
+        for (int i = 0; i < count; i++) {
+            builder.append(String.format("%n    %s", iterator.next()));
+        }
+        if (count < values.size()) {
+            builder.append(String.format("%n    + %d more", values.size() - count));
+        }
+    }
+
+    private static void addLocations(StringBuilder builder, Collection<String> locations) {
+        if (locations.isEmpty()) {
+            return;
+        }
+        builder.append(String.format("%nSearched in the following locations:"));
+        for (String location : locations) {
+            builder.append(String.format("%n    %s", location));
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java
new file mode 100644
index 0000000..9210021
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resolve;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.ModuleVersionSelector;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
+
+import java.util.*;
+
+ at Contextual
+public class ModuleVersionResolveException extends DefaultMultiCauseException {
+    private final List<List<ModuleVersionIdentifier>> paths = new ArrayList<List<ModuleVersionIdentifier>>();
+    private final ComponentSelector selector;
+
+    public ModuleVersionResolveException(ComponentSelector selector, String message) {
+        super(message);
+        this.selector = selector;
+    }
+
+    public ModuleVersionResolveException(ComponentSelector selector, Throwable cause) {
+        this(selector, format("Could not resolve %s.", selector));
+        initCause(cause);
+    }
+
+    public ModuleVersionResolveException(ComponentSelector selector, Iterable<? extends Throwable> causes) {
+        this(selector, format("Could not resolve %s.", selector));
+        initCauses(causes);
+    }
+
+    public ModuleVersionResolveException(ModuleVersionSelector selector, String message) {
+        this(DefaultModuleComponentSelector.newSelector(selector.getGroup(), selector.getName(), selector.getVersion()), message);
+    }
+
+    public ModuleVersionResolveException(ModuleVersionIdentifier id, String message) {
+        this(DefaultModuleComponentSelector.newSelector(id.getGroup(), id.getName(), id.getVersion()), message);
+    }
+
+    public ModuleVersionResolveException(ModuleComponentIdentifier id, String messageFormat) {
+        this(DefaultModuleComponentSelector.newSelector(id.getGroup(), id.getModule(), id.getVersion()), messageFormat);
+    }
+
+    public ModuleVersionResolveException(ModuleComponentIdentifier id, Throwable cause) {
+        this(DefaultModuleComponentSelector.newSelector(id.getGroup(), id.getModule(), id.getVersion()), Arrays.asList(cause));
+    }
+
+    public ModuleVersionResolveException(ModuleComponentIdentifier id, Iterable<? extends Throwable> causes) {
+        this(DefaultModuleComponentSelector.newSelector(id.getGroup(), id.getModule(), id.getVersion()), causes);
+    }
+
+    public ModuleVersionResolveException(ModuleVersionSelector selector, Throwable cause) {
+        this(DefaultModuleComponentSelector.newSelector(selector.getGroup(), selector.getName(), selector.getVersion()), cause);
+    }
+
+    public ModuleVersionResolveException(ModuleVersionSelector selector, Iterable<? extends Throwable> causes) {
+        this(DefaultModuleComponentSelector.newSelector(selector.getGroup(), selector.getName(), selector.getVersion()), causes);
+    }
+
+    /**
+     * Returns the selector that could not be resolved.
+     */
+    public ComponentSelector getSelector() {
+        return selector;
+    }
+
+    protected static String format(String messageFormat, ComponentSelector selector) {
+        return String.format(messageFormat, selector.getDisplayName());
+    }
+
+    /**
+     * Creates a copy of this exception, with the given incoming paths.
+     */
+    public ModuleVersionResolveException withIncomingPaths(Collection<? extends List<ModuleVersionIdentifier>> paths) {
+        ModuleVersionResolveException copy = createCopy();
+        copy.paths.addAll(paths);
+        copy.initCauses(getCauses());
+        copy.setStackTrace(getStackTrace());
+        return copy;
+    }
+
+    @Override
+    public String getMessage() {
+        if (paths.isEmpty()) {
+            return super.getMessage();
+        }
+        Formatter formatter = new Formatter();
+        formatter.format("%s%nRequired by:", super.getMessage());
+        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)));
+            }
+        }
+        return formatter.toString();
+    }
+
+    private String toString(ModuleVersionIdentifier identifier) {
+        return identifier.toString();
+    }
+
+    protected ModuleVersionResolveException createCopy() {
+        try {
+            return getClass().getConstructor(ComponentSelector.class, String.class).newInstance(selector, getMessage());
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java
new file mode 100644
index 0000000..7698a00
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.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.internal.resolve.resolver;
+
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.component.model.ComponentUsage;
+import org.gradle.internal.component.model.ModuleSource;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
+
+public interface ArtifactResolver {
+    /**
+     * Resolves a set of artifacts belonging to the given component, based on the supplied usage. Any failures are packaged up in the result.
+     */
+    void resolveModuleArtifacts(ComponentResolveMetaData component, ComponentUsage usage, BuildableArtifactSetResolveResult result);
+
+    /**
+     * Resolves a set of artifacts belonging to the given component, with the type specified. Any failures are packaged up in the result.
+     */
+    void resolveModuleArtifacts(ComponentResolveMetaData component, ArtifactType artifactType, BuildableArtifactSetResolveResult result);
+
+    /**
+     * Resolves the given artifact. Any failures are packaged up in the result.
+     */
+    void resolveArtifact(ComponentArtifactMetaData artifact, ModuleSource moduleSource, BuildableArtifactResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java
new file mode 100644
index 0000000..4429908
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.resolver;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+public interface ComponentMetaDataResolver {
+    /**
+     * Resolves the meta-data for a component instance. Failures should be attached to the returned result.
+     */
+    void resolve(DependencyMetaData dependency, ComponentIdentifier identifier, BuildableComponentResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java
new file mode 100644
index 0000000..19d857f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.resolver;
+
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult;
+
+public interface DependencyToComponentIdResolver {
+    /**
+     * Resolves the given dependency to a component instance. Failures should be attached to the result.
+     *
+     * <p>At some point in the future, this should resolve to a set of candidates rather than a single instance.
+     */
+    void resolve(DependencyMetaData dependency, BuildableComponentIdResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentResolver.java
new file mode 100755
index 0000000..52726e0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentResolver.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.internal.resolve.resolver;
+
+import org.gradle.internal.component.model.DependencyMetaData;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+/**
+ * Resolves a dependency to a component instance.
+ */
+public interface DependencyToComponentResolver {
+    /**
+     * Resolves the given dependency to component instance. Failures are packaged up in the returned result.
+     */
+    void resolve(DependencyMetaData dependency, BuildableComponentResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ModuleToComponentResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ModuleToComponentResolver.java
new file mode 100755
index 0000000..0f938d9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ModuleToComponentResolver.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.internal.resolve.resolver;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.artifacts.ModuleInternal;
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
+
+import java.util.Set;
+
+/**
+ * Resolves a module to the meta-data for a module.
+ */
+public interface ModuleToComponentResolver {
+    void resolve(ModuleInternal module, Set<? extends Configuration> configurations, BuildableComponentResolveResult result);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactResolveResult.java
new file mode 100644
index 0000000..055d189
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactResolveResult.java
@@ -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.internal.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resolve.ArtifactResolveException;
+
+import java.io.File;
+
+/**
+ * The result of resolving an artifact id to a file.
+ */
+public interface ArtifactResolveResult extends ResolveResult {
+    /**
+     * Returns the resolve failure, if any.
+     */
+    @Nullable
+    ArtifactResolveException getFailure();
+
+    /**
+     * @throws ArtifactResolveException If the resolution was unsuccessful.
+     */
+    File getFile() throws ArtifactResolveException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactSetResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactSetResolveResult.java
new file mode 100644
index 0000000..852dcec
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ArtifactSetResolveResult.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+import org.gradle.internal.resolve.ArtifactResolveException;
+
+import java.util.Set;
+
+/**
+ * The result of resolving a set of artifacts that match some criteria.
+ */
+public interface ArtifactSetResolveResult extends ResolveResult {
+    /**
+     * Returns the resolve failure, if any.
+     */
+    @Nullable
+    ArtifactResolveException getFailure();
+
+    Set<ComponentArtifactMetaData> getArtifacts();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactResolveResult.java
new file mode 100644
index 0000000..9774d6e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactResolveResult.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.internal.resolve.result;
+
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+
+import java.io.File;
+
+public interface BuildableArtifactResolveResult extends ArtifactResolveResult, ResourceAwareResolveResult {
+    /**
+     * Marks the artifact as resolved to the given file.
+     */
+    void resolved(File file);
+
+    /**
+     * Marks the resolve as failed with the given exception.
+     */
+    void failed(ArtifactResolveException failure);
+
+    /**
+     * Marks the artifact as not found.
+     */
+    void notFound(ComponentArtifactIdentifier artifact);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactSetResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactSetResolveResult.java
new file mode 100644
index 0000000..abb53d3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactSetResolveResult.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.internal.resolve.result;
+
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+
+import java.util.Collection;
+
+public interface BuildableArtifactSetResolveResult extends ArtifactSetResolveResult {
+    void resolved(Collection<? extends ComponentArtifactMetaData> artifacts);
+
+    void failed(ArtifactResolveException failure);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentIdResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentIdResolveResult.java
new file mode 100644
index 0000000..880fd27
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentIdResolveResult.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public interface BuildableComponentIdResolveResult extends ComponentIdResolveResult, ResourceAwareResolveResult {
+    void resolved(ComponentIdentifier id, ModuleVersionIdentifier moduleVersionIdentifier);
+
+    void resolved(ComponentResolveMetaData metaData);
+
+    void setSelectionReason(ComponentSelectionReason reason);
+
+    void failed(ModuleVersionResolveException failure);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentResolveResult.java
new file mode 100644
index 0000000..7c7b194
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentResolveResult.java
@@ -0,0 +1,43 @@
+/*
+ * 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.resolve.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public interface BuildableComponentResolveResult extends ComponentResolveResult, ResourceAwareResolveResult {
+    /**
+     * Marks the component as resolved, with the given meta-data.
+     */
+    void resolved(ComponentResolveMetaData metaData);
+
+    /**
+     * Marks the resolve as failed with the given exception.
+     */
+    BuildableComponentResolveResult failed(ModuleVersionResolveException failure);
+
+    /**
+     * Marks the component as not found.
+     */
+    void notFound(ModuleVersionIdentifier versionIdentifier);
+
+    /**
+     * Replaces the meta-data in the result. Result must already be resolved.
+     */
+    void setMetaData(ComponentResolveMetaData metaData);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentSelectionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentSelectionResult.java
new file mode 100644
index 0000000..903c462
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableComponentSelectionResult.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+/**
+ * The result of resolving some dynamic version selector to a particular component id.
+ */
+public interface BuildableComponentSelectionResult extends ResolveResult {
+    static enum State {
+        Match, NoMatch, Failed, Unknown
+    }
+
+    /**
+     * Returns the chosen module component identifier. The component identifier may be null.
+     *
+     * @return Chosen module component identifier
+     */
+    ModuleComponentIdentifier getMatch();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Nullable
+    @Override
+    ModuleVersionResolveException getFailure();
+
+    /**
+     * Marks the given module component identifier as matching.
+     *
+     * @param moduleComponentIdentifier Chosen module component identifier
+     */
+    void matches(ModuleComponentIdentifier moduleComponentIdentifier);
+
+    void failed(ModuleVersionResolveException failure);
+
+    /**
+     * Registers that there was no matching module component identifier.
+     */
+    void noMatchFound();
+
+    /**
+     * Returns the reason for choosing the component.
+     */
+    State getState();
+
+    /**
+     * Adds a candidate version that did not match the provided selector.
+     */
+    void notMatched(String candidateVersion);
+
+    /**
+     * Adds a candidate version that matched the provided selector, but was rejected by some rule.
+     */
+    void rejected(String version);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java
new file mode 100644
index 0000000..0426b69
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java
@@ -0,0 +1,72 @@
+/*
+ * 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+/**
+ * The result of attempting to resolve a component id to the meta-data for the component.
+ */
+public interface BuildableModuleComponentMetaDataResolveResult extends ResourceAwareResolveResult, ResolveResult {
+    static enum State {
+        Resolved, Missing, Failed, Unknown
+    }
+
+    /**
+     * Returns the current state of this descriptor.
+     */
+    State getState();
+
+    /**
+     * Returns true if this result is available, ie the state is not {@link State#Unknown}.
+     */
+    boolean hasResult();
+
+    /**
+     * Returns the meta-data.
+     *
+     * @throws ModuleVersionResolveException If the resolution was not successful.
+     */
+    MutableModuleComponentResolveMetaData getMetaData() throws ModuleVersionResolveException;
+
+    @Nullable
+    ModuleVersionResolveException getFailure();
+
+    /**
+     * Marks the module version as resolved, with the given meta-data and source.
+     */
+    void resolved(MutableModuleComponentResolveMetaData metaData);
+
+    /**
+     * Marks the resolve as failed with the given exception.
+     */
+    void failed(ModuleVersionResolveException failure);
+
+    /**
+     * Marks the module version as definitely missing.
+     */
+    void missing();
+
+    /**
+     * Returns true if the result is from an authoritative source. Defaults to true.
+     */
+    boolean isAuthoritative();
+
+    void setAuthoritative(boolean authoritative);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleVersionListingResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleVersionListingResolveResult.java
new file mode 100644
index 0000000..06aae9e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleVersionListingResolveResult.java
@@ -0,0 +1,65 @@
+/*
+ * 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * The result of attempting to resolve the list of versions for a particular module.
+ */
+public interface BuildableModuleVersionListingResolveResult extends ResourceAwareResolveResult, ResolveResult {
+
+    static enum State {
+        Listed, Failed, Unknown
+    }
+
+    /**
+     * Returns the current state of this result.
+     */
+    State getState();
+
+    /**
+     * Returns the versions that match the selector.
+     *
+     * @throws ModuleVersionResolveException If the resolution was not successful.
+     */
+    Set<String> getVersions() throws ModuleVersionResolveException;
+
+    @Nullable
+    ModuleVersionResolveException getFailure();
+
+    /**
+     * Marks the module as having been listed to have the specified versions available.
+     */
+    void listed(Collection<String> versions);
+
+    /**
+     * Marks the list as failed with the given exception.
+     */
+    void failed(ModuleVersionResolveException failure);
+
+    /**
+     * Returns true if the result is from an authoritative source. Defaults to true.
+     */
+    boolean isAuthoritative();
+
+    void setAuthoritative(boolean authoritative);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentIdResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentIdResolveResult.java
new file mode 100644
index 0000000..4aeb941
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentIdResolveResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+/**
+ * The result of resolving a module version selector to a particular component id. The result may optionally include the meta-data for the selected component, if it
+ * is cheaply available (for example, it was used to select the component).
+ */
+public interface ComponentIdResolveResult extends ResolveResult {
+    @Nullable
+    ModuleVersionResolveException getFailure();
+
+    ComponentIdentifier getId();
+
+    ModuleVersionIdentifier getModuleVersionId();
+
+    ComponentSelectionReason getSelectionReason();
+
+    /**
+     * Returns the meta-data for the component, if it was available at resolve time.
+     */
+    @Nullable
+    ComponentResolveMetaData getMetaData();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentResolveResult.java
new file mode 100644
index 0000000..cdea9e9
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ComponentResolveResult.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.internal.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+/**
+ * The result of resolving a module version selector to a particular component.
+ *
+ * <p>Very similar to {@link org.gradle.internal.resolve.result.ComponentIdResolveResult}, could probably merge these.
+ */
+public interface ComponentResolveResult extends ResolveResult {
+    /**
+     * Returns the module version id of the component.
+     *
+     * @throws org.gradle.internal.resolve.ModuleVersionResolveException If resolution was unsuccessful and the id is unknown.
+     */
+    ModuleVersionIdentifier getId() throws ModuleVersionResolveException;
+
+    /**
+     * Returns the meta-data for the component.
+     *
+     * @throws ModuleVersionResolveException If resolution was unsuccessful and the descriptor is not available.
+     */
+    ComponentResolveMetaData getMetaData() throws ModuleVersionResolveException;
+
+    /**
+     * Returns the resolve failure, if any.
+     */
+    @Nullable
+    ModuleVersionResolveException getFailure();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResult.java
new file mode 100644
index 0000000..4a9f7fb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResult.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.internal.resolve.result;
+
+import org.gradle.internal.resolve.ArtifactNotFoundException;
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.component.model.ComponentArtifactIdentifier;
+
+import java.io.File;
+
+public class DefaultBuildableArtifactResolveResult extends DefaultResourceAwareResolveResult implements BuildableArtifactResolveResult {
+    private ArtifactResolveException failure;
+    private File file;
+
+    public void failed(ArtifactResolveException failure) {
+        this.failure = failure;
+    }
+
+    public void resolved(File file) {
+        this.file = file;
+    }
+
+    public void notFound(ComponentArtifactIdentifier artifact) {
+        failed(new ArtifactNotFoundException(artifact, getAttempted()));
+    }
+
+    public ArtifactResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    public File getFile() throws ArtifactResolveException {
+        assertResolved();
+        return file;
+    }
+
+    public boolean hasResult() {
+        return failure != null || file != null;
+    }
+
+    private void assertResolved() {
+        assertHasResult();
+        if (failure != null) {
+            throw failure;
+        }
+    }
+
+    private void assertHasResult() {
+        if (!hasResult()) {
+            throw new IllegalStateException("No result has been specified.");
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResult.java
new file mode 100644
index 0000000..7db14cb
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResult.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.internal.resolve.ArtifactResolveException;
+import org.gradle.internal.component.model.ComponentArtifactMetaData;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultBuildableArtifactSetResolveResult implements BuildableArtifactSetResolveResult {
+    private ArtifactResolveException failure;
+    private Set<ComponentArtifactMetaData> artifacts;
+
+    public void resolved(Collection<? extends ComponentArtifactMetaData> artifacts) {
+        this.artifacts = new LinkedHashSet<ComponentArtifactMetaData>(artifacts);
+    }
+
+    public void failed(ArtifactResolveException failure) {
+        this.failure = failure;
+    }
+
+    public boolean hasResult() {
+        return artifacts != null || failure != null;
+    }
+
+    public ArtifactResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    public Set<ComponentArtifactMetaData> getArtifacts() {
+        assertResolved();
+        return artifacts;
+    }
+
+    private void assertResolved() {
+        assertHasResult();
+        if (failure != null) {
+            throw failure;
+        }
+    }
+
+    private void assertHasResult() {
+        if (!hasResult()) {
+            throw new IllegalStateException("No result has been specified.");
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResult.java
new file mode 100644
index 0000000..645690f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResult.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public class DefaultBuildableComponentIdResolveResult extends DefaultResourceAwareResolveResult implements BuildableComponentIdResolveResult {
+    private ModuleVersionResolveException failure;
+    private ComponentResolveMetaData metaData;
+    private ComponentIdentifier id;
+    private ModuleVersionIdentifier moduleVersionId;
+    private ComponentSelectionReason selectionReason;
+
+    public boolean hasResult() {
+        return id != null || failure != null;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        return failure;
+    }
+
+    public ComponentIdentifier getId() {
+        assertResolved();
+        return id;
+    }
+
+    public ModuleVersionIdentifier getModuleVersionId() {
+        assertResolved();
+        return moduleVersionId;
+    }
+
+    public ComponentSelectionReason getSelectionReason() {
+        return selectionReason;
+    }
+
+    public void setSelectionReason(ComponentSelectionReason reason) {
+        this.selectionReason = reason;
+    }
+
+    public ComponentResolveMetaData getMetaData() {
+        assertResolved();
+        return metaData;
+    }
+
+    public void resolved(ComponentIdentifier id, ModuleVersionIdentifier moduleVersionIdentifier) {
+        reset();
+        this.id = id;
+        this.moduleVersionId = moduleVersionIdentifier;
+    }
+
+    public void resolved(ComponentResolveMetaData metaData) {
+        resolved(metaData.getComponentId(), metaData.getId());
+        this.metaData = metaData;
+    }
+
+    public void failed(ModuleVersionResolveException failure) {
+        reset();
+        this.failure = failure;
+    }
+
+    private void assertResolved() {
+        if (failure != null) {
+            throw failure;
+        }
+        if (id == null) {
+            throw new IllegalStateException("Not resolved.");
+        }
+    }
+
+    private void reset() {
+        failure = null;
+        metaData = null;
+        id = null;
+        moduleVersionId = null;
+        selectionReason = VersionSelectionReasons.REQUESTED;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResult.java
new file mode 100644
index 0000000..0b77d4e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResult.java
@@ -0,0 +1,88 @@
+/*
+ * 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.resolve.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.internal.component.model.ComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionNotFoundException;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public class DefaultBuildableComponentResolveResult extends DefaultResourceAwareResolveResult implements BuildableComponentResolveResult {
+    private ComponentResolveMetaData metaData;
+    private ModuleVersionResolveException failure;
+
+    public DefaultBuildableComponentResolveResult failed(ModuleVersionResolveException failure) {
+        metaData = null;
+        this.failure = failure;
+        return this;
+    }
+
+    public void notFound(ModuleVersionIdentifier versionIdentifier) {
+        failed(new ModuleVersionNotFoundException(versionIdentifier, getAttempted()));
+    }
+
+    public void resolved(ComponentResolveMetaData metaData) {
+        this.metaData = metaData;
+    }
+
+    public void setMetaData(ComponentResolveMetaData metaData) {
+        assertResolved();
+        this.metaData = metaData;
+    }
+
+    public ModuleVersionIdentifier getId() throws ModuleVersionResolveException {
+        assertResolved();
+        return metaData.getId();
+    }
+
+    public ComponentResolveMetaData getMetaData() throws ModuleVersionResolveException {
+        assertResolved();
+        return metaData;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    private void assertResolved() {
+        assertHasResult();
+        if (failure != null) {
+            throw failure;
+        }
+    }
+
+    private void assertHasResult() {
+        if (!hasResult()) {
+            throw new IllegalStateException("No result has been specified.");
+        }
+    }
+
+    public boolean hasResult() {
+        return failure != null || metaData != null;
+    }
+
+    public void applyTo(BuildableComponentIdResolveResult idResolve) {
+        super.applyTo(idResolve);
+        if (failure != null) {
+            idResolve.failed(failure);
+        }
+        if (metaData != null) {
+            idResolve.resolved(metaData);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResult.java
new file mode 100644
index 0000000..6dc25c8
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResult.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultBuildableComponentSelectionResult implements BuildableComponentSelectionResult {
+    private State state = State.Unknown;
+    private ModuleComponentIdentifier moduleComponentIdentifier;
+    private ModuleVersionResolveException failure;
+    private final Set<String> unmatchedVersions = new LinkedHashSet<String>();
+    private final Set<String> rejectedVersions = new LinkedHashSet<String>();
+
+    public void matches(ModuleComponentIdentifier moduleComponentIdentifier) {
+        setChosenComponentWithReason(State.Match, moduleComponentIdentifier);
+    }
+
+    public void noMatchFound() {
+        setChosenComponentWithReason(State.NoMatch, null);
+    }
+
+    private void setChosenComponentWithReason(State state, ModuleComponentIdentifier moduleComponentIdentifier) {
+        this.state = state;
+        this.moduleComponentIdentifier = moduleComponentIdentifier;
+        this.failure = null;
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public ModuleComponentIdentifier getMatch() {
+        if (state != State.Match) {
+            throw new IllegalStateException("This result has not been resolved.");
+        }
+        return moduleComponentIdentifier;
+    }
+
+    @Override
+    public boolean hasResult() {
+        return state != State.Unknown;
+    }
+
+    @Nullable
+    @Override
+    public ModuleVersionResolveException getFailure() {
+        if (state == State.Unknown) {
+            throw new IllegalStateException("This result has not been resolved.");
+        }
+        return failure;
+    }
+
+    @Override
+    public void failed(ModuleVersionResolveException failure) {
+        this.moduleComponentIdentifier = null;
+        this.failure = failure;
+        this.state = State.Failed;
+    }
+
+    public Set<String> getUnmatchedVersions() {
+        return unmatchedVersions;
+    }
+
+    @Override
+    public void notMatched(String candidateVersion) {
+        unmatchedVersions.add(candidateVersion);
+    }
+
+    public Set<String> getRejectedVersions() {
+        return rejectedVersions;
+    }
+
+    @Override
+    public void rejected(String version) {
+        rejectedVersions.add(version);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java
new file mode 100644
index 0000000..723845b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.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.internal.resolve.result;
+
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+public class DefaultBuildableModuleComponentMetaDataResolveResult extends DefaultResourceAwareResolveResult implements BuildableModuleComponentMetaDataResolveResult {
+    private State state = State.Unknown;
+    private ModuleVersionResolveException failure;
+    private MutableModuleComponentResolveMetaData metaData;
+    private boolean authoritative;
+
+    private void reset(State state) {
+        this.state = state;
+        metaData = null;
+        failure = null;
+        authoritative = false;
+    }
+
+    public void reset() {
+        reset(State.Unknown);
+    }
+
+    public void resolved(MutableModuleComponentResolveMetaData metaData) {
+        reset(State.Resolved);
+        this.metaData = metaData;
+        authoritative = true;
+    }
+
+    public void missing() {
+        reset(State.Missing);
+        authoritative = true;
+    }
+
+    public void failed(ModuleVersionResolveException failure) {
+        reset(State.Failed);
+        this.failure = failure;
+        authoritative = true;
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public boolean hasResult() {
+        return state != State.Unknown;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    public MutableModuleComponentResolveMetaData getMetaData() throws ModuleVersionResolveException {
+        assertResolved();
+        return metaData;
+    }
+
+    public boolean isAuthoritative() {
+        assertHasResult();
+        return authoritative;
+    }
+
+    public void setAuthoritative(boolean authoritative) {
+        assertHasResult();
+        this.authoritative = authoritative;
+    }
+
+    private void assertHasResult() {
+        if (!hasResult()) {
+            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.");
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResult.java
new file mode 100644
index 0000000..af6314c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResult.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.internal.resolve.ModuleVersionResolveException;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DefaultBuildableModuleVersionListingResolveResult extends DefaultResourceAwareResolveResult implements BuildableModuleVersionListingResolveResult {
+    private State state = State.Unknown;
+    private ModuleVersionResolveException failure;
+    private Set<String> versions;
+    private boolean authoritative;
+
+    private void reset(State state) {
+        this.state = state;
+        versions = null;
+        failure = null;
+        authoritative = false;
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public boolean hasResult() {
+        return state != State.Unknown;
+    }
+
+    public Set<String> getVersions() throws ModuleVersionResolveException {
+        assertHasResult();
+        return versions;
+    }
+
+    public ModuleVersionResolveException getFailure() {
+        assertHasResult();
+        return failure;
+    }
+
+    public void listed(Collection<String> versions) {
+        reset(State.Listed);
+        this.versions = new LinkedHashSet<String>(versions);
+        this.authoritative = true;
+    }
+
+    public void failed(ModuleVersionResolveException failure) {
+        reset(State.Failed);
+        this.failure = failure;
+        this.authoritative = true;
+    }
+
+    public boolean isAuthoritative() {
+        assertHasResult();
+        return authoritative;
+    }
+
+    public void setAuthoritative(boolean authoritative) {
+        assertHasResult();
+        this.authoritative = authoritative;
+    }
+
+    private void assertHasResult() {
+        if (!hasResult()) {
+            throw new IllegalStateException("No result has been specified.");
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResult.java
new file mode 100644
index 0000000..d99e886
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.internal.resource.ExternalResourceName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultResourceAwareResolveResult implements ResourceAwareResolveResult {
+    private final List<String> attempted = new ArrayList<String>();
+
+    public List<String> getAttempted() {
+        return attempted;
+    }
+
+    public void attempted(String locationDescription) {
+        attempted.add(locationDescription);
+    }
+
+    public void attempted(ExternalResourceName location) {
+        attempted(location.getDisplayName());
+    }
+
+    public void applyTo(ResourceAwareResolveResult target) {
+        for (String location : attempted) {
+            target.attempted(location);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResolveResult.java
new file mode 100644
index 0000000..7d8325c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResolveResult.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.api.Nullable;
+
+public interface ResolveResult {
+    /**
+     * Returns the resolve failure, if any.
+     */
+    @Nullable
+    Throwable getFailure();
+
+    /**
+     * Returns true if the result either has some value, or a failure.
+     */
+    boolean hasResult();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResourceAwareResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResourceAwareResolveResult.java
new file mode 100644
index 0000000..f91a608
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/ResourceAwareResolveResult.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result;
+
+import org.gradle.internal.resource.ExternalResourceName;
+
+import java.util.List;
+
+public interface ResourceAwareResolveResult {
+    List<String> getAttempted();
+
+    /**
+     * Adds a location that was used to build this result. This is used for diagnostic messages and logging.
+     */
+    void attempted(String locationDescription);
+
+    /**
+     * Adds a location that was used to build this result. This is used for diagnostic messages and logging.
+     */
+    void attempted(ExternalResourceName location);
+
+    /**
+     * Copies the locations for this result to the given target.
+     */
+    void applyTo(ResourceAwareResolveResult target);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ByUrlCachedExternalResourceIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ByUrlCachedExternalResourceIndex.java
new file mode 100644
index 0000000..b058f97
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ByUrlCachedExternalResourceIndex.java
@@ -0,0 +1,28 @@
+/*
+ * 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.resource.cached;
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+public class ByUrlCachedExternalResourceIndex extends DefaultCachedExternalResourceIndex<String> {
+
+    public ByUrlCachedExternalResourceIndex(String persistentCacheFile, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        super(persistentCacheFile, String.class, timeProvider, cacheLockingManager);
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifact.java
new file mode 100644
index 0000000..a4e25d7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifact.java
@@ -0,0 +1,26 @@
+/*
+ * 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.resource.cached;
+
+import java.math.BigInteger;
+import java.util.List;
+
+public interface CachedArtifact extends CachedItem {
+    BigInteger getDescriptorHash();
+
+    List<String> attemptedLocations();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifactIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifactIndex.java
new file mode 100644
index 0000000..01efb97
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedArtifactIndex.java
@@ -0,0 +1,63 @@
+/*
+ * 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.resource.cached;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryKey;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.util.List;
+
+public interface CachedArtifactIndex {
+    /**
+     * Adds a resolution to the index.
+     *
+     * The incoming file is expected to be in the persistent local. This method will not move/copy the file there. <p>
+     *
+     * @param key The key to cache this resolution under in the index. Cannot be null.
+     * @param artifactFile The artifact file in the persistent file store. Cannot be null
+     * @param moduleDescriptorHash The checksum (SHA1) of the related moduledescriptor.
+     */
+    void store(ArtifactAtRepositoryKey key, File artifactFile, BigInteger moduleDescriptorHash);
+
+    /**
+     * Record that the artifact with the given key was missing.
+     *
+     * @param key The key to cache this resolution under in the index.
+     * @param descriptorHash The SHA1 hash of the related moduleDescriptor
+     */
+    void storeMissing(ArtifactAtRepositoryKey key, List<String> attemptedLocations, BigInteger descriptorHash);
+
+    /**
+     * Lookup a cached resolution.
+     *
+     * The {@link CachedExternalResource#getCachedFile()} is guaranteed to exist at the time that the entry is returned from this method.
+     *
+     * @param key The key to search the index for
+     * @return The cached artifact resolution if one exists, otherwise null.
+     */
+    @Nullable
+    CachedArtifact lookup(ArtifactAtRepositoryKey key);
+
+    /**
+     * Remove the entry for the given key if it exists.
+     *
+     * @param key The key of the item to remove.
+     */
+    void clear(ArtifactAtRepositoryKey key);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResource.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResource.java
new file mode 100644
index 0000000..576c62f
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResource.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.internal.resource.cached;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.util.Date;
+
+/**
+ * A record of some kind of external resource that has been cached locally (typically into the filestore).
+ */
+public interface CachedExternalResource extends CachedItem {
+
+    /**
+     * Always the actual content length of the cached file, not the external source.
+     *
+     * @return The content length of the cached file.
+     */
+    long getContentLength();
+
+    @Nullable
+    ExternalResourceMetaData getExternalResourceMetaData();
+
+    /**
+     * Null safe shortcut for getExternalResourceMetaData().getLastModified();
+     *
+     * @return The external last modified, or null if unavailable.
+     */
+    Date getExternalLastModified();
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResourceIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResourceIndex.java
new file mode 100644
index 0000000..06f0618
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedExternalResourceIndex.java
@@ -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.internal.resource.cached;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.File;
+
+/**
+ * Provides an indexed view into cached artifacts and a record of resolution attempts, successful or not.
+ *
+ * Maintains references to the location of files in the persistent local. Does not deal with moving files into the local.
+ * 
+ * @param <K> The type of the key to the index
+ */
+public interface CachedExternalResourceIndex<K> {
+
+    /**
+     * Adds a resolution to the index.
+     * 
+     * The incoming file is expected to be in the persistent local. This method will not move/copy the file there.
+     * <p>
+     *
+     * @param key The key to cache this resolution under in the index. Cannot be null.
+     * @param artifactFile The artifact file in the persistent file store. Cannot be null
+     * @param metaData Information about this resource at its source
+     * @see #storeMissing(Object)
+     */
+    void store(K key, File artifactFile, @Nullable ExternalResourceMetaData metaData);
+
+    /**
+     * Record that the artifact with the given key was missing.
+     *
+     * @param key The key to cache this resolution under in the index.
+     */
+    void storeMissing(K key);
+
+    /**
+     * Lookup a cached resolution.
+     *
+     * The {@link CachedExternalResource#getCachedFile()} is guaranteed
+     * to exist at the time that the entry is returned from this method.
+     *
+     * @param key The key to search the index for
+     * @return The cached artifact resolution if one exists, otherwise null.
+     */
+    @Nullable
+    CachedExternalResource lookup(K key);
+
+    /**
+     * Remove the entry for the given key if it exists.
+     *
+     * @param key The key of the item to remove.
+     */
+    void clear(K key);
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedItem.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedItem.java
new file mode 100644
index 0000000..d0f128b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/CachedItem.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.internal.resource.cached;
+
+import org.gradle.api.Nullable;
+
+import java.io.File;
+
+public interface CachedItem {
+    /**
+     * True if this cache entry represents that the resource does not exist.
+     *
+     * For a missing resource, all of the values will be null or similar “non” values.
+     *
+     * @return Whether this is a “missing” entry or not.
+     */
+    boolean isMissing();
+
+    /**
+     * The cached version of the external resource as a local file.
+     *
+     * @return The cached version of the external resource as a local file, or null if this {@link #isMissing()}.
+     */
+    @Nullable
+    File getCachedFile();
+
+    /**
+     * The timestamp of when this cache entry was created.
+     *
+     * @return The timestamp of when this cache entry was created.
+     */
+    long getCachedAt();
+
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedArtifact.java
new file mode 100644
index 0000000..36341a4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedArtifact.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.internal.resource.cached;
+
+import java.io.File;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultCachedArtifact implements CachedArtifact, Serializable {
+    private final File cachedFile;
+    private final long cachedAt;
+    private final BigInteger descriptorHash;
+    private final List<String> attemptedLocations;
+
+    public DefaultCachedArtifact(File cachedFile, long cachedAt, BigInteger descriptorHash) {
+        this.cachedFile = cachedFile;
+        this.cachedAt = cachedAt;
+        this.descriptorHash = descriptorHash;
+        this.attemptedLocations = Collections.emptyList();
+    }
+
+    public DefaultCachedArtifact(List<String> attemptedLocations, long cachedAt, BigInteger descriptorHash) {
+        this.attemptedLocations = attemptedLocations;
+        this.cachedAt = cachedAt;
+        this.cachedFile = null;
+        this.descriptorHash = descriptorHash;
+    }
+
+    public boolean isMissing() {
+        return cachedFile == null;
+    }
+
+    public File getCachedFile() {
+        return cachedFile;
+    }
+
+    public long getCachedAt() {
+        return cachedAt;
+    }
+
+    public BigInteger getDescriptorHash() {
+        return descriptorHash;
+    }
+
+    public List<String> attemptedLocations() {
+        return attemptedLocations;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResource.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResource.java
new file mode 100644
index 0000000..f865bea
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResource.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.internal.resource.cached;
+
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Date;
+
+public class DefaultCachedExternalResource implements CachedExternalResource, Serializable {
+    private final File cachedFile;
+    private final long cachedAt;
+    private final ExternalResourceMetaData externalResourceMetaData;
+
+    public DefaultCachedExternalResource(File cachedFile, long cachedAt, ExternalResourceMetaData externalResourceMetaData) {
+        this.cachedFile = cachedFile;
+        this.cachedAt = cachedAt;
+        this.externalResourceMetaData = externalResourceMetaData;
+    }
+
+    public DefaultCachedExternalResource(long cachedAt) {
+        this.cachedAt = cachedAt;
+
+        this.cachedFile = null;
+        this.externalResourceMetaData = null;
+    }
+
+    public boolean isMissing() {
+        return cachedFile == null;
+    }
+
+    public File getCachedFile() {
+        return cachedFile;
+    }
+
+    public long getCachedAt() {
+        return cachedAt;
+    }
+
+    public ExternalResourceMetaData getExternalResourceMetaData() {
+        return externalResourceMetaData;
+    }
+
+    public Date getExternalLastModified() {
+        return externalResourceMetaData != null ? externalResourceMetaData.getLastModified() : null;
+    }
+
+    public long getContentLength() {
+        return isMissing() ? -1 : cachedFile.length();
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResourceIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResourceIndex.java
new file mode 100644
index 0000000..d91325b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/DefaultCachedExternalResourceIndex.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.internal.resource.cached;
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.internal.resource.cached.ivy.AbstractCachedIndex;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.serialize.DefaultSerializer;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultCachedExternalResourceIndex<K extends Serializable> extends AbstractCachedIndex<K, CachedExternalResource> implements CachedExternalResourceIndex<K> {
+
+    private final BuildCommencedTimeProvider timeProvider;
+
+    public DefaultCachedExternalResourceIndex(String persistentCacheFile, Class<K> keyType, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        super(persistentCacheFile, new DefaultSerializer<K>(keyType.getClassLoader()), new DefaultSerializer<CachedExternalResource>(CachedExternalResource.class.getClassLoader()), cacheLockingManager);
+        this.timeProvider = timeProvider;
+    }
+
+    private DefaultCachedExternalResource createEntry(File artifactFile, ExternalResourceMetaData externalResourceMetaData) {
+        return new DefaultCachedExternalResource(artifactFile, timeProvider.getCurrentTime(), externalResourceMetaData);
+    }
+
+    public void store(final K key, final File artifactFile, ExternalResourceMetaData externalResourceMetaData) {
+        assertArtifactFileNotNull(artifactFile);
+        assertKeyNotNull(key);
+
+        storeInternal(key, createEntry(artifactFile, externalResourceMetaData));
+    }
+
+    public void storeMissing(K key) {
+        storeInternal(key, new DefaultCachedExternalResource(timeProvider.getCurrentTime()));
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/AbstractCachedIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/AbstractCachedIndex.java
new file mode 100644
index 0000000..b868e24
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/AbstractCachedIndex.java
@@ -0,0 +1,104 @@
+/*
+ * 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.resource.cached.ivy;
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.internal.resource.cached.CachedItem;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.internal.Factory;
+import org.gradle.internal.serialize.Serializer;
+
+import java.io.File;
+
+abstract public class AbstractCachedIndex<K, V extends CachedItem> {
+    private final String persistentCacheFile;
+    private final Serializer<K> keySerializer;
+    private final Serializer<V> valueSerializer;
+    private final CacheLockingManager cacheLockingManager;
+
+    private PersistentIndexedCache<K, V> persistentCache;
+
+    public AbstractCachedIndex(String persistentCacheFile, Serializer<K> keySerializer, Serializer<V> valueSerializer, CacheLockingManager cacheLockingManager) {
+
+        this.persistentCacheFile = persistentCacheFile;
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    private PersistentIndexedCache<K, V> getPersistentCache() {
+        if (persistentCache == null) {
+            persistentCache = initPersistentCache();
+        }
+        return persistentCache;
+    }
+
+    private PersistentIndexedCache<K, V> initPersistentCache() {
+        return cacheLockingManager.createCache(persistentCacheFile, keySerializer, valueSerializer);
+    }
+
+    private String operationName(String action) {
+        return String.format("%s artifact resolution cache '%s'", action, persistentCacheFile);
+    }
+
+    public V lookup(final K key) {
+        assertKeyNotNull(key);
+
+        return cacheLockingManager.useCache(operationName("lookup from"), new Factory<V>() {
+            public V create() {
+                V found = getPersistentCache().get(key);
+                if (found == null) {
+                    return null;
+                } else if (found.isMissing() || found.getCachedFile().exists()) {
+                    return found;
+                } else {
+                    clear(key);
+                    return null;
+                }
+            }
+        });
+    }
+
+    protected void storeInternal(final K key, final V entry) {
+        cacheLockingManager.useCache(operationName("store into"), new Runnable() {
+            public void run() {
+                getPersistentCache().put(key, entry);
+            }
+        });
+    }
+
+    protected void assertKeyNotNull(K key) {
+        if (key == null) {
+            throw new IllegalArgumentException("key cannot be null");
+        }
+    }
+
+    protected void assertArtifactFileNotNull(File artifactFile) {
+        if (artifactFile == null) {
+            throw new IllegalArgumentException("artifactFile cannot be null");
+        }
+    }
+
+    public void clear(final K key) {
+        assertKeyNotNull(key);
+        cacheLockingManager.useCache(operationName("clear from"), new Runnable() {
+            public void run() {
+                getPersistentCache().remove(key);
+            }
+        });
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryCachedArtifactIndex.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
new file mode 100644
index 0000000..6c26566
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryCachedArtifactIndex.java
@@ -0,0 +1,110 @@
+/*
+ * 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.resource.cached.ivy;
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactIdentifierSerializer;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.internal.resource.cached.CachedArtifact;
+import org.gradle.internal.resource.cached.CachedArtifactIndex;
+import org.gradle.internal.resource.cached.DefaultCachedArtifact;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArtifactAtRepositoryCachedArtifactIndex extends AbstractCachedIndex<ArtifactAtRepositoryKey, CachedArtifact> implements CachedArtifactIndex {
+    private final BuildCommencedTimeProvider timeProvider;
+
+    public ArtifactAtRepositoryCachedArtifactIndex(String persistentCacheFile, BuildCommencedTimeProvider timeProvider, CacheLockingManager cacheLockingManager) {
+        super(persistentCacheFile, new ArtifactAtRepositoryKeySerializer(), new CachedArtifactSerializer(), cacheLockingManager);
+        this.timeProvider = timeProvider;
+    }
+
+    private DefaultCachedArtifact createEntry(File artifactFile, BigInteger moduleDescriptorHash) {
+        return new DefaultCachedArtifact(artifactFile, timeProvider.getCurrentTime(), moduleDescriptorHash);
+    }
+
+    public void store(final ArtifactAtRepositoryKey key, final File artifactFile, BigInteger moduleDescriptorHash) {
+        assertArtifactFileNotNull(artifactFile);
+        assertKeyNotNull(key);
+        storeInternal(key, createEntry(artifactFile, moduleDescriptorHash));
+    }
+
+    public void storeMissing(ArtifactAtRepositoryKey key, List<String> attemptedLocations, BigInteger descriptorHash) {
+        storeInternal(key, createMissingEntry(attemptedLocations, descriptorHash));
+    }
+
+    CachedArtifact createMissingEntry(List<String> attemptedLocations, BigInteger descriptorHash) {
+        return new DefaultCachedArtifact(attemptedLocations, timeProvider.getCurrentTime(), descriptorHash);
+    }
+
+    private static class ArtifactAtRepositoryKeySerializer implements Serializer<ArtifactAtRepositoryKey> {
+        private final Serializer<ModuleComponentArtifactIdentifier> artifactIdSerializer = new ModuleVersionArtifactIdentifierSerializer();
+
+        public void write(Encoder encoder, ArtifactAtRepositoryKey value) throws Exception {
+            encoder.writeString(value.getRepositoryId());
+            artifactIdSerializer.write(encoder, value.getArtifactId());
+        }
+
+        public ArtifactAtRepositoryKey read(Decoder decoder) throws Exception {
+            String repositoryId = decoder.readString();
+            ModuleComponentArtifactIdentifier artifactIdentifier = artifactIdSerializer.read(decoder);
+            return new ArtifactAtRepositoryKey(repositoryId, artifactIdentifier);
+        }
+    }
+
+    private static class CachedArtifactSerializer implements Serializer<CachedArtifact> {
+        public void write(Encoder encoder, CachedArtifact value) throws Exception {
+            encoder.writeBoolean(value.isMissing());
+            encoder.writeLong(value.getCachedAt());
+            byte[] hash = value.getDescriptorHash().toByteArray();
+            encoder.writeBinary(hash);
+            if (!value.isMissing()) {
+                encoder.writeString(value.getCachedFile().getPath());
+            } else {
+                encoder.writeSmallInt(value.attemptedLocations().size());
+                for (String location : value.attemptedLocations()) {
+                    encoder.writeString(location);
+                }
+            }
+        }
+
+        public CachedArtifact read(Decoder decoder) throws Exception {
+            boolean isMissing = decoder.readBoolean();
+            long createTimestamp = decoder.readLong();
+            byte[] encodedHash = decoder.readBinary();
+            BigInteger hash = new BigInteger(encodedHash);
+            if (!isMissing) {
+                File file = new File(decoder.readString());
+                return new DefaultCachedArtifact(file, createTimestamp, hash);
+            } else {
+                int size = decoder.readSmallInt();
+                List<String> attempted = new ArrayList<String>(size);
+                for (int i = 0; i < size; i++) {
+                    attempted.add(decoder.readString());
+                }
+                return new DefaultCachedArtifact(attempted, createTimestamp, hash);
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryKey.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryKey.java
new file mode 100644
index 0000000..23beef4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/cached/ivy/ArtifactAtRepositoryKey.java
@@ -0,0 +1,56 @@
+/*
+ * 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.resource.cached.ivy;
+
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+
+public class ArtifactAtRepositoryKey {
+    private final String repositoryId;
+    private final ModuleComponentArtifactIdentifier artifactId;
+
+    public ArtifactAtRepositoryKey(String repositoryId, ModuleComponentArtifactIdentifier artifactId) {
+        this.repositoryId = repositoryId;
+        this.artifactId = artifactId;
+    }
+
+    public ModuleComponentArtifactIdentifier getArtifactId() {
+        return artifactId;
+    }
+
+    public String getRepositoryId() {
+        return repositoryId;
+    }
+
+    @Override
+    public String toString() {
+        return repositoryId + ":" + artifactId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof ArtifactAtRepositoryKey)) {
+            return false;
+        }
+        ArtifactAtRepositoryKey other = (ArtifactAtRepositoryKey) o;
+        return repositoryId.equals(other.repositoryId) && artifactId.equals(other.artifactId);
+    }
+
+    @Override
+    public int hashCode() {
+        return repositoryId.hashCode() ^ artifactId.hashCode();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/LocallyAvailableResourceFinderFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/LocallyAvailableResourceFinderFactory.java
new file mode 100644
index 0000000..0459b27
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/LocallyAvailableResourceFinderFactory.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.internal.resource.local.ivy;
+
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetaData;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.api.internal.artifacts.mvnsettings.CannotLocateLocalMavenRepositoryException;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.M2ResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
+import org.gradle.internal.resource.local.CompositeLocallyAvailableResourceFinder;
+import org.gradle.internal.resource.local.LocallyAvailableResourceCandidates;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinderSearchableFileStoreAdapter;
+import org.gradle.internal.Factory;
+import org.gradle.internal.resource.local.FileStoreSearcher;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+public class LocallyAvailableResourceFinderFactory implements Factory<LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>> {
+    private static final Logger LOGGER = LoggerFactory.getLogger(LocallyAvailableResourceFinderFactory.class);
+
+    private final File rootCachesDirectory;
+    private final LocalMavenRepositoryLocator localMavenRepositoryLocator;
+    private final FileStoreSearcher<ModuleComponentArtifactMetaData> fileStore;
+
+    public LocallyAvailableResourceFinderFactory(
+            ArtifactCacheMetaData artifactCacheMetaData, LocalMavenRepositoryLocator localMavenRepositoryLocator, FileStoreSearcher<ModuleComponentArtifactMetaData> fileStore) {
+        this.rootCachesDirectory = artifactCacheMetaData.getCacheDir().getParentFile();
+        this.localMavenRepositoryLocator = localMavenRepositoryLocator;
+        this.fileStore = fileStore;
+    }
+
+    public LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> create() {
+        List<LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>> finders = new LinkedList<LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>>();
+
+        // Order is important here, because they will be searched in that order
+
+        // The current filestore
+        finders.add(new LocallyAvailableResourceFinderSearchableFileStoreAdapter<ModuleComponentArtifactMetaData>(fileStore));
+
+        // 1.8
+        addForPattern(finders, "artifacts-26/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // 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])");
+
+        // 1.1, 1.2
+        addForPattern(finders, "artifacts-14/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // rc-1, 1.0
+        addForPattern(finders, "artifacts-13/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // Milestone 8 and 9
+        addForPattern(finders, "artifacts-8/filestore/[organisation]/[module](/[branch])/[revision]/[type]/*/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // Milestone 7
+        addForPattern(finders, "artifacts-7/artifacts/*/[organisation]/[module](/[branch])/[revision]/[type]/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // Milestone 6
+        addForPattern(finders, "artifacts-4/[organisation]/[module](/[branch])/*/[type]s/[artifact]-[revision](-[classifier])(.[ext])");
+        addForPattern(finders, "artifacts-4/[organisation]/[module](/[branch])/*/pom.originals/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // Milestone 3
+        addForPattern(finders, "../cache/[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])");
+
+        // Maven local
+        try {
+            File localMavenRepository = localMavenRepositoryLocator.getLocalMavenRepository();
+            if (localMavenRepository.exists()) {
+                addForPattern(finders, localMavenRepository, new M2ResourcePattern("[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"));
+            }
+        } catch (CannotLocateLocalMavenRepositoryException ex) {
+            finders.add(new NoMavenLocalRepositoryResourceFinder(ex));
+        }
+        return new CompositeLocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>(finders);
+    }
+
+    private void addForPattern(List<LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>> finders, String pattern) {
+        int wildcardPos = pattern.indexOf("/*/");
+        int patternPos = pattern.indexOf("/[");
+        if (wildcardPos < 0 && patternPos < 0) {
+            throw new IllegalArgumentException(String.format("Unsupported pattern '%s'", pattern));
+        }
+        int chopAt;
+        if (wildcardPos >= 0 && patternPos >= 0) {
+            chopAt = Math.min(wildcardPos, patternPos);
+        } else if (wildcardPos >= 0) {
+            chopAt = wildcardPos;
+        } else {
+            chopAt = patternPos;
+        }
+        String pathPart = pattern.substring(0, chopAt);
+        String patternPart = pattern.substring(chopAt + 1);
+        addForPattern(finders, new File(rootCachesDirectory, pathPart), new IvyResourcePattern(patternPart));
+    }
+
+    private void addForPattern(List<LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData>> finders, File baseDir, ResourcePattern pattern) {
+        if (baseDir.exists()) {
+            finders.add(new PatternBasedLocallyAvailableResourceFinder(baseDir, pattern));
+        }
+    }
+
+    private class NoMavenLocalRepositoryResourceFinder implements LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> {
+        private final CannotLocateLocalMavenRepositoryException ex;
+        private boolean logged;
+
+        public NoMavenLocalRepositoryResourceFinder(CannotLocateLocalMavenRepositoryException ex) {
+            this.ex = ex;
+        }
+
+        public LocallyAvailableResourceCandidates findCandidates(ModuleComponentArtifactMetaData criterion) {
+            if(!logged){
+                LOGGER.warn("Unable to locate local Maven repository.");
+                LOGGER.debug("Problems while locating local Maven repository.", ex);
+                logged = true;
+            }
+            return new LocallyAvailableResourceCandidates() {
+                public boolean isNone() {
+                    return true;
+                }
+
+                public LocallyAvailableResource findByHashValue(HashValue hashValue) {
+                    return null;
+                }
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/PatternBasedLocallyAvailableResourceFinder.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/PatternBasedLocallyAvailableResourceFinder.java
new file mode 100644
index 0000000..013fab2
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/local/ivy/PatternBasedLocallyAvailableResourceFinder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.local.ivy;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.file.EmptyFileVisitor;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
+import org.gradle.internal.resource.local.AbstractLocallyAvailableResourceFinder;
+import org.gradle.api.internal.file.collections.MinimalFileTree;
+import org.gradle.api.internal.file.collections.SingleIncludePatternFileTree;
+import org.gradle.internal.Factory;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+public class PatternBasedLocallyAvailableResourceFinder extends AbstractLocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> {
+
+    public PatternBasedLocallyAvailableResourceFinder(File baseDir, ResourcePattern pattern) {
+        super(createProducer(baseDir, pattern));
+    }
+
+    private static Transformer<Factory<List<File>>, ModuleComponentArtifactMetaData> createProducer(final File baseDir, final ResourcePattern pattern) {
+        return new Transformer<Factory<List<File>>, ModuleComponentArtifactMetaData>() {
+            public Factory<List<File>> transform(final ModuleComponentArtifactMetaData artifact) {
+                return new Factory<List<File>>() {
+                    public List<File> create() {
+                        final List<File> files = new LinkedList<File>();
+                        if (artifact != null) {
+                            getMatchingFiles(artifact).visit(new EmptyFileVisitor() {
+                                public void visitFile(FileVisitDetails fileDetails) {
+                                    files.add(fileDetails.getFile());
+                                }
+                            });
+                        }
+                        return files;
+                    }
+                };
+            }
+
+            private MinimalFileTree getMatchingFiles(ModuleComponentArtifactMetaData artifact) {
+                String patternString = pattern.getLocation(artifact).getPath();
+                return new SingleIncludePatternFileTree(baseDir, patternString);
+            }
+
+        };
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/AbstractProgressLoggingHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/AbstractProgressLoggingHandler.java
new file mode 100644
index 0000000..3dee6d5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/AbstractProgressLoggingHandler.java
@@ -0,0 +1,80 @@
+/*
+ * 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.resource.transfer;
+
+import org.gradle.logging.ProgressLogger;
+import org.gradle.logging.ProgressLoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AbstractProgressLoggingHandler {
+    protected final ProgressLoggerFactory progressLoggerFactory;
+
+    public AbstractProgressLoggingHandler(ProgressLoggerFactory progressLoggerFactory) {
+        this.progressLoggerFactory = progressLoggerFactory;
+    }
+
+    protected ResourceOperation createResourceOperation(String resourceName, ResourceOperation.Type operationType, Class loggingClazz, long contentLength) {
+        ProgressLogger progressLogger = startProgress(String.format("%s %s", operationType.getCapitalized(), resourceName), loggingClazz);
+        return new ResourceOperation(progressLogger, operationType, contentLength);
+    }
+
+    private ProgressLogger startProgress(String description, Class loggingClass) {
+        ProgressLogger progressLogger = progressLoggerFactory.newOperation(loggingClass != null ? loggingClass : getClass());
+        progressLogger.setDescription(description);
+        progressLogger.setLoggingHeader(description);
+        progressLogger.started();
+        return progressLogger;
+    }
+
+    protected static class ProgressLoggingInputStream extends InputStream {
+        private final InputStream inputStream;
+        private final ResourceOperation resourceOperation;
+
+        public ProgressLoggingInputStream(InputStream inputStream, ResourceOperation resourceOperation) {
+            this.inputStream = inputStream;
+            this.resourceOperation = resourceOperation;
+        }
+
+        @Override
+        public void close() throws IOException {
+            inputStream.close();
+        }
+
+        @Override
+        public int read() throws IOException {
+            int result = inputStream.read();
+            if (result >= 0) {
+                doLogProgress(1);
+            }
+            return result;
+        }
+
+        public int read(byte[] b, int off, int len) throws IOException {
+            int read = inputStream.read(b, off, len);
+            if (read > 0) {
+                doLogProgress(read);
+            }
+            return read;
+        }
+
+        private void doLogProgress(long numberOfBytes) {
+            resourceOperation.logProcessedBytes(numberOfBytes);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/CacheAwareExternalResourceAccessor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/CacheAwareExternalResourceAccessor.java
new file mode 100644
index 0000000..a595318
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/CacheAwareExternalResourceAccessor.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.internal.resource.transfer;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableResourceCandidates;
+import org.gradle.internal.resource.local.LocallyAvailableResource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+
+public interface CacheAwareExternalResourceAccessor {
+    @Nullable
+    LocallyAvailableExternalResource getResource(URI source, ResourceFileStore fileStore, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException;
+
+    interface ResourceFileStore {
+        /**
+         * Called when a resource is to be cached. Should *move* the given file into the appropriate location and return a handle to the file.
+         */
+        LocallyAvailableResource moveIntoCache(File downloadedResource);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessor.java
new file mode 100644
index 0000000..f393725
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessor.java
@@ -0,0 +1,237 @@
+/*
+ * 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.resource.transfer;
+
+import com.google.common.io.Files;
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultExternalResourceCachePolicy;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.ExternalResourceCachePolicy;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.Factory;
+import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.cached.CachedExternalResource;
+import org.gradle.internal.resource.cached.CachedExternalResourceIndex;
+import org.gradle.internal.resource.local.*;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaDataCompare;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.gradle.util.BuildCommencedTimeProvider;
+import org.gradle.util.GFileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class DefaultCacheAwareExternalResourceAccessor implements CacheAwareExternalResourceAccessor {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCacheAwareExternalResourceAccessor.class);
+
+    private final ExternalResourceRepository delegate;
+    private final CachedExternalResourceIndex<String> cachedExternalResourceIndex;
+    private final BuildCommencedTimeProvider timeProvider;
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final CacheLockingManager cacheLockingManager;
+    private final ExternalResourceCachePolicy externalResourceCachePolicy = new DefaultExternalResourceCachePolicy();
+
+    public DefaultCacheAwareExternalResourceAccessor(ExternalResourceRepository delegate, CachedExternalResourceIndex<String> cachedExternalResourceIndex, BuildCommencedTimeProvider timeProvider, TemporaryFileProvider temporaryFileProvider, CacheLockingManager cacheLockingManager) {
+        this.delegate = delegate;
+        this.cachedExternalResourceIndex = cachedExternalResourceIndex;
+        this.timeProvider = timeProvider;
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.cacheLockingManager = cacheLockingManager;
+    }
+
+    public LocallyAvailableExternalResource getResource(final URI location, final ResourceFileStore fileStore, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException {
+        LOGGER.debug("Constructing external resource: {}", location);
+        CachedExternalResource cached = cachedExternalResourceIndex.lookup(location.toString());
+
+        // If we have no caching options, just get the thing directly
+        if (cached == null && (localCandidates == null || localCandidates.isNone())) {
+            return copyToCache(location, fileStore, delegate.withProgressLogging().getResource(location));
+        }
+
+        // We might be able to use a cached/locally available version
+        if (cached != null && !externalResourceCachePolicy.mustRefreshExternalResource(getAgeMillis(timeProvider, cached))) {
+            return new DefaultLocallyAvailableExternalResource(location, new DefaultLocallyAvailableResource(cached.getCachedFile()), cached.getExternalResourceMetaData());
+        }
+
+        // Get the metadata first to see if it's there
+        final ExternalResourceMetaData remoteMetaData = delegate.getResourceMetaData(location);
+        if (remoteMetaData == null) {
+            return null;
+        }
+
+        // Is the cached version still current?
+        if (cached != null) {
+            boolean isUnchanged = ExternalResourceMetaDataCompare.isDefinitelyUnchanged(
+                    cached.getExternalResourceMetaData(),
+                    new Factory<ExternalResourceMetaData>() {
+                        public ExternalResourceMetaData create() {
+                            return remoteMetaData;
+                        }
+                    }
+            );
+
+            if (isUnchanged) {
+                LOGGER.info("Cached resource {} is up-to-date (lastModified: {}).", cached.getExternalLastModified(), location);
+                // TODO - update the index with the new remote meta-data
+                return new DefaultLocallyAvailableExternalResource(location, new DefaultLocallyAvailableResource(cached.getCachedFile()), cached.getExternalResourceMetaData());
+            }
+        }
+
+        // Either no cached, or it's changed. See if we can find something local with the same checksum
+        boolean hasLocalCandidates = localCandidates != null && !localCandidates.isNone();
+        if (hasLocalCandidates) {
+            // The “remote” may have already given us the checksum
+            HashValue remoteChecksum = remoteMetaData.getSha1();
+
+            if (remoteChecksum == null) {
+                remoteChecksum = getResourceSha1(location);
+            }
+
+            if (remoteChecksum != null) {
+                LocallyAvailableResource local = localCandidates.findByHashValue(remoteChecksum);
+                if (local != null) {
+                    LOGGER.info("Found locally available resource with matching checksum: [{}, {}]", location, local.getFile());
+                    // TODO - should iterate over each candidate until we successfully copy into the cache
+                    LocallyAvailableExternalResource resource = copyCandidateToCache(location, fileStore, remoteMetaData, remoteChecksum, local);
+                    if (resource != null) {
+                        return resource;
+                    }
+                }
+            }
+        }
+
+        // All local/cached options failed, get directly
+        return copyToCache(location, fileStore, delegate.withProgressLogging().getResource(location));
+    }
+
+    private HashValue getResourceSha1(URI location) {
+        try {
+            URI sha1Location = new URI(location.toASCIIString() + ".sha1");
+            ExternalResource resource = delegate.getResource(sha1Location);
+            if (resource == null) {
+                return null;
+            }
+            try {
+                return resource.withContent(new Transformer<HashValue, InputStream>() {
+                    @Override
+                    public HashValue transform(InputStream inputStream) {
+                        try {
+                            String sha = IOUtils.toString(inputStream, "us-ascii");
+                            return HashValue.parse(sha);
+                        } catch (IOException e) {
+                            throw new UncheckedIOException(e);
+                        }
+                    }
+                });
+            } finally {
+                resource.close();
+            }
+        } catch (Exception e) {
+            throw new ResourceException(location, String.format("Failed to download SHA1 for resource '%s'.", location), e);
+        }
+    }
+
+    private LocallyAvailableExternalResource copyCandidateToCache(URI source, ResourceFileStore fileStore, ExternalResourceMetaData remoteMetaData, HashValue remoteChecksum, LocallyAvailableResource local) throws IOException {
+        final File destination = temporaryFileProvider.createTemporaryFile("gradle_download", "bin");
+        try {
+            Files.copy(local.getFile(), destination);
+            HashValue localChecksum = HashUtil.createHash(destination, "SHA1");
+            if (!localChecksum.equals(remoteChecksum)) {
+                return null;
+            }
+            return moveIntoCache(source, destination, fileStore, remoteMetaData);
+        } finally {
+            destination.delete();
+        }
+    }
+
+    private LocallyAvailableExternalResource copyToCache(URI source, ResourceFileStore fileStore, ExternalResource resource) {
+        if (resource == null) {
+            return null;
+        }
+
+        final File destination = temporaryFileProvider.createTemporaryFile("gradle_download", "bin");
+        try {
+            final DownloadToFileAction downloadAction = new DownloadToFileAction(destination);
+            try {
+                try {
+                    LOGGER.debug("Downloading {} to {}", source, destination);
+                    if (destination.getParentFile() != null) {
+                        GFileUtils.mkdirs(destination.getParentFile());
+                    }
+                    resource.withContent(downloadAction);
+                } finally {
+                    resource.close();
+                }
+            } catch (Exception e) {
+                throw ResourceException.failure(source, String.format("Failed to download resource '%s'.", source), e);
+            }
+            return moveIntoCache(source, destination, fileStore, downloadAction.metaData);
+        } finally {
+            destination.delete();
+        }
+    }
+
+    private LocallyAvailableExternalResource moveIntoCache(final URI source, final File destination, final ResourceFileStore fileStore, final ExternalResourceMetaData metaData) {
+        return cacheLockingManager.useCache(String.format("Store %s", source), new Factory<LocallyAvailableExternalResource>() {
+            public LocallyAvailableExternalResource create() {
+                LocallyAvailableResource cachedResource = fileStore.moveIntoCache(destination);
+                File fileInFileStore = cachedResource.getFile();
+                cachedExternalResourceIndex.store(source.toString(), fileInFileStore, metaData);
+                return new DefaultLocallyAvailableExternalResource(source, cachedResource, metaData);
+            }
+        });
+    }
+
+    public long getAgeMillis(BuildCommencedTimeProvider timeProvider, CachedExternalResource cached) {
+        return timeProvider.getCurrentTime() - cached.getCachedAt();
+    }
+
+    private static class DownloadToFileAction implements ExternalResource.ContentAction<Object> {
+        private final File destination;
+        private ExternalResourceMetaData metaData;
+
+        public DownloadToFileAction(File destination) {
+            this.destination = destination;
+        }
+
+        @Override
+        public Object execute(InputStream inputStream, ExternalResourceMetaData metaData) throws IOException {
+            this.metaData = metaData;
+            FileOutputStream outputStream = new FileOutputStream(destination);
+            try {
+                IOUtils.copyLarge(inputStream, outputStream);
+            } finally {
+                outputStream.close();
+            }
+            return null;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessor.java
new file mode 100644
index 0000000..c3d28ea
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessor.java
@@ -0,0 +1,84 @@
+/*
+ * 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.resource.transfer;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.logging.ProgressLoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class ProgressLoggingExternalResourceAccessor extends AbstractProgressLoggingHandler implements ExternalResourceAccessor {
+    private final ExternalResourceAccessor delegate;
+
+    public ProgressLoggingExternalResourceAccessor(ExternalResourceAccessor delegate, ProgressLoggerFactory progressLoggerFactory) {
+        super(progressLoggerFactory);
+        this.delegate = delegate;
+    }
+
+    public ExternalResourceReadResponse openResource(URI location) {
+        ExternalResourceReadResponse resource = delegate.openResource(location);
+        if (resource != null) {
+            return new ProgressLoggingExternalResource(location, resource);
+        } else {
+            return null;
+        }
+    }
+
+    @Nullable
+    public ExternalResourceMetaData getMetaData(URI location) {
+        return delegate.getMetaData(location);
+    }
+
+    private class ProgressLoggingExternalResource implements ExternalResourceReadResponse {
+        private final ExternalResourceReadResponse resource;
+        private final ResourceOperation downloadOperation;
+
+        private ProgressLoggingExternalResource(URI location, ExternalResourceReadResponse resource) {
+            this.resource = resource;
+            downloadOperation = createResourceOperation(location.toString(), ResourceOperation.Type.download, getClass(), resource.getMetaData().getContentLength());
+        }
+
+        @Override
+        public InputStream openStream() throws IOException {
+            return new ProgressLoggingInputStream(resource.openStream(), downloadOperation);
+        }
+
+        public void close() throws IOException {
+            try {
+                resource.close();
+            } finally {
+                downloadOperation.completed();
+            }
+        }
+
+        @Nullable
+        public ExternalResourceMetaData getMetaData() {
+            return resource.getMetaData();
+        }
+
+        public boolean isLocal() {
+            return resource.isLocal();
+        }
+
+        public String toString(){
+            return resource.toString();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploader.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploader.java
new file mode 100644
index 0000000..d941694
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploader.java
@@ -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.internal.resource.transfer;
+
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.logging.ProgressLoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class ProgressLoggingExternalResourceUploader extends AbstractProgressLoggingHandler implements ExternalResourceUploader {
+    private final ExternalResourceUploader delegate;
+
+    public ProgressLoggingExternalResourceUploader(ExternalResourceUploader delegate, ProgressLoggerFactory progressLoggerFactory) {
+        super(progressLoggerFactory);
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void upload(final LocalResource resource, URI destination) throws IOException {
+        final ResourceOperation uploadOperation = createResourceOperation(destination.toString(), ResourceOperation.Type.upload, getClass(), resource.getContentLength());
+
+        try {
+            delegate.upload(new ProgressLoggingLocalResource(resource, uploadOperation), destination);
+        } finally {
+            uploadOperation.completed();
+        }
+    }
+
+    private class ProgressLoggingLocalResource implements LocalResource {
+        private final LocalResource delegate;
+        private final ResourceOperation uploadOperation;
+
+        private ProgressLoggingLocalResource(LocalResource delegate, ResourceOperation uploadOperation) {
+            this.delegate = delegate;
+            this.uploadOperation = uploadOperation;
+        }
+
+        public InputStream open() {
+            return new ProgressLoggingInputStream(delegate.open(), uploadOperation);
+        }
+
+        public long getContentLength() {
+            return delegate.getContentLength();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ResourceOperation.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ResourceOperation.java
new file mode 100644
index 0000000..b131d85
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transfer/ResourceOperation.java
@@ -0,0 +1,70 @@
+/*
+ * 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.resource.transfer;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.logging.ProgressLogger;
+
+public class ResourceOperation {
+    public enum Type{
+        download,
+        upload;
+
+        public String getCapitalized() {
+            return StringUtils.capitalize(toString());
+        }
+    }
+    private final ProgressLogger progressLogger;
+    private final Type operationType;
+    private final String contentLengthString;
+
+    private long loggedKBytes;
+    private long totalProcessedBytes;
+
+    public ResourceOperation(ProgressLogger progressLogger, Type type, long contentLength) {
+        this.progressLogger = progressLogger;
+        this.operationType = type;
+        this.contentLengthString = getLengthText(contentLength != 0 ? contentLength : null);
+    }
+
+    private String getLengthText(Long bytes) {
+        if (bytes == null) {
+            return "unknown size";
+        }
+        if (bytes < 1024) {
+            return bytes + " B";
+        } else if (bytes < 1048576) {
+            return (bytes / 1024) + " KB";
+        } else {
+            return String.format("%.2f MB", bytes / 1048576.0);
+        }
+    }
+
+    public void logProcessedBytes(long processedBytes) {
+        totalProcessedBytes += processedBytes;
+        long processedKB = totalProcessedBytes / 1024;
+        if (processedKB > loggedKBytes) {
+            loggedKBytes = processedKB;
+            final String progressMessage = String.format("%s/%s %sed", getLengthText(totalProcessedBytes), contentLengthString, operationType);
+            progressLogger.progress(progressMessage);
+        }
+    }
+
+    public void completed() {
+        this.progressLogger.completed();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/AbstractRepositoryTransport.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/AbstractRepositoryTransport.java
new file mode 100644
index 0000000..e16674a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/AbstractRepositoryTransport.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.internal.resource.transport;
+
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+
+public abstract class AbstractRepositoryTransport implements RepositoryTransport {
+    protected final String name;
+
+    protected AbstractRepositoryTransport(String name) {
+        this.name = name;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/DefaultExternalResourceRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/DefaultExternalResourceRepository.java
new file mode 100644
index 0000000..dd6d0a6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/DefaultExternalResourceRepository.java
@@ -0,0 +1,84 @@
+/*
+ * 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.resource.transport;
+
+
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.URI;
+import java.util.List;
+
+public class DefaultExternalResourceRepository implements ExternalResourceRepository {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExternalResourceRepository.class);
+    private final String name;
+    private final ExternalResourceAccessor accessor;
+    private final ExternalResourceUploader uploader;
+    private final ExternalResourceLister lister;
+    private final ExternalResourceAccessor loggingAccessor;
+    private final ExternalResourceUploader loggingUploader;
+
+    public DefaultExternalResourceRepository(String name,
+                                             ExternalResourceAccessor accessor,
+                                             ExternalResourceUploader uploader,
+                                             ExternalResourceLister lister,
+                                             ExternalResourceAccessor loggingAccessor,
+                                             ExternalResourceUploader loggingUploader) {
+        this.name = name;
+        this.accessor = accessor;
+        this.uploader = uploader;
+        this.lister = lister;
+        this.loggingAccessor = loggingAccessor;
+        this.loggingUploader = loggingUploader;
+    }
+
+    @Override
+    public ExternalResourceRepository withProgressLogging() {
+        if (loggingAccessor == accessor && loggingUploader == uploader) {
+            return this;
+        }
+        return new DefaultExternalResourceRepository(name, loggingAccessor, loggingUploader, lister, loggingAccessor, loggingUploader);
+    }
+
+    public ExternalResource getResource(URI source) {
+        ExternalResourceReadResponse response = accessor.openResource(source);
+        return response == null ? null : new DefaultExternalResource(source, response);
+    }
+
+    public ExternalResourceMetaData getResourceMetaData(URI source) {
+        return accessor.getMetaData(source);
+    }
+
+    public void put(LocalResource source, URI destination) throws IOException {
+        LOGGER.debug("Attempting to put resource {}.", destination);
+        uploader.upload(source, destination);
+    }
+
+    public List<String> list(URI parent) {
+        return lister.list(parent);
+    }
+
+    public String toString() {
+        return name;
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ExternalResourceRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ExternalResourceRepository.java
new file mode 100644
index 0000000..0ef28db
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ExternalResourceRepository.java
@@ -0,0 +1,72 @@
+/*
+ * 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.resource.transport;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+public interface ExternalResourceRepository {
+    /**
+     * Returns a copy of this repository with progress logging enabled.
+     */
+    ExternalResourceRepository withProgressLogging();
+
+    /**
+     * Attempts to fetch the given resource.
+     *
+     * @return null if the resource is not found.
+     * @throws ResourceException On failure to fetch resource.
+     */
+    @Nullable
+    ExternalResource getResource(URI source) throws ResourceException;
+
+    /**
+     * Transfer a resource to the repository
+     *
+     * @param source The local resource to be transferred.
+     * @param destination Where to transfer the resource.
+     * @throws IOException On publication failure.
+     */
+    void put(LocalResource source, URI destination) throws IOException;
+
+    /**
+     * Fetches only the metadata for the result.
+     *
+     * @param source The location of the resource to obtain the metadata for
+     * @return The resource metadata, or null if the resource does not exist
+     * @throws ResourceException On failure to fetch resource metadata.
+     */
+    @Nullable
+    ExternalResourceMetaData getResourceMetaData(URI source) throws ResourceException;
+
+    /**
+     * Return a listing of child resources names.
+     *
+     * @param parent The parent directory from which to generate the listing.
+     * @return A listing of the direct children of the given parent. Returns null when the parent resource does not exist.
+     * @throws ResourceException On listing failure.
+     */
+    @Nullable
+    List<String> list(URI parent) throws ResourceException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ResourceConnectorRepositoryTransport.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ResourceConnectorRepositoryTransport.java
new file mode 100644
index 0000000..13733bd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/ResourceConnectorRepositoryTransport.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport;
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager;
+import org.gradle.api.internal.file.TemporaryFileProvider;
+import org.gradle.internal.resource.cached.CachedExternalResourceIndex;
+import org.gradle.internal.resource.transfer.*;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.util.BuildCommencedTimeProvider;
+
+public class ResourceConnectorRepositoryTransport extends AbstractRepositoryTransport {
+    private final ExternalResourceRepository repository;
+    private final DefaultCacheAwareExternalResourceAccessor resourceAccessor;
+
+    public ResourceConnectorRepositoryTransport(String name,
+                                                ProgressLoggerFactory progressLoggerFactory,
+                                                TemporaryFileProvider temporaryFileProvider,
+                                                CachedExternalResourceIndex<String> cachedExternalResourceIndex,
+                                                BuildCommencedTimeProvider timeProvider,
+                                                CacheLockingManager cacheLockingManager,
+                                                ExternalResourceConnector connector) {
+        super(name);
+        ProgressLoggingExternalResourceUploader loggingUploader = new ProgressLoggingExternalResourceUploader(connector, progressLoggerFactory);
+        ProgressLoggingExternalResourceAccessor loggingAccessor = new ProgressLoggingExternalResourceAccessor(connector, progressLoggerFactory);
+        repository = new DefaultExternalResourceRepository(name, connector, connector, connector, loggingAccessor, loggingUploader);
+        resourceAccessor = new DefaultCacheAwareExternalResourceAccessor(repository, cachedExternalResourceIndex, timeProvider, temporaryFileProvider, cacheLockingManager);
+    }
+
+    public ExternalResourceRepository getRepository() {
+        return repository;
+    }
+
+    public CacheAwareExternalResourceAccessor getResourceAccessor() {
+        return resourceAccessor;
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileResourceConnector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileResourceConnector.java
new file mode 100644
index 0000000..b687c0b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileResourceConnector.java
@@ -0,0 +1,90 @@
+/*
+ * 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.resource.transport.file;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+public class FileResourceConnector implements ExternalResourceRepository {
+    @Override
+    public ExternalResourceRepository withProgressLogging() {
+        return this;
+    }
+
+    public List<String> list(URI parent) {
+        File dir = getFile(parent);
+        if (dir.exists() && dir.isDirectory()) {
+            String[] names = dir.list();
+            if (names != null) {
+                return Arrays.asList(names);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void put(LocalResource source, URI destination) throws IOException {
+        File target = getFile(destination);
+        if (!target.canWrite()) {
+            target.delete();
+        } // if target is writable, the copy will overwrite it without requiring a delete
+        GFileUtils.mkdirs(target.getParentFile());
+
+        InputStream input = source.open();
+        try {
+            FileOutputStream output = new FileOutputStream(target);
+            try {
+                IOUtils.copyLarge(input, output);
+            } finally {
+                output.close();
+            }
+        } finally {
+            input.close();
+        }
+    }
+
+    public LocallyAvailableExternalResource getResource(URI uri) {
+        File localFile = getFile(uri);
+        if (!localFile.exists()) {
+            return null;
+        }
+        return new DefaultLocallyAvailableExternalResource(uri, new DefaultLocallyAvailableResource(localFile));
+    }
+
+    public ExternalResourceMetaData getResourceMetaData(URI location) {
+        ExternalResource resource = getResource(location);
+        return resource == null ? null : resource.getMetaData();
+    }
+
+    private static File getFile(URI uri) {
+        return new File(uri);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileTransport.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileTransport.java
new file mode 100644
index 0000000..88de129
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resource/transport/file/FileTransport.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.internal.resource.transport.file;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource;
+import org.gradle.internal.resource.local.LocallyAvailableResourceCandidates;
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor;
+import org.gradle.internal.resource.transport.AbstractRepositoryTransport;
+import org.gradle.internal.resource.transport.ExternalResourceRepository;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class FileTransport extends AbstractRepositoryTransport {
+    private final FileResourceConnector repository;
+    private final NoOpCacheAwareExternalResourceAccessor resourceAccessor;
+
+    public FileTransport(String name) {
+        super(name);
+        repository = new FileResourceConnector();
+        resourceAccessor = new NoOpCacheAwareExternalResourceAccessor(repository);
+    }
+
+    public boolean isLocal() {
+        return true;
+    }
+
+    public ExternalResourceRepository getRepository() {
+        return repository;
+    }
+
+    public CacheAwareExternalResourceAccessor getResourceAccessor() {
+        return resourceAccessor;
+    }
+
+    private static class NoOpCacheAwareExternalResourceAccessor implements CacheAwareExternalResourceAccessor {
+        private final FileResourceConnector connector;
+
+        public NoOpCacheAwareExternalResourceAccessor(FileResourceConnector connector) {
+            this.connector = connector;
+        }
+
+        public LocallyAvailableExternalResource getResource(URI source, ResourceFileStore fileStore, @Nullable LocallyAvailableResourceCandidates localCandidates) throws IOException {
+            return connector.getResource(source);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/ClosureBackedRuleAction.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/ClosureBackedRuleAction.java
new file mode 100644
index 0000000..23bb1b2
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/ClosureBackedRuleAction.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import com.google.common.collect.Lists;
+import groovy.lang.Closure;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ClosureBackedRuleAction<T> implements RuleAction<T> {
+    private final Closure closure;
+    private final Class<? super T> subjectType;
+    private List<Class<?>> inputTypes;
+
+    public ClosureBackedRuleAction(Class<T> subjectType, Closure<?> closure) {
+        this.subjectType = subjectType;
+        this.closure = closure;
+        this.inputTypes = parseInputTypes(closure);
+    }
+
+    public List<Class<?>> getInputTypes() {
+        return inputTypes;
+    }
+
+    public void execute(T subject, List<?> inputs) {
+        Closure copy = (Closure) closure.clone();
+        copy.setResolveStrategy(Closure.DELEGATE_FIRST);
+        copy.setDelegate(subject);
+
+        if (closure.getMaximumNumberOfParameters() == 0) {
+            copy.call();
+        } else {
+            Object[] argList = new Object[inputs.size() + 1];
+            argList[0] = subject;
+            int i = 1;
+            for (Object arg : inputs) {
+                argList[i++] = arg;
+            }
+            copy.call(argList);
+        }
+    }
+
+    private List<Class<?>> parseInputTypes(Closure<?> closure) {
+        Class<?>[] parameterTypes = closure.getParameterTypes();
+        List<Class<?>> inputTypes = Lists.newArrayList();
+
+        if (parameterTypes.length != 0) {
+            if (parameterTypes[0].isAssignableFrom(subjectType)) {
+                for (Class<?> parameterType : Arrays.asList(parameterTypes).subList(1, parameterTypes.length)) {
+                    inputTypes.add(parameterType);
+                }
+            } else {
+                throw new RuleActionValidationException(String.format("First parameter of rule action closure must be of type '%s'.", subjectType.getSimpleName()));
+            }
+        }
+
+        return inputTypes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ClosureBackedRuleAction that = (ClosureBackedRuleAction) o;
+        return closure.equals(that.closure)
+                && subjectType.equals(that.subjectType);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = closure.hashCode();
+        result = 31 * result + subjectType.hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionAdapter.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionAdapter.java
new file mode 100644
index 0000000..af6f676
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionAdapter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.model.internal.type.ModelType;
+
+public class DefaultRuleActionAdapter<T> implements RuleActionAdapter<T> {
+    private static final String INVALID_CLOSURE_ERROR = "The closure provided is not valid as a rule for '%s'.";
+    private static final String INVALID_ACTION_ERROR = "The action provided is not valid as a rule for '%s'.";
+    private static final String INVALID_RULE_SOURCE_ERROR = "The rule source provided does not provide a valid rule for '%s'.";
+
+    private final RuleActionValidator<T> ruleActionValidator;
+    private final String context;
+
+    public DefaultRuleActionAdapter(RuleActionValidator<T> ruleActionValidator, String context) {
+        this.ruleActionValidator = ruleActionValidator;
+        this.context = context;
+    }
+
+    public RuleAction<? super T> createFromClosure(Class<T> subjectType, Closure<?> closure) {
+        try {
+            return ruleActionValidator.validate(new ClosureBackedRuleAction<T>(subjectType, closure));
+        } catch (RuleActionValidationException e) {
+            throw new InvalidUserCodeException(String.format(INVALID_CLOSURE_ERROR, context), e);
+        }
+    }
+
+    public RuleAction<? super T> createFromAction(Action<? super T> action) {
+        try {
+            return ruleActionValidator.validate(new NoInputsRuleAction<T>(action));
+        } catch (RuleActionValidationException e) {
+            throw new InvalidUserCodeException(String.format(INVALID_ACTION_ERROR, context), e);
+        }
+    }
+
+    public RuleAction<? super T> createFromRuleSource(Class<T> subjectType, Object ruleSource) {
+        try {
+            return ruleActionValidator.validate(RuleSourceBackedRuleAction.create(ModelType.of(subjectType), ruleSource));
+        } catch (RuleActionValidationException e) {
+            throw new InvalidUserCodeException(String.format(INVALID_RULE_SOURCE_ERROR, context), e);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionValidator.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionValidator.java
new file mode 100644
index 0000000..b3ccc86
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/DefaultRuleActionValidator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import org.gradle.api.Transformer;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+public class DefaultRuleActionValidator<T> implements RuleActionValidator<T> {
+    private static final String VALID_SINGLE_TYPES = "Rule may not have an input parameter of type: %s. Second parameter must be of type: %s.";
+    private static final String VALID_MULTIPLE_TYPES = "Rule may not have an input parameter of type: %s. Valid types (for the second and subsequent parameters) are: %s.";
+
+    private final List<Class<?>> validInputTypes;
+
+    public DefaultRuleActionValidator(List<Class<?>> validInputTypes) {
+        this.validInputTypes = validInputTypes;
+    }
+
+    public RuleAction<? super T> validate(RuleAction<? super T> ruleAction) {
+        validateInputTypes(ruleAction);
+        return ruleAction;
+    }
+
+    private void validateInputTypes(RuleAction<? super T> ruleAction) {
+        for (Class<?> inputType : ruleAction.getInputTypes()) {
+            if (!validInputTypes.contains(inputType)) {
+                throw new RuleActionValidationException(invalidParameterMessage(inputType));
+            }
+        }
+    }
+
+    private String invalidParameterMessage(Class<?> inputType) {
+        if (validInputTypes.size() == 1) {
+            return String.format(VALID_SINGLE_TYPES, inputType.getName(), className(validInputTypes.get(0)));
+        }
+        return String.format(VALID_MULTIPLE_TYPES, inputType.getName(),
+                             CollectionUtils.collect(validInputTypes, new ClassNameTransformer()));
+    }
+
+    private static String className(Class<?> aClass) {
+        return ModelType.of(aClass).toString();
+    }
+
+    private static class ClassNameTransformer implements Transformer<String, Class<?>> {
+        public String transform(Class<?> aClass) {
+            return className(aClass);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/NoInputsRuleAction.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/NoInputsRuleAction.java
new file mode 100644
index 0000000..fed4d57
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/NoInputsRuleAction.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import org.gradle.api.Action;
+
+import java.util.Collections;
+import java.util.List;
+
+public class NoInputsRuleAction<T> implements RuleAction<T> {
+    private final Action<? super T> action;
+
+    public NoInputsRuleAction(Action<? super T> action) {
+        this.action = action;
+    }
+
+    public List<Class<?>> getInputTypes() {
+        return Collections.emptyList();
+    }
+
+    public void execute(T subject, List<?> inputs) {
+        action.execute(subject);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        NoInputsRuleAction that = (NoInputsRuleAction) o;
+        return action.equals(that.action);
+    }
+
+    @Override
+    public int hashCode() {
+        return action.hashCode();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleAction.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleAction.java
new file mode 100644
index 0000000..03ad51d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleAction.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * An action representing a rule, taking declared inputs and performing an action on a subject.
+ *
+ * @param <T> The subject type
+ */
+ at Incubating
+public interface RuleAction<T> {
+    List<Class<?>> getInputTypes();
+    void execute(T subject, List<?> inputs);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionAdapter.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionAdapter.java
new file mode 100644
index 0000000..0dbbbaa
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+
+public interface RuleActionAdapter<T> {
+    RuleAction<? super T> createFromClosure(Class<T> subjectType, Closure<?> closure);
+
+    RuleAction<? super T> createFromAction(Action<? super T> action);
+
+    RuleAction<? super T> createFromRuleSource(Class<T> subjectType, Object ruleSource);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidationException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidationException.java
new file mode 100644
index 0000000..dd5d324
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidationException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+
+ at Incubating
+public class RuleActionValidationException extends GradleException {
+    public RuleActionValidationException() {
+    }
+
+    public RuleActionValidationException(String message) {
+        super(message);
+    }
+
+    public RuleActionValidationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidator.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidator.java
new file mode 100644
index 0000000..61dba63
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleActionValidator.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+public interface RuleActionValidator<T> {
+    public RuleAction<? super T> validate(RuleAction<? super T> ruleAction);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleSourceBackedRuleAction.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleSourceBackedRuleAction.java
new file mode 100644
index 0000000..c4883a1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/RuleSourceBackedRuleAction.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.JavaMethod;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.Mutate;
+import org.gradle.model.internal.type.ModelType;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+public class RuleSourceBackedRuleAction<R, T> implements RuleAction<T> {
+    private final R instance;
+    private final JavaMethod<R, T> ruleMethod;
+    private final List<Class<?>> inputTypes;
+
+    private RuleSourceBackedRuleAction(R instance, JavaMethod<R, T> ruleMethod) {
+        this.instance = instance;
+        this.ruleMethod = ruleMethod;
+        this.inputTypes = determineInputTypes(ruleMethod.getParameterTypes());
+    }
+
+    public static <R, T> RuleSourceBackedRuleAction<R, T> create(ModelType<T> subjectType, R ruleSourceInstance) {
+        ModelType<R> ruleSourceType = ModelType.typeOf(ruleSourceInstance);
+        List<Method> mutateMethods = JavaReflectionUtil.findAllMethods(ruleSourceType.getConcreteClass(), new Spec<Method>() {
+            public boolean isSatisfiedBy(Method element) {
+                return element.isAnnotationPresent(Mutate.class);
+            }
+        });
+        List<String> reasons = Lists.newArrayList();
+
+        if (mutateMethods.size() == 0) {
+            reasons.add("must have at exactly one method annotated with @org.gradle.model.Mutate");
+        } else {
+            if (mutateMethods.size() > 1) {
+                reasons.add("more than one method is annotated with @org.gradle.model.Mutate");
+            }
+
+            for (Method ruleMethod : mutateMethods) {
+                if (ruleMethod.getReturnType() != Void.TYPE) {
+                    reasons.add(String.format("rule method '%s' must return void", ruleMethod.getName()));
+                }
+                Type[] parameterTypes = ruleMethod.getGenericParameterTypes();
+                if (parameterTypes.length == 0 || !subjectType.isAssignableFrom(ModelType.of(parameterTypes[0]))) {
+                    reasons.add(String.format("first parameter of rule method '%s' must be of type %s", ruleMethod.getName(), subjectType));
+                }
+            }
+        }
+
+        if (reasons.size() > 0) {
+            throw invalid(ruleSourceType, reasons);
+        }
+
+        return new RuleSourceBackedRuleAction<R, T>(ruleSourceInstance, new JavaMethod<R, T>(ruleSourceType.getConcreteClass(), subjectType.getConcreteClass(), mutateMethods.get(0)));
+    }
+
+    private static RuntimeException invalid(ModelType<?> source, List<String> reasons) {
+        StringBuilder errorString = new StringBuilder(String.format("Type %s is not a valid model rule source: ", source));
+        for (String reason : reasons) {
+            errorString.append(String.format("\n- %s", reason));
+        }
+        return new RuleActionValidationException(null, new InvalidModelRuleDeclarationException(errorString.toString()));
+    }
+
+    public static List<Class<?>> determineInputTypes(Class<?>[] parameterTypes) {
+        return Arrays.asList(parameterTypes).subList(1, parameterTypes.length);
+    }
+
+    public List<Class<?>> getInputTypes() {
+        return inputTypes;
+    }
+
+    public void execute(T subject, List<?> inputs) {
+        Object[] args = new Object[inputs.size() + 1];
+        args[0] = subject;
+        for (int i = 0; i < inputs.size(); i++) {
+            Object input =  inputs.get(i);
+            args[i+1] = input;
+        }
+        ruleMethod.invoke(instance, args);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/SpecRuleAction.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/SpecRuleAction.java
new file mode 100644
index 0000000..007ef13
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/rules/SpecRuleAction.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules;
+
+import org.gradle.api.specs.Spec;
+
+/**
+ * Represents a tuple containing a Spec and a RuleAction
+ */
+public class SpecRuleAction<T> {
+    final RuleAction<? super T> action;
+    final Spec<? super T> spec;
+
+    public SpecRuleAction(RuleAction<? super T> action, Spec<? super T> spec) {
+        this.action = action;
+        this.spec = spec;
+    }
+
+    public RuleAction<? super T> getAction() {
+        return action;
+    }
+
+    public Spec<? super T> getSpec() {
+        return spec;
+    }
+}
diff --git a/subprojects/core-impl/src/main/resources/META-INF/services/org.gradle.api.internal.artifacts.DependencyManagementServices b/subprojects/dependency-management/src/main/resources/META-INF/services/org.gradle.api.internal.artifacts.DependencyManagementServices
similarity index 100%
rename from subprojects/core-impl/src/main/resources/META-INF/services/org.gradle.api.internal.artifacts.DependencyManagementServices
rename to subprojects/dependency-management/src/main/resources/META-INF/services/org.gradle.api.internal.artifacts.DependencyManagementServices
diff --git a/subprojects/core-impl/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/dependency-management/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
similarity index 100%
rename from subprojects/core-impl/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
rename to subprojects/dependency-management/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultComponentSelectionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultComponentSelectionTest.groovy
new file mode 100644
index 0000000..0d7a449
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultComponentSelectionTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component.ModuleComponentIdentifier
+import spock.lang.Specification
+
+class DefaultComponentSelectionTest extends Specification {
+    DefaultComponentSelection selection
+
+    def setup() {
+        selection = new DefaultComponentSelection(Stub(ModuleComponentIdentifier))
+    }
+
+    def "accepted by default"() {
+        expect:
+        !selection.rejected
+        selection.rejectionReason == null
+    }
+
+    def "accepted until rejected"() {
+        when:
+        selection.reject("bad")
+
+        then:
+        selection.rejected
+        selection.rejectionReason == "bad"
+    }
+
+    def "last rejection wins"() {
+        when:
+        selection.reject("bad")
+        selection.reject("worse")
+
+        then:
+        selection.rejected
+        selection.rejectionReason == "worse"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
new file mode 100644
index 0000000..bedce4f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultDependencyManagementServicesTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts
+
+import org.gradle.api.Action
+import org.gradle.api.artifacts.dsl.ArtifactHandler
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
+import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer
+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.dependencies.DefaultDependencyHandler
+import org.gradle.api.internal.artifacts.ivyservice.IvyBackedArtifactPublisher
+import org.gradle.api.internal.artifacts.repositories.DefaultBaseRepositoryFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.DefaultServiceRegistry
+import org.gradle.internal.service.ServiceRegistration
+import org.gradle.internal.service.ServiceRegistry
+import spock.lang.Specification
+
+import java.lang.reflect.ParameterizedType
+
+class DefaultDependencyManagementServicesTest extends Specification {
+    final ServiceRegistry parent = Mock()
+    final Instantiator instantiator = DirectInstantiator.INSTANCE
+    final DefaultDependencyManagementServices services = new DefaultDependencyManagementServices(parent)
+
+    def setup() {
+        _ * parent.get(Instantiator) >> instantiator
+        _ * parent.get({it instanceof Class}) >> { Class t -> Stub(t) }
+        _ * parent.get({it instanceof ParameterizedType}) >> { ParameterizedType t -> Stub(t.rawType) }
+    }
+
+    def "can create dependency resolution DSL services"() {
+        given:
+        def registry = new DefaultServiceRegistry(parent)
+
+        when:
+        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
+        def resolutionServices = registry.get(DependencyResolutionServices)
+
+        then:
+        resolutionServices.resolveRepositoryHandler instanceof DefaultRepositoryHandler
+        resolutionServices.configurationContainer instanceof DefaultConfigurationContainer
+        resolutionServices.dependencyHandler instanceof DefaultDependencyHandler
+        registry.get(ComponentMetadataHandler) instanceof DefaultComponentMetadataHandler
+        registry.get(ArtifactHandler) instanceof DefaultArtifactHandler
+        registry.get(BaseRepositoryFactory) instanceof DefaultBaseRepositoryFactory
+    }
+
+    def "publish services provide a repository handler"() {
+        given:
+        def registry = new DefaultServiceRegistry(parent)
+
+        when:
+        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
+        def publishServices = registry.get(ArtifactPublicationServices)
+
+        then:
+        def publishResolverHandler = publishServices.createRepositoryHandler()
+        publishResolverHandler instanceof DefaultRepositoryHandler
+        !publishResolverHandler.is(publishServices.createRepositoryHandler())
+    }
+
+    def "publish services provide an ArtifactPublisher"() {
+        given:
+        def registry = new DefaultServiceRegistry(parent)
+
+        when:
+        registry.register({ ServiceRegistration registration -> services.addDslServices(registration) } as Action)
+        def publishServices = registry.get(ArtifactPublicationServices)
+
+        then:
+        def ivyService = publishServices.createArtifactPublisher()
+        ivyService instanceof IvyBackedArtifactPublisher
+        !ivyService.is(publishServices.createArtifactPublisher())
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifierSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifierSpec.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifierSpec.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleIdentifierSpec.groovy
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifierSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifierSpec.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifierSpec.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionIdentifierSpec.groovy
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultModuleVersionSelectorTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.groovy
new file mode 100644
index 0000000..508e2fc
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedArtifactTest.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.api.internal.artifacts
+
+import org.gradle.api.artifacts.ResolvedModuleVersion
+import org.gradle.internal.component.model.IvyArtifactName
+import org.gradle.internal.Factory
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class DefaultResolvedArtifactTest extends Specification {
+    final Factory artifactSource = Mock()
+
+    def "artifacts are equal when module and name are equal"() {
+        def dependency = dep("group", "module1", "1.2")
+        def dependencySameModule = dep("group", "module1", "1.2")
+        def dependency2 = dep("group", "module2", "1-beta")
+        def ivyArt = Stub(IvyArtifactName)
+        def artifact = new DefaultResolvedArtifact(dependency, ivyArt, artifactSource, 0)
+        def equalArtifact = new DefaultResolvedArtifact(dependencySameModule, ivyArt, artifactSource, 0)
+        def differentModule = new DefaultResolvedArtifact(dependency2, ivyArt, artifactSource, 0)
+        def differentName = new DefaultResolvedArtifact(dependency, Stub(IvyArtifactName), artifactSource, 0)
+
+        expect:
+        artifact Matchers.strictlyEqual(equalArtifact)
+        artifact != differentModule
+        artifact != differentName
+    }
+
+    def dep(String group, String moduleName, String version) {
+        ResolvedModuleVersion module = Mock()
+        _ * module.id >> new DefaultModuleVersionIdentifier(group, moduleName, version)
+        module
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
new file mode 100644
index 0000000..534a6d3
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencySpec.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts
+
+import org.gradle.api.artifacts.ResolvedArtifact
+import spock.lang.Specification
+
+class DefaultResolvedDependencySpec extends Specification {
+    final dependency = new DefaultResolvedDependency(DefaultModuleVersionIdentifier.newId("group", "module", "version"), "config")
+
+    def "provides meta-data about the module"() {
+        expect:
+        dependency.module.id.group == "group"
+        dependency.module.id.name == "module"
+        dependency.module.id.version == "version"
+    }
+
+    def "artifacts are ordered by name then classifier then extension then type"() {
+        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
+        ResolvedArtifact artifact2 = artifact("b", null, "jar", "jar")
+        ResolvedArtifact artifact3 = artifact("b", "a-classifier", "jar", "jar")
+        ResolvedArtifact artifact4 = artifact("b", "b-classifier", "b-type", "a-ext")
+        ResolvedArtifact artifact5 = artifact("b", "b-classifier", "a-type", "b-ext")
+        ResolvedArtifact artifact6 = artifact("b", "b-classifier", "b-type", "b-ext")
+        ResolvedArtifact artifact7 = artifact("c", "a-classifier", "jar", "jar")
+
+        given:
+        dependency.addModuleArtifact(artifact6)
+        dependency.addModuleArtifact(artifact1)
+        dependency.addModuleArtifact(artifact3)
+        dependency.addModuleArtifact(artifact5)
+        dependency.addModuleArtifact(artifact2)
+        dependency.addModuleArtifact(artifact7)
+        dependency.addModuleArtifact(artifact4)
+
+        expect:
+        dependency.moduleArtifacts as List == [artifact1, artifact2, artifact3, artifact4, artifact5, artifact6, artifact7]
+    }
+
+    def "does not discard artifacts with the same name and classifier and extension and type"() {
+        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
+        ResolvedArtifact artifact2 = artifact("a", null, "jar", "jar")
+
+        given:
+        dependency.addModuleArtifact(artifact1)
+        dependency.addModuleArtifact(artifact2)
+
+        expect:
+        dependency.moduleArtifacts == [artifact1, artifact2] as Set
+    }
+
+    def "parent specific artifacts are ordered by name then classifier then extension then type"() {
+        ResolvedArtifact artifact1 = artifact("a", null, "jar", "jar")
+        ResolvedArtifact artifact2 = artifact("b", null, "jar", "jar")
+        ResolvedArtifact artifact3 = artifact("b", "a-classifier", "jar", "jar")
+        ResolvedArtifact artifact4 = artifact("b", "b-classifier", "b-type", "a-ext")
+        ResolvedArtifact artifact5 = artifact("b", "b-classifier", "a-type", "b-ext")
+        ResolvedArtifact artifact6 = artifact("b", "b-classifier", "b-type", "b-ext")
+        ResolvedArtifact artifact7 = artifact("c", "a-classifier", "jar", "jar")
+        DefaultResolvedDependency parent = Mock()
+
+        given:
+        dependency.parents.add(parent)
+        dependency.addParentSpecificArtifacts(parent, [artifact6, artifact1, artifact7, artifact5, artifact2, artifact3, artifact4] as Set)
+
+        expect:
+        dependency.getParentArtifacts(parent) as List == [artifact1, artifact2, artifact3, artifact4, artifact5, artifact6, artifact7]
+    }
+
+    def artifact(String name, String classifier, String type, String extension) {
+        ResolvedArtifact artifact = Mock()
+        _ * artifact.toString() >> "$name-$classifier-$type.$extension"
+        _ * artifact.name >> name
+        _ * artifact.classifier >> classifier
+        _ * artifact.type >> type
+        _ * artifact.extension >> extension
+        return artifact
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.java
new file mode 100644
index 0000000..4f1603c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolvedDependencyTest.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.artifacts;
+
+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.component.model.IvyArtifactName;
+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;
+
+import java.io.File;
+import java.util.Collections;
+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.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;
+
+public class DefaultResolvedDependencyTest {
+    private JUnit4Mockery context = new JUnit4GroovyMockery();
+
+    @Test
+    public void init() {
+        String someGroup = "someGroup";
+        String someName = "someName";
+        String someVersion = "someVersion";
+        String someConfiguration = "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));
+        assertThat(resolvedDependency.getModuleVersion(), equalTo(someVersion));
+        assertThat(resolvedDependency.getConfiguration(), equalTo(someConfiguration));
+        assertThat(resolvedDependency.getModuleArtifacts(), equalTo(Collections.<ResolvedArtifact>emptySet()));
+        assertThat(resolvedDependency.getChildren(), equalTo(Collections.<ResolvedDependency>emptySet()));
+        assertThat(resolvedDependency.getParents(), equalTo(Collections.<ResolvedDependency>emptySet()));
+    }
+
+    @Test
+    public void getAllModuleArtifacts() {
+        ResolvedArtifact moduleArtifact = createArtifact("moduleArtifact");
+        ResolvedArtifact childModuleArtifact = createArtifact("childModuleArtifact");
+        DefaultResolvedDependency resolvedDependency = new DefaultResolvedDependency(newId("someGroup", "someName", "someVersion"), "someConfiguration");
+        resolvedDependency.addModuleArtifact(moduleArtifact);
+        DefaultResolvedDependency childDependency = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
+        childDependency.addModuleArtifact(childModuleArtifact);
+        resolvedDependency.getChildren().add(childDependency);
+        assertThat(resolvedDependency.getAllModuleArtifacts(), equalTo(toSet(moduleArtifact, childModuleArtifact)));
+    }
+
+    @Test
+    public void getParentArtifacts() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        Set<ResolvedArtifact> parent1SpecificArtifacts = toSet(createArtifact("parent1Specific"));
+        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
+
+        Set<ResolvedArtifact> parent2SpecificArtifacts = toSet(createArtifact("parent2Specific"));
+        DefaultResolvedDependency parentResolvedDependency2 = createAndAddParent("parent2", resolvedDependency, parent2SpecificArtifacts);
+
+        assertThat(resolvedDependency.getParentArtifacts(parentResolvedDependency1), equalTo(parent1SpecificArtifacts));
+        assertThat(resolvedDependency.getParentArtifacts(parentResolvedDependency2), equalTo(parent2SpecificArtifacts));
+    }
+
+    private ResolvedArtifact createArtifact(String name) {
+        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 IvyArtifactName artifactStub = context.mock(IvyArtifactName.class, "artifact" + name);
+        context.checking(new Expectations() {{
+            allowing(artifactStub).getName();
+            will(returnValue(name));
+            allowing(artifactStub).getType();
+            will(returnValue(type));
+            allowing(artifactStub).getExtension();
+            will(returnValue(extension));
+            allowing(artifactStub).getClassifier();
+            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(), artifactStub, artifactSource, 0);
+    }
+
+    private DefaultResolvedDependency createResolvedDependency() {
+        return new DefaultResolvedDependency(newId("someGroup", "someName", "someVersion"), "someConfiguration");
+    }
+
+    @Test
+    public void getArtifacts() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        Set<ResolvedArtifact> parent1SpecificArtifacts = toSet(createArtifact("parent1Specific"));
+        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
+
+        assertThat(resolvedDependency.getArtifacts(parentResolvedDependency1), equalTo(parent1SpecificArtifacts));
+    }
+
+    @Test
+    public void getArtifactsWithParentWithoutParentArtifacts() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
+        resolvedDependency.getParents().add(parent);
+        assertThat(resolvedDependency.getArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
+    }
+
+    @Test
+    public void getParentArtifactsWithParentWithoutParentArtifacts() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        DefaultResolvedDependency parent = new DefaultResolvedDependency(newId("someGroup", "parent", "someVersion"), "someConfiguration");
+        resolvedDependency.getParents().add(parent);
+        assertThat(resolvedDependency.getParentArtifacts(parent), equalTo(Collections.<ResolvedArtifact>emptySet()));
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void getParentArtifactsWithUnknownParent() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
+        assertThat(resolvedDependency.getParentArtifacts(unknownParent),
+                equalTo(Collections.<ResolvedArtifact>emptySet()));
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void getArtifactsWithUnknownParent() {
+        Set<ResolvedArtifact> someModuleArtifacts = toSet(createArtifact("someModuleResolvedArtifact"));
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        DefaultResolvedDependency unknownParent = new DefaultResolvedDependency(newId("someGroup", "parent2", "someVersion"), "someConfiguration");
+        assertThat(resolvedDependency.getParentArtifacts(unknownParent),
+                equalTo(someModuleArtifacts));
+    }
+
+    @Test
+    public void getAllArtifacts() {
+        DefaultResolvedDependency resolvedDependency = createResolvedDependency();
+
+        Set<ResolvedArtifact> parent1SpecificArtifacts = newHashSet(createArtifact("parent1Specific"));
+        DefaultResolvedDependency parentResolvedDependency1 = createAndAddParent("parent1", resolvedDependency, parent1SpecificArtifacts);
+
+        createAndAddParent("parent2", resolvedDependency, newHashSet(createArtifact("parent2Specific")));
+
+        DefaultResolvedDependency child = new DefaultResolvedDependency(newId("someGroup", "someChild", "someVersion"), "someChildConfiguration");
+        resolvedDependency.getChildren().add(child);
+
+        Set<ResolvedArtifact> childParent1SpecificArtifacts = newHashSet(createArtifact("childParent1Specific"));
+        createAndAddParent("childParent1", child, childParent1SpecificArtifacts);
+
+        Set<ResolvedArtifact> childParent2SpecificArtifacts = newHashSet(createArtifact("childParent2Specific"));
+        createAndAddParent("childParent2", child, childParent2SpecificArtifacts);
+
+        Iterable<ResolvedArtifact> allArtifacts = newHashSet(concat(parent1SpecificArtifacts, childParent1SpecificArtifacts, childParent2SpecificArtifacts));
+        assertThat(resolvedDependency.getAllArtifacts(parentResolvedDependency1), equalTo(allArtifacts));
+    }
+
+    @Test
+    public void equalsAndHashCode() {
+        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)));
+        assertThat(dependency, not(equalTo(differentName)));
+        assertThat(dependency, not(equalTo(differentVersion)));
+        assertThat(dependency, not(equalTo(differentConfiguration)));
+    }
+
+    private DefaultResolvedDependency createAndAddParent(String parentName, DefaultResolvedDependency resolvedDependency, Set<ResolvedArtifact> parentSpecificArtifacts) {
+        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/DependencyManagementBuildScopeServicesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServicesTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServicesTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServicesTest.groovy
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServicesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServicesTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServicesTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServicesTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ModuleVersionSelectorSerializerTest.groovy
new file mode 100644
index 0000000..6e12ff1
--- /dev/null
+++ b/subprojects/dependency-management/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.internal.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/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSerializerTest.groovy
new file mode 100644
index 0000000..0aa319e
--- /dev/null
+++ b/subprojects/dependency-management/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.internal.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/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolvedConfigurationIdentifierSpec.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
new file mode 100644
index 0000000..3d2d62f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ResolverResultsSpec.groovy
@@ -0,0 +1,55 @@
+/*
+ * 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.ResolveException
+import org.gradle.api.artifacts.ResolvedConfiguration
+import org.gradle.api.artifacts.result.ResolutionResult
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults
+import spock.lang.Specification
+
+class ResolverResultsSpec extends Specification {
+    private resolvedConfiguration = Mock(ResolvedConfiguration)
+    private resolutionResult = Mock(ResolutionResult)
+    private projectConfigurationResult = Mock(ResolvedProjectConfigurationResults)
+    private fatalFailure = Mock(ResolveException)
+    private results = new ResolverResults()
+
+    def "does not provide ResolutionResult in case of fatal failure"() {
+        when:
+        results.failed(resolvedConfiguration, fatalFailure)
+
+        then:
+        results.resolvedConfiguration
+
+        when:
+        results.resolutionResult
+        then:
+        def ex = thrown(ResolveException)
+        ex == fatalFailure
+    }
+
+    def "provides resolve results"() {
+        when:
+        results.resolved(resolvedConfiguration, resolutionResult, projectConfigurationResult)
+
+        then:
+        results.resolvedConfiguration == resolvedConfiguration
+        results.resolutionResult == resolutionResult
+        results.resolvedProjectConfigurationResults == projectConfigurationResult
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy
new file mode 100644
index 0000000..98b3e05
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactoryTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.DefaultModule
+import org.gradle.api.internal.artifacts.ModuleInternal
+import org.gradle.api.internal.artifacts.ProjectBackedModule
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import spock.lang.Specification
+
+class DefaultComponentIdentifierFactoryTest extends Specification {
+    ComponentIdentifierFactory componentIdentifierFactory = new DefaultComponentIdentifierFactory()
+
+    def "can create project component identifier"() {
+        given:
+        Project project = Mock(ProjectInternal)
+        ModuleInternal module = new ProjectBackedModule(project)
+
+        when:
+        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module)
+
+        then:
+        project.path >> ':a'
+        componentIdentifier == new DefaultProjectComponentIdentifier(':a')
+    }
+
+    def "can create module component identifier"() {
+        given:
+        ModuleInternal module = new DefaultModule('some-group', 'some-name', '1.0')
+
+        when:
+        ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module)
+
+        then:
+        componentIdentifier == new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationsTest.java
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
new file mode 100644
index 0000000..b6a7920
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.configurations
+
+import org.gradle.api.artifacts.UnknownConfigurationException
+import org.gradle.api.internal.DomainObjectContext
+import org.gradle.api.internal.artifacts.ConfigurationResolver
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.event.ListenerManager
+import spock.lang.Specification
+
+public class DefaultConfigurationContainerSpec extends Specification {
+
+    private ConfigurationResolver resolver = Mock()
+    private Instantiator instantiator = Mock()
+    private DomainObjectContext domainObjectContext = Mock()
+    private ListenerManager listenerManager = Mock()
+    private DependencyMetaDataProvider metaDataProvider = Mock()
+    private ProjectAccessListener projectAccessListener = Mock()
+
+    def ConfigurationInternal conf = Mock()
+
+    private DefaultConfigurationContainer configurationContainer = new DefaultConfigurationContainer(
+            resolver, instantiator, domainObjectContext,
+            listenerManager, metaDataProvider, projectAccessListener);
+
+    def "adds and gets"() {
+        _ * conf.getName() >> "compile"
+        1 * domainObjectContext.absoluteProjectPath("compile") >> ":compile"
+        1 * instantiator.newInstance(DefaultResolutionStrategy.class) >> { new DefaultResolutionStrategy() }
+        1 * instantiator.newInstance(DefaultConfiguration.class, ":compile", "compile", configurationContainer,
+                resolver, listenerManager, metaDataProvider, _ as ResolutionStrategyInternal, projectAccessListener) >> conf
+
+        when:
+        def compile = configurationContainer.create("compile")
+
+        then:
+        configurationContainer.getByName("compile") == compile
+
+        when:
+        configurationContainer.getByName("fooo")
+
+        then:
+        thrown(UnknownConfigurationException)
+    }
+
+    def "configures and finds"() {
+        _ * conf.getName() >> "compile"
+        1 * domainObjectContext.absoluteProjectPath("compile") >> ":compile"
+        1 * instantiator.newInstance(DefaultResolutionStrategy.class) >> { new DefaultResolutionStrategy() }
+        1 * instantiator.newInstance(DefaultConfiguration.class, ":compile", "compile", configurationContainer,
+                resolver, listenerManager, metaDataProvider, _ as ResolutionStrategyInternal, projectAccessListener) >> conf
+
+        when:
+        def compile = configurationContainer.create("compile") {
+            description = "I compile!"
+        }
+
+        then:
+        configurationContainer.getByName("compile") == compile
+        1 * conf.setDescription("I compile!")
+
+        //finds configurations
+        configurationContainer.findByName("compile") == compile
+        configurationContainer.findByName("foo") == null
+        configurationContainer.findAll { it.name == "compile" } as Set == [compile] as Set
+        configurationContainer.findAll { it.name == "foo" } as Set == [] as Set
+
+        configurationContainer as List == [compile] as List
+    }
+
+    def "creates detached"() {
+        given:
+        def dependency1 = new DefaultExternalModuleDependency("group", "name", "version")
+        def dependency2 = new DefaultExternalModuleDependency("group", "name2", "version")
+
+        when:
+        def detached = configurationContainer.detachedConfiguration(dependency1, dependency2);
+
+        then:
+        detached.getAll() == [detached] as Set
+        detached.getHierarchy() == [detached] as Set
+        [dependency1, dependency2].each { detached.getDependencies().contains(it) }
+        detached.getDependencies().size() == 2
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
new file mode 100644
index 0000000..07f7bba
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.configurations
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.UnknownConfigurationException
+import org.gradle.api.internal.AsmBackedClassGenerator
+import org.gradle.api.internal.ClassGeneratorBackedInstantiator
+import org.gradle.api.internal.DomainObjectContext
+import org.gradle.api.internal.MissingMethodException
+import org.gradle.api.internal.artifacts.ConfigurationResolver
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.event.ListenerManager
+import org.gradle.util.JUnit4GroovyMockery
+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.assertThat
+
+
+ at RunWith(JMock)
+class DefaultConfigurationContainerTest {
+    private JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+
+    private ConfigurationResolver resolver = context.mock(ConfigurationResolver)
+    private ListenerManager listenerManager = context.mock(ListenerManager.class)
+    private DependencyMetaDataProvider metaDataProvider = context.mock(DependencyMetaDataProvider.class)
+    private ProjectAccessListener projectAccessListener = context.mock(ProjectAccessListener.class)
+    private Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
+    private DefaultConfigurationContainer configurationContainer = instantiator.newInstance(DefaultConfigurationContainer.class,
+            resolver, instantiator, { name -> name } as DomainObjectContext,
+            listenerManager, metaDataProvider, projectAccessListener)
+
+    @Before
+    public void setup() {
+        context.checking {
+            ignoring(listenerManager)
+        }
+    }
+
+    @Test
+    void addsNewConfigurationWhenConfiguringSelf() {
+        configurationContainer.configure {
+            newConf
+        }
+        assertThat(configurationContainer.findByName('newConf'), notNullValue())
+        assertThat(configurationContainer.newConf, notNullValue())
+    }
+
+    @Test(expected = UnknownConfigurationException)
+    void doesNotAddNewConfigurationWhenNotConfiguringSelf() {
+        configurationContainer.getByName('unknown')
+    }
+
+    @Test
+    void makesExistingConfigurationAvailableAsProperty() {
+        Configuration configuration = configurationContainer.create('newConf')
+        assertThat(configuration, notNullValue())
+        assertThat(configurationContainer.getByName("newConf"), sameInstance(configuration))
+        assertThat(configurationContainer.newConf, sameInstance(configuration))
+    }
+
+    @Test
+    void addsNewConfigurationWithClosureWhenConfiguringSelf() {
+        String someDesc = 'desc1'
+        configurationContainer.configure {
+            newConf {
+                description = someDesc
+            }
+        }
+        assertThat(configurationContainer.newConf.getDescription(), equalTo(someDesc))
+    }
+
+    @Test
+    void makesExistingConfigurationAvailableAsConfigureMethod() {
+        String someDesc = 'desc1'
+        configurationContainer.create('newConf')
+        Configuration configuration = configurationContainer.newConf {
+            description = someDesc
+        }
+        assertThat(configuration.getDescription(), equalTo(someDesc))
+    }
+
+    @Test
+    void makesExistingConfigurationAvailableAsConfigureMethodWhenConfiguringSelf() {
+        String someDesc = 'desc1'
+        Configuration configuration = configurationContainer.create('newConf')
+        configurationContainer.configure {
+            newConf {
+                description = someDesc
+            }
+        }
+        assertThat(configuration.getDescription(), equalTo(someDesc))
+    }
+
+    @Test(expected = MissingMethodException)
+    void newConfigurationWithNonClosureParametersShouldThrowMissingMethodEx() {
+        configurationContainer.newConf('a', 'b')
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
new file mode 100644
index 0000000..39feb9e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations
+
+import org.gradle.api.Action
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.Task
+import org.gradle.api.artifacts.*
+import org.gradle.api.artifacts.result.ResolutionResult
+import org.gradle.api.internal.artifacts.ConfigurationResolver
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.internal.event.ListenerBroadcast
+import org.gradle.internal.event.ListenerManager
+import spock.lang.Specification
+
+class DefaultConfigurationSpec extends Specification {
+
+    ConfigurationsProvider configurationsProvider = Mock()
+    ConfigurationResolver resolver = Mock()
+    ListenerManager listenerManager = Mock()
+    DependencyMetaDataProvider metaDataProvider = Mock()
+    ResolutionStrategyInternal resolutionStrategy = Mock()
+
+    DefaultConfiguration conf(String confName = "conf", String path = ":conf") {
+        new DefaultConfiguration(path, confName, configurationsProvider, resolver, listenerManager, metaDataProvider, resolutionStrategy, null)
+    }
+
+    DefaultPublishArtifact artifact(String name) {
+        artifact(name: name)
+    }
+
+    DefaultPublishArtifact artifact(Map props = [:]) {
+        new DefaultPublishArtifact(
+            props.name ?: "artifact",
+            props.extension ?: "artifact",
+            props.type,
+            props.classifier,
+            props.date,
+            props.file,
+            props.tasks ?: []
+        )
+    }
+
+    // You need to wrap this in an interaction {} block when calling it
+    ResolvedConfiguration resolvedConfiguration(Configuration config, ConfigurationResolver dependencyResolver = resolver) {
+        ResolvedConfiguration resolvedConfiguration = Mock()
+        def results = new ResolverResults()
+        results.resolved(resolvedConfiguration, Mock(ResolutionResult), Mock(ResolvedProjectConfigurationResults))
+        1 * dependencyResolver.resolve(config) >> results
+        resolvedConfiguration
+    }
+
+    def setup() {
+        ListenerBroadcast<DependencyResolutionListener> broadcast = new ListenerBroadcast<DependencyResolutionListener>(DependencyResolutionListener)
+        _ * listenerManager.createAnonymousBroadcaster(DependencyResolutionListener) >> broadcast
+    }
+
+    def "all artifacts collection has immediate artifacts"() {
+        given:
+        def c = conf()
+
+        when:
+        c.artifacts << artifact()
+        c.artifacts << artifact()
+
+        then:
+        c.allArtifacts.size() == 2
+    }
+
+    def "all artifacts collection has inherited artifacts"() {
+        given:
+        def master = conf()
+
+        def masterParent1 = conf()
+        def masterParent2 = conf()
+        master.extendsFrom masterParent1, masterParent2
+
+        def masterParent1Parent1 = conf()
+        def masterParent1Parent2 = conf()
+        masterParent1.extendsFrom masterParent1Parent1, masterParent1Parent2
+
+        def masterParent2Parent1 = conf()
+        def masterParent2Parent2 = conf()
+        masterParent2.extendsFrom masterParent2Parent1, masterParent2Parent2
+
+        def allArtifacts = master.allArtifacts
+
+        def added = []
+        allArtifacts.whenObjectAdded { added << it.name }
+        def removed = []
+        allArtifacts.whenObjectRemoved { removed << it.name }
+
+        expect:
+        allArtifacts.empty
+
+        when:
+        masterParent1.artifacts << artifact("p1-1")
+        masterParent1Parent1.artifacts << artifact("p1p1-1")
+        masterParent1Parent2.artifacts << artifact("p1p2-1")
+        masterParent2.artifacts << artifact("p2-1")
+        masterParent2Parent1.artifacts << artifact("p2p1-1")
+        masterParent2Parent2.artifacts << artifact("p2p2-1")
+
+        then:
+        allArtifacts.size() == 6
+        added == ["p1-1", "p1p1-1", "p1p2-1", "p2-1", "p2p1-1", "p2p2-1"]
+
+        when:
+        masterParent2Parent2.artifacts.remove masterParent2Parent2.artifacts.toList().first()
+
+        then:
+        allArtifacts.size() == 5
+        removed == ["p2p2-1"]
+
+        when:
+        removed.clear()
+        masterParent1.extendsFrom = []
+
+        then:
+        allArtifacts.size() == 3
+        removed == ["p1p1-1", "p1p2-1"]
+    }
+
+    def "incoming dependencies set has same name and path as owner configuration"() {
+        def config = conf("conf", ":path")
+
+        expect:
+        config.incoming.name == "conf"
+        config.incoming.path == ":path"
+    }
+
+    def "incoming dependencies set contains immediate dependencies"() {
+        def config = conf("conf")
+        Dependency dep1 = Mock()
+
+        given:
+        config.dependencies.add(dep1)
+
+        expect:
+        config.incoming.dependencies as List == [dep1]
+    }
+
+    def "incoming dependencies set contains inherited dependencies"() {
+        def parent = conf("conf")
+        def config = conf("conf")
+        Dependency dep1 = Mock()
+
+        given:
+        config.extendsFrom parent
+        parent.dependencies.add(dep1)
+
+        expect:
+        config.incoming.dependencies as List == [dep1]
+    }
+
+    def "incoming dependencies set files are resolved lazily"() {
+        setup:
+        def config = conf("conf")
+
+        when:
+        def files = config.incoming.files
+
+        then:
+        0 * _._
+
+        when:
+        files.files
+
+        then:
+        interaction { resolvedConfiguration(config) }
+        0 * resolver._
+    }
+
+    def "incoming dependencies set depends on all self resolving dependencies"() {
+        SelfResolvingDependency dependency = Mock()
+        Task task = Mock()
+        TaskDependency taskDep = Mock()
+        def config = conf("conf")
+
+        given:
+        config.dependencies.add(dependency)
+
+        when:
+        def depTaskDeps = config.incoming.dependencies.buildDependencies.getDependencies(null)
+        def fileTaskDeps = config.incoming.files.buildDependencies.getDependencies(null)
+
+        then:
+        depTaskDeps == [task] as Set
+        fileTaskDeps == [task] as Set
+        _ * dependency.buildDependencies >> taskDep
+        _ * taskDep.getDependencies(_) >> ([task] as Set)
+        0 * _._
+    }
+
+    def "notifies beforeResolve action on incoming dependencies set when dependencies are resolved"() {
+        Action<ResolvableDependencies> action = Mock()
+        def config = conf("conf")
+
+        given:
+        config.incoming.beforeResolve(action)
+
+        when:
+        config.resolvedConfiguration
+
+        then:
+        interaction { resolvedConfiguration(config) }
+        1 * action.execute(config.incoming)
+    }
+
+    def "calls beforeResolve closure on incoming dependencies set when dependencies are resolved"() {
+        def config = conf("conf")
+        resolvedConfiguration(config)
+        def called = false
+
+        expect:
+        config.incoming.afterResolve {
+            assert it == config.incoming
+            called = true
+        }
+
+        when:
+        config.resolvedConfiguration
+
+        then:
+        called
+    }
+
+    def "notifies afterResolve action on incoming dependencies set when dependencies are resolved"() {
+        Action<ResolvableDependencies> action = Mock()
+        def config = conf("conf")
+
+        given:
+        config.incoming.afterResolve(action)
+
+        when:
+        config.resolvedConfiguration
+
+        then:
+        interaction { resolvedConfiguration(config) }
+        1 * action.execute(config.incoming)
+
+    }
+
+    def "calls afterResolve closure on incoming dependencies set when dependencies are resolved"() {
+        def config = conf("conf")
+        resolvedConfiguration(config)
+        def called = false
+
+        expect:
+        config.incoming.afterResolve {
+            assert it == config.incoming
+            called = true
+        }
+
+        when:
+        config.resolvedConfiguration
+
+        then:
+        called
+    }
+    
+    def "a recursive copy of a configuration includes inherited exclude rules"() {
+        given:
+        def (p1, p2, child) = [conf("p1"), conf("p2"), conf("child")]
+        child.extendsFrom p1, p2
+        
+        and:
+        def (p1Exclude, p2Exclude) = [[group: 'p1', module: 'p1'], [group: 'p2', module: 'p2']]
+        p1.exclude p1Exclude
+        p2.exclude p2Exclude
+        
+        when:
+        def copied = child.copyRecursive()
+        
+        then:
+        1 * resolutionStrategy.copy() >> Mock(ResolutionStrategyInternal)
+        copied.excludeRules.size() == 2
+        copied.excludeRules.collect{[group: it.group, module: it.module]}.sort { it.group } == [p1Exclude, p2Exclude]
+    }
+
+    def "copied configuration has own instance of resolution strategy"() {
+        def strategy = Mock(ResolutionStrategyInternal)
+        def conf = conf()
+
+        when:
+        def copy = conf.copy()
+
+        then:
+        1 * resolutionStrategy.copy() >> strategy
+        conf.resolutionStrategy != copy.resolutionStrategy
+        copy.resolutionStrategy == strategy
+    }
+
+    def "provides resolution result"() {
+        def config = conf("conf")
+        def result = Mock(ResolutionResult)
+        def resolverResults = new ResolverResults()
+        resolverResults.resolved(Mock(ResolvedConfiguration), result, Mock(ResolvedProjectConfigurationResults))
+
+        when:
+        def out = config.incoming.resolutionResult
+
+        then:
+        1 * resolver.resolve(config) >> resolverResults
+        out == result
+    }
+
+    def "provides task dependency from project dependency using 'needed'"() {
+        def conf = conf("conf")
+        when: def dep = conf.getTaskDependencyFromProjectDependency(true, "foo") as TasksFromProjectDependencies
+        then: dep.taskName == "foo"
+    }
+
+    def "provides task dependency from project dependency using 'dependents'"() {
+        def conf = conf("conf")
+        when: def dep = conf.getTaskDependencyFromProjectDependency(false, "bar") as TasksFromDependentProjects
+        then:
+        dep.taskName == "bar"
+        dep.configurationName == "conf"
+    }
+
+    def "mutations are prohibited after resolution"() {
+        def conf = conf("conf")
+        def result = Mock(ResolutionResult)
+        def resolverResults = new ResolverResults()
+        resolverResults.resolved(Mock(ResolvedConfiguration), result, Mock(ResolvedProjectConfigurationResults))
+
+        when:
+        conf.incoming.getResolutionResult()
+        then:
+        1 * resolver.resolve(conf) >> resolverResults
+
+        when: conf.dependencies.add(Mock(Dependency))
+        then:
+        def exDependency = thrown(InvalidUserDataException);
+        exDependency.message == "Cannot change configuration ':conf' after it has been resolved."
+
+        when: conf.artifacts.add(Mock(PublishArtifact))
+        then:
+        def exArtifact = thrown(InvalidUserDataException);
+        exArtifact.message == "Cannot change configuration ':conf' after it has been resolved."
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
new file mode 100644
index 0000000..ca5131e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationTest.java
@@ -0,0 +1,906 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations;
+
+import groovy.lang.Closure;
+import org.gradle.api.GradleException;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.*;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultResolutionStrategy;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults;
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.util.*;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.*;
+
+import static org.gradle.util.Matchers.hasSameItems;
+import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class DefaultConfigurationTest {
+
+    //TODO **** if you touch this class, try pushing out more coverage into new DefaultConfigurationSpec ****
+
+    private JUnit4Mockery context = new JUnit4GroovyMockery();
+    private ConfigurationResolver dependencyResolver = context.mock(ConfigurationResolver.class);
+    private ConfigurationsProvider configurationContainer;
+    private ListenerManager listenerManager = context.mock(ListenerManager.class);
+    private DependencyMetaDataProvider metaDataProvider = context.mock(DependencyMetaDataProvider.class);
+    private DefaultConfiguration configuration;
+    private DependencyResolutionListener dependencyResolutionBroadcast = context.mock(DependencyResolutionListener.class);
+    private ListenerBroadcast resolutionListenerBroadcast = context.mock(ListenerBroadcast.class); 
+
+    @Before
+    public void setUp() {
+        configurationContainer = context.mock(ConfigurationsProvider.class);
+        context.checking(new Expectations(){{
+            allowing(listenerManager).createAnonymousBroadcaster(DependencyResolutionListener.class);
+            will(returnValue(resolutionListenerBroadcast));
+            allowing(resolutionListenerBroadcast).getSource();
+            will(returnValue(dependencyResolutionBroadcast));
+            allowing(dependencyResolutionBroadcast).afterResolve(with(any(ResolvableDependencies.class)));
+            allowing(dependencyResolutionBroadcast).beforeResolve(with(any(ResolvableDependencies.class)));
+            will(returnValue(null));
+        }});
+        configuration = createNamedConfiguration("path", "name");
+    }
+
+    @Test
+    public void defaultValues() {
+        assertThat(configuration.getName(), equalTo("name"));
+        assertThat(configuration.isVisible(), equalTo(true));
+        assertThat(configuration.getExtendsFrom().size(), equalTo(0));
+        assertThat(configuration.isTransitive(), equalTo(true));
+        assertThat(configuration.getDescription(), nullValue());
+        assertThat(configuration.getState(), equalTo(Configuration.State.UNRESOLVED));
+        assertThat(configuration.getDisplayName(), equalTo("configuration 'path'"));
+    }
+
+    @Test
+    public void hasUsefulDisplayName() {
+        assertThat(configuration.getDisplayName(), equalTo("configuration 'path'"));
+        assertThat(configuration.toString(), equalTo("configuration 'path'"));
+        assertThat(configuration.getIncoming().toString(), equalTo("dependencies 'path'"));
+    }
+
+    @Test
+    public void withPrivateVisibility() {
+        configuration.setVisible(false);
+        assertFalse(configuration.isVisible());
+    }
+
+    @Test
+    public void withIntransitive() {
+        configuration.setTransitive(false);
+        assertFalse(configuration.isTransitive());
+    }
+
+    @Test
+    public void exclude() {
+        Map<String, String> excludeArgs1 = toMap("group", "aGroup");
+        Map<String, String> excludeArgs2 = toMap("module", "aModule");
+        assertThat(configuration.exclude(excludeArgs1), sameInstance(configuration));
+        configuration.exclude(excludeArgs2);
+        assertThat(configuration.getExcludeRules(), equalTo(WrapUtil.<ExcludeRule>toSet(
+                new DefaultExcludeRule("aGroup", null), new DefaultExcludeRule(null, "aModule"))));
+    }
+
+    @Test
+    public void setExclude() {
+        Set<ExcludeRule> excludeRules = WrapUtil.<ExcludeRule>toSet(new DefaultExcludeRule("groupValue", null));
+        configuration.setExcludeRules(excludeRules);
+        assertThat(configuration.getExcludeRules(), equalTo(excludeRules));
+    }
+
+    @Test
+    public void withDescription() {
+        configuration.setDescription("description");
+        assertThat(configuration.getDescription(), equalTo("description"));
+    }
+
+    @Test
+    public void extendsOtherConfigurations() {
+        Configuration configuration1 = createNamedConfiguration("otherConf1");
+        configuration.extendsFrom(configuration1);
+        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1)));
+
+        Configuration configuration2 = createNamedConfiguration("otherConf2");
+        configuration.extendsFrom(configuration2);
+        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1, configuration2)));
+    }
+
+    @Test
+    public void setExtendsFrom() {
+        Configuration configuration1 = createNamedConfiguration("otherConf1");
+
+        configuration.setExtendsFrom(toSet(configuration1));
+        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration1)));
+
+        Configuration configuration2 = createNamedConfiguration("otherConf2");
+        configuration.setExtendsFrom(toSet(configuration2));
+        assertThat(configuration.getExtendsFrom(), equalTo(toSet(configuration2)));
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void extendsFromWithDirectCycleShouldThrowInvalidUserDataEx() {
+        Configuration otherConfiguration = createNamedConfiguration("otherConf");
+        otherConfiguration.extendsFrom(configuration);
+        configuration.extendsFrom(otherConfiguration);
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void extendsFromWithIndirectCycleShouldThrowInvalidUserDataEx() {
+        Configuration otherConfiguration1 = createNamedConfiguration("otherConf1");
+        Configuration otherConfiguration2 = createNamedConfiguration("otherConf2");
+        configuration.extendsFrom(otherConfiguration1);
+        otherConfiguration1.extendsFrom(otherConfiguration2);
+        otherConfiguration2.extendsFrom(configuration);
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void setExtendsFromWithCycleShouldThrowInvalidUserDataEx() {
+        Configuration otherConfiguration = createNamedConfiguration("otherConf");
+        otherConfiguration.extendsFrom(configuration);
+        configuration.setExtendsFrom(toSet(otherConfiguration));
+    }
+
+    @Test
+    public void getHierarchy() {
+        Configuration root1 = createNamedConfiguration("root1");
+        Configuration middle1 = createNamedConfiguration("middle1").extendsFrom(root1);
+        Configuration root2 = createNamedConfiguration("root2");
+        Configuration middle2 = createNamedConfiguration("middle2").extendsFrom(root1, root2);
+        createNamedConfiguration("root3");
+        Configuration leaf = createNamedConfiguration("leaf1").extendsFrom(middle1, middle2);
+        Set<Configuration> hierarchy = leaf.getHierarchy();
+        assertThat(hierarchy.size(), equalTo(5));
+        assertThat(hierarchy.iterator().next(), equalTo(leaf));
+        assertBothExistsAndOneIsBeforeOther(hierarchy, middle1, root1);
+        assertBothExistsAndOneIsBeforeOther(hierarchy, middle2, root2);
+    }
+
+    private void assertBothExistsAndOneIsBeforeOther(Set<Configuration> hierarchy, Configuration beforeConf, Configuration afterConf) {
+        assertThat(hierarchy, hasItem(beforeConf));
+        assertThat(hierarchy, hasItem(afterConf));
+
+        boolean foundBeforeConf = false;
+        for (Configuration configuration : hierarchy) {
+            if (configuration.equals(beforeConf)) {
+                foundBeforeConf = true;
+            }
+            if (configuration.equals(afterConf)) {
+                assertThat(foundBeforeConf, equalTo(true));
+            }
+        }
+    }
+
+    @Test
+    public void getAll() {
+        final Configuration conf1 = createNamedConfiguration("testConf1");
+        final Configuration conf2 = createNamedConfiguration("testConf2");
+        context.checking(new Expectations(){{
+            one(configurationContainer).getAll();
+            will(returnValue(toSet(conf1, conf2)));
+        }});
+        assertThat(configuration.getAll(), equalTo(toSet(conf1, conf2)));
+    }
+
+    @Test(expected = GradleException.class)
+    public void getAsPathShouldRethrownFailure() {
+        prepareForResolveWithErrors();
+        configuration.resolve();
+    }
+
+    @Test
+    public void resolve() {
+        final Set<File> fileSet = toSet(new File("somePath"));
+        makeResolveReturnFileSet(fileSet);
+        assertThat(configuration.resolve(), equalTo(fileSet));
+        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
+    }
+
+    @Test
+    public void filesWithDependencies() {
+        final Set<File> fileSet = toSet(new File("somePath"));
+        prepareForFilesBySpec(fileSet);
+        assertThat(configuration.files(context.mock(Dependency.class)), equalTo(fileSet));
+        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
+    }
+
+    @Test
+    public void fileCollectionWithDependencies() {
+        Dependency dependency1 = createDependency("group1", "name", "version");
+        Dependency dependency2 = createDependency("group2", "name", "version");
+        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
+                configuration.fileCollection(dependency1);
+        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(dependency1),
+                equalTo(true));
+        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(dependency2),
+                equalTo(false));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void filesWithSpec() {
+        final Set<File> fileSet = toSet(new File("somePath"));
+        prepareForFilesBySpec(fileSet);
+        Assert.<Set<File>>assertThat(configuration.files(context.mock(Spec.class)), equalTo(fileSet));
+        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
+    }
+
+    @Test
+    public void fileCollectionWithSpec() {
+        @SuppressWarnings("unchecked")
+        Spec<Dependency> spec = context.mock(Spec.class);
+        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
+                configuration.fileCollection(spec);
+        assertThat(fileCollection.getDependencySpec(), sameInstance((Object)spec));
+    }
+
+    @Test
+    public void filesWithClosureSpec() {
+        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
+        final Set<File> fileSet = toSet(new File("somePath"));
+        prepareForFilesBySpec(fileSet);
+        assertThat(configuration.files(closure), equalTo(fileSet));
+        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
+    }
+
+    @Test
+    public void fileCollectionWithClosureSpec() {
+        Closure closure = TestUtil.toClosure("{ dep -> dep.group == 'group1' }");
+        DefaultConfiguration.ConfigurationFileCollection fileCollection = (DefaultConfiguration.ConfigurationFileCollection)
+                configuration.fileCollection(closure);
+        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(createDependency("group1", "name", "version")),
+                equalTo(true));
+        assertThat(fileCollection.getDependencySpec().isSatisfiedBy(createDependency("group2", "name", "version")),
+                equalTo(false));
+    }
+
+    @SuppressWarnings("unchecked")
+    private void prepareForFilesBySpec(final Set<File> fileSet) {
+        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
+        prepareResolve(resolvedConfiguration, false);
+        context.checking(new Expectations() {{
+            one(resolvedConfiguration).getFiles(with(any(Spec.class)));
+            will(returnValue(fileSet));
+        }});
+    }
+
+    @Test(expected = GradleException.class)
+    public void resolveShouldRethrowFailure() {
+        prepareForResolveWithErrors();
+        configuration.resolve();
+    }
+
+    private void prepareForResolveWithErrors() {
+        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
+        prepareResolve(resolvedConfiguration, true);
+        context.checking(new Expectations(){{
+            one(resolvedConfiguration).rethrowFailure();
+            will(throwException(new GradleException()));
+        }});
+    }
+
+    private void makeResolveReturnFileSet(final Set<File> fileSet) {
+        final ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
+        context.checking(new Expectations() {{
+            prepareResolve(resolvedConfiguration, false);
+            allowing(resolvedConfiguration).getFiles(Specs.SATISFIES_ALL);
+            will(returnValue(fileSet));
+        }});
+    }
+
+    @Test
+    public void resolveSuccessfullyAsResolvedConfiguration() {
+        ResolvedConfiguration resolvedConfiguration = context.mock(ResolvedConfiguration.class);
+        prepareResolve(resolvedConfiguration, false);
+        assertThat(configuration.getResolvedConfiguration(), equalTo(resolvedConfiguration));
+        assertThat(configuration.getState(), equalTo(Configuration.State.RESOLVED));
+    }
+
+    private void prepareResolve(final ResolvedConfiguration resolvedConfiguration, final boolean withErrors) {
+        context.checking(new Expectations() {{
+            ResolverResults result = new ResolverResults();
+            result.resolved(resolvedConfiguration, context.mock(ResolutionResult.class), context.mock(ResolvedProjectConfigurationResults.class));
+            allowing(dependencyResolver).resolve(configuration);
+            will(returnValue(result));
+            allowing(resolvedConfiguration).hasError();
+            will(returnValue(withErrors));
+        }});
+    }
+
+    @Test
+    public void multipleResolvesShouldUseCachedResult() {
+        prepareResolve(context.mock(ResolvedConfiguration.class), true);
+        assertThat(configuration.getResolvedConfiguration(), sameInstance(configuration.getResolvedConfiguration()));
+    }
+
+    @Test
+    public void uploadTaskName() {
+        assertThat(configuration.getUploadTaskName(), equalTo("uploadName"));
+    }
+
+    private DefaultConfiguration createNamedConfiguration(String confName) {
+        return new DefaultConfiguration(confName, confName, configurationContainer,
+                dependencyResolver, listenerManager, metaDataProvider, new DefaultResolutionStrategy(), null);
+    }
+    
+    private DefaultConfiguration createNamedConfiguration(String path, String confName) {
+        return new DefaultConfiguration(path, confName, configurationContainer,
+                dependencyResolver, listenerManager, metaDataProvider, new DefaultResolutionStrategy(), null);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void buildArtifacts() {
+        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
+        final Task artifactTaskMock = context.mock(Task.class, "artifactTask");
+        final Configuration otherConfiguration = context.mock(Configuration.class);
+        final TaskDependency otherArtifactTaskDependencyMock = context.mock(TaskDependency.class, "otherConfTaskDep");
+        final PublishArtifact otherArtifact = context.mock(PublishArtifact.class, "otherArtifact");
+        final PublishArtifactSet inheritedArtifacts = new DefaultPublishArtifactSet("artifacts", toDomainObjectSet(PublishArtifact.class, otherArtifact));
+        DefaultPublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+        artifact.builtBy(artifactTaskMock);
+        configuration.getArtifacts().add(artifact);
+
+        context.checking(new Expectations() {{
+            allowing(otherConfiguration).getHierarchy();
+            will(returnValue(toSet()));
+
+            allowing(otherConfiguration).getAllArtifacts();
+            will(returnValue(inheritedArtifacts));
+
+            allowing(otherConfiguration).getAllDependencies();
+
+            allowing(otherArtifact).getBuildDependencies();
+            will(returnValue(otherArtifactTaskDependencyMock));
+            
+            allowing(otherArtifactTaskDependencyMock).getDependencies(with(any(Task.class)));
+            will(returnValue(toSet(otherConfTaskMock)));
+        }});
+        configuration.setExtendsFrom(toSet(otherConfiguration));
+        assertThat((Set<Task>) configuration.getAllArtifacts().getBuildDependencies().getDependencies(context.mock(Task.class, "caller")),
+                equalTo(toSet(artifactTaskMock, otherConfTaskMock)));
+    }
+
+    @Test
+    public void getAllArtifactFiles() {
+        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
+        final Task artifactTaskMock = context.mock(Task.class, "artifactTask");
+        final Configuration otherConfiguration = context.mock(Configuration.class);
+        final TaskDependency otherConfTaskDependencyMock = context.mock(TaskDependency.class, "otherConfTaskDep");
+        final TaskDependency artifactTaskDependencyMock = context.mock(TaskDependency.class, "artifactTaskDep");
+        final File artifactFile1 = new File("artifact1");
+        final File artifactFile2 = new File("artifact2");
+        final PublishArtifact artifact = context.mock(PublishArtifact.class, "artifact");
+        final PublishArtifact otherArtifact = context.mock(PublishArtifact.class, "otherArtifact");
+        final PublishArtifactSet otherArtifacts = new DefaultPublishArtifactSet("artifacts", toDomainObjectSet(PublishArtifact.class, otherArtifact));
+
+        context.checking(new Expectations() {{
+            allowing(otherConfiguration).getHierarchy();
+            will(returnValue(toSet()));
+
+            allowing(otherConfiguration).getAllArtifacts();
+            will(returnValue(otherArtifacts));
+
+            allowing(otherConfiguration).getAllDependencies();
+
+            allowing(otherConfiguration).getExtendsFrom();
+            will(returnValue(toSet()));
+
+            allowing(otherConfiguration).getArtifacts();
+            will(returnValue(toSet(otherArtifact)));
+
+            allowing(otherConfTaskDependencyMock).getDependencies(with(any(Task.class)));
+            will(returnValue(toSet(otherConfTaskMock)));
+
+            allowing(artifactTaskDependencyMock).getDependencies(with(any(Task.class)));
+            will(returnValue(toSet(artifactTaskMock)));
+
+            allowing(artifact).getFile();
+            will(returnValue(artifactFile1));
+
+            allowing(otherArtifact).getFile();
+            will(returnValue(artifactFile2));
+
+            allowing(artifact).getBuildDependencies();
+            will(returnValue(artifactTaskDependencyMock));
+
+            allowing(otherArtifact).getBuildDependencies();
+            will(returnValue(otherConfTaskDependencyMock));
+        }});
+
+        configuration.getArtifacts().add(artifact);
+        configuration.setExtendsFrom(toSet(otherConfiguration));
+
+        FileCollection files = configuration.getAllArtifacts().getFiles();
+        assertThat(files.getFiles(), equalTo(toSet(artifactFile1, artifactFile2)));
+        assertThat(files.getBuildDependencies().getDependencies(null), equalTo((Set) toSet(otherConfTaskMock, artifactTaskMock)));
+    }
+
+    @Test
+    public void buildDependenciesDelegatesToAllSelfResolvingDependencies() {
+        final Task target = context.mock(Task.class, "target");
+        final Task projectDepTaskDummy = context.mock(Task.class, "projectDepTask");
+        final Task fileDepTaskDummy = context.mock(Task.class, "fileDepTask");
+        final ProjectDependency projectDependencyStub = context.mock(ProjectDependency.class);
+        final FileCollectionDependency fileCollectionDependencyStub = context.mock(FileCollectionDependency.class);
+
+        context.checking(new Expectations() {{
+            TaskDependency projectTaskDependencyDummy = context.mock(TaskDependency.class, "projectDep");
+            TaskDependency fileTaskDependencyStub = context.mock(TaskDependency.class, "fileDep");
+
+            allowing(projectDependencyStub).getBuildDependencies();
+            will(returnValue(projectTaskDependencyDummy));
+
+            allowing(projectTaskDependencyDummy).getDependencies(target);
+            will(returnValue(toSet(projectDepTaskDummy)));
+
+            allowing(fileCollectionDependencyStub).getBuildDependencies();
+            will(returnValue(fileTaskDependencyStub));
+
+            allowing(fileTaskDependencyStub).getDependencies(target);
+            will(returnValue(toSet(fileDepTaskDummy)));
+        }});
+
+        configuration.getDependencies().add(projectDependencyStub);
+        configuration.getDependencies().add(fileCollectionDependencyStub);
+
+        assertThat(configuration.getBuildDependencies().getDependencies(target), equalTo((Set) toSet(fileDepTaskDummy,
+                projectDepTaskDummy)));
+    }
+
+    @Test
+    public void buildDependenciesDelegatesToInheritedConfigurations() {
+        final Task target = context.mock(Task.class, "target");
+        final Task otherConfTaskMock = context.mock(Task.class, "otherConfTask");
+        final TaskDependency dependencyTaskDependencyStub = context.mock(TaskDependency.class, "otherConfTaskDep");
+        final Configuration otherConfiguration = context.mock(Configuration.class, "otherConf");
+        final FileCollectionDependency fileCollectionDependencyStub = context.mock(FileCollectionDependency.class);
+        final DependencySet inherited = new DefaultDependencySet("dependencies", toDomainObjectSet(Dependency.class, fileCollectionDependencyStub));
+
+        context.checking(new Expectations() {{
+            allowing(otherConfiguration).getHierarchy();
+            will(returnValue(toSet()));
+
+            allowing(otherConfiguration).getAllArtifacts();
+
+            allowing(otherConfiguration).getAllDependencies();
+            will(returnValue(inherited));
+
+            allowing(fileCollectionDependencyStub).getBuildDependencies();
+            will(returnValue(dependencyTaskDependencyStub));
+
+            allowing(dependencyTaskDependencyStub).getDependencies(target);
+            will(returnValue(toSet(otherConfTaskMock)));
+        }});
+
+        configuration.extendsFrom(otherConfiguration);
+
+        assertThat(configuration.getBuildDependencies().getDependencies(target), equalTo((Set) toSet(otherConfTaskMock)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test public void taskDependencyFromProjectDependencyWithoutCommonConfiguration() {
+        // This test exists because a NullPointerException was thrown by
+        // getTaskDependencyFromProjectDependency() if the rootProject
+        // defined a task as the same name as a subproject's task, but did
+        // not define the same configuration.
+        final String configName = configuration.getName();
+        final String taskName = "testit";
+        final Task tdTask = context.mock(Task.class, "tdTask");
+        final Project taskProject = context.mock(Project.class, "taskProject");
+        final Project rootProject = context.mock(Project.class, "rootProject");
+        final Project dependentProject = context.mock(Project.class, "dependentProject");
+        final Task desiredTask = context.mock(Task.class, "desiredTask");
+        final Set<Task> taskSet = toSet(desiredTask);
+        final ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class);
+
+        context.checking(new Expectations() {{
+            allowing(tdTask).getProject(); will(returnValue(taskProject));
+            allowing(taskProject).getRootProject(); will(returnValue(rootProject));
+            allowing(rootProject).getTasksByName(taskName, true); will(returnValue(taskSet));
+            allowing(desiredTask).getProject(); will(returnValue(dependentProject));
+            allowing(dependentProject).getConfigurations(); will(returnValue(configurationContainer));
+
+            // return null to mock not finding the given configuration
+            allowing(configurationContainer).findByName(configName); will(returnValue(null));
+        }});
+
+        TaskDependency td = configuration.getTaskDependencyFromProjectDependency(false, taskName);
+        assertThat(td.getDependencies(tdTask), equalTo(Collections.EMPTY_SET));
+    }
+
+
+    @Test
+    public void getDependencies() {
+        Dependency dependency = context.mock(Dependency.class);
+        configuration.getDependencies().add(dependency);
+        assertThat(configuration.getDependencies(), hasSameItems(toSet(dependency)));
+    }
+
+    @Test
+    public void getTypedDependencies() {
+        ProjectDependency projectDependency = context.mock(ProjectDependency.class);
+        configuration.getDependencies().add(context.mock(Dependency.class));
+        configuration.getDependencies().add(projectDependency);
+        assertThat(configuration.getDependencies().withType(ProjectDependency.class), hasSameItems(toSet(projectDependency)));
+    }
+
+    @Test
+    public void getTypedDependenciesReturnsEmptySetWhenNoMatches() {
+        configuration.getDependencies().add(context.mock(Dependency.class));
+        assertThat(configuration.getDependencies().withType(ProjectDependency.class), isEmpty());
+    }
+
+    @Test
+    public void getAllDependencies() {
+        Dependency dependencyConf = createDependency("group1", "name1", "version1");
+        Dependency dependencyOtherConf1 = createDependency("group1", "name1", "version1");
+        Dependency dependencyOtherConf2 = context.mock(Dependency.class, "dep2");
+        Configuration otherConf = createNamedConfiguration("otherConf");
+        configuration.getDependencies().add(dependencyConf);
+        configuration.extendsFrom(otherConf);
+        otherConf.getDependencies().add(dependencyOtherConf1);
+        otherConf.getDependencies().add(dependencyOtherConf2);
+
+        assertThat(configuration.getAllDependencies(), hasSameItems(toLinkedSet(dependencyConf, dependencyOtherConf2)));
+        assertCorrectInstanceInAllDependencies(configuration.getAllDependencies(), dependencyConf);
+    }
+
+    @Test
+    public void getAllTypedDependencies() {
+        ProjectDependency projectDependencyCurrentConf = context.mock(ProjectDependency.class, "projectDepCurrentConf");
+        configuration.getDependencies().add(context.mock(Dependency.class, "depCurrentConf"));
+        configuration.getDependencies().add(projectDependencyCurrentConf);
+        Configuration otherConf = createNamedConfiguration("otherConf");
+        configuration.extendsFrom(otherConf);
+        ProjectDependency projectDependencyExtendedConf = context.mock(ProjectDependency.class, "projectDepExtendedConf");
+        otherConf.getDependencies().add(context.mock(Dependency.class, "depExtendedConf"));
+        otherConf.getDependencies().add(projectDependencyExtendedConf);
+
+        assertThat(configuration.getAllDependencies().withType(ProjectDependency.class), hasSameItems(toLinkedSet(projectDependencyCurrentConf, projectDependencyExtendedConf)));
+    }
+
+    @Test
+    public void getAllTypedDependenciesReturnsEmptySetWhenNoMatches() {
+        configuration.getDependencies().add(context.mock(Dependency.class, "depCurrentConf"));
+        Configuration otherConf = createNamedConfiguration("otherConf");
+        configuration.extendsFrom(otherConf);
+        otherConf.getDependencies().add(context.mock(Dependency.class, "depExtendedConf"));
+
+        assertThat(configuration.getAllDependencies().withType(ProjectDependency.class), isEmpty());
+    }
+
+    @Test
+    public void getAllArtifacts() {
+        PublishArtifact artifactConf = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+        PublishArtifact artifactOtherConf2 = createPublishArtifact("name2", "ext2", "type2", "classifier2");
+        Configuration otherConf = createNamedConfiguration("otherConf");
+        configuration.getArtifacts().add(artifactConf);
+        configuration.extendsFrom(otherConf);
+        otherConf.getArtifacts().add(artifactOtherConf2);
+        assertThat(configuration.getAllArtifacts(), hasSameItems(toLinkedSet(artifactConf, artifactOtherConf2)));
+    }
+
+    @Test
+    public void artifactAddedAction() {
+        final TestClosure closure = context.mock(TestClosure.class);
+        final PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+
+        context.checking(new Expectations() {{
+            one(closure).call(artifact);
+        }});
+
+        configuration.getArtifacts().whenObjectAdded(TestUtil.toClosure(closure));
+        configuration.getArtifacts().add(artifact);
+    }
+
+    @Test
+    public void artifactRemovedAction() {
+        final TestClosure closure = context.mock(TestClosure.class);
+        final PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+
+        configuration.getArtifacts().add(artifact);
+
+        context.checking(new Expectations() {{
+            one(closure).call(artifact);
+        }});
+
+        configuration.getArtifacts().whenObjectRemoved(TestUtil.toClosure(closure));
+
+        configuration.getArtifacts().remove(artifact);
+    }
+
+    @Test
+    public void removeArtifact() {
+        PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+        configuration.getArtifacts().add(artifact);
+        configuration.getArtifacts().remove(artifact);
+        assertThat(configuration.getAllArtifacts(), Matchers.isEmpty());
+    }
+
+    @Test
+    public void removeArtifactWithUnknownArtifact() {
+        PublishArtifact artifact = createPublishArtifact("name1", "ext1", "type1", "classifier1");
+        configuration.getArtifacts().add(artifact);
+        configuration.getArtifacts().remove(createPublishArtifact("name2", "ext1", "type1", "classifier1"));
+        assertThat(configuration.getAllArtifacts(), hasSameItems(toSet(artifact)));
+    }
+
+    private void assertCorrectInstanceInAllDependencies(Set<Dependency> allDependencies, Dependency correctInstance) {
+        for (Dependency dependency : allDependencies) {
+            if (dependency == correctInstance) {
+                return;
+            }
+        }
+        fail("Correct instance is missing!");
+    }
+
+    @Test
+    public void copy() {
+        prepareConfigurationForCopyTest();
+
+        Configuration copiedConfiguration = configuration.copy();
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, configuration.getDependencies());
+    }
+
+    @Test
+    public void copyWithSpec() {
+        prepareConfigurationForCopyTest();
+        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getDependencies());
+        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
+
+        Configuration copiedConfiguration = configuration.copy(new Spec<Dependency>() {
+            public boolean isSatisfiedBy(Dependency element) {
+                return !element.getGroup().equals("group3");
+            }
+        });
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
+    }
+
+    @Test
+    public void copyWithClosure() {
+        prepareConfigurationForCopyTest();
+        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getDependencies());
+        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
+
+        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
+        Configuration copiedConfiguration = configuration.copy(specClosure);
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
+    }
+
+    private void prepareConfigurationForCopyTest() {
+        configuration.setVisible(false);
+        configuration.setTransitive(false);
+        configuration.setDescription("descript");
+        configuration.exclude(toMap("group", "value"));
+        configuration.exclude(toMap("group", "value2"));
+        configuration.getArtifacts().add(createPublishArtifact("name1", "ext1", "type1", "classifier1"));
+        configuration.getArtifacts().add(createPublishArtifact("name2", "ext2", "type2", "classifier2"));
+        configuration.getDependencies().add(createDependency("group1", "name1", "version1"));
+        configuration.getDependencies().add(createDependency("group2", "name2", "version2"));
+    }
+
+    private void assertThatCopiedConfigurationHasElementsAndName(Configuration copiedConfiguration, Set<Dependency> expectedDependencies) {
+        assertThat(copiedConfiguration.getName(), equalTo(configuration.getName() + "Copy"));
+        assertThat(copiedConfiguration.isVisible(), equalTo(configuration.isVisible()));
+        assertThat(copiedConfiguration.isTransitive(), equalTo(configuration.isTransitive()));
+        assertThat(copiedConfiguration.getDescription(), equalTo(configuration.getDescription()));
+        assertThat(asSet(copiedConfiguration.getAllArtifacts()), equalTo(asSet(configuration.getAllArtifacts())));
+        assertThat(copiedConfiguration.getExcludeRules(), equalTo(configuration.getExcludeRules()));
+        assertThat(copiedConfiguration.getExcludeRules().iterator().next(), not(sameInstance(configuration.getExcludeRules().iterator().next())));
+        assertThat(WrapUtil.asSet(copiedConfiguration.getDependencies()), equalTo(WrapUtil.asSet(expectedDependencies)));
+        assertNotSameInstances(copiedConfiguration.getDependencies(), expectedDependencies);
+    }
+
+    @Test
+    public void copyRecursive() {
+        prepareConfigurationForCopyRecursiveTest();
+
+        Configuration copiedConfiguration = configuration.copyRecursive();
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, configuration.getAllDependencies());
+    }
+
+    @Test
+    public void copyRecursiveWithSpec() {
+        prepareConfigurationForCopyRecursiveTest();
+        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getAllDependencies());
+        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
+
+        Closure specClosure = TestUtil.toClosure("{ element ->  !element.group.equals(\"group3\")}");
+        Configuration copiedConfiguration = configuration.copyRecursive(specClosure);
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
+    }
+
+    @Test
+    public void copyRecursiveWithClosure() {
+        prepareConfigurationForCopyRecursiveTest();
+        Set<Dependency> expectedDependenciesToCopy = new HashSet<Dependency>(configuration.getAllDependencies());
+        configuration.getDependencies().add(createDependency("group3", "name3", "version3"));
+
+        Configuration copiedConfiguration = configuration.copyRecursive(new Spec<Dependency>() {
+            public boolean isSatisfiedBy(Dependency element) {
+                return !element.getGroup().equals("group3");
+            }
+        });
+
+        assertThatCopiedConfigurationHasElementsAndName(copiedConfiguration, expectedDependenciesToCopy);
+    }
+
+    private void prepareConfigurationForCopyRecursiveTest() {
+        prepareConfigurationForCopyTest();
+        Dependency similarDependency2InOtherConf = createDependency("group2", "name2", "version2");
+        Dependency otherConfDependency = createDependency("group4", "name4", "version4");
+        Configuration otherConf = createNamedConfiguration("otherConf");
+        otherConf.getDependencies().add(similarDependency2InOtherConf);
+        otherConf.getDependencies().add(otherConfDependency);
+        configuration.extendsFrom(otherConf);
+    }
+
+    private void assertNotSameInstances(Set<Dependency> dependencies, Set<Dependency> otherDependencies) {
+        for (Dependency dependency : dependencies) {
+            assertHasEqualButNotSameInstance(dependency, otherDependencies);
+        }
+    }
+
+    private void assertHasEqualButNotSameInstance(Dependency dependency, Set<Dependency> otherDependencies) {
+        assertThat(otherDependencies, hasItem(dependency));
+        for (Dependency otherDependency : otherDependencies) {
+            if (otherDependency.equals(dependency)) {
+                assertThat(otherDependency, not(sameInstance(dependency)));
+            }
+        }
+    }
+
+    @Test
+    public void propertyChangeWithNonUnresolvedStateShouldThrowEx() {
+        makeResolveReturnFileSet(new HashSet<File>());
+        configuration.resolve();
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.setTransitive(true);
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.setExcludeRules(new HashSet<ExcludeRule>());
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.setExtendsFrom(new HashSet<Configuration>());
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.setVisible(true);
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.getDependencies().add(context.mock(Dependency.class));
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.getDependencies().add(context.mock(Dependency.class));
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.exclude(new HashMap<String, String>());
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.extendsFrom(context.mock(Configuration.class));
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.getArtifacts().add(context.mock(PublishArtifact.class));
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.getArtifacts().remove(context.mock(PublishArtifact.class, "removeArtifact"));
+            }
+        });
+        assertInvalidUserDataException(new Executer() {
+            public void execute() {
+                configuration.getArtifacts().add(context.mock(PublishArtifact.class, "removeArtifact"));
+            }
+        });
+    }
+    
+    @Test
+    public void dumpString() {
+        Dependency configurationDependency = createDependency("dumpgroup1", "dumpname1", "dumpversion1");
+        Dependency otherConfSimilarDependency = createDependency("dumpgroup1", "dumpname1", "dumpversion1");
+        Dependency otherConfDependency = createDependency("dumpgroup2", "dumpname2", "dumpversion2");
+        Configuration otherConf = createNamedConfiguration("dumpConf");
+        configuration.extendsFrom(otherConf);
+        otherConf.getDependencies().add(otherConfDependency);
+        otherConf.getDependencies().add(otherConfSimilarDependency);
+        configuration.getDependencies().add(configurationDependency);
+
+        assertThat(configuration.dump(),
+                containsString(
+                "\nConfiguration:"
+                + "  class='class org.gradle.api.internal.artifacts.configurations.DefaultConfiguration'"
+                + "  name='name'"
+                + "  hashcode='"+ configuration.hashCode() +"'"
+                + "\nLocal Dependencies:"
+                + "\n   DefaultExternalModuleDependency{group='dumpgroup1', name='dumpname1', version='dumpversion1', configuration='default'}"
+                + "\nLocal Artifacts:"
+                + "\n   none"
+                + "\nAll Dependencies:"
+                + "\n   DefaultExternalModuleDependency{group='dumpgroup1', name='dumpname1', version='dumpversion1', configuration='default'}"
+                + "\n   DefaultExternalModuleDependency{group='dumpgroup2', name='dumpname2', version='dumpversion2', configuration='default'}"
+                + "\nAll Artifacts:"
+                + "\n   none"));
+    }
+
+    ModuleDependency createDependency(String group, String name, String version) {
+        return new DefaultExternalModuleDependency(group, name, version);
+    }
+
+    DefaultPublishArtifact createPublishArtifact(String name, String extension, String type, String classifier) {
+        return new DefaultPublishArtifact(name, extension, type, classifier, new Date(), new File(""));
+    }
+
+    private void assertInvalidUserDataException(Executer executer) {
+        try {
+            executer.execute();
+            fail();
+        } catch (InvalidUserDataException e) {
+            // ignore
+        }
+    }
+
+    private static interface Executer {
+        void execute();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjectsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjectsTest.groovy
new file mode 100644
index 0000000..0f0bf22
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromDependentProjectsTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations
+
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class TasksFromDependentProjectsTest extends Specification {
+
+    def context = Mock(TaskDependencyResolveContext)
+    def root = TestUtil.createRootProject()
+    def child1 = TestUtil.createChildProject(root, "child1")
+    def child2 = TestUtil.createChildProject(root, "child2")
+    def child3 = TestUtil.createChildProject(root, "child3")
+    def child4 = TestUtil.createChildProject(root, "child4")
+
+    def checker = Mock(TasksFromDependentProjects.TaskDependencyChecker)
+
+    def "provides all tasks from dependent projects"() {
+        def dep = new TasksFromDependentProjects("buildDependents", "testRuntime", checker)
+
+        [child1, child2, child3].each {
+            it.configurations.create "testRuntime"
+            it.configurations.create "conf"
+            it.tasks.create "buildDependents"
+            it.tasks.create "someTask"
+        }
+
+        when: dep.resolve(context)
+        then:
+        1 * context.getTask() >> child1.tasks["buildDependents"]
+        1 * checker.isDependent(child1, "testRuntime", child1) >> false
+        1 * checker.isDependent(child1, "testRuntime", child2) >> false
+        1 * checker.isDependent(child1, "testRuntime", child3) >> true
+        1 * context.add(child3.tasks["buildDependents"])
+        0 * _
+    }
+
+    def "knows which tasks come from dependent projects with specific configuration"() {
+        def checker = new TasksFromDependentProjects.TaskDependencyChecker()
+
+        child2.configurations.create "testRuntime"
+        child2.dependencies.add("testRuntime", child1) //good
+
+        child3.configurations.create "conf"
+        child3.configurations.create "testRuntime"
+        child3.dependencies.add("conf", child1) //different config dependency, no good
+
+        child4.configurations.create "conf"     //different config, no good
+
+        expect:
+        checker.isDependent(child1, "testRuntime", child2)
+        !checker.isDependent(child1, "testRuntime", child3)
+        !checker.isDependent(child1, "testRuntime", child4)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependenciesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependenciesTest.groovy
new file mode 100644
index 0000000..15476e2
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/TasksFromProjectDependenciesTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.configurations
+
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class TasksFromProjectDependenciesTest extends Specification {
+
+    def dependencies = Mock(DependencySet)
+    def context = Mock(TaskDependencyResolveContext)
+    def projectAccessListener = Mock(ProjectAccessListener)
+    def project1 = TestUtil.createRootProject()
+    def project2 = TestUtil.createChildProject(project1, "project2")
+    def projectDep1 = Mock(ProjectDependency) { getDependencyProject() >> project1 }
+    def projectDep2 = Mock(ProjectDependency) { getDependencyProject() >> project2 }
+    def taskContainerDummy = project1.tasks
+
+    def "provides tasks from project dependencies"() {
+        def tasks = new TasksFromProjectDependencies("buildNeeded", dependencies, projectAccessListener)
+
+        project1.tasks.create "buildNeeded"
+        project2.tasks.create "foo"
+
+        when: tasks.resolveProjectDependencies(context, [projectDep1, projectDep2] as Set)
+
+        then:
+        1 * context.add(project1.tasks["buildNeeded"])
+        0 * context._
+    }
+
+    def "notifies the listener about project access"() {
+        def tasks = new TasksFromProjectDependencies("buildNeeded", dependencies, projectAccessListener)
+
+        def project1 = Mock(ProjectInternal) { getTasks() >> taskContainerDummy }
+        def projectDep1 = Mock(ProjectDependency) { getDependencyProject() >> project1}
+
+        when: tasks.resolveProjectDependencies(context, [projectDep1] as Set)
+
+        then:
+        1 * projectAccessListener.beforeResolvingProjectDependency(project1)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ArtifactFileTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainerTest.groovy
new file mode 100644
index 0000000..638e2c6
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentModuleMetadataContainerTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvalidUserDataException
+import spock.lang.Specification
+import spock.lang.Subject
+
+import static org.gradle.api.internal.artifacts.DefaultModuleIdentifier.newId
+
+class ComponentModuleMetadataContainerTest extends Specification {
+
+    @Subject replacements = new ComponentModuleMetadataContainer()
+
+    def "keeps track of replacements"() {
+        replacements.module("com.google.collections:google-collections").replacedBy("com.google.guava:guava");
+        replacements.module(newId("foo", "bar")).replacedBy(newId("foo", "xxx"));
+
+        expect:
+        replacements.getReplacementFor(newId("com.google.collections", "google-collections")) == newId("com.google.guava", "guava")
+        replacements.getReplacementFor(newId("foo", "bar")) == newId("foo", "xxx")
+
+        !replacements.getReplacementFor(newId("com.google.guava", "guava"))
+        !replacements.getReplacementFor(newId("bar", "foo"))
+    }
+
+    def "does not allow replacing with the same module"() {
+        when: replacements.module("o:o").replacedBy("o:o")
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message == "Cannot declare module replacement that replaces self: o:o->o:o"
+    }
+
+    def "detects cycles early"() {
+        replacements.module("o:a").replacedBy("o:b")
+        when: replacements.module("o:b").replacedBy("o:a")
+
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message == "Cannot declare module replacement o:b->o:a because it introduces a cycle: o:b->o:a->o:b"
+    }
+
+    def "detects transitive cycles early"() {
+        replacements.module("o:o").replacedBy("o:x")
+        //a->b->c->a
+        replacements.module("o:a").replacedBy("o:b")
+        replacements.module("o:b").replacedBy("o:c")
+        when: replacements.module("o:c").replacedBy("o:a")
+
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message == "Cannot declare module replacement o:c->o:a because it introduces a cycle: o:c->o:a->o:b->o:c"
+    }
+
+    def "provides module metadata information"() {
+        def module = replacements.module("com.google.collections:google-collections")
+
+        expect:
+        module.id == newId("com.google.collections", "google-collections")
+        module.replacedBy == null
+
+        when: module.replacedBy("a:b")
+        then: module.replacedBy == newId("a", "b")
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy
new file mode 100644
index 0000000..407a91e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvalidUserDataException
+import org.gradle.api.Project
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.dsl.ComponentSelectorParsers.multiParser
+import static org.gradle.api.internal.artifacts.dsl.ComponentSelectorParsers.parser
+
+public class ComponentSelectorParsersTest extends Specification {
+
+    def "understands group:name:version notation"() {
+        when:
+        def v = multiParser().parseNotation("org.foo:bar:1.0") as List
+
+        then:
+        v.size() == 1
+        v[0] instanceof ModuleComponentSelector
+        v[0].group == 'org.foo'
+        v[0].module  == 'bar'
+        v[0].version  == '1.0'
+    }
+
+    def "works with CharSequences"() {
+        when:
+        def sb = new StringBuilder().append("org.foo:charsequence:1.0")
+        def v = multiParser().parseNotation(sb) as List
+
+        then:
+        v.size() == 1
+        v[0] instanceof ModuleComponentSelector
+        v[0].module  == 'charsequence'
+    }
+
+    def "allows exact type on input"() {
+        def id = DefaultModuleComponentSelector.newSelector("org.foo", "bar", "2.0")
+
+        when:
+        def v = multiParser().parseNotation(id) as List
+
+        then:
+        v.size() == 1
+        v[0] instanceof ModuleComponentSelector
+        v[0].group == 'org.foo'
+        v[0].module == 'bar'
+        v[0].version  == '2.0'
+    }
+
+    def "allows list of objects on input"() {
+        def id = DefaultModuleComponentSelector.newSelector("org.foo", "bar", "2.0")
+
+        when:
+        def v = multiParser().parseNotation([id, ["hey:man:1.0"], [group:'i', name:'like', version:'maps']]) as List
+
+        then:
+        v.size() == 3
+        v[0].module == 'bar'
+        v[1].module == 'man'
+        v[2].module == 'like'
+    }
+
+    def "allows map on input"() {
+        when:
+        def v = multiParser().parseNotation([group: 'org.foo', name: 'bar', version:'1.0']) as List
+
+        then:
+        v.size() == 1
+        v[0] instanceof ModuleComponentSelector
+        v[0].group == 'org.foo'
+        v[0].module  == 'bar'
+        v[0].version  == '1.0'
+    }
+
+    def "understands project input"() {
+        when:
+        def project = Mock(Project) {
+            getPath() >> ":bar"
+        }
+        def v = multiParser().parseNotation(project) as List
+
+        then:
+        v.size() == 1
+        v[0] instanceof ProjectComponentSelector
+        v[0].projectPath == ":bar"
+    }
+
+    def "fails for unknown types"() {
+        when:
+        multiParser().parseNotation(new Object())
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "reports missing keys for map notation"() {
+        when:
+        multiParser().parseNotation([name: "bar", version: "1.0"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports wrong keys for map notation"() {
+        when:
+        multiParser().parseNotation([groop: 'groop', name: "bar", version: "1.0"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports invalid format for string notation"() {
+        when:
+        multiParser().parseNotation(["blahblah"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports invalid missing data for string notation"() {
+        when:
+        multiParser().parseNotation([":foo:"])
+
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message.contains 'cannot be empty'
+    }
+
+    def "null is an invalid input"() {
+        when:
+        multiParser().parseNotation(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+
+        when:
+        parser().parseNotation(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "single parser understands String notation"() {
+        //just smoke testing the single parser, it is covered in multiParser, too.
+        when:
+        def v = parser().parseNotation("org.foo:bar:1.0")
+
+        then:
+        v instanceof ModuleComponentSelector
+        v.group == 'org.foo'
+        v.module  == 'bar'
+        v.version  == '1.0'
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy
new file mode 100644
index 0000000..9625c24
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultComponentMetadataHandlerTest.groovy
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvalidUserCodeException
+import org.gradle.api.artifacts.ComponentMetadataDetails
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.NamespaceId
+import org.gradle.api.specs.Specs
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.IvyModuleResolveMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.rules.RuleAction
+import org.gradle.internal.rules.RuleActionAdapter
+import org.gradle.internal.rules.RuleActionValidationException
+import org.gradle.internal.typeconversion.NotationParser
+import spock.lang.Specification
+
+import javax.xml.namespace.QName
+
+class DefaultComponentMetadataHandlerTest extends Specification {
+    private static final String GROUP = "group"
+    private static final String MODULE = "module"
+
+    // For testing ModuleMetadataProcessor capabilities
+    def handler = new DefaultComponentMetadataHandler(DirectInstantiator.INSTANCE)
+
+    // For testing ComponentMetadataHandler capabilities
+    RuleActionAdapter<ComponentMetadataDetails> adapter = Mock(RuleActionAdapter)
+    NotationParser<Object, String> notationParser = Mock(NotationParser)
+    def mockedHandler = new DefaultComponentMetadataHandler(DirectInstantiator.INSTANCE, adapter, notationParser)
+    def ruleAction = Stub(RuleAction)
+
+    def "add action rule that applies to all components" () {
+        def action = new Action<ComponentMetadataDetails>() {
+            @Override
+            void execute(ComponentMetadataDetails componentMetadataDetails) { }
+        }
+
+        when:
+        mockedHandler.all action
+
+        then:
+        1 * adapter.createFromAction(action) >> ruleAction
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add closure rule that applies to all components" () {
+        def closure = { ComponentMetadataDetails cmd -> }
+
+        when:
+        mockedHandler.all closure
+
+        then:
+        1 * adapter.createFromClosure(ComponentMetadataDetails, closure) >> ruleAction
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add rule source rule that applies to all components" () {
+        def ruleSource = new Object()
+
+        when:
+        mockedHandler.all(ruleSource)
+
+        then:
+        1 * adapter.createFromRuleSource(ComponentMetadataDetails, ruleSource) >> ruleAction
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add action rule that applies to module" () {
+        def action = new Action<ComponentMetadataDetails>() {
+            @Override
+            void execute(ComponentMetadataDetails componentMetadataDetails) { }
+        }
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        mockedHandler.withModule(notation, action)
+
+        then:
+        1 * adapter.createFromAction(action) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "add closure rule that applies to module" () {
+        def closure = { ComponentMetadataDetails cmd -> }
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        mockedHandler.withModule(notation, closure)
+
+        then:
+        1 * adapter.createFromClosure(ComponentMetadataDetails, closure) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "add rule source rule that applies to module" () {
+        def ruleSource = new Object()
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        mockedHandler.withModule(notation, ruleSource)
+
+        then:
+        1 * adapter.createFromRuleSource(ComponentMetadataDetails, ruleSource) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        mockedHandler.rules.size() == 1
+        mockedHandler.rules[0].action == (ruleAction)
+        mockedHandler.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "propagates error creating rule for closure" () {
+        when:
+        mockedHandler.all { }
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad closure"
+
+        and:
+        1 * adapter.createFromClosure(ComponentMetadataDetails, _) >> { throw new InvalidUserCodeException("bad closure") }
+    }
+
+    def "propagates error creating rule for action" () {
+        def action = Mock(Action)
+
+        when:
+        mockedHandler.all action
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad action"
+
+        and:
+        1 * adapter.createFromAction(action) >> { throw new InvalidUserCodeException("bad action") }
+    }
+
+    def "propagates error creating rule for rule source" () {
+        when:
+        mockedHandler.all(new Object())
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad rule source"
+
+        and:
+        1 * adapter.createFromRuleSource(ComponentMetadataDetails, _) >> { throw new InvalidUserCodeException("bad rule source") }
+    }
+
+    def "processing fails when status is not present in status scheme"() {
+        def metadata = Stub(MutableModuleComponentResolveMetaData) {
+            getComponentId() >> DefaultModuleComponentIdentifier.newId("group", "module", "version")
+            getStatus() >> "green"
+            getStatusScheme() >> ["alpha", "beta"]
+        }
+
+        when:
+        handler.processMetadata(metadata)
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e.message == /Unexpected status 'green' specified for group:module:version. Expected one of: [alpha, beta]/
+    }
+
+    def "produces sensible error when rule action throws an exception" () {
+        def failure = new Exception("from test")
+        def metadata = Stub(TestIvyMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+        }
+
+        when:
+        handler.all { throw failure }
+
+        and:
+        handler.processMetadata(metadata)
+
+        then:
+        InvalidUserCodeException e = thrown()
+        e.message == "There was an error while evaluating a component metadata rule for group:module:version."
+        e.cause == failure
+    }
+
+    def "all rules get evaluated" () {
+        def metadata = Stub(TestIvyMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+        }
+        def closuresCalled = []
+
+        when:
+        handler.all { ComponentMetadataDetails cmd -> closuresCalled << 1 }
+        handler.all { ComponentMetadataDetails cmd -> closuresCalled << 2 }
+        handler.all { ComponentMetadataDetails cmd, IvyModuleDescriptor imd -> closuresCalled << 3 }
+
+        and:
+        handler.processMetadata(metadata)
+
+        then:
+        closuresCalled.sort() == [ 1, 2, 3 ]
+    }
+
+    def "supports rule with typed ComponentMetaDataDetails parameter"() {
+        def metadata = Stub(MutableModuleComponentResolveMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+        }
+        def capturedDetails = null
+        handler.all { ComponentMetadataDetails details ->
+            capturedDetails = details
+        }
+
+        when:
+        handler.processMetadata(metadata)
+
+        then:
+        noExceptionThrown()
+        capturedDetails instanceof ComponentMetadataDetails
+        with(capturedDetails) {
+            id.group == "group"
+            id.name == "module"
+            id.version == "version"
+            status == "integration"
+            statusScheme == ["integration", "release"]
+        }
+    }
+
+    def "supports rule with typed IvyModuleDescriptor parameter"() {
+        def id1 = new NamespaceId('namespace', 'info1')
+        def id2 = new NamespaceId('namespace', 'info2')
+        def metadata = Stub(TestIvyMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+            getExtraInfo() >> [(id1): "info1 value", (id2): "info2 value"]
+            getBranch() >> "someBranch"
+        }
+        def capturedDescriptor = null
+        handler.all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            capturedDescriptor = descriptor
+        }
+
+        when:
+        handler.processMetadata(metadata)
+
+        then:
+        noExceptionThrown()
+        capturedDescriptor instanceof IvyModuleDescriptor
+        with(capturedDescriptor) {
+            extraInfo.asMap() == [(new QName(id1.namespace, id1.name)): "info1 value", (new QName(id2.namespace, id2.name)): "info2 value"]
+            branch == "someBranch"
+            ivyStatus == "integration"
+        }
+    }
+
+    def "rule with IvyModuleDescriptor parameter does not get invoked for non-Ivy components"() {
+        def metadata = Stub(MutableModuleComponentResolveMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+        }
+
+        def invoked = false
+        handler.all { ComponentMetadataDetails details, IvyModuleDescriptor descriptor ->
+            invoked = true
+        }
+
+        when:
+        handler.processMetadata(metadata)
+
+        then:
+        !invoked
+    }
+
+    def "complains if first parameter type isn't assignment compatible with ComponentMetadataDetails"() {
+        when:
+        handler.all { String s -> }
+
+        then:
+        InvalidUserCodeException e = thrown()
+        e.message == "The closure provided is not valid as a rule for 'ComponentMetadataHandler'."
+        e.cause instanceof RuleActionValidationException
+        e.cause.message == "First parameter of rule action closure must be of type 'ComponentMetadataDetails'."
+    }
+
+    def "complains if rule has unsupported parameter type"() {
+        def metadata = Stub(MutableModuleComponentResolveMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+        }
+
+        when:
+        handler.all { ComponentMetadataDetails details, String str -> }
+
+        then:
+        InvalidUserCodeException e = thrown()
+        e.message == "The closure provided is not valid as a rule for 'ComponentMetadataHandler'."
+        e.cause instanceof RuleActionValidationException
+        e.cause.message == "Rule may not have an input parameter of type: java.lang.String. Second parameter must be of type: org.gradle.api.artifacts.ivy.IvyModuleDescriptor."
+    }
+
+    def "supports rule with multiple inputs in arbitrary order"() {
+        def id1 = new NamespaceId('namespace', 'info1')
+        def id2 = new NamespaceId('namespace', 'info2')
+        def metadata = Stub(TestIvyMetaData) {
+            getId() >> new DefaultModuleVersionIdentifier("group", "module", "version")
+            getStatus() >> "integration"
+            getStatusScheme() >> ["integration", "release"]
+            getExtraInfo() >> [(id1): "info1 value", (id2): "info2 value"]
+        }
+
+        def capturedDetails1 = null
+        def capturedDescriptor1 = null
+        def capturedDescriptor2 = null
+
+        handler.all { ComponentMetadataDetails details1, IvyModuleDescriptor descriptor1, IvyModuleDescriptor descriptor2  ->
+            capturedDetails1 = details1
+            capturedDescriptor1 = descriptor1
+            capturedDescriptor2 = descriptor2
+        }
+
+        when:
+        handler.processMetadata(metadata)
+
+        then:
+        noExceptionThrown()
+        capturedDetails1 instanceof ComponentMetadataDetails
+        with(capturedDetails1) {
+            id.group == "group"
+            id.name == "module"
+            id.version == "version"
+            status == "integration"
+            statusScheme == ["integration", "release"]
+        }
+        capturedDescriptor1 instanceof IvyModuleDescriptor
+        with(capturedDescriptor1) {
+            extraInfo.asMap() == [(new QName(id1.namespace, id1.name)): "info1 value", (new QName(id2.namespace, id2.name)): "info2 value"]
+        }
+        capturedDescriptor2 instanceof IvyModuleDescriptor
+        with(capturedDescriptor2) {
+            extraInfo.asMap() == [(new QName(id1.namespace, id1.name)): "info1 value", (new QName(id2.namespace, id2.name)): "info2 value"]
+        }
+    }
+
+    def "ComponentMetadataDetailsSpec matches on group and name" () {
+        def spec = new DefaultComponentMetadataHandler.ComponentMetadataDetailsMatchingSpec(DefaultModuleIdentifier.newId(group, name))
+        def id = Mock(ModuleVersionIdentifier) {
+            1 * getGroup() >> { "org.gradle" }
+            (0..1) * getName() >> { "api" }
+        }
+        def details = Stub(ComponentMetadataDetails) {
+            getId() >> id
+        }
+
+        expect:
+        spec.isSatisfiedBy(details) == matches
+
+        where:
+        group        | name  | matches
+        "org.gradle" | "api" | true
+        "com.gradle" | "api" | false
+        "org.gradle" | "lib" | false
+    }
+
+    interface TestIvyMetaData extends IvyModuleResolveMetaData, MutableModuleComponentResolveMetaData {}
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
new file mode 100644
index 0000000..9f177e7
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ModuleVersionSelectorParsersTest.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.dsl;
+
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.multiParser
+import static org.gradle.api.internal.artifacts.dsl.ModuleVersionSelectorParsers.parser
+
+public class ModuleVersionSelectorParsersTest extends Specification {
+
+    def "understands group:name:version notation"() {
+        when:
+        def v = multiParser().parseNotation("org.foo:bar:1.0") as List
+
+        then:
+        v.size() == 1
+        v[0].group == 'org.foo'
+        v[0].name  == 'bar'
+        v[0].version  == '1.0'
+    }
+
+    def "works with CharSequences"() {
+        when:
+        def sb = new StringBuilder().append("org.foo:charsequence:1.0")
+        def v = multiParser().parseNotation(sb) as List
+
+        then:
+        v.size() == 1
+        v[0].name  == 'charsequence'
+    }
+
+    def "allows exact type on input"() {
+        def id = newSelector("org.foo", "bar", "2.0")
+
+        when:
+        def v = multiParser().parseNotation(id) as List
+
+        then:
+        v.size() == 1
+        v[0].group == 'org.foo'
+        v[0].name  == 'bar'
+        v[0].version  == '2.0'
+    }
+
+    def "allows list of objects on input"() {
+        def id = newSelector("org.foo", "bar", "2.0")
+
+        when:
+        def v = multiParser().parseNotation([id, ["hey:man:1.0"], [group:'i', name:'like', version:'maps']]) as List
+
+        then:
+        v.size() == 3
+        v[0].name == 'bar'
+        v[1].name == 'man'
+        v[2].name == 'like'
+    }
+
+    def "allows map on input"() {
+        when:
+        def v = multiParser().parseNotation([group: 'org.foo', name: 'bar', version:'1.0']) as List
+
+        then:
+        v.size() == 1
+        v[0].group == 'org.foo'
+        v[0].name  == 'bar'
+        v[0].version  == '1.0'
+    }
+
+    def "fails for unknown types"() {
+        when:
+        multiParser().parseNotation(new Object())
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "reports missing keys for map notation"() {
+        when:
+        multiParser().parseNotation([name: "bar", version: "1.0"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports wrong keys for map notation"() {
+        when:
+        multiParser().parseNotation([groop: 'groop', name: "bar", version: "1.0"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports invalid format for string notation"() {
+        when:
+        multiParser().parseNotation(["blahblah"])
+
+        then:
+        thrown(InvalidUserDataException)
+    }
+
+    def "reports invalid missing data for string notation"() {
+        when:
+        multiParser().parseNotation([":foo:"])
+
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message.contains 'cannot be empty'
+    }
+
+    def "null is an invalid input"() {
+        when:
+        multiParser().parseNotation(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+
+        when:
+        parser().parseNotation(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "single parser understands String notation"() {
+        //just smoke testing the single parser, it is covered in multiParser, too.
+        when:
+        def v = parser().parseNotation("org.foo:bar:1.0")
+
+        then:
+        v.group == 'org.foo'
+        v.name  == 'bar'
+        v.version  == '1.0'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationConverterFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationConverterFactoryTest.groovy
new file mode 100644
index 0000000..60c1720
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/PublishArtifactNotationConverterFactoryTest.groovy
@@ -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.internal.artifacts.dsl
+
+import org.gradle.api.Task
+import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.internal.ThreadGlobalInstantiator
+import org.gradle.api.internal.artifacts.ModuleInternal
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.tasks.bundling.AbstractArchiveTask
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.typeconversion.NotationParser
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+import java.awt.*
+
+public class PublishArtifactNotationConverterFactoryTest extends Specification {
+    final DependencyMetaDataProvider provider = Mock()
+    final Instantiator instantiator = ThreadGlobalInstantiator.getOrCreate()
+    final PublishArtifactNotationParserFactory publishArtifactNotationParserFactory = new PublishArtifactNotationParserFactory(instantiator, provider)
+    final NotationParser<Object, PublishArtifact> publishArtifactNotationParser = publishArtifactNotationParserFactory.create();
+
+    def setup() {
+        ModuleInternal module = Mock()
+        _ * provider.module >> module
+        _ * module.version >> '1.2'
+    }
+
+    def createArtifactFromPublishArtifactInstance() {
+        PublishArtifact original = Mock()
+
+        when:
+        def publishArtifact = publishArtifactNotationParser.parseNotation(original)
+
+        then:
+        publishArtifact == original
+    }
+
+    def createArtifactFromArchiveTask() {
+        AbstractArchiveTask archiveTask = Mock()
+        archiveTask.getArchivePath() >> new File("")
+
+        when:
+        def publishArtifact = publishArtifactNotationParser.parseNotation(archiveTask)
+
+        then:
+        publishArtifact instanceof ArchivePublishArtifact
+        publishArtifact.archiveTask == archiveTask
+    }
+
+    def createArtifactFromFile() {
+        def file = new File("some.zip")
+
+        when:
+        def publishArtifact = publishArtifactNotationParser.parseNotation(file)
+
+        then:
+        publishArtifact instanceof DefaultPublishArtifact
+        publishArtifact.file == file
+    }
+
+    def "creates artifact from extension-less file"() {
+        def file = new File("someFile")
+
+        when:
+        def publishArtifact = publishArtifactNotationParser.parseNotation(file)
+
+        then:
+        publishArtifact instanceof DefaultPublishArtifact
+        publishArtifact.file == file
+        publishArtifact.type == ''
+    }
+
+    def createArtifactFromFileInMap() {
+        Task task = Mock()
+        def file = new File("some-file-1.2-classifier.zip")
+
+        when:
+        def publishArtifact = publishArtifactNotationParser.parseNotation(file: file, type: 'someType', builtBy: task)
+
+        then:
+        publishArtifact instanceof DefaultPublishArtifact
+        publishArtifact.file == file
+        publishArtifact.type == 'someType'
+        publishArtifact.name == 'some-file'
+        publishArtifact.extension == 'zip'
+        publishArtifact.classifier == 'classifier'
+        publishArtifact.buildDependencies.getDependencies(null) == [task] as Set
+    }
+
+    public void createArtifactWithNullNotationShouldThrowInvalidUserDataEx() {
+        when:
+        publishArtifactNotationParser.parseNotation(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    public void createArtifactWithUnknownNotationShouldThrowInvalidUserDataEx() {
+        when:
+        publishArtifactNotationParser.parseNotation(new Point(1, 2))
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeTest.groovy
new file mode 100644
index 0000000..085166b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ArtifactTypeTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component.ArtifactType
+import spock.lang.Specification
+
+class ArtifactTypeTest extends Specification {
+    def "have sensible toString values"() {
+        expect:
+        ArtifactType.SOURCES.toString() == "'sources' artifacts"
+        ArtifactType.JAVADOC.toString() == "'javadoc' artifacts"
+        ArtifactType.IVY_DESCRIPTOR.toString() == "'ivy descriptor' artifacts"
+        ArtifactType.MAVEN_POM.toString() == "'maven pom' artifacts"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
new file mode 100644
index 0000000..2606d72
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.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
+
+import org.gradle.util.VersionNumber
+import spock.lang.Specification
+
+class CacheLayoutTest extends Specification {
+    def "use root layout"() {
+        when:
+        CacheLayout cacheLayout = CacheLayout.ROOT
+
+        then:
+        cacheLayout.key == 'modules-2'
+        cacheLayout.version == VersionNumber.parse("2.0.0")
+        cacheLayout.formattedVersion == '2'
+        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/modules-2')
+    }
+
+    def "use file store layout"() {
+        when:
+        CacheLayout cacheLayout = CacheLayout.FILE_STORE
+
+        then:
+        cacheLayout.key == 'files-2.1'
+        cacheLayout.version == VersionNumber.parse("2.1.0")
+        cacheLayout.formattedVersion == '2.1'
+        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/files-2.1')
+    }
+
+    def "use metadata store layout"() {
+        when:
+        CacheLayout cacheLayout = CacheLayout.META_DATA
+
+        then:
+        cacheLayout.key == 'metadata-2.15'
+        cacheLayout.version == VersionNumber.parse("2.15.0")
+        cacheLayout.formattedVersion == '2.15'
+        cacheLayout.getPath(new File('some/dir')) == new File('some/dir/metadata-2.15')
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.groovy
new file mode 100644
index 0000000..fb88c75
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLockingArtifactDependencyResolverTest.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.artifacts.ivyservice
+
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import spock.lang.Specification
+
+class CacheLockingArtifactDependencyResolverTest extends Specification {
+    final CacheLockingManager lockingManager = Mock()
+    final ArtifactDependencyResolver target = Mock()
+    final GlobalDependencyResolutionRules metadataHandler = Stub()
+    final List<ResolutionAwareRepository> repositories = [Mock(ResolutionAwareRepository)]
+    final CacheLockingArtifactDependencyResolver resolver = new CacheLockingArtifactDependencyResolver(lockingManager, target)
+
+    def "resolves while holding a lock on the cache"() {
+        ConfigurationInternal configuration = Mock()
+        ResolverResults resolverResults = Mock()
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, resolverResults)
+
+        then:
+        1 * lockingManager.useCache("resolve $configuration", !null) >> { String s, Runnable r ->
+            r.run()
+        }
+        1 * target.resolve(configuration, repositories, metadataHandler, resolverResults)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManagerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManagerTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManagerTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultCacheLockingManagerTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
new file mode 100644
index 0000000..7a82a34
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultDependencyResolveDetailsSpec.groovy
@@ -0,0 +1,154 @@
+/*
+ * 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.gradle.api.artifacts.ModuleVersionSelector
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import spock.lang.Specification
+
+class DefaultDependencyResolveDetailsSpec extends Specification {
+
+    def "can specify version to use"() {
+        when:
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "1.0")
+        !details.updated
+        !details.selectionReason
+
+        when:
+        details.useVersion("1.0") //the same version
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "1.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+
+        when:
+        details.useVersion("2.0") //different version
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "2.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "can specify version with selection reason"() {
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        when:
+        details.useVersion("1.0", VersionSelectionReasons.FORCED) //same version
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "1.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.FORCED
+
+        when:
+        details.useVersion("3.0", VersionSelectionReasons.FORCED) //different version
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "3.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.FORCED
+    }
+
+    def "can override version and selection reason"() {
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        when:
+        details.useVersion("2.0", VersionSelectionReasons.FORCED)
+        details.useVersion("3.0", VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        details.requested == newVersionSelector("org", "foo", "1.0")
+        details.target == newVersionSelector("org", "foo", "3.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "does not allow null version"() {
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        when:
+        details.useVersion(null)
+
+        then:
+        thrown(IllegalArgumentException)
+
+        when:
+        details.useVersion(null, VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+
+    def "can specify target module"() {
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        when:
+        details.useTarget("org:bar:2.0")
+
+        then:
+        details.target.toString() == 'org:bar:2.0'
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "can mix configuring version and target module"() {
+        def details = newDependencyResolveDetails("org", "foo", "1.0")
+
+        when:
+        details.useVersion("1.5")
+
+        then:
+        details.target.toString() == 'org:foo:1.5'
+
+        when:
+        details.useTarget("com:bar:3.0")
+
+        then:
+        details.target.toString() == 'com:bar:3.0'
+
+        when:
+        details.useVersion('5.0')
+
+        then:
+        details.target.toString() == 'com:bar:5.0'
+    }
+
+    private static def newDependencyResolveDetails(String group, String name, String version) {
+        return new DefaultDependencyResolveDetails(new DefaultModuleDependencySubstitution(newComponentSelector(group, name, version), newVersionSelector(group, name, version)))
+    }
+
+    private static ModuleComponentSelector newComponentSelector(String group, String module, String version) {
+        return DefaultModuleComponentSelector.newSelector(group, module, version)
+    }
+
+    private static ModuleVersionSelector newVersionSelector(String group, String name, String version) {
+        return DefaultModuleVersionSelector.newSelector(group, name, version)
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyContextManagerTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfoTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfoTest.groovy
new file mode 100644
index 0000000..7248622
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyExtraInfoTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvalidUserDataException
+import spock.lang.Specification
+
+import javax.xml.namespace.QName
+
+class DefaultIvyExtraInfoTest extends Specification {
+    def "can get value by name only" () {
+        given:
+        def extraInfo = new DefaultIvyExtraInfo([ (new NamespaceId('http://my.extra.info', 'foo')): 'fooValue' ])
+
+        expect:
+        extraInfo.get('foo') == 'fooValue'
+        extraInfo.get('goo') == null
+    }
+
+    def "throws exception when name matches multiple keys" () {
+        given:
+        def extraInfo = new DefaultIvyExtraInfo([
+                (new NamespaceId('http://my.extra.info', 'foo')): 'fooValue',
+                (new NamespaceId('http://some.extra.info', 'foo')): 'anotherValue',
+                (new NamespaceId('http://my.extra.info', 'bar')): 'barValue'
+        ])
+
+        when:
+        extraInfo.get('foo')
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message.equals('Cannot get extra info element named \'foo\' by name since elements with this name were found from multiple namespaces (http://my.extra.info, http://some.extra.info).  Use get(String namespace, String name) instead.')
+    }
+
+    def "can get by name and namespace" () {
+        given:
+        def extraInfo = new DefaultIvyExtraInfo([
+                (new NamespaceId('http://my.extra.info', 'foo')): 'fooValue',
+                (new NamespaceId('http://some.extra.info', 'foo')): 'anotherValue'
+        ])
+
+        expect:
+        extraInfo.get('http://my.extra.info', 'foo') == 'fooValue'
+        extraInfo.get('http://my.extra.info', 'goo') == null
+        extraInfo.get('http://other', 'foo') == null
+    }
+
+    def "can get extra info values as map" () {
+        given:
+        def extraInfo = new DefaultIvyExtraInfo([
+                (new NamespaceId('http://my.extra.info', 'foo')): 'fooValue',
+                (new NamespaceId('http://some.extra.info', 'foo')): 'anotherValue',
+                (new NamespaceId('http://my.extra.info', 'bar')): 'barValue'
+        ])
+
+        expect:
+        extraInfo.asMap() == [
+                (new QName('http://my.extra.info', 'foo')): 'fooValue',
+                (new QName('http://some.extra.info', 'foo')): 'anotherValue',
+                (new QName('http://my.extra.info', 'bar')): 'barValue'
+        ]
+    }
+
+    def "map view is immutable" () {
+        given:
+        def extraInfo = new DefaultIvyExtraInfo([ (new NamespaceId('http://my.extra.info', 'foo')): 'fooValue' ])
+
+        when:
+        extraInfo.asMap().put(new QName('http://some.extra.info', 'bar'), 'barValue')
+
+        then:
+        thrown(UnsupportedOperationException)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptorTest.groovy
new file mode 100644
index 0000000..b3c4a3c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyModuleDescriptorTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 javax.xml.namespace.QName
+import spock.lang.Specification
+
+class DefaultIvyModuleDescriptorTest extends Specification {
+    def "getExtraInfo returns immutable map"() {
+        setup:
+        def map = [(new NamespaceId('http://my.extra.info', 'some')): 'value']
+        def ivyModuleDescriptor = new DefaultIvyModuleDescriptor(map, null, null)
+
+        when:
+        ivyModuleDescriptor.getExtraInfo().asMap().put(new QName('namespace', 'new'), 'value')
+
+        then:
+        thrown(UnsupportedOperationException)
+    }
+
+    def "getBranch returns branch" () {
+        given:
+        def ivyModuleDescriptor = new DefaultIvyModuleDescriptor([:], 'someBranch', null)
+
+        expect:
+        ivyModuleDescriptor.getBranch() == 'someBranch'
+    }
+
+    def "getIvyStatus returns status"() {
+        given:
+        def ivyModuleDescriptor = new DefaultIvyModuleDescriptor([:], null, 'release')
+
+        expect:
+        ivyModuleDescriptor.getIvyStatus() == 'release'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitutionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitutionTest.groovy
new file mode 100644
index 0000000..caac01a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultModuleDependencySubstitutionTest.groovy
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Project
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+class DefaultModuleDependencySubstitutionTest extends Specification {
+
+    def "can specify version to use"() {
+        when:
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target == newComponentSelector("org", "foo", "1.0")
+        !details.updated
+        !details.selectionReason
+
+        when:
+        details.useVersion("1.0") //the same version
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target == newComponentSelector("org", "foo", "1.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+
+        when:
+        details.useVersion("2.0") //different version
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target != newComponentSelector("org", "foo", "1.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+
+        details.target.version == "2.0"
+        details.target.module == newComponentSelector("org", "foo", "1.0").module
+        details.target.group == newComponentSelector("org", "foo", "1.0").group
+    }
+
+    def "can specify version with selection reason"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useVersion("1.0", VersionSelectionReasons.FORCED) //same version
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target == newComponentSelector("org", "foo", "1.0")
+        details.updated
+        details.selectionReason == VersionSelectionReasons.FORCED
+
+        when:
+        details.useVersion("3.0", VersionSelectionReasons.FORCED) //different version
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target.version == "3.0"
+        details.target.module == newComponentSelector("org", "foo", "1.0").module
+        details.target.group == newComponentSelector("org", "foo", "1.0").group
+        details.updated
+        details.selectionReason == VersionSelectionReasons.FORCED
+    }
+
+    def "can override version and selection reason"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useVersion("2.0", VersionSelectionReasons.FORCED)
+        details.useVersion("3.0", VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        details.requested == newComponentSelector("org", "foo", "1.0")
+        details.target.version == "3.0"
+        details.target.module == newComponentSelector("org", "foo", "1.0").module
+        details.target.group == newComponentSelector("org", "foo", "1.0").group
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "does not allow null target"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useTarget(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+
+        when:
+        details.useTarget(null, VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "does not allow null version"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useVersion(null)
+
+        then:
+        thrown(IllegalArgumentException)
+
+        when:
+        details.useVersion(null, VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+
+    def "can specify target module"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useTarget("org:bar:2.0")
+
+        then:
+        details.target instanceof ModuleComponentSelector
+        details.target.toString() == 'org:bar:2.0'
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "can specify target project"() {
+        def project = Mock(Project)
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useTarget(project)
+
+        then:
+        _ * project.path >> ":bar"
+        details.target instanceof ProjectComponentSelector
+        details.target.projectPath == ":bar"
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "can mix configuring version and target module"() {
+        def details = newModuleDependencySubstitution("org", "foo", "1.0")
+
+        when:
+        details.useVersion("1.5")
+
+        then:
+        details.target.toString() == 'org:foo:1.5'
+
+        when:
+        details.useTarget("com:bar:3.0")
+
+        then:
+        details.target.toString() == 'com:bar:3.0'
+
+        when:
+        details.useVersion('2.0')
+
+        then:
+        details.target.toString() == 'org:foo:2.0'
+    }
+
+    private static def newModuleDependencySubstitution(String group, String name, String version) {
+        return new DefaultModuleDependencySubstitution(newComponentSelector(group, name, version), DefaultModuleVersionSelector.newSelector(group, name, version))
+    }
+
+    private static ModuleComponentSelector newComponentSelector(String group, String module, String version) {
+        return DefaultModuleComponentSelector.newSelector(group, module, version)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitutionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitutionTest.groovy
new file mode 100644
index 0000000..84a5af5
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultProjectDependencySubstitutionTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Project
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+class DefaultProjectDependencySubstitutionTest extends Specification {
+
+    def "can override target and selection reason"() {
+        def details = newProjectDependencySubstitution("foo")
+
+        when:
+        details.useTarget("org:foo:2.0", VersionSelectionReasons.FORCED)
+        details.useTarget("org:foo:3.0", VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        details.requested == newComponentSelector(":foo")
+        details.target.version == "3.0"
+        details.target.module == newComponentSelector("org", "foo", "1.0").module
+        details.target.group == newComponentSelector("org", "foo", "1.0").group
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "does not allow null target"() {
+        def details = newProjectDependencySubstitution("foo")
+
+        when:
+        details.useTarget(null)
+
+        then:
+        thrown(UnsupportedNotationException)
+
+        when:
+        details.useTarget(null, VersionSelectionReasons.SELECTED_BY_RULE)
+
+        then:
+        thrown(UnsupportedNotationException)
+    }
+
+    def "can specify target module"() {
+        def details = newProjectDependencySubstitution("foo")
+
+        when:
+        details.useTarget("org:bar:2.0")
+
+        then:
+        details.target instanceof ModuleComponentSelector
+        details.target.toString() == 'org:bar:2.0'
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    def "can specify target project"() {
+        def project = Mock(Project)
+        def details = newProjectDependencySubstitution("foo")
+
+        when:
+        details.useTarget(project)
+
+        then:
+        _ * project.path >> ":bar"
+        details.target instanceof ProjectComponentSelector
+        details.target.projectPath == ":bar"
+        details.updated
+        details.selectionReason == VersionSelectionReasons.SELECTED_BY_RULE
+    }
+
+    private static def newProjectDependencySubstitution(String name) {
+        return new DefaultProjectDependencySubstitution(newComponentSelector(":" + name), DefaultModuleVersionSelector.newSelector("com.test", name, "1.0"))
+    }
+
+    private static ProjectComponentSelector newComponentSelector(String projectPath) {
+        return DefaultProjectComponentSelector.newSelector(projectPath)
+    }
+
+    private static ModuleComponentSelector newComponentSelector(String group, String module, String version) {
+        return DefaultModuleComponentSelector.newSelector(group, module, version)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
new file mode 100644
index 0000000..dc9c175
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultUnresolvedDependencySpec.groovy
@@ -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.api.internal.artifacts.ivyservice
+
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import spock.lang.Specification
+
+class DefaultUnresolvedDependencySpec extends Specification {
+
+    def "provides module details"() {
+        when:
+        def dep = new DefaultUnresolvedDependency(DefaultModuleVersionSelector.newSelector('org.foo', "foo", '1.0'), new RuntimeException("boo!"))
+
+        then:
+        dep.selector.group == 'org.foo'
+        dep.selector.name == 'foo'
+        dep.selector.version == '1.0'
+        dep.toString() == 'org.foo:foo:1.0'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolverSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolverSpec.groovy
new file mode 100644
index 0000000..236a0d3
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DependencySubstitutionResolverSpec.groovy
@@ -0,0 +1,84 @@
+/*
+ * 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.gradle.api.Action
+import org.gradle.api.artifacts.DependencySubstitution
+import org.gradle.api.artifacts.ModuleDependencySubstitution
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult
+import spock.lang.Specification
+
+class DependencySubstitutionResolverSpec extends Specification {
+    def requested = new DefaultModuleVersionSelector("group", "module", "version")
+    def selector = new DefaultModuleComponentSelector("group", "module", "version")
+    def dependency = Mock(DependencyMetaData) {
+        getRequested() >> requested
+        getSelector() >> selector
+    }
+    def result = Mock(BuildableComponentIdResolveResult)
+    def target = Mock(DependencyToComponentIdResolver)
+    def rule = Mock(Action)
+    def resolver = new DependencySubstitutionResolver(target, rule)
+
+    def "passes through dependency when it does not match any rule"() {
+        given:
+        rule.execute(_) >> { DependencySubstitution details ->
+        }
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * target.resolve(dependency, result)
+    }
+
+    def "replaces dependency by rule"() {
+        def substitutedDependency = Stub(DependencyMetaData)
+
+        given:
+        rule.execute(_) >> { ModuleDependencySubstitution details ->
+            details.useVersion("new")
+        }
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * dependency.withTarget(DefaultModuleComponentSelector.newSelector("group", "module", "new")) >> substitutedDependency
+        1 * target.resolve(substitutedDependency, result)
+    }
+
+    def "explosive rule yields failure result that provides context"() {
+        given:
+        def failure = new RuntimeException("broken")
+        rule.execute(_) >> { DependencySubstitution details ->
+            throw failure
+        }
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * result.failed(_) >> { ModuleVersionResolveException e ->
+            assert e.cause == failure
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
new file mode 100644
index 0000000..a26a83f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingArtifactDependencyResolverTest.groovy
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.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
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import org.gradle.api.specs.Specs
+import spock.lang.Specification
+
+import static org.junit.Assert.fail
+
+class ErrorHandlingArtifactDependencyResolverTest extends Specification {
+    private delegate = Mock(ArtifactDependencyResolver)
+    private resolvedConfiguration = Mock(ResolvedConfiguration)
+    private resolutionResult = Mock(ResolutionResult)
+    private projectConfigResult = Mock(ResolvedProjectConfigurationResults)
+    private configuration = Mock(ConfigurationInternal.class, name: 'coolConf')
+    private repositories = [Mock(ResolutionAwareRepository)]
+    private metadataHandler = Stub(GlobalDependencyResolutionRules)
+    private results = new ResolverResults()
+    private resolver = new ErrorHandlingArtifactDependencyResolver(delegate);
+
+    void "delegates to backing service"() {
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        1 * delegate.resolve(configuration, repositories, metadataHandler, results) >> {
+            results.resolved(resolvedConfiguration, resolutionResult, projectConfigResult)
+        }
+    }
+
+    void "wraps operations with the failure"() {
+        given:
+        def failure = new RuntimeException()
+        delegate.resolve(configuration, repositories, metadataHandler, results) >> { throw failure }
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        results.resolvedConfiguration.hasError()
+
+        failsWith(failure)
+            .when { results.resolvedConfiguration.rethrowFailure(); }
+            .when { results.resolvedConfiguration.getFiles(Specs.satisfyAll()); }
+            .when { results.resolvedConfiguration.getFirstLevelModuleDependencies(); }
+            .when { results.resolvedConfiguration.getResolvedArtifacts(); }
+    }
+
+    void "wraps exceptions thrown by resolved configuration"() {
+        given:
+        def failure = new RuntimeException()
+
+        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, metadataHandler, results) >> { results.resolved(resolvedConfiguration, resolutionResult, projectConfigResult) }
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def result = results.resolvedConfiguration
+        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, metadataHandler, results) >> { results.resolved(resolvedConfiguration, resolutionResult, projectConfigResult) }
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def result = results.resolvedConfiguration.lenientConfiguration
+        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, metadataHandler, results) >> { results.resolved(resolvedConfiguration, resolutionResult, projectConfigResult) }
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def result = results.resolutionResult
+        failsWith(failure)
+                .when { result.root }
+    }
+
+    ExceptionFixture failsWith(Throwable failure) {
+        new ExceptionFixture(failure: failure)
+    }
+
+    class ExceptionFixture {
+        Throwable failure
+        ExceptionFixture when(Closure cl) {
+            try {
+                cl();
+                fail();
+            } catch (ResolveException e) {
+                assert e.message == "Could not resolve all dependencies for Mock for type 'ConfigurationInternal' named 'coolConf'."
+                assert e.cause.is(failure);
+            }
+            this
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
new file mode 100644
index 0000000..478b93c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyUtilTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * 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.module.id.ArtifactId
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import spock.lang.Specification
+
+class IvyUtilTest extends Specification {
+    def "create module revision ID"() {
+        when:
+        List l = ['myorg', 'myname', 'myrev']
+        ModuleRevisionId moduleRevisionId = IvyUtil.createModuleRevisionId(*l)
+
+        then:
+        l[0] == moduleRevisionId.organisation
+        l[1] == moduleRevisionId.name
+        l[2] == moduleRevisionId.revision
+    }
+
+    def "create artifact ID"() {
+        when:
+        ArtifactId artifactId = IvyUtil.createArtifactId('org', 'module', 'test', 'jar', 'jar')
+
+        then:
+        artifactId.moduleId == IvyUtil.createModuleId('org', 'module')
+        artifactId.name == 'test'
+        artifactId.type == 'jar'
+        artifactId.ext == 'jar'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy
new file mode 100644
index 0000000..5ee416c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyXmlModuleDescriptorWriterTest.groovy
@@ -0,0 +1,146 @@
+/*
+ * 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.*
+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.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+import java.text.SimpleDateFormat
+
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleRevisionId
+
+class IvyXmlModuleDescriptorWriterTest extends Specification {
+
+    private @Rule TestNameTestDirectoryProvider temporaryFolder;
+    private ModuleDescriptor md = Mock();
+    private ModuleRevisionId moduleRevisionId = Mock()
+    private ModuleRevisionId resolvedModuleRevisionId = Mock()
+    def ivyXmlModuleDescriptorWriter = new IvyXmlModuleDescriptorWriter()
+
+    def "can create ivy (unmodified) descriptor"() {
+        setup:
+        assertMockMethodInvocations()
+        def dependency1 = mockDependencyDescriptor("Dep1")
+        def dependency2 = mockDependencyDescriptor("Dep2")
+        1 * dependency2.force >> true
+        1 * dependency2.changing >> true
+        1 * md.dependencies >> [dependency1, dependency2]
+        1 * dependency1.transitive >> true
+        when:
+        File ivyFile = temporaryFolder.file("test/ivy/ivy.xml")
+        ivyXmlModuleDescriptorWriter.write(md, ivyFile);
+        then:
+        def ivyModule = new XmlSlurper().parse(ivyFile);
+        assert ivyModule. at version == "2.0"
+        assert ivyModule.info. at organisation == "org.test"
+        assert ivyModule.info. at module == "projectA"
+        assert ivyModule.info. at revision == "1.0"
+        assert ivyModule.info. at status == "integration"
+        assert ivyModule.info. at publication == "20120817120000"
+        assert ivyModule.info. at buildNr == "815"
+        assert ivyModule.configurations.conf.collect {it. at name } == ["archives", "compile", "runtime"]
+        assert ivyModule.publications.artifact.collect {it. at name } == ["testartifact"]
+        assert ivyModule.publications.artifact.collect {it. at conf } == ["archives,runtime"]
+        assert ivyModule.dependencies.dependency.collect { "${it. at org}:${it. at name}:${it. at rev}" } == ["org.test:Dep1:1.0", "org.test:Dep2:1.0"]
+    }
+
+    private void assertMockMethodInvocations() {
+        1 * md.extraAttributesNamespaces >> [:]
+        1 * md.extraAttributes >> [buildNr: "815"]
+        1 * md.qualifiedExtraAttributes >> [buildNr: "815"]
+        1 * md.getExtraInfo() >> Collections.emptyMap()
+        1 * md.getLicenses() >> Collections.emptyList()
+        1 * md.inheritedDescriptors >> Collections.emptyList()
+        1 * md.resolvedModuleRevisionId >> resolvedModuleRevisionId
+        1 * md.resolvedPublicationDate >> date("20120817120000")
+        1 * md.status >> "integration"
+        1 * resolvedModuleRevisionId.revision >> "1.0"
+        1 * md.moduleRevisionId >> moduleRevisionId;
+        1 * moduleRevisionId.organisation >> "org.test"
+        1 * moduleRevisionId.name >> "projectA"
+        1 * md.configurations >> [mockConfiguration("archives"), mockConfiguration("compile"), mockConfiguration("runtime", ["compile"])]
+        1 * md.allExcludeRules >> []
+        1 * md.allArtifacts >> [mockArtifact()]
+        0 * md.allDependencyDescriptorMediators
+    }
+
+    def "does not evaluate dependency descriptor mediators"() {
+        given:
+        ModuleRevisionId moduleRevisionId = createModuleRevisionId('org.test', 'projectA', '2.0')
+        ModuleDescriptor moduleDescriptor = DefaultModuleDescriptor.newDefaultInstance(moduleRevisionId)
+        ModuleId moduleId = createModuleId('org.test', 'projectA')
+        DependencyDescriptorMediator mediator = DefaultModuleDescriptor.newDefaultInstance(moduleRevisionId)
+        moduleDescriptor.addDependencyDescriptorMediator(moduleId, new ExactPatternMatcher(), mediator)
+        assert moduleDescriptor.allDependencyDescriptorMediators.allRules.size() == 1
+
+        when:
+        File ivyFile = temporaryFolder.file("test/ivy/ivy.xml")
+        ivyXmlModuleDescriptorWriter.write(moduleDescriptor, ivyFile)
+
+        then:
+        def ivyModule = new XmlSlurper().parse(ivyFile)
+        notThrown(UnsupportedOperationException)
+        assert ivyModule.info. at organisation == 'org.test'
+        assert ivyModule.info. at module == 'projectA'
+        assert ivyModule.info. at revision == '2.0'
+    }
+
+    def date(String timestamp) {
+        def format = new SimpleDateFormat("yyyyMMddHHmmss")
+        format.parse(timestamp)
+    }
+
+    def mockDependencyDescriptor(String organisation = "org.test", String moduleName, String revision = "1.0") {
+        DependencyDescriptor dependencyDescriptor = Mock()
+        ModuleRevisionId moduleRevisionId = Mock()
+        1 * moduleRevisionId.organisation >> organisation
+        1 * moduleRevisionId.name >> moduleName
+        1 * moduleRevisionId.revision >> revision
+        1 * dependencyDescriptor.dependencyRevisionId >> moduleRevisionId
+        1 * dependencyDescriptor.dynamicConstraintDependencyRevisionId >> moduleRevisionId
+        1 * dependencyDescriptor.moduleConfigurations >> ["default"]
+        1 * dependencyDescriptor.getDependencyConfigurations("default") >> ["compile, archives"]
+        1 * dependencyDescriptor.allDependencyArtifacts >> []
+        1 * dependencyDescriptor.allIncludeRules >> []
+        1 * dependencyDescriptor.allExcludeRules >> []
+        dependencyDescriptor
+    }
+
+    def mockArtifact() {
+        Artifact artifact = Mock()
+        1 * artifact.name >> "testartifact"
+        1 * artifact.ext >> "jar"
+        1 * artifact.type >> "jar"
+        1 * artifact.configurations >> ["archives", "runtime"]
+        artifact
+    }
+
+    def mockConfiguration(String configurationName, List extended = []) {
+        Configuration configuration = Mock()
+        1 * configuration.name >> configurationName
+        1 * configuration.description >> "just another test configuration"
+        1 * configuration.extends >> extended
+        1 * configuration.visibility >> Configuration.Visibility.PUBLIC
+        configuration
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/NamespaceIdTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/NamespaceIdTest.groovy
new file mode 100644
index 0000000..9723a5c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/NamespaceIdTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.Matchers
+import spock.lang.Specification
+
+class NamespaceIdTest extends Specification {
+    def "can set namespace and name" () {
+        given:
+        NamespaceId id = new NamespaceId("some-namespace", "some-name")
+
+        expect:
+        id.getNamespace() == "some-namespace"
+        id.getName() == "some-name"
+    }
+
+    def "hashCode and equals determine equality" () {
+        given:
+        NamespaceId id1 = new NamespaceId("some-namespace", "some-name")
+        NamespaceId id2 = new NamespaceId("some-namespace", "some-name")
+
+        expect:
+        id1 Matchers.strictlyEqual(id2)
+    }
+
+    def "hashCode and equals determine inequality" () {
+        given:
+        NamespaceId id1 = new NamespaceId(namespace1, name1)
+        NamespaceId id2 = new NamespaceId(namespace2, name2)
+
+        expect:
+        ! id1.equals(id2)
+        id1.hashCode() != id2.hashCode()
+
+        where:
+        namespace1        | name1        | namespace2        | name2
+        "some-namespace1" | "some-name"  | "some-namespace2" | "some-name"
+        "some-namespace"  | "some-name1" | "some-namespace"  | "some-name2"
+        "some-namespace1" | "some-name"  | "some-namespace"  | "some-name2"
+    }
+
+    def "toString returns name" () {
+        given:
+        NamespaceId id = new NamespaceId("some-namespace", "some-name")
+
+        expect:
+        id.toString().equals("some-name")
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy
new file mode 100644
index 0000000..8abfc6a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/SelfResolvingDependencyResolverTest.groovy
@@ -0,0 +1,136 @@
+/*
+ * 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.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.ResolvedConfiguration
+import org.gradle.api.artifacts.result.ResolutionResult
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
+import org.gradle.api.internal.artifacts.CachingDependencyResolveContext
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResults
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import org.gradle.api.specs.Spec
+import org.gradle.api.specs.Specs
+import spock.lang.Specification
+
+public class SelfResolvingDependencyResolverTest extends Specification {
+
+    private delegate = Mock(ArtifactDependencyResolver)
+    private resolvedConfiguration = Mock(ResolvedConfiguration)
+    private configuration = Mock(ConfigurationInternal)
+    private repositories = [Mock(ResolutionAwareRepository)]
+    private dependencies = Mock(DependencySet)
+    private metadataHandler = Stub(GlobalDependencyResolutionRules)
+    private results = new ResolverResults()
+    private resolver = new SelfResolvingDependencyResolver(delegate);
+
+    void "returns correct resolved configuration"() {
+        given:
+        delegate.resolve(configuration, repositories, metadataHandler, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult), Mock(ResolvedProjectConfigurationResults)) }
+        configuration.getAllDependencies() >> dependencies
+        configuration.isTransitive() >> true
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def conf = (SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration) results.resolvedConfiguration
+        conf.resolvedConfiguration == resolvedConfiguration
+        conf.selfResolvingFilesProvider
+        conf.selfResolvingFilesProvider.resolveContext.transitive
+        conf.selfResolvingFilesProvider.dependencies == dependencies
+    }
+
+    void "uses configuration transitive setting"() {
+        given:
+        delegate.resolve(configuration, repositories, metadataHandler, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult), Mock(ResolvedProjectConfigurationResults)) }
+        configuration.getAllDependencies() >> dependencies
+        configuration.isTransitive() >> false
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def conf = (SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration) results.resolvedConfiguration
+        !conf.selfResolvingFilesProvider.resolveContext.transitive
+    }
+
+    void "delegates to provided resolved configuration"() {
+        given:
+        delegate.resolve(configuration, repositories, metadataHandler, results) >> { results.resolved(resolvedConfiguration, Mock(ResolutionResult), Mock(ResolvedProjectConfigurationResults)) }
+        configuration.getAllDependencies() >> dependencies
+        configuration.isTransitive() >> true
+
+        when:
+        resolver.resolve(configuration, repositories, metadataHandler, results)
+        results.resolvedConfiguration.getFirstLevelModuleDependencies(Specs.satisfyAll())
+        results.resolvedConfiguration.getResolvedArtifacts()
+        results.resolvedConfiguration.hasError()
+        results.resolvedConfiguration.rethrowFailure()
+        results.resolvedConfiguration.getLenientConfiguration()
+
+        then:
+        1 * resolvedConfiguration.getFirstLevelModuleDependencies(Specs.satisfyAll())
+        1 * resolvedConfiguration.getResolvedArtifacts()
+        1 * resolvedConfiguration.hasError()
+        1 * resolvedConfiguration.rethrowFailure()
+        1 * resolvedConfiguration.getLenientConfiguration()
+    }
+
+    void "knows how to extract self resolving files"() {
+        given:
+        def resolvedFiles = Mock(FileCollection)
+        def resolveContext = Mock(CachingDependencyResolveContext)
+        def fooDep = new DefaultExternalModuleDependency("org", "foo", "1.0")
+        Set<Dependency> dependencies = [fooDep, new DefaultExternalModuleDependency("org", "bar", "1.0")]
+
+        def provider = new SelfResolvingDependencyResolver.SelfResolvingFilesProvider(resolveContext, dependencies)
+
+        when:
+        def files = provider.getFiles({ it.name == 'foo' } as Spec)
+
+        then:
+        1 * resolveContext.add(fooDep)
+        1 * resolveContext.resolve() >> resolvedFiles
+        1 * resolvedFiles.getFiles() >> [new File('foo.jar')]
+        0 * _._
+
+        files*.name == ['foo.jar']
+    }
+
+    void "aggregates files with self resolving files first"() {
+        given:
+        def provider = Mock(SelfResolvingDependencyResolver.SelfResolvingFilesProvider) {
+            getFiles(Specs.satisfyAll()) >> [new File("foo.jar")]
+        }
+        resolvedConfiguration.getFiles(Specs.satisfyAll()) >> new HashSet<File>([new File("bar.jar")])
+
+        def conf = new SelfResolvingDependencyResolver.FilesAggregatingResolvedConfiguration(resolvedConfiguration, provider)
+
+        when:
+        def files = conf.getFiles(Specs.satisfyAll())
+
+        then:
+        files*.name == ['foo.jar', 'bar.jar']
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy
new file mode 100644
index 0000000..3a1833f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortcircuitEmptyConfigsArtifactDependencyResolverSpec.groovy
@@ -0,0 +1,77 @@
+/*
+ * 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.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.internal.artifacts.ArtifactDependencyResolver
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import org.gradle.api.specs.Specs
+import spock.lang.Specification
+
+class ShortcircuitEmptyConfigsArtifactDependencyResolverSpec extends Specification {
+
+    def delegate = Mock(ArtifactDependencyResolver)
+    def configuration = Stub(ConfigurationInternal)
+    def repositories = [Stub(ResolutionAwareRepository)]
+    def metadataHandler = Stub(GlobalDependencyResolutionRules)
+    def dependencies = Stub(DependencySet)
+    def componentIdentifierFactory = Mock(ComponentIdentifierFactory)
+    def results = new ResolverResults()
+    def dependencyResolver = new ShortcircuitEmptyConfigsArtifactDependencyResolver(delegate, componentIdentifierFactory);
+
+    def "returns empty result when no dependencies"() {
+        given:
+        dependencies.isEmpty() >> true
+        configuration.getAllDependencies() >> dependencies
+
+        when:
+        dependencyResolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        def resolvedConfig = results.resolvedConfiguration
+        !resolvedConfig.hasError()
+        resolvedConfig.rethrowFailure();
+
+        resolvedConfig.getFiles(Specs.<Dependency> satisfyAll()).isEmpty()
+        resolvedConfig.getFirstLevelModuleDependencies().isEmpty()
+        resolvedConfig.getResolvedArtifacts().isEmpty()
+
+        and:
+        def result = results.resolutionResult
+        result.allComponents.size() == 1
+        result.allDependencies.empty
+
+        and:
+        0 * delegate._
+    }
+
+    def "delegates to backing service when there are one or more dependencies"() {
+        given:
+        dependencies.isEmpty() >> false
+        configuration.getAllDependencies() >> dependencies
+
+        when:
+        dependencyResolver.resolve(configuration, repositories, metadataHandler, results)
+
+        then:
+        1 * delegate.resolve(configuration, repositories, metadataHandler, results)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
new file mode 100644
index 0000000..515373f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolverTest.groovy
@@ -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.api.internal.artifacts.ivyservice.clientmodule
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor
+import org.gradle.api.artifacts.ClientModule
+import org.gradle.api.artifacts.ModuleDependency
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class ClientModuleResolverTest extends Specification {
+    final target = Mock(ComponentMetaDataResolver)
+    final dependencyDescriptorFactory = Mock(DependencyDescriptorFactory)
+    final ClientModuleResolver resolver = new ClientModuleResolver(target, dependencyDescriptorFactory)
+
+    def id = Mock(ComponentIdentifier)
+    def result = Mock(BuildableComponentResolveResult)
+    def metaData = Mock(MutableModuleComponentResolveMetaData)
+    def dependency = Mock(DslOriginDependencyMetaData)
+
+    def "replaces meta-data for a client module dependency"() {
+        def clientModule = Mock(ClientModule)
+        def dep = Mock(ModuleDependency)
+        def moduleDescriptor = Mock(ModuleDescriptor)
+        def dependencyMetaData = Mock(DependencyMetaData)
+        def artifact = Mock(ModuleComponentArtifactMetaData)
+
+        when:
+        resolver.resolve(dependency, id, result)
+
+        then:
+        1 * target.resolve(dependency, id, result)
+        1 * result.getFailure() >> null
+        1 * dependency.source >> clientModule
+        1 * result.getMetaData() >> metaData
+        1 * metaData.copy() >> metaData
+        1 * clientModule.getDependencies() >> ([dep] as Set)
+        1 * dep.getConfiguration() >> "config"
+        1 * metaData.getDescriptor() >> moduleDescriptor
+        1 * dependencyDescriptorFactory.createDependencyDescriptor("config", moduleDescriptor, dep) >> dependencyMetaData
+        1 * metaData.setDependencies([dependencyMetaData])
+        1 * metaData.artifact('jar', 'jar', null) >> artifact
+        1 * metaData.setArtifacts({
+            (it as List) == [artifact]
+        })
+        1 * result.setMetaData(metaData)
+        0 * _
+    }
+
+    def "does not replace meta-data when not client module"() {
+        def moduleDependency = Mock(ModuleDependency)
+
+        when:
+        resolver.resolve(dependency, id, result)
+
+        then:
+        1 * target.resolve(dependency, id, result)
+        1 * result.getFailure() >> null
+        1 * dependency.source >> moduleDependency
+        0 * _
+    }
+
+    def "does not replace meta-data for broken module version"() {
+        when:
+        resolver.resolve(dependency, id, result)
+
+        then:
+        1 * target.resolve(dependency, id, result)
+        _ * result.failure >> new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+        0 * _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryTest.groovy
new file mode 100644
index 0000000..a52607b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryTest.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.api.internal.artifacts.ivyservice.ivyresolve
+
+import spock.lang.Specification
+
+class BaseModuleComponentRepositoryTest extends Specification {
+    final delegate = Mock(ModuleComponentRepository)
+    final localAccess = Mock(ModuleComponentRepositoryAccess)
+    final remoteAccess = Mock(ModuleComponentRepositoryAccess)
+
+    def "delegates id and name methods"() {
+        when:
+        final repository = new BaseModuleComponentRepository(delegate, localAccess, remoteAccess)
+        1 * delegate.id >> "id"
+        1 * delegate.name >> "name"
+
+        then:
+        repository.id == "id"
+        repository.name == "name"
+    }
+
+    def "delegates access methods"() {
+        when:
+        final repository = new BaseModuleComponentRepository(delegate)
+
+        then:
+        repository.localAccess == localAccess
+        repository.remoteAccess == remoteAccess
+
+        and:
+        1 * delegate.localAccess >> localAccess
+        1 * delegate.remoteAccess >> remoteAccess
+    }
+
+    def "returns supplied local and remote access"() {
+        when:
+        final repository = new BaseModuleComponentRepository(delegate, localAccess, remoteAccess)
+
+        then:
+        repository.localAccess == localAccess
+        repository.remoteAccess == remoteAccess
+
+        and:
+        0 * delegate._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy
new file mode 100644
index 0000000..87e2d11
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * 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.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor
+import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache
+import org.gradle.api.internal.component.ArtifactType
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.component.model.*
+import org.gradle.internal.resolve.result.*
+import org.gradle.internal.resource.cached.CachedArtifactIndex
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryKey
+import org.gradle.util.BuildCommencedTimeProvider
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class CachingModuleComponentRepositoryTest extends Specification {
+    def realLocalAccess = Mock(ModuleComponentRepositoryAccess)
+    def realRemoteAccess = Mock(ModuleComponentRepositoryAccess)
+    def realRepo = Stub(ModuleComponentRepository) {
+        getId() >> "repo-id"
+        getLocalAccess() >> realLocalAccess
+        getRemoteAccess() >> realRemoteAccess
+    }
+    def moduleResolutionCache = Stub(ModuleVersionsCache)
+    def moduleDescriptorCache = Mock(ModuleMetaDataCache)
+    def moduleArtifactsCache = Mock(ModuleArtifactsCache)
+    def artifactAtRepositoryCache = Mock(CachedArtifactIndex)
+    def cachePolicy = Stub(CachePolicy)
+    def metadataProcessor = Stub(ComponentMetadataProcessor)
+    def repo = new CachingModuleComponentRepository(realRepo, moduleResolutionCache, moduleDescriptorCache, moduleArtifactsCache, artifactAtRepositoryCache,
+            cachePolicy, new BuildCommencedTimeProvider(), metadataProcessor)
+
+    @Unroll
+    def "artifact last modified date is cached - lastModified = #lastModified"() {
+        given:
+        def artifactId = Stub(ModuleComponentArtifactIdentifier)
+        def artifact = Stub(ModuleComponentArtifactMetaData) {
+            getId() >> artifactId
+        }
+
+        def file = new File("local")
+        def result = Stub(BuildableArtifactResolveResult) {
+            getFile() >> file
+            getFailure() >> null
+        }
+
+        def descriptorHash = 1234G
+        def moduleSource = Stub(CachingModuleComponentRepository.CachingModuleSource) {
+            getDescriptorHash() >> descriptorHash
+        }
+
+        ArtifactAtRepositoryKey atRepositoryKey = new ArtifactAtRepositoryKey("repo-id", artifactId)
+
+        when:
+        repo.remoteAccess.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * artifactAtRepositoryCache.store(atRepositoryKey, file, descriptorHash)
+        0 * moduleDescriptorCache._
+
+        where:
+        lastModified << [new Date(), null]
+    }
+
+    def "does not use cache when module version listing can be determined locally"() {
+        def dependency = Mock(DependencyMetaData)
+        def result = new DefaultBuildableModuleVersionListingResolveResult()
+
+        when:
+        repo.localAccess.listModuleVersions(dependency, result)
+
+        then:
+        realLocalAccess.listModuleVersions(dependency, result) >> {
+            result.listed(['a', 'b', 'c'])
+        }
+        0 * _
+    }
+
+    def "does not use cache when component metadata can be determined locally"() {
+        def dependency = Mock(DependencyMetaData)
+        def componentId = Mock(ModuleComponentIdentifier)
+        def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+
+        when:
+        repo.localAccess.resolveComponentMetaData(dependency, componentId, result)
+
+        then:
+        realLocalAccess.resolveComponentMetaData(dependency, componentId, result) >> {
+            result.resolved(Mock(MutableModuleComponentResolveMetaData))
+        }
+        0 * _
+    }
+
+    def "does not use cache when artifacts for type can be determined locally"() {
+        def component = Mock(ComponentResolveMetaData)
+        def source = Mock(ModuleSource)
+        def cachingSource = new CachingModuleComponentRepository.CachingModuleSource(BigInteger.ONE, false, source)
+        def artifactType = ArtifactType.JAVADOC
+        def result = new DefaultBuildableArtifactSetResolveResult()
+
+        when:
+        repo.localAccess.resolveModuleArtifacts(component, artifactType, result)
+
+        then:
+        1 * component.getSource() >> cachingSource
+        1 * component.withSource(source) >> component
+        realLocalAccess.resolveModuleArtifacts(component, artifactType, result) >> {
+            result.resolved([Mock(ComponentArtifactMetaData)])
+        }
+        0 * _
+    }
+
+    def "does not use cache when artifacts for usage can be determined locally"() {
+        def component = Mock(ComponentResolveMetaData)
+        def source = Mock(ModuleSource)
+        def cachingSource = new CachingModuleComponentRepository.CachingModuleSource(BigInteger.ONE, false, source)
+        def componentUsage = Mock(ComponentUsage)
+        def result = new DefaultBuildableArtifactSetResolveResult()
+
+        when:
+        repo.localAccess.resolveModuleArtifacts(component, componentUsage, result)
+
+        then:
+        1 * component.getSource() >> cachingSource
+        1 * component.withSource(source) >> component
+        realLocalAccess.resolveModuleArtifacts(component, componentUsage, result) >> {
+            result.resolved([Mock(ComponentArtifactMetaData)])
+        }
+        0 * _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessorTest.groovy
new file mode 100644
index 0000000..0930ae9
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentSelectionRulesProcessorTest.groovy
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.InvalidUserCodeException
+import org.gradle.api.artifacts.ComponentMetadata
+import org.gradle.api.artifacts.ComponentSelection
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor
+import org.gradle.internal.rules.ClosureBackedRuleAction
+import org.gradle.internal.rules.SpecRuleAction
+import org.gradle.api.internal.artifacts.ComponentSelectionInternal
+import org.gradle.api.internal.artifacts.DefaultComponentSelection
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultComponentSelectionRules
+import org.gradle.api.specs.Specs
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import spock.lang.Specification
+
+class ComponentSelectionRulesProcessorTest extends Specification {
+    def processor = new ComponentSelectionRulesProcessor()
+    def rules = []
+    ComponentSelectionInternal componentSelection
+
+    def setup() {
+        def componentIdentifier = DefaultModuleComponentIdentifier.newId("group", "module", "version")
+        componentSelection = new DefaultComponentSelection(componentIdentifier)
+    }
+
+    def "all non-rejecting rules are evaluated" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> true
+        }
+        def closureCalled = []
+        when:
+        rule { ComponentSelection cs -> closureCalled << 0 }
+        rule { ComponentSelection cs, ComponentMetadata cm -> closureCalled << 1 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd -> closureCalled << 2 }
+        rule { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closureCalled << 3 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closureCalled << 4 }
+        rule { ComponentSelection cs, ComponentMetadata cm, IvyModuleDescriptor imd -> closureCalled << 5 }
+        rule { ComponentSelection cs, ComponentMetadata cm, ModuleComponentResolveMetaData mvm -> closureCalled << 6 }
+        rule { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closureCalled << 7 }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        // rules without metadata should be executed first
+        closureCalled[0] == 0
+        // metadata rules get called second in indeterminate order
+        closureCalled[1..-1].sort() == 1..7
+    }
+
+    def "all non-rejecting targeted rules are evaluated" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> true
+        }
+        def closureCalled = []
+        when:
+        targetedRule("group", "module") { ComponentSelection cs -> closureCalled << 0 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm -> closureCalled << 1 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd -> closureCalled << 2 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closureCalled << 3 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closureCalled << 4 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, IvyModuleDescriptor imd -> closureCalled << 5 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, ModuleComponentResolveMetaData mvm -> closureCalled << 6 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closureCalled << 7 }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        // rules without metadata should be executed first
+        closureCalled[0] == 0
+        // metadata rules get called second in in-determinate order
+        closureCalled[1..-1].sort() == 1..7
+    }
+
+    def "can call both targeted and untargeted rules" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> true
+        }
+        def closureCalled = []
+        when:
+        rule { ComponentSelection cs -> closureCalled << 0 }
+        rule { ComponentSelection cs, ComponentMetadata cm -> closureCalled << 1 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd -> closureCalled << 2 }
+        rule { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closureCalled << 3 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closureCalled << 4 }
+        rule { ComponentSelection cs, ComponentMetadata cm, IvyModuleDescriptor imd -> closureCalled << 5 }
+        rule { ComponentSelection cs, ComponentMetadata cm, ModuleComponentResolveMetaData mvm -> closureCalled << 6 }
+        rule { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closureCalled << 7 }
+        targetedRule("group", "module") { ComponentSelection cs -> closureCalled << 8 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm -> closureCalled << 9 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd -> closureCalled << 10 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closureCalled << 11 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closureCalled << 12 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, IvyModuleDescriptor imd -> closureCalled << 13 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, ModuleComponentResolveMetaData mvm -> closureCalled << 14 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closureCalled << 15 }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        // rules without metadata should be executed first in in-determinate order
+        closureCalled[0..1].sort() == [0, 8]
+        // metadata rules get called second in in-determinate order
+        closureCalled[2..-1].sort() == [*1..7, *9..15]
+    }
+
+    def "short-circuit prefers non-metadata rules over rules requiring metadata"() {
+        def metadataProvider = Mock(MetadataProvider)
+        def closuresCalled = []
+
+        when:
+        rule { ComponentSelection cs -> closuresCalled << 0 }
+        rule { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 1 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd -> closuresCalled << 2 }
+        rule { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closuresCalled << 3 }
+        rule { ComponentSelection cs -> closuresCalled << 4 }
+        rule { ComponentSelection cs ->
+            closuresCalled << 5
+            cs.reject("rejected")
+        }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        componentSelection.rejected
+        componentSelection.rejectionReason == "rejected"
+
+        and:
+        // None of the metadata rules get fired because a non-metadata rule rejected first
+        closuresCalled.intersect(1..3) == []
+        closuresCalled.contains(5)
+
+        and:
+        0 * metadataProvider._
+    }
+
+    def "short-circuit prefers non-metadata rules over rules requiring metadata for targeted rules"() {
+        def metadataProvider = Mock(MetadataProvider)
+        def closuresCalled = []
+
+        when:
+        targetedRule("group", "module") { ComponentSelection cs -> closuresCalled << 0 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 1 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd -> closuresCalled << 2 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closuresCalled << 3 }
+        targetedRule("group", "module") { ComponentSelection cs -> closuresCalled << 4 }
+        targetedRule("group", "module") { ComponentSelection cs ->
+            closuresCalled << 5
+            cs.reject("rejected")
+        }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        componentSelection.rejected
+        componentSelection.rejectionReason == "rejected"
+
+        and:
+        // None of the metadata rules get fired because a non-metadata rule rejected first
+        closuresCalled.intersect(1..3) == []
+        closuresCalled.contains(5)
+
+        and:
+        0 * metadataProvider._
+    }
+
+    def "metadata is not requested for rules that don't require it"() {
+        def metadataProvider = Mock(MetadataProvider)
+        def closuresCalled = []
+
+        when:
+        rule { ComponentSelection cs -> closuresCalled << 0 }
+        targetedRule("group", "module") { ComponentSelection cs -> closuresCalled << 1 }
+
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        closuresCalled.sort() == [0, 1]
+
+        and:
+        0 * metadataProvider._
+    }
+
+    def "metadata is not requested for non-targeted components"() {
+        def metadataProvider = Mock(MetadataProvider)
+        def closuresCalled = []
+
+        when:
+        targetedRule("group", "module1") { ComponentSelection cs, IvyModuleDescriptor ivm -> closuresCalled << 0 }
+        targetedRule("group1", "module") { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 1 }
+        targetedRule("group1", "module") { ComponentSelection cs, IvyModuleDescriptor ivm, ComponentMetadata cm -> closuresCalled << 2 }
+
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        closuresCalled == []
+
+        and:
+        0 * metadataProvider._
+    }
+
+    def "produces sensible error when rule action throws exception" () {
+        def metadataProvider = Mock(MetadataProvider)
+        def failure = new Exception("From test")
+
+        when:
+        rule { ComponentSelection selection -> throw failure }
+        apply(metadataProvider)
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "There was an error while evaluating a component selection rule for group:module:version."
+        e.cause == failure
+    }
+
+    def "rule expecting IvyMetadataDescriptor does not get called when not an ivy component" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> true
+            getIvyModuleDescriptor() >> null
+        }
+        def closuresCalled = []
+
+        when:
+        rule { ComponentSelection cs, IvyModuleDescriptor ivm, ComponentMetadata cm -> closuresCalled << 0 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor ivm, ComponentMetadata cm -> closuresCalled << 1 }
+        apply(metadataProvider)
+
+        then:
+        closuresCalled == []
+    }
+
+    def "only matching targeted rules get called" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> true
+        }
+        def closuresCalled = []
+        when:
+        targetedRule("group", "module") { ComponentSelection cs -> closuresCalled << 0 }
+        targetedRule("group1", "module") { ComponentSelection cs -> closuresCalled << 1 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 2 }
+        targetedRule("group", "module1") { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 3 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd -> closuresCalled << 4 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm -> closuresCalled << 5 }
+        targetedRule("group", "module") { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closuresCalled << 6 }
+        targetedRule("group1", "module") { ComponentSelection cs, IvyModuleDescriptor imd, ComponentMetadata cm -> closuresCalled << 7 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, IvyModuleDescriptor imd -> closuresCalled << 8 }
+        targetedRule("group", "module") { ComponentSelection cs, ComponentMetadata cm, ModuleComponentResolveMetaData mvm -> closuresCalled << 9 }
+        targetedRule("group", "module") { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closuresCalled << 10 }
+        targetedRule("group", "module1") { ComponentSelection cs, ModuleComponentResolveMetaData mvm, ComponentMetadata cm -> closuresCalled << 11 }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        closuresCalled.sort() == [0, 2, 4, 5, 6, 8, 9, 10]
+    }
+
+    def "does not invoke rules that require meta-data when it cannot be resolved" () {
+        def metadataProvider = Stub(MetadataProvider) {
+            resolve() >> false
+        }
+        def closuresCalled = []
+        when:
+        rule { ComponentSelection cs -> closuresCalled << 0 }
+        rule { ComponentSelection cs, ComponentMetadata cm -> closuresCalled << 1 }
+        rule { ComponentSelection cs, IvyModuleDescriptor imd -> closuresCalled << 2 }
+
+        and:
+        apply(metadataProvider)
+
+        then:
+        !componentSelection.rejected
+        closuresCalled == [0]
+    }
+
+    def rule(Closure<?> closure) {
+        rules << new SpecRuleAction<ComponentSelection>(
+                new ClosureBackedRuleAction<ComponentSelection>(ComponentSelection, closure),
+                Specs.<ComponentSelection>satisfyAll()
+        )
+    }
+
+    def targetedRule(String group, String module, Closure<?> closure) {
+        rules << new SpecRuleAction<ComponentSelection>(
+                new ClosureBackedRuleAction<ComponentSelection>(ComponentSelection, closure),
+                new DefaultComponentSelectionRules.ComponentSelectionMatchingSpec(DefaultModuleIdentifier.newId(group, module))
+        )
+    }
+
+    def apply(def metadataProvider) {
+        processor.apply(componentSelection, rules, metadataProvider)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy
new file mode 100644
index 0000000..41b1e19
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.artifacts.ComponentSelection
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme
+import org.gradle.api.specs.Specs
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.result.DefaultBuildableComponentSelectionResult
+import org.gradle.internal.resolve.result.DefaultBuildableModuleComponentMetaDataResolveResult
+import org.gradle.internal.rules.ClosureBackedRuleAction
+import org.gradle.internal.rules.SpecRuleAction
+import spock.lang.Specification
+
+import static org.gradle.internal.resolve.result.BuildableComponentSelectionResult.State.Failed
+import static org.gradle.internal.resolve.result.BuildableComponentSelectionResult.State.NoMatch
+
+class DefaultVersionedComponentChooserTest extends Specification {
+    def versionSelectorScheme = new DefaultVersionSelectorScheme(new DefaultVersionComparator())
+    def versionComparator = new DefaultVersionComparator()
+    def componentSelectionRules = Mock(ComponentSelectionRulesInternal)
+
+    def chooser = new DefaultVersionedComponentChooser(versionComparator, versionSelectorScheme, componentSelectionRules)
+
+    def "chooses latest version for component meta data"() {
+        def one = Stub(ComponentResolveMetaData) {
+            getId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
+        }
+        def two = Stub(ComponentResolveMetaData) {
+            getId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.1")
+        }
+        def three = Stub(ComponentResolveMetaData) {
+            getId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.2")
+        }
+
+        when:
+        0 * componentSelectionRules.apply(_,_)
+
+        then:
+        chooser.selectNewestComponent(one, two) == two
+
+        when:
+        0 * componentSelectionRules.apply(_,_)
+
+        then:
+        chooser.selectNewestComponent(two, three) == three
+    }
+
+    def "chooses non-generated descriptor over generated"() {
+        def one = Mock(ComponentResolveMetaData) {
+            getId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
+        }
+        def two = Mock(ComponentResolveMetaData) {
+            getId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
+        }
+
+        when:
+        1 * one.generated >> true
+        1 * two.generated >> false
+        0 * componentSelectionRules.apply(_,_)
+
+        then:
+        chooser.selectNewestComponent(one, two) == two
+
+        when:
+        1 * one.generated >> false
+        0 * componentSelectionRules.apply(_,_)
+
+        then:
+        chooser.selectNewestComponent(one, two) == one
+    }
+
+    def "chooses newest matching version without requiring metadata"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "1.+")
+        def selected = DefaultModuleComponentIdentifier.newId("group", "name", "1.3")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.requested >> selector
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * b.id >> selected
+        _ * c.version >> "2.0"
+        _ * componentSelectionRules.rules >> []
+        0 * _
+
+        and:
+        selectedComponentResult.match == selected
+    }
+
+    def "chooses newest matching version requiring metadata"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "latest.milestone")
+        def selected = DefaultModuleComponentIdentifier.newId("group", "name", "1.3")
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def dependency = Mock(DependencyMetaData)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.requested >> selector
+        _ * dependency.withRequestedVersion(_) >> Stub(DependencyMetaData)
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * b.id >> selected
+        _ * c.version >> "2.0"
+        1 * c.resolve() >> resolvedWithStatus("integration")
+        1 * b.resolve() >> resolvedWithStatus("milestone")
+        _ * componentSelectionRules.rules >> []
+        0 * _
+
+        and:
+        selectedComponentResult.match == selected
+    }
+
+    def "rejects dynamic version by rule without metadata" () {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "1.+")
+        def selected = DefaultModuleComponentIdentifier.newId("group", "name", "1.3")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.getRequested() >> selector
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * b.id >> selected
+        _ * c.version >> "2.0"
+        _ * componentSelectionRules.rules >> rules({ComponentSelection selection ->
+            if (selection.candidate.version != '1.3') {
+                selection.reject("rejected")
+            }
+        })
+        0 * _
+
+        then:
+        selectedComponentResult.match == selected
+    }
+
+    def "rejects dynamic version by rule with metadata" () {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "latest.release")
+        def selected = DefaultModuleComponentIdentifier.newId("group", "name", "1.3")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.requested >> selector
+        _ * dependency.withRequestedVersion(_) >> Stub(DependencyMetaData)
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * b.id >> selected
+        _ * c.version >> "2.0"
+        1 * c.resolve() >> resolvedWithStatus("milestone")
+        1 * b.resolve() >> resolvedWithStatus("release")
+        1 * componentSelectionRules.rules >> rules({ComponentSelection selection ->
+            if (selection.candidate.version == '1.3') {
+                selection.reject("rejected")
+            }
+        })
+        0 * _
+
+        then:
+        // Since 1.3 is "latest.release" but it's rejected by rule, we should fail to resolve
+        selectedComponentResult.state == NoMatch
+    }
+
+    def "returns no match when no versions match without metadata"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "1.1")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, b, a], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.requested >> selector
+        _ * componentSelectionRules.rules >> []
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * c.version >> "2.0"
+        0 * _
+
+        and:
+        selectedComponentResult.state == NoMatch
+    }
+
+    def "returns no match when no versions are chosen with metadata"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "latest.release")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.getRequested() >> selector
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * c.version >> "2.0"
+        1 * a.resolve() >> resolvedWithStatus("integration")
+        1 * b.resolve() >> resolvedWithStatus("integration")
+        1 * c.resolve() >> resolvedWithStatus("integration")
+        _ * componentSelectionRules.rules >> []
+        0 * _
+
+        then:
+        selectedComponentResult.state == NoMatch
+    }
+
+    def "returns no match when all matching versions match are rejected by rule"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "+")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.getRequested() >> selector
+        _ * a.version >> "1.2"
+        _ * a.id >> Stub(ModuleComponentIdentifier)
+        _ * b.version >> "1.3"
+        _ * b.id >> Stub(ModuleComponentIdentifier)
+        _ * c.version >> "2.0"
+        _ * c.id >> Stub(ModuleComponentIdentifier)
+        _ * componentSelectionRules.rules >> rules({ ComponentSelection selection ->
+            selection.reject("Rejecting everything")
+        })
+        0 * _
+
+        then:
+        selectedComponentResult.state == NoMatch
+    }
+
+    def "stops when candidate cannot be resolved"() {
+        given:
+        def selector = new DefaultModuleVersionSelector("group", "name", "latest.release")
+        def dependency = Mock(DependencyMetaData)
+        def a = Mock(ModuleComponentResolveState)
+        def b = Mock(ModuleComponentResolveState)
+        def c = Mock(ModuleComponentResolveState)
+        def selectedComponentResult = new DefaultBuildableComponentSelectionResult()
+
+        when:
+        chooser.selectNewestMatchingComponent([c, a, b], dependency, selectedComponentResult)
+
+        then:
+        _ * dependency.getRequested() >> selector
+        _ * a.version >> "1.2"
+        _ * b.version >> "1.3"
+        _ * c.version >> "2.0"
+        1 * c.resolve() >> resolvedWithStatus("integration")
+        1 * b.resolve() >> resolvedWithFailure()
+        _ * componentSelectionRules.rules >> []
+        0 * _
+
+        then:
+        selectedComponentResult.state == Failed
+    }
+
+    def resolvedWithStatus(String status) {
+        def meta = Stub(MutableModuleComponentResolveMetaData) {
+            getStatusScheme() >> ["integration", "milestone", "release"]
+            getStatus() >> status
+        }
+        def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+        result.resolved(meta)
+        return result
+    }
+
+    def resolvedWithFailure() {
+        def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+        result.failed(Stub(ModuleVersionResolveException))
+        return result
+    }
+
+    def rules(Closure closure) {
+        return [
+            new SpecRuleAction<ComponentSelection>(
+                    new ClosureBackedRuleAction<ComponentSelection>(ComponentSelection, closure),
+                    Specs.<ComponentSelection>satisfyAll()
+            )
+        ]
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
new file mode 100644
index 0000000..6f8b389
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.ivyresolve
+
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver
+import spock.lang.Specification
+
+public class DependencyResolverIdentifierTest extends Specification {
+    def "dependency resolvers of type ExternalResourceResolver are differentiated by their patterns"() {
+        given:
+        ExternalResourceResolver resolver1 = Mock()
+        ExternalResourceResolver resolver1a = Mock()
+        ExternalResourceResolver resolver2 = Mock()
+        ExternalResourceResolver resolver2a = Mock()
+
+        resolver1.ivyPatterns >> ['ivy1', 'ivy2']
+        resolver1.artifactPatterns >> ['artifact1', 'artifact2']
+        resolver1a.ivyPatterns >> ['ivy1', 'ivy2']
+        resolver1a.artifactPatterns >> ['artifact1', 'artifact2']
+        resolver2.ivyPatterns >> ['ivy1', 'different']
+        resolver2.artifactPatterns >> ['artifact1', 'artifact2']
+        resolver2a.ivyPatterns >> ['ivy1', 'ivy2']
+        resolver2a.artifactPatterns >> ['artifact1', 'different']
+
+        expect:
+        id(resolver1) == id(resolver1a)
+        id(resolver1) != id(resolver2)
+        id(resolver1) != id(resolver2a)
+        id(resolver2) != id(resolver2a)
+    }
+
+    def "dependency resolvers of type ExternalResourceResolver are differentiated by m2compatible flag"() {
+        given:
+        ExternalResourceResolver resolver1 = Mock()
+        ExternalResourceResolver resolver2 = Mock()
+
+        resolver1.ivyPatterns >> ['ivy1']
+        resolver1.artifactPatterns >> ['artifact1']
+        resolver2.ivyPatterns >> ['ivy1']
+        resolver2.artifactPatterns >> ['artifact1']
+        resolver2.m2compatible >> true
+
+        expect:
+        id(resolver1) != id(resolver2)
+    }
+
+    def id(ExternalResourceResolver resolver) {
+        return DependencyResolverIdentifier.forExternalResourceResolver(resolver)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
new file mode 100644
index 0000000..f6bb45f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.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.internal.artifacts.ivyservice.ivyresolve
+
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.internal.component.model.ComponentArtifactIdentifier
+import org.gradle.internal.component.model.ComponentArtifactMetaData
+import org.gradle.internal.component.model.ComponentUsage
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import org.gradle.api.internal.component.ArtifactType
+import org.gradle.internal.component.model.ModuleSource
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.internal.resolve.resolver.ArtifactResolver
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult
+import spock.lang.Specification
+
+class ErrorHandlingArtifactResolverTest extends Specification {
+    def delegate = Mock(ArtifactResolver)
+    def artifactResolver = new ErrorHandlingArtifactResolver(delegate)
+
+    def "wraps resolveArtifact exception as failure"() {
+        def componentArtifactId = Stub(ComponentArtifactIdentifier) {
+            getDisplayName() >> "<component-artifact>"
+        }
+        def componentArtifact = Stub(ComponentArtifactMetaData) {
+            getId() >> componentArtifactId
+        }
+        def moduleSource = Mock(ModuleSource)
+        def artifactResolveResult = Mock(BuildableArtifactResolveResult)
+        def failure = new RuntimeException("foo")
+
+        when:
+        delegate.resolveArtifact(componentArtifact, moduleSource, artifactResolveResult) >> { throw failure }
+
+        and:
+        artifactResolver.resolveArtifact(componentArtifact, moduleSource, artifactResolveResult)
+
+        then:
+        1 * artifactResolveResult.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
+            assert e.message == "Could not download <component-artifact>"
+            assert e.cause == failure
+        }
+    }
+
+    def "wraps resolveModuleArtifacts exception as failure"() {
+        def componentId = Stub(ComponentIdentifier) {
+            getDisplayName() >> "<component>"
+        }
+        def component = Stub(ComponentResolveMetaData) {
+            getComponentId() >> componentId
+        }
+        def result = Mock(BuildableArtifactSetResolveResult)
+        def failure = new RuntimeException("foo")
+
+        when:
+        def artifactType = ArtifactType.JAVADOC
+        delegate.resolveModuleArtifacts(component, artifactType, result) >> { throw failure }
+
+        and:
+        artifactResolver.resolveModuleArtifacts(component, artifactType, result)
+
+        then:
+        1 * result.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
+            assert e.message == "Could not determine artifacts for <component>"
+            assert e.cause == failure
+        }
+        0 * _._
+
+        when:
+        def componentUsage = Mock(ComponentUsage)
+        delegate.resolveModuleArtifacts(component, componentUsage, result) >> { throw failure }
+
+        and:
+        artifactResolver.resolveModuleArtifacts(component, componentUsage, result)
+
+        then:
+        1 * result.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
+            assert e.message == "Could not determine artifacts for <component>"
+            assert e.cause == failure
+        }
+        0 * _._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccessTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccessTest.groovy
new file mode 100644
index 0000000..b544cc6
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepositoryAccessTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult
+import spock.lang.Specification
+
+class IvyDynamicResolveModuleComponentRepositoryAccessTest extends Specification {
+    final target = Mock(ModuleComponentRepositoryAccess)
+    final metaData = Mock(MutableModuleComponentResolveMetaData)
+    final requestedDependency = Mock(DependencyMetaData)
+    final moduleComponentId = Mock(ModuleComponentIdentifier)
+    final result = Mock(BuildableModuleComponentMetaDataResolveResult)
+    final ModuleComponentRepositoryAccess access = new IvyDynamicResolveModuleComponentRepositoryAccess(target)
+
+    def "replaces each dependency version with revConstraint"() {
+        def original = dependency('1.2+')
+        def transformed = dependency()
+
+        given:
+        result.state >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+        result.metaData >> metaData
+
+        when:
+        access.resolveComponentMetaData(requestedDependency, moduleComponentId, result)
+
+        then:
+        1 * target.resolveComponentMetaData(requestedDependency, moduleComponentId, result)
+
+        and:
+        1 * metaData.dependencies >> [original]
+        1 * original.withRequestedVersion('1.2+') >> transformed
+        1 * metaData.setDependencies([transformed])
+    }
+
+    def "does nothing when dependency has not been resolved"() {
+        when:
+        access.resolveComponentMetaData(requestedDependency, moduleComponentId, result)
+
+        then:
+        1 * target.resolveComponentMetaData(requestedDependency, moduleComponentId, result)
+        _ * result.state >> BuildableModuleComponentMetaDataResolveResult.State.Missing
+        0 * result._
+    }
+
+    def dependency(String revConstraint = '1.0') {
+        def dep = Mock(DependencyMetaData)
+        def descriptor = Mock(DependencyDescriptor)
+        _ * descriptor.dynamicConstraintDependencyRevisionId >> IvyUtil.createModuleRevisionId('org', 'module', revConstraint)
+        _ * dep.descriptor >> descriptor
+        return dep
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProviderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProviderTest.groovy
new file mode 100644
index 0000000..ff832bf
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/MetadataProviderTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.artifacts.component.ModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultIvyModuleResolveMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.result.DefaultBuildableModuleComponentMetaDataResolveResult
+import spock.lang.Specification
+
+class MetadataProviderTest extends Specification {
+    def dep = Stub(DependencyMetaData)
+    def id = Stub(ModuleComponentIdentifier) {
+        getVersion() >> "1.2"
+    }
+    def metaData = Stub(MutableModuleComponentResolveMetaData)
+    def resolveState = Mock(ModuleComponentResolveState)
+    def metadataProvider = new MetadataProvider(resolveState)
+
+    def "caches metadata result"() {
+        when:
+        metadataProvider.getMetaData()
+        metadataProvider.getMetaData()
+
+        then:
+        1 * resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.resolved(metaData)
+            return result
+        }
+        0 * resolveState.resolve()
+    }
+
+    def "verifies that metadata was provided"() {
+        given:
+        resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.resolved(metaData)
+            return result
+        }
+
+        expect:
+        metadataProvider.resolve()
+        metadataProvider.usable
+        metadataProvider.metaData
+    }
+
+    def "verifies that metadata was not provided"() {
+        given:
+        resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.missing()
+            return result
+        }
+
+        expect:
+        !metadataProvider.resolve()
+        !metadataProvider.usable
+    }
+
+    def "can provide component metadata" () {
+        given:
+        resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.resolved(metaData)
+            return result
+        }
+
+        when:
+        def componentMetadata = metadataProvider.getComponentMetadata()
+
+        then:
+        componentMetadata.metadata == metaData
+    }
+
+    def "can provide Ivy descriptor" () {
+        given:
+        def metaData = new DefaultIvyModuleResolveMetaData(Stub(ModuleDescriptor) {
+            getStatus() >> "test"
+        })
+        resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.resolved(metaData)
+            return result
+        }
+
+        when:
+        def returned = metadataProvider.getIvyModuleDescriptor()
+
+        then:
+        returned.ivyStatus == "test"
+    }
+
+    def "returns null when not Ivy descriptor" () {
+        given:
+        resolveState.resolve() >> {
+            def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
+            result.resolved(metaData)
+            return result
+        }
+
+        expect:
+        metadataProvider.getIvyModuleDescriptor() == null
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapterTest.groovy
new file mode 100644
index 0000000..7cb5383
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainAdapterTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult
+import spock.lang.Specification
+
+class RepositoryChainAdapterTest extends Specification {
+    def metaDataResolver = Mock(DependencyToComponentResolver)
+    def dynamicVersionResolver = Mock(DependencyToComponentIdResolver)
+    def idResult = Mock(BuildableComponentIdResolveResult)
+    def versionSelectorScheme = Stub(VersionSelectorScheme)
+    def requested = new DefaultModuleVersionSelector("group", "module", "version")
+    def id = new DefaultModuleComponentIdentifier("group", "module", "version")
+    def mvId = new DefaultModuleVersionIdentifier("group", "module", "version")
+    def dependency = Stub(DependencyMetaData) {
+        getRequested() >> requested
+    }
+    def resolver = new RepositoryChainAdapter(dynamicVersionResolver, metaDataResolver, versionSelectorScheme)
+
+    def "short-circuits static version resolution"() {
+        given:
+        versionSelectorScheme.parseSelector("version") >> {
+            Stub(VersionSelector) {
+                isDynamic() >> false
+            }
+        }
+
+        when:
+        resolver.resolve(dependency, idResult)
+
+        then:
+        1 * idResult.resolved(id, mvId)
+    }
+
+    def "resolves dynamic version"() {
+        given:
+        versionSelectorScheme.parseSelector("version") >> {
+            Stub(VersionSelector) {
+                isDynamic() >> true
+            }
+        }
+
+        when:
+        resolver.resolve(dependency, idResult)
+
+        then:
+        1 * dynamicVersionResolver.resolve(dependency, idResult)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy
new file mode 100644
index 0000000..08af31e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy
@@ -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.artifacts.ivyservice.ivyresolve
+
+import org.gradle.internal.component.model.ComponentUsage
+import org.gradle.internal.component.model.ModuleSource
+import org.gradle.internal.resolve.result.DefaultBuildableArtifactResolveResult
+import org.gradle.internal.resolve.result.DefaultBuildableArtifactSetResolveResult
+import org.gradle.internal.component.model.ComponentArtifactMetaData
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import spock.lang.Specification
+
+class RepositoryChainArtifactResolverTest extends Specification {
+    final artifact = Mock(ComponentArtifactMetaData)
+    final component = Mock(ComponentResolveMetaData)
+    final originalSource = Mock(ModuleSource)
+    final result = new DefaultBuildableArtifactResolveResult()
+    final artifactSetResult = new DefaultBuildableArtifactSetResolveResult()
+
+    def repo1 = Stub(ModuleComponentRepository) {
+        getId() >> "repo1"
+    }
+    def localAccess2 = Mock(ModuleComponentRepositoryAccess)
+    def remoteAccess2 = Mock(ModuleComponentRepositoryAccess)
+    def repo2 = Mock(ModuleComponentRepository) {
+        getLocalAccess() >> localAccess2
+        getRemoteAccess() >> remoteAccess2
+        getId() >> "repo2"
+    }
+    def repo2Source = new RepositoryChainModuleSource("repo2", originalSource)
+
+    final RepositoryChainArtifactResolver resolver = new RepositoryChainArtifactResolver()
+
+    def setup() {
+        resolver.add(repo1)
+        resolver.add(repo2)
+    }
+
+    def "uses module artifacts from local access to repository defined by module source"() {
+        def usage = Mock(ComponentUsage)
+        def artifact = Mock(ComponentArtifactMetaData)
+        when:
+        resolver.resolveModuleArtifacts(component, usage, artifactSetResult)
+
+        then:
+        _ * component.getSource() >> repo2Source
+        1 * component.withSource(originalSource) >> component
+        1 * repo2.getLocalAccess() >> localAccess2
+        1 * localAccess2.resolveModuleArtifacts(component, usage, artifactSetResult) >> {
+            it[2].resolved([artifact])
+        }
+        0 * _._
+
+        and:
+        artifactSetResult.artifacts == [artifact] as Set
+    }
+
+    def "uses module artifacts from remote access to repository defined by module source"() {
+        def usage = Mock(ComponentUsage)
+        def artifact = Mock(ComponentArtifactMetaData)
+        when:
+        resolver.resolveModuleArtifacts(component, usage, artifactSetResult)
+
+        then:
+        _ * component.getSource() >> repo2Source
+        1 * component.withSource(originalSource) >> component
+        1 * repo2.getLocalAccess() >> localAccess2
+        1 * localAccess2.resolveModuleArtifacts(component, usage, artifactSetResult)
+        1 * repo2.getRemoteAccess() >> remoteAccess2
+        1 * remoteAccess2.resolveModuleArtifacts(component, usage, artifactSetResult) >> {
+            it[2].resolved([artifact])
+        }
+        0 * _._
+
+        and:
+        artifactSetResult.artifacts == [artifact] as Set
+    }
+
+    def "locates artifact with local access in repository defined by module source"() {
+        def artifactFile = Mock(File)
+        def artifact = Mock(ComponentArtifactMetaData)
+        when:
+        resolver.resolveArtifact(artifact, repo2Source, result)
+
+        then:
+        1 * repo2.getLocalAccess() >> localAccess2
+        1 * localAccess2.resolveArtifact(artifact, originalSource, result) >> {
+            it[2].resolved(artifactFile)
+        }
+        0 * _._
+
+        and:
+        result.file == artifactFile
+    }
+
+    def "locates artifact with remote access in repository defined by module source"() {
+        def artifactFile = Mock(File)
+        def artifact = Mock(ComponentArtifactMetaData)
+        when:
+        resolver.resolveArtifact(artifact, repo2Source, result)
+
+        then:
+        1 * repo2.getLocalAccess() >> localAccess2
+        1 * localAccess2.resolveArtifact(artifact, originalSource, result)
+        1 * repo2.getRemoteAccess() >> remoteAccess2
+        1 * remoteAccess2.resolveArtifact(artifact, originalSource, result) >> {
+            it[2].resolved(artifactFile)
+        }
+        0 * _._
+
+        and:
+        result.file == artifactFile
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy
new file mode 100644
index 0000000..43cb9b9
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyResolverTest.groovy
@@ -0,0 +1,582 @@
+/*
+ * 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.gradle.api.Transformer
+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.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult
+import spock.lang.Specification
+
+class RepositoryChainDependencyResolverTest extends Specification {
+    final metaData = metaData("1.2")
+    final moduleComponentId = DefaultModuleComponentIdentifier.newId("group", "project", "1.0")
+    final dependency = Stub(DependencyMetaData)
+    final selector = DefaultModuleVersionSelector.newSelector("group", "project", "1.0")
+    final moduleVersionId = DefaultModuleVersionIdentifier.newId("group", "project", "1.0")
+    final dependencyDescriptor = Stub(DependencyDescriptor)
+
+    final Transformer<ModuleComponentResolveMetaData, RepositoryChainModuleResolution> transformer = Mock(Transformer)
+    final result = Mock(BuildableComponentResolveResult)
+    def localAccess = Mock(ModuleComponentRepositoryAccess)
+    def remoteAccess = Mock(ModuleComponentRepositoryAccess)
+    def localAccess2 = Mock(ModuleComponentRepositoryAccess)
+    def remoteAccess2 = Mock(ModuleComponentRepositoryAccess)
+
+    final VersionedComponentChooser componentSelectionStrategy = Mock(VersionedComponentChooser)
+    final RepositoryChainDependencyResolver resolver = new RepositoryChainDependencyResolver(componentSelectionStrategy, transformer)
+
+    ModuleVersionIdentifier moduleVersionIdentifier(ModuleDescriptor moduleDescriptor) {
+        def moduleRevId = moduleDescriptor.moduleRevisionId
+        new DefaultModuleVersionIdentifier(moduleRevId.organisation, moduleRevId.name, moduleRevId.revision)
+    }
+
+    def setup() {
+        _ * dependency.requested >> selector
+        _ * dependency.descriptor >> dependencyDescriptor
+    }
+
+    def addRepo1() {
+        addModuleComponentRepository("repo1", localAccess, remoteAccess)
+    }
+
+    def addRepo2() {
+        addModuleComponentRepository("repo2", localAccess2, remoteAccess2)
+    }
+
+    def addModuleComponentRepository(def name, def repoLocalAccess, def repoRemoteAccess) {
+        def repo = Stub(ModuleComponentRepository) {
+            getLocalAccess() >> repoLocalAccess
+            getRemoteAccess() >> repoRemoteAccess
+            getName() >> name
+        }
+        resolver.add(repo)
+        repo
+    }
+
+    def "uses local dependency when available"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is unknown"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is probably missing"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "fails with not found when local static dependency is marked as missing"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.attempted("scheme:thing")
+            result.missing()
+        }
+        1 * result.attempted("scheme:thing")
+        1 * result.notFound(moduleVersionId)
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "fails with not found when local and remote static dependency marked as missing"() {
+        given:
+        addRepo1()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * result.notFound(moduleVersionId)
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "stops on first available local dependency for static version"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = Mock(ModuleComponentRepository)
+        resolver.add(repo2)
+        def repo3 = Mock(ModuleComponentRepository)
+        resolver.add(repo3)
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo1
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * repo2._
+        0 * repo3._
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "uses local dependency when available in one repository and missing from all other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "uses local dependency when available in one repository and probably missing in all other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "uses remote dependency when local dependency is unknown for a given repository and probably missing in other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is probably missing in all repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "does not attempt to resolve remote dependency when local dependency is missing"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is missing or unknown in all repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo1
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "ignores failure to resolve local dependency when available in another repository"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.failed(new ModuleVersionResolveException(id, "broken"))
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "ignores failure to resolve remote dependency when available in another repository"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.failed(new ModuleVersionResolveException(id, "broken"))
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.resolved(metaData)
+        }
+        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
+            assert it.module == metaData
+            assert it.repository == repo2
+            metaData
+        }
+        1 * result.resolved(_) >> { ModuleComponentResolveMetaData metaData ->
+            assert metaData == this.metaData
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "rethrows failure to resolve local dependency when not available in any repository"() {
+        given:
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), "broken")
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.failed(failure)
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * result.failed({ it.cause == failure })
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "rethrows failure to resolve remote dependency when not available in any repository"() {
+        given:
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), "broken")
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(dependency, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.failed(failure)
+        }
+        1 * localAccess2.resolveComponentMetaData(dependency, moduleComponentId, _)
+        1 * remoteAccess2.resolveComponentMetaData(dependency, moduleComponentId, _) >> { dep, id, result ->
+            result.missing()
+        }
+        1 * result.failed({ it.cause == failure })
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def descriptor(String version) {
+        def descriptor = Stub(ModuleDescriptor)
+        descriptor.resolvedModuleRevisionId >> IvyUtil.createModuleRevisionId("org", "module", version)
+        return descriptor
+    }
+
+    def metaData(String version) {
+        return Stub(MutableModuleComponentResolveMetaData) {
+            toString() >> version
+            getId() >> DefaultModuleVersionIdentifier.newId("org", "module", version)
+            getDescriptor() >> descriptor(version)
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy
new file mode 100644
index 0000000..80dd9f4
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.collect.Lists
+import org.gradle.api.internal.artifacts.ComponentMetadataProcessor
+import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.api.internal.artifacts.ivyservice.dynamicversions.ModuleVersionsCache
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.memcache.InMemoryCachedRepositoryFactory
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleArtifactsCache
+import org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetaDataCache
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver
+import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import org.gradle.internal.resource.cached.CachedArtifactIndex
+import org.gradle.internal.resource.local.FileStore
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.gradle.util.BuildCommencedTimeProvider
+import spock.lang.Specification
+
+class ResolveIvyFactoryTest extends Specification {
+    ResolveIvyFactory resolveIvyFactory
+    ModuleVersionsCache moduleVersionsCache
+    ModuleMetaDataCache moduleMetaDataCache
+    ModuleArtifactsCache moduleArtifactsCache
+    CachedArtifactIndex cachedArtifactIndex
+    CacheLockingManager cacheLockingManager
+    StartParameterResolutionOverride startParameterResolutionOverride
+    BuildCommencedTimeProvider buildCommencedTimeProvider
+    InMemoryCachedRepositoryFactory inMemoryCachedRepositoryFactory
+    VersionSelectorScheme versionSelectorScheme
+    VersionComparator versionComparator
+
+    def setup() {
+        moduleVersionsCache = Mock(ModuleVersionsCache)
+        moduleMetaDataCache = Mock(ModuleMetaDataCache)
+        moduleArtifactsCache = Mock(ModuleArtifactsCache)
+        cachedArtifactIndex = Mock(CachedArtifactIndex)
+        cacheLockingManager = Mock(CacheLockingManager)
+        startParameterResolutionOverride = Mock(StartParameterResolutionOverride) {
+            _ * overrideModuleVersionRepository(_) >> { ModuleComponentRepository repository -> repository }
+        }
+        buildCommencedTimeProvider = Mock(BuildCommencedTimeProvider)
+        inMemoryCachedRepositoryFactory = Mock(InMemoryCachedRepositoryFactory) {
+            _ * cached(_) >> { ModuleComponentRepository repository -> repository }
+        }
+        versionSelectorScheme = Mock(VersionSelectorScheme)
+        versionComparator = Mock(VersionComparator)
+
+        resolveIvyFactory = new ResolveIvyFactory(moduleVersionsCache, moduleMetaDataCache, moduleArtifactsCache,
+              cachedArtifactIndex, cacheLockingManager, startParameterResolutionOverride, buildCommencedTimeProvider,
+              inMemoryCachedRepositoryFactory, versionSelectorScheme, versionComparator)
+    }
+
+    def "returns an empty resolver when no repositories are configured" () {
+        when:
+        def resolver = resolveIvyFactory.create(Stub(ConfigurationInternal), Collections.emptyList(), Stub(ComponentMetadataProcessor))
+
+        then:
+        resolver instanceof NoRepositoriesResolver
+    }
+
+    def "sets parent resolver with different selection rules when repository is external" () {
+        def componentSelectionRules = Stub(ComponentSelectionRulesInternal)
+
+        def configuration = Stub(ConfigurationInternal) {
+            getResolutionStrategy() >> Stub(ResolutionStrategyInternal) {
+                getComponentSelection() >> componentSelectionRules
+            }
+        }
+
+        def spyResolver = externalResourceResolverSpy()
+        def repositories = Lists.newArrayList(Stub(ResolutionAwareRepository) {
+            createResolver() >> spyResolver
+        })
+
+        when:
+        def resolver = resolveIvyFactory.create(configuration, repositories, Stub(ComponentMetadataProcessor))
+
+        then:
+        assert resolver instanceof UserResolverChain
+        resolver.componentSelectionRules == componentSelectionRules
+
+        1 * spyResolver.setRepositoryChain(_) >> { RepositoryChain parentResolver ->
+            assert parentResolver instanceof ResolveIvyFactory.ParentModuleLookupResolver
+            // Validate that the parent repository chain selection rules are different and empty
+            def parentComponentSelectionRules = parentResolver.delegate.componentSelectionRules
+            assert parentComponentSelectionRules != componentSelectionRules
+            assert parentComponentSelectionRules.rules.empty
+
+        }
+    }
+
+    def externalResourceResolverSpy() {
+        ExternalResourceRepository externalResourceRepository = Stub()
+        CacheAwareExternalResourceAccessor cacheAwareExternalResourceAccessor = Stub()
+        VersionLister versionLister = Stub()
+        LocallyAvailableResourceFinder<ModuleComponentArtifactMetaData> locallyAvailableResourceFinder = Stub()
+        FileStore<ModuleComponentArtifactMetaData> fileStore = Stub()
+
+        return Spy(ExternalResourceResolver,
+            constructorArgs: [
+                    "Spy Resolver",
+                    false,
+                    externalResourceRepository,
+                    cacheAwareExternalResourceAccessor,
+                    versionLister,
+                    locallyAvailableResourceFinder,
+                    fileStore
+            ]
+        ) {
+            getLocalAccess() >> Stub(ModuleComponentRepositoryAccess)
+            getRemoteAccess() >> Stub(ModuleComponentRepositoryAccess)
+            isM2compatible() >> true
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy
new file mode 100644
index 0000000..baf3073
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/CachedModuleVersionResultTest.groovy
@@ -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.api.internal.artifacts.ivyservice.ivyresolve.memcache
+
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult
+import spock.lang.Specification
+
+class CachedModuleVersionResultTest extends Specification {
+
+    def "knows if result is cachable"() {
+        def resolved = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+            getMetaData() >> Stub(MutableModuleComponentResolveMetaData)
+        }
+        def missing = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Missing
+        }
+        def failed = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Failed
+        }
+        def unknown = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Unknown
+        }
+
+        expect:
+        new CachedModuleVersionResult(resolved).cacheable
+        new CachedModuleVersionResult(missing).cacheable
+        !new CachedModuleVersionResult(failed).cacheable
+        !new CachedModuleVersionResult(unknown).cacheable
+    }
+
+    def "interrogates result only when resolved"() {
+        def resolved = Mock(BuildableModuleComponentMetaDataResolveResult)
+        def missing = Mock(BuildableModuleComponentMetaDataResolveResult)
+
+        when:
+        new CachedModuleVersionResult(missing)
+
+        then:
+        1 * missing.state >> BuildableModuleComponentMetaDataResolveResult.State.Missing
+        1 * missing.authoritative >> true
+        0 * missing._
+
+        when:
+        new CachedModuleVersionResult(resolved)
+
+        then:
+        1 * resolved.state >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+        1 * resolved.metaData >> Stub(MutableModuleComponentResolveMetaData)
+    }
+
+    def "supplies cached data"() {
+        def suppliedMetaData = Mock(MutableModuleComponentResolveMetaData)
+        def cachedMetaData = Mock(MutableModuleComponentResolveMetaData)
+        def metaData = Mock(MutableModuleComponentResolveMetaData)
+        def resolved = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+            getMetaData() >> metaData
+        }
+        def missing = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Missing
+            isAuthoritative() >> true
+        }
+        def probablyMissing = Mock(BuildableModuleComponentMetaDataResolveResult) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Missing
+            isAuthoritative() >> false
+        }
+
+        def result = Mock(BuildableModuleComponentMetaDataResolveResult)
+
+        when:
+        def cached = new CachedModuleVersionResult(resolved)
+
+        then:
+        1 * metaData.copy() >> cachedMetaData
+
+        when:
+        cached.supply(result)
+
+        then:
+        1 * cachedMetaData.copy() >> suppliedMetaData
+        1 * result.resolved(suppliedMetaData)
+        1 * result.setAuthoritative(false)
+        0 * result._
+
+
+        when:
+        new CachedModuleVersionResult(missing).supply(result)
+
+        then:
+        1 * result.missing()
+        1 * result.setAuthoritative(true)
+        0 * result._
+
+        when:
+        new CachedModuleVersionResult(probablyMissing).supply(result)
+
+        then:
+        1 * result.missing()
+        1 * result.setAuthoritative(false)
+        0 * result._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCacheTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCacheTest.groovy
new file mode 100644
index 0000000..cecf492
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryArtifactsCacheTest.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.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult
+import org.gradle.internal.resolve.result.DefaultBuildableArtifactResolveResult
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
+import spock.lang.Specification
+
+class InMemoryArtifactsCacheTest extends Specification {
+
+    def stats = new InMemoryCacheStats()
+    def cache = new InMemoryArtifactsCache(stats)
+
+    static componentId(String group, String module, String version) {
+        return DefaultModuleComponentIdentifier.newId(group, module, version)
+    }
+
+    def "caches and supplies artifacts"() {
+        def artifactId = Stub(ModuleComponentArtifactIdentifier)
+        def artifactFile = new File("foo")
+
+        given:
+        def originalResult = new DefaultBuildableArtifactResolveResult()
+        originalResult.resolved(artifactFile)
+        cache.newArtifact(artifactId, originalResult)
+
+        def differentIdResult = Mock(BuildableArtifactResolveResult)
+        def sameIdResult = Mock(BuildableArtifactResolveResult)
+
+        when:
+        def differentId = Stub(ModuleComponentArtifactIdentifier)
+        def differentIdFound = cache.supplyArtifact(differentId, differentIdResult)
+
+        then:
+        !differentIdFound
+        0 * differentIdResult._
+
+        when:
+        def sameIdFound = cache.supplyArtifact(artifactId, sameIdResult)
+
+        then:
+        sameIdFound
+        1 * sameIdResult.resolved(artifactFile)
+    }
+
+    def "does not cache failed artifact resolves"() {
+        def artifactId = Stub(ModuleComponentArtifactIdentifier)
+        def failedResult = Stub(BuildableArtifactResolveResult) { getFailure() >> new ArtifactResolveException("bad") }
+        cache.newArtifact(artifactId, failedResult)
+
+        def result = Mock(BuildableArtifactResolveResult)
+
+        when:
+        def fromCache = cache.supplyArtifact(artifactId, result)
+
+        then:
+        !fromCache
+        0 * result._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepositoryTest.groovy
new file mode 100644
index 0000000..f7d0d5e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedModuleComponentRepositoryTest.groovy
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.component.ModuleComponentIdentifier
+import org.gradle.api.internal.component.ArtifactType
+import org.gradle.internal.component.model.ModuleSource
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult
+import org.gradle.internal.component.model.ComponentUsage
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.*
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class InMemoryCachedModuleComponentRepositoryTest extends Specification {
+
+    def stats = new InMemoryCacheStats()
+    def localArtifactsCache = Mock(InMemoryArtifactsCache)
+    def remoteArtifactsCache = Mock(InMemoryArtifactsCache)
+    def localMetaDataCache = Mock(InMemoryMetaDataCache)
+    def remoteMetaDataCache = Mock(InMemoryMetaDataCache)
+    def caches = new InMemoryModuleComponentRepositoryCaches(localArtifactsCache, remoteArtifactsCache, localMetaDataCache, remoteMetaDataCache, stats);
+    def localDelegate = Mock(ModuleComponentRepositoryAccess)
+    def remoteDelegate = Mock(ModuleComponentRepositoryAccess)
+    def delegate = Mock(ModuleComponentRepository) {
+        getLocalAccess() >> localDelegate
+        getRemoteAccess() >> remoteDelegate
+    }
+    def repo = new InMemoryCachedModuleComponentRepository(caches, delegate)
+    def lib = Mock(ModuleComponentIdentifier)
+    def selector = newSelector("org", "lib", "1.0")
+    def dep = Stub(DependencyMetaData) { getRequested() >> selector }
+
+    def listingResult = Mock(BuildableModuleVersionListingResolveResult)
+    def metaDataResult = Mock(BuildableModuleComponentMetaDataResolveResult)
+
+    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 module version listings"() {
+        when:
+        repo.localAccess.listModuleVersions(dep, listingResult)
+
+        then:
+        1 * localMetaDataCache.supplyModuleVersions(selector, listingResult) >> false
+        1 * localDelegate.listModuleVersions(dep, listingResult)
+        1 * localMetaDataCache.newModuleVersions(selector, listingResult)
+        0 * _
+
+        when:
+        repo.remoteAccess.listModuleVersions(dep, listingResult)
+
+        then:
+        1 * remoteMetaDataCache.supplyModuleVersions(selector, listingResult) >> false
+        1 * remoteDelegate.listModuleVersions(dep, listingResult)
+        1 * remoteMetaDataCache.newModuleVersions(selector, listingResult)
+        0 * _
+    }
+
+    def "uses module version listings from cache"() {
+        when:
+        repo.localAccess.listModuleVersions(dep, listingResult)
+
+        then:
+        1 * localMetaDataCache.supplyModuleVersions(selector, listingResult) >> true
+        0 * _
+
+        when:
+        repo.remoteAccess.listModuleVersions(dep, listingResult)
+
+        then:
+        1 * remoteMetaDataCache.supplyModuleVersions(selector, listingResult) >> true
+        0 * _
+    }
+
+    def "retrieves and caches local dependencies"() {
+        when:
+        repo.localAccess.resolveComponentMetaData(dep, lib, metaDataResult)
+
+        then:
+        1 * localMetaDataCache.supplyMetaData(lib, metaDataResult) >> false
+        1 * localDelegate.resolveComponentMetaData(dep, lib, metaDataResult)
+        1 * localMetaDataCache.newDependencyResult(lib, metaDataResult)
+        0 * _
+    }
+
+    def "uses local dependencies from cache"() {
+        when:
+        repo.localAccess.resolveComponentMetaData(dep, lib, metaDataResult)
+
+        then:
+        1 * localMetaDataCache.supplyMetaData(lib, metaDataResult) >> true
+        0 * _
+    }
+
+    def "retrieves and caches dependencies"() {
+        when:
+        repo.remoteAccess.resolveComponentMetaData(dep, lib, metaDataResult)
+
+        then:
+        1 * remoteMetaDataCache.supplyMetaData(lib, metaDataResult) >> false
+        1 * remoteDelegate.resolveComponentMetaData(dep, lib, metaDataResult)
+        1 * remoteMetaDataCache.newDependencyResult(lib, metaDataResult)
+        0 * _
+    }
+
+    def "uses dependencies from cache"() {
+        when:
+        repo.remoteAccess.resolveComponentMetaData(dep, lib, metaDataResult)
+
+        then:
+        1 * remoteMetaDataCache.supplyMetaData(lib, metaDataResult) >> true
+        0 * _
+    }
+
+    def "delegates request for module artifacts by type"() {
+        def moduleMetaData = Stub(ModuleComponentResolveMetaData)
+        def artifactType = ArtifactType.JAVADOC
+        def result = Mock(BuildableArtifactSetResolveResult)
+
+        when:
+        repo.localAccess.resolveModuleArtifacts(moduleMetaData, artifactType, result)
+
+        then:
+        1 * localDelegate.resolveModuleArtifacts(moduleMetaData, artifactType, result)
+        0 * _
+
+        when:
+        repo.remoteAccess.resolveModuleArtifacts(moduleMetaData, artifactType, result)
+
+        then:
+        1 * remoteDelegate.resolveModuleArtifacts(moduleMetaData, artifactType, result)
+        0 * _
+    }
+
+    def "delegates request for module artifacts for usage"() {
+        def moduleMetaData = Stub(ModuleComponentResolveMetaData)
+        def componentUsage = Stub(ComponentUsage)
+        def result = Mock(BuildableArtifactSetResolveResult)
+
+        when:
+        repo.localAccess.resolveModuleArtifacts(moduleMetaData, componentUsage, result)
+
+        then:
+        1 * localDelegate.resolveModuleArtifacts(moduleMetaData, componentUsage, result)
+        0 * _
+
+        when:
+        repo.remoteAccess.resolveModuleArtifacts(moduleMetaData, componentUsage, result)
+
+        then:
+        1 * remoteDelegate.resolveModuleArtifacts(moduleMetaData, componentUsage, result)
+        0 * _
+    }
+
+    def "retrieves and caches artifacts"() {
+        def result = Mock(BuildableArtifactResolveResult)
+        def artifactId = Stub(ModuleComponentArtifactIdentifier)
+        def artifact = Stub(ModuleComponentArtifactMetaData) {
+            getId() >> artifactId
+        }
+        def moduleSource = Mock(ModuleSource)
+
+        when:
+        repo.localAccess.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * localArtifactsCache.supplyArtifact(artifactId, result) >> false
+        1 * localDelegate.resolveArtifact(artifact, moduleSource, result)
+        1 * localArtifactsCache.newArtifact(artifactId, result)
+        0 * _
+
+        when:
+        repo.remoteAccess.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * remoteArtifactsCache.supplyArtifact(artifactId, result) >> false
+        1 * remoteDelegate.resolveArtifact(artifact, moduleSource, result)
+        1 * remoteArtifactsCache.newArtifact(artifactId, result)
+        0 * _
+    }
+
+    def "uses artifacts from cache"() {
+        def result = Mock(BuildableArtifactResolveResult)
+        def artifactId = Stub(ModuleComponentArtifactIdentifier)
+        def artifact = Stub(ModuleComponentArtifactMetaData) {
+            getId() >> artifactId
+        }
+        def moduleSource = Mock(ModuleSource)
+
+        when:
+        repo.localAccess.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * localArtifactsCache.supplyArtifact(artifactId, result) >> true
+        0 * _
+
+        when:
+        repo.remoteAccess.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * remoteArtifactsCache.supplyArtifact(artifactId, result) >> true
+        0 * _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactoryTest.groovy
new file mode 100644
index 0000000..e2bfd1b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryCachedRepositoryFactoryTest.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.api.internal.artifacts.ivyservice.ivyresolve.memcache
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+class InMemoryCachedRepositoryFactoryTest extends Specification {
+
+    @Rule SetSystemProperties sysProp = new SetSystemProperties()
+    def cache = new InMemoryCachedRepositoryFactory()
+
+    def "can be turned off via system property"() {
+        System.properties.setProperty(InMemoryCachedRepositoryFactory.TOGGLE_PROPERTY, "false")
+        def repo = Mock(ModuleComponentRepository) { getId() >> "mavenCentral" }
+
+        when:
+        def out = cache.cached(repo)
+
+        then:
+        out.is(repo)
+    }
+
+    def "wraps repositories"() {
+        def repo1 = Mock(ModuleComponentRepository) { getId() >> "mavenCentral" }
+        def repo2 = Mock(ModuleComponentRepository) { getId() >> "localRepo" }
+        def repo3 = Mock(ModuleComponentRepository) { getId() >> "mavenCentral" }
+
+        when:
+        ModuleComponentRepository c1 = cache.cached(repo1)
+        ModuleComponentRepository c2 = cache.cached(repo2)
+        ModuleComponentRepository c3 = cache.cached(repo3)
+
+        then:
+        c1.delegate == repo1
+        c2.delegate == repo2
+        c3.delegate == repo3
+
+        // Caches are shared for same repository id
+        c1.localAccess.artifactsCache == c3.localAccess.artifactsCache
+        c1.localAccess.metaDataCache == c3.localAccess.metaDataCache
+        c1.remoteAccess.artifactsCache == c3.remoteAccess.artifactsCache
+        c1.remoteAccess.metaDataCache == c3.remoteAccess.metaDataCache
+
+        c2.localAccess.artifactsCache != c1.localAccess.artifactsCache
+
+        cache.stats.reposWrapped == 3
+        cache.stats.cacheInstances == 2
+
+        cache.cachePerRepo.size() == 2
+    }
+
+    def "cleans cache on close"() {
+        when:
+        cache.cached(Mock(ModuleComponentRepository) { getId() >> "x"} )
+        cache.stop()
+
+        then:
+        cache.cachePerRepo.isEmpty()
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCacheTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCacheTest.groovy
new file mode 100644
index 0000000..849e3a1
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/memcache/InMemoryMetaDataCacheTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult
+import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class InMemoryMetaDataCacheTest extends Specification {
+
+    def stats = new InMemoryCacheStats()
+    def cache = new InMemoryMetaDataCache(stats)
+
+    static componentId(String group, String module, String version) {
+        return DefaultModuleComponentIdentifier.newId(group, module, version)
+    }
+
+    def "caches and supplies module versions"() {
+        def versions = ['1', '2', '3'] as Set
+        def result = Mock(BuildableModuleVersionListingResolveResult)
+        def missingResult = Mock(BuildableModuleVersionListingResolveResult)
+
+        given:
+        cache.newModuleVersions(newSelector("org", "foo-remote", "1.0"), Stub(BuildableModuleVersionListingResolveResult) {
+            getState() >> BuildableModuleVersionListingResolveResult.State.Listed
+            getVersions() >> versions
+        })
+
+        when:
+        def found = cache.supplyModuleVersions(newSelector("org", "foo-remote", "1.0"), result)
+        def missing = cache.supplyModuleVersions(newSelector("org", "foo-local", "1.0"), missingResult)
+
+        then:
+        found
+        1 * result.listed(versions)
+
+        and:
+        !missing
+        0 * missingResult._
+    }
+
+    def "does not cache failed module version listing"() {
+        def failedResult = Stub(BuildableModuleVersionListingResolveResult) {
+            getState() >> BuildableModuleVersionListingResolveResult.State.Failed
+        }
+        cache.newModuleVersions(newSelector("org", "lib", "1.0"), failedResult)
+
+        def result = Mock(BuildableModuleVersionListingResolveResult)
+
+        when:
+        def foundInCache = cache.supplyModuleVersions(newSelector("org", "lib", "1.0"), result)
+
+        then:
+        !foundInCache
+        0 * result._
+    }
+
+    def "caches and supplies remote metadata"() {
+        def suppliedMetaData = Stub(MutableModuleComponentResolveMetaData)
+        def cachedCopy = Stub(MutableModuleComponentResolveMetaData)
+        def originalMetaData = Stub(MutableModuleComponentResolveMetaData)
+        def resolvedResult = Mock(BuildableModuleComponentMetaDataResolveResult.class) {
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+            getMetaData() >> originalMetaData
+        }
+        def result = Mock(BuildableModuleComponentMetaDataResolveResult.class)
+
+        given:
+        _ * originalMetaData.copy() >> cachedCopy
+        cache.newDependencyResult(componentId("org", "foo", "1.0"), resolvedResult)
+
+        when:
+        def differentSelector = cache.supplyMetaData(componentId("org", "XXX", "1.0"), result)
+
+        then:
+        !differentSelector
+        stats.metadataServed == 0
+        0 * result._
+
+        when:
+        def match = cache.supplyMetaData(componentId("org", "foo", "1.0"), result)
+
+        then:
+        match
+        stats.metadataServed == 1
+        _ * cachedCopy.copy() >> suppliedMetaData
+        1 * result.resolved(suppliedMetaData)
+    }
+
+    def "caches and supplies remote and local metadata"() {
+        def moduleMetaData = Stub(MutableModuleComponentResolveMetaData)
+        _ * moduleMetaData.copy() >> moduleMetaData
+        def resolvedResult = Mock(BuildableModuleComponentMetaDataResolveResult.class) {
+            getMetaData() >> moduleMetaData
+            getState() >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+        }
+        def result = Mock(BuildableModuleComponentMetaDataResolveResult.class)
+
+        given:
+        cache.newDependencyResult(componentId("org", "remote", "1.0"), resolvedResult)
+
+        when:
+        def found = cache.supplyMetaData(componentId("org", "remote", "1.0"), result)
+
+        then:
+        found
+        stats.metadataServed == 1
+        1 * result.resolved(moduleMetaData)
+    }
+
+    def "does not cache failed resolves"() {
+        def failedResult = Mock(BuildableModuleComponentMetaDataResolveResult.class) { getState() >> BuildableModuleComponentMetaDataResolveResult.State.Failed }
+        cache.newDependencyResult(componentId("org", "lib", "1.0"), failedResult)
+
+        def result = Mock(BuildableModuleComponentMetaDataResolveResult.class)
+
+        when:
+        def fromCache = cache.supplyMetaData(componentId("org", "lib", "1.0"), result)
+
+        then:
+        !fromCache
+        0 * result._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractGradlePomModuleDescriptorParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractGradlePomModuleDescriptorParserTest.groovy
new file mode 100644
index 0000000..7c0c542
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractGradlePomModuleDescriptorParserTest.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.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.ModuleRevisionId
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+abstract class AbstractGradlePomModuleDescriptorParserTest extends Specification {
+    @Rule
+    public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final GradlePomModuleDescriptorParser parser = new GradlePomModuleDescriptorParser(new DefaultVersionSelectorScheme())
+    final parseContext = Mock(DescriptorParseContext)
+    TestFile pomFile
+
+    def "setup"() {
+        pomFile = tmpDir.file('foo')
+    }
+
+    protected ModuleDescriptor parsePom() {
+        parseMetaData().descriptor
+    }
+
+    protected MutableModuleComponentResolveMetaData parseMetaData() {
+        parser.parseMetaData(parseContext, pomFile, true)
+    }
+
+    protected void hasDefaultDependencyArtifact(DependencyDescriptor descriptor) {
+        assert descriptor.allDependencyArtifacts.length == 0
+    }
+
+    protected void hasDependencyArtifact(DependencyDescriptor descriptor, String name, String type, String ext, String classifier = null) {
+        assert descriptor.allDependencyArtifacts.length == 1
+        def artifact = descriptor.allDependencyArtifacts.first()
+        assert artifact.name == name
+        assert artifact.type == type
+        assert artifact.ext == ext
+        assert artifact.extraAttributes['classifier'] == classifier
+    }
+
+    protected ModuleRevisionId moduleId(String group, String name, String version) {
+        IvyUtil.createModuleRevisionId(group, name, version)
+    }
+
+    protected ArtifactRevisionId artifactId(ModuleRevisionId moduleId, String name, String type, String ext) {
+        ArtifactRevisionId.newInstance(moduleId, name, type, ext)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.groovy
new file mode 100644
index 0000000..f2ecd07
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/AbstractPomReaderTest.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.internal.artifacts.ivyservice.ivyresolve.parser
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource
+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.junit.Rule
+import spock.lang.Specification
+
+abstract class AbstractPomReaderTest extends Specification {
+    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    PomReader pomReader
+    TestFile pomFile
+    LocallyAvailableExternalResource locallyAvailableExternalResource
+
+    def setup() {
+        pomFile = tmpDir.file('pom.xml')
+        pomFile.createFile()
+        LocallyAvailableResource locallyAvailableResource = new DefaultLocallyAvailableResource(pomFile)
+        locallyAvailableExternalResource = new DefaultLocallyAvailableExternalResource(pomFile.toURI(), locallyAvailableResource)
+    }
+
+    protected void assertResolvedPomDependency(MavenDependencyKey key, String version) {
+        assert pomReader.dependencies.containsKey(key)
+        PomReader.PomDependencyData dependency = pomReader.dependencies[key]
+        assertPomDependencyValues(key, version, dependency)
+    }
+
+    protected void assertResolvedPomDependencyManagement(MavenDependencyKey key, String version) {
+        assert pomReader.dependencyMgt.containsKey(key)
+        PomDependencyMgt dependency = pomReader.dependencyMgt[key]
+        assertPomDependencyValues(key, version, dependency)
+    }
+
+    protected void assertPomDependencyValues(MavenDependencyKey key, String version, PomDependencyMgt dependency) {
+        assert dependency.groupId == key.groupId
+        assert dependency.artifactId == key.artifactId
+        assert dependency.type == key.type
+        assert dependency.classifier == key.classifier
+        assert dependency.version == version
+    }
+
+    protected PomReader createPomReader(String pomFileName, String pomDefinition) {
+        TestFile pomFile = tmpDir.file(pomFileName)
+        pomFile.createFile()
+        pomFile << pomDefinition
+        LocallyAvailableResource locallyAvailableResource = new DefaultLocallyAvailableResource(pomFile)
+        LocallyAvailableExternalResource locallyAvailableExternalResource = new DefaultLocallyAvailableExternalResource(pomFile.toURI(), locallyAvailableResource)
+        return new PomReader(locallyAvailableExternalResource)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.groovy
new file mode 100644
index 0000000..b9c5fcc
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DisconnectedIvyXmlModuleDescriptorParserTest.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.ivyresolve.parser
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+import org.gradle.internal.resource.ExternalResource
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource
+import org.gradle.internal.resource.local.LocallyAvailableResource
+import spock.lang.Specification
+
+class DisconnectedIvyXmlModuleDescriptorParserTest extends Specification {
+    LocallyAvailableExternalResource localExternalResource = Mock()
+    LocallyAvailableResource localResource = Mock()
+    ExternalResource externalResource = Mock()
+    ResolverStrategy resolverStrategy = Stub()
+    IvyXmlModuleDescriptorParser parser = new DisconnectedIvyXmlModuleDescriptorParser(resolverStrategy)
+    DescriptorParseContext parseContext = Mock()
+
+    def "creates overridden internal Ivy parser"() throws Exception {
+        when:
+        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
+
+        then:
+        localExternalResource.getLocalResource() >> localResource
+        localResource.getFile() >> new File('ivy.xml')
+        disconnectedParser != null
+        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
+    }
+
+    def "creates new internal Ivy parser"() throws Exception {
+        when:
+        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
+
+        and:
+        IvyXmlModuleDescriptorParser.Parser newDisconnectedParser = disconnectedParser.newParser(externalResource, new File('ivy.xml').toURI().toURL())
+
+        then:
+        localExternalResource.getLocalResource() >> localResource
+        localResource.getFile() >> new File('ivy.xml')
+        disconnectedParser != null
+        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
+        newDisconnectedParser != null
+        newDisconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
+    }
+
+    def "parses other Ivy file and return with standard module descriptor"() throws Exception {
+        when:
+        IvyXmlModuleDescriptorParser.Parser disconnectedParser = parser.createParser(parseContext, localExternalResource, [:], resolverStrategy)
+
+        and:
+        ModuleDescriptor moduleDescriptor = disconnectedParser.parseOtherIvyFile('myorg', 'parentMod', 'parentRev')
+
+        then:
+        localExternalResource.getLocalResource() >> localResource
+        localResource.getFile() >> new File('ivy.xml')
+        disconnectedParser
+        disconnectedParser instanceof DisconnectedIvyXmlModuleDescriptorParser.DisconnectedParser
+        moduleDescriptor != null
+        moduleDescriptor.status == 'release'
+        moduleDescriptor.publicationDate != null
+        moduleDescriptor.moduleRevisionId.organisation == 'myorg'
+        moduleDescriptor.moduleRevisionId.moduleId.name == 'parentMod'
+        moduleDescriptor.moduleRevisionId.revision == 'parentRev'
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/DownloadedIvyModuleDescriptorParserTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy
new file mode 100644
index 0000000..451d612
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserProfileTest.groovy
@@ -0,0 +1,1735 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.resource.local.DefaultLocallyAvailableExternalResource
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
+import spock.lang.Issue
+
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
+import static org.gradle.api.internal.component.ArtifactType.MAVEN_POM
+
+class GradlePomModuleDescriptorParserProfileTest extends AbstractGradlePomModuleDescriptorParserTest {
+    def "pom with project coordinates defined by active profile properties"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>\${some.group}</groupId>
+    <artifactId>\${some.artifact}</artifactId>
+    <version>\${some.version}</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <some.group>group-one</some.group>
+                <some.artifact>artifact-one</some.artifact>
+                <some.version>version-one</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 0
+    }
+
+    def "pom with dependency coordinates defined by active profile properties"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    def "uses parent properties from active profile to provide default values for a dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent properties from active profile to provide default values for a dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section in active profile 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>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <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>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        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 == createModuleId('group-three', 'artifact-three')
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom dependency management section in active profile to provide default values for a dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>1.2</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent pom dependency management section in active profile to provide default values for a dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>1.2</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section from imported POM in active profile to define defaults for main POM body dependency"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        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>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>different-group</groupId>
+                        <artifactId>imported</artifactId>
+                        <version>different-version</version>
+                        <type>pom</type>
+                        <scope>import</scope>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "pom with dependency defined by active profile"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    def "uses parent dependency from active profile"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent dependency from active profile"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.2</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent dependency over grand parent dependency with same groupId and artifactId from active profile"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.2</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <profiles>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.3</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.3')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section from imported POM in active profile to define defaults for profile dependency"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>different-group</groupId>
+                        <artifactId>imported</artifactId>
+                        <version>different-version</version>
+                        <type>pom</type>
+                        <scope>import</scope>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "pom with project coordinates defined by profile activated by absence of system property"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>\${some.group}</groupId>
+    <artifactId>\${some.artifact}</artifactId>
+    <version>\${some.version}</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!somePrpperty</name>
+                </property>
+            </activation>
+            <properties>
+                <some.group>group-one</some.group>
+                <some.artifact>artifact-one</some.artifact>
+                <some.version>version-one</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 0
+    }
+
+    def "pom with dependency coordinates defined by profile activated by absence of system property"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    def "uses parent properties from activated by absence of system property to provide default values for a dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent properties from profile activated by absence of system property to provide default values for a dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <properties>
+                <some.group>group-two</some.group>
+                <some.artifact>artifact-two</some.artifact>
+                <some.version>version-two</some.version>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section in profile activated by absence of system property 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>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <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>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        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 == createModuleId('group-three', 'artifact-three')
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom dependency management section in profile activated by absence of system property to provide default values for a dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>1.2</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent pom dependency management section in profile activated by absence of system property to provide default values for a dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>1.2</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section from imported POM in profile activated by absence of system property to define defaults for main POM body dependency"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        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>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>different-group</groupId>
+                        <artifactId>imported</artifactId>
+                        <version>different-version</version>
+                        <type>pom</type>
+                        <scope>import</scope>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "pom with dependency defined in profile activated by absence of system property"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    def "uses parent dependency from profile activated by absence of system property"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent dependency from profile activated by absence of system property"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.2</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent dependency over grand parent dependency with same groupId and artifactId from profile activated by absence of system property"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.2</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <profiles>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>1.3</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.3')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses dependency management section from imported POM in profile activated by absence of system property to define defaults for profile dependency"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!someProperty</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>different-group</groupId>
+                        <artifactId>imported</artifactId>
+                        <version>different-version</version>
+                        <type>pom</type>
+                        <scope>import</scope>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    @Issue("GRADLE-3074")
+    def "pom with packaging defined in active profile"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <package.type>war</package.type>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.allArtifacts.length == 0
+        metaData.packaging == 'war'
+    }
+
+    @Issue("GRADLE-3074")
+    def "pom with packaging defined by custom property in active profile of parent pom"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <package.type>war</package.type>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.allArtifacts.length == 0
+        metaData.packaging == 'war'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
new file mode 100644
index 0000000..eef9172
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParserTest.groovy
@@ -0,0 +1,2250 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.external.model.MavenModuleResolveMetaData
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
+import spock.lang.Issue
+import spock.lang.Unroll
+
+import static org.gradle.api.internal.artifacts.ivyservice.IvyUtil.createModuleId
+import static org.gradle.api.internal.component.ArtifactType.MAVEN_POM
+
+class GradlePomModuleDescriptorParserTest extends AbstractGradlePomModuleDescriptorParserTest {
+    def "parses simple pom"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <description>The first test artifact</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>version-two</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        metaData instanceof MavenModuleResolveMetaData
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+        parser.typeName == 'POM'
+        parser.toString() == 'gradle pom parser'
+    }
+
+    def "converts timestamp version to SNAPSHOT version"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>my-version-20141012.121000-1</version>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'my-version-SNAPSHOT')
+    }
+
+    def "merges dependencies declared in pom with those declared in parent"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<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>
+            <version>1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-three</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-one</artifactId>
+            <version>11</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-three</artifactId>
+            <version>11</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 3
+
+        def dep1 = descriptor.dependencies[0]
+        dep1.dependencyRevisionId == moduleId('group-two', 'artifact-one', '11')
+        dep1.moduleConfigurations == ['compile', 'runtime']
+
+        def dep2 = descriptor.dependencies[1]
+        dep2.dependencyRevisionId == moduleId('group-two', 'artifact-three', '11')
+        dep2.moduleConfigurations == ['compile', 'runtime']
+
+        def inheritedDep = descriptor.dependencies[2]
+        inheritedDep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.2')
+        inheritedDep.moduleConfigurations == ['compile', 'runtime']
+    }
+
+    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>
+"""
+
+        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 == createModuleId('group-three', 'artifact-three')
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "throws exception if parent pom dependency management section does not provide default values for dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-three</groupId>
+                <artifactId>artifact-three</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        parsePom()
+
+        then:
+        Throwable t = thrown(MetaDataParseException)
+        t.cause instanceof UnresolvedDependencyVersionException
+        t.cause.message == "Unable to resolve version for dependency 'group-two:artifact-two:jar'"
+    }
+
+    def "uses parent pom dependency management section to provide default values for a dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+                <scope>test</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        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']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom dependency management section with multiple versions of same dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.3</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.1</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.1')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses imported child pom over parent pom dependency management section"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>different-group</groupId>
+                <artifactId>imported</artifactId>
+                <version>different-version</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.5')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses importing pom dependency management over imported pom definition with same group ID and artifact ID "() {
+        given:
+
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>different-group</groupId>
+                <artifactId>imported</artifactId>
+                <version>different-version</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses child dependency over parent dependency with same group ID and artifact ID"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<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>
+            <version>1.5</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom dependency with multiple versions of same dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<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>
+            <version>1.3</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>1.5</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>1.4</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.4')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom over grand parent pom dependency management section"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent pom properties for parent pom dependency management section"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <properties>
+        <grandparent.groupid>group-two</grandparent.groupid>
+        <grandparent.artifactid>artifact-two</grandparent.artifactid>
+    </properties>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>\${grandparent.groupid}</groupId>
+                <artifactId>\${grandparent.artifactid}</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom properties over grand parent pom properties for dependency management if overridden"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <properties>
+        <my.groupid>group-three</my.groupid>
+        <my.artifactid>artifact-three</my.artifactid>
+    </properties>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <properties>
+        <my.groupid>group-two</my.groupid>
+        <my.artifactid>artifact-two</my.artifactid>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>\${my.groupid}</groupId>
+                <artifactId>\${my.artifactid}</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses properties from parent pom to replace variable placeholders in pom"() {
+        given:
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <properties>
+        <artifact-two.version>v2</artifact-two.version>
+        <artifact-three.version>ignore-me</artifact-three.version>
+    </properties>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <properties>
+        <artifact-three.version>v3</artifact-three.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>\${artifact-two.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>group-three</groupId>
+            <artifactId>artifact-three</artifactId>
+            <version>\${artifact-three.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 2
+        def artifactTwo = descriptor.dependencies[0]
+        artifactTwo.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'v2')
+        hasDefaultDependencyArtifact(artifactTwo)
+        def artifactThree = descriptor.dependencies[1]
+        artifactThree.dependencyRevisionId == moduleId('group-three', 'artifact-three', 'v3')
+        hasDefaultDependencyArtifact(artifactThree)
+    }
+
+    def "replaces variable placeholders in parent pom dependency management section"() {
+        given:
+        def grandParent = tmpDir.file("grand-parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>\${project.groupId}</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>\${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>\${project.groupId}</groupId>
+                <artifactId>artifact-three</artifactId>
+                <version>\${project.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${project.groupId}</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-one</groupId>
+            <artifactId>artifact-three</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 2
+        def artifactTwo = descriptor.dependencies[0]
+        artifactTwo.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
+        artifactTwo.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(artifactTwo)
+        def artifactThree = descriptor.dependencies[1]
+        artifactThree.dependencyRevisionId == moduleId('group-one', 'artifact-three', 'version-one')
+        artifactThree.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(artifactThree)
+    }
+
+    @Issue("GRADLE-2918")
+    def "uses imported BOM in grand parent dependency management section"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <properties>
+        <myversion>some-version</myversion>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-one</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>\${myversion}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        def grandParent = tmpDir.file("grand-parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>different-group</groupId>
+                <artifactId>imported</artifactId>
+                <version>different-version</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xlm") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-one</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def artifactTwo = descriptor.dependencies[0]
+        artifactTwo.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'some-version')
+        artifactTwo.moduleConfigurations == ['compile', 'runtime']
+    }
+
+    def "pom with dependency with classifier"() {
+        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>
+            <version>version-two</version>
+            <classifier>classifier-two</classifier>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDependencyArtifact(descriptor.dependencies.first(), 'artifact-two', 'jar', 'jar', 'classifier-two')
+    }
+
+    @Issue("GRADLE-2068")
+    def "pom with dependency with empty classifier is treated like dependency without classifier"() {
+        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>
+            <version>version-two</version>
+            <classifier></classifier>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    @Issue("GRADLE-2076")
+    def "pom with packaging of type eclipse-plugin creates jar artifact"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>eclipse-plugin</packaging>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 0
+    }
+
+    def "fails when POM is not well formed XML"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion
+</project>
+"""
+
+        when:
+        parseMetaData()
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse POM ${pomFile.toURI()}"
+        e.cause.message.contains('Element type "modelVersion"')
+    }
+
+
+    def "pom with no dependencies "() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>pom</packaging>
+</project>
+"""
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 0
+    }
+
+    def "pom with packaging 'pom'"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>pom</packaging>
+</project>
+"""
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.allArtifacts.length == 0
+        metaData.packaging == 'pom'
+    }
+
+    @Issue("GRADLE-3074")
+    def "pom with packaging defined by custom property"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+    <properties>
+        <package.type>war</package.type>
+    </properties>
+</project>
+"""
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.allArtifacts.length == 0
+        metaData.packaging == 'war'
+    }
+
+    @Issue("GRADLE-3074")
+    def "pom with packaging defined by custom property in parent pom"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <properties>
+        <package.type>war</package.type>
+    </properties>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def metaData = parseMetaData()
+        def descriptor = metaData.descriptor
+
+        then:
+        descriptor.allArtifacts.length == 0
+        metaData.packaging == 'war'
+    }
+
+    def "pom with project coordinates defined by custom properties"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>\${some.group}</groupId>
+    <artifactId>\${some.artifact}</artifactId>
+    <version>\${some.version}</version>
+    <properties>
+        <some.group>group-one</some.group>
+        <some.artifact>artifact-one</some.artifact>
+        <some.version>version-one</some.version>
+    </properties>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 0
+    }
+
+    def "pom with dependency coordinates defined by custom properties"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <properties>
+        <some.group>group-two</some.group>
+        <some.artifact>artifact-two</some.artifact>
+        <some.version>version-two</some.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${some.group}</groupId>
+            <artifactId>\${some.artifact}</artifactId>
+            <version>\${some.version}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        descriptor.dependencies.first().dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        hasDefaultDependencyArtifact(descriptor.dependencies.first())
+    }
+
+    def "uses parent pom over grand parent pom dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>1.5</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses grand parent pom properties for parent pom dependency"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <properties>
+        <grandparent.groupid>group-two</grandparent.groupid>
+        <grandparent.artifactid>artifact-two</grandparent.artifactid>
+    </properties>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${grandparent.groupid}</groupId>
+            <artifactId>\${grandparent.artifactid}</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "uses parent pom properties over grand parent pom properties for dependency if overridden"() {
+        given:
+        def grandParent = tmpDir.file("grandparent.xml") << """
+<project>
+    <groupId>different-group</groupId>
+    <artifactId>grandparent</artifactId>
+    <version>different-version</version>
+
+    <properties>
+        <my.groupid>group-three</my.groupid>
+        <my.artifactid>artifact-three</my.artifactid>
+    </properties>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>different-group</groupId>
+        <artifactId>grandparent</artifactId>
+        <version>different-version</version>
+    </parent>
+
+    <properties>
+        <my.groupid>group-two</my.groupid>
+        <my.artifactid>artifact-two</my.artifactid>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${my.groupid}</groupId>
+            <artifactId>\${my.artifactid}</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'grandparent' }, MAVEN_POM) >> {
+            new DefaultLocallyAvailableExternalResource(grandParent.toURI(), new DefaultLocallyAvailableResource(grandParent))
+        }
+
+        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 == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    def "defines relocation but doesn't include any explicit dependencies or artifacts"() {
+        given:
+        def relocated = tmpDir.file("relocated.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-relocated</groupId>
+    <artifactId>relocated</artifactId>
+    <version>version-one</version>
+</project>
+"""
+
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-three</groupId>
+            <artifactId>artifact-three</artifactId>
+            <version>1.3</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <distributionManagement>
+        <relocation>
+            <groupId>group-relocated</groupId>
+            <artifactId>relocated</artifactId>
+        </relocation>
+    </distributionManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+        parseContext.getMetaDataArtifact({ it.name == 'relocated' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(relocated.toURI(), new DefaultLocallyAvailableResource(relocated)) }
+
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-relocated', 'relocated', 'version-one')
+        dep.moduleConfigurations == ['default', 'master', 'compile', 'provided', 'runtime', 'system', 'sources', 'javadoc', 'optional']
+        hasDefaultDependencyArtifact(dep)
+
+        and:
+        descriptor.allArtifacts.length == 0
+    }
+
+    @Issue("GRADLE-2931")
+    def "handles dependencies with same group ID and artifact ID but different type and classifier"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.1</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>test-jar</type>
+                <version>1.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <artifactId>artifact-one</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 2
+        def depCompile = descriptor.dependencies[0]
+        depCompile.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.1')
+        depCompile.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(depCompile)
+        def depTest = descriptor.dependencies[1]
+        depTest.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.2')
+        depTest.moduleConfigurations == ['test']
+        hasDependencyArtifact(depTest, 'artifact-two', 'test-jar', 'jar', 'tests')
+    }
+
+    @Issue("GRADLE-2931")
+    def "throws exception if parent dependency management doesn't provide correct defaults for dependency"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>parent</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.1</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <artifactId>artifact-one</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'parent' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        parsePom()
+
+        then:
+        Throwable t = thrown(MetaDataParseException)
+        t.cause instanceof UnresolvedDependencyVersionException
+        t.cause.message == "Unable to resolve version for dependency 'group-two:artifact-two:test-jar'"
+    }
+
+    @Issue("GRADLE-2931")
+    def "picks version of last dependency defined by artifact ID, group ID, type and classifier"() {
+        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>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-two</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-three</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-four</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies[0]
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-four')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDependencyArtifact(dep, 'artifact-two', 'jar', 'jar', 'myjar')
+    }
+
+    @Issue("GRADLE-2931")
+    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier"() {
+        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>
+            <version>version-two</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-three</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <version>version-four</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <classifier>test</classifier>
+            <version>version-five</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>ejb-client</type>
+            <version>version-six</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 5
+        def defDep = descriptor.dependencies[0]
+        defDep.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        defDep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(defDep)
+        def depJar = descriptor.dependencies[1]
+        depJar.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-three')
+        depJar.moduleConfigurations == ['compile', 'runtime']
+        hasDependencyArtifact(depJar, 'artifact-two', 'jar', 'jar', 'myjar')
+        def depTestJar = descriptor.dependencies[2]
+        depTestJar.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-four')
+        depTestJar.moduleConfigurations == ['compile', 'runtime']
+        hasDependencyArtifact(depTestJar, 'artifact-two', 'test-jar', 'jar', 'tests')
+        def depTestJarWithClassifier = descriptor.dependencies[3]
+        depTestJarWithClassifier.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-five')
+        depTestJarWithClassifier.moduleConfigurations == ['compile', 'runtime']
+        hasDependencyArtifact(depTestJarWithClassifier, 'artifact-two', 'test-jar', 'jar', 'test')
+        def depEjbClient = descriptor.dependencies[4]
+        depEjbClient.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-six')
+        depEjbClient.moduleConfigurations == ['compile', 'runtime']
+        hasDependencyArtifact(depEjbClient, 'artifact-two', 'ejb-client', 'jar', 'client')
+    }
+
+    @Issue("GRADLE-2371")
+    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier and scope"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>group-one</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>version-one</version>
+        </dependency>
+        <dependency>
+            <groupId>group-one</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <version>version-one</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${project.groupId}</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>\${project.groupId}</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.moduleRevisionId == moduleId('group-one', 'artifact-one', 'version-one')
+        descriptor.dependencies.length == 2
+        def defDep = descriptor.dependencies[0]
+        defDep.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
+        defDep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(defDep)
+        def depJar = descriptor.dependencies[1]
+        depJar.dependencyRevisionId == moduleId('group-one', 'artifact-two', 'version-one')
+        depJar.moduleConfigurations == ['test']
+        hasDependencyArtifact(depJar, 'artifact-two', 'test-jar', 'jar', 'tests')
+    }
+
+    @Issue("GRADLE-2938")
+    def "uses default dependency type if only the dependency management or dependency element declares it"() {
+        given:
+        def parent = tmpDir.file("parent.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+                <type>jar</type>
+            </dependency>
+            <dependency>
+                <groupId>group-three</groupId>
+                <artifactId>artifact-three</artifactId>
+                <version>version-three</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-one</groupId>
+        <artifactId>parent</artifactId>
+        <version>version-one</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-three</groupId>
+            <artifactId>artifact-three</artifactId>
+            <type>jar</type>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(parent.toURI(), new DefaultLocallyAvailableResource(parent)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 2
+        def depGroupTwo = descriptor.dependencies[0]
+        depGroupTwo.dependencyRevisionId == moduleId('group-two', 'artifact-two', 'version-two')
+        depGroupTwo.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(depGroupTwo)
+        def depGroupThree = descriptor.dependencies[1]
+        depGroupThree.dependencyRevisionId == moduleId('group-three', 'artifact-three', 'version-three')
+        depGroupThree.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(depGroupThree)
+    }
+
+    @Issue("GRADLE-2982")
+    def "use imported pom even though dependency is declared multiple times with different scopes"() {
+        given:
+        def imported = tmpDir.file("imported.xml") << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>different-group</groupId>
+    <artifactId>imported</artifactId>
+    <version>different-version</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>1.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>different-group</groupId>
+                <artifactId>imported</artifactId>
+                <version>different-version</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>different-group</groupId>
+                <artifactId>imported</artifactId>
+                <version>different-version</version>
+                <type>pom</type>
+                <scope>provided</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        and:
+        parseContext.getMetaDataArtifact({ it.name == 'imported' }, MAVEN_POM) >> { new DefaultLocallyAvailableExternalResource(imported.toURI(), new DefaultLocallyAvailableResource(imported)) }
+
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 1
+        def dep = descriptor.dependencies.first()
+        dep.dependencyRevisionId == moduleId('group-two', 'artifact-two', '1.5')
+        dep.moduleConfigurations == ['compile', 'runtime']
+        hasDefaultDependencyArtifact(dep)
+    }
+
+    @Unroll
+    def "converts #inputVersion version to #outputVersion"() {
+        given:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>$inputVersion</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-three</groupId>
+            <artifactId>artifact-three</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>$inputVersion</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        when:
+        def descriptor = parsePom()
+
+        then:
+        descriptor.dependencies.length == 2
+        descriptor.dependencies.every{it.dependencyRevisionId.revision == outputVersion}
+        where:
+        inputVersion | outputVersion
+        "RELEASE"    | "latest.release"
+        "LATEST"     | "latest.integration"
+    }
+
+
+    @Unroll
+    def "handles dependency with type #type"() {
+        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>
+            <version>1.2</version>
+            <type>${type}</type>
+        </dependency>
+    </dependencies>
+</project>
+"""
+
+        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 == ['compile', 'runtime']
+        hasDependencyArtifact(dep, 'artifact-two', type, extension, classifier)
+
+        where:
+        type          | extension     | classifier
+        'test-jar'    | 'jar'         | 'tests'
+        'ejb'         | 'jar'         | null
+        'ejb-client'  | 'jar'         | 'client'
+        'bundle'      | 'jar'         | null
+        'custom-type' | 'custom-type' | null
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
new file mode 100644
index 0000000..5c0dcca
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/IvyXmlModuleDescriptorParserTest.groovy
@@ -0,0 +1,939 @@
+/*
+ * 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.ivyresolve.parser
+
+import org.apache.ivy.core.module.descriptor.*
+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.api.internal.artifacts.ivyservice.NamespaceId
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+import org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Resources
+import org.junit.Rule
+import spock.lang.Issue
+import spock.lang.Specification
+
+import static org.gradle.api.internal.component.ArtifactType.IVY_DESCRIPTOR
+import static org.junit.Assert.*
+
+class IvyXmlModuleDescriptorParserTest extends Specification {
+    @Rule
+    public final Resources resources = new Resources()
+    @Rule TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    ResolverStrategy resolverStrategy = Stub()
+    IvyXmlModuleDescriptorParser parser = new IvyXmlModuleDescriptorParser(resolverStrategy)
+    DescriptorParseContext parseContext = Mock()
+
+    def setup() {
+        resolverStrategy.getPatternMatcher("exact") >> ExactPatternMatcher.INSTANCE
+        resolverStrategy.getPatternMatcher("glob") >> GlobPatternMatcher.INSTANCE
+        resolverStrategy.getPatternMatcher("regexp") >> RegexpPatternMatcher.INSTANCE
+    }
+
+    def "parses minimal Ivy descriptor"() throws Exception {
+        when:
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="mymodule"
+          revision="myrev"
+    />
+</ivy-module>
+"""
+        ModuleDescriptor md = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        md != null
+        md.moduleRevisionId.organisation == "myorg"
+        md.moduleRevisionId.name == "mymodule"
+        md.moduleRevisionId.revision == "myrev"
+        md.moduleRevisionId.branch == null
+        md.status == "integration"
+        md.configurations*.name == ["default"]
+        md.getArtifacts("default").length == 1
+        md.dependencies.length == 0
+        md.inheritedDescriptors.length == 0
+    }
+
+    def "adds implicit configurations"() throws Exception {
+        when:
+        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.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        md != null
+        md.moduleRevisionId.organisation == "myorg"
+        md.moduleRevisionId.name == "mymodule"
+        md.moduleRevisionId.revision == "myrev"
+        md.status == "integration"
+        md.configurations*.name == ["default"]
+        md.getArtifacts("default") != null
+        md.getArtifacts("default").length == 1
+        md.getArtifacts("default")[0].name == "mymodule"
+        md.getArtifacts("default")[0].type == "jar"
+        md.dependencies.length == 0
+        md.inheritedDescriptors.length == 0
+    }
+
+    def "adds implicit artifact when none declared"() throws Exception {
+        when:
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="mymodule"
+          revision="myrev"
+    />
+    <configurations>
+        <conf name="A"/>
+        <conf name="B"/>
+    </configurations>
+</ivy-module>
+"""
+        ModuleDescriptor md = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        md.allArtifacts.length == 1
+
+        def artifact = md.allArtifacts[0]
+        artifact.name == 'mymodule'
+        artifact.type == 'jar'
+        artifact.ext == 'jar'
+        md.configurations*.name == ["A", "B"]
+        md.getArtifacts("A") == [artifact]
+        md.getArtifacts("B") == [artifact]
+
+        md.dependencies.length == 0
+        md.inheritedDescriptors.length == 0
+    }
+
+    public void "fails when ivy.xml uses unknown version of descriptor format"() throws IOException {
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="unknown">
+    <info organisation="myorg"
+          module="mymodule"
+    />
+</ivy-module>
+"""
+
+        when:
+        parser.parseMetaData(parseContext, file, true)
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message == "invalid version unknown"
+    }
+
+    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.parseMetaData(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.parseMetaData(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.parseMetaData(parseContext, file, true)
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message.contains('Element type "info"')
+    }
+
+    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.parseMetaData(parseContext, file, true)
+
+        then:
+        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 "fails when descriptor does declare module version id"() {
+        def file = temporaryFolder.file("ivy.xml") << xml
+        when:
+        parser.parseMetaData(parseContext, file, true)
+
+        then:
+        def e = thrown(MetaDataParseException)
+        e.message == "Could not parse Ivy file ${file.toURI()}"
+        e.cause.message.contains('null name not allowed')
+
+        where:
+        xml << [
+            """<ivy-module version="1.0">
+                <info>
+            </ivy-module>"""
+        ]
+    }
+
+    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.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        assertNotNull(md)
+        assertEquals("myorg", md.getModuleRevisionId().getOrganisation())
+        assertEquals("mymodule", md.getModuleRevisionId().getName())
+        assertEquals("myrev", md.getModuleRevisionId().getRevision())
+        assertEquals("branch", md.getModuleRevisionId().getBranch())
+        assertEquals("integration", md.getStatus())
+        Date pubdate = new GregorianCalendar(2004, 10, 1, 11, 0, 0).getTime()
+        assertEquals(pubdate, md.getPublicationDate())
+
+        License[] licenses = md.getLicenses()
+        assertEquals(1, licenses.length)
+        assertEquals("MyLicense", licenses[0].getName())
+        assertEquals("http://www.my.org/mymodule/mylicense.html", licenses[0].getUrl())
+
+        assertEquals("http://www.my.org/mymodule/", md.getHomePage())
+        assertEquals("This module is <b>great</b> !<br/>\n\t"
+                + "You can use it especially with myconf1 and myconf2, "
+                + "and myconf4 is not too bad too.",
+                md.getDescription().replaceAll("\r\n", "\n").replace('\r', '\n'))
+
+        assertEquals(1, md.getExtraInfo().size())
+        assertEquals("56576", md.getExtraInfo().get(new NamespaceId("http://ant.apache.org/ivy/extra", "someExtra")))
+
+        Configuration[] confs = md.getConfigurations()
+        assertNotNull(confs)
+        assertEquals(5, confs.length)
+
+        assertConf(md, "myconf1", "desc 1", Configuration.Visibility.PUBLIC, new String[0])
+        assertConf(md, "myconf2", "desc 2", Configuration.Visibility.PUBLIC, new String[0])
+        assertConf(md, "myconf3", "desc 3", Configuration.Visibility.PRIVATE, new String[0])
+        assertConf(md, "myconf4", "desc 4", Configuration.Visibility.PUBLIC, ["myconf1", "myconf2"].toArray(new String[2]))
+        assertConf(md, "myoldconf", "my old desc", Configuration.Visibility.PUBLIC, new String[0])
+
+        assertArtifacts(md.getArtifacts("myconf1"), ["myartifact1", "myartifact2", "myartifact3", "myartifact4"].toArray(new String[4]))
+
+        assertArtifacts(md.getArtifacts("myconf2"), ["myartifact1", "myartifact3"].toArray(new String[2]))
+        assertArtifacts(md.getArtifacts("myconf3"), ["myartifact1", "myartifact3", "myartifact4"].toArray(new String[3]))
+        assertArtifacts(md.getArtifacts("myconf4"), ["myartifact1"].toArray(new String[1]))
+
+        DependencyDescriptor[] dependencies = md.getDependencies()
+        assertNotNull(dependencies)
+        assertEquals(13, dependencies.length)
+
+        verifyFullDependencies(dependencies)
+
+        ExcludeRule[] rules = md.getAllExcludeRules()
+        assertNotNull(rules)
+        assertEquals(2, rules.length)
+        assertEquals(GlobPatternMatcher.INSTANCE, rules[0].getMatcher())
+        assertEquals(ExactPatternMatcher.INSTANCE, rules[1].getMatcher())
+        assertEquals(Arrays.asList("myconf1"), Arrays.asList(rules[0]
+                .getConfigurations()))
+        assertEquals(Arrays.asList("myconf1", "myconf2", "myconf3", "myconf4", "myoldconf"), Arrays.asList(rules[1].getConfigurations()))
+        md.inheritedDescriptors.length == 0
+    }
+
+    def "merges values from parent Ivy descriptor"() throws Exception {
+        given:
+        def parentFile = temporaryFolder.file("parent.xml") << """
+<ivy-module version="1.0">
+    <info organisation="myorg"
+          module="parent"
+          revision="parentrev"
+          status="not-the-default"
+          publication="20041101110000"
+    />
+    <configurations>
+        <conf name='default'/>
+    </configurations>
+    <dependencies>
+        <dependency conf="*->*" org="deporg" name="depname" rev="deprev"/>
+    </dependencies>
+</ivy-module>
+"""
+        def file = temporaryFolder.file("ivy.xml") << """
+<ivy-module version="1.0">
+    <info module="mymodule" revision="myrev">
+        <extends organisation="myorg" module="parent" revision="parentrev"/>
+    </info>
+</ivy-module>
+"""
+        and:
+        parseContext.getMetaDataArtifact(_, IVY_DESCRIPTOR) >> new DefaultLocallyAvailableExternalResource(parentFile.toURI(), new DefaultLocallyAvailableResource(parentFile))
+
+        when:
+        ModuleDescriptor md = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        md != null
+        md.moduleRevisionId.organisation == "myorg"
+        md.moduleRevisionId.name == "mymodule"
+        md.moduleRevisionId.revision == "myrev"
+        md.status == "integration"
+        md.configurations*.name == ["default"]
+        md.dependencies.length == 1
+        md.dependencies[0].dependencyRevisionId.organisation == 'deporg'
+        md.dependencies[0].dependencyRevisionId.name == 'depname'
+        md.dependencies[0].dependencyRevisionId.revision == 'deprev'
+        md.inheritedDescriptors.length == 0
+    }
+
+    @Issue("https://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" rev="1.2"/>
+                </dependencies>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+        def dependency = descriptor.dependencies.first()
+
+        then:
+        dependency.moduleConfigurations == ["myconf"]
+    }
+
+    def "defaultconf 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="conf1" />
+                    <conf name="conf2" />
+                </configurations>
+                <publications defaultconf="conf2">
+                    <artifact/>
+                </publications>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        def artifact = descriptor.allArtifacts[0]
+        artifact.configurations == ["conf2"]
+        descriptor.getArtifacts("conf2") == [artifact]
+        descriptor.getArtifacts("conf1") == []
+    }
+
+    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" rev="1.2" conf="a"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->other"/>
+                    <dependency name="mymodule2" rev="1.2" conf="*->@"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->other;%->@"/>
+                    <dependency name="mymodule2" rev="1.2" conf="*,!a->@"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->*"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->one,two;a,b->three;*->four;%->none"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->#"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->a;%->@"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->a;*,!a->b"/>
+                    <dependency name="mymodule2" rev="1.2" conf="*->*"/>
+                    <dependency name="mymodule2" rev="1.2" conf=""/>
+                    <dependency name="mymodule2" rev="1.2"/>
+                </dependencies>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        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 dependency11 = descriptor.dependencies[10]
+        dependency11.moduleConfigurations == ["*"]
+        dependency11.getDependencyConfigurations("a") == ["*"]
+        dependency11.getDependencyConfigurations("a", "requested") == ["*"]
+
+        def dependency12 = descriptor.dependencies[11]
+        dependency12.moduleConfigurations == ["*"]
+        dependency12.getDependencyConfigurations("a") == ["*"]
+        dependency12.getDependencyConfigurations("a", "requested") == ["*"]
+
+        def dependency13 = descriptor.dependencies[12]
+        dependency13.moduleConfigurations == ["*"]
+        dependency13.getDependencyConfigurations("a") == ["*"]
+        dependency13.getDependencyConfigurations("a", "requested") == ["*"]
+    }
+
+    def "parses dependency config mappings with defaults"() {
+        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 defaultconf="a" defaultconfmapping="a->a1;b->b1,b2">
+                    <dependency name="mymodule2" rev="1.2"/>
+                    <dependency name="mymodule2" rev="1.2" conf=""/>
+                    <dependency name="mymodule2" rev="1.2" conf="a"/>
+                    <dependency name="mymodule2" rev="1.2" conf="b"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->other"/>
+                    <dependency name="mymodule2" rev="1.2" conf="*->@"/>
+                    <dependency name="mymodule2" rev="1.2" conf="c->other"/>
+                    <dependency name="mymodule2" rev="1.2" conf="a->"/>
+                </dependencies>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        def dependency1 = descriptor.dependencies[0]
+        dependency1.moduleConfigurations == ["a"]
+        dependency1.getDependencyConfigurations("a") == ["a1"]
+        dependency1.getDependencyConfigurations("a", "requested") == ["a1"]
+
+        def dependency2 = descriptor.dependencies[1]
+        dependency2.moduleConfigurations == ["a"]
+        dependency2.getDependencyConfigurations("a") == ["a1"]
+        dependency2.getDependencyConfigurations("a", "requested") == ["a1"]
+
+        def dependency3 = descriptor.dependencies[2]
+        dependency3.moduleConfigurations == ["a"]
+        dependency3.getDependencyConfigurations("a") == ["a1"]
+        dependency3.getDependencyConfigurations("a", "requested") == ["a1"]
+
+        def dependency4 = descriptor.dependencies[3]
+        dependency4.moduleConfigurations == ["b"]
+        dependency4.getDependencyConfigurations("b") == ["b1", "b2"]
+        dependency4.getDependencyConfigurations("b", "requested") == ["b1", "b2"]
+
+        def dependency5 = descriptor.dependencies[4]
+        dependency5.moduleConfigurations == ["a"]
+        dependency5.getDependencyConfigurations("a") == ["other"]
+        dependency5.getDependencyConfigurations("a", "requested") == ["other"]
+
+        def dependency6 = descriptor.dependencies[5]
+        dependency6.moduleConfigurations == ["*"]
+        dependency6.getDependencyConfigurations("a") == ["a"]
+        dependency6.getDependencyConfigurations("a", "requested") == ["a"]
+
+        def dependency7 = descriptor.dependencies[6]
+        dependency7.moduleConfigurations == ["c"]
+        dependency7.getDependencyConfigurations("c") == ["other"]
+        dependency7.getDependencyConfigurations("c", "requested") == ["other"]
+
+        def dependency8 = descriptor.dependencies[7]
+        dependency8.moduleConfigurations == ["a"]
+        dependency8.getDependencyConfigurations("a") == ["a1"]
+        dependency8.getDependencyConfigurations("a", "requested") == ["a1"]
+    }
+
+    def "parses artifact config mappings"() {
+        given:
+        def file = temporaryFolder.createFile("ivy.xml")
+        file.text = """
+           <ivy-module version="2.0">
+                <info organisation="myorg"
+                      module="mymodule"
+                      revision="myrev">
+                </info>
+                <configurations>
+                    <conf name="a" />
+                    <conf name="b" />
+                    <conf name="c" extends="a"/>
+                    <conf name="d" />
+                </configurations>
+                <publications>
+                    <artifact/>
+                    <artifact name='art2' type='type' ext='ext' conf='*'/>
+                    <artifact name='art3' type='type2' conf='a, b  '/>
+                </publications>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        descriptor.allArtifacts.length == 3
+        descriptor.allArtifacts[0].configurations == ['a', 'b', 'c', 'd']
+        descriptor.allArtifacts[1].configurations == ['a', 'b', 'c', 'd']
+        descriptor.allArtifacts[2].configurations == ['a', 'b']
+
+        and:
+        descriptor.getArtifacts("a")*.name == ['mymodule', 'art2', 'art3']
+        descriptor.getArtifacts("a")*.type == ['jar', 'type', 'type2']
+        descriptor.getArtifacts("a")*.ext == ['jar', 'ext', 'type2']
+
+        and:
+        descriptor.getArtifacts("b")*.name == ['mymodule', 'art2', 'art3']
+        descriptor.getArtifacts("c")*.name == ['mymodule', 'art2']
+        descriptor.getArtifacts("d")*.name == ['mymodule', 'art2']
+    }
+
+    def "accumulates configurations if the same artifact listed more than once"() {
+        given:
+        def file = temporaryFolder.createFile("ivy.xml")
+        file.text = """
+           <ivy-module version="2.0">
+                <info organisation="myorg" module="mymodule" revision="myrev"/>
+                <configurations><conf name="a"/><conf name="b"/><conf name="c"/><conf name="d"/><conf name="e"/></configurations>
+                <publications>
+                    <artifact name='art' type='type' ext='ext' conf='a,b'/>
+                    <artifact name='art' type='type' ext='ext' conf='b,c'/>
+                </publications>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        descriptor.allArtifacts.length == 1
+        descriptor.allArtifacts[0].configurations == ['a', 'b', 'c']
+    }
+
+    def "parses extra attributes and extra info"() {
+        given:
+        def file = temporaryFolder.createFile("ivy.xml")
+        file.text = """
+           <ivy-module version="2.0" xmlns:b="namespace-b" xmlns:c="namespace-c">
+                <info organisation="myorg"
+                      module="mymodule"
+                      revision="myrev"
+                      b:a="1"
+                      b:b="2"
+                      c:a="3">
+                    <b:a>info 1</b:a>
+                    <c:a>info 2</c:a>
+                </info>
+            </ivy-module>
+        """
+
+        when:
+        def descriptor = parser.parseMetaData(parseContext, file, true).descriptor
+
+        then:
+        descriptor.moduleRevisionId.qualifiedExtraAttributes.size() == 3
+        descriptor.moduleRevisionId.qualifiedExtraAttributes['b:a'] == "1"
+        descriptor.moduleRevisionId.qualifiedExtraAttributes['b:b'] == "2"
+        descriptor.moduleRevisionId.qualifiedExtraAttributes['c:a'] == "3"
+        descriptor.extraInfo.size() == 2
+        descriptor.extraInfo[new NamespaceId("namespace-b", "a")] == "info 1"
+        descriptor.extraInfo[new NamespaceId("namespace-c", "a")] == "info 2"
+    }
+
+    def verifyFullDependencies(DependencyDescriptor[] dependencies) {
+        // no conf def => equivalent to *->*
+        DependencyDescriptor dd = getDependency(dependencies, "mymodule2")
+        assertNotNull(dd)
+        assertEquals("myorg", dd.getDependencyId().getOrganisation())
+        assertEquals("2.0", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["*"], Arrays.asList(dd.getModuleConfigurations()))
+        assertEquals(["*"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals(["*"], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+        assertFalse(dd.isChanging())
+        assertTrue(dd.isTransitive())
+
+        // changing = true
+        dd = getDependency(dependencies, "mymodule3")
+        assertNotNull(dd)
+        assertTrue(getDependency(dependencies, "mymodule3").isChanging())
+        assertFalse(getDependency(dependencies, "mymodule3").isTransitive())
+        // conf="myconf1" => equivalent to myconf1->myconf1
+        dd = getDependency(dependencies, "yourmodule1")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("trunk", dd.getDependencyRevisionId().getBranch())
+        assertEquals("1.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals("branch1", dd.getDynamicConstraintDependencyRevisionId().getBranch())
+        assertEquals("1+", dd.getDynamicConstraintDependencyRevisionId().getRevision())
+        assertEquals("yourorg#yourmodule1#branch1;1+", dd.getDynamicConstraintDependencyRevisionId().toString())
+
+        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
+        assertEquals(["myconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        // conf="myconf1->yourconf1"
+        dd = getDependency(dependencies, "yourmodule2")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("2+", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
+        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        // conf="myconf1->yourconf1, yourconf2"
+        dd = getDependency(dependencies, "yourmodule3")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("3.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["myconf1"], Arrays.asList(dd.getModuleConfigurations()))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf2", "myconf3", "myconf4"].toArray(new String[3]))))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        // conf="myconf1, myconf2->yourconf1, yourconf2"
+        dd = getDependency(dependencies, "yourmodule4")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("4.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(new HashSet(["myconf1", "myconf2"]), new HashSet(
+                Arrays.asList(dd.getModuleConfigurations())))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd
+                .getDependencyConfigurations("myconf1")))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd
+                .getDependencyConfigurations("myconf2")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        // conf="myconf1->yourconf1myconf2->yourconf1, yourconf2"
+        dd = getDependency(dependencies, "yourmodule5")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("5.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["myconf1", "myconf2"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        // conf="*->@"
+        dd = getDependency(dependencies, "yourmodule11")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("11.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["*"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertEquals(["myconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals(["myconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
+        assertEquals(["myconf3"], Arrays.asList(dd.getDependencyConfigurations("myconf3")))
+        assertEquals(["myconf4"], Arrays.asList(dd.getDependencyConfigurations("myconf4")))
+
+        dd = getDependency(dependencies, "yourmodule6")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("latest.integration", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["myconf1", "myconf2"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        dd = getDependency(dependencies, "yourmodule7")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("7.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(new HashSet(["myconf1", "myconf2"]), new HashSet(
+                Arrays.asList(dd.getModuleConfigurations())))
+        assertEquals(["yourconf1"], Arrays.asList(dd.getDependencyConfigurations("myconf1")))
+        assertEquals(["yourconf1", "yourconf2"], Arrays.asList(dd.getDependencyConfigurations("myconf2")))
+        assertEquals([], Arrays.asList(dd.getDependencyConfigurations(["myconf3", "myconf4"])))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1", "myconf2", "myconf3", "myconf4"], [])
+
+        dd = getDependency(dependencies, "yourmodule8")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("8.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["*"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertDependencyArtifacts(dd, ["myconf1"], ["yourartifact8-1", "yourartifact8-2"])
+        assertDependencyArtifacts(dd, ["myconf2"], ["yourartifact8-1", "yourartifact8-2"])
+        assertDependencyArtifacts(dd, ["myconf3"], ["yourartifact8-1", "yourartifact8-2"])
+        assertDependencyArtifacts(dd, ["myconf4"], ["yourartifact8-1", "yourartifact8-2"])
+        dd = getDependency(dependencies, "yourmodule9")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("9.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(["myconf1", "myconf2", "myconf3"] as Set, new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertDependencyArtifacts(dd, ["myconf1"], ["yourartifact9-1"])
+        assertDependencyArtifacts(dd, ["myconf2"], ["yourartifact9-1", "yourartifact9-2"])
+        assertDependencyArtifacts(dd, ["myconf3"], ["yourartifact9-2"])
+        assertDependencyArtifacts(dd, ["myconf4"], [])
+        assertDependencyArtifactExcludeRules(dd, ["myconf1"], [])
+        assertDependencyArtifactExcludeRules(dd, ["myconf2"], [])
+        assertDependencyArtifactExcludeRules(dd, ["myconf3"], [])
+        assertDependencyArtifactExcludeRules(dd, ["myconf4"], [])
+
+        dd = getDependency(dependencies, "yourmodule10")
+        assertNotNull(dd)
+        assertEquals("yourorg", dd.getDependencyId().getOrganisation())
+        assertEquals("10.1", dd.getDependencyRevisionId().getRevision())
+        assertEquals(new HashSet(["*"]), new HashSet(Arrays.asList(dd.getModuleConfigurations())))
+        assertDependencyArtifactIncludeRules(dd, ["myconf1"], ["your.*", PatternMatcher.ANY_EXPRESSION])
+        assertDependencyArtifactIncludeRules(dd, ["myconf2"], ["your.*", PatternMatcher.ANY_EXPRESSION])
+        assertDependencyArtifactIncludeRules(dd, ["myconf3"], ["your.*", PatternMatcher.ANY_EXPRESSION])
+        assertDependencyArtifactIncludeRules(dd, ["myconf4"], ["your.*", PatternMatcher.ANY_EXPRESSION])
+        assertDependencyArtifactExcludeRules(dd, ["myconf1"], ["toexclude"])
+        assertDependencyArtifactExcludeRules(dd, ["myconf2"], ["toexclude"])
+        assertDependencyArtifactExcludeRules(dd, ["myconf3"], ["toexclude"])
+        assertDependencyArtifactExcludeRules(dd, ["myconf4"], ["toexclude"])
+        true
+    }
+
+    void assertDependencyArtifactExcludeRules(DependencyDescriptor dd, List<String> confs,
+                                              List<String> artifactsNames) {
+        ExcludeRule[] rules = dd.getExcludeRules(confs.toArray(new String[confs.size()]))
+        assertNotNull(rules)
+        assertEquals(artifactsNames.size(), rules.length)
+        for (String artifactName : artifactsNames) {
+            boolean found = false
+            for (int j = 0; j < rules.length; j++) {
+                assertNotNull(rules[j])
+                if (rules[j].getId().getName().equals(artifactName)) {
+                    found = true
+                    break
+                }
+            }
+            assertTrue("dependency exclude not found: " + artifactName, found)
+        }
+    }
+
+    protected void assertDependencyArtifactIncludeRules(DependencyDescriptor dd, List<String> confs,
+                                                        List<String> artifactsNames) {
+        IncludeRule[] dads = dd.getIncludeRules(confs.toArray(new String[confs.size()]))
+        assertNotNull(dads)
+        assertEquals(artifactsNames.size(), dads.length)
+        for (String artifactName : artifactsNames) {
+            boolean found = false
+            for (int j = 0; j < dads.length; j++) {
+                assertNotNull(dads[j])
+                if (dads[j].getId().getName().equals(artifactName)) {
+                    found = true
+                    break
+                }
+            }
+            assertTrue("dependency include not found: " + artifactName, found)
+        }
+    }
+
+    protected void assertArtifacts(Artifact[] artifacts, String[] artifactsNames) {
+        assertNotNull(artifacts)
+        assertEquals(artifactsNames.length, artifacts.length)
+        for (int i = 0; i < artifactsNames.length; i++) {
+            boolean found = false
+            for (int j = 0; j < artifacts.length; j++) {
+                assertNotNull(artifacts[j])
+                if (artifacts[j].getName().equals(artifactsNames[i])) {
+                    found = true
+                    break
+                }
+            }
+            assertTrue("artifact not found: " + artifactsNames[i], found)
+        }
+    }
+
+    protected void assertConf(ModuleDescriptor md, String name, String desc, Configuration.Visibility visibility,
+                              String[] exts) {
+        Configuration conf = md.getConfiguration(name)
+        assertNotNull("configuration not found: " + name, conf)
+        assertEquals(name, conf.getName())
+        assertEquals(desc, conf.getDescription())
+        assertEquals(visibility, conf.getVisibility())
+        assertEquals(Arrays.asList(exts), Arrays.asList(conf.getExtends()))
+    }
+
+    protected DependencyDescriptor getDependency(DependencyDescriptor[] dependencies, String name) {
+        for (int i = 0; i < dependencies.length; i++) {
+            assertNotNull(dependencies[i])
+            assertNotNull(dependencies[i].getDependencyId())
+            if (name.equals(dependencies[i].getDependencyId().getName())) {
+                return dependencies[i]
+            }
+        }
+        return null
+    }
+
+    protected void assertDependencyArtifacts(DependencyDescriptor dd, List<String> confs,
+                                             List<String> artifactsNames) {
+        DependencyArtifactDescriptor[] dads = dd.getDependencyArtifacts(confs.toArray(new String[confs.size()]));
+        assertNotNull(dads);
+        assertEquals(artifactsNames.size(), dads.length);
+        for (String artifactName : artifactsNames) {
+            boolean found = false;
+            for (int j = 0; j < dads.length; j++) {
+                assertNotNull(dads[j]);
+                if (dads[j].getName().equals(artifactName)) {
+                    found = true;
+                    break;
+                }
+            }
+            assertTrue("dependency artifact not found: " + artifactName, found);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy
new file mode 100644
index 0000000..d220d08
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderProfileTest.groovy
@@ -0,0 +1,2364 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.parser.data.MavenDependencyKey
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomProfile
+import org.gradle.internal.id.UUIDGenerator
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class PomReaderProfileTest extends AbstractPomReaderTest {
+    def "parse POM without active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation/>
+        </profile>
+        <profile>
+            <id>profile-3</id>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.parseActivePomProfiles().size() == 0
+    }
+
+    def "parse POM with multiple active profiles having the same ID"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.parseActivePomProfiles().size() == 4
+    }
+
+    def "parse POM with a mix of active and inactive profiles"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <prop2>myproperty2</prop2>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-three</groupId>
+                        <artifactId>artifact-three</artifactId>
+                        <version>version-three</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+        <profile>
+            <id>profile-3</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <prop3>myproperty3</prop3>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-four</groupId>
+                        <artifactId>artifact-four</artifactId>
+                        <version>version-four</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.parseActivePomProfiles().size() == 1
+        pomReader.properties.size() == 5
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.properties.containsKey('prop3')
+        pomReader.properties['prop2'] == 'myproperty2'
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-four', 'artifact-four', 'jar', null))
+        pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null))
+    }
+
+    def "cannot use POM property to set profile ID"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <properties>
+        <activate.id>profile-1</activate.id>
+    </properties>
+    <profiles>
+        <profile>
+            <id>\${activate.id}</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].id == '${activate.id}'
+    }
+
+    def "cannot use POM property to control profile activation"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <properties>
+        <activate.profile>true</activate.profile>
+    </properties>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>\${activate.profile}</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('groupId.prop')
+        !pomReader.properties.containsKey('artifactId.prop')
+        !pomReader.properties.containsKey('version.prop')
+    }
+
+    def "parse POM with active profile providing properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        PomProfile pomProfile = activePomProfiles[0]
+        pomProfile
+        pomProfile.id == 'profile-1'
+        pomProfile.properties.size() == 3
+        pomProfile.properties['groupId.prop'] == 'group-two'
+        pomProfile.properties['artifactId.prop'] == 'artifact-two'
+        pomProfile.properties['version.prop'] == 'version-two'
+        pomReader.properties.size() == 7
+        pomReader.properties['groupId.prop'] == 'group-two'
+        pomReader.properties['artifactId.prop'] == 'artifact-two'
+        pomReader.properties['version.prop'] == 'version-two'
+    }
+
+    def "parse POM with multiple active profile providing same properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.parseActivePomProfiles().size() == 2
+        pomReader.properties.size() == 7
+        pomReader.properties['groupId.prop'] == 'group-two'
+        pomReader.properties['artifactId.prop'] == 'artifact-two'
+        pomReader.properties['version.prop'] == 'version-two'
+    }
+
+    def "get dependencies with custom properties of active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    def "custom properties from last active profile determines dependency coordinates"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-three</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "properties from an active profile override existing POM properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <properties>
+        <groupId.prop>group-two</groupId.prop>
+        <artifactId.prop>artifact-two</artifactId.prop>
+        <version.prop>version-two</version.prop>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-three</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "multiple active profiles can determine different dependency coordinates"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+        <dependency>
+            <groupId>\${otherGroupId.prop}</groupId>
+            <artifactId>\${otherArtifactId.prop}</artifactId>
+            <version>\${otherVersion.prop}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <otherGroupId.prop>group-three</otherGroupId.prop>
+                <otherArtifactId.prop>artifact-three</otherArtifactId.prop>
+                <otherVersion.prop>version-three</otherVersion.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyDep1 = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyDep2 = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 2
+        assertResolvedPomDependency(keyDep1, 'version-two')
+        assertResolvedPomDependency(keyDep2, 'version-three')
+    }
+
+    def "get dependencies management without properties from active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].getDependencyMgts().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+    }
+
+    def "get dependencies management with properties from active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>\${groupId.prop}</groupId>
+                        <artifactId>\${artifactId.prop}</artifactId>
+                        <version>\${version.prop}</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].getDependencyMgts().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+    }
+
+    def "finds dependency default with properties defined in main body of POM"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <properties>
+        <groupId.prop>group-two</groupId.prop>
+        <artifactId.prop>artifact-two</artifactId.prop>
+        <version.prop>version-two</version.prop>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>\${groupId.prop}</groupId>
+                        <artifactId>\${artifactId.prop}</artifactId>
+                        <version>\${version.prop}</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
+    }
+
+    def "finds dependency default if declared in active profile"() {
+        when:
+        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>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-three</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        !pomReader.findDependencyDefaults(keyGroupThree)
+    }
+
+    def "uses dependency default from active profile over POM dependency default"() {
+        when:
+        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>version-two</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-three</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-three')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+    }
+
+    def "finds dependency default if declared in multiple active profiles"() {
+        when:
+        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>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-three</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-three</artifactId>
+                        <version>version-three</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 2
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        assertResolvedPomDependencyManagement(keyGroupThree, 'version-three')
+        pomReader.findDependencyDefaults(keyGroupThree) == pomReader.dependencyMgt[keyGroupThree]
+    }
+
+    def "finds dependency default if declared in parent POM active profile"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-four</groupId>
+            <artifactId>artifact-four</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-five</groupId>
+            <artifactId>artifact-five</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        !pomReader.findDependencyDefaults(keyGroupThree)
+    }
+
+    def "uses child properties over parent properties if defined in active profile with the different values"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-three</groupId.prop>
+                <artifactId.prop>artifact-three</artifactId.prop>
+                <version.prop>version-three</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-four</groupId.prop>
+                <artifactId.prop>artifact-four</artifactId.prop>
+                <version.prop>version-four</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey key = new MavenDependencyKey('group-four', 'artifact-four', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-four')
+    }
+
+    def "gets dependencies declared in a single active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 2
+        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
+        assertResolvedPomDependency(keyArtifactThree, 'version-three')
+    }
+
+    def "uses last declaration of dependency with same groupId and artifactId in a single active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "gets dependencies declared in a multiple active profiles"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-3</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-four</groupId>
+                    <artifactId>artifact-four</artifactId>
+                    <version>version-four</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+        MavenDependencyKey keyArtifactFour = new MavenDependencyKey('group-four', 'artifact-four', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 3
+        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
+        assertResolvedPomDependency(keyArtifactThree, 'version-three')
+        assertResolvedPomDependency(keyArtifactFour, 'version-four')
+    }
+
+    def "uses last declaration of dependency with same groupId and artifactId in multiple active profiles"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "gets dependency with provided properties declared in active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+            <dependencies>
+                <dependency>
+                    <groupId>\${groupId.prop}</groupId>
+                    <artifactId>\${artifactId.prop}</artifactId>
+                    <version>\${version.prop}</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    def "gets dependency in active profile with provided properties in POM main body"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <properties>
+        <groupId.prop>group-two</groupId.prop>
+        <artifactId.prop>artifact-two</artifactId.prop>
+        <version.prop>version-two</version.prop>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>\${groupId.prop}</groupId>
+                    <artifactId>\${artifactId.prop}</artifactId>
+                    <version>\${version.prop}</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    def "gets dependency with provided defaults declared in active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
+    }
+
+    def "gets dependency with provided defaults declared in POM main body"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+        pomReader.findDependencyDefaults(key) == pomReader.dependencyMgt[key]
+    }
+
+    def "uses active profile dependency over dependency with same groupId and artifactId declared in POM main body"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+
+    <dependency>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </dependency>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "uses dependency if declared in parent POM active profile"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    def "uses dependency if declared in parent and child POM active profile"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <profiles>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey keyArtifactTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyArtifactThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 2
+        assertResolvedPomDependency(keyArtifactTwo, 'version-two')
+        assertResolvedPomDependency(keyArtifactThree, 'version-three')
+    }
+
+    def "uses child dependency over parent dependency in POM active profile"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <profiles>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>group-two</groupId>
+                    <artifactId>artifact-two</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-three')
+    }
+
+    def "cannot use POM property to control profile property activation"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <properties>
+        <activate.profile>BLUE</activate.profile>
+    </properties>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>\${activate.profile}</value>
+                </property>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('groupId.prop')
+        !pomReader.properties.containsKey('artifactId.prop')
+        !pomReader.properties.containsKey('version.prop')
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "does not activate profile for a matching system property value"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "does not activate multiple profiles for a matching same system property with the same value"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <properties>
+                <prop2>myproperty2</prop2>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.properties.containsKey('prop2')
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "does not activate multiple profiles for a matching different system property with different values"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        String someOtherPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+        System.properties[someOtherPropertyName] = 'GREEN'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <property>
+                    <name>${someOtherPropertyName}</name>
+                    <value>GREEN</value>
+                </property>
+            </activation>
+            <properties>
+                <prop2>myproperty2</prop2>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.properties.containsKey('prop2')
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+        System.clearProperty(someOtherPropertyName)
+    }
+
+    def "does not activate profile if system property value is not matching"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'GREEN'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                    <value>BLUE</value>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        !pomReader.dependencies.containsKey(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null))
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "activates profile by the absence of a property"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].id == 'profile-1'
+        pomReader.properties['prop1'] == 'myproperty1'
+        pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        assertResolvedPomDependency(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null), 'version-three')
+    }
+
+    def "activates profile for negated property if system property is provided"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].id == 'profile-1'
+        pomReader.properties['prop1'] == 'myproperty1'
+        pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        assertResolvedPomDependency(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null), 'version-three')
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "does not activate profile if property value is not declared and system property is not set"() {
+        given:
+        String customPropertyName = new UUIDGenerator().generateId()
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        !pomReader.dependencies.containsKey(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null))
+    }
+
+    def "does not activate profile if property value is not declared and system property is set with any value"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+        System.properties[customPropertyName] = 'BLUE'
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 0
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null))
+        !pomReader.dependencies.containsKey(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null))
+
+        cleanup:
+        System.clearProperty(customPropertyName)
+    }
+
+    def "system property activation removes all other profiles that are active by default"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <prop1>myproperty1</prop1>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-one</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-one</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop2>myproperty2</prop2>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-two</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-3</id>
+            <activation>
+                <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <prop3>myproperty3</prop3>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-three</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-three</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>profile-4</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <prop4>myproperty4</prop4>
+            </properties>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-four</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+            <dependencies>
+                <dependency>
+                    <groupId>group-three</groupId>
+                    <artifactId>artifact-three</artifactId>
+                    <version>version-four</version>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 2
+        activePomProfiles[0].id == 'profile-2'
+        activePomProfiles[1].id == 'profile-3'
+        !pomReader.properties.containsKey('prop1')
+        !pomReader.properties.containsKey('prop4')
+        pomReader.properties['prop2'] == 'myproperty2'
+        pomReader.properties['prop3'] == 'myproperty3'
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-one', 'jar', null))
+        !pomReader.dependencyMgt.containsKey(new MavenDependencyKey('group-two', 'artifact-four', 'jar', null))
+        assertResolvedPomDependencyManagement(new MavenDependencyKey('group-two', 'artifact-two', 'jar', null), 'version-three')
+        assertResolvedPomDependency(new MavenDependencyKey('group-three', 'artifact-three', 'jar', null), 'version-three')
+    }
+
+    def "parse POM with multiple active profiles activated by absence of system property"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                 <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+        <profile>
+            <id>profile-2</id>
+            <activation>
+                 <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <properties>
+                <groupId.prop>group-two</groupId.prop>
+                <artifactId.prop>artifact-two</artifactId.prop>
+                <version.prop>version-two</version.prop>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.parseActivePomProfiles().size() == 2
+        pomReader.properties.size() == 7
+        pomReader.properties['groupId.prop'] == 'group-two'
+        pomReader.properties['artifactId.prop'] == 'artifact-two'
+        pomReader.properties['version.prop'] == 'version-two'
+    }
+
+    def "finds dependency default if declared in parent POM profile activated by absence of system property"() {
+        setup:
+        String customPropertyName = new UUIDGenerator().generateId()
+
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <property>
+                    <name>!${customPropertyName}</name>
+                </property>
+            </activation>
+            <dependencyManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>group-two</groupId>
+                        <artifactId>artifact-two</artifactId>
+                        <version>version-two</version>
+                    </dependency>
+                </dependencies>
+            </dependencyManagement>
+        </profile>
+    </profiles>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-four</groupId>
+            <artifactId>artifact-four</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-five</groupId>
+            <artifactId>artifact-five</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        !pomReader.findDependencyDefaults(keyGroupThree)
+    }
+
+    @Issue("GRADLE-3074")
+    @Unroll
+    def "can define #packaging packaging in active profile"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+
+    <profiles>
+        <profile>
+            <id>profile-1</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <properties>
+                <package.type>$packaging</package.type>
+            </properties>
+        </profile>
+    </profiles>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == packaging
+        List<PomProfile> activePomProfiles = pomReader.parseActivePomProfiles()
+        activePomProfiles.size() == 1
+        activePomProfiles[0].properties.size() == 1
+        activePomProfiles[0].properties.containsKey('package.type')
+        activePomProfiles[0].properties['package.type'] == packaging
+
+        where:
+        packaging << ['pom', 'jar', 'ejb', 'war', 'ear', 'rar', 'par']
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy
new file mode 100644
index 0000000..65e045b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/PomReaderTest.groovy
@@ -0,0 +1,851 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.License
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey
+import org.xml.sax.SAXParseException
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class PomReaderTest extends AbstractPomReaderTest {
+    def "parse POM with invalid XML"() {
+        when:
+        pomFile << """
+<projectx>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <description>The first test artifact</description>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        thrown(MetaDataParseException)
+    }
+
+    def "parse malformed POM"() {
+        when:
+        pomFile << """
+<someothertag>
+    <project>
+        <modelVersion>4.0.0</modelVersion>
+        <groupId>group-one</groupId>
+        <artifactId>artifact-one</artifactId>
+        <version>version-one</version>
+        <name>Test Artifact One</name>
+        <description>The first test artifact</description>
+    </project>
+</someothertag>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        Throwable t = thrown(SAXParseException)
+        t.message == 'project must be the root tag'
+    }
+
+    def "parse simple POM"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <name>Test Artifact One</name>
+    <description>The first test artifact</description>
+    <url>http://www.myproject.com</url>
+    <licenses>
+        <license>
+            <name>The Apache Software License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+        </license>
+    </licenses>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.description == 'The first test artifact'
+        pomReader.homePage == 'http://www.myproject.com'
+        pomReader.packaging == 'jar'
+        pomReader.licenses.size() == 1
+        pomReader.licenses[0].name == 'The Apache Software License, Version 2.0'
+        pomReader.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.properties.size() == 4
+        pomReader.properties['parent.version'] == 'version-one'
+        pomReader.properties['parent.groupId'] == 'group-one'
+        pomReader.properties['project.parent.version'] == 'version-one'
+        pomReader.properties['project.parent.groupId'] == 'group-one'
+        pomReader.relocation == null
+    }
+
+    def "use custom properties in POM project coordinates"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>\${groupId.prop}</groupId>
+    <artifactId>\${artifactId.prop}</artifactId>
+    <version>\${version.prop}</version>
+    <name>Test Artifact One</name>
+    <description>The first test artifact</description>
+    <properties>
+        <some.prop1>test1</some.prop1>
+        <some.prop2>test2</some.prop2>
+        <groupId.prop>group-one</groupId.prop>
+        <artifactId.prop>artifact-one</artifactId.prop>
+        <version.prop>version-one</version.prop>
+    </properties>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.description == 'The first test artifact'
+        pomReader.packaging == 'jar'
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 5
+        pomReader.pomProperties.containsKey('some.prop1')
+        pomReader.pomProperties.containsKey('some.prop2')
+        pomReader.properties.size() == 9
+        pomReader.properties.containsKey('some.prop1')
+        pomReader.properties.containsKey('some.prop2')
+    }
+
+    def "get dependencies without custom properties"() {
+        when:
+        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>
+            <version>version-two</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    def "get dependencies with custom properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <properties>
+        <groupId.prop>group-two</groupId.prop>
+        <artifactId.prop>artifact-two</artifactId.prop>
+        <version.prop>version-two</version.prop>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>\${groupId.prop}</groupId>
+            <artifactId>\${artifactId.prop}</artifactId>
+            <version>\${version.prop}</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-two')
+    }
+
+    @Issue("GRADLE-2931")
+    def "picks version of last dependency defined by artifact ID, group ID, type and classifier"() {
+        when:
+        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>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-two</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-three</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-four</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
+
+        then:
+        pomReader.getDependencies().size() == 1
+        assertResolvedPomDependency(key, 'version-four')
+    }
+
+    @Issue("GRADLE-2931")
+    def "can declare multiple dependencies with same artifact ID and group ID but different type and classifier"() {
+        when:
+        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>
+            <type>jar</type>
+            <classifier>myjar</classifier>
+            <version>version-two</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>test-jar</type>
+            <classifier>test</classifier>
+            <version>version-three</version>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <type>ejb-client</type>
+            <classifier>client</classifier>
+            <version>version-four</version>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey dependencyJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
+        MavenDependencyKey dependencyTestJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'test-jar', 'test')
+        MavenDependencyKey dependencyEjbClientKey = new MavenDependencyKey('group-two', 'artifact-two', 'ejb-client', 'client')
+
+        then:
+        pomReader.getDependencies().size() == 3
+        assertResolvedPomDependency(dependencyJarkey, 'version-two')
+        assertResolvedPomDependency(dependencyTestJarkey, 'version-three')
+        assertResolvedPomDependency(dependencyEjbClientKey, 'version-four')
+    }
+
+    def "get dependencies management without custom properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+    }
+
+    def "get dependencies management with custom properties"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <properties>
+        <groupId.prop>group-two</groupId.prop>
+        <artifactId.prop>artifact-two</artifactId.prop>
+        <version.prop>version-two</version.prop>
+    </properties>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>\${groupId.prop}</groupId>
+                <artifactId>\${artifactId.prop}</artifactId>
+                <version>\${version.prop}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-two')
+    }
+
+    def "picks version of last dependency management defined by artifact ID, group ID, type and classifier"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>jar</type>
+                <classifier>myjar</classifier>
+                <version>version-two</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>jar</type>
+                <classifier>myjar</classifier>
+                <version>version-three</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>jar</type>
+                <classifier>myjar</classifier>
+                <version>version-four</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey key = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(key, 'version-four')
+    }
+
+    def "can declare multiple dependency managements with same artifact ID and group ID but different type and classifier"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>jar</type>
+                <classifier>myjar</classifier>
+                <version>version-two</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>test-jar</type>
+                <classifier>test</classifier>
+                <version>version-three</version>
+            </dependency>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <type>ejb-client</type>
+                <classifier>client</classifier>
+                <version>version-four</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey dependencyJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'jar', 'myjar')
+        MavenDependencyKey dependencyTestJarkey = new MavenDependencyKey('group-two', 'artifact-two', 'test-jar', 'test')
+        MavenDependencyKey dependencyEjbClientKey = new MavenDependencyKey('group-two', 'artifact-two', 'ejb-client', 'client')
+
+        then:
+        pomReader.getDependencyMgt().size() == 3
+        assertResolvedPomDependencyManagement(dependencyJarkey, 'version-two')
+        assertResolvedPomDependencyManagement(dependencyTestJarkey, 'version-three')
+        assertResolvedPomDependencyManagement(dependencyEjbClientKey, 'version-four')
+    }
+
+    def "parse POM with parent POM"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>artifact-one</artifactId>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-two'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-two'
+        pomReader.parentGroupId == 'group-two'
+        pomReader.parentArtifactId == 'artifact-two'
+        pomReader.parentVersion == 'version-two'
+    }
+
+    def "Parse minimal POM and resolve GAV"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.resolveGAV()
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == 'jar'
+        pomReader.homePage == ''
+        pomReader.description == ''
+        pomReader.licenses == new License[0]
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.properties.size() == 13
+        pomReader.properties['parent.version'] == 'version-one'
+        pomReader.properties['parent.groupId'] == 'group-one'
+        pomReader.properties['project.parent.version'] == 'version-one'
+        pomReader.properties['project.parent.groupId'] == 'group-one'
+        pomReader.properties['project.groupId'] == 'group-one'
+        pomReader.properties['pom.groupId'] == 'group-one'
+        pomReader.properties['groupId'] == 'group-one'
+        pomReader.properties['project.artifactId'] == 'artifact-one'
+        pomReader.properties['pom.artifactId'] == 'artifact-one'
+        pomReader.properties['artifactId'] == 'artifact-one'
+        pomReader.properties['project.version'] == 'version-one'
+        pomReader.properties['pom.version'] == 'version-one'
+        pomReader.properties['version'] == 'version-one'
+    }
+
+    def "Parse relocated POM without provided coordinates"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <distributionManagement>
+        <relocation>
+        </relocation>
+    </distributionManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == 'jar'
+        pomReader.homePage == ''
+        pomReader.description == ''
+        pomReader.licenses == new License[0]
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.relocation != null
+        pomReader.relocation == IvyUtil.createModuleRevisionId('group-one', 'artifact-one', 'version-one')
+    }
+
+    def "Parse relocated POM with provided group ID"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <distributionManagement>
+        <relocation>
+            <groupId>group-two</groupId>
+        </relocation>
+    </distributionManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == 'jar'
+        pomReader.homePage == ''
+        pomReader.description == ''
+        pomReader.licenses == new License[0]
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.relocation != null
+        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-one', 'version-one')
+    }
+
+    def "Parse relocated POM with provided group ID and artifact ID"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <distributionManagement>
+        <relocation>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </relocation>
+    </distributionManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == 'jar'
+        pomReader.homePage == ''
+        pomReader.description == ''
+        pomReader.licenses == new License[0]
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.relocation != null
+        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-two', 'version-one')
+    }
+
+    def "Parse relocated POM with all provided coordinates"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <distributionManagement>
+        <relocation>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+            <version>version-two</version>
+        </relocation>
+    </distributionManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == 'jar'
+        pomReader.homePage == ''
+        pomReader.description == ''
+        pomReader.licenses == new License[0]
+        !pomReader.hasParent()
+        pomReader.pomProperties.size() == 0
+        pomReader.relocation != null
+        pomReader.relocation == IvyUtil.createModuleRevisionId('group-two', 'artifact-two', 'version-two')
+    }
+
+    @Issue("GRADLE-2938")
+    def "uses default type for dependency if not declared"() {
+        when:
+        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>
+            <version>version-two</version>
+        </dependency>
+        <dependency>
+            <groupId>group-three</groupId>
+            <artifactId>artifact-three</artifactId>
+            <version>version-three</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>group-four</groupId>
+            <artifactId>artifact-four</artifactId>
+            <version>version-four</version>
+            <type>ejb-client</type>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+        MavenDependencyKey keyGroupFour = new MavenDependencyKey('group-four', 'artifact-four', 'ejb-client', null)
+
+        then:
+        pomReader.getDependencies().size() == 3
+        assertResolvedPomDependency(keyGroupTwo, 'version-two')
+        assertResolvedPomDependency(keyGroupThree, 'version-three')
+        assertResolvedPomDependency(keyGroupFour, 'version-four')
+    }
+
+    @Issue("GRADLE-2938")
+    def "uses default type for dependency management if not declared"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+            </dependency>
+            <dependency>
+                <groupId>group-three</groupId>
+                <artifactId>artifact-three</artifactId>
+                <version>version-three</version>
+                <type>jar</type>
+            </dependency>
+            <dependency>
+                <groupId>group-four</groupId>
+                <artifactId>artifact-four</artifactId>
+                <version>version-four</version>
+                <type>ejb-client</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-three', 'artifact-three', 'jar', null)
+        MavenDependencyKey keyGroupFour = new MavenDependencyKey('group-four', 'artifact-four', 'ejb-client', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 3
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        assertResolvedPomDependencyManagement(keyGroupThree, 'version-three')
+        assertResolvedPomDependencyManagement(keyGroupFour, 'version-four')
+    }
+
+    def "finds dependency default if declared in same POM"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-two</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-two</groupId>
+            <artifactId>artifact-three</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        !pomReader.findDependencyDefaults(keyGroupThree)
+    }
+
+    def "finds dependency default if declared in parent POM"() {
+        when:
+        String parentPom = """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-two</groupId>
+    <artifactId>artifact-two</artifactId>
+    <version>version-two</version>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>group-two</groupId>
+                <artifactId>artifact-two</artifactId>
+                <version>version-two</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+"""
+        PomReader parentPomReader = createPomReader('parent-pom.xml', parentPom)
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+
+    <parent>
+        <groupId>group-two</groupId>
+        <artifactId>artifact-two</artifactId>
+        <version>version-two</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>group-four</groupId>
+            <artifactId>artifact-four</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>group-five</groupId>
+            <artifactId>artifact-five</artifactId>
+        </dependency>
+    </dependencies>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+        pomReader.setPomParent(parentPomReader)
+        MavenDependencyKey keyGroupTwo = new MavenDependencyKey('group-two', 'artifact-two', 'jar', null)
+        MavenDependencyKey keyGroupThree = new MavenDependencyKey('group-two', 'artifact-three', 'jar', null)
+
+        then:
+        pomReader.getDependencyMgt().size() == 1
+        assertResolvedPomDependencyManagement(keyGroupTwo, 'version-two')
+        pomReader.findDependencyDefaults(keyGroupTwo) == pomReader.dependencyMgt[keyGroupTwo]
+        !pomReader.findDependencyDefaults(keyGroupThree)
+    }
+
+    @Issue("GRADLE-3074")
+    @Unroll
+    def "can define #packaging packaging with custom property"() {
+        when:
+        pomFile << """
+<project>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>group-one</groupId>
+    <artifactId>artifact-one</artifactId>
+    <version>version-one</version>
+    <packaging>\${package.type}</packaging>
+
+    <properties>
+        <package.type>$packaging</package.type>
+    </properties>
+</project>
+"""
+        pomReader = new PomReader(locallyAvailableExternalResource)
+
+        then:
+        pomReader.groupId == 'group-one'
+        pomReader.artifactId == 'artifact-one'
+        pomReader.version == 'version-one'
+        pomReader.packaging == packaging
+        pomReader.pomProperties.size() == 1
+        pomReader.pomProperties.containsKey('package.type')
+        pomReader.pomProperties['package.type'] == packaging
+
+        where:
+        packaging << ['pom', 'jar', 'ejb', 'war', 'ear', 'rar', 'par']
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKeyTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKeyTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKeyTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/data/MavenDependencyKeyTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelectorTest.groovy
new file mode 100644
index 0000000..8a52f9d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/AbstractVersionSelectorTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentMetadata
+import spock.lang.Specification
+
+abstract class AbstractVersionSelectorTest extends Specification {
+    abstract VersionSelector getSelector(String selector);
+
+    def accept(String s1, String s2) {
+        return getSelector(s1).accept(s2)
+    }
+
+    def accept(String s1, ComponentMetadata cmd) {
+        return getSelector(s1).accept(cmd)
+    }
+
+    def isDynamic(String s1) {
+        return getSelector(s1).isDynamic()
+    }
+
+    def requiresMetadata(String s1) {
+        return getSelector(s1).requiresMetadata()
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparatorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparatorTest.groovy
new file mode 100644
index 0000000..5bfbbb8
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionComparatorTest.groovy
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 DefaultVersionComparatorTest extends Specification {
+    def comparator = new DefaultVersionComparator()
+
+    def compare(String s1, String s2) {
+        return comparator.compare(new VersionInfo(s1), new VersionInfo(s2))
+    }
+
+    def "compares versions numerically when parts are digits"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller | larger
+        "1.0"   | "2.0"
+        "1.0"   | "1.1"
+        "1.2"   | "1.10"
+        "1.0.1" | "1.1.0"
+        "1.2"   | "1.2.3"
+        "12"    | "12.2.3"
+        "12"    | "13"
+        "1.0-1" | "1.0-2"
+        "1.0-1" | "1.0.2"
+        "1.0-1" | "1+0_2"
+    }
+
+    def "compares versions lexicographically when parts are not digits"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller     | larger
+        "1.0.a"     | "1.0.b"
+        "1.0-alpha" | "1.0-beta"
+        "1.0.alpha" | "1.0.b"
+        "alpha"     | "beta"
+    }
+
+    def "considers parts that are digits as larger than parts that are not"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller     | larger
+        "1.0-alpha" | "1.0.1"
+        "a.b.c"     | "a.b.123"
+        "a"         | "123"
+    }
+
+    def "considers a trailing part that contains no digits as smaller"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller     | larger
+        "1.0-alpha" | "1.0"
+        "1.0.a"     | "1.0"
+        "1.beta.a"  | "1.beta"
+        "a-b-c"     | "a.b"
+    }
+
+    def "gives some special treatment to 'dev', 'rc', and 'final' qualifiers"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller     | larger
+        "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-dev-1" | "1.0-xx-1"
+        "1.0-xx-1"  | "1.0-rc-1"
+        "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:
+        compare(v1, v2) == 0
+        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:
+        compare("1.0", "1_0") == 0
+        compare("1_0", "1-0") == 0
+        compare("1-0", "1+0") == 0
+        compare("1.a.2", "1a2") == 0 // number-word and word-number boundaries are considered separators
+    }
+
+    def "compares unrelated versions unequal"() {
+        expect:
+        compare("1.0", "") != 0
+        compare("1.0", "!@#%") != 0
+        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:
+        compare(larger, smaller) > 0
+        compare(smaller, larger) < 0
+
+        where:
+        larger  | smaller
+        "1.0.0" | "1.0"
+        "1.0.0" | "1"
+    }
+
+    def "does not compare versions with different capitalization equal"() {
+        expect:
+        compare(larger, smaller) > 0
+        compare(smaller, larger) < 0
+
+        where:
+        larger      | smaller
+        "1.0-alpha" | "1.0-ALPHA"
+    }
+
+    def "incorrectly compares Maven snapshot-like versions (current behaviour not necessarily desired behaviour"() {
+        expect:
+        compare(smaller, larger) < 0
+        compare(larger, smaller) > 0
+        compare(smaller, smaller) == 0
+        compare(larger, larger) == 0
+
+        where:
+        smaller                   | larger
+        "1.0-SNAPSHOT"            | "1.0"
+        "1.0"                     | "1.0-20150201.121010-123" // incorrect!
+        "1.0-20150201.121010-123" | "1.0-20150201.121010-124"
+        "1.0-20150201.121010-123" | "1.0-20150201.131010-1"
+        "1.0-SNAPSHOT"            | "1.0-20150201.131010-1" // probably not right
+        "1.0"                     | "1.1-SNAPSHOT"
+        "1.0"                     | "1.1-20150201.121010-12"
+    }
+
+    def "can compare version strings"() {
+        expect:
+        def stringComparator = comparator.asStringComparator()
+        stringComparator.compare("1.2", "1.3") < 0
+    }
+
+    def "can compare Version objects"() {
+        def v1 = Stub(Version) {
+            getParts() >> ["1", "2"]
+        }
+        def v2 = Stub(Version) {
+            getParts() >> ["1", "3"]
+        }
+
+        expect:
+        def versionComparator = comparator.asVersionComparator()
+        versionComparator.compare(v1, v2) < 0
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorSchemeTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorSchemeTest.groovy
new file mode 100644
index 0000000..e1f269e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/DefaultVersionSelectorSchemeTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 spock.lang.Specification
+
+class DefaultVersionSelectorSchemeTest extends Specification {
+    def matcher = new DefaultVersionSelectorScheme(new DefaultVersionComparator())
+
+    def "creates version range selector" () {
+        expect:
+        matcher.parseSelector(selector) instanceof VersionRangeSelector
+        
+        where:
+        selector << [
+            "[1.0,2.0]",
+            "[1.0,2.0[",
+            "]1.0,2.0]",
+            "]1.0,2.0[",
+            "[1.0,)",
+            "]1.0,)",
+            "(,2.0]",
+            "(,2.0[",
+        ]
+    }
+    
+    def "creates sub version selector" () {
+        expect:
+        matcher.parseSelector(selector) instanceof SubVersionSelector
+
+        where:
+        selector << [
+            "1+",
+            "1.2.3+"
+        ]
+    }
+
+    def "creates latest version selector" () {
+        expect:
+        matcher.parseSelector(selector) instanceof LatestVersionSelector
+
+        where:
+        selector << [
+            "latest.integration",
+            "latest.foo",
+            "latest.123"
+        ]
+    }
+
+    def "creates exact version selector as default" () {
+        expect:
+        matcher.parseSelector(selector) instanceof ExactVersionSelector
+
+        where:
+        selector << [
+            "1.0",
+            "!@#%",
+            "1",
+            "1.+.3",
+            "[1",
+            "[]",
+            "[1,2,3]",
+        ]
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelectorTest.groovy
new file mode 100644
index 0000000..0de55db
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/ExactVersionSelectorTest.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.strategy
+
+import org.gradle.api.artifacts.ComponentMetadata
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+
+class ExactVersionSelectorTest extends AbstractVersionSelectorTest {
+    def "considers selector as static"() {
+        expect:
+        !isDynamic("1.0")
+        !isDynamic("[1.0,2.0]")
+    }
+
+    def "doesn't need metadata"() {
+        expect:
+        !requiresMetadata("1.0")
+        !requiresMetadata("[1.0,2.0]")
+    }
+
+    def "accepts candidate version iff it literally matches the selector"() {
+        expect:
+        accept("", "")
+        accept("1.0", "1.0")
+        accept("2.0", "2.0")
+        accept("!@#%", "!@#%")
+        accept("hey joe", "hey joe")
+        !accept("1.0", "1.1")
+        !accept("2.0", "3.0")
+        !accept("!@#%", "%#@!")
+        !accept("hey joe", "hoe hey")
+    }
+
+    def "does not accept candidate version that differs in separator"() {
+        expect:
+        !accept("1.0", "1_0")
+        !accept("1_0", "1-0")
+        !accept("1-0", "1+0")
+        !accept("1.a.2", "1a2")
+    }
+
+    def "does not accept candidate version that has different number of trailing .0's"() {
+        expect:
+        !accept("1.0.0", "1.0")
+        !accept("1", "1.0.0")
+    }
+
+    def "does not accept candidate version that has different capitalization"() {
+        !accept("1.0-alpha", "1.0-ALPHA")
+    }
+
+    def "supports metadata-aware accept method (with same result)"() {
+        def metadata = Stub(ComponentMetadata) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        accept("1.0", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.0"           | true
+        "2.0"           | false
+    }
+
+    @Override
+    VersionSelector getSelector(String selector) {
+        return new ExactVersionSelector(selector)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelectorTest.groovy
new file mode 100644
index 0000000..be9fd06
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/LatestVersionSelectorTest.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.internal.artifacts.ivyservice.ivyresolve.strategy
+
+import org.gradle.api.artifacts.ComponentMetadata
+
+class LatestVersionSelectorTest extends AbstractVersionSelectorTest {
+
+    def "all handled selectors are dynamic"() {
+        expect:
+        isDynamic("latest.integration")
+        isDynamic("latest.foo")
+        isDynamic("latest.123")
+    }
+
+    def "always needs metadata"() {
+        expect:
+        requiresMetadata("latest.integration")
+        requiresMetadata("latest.foo")
+        requiresMetadata("latest.123")
+    }
+
+    def "only supports metadata-aware accept method"() {
+        when:
+        accept("latest.integration", "1.0")
+
+        then:
+        UnsupportedOperationException e = thrown()
+        e.message.contains("accept")
+    }
+
+    def "accepts a candidate version if its status is equal to or higher than the selector's status"() {
+        def metadata = Stub(ComponentMetadata) {
+            getStatus() >> "silver"
+            getStatusScheme() >> ["bronze", "silver", "gold"]
+        }
+
+        expect:
+        accept("latest.bronze", metadata)
+        accept("latest.silver", metadata)
+        !accept("latest.gold", metadata)
+    }
+
+    def "rejects a candidate version if selector's status is not contained in candidate's status scheme"() {
+        def metadata = Stub(ComponentMetadata) {
+            getStatus() >> "silver"
+            getStatusScheme() >> ["bronze", "silver", "gold"]
+        }
+
+        expect:
+        !accept("latest.other", metadata)
+    }
+
+    @Override
+    VersionSelector getSelector(String selector) {
+        return new LatestVersionSelector(selector)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorSchemeTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorSchemeTest.groovy
new file mode 100644
index 0000000..129a809
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/MavenVersionSelectorSchemeTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 spock.lang.Specification
+
+class MavenVersionSelectorSchemeTest extends Specification {
+    def defaultMatcher = new DefaultVersionSelectorScheme(new DefaultVersionComparator())
+    def mapper = new MavenVersionSelectorScheme(defaultMatcher)
+
+    def "translates to maven syntax"() {
+        given:
+        def selector = defaultMatcher.parseSelector(input)
+
+        expect:
+        mapper.renderSelector(selector) == output
+
+        where:
+        input                | output
+        "]2,3]"              | "(2,3]"
+        "[2,3["              | "[2,3)"
+        "]2,3["              | "(2,3)"
+        "1.0"                | "1.0"
+        "[1.0]"              | "[1.0]"
+        "+"                  | "+"
+        "latest.integration" | "LATEST"
+        "latest.release"     | "RELEASE"
+        "1+"                 | "1+"
+        "1.+"                | "1.+"
+        "1.5+"               | "1.5+"
+        "1.100+"             | "1.100+"
+        "10.1+"              | "10.1+"
+
+    }
+
+    def "translates from maven syntax"() {
+        expect:
+        def selector = mapper.parseSelector(input)
+        defaultMatcher.renderSelector(selector) == output
+
+        where:
+        output               | input
+        "1.0"                | "1.0"
+        "[1,2)"              | "[1,2)"
+        "(1,2)"              | "(1,2)"
+        "[1.5,1.6)"          | "[1.5,1.6)"
+        "1.5+"               | "1.5+"
+        "+"                  | "+"
+        "latest.integration" | "LATEST"
+        "latest.release"     | "RELEASE"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelectorTest.groovy
new file mode 100644
index 0000000..b5f2563
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/SubVersionSelectorTest.groovy
@@ -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 org.gradle.api.artifacts.ComponentMetadata
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+
+class SubVersionSelectorTest extends AbstractVersionSelectorTest {
+    def "all handled selectors are dynamic"() {
+        expect:
+        isDynamic("1+")
+        isDynamic("1.2.3+")
+    }
+
+    def "never needs metadata"() {
+        expect:
+        !requiresMetadata("1+")
+        !requiresMetadata("1.2.3+")
+    }
+
+    def "accepts candidate versions that literally match the selector up until the trailing '+'"() {
+        expect:
+        accept("1+", "11")
+        accept("1.+", "1.2")
+        accept("1.2.3+", "1.2.3.11")
+        !accept("1+", "2")
+        !accept("1.+", "11")
+        !accept("1.2.3+", "1.2")
+    }
+
+    def "metadata-aware accept method delivers same results"() {
+        def metadata = Stub(ComponentMetadata) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        accept("1.+", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.5"           | true
+        "2.5"           | false
+    }
+
+    @Override
+    VersionSelector getSelector(String selector) {
+        return new SubVersionSelector(selector)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParserTest.groovy
new file mode 100644
index 0000000..d9dd85c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionParserTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 spock.lang.Specification
+
+import static org.gradle.util.Matchers.strictlyEqual
+
+class VersionParserTest extends Specification {
+    def versionParser = new VersionParser()
+
+    def "parsed version is equal when source string is equal"() {
+        def v = parse("1.2.b")
+        def equal = parse("1.2.b")
+
+        expect:
+        v strictlyEqual(equal)
+        v != parse("1.2.c")
+        v != parse("1.2")
+        v != parse("1.2.b.0")
+        v != parse("1.2-b")
+        v != parse("1.2b")
+    }
+
+    def "splits version on punctuation"() {
+        expect:
+        def version = parse(versionStr)
+        version.parts as List == parts
+
+        where:
+        versionStr      | parts
+        'a.b.c'         | ['a', 'b', 'c']
+        'a-b-c'         | ['a', 'b', 'c']
+        'a_b_c'         | ['a', 'b', 'c']
+        'a+b+c'         | ['a', 'b', 'c']
+        'a.b-c+d_e'     | ['a', 'b', 'c', 'd', 'e']
+        '\u03b1-\u03b2' | ['\u03b1', '\u03b2']
+    }
+
+    def "splits on adjacent digits and alpha"() {
+        expect:
+        def version = parse(versionStr)
+        version.parts as List == parts
+
+        where:
+        versionStr       | parts
+        'abc123'         | ['abc', '123']
+        '1a2b'           | ['1', 'a', '2', 'b']
+        '1\u03b12\u03b2' | ['1', '\u03b1', '2', '\u03b2']
+    }
+
+    def "base version includes the first . separated parts"() {
+        expect:
+        def version = parse(versionStr)
+        version.baseVersion == parse(baseStr)
+        version.qualified == qualified
+
+        where:
+        versionStr        | baseStr     | qualified
+        "1.2.3"           | "1.2.3"     | false
+        "1.2-3"           | "1.2"       | true
+        "1.2-beta_3+0000" | "1.2"       | true
+        "1.2b3"           | "1.2"       | true
+        "1-alpha"         | "1"         | true
+        "abc.1-3"         | "abc.1"     | true
+        "123"             | "123"       | false
+        "abc"             | "abc"       | false
+        "a.b.c.1.2"       | "a.b.c.1.2" | false
+        "1b2.1.2.3"       | "1"         | true
+        "b1-2-3.3"        | "b"         | true
+    }
+
+    def "handles empty parts and retains whitespace"() {
+        expect:
+        def version = parse(versionStr)
+        version.parts as List == parts
+
+        where:
+        versionStr  | parts
+        ''          | []
+        'a b c'     | ['a b c']
+        '...'       | ['', '', '']
+        '-a b c-  ' | ['', 'a b c', '  ']
+    }
+
+    def parse(String v) {
+        return versionParser.transform(v)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelectorTest.groovy
new file mode 100644
index 0000000..ecfee14
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/strategy/VersionRangeSelectorTest.groovy
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComponentMetadata
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+
+public class VersionRangeSelectorTest extends AbstractVersionSelectorTest {
+    def "all handled selectors are dynamic"() {
+        expect:
+        isDynamic("[1.0,2.0]")
+        isDynamic("[1.0,)")
+    }
+
+    def "never needs metadata"() {
+        expect:
+        !requiresMetadata("[1.0,2.0]")
+        !requiresMetadata("[1.0,)")
+    }
+
+    def "accepts candidate versions that fall into the selector's range"() {
+        expect:
+        accept("[1.0,2.0]", "1.0")
+        accept("[1.0,2.0]", "1.2.3")
+        accept("[1.0,2.0]", "2.0")
+
+        accept("[1.0,2.0[", "1.0")
+        accept("[1.0,2.0[", "1.2.3")
+        accept("[1.0,2.0[", "1.99")
+
+        accept("]1.0,2.0]", "1.0.1")
+        accept("]1.0,2.0]", "1.2.3")
+        accept("]1.0,2.0]", "2.0")
+
+        accept("]1.0,2.0[", "1.0.1")
+        accept("]1.0,2.0[", "1.2.3")
+        accept("]1.0,2.0[", "1.99")
+
+        accept("[1.0,)", "1.0")
+        accept("[1.0,)", "1.2.3")
+        accept("[1.0,)", "2.3.4")
+
+        accept("]1.0,)", "1.0.1")
+        accept("]1.0,)", "1.2.3")
+        accept("]1.0,)", "2.3.4")
+
+        accept("(,2.0]", "0")
+        accept("(,2.0]", "0.1.2")
+        accept("(,2.0]", "2.0")
+
+        accept("(,2.0[", "0")
+        accept("(,2.0[", "0.1.2")
+        accept("(,2.0[", "1.99")
+    }
+
+    def "accepts candidate versions that fall into the selector's range (adding qualifiers to the mix)"() {
+        expect:
+        accept("[1.0,2.0]", "1.5-dev-1")
+        accept("[1.0,2.0]", "1.2.3-rc-2")
+        accept("[1.0,2.0]", "2.0-final")
+
+        accept("[1.0-dev-1,2.0[", "1.0")
+        accept("[1.0,2.0-rc-2[", "2.0-rc-1")
+        accept("[1.0,2.0[", "2.0-final")
+
+        accept("]1.0-dev-1,2.0]", "1.0")
+        accept("]1.0-rc-2,2.0]", "1.0-rc-3")
+
+        accept("]1.0-dev-1,1.0-dev-3[", "1.0-dev-2")
+        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:
+        !accept("[1.0,2.0]", "0.99")
+        !accept("[1.0,2.0]", "2.0.1")
+        !accept("[1.0,2.0]", "42")
+
+        !accept("[1.0,2.0[", "0.99")
+        !accept("[1.0,2.0[", "2.0")
+        !accept("[1.0,2.0[", "42")
+
+        !accept("]1.0,2.0]", "1.0")
+        !accept("]1.0,2.0]", "2.0.1")
+        !accept("]1.0,2.0]", "42")
+
+        !accept("]1.0,2.0[", "1.0")
+        !accept("]1.0,2.0[", "2.0")
+        !accept("]1.0,2.0[", "42")
+
+        !accept("[1.0,)", "0")
+        !accept("[1.0,)", "0.99")
+
+        !accept("]1.0,)", "0")
+        !accept("]1.0,)", "1")
+        !accept("]1.0,)", "1.0")
+
+        !accept("(,2.0]", "2.0.1")
+        !accept("(,2.0]", "42")
+
+        !accept("(,2.0[", "2.0")
+        !accept("(,2.0[", "42")
+    }
+
+    def "rejects candidate versions that don't fall into the selector's range (adding qualifiers to the mix)"() {
+        expect:
+        !accept("[1.0,2.0]", "2.5-dev-1")
+        !accept("[1.0,2.0]", "1.0-rc-2")
+        !accept("[1.0,2.0]", "1.0-final")
+
+        !accept("[1.0-dev-2,2.0[", "1.0-dev-1")
+        !accept("[1.0,2.0-rc-2[", "2.0-rc-2")
+        !accept("[1.0,2.0-final[", "2.0")
+
+        !accept("]1.0-dev-1,2.0]", "1.0-dev-1")
+        !accept("]1.0-rc-2,2.0]", "1.0-dev-3")
+
+        !accept("]1.0-dev-1,1.0-dev-3[", "1.0-dev-3")
+        !accept("]1.0-dev-1,1.0-rc-1[", "1.0-final-0")
+    }
+
+    def "metadata-aware accept method delivers same results"() {
+        def metadata = Stub(ComponentMetadata) {
+            getId() >> Stub(ModuleVersionIdentifier) {
+                getVersion() >> metadataVersion
+            }
+        }
+
+        expect:
+        accept("[1.0,2.0]", metadata) == result
+
+        where:
+        metadataVersion | result
+        "1.5"           | true
+        "2.5"           | false
+    }
+
+    @Override
+    VersionSelector getSelector(String selector) {
+        return new VersionRangeSelector(selector, new DefaultVersionComparator().asStringComparator())
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.groovy
new file mode 100644
index 0000000..0d1f487
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleDescriptorStoreTest.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.api.internal.artifacts.ivyservice.modulecache
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyModuleDescriptorWriter
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver
+import org.gradle.internal.resource.local.LocallyAvailableResource
+import org.gradle.internal.resource.local.PathKeyFileStore
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class ModuleDescriptorStoreTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temporaryFolder
+    ModuleDescriptorStore store
+    PathKeyFileStore pathKeyFileStore = Mock()
+    ModuleComponentRepository repository = Mock()
+    LocallyAvailableResource fileStoreEntry = Mock()
+    ModuleDescriptor moduleDescriptor = Mock()
+    IvyModuleDescriptorWriter ivyModuleDescriptorWriter = Mock()
+    IvyXmlModuleDescriptorParser ivyXmlModuleDescriptorParser = Mock()
+    ModuleComponentIdentifier moduleComponentIdentifier = Mock()
+    def resolver = Mock(DependencyToComponentResolver)
+
+    def setup() {
+        store = new ModuleDescriptorStore(pathKeyFileStore, ivyModuleDescriptorWriter, ivyXmlModuleDescriptorParser);
+        _ * repository.getId() >> "repositoryId"
+        _ * moduleComponentIdentifier.group >> "org.test"
+        _ * moduleComponentIdentifier.module >> "testArtifact"
+        _ * moduleComponentIdentifier.version >> "1.0"
+    }
+
+    def "getModuleDescriptorFile returns null for not cached descriptors"() {
+        when:
+        pathKeyFileStore.get("org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
+        then:
+        null == store.getModuleDescriptor(repository, moduleComponentIdentifier)
+    }
+
+    def "getModuleDescriptorFile uses PathKeyFileStore to get file"() {
+        when:
+        store.getModuleDescriptor(repository, moduleComponentIdentifier);
+        then:
+        1 * pathKeyFileStore.get("org.test/testArtifact/1.0/repositoryId/ivy.xml") >> null
+    }
+
+    def "putModuleDescriptor uses PathKeyFileStore to write file"() {
+        setup:
+        File descriptorFile = temporaryFolder.createFile("fileStoreEntry")
+        when:
+        store.putModuleDescriptor(repository, moduleComponentIdentifier, moduleDescriptor);
+        then:
+        1 * pathKeyFileStore.add("org.test/testArtifact/1.0/repositoryId/ivy.xml", _) >> { path, action ->
+            action.execute(descriptorFile); fileStoreEntry
+        };
+        1 * ivyModuleDescriptorWriter.write(moduleDescriptor, descriptorFile)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.groovy
new file mode 100644
index 0000000..368ac51
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToArtifactsConverterTest.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.internal.artifacts.ivyservice.moduleconverter
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.DefaultPublishArtifactSet
+import org.gradle.internal.component.model.IvyArtifactName
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData
+import spock.lang.Specification
+
+class DefaultConfigurationsToArtifactsConverterTest extends Specification {
+    def converter = new DefaultConfigurationsToArtifactsConverter()
+
+    def "adds artifacts from each configuration"() {
+        def metaData = Mock(MutableLocalComponentMetaData)
+        def config1 = Stub(Configuration)
+        def config2 = Stub(Configuration)
+        def artifact1 = Stub(PublishArtifact)
+        def artifact2 = Stub(PublishArtifact)
+        def file1 = new File("file-1")
+        def file2 = new File("file-2")
+
+        given:
+        config1.name >> "config1"
+        config1.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact1]))
+        config2.name >> "config2"
+        config2.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact2]))
+
+        and:
+        artifact1.name >> 'art1'
+        artifact1.type >> 'type1'
+        artifact1.extension >> 'ext1'
+        artifact2.name >> 'art2'
+        artifact2.type >> 'type2'
+        artifact2.extension >> 'ext2'
+        artifact2.classifier >> 'classifier'
+
+        when:
+        converter.addArtifacts(metaData, [config1, config2])
+
+        then:
+        1 * metaData.addArtifact("config1", _, file1) >> { name, IvyArtifactName artifact, file ->
+            assert artifact.name == 'art1'
+            assert artifact.type == 'type1'
+            assert artifact.extension == 'ext1'
+            assert artifact.attributes == [:]
+        }
+        1 * metaData.addArtifact("config2", _, file2) >> { name, IvyArtifactName artifact, file ->
+            assert artifact.name == 'art2'
+            assert artifact.type == 'type2'
+            assert artifact.extension == 'ext2'
+            assert artifact.attributes == [(Dependency.CLASSIFIER): 'classifier']
+        }
+        _ * metaData.id
+        0 * metaData._
+    }
+
+    def "artifact name defaults to module name when not specified"() {
+        def metaData = Mock(MutableLocalComponentMetaData)
+        def config = Stub(Configuration)
+        def artifact = Stub(PublishArtifact)
+        def file = new File("file-1")
+
+        given:
+        config.name >> "config1"
+        config.artifacts >> new DefaultPublishArtifactSet("art1", new DefaultDomainObjectSet<PublishArtifact>(PublishArtifact, [artifact]))
+
+        and:
+        artifact.type >> 'type1'
+        artifact.extension >> 'ext1'
+
+        when:
+        converter.addArtifacts(metaData, [config])
+
+        then:
+        1 * metaData.addArtifact("config1", _, file) >> { name, IvyArtifactName ivyArtifact, f ->
+            assert ivyArtifact.name == 'module'
+        }
+        _ * metaData.id >> DefaultModuleVersionIdentifier.newId("group", "module", "version")
+        0 * metaData._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
new file mode 100644
index 0000000..3516f09
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultConfigurationsToModuleDescriptorConverterTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2007-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.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.internal.component.local.model.DefaultLocalComponentMetaData;
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData;
+import org.gradle.util.TestUtil;
+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.util.Collections;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public class DefaultConfigurationsToModuleDescriptorConverterTest {
+    private DefaultConfigurationsToModuleDescriptorConverter configurationsToModuleDescriptorConverter = new DefaultConfigurationsToModuleDescriptorConverter();
+
+    private JUnit4Mockery context = new JUnit4Mockery();
+
+    @Test
+    public void testAddConfigurations() {
+        Configuration configurationStub1 = createNamesAndExtendedConfigurationStub("conf1");
+        Configuration configurationStub2 = createNamesAndExtendedConfigurationStub("conf2", configurationStub1);
+        DefaultModuleDescriptor moduleDescriptor = TestUtil.createModuleDescriptor(Collections.EMPTY_SET);
+        MutableLocalComponentMetaData metaData = new DefaultLocalComponentMetaData(moduleDescriptor, null);
+
+        configurationsToModuleDescriptorConverter.addConfigurations(metaData, WrapUtil.toSet(configurationStub1, configurationStub2));
+
+        assertIvyConfigurationIsCorrect(moduleDescriptor.getConfiguration(configurationStub1.getName()),
+                expectedIvyConfiguration(configurationStub1));
+        assertIvyConfigurationIsCorrect(moduleDescriptor.getConfiguration(configurationStub2.getName()),
+                expectedIvyConfiguration(configurationStub2));
+        assertThat(moduleDescriptor.getConfigurations().length, equalTo(2));
+    }
+
+    private void assertIvyConfigurationIsCorrect(org.apache.ivy.core.module.descriptor.Configuration actualConfiguration,
+                                                 org.apache.ivy.core.module.descriptor.Configuration expectedConfiguration) {
+        assertThat(actualConfiguration.getDescription(), equalTo(expectedConfiguration.getDescription()));
+        assertThat(actualConfiguration.isTransitive(), equalTo(expectedConfiguration.isTransitive()));
+        assertThat(actualConfiguration.getVisibility(), equalTo(expectedConfiguration.getVisibility()));
+        assertThat(actualConfiguration.getName(), equalTo(expectedConfiguration.getName()));
+        assertThat(actualConfiguration.getExtends(), equalTo(expectedConfiguration.getExtends()));
+    }
+
+    private org.apache.ivy.core.module.descriptor.Configuration expectedIvyConfiguration(Configuration configuration) {
+        return new org.apache.ivy.core.module.descriptor.Configuration(
+                configuration.getName(),
+                configuration.isVisible() ? org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC : org.apache.ivy.core.module.descriptor.Configuration.Visibility.PRIVATE,
+                configuration.getDescription(),
+                Configurations.getNames(configuration.getExtendsFrom()).toArray(new String[configuration.getExtendsFrom().size()]),
+                configuration.isTransitive(),
+                null);
+    }
+
+    private Configuration createNamesAndExtendedConfigurationStub(final String name, final Configuration... extendsFromConfigurations) {
+        final Configuration configurationStub = IvyConverterTestUtil.createNamedConfigurationStub(name, context);
+        context.checking(new Expectations() {{
+            allowing(configurationStub).isTransitive();
+            will(returnValue(true));
+
+            allowing(configurationStub).getDescription();
+            will(returnValue(TestUtil.createUniqueId()));
+
+            allowing(configurationStub).isVisible();
+            will(returnValue(true));
+
+            allowing(configurationStub).getExtendsFrom();
+            will(returnValue(WrapUtil.toSet(extendsFromConfigurations)));
+        }});
+        return configurationStub;
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultExcludeRuleConverterTest.java
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/IvyConverterTestUtil.java
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy
new file mode 100644
index 0000000..7faca5b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/ResolveLocalComponentFactoryTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * 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.moduleconverter
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.internal.artifacts.DefaultModule
+import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependenciesToModuleDescriptorConverter
+import org.gradle.internal.component.local.model.DefaultLocalComponentMetaData
+import spock.lang.Specification
+
+public class ResolveLocalComponentFactoryTest extends Specification {
+    def configurationsConverter = Mock(ConfigurationsToModuleDescriptorConverter)
+    def dependenciesConverter = Mock(DependenciesToModuleDescriptorConverter)
+    def componentIdentifierFactory = Mock(ComponentIdentifierFactory)
+    def configurationsToArtifactsConverter = Mock(ConfigurationsToArtifactsConverter)
+
+    ResolveLocalComponentFactory resolveModuleDescriptorConverter = new ResolveLocalComponentFactory(
+            configurationsConverter,
+            dependenciesConverter,
+            componentIdentifierFactory,
+            configurationsToArtifactsConverter)
+
+    def "converts for provided default module"() {
+        given:
+        def configurations = [Mock(Configuration), Mock(Configuration)] as Set
+        def module = new DefaultModule('group-one', 'name-one', 'version-one')
+
+        when:
+        def actualDescriptor = resolveModuleDescriptorConverter.convert(configurations, module);
+
+        then:
+        1 * configurationsConverter.addConfigurations(!null, configurations)
+        1 * dependenciesConverter.addDependencyDescriptors(!null, configurations)
+        1 * componentIdentifierFactory.createComponentIdentifier(module) >> new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
+
+        and:
+        actualDescriptor instanceof DefaultLocalComponentMetaData
+        actualDescriptor.moduleDescriptor instanceof DefaultModuleDescriptor
+        actualDescriptor.toResolveMetaData().componentId == new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
new file mode 100644
index 0000000..8ee1970
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyDescriptorFactoryInternalTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.DependencyArtifact;
+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.IvyUtil;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter;
+import org.gradle.util.TestUtil;
+import org.gradle.util.WrapUtil;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+ at RunWith(JMock.class)
+public abstract class AbstractDependencyDescriptorFactoryInternalTest {
+    private JUnit4Mockery context = new JUnit4Mockery();
+
+    protected static final String TEST_CONF = "conf";
+    protected static final String TEST_DEP_CONF = "depconf1";
+
+    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 = 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");
+
+    @Before
+    public void setUp() {
+        expectExcludeRuleConversion(TEST_EXCLUDE_RULE, TEST_IVY_EXCLUDE_RULE);
+    }
+
+    protected void expectExcludeRuleConversion(final ExcludeRule excludeRule, final org.apache.ivy.core.module.descriptor.ExcludeRule ivyExcludeRule) {
+        context.checking(new Expectations() {{
+            allowing(excludeRuleConverterStub).createExcludeRule(TEST_CONF, excludeRule);
+            will(returnValue(ivyExcludeRule));
+        }});
+    }
+
+    protected Dependency setUpDependency(ModuleDependency dependency) {
+        return dependency.addArtifact(artifact).
+                addArtifact(artifactWithClassifiers).
+                exclude(WrapUtil.toMap("group", TEST_EXCLUDE_RULE.getGroup())).
+                setTransitive(true);
+    }
+
+    protected void assertDependencyDescriptorHasCommonFixtureValues(DependencyDescriptor dependencyDescriptor) {
+        assertThat(dependencyDescriptor.getParentRevisionId(), equalTo(moduleDescriptor.getModuleRevisionId()));
+        assertEquals(TEST_IVY_EXCLUDE_RULE, dependencyDescriptor.getExcludeRules(TEST_CONF)[0]);
+        assertThat(dependencyDescriptor.getDependencyConfigurations(TEST_CONF), equalTo(WrapUtil.toArray(TEST_DEP_CONF)));
+        assertThat(dependencyDescriptor.isTransitive(), equalTo(true));
+        assertDependencyDescriptorHasArtifacts(dependencyDescriptor);
+    }
+
+    private void assertDependencyDescriptorHasArtifacts(DependencyDescriptor dependencyDescriptor) {
+        List<DependencyArtifactDescriptor> artifactDescriptors = WrapUtil.toList(dependencyDescriptor.getDependencyArtifacts(TEST_CONF));
+        assertThat(artifactDescriptors.size(), equalTo(2));
+
+        
+        DependencyArtifactDescriptor artifactDescriptorWithoutClassifier = findDescriptor(artifactDescriptors, artifact);
+        assertEquals(new HashMap(), artifactDescriptorWithoutClassifier.getExtraAttributes());
+        assertEquals(null, artifactDescriptorWithoutClassifier.getUrl());
+        compareArtifacts(artifact, artifactDescriptorWithoutClassifier);
+        assertEquals(artifact.getType(), artifactDescriptorWithoutClassifier.getExt());
+
+        DependencyArtifactDescriptor artifactDescriptorWithClassifierAndConfs = findDescriptor(artifactDescriptors, artifactWithClassifiers);
+        assertEquals(WrapUtil.toMap(Dependency.CLASSIFIER, artifactWithClassifiers.getClassifier()), artifactDescriptorWithClassifierAndConfs.getQualifiedExtraAttributes());
+        compareArtifacts(artifactWithClassifiers, artifactDescriptorWithClassifierAndConfs);
+        assertEquals(artifactWithClassifiers.getExtension(), artifactDescriptorWithClassifierAndConfs.getExt());
+        try {
+            assertEquals(new URL(artifactWithClassifiers.getUrl()), artifactDescriptorWithClassifierAndConfs.getUrl());
+        } catch (MalformedURLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private DependencyArtifactDescriptor findDescriptor(List<DependencyArtifactDescriptor> artifactDescriptors, DefaultDependencyArtifact dependencyArtifact) {
+        for (DependencyArtifactDescriptor artifactDescriptor : artifactDescriptors) {
+            if (artifactDescriptor.getName().equals(dependencyArtifact.getName())) {
+                return artifactDescriptor;
+            }
+        }
+        throw new RuntimeException("Descriptor could not be found");
+    }
+
+    private void compareArtifacts(DependencyArtifact artifact, DependencyArtifactDescriptor artifactDescriptor) {
+        assertEquals(artifact.getName(), artifactDescriptor.getName());
+        assertEquals(artifact.getType(), artifactDescriptor.getType());
+    }
+
+    private static DefaultExcludeRule getTestExcludeRule() {
+        return new DefaultExcludeRule(new ArtifactId(
+                IvyUtil.createModuleId("org", "testOrg"), PatternMatcher.ANY_EXPRESSION,
+                PatternMatcher.ANY_EXPRESSION,
+                PatternMatcher.ANY_EXPRESSION),
+                ExactPatternMatcher.INSTANCE, null);
+    }
+}
+
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.groovy
new file mode 100644
index 0000000..ce63e9c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependenciesToModuleDescriptorConverterTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.descriptor.DefaultModuleDescriptor
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.ExcludeRule
+import org.gradle.api.artifacts.ModuleDependency
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.ExcludeRuleConverter
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData
+import spock.lang.Specification
+
+import static org.gradle.util.WrapUtil.toDomainObjectSet
+
+public class DefaultDependenciesToModuleDescriptorConverterTest extends Specification {
+
+    def dependencyDescriptorFactory = Mock(DependencyDescriptorFactory)
+    def excludeRuleConverter = Mock(ExcludeRuleConverter)
+    def converter = new DefaultDependenciesToModuleDescriptorConverter(dependencyDescriptorFactory, excludeRuleConverter)
+
+    def descriptor = Mock(DefaultModuleDescriptor)
+    def metaData = Mock(MutableLocalComponentMetaData)
+    def configuration = Mock(Configuration)
+    def dependencySet = Mock(DependencySet.class);
+
+    def "ignores configuration with no dependencies or exclude rules"() {
+        when:
+        converter.addDependencyDescriptors(metaData, [configuration])
+
+        then:
+        1 * configuration.dependencies >> dependencySet
+        1 * dependencySet.withType(ModuleDependency) >> toDomainObjectSet(ModuleDependency)
+        1 * configuration.excludeRules >> ([] as Set)
+        0 * _
+    }
+
+    def "adds dependencies from configuration"() {
+        def dependencyDescriptor = Mock(DependencyMetaData)
+        def dependency = Mock(ModuleDependency)
+
+        when:
+        converter.addDependencyDescriptors(metaData, [configuration])
+
+        then:
+        _ * metaData.moduleDescriptor >> descriptor
+        1 * configuration.dependencies >> dependencySet
+        1 * dependencySet.withType(ModuleDependency) >> toDomainObjectSet(ModuleDependency, dependency)
+        1 * configuration.name >> "config"
+        1 * dependencyDescriptorFactory.createDependencyDescriptor("config", descriptor, dependency) >> dependencyDescriptor
+        1 * metaData.addDependency(dependencyDescriptor)
+        1 * configuration.excludeRules >> ([] as Set)
+        0 * _
+    }
+
+    def "adds exclude rule from configuration"() {
+        def excludeRule = Mock(ExcludeRule)
+        def ivyExcludeRule = Mock(org.apache.ivy.core.module.descriptor.ExcludeRule)
+
+        when:
+        converter.addDependencyDescriptors(metaData, [configuration])
+
+        then:
+        1 * configuration.dependencies >> dependencySet
+        1 * dependencySet.withType(ModuleDependency) >> toDomainObjectSet(ModuleDependency)
+
+        1 * configuration.excludeRules >> ([excludeRule] as Set)
+        1 * configuration.getName() >> "config"
+        1 * excludeRuleConverter.createExcludeRule("config", excludeRule) >> ivyExcludeRule
+        1 * metaData.addExcludeRule(ivyExcludeRule)
+        0 * _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
new file mode 100644
index 0000000..d8e78c5
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * 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.dependencies
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData
+import spock.lang.Specification
+
+public class DefaultDependencyDescriptorFactoryTest extends Specification {
+    def configurationName = "conf"
+    def moduleDescriptor = Stub(DefaultModuleDescriptor)
+    def projectDependency = Stub(ProjectDependency)
+
+    def "delegates to internal factory"() {
+        given:
+        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory)
+        def ivyDependencyDescriptorFactory2 = Mock(IvyDependencyDescriptorFactory)
+        def result = Stub(DslOriginDependencyMetaData)
+
+        when:
+        def dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
+                ivyDependencyDescriptorFactory1, ivyDependencyDescriptorFactory2
+        );
+        def created = dependencyDescriptorFactory.createDependencyDescriptor(configurationName, moduleDescriptor, projectDependency)
+
+        then:
+        created == result
+
+        and:
+        1 * ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
+        1 * ivyDependencyDescriptorFactory2.canConvert(projectDependency) >> true
+        1 * ivyDependencyDescriptorFactory2.createDependencyDescriptor(configurationName, projectDependency, moduleDescriptor) >> result
+    }
+
+    def "fails where no internal factory can handle dependency type"() {
+        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory);
+
+        when:
+        ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
+
+        and:
+        def dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
+                ivyDependencyDescriptorFactory1
+        );
+        dependencyDescriptorFactory.createDependencyDescriptor(configurationName, moduleDescriptor, projectDependency)
+
+        then:
+        thrown InvalidUserDataException
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.java
new file mode 100644
index 0000000..9c96e6d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.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.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.gradle.api.artifacts.ExternalModuleDependency;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData;
+import org.hamcrest.Matchers;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class ExternalModuleDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalTest {
+    private JUnit4Mockery context = new JUnit4Mockery();
+
+    ExternalModuleIvyDependencyDescriptorFactory externalModuleDependencyDescriptorFactory =
+            new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverterStub);
+    
+    @Test
+    public void canConvert() {
+        assertThat(externalModuleDependencyDescriptorFactory.canConvert(context.mock(ProjectDependency.class)), Matchers.equalTo(false));
+        assertThat(externalModuleDependencyDescriptorFactory.canConvert(context.mock(ExternalModuleDependency.class)), Matchers.equalTo(true));
+    }
+
+    @Test
+    public void testAddWithNullGroupAndNullVersionShouldHaveEmptyStringModuleRevisionValues() {
+        ModuleDependency dependency = new DefaultExternalModuleDependency(null, "gradle-core", null, TEST_DEP_CONF);
+        DslOriginDependencyMetaData dependencyMetaData = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, dependency, moduleDescriptor);
+        assertThat(dependencyMetaData.getDescriptor().getDependencyRevisionId(), equalTo(IvyUtil.createModuleRevisionId(dependency)));
+    }
+
+    @Test
+    public void testCreateFromModuleDependency() {
+        DefaultExternalModuleDependency moduleDependency = new DefaultExternalModuleDependency("org.gradle",
+                "gradle-core", "1.0", TEST_DEP_CONF);
+        setUpDependency(moduleDependency);
+
+        DslOriginDependencyMetaData dependencyMetaData = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, moduleDependency, moduleDescriptor);
+
+        DependencyDescriptor dependencyDescriptor = dependencyMetaData.getDescriptor();
+        assertEquals(moduleDependency.isChanging(), dependencyDescriptor.isChanging());
+        assertEquals(dependencyDescriptor.isForce(), moduleDependency.isForce());
+        assertEquals(IvyUtil.createModuleRevisionId(moduleDependency), dependencyDescriptor.getDependencyRevisionId());
+        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
new file mode 100644
index 0000000..51babb2
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies
+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.artifacts.ivyservice.IvyUtil
+import org.gradle.api.internal.project.AbstractProject
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaData
+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.*
+
+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);
+        DslOriginDependencyMetaData dependencyMetaData = projectDependencyDescriptorFactory.createDependencyDescriptor(TEST_CONF, projectDependency, moduleDescriptor);
+
+        def dependencyDescriptor = dependencyMetaData.descriptor
+        assertDependencyDescriptorHasCommonFixtureValues(dependencyDescriptor);
+        assertFalse(dependencyDescriptor.isChanging());
+        assertFalse(dependencyDescriptor.isForce());
+        assertEquals(IvyUtil.createModuleRevisionId("someGroup", "test", "someVersion"), dependencyDescriptor.getDependencyRevisionId());
+        assertSame(projectDependency, dependencyMetaData.source);
+    }
+
+    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/ReflectiveDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ReflectiveDependencyDescriptorFactoryTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
new file mode 100644
index 0000000..2b31818
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * 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.projectmodule
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor
+import org.gradle.api.internal.artifacts.ivyservice.LocalComponentFactory
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import org.gradle.internal.component.local.model.MutableLocalComponentMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult
+import spock.lang.Specification
+
+class ProjectDependencyResolverTest extends Specification {
+    final ProjectComponentRegistry registry = Mock()
+    final DependencyToComponentIdResolver target = Mock()
+    final LocalComponentFactory converter = Mock()
+    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, converter, target)
+
+    def "resolves project dependency"() {
+        setup:
+        def resolveMetaData = Stub(ModuleComponentResolveMetaData)
+        def componentMetaData = Stub(MutableLocalComponentMetaData) {
+            toResolveMetaData() >> resolveMetaData
+        }
+        def result = Mock(BuildableComponentIdResolveResult)
+        def dependencyMetaData = Stub(DependencyMetaData) {
+            getSelector() >> DefaultProjectComponentSelector.newSelector(":project")
+        }
+
+        when:
+        resolver.resolve(dependencyMetaData, result)
+
+        then:
+        1 * registry.getProject(":project") >> componentMetaData
+        1 * result.resolved(resolveMetaData)
+        0 * result._
+    }
+
+    def "delegates to backing resolver for non-project dependency"() {
+        def result = Mock(BuildableComponentIdResolveResult)
+        def dependencyDescriptor = Stub(DependencyDescriptor)
+        def dependencyMetaData = Stub(DependencyMetaData) {
+            getDescriptor() >> dependencyDescriptor
+        }
+
+        when:
+        resolver.resolve(dependencyMetaData, result)
+
+        then:
+        1 * target.resolve(dependencyMetaData, result)
+        0 * _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy
new file mode 100644
index 0000000..a459b1e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultCachePolicySpec.groovy
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy
+
+import org.gradle.api.Action
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.ResolvedModuleVersion
+import org.gradle.api.artifacts.cache.ArtifactResolutionControl
+import org.gradle.api.artifacts.cache.DependencyResolutionControl
+import org.gradle.api.artifacts.cache.ModuleResolutionControl
+import org.gradle.api.internal.artifacts.DefaultArtifactIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.configurations.MutationValidator
+import org.gradle.internal.Actions
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import spock.lang.Specification
+
+import java.util.concurrent.TimeUnit
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY
+
+public class DefaultCachePolicySpec extends Specification {
+    private static final int SECOND = 1000;
+    private static final int MINUTE = SECOND * 60;
+    private static final int HOUR = MINUTE * 60;
+    private static final int DAY = HOUR * 24;
+    private static final int WEEK = DAY * 7;
+    private static final int FOREVER = Integer.MAX_VALUE
+
+    DefaultCachePolicy cachePolicy = new DefaultCachePolicy()
+
+    def "will cache default"() {
+        expect:
+        hasDynamicVersionTimeout(DAY)
+        hasChangingModuleTimeout(DAY)
+        hasModuleTimeout(FOREVER)
+        hasMissingArtifactTimeout(DAY)
+        hasMissingModuleTimeout(FOREVER)
+    }
+
+    def "uses changing module timeout for changing modules"() {
+        when:
+        cachePolicy.cacheChangingModulesFor(10, TimeUnit.SECONDS);
+
+        then:
+        hasDynamicVersionTimeout(DAY);
+        hasChangingModuleTimeout(10 * SECOND)
+        hasModuleTimeout(FOREVER)
+        hasMissingArtifactTimeout(DAY)
+        hasMissingModuleTimeout(FOREVER)
+    }
+
+    def "uses dynamic version timeout for dynamic versions"() {
+        when:
+        cachePolicy.cacheDynamicVersionsFor(10, TimeUnit.SECONDS)
+
+        then:
+        hasDynamicVersionTimeout(10 * SECOND)
+        hasChangingModuleTimeout(DAY)
+        hasModuleTimeout(FOREVER)
+        hasMissingArtifactTimeout(DAY)
+        hasMissingModuleTimeout(FOREVER)
+    }
+
+    def "applies invalidate rule for dynamic versions"() {
+        when:
+        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
+            void execute(DependencyResolutionControl t) {
+                t.refresh()
+            }
+        })
+
+        then:
+        cachePolicy.mustRefreshVersionList(null, null, 2 * SECOND)
+    }
+
+    def "applies useCachedResult for dynamic versions"() {
+        when:
+        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
+            void execute(DependencyResolutionControl t) {
+                t.useCachedResult()
+            }
+        })
+
+        then:
+        !cachePolicy.mustRefreshVersionList(null, null, 2 * SECOND)
+    }
+
+    def "applies cacheFor rules for dynamic versions"() {
+        when:
+        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
+            void execute(DependencyResolutionControl t) {
+                t.cacheFor(100, TimeUnit.SECONDS)
+            }
+        })
+
+        then:
+        hasDynamicVersionTimeout(100 * SECOND)
+    }
+
+    def "provides details of cached version list"() {
+        expect:
+        cachePolicy.eachDependency(new Action<DependencyResolutionControl>() {
+            void execute(DependencyResolutionControl t) {
+                assert t.request.group == 'g'
+                assert t.request.name == 'n'
+                assertId(t.cachedResult.iterator().next(), 'group', 'name', 'version')
+                t.refresh()
+            }
+        })
+        cachePolicy.mustRefreshVersionList(moduleIdentifier('g', 'n', 'v').module, [moduleIdentifier('group', 'name', 'version')] as Set, 0)
+    }
+
+    def "provides details of cached module"() {
+        expect:
+        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
+            void execute(ModuleResolutionControl t) {
+                assertId(t.request, 'g', 'n', 'v')
+                assertId(t.cachedResult.id, 'group', 'name', 'version')
+                assert !t.changing
+                t.refresh()
+            }
+        })
+        cachePolicy.mustRefreshModule(moduleComponent('g', 'n', 'v'), moduleVersion('group', 'name', 'version'), 0)
+    }
+
+    def "provides details of cached changing module"() {
+        expect:
+        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
+            void execute(ModuleResolutionControl t) {
+                assertId(t.request, 'g', 'n', 'v')
+                assertId(t.cachedResult.id, 'group', 'name', 'version')
+                assert t.changing
+                t.refresh()
+            }
+        })
+        cachePolicy.mustRefreshChangingModule(moduleComponent('g', 'n', 'v'), moduleVersion('group', 'name', 'version'), 0)
+    }
+
+    def "provides details of cached artifact"() {
+        expect:
+        cachePolicy.eachArtifact(new Action<ArtifactResolutionControl>() {
+            void execute(ArtifactResolutionControl t) {
+                assertId(t.request.moduleVersionIdentifier, 'group', 'name', 'version')
+                assert t.request.name == 'artifact'
+                assert t.request.type == 'type'
+                assert t.request.extension == 'ext'
+                assert t.request.classifier == 'classifier'
+                assert t.cachedResult == null
+                t.refresh()
+            }
+        })
+        def artifactIdentifier = new DefaultArtifactIdentifier(moduleIdentifier('group', 'name', 'version'), 'artifact', 'type', 'ext', 'classifier')
+        cachePolicy.mustRefreshArtifact(artifactIdentifier, null, 0, true, true)
+    }
+
+    def "can use cacheFor to control missing module and artifact timeout"() {
+        when:
+        cachePolicy.eachModule(new Action<ModuleResolutionControl>() {
+            void execute(ModuleResolutionControl t) {
+                if (t.cachedResult == null) {
+                    t.cacheFor(10, TimeUnit.SECONDS)
+                }
+            }
+        });
+        cachePolicy.eachArtifact(new Action<ArtifactResolutionControl>() {
+            void execute(ArtifactResolutionControl t) {
+                if (t.cachedResult == null) {
+                    t.cacheFor(20, TimeUnit.SECONDS)
+                }
+            }
+        });
+
+        then:
+        hasDynamicVersionTimeout(DAY)
+        hasChangingModuleTimeout(DAY)
+        hasModuleTimeout(FOREVER)
+        hasMissingModuleTimeout(10 * SECOND)
+        hasMissingArtifactTimeout(20 * SECOND)
+    }
+
+    def "must refresh artifact for changing modules when moduledescriptorhash not in sync"() {
+        expect:
+        !cachePolicy.mustRefreshArtifact(null, null, 1000, false, true)
+        !cachePolicy.mustRefreshArtifact(null, null, 1000, false, false)
+        cachePolicy.mustRefreshArtifact(null, null, 1000, true, false)
+    }
+
+    def "provides a copy"() {
+        expect:
+        def copy = cachePolicy.copy()
+
+        !copy.is(cachePolicy)
+        !copy.dependencyCacheRules.is(cachePolicy.dependencyCacheRules)
+        !copy.moduleCacheRules.is(cachePolicy.moduleCacheRules)
+        !copy.artifactCacheRules.is(cachePolicy.artifactCacheRules)
+
+        copy.dependencyCacheRules == cachePolicy.dependencyCacheRules
+        copy.moduleCacheRules == cachePolicy.moduleCacheRules
+        copy.artifactCacheRules == cachePolicy.artifactCacheRules
+    }
+
+    def "mutation is checked"() {
+        def validator = Mock(MutationValidator)
+        given:
+        cachePolicy.beforeChange(validator)
+
+        when: cachePolicy.cacheChangingModulesFor(0, TimeUnit.HOURS)
+        then: (1.._) * validator.validateMutation(STRATEGY)
+
+        when: cachePolicy.cacheDynamicVersionsFor(0, TimeUnit.HOURS)
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: cachePolicy.eachArtifact(Actions.doNothing())
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: cachePolicy.eachDependency(Actions.doNothing())
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: cachePolicy.eachModule(Actions.doNothing())
+        then: 1 * validator.validateMutation(STRATEGY)
+    }
+
+    def "mutation is not checked for copy"() {
+        def validator = Mock(MutationValidator)
+        given:
+        cachePolicy.beforeChange(validator)
+        def copy = cachePolicy.copy()
+
+        when: copy.cacheChangingModulesFor(0, TimeUnit.HOURS)
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.cacheDynamicVersionsFor(0, TimeUnit.HOURS)
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.eachArtifact(Actions.doNothing())
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.eachDependency(Actions.doNothing())
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.eachModule(Actions.doNothing())
+        then: 0 * validator.validateMutation(_)
+    }
+
+    private def hasDynamicVersionTimeout(int timeout) {
+        def moduleId = moduleIdentifier('group', 'name', 'version')
+        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, 100)
+        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout);
+        assert !cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout - 1)
+        assert cachePolicy.mustRefreshVersionList(null, [moduleId] as Set, timeout + 1)
+        true
+    }
+
+    private def hasChangingModuleTimeout(int timeout) {
+        def id = moduleComponent('group', 'name', 'version')
+        def module = moduleVersion('group', 'name', 'version')
+        assert !cachePolicy.mustRefreshChangingModule(id, module, timeout - 1)
+        assert !cachePolicy.mustRefreshChangingModule(id, module, timeout);
+        assert cachePolicy.mustRefreshChangingModule(id, module, timeout + 1)
+        true
+    }
+
+    private def hasModuleTimeout(int timeout) {
+        def id = moduleComponent('group', 'name', 'version')
+        def module = moduleVersion('group', 'name', 'version')
+        assert !cachePolicy.mustRefreshModule(id, module, timeout);
+        assert !cachePolicy.mustRefreshModule(id, module, timeout - 1)
+        if (timeout != FOREVER) {
+            assert cachePolicy.mustRefreshModule(id, module, timeout + 1)
+        }
+        true
+    }
+
+    private def hasMissingModuleTimeout(int timeout) {
+        def id = moduleComponent('group', 'name', 'version')
+        assert !cachePolicy.mustRefreshModule(id, null, timeout);
+        assert !cachePolicy.mustRefreshModule(id, null, timeout - 1)
+        if (timeout != FOREVER) {
+            assert cachePolicy.mustRefreshModule(id, null, timeout + 1)
+        }
+        return true
+    }
+
+    private def hasMissingArtifactTimeout(int timeout) {
+        assert !cachePolicy.mustRefreshArtifact(null, null, timeout, false, false);
+        assert !cachePolicy.mustRefreshArtifact(null, null, timeout - 1, false, false)
+        if (timeout != FOREVER) {
+            assert cachePolicy.mustRefreshArtifact(null, null, timeout + 1, false, false)
+        }
+        true
+    }
+
+    private def assertId(def moduleId, String group, String name, String version) {
+        assert moduleId.group == group
+        assert moduleId.name == name
+        assert moduleId.version == version
+    }
+
+    private def moduleComponent(String group, String name, String version) {
+        new DefaultModuleComponentIdentifier(group, name, version)
+    }
+
+    private def moduleIdentifier(String group, String name, String version) {
+        new DefaultModuleVersionIdentifier(group, name, version)
+    }
+
+    private def moduleVersion(String group, String name, String version) {
+        return new ResolvedModuleVersion() {
+            ModuleVersionIdentifier getId() {
+                return new DefaultModuleVersionIdentifier(group, name, version);
+            }
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRulesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRulesTest.groovy
new file mode 100644
index 0000000..9a88d1a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultComponentSelectionRulesTest.groovy
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy
+
+import org.gradle.api.Action
+import org.gradle.api.InvalidUserCodeException
+import org.gradle.api.artifacts.ComponentSelection
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ComponentSelectionInternal
+import org.gradle.api.internal.artifacts.DefaultComponentSelection
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.configurations.MutationValidator
+import org.gradle.api.specs.Specs
+import org.gradle.internal.Actions
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.rules.RuleAction
+import org.gradle.internal.rules.RuleActionAdapter
+import org.gradle.internal.typeconversion.NotationParser
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY
+
+class DefaultComponentSelectionRulesTest extends Specification {
+    static final GROUP = "group"
+    static final MODULE = "module"
+    RuleActionAdapter<ComponentSelection> adapter = Mock(RuleActionAdapter)
+    NotationParser<Object, String> notationParser = Mock(NotationParser)
+    DefaultComponentSelectionRules rules = new DefaultComponentSelectionRules(adapter, notationParser)
+    ComponentSelectionInternal componentSelection
+    def ruleAction = Mock(RuleAction)
+    def ruleSource = new Object()
+
+    def setup() {
+        def componentIdentifier = DefaultModuleComponentIdentifier.newId(GROUP, MODULE, "version")
+        componentSelection = new DefaultComponentSelection(componentIdentifier)
+    }
+
+    def "add closure rule that applies to all components"() {
+        def input = { ComponentSelection cs ->  }
+
+        when:
+        rules.all input
+
+        then:
+        1 * adapter.createFromClosure(ComponentSelection, input) >> ruleAction
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add closure rule that applies to module"() {
+        def input = { ComponentSelection cs ->  }
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        rules.withModule(notation, input)
+
+        then:
+        1 * adapter.createFromClosure(ComponentSelection, input) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "add action rule that applies to all components"() {
+        def Action<ComponentSelection> action = Mock(Action)
+
+        when:
+        rules.all action
+
+        then:
+        1 * adapter.createFromAction(action) >> ruleAction
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add action rule that applies to module"() {
+        def Action<ComponentSelection> action = Mock(Action)
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        rules.withModule(notation, action)
+
+        then:
+        1 * adapter.createFromAction(action) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "add rule source rule that applies to all components"() {
+        when:
+        rules.all ruleSource
+
+        then:
+        1 * adapter.createFromRuleSource(ComponentSelection, ruleSource) >> ruleAction
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec == Specs.satisfyAll()
+    }
+
+    def "add rule source rule that applies to module"() {
+        def notation = "${GROUP}:${MODULE}"
+
+        when:
+        rules.withModule(notation, ruleSource)
+
+        then:
+        1 * adapter.createFromRuleSource(ComponentSelection, ruleSource) >> ruleAction
+        1 * notationParser.parseNotation(notation) >> DefaultModuleIdentifier.newId(GROUP, MODULE)
+
+        and:
+        rules.rules.size() == 1
+        rules.rules[0].action == ruleAction
+        rules.rules[0].spec.target == DefaultModuleIdentifier.newId(GROUP, MODULE)
+    }
+
+    def "propagates error creating rule for closure" () {
+        when:
+        rules.all { }
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad closure"
+
+        and:
+        1 * adapter.createFromClosure(ComponentSelection, _) >> { throw new InvalidUserCodeException("bad closure") }
+
+        when:
+        rules.withModule("group:module") { }
+
+        then:
+        e = thrown(InvalidUserCodeException)
+        e.message == "bad targeted closure"
+
+        and:
+        1 * adapter.createFromClosure(ComponentSelection, _) >> { throw new InvalidUserCodeException("bad targeted closure") }
+    }
+
+    def "propagates error creating rule for rule source" () {
+        when:
+        rules.all ruleSource
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad rule source"
+
+        and:
+        1 * adapter.createFromRuleSource(ComponentSelection, ruleSource) >> { throw new InvalidUserCodeException("bad rule source") }
+
+        when:
+        rules.withModule("group:module", ruleSource)
+
+        then:
+        e = thrown(InvalidUserCodeException)
+        e.message == "bad targeted rule source"
+
+        and:
+        1 * adapter.createFromRuleSource(ComponentSelection, ruleSource) >> { throw new InvalidUserCodeException("bad targeted rule source") }
+    }
+
+    def "propagates error creating rule for action" () {
+        def action = Mock(Action)
+
+        when:
+        rules.all action
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "bad action"
+
+        and:
+        1 * adapter.createFromAction(action) >> { throw new InvalidUserCodeException("bad action") }
+
+        when:
+        rules.withModule("group:module", action)
+
+        then:
+        e = thrown(InvalidUserCodeException)
+        e.message == "bad targeted action"
+
+        and:
+        1 * adapter.createFromAction(action) >> { throw new InvalidUserCodeException("bad targeted action") }
+    }
+
+    def "propagates error parsing module identifier for closure" () {
+        def input = { ComponentSelection cs -> }
+        def notation = "group:module:1.0"
+
+        when:
+        rules.withModule(notation, input)
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "Could not add a component selection rule for module '${notation}'."
+        def cause = e.cause
+        cause instanceof UnsupportedNotationException
+        cause.notation == notation
+
+        and:
+        1 * notationParser.parseNotation(notation) >> { throw new UnsupportedNotationException(notation) }
+    }
+
+    def "propagates error parsing module identifier for action" () {
+        def input = Mock(Action)
+        def notation = "group:module:1.0"
+
+        when:
+        rules.withModule(notation, input)
+
+        then:
+        def e = thrown(InvalidUserCodeException)
+        e.message == "Could not add a component selection rule for module '${notation}'."
+        def cause = e.cause
+        cause instanceof UnsupportedNotationException
+        cause.notation == notation
+
+        and:
+        1 * notationParser.parseNotation(notation) >> { throw new UnsupportedNotationException(notation) }
+    }
+
+    def "ComponentSelectionSpec matches on group and name" () {
+        def spec = new DefaultComponentSelectionRules.ComponentSelectionMatchingSpec(DefaultModuleIdentifier.newId(group, name))
+        def candidate = Mock(ModuleComponentIdentifier) {
+            1 * getGroup() >> "org.gradle"
+            (0..1) * getModule() >> "api"
+        }
+        def selection = Stub(ComponentSelection) {
+            getCandidate() >> candidate
+        }
+
+        expect:
+        spec.isSatisfiedBy(selection) == matches
+
+        where:
+        group        | name  | matches
+        "org.gradle" | "api" | true
+        "org.gradle" | "lib" | false
+        "com.gradle" | "api" | false
+    }
+
+    def "mutation is checked for public API"() {
+        def checker = Mock(MutationValidator)
+        rules.beforeChange(checker)
+
+        when: rules.all(Actions.doNothing())
+        then: 1 * checker.validateMutation(STRATEGY)
+
+        when: rules.all(Closure.IDENTITY)
+        then: 1 * checker.validateMutation(STRATEGY)
+
+        when: rules.all(ruleSource)
+        then: 1 * checker.validateMutation(STRATEGY)
+
+        when: rules.withModule("something", Actions.doNothing())
+        then: 1 * checker.validateMutation(STRATEGY)
+
+        when: rules.withModule("something", Closure.IDENTITY)
+        then: 1 * checker.validateMutation(STRATEGY)
+
+        when: rules.withModule("something", ruleSource)
+        then: 1 * checker.validateMutation(STRATEGY)
+    }
+
+    private class TestComponentSelectionAction implements Action<ComponentSelection> {
+        boolean called = false
+
+        @Override
+        void execute(ComponentSelection componentSelection) {
+            called = true
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutionsSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutionsSpec.groovy
new file mode 100644
index 0000000..24b87b4
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultDependencySubstitutionsSpec.groovy
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy
+
+import org.gradle.api.Action
+import org.gradle.api.Project
+import org.gradle.api.artifacts.ModuleDependencySubstitution
+import org.gradle.api.artifacts.ProjectDependencySubstitution
+import org.gradle.api.internal.artifacts.*
+import org.gradle.api.internal.artifacts.configurations.MutationValidator
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY
+
+class DefaultDependencySubstitutionsSpec extends Specification {
+    DependencySubstitutionsInternal substitutions;
+
+    def setup() {
+        substitutions = new DefaultDependencySubstitutions()
+    }
+
+    def "provides no op resolve rule when no rules or forced modules configured"() {
+        given:
+        def details = Mock(DependencySubstitutionInternal)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(details)
+
+        then:
+        0 * details._
+    }
+
+    def "all() matches modules and projects"() {
+        given:
+        def action = Mock(Action)
+        substitutions.all(action)
+
+        def moduleDetails = Mock(ModuleDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.requested >> DefaultModuleComponentSelector.newSelector("org.utils", "api", "1.5")
+        1 * action.execute(moduleDetails)
+        0 * _
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        1 * action.execute(projectDetails)
+        0 * _
+    }
+
+    def "allWithDependencyResolveDetails() wraps substitution in legacy format"() {
+        given:
+        def action = Mock(Action)
+        substitutions.allWithDependencyResolveDetails(action)
+
+        def moduleOldRequested = DefaultModuleVersionSelector.newSelector("org.utils", "api", "1.5")
+        def moduleTarget = DefaultModuleComponentSelector.newSelector(moduleOldRequested)
+        def moduleDetails = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.target >> moduleTarget
+        _ * moduleDetails.oldRequested >> moduleOldRequested
+        1 * action.execute({ DependencyResolveDetailsInternal details ->
+            details.requested == moduleOldRequested
+        })
+        0 * _
+
+        def projectOldRequested = DefaultModuleVersionSelector.newSelector("org.utils", "api", "1.5")
+        def projectTarget = DefaultProjectComponentSelector.newSelector(":api")
+        def projectDetails = Mock(ProjectDependencySubstitutionInternal)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.target >> projectTarget
+        _ * projectDetails.oldRequested >> projectOldRequested
+        1 * action.execute({ DependencyResolveDetailsInternal details ->
+            details.requested == projectOldRequested
+        })
+        0 * _
+    }
+
+    def "eachModule() matches only modules"() {
+        given:
+        def action = Mock(Action)
+        substitutions.eachModule(action)
+
+        def moduleDetails = Mock(ModuleDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.requested >> DefaultModuleComponentSelector.newSelector("org.utils", "api", "1.5")
+        1 * action.execute(moduleDetails)
+        0 * _
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        0 * _
+    }
+
+    @Unroll
+    def "withModule() matches only given module: #matchingModule"() {
+        given:
+        def matchingModuleVersionAction = Mock(Action)
+        def nonMatchingModuleVersionAction = Mock(Action)
+
+        substitutions.withModule(matchingModule, matchingModuleVersionAction)
+        substitutions.withModule(nonMatchingModule, nonMatchingModuleVersionAction)
+
+        def moduleDetails = Mock(ModuleDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.requested >> DefaultModuleComponentSelector.newSelector("org.utils", "api", "1.5")
+        1 * matchingModuleVersionAction.execute(moduleDetails)
+        0 * nonMatchingModuleVersionAction.execute(_)
+        0 * _
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        0 * _
+
+        where:
+        matchingModule                    | nonMatchingModule
+        "org.utils:api"                   | "org.utils:impl"
+        [group: "org.utils", name: "api"] | [group: "org.utils", name: "impl"]
+    }
+
+    def "withModule() does not match projects"() {
+        given:
+        def action = Mock(Action)
+
+        substitutions.withModule("org.utils:api", action)
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        0 * _
+    }
+
+    def "eachProject() matches only projects"() {
+        given:
+        def action = Mock(Action)
+        substitutions.eachProject(action)
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        1 * action.execute(projectDetails)
+        0 * _
+
+        def moduleDetails = Mock(ModuleDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.requested >> DefaultModuleComponentSelector.newSelector("org.utils", "api", "1.5")
+        0 * _
+    }
+
+    @Unroll
+    def "withProject() matches only given project: #matchingProject"() {
+        given:
+        def matchingProjectAction = Mock(Action)
+        def nonMatchingProjectAction = Mock(Action)
+
+        substitutions.withProject(matchingProject, matchingProjectAction)
+        substitutions.withProject(nonMatchingProject, nonMatchingProjectAction)
+
+        def projectDetails = Mock(ProjectDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(projectDetails)
+
+        then:
+        _ * projectDetails.requested >> DefaultProjectComponentSelector.newSelector(":api")
+        1 * matchingProjectAction.execute(projectDetails)
+        0 * nonMatchingProjectAction.execute(_)
+        0 * _
+
+        where:
+        matchingProject                                             | nonMatchingProject
+        ":api"                                                      | ":impl"
+        Mock(Project) { Project project -> project.path >> ":api" } | Mock(Project) { Project project -> project.path >> ":impl" }
+    }
+
+    def "withProject() does not match modules"() {
+        def action = Mock(Action)
+        substitutions.withProject(":api", action)
+        def moduleDetails = Mock(ModuleDependencySubstitution)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(moduleDetails)
+
+        then:
+        _ * moduleDetails.requested >> DefaultModuleComponentSelector.newSelector("org.utils", "api", "1.5")
+        0 * _
+    }
+
+    def "provides dependency substitution rule that orderly aggregates user specified rules"() {
+        given:
+        substitutions.eachModule({ it.useVersion("1.0") } as Action)
+        substitutions.eachModule({ it.useVersion("2.0") } as Action)
+        substitutions.eachModule({ it.useVersion("3.0") } as Action)
+        def details = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        substitutions.dependencySubstitutionRule.execute(details)
+
+        then:
+        1 * details.useVersion("1.0")
+        then:
+        1 * details.useVersion("2.0")
+        then:
+        1 * details.useVersion("3.0")
+        0 * details._
+    }
+    
+    def "mutations trigger lenient validation"() {
+        given:
+        def validator = Mock(MutationValidator)
+        substitutions.beforeChange(validator)
+        
+        when: substitutions.all(Mock(Action))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.all(Mock(Closure))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.eachModule(Mock(Action))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.eachModule(Mock(Closure))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.withModule("org:foo", Mock(Action))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.withModule("org:foo", Mock(Closure))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.eachProject(Mock(Action))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.eachProject(Mock(Closure))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.withProject(":foo", Mock(Action))
+        then: 1 * validator.validateMutation(STRATEGY)
+        
+        when: substitutions.withProject(":foo", Mock(Closure))
+        then: 1 * validator.validateMutation(STRATEGY)
+    }
+
+    def "mutating copy does not trigger original validator"() {
+        given:
+        def validator = Mock(MutationValidator)
+        substitutions.beforeChange(validator)
+        def copy = substitutions.copy()
+
+        when:
+        copy.all(Mock(Action))
+        copy.all(Mock(Closure))
+        copy.eachModule(Mock(Action))
+        copy.eachModule(Mock(Closure))
+        copy.withModule("org:foo", Mock(Action))
+        copy.withModule("org:foo", Mock(Closure))
+        copy.eachProject(Mock(Action))
+        copy.eachProject(Mock(Closure))
+        copy.withProject(":foo", Mock(Action))
+        copy.withProject(":foo", Mock(Closure))
+
+        then:
+        0 * validator.validateMutation(_)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
new file mode 100644
index 0000000..acc5c9c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategySpec.groovy
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.resolutionstrategy
+
+import org.gradle.api.Action
+import org.gradle.api.artifacts.ComponentSelection
+import org.gradle.api.artifacts.ComponentSelectionRules
+import org.gradle.api.internal.artifacts.ModuleDependencySubstitutionInternal
+import org.gradle.api.internal.artifacts.configurations.MutationValidator
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import org.gradle.internal.Actions
+import org.gradle.internal.rules.NoInputsRuleAction
+import spock.lang.Specification
+
+import java.util.concurrent.TimeUnit
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import static org.gradle.api.internal.artifacts.configurations.MutationValidator.MutationType.STRATEGY
+
+public class DefaultResolutionStrategySpec extends Specification {
+
+    def cachePolicy = Mock(DefaultCachePolicy)
+    def dependencySubstitutions = Mock(DependencySubstitutionsInternal)
+    def strategy = new DefaultResolutionStrategy(cachePolicy, dependencySubstitutions)
+
+    def "allows setting forced modules"() {
+        expect:
+        strategy.forcedModules.empty
+
+        when:
+        strategy.force 'org.foo:bar:1.0', 'org.foo:baz:2.0'
+
+        then:
+        def versions = strategy.forcedModules as List
+        versions.size() == 2
+
+        versions[0].group == 'org.foo'
+        versions[0].name == 'bar'
+        versions[0].version == '1.0'
+
+        versions[1].group == 'org.foo'
+        versions[1].name == 'baz'
+        versions[1].version == '2.0'
+    }
+
+    def "allows replacing forced modules"() {
+        given:
+        strategy.force 'org.foo:bar:1.0'
+
+        when:
+        strategy.forcedModules = ['hello:world:1.0', [group:'g', name:'n', version:'1']]
+
+        then:
+        def versions = strategy.forcedModules as List
+        versions.size() == 2
+        versions[0].group == 'hello'
+        versions[1].group == 'g'
+    }
+
+    def "provides dependency resolve rule that forces modules"() {
+        given:
+        strategy.force 'org:bar:1.0', 'org:foo:2.0'
+        def details = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        strategy.dependencySubstitutionRule.execute(details)
+
+        then:
+        _ * dependencySubstitutions.dependencySubstitutionRule >> Actions.doNothing()
+        _ * details.getOldRequested() >> newSelector("org", "foo", "1.0")
+        1 * details.useVersion("2.0", VersionSelectionReasons.FORCED)
+        0 * details._
+    }
+
+    def "eachDependency calls through to substitution rules"() {
+        given:
+        def action = Mock(Action)
+
+        when:
+        strategy.eachDependency(action)
+
+        then:
+        1 * dependencySubstitutions.allWithDependencyResolveDetails(action)
+    }
+
+    def "provides dependency resolve rule with forced modules first and then user specified rules"() {
+        given:
+        strategy.force 'org:bar:1.0', 'org:foo:2.0'
+        def details = Mock(ModuleDependencySubstitutionInternal)
+        def substitutionAction = Mock(Action)
+
+        when:
+        strategy.dependencySubstitutionRule.execute(details)
+
+        then: //forced modules:
+        dependencySubstitutions.dependencySubstitutionRule >> substitutionAction
+        _ * details.oldRequested >> newSelector("org", "foo", "1.0")
+        1 * details.useVersion("2.0", VersionSelectionReasons.FORCED)
+
+        then: //user rules follow:
+        1 * substitutionAction.execute(details)
+        0 * details._
+    }
+
+    def "copied instance does not share state"() {
+        when:
+        def copy = strategy.copy()
+
+        then:
+        1 * cachePolicy.copy() >> Mock(DefaultCachePolicy)
+        !copy.is(strategy)
+        !copy.cachePolicy.is(strategy.cachePolicy)
+        !copy.componentSelection.is(strategy.componentSelection)
+    }
+
+    def "provides a copy"() {
+        given:
+        def newCachePolicy = Mock(DefaultCachePolicy)
+        cachePolicy.copy() >> newCachePolicy
+        def newDependencySubstitutions = Mock(DependencySubstitutionsInternal)
+        dependencySubstitutions.copy() >> newDependencySubstitutions
+
+        strategy.failOnVersionConflict()
+        strategy.force("org:foo:1.0")
+        strategy.componentSelection.rules.add(new NoInputsRuleAction<ComponentSelection>({}))
+
+        when:
+        def copy = strategy.copy()
+
+        then:
+        copy.forcedModules == strategy.forcedModules
+        copy.componentSelection.rules == strategy.componentSelection.rules
+        copy.conflictResolution instanceof StrictConflictResolution
+
+        strategy.cachePolicy == cachePolicy
+        copy.cachePolicy == newCachePolicy
+
+        strategy.dependencySubstitution == dependencySubstitutions
+        copy.dependencySubstitution == newDependencySubstitutions
+    }
+
+    def "configures changing modules cache with jdk5+ units"() {
+        when:
+        strategy.cacheChangingModulesFor(30000, "milliseconds")
+
+        then:
+        1 * cachePolicy.cacheChangingModulesFor(30000, TimeUnit.MILLISECONDS)
+    }
+
+    def "configures changing modules cache with jdk6+ units"() {
+        when:
+        strategy.cacheChangingModulesFor(5, "minutes")
+
+        then:
+        1 * cachePolicy.cacheChangingModulesFor(5 * 60 * 1000, TimeUnit.MILLISECONDS)
+    }
+
+    def "configures dynamic version cache with jdk5+ units"() {
+        when:
+        strategy.cacheDynamicVersionsFor(10000, "milliseconds")
+
+        then:
+        1 * cachePolicy.cacheDynamicVersionsFor(10000, TimeUnit.MILLISECONDS)
+    }
+
+    def "configures dynamic version cache with jdk6+ units"() {
+        when:
+        strategy.cacheDynamicVersionsFor(1, "hours")
+
+        then:
+        1 * cachePolicy.cacheDynamicVersionsFor(1 * 60 * 60 * 1000, TimeUnit.MILLISECONDS)
+    }
+
+    def "mutation is checked for public API"() {
+        def validator = Mock(MutationValidator)
+        strategy.beforeChange(validator)
+
+        when: strategy.failOnVersionConflict()
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: strategy.force("org.utils:api:1.3")
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: strategy.forcedModules = ["org.utils:api:1.4"]
+        then: (1.._) * validator.validateMutation(STRATEGY)
+
+        // DependencySubstitutionsInternal.allWithDependencyResolveDetails() will call back to validateMutation() instead
+        when: strategy.eachDependency(Actions.doNothing())
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: strategy.componentSelection.all(Actions.doNothing())
+        then: 1 * validator.validateMutation(STRATEGY)
+
+        when: strategy.componentSelection(new Action<ComponentSelectionRules>() {
+            @Override
+            void execute(ComponentSelectionRules componentSelectionRules) {
+                componentSelectionRules.all(Actions.doNothing())
+            }
+        })
+        then: 1 * validator.validateMutation(STRATEGY)
+    }
+
+    def "mutation is not checked for copy"() {
+        given:
+        cachePolicy.copy() >> Mock(DefaultCachePolicy)
+        dependencySubstitutions.copy() >> Mock(DependencySubstitutionsInternal)
+        def validator = Mock(MutationValidator)
+        strategy.beforeChange(validator)
+        def copy = strategy.copy()
+
+        when: copy.failOnVersionConflict()
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.force("org.utils:api:1.3")
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.forcedModules = ["org.utils:api:1.4"]
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.componentSelection.all(Actions.doNothing())
+        then: 0 * validator.validateMutation(_)
+
+        when: copy.componentSelection(new Action<ComponentSelectionRules>() {
+            @Override
+            void execute(ComponentSelectionRules componentSelectionRules) {
+                componentSelectionRules.all(Actions.doNothing())
+            }
+        })
+        then: 0 * validator.validateMutation(_)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
new file mode 100644
index 0000000..61cd944
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/ModuleForcingResolveRuleSpec.groovy
@@ -0,0 +1,89 @@
+/*
+ * 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.resolutionstrategy
+
+import org.gradle.api.internal.artifacts.ModuleDependencySubstitutionInternal
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class ModuleForcingResolveRuleSpec extends Specification {
+
+    def "forces modules"() {
+        given:
+        def details = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        new ModuleForcingResolveRule([
+            newSelector("org",  "module1", "1.0"),
+            newSelector("org",  "module2", "2.0"),
+            //exotic module with colon in the name
+            newSelector("org",  "module:with:colon", "3.0"),
+            newSelector("org:with:colon",  "module2", "4.0")
+        ]).execute(details)
+
+        then:
+        _ * details.getOldRequested() >> requested
+        1 * details.useVersion(forcedVersion, VersionSelectionReasons.FORCED)
+        0 * details._
+
+        where:
+        requested                                        | forcedVersion
+        newSelector("org",  "module2", "0.9")            | "2.0"
+        newSelector("org",  "module2", "2.1")            | "2.0"
+        newSelector("org",  "module:with:colon", "2.0")  | "3.0"
+        newSelector("org:with:colon",  "module2", "5.0") | "4.0"
+    }
+
+    def "does not force modules if they dont match"() {
+        given:
+        def details = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        new ModuleForcingResolveRule([
+            newSelector("org",  "module1", "1.0"),
+            newSelector("org",  "module2", "2.0"),
+            newSelector("org",  "module:with:colon", "3.0"),
+            newSelector("org:with:colon",  "module2", "4.0")
+        ]).execute(details)
+
+        then:
+        _ * details.getOldRequested() >> requested
+        0 * details._
+
+        where:
+        requested << [
+            newSelector("orgX", "module2", "0.9"),
+            newSelector("org",  "moduleX", "2.9"),
+            newSelector("orgX",  "module:with:colon", "2.9"),
+            newSelector("org:with:colon",  "moduleX", "2.9"),
+            newSelector("org:with",  "colon:module2", "2.9"),
+            newSelector("org",  "with:colon:module2", "2.9"),
+        ]
+    }
+
+    def "does not force anything when input empty"() {
+        def details = Mock(ModuleDependencySubstitutionInternal)
+
+        when:
+        new ModuleForcingResolveRule([]).execute(details)
+
+        then:
+        0 * details._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilterTest.groovy
new file mode 100644
index 0000000..95b8484
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultModuleResolutionFilterTest.groovy
@@ -0,0 +1,623 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.DefaultExcludeRule
+import org.apache.ivy.core.module.descriptor.ExcludeRule
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher
+import org.apache.ivy.plugins.matcher.RegexpPatternMatcher
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultModuleResolutionFilterTest extends Specification {
+    def "accepts all modules default"() {
+        def spec = DefaultModuleResolutionFilter.excludeAny()
+
+        expect:
+        spec.acceptModule(moduleId("org", "module"))
+    }
+
+    def "accepts all artifacts by default"() {
+        def spec = DefaultModuleResolutionFilter.excludeAny()
+
+        expect:
+        spec.acceptArtifact(moduleId("org", "module"), artifactName("test", "jar", "jar"))
+    }
+
+    def "default specs accept the same modules as each other"() {
+        expect:
+        DefaultModuleResolutionFilter.excludeAny().acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny())
+    }
+
+    @Unroll
+    def "does not accept module that matches single module exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        !spec.acceptModule(moduleId('org', 'module'))
+
+        where:
+        rule << [excludeRule('*', '*'),
+                 excludeRule('org', 'module'),
+                 excludeRule('org', '*'),
+                 excludeRule('*', 'module'),
+                 regexpExcludeRule('*', '*'),
+                 regexpExcludeRule('or.*', 'module'),
+                 regexpExcludeRule('org', 'mod.*'),
+                 regexpExcludeRule('or.*', '*'),
+                 regexpExcludeRule('*', 'mod.*')]
+    }
+
+    @Unroll
+    def "accepts module that doesn't match single module exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        spec.acceptModule(moduleId('org', 'module'))
+
+        where:
+        rule << [excludeRule('org2', 'module2'),
+                 excludeRule('*', 'module2'),
+                 excludeRule('org2', '*'),
+                 regexpExcludeRule('or.*2', "module"),
+                 regexpExcludeRule('org', "mod.*2"),
+                 regexpExcludeRule('or.*2', "*"),
+                 regexpExcludeRule('*', "mod.*2")]
+    }
+
+    @Unroll
+    def "module exclude rule selects the same modules as itself (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+        def same = DefaultModuleResolutionFilter.excludeAny(rule)
+        def all = DefaultModuleResolutionFilter.excludeAny()
+        def otherRule = DefaultModuleResolutionFilter.excludeAny(excludeRule('*', 'other'))
+        def artifactRule = DefaultModuleResolutionFilter.excludeAny(excludeRule('*', 'other', 'thing', '*', '*'))
+
+        then:
+        spec.acceptsSameModulesAs(spec)
+        spec.acceptsSameModulesAs(same)
+        !spec.acceptsSameModulesAs(all)
+        !spec.acceptsSameModulesAs(otherRule)
+        !spec.acceptsSameModulesAs(artifactRule)
+
+        where:
+        rule << [excludeRule('*', '*'),
+                 excludeRule('*', 'module'),
+                 excludeRule('org', '*'),
+                 excludeRule('org', 'module'),
+                 regexpExcludeRule('or.*', "module"),
+                 regexpExcludeRule('org', "mod.*")]
+    }
+
+    @Unroll
+    def "accepts module for every artifact exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        spec.acceptModule(moduleId('org', 'module'))
+
+        where:
+        rule << [excludeRule('*', '*', 'artifact'),
+                 excludeRule('org', '*', 'artifact'),
+                 excludeRule('org', 'module', 'artifact'),
+                 regexpExcludeRule('.*', "m.*", 'artifact')]
+    }
+
+    @Unroll
+    def "accepts artifact for every module exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        spec.acceptArtifact(moduleId('org', 'module'), artifactName('name', 'jar', 'jar'))
+
+        where:
+        rule << [excludeRule('*', '*'),
+                 excludeRule('org', 'module'),
+                 excludeRule('org', '*'),
+                 excludeRule('*', 'module'),
+                 regexpExcludeRule('*', '*'),
+                 regexpExcludeRule('or.*', 'module'),
+                 regexpExcludeRule('org', 'mod.*'),
+                 regexpExcludeRule('or.*', '*'),
+                 regexpExcludeRule('*', 'mod.*')]
+    }
+
+    @Unroll
+    def "does not accept artifact that matches single artifact exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        !spec.acceptArtifact(moduleId('org', 'module'), artifactName('mylib', 'jar', 'jar'))
+
+        where:
+        rule << [excludeRule('org', 'module', 'mylib', 'jar', 'jar'),
+                 excludeRule('org', 'module', '*', 'jar', 'jar'),
+                 excludeRule('org', 'module', 'mylib', '*', 'jar'),
+                 excludeRule('org', 'module', 'mylib', 'jar', '*'),
+                 regexpExcludeRule('org', 'module', 'my.*', 'jar', 'jar'),
+                 regexpExcludeRule('org', 'module', 'my.*', '*', '*'),
+                 regexpExcludeRule('org', 'module', 'mylib', 'j.*', 'jar'),
+                 regexpExcludeRule('org', 'module', '*', 'j.*', 'jar'),
+                 regexpExcludeRule('org', 'module', 'mylib', 'jar', 'j.*'),
+                 regexpExcludeRule('org', 'module', 'mylib', '*', 'j.*')]
+    }
+
+    @Unroll
+    def "accepts artifact that doesn't match single artifact exclude rule (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+
+        then:
+        spec.acceptArtifact(moduleId('org', 'module'), artifactName('mylib', 'jar', 'jar'))
+
+        where:
+        rule << [excludeRule('*', 'module', '*', '*', '*'),
+                 excludeRule('org', '*', '*', '*', '*'),
+                 excludeRule('org', 'module', '*', '*', '*'),
+                 excludeRule('*', 'module2', '*', '*', '*'),
+                 excludeRule('org2', '*', '*', '*', '*'),
+                 excludeRule('org2', 'module2', '*', '*', '*'),
+                 excludeRule('org', 'module', 'mylib', 'sources', 'jar'),
+                 excludeRule('org', 'module', 'mylib', 'jar', 'war'),
+                 excludeRule('org', 'module', 'otherlib', 'jar', 'jar'),
+                 excludeRule('org', 'module', 'otherlib', '*', '*'),
+                 excludeRule('org', 'module', 'otherlib', '*', 'jar'),
+                 excludeRule('org', 'module', 'otherlib', 'jar', '*'),
+                 excludeRule('org', 'module', '*', 'sources', 'jar'),
+                 excludeRule('org', 'module', '*', 'sources', '*'),
+                 excludeArtifactRule('mylib', 'sources', 'jar'),
+                 excludeArtifactRule('mylib', 'jar', 'war'),
+                 excludeArtifactRule('otherlib', 'jar', 'jar'),
+                 excludeArtifactRule('otherlib', '*', '*'),
+                 excludeArtifactRule('*', 'sources', 'jar'),
+                 excludeArtifactRule('otherlib', '*', 'jar'),
+                 excludeArtifactRule('otherlib', 'jar', '*'),
+                 regexpExcludeRule('or.*2', 'module', '*', '*', '*'),
+                 regexpExcludeRule('org', 'mod.*2', '*', '*', '*'),
+                 regexpExcludeRule('org', 'module', 'my.*2', '*', '*'),
+                 regexpExcludeRule('org', 'module', 'mylib', 'j.*2', '*'),
+                 regexpExcludeRule('org', 'module', 'mylib', 'jar', 'j.*2'),
+                 regexpExcludeArtifactRule('my.*2', '*', '*'),
+                 regexpExcludeArtifactRule('mylib', 'j.*2', '*'),
+                 regexpExcludeArtifactRule('mylib', 'jar', 'j.*2')]
+    }
+
+    @Unroll
+    def "artifact exclude rule accepts the same modules as other rules that accept all modules (#rule)"() {
+        when:
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule)
+        def sameRule = DefaultModuleResolutionFilter.excludeAny(rule)
+        def otherRule = DefaultModuleResolutionFilter.excludeAny(excludeRule('*', '*', 'thing', '*', '*'))
+        def all = DefaultModuleResolutionFilter.all()
+        def moduleRule = DefaultModuleResolutionFilter.excludeAny(excludeRule('*', 'module'))
+
+        then:
+        spec.acceptsSameModulesAs(spec)
+        spec.acceptsSameModulesAs(sameRule)
+        spec.acceptsSameModulesAs(otherRule)
+        spec.acceptsSameModulesAs(all)
+        all.acceptsSameModulesAs(spec)
+
+        !spec.acceptsSameModulesAs(moduleRule)
+        !moduleRule.acceptsSameModulesAs(spec)
+
+        spec.acceptsSameModulesAs(spec.union(otherRule))
+        spec.acceptsSameModulesAs(spec.union(moduleRule))
+        spec.acceptsSameModulesAs(spec.intersect(otherRule.union(sameRule)))
+
+        where:
+        rule << [excludeRule('*', '*', '*', 'jar', 'jar'),
+                 excludeRule('org', 'module', 'mylib', 'jar', 'jar'),
+                 excludeRule('org', 'module', '*', 'jar', 'jar'),
+                 excludeRule('org', 'module', 'mylib', '*', 'jar'),
+                 excludeRule('org', 'module', 'mylib', 'jar', '*'),
+                 regexpExcludeRule('org', "module", 'my.*', 'jar', 'jar'),
+                 regexpExcludeRule('org', "module", 'mylib', 'j.*', 'jar'),
+                 regexpExcludeRule('org', "module", 'mylib', 'jar', 'j.*')]
+    }
+
+    def "does not accept module version that matches any exclude rule"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = excludeRule("org2", "*")
+        def rule4 = excludeRule("*", "module4")
+        def rule5 = regexpExcludeRule("regexp-\\d+", "module\\d+")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4, rule5)
+
+        expect:
+        !spec.acceptModule(moduleId("org", "module"))
+        !spec.acceptModule(moduleId("org", "module2"))
+        !spec.acceptModule(moduleId("org2", "anything"))
+        !spec.acceptModule(moduleId("other", "module4"))
+        !spec.acceptModule(moduleId("regexp-72", "module12"))
+        spec.acceptModule(moduleId("org", "other"))
+        spec.acceptModule(moduleId("regexp-72", "other"))
+        spec.acceptModule(moduleId("regexp", "module2"))
+    }
+
+    def "specs with the same set of exclude rules accept the same modules as each other"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = excludeRule("org2", "*")
+        def rule4 = excludeRule("*", "module4")
+        def rule5 = regexpExcludeRule("pattern1", "pattern2")
+        def exactMatchSpec = DefaultModuleResolutionFilter.excludeAny(rule1)
+        def moduleWildcard = DefaultModuleResolutionFilter.excludeAny(rule3)
+        def groupWildcard = DefaultModuleResolutionFilter.excludeAny(rule4)
+        def regexp = DefaultModuleResolutionFilter.excludeAny(rule5)
+        def manyRules = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4, rule5)
+
+        expect:
+        exactMatchSpec.acceptsSameModulesAs(exactMatchSpec)
+        exactMatchSpec.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1))
+
+        !exactMatchSpec.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule2))
+        !exactMatchSpec.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny())
+        !exactMatchSpec.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2))
+
+        moduleWildcard.acceptsSameModulesAs(moduleWildcard)
+        moduleWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule3))
+
+        !moduleWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1))
+        !moduleWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule3))
+        !moduleWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny())
+        !moduleWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(excludeRule("org3", "*")))
+
+        groupWildcard.acceptsSameModulesAs(groupWildcard)
+        groupWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule4))
+
+        !groupWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1))
+        !groupWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule4))
+        !groupWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny())
+        !groupWildcard.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(excludeRule("*", "module5")))
+
+        regexp.acceptsSameModulesAs(regexp)
+        regexp.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule5))
+
+        !regexp.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1))
+        !regexp.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule5))
+        !regexp.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny())
+        !regexp.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(regexpExcludeRule("pattern", "other")))
+
+        manyRules.acceptsSameModulesAs(manyRules)
+        manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4, rule5))
+
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule3, rule4, rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule4, rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4))
+
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, excludeRule("org", "module3"), rule3, rule4, rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, excludeRule("org3", "*"), rule4, rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, excludeRule("*", "module5"), rule5))
+        !manyRules.acceptsSameModulesAs(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4, regexpExcludeRule("other", "other")))
+    }
+
+    def "union with empty spec is empty spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeArtifactRule("b", "jar", "jar")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny()
+
+        expect:
+        spec.union(spec2) == spec2
+        spec2.union(spec) == spec2
+    }
+
+    def "union of a spec with itself returns the original spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = excludeArtifactRule("a", "jar", "jar")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3)
+
+        expect:
+        spec.union(spec) == spec
+    }
+
+    def "union of two specs with the same exclude rule instances returns one of the original specs"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = regexpExcludeRule("org", "module2")
+        def rule3 = excludeRule("org", "*")
+        def rule4 = excludeRule("*", "module")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule2, rule3, rule1, rule4)
+
+        expect:
+        spec.union(spec2) == spec
+    }
+
+    def "union of two specs with exact matching exclude rules uses the intersection of the exclude rules"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = excludeRule("org", "module3")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule1, rule3)
+
+        expect:
+        def union = spec.union(spec2)
+        union == DefaultModuleResolutionFilter.excludeAny(rule1)
+    }
+
+    def "union of spec with module wildcard uses the most specific matching exclude rules"() {
+        def rule1 = excludeRule("org", "*")
+        def rule2 = excludeRule("org", "module")
+        def rule3 = excludeRule("org", "module2")
+        def rule4 = excludeRule("other", "module")
+        def rule5 = excludeRule("*", "module3")
+        def rule6 = excludeRule("org2", "*")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1)
+
+        expect:
+        def union = spec.union(DefaultModuleResolutionFilter.excludeAny(rule2, rule3, rule4))
+        union == DefaultModuleResolutionFilter.excludeAny(rule2, rule3)
+
+        def union2 = spec.union(DefaultModuleResolutionFilter.excludeAny(rule5))
+        union2 == DefaultModuleResolutionFilter.excludeAny(excludeRule("org", "module3"))
+
+        def union3 = spec.union(DefaultModuleResolutionFilter.excludeAny(rule6, rule2))
+        union3 == DefaultModuleResolutionFilter.excludeAny(rule2)
+    }
+
+    def "union of spec with group wildcard uses the most specific matching exclude rules"() {
+        def rule1 = excludeRule("*", "module")
+        def rule2 = excludeRule("org", "module")
+        def rule3 = excludeRule("org", "module2")
+        def rule4 = excludeRule("other", "module")
+        def rule5 = excludeRule("org", "*")
+        def rule6 = excludeRule("*", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1)
+
+        expect:
+        def union = spec.union(DefaultModuleResolutionFilter.excludeAny(rule2, rule3, rule4))
+        union == DefaultModuleResolutionFilter.excludeAny(rule2, rule4)
+
+        def union2 = spec.union(DefaultModuleResolutionFilter.excludeAny(rule5))
+        union2 == DefaultModuleResolutionFilter.excludeAny(excludeRule("org", "module"))
+
+        def union3 = spec.union(DefaultModuleResolutionFilter.excludeAny(rule6))
+        union3 == DefaultModuleResolutionFilter.all()
+    }
+
+    def "union of two specs with disjoint exact matching exclude rules matches all modules"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule2)
+
+        expect:
+        def union = spec.union(spec2)
+        union == DefaultModuleResolutionFilter.all()
+    }
+
+    def "union of module spec and artifact spec uses the artifact spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("*", "module-2")
+        def rule3 = excludeRule("org", "*-2")
+        def artifactRule1 = excludeRule("org", "module", "art", "*", "*")
+        def artifactRule2 = excludeRule("*", "*", "*", "jar", "*")
+        def artifactSpec1 = DefaultModuleResolutionFilter.excludeAny(artifactRule1)
+        def artifactSpec2 = DefaultModuleResolutionFilter.excludeAny(artifactRule1, artifactRule2)
+
+        expect:
+        def union = artifactSpec1.union(DefaultModuleResolutionFilter.excludeAny(rule1))
+        union == artifactSpec1
+
+        def union2 = artifactSpec1.union(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3))
+        union2 == artifactSpec1
+
+        def union3 = artifactSpec2.union(DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3))
+        union3 == artifactSpec2
+    }
+
+    def "union of two specs with non-exact matching exclude rules is a union spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = regexpExcludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule2)
+
+        expect:
+        def union = spec.union(spec2)
+        def specs = []
+        union.unpackUnion(specs)
+        specs.size() == 2
+        specs[0] == spec
+        specs[1] == spec2
+    }
+
+    def "union of union specs is the union of the original specs"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = regexpExcludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec3 = DefaultModuleResolutionFilter.excludeAny(rule3)
+
+        expect:
+        def union = spec.union(spec3).union(spec2)
+
+        union instanceof DefaultModuleResolutionFilter.UnionSpec
+        union.specs.size() == 2
+        union.specs.any {
+            it instanceof DefaultModuleResolutionFilter.ExcludeRuleBackedSpec && it.excludeSpecs == spec.excludeSpecs
+        }
+        union.specs.contains(spec3)
+    }
+
+    def "union accept module that is accepted by any merged exclude rule"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule1)
+
+        expect:
+        def union = spec.union(spec2)
+
+        !spec.acceptModule(moduleId("org", "module"))
+        !union.acceptModule(moduleId("org", "module"))
+
+        !spec.acceptModule(moduleId("org", "module2"))
+        union.acceptModule(moduleId("org", "module2"))
+    }
+
+    def "unions accepts same modules when original specs accept same modules"() {
+        def rule1 = regexpExcludeRule("org", "module")
+        def rule2 = regexpExcludeRule("org", "module2")
+        def rule3 = regexpExcludeRule("org", "module3")
+        def spec1 = DefaultModuleResolutionFilter.excludeAny(rule1)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule2)
+        def spec3 = DefaultModuleResolutionFilter.excludeAny(rule3)
+
+        expect:
+        spec1.union(spec2).acceptsSameModulesAs(spec2.union(spec1))
+
+        !spec1.union(spec2).acceptsSameModulesAs(spec2)
+        !spec1.union(spec2).acceptsSameModulesAs(spec1)
+        !spec1.union(spec2).acceptsSameModulesAs(spec1.union(spec3))
+    }
+
+    def "intersection with empty spec is original spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeArtifactRule("b", "jar", "jar")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny()
+
+        expect:
+        spec.intersect(spec2) == spec
+        spec2.intersect(spec) == spec
+    }
+
+    def "intersection of a spec with itself returns the original spec"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def rule3 = excludeArtifactRule("b", "jar", "jar")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3)
+
+        expect:
+        spec.intersect(spec) == spec
+    }
+
+    def "intersection does not accept module that is not accepted by any merged exclude rules"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule1)
+
+        expect:
+        def intersect = spec.intersect(spec2)
+
+        !spec.acceptModule(moduleId("org", "module"))
+        !intersect.acceptModule(moduleId("org", "module"))
+
+        !spec.acceptModule(moduleId("org", "module2"))
+        !intersect.acceptModule(moduleId("org", "module2"))
+
+        spec.acceptModule(moduleId("org", "module3"))
+        spec2.acceptModule(moduleId("org", "module3"))
+        intersect.acceptModule(moduleId("org", "module3"))
+    }
+
+    def "intersection of two specs with exclude rules is the union of the exclude rules"() {
+        def rule1 = excludeRule("org", "module")
+        def rule2 = excludeRule("org", "module2")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule1)
+
+        expect:
+        def intersection = spec.intersect(spec2)
+        intersection == DefaultModuleResolutionFilter.excludeAny(rule1, rule2)
+    }
+
+    def "intersections accepts same modules when original specs accept same modules"() {
+        def rule1 = regexpExcludeRule("org", "module")
+        def rule2 = regexpExcludeRule("org", "module2")
+        def rule3 = regexpExcludeRule("org", "module3")
+        def spec1 = DefaultModuleResolutionFilter.excludeAny(rule1).union(DefaultModuleResolutionFilter.excludeAny(rule2))
+        def spec2 = DefaultModuleResolutionFilter.excludeAny(rule2).union(DefaultModuleResolutionFilter.excludeAny(rule1))
+        def spec3 = DefaultModuleResolutionFilter.excludeAny(rule3)
+        assert spec1.acceptsSameModulesAs(spec2)
+
+        expect:
+        spec1.intersect(spec2).acceptsSameModulesAs(spec2.intersect(spec1))
+
+        !spec1.intersect(spec2).acceptsSameModulesAs(spec1)
+        !spec1.intersect(spec2).acceptsSameModulesAs(spec2)
+        !spec1.intersect(spec2).acceptsSameModulesAs(spec1.intersect(spec3))
+    }
+
+    def "does not accept artifact that matches specific exclude rule"() {
+        def rule1 = excludeArtifactRule("a", "jar", "jar")
+        def rule2 = excludeArtifactRule("b", "jar", "jar")
+        def rule3 = excludeArtifactRule("c", "*", "*")
+        def rule4 = excludeArtifactRule("d", "*", "jar")
+        def rule5 = excludeArtifactRule("e", "sources", "jar")
+        def rule6 = excludeArtifactRule("f", "sources", "*")
+        def rule7 = excludeArtifactRule("g", "jar", "war")
+        def rule8 = regexpExcludeArtifactRule("regexp-\\d+", "jar", "jar")
+        def spec = DefaultModuleResolutionFilter.excludeAny(rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8)
+
+        expect:
+        !spec.acceptArtifact(moduleId("org", "module"), artifactName("a", "jar", "jar"))
+        !spec.acceptArtifact(moduleId("org", "module2"), artifactName("b", "jar", "jar"))
+        !spec.acceptArtifact(moduleId("org2", "anything"), artifactName("c", "jar", "jar"))
+        !spec.acceptArtifact(moduleId("other", "module4"), artifactName("d", "jar", "jar"))
+        !spec.acceptArtifact(moduleId("some", "app"), artifactName("e", "sources", "jar"))
+        !spec.acceptArtifact(moduleId("foo", "bar"), artifactName("f", "sources", "jar"))
+        !spec.acceptArtifact(moduleId("well", "known"), artifactName("g", "jar", "war"))
+        !spec.acceptArtifact(moduleId("other", "sample"), artifactName("regexp-99", "jar", "jar"))
+        spec.acceptArtifact(moduleId("some", "app"), artifactName("e", "jar", "jar"))
+        spec.acceptArtifact(moduleId("some", "app"), artifactName("e", "javadoc", "jar"))
+        spec.acceptArtifact(moduleId("foo", "bar"), artifactName("f", "jar", "jar"))
+        spec.acceptArtifact(moduleId("well", "known"), artifactName("g", "jar", "jar"))
+        spec.acceptArtifact(moduleId("well", "known"), artifactName("g", "jar", "zip"))
+        spec.acceptArtifact(moduleId("other", "sample"), artifactName("regexp", "jar", "jar"))
+    }
+
+    static specForRule(def spec, ExcludeRule rule) {
+        return spec.moduleId.group == rule.id.moduleId.organisation && spec.moduleId.name == rule.id.moduleId.name
+    }
+
+    def moduleId(String group, String name) {
+        return DefaultModuleIdentifier.newId(group, name);
+    }
+
+    def artifactName(String name, String type, String ext) {
+        return new DefaultIvyArtifactName(name, type, ext)
+    }
+
+    def excludeRule(String org, String module, String name = "*", String type = "*", String ext = "*") {
+        new DefaultExcludeRule(IvyUtil.createArtifactId(org, module, name, type, ext), ExactPatternMatcher.INSTANCE, [:])
+    }
+
+    def excludeArtifactRule(String name, String type, String ext) {
+        excludeRule("*", "*", name, type, ext)
+    }
+
+    def regexpExcludeRule(String org, String module, String name = "*", String type = "*", String ext = "*") {
+        new DefaultExcludeRule(IvyUtil.createArtifactId(org, module, name, type, ext), RegexpPatternMatcher.INSTANCE, [:])
+    }
+
+    def regexpExcludeArtifactRule(String name, String type, String ext) {
+        regexpExcludeRule("*", "*", name, type, ext)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
new file mode 100644
index 0000000..1dcdc07
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
@@ -0,0 +1,1095 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.*
+import org.apache.ivy.core.module.id.ArtifactId
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher
+import org.apache.ivy.plugins.matcher.PatternMatcher
+import org.gradle.api.artifacts.*
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultConflictHandler
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.DefaultResolvedConfigurationBuilder
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsBuilder
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfigurationResultBuilder
+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.ResolutionResultBuilder
+import org.gradle.api.specs.Spec
+import org.gradle.internal.component.external.model.BuildableIvyModuleResolveMetaData
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import org.gradle.internal.component.local.model.DslOriginDependencyMetaDataWrapper
+import org.gradle.internal.component.model.*
+import org.gradle.internal.resolve.ModuleVersionNotFoundException
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.resolver.ArtifactResolver
+import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver
+import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver
+import org.gradle.internal.resolve.resolver.ModuleToComponentResolver
+import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult
+import org.gradle.internal.resolve.result.BuildableComponentIdResolveResult
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult
+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.IvyUtil.createModuleRevisionId
+
+class DependencyGraphBuilderTest extends Specification {
+    def configuration = Mock(ConfigurationInternal)
+    def conflictResolver = Mock(ModuleConflictResolver)
+    def idResolver = Mock(DependencyToComponentIdResolver)
+    def metaDataResolver = Mock(ComponentMetaDataResolver)
+    def artifactResolver = Mock(ArtifactResolver)
+    def resultBuilder = Mock(ResolutionResultBuilder)
+    def projectModelBuilder = Mock(ResolvedProjectConfigurationResultBuilder)
+    def TestMetaData root = project('root')
+    def moduleResolver = Mock(ModuleToComponentResolver)
+    def dependencyToConfigurationResolver = new DefaultDependencyToConfigurationResolver()
+    def moduleReplacements = Mock(ModuleReplacementsData)
+    def builder = new DependencyGraphBuilder(idResolver, metaDataResolver, moduleResolver, artifactResolver, new DefaultConflictHandler(conflictResolver, moduleReplacements), dependencyToConfigurationResolver)
+
+    def setup() {
+        config(root, 'root', 'default')
+        _ * configuration.name >> 'root'
+        _ * configuration.path >> 'root'
+        _ * moduleResolver.resolve(_, _, _) >> { it[2].resolved(root) }
+
+        _ * artifactResolver.resolveModuleArtifacts(_, _, _,) >> { ComponentResolveMetaData module, ComponentUsage context, BuildableArtifactSetResolveResult result ->
+            result.resolved(module.artifacts)
+        }
+    }
+
+    private DefaultLenientConfiguration resolve() {
+        def results = new DefaultResolvedConfigurationBuilder(new TransientConfigurationResultsBuilder(new DummyBinaryStore(), new DummyStore()))
+        builder.resolve(configuration, resultBuilder, results, projectModelBuilder)
+        new DefaultLenientConfiguration(configuration, results, Stub(CacheLockingManager))
+    }
+
+    def "does not resolve a given module selector more than once"() {
+        given:
+        def a = revision("a")
+        def b = revision("b")
+        def c = revision("c")
+        traverses root, a
+        traverses root, b
+        traverses a, c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c)
+    }
+
+    def "correctly notifies the resolution result builder"() {
+        given:
+        def a = revision("a")
+        def b = revision("b")
+        def c = revision("c")
+        def d = revision("d")
+        traverses root, a
+        traverses root, b
+        traverses a, c
+        traversesMissing a, d
+
+        when:
+        resolve()
+
+        then:
+        1 * resultBuilder.start(newId("group", "root", "1.0"), new DefaultProjectComponentIdentifier(":root"))
+        then:
+        1 * resultBuilder.resolvedConfiguration({ it.name == 'root' }, { it*.requested.module == ['a', 'b'] })
+        then:
+        1 * resultBuilder.resolvedConfiguration({ it.name == 'a' }, { it*.requested.module == ['c', 'd'] && it*.failure.count { it != null } == 1 })
+    }
+
+    def "correctly notifies the project configuration result builder"() {
+        given:
+        def a = project("a")
+        def b = project("b")
+        def c = project("c")
+        def d = revision("d")
+        traverses root, a
+        traverses root, b
+        traverses a, c
+        traversesMissing a, d
+
+        when:
+        resolve()
+
+        then:
+        1 * projectModelBuilder.registerRoot({ it.projectPath == ':root'})
+        1 * projectModelBuilder.addProjectComponentResult({ it.projectPath == ':root'}, { it == 'root' })
+        1 * projectModelBuilder.addProjectComponentResult({ it.projectPath == ':a'}, { it == 'default' })
+        1 * projectModelBuilder.addProjectComponentResult({ it.projectPath == ':b'}, { it == 'default' })
+        1 * projectModelBuilder.addProjectComponentResult({ it.projectPath == ':c'}, { it == 'default' })
+        0 * projectModelBuilder._
+    }
+
+    def "honors component replacements"() {
+        given:
+        def a = revision('a') // a->c
+        def b = revision('b') // b->d, replaces a
+        def c = revision('c') //transitive of evicted a
+        def d = revision('d')
+
+        traverses root, a
+        traverses root, b
+        doesNotResolve a, c
+        traverses b, d
+
+        moduleReplacements.getReplacementFor(new DefaultModuleIdentifier("group", "a")) >> new DefaultModuleIdentifier("group", "b")
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            def sel = candidates.find { it.id.name == 'b' }
+            assert sel
+            sel
+        }
+        0 * conflictResolver._
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(b, d)
+    }
+
+    def "does not resolve a given dynamic module selector more than once"() {
+        given:
+        def a = revision("a")
+        def b = revision("b")
+        def c = revision("c")
+        def d = revision("d")
+        traverses root, a
+        traverses root, b
+        traverses root, c
+        traverses a, d, revision: 'latest'
+        doesNotResolve b, d, revision: 'latest'
+        doesNotTraverse c, d
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c, d)
+    }
+
+    def "does not include evicted module when selected module already traversed before conflict detected"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, selected
+        traverses selected, c
+        traverses root, b
+        traverses b, d
+        doesNotTraverse d, evicted // Conflict is deeper than all dependencies of selected module
+        doesNotResolve evicted, e
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            assert candidates*.version == ['1.2', '1.1']
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selected, b, c, d)
+    }
+
+    def "does not include evicted module when evicted module already traversed before conflict detected"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, evicted
+        traverses evicted, c
+        traverses root, b
+        traverses b, d
+        traverses d, selected // Conflict is deeper than all dependencies of other module
+        traverses selected, e
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selected, b, d, e)
+    }
+
+    def "does not include evicted module when path through evicted module is queued for traversal when conflict detected"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, evicted
+        traverses evicted, c
+        doesNotResolve c, d
+        traverses root, b
+        traverses b, selected
+        traverses selected, e
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selected, b, e)
+    }
+
+    def "resolves when path through selected module is queued for traversal when conflict detected"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, selected
+        traverses selected, b
+        doesNotTraverse root, evicted
+        doesNotResolve evicted, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            assert candidates*.version == ['1.2', '1.1']
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selected, b)
+    }
+
+    def "does not include evicted module when another path through evicted module traversed after conflict detected"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        traverses root, evicted
+        doesNotResolve evicted, d
+        traverses root, selected
+        traverses selected, c
+        traverses root, b
+        doesNotResolve b, evicted
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            assert candidates*.version == ['1.1', '1.2']
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selected, b, c)
+    }
+
+    def "restarts conflict resolution when later conflict on same module discovered"() {
+        given:
+        def selectedA = revision('a', '1.2')
+        def evictedA1 = revision('a', '1.1')
+        def evictedA2 = revision('a', '1.0')
+        def selectedB = revision('b', '2.2')
+        def evictedB = revision('b', '2.1')
+        def c = revision('c')
+        traverses root, evictedA1
+        traverses root, selectedA
+        traverses selectedA, c
+        traverses root, evictedB
+        traverses root, selectedB
+        doesNotTraverse selectedB, evictedA2
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select({ it*.version == ['1.1', '1.2'] }) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+        1 * conflictResolver.select({ it*.version == ['2.1', '2.2'] }) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '2.2' }
+        }
+        1 * conflictResolver.select({ it*.version == ['1.1', '1.2', '1.0'] }) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(selectedA, c, selectedB)
+    }
+
+    def "does not include module version that is excluded after conflict resolution has been applied"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def evicted = revision('c', '1')
+        def selected = revision('c', '2')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, evicted
+        traverses root, a, exclude: b
+        doesNotResolve evicted, a
+        traverses a, b
+        traverses root, d
+        traverses d, e
+        traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(a, selected, d, e)
+    }
+
+    def "does not include dependencies of module version that is no longer transitive after conflict resolution has been applied"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def evicted = revision('c', '1')
+        def selected = revision('c', '2')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, evicted
+        traverses root, a, transitive: false
+        doesNotResolve evicted, a
+        traverses a, b
+        traverses root, d
+        traverses d, e
+        traverses e, selected // conflict is deeper than 'b', to ensure 'b' has been visited
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select({ it*.version == ['1', '2'] }) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '2' }
+        }
+        0 * conflictResolver._
+
+        and:
+        modules(result) == ids(a, selected, d, e)
+    }
+
+    def "does not attempt to resolve a dependency whose target module is excluded earlier in the path"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses a, b, exclude: c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b)
+    }
+
+    def "does not include the artifacts of evicted modules"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        traverses root, selected
+        doesNotTraverse root, evicted
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+
+        and:
+        artifacts(result) == ids(selected)
+    }
+
+    def "does not include the artifacts of excluded modules when excluded by all paths"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        traverses root, a
+        traverses a, b, exclude: c
+        doesNotResolve b, c
+        traverses root, d, exclude: c
+        doesNotResolve d, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, d)
+        artifacts(result) == ids(a, b, d)
+    }
+
+    def "includes a module version when there is a path to the version that does not exclude it"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        traverses root, a
+        traverses a, b, exclude: c
+        doesNotResolve b, c
+        traverses root, d
+        traverses d, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c, d)
+        artifacts(result) == ids(a, b, c, d)
+    }
+
+    def "ignores a new incoming path that includes a subset of those already included"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses a, b
+        traverses root, c, exclude: b
+        doesNotResolve c, a
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c)
+        artifacts(result) == ids(a, b, c)
+    }
+
+    def "ignores a new incoming path that includes the same set of module versions"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, a, exclude: e
+        traverses a, b
+        traverses a, c
+        traverses b, d
+        doesNotResolve c, d
+        doesNotResolve d, e
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c, d)
+        artifacts(result) == ids(a, b, c, d)
+    }
+
+    def "restarts traversal when new incoming path excludes fewer module versions"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a, exclude: b
+        traverses root, c
+        doesNotResolve c, a
+        traverses a, b
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b, c)
+        artifacts(result) == ids(a, b, c)
+    }
+
+    def "does not traverse outgoing paths of a non-transitive dependency"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses a, b, transitive: false
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(a, b)
+        artifacts(result) == ids(a, b)
+    }
+
+    def "reports shortest incoming paths for a failed dependency"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses root, b
+        doesNotResolve b, a
+        traversesBroken a, c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
+        unresolved.problem instanceof ModuleVersionResolveException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionResolveException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0"
+        e.cause.message.contains "group:root:1.0 > group:b:1.0"
+        !e.cause.message.contains("group:root:1.0 > group:b:1.0 > group:a:1.0")
+    }
+
+    def "reports failure to resolve version selector to module version"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        traverses root, a
+        traverses root, b
+        doesNotResolve b, a
+        brokenSelector a, 'unknown'
+        doesNotResolve b, revision('unknown')
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'unknown', '1.0')
+        unresolved.problem instanceof ModuleVersionResolveException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionResolveException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0"
+        e.cause.message.contains "group:root:1.0 > group:b:1.0"
+    }
+
+    def "merges all failures for all dependencies with a given module version selector"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses root, b
+        traversesBroken a, c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
+        unresolved.problem instanceof ModuleVersionResolveException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionResolveException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0"
+        e.cause.message.contains "group:root:1.0 > group:b:1.0"
+    }
+
+    def "reports shortest incoming paths for a missing module version"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses root, b
+        doesNotResolve b, a
+        traversesMissing a, c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
+        unresolved.problem instanceof ModuleVersionNotFoundException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionNotFoundException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0"
+        e.cause.message.contains "group:root:1.0 > group:b:1.0"
+        !e.cause.message.contains("group:root:1.0 > group:b:1.0 > group:a:1.0")
+    }
+
+    def "merges all dependencies with a given module version selector when reporting missing version"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses root, b
+        traversesMissing a, c
+        doesNotResolve b, c
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
+        unresolved.problem instanceof ModuleVersionResolveException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionNotFoundException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0"
+        e.cause.message.contains "group:root:1.0 > group:b:1.0"
+    }
+
+    def "can handle a cycle in the incoming paths of a broken module"() {
+        given:
+        def a = revision('a')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, a
+        traverses a, b
+        doesNotResolve b, a
+        traversesMissing b, c
+
+        when:
+        def result = resolve()
+
+        then:
+        result.unresolvedModuleDependencies.size() == 1
+        def unresolved = result.unresolvedModuleDependencies.iterator().next()
+        unresolved.selector == new DefaultModuleVersionSelector('group', 'c', '1.0')
+        unresolved.problem instanceof ModuleVersionResolveException
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException e = thrown()
+        e.cause instanceof ModuleVersionNotFoundException
+        e.cause.message.contains "group:root:1.0 > group:a:1.0 > group:b:1.0"
+    }
+
+    def "does not report a path through an evicted version"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        def d = revision('d')
+        def e = revision('e')
+        traverses root, evicted
+        traverses evicted, b
+        traversesMissing b, c
+        traverses root, d
+        traverses d, e
+        traverses e, selected
+        doesNotResolve selected, c
+
+        when:
+        def result = resolve()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+
+        when:
+        result.rethrowFailure()
+
+        then:
+        ResolveException ex = thrown()
+        ex.cause instanceof ModuleVersionNotFoundException
+        !ex.cause.message.contains("group:a:1.1")
+        ex.cause.message.contains "group:root:1.0 > group:a:1.2"
+
+        and:
+        modules(result) == ids(selected, d, e)
+    }
+
+    def "fails when conflict resolution selects a version that does not exist"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        traverses root, evicted
+        traverses root, b
+        traversesMissing b, selected
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> 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")
+    }
+
+    def "does not fail when conflict resolution evicts a version that does not exist"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        traversesMissing root, evicted
+        traverses root, b
+        traverses b, selected
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+
+        and:
+        modules(result) == ids(selected, b)
+    }
+
+    def "does not fail when a broken version is evicted"() {
+        given:
+        def selected = revision('a', '1.2')
+        def evicted = revision('a', '1.1')
+        def b = revision('b')
+        def c = revision('c')
+        traverses root, evicted
+        traversesBroken evicted, b
+        traverses root, c
+        traverses c, selected
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        1 * conflictResolver.select(!null) >> {
+            Collection<ComponentResolutionState> candidates = it[0]
+            return candidates.find { it.version == '1.2' }
+        }
+
+        and:
+        modules(result) == ids(selected, c)
+    }
+
+    def "direct dependency can force a particular version"() {
+        given:
+        def forced = revision("a", "1")
+        def evicted = revision("a", "2")
+        def b = revision("b")
+        traverses root, b
+        traverses root, forced, force: true
+        doesNotTraverse b, evicted
+
+        when:
+        def result = resolve()
+        result.rethrowFailure()
+
+        then:
+        modules(result) == ids(forced, b)
+    }
+
+    def revision(String name, String revision = '1.0') {
+        def descriptor = new DefaultModuleDescriptor(createModuleRevisionId("group", name, revision), "release", new Date())
+        def metaData = new TestModuleMetaData(descriptor)
+        config(metaData, 'default')
+        descriptor.addArtifact('default', new DefaultArtifact(descriptor.moduleRevisionId, new Date(), "art1", "art", "zip"))
+        return metaData
+    }
+
+    def project(String name, String revision = '1.0') {
+        def revisionId = createModuleRevisionId("group", name, revision)
+        def descriptor = new DefaultModuleDescriptor(revisionId, "release", new Date())
+        def metaData = new TestProjectMetaData(newId(revisionId), descriptor, DefaultProjectComponentIdentifier.newId(":${name}"))
+        config(metaData, 'default')
+        descriptor.addArtifact('default', new DefaultArtifact(descriptor.moduleRevisionId, new Date(), "art1", "art", "zip"))
+        return metaData
+    }
+
+    def config(ComponentResolveMetaData metaData, String name, String... extendsFrom) {
+        def configuration = new org.apache.ivy.core.module.descriptor.Configuration(name, org.apache.ivy.core.module.descriptor.Configuration.Visibility.PUBLIC, null, extendsFrom, true, null)
+        metaData.descriptor.addConfiguration(configuration)
+        return configuration
+    }
+
+    def traverses(Map<String, ?> args = [:], TestMetaData from, ComponentResolveMetaData to) {
+        def dependencyMetaData = dependsOn(args, from, to.descriptor.moduleRevisionId)
+        selectorResolvesTo(dependencyMetaData, to.componentId, to.id)
+        1 * metaDataResolver.resolve(dependencyMetaData, to.componentId, _) >> { DependencyMetaData dep, ComponentIdentifier id, BuildableComponentResolveResult result ->
+            result.resolved(to)
+        }
+    }
+
+    def doesNotTraverse(Map<String, ?> args = [:], TestMetaData from, ComponentResolveMetaData to) {
+        def dependencyMetaData = dependsOn(args, from, to.descriptor.moduleRevisionId)
+        selectorResolvesTo(dependencyMetaData, to.componentId, to.id)
+        0 * metaDataResolver.resolve(_, to.componentId, _)
+    }
+
+    def doesNotResolve(Map<String, ?> args = [:], TestMetaData from, ComponentResolveMetaData to) {
+        def dependencyMetaData = dependsOn(args, from, to.descriptor.moduleRevisionId)
+        0 * idResolver.resolve(dependencyMetaData, _)
+        0 * metaDataResolver.resolve(_, to.componentId, _)
+    }
+
+    def traversesMissing(Map<String, ?> args = [:], TestMetaData from, ComponentResolveMetaData to) {
+        def dependencyMetaData = dependsOn(args, from, to.descriptor.moduleRevisionId)
+        selectorResolvesTo(dependencyMetaData, to.componentId, to.id)
+        1 * metaDataResolver.resolve(_, to.componentId, _) >> { DependencyMetaData dep, ComponentIdentifier id, BuildableComponentResolveResult result ->
+            result.notFound(to.id)
+        }
+    }
+
+    def traversesBroken(Map<String, ?> args = [:], TestMetaData from, ComponentResolveMetaData to) {
+        def dependencyMetaData = dependsOn(args, from, to.descriptor.moduleRevisionId)
+        selectorResolvesTo(dependencyMetaData, to.componentId, to.id)
+        1 * metaDataResolver.resolve(_, to.componentId, _) >> { DependencyMetaData dep, ComponentIdentifier id, BuildableComponentResolveResult result ->
+            result.failed(new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken"))
+        }
+    }
+
+    ModuleVersionIdentifier toModuleVersionIdentifier(ModuleRevisionId moduleRevisionId) {
+        ModuleVersionIdentifier moduleVersionIdentifier = Mock();
+        (0..2) * moduleVersionIdentifier.group >> moduleRevisionId.organisation;
+        (0..2) * moduleVersionIdentifier.name >> moduleRevisionId.name;
+        (0..2) * moduleVersionIdentifier.version >> moduleRevisionId.revision;
+        moduleVersionIdentifier
+    }
+
+    def brokenSelector(Map<String, ?> args = [:], TestMetaData from, String to) {
+        def dependencyMetaData = dependsOn(args, from, createModuleRevisionId("group", to, "1.0"))
+        1 * idResolver.resolve(dependencyMetaData, _) >> { DependencyMetaData dep, BuildableComponentIdResolveResult result ->
+            result.failed(new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken"))
+        }
+    }
+
+    def dependsOn(Map<String, ?> args = [:], TestMetaData from, ModuleRevisionId to) {
+        def dependencyId = args.revision ? createModuleRevisionId(to.moduleId.organisation, to.moduleId.name, args.revision) : to
+        boolean transitive = args.transitive == null || args.transitive
+        boolean force = args.force
+        def descriptor = new DefaultDependencyDescriptor(from.descriptor, dependencyId, force, false, transitive)
+        descriptor.addDependencyConfiguration("default", "default")
+        if (args.exclude) {
+            descriptor.addExcludeRule("default", new DefaultExcludeRule(new ArtifactId(
+                    args.exclude.descriptor.moduleRevisionId.moduleId, PatternMatcher.ANY_EXPRESSION,
+                    PatternMatcher.ANY_EXPRESSION,
+                    PatternMatcher.ANY_EXPRESSION),
+                    ExactPatternMatcher.INSTANCE, null))
+        }
+        def dependencyMetaData = new DslOriginDependencyMetaDataWrapper(new DefaultDependencyMetaData(descriptor), Stub(ModuleDependency))
+        from.addDependency(dependencyMetaData)
+        return dependencyMetaData
+    }
+
+    def selectorResolvesTo(DependencyMetaData dependencyMetaData, ComponentIdentifier id, ModuleVersionIdentifier mvId) {
+        1 * idResolver.resolve(dependencyMetaData, _) >> { DependencyMetaData dep, BuildableComponentIdResolveResult result ->
+            result.resolved(id, mvId)
+        }
+    }
+
+    def ids(ModuleComponentResolveMetaData... descriptors) {
+        return descriptors.collect { it.id } as Set
+    }
+
+    def modules(LenientConfiguration config) {
+        Set<ModuleVersionIdentifier> result = new LinkedHashSet<ModuleVersionIdentifier>()
+        List<ResolvedDependency> queue = []
+        queue.addAll(config.getFirstLevelModuleDependencies({ true } as Spec))
+        while (!queue.empty) {
+            def node = queue.remove(0)
+            result.add(node.module.id)
+            queue.addAll(0, node.children)
+        }
+        return result
+    }
+
+    def artifacts(LenientConfiguration config) {
+        return config.resolvedArtifacts.collect { it.moduleVersion.id } as Set
+    }
+
+    private interface TestMetaData extends ComponentResolveMetaData {
+        void addDependency(DependencyMetaData dependencyMetaData)
+    }
+
+    private static class TestModuleMetaData extends BuildableIvyModuleResolveMetaData implements TestMetaData {
+        def deps = []
+
+        TestModuleMetaData(DefaultModuleDescriptor module) {
+            super(module)
+        }
+
+        @Override
+        void addDependency(DependencyMetaData dependencyMetaData) {
+            deps.add(dependencyMetaData)
+        }
+
+        @Override
+        protected List<DependencyMetaData> populateDependenciesFromDescriptor() {
+            return deps
+        }
+    }
+
+    private static class TestProjectMetaData extends AbstractModuleDescriptorBackedMetaData implements TestMetaData {
+        def deps = []
+
+        TestProjectMetaData(ModuleVersionIdentifier moduleVersionIdentifier, ModuleDescriptor module, ComponentIdentifier componentIdentifier) {
+            super(moduleVersionIdentifier, module, componentIdentifier)
+        }
+
+        @Override
+        void addDependency(DependencyMetaData dependencyMetaData) {
+            deps.add(dependencyMetaData)
+        }
+
+        @Override
+        protected List<DependencyMetaData> populateDependenciesFromDescriptor() {
+            return deps
+        }
+
+        @Override
+        protected Set<ComponentArtifactMetaData> getArtifactsForConfiguration(ConfigurationMetaData configuration) {
+            return []
+        }
+
+        @Override
+        ComponentResolveMetaData withSource(ModuleSource source) {
+            return null
+        }
+
+        @Override
+        ComponentArtifactMetaData artifact(Artifact artifact) {
+            return null
+        }
+
+        @Override
+        Set<? extends ComponentArtifactMetaData> getArtifacts() {
+            return []
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.groovy
new file mode 100644
index 0000000..912520d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/VersionSelectionReasonResolverTest.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.api.internal.artifacts.ivyservice.resolveengine
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import spock.lang.Specification
+
+class VersionSelectionReasonResolverTest extends Specification {
+
+    def "configures selection reason"() {
+        def delegate = Mock(ModuleConflictResolver)
+        VersionSelectionReasonResolver resolver = new VersionSelectionReasonResolver(delegate)
+
+        def candidate1 = Mock(ComponentResolutionState)
+        def candidate2 = Mock(ComponentResolutionState)
+
+        when:
+        def out = resolver.select([candidate1, candidate2])
+
+        then:
+        out == candidate2
+
+        and:
+        1 * delegate.select([candidate1, candidate2]) >> candidate2
+        1 * candidate2.getSelectionReason() >> VersionSelectionReasons.REQUESTED
+        1 * candidate2.setSelectionReason(VersionSelectionReasons.CONFLICT_RESOLUTION)
+        0 * _._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainerTest.groovy
new file mode 100644
index 0000000..6ba262d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/ConflictContainerTest.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts
+
+import spock.lang.Specification
+import spock.lang.Subject
+
+class ConflictContainerTest extends Specification {
+
+    @Subject container = new ConflictContainer<String, Integer>()
+
+    def "contains few unconflicted elements"() {
+        container.newElement("a", [1], null)
+        container.newElement("b", [1], null)
+        container.newElement("c", [1], "d")
+        expect:
+        container.size == 0
+    }
+
+    def "contains conflicting element"() {
+        container.newElement("a", [1], null)
+        container.newElement("b", [1, 2], null)
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "b:1,2"
+    }
+
+    def "contains multiple conflicting elements"() {
+        container.newElement("a", [1, 2], null)
+        container.newElement("b", [3, 4], null)
+        expect:
+        container.size == 2
+        container.popConflict().toString() == "a:1,2"
+        container.popConflict().toString() == "b:3,4"
+    }
+
+    def "pops conflicts orderly"() {
+        container.newElement("c", [5, 6], null)
+        container.newElement("b", [3, 4], null)
+        container.newElement("a", [1, 2], null)
+        expect:
+        container.size == 3
+        container.popConflict().toString() == "c:5,6"
+        container.popConflict().toString() == "b:3,4"
+        container.popConflict().toString() == "a:1,2"
+    }
+
+    def "contains replacement conflict"() {
+        container.newElement("a", [1, 2], "b")
+        container.newElement("b", [3, 4], null)
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "a,b:3,4"
+    }
+
+    def "contains replacement conflict reversed"() {
+        container.newElement("b", [3, 4], null)
+        container.newElement("a", [1, 2], "b")
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "b,a:3,4"
+    }
+
+    def "contains replacement and standard conflict"() {
+        container.newElement("a", [1, 2], "b")
+        container.newElement("b", [3], null)
+        container.newElement("b", [3, 4], null)
+
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "a,b:3,4"
+    }
+
+    def "contains replacement and standard conflict reversed"() {
+        container.newElement("b", [3], null)
+        container.newElement("b", [3, 4], null)
+        container.newElement("a", [1, 2], "b")
+
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "b,a:3,4"
+    }
+
+    def "contains replacement and standard conflict mixed"() {
+        container.newElement("b", [3], null)
+        container.newElement("a", [1, 2], "b")
+        container.newElement("b", [3, 4], null)
+
+        expect:
+        container.size == 1
+        container.popConflict().toString() == "a,b:3,4"
+    }
+
+    def "contains replacement and standard conflict interleaving"() {
+        container.newElement("a", [1], null)
+        container.newElement("b", [1, 2], null) //standard
+        container.newElement("c", [3], "a") //module
+        container.newElement("d", [4], null)
+
+        expect:
+        container.size == 2
+        container.popConflict().toString() == "b:1,2"
+        container.popConflict().toString() == "c,a:1"
+    }
+
+    def "contains chained conflicts"() {
+        container.newElement("a", [1], "b") //a->b->c
+        container.newElement("b", [2], "c")
+        container.newElement("c", [3], null)
+
+        expect:
+        container.conflicts.size() == 1
+        container.popConflict().toString() == "a,b,c:3"
+    }
+
+    def "allows registering multiple elements with the same replacedBy before the replacedBy"() {
+        container.newElement("a", [1], "c")
+        container.newElement("b", [2], "c")
+        container.newElement("c", [3], null)
+
+        expect:
+        container.conflicts.size() == 1
+        container.popConflict().toString() == "a,b,c:3"
+    }
+
+    def "allows registering multiple elements with the same replacedBy after the replacedBy"() {
+        container.newElement("c", [3], null)
+        container.newElement("a", [1], "c")
+        container.newElement("b", [2], "c")
+
+        expect:
+        container.conflicts.size() == 1
+        container.popConflict().toString() == "a,c,b:3"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandlerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandlerTest.groovy
new file mode 100644
index 0000000..1accd4b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultConflictHandlerTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.graph.conflicts
+
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ModuleConflictResolver
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState
+import spock.lang.Specification
+import spock.lang.Subject
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+
+class DefaultConflictHandlerTest extends Specification {
+
+    def resolver = Mock(ModuleConflictResolver)
+    def replacements = Mock(ModuleReplacementsData)
+    @Subject handler = new DefaultConflictHandler(resolver, replacements)
+
+    def "registers unconflicted modules"() {
+        def a = candidate("org", "a")
+        def b = candidate("org", "b")
+
+        expect:
+        !handler.registerModule(a).conflictExists()
+        !handler.registerModule(b).conflictExists()
+        !handler.hasConflicts()
+    }
+
+    def "registers module with version conflict"() {
+        def a = candidate("org", "a", "1", "2")
+        def b = candidate("org", "b", "1")
+
+        when:
+        def aX = handler.registerModule(a)
+        def bX = handler.registerModule(b)
+
+        then:
+        aX.conflictExists()
+        !bX.conflictExists()
+        handler.hasConflicts()
+    }
+
+    def "registers module with module conflict"() {
+        def a = candidate("org", "a", "1", "2")
+        def b = candidate("org", "b", "1")
+
+        replacements.getReplacementFor(DefaultModuleIdentifier.newId("org", "a")) >> DefaultModuleIdentifier.newId("org", "b")
+
+        when:
+        def aX = handler.registerModule(a)
+        def bX = handler.registerModule(b)
+
+        then:
+        aX.conflictExists()
+        bX.conflictExists()
+        handler.hasConflicts()
+    }
+
+    def "resolves conflict"() {
+        def a = candidate("org", "a", "1", "2")
+        handler.registerModule(a)
+
+        when:
+        handler.resolveNextConflict { ConflictResolutionResult r ->
+            assert r.selected.id == newId("org", "a", "2")
+        }
+
+        then:
+        1 * resolver.select({ it*.id.version == ["1", "2"] }) >> { args -> args[0].find { it.id.version == "2" } }
+        0 * resolver._
+
+        then:
+        !handler.hasConflicts()
+    }
+
+    private CandidateModule candidate(String group, String name, String ... versions) {
+        def candidate = Stub(CandidateModule)
+        candidate.getId() >> DefaultModuleIdentifier.newId(group, name)
+        candidate.getVersions() >> versions.collect { String version ->
+            def v = Stub(ComponentResolutionState)
+            v.getId() >> newId(group, name, version)
+            v
+        }
+        candidate
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.groovy
new file mode 100644
index 0000000..f4c9e3a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactoryTest.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.api.internal.artifacts.ivyservice.resolveengine.result
+
+import org.gradle.api.artifacts.result.ComponentSelectionReason
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.newModule
+
+class CachingDependencyResultFactoryTest extends Specification {
+
+    CachingDependencyResultFactory factory = new CachingDependencyResultFactory()
+
+    def "creates and caches resolved dependencies"() {
+        def fromModule = newModule('from')
+        def selectedModule = newModule('selected')
+
+        when:
+        def dep = factory.createResolvedDependency(selector('requested'), fromModule, selectedModule)
+        def same = factory.createResolvedDependency(selector('requested'), fromModule, selectedModule)
+
+        def differentRequested = factory.createResolvedDependency(selector('xxx'), fromModule, selectedModule)
+        def differentFrom = factory.createResolvedDependency(selector('requested'), newModule('xxx'), selectedModule)
+        def differentSelected = factory.createResolvedDependency(selector('requested'), fromModule, newModule('xxx'))
+
+        then:
+        dep.is(same)
+        !dep.is(differentFrom)
+        !dep.is(differentRequested)
+        !dep.is(differentSelected)
+    }
+
+    def "creates and caches unresolved dependencies"() {
+        def fromModule = newModule('from')
+        def selectedModule = Mock(ComponentSelectionReason)
+
+        when:
+        def dep = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
+        def same = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
+
+        def differentRequested = factory.createUnresolvedDependency(selector('xxx'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('xxx'), "foo"))
+        def differentFrom = factory.createUnresolvedDependency(selector('requested'), newModule('xxx'), selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
+        def differentFailure = factory.createUnresolvedDependency(selector('requested'), fromModule, selectedModule, new ModuleVersionResolveException(moduleVersionSelector('requested'), "foo"))
+
+        then:
+        dep.is(same)
+        !dep.is(differentFrom)
+        !dep.is(differentRequested)
+        dep.is(differentFailure) //the same dependency edge cannot have different failures
+    }
+
+    def selector(String group='a', String module='a', String version='1') {
+        DefaultModuleComponentSelector.newSelector(group, module, version)
+    }
+
+    def moduleVersionSelector(String group='a', String module='a', String version='1') {
+        newSelector(group, module, version)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
new file mode 100644
index 0000000..bb068c4
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.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.api.internal.artifacts.ivyservice.resolveengine.result
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import org.gradle.internal.serialize.SerializerSpec
+
+class ComponentIdentifierSerializerTest extends SerializerSpec {
+    ComponentIdentifierSerializer serializer = new ComponentIdentifierSerializer()
+
+    def "throws exception if null is provided"() {
+        when:
+        serialize(null, serializer)
+
+        then:
+        Throwable t = thrown(IllegalArgumentException)
+        t.message == 'Provided component identifier may not be null'
+    }
+
+    def "serializes ModuleComponentIdentifier"() {
+        given:
+        ModuleComponentIdentifier selection = new DefaultModuleComponentIdentifier('group-one', 'name-one', 'version-one')
+
+        when:
+        ModuleComponentIdentifier result = serialize(selection, serializer)
+
+        then:
+        result.group == 'group-one'
+        result.module == 'name-one'
+        result.version == 'version-one'
+    }
+
+    def "serializes BuildComponentIdentifier"() {
+        given:
+        ProjectComponentIdentifier selection = new DefaultProjectComponentIdentifier(':myPath')
+
+        when:
+        ProjectComponentIdentifier result = serialize(selection, serializer)
+
+        then:
+        result.projectPath == ':myPath'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.groovy
new file mode 100644
index 0000000..93dff2d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionReasonSerializerTest.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.ComponentSelectionReason
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.serialize.SerializerSpec
+
+class ComponentSelectionReasonSerializerTest extends SerializerSpec {
+
+    private serializer = new ComponentSelectionReasonSerializer()
+
+    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 ComponentSelectionReason)
+        then:
+        thrown(IllegalArgumentException)
+
+        when:
+        serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream("foo".bytes)))
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+
+    void check(ComponentSelectionReason reason) {
+        def result = serialize(reason, serializer)
+        assert result == reason
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
new file mode 100644
index 0000000..4ee1d62
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.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.api.internal.artifacts.ivyservice.resolveengine.result
+
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.serialize.SerializerSpec
+
+public class ComponentSelectorSerializerTest extends SerializerSpec {
+    ComponentSelectorSerializer serializer = new ComponentSelectorSerializer()
+
+    def "throws exception if null is provided"() {
+        when:
+        serialize(null, serializer)
+
+        then:
+        Throwable t = thrown(IllegalArgumentException)
+        t.message == 'Provided component selector may not be null'
+    }
+
+    def "serializes ModuleComponentSelector"() {
+        given:
+        ModuleComponentSelector selection = new DefaultModuleComponentSelector('group-one', 'name-one', 'version-one')
+
+        when:
+        ModuleComponentSelector result = serialize(selection, serializer)
+
+        then:
+        result.group == 'group-one'
+        result.module == 'name-one'
+        result.version == 'version-one'
+    }
+
+    def "serializes BuildComponentSelector"() {
+        given:
+        ProjectComponentSelector selection = new DefaultProjectComponentSelector(':myPath')
+
+        when:
+        ProjectComponentSelector result = serialize(selection, serializer)
+
+        then:
+        result.projectPath == ':myPath'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
new file mode 100644
index 0000000..c77eb73
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
@@ -0,0 +1,284 @@
+/*
+ * 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.resolveengine.result
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.artifacts.component.ComponentSelector
+import org.gradle.api.artifacts.result.ComponentSelectionReason
+import org.gradle.api.artifacts.result.ResolvedDependencyResult
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.resolve.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
+
+class DefaultResolutionResultBuilderSpec extends Specification {
+
+    def builder = new DefaultResolutionResultBuilder()
+
+    def "builds basic graph"() {
+        given:
+        builder.start(confId("root"), createComponentIdentifier("root"))
+
+        node("root")
+        node("mid1")
+        node("mid2")
+        node("leaf1")
+        node("leaf2")
+        node("leaf3")
+        node("leaf4")
+
+        resolvedConf("root", [dep("mid1"), dep("mid2")])
+
+        resolvedConf("mid1", [dep("leaf1"), dep("leaf2")])
+        resolvedConf("mid2", [dep("leaf3"), dep("leaf4")])
+
+        resolvedConf("leaf1", [])
+        resolvedConf("leaf2", [])
+        resolvedConf("leaf3", [])
+        resolvedConf("leaf4", [])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """x:root:1
+  x:mid1:1 [root]
+    x:leaf1:1 [mid1]
+    x:leaf2:1 [mid1]
+  x:mid2:1 [root]
+    x:leaf3:1 [mid2]
+    x:leaf4:1 [mid2]
+"""
+    }
+
+    def "graph with multiple dependents"() {
+        given:
+        builder.start(confId("a"), createComponentIdentifier("a"))
+
+        node("a")
+        node("b1")
+        node("b2")
+        node("b3")
+
+        resolvedConf("a", [dep("b1"), dep("b2"), dep("b3")])
+
+        resolvedConf("b1", [dep("b2"), dep("b3")])
+        resolvedConf("b2", [dep("b3")])
+        resolvedConf("b3", [])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """x:a:1
+  x:b1:1 [a]
+    x:b2:1 [a,b1]
+      x:b3:1 [a,b1,b2]
+  x:b2:1 [a,b1]
+    x:b3:1 [a,b1,b2]
+  x:b3:1 [a,b1,b2]
+"""
+    }
+
+    def "builds graph with cycles"() {
+        given:
+        builder.start(confId("a"), createComponentIdentifier("a"))
+        node("a")
+        node("b")
+        node("c")
+        resolvedConf("a", [dep("b")])
+        resolvedConf("b", [dep("c")])
+        resolvedConf("c", [dep("a")])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """x:a:1
+  x:b:1 [a]
+    x:c:1 [b]
+      x:a:1 [c]
+"""
+    }
+
+    def "includes selection reason"() {
+        given:
+        builder.start(confId("a"), createComponentIdentifier("a"))
+        node("b", VersionSelectionReasons.FORCED)
+        node("c", VersionSelectionReasons.CONFLICT_RESOLUTION)
+        node("d")
+        resolvedConf("a", [dep("b"), dep("c"), dep("d", new RuntimeException("Boo!"))])
+        resolvedConf("b", [])
+        resolvedConf("c", [])
+        resolvedConf("d", [])
+
+        when:
+        def deps = builder.complete().root.dependencies
+
+        then:
+        def b = deps.find { it.selected.id.module == 'b' }
+        def c = deps.find { it.selected.id.module == 'c' }
+
+        b.selected.selectionReason.forced
+        c.selected.selectionReason.conflictResolution
+    }
+
+    def "links dependents correctly"() {
+        given:
+        builder.start(confId("a"), createComponentIdentifier("a"))
+        node("a")
+        node("b")
+        node("c")
+        resolvedConf("a", [dep("b")])
+        resolvedConf("b", [dep("c")])
+        resolvedConf("c", [dep("a")])
+
+        when:
+        def a = builder.complete().root
+
+        then:
+        def b  = first(a.dependencies).selected
+        def c  = first(b.dependencies).selected
+        def a2 = first(c.dependencies).selected
+
+        a2.is(a)
+
+        first(b.dependents).is(first(a.dependencies))
+        first(c.dependents).is(first(b.dependencies))
+        first(a.dependents).is(first(c.dependencies))
+
+        first(b.dependents).from.is(a)
+        first(c.dependents).from.is(b)
+        first(a.dependents).from.is(c)
+    }
+
+    ResolvedDependencyResult first(Set<? extends ResolvedDependencyResult> dependencies) {
+        dependencies.iterator().next()
+    }
+
+    def "accumulates and avoids duplicate dependencies"() {
+        given:
+        builder.start(confId("root"), createComponentIdentifier("root"))
+        node("root")
+        node("mid1")
+        node("leaf1")
+        node("leaf2")
+
+        resolvedConf("root", [dep("mid1")])
+
+        resolvedConf("mid1", [dep("leaf1")])
+        resolvedConf("mid1", [dep("leaf1")]) //dupe
+        resolvedConf("mid1", [dep("leaf2")])
+
+        resolvedConf("leaf1", [])
+        resolvedConf("leaf2", [])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        printGraph(result.root) == """x:root:1
+  x:mid1:1 [root]
+    x:leaf1:1 [mid1]
+    x:leaf2:1 [mid1]
+"""
+    }
+
+    def "accumulates and avoids duplicate unresolved dependencies"() {
+        given:
+        builder.start(confId("root"), createComponentIdentifier("root"))
+        node("mid1")
+        node("leaf1")
+        node("leaf2")
+        resolvedConf("root", [dep("mid1")])
+
+        resolvedConf("mid1", [dep("leaf1", new RuntimeException("foo!"))])
+        resolvedConf("mid1", [dep("leaf1", new RuntimeException("bar!"))]) //dupe
+        resolvedConf("mid1", [dep("leaf2", new RuntimeException("baz!"))])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        def mid1 = first(result.root.dependencies)
+        mid1.selected.dependencies.size() == 2
+        mid1.selected.dependencies*.requested.module == ['leaf1', 'leaf2']
+    }
+
+    def "graph includes unresolved deps"() {
+        given:
+        builder.start(confId("a"), createComponentIdentifier("a"))
+        node("b")
+        node("c")
+        resolvedConf("a", [dep("b"), dep("c"), dep("U", new RuntimeException("unresolved!"))])
+        resolvedConf("b", [])
+        resolvedConf("c", [])
+
+        when:
+        def result = builder.complete()
+
+        then:
+        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.
+"""
+    }
+
+    private void node(String module, ComponentSelectionReason reason = VersionSelectionReasons.REQUESTED) {
+        def moduleVersion = new DummyModuleVersionSelection(id: newId("x", module, "1"), selectionReason: reason, componentId: new DefaultModuleComponentIdentifier("x", module, "1"))
+        builder.resolvedModuleVersion(moduleVersion)
+    }
+
+    private void resolvedConf(String module, List<InternalDependencyResult> deps) {
+        builder.resolvedConfiguration(confId(module), deps)
+    }
+
+    private InternalDependencyResult dep(String requested, Exception failure = null, String selected = requested) {
+        def selection = newId("x", selected, "1")
+        def selector = new DefaultModuleComponentSelector("x", requested, "1")
+        def moduleVersionSelector = newSelector("x", requested, "1")
+        failure = failure == null ? null : new ModuleVersionResolveException(moduleVersionSelector, failure)
+        new DummyInternalDependencyResult(requested: selector, selected: selection, failure: failure)
+    }
+
+    private ModuleVersionIdentifier confId(String module) {
+        newId("x", module, "1")
+    }
+
+    private ComponentIdentifier createComponentIdentifier(String module) {
+        new DefaultModuleComponentIdentifier("x", module, "1")
+    }
+
+    class DummyModuleVersionSelection implements ModuleVersionSelection {
+        ModuleVersionIdentifier id
+        ComponentSelectionReason selectionReason
+        ComponentIdentifier componentId
+    }
+
+    class DummyInternalDependencyResult implements InternalDependencyResult {
+        ComponentSelector requested
+        ModuleVersionIdentifier selected
+        ModuleVersionResolveException failure
+        ComponentSelectionReason reason
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
new file mode 100644
index 0000000..346ade9
--- /dev/null
+++ b/subprojects/dependency-management/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
+import org.gradle.internal.serialize.Decoder
+import org.gradle.internal.serialize.Encoder
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.serialize.OutputStreamBackedEncoder
+
+public class DummyBinaryStore implements BinaryStore {
+
+    private final ByteArrayOutputStream bytes = new ByteArrayOutputStream()
+    private Encoder output = new OutputStreamBackedEncoder(bytes)
+
+    void write(BinaryStore.WriteAction write) {
+        write.write(output)
+    }
+
+    BinaryStore.BinaryData done() {
+        new BinaryStore.BinaryData() {
+            Decoder decoder
+            def <T> T read(BinaryStore.ReadAction<T> readAction) {
+                if (decoder == null) {
+                    decoder = new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray()))
+                }
+                readAction.read(decoder)
+            }
+
+            void close() {
+                decoder = 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/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyStore.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/InternalDependencyResultSerializerTest.groovy
new file mode 100644
index 0000000..c5354e7
--- /dev/null
+++ b/subprojects/dependency-management/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.component.ModuleComponentSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.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() >> DefaultModuleComponentSelector.newSelector("org", "foo", "1.0")
+            getFailure() >> null
+            getSelected() >> newId("org", "foo", "1.0")
+            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 == DefaultModuleComponentSelector.newSelector("org", "foo", "1.0")
+        out.failure == null
+        out.selected == newId("org", "foo", "1.0")
+    }
+
+    def "serializes failed dependency result"() {
+        ModuleComponentSelector requested = DefaultModuleComponentSelector.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<ModuleComponentSelector, ModuleVersionResolveException> map = new HashMap<>()
+        map.put(requested, failure)
+        def out = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray())), map)
+
+        then:
+        out.requested == DefaultModuleComponentSelector.newSelector("x", "y", "1.0")
+        out.failure.cause.message == "Boo!"
+        out.selected == null
+        out.reason == VersionSelectionReasons.CONFLICT_RESOLUTION
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.groovy
new file mode 100644
index 0000000..df6887b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ModuleVersionSelectionSerializerTest.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.result
+
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.serialize.SerializerSpec
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
+
+class ModuleVersionSelectionSerializerTest extends SerializerSpec {
+
+    def serializer = new ModuleVersionSelectionSerializer()
+
+    def "serializes"() {
+        def componentIdentifier = new DefaultModuleComponentIdentifier('group', 'module', 'version')
+        def selection = new DefaultModuleVersionSelection(newId("org", "foo", "2.0"), VersionSelectionReasons.REQUESTED, componentIdentifier)
+
+        when:
+        def result = serialize(selection, serializer)
+
+        then:
+        result.selectionReason == VersionSelectionReasons.REQUESTED
+        result.id == newId("org", "foo", "2.0")
+        result.componentId == componentIdentifier
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolutionResultPrinter.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
new file mode 100644
index 0000000..c0eb38b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
@@ -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.api.internal.artifacts.ivyservice.resolveengine.result
+
+import org.gradle.api.artifacts.result.ComponentSelectionReason
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.resolve.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"), new DefaultModuleComponentIdentifier("org", "root", "1.0"))
+
+        when:
+        def result = builder.complete()
+
+        then:
+        with(result) {
+            root.id == DefaultModuleComponentIdentifier.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"), new DefaultModuleComponentIdentifier("org", "root", "1.0"))
+
+        builder.resolvedModuleVersion(sel("org", "dep1", "2.0", CONFLICT_RESOLUTION))
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), CONFLICT_RESOLUTION, null),
+                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), 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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), newId("org", "dep1", "2.0"), REQUESTED, null),
+        ])
+        builder.resolvedConfiguration(newId("org", "root", "1.0"), [
+                new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep2", "2.0"), newId("org", "dep2", "2.0"), 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"), new DefaultModuleComponentIdentifier("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(DefaultModuleComponentSelector.newSelector("org", "dep1", "2.0"), null, REQUESTED, new ModuleVersionResolveException(newSelector("org", "dep1", "1.0"), new RuntimeException())),
+            new DefaultInternalDependencyResult(DefaultModuleComponentSelector.newSelector("org", "dep2", "2.0"), newId("org", "dep2", "2.0"), REQUESTED, null),
+        ])
+        builder.resolvedConfiguration(newId("org", "dep2", "2.0"), [
+            new DefaultInternalDependencyResult(DefaultModuleComponentSelector.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, ComponentSelectionReason reason) {
+        new DefaultModuleVersionSelection(newId(org, name, ver), reason, new DefaultModuleComponentIdentifier(org, name, ver))
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
new file mode 100644
index 0000000..ea2aca4
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/VersionSelectionReasonsTest.groovy
@@ -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.resolveengine.result
+
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.*
+
+class VersionSelectionReasonsTest extends Specification {
+
+    def "decorates with conflict resolution"() {
+        expect:
+        withConflictResolution(REQUESTED) == CONFLICT_RESOLUTION
+        withConflictResolution(FORCED) == CONFLICT_RESOLUTION
+        withConflictResolution(SELECTED_BY_RULE) == CONFLICT_RESOLUTION_BY_RULE
+        withConflictResolution(CONFLICT_RESOLUTION) == CONFLICT_RESOLUTION
+        withConflictResolution(CONFLICT_RESOLUTION_BY_RULE) == CONFLICT_RESOLUTION_BY_RULE
+    }
+
+    def "does not decorate unsupported reasons"() {
+        when:
+        withConflictResolution(ROOT)
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message.contains ROOT.toString()
+    }
+}
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/CachedStoreFactoryTest.groovy
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/DefaultBinaryStoreTest.groovy
diff --git a/subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy
similarity index 100%
rename from subprojects/core-impl/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy
rename to subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/store/ResolutionResultsStoreFactoryTest.groovy
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
new file mode 100644
index 0000000..bd88062
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.mvnsettings
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultLocalMavenRepositoryLocatorTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    SimpleMavenFileLocations locations
+    DefaultLocalMavenRepositoryLocator locator
+
+    def system = Mock(DefaultLocalMavenRepositoryLocator.SystemPropertyAccess)
+
+    File repo1 = tmpDir.file("repo1")
+    File repo2 = tmpDir.file("repo2")
+    File userHome1 = tmpDir.file("user_home_1")
+    File userHome2 = tmpDir.file("user_home_2")
+
+    def setup() {
+        locations = new SimpleMavenFileLocations()
+        locator = new DefaultLocalMavenRepositoryLocator(new DefaultMavenSettingsProvider(locations), system)
+    }
+
+    def "returns default location if no settings file exists"() {
+        when:
+        1 * system.getProperty("user.home") >> userHome1.absolutePath
+        then:
+        locator.localMavenRepository == new File(userHome1, ".m2/repository")
+
+        // Ensure that modified user.home is honoured (see http://forums.gradle.org/gradle/topics/override_location_of_the_local_maven_repo)
+        when:
+        1 * system.getProperty("user.home") >> userHome2.absolutePath
+        then:
+        locator.localMavenRepository == new File(userHome2, ".m2/repository")
+    }
+
+    def "returns value of system property if it is specified"() {
+        when:
+        1 * system.getProperty("maven.repo.local") >> repo1.absolutePath
+        then:
+        locator.localMavenRepository == repo1
+
+        // Ensure that modified system property is honoured
+        when:
+        1 * system.getProperty("maven.repo.local") >> repo2.absolutePath
+        then:
+        locator.localMavenRepository == repo2
+    }
+
+    def "throws exception on broken global settings file with decent error message"() {
+        given:
+        def settingsFile = locations.globalSettingsFile
+        settingsFile << "broken content"
+        when:
+        locator.localMavenRepository
+        then:
+        def ex = thrown(CannotLocateLocalMavenRepositoryException);
+        ex.message == "Unable to parse local Maven settings."
+        ex.cause.message.contains(settingsFile.absolutePath)
+    }
+
+    def "throws exception on broken user settings file with decent error message"() {
+        given:
+        def settingsFile = locations.userSettingsFile
+        settingsFile << "broken content"
+        when:
+        locator.localMavenRepository
+        then:
+        def ex = thrown(CannotLocateLocalMavenRepositoryException)
+        ex.message == "Unable to parse local Maven settings."
+        ex.cause.message.contains(settingsFile.absolutePath)
+    }
+
+    def "honors location specified in user settings file"() {
+        writeSettingsFile(locations.userSettingsFile, repo1)
+
+        expect:
+        locator.localMavenRepository == repo1
+    }
+
+    def "ignores changes to maven settings file after initial load"() {
+        when:
+        writeSettingsFile(locations.userSettingsFile, repo1)
+
+        then:
+        locator.localMavenRepository == repo1
+
+        when:
+        writeSettingsFile(locations.userSettingsFile, repo2)
+
+        then:
+        locator.localMavenRepository == repo1
+    }
+
+    def "honors location specified in global settings file"() {
+        writeSettingsFile(locations.globalSettingsFile, repo1)
+
+        expect:
+        locator.localMavenRepository == repo1
+    }
+
+    def "prefers location specified in user settings file over that in global settings file"() {
+        writeSettingsFile(locations.userSettingsFile, repo1)
+        writeSettingsFile(locations.globalSettingsFile, repo2)
+
+        expect:
+        locator.localMavenRepository == repo1
+    }
+
+    def "handles the case where (potential) location of global settings file cannot be determined"() {
+        locations.globalSettingsFile = null
+
+        when:
+        system.getProperty("user.home") >> userHome1.absolutePath
+
+        then:
+        locator.localMavenRepository == new File(userHome1, ".m2/repository")
+
+        when:
+        writeSettingsFile(locations.userSettingsFile, repo1)
+
+        then:
+        locator.localMavenRepository == repo1
+    }
+
+    def "replaces placeholders for system properties and environment variables"() {
+        when:
+        writeSettingsFile(locations.userSettingsFile, tmpDir.file('${sys.prop}/${env.ENV_VAR}'))
+
+        and:
+        system.getProperty("sys.prop") >> "sys/prop/value"
+        system.getEnv("ENV_VAR") >> "env/var/value"
+
+        then:
+        locator.localMavenRepository == tmpDir.file("sys/prop/value/env/var/value")
+    }
+
+    @Unroll
+    def "unresolvable placeholder for #propType throws exception with decent error message"() {
+        TestFile repoPath = tmpDir.file("\${$prop}")
+        writeSettingsFile(locations.userSettingsFile, repoPath)
+        when:
+        locator.localMavenRepository
+        then:
+        def ex = thrown(CannotLocateLocalMavenRepositoryException);
+        ex.message == "Cannot resolve placeholder '${prop}' in value '${repoPath.absolutePath}'"
+        where:
+        prop                  |   propType
+        'sys.unknown.prop'    |   "system property"
+        'env.unknown.ENV_VAR' |   "environment variable"
+    }
+
+    private void writeSettingsFile(File settings, File repo) {
+        writeSettingsFile(settings, repo.absolutePath)
+    }
+
+    private void writeSettingsFile(File settings, String repo) {
+        settings.text = """
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <localRepository>$repo</localRepository>
+</settings>"""
+    }
+
+    private class SimpleMavenFileLocations implements MavenFileLocations {
+        File userMavenDir
+        File globalMavenDir
+        File userSettingsFile = tmpDir.file("userSettingsFile")
+        File globalSettingsFile = tmpDir.file("globalSettingsFile")
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryTest.groovy
new file mode 100644
index 0000000..4a0a9a3
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQueryTest.groovy
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.query
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.artifacts.dsl.RepositoryHandler
+import org.gradle.api.artifacts.result.ArtifactResolutionResult
+import org.gradle.api.artifacts.result.UnresolvedComponentResult
+import org.gradle.api.component.Artifact
+import org.gradle.api.component.Component
+import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChain
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ResolveIvyFactory
+import org.gradle.api.internal.component.ComponentTypeRegistration
+import org.gradle.api.internal.component.ComponentTypeRegistry
+import org.gradle.internal.Factory
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import org.gradle.internal.component.model.DependencyMetaData
+import org.gradle.internal.resolve.resolver.ArtifactResolver
+import org.gradle.internal.resolve.resolver.DependencyToComponentResolver
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultArtifactResolutionQueryTest extends Specification {
+    def configurationContainerInternal = Mock(ConfigurationContainerInternal)
+    def repositoryHandler = Stub(RepositoryHandler)
+    def resolveIvyFactory = Mock(ResolveIvyFactory)
+    def globalDependencyResolutionRules = Mock(GlobalDependencyResolutionRules)
+    def cacheLockingManager = Mock(CacheLockingManager)
+    def componentTypeRegistry = Mock(ComponentTypeRegistry)
+    def artifactResolver = Mock(ArtifactResolver)
+    def repositoryChain = Mock(RepositoryChain)
+    def dependencyToComponentResolver = Mock(DependencyToComponentResolver)
+    def componentResolveMetaData = Mock(ComponentResolveMetaData)
+
+    @Shared ComponentTypeRegistry testComponentTypeRegistry = createTestComponentTypeRegistry()
+
+    def "cannot call withArtifacts multiple times"() {
+        def query = createArtifactResolutionQuery(componentTypeRegistry)
+
+        given:
+        query.withArtifacts(Component, Artifact)
+
+        when:
+        query.withArtifacts(Component, Artifact)
+
+        then:
+        def e = thrown IllegalStateException
+        e.message == "Cannot specify component type multiple times."
+    }
+
+    def "cannot call execute without first specifying arguments"() {
+        def query = createArtifactResolutionQuery(componentTypeRegistry)
+
+        when:
+        query.execute()
+
+        then:
+        def e = thrown IllegalStateException
+        e.message == "Must specify component type and artifacts to query."
+    }
+
+    @Unroll
+    def "invalid component type #selectedComponentType and artifact type #selectedArtifactType is wrapped in UnresolvedComponentResult"() {
+        def query = createArtifactResolutionQuery(givenComponentTypeRegistry)
+
+        when:
+        ModuleComponentIdentifier componentIdentifier = new DefaultModuleComponentIdentifier('mygroup', 'mymodule', '1.0')
+        ArtifactResolutionResult result = query.forComponents(componentIdentifier).withArtifacts(selectedComponentType, selectedArtifactType).execute()
+
+        then:
+        1 * cacheLockingManager.useCache(_, _) >> { String operationDisplayName, Factory action ->
+            action.create()
+        }
+        1 * resolveIvyFactory.create(_, _, _) >> repositoryChain
+        1 * repositoryChain.artifactResolver >> artifactResolver
+        1 * repositoryChain.dependencyResolver >> dependencyToComponentResolver
+        1 * dependencyToComponentResolver.resolve(_, _) >> { DependencyMetaData dependency, BuildableComponentResolveResult resolveResult ->
+            resolveResult.resolved(componentResolveMetaData)
+        }
+        result
+        result.components.size() == 1
+        def componentResult = result.components.iterator().next()
+        componentResult.id.displayName == componentIdentifier.displayName
+        componentResult instanceof UnresolvedComponentResult
+        UnresolvedComponentResult unresolvedComponentResult = (UnresolvedComponentResult)componentResult
+        unresolvedComponentResult.failure instanceof IllegalArgumentException
+        unresolvedComponentResult.failure.message == failureMessage
+
+        where:
+        givenComponentTypeRegistry | selectedComponentType | selectedArtifactType   | failureMessage
+        testComponentTypeRegistry  | UnknownComponent      | TestArtifact           | "Not a registered component type: ${UnknownComponent.name}."
+        testComponentTypeRegistry  | TestComponent         | UnknownArtifact        | "Artifact type $UnknownArtifact.name is not registered for component type ${TestComponent.name}."
+    }
+
+    private DefaultArtifactResolutionQuery createArtifactResolutionQuery(ComponentTypeRegistry componentTypeRegistry) {
+        new DefaultArtifactResolutionQuery(configurationContainerInternal, repositoryHandler, resolveIvyFactory, globalDependencyResolutionRules, cacheLockingManager, componentTypeRegistry)
+    }
+
+    private ComponentTypeRegistry createTestComponentTypeRegistry() {
+        return Stub(ComponentTypeRegistry) {
+            getComponentRegistration(_) >> { Class componentType ->
+                if (componentType == TestComponent) {
+                    return createStubComponentRegistration()
+                } else {
+                    throw new IllegalArgumentException(String.format("Not a registered component type: %s.", componentType.getName()));
+                }
+            }
+        }
+    }
+
+    private ComponentTypeRegistration createStubComponentRegistration() {
+        return Stub(ComponentTypeRegistration) {
+            getArtifactType(_) >> { Class artifactType ->
+                throw new IllegalArgumentException(String.format("Artifact type %s is not registered for component type %s.", artifactType.getName(), TestComponent.getName()));
+            }
+        }
+    }
+
+    private static class TestComponent implements Component {
+    }
+
+    private static class TestArtifact implements Artifact {
+    }
+
+    private static class UnknownComponent implements Component {
+    }
+
+    private static class UnknownArtifact implements Artifact {
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepositoryChangingNameAfterContainerInclusion.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepositoryChangingNameAfterContainerInclusion.groovy
new file mode 100644
index 0000000..69027e0
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractArtifactRepositoryChangingNameAfterContainerInclusion.groovy
@@ -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.internal.artifacts.repositories
+
+import org.gradle.api.Project
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class AbstractArtifactRepositoryChangingNameAfterContainerInclusion extends Specification {
+    Project project = TestUtil.createRootProject()
+
+    def "cannot change the name of the repository after it has been added to a container"() {
+        def repo = new TestRepo(name: "name")
+
+        given:
+        project.repositories.add(repo)
+
+        when:
+        repo.name = "changed"
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'The name of an ArtifactRepository cannot be changed after it has been added to a repository container. You should set the name when creating the repository.'
+    }
+
+    class TestRepo extends AbstractArtifactRepository {}
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepositoryTest.groovy
new file mode 100644
index 0000000..d637e93
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepositoryTest.groovy
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.repositories
+
+import org.gradle.api.Action
+import org.gradle.api.artifacts.repositories.PasswordCredentials
+import org.gradle.api.credentials.AwsCredentials
+import org.gradle.api.credentials.Credentials
+import org.gradle.api.internal.ClosureBackedAction
+import org.gradle.internal.credentials.DefaultAwsCredentials
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class AbstractAuthenticationSupportedRepositoryTest extends Specification {
+
+    AuthSupportedRepository repo() {
+        new AuthSupportedRepository(DirectInstantiator.INSTANCE)
+    }
+
+    def "should configure default password credentials using an action only"() {
+        setup:
+        DefaultPasswordCredentials passwordCredentials = new DefaultPasswordCredentials()
+        enhanceCredentials(passwordCredentials, 'username', 'password')
+
+        Instantiator instantiator = Mock()
+        instantiator.newInstance(DefaultPasswordCredentials) >> passwordCredentials
+
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+
+        def configAction = new Action<PasswordCredentials>() {
+            @Override
+            void execute(PasswordCredentials credentials) {
+                credentials.username = "myUsername"
+                credentials.password = "myPassword"
+            }
+        }
+
+        when:
+        repo.credentials(configAction)
+
+        then:
+        repo.getCredentials(PasswordCredentials.class)
+        repo.getCredentials(PasswordCredentials.class).username == 'myUsername'
+        repo.getCredentials(PasswordCredentials.class).password == 'myPassword'
+    }
+
+    def "getCredentials(Class) instantiates credentials if not yet configured"() {
+        given:
+        DefaultAwsCredentials enhancedCredentials = new DefaultAwsCredentials()
+        enhanceCredentials(enhancedCredentials, 'accessKey', 'secretKey')
+
+        Instantiator instantiator = Mock()
+        instantiator.newInstance(DefaultAwsCredentials) >> enhancedCredentials
+
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+
+        def action = new ClosureBackedAction<DefaultAwsCredentials>({
+            accessKey = 'key'
+            secretKey = 'secret'
+        })
+
+        expect:
+        repo.getCredentials(AwsCredentials.class) instanceof AwsCredentials
+
+    }
+
+
+    @Unroll
+    def "getCredentials(Class) instantiates the correct credential types "() {
+        Instantiator instantiator = Mock()
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+
+        when:
+        repo.getCredentials(credentialType) == credentials
+
+        then:
+        1 * instantiator.newInstance(_) >> credentials
+
+        where:
+        credentialType      | credentials
+        AwsCredentials      | Mock(AwsCredentials)
+        PasswordCredentials | Mock(PasswordCredentials)
+    }
+
+    def "getCredentials(Class) throws IllegalArgumentException when setting credentials with different type than already set"() {
+        Instantiator instantiator = Mock()
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+        1 * instantiator.newInstance(_) >> credentials
+
+        when:
+        repo.getCredentials(AwsCredentials)
+        and:
+        repo.getCredentials(PasswordCredentials)
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Given credentials type '$PasswordCredentials.name' does not match actual type '$AwsCredentials.name'"
+
+        where:
+        credentials << Mock(AwsCredentials)
+    }
+
+    def "getCredentials(Class) throws IllegalArgumentException when setting credentials with unknown type"() {
+        AuthSupportedRepository repo = new AuthSupportedRepository(Mock(Instantiator))
+        when:
+        repo.getCredentials(UnsupportedCredentials)
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Unknown credentials type: '$UnsupportedCredentials.name' (supported types: $PasswordCredentials.name and $AwsCredentials.name)."
+    }
+
+    def "credentials(Class, Action) creates credentials on demand if required"() {
+        Instantiator instantiator = Mock()
+        Action action = Mock()
+
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+
+        when:
+        repo.credentials(credentialType, action)
+
+        then:
+        1 * instantiator.newInstance(_) >> credentials
+        1 * action.execute(credentials)
+        repo.getCredentials(credentialType) == credentials
+
+        where:
+        credentialType      | credentials
+        AwsCredentials      | Mock(AwsCredentials)
+        PasswordCredentials | Mock(PasswordCredentials)
+    }
+
+    def "can reference alternative credentials"() {
+        given:
+        Instantiator instantiator = Mock()
+        Action action = Mock()
+        def credentials = Mock(AwsCredentials)
+        1 * instantiator.newInstance(_) >> credentials
+        1 * action.execute(credentials)
+
+        AuthSupportedRepository repo = new AuthSupportedRepository(instantiator)
+        when:
+        repo.credentials(AwsCredentials, action)
+
+        then:
+        repo.configuredCredentials instanceof AwsCredentials
+    }
+
+    def "get credentials throws ISE if not using password credentials"() {
+        when:
+        repo().with {
+            credentials(AwsCredentials, {})
+            credentials
+        }
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "credentials(Action) throws ISE if not using password credentials"() {
+        when:
+        repo().with {
+            credentials(AwsCredentials, {})
+            credentials {}
+        }
+
+        then:
+        thrown IllegalStateException
+    }
+
+    private void enhanceCredentials(Credentials credentials, String... props) {
+        props.each { prop ->
+            credentials.metaClass."$prop" = { String val ->
+                delegate."set${prop.capitalize()}"(val)
+            }
+        }
+    }
+
+    class AuthSupportedRepository extends AbstractAuthenticationSupportedRepository {
+        AuthSupportedRepository(Instantiator instantiator) {
+            super(instantiator)
+        }
+    }
+
+    interface UnsupportedCredentials extends Credentials {}
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
new file mode 100644
index 0000000..1566abb
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultBaseRepositoryFactoryTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.artifacts.repositories
+
+import org.gradle.api.artifacts.dsl.RepositoryHandler
+import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
+
+class DefaultBaseRepositoryFactoryTest extends Specification {
+    final LocalMavenRepositoryLocator localMavenRepoLocator = Mock()
+    final FileResolver fileResolver = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final ArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
+    final ResolverStrategy resolverStrategy = Mock()
+    final MetaDataParser pomParser = Mock()
+
+    final DefaultBaseRepositoryFactory factory = new DefaultBaseRepositoryFactory(
+            localMavenRepoLocator, fileResolver, DirectInstantiator.INSTANCE, transportFactory, locallyAvailableResourceFinder,
+            resolverStrategy, artifactIdentifierFileStore, pomParser
+    )
+
+    def testCreateFlatDirResolver() {
+        expect:
+        def repo =factory.createFlatDirRepository()
+        repo instanceof DefaultFlatDirArtifactRepository
+    }
+
+    def testCreateLocalMavenRepo() {
+        given:
+        File repoDir = new File(".m2/repository")
+
+        when:
+        localMavenRepoLocator.getLocalMavenRepository() >> repoDir
+        fileResolver.resolveUri(repoDir) >> repoDir.toURI()
+
+        then:
+        def repo = factory.createMavenLocalRepository()
+        repo instanceof DefaultMavenLocalArtifactRepository
+        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
+    }
+
+    def testCreateMavenCentralRepo() {
+        given:
+        def centralUrl = new URI(RepositoryHandler.MAVEN_CENTRAL_URL)
+
+        when:
+        fileResolver.resolveUri(RepositoryHandler.MAVEN_CENTRAL_URL) >> centralUrl
+
+        then:
+        def repo = factory.createMavenCentralRepository()
+        repo instanceof DefaultMavenArtifactRepository
+        repo.url == centralUrl
+    }
+
+    def createIvyRepository() {
+        expect:
+        def repo = factory.createIvyRepository()
+        repo instanceof DefaultIvyArtifactRepository
+    }
+
+    def createMavenRepository() {
+        expect:
+        def repo = factory.createMavenRepository()
+        repo instanceof DefaultMavenArtifactRepository
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
new file mode 100644
index 0000000..e776710
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.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.repositories
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+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.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore
+import spock.lang.Specification
+
+class DefaultFlatDirArtifactRepositoryTest extends Specification {
+    final FileResolver fileResolver = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
+    final RepositoryTransport repositoryTransport = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder<ArtifactRevisionId> locallyAvailableResourceFinder = Mock()
+    final ResolverStrategy resolverStrategy = Stub()
+    final ArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
+
+    final DefaultFlatDirArtifactRepository repository = new DefaultFlatDirArtifactRepository(fileResolver, transportFactory, locallyAvailableResourceFinder, resolverStrategy, artifactIdentifierFileStore)
+
+    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.createResolver()
+
+        then:
+        1 * transportFactory.createTransport("file", "repo-name", null) >> repositoryTransport
+
+        and:
+        repo instanceof IvyResolver
+        def expectedPatterns = [
+                "${dir1.toURI()}/[artifact]-[revision](-[classifier]).[ext]",
+                "${dir1.toURI()}/[artifact](-[classifier]).[ext]",
+                "${dir2.toURI()}/[artifact]-[revision](-[classifier]).[ext]",
+                "${dir2.toURI()}/[artifact](-[classifier]).[ext]"
+        ]
+        repo.ivyPatterns == []
+        repo.artifactPatterns == expectedPatterns
+    }
+
+    def "fails when no directories specified"() {
+        given:
+        _ * fileResolver.resolveFiles(_) >> new SimpleFileCollection()
+
+        when:
+        repository.createResolver()
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == 'You must specify at least one directory for a flat directory repository.'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
new file mode 100644
index 0000000..f3657ea
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+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.file.FileResolver
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
+
+class DefaultIvyArtifactRepositoryTest extends Specification {
+    final FileResolver fileResolver = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final ResolverStrategy resolverStrategy = Stub()
+    final ArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
+
+    final DefaultIvyArtifactRepository repository = new DefaultIvyArtifactRepository(
+            fileResolver, transportFactory, locallyAvailableResourceFinder,
+            DirectInstantiator.INSTANCE, resolverStrategy, artifactIdentifierFileStore
+    )
+
+    def "default values"() {
+        expect:
+        repository.url == null
+        !repository.resolve.dynamicMode
+    }
+
+    def "creates a resolver for HTTP patterns"() {
+        repository.name = 'name'
+        repository.artifactPattern 'http://host/[organisation]/[artifact]-[revision].[ext]'
+        repository.artifactPattern 'http://other/[module]/[artifact]-[revision].[ext]'
+        repository.ivyPattern 'http://host/[module]/ivy-[revision].xml'
+
+        given:
+        fileResolver.resolveUri('http://host/') >> new URI('http://host/')
+        fileResolver.resolveUri('http://other/') >> new URI('http://other/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository == resourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[organisation]/[artifact]-[revision].[ext]', 'http://other/[module]/[artifact]-[revision].[ext]']
+            ivyPatterns == ['http://host/[module]/ivy-[revision].xml']
+        }
+    }
+
+    def "creates a resolver for file patterns"() {
+        repository.name = 'name'
+        repository.artifactPattern 'repo/[organisation]/[artifact]-[revision].[ext]'
+        repository.artifactPattern 'repo/[organisation]/[module]/[artifact]-[revision].[ext]'
+        repository.ivyPattern 'repo/[organisation]/[module]/ivy-[revision].xml'
+        def file = new File("test")
+        def fileUri = file.toURI()
+
+        given:
+        fileResolver.resolveUri('repo/') >> fileUri
+        transportFactory.createTransport({ it == ['file'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ["${fileUri}/[organisation]/[artifact]-[revision].[ext]", "${fileUri}/[organisation]/[module]/[artifact]-[revision].[ext]"]
+            ivyPatterns == ["${fileUri}/[organisation]/[module]/ivy-[revision].xml"]
+        }
+    }
+
+    def "uses ivy patterns with specified url and default layout"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+        repository.layout 'ivy'
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[type]s/[artifact](.[ext])']
+            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/[type]s/[artifact](.[ext])"]
+        }
+    }
+
+    def "uses gradle patterns with specified url and default layout"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])']
+            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml"]
+        }
+    }
+
+    def "uses maven patterns with specified url and maven layout"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+        repository.layout 'maven'
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])']
+            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml"]
+            m2compatible
+        }
+    }
+
+    def "uses specified base url with configured pattern layout"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+        repository.layout 'pattern', {
+            artifact '[module]/[revision]/[artifact](.[ext])'
+            ivy '[module]/[revision]/ivy.xml'
+        }
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[module]/[revision]/[artifact](.[ext])']
+            ivyPatterns == ["http://host/[module]/[revision]/ivy.xml"]
+            !resolver.m2compatible
+        }
+    }
+
+    def "when requested uses maven patterns with configured pattern layout"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+        repository.layout 'pattern', {
+            artifact '[module]/[revision]/[artifact](.[ext])'
+            ivy '[module]/[revision]/ivy.xml'
+            m2compatible = true
+        }
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[module]/[revision]/[artifact](.[ext])']
+            ivyPatterns == ["http://host/[module]/[revision]/ivy.xml"]
+            m2compatible
+        }
+    }
+
+    def "combines layout patterns with additionally specified patterns"() {
+        repository.name = 'name'
+        repository.url = 'http://host/'
+        repository.artifactPattern 'http://host/[other]/artifact'
+        repository.ivyPattern 'http://host/[other]/ivy'
+
+        given:
+        fileResolver.resolveUri('http://host/') >> new URI('http://host/')
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        with(resolver) {
+            it instanceof IvyResolver
+            repository instanceof ExternalResourceRepository
+            name == 'name'
+            artifactPatterns == ['http://host/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])(.[ext])', 'http://host/[other]/artifact']
+            ivyPatterns == ["http://host/[organisation]/[module]/[revision]/ivy-[revision].xml", 'http://host/[other]/ivy']
+        }
+    }
+
+    def "uses artifact pattern for ivy files when no ivy pattern provided"() {
+        repository.name = 'name'
+        repository.url = 'http://host'
+        repository.layout 'pattern', {
+            artifact '[layoutPattern]'
+        }
+        repository.artifactPattern 'http://other/[additionalPattern]'
+        transportFactory.createTransport({ it == ['http'] as Set}, 'name', null) >> transport()
+
+        given:
+        fileResolver.resolveUri('http://host') >> new URI('http://host')
+        fileResolver.resolveUri('http://other/') >> new URI('http://other/')
+
+        when:
+        def resolver = repository.createResolver()
+
+        then:
+        resolver.artifactPatterns == ['http://host/[layoutPattern]', 'http://other/[additionalPattern]']
+        resolver.ivyPatterns == resolver.artifactPatterns
+    }
+
+    def "fails when no artifact patterns specified"() {
+        given:
+        transportFactory.createHttpTransport('name', null) >> transport()
+
+        when:
+        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 RepositoryTransport transport() {
+        return Mock(RepositoryTransport) {
+            getRepository() >> resourceRepository
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
new file mode 100644
index 0000000..e30a765
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories
+import org.gradle.api.InvalidUserDataException
+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.file.FileResolver
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import spock.lang.Specification
+
+class DefaultMavenArtifactRepositoryTest extends Specification {
+    final FileResolver resolver = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
+    final ArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
+    final MetaDataParser pomParser = Stub()
+
+    final DefaultMavenArtifactRepository repository = new DefaultMavenArtifactRepository(
+            resolver, transportFactory, locallyAvailableResourceFinder, DirectInstantiator.INSTANCE, artifactIdentifierFileStore, pomParser)
+
+    def "creates local repository"() {
+        given:
+        def file = new File('repo')
+        def uri = file.toURI()
+        _ * resolver.resolveUri('repo-dir') >> uri
+        transportFactory.createTransport('file', 'repo', null) >> transport()
+
+        and:
+        repository.name = 'repo'
+        repository.url = 'repo-dir'
+
+        when:
+        def repo = repository.createRealResolver()
+
+        then:
+        repo instanceof MavenResolver
+        repo.root == uri
+    }
+
+    def "creates http repository"() {
+        given:
+        def uri = new URI("http://localhost:9090/repo")
+        _ * resolver.resolveUri('repo-dir') >> uri
+        transportFactory.createTransport('http', 'repo', null) >> transport()
+
+        and:
+        repository.name = 'repo'
+        repository.url = 'repo-dir'
+
+        when:
+        def repo = repository.createRealResolver()
+
+        then:
+        repo instanceof MavenResolver
+        repo.root == uri
+    }
+
+    def "creates repository with additional artifact URLs"() {
+        given:
+        def uri = new URI("http://localhost:9090/repo")
+        def uri1 = new URI("http://localhost:9090/repo1")
+        def uri2 = new URI("http://localhost:9090/repo2")
+        _ * resolver.resolveUri('repo-dir') >> uri
+        _ * resolver.resolveUri('repo1') >> uri1
+        _ * resolver.resolveUri('repo2') >> uri2
+        transportFactory.createTransport('http', 'repo', null) >> transport()
+
+        and:
+        repository.name = 'repo'
+        repository.url = 'repo-dir'
+        repository.artifactUrls('repo1', 'repo2')
+
+        when:
+        def repo = repository.createRealResolver()
+
+        then:
+        repo instanceof MavenResolver
+        repo.root == uri
+        repo.artifactPatterns.size() == 3
+        repo.artifactPatterns.any { it.startsWith uri.toString() }
+        repo.artifactPatterns.any { it.startsWith uri1.toString() }
+        repo.artifactPatterns.any { it.startsWith uri2.toString() }
+    }
+
+    def "creates s3 repository"() {
+        given:
+        def uri = new URI("s3://localhost:9090/repo")
+        _ * resolver.resolveUri(_) >> uri
+        transportFactory.createTransport(_, 'repo', _) >> transport()
+
+        and:
+        repository.name = 'repo'
+        repository.url = 'repo-dir'
+
+        when:
+        def repo = repository.createRealResolver()
+
+        then:
+        repo instanceof MavenResolver
+        repo.root == uri
+
+    }
+
+    def "fails when no root url specified"() {
+        when:
+        repository.createRealResolver()
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == 'You must specify a URL for a Maven repository.'
+    }
+
+    private RepositoryTransport transport() {
+        return Mock(RepositoryTransport) {
+            getRepository() >> resourceRepository
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalRepositoryTest.groovy
new file mode 100644
index 0000000..101f449
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalRepositoryTest.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
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
+import org.gradle.api.internal.artifacts.repositories.resolver.MavenLocalResolver
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.filestore.ivy.ArtifactIdentifierFileStore
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
+
+class DefaultMavenLocalRepositoryTest extends Specification {
+    final FileResolver resolver = Mock()
+    final RepositoryTransportFactory transportFactory = Mock()
+    final LocallyAvailableResourceFinder locallyAvailableResourceFinder = Mock()
+    final ExternalResourceRepository resourceRepository = Mock()
+    final ArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
+    final MetaDataParser pomParser = Stub()
+
+    final DefaultMavenArtifactRepository repository = new DefaultMavenLocalArtifactRepository(
+            resolver, transportFactory, locallyAvailableResourceFinder, DirectInstantiator.INSTANCE, artifactIdentifierFileStore, pomParser)
+    final ProgressLoggerFactory progressLoggerFactory = Mock()
+
+    def "creates local repository"() {
+        given:
+        def file = new File('repo')
+        def uri = file.toURI()
+        _ * resolver.resolveUri('repo-dir') >> uri
+        transportFactory.createTransport('file', 'repo', null) >> transport()
+
+        and:
+        repository.name = 'repo'
+        repository.url = 'repo-dir'
+
+        when:
+        def repo = repository.createRealResolver()
+
+        then:
+        repo instanceof MavenLocalResolver
+        repo.root == uri
+    }
+
+    private RepositoryTransport transport() {
+        return Mock(RepositoryTransport) {
+            getRepository() >> resourceRepository
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
new file mode 100644
index 0000000..7862a25
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ChainedVersionListerTest.groovy
@@ -0,0 +1,141 @@
+/*
+ * 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.resolver
+
+import org.gradle.api.artifacts.ModuleIdentifier
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult
+import org.gradle.internal.component.model.IvyArtifactName
+import org.gradle.internal.resource.ResourceException
+import org.gradle.internal.resource.ResourceNotFoundException
+import spock.lang.Specification
+
+class ChainedVersionListerTest extends Specification {
+
+    VersionLister lister1 = Mock()
+    VersionLister lister2 = Mock()
+
+    VersionPatternVisitor versionList1 = Mock()
+    VersionPatternVisitor versionList2 = Mock()
+
+    ResourcePattern pattern = Mock()
+    ModuleIdentifier module = Mock()
+    IvyArtifactName artifact = Mock()
+    ResourceAwareResolveResult result = Mock()
+
+    def chainedVersionLister = new ChainedVersionLister(lister1, lister2)
+
+    def "visit stops listing after first success"() {
+        def versions = []
+
+        when:
+        VersionPatternVisitor versionList = chainedVersionLister.newVisitor(module, versions, result)
+
+        then:
+        1 * lister1.newVisitor(module, versions, result) >> versionList1
+        1 * lister2.newVisitor(module, versions, result) >> versionList2
+
+        when:
+        versionList.visit(pattern, artifact)
+
+        then:
+        1 * versionList1.visit(pattern, artifact)
+        0 * _._
+    }
+
+    def "visit ignores ResourceNotFoundException when another VersionLister provides a result"() {
+        def versions = []
+
+        given:
+        lister1.newVisitor(module, versions, result) >> versionList1
+        lister2.newVisitor(module, versions, result) >> versionList2
+
+        VersionPatternVisitor versionList = chainedVersionLister.newVisitor(module, versions, result)
+
+        when:
+        versionList.visit(pattern, artifact)
+
+        then:
+        1 * versionList1.visit(pattern, artifact) >> { throw new ResourceNotFoundException(URI.create("scheme:thing"), "ignore me") }
+        1 * versionList2.visit(pattern, artifact)
+    }
+
+    def "visit fails when VersionLister throws exception"() {
+        def versions = []
+        def exception = new ResourceException(URI.create("scheme:thing"), "test resource exception")
+
+        given:
+        lister1.newVisitor(module, versions, result) >> versionList1
+        lister2.newVisitor(module, versions, result) >> versionList2
+
+        VersionPatternVisitor versionList = chainedVersionLister.newVisitor(module, versions, result)
+
+        when:
+        versionList.visit(pattern, artifact)
+
+        then:
+        def e = thrown(ResourceException)
+        e.message == "Failed to list versions for ${module}."
+        e.cause == exception
+
+        and:
+        1 * versionList1.visit(pattern, artifact) >> { throw exception }
+        0 * versionList2._
+    }
+
+    def "visit rethrows ResourceNotFoundException of failed first VersionLister"() {
+        given:
+        def exception = new ResourceNotFoundException(URI.create("scheme:thing"), "not found")
+        def versions = []
+        lister1.newVisitor(module, versions, result) >> versionList1
+        lister2.newVisitor(module, versions, result) >> versionList2
+
+        VersionPatternVisitor versionList = chainedVersionLister.newVisitor(module, versions, result)
+
+        when:
+        versionList.visit(pattern, artifact)
+
+        then:
+        def e = thrown(ResourceNotFoundException)
+        e == exception
+
+        and:
+        1 * versionList1.visit(pattern, artifact) >> { throw exception }
+        1 * versionList2.visit(pattern, artifact) >> { throw new ResourceNotFoundException(URI.create("scheme:thing"), "ignore me") }
+    }
+
+    def "visit wraps failed last VersionLister"() {
+        given:
+        def versions = []
+        def exception = new RuntimeException("broken")
+        lister1.newVisitor(module, versions, result) >> versionList1
+        lister2.newVisitor(module, versions, result) >> versionList2
+
+        VersionPatternVisitor versionList = chainedVersionLister.newVisitor(module, versions, result)
+
+        when:
+        versionList.visit(pattern, artifact)
+
+        then:
+        def e = thrown(ResourceException)
+        e.message == "Failed to list versions for ${module}."
+        e.cause == exception
+
+        and:
+        1 * versionList1.visit(pattern, artifact) >> { throw new ResourceNotFoundException(URI.create("scheme:thing"), "ignore me") }
+        1 * versionList2.visit(pattern, artifact) >> { throw exception }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
new file mode 100644
index 0000000..08d75e2
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * 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.resolver
+
+import org.gradle.api.artifacts.ArtifactIdentifier
+import org.gradle.internal.component.model.ModuleSource
+import org.gradle.internal.resolve.result.BuildableArtifactResolveResult
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import org.gradle.internal.resource.local.FileStore
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import spock.lang.Specification
+
+class ExternalResourceResolverTest extends Specification {
+    String name = "TestResolver"
+    ExternalResourceRepository repository = Mock()
+    VersionLister versionLister = Mock()
+    LocallyAvailableResourceFinder<ArtifactIdentifier> locallyAvailableResourceFinder = Mock()
+    BuildableArtifactResolveResult result = Mock()
+    ModuleComponentArtifactIdentifier artifactIdentifier = Stub() {
+        getDisplayName() >> '<some-artifact>'
+    }
+    ModuleComponentArtifactMetaData artifact = Stub() {
+        getId() >> artifactIdentifier
+    }
+    ModuleSource moduleSource = Mock()
+    File downloadedFile = Mock(File)
+    CacheAwareExternalResourceAccessor resourceAccessor = Stub()
+    FileStore<ModuleComponentArtifactMetaData> fileStore = Stub()
+    ExternalResourceResolver resolver
+
+    def setup() {
+        //We use a spy here to avoid dealing with all the overhead ivys basicresolver brings in here.
+        resolver = Spy(ExternalResourceResolver, constructorArgs: [name, true, repository, resourceAccessor, versionLister, locallyAvailableResourceFinder, fileStore])
+    }
+
+    def reportsNotFoundArtifactResolveResult() {
+        given:
+        artifactIsMissing()
+
+        when:
+        resolver.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * result.notFound(artifactIdentifier)
+        0 * result._
+    }
+
+    def reportsFailedArtifactResolveResult() {
+        given:
+        downloadIsFailing(new IOException("DOWNLOAD FAILURE"))
+
+        when:
+        resolver.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * result.failed(_) >> { ArtifactResolveException exception ->
+            assert exception.message == "Could not download <some-artifact>"
+            assert exception.cause.message == "DOWNLOAD FAILURE"
+        }
+        0 * result._
+    }
+
+    def reportsResolvedArtifactResolveResult() {
+        given:
+        artifactCanBeResolved()
+
+        when:
+        resolver.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * result.resolved(_)
+        0 * result._
+    }
+
+    def reportsResolvedArtifactResolveResultWithSnapshotVersion() {
+        given:
+        artifactIsTimestampedSnapshotVersion()
+        artifactCanBeResolved()
+
+        when:
+        resolver.resolveArtifact(artifact, moduleSource, result)
+
+        then:
+        1 * result.resolved(_)
+        0 * result._
+    }
+
+    def artifactIsTimestampedSnapshotVersion() {
+        _ * moduleSource.timestamp >> "1.0-20100101.120001-1"
+    }
+
+    def artifactIsMissing() {
+        resolver.download(_, _, _) >> null
+    }
+
+    def downloadIsFailing(IOException failure) {
+        resolver.download(_, _, _) >> {
+            throw failure
+        }
+    }
+
+    def artifactCanBeResolved() {
+        resolver.download(_, _, _) >> downloadedFile
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy
new file mode 100644
index 0000000..6f23393
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
+import org.gradle.internal.resource.local.FileStore
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import spock.lang.Specification
+
+class IvyResolverTest extends Specification {
+    def resolver = new IvyResolver("repo", Stub(RepositoryTransport), Stub(LocallyAvailableResourceFinder), false, Stub(ResolverStrategy), Stub(FileStore))
+
+    def "has useful string representation"() {
+        expect:
+        resolver.toString() == "Ivy repository 'repo'"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy
new file mode 100644
index 0000000..e322c65
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResourcePatternTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * 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.resolver
+
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetaData
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import spock.lang.Specification
+
+class IvyResourcePatternTest extends Specification {
+    def "can construct from URI and pattern"() {
+        expect:
+        new IvyResourcePattern(new URI(uri), pattern).pattern == expectedPattern
+
+        where:
+        uri                | pattern            | expectedPattern
+        "http://host/"     | "a/[revision]/c"   | "http://host/a/[revision]/c"
+        "http://host"      | "a/b/c"            | "http://host/a/b/c"
+        "http://host/"     | "/a/b/c"           | "http://host/a/b/c"
+        "http://host/"     | ""                 | "http://host/"
+        "http://host"      | ""                 | "http://host"
+        "http://host/"     | "/"                | "http://host/"
+        "http://host/repo" | "query?[revision]" | "http://host/repo/query?[revision]"
+    }
+
+    def "substitutes artifact attributes into pattern"() {
+        def pattern = new IvyResourcePattern("prefix/[organisation]-[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def artifact = artifact(group, module, version)
+
+        expect:
+        pattern.getLocation(artifact).path == expectedPath
+
+        where:
+        group       | module     | version | expectedPath
+        "group"     | "projectA" | "1.2"   | 'prefix/group-projectA/1.2/ivys/1.2/ivy.xml'
+        "org.group" | "projectA" | "1.2"   | 'prefix/org.group-projectA/1.2/ivys/1.2/ivy.xml'
+        "##??::"    | "projectA" | "1.2"   | 'prefix/##??::-projectA/1.2/ivys/1.2/ivy.xml'
+    }
+
+    def "determines artifact location by substituting artifact attributes into pattern and resolving relative to base URI"() {
+        def pattern = new IvyResourcePattern(new URI('http://server/repo'), "lookup/[organisation]-[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def artifact = artifact(group, module, version)
+
+        expect:
+        pattern.getLocation(artifact).uri == new URI(expectedUri)
+
+        where:
+        group       | module     | version | expectedUri
+        "group"     | "projectA" | "1.2"   | 'http://server/repo/lookup/group-projectA/1.2/ivys/1.2/ivy.xml'
+        "org.group" | "projectA" | "1.2"   | 'http://server/repo/lookup/org.group-projectA/1.2/ivys/1.2/ivy.xml'
+        "#?: %12"   | "projectA" | "1.2"   | 'http://server/repo/lookup/%23%3F:%20%2512-projectA/1.2/ivys/1.2/ivy.xml'
+    }
+
+    def "substitutes attributes into pattern to determine version list pattern"() {
+        def pattern = new IvyResourcePattern("prefix/[organisation]-[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def ivyName = new DefaultIvyArtifactName("ivy", "ivy", "xml")
+        def moduleId = new DefaultModuleIdentifier(group, module)
+
+        expect:
+        pattern.toVersionListPattern(moduleId, ivyName).path == expectedPath
+
+        where:
+        group       | module     | expectedPath
+        "group"     | "projectA" | 'prefix/group-projectA/[revision]/ivys/[revision]/ivy.xml'
+        "org.group" | "projectA" | 'prefix/org.group-projectA/[revision]/ivys/[revision]/ivy.xml'
+    }
+
+    private static ModuleComponentArtifactMetaData artifact(String group, String name, String version) {
+        final componentIdentifier = DefaultModuleComponentIdentifier.newId(group, name, version)
+        return new DefaultModuleComponentArtifactMetaData(new DefaultModuleComponentArtifactIdentifier(componentIdentifier, "ivy", "ivy", "xml"))
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy
new file mode 100644
index 0000000..768407e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/M2ResourcePatternTest.groovy
@@ -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.api.internal.artifacts.repositories.resolver
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetaData
+import org.gradle.internal.component.external.model.ModuleComponentArtifactMetaData
+import spock.lang.Specification
+
+import static org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier.newId
+
+class M2ResourcePatternTest extends Specification {
+    def "can construct from URI and pattern"() {
+        expect:
+        new M2ResourcePattern(new URI(uri), pattern).pattern == expectedPattern
+
+        where:
+        uri            | pattern  | expectedPattern
+        "http://host/" | "a/b/c"  | "http://host/a/b/c"
+        "http://host"  | "a/b/c"  | "http://host/a/b/c"
+        "http://host/" | "/a/b/c" | "http://host/a/b/c"
+        "http://host/" | ""       | "http://host/"
+        "http://host"  | ""       | "http://host"
+    }
+
+    def "substitutes artifact attributes into pattern"() {
+        def pattern = new M2ResourcePattern("prefix/[organisation]/[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def artifact = artifact(group, module, version)
+
+        expect:
+        pattern.getLocation(artifact).path == expectPath
+
+        where:
+        group       | module     | version | expectPath
+        "group"     | "projectA" | "1.2"   | 'prefix/group/projectA/1.2/ivys/1.2/ivy.xml'
+        "org.group" | "projectA" | "1.2"   | 'prefix/org/group/projectA/1.2/ivys/1.2/ivy.xml'
+        "##??::"    | "projectA" | "1.2"   | 'prefix/##??::/projectA/1.2/ivys/1.2/ivy.xml'
+    }
+
+    def "substitutes snapshot artifact attributes into pattern"() {
+        def pattern = new M2ResourcePattern("prefix/" + MavenPattern.M2_PATTERN)
+        def snapshotId = new MavenUniqueSnapshotComponentIdentifier("group", "projectA", "1.2-SNAPSHOT", "2014-timestamp-3333")
+
+        def artifact1 = new DefaultModuleComponentArtifactMetaData(new DefaultModuleComponentArtifactIdentifier(snapshotId, "projectA", "pom", "pom"))
+
+        expect:
+        pattern.getLocation(artifact1).path == 'prefix/group/projectA/1.2-SNAPSHOT/projectA-1.2-2014-timestamp-3333.pom'
+    }
+
+    def "determines artifact location by substituting artifact attributes into pattern and resolving relative to base URI"() {
+        def pattern = new M2ResourcePattern(new URI("http://server/lookup"), "[organisation]/[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def artifact = artifact(group, module, version)
+
+        expect:
+        pattern.getLocation(artifact).uri == new URI(expectPath)
+
+        where:
+        group       | module     | version | expectPath
+        "group"     | "projectA" | "1.2"   | 'http://server/lookup/group/projectA/1.2/ivys/1.2/ivy.xml'
+        "org.group" | "projectA" | "1.2"   | 'http://server/lookup/org/group/projectA/1.2/ivys/1.2/ivy.xml'
+        "#?:%12"    | "projectA" | "1.2"   | 'http://server/lookup/%23%3F:%2512/projectA/1.2/ivys/1.2/ivy.xml'
+    }
+
+    def "substitutes attributes into pattern to determine version list pattern"() {
+        def pattern = new M2ResourcePattern("prefix/[organisation]/[module]/[revision]/[type]s/[revision]/[artifact].[ext]")
+        def ivyName = new DefaultIvyArtifactName("projectA", "pom", "pom")
+        def moduleId = new DefaultModuleIdentifier(group, module)
+
+        expect:
+        pattern.toVersionListPattern(moduleId, ivyName).path == expectedPath
+
+        where:
+        group       | module     | expectedPath
+        "group"     | "projectA" | 'prefix/group/projectA/[revision]/poms/[revision]/projectA.pom'
+        "org.group" | "projectA" | 'prefix/org/group/projectA/[revision]/poms/[revision]/projectA.pom'
+    }
+
+    def "can build module path"() {
+        def pattern = new M2ResourcePattern("prefix/" + MavenPattern.M2_PATTERN)
+        def module1 = new DefaultModuleIdentifier("group", "projectA")
+        def module2 = new DefaultModuleIdentifier("org.group", "projectA")
+
+        expect:
+        pattern.toModulePath(module1).path == 'prefix/group/projectA'
+        pattern.toModulePath(module2).path == 'prefix/org/group/projectA'
+    }
+
+    def "can build module version path"() {
+        def pattern = new M2ResourcePattern("prefix/" + MavenPattern.M2_PATTERN)
+        def component1 = newId("group", "projectA", "1.2")
+        def component2 = newId("org.group", "projectA", "1.2")
+
+        expect:
+        pattern.toModuleVersionPath(component1).path == 'prefix/group/projectA/1.2'
+        pattern.toModuleVersionPath(component2).path == 'prefix/org/group/projectA/1.2'
+    }
+
+    def "throws UnsupportedOperationException for non M2 compatible pattern"() {
+        def pattern = new M2ResourcePattern("/non/m2/pattern")
+
+        when:
+        pattern.toModulePath(new DefaultModuleIdentifier("group", "module"))
+
+        then:
+        thrown(UnsupportedOperationException)
+    }
+
+    private static ModuleComponentArtifactMetaData artifact(String group, String name, String version) {
+        final moduleVersionId = newId(group, name, version)
+        return new DefaultModuleComponentArtifactMetaData(new DefaultModuleComponentArtifactIdentifier(moduleVersionId, "ivy", "ivy", "xml"))
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
new file mode 100644
index 0000000..2d11f23
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
+import org.gradle.internal.resource.local.FileStore
+import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
+import spock.lang.Specification
+
+class MavenResolverTest extends Specification {
+    def resolver = new MavenResolver("repo", new URI("http://localhost"), Stub(RepositoryTransport), Stub(LocallyAvailableResourceFinder), Stub(FileStore), Stub(MetaDataParser))
+
+    def "has useful string representation"() {
+        expect:
+        resolver.toString() == "Maven repository 'repo'"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifierTest.groovy
new file mode 100644
index 0000000..fa7c695
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotComponentIdentifierTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 spock.lang.Specification
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class MavenUniqueSnapshotComponentIdentifierTest extends Specification {
+
+    def "delegates module version to owner"() {
+        def id = new MavenUniqueSnapshotComponentIdentifier("group", "module", version, "timestamp")
+
+        expect:
+        id.group == "group"
+        id.module == "module"
+        id.version == version
+        id.timestamp == "timestamp"
+
+        where:
+        version         | _
+        "1.0-SNAPSHOT"  | _
+        "1.0-timestamp" | _
+    }
+
+    def "can request timestamp or symbolic version"() {
+        def id = new MavenUniqueSnapshotComponentIdentifier("group", "module", version, "timestamp")
+
+        expect:
+        id.timestampedVersion == "1.0-timestamp"
+        id.snapshotVersion == "1.0-SNAPSHOT"
+
+        where:
+        version         | _
+        "1.0-SNAPSHOT"  | _
+        "1.0-timestamp" | _
+    }
+
+    def "has useful display name"() {
+        def id = new MavenUniqueSnapshotComponentIdentifier("group", "module", version, "timestamp")
+
+        expect:
+        id.displayName == "group:module:1.0-SNAPSHOT:timestamp"
+
+        where:
+        version         | _
+        "1.0-SNAPSHOT"  | _
+        "1.0-timestamp" | _
+    }
+
+    def "can compare with other instance"() {
+        when:
+        def id1 = new MavenUniqueSnapshotComponentIdentifier("group", "module", "1.0-SNAPSHOT", "timestamp")
+        def same = new MavenUniqueSnapshotComponentIdentifier("group", "module", "1.0-SNAPSHOT", "timestamp")
+        def differentVersion = new MavenUniqueSnapshotComponentIdentifier("group", "module", "1.1-SNAPSHOT", "timestamp")
+        def differentTimestamp = new MavenUniqueSnapshotComponentIdentifier("group", "module", "1.0-SNAPSHOT", "other")
+
+        then:
+        assert strictlyEquals(id1, same)
+        assert id1.hashCode() != differentVersion.hashCode()
+        assert id1 != differentVersion
+        assert id1.hashCode() != differentTimestamp
+        assert id1 != differentTimestamp
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolverTest.groovy
new file mode 100644
index 0000000..684e15f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenUniqueSnapshotExternalResourceArtifactResolverTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.resolve.result.ResourceAwareResolveResult
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetaData
+import org.gradle.internal.component.model.IvyArtifactName
+import org.gradle.internal.resource.local.LocallyAvailableExternalResource
+import spock.lang.Specification
+
+class MavenUniqueSnapshotExternalResourceArtifactResolverTest extends Specification {
+
+    def delegate = Mock(ExternalResourceArtifactResolver)
+    def resolver = new MavenUniqueSnapshotExternalResourceArtifactResolver(delegate, "timestamp")
+
+    def "creates timestamped artifact"() {
+        when:
+        def originalComponentId = DefaultModuleComponentIdentifier.newId("group", "name", "1.0-SNAPSHOT")
+        def originalIvyName = Mock(IvyArtifactName)
+        def originalArtifact = new DefaultModuleComponentArtifactMetaData(originalComponentId, originalIvyName)
+        def artifact = resolver.timestamp(originalArtifact)
+
+        then:
+        with (artifact.id.componentIdentifier) {
+            group == "group"
+            module == "name"
+            version == "1.0-SNAPSHOT"
+            timestamp == "timestamp"
+            timestampedVersion == "1.0-timestamp"
+        }
+    }
+
+    def "delegates with timestamped artifact"() {
+        given:
+        def originalComponentId = DefaultModuleComponentIdentifier.newId("group", "name", "1.0-SNAPSHOT")
+        def originalIvyName = new DefaultIvyArtifactName("name", "type", "extension")
+        def originalArtifact = new DefaultModuleComponentArtifactMetaData(originalComponentId, originalIvyName)
+        def artifact = resolver.timestamp(originalArtifact)
+        def result = Mock(ResourceAwareResolveResult)
+        def resource1 = Mock(LocallyAvailableExternalResource)
+        def resource2 = Mock(LocallyAvailableExternalResource)
+
+        when:
+        1 * delegate.resolveMetaDataArtifact({ it.id == artifact.id }, result) >> resource1
+        1 * delegate.resolveArtifact({ it.id == artifact.id }, result) >> resource2
+        1 * delegate.artifactExists({ it.id == artifact.id }, result) >> true
+
+        then:
+        resolver.resolveMetaDataArtifact(originalArtifact, result) == resource1
+        resolver.resolveArtifact(originalArtifact, result) == resource2
+        resolver.artifactExists(originalArtifact, result)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
new file mode 100644
index 0000000..336f58f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenVersionListerTest.groovy
@@ -0,0 +1,212 @@
+/*
+ * 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.resolver
+
+import org.gradle.api.Action
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.internal.resolve.result.DefaultResourceAwareResolveResult
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.UncheckedException
+import org.gradle.internal.resource.ExternalResource
+import org.gradle.internal.resource.ResourceException
+import org.gradle.internal.resource.ResourceNotFoundException
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.xml.sax.SAXParseException
+import spock.lang.Specification
+
+class MavenVersionListerTest extends Specification {
+    def repo = Mock(ExternalResourceRepository)
+    def moduleRevisionId = IvyUtil.createModuleRevisionId("org.acme", "testproject", "1.0")
+    def module = new DefaultModuleIdentifier("org.acme", "testproject")
+    def result = new DefaultResourceAwareResolveResult()
+    def moduleVersion = new DefaultModuleVersionIdentifier(module, "1.0")
+    def artifact = new DefaultIvyArtifactName("testproject", "jar", "jar")
+
+    def repository = Mock(ExternalResourceRepository)
+    def pattern = pattern("testRepo/" + MavenPattern.M2_PATTERN)
+    def metaDataResource = new URI('testRepo/org/acme/testproject/maven-metadata.xml')
+
+    final MavenVersionLister lister = new MavenVersionLister(repository)
+
+    def "visit parses maven-metadata.xml"() {
+        ExternalResource resource = Mock()
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern, artifact)
+
+        then:
+        versions == ['1.1', '1.2']
+        result.attempted == [metaDataResource.toString()]
+
+        and:
+        1 * repository.getResource(metaDataResource) >> resource
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
+<metadata>
+    <versioning>
+        <versions>
+            <version>1.1</version>
+            <version>1.2</version>
+        </versions>
+    </versioning>
+</metadata>""".bytes))
+        }
+        1 * resource.close()
+        0 * repository._
+        0 * resource._
+    }
+
+    def "visit builds union of versions"() {
+        ExternalResource resource1 = Mock()
+        ExternalResource resource2 = Mock()
+        def pattern1 = pattern("prefix1/" + MavenPattern.M2_PATTERN)
+        def pattern2 = pattern("prefix2/" + MavenPattern.M2_PATTERN)
+        def location1 = new URI('prefix1/org/acme/testproject/maven-metadata.xml')
+        def location2 = new URI('prefix2/org/acme/testproject/maven-metadata.xml')
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern1, artifact)
+        versionList.visit(pattern2, artifact)
+
+        then:
+        versions == ['1.1', '1.2', '1.2', '1.3']
+        result.attempted == [location1.toString(), location2.toString()]
+
+        and:
+        1 * repository.getResource(location1) >> resource1
+        1 * resource1.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
+<metadata>
+    <versioning>
+        <versions>
+            <version>1.1</version>
+            <version>1.2</version>
+        </versions>
+    </versioning>
+</metadata>""".bytes))
+        }
+        1 * repository.getResource(location2) >> resource2
+        1 * resource2.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
+<metadata>
+    <versioning>
+        <versions>
+            <version>1.2</version>
+            <version>1.3</version>
+        </versions>
+    </versioning>
+</metadata>""".bytes))
+        }
+    }
+
+    def "visit ignores duplicate patterns"() {
+        ExternalResource resource = Mock()
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern, artifact)
+        versionList.visit(pattern, artifact)
+
+        then:
+        versions == ['1.1', '1.2']
+        result.attempted == [metaDataResource.toString()]
+
+        and:
+        1 * repository.getResource(metaDataResource) >> resource
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("""
+<metadata>
+    <versioning>
+        <versions>
+            <version>1.1</version>
+            <version>1.2</version>
+        </versions>
+    </versioning>
+</metadata>""".bytes))
+        }
+        1 * resource.close()
+        0 * repository._
+        0 * resource._
+    }
+
+    def "visit throws ResourceNotFoundException when maven-metadata not available"() {
+        when:
+        def versionList = lister.newVisitor(module, [], result)
+        versionList.visit(pattern, artifact)
+
+        then:
+        ResourceNotFoundException e = thrown()
+        e.message == "Maven meta-data not available: $metaDataResource"
+
+        and:
+        result.attempted == [metaDataResource.toString()]
+
+        and:
+        1 * repository.getResource(metaDataResource) >> null
+        0 * repository._
+    }
+
+    def "visit throws ResourceException when maven-metadata cannot be parsed"() {
+        ExternalResource resource = Mock()
+
+        when:
+        def versionList = lister.newVisitor(module, [], result)
+        versionList.visit(pattern, artifact)
+
+        then:
+        ResourceException e = thrown()
+        e.message == "Unable to load Maven meta-data from $metaDataResource."
+        e.cause instanceof UncheckedException
+        e.cause.cause instanceof SAXParseException
+
+        and:
+        result.attempted == [metaDataResource.toString()]
+
+        and:
+        1 * resource.close()
+        1 * repository.getResource(metaDataResource) >> resource;
+        1 * resource.withContent(_) >> { Action action -> action.execute(new ByteArrayInputStream("yo".bytes)) }
+        0 * repository._
+    }
+
+    def "visit throws ResourceException when maven-metadata cannot be loaded"() {
+        def failure = new RuntimeException()
+
+        when:
+        def versionList = lister.newVisitor(module, [], result)
+        versionList.visit(pattern, artifact)
+
+        then:
+        ResourceException e = thrown()
+        e.message == "Unable to load Maven meta-data from $metaDataResource."
+        e.cause == failure
+
+        and:
+        result.attempted == [metaDataResource.toString()]
+
+        and:
+        1 * repository.getResource(metaDataResource) >> { throw failure }
+        0 * repository._
+    }
+
+    def pattern(String pattern) {
+        return new M2ResourcePattern(pattern)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
new file mode 100644
index 0000000..cd99085
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ResourceVersionListerTest.groovy
@@ -0,0 +1,198 @@
+/*
+ * 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.resolver
+
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.resolve.result.DefaultResourceAwareResolveResult
+import org.gradle.internal.resource.ResourceException
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ResourceVersionListerTest extends Specification {
+
+    def repo = Mock(ExternalResourceRepository)
+    def moduleRevisionId = IvyUtil.createModuleRevisionId("org.acme", "proj1", "1.0")
+    def module = new DefaultModuleIdentifier("org.acme", "proj1")
+    def moduleVersion = new DefaultModuleVersionIdentifier(module, "1.0")
+    def artifact = new DefaultIvyArtifactName("proj1", "jar", "jar")
+    def result = new DefaultResourceAwareResolveResult()
+
+    def ResourceVersionLister lister;
+
+    def setup() {
+        lister = new ResourceVersionLister(repo)
+    }
+
+    def "visit propagates Exceptions as ResourceException"() {
+        setup:
+        def failure = new RuntimeException("Test IO Exception")
+        def testPattern = pattern("/a/pattern/with/[revision]/")
+        1 * repo.list(_) >> { throw failure }
+
+        when:
+        def versionList = lister.newVisitor(module, [], result)
+        versionList.visit(testPattern, artifact)
+
+        then:
+        ResourceException e = thrown()
+        e.message == "Could not list versions using Ivy pattern '/a/pattern/with/[revision]/'."
+        e.cause == failure
+    }
+
+    def "visit produces empty versionList for missing resource"() {
+        setup:
+        1 * repo.list(_) >> null
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern(testPattern), artifact)
+
+        then:
+        versions.empty
+
+        where:
+        testPattern << ["/some/[revision]", "/some/version-[revision]"]
+    }
+
+    def "visit returns empty VersionList when repository contains empty list"() {
+        setup:
+        1 * repo.list(_) >> []
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern("/some/[revision]"), artifact)
+
+        then:
+        versions.empty
+    }
+
+    @Unroll
+    def "visit resolves versions from pattern with '#testPattern'"() {
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern(testPattern), artifact)
+
+        then:
+        versions == ["1", "2.1", "a-version"]
+
+        and:
+        1 * repo.list(URI.create(repoListingPath)) >> repoResult
+        0 * repo._
+
+        where:
+        testPattern                              | repoListingPath | repoResult
+        "[revision]"                             | ""              | ["1", "2.1", "a-version"]
+        "[revision]/"                            | ""              | ["1", "2.1", "a-version"]
+        "/[revision]"                            | "/"             | ["1", "2.1", "a-version"]
+        "/[revision]/"                           | "/"             | ["1", "2.1", "a-version"]
+        "/some/[revision]"                       | "/some/"        | ["1", "2.1", "a-version"]
+        "/some/[revision]/"                      | "/some/"        | ["1", "2.1", "a-version"]
+        "/some/[revision]/lib"                   | "/some/"        | ["1", "2.1", "a-version"]
+        "/some/version-[revision]"               | "/some/"        | ["version-1", "version-2.1", "version-a-version", "nonmatching"]
+        "/some/version-[revision]/lib"           | "/some/"        | ["version-1", "version-2.1", "version-a-version", "nonmatching"]
+        "/some/version-[revision]/lib/"          | "/some/"        | ["version-1", "version-2.1", "version-a-version", "nonmatching"]
+        "/some/[revision]-version"               | "/some/"        | ["1-version", "2.1-version", "a-version-version", "nonmatching"]
+        "/some/[revision]-version/lib"           | "/some/"        | ["1-version", "2.1-version", "a-version-version", "nonmatching"]
+        "/some/[revision]-lib.[ext]"             | "/some/"        | ["1-lib.jar", "1-lib.zip", "2.1-lib.jar", "a-version-lib.jar", "nonmatching"]
+        "/some/any-[revision]-version/lib"       | "/some/"        | ["any-1-version", "any-2.1-version", "any-a-version-version", "nonmatching"]
+        "/some/any-[revision]-version/lib/"      | "/some/"        | ["any-1-version", "any-2.1-version", "any-a-version-version", "nonmatching"]
+        "/some/[revision]/lib/myjar-[revision]/" | "/some/"        | ["1", "2.1", "a-version"]
+        "/some/proj-[revision]/[revision]/lib/"  | "/some/"        | ["proj-1", "proj-2.1", "proj-a-version"]
+    }
+
+    def "visit builds union of versions"() {
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        def pattern1 = pattern("/[revision]/[artifact]-[revision].[ext]")
+        def pattern2 = pattern("/[organisation]/[revision]/[artifact]-[revision].[ext]")
+        versionList.visit(pattern1, artifact)
+        versionList.visit(pattern2, artifact)
+
+        then:
+        versions == ["1.2", "1.3", "1.3", "1.4"]
+
+        and:
+        1 * repo.list(URI.create("/")) >> ["1.2", "1.3"]
+        1 * repo.list(URI.create("/org.acme/")) >> ["1.3", "1.4"]
+        0 * repo._
+    }
+
+    def "visit ignores duplicate patterns"() {
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        final patternA = pattern("/a/[revision]/[artifact]-[revision].[ext]")
+        versionList.visit(patternA, artifact)
+        versionList.visit(pattern("/a/[revision]/[artifact]-[revision]"), artifact)
+
+        then:
+        versions == ["1.2", "1.3"]
+
+        and:
+        1 * repo.list(URI.create("/a/")) >> ["1.2", "1.3"]
+        0 * repo._
+    }
+
+    def "visit substitutes non revision placeholders from pattern before hitting repository"() {
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern(inputPattern), artifact)
+
+        then:
+        1 * repo.list(URI.create(repoPath)) >> ['1.2']
+
+        where:
+        inputPattern                                  | repoPath
+        "/[organisation]/[revision]"                  | "/org.acme/"
+        "/[organization]/[revision]"                  | "/org.acme/"
+        "/[module]/[revision]"                        | "/proj1/"
+        "/[module]/[revision]-lib.[ext]"              | "/proj1/"
+        "/[organisation]/[module]/[revision]"         | "/org.acme/proj1/"
+        "/[revision]/[module]/[organisation]"         | "/"
+        "/[type]s/[module]/[organisation]/[revision]" | "/jars/proj1/org.acme/"
+    }
+
+    def "visit returns empty version list when pattern has no revision token"() {
+        setup:
+        repo.list(_) >> repoResult
+
+        when:
+        def versions = []
+        def versionList = lister.newVisitor(module, versions, result)
+        versionList.visit(pattern(testPattern), artifact)
+
+        then:
+        versions.empty
+
+        where:
+        testPattern                      | repoResult
+        "/some/pattern/with/no/revision" | ["/some/1-version", "/some/2.1-version", "/some/a-version-version"]
+    }
+
+    def pattern(String pattern) {
+        return new IvyResourcePattern(pattern)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactoryTest.groovy
new file mode 100644
index 0000000..ffafd6f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/transport/RepositoryTransportFactoryTest.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.repositories.transport
+import com.google.common.collect.Lists
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.repositories.PasswordCredentials
+import org.gradle.api.internal.artifacts.repositories.DefaultPasswordCredentials
+import org.gradle.internal.credentials.DefaultAwsCredentials
+import org.gradle.internal.resource.connector.ResourceConnectorFactory
+import org.gradle.internal.resource.transport.ResourceConnectorRepositoryTransport
+import spock.lang.Specification
+
+class RepositoryTransportFactoryTest extends Specification {
+
+    def connectorFactory1 = Mock(ResourceConnectorFactory)
+    def connectorFactory2 = Mock(ResourceConnectorFactory)
+    def repositoryTransportFactory
+
+    def setup() {
+        connectorFactory1.getSupportedProtocols() >> (["protocol1"] as Set)
+        connectorFactory2.getSupportedProtocols() >> (["protocol2a", "protocol2b"] as Set)
+        List<ResourceConnectorFactory> resourceConnectorFactories = Lists.newArrayList(connectorFactory1, connectorFactory2)
+        repositoryTransportFactory = new RepositoryTransportFactory(resourceConnectorFactories, null, null, null, null, null)
+    }
+
+    def "cannot create a transport for url with unsupported scheme"() {
+        when:
+        repositoryTransportFactory.createTransport(['unsupported'] as Set, null, null)
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == "Not a supported repository protocol 'unsupported': valid protocols are [file, protocol1, protocol2a, protocol2b]"
+    }
+
+    def "cannot creates a transport for mixed url scheme"() {
+        when:
+        repositoryTransportFactory.createTransport(['protocol1', 'protocol2b'] as Set, null, null)
+
+        then:
+        InvalidUserDataException e = thrown()
+        e.message == "You cannot mix different URL schemes for a single repository. Please declare separate repositories."
+    }
+
+    def "should create a transport for known scheme"() {
+        def credentials = Mock(DefaultPasswordCredentials)
+
+        when:
+        def transport = repositoryTransportFactory.createTransport(['protocol1'] as Set, null, credentials)
+
+        then:
+        transport.class == ResourceConnectorRepositoryTransport
+    }
+
+    def "should throw when credentials types is invalid"(){
+        when:
+        repositoryTransportFactory.convertPasswordCredentials(new DefaultAwsCredentials())
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Credentials must be an instance of: ${PasswordCredentials.class.getCanonicalName()}"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResultTest.groovy
new file mode 100644
index 0000000..d8c536a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultArtifactResolutionResultTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.result
+
+import org.gradle.api.artifacts.result.ArtifactResolutionResult
+import org.gradle.api.artifacts.result.ComponentArtifactsResult
+import org.gradle.api.artifacts.result.UnresolvedComponentResult
+import spock.lang.Specification
+
+class DefaultArtifactResolutionResultTest extends Specification {
+    ComponentArtifactsResult componentArtifactsResult1 = Mock()
+    ComponentArtifactsResult componentArtifactsResult2 = Mock()
+    UnresolvedComponentResult unresolvedComponentResult1 = Mock()
+    UnresolvedComponentResult unresolvedComponentResult2 = Mock()
+
+    def "component results match the ones passed into the constructor"() {
+        ArtifactResolutionResult artifactResolutionResult = new DefaultArtifactResolutionResult([componentArtifactsResult1, unresolvedComponentResult1] as LinkedHashSet)
+
+        expect:
+        artifactResolutionResult.components.size() == 2
+        artifactResolutionResult.components.contains(componentArtifactsResult1)
+        artifactResolutionResult.components.contains(unresolvedComponentResult1)
+    }
+
+    def "gets resolved components for component artifact results"() {
+        ArtifactResolutionResult artifactResolutionResult = new DefaultArtifactResolutionResult([componentArtifactsResult1, componentArtifactsResult2] as LinkedHashSet)
+
+        when:
+        Set<ComponentArtifactsResult> resolvedComponents = artifactResolutionResult.resolvedComponents
+
+        then:
+        resolvedComponents.size() == 2
+        resolvedComponents.each { assert it instanceof ComponentArtifactsResult }
+    }
+
+    def "gets resolved components for unresolved artifact results"() {
+        ArtifactResolutionResult artifactResolutionResult = new DefaultArtifactResolutionResult([unresolvedComponentResult1, unresolvedComponentResult2] as LinkedHashSet)
+
+        when:
+        Set<ComponentArtifactsResult> resolvedComponents = artifactResolutionResult.resolvedComponents
+
+        then:
+        resolvedComponents.size() == 0
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResultTest.groovy
new file mode 100644
index 0000000..6bb571c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultComponentArtifactsResultTest.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.api.internal.artifacts.result
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.artifacts.result.ArtifactResult
+import org.gradle.api.component.Artifact
+import spock.lang.Specification
+
+class DefaultComponentArtifactsResultTest extends Specification {
+    def id = Mock(ComponentIdentifier)
+    def result = new DefaultComponentArtifactsResult(id)
+
+    def "returns artifacts matching type"() {
+        given:
+        def artifact = Mock(ArtifactResult)
+        result.addArtifact(artifact)
+
+        when:
+        artifact.type >> ArtifactOne
+
+        then:
+        result.getArtifacts(Artifact) == [artifact] as Set
+        result.getArtifacts(ArtifactOne) == [artifact] as Set
+        result.getArtifacts(ArtifactTwo) == [] as Set
+    }
+
+    interface ArtifactOne extends Artifact {}
+    interface ArtifactTwo extends Artifact {}
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
new file mode 100644
index 0000000..6613eb0
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
@@ -0,0 +1,113 @@
+/*
+ * 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.result
+
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.Factory
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
+
+class DefaultResolutionResultTest extends Specification {
+
+    def "provides all modules and dependencies including unresolved"() {
+        given:
+        def dep1 = newDependency('dep1')
+        def dep2 = newDependency('dep2')
+
+        def root = newModule('root').addDependency(dep1).addDependency(dep2)
+
+        def dep3 = newDependency('dep3')
+        def dep4 = newUnresolvedDependency('dep4')
+
+        dep2.selected.addDependency(dep3).addDependency(dep4)
+
+        when:
+        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
+        def modules = new DefaultResolutionResult({root} as Factory).allComponents
+
+        then:
+        deps == [dep1, dep2, dep3, dep4] as Set
+
+        and:
+        //does not contain unresolved dep, contains root
+        modules == [root, dep1.selected, dep2.selected, dep3.selected] as Set
+    }
+
+    def "provides hooks for iterating each module or dependency exactly once"() {
+        given:
+        //root -> dep1,dep2; dep1 -> dep3
+        def dep = newDependency('dep1')
+        def dep3 = newDependency('dep3')
+        def root = newModule('root').addDependency(dep).addDependency(newDependency('dep2')).addDependency(dep3)
+        dep.selected.addDependency(dep3)
+
+        def result = new DefaultResolutionResult({root} as Factory)
+
+        when:
+        def deps = []
+        def modules = []
+        result.allDependencies { deps << it }
+        result.allComponents { modules << it }
+
+        then:
+        deps*.requested.group == ['dep1', 'dep3', 'dep2', 'dep3']
+
+        and:
+        modules*.id.group == ['root', 'dep1', 'dep3', 'dep2']
+    }
+
+    def "deals with dependency cycles"() {
+        given:
+        // a->b->a
+        def root = newModule('a', 'a', '1')
+        def dep1 = newDependency('b', 'b', '1')
+        root.addDependency(dep1)
+        dep1.selected.addDependency(new DefaultResolvedDependencyResult(DefaultModuleComponentSelector.newSelector('a', 'a', '1'), root, dep1.selected))
+
+        when:
+        def deps = new DefaultResolutionResult({root} as Factory).allDependencies
+        def modules = new DefaultResolutionResult({root} as Factory).allComponents
+
+        then:
+        deps.size() == 2
+        modules.size() == 2
+    }
+
+    def "mutating all dependencies or modules is harmless"() {
+        given:
+        def dep1 = newDependency('dep1')
+        def dep2 = newDependency('dep2')
+
+        def root = newModule('root').addDependency(dep1).addDependency(dep2)
+
+        when:
+        def result = new DefaultResolutionResult({root} as Factory)
+
+        then:
+        result.allDependencies == [dep1, dep2] as Set
+        result.allComponents == [root, dep1.selected, dep2.selected] as Set
+
+        when:
+        result.allDependencies << newDependency('dep3')
+        result.allComponents << newModule('foo')
+
+        then:
+        result.allDependencies == [dep1, dep2] as Set
+        result.allComponents == [root, dep1.selected, dep2.selected] as Set
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResultTest.groovy
new file mode 100644
index 0000000..e578c4b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResultTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * 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.result
+
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.*
+
+class DefaultResolvedComponentResultTest extends Specification {
+
+    def "mutating dependencies or dependents is harmless"() {
+        given:
+        def module = newModule("a", "c", "1")
+        def dependency  = newDependency("a", "x", "1")
+        def dependent   = newDependency("a", "x2", "1")
+
+        when:
+        module.addDependency(dependency)
+        module.addDependent(dependent)
+
+        then:
+        module.dependencies == [dependency] as Set
+        module.dependents   == [dependent] as Set
+
+        when:
+        module.dependencies << newDependency("a", "y", "1")
+        then:
+        thrown(UnsupportedOperationException)
+
+        when:
+        module.dependents <<   newDependency("a", "y2", "1")
+        then:
+        thrown(UnsupportedOperationException)
+    }
+
+    def "includes unresolved dependencies"() {
+        given:
+        def module = newModule()
+        def dependency = newDependency()
+        def unresolved = newUnresolvedDependency()
+
+        when:
+        module.addDependency(dependency)
+        module.addDependency(unresolved)
+
+        then:
+        module.dependencies == [dependency, unresolved] as Set
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationConverterTest.groovy
new file mode 100755
index 0000000..27048e0
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyClassPathNotationConverterTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations
+
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.SelfResolvingDependency
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.ClassPathRegistry
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
+import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.classpath.ClassPath
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.typeconversion.NotationParserBuilder
+import spock.lang.Specification
+
+public class DependencyClassPathNotationConverterTest extends Specification {
+    def instantiator = Mock(Instantiator.class)
+    def classPathRegistry = Mock(ClassPathRegistry.class)
+    def fileResolver = Mock(FileResolver.class)
+    def factory = new DependencyClassPathNotationConverter(instantiator, classPathRegistry, fileResolver)
+
+    def "parses classpath literals"() {
+        given:
+        def dependency = Mock(SelfResolvingDependency.class)
+        def fileCollection = Mock(FileCollection.class)
+        def classpath = Mock(ClassPath.class)
+        def files = [new File('foo')]
+
+        and:
+        classPathRegistry.getClassPath('GRADLE_API') >> classpath
+        classpath.asFiles >> files
+        fileResolver.resolveFiles(files) >> fileCollection
+        instantiator.newInstance(DefaultSelfResolvingDependency.class, fileCollection as Object) >> dependency
+
+        when:
+        def out = parse(DependencyFactory.ClassPathNotation.GRADLE_API)
+
+        then:
+        out.is dependency
+
+        when: // same instance is reused
+        def out2 = parse(DependencyFactory.ClassPathNotation.GRADLE_API)
+
+        then:
+        0 * instantiator._
+        out2.is out
+    }
+
+    def parse(def value) {
+        return NotationParserBuilder.toType(Dependency).fromType(DependencyFactory.ClassPathNotation, factory).toComposite().parseNotation(value)
+    }
+
+}
+
+
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationConverterTest.groovy
new file mode 100644
index 0000000..d735d8a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyMapNotationConverterTest.groovy
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.notations
+
+import org.gradle.api.artifacts.DependencyArtifact
+import org.gradle.api.artifacts.ExternalModuleDependency
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.typeconversion.NotationParserBuilder
+import spock.lang.Specification
+
+public class DependencyMapNotationConverterTest extends Specification {
+
+    def parser = NotationParserBuilder.toType(ExternalModuleDependency).converter(new DependencyMapNotationConverter<DefaultExternalModuleDependency>(DirectInstantiator.INSTANCE, DefaultExternalModuleDependency.class)).toComposite()
+
+    def "with artifact"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name: 'gradle-core', version: '4.4-beta2', ext: 'mytype'])
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '4.4-beta2'
+
+        !d.force
+        !d.transitive
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == null && it.type == 'mytype' }
+    }
+
+    def "with classified artifact"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name: 'gradle-core', version: '10', ext: 'zip', classifier: 'jdk-1.4'])
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10'
+
+        !d.force
+        !d.transitive
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' && it.type == 'zip' }
+    }
+
+    def "with classifier"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'10', classifier:'jdk-1.4']);
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10'
+        d.transitive
+
+        !d.force
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' &&
+                it.type == DependencyArtifact.DEFAULT_TYPE && it.extension == DependencyArtifact.DEFAULT_TYPE }
+    }
+
+    def "with 3-element map"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0']);
+
+        then:
+        d.group == 'org.gradle'
+        d.name == 'gradle-core'
+        d.version == '1.0'
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with 3-element map and configuration"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0', configuration:'compile']);
+
+        then:
+        d.group == 'org.gradle'
+        d.name == 'gradle-core'
+        d.version == '1.0'
+        d.configuration == 'compile'
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with 3-element map and property"() {
+        when:
+        def d = parser.parseNotation([group: 'org.gradle', name:'gradle-core', version:'1.0', transitive:false]);
+
+        then:
+        d.group == 'org.gradle'
+        d.name == 'gradle-core'
+        d.version == '1.0'
+
+        !d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with no group and no version"() {
+        when:
+        def d = parser.parseNotation([name:'foo'])
+
+        then:
+        d.group == null
+        d.name == 'foo'
+        d.version == null
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationConverterTest.groovy
new file mode 100644
index 0000000..f370082
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/DependencyStringNotationConverterTest.groovy
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.notations
+
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.DependencyArtifact
+import org.gradle.api.internal.artifacts.dependencies.DefaultClientModule
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.typeconversion.NotationParserBuilder
+import spock.lang.Specification
+
+public class DependencyStringNotationConverterTest extends Specification {
+    def parser = new DependencyStringNotationConverter(DirectInstantiator.INSTANCE, DefaultExternalModuleDependency.class);
+
+    def "with artifact"() {
+        when:
+        def d = parse(parser, 'org.gradle:gradle-core:4.4-beta2 at mytype')
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '4.4-beta2'
+
+        !d.force
+        !d.transitive
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == null && it.type == 'mytype' }
+    }
+
+    def "with classified artifact"() {
+        when:
+        def d = parse(parser, 'org.gradle:gradle-core:10:jdk-1.4 at zip')
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10'
+
+        !d.force
+        !d.transitive
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' && it.type == 'zip' }
+    }
+
+    def "with classifier"() {
+        when:
+        def d = parse(parser, 'org.gradle:gradle-core:10:jdk-1.4')
+
+        then:
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10'
+        d.transitive
+
+        !d.force
+        !d.changing
+
+        d.artifacts.size() == 1
+        d.artifacts.find { it.name == 'gradle-core' && it.classifier == 'jdk-1.4' &&
+                it.type == DependencyArtifact.DEFAULT_TYPE && it.extension == DependencyArtifact.DEFAULT_TYPE }
+    }
+
+    def "with 3-element GString"() {
+        when:
+        def descriptor = 'org.gradle:gradle-core:1.0'
+        def gstring = "$descriptor"
+        def d = parse(parser, gstring)
+
+        then:
+        d.group == 'org.gradle'
+        d.name == 'gradle-core'
+        d.version == '1.0'
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with no group"() {
+        when:
+        def d = parse(parser, ":foo:1.0")
+
+        then:
+        d.group == null
+        d.name == 'foo'
+        d.version == '1.0'
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with no version"() {
+        when:
+        def d = parse(parser, "hey:foo:")
+
+        then:
+        d.group == 'hey'
+        d.name == 'foo'
+        d.version == null
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "with no version and no group"() {
+        when:
+        def d = parse(parser, ":foo:")
+
+        then:
+        d.group == null
+        d.name == 'foo'
+        d.version == null
+        d.transitive
+
+        !d.force
+        !d.changing
+    }
+
+    def "can create client module"() {
+        def parser = new DependencyStringNotationConverter(DirectInstantiator.INSTANCE, DefaultClientModule);
+
+        when:
+        def d = parse(parser, 'org.gradle:gradle-core:10')
+
+        then:
+        d instanceof DefaultClientModule
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10'
+        d.transitive
+
+        !d.force
+    }
+
+    def "client module ignores the artifact only notation"() {
+        def parser = new DependencyStringNotationConverter(DirectInstantiator.INSTANCE, DefaultClientModule);
+
+        when:
+        def d = parse(parser, 'org.gradle:gradle-core:10 at jar')
+
+        then:
+        d instanceof DefaultClientModule
+        d.name == 'gradle-core'
+        d.group == 'org.gradle'
+        d.version == '10 at jar'
+        d.transitive
+
+        !d.force
+        d.artifacts.size() == 0
+    }
+
+    def parse(def parser, def value) {
+        return NotationParserBuilder.toType(Dependency).fromCharSequence(parser).toComposite().parseNotation(value)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ModuleIdentiferNotationConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ModuleIdentiferNotationConverterTest.groovy
new file mode 100644
index 0000000..d743cf8
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ModuleIdentiferNotationConverterTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.api.internal.notations
+
+import org.gradle.api.artifacts.ModuleIdentifier
+import org.gradle.internal.typeconversion.NotationParserBuilder
+import org.gradle.internal.typeconversion.UnsupportedNotationException
+import spock.lang.Specification
+import spock.lang.Subject
+
+import static org.gradle.api.internal.artifacts.DefaultModuleIdentifier.newId
+
+class ModuleIdentiferNotationConverterTest extends Specification {
+
+    @Subject parser = NotationParserBuilder.toType(ModuleIdentifier).converter(new ModuleIdentiferNotationConverter()).toComposite()
+
+    def "parses module identifer notation"() {
+        expect:
+        parser.parseNotation("org.gradle:gradle-core") == newId("org.gradle", "gradle-core")
+        parser.parseNotation(" foo:bar ") == newId("foo", "bar")
+    }
+
+    def "reports invalid notation"() {
+        when: parser.parseNotation(notation)
+        then: thrown(UnsupportedNotationException)
+        where: notation << [null, "", ":", "foo:", "bar:", "foo:bar:baz", "  :", ":  ", "  :  "]
+    }
+
+    def "reports notation with invalid character"() {
+        when: parser.parseNotation("group:module${character}")
+        then: thrown(UnsupportedNotationException)
+        where: character << ["+", "*", "[", "]", "(", ")", ","]
+    }
+
+
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
new file mode 100644
index 0000000..7d35162
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/notations/ProjectDependencyFactoryTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.notations;
+
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.internal.artifacts.DefaultProjectDependencyFactory
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.initialization.ProjectAccessListener
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.util.GUtil
+import spock.lang.Specification
+
+public class ProjectDependencyFactoryTest extends Specification {
+
+    def projectDummy = Mock(ProjectInternal)
+    def projectFinder = Mock(ProjectFinder)
+
+    def depFactory = new DefaultProjectDependencyFactory(Mock(ProjectAccessListener), DirectInstantiator.INSTANCE, true)
+    def factory = new ProjectDependencyFactory(depFactory)
+
+    def "creates project dependency with map notation"() {
+        given:
+        boolean expectedTransitive = false;
+        final Map<String, Object> mapNotation = GUtil.map("path", ":foo:bar", "configuration", "compile", "transitive", expectedTransitive);
+
+        and:
+        projectFinder.getProject(':foo:bar') >> projectDummy
+
+        when:
+        def projectDependency = factory.createFromMap(projectFinder, mapNotation);
+
+        then:
+        projectDependency.getDependencyProject() == projectDummy
+        projectDependency.getConfiguration() == "compile"
+        projectDependency.isTransitive() == expectedTransitive
+    }
+
+    def "fails with decent message if provided map is invalid"() {
+        given:
+        projectFinder.getProject(':foo:bar') >> projectDummy
+
+        when:
+        factory.createFromMap(projectFinder, GUtil.map("paths", ":foo:bar"));
+
+        then:
+        def ex = thrown(InvalidUserDataException)
+        ex.message.contains("Required keys [path] are missing from map")
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaDataTest.groovy
new file mode 100644
index 0000000..7bfdf9d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetaDataTest.groovy
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model
+import org.apache.ivy.core.module.descriptor.*
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DependencyMetaData
+import spock.lang.Specification
+
+abstract class AbstractModuleComponentResolveMetaDataTest extends Specification {
+
+    def id = Stub(ModuleVersionIdentifier)
+    def componentId = Stub(ModuleComponentIdentifier)
+    def moduleDescriptor = Mock(ModuleDescriptor)
+    def metaData
+
+    def setup() {
+        metaData = createMetaData(id, moduleDescriptor, componentId)
+    }
+
+    abstract AbstractModuleComponentResolveMetaData createMetaData(ModuleVersionIdentifier id, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier);
+
+    def "has useful string representation"() {
+        given:
+        def config = Stub(Configuration)
+        moduleDescriptor.getConfiguration('config') >> config
+        componentId.getDisplayName() >> '<component>'
+
+        expect:
+        metaData.toString() == '<component>'
+        metaData.getConfiguration('config').toString() == '<component>:config'
+    }
+
+    def "can replace identifiers"() {
+        def newId = DefaultModuleComponentIdentifier.newId("group", "module", "version")
+
+        given:
+        metaData.setComponentId(newId)
+
+        expect:
+        metaData.componentId.is(newId)
+        metaData.id.group == "group"
+        metaData.id.name == "module"
+        metaData.id.version == "version"
+    }
+
+    def "builds and caches the dependency meta-data from the module descriptor"() {
+        def dependency1 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        def dependency2 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+
+        given:
+        moduleDescriptor.dependencies >> ([dependency1, dependency2] as DependencyDescriptor[])
+
+        when:
+        def deps = metaData.dependencies
+
+        then:
+        deps.size() == 2
+        deps[0].descriptor == dependency1
+        deps[1].descriptor == dependency2
+
+        when:
+        def deps2 = metaData.dependencies
+
+        then:
+        deps2.is(deps)
+
+        and:
+        0 * moduleDescriptor._
+    }
+
+    def "builds and caches the configuration meta-data from the module descriptor"() {
+        when:
+        def config = metaData.getConfiguration("conf")
+
+        then:
+        1 * moduleDescriptor.getConfiguration("conf") >> Stub(Configuration)
+
+        when:
+        def config2 = metaData.getConfiguration("conf")
+
+        then:
+        config2.is(config)
+
+        and:
+        0 * moduleDescriptor._
+    }
+
+    def "returns null for unknown configuration"() {
+        given:
+        moduleDescriptor.getConfiguration("conf") >> null
+
+        expect:
+        metaData.getConfiguration("conf") == null
+    }
+
+    def "builds and caches dependencies for a configuration"() {
+        def config = Stub(Configuration)
+        def parent = Stub(Configuration)
+        def dependency1 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        dependency1.addDependencyConfiguration("conf", "a")
+        def dependency2 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        dependency2.addDependencyConfiguration("*", "b")
+        def dependency3 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        dependency3.addDependencyConfiguration("super", "c")
+        def dependency4 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        dependency4.addDependencyConfiguration("other", "d")
+        def dependency5 = new DefaultDependencyDescriptor(IvyUtil.createModuleRevisionId("org", "module", "1.2"), false)
+        dependency5.addDependencyConfiguration("%", "e")
+
+        given:
+        moduleDescriptor.dependencies >> ([dependency1, dependency2, dependency3, dependency4, dependency5] as DependencyDescriptor[])
+        moduleDescriptor.getConfiguration("conf") >> config
+        moduleDescriptor.getConfiguration("super") >> parent
+        config.extends >> ["super"]
+
+        when:
+        def dependencies = metaData.getConfiguration("conf").dependencies
+
+        then:
+        dependencies*.descriptor == [dependency1, dependency2, dependency3, dependency5]
+
+        and:
+        metaData.getConfiguration("conf").dependencies.is(dependencies)
+
+        when:
+        metaData.setDependencies([])
+
+        then:
+        metaData.getConfiguration("conf").dependencies == []
+    }
+
+    def "builds and caches artifacts from the module descriptor"() {
+        def artifact1 = artifact("one")
+        def artifact2 = artifact("two")
+
+        given:
+        moduleDescriptor.allArtifacts >> ([artifact1, artifact2] as Artifact[])
+        moduleDescriptor.configurationsNames >> ["conf1"]
+        moduleDescriptor.getArtifacts("conf1") >> ([artifact1, artifact2] as Artifact[])
+
+        when:
+        def artifacts = metaData.artifacts
+
+        then:
+        artifacts*.name.name == ["one", "two"]
+
+        and:
+        metaData.artifacts.is(artifacts)
+    }
+
+    Artifact artifact(String name) {
+        return Stub(Artifact) {
+            getName() >> name
+            getType() >> "type"
+            getExt() >> "ext"
+            getExtraAttributes() >> [classifier: "classifier"]
+        }
+    }
+
+    def "builds and caches artifacts for a configuration"() {
+        def artifact1 = artifact("one")
+        def artifact2 = artifact("two")
+        def config = Stub(Configuration)
+
+        given:
+        moduleDescriptor.allArtifacts >> ([artifact1, artifact2] as Artifact[])
+        moduleDescriptor.configurationsNames >> ["conf"]
+        moduleDescriptor.getArtifacts("conf") >> ([artifact1, artifact2] as Artifact[])
+        moduleDescriptor.getConfiguration("conf") >> config
+
+        when:
+        def artifacts = metaData.getConfiguration("conf").artifacts
+
+        then:
+        artifacts*.name.name == ["one", "two"]
+        metaData.artifacts*.name.name == ["one", "two"]
+
+        and:
+        metaData.getConfiguration("conf").artifacts.is(artifacts)
+    }
+
+    def "can adapt an Ivy artifact to a Gradle artifact"() {
+        def artifact = artifact("one")
+
+        expect:
+        def artifactMetaData = metaData.artifact(artifact)
+        artifactMetaData.componentId == metaData.componentId
+        artifactMetaData.id.componentIdentifier == metaData.componentId
+        artifactMetaData.name.name == "one"
+        artifactMetaData.name.type == "type"
+        artifactMetaData.name.extension == "ext"
+        artifactMetaData.name.classifier == "classifier"
+    }
+
+    def "artifacts include union of those inherited from other configurations"() {
+        def config = Stub(Configuration)
+        def parent = Stub(Configuration)
+        def artifact1 = artifact("one")
+        def artifact2 = artifact("two")
+        def artifact3 = artifact("three")
+
+        given:
+        moduleDescriptor.configurationsNames >> ["conf", "super"]
+        moduleDescriptor.getConfiguration("conf") >> config
+        moduleDescriptor.getConfiguration("super") >> parent
+        config.extends >> ["super"]
+        moduleDescriptor.allArtifacts >> ([artifact1, artifact2, artifact3] as Artifact[])
+        moduleDescriptor.getArtifacts("conf") >> ([artifact1, artifact2] as Artifact[])
+        moduleDescriptor.getArtifacts("super") >> ([artifact2, artifact3] as Artifact[])
+
+        when:
+        def artifacts = metaData.getConfiguration("conf").artifacts
+
+        then:
+        artifacts*.name.name == ["one", "two", "three"]
+    }
+
+    def "builds and caches exclude rules for a configuration"() {
+        def rule1 = Stub(ExcludeRule)
+        def rule2 = Stub(ExcludeRule)
+        def rule3 = Stub(ExcludeRule)
+        def config = Stub(Configuration)
+        def parent = Stub(Configuration)
+
+        given:
+        rule1.configurations >> ["conf"]
+        rule2.configurations >> ["super"]
+        rule3.configurations >> ["other"]
+
+        and:
+        moduleDescriptor.getConfiguration("conf") >> config
+        moduleDescriptor.getConfiguration("super") >> parent
+        config.extends >> ["super"]
+        moduleDescriptor.allExcludeRules >> ([rule1, rule2, rule3] as ExcludeRule[])
+
+        when:
+        def excludeRules = metaData.getConfiguration("conf").excludeRules
+
+        then:
+        excludeRules as List == [rule1, rule2]
+
+        and:
+        metaData.getConfiguration("conf").excludeRules.is(excludeRules)
+    }
+
+    def "can replace the dependencies for the module version"() {
+        def dependency1 = Stub(DependencyMetaData)
+        def dependency2 = Stub(DependencyMetaData)
+
+        when:
+        metaData.dependencies = [dependency1, dependency2]
+
+        then:
+        metaData.dependencies == [dependency1, dependency2]
+
+        and:
+        0 * moduleDescriptor._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaDataTest.groovy
new file mode 100644
index 0000000..0d81b4b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/BuildableIvyModuleResolveMetaDataTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model
+
+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.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.component.model.IvyArtifactName
+import spock.lang.Specification
+
+import static com.google.common.collect.Sets.newHashSet
+
+class BuildableIvyModuleResolveMetaDataTest extends Specification {
+
+    def md = new DefaultModuleDescriptor(ModuleRevisionId.newInstance("org", "foo", "1.0"), "release", null)
+    def meta = new BuildableIvyModuleResolveMetaData(md)
+
+    def "adds correct artifact to meta-data"() {
+        def a = ivyArtifact("foo", "jar", "ext", [a: 'b'])
+        md.addConfiguration(new Configuration("runtime"))
+
+        when: meta.addArtifact(a, newHashSet("runtime"))
+
+        then:
+        md.allArtifacts*.toString() == ["org#foo;1.0!foo.ext(jar)"]
+        md.getArtifacts("runtime")*.toString() == ["org#foo;1.0!foo.ext(jar)"]
+    }
+
+    private static IvyArtifactName ivyArtifact(String name, String type, String ext, Map<String, String> attributes = [:]) {
+        return new DefaultIvyArtifactName(name, type, ext, attributes)
+    }
+
+    def "prevents adding artifact without configurations"() {
+        def unattached = ivyArtifact("foo", "jar", "ext", [a: 'b'])
+        md.addConfiguration(new Configuration("runtime"))
+
+        when: meta.addArtifact(unattached, newHashSet())
+
+        then: thrown(IllegalArgumentException)
+    }
+
+    def "can be added to metadata that already contains artifacts"() {
+        def a1 = ivyArtifact("foo", "jar", "jar")
+        def a2 = ivyArtifact("foo-all", "zip", "zip")
+
+        md.addConfiguration(new Configuration("runtime"))
+        md.addConfiguration(new Configuration("testUtil"))
+
+        when:
+        meta.addArtifact(a1, newHashSet("runtime"))
+        meta.addArtifact(a2, newHashSet("testUtil"))
+
+        then:
+        md.allArtifacts*.toString() == ["org#foo;1.0!foo.jar", "org#foo;1.0!foo-all.zip"]
+        md.getArtifacts("runtime")*.toString() == ["org#foo;1.0!foo.jar"]
+        md.getArtifacts("testUtil")*.toString() == ["org#foo;1.0!foo-all.zip"]
+    }
+
+    def "can be added to metadata that already contains the same artifact in different configuration"() {
+        def a1 = ivyArtifact("foo", "jar", "jar")
+        //some publishers create ivy metadata that contains separate entries for the same artifact but different configurations
+        def a2 = ivyArtifact("foo", "jar", "jar")
+
+        md.addConfiguration(new Configuration("runtime"))
+        md.addConfiguration(new Configuration("archives"))
+
+        when:
+        meta.addArtifact(a1, newHashSet("archives"))
+        meta.addArtifact(a2, newHashSet("runtime"))
+
+        then:
+        md.allArtifacts*.toString() == ["org#foo;1.0!foo.jar"]
+        md.getArtifacts("archives")*.toString() == ["org#foo;1.0!foo.jar"]
+        md.getArtifacts("runtime")*.toString() == ["org#foo;1.0!foo.jar"]
+        md.allArtifacts[0].configurations == ["archives", "runtime"]
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaDataTest.groovy
new file mode 100644
index 0000000..9bcb0df
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModulePublishMetaDataTest.groovy
@@ -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.internal.component.external.model
+
+import org.apache.ivy.core.module.descriptor.Artifact
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import spock.lang.Specification
+
+class DefaultIvyModulePublishMetaDataTest extends Specification {
+    def metaData = new DefaultIvyModulePublishMetaData(Stub(ModuleVersionIdentifier))
+
+    def "can add artifacts"() {
+        def artifact = Stub(Artifact)
+        def file = new File("artifact.zip")
+
+        when:
+        metaData.addArtifact(artifact, file)
+
+        then:
+        metaData.artifacts.size() == 1
+        def publishArtifact = metaData.artifacts.iterator().next()
+        publishArtifact.artifact == artifact
+        publishArtifact.file == file
+
+        and:
+        metaData.getArtifact(publishArtifact.id) == publishArtifact
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaDataTest.groovy
new file mode 100644
index 0000000..8e32db8
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultIvyModuleResolveMetaDataTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model
+
+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.component.ModuleComponentIdentifier
+import org.gradle.internal.component.model.DependencyMetaData
+
+class DefaultIvyModuleResolveMetaDataTest extends AbstractModuleComponentResolveMetaDataTest {
+
+    @Override
+    AbstractModuleComponentResolveMetaData createMetaData(ModuleVersionIdentifier id, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier) {
+        return new DefaultIvyModuleResolveMetaData(componentIdentifier, moduleDescriptor)
+    }
+
+    def "can make a copy"() {
+        def dependency1 = Stub(DependencyMetaData)
+        def dependency2 = Stub(DependencyMetaData)
+
+        given:
+        metaData.changing = true
+        metaData.dependencies = [dependency1, dependency2]
+        metaData.status = 'a'
+        metaData.statusScheme = ['a', 'b', 'c']
+
+        when:
+        def copy = metaData.copy()
+
+        then:
+        copy != metaData
+        copy.descriptor == moduleDescriptor
+        copy.changing
+        copy.dependencies == [dependency1, dependency2]
+        copy.status == 'a'
+        copy.statusScheme == ['a', 'b', 'c']
+    }
+
+    def "getBranch returns branch from moduleDescriptor" () {
+        setup:
+        _ * moduleDescriptor.getModuleRevisionId() >> ModuleRevisionId.newInstance('orgId', 'moduleId', expectedBranch, 'version')
+
+        expect:
+        metaData.branch == expectedBranch
+
+        where:
+        expectedBranch | _
+        null           | _
+        'someBranch'   | _
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaDataTest.groovy
new file mode 100644
index 0000000..755aff7
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetaDataTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.external.model
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.internal.component.model.DependencyMetaData
+
+class DefaultMavenModuleResolveMetaDataTest extends AbstractModuleComponentResolveMetaDataTest {
+
+    @Override
+    AbstractModuleComponentResolveMetaData createMetaData(ModuleVersionIdentifier id, ModuleDescriptor moduleDescriptor, ModuleComponentIdentifier componentIdentifier) {
+        return new DefaultMavenModuleResolveMetaData(componentIdentifier, moduleDescriptor, "pom", false)
+    }
+
+    def "can make a copy"() {
+        def dependency1 = Stub(DependencyMetaData)
+        def dependency2 = Stub(DependencyMetaData)
+
+        given:
+        metaData.changing = true
+        metaData.dependencies = [dependency1, dependency2]
+        metaData.status = 'a'
+        metaData.statusScheme = ['a', 'b', 'c']
+        metaData.snapshotTimestamp = '123'
+
+        when:
+        def copy = metaData.copy()
+
+        then:
+        copy != metaData
+        copy.descriptor == moduleDescriptor
+        copy.changing
+        copy.dependencies == [dependency1, dependency2]
+        copy.status == 'a'
+        copy.statusScheme == ['a', 'b', 'c']
+        copy.packaging == "pom"
+        !copy.relocated
+        copy.snapshotTimestamp == '123'
+    }
+
+    def "recognises pom packaging"() {
+        when:
+        def metaData = new DefaultMavenModuleResolveMetaData(componentId, moduleDescriptor, packaging, false)
+
+        then:
+        metaData.packaging == packaging
+        metaData.isPomPackaging() == isPom
+        metaData.isKnownJarPackaging() == isJar
+
+        where:
+        packaging      | isPom | isJar
+        "pom"          | true  | false
+        "jar"          | false | true
+        "war"          | false | false
+        "maven-plugin" | false | true
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifierTest.groovy
new file mode 100644
index 0000000..f102081
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactIdentifierTest.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.internal.component.external.model
+
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class DefaultModuleComponentArtifactIdentifierTest extends Specification {
+    def "has useful string representation"() {
+        def componentId = DefaultModuleComponentIdentifier.newId("group", "module", "version")
+
+        expect:
+        def noClassifier = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", "ext", [:])
+        noClassifier.displayName == "name.ext (group:module:version)"
+        noClassifier.toString() == "name.ext (group:module:version)"
+
+        def withClassifier = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': 'classifier'])
+        withClassifier.displayName == "name-classifier.ext (group:module:version)"
+        withClassifier.toString() == "name-classifier.ext (group:module:version)"
+
+        def noExtension = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", null, ['classifier': 'classifier'])
+        noExtension.displayName == "name-classifier (group:module:version)"
+        noExtension.toString() == "name-classifier (group:module:version)"
+    }
+
+    def "is equal when all attributes and module version are the same"() {
+        def componentId = DefaultModuleComponentIdentifier.newId("group", "module", "version")
+        def otherComponentId = DefaultModuleComponentIdentifier.newId("group", "module", "2")
+
+        def withClassifier = new DefaultModuleComponentArtifactIdentifier(componentId,  "name", "type", "ext", ['classifier': 'classifier'])
+        def same = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': 'classifier'])
+        def differentModule = new DefaultModuleComponentArtifactIdentifier(otherComponentId, "name", "type", "ext", ['classifier': 'classifier'])
+        def differentName = new DefaultModuleComponentArtifactIdentifier(componentId, "2", "type", "ext", ['classifier': 'classifier'])
+        def differentType = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "2", "ext", ['classifier': 'classifier'])
+        def differentExt = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", "2", ['classifier': 'classifier'])
+        def differentAttributes = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", "ext", ['classifier': '2'])
+        def emptyParts = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", null, [:])
+        def emptyPartsSame = new DefaultModuleComponentArtifactIdentifier(componentId, "name", "type", null, [:])
+
+        expect:
+        withClassifier Matchers.strictlyEqual(same)
+        withClassifier != differentModule
+        withClassifier != differentName
+        withClassifier != differentType
+        withClassifier != differentExt
+        withClassifier != differentAttributes
+        withClassifier != emptyParts
+
+        emptyParts Matchers.strictlyEqual(emptyPartsSame)
+        emptyParts != withClassifier
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaDataTest.groovy
new file mode 100644
index 0000000..1bd56ce
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentArtifactMetaDataTest.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.internal.component.external.model
+import org.apache.ivy.core.module.descriptor.Artifact
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import spock.lang.Specification
+
+class DefaultModuleComponentArtifactMetaDataTest extends Specification {
+    def "has reasonable string representation"() {
+        expect:
+        def artifact = new DefaultModuleComponentArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", ['classifier': 'classifier']))
+        artifact.toString() == artifact.id.toString()
+    }
+
+    def "extracts attributes from provided artifact instance"() {
+        expect:
+        def artifact = new DefaultModuleComponentArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", ['classifier': 'classifier']))
+        artifact.name.name == "name"
+        artifact.name.type == "type"
+        artifact.name.extension == "ext"
+        artifact.name.classifier == "classifier"
+
+        and:
+        def noClassifier = new DefaultModuleComponentArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", "ext", [:]))
+        noClassifier.name.name == "name"
+        noClassifier.name.type == "type"
+        noClassifier.name.extension == "ext"
+        noClassifier.name.classifier == null
+
+        and:
+        def noExtension = new DefaultModuleComponentArtifactMetaData(Stub(ModuleComponentIdentifier), ivyArtifact("name", "type", null, [:]))
+        noExtension.name.name == "name"
+        noExtension.name.type == "type"
+        noExtension.name.extension == null
+        noExtension.name.classifier == null
+    }
+
+    def "converts to Ivy artifact"() {
+        expect:
+        def original = ivyArtifact("name", "type", "ext", ['classifier': 'classifier'])
+        def artifact = new DefaultModuleComponentArtifactMetaData(Stub(ModuleComponentIdentifier), original)
+        def ivyArtifact = artifact.toIvyArtifact()
+        ivyArtifact.name == "name"
+        ivyArtifact.type == "type"
+        ivyArtifact.ext == "ext"
+        ivyArtifact.extraAttributes == [classifier: "classifier"]
+    }
+
+    def ivyArtifact(String name, String type, String extension, Map attributes) {
+        def artifact = Mock(Artifact)
+        _ * artifact.name >> name
+        _ * artifact.type >> type
+        _ * artifact.ext >> extension
+        _ * artifact.extraAttributes >> attributes
+        return artifact
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifierTest.groovy
new file mode 100644
index 0000000..6fbd3cf
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentIdentifierTest.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.internal.component.external.model
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class DefaultModuleComponentIdentifierTest extends Specification {
+    def "is instantiated with non-null constructor parameter values"() {
+        when:
+        ModuleComponentIdentifier defaultModuleComponentIdentifier = new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
+
+        then:
+        defaultModuleComponentIdentifier.group == 'some-group'
+        defaultModuleComponentIdentifier.module == 'some-name'
+        defaultModuleComponentIdentifier.version == '1.0'
+        defaultModuleComponentIdentifier.displayName == 'some-group:some-name:1.0'
+        defaultModuleComponentIdentifier.toString() == 'some-group:some-name:1.0'
+    }
+
+    @Unroll
+    def "is instantiated with null constructor parameter values (#group, #name, #version)"() {
+        when:
+        new DefaultModuleComponentIdentifier(group, name, version)
+
+        then:
+        thrown(AssertionError)
+
+        where:
+        group        | name        | version
+        null         | 'some-name' | '1.0'
+        'some-group' | null        | '1.0'
+        'some-group' | 'some-name' | null
+    }
+
+    @Unroll
+    def "can compare with other instance (#group, #name, #version)"() {
+        expect:
+        ModuleComponentIdentifier defaultModuleComponentIdentifier1 = new DefaultModuleComponentIdentifier('some-group', 'some-name', '1.0')
+        ModuleComponentIdentifier defaultModuleComponentIdentifier2 = new DefaultModuleComponentIdentifier(group, name, version)
+        strictlyEquals(defaultModuleComponentIdentifier1, defaultModuleComponentIdentifier2) == equality
+        (defaultModuleComponentIdentifier1.hashCode() == defaultModuleComponentIdentifier2.hashCode()) == hashCode
+        (defaultModuleComponentIdentifier1.toString() == defaultModuleComponentIdentifier2.toString()) == stringRepresentation
+
+        where:
+        group         | name         | version | equality | hashCode | stringRepresentation
+        'some-group'  | 'some-name'  | '1.0'   | true     | true     | true
+        'other-group' | 'some-name'  | '1.0'   | false    | false    | false
+        'some-group'  | 'other-name' | '1.0'   | false    | false    | false
+        'some-group'  | 'some-name'  | '2.0'   | false    | false    | false
+    }
+
+    def "can create new ID"() {
+        when:
+        ModuleComponentIdentifier defaultModuleComponentIdentifier = DefaultModuleComponentIdentifier.newId('some-group', 'some-name', '1.0')
+
+        then:
+        defaultModuleComponentIdentifier.group == 'some-group'
+        defaultModuleComponentIdentifier.module == 'some-name'
+        defaultModuleComponentIdentifier.version == '1.0'
+        defaultModuleComponentIdentifier.displayName == 'some-group:some-name:1.0'
+        defaultModuleComponentIdentifier.toString() == 'some-group:some-name:1.0'
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentSelectorTest.groovy
new file mode 100644
index 0000000..7473d06
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultModuleComponentSelectorTest.groovy
@@ -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.component.external.model
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class DefaultModuleComponentSelectorTest extends Specification {
+    def "is instantiated with non-null constructor parameter values"() {
+        when:
+        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
+
+        then:
+        defaultModuleComponentSelector.group == 'some-group'
+        defaultModuleComponentSelector.module == 'some-name'
+        defaultModuleComponentSelector.version == '1.0'
+        defaultModuleComponentSelector.displayName == 'some-group:some-name:1.0'
+        defaultModuleComponentSelector.toString() == 'some-group:some-name:1.0'
+    }
+
+    @Unroll
+    def "is instantiated with null constructor parameter values (#group, #name, #version)"() {
+        when:
+        new DefaultModuleComponentSelector(group, name, version)
+
+        then:
+        Throwable t = thrown(AssertionError)
+        assert t.message == assertionMessage
+
+        where:
+        group        | name        | version | assertionMessage
+        null         | 'some-name' | '1.0'   | 'group cannot be null'
+        'some-group' | null        | '1.0'   | 'module cannot be null'
+        'some-group' | 'some-name' | null    | 'version cannot be null'
+    }
+
+    @Unroll
+    def "can compare with other instance (#group, #name, #version)"() {
+        expect:
+        ModuleComponentSelector defaultModuleComponentSelector1 = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
+        ModuleComponentSelector defaultModuleComponentSelector2 = new DefaultModuleComponentSelector(group, name, version)
+        strictlyEquals(defaultModuleComponentSelector1, defaultModuleComponentSelector2) == equality
+        (defaultModuleComponentSelector1.hashCode() == defaultModuleComponentSelector2.hashCode()) == hashCode
+        (defaultModuleComponentSelector1.toString() == defaultModuleComponentSelector2.toString()) == stringRepresentation
+
+        where:
+        group         | name         | version | equality | hashCode | stringRepresentation
+        'some-group'  | 'some-name'  | '1.0'   | true     | true     | true
+        'other-group' | 'some-name'  | '1.0'   | false    | false    | false
+        'some-group'  | 'other-name' | '1.0'   | false    | false    | false
+        'some-group'  | 'some-name'  | '2.0'   | false    | false    | false
+    }
+
+    def "can create new selector"() {
+        when:
+        ModuleComponentSelector defaultModuleComponentSelector = DefaultModuleComponentSelector.newSelector('some-group', 'some-name', '1.0')
+
+        then:
+        defaultModuleComponentSelector.group == 'some-group'
+        defaultModuleComponentSelector.module == 'some-name'
+        defaultModuleComponentSelector.version == '1.0'
+        defaultModuleComponentSelector.displayName == 'some-group:some-name:1.0'
+        defaultModuleComponentSelector.toString() == 'some-group:some-name:1.0'
+    }
+
+    def "prevents matching of null id"() {
+        when:
+        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
+        defaultModuleComponentSelector.matchesStrictly(null)
+
+        then:
+        Throwable t = thrown(AssertionError)
+        assert t.message == 'identifier cannot be null'
+    }
+
+    def "does not match id for unexpected component selector type"() {
+        when:
+        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
+        boolean matches = defaultModuleComponentSelector.matchesStrictly(new DefaultProjectComponentIdentifier(':mypath'))
+
+        then:
+        assert !matches
+    }
+
+    @Unroll
+    def "matches id (#group, #name, #version)"() {
+        expect:
+        ModuleComponentSelector defaultModuleComponentSelector = new DefaultModuleComponentSelector('some-group', 'some-name', '1.0')
+        ModuleComponentIdentifier defaultModuleComponentIdentifier = new DefaultModuleComponentIdentifier(group, name, version)
+        defaultModuleComponentSelector.matchesStrictly(defaultModuleComponentIdentifier) == matchesId
+
+        where:
+        group         | name         | version | matchesId
+        'some-group'  | 'some-name'  | '1.0'   | true
+        'other-group' | 'some-name'  | '1.0'   | false
+        'some-group'  | 'other-name' | '1.0'   | false
+        'some-group'  | 'some-name'  | '2.0'   | false
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifierTest.groovy
new file mode 100644
index 0000000..677712f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalArtifactIdentifierTest.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.internal.component.local.model
+
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class DefaultLocalArtifactIdentifierTest extends Specification {
+    def "has useful string representation"() {
+        def componentId = Stub(ComponentIdentifier)
+
+        expect:
+        def noClassifier = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", "ext", [:])
+        noClassifier.displayName == "name.ext (<comp>)"
+        noClassifier.toString() == "name.ext (<comp>)"
+
+        def withClassifier = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", "ext", ['classifier': 'classifier'])
+        withClassifier.displayName == "name-classifier.ext (<comp>)"
+        withClassifier.toString() == "name-classifier.ext (<comp>)"
+
+        def noExtension = new DefaultLocalArtifactIdentifier(componentId, "<comp>", "name", "type", null, ['classifier': 'classifier'])
+        noExtension.displayName == "name-classifier (<comp>)"
+        noExtension.toString() == "name-classifier (<comp>)"
+    }
+
+    def "is equal when all attributes and module version are the same"() {
+        def moduleVersion = DefaultModuleVersionIdentifier.newId("group", "module", "version")
+        def componentId = DefaultModuleComponentIdentifier.newId(moduleVersion)
+
+        def withClassifier = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': 'classifier'])
+        def same = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': 'classifier'])
+        def differentName = new DefaultLocalArtifactIdentifier(componentId, "comp", "2", "type", "ext", ['classifier': 'classifier'])
+        def differentType = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "2", "ext", ['classifier': 'classifier'])
+        def differentExt = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "2", ['classifier': 'classifier'])
+        def differentAttributes = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", "ext", ['classifier': '2'])
+        def emptyParts = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", null, [:])
+        def emptyPartsSame = new DefaultLocalArtifactIdentifier(componentId, "comp", "name", "type", null, [:])
+
+        expect:
+        withClassifier Matchers.strictlyEqual(same)
+        withClassifier != differentName
+        withClassifier != differentType
+        withClassifier != differentExt
+        withClassifier != differentAttributes
+        withClassifier != emptyParts
+
+        emptyParts Matchers.strictlyEqual(emptyPartsSame)
+        emptyParts != withClassifier
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetaDataTest.groovy
new file mode 100644
index 0000000..680a1d6
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetaDataTest.groovy
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model
+
+import org.apache.ivy.core.module.descriptor.Configuration
+import org.apache.ivy.core.module.descriptor.DefaultArtifact
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.model.DefaultIvyArtifactName
+import org.gradle.internal.component.model.DependencyMetaData
+import spock.lang.Specification
+
+class DefaultLocalComponentMetaDataTest extends Specification {
+    def moduleDescriptor = new DefaultModuleDescriptor(IvyUtil.createModuleRevisionId("group", "module", "version"), "status", null)
+    def componentIdentifier = Mock(ComponentIdentifier)
+    def metaData = new DefaultLocalComponentMetaData(moduleDescriptor, componentIdentifier)
+
+    def "can lookup configuration after it has been added"() {
+        when:
+        metaData.addConfiguration("conf", true, "description", ["super"] as String[], true)
+
+        then:
+        metaData.moduleDescriptor.configurations.length == 1
+        metaData.moduleDescriptor.getConfiguration("conf") != null
+    }
+
+    def "can lookup artifact in various ways after it has been added"() {
+        def artifact = artifactName()
+        def file = new File("artifact.zip")
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("conf"))
+
+        when:
+        metaData.addArtifact("conf", artifact, file)
+
+        then:
+        metaData.artifacts.size() == 1
+        def publishArtifact = (metaData.artifacts as List).first()
+        publishArtifact.id
+        publishArtifact.name.name == artifact.name
+        publishArtifact.name.type == artifact.type
+        publishArtifact.name.extension == artifact.extension
+        publishArtifact.file == file
+
+        and:
+        metaData.getArtifact(publishArtifact.id) == publishArtifact
+
+        and:
+        def resolveMetaData = metaData.toResolveMetaData()
+        resolveMetaData.artifacts.size() == 1
+        def resolveArtifact = (resolveMetaData.artifacts as List).first()
+        resolveArtifact.id
+        resolveArtifact.componentId == resolveMetaData.componentId
+        resolveArtifact.name.name == artifact.name
+        resolveArtifact.name.type == artifact.type
+        resolveArtifact.name.extension == artifact.extension
+
+        and:
+        moduleDescriptor.getArtifacts("conf").size() == 1
+        def ivyArtifact = (moduleDescriptor.getArtifacts("conf") as List).first()
+        ivyArtifact.name == artifact.name
+        ivyArtifact.type == artifact.type
+        ivyArtifact.ext == artifact.extension
+        ivyArtifact.configurations == ["conf"]
+    }
+
+    def "can add artifact to several configurations"() {
+        def artifact = artifactName()
+        def file = new File("artifact.zip")
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("conf1"))
+        moduleDescriptor.addConfiguration(new Configuration("conf2"))
+        moduleDescriptor.addConfiguration(new Configuration("conf3"))
+
+        when:
+        metaData.addArtifact("conf1", artifact, file)
+        metaData.addArtifact("conf2", artifact, file)
+
+        then:
+        metaData.artifacts.size() == 1
+
+        and:
+        def resolveMetaData = metaData.toResolveMetaData()
+        resolveMetaData.artifacts.size() == 1
+
+        and:
+        moduleDescriptor.getArtifacts("conf1").size() == 1
+        moduleDescriptor.getArtifacts("conf2").size() == 1
+        def ivyArtifact = (moduleDescriptor.getArtifacts("conf1") as List).first()
+        ivyArtifact.configurations == ["conf1", "conf2"]
+    }
+
+    def "can lookup an artifact given an Ivy artifact"() {
+        def artifact = artifactName()
+        def file = new File("artifact.zip")
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("conf"))
+
+        and:
+        metaData.addArtifact("conf", artifact, file)
+
+        and:
+        def ivyArtifact = metaData.toResolveMetaData().descriptor.allArtifacts.find { it.name == artifact.name }
+
+        expect:
+        def resolveArtifact = metaData.toResolveMetaData().artifact(ivyArtifact)
+        resolveArtifact.file == file
+        resolveArtifact == metaData.getArtifact(resolveArtifact.id)
+    }
+
+    def "can lookup an unknown artifact given an Ivy artifact"() {
+        def artifact = artifact()
+
+        expect:
+        def resolveArtifact = metaData.toResolveMetaData().artifact(artifact)
+        resolveArtifact != null
+        resolveArtifact.file == null
+        metaData.getArtifact(resolveArtifact.id) == null
+    }
+
+    def "treats as distinct two artifacts with duplicate attributes and different files"() {
+        def artifact1 = artifactName()
+        def artifact2 = artifactName()
+        def file1 = new File("artifact-1.zip")
+        def file2 = new File("artifact-2.zip")
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("conf1"))
+        moduleDescriptor.addConfiguration(new Configuration("conf2"))
+        metaData.addArtifact("conf1", artifact1, file1)
+        metaData.addArtifact("conf2", artifact2, file2)
+
+        when:
+        def resolveMetaData = metaData.toResolveMetaData()
+
+        then:
+        def conf1Artifacts = resolveMetaData.getConfiguration("conf1").artifacts as List
+        conf1Artifacts.size() == 1
+        def artifactMetadata1 = conf1Artifacts[0]
+
+        def conf2Artifacts = resolveMetaData.getConfiguration("conf2").artifacts as List
+        conf2Artifacts.size() == 1
+        def artifactMetadata2 = conf2Artifacts[0]
+
+        and:
+        artifactMetadata1.id != artifactMetadata2.id
+
+        and:
+        resolveMetaData.artifacts == [artifactMetadata1, artifactMetadata2] as Set
+
+        and:
+        metaData.getArtifact(artifactMetadata1.id).file == file1
+        metaData.getArtifact(artifactMetadata2.id).file == file2
+    }
+
+    def "can add dependencies"() {
+        def dependencyDescriptor = Stub(DependencyDescriptor)
+        def dependency = Stub(DependencyMetaData) {
+            getDescriptor() >> dependencyDescriptor
+        }
+
+        when:
+        metaData.addDependency(dependency)
+
+        then:
+        metaData.moduleDescriptor.dependencies as List == [dependencyDescriptor]
+        metaData.toResolveMetaData().dependencies == [dependency]
+        metaData.toResolveMetaData().descriptor.dependencies as List == [dependencyDescriptor]
+    }
+
+    def "can convert to publish meta-data"() {
+        def artifact = artifactName()
+        def file = new File("artifact.zip")
+
+        given:
+        moduleDescriptor.addConfiguration(new Configuration("conf"))
+        metaData.addArtifact("conf", artifact, file)
+
+        when:
+        def publishMetaData = metaData.toPublishMetaData()
+
+        then:
+        publishMetaData.id == metaData.id
+
+        and:
+        publishMetaData.artifacts.size() == 1
+        def artifacts = publishMetaData.artifacts as List
+        def publishArtifact = artifacts[0]
+        publishArtifact.artifact.name == artifact.name
+        publishArtifact.artifact.type == artifact.type
+        publishArtifact.artifact.ext == artifact.extension
+        publishArtifact.file == file
+    }
+
+    def artifact() {
+        return new DefaultArtifact(moduleDescriptor.getModuleRevisionId(), null, "artifact", "type", "ext")
+    }
+
+    def artifactName() {
+        return new DefaultIvyArtifactName("artifact", "type", "ext")
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifierTest.groovy
new file mode 100644
index 0000000..bc6e820
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentIdentifierTest.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.internal.component.local.model
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class DefaultProjectComponentIdentifierTest extends Specification {
+    def "is instantiated with non-null constructor parameter values"() {
+        when:
+        ProjectComponentIdentifier defaultBuildComponentIdentifier = new DefaultProjectComponentIdentifier(':myPath')
+
+        then:
+        defaultBuildComponentIdentifier.projectPath == ':myPath'
+        defaultBuildComponentIdentifier.displayName == 'project :myPath'
+        defaultBuildComponentIdentifier.toString() == 'project :myPath'
+    }
+
+    def "is instantiated with null constructor parameter value"() {
+        when:
+        new DefaultProjectComponentIdentifier(null)
+
+        then:
+        Throwable t = thrown(AssertionError)
+        t.message == 'project path cannot be null'
+    }
+
+    @Unroll
+    def "can compare with other instance (#projectPath)"() {
+        expect:
+        ProjectComponentIdentifier defaultBuildComponentIdentifier1 = new DefaultProjectComponentIdentifier(':myProjectPath1')
+        ProjectComponentIdentifier defaultBuildComponentIdentifier2 = new DefaultProjectComponentIdentifier(projectPath)
+        strictlyEquals(defaultBuildComponentIdentifier1, defaultBuildComponentIdentifier2) == equality
+        (defaultBuildComponentIdentifier1.hashCode() == defaultBuildComponentIdentifier2.hashCode()) == hashCode
+        (defaultBuildComponentIdentifier1.toString() == defaultBuildComponentIdentifier2.toString()) == stringRepresentation
+
+        where:
+        projectPath       | equality | hashCode | stringRepresentation
+        ':myProjectPath1' | true     | true     | true
+        ':myProjectPath2' | false    | false    | false
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentSelectorTest.groovy
new file mode 100644
index 0000000..18cee2c
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultProjectComponentSelectorTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model
+
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class DefaultProjectComponentSelectorTest extends Specification {
+    def "is instantiated with non-null constructor parameter values"() {
+        when:
+        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
+
+        then:
+        defaultBuildComponentSelector.projectPath == ':myPath'
+        defaultBuildComponentSelector.displayName == 'project :myPath'
+        defaultBuildComponentSelector.toString() == 'project :myPath'
+    }
+
+    def "is instantiated with null constructor parameter value"() {
+        when:
+        new DefaultProjectComponentSelector(null)
+
+        then:
+        Throwable t = thrown(AssertionError)
+        t.message == 'project path cannot be null'
+    }
+
+    @Unroll
+    def "can compare with other instance (#projectPath)"() {
+        expect:
+        ProjectComponentSelector defaultBuildComponentSelector1 = new DefaultProjectComponentSelector(':myProjectPath1')
+        ProjectComponentSelector defaultBuildComponentSelector2 = new DefaultProjectComponentSelector(projectPath)
+        strictlyEquals(defaultBuildComponentSelector1, defaultBuildComponentSelector2) == equality
+        (defaultBuildComponentSelector1.hashCode() == defaultBuildComponentSelector2.hashCode()) == hashCode
+        (defaultBuildComponentSelector1.toString() == defaultBuildComponentSelector2.toString()) == stringRepresentation
+
+        where:
+        projectPath       | equality | hashCode | stringRepresentation
+        ':myProjectPath1' | true     | true     | true
+        ':myProjectPath2' | false    | false    | false
+    }
+
+    def "prevents matching of null id"() {
+        when:
+        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
+        defaultBuildComponentSelector.matchesStrictly(null)
+
+        then:
+        Throwable t = thrown(AssertionError)
+        assert t.message == 'identifier cannot be null'
+    }
+
+    def "does not match id for unexpected component selector type"() {
+        when:
+        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myPath')
+        boolean matches = defaultBuildComponentSelector.matchesStrictly(new DefaultModuleComponentIdentifier('group', 'name', '1.0'))
+
+        then:
+        assert !matches
+    }
+
+    @Unroll
+    def "matches id (#projectPath)"() {
+        expect:
+        ProjectComponentSelector defaultBuildComponentSelector = new DefaultProjectComponentSelector(':myProjectPath1')
+        ProjectComponentIdentifier defaultBuildComponentIdentifier = new DefaultProjectComponentIdentifier(projectPath)
+        defaultBuildComponentSelector.matchesStrictly(defaultBuildComponentIdentifier) == matchesId
+
+        where:
+        projectPath       | matchesId
+        ':myProjectPath1' | true
+        ':myProjectPath2' | false
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/OpaqueComponentIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/OpaqueComponentIdentifierTest.groovy
new file mode 100644
index 0000000..bea52eb
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/OpaqueComponentIdentifierTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.component.local.model
+
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.strictlyEquals
+
+class OpaqueComponentIdentifierTest extends Specification {
+    def "is instantiated with non-null constructor parameter value"() {
+        given:
+        String configurationName = 'compile'
+
+        when:
+        ComponentIdentifier componentIdentifier = new OpaqueComponentIdentifier(configurationName)
+
+        then:
+        componentIdentifier.displayName == configurationName
+        componentIdentifier.toString() == configurationName
+    }
+
+    def "is instantiated with null constructor parameter value"() {
+        when:
+        new OpaqueComponentIdentifier(null)
+
+        then:
+        thrown(AssertionError)
+    }
+
+    @Unroll
+    def "can compare with other instance (#displayName)"() {
+        expect:
+        ComponentIdentifier componentIdentifier1 = new OpaqueComponentIdentifier('compile')
+        ComponentIdentifier componentIdentifier2 = new OpaqueComponentIdentifier(displayName)
+        strictlyEquals(componentIdentifier1, componentIdentifier2) == equality
+        (componentIdentifier1.hashCode() == componentIdentifier2.hashCode()) == hashCode
+        (componentIdentifier1.toString() == componentIdentifier2.toString()) == stringRepresentation
+
+        where:
+        displayName | equality | hashCode | stringRepresentation
+        'compile'   | true     | true     | true
+        'runtime'   | false    | false    | false
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultDependencyMetaDataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultDependencyMetaDataTest.groovy
new file mode 100644
index 0000000..7ddc8cb
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultDependencyMetaDataTest.groovy
@@ -0,0 +1,203 @@
+/*
+ * 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.component.model
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.gradle.api.artifacts.component.ComponentSelector
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector
+import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import spock.lang.Specification
+
+class DefaultDependencyMetaDataTest extends Specification {
+    final requestedModuleId = IvyUtil.createModuleRevisionId("org", "module", "1.2+")
+
+    def "constructs meta-data from ivy descriptor"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        expect:
+        metaData.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.2+")
+    }
+
+    def "constructs meta-data from component id"() {
+        def id = new DefaultModuleComponentIdentifier("org", "module", "1.1")
+        def metaData = new DefaultDependencyMetaData(id)
+
+        expect:
+        metaData.descriptor.dependencyRevisionId == IvyUtil.createModuleRevisionId("org", "module", "1.1")
+        metaData.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.1")
+    }
+
+    def "constructs meta-data from module version id"() {
+        def id = new DefaultModuleVersionIdentifier("org", "module", "1.1")
+        def metaData = new DefaultDependencyMetaData(id)
+
+        expect:
+        metaData.descriptor.dependencyRevisionId == IvyUtil.createModuleRevisionId("org", "module", "1.1")
+        metaData.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.1")
+    }
+
+    def "creates a copy with new requested version"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        given:
+
+        when:
+        def copy = metaData.withRequestedVersion("1.3+")
+
+        then:
+        copy.requested == DefaultModuleVersionSelector.newSelector("org", "module", "1.3+")
+        copy.descriptor.dependencyRevisionId == IvyUtil.createModuleRevisionId("org", "module", "1.3+")
+    }
+
+    def "returns this if new requested version is the same as current requested version"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        expect:
+        metaData.withRequestedVersion("1.2+").is(metaData)
+        metaData.withTarget(DefaultModuleComponentSelector.newSelector("org", "module", "1.2+")).is(metaData)
+    }
+
+    def "can set changing flag"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        when:
+        def copy = metaData.withChanging()
+
+        then:
+        copy.descriptor.dependencyRevisionId == requestedModuleId
+        copy.descriptor.changing
+    }
+
+    def "returns this when changing is already true"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, true)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        expect:
+        metaData.withChanging().is(metaData)
+    }
+
+    def "returns empty set of artifacts when dependency descriptor does not declare any artifacts"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+        def fromConfiguration = Stub(ConfigurationMetaData)
+        def toConfiguration = Stub(ConfigurationMetaData)
+
+        expect:
+        metaData.getArtifacts(fromConfiguration, toConfiguration).empty
+    }
+
+    def "returns empty set of artifacts when dependency descriptor does not declare any artifacts for source configuration"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+        def fromConfiguration = Stub(ConfigurationMetaData)
+        def toConfiguration = Stub(ConfigurationMetaData)
+
+        given:
+        descriptor.addDependencyArtifact("other", new DefaultDependencyArtifactDescriptor(descriptor, "art", "type", "ext", null, [:]))
+
+        expect:
+        metaData.getArtifacts(fromConfiguration, toConfiguration).empty
+    }
+
+    def "uses artifacts defined by dependency descriptor for specified source and target configurations "() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+        def fromConfiguration = Stub(ConfigurationMetaData)
+        def targetComponent = Stub(ComponentResolveMetaData)
+        def toConfiguration = Stub(ConfigurationMetaData)
+        def artifact1 = Stub(ComponentArtifactMetaData)
+        def artifact2 = Stub(ComponentArtifactMetaData)
+
+        given:
+        fromConfiguration.hierarchy >> (['config', 'super'] as LinkedHashSet)
+        toConfiguration.component >> targetComponent
+        descriptor.addDependencyArtifact("config", new DefaultDependencyArtifactDescriptor(descriptor, "art1", "type", "ext", null, [:]))
+        descriptor.addDependencyArtifact("other", new DefaultDependencyArtifactDescriptor(descriptor, "art2", "type", "ext", null, [:]))
+        descriptor.addDependencyArtifact("super", new DefaultDependencyArtifactDescriptor(descriptor, "art3", "type", "ext", null, [:]))
+        targetComponent.artifact({it.name == 'art1'}) >> artifact1
+        targetComponent.artifact({it.name == 'art3'}) >> artifact2
+
+        expect:
+        metaData.getArtifacts(fromConfiguration, toConfiguration) == [artifact1, artifact2] as Set
+    }
+
+    def "uses artifacts defined by dependency descriptor"() {
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        given:
+        descriptor.addDependencyArtifact("config", new DefaultDependencyArtifactDescriptor(descriptor, "art1", "type", "ext", null, [:]))
+        descriptor.addDependencyArtifact("other", new DefaultDependencyArtifactDescriptor(descriptor, "art2", "type", "ext", null, [:]))
+        descriptor.addDependencyArtifact("super", new DefaultDependencyArtifactDescriptor(descriptor, "art3", "type", "ext", null, [:]))
+
+        expect:
+        metaData.artifacts.size() == 3
+        def artifacts = metaData.artifacts as List
+        artifacts[0].name == 'art1'
+        artifacts[1].name == 'art2'
+        artifacts[2].name == 'art3'
+    }
+
+    def "returns a module component selector if descriptor indicates a default dependency"() {
+        given:
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleId, false, false)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        when:
+        ComponentSelector componentSelector = metaData.getSelector()
+
+        then:
+        componentSelector instanceof ModuleComponentSelector
+        componentSelector.group == 'org'
+        componentSelector.module == 'module'
+        componentSelector.version == '1.2+'
+    }
+
+    def "retains transitive and changing flags in substituted dependency"() {
+        given:
+        def requestedModuleDescriptor = DefaultModuleDescriptor.newDefaultInstance(requestedModuleId)
+        def descriptor = new DefaultDependencyDescriptor(requestedModuleDescriptor, requestedModuleId, false, changing, transitive)
+        def metaData = new DefaultDependencyMetaData(descriptor)
+
+        when:
+        DependencyMetaData replacedMetaData = metaData.withTarget(DefaultProjectComponentSelector.newSelector("test"))
+
+        then:
+        replacedMetaData.getSelector() instanceof ProjectComponentSelector
+        replacedMetaData.isTransitive() == metaData.isTransitive()
+        replacedMetaData.isChanging() == metaData.isChanging()
+
+        where:
+        transitive | changing
+        true       | true
+        false      | true
+        true       | false
+        false      | false
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultIvyArtifactNameTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultIvyArtifactNameTest.groovy
new file mode 100644
index 0000000..2a4729b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultIvyArtifactNameTest.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.internal.component.model
+
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class DefaultIvyArtifactNameTest extends Specification {
+    def "has useful string representation"() {
+        expect:
+        def name = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
+        name.toString() == "name.ext"
+    }
+
+    def "is equal when all fields are equal"() {
+        def name = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
+        def same = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr"])
+        def differentName = new DefaultIvyArtifactName("other", "type", "ext", [attr1: "attr"])
+        def differentType = new DefaultIvyArtifactName("name", "other", "ext", [attr1: "attr"])
+        def differentExt = new DefaultIvyArtifactName("name", "type", "other", [attr1: "attr"])
+        def differentAttr = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "other"])
+        def differentAttrs = new DefaultIvyArtifactName("name", "type", "ext", [other: null])
+
+        expect:
+        name Matchers.strictlyEqual(same)
+        name != differentName
+        name != differentType
+        name != differentExt
+        name != differentAttr
+        name != differentAttrs
+    }
+
+    def "ignores empty and null attributes when determining equality"() {
+        def name = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr", attr2: "", attr3: null])
+        def same = new DefaultIvyArtifactName("name", "type", "ext", [attr1: "attr", attr4: "", attr5: null])
+
+        expect:
+        name Matchers.strictlyEqual(same)
+    }
+
+    def "uses extended attributes to determine classifier"() {
+        def withClassifier = new DefaultIvyArtifactName("name", "type", "ext", ['classifier': 'classifier'])
+        def emptyClassifier = new DefaultIvyArtifactName("name", "type", "ext", ['classifier': ''])
+        def noClassifier = new DefaultIvyArtifactName("name", "type", "ext", [:])
+
+        expect:
+        withClassifier.classifier == 'classifier'
+        emptyClassifier.classifier == null
+        noClassifier.classifier == null
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ArtifactNotFoundExceptionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ArtifactNotFoundExceptionTest.groovy
new file mode 100644
index 0000000..43e1cce
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ArtifactNotFoundExceptionTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve
+
+import org.gradle.internal.component.model.ComponentArtifactIdentifier
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class ArtifactNotFoundExceptionTest extends Specification {
+    def "formats message and locations"() {
+        def artifactId = Stub(ComponentArtifactIdentifier) {
+            getDisplayName() >> "<artifact>"
+        }
+        def locations = ["http://somewhere", "sftp://elsewhere"]
+        def exception = new ArtifactNotFoundException(artifactId, locations)
+
+        expect:
+        exception.message == TextUtil.toPlatformLineSeparators("""Could not find <artifact>.
+Searched in the following locations:
+    http://somewhere
+    sftp://elsewhere""")
+    }
+
+    def "formats message when no locations attempted"() {
+        def artifactId = Stub(ComponentArtifactIdentifier) {
+            getDisplayName() >> "<artifact>"
+        }
+        def exception = new ArtifactNotFoundException(artifactId, [])
+
+        expect:
+        exception.message == "Could not find <artifact>."
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionNotFoundExceptionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionNotFoundExceptionTest.groovy
new file mode 100644
index 0000000..6c1a6a2
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionNotFoundExceptionTest.groovy
@@ -0,0 +1,135 @@
+/*
+ * 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.resolve
+
+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.util.TextUtil.toPlatformLineSeparators
+
+class ModuleVersionNotFoundExceptionTest extends Specification {
+    def "formats message to include id when no locations"() {
+        def exception = new ModuleVersionNotFoundException(newId("org", "a", "1.2"), [])
+
+        expect:
+        exception.message == 'Could not find org:a:1.2.'
+    }
+
+    def "formats message to include id and locations"() {
+        def exception = new ModuleVersionNotFoundException(newId("org", "a", "1.2"), ["http://somewhere", "file:/somewhere"])
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find org:a:1.2.
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "formats message for selector and locations when no versions attempted"() {
+        def exception = new ModuleVersionNotFoundException(newSelector("org", "a", "1.+"), ["http://somewhere", "file:/somewhere"], [], [])
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find any matches for org:a:1.+ as no versions of org:a are available.
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "formats message for selector and locations when versions attempted and non rejected"() {
+        def exception = new ModuleVersionNotFoundException(newSelector("org", "a", "1.+"), ["http://somewhere", "file:/somewhere"], ["1.1", "1.2"], [])
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find any version that matches org:a:1.+.
+Versions that do not match:
+    1.1
+    1.2
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "formats message for selector and locations when versions attempted and some rejected"() {
+        def exception = new ModuleVersionNotFoundException(newSelector("org", "a", "1.+"), ["http://somewhere", "file:/somewhere"], ["0.9", "0.10"], ["1.1", "1.2"])
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find any version that matches org:a:1.+.
+Versions that do not match:
+    0.9
+    0.10
+Versions rejected by component selection rules:
+    1.1
+    1.2
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "formats message for selector and locations when versions attempted and all rejected"() {
+        def exception = new ModuleVersionNotFoundException(newSelector("org", "a", "1.+"), ["http://somewhere", "file:/somewhere"], [], ["1.1", "1.2"])
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find any version that matches org:a:1.+.
+Versions rejected by component selection rules:
+    1.1
+    1.2
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "limits list of candidates"() {
+        def exception = new ModuleVersionNotFoundException(newSelector("org", "a", "1.+"), ["http://somewhere", "file:/somewhere"], (1..20).collect { it.toString() }, (1..10).collect { it.toString() })
+
+        expect:
+        exception.message == toPlatformLineSeparators("""Could not find any version that matches org:a:1.+.
+Versions that do not match:
+    1
+    2
+    3
+    4
+    5
+    + 15 more
+Versions rejected by component selection rules:
+    1
+    2
+    3
+    4
+    5
+    + 5 more
+Searched in the following locations:
+    http://somewhere
+    file:/somewhere""")
+    }
+
+    def "can add incoming paths to exception"() {
+        def a = newId("org", "a", "1.2")
+        def b = newId("org", "b", "5")
+        def c = newId("org", "c", "1.0")
+
+        def exception = new ModuleVersionNotFoundException(newId("a", "b", "c"), ["http://somewhere"])
+        def onePath = exception.withIncomingPaths([[a, b, c]])
+
+        expect:
+        onePath.message == toPlatformLineSeparators('''Could not find a:b:c.
+Searched in the following locations:
+    http://somewhere
+Required by:
+    org:a:1.2 > org:b:5 > org:c:1.0''')
+        onePath.stackTrace == exception.stackTrace
+    }
+
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionResolveExceptionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionResolveExceptionTest.groovy
new file mode 100644
index 0000000..d167be7
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/ModuleVersionResolveExceptionTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resolve
+
+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.util.TextUtil.toPlatformLineSeparators
+
+class ModuleVersionResolveExceptionTest extends Specification {
+    def "provides default message that includes selector"() {
+        def exception1 = new ModuleVersionResolveException(newSelector("org", "a", "1.2"), new RuntimeException())
+
+        expect:
+        exception1.message == 'Could not resolve org:a:1.2.'
+    }
+
+    def "can add incoming paths to exception"() {
+        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(newSelector("a", "b", "c"), cause)
+        def onePath = exception.withIncomingPaths([[a, b, c]])
+        def twoPaths = exception.withIncomingPaths([[a, b, c], [a, c]])
+
+        expect:
+        exception.message == 'Could not resolve a:b:c.'
+
+        onePath.message == toPlatformLineSeparators('''Could not resolve a:b:c.
+Required by:
+    org:a:1.2 > org:b:5 > org:c:1.0''')
+        onePath.stackTrace == exception.stackTrace
+        onePath.cause == cause
+
+        twoPaths.message == toPlatformLineSeparators('''Could not resolve a:b:c.
+Required by:
+    org:a:1.2 > org:b:5 > org:c:1.0
+    org:a:1.2 > org:c:1.0''')
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResultTest.groovy
new file mode 100644
index 0000000..1e56de1
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactResolveResultTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * 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.resolve.result
+import org.gradle.internal.resolve.ArtifactNotFoundException
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.internal.component.model.ComponentArtifactIdentifier
+import spock.lang.Specification
+
+class DefaultBuildableArtifactResolveResultTest extends Specification {
+    final result = new DefaultBuildableArtifactResolveResult()
+    final artifactFile = Mock(File)
+    final artifactId = Mock(ComponentArtifactIdentifier)
+
+    def "has no result by default"() {
+        expect:
+        !result.hasResult()
+    }
+
+    def "can have artifact result"() {
+        when:
+        result.resolved(artifactFile)
+
+        then:
+        result.file == artifactFile
+        result.failure == null
+        result.hasResult()
+    }
+
+    def "can have missing result"() {
+        when:
+        result.notFound(artifactId)
+
+        then:
+        result.failure instanceof ArtifactNotFoundException
+        result.hasResult()
+
+        when:
+        result.file
+
+        then:
+        def e = thrown(ArtifactNotFoundException)
+        result.failure == e
+    }
+
+    def "can have failure result"() {
+        def failure = new ArtifactResolveException("broken")
+
+        when:
+        result.failed(failure)
+
+        then:
+        result.failure == failure
+        result.hasResult()
+
+        when:
+        result.file
+
+        then:
+        ArtifactResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot get file when no result specified"() {
+        when:
+        result.file
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get failure when no result specified"() {
+        when:
+        result.failure
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get file when resolve failed"() {
+        def failure = new ArtifactResolveException("broken")
+
+        when:
+        result.failed(failure)
+        result.file
+
+        then:
+        ArtifactResolveException e = thrown()
+        e == failure
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResultTest.groovy
new file mode 100644
index 0000000..57ba385
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableArtifactSetResolveResultTest.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.resolve.result
+import org.gradle.internal.resolve.ArtifactResolveException
+import org.gradle.internal.component.model.ComponentArtifactMetaData
+import spock.lang.Specification
+
+class DefaultBuildableArtifactSetResolveResultTest extends Specification {
+    final result = new DefaultBuildableArtifactSetResolveResult()
+
+    def "cannot get artifacts when no result specified"() {
+        when:
+        result.artifacts
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get failure when no result specified"() {
+        when:
+        result.failure
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get artifacts when resolve failed"() {
+        def failure = new ArtifactResolveException("broken")
+
+        when:
+        result.failed(failure)
+        result.artifacts
+
+        then:
+        ArtifactResolveException e = thrown()
+        e == failure
+    }
+
+    def "has result when artifacts set"() {
+        when:
+        def artifact = Mock(ComponentArtifactMetaData)
+        result.resolved([artifact])
+
+        then:
+        result.hasResult()
+        result.failure == null
+        result.artifacts == [artifact] as Set
+    }
+
+    def "has result when failure set"() {
+        when:
+        final failure = new ArtifactResolveException("bad")
+        result.failed(failure)
+
+        then:
+        result.hasResult()
+        result.failure == failure
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResultTest.groovy
new file mode 100644
index 0000000..acaf3da
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentIdResolveResultTest.groovy
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.ModuleVersionSelector
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+class DefaultBuildableComponentIdResolveResultTest extends Specification {
+    def result = new DefaultBuildableComponentIdResolveResult()
+
+    def "can resolve using id"() {
+        def id = Stub(ComponentIdentifier)
+        def mvId = Stub(ModuleVersionIdentifier)
+
+        when:
+        result.resolved(id, mvId)
+
+        then:
+        result.hasResult()
+        result.id == id
+        result.moduleVersionId == mvId
+        result.selectionReason == VersionSelectionReasons.REQUESTED
+        result.metaData == null
+        result.failure == null
+    }
+
+    def "can override selection reason"() {
+        def id = Stub(ComponentIdentifier)
+        def mvId = Stub(ModuleVersionIdentifier)
+
+        when:
+        result.resolved(id, mvId)
+        result.selectionReason = VersionSelectionReasons.CONFLICT_RESOLUTION
+
+        then:
+        result.selectionReason == VersionSelectionReasons.CONFLICT_RESOLUTION
+    }
+
+    def "can resolve using meta-data"() {
+        def id = Stub(ComponentIdentifier)
+        def mvId = Stub(ModuleVersionIdentifier)
+        def metaData = Stub(ComponentResolveMetaData) {
+            getId() >> mvId
+            getComponentId() >> id
+        }
+
+        when:
+        result.resolved(metaData)
+
+        then:
+        result.hasResult()
+        result.id == id
+        result.moduleVersionId == mvId
+        result.metaData == metaData
+        result.selectionReason == VersionSelectionReasons.REQUESTED
+        result.failure == null
+    }
+
+    def "can mark as failed"() {
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), "broken")
+
+        when:
+        result.failed(failure)
+
+        then:
+        result.hasResult()
+        result.failure == failure
+
+        when:
+        result.id
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+        result.selectionReason == VersionSelectionReasons.REQUESTED
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResultTest.groovy
new file mode 100644
index 0000000..a731f49
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentResolveResultTest.groovy
@@ -0,0 +1,154 @@
+/*
+ * 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.resolve.result
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.ModuleVersionSelector
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetaData
+import org.gradle.internal.component.model.ComponentResolveMetaData
+import org.gradle.internal.resolve.ModuleVersionNotFoundException
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class DefaultBuildableComponentResolveResultTest extends Specification {
+    def result = new DefaultBuildableComponentResolveResult()
+
+    def "can query id and meta-data when resolved"() {
+        ModuleVersionIdentifier id = Stub()
+        ModuleComponentResolveMetaData metaData = Stub() {
+            getId() >> id
+        }
+
+        when:
+        result.resolved(metaData)
+
+        then:
+        result.id == id
+        result.metaData == metaData
+    }
+
+    def "cannot get id when no result has been specified"() {
+        when:
+        result.id
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get meta-data when no result has been specified"() {
+        when:
+        result.metaData
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get failure when no result has been specified"() {
+        when:
+        result.failure
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'No result has been specified.'
+    }
+
+    def "cannot get id when resolve failed"() {
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+
+        when:
+        result.failed(failure)
+        result.id
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "cannot get meta-data when resolve failed"() {
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+
+        when:
+        result.failed(failure)
+        result.metaData
+
+        then:
+        ModuleVersionResolveException e = thrown()
+        e == failure
+    }
+
+    def "failure is null when successfully resolved"() {
+        when:
+        result.resolved(Mock(ModuleComponentResolveMetaData))
+
+        then:
+        result.failure == null
+    }
+
+    def "fails with not found exception when not found using module version id"() {
+        def id = Mock(ModuleVersionIdentifier) {
+            it.group >> "org.gradle"
+            it.name >> "core"
+            it.version >> "2.3"
+        }
+
+        when:
+        result.notFound(id)
+
+        then:
+        result.failure instanceof ModuleVersionNotFoundException
+    }
+
+    def "copies results to an id resolve result"() {
+        def idResult = Mock(BuildableComponentIdResolveResult)
+        def metaData = Stub(ComponentResolveMetaData)
+
+        given:
+        result.attempted("a")
+        result.attempted("b")
+        result.resolved(metaData)
+
+        when:
+        result.applyTo(idResult)
+
+        then:
+        1 * idResult.attempted("a")
+        1 * idResult.attempted("b")
+        1 * idResult.resolved(metaData)
+    }
+
+    def "copies failure result to an id resolve result"() {
+        def idResult = Mock(BuildableComponentIdResolveResult)
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), "broken")
+
+        given:
+        result.attempted("a")
+        result.attempted("b")
+        result.failed(failure)
+
+        when:
+        result.applyTo(idResult)
+
+        then:
+        1 * idResult.attempted("a")
+        1 * idResult.attempted("b")
+        1 * idResult.failed(failure)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResultTest.groovy
new file mode 100644
index 0000000..d08373b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableComponentSelectionResultTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.internal.resolve.result.BuildableComponentSelectionResult.State.*
+
+class DefaultBuildableComponentSelectionResultTest extends Specification {
+    DefaultBuildableComponentSelectionResult result = new DefaultBuildableComponentSelectionResult()
+
+    def "has no matching state by default"() {
+        expect:
+        result.state == Unknown
+        !result.hasResult()
+
+        when:
+        result.match
+
+        then:
+        thrown(IllegalStateException)
+
+        when:
+        result.failure
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "can mark matching"() {
+        given:
+        ModuleComponentIdentifier moduleComponentIdentifier = DefaultModuleComponentIdentifier.newId('org.company', 'foo', '1.5')
+
+        when:
+        result.matches(moduleComponentIdentifier)
+
+        then:
+        result.state == Match
+        result.hasResult()
+        result.match == moduleComponentIdentifier
+        result.failure == null
+    }
+
+    def "can mark no match"() {
+        when:
+        result.noMatchFound()
+
+        then:
+        result.state == NoMatch
+        result.hasResult()
+        result.failure == null
+
+        when:
+        result.match
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "can mark failed"() {
+        def failure = new ModuleVersionResolveException(Stub(ModuleComponentIdentifier), "")
+
+        when:
+        result.failed(failure)
+
+        then:
+        result.state == Failed
+        result.hasResult()
+        result.failure == failure
+
+        when:
+        result.match
+
+        then:
+        thrown(IllegalStateException)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResultTest.groovy
new file mode 100644
index 0000000..5fe9bdd
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResultTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * 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.resolve.result
+
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+
+class DefaultBuildableModuleComponentMetaDataResolveResultTest extends Specification {
+    def descriptor = new DefaultBuildableModuleComponentMetaDataResolveResult()
+
+    def "has unknown state by default"() {
+        expect:
+        descriptor.state == BuildableModuleComponentMetaDataResolveResult.State.Unknown
+        !descriptor.hasResult()
+    }
+
+    def "can mark as missing"() {
+        when:
+        descriptor.missing()
+
+        then:
+        descriptor.state == BuildableModuleComponentMetaDataResolveResult.State.Missing
+        descriptor.failure == null
+        descriptor.authoritative
+        descriptor.hasResult()
+    }
+
+    def "can mark as failed"() {
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+
+        when:
+        descriptor.failed(failure)
+
+        then:
+        descriptor.state == BuildableModuleComponentMetaDataResolveResult.State.Failed
+        descriptor.failure == failure
+        descriptor.authoritative
+        descriptor.hasResult()
+    }
+
+    def "can mark as resolved using meta-data"() {
+        def metaData = Stub(MutableModuleComponentResolveMetaData)
+
+        when:
+        descriptor.resolved(metaData)
+
+        then:
+        descriptor.state == BuildableModuleComponentMetaDataResolveResult.State.Resolved
+        descriptor.failure == null
+        descriptor.metaData == metaData
+        descriptor.authoritative
+        descriptor.hasResult()
+    }
+
+    def "cannot get failure when has no result"() {
+        when:
+        descriptor.failure
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get meta-data when has no result"() {
+        when:
+        descriptor.metaData
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get authoritative flag when has no result"() {
+        when:
+        descriptor.authoritative
+
+        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
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResultTest.groovy
new file mode 100644
index 0000000..a09599b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultBuildableModuleVersionListingResolveResultTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result
+
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import spock.lang.Specification
+
+import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
+import static org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult.State.*
+
+class DefaultBuildableModuleVersionListingResolveResultTest extends Specification {
+    def descriptor = new DefaultBuildableModuleVersionListingResolveResult()
+
+    def "has unknown state by default"() {
+        expect:
+        descriptor.state == Unknown
+        !descriptor.hasResult()
+    }
+
+    def "can mark as listed using version strings"() {
+        when:
+        descriptor.listed(['1.2', '1.3'])
+
+        then:
+        descriptor.state == Listed
+        descriptor.authoritative
+        descriptor.versions == ['1.2', '1.3'] as Set
+    }
+
+    def "can mark as failed"() {
+        def failure = new ModuleVersionResolveException(newSelector("a", "b", "c"), "broken")
+
+        when:
+        descriptor.failed(failure)
+
+        then:
+        descriptor.state == Failed
+        descriptor.failure == failure
+        descriptor.authoritative
+        descriptor.hasResult()
+    }
+
+    def "cannot get failure when has no result"() {
+        when:
+        descriptor.failure
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get listing when has no result"() {
+        when:
+        descriptor.versions
+
+        then:
+        thrown(IllegalStateException)
+    }
+
+    def "cannot get authoritative flag when has no result"() {
+        when:
+        descriptor.authoritative
+
+        then:
+        thrown(IllegalStateException)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResultTest.groovy
new file mode 100644
index 0000000..03e2bc6
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resolve/result/DefaultResourceAwareResolveResultTest.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resolve.result
+
+import spock.lang.Specification
+
+class DefaultResourceAwareResolveResultTest extends Specification {
+    def "copies values to target result"() {
+        def src = new DefaultResourceAwareResolveResult()
+        src.attempted("a")
+        src.attempted("b")
+        def dest = new DefaultResourceAwareResolveResult()
+
+        when:
+        src.applyTo(dest)
+
+        then:
+        dest.attempted == ["a", "b"]
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/cached/DefaultArtifactResolutionCacheTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/cached/DefaultArtifactResolutionCacheTest.groovy
new file mode 100644
index 0000000..691b376
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/cached/DefaultArtifactResolutionCacheTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * 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.resource.cached
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData
+import org.gradle.internal.serialize.Serializer
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.InMemoryIndexedCache
+import org.gradle.util.BuildCommencedTimeProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultArtifactResolutionCacheTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
+
+    BuildCommencedTimeProvider timeProvider = Stub(BuildCommencedTimeProvider) {
+        getCurrentTime() >> 0
+    }
+
+    def cacheLockingManager = Stub(CacheLockingManager) {
+        useCache(_, _) >> { displayName, action ->
+            if (action instanceof org.gradle.internal.Factory) {
+                return action.create()
+            } else {
+                action.run()
+            }
+        }
+
+        createCache(_, _, _) >> { String file, Serializer keySerializer, Serializer valueSerializer ->
+            return new InMemoryIndexedCache<>(valueSerializer)
+        }
+    }
+    
+    DefaultCachedExternalResourceIndex<String> index
+
+    def setup() {
+        index = new DefaultCachedExternalResourceIndex("index", String, timeProvider, cacheLockingManager)
+    }
+
+    @Unroll "stores entry - lastModified = #lastModified"() {
+        given:
+        def key = "key"
+        def artifactFile = tmp.createFile("artifact") << "content"
+        
+        when:
+        index.store(key, artifactFile, new DefaultExternalResourceMetaData(new URI("abc"), lastModified, 100, null, null, null))
+        
+        then:
+        def cached = index.lookup(key)
+        
+        and:
+        cached != null
+        cached.cachedFile == artifactFile
+        cached.externalResourceMetaData != null
+        cached.externalResourceMetaData.lastModified == lastModified
+        cached.externalResourceMetaData.location == new URI("abc")
+
+
+        where:
+        lastModified << [new Date(), null]
+    }
+
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinderTest.groovy
new file mode 100644
index 0000000..f190d38
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinderTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * 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.resource.local
+
+import spock.lang.Specification
+import org.gradle.internal.hash.HashUtil
+
+class CompositeLocallyAvailableResourceFinderTest extends Specification {
+    
+    def "interrogates composites in turn as needed"() {
+        given:
+        def f1 = Mock(LocallyAvailableResourceFinder)
+        def c1 = Mock(LocallyAvailableResourceCandidates)
+        def f2 = Mock(LocallyAvailableResourceFinder)
+        def c2 = Mock(LocallyAvailableResourceCandidates)
+        def f3 = Mock(LocallyAvailableResourceFinder)
+        def c3 = Mock(LocallyAvailableResourceCandidates)
+        def hash = HashUtil.sha1("abc".bytes)
+
+        def composite = new CompositeLocallyAvailableResourceFinder<String>([f1, f2, f3])
+        def criterion = "abc"
+
+        when:
+        def candidates = composite.findCandidates(criterion)
+
+        then:
+        1 * f1.findCandidates(criterion) >> c1
+
+        then:
+        1 * f2.findCandidates(criterion) >> c2
+
+        then:
+        1 * f3.findCandidates(criterion) >> c3
+
+        and:
+        0 * c1._(*_)
+        0 * c2._(*_)
+        0 * c3._(*_)
+
+        when:
+        def isNone = candidates.isNone()
+
+        then:
+        1 * c1.isNone() >> true
+
+        and:
+        1 * c2.isNone() >> false
+        0 * c3.isNone()
+
+        when:
+        def match = candidates.findByHashValue(hash)
+
+        then:
+        1 * c1.findByHashValue(hash) >> null
+
+        and:
+        1 * c2.findByHashValue(hash) >> Mock(LocallyAvailableResource)
+        0 * c3.findByHashValue(_)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidatesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidatesTest.groovy
new file mode 100644
index 0000000..a2f685a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidatesTest.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.internal.resource.local
+
+import org.gradle.internal.Factory
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.internal.hash.HashUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class LazyLocallyAvailableResourceCandidatesTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider tmp
+    
+    def "does not query factory until necessary"() {
+        given:
+        def factory = Mock(Factory)
+
+        when:
+        def candidates = new LazyLocallyAvailableResourceCandidates(factory)
+
+        then:
+        0 * factory.create()
+        
+        when:
+        def isNone = candidates.isNone()
+        
+        then:
+        !isNone
+        1 * factory.create() >> [file("abc"), file("def")]
+        
+        when:
+        def candidate = candidates.findByHashValue(HashUtil.sha1("def".bytes))
+
+        then:
+        candidate.file.name == "def"
+        0 * factory.create()
+    }
+    
+    File file(path) {
+        tmp.createFile(path) << path
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
new file mode 100644
index 0000000..0a2248d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/DefaultCacheAwareExternalResourceAccessorTest.groovy
@@ -0,0 +1,335 @@
+/*
+ * 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.resource.transfer
+
+import org.gradle.api.Transformer
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.api.internal.file.TemporaryFileProvider
+import org.gradle.internal.hash.HashUtil
+import org.gradle.internal.resource.ExternalResource
+import org.gradle.internal.resource.cached.CachedExternalResource
+import org.gradle.internal.resource.cached.CachedExternalResourceIndex
+import org.gradle.internal.resource.local.DefaultLocallyAvailableResource
+import org.gradle.internal.resource.local.LocallyAvailableResource
+import org.gradle.internal.resource.local.LocallyAvailableResourceCandidates
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.BuildCommencedTimeProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultCacheAwareExternalResourceAccessorTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tempDir = new TestNameTestDirectoryProvider()
+    final repository = Mock(ExternalResourceRepository)
+    final progressLoggingRepo = Mock(ExternalResourceRepository)
+    final index = Mock(CachedExternalResourceIndex)
+    final timeProvider = Mock(BuildCommencedTimeProvider)
+    final tempFile = tempDir.file("temp-file")
+    final cachedFile = tempDir.file("cached-file")
+    final temporaryFileProvider = Stub(TemporaryFileProvider) {
+        createTemporaryFile(_, _, _) >> tempFile
+    }
+    final cacheLockingManager = Mock(CacheLockingManager)
+    final cache = new DefaultCacheAwareExternalResourceAccessor(repository, index, timeProvider, temporaryFileProvider, cacheLockingManager)
+
+    def "returns null when the request resource is not cached and does not exist in the remote repository"() {
+        def uri = new URI("scheme:thing")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result == null
+
+        and:
+        1 * index.lookup("scheme:thing") >> null
+        1 * localCandidates.isNone() >> true
+        1 * repository.withProgressLogging() >> progressLoggingRepo
+        1 * progressLoggingRepo.getResource(uri) >> null
+        0 * _._
+    }
+
+    def "returns null when the request resource is not cached and there are local candidates but the resource does not exist in the remote repository"() {
+        def uri = new URI("scheme:thing")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result == null
+
+        and:
+        1 * index.lookup("scheme:thing") >> null
+        1 * localCandidates.isNone() >> false
+        1 * repository.getResourceMetaData(uri) >> null
+        0 * _._
+    }
+
+    def "downloads resource and moves it into the cache when it is not cached"() {
+        def uri = new URI("scheme:thing")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def remoteResource = Mock(ExternalResource)
+        def metaData = Mock(ExternalResourceMetaData)
+        def localResource = new DefaultLocallyAvailableResource(cachedFile)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == metaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> null
+        1 * localCandidates.isNone() >> true
+        1 * repository.withProgressLogging() >> progressLoggingRepo
+        1 * progressLoggingRepo.getResource(uri) >> remoteResource
+        _ * remoteResource.name >> "remoteResource"
+        1 * remoteResource.withContent(_) >> { ExternalResource.ContentAction a ->
+            a.execute(new ByteArrayInputStream(), metaData)
+        }
+        1 * remoteResource.close()
+
+        and:
+        1 * cacheLockingManager.useCache(_, _) >> { String description, org.gradle.internal.Factory factory ->
+            return factory.create()
+        }
+        1 * fileStore.moveIntoCache(tempFile) >> localResource
+        1 * index.store("scheme:thing", cachedFile, metaData)
+        0 * _._
+    }
+
+    def "reuses cached resource if it has not expired"() {
+        def uri = new URI("scheme:thing")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def metaData = Mock(ExternalResourceMetaData)
+        def cachedResource = Stub(CachedExternalResource)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == metaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> cachedResource
+        _ * timeProvider.currentTime >> 24000L
+        _ * cachedResource.cachedAt >> 24000L
+        _ * cachedResource.cachedFile >> cachedFile
+        _ * cachedResource.externalResourceMetaData >> metaData
+        0 * _._
+    }
+
+    def "will use sha1 from metadata for finding candidates if available"() {
+        given:
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def cached = Mock(CachedExternalResource)
+        def candidate = tempDir.createFile("candidate-file")
+        def sha1 = HashUtil.createHash(candidate, "sha1")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def cachedMetaData = Mock(ExternalResourceMetaData)
+        def remoteMetaData = Mock(ExternalResourceMetaData)
+        def localCandidate = Mock(LocallyAvailableResource)
+        def uri = new URI("scheme:thing")
+        def localResource = new DefaultLocallyAvailableResource(cachedFile)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == remoteMetaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> cached
+        timeProvider.currentTime >> 24000L
+        cached.cachedAt >> 23999L
+        cached.externalResourceMetaData >> cachedMetaData
+        1 * repository.getResourceMetaData(uri) >> remoteMetaData
+        localCandidates.none >> false
+        remoteMetaData.sha1 >> sha1
+        remoteMetaData.etag >> null
+        remoteMetaData.lastModified >> null
+        cachedMetaData.etag >> null
+        cachedMetaData.lastModified >> null
+        1 * localCandidates.findByHashValue(sha1) >> localCandidate
+        localCandidate.file >> candidate
+        cached.cachedFile >> cachedFile
+        0 * _._
+
+        and:
+        1 * cacheLockingManager.useCache(_, _) >> { String description, org.gradle.internal.Factory factory ->
+            return factory.create()
+        }
+        1 * fileStore.moveIntoCache(tempFile) >> localResource
+        1 * index.store("scheme:thing", cachedFile, remoteMetaData)
+        0 * _._
+    }
+
+    def "will download sha1 for finding candidates if not available in meta-data"() {
+        given:
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def candidate = tempDir.createFile("candidate-file")
+        def sha1 = HashUtil.createHash(candidate, "sha1")
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def cachedMetaData = Mock(ExternalResourceMetaData)
+        def remoteMetaData = Mock(ExternalResourceMetaData)
+        def localCandidate = Mock(LocallyAvailableResource)
+        def remoteSha1 = Mock(ExternalResource)
+        def uri = new URI("scheme:thing")
+        def localResource = new DefaultLocallyAvailableResource(cachedFile)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == remoteMetaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> null
+        1 * repository.getResourceMetaData(uri) >> remoteMetaData
+        localCandidates.none >> false
+        remoteMetaData.sha1 >> null
+        remoteMetaData.etag >> null
+        remoteMetaData.lastModified >> null
+        cachedMetaData.etag >> null
+        cachedMetaData.lastModified >> null
+        1 * repository.getResource(new URI("scheme:thing.sha1")) >> remoteSha1
+        1 * remoteSha1.withContent(_) >> { Transformer t ->
+            t.transform(new ByteArrayInputStream(sha1.asZeroPaddedHexString(40).bytes))
+        }
+        1 * remoteSha1.close()
+        1 * localCandidates.findByHashValue(sha1) >> localCandidate
+        localCandidate.file >> candidate
+        0 * _._
+
+        and:
+        1 * cacheLockingManager.useCache(_, _) >> { String description, org.gradle.internal.Factory factory ->
+            return factory.create()
+        }
+        1 * fileStore.moveIntoCache(tempFile) >> localResource
+        1 * index.store("scheme:thing", cachedFile, remoteMetaData)
+        0 * _._
+    }
+
+    def "downloads resource directly when no remote sha1 available"() {
+        given:
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def cachedMetaData = Mock(ExternalResourceMetaData)
+        def remoteMetaData = Mock(ExternalResourceMetaData)
+        def remoteResource = Mock(ExternalResource)
+        def uri = new URI("scheme:thing")
+        def localResource = new DefaultLocallyAvailableResource(cachedFile)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == remoteMetaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> null
+        1 * repository.getResourceMetaData(uri) >> remoteMetaData
+        localCandidates.none >> false
+        remoteMetaData.sha1 >> null
+        remoteMetaData.etag >> null
+        remoteMetaData.lastModified >> null
+        cachedMetaData.etag >> null
+        cachedMetaData.lastModified >> null
+        1 * repository.getResource(new URI("scheme:thing.sha1")) >> null
+        1 * repository.withProgressLogging() >> progressLoggingRepo
+        1 * progressLoggingRepo.getResource(uri) >> remoteResource
+        1 * remoteResource.withContent(_) >> { ExternalResource.ContentAction a ->
+            a.execute(new ByteArrayInputStream(), remoteMetaData)
+        }
+        1 * remoteResource.close()
+        0 * _._
+
+        and:
+        1 * cacheLockingManager.useCache(_, _) >> { String description, org.gradle.internal.Factory factory ->
+            return factory.create()
+        }
+        1 * fileStore.moveIntoCache(tempFile) >> localResource
+        1 * index.store("scheme:thing", cachedFile, remoteMetaData)
+        0 * _._
+    }
+
+    def "downloads resource directly when local candidate cannot be copied"() {
+        given:
+        def localCandidates = Mock(LocallyAvailableResourceCandidates)
+        def cached = Mock(CachedExternalResource)
+        def candidate = tempDir.createFile("candidate-file")
+        def sha1 = HashUtil.createHash(candidate, "sha1")
+        candidate << "some extra stuff"
+        def fileStore = Mock(CacheAwareExternalResourceAccessor.ResourceFileStore)
+        def cachedMetaData = Mock(ExternalResourceMetaData)
+        def remoteMetaData = Mock(ExternalResourceMetaData)
+        def localCandidate = Mock(LocallyAvailableResource)
+        def uri = new URI("scheme:thing")
+        def remoteResource = Mock(ExternalResource)
+        def localResource = new DefaultLocallyAvailableResource(cachedFile)
+
+        when:
+        def result = cache.getResource(uri, fileStore, localCandidates)
+
+        then:
+        result.localResource.file == cachedFile
+        result.metaData == remoteMetaData
+
+        and:
+        1 * index.lookup("scheme:thing") >> cached
+        timeProvider.currentTime >> 24000L
+        cached.cachedAt >> 23999L
+        cached.externalResourceMetaData >> cachedMetaData
+        1 * repository.getResourceMetaData(uri) >> remoteMetaData
+        localCandidates.none >> false
+        remoteMetaData.sha1 >> sha1
+        remoteMetaData.etag >> null
+        remoteMetaData.lastModified >> null
+        cachedMetaData.etag >> null
+        cachedMetaData.lastModified >> null
+        1 * localCandidates.findByHashValue(sha1) >> localCandidate
+        localCandidate.file >> candidate
+        cached.cachedFile >> cachedFile
+        1 * repository.withProgressLogging() >> progressLoggingRepo
+        1 * progressLoggingRepo.getResource(uri) >> remoteResource
+        1 * remoteResource.withContent(_) >> { ExternalResource.ContentAction a ->
+            a.execute(new ByteArrayInputStream(), remoteMetaData)
+        }
+        1 * remoteResource.close()
+        0 * _._
+
+        and:
+        1 * cacheLockingManager.useCache(_, _) >> { String description, org.gradle.internal.Factory factory ->
+            return factory.create()
+        }
+        1 * fileStore.moveIntoCache(tempFile) >> localResource
+        1 * index.store("scheme:thing", cachedFile, remoteMetaData)
+        0 * _._
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
new file mode 100644
index 0000000..8be98df
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceAccessorTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * 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.resource.transfer
+
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData
+import org.gradle.logging.ProgressLogger
+import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ProgressLoggingExternalResourceAccessorTest extends Specification {
+
+    ExternalResourceAccessor accessor = Mock()
+    ProgressLoggerFactory progressLoggerFactory = Mock();
+    ProgressLoggingExternalResourceAccessor progressLoggerAccessor = new ProgressLoggingExternalResourceAccessor(accessor, progressLoggerFactory)
+    ProgressLogger progressLogger = Mock()
+    ExternalResourceReadResponse externalResource = Mock()
+    ExternalResourceMetaData metaData = Mock()
+
+    def setup() {
+        externalResource.metaData >> metaData
+    }
+
+    @Unroll
+    def "delegates #method to delegate resource accessor"() {
+        when:
+        progressLoggerAccessor."$method"(new URI("location"))
+
+        then:
+        1 * accessor."$method"(new URI("location"))
+
+        where:
+        method << ['getMetaData', 'openResource']
+    }
+
+    def "getResource returns null when delegate returns null"() {
+        setup:
+        accessor.openResource(new URI("location")) >> null
+
+        when:
+        def loadedResource = progressLoggerAccessor.openResource(new URI("location"))
+
+        then:
+        loadedResource == null
+    }
+
+    def "wraps response in delegate"() {
+        setup:
+        accessor.openResource(new URI("location")) >> externalResource
+
+        when:
+        def loadedResource = progressLoggerAccessor.openResource(new URI("location"))
+
+        then:
+        loadedResource != null
+        1 * progressLoggerFactory.newOperation(_) >> progressLogger
+        1 * progressLogger.started()
+
+        when:
+        loadedResource.close()
+
+        then:
+        1 * externalResource.close()
+        1 * progressLogger.completed()
+    }
+
+    def "fires progress events as content is read"() {
+        setup:
+        accessor.openResource(new URI("location")) >> externalResource
+        metaData.getContentLength() >> 4096
+        externalResource.openStream() >> new ByteArrayInputStream(new byte[4096])
+
+        when:
+        def resource = progressLoggerAccessor.openResource(new URI("location"))
+        def inputStream = resource.openStream()
+        inputStream.read()
+        inputStream.read()
+        inputStream.read(new byte[560])
+        inputStream.read(new byte[1000])
+        inputStream.read(new byte[1600])
+        inputStream.read(new byte[1024])
+        inputStream.read(new byte[1024])
+        resource.close()
+
+        then:
+        1 * progressLoggerFactory.newOperation(_) >> progressLogger
+        1 * progressLogger.started()
+        1 * progressLogger.progress('1 KB/4 KB downloaded')
+        1 * progressLogger.progress('3 KB/4 KB downloaded')
+        1 * progressLogger.progress('4 KB/4 KB downloaded')
+        1 * progressLogger.completed()
+        0 * progressLogger.progress(_)
+    }
+
+    def "fires complete event when response closed with partially read stream"() {
+        setup:
+        accessor.openResource(new URI("location")) >> externalResource
+        metaData.getContentLength() >> 4096
+        externalResource.openStream() >> new ByteArrayInputStream(new byte[4096])
+
+        when:
+        def resource = progressLoggerAccessor.openResource(new URI("location"))
+        def inputStream = resource.openStream()
+        inputStream.read(new byte[1600])
+        resource.close()
+
+        then:
+        1 * progressLoggerFactory.newOperation(_) >> progressLogger
+        1 * progressLogger.started()
+        1 * progressLogger.progress('1 KB/4 KB downloaded')
+        1 * progressLogger.completed()
+        0 * progressLogger.progress(_)
+    }
+
+    def "no progress events logged for resources smaller 1024 bytes"() {
+        setup:
+        accessor.openResource(new URI("location")) >> externalResource
+        metaData.getContentLength() >> 1023
+        externalResource.openStream() >> new ByteArrayInputStream(new byte[1023])
+
+        when:
+        def resource = progressLoggerAccessor.openResource(new URI("location"))
+        def inputStream = resource.openStream()
+        inputStream.read(new byte[1024])
+        resource.close()
+
+        then:
+        1 * progressLoggerFactory.newOperation(_) >> progressLogger
+        1 * progressLogger.started()
+        1 * progressLogger.completed()
+        0 * progressLogger.progress(_)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploaderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploaderTest.groovy
new file mode 100644
index 0000000..2e83667
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ProgressLoggingExternalResourceUploaderTest.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.internal.resource.transfer
+
+import org.gradle.internal.resource.local.LocalResource
+import org.gradle.logging.ProgressLogger
+import org.gradle.logging.ProgressLoggerFactory
+import spock.lang.Specification
+
+class ProgressLoggingExternalResourceUploaderTest extends Specification {
+    def uploader = Mock(ExternalResourceUploader)
+    def progressLoggerFactory = Mock(ProgressLoggerFactory);
+    def progressLoggerUploader = new ProgressLoggingExternalResourceUploader(uploader, progressLoggerFactory)
+    def progressLogger = Mock(ProgressLogger)
+    def inputStream = Mock(InputStream)
+    def delegateResource = Mock(LocalResource)
+
+    def "delegates upload to delegate uploader and logs progress"() {
+        setup:
+        startsProgress()
+
+        when:
+        progressLoggerUploader.upload(localResource(), new URI("http://a/remote/path"))
+        then:
+        1 * delegateResource.open() >> inputStream
+        1 * uploader.upload(_, new URI("http://a/remote/path")) >> {resource, destination ->
+            def stream = resource.open();
+            assert stream.read(new byte[1024]) == 1024
+            assert stream.read() == 48
+        }
+        1 * inputStream.read(_, 0, 1024) >> 1024
+        1 * inputStream.read() >> 48
+        1 * progressLogger.progress(_)
+        1 * progressLogger.completed()
+    }
+
+    private LocalResource localResource() {
+        return new LocalResource() {
+            @Override
+            InputStream open() {
+                return delegateResource.open()
+            }
+
+            @Override
+            long getContentLength() {
+                return delegateResource.contentLength
+            }
+        }
+    }
+
+    def startsProgress() {
+        1 * progressLoggerFactory.newOperation(ProgressLoggingExternalResourceUploader.class) >> progressLogger;
+        1 * progressLogger.setDescription("Upload http://a/remote/path")
+        1 * progressLogger.setLoggingHeader("Upload http://a/remote/path")
+        1 * progressLogger.started()
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ResourceOperationTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ResourceOperationTest.groovy
new file mode 100644
index 0000000..41405a0
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/resource/transfer/ResourceOperationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * 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.resource.transfer
+
+import spock.lang.Specification
+import org.gradle.logging.ProgressLogger
+
+class ResourceOperationTest extends Specification {
+
+    ProgressLogger progressLogger = Mock()
+
+    def "no progress event is logged for files < 1024bytes"(){
+        given:
+        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 1023)
+        when:
+        operation.logProcessedBytes(1023)
+        then:
+        0 * progressLogger.progress(_)
+    }
+
+    def "logs processed bytes in kbyte intervalls"() {
+        given:
+        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 1024 * 10)
+        when:
+        operation.logProcessedBytes(512 * 0)
+        operation.logProcessedBytes(512 * 1)
+        then:
+        0 * progressLogger.progress(_)
+
+        when:
+        operation.logProcessedBytes(512 * 1)
+        operation.logProcessedBytes(512 * 2)
+        then:
+        1 * progressLogger.progress("1 KB/10 KB downloaded")
+        1 * progressLogger.progress("2 KB/10 KB downloaded")
+        0 * progressLogger.progress(_)
+    }
+
+    def "last chunk of bytes <1k is not logged"(){
+        given:
+        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.download, 2000)
+        when:
+        operation.logProcessedBytes(1000)
+        operation.logProcessedBytes(1000)
+        then:
+        1 * progressLogger.progress("1 KB/1 KB downloaded")
+        0 * progressLogger.progress(_)
+    }
+
+    def "adds operationtype information in progress output"() {
+        given:
+        def operation = new ResourceOperation(progressLogger, type, 1024 * 10)
+        when:
+        operation.logProcessedBytes(1024)
+        then:
+        1 * progressLogger.progress(message)
+        where:
+        type                            | message
+        ResourceOperation.Type.download | "1 KB/10 KB downloaded"
+        ResourceOperation.Type.upload   | "1 KB/10 KB uploaded"
+    }
+
+    void "completed completes progressLogger"() {
+        given:
+        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.upload, 1)
+        when:
+        operation.completed()
+        then:
+        1 * progressLogger.completed()
+    }
+
+    void "handles unknown content length"() {
+        given:
+        def operation = new ResourceOperation(progressLogger, ResourceOperation.Type.upload, 0)
+        when:
+        operation.logProcessedBytes(1024)
+        then:
+        1 * progressLogger.progress("1 KB/unknown size uploaded")
+    }
+}
+
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/ClosureBackedRuleActionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/ClosureBackedRuleActionTest.groovy
new file mode 100644
index 0000000..268f28f
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/ClosureBackedRuleActionTest.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules
+
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class ClosureBackedRuleActionTest extends Specification {
+
+    def "one arg closure called with subject and no inputs"() {
+        given:
+        def called = false
+        String thing = "1"
+        def closure = { String val ->
+            called = true
+            assert val.is(thing)
+            assert delegate.is(thing)
+        }
+
+        when:
+        action(closure).execute(thing, [])
+
+        then:
+        called
+    }
+
+    def "multiple arg closure called with correct subject and correct inputs"() {
+        given:
+        def called = false
+        String thing = "1"
+        def closure = { String subject, Integer other, String another ->
+            called = true
+            assert subject.is(thing)
+            assert delegate.is(thing)
+        }
+
+        when:
+        action(closure).execute(thing, [12, "another"])
+
+        then:
+        called
+    }
+
+    def "can construct with zero arg closure"() {
+        given:
+        def called = false
+        String thing = "1"
+        def closure = { ->
+            called = true
+            assert delegate.is(thing)
+        }
+
+        when:
+        action(closure).execute(thing, [])
+
+        then:
+        called
+    }
+
+    def "can construct with empty arg closure"() {
+        given:
+        def called = false
+        String thing = "1"
+        def closure = {
+            called = true
+            assert it.is(thing)
+            assert delegate.is(thing)
+        }
+
+        when:
+        action(closure).execute(thing, [])
+
+        then:
+        called
+    }
+
+    def "can construct with untyped single arg closure"() {
+        given:
+        def called = false
+        String thing = "1"
+        def closure = { subject ->
+            called = true
+            assert subject.is(thing)
+            assert delegate.is(thing)
+        }
+
+        when:
+        action(closure).execute(thing, [])
+
+        then:
+        called
+    }
+
+    def "fails to construct with incorrect subject type"() {
+        given:
+        def closure = { Integer val ->
+        }
+
+        when:
+        action(closure)
+
+        then:
+        def e = thrown RuleActionValidationException
+        e.message == "First parameter of rule action closure must be of type 'String'."
+    }
+
+    def "fails to construct with multiple arg closure with incorrect subject"() {
+        given:
+        def closure = { Integer val, String other ->
+        }
+
+        when:
+        action(closure)
+
+        then:
+        def e = thrown RuleActionValidationException
+        e.message == "First parameter of rule action closure must be of type 'String'."
+    }
+
+    def "equality"() {
+        given:
+        def c = {String foo -> }
+        def a1 = action(c)
+
+        expect:
+        Matchers.strictlyEquals(a1, action(c))
+        a1 == action(c)
+        a1 != action({String bar -> })
+        a1 != new ClosureBackedRuleAction(Integer.class, { Integer val -> })
+    }
+
+    RuleAction<String> action(Closure<?> c) {
+        new ClosureBackedRuleAction<String>(String.class, c)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionAdapterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionAdapterTest.groovy
new file mode 100644
index 0000000..37f78a8
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionAdapterTest.groovy
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules
+import org.gradle.api.Action
+import org.gradle.api.InvalidUserCodeException
+import spock.lang.Specification
+
+class DefaultRuleActionAdapterTest extends Specification {
+    def RuleActionValidator<String> noopValidator
+    def ruleActionAdapter
+
+    def setup() {
+        noopValidator = Stub(RuleActionValidator) {
+            validate(_) >> { RuleAction ruleAction -> ruleAction }
+        }
+    }
+
+    def "can adapt from closure" () {
+        ruleActionAdapter = new DefaultRuleActionAdapter<String>(noopValidator, "context")
+        def closureCalled = ""
+
+        when:
+        def ruleAction = ruleActionAdapter.createFromClosure(String, { String s -> closureCalled = s })
+        ruleAction.execute("string", [])
+
+        then:
+        ruleAction.inputTypes == []
+        closureCalled == "string"
+
+        when:
+        ruleAction = ruleActionAdapter.createFromClosure(String, { s -> closureCalled = s })
+        ruleAction.execute("object", [])
+
+        then:
+        ruleAction.inputTypes == []
+        closureCalled == "object"
+
+        when:
+        ruleAction = ruleActionAdapter.createFromClosure(String, { -> closureCalled = delegate })
+        ruleAction.execute("zero", [])
+
+        then:
+        ruleAction.inputTypes == []
+        closureCalled == "zero"
+
+        when:
+        ruleAction = ruleActionAdapter.createFromClosure(String, { closureCalled = it })
+        ruleAction.execute("it", [])
+
+        then:
+        ruleAction.inputTypes == []
+        closureCalled == "it"
+
+        when:
+        ruleAction = ruleActionAdapter.createFromClosure(String, { String s, String input1, Integer input2 -> closureCalled = input1 + input2 })
+        ruleAction.execute("", ["foo", 3])
+
+        then:
+        ruleAction.inputTypes == [String, Integer]
+        closureCalled == "foo3"
+    }
+
+    def "can adapt from action" () {
+        ruleActionAdapter = new DefaultRuleActionAdapter<String>(noopValidator, "context")
+        def actionCalled = false
+
+        when:
+        def ruleAction = ruleActionAdapter.createFromAction(Stub(Action) {
+            execute(_) >> { actionCalled = true }
+        })
+        ruleAction.execute("", [])
+
+        then:
+        actionCalled
+    }
+
+    def "fails to adapt closure with invalid subject" () {
+        ruleActionAdapter = new DefaultRuleActionAdapter<String>(noopValidator, "context")
+
+        when:
+        ruleActionAdapter.createFromClosure(String, {List subject -> })
+
+        then:
+        def failure = thrown(InvalidUserCodeException)
+        failure.message == "The closure provided is not valid as a rule for 'context'."
+        failure.cause instanceof RuleActionValidationException
+        failure.cause.message == "First parameter of rule action closure must be of type 'String'."
+    }
+
+    def "fails to adapt closure when validation fails" () {
+        def RuleActionValidator<String> ruleActionValidator = Stub(RuleActionValidator) {
+            validate(_) >> { RuleAction ruleAction -> throw new RuleActionValidationException("FAILED") }
+        }
+        ruleActionAdapter = new DefaultRuleActionAdapter<String>(ruleActionValidator, "context")
+
+        when:
+        ruleActionAdapter.createFromClosure(String, { String s -> })
+
+        then:
+        def failure = thrown(InvalidUserCodeException)
+        failure.message == "The closure provided is not valid as a rule for 'context'."
+        failure.cause instanceof RuleActionValidationException
+        failure.cause.message == "FAILED"
+    }
+
+    def "fails to adapt action when validation fails" () {
+        def RuleActionValidator<String> ruleActionValidator = Stub(RuleActionValidator) {
+            validate(_) >> { RuleAction ruleAction -> throw new RuleActionValidationException("FAILED") }
+        }
+        ruleActionAdapter = new DefaultRuleActionAdapter<String>(ruleActionValidator, "context")
+
+        when:
+        ruleActionAdapter.createFromAction(Stub(Action))
+
+        then:
+        def failure = thrown(InvalidUserCodeException)
+        failure.message == "The action provided is not valid as a rule for 'context'."
+        failure.cause instanceof RuleActionValidationException
+        failure.cause.message == "FAILED"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionValidatorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionValidatorTest.groovy
new file mode 100644
index 0000000..30374bb
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/DefaultRuleActionValidatorTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules
+
+import spock.lang.Specification
+
+class DefaultRuleActionValidatorTest extends Specification {
+
+    def "rejects invalid types" () {
+        when:
+        def ruleValidator = new DefaultRuleActionValidator<Object>([String, Integer])
+        ruleValidator.validate(Stub(RuleAction) {
+            getInputTypes() >> { [ String, Long ] }
+        })
+
+        then:
+        def failure = thrown(RuleActionValidationException)
+        failure.message == "Rule may not have an input parameter of type: java.lang.Long. Valid types (for the second and subsequent parameters) are: [java.lang.String, java.lang.Integer]."
+    }
+
+    def "rejects invalid type" () {
+        when:
+        def ruleValidator = new DefaultRuleActionValidator<Object>([Integer])
+        ruleValidator.validate(Stub(RuleAction) {
+            getInputTypes() >> { [ Long ] }
+        })
+
+        then:
+        def failure = thrown(RuleActionValidationException)
+        failure.message == "Rule may not have an input parameter of type: java.lang.Long. Second parameter must be of type: java.lang.Integer."
+    }
+
+    def "accepts valid types" () {
+        def ruleValidator = new DefaultRuleActionValidator<Object>([String, Integer])
+        def ruleAction = Stub(RuleAction) {
+            getInputTypes() >> { [ String, Integer ] }
+        }
+
+        expect:
+        ruleValidator.validate(ruleAction) == ruleAction
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/NoInputsRuleActionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/NoInputsRuleActionTest.groovy
new file mode 100644
index 0000000..bb5baa7
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/NoInputsRuleActionTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules
+import org.gradle.api.Action
+import org.gradle.util.Matchers
+import spock.lang.Specification
+
+class NoInputsRuleActionTest extends Specification {
+
+    def "creates rule action based on action"() {
+        given:
+        def called = false
+        String thing = "1"
+        def baseAction = { String val ->
+            called = true
+            assert val.is(thing)
+        } as Action<String>
+
+        when:
+        ruleAction(baseAction).execute(thing, [])
+
+        then:
+        called
+    }
+
+    def "rule action has no inputs"() {
+        when:
+        def action = ruleAction({ String val -> } as Action<String>)
+
+        then:
+        action.inputTypes.empty
+    }
+
+    def "equality"() {
+        def baseAction = {String val -> } as Action<String>
+        def noInputsAction = ruleAction(baseAction)
+
+        expect:
+        Matchers.strictlyEquals(noInputsAction, ruleAction(baseAction))
+        noInputsAction != ruleAction({String val -> } as Action<String>)
+    }
+
+    RuleAction<String> ruleAction(Action<String> action) {
+        new NoInputsRuleAction<String>(action)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/RuleSourceBackedRuleActionTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/RuleSourceBackedRuleActionTest.groovy
new file mode 100644
index 0000000..3e899e3
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/rules/RuleSourceBackedRuleActionTest.groovy
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.rules
+
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.Mutate
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+import java.util.concurrent.atomic.AtomicReference
+
+class RuleSourceBackedRuleActionTest extends Specification {
+    private ModelType<List<String>> listType = new ModelType<List<String>>() {}
+    private action
+    private List collector = []
+
+    def "creates rule action for rule source"() {
+        when:
+        action = RuleSourceBackedRuleAction.create(ModelType.of(List), ruleSource)
+
+        then:
+        action.inputTypes == [String, Integer, Set]
+
+        when:
+        action.execute(collector, ["foo", 1, ["bar", "baz"] as Set])
+
+        then:
+        collector == ["foo", 1, "bar", "baz"]
+
+        where:
+        ruleSource << [new ListRuleSource(), new ArrayListRuleSource()]
+    }
+
+    static class ListRuleSource {
+        @Mutate
+        void theRule(List subject, String input1, Integer input2, Set input3) {
+            subject.add(input1)
+            subject.add(input2)
+            subject.addAll(input3)
+        }
+    }
+
+    static class ArrayListRuleSource {
+        @Mutate
+        void theRule(ArrayList subject, String input1, Integer input2, Set input3) {
+            subject.add(input1)
+            subject.add(input2)
+            subject.addAll(input3)
+        }
+    }
+
+    def "creates rule action for rule source with typed params"() {
+        when:
+        action = RuleSourceBackedRuleAction.create(listType, new RuleSourceWithTypedParams())
+
+        then:
+        action.inputTypes == [AtomicReference, Map, Set]
+
+        when:
+        action.execute(collector, [new AtomicReference<String>("foo"), [2: "bar"], [4, 5] as Set])
+
+        then:
+        collector == ["foo", "2", "bar", "4", "5"]
+    }
+
+    static class RuleSourceWithTypedParams {
+        @Mutate
+        void theRule(List<String> subject, AtomicReference<String> input1, Map<Integer, String> input2, Set<Number> input3) {
+            subject.add(input1.get())
+            subject.addAll(input2.keySet().collect({ it.toString() }))
+            subject.addAll(input2.values())
+            subject.addAll(input3.collect({ it.toString() }))
+        }
+    }
+
+    def "fails to create rule action for invalid rule source"() {
+        when:
+        action = RuleSourceBackedRuleAction.create(listType, ruleSource)
+
+        then:
+        def e = thrown RuleActionValidationException
+        def cause = e.cause
+        cause instanceof InvalidModelRuleDeclarationException
+        cause.message.startsWith("Type ${ruleSource.class.name} is not a valid model rule source:")
+        def messageReasons = getReasons(cause.message)
+        messageReasons.size() == reasons.size()
+        messageReasons.sort() == reasons.sort()
+
+        where:
+        ruleSource                                | reasons
+        new RuleSourceWithNoMethod()              | [ "must have at exactly one method annotated with @org.gradle.model.Mutate" ]
+        new RuleSourceWithNoMutateMethod()        | [ "must have at exactly one method annotated with @org.gradle.model.Mutate" ]
+        new RuleSourceWithMultipleMutateMethods() | [ "more than one method is annotated with @org.gradle.model.Mutate" ]
+        new RuleSourceWithDifferentSubjectClass() | [ "first parameter of rule method 'theRule' must be of type java.util.List<java.lang.String>" ]
+        new RuleSourceWithDifferentSubjectType()  | [ "first parameter of rule method 'theRule' must be of type java.util.List<java.lang.String>" ]
+        new RuleSourceWithNoSubject()             | [ "first parameter of rule method 'theRule' must be of type java.util.List<java.lang.String>" ]
+        new RuleSourceWithReturnValue()           | [ "rule method 'theRule' must return void" ]
+        new RuleSourceWithMultipleIssues()        | [ "more than one method is annotated with @org.gradle.model.Mutate", "rule method 'theRule' must return void", "first parameter of rule method 'theRule' must be of type java.util.List<java.lang.String>", "first parameter of rule method 'anotherRule' must be of type java.util.List<java.lang.String>" ]
+    }
+
+    def getReasons(String message) {
+        String[] lines = message.split("\n")
+        def reasons = []
+        lines.each { line ->
+            if (line.startsWith("- ")) {
+                reasons.add(line.substring(2))
+            }
+        }
+        return reasons
+    }
+
+    static class RuleSourceWithNoMethod {}
+
+    static class RuleSourceWithNoMutateMethod {
+        void theRule(List<String> subject) {}
+    }
+
+    static class RuleSourceWithMultipleMutateMethods {
+        @Mutate
+        void theRule(List<String> subject) {}
+
+        @Mutate
+        void theOtherRule(List<String> subject) {}
+    }
+
+    static class RuleSourceWithDifferentSubjectClass {
+        @Mutate
+        void theRule(String subject) {}
+    }
+
+    static class RuleSourceWithDifferentSubjectType {
+        @Mutate
+        void theRule(List<Integer> subject) {}
+    }
+
+    static class RuleSourceWithReturnValue {
+        @Mutate
+        String theRule(List<String> subject) {}
+    }
+
+    static class RuleSourceWithNoSubject {
+        @Mutate
+        void theRule() {}
+    }
+
+    static class RuleSourceWithMultipleIssues {
+        @Mutate
+        String theRule(List<Integer> subject) {}
+
+        @Mutate
+        void anotherRule() {}
+    }
+}
diff --git a/subprojects/dependency-management/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml b/subprojects/dependency-management/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
new file mode 100644
index 0000000..8d6e2ff
--- /dev/null
+++ b/subprojects/dependency-management/src/test/resources/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/test-full.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+   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="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
+	<info organisation="myorg"
+	       module="mymodule"
+	       revision="myrev"
+	       status="integration"
+           branch="branch"
+	       publication="20041101110000"
+	       e:attr1="value1">
+	       
+		<license name="MyLicense" url="http://www.my.org/mymodule/mylicense.html"/>
+		<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.
+		</description>
+
+		<e:someExtra>56576</e:someExtra>
+	</info>
+	<configurations>
+		<conf name="myconf1" description="desc 1" e:attr2="value2"/>
+		<conf name="myconf2" description="desc 2" visibility="public"/>
+		<conf name="myconf3" description="desc 3" visibility="private"/>
+		<conf name="myconf4" description="desc 4" extends="myconf1, myconf2"/>		
+		<conf name="myoldconf" description="my old desc" deprecated="20050115"/>
+	</configurations>
+	<publications>
+		<artifact name="myartifact1" type="jar" e:attr3="value3"/>
+		<artifact name="myartifact2" type="jar" conf="myconf1"/>
+		<artifact name="myartifact3" type="jar" conf="myconf1, myconf2, myconf3"/>
+		<artifact name="myartifact4" type="jar">
+			<conf name="myconf1"/>
+			<conf name="myconf3"/>
+		</artifact>
+	</publications>
+	<dependencies>
+		<dependency name="mymodule2" rev="2.0" e:attr4="value4"/>
+		<dependency name="mymodule3" rev="2.0" changing="true" transitive="false"/>
+		<dependency org="yourorg" name="yourmodule1" branch="trunk" rev="1.1" branchConstraint="branch1" revConstraint="1+" conf="myconf1"/>
+		<dependency org="yourorg" name="yourmodule2" rev="2+" conf="myconf1->yourconf1"/>
+		<dependency org="yourorg" name="yourmodule3" rev="3.1" conf="myconf1->yourconf1, yourconf2"/>
+		<dependency org="yourorg" name="yourmodule4" rev="4.1" conf="myconf1, myconf2->yourconf1, yourconf2"/>
+		<dependency org="yourorg" name="yourmodule5" rev="5.1" conf="myconf1->yourconf1;myconf2->yourconf1, yourconf2"/>
+
+		<dependency org="yourorg" name="yourmodule6" rev="latest.integration">
+			<conf name="myconf1" mapped="yourconf1"/>
+			<conf name="myconf2" mapped="yourconf1, yourconf2"/>
+		</dependency>
+
+		<dependency org="yourorg" name="yourmodule7" rev="7.1">
+			<conf name="myconf1">
+				<mapped name="yourconf1"/>
+			</conf>
+			<conf name="myconf2">
+				<mapped name="yourconf1"/>
+				<mapped name="yourconf2"/>
+			</conf>
+		</dependency>
+
+		<dependency org="yourorg" name="yourmodule8" rev="8.1">
+			<artifact name="yourartifact8-1" type="jar" e:attr5="value5"/>
+			<artifact name="yourartifact8-2" type="jar"/>
+		</dependency>		
+
+		<dependency org="yourorg" name="yourmodule9" rev="9.1" conf="myconf1,myconf2,myconf3->default">
+			<artifact name="yourartifact9-1" type="jar" conf="myconf1,myconf2"/>
+			<artifact name="yourartifact9-2" type="jar">
+				<conf name="myconf2"/>
+				<conf name="myconf3"/>
+			</artifact>
+		</dependency>		
+
+		<dependency org="yourorg" name="yourmodule10" rev="10.1">
+			<include name="your.*" type="jar"/>
+			<include ext="xml"/>
+			<exclude name="toexclude"/>
+		</dependency>
+		<dependency org="yourorg" name="yourmodule11" rev="11.1" conf="*->@"/>
+		
+		<exclude module="*servlet*" matcher="glob" conf="myconf1" /> 
+		<exclude org="acme" module="test" artifact="test" type="source" ext="jar" />
+        <override org="yourorg" module=".*1" matcher="regexp" branch="BRANCH" rev="1.0" /> 
+		<conflict org="yourorg" module=".*" matcher="regexp" manager="all" />
+		<conflict org="theirorg" module="theirmodule1" rev="1.0, 1.1"/>
+	</dependencies>
+</ivy-module>
diff --git a/subprojects/dependency-management/src/test/resources/org/gradle/internal/resource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy b/subprojects/dependency-management/src/test/resources/org/gradle/internal/resource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
new file mode 100644
index 0000000..3c2c612
--- /dev/null
+++ b/subprojects/dependency-management/src/test/resources/org/gradle/internal/resource/ivy/ArtifactAtRepositoryCachedArtifactIndexTest.groovy
@@ -0,0 +1,130 @@
+/*
+ * 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.resource.ivy
+
+import org.gradle.api.internal.artifacts.ivyservice.CacheLockingManager
+import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
+import org.gradle.internal.resource.cached.CachedArtifact
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryCachedArtifactIndex
+import org.gradle.internal.resource.cached.ivy.ArtifactAtRepositoryKey
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData
+import org.gradle.cache.PersistentIndexedCache
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.BuildCommencedTimeProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class ArtifactAtRepositoryCachedArtifactIndexTest extends Specification {
+
+    CacheLockingManager cacheLockingManager = Mock()
+    BuildCommencedTimeProvider timeProvider = Mock()
+    ArtifactAtRepositoryKey key = Mock()
+    ExternalResourceMetaData metaData = Mock()
+    @Rule TestNameTestDirectoryProvider folder = new TestNameTestDirectoryProvider();
+
+    PersistentIndexedCache persistentIndexedCache = Mock()
+
+    ArtifactAtRepositoryCachedArtifactIndex index
+    String persistentCacheFile
+    CachedArtifact cachedArtifact = Mock()
+
+
+    def setup() {
+        persistentCacheFile = "cacheFile"
+        index = new ArtifactAtRepositoryCachedArtifactIndex(persistentCacheFile, timeProvider, cacheLockingManager)
+    }
+
+    def "storing null artifactFile not supported"() {
+        when:
+        index.store(key, null, 0)
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "artifactFile cannot be null"
+    }
+
+    def "artifact key must be provided"() {
+        when:
+        index.store(null, Mock(File), 0)
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "key cannot be null"
+    }
+
+    def "stored artifact is put into persistentIndexedCache"() {
+        setup:
+        1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
+        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ModuleComponentArtifactIdentifier));
+        def testFile = folder.createFile("aTestFile");
+        when:
+        index.store(key, testFile, BigInteger.TEN)
+
+        then:
+        1 * cacheLockingManager.useCache("store into artifact resolution cache \'cacheFile\'", _) >> {descr, action -> action.run()}
+        1 * timeProvider.currentTime >> 123
+        1 * persistentIndexedCache.put(key, _) >> { k, v ->
+            assert v.cachedAt == 123
+            assert v.descriptorHash == BigInteger.TEN
+            assert v.cachedFile == testFile
+        }
+    }
+
+    def "lookup key must not be null"() {
+        when:
+        index.lookup(null)
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "key cannot be null"
+    }
+
+    def "loads missing CachedArtifact from persistentIndexedCache"() {
+        setup:
+        def key = createEntryInPersistentCache()
+        _ * cachedArtifact.missing >> true
+
+        when:
+        def fromCache = index.lookup(key)
+
+        then:
+        fromCache == cachedArtifact
+        fromCache.isMissing()
+    }
+
+    def "loads CachedArtifact with file ref from persistentIndexedCache"() {
+        setup:
+        def key = createEntryInPersistentCache()
+        _ * cachedArtifact.missing >> false
+        File cachedFile = Mock()
+        cachedFile.exists() >> true;
+        cachedArtifact.getCachedFile() >> cachedFile
+        when:
+        def fromCache = index.lookup(key)
+
+        then:
+        fromCache == cachedArtifact
+        !fromCache.isMissing()
+        fromCache.cachedFile == cachedArtifact.cachedFile
+    }
+
+    def createEntryInPersistentCache() {
+        1 * cacheLockingManager.createCache(persistentCacheFile, _, _) >> persistentIndexedCache
+        1 * cacheLockingManager.useCache("lookup from artifact resolution cache \'cacheFile\'", _) >> {descr, factory -> factory.create()}
+        def key = new ArtifactAtRepositoryKey("RepoID", Stub(ModuleComponentArtifactIdentifier));
+        1 * persistentIndexedCache.get(key) >> cachedArtifact;
+        key
+    }
+}
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
new file mode 100644
index 0000000..817da19
--- /dev/null
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
@@ -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.artifacts.result
+
+import org.gradle.api.artifacts.component.ComponentSelector
+import org.gradle.api.artifacts.result.ComponentSelectionReason
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.resolve.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
+
+class ResolutionResultDataBuilder {
+
+    static DefaultResolvedDependencyResult newDependency(String group='a', String module='a', String version='1', String selectedVersion='1') {
+        new DefaultResolvedDependencyResult(DefaultModuleComponentSelector.newSelector(group, module, version), newModule(group, module, selectedVersion), newModule())
+    }
+
+    static DefaultUnresolvedDependencyResult newUnresolvedDependency(String group='x', String module='x', String version='1', String selectedVersion='1') {
+        def requested = DefaultModuleComponentSelector.newSelector(group, module, version)
+        new DefaultUnresolvedDependencyResult(requested, VersionSelectionReasons.REQUESTED, newModule(group, module, selectedVersion), new ModuleVersionResolveException(newSelector(group, module, version), "broken"))
+    }
+
+    static DefaultResolvedComponentResult newModule(String group='a', String module='a', String version='1',
+                                                        ComponentSelectionReason selectionReason = VersionSelectionReasons.REQUESTED) {
+        new DefaultResolvedComponentResult(newId(group, module, version), selectionReason, new DefaultModuleComponentIdentifier(group, module, version))
+    }
+
+    static DefaultResolvedDependencyResult newDependency(ComponentSelector componentSelector, String group='a', String module='a', String selectedVersion='1') {
+        new DefaultResolvedDependencyResult(componentSelector, newModule(group, module, selectedVersion), newModule())
+    }
+}
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyRemoteRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyRemoteRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..22feca3
--- /dev/null
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/ivy/AbstractIvyRemoteRepoResolveIntegrationTest.groovy
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.junit.Rule
+import spock.lang.Unroll
+
+ at Unroll
+abstract class AbstractIvyRemoteRepoResolveIntegrationTest extends AbstractIntegrationSpec {
+
+    abstract RepositoryServer getServer()
+
+    def setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    @Rule ProgressLoggingFixture progressLogger = new ProgressLoggingFixture(executer, temporaryFolder)
+
+    void "can resolve dependencies from a remote Ivy repository with #layout layout"() {
+        given:
+        def remoteIvyRepo = server.getRemoteIvyRepo(m2Compatible, null, ivyFilePattern, artifactFilePattern)
+        def module = remoteIvyRepo.module('org.group.name', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${remoteIvyRepo.uri}"
+                    $server.validCredentials
+                    layout '$layout'
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        module.ivy.expectDownload()
+        module.jar.expectDownload()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants 'projectA-1.2.jar'
+
+        where:
+        layout     | m2Compatible | ivyFilePattern             | artifactFilePattern
+        'gradle'   | false        | 'ivy-[revision].xml'       | '[artifact]-[revision](.[ext])'
+        'maven'    | true         | 'ivy-[revision].xml'       | '[artifact]-[revision](.[ext])'
+        'ivy'      | false        | '[type]s/[artifact].[ext]' | '[type]s/[artifact].[ext]'
+    }
+
+    void "can resolve dependencies from a remote Ivy repository with pattern layout and m2compatible: #m2Compatible"() {
+        given:
+        def remoteIvyRepo = server.getRemoteIvyRepo(m2Compatible, "[module]/[organisation]/[revision]")
+        def module = remoteIvyRepo.module('org.group.name', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${remoteIvyRepo.uri}"
+                    $server.validCredentials
+                    layout "pattern", {
+                        artifact "${remoteIvyRepo.baseArtifactPattern}"
+                        ivy "${remoteIvyRepo.baseIvyPattern}"
+                        m2compatible = $m2Compatible
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        module.ivy.expectDownload()
+        module.jar.expectDownload()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants 'projectA-1.2.jar'
+
+        where:
+        m2Compatible << [false, true]
+    }
+
+    void "can resolve dependencies from a remote Ivy repository with multiple patterns configured"() {
+        given:
+        def emptyRepo = server.getRemoteIvyRepo('/empty')
+        def thirdPartyModuleInEmptyRepo = emptyRepo.module('other', '3rdParty', '1.2')
+        def companyModuleInEmptyRepo = emptyRepo.module('company', 'original', '1.1')
+
+        def thirdPartyIvyRepo = server.getRemoteIvyRepo(false, "third-party/[organisation]/[module]/[revision]")
+        def thirdPartyModule = thirdPartyIvyRepo.module('other', '3rdParty', '1.2')
+        thirdPartyModule.publish()
+        def companyModuleInThirdPartyRepo = thirdPartyIvyRepo.module('company', 'original', '1.1')
+
+        and:
+        def companyIvyRepo = server.getRemoteIvyRepo(false, "company/[module]/[revision]")
+        def companyModule = companyIvyRepo.module('company', 'original', '1.1')
+        companyModule.publish()
+
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    $server.validCredentials
+                    url "${emptyRepo.uri}"
+                    artifactPattern "${thirdPartyIvyRepo.artifactPattern}"
+                    artifactPattern "${companyIvyRepo.artifactPattern}"
+                    ivyPattern "${thirdPartyIvyRepo.ivyPattern}"
+                    ivyPattern "${companyIvyRepo.ivyPattern}"
+                }
+            }
+            configurations { compile }
+            dependencies {
+                compile 'other:3rdParty:1.2', 'company:original:1.1'
+            }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+        println buildFile.text
+
+        when:
+        thirdPartyModuleInEmptyRepo.ivy.expectDownloadMissing()
+        thirdPartyModuleInEmptyRepo.jar.expectDownloadMissing()
+        thirdPartyModule.ivy.expectDownload()
+        thirdPartyModule.jar.expectDownload()
+
+        companyModuleInEmptyRepo.ivy.expectDownloadMissing()
+        companyModuleInEmptyRepo.jar.expectDownloadMissing()
+        companyModuleInThirdPartyRepo.ivy.expectDownloadMissing()
+        companyModuleInThirdPartyRepo.jar.expectDownloadMissing()
+        companyModule.ivy.expectDownload()
+        companyModule.jar.expectDownload()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants '3rdParty-1.2.jar', 'original-1.1.jar'
+    }
+
+    public void "can resolve and cache dependencies from multiple remote Ivy repositories"() {
+        given:
+        def repo1 = server.getRemoteIvyRepo("/repo1")
+        def repo2 = server.getRemoteIvyRepo("/repo2")
+        def moduleA = repo1.module('group', 'projectA')
+        moduleA.publish()
+        def missingModuleB = repo1.module('group', 'projectB')
+        def moduleB = repo2.module('group', 'projectB')
+        moduleB.publish()
+        def brokenModuleC = repo1.module('group', 'projectC')
+        def moduleC = repo2.module('group', 'projectC')
+        moduleC.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${repo1.uri}"
+                    $server.validCredentials
+                }
+                ivy {
+                    url "${repo2.uri}"
+                    $server.validCredentials
+                }
+            }
+            configurations {
+                compile {
+                    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+                }
+            }
+            dependencies {
+                compile 'group:projectA:1.0', 'group:projectB:1.0', 'group:projectC:1.0'
+            }
+            task listJars << {
+                assert configurations.compile.collect { it.name } == ['projectA-1.0.jar', 'projectB-1.0.jar', 'projectC-1.0.jar']
+            }
+        """
+
+        when:
+        moduleA.ivy.expectDownload()
+        moduleA.jar.expectDownload()
+
+        // Handles missing in repo1
+        missingModuleB.ivy.expectDownloadMissing()
+        missingModuleB.jar.expectMetadataRetrieveMissing()
+
+        moduleB.ivy.expectDownload()
+        moduleB.jar.expectDownload()
+
+        // Handles from broken url in repo1 (but does not cache)
+        brokenModuleC.ivy.expectDownloadBroken()
+
+        moduleC.ivy.expectDownload()
+        moduleC.jar.expectDownload()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+        // Will always re-attempt a broken repository
+        brokenModuleC.ivy.expectMetadataRetrieveBroken()
+        // No extra calls for cached dependencies
+
+        then:
+        succeeds('listJars')
+    }
+
+    public void "can resolve and cache dependencies from a remote Ivy repository"() {
+        given:
+        def module = server.remoteIvyRepo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${server.remoteIvyRepo.uri}"
+                    $server.validCredentials
+                }
+            }
+            configurations {
+                compile {
+                    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+                }
+            }
+            dependencies { compile 'group:projectA:1.2' }
+            task listJars << {
+                assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+            }
+        """
+        when:
+        module.ivy.expectDownload()
+        module.jar.expectDownload()
+
+        then:
+        succeeds 'listJars'
+        progressLogger.downloadProgressLogged(module.ivy.uri)
+        progressLogger.downloadProgressLogged(module.jar.uri)
+
+        when:
+        server.resetExpectations()
+
+        then:
+        succeeds 'listJars'
+    }
+
+    public void "can resolve and cache artifact-only dependencies from a remote Ivy repository"() {
+        given:
+        def module = server.remoteIvyRepo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${server.remoteIvyRepo.uri}"
+                    $server.validCredentials
+                }
+            }
+            configurations {
+                compile {
+                    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+                }
+            }
+            dependencies { compile 'group:projectA:1.2 at jar' }
+            task listJars << {
+                assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+            }
+        """
+
+
+        when:
+        module.ivy.expectDownload()
+        module.jar.expectDownload()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+        // No extra calls for cached dependencies
+
+        then:
+        succeeds('listJars')
+    }
+
+    def "can resolve and cache artifact-only dependencies with no descriptor from a remote Ivy repository"() {
+        given:
+        def module = server.remoteIvyRepo.module('group', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${server.remoteIvyRepo.uri}"
+                    $server.validCredentials
+                }
+            }
+            configurations {
+                compile {
+                    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+                }
+            }
+            dependencies { compile 'group:projectA:1.2 at jar' }
+            task listJars << {
+                assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+            }
+        """
+
+
+        when:
+        module.ivy.expectDownloadMissing()
+        module.jar.expectMetadataRetrieve()
+        module.jar.expectDownload()
+
+        then:
+        succeeds('listJars')
+
+        when:
+        server.resetExpectations()
+        // No extra calls for cached dependencies
+
+        then:
+        succeeds('listJars')
+    }
+
+    def "reuses cached details when switching ivy resolve mode"() {
+        given:
+        buildFile << """
+            configurations {
+                compile {
+                    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+                }
+            }
+            dependencies {
+                repositories {
+                    ivy {
+                        url "${server.remoteIvyRepo.uri}"
+                        $server.validCredentials
+                        resolve.dynamicMode = project.hasProperty('useDynamicResolve')
+                    }
+                }
+                compile 'org:projectA:1.2'
+            }
+            task retrieve(type: Sync) {
+              from configurations.compile
+              into 'libs'
+            }
+        """
+        def moduleA = server.remoteIvyRepo.module('org', 'projectA', '1.2')
+        moduleA.dependsOn(organisation: 'org', module: 'projectB', revision: '1.5', revConstraint: 'latest.integration')
+                .publish()
+
+        def moduleB15 = server.remoteIvyRepo.module('org', 'projectB', '1.5')
+        moduleB15.publish()
+
+        def moduleB16 = server.remoteIvyRepo.module('org', 'projectB', '1.6')
+        moduleB16.publish()
+
+        when:
+        moduleA.ivy.expectDownload()
+        moduleA.jar.expectDownload()
+        moduleB15.ivy.expectDownload()
+        moduleB15.jar.expectDownload()
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar')
+
+        when:
+        server.resetExpectations()
+        server.remoteIvyRepo.directoryList('org', 'projectB').expectDownload()
+        moduleB16.ivy.expectDownload()
+        moduleB16.jar.expectDownload()
+        executer.withArguments("-PuseDynamicResolve=true")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar')
+
+        when:
+        server.resetExpectations()
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.5.jar')
+
+        when:
+        server.resetExpectations()
+        executer.withArguments("-PuseDynamicResolve=true")
+        run 'retrieve'
+
+        then:
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-1.6.jar')
+    }
+}
diff --git a/subprojects/diagnostics/diagnostics.gradle b/subprojects/diagnostics/diagnostics.gradle
index 875e578..554c813 100644
--- a/subprojects/diagnostics/diagnostics.gradle
+++ b/subprojects/diagnostics/diagnostics.gradle
@@ -18,9 +18,14 @@ dependencies {
     compile libraries.groovy
     compile project(':core')
     compile project(':reporting')
-    compile project(':plugins')
-    compile project(':coreImpl')
+    compile project(':platformBase')
+    compile project(':dependencyManagement')
+
+    integTestRuntime project(':plugins')
+    integTestRuntime project(':platformNative')
+    integTestRuntime project(':languageNative')
 }
 
 useTestFixtures()
-useTestFixtures(project: ':coreImpl')
\ No newline at end of file
+useTestFixtures(project: ':dependencyManagement')
+useTestFixtures(project: ':platformNative', sourceSet: 'testFixtures')
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/HelpTasksPluginIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/HelpTasksPluginIntegrationTest.groovy
new file mode 100644
index 0000000..03ef77f
--- /dev/null
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/HelpTasksPluginIntegrationTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import spock.lang.Unroll
+
+class HelpTasksPluginIntegrationTest extends WellBehavedPluginTest {
+
+    @Override
+    String getMainTask() {
+        "tasks"
+    }
+
+    @Unroll
+    def "can fetch tasks during configuration - #task"() {
+        when:
+        buildScript """
+            assert tasks.names.contains("$task")
+            tasks.findByName "$task"
+        """
+
+        then:
+        succeeds "tasks"
+
+        where:
+        task << ["help", "projects", "tasks", "properties", "dependencyInsight", "dependencies", "components", "model"]
+    }
+}
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/components/ComponentReportIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/components/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..b182da1
--- /dev/null
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/components/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components
+
+import org.gradle.api.JavaVersion
+import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private JavaVersion currentJvm = JavaVersion.current()
+    private String currentJava = "java" + currentJvm.majorVersion
+    private String currentJdk = String.format("JDK %s (%s)", currentJvm.majorVersion, currentJvm);
+    private String currentNative = NativePlatformsTestFixture.defaultPlatformName
+
+    def "informs the user when project has no components defined"() {
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+No components defined for this project.
+"""
+    }
+
+    def "shows details of multiple components"() {
+        given:
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'java-lang'
+    id 'cpp'
+    id 'c'
+}
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        jvmLib(JvmLibrarySpec) {
+            targetPlatform "$currentJava"
+        }
+        nativeLib(NativeLibrarySpec)
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+JVM library 'jvmLib'
+--------------------
+
+Source sets
+    Java source 'jvmLib:java'
+        src/jvmLib/java
+    JVM resources 'jvmLib:resources'
+        src/jvmLib/resources
+
+Binaries
+    Jar 'jvmLibJar'
+        build using task: :jvmLibJar
+        platform: ${currentJava}
+        tool chain: $currentJdk
+        Jar file: build/jars/jvmLibJar/jvmLib.jar
+
+Native library 'nativeLib'
+--------------------------
+
+Source sets
+    C source 'nativeLib:c'
+        src/nativeLib/c
+    C++ source 'nativeLib:cpp'
+        src/nativeLib/cpp
+
+Binaries
+    Shared library 'nativeLib:sharedLibrary'
+        build using task: :nativeLibSharedLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/nativeLibSharedLibrary/libnativeLib.dylib
+    Static library 'nativeLib:staticLibrary'
+        build using task: :nativeLibStaticLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/nativeLibStaticLibrary/libnativeLib.a
+"""
+    }
+
+    def "shows an error when targeting a native platform from a jvm component"() {
+        given:
+        buildFile << """
+    apply plugin: 'jvm-component'
+    apply plugin: 'native-component'
+    apply plugin: 'java-lang'
+
+    model {
+        platforms {
+            i386 { architecture 'i386' }
+        }
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "i386"
+            }
+        }
+    }
+"""
+        when:
+        fails "components"
+
+        then:
+        failure.assertHasCause("Invalid JavaPlatform: i386")
+    }
+
+    def "shows an error when targeting a jvm platform from a native component"() {
+        given:
+        buildFile << """
+    apply plugin: 'jvm-component'
+    apply plugin: 'native-component'
+    apply plugin: 'java-lang'
+
+    model {
+        components {
+            myLib(NativeLibrarySpec) {
+                targetPlatform "java8"
+            }
+        }
+    }
+"""
+        when:
+        fails "components"
+
+        then:
+        failure.assertHasCause("Invalid NativePlatform: java8")
+    }
+}
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
index d4d4ca0..23d784b 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.api.reporting.dependencies
 import groovy.json.JsonSlurper
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
+import org.jsoup.Jsoup
 import spock.lang.Issue
 
 /**
@@ -208,11 +209,6 @@ class HtmlDependencyReportTaskIntegrationTest extends AbstractIntegrationSpec {
                 projects = [project(':a'), project(':b')]
             }
         """
-        ["a", "b"].each { module ->
-            file(module, "build.gradle") << """
-                apply plugin: 'project-report'
-            """
-        }
 
         when:
         run "htmlDependencyReport"
@@ -235,21 +231,20 @@ class HtmlDependencyReportTaskIntegrationTest extends AbstractIntegrationSpec {
         run "htmlDependencyReport"
 
         then:
-        file("build/reports/project/dependencies/d.gif").assertExists()
-        file("build/reports/project/dependencies/d.png").assertExists()
-        file("build/reports/project/dependencies/jquery.jstree.js").assertExists()
-        file("build/reports/project/dependencies/jquery-1.10.1.min.js").assertExists()
-        file("build/reports/project/dependencies/script.js").assertExists()
-        file("build/reports/project/dependencies/style.css").assertExists()
-        file("build/reports/project/dependencies/throbber.gif").assertExists()
-        file("build/reports/project/dependencies/tree.css").assertExists()
-
-        file("build/reports/project/dependencies/root.html").getText().contains('<script src="root.js" charset="utf-8"></script>');
+        file("build/reports/project/dependencies/images/d.gif").assertExists()
+        file("build/reports/project/dependencies/images/d.png").assertExists()
+        file("build/reports/project/dependencies/js/jquery.jstree.js").assertExists()
+        file("build/reports/project/dependencies/js/jquery.min-1.11.0.js").assertExists()
+        file("build/reports/project/dependencies/js/script.js").assertExists()
+        file("build/reports/project/dependencies/css/style.css").assertExists()
+        file("build/reports/project/dependencies/images/throbber.gif").assertExists()
+        file("build/reports/project/dependencies/css/tree.css").assertExists()
+
+        file("build/reports/project/dependencies/root.html").getText().contains('<script src="root.js" charset="utf-8">');
         file("build/reports/project/dependencies/root.js").assertExists();
-        file("build/reports/project/dependencies/index.html").getText().contains('<script src="index.js" charset="utf-8"></script>');
     }
 
-    def "generates index.js file"() {
+    def "generates index.html file"() {
         given:
         file("settings.gradle") << """
             rootProject.name = 'fooProject'
@@ -264,35 +259,26 @@ class HtmlDependencyReportTaskIntegrationTest extends AbstractIntegrationSpec {
                 projects = project.allprojects
             }
         """
-        ["a", "b"].each { module ->
-            file(module, "build.gradle") << """
-                apply plugin: 'project-report'
-            """
-        }
 
         when:
         run "htmlDependencyReport"
-        def json = readGeneratedJson("index")
+        def html = readGeneratedHtml("index")
 
         then:
-        json.generationDate != null
-        json.gradleVersion != null
+        def rows = html.select("table tbody tr")
+        rows.size() == 3
+
+        def rootProject = rows[0].select("td")
+        rootProject[0].text() == "root project 'fooProject'"
+        rootProject[1].text() == "dummy description"
 
-        json.projects.size() == 3
-        json.projects[0].path == 'root:'
-        json.projects[0].name == 'fooProject'
-        json.projects[0].description == 'dummy description'
-        json.projects[0].file == 'root.html'
-
-        json.projects[1].path == 'root:a'
-        json.projects[1].name == 'a'
-        json.projects[1].description == null
-        json.projects[1].file == 'root.a.html'
-
-        json.projects[2].path == 'root:b'
-        json.projects[2].name == 'b'
-        json.projects[2].description == null
-        json.projects[2].file == 'root.b.html'
+        def projectA = rows[1].select("td")
+        projectA[0].text() == "project ':a'"
+        projectA[1].text() == ""
+
+        def projectB = rows[2].select("td")
+        projectB[0].text() == "project ':b'"
+        projectB[1].text() == ""
     }
 
     def "renders insights graphs"() {
@@ -526,4 +512,9 @@ rootProject.name = 'root'
         JsonSlurper json = new JsonSlurper()
         return json.parseText(jsonAsString)
     }
+
+    private def readGeneratedHtml(fileNameWithoutExtension) {
+        TestFile htmlReport = file("build/reports/project/dependencies/" + fileNameWithoutExtension + ".html")
+        return Jsoup.parse(htmlReport, "utf-8")
+    }
 }
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/model/ModelReportIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/model/ModelReportIntegrationTest.groovy
new file mode 100644
index 0000000..62cd7fd
--- /dev/null
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/model/ModelReportIntegrationTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.model
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class ModelReportIntegrationTest extends AbstractIntegrationSpec {
+    def "displays basic structure of an empty project"() {
+        when:
+        run "model"
+
+        then:
+        // just check that it doesn't blow up for now
+        output.contains("tasks")
+    }
+
+    def "displays basic structure of a polyglot project"() {
+        given:
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'java-lang'
+    id 'cpp'
+    id 'c'
+}
+
+model {
+    components {
+        jvmLib(JvmLibrarySpec)
+        nativeLib(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        run "model"
+
+        then:
+        // just check that it doesn't blow up for now
+        output.contains("components")
+        output.contains("tasks")
+    }
+}
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
index 6624d9b..0c12187 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
@@ -909,7 +909,8 @@ org:leaf2:1.0
         output.contains(toPlatformLineSeparators("""
 project :
 \\--- project :impl
-     \\--- project : (*)"""))
+     \\--- project : (*)
+"""))
     }
 
     def "selects a module component dependency with a given name"() {
@@ -1085,4 +1086,52 @@ project :api
      \\--- compile
 """))
     }
+
+    def "renders tree with a mix of project and external dependencies"() {
+        given:
+        mavenRepo.module("org", "leaf1").dependsOn("leaf2").publish()
+        mavenRepo.module("org", "leaf2").dependsOn("leaf3").publish()
+        mavenRepo.module("org", "leaf3").publish()
+
+        file("settings.gradle") << "include 'api', 'impl'; rootProject.name='root'"
+
+        file("build.gradle") << """
+            allprojects {
+                apply plugin: 'java'
+                group = 'org.foo'
+                version = '1.0-SNAPSHOT'
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+            }
+            dependencies {
+                compile project(':impl')
+            }
+            project(':api') {
+                dependencies {
+                    compile 'org:leaf2:1.0'
+                }
+            }
+            project(':impl') {
+                dependencies {
+                    compile project(':api')
+                    compile 'org:leaf1:1.0'
+                }
+            }
+        """
+
+        when:
+        run "dependencyInsight", "--dependency", "leaf3"
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+org:leaf3:1.0
+\\--- org:leaf2:1.0
+     +--- project :api
+     |    \\--- project :impl
+     |         \\--- compile
+     \\--- org:leaf1:1.0
+          \\--- project :impl (*)
+"""))
+    }
 }
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
index 48d38c4..8ee0e90 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
@@ -28,34 +28,39 @@ class DependencyReportTaskIntegrationTest extends AbstractIntegrationSpec {
         given:
         file("settings.gradle") << "include 'client', 'a', 'b', 'c'"
 
-        [a: "b", b: "c", c: "a"].each { module, dep ->
-            def upped = module.toUpperCase()
-            file(module, "build.gradle") << """
-                apply plugin: 'java'
-                group = "group"
-                version = 1.0
-
-                dependencies {
-                    compile project(":$dep")
-                }
-            """
-            file(module, "src", "main", "java", "${upped}.java") << "public class $upped {}"
-        }
+        buildFile << """
+allprojects {
+    configurations { compile; "default" { extendsFrom compile } }
+    group = "group"
+    version = 1.0
+}
+
+project(":a") {
+    dependencies { compile project(":b") }
+    dependencies { compile project(":c") }
+}
+
+project(":b") {
+    dependencies { compile project(":c") }
+}
+
+project(":c") {
+    dependencies { compile project(":a") }
+}
+"""
 
-        and:
-        file("client", "build.gradle") << """
-            apply plugin: 'java'
-            
-            dependencies {
-                compile project(":a")
-            }
-        """
-        
         when:
-        run ":client:dependencies"
-        
+        run ":c:dependencies"
+
         then:
-        output.contains "(*) - dependencies omitted (listed previously)"
+        output.contains(toPlatformLineSeparators("""
+compile
+\\--- project :a
+     +--- project :b
+     |    \\--- project :c (*)
+     \\--- project :c (*)
+"""))
+        output.contains '(*) - dependencies omitted (listed previously)'
     }
 
     def "marks modules that can't be resolved as 'FAILED'"() {
@@ -325,6 +330,7 @@ rootProject.name = 'root'
 
         then:
         output.contains(toPlatformLineSeparators("""
+conf
 \\--- org:toplevel:1.0
      +--- org:middle1:1.0
      |    +--- org:leaf1:1.0
@@ -366,6 +372,7 @@ rootProject.name = 'root'
 
         then:
         output.contains(toPlatformLineSeparators("""
+conf
 +--- bar:bar:5.0 -> 6.0
 |    \\--- foo:foo:3.0
 +--- bar:bar:6.0 (*)
@@ -402,6 +409,7 @@ rootProject.name = 'root'
 
         then:
         output.contains(toPlatformLineSeparators("""
+conf
 +--- foo:foo:1+ -> 2.5
 |    \\--- foo:bar:2.0
 \\--- foo:foo:2+ -> 2.5 (*)
@@ -436,7 +444,11 @@ rootProject.name = 'root'
         run ":dependencies"
 
         then:
-        output.contains "org:child:1.0"
+        output.contains(toPlatformLineSeparators("""
+conf
+\\--- org:parent:1.0
+     \\--- org:child:1.0
+"""))
     }
 
     def "renders the ivy tree with conflicts"() {
@@ -471,6 +483,7 @@ rootProject.name = 'root'
 
         then:
         output.contains(toPlatformLineSeparators("""
+conf
 +--- org:toplevel:1.0
 |    +--- org:middle1:1.0
 |    |    +--- org:leaf1:1.0
@@ -481,6 +494,7 @@ rootProject.name = 'root'
 \\--- org:leaf4:2.0
 """))
     }
+
     def "tells if there are no dependencies"() {
         given:
         buildFile << "configurations { foo }"
@@ -489,7 +503,10 @@ rootProject.name = 'root'
         run "dependencies"
 
         then:
-        output.contains "No dependencies"
+        output.contains(toPlatformLineSeparators("""
+foo
+No dependencies
+"""))
     }
 
     def "tells if there are no configurations"() {
@@ -585,7 +602,8 @@ conf2
         run "dependencies"
 
         then:
-        output.contains(toPlatformLineSeparators("""conf
+        output.contains(toPlatformLineSeparators("""
+conf
 \\--- org.utils:impl:1.3 FAILED
 """))
     }
@@ -650,6 +668,7 @@ rootProject.name = 'root'
         output.contains "compile - Compile classpath for source set 'main'."
 
         output.contains(toPlatformLineSeparators("""
+compile - Compile classpath for source set 'main'.
 +--- project :a
 |    \\--- foo:bar:1.0 -> 2.0
 +--- project :b
@@ -661,4 +680,140 @@ rootProject.name = 'root'
           \\--- foo:bar:2.0
 """))
     }
+
+    def "reports external dependency replaced to project dependency"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+
+        file("settings.gradle") << "include 'client', 'api', 'impl'"
+
+        buildFile << """
+            allprojects {
+                version = '1.0'
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+
+                configurations {
+                    compile
+                }
+
+                group "org.utils"
+            }
+
+            project(":api") {
+                version = '1.5'
+            }
+
+            project(":impl") {
+                dependencies {
+                    compile group: 'org.utils', name: 'api', version: '1.3', configuration: 'compile'
+                }
+
+                configurations.compile.resolutionStrategy.dependencySubstitution.all {
+                    if (it.requested.version == '1.3') {
+                        it.useTarget project(":api")
+                    }
+                }
+            }
+"""
+
+        when:
+        run(":impl:dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+compile
+\\--- org.utils:api:1.3 -> project :api
+"""))
+    }
+
+    def "reports external dependency replaced to project dependency with other group/name"()
+    {
+        mavenRepo.module("org.utils", "api",  '1.3').publish()
+
+        file("settings.gradle") << "include 'client', 'api2', 'impl'"
+
+        buildFile << """
+            allprojects {
+                version = '1.0'
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+
+                configurations {
+                    compile
+                }
+
+                group "org.somethingelse"
+            }
+
+            project(":api2") {
+                version = '1.5'
+            }
+
+            project(":impl") {
+                dependencies {
+                    compile group: 'org.utils', name: 'api', version: '1.3', configuration: 'compile'
+                }
+
+                configurations.compile.resolutionStrategy.dependencySubstitution.all {
+                    if (it.requested.version == '1.3') {
+                        it.useTarget project(":api2")
+                    }
+                }
+            }
+"""
+
+        when:
+        run(":impl:dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+compile
+\\--- org.utils:api:1.3 -> project :api2
+"""))
+    }
+
+    def "reports external dependency name and version change"() {
+        mavenRepo.module("org.utils", "api2", '0.1').publish()
+
+        file("settings.gradle") << "include 'client', 'impl'"
+
+        buildFile << """
+            allprojects {
+                version = '1.0'
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+
+                configurations {
+                    compile
+                }
+
+                group "org.utils"
+            }
+
+            project(":impl") {
+                dependencies {
+                    compile group: 'org.utils', name: 'api', version: '1.3'
+                }
+
+                configurations.compile.resolutionStrategy.eachDependency {
+                    if (it.requested.version == '1.3') {
+                        it.useTarget group: 'org.utils', name: 'api2', version: '0.1'
+                    }
+                }
+            }
+"""
+
+        when:
+        run(":impl:dependencies")
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+compile
+\\--- org.utils:api:1.3 -> org.utils:api2:0.1
+"""))
+    }
 }
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
index 693fcf3..5b48061 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
@@ -17,6 +17,7 @@ package org.gradle.api.tasks.diagnostics
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.GradleVersion
 import org.junit.Rule
 
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
@@ -26,6 +27,30 @@ class HelpTaskIntegrationTest extends AbstractIntegrationSpec {
     @Rule
     public final TestResources resources = new TestResources(temporaryFolder)
 
+    def "shows basic welcome message for current project only"() {
+        given:
+        settingsFile << "include 'a', 'b', 'c'"
+
+        when:
+        run "help"
+
+        then:
+        output.startsWith(toPlatformLineSeparators(""":help
+
+Welcome to Gradle ${GradleVersion.current().version}.
+
+To run a build, run gradle <task> ...
+
+To see a list of available tasks, run gradle tasks
+
+To see a list of command-line options, run gradle --help
+
+To see more detail about a task, run gradle help --task <task>
+
+BUILD SUCCESSFUL
+"""))
+    }
+
     def "can print help for implicit tasks"() {
         when:
         run "help", "--task", "dependencies"
@@ -44,11 +69,12 @@ Options
 Description
      Displays all dependencies declared in root project '${testDirectory.getName()}'.
 
+Group
+     help
 
 BUILD SUCCESSFUL"""))
     }
 
-
     def "can print help for placeholder added tasks"() {
         when:
         run "help", "--task", "help"
@@ -62,16 +88,17 @@ Type
      Help (org.gradle.configuration.Help)
 
 Options
-     --task     The task, detailed help is requested for.
+     --task     The task to show help for.
 
 Description
-     Displays a help message
+     Displays a help message.
 
+Group
+     help
 
 BUILD SUCCESSFUL"""))
     }
 
-
     def "help for tasks same type different descriptions"() {
         setup:
         settingsFile.text = """
@@ -101,9 +128,58 @@ Type
 
 Descriptions
      (:hello) hello task from root
-     (:someproj:hello) hello task from someproj"""))
+     (:someproj:hello) hello task from someproj
+
+Group
+     -
+
+BUILD SUCCESSFUL"""))
     }
 
+    def "help for tasks same type different groups"() {
+        setup:
+        settingsFile.text = """
+include ":someproj1"
+include ":someproj2"
+"""
+        buildFile.text = """
+        task hello {
+            group = "group of root task"
+        }
+        project(":someproj1"){
+            task hello {
+                group = "group of subproject task"
+            }
+        }
+        project(":someproj2"){
+            task hello {
+                group = "group of subproject task"
+            }
+        }
+"""
+        when:
+        run "help", "--task", "hello"
+        then:
+        output.contains(toPlatformLineSeparators("""Detailed task information for hello
+
+Paths
+     :hello
+     :someproj1:hello
+     :someproj2:hello
+
+Type
+     Task (org.gradle.api.Task)
+
+Description
+     -
+
+Groups
+     (:hello) group of root task
+     (:someproj1:hello) group of subproject task
+     (:someproj2:hello) group of subproject task
+
+BUILD SUCCESSFUL"""))
+    }
 
     def "matchingTasksOfSameType"() {
         setup:
@@ -123,6 +199,8 @@ Type
 Description
      Assembles a jar archive containing the main classes.
 
+Group
+     build
 
 BUILD SUCCESSFUL"""))
 
@@ -141,6 +219,8 @@ Type
 Description
      Assembles a jar archive containing the main classes.
 
+Group
+     build
 
 BUILD SUCCESSFUL"""))
 
@@ -173,6 +253,9 @@ Type
 Description
      a copy operation
 
+Group
+     -
+
 ----------------------
 
 Path
@@ -184,6 +267,9 @@ Type
 Description
      an archiving operation
 
+Group
+     -
+
 ----------------------
 
 BUILD SUCCESSFUL"""))
@@ -216,11 +302,16 @@ Type
      Task (org.gradle.api.Task)
 
 Description
-     a description"""))
+     a description
+
+Group
+     -
+
+BUILD SUCCESSFUL"""))
 
     }
 
-    def "prints hint when using invalid commandlineoptions"() {
+    def "prints hint when using invalid command line options"() {
         when:
         fails "help", "--tasssk", "help"
 
@@ -254,7 +345,12 @@ Options
                           GHIJKL
 
 Description
-     -"""))
+     -
+
+Group
+     -
+
+BUILD SUCCESSFUL"""))
     }
 
     def "listsCommonDynamicAvailableValues"() {
@@ -277,6 +373,12 @@ Options
                             optionB
 
 Description
-     -"""))
+     -
+
+Group
+     -
+
+BUILD SUCCESSFUL"""))
     }
-}
\ No newline at end of file
+
+}
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskIntegrationTest.groovy
index 4d5acba..e8b01eb 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskIntegrationTest.groovy
@@ -19,9 +19,83 @@ package org.gradle.api.tasks.diagnostics
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import spock.lang.Issue
 
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
 class TaskReportTaskIntegrationTest extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2023")
+    def "task selector description is taken from task that TaskNameComparator considers to be of lowest ordering"() {
+        given:
+        settingsFile << """
+include 'sub1'
+include 'sub2'
+"""
+        file("sub1/build.gradle") << """
+task alpha { description = 'ALPHA_in_sub1' }
+"""
+        file("sub2/build.gradle") << """
+task alpha { description = 'ALPHA_in_sub2' }
+"""
+
+        when:
+        run "tasks"
+
+        then:
+        output.contains(toPlatformLineSeparators("""
+Other tasks
+-----------
+alpha - ALPHA_in_sub1
+"""))
+    }
+
+    def "task report includes tasks defined via model rules"() {
+        when:
+        buildScript """
+            model {
+                tasks {
+                    create("t1") {
+                        description = "from model rule"
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "tasks"
+
+        and:
+        output.contains("t1 - from model rule")
+    }
+
+    def "task report includes task container rule based tasks which are a dependency of a task defined via model rule"() {
+        when:
+        buildScript """
+            tasks.addRule("Pattern: containerRule<ID>") { taskName ->
+                if (taskName.startsWith("containerRule")) {
+                    task(taskName) {
+                        description = "from container rule"
+                    }
+                }
+            }
+
+            model {
+                tasks {
+                    create("t1") {
+                        description = "from model rule"
+                        dependsOn "containerRule1"
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "tasks", "--all"
+
+        and:
+        output.contains("t1 - from model rule")
+        output.contains("containerRule1 - from container rule")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2023")
     def "can deal with tasks with named task dependencies that are created by rules"() {
         when:
         buildFile << getBuildScriptContent()
@@ -30,7 +104,7 @@ class TaskReportTaskIntegrationTest extends AbstractIntegrationSpec {
         succeeds "tasks", "--all"
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2023")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2023")
     def "can deal with tasks with named task dependencies that are created by rules - multiproject"() {
         when:
         settingsFile << "include 'module'"
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.groovy
deleted file mode 100644
index c7e5587..0000000
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.groovy
+++ /dev/null
@@ -1,68 +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.plugins
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.tasks.diagnostics.*
-import org.gradle.configuration.Help
-
-import static org.gradle.configuration.ImplicitTasksConfigurer.*
-
- at Incubating
-class HelpTasksPlugin implements Plugin<ProjectInternal> {
-
-    void apply(ProjectInternal project) {
-        project.implicitTasks.create(name: HELP_TASK, type: Help) {
-            description = "Displays a help message"
-            group = HELP_GROUP
-        }
-
-        project.implicitTasks.create(name: PROJECTS_TASK, type: ProjectReportTask) {
-            description = "Displays the sub-projects of $project."
-            group = HELP_GROUP
-        }
-
-        project.implicitTasks.create(name: TASKS_TASK, type: TaskReportTask) {
-            if (project.subprojects.empty) {
-                description = "Displays the tasks runnable from $project."
-            } else {
-                description = "Displays the tasks runnable from $project (some of the displayed tasks may belong to subprojects)."
-            }
-            group = HELP_GROUP
-        }
-
-        project.implicitTasks.create(name: PROPERTIES_TASK, type: PropertyReportTask) {
-            description = "Displays the properties of $project."
-            group = HELP_GROUP
-        }
-
-        project.implicitTasks.create(name: DEPENDENCY_INSIGHT_TASK, type: DependencyInsightReportTask) { task ->
-            description = "Displays the insight into a specific dependency in $project."
-            group = HELP_GROUP
-            project.plugins.withType(JavaPlugin) {
-                task.configuration = project.configurations.compile
-            }
-        }
-
-        project.implicitTasks.create(name: DEPENDENCIES_TASK, type: DependencyReportTask) {
-            description = "Displays all dependencies declared in $project."
-            group = HELP_GROUP
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.java
new file mode 100644
index 0000000..6eebff1
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/HelpTasksPlugin.java
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.internal.component.BuildableJavaComponent;
+import org.gradle.api.internal.component.ComponentRegistry;
+import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
+import org.gradle.api.reporting.components.ComponentReport;
+import org.gradle.api.reporting.model.ModelReport;
+import org.gradle.api.tasks.diagnostics.*;
+import org.gradle.configuration.Help;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Defaults;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Adds various reporting tasks that provide information about the project.
+ */
+ at Incubating
+public class HelpTasksPlugin implements Plugin<ProjectInternal> {
+
+    public static final String HELP_GROUP = "help";
+    public static final String PROPERTIES_TASK = "properties";
+    public static final String DEPENDENCIES_TASK = "dependencies";
+    public static final String DEPENDENCY_INSIGHT_TASK = "dependencyInsight";
+    public static final String COMPONENTS_TASK = "components";
+    public static final String MODEL_TASK = "model";
+
+    public void apply(final ProjectInternal project) {
+        final TaskContainerInternal tasks = project.getTasks();
+
+        // static classes are used for the actions to avoid implicitly dragging project/tasks into the model registry
+        String projectName = project.toString();
+        tasks.addPlaceholderAction(ProjectInternal.HELP_TASK, Help.class, new HelpAction());
+        tasks.addPlaceholderAction(ProjectInternal.PROJECTS_TASK, ProjectReportTask.class, new ProjectReportTaskAction(projectName));
+        tasks.addPlaceholderAction(ProjectInternal.TASKS_TASK, TaskReportTask.class, new TaskReportTaskAction(projectName, project.getChildProjects().isEmpty()));
+        tasks.addPlaceholderAction(PROPERTIES_TASK, PropertyReportTask.class, new PropertyReportTaskAction(projectName));
+        tasks.addPlaceholderAction(DEPENDENCY_INSIGHT_TASK, DependencyInsightReportTask.class, new DependencyInsightReportTaskAction(projectName));
+        tasks.addPlaceholderAction(DEPENDENCIES_TASK, DependencyReportTask.class, new DependencyReportTaskAction(projectName));
+        tasks.addPlaceholderAction(COMPONENTS_TASK, ComponentReport.class, new ComponentReportAction(projectName));
+        tasks.addPlaceholderAction(MODEL_TASK, ModelReport.class, new ModelReportAction(projectName));
+    }
+
+    static class Rules extends RuleSource {
+        @Defaults
+        void addDefaultDependenciesReportConfiguration(@Path("tasks.dependencyInsight") DependencyInsightReportTask task, final ServiceRegistry services) {
+            new DslObject(task).getConventionMapping().map("configuration", new Callable<Object>() {
+                public Object call() {
+                    BuildableJavaComponent javaProject = services.get(ComponentRegistry.class).getMainComponent();
+                    return javaProject == null ? null : javaProject.getCompileDependencies();
+                }
+            });
+        }
+    }
+
+    private static class HelpAction implements Action<Help> {
+        public void execute(Help task) {
+            task.setDescription("Displays a help message.");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class ProjectReportTaskAction implements Action<ProjectReportTask> {
+        private final String project;
+
+        public ProjectReportTaskAction(String projectName) {
+            this.project = projectName;
+        }
+
+        public void execute(ProjectReportTask task) {
+            task.setDescription("Displays the sub-projects of " + project + ".");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class TaskReportTaskAction implements Action<TaskReportTask> {
+        private final String projectName;
+        private final boolean noChildren;
+
+        public TaskReportTaskAction(String projectName, boolean noChildren) {
+            this.projectName = projectName;
+            this.noChildren = noChildren;
+        }
+
+        public void execute(TaskReportTask task) {
+            String description;
+            if (noChildren) {
+                description = "Displays the tasks runnable from " + projectName + ".";
+            } else {
+                description = "Displays the tasks runnable from " + projectName + " (some of the displayed tasks may belong to subprojects).";
+            }
+            task.setDescription(description);
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class PropertyReportTaskAction implements Action<PropertyReportTask> {
+        private final String projectName;
+
+        public PropertyReportTaskAction(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public void execute(PropertyReportTask task) {
+            task.setDescription("Displays the properties of " + projectName + ".");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class DependencyInsightReportTaskAction implements Action<DependencyInsightReportTask> {
+        private final String projectName;
+
+        public DependencyInsightReportTaskAction(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public void execute(final DependencyInsightReportTask task) {
+            task.setDescription("Displays the insight into a specific dependency in " + projectName + ".");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class DependencyReportTaskAction implements Action<DependencyReportTask> {
+        private final String projectName;
+
+        public DependencyReportTaskAction(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public void execute(DependencyReportTask task) {
+            task.setDescription("Displays all dependencies declared in " + projectName + ".");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class ComponentReportAction implements Action<ComponentReport> {
+        private final String projectName;
+
+        public ComponentReportAction(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public void execute(ComponentReport task) {
+            task.setDescription("Displays the components produced by " + projectName + ". [incubating]");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+
+    private static class ModelReportAction implements Action<ModelReport> {
+        private final String projectName;
+
+        public ModelReportAction(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public void execute(ModelReport task) {
+            task.setDescription("Displays the configuration model of " + projectName + ". [incubating]");
+            task.setGroup(HELP_GROUP);
+            task.setImpliesSubProjects(true);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/ProjectReportsPlugin.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/ProjectReportsPlugin.java
index 3d244e4..e0eb062 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/ProjectReportsPlugin.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/ProjectReportsPlugin.java
@@ -19,8 +19,8 @@ import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.tasks.diagnostics.DependencyReportTask;
 import org.gradle.api.reporting.dependencies.HtmlDependencyReportTask;
+import org.gradle.api.tasks.diagnostics.DependencyReportTask;
 import org.gradle.api.tasks.diagnostics.PropertyReportTask;
 import org.gradle.api.tasks.diagnostics.TaskReportTask;
 
@@ -38,7 +38,7 @@ public class ProjectReportsPlugin implements Plugin<Project> {
     public static final String PROJECT_REPORT = "projectReport";
 
     public void apply(Project project) {
-        project.getPlugins().apply(ReportingBasePlugin.class);
+        project.getPluginManager().apply(ReportingBasePlugin.class);
         final ProjectReportsPluginConvention convention = new ProjectReportsPluginConvention(project);
         project.getConvention().getPlugins().put("projectReports", convention);
 
@@ -86,10 +86,10 @@ public class ProjectReportsPlugin implements Plugin<Project> {
                 HtmlDependencyReportTask.class);
         htmlDependencyReportTask.setDescription("Generates an HTML report about your library dependencies.");
         new DslObject(htmlDependencyReportTask.getReports().getHtml()).getConventionMapping().map("destination", new Callable<Object>() {
-                    public Object call() throws Exception {
-                        return new File(convention.getProjectReportDir(), "dependencies");
-                    }
-                });
+            public Object call() throws Exception {
+                return new File(convention.getProjectReportDir(), "dependencies");
+            }
+        });
         htmlDependencyReportTask.conventionMapping("projects", new Callable<Object>() {
             public Object call() throws Exception {
                 return convention.getProjects();
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/internal/HelpTasksAutoApplyAction.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/internal/HelpTasksAutoApplyAction.java
index 8852340..1253861 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/internal/HelpTasksAutoApplyAction.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/plugins/internal/HelpTasksAutoApplyAction.java
@@ -21,6 +21,6 @@ import org.gradle.configuration.project.ProjectConfigureAction;
 //This one should go away once we complete the auto-apply plugins
 public class HelpTasksAutoApplyAction implements ProjectConfigureAction {
     public void execute(ProjectInternal project) {
-        project.getPlugins().apply("help-tasks");
+        project.getPluginManager().apply("org.gradle.help-tasks");
     }
 }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/ComponentReport.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/ComponentReport.java
new file mode 100644
index 0000000..5020506
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/ComponentReport.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.Project;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.reporting.components.internal.ComponentReportRenderer;
+import org.gradle.api.reporting.components.internal.TypeAwareBinaryRenderer;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.test.TestSuiteContainer;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Displays some details about the software components produced by the project.
+ */
+ at Incubating
+public class ComponentReport extends DefaultTask {
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ModelRegistry getModelRegistry() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected TypeAwareBinaryRenderer getBinaryRenderer() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    public void report() {
+        Project project = getProject();
+
+        StyledTextOutput textOutput = getTextOutputFactory().create(ComponentReport.class);
+        ComponentReportRenderer renderer = new ComponentReportRenderer(getFileResolver(), getBinaryRenderer());
+        renderer.setOutput(textOutput);
+
+        renderer.startProject(project);
+
+        Collection<ComponentSpec> components = new ArrayList<ComponentSpec>();
+        ComponentSpecContainer componentSpecs = getModelRegistry().find(ModelPath.path("components"), ModelType.of(ComponentSpecContainer.class));
+        if (componentSpecs != null) {
+            components.addAll(componentSpecs);
+        }
+
+        TestSuiteContainer testSuites = getModelRegistry().find(ModelPath.path("testSuites"), ModelType.of(TestSuiteContainer.class));
+        if (testSuites != null) {
+            components.addAll(testSuites);
+        }
+
+        renderer.renderComponents(components);
+
+        ProjectSourceSet sourceSets = getModelRegistry().find(ModelPath.path("sources"), ModelType.of(ProjectSourceSet.class));
+        if (sourceSets != null) {
+            renderer.renderSourceSets(sourceSets);
+        }
+        BinaryContainer binaries = getModelRegistry().find(ModelPath.path("binaries"), ModelType.of(BinaryContainer.class));
+        if (binaries != null) {
+            renderer.renderBinaries(binaries);
+        }
+
+        renderer.completeProject(project);
+        renderer.complete();
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/AbstractBinaryRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/AbstractBinaryRenderer.java
new file mode 100644
index 0000000..d220831
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/AbstractBinaryRenderer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.internal.BinaryBuildAbility;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.reporting.ReportRenderer;
+
+// TODO - bust up this hierarchy and compose using interfaces instead
+public abstract class AbstractBinaryRenderer<T extends BinarySpec> extends ReportRenderer<BinarySpec, TextReportBuilder> {
+    public void render(BinarySpec binary, TextReportBuilder builder) {
+        StyledTextOutput textOutput = builder.getOutput();
+
+        textOutput.append(StringUtils.capitalize(binary.getDisplayName()));
+        if (!binary.isBuildable()) {
+            textOutput.append(" (not buildable)");
+        }
+        textOutput.println();
+
+        builder.item("build using task", binary.getBuildTask().getPath());
+
+        T specialized = getTargetType().cast(binary);
+
+        renderTasks(specialized, builder);
+
+        renderDetails(specialized, builder);
+
+        renderOutputs(specialized, builder);
+
+        renderBuildAbility(specialized, builder);
+    }
+
+    public abstract Class<T> getTargetType();
+
+    protected void renderOutputs(T binary, TextReportBuilder builder) {
+    }
+
+    protected void renderDetails(T binary, TextReportBuilder builder) {
+    }
+
+    protected void renderTasks(T binary, TextReportBuilder builder) {
+    }
+
+    private void renderBuildAbility(BinarySpec binary, TextReportBuilder builder) {
+        BinaryBuildAbility buildAbility = ((BinarySpecInternal) binary).getBuildAbility();
+        if (!buildAbility.isBuildable()) {
+            TreeFormatter formatter = new TreeFormatter();
+            buildAbility.explain(formatter);
+            builder.item(formatter.toString());
+        }
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/BinaryRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/BinaryRenderer.java
new file mode 100644
index 0000000..43b9e13
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/BinaryRenderer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.gradle.platform.base.BinarySpec;
+
+public class BinaryRenderer extends AbstractBinaryRenderer<BinarySpec> {
+    @Override
+    public Class<BinarySpec> getTargetType() {
+        return BinarySpec.class;
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentRenderer.java
new file mode 100644
index 0000000..a57509f
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentRenderer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.reporting.ReportRenderer;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Comparator;
+
+public class ComponentRenderer extends ReportRenderer<ComponentSpec, TextReportBuilder> {
+    private final ReportRenderer<LanguageSourceSet, TextReportBuilder> sourceSetRenderer;
+    private final ReportRenderer<BinarySpec, TextReportBuilder> binaryRenderer;
+
+    public ComponentRenderer(ReportRenderer<LanguageSourceSet, TextReportBuilder> sourceSetRenderer, ReportRenderer<BinarySpec, TextReportBuilder> binaryRenderer) {
+        this.sourceSetRenderer = sourceSetRenderer;
+        this.binaryRenderer = binaryRenderer;
+    }
+
+    @Override
+    public void render(ComponentSpec component, TextReportBuilder builder) {
+        builder.subheading(StringUtils.capitalize(component.getDisplayName()));
+        builder.getOutput().println();
+        builder.collection("Source sets", component.getSource(), sourceSetRenderer, "source sets");
+        builder.getOutput().println();
+        builder.collection("Binaries", CollectionUtils.sort(component.getBinaries(), new Comparator<BinarySpec>() {
+            public int compare(BinarySpec binary1, BinarySpec binary2) {
+                return binary1.getName().compareTo(binary2.getName());
+            }
+        }), binaryRenderer, "binaries");
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentReportRenderer.java
new file mode 100644
index 0000000..6d78494
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/ComponentReportRenderer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.ComponentSpec;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.gradle.logging.StyledTextOutput.Style.Info;
+
+public class ComponentReportRenderer extends TextReportRenderer {
+    private final ComponentRenderer componentRenderer;
+    private final SourceSetRenderer sourceSetRenderer;
+    private final TypeAwareBinaryRenderer binaryRenderer;
+    private final Set<LanguageSourceSet> componentSourceSets = new HashSet<LanguageSourceSet>();
+    private final Set<BinarySpec> componentBinaries = new HashSet<BinarySpec>();
+
+    public ComponentReportRenderer(FileResolver fileResolver, TypeAwareBinaryRenderer binaryRenderer) {
+        setFileResolver(fileResolver);
+        sourceSetRenderer = new SourceSetRenderer();
+        this.binaryRenderer = binaryRenderer;
+        componentRenderer = new ComponentRenderer(sourceSetRenderer, binaryRenderer);
+    }
+
+    @Override
+    public void complete() {
+        getTextOutput().println();
+        getTextOutput().println("Note: currently not all plugins register their components, so some components may not be visible here.");
+        super.complete();
+    }
+
+    public void renderComponents(Collection<ComponentSpec> components) {
+        if (components.isEmpty()) {
+            getTextOutput().withStyle(Info).println("No components defined for this project.");
+            return;
+        }
+        boolean seen = false;
+        for (ComponentSpec component : components) {
+            if (seen) {
+                getBuilder().getOutput().println();
+            } else {
+                seen = true;
+            }
+            componentRenderer.render(component, getBuilder());
+            componentSourceSets.addAll(component.getSource());
+            componentBinaries.addAll(component.getBinaries());
+        }
+    }
+
+    public void renderSourceSets(Collection<LanguageSourceSet> sourceSets) {
+        Set<LanguageSourceSet> additionalSourceSets = new LinkedHashSet<LanguageSourceSet>();
+        for (LanguageSourceSet sourceSet : sourceSets) {
+            if (!componentSourceSets.contains(sourceSet)) {
+                additionalSourceSets.add(sourceSet);
+            }
+        }
+        if (!additionalSourceSets.isEmpty()) {
+            getBuilder().getOutput().println();
+            getBuilder().subheading("Additional source sets");
+            for (LanguageSourceSet sourceSet : additionalSourceSets) {
+                sourceSetRenderer.render(sourceSet, getBuilder());
+            }
+        }
+    }
+
+    public void renderBinaries(Collection<BinarySpec> binaries) {
+        Set<BinarySpec> additionalBinaries = new LinkedHashSet<BinarySpec>();
+        for (BinarySpec binary : binaries) {
+            if (!componentBinaries.contains(binary)) {
+                additionalBinaries.add(binary);
+            }
+        }
+        if (!additionalBinaries.isEmpty()) {
+            getBuilder().getOutput().println();
+            getBuilder().subheading("Additional binaries");
+            for (BinarySpec binary : additionalBinaries) {
+                try {
+                    binaryRenderer.render(binary, getBuilder());
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/DiagnosticsServices.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/DiagnosticsServices.java
new file mode 100644
index 0000000..d81a773
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/DiagnosticsServices.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class DiagnosticsServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new Object() {
+            TypeAwareBinaryRenderer createBinaryRenderer(ServiceRegistry services) {
+                TypeAwareBinaryRenderer renderer = new TypeAwareBinaryRenderer();
+                renderer.register(new BinaryRenderer());
+                for (AbstractBinaryRenderer binaryRenderer : services.getAll(AbstractBinaryRenderer.class)) {
+                    renderer.register(binaryRenderer);
+                }
+                return renderer;
+            }
+        });
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/SourceSetRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/SourceSetRenderer.java
new file mode 100644
index 0000000..15f87ee
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/SourceSetRenderer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.reporting.ReportRenderer;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.Set;
+
+class SourceSetRenderer extends ReportRenderer<LanguageSourceSet, TextReportBuilder> {
+    @Override
+    public void render(LanguageSourceSet sourceSet, TextReportBuilder builder) {
+        StyledTextOutput textOutput = builder.getOutput();
+        textOutput.println(StringUtils.capitalize(sourceSet.getDisplayName()));
+        Set<File> srcDirs = sourceSet.getSource().getSrcDirs();
+        if (srcDirs.isEmpty()) {
+            textOutput.println("    No source directories");
+        } else {
+            for (File file : srcDirs) {
+                builder.item(file);
+            }
+            SourceDirectorySet source = sourceSet.getSource();
+            Set<String> includes = source.getIncludes();
+            if(!includes.isEmpty()) {
+                builder.item("includes", CollectionUtils.join(", ", includes));
+            }
+            Set<String> excludes = source.getExcludes();
+            if(!excludes.isEmpty()) {
+                builder.item("excludes", CollectionUtils.join(", ", excludes));
+            }
+        }
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/TypeAwareBinaryRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/TypeAwareBinaryRenderer.java
new file mode 100644
index 0000000..39706e2
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/internal/TypeAwareBinaryRenderer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.reporting.ReportRenderer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TypeAwareBinaryRenderer extends ReportRenderer<BinarySpec, TextReportBuilder> {
+    private final Map<Class<?>, ReportRenderer<BinarySpec, TextReportBuilder>> renderers = new HashMap<Class<?>, ReportRenderer<BinarySpec, TextReportBuilder>>();
+
+    public void register(AbstractBinaryRenderer<?> renderer) {
+        renderers.put(renderer.getTargetType(), renderer);
+    }
+
+    @Override
+    public void render(BinarySpec model, TextReportBuilder output) throws IOException {
+        ReportRenderer<BinarySpec, TextReportBuilder> renderer = getRendererForType(model.getClass());
+        renderer.render(model, output);
+    }
+
+    private ReportRenderer<BinarySpec, TextReportBuilder> getRendererForType(Class<? extends BinarySpec> type) {
+        ReportRenderer<BinarySpec, TextReportBuilder> renderer = renderers.get(type);
+        if (renderer == null) {
+            Class<?> bestType = null;
+            for (Map.Entry<Class<?>, ReportRenderer<BinarySpec, TextReportBuilder>> entry : renderers.entrySet()) {
+                if (!entry.getKey().isAssignableFrom(type)) {
+                    continue;
+                }
+                if (bestType == null || bestType.isAssignableFrom(entry.getKey())) {
+                    bestType = entry.getKey();
+                    renderer = entry.getValue();
+                }
+            }
+            renderers.put(type, renderer);
+        }
+        return renderer;
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/package-info.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/package-info.java
new file mode 100644
index 0000000..5ca2aa6
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/components/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Component reporting tasks.
+ */
+package org.gradle.api.reporting.components;
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
index 864654c..615abed 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
@@ -20,17 +20,17 @@ import groovy.lang.Closure;
 import org.gradle.api.Incubating;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
 import org.gradle.api.reporting.Reporting;
 import org.gradle.api.reporting.dependencies.internal.DefaultDependencyReportContainer;
+import org.gradle.api.reporting.dependencies.internal.HtmlDependencyReporter;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.reporting.dependencies.internal.HtmlDependencyReporter;
 import org.gradle.internal.reflect.Instantiator;
 
-import java.io.IOException;
+import javax.inject.Inject;
 import java.util.Set;
 
 /**
@@ -60,7 +60,7 @@ public class HtmlDependencyReportTask extends ConventionTask implements Reportin
     private final DefaultDependencyReportContainer reports;
 
     public HtmlDependencyReportTask() {
-        reports = getServices().get(Instantiator.class).newInstance(DefaultDependencyReportContainer.class, this);
+        reports = getInstantiator().newInstance(DefaultDependencyReportContainer.class, this);
         reports.getHtml().setEnabled(true);
         getOutputs().upToDateWhen(new Spec<Task>() {
             public boolean isSatisfiedBy(Task element) {
@@ -78,6 +78,21 @@ public class HtmlDependencyReportTask extends ConventionTask implements Reportin
         return reports;
     }
 
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected VersionSelectorScheme getVersionSelectorScheme() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected VersionComparator getVersionComparator() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     public void generate() {
         if (!reports.getHtml().isEnabled()) {
@@ -85,13 +100,8 @@ public class HtmlDependencyReportTask extends ConventionTask implements Reportin
             return;
         }
 
-        try {
-            HtmlDependencyReporter reporter = new HtmlDependencyReporter(getServices().get(VersionMatcher.class));
-            reporter.setOutputDirectory(reports.getHtml().getDestination());
-            reporter.generate(getProjects());
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
+        HtmlDependencyReporter reporter = new HtmlDependencyReporter(getVersionSelectorScheme(), getVersionComparator());
+        reporter.render(getProjects(), reports.getHtml().getDestination());
     }
 
     /**
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/HtmlDependencyReporter.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/HtmlDependencyReporter.groovy
index 76ef2e4..9763848 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/HtmlDependencyReporter.groovy
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/HtmlDependencyReporter.groovy
@@ -18,7 +18,11 @@ package org.gradle.api.reporting.dependencies.internal
 
 import org.gradle.api.Project
 import org.gradle.api.Transformer
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
+import org.gradle.reporting.HtmlReportBuilder
+import org.gradle.reporting.HtmlReportRenderer
+import org.gradle.reporting.ReportRenderer
 import org.gradle.util.GFileUtils
 
 /**
@@ -32,54 +36,37 @@ import org.gradle.util.GFileUtils
  * The HTML file uses a JavaScript script to generate an interactive page from the data contained in
  * the JSON structure.
  * <p>
- * The same technique is also used to generate the index report, listing all the projects for which
- * a dependency report has been generated.
  *
- * @see JsonDependencyReportIndexRenderer
  * @see JsonProjectDependencyRenderer
  */
-class HtmlDependencyReporter {
+class HtmlDependencyReporter extends ReportRenderer<Set<Project>, File> {
+    private File outputDirectory;
+    private final JsonProjectDependencyRenderer renderer
 
-    File outputDirectory;
-    JsonProjectDependencyRenderer renderer
-    JsonDependencyReportIndexRenderer indexRenderer = new JsonDependencyReportIndexRenderer()
-
-    HtmlDependencyReporter(VersionMatcher versionMatcher) {
-        renderer = new JsonProjectDependencyRenderer(versionMatcher)
+    HtmlDependencyReporter(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+        renderer = new JsonProjectDependencyRenderer(versionSelectorScheme, versionComparator)
     }
 
-    /**
-     * Sets the output directory of the report. This directory contains the generated HTML file,
-     * but also JS and CSS files. This method must be called before generating the report.
-     */
-    void setOutputDirectory(File outputDirectory) {
+    @Override
+    void render(Set<Project> projects, File outputDirectory) {
         this.outputDirectory = outputDirectory
-    }
-
-    /**
-     * Generates a report for each of the given projects, and generates the index report
-     */
-    void generate(Set<Project> projects) throws IOException {
-        GFileUtils.copyURLToFile(getClass().getResource("/org/gradle/reporting/base-style.css"), new File(outputDirectory, "base-style.css"))
-        copyReportFile("d.gif")
-        copyReportFile("d.png")
-        copyReportFile("jquery.jstree.js")
-        copyReportFile("jquery-1.10.1.min.js")
-        copyReportFile("script.js")
-        copyReportFile("style.css")
-        copyReportFile("throbber.gif")
-        copyReportFile("tree.css")
-        copyReportFile("index.html")
-
-        String template = readHtmlTemplate();
-        for (Project project : projects) {
-            String jsFileName = toFileName(project, '.js')
-            generateJsFile(project, jsFileName)
-            String htmlFileName = toFileName(project, '.html')
-            generateHtmlFile(template, htmlFileName, jsFileName)
-        }
 
-        generateIndexJsFile(projects, 'index.js')
+        def renderer = new HtmlReportRenderer()
+        renderer.render(projects, new ReportRenderer<Set<Project>, HtmlReportBuilder>() {
+            @Override
+            void render(Set<Project> model, HtmlReportBuilder builder) {
+                def htmlPageScheme = projectNamingScheme("html")
+                def jsScheme = projectNamingScheme("js")
+                def projectPageRenderer = new ProjectPageRenderer(jsScheme)
+                builder.renderRawHtmlPage("index.html", projects, new ProjectsPageRenderer(htmlPageScheme))
+                for (Project project : projects) {
+                    String jsFileName = jsScheme.transform(project)
+                    generateJsFile(project, jsFileName)
+                    String htmlFileName = htmlPageScheme.transform(project)
+                    builder.renderRawHtmlPage(htmlFileName, project, projectPageRenderer)
+                }
+            }
+        }, outputDirectory)
     }
 
     private void generateJsFile(Project project, String fileName) {
@@ -88,34 +75,12 @@ class HtmlDependencyReporter {
         GFileUtils.writeFile(content, new File(outputDirectory, fileName), "utf-8")
     }
 
-    private void generateIndexJsFile(Set<Project> projects, String fileName) {
-        String json = indexRenderer.render(projects, new Transformer<String, Project>() {
+    private Transformer<String, Project> projectNamingScheme(String extension) {
+        new Transformer<String, Project>() {
             String transform(Project project) {
-                toFileName(project, ".html")
+                toFileName(project, "." + extension)
             }
-        })
-
-        String content = "var mainDependencyReport = " + json.toString() + ";";
-        GFileUtils.writeFile(content, new File(outputDirectory, fileName), "utf-8")
-    }
-
-    private void generateHtmlFile(String template, String fileName, String jsFileName) {
-        String content = template.replace('@js@', jsFileName);
-        GFileUtils.writeFile(content, new File(outputDirectory, fileName), "utf-8")
-    }
-
-    private copyReportFile(String fileName) {
-        GFileUtils.copyURLToFile(getClass().getResource(getReportResourcePath(fileName)),
-                                 new File(outputDirectory, fileName))
-
-    }
-
-    private String readHtmlTemplate() {
-        getClass().getResourceAsStream(getReportResourcePath("template.html")).getText("UTF8")
-    }
-
-    private String getReportResourcePath(String fileName) {
-        "/org/gradle/api/tasks/diagnostics/htmldependencyreport/" + fileName
+        }
     }
 
     private String toFileName(Project project, String extension) {
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonDependencyReportIndexRenderer.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonDependencyReportIndexRenderer.groovy
deleted file mode 100644
index 8749b64..0000000
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonDependencyReportIndexRenderer.groovy
+++ /dev/null
@@ -1,79 +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.reporting.dependencies.internal
-import groovy.json.JsonBuilder
-import org.gradle.api.Project
-import org.gradle.api.Transformer
-import org.gradle.util.GradleVersion
-/**
- * Renderer that emits a JSON tree containing the structure of the data displayed in the index page
- * of an HTML dependency report (list of projects).
- * The structure is the following:
- * <pre>
- *     {
- *         "gradleVersion" : "...",
- *         "generationDate" : "...",
- *         "projects" : [
- *             {
- *                 "path" : "...",
- *                 "name" : "...",
- *                 "description" : "...",
- *                 "file" : "..."
- *             },
- *             ...
- *         ]
- *     }
- * </pre>
- */
-class JsonDependencyReportIndexRenderer {
-
-    /**
-     * Generates the project dependency report structure
-     * @param project the project for which the report must be generated
-     * @return the generated JSON, as a String
-     */
-    String render(Set<Project> projects, Transformer<String, Project> projectToFileName) {
-        JsonBuilder json = new JsonBuilder();
-        renderProjects(projects, projectToFileName, json);
-        return json.toString();
-    }
-
-    private void renderProjects(Set<Project> projects,
-                                Transformer<String, Project> projectToFileName,
-                                JsonBuilder json) {
-        List<Project> sortedProjects = sortProjects(projects)
-        List jsonProjectList = sortedProjects.collect { project ->
-            [
-                path: "root" + project.path,
-                name: project.name,
-                description: project.description,
-                file: projectToFileName.transform(project)
-            ]
-        }
-        json gradleVersion : GradleVersion.current().toString(),
-             generationDate : new Date().toString(),
-             projects : jsonProjectList
-    }
-
-    private List<Project> sortProjects(Set<Project> projects) {
-        List<Project> sortedProjects = new ArrayList<Project>(projects);
-        sortedProjects.sort {
-            it.path
-        }
-        sortedProjects
-    }
-}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonProjectDependencyRenderer.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonProjectDependencyRenderer.groovy
index dae08e2..777a120 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonProjectDependencyRenderer.groovy
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/JsonProjectDependencyRenderer.groovy
@@ -25,7 +25,8 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier
 import org.gradle.api.artifacts.result.DependencyResult
 import org.gradle.api.artifacts.result.ResolutionResult
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
 import org.gradle.api.specs.Spec
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableModuleResult
@@ -94,10 +95,12 @@ import org.gradle.util.GradleVersion
  * </pre>
  */
 class JsonProjectDependencyRenderer {
-    private final VersionMatcher versionMatcher
+    private final VersionSelectorScheme versionSelectorScheme
+    private final VersionComparator versionComparator
 
-    JsonProjectDependencyRenderer(VersionMatcher versionMatcher) {
-        this.versionMatcher = versionMatcher
+    JsonProjectDependencyRenderer(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+        this.versionSelectorScheme = versionSelectorScheme
+        this.versionComparator = versionComparator
     }
 
     /**
@@ -211,7 +214,7 @@ class JsonProjectDependencyRenderer {
             }
         }
 
-        Collection<RenderableDependency> sortedDeps = new DependencyInsightReporter().prepare(selectedDependencies, versionMatcher)
+        Collection<RenderableDependency> sortedDeps = new DependencyInsightReporter().prepare(selectedDependencies, versionSelectorScheme, versionComparator)
         return sortedDeps.collect { dependency ->
             String name = replaceArrow(dependency.name);
             [
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectPageRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectPageRenderer.java
new file mode 100644
index 0000000..0ff5572
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectPageRenderer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.dependencies.internal;
+
+import com.googlecode.jatl.Html;
+import org.gradle.api.Project;
+import org.gradle.api.Transformer;
+import org.gradle.reporting.HtmlPageBuilder;
+import org.gradle.reporting.ReportRenderer;
+import org.gradle.util.GradleVersion;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Date;
+
+public class ProjectPageRenderer extends ReportRenderer<Project, HtmlPageBuilder<Writer>> {
+    private final Transformer<String, Project> namingScheme;
+
+    public ProjectPageRenderer(Transformer<String, Project> namingScheme) {
+        this.namingScheme = namingScheme;
+    }
+
+    @Override
+    public void render(final Project project, final HtmlPageBuilder<Writer> builder) throws IOException {
+        final String baseCssLink = builder.requireResource(getClass().getResource("/org/gradle/reporting/base-style.css"));
+        final String cssLink = builder.requireResource(getClass().getResource(getReportResourcePath("style.css")));
+        final String jqueryLink = builder.requireResource(getClass().getResource("/org/gradle/reporting/jquery.min-1.11.0.js"));
+        final String jtreeLink = builder.requireResource(getClass().getResource(getReportResourcePath("jquery.jstree.js")));
+        final String scriptLink = builder.requireResource(getClass().getResource(getReportResourcePath("script.js")));
+        builder.requireResource(getClass().getResource(getReportResourcePath("tree.css")));
+        builder.requireResource(getClass().getResource(getReportResourcePath("d.gif")));
+        builder.requireResource(getClass().getResource(getReportResourcePath("d.png")));
+        builder.requireResource(getClass().getResource(getReportResourcePath("throbber.gif")));
+
+        new Html(builder.getOutput()) {{
+            html();
+                head();
+                    meta().httpEquiv("Content-Type").content("text/html; charset=utf-8");
+                    meta().httpEquiv("x-ua-compatible").content("IE=edge");
+                    link().rel("stylesheet").type("text/css").href(baseCssLink).end();
+                    link().rel("stylesheet").type("text/css").href(cssLink).end();
+                    script().src(jqueryLink).charset("utf-8").end();
+                    script().src(jtreeLink).charset("utf-8").end();
+                    script().src(namingScheme.transform(project)).charset("utf-8").end();
+                    script().src(scriptLink).charset("utf-8").end();
+                    title().text("Dependency reports").end();
+                end();
+                body();
+                    div().id("content");
+                        h1().text("Dependency Report").end();
+                        div().classAttr("breadcrumbs");
+                            a().href("index.html").text("Projects").end();
+                            text(" > ");
+                            span().id("projectBreacrumb").end();
+                        end();
+                        div().id("insight").end();
+                        div().id("dependencies").end();
+                        div().id("footer");
+                            p();
+                                text("Generated by ");
+                                a().href("http://www.gradle.org").text(GradleVersion.current().toString()).end();
+                                text(String.format(" at %s", builder.formatDate(new Date())));
+                            end();
+                        end();
+                    end();
+                end();
+            endAll();
+        }};
+    }
+
+    private String getReportResourcePath(String fileName) {
+        return "/org/gradle/api/tasks/diagnostics/htmldependencyreport/" + fileName;
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectsPageRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectsPageRenderer.java
new file mode 100644
index 0000000..7b56bb7
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/dependencies/internal/ProjectsPageRenderer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.dependencies.internal;
+
+import com.googlecode.jatl.Html;
+import org.gradle.api.Project;
+import org.gradle.api.Transformer;
+import org.gradle.reporting.HtmlPageBuilder;
+import org.gradle.reporting.ReportRenderer;
+import org.gradle.util.GradleVersion;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Date;
+import java.util.Set;
+
+public class ProjectsPageRenderer extends ReportRenderer<Set<Project>, HtmlPageBuilder<Writer>> {
+    private final Transformer<String, Project> namingScheme;
+
+    public ProjectsPageRenderer(Transformer<String, Project> namingScheme) {
+        this.namingScheme = namingScheme;
+    }
+
+    @Override
+    public void render(final Set<Project> projects, final HtmlPageBuilder<Writer> builder) throws IOException {
+        final String baseCssLink = builder.requireResource(getClass().getResource("/org/gradle/reporting/base-style.css"));
+        final String cssLink = builder.requireResource(getClass().getResource("/org/gradle/api/tasks/diagnostics/htmldependencyreport/style.css"));
+
+        new Html(builder.getOutput()) {{
+            html();
+                head();
+                    meta().httpEquiv("Content-Type").content("text/html; charset=utf-8");
+                    meta().httpEquiv("x-ua-compatible").content("IE=edge");
+                    link().rel("stylesheet").type("text/css").href(baseCssLink).end();
+                    link().rel("stylesheet").type("text/css").href(cssLink).end();
+                    title().text("Dependency reports").end();
+                end();
+                body();
+                    div().id("content");
+                        h1().text("Dependency Reports").end();
+                        div().classAttr("tab");
+                            table();
+                                thead();
+                                    tr();
+                                        th().text("Project").end();
+                                        th().text("Description").end();
+                                    end();
+                                end();
+                                tbody();
+                                for (Project project : projects) {
+                                    tr();
+                                        td().a().href(namingScheme.transform(project)).text(project.toString()).end().end();
+                                        td().text(project.getDescription()).end();
+                                    end();
+                                }
+                                end();
+                            end();
+                        end();
+                        div().id("footer");
+                            p();
+                                text("Generated by ");
+                                a().href("http://www.gradle.org").text(GradleVersion.current().toString()).end();
+                                text(String.format(" at %s", builder.formatDate(new Date())));
+                            end();
+                        end();
+                    end();
+                end();
+            endAll();
+        }};
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/ModelReport.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/ModelReport.java
new file mode 100644
index 0000000..0e5e501
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/ModelReport.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.model;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.Project;
+import org.gradle.api.reporting.model.internal.ModelReportRenderer;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.model.internal.core.ModelNode;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
+
+import javax.inject.Inject;
+
+/**
+ * Displays some details about the configuration model of the project.
+ */
+ at Incubating
+public class ModelReport extends DefaultTask {
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ModelRegistry getModelRegistry() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    public void report() {
+        Project project = getProject();
+
+        StyledTextOutput textOutput = getTextOutputFactory().create(ModelReport.class);
+        ModelReportRenderer renderer = new ModelReportRenderer();
+        renderer.setOutput(textOutput);
+
+        renderer.startProject(project);
+
+        // Configure the world
+        ModelNode rootNode = getModelRegistry().realizeNode(ModelPath.ROOT);
+        renderer.render(rootNode);
+
+        renderer.completeProject(project);
+        renderer.complete();
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/internal/ModelReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/internal/ModelReportRenderer.java
new file mode 100644
index 0000000..b52ad51
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/internal/ModelReportRenderer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.model.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.model.internal.core.ModelNode;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.reporting.ReportRenderer;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class ModelReportRenderer extends TextReportRenderer {
+    private final NodeRenderer nodeRenderer = new NodeRenderer();
+
+    public void render(ModelNode node) {
+        nodeRenderer.render(node, getBuilder());
+    }
+
+    private static class NodeRenderer extends ReportRenderer<ModelNode, TextReportBuilder> {
+        @Override
+        public void render(ModelNode model, TextReportBuilder output) {
+            if (model.isHidden()) {
+                return;
+            }
+
+            if (model.getPath().equals(ModelPath.ROOT)) {
+                output.getOutput().println("model");
+            } else {
+                output.getOutput().println(model.getPath().getName());
+            }
+
+            Map<String, ModelNode> links = new TreeMap<String, ModelNode>();
+            for (ModelNode node : model.getLinks(ModelType.untyped())) {
+                links.put(node.getPath().getName(), node);
+            }
+            output.collection(links.values(), this);
+        }
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/package-info.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/package-info.java
new file mode 100644
index 0000000..6f4e3e4
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/reporting/model/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Configuration model reporting tasks.
+ */
+package org.gradle.api.reporting.model;
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
index 9f4926b..6f74587 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
@@ -24,8 +24,10 @@ import org.gradle.api.tasks.Optional;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
+import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.logging.StyledTextOutputFactory;
 
+import javax.inject.Inject;
 import java.io.File;
 import java.io.IOException;
 import java.util.HashSet;
@@ -38,7 +40,7 @@ import java.util.TreeSet;
 public abstract class AbstractReportTask extends ConventionTask {
     private File outputFile;
 
-    // todo annotate as required 
+    // todo annotate as required
     private Set<Project> projects;
 
     protected AbstractReportTask() {
@@ -51,15 +53,26 @@ public abstract class AbstractReportTask extends ConventionTask {
         projects.add(getProject());
     }
 
+    @Inject
+    protected BuildClientMetaData getClientMetaData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     public void generate() {
         try {
             ReportRenderer renderer = getRenderer();
+            renderer.setClientMetaData(getClientMetaData());
             File outputFile = getOutputFile();
             if (outputFile != null) {
                 renderer.setOutputFile(outputFile);
             } else {
-                renderer.setOutput(getServices().get(StyledTextOutputFactory.class).create(getClass()));
+                renderer.setOutput(getTextOutputFactory().create(getClass()));
             }
             Set<Project> projects = new TreeSet<Project>(getProjects());
             for (Project project : projects) {
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.groovy
index ea366d6..118fe1b 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.groovy
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.groovy
@@ -23,11 +23,12 @@ import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.result.DependencyResult
 import org.gradle.api.artifacts.result.ResolutionResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
 import org.gradle.api.internal.tasks.options.Option
 import org.gradle.api.specs.Spec
 import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.diagnostics.internal.dsl.DependencyResultSpecNotationParser
+import org.gradle.api.tasks.diagnostics.internal.dsl.DependencyResultSpecNotationConverter
 import org.gradle.api.tasks.diagnostics.internal.graph.DependencyGraphRenderer
 import org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency
@@ -80,15 +81,19 @@ public class DependencyInsightReportTask extends DefaultTask {
      */
     Spec<DependencyResult> dependencySpec;
 
-    private final StyledTextOutput output;
-    private final GraphRenderer renderer;
-    private final VersionMatcher versionMatcher;
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Inject
+    protected VersionSelectorScheme getVersionSelectorScheme() {
+        throw new UnsupportedOperationException()
+    }
 
     @Inject
-    DependencyInsightReportTask(StyledTextOutputFactory outputFactory, VersionMatcher versionMatcher) {
-        output = outputFactory.create(getClass());
-        renderer = new GraphRenderer(output)
-        this.versionMatcher = versionMatcher
+    protected VersionComparator getVersionComparator() {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -116,7 +121,7 @@ public class DependencyInsightReportTask extends DefaultTask {
      */
     @Option(option = "dependency", description = "Shows the details of given dependency.")
     public void setDependencySpec(Object dependencyInsightNotation) {
-        def parser = DependencyResultSpecNotationParser.create()
+        def parser = DependencyResultSpecNotationConverter.parser()
         this.dependencySpec = parser.parseNotation(dependencyInsightNotation)
     }
 
@@ -144,6 +149,7 @@ public class DependencyInsightReportTask extends DefaultTask {
 
     @TaskAction
     public void report() {
+        def configuration = getConfiguration()
         if (configuration == null) {
             throw new InvalidUserDataException("Dependency insight report cannot be generated because the input configuration was not specified. "
                     + "\nIt can be specified from the command line, e.g: '$path --configuration someConf --dependency someDep'")
@@ -153,6 +159,9 @@ public class DependencyInsightReportTask extends DefaultTask {
                     + "\nIt can be specified from the command line, e.g: '$path --dependency someDep'")
         }
 
+        StyledTextOutput output = textOutputFactory.create(getClass())
+        GraphRenderer renderer = new GraphRenderer(output)
+
         ResolutionResult result = configuration.getIncoming().getResolutionResult();
 
         Set<DependencyResult> selectedDependencies = new LinkedHashSet<DependencyResult>()
@@ -163,18 +172,18 @@ public class DependencyInsightReportTask extends DefaultTask {
         }
 
         if (selectedDependencies.empty) {
-            output.println("No dependencies matching given input were found in $configuration")
+            output.println("No dependencies matching given input were found in ${configuration}")
             return
         }
 
-        def sortedDeps = new DependencyInsightReporter().prepare(selectedDependencies, versionMatcher)
+        def sortedDeps = new DependencyInsightReporter().prepare(selectedDependencies, versionSelectorScheme, versionComparator)
 
         def nodeRenderer = new NodeRenderer() {
-            void renderNode(StyledTextOutput output, RenderableDependency node, boolean alreadyRendered) {
+            void renderNode(StyledTextOutput target, RenderableDependency node, boolean alreadyRendered) {
                 boolean leaf = node.children.empty
-                output.text(leaf ? DependencyInsightReportTask.this.configuration.name : node.name);
+                target.text(leaf ? configuration.name : node.name);
                 if (alreadyRendered && !leaf) {
-                    output.withStyle(Info).text(" (*)")
+                    target.withStyle(Info).text(" (*)")
                 }
             }
         }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java
index 5c0ca6e..fe63ade 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java
@@ -18,8 +18,8 @@ package org.gradle.api.tasks.diagnostics;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Action;
 import org.gradle.api.Project;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
-import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.internal.graph.GraphRenderer;
 import org.gradle.logging.StyledTextOutput;
@@ -45,7 +45,7 @@ public class ProjectReportTask extends AbstractReportTask {
 
     @Override
     protected void generate(Project project) throws IOException {
-        BuildClientMetaData metaData = getServices().get(BuildClientMetaData.class);
+        BuildClientMetaData metaData = getClientMetaData();
 
         StyledTextOutput textOutput = getRenderer().getTextOutput();
 
@@ -58,20 +58,20 @@ public class ProjectReportTask extends AbstractReportTask {
         textOutput.println();
         textOutput.text("To see a list of the tasks of a project, run ");
         metaData.describeCommand(textOutput.withStyle(UserInput), String.format("<project-path>:%s",
-                ImplicitTasksConfigurer.TASKS_TASK));
+                ProjectInternal.TASKS_TASK));
         textOutput.println();
 
         textOutput.text("For example, try running ");
         Project exampleProject = project.getChildProjects().isEmpty() ? project : getChildren(project).get(0);
         metaData.describeCommand(textOutput.withStyle(UserInput), exampleProject.absoluteProjectPath(
-                ImplicitTasksConfigurer.TASKS_TASK));
+                ProjectInternal.TASKS_TASK));
         textOutput.println();
 
         if (project != project.getRootProject()) {
             textOutput.println();
             textOutput.text("To see a list of all the projects in this build, run ");
             metaData.describeCommand(textOutput.withStyle(UserInput), project.getRootProject().absoluteProjectPath(
-                    ImplicitTasksConfigurer.PROJECTS_TASK));
+                    ProjectInternal.PROJECTS_TASK));
             textOutput.println();
         }
     }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
index ae2fef1..dedbc5c 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
@@ -15,14 +15,13 @@
  */
 package org.gradle.api.tasks.diagnostics;
 
-import com.google.common.collect.Sets;
 import org.gradle.api.Project;
 import org.gradle.api.Rule;
-import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectTaskLister;
 import org.gradle.api.internal.tasks.options.Option;
-import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.tasks.diagnostics.internal.*;
 
+import javax.inject.Inject;
 import java.io.IOException;
 
 /**
@@ -59,18 +58,12 @@ public class TaskReportTask extends AbstractReportTask {
         TaskDetailsFactory taskDetailsFactory = new TaskDetailsFactory(project);
 
         SingleProjectTaskReportModel projectTaskModel = new SingleProjectTaskReportModel(taskDetailsFactory);
-        ProjectInternal projectInternal = (ProjectInternal) project;
-        TaskContainerInternal tasks = projectInternal.getTasks();
-        tasks.actualize();
-        projectTaskModel.build(Sets.union(tasks, projectInternal.getImplicitTasks()));
+        projectTaskModel.build(getProjectTaskLister().listProjectTasks(project));
         aggregateModel.add(projectTaskModel);
 
         for (Project subproject : project.getSubprojects()) {
             SingleProjectTaskReportModel subprojectTaskModel = new SingleProjectTaskReportModel(taskDetailsFactory);
-            ProjectInternal subprojectInternal = (ProjectInternal) subproject;
-            TaskContainerInternal subprojectTasks = subprojectInternal.getTasks();
-            subprojectTasks.actualize();
-            subprojectTaskModel.build(subprojectTasks);
+            subprojectTaskModel.build(getProjectTaskLister().listProjectTasks(subproject));
             aggregateModel.add(subprojectTaskModel);
         }
 
@@ -94,4 +87,9 @@ public class TaskReportTask extends AbstractReportTask {
             renderer.addRule(rule);
         }
     }
+
+    @Inject
+    protected ProjectTaskLister getProjectTaskLister() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java
index 414269c..28617bf 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java
@@ -16,6 +16,7 @@
 package org.gradle.api.tasks.diagnostics.internal;
 
 import org.gradle.api.Project;
+import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.logging.StyledTextOutput;
 
 import java.io.File;
@@ -25,6 +26,14 @@ import java.io.IOException;
  * Renders the model of a project report.
  */
 public interface ReportRenderer {
+
+    /**
+     * Set the build client metadata.
+     *
+     * @param clientMetaData the build client metadata, never null
+     */
+    void setClientMetaData(BuildClientMetaData clientMetaData);
+
     /**
      * Sets the text output for the report. This method must be called before any other methods on this renderer.
      *
@@ -56,5 +65,6 @@ public interface ReportRenderer {
     /**
      * Completes this report. This method must be called last on this renderer.
      */
-    void complete() throws IOException;
+    void complete();
+
 }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java
index 19257ac..16a7037 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java
@@ -19,11 +19,12 @@ package org.gradle.api.tasks.diagnostics.internal;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Project;
 import org.gradle.api.Rule;
+import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.logging.StyledTextOutput;
 import org.gradle.util.CollectionUtils;
 import org.gradle.util.GUtil;
 import org.gradle.util.Path;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -57,7 +58,7 @@ public class TaskReportRenderer extends TextReportRenderer {
     public void showDetail(boolean detail) {
         this.detail = detail;
     }
-    
+
     /**
      * Writes the default task names for the current project.
      *
@@ -117,7 +118,7 @@ public class TaskReportRenderer extends TextReportRenderer {
             getTextOutput().println();
         }
         hasContent = true;
-        writeSubheading(header);
+        getBuilder().subheading(header);
     }
 
     /**
@@ -144,11 +145,19 @@ public class TaskReportRenderer extends TextReportRenderer {
     }
 
     @Override
-    public void complete() throws IOException {
+    public void complete() {
         if (!detail) {
-            getTextOutput().println();
-            getTextOutput().text("To see all tasks and more detail, run with ").style(UserInput).text("--all.");
-            getTextOutput().println();
+            StyledTextOutput output = getTextOutput();
+            BuildClientMetaData clientMetaData = getClientMetaData();
+
+            output.println();
+            output.text("To see all tasks and more detail, run ");
+            clientMetaData.describeCommand(output.withStyle(UserInput), "tasks --all");
+            output.println();
+            output.println();
+            output.text("To see more detail about a task, run ");
+            clientMetaData.describeCommand(output.withStyle(UserInput), "help --task <task>");
+            output.println();
         }
         super.complete();
     }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java
index ceef522..79b2e83 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java
@@ -16,23 +16,38 @@
 package org.gradle.api.tasks.diagnostics.internal;
 
 import org.gradle.api.Project;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.tasks.diagnostics.internal.text.DefaultTextReportBuilder;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.logging.StyledTextOutput;
 import org.gradle.logging.internal.StreamingStyledTextOutput;
 import org.gradle.util.GUtil;
 
-import java.io.*;
-
-import static org.gradle.logging.StyledTextOutput.Style.Header;
-import static org.gradle.logging.StyledTextOutput.Style.Normal;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 
 /**
  * <p>A basic {@link ReportRenderer} which writes out a text report.
  */
 public class TextReportRenderer implements ReportRenderer {
-    public static final String SEPARATOR = "------------------------------------------------------------";
+    private BuildClientMetaData clientMetaData;
+    private FileResolver fileResolver;
     private StyledTextOutput textOutput;
+    private TextReportBuilder builder;
     private boolean close;
 
+    public void setFileResolver(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+    }
+
+    public void setClientMetaData(BuildClientMetaData clientMetaData) {
+        this.clientMetaData = clientMetaData;
+    }
+
     public void setOutput(StyledTextOutput textOutput) {
         setWriter(textOutput, false);
     }
@@ -44,7 +59,7 @@ public class TextReportRenderer implements ReportRenderer {
 
     public void startProject(Project project) {
         String header = createHeader(project);
-        writeHeading(header);
+        builder.heading(header);
     }
 
     protected String createHeader(Project project) {
@@ -63,43 +78,36 @@ public class TextReportRenderer implements ReportRenderer {
     public void completeProject(Project project) {
     }
 
-    public void complete() throws IOException {
+    public void complete() {
         cleanupWriter();
     }
 
     private void setWriter(StyledTextOutput styledTextOutput, boolean close) {
         this.textOutput = styledTextOutput;
+        this.builder = new DefaultTextReportBuilder(textOutput, fileResolver);
         this.close = close;
     }
 
-    private void cleanupWriter() throws IOException {
+    private void cleanupWriter() {
         try {
-            if (textOutput != null && close) {
-                ((Closeable) textOutput).close();
+            if (close) {
+                CompositeStoppable.stoppable(textOutput).stop();
             }
         } finally {
             textOutput = null;
         }
     }
 
+    public BuildClientMetaData getClientMetaData() {
+        return clientMetaData;
+    }
+
     public StyledTextOutput getTextOutput() {
         return textOutput;
     }
 
-    public void writeHeading(String heading) {
-        textOutput.println().style(Header);
-        textOutput.println(SEPARATOR);
-        textOutput.println(heading);
-        textOutput.text(SEPARATOR);
-        textOutput.style(Normal);
-        textOutput.println().println();
+    public TextReportBuilder getBuilder() {
+        return builder;
     }
 
-    public void writeSubheading(String heading) {
-        getTextOutput().style(Header).println(heading);
-        for (int i = 0; i < heading.length(); i++) {
-            getTextOutput().text("-");
-        }
-        getTextOutput().style(Normal).println();
-    }
 }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dependencies/AsciiDependencyReportRenderer.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dependencies/AsciiDependencyReportRenderer.java
index e18db8e..23e4144 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dependencies/AsciiDependencyReportRenderer.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dependencies/AsciiDependencyReportRenderer.java
@@ -94,7 +94,7 @@ public class AsciiDependencyReportRenderer extends TextReportRenderer implements
         dependencyGraphRenderer.render(root);
     }
 
-    public void complete() throws IOException {
+    public void complete() {
         if (dependencyGraphRenderer != null) {
             dependencyGraphRenderer.printLegend();
         }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationConverter.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationConverter.java
new file mode 100644
index 0000000..47f6f91
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationConverter.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.tasks.diagnostics.internal.dsl;
+
+import groovy.lang.Closure;
+import org.gradle.api.artifacts.result.DependencyResult;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+
+public class DependencyResultSpecNotationConverter implements NotationConverter<String, Spec<DependencyResult>> {
+    public void convert(String notation, NotationConvertResult<? super Spec<DependencyResult>> result) throws TypeConversionException {
+        final String stringNotation = notation.trim();
+        if (stringNotation.length() > 0) {
+            result.converted(new DependencyResultSpec(stringNotation));
+        }
+    }
+
+    @Override
+    public void describe(DiagnosticsVisitor visitor) {
+        visitor.candidate("Non-empty String or CharSequence value").example("'some-lib' or 'org.libs:some-lib'");
+    }
+
+    public static NotationParser<Object, Spec<DependencyResult>> parser() {
+        return NotationParserBuilder
+                .toType(new TypeInfo<Spec<DependencyResult>>(Spec.class))
+                .invalidNotationMessage("Please check the input for the DependencyInsight.dependency element.")
+                .fromType(Closure.class, new ClosureToSpecNotationConverter<DependencyResult>(DependencyResult.class))
+                .fromCharSequence(new DependencyResultSpecNotationConverter())
+                .toComposite();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParser.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParser.java
deleted file mode 100644
index 6199ab5..0000000
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParser.java
+++ /dev/null
@@ -1,54 +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.tasks.diagnostics.internal.dsl;
-
-import org.gradle.api.artifacts.result.DependencyResult;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.TypeInfo;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
-import org.gradle.internal.typeconversion.ClosureToSpecNotationParser;
-import org.gradle.api.specs.Spec;
-
-import java.util.Collection;
-
-public class DependencyResultSpecNotationParser implements NotationParser<Object, Spec<DependencyResult>> {
-
-    public Spec<DependencyResult> parseNotation(final Object notation) throws UnsupportedNotationException {
-        if (notation instanceof CharSequence) {
-            final String stringNotation = notation.toString().trim();
-            if (stringNotation.length() > 0) {
-                return new DependencyResultSpec(stringNotation);
-            }
-        }
-        throw new UnsupportedNotationException(notation);
-    }
-
-    public void describe(Collection<String> candidateFormats) {
-        candidateFormats.add("Non-empty String value, e.g. 'some-lib' or 'org.libs:some-lib'.");
-        candidateFormats.add("Closure that returns boolean and takes a single DependencyResult as parameter.");
-    }
-
-    public static NotationParser<Object, Spec<DependencyResult>> create() {
-        return new NotationParserBuilder<Spec<DependencyResult>>()
-                .resultingType(new TypeInfo<Spec<DependencyResult>>(Spec.class))
-                .invalidNotationMessage("Please check the input for the DependencyInsight.dependency element.")
-                .parser(new ClosureToSpecNotationParser<DependencyResult>())
-                .parser(new DependencyResultSpecNotationParser())
-                .toComposite();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/DependencyGraphRenderer.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/DependencyGraphRenderer.groovy
index 28b70f2..e911fee 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/DependencyGraphRenderer.groovy
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/DependencyGraphRenderer.groovy
@@ -45,12 +45,12 @@ class DependencyGraphRenderer {
         def i = 0
         for (RenderableDependency child : children) {
             boolean last = i++ == children.size() - 1
-            render(child, last, visited)
+            doRender(child, last, visited)
         }
         renderer.completeChildren()
     }
 
-    private void render(final RenderableDependency node, boolean last, Set<ComponentIdentifier> visited) {
+    private void doRender(final RenderableDependency node, boolean last, Set<ComponentIdentifier> visited) {
         def children = node.getChildren()
         def alreadyRendered = !visited.add(node.getId())
         if (alreadyRendered) {
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvedDependencyEdge.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvedDependencyEdge.java
index faa137e..786843c 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvedDependencyEdge.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvedDependencyEdge.java
@@ -21,7 +21,7 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
 import org.gradle.api.artifacts.result.UnresolvedDependencyResult;
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
 
 import java.util.Collections;
 import java.util.Set;
@@ -32,6 +32,7 @@ public class UnresolvedDependencyEdge implements DependencyEdge {
 
     public UnresolvedDependencyEdge(UnresolvedDependencyResult dependency) {
         this.dependency = dependency;
+        // TODO:Prezi Is this cast safe? Can't this be a LibraryComponentSelector, say?
         ModuleComponentSelector attempted = (ModuleComponentSelector)dependency.getAttempted();
         actual = DefaultModuleComponentIdentifier.newId(attempted.getGroup(), attempted.getModule(), attempted.getVersion());
     }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.groovy b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.groovy
index 71ab21e..c7a52e9 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.groovy
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.groovy
@@ -20,15 +20,13 @@ import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.artifacts.result.ComponentSelectionReason
 import org.gradle.api.artifacts.result.DependencyResult
 import org.gradle.api.artifacts.result.UnresolvedDependencyResult
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.*
 
-/**
- * Created: 23/08/2012
- */
 public class DependencyInsightReporter {
 
-    Collection<RenderableDependency> prepare(Collection<DependencyResult> input, VersionMatcher versionMatcher) {
+    Collection<RenderableDependency> prepare(Collection<DependencyResult> input, VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
         def out = new LinkedList<RenderableDependency>()
         def dependencies = input.collect {
             if (it instanceof UnresolvedDependencyResult) {
@@ -38,7 +36,7 @@ public class DependencyInsightReporter {
             }
         }
 
-        def sorted = DependencyResultSorter.sort(dependencies, versionMatcher)
+        def sorted = DependencyResultSorter.sort(dependencies, versionSelectorScheme, versionComparator)
 
         //remember if module id was annotated
         def annotated = new HashSet<ComponentIdentifier>()
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorter.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorter.java
index 5e9fc59..80dcf0c 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorter.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorter.java
@@ -17,32 +17,31 @@
 package org.gradle.api.tasks.diagnostics.internal.insight;
 
 import org.gradle.api.artifacts.component.*;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.DependencyEdge;
 import org.gradle.util.CollectionUtils;
 
 import java.util.Collection;
 import java.util.Comparator;
 
-/**
- * Created: 17/08/2012
- */
 public class DependencyResultSorter {
-
     /**
      * sorts by group:name:version mostly.
      * If requested matches selected then it will override the version comparison
      * so that the dependency that was selected is more prominent.
      */
-    public static Collection<DependencyEdge> sort(Collection<DependencyEdge> input, VersionMatcher versionMatcher) {
-        return CollectionUtils.sort(input, new DependencyComparator(versionMatcher));
+    public static Collection<DependencyEdge> sort(Collection<DependencyEdge> input, VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+        return CollectionUtils.sort(input, new DependencyComparator(versionSelectorScheme, versionComparator));
     }
 
     private static class DependencyComparator implements Comparator<DependencyEdge> {
-        private final VersionMatcher matcher;
+        private final VersionSelectorScheme versionSelectorScheme;
+        private final Comparator<String> versionComparator;
 
-        private DependencyComparator(VersionMatcher matcher) {
-            this.matcher = matcher;
+        private DependencyComparator(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
+            this.versionSelectorScheme = versionSelectorScheme;
+            this.versionComparator = versionComparator.asStringComparator();
         }
 
         public int compare(DependencyEdge left, DependencyEdge right) {
@@ -123,8 +122,8 @@ public class DependencyResultSorter {
             }
 
             //order dynamic selectors after static selectors
-            boolean leftDynamic = matcher.isDynamic(leftRequested.getVersion());
-            boolean rightDynamic = matcher.isDynamic(rightRequested.getVersion());
+            boolean leftDynamic = versionSelectorScheme.parseSelector(leftRequested.getVersion()).isDynamic();
+            boolean rightDynamic = versionSelectorScheme.parseSelector(rightRequested.getVersion()).isDynamic();
             if (leftDynamic && !rightDynamic) {
                 return 1;
             } else if (!leftDynamic && rightDynamic) {
@@ -137,7 +136,7 @@ public class DependencyResultSorter {
                 byVersion = leftRequested.getVersion().compareTo(rightRequested.getVersion());
             } else {
                 // order static selectors semantically
-                byVersion = matcher.compare(leftRequested.getVersion(), rightRequested.getVersion());
+                byVersion = compareVersions(leftRequested.getVersion(), rightRequested.getVersion());
             }
             if (byVersion != 0) {
                 return byVersion;
@@ -183,7 +182,11 @@ public class DependencyResultSorter {
                 return byModule;
             }
 
-            return matcher.compare(leftFrom.getVersion(), rightFrom.getVersion());
+            return compareVersions(leftFrom.getVersion(), rightFrom.getVersion());
+        }
+
+        private int compareVersions(String left, String right) {
+            return versionComparator.compare(left, right);
         }
 
         private boolean isLeftAndRightFromProjectComponentIdentifier(ComponentIdentifier left, ComponentIdentifier right) {
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilder.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilder.java
new file mode 100644
index 0000000..9205747
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.diagnostics.internal.text;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.internal.LinePrefixingStyledTextOutput;
+import org.gradle.reporting.ReportRenderer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+import static org.gradle.logging.StyledTextOutput.Style.Header;
+import static org.gradle.logging.StyledTextOutput.Style.Normal;
+
+public class DefaultTextReportBuilder implements TextReportBuilder {
+    public static final String SEPARATOR = "------------------------------------------------------------";
+    private StyledTextOutput textOutput;
+    private final FileResolver fileResolver;
+
+    public DefaultTextReportBuilder(StyledTextOutput textOutput, FileResolver fileResolver) {
+        this.textOutput = textOutput;
+        this.fileResolver = fileResolver;
+    }
+
+    public void item(String title, String value) {
+        textOutput.append("    ").append(title).append(": ");
+        StyledTextOutput itemOutput = new LinePrefixingStyledTextOutput(textOutput, "    ");
+        itemOutput.append(value).println();
+    }
+
+    public void item(String title, File value) {
+        item(title, fileResolver.resolveAsRelativePath(value));
+    }
+
+    public void item(String value) {
+        textOutput.append("    ");
+        StyledTextOutput itemOutput = new LinePrefixingStyledTextOutput(textOutput, "    ");
+        itemOutput.append(value).println();
+    }
+
+    public void item(File value) {
+        item(fileResolver.resolveAsRelativePath(value));
+    }
+
+    public void heading(String heading) {
+        textOutput.println().style(Header);
+        textOutput.println(SEPARATOR);
+        textOutput.println(heading);
+        textOutput.text(SEPARATOR);
+        textOutput.style(Normal);
+        textOutput.println().println();
+    }
+
+    public void subheading(String heading) {
+        textOutput.style(Header).println(heading);
+        for (int i = 0; i < heading.length(); i++) {
+            textOutput.text("-");
+        }
+        textOutput.style(Normal).println();
+    }
+
+    public <T> void collection(String title, Collection<? extends T> items, ReportRenderer<T, TextReportBuilder> renderer, String elementsPlural) {
+        textOutput.println(title);
+        if (items.isEmpty()) {
+            textOutput.formatln("    No %s.", elementsPlural);
+            return;
+        }
+        collection(items, renderer);
+    }
+
+    @Override
+    public <T> void collection(Iterable<? extends T> items, ReportRenderer<T, TextReportBuilder> renderer) {
+        StyledTextOutput original = textOutput;
+        boolean hasItem = false;
+        try {
+            textOutput = new LinePrefixingStyledTextOutput(original, "    ");
+            for (T t : items) {
+                // TODO - change LinePrefixingStyledTextOutput to prefix every line
+                if (!hasItem) {
+                    textOutput.append("    ");
+                    hasItem = true;
+                }
+                try {
+                    renderer.render(t, this);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        } finally {
+            textOutput = original;
+        }
+    }
+
+    public StyledTextOutput getOutput() {
+        return textOutput;
+    }
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/TextReportBuilder.java b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/TextReportBuilder.java
new file mode 100644
index 0000000..10b9831
--- /dev/null
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/text/TextReportBuilder.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.diagnostics.internal.text;
+
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.reporting.ReportRenderer;
+
+import java.io.File;
+import java.util.Collection;
+
+public interface TextReportBuilder {
+    void heading(String heading);
+
+    void subheading(String heading);
+
+    <T> void collection(String title, Collection<? extends T> items, ReportRenderer<T, TextReportBuilder> renderer, String elementsPlural);
+
+    <T> void collection(Iterable<? extends T> items, ReportRenderer<T, TextReportBuilder> renderer);
+
+    void item(String title, String value);
+
+    void item(String title, File value);
+
+    void item(String value);
+
+    void item(File value);
+
+    StyledTextOutput getOutput();
+}
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/Help.java b/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/Help.java
index 60b875b..af63c1b 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/Help.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/Help.java
@@ -25,15 +25,37 @@ import org.gradle.logging.StyledTextOutput;
 import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.util.GradleVersion;
 
+import javax.inject.Inject;
+
 import static org.gradle.logging.StyledTextOutput.Style.UserInput;
 
 public class Help extends DefaultTask {
     private String taskPath;
 
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected BuildClientMetaData getClientMetaData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected TaskSelector getTaskSelector() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected OptionReader getOptionReader() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     void displayHelp() {
-        StyledTextOutput output = getServices().get(StyledTextOutputFactory.class).create(Help.class);
-        BuildClientMetaData metaData = getServices().get(BuildClientMetaData.class);
+        StyledTextOutput output = getTextOutputFactory().create(Help.class);
+        BuildClientMetaData metaData = getClientMetaData();
         if (taskPath != null) {
             printTaskHelp(output);
         } else {
@@ -42,9 +64,9 @@ public class Help extends DefaultTask {
     }
 
     private void printTaskHelp(StyledTextOutput output) {
-        TaskSelector selector = getServices().get(TaskSelector.class);
-        final TaskSelector.TaskSelection selection = selector.getSelection(taskPath);
-        final OptionReader optionReader = getServices().get(OptionReader.class);
+        TaskSelector selector = getTaskSelector();
+        TaskSelector.TaskSelection selection = selector.getSelection(taskPath);
+        OptionReader optionReader = getOptionReader();
         TaskDetailPrinter taskDetailPrinter = new TaskDetailPrinter(taskPath, selection, optionReader);
         taskDetailPrinter.print(output);
     }
@@ -64,9 +86,13 @@ public class Help extends DefaultTask {
         output.text("To see a list of command-line options, run ");
         metaData.describeCommand(output.withStyle(UserInput), "--help");
         output.println();
+        output.println();
+        output.text("To see more detail about a task, run ");
+        metaData.describeCommand(output.withStyle(UserInput), "help --task <task>");
+        output.println();
     }
 
-    @Option(option = "task", description = "The task, detailed help is requested for.")
+    @Option(option = "task", description = "The task to show help for.")
     public void setTaskPath(String taskPath) {
         this.taskPath = taskPath;
     }
diff --git a/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/TaskDetailPrinter.java b/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/TaskDetailPrinter.java
index f7b0338..5ac4ac2 100644
--- a/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/TaskDetailPrinter.java
+++ b/subprojects/diagnostics/src/main/groovy/org/gradle/configuration/TaskDetailPrinter.java
@@ -67,6 +67,7 @@ public class TaskDetailPrinter {
             for (Task task : tasksByType) {
                 pathOutput.withStyle(UserInput).println(task.getPath());
             }
+
             output.println();
             final LinePrefixingStyledTextOutput typeOutput = createIndentedOutput(output, INDENT);
             typeOutput.println("Type");
@@ -77,8 +78,12 @@ public class TaskDetailPrinter {
 
             output.println();
             printTaskDescription(output, tasksByType);
+
             output.println();
+            printTaskGroup(output, tasksByType);
+
             if (multipleClasses) {
+                output.println();
                 output.println("----------------------");
             }
         }
@@ -117,17 +122,36 @@ public class TaskDetailPrinter {
     }
 
     private void printTaskDescription(StyledTextOutput output, List<Task> tasks) {
-        int differentDescriptionsCount = differentDescriptions(tasks);
-        final LinePrefixingStyledTextOutput descriptorOutput = createIndentedOutput(output, INDENT);
-        descriptorOutput.println(differentDescriptionsCount > 1 ? "Descriptions" : "Description");
-        if (differentDescriptionsCount == 1) {
-            // all tasks have the same description
+        printTaskAttribute(output, "Description", tasks, new Transformer<String, Task>() {
+            public String transform(Task task) {
+                return task.getDescription();
+            }
+        });
+    }
+
+    private void printTaskGroup(StyledTextOutput output, List<Task> tasks) {
+        printTaskAttribute(output, "Group", tasks, new Transformer<String, Task>() {
+            public String transform(Task task) {
+                return task.getGroup();
+            }
+        });
+    }
+
+    private void printTaskAttribute(StyledTextOutput output, String attributeHeader, List<Task> tasks, Transformer<String, Task> transformer) {
+        int count = collect(tasks, new HashSet<String>(), transformer).size();
+        final LinePrefixingStyledTextOutput attributeOutput = createIndentedOutput(output, INDENT);
+        if (count == 1) {
+            // all tasks have the same value
+            attributeOutput.println(attributeHeader);
             final Task task = tasks.iterator().next();
-            descriptorOutput.println(task.getDescription() == null ? "-" : task.getDescription());
+            String value = transformer.transform(task);
+            attributeOutput.println(value == null ? "-" : value);
         } else {
+            attributeOutput.println(attributeHeader + "s");
             for (Task task : tasks) {
-                descriptorOutput.withStyle(UserInput).text(String.format("(%s) ", task.getPath()));
-                descriptorOutput.println(task.getDescription() == null ? "-" : task.getDescription());
+                attributeOutput.withStyle(UserInput).text(String.format("(%s) ", task.getPath()));
+                String value = transformer.transform(task);
+                attributeOutput.println(value == null ? "-" : value);
             }
         }
     }
@@ -193,14 +217,4 @@ public class TaskDetailPrinter {
     private LinePrefixingStyledTextOutput createIndentedOutput(StyledTextOutput output, String prefix) {
         return new LinePrefixingStyledTextOutput(output, prefix);
     }
-
-    private int differentDescriptions(List<Task> tasks) {
-        return toSet(
-                collect(tasks, new Transformer<String, Task>() {
-                    public String transform(Task original) {
-                        return original.getDescription();
-                    }
-                })
-        ).size();
-    }
 }
diff --git a/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/help-tasks.properties b/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.help-tasks.properties
similarity index 100%
rename from subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/help-tasks.properties
rename to subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.help-tasks.properties
diff --git a/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/project-report.properties b/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.project-report.properties
similarity index 100%
rename from subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/project-report.properties
rename to subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.project-report.properties
diff --git a/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/project-reports.properties b/subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.project-reports.properties
similarity index 100%
rename from subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/project-reports.properties
rename to subprojects/diagnostics/src/main/resources/META-INF/gradle-plugins/org.gradle.project-reports.properties
diff --git a/subprojects/diagnostics/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/diagnostics/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..6e94d41
--- /dev/null
+++ b/subprojects/diagnostics/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.api.reporting.components.internal.DiagnosticsServices
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/index.html b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/index.html
deleted file mode 100644
index 928c0ee..0000000
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/index.html
+++ /dev/null
@@ -1,48 +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.
-  -->
-
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Dependency Reports</title>
-        <link href="base-style.css" rel="stylesheet" type="text/css"/>
-        <link href="style.css" rel="stylesheet" type="text/css"/>
-        <script src="jquery-1.10.1.min.js" charset="utf-8"></script>
-        <script src="index.js" charset="utf-8"></script>
-        <script src="script.js" charset="utf-8"></script>
-    </head>
-    <body>
-        <div id="content">
-            <h1>Dependency Reports</h1>
-            <div id="tab0" class="tab selected">
-                <table id="projects">
-                    <thead>
-                        <tr>
-                            <th>Project path</th>
-                            <th>Name</th>
-                            <th>Description</th>
-                        </tr>
-                    </thead>
-                    <tbody>
-                    </tbody>
-                </table>
-            </div>
-            <div id="footer">
-                <p>Generated by <a id="gradleVersion" href="http://www.gradle.org"></a> at <span id="generationDate"></span></p>
-            </div>
-        </div>
-    </body>
-</html>
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/jquery-1.10.1.min.js b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/jquery-1.10.1.min.js
deleted file mode 100644
index e407e76..0000000
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/jquery-1.10.1.min.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*! jQuery v1.10.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
-//@ sourceMappingURL=jquery-1.10.1.min.map
-*/
-(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.1",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/ [...]
-}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"== [...]
-u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.first [...]
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/script.js b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/script.js
index 2888d66..7f4d9a8 100644
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/script.js
+++ b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/script.js
@@ -116,7 +116,7 @@ function initializeProjectPage(report) {
                     data : nodes
                 },
                 themes : {
-                    url : 'tree.css',
+                    url : 'css/tree.css',
                     icons : false
                 },
                 plugins : ['json_data', 'themes']
@@ -163,7 +163,7 @@ function initializeProjectPage(report) {
                         data : nodes
                     },
                     themes : {
-                        url : 'tree.css',
+                        url : 'css/tree.css',
                         icons : false
                     },
                     plugins : ['json_data', 'themes']
@@ -197,29 +197,6 @@ function initializeProjectPage(report) {
     });
 }
 
-function initializeIndexPage(report) {
-    $(document).ready(function() {
-        var $tbody = $('#projects tbody');
-        $.each(report.projects, function(index, project) {
-            var $tr = $('<tr></tr>');
-            var $pathTd = $('<td></td>');
-            $pathTd.append($('<a></a>').attr("href", project.file).text(project.path))
-            var $nameTd = $('<td></td>').text(project.name);
-            $tr.append($pathTd).append($nameTd);
-            var $descriptionTd = $('<td></td>').text(project.description ? project.description : '');
-            $tr.append($pathTd).append($nameTd).append($descriptionTd);
-            $tbody.append($tr);
-        });
-
-        populateFooter(report);
-    });
-}
-
 if (window.projectDependencyReport) {
     initializeProjectPage(window.projectDependencyReport);
 }
-else if (window.mainDependencyReport) {
-    initializeIndexPage(window.mainDependencyReport);
-}
-
-
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/style.css b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/style.css
index 4915a29..3186aaa 100644
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/style.css
+++ b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/style.css
@@ -17,7 +17,6 @@
 .dependencyTree, #insight {
     border: solid 2px #d0d0d0;
     border-radius: 10px;
-    behavior: url(css3-pie-1.0beta3.htc);
     padding: 8px;
     background-color: #FFFFEE
 }
@@ -44,7 +43,7 @@
     cursor: pointer;
 }
 .configuration h3 ins {
-    background-image: url(d.png); background-repeat:no-repeat; background-color:white;
+    background-image: url(../images/d.png); background-repeat:no-repeat; background-color:white;
     background-position: -18px 0;
     display: inline-block;
     width: 18px;
@@ -68,7 +67,7 @@
 }
 #dismissInsight {
     float: right;
-    background:url("d.png") -18px -53px no-repeat !important;
+    background:url("../images/d.png") -18px -53px no-repeat !important;
     width: 20px;
     height: 20px;
     cursor: pointer;
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/template.html b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/template.html
deleted file mode 100644
index b9a6808..0000000
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/template.html
+++ /dev/null
@@ -1,41 +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.
-  -->
-
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Dependency report</title>
-        <link href="base-style.css" rel="stylesheet" type="text/css">
-        <link href="style.css" rel="stylesheet" type="text/css">
-        <script src="jquery-1.10.1.min.js" charset="utf-8"></script>
-        <script src="jquery.jstree.js" charset="utf-8"></script>
-        <script src="@js@" charset="utf-8"></script>
-        <script src="script.js" charset="utf-8"></script>
-    </head>
-    <body>
-        <div id="content">
-            <h1>Dependency Report</h1>
-            <div class="breadcrumbs">
-                <a href="index.html">Projects</a> > <span id="projectBreacrumb"></span>
-            </div>
-            <div id="insight"></div>
-            <div id="dependencies"></div>
-            <div id="footer">
-                <p>Generated by <a id="gradleVersion" href="http://www.gradle.org"></a> at <span id="generationDate"></span></p>
-            </div>
-        </div>
-    </body>
-</html>
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/tree.css b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/tree.css
index afbf8d3..c474485 100644
--- a/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/tree.css
+++ b/subprojects/diagnostics/src/main/resources/org/gradle/api/tasks/diagnostics/htmldependencyreport/tree.css
@@ -21,7 +21,7 @@
  */
 
 .jstree-default li, 
-.jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-default ins { background-image:url("../images/d.png"); background-repeat:no-repeat; background-color:transparent; }
 .jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
 .jstree-default li.jstree-last { background:transparent; }
 .jstree-default .jstree-open > ins { background-position:-72px 0; }
@@ -54,9 +54,9 @@
 .jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
 
 #vakata-dragged.jstree-default ins { background:transparent !important; }
-#vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
-#vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
-#jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }
+#vakata-dragged.jstree-default .jstree-ok { background:url("../images/d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-default .jstree-invalid { background:url("../images/d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-default { background:url("../images/d.png") -41px -57px no-repeat !important; text-indent:-100px; }
 
 .jstree-default a.jstree-search { color:aqua; }
 .jstree-default .jstree-locked a { color:silver; cursor:default; }
@@ -77,7 +77,7 @@
 .jstree-default ins,
 #vakata-dragged.jstree-default .jstree-invalid, 
 #vakata-dragged.jstree-default .jstree-ok, 
-#jstree-marker.jstree-default { _background-image:url("d.gif"); }
+#jstree-marker.jstree-default { _background-image:url("../images/d.gif"); }
 .jstree-default .jstree-open ins { _background-position:-72px 0; }
 .jstree-default .jstree-closed ins { _background-position:-54px 0; }
 .jstree-default .jstree-leaf ins { _background-position:-36px 0; }
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/plugins/HelpTasksPluginSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/plugins/HelpTasksPluginSpec.groovy
index 203acf4..c5f7ddb 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/plugins/HelpTasksPluginSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/plugins/HelpTasksPluginSpec.groovy
@@ -16,27 +16,28 @@
 
 package org.gradle.api.plugins
 
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.reporting.components.ComponentReport
 import org.gradle.api.tasks.diagnostics.*
 import org.gradle.configuration.Help
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
-import static org.gradle.configuration.ImplicitTasksConfigurer.*
-
 class HelpTasksPluginSpec extends Specification {
     final project = TestUtil.createRootProject()
 
     def "adds help tasks"() {
         when:
-        project.apply(plugin: 'help-tasks')
+        project.pluginManager.apply(HelpTasksPlugin)
 
         then:
-        hasHelpTask(HELP_TASK, Help)
-        hasHelpTask(DEPENDENCY_INSIGHT_TASK, DependencyInsightReportTask)
-        hasHelpTask(DEPENDENCIES_TASK, DependencyReportTask)
-        hasHelpTask(PROJECTS_TASK, ProjectReportTask)
-        hasHelpTask(TASKS_TASK, TaskReportTask)
-        hasHelpTask(PROPERTIES_TASK, PropertyReportTask)
+        hasHelpTask(ProjectInternal.HELP_TASK, Help)
+        hasHelpTask(HelpTasksPlugin.DEPENDENCY_INSIGHT_TASK, DependencyInsightReportTask)
+        hasHelpTask(HelpTasksPlugin.DEPENDENCIES_TASK, DependencyReportTask)
+        hasHelpTask(ProjectInternal.PROJECTS_TASK, ProjectReportTask)
+        hasHelpTask(ProjectInternal.TASKS_TASK, TaskReportTask)
+        hasHelpTask(HelpTasksPlugin.PROPERTIES_TASK, PropertyReportTask)
+        hasHelpTask(HelpTasksPlugin.COMPONENTS_TASK, ComponentReport)
     }
 
     def "tasks description reflects whether project has sub-projects or not"() {
@@ -44,32 +45,19 @@ class HelpTasksPluginSpec extends Specification {
         def child = TestUtil.createChildProject(project, "child")
 
         when:
-        project.apply(plugin: 'help-tasks')
-        child.apply(plugin: 'help-tasks')
-
-        then:
-        project.implicitTasks[TASKS_TASK].description == "Displays the tasks runnable from root project 'test' (some of the displayed tasks may belong to subprojects)."
-        child.implicitTasks[TASKS_TASK].description == "Displays the tasks runnable from project ':child'."
-    }
-
-    def "configures tasks for java plugin"() {
-        when:
-        project.apply(plugin: 'help-tasks')
-
-        then:
-        !project.implicitTasks[DEPENDENCY_INSIGHT_TASK].configuration
-
-        when:
-        project.plugins.apply(JavaPlugin)
+        project.pluginManager.apply(HelpTasksPlugin)
+        child.pluginManager.apply(HelpTasksPlugin)
 
         then:
-        project.implicitTasks[DEPENDENCY_INSIGHT_TASK].configuration == project.configurations.compile
+        project.tasks[ProjectInternal.TASKS_TASK].description == "Displays the tasks runnable from root project 'test' (some of the displayed tasks may belong to subprojects)."
+        child.tasks[ProjectInternal.TASKS_TASK].description == "Displays the tasks runnable from project ':child'."
     }
 
     private hasHelpTask(String name, Class type) {
-        def task = project.implicitTasks.getByName(name)
+        def task = project.tasks.getByName(name)
         assert type.isInstance(task)
-        assert task.group == HELP_GROUP
+        assert task.group == HelpTasksPlugin.HELP_GROUP
+        assert task.impliesSubProjects
         if (type != Help.class) {
             assert task.description.contains(project.name)
         }
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentRendererTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentRendererTest.groovy
new file mode 100644
index 0000000..1fbd553
--- /dev/null
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentRendererTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.api.reporting.components.internal
+
+import org.gradle.api.Project
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.tasks.diagnostics.internal.text.DefaultTextReportBuilder
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.logging.TestStyledTextOutput
+import org.gradle.platform.base.BinarySpec
+import org.gradle.platform.base.ComponentSpec
+import spock.lang.Specification
+
+class ComponentRendererTest extends Specification {
+    def project = Stub(Project) {
+        toString() >> "<project>"
+    }
+    def resolver = Stub(FileResolver)
+    def output = new TestStyledTextOutput()
+    def builder = new DefaultTextReportBuilder(output, resolver)
+    def sourceSetRenderer = Mock(SourceSetRenderer)
+    def binaryRenderer = Mock(BinaryRenderer)
+    def renderer = new ComponentRenderer(sourceSetRenderer, binaryRenderer)
+
+    def "renders component"() {
+        def component = Stub(ComponentSpec)
+        component.displayName >> "<component>"
+        component.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
+
+        when:
+        renderer.render(component, builder)
+
+        then:
+        output.value.startsWith("""{header}<component>
+-----------{normal}
+""")
+    }
+
+    def "renders component with no source sets"() {
+        def component = Stub(ComponentSpec)
+        component.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
+
+        when:
+        renderer.render(component, builder)
+
+        then:
+        output.value.contains("No source sets")
+    }
+
+    def "renders component with no binaries"() {
+        def component = Stub(ComponentSpec)
+        component.binaries >> new DefaultDomainObjectSet<BinarySpec>(BinarySpec)
+
+        when:
+        renderer.render(component, builder)
+
+        then:
+        output.value.contains("No binaries")
+    }
+
+    def "renders component binaries ordered by name"() {
+        def component = Stub(ComponentSpec)
+        def binaries = new DefaultDomainObjectSet<BinarySpec>(BinarySpec)
+        binaries.add(binary("cBinary"))
+        binaries.add(binary("aBinary"))
+        binaries.add(binary("bBinary"))
+        binaries.add(binary("dBinary"))
+        component.binaries >> binaries
+        binaryRenderer.render(_, _) >> { BinarySpec binary, TextReportBuilder output -> output.output.println("binary: $binary.name") }
+
+        when:
+        renderer.render(component, builder)
+
+        then:
+        output.value.contains("""Binaries
+    binary: aBinary
+    binary: bBinary
+    binary: cBinary
+    binary: dBinary
+""")
+    }
+
+    def binary(String name) {
+        Mock(BinarySpec){
+            _ * getName() >> name
+        }
+    }
+}
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentReportRendererTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentReportRendererTest.groovy
new file mode 100644
index 0000000..6f73657
--- /dev/null
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/ComponentReportRendererTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal
+import org.gradle.api.Project
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.logging.TestStyledTextOutput
+import org.gradle.platform.base.BinarySpec
+import org.gradle.platform.base.ComponentSpec
+import spock.lang.Specification
+
+class ComponentReportRendererTest extends Specification {
+    def project = Stub(Project) {
+        toString() >> "<project>"
+    }
+    def resolver = Stub(FileResolver)
+    def binaryRenderer = Stub(TypeAwareBinaryRenderer)
+    def output = new TestStyledTextOutput()
+    def renderer = new ComponentReportRenderer(resolver, binaryRenderer)
+
+    def setup() {
+        renderer.output = output
+    }
+
+    def "renders project with no components"() {
+        when:
+        renderer.startProject(project)
+        renderer.renderComponents([])
+        renderer.completeProject(project)
+        renderer.complete()
+
+        then:
+        output.value.contains("{info}No components defined for this project.{normal}")
+    }
+
+    def "renders project with single component"() {
+        def component = Stub(ComponentSpec) {
+            getDisplayName() >> "<component>"
+        }
+
+        when:
+        renderer.startProject(project)
+        renderer.renderComponents([component])
+        renderer.completeProject(project)
+        renderer.complete()
+
+        then:
+        output.value.contains("\n{header}<component>\n")
+    }
+
+    def "renders project with multiple components"() {
+        def component1 = Stub(ComponentSpec) {
+            getDisplayName() >> "<component 1>"
+        }
+        def component2 = Stub(ComponentSpec) {
+            getDisplayName() >> "<component 2>"
+        }
+
+        when:
+        renderer.startProject(project)
+        renderer.renderComponents([component1, component2])
+        renderer.completeProject(project)
+        renderer.complete()
+
+        then:
+        output.value.contains("""
+{header}<component 1>
+""")
+        output.value.contains("""
+{header}<component 2>
+""")
+    }
+
+    def "renders additional source sets"() {
+        def sourceSet1 = Stub(LanguageSourceSet)
+        def sourceSet2 = Stub(LanguageSourceSet) {
+            getDisplayName() >> "<source set>"
+        }
+        def component = Stub(ComponentSpec) {
+            getSource() >> set(LanguageSourceSet, sourceSet1)
+        }
+
+        when:
+        renderer.startProject(project)
+        renderer.renderComponents([component])
+        renderer.renderSourceSets([sourceSet1, sourceSet2])
+        renderer.completeProject(project)
+        renderer.complete()
+
+        then:
+        output.value.contains("""{header}Additional source sets
+----------------------{normal}
+<source set>
+    No source directories
+
+""")
+    }
+
+    def "renders additional binaries"() {
+        def binary1 = Stub(BinarySpec)
+        def binary2 = Stub(BinarySpec)
+        def component = Stub(ComponentSpec) {
+            getBinaries() >> set(BinarySpec, binary1)
+        }
+        binaryRenderer.render(binary2, _) >> { BinarySpec binary, TextReportBuilder builder -> builder.output.println("<binary>")}
+
+        when:
+        renderer.startProject(project)
+        renderer.renderComponents([component])
+        renderer.renderBinaries([binary2])
+        renderer.completeProject(project)
+        renderer.complete()
+
+        then:
+        output.value.contains("""{header}Additional binaries
+-------------------{normal}
+<binary>
+""")
+    }
+
+    def set(Class type, Object... values) {
+        def collection = new DefaultDomainObjectSet(type)
+        collection.addAll(values)
+        return collection
+    }
+}
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/SourceSetRendererTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/SourceSetRendererTest.groovy
new file mode 100644
index 0000000..c844a21
--- /dev/null
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/reporting/components/internal/SourceSetRendererTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components.internal
+
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.logging.StyledTextOutput
+import spock.lang.Specification
+
+class SourceSetRendererTest extends Specification {
+
+    SourceSetRenderer renderer = new SourceSetRenderer()
+    LanguageSourceSet languageSourceSet = Mock()
+    TextReportBuilder reportBuilder = Mock()
+    StyledTextOutput styledTextOutput = Mock()
+    SourceDirectorySet sourceDirectorySet = Mock()
+
+    File srcFolder1
+    File srcFolder2
+
+    def setup() {
+        _ * languageSourceSet.displayName >> "acme:sample"
+        _ * languageSourceSet.source >> sourceDirectorySet
+        _ * reportBuilder.getOutput() >> styledTextOutput
+        1 * styledTextOutput.println("Acme:sample")
+    }
+
+    def "shows sourceSet folders"() {
+        given:
+        withTwoSourceFolders()
+        1 * sourceDirectorySet.getIncludes() >> []
+        1 * sourceDirectorySet.getExcludes() >> []
+        when:
+        renderer.render(languageSourceSet, reportBuilder)
+        then:
+        1 * reportBuilder.item(srcFolder1)
+        1 * reportBuilder.item(srcFolder2)
+
+    }
+
+    def "shows includes / excludes"() {
+        given:
+        withTwoSourceFolders()
+        1 * sourceDirectorySet.getIncludes() >> includes
+        1 * sourceDirectorySet.getExcludes() >> excludes
+        when:
+        renderer.render(languageSourceSet, reportBuilder)
+        then:
+        expectedOutputs.each { key, value ->
+            1 * reportBuilder.item(key, value)
+        }
+        where:
+        includes                    | excludes                   | expectedOutputs
+        ["**/*.java"]               | ["**/gen/**"]              | [includes: "**/*.java", excludes: "**/gen/**"]
+        ["**/*.java", "**/*.scala"] | ["**/gen/**"]              | [includes: "**/*.java, **/*.scala", excludes: "**/gen/**"]
+        ["**/*.scala"]              | ["**/gen/**", "**/*.java"] | [includes: "**/*.scala", excludes: "**/gen/**, **/*.java"]
+    }
+
+    def withTwoSourceFolders() {
+        srcFolder1 = new File("src/folder1")
+        srcFolder2 = new File("src/folder2")
+        1 * sourceDirectorySet.srcDirs >> [srcFolder1, srcFolder2]
+    }
+}
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
index 91f3013..8f38f6f 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
@@ -18,6 +18,7 @@ package org.gradle.api.tasks.diagnostics;
 import org.gradle.api.Project;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
+import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.logging.StyledTextOutput;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.util.TestUtil;
@@ -62,6 +63,8 @@ public class AbstractReportTaskTest {
     public void completesRendererAtEndOfGeneration() throws IOException {
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("sequence");
+            one(renderer).setClientMetaData((BuildClientMetaData) with(notNullValue()));
+            inSequence(sequence);
             one(renderer).setOutput((StyledTextOutput) with(notNullValue()));
             inSequence(sequence);
             one(renderer).startProject(project);
@@ -83,6 +86,8 @@ public class AbstractReportTaskTest {
 
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("sequence");
+            one(renderer).setClientMetaData((BuildClientMetaData) with(notNullValue()));
+            inSequence(sequence);
             one(renderer).setOutputFile(file);
             inSequence(sequence);
             one(renderer).startProject(project);
@@ -106,7 +111,8 @@ public class AbstractReportTaskTest {
         task.setProjects(project.getAllprojects());
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("seq");
-
+            one(renderer).setClientMetaData((BuildClientMetaData) with(notNullValue()));
+            inSequence(sequence);
             one(renderer).setOutput((StyledTextOutput) with(notNullValue()));
             inSequence(sequence);
             one(renderer).startProject(project);
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskSpec.groovy
index 98bfbe6..662cb72 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskSpec.groovy
@@ -18,6 +18,7 @@ package org.gradle.api.tasks.diagnostics
 
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.specs.Spec
+import org.gradle.internal.typeconversion.UnsupportedNotationException
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
@@ -50,7 +51,7 @@ class DependencyInsightReportTaskSpec extends Specification {
         task.setDependencySpec("")
 
         then:
-        thrown(InvalidUserDataException)
+        thrown(UnsupportedNotationException)
     }
 
     def "can set spec and configuration directly"() {
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
index b3168ef..74621b7 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
@@ -61,8 +61,6 @@ public class TaskReportTaskTest {
             will(returnValue(":path"));
             allowing(project).getTasks();
             will(returnValue(taskContainer));
-            allowing(project).getImplicitTasks();
-            will(returnValue(implicitTasks));
             allowing(project).getConvention();
             will(returnValue(null));
             allowing(project).getAllprojects();
@@ -87,7 +85,7 @@ public class TaskReportTaskTest {
             allowing(project).getDefaultTasks();
             will(returnValue(testDefaultTasks));
 
-            allowing(taskContainer).actualize();
+            allowing(taskContainer).realize();
 
             allowing(taskContainer).size();
             will(returnValue(4));
@@ -144,7 +142,7 @@ public class TaskReportTaskTest {
             allowing(project).getDefaultTasks();
             will(returnValue(defaultTasks));
 
-            allowing(taskContainer).actualize();
+            allowing(taskContainer).realize();
 
             allowing(taskContainer).size();
             will(returnValue(0));
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParserSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParserSpec.groovy
index 7a62468..e3741b2 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParserSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecNotationParserSpec.groovy
@@ -16,16 +16,18 @@
 
 package org.gradle.api.tasks.diagnostics.internal.dsl
 
-import org.gradle.api.InvalidUserDataException
 import org.gradle.api.artifacts.result.DependencyResult
 import org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder
-import org.gradle.internal.typeconversion.NotationParser
 import org.gradle.api.specs.Spec
+import org.gradle.internal.typeconversion.NotationParser
+import org.gradle.internal.typeconversion.UnsupportedNotationException
 import spock.lang.Specification
 
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
 class DependencyResultSpecNotationParserSpec extends Specification {
 
-    NotationParser<Object, Spec<DependencyResult>> parser = DependencyResultSpecNotationParser.create()
+    NotationParser<Object, Spec<DependencyResult>> parser = DependencyResultSpecNotationConverter.parser()
 
     def "accepts closures"() {
         given:
@@ -75,20 +77,25 @@ class DependencyResultSpecNotationParserSpec extends Specification {
         parser.parseNotation(['not supported'])
 
         then:
-        def ex = thrown(InvalidUserDataException)
-        ex.message.contains 'not supported'
-        ex.message.contains 'DependencyInsight.dependency'
+        def ex = thrown(UnsupportedNotationException)
+        ex.message == toPlatformLineSeparators("""Cannot convert the provided notation to an object of type Spec: [not supported].
+The following types/formats are supported:
+  - Instances of Spec.
+  - Closure that returns boolean and takes a single DependencyResult as a parameter.
+  - Non-empty String or CharSequence value, for example 'some-lib' or 'org.libs:some-lib'.
+
+Please check the input for the DependencyInsight.dependency element.""")
     }
 
     def "does not accept empty Strings"() {
         when:
         parser.parseNotation('')
         then:
-        thrown(InvalidUserDataException)
+        thrown(UnsupportedNotationException)
 
         when:
         parser.parseNotation(' ')
         then:
-        thrown(InvalidUserDataException)
+        thrown(UnsupportedNotationException)
     }
 }
\ No newline at end of file
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
index bd09a92..3e8c944 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.api.tasks.diagnostics.internal.dsl
 
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.newDependency
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableDependencyResultSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableDependencyResultSpec.groovy
index 57d7bd5..2ac0633 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableDependencyResultSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableDependencyResultSpec.groovy
@@ -18,10 +18,10 @@ package org.gradle.api.tasks.diagnostics.internal.graph.nodes
 
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.artifacts.component.ComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
 import spock.lang.Specification
 
 class AbstractRenderableDependencyResultSpec extends Specification {
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableDependencyResultTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableDependencyResultTest.groovy
index c9be130..48da60f 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableDependencyResultTest.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableDependencyResultTest.groovy
@@ -20,7 +20,7 @@ import org.gradle.api.artifacts.result.ResolvedComponentResult
 import org.gradle.api.artifacts.result.ResolvedDependencyResult
 import spock.lang.Specification
 
-import static org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector.newSelector
+import static org.gradle.internal.component.external.model.DefaultModuleComponentSelector.newSelector
 import static org.gradle.api.internal.artifacts.result.ResolutionResultDataBuilder.newModule
 
 class RenderableDependencyResultTest extends Specification {
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableUnresolvedDependencyResultTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableUnresolvedDependencyResultTest.groovy
index 7ab4407..aabc285 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableUnresolvedDependencyResultTest.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/RenderableUnresolvedDependencyResultTest.groovy
@@ -19,7 +19,7 @@ import org.gradle.api.artifacts.component.ModuleComponentSelector
 import org.gradle.api.artifacts.result.UnresolvedDependencyResult
 import spock.lang.Specification
 
-import static org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector.newSelector
+import static org.gradle.internal.component.external.model.DefaultModuleComponentSelector.newSelector
 
 class RenderableUnresolvedDependencyResultTest extends Specification {
 
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/SimpleDependency.java b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/SimpleDependency.java
index 6ed1a4e..cd3b56d 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/SimpleDependency.java
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/graph/nodes/SimpleDependency.java
@@ -21,7 +21,7 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-import static org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier.newId;
+import static org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier.newId;
 
 public class SimpleDependency implements RenderableDependency {
 
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
index 003725d..1b95c77 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
@@ -17,13 +17,13 @@
 package org.gradle.api.tasks.diagnostics.internal.insight
 
 import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionMatcher
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionMatcher
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons
 import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult
 import org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
@@ -31,13 +31,14 @@ import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.
 import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.VersionSelectionReasons.FORCED
 
 class DependencyInsightReporterSpec extends Specification {
-    VersionMatcher versionMatcher = new ExactVersionMatcher()
+    def versionSelectorScheme = new DefaultVersionSelectorScheme(new DefaultVersionComparator())
+    def versionComparator = new DefaultVersionComparator()
 
     def "sorts dependencies"() {
         def dependencies = [dep("a", "x", "1.0", "2.0"), dep("a", "x", "1.5", "2.0"), dep("b", "a", "5.0"), dep("a", "z", "1.0"), dep("a", "x", "2.0")]
 
         when:
-        def sorted = new DependencyInsightReporter().prepare(dependencies, versionMatcher);
+        def sorted = new DependencyInsightReporter().prepare(dependencies, versionSelectorScheme, versionComparator);
 
         then:
         sorted.size() == 5
@@ -62,7 +63,7 @@ class DependencyInsightReporterSpec extends Specification {
         def dependencies = [dep("a", "x", "1.0", "2.0", FORCED), dep("a", "x", "1.5", "2.0", FORCED), dep("b", "a", "5.0")]
 
         when:
-        def sorted = new DependencyInsightReporter().prepare(dependencies, versionMatcher);
+        def sorted = new DependencyInsightReporter().prepare(dependencies, versionSelectorScheme, versionComparator);
 
         then:
         sorted.size() == 4
@@ -84,7 +85,7 @@ class DependencyInsightReporterSpec extends Specification {
         def dependencies = [dep("a", "x", "1.0", "2.0", CONFLICT_RESOLUTION), dep("a", "x", "2.0", "2.0", CONFLICT_RESOLUTION), dep("b", "a", "5.0", "5.0", FORCED)]
 
         when:
-        def sorted = new DependencyInsightReporter().prepare(dependencies, versionMatcher);
+        def sorted = new DependencyInsightReporter().prepare(dependencies, versionSelectorScheme, versionComparator);
 
         then:
         sorted.size() == 3
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorterSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorterSpec.groovy
index 9dae2b8..81ea594 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorterSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyResultSorterSpec.groovy
@@ -18,22 +18,24 @@ package org.gradle.api.tasks.diagnostics.internal.insight
 
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.artifacts.component.ComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentSelector
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.component.DefaultProjectComponentSelector
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.DependencyEdge
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.local.model.DefaultProjectComponentIdentifier
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
 import spock.lang.Specification
 import spock.lang.Unroll
 
 class DependencyResultSorterSpec extends Specification {
-    def matcher = new ResolverStrategy().versionMatcher
+    def versionSelectorScheme = new DefaultVersionSelectorScheme(new DefaultVersionComparator())
+    def versionComparator = new DefaultVersionComparator()
 
     @Unroll
     def "throws exception if dependencyt or requested component selector is null (#d1, #d2)"() {
         when:
-        DependencyResultSorter.sort([d1, d2], matcher)
+        DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         Throwable t = thrown(IllegalArgumentException)
@@ -53,7 +55,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(DefaultModuleComponentSelector.newSelector("org.aha", "aha", "1.0"), DefaultModuleComponentIdentifier.newId("org.gradle", "zzzz", "3.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2]
@@ -64,7 +66,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(new DefaultProjectComponentSelector(":hisProject"), DefaultModuleComponentIdentifier.newId("org.gradle", "zzzz", "3.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d2, d1]
@@ -83,7 +85,7 @@ class DependencyResultSorterSpec extends Specification {
         def d7 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "zzzz", "2.0"), DefaultModuleComponentIdentifier.newId("org.gradle", "zzzz", "3.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d5, d3, d6, d1, d2, d7, d4], matcher)
+        def sorted = DependencyResultSorter.sort([d5, d3, d6, d1, d2, d7, d4], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2, d3, d4, d5, d6, d7]
@@ -97,7 +99,7 @@ class DependencyResultSorterSpec extends Specification {
         def d5 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "3.0"), DefaultModuleComponentIdentifier.newId("org.gradle", "core", "2.2"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d3, d1, d5, d2, d4], matcher)
+        def sorted = DependencyResultSorter.sort([d3, d1, d5, d2, d4], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2, d3, d4, d5]
@@ -114,7 +116,7 @@ class DependencyResultSorterSpec extends Specification {
         def d8 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "2"), DefaultModuleComponentIdentifier.newId("org.gradle", "core", "2.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d4, d7, d1, d6, d8, d5, d3, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d4, d7, d1, d6, d8, d5, d3, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2, d3, d4, d5, d6, d7, d8]
@@ -132,7 +134,7 @@ class DependencyResultSorterSpec extends Specification {
         def d9 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "latest.zzz"), DefaultModuleComponentIdentifier.newId("org.gradle", "core", "2.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d4, d7, d1, d6, d8, d5, d3, d9, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d4, d7, d1, d6, d8, d5, d3, d9, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2, d3, d4, d5, d6, d7, d8, d9]
@@ -149,7 +151,7 @@ class DependencyResultSorterSpec extends Specification {
         def d8 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "other", "1.0"), DefaultModuleComponentIdentifier.newId("org.gradle", "other", "1.0"), DefaultModuleComponentIdentifier.newId("org.b", "a", "0.9.1"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d7, d8, d1, d3, d5, d2, d4, d6], matcher)
+        def sorted = DependencyResultSorter.sort([d7, d8, d1, d3, d5, d2, d4, d6], versionSelectorScheme, versionComparator)
 
 
         then:
@@ -169,7 +171,7 @@ class DependencyResultSorterSpec extends Specification {
         def d7 = newDependency(new DefaultProjectComponentSelector(":project5"), DefaultModuleComponentIdentifier.newId("org.gradle", "zzzz", "3.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d5, d3, d6, d1, d2, d7, d4], matcher)
+        def sorted = DependencyResultSorter.sort([d5, d3, d6, d1, d2, d7, d4], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2, d3, d4, d5, d6, d7]
@@ -180,7 +182,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "1.0"), DefaultProjectComponentIdentifier.newId(":project5"), DefaultProjectComponentIdentifier.newId(":project1"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d2, d1]
@@ -191,7 +193,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "1.0"), DefaultProjectComponentIdentifier.newId(":project6"), DefaultModuleComponentIdentifier.newId("org.gradle", "xxxx", "1.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d2, d1]
@@ -202,7 +204,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "1.0"), DefaultProjectComponentIdentifier.newId(":project6"), DefaultModuleComponentIdentifier.newId("org.gradle", "xxxx", "1.0"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d1, d2]
@@ -213,7 +215,7 @@ class DependencyResultSorterSpec extends Specification {
         def d2 = newDependency(DefaultModuleComponentSelector.newSelector("org.gradle", "core", "1.0"), DefaultProjectComponentIdentifier.newId(":project5"), DefaultProjectComponentIdentifier.newId(":project1"))
 
         when:
-        def sorted = DependencyResultSorter.sort([d1, d2], matcher)
+        def sorted = DependencyResultSorter.sort([d1, d2], versionSelectorScheme, versionComparator)
 
         then:
         sorted == [d2, d1]
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilderTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilderTest.groovy
new file mode 100644
index 0000000..c48d571
--- /dev/null
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/text/DefaultTextReportBuilderTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.diagnostics.internal.text
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.SystemProperties
+import org.gradle.logging.TestStyledTextOutput
+import org.gradle.reporting.ReportRenderer
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class DefaultTextReportBuilderTest extends Specification {
+    def output = new TestStyledTextOutput()
+    def fileResolver = Stub(FileResolver)
+    def builder = new DefaultTextReportBuilder(output, fileResolver)
+    def eol = SystemProperties.instance.getLineSeparator()
+
+    def "formats heading"() {
+        given:
+        builder.heading("heading")
+
+        expect:
+        output.value == """
+{header}------------------------------------------------------------
+heading
+------------------------------------------------------------{normal}
+
+"""
+    }
+
+    def "formats subheading"() {
+        given:
+        builder.subheading("heading")
+
+        expect:
+        output.value == """{header}heading
+-------{normal}
+"""
+    }
+
+    def "formats collection"() {
+        def renderer = Mock(ReportRenderer)
+
+        given:
+        renderer.render(_, _) >> { Number number, TextReportBuilder builder ->
+            builder.output.println("[$number]")
+        }
+
+        when:
+        builder.collection("Things", [1, 2], renderer, "things")
+
+        then:
+        output.value == """Things
+    [1]
+    [2]
+"""
+    }
+
+    def "formats empty collection"() {
+        def renderer = Mock(ReportRenderer)
+
+        when:
+        builder.collection("Things", [], renderer, "things")
+
+        then:
+        output.value == """Things
+    No things.
+"""
+    }
+
+    def "formats item"() {
+        when:
+        builder.item("some value")
+
+        then:
+        output.value == """    some value
+"""
+    }
+
+    def "formats item with file value"() {
+        def file = new File("thing")
+        fileResolver.resolveAsRelativePath(file) >> "path/thing"
+
+        when:
+        builder.item(file)
+
+        then:
+        output.value == """    path/thing
+"""
+    }
+
+    def "formats item with title"() {
+        when:
+        builder.item("the title", "the value")
+
+        then:
+        output.value == """    the title: the value
+"""
+    }
+
+    def "formats item with title and file value"() {
+        def file = new File("thing")
+        fileResolver.resolveAsRelativePath(file) >> "path/thing"
+
+        when:
+        builder.item("the title", file)
+
+        then:
+        output.value == """    the title: path/thing
+"""
+    }
+
+    def "formats multiline item" () {
+        when:
+        builder.item("first line${eol}  second line${eol}  third line")
+
+        then:
+        outputMatches """    first line
+      second line
+      third line
+"""
+    }
+
+    def "formats multiline item with title" () {
+        when:
+        builder.item("the title", "first line${eol}  second line${eol}  third line")
+
+        then:
+        outputMatches """    the title: first line
+      second line
+      third line
+"""
+    }
+
+    void outputMatches(String text) {
+        assert TextUtil.normaliseLineSeparators(output.value) == TextUtil.normaliseLineSeparators(text)
+    }
+}
diff --git a/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/AbstractComponentReportIntegrationTest.groovy b/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/AbstractComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..5cef101
--- /dev/null
+++ b/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/AbstractComponentReportIntegrationTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.api.reporting.components
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+
+abstract class AbstractComponentReportIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        settingsFile << "rootProject.name = 'test'"
+    }
+
+    boolean outputMatches(String actualOutput, String expectedOutput) {
+        String cleaned = actualOutput.substring(0, actualOutput.lastIndexOf("BUILD SUCCESSFUL"))
+        assert cleaned == expected(expectedOutput)
+        return true
+    }
+
+    String expected(String normalised) {
+        String raw = """:components
+
+------------------------------------------------------------
+Root project
+------------------------------------------------------------
+""" + normalised + """
+Note: currently not all plugins register their components, so some components may not be visible here.
+
+"""
+        return new ComponentReportOutputFormatter(toolChain).transform(raw)
+    }
+
+    AvailableToolChains.InstalledToolChain getToolChain() {
+        return AvailableToolChains.defaultToolChain
+    }
+}
diff --git a/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/ComponentReportOutputFormatter.groovy b/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/ComponentReportOutputFormatter.groovy
new file mode 100644
index 0000000..f7f97d1
--- /dev/null
+++ b/subprojects/diagnostics/src/testFixtures/groovy/org/gradle/api/reporting/components/ComponentReportOutputFormatter.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.reporting.components
+import org.gradle.api.Transformer
+import org.gradle.internal.SystemProperties
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+
+class ComponentReportOutputFormatter implements Transformer<String, String> {
+    final AvailableToolChains.InstalledToolChain toolChain
+
+    ComponentReportOutputFormatter() {
+        this.toolChain = AvailableToolChains.getDefaultToolChain()
+    }
+
+    ComponentReportOutputFormatter(AvailableToolChains.InstalledToolChain toolChain) {
+        this.toolChain = toolChain
+    }
+
+    @Override
+    String transform(String original) {
+         return original
+                .replace("Tool chain 'clang' (Clang)", toolChain.instanceDisplayName)
+                .replace("platform: current", "platform: " + NativePlatforms.defaultPlatformName)
+                .replace("\n", SystemProperties.instance.lineSeparator)
+                .replaceAll('(?m)(build/binaries/.+/)lib(\\w+).dylib$') { it[1] + OperatingSystem.current().getSharedLibraryName(it[2]) }
+                .replaceAll('(?m)(build/binaries/.+/)lib(\\w+).a$') { it[1] + OperatingSystem.current().getStaticLibraryName(it[2]) }
+                .replaceAll('(?m)(build/binaries/.+/)(\\w+)$') { it[1] + OperatingSystem.current().getExecutableName(it[2]) }
+                .replaceAll("(\\w+/)+\\w+") { it[0].replace('/', File.separator) }
+    }
+}
diff --git a/subprojects/distributions/distributions.gradle b/subprojects/distributions/distributions.gradle
index 18c38d3..1717a94 100644
--- a/subprojects/distributions/distributions.gradle
+++ b/subprojects/distributions/distributions.gradle
@@ -60,6 +60,9 @@ ext {
         into('lib') {
             from rootProject.configurations.runtime
             into('plugins') {
+                into('sonar') {
+                    from rootProject.configurations.sonar - rootProject.configurations.runtime
+                }
                 from rootProject.configurations.gradlePlugins - rootProject.configurations.runtime
             }
         }
diff --git a/subprojects/distributions/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy b/subprojects/distributions/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
index b5f581e..30079d1 100644
--- a/subprojects/distributions/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
@@ -28,6 +28,11 @@ class AllDistributionIntegrationSpec extends DistributionIntegrationSpec {
         "all"
     }
 
+    @Override
+    int getLibJarsCount() {
+        171
+    }
+
     def allZipContents() {
         given:
         TestFile contentsDir = unpackDistribution()
diff --git a/subprojects/distributions/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy b/subprojects/distributions/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
index abfab6b..ed47d7f 100644
--- a/subprojects/distributions/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
@@ -25,6 +25,11 @@ class BinDistributionIntegrationSpec extends DistributionIntegrationSpec {
         "bin"
     }
 
+    @Override
+    int getLibJarsCount() {
+        171
+    }
+
     def binZipContents() {
         given:
         TestFile contentsDir = unpackDistribution()
diff --git a/subprojects/distributions/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy b/subprojects/distributions/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
index 1037dc6..f1df5ab 100644
--- a/subprojects/distributions/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
+++ b/subprojects/distributions/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
@@ -38,20 +38,53 @@ abstract class DistributionIntegrationSpec extends AbstractIntegrationSpec {
 
     abstract String getDistributionLabel()
 
+    abstract int getLibJarsCount()
+
     def "no duplicate entries"() {
         given:
-        ZipFile zipFile = new ZipFile(zip)
+        def entriesByPath = zipEntries.findAll { !it.name.contains('/META-INF/services/') }.groupBy { it.name }
+        def dupes = entriesByPath.findAll { it.value.size() > 1 }
 
         when:
-        def entries = zipFile.entries().toList()
-        def entriesByPath = entries.groupBy { ZipEntry zipEntry -> zipEntry.name }
-        def dupes = entriesByPath.findAll { it.value.size() > 1 && !it.key.contains('/META-INF/services/') }
         def dupesWithCount = dupes.collectEntries { [it.key, it.value.size()]}
 
         then:
         dupesWithCount.isEmpty()
     }
 
+    def "all files under lib directory are jars"() {
+        when:
+        def nonJarLibEntries = libZipEntries.findAll { !it.name.endsWith(".jar") }
+
+        then:
+        nonJarLibEntries.isEmpty()
+    }
+
+    def "no additional jars are added to the distribution"() {
+        when:
+        def jarLibEntries = libZipEntries.findAll { it.name.endsWith(".jar") }
+
+        then:
+        //ME: This is not a foolproof way of checking that additional jars have not been accidentally added to the distribution
+        //but should be good enough. If this test fails for you and you did not intend to add new jars to the distribution
+        //then there is something to be fixed. If you intentionally added new jars to the distribution and this is now failing please
+        //accept my sincere apologies that you have to manually bump the numbers here.
+        jarLibEntries.size() == libJarsCount
+    }
+
+    protected List<? extends ZipEntry> getLibZipEntries() {
+        zipEntries.findAll { !it.isDirectory() && it.name.tokenize("/")[1] == "lib" }
+    }
+
+    protected List<? extends ZipEntry> getZipEntries() {
+        ZipFile zipFile = new ZipFile(zip)
+        try {
+            zipFile.entries().toList()
+        } finally {
+            zipFile.close()
+        }
+    }
+
     protected TestFile unpackDistribution(type = getDistributionLabel()) {
         TestFile zip = getZip(type)
         zip.usingNativeTools().unzipTo(testDirectory)
@@ -76,15 +109,15 @@ abstract class DistributionIntegrationSpec extends AbstractIntegrationSpec {
 
         // Core libs
         def coreLibs = contentsDir.file("lib").listFiles().findAll { it.name.startsWith("gradle-") }
-        assert coreLibs.size() == 13
+        assert coreLibs.size() == 15
         coreLibs.each { assertIsGradleJar(it) }
 
         def toolingApiJar = contentsDir.file("lib/gradle-tooling-api-${version}.jar")
         toolingApiJar.assertIsFile()
-        assert toolingApiJar.length() < 200 * 1024; // tooling api jar is the small plain tooling api jar version and not the fat jar.
+        assert toolingApiJar.length() < 240 * 1024; // tooling api jar is the small plain tooling api jar version and not the fat jar.
 
         // Plugins
-        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-core-impl-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-dependency-management-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-plugins-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-ide-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-scala-${version}.jar"))
@@ -96,8 +129,14 @@ abstract class DistributionIntegrationSpec extends AbstractIntegrationSpec {
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-maven-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-osgi-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-signing-${version}.jar"))
-        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-cpp-${version}.jar"))
         assertIsGradleJar(contentsDir.file("lib/plugins/gradle-ear-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-platform-native-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-ide-native-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-language-native-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-platform-jvm-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-language-jvm-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-language-java-${version}.jar"))
+        assertIsGradleJar(contentsDir.file("lib/plugins/gradle-language-groovy-${version}.jar"))
 
         // Docs
         contentsDir.file('getting-started.html').assertIsFile()
diff --git a/subprojects/distributions/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy b/subprojects/distributions/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
index 80ebc4e..03063bf 100644
--- a/subprojects/distributions/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
@@ -29,6 +29,11 @@ class SrcDistributionIntegrationSpec extends DistributionIntegrationSpec {
         "src"
     }
 
+    @Override
+    int getLibJarsCount() {
+        0
+    }
+
     @Requires(TestPrecondition.NOT_WINDOWS)
     def sourceZipContents() {
         given:
diff --git a/subprojects/distributions/src/toplevel/LICENSE b/subprojects/distributions/src/toplevel/LICENSE
index 3bec7a1..410cd1c 100644
--- a/subprojects/distributions/src/toplevel/LICENSE
+++ b/subprojects/distributions/src/toplevel/LICENSE
@@ -203,23 +203,7 @@
 
 
 ------------------------------------------------------------------------------
-License for the logback package
-------------------------------------------------------------------------------
-Logback License
-
-
-Logback source code and binaries are distributed under the
-GNU Lesser General Public License  as published by the Free Software Foundation.
-
-Logback: the generic, reliable, fast and flexible logging library for Java.
-
-Copyright (C) 2000-2006, QOS.ch This library is free software, you can
-redistribute it and/or modify it under the terms of the GNU Lesser General Public License
-as published by the Free Software Foundation.
-
-
-------------------------------------------------------------------------------
-License for the sl4j package
+License for the slf4j package
 ------------------------------------------------------------------------------
 SLF4J License
 
diff --git a/subprojects/distributions/src/toplevel/NOTICE b/subprojects/distributions/src/toplevel/NOTICE
index c0082da..9f8c1c7 100644
--- a/subprojects/distributions/src/toplevel/NOTICE
+++ b/subprojects/distributions/src/toplevel/NOTICE
@@ -10,7 +10,6 @@ The Apache Software Foundation (http://www.apache.org/).
 It includes the following other software:
 
 Groovy (http://groovy.codehaus.org)
-Logback (http://logback.qos.ch)
 SLF4J (http://www.slf4j.org)
 Junit (http://www.junit.org)
 JCIFS (http://jcifs.samba.org)
diff --git a/subprojects/docs/docs.gradle b/subprojects/docs/docs.gradle
index 892972d..88bf81a 100755
--- a/subprojects/docs/docs.gradle
+++ b/subprojects/docs/docs.gradle
@@ -42,7 +42,7 @@ repositories {
         }
     }
 
-    maven { url 'http://repo.gradle.org/gradle/gradle-build-internal' }
+    maven { url 'https://repo.gradle.org/gradle/gradle-build-internal' }
 }
 
 configurations {
@@ -80,8 +80,8 @@ dependencies {
     testCompile libraries.groovy
     testCompile "org.pegdown:pegdown:1.1.0"
     testCompile libraries.jsoup
-    testCompile "org.gebish:geb-spock:0.9.0-RC-1"
-    testCompile 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.28.0'
+    testCompile "org.gebish:geb-spock:0.9.3"
+    testCompile 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.42.2'
     testCompile project(":core")
 }
 
@@ -121,9 +121,9 @@ tasks.withType(UserGuideTransformTask) {
 
     if (name in ["pdfUserguideDocbook", "userguideFragmentSrc"]) {
         // These will only be valid for releases, but that's ok
-        javadocUrl = "http://www.gradle.org/doc/${->version}/javadoc"
-        groovydocUrl = "http://www.gradle.org/doc/${->version}/groovydoc"
-        dsldocUrl = "http://www.gradle.org/doc/${->version}/dsl"
+        javadocUrl = "http://www.gradle.org/docs/${->version}/javadoc"
+        groovydocUrl = "http://www.gradle.org/docs/${->version}/groovydoc"
+        dsldocUrl = "http://www.gradle.org/docs/${->version}/dsl"
     } else {
         javadocUrl = '../javadoc'
         groovydocUrl = '../groovydoc'
@@ -168,7 +168,8 @@ task samples(type: ExtractSnippetsTask) {
 
     // Resources that should not be filtered
     exclude 'userguide/tutorial/antLoadfileResources/**'
-    exclude 'native-binaries/cunit/lib/**'
+    exclude 'native-binaries/cunit/libs/**'
+    exclude 'native-binaries/google-test/libs/**'
 
     destDir = samplesDir
     snippetsDir = new File(buildDir, 'snippets')
@@ -177,7 +178,8 @@ task samples(type: ExtractSnippetsTask) {
             from samplesSrcDir
             into samplesDir
             include 'userguide/tutorial/antLoadfileResources/**'
-            include 'native-binaries/cunit/lib/**'
+            include 'native-binaries/cunit/libs/**'
+            include 'native-binaries/google-test/libs/**'
         }
     }
 }
@@ -252,6 +254,7 @@ task dslHtml(type: Docbook2Xhtml) {
     resources = cssFiles + fileTree(dslSrcDir) {
         include '*.js'
     }
+    ext.entryPoint = "$destDir/index.html"
 }
 
 // This is used in the distribution and for the online version
@@ -279,6 +282,7 @@ task userguideHtml(type: Docbook2Xhtml) {
     resources = fileTree(userguideSrcDir) {
         include 'img/*.png'
     } + cssFiles
+    ext.entryPoint = "$destDir/userguide.html"
 }
 
 task userguideSingleHtml(type: Docbook2Xhtml) {
@@ -288,6 +292,7 @@ task userguideSingleHtml(type: Docbook2Xhtml) {
     resources = fileTree(userguideSrcDir) {
         include 'img/*.png'
     } + cssFiles
+    ext.entryPoint = destFile
 }
 
 task pdfUserguideXhtml(type: Docbook2Xhtml) {
@@ -297,17 +302,20 @@ task pdfUserguideXhtml(type: Docbook2Xhtml) {
     resources = fileTree(userguideSrcDir) {
         include 'img/*.png'
     } + cssFiles
+    ext.entryPoint = destFile
 }
 
 task userguidePdf(type: Xhtml2Pdf, dependsOn: pdfUserguideXhtml) {
-    inputs.files cssFiles
+    inputs.files fileTree(userguideSrcDir) {
+        include 'img/*.png'
+    } + cssFiles
     sourceFile = pdfUserguideXhtml.destFile
     destFile = new File(userguideDir, 'userguide.pdf')
     classpath = configurations.userGuideTask
 }
 
-def javaApiUrl = "http://docs.oracle.com/javase/1.5.0/docs/api/"
-def groovyApiUrl = "http://groovy.codehaus.org/gapi/"
+def javaApiUrl = "https://docs.oracle.com/javase/6/docs/api"
+def groovyApiUrl = "http://docs.groovy-lang.org/docs/groovy-${versions.groovy}/html/gapi"
 
 task javadocAll(type: Javadoc) {
     ext.stylesheetFile = file("src/docs/css/javadoc.css")
@@ -317,28 +325,40 @@ task javadocAll(type: Javadoc) {
     options.encoding = 'utf-8'
     options.docEncoding = 'utf-8'
     options.charSet = 'utf-8'
+    if (JavaVersion.current().isJava8Compatible()) {
+        options.addStringOption 'Xdoclint:none', '-quiet'
+    }
     options.addStringOption "stylesheetfile", stylesheetFile.absolutePath
     source publicGroovyProjects.collect {project -> project.sourceSets.main.allJava }
     destinationDir = new File(docsDir, 'javadoc')
     classpath = files(publicGroovyProjects.collect {project -> [project.sourceSets.main.compileClasspath, project.sourceSets.main.output] })
-    include 'org/gradle/api/**'
     include 'org/gradle/*'
+    include 'org/gradle/api/**'
+    include 'org/gradle/buildinit/**'
     include 'org/gradle/external/javadoc/**'
-    include 'org/gradle/language/**'
     include 'org/gradle/ide/**'
-    include 'org/gradle/nativebinaries/**'
-    include 'org/gradle/process/**'
+    include 'org/gradle/ivy/**'
+    include 'org/gradle/jvm/**'
+    include 'org/gradle/language/**'
+    include 'org/gradle/maven/**'
+    include 'org/gradle/nativeplatform/**'
+    include 'org/gradle/platform/**'
+    include 'org/gradle/play/**'
+    include 'org/gradle/plugin/use/*'
     include 'org/gradle/plugins/**'
+    include 'org/gradle/process/**'
     include 'org/gradle/testfixtures/**'
-    include 'org/gradle/tooling/**'
     include 'org/gradle/testing/jacoco/**'
-    include 'org/gradle/buildinit/**'
+    include 'org/gradle/sonar/runner/**'
+    include 'org/gradle/tooling/**'
+    include 'org/gradle/model/**'
     exclude '**/internal/**'
     options.links(javaApiUrl, groovyApiUrl, "http://maven.apache.org/ref/2.2.1/maven-core/apidocs",
         "http://maven.apache.org/ref/2.2.1/maven-model/apidocs")
     doFirst {
         title = "Gradle API $version"
     }
+    ext.entryPoint = "$destinationDir/index.html"
 }
 
 task configureGroovydocAll {
@@ -358,7 +378,11 @@ task groovydocAll(type: Groovydoc, dependsOn: configureGroovydocAll) {
     group = 'documentation'
     source publicGroovyProjects.collect {project -> project.sourceSets.main.groovy + project.sourceSets.main.java }
     destinationDir = new File(docsDir, 'groovydoc')
-    classpath = javadocAll.classpath
+
+    // Groovydoc runs static initializers, and at least ProjectBuilder's initializers depend on runtime classes
+    // http://jira.codehaus.org/browse/GROOVY-7096
+    classpath = files(publicGroovyProjects.collect {project -> [project.sourceSets.main.runtimeClasspath, project.sourceSets.main.output] })
+
     includes = javadocAll.includes
     excludes = javadocAll.excludes
     doFirst {
@@ -370,6 +394,7 @@ task groovydocAll(type: Groovydoc, dependsOn: configureGroovydocAll) {
         def index = new File(destinationDir, "index.html")
         index.text = index.text.replace("{todo.title}", windowTitle) // workaround groovydoc bug
     }
+    ext.entryPoint = "$destinationDir/index.html"
 }
 
 task checkstyleApi(type: Checkstyle) {
@@ -419,12 +444,22 @@ task releaseNotes(type: Copy) {
     from releaseNotesMarkdown
     jsoup.plugins "src/transforms/release-notes.gradle"
     filter(ReplaceTokens, tokens: [version: project.version.toString(), versionBase: rootProject.versionBase])
-}
-
-task viewReleaseNotes(dependsOn: releaseNotes) {
-    group = "release notes"
-    doLast {
-        Class.forName("java.awt.Desktop").newInstance().browse(new File(releaseNotes.destinationDir, releaseNotes.fileName).toURI())
+    ext.entryPoint = file("$docsDir/$fileName")
+}
+
+tasks.addRule("view«Doc Task Name» - Opens entry point") { String taskName ->
+    if (taskName.startsWith("view")) {
+        def realTaskName = (taskName - "view")
+        realTaskName = realTaskName[0].toLowerCase() + realTaskName[1..-1]
+        def task = tasks.findByName(realTaskName)
+        if (task && task.hasProperty("entryPoint")) {
+            tasks.create(taskName) {
+                dependsOn task
+                doLast {
+                    Class.forName("java.awt.Desktop").newInstance().browse(file(task.entryPoint).toURI())
+                }
+            }
+        }
     }
 }
 
diff --git a/subprojects/docs/src/docs/css/base.css b/subprojects/docs/src/docs/css/base.css
index a673e35..d01e0df 100644
--- a/subprojects/docs/src/docs/css/base.css
+++ b/subprojects/docs/src/docs/css/base.css
@@ -120,7 +120,7 @@ caption {background:#eee;}
 }
 
 body {
-  font-size: 14px;
+  font-size: 16px;
   font-family: 'Lato', Arial, serif;
   margin: 28px 28px 14px;
   color: #333;
diff --git a/subprojects/docs/src/docs/css/dsl.css b/subprojects/docs/src/docs/css/dsl.css
index 36c47f8..31068aa 100644
--- a/subprojects/docs/src/docs/css/dsl.css
+++ b/subprojects/docs/src/docs/css/dsl.css
@@ -1,6 +1,6 @@
 
 .caution {
-  font-size: 13px;
+  font-size: 15px;
   color: #AF5252;
   background-color: pink;
   text-transform: lowercase;
@@ -17,7 +17,7 @@
 
 table .caution {
   float: left;
-  font-size: 12px;
+  font-size: 13px;
   margin-right: 6px;
   margin-top: 2px;
   margin-bottom: 0px;
@@ -58,7 +58,7 @@ table .caution {
     margin-bottom: 0.4em;
     font-weight: bold;
     color: #505050;
-    font-size: 15px
+    font-size: 16px
 }
 
 .sidebar li.selected {
diff --git a/subprojects/docs/src/docs/css/release-notes.css b/subprojects/docs/src/docs/css/release-notes.css
index 1c41eaa..46e267c 100644
--- a/subprojects/docs/src/docs/css/release-notes.css
+++ b/subprojects/docs/src/docs/css/release-notes.css
@@ -30,10 +30,6 @@ section.minor-detail {
   padding: 0 10px;
 }
 
-ul.toc {
-  font-size: 15px
-}
-
 ul.toc-sub {
   margin-bottom: 0px;
 }
@@ -118,7 +114,7 @@ section.footer {
 }
 
 #tiptip_content {
-    font-size: 13px;
+    font-size: 14px;
     color: #717171;
     padding: 6px 10px;
     border: 1px solid #8B8B8B;
diff --git a/subprojects/docs/src/docs/css/userguide.css b/subprojects/docs/src/docs/css/userguide.css
index 9a4d2ac..f45f025 100644
--- a/subprojects/docs/src/docs/css/userguide.css
+++ b/subprojects/docs/src/docs/css/userguide.css
@@ -15,10 +15,6 @@
     margin-top: 1.5em;
 }
 
-div.toc {
-    font-size: 15px;
-}
-
 div.toc p { /* Is "Table Of Contents" header */
     font-size: 28px;
     color: #007042;
diff --git a/subprojects/docs/src/docs/dsl/dsl.xml b/subprojects/docs/src/docs/dsl/dsl.xml
index 767e49b..9614d5a 100644
--- a/subprojects/docs/src/docs/dsl/dsl.xml
+++ b/subprojects/docs/src/docs/dsl/dsl.xml
@@ -143,6 +143,9 @@
                 <td>org.gradle.api.Script</td>
             </tr>
             <tr>
+                <td>org.gradle.jvm.toolchain.JavaToolChain</td>
+            </tr>
+            <tr>
                 <td>org.gradle.api.tasks.SourceSet</td>
             </tr>
             <tr>
@@ -158,6 +161,15 @@
                 <td>org.gradle.api.artifacts.ResolutionStrategy</td>
             </tr>
             <tr>
+                <td>org.gradle.api.artifacts.query.ArtifactResolutionQuery</td>
+            </tr>
+            <tr>
+                <td>org.gradle.api.artifacts.ComponentSelection</td>
+            </tr>
+            <tr>
+                <td>org.gradle.api.artifacts.ComponentSelectionRules</td>
+            </tr>
+            <tr>
                 <td>org.gradle.api.dsl.ConventionProperty</td>
             </tr>
             <tr>
@@ -191,7 +203,7 @@
                 <td>org.gradle.api.publish.ivy.IvyArtifactSet</td>
             </tr>
             <tr>
-                <td>org.gradle.api.publish.ivy.IvyModuleDescriptor</td>
+                <td>org.gradle.api.publish.ivy.IvyModuleDescriptorSpec</td>
             </tr>
             <tr>
                 <td>org.gradle.api.publish.maven.MavenPublication</td>
@@ -205,6 +217,18 @@
             <tr>
                 <td>org.gradle.api.publish.maven.MavenPom</td>
             </tr>
+            <tr>
+                <td>org.gradle.plugin.use.PluginDependenciesSpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.plugin.use.PluginDependencySpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.api.resources.ResourceHandler</td>
+            </tr>
+            <tr>
+                <td>org.gradle.api.resources.TextResourceFactory</td>
+            </tr>
         </table>
     </section>
 
@@ -250,6 +274,12 @@
             <tr>
                 <td>org.gradle.api.tasks.diagnostics.PropertyReportTask</td>
             </tr>
+            <tr>
+                <td>org.gradle.api.reporting.components.ComponentReport</td>
+            </tr>
+            <tr>
+                <td>org.gradle.api.reporting.model.ModelReport</td>
+            </tr>
         </table>
     </section>
 
@@ -274,15 +304,12 @@
                 <td>org.gradle.api.tasks.Copy</td>
             </tr>
             <tr>
-                <td>org.gradle.api.tasks.application.CreateStartScripts</td>
+                <td>org.gradle.jvm.application.tasks.CreateStartScripts</td>
             </tr>
             <tr>
                 <td>org.gradle.api.tasks.Delete</td>
             </tr>
             <tr>
-                <td>org.gradle.api.tasks.Directory</td>
-            </tr>
-            <tr>
                 <td>org.gradle.plugins.ear.Ear</td>
             </tr>
             <tr>
@@ -367,6 +394,9 @@
                 <td>org.gradle.api.plugins.sonar.SonarAnalyze</td>
             </tr>
             <tr>
+                <td>org.gradle.sonar.runner.tasks.SonarRunner</td>
+            </tr>
+            <tr>
                 <td>org.gradle.api.tasks.Sync</td>
             </tr>
             <tr>
@@ -478,49 +508,79 @@
         <table>
             <title>Native component core types</title>
             <tr>
-                <td>org.gradle.nativebinaries.ProjectNativeComponent</td>
+                <td>org.gradle.nativeplatform.NativeExecutable</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.Executable</td>
+                <td>org.gradle.nativeplatform.NativeLibrary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.Library</td>
+                <td>org.gradle.nativeplatform.NativeBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.NativeBinary</td>
+                <td>org.gradle.nativeplatform.NativeExecutableBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.ProjectNativeBinary</td>
+                <td>org.gradle.nativeplatform.SharedLibraryBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.ExecutableBinary</td>
+                <td>org.gradle.nativeplatform.StaticLibraryBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.SharedLibraryBinary</td>
+                <td>org.gradle.nativeplatform.PrebuiltLibrary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.StaticLibraryBinary</td>
+                <td>org.gradle.nativeplatform.PrebuiltSharedLibraryBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.BuildType</td>
+                <td>org.gradle.nativeplatform.PrebuiltStaticLibraryBinary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.Flavor</td>
+                <td>org.gradle.nativeplatform.NativeComponentSpec</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.platform.Platform</td>
+                <td>org.gradle.nativeplatform.NativeExecutableSpec</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.toolchain.ToolChain</td>
+                <td>org.gradle.nativeplatform.NativeLibrarySpec</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.toolchain.Gcc</td>
+                <td>org.gradle.nativeplatform.NativeBinarySpec</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.toolchain.Clang</td>
+                <td>org.gradle.nativeplatform.NativeExecutableBinarySpec</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.toolchain.VisualCpp</td>
+                <td>org.gradle.nativeplatform.SharedLibraryBinarySpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.StaticLibraryBinarySpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.BuildType</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.Flavor</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.toolchain.Gcc</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.toolchain.Clang</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.toolchain.VisualCpp</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.test.NativeTestSuiteSpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec</td>
+            </tr>
+            <tr>
+                <td>org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec</td>
             </tr>
             <tr>
                 <td>org.gradle.language.assembler.AssemblerSourceSet</td>
@@ -546,9 +606,6 @@
             <tr>
                 <td>org.gradle.ide.visualstudio.VisualStudioSolution</td>
             </tr>
-            <tr>
-                <td>org.gradle.nativebinaries.test.cunit.CUnitTestSuite</td>
-            </tr>
         </table>
     </section>
     <section>
@@ -557,35 +614,38 @@
         <table>
             <title>Native component task types</title>
             <tr>
-                <td>org.gradle.nativebinaries.language.cpp.tasks.CppCompile</td>
+                <td>org.gradle.language.cpp.tasks.CppCompile</td>
+            </tr>
+            <tr>
+                <td>org.gradle.language.c.tasks.CCompile</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.language.c.tasks.CCompile</td>
+                <td>org.gradle.language.assembler.tasks.Assemble</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.language.assembler.tasks.Assemble</td>
+                <td>org.gradle.language.objectivec.tasks.ObjectiveCCompile</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile</td>
+                <td>org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile</td>
+                <td>org.gradle.language.rc.tasks.WindowsResourceCompile</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.language.rc.tasks.WindowsResourceCompile</td>
+                <td>org.gradle.nativeplatform.tasks.LinkExecutable</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.tasks.LinkExecutable</td>
+                <td>org.gradle.nativeplatform.tasks.LinkSharedLibrary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.tasks.LinkSharedLibrary</td>
+                <td>org.gradle.nativeplatform.tasks.CreateStaticLibrary</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.tasks.CreateStaticLibrary</td>
+                <td>org.gradle.nativeplatform.tasks.InstallExecutable</td>
             </tr>
             <tr>
-                <td>org.gradle.nativebinaries.tasks.InstallExecutable</td>
+                <td>org.gradle.nativeplatform.test.tasks.RunTestExecutable</td>
             </tr>
         </table>
     </section>
-</book>
\ No newline at end of file
+</book>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectCollection.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectCollection.xml
new file mode 100644
index 0000000..791735e
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectCollection.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>getByName</td>
+            </tr>
+            <tr>
+                <td>getAt</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectContainer.xml
new file mode 100644
index 0000000..4dddde4
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectContainer.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>create</td>
+            </tr>
+            <tr>
+                <td>maybeCreate</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectList.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectList.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectSet.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivecpp.tasks.ObjectiveCppCompile.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.api.NamedDomainObjectSet.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.Project.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.Project.xml
index fe76585..ce14b0e 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.Project.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.Project.xml
@@ -38,9 +38,6 @@
                 <td>dependencies</td>
             </tr>
             <tr>
-                <td>dependsOnProjects</td>
-            </tr>
-            <tr>
                 <td>defaultTasks</td>
             </tr>
             <tr>
@@ -71,9 +68,6 @@
                 <td>path</td>
             </tr>
             <tr>
-                <td>plugins</td>
-            </tr>
-            <tr>
                 <td>project</td>
             </tr>
             <tr>
@@ -147,9 +141,6 @@
                 <td>buildscript</td>
             </tr>
             <tr>
-                <td>apply</td>
-            </tr>
-            <tr>
                 <td>file</td>
             </tr>
             <tr>
@@ -201,18 +192,9 @@
                 <td>task</td>
             </tr>
             <tr>
-                <td>dependsOn</td>
-            </tr>
-            <tr>
                 <td>evaluationDependsOn</td>
             </tr>
             <tr>
-                <td>childrenDependOnMe</td>
-            </tr>
-            <tr>
-                <td>dependsOnChildren</td>
-            </tr>
-            <tr>
                 <td>findProject</td>
             </tr>
             <tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelection.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelection.xml
new file mode 100644
index 0000000..1659e09
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelection.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+                <tr>
+                    <td>requested</td>
+                </tr>
+                <tr>
+                    <td>candidate</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>reject</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelectionRules.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelectionRules.xml
new file mode 100644
index 0000000..ec06e3e
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ComponentSelectionRules.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>all</td>
+            </tr>
+            <tr>
+                <td>withModule</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ConfigurationContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ConfigurationContainer.xml
index 247511a..251af1d 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ConfigurationContainer.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ConfigurationContainer.xml
@@ -24,9 +24,6 @@
                 <td>getAt</td>
             </tr>
             <tr>
-                <td>add</td>
-            </tr>
-            <tr>
                 <td>detachedConfiguration</td>
             </tr>
         </table>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ResolutionStrategy.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ResolutionStrategy.xml
index e5bc1b3..a5cb5c2 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ResolutionStrategy.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.ResolutionStrategy.xml
@@ -10,6 +10,9 @@
             <tr>
                 <td>forcedModules</td>
             </tr>
+            <tr>
+                <td>componentSelection</td>
+            </tr>
         </table>
     </section>
     <section>
@@ -35,6 +38,9 @@
             <tr>
                 <td>eachDependency</td>
             </tr>
+            <tr>
+                <td>componentSelection</td>
+            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentMetadataHandler.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentMetadataHandler.xml
new file mode 100644
index 0000000..ec06e3e
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentMetadataHandler.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>all</td>
+            </tr>
+            <tr>
+                <td>withModule</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler.xml
new file mode 100644
index 0000000..33a04ee
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>module</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.xml
index 1acf8f3..4578f38 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.DependencyHandler.xml
@@ -10,6 +10,9 @@
             <tr>
                 <td>components</td>
             </tr>
+            <tr>
+                <td>modules</td>
+            </tr>
         </table>
     </section>
     <section>
@@ -41,6 +44,12 @@
             <tr>
                 <td>components</td>
             </tr>
+            <tr>
+                <td>modules</td>
+            </tr>
+            <tr>
+                <td>createArtifactResolutionQuery</td>
+            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.xml
index 74a90e7..19fd507 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.xml
@@ -40,9 +40,6 @@
                 <td>maven</td>
             </tr>
             <tr>
-                <td>mavenRepo</td>
-            </tr>
-            <tr>
                 <td>mavenCentral</td>
             </tr>
             <tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.query.ArtifactResolutionQuery.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.query.ArtifactResolutionQuery.xml
new file mode 100644
index 0000000..18295ee
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.query.ArtifactResolutionQuery.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>forComponents</td>
+            </tr>
+            <tr>
+                <td>withArtifacts</td>
+            </tr>
+            <tr>
+                <td>execute</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.ArtifactRepository.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.ArtifactRepository.xml
new file mode 100644
index 0000000..cb561a1
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.ArtifactRepository.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>name</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.AuthenticationSupported.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.AuthenticationSupported.xml
new file mode 100644
index 0000000..ec8541f
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.artifacts.repositories.AuthenticationSupported.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2015 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>credentials</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>credentials</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginAware.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginAware.xml
new file mode 100644
index 0000000..3aafccd
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginAware.xml
@@ -0,0 +1,31 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>plugins</td>
+            </tr>
+            <tr>
+                <td>pluginManager</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>apply</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginManager.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginManager.xml
new file mode 100644
index 0000000..76673c1
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.PluginManager.xml
@@ -0,0 +1,34 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>apply</td>
+            </tr>
+            <tr>
+                <td>findPlugin</td>
+            </tr>
+            <tr>
+                <td>hasPlugin</td>
+            </tr>
+            <tr>
+                <td>withPlugin</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrTask.xml
index f64e416..e914bd5 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrTask.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.antlr.AntlrTask.xml
@@ -15,6 +15,7 @@
             <tr><td>traceLexer</td><td><literal>false</literal></td></tr>
             <tr><td>traceParser</td><td><literal>false</literal></td></tr>
             <tr><td>traceTreeWalker</td><td><literal>false</literal></td></tr>
+            <tr><td>maxHeapSize</td><td><literal>null</literal></td></tr>
             <tr><td>source</td><td><literal><replaceable>sourceSet</replaceable>.antlr</literal></td></tr>
         </table>
     </section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Checkstyle.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Checkstyle.xml
index 15150d6..6cba897 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Checkstyle.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Checkstyle.xml
@@ -13,6 +13,10 @@
                 <td><literal><replaceable>sourceSet</replaceable>.output</literal></td>
             </tr>
             <tr>
+                <td>config</td>
+                <td><literal>project.checkstyle.config</literal></td>
+            </tr>
+            <tr>
                 <td>configFile</td>
                 <td><literal>project.checkstyle.configFile</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CheckstyleExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CheckstyleExtension.xml
index 79de687..1535522 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CheckstyleExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CheckstyleExtension.xml
@@ -6,33 +6,31 @@
                 <tr>
                     <td>Name</td>
                     <td>Default with <literal>checkstyle</literal> plugin</td>
-                    <td>Default with <literal>code-quality</literal> plugin</td>
                 </tr>
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>5.5</literal></td>
-                <td><literal>5.5</literal></td>
+                <td><literal>5.9</literal></td>
             </tr>
             <tr>
                 <td>reportsDir</td>
                 <td><literal><replaceable>${project.reporting.baseDir}</replaceable>/checkstyle</literal></td>
-                <td><literal>project.checkstyleResultsDir</literal></td>
+            </tr>
+            <tr>
+                <td>config</td>
+                <td><literal><replaceable>${project.projectDir}</replaceable>/config/checkstyle/checkstyle.xml</literal></td>
             </tr>
             <tr>
                 <td>configFile</td>
                 <td><literal><replaceable>${project.projectDir}</replaceable>/config/checkstyle/checkstyle.xml</literal></td>
-                <td><literal>project.checkstyleConfigFile</literal></td>
             </tr>
             <tr>
                 <td>configProperties</td>
                 <td><literal>[:]</literal></td>
-                <td><literal>project.checkstyleProperties</literal></td>
             </tr>
             <tr>
                 <td>showViolations</td>
                 <td><literal>true</literal></td>
-                <td><literal>true</literal></td>
             </tr>
         </table>
     </section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarc.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarc.xml
index b0babdf..d7fe211 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarc.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarc.xml
@@ -9,6 +9,10 @@
                 </tr>
             </thead>
             <tr>
+                <td>config</td>
+                <td><literal>project.codenarc.config</literal></td>
+            </tr>
+            <tr>
                 <td>configFile</td>
                 <td><literal>project.codenarc.configFile</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarcExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarcExtension.xml
index 9c22989..3339a62 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarcExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.CodeNarcExtension.xml
@@ -6,43 +6,39 @@
                 <tr>
                     <td>Name</td>
                     <td>Default with <literal>codenarc</literal> plugin</td>
-                    <td>Default with <literal>code-quality</literal> plugin</td>
                 </tr>
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>0.16.1</literal></td>
-                <td><literal>0.16.1</literal></td>
+                <td><literal>0.23</literal></td>
             </tr>
             <tr>
                 <td>reportsDir</td>
                 <td><literal><replaceable>${project.reporting.baseDir}</replaceable>/codenarc</literal></td>
-                <td><literal>project.codeNarcReportsDir</literal></td>
+            </tr>
+            <tr>
+                <td>config</td>
+                <td><literal><replaceable>${project.projectDir}</replaceable>/config/codenarc/codenarc.xml</literal></td>
             </tr>
             <tr>
                 <td>configFile</td>
                 <td><literal><replaceable>${project.projectDir}</replaceable>/config/codenarc/codenarc.xml</literal></td>
-                <td><literal>project.codeNarcConfigFile</literal></td>
             </tr>
             <tr>
                 <td>maxPriority1Violations</td>
                 <td><literal>0</literal></td>
-                <td><literal>0</literal></td>
             </tr>
             <tr>
                 <td>maxPriority2Violations</td>
                 <td><literal>0</literal></td>
-                <td><literal>0</literal></td>
             </tr>
             <tr>
                 <td>maxPriority3Violations</td>
                 <td><literal>0</literal></td>
-                <td><literal>0</literal></td>
             </tr>
             <tr>
                 <td>reportFormat</td>
                 <td><literal>html</literal></td>
-                <td><literal>project.codeNarcReportsFormat</literal></td>
             </tr>
         </table>
     </section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugs.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugs.xml
index 6cc225b..cebbc3c 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugs.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugs.xml
@@ -53,6 +53,14 @@
                 <td><literal>project.findbugs.omitVisitors</literal></td>
             </tr>
             <tr>
+                <td>includeFilterConfig</td>
+                <td><literal>project.findbugs.includeFilterConfig</literal></td>
+            </tr>
+            <tr>
+                <td>excludeFilterConfig</td>
+                <td><literal>project.findbugs.excludeFilterConfig</literal></td>
+            </tr>
+            <tr>
                 <td>includeFilter</td>
                 <td><literal>project.findbugs.includeFilter</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugsExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugsExtension.xml
index 87e9a5d..9b3ede5 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugsExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.FindBugsExtension.xml
@@ -10,7 +10,7 @@
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>2.0.1</literal></td>
+                <td><literal>3.0.1</literal></td>
             </tr>
             <tr>
                 <td>reportsDir</td>
@@ -33,6 +33,14 @@
                 <td><literal>[]</literal></td>
             </tr>
             <tr>
+                <td>includeFilterConfig</td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
+                <td>excludeFilterConfig</td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
                 <td>includeFilter</td>
                 <td><literal>null</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.GroovyCodeQualityPluginConvention.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.GroovyCodeQualityPluginConvention.xml
deleted file mode 100644
index 5ac5d1e..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.GroovyCodeQualityPluginConvention.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                    <td>Default with <literal>code-quality</literal> and <literal>groovy</literal> plugin</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>codeNarcConfigFileName</td>
-                <td><literal>'config/codenarc/codenarc.xml'</literal></td>
-            </tr>
-            <tr>
-                <td>codeNarcConfigFile</td>
-                <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.codeNarcConfigFileName}</replaceable></literal></td>
-            </tr>
-            <tr>
-                <td>codeNarcReportsDirName</td>
-                <td><literal>'codenarc'</literal></td>
-            </tr>
-            <tr>
-                <td>codeNarcReportsDir</td>
-                <td><literal><replaceable>${project.reporting.baseDir}</replaceable>/<replaceable>${project.codeNarcReportsDirName}</replaceable></literal></td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.JavaCodeQualityPluginConvention.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.JavaCodeQualityPluginConvention.xml
deleted file mode 100644
index 65c9658..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.JavaCodeQualityPluginConvention.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                    <td>Default with <literal>code-quality</literal> and <literal>java</literal> plugin</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>checkstyleConfigFileName</td>
-                <td><literal>'config/checkstyle/checkstyle.xml'</literal></td>
-            </tr>
-            <tr>
-                <td>checkstyleConfigFile</td>
-                <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.checkstyleConfigFileName}</replaceable></literal></td>
-            </tr>
-            <tr>
-                <td>checkstyleResultsDirName</td>
-                <td><literal>checkstyle</literal></td>
-            </tr>
-            <tr>
-                <td>checkstyleResultsDir</td>
-                <td><literal><replaceable>${project.buildDir}</replaceable>/<replaceable>${project.checkstyleResultsDirName}</replaceable></literal></td>
-            </tr>
-            <tr>
-                <td>checkstyleProperties</td>
-                <td><literal>[:]</literal></td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Pmd.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Pmd.xml
index e5ba89a..f1b5b4a 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Pmd.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.Pmd.xml
@@ -17,6 +17,10 @@
                 <td><literal>project.pmd.ruleSets</literal></td>
             </tr>
             <tr>
+                <td>ruleSetConfig</td>
+                <td><literal>project.pmd.ruleSetConfig</literal></td>
+            </tr>
+            <tr>
                 <td>ruleSetFiles</td>
                 <td><literal>project.pmd.ruleSetFiles</literal></td>
             </tr>
@@ -25,6 +29,10 @@
                 <td><literal>project.pmd.ignoreFailures</literal></td>
             </tr>
             <tr>
+                <td>consoleOutput</td>
+                <td><literal>project.pmd.consoleOutput</literal></td>
+            </tr>
+            <tr>
                 <td>targetJdk</td>
                 <td><literal>project.pmd.targetJdk</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.PmdExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.PmdExtension.xml
index b8316e8..0c94149 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.PmdExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.plugins.quality.PmdExtension.xml
@@ -10,7 +10,7 @@
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>4.3</literal></td>
+                <td><literal>5.2.3</literal></td>
             </tr>
             <tr>
                 <td>reportsDir</td>
@@ -21,6 +21,10 @@
                 <td><literal>["basic"]</literal></td>
             </tr>
             <tr>
+                <td>ruleSetConfig</td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
                 <td>ruleSetFiles</td>
                 <td><literal>[]</literal></td>
             </tr>
@@ -28,6 +32,10 @@
                 <td>targetJdk</td>
                 <td><literal>project.sourceCompatibility</literal></td>
             </tr>
+            <tr>
+                <td>consoleOutput</td>
+                <td><literal>false</literal></td>
+            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptor.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptor.xml
deleted file mode 100644
index fbf89fd..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptor.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>withXml</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptorSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptorSpec.xml
new file mode 100644
index 0000000..7915400
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.ivy.IvyModuleDescriptorSpec.xml
@@ -0,0 +1,37 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>branch</td>
+            </tr>
+            <tr>
+                <td>status</td>
+            </tr>
+            <tr>
+                <td>extraInfo</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>withXml</td>
+            </tr>
+            <tr>
+                <td>extraInfo</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.MavenPom.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.MavenPom.xml
index fbf89fd..ea95f99 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.MavenPom.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.MavenPom.xml
@@ -7,6 +7,9 @@
                     <td>Name</td>
                 </tr>
             </thead>
+            <tr>
+                <td>packaging</td>
+            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.AbstractPublishToMaven.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.AbstractPublishToMaven.xml
new file mode 100644
index 0000000..fe21dd1
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.AbstractPublishToMaven.xml
@@ -0,0 +1,25 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>publication</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.PublishToMavenRepository.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.PublishToMavenRepository.xml
index 31d4151..3f2e2d6 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.PublishToMavenRepository.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.publish.maven.tasks.PublishToMavenRepository.xml
@@ -7,7 +7,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr><td>publication</td></tr>
             <tr><td>repository</td></tr>
         </table>
     </section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.reporting.components.ComponentReport.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.reporting.components.ComponentReport.xml
new file mode 100644
index 0000000..f46d04e
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.reporting.components.ComponentReport.xml
@@ -0,0 +1,22 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.reporting.model.ModelReport.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.api.reporting.model.ModelReport.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.resources.ResourceHandler.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.ResourceHandler.xml
new file mode 100644
index 0000000..3a34d01
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.ResourceHandler.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>text</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>gzip</td>
+            </tr>
+            <tr>
+                <td>bzip2</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResource.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResource.xml
new file mode 100644
index 0000000..63ea897
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResource.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>asString</td>
+            </tr>
+            <tr>
+                <td>asReader</td>
+            </tr>
+            <tr>
+                <td>asFile</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResourceFactory.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResourceFactory.xml
new file mode 100644
index 0000000..c0acf9d
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.resources.TextResourceFactory.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>fromString</td>
+            </tr>
+            <tr>
+                <td>fromFile</td>
+            </tr>
+            <tr>
+                <td>fromArchiveEntry</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.AbstractExecTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.AbstractExecTask.xml
new file mode 100644
index 0000000..83521dc
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.AbstractExecTask.xml
@@ -0,0 +1,67 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>args</td>
+            </tr>
+            <tr>
+                <td>commandLine</td>
+            </tr>
+            <tr>
+                <td>executable</td>
+            </tr>
+            <tr>
+                <td>workingDir</td>
+            </tr>
+            <tr>
+                <td>environment</td>
+            </tr>
+            <tr>
+                <td>standardInput</td>
+            </tr>
+            <tr>
+                <td>standardOutput</td>
+            </tr>
+            <tr>
+                <td>errorOutput</td>
+            </tr>
+            <tr>
+                <td>ignoreExitValue</td>
+            </tr>
+            <tr>
+                <td>execResult</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>commandLine</td>
+            </tr>
+            <tr>
+                <td>args</td>
+            </tr>
+            <tr>
+                <td>executable</td>
+            </tr>
+            <tr>
+                <td>workingDir</td>
+            </tr>
+            <tr>
+                <td>environment</td>
+            </tr>
+        </table>
+    </section>
+</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.Exec.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.Exec.xml
index 8330f20..5d063ff 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.Exec.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.Exec.xml
@@ -7,36 +7,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr>
-                <td>args</td>
-            </tr>
-            <tr>
-                <td>commandLine</td>
-            </tr>
-            <tr>
-                <td>executable</td>
-            </tr>
-            <tr>
-                <td>workingDir</td>
-            </tr>
-            <tr>
-                <td>environment</td>
-            </tr>
-            <tr>
-                <td>standardInput</td>
-            </tr>
-            <tr>
-                <td>standardOutput</td>
-            </tr>
-            <tr>
-                <td>errorOutput</td>
-            </tr>
-            <tr>
-                <td>ignoreExitValue</td>
-            </tr>
-            <tr>
-                <td>execResult</td>
-            </tr>
         </table>
     </section>
     <section>
@@ -47,21 +17,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr>
-                <td>commandLine</td>
-            </tr>
-            <tr>
-                <td>args</td>
-            </tr>
-            <tr>
-                <td>executable</td>
-            </tr>
-            <tr>
-                <td>workingDir</td>
-            </tr>
-            <tr>
-                <td>environment</td>
-            </tr>
         </table>
     </section>
-</section>
\ No newline at end of file
+</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml
deleted file mode 100644
index 7773624..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr><td>Name</td></tr>
-            </thead>
-            <tr><td>applicationName</td></tr>
-            <tr><td>mainClassName</td></tr>
-            <tr><td>classpath</td></tr>
-            <tr><td>defaultJvmOpts</td></tr>
-            <tr><td>outputDir</td></tr>
-            <tr><td>optsEnvironmentVar</td></tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr><td>Name</td></tr>
-            </thead>
-        </table>
-    </section>
-</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.bundling.Jar.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.bundling.Jar.xml
index 2c5d058..f46d04e 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.bundling.Jar.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.bundling.Jar.xml
@@ -5,21 +5,8 @@
             <thead>
                 <tr>
                     <td>Name</td>
-                    <td>Default with <literal>java</literal> plugin</td>
                 </tr>
             </thead>
-            <tr>
-                <td>manifest</td>
-                <td/>
-            </tr>
-            <tr>
-                <td>destinationDir</td>
-                <td><literal>project.libsDir</literal></td>
-            </tr>
-            <tr>
-                <td>extension</td>
-                <td><literal>jar</literal></td>
-            </tr>
         </table>
     </section>
     <section>
@@ -30,12 +17,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr>
-                <td>manifest</td>
-            </tr>
-            <tr>
-                <td>metaInf</td>
-            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.CompileOptions.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.CompileOptions.xml
index cbd9b35..d00d9d9 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.CompileOptions.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.CompileOptions.xml
@@ -33,10 +33,6 @@
                 <td><literal>null</literal></td>
             </tr>
             <tr>
-                <td>optimize</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
                 <td>debug</td>
                 <td><literal>true</literal></td>
             </tr>
@@ -61,14 +57,6 @@
                 <td></td>
             </tr>
             <tr>
-                <td>compiler</td>
-                <td><literal>null</literal></td>
-            </tr>
-            <tr>
-                <td>includeJavaRuntime</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
                 <td>bootClasspath</td>
                 <td><literal>null</literal></td>
             </tr>
@@ -81,8 +69,8 @@
                 <td><literal>[]</literal></td>
             </tr>
             <tr>
-                <td>useAnt</td>
-                <td><literal>false</literal></td>
+                <td>sourcepath</td>
+                <td><literal>null</literal></td>
             </tr>
         </table>
     </section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.GroovyCompileOptions.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.GroovyCompileOptions.xml
index 6a82a81..985723c 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.GroovyCompileOptions.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.GroovyCompileOptions.xml
@@ -21,6 +21,10 @@
                 <td><literal>false</literal></td>
             </tr>
             <tr>
+                <td>configurationScript</td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
                 <td>encoding</td>
                 <td><literal>UTF-8</literal></td>
             </tr>
@@ -37,18 +41,6 @@
                 <td><literal>[:]</literal></td>
             </tr>
             <tr>
-                <td>stacktrace</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
-                <td>useAnt</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
-                <td>includeJavaRuntime</td>
-                <td><literal>false</literal></td>
-            </tr>
-            <tr>
                 <td>stubDir</td>
                 <td><literal>null</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.JavaCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.JavaCompile.xml
index 99339ae..2359356 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.JavaCompile.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.compile.JavaCompile.xml
@@ -13,6 +13,10 @@
                 <td/>
             </tr>
             <tr>
+                <td>toolResolver</td>
+                <td/>
+            </tr>
+            <tr>
                 <td>source</td>
                 <td><literal><replaceable>sourceSet</replaceable>.java</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.javadoc.Javadoc.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.javadoc.Javadoc.xml
index ea84775..a2508a7 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.javadoc.Javadoc.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.javadoc.Javadoc.xml
@@ -48,6 +48,10 @@
                 <td>excludes</td>
                 <td><literal>[]</literal></td>
             </tr>
+            <tr>
+                <td>toolResolver</td>
+                <td></td>
+            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompile.xml
index 09714bb..24010e1 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompile.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompile.xml
@@ -5,25 +5,13 @@
             <thead>
                 <tr>
                     <td>Name</td>
-                    <td>Default with <literal>scala</literal> plugin <overrides>Default with <litera>java</litera> plugin</overrides></td>
+                    <td>Default with <literal>scala</literal> plugin <overrides>Default with <literal>java</literal> plugin</overrides></td>
                 </tr>
             </thead>
             <tr>
                 <td>scalaClasspath</td>
                 <td><literal>scala-compiler</literal> dependency matching the <literal>scala-library</literal> version found on <literal>classpath</literal></td>
             </tr>
-            <tr>
-                <td>options</td>
-                <td/>
-            </tr>
-            <tr>
-                <td>scalaCompileOptions</td>
-                <td/>
-            </tr>
-            <tr>
-                <td>source</td>
-                <td><literal><replaceable>sourceSet</replaceable>.scala</literal></td>
-            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompileOptions.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompileOptions.xml
index 7971304..9155ac7 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompileOptions.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.scala.ScalaCompileOptions.xml
@@ -61,10 +61,6 @@
                 <td><literal>never</literal></td>
             </tr>
             <tr>
-                <td>targetCompatibility</td>
-                <td><literal>1.5</literal></td>
-            </tr>
-            <tr>
                 <td>additionalParameters</td>
                 <td><literal>null</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.testing.Test.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.testing.Test.xml
index ecf3e50..a9b98d6 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.testing.Test.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.testing.Test.xml
@@ -13,18 +13,10 @@
                 <td><literal>project.sourceSets.test.output.classesDir</literal></td>
             </tr>
             <tr>
-                <td>testResultsDir</td>
-                <td><literal>project.testResultsDir</literal></td>
-            </tr>
-            <tr>
                 <td>binResultsDir</td>
                 <td><literal><replaceable>project.testResultsDir</replaceable>/binary/<replaceable>task.name</replaceable></literal></td>
             </tr>
             <tr>
-                <td>testReportDir</td>
-                <td><literal>project.testReportDir</literal></td>
-            </tr>
-            <tr>
                 <td>reports</td>
                 <td><literal>project.testReportDir</literal></td>
             </tr>
@@ -41,10 +33,6 @@
                 <td><literal>project.projectDir</literal></td>
             </tr>
             <tr>
-                <td>testReport</td>
-                <td><literal>true</literal></td>
-            </tr>
-            <tr>
                 <td>scanForTestClasses</td>
                 <td><literal>true</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.jvm.application.tasks.CreateStartScripts.xml b/subprojects/docs/src/docs/dsl/org.gradle.jvm.application.tasks.CreateStartScripts.xml
new file mode 100644
index 0000000..3338bb5
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.jvm.application.tasks.CreateStartScripts.xml
@@ -0,0 +1,26 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+            <tr><td>applicationName</td></tr>
+            <tr><td>mainClassName</td></tr>
+            <tr><td>classpath</td></tr>
+            <tr><td>defaultJvmOpts</td></tr>
+            <tr><td>outputDir</td></tr>
+            <tr><td>optsEnvironmentVar</td></tr>
+            <tr><td>unixStartScriptGenerator</td></tr>
+            <tr><td>windowsStartScriptGenerator</td></tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr><td>Name</td></tr>
+            </thead>
+        </table>
+    </section>
+</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.jvm.tasks.Jar.xml b/subprojects/docs/src/docs/dsl/org.gradle.jvm.tasks.Jar.xml
new file mode 100644
index 0000000..a21fff3
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.jvm.tasks.Jar.xml
@@ -0,0 +1,33 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>java</literal> plugin</td>
+                </tr>
+            </thead>
+                <tr>
+                    <td>manifest</td>
+                    <td/>
+                </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>manifest</td>
+            </tr>
+            <tr>
+                <td>metaInf</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.jvm.toolchain.JavaToolChain.xml b/subprojects/docs/src/docs/dsl/org.gradle.jvm.toolchain.JavaToolChain.xml
new file mode 100644
index 0000000..f46d04e
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.jvm.toolchain.JavaToolChain.xml
@@ -0,0 +1,22 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.DependentSourceSet.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.DependentSourceSet.xml
index 58c7341..b5dc4f9 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.language.DependentSourceSet.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.language.DependentSourceSet.xml
@@ -39,9 +39,6 @@
             <tr>
                 <td>lib</td>
             </tr>
-            <tr>
-                <td>dependency</td>
-            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.PreprocessingTool.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.PreprocessingTool.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.PreprocessingTool.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.PreprocessingTool.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.assembler.tasks.Assemble.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.assembler.tasks.Assemble.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.assembler.tasks.Assemble.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.assembler.tasks.Assemble.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.c.tasks.CCompile.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.language.c.tasks.CCompile.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.BuildType.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.cpp.plugins.CppPlugin.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.BuildType.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.cpp.plugins.CppPlugin.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.BuildTypeContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.cpp.tasks.CppCompile.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.BuildTypeContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.cpp.tasks.CppCompile.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.c.tasks.AbstractNativeCompileTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.c.tasks.AbstractNativeCompileTask.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.objectivec.tasks.ObjectiveCCompile.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.language.objectivec.tasks.ObjectiveCCompile.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.rc.tasks.WindowsResourceCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.rc.tasks.WindowsResourceCompile.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.rc.tasks.WindowsResourceCompile.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.language.rc.tasks.WindowsResourceCompile.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.scala.tasks.AbstractScalaCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.language.scala.tasks.AbstractScalaCompile.xml
new file mode 100644
index 0000000..c8a31a0
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.language.scala.tasks.AbstractScalaCompile.xml
@@ -0,0 +1,51 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default with <literal>scala</literal> plugin <overrides>Default with <literal>java</literal> plugin</overrides></td>
+                </tr>
+            </thead>
+            <tr>
+                <td>options</td>
+                <td/>
+            </tr>
+            <tr>
+                <td>scalaCompileOptions</td>
+                <td/>
+            </tr>
+            <tr>
+                <td>source</td>
+                <td><literal><replaceable>sourceSet</replaceable>.scala</literal></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeBinary.xml
deleted file mode 100644
index 2fbfb60..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeBinary.xml
+++ /dev/null
@@ -1,68 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>component</td>
-            </tr>
-            <tr>
-                <td>buildable</td>
-            </tr>
-            <tr>
-                <td>toolChain</td>
-            </tr>
-            <tr>
-                <td>source</td>
-            </tr>
-            <tr>
-                <td>libs</td>
-            </tr>
-            <tr>
-                <td>linker</td>
-            </tr>
-            <tr>
-                <td>staticLibArchiver</td>
-            </tr>
-            <tr>
-                <td>tasks</td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>source</td>
-            </tr>
-            <tr>
-                <td>lib</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeComponent.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeComponent.xml
deleted file mode 100644
index 780bd65..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ProjectNativeComponent.xml
+++ /dev/null
@@ -1,53 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>binaries</td>
-            </tr>
-            <tr>
-                <td>baseName</td>
-            </tr>
-            <tr>
-                <td>source</td>
-            </tr>
-            <tr>
-                <td>displayName</td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>source</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.TargetedNativeComponent.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.TargetedNativeComponent.xml
deleted file mode 100644
index e81e1a1..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.TargetedNativeComponent.xml
+++ /dev/null
@@ -1,48 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>targetFlavors</td>
-            </tr>
-            <tr>
-                <td>targetBuildTypes</td>
-            </tr>
-            <tr>
-                <td>targetPlatforms</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.platform.Platform.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.platform.Platform.xml
deleted file mode 100644
index 91d2d7e..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.platform.Platform.xml
+++ /dev/null
@@ -1,50 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>architecture</td>
-            </tr>
-            <tr>
-                <td>operatingSystem</td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>architecture</td>
-            </tr>
-            <tr>
-                <td>operatingSystem</td>
-            </tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.ProjectComponentTestSuite.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.ProjectComponentTestSuite.xml
deleted file mode 100644
index 2424106..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.ProjectComponentTestSuite.xml
+++ /dev/null
@@ -1,41 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr>
-                <td>testedComponent</td>
-            </tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.Gcc.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.Gcc.xml
deleted file mode 100644
index c2b9e7e..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.Gcc.xml
+++ /dev/null
@@ -1,40 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr><td>path</td></tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr><td>path</td></tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.PlatformConfigurableToolChain.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.PlatformConfigurableToolChain.xml
deleted file mode 100644
index 90075bd..0000000
--- a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.PlatformConfigurableToolChain.xml
+++ /dev/null
@@ -1,44 +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.
-  -->
-
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr><td>assembler</td></tr>
-            <tr><td>cCompiler</td></tr>
-            <tr><td>cppCompiler</td></tr>
-            <tr><td>linker</td></tr>
-            <tr><td>staticLibArchiver</td></tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr>
-                    <td>Name</td>
-                </tr>
-            </thead>
-            <tr><td>addPlatformConfiguration</td></tr>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Executable.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.BuildType.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Executable.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.BuildType.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ExecutableBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.BuildTypeContainer.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ExecutableBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.BuildTypeContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ExecutableContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.Flavor.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.ExecutableContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.Flavor.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Flavor.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.FlavorContainer.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Flavor.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.FlavorContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.NativeBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.NativeBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeBinarySpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeBinarySpec.xml
new file mode 100644
index 0000000..50fcdfc
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeBinarySpec.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>component</td>
+            </tr>
+            <tr>
+                <td>toolChain</td>
+            </tr>
+            <tr>
+                <td>libs</td>
+            </tr>
+            <tr>
+                <td>linker</td>
+            </tr>
+            <tr>
+                <td>staticLibArchiver</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>lib</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentExtension.xml
new file mode 100644
index 0000000..57cc5f1
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentExtension.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>executables</td>
+            </tr>
+            <tr>
+                <td>libraries</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>executables</td>
+            </tr>
+            <tr>
+                <td>libraries</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentSpec.xml
new file mode 100644
index 0000000..96f7cef
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeComponentSpec.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>baseName</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.FlavorContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutable.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.FlavorContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutable.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.LibraryBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.LibraryBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.LibraryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableBinarySpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.LibraryContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableBinarySpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.c.tasks.CCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableSpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.c.tasks.CCompile.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeExecutableSpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppExeConventionPlugin.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibrary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppExeConventionPlugin.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibrary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppLibConventionPlugin.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibraryBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppLibConventionPlugin.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibraryBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Library.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibrarySpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Library.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.NativeLibrarySpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppPlugin.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltLibrary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.plugins.CppPlugin.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltLibrary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.tasks.CppCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltSharedLibraryBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.cpp.tasks.CppCompile.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltSharedLibraryBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.platform.PlatformContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltStaticLibraryBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.platform.PlatformContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.PrebuiltStaticLibraryBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.SharedLibraryBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.SharedLibraryBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.SharedLibraryBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.SharedLibraryBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.SharedLibraryBinarySpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.SharedLibraryBinarySpec.xml
new file mode 100644
index 0000000..f6d2f9d
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.SharedLibraryBinarySpec.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sharedLibraryFile</td>
+            </tr>
+            <tr>
+                <td>sharedLibraryLinkFile</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.StaticLibraryBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.StaticLibraryBinary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.StaticLibraryBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.StaticLibraryBinary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.LinkExecutable.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.StaticLibraryBinarySpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.LinkExecutable.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.StaticLibraryBinarySpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.TargetedNativeComponent.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.TargetedNativeComponent.xml
new file mode 100644
index 0000000..ceed2e4
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.TargetedNativeComponent.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>targetFlavors</td>
+            </tr>
+            <tr>
+                <td>targetBuildTypes</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Tool.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.Tool.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.Tool.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.Tool.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.LinkSharedLibrary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.platform.PlatformContainer.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.LinkSharedLibrary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.platform.PlatformContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.AbstractLinkTask.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.AbstractLinkTask.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.AbstractLinkTask.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.AbstractLinkTask.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.CreateStaticLibrary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.CreateStaticLibrary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.CreateStaticLibrary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.CreateStaticLibrary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.InstallExecutable.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.InstallExecutable.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.tasks.InstallExecutable.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.InstallExecutable.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuite.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.LinkExecutable.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuite.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.LinkExecutable.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuiteContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.LinkSharedLibrary.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuiteContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.tasks.LinkSharedLibrary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuiteExecutableBinary.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.TestSuiteExecutableBinary.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.cunit.CUnitTestSuite.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.NativeTestSuiteSpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.test.cunit.CUnitTestSuite.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.NativeTestSuiteSpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.ToolChain.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.TestSuiteContainer.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.ToolChain.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.TestSuiteContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.ToolChainRegistry.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.ToolChainRegistry.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec.xml
new file mode 100644
index 0000000..84e5a07
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.tasks.RunTestExecutable.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.tasks.RunTestExecutable.xml
new file mode 100644
index 0000000..5d063ff
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.test.tasks.RunTestExecutable.xml
@@ -0,0 +1,22 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.plugins.GppCompilerPlugin.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.Clang.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.plugins.GppCompilerPlugin.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.Clang.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.ConfigurableToolChain.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.ConfigurableToolChain.xml
new file mode 100644
index 0000000..afaf0d0
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.ConfigurableToolChain.xml
@@ -0,0 +1,45 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <!-- <tr><td>assembler</td></tr>
+            <tr><td>cCompiler</td></tr>
+            <tr><td>cppCompiler</td></tr>
+            <tr><td>linker</td></tr>
+            <tr><td>objcCompiler</td></tr>
+            <tr><td>objcppCompiler</td></tr>
+            <tr><td>staticLibArchiver</td></tr>-->
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.plugins.MicrosoftVisualCppPlugin.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.Gcc.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.plugins.MicrosoftVisualCppPlugin.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.Gcc.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.Clang.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.GccCompatibleToolChain.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.Clang.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.GccCompatibleToolChain.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.PlatformConfigurableToolChain.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.PlatformConfigurableToolChain.xml
new file mode 100644
index 0000000..ceb6d02
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.PlatformConfigurableToolChain.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr><td>target</td></tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.ToolChainRegistry.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.ToolChainRegistry.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.VisualCpp.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.VisualCpp.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.toolchain.VisualCpp.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.VisualCpp.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.plugins.GppCompilerPlugin.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.plugins.GppCompilerPlugin.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.plugins.MicrosoftVisualCppPlugin.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.nativeplatform.toolchain.plugins.MicrosoftVisualCppPlugin.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.Application.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.platform.base.Application.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.Binary.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.platform.base.Binary.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.BinaryContainer.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.platform.base.BinaryContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.platform.base.BinarySpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.BinarySpec.xml
new file mode 100644
index 0000000..96f5c37
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.BinarySpec.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>buildable</td>
+            </tr>
+            <tr>
+                <td>source</td>
+            </tr>
+            <tr>
+                <td>tasks</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>source</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.platform.base.ComponentSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.ComponentSpec.xml
new file mode 100644
index 0000000..ea0d6cd
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.ComponentSpec.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>source</td>
+            </tr>
+            <tr>
+                <td>binaries</td>
+            </tr>
+            <tr>
+                <td>displayName</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sources</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.ComponentSpecContainer.xml
similarity index 100%
copy from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
copy to subprojects/docs/src/docs/dsl/org.gradle.platform.base.ComponentSpecContainer.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.Library.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.language.base.BinaryContainer.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.platform.base.Library.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.LibrarySpec.xml
similarity index 100%
rename from subprojects/docs/src/docs/dsl/org.gradle.nativebinaries.language.objectivec.tasks.ObjectiveCCompile.xml
rename to subprojects/docs/src/docs/dsl/org.gradle.platform.base.LibrarySpec.xml
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.platform.base.PlatformAwareComponentSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.PlatformAwareComponentSpec.xml
new file mode 100644
index 0000000..427c9b6
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.platform.base.PlatformAwareComponentSpec.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>targetPlatform</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependenciesSpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependenciesSpec.xml
new file mode 100644
index 0000000..4996e2c
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependenciesSpec.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>id</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependencySpec.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependencySpec.xml
new file mode 100644
index 0000000..78f39ad
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugin.use.PluginDependencySpec.xml
@@ -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.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>version</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml
index f32b8ab..fa588c4 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.xml
@@ -5,40 +5,12 @@
             <thead>
                 <tr>
                     <td>Name</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>java</literal> plugins</td>
+                    <td>Default with <literal>eclipse</literal> plugins</td>
                 </tr>
             </thead>
             <tr>
-                <td>sourceSets</td>
-                <td><literal>project.sourceSets</literal></td>
-            </tr>
-            <tr>
-                <td>plusConfigurations</td>
-                <td><literal>project.configurations.testRuntime</literal></td>
-            </tr>
-            <tr>
-                <td>minusConfigurations</td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>variables</td>
-                <td><literal>[:]</literal></td>
-            </tr>
-            <tr>
-                <td>containers</td>
-                <td>[JRE container]</td>
-            </tr>
-            <tr>
-                <td>defaultOutputDir</td>
-                <td><filename><replaceable>${project.projectDir}</replaceable>/bin</filename></td>
-            </tr>
-            <tr>
-                <td>downloadSources</td>
-                <td><literal>true</literal></td>
-            </tr>
-            <tr>
-                <td>downloadJavadoc</td>
-                <td><literal>false</literal></td>
+                <td>classpath</td>
+                <td><literal>project.eclipse.classpath</literal></td>
             </tr>
             <tr>
                 <td>outputFile</td>
@@ -54,12 +26,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr>
-                <td>containers</td>
-            </tr>
-            <tr>
-                <td>variables</td>
-            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml
index d7fcf44..04f835b 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.xml
@@ -9,6 +9,10 @@
                 </tr>
             </thead>
             <tr>
+                <td>jdt</td>
+                <td><literal>project.eclipse.jdt</literal></td>
+            </tr>
+            <tr>
                 <td>outputFile</td>
                 <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.jdt.core.prefs</filename></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml
index da281c8..6c04269 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseProject.xml
@@ -5,10 +5,14 @@
             <thead>
                 <tr>
                     <td>Name</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>java</literal> plugins</td>
+                    <td>Default with <literal>eclipse</literal> plugins</td>
                 </tr>
             </thead>
             <tr>
+                <td>projectModel</td>
+                <td><literal>project.eclipse.project</literal></td>
+            </tr>
+            <tr>
                 <td>outputFile</td>
                 <td><filename><replaceable>${project.projectDir}</replaceable>/.project</filename></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml
index 1800966..ee36327 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.xml
@@ -5,54 +5,16 @@
             <thead>
                 <tr>
                     <td>Name</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>war</literal> plugins</td>
-                    <td>Default with <literal>eclipse</literal> and <literal>ear</literal> plugins</td>
+                    <td>Default with <literal>eclipse-wtp</literal> and <literal>war</literal> or <literal>ear</literal> plugins</td>
                 </tr>
             </thead>
             <tr>
-                <td>inputFile</td>
-                <td/>
-                <td/>
+                <td>component</td>
+                <td><literal>project.eclipse.wtp.component</literal></td>
             </tr>
             <tr>
                 <td>outputFile</td>
                 <td><filename><replaceable>${project.projectDir}</replaceable>/.settings/org.eclipse.wst.common.component</filename></td>
-                <td>(same)</td>
-            </tr>
-            <tr>
-                <td>sourceDirs</td>
-                <td>source dirs from <literal>project.sourceSets.main.allSource</literal></td>
-                <td>(same)</td>
-            </tr>
-            <tr>
-                <td>minusConfigurations</td>
-                <td><literal>[project.configurations.providedRuntime]</literal></td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>deployName</td>
-                <td><literal>project.name</literal></td>
-                <td><literal>project.name</literal></td>
-            </tr>
-            <tr>
-                <td>variables</td>
-                <td><literal>[:]</literal></td>
-                <td><literal>[:]</literal></td>
-            </tr>
-            <tr>
-                <td>resources</td>
-                <td><literal>[deployPath: '/', sourcePath: project.webAppDirName]</literal></td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>properties</td>
-                <td><literal>[]</literal></td>
-                <td><literal>[]</literal></td>
-            </tr>
-            <tr>
-                <td>contextPath</td>
-                <td><literal>project.war.baseName</literal></td>
-                <td/>
             </tr>
         </table>
     </section>
@@ -64,15 +26,6 @@
                     <td>Name</td>
                 </tr>
             </thead>
-            <tr>
-                <td>variables</td>
-            </tr>
-            <tr>
-                <td>property</td>
-            </tr>
-            <tr>
-                <td>resource</td>
-            </tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml
index c999272..a15b37b 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.xml
@@ -9,8 +9,8 @@
                 </tr>
             </thead>
             <tr>
-                <td>inputFile</td>
-                <td/>
+                <td>facet</td>
+                <td><literal>project.eclipse.wtp.facet</literal></td>
             </tr>
             <tr>
                 <td>outputFile</td>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml
index ee127b3..a59c5f7 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaModule.xml
@@ -10,6 +10,11 @@
                 </tr>
             </thead>
             <tr>
+                <td>module</td>
+                <td><literal>project.idea.module</literal></td>
+                <td/>
+            </tr>
+            <tr>
                 <td>outputFile</td>
                 <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.name}</replaceable>.iml</literal>
                     (sometimes the <replaceable>project.name</replaceable> is prefixed with parts of <replaceable>${project.path}</replaceable> to guarantee uniqeness).
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml
index ac7581f..09d3070 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaProject.xml
@@ -10,6 +10,11 @@
                 </tr>
             </thead>
             <tr>
+                <td>ideaProject</td>
+                <td><literal>project.idea.project</literal></td>
+                <td/>
+            </tr>
+            <tr>
                 <td>outputFile</td>
                 <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.name}</replaceable>.ipr</literal></td>
                 <td/>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml
index cd2e510..d23b1d4 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.xml
@@ -9,6 +9,10 @@
                 </tr>
             </thead>
             <tr>
+                <td>workspace</td>
+                <td><literal>project.idea.workspace</literal></td>
+            </tr>
+            <tr>
                 <td>outputFile</td>
                 <td><literal><replaceable>${project.projectDir}</replaceable>/<replaceable>${project.name}</replaceable>.iws</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.model.IdeaProject.xml b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.model.IdeaProject.xml
index fc85d08..d41a737 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.model.IdeaProject.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.plugins.ide.idea.model.IdeaProject.xml
@@ -15,6 +15,11 @@
                 <td/>
             </tr>
             <tr>
+                <td>vcs</td>
+                <td/>
+                <td/>
+            </tr>
+            <tr>
                 <td>jdkName</td>
                 <td>Java version used to run Gradle, for example <literal>'1.6'</literal></td>
                 <td>Java version used to run Gradle, for example <literal>'1.6'</literal></td>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarProperties.xml b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarProperties.xml
new file mode 100644
index 0000000..f554022
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarProperties.xml
@@ -0,0 +1,48 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>properties</td>
+                <td></td>
+            </tr>
+
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+                <tr>
+                    <td>properties</td>
+                    <td></td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerExtension.xml
new file mode 100644
index 0000000..93be3f2
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerExtension.xml
@@ -0,0 +1,46 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>skipProject</td>
+                <td><literal>false</literal></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sonarProperties</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerRootExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerRootExtension.xml
new file mode 100644
index 0000000..7e5420d
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.SonarRunnerRootExtension.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>toolVersion</td>
+                <td><literal>2.3</literal></td>
+            </tr>
+            <tr>
+                <td>forkOptions</td>
+                <td></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>forkOptions</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.tasks.SonarRunner.xml b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.tasks.SonarRunner.xml
new file mode 100644
index 0000000..395a118
--- /dev/null
+++ b/subprojects/docs/src/docs/dsl/org.gradle.sonar.runner.tasks.SonarRunner.xml
@@ -0,0 +1,47 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Default</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sonarProperties</td>
+                <td></td>
+            </tr>
+            <tr>
+                <td>forkOptions</td>
+                <td></td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
index b846737..b30d522 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
@@ -26,7 +26,7 @@
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>0.6.2.201302030002</literal></td>
+                <td><literal>0.7.1.201405082137</literal></td>
             </tr>
             <tr>
                 <td>reportsDir</td>
diff --git a/subprojects/docs/src/docs/dsl/plugins.xml b/subprojects/docs/src/docs/dsl/plugins.xml
index e6121ad..b70a237 100755
--- a/subprojects/docs/src/docs/dsl/plugins.xml
+++ b/subprojects/docs/src/docs/dsl/plugins.xml
@@ -1,8 +1,8 @@
 <plugins>
     <plugin id="java" description="Java Plugin">
         <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.BasePluginConvention"/>
-        <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.ReportingBasePluginConvention"/>
         <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.JavaPluginConvention"/>
+        <extends targetClass="org.gradle.api.Project" id="reporting" extensionClass="org.gradle.api.reporting.ReportingExtension"/>
     </plugin>
     <plugin id="groovy" description="Groovy Plugin">
         <extends targetClass="org.gradle.api.tasks.SourceSet" mixinClass="org.gradle.api.tasks.GroovySourceSet"/>
@@ -16,10 +16,6 @@
     <plugin id="antlr" description="Antlr Plugin">
         <extends targetClass="org.gradle.api.tasks.SourceSet" mixinClass="org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory"/>
     </plugin>
-    <plugin id="code-quality" description="Code Quality Plugin">
-        <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.quality.JavaCodeQualityPluginConvention"/>
-        <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.quality.GroovyCodeQualityPluginConvention"/>
-    </plugin>
     <plugin id="maven" description="Maven Plugin">
         <extends targetClass="org.gradle.api.Project" mixinClass="org.gradle.api.plugins.MavenPluginConvention"/>
         <extends targetClass="org.gradle.api.artifacts.dsl.RepositoryHandler" mixinClass="org.gradle.api.plugins.MavenRepositoryHandlerConvention"/>
@@ -78,40 +74,34 @@
         <extends targetClass="org.gradle.api.tasks.testing.Test" id="jacoco" extensionClass="org.gradle.testing.jacoco.plugins.JacocoTaskExtension"/>
     </plugin>
     <plugin id="LanguageBasePlugin" description="Language Base Plugin">
-        <extends targetClass="org.gradle.api.Project" id="binaries" extensionClass="org.gradle.language.base.BinaryContainer"/>
+        <extends targetClass="org.gradle.api.Project" id="projectComponents" extensionClass="org.gradle.platform.base.ComponentSpecContainer"/>
+        <extends targetClass="org.gradle.api.Project" id="binaries" extensionClass="org.gradle.platform.base.BinaryContainer"/>
         <extends targetClass="org.gradle.api.Project" id="sources" extensionClass="org.gradle.language.base.ProjectSourceSet"/>
     </plugin>
-    <plugin id="native-binaries" description="Native Binaries Model Plugin">
-        <extends targetClass="org.gradle.api.Project" id="executables" extensionClass="org.gradle.nativebinaries.ExecutableContainer"/>
-        <extends targetClass="org.gradle.api.Project" id="libraries" extensionClass="org.gradle.nativebinaries.LibraryContainer"/>
-        <extends targetClass="org.gradle.api.Project" id="binaries" extensionClass="org.gradle.language.base.BinaryContainer"/>
-        <extends targetClass="org.gradle.api.Project" id="toolChains" extensionClass="org.gradle.nativebinaries.toolchain.ToolChainRegistry"/>
-        <extends targetClass="org.gradle.api.Project" id="buildTypes" extensionClass="org.gradle.nativebinaries.BuildTypeContainer"/>
-        <extends targetClass="org.gradle.api.Project" id="platforms" extensionClass="org.gradle.nativebinaries.platform.PlatformContainer"/>
-        <extends targetClass="org.gradle.api.Project" id="flavors" extensionClass="org.gradle.nativebinaries.FlavorContainer"/>
-    </plugin>
+    <plugin id="native-component" description="Native Component Model Plugin"></plugin>
     <plugin id="cpp" description="C++ Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="cppCompiler" extensionClass="org.gradle.nativebinaries.language.PreprocessingTool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="cppCompiler" extensionClass="org.gradle.language.PreprocessingTool"/>
     </plugin>
     <plugin id="c" description="C Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="cCompiler" extensionClass="org.gradle.nativebinaries.language.PreprocessingTool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="cCompiler" extensionClass="org.gradle.language.PreprocessingTool"/>
     </plugin>
     <plugin id="assembler" description="Assembler Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="assembler" extensionClass="org.gradle.nativebinaries.Tool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="assembler" extensionClass="org.gradle.nativeplatform.Tool"/>
     </plugin>
     <plugin id="objective-c" description="Objective-C Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="objectiveCCompiler" extensionClass="org.gradle.nativebinaries.language.PreprocessingTool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="objectiveCCompiler" extensionClass="org.gradle.language.PreprocessingTool"/>
     </plugin>
     <plugin id="objective-cpp" description="Objective-C++ Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="objectiveCppCompiler" extensionClass="org.gradle.nativebinaries.language.PreprocessingTool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="objectiveCppCompiler" extensionClass="org.gradle.language.PreprocessingTool"/>
     </plugin>
     <plugin id="windows-resources" description="Windows Resources Plugin">
-        <extends targetClass="org.gradle.nativebinaries.NativeBinary" id="rcCompiler" extensionClass="org.gradle.nativebinaries.language.PreprocessingTool"/>
+        <extends targetClass="org.gradle.nativeplatform.NativeBinary" id="rcCompiler" extensionClass="org.gradle.language.PreprocessingTool"/>
     </plugin>
     <plugin id="visual-studio" description="Visual Studio Plugin">
         <extends targetClass="org.gradle.api.Project" id="visualStudio" extensionClass="org.gradle.ide.visualstudio.VisualStudioExtension"/>
     </plugin>
-    <plugin id="cunit" description="CUnit Test Plugin">
-        <extends targetClass="org.gradle.api.Project" id="testSuites" extensionClass="org.gradle.nativebinaries.test.TestSuiteContainer"/>
+    <plugin id="sonar-runner" description="Sonar Runner Plugin">
+        <extends targetClass="org.gradle.api.Project" id="sonarRunner" extensionClass="org.gradle.sonar.runner.SonarRunnerExtension"/>
+        <extends targetClass="org.gradle.api.Project" id="sonarRunner" extensionClass="org.gradle.sonar.runner.SonarRunnerRootExtension"/>
     </plugin>
 </plugins>
diff --git a/subprojects/docs/src/docs/release/content/script.js b/subprojects/docs/src/docs/release/content/script.js
index 9978212..1e2b775 100644
--- a/subprojects/docs/src/docs/release/content/script.js
+++ b/subprojects/docs/src/docs/release/content/script.js
@@ -64,7 +64,8 @@ $(function() {
         if (data.length > 0) {
           var list = $("<ul id='" + idBase + "-list'></ul>").hide().insertAfter(para);
           $.each(data, function (i, issue) {
-            $("<li>[<a href='" + issue["link"] + "'>"+ issue["key"] + "</a>] - " + issue["summary"] + "</li>").appendTo(list);
+            var link = $("<a></a>").attr("href", issue["link"]).text(issue["key"]);
+            $("<li></li>").append(document.createTextNode("["), link, document.createTextNode("] - " + issue["summary"])).appendTo(list);
           });
           list.slideDown("slow");
         }
@@ -83,7 +84,7 @@ $(function() {
   });
 
   injectIssues(
-    "http://services.gradle.org/fixed-issues/@versionBase@", 
+    "https://services.gradle.org/fixed-issues/@versionBase@",
     $("h2#fixed-issues"), 
     "fixed-issues", 
     "Retrieving the fixed issue information for @versionBase@", 
@@ -93,7 +94,7 @@ $(function() {
   );
   
   injectIssues(
-    "http://services.gradle.org/known-issues/@versionBase@", 
+    "https://services.gradle.org/known-issues/@versionBase@",
     $("h2#known-issues").next("p"), 
     "known-issues", 
     "Retrieving the known issue information for @versionBase@", 
diff --git a/subprojects/docs/src/docs/release/notes-next.md b/subprojects/docs/src/docs/release/notes-next.md
new file mode 100644
index 0000000..30ffb2a
--- /dev/null
+++ b/subprojects/docs/src/docs/release/notes-next.md
@@ -0,0 +1,113 @@
+## New and noteworthy
+
+Here are the new features introduced in this Gradle release.
+
+### Google Test support (i)
+
+- TBD
+
+### Dependency substitution accepts projects
+
+You can now replace an external dependency with a project dependency. The `DependencyResolveDetails` object
+allows access to the `ComponentSelector` as well:
+
+    resolutionStrategy {
+        eachDependency { details ->
+            if (details.selector instanceof ModuleComponentSelector && details.selector.group == 'com.example' && details.selector.module == 'my-module') {
+                useTarget project(":my-module")
+            }
+        }
+    }
+### Dependency substitution rules
+
+In previous Gradle versions you could replace an external dependency with another like this:
+
+    resolutionStrategy {
+        eachDependency { details ->
+            if (details.requested.group == 'com.example' && details.requested.module == 'my-module') {
+                useVersion '1.3'
+            }
+        }
+    }
+
+This behaviour has been enhanced and extended, with the introduction of 'Dependency Substitution Rules'.
+These rules allow an external dependency to be replaced with a project dependency, and vice-versa. 
+
+You replace a project dependency with an external dependency like this:
+
+    resolutionStrategy {
+        dependencySubstitution {
+            withProject(project(":api")) { 
+                useTarget group: "org.utils", name: "api", version: "1.3" 
+            }
+        }
+    }
+
+And replace an external dependency with an project dependency like this:
+
+
+    resolutionStrategy {
+        dependencySubstitution {
+            withModule("com.example:my-module") {
+                useTarget project(":project1")  
+            }
+        }
+    }
+
+There are other options available to match module and project dependencies:
+
+    all { DependencySubstitution<ComponentSelector> details -> /* ... */ }
+    eachModule() { ModuleDependencySubstitution details -> /* ... */ }
+    withModule("com.example:my-module") { ModuleDependencySubstitution details -> /* ... */ }
+    eachProject() { ProjectDependencySubstitution details -> /* ... */ }
+    withProject(project(":api)) { ProjectDependencySubstitution details -> /* ... */ }
+
+It is also possible to replace one project dependency with another, or one external dependency with another. (The latter provides the same functionality
+as `eachDependency`).
+Note that the `ModuleDependencySubstitution` has a convenience `useVersion()` method. For the other substitutions you should use `useTarget()`.
+
+## Promoted features
+
+Promoted features are features that were incubating in previous versions of Gradle but are now supported and subject to backwards compatibility.
+See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
+
+The following are the features that have been promoted in this Gradle release.
+
+<!--
+### Example promoted
+-->
+
+## Fixed issues
+
+## Deprecations
+
+Features that have become superseded or irrelevant due to the natural evolution of Gradle become *deprecated*, and scheduled to be removed
+in the next major Gradle version (Gradle 3.0). See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
+
+The following are the newly deprecated items in this Gradle release. If you have concerns about a deprecation, please raise it via the [Gradle Forums](http://forums.gradle.org).
+
+<!--
+### Example deprecation
+-->
+
+### Changing a configuration after it has been resolved
+
+TODO
+
+## Potential breaking changes
+
+<!--
+### Example breaking change
+-->
+
+## External contributions
+
+We would like to thank the following community members for making contributions to this release of Gradle.
+
+* [Lorant Pinter](https://github.com/lptr), [Daniel Vigovszky](https://github.com/vigoo) and [Mark Vujevits](https://github.com/vujevits) - implement dependency substitution for projects
+
+We love getting contributions from the Gradle community. For information on contributing, please see [gradle.org/contribute](http://gradle.org/contribute).
+
+## Known issues
+
+Known issues are problems that were discovered post release that are directly related to changes made in this release.
diff --git a/subprojects/docs/src/docs/release/notes-template.md b/subprojects/docs/src/docs/release/notes-template.md
index 438cd29..6ec04ab 100644
--- a/subprojects/docs/src/docs/release/notes-template.md
+++ b/subprojects/docs/src/docs/release/notes-template.md
@@ -3,6 +3,10 @@
 Here are the new features introduced in this Gradle release.
 
 <!--
+IMPORTANT: if this is a patch release, ensure that a prominent link is included in the foreword to all releases of the same minor stream.
+Add-->
+
+<!--
 ### Example new and noteworthy
 -->
 
@@ -22,7 +26,7 @@ The following are the features that have been promoted in this Gradle release.
 ## Deprecations
 
 Features that have become superseded or irrelevant due to the natural evolution of Gradle become *deprecated*, and scheduled to be removed
-in the next major Gradle version (Gradle 2.0). See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
+in the next major Gradle version (Gradle 3.0). See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
 
 The following are the newly deprecated items in this Gradle release. If you have concerns about a deprecation, please raise it via the [Gradle Forums](http://forums.gradle.org).
 
diff --git a/subprojects/docs/src/docs/release/notes.md b/subprojects/docs/src/docs/release/notes.md
index c0189d8..4fe803e 100644
--- a/subprojects/docs/src/docs/release/notes.md
+++ b/subprojects/docs/src/docs/release/notes.md
@@ -1,311 +1,461 @@
-Gradle 1.12 brings some nice universal performance improvements, useful new features and a slew of bug and compatibility fixes.
+The big story for Gradle 2.4 is the improved performance.
+While it's not unusual for a new Gradle release to be the fastest Gradle yet, Gradle 2.4 is _significantly_ faster.
+Many early testers of Gradle 2.4 have reported that overall build times have improved by 20% up to 40%.
 
-In the native space, improvements have been made to the Clang support as well as improved integration with Visual Studio.
-Gradle also now provides support for unit testing your native code via CUnit.
+There are two main components to the improved performance; [general configuration time optimizations](#significant-configuration-time-performance-improvements),
+and the [class reuse with the Gradle Daemon](#improved-performance-of-gradle-daemon-via-class-reuse).
+As such, using the [Gradle Daemon](userguide/gradle_daemon.html) now provides an even greater performance advantage.
 
-A considerable amount of work in this release went in to the dependency management engine, preparing for new APIs that allow more control over the dependency resolution process.
-The visible result of this work in Gradle 1.12 is the ability to specify rules that identify _changing_ components (e.g. snapshots).
+If you are using Gradle to build native code (e.g. C/C++), the news gets even better with the introduction of [parallel compilation](#parallel-native-compilation).
 
-Another significant area of investment in 1.12 is in the area of IDE integration, via the [Tooling API](userguide/embedding.html).
-Integration with IntelliJ IDEA in particular has been improved, along with general improvements that will offer tooling providers more integration capabilities.
-Optimizations have also been made to the parts of the codebase that deal with IDE import, making importing Gradle 1.12 projects into IDEs faster than any previous Gradle version.
+Memory consumption has also been reduced when compiling Java source code with Java 7 and 8, which can significantly improve compile times for large Java projects.
 
-Gradle 1.12 set a new high watermark for contributions.
-It includes contributions from 16 different people.
-Contributions range from documentation improvements, to Ant import improvements, to support for building large zips,
-to Sonar improvements, to test reporting improvements and more.
-Thank you to all of the contributors.
-
-Gradle 1.12 will be the last release in the Gradle 1.x line.
-The next release will be Gradle 2.0.
-For information on what this means for you and for the future of Gradle, please see [the Gradle 2.0 announcement](http://forums.gradle.org/gradle/topics/after_1_12_comes_2_0).
+Other highlights include [support for Amazon S3 dependency repositories](#support-for-amazon-web-services-s3-backed-repositories) 
+and [support for using annotation processors with Groovy code](#support-for-“annotation-processing”-of-groovy-code) and more. 
 
 ## New and noteworthy
 
 Here are the new features introduced in this Gradle release.
 
-### CUnit integration (i)
+### Significant configuration time performance improvements
 
-The new Gradle `cunit` plugin provides support for compiling and executing [CUnit](http://cunit.sourceforge.net) tests in your native-binary project.
+Gradle 2.4 features a collection of performance improvements particularly targeted at “configuration time”
+(i.e. the part of the build lifecycle where Gradle is comprehending the definition of the build by executing build scripts and plugins).
+Several users of early Gradle 2.4 builds have reported build time improvements of around 20% just by upgrading to Gradle 2.4.
 
-You simply need to include your CUnit test sources, and provide a hook for Gradle to register the suites and tests defined.
+Most performance improvements were realized by optimizing internal algorithms along with data and caching structures.
+Builds that have more configuration (i.e. more projects, more build scripts, more plugins, larger build scripts) stand to gain more from the improvements.
+The Gradle build itself, which is of non trivial complexity, realized improved configuration times of 34%.
+Stress tests run as part of Gradle's own build pipeline have demonstrated an 80% improvement in configuration time with Gradle 2.4.
 
-    #include <CUnit/Basic.h>
-    #include "gradle_cunit_register.h"
-    #include "operator_tests.h"
+No change is required to builds to leverage the performance improvements.
 
-    void gradle_cunit_register() {
-        CU_pSuite mySuite = CU_add_suite("operator tests", suite_init, suite_clean);
-        CU_add_test(mySuite, "test plus", test_plus);
-        CU_add_test(mySuite, "test minus", test_minus);
-    }
+### Improved performance of Gradle Daemon via class reuse
 
-Gradle will then generate the required boiler-plate CUnit code, build a test executable, and run your tests.
+The [Gradle Daemon](userguide/gradle_daemon.html) is now much smarter about reusing classes across builds.
+This makes all Gradle builds faster when using the Daemon, and builds that use non-core plugins in particular.
+This feature is completely transparent and applies to all builds.
 
-<pre><tt>> gradle -q runFailingOperatorsTest
+The Daemon is a persistent process.
+For a long time it has reused the Gradle core infrastructure and plugins across builds.
+This allows these classes to be loaded _once_ during a “session”, instead of for each build (as is the case when not using the Daemon).
+The level of class reuse has been greatly improved in Gradle 2.4 to also cover build scripts and third-party plugins.
+This improves performance in several ways.
+Class loading is expensive and by reusing classes this just happens less.
+Classes also reside in memory and with the Daemon being a persistent process reuse also reduces memory usage.
+This also reduces the severity of class loader leaks (because fewer class loaders actually leak) which again reduces memory usage.
 
-There were test failures:
-  1. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:6  - plus(0, -2) == -2
-  2. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:7  - plus(2, 2) == 4
+Perhaps more subtly, reusing classes across builds also improves performance by giving the JVM more opportunity to optimize the code.
+The optimizer typically improves build performance _dramatically_ over the first half dozen builds in a JVM.
 
-BUILD FAILED</tt></pre>
+The [Tooling API](userguide/embedding.html), which allows Gradle to be embedded in IDEs automatically uses the Gradle Daemon.
+The Gradle integration in IDEs such as Android Studio, Eclipse, IntelliJ IDEA and NetBeans also benefits from these performance improvements.
 
-See the [user guide chapter](userguide/nativeBinaries.html#native_binaries:cunit) and the cunit sample (`samples/native-binaries/cunit`)
-in the distribution to learn more. Expect deeper integration with CUnit (and other native testing tools) in the future.
+If you aren't using the [Gradle Daemon](userguide/gradle_daemon.html), we urge you to try it out with Gradle 2.4.
 
-### Component metadata rules can control whether a component version is considered changing (i)
+### Parallel native compilation
 
-Component metadata rules ([introduced in Gradle 1.8](http://www.gradle.org/docs/1.8/release-notes#component-metadata-rules)) can now be used to specify whether a component version is considered _changing_.
+Starting with 2.4, Gradle uses multiple concurrent compilation processes when compiling C/C++/Objective-C/Objective-C++/Assembler languages. 
+This is automatically enabled for all builds and works for all supported compilers (GCC, Clang, Visual C++). 
+Up until this release, Gradle compiled all native source files sequentially.
 
-A _changing_ component is expected to change over time without a change to the version number.
-A commonly used and well understood example of a changing component is a “`-SNAPSHOT`” dependency from an Apache Maven repository (which Gradle implicitly considers to be changing).
+This change has dramatic performance implications for native builds.
+Benchmarks for a project with a 500 source files on a machine with 8 processing cores available exhibited reduced build times of 53.4s to 12.9s. 
 
-This new feature makes it possible to implement custom strategies for deciding if a component version is changing.
-In the following example, every component version whose group is “`my.company`” and whose version number ends in “`-dev`” will be considered changing:
+The degree of concurrency is determined by the new [“max workers” build setting](#new-“max-workers”-build-setting).
 
-    dependencies {
-        components {
-            eachComponent { ComponentMetadataDetails details ->
-                details.changing =
-                    details.id.group == "my.company" &&
-                        details.id.version.endsWith("-dev")
-            }
-        }
-    }
+### New “max workers” build setting (i)
+
+The new `--max-workers=«N»` command line switch, and synonymous `org.gradle.workers.max=«N»` build property (e.g. specified in `gradle.properties`) determines the degree of build concurrency.
+
+As of Gradle 2.4, this setting influences [native code compilation](#parallel-native-compilation) and [parallel project execution](userguide/multi_project_builds.html#sec:parallel_execution).
+The “max workers” setting specifies the size of these _independent_ worker pools.
+However, a single worker pool is used for all native compilation operations.
+This means that if two (or more) native compilation tasks are executing at the same time, 
+they will share the worker pool and the total number of concurrent compilation options will not exceed the “max workers” setting.
 
-This feature is especially useful when dealing with Ivy repositories, as it is a generalized form of Ivy's `changingPattern` concept.
+Future versions of Gradle will leverage the shared worker pool for more concurrent work, allowing more precise control over the total build concurrency.
 
-See [ComponentMetadataHandler](javadoc/org/gradle/api/artifacts/dsl/ComponentMetadataHandler.html) for more information.
+The default value is the number of processors available to the build JVM (as reported by 
+[`Runtime.availableProcessors()`](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors\(\))).
+Alternatively, it can be set via the `--max-workers=«N»` command line switch or `org.gradle.workers.max=«N»` build property where `«N»` is a positive, non-zero, number.
 
-### Tooling API exposes information on a project's publications (i)
+Please note: the `--parallel-threads` command line switch [has been deprecated](#setting-number-of-build-execution-threads-with---parallel-threads) in favor of this new setting.
 
-The [Tooling API](userguide/embedding.html) is a mechanism for embedding Gradle and/or driving Gradle programmatically.
-The new [`ProjectPublications`](javadoc/org/gradle/tooling/model/gradle/ProjectPublications.html) Tooling API model type provides basic information about a project's publications.
+### Reduced memory consumption when compiling Java source code with Java 7 and 8
 
-The following example demonstrates using the `ProjectPublications` model to print out the group/name/version of each publication.
+By working around JDK bug [JDK-7177211](https://bugs.openjdk.java.net/browse/JDK-7177211), Java compilation requires less memory in Gradle 2.4.
+This JDK bug causes what was intended to be a performance improvement to not improve compilation performance and use more memory.
+The workaround is to implicitly apply the internal compiler flag `-XDuseUnsharedTable=true` to all compilation operations.
 
-    def projectConnection = ...
-    def publications = projectConnection.getModel(ProjectPublications)
-    for (publication in project.publications) {
-        println publication.id.group
-        println publication.id.name
-        println publication.id.version
+Very large Java projects (building with Java 7 or 8) may notice dramatically improved build times due to the decreased memory throughput which in turn
+requires less aggressive garbage collection in the build process.
+
+### Support for Amazon Web Services S3 backed repositories (i)
+
+It is now possible to consume dependencies from, and publish to, [Amazon Web Services S3](http://aws.amazon.com/s3) stores
+when using [`MavenArtifactRepository`](dsl/org.gradle.api.artifacts.repositories.MavenArtifactRepository.html) 
+or [`IvyArtifactRepository`](dsl/org.gradle.api.artifacts.repositories.IvyArtifactRepository.html).
+
+    repositories {
+        maven {
+            url "s3://someS3Bucket/maven2"
+            credentials(AwsCredentials) {
+                accessKey "someKey"
+                secretKey "someSecret"
+            }
+        }
+
+        ivy {
+            url "s3://someS3Bucket/ivy"
+            credentials(AwsCredentials) {
+                accessKey "someKey"
+                secretKey "someSecret"
+            }
+        }
     }
 
-Both publications declared in the old (`artifacts` block, `Upload` task) and new (`publishing.publications` block) way are reflected in the result.
+Downloading dependencies from S3 is supported for Maven and Ivy type repositories as above.
 
-### Tooling API exposes more information on how to launch a Gradle build (i)
+Publishing to S3 is supported with both the [`ivy-publish`](userguide/publishing_ivy.html) and [`maven-publish`](userguide/publishing_maven.html) plugins, 
+as well as when using an [`IvyArtifactRepository`](dsl/org.gradle.api.artifacts.repositories.IvyArtifactRepository.html) with an [`Upload`](dsl/org.gradle.api.tasks.Upload.html) task
+(see section [8.6. Publishing artifacts](userguide/artifact_dependencies_tutorial.html#N10669) of the User Guide).
 
-The [Tooling API](userguide/embedding.html) is a mechanism for embedding Gradle and/or driving Gradle programmatically.
-The new [`BuildInvocations`](javadoc/org/gradle/tooling/model/gradle/BuildInvocations.html) Tooling API model type provides information about the possible ways to invoke the build.
+Please see section [50.6. Repositories](userguide/dependency_management.html#sec:repositories) of the User Guide for more information on configuring S3 repository access.
 
-It provides the invokable tasks of a project, and importantly also its applicable task _selectors_.
-A task selector effectively refers to all of the tasks with a given name in a project and its child projects.
-For example, it is common in a multi project build for all projects to have a `build` task.
-Invoking the build via the Tooling API with the `build` task _selector_ would run the `build` task in every project, effectively building the entire multi project build.
-In contrast, invoking the build with the `build` _task_ would only build the root project (or which ever project is being targeted).
+This feature was contributed by [Adrian Kelly](https://github.com/adrianbk).
 
-This new capability makes it easier for integrators to provide more powerful interfaces for invoking Gradle builds.
+### Support for publishing to Maven repositories over SFTP using `maven-publish` plugin
 
-### Easier to identify ignored tests in HTML test report
+In previous releases, it was not possible to publish to a Maven repository (via the [`maven-publish` plugin](userguide/publishing_maven.html))
+via SFTP in the same manner that it was for an Ivy repository or when downloading dependencies.
+This restriction has been lifted, with the publishing now supporting all of the transport protocols that Gradle currently supports (`file`, `http(s)`, `sftp` and `s3`).
 
-The HTML test report now has a dedicated tab for ignored tests, at the overview and package level.
-This makes it much easier to see which tests were ignored at a glance.
+This change will also make it possible to seamlessly use any transports that Gradle will support in the future at that time.
 
-Thanks to [Paul Merlin](https://github.com/eskatos) for this improvement.
+Please see section [50.6. Repositories](userguide/dependency_management.html#sec:repositories) of the User Guide for more information on configuring SFTP repository access.
 
-### Support for building large zips
+### Depending on a particular Maven snapshot version
 
-It is now possible to build zips with the [Zip64 extension](http://en.wikipedia.org/wiki/Zip_\(file_format\)#ZIP64), enabling the building of large zip files.
+It is now possible to depend on particular Maven snapshot, rather than just the “latest” published version.
 
-    task largeZip(type: Zip) {
-        from 'lotsOfLargeFiles'
-        zip64 = true
+    dependencies {
+      compile "org.company:my-lib:1.0.0-20150102.010203-20"
     }
+    
+The Maven snapshot version number is a timestamp and snapshot number.
+The snippet above is depending on the snapshot of version `1.0.0` published on the 2nd of January 2015, at 01:02:03 AM which was the 20th snapshot published.
 
-The zip standard does not support containing more than 65535 files, containing any file greater than 4GB or being greater than 4GB compressed.
-If your zip file meets any of these criteria, then the zip must be built with the
-[`zip64` property](dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:zip64) set to `true` (it is `false` by default).
-This flag also applies to all JARs, WARs, EARs and anything else that uses the Zip format.
+This feature was contributed by [Noam Y. Tenne](https://github.com/noamt).
 
-However, not all Zip readers support the Zip64 extensions.
-Notably, the `ZipInputStream` JDK class does not support Zip64 for versions earlier than Java 7.
-This means you should not enable this property if you are building JARs to be used with Java 6 and earlier runtimes.
+### Support for “annotation processing” of Groovy code
 
-Thanks to [Jason Gauci](https://github.com/MisterTea) for this improvement.
+It is now possible to use Java's [“annotation processing”](https://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/Processor.html) with Groovy code.
+This, for example, allows using the [Dagger](http://square.github.io/dagger) dependency injection library, that relies on annotation processing, with Groovy code.
 
-### Support for consuming Apache Maven POMs with active profiles
+Annotation processing is a Java centric feature.
+Support for Groovy is achieved by having annotation processors process the Java “stubs” that are generated from Groovy code.
+The stubs convey the structure of the class, which is typically used to allow Java code to compile against the Groovy code in “one pass”.
+Annotations on structural elements (i.e. classes/methods/fields) will be present in the generated stubs.
+Annotation processors will detect such annotations on stubs as they would with “normal” Java code.
 
-Gradle now respects POM profiles that are [active by default](https://maven.apache.org/pom.html#Activation), during dependency resolution.
-More specifically, the properties, dependencies and dependency management information is now respected.
+The support for annotation processing of Groovy code is limited to annotation processors that generate new classes, and not to processors that modify annotated classes.
+The official and supported annotation processing mechanisms _do not_ support modifying classes, so almost all annotation processors will work.
+However, some popular annotation processing tools, notably [Project Lombok](http://projectlombok.org), that use unofficial API to modify classes will not work.
 
-### Customise Clang compiler tool chain (i)
+This feature was contributed by [Will Erickson](https://github.com/Sarev0k).
 
-In earlier Gradle versions, it was possible to customize the GCC compiler tool chain in various ways. For example, you could use the `cCompiler.executable` property
-to specify a custom C compiler to use.
+### Generate wrapper with specific version from command-line
 
-Now you can customize the Clang tool chain in exactly the same way as the GCC tool chain.
+Previously to generate a Gradle wrapper with a specific version, or a custom distribution URL,
+you had to change the `build.gradle` file to contain a wrapper task with a configured `gradleVersion` property.
 
-For more details see [Clang](dsl/org.gradle.nativebinaries.toolchain.Clang.html) and [GCC](dsl/org.gradle.nativebinaries.toolchain.Gcc.html).
+Now the target Gradle version or the distribution URL can be configured from the command-line, without having
+to add or modify the task in `build.gradle`:
 
-### Improved Visual Studio project file generation (i)
+<pre><tt>gradle wrapper --gradle-version 2.3</tt></pre>
 
-Gradle 1.11 added support for [generating Visual Studio configuration files](http://www.gradle.org/docs/current/release-notes#generate-visual-studio-configuration-for-a-native-binary-project).
-This feature has been improved in the following ways in Gradle 1.12:
+And with a distribution URL:
 
-* Visual studio log files are generated into `.vs` instead of the project directory
-* Project files are named by `ProjectNativeComponent.name` instead of `ProjectNativeComponent.baseName`
-* Header files co-located with source files are now include in the generated project
+<pre><tt>gradle wrapper --gradle-distribution-url https://myEnterpriseRepository:7070/gradle/distributions/gradle-2.3-bin.zip</tt></pre>
 
-### Updated mapping of dependencies to IDEA classpath scopes
+This feature was contributed by [Lóránt Pintér](https://github.com/lptr).
 
-There were changes in IDEA project mapping related to bug reports [GRADLE-2017] and [GRADLE-2231]. Projects generated by the [IDEA plugin](userguide/idea_plugin.html) now
-better map project dependencies to classpath scopes in IDEA modules.
+### Customization of application plugin start script generation (i)
 
-### Easier debugging of JVM `Test` and `JavaExec` processes (i)
+The [application plugin](userguide/application_plugin.html) can be used to create “executable” distributions Java-based application, including operating system specific start scripts.
+While certain values in the generated scripts (e.g. main class name, classpath) were customizable, the script content was generally hardcoded and cumbersome to change.
+With Gradle 2.4, it is now much easier to fully customise the start scripts.
 
-The [`Test`](dsl/org.gradle.api.tasks.testing.Test.html) and [`JavaExec`](dsl/org.gradle.api.tasks.JavaExec.html) tasks both now support a `--debug-jvm` invocation time switch, which is equivalent
-to setting the `debug` property of these tasks to `true`.
+The generation of the scripts is performed by a [`CreateStartScripts`](dsl/org.gradle.jvm.application.tasks.CreateStartScripts.html) task.
+Please consult its [DSL reference](dsl/org.gradle.jvm.application.tasks.CreateStartScripts.html) for customization examples.
 
-This makes it easy, for example, to launch the application in debug mode when using the [Application plugin](userguide/application_plugin.html)…
+### Tooling API improvements (i)
 
-<pre><tt>gradle run --debug-jvm</tt></pre>
+The tooling API allows Gradle to be embedded in other applications, such as an IDE. This release includes a number of improvements to the tooling API:
 
-This starts the JVM process in debug mode, and halts the process until a debugger attaches on port 5005.
-The same can be done for any [`Test`](dsl/org.gradle.api.tasks.testing.Test.html) task.
+An application can now receive test progress events as a build is executed. Using these events, an application can display or report on test execution
+as tests run during the build.
 
-### Source and Javadoc artifacts declared in ivy.xml are recognised by IDE plugins
+An application receives test events using the
+[`LongRunningOperation.addTestProgressListener()`](javadoc/org/gradle/tooling/LongRunningOperation.html#addTestProgressListener-org.gradle.tooling.events.test.TestProgressListener-) method.
 
-The Gradle `eclipse` and `idea` plugins are able to find and download the source artifacts for external dependencies, and link
-these artifacts into the generated IDE files.
+The following additions have been added to the respective [Tooling API](userguide/embedding.html) models:
 
-In addition to the conventional classifier-based scheme for locating source and javadoc artifacts, Gradle will now recognise
-artifacts declared in a specific configuration of an `ivy.xml` file.
+* [`GradleProject.getProjectDirectory()`](javadoc/org/gradle/tooling/model/GradleProject.html#getProjectDirectory--) returns the project directory of the project.
+* [`GradleEnvironment.getGradleUserHome()`](javadoc/org/gradle/tooling/model/build/GradleEnvironment.html#getGradleUserHome--)
+returns the effective Gradle user home directory that will be used for operations performed through the Tooling API.
 
-For an IDE project that references an external module located in an `ivy` repository, Gradle will now include:
+### Rule based model configuration improvements 
 
-* Source artifacts declared in a `sources` configuration in `ivy.xml`
-* Javadoc artifacts declared in a `javadoc` configuration in `ivy.xml`
-* Source artifacts conventionally named with a `sources` classifier: eg. `module-1.0-sources.jar`
-* Javadoc artifacts conventionally named with a `javadoc` classifier: eg. `module-1.0-javadoc.jar`
+A number of improvements have been made to the rule based model configuration used by the native language plugins in this release.
+There is also a [new User Guide chapter that explains just what “rule based model configuration” is](userguide/new_model.html).
 
-### HTTPS wrapper downloads
+Key additions and improvements in Gradle 2.4:
 
-The [Gradle Wrapper](userguide/gradle_wrapper.html) is now downloaded over HTTPS.
-The `gradle-wrapper.properties` file created by the `wrapper` task will now specify a HTTPS URL as the location of the Gradle distribution to download.
+- A basic [model report](userguide/new_model.html#N17A51) task that visualizes the model space for a project.
+- Addition of [`@Defaults`](javadoc/org/gradle/model/Defaults.html) and [`@Validate`](javadoc/org/gradle/model/Validate.html) lifecycle rules.
+- Additions to the API of [`CollectionBuilder`](javadoc/org/gradle/model/collection/CollectionBuilder.html) and 
+[`ManagedSet`](javadoc/org/gradle/model/collection/ManagedSet.html) that allow rules to be applied to all elements in the collection, 
+or to a particular element, or all elements of a given type.
 
-Existing projects should consider updating the `gradle/wrapper/gradle-wrapper.properties` file to use `https` instead of `http` for the `distributionUrl` property value.
-No other change to the URL is necessary.
+It is now also possible to [create model elements via the DSL](userguide/new_model.html#N17A0F).
 
 ## Fixed issues
 
 ## Deprecations
 
 Features that have become superseded or irrelevant due to the natural evolution of Gradle become *deprecated*, and scheduled to be removed
-in the next major Gradle version (Gradle 2.0). See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
+in the next major Gradle version (Gradle 3.0). See the User guide section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
+
+The following are the newly deprecated items in this Gradle release. If you have concerns about a deprecation, please raise it via the [Gradle Forums](http://discuss.gradle.org).
+
+### Setting number of build execution threads with `--parallel-threads`
+
+The, incubating, `--parallel-threads` command line switch has been superseded by the new `--max-workers` command line switch and synonymous `org.gradle.workers.max` build property.
+Likewise, the [`StartParameter.getParallelThreadCount()`](javadoc/org/gradle/StartParameter.html#getParallelThreadCount\(\)) has also been deprecated.
+
+This setting configured [parallel project execution](userguide/multi_project_builds.html#sec:parallel_execution).
+
+The `--parallel-threads` is still respected, until removed.
+If not specified, the value specified for `--max-workers` will be used.
+
+If you were using an invocation such as:
 
-The following are the newly deprecated items in this Gradle release. If you have concerns about a deprecation, please raise it via the [Gradle Forums](http://forums.gradle.org).
+<pre><tt>./gradlew build --parallel-threads=4</tt></pre>
 
-### Tooling API version compatibility
+The replacement is now:
 
-The [Tooling API](userguide/embedding.html) is a mechanism for embedding Gradle and/or driving Gradle programmatically.
-It is used by IDEs and other _tooling_ to integrate with Gradle.
+<pre><tt>./gradlew build --max-workers=4 --parallel</tt></pre>
 
-* Connecting to 1.0-milestone-8 and earlier providers is now deprecated and will not be supported in Gradle 2.0. This means that the Gradle 2.0 Tooling API client will
-not be able to run builds that use Gradle 1.0-milestone-8 and earlier.
-* Client versions older than 1.2 are now deprecated and will not be supported in Gradle 2.0. This means that the Gradle 1.2 and earlier Tooling API clients will not
-be able to run builds that use Gradle 2.0 and later.
+Alternatively, the following can be used, which will use the default value for `--max-workers`:
 
-If your project is building with Gradle 1.0-milestone-8 or earlier, you are __strongly__ encouraged to upgrade to a more recent Gradle version.
-All versions of integrating tools released since the release of Gradle 1.2 (September 2012) should be using a Tooling API client newer than version 1.2.
+<pre><tt>./gradlew build --parallel</tt></pre>
 
-### Deprecated method in Tooling API
+### Lifecycle plugin changes
 
-The <a href="javadoc/org/gradle/tooling/model/Task.html#getProject()">`org.gradle.tooling.model.Task.getProject()`</a> method is now deprecated and
-can throw `UnsupportedMethodException`. There is no replacement as it is expected that the caller has a reference to project prior calling to this method.
+The tasks `build`, `clean`, `assemble` and `check` are part of the standard build lifecycle and are added by most plugins, typically implicitly through the `base` or `language-base` plugins.
+Due to the way these tasks are implemented, it is possible to redefine them simply by creating your own task of the same name.
+This behavior has been deprecated and will not be supported in Gradle 3.0.
+That is, attempting to define a task with the same name as one of these lifecycle tasks when they are present will become an error just like any other attempt to create a task with the same name as an existing task.
 
 ## Potential breaking changes
 
-### Incremental Scala compilation
+### Incompatibility with Team City 9.0.3 and earlier
+
+An internal change to Gradle test execution has broken the Gradle integration provided by [JetBrains' TeamCity integration server](https://www.jetbrains.com/teamcity/).
+JetBrains have acknowledged the issue and have fixed the problem for the pending 9.0.4 release of TeamCity.
+
+Please see [https://youtrack.jetbrains.com/issue/TW-40615](https://youtrack.jetbrains.com/issue/TW-40615) for more information on the problem.
+
+If you are affected by this issue, you can install a pre-release version of the Gradle plugin for TeamCity which is available via the above link.
+
+### Class reuse across builds when using the Daemon
+
+Due to the [performance improvements enabled by reusing classes across builds with the Daemon](#improved-performance-of-gradle-daemon-via-class-reuse), 
+builds that equate static state to build state may behave differently with Gradle 2.4.
+
+Previously, classes were not reused across builds.
+This meant that each build created a new class object and therefore fresh static class state.
+It was therefore theoretically possible to use static state of classes loaded during the build as a kind of “build state”,
+which was reset for each build.
+This was never a supported feature or recommended technique.
+
+Now, due to classes being reused this is no longer possible.
+Class objects are reused across builds, including their static state.
+
+The recommended way of achieving “build state” is to use a root project extension, or extra property.
+
+### Model DSL changes
+
+There have been some changes to the behaviour of the `model { ... }` block:
+
+- The `tasks` container now delegates to a `CollectionBuilder<Task>` instead of a `TaskContainer`.
+- The `components` container now delegates to a `CollectionBuilder<ComponentSpec>` instead of a `ComponentSpecContainer`.
+- The `binaries` container now delegates to a `CollectionBuilder<BinarySpec>` instead of a `BinaryContainer`.
+
+Generally, the DSL should be the same, except:
+
+- Elements are not implicitly created. In particular, to define a task with default type, you need to use `model { tasks { myTask(Task) { ... } }`
+- Elements are not created or configured eagerly, but are configured as required.
+- The `create` method returns void.
+- The `withType()` method selects elements based on the public contract type rather than implementation type.
+- Using create syntax fails when the element already exists.
+- There are currently no query method on this interface.
+
+### Updated default Scala Zinc compiler version
+
+The default version of the Scala Zinc compiler has changed from 0.3.0 to 0.3.5.3.
+
+### `MavenDeployer` no longer uses global Maven `settings.xml`
+
+When publishing to a Maven repository using an `Upload` task (e.g. `uploadArchives`) and the `mavenDeployer` repository type,
+the global Maven `settings.xml` file is no longer consulted.
+
+Previously, the “mirror”, “authentication” and “proxy” settings defined in the global `settings.xml` file were effecting publishing,
+making builds less portable which is no longer the case.
+
+This resolves [GRADLE-2681].
+
+The _user_ settings file (i.e. `~/.m2/settings.xml`) is still consulted for the location of the local repository when performing a local install
+(e.g. `gradle install` or `gradle publishToMavenLocal`).
+
+### `PublishToMavenLocal.repository` property has been removed
+
+Previously, the [`PublishToMavenLocal`](javadoc/org/gradle/api/publish/maven/tasks/PublishToMavenLocal.html) task 
+(as used by the [`maven-publishing` plugin](userguide/publishing_maven.html)) could be configured with an `ArtifactRepository` instance, 
+which would specify the location to install to.
+The default repository was the repository that returned by
+[`RepositoryHandler.mavenLocal()`](dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html#org.gradle.api.artifacts.dsl.RepositoryHandler:mavenLocal\(\)).
+
+It is no longer possible to provide an arbitrary repository to this task.
+If you need to publish to an arbitrary repository, please use the [`PublishToMavenRepository`](javadoc/org/gradle/api/publish/maven/tasks/PublishToMavenRepository.html) task type instead.
+
+### Changed default assembler executable for GCC/Clang tool chains
+
+The default tool used when turning assembler into object files is now `gcc` or `clang` instead of `as`.  
+Some arguments that were specific to `as` were converted to their GCC/Clang equivalents.  
+If you were passing arguments to the assembler, you may now need to use `-Wa` when passing them.  
+
+Please see [GCC's documentation](https://gcc.gnu.org/onlinedocs/gcc/Assembler-Options.html) for passing arguments to the assembler.
+
+### Changes to `CommandLineToolConfiguration.withArguments()` usage
+
+The [`CommandLineToolConfiguration`](javadoc/org/gradle/nativeplatform/toolchain/CommandLineToolConfiguration.html) allows fine grained configuration of a tool execution for the native domain,
+for example when configuring a [`GccPlatformToolChain`](javadoc/org/gradle/nativeplatform/toolchain/GccPlatformToolChain.html).
+There are changes to the semantics of the `withArguments()` method.
+
+The `withArguments()` method used to be called just before Gradle built the command-line arguments for the underlying tool for each source file.
+The arguments passed to this would include the path to the source file and output file. 
+This hook was intended to capture "overall" arguments to the command-line tool instead of "per-file" arguments. 
+Now, the `withArguments()` method is called once per task execution and does not contain any specific file arguments.  
+Any changes to arguments using this method will affect all source files.
+
+### Implicit Groovy source compilation while compiling build script is now disabled
+
+The Groovy compiler by default looks for dependencies in source form before looking for them in class form.
+That is, if Groovy code being compiled references `foo.bar.MyClass` then the compiler will look for `foo/bar/MyClass.groovy` on the classpath.
+If it finds such a file, it will try to compile it.
+If it doesn't it will then look for a corresponding class file.
+
+As of Gradle 2.4, this feature has been disabled for _build script_ compilation.
+It does not affect the compilation of “application” Groovy code (e.g. `src/main/groovy`).
+It has been disabled to make build script compilation faster.
+
+If you were relying on this feature, please use the [`buildSrc` feature](userguide/organizing_build_logic.html#sec:build_sources) as a replacement.
+
+### Changes to Groovy compilation when annotation processors are present
+
+When annotation processors are “present” for a Groovy compilation operation, all generated stubs are now compiled regardless of whether they are required or not.
+This change was required in order to have annotation processors process the stubs.
+Previously the stubs were made available to the Java code under compilation via the source path, which meant that only classes actually referenced by Java code were compiled.
+The implication is that more compilation is now required for Groovy code when annotation processors are present, which means longer compile times.
+
+This is unlikely to be noticeable unless the code base contains a lot of Groovy code.
+If this is problematic for your build, the solution is to separate the code that requires annotation processing from the code that does not to some degree.
+
+### Changes to default value for Java compilation `sourcepath`
 
-The version of the Scala incremental compiler, Zinc, that Gradle uses has been upgraded to a version 0.3.0.
-This might be a breaking change for users who explicitly configured an early version of zinc in their build scripts.
-There should be very few such users, if any.
+The source path indicates the location of source files that _may_ be compiled if necessary.
+It is effectively a complement to the class path, where the classes to be compiled against are in source form.
+It does __not__ indicate the actual primary source being compiled.
 
-### Changes to incubating native support
+The source path feature of the Java compiler is rarely needed for modern builds that use dependency management.
 
-* '-Xlinker' is no longer automatically added to linker args for GCC or Clang. If you want to pass an argument directly to 'ld' you need to add this escape yourself.
-* Tasks for Windows resource compilation are now named 'compileXXXX' instead of 'resourceCompileXXX'.
+The default value for the source path as of this release is now effectively an empty source path.
+Previously Gradle implicitly used the same default as the `javac` tool, which is the `-classpath` value.
+This causes unexpected build results when source accidentally ends up on the classpath, which can happen when dependencies surprisingly include source as well as binaries.
 
-### Change to JUnit XML file for skipped tests
+This improvement was contributed by [Thomas Broyer](https://github.com/tbroyer).
 
-The way that skipped/ignored tests are represented in the JUnit XML output file produced by the `Test` task.
-Gradle now produces the same output, with regard to skipped tests, as Apache Ant and Apache Maven.
-This format is accepted, and expected, by all major Continuous Integration servers.
+### Changes to `AuthenticationSupported.getCredentials()` method
 
-This change is described as follows:
+Due to the addition of support for [AWS S3 backed repositories](#support-for-amazon-web-services-s3-backed-repositories), some behavioral changes have been made to the
+[`AuthenticationSupported`](dsl/org.gradle.api.artifacts.repositories.AuthenticationSupported.html) interface, which is implemented by 
+[`MavenArtifactRepository`](dsl/org.gradle.api.artifacts.repositories.MavenArtifactRepository.html) and [`IvyArtifactRepository`](dsl/org.gradle.api.artifacts.repositories.IvyArtifactRepository.html).
 
-1. The `testsuite` element now contains a `skipped` attribute, indicating the number of skipped tests (may be 0)
-2. The element representing a test case is now always named `testcase` (previously it was named `ignored-testcase` if it was a skipped test)
-3. If a test case was skipped, a child `<skipped/>` element will be present
+The `getCredentials()` and `credentials(Action)` methods now throw an `IllegalStateException` if the configured credentials are not of type 
+[`PasswordCredentials`](javadoc/org/gradle/api/artifacts/repositories/PasswordCredentials.html), now that it is possible to use different types of credentials.
 
-No changes are necessary to build scripts or Continuous Integration server configuration to accommodate this change.
+### Changes to API of `AntlrTask`
 
-### Ordering of dependencies in imported Ant builds
+The [`AntlrTask`](dsl/org.gradle.api.plugins.antlr.AntlrTask.html) previous unnecessarily exposed the internal methods `buildArguments()` and `evaluateAntlrResult()`.
 
-The ordering of Ant target dependencies is now respected when possible.
-This may cause tasks of imported Ant builds to executed in a different order from this version of Gradle on.
+These methods have been removed.
 
-Given…
+### Updated libraries used by the Gradle API
 
-    <target name='a' depends='d,c,b'/>
+Some dependencies used in Gradle have been updated.
 
-A shouldRunAfter [task ordering](userguide/more_about_tasks.html#sec:ordering_tasks) will be applied to the dependencies so that,
-`c.shouldRunAfter d` and `b.shouldRunAfter c`.
+* **Slf4j** - 1.7.7 to 1.7.10
+* **Groovy** - 2.3.9 to 2.3.10
+* **Ant** - 1.9.3 to 1.9.4
 
-This is in alignment with Ant's ordering of target dependencies and resolves a long standing Gradle enhancement request [GRADLE-1102].
+These libraries are expected to be fully backwards compatible.
+It is expected that no Gradle builds will be negatively affected by these changes.
 
-### Invalid large zip files now fail the build
+### Updated default tool versions for code quality plugins
 
-If a zip file is built without the `zip64` flag (new in this release) set to true that surpasses the file size and count limits
-of the zip format, Gradle will now fail the build.
-Previously, it may have silently created an invalid zip.
+The default version of the corresponding tool of the following code quality plugins have been updated:
 
-To allow the large zip to be correctly built, set the `zip64` property of the task to `true`.
+* The `checkstyle` plugin now uses version 5.9 as default (was 5.7).
+   - The latest checkstyle version currently available is 6.4.1 but be aware that this version is not java 1.6 compliant
+   - Be aware that there is was a breaking change of the `LeftCurly` rule introduced in checkstyle 5.8 (see https://github.com/checkstyle/checkstyle/issues/247)
+* The `pmd` plugin now uses version 5.2.3 as default (was 5.1.1).
+* The `findbugs` plugin now uses version 3.0.1 as default (was 3.0.0).
+* The `codenarc` plugin now uses version 0.23 as default (was 0.21).
 
-### Change to signature of `Test.filter(Closure)`
+### Deprecated ComponentMetadataHandler.eachComponent() has been removed
 
-The incubating `Test.filter(Closure)` method introduced in 1.10 for configuring the `TestFilter` has been changed to be more consistent with other configuration methods.
-This method now accepts an `Action` and no longer returns the `TestFilter`.
-This change should not require any adjustments to build scripts as this method can still be called with a `Closure`, upon which it will be implicitly converted into an `Action`.
+This method (and all overloads) has been removed in 2.4, after [being deprecated in Gradle 2.3](http://gradle.org/docs/2.3/release-notes#component-metadata-rule-enhancements)
+and superseded by the [`all()` method](javadoc/org/gradle/api/artifacts/dsl/ComponentMetadataHandler.html#all\(org.gradle.api.Action\)).
 
-### Change to signature of `IdeaModule.singleEntryLibraries`
+### Removal of package scoped methods of `LogLevel`
 
-The property `IdeaModule.singleEntryLibraries` was previously declared as a `Map` with values of type `Collection<File>`. The values are in fact `Iterable<File>` and
-the type signature has been updated.
+The package scoped methods of the [`LogLevel`](javadoc/org/gradle/api/logging/LogLevel.html) enumeration have been removed.
 
 ## External contributions
 
 We would like to thank the following community members for making contributions to this release of Gradle.
 
-* [Marcin Erdmann](https://github.com/erdi)
-    * dependency ordering of imported Ant targets [GRADLE-1102]
-    * fixes for excluding tasks [GRADLE-2974] & [GRADLE-3031]
-* [Jesse Glick](https://github.com/jglick) - enabling newlines in option values passed to build
-* [Zeeke](https://github.com/zeeke) - documentation improvements
-* [Kamil Szymański](https://github.com/kamilszymanski) - documentation improvements
-* [Jakub Kubryński](https://github.com/jkubrynski) - handling of empty string proxy system property values
-* [Lee Symes](https://github.com/leesdolphin) & [Greg Temchenko](https://github.com/soid) - fix skipped test representation in JUnit XML result files [GRADLE-2731]
-* [Ivan Vyshnevskyi](https://github.com/sainaen) - Fixes to HTML test report
-* [Vincent Cantin](https://github.com/green-coder) - documentation improvements
-* [Sterling Greene](https://github.com/big-guy) - Support for developing Gradle in Eclipse
-* [Matthew Michihara](https://github.com/matthewmichihara) - Documentation improvements
-* [Andrew Oberstar](https://github.com/ajoberstar) - Improved Sonar support on Java 1.5 [GRADLE-3005]
-* [Mark Johnson](https://github.com/elucify) - documentation improvements
-* [Paul Merlin](https://github.com/eskatos) - ignored tests tab for HTML test report
-* [Jason Gauci](https://github.com/MisterTea) - Support for large zips
-* [Zsolt Kúti](https://github.com/tinca) - Fixes for Gradle on FreeBSD
-* [Rich Jones](https://github.com/Miserlou) - HTTPS URLs for wrapper downloads
+* [Adrian Kelly](https://github.com/adrianbk) - Support for Amazon Web Services S3 dependency repositories
+* [Victor Bronstein](https://github.com/victorbr)
+    - Convert NotationParser implementations to NotationConverter
+    - Only parse Maven settings once per project to determine local Maven repository location (GRADLE-3219)
+* [Vyacheslav Blinov](https://github.com/dant3) - Fix for `test.testLogging.showStandardStreams = false` (GRADLE-3218)
+* [Michal Bendowski](https://github.com/bendowski-google) - Documentation improvements
+* [Daniel Siwiec](https://github.com/danielsiwiec) - Documentation improvements
+* [Andreas Schmid](https://github.com/aaschmid) - Improved test coverage for facet type configuration in `GenerateEclipseWtpFacet`
+* [Roman Donchenko](https://github.com/SpecLad)
+    - Fix PatternSet so that all files are not excluded when Ant global excludes are cleared (GRADLE-3254)
+    - Improvements to `Spec` implementations
+* [Lóránt Pintér](https://github.com/lptr) - Allow setting wrapper version on command-line
+* [Andreas Schmid](https://github.com/aaschmid) - Retain defaults when using `EclipseWtpComponent.resource()` and `EclipseWtpComponent.property()`
+* [Mikolaj Izdebski](https://github.com/mizdebsk) - Use hostname command as fallback way of getting build host name in Gradle build
+* [Andrea Cisternino](https://github.com/acisternino) - Make JavaFX available to Groovy compilation on Java 8
+* [Will Erickson](https://github.com/Sarev0k) - Support for annotation processing of Groovy code
+* [Noam Y. Tenne](https://github.com/noamt) - Declare a dependency on a specific timestamped Maven snapshot
+* [Thomas Broyer](https://github.com/tbroyer) - Better defaults for Java compilation source path
 
 We love getting contributions from the Gradle community. For information on contributing, please see [gradle.org/contribute](http://gradle.org/contribute).
 
diff --git a/subprojects/docs/src/docs/userguide/announcePlugin.xml b/subprojects/docs/src/docs/userguide/announcePlugin.xml
index bbd7b7d..79df721 100644
--- a/subprojects/docs/src/docs/userguide/announcePlugin.xml
+++ b/subprojects/docs/src/docs/userguide/announcePlugin.xml
@@ -1,6 +1,6 @@
 <chapter id='announce_plugin'>
     <title>The Announce Plugin</title>
-    <para>The Gradle announce allows to send custom announcements during a build. The following notification systems are supported:
+    <para>The Gradle announce plugin allows you to send custom announcements during a build. The following notification systems are supported:
 	<itemizedlist>
  		<listitem><ulink url='http://twitter.com'>Twitter</ulink></listitem>
  		<listitem><ulink url='http://manpages.ubuntu.com/manpages/gutsy/man1/notify-send.1.html'>notify-send</ulink> (Ubuntu)</listitem>
@@ -76,6 +76,6 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.announce.AnnouncePluginExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.announce.AnnouncePluginExtension"/> class in the API documentation.</para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/ant.xml b/subprojects/docs/src/docs/userguide/ant.xml
index 6831128..a32ee04 100644
--- a/subprojects/docs/src/docs/userguide/ant.xml
+++ b/subprojects/docs/src/docs/userguide/ant.xml
@@ -23,7 +23,7 @@
     </para>
 
     <para>Ant can be divided into two layers. The first layer is the Ant language. It provides the syntax for the
-        <filename>build.xml</filename>, the handling of the targets, special constructs like macrodefs, and so on.
+        <filename>build.xml</filename> file, the handling of the targets, special constructs like macrodefs, and so on.
         In other words, everything except the Ant tasks and types. Gradle understands this language, and allows you to
         import your Ant <filename>build.xml</filename> directly into a Gradle project. You can then use the targets of
         your Ant build as if they were Gradle tasks.
@@ -62,7 +62,7 @@
         <para>You execute an Ant task by calling a method on the <literal>AntBuilder</literal> instance. You use the task
             name as the method name. For example, you execute the Ant <literal>echo</literal> task by calling the
             <literal>ant.echo()</literal> method. The attributes of the Ant task are passed as Map parameters to the
-            method. Below is an example which executes the <literal>echo</literal> task. Notice that we can also mix
+            method. Below is an example of the <literal>echo</literal> task. Notice that we can also mix
             Groovy code and the Ant task markup. This can be extremely powerful.</para>
         <sample id="useAntTask" dir="userguide/ant/useAntTask" title="Using an Ant task">
             <sourcefile file="build.gradle"/>
@@ -151,6 +151,19 @@
             <sourcefile file="build.xml"/>
             <output args="hello"/>
         </sample>
+        <para>
+            Sometimes it may be necessary to “rename” the task generated for an Ant target to avoid a naming collision with existing Gradle tasks.
+            To do this, use the <apilink class="org.gradle.api.AntBuilder" method="importBuild(java.lang.Object, org.gradle.api.Transformer)" /> method.
+        </para>
+        <sample id="renameAntDelegate" dir="userguide/ant/renameTask" title="Renaming imported Ant targets">
+            <sourcefile file="build.gradle"/>
+            <sourcefile file="build.xml"/>
+            <output args="a-hello"/>
+        </sample>
+        <para>
+            Note that while the second argument to this method should be a <apilink class="org.gradle.api.Transformer"/>, when programming in Groovy we can simply
+            use a closure instead of an anonymous inner class (or similar) due to <ulink url="http://mrhaki.blogspot.ie/2013/11/groovy-goodness-implicit-closure.html">Groovy's support for automatically coercing closures to single-abstract-method types</ulink>.
+        </para>
     </section>
 
     <section>
diff --git a/subprojects/docs/src/docs/userguide/antlrPlugin.xml b/subprojects/docs/src/docs/userguide/antlrPlugin.xml
index 42f64bd..cdf9d2a 100644
--- a/subprojects/docs/src/docs/userguide/antlrPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/antlrPlugin.xml
@@ -21,13 +21,13 @@
 
     <note>
         <para>
-            The ANTLR plugin only supports ANTLR version 2.
+            The ANTLR plugin supports ANTLR version 2, 3 and 4.
         </para>
     </note>
 
     <section>
         <title>Usage</title>
-        <para>To use the ANTLR plugin, include in your build script:</para>
+        <para>To use the ANTLR plugin, include the following in your build script:</para>
         <sample id="useAntlrPlugin" dir="antlr" title="Using the ANTLR plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -127,11 +127,17 @@
 
     <section>
         <title>Dependency management</title>
-        <para>The ANTLR plugin adds an <literal>antlr</literal> dependency configuration. You use this to declare the
-            ANTLR dependency that you wish to use.</para>
+        <para>
+            The ANTLR plugin adds an <literal>antlr</literal> dependency configuration which provides the ANTLR implementation to use.
+            The following example shows how to use ANTLR version 3.
+        </para>
         <sample id="declareAntlrVersion" dir="antlr" title="Declare ANTLR version">
             <sourcefile file="build.gradle" snippet="declare-dependency"/>
         </sample>
+        <para>
+            If no dependency is declared, <literal>antlr:antlr:2.7.7</literal> will be used as the default.
+            To use a different ANTLR version add the appropriate dependency to the <literal>antlr</literal> dependency configuration as above.
+        </para>
     </section>
 
     <section>
@@ -163,7 +169,7 @@
                     Not null
                 </td>
                 <td>
-                    The ANTLR grammar files of this source set. Contains all <filename>.g</filename> found in the ANTLR
+                    The ANTLR grammar files of this source set. Contains all <filename>.g</filename> files found in the ANTLR
                     source directories, and excludes all other types of files.
                 </td>
             </tr>
@@ -183,5 +189,17 @@
             </tr>
         </table>
     </section>
+    <section>
+        <title>Controlling the ANTLR generator process</title>
+        <para>
+            The ANTLR tool is executed in a forked process.
+            This allows fine grained control over memory settings for the ANTLR process.
+            To set the heap size of a ANTLR process, the <literal>maxHeapSize</literal> property
+            of <apilink class="org.gradle.api.plugins.antlr.AntlrTask"/> can be used.
+        </para>
+        <sample id="advanced" dir="antlr" title="setting custom max heap size for ANTLR">
+            <sourcefile file="build.gradle" snippet="memory-settings"/>
+        </sample>
+    </section>
 
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/applicationPlugin.xml b/subprojects/docs/src/docs/userguide/applicationPlugin.xml
index e0a14bf..fb426c1 100644
--- a/subprojects/docs/src/docs/userguide/applicationPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/applicationPlugin.xml
@@ -15,43 +15,114 @@
   -->
 <chapter id='application_plugin'>
     <title>The Application Plugin</title>
-    <para>The Gradle application plugin extends the language plugins with common application related tasks.
-	It allows running and bundling applications for the jvm.
-	</para>
-
+    <para>
+        The Application plugin facilitates creating an executable JVM application.
+        It makes it easy to start the application locally during development, and to packaging the application as a TAR and/or ZIP including operating system specific start scripts.
+    </para>
+    <para>
+        Applying the Application plugin also implicitly applies the <link linkend="java_plugin">Java plugin</link>.
+        The <literal>main</literal> source set is effectively the “application”.
+    </para>
+    <para>
+        Applying the Application plugin also implicitly applies the <link linkend="distribution_plugin">Distribution plugin</link>.
+        A <literal>main</literal> distribution is created that packages up the application, including code dependencies and generated start scripts.
+    </para>
     <section>
         <title>Usage</title>
-        <para>To use the application plugin, include in your build script:</para>
+        <para>To use the application plugin, include the following in your build script:</para>
         <sample id="useApplicationPlugin" dir="application" title="Using the application plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
-        <para>To define the main-class for the application you have to set the <literal>mainClassName</literal> property as shown below</para>
+        <para>
+            The only mandatory configuration for the plugin is the specification of the main class (i.e. entry point) of the application.
+        </para>
         <sample id="useApplicationPlugin" dir="application" title="Configure the application main class">
             <sourcefile file="build.gradle" snippet="mainClassName-conf"/>
         </sample>
-
-        <para>Then, you can run the application by running <userinput>gradle run</userinput>. Gradle will take care of building the application classes,
-            along with their runtime dependencies, and starting the application with the correct classpath.
+        <para>
+            You can run the application by executing the <userinput>run</userinput> task (type: <apilink class="org.gradle.api.tasks.JavaExec"/>).
+            This will compile the main source set, and launch a new JVM with its classes (along with all runtime dependencies) as the classpath and using the specified main class.
             You can launch the application in debug mode with <userinput>gradle run --debug-jvm</userinput> (see <apilink class="org.gradle.api.tasks.JavaExec" method="setDebug(boolean)" />).
         </para>
-
-        <para>The plugin can also build a distribution for your application. The distribution will package up the runtime dependencies of the application
-            along with some OS specific start scripts. All files stored in <filename>src/dist</filename> will be added to the root of the
-            distribution. You can run <userinput>gradle installApp</userinput> to create an image of the application in
-            <filename>build/install/<replaceable>projectName</replaceable></filename>. You can run <userinput>gradle distZip</userinput> to create a
-            ZIP containing the distribution.
-        </para>
-
         <para>
-            If your Java application requires a specific set of JVM settings or system properties, you can configure the <literal>applicationDefaultJvmArgs</literal> property.
+            If your application requires a specific set of JVM settings or system properties, you can configure the <literal>applicationDefaultJvmArgs</literal> property.
             These JVM arguments are applied to the <literal>run</literal> task and also considered in the generated start scripts of your distribution.
         </para>
         <sample id="configureApplicationDefaultJvmArgs" dir="application" title="Configure default JVM settings">
             <sourcefile file="build.gradle" snippet="application-defaultjvmargs"/>
         </sample>
-
+        <section>
+            <title>The distribution</title>
+            <para>
+                A distribution of the application can be created, by way of the <link linkend="distribution_plugin">Distribution plugin</link> (which is automatically applied).
+                A <literal>main</literal> distribution is created with the following content:
+            </para>
+            <table>
+                <title>Distribution content</title>
+                <thead>
+                    <tr>
+                        <td>Location</td>
+                        <td>Content</td>
+                    </tr>
+                </thead>
+                <tr>
+                    <td>
+                        (root dir)
+                    </td>
+                    <td>
+                        <filename>src/dist</filename>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <filename>lib</filename>
+                    </td>
+                    <td>
+                        All runtime dependencies and main source set class files.
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <filename>bin</filename>
+                    </td>
+                    <td>
+                        Start scripts (generated by <literal>createStartScripts</literal> task).
+                    </td>
+                </tr>
+            </table>
+            <para>
+                Static files to be added to the distribution can be simply added to <filename>src/dist</filename>.
+                More advanced customization can be done by configuring the <apilink class="org.gradle.api.file.CopySpec"/> exposed by the main distribution.
+            </para>
+            <sample id="includeTaskOutputInApplicationDistribution" dir="application" title="Include output from other tasks in the application distribution">
+                <sourcefile file="build.gradle" snippet="distribution-spec"/>
+            </sample>
+            <para>
+                By specifying that the distribution should include the task's output files (see <xref linkend="sec:task_inputs_outputs"/>), Gradle knows
+                that the task that produces the files must be invoked before the distribution can be assembled and will take care of this for you.
+            </para>
+            <sample id="dependentTaskForApplicationDistributionOutput" dir="application" title="Automatically creating files for distribution">
+                <output args="distZip"/>
+            </sample>
+            <para>
+                You can run <userinput>gradle installDist</userinput> to create an image of the application in <filename>build/install/<replaceable>projectName</replaceable></filename>.
+                You can run <userinput>gradle distZip</userinput> to create a ZIP containing the distribution,
+                <userinput>gradle distTar</userinput> to create an application TAR or <userinput>gradle assemble</userinput> to build both.
+            </para>
+        </section>
+        <section>
+            <title>Customizing start script generation</title>
+            <para>
+                The application plugin can generate Unix (suitable for Linux, Mac OS X etc.) and Windows start scripts out of the box.
+                The start scripts launch a JVM with the specified settings defined as part of the original build and runtime environment (e.g. <literal>JAVA_OPTS</literal> env var).
+                The default script templates are based on the same scripts used to launch Gradle itself, that ship as part of a Gradle distribution.
+            </para>
+            <para>
+                The start scripts are completely customizable.
+                Please refer to the documentation of <apilink class="org.gradle.jvm.application.tasks.CreateStartScripts"/> for more details and customization examples.
+            </para>
+        </section>
     </section>
-
     <section>
         <title>Tasks</title>
         <para>The Application plugin adds the following tasks to the project.</para>
@@ -87,7 +158,7 @@
             </tr>
             <tr>
                 <td>
-                    <literal>installApp</literal>
+                    <literal>installDist</literal>
                 </td>
                 <td>
                     <literal>jar</literal>, <literal>startScripts</literal>
@@ -105,7 +176,7 @@
                 <td><apilink class="org.gradle.api.tasks.bundling.Zip"/></td>
                 <td>Creates a full distribution ZIP archive including runtime libraries and OS specific scripts.</td>
             </tr>
-	        <tr>
+            <tr>
                 <td>
                     <literal>distTar</literal>
                 </td>
@@ -117,36 +188,11 @@
             </tr>
         </table>
     </section>
-
     <section>
-            <title>Convention properties</title>
-            <para>The application plugin adds some properties to the project, which you can use to configure its behaviour. See <apilink class="org.gradle.api.Project"/>.
-            </para>
-    </section>
-
-    <section id="application_distribution_resources">
-            <title>Including other resources in the distribution</title>
-            <para>
-                One of the convention properties added by the plugin is <literal>applicationDistribution</literal> which is a <apilink class="org.gradle.api.file.CopySpec"/>.
-                This specification is used by the <literal>installApp</literal> and <literal>distZip</literal> tasks as the specification of what is to be 
-                include in the distribution. Above copying the starting scripts to the <filename>bin</filename> dir and necessary jars to <filename>lib</filename> 
-                in the distribution, all of the files from the <filename>src/dist</filename> directory are also copied. To include any static files in the 
-                distribution, simply arrange them in the <filename>src/dist</filename> directory.
-            </para>
-            <para>
-                If your project generates files to be included in the distribution, e.g. documentation, you can add these files to the distribution by adding to the 
-                <literal>applicationDistribution</literal> copy spec.
-            </para>
-            <sample id="includeTaskOutputInApplicationDistribution" dir="application" title="Include output from other tasks in the application distribution">
-                <sourcefile file="build.gradle" snippet="distribution-spec"/>
-            </sample>
-            <para>
-                By specifying that the distribution should include the task's output files (see <xref linkend="sec:task_inputs_outputs"/>), Gradle knows 
-                that the task that produces the files must be invoked before the distribution can be assembled and will take care of this for you.
-            </para>
-            <sample id="dependentTaskForApplicationDistributionOutput" dir="application" title="Automatically creating files for distribution">
-                <output args="distZip"/>
-            </sample>
+        <title>Convention properties</title>
+        <para>
+            The application plugin adds some properties to the project, which you can use to configure its behaviour.
+            See the <apilink class="org.gradle.api.Project"/> class in the API documentation.
+        </para>
     </section>
-    
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/artifactDependenciesTutorial.xml b/subprojects/docs/src/docs/userguide/artifactDependenciesTutorial.xml
index a216488..d3f3e99 100644
--- a/subprojects/docs/src/docs/userguide/artifactDependenciesTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/artifactDependenciesTutorial.xml
@@ -27,18 +27,24 @@
             We call this process <firstterm>dependency resolution</firstterm>.
         </para>
         <para>
+        	Note that this feature provides a major advantage over Ant. With Ant, you only have the ability to
+        	specify absolute or relative paths to specific jars to load. With Gradle, you simply declare the “names”
+        	of your dependencies, and other layers determine where to get those dependencies from. You can get
+        	similar behavior from Ant by adding Apache Ivy, but Gradle does it better.
+        </para>
+        <para>
             Often, the dependencies of a project will themselves have dependencies. For example, Hibernate core requires
             several other libraries to be present on the classpath with it runs. So, when Gradle runs the tests for your
             project, it also needs to find these dependencies and make them available. We call these <firstterm>transitive dependencies</firstterm>.
         </para>
         <para>
             The main purpose of most projects is to build some files that are to be used outside the project.
-            For example, if your project produces a java library, you need to build a jar, and maybe a source jar and
+            For example, if your project produces a Java library, you need to build a jar, and maybe a source jar and
             some documentation, and publish them somewhere.
         </para>
         <para>These outgoing files form the publications of the project. Gradle also takes care of this important work for you. You declare the
             publications of your project, and Gradle take care of building them and publishing them somewhere.
-            Exactly what "publishing" means depends on what you want to do. You might want to copy the files to a local directory,
+            Exactly what “publishing” means depends on what you want to do. You might want to copy the files to a local directory,
             or upload them to a remote Maven or Ivy repository. Or you might use the files in another project in the same
             multi-project build.
             We call this process <firstterm>publication</firstterm>.
@@ -130,7 +136,7 @@
             An external dependency is identified using <literal>group</literal>, <literal>name</literal> and <literal>version</literal> attributes.
             Depending on which kind of repository you are using, <literal>group</literal> and <literal>version</literal> may be optional.
         </para>
-        <para>There is a shortcut form for declaring external dependencies, which uses a string of the form <literal>"<replaceable>group</replaceable>:<replaceable>name</replaceable>:<replaceable>version</replaceable>"</literal>.
+        <para>The shortcut form for declaring external dependencies looks like “<literal><replaceable>group</replaceable>:<replaceable>name</replaceable>:<replaceable>version</replaceable></literal>”.
         </para>
         <sample id="externalDependencies" dir="userguide/artifacts/externalDependencies" title="Shortcut definition of an external dependency">
             <sourcefile file="build.gradle" snippet="define-dependency-shortcut"/>
@@ -191,7 +197,7 @@
             You can also publish to Maven repositories. The syntax is slightly different.<footnote>
                 <para>We are working to make the syntax consistent for resolving from and publishing to Maven repositories.</para>
             </footnote>
-            Note that you also need to apply the Maven plugin in order to publish to a Maven repository. In this instance, Gradle
+            Note that you also need to apply the Maven plugin in order to publish to a Maven repository. when this is in place, Gradle
             will generate and upload a <filename>pom.xml</filename>.
         </para>
         <sample id="publishMavenRepository" dir="userguide/artifacts/maven" title="Publishing to a Maven repository">
diff --git a/subprojects/docs/src/docs/userguide/artifactMngmt.xml b/subprojects/docs/src/docs/userguide/artifactMngmt.xml
index 226c93a..b244179 100644
--- a/subprojects/docs/src/docs/userguide/artifactMngmt.xml
+++ b/subprojects/docs/src/docs/userguide/artifactMngmt.xml
@@ -42,7 +42,7 @@
         the Java plugin.</para></footnote>
         Execution of these tasks will build or upload the artifacts belonging to the respective configuration.
         </para>
-        <para>Table <xref linkend="tab:configurations"/> shows the configurations added by the Java plugin. Two of the
+        <para><xref linkend="tab:configurations"/> shows the configurations added by the Java plugin. Two of the
         configurations are relevant for the usage with artifacts. The <code>archives</code> configuration is the standard
         configuration to assign your artifacts to. The Java plugin automatically assigns the default jar to this
         configuration. We will talk more about the <code>runtime</code> configuration in <xref linkend="project_libraries"/>.
@@ -82,10 +82,10 @@
 
     <section>
         <title>Publishing artifacts</title>
-        <para>We have said that there is a specific upload task for each configuration. But before you can do an upload,
+        <para>We have said that there is a specific upload task for each configuration. Before you can do an upload,
             you have to configure the upload task and define where to publish the artifacts to. The repositories you have defined (as described
-            in <xref linkend="sec:repositories"/>) are not automatically used for uploading. In fact, some of those repositories allow only for artifact downloading.
-            Here is an example how you can configure the upload task of a configuration:
+            in <xref linkend="sec:repositories"/>) are not automatically used for uploading. In fact, some of those repositories only allow downloading artifacts, not uploading.
+            Here is an example of how you can configure the upload task of a configuration:
         </para>
         <sample id="uploading" dir="userguide/artifacts/uploading" title="Configuration of the upload task">
             <sourcefile file="build.gradle" snippet="uploading"/>
@@ -107,10 +107,10 @@
             and what are the dependencies of these artifacts. The Java plugin adds a <code>runtime</code> configuration for
             this purpose, with the implicit assumption that the <code>runtime</code> dependencies are the dependencies of
             the artifact you want to publish. Of course this is fully customizable. You can add your own custom configuration or let the
-            existing configurations extend from other configurations. You might have different group of artifacts which have
+            existing configurations extend from other configurations. You might have a different group of artifacts which have
             a different set of dependencies. This mechanism is very powerful and flexible.
             </para>
-        <para>If someone wants to use your project as a library, she simply needs to declare on which configuration of
+        <para>If someone wants to use your project as a library, she simply needs to declare which configuration of
             the dependency to depend on.
             A Gradle dependency offers the <code>configuration</code> property to declare this. If this
             is not specified, the <code>default</code> configuration is used (see <xref linkend="sec:dependency_configurations"/>).
diff --git a/subprojects/docs/src/docs/userguide/buildAnnouncementsPlugin.xml b/subprojects/docs/src/docs/userguide/buildAnnouncementsPlugin.xml
index 6e46ac3..e70591c 100644
--- a/subprojects/docs/src/docs/userguide/buildAnnouncementsPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/buildAnnouncementsPlugin.xml
@@ -8,7 +8,7 @@
     <para>The build announcements plugin uses the <link linkend="announce_plugin">announce</link> plugin to send local announcements on important events in the build.</para>
     <section>
         <title>Usage</title>
-        <para>To use the build announcements plugin, include in your build script:</para>
+        <para>To use the build announcements plugin, include the following in your build script:</para>
         <sample id="useBuildAnnouncementsPlugin" dir="announce" title="Using the build announcements plugin">
             <sourcefile file="build.gradle" snippet="use-build-announcements-plugin"/>
         </sample>
diff --git a/subprojects/docs/src/docs/userguide/buildDashboardPlugin.xml b/subprojects/docs/src/docs/userguide/buildDashboardPlugin.xml
index ddf8f17..c4bce8c 100644
--- a/subprojects/docs/src/docs/userguide/buildDashboardPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/buildDashboardPlugin.xml
@@ -41,8 +41,8 @@
         </para>
         <para>
             The <literal>buildDashboard</literal> task does not depend on any other tasks. It will only aggregate the reporting tasks that are independently
-            being executed as part of the build run. To generate the build dashboard, simply include the <literal>buildDashboard</literal> task in the list of tasks to execute.
-            For example, <userinput>gradle buildDashboard build</userinput> will generate a dashboard for all of the reporting tasks that are dependents of the <literal>build</literal> task.
+            being executed as part of the build run. To generate the build dashboard, simply include this task in the list of tasks to execute.
+            For example, “<userinput>gradle buildDashboard build</userinput>” will generate a dashboard for all of the reporting tasks that are dependents of the <literal>build</literal> task.
         </para>
     </section>
 
@@ -85,4 +85,4 @@
         <para>You can influence the location of build dashboard plugin generation via <apilink class="org.gradle.api.reporting.ReportingExtension"/>.</para>
     </section>
 
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/buildEnvironment.xml b/subprojects/docs/src/docs/userguide/buildEnvironment.xml
index 8171748..5837c2c 100644
--- a/subprojects/docs/src/docs/userguide/buildEnvironment.xml
+++ b/subprojects/docs/src/docs/userguide/buildEnvironment.xml
@@ -21,22 +21,22 @@
         <para>Gradle provides several options that make it easy to configure the Java process that will be used to execute your build.
             While it's possible to configure these in your local environment via GRADLE_OPTS or JAVA_OPTS,
             certain settings like JVM memory settings, Java home, daemon on/off
-            can be more useful if they can versioned with the project in your VCS so that
-            the entire team can work with consistent environment.
-            Setting up a consistent environment for your build is as simple as placing those settings into a <filename>gradle.properties</filename> file.
+            can be more useful if they can be versioned with the project in your VCS so that
+            the entire team can work with a consistent environment.
+            Setting up a consistent environment for your build is as simple as placing these settings into a <filename>gradle.properties</filename> file.
             The configuration is applied in following order
-            (in case an option is configured in multiple locations the last one wins):
+            (if an option is configured in multiple locations the last one wins):
             <itemizedlist>
-                <listitem>from <filename>gradle.properties</filename> located in project build dir.</listitem>
-                <listitem>from <filename>gradle.properties</filename> located in <literal>gradle user home</literal>.</listitem>
-                <listitem>from system properties, e.g. when <literal>-Dsome.property</literal> is used in the command line.</listitem>
+                <listitem>from <filename>gradle.properties</filename> in project build dir.</listitem>
+                <listitem>from <filename>gradle.properties</filename> in <literal>gradle user home</literal>.</listitem>
+                <listitem>from system properties, e.g. when <literal>-Dsome.property</literal> is set on the command line.</listitem>
             </itemizedlist>
         </para>
         <para>
             The following properties can be used to configure the Gradle build environment:
             <varlistentry>
                 <term><literal>org.gradle.daemon</literal></term>
-                <listitem><para>When set to <literal>true</literal> the Gradle daemon is to run the build.
+                <listitem><para>When set to <literal>true</literal> the Gradle daemon is used to run the build.
                     For local developer builds this is our favorite property. The developer environment is optimized for speed and feedback
                     so we nearly always run Gradle jobs with the daemon.
                     We don't run CI builds with the daemon (i.e. a long running process)
@@ -46,10 +46,10 @@
             </varlistentry>
             <varlistentry>
                 <term><literal>org.gradle.java.home</literal></term>
-                <listitem><para>Specifies the java home for the Gradle build process.
-                    The value can be set to either <literal>jdk</literal> or <literal>jre</literal> location,
-                    however, depending on what does your build do, <literal>jdk</literal> is safer.
-                    Reasonable default is used if the setting is unspecified.</para>
+                <listitem><para>Specifies the Java home for the Gradle build process.
+                    The value can be set to either a <literal>jdk</literal> or <literal>jre</literal> location,
+                    however, depending on what your build does, <literal>jdk</literal> is safer.
+                    A reasonable default is used if the setting is unspecified.</para>
                 </listitem>
             </varlistentry>
             <varlistentry>
@@ -71,26 +71,97 @@
                 <listitem><para>When configured, Gradle will run in incubating parallel mode.</para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term><literal>org.gradle.workers.max</literal></term>
+                <listitem><para>When configured, Gradle will use a maximum of the given number of workers.  See <literal>--max-workers</literal> for details.</para>
+                </listitem>
+            </varlistentry>
         </para>
         <section>
-            <title>Forked java processes</title>
-            <para>Many settings (like the java version and maximum heap size) can only be specified when launching a new JVM for the build process. This means that Gradle
+            <title>Forked Java processes</title>
+            <para>Many settings (like the Java version and maximum heap size) can only be specified when launching a new JVM for the build process. This means that Gradle
                 must launch a separate JVM process to execute the build after parsing the various <filename>gradle.properties</filename> files.
                 When running with the daemon, a JVM with the correct parameters is started once and reused for each daemon build execution.
                 When Gradle is executed without the daemon, then a new JVM must be launched for every build execution,
                 unless the JVM launched by the Gradle start script happens to have the same parameters.
             </para>
             <para>
-            This launching of an extra JVM on every build execution is quite expensive, which is why we highly recommend that you use the Gradle Daemon if you are
-            specifying <literal>org.gradle.java.home</literal> or <literal>org.gradle.jvmargs</literal>. See <xref linkend="gradle_daemon"/> for more details.</para>
+            This launching of an extra JVM on every build execution is quite expensive, which is why if you are
+            setting either <literal>org.gradle.java.home</literal> or <literal>org.gradle.jvmargs</literal>
+            we highly recommend that you use the Gradle Daemon. See <xref linkend="gradle_daemon"/> for more details.</para>
         </section>
     </section>
-
+    <section id='sec:gradle_properties_and_system_properties'>
+        <title>Gradle properties and system properties</title>
+        <para>Gradle offers a variety of ways to add properties to your build. With the <option>-D</option> command line
+            option you can pass a system property to the JVM which runs Gradle. The <option>-D</option> option of the
+            <command>gradle</command> command has the same effect as the <option>-D</option> option of the
+            <command>java</command> command.
+        </para>
+        <para>You can also add properties to your project objects using properties files. You can place a
+            <filename>gradle.properties</filename> file in the Gradle user home directory (defined by the
+            “<literal>GRADLE_USER_HOME</literal>” environment variable, which if not set defaults to
+            <filename><replaceable>USER_HOME</replaceable>/.gradle</filename>) or in your project directory. For
+            multi-project builds you can place <filename>gradle.properties</filename> files in any subproject directory.
+            The properties set in a <filename>gradle.properties</filename> file can be accessed via the project object. The
+            properties file in the user's home directory has precedence over property files in the project directories.
+        </para>
+        <para>You can also add properties directly to your project object via the <option>-P</option> command line option.
+        </para>
+        <para>Gradle can also set project properties when it sees specially-named system properties or
+            environment variables. This feature is very useful when you don't have admin rights to a continuous integration
+            server and you need to set property values that should not be easily visible, typically for security reasons.
+            In that situation, you can't use the <option>-P</option> option, and you can't change the system-level
+            configuration files.  The correct strategy is to change the configuration of your continuous integration
+            build job, adding an environment variable setting that matches an expected pattern.  This won't be visible
+            to normal users on the system.
+            <footnote>
+                <para>
+                    <emphasis>Jenkins</emphasis>, <emphasis>Teamcity</emphasis>, or <emphasis>Bamboo</emphasis> are some CI servers which offer this functionality.
+                </para>
+            </footnote>
+        </para>
+        <para>
+            If the environment variable name looks like
+            <literal>ORG_GRADLE_PROJECT_<replaceable>prop</replaceable>=somevalue</literal>,
+            then Gradle will set a <literal>prop</literal> property on your project object, with the value
+            of <literal>somevalue</literal>. Gradle also supports this for
+            system properties, but with a different naming pattern, which looks like
+            <literal>org.gradle.project.<replaceable>prop</replaceable></literal>.
+        </para>
+        <para>You can also set system properties in the <filename>gradle.properties</filename> file. If a property
+            name in such a file has the prefix “<literal>systemProp.</literal>”, like “<literal>systemProp.propName</literal>”,
+            then the property and its value will be set as a system property, without the prefix. In a multi project
+            build, “<literal>systemProp.</literal>” properties set in any project except the root will be ignored.
+            That is, only the root project's <filename>gradle.properties</filename> file will be checked for
+            properties that begin with the “<literal>systemProp.</literal>” prefix.
+        </para>
+        <sample id="properties" dir="userguide/tutorial/properties" title="Setting properties with a gradle.properties file">
+            <sourcefile file="gradle.properties"/>
+            <sourcefile file="build.gradle"/>
+            <output args="-q -PcommandLineProjectProp=commandLineProjectPropValue -Dorg.gradle.project.systemProjectProp=systemPropertyValue printProps"/>
+        </sample>
+        <section id='sub:checking_for_project_properties'>
+            <title>Checking for project properties</title>
+            <para>You can access a project property in your build script simply by using its name as you would use a
+                variable. If this property does not exist, an exception will be thrown and the build will fail. If your
+                build script relies on optional properties the user might set, perhaps in a <literal>gradle.properties</literal> file,
+                you need to check for existence before you access them. You can do this by using the method
+                <literal>hasProperty('propertyName')</literal>
+                which returns
+                <literal>true</literal>
+                or <literal>false</literal>.
+            </para>
+        </section>
+    </section>
+    
     <section id='sec:accessing_the_web_via_a_proxy'>
         <title>Accessing the web via a proxy</title>
-        <para>Configuring an HTTP proxy (for example for downloading dependencies) is done via standard JVM system properties. These properties can be set directly in the build script; for example <literal>
-                System.setProperty('http.proxyHost', 'www.somehost.org')</literal> for the proxy host. Alternatively, the properties can be specified in a gradle.properties file,
-            either in the build's root directory or in the Gradle home directory.
+        <para>Configuring an HTTP proxy (for downloading dependencies, for example) is done via standard JVM system
+        properties. These properties can be set directly in the build script; for example, setting the proxy host
+        would be done with <literal>System.setProperty('http.proxyHost', 'www.somehost.org')</literal>.
+        Alternatively, the properties can be specified in a gradle.properties file, either in the build's root
+        directory or in the Gradle home directory.
         </para>
         <example>
             <title>Configuring an HTTP proxy</title>
@@ -116,13 +187,13 @@ systemProp.https.nonProxyHosts=*.nonproxyrepos.com|localhost
 ]]></programlisting>
         </example>
         <para>We could not find a good overview for all possible proxy settings. One place to look are the constants
-            in a file from the Ant project. Here a
+            in a file from the Ant project. Here's a
             <ulink url='http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/util/ProxySetup.java?view=markup&pathrev=556977'>
                 link
             </ulink> to the Subversion view. The other is a
             <ulink url='http://download.oracle.com/javase/6/docs/technotes/guides/net/properties.html'>
                 Networking Properties page
-            </ulink> from the JDK docs. If anyone knows a better overview, please let us know via the mailing list.
+            </ulink> from the JDK docs. If anyone knows of a better overview, please let us know via the mailing list.
         </para>
         <section id='sub:ntlm_authentication'>
             <title>NTLM Authentication</title>
diff --git a/subprojects/docs/src/docs/userguide/buildInitPlugin.xml b/subprojects/docs/src/docs/userguide/buildInitPlugin.xml
index 41a24a2..27a6add 100644
--- a/subprojects/docs/src/docs/userguide/buildInitPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/buildInitPlugin.xml
@@ -93,28 +93,28 @@
             If a
             <literal>--type</literal>
             parameter is not supplied, Gradle will attempt to infer the type from the environment. For example, it will
-            infer a type value of "<literal>pom</literal>" if it finds a
+            infer a type value of “<literal>pom</literal>” if it finds a
             <literal>pom.xml</literal>
             to convert to a Gradle build.
         </para>
         <para>
-            If the type could not be inferred, the type "<literal>basic</literal>" will be used.
+            If the type could not be inferred, the type “<literal>basic</literal>” will be used.
         </para>
         <para>All build setup types include the setup of the Gradle Wrapper.</para>
     </section>
     <section>
         <title>Build init types</title>
         <note>
-            As this plugin is currently <link linkend="feature_lifecycle">incubating</link>, only 3 build init types are currently supported.
+            As this plugin is currently <link linkend="feature_lifecycle">incubating</link>, only a few build init types are currently supported.
             More types will be added in future Gradle releases.
         </note>
         <section>
-            <title>"<literal>pom</literal>" (Maven conversion)
+            <title>“<literal>pom</literal>” (Maven conversion)
             </title>
             <para>
-                The "<literal>pom</literal>" type can be used to convert an Apache Maven build to a Gradle build.
+                The “<literal>pom</literal>” type can be used to convert an Apache Maven build to a Gradle build.
                 This works by converting the POM to one or more Gradle files.
-                It is only able to be used if there is a valid "<literal>pom.xml</literal>"
+                It is only able to be used if there is a valid “<literal>pom.xml</literal>”
                 file in the directory that the
                 <literal>init</literal>
                 task is invoked in. This type will be automatically inferred if such a file exists.
@@ -136,27 +136,25 @@
                 <listitem>Supports packaging war projects as jars if needed</listitem>
                 <listitem>Generates dependencies (both external and inter-module)</listitem>
                 <listitem>Generates download repositories (inc. local Maven repository)</listitem>
-                <listitem>Adjusts java compiler settings</listitem>
+                <listitem>Adjusts Java compiler settings</listitem>
                 <listitem>Supports packaging of sources and tests</listitem>
                 <listitem>Supports TestNG runner</listitem>
                 <listitem>Generates global exclusions from Maven enforcer plugin settings</listitem>
             </itemizedlist>
         </section>
         <section>
-            <title>"<literal>java-library</literal>"
+            <title>“<literal>java-library</literal>”
             </title>
             <para>
-                The "<literal>java-library</literal>" build init type is not inferable. It must be explicitly specified.
+                The “<literal>java-library</literal>” build init type is not inferable. It must be explicitly specified.
             </para>
             <para>
                 It has the following features:
             </para>
             <itemizedlist>
-                <listitem>Uses the "<literal>java</literal>" plugin
+                <listitem>Uses the “<literal>java</literal>” plugin
                 </listitem>
-                <listitem>Uses the "
-                    <literal>mavenCentral()</literal>
-                    dependency repository
+                <listitem>Uses the “<literal>jcenter</literal>” dependency repository
                 </listitem>
                 <listitem>Uses <ulink url="http://junit.org">JUnit</ulink>
                  for testing</listitem>
@@ -165,53 +163,49 @@
             </itemizedlist>
         </section>
         <section>
-            <title>"<literal>scala-library</literal>"
+            <title>“<literal>scala-library</literal>”
             </title>
             <para>
-                The "<literal>scala-library</literal>" build init type is not inferable. It must be explicitly specified.
+                The “<literal>scala-library</literal>” build init type is not inferable. It must be explicitly specified.
             </para>
             <para>
                 It has the following features:
             </para>
             <itemizedlist>
-                <listitem>Uses the "<literal>scala</literal>" plugin
+                <listitem>Uses the “<literal>scala</literal>” plugin
                 </listitem>
-                <listitem>Uses the "
-                    <literal>mavenCentral()</literal>
-                    dependency repository
+                <listitem>Uses the “<literal>jcenter</literal>” dependency repository
                 </listitem>
                 <listitem>Uses Scala 2.10</listitem>
-                <listitem>Uses <ulink url="http://www.scalatest.org">ScalaTest</ulink> is used for testing</listitem>
+                <listitem>Uses <ulink url="http://www.scalatest.org">ScalaTest</ulink> for testing</listitem>
                 <listitem>Has directories in the conventional locations for source code</listitem>
-                <listitem>Contains a sample scala class and an according ScalaTest test suite, if there are no existing source or test files</listitem>
+                <listitem>Contains a sample scala class and an associated ScalaTest test suite, if there are no existing source or test files</listitem>
             </itemizedlist>
         </section>
         <section>
-            <title>"<literal>groovy-library</literal>"
+            <title>“<literal>groovy-library</literal>”
             </title>
             <para>
-                The "<literal>groovy-library</literal>" build init type is not inferable. It must be explicitly specified.
+                The “<literal>groovy-library</literal>” build init type is not inferable. It must be explicitly specified.
             </para>
             <para>
                 It has the following features:
             </para>
             <itemizedlist>
-                <listitem>Uses the "<literal>groovy</literal>" plugin
+                <listitem>Uses the “<literal>groovy</literal>” plugin
                 </listitem>
-                <listitem>Uses the "
-                    <literal>mavenCentral()</literal>
-                    dependency repository
+                <listitem>Uses the “<literal>jcenter</literal>” dependency repository
                 </listitem>
                 <listitem>Uses Groovy 2.x </listitem>
                 <listitem>Uses <ulink url="http://code.google.com/p/spock/">Spock testing framework</ulink> for testing</listitem>
                 <listitem>Has directories in the conventional locations for source code</listitem>
-                <listitem>Contains a sample groovy class and an according Spock specification, if there are no existing source or test files</listitem>
+                <listitem>Contains a sample Groovy class and an associated Spock specification, if there are no existing source or test files</listitem>
             </itemizedlist>
         </section>
         <section>
-            <title>"basic"</title>
+            <title>“basic”</title>
             <para>
-                The "<literal>basic</literal>" build init type is useful for creating a fresh new Gradle project.
+                The “<literal>basic</literal>” build init type is useful for creating a fresh new Gradle project.
                 It creates a sample
                 <literal>build.gradle</literal>
                 file, with comments and links to help get started.
diff --git a/subprojects/docs/src/docs/userguide/buildLifecycle.xml b/subprojects/docs/src/docs/userguide/buildLifecycle.xml
index 5aa0300..aa2761c 100644
--- a/subprojects/docs/src/docs/userguide/buildLifecycle.xml
+++ b/subprojects/docs/src/docs/userguide/buildLifecycle.xml
@@ -15,9 +15,9 @@
   -->
 <chapter id='build_lifecycle'>
     <title>The Build Lifecycle</title>
-    <para>We said earlier, that the core of Gradle is a language for dependency based programming. In Gradle terms this
+    <para>We said earlier that the core of Gradle is a language for dependency based programming. In Gradle terms this
         means that you can define tasks and dependencies between tasks. Gradle guarantees that these tasks are executed
-        in the order of their dependencies, and that each task is executed only once. Those tasks form a
+        in the order of their dependencies, and that each task is executed only once. These tasks form a
         <ulink url='http://en.wikipedia.org/wiki/Directed_acyclic_graph'>Directed Acyclic Graph</ulink>. There are
         build tools that build up such a dependency graph as they execute their tasks. Gradle builds the complete
         dependency graph <emphasis>before</emphasis> any task is executed. This lies at the heart of Gradle and makes
@@ -45,7 +45,7 @@
                 <listitem>
                     <para>During this phase the project objects are configured.
                         The build scripts of <emphasis>all</emphasis> projects which are part of the build are
-                        executed. Gradle 1.4 introduces an <link linkend="feature_lifecycle">incubating</link> opt-in feature called <firstterm>configuration on demand</firstterm>.
+                        executed. Gradle 1.4 introduced an <link linkend="feature_lifecycle">incubating</link> opt-in feature called <firstterm>configuration on demand</firstterm>.
                         In this mode, Gradle configures only relevant projects (see <xref linkend="sec:configuration_on_demand"/>).
                     </para>
                 </listitem>
@@ -68,23 +68,23 @@
             via a naming convention. The default name for this file is <filename>settings.gradle</filename>. Later in
             this chapter we explain how Gradle looks for a settings file.
         </para>
-        <para>The settings file gets executed during the initialization phase. A multiproject build must have a
+        <para>The settings file is executed during the initialization phase. A multiproject build must have a
             <filename>settings.gradle</filename>
-            file in the root project of the multiproject hierarchy. It is required because in the
-            settings file it is defined, which projects are taking part in the multi-project build (see
+            file in the root project of the multiproject hierarchy. It is required because the
+            settings file defines which projects are taking part in the multi-project build (see
             <xref linkend='multi_project_builds'/>). For a single-project build, a settings file is optional.
-            You might need it for example, to add libraries to your build script classpath (see <xref
+            Besides defining the included projects, you might need it to add libraries to your build script classpath (see <xref
                     linkend='organizing_build_logic'/>). Let's first do some introspection with a single project
             build:
         </para>
         <sample id="buildlifecycle" dir="userguide/buildlifecycle/basic" title="Single project build">
             <sourcefile file="settings.gradle"/>
             <sourcefile file="build.gradle"/>
-            <output args="test"/>
+            <output args="test testBoth"/>
         </sample>
         <para>For a build script, the property access and method calls are delegated to a project object. Similarly
-            property access and method calls within the settings file is delegated to a settings object. Have a look at
-            <apilink class='org.gradle.api.initialization.Settings'/>.
+            property access and method calls within the settings file is delegated to a settings object. Look at
+            the <apilink class='org.gradle.api.initialization.Settings'/> class in the API documentation for more information.
         </para>
     </section>
     <section id='sec:multi_project_builds'>
@@ -98,7 +98,7 @@
             <title>Project locations</title>
             <para>Multi-project builds are always represented by a tree with a single root. Each element in the tree
                 represents a project. A project has a path which denotes the position
-                of the project in the multi-project build tree. In majority of cases the project path is consistent with
+                of the project in the multi-project build tree. In most cases the project path is consistent with
                 the physical location of the project in the file system. However, this behavior is configurable.
                 The project tree is created in the <filename>settings.gradle</filename> file.
                 By default it is assumed that the location of the settings
@@ -129,8 +129,8 @@
                 <sample id="standardLayouts" dir="userguide/multiproject/standardLayouts" title="Flat layout">
                     <sourcefile file="settings.gradle" snippet="flat-layout"/>
                 </sample>
-                <para>The <literal>includeFlat</literal> method takes directory names as an argument. Those directories
-                    need to exist as siblings of the root project directory. The location of those directories
+                <para>The <literal>includeFlat</literal> method takes directory names as an argument. These directories
+                    need to exist as siblings of the root project directory. The location of these directories
                     are considered as child projects of the root project in the multi-project tree.
                 </para>
             </section>
@@ -148,34 +148,33 @@
             <sample id="customLayout" dir="userguide/multiproject/customLayout" title="Modification of elements of the project tree">
                 <sourcefile file="settings.gradle" snippet="change-project"/>
             </sample>
-            <para>Have a look at <apilink class="org.gradle.api.initialization.ProjectDescriptor"/> for more details.</para>
+            <para>Look at the <apilink class="org.gradle.api.initialization.ProjectDescriptor"/> class in the API documentation for more information.</para>
         </section>
     </section>
     <section id='sec:initialization'>
         <title>Initialization</title>
         <para>How does Gradle know whether to do a single or multiproject build? If you trigger a multiproject build
-            from the directory where the settings file is, things are easy. But Gradle also allows you to execute the
+            from a directory with a settings file, things are easy. But Gradle also allows you to execute the
             build from within any subproject taking part in the build.
             <footnote>
                 <para>Gradle supports partial multiproject builds (see <xref linkend='multi_project_builds'/>).
                 </para>
             </footnote>
-            If you execute Gradle from within a project that has no <filename>settings.gradle</filename> file,
-            Gradle does the following:
+            If you execute Gradle from within a project with no <filename>settings.gradle</filename> file,
+            Gradle looks for a <filename>settings.gradle</filename> file in the following way:
         </para>
         <itemizedlist>
             <listitem>
-                <para>It searches for a <filename>settings.gradle</filename> in a directory called <filename>master</filename>
-                    which has the same nesting level as the current dir.
+                <para>It looks in a directory called <filename>master</filename> which has the same nesting level
+                as the current dir.
                 </para>
             </listitem>
             <listitem>
-                <para>If no <filename>settings.gradle</filename> is found, it searches the parent directories for the existence of a
-                    <filename>settings.gradle</filename> file.
+                <para>If not found yet, it searches parent directories.
                 </para>
             </listitem>
             <listitem>
-                <para>If no <filename>settings.gradle</filename> file is found, the build is executed as a single project build.
+                <para>If not found yet, the build is executed as a single project build.
                 </para>
             </listitem>
             <listitem>
@@ -185,12 +184,12 @@
                 </para>
             </listitem>
         </itemizedlist>
-        <para>What is the purpose of this behavior? Somehow Gradle has to find out, whether the project you are into, is
+        <para>What is the purpose of this behavior? Gradle needs to determine whether the project you are in is
             a subproject of a multiproject build or not. Of course, if it is a subproject, only the subproject and its
-            dependent projects are build. But Gradle needs to create the build configuration for the whole multiproject
-            build (see <xref linkend='multi_project_builds'/>). Via the <option>-u</option>
-            command line option, you can tell Gradle not to look in the parent hierarchy for a <filename>settings.gradle</filename> file. The
-            current project is then always build as a single project build. If the current project contains a
+            dependent projects are built, but Gradle needs to create the build configuration for the whole multiproject
+            build (see <xref linkend='multi_project_builds'/>). You can use the <option>-u</option>
+            command line option to tell Gradle not to look in the parent hierarchy for a <filename>settings.gradle</filename> file. The
+            current project is then always built as a single project build. If the current project contains a
             <filename>settings.gradle</filename> file, the <option>-u</option> option has no meaning. Such a build is always executed as:
         </para>
         <itemizedlist>
@@ -203,16 +202,18 @@
                 </para>
             </listitem>
         </itemizedlist>
-        <para>The auto search for a settings file does only work for multi-project builds with a physical hierarchical
-            or flat layout. For a flat layout you must additionally obey to the naming convention described above.
-            Gradle supports arbitrary physical layouts for a multiproject build. But for such arbitrary layouts you need
-            to execute the build from the directory where the settings file is located. For how to run partial builds
-            from the root see <xref linkend='sec:running_partial_build_from_the_root'/>. In our next release we want to
-            enable partial builds from subprojects by specifying the location of the settings file as a command line
-            parameter. Gradle creates Project objects for every project taking part in the build. For a single
-            project build this is only one project. For a multi-project build these are the projects specified in
-            Settings object (plus the root project). Each project object has by default a name equals to the name of its
-            top level directory. Every project except the root project has a parent project and might have child projects.
+        <para>The automatic search for a <filename>settings.gradle</filename> file only works for multi-project
+        builds with a physical hierarchical or flat layout. For a flat layout you must additionally follow the
+        naming convention described above (“<literal>master</literal>”). Gradle supports arbitrary physical layouts
+        for a multiproject build, but for such arbitrary layouts you need to execute the build from the directory
+        where the settings file is located. For information on how to run partial builds from the root see
+        <xref linkend='sec:running_partial_build_from_the_root'/>.
+        </para>
+        <para>
+        Gradle creates a Project object for every project taking part in the build.
+        For a multi-project build these are the projects specified in the Settings object (plus the root
+        project). Each project object has by default a name equal to the name of its top level directory,
+        and every project except the root project has a parent project. Any project may have child projects.
         </para>
     </section>
     <section id='sec:configuration_and_execution_of_a_single_project_build'>
@@ -228,7 +229,7 @@
         <title>Responding to the lifecycle in the build script</title>
 
         <para>Your build script can receive notifications as the build progresses through its lifecycle. These
-            notifications generally take 2 forms: You can either implement a particular listener interface, or you can
+            notifications generally take two forms: You can either implement a particular listener interface, or you can
             provide a closure to execute when the notification is fired. The examples below use closures. For details on
             how to use the listener interfaces, refer to the API documentation.
         </para>
@@ -239,8 +240,8 @@
                 to do things like performing additional configuration once all the definitions in a build script have
                 been applied, or for some custom logging or profiling.
             </para>
-            <para>Below is an example which adds a <literal>test</literal> task to each project with the
-                <literal>hasTests</literal> property set to true.
+            <para>Below is an example which adds a <literal>test</literal> task to each project which has a
+                <literal>hasTests</literal> property value of true.
             </para>
             <sample id="projectEvaluateEvents" dir="userguide/buildlifecycle/projectEvaluateEvents" title="Adding of test task to each project which has certain property set">
                 <sourcefile file="build.gradle" snippet="after-evaluate"/>
diff --git a/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml b/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
index 530b0b2..a77018f 100644
--- a/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/buildScriptsTutorial.xml
@@ -27,7 +27,7 @@
         a more concrete definition for what a project is.
     </para>
     <para>Each project is made up of one or more <firstterm>tasks</firstterm>. A task represents some atomic piece
-        of work which a build performs. This might be compiling some classes, creating a JAR, generating javadoc,
+        of work which a build performs. This might be compiling some classes, creating a JAR, generating Javadoc,
         or publishing some archives to a repository.</para>
     <para>For now, we will look at defining some simple tasks in a build with one project. Later chapters will look at
         working with multiple projects and more about working with projects and tasks.</para>
@@ -42,17 +42,17 @@
         </para>
         <para>To try this out, create the following build script named <filename>build.gradle</filename>.
         </para>
-        <sample id="hello" dir="userguide/tutorial/hello" title="The first build script">
+        <sample id="hello" dir="userguide/tutorial/hello" title="Your first build script">
             <sourcefile file="build.gradle"/>
         </sample>
-        <para>In a command-line shell, enter into the containing directory and execute the build script by running
+        <para>In a command-line shell, move to the containing directory and execute the build script with
             <userinput>gradle -q hello</userinput>:
         </para>
         <tip>
             <title>What does <option>-q</option> do?</title>
             <para>Most of the examples in this user guide are run with the <option>-q</option> command-line option.
                 This suppresses Gradle's log messages, so that only the output of the tasks is shown. This keeps the example
-                output in this user guide a little clearer. You don't need to use this option if you don't want.
+                output in this user guide a little clearer. You don't need to use this option if you don't want to.
                 See <xref linkend="logging"/> for more details about the command-line options which affect Gradle's output.
             </para>
         </tip>
@@ -64,13 +64,13 @@
             <literal>hello</literal> task, which in turn executes the action you've provided. The action is simply a
             closure containing some Groovy code to execute.
         </para>
-        <para>If you think this looks similar to Ant's targets, well, you are right. Gradle tasks are the equivalent to
-            Ant targets. But as you will see, they are much more powerful. We have used a different terminology than Ant
+        <para>If you think this looks similar to Ant's targets, you would be right. Gradle tasks are the equivalent to
+            Ant targets, but as you will see, they are much more powerful. We have used a different terminology than Ant
             as we think the word <emphasis>task</emphasis> is more expressive than the word <emphasis>target</emphasis>.
             Unfortunately this introduces a terminology clash with Ant, as Ant calls its commands, such as
             <literal>javac</literal> or <literal>copy</literal>, tasks. So when we talk about tasks,
             we <emphasis>always</emphasis> mean Gradle tasks, which are the equivalent to Ant's targets. If we talk
-            about Ant tasks (Ant commands), we explicitly say <emphasis>ant task</emphasis>.
+            about Ant tasks (Ant commands), we explicitly say <emphasis>Ant task</emphasis>.
         </para>
     </section>
     <section>
@@ -85,7 +85,7 @@
     </section>
     <section>
         <title>Build scripts are code</title>
-        <para>Gradle's build scripts expose to you the full power of Groovy. As an appetizer, have a look at this:
+        <para>Gradle's build scripts give you the full power of Groovy. As an appetizer, have a look at this:
         </para>
         <sample id="upper" dir="userguide/tutorial/upper" title="Using Groovy in Gradle's tasks">
             <sourcefile file="build.gradle"/>
@@ -99,9 +99,9 @@
     </section>
     <section id='sec:task_dependencies'>
         <title>Task dependencies</title>
-        <para>As you probably have guessed, you can declare dependencies between your tasks.
+        <para>As you probably have guessed, you can declare tasks that depend on other tasks.
         </para>
-        <sample id="intro" dir="userguide/tutorial/intro" title="Declaration of dependencies between tasks">
+        <sample id="intro" dir="userguide/tutorial/intro" title="Declaration of task that depends on other task">
             <sourcefile file="build.gradle"/>
             <output args="-q intro"/>
         </sample>
@@ -129,8 +129,8 @@
     </section>
     <section>
         <title>Manipulating existing tasks</title>
-        <para>Once tasks are created they can be accessed via an <emphasis>API</emphasis>. This is different to Ant. For
-            example you can create additional dependencies.
+        <para>Once tasks are created they can be accessed via an <emphasis>API</emphasis>. For instance, you could use this to
+        dynamically add dependencies to a task, at runtime.  Ant doesn't allow anything like this.
         </para>
         <sample id="dynamicDepends" dir="userguide/tutorial/dynamicDepends" title="Accessing a task via API - adding a dependency">
             <sourcefile file="build.gradle"/>
@@ -156,8 +156,9 @@
             <sourcefile file="build.gradle"/>
             <output args="-q hello"/>
         </sample>
-        <para>This enables very readable code, especially when using the out of the box tasks provided by the plugins
-            (e.g. <literal>compile</literal>).</para>
+        <para>This enables very readable code, especially when using the tasks provided by the plugins,
+              like the <literal>compile</literal> task.
+        </para>
     </section>
     <section id='sec:extra_task_properties'>
         <title>Extra task properties</title>
@@ -176,7 +177,7 @@
         <para>Ant tasks are first-class citizens in Gradle. Gradle provides excellent integration for Ant tasks by simply
             relying on Groovy. Groovy is shipped with the fantastic <literal>AntBuilder</literal>. Using Ant tasks
             from Gradle is as convenient and more powerful than using Ant tasks from a <filename>build.xml</filename>
-            file. From the example below, you can learn how to execute ant tasks and how to access ant properties:
+            file. From the example below, you can learn how to execute Ant tasks and how to access Ant properties:
         </para>
         <sample id="antLoadfile" dir="userguide/tutorial/antLoadfile" title="Using AntBuilder to execute ant.loadfile target">
             <sourcefile file="build.gradle"/>
diff --git a/subprojects/docs/src/docs/userguide/checkstylePlugin.xml b/subprojects/docs/src/docs/userguide/checkstylePlugin.xml
index 7097f21..467e4ef 100644
--- a/subprojects/docs/src/docs/userguide/checkstylePlugin.xml
+++ b/subprojects/docs/src/docs/userguide/checkstylePlugin.xml
@@ -6,7 +6,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Checkstyle plugin, include in your build script:</para>
+        <para>To use the Checkstyle plugin, include the following in your build script:</para>
         <sample id="useCheckstylePlugin" dir="codeQuality" title="Using the Checkstyle plugin">
             <sourcefile file="build.gradle" snippet="use-checkstyle-plugin"/>
         </sample>
@@ -107,7 +107,7 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.quality.CheckstyleExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.quality.CheckstyleExtension"/> class in the API documentation.</para>
     </section>
 
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/codeNarcPlugin.xml b/subprojects/docs/src/docs/userguide/codeNarcPlugin.xml
index 224d0db..8898db0 100644
--- a/subprojects/docs/src/docs/userguide/codeNarcPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/codeNarcPlugin.xml
@@ -6,7 +6,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the CodeNarc plugin, include in your build script:</para>
+        <para>To use the CodeNarc plugin, include the following in your build script:</para>
         <sample id="useCodeNarcPlugin" dir="codeQuality" title="Using the CodeNarc plugin">
             <sourcefile file="build.gradle" snippet="use-codenarc-plugin"/>
         </sample>
@@ -108,7 +108,7 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.quality.CodeNarcExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.quality.CodeNarcExtension"/> class in the API documentation.</para>
     </section>
 
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/commandLine.xml b/subprojects/docs/src/docs/userguide/commandLine.xml
index 014e60d..85737f3 100644
--- a/subprojects/docs/src/docs/userguide/commandLine.xml
+++ b/subprojects/docs/src/docs/userguide/commandLine.xml
@@ -54,6 +54,21 @@
         </varlistentry>
         <varlistentry>
             <term>
+                <option>--console</option>
+            </term>
+            <listitem>
+                <para>Specifies which type of console output to generate.</para>
+                <para>Set to <literal>plain</literal> to generate plain text only. This option disables all color and other rich output in the console output.</para>
+                <para>Set to <literal>auto</literal> (the default) to enable color and other rich output in the console output when
+                    the build process is attached to a console, or to generate plain text only when not attached to a console.</para>
+                <para>Set to <literal>rich</literal> to enable color and other rich output in the console output,
+                    regardless of whether the build process is not attached to a console. When not attached to a console, the build output will use ANSI control
+                    characters to generate the rich output.
+                </para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term>
                 <option>--continue</option>
             </term>
             <listitem>
@@ -132,14 +147,6 @@
         </varlistentry>
         <varlistentry>
             <term>
-                <option>--no-color</option>
-            </term>
-            <listitem>
-                <para>Do not use color in the console output.</para>
-            </listitem>
-        </varlistentry>
-        <varlistentry>
-            <term>
                 <option>--offline</option>
             </term>
             <listitem>
@@ -177,12 +184,24 @@
         </varlistentry>
         <varlistentry>
             <term>
-                <option>--parallel-threads (incubating)</option>
+                <option>--parallel-threads (deprecated, incubating)</option>
             </term>
             <listitem>
                 <para>
                     Build projects in parallel, using the specified number of executor threads. For example<literal>--parallel-threads=3</literal>.
                     This option should only be used with decoupled projects (see <xref linkend="sec:decoupled_projects"/>).
+                    This option has been replaced by <literal>--max-workers</literal>.
+                </para>
+            </listitem>
+        </varlistentry>
+        <varlistentry>
+            <term>
+                <option>--max-workers (incubating)</option>
+            </term>
+            <listitem>
+                <para>
+                    Sets the maximum number of workers that Gradle may use. For example<literal>--max-workers=3</literal>.  The default is the number of processors.
+                    This option replaces <literal>--parallel-threads</literal> when used in conjuction with <literal>--parallel</literal>.
                 </para>
             </listitem>
         </varlistentry>
@@ -221,9 +240,7 @@
                 <option>--recompile-scripts</option>
             </term>
             <listitem>
-                <para>Specifies that cached build scripts are skipped and forced to be recompiled.
-                    See <xref linkend="sec:caching"/>.
-                </para>
+                <para>Forces scripts to be recompiled, bypassing caching.</para>
             </listitem>
         </varlistentry>
         <varlistentry>
@@ -293,45 +310,20 @@
 
     <section>
         <title>Deprecated command-line options</title>
-        <para>The following options are deprecated and will be removed in a future version of Gradle:</para>
         <variablelist>
             <varlistentry>
-                <term><option>-C</option>, <option>--cache</option>
-                </term>
-                <listitem>
-                    <para>(deprecated) Specifies how compiled build scripts should be cached. Possible values are:
-                        <literal>rebuild</literal>
-                        or <literal>on</literal>. Default value is
-                        <literal>on</literal>. You should use
-                        <option>--recompile-scripts</option>
-                        instead.
-                    </para>
-                </listitem>
-            </varlistentry>
-            <varlistentry>
-                <term><option>--no-opt</option>
-                </term>
-                <listitem>
-                    <para>(deprecated) Specifies to ignore all task optimization. You should use
-                        <option>--rerun-tasks</option>
-                        instead.
-                    </para>
-                </listitem>
-            </varlistentry>
-            <varlistentry>
-                <term><option>--refresh</option>
+                <term>
+                    <option>--no-color</option>
                 </term>
                 <listitem>
-                    <para>(deprecated) Refresh the state of resources of the type(s) specified. Currently only <literal>dependencies</literal> is supported.
-                        You should use <option>--refresh-dependencies</option> instead.
-                    </para>
+                    <para>Do not use color in the console output. This option has been replaced by the <option>--console plain</option> option.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
     </section>
 
     <section id='daemon_cli'>
-        <title>Daemon command-line options:</title>
+        <title>Daemon command-line options</title>
         <para>The
             <xref linkend="gradle_daemon"/>
             contains more information about the daemon.
@@ -441,7 +433,15 @@
                     <literal>GRADLE_USER_HOME</literal>
                 </term>
                 <listitem>
-                    <para>Specifies the Gradle user home directory.</para>
+                    <para>Specifies the Gradle user home directory (which defaults to “<literal>USER_HOME/.gradle</literal>” if not set).</para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>
+                    <literal>JAVA_HOME</literal>
+                </term>
+                <listitem>
+                    <para>Specifies the JDK installation directory to use.</para>
                 </listitem>
             </varlistentry>
         </variablelist>
diff --git a/subprojects/docs/src/docs/userguide/commandLineTutorial.xml b/subprojects/docs/src/docs/userguide/commandLineTutorial.xml
index 733ea45..3078bf3 100644
--- a/subprojects/docs/src/docs/userguide/commandLineTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/commandLineTutorial.xml
@@ -24,7 +24,7 @@
             the command <userinput>gradle compile test</userinput> will execute the <literal>compile</literal> and
             <literal>test</literal> tasks. Gradle will execute the tasks in the order that they are listed on the
             command-line, and will also execute the dependencies for each task. Each task is executed once only,
-            regardless of how it came to be included in the build: whether it was specified on the command-line, or it a
+            regardless of how it came to be included in the build: whether it was specified on the command-line, or as a
             dependency of another task, or both. Let's look at an example.</para>
         <para>
             Below four tasks are defined. Both <literal>dist</literal> and <literal>test</literal> depend on the
@@ -41,8 +41,8 @@
             <output args="dist test"/>
         </sample>
         <para>
-            Because each task is executed once only, executing <userinput>gradle test test</userinput> is exactly the same
-            as executing <userinput>gradle test</userinput>.</para>
+            Each task is executed only once, so <userinput>gradle test test</userinput> is exactly the same
+            as <userinput>gradle test</userinput>.</para>
     </section>
 
     <section id="sec:excluding_tasks_from_the_command_line">
@@ -157,7 +157,7 @@
         <section id="sec:show_task_details">
             <title>Show task usage details</title>
             <para>Running <userinput>gradle help --task someTask</userinput> gives you detailed information about a specific task or multiple
-                tasks matching the given task name in your multiproject build <!--TODO RG ref multiproject-->
+                tasks matching the given task name in your multiproject build.
                 Below is an example of this detailed information:
             </para>
             <sample id="taskHelp" dir="userguide/tutorial/projectReports" title="Obtaining detailed help for tasks">
@@ -196,16 +196,16 @@
             <para>
                 This task is extremely useful for investigating the dependency resolution,
                 finding out where certain dependencies are coming from and why certain versions are selected.
-                For more information please see <apilink class="org.gradle.api.tasks.diagnostics.DependencyInsightReportTask"/>.
+                For more information please see the <apilink class="org.gradle.api.tasks.diagnostics.DependencyInsightReportTask"/> class in the API documentation.
             </para>
             <para>
                 The built-in dependencyInsight task is a part of the 'Help' tasks group.
                 The task needs to configured with the dependency and the configuration.
                 The report looks for the dependencies that match the specified dependency spec in the specified configuration.
-                If java related plugin is applied, the dependencyInsight task is pre-configured with 'compile' configuration because typically it's the compile dependencies we are interested in.
+                If Java related plugin is applied, the dependencyInsight task is pre-configured with 'compile' configuration because typically it's the compile dependencies we are interested in.
                 You should specify the dependency you are interested in via the command line '--dependency' option.
                 If you don't like the defaults you may select the configuration via '--configuration' option.
-                For more information see <apilink class="org.gradle.api.tasks.diagnostics.DependencyInsightReportTask"/>.
+                For more information see the <apilink class="org.gradle.api.tasks.diagnostics.DependencyInsightReportTask"/> class in the API documentation.
             </para>
         </section>
         <section id="sec:listing_properties">
@@ -242,13 +242,12 @@
         <title>Dry Run</title>
         <para>Sometimes you are interested in which tasks are executed in which order for a given set of tasks specified on the
             command line, but you don't want the tasks to be executed. You can use the <option>-m</option> option for this.
-            For example <userinput>gradle -m clean compile</userinput> shows you all tasks to be executed as
-            part of the <literal>clean</literal> and <literal>compile</literal> tasks.
+            For example, if you run “<userinput>gradle -m clean compile</userinput>”, you'll see all the tasks that would be
+            executed as part of the <literal>clean</literal> and <literal>compile</literal> tasks.
             This is complementary to the <option>tasks</option> task, which shows you the tasks which are available for
             execution.
         </para>
     </section>
-
     <section>
         <title>Summary</title>
         <para>In this chapter, you have seen some of the things you can do with Gradle from the command-line. You can
diff --git a/subprojects/docs/src/docs/userguide/comparingBuilds.xml b/subprojects/docs/src/docs/userguide/comparingBuilds.xml
index 06622b5..6f9ff13 100644
--- a/subprojects/docs/src/docs/userguide/comparingBuilds.xml
+++ b/subprojects/docs/src/docs/userguide/comparingBuilds.xml
@@ -132,9 +132,9 @@
         <section>
             <title>Supported builds</title>
             <para>
-                Only support for executing Gradle builds is available at this time.
-                Source and target build must execute with Gradle newer or equal to <literal>1.0</literal>.
-                Host build must be at least <literal>1.2</literal>.
+                Only support for comparing Gradle builds is available at this time.
+                Both the source and target build must execute with Gradle newer or equal to version <literal>1.0</literal>.
+                The host build must be at least version <literal>1.2</literal>.
             </para>
             <para>
                 Future versions will provide support for executing builds from other build systems such as Apache Ant
@@ -167,7 +167,7 @@
             This task can be configured to change what is compared.
         </para>
         <programlisting>
-            compareGradleBuilds {
+compareGradleBuilds {
     sourceBuild {
         projectDir "/projects/project-a"
         gradleVersion "1.1"
@@ -179,7 +179,7 @@
 }
         </programlisting>
         <para>
-            The example above configures a comparison between two different projects using two different Gradle versions.
+            The example above specifies a comparison between two different projects using two different Gradle versions.
         </para>
         <section>
             <title>Trying Gradle upgrades</title>
@@ -205,7 +205,7 @@ compareGradleBuilds {
             <para>
                 If there are any differences between the <firstterm>compared outcomes</firstterm>, the task will fail. The location of the HTML report
                 providing insight into the comparison will be given. If all compared outcomes are found to be identical, and there are no uncompared outcomes,
-                and there are no unknown build outcomes the task will succeed.
+                and there are no unknown build outcomes, the task will succeed.
             </para>
             <para>
                 You can configure the task to not fail on compared outcome differences by setting the <literal>ignoreFailures</literal> property to true.
diff --git a/subprojects/docs/src/docs/userguide/customPlugins.xml b/subprojects/docs/src/docs/userguide/customPlugins.xml
index 3b16711..c15a0e1 100644
--- a/subprojects/docs/src/docs/userguide/customPlugins.xml
+++ b/subprojects/docs/src/docs/userguide/customPlugins.xml
@@ -73,7 +73,11 @@
         </sample>
 
         <para>
-            One thing to note is that a new instance of a given plugin is created for each project it is applied to.
+            One thing to note is that a new instance of a given plugin is created for each project it is applied to. Also
+            note that the <apilink class="org.gradle.api.Plugin"/> class is a generic type. This example has it receiving the
+            <apilink class="org.gradle.api.Plugin"/> type as a type parameter. It's possible to write unusual custom
+            plugins that take different type parameters, but this will be unlikely (until someone figures out more
+            creative things to do here). 
         </para>
     </section>
 
@@ -148,24 +152,71 @@
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
 
-	<para>
-            So how does Gradle find the <apilink class="org.gradle.api.Plugin"/> implementation? The answer is you need to provide a properties file in the jar's
-	    <filename>META-INF/gradle-plugins</filename> directory that matches the name of your plugin.
-	</para>
+        <para>
+                So how does Gradle find the <apilink class="org.gradle.api.Plugin"/> implementation? The answer is you need to provide a properties file in the jar's
+            <filename>META-INF/gradle-plugins</filename> directory that matches the id of your plugin.
+        </para>
 
         <sample id="customPluginStandalone" dir="customPlugin/plugin" title="Wiring for a custom plugin">
-            <sourcefile file="src/main/resources/META-INF/gradle-plugins/greeting.properties"/>
+            <sourcefile file="src/main/resources/META-INF/gradle-plugins/org.samples.greeting.properties"/>
         </sample>	
 
-	<para>
-	    Notice that the properties filename matches the plugin's name and is placed in the resources folder, and
-	    that the <literal>implementation-class</literal> property identifies the <apilink class="org.gradle.api.Plugin"/> implementation class.
-	</para>
+        <para>
+            Notice that the properties filename matches the plugin id and is placed in the resources folder, and
+            that the <literal>implementation-class</literal> property identifies the <apilink class="org.gradle.api.Plugin"/> implementation class.
+        </para>
+
+        <section>
+            <title>Creating a plugin id</title>
+            <para>
+                Plugin ids are fully qualified in a manner similar to Java packages (i.e. a reverse domain name).  This helps to avoid
+                collisions and provides a way to group plugins with similar ownership.
+            </para>
+            <para>
+                Your plugin id should be a combination of components that reflect namespace (a reasonable pointer to you or your organization)
+                and the name of the plugin it provides.  For example if you had a Github account named “foo” and your plugin was named “bar”,
+                a suitable plugin id might be <literal>com.github.foo.bar</literal>.  Similarly, if the plugin was developed at the baz organization,
+                the plugin id might be <literal>org.baz.bar</literal>.
+            </para>
+            <para>
+                Plugin ids should conform to the following:
+            </para>
+            <itemizedlist>
+                <listitem>May contain any alphanumeric character, '.', and '-'.</listitem>
+                <listitem>Must contain at least one '.' character separating the namespace from the name of the plugin.</listitem>
+                <listitem>Conventionally use a lowercase reverse domain name convention for the namespace.</listitem>
+                <listitem>Conventionally use only lowercase characters in the name.</listitem>
+                <listitem><literal>org.gradle</literal> and <literal>com.gradleware</literal> namespaces may not be used.</listitem>
+                <listitem>Cannot start or end with a '.' character.</listitem>
+                <listitem>Cannot contain consecutive '.' characters (i.e. '..').</listitem>
+            </itemizedlist>
+            <para>
+                Although there are conventional similarities between plugin ids and package names, package names are generally more detailed than is
+                necessary for a plugin id.  For instance, it might seem reasonable to add “gradle” as a component of your plugin id, but since plugin
+                ids are only used for Gradle plugins, this would be superfluous.  Generally, a namespace that identifies ownership and a name are all
+                that are needed for a good plugin id.
+            </para>
+        </section>
+
+        <section>
+            <title>Publishing your plugin</title>
+            <para>
+                If you are publishing your plugin internally for use within your organization, you can publish it like
+                any other code artifact.  See the <link linkend="publishing_ivy">ivy</link> and <link linkend="publishing_maven">maven</link>
+                chapters on publishing artifacts.
+            </para>
+            <para>
+                If you are interested in publishing your plugin to be used by the wider Gradle community, you can publish it to the
+                <ulink url="http://plugins.gradle.org">Gradle plugin portal</ulink>.  This site provides the ability to search for and
+                gather information about plugins contributed by the Gradle community.  See the instructions
+                <ulink url="http://plugins.gradle.org/docs/submit">here</ulink> on how to make your plugin available on this site.
+            </para>
+        </section>
 
         <section>
             <title>Using your plugin in another project</title>
             <para>To use a plugin in a build script, you need to add the plugin classes to the build script's classpath. To
-                do this, you use a <literal>buildscript { }</literal> block, as described in <xref linkend="sec:external_dependencies"/>.
+                do this, you use a “<literal>buildscript { }</literal>” block, as described in <xref linkend="sec:applying_plugins_buildscript"/>.
                 The following example shows how you might do this when the JAR containing the plugin has been published
                 to a local repository:
             </para>
@@ -174,7 +225,15 @@
                 <test args="hello"/>
                 <sourcefile file="build.gradle" snippet="use-plugin"/>
             </sample>
+            <para>
+                Alternatively, if your plugin is published to the plugin portal, you can use the incubating plugins DSL (see <xref linkend="sec:plugins_block" />)
+                to apply the plugin:
+            </para>
+            <sample id="useCommunityPluginDSL" dir="plugins/dsl" title="Applying a community plugin with the plugins DSL">
+                <sourcefile file="build.gradle" snippet="use-community-plugin"/>
+            </sample>
         </section>
+
         <section>
             <title>Writing tests for your plugin</title>
             <para>You can use the <apilink class="org.gradle.testfixtures.ProjectBuilder"/> class to create
@@ -184,12 +243,25 @@
                 <sourcefile file="src/test/groovy/org/gradle/GreetingPluginTest.groovy" snippet="test-plugin"/>
             </sample>
         </section>
+
+        <section>
+            <title>Using the Java Gradle Plugin development plugin</title>
+            <para>You can use the incubating <link linkend="javaGradle_plugin">Java Gradle Plugin development plugin</link>
+                to eliminate some of the boilerplate declarations in your build script and provide some basic validations
+                of plugin metadata.  This plugin will automatically apply the <link linkend="java_plugin">Java plugin</link>,
+                add the <literal>gradleApi()</literal> dependency to the compile configuration, and perform plugin metadata
+                validations as part of the <literal>jar</literal> task execution.
+            </para>
+            <sample id="useJavaGradlePluginPlugin" dir="javaGradlePlugin" title="Using the Java Gradle Plugin Development plugin">
+                <sourcefile file="build.gradle" snippet="use-java-gradle-plugin-plugin"/>
+            </sample>
+        </section>
     </section>
 
     <section>
         <title>Maintaining multiple domain objects</title>
 
-        <para>Gradle provides some utility classes for maintaining collections of object, which work well with the Gradle build language.</para>
+        <para>Gradle provides some utility classes for maintaining collections of objects, which work well with the Gradle build language.</para>
 
         <sample id="domainObjectContainer" dir="userguide/organizeBuildLogic/customPluginWithDomainObjectContainer" title="Managing domain objects">
             <sourcefile file="build.gradle"/>
diff --git a/subprojects/docs/src/docs/userguide/customTasks.xml b/subprojects/docs/src/docs/userguide/customTasks.xml
index 94afb48..5cb8786 100644
--- a/subprojects/docs/src/docs/userguide/customTasks.xml
+++ b/subprojects/docs/src/docs/userguide/customTasks.xml
@@ -64,7 +64,7 @@
                         classpath of the build script. The task class is visible to every build script used by the build.
                         However, it is not visible outside the build, and so you cannot reuse the task class outside the
                         build it is defined in.
-                        Using the <filename>buildSrc</filename> project approach keeps separate
+                        Using the <filename>buildSrc</filename> project approach separates
                         the task declaration - that is, what the task should do - from the task implementation - that is,
                         how the task does it.</para>
                     <para>
@@ -171,7 +171,7 @@
             </para>
             <para>
                 The feature incubation process, which is part of the Gradle feature lifecycle (see <xref linkend="feature_lifecycle"/>), exists for this purpose of ensuring high quality
-                final implementation through incorporation of early user feedback.
+                final implementations through incorporation of early user feedback.
             </para>
         </note>
         <para>
@@ -238,7 +238,7 @@
             </para>
             <para>
                 First, consider the <literal>IncrementalReverseTask</literal> executed against a set of inputs for the first time.
-                In this case, all inputs will be considered "out of date":
+                In this case, all inputs will be considered “out of date”:
             </para>
             <sample id="incrementalTaskFirstRun" dir="userguide/tasks/incrementalTask" title="Running the incremental task for the first time">
                 <sourcefile file="build.gradle" snippet="reverse"/>
@@ -252,7 +252,7 @@
                 <output args="-q incrementalReverse" ignoreLineOrder="true"/>
             </sample>
             <para>
-                Naturally when the task is executed again with no changes, then task itself is up to date and no files are reported to the task action:
+                Naturally when the task is executed again with no changes, then the entire task is up to date and no files are reported to the task action:
             </para>
             <sample id="incrementalTaskNoChange" dir="userguide/tasks/incrementalTask" title="Running the incremental task with unchanged inputs">
                 <test args="-q originalInputs incrementalReverse"/>
@@ -269,7 +269,7 @@
             </sample>
 
             <para>
-                When an existing input file is removed, then re-executing the task results that file being reported to <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/>:
+                When an existing input file is removed, then re-executing the task results in that file being reported to <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/>:
             </para>
             <sample id="incrementalTaskRemovedInput" dir="userguide/tasks/incrementalTask" title="Running the incremental task with an input file removed">
                 <sourcefile file="build.gradle" snippet="removed-input" />
@@ -289,7 +289,7 @@
             </sample>
 
             <para>
-                When a task input property modified, Gradle is not able to determine how this property impacted the task outputs, so all input files are assumed to be out of date.
+                When a task input property is modified, Gradle is unable to determine how this property impacted the task outputs, so all input files are assumed to be out of date.
                 So similar to the changed output file example, <emphasis>all</emphasis> input files are reported to
                 the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="outOfDate"/> action,
                 and no input files are reported to the <apilink class="org.gradle.api.tasks.incremental.IncrementalTaskInputs" method="removed"/> action:
@@ -300,4 +300,4 @@
             </sample>
         </section>
     </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/depMngmt.xml b/subprojects/docs/src/docs/userguide/depMngmt.xml
index 7fe1cf6..aaffa5e 100644
--- a/subprojects/docs/src/docs/userguide/depMngmt.xml
+++ b/subprojects/docs/src/docs/userguide/depMngmt.xml
@@ -17,7 +17,7 @@
     <title>Dependency Management</title>
     <section id='sec:Introduction'>
         <title>Introduction</title>
-        <para>Dependency management is a critical feature of every build, and Gradle has placed an emphasis on offering first-class dependency management that is both easy-to-understand and
+        <para>Dependency management is a critical feature of every build, and Gradle has placed an emphasis on offering first-class dependency management that is both easy to understand and
             compatible with a wide variety of approaches. If you are familiar with the approach used by either Maven or Ivy you will be delighted to learn that Gradle is fully compatible with both
             approaches in addition to being flexible enough to support fully-customized approaches.
         </para>
@@ -43,7 +43,7 @@
                 </para>
             </listitem>
             <listitem>
-                <para><emphasis>Full Compatibility with Maven and Ivy</emphasis>: If you have defined dependencies in a Maven POM or an Ivy file, Gradle provide seamless integration with a range of
+                <para><emphasis>Full Compatibility with Maven and Ivy</emphasis>: If you have defined dependencies in a Maven POM or an Ivy file, Gradle provides seamless integration with a range of
                     popular build tools.
                 </para>
             </listitem>
@@ -108,9 +108,9 @@
         </para>
         <section id='sub:versioning_the_jar_name'>
             <title>Put the Version in the Filename (Version the jar)</title>
-            <para>The version of a library must be easy to recognize in the filename. While the version of a jar is usually in the Manifest file, it isn't readily apparent when you are inspecting a
+            <para>The version of a library must be part of the filename. While the version of a jar is usually in the Manifest file, it isn't readily apparent when you are inspecting a
                 project. If someone asks you to look at a collection of 20 jar files, which would you prefer? A collection of files with names like <filename>commons-beanutils-1.3.jar</filename>
-                or a collection of files with names like <filename>spring.jar</filename>? If dependencies have file names with version numbers it is much easier to quickly identify the versions of
+                or a collection of files with names like <filename>spring.jar</filename>? If dependencies have file names with version numbers you can quickly identify the versions of
                 your dependencies.
             </para>
             <para>If versions are unclear you can introduce subtle bugs which are very hard to find. For example there might be a project which uses Hibernate 2.5. Think about a developer who decides
@@ -127,11 +127,11 @@
             <para>Transitive dependency management is a technique that enables your project to depend on libraries which, in turn, depend on other libraries. This recursive pattern of transitive
                 dependencies results in a tree of dependencies including your project's first-level dependencies, second-level dependencies, and so on. If you don't model your dependencies as a
                 hierarchical tree of first-level and second-level dependencies it is very easy to quickly lose control over an assembled mess of unstructured dependencies. Consider the Gradle project
-                itself, while Gradle only has a few direct, first-level dependencies, when Gradle is compiled it needs more that one hundred dependencies on the classpath. On a far larger scale,
-                Enterprise projects using Spring, Hibernate, and other libraries, alongside hundreds or thousands of internal projects can have very large dependency trees.
+                itself, while Gradle only has a few direct, first-level dependencies, when Gradle is compiled it needs more than one hundred dependencies on the classpath. On a far larger scale,
+                Enterprise projects using Spring, Hibernate, and other libraries, alongside hundreds or thousands of internal projects, can result in very large dependency trees.
             </para>
             <para>When these large dependency trees need to change, you'll often have to solve some dependency version conflicts. Say one open source library needs one version of a logging library and
-                a another uses an alternative version. Gradle and other build tools all have the ability to solve this dependency tree and resolve conflicts, but what differentiates Gradle is the
+                a another uses an alternative version. Gradle and other build tools all have the ability to resolve conflicts, but what differentiates Gradle is the
                 control it gives you over transitive dependencies and conflict resolution.
             </para>
             <para>While you could try to manage this problem manually, you will quickly find that this approach doesn't scale. If you want to get rid of a first level dependency you really can't be
@@ -139,8 +139,8 @@
                 first level dependency. If you try to manage transitive dependencies yourself, the end of the story is that your build becomes brittle: no one dares to change your dependencies because
                 the risk of breaking the build is too high. The project classpath becomes a complete mess, and, if a classpath problem arises, hell on earth invites you for a ride.
             </para>
-            <note><emphasis>NOTE:</emphasis>In one project, we found a mystery, LDAP related jar in the classpath. No code referenced this jar and there was no connection to the project. No one could
-                figure out what the jar was for, until it was removed from the build and the application suffered massive performance problem whenever it attempted to authenticate to LDAP. This
+            <note><emphasis>NOTE:</emphasis>In one project, we found a mystery LDAP related jar in the classpath. No code referenced this jar and there was no connection to the project. No one could
+                figure out what the jar was for, until it was removed from the build and the application suffered massive performance problems whenever it attempted to authenticate to LDAP. This
                 mystery jar was a necessary transitive, fourth-level dependency that was easy to miss because no one had bothered to use managed transitive dependencies.
             </note>
             <para>Gradle offers you different ways to express first-level and transitive dependencies. With Gradle you can mix and match approaches; for example, you could store your jars in an SCM
@@ -171,7 +171,7 @@
                     <emphasis>Newest</emphasis>: The newest version of the dependency is used. This is Gradle's default strategy, and is often an appropriate choice as long as versions are backwards-compatible.
                 </listitem>
                 <listitem>
-                    <emphasis>Fail</emphasis>: A version conflict results in a build failure. This strategy enforces that all version conflicts are resolved explicitly in the build script. See
+                    <emphasis>Fail</emphasis>: A version conflict results in a build failure. This strategy requires all version conflicts to be resolved explicitly in the build script. See
                     <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/> for details on how to explicitly choose a particular version.
                 </listitem>
             </itemizedlist>
@@ -194,21 +194,21 @@
         </section>
         <section id='sub:dynamic_versions_and_changing_modules'>
             <title>Use Dynamic Versions and Changing Modules</title>
-            <para>There are many situation when you want to use the latest version of a particular dependency, or the latest in a range of versions. This can be a requirement during development, or
+            <para>There are many situations when you want to use the latest version of a particular dependency, or the latest in a range of versions. This can be a requirement during development, or
                 you may be developing a library that is designed to work with a range of dependency versions. You can easily depend on these constantly changing dependencies by using a
                 <emphasis>dynamic version</emphasis>. A dynamic version can be either a version range (e.g. <literal>2.+</literal>) or it can be a placeholder for the latest version available
                 (e.g. <literal>latest.integration</literal>).
             </para>
             <para>Alternatively, sometimes the module you request can change over time, even for the same version. An example of this type of <emphasis>changing module</emphasis>
                 is a Maven <literal>SNAPSHOT</literal> module, which always points at the latest artifact published. In other words, a standard Maven snapshot is a module that never stands still
-                so to speak, it is a "changing module".
+                so to speak, it is a “changing module”.
             </para>
             <para>The main difference between a <emphasis>dynamic version</emphasis> and a <emphasis>changing module</emphasis> is that when you resolve a <emphasis>dynamic version</emphasis>, you'll
                 get the real, static version as the module name. When you resolve a <emphasis>changing module</emphasis>, the artifacts are named using the version you requested, but the underlying
                 artifacts may change over time.
             </para>
             <para>By default, Gradle caches dynamic versions and changing modules for 24 hours. You can override the default cache modes using <link linkend="sec:cache_command_line_options">command
-                line options</link>. You can change the cache expiry times in your build using the <literal>resolution strategy</literal> (see <xref linkend='sec:controlling_caching'/>).
+                line options</link>. You can change the cache expiry times in your build using the resolution strategy (see <xref linkend='sec:controlling_caching'/>).
             </para>
         </section>
     </section>
@@ -216,7 +216,7 @@
         <title>Dependency configurations</title>
         <para>In Gradle dependencies are grouped into configurations. Configurations have a name, a number of other properties,
             and they can extend each other.
-            Many Gradle plugin add pre-defined configurations to your project. The Java plugin, for example,
+            Many Gradle plugins add pre-defined configurations to your project. The Java plugin, for example,
             adds some configurations to represent the various classpaths it needs. see
             <xref linkend='sec:java_plugin_and_dependency_management'/>
             for details. Of course you can add custom configurations on top of that. There are many use cases
@@ -305,11 +305,11 @@
             <sample id="moduleDependencies" dir="userguide/artifacts/externalDependencies" title="Module dependencies">
                 <sourcefile file="build.gradle" snippet="module-dependencies"/>
             </sample>
-            <para>See <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/>
+            <para>See the <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/> class in the API documentation
                 for more examples and a complete reference.
             </para>
             <para>Gradle provides different notations for module dependencies. There is a string notation and
-                a map notation. A module dependency has an API which allows for further configuration. Have a look at
+                a map notation. A module dependency has an API which allows further configuration. Have a look at
                 <apilink class='org.gradle.api.artifacts.ExternalModuleDependency'/>
                 to learn all about the API.
                 This API provides properties and configuration methods. Via the string notation you can define a subset
@@ -317,7 +317,7 @@
                 either with the map or with the string notation, you can assign a single dependency to a configuration
                 together with a closure.
             </para>
-            <para>If you declare a module dependency, Gradle looks for a corresponding module descriptor file (<filename>pom.xml</filename> or
+            <para>If you declare a module dependency, Gradle looks for a module descriptor file (<filename>pom.xml</filename> or
                 <filename>ivy.xml</filename>) in the repositories. If such a module descriptor file exists, it is parsed and the artifacts of
                 this module (e.g. <filename>hibernate-3.0.5.jar</filename>) as well as its dependencies (e.g. cglib) are downloaded. If no such
                 module descriptor file exists, Gradle looks for a file called <filename>hibernate-3.0.5.jar</filename>
@@ -341,7 +341,7 @@
                     </listitem>
                 </itemizedlist>
                 There are other situations where it is necessary to fine-tune dependency declarations.
-                Please see <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/> for examples and a complete reference for declaring dependencies.
+                Please see the <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/> class in the API documentation for examples and a complete reference for declaring dependencies.
             </section>
             <section id='ssub:artifact_dependencies'>
                 <title>Artifact only notation</title>
@@ -372,7 +372,7 @@
                 <sample id="classifier" dir="userguide/artifacts/excludesAndClassifiers" title="Dependency with classifier">
                     <sourcefile file="build.gradle" snippet="classifier"/>
                 </sample>
-                <para>As can be seen in the first line above, classifiers can be used together with artifact only notation.
+                <para>As can be seen in the first line above, classifiers can be used together with the artifact only notation.
                 </para>
             </section>
             <para>It is easy to iterate over the dependency artifacts of a configuration:</para>
@@ -384,7 +384,7 @@
 
         <section id='sub:client_module_dependencies'>
             <title>Client module dependencies</title>
-            <para>Client module dependencies allow to declare <emphasis>transitive</emphasis> dependencies directly in the build script. They are a replacement for a module descriptor
+            <para>Client module dependencies allow you to declare <emphasis>transitive</emphasis> dependencies directly in the build script. They are a replacement for a module descriptor
                 in an external repository.
             </para>
             <sample id="client-modules" dir="userguide/artifacts/externalDependencies" title="Client module dependencies - transitive dependencies">
@@ -393,8 +393,7 @@
             <para>This declares a dependency on Groovy. Groovy itself has dependencies. But Gradle does
                 not look for an XML descriptor to figure them out but gets the information from the build file. The
                 dependencies of a client module can be normal module dependencies or artifact dependencies or another
-                client module. Have also a look at the API documentation:
-                <apilink class='org.gradle.api.artifacts.ClientModule'/>
+                client module. Also look at the API documentation for the <apilink class='org.gradle.api.artifacts.ClientModule'/> class.
             </para>
             <para>In the current release client modules have one limitation. Let's say your project is a library and
                 you want this library to be uploaded to your company's Maven or Ivy repository. Gradle uploads the
@@ -413,9 +412,9 @@
                 <sourcefile file="build.gradle" snippet="project-dependencies"/>
             </sample>
             <para>For more information see the API documentation for
-                <apilink class="org.gradle.api.artifacts.ProjectDependency"/>
+                <apilink class="org.gradle.api.artifacts.ProjectDependency"/>.
             </para>
-            <para>Multi-project builds are discussed in<xref linkend='multi_project_builds'/>.
+            <para>Multi-project builds are discussed in <xref linkend='multi_project_builds'/>.
             </para>
         </section>
 
@@ -478,7 +477,7 @@
                 If you want to exclude a transitive dependency from all your
                 configurations you can use the Groovy spread-dot operator to express this in a concise way, as shown in the example.
                 When defining an exclude, you can specify either only the organization or only the module name or both.
-                Have also a look at the API documentation of <apilink class="org.gradle.api.artifacts.Dependency"/> and <apilink class="org.gradle.api.artifacts.Configuration"/>.
+                Also look at the API documentation of the <apilink class="org.gradle.api.artifacts.Dependency"/> and <apilink class="org.gradle.api.artifacts.Configuration"/> classes.
             </para>
             <para>
                 Not every transitive dependency can be excluded - some transitive dependencies might be essential
@@ -488,12 +487,12 @@
             </para>
             <para>
                 Should you exclude per-dependency or per-configuration?
-                It turns out that in majority of cases you want to use the per-configuration exclusion.
-                Here are the some exemplary reasons why one might want to exclude a transitive dependency.
-                Bear in mind that for some of those use cases there are better solutions than exclusions!
+                It turns out that in the majority of cases you want to use the per-configuration exclusion.
+                Here are some typical reasons why one might want to exclude a transitive dependency.
+                Bear in mind that for some of these use cases there are better solutions than exclusions!
                 <itemizedlist>
                     <listitem>The dependency is undesired due to licensing reasons.</listitem>
-                    <listitem>The dependency is not available in any of remote repositories.</listitem>
+                    <listitem>The dependency is not available in any remote repositories.</listitem>
                     <listitem>The dependency is not needed for runtime.</listitem>
                     <listitem>The dependency has a version that conflicts with a desired version. For that use case please refer to <xref linkend='sub:version_conflicts'/>
                         and the documentation on <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/> for a potentially better solution to the problem.
@@ -506,15 +505,15 @@
                 that unwanted transitive dependency.
             </para>
             <para>
-                Other examples of the dependency exclusions can be found in the reference for <apilink class='org.gradle.api.artifacts.ModuleDependency'/> or
-                <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/>.
+                Other examples of dependency exclusions can be found in the reference for the <apilink class='org.gradle.api.artifacts.ModuleDependency'/> or
+                <apilink class='org.gradle.api.artifacts.dsl.DependencyHandler'/> classes.
             </para>
         </section>
         <section>
             <title>Optional attributes</title>
-            <para id="para:dependencies_with_empty_attributes">All attributes for a dependency are optional, except the name. It depends on the repository type,
-                which information is need for actually finding the dependencies in the repository.
-                See <xref linkend='sec:repositories'/>. If you work for example with Maven repositories, you need to define the
+            <para id="para:dependencies_with_empty_attributes">All attributes for a dependency are optional, except the name.
+                Which attributes are required for actually finding dependencies in the repository will depend on the repository type.
+                See <xref linkend='sec:repositories'/>. For example, if you work with Maven repositories, you need to define the
                 group, name and version. If you work with filesystem repositories you might only need the name or the name
                 and the version.
             </para>
@@ -531,7 +530,7 @@
             <title>Dependency configurations</title>
             <para>In Gradle a dependency can have different configurations (as your project can have different configurations). If you
                 don't specify anything explicitly, Gradle uses the default configuration of the dependency. For dependencies
-                from a Maven repository, the default configuration is the only available one anyway. If you work with Ivy repositories and
+                from a Maven repository, the default configuration is the only possibility anyway. If you work with Ivy repositories and
                 want to declare a non-default configuration for your dependency you have to use the map notation and declare:
             </para>
             <sample id="dependencyConfigurations" dir="userguide/artifacts/externalDependencies" title="Dependency configurations">
@@ -550,9 +549,9 @@
             <para>
                 Since Gradle 1.2 there is also a new programmatic API to access the resolved dependency information.
                 The dependency reports (see the previous paragraph) are using this API under the covers.
-                The API lets you to walk the resolved dependency graph and provides information about the dependencies.
-                With the coming releases the API will grow to provide more information about the resolution result.
-                For more information about the API please refer to the javadocs on
+                The API lets you walk the resolved dependency graph and provides information about the dependencies.
+                In future releases the API will grow to provide more information about the resolution result.
+                For more information about the API please refer to the Javadocs on
                 <apilink class="org.gradle.api.artifacts.ResolvableDependencies" method="getResolutionResult"/>.
                 Potential usages of the <apilink class="org.gradle.api.artifacts.result.ResolutionResult"/> API:
                 <itemizedlist>
@@ -578,8 +577,8 @@
             <sourcefile file="build.gradle" snippet="dependencies"/>
             <output args="-q dependencies"/>
         </sample>
-        <para><code>dependencies</code> returns only the dependencies belonging explicitly to the configuration.
-            <code>allDependencies</code> includes the dependencies from extended configurations.
+        <para>The <code>dependencies</code> task returns only the dependencies belonging explicitly to the configuration.
+            The <code>allDependencies</code> task includes the dependencies from extended configurations.
         </para>
         <para>To get the library files of the configuration dependencies you can do:
         </para>
@@ -683,6 +682,159 @@
             </tr>
         </table>
 
+        <section id='sub:supported_transport_protocols'>
+            <title>Supported repository transport protocols</title>
+            <para>Maven and Ivy repositories support the use of various transport protocols. At the moment the following protocols are supported:
+            </para>
+            <table>
+                <title>Repository transport protocols</title>
+                <thead>
+                    <tr>
+                        <td>Type</td>
+                        <td>Authentication schemes</td>
+                    </tr>
+                </thead>
+                <tr>
+                    <td><literal>file</literal></td>
+                    <td>none</td>
+                </tr>
+                <tr>
+                    <td><literal>http</literal></td>
+                    <td>username/password</td>
+                </tr>
+                <tr>
+                    <td><literal>https</literal></td>
+                    <td>username/password</td>
+                </tr>
+                <tr>
+                    <td><literal>sftp</literal></td>
+                    <td>username/password</td>
+                </tr>
+                <tr>
+                    <td><literal>s3</literal></td>
+                    <td>access key/secret key</td>
+                </tr>
+            </table>
+            <para>To define a repository use the <literal>repositories</literal> configuration block. Within the <literal>repositories</literal> closure,
+                a Maven repository is declared with <literal>maven</literal>. An Ivy repository is declared with <literal>ivy</literal>. The transport protocol
+                is part of the URL definition for a repository. The following build script demonstrates how to create a HTTP-based Maven and Ivy repository:
+            </para>
+            <sample id="mavenIvyRepositoriesNoAuth" dir="userguide/artifacts/defineRepository" title="Declaring a Maven and Ivy repository">
+                <sourcefile file="build.gradle" snippet="maven-ivy-repository-no-auth"/>
+            </sample>
+            <para>If authentication is required for a repository, the relevant credentials can be provided. The following example shows how to provide
+                username/password-based authentication for SFTP repositories:
+            </para>
+            <sample id="mavenIvyRepositoriesAuth" dir="userguide/artifacts/defineRepository" title="Providing credentials to a Maven and Ivy repository">
+                <sourcefile file="build.gradle" snippet="maven-ivy-repository-auth"/>
+            </sample>
+            <para>When using an AWS S3 backed repository you need to authenticate using <apilink class="org.gradle.api.credentials.AwsCredentials" />, providing access-key and a private-key.
+                The following example shows how to declare a S3 backed repository and providing AWS credentials:
+            </para>
+            <sample id="mavenIvyS3RepositoriesAuth" dir="userguide/artifacts/defineRepository" title="Declaring a S3 backed Maven and Ivy repository">
+                <sourcefile file="build.gradle" snippet="maven-ivy-s3-repository"/>
+            </sample>
+
+            <section id='sub:s3_configuration properties'>
+                <title>S3 configuration properties</title>
+                <para>
+                    The following system properties can be used to configure the interactions with s3 repositories:
+                </para>
+                <table>
+                    <title>S3 Configuration Properties</title>
+                    <thead>
+                        <tr>
+                            <td>Property</td>
+                            <td>Description</td>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr>
+                            <td>org.gradle.s3.endpoint</td>
+                            <td>Used to override the AWS S3 endpoint when using a non AWS, S3 API compatible, storage service.</td>
+                        </tr>
+                        <tr>
+                            <td>org.gradle.s3.maxErrorRetry</td>
+                            <td>Specifies the maximum number of times to retry a request in the event that the S3 server responds with a HTTP 5xx status code. When not specified a default value of 3
+                                is used.
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </section>
+
+            <section id='sub:s3_url_formats'>
+                <title>S3 URL formats</title>
+                <para>S3 URL's are 'virtual-hosted-style' and must be in the following format
+                    <literal>s3://<bucketName>[.<regionSpecificEndpoint>]/<s3Key></literal>
+                </para>
+                <para>e.g.
+                    <literal>s3://myBucket.s3.eu-central-1.amazonaws.com/maven/release</literal>
+                </para>
+                <itemizedlist>
+                    <listitem>
+                        <literal>myBucket</literal>
+                        is the AWS S3 bucket name.
+                    </listitem>
+                    <listitem>
+                        <literal>s3.eu-central-1.amazonaws.com</literal>
+                        is the <emphasis>optional</emphasis> <ulink url='http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region'> region specific endpoint</ulink>.
+                    </listitem>
+                    <listitem>
+                        <literal>/maven/release</literal>
+                        is the AWS S3 key (unique identifier for an object within a bucket)
+                    </listitem>
+                </itemizedlist>
+            </section>
+
+            <section id='sub:s3_proxy_settings'>
+                <title>S3 proxy settings</title>
+                <para>A proxy for S3 can be configured using the following system properties:</para>
+                <itemizedlist>
+                    <listitem><literal>https.proxyHost</literal></listitem>
+                    <listitem><literal>https.proxyPort</literal></listitem>
+                    <listitem><literal>https.proxyUser</literal></listitem>
+                    <listitem><literal>https.proxyPassword</literal></listitem>
+                    <listitem><literal>https.nonProxyHosts</literal></listitem>
+                </itemizedlist>
+
+
+                <para>If the 'org.gradle.s3.endpoint' property has been specified with a http (not https) URI the following system proxy settings can be used:</para>
+                <itemizedlist>
+                    <listitem><literal>http.proxyHost</literal></listitem>
+                    <listitem><literal>http.proxyPort</literal></listitem>
+                    <listitem><literal>http.proxyUser</literal></listitem>
+                    <listitem><literal>http.proxyPassword</literal></listitem>
+                    <listitem><literal>http.nonProxyHosts</literal></listitem>
+                </itemizedlist>
+            </section>
+
+            <section id="s3_v4_signatures">
+                <title>AWS S3 V4 Signatures (AWS4-HMAC-SHA256)</title>
+                <para>
+                    Some of the AWS S3 regions (eu-central-1 - Frankfurt) require that all HTTP requests are signed in accordance with AWS's
+                    <ulink url="http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html">signature version 4</ulink>. It is recommended to specify
+                    S3 URL's containing the region specific endpoint when using buckets that require V4 signatures.
+                    e.g. <literal>s3://somebucket.s3.eu-central-1.amazonaws.com/maven/release</literal>
+                </para>
+                <note><emphasis>NOTE:</emphasis> When a region-specific endpoint is not specified for buckets requiring V4 Signatures, Gradle will use the default AWS region (us-east-1) and the
+                    following warning will appear on the console:
+                    <para>
+                        Attempting to re-send the request to .... with AWS V4 authentication. To avoid this warning in the future, please use region-specific endpoint to access buckets located in
+                        regions that require V4 signing.
+                    </para>
+                    <para>
+                        Failing to specify the region-specific endpoint for buckets requiring V4 signatures means:
+                    </para>
+                    <itemizedlist>
+                        <listitem><literal>3 round-trips to AWS, as opposed to one, for every file upload and download.</literal></listitem>
+                        <listitem><literal>Depending on location - increased network latencies and slower builds.</literal></listitem>
+                        <listitem><literal>Increased likelihood of transmission failures.</literal></listitem>
+                    </itemizedlist>
+                </note>
+            </section>
+        </section>
+
         <section id='sub:maven_central'>
             <title>Maven central repository</title>
             <para>To add the central Maven 2 repository (<ulink url='http://repo1.maven.org/maven2'/>) simply add this to your build script:
@@ -690,7 +842,10 @@
             <sample id="mavenCentral" dir="userguide/artifacts/defineRepository" title="Adding central Maven repository">
                 <sourcefile file="build.gradle" snippet="maven-central"/>
             </sample>
-            <para>Now Gradle will look for your dependencies in this repository.
+            <para>Now Gradle will look for your dependencies in this repository.</para>
+            <para>
+                <emphasis>Warning:</emphasis> Be aware that the central Maven 2 repository is HTTP only and
+                HTTPS is not supported. If you need a public HTTPS enabled central repository, you can use the <ulink url='http://jcenter.bintray.com'>JCenter</ulink> public repository (see <xref linkend="sub:maven_jcenter"/>).
             </para>
         </section>
 
@@ -698,13 +853,17 @@
             <title>Maven JCenter repository</title>
             <para><ulink url='http://bintray.com'>Bintray</ulink>'s JCenter is an up-to-date collection of all popular Maven OSS artifacts, including artifacts published directly to Bintray.
             </para>
-            <para>To add the JCenter Maven repository (<ulink url='http://jcenter.bintray.com'/>) simply add this to your build script:
+            <para>To add the JCenter Maven repository (<ulink url='https://jcenter.bintray.com'/>) simply add this to your build script:
             </para>
             <sample id="mavenJcenter" dir="userguide/artifacts/defineRepository" title="Adding Bintray's JCenter Maven repository">
                 <sourcefile file="build.gradle" snippet="maven-jcenter"/>
             </sample>
-            <para>Now Gradle will look for your dependencies in the JCenter repository.
+            <para>Now Gradle will look for your dependencies in the JCenter repository. <emphasis>jcenter()</emphasis> uses HTTPS to connect to the repository.
+                If you want to use HTTP you can configure <literal>jcenter()</literal>:
             </para>
+            <sample id="mavenJcenter" dir="userguide/artifacts/defineRepository" title="Using Bintrays's JCenter with HTTP">
+                <sourcefile file="build.gradle" snippet="maven-jcenter-http"/>
+            </sample>
         </section>
 
         <section id='sub:maven_local'>
@@ -755,52 +914,65 @@
             <sample id="flatDirMulti" dir="userguide/artifacts/defineRepository" title="Flat repository resolver">
                 <sourcefile file="build.gradle" snippet="flat-dir-multi"/>
             </sample>
-            <para>This adds repositories which look into one or more directories for finding dependencies. If you only
-                work with flat directory resolvers you don't need to set all attributes of a dependency.
-                See
-                <xref linkend='para:dependencies_with_empty_attributes'/>
+            <para>This adds repositories which look into one or more directories for finding dependencies. Note that
+                this type of repository does not support any meta-data formats like Ivy XML or Maven POM files. Instead,
+                Gradle will dynamically generate a module descriptor (without any dependency information) based on the
+                presence of artifacts. However, as Gradle prefers to use modules whose descriptor has been created from
+                real meta-data rather than being generated, flat directory repositories cannot be used to override
+                artifacts with real meta-data from other repositories. So, for example, if Gradle finds only
+                <filename>jmxri-1.2.1.jar</filename> in a flat directory repository, but <filename>jmxri-1.2.1.pom</filename>
+                in another repository that supports meta-data, it will use the second repository to provide the module.
+                For the use case of overriding remote artifacts with local ones consider using an Ivy or Maven repository
+                instead whose URL points to a local directory.
+                If you only work with flat directory repositories you don't need to set all attributes of a dependency.
+                See <xref linkend='para:dependencies_with_empty_attributes'/>.
             </para>
         </section>
 
         <section id="sec:ivy_repositories">
             <title>Ivy repositories</title>
-            <para>To use an Ivy repository with a standard layout:</para>
-            <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository">
-                <sourcefile file="build.gradle" snippet="ivy-repo-with-maven-layout"/>
-            </sample>
-            <para>See
-                <apilink class="org.gradle.api.artifacts.repositories.IvyArtifactRepository"/>
-                for details.
-            </para>
             <section>
-                <title>Defining custom patterns for an Ivy repository</title>
-                <para>To define an Ivy repository with a non-standard layout, you can define a pattern layout for the repository:
-                </para>
-                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with pattern layout">
-                    <sourcefile file="build.gradle" snippet="ivy-repo-with-pattern-layout"/>
+                <title>Defining an Ivy repository with a standard layout</title>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository">
+                    <sourcefile file="build.gradle" snippet="ivy-repo"/>
                 </sample>
             </section>
             <section>
-                <title>Ivy repository with Maven compatible layout</title>
-                <para>Optionally, a repository with pattern layout can have its 'organisation' part laid out in Maven style, with
-                    forward slashes replacing dots as separators. For example, the organisation <literal>my.company</literal> would then be represented as <literal>my/company</literal>.
+                <title>Defining a named layout for an Ivy repository</title>
+                <para>
+                    You can specify that your repository conforms to the Ivy or Maven default layout by using a named layout.
                 </para>
-                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with Maven compatible layout">
-                    <sourcefile file="build.gradle" snippet="ivy-repo-with-m2compatible-layout"/>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with named layout">
+                    <sourcefile file="build.gradle" snippet="ivy-repo-with-maven-layout"/>
                 </sample>
+                <para>
+                    Valid named layout values are <literal>'gradle'</literal> (the default), <literal>'maven'</literal> and <literal>'ivy'</literal>.
+                    See <apilink class="org.gradle.api.artifacts.repositories.IvyArtifactRepository" method="layout(java.lang.String, groovy.lang.Closure)"/> in the API documentation for details of these named layouts.
+                </para>
             </section>
             <section>
-                <title>Defining different artifact and Ivy file locations for an Ivy repository</title>
-                <para>To define an Ivy repository which fetches Ivy files and artifacts from different locations, you can use the pattern layout with separate patterns to use to locate
-                    the Ivy files and artifacts:
+                <title>Defining custom pattern layout for an Ivy repository</title>
+                <para>To define an Ivy repository with a non-standard layout, you can define a 'pattern' layout for the repository:
                 </para>
-                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with custom patterns">
-                    <sourcefile file="build.gradle" snippet="ivy-repo-with-custom-pattern"/>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with pattern layout">
+                    <sourcefile file="build.gradle" snippet="ivy-repo-with-pattern-layout"/>
                 </sample>
+                <para>To define an Ivy repository which fetches Ivy files and artifacts from different locations,
+                    you can define separate patterns to use to locate the Ivy files and artifacts:
+                </para>
                 <para>
-                    Each <literal>artifact</literal> or <literal>ivy</literal> specified for a repository adds an <emphasis>additional</emphasis> pattern to use. The patterns are used in the order
-                    that they are defined.
+                    Each <literal>artifact</literal> or <literal>ivy</literal> specified for a repository adds an <emphasis>additional</emphasis> pattern to use.
+                    The patterns are used in the order that they are defined.
                 </para>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with multiple custom patterns">
+                    <sourcefile file="build.gradle" snippet="ivy-repo-with-custom-pattern"/>
+                </sample>
+                <para>Optionally, a repository with pattern layout can have its 'organisation' part laid out in Maven style, with
+                    forward slashes replacing dots as separators. For example, the organisation <literal>my.company</literal> would then be represented as <literal>my/company</literal>.
+                </para>
+                <sample id="ivyRepository" dir="userguide/artifacts/defineRepository" title="Ivy repository with Maven compatible layout">
+                    <sourcefile file="build.gradle" snippet="ivy-repo-with-m2compatible-layout"/>
+                </sample>
             </section>
             <section>
                 <title>Accessing password protected Ivy repositories</title>
@@ -825,11 +997,15 @@
         </section>
         <section id='sub:more_about_ivy_resolvers'>
             <title>More about Ivy resolvers</title>
-            <para>Gradle, thanks to Ivy under its hood, is extremely flexible regarding repositories:
+            <para>Gradle is extremely flexible regarding repositories:
             </para>
             <itemizedlist>
                 <listitem>
-                    <para>There are many options for the protocol to communicate with the repository (e.g. filesystem, http, ssh, ...)
+                    <para>There are many options for the protocol to communicate with the repository (e.g. filesystem, http, ssh, sftp ...)
+                    </para>
+                </listitem>
+                <listitem>
+                    <para>The protocol sftp currently only supports username/password-based authentication.
                     </para>
                 </listitem>
                 <listitem>
@@ -925,7 +1101,7 @@ someroot/[artifact]-[revision].[ext]
             <para>
                 Force versions can also be used to deal with rogue metadata of transitive dependencies.
                 If a transitive dependency has poor quality metadata that leads to problems at dependency resolution time, you can force Gradle to use a newer, fixed version of this dependency.
-                For an example, see <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/>.
+                For an example, see the <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/> class in the API documentation.
                 Note that 'dependency resolve rules' (outlined below) provide a more powerful mechanism for replacing a broken module dependency. See <xref linkend='sec:blacklisting_version'/>.
             </para>
         </section>
@@ -938,7 +1114,7 @@ someroot/[artifact]-[revision].[ext]
             <para>
                 Dependency resolve rules provide a very powerful way to control the dependency resolution process, and can be used to implement all sorts of advanced
                 patterns in dependency management. Some of these patterns are outlined below.
-                For more information and code samples see <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/>.
+                For more information and code samples see the <apilink class='org.gradle.api.artifacts.ResolutionStrategy'/> class in the API documentation.
             </para>
             <section id='sec:releasable_unit'>
                 <title>Modelling releaseable units</title>
@@ -964,7 +1140,7 @@ someroot/[artifact]-[revision].[ext]
             <section id='sec:custom_versioning_scheme'>
                 <title>Implement a custom versioning scheme</title>
                 <para>
-                    In some corporate environments, the list of module versions that can be declared in gradle builds is maintained and audited externally.
+                    In some corporate environments, the list of module versions that can be declared in Gradle builds is maintained and audited externally.
                     Dependency resolve rules provide a neat implementation of this pattern:
                     <itemizedlist>
                         <listitem>In the build script, the developer declares dependencies with the module group and name, but uses a placeholder version, for example: '<literal>default</literal>'.</listitem>
@@ -1006,6 +1182,56 @@ someroot/[artifact]-[revision].[ext]
                     </sample>
                 </para>
             </section>
+            <section id='sec:module_replacement'>
+                <title>Declaring that a legacy library is replaced by a new one</title>
+                <para>
+                    A good example when a new library replaced a legacy one is the "google-collections" -> "guava" migration.
+                    The team that created google-collections decided to change the module name from "com.google.collections:google-collections" into "com.google.guava:guava".
+                    This a legal scenario in the industry: teams need to be able to change the names of products they maintain, including the module coordinates.
+                    Renaming of the module coordinates has impact on conflict resolution.
+                </para>
+                <para>
+                    To explain the impact on conflict resolution, let's consider the "google-collections" -> "guava" scenario.
+                    It may happen that both libraries are pulled into the same dependency graph.
+                    For example, "our" project depends on guava but some of our dependencies pull in a legacy version of google-collections.
+                    This can cause runtime errors, for example during test or application execution.
+                    Gradle does not automatically resolve the google-collections VS guava conflict because it is not considered as a "version conflict".
+                    It's because the module coordinates for both libraries are completely different and conflict resolution is activated when
+                    "group" and "name" coordinates are the same but there are different versions available in the dependency graph
+                    (for more info, please refer to the section on conflict resolution).
+                    Traditional remedies to this problem are:
+                    <itemizedlist>
+                        <listitem>Declare exclusion rule to avoid pulling in "google-collections" to graph. It is probably the most popular approach.</listitem>
+                        <listitem>Avoid dependencies that pull in legacy libraries.</listitem>
+                        <listitem>Upgrade the dependency version if the new version no longer pulls in a legacy library.</listitem>
+                        <listitem>Downgrade to "google-collections". It's not recommended, just mentioned for completeness.</listitem>
+                    </itemizedlist>
+                    Traditional approaches work but they are not general enough.
+                    For example, an organisation wants to resolve the google-collections VS guava conflict resolution problem in all projects.
+                    Starting from Gradle 2.2 it is possible to declare that certain module was replaced by other.
+                    This enables organisations to include the information about module replacement in the corporate plugin suite and resolve the problem
+                    holistically for all Gradle-powered projects in the enterprise.
+                    <sample id="module_replacement_declaration" dir="userguide/artifacts/componentModuleMetadata" title="Declaring module replacement">
+                        <sourcefile file="build.gradle" snippet="module_replacement_declaration"/>
+                    </sample>
+                    For more examples and detailed API, please refer to the DSL reference for <apilink class="org.gradle.api.artifacts.dsl.ComponentMetadataHandler"/>.
+                </para>
+                <para>
+                    What happens when we declare that "google-collections" are replaced by "guava"?
+                    Gradle can use this information for conflict resolution.
+                    Gradle will consider every version of "guava" newer/better than any version of "google-collections".
+                    Also, Gradle will ensure that only guava jar is present in the classpath / resolved file list.
+                    Please note that if only "google-collections" appears in the dependency graph (e.g. no "guava")
+                    Gradle will not eagerly replace it with "guava".
+                    Module replacement is an information that Gradle uses for resolving conflicts. If there is no conflict
+                    (e.g. only "google-collections" or only "guava" in the graph) the replacement information is not used.
+                </para>
+                <para>
+                    Currently it is not possible to declare that certain modules is replaced by a set of modules.
+                    However, it is possible to declare that multiple modules are replaced by a single module.
+                </para>
+            </section>
+
         </section>
         <section id="ivy_dynamic_resolve_mode">
             <title>Enabling Ivy dynamic resolve mode</title>
@@ -1041,16 +1267,110 @@ someroot/[artifact]-[revision].[ext]
                 <literal>latest.integration</literal> will select the highest module version regardless of its status (because <literal>integration</literal> is the least mature status),
                 whereas <literal>latest.release</literal> will select the highest module version with status <literal>release</literal>. Here is what this looks like in code:
             </para>
-            <sample id="latestSelector" dir="userguide/artifacts/componentMetadata" title="'Latest' version selector">
+            <sample id="latestSelector" dir="componentMetadataRules" title="'Latest' version selector">
                 <sourcefile file="build.gradle" snippet="latest-selector"/>
-                <output args="-q listFish"/>
+                <output args="-q listConfigs"/>
             </sample>
             <para>
-                The next example demonstrates <literal>latest</literal> selectors based on a custom status scheme declared in a module metadata rule:
+                The next example demonstrates <literal>latest</literal> selectors based on a custom status scheme declared in a component metadata rule that applies to all modules:
             </para>
-            <sample id="customStatusScheme" dir="userguide/artifacts/componentMetadata" title="Custom status scheme">
+            <sample id="customStatusScheme" dir="componentMetadataRules" title="Custom status scheme">
                 <sourcefile file="build.gradle" snippet="custom-status-scheme"/>
-                <output args="-q listBirds"/>
+            </sample>
+            <para>
+                Component metadata rules can be applied to a specified module.  Modules must be specified in the form of "group:module".
+            </para>
+            <sample id="customStatusSchemeModule" dir="componentMetadataRules" title="Custom status scheme by module">
+                <sourcefile file="build.gradle" snippet="custom-status-scheme-module"/>
+            </sample>
+            <para>
+                Gradle can also create component metadata rules utilizing Ivy-specific metadata for modules resolved from an Ivy repository.
+                Values from the Ivy descriptor are made available via the <apilink class="org.gradle.api.artifacts.ivy.IvyModuleDescriptor"/> interface.
+            </para>
+            <sample id="ivyComponentMetadataRule" dir="componentMetadataRules" title="Ivy component metadata rule">
+                <sourcefile file="build.gradle" snippet="ivy-component-metadata-rule" />
+            </sample>
+            <para>
+                Note that any rule that declares specific arguments must <emphasis>always</emphasis> include a <apilink class="org.gradle.api.artifacts.ComponentMetadataDetails"/> argument
+                as the first argument. The second Ivy metadata argument is optional.
+            </para>
+            <para>
+                Component metadata rules can also be defined using a <emphasis>rule source</emphasis> object.  A rule source object
+                is any object that contains exactly one method that defines the rule action and is annotated with <literal>@Mutate</literal>.
+            </para>
+            <para>
+                This method:
+            </para>
+            <itemizedlist>
+                <listitem>must return void.</listitem>
+                <listitem>must have <apilink class="org.gradle.api.artifacts.ComponentMetadataDetails" /> as the first argument.</listitem>
+                <listitem>may have an additional parameter of type <apilink class="org.gradle.api.artifacts.ivy.IvyModuleDescriptor" />.</listitem>
+            </itemizedlist>
+            <sample id="ruleSourceComponentMetadataRule" dir="componentMetadataRules" title="Rule source component metadata rule">
+                <sourcefile file="build.gradle" snippet="rule-source-component-metadata-rule" />
+            </sample>
+        </section>
+        <section id="component_selection_rules">
+            <title>Component Selection Rules</title>
+            <para>
+                Component selection rules may influence which component instance should be selected when multiple versions are available that match a version selector.
+                Rules are applied against every available version and allow the version to be explicitly rejected by rule.
+                This allows Gradle to ignore any component instance that does not satisfy conditions set by the rule. Examples include:
+            </para>
+            <itemizedlist>
+                <listitem>For a dynamic version like '1.+' certain versions may be explicitly rejected from selection</listitem>
+                <listitem>For a static version like '1.4' an instance may be rejected based on extra component metadata such as the Ivy branch attribute, allowing an instance from a subsequent repository to be used.</listitem>
+            </itemizedlist>
+            <para>
+                Rules are configured via the <apilink class="org.gradle.api.artifacts.ComponentSelectionRules"/> object.  Each rule configured
+                will be called with a <apilink class="org.gradle.api.artifacts.ComponentSelection"/> object as an argument which contains information
+                about the candidate version being considered.
+                Calling <apilink class="org.gradle.api.artifacts.ComponentSelection" method="reject"/> causes the given candidate version to be explicitly rejected,
+                in which case the candidate will not be considered for the selector.
+            </para>
+            <para>
+                The following example shows a rule that disallows a particular version of a module but allows the dynamic version to
+                choose the next best candidate.
+            </para>
+            <sample id="componentSelectionRulesReject" dir="componentSelectionRules" title="Component selection rule">
+                <sourcefile file="build.gradle" snippet="reject-version-1.1" />
+            </sample>
+            <para>
+                Note that version selection is applied starting with the highest version first.  The version selected will be the first version
+                found that all component selection rules accept.  A version is considered accepted no rule explicitly rejects it.
+            </para>
+            <para>
+                Similarly, rules can be targeted at specific modules.  Modules must be specified in the form of "group:module".
+            </para>
+            <sample id="componentSelectionRulesTarget" dir="componentSelectionRules" title="Component selection rule with module target">
+                <sourcefile file="build.gradle" snippet="targeted-component-selection" />
+            </sample>
+            <para>
+                Component selection rules can also consider component metadata when selecting a version.  Possible metadata arguments that can be considered are
+                <apilink class="org.gradle.api.artifacts.ComponentMetadata"/> and <apilink class="org.gradle.api.artifacts.ivy.IvyModuleDescriptor"/>.
+            </para>
+            <sample id="componentSelectionRulesMetadata" dir="componentSelectionRules" title="Component selection rule with metadata">
+                <sourcefile file="build.gradle" snippet="component-selection-with-metadata" />
+            </sample>
+            <para>
+                Note that a <apilink class="org.gradle.api.artifacts.ComponentSelection"/> argument is <emphasis>always</emphasis> required as the first
+                parameter when declaring a component selection rule with additional Ivy metadata parameters, but the metadata parameters can be declared
+                in any order.
+            </para>
+            <para>
+                Lastly, component selection rules can also be defined using a <emphasis>rule source</emphasis> object.  A rule source object
+                is any object that contains exactly one method that defines the rule action and is annotated with <literal>@Mutate</literal>.
+            </para>
+            <para>
+                This method:
+            </para>
+            <itemizedlist>
+                <listitem>must return void.</listitem>
+                <listitem>must have <apilink class="org.gradle.api.artifacts.ComponentSelection" /> as the first argument.</listitem>
+                <listitem>may have additional parameters of type <apilink class="org.gradle.api.artifacts.ComponentMetadata" /> and/or <apilink class="org.gradle.api.artifacts.ivy.IvyModuleDescriptor" />.</listitem>
+            </itemizedlist>
+            <sample id="componentSelectionRulesRuleSource" dir="componentSelectionRules" title="Component selection rule using a rule source object">
+                <sourcefile file="build.gradle" snippet="api-component-selection" />
             </sample>
         </section>
     </section>
@@ -1075,7 +1395,7 @@ someroot/[artifact]-[revision].[ext]
         <para>Separating the storage of downloaded artifacts from the cache metadata permits us to do some very powerful things with our cache that would be difficult with a transparent,
             file-only cache layout.
         </para>
-        <para>The Gradle cache does not allow the local cache to hide problems and creating mysterious and difficult to debug behavior
+        <para>The Gradle cache does not allow the local cache to hide problems and create other mysterious and difficult to debug behavior
             that has been a challenge with many build tools. This new behavior is implemented in a bandwidth and storage efficient way.
             In doing so, Gradle enables reliable and reproducible enterprise builds.
         </para>
@@ -1108,7 +1428,7 @@ someroot/[artifact]-[revision].[ext]
                             linkend='sub:cache_artifact_reuse'/>, below).
                 </para>
                 <para>Dependency resolution will fail if the required artifacts are not available in any repository specified by the build,
-                    regardless whether the local cache has retrieved this artifact from a different repository.
+                    even if the local cache has a copy of this artifact which was retrieved from a different repository.
                     Repository independence allows builds to be isolated from each other in an advanced way that no build tool has done before.
                     This is a key feature to create builds that are reliable and reproducible in any environment.
                 </para>
@@ -1153,7 +1473,7 @@ someroot/[artifact]-[revision].[ext]
             <section id='sub:cache_refresh'>
                 <title>Refresh</title>
                 <para>At times, the Gradle Dependency Cache can be out of sync with the actual state of the configured repositories. Perhaps a repository was initially misconfigured,
-                    or perhaps a "non-changing" module was published incorrectly. To refresh all dependencies in the dependency cache, use the
+                    or perhaps a “non-changing” module was published incorrectly. To refresh all dependencies in the dependency cache, use the
                     <literal>--refresh-dependencies</literal> option on the command line.
                 </para>
                 <para>The <literal>--refresh-dependencies</literal> option tells Gradle to ignore all cached entries for resolved modules and artifacts.
@@ -1179,22 +1499,23 @@ someroot/[artifact]-[revision].[ext]
             <sample id="changing-module-cache-control" dir="userguide/artifacts/resolutionStrategy" title="Changing module cache control">
                 <sourcefile file="build.gradle" snippet="changing-module-cache-control"/>
             </sample>
-            <para>For more details, take a look at the API documentation for<apilink class="org.gradle.api.artifacts.ResolutionStrategy"/>.
+            <para>For more details, take a look at the API documentation for <apilink class="org.gradle.api.artifacts.ResolutionStrategy"/>.
             </para>
         </section>
     </section>
     <section id='sec:strategies_of_transitive_dependency_management'>
         <title>Strategies for transitive dependency management</title>
-        <para>Many projects rely on the <ulink url='http://repo1.maven.org/maven2'>Maven Central repository</ulink>. This is not without problems.
+        <para>Many projects rely on the <ulink url='https://repo1.maven.org/maven2'>Maven Central repository</ulink>. This is not without problems.
         </para>
         <itemizedlist>
             <listitem>
-                <para>The Maven Central repository can be down or has a very long response time.
+                <para>The Maven Central repository can be down or can be slow to respond.
                 </para>
             </listitem>
             <listitem>
-                <para>The POM files of many projects have wrong information (as one example, the POM of <literal>commons-httpclient-3.0</literal>
-                    declares JUnit as a runtime dependency).
+                <para>The POM files of many popular projects specify dependencies or other configuration that
+                are just plain wrong (for instance, the POM file of the “<literal>commons-httpclient-3.0</literal>”
+                module declares JUnit as a runtime dependency).
                 </para>
             </listitem>
             <listitem>
@@ -1211,59 +1532,64 @@ someroot/[artifact]-[revision].[ext]
                 </para>
             </listitem>
             <listitem>
-                <para>You want to deal properly with wrong metadata in a Maven Central POM file.
+                <para>You want to deal properly with invalid metadata in a Maven Central POM file.
                 </para>
             </listitem>
             <listitem>
-                <para>You don't want to expose people who want to build your project, to the downtimes or sometimes very long response times of Maven Central.
+                <para>You don't want to expose people to the downtimes or slow response of Maven Central, if they
+                just want to build your project.
                 </para>
             </listitem>
         </itemizedlist>
-        <para>It is not a big deal to set-up a custom repository.
+        <para>It is not a big deal to set-up a custom repository,
             <footnote>
                 <para>If you want to shield your project from the downtimes of Maven Central things get more complicated. You
                     probably want to set-up a repository proxy for this. In an enterprise environment this is rather
                     common. For an open source project it looks like overkill.
                 </para>
             </footnote>
-            But it can be tedious, to keep it up to date. For a new version, you have always to create the new XML
-            descriptor and the directories. And your custom repository is another infrastructure element which might
+            but it can be tedious to keep it up to date. For a new version, you always have to create the new XML
+            descriptor and the directories. Your custom repository is another infrastructure element which might
             have downtimes and needs to be updated. To enable historical builds, you need to keep all the past
-            libraries and you need a backup. It is another layer of indirection. Another source of information
-            you have to lookup. All this is not really a big deal but in its sum it has an impact. Repository Manager like
-            Artifactory or Nexus make this easier. But for example open source projects don't usually have a host for those products.
+            libraries, not to mention a backup of these. It is another layer of indirection. Another source of information
+            you have to lookup. All this is not really a big deal but in its sum it has an impact. Repository managers like
+            Artifactory or Nexus make this easier, but most open source projects don't usually have a host for those products.
             This is changing with new services like <ulink url='http://bintray.com'>Bintray</ulink> that let developers host and
             distribute their release binaries using a self-service repository platform. Bintray also supports sharing approved artifacts
             though the <ulink url='http://jcenter.bintray.com'>JCenter</ulink> public repository to provide a single resolution address for
-            all popular OSS java artifacts (see <xref linkend="sub:maven_jcenter"/>).
+            all popular OSS Java artifacts (see <xref linkend="sub:maven_jcenter"/>).
         </para>
-        <para>This is a reason why some projects prefer to store their libraries in their version control system. This
+        <para>This is a common reason why many projects prefer to store their libraries in their version control system. This
             approach is fully supported by Gradle. The libraries can be stored in a flat directory without any XML module
             descriptor files. Yet Gradle offers complete transitive dependency management. You can use either client module
             dependencies to express the dependency relations, or artifact dependencies in case a first level dependency has no
-            transitive dependencies. People can check out such a project from svn and have everything necessary to build it.
+            transitive dependencies. People can check out such a project from your source code control system and
+            have everything necessary to build it.
         </para>
         <para>If you are working with a distributed version control system like Git you probably don't want to
             use the version control system to store libraries as people check out the whole history. But even here the flexibility
-            of Gradle can make your life easier. For example you can use a shared flat directory without XML descriptors and
-            yet you can have full transitive dependency management as described above.
+            of Gradle can make your life easier. For example, you can use a shared flat directory without XML descriptors and
+            yet you can have full transitive dependency management, as described above.
         </para>
         <para>You could also have a mixed strategy. If your main concern is bad metadata in the POM file and maintaining custom XML descriptors,
-            <emphasis>Client Modules</emphasis> offer an alternative. But you can of course still use Maven2 repo and your custom repository as a repository for
-            <emphasis>jars only</emphasis> and still enjoy <emphasis>transitive</emphasis> dependency management. Or you can only provide client modules for POMs with bad metadata. For the
-            jars and the correct POMs you still use the remote repository.
+            then <emphasis>Client Modules</emphasis> offer an alternative. However, you can still use a Maven2 repo or your custom repository as a repository for
+            <emphasis>jars only</emphasis> and still enjoy <emphasis>transitive</emphasis> dependency management. Or
+            you can only provide client modules for POMs with bad metadata. For the jars and the correct POMs you
+            still use the remote repository.
         </para>
         <section id='sub:implicit_transitive_dependencies'>
             <title>Implicit transitive dependencies</title>
-            <para>There is another way to deal with transitive dependencies <emphasis>without</emphasis> XML descriptor files. You can do this with Gradle, but we don't recommend it.
-                We mention it for the sake of completeness and comparison with other build tools.
-            </para>
-            <para>The trick is to use only artifact dependencies and group them in lists. That way you have somehow
-                expressed, what are your first level dependencies and what are transitive dependencies (see <xref linkend="para:notation_collections"/>).
-                But the draw-back is, that for the Gradle dependency management all dependencies are considered first level dependencies. The
-                dependency reports don't show your real dependency graph and the <literal>compile</literal>
-                task uses all dependencies, not just the first level dependencies. All in all, your build is less
-                maintainable and reliable than it could be when using client modules. And you don't gain anything.
+            <para>There is another way to deal with transitive dependencies <emphasis>without</emphasis> XML
+            descriptor files. You can do this with Gradle, but we don't recommend it. We mention it for the sake of
+            completeness and comparison with other build tools.
+            </para>
+            <para>The trick is to use only artifact dependencies and group them in lists. This will directly express
+                your first level dependencies and your transitive dependencies (see <xref linkend="para:notation_collections"/>).
+                The problem with this is that Gradle dependency management will see this as specifying all
+                dependencies as first level dependencies. The dependency reports won't show your real dependency
+                graph and the <literal>compile</literal> task uses all dependencies, not just the first level
+                dependencies. All in all, your build is less maintainable and reliable than it could be when using
+                client modules, and you don't gain anything.
             </para>
         </section>
     </section>
diff --git a/subprojects/docs/src/docs/userguide/distributionPlugin.xml b/subprojects/docs/src/docs/userguide/distributionPlugin.xml
index 355db07..e4484b5 100644
--- a/subprojects/docs/src/docs/userguide/distributionPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/distributionPlugin.xml
@@ -1,18 +1,18 @@
 <!--
-  ~ 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.
-  -->
+~ 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.
+-->
 
 <chapter id='distribution_plugin'>
     <title>The Distribution Plugin</title>
@@ -24,27 +24,27 @@
 
     <para>
         The distribution plugin facilitates building archives that serve as distributions of the project.
-        Distribution archives typically contain then executable application and other supporting files, such as documentation.
+        Distribution archives typically contain the executable application and other supporting files, such as documentation.
 	</para>
 
     <section>
         <title>Usage</title>
-        <para>To use the distribution plugin, include in your build script:</para>
+        <para>To use the distribution plugin, include the following in your build script:</para>
         <sample id="useDistributionPlugin" dir="userguide/distribution" title="Using the distribution plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
         <para>
-            The plugin adds an extension named "<literal>distributions</literal>" of type <apilink class="org.gradle.api.distribution.DistributionContainer" /> to the project.
-            It also creates a single distribution in the distributions container extension named "<literal>main</literal>".
+            The plugin adds an extension named “<literal>distributions</literal>” of type <apilink class="org.gradle.api.distribution.DistributionContainer" /> to the project.
+            It also creates a single distribution in the distributions container extension named “<literal>main</literal>”.
             If your build only produces one distribution you only need to configure this distribution (or use the defaults).
         </para>
         <para>
-            You can run "<userinput>gradle distZip</userinput>" to package the main distribution as a ZIP, or "<userinput>gradle distTar</userinput>" to create
-            a GZip compressed TAR file.
-            The files will be created at "<literal><replaceable>$buildDir</replaceable>/distributions/<replaceable>$project.name</replaceable>-<replaceable>$project.version</replaceable>.<replaceable>«ext»</replaceable></literal>".
+            You can run “<userinput>gradle distZip</userinput>” to package the main distribution as a ZIP, or “<userinput>gradle distTar</userinput>” to create
+            a TAR file. To build both types of archives just run <userinput>gradle assembleDist</userinput>.
+            The files will be created at “<literal><replaceable>$buildDir</replaceable>/distributions/<replaceable>$project.name</replaceable>-<replaceable>$project.version</replaceable>.<replaceable>«ext»</replaceable></literal>”.
         </para>
         <para>
-            You can run "<userinput>gradle installDist</userinput>" to assembles the distribution content, uncompressed, into "<literal><replaceable>$buildDir</replaceable>/install/<replaceable>main</replaceable></literal>".
+            You can run “<userinput>gradle installDist</userinput>” to assemble the uncompressed distribution into “<literal><replaceable>$buildDir</replaceable>/install/<replaceable>main</replaceable></literal>”.
         </para>
     </section>
 
@@ -86,7 +86,21 @@
                     <apilink class="org.gradle.api.tasks.bundling.Tar"/>
                 </td>
                 <td>
-                    Creates a ZIP archive of the distribution contents
+                    Creates a TAR archive of the distribution contents
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>assembleDist</literal>
+                </td>
+                <td>
+                    <literal>-</literal>
+                </td>
+                <td>
+                    <apilink class="org.gradle.api.Task"/>
+                </td>
+                <td>
+                    Creates ZIP and TAR archives with the distribution contents
                 </td>
             </tr>
             <tr>
@@ -145,6 +159,20 @@
             </tr>
             <tr>
                 <td>
+                    <literal>assemble<replaceable>${distribution.name.capitalize()}</replaceable>Dist</literal>
+                </td>
+                <td>
+                    <literal>-</literal>
+                </td>
+                <td>
+                    <apilink class="org.gradle.api.Task"/>
+                </td>
+                <td>
+                    Assembles all distribution archives
+                </td>
+            </tr>
+            <tr>
+                <td>
                     <literal>install<replaceable>${distribution.name.capitalize()}</replaceable>Dist</literal>
                 </td>
                 <td>
@@ -166,32 +194,44 @@
             <itemizedlist>
                 <listitem>customDistZip</listitem>
                 <listitem>customDistTar</listitem>
+                <listitem>assembleCustomDist</listitem>
                 <listitem>installCustomDist</listitem>
             </itemizedlist>
         </para>
         <para>
-            Given that the project name is "<literal>myproject</literal>" and version "<literal>1.2</literal>", running "<userinput>gradle customDistZip</userinput>" will produce a
-            ZIP file named "<literal>myproject-custom-1.2.zip</literal>".
+            Given that the project name is “<literal>myproject</literal>” and version “<literal>1.2</literal>”, running “<userinput>gradle customDistZip</userinput>” will produce a
+            ZIP file named “<literal>myproject-custom-1.2.zip</literal>”.
         </para>
         <para>
-            Running "<userinput>gradle installCustomDist</userinput>" will install the distribution contents into "<literal><replaceable>$buildDir</replaceable>/install/custom</literal>".
+            Running “<userinput>gradle installCustomDist</userinput>” will install the distribution contents into “<literal><replaceable>$buildDir</replaceable>/install/custom</literal>”.
         </para>
     </section>
     <section>
       <title>Distribution contents</title>
         <para>
-            All of the files in the "<literal>src/<replaceable>$distribution.name</replaceable>/dist</literal>" directory will automatically be included in the distribution.
+            All of the files in the “<literal>src/<replaceable>$distribution.name</replaceable>/dist</literal>” directory will automatically be included in the distribution.
             You can add additional files by configuring the <apilink class="org.gradle.api.distribution.Distribution" /> object that is part of the container.
         </para>
         <sample id="configureDistribution" dir="userguide/distribution" title="Configuring the main distribution">
             <sourcefile file="build.gradle" snippet="configure-distribution"/>
         </sample>
         <para>
-            In the example above, the content of the "<literal>src/readme</literal>" directory will be included in the distribution
-            (along with the files in the "<literal>src/dist/main</literal>" directory which are added by default).
+            In the example above, the content of the “<literal>src/readme</literal>” directory will be included in the distribution
+            (along with the files in the “<literal>src/dist/main</literal>” directory which are added by default).
         </para>
         <para>
-            The "<literal>baseName</literal>" property has also been changed. This will cause the distribution archives to be created with a different name.
+            The “<literal>baseName</literal>” property has also been changed. This will cause the distribution archives to be created with a different name.
         </para>
     </section>
+    <section>
+        <title>Publishing distributions</title>
+        <para>
+            The distribution plugin adds the distribution archives as candidate for default publishing artifacts.
+            With the <literal>maven</literal> plugin applied the distribution zip file will be published when running uploadArchives
+            if no other default artifact is configured
+        </para>
+        <sample id="publishDistribution" dir="userguide/distribution" title="publish main distribution">
+            <sourcefile file="build.gradle" snippet="publish-distribution"/>
+        </sample>
+    </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/earPlugin.xml b/subprojects/docs/src/docs/userguide/earPlugin.xml
index a781e06..9c1fb13 100644
--- a/subprojects/docs/src/docs/userguide/earPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/earPlugin.xml
@@ -22,7 +22,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Ear plugin, include in your build script:</para>
+        <para>To use the Ear plugin, include the following in your build script:</para>
         <sample id="earWithWar" dir="ear/earWithWar" title="Using the Ear plugin">
             <sourcefile file="build.gradle" snippet="use-ear-plugin"/>
             <test args="assemble"/>
@@ -141,7 +141,7 @@
                     <literal>deploymentDescriptor</literal>
                 </td>
                 <td>
-                    <classname>org.gradle.plugins.ear.descriptor.DeploymentDescriptor</classname>
+                    <classname>org.gradle.plugins.</classname><classname>ear.descriptor.</classname><classname>DeploymentDescriptor</classname>
                 </td>
                 <td>
                     A deployment descriptor with sensible defaults named <literal>application.xml</literal>
@@ -165,8 +165,8 @@
         <para>The default behavior of the Ear task is to copy the content of <literal>src/main/application</literal>
             to the root of the archive. If your <literal>application</literal> directory doesn't contain a
             <literal>META-INF/application.xml</literal> deployment descriptor then one will be generated for you.
-		</para>
-        <para>Also have a look at <apilink class="org.gradle.plugins.ear.Ear"/>.</para>
+        </para>
+        <para>The <apilink class="org.gradle.plugins.ear.Ear"/> class in the API documentation has additional useful information.</para>
     </section>
     <section id='sec:customizing'>
         <title>Customizing</title>
@@ -177,15 +177,17 @@
             <test args="assemble"/>
         </sample>
         <para>You can also use customization options that the <apilink class="org.gradle.plugins.ear.Ear"/>
-        	task provides, such as <literal>from</literal> and <literal>metaInf</literal>.
+            task provides, such as <literal>from</literal> and <literal>metaInf</literal>.
         </para>
     </section>
     <section id='sec:using_custom_app_xml'>
         <title>Using custom descriptor file</title>
         <para>
-            Let's say you already have the <literal>application.xml</literal> and want to use it instead of configuring the <literal>ear.deploymentDescriptor</literal> section.
-            To accommodate that place the <literal>META-INF/application.xml</literal> in the right place inside your source folders (see the <literal>appDirName</literal> property).
-            The existing file contents will be used and the explicit configuration in the <literal>ear.deploymentDescriptor</literal> will be ignored.
+            You may already have appropriate settings in a <literal>application.xml</literal> file and want
+            to use that instead of configuring the <literal>ear.deploymentDescriptor</literal> section of the
+            build script. To accommodate that goal, place the <literal>META-INF/application.xml</literal> in
+            the right place inside your source folders (see the <literal>appDirName</literal> property). The
+            file contents will be used and the explicit configuration in the <literal>ear.deploymentDescriptor</literal> will be ignored.
         </para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/eclipsePlugin.xml b/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
index e0e9c59..3dfb503 100644
--- a/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
+++ b/subprojects/docs/src/docs/userguide/eclipsePlugin.xml
@@ -15,19 +15,19 @@
   -->
 
 <chapter id="eclipse_plugin">
-    <title>The Eclipse Plugin</title>
+    <title>The Eclipse Plugins</title>
     
-    <para>The Eclipse plugin generates files that are used by the <ulink url="http://eclipse.org">Eclipse IDE</ulink>, thus
+    <para>The Eclipse plugins generate files that are used by the <ulink url="http://eclipse.org">Eclipse IDE</ulink>, thus
         making it possible to import the project into Eclipse (<guimenuitem>File</guimenuitem> - <guimenuitem>Import...</guimenuitem> - <guimenuitem>Existing Projects into Workspace</guimenuitem>).
-        Both external dependencies (including associated source and javadoc files) and project dependencies are considered.</para>
+        Both external dependencies (including associated source and Javadoc files) and project dependencies are considered.</para>
 
-    <para>Since 1.0-milestone-4 WTP-generating code was refactored into a separate plugin called <literal>eclipse-wtp</literal>.
-        So if you are interested in WTP integration then only apply the <literal>eclipse-wtp</literal> plugin. Otherwise applying <literal>eclipse</literal> plugin is enough.
-        This change was requested by Eclipse users who take advantage of <literal>war</literal> or <literal>ear</literal> plugin
-        but they don't use Eclipse WTP. Internally, <literal>eclipse-wtp</literal> also applies the <literal>eclipse</literal> plugin so you don't need to apply both of those plugins.
+    <para>Since version 1.0-milestone-4 of Gradle, the WTP-generating code was refactored into a separate plugin called <literal>eclipse-wtp</literal>.
+        So if you are interested in WTP integration then only apply the <literal>eclipse-wtp</literal> plugin. Otherwise applying the <literal>eclipse</literal> plugin is enough.
+        This change was requested by Eclipse users who take advantage of the <literal>war</literal> or <literal>ear</literal> plugins,
+        but who don't use Eclipse WTP. Internally, the <literal>eclipse-wtp</literal> plugin also applies the <literal>eclipse</literal> plugin so you don't need to apply both of those plugins.
     </para>
 
-    <para>What exactly the Eclipse plugin generates depends on which other plugins are used:</para>
+    <para>What exactly the <literal>eclipse</literal> plugin generates depends on which other plugins are used:</para>
     <table>
         <title>Eclipse plugin behavior</title>
         <thead>
@@ -47,33 +47,45 @@
             <td><link linkend="scala_plugin">Scala</link></td><td>Adds Scala support to <filename>.project</filename> and <filename>.classpath</filename> files.</td>
         </tr>
         <tr>
-            <td><link linkend="war_plugin">War</link></td><td>Adds web application support to <filename>.project</filename> file.
-            Generates WTP settings files only if <literal>eclipse-wtp</literal> plugin was applied.</td>
+            <td><link linkend="war_plugin">War</link></td><td>Adds web application support to <filename>.project</filename> file.</td>
         </tr>
         <tr>
-            <td><link linkend="ear_plugin">Ear</link></td><td>Adds ear application support to <filename>.project</filename> file.
-            Generates WTP settings files only if <literal>eclipse-wtp</literal> plugin was applied.</td>
+            <td><link linkend="ear_plugin">Ear</link></td><td>Adds ear application support to <filename>.project</filename> file.</td>
         </tr>
     </table>
 
-    <para>The Eclipse plugin is open to customization and provides a standardized set of hooks
+    <para>However, the <literal>eclipse-wtp</literal> plugin <emphasis>always</emphasis> generates all WTP settings files and enhances the
+        <filename>.project</filename> file. If a <link linkend="java_plugin">Java</link> or <link linkend="war_plugin">War</link> is applied,
+        <filename>.classpath</filename> will be extended to get a proper packaging structure for this utility library or web
+        application project.
+    </para>
+
+    <para>Both Eclipse plugins are open to customization and provide a standardized set of hooks
         for adding and removing content from the generated files.
     </para>
 
     <section>
         <title>Usage</title>
-        <para>To use the Eclipse plugin, include this in your build script:</para>
+        <para>To use either the Eclipse or the Eclipse WTP plugin, include one of the lines in your build script:</para>
+
         <sample id="useEclipsePlugin" dir="eclipse" title="Using the Eclipse plugin">
-            <sourcefile file="build.gradle" snippet="use-plugin"/>
+            <sourcefile file="build.gradle" snippet="use-eclipse-plugin"/>
+        </sample>
+        <sample id="useEclipseWtpPlugin" dir="eclipse" title="Using the Eclipse WTP plugin">
+            <sourcefile file="build.gradle" snippet="use-eclipse-wtp-plugin"/>
         </sample>
-        <para>The Eclipse plugin adds a number of tasks to your projects. The main tasks that you will use
+
+        <para><emphasis>Note: </emphasis> Internally, the <literal>eclipse-wtp</literal> plugin also applies the
+            <literal>eclipse</literal> plugin so you don't need to apply both.</para>
+
+        <para>Both Eclipse plugins add a number of tasks to your projects. The main tasks that you will use
             are the <literal>eclipse</literal> and <literal>cleanEclipse</literal> tasks.
         </para>
     </section>
     <section>
         <title>Tasks</title>
 
-        <para>The Eclipse plugin adds the tasks shown below to a project.</para>
+        <para>The Eclipse plugins add the tasks shown below to a project.</para>
 
         <table id='eclipsetasks'>
             <title>Eclipse plugin - tasks</title>
@@ -89,8 +101,7 @@
                 <td>
                     <literal>eclipse</literal>
                 </td>
-                <td><literal>eclipseProject</literal>, <literal>eclipseClasspath</literal>, <literal>eclipseJdt</literal>,
-                    <literal>eclipseWtpComponent</literal>, <literal>cleanEclipseWtpFacet</literal></td>
+                <td>all Eclipse configuration file generation tasks</td>
                 <td><apilink class="org.gradle.api.Task"/></td>
                 <td>Generates all Eclipse configuration files</td>
             </tr>
@@ -98,10 +109,7 @@
                 <td>
                     <literal>cleanEclipse</literal>
                 </td>
-                <td>
-                    <literal>cleanEclipseProject</literal>, <literal>cleanEclipseClasspath</literal>, <literal>cleanEclipseJdt</literal>,
-                    <literal>cleanEclipseWtpComponent</literal>, <literal>cleanEclipseWtpFacet</literal>
-                </td>
+                <td>all Eclipse configuration file clean tasks</td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
                 <td>Removes all Eclipse configuration files</td>
             </tr>
@@ -137,56 +145,67 @@
             </tr>
             <tr>
                 <td>
-                    <literal>cleanEclipseWtpComponent</literal>
+                    <literal>eclipseProject</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Removes the <filename>.settings/org.eclipse.wst.common.component</filename> file.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseProject"/></td>
+                <td>Generates the <filename>.project</filename> file.</td>
             </tr>
             <tr>
                 <td>
-                    <literal>cleanEclipseWtpFacet</literal>
+                    <literal>eclipseClasspath</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td>
-                    <apilink class="org.gradle.api.tasks.Delete"/>
-                </td>
-                <td>Removes the <filename>.settings/org.eclipse.wst.common.component</filename> file.
-                </td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath"/></td>
+                <td>Generates the <filename>.classpath</filename> file.</td>
             </tr>
             <tr>
                 <td>
-                    <literal>eclipseProject</literal>
+                    <literal>eclipseJdt</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseProject"/></td>
-                <td>Generates the <filename>.project</filename> file.</td>
+                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseJdt"/></td>
+                <td>Generates the <filename>.settings/org.eclipse.jdt.core.prefs</filename> file.</td>
             </tr>
+        </table>
+        <table id='eclipsewtptasks'>
+            <title>Eclipse WTP plugin - additional tasks</title>
+            <thead>
+                <tr>
+                    <td>Task name</td>
+                    <td>Depends on</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
             <tr>
                 <td>
-                    <literal>eclipseClasspath</literal>
+                    <literal>cleanEclipseWtpComponent</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath"/></td>
-                <td>Generates the <filename>.classpath</filename> file.</td>
+                <td><apilink class="org.gradle.api.tasks.Delete"/></td>
+                <td>Removes the <filename>.settings/org.eclipse.wst.common.component</filename> file.</td>
             </tr>
             <tr>
                 <td>
-                    <literal>eclipseJdt</literal>
+                    <literal>cleanEclipseWtpFacet</literal>
                 </td>
                 <td>
                     <literal>-</literal>
                 </td>
-                <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseJdt"/></td>
-                <td>Generates the <filename>.settings/org.eclipse.jdt.core.prefs</filename> file.</td>
+                <td>
+                    <apilink class="org.gradle.api.tasks.Delete"/>
+                </td>
+                <td>Removes the <filename>.settings/org.eclipse.wst.common.project.facet.core.xml</filename> file.
+                </td>
             </tr>
             <tr>
                 <td>
@@ -196,7 +215,7 @@
                     <literal>-</literal>
                 </td>
                 <td><apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent"/></td>
-                <td>Generates the <filename>.settings/org.eclipse.wst.common.component</filename> file only if <literal>eclipse-wtp</literal> plugin was applied.</td>
+                <td>Generates the <filename>.settings/org.eclipse.wst.common.component</filename> file.</td>
             </tr>
             <tr>
                 <td>
@@ -208,7 +227,7 @@
                 <td>
                     <apilink class="org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet"/>
                 </td>
-                <td>Generates the <filename>.settings/org.eclipse.wst.common.project.facet.core.xml</filename> file only if <literal>eclipse-wtp</literal> plugin was applied.</td>
+                <td>Generates the <filename>.settings/org.eclipse.wst.common.project.facet.core.xml</filename> file.</td>
             </tr>
         </table>
     </section>
@@ -216,7 +235,7 @@
         <title>Configuration</title>
 
         <table id='eclipse-configuration'>
-            <title>Configuration of the Eclipse plugin</title>
+            <title>Configuration of the Eclipse plugins</title>
             <thead>
                 <tr>
                     <td>Model</td>
@@ -229,7 +248,7 @@
                     <apilink class="org.gradle.plugins.ide.eclipse.model.EclipseModel"/>
                 </td>
                 <td><literal>eclipse</literal></td>
-                <td>Top level element that enables configuration of the Eclipse plugin in a DSL-friendly fashion</td>
+                <td>Top level element that enables configuration of the Eclipse plugin in a DSL-friendly fashion.</td>
             </tr>
             <tr>
                 <td>
@@ -243,14 +262,14 @@
                     <apilink class="org.gradle.plugins.ide.eclipse.model.EclipseClasspath"/>
                 </td>
                 <td><literal>eclipse.classpath</literal></td>
-                <td>Allows configuring classpath information</td>
+                <td>Allows configuring classpath information.</td>
             </tr>
             <tr>
                 <td>
                     <apilink class="org.gradle.plugins.ide.eclipse.model.EclipseJdt"/>
                 </td>
                 <td><literal>eclipse.jdt</literal></td>
-                <td>Allows configuring jdt information (source/target java compatibility)</td>
+                <td>Allows configuring jdt information (source/target Java compatibility).</td>
             </tr>
             <tr>
                 <td>
@@ -271,30 +290,31 @@
     <section>
         <title>Customizing the generated files</title>
         <para>
-            The Eclipse plugin allows you to customize the generated metadata files. The plugin provides a DSL for configuring model objects
+            The Eclipse plugins allow you to customize the generated metadata files. The plugins provide a DSL for configuring model objects
             that model the Eclipse view of the project. These model objects are then merged with the existing Eclipse XML metadata to ultimately
             generate new metadata. The model objects provide lower level hooks for working with domain objects representing the file content 
             before and after merging with the model configuration. They also provide a very low level hook for working directly with the raw 
-            XML for adjustment before it is persisted, for fine tuning and configuration that the Eclipse plugin does not model.
+            XML for adjustment before it is persisted, for fine tuning and configuration that the Eclipse and Eclipse WTP plugins do not model.
         </para>
         <section>
             <title>Merging</title>
             <para>Sections of existing Eclipse files that are also the target of generated content will be amended or overwritten,
                  depending on the particular section. The remaining sections will be left as-is.</para>
-            <section id="sec:complete-overwrite">
-                <title>Disabling merging with a complete overwrite</title>
-                <para>To completely overwrite existing Eclipse files, execute a clean task together with its corresponding generation task,
-                    for example <userinput>gradle cleanEclipse eclipse</userinput> (in that order). If you want to make this
-                    the default behavior, add <code>tasks.eclipse.dependsOn(cleanEclipse)</code> to your build script. This makes it
+            <section id="sec:complete-rewrite">
+                <title>Disabling merging with a complete rewrite</title>
+                <para>To completely rewrite existing Eclipse files, execute a clean task together with its corresponding generation task,
+                    like “<userinput>gradle cleanEclipse eclipse</userinput>” (in that order). If you want to make this
+                    the default behavior, add “<code>tasks.eclipse.dependsOn(cleanEclipse)</code>” to your build script. This makes it
                     unnecessary to execute the clean task explicitly.
                 </para>
-                <para>Complete overwrite works equally well for individual files, for example by executing <userinput> gradle cleanEclipseClasspath eclipseClasspath</userinput>.
+                <para>This strategy can also be used for individual files that the plugins would generate. For instance,
+                    this can be done for the “<literal>.classpath</literal>” file with “<userinput>gradle cleanEclipseClasspath eclipseClasspath</userinput>”.
                 </para>
             </section>
         </section>
         <section>
             <title>Hooking into the generation lifecycle</title>
-            <para>The Eclipse plugin provides objects modeling the sections of the Eclipse files
+            <para>The Eclipse plugins provide objects modeling the sections of the Eclipse files
                 that are generated by Gradle. The generation lifecycle is as follows:
                 <orderedlist>
                     <listitem>The file is read; or a default version provided by Gradle is used if it does not exist</listitem>
@@ -314,6 +334,7 @@
                         <td><literal>beforeMerged { arg -> }</literal> argument type</td>
                         <td><literal>whenMerged { arg -> }</literal> argument type</td>
                         <td><literal>withXml { arg -> }</literal> argument type</td>
+                        <td><literal>withProperties { arg -> }</literal> argument type</td>
                     </tr>
                 </thead>
                 <tr>
@@ -321,30 +342,35 @@
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Project"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Project"/></td>
                     <td><apilink class="org.gradle.api.XmlProvider"/></td>
+                    <td>-</td>
                 </tr>
                 <tr>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.EclipseClasspath"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Classpath"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Classpath"/></td>
                     <td><apilink class="org.gradle.api.XmlProvider"/></td>
+                    <td>-</td>
                 </tr>
                 <tr>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.EclipseJdt"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Jdt"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.Jdt"/></td>
-                    <td/>
+                    <td>-</td>
+                    <td><ulink url="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html"><code>java.util.Properties</code></ulink></td>
                 </tr>
                 <tr>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpComponent"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpComponent"/></td>
                     <td><apilink class="org.gradle.api.XmlProvider"/></td>
+                    <td>-</td>
                 </tr>
                 <tr>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpFacet"/></td>
                     <td><apilink class="org.gradle.plugins.ide.eclipse.model.WtpFacet"/></td>
                     <td><apilink class="org.gradle.api.XmlProvider"/></td>
+                    <td>-</td>
                 </tr>
             </table>
             <section id="sec:partial-overwrite">
@@ -391,4 +417,4 @@
             </section>
         </section>
     </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/embedding.xml b/subprojects/docs/src/docs/userguide/embedding.xml
index 32f6591..1be543b 100644
--- a/subprojects/docs/src/docs/userguide/embedding.xml
+++ b/subprojects/docs/src/docs/userguide/embedding.xml
@@ -19,9 +19,9 @@
     <section id='sec:embedding_introduction'>
         <title>Introduction to the Tooling API</title>
         <para>The 1.0 milestone 3 release brought a new API called the tooling API,
-            which you can use for embedding Gradle. This API allows you to execute and monitor builds,
+            which you can use for embedding Gradle into your own custom software. This API allows you to execute and monitor builds,
             and to query Gradle about the details of a build.
-            The main audience for this API is IDE, CI server, other UI authors, or integration testing of your Gradle plugins.
+            The main audience for this API will be IDEs, CI servers, other UI authors, or integration testing of your Gradle plugins.
             However, it is open for anyone who needs to embed Gradle in their application.
         </para>
 
@@ -35,23 +35,23 @@
             <itemizedlist>
                 <listitem>You can query Gradle for the details of a build,
                     including the project hierarchy and the project dependencies,
-                    external dependencies (including source and javadoc jars),
+                    external dependencies (including source and Javadoc jars),
                     source directories and tasks of each project.
                 </listitem>
-                <listitem>You can execute a build, and listen to stdout and stderr logging and progress
+                <listitem>You can execute a build and listen to stdout and stderr logging and progress
                     (e.g. the stuff shown in the 'status bar' when you run on the command line).
                 </listitem>
                 <listitem>Tooling API can download and install the appropriate Gradle version, similar to the wrapper.
                     Bear in mind that the tooling API is wrapper aware so you should not need to configure a Gradle distribution directly.
                 </listitem>
                 <listitem>The implementation is lightweight, with only a small number of dependencies.
-                    It is also a well-behaved library, and makes no assumptions about your class loader structure or logging configuration.
+                    It is also a well-behaved library, and makes no assumptions about your classloader structure or logging configuration.
                     This makes the API easy to bundle in your application.
                 </listitem>
             </itemizedlist>
         </para>
 
-        <para>In future we may support other interesting features:
+        <para>In the future we may support other interesting features:
             <itemizedlist>
                 <listitem>Performance. The API gives us the opportunity to do lots of caching,
                     static analysis and preemptive work, to make things faster for the user.
@@ -68,19 +68,12 @@
                 </listitem>
             </itemizedlist>
         </para>
-
-        <para>The Tooling API is the official and recommended way to embed Gradle.
-            This means that the existing APIs, namely <apilink class="org.gradle.GradleLauncher"/>
-            and the open API (the UIFactory and friends),
-            are deprecated and will be removed in some future version of Gradle.
-            If you happen to use one of the above APIs, please consider changing your application to use the tooling API instead.
-        </para>
     </section>
 
     <section id='sec:embedding_daemon'>
         <title>Tooling API and the Gradle Build Daemon</title>
         <para>Please take a look at <xref linkend="gradle_daemon"/>.
-            The Tooling API uses the daemon all the time, e.g. you cannot officially use the Tooling API without the daemon.
+            The Tooling API uses the daemon all the time.  In fact, you cannot officially use the Tooling API without the daemon.
             This means that subsequent calls to the Tooling API, be it model building requests or task executing requests
             can be executed in the same long-living process. <xref linkend="gradle_daemon"/> contains more details about the daemon,
             specifically information on situations when new daemons are forked.
@@ -89,21 +82,16 @@
 
     <section id='sec:embedding_quickstart'>
         <title>Quickstart</title>
-        <para>Since the tooling API is an interface for a programmer most of the documentation lives in the Javadoc.
+        <para>As the tooling API is an interface for developers, the Javadoc is the main documentation for it.
             This is exactly our intention - we don't expect this chapter to grow very much.
             Instead we will add more code samples and improve the Javadoc documentation.
             The main entry point to the tooling API is the
             <apilink class="org.gradle.tooling.GradleConnector"/>.
-            You can navigate from there and find code samples and other instructions.
-            Pretty effective way of learning how to use the tooling API is checking out and running
-            the <emphasis>samples</emphasis> that live in <filename>$gradleHome/samples/toolingApi</filename>.
-        </para>
-        <para>
-            If you're embedding Gradle and you're looking for exact set of dependencies the tooling API Jar requires
-            please look at one of the samples in <filename>$gradleHome/samples/toolingApi</filename>.
-            The dependencies are declared in the Gradle build scripts.
-            You can also find the repository declarations where the Jars are obtained from.
+            You can navigate from there to find code samples and other instructions.
+            Another very important set of resources are the <emphasis>samples</emphasis> that live
+            in “<filename>$gradleHome/samples/toolingApi</filename>”.  These samples also specify all of the
+            required dependencies for the Tooling API, along with the suggested repositories to obtain the jars from.
         </para>
     </section>
 
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/featureLifecycle.xml b/subprojects/docs/src/docs/userguide/featureLifecycle.xml
index 291919b..0fa8e1b 100644
--- a/subprojects/docs/src/docs/userguide/featureLifecycle.xml
+++ b/subprojects/docs/src/docs/userguide/featureLifecycle.xml
@@ -17,13 +17,13 @@
 <appendix id="feature_lifecycle">
     <title>The Feature Lifecycle</title>
     <para>
-        Gradle is under constant development and improvement. New versions are also delivered on a regular and frequent basis
+        Gradle is under constant development and improvement. New versions are delivered on a regular and frequent basis
         (approximately every 6 weeks). Continuous improvement combined with frequent delivery allows new features to be made available
         to users early and for invaluable real world feedback to be incorporated into the development process. Getting
         new functionality into the hands of users regularly is a core value of the Gradle platform.
         At the same time, API and feature stability is taken very seriously and is also considered a core value of the Gradle platform.
         This is something that is engineered into the development process by design choices and automated testing, and is formalised by
-        the <xref linkend='backwards_compatibility'>Backwards Compatibility Policy</xref>.
+        <xref linkend='backwards_compatibility'>Backwards Compatibility Policy</xref>.
     </para>
     <para>
         The Gradle
@@ -107,10 +107,10 @@
             <para>
                 Deprecated features are clearly indicated to be so. In the source code, all methods/properties/classes that are deprecated are
                 annotated with
-                <literal>@java.lang.Deprecated</literal>
+                “<literal>@java.lang.Deprecated</literal>”
                 which is reflected in the DSL and API references.
                 In most cases, there is a replacement for the deprecated element, and this will be described in the documentation.
-                Using a deprecated feature will also result in runtime warning in Gradle's output.
+                Using a deprecated feature will also result in a runtime warning in Gradle's output.
             </para>
             <para>
                 Use of deprecated features should be avoided. The release notes for each release indicate any features that are being deprecated
@@ -121,11 +121,11 @@
     <section id="backwards_compatibility">
         <title>Backwards Compatibility Policy</title>
         <para>
-            Gradle provides backwards compatibility for across major versions (e.g. <literal>1.x</literal>, <literal>2.x</literal>
+            Gradle provides backwards compatibility across major versions (e.g. <literal>1.x</literal>, <literal>2.x</literal>,
             etc.).
             Once a public feature is introduced or promoted in a Gradle release it will remain indefinitely or until it is deprecated. Once
             deprecated, it may be removed in the next major release. Deprecated features may be supported across major
             releases, but this is not guaranteed.
         </para>
     </section>
-</appendix>
\ No newline at end of file
+</appendix>
diff --git a/subprojects/docs/src/docs/userguide/findBugsPlugin.xml b/subprojects/docs/src/docs/userguide/findBugsPlugin.xml
index eecac59..30d92bd 100644
--- a/subprojects/docs/src/docs/userguide/findBugsPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/findBugsPlugin.xml
@@ -6,7 +6,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the FindBugs plugin, include in your build script:</para>
+        <para>To use the FindBugs plugin, include the following in your build script:</para>
         <sample id="useFindBugsPlugin" dir="codeQuality" title="Using the FindBugs plugin">
             <sourcefile file="build.gradle" snippet="use-findbugs-plugin"/>
         </sample>
@@ -90,6 +90,6 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.quality.FindBugsExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.quality.FindBugsExtension"/> class in the API documentation.</para>
     </section>
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/gradleDaemon.xml b/subprojects/docs/src/docs/userguide/gradleDaemon.xml
index bd945f5..61aaded 100644
--- a/subprojects/docs/src/docs/userguide/gradleDaemon.xml
+++ b/subprojects/docs/src/docs/userguide/gradleDaemon.xml
@@ -15,120 +15,239 @@
   -->
 <chapter id='gradle_daemon'>
     <title>The Gradle Daemon</title>
-
-    <section id='enter_the_daemon'>
-        <title>Enter the daemon</title>
-        <para><emphasis>The Gradle daemon</emphasis> (sometimes referred as <emphasis>the build daemon</emphasis>)
-            aims to improve the startup and execution time of Gradle.
+    <section id='what_is_the_daemon'>
+        <title>What is the Gradle Daemon?</title>
+        <para>From Wikipedia…</para>
+        <blockquote>
+            A daemon is a computer program that runs as a background process,
+            rather than being under the direct control of an interactive user.
+        </blockquote>
+        <para>
+            The Gradle Daemon is a background process that does the heavy lifting of running builds, then stays alive between builds waiting for the next build.
+            This allows data and code that is likely to be required in the next build to be kept in memory, ready to go.
+            This <emphasis>dramatically</emphasis> improves the performance of subsequent builds.
+            Enabling the Gradle Daemon is an extremely cheap way to decrease build times.
         </para>
-
         <para>
-            We came up with several use cases where the daemon is very useful.
-            For some workflows, the user invokes Gradle many times to execute a small number of relatively quick tasks.
-            For example:
-            <itemizedlist>
-                <listitem>When using test driven development, where the unit tests are executed many times.</listitem>
-                <listitem>When developing a web application, where the application is assembled many times.</listitem>
-                <listitem>When discovering what a build can do, where <userinput>gradle tasks</userinput> is executed a number of times.</listitem>
-            </itemizedlist>
-            For above sorts of workflows, it is important that the startup cost of invoking Gradle is as small as possible.
+            <emphasis>It is strongly recommended that the Gradle Daemon be enabled on all developer machines</emphasis>.
+            It is recommend to <emphasis>not enable</emphasis> the Daemon for Continuous Integration and build server environments (see <xref linkend="when_should_i_not_use_the_gradle_daemon"/>).
         </para>
         <para>
-            In addition, user interfaces can provide some interesting features if the Gradle model can be built relatively quickly.
-            For example, the daemon might be useful for following scenarios:
+            Gradle manages the Daemon automatically.
+            If the build environment is configured to leverage the Daemon, Gradle will automatically start a Daemon process if none are available, or use any existing <emphasis>compatible</emphasis> daemons that are not currently busy.
+            If a Daemon process has not been used for 3 hours, it will terminate itself.
+            Once a development environment is configured to use the Daemon, it is typically transparent and able to be forgotten about.
+        </para>
+    </section>
+    <section>
+        <title>Management and configuration</title>
+        <section>
+            <title>How do I enable the Gradle Daemon?</title>
+            <para>
+                The <literal>--daemon</literal> and <literal>--no-daemon</literal> command line switches enable and disable usage of the Daemon for individual build invocations when using the Gradle command line interface.
+                Typically, it is more convenient to enable the Daemon for an environment (e.g. a user account) so that all builds use the Daemon without requiring to remember to supply the <literal>--daemon</literal> switch.
+            </para>
+            <para>
+                There are two recommended ways to enable the Daemon persistently for an environment:
+            </para>
             <itemizedlist>
-                <listitem>Content assistance in the IDE</listitem>
-                <listitem>Live visualisation of the build in a GUI</listitem>
-                <listitem>Tab completion in a CLI</listitem>
+            <listitem>
+                <para>Via environment variables - add the flag <literal>-Dorg.gradle.daemon=true</literal> to the <literal>GRADLE_OPTS</literal> environment variable</para>
+                <para>Via properties file - add <literal>org.gradle.daemon=true</literal> to the <literal>«GRADLE_USER_HOME»/gradle.properties</literal> file</para>
+            </listitem>
             </itemizedlist>
-        </para>
+            <note>
+                <para>
+                    Note, <literal>«GRADLE_USER_HOME»</literal> defaults to <literal>«USER_HOME»/.gradle</literal>, where <literal>«USER_HOME»</literal> is the home directory of the current user.
+                    This location can be configured via the <literal>-g</literal> and <literal>--gradle-user-home</literal> command line switches,
+                    as well as by the <literal>GRADLE_USER_HOME</literal> environment variable and <literal>org.gradle.user.home</literal> JVM system property.
+                </para>
+            </note>
+            <para>
+                Both approaches have the same effect.
+                Which one to use is up to personal preference.
+                Most Gradle users choose the second option and add the entry to the user <literal>gradle.properties</literal> file.
+            </para>
+            <para>
+                On Windows, this command will enable the Daemon for the current user:
+                <programlisting>
+                    (if not exist "%HOMEPATH%/.gradle" mkdir "%HOMEPATH%/.gradle") && (echo foo >> "%HOMEPATH%/.gradle/gradle.properties")
+                </programlisting>
+                On UNIX-like operating systems, the following Bash shell command will enable the Daemon for the current user:
+                <programlisting>
+                    touch ~/.gradle/gradle.properties && echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties
+                </programlisting>
+                Once the Daemon is enabled for a build environment in this way, all builds will implicitly use a Daemon.
+            </para>
+        </section>
+        <section>
+            <title>How do I disable the Gradle Daemon?</title>
+            <para>
+                The Gradle Daemon is not enabled by default.
+                However, once it is enabled it is sometimes desirable to disable for some projects or for some build invocations.
+            </para>
+            <para>
+                The <literal>--no-daemon</literal> command line switch can be used to force that a Daemon not be used for that build.
+                This is rarely used, but can sometimes be useful when debugging issues with certain builds or Gradle plugins.
+                This command line switch has the <emphasis>highest</emphasis> precedence when considering the build environment.
+            </para>
+        </section>
+        <section>
+            <title>How do I suppress the “please consider using the Gradle Daemon” message?</title>
+            <para>
+                Gradle may emit a warning at the end of the build suggesting that you use the Gradle Daemon.
+                To avoid this warning you can enable the Daemon via the methods above, or explicitly disable the Daemon.
+                You can explicitly disable the Daemon by using the <literal>--no-daemon</literal> command line switch as described above,
+                or use one of the methods for enabling the daemon mentioned above but using a value of <literal>false</literal> for the <literal>org.gradle.daemon</literal> property instead of <literal>true</literal>.
+            </para>
+            <para>
+                As it is not recommend to use the Daemon for Continuous Integration builds, Gradle will not emit the message if the <literal>CI</literal> environment variable is present.
+            </para>
+        </section>
+        <section>
+            <title>Why is there more than one Daemon process on my machine?</title>
+            <para>
+                There are several reasons why Gradle will create a new Daemon, instead of using one that is already running.
+                A new Daemon will be started if there are no idle, compatible, Daemons.
+            </para>
+            <para>
+                An <emphasis>idle</emphasis> Daemon is one that is not currently executing a build or doing other useful work.
+            </para>
+            <para>
+                A <emphasis>compatible</emphasis> Daemon is one that can (or can be made to) meet the requirements of the requested build environment.
+                The Java installation to use to run the build is an example of an aspect of the build environment.
+                Required JVM system properties for the build runtime is another example.
+            </para>
+            <para>
+                Some aspects of the requested build environment may not be able to be met by an already running Java process.
+                If the Daemon is running with a Java 7 runtime, but the requested environment calls for Java 8 then the Daemon is not compatible and another must be started.
+                Moreover, certain properties of a Java runtime cannot be changed once the JVM has started.
+                It is not possible to change the memory allocation (e.g. <literal>-Xmx1024m</literal>), default text encoding, default locale, etc of a running JVM.
+            </para>
+            <para>
+                The “requested build environment” is typically constructed implicitly from aspects of the build client’s (e.g. Gradle command line client, IDE etc.) environment and explicitly via command line switches and settings.
+                See <xref linkend="build_environment"/> for details on how to specify and control the build environment.
+            </para>
+            <para>
+                The following JVM system properties are effectively immutable.
+                If the requested build environment requires any of these properties, with a different value than a Daemon’s JVM has for this property, the Daemon is not compatible.
+            </para>
+            <itemizedlist>
+                <listitem>file.encoding</listitem>
+                <listitem>user.language</listitem>
+                <listitem>user.country</listitem>
+                <listitem>user.variant</listitem>
+                <listitem>com.sun.management.jmxremote</listitem>
+            </itemizedlist>
+            <para>
+                The following JVM attributes, controlled by startup arguments, are also effectively immutable.
+                The corresponding attributes of the requested build environment and the Daemon’s environment must match exactly in order for a Daemon to be compatible.
+            </para>
+            <itemizedlist>
+                <listitem>The maximum heap size (i.e. the -Xmx JVM argument)</listitem>
+                <listitem>The minimum heap size (i.e. the -Xms JVM argument)</listitem>
+                <listitem>The boot classpath (i.e. the -Xbootclasspath argument)</listitem>
+                <listitem>The “assertion” status (i.e. the -ea argument)</listitem>
+            </itemizedlist>
+            <para>
+                The required Gradle version is another aspect of the requested build environment.
+                Daemon processes are coupled to a specific Gradle runtime.
+                Working on multiple Gradle projects during a session that use different Gradle versions is a common reason for having more than one running Daemon process.
+            </para>
+        </section>
+        <section>
+            <title>How much memory does the Daemon use and can I give it more?</title>
+            <para>
+                If the requested build environment does not specify a maximum heap size, the Daemon will use up to 1GB of heap.
+                It will use your the JVM's default minimum heap size.
+                1GB is more than enough for most builds.
+                Larger builds with hundreds of subprojects, lots of configuration, and source code may require, or perform better, with more memory.
+            </para>
+            <para>
+                To increase the amount of memory the Daemon can use, specify the appropriate flags as part of the requested build environment.
+                Please see <xref linkend="build_environment"/> for details.
+            </para>
+        </section>
+        <section>
+            <title>How can I stop a Daemon?</title>
+            <para>
+                Daemon processes will automatically terminate themselves after 3 hours of inactivity.
+                If you wish to stop a Daemon process before this, you can either kill the process via your operating system or run the <literal>gradle --stop</literal> command.
+                The <literal>--stop</literal> switch causes Gradle to request that <emphasis>all</emphasis> running Daemon processes, <emphasis>of the same Gradle version used to run the command</emphasis>, terminate themselves.
+            </para>
+        </section>
+        <section>
+            <title>What can go wrong with Daemon?</title>
+            <para>
+                Considerable engineering effort has gone into making the Daemon robust, transparent and unobtrusive during day to day development.
+                However, Daemon processes can occasionally be corrupted or exhausted.
+                A Gradle build executes arbitrary code from multiple sources.
+                While Gradle itself is designed for and heavily tested with the Daemon, user build scripts and third party plugins can destabilize the Daemon process through defects such as memory leaks or global state corruption.
+            </para>
+            <para>
+                It is also possible to destabilize the Daemon (and build environment in general) by running builds that do not release resources correctly.
+                This is a particularly poignant problem when using Microsoft Windows as it is less forgiving of programs that fail to close files after reading or writing.
+            </para>
+            <para>
+                If it is suspected that the Daemon process has become unstable, it can simply be killed.
+                Recall that the <literal>--no-daemon</literal> switch can be specified for a build to prevent use of the Daemon.
+                This can be useful to diagnose whether or not the Daemon is actually the culprit of a problem.
+            </para>
+        </section>
+    </section>
+    <section id="when_should_i_not_use_the_gradle_daemon">
+        <title>When should I not use the Gradle Daemon?</title>
         <para>
-            In general, snappy behavior of the build tool is always handy.
-            If you try using the daemon for your local builds it's going to be hard
-            for you to go back to regular use of Gradle.
+            It is recommended that the Daemon is used in all developer environments.
+            It is recommend to <emphasis>not enable</emphasis> the Daemon for Continuous Integration and build server environments.
         </para>
         <para>
-            The Tooling API (see <xref linkend="embedding"/>)
-            uses the daemon all the time, e.g. you cannot officially use the Tooling API without the daemon.
-            This means that whenever you are using the STS Gradle plugin for Eclipse or the Gradle support in Intellij IDEA,
-            you're already using the Gradle Daemon.
-        </para>
-        <para>In future the daemon will offer more features:
-            <itemizedlist>
-                <listitem>Snappy up-to-date checks: use native file system change notifications (e.g. via jdk7 nio.2)
-                    to preemptively perform up-to-date analysis.</listitem>
-                <listitem>Even faster builds: preemptively evaluate projects, so that the model is ready
-                    when the user next invokes Gradle.</listitem>
-                <listitem>Did we mention faster builds? The daemon can potentially preemptively download dependencies
-                    or check for new versions of snapshot dependencies.</listitem>
-                <listitem>Utilize a pool of reusable processes available for compilation and testing.
-                    For example, both the Groovy and Scala compilers have a large startup cost.
-                    The build daemon could maintain a process with Groovy and/or Scala already loaded.</listitem>
-                <listitem>Preemptive execution of certain tasks, for example compilation. Quicker feedback.</listitem>
-                <listitem>Fast and accurate bash tab completion.</listitem>
-                <listitem>Periodically garbage collect the Gradle caches.</listitem>
-            </itemizedlist>
+            The Daemon enables faster builds, which is particularly important when a human is sitting in front of the build.
+            For CI builds, stability and predictability is of utmost importance.
+            Using a fresh runtime (i.e. process) for each build is more reliable as the runtime is <emphasis>completely</emphasis> isolated from previous builds.
         </para>
     </section>
-
-    <section id='reusing_daemons'>
-        <title>Reusing and expiration of daemons</title>
-        <para>The basic idea is that the gradle command forks a daemon process, which performs the actual build.
-            Subsequent invocations of the gradle command will reuse the daemon, avoiding the startup costs.
-            Sometimes we cannot use an existing daemon because it is busy or its java version or jvm arguments are different.
-            For exact details on when exactly new daemon process is forked read the dedicated section below.
-            The daemon process automatically expire after 3 hours of idle time.
+    <section>
+        <title>Tools & IDEs</title>
+        <para>
+            The Gradle Tooling API (see <xref linkend="embedding"/>), that is used by IDEs and other tools to integrate with Gradle, <emphasis>always</emphasis> use the Gradle Daemon to execute builds.
+            If you are executing Gradle builds from within you're IDE you are using the Gradle Daemon and do not need to enable it for your environment.
         </para>
         <para>
-            Here're all situations in which we fork a new daemon process:
-            <itemizedlist>
-                <listitem>If the daemon process is currently busy running some job, a brand new daemon process will be started.</listitem>
-                <listitem>We fork a separate daemon process per java home. So even if there is some idle daemon waiting
-                    for build requests but you happen to run build with a different java home then a brand new daemon will be forked.</listitem>
-                <listitem>We fork a separate daemon process if the jvm arguments for the build are sufficiently different.
-                    For example we will not fork a new daemon if a some system property has changed.
-                    However if -Xmx memory setting change or some fundamental immutable system property changes (e.g. file.encoding)
-                    then new daemon will be forked.
-                </listitem>
-                <listitem>At the moment daemon is coupled with particular version of Gradle.
-                    This means that even if some daemon is idle but you are running the build
-                    with a different version of Gradle, a new daemon will be started.
-                    This also has a consequence for the <literal>--stop</literal> command line instruction:
-                    You can only stop daemons that were started with the Gradle version you use when running <literal>--stop</literal>.
-                </listitem>
-            </itemizedlist>
-            We plan to improve the ways of managing / pooling the daemons in future.
+            However, unless you have explicitly enabled the Gradle Daemon for you environment your builds from the command line will not use the Gradle Daemon.
         </para>
     </section>
-
-    <section id='daemon_usage_and_troubleshooting'>
-        <title>Usage and troubleshooting</title>
-        <para>For command line usage take a look dedicated section in <xref linkend="gradle_command_line"/>.
-            If you are tired of using the same command line options again and again, take a look at
-            <xref linkend="sec:gradle_configuration_properties"/>.
-            The section contains information on how to configure certain behavior of the daemon
-            (including turning on the daemon by default) in a more 'persistent' way.
+    <section>
+        <title>How does the Gradle Daemon make builds faster?</title>
+        <para>
+            The Gradle Daemon is a <emphasis>long lived</emphasis> build process.
+            In between builds it waits idly for the next build.
+            This has the obvious benefit of only requiring Gradle to be loaded into memory once for multiple builds, as opposed to once for each build.
+            This in itself is a significant performance optimization, but that's not where it stops.
         </para>
         <para>
-            Some ways of troubleshooting the Gradle daemon:
-            <itemizedlist>
-                <listitem>If you have a problem with your build, try temporarily disabling the daemon
-                    (you can pass the command line switch <literal>--no-daemon</literal>).</listitem>
-                <listitem>Occasionally, you may want to stop the daemons either via the <literal>--stop</literal>
-                    command line option or in a more forceful way.</listitem>
-                <listitem>There is a daemon log file, which by default is located in the
-                    Gradle user home directory.</listitem>
-                <listitem>You may want to start the daemon in <literal>--foreground</literal>
-                    mode to observe how the build is executed.</listitem>
-            </itemizedlist>
+            A significant part of the story for modern JVM performance is runtime code optimization.
+            For example, HotSpot (the JVM implementation provided by Oracle and used as the basis of OpenJDK) applies optimization to code while it is running.
+            The optimization is progressive and not instantaneous.
+            That is, the code is progressively optimized during execution which means that subsequent builds can be faster purely due to this optimization process.
+            Experiments with HotSpot have shown that it takes somewhere between 5 and 10 builds for optimization to stabilize.
+            The difference in perceived build time between the first build and the 10th for a Daemon can be quite dramatic.
         </para>
-    </section>
-
-    <section id="sec:daemon_properties">
-        <title>Configuring the daemon</title>
-        <para>Some daemon settings, such as JVM arguments, memory settings or the Java home, can be configured.
-            Please find more information in <xref linkend="sec:gradle_configuration_properties"/>
+        <para>
+            The Daemon also allows more effective in memory caching across builds.
+            For example, the classes needed by the build (e.g. plugins, build scripts) can be held in memory between builds.
+            Similarly, Gradle can maintain in-memory caches of build data such as the hashes of task inputs and outputs, used for incremental building.
         </para>
+        <section>
+            <title>Potential future enhancements</title>
+            <para>
+                Currently, the Daemon makes builds faster by effectively supporting in memory caching and by the JVM optimizer making the code faster.
+                In future Gradle versions, the Daemon will become even smarter and perform work <emphasis>preemptively</emphasis>.
+                It could, for example, start downloading dependencies immediately after the build script has been edited under the assumption that the build is about to be run and the newly changed or added dependencies will be required.
+            </para>
+            <para>
+                There are many other ways in that the Gradle Daemon will enable even faster builds in future Gradle versions.
+            </para>
+        </section>
     </section>
-
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/gradleWrapper.xml b/subprojects/docs/src/docs/userguide/gradleWrapper.xml
index 3c1b519..1ac59dc 100644
--- a/subprojects/docs/src/docs/userguide/gradleWrapper.xml
+++ b/subprojects/docs/src/docs/userguide/gradleWrapper.xml
@@ -16,7 +16,7 @@
 <chapter id='gradle_wrapper'>
     <title>The Gradle Wrapper</title>
     <para>
-        The Gradle Wrapper (henceforth referred to as the “wrapper) is the preferred way of starting a Gradle build. The wrapper
+        The Gradle Wrapper (henceforth referred to as the “wrapper”) is the preferred way of starting a Gradle build. The wrapper
         is a batch script on Windows, and a shell script for other operating systems. When you start a Gradle build via the wrapper,
         Gradle will be automatically downloaded and used to run the build. 
     </para>
@@ -28,7 +28,17 @@
         build your project) as it requires no configuration on the server.
     </para>
     <para>
-        You install the wrapper into your project by adding and configuring a <apilink class="org.gradle.api.tasks.wrapper.Wrapper"/> 
+        You install the wrapper into your project by running the <literal>wrapper</literal> task. (This task is always available, even if
+        you don't add it to your build). To specify a Gradle version use <literal>--gradle-version</literal> on the command-line.
+        You can also set the URL to download Gradle from directly via <literal>--gradle-distribution-url</literal>. If no version or distribution
+        URL is specified, the wrapper will be configured to use the gradle version the wrapper task is executed with.
+        So if you run the wrapper task with Gradle 2.4 and the wrapper configuration will default to Gradle 2.4.
+    </para>
+    <sample id="wrapperCommandLine" dir="userguide/wrapper/simple" title="Running the wrapper task">
+        <output args="wrapper --gradle-version 2.0"/>
+    </sample>
+    <para>
+        The wrapper can be further customized by adding and configuring a <apilink class="org.gradle.api.tasks.wrapper.Wrapper"/>
         task in your build script, and then executing it.
     </para>
     <sample id="wrapperSimple" dir="userguide/wrapper/simple" title="Wrapper task">
@@ -52,27 +62,27 @@
         The <command>gradlew</command> command can be used <emphasis>exactly</emphasis> the same way as the <command>gradle</command> command.
     </para>
     <para>If you want to switch to a new version of Gradle you don't need to rerun the wrapper task. It is good enough
-        to change the respective entry in the <literal>gradle-wrapper.properties</literal> file. But if there is for
-        example an improvement in the gradle-wrapper functionality you need to regenerate the wrapper files.
+        to change the respective entry in the <literal>gradle-wrapper.properties</literal> file, but if you want to take
+        advantage of new functionality in the Gradle wrapper, then you would need to regenerate the wrapper files.
     </para>
     <section id='sec:configuration'>
         <title>Configuration</title>
         <para>If you run Gradle with <command>gradlew</command>, the wrapper checks if a Gradle distribution for the wrapper
-            is available. If not it tries to download it, otherwise it delegates to the <command>gradle</command>
+            is available. If so, it delegates to the <command>gradle</command>
             command of this distribution with all the arguments passed originally to the <command>gradlew</command>
-            command.
+            command.  If it didn't find a Gradle distribution, it will download it first.
         </para>
         <para>
             When you configure the <literal>Wrapper</literal> task, you can specify the Gradle version you wish to use. The <command>gradlew</command>
             command will download the appropriate distribution from the Gradle repository.
             Alternatively, you can specify the download URL of the Gradle distribution. The <command>gradlew</command> command will use this URL to download
             the distribution.
-            If you specify neither a Gradle version or download URL, the <command>gradlew</command> command will by default download whichever version
+            If you specified neither a Gradle version nor download URL, the <command>gradlew</command> command will download whichever version
             of Gradle was used to generate the wrapper files.
         </para>
-        <para>For the details on how to configure the wrapper, see <apilink class="org.gradle.api.tasks.wrapper.Wrapper"/>
+        <para>For the details on how to configure the wrapper, see the <apilink class="org.gradle.api.tasks.wrapper.Wrapper"/> class in the API documentation.
         </para>
-        <para>If you don't want any download to happen when your project is build via <command>gradlew</command>, simply add the Gradle
+        <para>If you don't want any download to happen when your project is built via <command>gradlew</command>, simply add the Gradle
             distribution zip to your version control at the location specified by your wrapper configuration.
             A relative URL is supported - you can specify a distribution file relative to the location of <literal>gradle-wrapper.properties</literal> file.
         </para>
@@ -81,9 +91,9 @@
     </section>
     <section id='sec:unix_file_permissions'>
         <title>Unix file permissions</title>
-        <para>The Wrapper task adds appropriate file permissions to allow the execution for the gradlew *NIX command.
+        <para>The Wrapper task adds appropriate file permissions to allow the execution of the <literal>gradlew</literal> *NIX command.
             Subversion preserves this file permission. We are not sure how other version control systems deal with this.
-            What should always work is to execute <literal>sh gradlew</literal>.
+            What should always work is to execute “<literal>sh gradlew</literal>”.
         </para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/groovyPlugin.xml b/subprojects/docs/src/docs/userguide/groovyPlugin.xml
index 8754dab..12929dd 100644
--- a/subprojects/docs/src/docs/userguide/groovyPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/groovyPlugin.xml
@@ -17,14 +17,14 @@
     <title>The Groovy Plugin</title>
     <para>The Groovy plugin extends the Java plugin to add support for Groovy projects. It can deal with
         Groovy code, mixed Groovy and Java code, and even pure Java code (although we don't necessarily recommend to use it for the latter).
-        The plugin supports <emphasis>joint compilation</emphasis>, which allows to freely mix and match Groovy and Java code,
+        The plugin supports <emphasis>joint compilation</emphasis>, which allows you to freely mix and match Groovy and Java code,
         with dependencies in both directions. For example, a Groovy class can extend a Java class that in turn extends a Groovy class.
         This makes it possible to use the best language for the job, and to rewrite any class in the other language if needed.
     </para>
 
     <section>
         <title>Usage</title>
-        <para>To use the Groovy plugin, include in your build script:</para>
+        <para>To use the Groovy plugin, include the following in your build script:</para>
         <sample id="useGroovyPlugin" dir="groovy/quickstart" title="Using the Groovy plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -143,7 +143,7 @@
 
         <section>
             <title>Changing the project layout</title>
-            <para>Just like the Java plugin, the Groovy plugin allows to configure custom locations for Groovy production and test sources.</para>
+            <para>Just like the Java plugin, the Groovy plugin allows you to configure custom locations for Groovy production and test sources.</para>
             <sample id="customGroovySourceLayout" dir="groovy/customizedLayout" title="Custom Groovy source layout">
                 <sourcefile file="build.gradle" snippet="custom-source-locations"/>
             </sample>
@@ -154,7 +154,7 @@
     <section>
         <title>Dependency management</title>
         <para>Because Gradle's build language is based on Groovy, and parts of Gradle are implemented in Groovy, Gradle already ships
-            with a Groovy library (1.8.6 as of Gradle 1.6). Nevertheless, Groovy projects need to explicitly declare a Groovy dependency.
+            with a Groovy library (2.3.3 as of Gradle 2.0). Nevertheless, Groovy projects need to explicitly declare a Groovy dependency.
             This dependency will then be used on compile and runtime class paths. It will also be used to get hold of the Groovy compiler
             and Groovydoc tool, respectively.
         </para>
@@ -185,13 +185,14 @@
         <sample id="groovyFileDependency" dir="userguide/tutorial/groovyWithFlatDir" title="Configuration of Groovy file dependency">
             <sourcefile file="build.gradle" snippet="groovy-dependency"/>
         </sample>
-
+        <para>The “<literal>module</literal>” reference may be new to you.  See <xref linkend="dependency_management"/> for more information about this and other
+        information about dependency management.</para>
     </section>
 
     <section>
         <title>Automatic configuration of groovyClasspath</title>
         <para>
-            <literal>GroovyCompile</literal> and <literal>Groovydoc</literal> tasks consume Groovy in two ways: on their <literal>classpath</literal>,
+            The <literal>GroovyCompile</literal> and <literal>Groovydoc</literal> tasks consume Groovy code in two ways: on their <literal>classpath</literal>,
             and on their <literal>groovyClasspath</literal>. The former is used to locate classes referenced by the source code, and will typically
             contain the Groovy library along with other libraries. The latter is used to load and execute the Groovy compiler and Groovydoc tool,
             respectively, and should only contain the Groovy library and its dependencies.
@@ -201,11 +202,11 @@
             from the task's <literal>classpath</literal>. This is done as follows:
             <itemizedlist>
                 <listitem>
-                    If a <literal>groovy-all(-indy)</literal> Jar is found on <literal>classpath</literal>, the same Jar will be added to
+                    If a <literal>groovy-all(-indy)</literal> Jar is found on <literal>classpath</literal>, that jar will be added to
                     <literal>groovyClasspath</literal>.
                 </listitem>
                 <listitem>
-                    If a <literal>groovy(-indy)</literal> Jar is found on <literal>classpath</literal>, and the project has at least one repository declared,
+                    If a <literal>groovy(-indy)</literal> jar is found on <literal>classpath</literal>, and the project has at least one repository declared,
                     a corresponding <literal>groovy(-indy)</literal> repository dependency will be added to <literal>groovyClasspath</literal>.
                 </listitem>
                 <listitem>
@@ -213,6 +214,7 @@
                 </listitem>
             </itemizedlist>
         </para>
+        <para>Note that the “<literal>-indy</literal>” variation of each jar refers to the version with <literal>invokedynamic</literal> support.</para>
     </section>
 
     <section>
@@ -312,8 +314,7 @@
         <title>GroovyCompile</title>
         <para>The Groovy plugin adds a <apilink class="org.gradle.api.tasks.compile.GroovyCompile"/> task for
             each source set in the project. The task type extends the <literal>JavaCompile</literal>
-            task (see <xref linkend='sec:compile'/>). Unless <literal>groovyOptions.useAnt</literal> is set to <literal>true</literal>,
-            Gradle's native Groovy compiler integration is used. For most projects, this is the better choice than the Ant-based compiler.
+            task (see <xref linkend='sec:compile'/>).
             The <literal>GroovyCompile</literal> task supports most configuration options of the official Groovy compiler.
         </para>
         <table>
diff --git a/subprojects/docs/src/docs/userguide/groovyTutorial.xml b/subprojects/docs/src/docs/userguide/groovyTutorial.xml
index 9ad21f1..87e2ba6 100644
--- a/subprojects/docs/src/docs/userguide/groovyTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/groovyTutorial.xml
@@ -33,14 +33,14 @@
             extends the <literal>compile</literal> task to look for source files in directory
             <filename>src/main/groovy</filename>, and the <literal>compileTest</literal> task to look for test source
             files in directory <filename>src/test/groovy</filename>. The compile tasks use joint compilation for these
-            directories, which means they can contain a mixture of java and groovy source files.
+            directories, which means they can contain a mixture of Java and Groovy source files.
         </para>
-        <para>To use the groovy compilation tasks, you must also declare the Groovy version to use and where to find the
+        <para>To use the Groovy compilation tasks, you must also declare the Groovy version to use and where to find the
             Groovy libraries. You do this by adding a dependency to the <literal>groovy</literal> configuration.
-            The <literal>compile</literal> configuration inherits this dependency, so the groovy libraries will
+            The <literal>compile</literal> configuration inherits this dependency, so the Groovy libraries will
             be included in classpath when compiling Groovy and Java source.  For our sample, we will use Groovy 2.2.0
             from the public Maven repository:</para>
-        <sample id="groovyQuickstart" dir="groovy/quickstart" title="Dependency on Groovy 2.2.0">
+        <sample id="groovyQuickstart" dir="groovy/quickstart" title="Dependency on Groovy">
             <sourcefile file="build.gradle" snippet="groovy-dependency"/>
         </sample>
         <para>Here is our complete build file:</para>
diff --git a/subprojects/docs/src/docs/userguide/guiTutorial.xml b/subprojects/docs/src/docs/userguide/guiTutorial.xml
index c906d9c..60d838c 100644
--- a/subprojects/docs/src/docs/userguide/guiTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/guiTutorial.xml
@@ -62,22 +62,22 @@ gradle --gui
     <section>
         <title>Favorites</title>
         <para>
-            The Favorites tab is place to store commonly-executed commands. These can be complex commands
+            The Favorites tab is a good place to store commonly-executed commands. These can be complex commands
             (anything that's legal to Gradle) and you can provide them with a display name. This is useful for creating,
             say, a custom build command that explicitly skips tests, documentation, and samples that you could call
-            "fast build".
+            “fast build”.
         </para>
         <para>
             You can reorder favorites to your liking and even export them to disk so they can imported by others.
-            If you edit them, you are given options to "Always Show Live Output."
-            This only applies if you have 'Only Show Output When Errors Occur'.
+            If you edit them, you are given options to “Always Show Live Output”.
+            This only applies if you have “Only Show Output When Errors Occur”.
             This override always forces the output to be shown.
         </para>
     </section>
     <section>
         <title>Command Line</title>
         <para>
-            The Command Line tab is place to execute a single Gradle command directly.
+            The Command Line tab is where you can execute a single Gradle command directly.
             Just enter whatever you would normally enter after 'gradle' on the command line.  This also provides
             a place to try out commands before adding them to favorites.
         </para>
@@ -101,8 +101,8 @@ gradle --gui
             <listitem>
                 <para>Stack Trace Output</para>
                 <para>
-                    This determines how much information to write out stack traces when errors occur.
-                    Note: if you specify a stack trace level on either  the Command Line or Favorites tab, it will override
+                    This determines how much information to write out in stack traces when errors occur.
+                    Note: if you specify a stack trace level on either the Command Line or Favorites tab, it will override
                     this stack trace level.
                 </para>
             </listitem>
@@ -123,4 +123,4 @@ gradle --gui
         </itemizedlist>
 
     </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/ideaPlugin.xml b/subprojects/docs/src/docs/userguide/ideaPlugin.xml
index 9948f06..dbbc54c 100644
--- a/subprojects/docs/src/docs/userguide/ideaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/ideaPlugin.xml
@@ -19,7 +19,7 @@
 
     <para>The IDEA plugin generates files that are used by <ulink url="http://www.jetbrains.com/idea/">IntelliJ IDEA</ulink>, thus
         making it possible to open the project from IDEA (<guimenuitem>File</guimenuitem> - <guimenuitem>Open Project</guimenuitem>).
-        Both external dependencies (including associated source and javadoc files) and project dependencies are considered.</para>
+        Both external dependencies (including associated source and Javadoc files) and project dependencies are considered.</para>
 
     <para>What exactly the IDEA plugin generates depends on which other plugins are used:</para>
     <table>
@@ -59,8 +59,8 @@
         <title>Tasks</title>
 
         <para>The IDEA plugin adds the tasks shown below to a project.
-            Notice that <literal>clean</literal> does not depend on <literal>cleanIdeaWorkspace</literal>.
-            It's because workspace contains a lot of user specific temporary data and typically it is not desirable to manipulate it outside IDEA.
+            Notice that the <literal>clean</literal> task does not depend on the <literal>cleanIdeaWorkspace</literal> task.
+            This is because the workspace typically contains a lot of user specific temporary data and it is not desirable to manipulate it outside IDEA.
         </para>
 
         <table id='ideatasks'>
@@ -199,7 +199,7 @@
 
     <section>
         <title>Customizing the generated files</title>
-        <para>IDEA plugin provides hooks and behavior for customizing the generated content.
+        <para>The IDEA plugin provides hooks and behavior for customizing the generated content.
             The workspace file can effectively only be manipulated via the <code>withXml</code> hook
             because its corresponding domain object is essentially empty.</para>
         <para>The tasks recognize existing IDEA files, and merge them with the generated content.</para>
@@ -209,12 +209,14 @@
                 depending on the particular section. The remaining sections will be left as-is.</para>
             <section id="sec:complete-overwrite">
                 <title>Disabling merging with a complete overwrite</title>
-                <para>To completely overwrite existing IDEA files, execute a clean task together with its corresponding generation task,
-                    for example <userinput>gradle cleanIdea idea</userinput> (in that order). If you want to make this
-                    the default behavior, add <code>tasks.idea.dependsOn(cleanIdea)</code> to your build script. This makes it
+                <para>To completely rewrite existing IDEA files, execute a clean task together with its corresponding generation task,
+                    like “<userinput>gradle cleanIdea idea</userinput>” (in that order). If you want to make this
+                    the default behavior, add “<code>tasks.idea.dependsOn(cleanIdea)</code>” to your build script. This makes it
                     unnecessary to execute the clean task explicitly.
                 </para>
-                <para>Complete overwrite works equally well for individual files, for example by executing <userinput> gradle cleanIdeaModule ideaModule</userinput>.</para>
+                <para>This strategy can also be used for individual files that the plugin would generate.  For instance,
+                this can be done for the “<literal>.iml</literal>” file with “<userinput>gradle cleanIdeaModule ideaModule</userinput>”.
+                </para>
             </section>
         </section>
         <section>
@@ -260,29 +262,29 @@
                     <td><apilink class="org.gradle.api.XmlProvider"/></td>
                 </tr>
             </table>
-            <section id="sec:partial-overwrite">
-                <title>Partial overwrite of existing content</title>
-                <para>A <link linkend="sec:complete-overwrite">complete overwrite</link> causes all existing content to be discarded,
+            <section id="sec:partial-rewrite">
+                <title>Partial rewrite of existing content</title>
+                <para>A <link linkend="sec:complete-rewrite">complete rewrite</link> causes all existing content to be discarded,
                     thereby losing any changes made directly in the IDE. The <code>beforeMerged</code> hook makes it possible
                     to overwrite just certain parts of the existing content. The following example removes all existing dependencies
                     from the <literal>Module</literal> domain object:
-                    <sample id="partialOverwrites" dir="idea"
-                            title="Partial Overwrite for Module">
+                    <sample id="partialRewrites" dir="idea"
+                            title="Partial Rewrite for Module">
                         <sourcefile file="build.gradle" snippet="module-before-merged"/>
                     </sample>
                     The resulting module file will only contain Gradle-generated dependency entries, but
                     not any other dependency entries that may have been present in the original file. (In the case of dependency entries,
                     this is also the default behavior.) Other sections of the module file will be either left as-is or merged.
                     The same could be done for the module paths in the project file:
-                    <sample id="partialOverwritesProject" dir="idea"
-                            title="Partial Overwrite for Project">
+                    <sample id="partialRewritesProject" dir="idea"
+                            title="Partial Rewrite for Project">
                         <sourcefile file="build.gradle" snippet="project-before-merged"/>
                     </sample>
                 </para>
             </section>
             <section>
                 <title>Modifying the fully populated domain objects</title>
-                <para>The <code>whenMerged</code> hook allows to manipulate the fully populated domain objects. Often this is the
+                <para>The <code>whenMerged</code> hook allows you to manipulate the fully populated domain objects. Often this is the
                     preferred way to customize IDEA files. Here is how you would export all the dependencies of an IDEA module:
                     <sample id="exportDependencies" dir="idea"
                             title="Export Dependencies">
@@ -293,7 +295,7 @@
             </section>
             <section>
                 <title>Modifying the XML representation</title>
-                <para>The <code>withXml</code>hook allows to manipulate the in-memory XML representation just before the file gets written to disk.
+                <para>The <code>withXml</code>hook allows you to manipulate the in-memory XML representation just before the file gets written to disk.
                     Although Groovy's XML support makes up for a lot, this approach is less convenient than manipulating the domain objects.
                     In return, you get total control over the generated file, including sections not modeled by the domain objects.
                     <sample id="projectWithXml" dir="idea"
@@ -307,9 +309,9 @@
     </section>
     <section>
         <title>Further things to consider</title>
-        <para>The paths of the dependencies in the generated IDEA files are absolute. If you manually define a path variable
+        <para>The paths of dependencies in the generated IDEA files are absolute. If you manually define a path variable
             pointing to the Gradle dependency cache, IDEA will automatically replace the absolute dependency paths with
-            this path variable. If you use such a path variable, you need to configure this path variable via <literal>idea.pathVariables</literal>,
+            this path variable. you can configure this path variable via the “<literal>idea.pathVariables</literal>” property,
             so that it can do a proper merge without creating duplicates.</para>
     </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.graphml
deleted file mode 100644
index 69309ff..0000000
--- a/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.graphml
+++ /dev/null
@@ -1,135 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="5" parse.nodes="6" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="150.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="75.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="35.34765625" x="52.326171875" y="6.296875">check</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="100.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="86.03125" x="26.984375" y="6.296875">checkstyleMain</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="150.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="83.359375" x="28.3203125" y="6.296875">checkstyleTest</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="79.375" x="30.3125" y="6.296875">codenarcMain</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="50.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="76.703125" x="31.6484375" y="6.296875">codenarcTest</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n0" target="n3">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n2" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="3.75"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n3" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="11.25"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n5" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-3.75"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n4" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-11.25"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.png b/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.png
deleted file mode 100644
index 3914893..0000000
Binary files a/subprojects/docs/src/docs/userguide/img/codeQualityPluginTasks.png and /dev/null differ
diff --git a/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.graphml b/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.graphml
index b70c1b1..9eed89c 100644
--- a/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.graphml
@@ -1,123 +1,122 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="5" parse.nodes="4" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="12.5"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="68.6875" x="35.65625" y="6.296875">compileTest</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="25.535714285714285"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.341796875" x="58.3291015625" y="6.296875">test</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="7.035714285714285"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="22.673828125" x="58.6630859375" y="6.296875">dist</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="12.5"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="45.349609375" x="47.3251953125" y="6.296875">compile</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n0" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:PolyLineEdge>
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n1" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:PolyLineEdge>
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n3" target="n0">
-      <data key="d2"/>
-      <data key="d3">
-        <y:PolyLineEdge>
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n3" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:PolyLineEdge>
-          <y:Path sx="70.0" sy="10.0" tx="-70.0" ty="7.5">
-            <y:Point x="180.0" y="53.0"/>
-            <y:Point x="320.0" y="53.0"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n3" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:PolyLineEdge>
-          <y:Path sx="70.0" sy="-10.0" tx="-70.0" ty="-7.5">
-            <y:Point x="180.0" y="0.0"/>
-            <y:Point x="500.0" y="0.0"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-          <y:EdgeLabel alignment="center" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="4.0" modelName="free" modelPosition="anywhere" preferredPlacement="on_edge" ratio="0.5" textColor="#000000" visible="true" width="4.0" x="288.0" y="-19.5"></y:EdgeLabel>
-          <y:BendStyle smoothed="false"/>
-        </y:PolyLineEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="12.5"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="68.6875" x="35.65625" y="6.296875">compileTest</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="25.535714285714285"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.341796875" x="58.3291015625" y="6.296875">test</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="7.035714285714285"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="22.673828125" x="58.6630859375" y="6.296875">dist</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="12.5"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="45.349609375" x="47.3251953125" y="6.296875">compile</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n0" target="n1">
+      <data key="d9">
+        <y:PolyLineEdge>
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n1" target="n2">
+      <data key="d9">
+        <y:PolyLineEdge>
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n3" target="n0">
+      <data key="d9">
+        <y:PolyLineEdge>
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="e3" source="n3" target="n1">
+      <data key="d9">
+        <y:PolyLineEdge>
+          <y:Path sx="70.0" sy="10.0" tx="-70.0" ty="7.5">
+            <y:Point x="180.0" y="53.0"/>
+            <y:Point x="320.0" y="53.0"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+    <edge id="e4" source="n3" target="n2">
+      <data key="d9">
+        <y:PolyLineEdge>
+          <y:Path sx="70.0" sy="-10.0" tx="-70.0" ty="-7.5">
+            <y:Point x="180.0" y="0.0"/>
+            <y:Point x="500.0" y="0.0"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+          <y:EdgeLabel alignment="center" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="free" modelPosition="anywhere" preferredPlacement="on_edge" ratio="0.5" textColor="#000000" visible="true" width="4.0" x="288.0" y="-19.5">
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="on_edge" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+          <y:BendStyle smoothed="false"/>
+        </y:PolyLineEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.png b/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.png
index a957e99..c1013ef 100644
Binary files a/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.png and b/subprojects/docs/src/docs/userguide/img/commandLineTutorialTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.graphml
index 7ef1103..439d8e9 100644
--- a/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.graphml
@@ -1,226 +1,213 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="10" parse.nodes="9" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="176.11363636363637"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="59.365234375" x="40.3173828125" y="6.296875">groovydoc</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="101.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="84.02734375" x="27.986328125" y="6.296875">compileGroovy</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.99999999999999"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="110.03125" x="14.984375" y="6.296875">CompileTestGroovy</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="51.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="900.0" y="45.86363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="720.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n6">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n7">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="56.68181818181817"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n8">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="68.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n3" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n5" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-10.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n6" target="n8">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n7" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-10.0">
-            <y:Point x="180.0" y="40.61363636363636"/>
-            <y:Point x="320.0" y="40.61363636363636"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n8" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="10.0">
-            <y:Point x="860.0" y="90.5"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e5" source="n1" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="10.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e6" source="n7" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e7" source="n2" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e8" source="n8" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e9" source="n6" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="176.11363636363637"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="59.365234375" x="40.3173828125" y="6.296875">groovydoc</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="101.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="84.02734375" x="27.986328125" y="6.296875">compileGroovy</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.99999999999999"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="110.03125" x="14.984375" y="6.296875000000007">CompileTestGroovy</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="51.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n4">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="900.0" y="45.86363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n5">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="720.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n6">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n7">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="56.68181818181817"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n8">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="68.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n3" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n5" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-10.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n6" target="n8">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e3" source="n7" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-10.0">
+            <y:Point x="180.0" y="40.61363636363636"/>
+            <y:Point x="320.0" y="40.61363636363636"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e4" source="n8" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="10.0">
+            <y:Point x="860.0" y="90.5"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e5" source="n1" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="10.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e6" source="n7" target="n1">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e7" source="n2" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e8" source="n8" target="n2">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e9" source="n6" target="n2">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.png b/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.png
index 661d0e3..3b6b0db 100644
Binary files a/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.png and b/subprojects/docs/src/docs/userguide/img/groovyPluginTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/img/javaPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/javaPluginTasks.graphml
index dd1f23d..21950b2 100644
--- a/subprojects/docs/src/docs/userguide/img/javaPluginTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/javaPluginTasks.graphml
@@ -1,326 +1,304 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="14" parse.nodes="14" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="73.32352941176471"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="69.07352941176471"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="101.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="161.5735294117647"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="17.3359375" x="61.33203125" y="6.296875">jar</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.83823529411765"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.341796875" x="58.3291015625" y="6.296875">test</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="45.361328125" x="47.3193359375" y="6.296875">javadoc</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n6">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="193.5"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="55.357421875" x="42.3212890625" y="6.296875">assemble</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n7">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="268.5"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="32.6875" x="53.65625" y="6.296875">clean</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n8">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="1080.0" y="103.58823529411765"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="29.353515625" x="55.3232421875" y="6.296875">build</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n9">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="38.25"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n10">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="23.32352941176471"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n11">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n12">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="900.0" y="49.83823529411765"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="35.34765625" x="52.326171875" y="6.296875">check</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n13">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="143.5"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="86.048828125" x="26.9755859375" y="6.296875">uploadArchives</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n1" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n3" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n9" target="n3">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="11.25" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n9" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-3.75" tx="-70.0" ty="-7.5">
-            <y:Point x="360.0" y="40.5"/>
-            <y:Point x="680.0" y="40.5"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n10" target="n9">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e5" source="n0" target="n9">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e6" source="n2" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e7" source="n11" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e8" source="n9" target="n11">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="3.75" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e9" source="n6" target="n8">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5">
-            <y:Point x="1040.0" y="208.5"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e10" source="n4" target="n12">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e11" source="n12" target="n8">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e12" source="n3" target="n13">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e13" source="n9" target="n5">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-11.25" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="73.32352941176471"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="69.07352941176471"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="101.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="161.5735294117647"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="17.3359375" x="61.33203125" y="6.296875">jar</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n4">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.83823529411765"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.341796875" x="58.3291015625" y="6.296875">test</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n5">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="45.361328125" x="47.3193359375" y="6.296875">javadoc</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n6">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="193.5"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="55.357421875" x="42.3212890625" y="6.296875">assemble</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n7">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="268.5"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="32.6875" x="53.65625" y="6.296875">clean</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n8">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="1080.0" y="103.58823529411765"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="29.353515625" x="55.3232421875" y="6.296875">build</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n9">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="38.25"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n10">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="23.32352941176471"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n11">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n12">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="900.0" y="49.83823529411765"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="35.34765625" x="52.326171875" y="6.296875">check</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n13">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="143.5"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="86.048828125" x="26.9755859375" y="6.296875">uploadArchives</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n1" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n3" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n9" target="n3">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="11.25" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e3" source="n9" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-3.75" tx="-70.0" ty="-7.5">
+            <y:Point x="360.0" y="40.5"/>
+            <y:Point x="680.0" y="40.5"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e4" source="n10" target="n9">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e5" source="n0" target="n9">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e6" source="n2" target="n1">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e7" source="n11" target="n1">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e8" source="n9" target="n11">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="3.75" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e9" source="n6" target="n8">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="7.5">
+            <y:Point x="1040.0" y="208.5"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e10" source="n4" target="n12">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e11" source="n12" target="n8">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e12" source="n3" target="n13">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e13" source="n9" target="n5">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-11.25" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/javaPluginTasks.png b/subprojects/docs/src/docs/userguide/img/javaPluginTasks.png
index fd0e698..b4a3f3b 100644
Binary files a/subprojects/docs/src/docs/userguide/img/javaPluginTasks.png and b/subprojects/docs/src/docs/userguide/img/javaPluginTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.graphml
index c77580d..5817de2 100644
--- a/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.graphml
@@ -1,103 +1,101 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="3" parse.nodes="5" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="50.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.3359375" x="58.33203125" y="6.296875">war</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="25.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="48.021484375" x="45.9892578125" y="6.296875">jettyRun</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="50.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.017578125" x="34.9912109375" y="6.296875">jettyRunWar</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="125.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="50.693359375" x="44.6533203125" y="6.296875">jettyStop</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n1" target="n0">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n1" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n0" target="n3">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="50.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.3359375" x="58.33203125" y="6.296875">war</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="25.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="48.021484375" x="45.9892578125" y="6.296875">jettyRun</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="50.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.017578125" x="34.9912109375" y="6.296875">jettyRunWar</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n4">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="125.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="50.693359375" x="44.6533203125" y="6.296875">jettyStop</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n1" target="n0">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n1" target="n2">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n0" target="n3">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.png b/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.png
index b9cc069..1d5d579 100644
Binary files a/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.png and b/subprojects/docs/src/docs/userguide/img/jettyPluginTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.graphml
index 47ccc92..1650791 100644
--- a/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.graphml
@@ -1,226 +1,213 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="10" parse.nodes="9" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="176.11363636363637"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="51.361328125" x="44.3193359375" y="6.296875">scaladoc</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="101.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="75.3671875" x="32.31640625" y="6.296875">compileScala</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.99999999999999"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="98.705078125" x="20.6474609375" y="6.296875">compileTestScala</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n3">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="51.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n4">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="900.0" y="45.86363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n5">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="720.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n6">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.11363636363636"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n7">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="56.68181818181817"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n8">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="540.0" y="68.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n3" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n5" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-10.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e2" source="n6" target="n8">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e3" source="n7" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-10.0">
-            <y:Point x="180.0" y="40.61363636363636"/>
-            <y:Point x="320.0" y="40.61363636363636"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e4" source="n8" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="10.0">
-            <y:Point x="860.0" y="90.5"/>
-          </y:Path>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e5" source="n1" target="n6">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="10.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e6" source="n7" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e7" source="n2" target="n4">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e8" source="n8" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e9" source="n6" target="n2">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-7.5"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="176.11363636363637"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="51.361328125" x="44.3193359375" y="6.296875">scaladoc</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="101.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="75.3671875" x="32.31640625" y="6.296875">compileScala</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="720.0" y="49.99999999999999"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="98.705078125" x="20.6474609375" y="6.296875000000007">compileTestScala</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="51.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="103.375" x="18.3125" y="6.296875">processResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n4">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="900.0" y="45.86363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="66.021484375" x="36.9892578125" y="6.296875">testClasses</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n5">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="720.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="126.712890625" x="6.6435546875" y="6.296875">processTestResources</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n6">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="51.11363636363636"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n7">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="56.68181818181817"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="70.697265625" x="34.6513671875" y="6.296875">compileJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n8">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="540.0" y="68.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="94.03515625" x="22.982421875" y="6.296875">compileTestJava</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n3" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n5" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-10.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n6" target="n8">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e3" source="n7" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-10.0">
+            <y:Point x="180.0" y="40.61363636363636"/>
+            <y:Point x="320.0" y="40.61363636363636"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e4" source="n8" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="10.0">
+            <y:Point x="860.0" y="90.5"/>
+          </y:Path>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e5" source="n1" target="n6">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="10.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e6" source="n7" target="n1">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="7.5" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e7" source="n2" target="n4">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e8" source="n8" target="n2">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e9" source="n6" target="n2">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-7.5" tx="-70.0" ty="-7.5"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.png b/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.png
index d3c99e2..7d79781 100644
Binary files a/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.png and b/subprojects/docs/src/docs/userguide/img/scalaPluginTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/img/warPluginTasks.graphml b/subprojects/docs/src/docs/userguide/img/warPluginTasks.graphml
index 736faad..9b01a71 100644
--- a/subprojects/docs/src/docs/userguide/img/warPluginTasks.graphml
+++ b/subprojects/docs/src/docs/userguide/img/warPluginTasks.graphml
@@ -1,69 +1,70 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
-  <key attr.name="description" attr.type="string" for="node" id="d0"/>
-  <key for="node" id="d1" yfiles.type="nodegraphics"/>
-  <key attr.name="description" attr.type="string" for="edge" id="d2"/>
-  <key for="edge" id="d3" yfiles.type="edgegraphics"/>
-  <key for="graphml" id="d4" yfiles.type="resources"/>
-  <graph edgedefault="directed" id="G" parse.edges="2" parse.nodes="3" parse.order="free">
-    <node id="n0">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="180.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.3359375" x="58.33203125" y="6.296875">war</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n1">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="360.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="55.357421875" x="42.3212890625" y="6.296875">assemble</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <node id="n2">
-      <data key="d0"/>
-      <data key="d1">
-        <y:ShapeNode>
-          <y:Geometry height="30.0" width="140.0" x="0.0" y="0.0"/>
-          <y:Fill color="#C3D9E6" transparent="false"/>
-          <y:BorderStyle color="#000000" type="line" width="1.0"/>
-          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
-          <y:Shape type="roundrectangle"/>
-        </y:ShapeNode>
-      </data>
-    </node>
-    <edge id="e0" source="n0" target="n1">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-    <edge id="e1" source="n2" target="n0">
-      <data key="d2"/>
-      <data key="d3">
-        <y:QuadCurveEdge straightness="0.1">
-          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
-          <y:LineStyle color="#000000" type="line" width="1.0"/>
-          <y:Arrows source="none" target="delta"/>
-        </y:QuadCurveEdge>
-      </data>
-    </edge>
-  </graph>
-  <data key="d4">
-    <y:Resources/>
-  </data>
-</graphml>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.12.2-->
+  <key for="graphml" id="d0" yfiles.type="resources"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d7"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d8"/>
+  <key for="edge" id="d9" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <node id="n0">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="180.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="23.3359375" x="58.33203125" y="6.296875">war</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="360.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="55.357421875" x="42.3212890625" y="6.296875">assemble</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n2">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="30.0" width="140.0" x="0.0" y="0.0"/>
+          <y:Fill color="#C3D9E6" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Arial" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.40625" modelName="internal" modelPosition="c" textColor="#555555" visible="true" width="44.013671875" x="47.9931640625" y="6.296875">classes</y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n0" target="n1">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n2" target="n0">
+      <data key="d9">
+        <y:QuadCurveEdge straightness="0.1">
+          <y:Path sx="70.0" sy="-0.0" tx="-70.0" ty="-0.0"/>
+          <y:LineStyle color="#000000" type="line" width="1.0"/>
+          <y:Arrows source="delta" target="none"/>
+        </y:QuadCurveEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d0">
+    <y:Resources/>
+  </data>
+</graphml>
diff --git a/subprojects/docs/src/docs/userguide/img/warPluginTasks.png b/subprojects/docs/src/docs/userguide/img/warPluginTasks.png
index 79590bf..16319f2 100644
Binary files a/subprojects/docs/src/docs/userguide/img/warPluginTasks.png and b/subprojects/docs/src/docs/userguide/img/warPluginTasks.png differ
diff --git a/subprojects/docs/src/docs/userguide/initscripts.xml b/subprojects/docs/src/docs/userguide/initscripts.xml
index 2b8c208..3f59d3c 100644
--- a/subprojects/docs/src/docs/userguide/initscripts.xml
+++ b/subprojects/docs/src/docs/userguide/initscripts.xml
@@ -18,6 +18,9 @@
     <para>Gradle provides a powerful mechanism to allow customizing the build based on the current environment.  This
         mechanism also supports tools that wish to integrate with Gradle.
     </para>
+    <para>Note that this is completely different from the “<literal>init</literal>” task
+    provided by the “<literal>build-init</literal>” incubating plugin (see <xref linkend='build_init_plugin'/>).
+    </para>
     <section id='sec:basic_usage'>
         <title>Basic usage</title>
         <para>Initialization scripts (a.k.a. <firstterm>init scripts</firstterm>) are similar to other scripts in Gradle.
@@ -50,7 +53,7 @@
                     </para>
                 </listitem>
             </itemizedlist>
-            One main limitation of init scripts is that they cannot access classes in the buildSrc project (see
+            One main limitation of init scripts is that they cannot access classes in the <literal>buildSrc</literal> project (see
             <xref linkend='sec:build_sources'/> for details of this feature).
         </para>
     </section>
@@ -86,7 +89,7 @@
     </section>
     <section>
         <title>Writing an init script</title>
-        <para>Similar to a Gradle build script, an init script is a groovy script. Each init script has a
+        <para>Similar to a Gradle build script, an init script is a Groovy script. Each init script has a
             <apilink class="org.gradle.api.invocation.Gradle"/> instance associated with it. Any property reference
             and method call in the init script will delegate to this <classname>Gradle</classname> instance.
         </para>
@@ -107,8 +110,8 @@
     </section>
     <section id='sec:custom_classpath'>
         <title>External dependencies for the init script</title>
-        <para>In <xref linkend='sec:external_dependencies'/> is was explained how to add external dependencies to a
-            build script. Init scripts can similarly have external dependencies defined.  You do this using the
+        <para>In <xref linkend='sec:external_dependencies'/> it was explained how to add external dependencies to a
+            build script. Init scripts can also declare dependencies.  You do this with the
             <literal>initscript()</literal> method, passing in a closure which declares the init script classpath.
         </para>
         <sample id="declareExternalInitDependency" dir="userguide/initScripts/externalDependency" title="Declaring external dependencies for an init script">
@@ -136,12 +139,12 @@
                     <sourcefile file="build.gradle" snippet="show-repos-task"/>
                     <output args="-q -I init.gradle showRepositories"/>
             </sample>
-            <para>The plugin in the sample init scripts ensures, that only a specified repository is used when running the build.
+            <para>The plugin in the init script ensures that only a specified repository is used when running the build.
             </para>
             <para>When applying plugins within the init script, Gradle instantiates the plugin and calls the plugin
                 instance's <apilink class="org.gradle.api.Plugin" method="apply"/> method. The <literal>gradle</literal> object is passed as a parameter,
                 which can be used to configure all aspects of a build.
-                Of course, the applied plugin can be resolved as external dependency as described in <xref linkend='sec:custom_classpath'/>
+                Of course, the applied plugin can be resolved as an external dependency as described in <xref linkend='sec:custom_classpath'/>
             </para>
         </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/installation.xml b/subprojects/docs/src/docs/userguide/installation.xml
index 78cb916..65b74f0 100644
--- a/subprojects/docs/src/docs/userguide/installation.xml
+++ b/subprojects/docs/src/docs/userguide/installation.xml
@@ -20,12 +20,12 @@
 <section>
     <title>Prerequisites</title>
 
-    <para>Gradle requires a Java JDK to be installed. Gradle requires a JDK 1.5 or higher. Gradle ships with its own
-        Groovy library, therefore no Groovy needs to be installed. Any existing Groovy installation is ignored by Gradle.
+    <para>Gradle requires a Java JDK or JRE to be installed, version 6 or higher (to check, use <userinput>java -version</userinput>).
+    	Gradle ships with its own Groovy library, therefore Groovy does not need to be installed. Any existing Groovy installation is ignored by Gradle.
     </para>
 
-    <para>Gradle uses whichever JDK it finds in your path (to check, use <userinput>java -version</userinput>).
-        Alternatively, you can set the <envar>JAVA_HOME</envar> environment variable to point to the install directory
+    <para>Gradle uses whatever JDK it finds in your path.
+        Alternatively, you can set the <envar>JAVA_HOME</envar> environment variable to point to the installation directory
         of the desired JDK.
     </para>
 </section>
@@ -47,7 +47,7 @@
     <listitem><para>The API documentation (Javadoc and Groovydoc).</para></listitem>
     <listitem>
         <para>Extensive samples, including the examples referenced in the user guide, along with some complete and more
-            complex builds you can use the starting point for your own build.
+            complex builds you can use as a starting point for your own build.
         </para>
     </listitem>
     <listitem>
@@ -70,8 +70,8 @@
 <title>Running and testing your installation</title>
 
 <para>You run Gradle via the <command>gradle</command> command. To check if Gradle is properly installed just type
-<command>gradle -v</command>. The output shows Gradle version and also local environment configuration (groovy and jvm version, etc.).
-    The displayed gradle version should match the distribution you have downloaded.
+<command>gradle -v</command>. The output shows the Gradle version and also the local environment configuration (Groovy, JVM version, OS, etc.).
+    The displayed Gradle version should match the distribution you have downloaded.
 </para>
 
 </section>
@@ -79,13 +79,13 @@
 <section>
 <title>JVM options</title>
 
-<para>JVM options for running Gradle can be set via environment variables. You can use <envar>GRADLE_OPTS</envar>
-or <envar>JAVA_OPTS</envar>. Those variables can be used together. <envar>JAVA_OPTS</envar> is by convention an environment
+<para>JVM options for running Gradle can be set via environment variables. You can use either <envar>GRADLE_OPTS</envar>
+or <envar>JAVA_OPTS</envar>, or both. <envar>JAVA_OPTS</envar> is by convention an environment
 variable shared by many Java applications. A typical use case would be to set the HTTP proxy in <envar>JAVA_OPTS</envar>
 and the memory options in <envar>GRADLE_OPTS</envar>. Those variables can also be set at the beginning
 of the <command>gradle</command> or <command>gradlew</command> script.
 </para>
-
+<para>Note that it's not currently possible to set JVM options for Gradle on the command line.</para>
 </section>
 
 <section condition="standalone">
diff --git a/subprojects/docs/src/docs/userguide/introduction.xml b/subprojects/docs/src/docs/userguide/introduction.xml
index 3a07ccc..af989a7 100644
--- a/subprojects/docs/src/docs/userguide/introduction.xml
+++ b/subprojects/docs/src/docs/userguide/introduction.xml
@@ -71,5 +71,8 @@
             guide. You can find out more about contributing to the documentation at the
             <ulink url="website:contribute">Gradle web site</ulink>.
         </para>
+        <para>Throughout the user guide, you will find some diagrams that represent dependency relationships
+        between Gradle tasks. These use something analogous to the UML dependency notation, which renders an
+        arrow from one task to the task that the first task depends on.</para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/jacocoPlugin.xml b/subprojects/docs/src/docs/userguide/jacocoPlugin.xml
index 26e49ac..f556ef0 100644
--- a/subprojects/docs/src/docs/userguide/jacocoPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/jacocoPlugin.xml
@@ -60,7 +60,7 @@
             </thead>
             <tr>
                 <td>reportsDir</td>
-                <td>"<replaceable>$buildDir</replaceable>/reports/jacoco"
+                <td>“<replaceable>$buildDir</replaceable>/reports/jacoco”
                 </td>
             </tr>
         </table>
@@ -245,4 +245,4 @@
         </table>
     </section>
 
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/javaGradlePlugin.xml b/subprojects/docs/src/docs/userguide/javaGradlePlugin.xml
new file mode 100644
index 0000000..19b951b
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/javaGradlePlugin.xml
@@ -0,0 +1,55 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<chapter id="javaGradle_plugin">
+    <title>The Java Gradle Plugin Development Plugin</title>
+    <note>
+        <para>
+            The Java Gradle plugin development plugin is currently <link linkend="feature_lifecycle">incubating</link>.
+            Please be aware that the DSL and other configuration may change in later Gradle versions.
+        </para>
+    </note>
+
+    <para>
+        The Java Gradle Plugin development plugin can be used to assist in the development of Gradle plugins.  It automatically applies
+        the <link linkend="java_plugin">Java</link> plugin, adds the <literal>gradleApi()</literal> dependency to the compile configuration
+        and performs validation of plugin metadata during <literal>jar</literal> task execution.
+    </para>
+
+    <section>
+        <title>Usage</title>
+        <para>To use the Java Gradle Plugin Development plugin, include the following in your build script:</para>
+        <sample id="useJavaGradlePluginPlugin" dir="javaGradlePlugin" title="Using the Java Gradle Plugin Development plugin">
+            <sourcefile file="build.gradle" snippet="use-java-gradle-plugin-plugin"/>
+        </sample>
+        <para>
+            Applying the plugin automatically applies the <link linkend="java_plugin">Java</link> plugin and adds the
+            <literal>gradleApi()</literal> dependency to the compile configuration.  It also decorates the <literal>jar</literal>
+            task with validations.
+        </para>
+        <para>
+            The following validations are performed:
+        </para>
+        <itemizedlist>
+            <listitem>There is a plugin descriptor defined for the plugin.</listitem>
+            <listitem>The plugin descriptor contains an <literal>implementation-class</literal> property.</listitem>
+            <listitem>The <literal>implementation-class</literal> property references a valid class file in the jar.</listitem>
+        </itemizedlist>
+        <para>
+            Any failed validations will result in a warning message.
+        </para>
+    </section>
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/javaLibraryDistributionPlugin.xml b/subprojects/docs/src/docs/userguide/javaLibraryDistributionPlugin.xml
index 28806f3..8bf1d9c 100644
--- a/subprojects/docs/src/docs/userguide/javaLibraryDistributionPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/javaLibraryDistributionPlugin.xml
@@ -12,8 +12,8 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Java library distribution plugin, include in your build script:</para>
-        <sample id="usejavaLibraryPlugin" dir="userguide/javaLibraryDistribution" title="Using the java library distribution plugin">
+        <para>To use the Java library distribution plugin, include the following in your build script:</para>
+        <sample id="usejavaLibraryPlugin" dir="userguide/javaLibraryDistribution" title="Using the Java library distribution plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
         <para>To define the name for the distribution you have to set the <literal>baseName</literal> property as shown below:
@@ -22,9 +22,9 @@
             <sourcefile file="build.gradle" snippet="name-conf"/>
         </sample>
 
-        <para>The plugin build a distribution for your library. The distribution will package up the runtime dependencies of the library
+        <para>The plugin builds a distribution for your library. The distribution will package up the runtime dependencies of the library.
             All files stored in <filename>src/main/dist</filename> will be added to the root of the archive distribution. You can run
-            <userinput>gradle distZip</userinput> to create a ZIP containing the distribution.
+            “<userinput>gradle distZip</userinput>” to create a ZIP file containing the distribution.
         </para>
     </section>
 
@@ -60,7 +60,7 @@
         <title>Including other resources in the distribution</title>
         <para>
             All of the files from the <filename>src/dist</filename> directory are copied. To include any static files in the distribution, simply arrange
-            them in the <filename>src/dist</filename> directory, or add it to the content of the distribution.
+            them in the <filename>src/dist</filename> directory, or add them to the content of the distribution.
         </para>
         <sample id="includeTaskOutputInApplicationDistribution" dir="userguide/javaLibraryDistribution" title="Include files in the distribution">
             <sourcefile file="build.gradle" snippet="custom-distribution"/>
diff --git a/subprojects/docs/src/docs/userguide/javaPlugin.xml b/subprojects/docs/src/docs/userguide/javaPlugin.xml
index 7c79347..64c061a 100644
--- a/subprojects/docs/src/docs/userguide/javaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/javaPlugin.xml
@@ -17,13 +17,13 @@
 <chapter id='java_plugin' xmlns:xi="http://www.w3.org/2001/XInclude">
     <title>The Java Plugin</title>
 
-    <para>The Java plugin adds Java compilation, testing and bundling capabilities to a project. It serves as the basis
+    <para>The Java plugin adds Java compilation along with testing and bundling capabilities to a project. It serves as the basis
         for many of the other Gradle plugins.
     </para>
 
     <section>
         <title>Usage</title>
-        <para>To use the Java plugin, include in your build script:</para>
+        <para>To use the Java plugin, include the following in your build script:</para>
         <sample id="useJavaPlugin" dir="java/quickstart" title="Using the Java plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -43,8 +43,9 @@
         </para>
         <para>The Java plugin defines two standard source sets, called <literal>main</literal> and <literal>test</literal>.
             The <literal>main</literal> source set contains your production source code, which is compiled and assembled
-            into a JAR file. The <literal>test</literal> source set contains your unit test source code, which is
-            compiled and executed using JUnit or TestNG.
+            into a JAR file. The <literal>test</literal> source set contains your test source code, which is
+            compiled and executed using JUnit or TestNG.  These can be unit tests, integration tests, acceptance tests,
+            or any combination that is useful to you.
         </para>
     </section>
 
@@ -85,7 +86,7 @@
                     <literal>classes</literal>
                 </td>
                 <td>
-                    <literal>compileJava</literal> and <literal>processResources</literal>.
+                    The <literal>compileJava</literal> task and the <literal>processResources</literal> task.
                     Some plugins add additional compilation tasks.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
@@ -114,7 +115,7 @@
                     <literal>testClasses</literal>
                 </td>
                 <td>
-                    <literal>compileTestJava</literal> and <literal>processTestResources</literal>.
+                    <literal>compileTestJava</literal> task and <literal>processTestResources</literal> task.
                     Some plugins add additional test compilation tasks.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
@@ -158,7 +159,7 @@
                     The tasks which produce the artifacts in the <literal>archives</literal> configuration, including <literal>jar</literal>.
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Upload"/></td>
-                <td>Uploads the artifacts in the <literal>archives</literal> configuration, including the JAR file.</td>
+                <td>Uploads artifacts in the <literal>archives</literal> configuration, including the JAR file.</td>
             </tr>
             <tr>
                 <td>
@@ -174,8 +175,8 @@
                 </td>
                 <td>-</td>
                 <td><apilink class="org.gradle.api.tasks.Delete"/></td>
-                <td>Deletes the output files produced by the specified task. For example <literal>cleanJar</literal>
-                    will delete the JAR file created by the <literal>jar</literal> task, and
+                <td>Deletes files created by specified task. <literal>cleanJar</literal>
+					will delete the JAR file created by the <literal>jar</literal> task, and
                     <literal>cleanTest</literal> will delete the test results created by the <literal>test</literal> task.
                 </td>
             </tr>
@@ -215,8 +216,8 @@
                     <literal><replaceable>sourceSet</replaceable>Classes</literal>
                 </td>
                 <td>
-                    <literal>compile<replaceable>SourceSet</replaceable>Java</literal> and
-                    <literal>process<replaceable>SourceSet</replaceable>Resources</literal>.
+                    The <literal>compile<replaceable>SourceSet</replaceable>Java</literal> task and
+                    the <literal>process<replaceable>SourceSet</replaceable>Resources</literal> task.
                     Some plugins add additional compilation tasks for the source set.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
@@ -273,7 +274,7 @@
                     <literal>buildNeeded</literal>
                 </td>
                 <td>
-                    <literal>build</literal> and <literal>build</literal> tasks in all project lib dependencies of the
+                    <literal>build</literal> and <literal>buildNeeded</literal> tasks in all project lib dependencies of the
                     <literal>testRuntime</literal> configuration.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
@@ -284,7 +285,7 @@
                     <literal>buildDependents</literal>
                 </td>
                 <td>
-                    <literal>build</literal> and <literal>build</literal> tasks in all projects with a project lib
+                    <literal>build</literal> and <literal>buildDependents</literal> tasks in all projects with a project lib
                     dependency on this project in a <literal>testRuntime</literal> configuration.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
@@ -292,10 +293,10 @@
             </tr>
             <tr>
                 <td>
-                    <literal>build<replaceable>ConfigurationName</replaceable></literal>
+                    <literal>build<replaceable>ConfigName</replaceable></literal>
                 </td>
                 <td>
-                    The tasks which produce the artifacts in configuration <replaceable>ConfigurationName</replaceable>.
+                    The tasks which produce the artifacts in configuration <replaceable>ConfigName</replaceable>.
                 </td>
                 <td><apilink class="org.gradle.api.Task"/></td>
                 <td>Assembles the artifacts in the specified configuration.
@@ -304,10 +305,10 @@
             </tr>
             <tr>
                 <td>
-                    <literal>upload<replaceable>ConfigurationName</replaceable></literal>
+                    <literal>upload<replaceable>ConfigName</replaceable></literal>
                 </td>
                 <td>
-                    The tasks which uploads the artifacts in configuration <replaceable>ConfigurationName</replaceable>.
+                    The tasks which uploads the artifacts in configuration <replaceable>ConfigName</replaceable>.
                 </td>
                 <td><apilink class="org.gradle.api.tasks.Upload"/></td>
                 <td>Assembles and uploads the artifacts in the specified configuration.
@@ -337,6 +338,7 @@
             <thead>
                 <tr>
                     <td>Directory</td>
+                    <td></td>
                     <td>Meaning</td>
                 </tr>
             </thead>
@@ -429,16 +431,16 @@
                 </tr>
             </thead>
             <tr>
-                <td><replaceable>sourceSet</replaceable>Compile</td>
+                <td><literal><replaceable>sourceSet</replaceable>Compile</literal></td>
                 <td>-</td>
-                <td>compile<replaceable>SourceSet</replaceable>Java</td>
+                <td><literal>compile<replaceable>SourceSet</replaceable>Java</literal></td>
                 <td>Compile time dependencies for the given source set</td>
             </tr>
             <tr>
-                <td><replaceable>sourceSet</replaceable>Runtime</td>
-                <td><replaceable>sourceSet</replaceable>Compile</td>
+                <td><literal><replaceable>sourceSet</replaceable>Runtime</literal></td>
+                <td><literal><replaceable>sourceSet</replaceable>Compile</literal></td>
                 <td>-</td>
-                <td>Runtime time dependencies for the given source set</td>
+                <td>Runtime dependencies for the given source set</td>
             </tr>
         </table>
     </section>
@@ -657,7 +659,7 @@
                 <td><apilink class="org.gradle.api.JavaVersion"/>. Can also set using a String or a Number, e.g.
                     <literal>'1.5'</literal> or <literal>1.5</literal>.
                 </td>
-                <td>Value of the current used JVM</td>
+                <td>version of the current JVM in use</td>
                 <td>Java version compatibility to use when compiling Java source.</td>
             </tr>
             <tr>
@@ -694,8 +696,7 @@
 
         <para>
             These properties are provided by convention objects of type <apilink class="org.gradle.api.plugins.JavaPluginConvention"/>,
-            <apilink class="org.gradle.api.plugins.BasePluginConvention"/> and
-            <apilink class="org.gradle.api.plugins.ReportingBasePluginConvention"/>.
+            and <apilink class="org.gradle.api.plugins.BasePluginConvention"/>.
         </para>
     </section>
 
@@ -926,8 +927,8 @@
             <para>
                 The Java plugin also adds a number of tasks which assemble the classes for the
                 source set, as shown in <xref linkend="java_source_set_tasks"/>. For example, for a source set called
-                <literal>intTest</literal>, you can run <userinput>gradle intTestClasses</userinput> to compile the
-                int test classes.
+                <literal>intTest</literal>, compiling the classes for this source set is done by
+                running <userinput>gradle intTestClasses</userinput>.
             </para>
             <sample id="compileSourceSet" dir="userguide/java/sourceSets" title="Compiling a source set">
                 <output args="intTestClasses"/>
@@ -954,7 +955,7 @@
     <section id='sec:javadoc'>
         <title>Javadoc</title>
         <para>The <literal>javadoc</literal> task is an instance of <apilink class="org.gradle.api.tasks.javadoc.Javadoc"/>.
-            It supports the core javadoc options and the options of the standard doclet described in the
+            It supports the core Javadoc options and the options of the standard doclet described in the
             <ulink url='http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#referenceguide'>reference documentation</ulink>
             of the Javadoc executable.
             For a complete list of supported Javadoc options consult the API documentation of the following classes:
@@ -974,7 +975,7 @@
                     <literal>classpath</literal>
                 </td>
                 <td><apilink class="org.gradle.api.file.FileCollection"/></td>
-                <td><literal>sourceSets.main.output + sourceSets.main.compileClasspath</literal></td>
+                <td><literal>sourceSets.main.output</literal> + <literal>sourceSets.main.compileClasspath</literal></td>
             </tr>
             <tr>
                 <td><literal>source</literal></td>
@@ -1092,10 +1093,6 @@
             </tr>
         </table>
 
-        <para>The compile task delegates to Ant's javac task. Setting <literal>options.useAnt</literal> to <literal>false</literal>
-            activates Gradle's direct compiler integration, bypassing the Ant task. In a future Gradle release, this will become the default.
-        </para>
-
         <para>By default, the Java compiler runs in the Gradle process. Setting <literal>options.fork</literal> to <literal>true</literal>
             causes compilation to occur in a separate process. In the case of the Ant javac task, this means that a new process will be
             forked for each compile task, which can slow down compilation. Conversely, Gradle's direct compiler integration (see above) will
@@ -1104,6 +1101,32 @@
         </para>
     </section>
 
+    <section id='sec:incremental_compile'>
+        <title>Incremental Java compilation</title>
+        <para>Starting with Gradle 2.1, it is possible to compile Java incrementally. This feature is still incubating.
+            See the <apilink class="org.gradle.api.tasks.compile.JavaCompile"/> task for information on how to enable it.</para>
+        <para>
+            Main goals for incremental compilations are:
+            <itemizedlist>
+                <listitem>Avoid wasting time compiling source classes that don't have to be compiled.
+                    This means faster builds, especially when a change to a source class or a jar does not incur recompilation of many source classes that depend on the changed input.</listitem>
+                <listitem>Change as few output classes as possible. Classes that don't need to be recompiled remain unchanged in the output directory.
+                    An example scenario when this is really useful is using JRebel - the fewer output classes are changed the quicker the jvm can use refreshed classes.</listitem>
+            </itemizedlist>
+            The incremental compilation at a high level:
+            <itemizedlist>
+                <listitem>The detection of the correct set of stale classes is reliable at some expense of speed.
+                    The algorithm uses bytecode analysis and deals gracefully with compiler optimizations (inlining of non-private constants), transitive class dependencies, etc.
+                    Example: When a class with a public constant changes, we eagerly compile everything to avoid problems with constants inlined by the compiler.
+                    Down the road we will tune the algorithm and caching so that incremental Java compilation can be a default setting for every compile task.
+                </listitem>
+                <listitem>To make incremental compilation fast, we cache class analysis results and jar snapshots.
+                    The initial incremental compilation can be slower due to the cold caches.
+                </listitem>
+            </itemizedlist>
+        </para>
+    </section>
+
     <section id='sec:java_test'>
         <title>Test</title>
         <para>The <literal>test</literal> task is an instance of <apilink class="org.gradle.api.tasks.testing.Test"/>.
@@ -1137,10 +1160,10 @@
                 maximum number of test classes to execute in a test process. The default is to execute an unlimited number
                 of tests in each test process.</para>
             <para>The task has an <literal>ignoreFailures</literal> property to control the behavior when tests fail.
-                Test always executes every test that it detects. It stops the build afterwards if <literal>ignoreFailures</literal>
+                The <literal>Test</literal> task always executes every test that it detects. It stops the build afterwards if <literal>ignoreFailures</literal>
                 is false and there are failing tests. The default value of <literal>ignoreFailures</literal> is false.
             </para>
-            <para>The <literal>testLogging</literal> property allows to configure which test events are going to be logged and at
+            <para>The <literal>testLogging</literal> property allows you to configure which test events are going to be logged and at
                 which detail level. By default, a concise message will be logged for every failed test. See
                 <apilink class="org.gradle.api.tasks.testing.logging.TestLoggingContainer"/> for how to tune test logging to your
                 preferences.
@@ -1154,7 +1177,7 @@
                 debugger to attach to port 5005 before proceeding with test execution.
             </para>
             <para>
-                This can also be enabled at invocation time via the <userinput>--debug-jvm</userinput> task option.
+                This can also be enabled at invocation time via the <userinput>--debug-jvm</userinput> task option (since Gradle 1.12).
             </para>
         </section>
 
@@ -1167,7 +1190,7 @@
                 (<literal>-Dtest.single</literal>, <literal>test.include</literal> and friends).
                 The latter is based on files, e.g. the physical location of the test implementation class.
                 File-level test selection does not support many interesting scenarios that are possible with test-level filtering.
-                Some of them Gradle handles now and some will be satisfied in the future releases:
+                Some of them Gradle handles now and some will be satisfied in future releases:
                 <itemizedlist>
                     <listitem>Filtering at the level of specific test methods; executing a single test method</listitem>
                     <listitem>Filtering based on custom annotations (future)</listitem>
@@ -1180,15 +1203,15 @@
                 Test filtering feature has following characteristic:
                 <itemizedlist>
                     <listitem>Fully qualified class name or fully qualified method name is supported,
-                        e.g. "org.gradle.SomeTest", "org.gradle.SomeTest.someMethod"</listitem>
+                        e.g. “org.gradle.SomeTest”, “org.gradle.SomeTest.someMethod”</listitem>
                     <listitem>Wildcard '*' is supported for matching any characters</listitem>
-                    <listitem>Command line option "--tests" is provided to conveniently set the test filter.
+                    <listitem>Command line option “--tests” is provided to conveniently set the test filter.
                         Especially useful for the classic 'single test method execution' use case.
                         When the command line option is used, the inclusion filters declared in the build script are ignored.
                     </listitem>
-                    <listitem>Gradle tries best to filter the tests given limitations of particular test framework API.
+                    <listitem>Gradle tries to filter the tests given the limitations of the test framework API.
                         Some advanced, synthetic tests may not be fully compatible with filtering.
-                        However, vast majority of tests and use cases should be handled neatly.
+                        However, the vast majority of tests and use cases should be handled neatly.
                     </listitem>
                     <listitem>Test filtering supersedes the file-based test selection. The latter may be completely replaced in future.
                         We will grow the the test filtering api and add more kinds of filters.
@@ -1236,9 +1259,9 @@
             <note>This mechanism has been superseded by 'Test Filtering', described above.</note>
             <para>Setting a system property of <replaceable>taskName.single</replaceable> = <replaceable>testNamePattern</replaceable>
                   will only execute tests that match the specified <replaceable>testNamePattern</replaceable>.
-                  The <replaceable>taskName</replaceable> can be a full multi-project path like ":sub1:sub2:test"
+                  The <replaceable>taskName</replaceable> can be a full multi-project path like “:sub1:sub2:test”
                   or just the task name.  The <replaceable>testNamePattern</replaceable> will be used to form an include
-                  pattern of "**/testNamePattern*.class".
+                  pattern of “**/testNamePattern*.class”;.
                   If no tests with this pattern can be found an exception is thrown. This is to shield you from false security.
                   If tests of more than one subproject are executed, the pattern is applied to each subproject. An exception
                   is thrown if no tests can be found for a particular subproject. In such a case you can use the path notation of the
@@ -1290,17 +1313,16 @@
                 When using TestNG, we scan for methods annotated with <classname>@Test</classname>.
             </para>
             <para>
-                Note that abstract classes are not executed. Gradle also scan up the inheritance tree into jar files
+                Note that abstract classes are not executed. Gradle also scans up the inheritance tree into jar files
                 on the test classpath.
             </para>
             <para>
-                In case you don't want to use the test class detection, you can disable it by setting
-                <literal>scanForTestClasses</literal> to false. This will make the test task only use the includes /
-                excludes to find test classes.
-
-                If <literal>scanForTestClasses</literal> is disabled and no include or exclude patterns are specified, the
-                respective defaults are used. For include this is <literal>"**/*Tests.class", "**/*Test.class"</literal>
-                and the for exclude it is <literal>"**/Abstract*.class"</literal>.
+                If you don't want to use test class detection, you can disable it by setting
+                <literal>scanForTestClasses</literal> to false. This will make the test task only use includes /
+                excludes to find test classes. If <literal>scanForTestClasses</literal> is false and no include
+                / exclude patterns are specified, the defaults are
+                “<literal>**/*Tests.class</literal>”, “<literal>**/*Test.class</literal>” and 
+                “<literal>**/Abstract*.class</literal>” for include and exclude, respectively.
             </para>
         </section>
 
@@ -1339,10 +1361,6 @@
                 <listitem><para>Results in an efficient binary format. The task generates the other results from these binary results.</para></listitem>
             </itemizedlist>
 
-            <para>You can disable the HTML test report using the <apilink class="org.gradle.api.tasks.testing.Test" method="setTestReport"/> method. The other
-                results currently cannot be disabled.
-            </para>
-
             <para>There is also a stand-alone <apilink class="org.gradle.api.tasks.testing.TestReport"/> task type which can generate the HTML test
                 report from the binary results generated by one or more <classname>Test</classname> task instances. To use this task type, you need
                 to define a <literal>destinationDir</literal> and the test results to include in the report. Here is a sample which generates a
@@ -1365,8 +1383,8 @@
                     in its reporting of the test method execution.
                 </para>
                 <para>
-                    Given a parameterized test method named <literal>aParameterizedTestMethod</literal> that takes two parameters, it will be reported with the name:
-                    <literal>aParameterizedTestMethod(toStringValueOfParam1, toStringValueOfParam2)</literal>. This makes identifying the parameter values for a
+                    Given a parameterized test method named <literal>aTestMethod</literal> that takes two parameters, it will be reported with the name:
+                    <literal>aTestMethod(toStringValueOfParam1, toStringValueOfParam2)</literal>. This makes identifying the parameter values for a
                     particular iteration easy.
                 </para>
             </section>
@@ -1445,8 +1463,8 @@
             <sample id="manifest" dir="userguide/tutorial/manifest" title="Separate MANIFEST.MF for a particular archive">
                 <sourcefile file="build.gradle" snippet="merge"/>
             </sample>
-            <para>Manifest are merged in the order they are declared by the <literal>from</literal> statement. If
-            the based manifest and the merged manifest both define values for the same key, the merged manifest wins by default.
+            <para>Manifests are merged in the order they are declared by the <literal>from</literal> statement. If
+            the base manifest and the merged manifest both define values for the same key, the merged manifest wins by default.
             You can fully customize the merge behavior by adding <literal>eachEntry</literal> actions in which
                 you have access to a <apilink class="org.gradle.api.java.archives.ManifestMergeDetails"/> instance for each entry
                 of the resulting manifest. The merge is not immediately triggered by the from statement. It is done lazily,
diff --git a/subprojects/docs/src/docs/userguide/javaProjectGenericLayout.xml b/subprojects/docs/src/docs/userguide/javaProjectGenericLayout.xml
index 8146b4d..e5fb826 100644
--- a/subprojects/docs/src/docs/userguide/javaProjectGenericLayout.xml
+++ b/subprojects/docs/src/docs/userguide/javaProjectGenericLayout.xml
@@ -18,12 +18,14 @@
         <td>
             <filename>src/<replaceable>sourceSet</replaceable>/java</filename>
         </td>
+        <td></td>
         <td>Java source for the given source set</td>
     </tr>
     <tr>
         <td>
             <filename>src/<replaceable>sourceSet</replaceable>/resources</filename>
         </td>
+        <td></td>
         <td>Resources for the given source set</td>
     </tr>
 </tgroup>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/javaProjectMainLayout.xml b/subprojects/docs/src/docs/userguide/javaProjectMainLayout.xml
index 0e32c44..3a4c2fb 100644
--- a/subprojects/docs/src/docs/userguide/javaProjectMainLayout.xml
+++ b/subprojects/docs/src/docs/userguide/javaProjectMainLayout.xml
@@ -18,12 +18,14 @@
         <td>
             <filename>src/main/java</filename>
         </td>
+        <td></td>
         <td>Production Java source</td>
     </tr>
     <tr>
         <td>
             <filename>src/main/resources</filename>
         </td>
+        <td></td>
         <td>Production resources</td>
     </tr>
 </tgroup>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/javaProjectTestLayout.xml b/subprojects/docs/src/docs/userguide/javaProjectTestLayout.xml
index 4674da9..22ad2ff 100644
--- a/subprojects/docs/src/docs/userguide/javaProjectTestLayout.xml
+++ b/subprojects/docs/src/docs/userguide/javaProjectTestLayout.xml
@@ -18,12 +18,14 @@
         <td>
             <filename>src/test/java</filename>
         </td>
+        <td></td>
         <td>Test Java source</td>
     </tr>
     <tr>
         <td>
             <filename>src/test/resources</filename>
         </td>
+        <td></td>
         <td>Test resources</td>
     </tr>
 </tgroup>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/javaTutorial.xml b/subprojects/docs/src/docs/userguide/javaTutorial.xml
index d1e7c98..08fca65 100644
--- a/subprojects/docs/src/docs/userguide/javaTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/javaTutorial.xml
@@ -101,7 +101,7 @@
                     <term>check</term>
                     <listitem>
                         <para>Compiles and tests your code. Other plugins add more checks to this task. For example, if
-                            you use the Code-quality plugin, this task will also run Checkstyle against your source
+                            you use the <literal>checkstyle</literal> plugin, this task will also run Checkstyle against your source
                             code.
                         </para>
                     </listitem>
@@ -111,7 +111,6 @@
 
         <section>
             <title>External dependencies</title>
-
             <para>Usually, a Java project will have some dependencies on external JAR files. To reference these JAR
                 files in the project, you need to tell Gradle where to find them. In Gradle, artifacts such as JAR
                 files, are located in a <firstterm>repository</firstterm>. A repository can be used for fetching the
@@ -130,7 +129,7 @@
         </section>
 
         <section>
-            <title>customizing the project</title>
+            <title>Customizing the project</title>
             <para>The Java plugin adds a number of properties to your project. These properties have default values
                 which are usually sufficient to get started. It's easy to change these values if they don't suit. Let's
                 look at this for our sample. Here we will specify the version number for our Java project, along
@@ -167,14 +166,15 @@
             <para>To publish the JAR file, run <userinput>gradle uploadArchives</userinput>.</para>
         </section>
         
-		<section>
+        <section>
             <title>Creating an Eclipse project</title>
-            <para>To import your project into Eclipse, you need to add another plugin to your build file:</para>
-        	<sample id="javaQuickstart" dir="java/quickstart" title="Eclipse plugin">
-            	<sourcefile file="build.gradle" snippet="use-eclipse-plugin"/>
-        	</sample>
-        	<para>Now execute <userinput>gradle eclipse</userinput> command to generate Eclipse project files. More on Eclipse
-                task can be found in <xref linkend="eclipse_plugin"/>.
+            <para>To create the Eclipse-specific descriptor files, like <literal>.project</literal>, you need to add
+            another plugin to your build file:</para>
+            <sample id="javaQuickstart" dir="java/quickstart" title="Eclipse plugin">
+                <sourcefile file="build.gradle" snippet="use-eclipse-plugin"/>
+            </sample>
+            <para>Now execute <userinput>gradle eclipse</userinput> command to generate Eclipse project files. More information
+            about the <literal>eclipse</literal> task can be found in <xref linkend="eclipse_plugin"/>.
             </para>
         </section>
 
@@ -196,12 +196,16 @@
                 api/
                 services/webservice/
                 shared/
+                services/shared/
             </layout>
         </sample>
-        <para>Here we have three projects. Project <literal>api</literal> produces a JAR file which is shipped to the
+        <!--  TODO: Note that the "settings.gradle" lists four projects, also including "services:shared", in addition
+        to "services:webservice". -->
+        <para>Here we have four projects. Project <literal>api</literal> produces a JAR file which is shipped to the
             client to provide them a Java client for your XML webservice. Project <literal>webservice</literal> is a
             webapp which returns XML. Project <literal>shared</literal> contains code used both by <literal>api</literal>
-            and <literal>webservice</literal>.
+            and <literal>webservice</literal>. Project <literal>services/shared</literal> has code that depends on the
+            <literal>shared</literal> project.
         </para>
 
         <section>
@@ -234,13 +238,16 @@
                 configuration properties we have seen in the previous section are available in each subproject.
                 So, you can compile, test, and JAR all the projects by running <userinput>gradle build</userinput> from
                 the root project directory.</para>
+            <para>Also note that these plugins are only applied within the <literal>subprojects</literal> section, not
+            at the root level, so the root build will not expect to find Java source files in the root project,
+            only in the subprojects.</para>
         </section>
 
         <section>
             <title>Dependencies between projects</title>
             <para>You can add dependencies between projects in the same build, so that, for example, the JAR file of one
                 project is used to compile another project. In the <literal>api</literal> build file we will add a dependency
-                on the JAR produced by the <literal>shared</literal> project. Due to this dependency, Gradle will
+                on the <literal>shared</literal> project. Due to this dependency, Gradle will
                 ensure that project <literal>shared</literal> always gets built before project <literal>api</literal>.
             </para>
             <sample id="javaMultiProject" dir="java/multiproject" title="Multi-project build - dependencies between projects">
diff --git a/subprojects/docs/src/docs/userguide/jdependPlugin.xml b/subprojects/docs/src/docs/userguide/jdependPlugin.xml
index 9918c37..48e558e 100644
--- a/subprojects/docs/src/docs/userguide/jdependPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/jdependPlugin.xml
@@ -6,7 +6,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the JDepend plugin, include in your build script:</para>
+        <para>To use the JDepend plugin, include the following in your build script:</para>
         <sample id="useJDependPlugin" dir="codeQuality" title="Using the JDepend plugin">
             <sourcefile file="build.gradle" snippet="use-jdepend-plugin"/>
         </sample>
@@ -90,6 +90,6 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.quality.JDependExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.quality.JDependExtension"/> class in the API documentation.</para>
     </section>
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/jettyPlugin.xml b/subprojects/docs/src/docs/userguide/jettyPlugin.xml
index ac6775b..231a9cd 100644
--- a/subprojects/docs/src/docs/userguide/jettyPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/jettyPlugin.xml
@@ -22,7 +22,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Jetty plugin, include in your build script:</para>
+        <para>To use the Jetty plugin, include the following in your build script:</para>
         <sample id="useJettyPlugin" dir="webApplication/quickstart" title="Using the Jetty plugin">
             <sourcefile file="build.gradle" snippet="use-jetty-plugin"/>
         </sample>
diff --git a/subprojects/docs/src/docs/userguide/logging.xml b/subprojects/docs/src/docs/userguide/logging.xml
index 3b632e3..7386df7 100644
--- a/subprojects/docs/src/docs/userguide/logging.xml
+++ b/subprojects/docs/src/docs/userguide/logging.xml
@@ -16,7 +16,7 @@
 <chapter id='logging'>
     <title>Logging</title>
     <para>The log is the main 'UI' of a build tool. If it is too verbose, real warnings and problems are easily hidden
-        by this. On the other hand you need the relevant information for figuring out if things have gone wrong. Gradle
+        by this. On the other hand you need relevant information for figuring out if things have gone wrong. Gradle
         defines 6 log levels, as shown in <xref linkend="logLevels"/>. There are two Gradle-specific log levels, in
         addition to the ones you might normally see. Those levels are <emphasis>QUIET</emphasis> and
         <emphasis>LIFECYCLE</emphasis>. The latter is the default, and is used to report build progress.
diff --git a/subprojects/docs/src/docs/userguide/mavenPlugin.xml b/subprojects/docs/src/docs/userguide/mavenPlugin.xml
index 4848515..3d0f705 100644
--- a/subprojects/docs/src/docs/userguide/mavenPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/mavenPlugin.xml
@@ -22,7 +22,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Maven plugin, include in your build script:</para>
+        <para>To use the Maven plugin, include the following in your build script:</para>
         <sample id="useMavenPlugin" dir="maven/quickstart" title="Using the Maven plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -158,25 +158,25 @@
                 <literal>uploadArchives</literal>
                 task will generate the POM and deploys the artifact and the POM to the specified repository.
             </para>
-            <para>There is some more work to do if you need support for other protocols than <literal>file</literal>. In
-                this case the native Maven code we delegate to needs additional libraries. Which libraries depend on the
-                protocol you need. The available protocols and the corresponding libraries are listed in <xref
-                        linkend='wagonLibs'/> (those libraries have again transitive dependencies which have transitive
+            <para>There is more work to do if you need support for protocols other than <literal>file</literal>. In
+                this case the native Maven code we delegate to needs additional libraries. Which libraries are needed depends on what
+                protocol you plan to use. The available protocols and the corresponding libraries are listed in <xref
+                        linkend='wagonLibs'/> (those libraries have transitive dependencies which have transitive
                 dependencies).
                 <footnote>
                     <para>It is planned for a future release to provide out-of-the-box support for this
                     </para>
                 </footnote>
-                For example to use the ssh protocol you can do:
+                For example, to use the ssh protocol you can do:
             </para>
             <sample id="uploadWithSsh" dir="userguide/artifacts/maven" title="Upload of file via SSH">
                 <sourcefile file="build.gradle" snippet="upload-with-ssh"/>
             </sample>
             <para>There are many configuration options for the Maven deployer. The configuration is done via a Groovy builder.
                 All the elements of this tree are Java beans. To configure the simple attributes you pass a map to the bean
-                elements. To add another bean elements to its parent, you use a closure. In the example above <emphasis>repository</emphasis>
+                elements. To add bean elements to its parent, you use a closure. In the example above <emphasis>repository</emphasis>
                 and <emphasis>authentication</emphasis> are such bean elements. <xref linkend='deployerConfig'/>
-                lists the available bean elements and a link to the javadoc of the corresponding class. In the javadoc you
+                lists the available bean elements and a link to the Javadoc of the corresponding class. In the Javadoc you
                 can see the possible attributes you can set for a particular element.
             </para>
             <para>In Maven you can define repositories and optionally snapshot repositories. If no snapshot repository
@@ -236,7 +236,7 @@
                 <tr>
                     <td>repository</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/RemoteRepository.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/RemoteRepository.html'>
                             org.apache.maven.artifact.ant.RemoteRepository
                         </ulink>
                     </td>
@@ -244,7 +244,7 @@
                 <tr>
                     <td>authentication</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/Authentication.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/Authentication.html'>
                             org.apache.maven.artifact.ant.Authentication
                         </ulink>
                     </td>
@@ -252,7 +252,7 @@
                 <tr>
                     <td>releases</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/RepositoryPolicy.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/RepositoryPolicy.html'>
                             org.apache.maven.artifact.ant.RepositoryPolicy
                         </ulink>
                     </td>
@@ -260,7 +260,7 @@
                 <tr>
                     <td>snapshots</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/RepositoryPolicy.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/RepositoryPolicy.html'>
                             org.apache.maven.artifact.ant.RepositoryPolicy
                         </ulink>
                     </td>
@@ -268,7 +268,7 @@
                 <tr>
                     <td>proxy</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/Proxy.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/Proxy.html'>
                             org.apache.maven.artifact.ant.Proxy
                         </ulink>
                     </td>
@@ -276,7 +276,7 @@
                 <tr>
                     <td>snapshotRepository</td>
                     <td>
-                        <ulink url='http://maven.apache.org/ant-tasks/apidocs/org/apache/maven/artifact/ant/RemoteRepository.html'>
+                        <ulink url='http://www.docjar.com/docs/api/org/apache/maven/artifact/ant/RemoteRepository.html'>
                             org.apache.maven.artifact.ant.RemoteRepository
                         </ulink>
                     </td>
@@ -331,7 +331,7 @@
             </para>
             <note>
                 <para>
-                    When you set <literal>archiveTask.baseName</literal> to a value other than the default, make sure to set
+                    When you set the “<literal>archiveTask.baseName</literal>” property to a value other than the default, you'll also have to set
                     <literal>uploadTask.repositories.mavenDeployer.pom.artifactId</literal> to the same value. Otherwise, the project at hand may
                     be referenced with the wrong artifact ID from generated POMs for other projects in the same build.
                 </para>
@@ -356,10 +356,10 @@
             <sample id="pomBuilder" dir="maven/pomGeneration" title="Modifying auto-generated content">
                 <sourcefile file="build.gradle" snippet="when-configured"/>
             </sample>
-            <para>If you have more than one artifact to publish, things work a little bit differently. See<xref
+            <para>If you have more than one artifact to publish, things work a little bit differently. See <xref
                         linkend="sub:multiple_artifacts_per_project"/>.
             </para>
-            <para>To customize the settings for the Maven installer (see<xref
+            <para>To customize the settings for the Maven installer (see <xref
                     linkend='sub:installing_to_the_local_repository'/>), you can do:
             </para>
             <sample id="customizeInstaller" dir="userguide/artifacts/maven" title="Customization of Maven installer">
@@ -388,7 +388,7 @@
                     Java and War plugin and the Maven scopes. Most
                     of the time you don't need to touch this and you can safely skip this section. The mapping
                     works like the following. You can map a configuration to one and only one scope. Different
-                    configurations can be mapped to one or different scopes. One can assign also a priority to a particular
+                    configurations can be mapped to one or different scopes. You can also assign a priority to a particular
                     configuration-to-scope mapping. Have a look at
                     <apilink class="org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer"/>
                     to learn more. To access the mapping configuration you can say:
@@ -404,4 +404,4 @@
         </section>
     </section>
     
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/multiproject.xml b/subprojects/docs/src/docs/userguide/multiproject.xml
index 061b6fa..bff3456 100644
--- a/subprojects/docs/src/docs/userguide/multiproject.xml
+++ b/subprojects/docs/src/docs/userguide/multiproject.xml
@@ -18,62 +18,75 @@
     <para>The powerful support for multi-project builds is one of Gradle's unique selling points. This topic is also the
         most intellectually challenging.
     </para>
+    <para>
+        A multi-project build in gradle consists of one root project, and one or more subprojects that may also have subprojects.
+    </para>
     <section id='sec:cross_project_configuration'>
         <title>Cross project configuration</title>
-        <para>Let's start with a very simple multi-project build. After all Gradle is a general purpose build tool at
-            its core, so the projects don't have to be java projects. Our first examples are about marine life.
+        <para>
+            While each subproject could configure itself in complete isolation of the other subprojects, it is
+            common that subprojects share common traits. It is then usually preferable to share configurations
+            among projects, so the same configuration affects several subprojects.
+        </para>
+        <para>
+            Let's start with a very simple multi-project build. Gradle is a general purpose build tool at
+            its core, so the projects don't have to be Java projects. Our first examples are about marine life.
         </para>
         <section>
             <title>Configuration and execution</title>
             <para><xref linkend="sec:build_phases"/> describes the phases of every Gradle build.
-                Let's zoom into configuration and execution phases of a multi-project build.
-                The configuration of all projects happens before any task is executed.
+                Let's zoom into the configuration and execution phases of a multi-project build.
+                Configuration here means executing the <literal>build.gradle</literal> file of a project, which
+                implies e.g. downloading all plugins that were declared using '<literal>apply plugin</literal>'.
+                By default, the configuration of all projects happens before any task is executed.
                 This means that when a single task, from a single project is requested,
                 <emphasis>all</emphasis> projects of multi-project build are configured first.
                 The reason every project needs to be configured is to support
-                the flexibility of accessing and changing any part of Gradle project model.
+                the flexibility of accessing and changing any part of the Gradle project model.
             </para>
             <section id='sec:configuration_on_demand'>
                 <title>Configuration on demand</title>
-                <para>Configuration injection feature and access to the complete project model
+                <para>The <firstterm>Configuration injection</firstterm> feature and access to the complete project model
                     are possible because every project is configured before the execution phase.
-                    Yet, this approach may not be the most efficient in a very large multi-project builds.
+                    Yet, this approach may not be the most efficient in a very large multi-project build.
                     There are Gradle builds with a hierarchy of hundreds of subprojects.
-                    Configuration time of huge multi-project builds may become noticeable.
-                    Scalability is an important requirement for Gradle. Hence, starting from version 1.4
-                    new incubating 'configuration on demand' mode is introduced.</para>
+                    The configuration time of huge multi-project builds may become noticeable.
+                    Scalability is an important requirement for Gradle.
+                    Hence, starting from version 1.4 a new incubating 'configuration on demand' mode is introduced.
+                </para>
                 <para>
-                    Configuration on demand mode attempts to configure only projects that are relevant for requested tasks.
-                    This way, the configuration time of a large multi-project build is greatly improved.
+                    Configuration on demand mode attempts to configure only projects that are relevant for requested
+                    tasks, i.e. it only executes the <literal>build.gradle</literal> file of projects that are participating in the build.
+                    This way, the configuration time of a large multi-project build can be reduced.
                     In the long term, this mode will become the default mode, possibly the only mode for Gradle build execution.
                     The configuration on demand feature is incubating so not every build is guaranteed to work correctly.
                     The feature should work very well for multi-project builds that have decoupled projects (<xref linkend="sec:decoupled_projects"/>).
-                    In configuration on demand mode projects are configured as follows:
+                    In “configuration on demand” mode, projects are configured as follows:
                     <itemizedlist>
-                        <listitem>Root project is always configured.
+                        <listitem>The root project is always configured.
                             This way the typical common configuration is supported (allprojects or subprojects script blocks).</listitem>
-                        <listitem>Project in the directory where the build is executed is also configured, but only when Gradle is executed without any tasks.
+                        <listitem>The project in the directory where the build is executed is also configured, but only when Gradle is executed without any tasks.
                             This way the default tasks behave correctly when projects are configured on demand.</listitem>
                         <listitem>The standard project dependencies are supported and makes relevant projects configured.
-                            If project A has a compile dependency on project B then building A causes configuration of both projects: A and B.</listitem>
-                        <listitem>The task dependencies declared via task path are supported and cause relevant projects configured.
+                            If project A has a compile dependency on project B then building A causes configuration of both projects.</listitem>
+                        <listitem>The task dependencies declared via task path are supported and cause relevant projects to be configured.
                             Example: someTask.dependsOn(":someOtherProject:someOtherTask")
                         </listitem>
-                        <listitem>Task requested via task path from the command line (or Tooling API) causes the relevant project configured.
-                            Building 'projectA:projectB:someTask' causes configuration of projectB.
+                        <listitem>A task requested via task path from the command line (or Tooling API) causes the relevant project to be configured.
+                            For example, building 'projectA:projectB:someTask' causes configuration of projectB.
                         </listitem>
                     </itemizedlist>
                 </para>
                 <para>
                     Eager to try out this new feature? To configure on demand with every build run see <xref linkend="sec:gradle_configuration_properties"/>.
-                    To configure on demand just for given build please see <xref linkend="gradle_command_line"/>.
+                    To configure on demand just for a given build please see <xref linkend="gradle_command_line"/>.
                 </para>
             </section>
         </section>
         <section>
             <title>Defining common behavior</title>
-            <para>We have the following project tree. This is a multi-project build with a root project
-                <literal>water</literal> and a subproject <literal>bluewhale</literal>.
+            <para>Let's look at some examples with the following project tree. This is a multi-project build with a root project
+                named <literal>water</literal> and a subproject named <literal>bluewhale</literal>.
             </para>
             <sample id="multiprojectFirstExample" dir="userguide/multiproject/firstExample/water" includeLocation="true" title="Multi-project tree - water & bluewhale projects">
                 <layout>
@@ -129,6 +142,10 @@
                 of defining common behavior. We think it provides a very powerful and flexible way of configuring
                 multiproject builds.
             </para>
+            <para>
+                Another possibilty for sharing configuration is to use a common external script.
+                See <xref linkend="sec:configuring_using_external_script"/> for more information.
+            </para>
         </section>
     </section>
     <section id='sec:subproject_configuration'>
@@ -137,10 +154,16 @@
         </para>
         <section>
             <title>Defining common behavior</title>
-            <sample id="multiprojectUseSubprojects" dir="userguide/multiproject/useSubprojects/water" title="Defining common behaviour of all projects and subprojects">
+            <sample id="multiprojectUseSubprojects" dir="userguide/multiproject/useSubprojects/water" title="Defining common behavior of all projects and subprojects">
                 <sourcefile file="build.gradle"/>
                 <output args="-q hello"/>
             </sample>
+            <para>
+                You may notice that there are two code snippets referencing the “<literal>hello</literal>” task.
+                The first one, which uses the “<literal>task</literal>” keyword, constructs the task and provides it's base configuration.
+                The second piece doesn't use the “<literal>task</literal>” keyword, as it is further configuring the existing “<literal>hello</literal>” task.
+                You may only construct a task once in a project, but you may any number of code blocks providing additional configuration.
+            </para>
         </section>
         <section id='sub:adding_specific_behavior'>
             <title>Adding specific behavior</title>
@@ -226,7 +249,7 @@
     </section>
     <section id='sec:execution_rules_for_multi_project_builds'>
         <title>Execution rules for multi-project builds</title>
-        <para>When we have executed the <literal>hello</literal> task from the root project dir things behaved in an
+        <para>When we executed the <literal>hello</literal> task from the root project dir, things behaved in an
             intuitive way. All the <literal>hello</literal> tasks of the different projects were executed. Let's switch
             to the <literal>bluewhale</literal> dir and see what happens if we execute Gradle from there.
         </para>
@@ -236,7 +259,7 @@
         <para>The basic rule behind Gradle's behavior is simple. Gradle looks down the hierarchy, starting with the
             <emphasis>current dir</emphasis>, for tasks with the name
             <literal>hello</literal>
-            an executes them. One thing is very important to note. Gradle
+            and executes them. One thing is very important to note. Gradle
             <emphasis>always</emphasis>
             evaluates
             <emphasis>every</emphasis>
@@ -251,7 +274,7 @@
             <sourcefile file="krill/build.gradle"/>
             <output args="-q distanceToIceberg"/>
         </sample>
-        <para>Here the output without the <literal>-q</literal> option:</para>
+        <para>Here's the output without the <literal>-q</literal> option:</para>
         <sample id="multiprojectPartialTasksNotQuiet" dir="userguide/multiproject/partialTasks/water" title="Evaluation and execution of projects">
             <output args="distanceToIceberg"/>
         </sample>
@@ -272,37 +295,26 @@
         </sample>
         <para>The build is executed from the <literal>tropicalFish</literal> project. We execute the <literal>hello</literal>
             tasks of the <literal>water</literal>, the <literal>krill</literal> and the <literal>tropicalFish</literal>
-            project. The first two tasks are specified by there absolute path, the last task is executed on the name
+            project. The first two tasks are specified by their absolute path, the last task is executed using the name
             matching mechanism described above.
         </para>
     </section>
     <section id='sec:project_and_task_paths'>
         <title>Project and task paths</title>
-        <para>A project path has the following pattern: It starts always with a colon, which denotes the root project.
-            The root project is the only project in a path that is not specified by its name. The path
-            <literal>:bluewhale</literal>
-            corresponds to the file system path
-            <literal>water/bluewhale</literal>
-            in the case of the example above.
-        </para>
-        <para>The path of a task is simply its project path plus the task name. For example <literal>
-            :bluewhale:hello</literal>. Within a project you can address a task of the same project just by its name.
-            This is interpreted as a relative path.
+        <para>A project path has the following pattern: It starts
+        with an optional colon, which denotes the root project.  The root
+        project is the only project in a path that is not specified by
+        its name. The rest of a project path is a colon-separated
+        sequence of project names, where the next project is a subproject of the previous project.
         </para>
-        <para>Originally Gradle has used the
-            <literal>'/'</literal>
-            character as a natural path separator. With the introduction of directory tasks (see <xref
-                    linkend='sec:directory_creation'/>) this was no longer possible, as the name of the directory task
-            contains the
-            <literal>'/'</literal>
-            character.
+        <para>The path of a task is simply its project path plus the task name, like “<literal>:bluewhale:hello</literal>”. Within a project you can address a task of the same project just by its name.
+            This is interpreted as a relative path.
         </para>
     </section>
     <section id='sec:dependencies_which_dependencies'>
         <title>Dependencies - Which dependencies?</title>
         <para>The examples from the last section were special, as the projects had no <emphasis>Execution
-            Dependencies</emphasis>. They had only <emphasis>Configuration Dependencies</emphasis>. Here is an example
-            where this is different:
+            Dependencies</emphasis>. They had only <emphasis>Configuration Dependencies</emphasis>. The following sections illustrate the differences between these two types of dependencies.
         </para>
         <section id='sub:execution_time_dependencies'>
             <title>Execution dependencies</title>
@@ -319,11 +331,11 @@
                     <sourcefile file="producer/build.gradle"/>
                     <output args="-q action"/>
                 </sample>
-                <para>This did not work out. If nothing else is defined, Gradle executes the task in alphanumeric order.
-                    Therefore
-                    <literal>:consumer:action</literal>
-                    is executed before <literal>:producer:action</literal>. Let's try to solve this with a hack and
-                    rename the producer project to <literal>aProducer</literal>.
+                <para>This didn't quite do what we want. If nothing else is defined, Gradle executes the task in alphanumeric order.
+                    Therefore, Gradle will execute
+                    “<literal>:consumer:action</literal>”
+                    before “<literal>:producer:action</literal>”. Let's try to solve this with a hack and
+                    rename the producer project to “<literal>aProducer</literal>”.
                 </para>
                 <sample id="multiprojectMessagesHack" dir="userguide/multiproject/dependencies/messagesHack/messages" title="Dependencies and execution order">
                     <layout>
@@ -336,20 +348,20 @@
                     <sourcefile file="consumer/build.gradle"/>
                     <output args="-q action"/>
                 </sample>
-                <para>Now we take the air out of this hack. We simply switch to the <literal>consumer</literal> dir and
+                <para>We can show where this hack doesn't work if we now switch to the <literal>consumer</literal> dir and
                     execute the build.
                 </para>
                 <sample id="multiprojectMessagesHackBroken" dir="userguide/multiproject/dependencies/messagesHack/messages/consumer" title="Dependencies and execution order">
                     <output args="-q action"/>
                 </sample>
-                <para>For Gradle the two
-                    <literal>action</literal>
-                    tasks are just not related. If you execute the build from the
-                    <literal>messages</literal>
+                <para>The problem is that the two
+                    “<literal>action</literal>”
+                    tasks are unrelated. If you execute the build from the
+                    “<literal>messages</literal>”
                     project Gradle executes them both because they have the same name and they are down the hierarchy.
                     In the last example only one
-                    <literal>action</literal>
-                    was down the hierarchy and therefore it was the only task that got executed. We need something
+                    “<literal>action</literal>” task
+                    was down the hierarchy and therefore it was the only task that was executed. We need something
                     better than this hack.
                 </para>
             </section>
@@ -370,12 +382,12 @@
                 <sample id="multiprojectMessagesDependenciesSubBuild" dir="userguide/multiproject/dependencies/messagesWithDependencies/messages/consumer" title="Declaring dependencies">
                     <output args="-q action"/>
                 </sample>
-                <para>We have now declared that the
-                    <literal>action</literal> task in the <literal>consumer</literal>
+                <para>This is now working better because we have declared that the
+                    “<literal>action</literal>” task in the “<literal>consumer</literal>”
                     project has an
                     <emphasis>execution dependency</emphasis>
-                    on the <literal>action</literal> task on the
-                    <literal>producer</literal>
+                    on the “<literal>action</literal>” task in the
+                    “<literal>producer</literal>”
                     project.
                 </para>
             </section>
@@ -393,10 +405,10 @@
         </section>
         <section id='sub:configuration_time_dependencies'>
             <title>Configuration time dependencies</title>
-            <para>Let's have one more example with our producer-consumer build before we enter
+            <para>Let's see one more example with our producer-consumer build before we enter
                 <emphasis>Java</emphasis>
-                land. We add a property to the producer project and create now a configuration time dependency from
-                consumer on producer.
+                land. We add a property to the “<literal>producer</literal>” project and create a configuration time dependency from
+                “<literal>consumer</literal>” to “<literal>producer</literal>”.
             </para>
             <sample id="multiprojectMessagesConfigDependenciesBroken" dir="userguide/multiproject/dependencies/messagesConfigDependenciesBroken/messages" title="Configuration time dependencies">
                 <sourcefile file="consumer/build.gradle"/>
@@ -405,31 +417,29 @@
             </sample>
             <para>The default
                 <emphasis>evaluation</emphasis>
-                order of the projects is alphanumeric (for the same nesting level). Therefore the
-                <literal>consumer</literal>
+                order of projects is alphanumeric (for the same nesting level). Therefore the
+                “<literal>consumer</literal>”
                 project is evaluated before the
-                <literal>producer</literal>
+                “<literal>producer</literal>”
                 project and the
-                <literal>key</literal>
-                value of the
-                <literal>producer</literal>
-                is set
+                “<literal>producerMessage</literal>”
+                value is set
                 <emphasis>after</emphasis>
                 it is read by the
-                <literal>consumer</literal>
+                “<literal>consumer</literal>”
                 project. Gradle offers a solution for this.
             </para>
             <sample id="multiprojectMessagesConfigDependencies" dir="userguide/multiproject/dependencies/messagesConfigDependencies/messages" title="Configuration time dependencies - evaluationDependsOn">
                 <sourcefile file="consumer/build.gradle"/>
                 <output args="-q consume"/>
             </sample>
-            <para>The command
-                <literal>evaluationDependsOn</literal>
-                triggers the evaluation of
-                <literal>producer</literal>
-                <emphasis>before</emphasis>
-                <literal>consumer</literal>
-                is evaluated. The example is a bit contrived for the sake of showing the mechanism. In
+            <para>The use of the 
+                “<literal>evaluationDependsOn</literal>” command
+                results in the evaluation of
+                the “<literal>producer</literal>” project
+                <emphasis>before</emphasis> the
+                “<literal>consumer</literal>” project
+                is evaluated. This example is a bit contrived to show the mechanism. In
                 <emphasis>this</emphasis>
                 case there would be an easier solution by reading the key property at execution time.
             </para>
@@ -437,14 +447,12 @@
                 <sourcefile file="consumer/build.gradle"/>
                 <output args="-q consume"/>
             </sample>
-            <para>Configuration dependencies are very different to execution dependencies. Configuration dependencies
-                are between projects whereas execution dependencies are always resolved to task dependencies. Another
-                difference is that always all projects are configured, even when you start the build from a subproject.
+            <para>Configuration dependencies are very different from execution dependencies. Configuration dependencies
+                are between projects whereas execution dependencies are always resolved to task dependencies. Also note that all projects are always configured, even when you start the build from a subproject.
                 The default configuration order is top down, which is usually what is needed.
             </para>
             <para>
-                To change the the default configuration order to be bottom up, That means that a project configuration
-                depends on the configuration of its child projects, the <literal>evaluationDependsOnChildren()</literal> method can be used.
+                To change the default configuration order to “bottom up”, use the “<literal>evaluationDependsOnChildren()</literal>” method instead.
             </para>
             <para>On the same nesting level the configuration order depends on the alphanumeric position. The most
                 common use case is to have multi-project builds that share a common lifecycle (e.g. all projects use the
@@ -452,7 +460,7 @@
                 <literal>dependsOn</literal>
                 a
                 <emphasis>execution dependency</emphasis>
-                between different projects, the default behavior of this method is to create also a
+                between different projects, the default behavior of this method is to also create a
                 <emphasis>configuration</emphasis>
                 dependency between the two projects. Therefore it is likely that you don't have to define configuration
                 dependencies explicitly.
@@ -460,9 +468,7 @@
         </section>
         <section id='sub:real_life_examples'>
             <title>Real life examples</title>
-            <para>Gradle's multi-project features are driven by real life use cases. The first example for describing
-                such a use case, consists of two webapplication projects and a parent project that creates a
-                distribution out of them.
+            <para>Gradle's multi-project features are driven by real life use cases. One good example consists of two web application projects and a parent project that creates a distribution including the two web applications.
                 <footnote>
                     <para>The real use case we had, was using <ulink url='http://lucene.apache.org/solr'/>, where you
                         need a separate war for each index you are accessing. That was one reason why we have created a
@@ -510,15 +516,15 @@
                 because it needs to know the <literal>archivePath</literal>. But it asks for this information at
                 <emphasis>execution time</emphasis>. Therefore we have no circular dependency.
             </para>
-            <para>Such and other dependency patterns are daily bread in the problem space of multi-project builds. If a
-                build system does not support such patterns, you either can't solve your problem or you need to do ugly
-                hacks which are hard to maintain and massively afflict your productivity as a build master.
+            <para>Such dependency patterns are daily bread in the problem space of multi-project builds. If a
+                build system does not support these patterns, you either can't solve your problem or you need to do ugly
+                hacks which are hard to maintain and massively impair your productivity as a build master.
             </para>
         </section>
     </section>
     <section id='sec:project_jar_dependencies'>
         <title>Project lib dependencies</title>
-        <para>What if one projects needs the jar produced by another project in its compile path? And not just the jar
+        <para>What if one project needs the jar produced by another project in its compile path, and not just the jar
             but also the transitive dependencies of this jar? Obviously this is a very common use case for Java
             multi-project builds. As already mentioned in <xref linkend='sub:project_dependencies'/>, Gradle
             offers project lib dependencies for this.
@@ -541,12 +547,12 @@
                 shared/src/main/java/org/gradle/sample/shared/Helper.java
             </layout>
         </sample>
-        <para>We have the projects <literal>shared</literal>, <literal>api</literal> and<literal>personService</literal>.
-            <literal>personService</literal> has a lib dependency on the other two projects. <literal>api</literal>
-            has a lib dependency on <literal>shared</literal>.
+        <para>We have the projects “<literal>shared</literal>”, “<literal>api</literal>” and “<literal>personService</literal>”.
+            The “<literal>personService</literal>” project has a lib dependency on the other two projects. The “<literal>api</literal>” project
+            has a lib dependency on the “<literal>shared</literal>” project.
             <footnote>
                 <para>
-                    <literal>services</literal>
+                    “<literal>services</literal>”
                     is also a project, but we use it just as a container. It has no build script and gets nothing
                     injected by another build script.
                 </para>
@@ -557,21 +563,21 @@
             <sourcefile file="build.gradle"/>
         </sample>
         <para>All the build logic is in the
-            <literal>build.gradle</literal> of the root project.
+            “<literal>build.gradle</literal>” file of the root project.
             <footnote>
                 <para>We do this here, as it makes the layout a bit easier. We usually put the project specific stuff
                     into the build script of the respective projects.
                 </para>
             </footnote>
-            A <emphasis>lib</emphasis>
+            A “<emphasis>lib</emphasis>”
             dependency is a special form of an execution dependency. It causes the other project to be built first and
             adds the jar with the classes of the other project to the classpath. It also adds the dependencies of the
             other project to the classpath. So you can enter the
-            <literal>api</literal>
-            directory and trigger a <userinput>gradle compile</userinput>. First
-            <literal>shared</literal>
+            “<literal>api</literal>”
+            directory and trigger a “<userinput>gradle compile</userinput>”. First
+            the “<literal>shared</literal>” project
             is built and then
-            <literal>api</literal>
+            the “<literal>api</literal>” project
             is built. Project dependencies enable partial multi-project builds.
         </para>
         <para>If you come from Maven land you might be perfectly happy with this. If you come from Ivy land, you might
@@ -584,11 +590,11 @@
             example we create an
             <emphasis>additional</emphasis>
             library containing only the interfaces of the
-            <literal>api</literal>
+            “<literal>api</literal>”
             project. We assign this library to a new <emphasis>dependency configuration</emphasis>. For the person
             service we declare that the project should be compiled only against the
-            <literal>api</literal>
-            interfaces but tested with all classes from <literal>api</literal>.
+            “<literal>api</literal>”
+            interfaces but tested with all classes from “<literal>api</literal>”.
         </para>
         <section id="disable_dependency_projects">
             <title>Disabling the build of dependency projects</title>
@@ -625,9 +631,9 @@
             </itemizedlist>
         </para>
         <para>
-            How does the parallel execution work? First, you need to tell Gradle to use the parallel mode.
+            How does parallel execution work? First, you need to tell Gradle to use the parallel mode.
             You can use the command line argument (<xref linkend='gradle_command_line'/>) or configure your build environment (<xref linkend="sec:gradle_configuration_properties"/>).
-            Unless you provide specific number of parallel threads Gradle attempts to choose the right number based on available CPU cores.
+            Unless you provide a specific number of parallel threads Gradle attempts to choose the right number based on available CPU cores.
             Every parallel worker exclusively owns a given project while executing a task.
             This means that 2 tasks from the same project are never executed in parallel.
             Therefore only multi-project builds can take advantage of parallel execution.
@@ -638,26 +644,41 @@
     </section>
     <section id="sec:decoupled_projects">
         <title>Decoupled Projects</title>
-        <para>Gradle allows any project to access any other project during both the configuration and execution phases. While this provides a great deal of power
-            and flexibility to the build author, it also limits the flexibility that Gradle has when building those projects. For instance, this tight <emphasis>coupling</emphasis> of projects
-            effectively prevents Gradle from building multiple projects in parallel, or from substituting a pre-built artifact in place of a project dependency.
+        <para>Gradle allows any project to access any other project during both the configuration and execution phases.
+            While this provides a great deal of power and flexibility to the build author, it also limits the flexibility that Gradle has when building those projects.
+            For instance, this effectively prevents Gradle from correctly building multiple projects in parallel, configuring only a subset of projects, or from substituting
+            a pre-built artifact in place of a project dependency.
         </para>
         <para>Two projects are said to be <emphasis>decoupled</emphasis> if they do not directly access each other's project model. Decoupled projects may only interact in terms of
             declared dependencies: project dependencies (<xref linkend='sub:project_dependencies'/>) and/or task dependencies (<xref linkend='sec:task_dependencies'/>).
             Any other form of project interaction (i.e. by modifying another project object or by reading a value from another project object) causes the projects to
-            be coupled.
+            be coupled. The consequence of coupling during the configuration phase is that if gradle is invoked with the 'configuration on demand' option, the result of the build can be flawed
+            in several ways. The consequence of coupling during execution phase is that if gradle is invoked with the parallel option,
+            one project task runs too late to influence a task of a project building in parallel.
+            Gradle does not attempt to detect coupling and warn the user, as there are too many possibilities to introduce coupling.
         </para>
         <para>
             A very common way for projects to be coupled is by using configuration injection (<xref linkend='sec:cross_project_configuration'/>). It may not be immediately apparent, but using key
-            Gradle features like the <literal>allprojects</literal> and <literal>subprojects</literal> keywords automatically cause your projects to be coupled. This is
-            because these keywords are used in a <literal>build.gradle</literal> file, which defines a project. Often this is a "root project" that does nothing more than
+            Gradle features like the <literal>allprojects</literal> and <literal>subprojects</literal> keywords automatically cause your projects to be coupled.
+            This is because these keywords are used in a <literal>build.gradle</literal> file, which defines a project. Often this is a “root project” that does nothing more than
             define common configuration, but as far as Gradle is concerned this root project is still a fully-fledged project, and by using <literal>allprojects</literal>
-            that project is effectively coupled to all other projects.
+            that project is effectively coupled to all other projects. Coupling of the root project to subprojects does not impact 'configuration on demand', but using the
+            <literal>allprojects</literal> and <literal>subprojects</literal> in any subproject's <literal>build.gradle</literal> file will have an impact.
         </para>
         <para>This means that using any form of shared build script logic or configuration injection (<literal>allprojects</literal>, <literal>subprojects</literal>, etc.)
             will cause your projects to be coupled. As we extend the concept of project decoupling and provide features that take advantage of decoupled projects,
             we will also introduce new features to help you to solve common use cases (like configuration injection) without causing your projects to be coupled.
         </para>
+        <para>In order to make good use of cross project configuration without running into issues for parallel and 'configuration on demand' options, follow these recommendations:
+        <itemizedlist>
+            <listitem>
+                Avoid a subproject's <literal>build.gradle</literal> referencing other subprojects; prefering cross configuration from the root project.
+            </listitem>
+            <listitem>
+                Avoid changing the configuration of other projects at execution time.
+            </listitem>
+        </itemizedlist>
+        </para>
     </section>
     <section id="sec:multiproject_build_and_test">
         <title>Multi-Project Building and Testing</title>
@@ -666,11 +687,10 @@
             you may often want to do all of these tasks across a range of projects.  The <literal>buildNeeded</literal>
             and <literal>buildDependents</literal> tasks can help with this.
         </para>
-        <para>Let's use the project structure shown in <xref linkend='javadependencies_2'/>.  In this
-            example :services:personservice depends on both :api and :shared.  The :api project also depends on
-            :shared.
+        <para>Look at <xref linkend='javadependencies_2'/>.  In this
+            example, the “<literal>:services:personservice</literal>” project depends on both the “<literal>:api</literal>” and “<literal>:shared</literal>” projects.  The “<literal>:api</literal>” project also depends on the “<literal>:shared</literal>” project.
         </para>
-        <para>Assume you are working on a single project, the :api project.  You have been making changes, but
+        <para>Assume you are working on a single project, the “<literal>:api</literal>” project.  You have been making changes, but
             have not built the entire project since performing a clean.  You want to build any necessary supporting
             jars, but only perform code quality and unit tests on the project you have changed.
             The <literal>build</literal> task does this.
@@ -680,9 +700,9 @@
         </sample>
 
         <para>While you are working in a typical development cycle repeatedly building and testing changes to
-            the :api project (knowing that you are only changing files in this one project), you may not want to
-            even suffer the expense of :shared:compile checking to see what has changed in the :shared project.
-            Adding the <literal>-a</literal> option will cause Gradle to use cached jars to resolve any project lib
+            the “<literal>:api</literal>” project (knowing that you are only changing files in this one project), you may not want to
+            even suffer the expense of building “<literal>:shared:compile</literal>” to see what has changed in the “<literal>:shared</literal>” project.
+            Adding the “<literal>-a</literal>” option will cause Gradle to use cached jars to resolve any project lib
             dependencies and not try to re-build the depended on projects.
         </para>
         <sample id="multitestingBuildDashA" dir="userguide/multiproject/dependencies/java" title="Partial Build and Test Single Project">
@@ -691,7 +711,7 @@
         </sample>
 
         <para>If you have just gotten the latest version of source from your version control system which included changes
-            in other projects that :api depends on, you might want to not only build all the projects you depend on,
+            in other projects that “<literal>:api</literal>” depends on, you might want to not only build all the projects you depend on,
             but test them as well. The <literal>buildNeeded</literal> task also tests all the projects from the
             project lib dependencies of the testRuntime configuration.
         </para>
@@ -699,9 +719,9 @@
             <output args=":api:buildNeeded"/>
         </sample>
 
-        <para>You also might want to refactor some part of the :api project that is used in other projects.
-            If you make these types of changes, it is not sufficient to test just the :api
-            project, you also need to test all projects that depend on the :api project.
+        <para>You also might want to refactor some part of the “<literal>:api</literal>” project that is used in other projects.
+            If you make these types of changes, it is not sufficient to test just the “<literal>:api</literal>”
+            project, you also need to test all projects that depend on the “<literal>:api</literal>” project.
             The <literal>buildDependents</literal> task also tests all the projects that have a project lib dependency
             (in the testRuntime configuration) on the specified project.
         </para>
@@ -710,8 +730,16 @@
         </sample>
         <para>Finally, you may want to build and test everything in all projects. Any task you run in the root project folder
         will cause that same named task to be run on all the children. So you can just run
-        <literal>gradle build</literal> to build and test all projects.
+        “<literal>gradle build</literal>” to build and test all projects.
+        </para>
+    </section>
+    <section>
+        <title>Multi Project and buildSrc</title>
+        <para>
+            <xref linkend="sec:build_sources"/> tells us that we can place build logic to be compiled and tested in the special <literal>buildSrc</literal> directory.
+            In a multi project build, there can only be one <literal>buildSrc</literal> directory which must be located in the root directory.
         </para>
+
     </section>
     <section id='sec:property_and_method_inheritance'>
         <title>Property and method inheritance</title>
@@ -745,7 +773,7 @@
                 </para>
             </footnote>
             With those elements, and keeping in mind that Gradle has a distinct configuration and execution phase, you
-            have already a lot of flexibility. But when you enter steep territory Gradle does not become an obstacle and
+            already have a lot of flexibility. But when you enter steep territory Gradle does not become an obstacle and
             usually accompanies and carries you to the top of the mountain.
         </para>
     </section>
diff --git a/subprojects/docs/src/docs/userguide/nativeBinaries.xml b/subprojects/docs/src/docs/userguide/nativeBinaries.xml
index d42f5eb..a20fee0 100755
--- a/subprojects/docs/src/docs/userguide/nativeBinaries.xml
+++ b/subprojects/docs/src/docs/userguide/nativeBinaries.xml
@@ -1,19 +1,3 @@
-<!--
-  ~ Copyright 2010 the original author or authors.
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
 <chapter id='nativeBinaries'>
     <title>Building native binaries</title>
 
@@ -24,25 +8,41 @@
     </note>
 
     <para>
-        The various native binary plugins add support for building native software components from C++, C, Objective-C, Objective-C++ and Assembler sources.
-        While many excellent build tools exist for this space of software development, Gradle offers developers it's trademark power and flexibility
-        together with the dependency management practices more traditionally found in the JVM development space.
+        The various native binary plugins add support for building native software components, such as executables
+        or shared libraries, from code written in C++, C and other languages. While many excellent build tools exist
+        for this space of software development, Gradle offers developers its trademark power and flexibility
+        together with dependency management practices more traditionally found in the JVM development space.
     </para>
 
+    <section>
+        <title>Supported languages</title>
+        <para>
+            The following source languages are currently supported:
+        </para>
+        <itemizedlist>
+            <listitem><para>C</para></listitem>
+            <listitem><para>C++</para></listitem>
+            <listitem><para>Objective-C</para></listitem>
+            <listitem><para>Objective-C++</para></listitem>
+            <listitem><para>Assembly</para></listitem>
+            <listitem><para>Windows resources</para></listitem>
+        </itemizedlist>
+    </section>
+
     <section id="native-binaries:tool-chain-support">
         <title>Tool chain support</title>
         <para>
-            Gradle offers the ability to execute the same build using different tool chains. You can control which tool chain will be used to build
-            by changing the operating system PATH to include the desired tool chain compiler. Alternatively, you can configure the tool chains directly,
-            as described in the `Native Binary Variants` section, below.
+            Gradle offers the ability to execute the same build using different tool chains. When you build a native binary, Gradle will attempt to locate a
+            tool chain installed on your machine that can build the binary. You can fine tune exactly how this works,
+            see <xref linkend="native_binaries:tool_chain"/> for details.
         </para>
         <para>
             The following tool chains are supported:
         </para>
         <table>
-            <thread>
+            <thead>
                 <tr><td>Operating System</td><td>Tool Chain</td><td>Notes</td></tr>
-            </thread>
+            </thead>
             <tr>
                 <td>Linux</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink></td> <td></td>
             </tr>
@@ -50,75 +50,187 @@
                 <td>Linux</td><td><ulink url="http://clang.llvm.org">Clang</ulink></td><td></td>
             </tr>
             <tr>
-                <td>Mac OS X</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink></td><td>Using GCC distributed with XCode.</td>
+                <td>Mac OS X</td><td>XCode</td><td>Uses the Clang tool chain bundled with XCode.</td>
             </tr>
             <tr>
-                <td>Mac OS X</td><td><ulink url="http://clang.llvm.org">Clang</ulink></td><td>Using Clang distributed with XCode.</td>
+                <td>Windows</td><td><ulink url="http://www.microsoft.com/visualstudio/en-us">Visual C++</ulink></td><td>Windows XP and later, Visual C++ 2010 and later.</td>
             </tr>
             <tr>
-                <td>Windows</td><td><ulink url="http://www.microsoft.com/visualstudio/en-us">Visual C++</ulink></td><td>Windows XP and later, Visual C++ 2010 and later.</td>
+                <td>Windows</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink> with <ulink url="http://cygwin.com">Cywin 32</ulink></td><td>Windows XP and later.</td>
             </tr>
             <tr>
-                <td>Windows</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink></td><td>Windows XP and later, using GCC distributed with Cygwin.</td>
+                <td>Windows</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink> with <ulink url="http://www.mingw.org/">MinGW</ulink></td><td>Windows XP and later. <ulink url="http://mingw-w64.sourceforge.net">Mingw-w64</ulink> is currently not supported.</td>
+            </tr>
+        </table>
+        <para>
+            The following tool chains are unofficially supported. They generally work fine, but are not tested continuously:
+        </para>
+        <table>
+            <thead>
+                <tr><td>Operating System</td><td>Tool Chain</td><td>Notes</td></tr>
+            </thead>
+            <tr>
+                <td>Mac OS X</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink> from Macports</td><td></td>
             </tr>
             <tr>
-                <td>Windows</td><td><ulink url="http://www.mingw.org/">MinGW</ulink></td><td>Windows XP and later.</td>
+                <td>Mac OS X</td><td><ulink url="http://clang.llvm.org">Clang</ulink> from Macports</td><td></td>
+            </tr>
+            <tr>
+                <td>Windows</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink> with <ulink url="http://cygwin.com">Cywin 64</ulink></td><td>Windows XP and later.</td>
+            </tr>
+            <tr>
+                <td>UNIX-like</td><td><ulink url="http://gcc.gnu.org/">GCC</ulink></td><td></td>
+            </tr>
+            <tr>
+                <td>UNIX-like</td><td><ulink url="http://clang.llvm.org">Clang</ulink></td><td></td>
             </tr>
         </table>
     </section>
+    <section>
+        <title>Tool chain installation</title>
+        <note>
+            <para>
+                Note that if you are using GCC then you currently need to install support for C++, even if you are not building from C++ source. This caveat will be removed in a future Gradle version.
+            </para>
+        </note>
+        <para>To build native binaries, you will need to have a compatible tool chain installed:
+        </para>
+        <section>
+            <title>Windows</title>
+            <para>To build on Windows, install a compatible version of Visual Studio. The native plugins will discover the Visual Studio installations
+                and select the latest version. There is no need to mess around with environment variables or batch scripts. This works fine from a Cygwin shell
+                or the Windows command-line.
+            </para>
+            <para>Alternatively, you can install Cygwin with GCC or MinGW. Clang is currently not supported.</para>
+        </section>
+        <section>
+            <title>OS X</title>
+            <para>To build on OS X, you should install XCode. The native plugins will discover the XCode installation using the system PATH.
+            </para>
+            <para>
+                The native plugins also work with GCC and Clang bundled with Macports. To use one of the Macports tool chains,
+                you will need to make the tool chain the default using the <userinput>port select</userinput> command and add Macports to the system PATH.
+            </para>
+        </section>
+        <section>
+            <title>Linux</title>
+            <para>To build on Linux, install a compatible version of GCC or Clang. The native plugins will discover GCC or Clang using the system PATH.
+            </para>
+        </section>
+    </section>
 
     <section>
         <title>Component model</title>
         <para>
-            A native binary project defines a set of <apilink class="org.gradle.nativebinaries.Executable"/> and <apilink class="org.gradle.nativebinaries.Library"/> components,
-            each of which Gradle maps to a number of <apilink class="org.gradle.nativebinaries.NativeBinary"/> outputs.
-            For each <literal>executable</literal> or <literal>library</literal> defined, Gradle adds a <apilink class="org.gradle.language.base.FunctionalSourceSet"/> with the same name.
-            Each of these functional source sets will contain a language-specific source set for each of the languages supported by the project.
+            To build native binaries using Gradle, your project should define one or more <firstterm>native components</firstterm>. Each component represents either an
+            executable or a library that Gradle should build. A project can define any number of components. Gradle does not define any components by default.
+        </para>
+        <para>
+            For each component, Gradle defines a <firstterm>source set</firstterm> for each language that the component can be built from. A source set is essentially
+            just a set of source directories containing source files. For example, when you apply the <literal>c</literal> plugin and define a library called
+            <literal>helloworld</literal>, Gradle will define, by default, a source set containing the C source files in the <literal>src/helloworld/c</literal> directory.
+            It will use these source files to build the <literal>helloworld</literal> library.
+            This is described in more detail below.
+        </para>
+        <para>
+            For each component, Gradle defines one or more <firstterm>binaries</firstterm> as output. To build a binary, Gradle will take the source files defined for
+            the component, compile them as appropriate for the source language, and link the result into a binary file. For an executable component, Gradle can
+            produce executable binary files. For a library component, Gradle can produce both static and shared library binary files. For example, when you define
+            a library called <literal>helloworld</literal> and build on Linux, Gradle will, by default, produce <filename>libhelloworld.so</filename> and
+            <filename>libhelloworld.a</filename> binaries.
         </para>
         <para>
-            To build either a static or shared native library binary,
-            a <apilink class="org.gradle.nativebinaries.Library"/> component is added to the <literal>libraries</literal> container.
-            Each <literal>library</literal> component can produce at least one <apilink class="org.gradle.nativebinaries.SharedLibraryBinary"/>
-            and at least one <apilink class="org.gradle.nativebinaries.StaticLibraryBinary"/>.
+            In many cases, more than one binary can be produced for a component.
+            These binaries may vary based on the tool chain used to build, the compiler/linker flags supplied, the dependencies
+            provided, or additional source files provided. Each native binary produced for a component is referred to as <firstterm>variant</firstterm>.
+            Binary variants are discussed in detail below.
+        </para>
+    </section>
+
+    <section>
+        <title>Parallel Compilation</title>
+        <para>
+            Gradle uses the single build worker pool to concurrently compile and link native components, by default.
+            No special configuration is required to enable concurrent building.
+        </para>
+        <para>
+            By default, the worker pool size is determined by the number of available processors on the build machine (as reported to the build JVM).
+            To explicitly set the number of workers use the <literal>--max-workers</literal> command-line option or <literal>org.gradle.workers.max</literal> system property.
+            There is generally no need to change this setting from its default.
+        </para>
+        <para>
+            The build worker pool is shared across all build tasks.
+            This means that when using <link linkend="sec:parallel_execution">parallel project execution</link>, the maximum number of concurrent individual compilation operations does not increase.
+            For example, if the build machine has 4 processing cores and 10 projects are compiling in parallel, Gradle will only use 4 total workers, not 40.
+        </para>
+    </section>
+    <section>
+        <title>Building a library</title>
+        <para>
+            To build either a static or shared native library, you define a library component in the <literal>components</literal> container. The following sample
+            defines a library called <literal>hello</literal>:
         </para>
         <sample id="cppLibraries" dir="native-binaries/c" title="Defining a library component">
             <sourcefile file="build.gradle" snippet="libraries"/>
         </sample>
         <para>
-            To build an executable binary,
-            an <apilink class="org.gradle.nativebinaries.Executable"/> component is added to the <literal>executables</literal> container
-            and associated with a set of sources.
+            A library component is represented using <apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/>.
+            Each library component can produce at least one shared library binary (<apilink class="org.gradle.nativeplatform.SharedLibraryBinarySpec"/>)
+            and at least one static library binary (<apilink class="org.gradle.nativeplatform.StaticLibraryBinarySpec"/>).
+        </para>
+    </section>
+    <section>
+        <title>Building an executable</title>
+        <para>
+            To build a native executable, you define an executable component in the <literal>components</literal> container. The following sample defines
+            an executable called <literal>main</literal>:
         </para>
         <sample id="cppExecutables" dir="native-binaries/c" title="Defining executable components">
             <sourcefile file="build.gradle" snippet="executables"/>
         </sample>
         <para>
-            In many cases, more than one native binary can be produced for a component.
-            These binaries may vary based on the tool chain used to build, the compiler/linker flags supplied, the dependencies
-            provided, or additional source files provided. Each native binary produced for a component is referred to as <literal>variant</literal>.
-            Binary variants are discussed in detail below.
+            An executable component is represented using <apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/>. Each executable component can
+            produce at least one executable binary (<apilink class="org.gradle.nativeplatform.NativeExecutableBinarySpec"/>).
+        </para>
+        <para>
+            For each component defined, Gradle adds a <apilink class="org.gradle.language.base.FunctionalSourceSet"/> with the same name.
+            Each of these functional source sets will contain a language-specific source set for each of the languages supported by the project.
         </para>
     </section>
 
     <section>
         <title>Tasks</title>
         <para>
-            For each <apilink class="org.gradle.nativebinaries.NativeBinary"/> that can be produced by a build,
+            For each <apilink class="org.gradle.nativeplatform.NativeBinarySpec"/> that can be produced by a build,
             a single <firstterm>lifecycle task</firstterm> is constructed that can be used to create that binary, together with a set of other tasks that do the actual
             work of compiling, linking or assembling the binary.
         </para>
         <table>
-            <thread>
-                <tr><td>Component Type</td><td>Native Binary Type</td><td>Lifecycle task</td><td>Location of created binary</td></tr>
-            </thread>
+            <thead>
+                <tr>
+                    <td>Component Type</td>
+                    <td>Native Binary Type</td>
+                    <td>Lifecycle task</td>
+                    <td>Location of created binary</td>
+                </tr>
+            </thead>
             <tr>
-                <td><apilink class="org.gradle.nativebinaries.Executable"/></td><td><apilink class="org.gradle.nativebinaries.ExecutableBinary"/></td><td><literal><replaceable>$component.name</replaceable>Executable</literal></td><td><filename><replaceable>$buildDir</replaceable>/binaries/<replaceable>$binary.name</replaceable>/<replaceable>$component.name</replaceable></filename></td>
+                <td><apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/></td>
+                <td><apilink class="org.gradle.nativeplatform.NativeExecutableBinarySpec"/></td>
+                <td><literal><replaceable>${component.name}</replaceable>Executable</literal></td>
+                <td><filename><replaceable>${project.buildDir}</replaceable>/binaries/<replaceable>${component.name}</replaceable>Executable/<replaceable>${component.name}</replaceable></filename></td>
             </tr>
             <tr>
-                <td><apilink class="org.gradle.nativebinaries.Library"/></td><td><apilink class="org.gradle.nativebinaries.SharedLibraryBinary"/></td><td><literal><replaceable>$component.name</replaceable>SharedLibrary</literal></td><td><filename><replaceable>$buildDir</replaceable>/binaries/<replaceable>$binary.name</replaceable>/lib<replaceable>$component.name</replaceable>.so</filename></td>
+                <td><apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/></td>
+                <td><apilink class="org.gradle.nativeplatform.SharedLibraryBinarySpec"/></td>
+                <td><literal><replaceable>${component.name}</replaceable>SharedLibrary</literal></td>
+                <td><filename><replaceable>${project.buildDir}</replaceable>/binaries/<replaceable>${component.name}</replaceable>SharedLibrary/lib<replaceable>${component.name}</replaceable>.so</filename></td>
             </tr>
             <tr>
-                <td><apilink class="org.gradle.nativebinaries.Library"/></td><td><apilink class="org.gradle.nativebinaries.StaticLibraryBinary"/></td><td><literal><replaceable>$component.name</replaceable>StaticLibrary</literal></td><td><filename><replaceable>$buildDir</replaceable>/binaries/<replaceable>$binary.name</replaceable>/<replaceable>$component.name</replaceable>.a</filename></td>
+                <td><apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/></td>
+                <td><apilink class="org.gradle.nativeplatform.StaticLibraryBinarySpec"/></td>
+                <td><literal><replaceable>${component.name}</replaceable>StaticLibrary</literal></td>
+                    <td><filename><replaceable>${project.buildDir}</replaceable>/binaries/<replaceable>${component.name}</replaceable>StaticLibrary/<replaceable>${component.name}</replaceable>.a</filename></td>
             </tr>
         </table>
 
@@ -132,13 +244,32 @@
         </section>
     </section>
 
+    <section>
+        <title>Finding out more about your project</title>
+        <para>Gradle provides a report that you can run from the command-line that shows some details about the components and binaries that your
+            project produces. To use this report, just run <userinput>gradle components</userinput>. Below is an example of running this report for
+            one of the sample projects:
+        </para>
+        <sample id="nativeComponentReport" dir="native-binaries/cpp" title="The components report">
+            <output args='components'/>
+        </sample>
+    </section>
+
     <section id="native_binaries:languages">
-        <title>Core language support: C, C++, Assembler, Objective-C and Objective-C++ </title>
+        <title>Language support</title>
         <para>
-            Presently, Gradle supports building native binaries from any combination of C++, C, Assembler, Objective-C and Objective-C++ sources.
+            Presently, Gradle supports building native binaries from any combination of source languages listed below.
             A native binary project will contain one or more named <literal>FunctionalSourceSet</literal> instances (eg 'main', 'test', etc),
-            each of which can contain <literal>LanguageSourceSet</literal>s containing C++, C, Assembler, Objective-C or Objective-C++ source files.
+            each of which can contain <literal>LanguageSourceSet</literal>s containing source files, one for each language.
         </para>
+        <itemizedlist>
+            <listitem><para>C</para></listitem>
+            <listitem><para>C++</para></listitem>
+            <listitem><para>Objective-C</para></listitem>
+            <listitem><para>Objective-C++</para></listitem>
+            <listitem><para>Assembly</para></listitem>
+            <listitem><para>Windows resources</para></listitem>
+        </itemizedlist>
 
         <section>
             <title>C++ sources</title>
@@ -163,8 +294,8 @@
                 <sourcefile file="build.gradle" snippet="cpp-sources"/>
             </sample>
             <para>
-                For a library named 'main', files in <filename>src/main/headers</filename> are considered the “public” or “exported” headers.
-                Header files that should not be exported (but are used internally) should be placed inside the <filename>src/main/cpp</filename> directory (though be aware that
+                For a library named 'main', header files in <filename>src/main/headers</filename> are considered the “public” or “exported” headers.
+                Header files that should not be exported should be placed inside the <filename>src/main/cpp</filename> directory (though be aware that
                 such header files should always be referenced in a manner relative to the file including them).
             </para>
         </section>
@@ -192,8 +323,8 @@
                 <sourcefile file="build.gradle" snippet="c-sources"/>
             </sample>
             <para>
-                For a library named 'main', files in <filename>src/main/headers</filename> are considered the “public” or “exported” headers.
-                Header files that should not be exported (but are used internally) should be placed inside the <filename>src/main/c</filename> directory (though be aware that
+                For a library named 'main', header files in <filename>src/main/headers</filename> are considered the “public” or “exported” headers.
+                Header files that should not be exported should be placed inside the <filename>src/main/c</filename> directory (though be aware that
                 such header files should always be referenced in a manner relative to the file including them).
             </para>
         </section>
@@ -257,7 +388,7 @@
             <sourcefile file="build.gradle" snippet="all-binaries"/>
         </sample>
         <para>
-            Each binary is associated with a particular <apilink class="org.gradle.nativebinaries.toolchain.ToolChain"/>, allowing settings to be targeted based on
+            Each binary is associated with a particular <apilink class="org.gradle.nativeplatform.toolchain.NativeToolChain"/>, allowing settings to be targeted based on
             this value.
         </para>
         <para>
@@ -267,7 +398,7 @@
             <sourcefile file="build.gradle" snippet="all-shared-libraries"/>
         </sample>
         <para>
-            Furthermore, it is possible to specify settings that apply to all binaries produces for a particular <literal>executable</literal>
+            Furthermore, it is possible to specify settings that apply to all binaries produced for a particular <literal>executable</literal>
             or <literal>library</literal> component:
         </para>
         <sample id="componentBinarySettings" dir="native-binaries/assembler" title="Settings that apply to all binaries produced for the 'main' executable component">
@@ -278,7 +409,7 @@
         </para>
         <para>
             Similarly, settings can be specified to target binaries for a component that are of a particular type:
-            eg all <literal>shared libraries</literal> for the <literal>main library</literal> component.
+            eg all shared libraries for the main library component.
         </para>
         <sample id="sharedLibraryArgs" dir="native-binaries/cpp-lib" title="Settings that apply only to shared libraries produced for the 'main' library component">
             <sourcefile file="build.gradle" snippet="args"/>
@@ -288,7 +419,7 @@
     <section id="native_binaries:windows-resources">
         <title>Windows Resources</title>
         <para>
-            When using the <apilink class="org.gradle.nativebinaries.toolchain.VisualCpp"/> tool chain, Gradle is able to compile Window Resource (<literal>rc</literal>)
+            When using the <apilink class="org.gradle.nativeplatform.toolchain.VisualCpp"/> tool chain, Gradle is able to compile Window Resource (<literal>rc</literal>)
             files and link them into a native binary. This functionality is provided by the <literal>'windows-resources'</literal> plugin.
         </para>
         <sample id="windowsResourcesPlugin" dir="native-binaries/windows-resources" title="The 'windows-resources' plugin">
@@ -301,7 +432,7 @@
             <filename>.rc</filename> source files under <filename>src/${name}/rc</filename>.
         </para>
         <para>
-            As with other source types, you can configure the location of the windows resources that should in included in the binary.
+            As with other source types, you can configure the location of the windows resources that should be included in the binary.
         </para>
         <sample id="windowsResourceSet" dir="native-binaries/windows-resources" title="Configuring the location of Windows resource sources">
             <sourcefile file="build-resource-only-dll.gradle" snippet="windows-resource-set"/>
@@ -315,15 +446,15 @@
         </sample>
         <para>
             The example above also demonstrates the mechanism of passing extra command-line arguments to the resource compiler.
-            The <literal>rcCompiler</literal> extension is of type <apilink class="org.gradle.nativebinaries.language.PreprocessingTool"/>.
+            The <literal>rcCompiler</literal> extension is of type <apilink class="org.gradle.language.PreprocessingTool"/>.
         </para>
     </section>
 
     <section>
         <title>Library Dependencies</title>
         <para>
-            Dependencies for C++ projects are binary libraries that export header files. The header files are used during compilation, with the compiled
-            binary dependency being used during the linking.
+            Dependencies for native components are binary libraries that export header files. The header files are used during compilation, with the compiled
+            binary dependency being used during linking and execution.
         </para>
         <section>
            <title>Dependencies within the same project</title>
@@ -332,13 +463,13 @@
                A common example is a native executable component that uses functions provided by a separate native library component.
            </para>
            <para>
-               Such a library dependency can be easily provided to source set associated with the <literal>executable</literal> component:
+               Such a library dependency can be added to a source set associated with the <literal>executable</literal> component:
            </para>
            <sample id="cppSourceLibrary" dir="native-binaries/cpp" title="Providing a library dependency to the source set">
                <sourcefile file="build.gradle" snippet="source-library"/>
            </sample>
            <para>
-               Alternatively, a library dependency can be provided directly to the <literal>ExecutableBinary</literal> for the <literal>executable</literal>.
+               Alternatively, a library dependency can be provided directly to the <literal>NativeExecutableBinary</literal> for the <literal>executable</literal>.
            </para>
            <sample id="cppBinaryLibrary" dir="native-binaries/custom-layout" title="Providing a library dependency to the binary">
                <sourcefile file="build.gradle" snippet="binary-library"/>
@@ -365,7 +496,7 @@
         <para>
             Binaries produced by Gradle can be differentiated on
                 <link linkend="native_binaries:build_type">build type</link>,
-                <link linkend="native_binaries:platform">platform</link> and
+                <link linkend="native_binaries:platform">platform</link>, and
                 <link linkend="native_binaries:flavor">flavor</link>.
             For each of these 'variant dimensions', it is possible to specify a set of available values as well as target each component at
             one, some or all of these. For example, a plugin may define a range of support platforms, but you
@@ -402,7 +533,7 @@
             <title>Platform</title>
             <para>
                 An executable or library can be built to run on different operating systems and cpu architectures, with a variant being
-                produced for each platform. Gradle defines each OS/architecture combination as a <apilink class="org.gradle.nativebinaries.platform.Platform"/>,
+                produced for each platform. Gradle defines each OS/architecture combination as a <apilink class="org.gradle.nativeplatform.platform.NativePlatform"/>,
                 and a project may define any number of platforms.
                 If no platforms are defined in a project, then a single, default platform 'current' is added.
             </para>
@@ -416,9 +547,9 @@
                 <sourcefile file="build.gradle" snippet="platforms"/>
             </sample>
             <para>
-                For a given variant, Gradle will attempt to find a <apilink class="org.gradle.nativebinaries.toolchain.ToolChain"/> that is able to build
+                For a given variant, Gradle will attempt to find a <apilink class="org.gradle.nativeplatform.toolchain.NativeToolChain"/> that is able to build
                 for the target platform. Available tool chains are searched in the order defined.
-                See the <link linkend="native_binaries:tool_chain">tool chain</link> section below for more details.
+                See the <link linkend="native_binaries:tool_chain">tool chains</link> section below for more details.
             </para>
         </section>
 
@@ -449,30 +580,30 @@
             <para>
                 For a default component, Gradle will attempt to create a native binary variant for each and every combination of <literal>buildType</literal>,
                 <literal>platform</literal> and <literal>flavor</literal> defined for the project. It is possible to override this on a per-component
-                basis, by specifying the set of <literal>targetBuildTypes</literal>, <literal>targetPlatforms</literal> and/or <literal>targetFlavors</literal>.
+                basis, by specifying the set of <literal>targetBuildTypes</literal>, <literal>targetPlatform</literal> and/or <literal>targetFlavors</literal>.
             </para>
             <sample id="targets" dir="native-binaries/variants" title="Targeting a component at particular platforms">
                 <sourcefile file="build.gradle" snippet="target-platforms"/>
             </sample>
             <para>
-                Here you can see that the <apilink class="org.gradle.nativebinaries.TargetedNativeComponent" method="targetPlatforms"/> method is used to
-                select the set of platforms to target for <literal>executables.main</literal>.
+                Here you can see that the <apilink class="org.gradle.nativeplatform.TargetedNativeComponent" method="targetPlatform"/> method is used to
+                specify a platform that the <literal>NativeExecutableSpec</literal> named <literal>main</literal> should be built for.
             </para>
             <para>
-                A similar mechanism exists for selecting <apilink class="org.gradle.nativebinaries.TargetedNativeComponent" method="targetBuildTypes"/>
-                and <apilink class="org.gradle.nativebinaries.TargetedNativeComponent" method="targetFlavors"/>.
+                A similar mechanism exists for selecting <apilink class="org.gradle.nativeplatform.TargetedNativeComponent" method="targetBuildTypes"/>
+                and <apilink class="org.gradle.nativeplatform.TargetedNativeComponent" method="targetFlavors"/>.
             </para>
         </section>
         <section>
             <title>Building all possible variants</title>
             <para>
                 When a set of build types, target platforms, and flavors is defined for a component,
-                a <apilink class="org.gradle.nativebinaries.NativeBinary"/> model element is created for every possible
+                a <apilink class="org.gradle.nativeplatform.NativeBinarySpec"/> model element is created for every possible
                 combination of these. However, in many cases it is not possible to build a particular variant, perhaps because
                 no tool chain is available to build for a particular platform.
             </para>
             <para>
-                If a binary variant cannot be built for any reason, then the <apilink class="org.gradle.nativebinaries.NativeBinary"/>
+                If a binary variant cannot be built for any reason, then the <apilink class="org.gradle.nativeplatform.NativeBinarySpec"/>
                 associated with that variant will not be <literal>buildable</literal>. It is possible to use this property to create a task
                 to generate all possible variants on a particular machine.
             </para>
@@ -497,13 +628,13 @@
             </para>
             <itemizedlist>
                 <listitem>
-                    <para><apilink class="org.gradle.nativebinaries.toolchain.Gcc"/></para>
+                    <para><apilink class="org.gradle.nativeplatform.toolchain.Gcc"/></para>
                 </listitem>
                 <listitem>
-                    <para><apilink class="org.gradle.nativebinaries.toolchain.Clang"/></para>
+                    <para><apilink class="org.gradle.nativeplatform.toolchain.Clang"/></para>
                 </listitem>
                 <listitem>
-                    <para><apilink class="org.gradle.nativebinaries.toolchain.VisualCpp"/></para>
+                    <para><apilink class="org.gradle.nativeplatform.toolchain.VisualCpp"/></para>
                 </listitem>
             </itemizedlist>
             <sample id="toolChains" dir="native-binaries/tool-chains" title="Defining tool chains">
@@ -512,13 +643,14 @@
             <para>
                 Each tool chain implementation allows for a certain degree of configuration (see the API documentation for more details).
             </para>
+
         </section>
 
         <section>
             <title>Using tool chains</title>
             <para>
                 It is not necessary or possible to specify the tool chain that should be used to build.
-                For a given variant, Gradle will attempt to locate a <apilink class="org.gradle.nativebinaries.toolchain.ToolChain"/> that is able to build
+                For a given variant, Gradle will attempt to locate a <apilink class="org.gradle.nativeplatform.toolchain.NativeToolChain"/> that is able to build
                 for the target platform. Available tool chains are searched in the order defined.
             </para>
             <note>
@@ -531,9 +663,9 @@
                 will target the current operating system. See the next section for information on cross-compiling for other operating systems.
             </para>
             <table>
-                <thread>
+                <thead>
                     <tr><td>Tool Chain</td><td>Architectures</td></tr>
-                </thread>
+                </thead>
                 <tr>
                     <td>GCC</td><td>x86, x86_64</td>
                 </tr>
@@ -547,24 +679,33 @@
             <para>
                 So for GCC running on linux, the supported target platforms are 'linux/x86' and 'linux/x86_64'.
                 For GCC running on Windows via Cygwin, platforms 'windows/x86' and 'windows/x86_64' are supported.
-                (The Cygwin runtime is not yet modelled as part of the Platform, but will be in the future.)
+                (The Cygwin POSIX runtime is not yet modelled as part of the platform, but will be in the future.)
             </para>
             <para>
                 If no target platforms are defined for a project, then all binaries are built to target a default platform named 'current'.
                 This default platform does not specify any <literal>architecture</literal> or <literal>operatingSystem</literal> value,
                 hence using the default values of the first available tool chain.
             </para>
+            <para>
+                Gradle provides a <firstterm>hook</firstterm> that allows the build author to control the exact set of arguments passed to a tool chain executable.
+                This enables the build author to work around any limitations in Gradle, or assumptions that Gradle makes.
+                The arguments hook should be seen as a 'last-resort' mechanism, with preference given to truly modelling the underlying domain.
+            </para>
+            <sample id="withArguments" dir="native-binaries/tool-chains" title="Reconfigure tool arguments">
+                <sourcefile file="build.gradle" snippet="withArguments"/>
+            </sample>
         </section>
 
         <section>
             <title>Cross-compiling with GCC</title>
             <para>
-                Cross-compiling is possible with the <apilink class="org.gradle.nativebinaries.toolchain.Gcc"/> and <apilink class="org.gradle.nativebinaries.toolchain.Clang"/> tool chains,
-                by programmatically adding support for additional target platforms.
-                This is done using the <apilink class="org.gradle.nativebinaries.toolchain.PlatformConfigurableToolChain"/> API.
-                Each added <apilink class="org.gradle.nativebinaries.toolchain.TargetPlatformConfiguration"/> defines support for a particular target platform,
-                and supplies additional tool arguments that are required to target this platform.
+                Cross-compiling is possible with the <apilink class="org.gradle.nativeplatform.toolchain.Gcc"/> and <apilink class="org.gradle.nativeplatform.toolchain.Clang"/> tool chains,
+                by adding support for additional target platforms.
+                This is done by specifying a target platform for a toolchain. For each target platform a custom configuration can be specified.
             </para>
+            <sample id="targetPlatforms" dir="native-binaries/target-platforms" title="Defining target platforms">
+                <sourcefile file="build.gradle" snippet="targetplatforms"/>
+            </sample>
         </section>
     </section>
     <section id="native_binaries:visual_studio">
@@ -580,32 +721,32 @@
             a Visual Studio Project for that component, as well as linking to project files for each depended-on binary.
         </para>
         <para>
-            The content of the generated visual studio files can be modified via programmatic hooks, provided by the <literal>visualStudio</literal>
+            The content of the generated visual studio files can be modified via API hooks, provided by the <literal>visualStudio</literal>
             extension. Take a look at the 'visual-studio' sample, or see <apilink class="org.gradle.ide.visualstudio.VisualStudioExtension" method="getProjects"/>
-            and <apilink class="org.gradle.ide.visualstudio.VisualStudioExtension" method="getSolutions"/> for more details.
+            and <apilink class="org.gradle.ide.visualstudio.VisualStudioExtension" method="getSolutions"/> in the API documentation for more details.
         </para>
     </section>
     <section id="native_binaries:cunit">
         <title>CUnit support</title>
         <para>
             The Gradle <literal>cunit</literal> plugin provides support for compiling and executing CUnit tests in your native-binary project.
-            For each <apilink class="org.gradle.nativebinaries.Executable"/> and <apilink class="org.gradle.nativebinaries.Library"/>
-            defined in your project, Gradle will create a matching <apilink class="org.gradle.nativebinaries.test.cunit.CUnitTestSuite"/> component,
+            For each <apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/> and <apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/>
+            defined in your project, Gradle will create a matching <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec"/> component,
             named <literal>${component.name}Test</literal>.
         </para>
         <section>
             <title>CUnit sources</title>
             <para>
-                Gradle will create a <apilink class="org.gradle.language.c.CSourceSet"/> named 'cunit' for each <apilink class="org.gradle.nativebinaries.test.cunit.CUnitTestSuite"/> component
+                Gradle will create a <apilink class="org.gradle.language.c.CSourceSet"/> named 'cunit' for each <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec"/> component
                 in the project. This source set should contain the cunit test files for the component sources. Source files can be located in the conventional location
                 (<literal>src/${component.name}Test/cunit</literal>) or can be configured like any other source set.
             </para>
             <para>
-                The job of initialising the CUnit test registry and executing the tests is performed by Gradle, via some generated CUnit launcher sources.
+                Gradle initialises the CUnit test registry and executes the tests, utilising some generated CUnit launcher sources.
                 Gradle will expect and call a function with the signature <literal>void gradle_cunit_register()</literal> that you can use to configure the
                 actual CUnit suites and tests to execute.
             </para>
-            <sample id="cunitSources" dir="native-binaries/cunit/src/operatorsTest/cunit" title="Registering CUnit tests">
+            <sample id="cunitSources" dir="native-binaries/cunit/src/operatorsTest/c" title="Registering CUnit tests">
                 <sourcefile file="suite_operators.c"/>
             </sample>
             <note>
@@ -616,10 +757,10 @@
         <section>
             <title>Building CUnit executables</title>
             <para>
-                A <apilink class="org.gradle.nativebinaries.test.cunit.CUnitTestSuite"/> component has an associated
-                <apilink class="org.gradle.nativebinaries.Executable"/> or <apilink class="org.gradle.nativebinaries.Library"/> component.
-                For each <apilink class="org.gradle.nativebinaries.ProjectNativeBinary"/> configured for the main component, a matching
-                <apilink class="org.gradle.nativebinaries.test.TestSuiteExecutableBinary"/> will be configured on the test suite component.
+                A <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec"/> component has an associated
+                <apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/> or <apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/> component.
+                For each <apilink class="org.gradle.nativeplatform.NativeBinarySpec"/> configured for the main component, a matching
+                <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec"/> will be configured on the test suite component.
                 These test suite binaries can be configured in a similar way to any other binary instance:
             </para>
             <sample id="cunitSources" dir="native-binaries/cunit" title="Registering CUnit tests">
@@ -627,36 +768,21 @@
             </sample>
             <note>
                 Both the CUnit sources provided by your project and the generated launcher require the core CUnit headers and libraries.
-                Presently, this library dependency must be provided by your project for each <apilink class="org.gradle.nativebinaries.test.TestSuiteExecutableBinary"/>.
+                Presently, this library dependency must be provided by your project for each <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec"/>.
             </note>
         </section>
 
         <section>
             <title>Running CUnit tests</title>
             <para>
-                For each <apilink class="org.gradle.nativebinaries.test.TestSuiteExecutableBinary"/>, Gradle will create a task to execute this binary,
+                For each <apilink class="org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec"/>, Gradle will create a task to execute this binary,
                 which will run all of the registered CUnit tests.
-                The generated test results will be located in the <literal><replaceable>${build.dir}</replaceable>/test-results</literal> directory.
+                Test results will be found in the <literal><replaceable>${build.dir}</replaceable>/test-results</literal> directory.
             </para>
             <sample id="completeCUnitExample" dir="native-binaries/cunit" title="Running CUnit tests" includeLocation="true">
                 <sourcefile file="build.gradle" snippet="complete-example"/>
-                <!--<output args='runFailingOperatorsTestCUnitExe' expectFailure="true"/>-->
+                <output args='-q runFailingOperatorsTestCUnitExe' expectFailure="true"/>
             </sample>
-            <!-- Cannot presently use generated output since the actual output varies from Windows and Linux,
-                 and UserGuideSamplesIntegrationTest doesn't permit this difference. -->
-            <screen>
-> gradle -q runFailingOperatorsTestCUnitExe
-
-There were test failures:
-  1. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:6  - plus(0, -2) == -2
-  2. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c:7  - plus(2, 2) == 4
-
-:runFailingOperatorsTestCUnitExe FAILED
-
-BUILD FAILED
-
-Total time: 1 secs
-            </screen>
         </section>
 
         <note>
@@ -665,8 +791,74 @@ Total time: 1 secs
             </para>
             <itemizedlist>
                 <listitem>
-                    <para>Allow tests to be declared with javadoc-style annotations.</para>
+                    <para>Allow tests to be declared with Javadoc-style annotations.</para>
+                </listitem>
+                <listitem>
+                    <para>Improved HTML reporting, similar to that available for JUnit.</para>
+                </listitem>
+                <listitem>
+                    <para>Real-time feedback for test execution.</para>
+                </listitem>
+                <listitem>
+                    <para>Support for additional test frameworks.</para>
                 </listitem>
+            </itemizedlist>
+        </note>
+    </section>
+    <section id="native_binaries:google_test">
+        <title>GoogleTest support</title>
+        <para>
+            The Gradle <literal>google-test</literal> plugin provides support for compiling and executing GoogleTest tests in your native-binary project.
+            For each <apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/> and <apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/>
+            defined in your project, Gradle will create a matching <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec"/> component,
+            named <literal>${component.name}Test</literal>.
+        </para>
+        <section>
+            <title>GoogleTest sources</title>
+            <para>
+                Gradle will create a <apilink class="org.gradle.language.cpp.CppSourceSet"/> named 'cpp' for each <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec"/> component
+                in the project. This source set should contain the GoogleTest test files for the component sources. Source files can be located in the conventional location
+                (<literal>src/${component.name}Test/cpp</literal>) or can be configured like any other source set.
+            </para>
+        </section>
+
+        <section>
+            <title>Building GoogleTest executables</title>
+            <para>
+                A <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec"/> component has an associated
+                <apilink class="org.gradle.nativeplatform.NativeExecutableSpec"/> or <apilink class="org.gradle.nativeplatform.NativeLibrarySpec"/> component.
+                For each <apilink class="org.gradle.nativeplatform.NativeBinarySpec"/> configured for the main component, a matching
+                <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec"/> will be configured on the test suite component.
+                These test suite binaries can be configured in a similar way to any other binary instance:
+            </para>
+            <sample id="googleTestSources" dir="native-binaries/google-test" title="Registering GoogleTest tests">
+                <sourcefile file="build.gradle" snippet="configure-test-binary"/>
+            </sample>
+            <note>
+                The GoogleTest sources provided by your project require the core GoogleTest headers and libraries.
+                Presently, this library dependency must be provided by your project for each <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec"/>.
+            </note>
+        </section>
+
+        <section>
+            <title>Running GoogleTest tests</title>
+            <para>
+                For each <apilink class="org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec"/>, Gradle will create a task to execute this binary,
+                which will run all of the registered GoogleTest tests.
+                Test results will be found in the <literal><replaceable>${build.dir}</replaceable>/test-results</literal> directory.
+            </para>
+            <!-- TODO:DAZ Add this back in when we have functional libs -->
+            <!--<sample id="completeGoogleTestExample" dir="native-binaries/google-test" title="Running GoogleTest tests" includeLocation="true">-->
+                <!--<sourcefile file="build.gradle" snippet="complete-example"/>-->
+                <!--<output args='-q runFailingOperatorsTestGoogleTestExe' expectFailure="true"/>-->
+            <!--</sample>-->
+        </section>
+
+        <note>
+            <para>
+                The current support for GoogleTest is quite rudimentary. Plans for future integration include:
+            </para>
+            <itemizedlist>
                 <listitem>
                     <para>Improved HTML reporting, similar to that available for JUnit.</para>
                 </listitem>
diff --git a/subprojects/docs/src/docs/userguide/newModel.xml b/subprojects/docs/src/docs/userguide/newModel.xml
new file mode 100644
index 0000000..5434d59
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/newModel.xml
@@ -0,0 +1,370 @@
+<!--
+  ~ Copyright 2015 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<chapter id='new_model'>
+    <title>Rule based model configuration</title>
+    <para>
+        This chapter describes and documents what is essentially the foundation for the Gradle 3.0 and the next generation of Gradle builds.
+        It is being incrementally developed during the Gradle 2.x stream and is in use for <link linkend="nativeBinaries">Gradle's support for building native binaries</link>.
+    </para>
+    <para>
+        All of the mechanisms, DSL, API, and techniques discussed here are <emphasis>incubating</emphasis> (i.e. not considered stable and subject to change - see <xref linkend="feature_lifecycle"/>).
+        Exposing new features early, during incubation, allows early testing and the incorporation of real world feedback ultimately resulting in a better Gradle.
+    </para>
+    <para>
+        The following build script is an example of a rule based build.
+    </para>
+    <sample id='basicRuleSourcePlugin-all' dir='modelRules/basicRuleSourcePlugin' includeLocation="true" title='an example of a simple rule based build'>
+        <sourcefile file='build.gradle' />
+        <output args='hello'/>
+    </sample>
+    <para>
+        The rest of this chapter is dedicated to explaining what is going on in this build script, and why Gradle is moving in this direction.
+    </para>
+    <section>
+        <title>Background</title>
+        <para>
+            Gradle embraces domain modelling as core tenet.
+            Focusing on the domain model as opposed to the execution model (like prior generation build tools such as Apache Ant) has many advantages.
+            A strong domain model communicates the intent (i.e. the what) over the mechanics (i.e. the how).
+            This allows humans to understand builds at a level that is meaningful to them.
+        </para>
+        <para>
+            As well as helping humans, a strong domain model also helps the dutiful machines.
+            Plugins can more effectively collaborate around a strong domain model (e.g. plugins can say something about Java applications, such as providing conventions).
+            Very importantly, by having a model of the <emphasis>what</emphasis> instead of the <emphasis>how</emphasis> Gradle can make intelligent choices on just how to do the how.
+        </para>
+        <para>
+            The move towards “Rule based model configuration” can be summarised as improving Gradle's ability to model richer domains in a more effective way.
+            It also makes expressing the kinds of models present in today's Gradle more robust and simpler.
+        </para>
+    </section>
+    <section>
+        <title>Motivations for change</title>
+        <para>
+            Domain modelling in Gradle is not new.
+            The Java plugin's <apilink class="org.gradle.api.tasks.SourceSet" /> concept is an example of domain modelling,
+            as is the modelling of <apilink class="org.gradle.nativeplatform.NativeBinary"/> in the Native plugin suite.
+        </para>
+        <para>
+            One distinguishing characteristic of Gradle compared to other build tools that also embrace modelling is that Gradle's model is open and collaborative.
+            Gradle is fundamentally a tool for modelling software construction and then realizing the model, via tasks such as compilation etc..
+            Different domain plugins (e.g. Java, C++, Android) provide models that other plugins can collaborate with and build upon.
+        </para>
+        <para>
+            While Gradle has long employed sophisticated techniques when it comes to realizing the model (i.e. what we know as building things),
+            the next generation of Gradle builds will employ some of the same techniques to building of the model itself.
+            By defining build tasks as effectively a graph of dependent functions with explicit inputs and outputs,
+            Gradle is able to order, cache, parallelize and apply other optimizations to the work.
+            Using a “graph of tasks” for the production of software is a long established idea, and necessary given the complexity of software production.
+            The task graph effectively defines the <emphasis>rules</emphasis> of execution the Gradle must follow.
+            The term “Rule based model configuration” refers to applying the same concepts to building the model that builds the task graph.
+        </para>
+        <para>
+            Another <emphasis>key</emphasis> motivation is performance and scale.
+            Aspects of the current approach that Gradle takes to modelling the build prevent pervasive parallelism and limit scalability.
+            The new model is being designed with the requirements of modern software delivery in mind, where immediate responsiveness is critical for projects large and small.
+        </para>
+    </section>
+    <section>
+        <title>Concepts</title>
+        <para>
+            This section outlines the key concepts of rule based model configuration.
+            Subsequent sections in this chapter will show the concepts in action.
+        </para>
+        <section>
+            <title>The “model space”</title>
+            <para>
+                The term “model space” is used to refer to the formal model, addressable by rules.
+            </para>
+            <para>
+                An analog with existing model is effectively the “project space”.
+                The <apilink class="org.gradle.api.Project"/> object is effectively the root of a graph of objects (e.g <literal>project.repositories</literal>, <literal>project.tasks</literal> etc.).
+                A build script is effectively adding and configuring objects of this graph.
+                For the most part, the “project space” is opaque to Gradle.
+                It is an arbitrary graph of objects that Gradle only partially understands.
+            </para>
+            <para>
+                Each project also has its own model space, which is distinct from the project space.
+                A key characteristic of the “model space” is that Gradle knows much more about it (which is knowledge that can be put to good use).
+                The objects in the model space are “managed”, to a greater extent than objects in the project space.
+                The origin, structure, state, collaborators and relationships of objects in the model space are first class constructs.
+                This is effectively the characteristic that functionally distinguishes the model space from the project space:
+                the objects of the model space are defined in ways that Gradle can understand them intimately, as opposed to an object that is the result of running relatively opaque code.
+                A “rule” is effectively a building block of this definition.
+            </para>
+            <para>
+                The model space will eventually replace the project space, in so far as it will be the only “space”.
+                However, during the transition the distinction is helpful.
+            </para>
+        </section>
+        <section>
+            <title>Model paths</title>
+            <para>
+                A model path identifies a path through a model space, to an element.
+                A common representation is a period-delimited set of names.
+                The model path <literal>"tasks"</literal> is the path to the element that is the task container.
+                Assuming a task who's name is <literal>hello</literal>, the path <literal>"tasks.hello"</literal> is the path to this task.
+            </para>
+            <para>
+                TBD - more needed here.
+            </para>
+        </section>
+        <section>
+            <title>Rules</title>
+            <para>
+                The model space is defined in terms of “rules”.
+                A rule is just a function (in the abstract sense) that either produces a model element, or acts upon a model element.
+                Every rule has a single subject and zero or more inputs.
+                Only the subject can be changed by a rule, while the inputs are effectively immutable.
+            </para>
+            <para>
+                Gradle guarantees that all inputs are fully “realized“ before the rule executes.
+                The process of “realizing” a model element is effectively executing all the rules for which it is the subject, transitioning it to its final state.
+                There is a strong analogy here to Gradle's task graph and task execution model.
+                Just as tasks depend on each other and Gradle ensures that dependencies are satisfied before executing a task,
+                rules effectively depend on each other (i.e. a rule depends on all rules who's subject is one of the inputs) and Gradle ensures that all dependencies are satisfied
+                before executing the rule.
+            </para>
+            <para>
+                Model elements are very often defined in terms of other model elements.
+                For example, a compile task's configuration can be defined in terms of the configuration of the source set that it is compiling.
+                In this scenario, the compile task would be the subject of a rule and the source set an input.
+                Such a rule could configure the task subject based on the source set input without concern for how it was configured, who it was configured by or when the configuration was specified.
+            </para>
+            <para>
+                There are several ways to declare rules, and in several forms.
+                An explanation of the different forms and mechanisms along with concrete examples is forthcoming in this chapter.
+            </para>
+        </section>
+        <section>
+            <title>Managed model elements</title>
+            <para>
+                Currently, any kind of Java object can be part of the model space.
+                However, there is a difference between “managed” and “unmanaged” objects.
+            </para>
+            <para>
+                A “managed” object is transparent and enforces immutability once realized.
+                Being transparent means that its structure is understood by the rule infrastructure and as such each of its properties are also individual elements in the model space.
+                Please see the <apilink class="org.gradle.model.Managed" /> annotation for more information on creating managed model objects.
+            </para>
+            <para>
+                An “unmanaged” object is opaque to the the model space and does not enforce immutability.
+                Over time, more mechanisms will be available for defining managed model elements culminating in all model elements being managed in some way.
+            </para>
+        </section>
+        <section>
+            <title>References, binding and scopes</title>
+            <para>
+                As previously mentioned, a rule has a subject and zero or more inputs.
+                The rule's subject and inputs are declared as “references” and are “bound” to model elements before execution by Gradle.
+                Each rule must effectively forward declare the subject and inputs as references.
+                Precisely how this is done depends on the form of the rule.
+                For example, the rules provided by a <apilink class="org.gradle.model.RuleSource" /> declare references as method parameters.
+            </para>
+            <para>
+                A reference is either “by-path” or “by-type”.
+            </para>
+            <para>
+                A “by-type” reference identifies a particular model element by its type.
+                For example, a reference to the <apilink class="org.gradle.api.tasks.TaskContainer" /> effectively identifies the <literal>"tasks"</literal> element in the project model space.
+                The model space is not exhaustively searched for candidates for by-type binding.
+                The search space for a by-type binding is determined by the “scope” of the rule (discussed later).
+            </para>
+            <para>
+                A “by-path” reference identifies a particular model element by its path in model space.
+                By-path references are always relative to the rule scope; there is currently no way to path “out” of the scope.
+                All by-path references also have an associated type, but this does not influence what the reference binds to.
+                The element identified by the path must however by type compatible with the reference, or a fatal “binding failure” will occur.
+            </para>
+            <section>
+                <title>Binding scope</title>
+                <para>
+                    Rules are bound within a “scope”, which determines how references bind.
+                    Most rules are bound at the project scope (i.e. the root of the model graph for the project).
+                    However, rules can be scoped to a node within the graph.
+                    The <apilink class="org.gradle.model.collection.CollectionBuilder" method="named(java.lang.String, java.lang.Class)" /> method is an example,
+                    of a mechanism for applying scoped rules.
+                    Rules declared in the build script using the <literal>model {}</literal> block, or via a <literal>RuleSource</literal> applied as a plugin use the root of the model space as the scope.
+                    This can be considered the default scope.
+                </para>
+                <para>
+                    By-path references are always relative to the rule scope.
+                    When the scope is the root, this effectively allows binding to any element in the graph.
+                    When it is not, the children of the scope can be referred to by-path.
+                </para>
+                <para>
+                    When binding by-type references, the following elements are considered:
+                </para>
+                <itemizedlist>
+                    <listitem>The scope element itself.</listitem>
+                    <listitem>The immediate children of the scope element.</listitem>
+                    <listitem>The immediate children of the model space (i.e. project space) root.</listitem>
+                </itemizedlist>
+                <para>
+                    For the common case, where the rule is effectively scoped to the root, only the immediate children of the root need to be considered.
+                </para>
+            </section>
+        </section>
+    </section>
+    <section>
+        <title>Rule sources</title>
+        <para>
+            One way to define rules, is via a <apilink class="org.gradle.model.RuleSource" /> subclass.
+            Such types can be applied in the same manner (to project objects) as <apilink class="org.gradle.api.Plugin" /> implementations (i.e. via <apilink class="org.gradle.api.Project" method="apply(java.util.Map)" />).
+        </para>
+        <sample id='basicRuleSourcePlugin' dir='modelRules/basicRuleSourcePlugin' title='applying a rule source plugin'>
+            <sourcefile file='build.gradle' snippet='managed-type-and-plugin'/>
+        </sample>
+        <para>
+            Rule source plugins can be packaged and distributed in the same manner as other types of plugins (see <xref linkend="custom_plugins" />).
+        </para>
+        <para>
+            The different methods of the rule source are discrete, independent rules.
+            Their order, or the fact that they belong to the same class, are irrelevant.
+        </para>
+        <sample id='basicRuleSourcePlugin' dir='modelRules/basicRuleSourcePlugin' title="a model creation rule">
+            <sourcefile file='build.gradle' snippet='create-rule'/>
+        </sample>
+        <para>
+            This rule declares the there is a model element at path <literal>"person"</literal> (defined by the method name), of type <literal>Person</literal>.
+            This is the form of the <apilink class="org.gradle.model.Model" /> type rule for <apilink class="org.gradle.model.Managed" /> types.
+            Here, the person object is the rule subject.
+            The method could potentially have a body, that mutated the person instance.
+            It could also potentially have more parameters, that would be the rule inputs.
+        </para>
+        <sample id='basicRuleSourcePlugin' dir='modelRules/basicRuleSourcePlugin' title="a model mutation rule">
+            <sourcefile file='build.gradle' snippet='plugin-mutate-rule'/>
+        </sample>
+        <para>
+            This <apilink class="org.gradle.model.Mutate" /> rule mutates the person object.
+            The first parameter to the method is the subject.
+            Here, a by-type reference is used as no <apilink class="org.gradle.model.Path" /> annotation is present on the parameter.
+            It could also potentially have more parameters, that would be the rule inputs.
+        </para>
+        <sample id='basicRuleSourcePlugin' dir='modelRules/basicRuleSourcePlugin' title="creating a task">
+            <sourcefile file='build.gradle' snippet='task-create-rule'/>
+        </sample>
+        <para>
+            This <apilink class="org.gradle.model.Mutate" /> rule effectively adds a task, by mutating the tasks collection.
+            The subject here is the <literal>"tasks"</literal> node, which is available as a <apilink class="org.gradle.model.collection.CollectionBuilder" /> of <apilink class="org.gradle.api.Task" />.
+            The lone input is our person element.
+            As the person is being used as an input here, it will have been realised before executing this rule.
+            That is, the task container effectively <emphasis>depends on</emphasis> the person element.
+            If there are other configuration rules for the person element, potentially specified in a build script or other plugin, the will also be guaranteed to have been executed.
+        </para>
+        <para>
+            As <literal>Person</literal> is a <apilink class="org.gradle.model.Managed" /> type in this example, any attempt to modify the person parameter in this method would result in an exception being thrown.
+            Managed objects enforce immutability at the appropriate point in their lifecycle.
+        </para>
+        <para>
+            Please see the documentation for <apilink class="org.gradle.model.RuleSource" /> for more information on constraints on how rule sources must be implemented and for more types of rules.
+        </para>
+    </section>
+    <section>
+        <title>The “model DSL”</title>
+        <para>
+            It is also possible to declare rules directly in the build script using the “model DSL”.
+        </para>
+        <sample id='basicRuleSourcePlugin' dir='modelRules/basicRuleSourcePlugin' title="the model dsl">
+            <sourcefile file='build.gradle' snippet='dsl'/>
+        </sample>
+        <para>
+            Continuing with the example so far of the model element <literal>"person"</literal> of type <literal>Person</literal> being present,
+            the above DSL snippet effectively adds a mutation rule for the person that sets its <literal>lastName</literal> property.
+        </para>
+        <para>
+            The general form of the model DSL is:
+        </para>
+        <programlisting>
+model {
+  «model-path-to-subject» {
+    «imperative code»
+  }
+}
+        </programlisting>
+        <para>
+            Where there may be multiple blocks.
+        </para>
+        <para>
+            It is also possible to create <apilink class="org.gradle.model.Managed" /> type elements at the root level.
+        </para>
+        <para>
+            The general form of a creation rule is:
+        </para>
+        <programlisting>
+model {
+  «element-name»(«element-type») {
+    «imperative code»
+  }
+}
+        </programlisting>
+        <para>
+            The following model rule is creating the person element:
+        </para>
+        <sample id='modelDslCreate' dir='modelRules/modelDsl' includeLocation="true" title="a DSL creation rule">
+            <sourcefile file='build.gradle' snippet="create-rule"/>
+        </sample>
+        <para>
+            The model DSL is currently quite limited.
+            It is only possible to declare creation and general mutation rules.
+            It is also only possible to refer to the subject by-path and it is not possible for the rule to have inputs.
+            These are all limitations that will be addressed in future Gradle versions.
+        </para>
+    </section>
+    <section>
+        <title>The model report</title>
+        <para>
+            The built-in <link>model</link> task displays the model space as a tree.
+            It can be used to see what the potential elements to bind to are.
+        </para>
+        <sample id='basicRuleSourcePlugin-model-task' dir='modelRules/basicRuleSourcePlugin' title='model task output'>
+            <output args='model' ignoreExtraLines="true"/>
+        </sample>
+        <para>
+            Currently the report only shows the structure of the model space.
+            In future Gradle versions it will also display the types and values of the nodes.
+            Future versions will also provide richer and more interactive ways of exploring the model space.
+        </para>
+    </section>
+    <section>
+        <title>Limitations and future direction</title>
+        <para>
+            Rule based model configuration is the future of Gradle.
+            This area is fledgling, but under very active development.
+            Early experiments have demonstrated that this approach is more efficient, able to provide richer diagnostics and authoring assistance and is more extensible.
+            However, there are currently many limitations.
+        </para>
+        <para>
+            The majority of the development to date has been focused on proving the efficacy of the approach, and building the internal rule execution engine and model graph mechanics.
+            The user facing aspects (e.g the DSL, rule source classes) are yet to be optimized for conciseness and general usability.
+            Likewise, many necessary configuration patterns and constructs are not yet able to be expressed via the API.
+        </para>
+        <para>
+            In conjunction with the addition of better syntax, a richer toolkit of configuration constructs and generally more expressive power, more tooling will be added that will enable
+            build engineers and users alike to comprehend, modify and extend builds in new ways.
+        </para>
+        <para>
+            Due to the inherent nature of the rule based approach, it is more efficient at constructing the build model than today's Gradle.
+            However, in the future Gradle will also leverage the parallelism that this approach enables both at configuration and execution time.
+            Moreover, due to increased transparency of the model Gradle will be able to further reduce build times by caching and pre-computing the build model.
+            Beyond improved general build performance, this will greatly improve the experience when using Gradle from tools such as IDEs.
+        </para>
+        <para>
+            As this area of Gradle is under active development, it will be changing rapidly.
+            Please be sure to consult the documentation of Gradle corresponding to the version you are using and to watch for changes announced in the release notes for future versions.
+        </para>
+    </section>
+</chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/organizeBuildLogic.xml b/subprojects/docs/src/docs/userguide/organizeBuildLogic.xml
index 4681cd8..bd85722 100644
--- a/subprojects/docs/src/docs/userguide/organizeBuildLogic.xml
+++ b/subprojects/docs/src/docs/userguide/organizeBuildLogic.xml
@@ -18,8 +18,8 @@
     <para>Gradle offers a variety of ways to organize your build logic. First of all you can put your build logic
         directly in the action closure of a task. If a couple of tasks share the same logic you can extract this logic
         into a method. If multiple projects of a multi-project build share some logic you can define this method in the
-        parent project. If the build logic gets too complex for being properly modeled by methods you want have an OO
-        Model.
+        parent project. If the build logic gets too complex for being properly modeled by methods then you likely should
+        implement your logic with classes to encapsulate your logic.
         <footnote>
             <para>Which might range from a single class to something very complex.
             </para>
@@ -105,7 +105,19 @@
             <output args="-q show"/>
         </sample>
     </section>
-    
+
+    <section id="sec:configuring_using_external_script">
+        <title>Configuring the project using an external build script</title>
+        <para>You can configure the current project using an external build script. All of the Gradle build language
+            is available in the external script. You can even apply other scripts from the external script.
+        </para>
+        <sample id="configureProjectUsingScript" dir="userguide/tutorial/configureProjectUsingScript" title="Configuring the project using an external build script">
+            <sourcefile file="build.gradle"/>
+            <sourcefile file="other.gradle"/>
+            <output args="-q hello"/>
+        </sample>
+    </section>
+
     <section id='sec:build_sources'>
         <title>Build sources in the <filename>buildSrc</filename> project</title>
         <para>When you run Gradle, it checks for the existence of a directory called <filename>buildSrc</filename>. 
@@ -121,7 +133,7 @@
             <programlisting><xi:include href='../../../../../subprojects/core/src/main/resources/org/gradle/initialization/buildsrc/defaultBuildSourceScript.txt' parse='text'/></programlisting>
         </figure>
         <para>
-            This means that you can just put you build source code in this directory and stick to the layout convention for a 
+            This means that you can just put your build source code in this directory and stick to the layout convention for a 
             Java/Groovy project (see <xref linkend='javalayout'/>).
         </para>
         <para>
@@ -133,8 +145,8 @@
             <sourcefile file="buildSrc/build.gradle"/>
         </sample>
         <para>
-            The <filename>buildSrc</filename> project can be a multi-project build. This works like any other regular Gradle multi-project build. However,
-            you need to make all of the projects that you wish be on the classpath of the actual build <literal>runtime</literal> dependencies of the root project in 
+            The <filename>buildSrc</filename> project can be a multi-project build, just like any other regular multi-project build. However,
+            all of the projects that should be on the classpath of the actual build must be <literal>runtime</literal> dependencies of the root project in
             <filename>buildSrc</filename>. You can do this by adding this to the configuration of each project you wish to export:
         </para>
         <sample id="multiProjectBuildSrc" dir="multiProjectBuildSrc" includeLocation="true" title="Adding subprojects to the root buildSrc project">
@@ -187,17 +199,17 @@
         <para>For reasons we don't fully understand yet, external dependencies are not picked up by Ant's optional
             tasks. But you can easily do it in another way.
             <footnote>
-                <para>In fact, we think this is anyway the nicer solution. Only if your buildscript and Ant's optional
-                    task need the <emphasis>same</emphasis> library you would have to define it two times. In such a
-                    case it would be nice, if Ant's optional task would automatically pickup the classpath defined
-                    in the <literal>gradesettings</literal>.
+                <para>In fact, we think this is a better solution. Only if your buildscript and Ant's optional
+                    task need the <emphasis>same</emphasis> library would you have to define it twice. In such a
+                    case it would be nice if Ant's optional task would automatically pick up the classpath defined
+                    in the “<filename>gradle.settings</filename>” file.
                 </para>
             </footnote>
         </para>
         <sample id="buildLogic" dir="userguide/organizeBuildLogic" title="Ant optional dependencies">
             <sourcefile file="build.gradle"/>
         </sample>
-        <para>This is also nice example for the usage of client modules. The POM file in Maven Central for the
+        <para>This is also a good example for the usage of client modules. The POM file in Maven Central for the
         ant-commons-net task does not provide the right information for this use case.</para>
     </section>
     <section id='sec:philosophy'>
diff --git a/subprojects/docs/src/docs/userguide/osgi.xml b/subprojects/docs/src/docs/userguide/osgi.xml
index 3831d69..0585f7a 100644
--- a/subprojects/docs/src/docs/userguide/osgi.xml
+++ b/subprojects/docs/src/docs/userguide/osgi.xml
@@ -27,7 +27,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the OSGi plugin, include in your build script:</para>
+        <para>To use the OSGi plugin, include the following in your build script:</para>
         <sample id="useOsgiPlugin" dir="osgi" title="Using the OSGi plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -92,7 +92,7 @@
         </section>
     </section>
     <section>
-        <para>The classes in the classes dir are analyzed regarding there package dependencies and the packages they expose.
+        <para>The classes in the classes dir are analyzed regarding their package dependencies and the packages they expose.
             Based on this the <emphasis>Import-Package</emphasis> and the <emphasis>Export-Package</emphasis> values of the
             OSGi Manifest are calculated. If the classpath contains jars with an OSGi bundle, the bundle
             information is used to specify version information for the <emphasis>Import-Package</emphasis>
@@ -102,7 +102,6 @@
             <sourcefile file="build.gradle" snippet="configure-jar"/>
         </sample>
         <para>The first argument of the instruction call is the key of the property. The other arguments form the value.
-            They are joined by Gradle with the <literal>,</literal> separator. To learn more about the available
-            instructions have a look at the <ulink url='http://www.aqute.biz/Code/Bnd'>BND tool</ulink>.</para>
+            To learn more about the available instructions have a look at the <ulink url='http://www.aqute.biz/Code/Bnd'>BND tool</ulink>.</para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/overview.xml b/subprojects/docs/src/docs/userguide/overview.xml
index bc254c1..2de80e4 100644
--- a/subprojects/docs/src/docs/userguide/overview.xml
+++ b/subprojects/docs/src/docs/userguide/overview.xml
@@ -27,7 +27,7 @@
                         Gradle pushes declarative builds to the next level by providing declarative language elements
                         that you can assemble as you like. Those elements also provide build-by-convention support for
                         Java, Groovy, OSGi, Web and Scala projects. Even more, this declarative language is extensible.
-                        Add your own new language elements or enhance the existing ones. Thus providing concise,
+                        Add your own new language elements or enhance the existing ones, thus providing concise,
                         maintainable and comprehensible builds.
                     </para>
                 </listitem>
@@ -46,7 +46,7 @@
                     <para>The suppleness and richness of Gradle finally allows you to apply common design principles to your build.
                         For example, it is very easy to compose your build from reusable
                         pieces of build logic. Inline stuff where unnecessary indirections would be inappropriate. Don't be
-                        forced to tear apart what belongs together (e.g. in your project hierarchy). Thus avoiding smells
+                        forced to tear apart what belongs together (e.g. in your project hierarchy). Avoid smells
                         like shotgun changes or divergent change that turn your build into a maintenance nightmare.
                         At last you can create a well structured, easily maintained, comprehensible build.</para>
                 </listitem>
@@ -89,7 +89,7 @@
                 <listitem>
                     <para>Different teams prefer different ways to manage their external dependencies.
                         Gradle provides convenient support for any strategy. From transitive dependency 
-                        management with remote Maven and Ivy repositories to jars or dirs on the local file system.</para>
+                        management with remote Maven and Ivy repositories to jars or directories on the local file system.</para>
                 </listitem>
             </varlistentry>
             <varlistentry>
@@ -123,9 +123,9 @@
                         difficult to maintain build. The whole design of Gradle is oriented towards being used as a language,
                         not as a rigid framework. And Groovy is our glue that allows you to tell your individual story with
                         the abstractions Gradle (or you) provide. Gradle provides some standard stories but they are not
-                        privileged in any form. This is for us a major distinguishing features compared to other declarative
-                        build systems. Our Groovy support is also not just some simple coating sugar layer. The whole Gradle API
-                        is fully groovynized. Only by that using Groovy is the fun and productivity gain it can be.
+                        privileged in any form. This is for us a major distinguishing feature compared to other declarative
+                        build systems. Our Groovy support is not just sugar coating. The whole Gradle API
+                        is fully Groovy-ized. Adding Groovy results in an enjoyable and productive experience.
                     </para>
                 </listitem>
             </varlistentry>
@@ -151,16 +151,16 @@
     </section>
     <section id='sec:why_groovy'>
         <title>Why Groovy?</title>
-        <para>We think the advantages of an internal DSL (based on a dynamic language) over XML are tremendous in case
-            of <emphasis>build scripts</emphasis>. There are a couple of dynamic languages out there. Why Groovy? The
+        <para>We think the advantages of an internal DSL (based on a dynamic language) over XML are tremendous when used
+            in <emphasis>build scripts</emphasis>. There are a couple of dynamic languages out there. Why Groovy? The
             answer lies in the context Gradle is operating in. Although Gradle is a general purpose build tool at its
             core, its main focus are Java projects.
-            In such projects obviously the team members know Java. We think a build
+            In such projects the team members will be very familiar with Java. We think a build
             should be as transparent as possible to <emphasis>all</emphasis> team members.
         </para>
-        <para>You might argue why not using Java then as the language for build scripts. We think this is a valid
-            question. It would have the highest transparency for your team and the lowest learning curve. But due to
-            limitations of Java such a build language would not be as nice, expressive and powerful as it could be.
+        <para>In that case, you might argue why we don't just use Java as the language for build scripts. We think this is a valid
+            question. It would have the highest transparency for your team and the lowest learning curve, but because of the
+            limitations of Java, such a build language would not be as nice, expressive and powerful as it could be.
             <footnote>
                 <para>At
                     <ulink url='http://www.defmacro.org/ramblings/lisp.html'/>
@@ -170,9 +170,10 @@
             </footnote>
             Languages like Python, Groovy or Ruby do a much better job here. We have chosen Groovy as it offers by far
             the greatest transparency for Java people. Its base syntax is the same as Java's as well as its type system,
-            its package structure and other things. Groovy builds a lot on top of that. But on a common ground with Java.
+            its package structure and other things. Groovy provides much more on top of that, but with the common foundation
+            of Java.
         </para>
-        <para>For Java teams which share also Python or Ruby knowledge or are happy to learn it, the above arguments
+        <para>For Java developers with Python or Ruby knowledge or the desire to learn them, the above arguments
             don't apply. The Gradle design is well-suited for creating another build script engine in JRuby or Jython.
             It just doesn't have the highest priority for us at the moment. We happily support any community effort
             to create additional build script engines.
diff --git a/subprojects/docs/src/docs/userguide/plugins.xml b/subprojects/docs/src/docs/userguide/plugins.xml
index 3b985e2..2a00cfc 100644
--- a/subprojects/docs/src/docs/userguide/plugins.xml
+++ b/subprojects/docs/src/docs/userguide/plugins.xml
@@ -16,135 +16,228 @@
 <chapter id='plugins'>
     <title>Gradle Plugins</title>
     <para>
-        Gradle at its core intentionally provides little useful functionality for real world automation. All of the useful
-        features, such as the ability to compile Java code for example, are added by <emphasis>plugins</emphasis>.
+        Gradle at its core intentionally provides very little for real world automation. All of the useful
+        features, like the ability to compile Java code, are added by <emphasis>plugins</emphasis>.
         Plugins add new tasks (e.g. <apilink class='org.gradle.api.tasks.compile.JavaCompile'/>), domain objects (e.g.
-        <apilink class="org.gradle.api.tasks.SourceSet"/>), conventions (e.g. main Java source is located at
+        <apilink class="org.gradle.api.tasks.SourceSet"/>), conventions (e.g. Java source is located at
         <literal>src/main/java</literal>) as well as extending core objects and objects from other plugins.
     </para>
     <para>
         In this chapter we will discuss how to use plugins and the terminology and concepts surrounding plugins.
     </para>
-    <section id='sec:using_plugins'>
-        <title>Applying plugins</title>
-        <para>
-            Plugins are said to be <emphasis>applied</emphasis>, which is done via the <apilink class="org.gradle.api.Project" method="apply(java.util.Map)" /> method.
-        </para>
-        <sample id="useJavaPlugin" dir="java/quickstart" title="Applying a plugin">
-            <sourcefile file="build.gradle" snippet="use-plugin"/>
-        </sample>
-        <para>
-            Plugins advertise a short name for themselves. In the above case, we are using the short name ‘<literal>java</literal>’ to apply
-            the <apilink class="org.gradle.api.plugins.JavaPlugin" />.
-        </para>
-        <para>
-            We could also have used the following syntax:
-        </para>
-        <sample id="pluginIntro" dir="userguide/tutorial/pluginIntro" title="Applying a plugin by type">
-            <sourcefile file="build.gradle" snippet="apply-by-type"/>
-        </sample>
-        <para>
-            Thanks to Gradle's default imports (see <xref linkend='ide_support'/>) you could also write:
-        </para>
-        <sample id="pluginIntro" dir="userguide/tutorial/pluginIntro" title="Applying a plugin by type">
-            <sourcefile file="build.gradle" snippet="apply-by-type-with-import"/>
-        </sample>
-        <para>
-            The application of plugins is <emphasis>idempotent</emphasis>. That is, a plugin can be applied multiple times. If the plugin
-            has previously been applied, any further applications will have no effect.
-        </para>
-        <para>
-            A plugin is simply any class that implements the <apilink class="org.gradle.api.Plugin"/> interface. Gradle provides
-            the core plugins as part of its distribution so simply applying the plugin as above is all you need to do.
-            For 3rd party plugins however, you need to make the plugins available to the build classpath. For more information
-            on how to do this, see <xref linkend="sec:external_dependencies" />.
-        </para>
-        <para>
-            For more on writing your own plugins, see <xref linkend="custom_plugins" />.
-        </para>
-    </section>
     <section>
         <title>What plugins do</title>
         <para>
-            Applying a plugin to the project allows the plugin to extend the project's capabilities. It can do things
+            Applying a plugin to a project allows the plugin to extend the project's capabilities. It can do things
             such as:
         </para>
         <itemizedlist>
-            <listitem>Add tasks to the project (e.g. compile, test)</listitem>
-            <listitem>Pre-configure added tasks with useful defaults.</listitem>
-            <listitem>Add dependency configurations to the project (see <xref linkend="configurations"/>).</listitem>
-            <listitem>Add new properties and methods to existing type via extensions.</listitem>
+            <listitem>Extend the Gradle model (e.g. add new DSL elements that can be configured)</listitem>
+            <listitem>Configure the project according to conventions (e.g. add new tasks or configure sensible defaults)</listitem>
+            <listitem>Apply specific configuration (e.g. add organizational repositories or enforce standards)</listitem>
         </itemizedlist>
         <para>
-            Let's check this out:
-        </para>
-        <sample id="pluginIntro" dir="userguide/tutorial/pluginIntro" title="Tasks added by a plugin">
-            <sourcefile file="build.gradle" snippet="apply-by-id"/>
-            <output args="-q show"/>
-        </sample>
-        <para>
-            The Java plugin has added a <literal>compileJava</literal> task and a <literal>processResources</literal> task
-            to the project and configured the <literal>destinationDir</literal> property of both of these tasks.
+            By applying plugins, rather than adding logic to the project build script, we can reap a number of benefits.  Applying plugins:
         </para>
+        <itemizedlist>
+            <listitem>Promotes reuse and reduces the overhead of maintaining similar logic across multiple projects</listitem>
+            <listitem>Allows a higher degree of modularization, enhancing comprehensibility and organization</listitem>
+            <listitem>Encapsulates imperative logic and allows build scripts to be as declarative as possible</listitem>
+        </itemizedlist>
     </section>
-    <section id='sub:more_about_convention_objects'>
-        <title>Conventions</title>
+    <section id="sec:types_of_plugins">
+        <title>Types of plugins</title>
         <para>
-            Plugins can pre-configure the project in smart ways to support convention-over-configuration. Gradle
-            provides mechanisms and sophisticated support and it's a key ingredient in powerful-yet-concise build scripts.
+            There are two general types of plugins in Gradle, <emphasis>script</emphasis> plugins and <emphasis>binary</emphasis> plugins.
+            Script plugins are additional build scripts that further configure the build and usually implement a declarative approach to
+            manipulating the build.  They are typically used within a build although they can be externalized and accessed from a remote
+            location.  Binary plugins are classes that implement the <apilink class='org.gradle.api.Plugin'/> interface and adopt a programmatic
+            approach to manipulating the build.  Binary plugins can reside within a build script, within the project hierarchy or externally
+            in a plugin jar.
         </para>
+    </section>
+    <section id='sec:using_plugins'>
+        <title>Applying plugins</title>
         <para>
-            We saw in the example above that the Java plugins adds a task named <literal>compileJava</literal> that has
-            a property named <literal>destinationDir</literal> (that configures where the compiled Java source should be placed).
-            The Java plugin defaults this property to point to <literal>build/classes/main</literal> in the project directory.
-            This is an example of convention-over-configuration via a <emphasis>reasonable default</emphasis>.
-        </para>
+            Plugins are said to be <emphasis>applied</emphasis>, which is done via the <apilink class="org.gradle.api.Project" method="apply(java.util.Map)" /> method.
+            The application of plugins is <emphasis>idempotent</emphasis>. That is, the same plugin can be applied multiple times. If the plugin
+            has previously been applied, any further applications are safe and will have no effect.
+        </para>
+        <section>
+            <title>Script plugins</title>
+            <sample id="configureProjectUsingScript" dir="userguide/tutorial/configureProjectUsingScript" title="Applying a script plugin">
+                <sourcefile file="build.gradle"/>
+            </sample>
+            <para>
+                Script plugins can be applied from a script on the local filesystem or at a remote location.  Filesystem
+                locations are relative to the project directory, while remote script locations are specified with an HTTP URL.
+                Multiple script plugins (of either form) can be applied to a given build.
+            </para>
+        </section>
+        <section id="sec:applying_binary_plugins">
+            <title>Binary plugins</title>
+            <sample id="useJavaPlugin" dir="java/quickstart" title="Applying a binary plugin">
+                <sourcefile file="build.gradle" snippet="use-plugin"/>
+            </sample>
+            <para>
+                Plugins can be applied using a <emphasis>plugin id</emphasis>.  The plugin id serves as a unique identifier
+                for a given plugin.  Core plugins register a short name that can be used as the plugin id. In the above case, we are
+                using the short name ‘<literal>java</literal>’ to apply the <apilink class="org.gradle.api.plugins.JavaPlugin" />.
+                Community plugins, on the other hand, use a fully qualified form for the plugin id (e.g. <literal>com.github.foo.bar</literal>),
+                although some legacy plugins may still utilize a short, unqualified form.
+            </para>
+            <para>
+                Rather than using a plugin id, plugins can also be applied by simply specifying the class of the plugin:
+            </para>
+            <sample id="pluginIntro" dir="userguide/tutorial/pluginIntro" title="Applying a binary plugin by type">
+                <sourcefile file="build.gradle" snippet="apply-by-type"/>
+            </sample>
+            <para>
+                The <literal>JavaPlugin</literal> symbol in the above sample refers to the the <apilink class="org.gradle.api.plugins.JavaPlugin"/>.
+                This class does not strictly need to be imported as the <literal>org.gradle.api.plugins</literal> package is automatically imported in all build scripts
+                (see <xref linkend='ide_support'/>). Furthermore, it is not necessary to append <literal>.class</literal> to identify a class literal in Groovy as it is in Java.
+            </para>
+            <section>
+                <title>Locations of binary plugins</title>
+                <para>
+                    A plugin is simply any class that implements the <apilink class="org.gradle.api.Plugin" /> interface. Gradle provides
+                    the core plugins as part of its distribution so simply applying the plugin as above is all you need to do.
+                    However, non-core binary plugins need to be available to the build classpath before they can be applied.  This can
+                    be achieved in a number of ways, including:
+                </para>
+                <itemizedlist>
+                    <listitem>Defining the plugin as an inline class declaration inside a build script.</listitem>
+                    <listitem>Defining the plugin as a source file under the buildSrc directory in the project (see <xref linkend="sec:build_sources"/>).</listitem>
+                    <listitem>Including the plugin from an external jar defined as a buildscript dependency (see <xref linkend="sec:applying_plugins_buildscript" />).</listitem>
+                    <listitem>Including the plugin from the plugin portal using the plugins DSL (see <xref linkend="sec:plugins_block" />).</listitem>
+                </itemizedlist>
+                <para>
+                    For more on defining your own plugins, see <xref linkend="custom_plugins" />.
+                </para>
+            </section>
+        </section>
+    </section>
+    <section id="sec:applying_plugins_buildscript">
+        <title>Applying plugins with the buildscript block</title>
         <para>
-            We can change this property simply by giving it a new value.
+            Binary plugins that have been published as external jar files can be added to a project by adding the plugin to the
+            build script classpath and then applying the plugin.  External jars can be added to the build script classpath
+            using the <code>buildscript {}</code> block as described in <xref linkend="sec:external_dependencies" />.
         </para>
-        <sample id="pluginConfig" dir="userguide/tutorial/pluginConfig" title="Changing plugin defaults">
-            <sourcefile file="build.gradle"/>
-            <output args="-q show"/>
+        <sample id="applyPluginBuildscript" dir="plugins/buildscript" title="Applying a plugin with the buildscript block">
+            <sourcefile file="build.gradle" snippet="buildscript_block" />
         </sample>
+    </section>
+    <section id="sec:plugins_block">
+        <title>Applying plugins with the plugins DSL</title>
+        <note>
+            <para>
+                The plugins DSL is currently <link linkend="feature_lifecycle">incubating</link>.
+                Please be aware that the DSL and other configuration may change in later Gradle versions.
+            </para>
+        </note>
         <para>
-            However, the <literal>compileJava</literal> task is likely to not be the only task that needs to know where
-            the class files are. 
-        </para>
-        <para>
-            The Java plugin adds the concept of <emphasis>source sets</emphasis> (see <apilink class="org.gradle.api.tasks.SourceSet" />)
-            to describe the aspects of a set of source, one aspect being where the class files should be written to when it is compiled.
-            The Java plugin maps the <literal>destinationDir</literal> property of the <literal>compileJava</literal> task to this aspect of the source set.
+            The new plugins DSL provides a more succinct and convenient way to declare plugin dependencies.  It works with the
+            new <ulink url="http://plugins.gradle.org">Gradle plugin portal</ulink> to provide easy access to both core and community
+            plugins.  The plugins script block configures an instance of <apilink class="org.gradle.plugin.use.PluginDependenciesSpec" />.
         </para>
         <para>
-            We can change where the class files are written via the source set.
+            To apply a core plugin, the short name can be used:
         </para>
-        <sample id="pluginConvention" dir="userguide/tutorial/pluginConvention" title="Plugin convention object">
-            <sourcefile file="build.gradle"/>
-            <output args="-q show"/>
+        <sample id="useJavaPluginDSL" dir="plugins/dsl" title="Applying a core plugin">
+            <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
         <para>
-            In the example above, we applied the Java plugin which, among other things, did the following:
-        </para>
-        <itemizedlist>
-            <listitem>Added a new domain object type: <apilink class="org.gradle.api.tasks.SourceSet" /></listitem>
-            <listitem>Configured a <literal>main</literal> source set with default (i.e. conventional) values for properties</listitem>
-            <listitem>Configured supporting tasks to use these properties to perform work</listitem>
-        </itemizedlist>
-        <para>
-            All of this happened during the <literal>apply plugin: "java"</literal> step. In the example above, we <emphasis>changed</emphasis>
-            the desired location of the class files after this conventional configuration had been performed. Notice by the output with the example
-            that the value for <literal>compileJava.destinationDir</literal> also changed to reflect the configuration change.
+            To apply a community plugin from the portal, the fully qualified plugin id must be used:
         </para>
+        <sample id="useCommunityPluginDSL" dir="plugins/dsl" title="Applying a community plugin">
+            <sourcefile file="build.gradle" snippet="use-community-plugin"/>
+        </sample>
         <para>
-            Consider the case where another task is to consume the classes files. If this task is configured to use the value from
-            <literal>sourceSets.main.output.classesDir</literal>, then changing it in this location will update both the
-            <literal>compileJava</literal> task and this other consumer task whenever it is changed.
-        </para>
+            No further configuration is necessary.  Specifically, there is no need to configure the buildscript classpath.
+            Gradle will resolve the plugin in the plugin portal, locate it, and make it available to the build.
+        </para>
+        <para>
+            See <apilink class="org.gradle.plugin.use.PluginDependenciesSpec" /> for more information on using the Plugin DSL.
+        </para>
+        <section id="plugins_dsl_limitations">
+            <title>Limitations of the plugins DSL</title>
+            <para>
+                The new way to add plugins to a project is much more than a more convenient syntax. The new DSL is processed very
+                differently to the old one. The new mechanism allows Gradle to determine the plugins in use very early and very
+                quickly. This allows Gradle to do smart things such as:
+            </para>
+            <itemizedlist>
+                <listitem>Optimize the loading and reuse of plugin classes.</listitem>
+                <listitem>Allow different plugins to use different versions of dependencies.</listitem>
+                <listitem>Provide editors detailed information about the potential properties and values in the buildscript for
+                editing assistance.</listitem>
+            </itemizedlist>
+            <para>
+                This requires that plugins be specified in a way that Gradle can easily and quickly extract, before executing the
+                rest of the build script. It also requires that the definition of plugins to use be somewhat static.
+            </para>
+            <para>
+                There are some key differences between the new plugin mechanism and the “traditional” <code>apply()</code> method
+                mechanism. There are also some constraints, some of which are temporary limitations while the mechanism is still
+                being developed and some are inherent to the new approach.
+            </para>
+            <section>
+                <title>Constrained Syntax</title>
+                <para>
+                    The new <code>plugins {}</code> block does not support arbitrary Groovy code. It is constrained, in order to be idempotent
+                    (produce the same result every time) and side effect free (safe for Gradle to execute at any time).
+                </para>
+                <para>
+                    The form is:
+                </para>
+                <programlisting>
+plugins {
+    id «plugin id» version «plugin version»
+}
+                </programlisting>
+                <para>
+                    Where <literal>«plugin version»</literal> and <literal>«plugin id»</literal> must be constant, literal, strings.
+                    No other statements are allowed; their presence will cause a compilation error.
+                </para>
+                <para>
+                    The <code>plugins {}</code> block must also be a top level statement in the buildscript. It cannot be nested inside
+                    another construct (e.g. an if-statement or for-loop).
+                </para>
+            </section>
+            <section>
+                <title>Can only be used in build scripts</title>
+                <para>
+                    The <code>plugins {}</code> block can currently only be used in a project's build script. It cannot be used in
+                    script plugins, the settings.gradle file or init scripts.
+                </para>
+                <para>
+                    <emphasis>Future versions of Gradle will remove this restriction.</emphasis>
+                </para>
+            </section>
+            <section>
+                <title>Cannot be used in conjunction with subprojects {}, allprojects {}, etc</title>
+                <para>
+                    It is not possible to use the familiar pattern of applying a plugin to multiple projects at once using <code>subprojects {}</code>,
+                    etc at the moment. There is currently no mechanism for applying a plugin to multiple projects at once. At the moment, each
+                    project that requires a plugin must declare so in the <code>plugins {}</code> block in its buildscript.
+                </para>
+                <para>
+                    <emphasis>Future versions of Gradle will remove this restriction.</emphasis>
+                </para>
+            </section>
+            <para>
+                If the restrictions of the new syntax are prohibitive, the recommended approach is to apply plugins using the
+                <link linkend="sec:applying_plugins_buildscript">buildscript {} block</link>.
+            </para>
+        </section>
+    </section>
+    <section>
+        <title>Finding community plugins</title>
         <para>
-            This ability to configure properties of objects to reflect the value of another object's task at all times (i.e. even when it changes) is
-            known as “<emphasis>convention mapping</emphasis>”. It allows Gradle to provide conciseness through convention-over-configuration and
-            sensible defaults yet not require complete reconfiguration if a conventional default needs to be changed. Without this, in the example above,
-            we would have had to reconfigure every object that needs to work with the class files.
+            Gradle has a vibrant community of plugin developers who contribute plugins for a wide variety of capabilities.
+            The Gradle <ulink url="http://plugins.gradle.org">plugin portal</ulink> provides an interface for searching and
+            exploring community plugins.
         </para>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/userguide/pmdPlugin.xml b/subprojects/docs/src/docs/userguide/pmdPlugin.xml
index b7e743a..8ee50b8 100644
--- a/subprojects/docs/src/docs/userguide/pmdPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/pmdPlugin.xml
@@ -6,7 +6,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the PMD plugin, include in your build script:</para>
+        <para>To use the PMD plugin, include the following in your build script:</para>
         <sample id="usePmdPlugin" dir="codeQuality" title="Using the PMD plugin">
             <sourcefile file="build.gradle" snippet="use-pmd-plugin"/>
         </sample>
@@ -88,7 +88,7 @@
 
     <section>
         <title>Configuration</title>
-        <para>See <apilink class="org.gradle.api.plugins.quality.PmdExtension"/>.</para>
+        <para>See the <apilink class="org.gradle.api.plugins.quality.PmdExtension"/> class in the API documentation.</para>
     </section>
 
 </chapter>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/userguide/potentialTraps.xml b/subprojects/docs/src/docs/userguide/potentialTraps.xml
index 8f1d7d3..3d69d62 100644
--- a/subprojects/docs/src/docs/userguide/potentialTraps.xml
+++ b/subprojects/docs/src/docs/userguide/potentialTraps.xml
@@ -18,7 +18,7 @@
     <section id='sec:groovy_script_variables'>
         <title>Groovy script variables</title>
         <para>For Gradle users it is important to understand how Groovy deals with script variables. Groovy has two types
-            of script variables. One with a local scope and one with a script wide scope.
+            of script variables. One with a local scope and one with a script-wide scope.
         </para>
         <sample id="scope" dir="userguide/tutorial" title="Variables scope: local and script wide">
             <sourcefile file="scope.groovy"/>
diff --git a/subprojects/docs/src/docs/userguide/projectReports.xml b/subprojects/docs/src/docs/userguide/projectReports.xml
index d1e48f9..20216ce 100644
--- a/subprojects/docs/src/docs/userguide/projectReports.xml
+++ b/subprojects/docs/src/docs/userguide/projectReports.xml
@@ -17,9 +17,8 @@
     <title>The Project Report Plugin</title>
 
     <para>The Project report plugin adds some tasks to your project which generate reports containing useful
-        information about your build. Those tasks generate exactly the same content as the command line reports triggered
-        by <userinput>gradle tasks</userinput>, <userinput>gradle dependencies</userinput> and
-        <userinput>gradle properties</userinput> (see <xref linkend="sec:obtaining_information_about_your_build"/>).
+        information about your build. These tasks generate the same content that you get by executing the <userinput>tasks</userinput>, <userinput>dependencies</userinput>, and
+        <userinput>properties</userinput> tasks from the command line (see <xref linkend="sec:obtaining_information_about_your_build"/>).
         In contrast to the command line reports, the report plugin generates the reports into a file. There is also an
         aggregating task that depends on all report tasks added by the plugin.
     </para>
@@ -29,7 +28,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Project report plugin, include in your build script:</para>
+        <para>To use the Project report plugin, include the following in your build script:</para>
 <programlisting language="java">
 apply plugin: 'project-report'
 </programlisting>
@@ -125,7 +124,7 @@ apply plugin: 'project-report'
                     <classname>Set<Project></classname>
                 </td>
                 <td>
-                    <literal>A one element set with the project the plugin was applied to.</literal>
+                    A one element set with the project the plugin was applied to.
                 </td>
                 <td>
                     The projects to generate the reports for.
diff --git a/subprojects/docs/src/docs/userguide/publishingIvy.xml b/subprojects/docs/src/docs/userguide/publishingIvy.xml
index 35278e3..01c295a 100644
--- a/subprojects/docs/src/docs/userguide/publishingIvy.xml
+++ b/subprojects/docs/src/docs/userguide/publishingIvy.xml
@@ -69,8 +69,8 @@
         <title>Publications</title>
         <note>
             <para>
-                If you are not familiar with project artifacts and configurations, you should read the <xref linkend="artifact_management" />
-                that introduces these concepts. This chapter also describes “publishing artifacts” using a different mechanism than what is
+                If you are not familiar with project artifacts and configurations, you should read <xref linkend="artifact_management" />,
+                which introduces these concepts. This chapter also describes “publishing artifacts” using a different mechanism than what is
                 described in this chapter. The publishing functionality described here will eventually supersede that functionality.
             </para>
         </note>
@@ -80,7 +80,7 @@
             <apilink class="org.gradle.api.publish.PublishingExtension" method="getPublications()" /> container. Each publication has a unique name within the project.
         </para>
         <para>
-            For the “<literal>ivy-publish</literal>” plugin to have any effect, a <apilink class="org.gradle.api.publish.ivy.IvyPublication" /> must be added to the set of publications.
+            For the “<literal>ivy-publish</literal>” plugin to have any effect, an <apilink class="org.gradle.api.publish.ivy.IvyPublication" /> must be added to the set of publications.
             This publication determines which artifacts are actually published as well as the details included in the associated Ivy module descriptor file.
             A publication can be configured by adding components, customizing artifacts, and by modifying the generated module descriptor file directly.
         </para>
@@ -116,7 +116,7 @@
             <para>
                  In the following example, artifacts and runtime dependencies are taken from the `java` component, which is added by the <literal>Java Plugin</literal>.
              </para>
-            <sample dir="ivy-publish/quickstart" id="publishing_ivy:publish-component-snippet" title="Publishing a java module to Ivy">
+            <sample dir="ivy-publish/quickstart" id="publishing_ivy:publish-component-snippet" title="Publishing a Java module to Ivy">
                 <sourcefile file="build.gradle" snippet="publish-component" />
             </sample>
         </section>
@@ -137,25 +137,29 @@
                 <sourcefile file="build.gradle" snippet="publish-custom-artifact" />
             </sample>
             <para>
-                See <apilink class="org.gradle.api.publish.ivy.IvyPublication" /> for more detailed documentation on how artifacts can be customized.
+                See the <apilink class="org.gradle.api.publish.ivy.IvyPublication" /> class in the API documentation for more detailed information on how artifacts can be customized.
             </para>
         </section>
         <section>
             <title>Identity values for the published project</title>
             <para>
-                The generated Ivy module descriptor file contains an <literal><info></literal> tag that identifies the module.
-                The default identity values are derived from the following project properties:
+                The generated Ivy module descriptor file contains an <literal><info></literal> element that identifies the module.
+                The default identity values are derived from the following:
             </para>
             <itemizedlist>
                 <listitem><literal>organisation</literal> - <apilink class="org.gradle.api.Project" method="getGroup()" /></listitem>
                 <listitem><literal>module</literal> - <apilink class="org.gradle.api.Project" method="getName()" /></listitem>
                 <listitem><literal>revision</literal> - <apilink class="org.gradle.api.Project" method="getVersion()" /></listitem>
                 <listitem><literal>status</literal> - <apilink class="org.gradle.api.Project" method="getStatus()" /></listitem>
+                <listitem><literal>branch</literal> - (not set)</listitem>
             </itemizedlist>
 
             <para>
                 Overriding the default identity values is easy: simply specify the <literal>organisation</literal>, <literal>module</literal>
-                or <literal>revision</literal> attributes when configuring the <literal>IvyPublication</literal>.
+                or <literal>revision</literal> attributes when configuring the <literal>IvyPublication</literal>.  The <literal>status</literal>
+                and <literal>branch</literal> attributes can be set via the <literal>descriptor</literal> property (see
+                <apilink class="org.gradle.api.publish.ivy.IvyModuleDescriptorSpec" />).  The <literal>descriptor</literal> property can also be
+                used to add additional custom elements as children of the <literal><info></literal> element.
             </para>
             <sample dir="ivy-publish/multiple-publications" id="publishing_ivy:publish-customize-identity" title="customizing the publication identity">
                 <sourcefile file="build.gradle" snippet="customize-identity" />
@@ -166,7 +170,8 @@
             </tip>
             <para>
                 Gradle will handle any valid Unicode character for organisation, module and revision (as well as artifact name, extension and classifier).
-                The only values that are explicitly prohibited are '<literal>\</literal>', '<literal>/</literal>' and any ISO control character. The supplied values are validated early in publication.
+                The only values that are explicitly prohibited are '<literal>\</literal>', '<literal>/</literal>' and any ISO control character.
+                The supplied values are validated early during publication.
             </para>
         </section>
         <section>
@@ -179,11 +184,11 @@
                 <sourcefile file="build.gradle" snippet="customize-descriptor" />
             </sample>
             <para>
-                In this example we are adding a 'description' element to the generated Ivy dependency descriptor, but this this hook, allows you to modify any aspect
+                In this example we are simply adding a 'description' element to the generated Ivy dependency descriptor, but this hook allows you to modify any aspect
                 of the generated descriptor. For example, you could replace the version range for a dependency with the actual version used to produce the build.
             </para>
             <para>
-                See <apilink class="org.gradle.api.publish.ivy.IvyModuleDescriptor" method="withXml(org.gradle.api.Action)" /> for the relevant API reference documentation.
+                See <apilink class="org.gradle.api.publish.ivy.IvyModuleDescriptorSpec" method="withXml(org.gradle.api.Action)" /> in the API documentation for more information.
             </para>
             <para>
                 It is possible to modify virtually any aspect of the created descriptor should you need to.
@@ -232,8 +237,9 @@
             combination in the <literal>publishing.publications</literal> and <literal>publishing.repositories</literal> containers respectively.
         </para>
         <para>
-            The created task is named using the pattern "<literal>publish«<emphasis>NAME OF PUBLICATION</emphasis>»PublicationTo«<emphasis>NAME OF REPOSITORY</emphasis>»Repository</literal>".
-            So in this example a single <apilink class="org.gradle.api.publish.ivy.tasks.PublishToIvyRepository" /> task is be added, named '<literal>publishIvyJavaPublicationToIvyRepository</literal>'.
+            The created task is named “<literal>publish«<emphasis>PUBNAME</emphasis>»PublicationTo«<emphasis>REPONAME</emphasis>»Repository</literal>”,
+            which is “<literal>publishIvyJavaPublicationToIvyRepository</literal>” for this example.  This task is of type
+            <apilink class="org.gradle.api.publish.ivy.tasks.PublishToIvyRepository" />.
         </para>
         <sample dir="ivy-publish/quickstart" id="publishingIvyPublishSingle" title="Choosing a particular publication to publish">
             <sourcefile file="build.gradle"/>
@@ -261,14 +267,14 @@
             Since descriptor file generation is performed by a separate task, this is very easy to do.
         </para>
         <para>
-            The “<literal>ivy-publish</literal>” plugin automatically wires in one <apilink class="org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor" /> task
-            for each registered <apilink class="org.gradle.api.publish.ivy.IvyPublication" />.
-            This task is given a name based on the name of the publication:  "<literal>generateDescriptorFileFor«<emphasis>NAME OF PUBLICATION</emphasis>»Publication</literal>".
-            So in the example above where the publication is named "<literal>ivyJava</literal>", the task will be named "<literal>generateDescriptorFileForIvyJavaPublication</literal>".
+            The “<literal>ivy-publish</literal>” plugin creates one <apilink class="org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor" /> task
+            for each registered <apilink class="org.gradle.api.publish.ivy.IvyPublication" />,
+            named  “<literal>generateDescriptorFileFor«<emphasis>PUBNAME</emphasis>»Publication</literal>”, which will be
+            “<literal>generateDescriptorFileForIvyJavaPublication</literal>” for the previous example of the “<literal>ivyJava</literal>” publication.
         </para>
         <para>
-            You can specify where the generated Ivy file will be located by setting the <literal>destination</literal> property on the generate task.
-            By default this file is generated to <literal>build/publications/«<emphasis>NAME OF PUBLICATION</emphasis>»/ivy.xml</literal>.
+            You can specify where the generated Ivy file will be located by setting the <literal>destination</literal> property on the generated task.
+            By default this file is written to “<literal>build/publications/«<emphasis>PUBNAME</emphasis>»/ivy.xml</literal>”.
         </para>
         <sample dir="ivy-publish/descriptor-customization" id="publishingIvyGenerateDescriptor" title="Generating the Ivy module descriptor file">
             <sourcefile file="build.gradle" snippet="generate" />
@@ -279,36 +285,36 @@
                 The “<literal>ivy-publish</literal>” plugin leverages some experimental support for late plugin configuration,
                 and the <literal>GenerateIvyDescriptor</literal> task will not be constructed until the publishing extension is configured.
                 The simplest way to ensure that the publishing plugin is configured when you attempt to access the <literal>GenerateIvyDescriptor</literal> task
-                is to place the access inside a <literal>publishing</literal> block, as the example above demonstrates.
+                is to place the access inside a <literal>model</literal> block, as the example above demonstrates.
             </para>
             <para>
                 The same applies to any attempt to access publication-specific tasks like <apilink class="org.gradle.api.publish.ivy.tasks.PublishToIvyRepository" />.
-                These tasks should be referenced from within a <literal>publishing</literal> block.
+                These tasks should be referenced from within a <literal>model</literal> block.
             </para>
         </note>
     </section>
     <section id="publishing_ivy:example">
         <title>Complete example</title>
         <para>
-            The following example demonstrates publishing with a multi-project build. Each project publishes a java component and a configured additional source artifact.
+            The following example demonstrates publishing with a multi-project build. Each project publishes a Java component and a configured additional source artifact.
             The descriptor file is customized to include the project description for each project.
         </para>
-        <sample dir="ivy-publish/java-multi-project" id="publishing_ivy:complete_example" title="Publishing a java module">
+        <sample dir="ivy-publish/java-multi-project" id="publishing_ivy:complete_example" title="Publishing a Java module">
             <sourcefile file="build.gradle" />
         </sample>
         <para>
             The result is that the following artifacts will be published for each project:
         </para>
         <itemizedlist>
-            <listitem>The Ivy module descriptor file: <filename>ivy-1.0.xml</filename>.</listitem>
-           <listitem>The primary “jar” artifact for the java component: <filename>project1-1.0.jar</filename>.</listitem>
-           <listitem>The source “jar” artifact that has been explicitly configured:<filename>project1-1.0-source.jar</filename>.</listitem>
+            <listitem>The Ivy module descriptor file: “<filename>ivy-1.0.xml</filename>”.</listitem>
+           <listitem>The primary “jar” artifact for the Java component: “<filename>project1-1.0.jar</filename>”.</listitem>
+           <listitem>The source “jar” artifact that has been explicitly configured: “<filename>project1-1.0-source.jar</filename>”.</listitem>
        </itemizedlist>
         <para>
-            When <literal>project1</literal> is published, the module descriptor (i.e. the <filename>ivy.xml</filename> file) that is produced will look like…
+            When <literal>project1</literal> is published, the module descriptor (i.e. the <filename>ivy.xml</filename> file) that is produced will look like:
         </para>
         <tip>
-            <para>Note that the <literal>«PUBLICATION-TIME-STAMP»</literal> in this example Ivy module descriptor will be the timestamp of when the descriptor was generated.</para>
+            <para>Note that <literal>«PUBLICATION-TIME-STAMP»</literal> in this example Ivy module descriptor will be the timestamp of when the descriptor was generated.</para>
         </tip>
         <sample dir="ivy-publish/java-multi-project" id="publishing_ivy:output_ivy.xml" title="Example generated ivy.xml">
             <sourcefile file="output-ivy.xml" snippet="content" />
@@ -318,12 +324,12 @@
         <title>Future features</title>
         <para>
             The “<literal>ivy-publish</literal>” plugin functionality as described above is incomplete, as the feature is still <link linkend="feature_lifecycle">incubating</link>.
-            Over the coming Gradle releases, the functionality will be expanded to include (but not limited to):
+            In upcoming Gradle releases, the functionality will be expanded to include (but not limited to):
         </para>
         <itemizedlist>
             <listitem>Convenient customization of module attributes (<literal>module</literal>, <literal>organisation</literal> etc.)</listitem>
             <listitem>Convenient customization of dependencies reported in <literal>module descriptor</literal>.</listitem>
-            <listitem>Multiple discreet publications per project</listitem>
+            <listitem>Multiple discrete publications per project</listitem>
         </itemizedlist>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/publishingMaven.xml b/subprojects/docs/src/docs/userguide/publishingMaven.xml
index a766178..38ea91c 100644
--- a/subprojects/docs/src/docs/userguide/publishingMaven.xml
+++ b/subprojects/docs/src/docs/userguide/publishingMaven.xml
@@ -115,7 +115,7 @@
             <para>
                  In the following example, artifacts and runtime dependencies are taken from the `java` component, which is added by the <literal>Java Plugin</literal>.
              </para>
-            <sample dir="maven-publish/quickstart" id="publishing_maven:publish-component" title="Adding a MavenPublication for a java component">
+            <sample dir="maven-publish/quickstart" id="publishing_maven:publish-component" title="Adding a MavenPublication for a Java component">
                 <sourcefile file="build.gradle" snippet="publish-component" />
             </sample>
         </section>
@@ -136,7 +136,7 @@
                 <sourcefile file="build.gradle" snippet="publish-custom-artifact" />
             </sample>
             <para>
-                See <apilink class="org.gradle.api.publish.maven.MavenPublication" /> for more detailed documentation on how artifacts can be customized.
+                See the <apilink class="org.gradle.api.publish.maven.MavenPublication" /> class in the API documentation for more information about how artifacts can be customized.
             </para>
         </section>
 
@@ -174,7 +174,7 @@
         <section>
             <title>Modifying the generated POM</title>
             <para>
-                At times, the POM file generated from the project information will need to be tweaked before publishing. The “<literal>maven-publish</literal>”
+                The generated POM file may need to be tweaked before publishing. The “<literal>maven-publish</literal>”
                 plugin provides a hook to allow such modification.
             </para>
             <sample dir="maven-publish/pomCustomization" id="publishing_maven:pom_modification" title="Modifying the POM file">
@@ -185,7 +185,7 @@
                 For example, you could replace the version range for a dependency with the actual version used to produce the build.
             </para>
             <para>
-                See <apilink class="org.gradle.api.publish.maven.MavenPom" method="withXml(org.gradle.api.Action)" /> for the relevant API reference documentation.
+                See <apilink class="org.gradle.api.publish.maven.MavenPom" method="withXml(org.gradle.api.Action)" /> in the API documentation for more information.
             </para>
             <para>
                 It is possible to modify virtually any aspect of the created POM should you need to.
@@ -235,16 +235,17 @@
             combination in the <literal>publishing.publications</literal> and <literal>publishing.repositories</literal> containers respectively.
         </para>
         <para>
-            The created task is named using the pattern "<literal>publish«<emphasis>NAME OF PUBLICATION</emphasis>»PublicationTo«<emphasis>NAME OF REPOSITORY</emphasis>»Repository</literal>".
+            The created task is named “<literal>publish«<emphasis>PUBNAME</emphasis>»PublicationTo«<emphasis>REPONAME</emphasis>»Repository</literal>”.
         </para>
         <sample dir="maven-publish/quickstart" id="publishingMavenPublishMinimal" title="Publishing a project to a Maven repository">
             <sourcefile file="build.gradle"/>
             <output args="publish"/>
         </sample>
         <para>
-            So in this example a single <apilink class="org.gradle.api.publish.maven.tasks.PublishToMavenRepository" /> task is be added, named '<literal>publishMavenJavaPublicationToMavenRepository</literal>'.
+        	In this example, a task named “<literal>publishMavenJavaPublicationToMavenRepository</literal>” is created, which is of type
+            <apilink class="org.gradle.api.publish.maven.tasks.PublishToMavenRepository" />. 
             This task is wired into the <literal>publish</literal> lifecycle task.
-            Executing <literal>gradle publish</literal> builds the POM file and all of the artifacts to be published, and transfers them to the repository.
+            Executing “<literal>gradle publish</literal>” builds the POM file and all of the artifacts to be published, and transfers them to the repository.
         </para>
     </section>
     <section id="publishing_maven:install">
@@ -257,15 +258,14 @@
             You do not need to have `mavenLocal` in your `publishing.repositories` section.
         </para>
         <para>
-            The created task is named using the pattern "<literal>publish«<emphasis>NAME OF PUBLICATION</emphasis>»PublicationToMavenLocal</literal>".
+            The created task is named “<literal>publish«<emphasis>PUBNAME</emphasis>»PublicationToMavenLocal</literal>”.
         </para>
         <sample dir="maven-publish/quickstart" id="publishingMavenPublishLocal" title="Publish a project to the Maven local repository">
             <output args="publishToMavenLocal"/>
         </sample>
         <para>
-            So in this example you can see that a single <apilink class="org.gradle.api.publish.maven.tasks.PublishToMavenLocal" /> task is be added,
-            named '<literal>publishMavenJavaPublicationToMavenLocal</literal>'. This task is wired into the <literal>publishToMavenLocal</literal> lifecycle task.
-            Executing <literal>gradle publishToMavenLocal</literal> builds the POM file and all of the artifacts to be published, and 'installs' them into the local Maven repository.
+            The resulting task in this example is named “<literal>publishMavenJavaPublicationToMavenLocal</literal>”. This task is wired into the <literal>publishToMavenLocal</literal> lifecycle task.
+            Executing “<literal>gradle publishToMavenLocal</literal>” builds the POM file and all of the artifacts to be published, and “installs” them into the local Maven repository.
         </para>
     </section>
     <section id="publishing_maven:generate-pom">
@@ -276,9 +276,9 @@
         </para>
         <para>
             The task for generating the POM file is of type <apilink class="org.gradle.api.publish.maven.tasks.GenerateMavenPom"/>, and it is given a name based on the name
-            of the publication: "<literal>generatePomFileFor«<emphasis>NAME OF PUBLICATION</emphasis>»Publication</literal>". So in the example below,
-            where the publication is named "<literal>mavenCustom</literal>",
-            the task will be named "<literal>generatePomFileForMavenCustomPublication</literal>".
+            of the publication: “<literal>generatePomFileFor«<emphasis>PUBNAME</emphasis>»Publication</literal>”. So in the example below,
+            where the publication is named “<literal>mavenCustom</literal>”,
+            the task will be named “<literal>generatePomFileForMavenCustomPublication</literal>”.
         </para>
         <sample dir="maven-publish/pomCustomization" id="publishingMavenGeneratePom" title="Generate a POM file without publishing">
             <sourcefile file="build.gradle" snippet="generate" />
@@ -293,11 +293,11 @@
                 The “<literal>maven-publish</literal>” plugin leverages some experimental support for late plugin configuration,
                 and any <literal>GenerateMavenPom</literal> tasks will not be constructed until the publishing extension is configured.
                 The simplest way to ensure that the publishing plugin is configured when you attempt to access the <literal>GenerateMavenPom</literal> task
-                is to place the access inside a <literal>publishing</literal> block, as the example above demonstrates.
+                is to place the access inside a <literal>model</literal> block, as the example above demonstrates.
             </para>
             <para>
                 The same applies to any attempt to access publication-specific tasks like <apilink class="org.gradle.api.publish.maven.tasks.PublishToMavenRepository"/>.
-                These tasks should be referenced from within a <literal>publishing</literal> block.
+                These tasks should be referenced from within a <literal>model</literal> block.
             </para>
         </note>
     </section>
diff --git a/subprojects/docs/src/docs/userguide/scalaPlugin.xml b/subprojects/docs/src/docs/userguide/scalaPlugin.xml
index f44f371..c4df510 100644
--- a/subprojects/docs/src/docs/userguide/scalaPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/scalaPlugin.xml
@@ -18,14 +18,14 @@
 
     <para>The Scala plugin extends the Java plugin to add support for Scala projects. It can deal with
         Scala code, mixed Scala and Java code, and even pure Java code (although we don't necessarily recommend to use it for the latter).
-        The plugin supports <emphasis>joint compilation</emphasis>, which allows to freely mix and match Scala and Java code,
+        The plugin supports <emphasis>joint compilation</emphasis>, which allows you to freely mix and match Scala and Java code,
         with dependencies in both directions. For example, a Scala class can extend a Java class that in turn extends a Scala class.
         This makes it possible to use the best language for the job, and to rewrite any class in the other language if needed.
     </para>
 
     <section>
         <title>Usage</title>
-        <para>To use the Scala plugin, include in your build script:</para>
+        <para>To use the Scala plugin, include the following in your build script:</para>
         <sample id="useScalaPlugin" dir="scala/quickstart" title="Using the Scala plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -149,7 +149,7 @@
 
         <section>
             <title>Changing the project layout</title>
-            <para>Just like the Java plugin, the Scala plugin allows to configure custom locations for Scala production and test sources.</para>
+            <para>Just like the Java plugin, the Scala plugin allows you to configure custom locations for Scala production and test sources.</para>
             <sample id="customScalaSourceLayout" dir="scala/customizedLayout" title="Custom Scala source layout">
                 <sourcefile file="build.gradle" snippet="custom-source-locations"/>
             </sample>
@@ -185,7 +185,7 @@
     <section id="sec:configure_scala_classpath">
         <title>Automatic configuration of scalaClasspath</title>
         <para>
-            <literal>ScalaCompile</literal> and <literal>ScalaDoc</literal> tasks consume Scala in two ways: on their <literal>classpath</literal>,
+            The <literal>ScalaCompile</literal> and <literal>ScalaDoc</literal> tasks consume Scala code in two ways: on their <literal>classpath</literal>,
             and on their <literal>scalaClasspath</literal>. The former is used to locate classes referenced by the source code, and will typically
             contain <literal>scala-library</literal> along with other libraries. The latter is used to load and execute the Scala compiler
             and Scaladoc tool, respectively, and should only contain the <literal>scala-compiler</literal> library and its dependencies.
@@ -316,13 +316,13 @@
         <title>Compiling in external process</title>
         <para>
             When <literal>scalaCompileOptions.fork</literal> is set to <literal>true</literal>, compilation will take place
-            in an external process. The details of forking depend on which compiler is used. The Ant based compiler
-            (<literal>scalaCompileOptions.useAnt = true</literal>) will fork a new process for every <literal>ScalaCompile</literal> task,
+            in an external process. The Ant based compiler
+            (<literal>scalaCompileOptions.useAnt = true</literal>) will fork a new process for every invocation of the <literal>ScalaCompile</literal> task,
             and does not fork by default. The Zinc based compiler (<literal>scalaCompileOptions.useAnt = false</literal>) will leverage
             the Gradle compiler daemon, and does so by default.
         </para>
-        <para>Memory settings for the external process default to the JVM's defaults. To adjust memory settings,
-            configure <literal>scalaCompileOptions.forkOptions</literal> as needed:
+        <para>Memory settings for the external process default to the defaults of the JVM. To adjust memory settings,
+            configure the <literal>scalaCompileOptions.forkOptions</literal> property as needed:
             <sample id="zinc" dir="scala/zinc" title="Adjusting memory settings">
                 <sourcefile file="build.gradle" snippet="adjust-memory"/>
             </sample>
@@ -348,14 +348,15 @@
         </para>
 
         <para>
-            Except where noted in the<ulink url="http://gradle.org/docs/current/dsl/org.gradle.api.tasks.scala.ScalaCompile.html">API documentation</ulink>,
+            Except where noted in the <ulink url="http://gradle.org/docs/current/dsl/org.gradle.api.tasks.scala.ScalaCompile.html">API documentation</ulink>,
             the Zinc based compiler supports exactly the same configuration options as the Ant based compiler. Note, however, that the Zinc compiler requires
             Java 6 or higher to run. This means that Gradle itself has to be run with Java 6 or higher.
         </para>
 
         <para>
-            The Scala plugin adds a configuration named <literal>zinc</literal> to resolve the Zinc library and its dependencies. To override the
-            Zinc version that Gradle uses by default, add an explicit Zinc dependency (for example <literal>zinc "com.typesafe.zinc:zinc:0.1.4"</literal>).
+            The Scala plugin adds a configuration named <literal>zinc</literal> to resolve the Zinc library and its dependencies.
+            Gradle will have a default version of the Zinc library, but if you want to override the
+            Zinc version that Gradle uses, add an explicit dependency like <literal>“com.typesafe.zinc:zinc:0.1.4”</literal>.
             Regardless of which Zinc version is used, Zinc will always use the Scala compiler found on the <literal>scalaTools</literal> configuration.
         </para>
 
@@ -369,7 +370,7 @@
             by <literal>scalaCompileOptions.incrementalOptions.analysisFile</literal> (which has a sensible default). In a multi-project build, analysis
             files are passed on to downstream <literal>ScalaCompile</literal> tasks to enable incremental compilation across project boundaries. For
             <literal>ScalaCompile</literal> tasks added by the Scala plugin, no configuration is necessary to make this work. For other
-            <literal>ScalaCompile</literal> tasks, <literal>scalaCompileOptions.incrementalOptions.publishedCode</literal> needs to be configured to point
+            <literal>ScalaCompile</literal> tasks that you might add, the property <literal>scalaCompileOptions.incrementalOptions.publishedCode</literal> needs to be configured to point
             to the classes folder or Jar archive by which the code is passed on to compile class paths of downstream <literal>ScalaCompile</literal> tasks.
             Note that if <literal>publishedCode</literal> is not set correctly, downstream tasks may not recompile code affected by upstream changes,
             leading to incorrect compilation results.
@@ -401,4 +402,4 @@
             the plugin adds a Scala facet and a Scala compiler library that matches the Scala version on the project's class path.
         </para>
     </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/signingPlugin.xml b/subprojects/docs/src/docs/userguide/signingPlugin.xml
index f0cccc4..94ce070 100644
--- a/subprojects/docs/src/docs/userguide/signingPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/signingPlugin.xml
@@ -30,7 +30,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the Signing plugin, include in your build script:</para>
+        <para>To use the Signing plugin, include the following in your build script:</para>
         <sample id="useSigningPlugin" dir="signing/maven" title="Using the Signing plugin">
             <sourcefile file="build.gradle" snippet="use-plugin"/>
         </sample>
@@ -49,12 +49,12 @@
             <listitem><para>The passphrase used to protect your private key.</para></listitem>
         </itemizedlist>
         <para>
-            These items must be supplied as the property projects <literal>signing.keyId</literal>, <literal>signing.password</literal> 
-            and <literal>signing.secretKeyRingFile</literal> respectively. Given the personal and private nature of these values, a good practice
+            These items must be supplied as the values of properties <literal>signing.keyId</literal>,  
+            <literal>signing.secretKeyRingFile</literal>, and <literal>signing.password</literal> respectively. Given the personal and private nature of these values, a good practice
             is to store them in the user <literal>gradle.properties</literal> file (described in <xref linkend="sec:gradle_properties_and_system_properties"/>).
         </para>
         <programlisting>
-            signing.keyId=24875D73
+signing.keyId=24875D73
 signing.password=secret
 signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg
         </programlisting>
@@ -70,9 +70,11 @@ import org.gradle.plugins.signing.Sign
 
 gradle.taskGraph.whenReady { taskGraph ->
     if (taskGraph.allTasks.any { it instanceof Sign }) {
-        // Use Java 6's console to read from the console (no good for a CI environment)
+        // Use Java 6's console to read from the console (no good for
+        // a CI environment)
         Console console = System.console()
-        console.printf "\n\nWe have to sign some things in this build.\n\nPlease enter your signing details.\n\n"
+        console.printf "\n\nWe have to sign some things in this build." +
+                       "\n\nPlease enter your signing details.\n\n"
 
         def id = console.readLine("PGP Key Id: ")
         def file = console.readLine("PGP Secret Key Ring File (absolute path): ")
@@ -98,7 +100,7 @@ gradle.taskGraph.whenReady { taskGraph ->
             <title>Signing Configurations</title>
             <para>
                 It is common to want to sign the artifacts of a configuration. For example, the <link linkend="java_plugin">Java plugin</link> 
-                configures a jar to built and this jar artifact is added to the <literal>archives</literal> configuration. 
+                configures a jar to build and this jar artifact is added to the <literal>archives</literal> configuration. 
                 Using the Signing DSL, you can specify that all of the artifacts of this configuration should be signed.
             </para>
             <sample id="signingArchives" dir="signing/maven" title="Signing a configuration">
@@ -178,4 +180,4 @@ gradle.taskGraph.whenReady { taskGraph ->
         </para>
     </section>
     
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/subprojects/docs/src/docs/userguide/sonarRunnerPlugin.xml b/subprojects/docs/src/docs/userguide/sonarRunnerPlugin.xml
index e1fb5d6..0608bab 100644
--- a/subprojects/docs/src/docs/userguide/sonarRunnerPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/sonarRunnerPlugin.xml
@@ -17,11 +17,15 @@
     <title>The Sonar Runner Plugin</title>
     <note>
         <para>
-            The Sonar runner plugin is currently <link linkend="feature_lifecycle">incubating</link>. Please be aware that the DSL and other configuration may change in later Gradle versions.
+            The Sonar Runner plugin is currently <link linkend="feature_lifecycle">incubating</link>.
+            Please be aware that the DSL and other configuration may change in later Gradle versions.
+        </para>
+        <para>
+            It is intended that this plugin will replace the older <link linkend="sonar_plugin">Sonar Plugin</link> in a future Gradle version.
         </para>
     </note>
     <para>The Sonar Runner plugin provides integration with <ulink url="http://www.sonarsource.org">Sonar</ulink>,
-        a web-based platform for monitoring code quality. It is based on the <ulink url="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">Sonar Runner</ulink>,
+        a web-based platform for monitoring code quality. It is based on the <ulink url="http://docs.codehaus.org/display/SONAR/Analyzing+with+SonarQube+Runner">Sonar Runner</ulink>,
         a Sonar client component that analyzes source code and build outputs, and stores all collected information in the Sonar database.
         Compared to using the standalone Sonar Runner, the Sonar Runner plugin offers the following benefits:
     </para>
@@ -30,7 +34,7 @@
             <term>Automatic provisioning of Sonar Runner</term>
             <listitem>
                 <para>The ability to execute the Sonar Runner via a regular Gradle task makes it available anywhere Gradle is available
-                    (developer build, CI server, etc.), without the need to download, setup, and maintain a Sonar Runner installation.</para>
+                    (developer build, CI server, etc.), without the need to manually download, setup, and maintain a Sonar Runner installation.</para>
             </listitem>
         </varlistentry>
         <varlistentry>
@@ -49,16 +53,14 @@
     </variablelist>
 
     <section>
-        <title>Plugin Status and Compatibility</title>
+        <title>Sonar Runner version and compatibility</title>
         <para>
-            The Sonar Runner plugin is the successor to the <link linkend="sonar_plugin">Sonar Plugin</link>. It is currently
-            <link linkend="sec:incubating_state">incubating</link>. The plugin is based on Sonar Runner 2.0, which makes it compatible
-            with Sonar 2.11 and higher. Unlike the Sonar plugin, the Sonar Runner plugin works fine with Sonar 3.4 and higher.
+            The default version of the Sonar Runner used by the plugin is 2.3, which makes it compatible with Sonar 3.0 and higher.
+            For compatibility with Sonar versions earlier than 3.0, you can configure the use of an earlier Sonar Runner version (see <xref linkend="sec:specify_sonar_runner_version"/>).
         </para>
     </section>
-
     <section>
-        <title>Getting Started</title>
+        <title>Getting started</title>
         <para>To get started, apply the Sonar Runner plugin to the project to be analyzed.</para>
         <sample id="quickstart" dir="sonarRunner/quickstart" title="Applying the Sonar Runner plugin">
             <sourcefile file="build.gradle" snippet="apply-plugin"/>
@@ -78,19 +80,23 @@
 
     <section>
         <title>Configuring the Sonar Runner</title>
-        <para>The Sonar Runner plugin adds a <apilink class="org.gradle.api.sonar.runner.SonarRunner" /> extension to the project,
-            which allows to configure the Sonar Runner via key/value pairs known as <firstterm>Sonar properties</firstterm>. A typical base line configuration
+        <para>The Sonar Runner plugin adds a <apilink class="org.gradle.sonar.runner.SonarRunnerRootExtension" /> extension to the project and a
+            <apilink class="org.gradle.sonar.runner.SonarRunnerExtension" /> extension to its subprojects,
+            which allows you to configure the Sonar Runner via key/value pairs known as <firstterm>Sonar properties</firstterm>. A typical base line configuration
             includes connection settings for the Sonar server and database.
         </para>
         <sample id="quickstart" dir="sonarRunner/quickstart" title="Configuring Sonar connection settings">
             <sourcefile file="build.gradle" snippet="connection-settings"/>
         </sample>
         <para>
+            Alternatively, Sonar properties can be set from the command line. See <xref linkend="sec:sonar_command_line_parameters" /> for more information.
+        </para>
+        <para>
             For a complete list of standard Sonar properties, consult the <ulink url="http://docs.codehaus.org/display/SONAR/Analysis+Parameters">Sonar documentation</ulink>.
             If you happen to use additional Sonar plugins, consult their documentation.
         </para>
-        <para>
-            Alternatively, Sonar properties can be set from the command line. See <xref linkend="sec:sonar_command_line_parameters" /> for more information.
+        <para>In addition to set Sonar properties, the <apilink class="org.gradle.sonar.runner.SonarRunnerRootExtension" /> extension allows the configuration of the Sonar Runner version and
+            the <apilink class="org.gradle.process.JavaForkOptions" /> of the forked Sonar Runner process.
         </para>
         <para>
             The Sonar Runner plugin leverages information contained in Gradle's object model to provide smart defaults for many of the standard Sonar properties.
@@ -108,7 +114,7 @@
             </thead>
             <tr>
                 <td>sonar.projectKey</td>
-                <td>"$project.group:$project.name" (for root project of analysed hierarchy; left to Sonar Runner otherwise)</td>
+                <td>“$project.group:$project.name” (for root project of analysed hierarchy; left to Sonar Runner otherwise)</td>
             </tr>
             <tr>
                 <td>sonar.projectName</td>
@@ -128,11 +134,11 @@
             </tr>
             <tr>
                 <td>sonar.working.directory</td>
-                <td>"$project.buildDir/sonar"</td>
+                <td>“$project.buildDir/sonar”</td>
             </tr>
             <tr>
                 <td>sonar.dynamicAnalysis</td>
-                <td>"reuseReports"</td>
+                <td>“reuseReports”</td>
             </tr>
         </table>
         <table>
@@ -185,8 +191,32 @@
                 <td>test.testResultsDir (if the directory exists)</td>
             </tr>
         </table>
+        <table>
+            <title>Additional defaults when <literal>jacoco</literal> plugin is applied</title>
+            <thead>
+                <tr>
+                    <td>Property</td>
+                    <td>Gradle default</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>sonar.jacoco.reportPath</td>
+                <td>jacoco.destinationFile</td>
+            </tr>
+        </table>
+    </section>
+    <section id="sec:specify_sonar_runner_version">
+        <title>Specifying the Sonar Runner version</title>
+        <para>
+            By default, version 2.3 of the Sonar Runner is used.
+            To specify an alternative version, set the <apilink class="org.gradle.sonar.runner.SonarRunnerRootExtension" method="getToolVersion()"/> property
+            of the <literal>sonarRunner</literal> extension of the project the plugin was applied to to the desired version.
+            This will result in the Sonar Runner dependency <literal>org.codehaus.sonar.runner:sonar-runner-dist:«toolVersion»</literal> being used as the Sonar Runner.
+        </para>
+        <sample id="quickstart" dir="sonarRunner/quickstart" title="Configuring Sonar runner version">
+            <sourcefile file="build.gradle" snippet="version-settings"/>
+        </sample>
     </section>
-
     <section>
         <title>Analyzing Multi-Project Builds</title>
         <para>The Sonar Runner is capable of analyzing whole project hierarchies at once. This yields a hierarchical view in the
@@ -216,7 +246,7 @@
             <sourcefile file="build.gradle" snippet="individual-configuration-settings"/>
         </sample>
         <para>
-            The skip Sonar analysis for a particular subproject, set <literal>sonarRunner.skipProject</literal>.
+            To skip Sonar analysis for a particular subproject, set <literal>sonarRunner.skipProject</literal> to <literal>true</literal>.
         </para>
         <sample id="multiProject" dir="sonarRunner/multiProject" title="Skipping analysis of a project">
             <sourcefile file="build.gradle" snippet="skip-project"/>
@@ -237,9 +267,9 @@
     <section>
         <title>Analyzing languages other than Java</title>
         <para>
-            To analyze code written in a language other than Java, install the corresponding
-            <ulink url="http://www.sonarsource.com/products/plugins/languages/">Sonar plugin</ulink>, and set
-            <literal>sonar.project.language</literal> accordingly:
+            To analyze code written in a language other than Java, you'll need to set <literal>sonar.project.language</literal> accordingly.
+            However, note that your Sonar server has to have the <ulink url="http://www.sonarsource.com/products/plugins/languages/">Sonar plugin</ulink>
+            that handles that programming language.
         </para>
         <sample id="advanced" dir="sonarRunner/advanced" title="Analyzing languages other than Java">
             <sourcefile file="build.gradle" snippet="languages" />
@@ -254,13 +284,13 @@
         <title>More on configuring Sonar properties</title>
         <para>
             Let's take a closer look at the <literal>sonarRunner.sonarProperties {}</literal> block. As we have already seen in the examples,
-            the <literal>property()</literal> method allows to set new properties or override existing ones. Furthermore, all properties that have
+            the <literal>property()</literal> method allows you to set new properties or override existing ones. Furthermore, all properties that have
             been configured up to this point, including all properties preconfigured by Gradle, are available via the <literal>properties</literal>
             accessor.
         <para>
         </para>
             Entries in the <literal>properties</literal> map can be read and written with the usual Groovy syntax. To facilitate their manipulation,
-            values still have their "idiomatic" type (<classname>File</classname>, <classname>List</classname>, etc.). After the sonarProperties block
+            values still have their “idiomatic” type (<classname>File</classname>, <classname>List</classname>, etc.). After the sonarProperties block
             has been evaluated, values are converted to Strings as follows: Collection values are (recursively) converted to comma-separated Strings,
             and all other values are converted by calling their <literal>toString()</literal> method.
         </para>
@@ -287,16 +317,24 @@
         </note>
         <para>A Sonar property value set via a system property overrides any value set in a build script (for the same property). When
             analyzing a project hierarchy, values set via system properties apply to the root project of the analyzed hierarchy.
+            Each system property starting with "<literal>"sonar."</literal> will taken into account for the sonar runner setup.
         </para>
     </section>
 
     <section>
-        <title>Executing Sonar Runner in a separate process</title>
+        <title>Controlling the Sonar Runner process</title>
+        <para>
+            The Sonar Runner is executed in a forked process.
+            This allows fine grained control over memory settings, system properties etc. just for the Sonar Runner process.
+            The <literal>forkOptions</literal> property of the <literal>sonarRunner</literal> extension of the project that applies the <literal>sonar-runner</literal> plugin
+            (Usually the <literal>rootProject</literal> but not necessarily) allows the process configuration to be specified.
+            This property is not available in the <apilink class="org.gradle.sonar.runner.SonarRunnerExtension"/> extension applied to the subprojects.
+        </para>
+        <sample id="advanced" dir="sonarRunner/advanced" title="setting custom Sonar Runner fork options">
+            <sourcefile file="build.gradle" snippet="forkoptions"/>
+        </sample>
         <para>
-            Depending on project size, the Sonar Runner may require a lot of memory. For this and other (mainly isolation) reasons,
-            it is desirable to execute the Sonar Runner in a separate process. This feature will be provided once Sonar Runner 2.1
-            has been released and adopted by the Sonar Runner plugin. Until then, the Sonar Runner is executed in the
-            main Gradle process. See <xref linkend="sec:gradle_configuration_properties"/> for how to manage memory settings for that process.
+            For a complete reference about the available options, see <apilink class="org.gradle.process.JavaForkOptions" />.
         </para>
     </section>
 
@@ -316,7 +354,7 @@
             <tr>
                 <td><literal>sonarRunner</literal></td>
                 <td>-</td>
-                <td><apilink class="org.gradle.api.sonar.runner.SonarRunner"/></td>
+                <td><apilink class="org.gradle.sonar.runner.tasks.SonarRunner"/></td>
                 <td>Analyzes a project hierarchy and stores the results in the Sonar database.</td>
             </tr>
         </table>
diff --git a/subprojects/docs/src/docs/userguide/standardPlugins.xml b/subprojects/docs/src/docs/userguide/standardPlugins.xml
index ee34559..c4562f4 100644
--- a/subprojects/docs/src/docs/userguide/standardPlugins.xml
+++ b/subprojects/docs/src/docs/userguide/standardPlugins.xml
@@ -658,6 +658,18 @@
                     </para>
                 </td>
             </tr>
+            <tr>
+                <td>
+                    <link linkend="javaGradle_plugin">
+                        <literal>java-gradle-plugin</literal>
+                    </link>
+                </td>
+                <td>java</td>
+                <td></td>
+                <td>
+                    <para>Assists with development of Gradle plugins by providing standard plugin build configuration and validation.</para>
+                </td>
+            </tr>
         </table>
     </section>
     <section>
@@ -734,8 +746,7 @@
     </section>
     <section>
         <title>Third party plugins</title>
-        <para>You can find a list of external plugins on the
-            <ulink url="http://wiki.gradle.org/display/GRADLE/Plugins">wiki</ulink>.
+        <para>You can find a list of external plugins at the <ulink url="http://plugins.gradle.org/">Gradle Plugins site</ulink>.
         </para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/tasks.xml b/subprojects/docs/src/docs/userguide/tasks.xml
index 015d13e..d869458 100644
--- a/subprojects/docs/src/docs/userguide/tasks.xml
+++ b/subprojects/docs/src/docs/userguide/tasks.xml
@@ -15,12 +15,12 @@
   -->
 <chapter id='more_about_tasks'>
     <title>More about Tasks</title>
-    <para>In the introductory tutorial (<xref linkend='tutorial_using_tasks'/>) you have learned how to
-        create simple tasks. You have also learned how to add additional behavior to these tasks later on. And you have
-        learned how to create dependencies between tasks. This was all about simple tasks. But Gradle takes the concept
-        of tasks further. Gradle supports <firstterm>enhanced tasks</firstterm>, that is, tasks which have their own
-        properties and methods. This is really different to what you are used to with Ant targets. Such enhanced tasks are
-        either provided by you or are provided by Gradle.
+    <para>In the introductory tutorial (<xref linkend='tutorial_using_tasks'/>) you learned how to
+        create simple tasks. You also learned how to add additional behavior to these tasks later on, and you 
+        learned how to create dependencies between tasks. This was all about simple tasks, but Gradle takes the concept
+        of tasks further. Gradle supports <firstterm>enhanced tasks</firstterm>, which are tasks that have their own
+        properties and methods. This is really different from what you are used to with Ant targets. Such enhanced tasks are
+        either provided by you or built into Gradle.
     </para>
     <section>
         <title>Defining tasks</title>
@@ -77,10 +77,15 @@
             The task can be configured using its API (see <apilink class="org.gradle.api.tasks.Copy"/>).
 			The following examples show several different ways to achieve the same configuration.
         </para>
+        <para>Just to be clear, realize that the name of this task is “<literal>myCopy</literal>”, but it is of
+        <emphasis>type</emphasis> “<literal>Copy</literal>”.  You can have multiple tasks of the same
+        <emphasis>type</emphasis>, but with different names. You'll find this gives you a lot of power to implement
+        cross-cutting concerns across all tasks of a particular type.
+        </para>
         <sample id="configureUsingVar" dir="userguide/tasks/configureUsingVar" title="Configuring a task - various ways">
             <sourcefile file="build.gradle"/>
         </sample>
-        <para>This is similar to the way we would normally configure objects in Java. You have to repeat the context
+        <para>This is similar to the way we would configure objects in Java. You have to repeat the context
             (<literal>myCopy</literal>) in the configuration statement every time. This is a redundancy and not very
             nice to read.
         </para>
@@ -99,6 +104,14 @@
         <sample id="defineAndConfigure" dir="userguide/tasks/defineAndConfigure" title="Defining a task with closure">
             <sourcefile file="build.gradle" snippet="no-description"/>
         </sample>
+        <tip>
+            <title>Don't forget about the build phases</title>
+            <para>A task has both configuration and actions.
+                When using the <literal><<</literal>, you are simply using a shortcut to define an action.
+                Code defined in the configuration section of your task will get executed during the configuration phase of the build regardless of what task was targeted.
+                See <xref linkend="build_lifecycle"/> for more details about the build lifecycle.
+            </para>
+        </tip>
     </section>
     <section id='sec:adding_dependencies_to_tasks'>
         <title>Adding dependencies to a task</title>
@@ -106,7 +119,7 @@
             <xref linkend='sec:task_dependencies'/>
             you were introduced to defining dependencies using task names. Task names can refer to tasks in the same
             project as the task, or to tasks in other projects. To refer to a task in another project, you prefix the
-            name of the task with the path of the project it belongs to. Below is an example which adds a dependency
+            name of the task with the path of the project it belongs to. The following is an example which adds a dependency
             from
             <literal>projectA:taskX</literal>
             to
@@ -157,10 +170,16 @@
             <listitem>A task that aggregates the results of all tasks of a particular type: eg. test report task combines the outputs of all executed test tasks.</listitem>
         </itemizedlist>
         <para>
-            There are two ordering rules available: "<emphasis>must run after</emphasis>" and "<emphasis>should run after</emphasis>".
+            There are two ordering rules available: “<emphasis>must run after</emphasis>” and “<emphasis>should run after</emphasis>”.
         </para>
-        <para>By using 'must run after" ordering rule you can specify that <literal>taskB</literal> must always run after <literal>taskA</literal>,
-            whenever both <literal>taskA</literal> and <literal>taskB</literal> are scheduled for execution. This is expressed as <literal>taskB.mustRunAfter(taskA)</literal>. The 'should run after' ordering rule is similar but less strict as it will be ignored in two situations. Firstly if using that rule introduces an ordering cycle. Secondly when using parallel execution and all dependencies of a task have been satisfied apart from should run after then this task will be run regardles [...]
+        <para>When you use the “must run after” ordering rule you specify that <literal>taskB</literal> must always
+        run after <literal>taskA</literal>, whenever both <literal>taskA</literal> and <literal>taskB</literal> will
+        be run. This is expressed as <literal>taskB.mustRunAfter(taskA)</literal>. The “should run
+        after” ordering rule is similar but less strict as it will be ignored in two situations. Firstly if using
+        that rule introduces an ordering cycle. Secondly when using parallel execution and all dependencies of a
+        task have been satisfied apart from the “should run after” task, then this task will be run regardless of
+        whether its “should run after” dependencies have been run or not. You should use “should run after”
+        where the ordering is helpful but not strictly required.
         </para>
         <para>
             With these rules present it is still possible to execute <literal>taskA</literal> without <literal>taskB</literal> and vice-versa.
@@ -177,17 +196,17 @@
         <sample id="mustRunAfterSingleTask" dir="userguide/tasks/mustRunAfter" title="Task ordering does not imply task execution">
             <output args="-q taskY"/>
         </sample>
-        <para>To specify a "must run after" or "should run after" ordering between 2 tasks, you use the <apilink class="org.gradle.api.Task" method="mustRunAfter"/> and <apilink class="org.gradle.api.Task" method="shouldRunAfter"/> methods.
-            These method accept a task instance, a task name or any other input accepted by <apilink class="org.gradle.api.Task" method="dependsOn"/>.
+        <para>To specify a “must run after” or “should run after” ordering between 2 tasks, you use the <apilink class="org.gradle.api.Task" method="mustRunAfter"/> and <apilink class="org.gradle.api.Task" method="shouldRunAfter"/> methods.
+            These methods accept a task instance, a task name or any other input accepted by <apilink class="org.gradle.api.Task" method="dependsOn"/>.
         </para>
         <para>
-            Note that "<literal>B.mustRunAfter(A)</literal>" or "<literal>B.shouldRunAfter(A)</literal>" does not imply any execution dependency between the tasks:
+            Note that “<literal>B.mustRunAfter(A)</literal>” or “<literal>B.shouldRunAfter(A)</literal>” does not imply any execution dependency between the tasks:
         </para>
         <itemizedlist>
             <listitem>It is possible to execute tasks <literal>A</literal> and <literal>B</literal> independently. The ordering rule only has an effect when both tasks are scheduled for execution.</listitem>
             <listitem>When run with <literal>--continue</literal>, it is possible for <literal>B</literal> to execute in the event that <literal>A</literal> fails.</listitem>
         </itemizedlist>
-        <para>As mentioned before 'should run after' ordering rule will be ignored if it introduces an ordering cycle:</para>
+        <para>As mentioned before, the “should run after” ordering rule will be ignored if it introduces an ordering cycle:</para>
         <sample id="shouldRunAfterWithCycle" dir="userguide/tasks/shouldRunAfterWithCycle" title="A 'should run after' task ordering is ignored if it introduces an ordering cycle">
             <sourcefile file="build.gradle"/>
             <output args="-q taskX"/>
@@ -195,7 +214,7 @@
     </section>
     <section>
         <title>Adding a description to a task</title>
-        <para>You can add a description to your task. This description is for example displayed when executing
+        <para>You can add a description to your task. This description is displayed when executing
             <userinput>gradle tasks</userinput>.
         </para>
         <sample id="describeTask" dir="userguide/tasks/defineAndConfigure" title="Adding a description to a task">
@@ -204,16 +223,16 @@
     </section>
     <section>
         <title>Replacing tasks</title>
-        <para>Sometimes you want to replace a task. For example if you want to exchange a task added by the Java plugin
+        <para>Sometimes you want to replace a task. For example, if you want to exchange a task added by the Java plugin
             with a custom task of a different type. You can achieve this with:
         </para>
         <sample id="replaceTask" dir="userguide/tutorial/replaceTask" title="Overwriting a task">
             <sourcefile file="build.gradle"/>
             <output args="-q copy"/>
         </sample>
-        <para>Here we replace a task of type <literal>Copy</literal> with a simple task. When creating the simple
-            task, you have to set the <literal>overwrite</literal> property to true. Otherwise Gradle throws an
-            exception, saying that a task with such a name already exists.
+        <para>This will replace a task of type <literal>Copy</literal> with the task you've defined, because it
+        uses the same name. When you define the new task, you have to set the <literal>overwrite</literal> property
+        to true. Otherwise Gradle throws an exception, saying that a task with that name already exists.
         </para>
     </section>
     <section>
@@ -236,7 +255,7 @@
 
         <section>
         <title>Using StopExecutionException</title>
-        <para>If the rules for skipping a task can't be expressed with predicate, you can use the
+        <para>If the logic for skipping a task can't be expressed with a predicate, you can use the
             <apilink class="org.gradle.api.tasks.StopExecutionException"/>. If this exception is thrown by an action,
             the further execution of this action as well as the execution of
             any following action of this task is skipped. The build continues with executing the next task.
@@ -259,7 +278,7 @@
 
         <section>
         <title>Enabling and disabling tasks</title>
-        <para>Every task has also an <literal>enabled</literal>
+        <para>Every task has an <literal>enabled</literal>
             flag which defaults to <literal>true</literal>. Setting it to <literal>false</literal> prevents the
             execution of any of the task's actions.
         </para>
@@ -324,14 +343,14 @@
                 Before a task is executed for the first time, Gradle takes a snapshot of the inputs. This snapshot contains
                 the set of input files and a hash of the contents of each file. Gradle then executes the task. If the
                 task completes successfully, Gradle takes a snapshot of the outputs. This snapshot contains the set of
-                output files and a hash of the contents of each file. Gradle persists both snapshots for next time the task
+                output files and a hash of the contents of each file. Gradle persists both snapshots for the next time the task
                 is executed.
             </para>
             <para>
                 Each time after that, before the task is executed, Gradle takes a new snapshot of the inputs and outputs.
                 If the new snapshots are the same as the previous snapshots, Gradle assumes that the outputs are up to
                 date and skips the task. If they are not the same, Gradle executes the task. Gradle persists both snapshots
-                for next time the task is executed.
+                for the next time the task is executed.
             </para>
             <para>
                 Note that if a task has an output directory specified, any files added to that directory since the last time it was executed
@@ -350,16 +369,18 @@
             <sourcefile file="build.gradle" snippet="task-rule"/>
             <output args="-q pingServer1"/>
         </sample>
-        <para>The String parameter is used as a description for the rule. This description is shown when running
-            for example <userinput>gradle tasks</userinput>.
+        <para>The String parameter is used as a description for the rule, which is shown with <userinput>gradle tasks</userinput>.
         </para>
-        <para>Rules not just work when calling tasks from the command line. You can also create dependsOn relations
+        <para>Rules are not only used when calling tasks from the command line. You can also create dependsOn relations
             on rule based tasks:
         </para>
         <sample id="taskRuleDependsOn" dir="userguide/tasks/addRules" title="Dependency on rule based tasks">
             <sourcefile file="build.gradle"/>
             <output args="-q groupPing"/>
         </sample>
+        <para>If you run “<literal>gradle -q tasks</literal>” you won't find a task named
+        “<literal>pingServer1</literal>” or “<literal>pingServer2</literal>”, but this script is executing logic
+        based on the request to run those tasks.</para>
     </section>
 
     <section>
@@ -374,29 +395,29 @@
             <sourcefile file="build.gradle"/>
             <output args="-q taskX"/>
         </sample>
-        <para>Finalizer task will be executed even if the finalized task fails.</para>
+        <para>Finalizer tasks will be executed even if the finalized task fails.</para>
         <sample id="taskFinalizersWithFailure" dir="userguide/tasks/finalizersWithFailure" title="Task finalizer for a failing task">
             <sourcefile file="build.gradle"/>
             <output args="-q taskX"/>
         </sample>
-        <para>On the other hand, finalizer tasks are not executed if the finalized task didn't do any work, for example due to failed
-            task dependency or if it's considered up to date.</para>
-        <para>Finalizer tasks are useful in situations where build creates a resource that has to be cleaned up regardless
-            of the build failing or succeeding. An example of such resource is a web container started before an integration test task
+        <para>On the other hand, finalizer tasks are not executed if the finalized task didn't do any work, for example if it
+        is considered up to date or if a dependent task fails.</para>
+        <para>Finalizer tasks are useful in situations where the build creates a resource that has to be cleaned up regardless
+            of the build failing or succeeding. An example of such a resource is a web container that is started before an integration test task
             and which should be always shut down, even if some of the tests fail.</para>
         <para>To specify a finalizer task you use the <apilink class="org.gradle.api.Task" method="finalizedBy"/> method.
-            This method accepts a task instance, a task name or any other input accepted by <apilink class="org.gradle.api.Task" method="dependsOn"/>.
+            This method accepts a task instance, a task name, or any other input accepted by <apilink class="org.gradle.api.Task" method="dependsOn"/>.
         </para>
     </section>
 
     <section id='sec:the_idea_behind_gradle_tasks'>
         <title>Summary</title>
-        <para>If you are coming from Ant, such an enhanced Gradle task as <emphasis>Copy</emphasis> looks like a mixture
-            between an Ant target and an Ant task. And this is actually the case. The separation that Ant does between
-            tasks and targets is not done by Gradle. The simple Gradle tasks are like Ant's targets and the enhanced
-            Gradle tasks also include the Ant task aspects. All of Gradle's tasks share a common API and you can create
-            dependencies between them. Such a task might be nicer to configure than an Ant task.
-            It makes full use of the type system, is more expressive and easier to maintain.
+        <para>If you are coming from Ant, an enhanced Gradle task like <emphasis>Copy</emphasis> seems like a cross
+            between an Ant target and an Ant task. Although Ant's tasks and targets are really different entities,
+            Gradle combines these notions into a single entity. Simple Gradle tasks are like Ant's targets, but enhanced
+            Gradle tasks also include aspects of Ant tasks. All of Gradle's tasks share a common API and you can create
+            dependencies between them. These tasks are much easier to configure than an Ant task.
+            They make full use of the type system, and are more expressive and easier to maintain.
         </para>
     </section>
 </chapter>
diff --git a/subprojects/docs/src/docs/userguide/thisAndThat.xml b/subprojects/docs/src/docs/userguide/thisAndThat.xml
deleted file mode 100644
index cff91bc..0000000
--- a/subprojects/docs/src/docs/userguide/thisAndThat.xml
+++ /dev/null
@@ -1,132 +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.
-  -->
-<chapter id='tutorial_this_and_that'>
-    <title>Tutorial - 'This and That'</title>
-    <section id='sec:directory_creation'>
-        <title>Directory creation</title>
-        <para>There is a common situation, that multiple tasks depend on the existence of a directory. Of course you can
-            deal with this by adding a
-            <literal>mkdir</literal>
-            to the beginning of those tasks. But this is kind of bloated. There is a better solution (works only if the
-            tasks that need the directory have a
-            <emphasis>dependsOn</emphasis>
-            relationship):
-        </para>
-        <sample id="makeDirectory" dir="userguide/tutorial/makeDirectory" title="Directory creation with mkdir">
-            <sourcefile file="build.gradle"/>
-            <output args="-q compile"/>
-        </sample>
-    </section>
-    <section id='sec:gradle_properties_and_system_properties'>
-        <title>Gradle properties and system properties</title>
-        <para>Gradle offers a variety of ways to add properties to your build. With the <option>-D</option> command line
-            option you can pass a system property to the JVM which runs Gradle. The <option>-D</option> option of the
-            <command>gradle</command> command has the same effect as the <option>-D</option> option of the
-            <command>java</command> command.
-        </para>
-        <para>You can also directly add properties to your project objects using properties files. You can place a
-            <filename>gradle.properties</filename> file in the Gradle user home directory (defaults to
-            <filename><replaceable>USER_HOME</replaceable>/.gradle</filename>) or in your project directory. For
-            multi-project builds you can place <filename>gradle.properties</filename> files in any subproject directory.
-            The properties of the <filename>gradle.properties</filename> can be accessed via the project object. The
-            properties file in the user's home directory has precedence over property files in the project directories.
-        </para>
-        <para>You can also add properties directly to your project object via the <option>-P</option>
-            command line option. For more exotic use cases you can even pass properties <emphasis>directly</emphasis>
-            to the project object via system and environment properties. For example if you run a build on a continuous
-            integration server where you have no admin rights for the <emphasis>machine</emphasis>. Your build script
-            needs properties which values should not be seen by others. Therefore you can't use the <option>-P</option>
-            option. In this case you can add an environment property in the project administration section (invisible to
-            normal users).
-            <footnote>
-                <para>
-                    <emphasis>Teamcity</emphasis> or <emphasis>Bamboo</emphasis> are for example CI servers which
-                    offer this functionality.
-                </para>
-            </footnote>
-            If the environment property follows the pattern
-            <literal>ORG_GRADLE_PROJECT_<replaceable>propertyName</replaceable>=somevalue</literal>,
-            <literal>propertyName</literal> is added to your project object. We also support the same mechanism for
-            system properties. The only difference is the pattern, which is
-            <literal>org.gradle.project.<replaceable>propertyName</replaceable></literal>.
-        </para>
-        <para>With the <filename>gradle.properties</filename> files you can also set system properties. If a property
-            in such a file has the prefix <literal>systemProp.</literal> the property and its value are added to the 
-            system properties, without the prefix.
-            In a multi project build, <literal>systemProp.</literal> properties set in any project except the root will be ignored.
-            That is, only <literal>systemProp.</literal> in the root project's <filename>gradle.properties</filename> file will be set as system properties.
-        </para>
-        <sample id="properties" dir="userguide/tutorial/properties" title="Setting properties with a gradle.properties file">
-            <sourcefile file="gradle.properties"/>
-            <sourcefile file="build.gradle"/>
-            <output args="-q -PcommandLineProjectProp=commandLineProjectPropValue -Dorg.gradle.project.systemProjectProp=systemPropertyValue printProps"/>
-        </sample>
-        <section id='sub:checking_for_project_properties'>
-            <title>Checking for project properties</title>
-            <para>You can access a project property in your build script simply by using its name as you would use a
-                variable. In case this property does not exists, an exception is thrown and the build fails. If your
-                build script relies on optional properties the user might set for example in a gradle.properties file,
-                you need to check for existence before you can access them. You can do this by using the method
-                <literal>hasProperty('propertyName')</literal>
-                which returns
-                <literal>true</literal>
-                or <literal>false</literal>.
-            </para>
-        </section>
-    </section>
-
-    <section id="sec:configuring_using_external_script">
-        <title>Configuring the project using an external build script</title>
-        <para>You can configure the current project using an external build script. All of the Gradle build language
-            is available in the external script. You can even apply other scripts from the external script.
-        </para>
-        <sample id="configureProjectUsingScript" dir="userguide/tutorial/configureProjectUsingScript" title="Configuring the project using an external build script">
-            <sourcefile file="build.gradle"/>
-            <sourcefile file="other.gradle"/>
-            <output args="-q hello"/>
-        </sample>
-    </section>
-    <section id='sec:configuring_arbitrary_objects'>
-        <title>Configuring arbitrary objects</title>
-        <para>You can configure arbitrary objects in the following very readable way.
-        </para>
-        <sample id="configureObject" dir="userguide/tutorial/configureObject" title="Configuring arbitrary objects">
-            <sourcefile file="build.gradle"/>
-            <output args="-q configure"/>
-        </sample>
-    </section>
-    <section>
-        <title>Configuring arbitrary objects using an external script</title>
-        <para>You can also configure arbitrary objects using an external script.
-        </para>
-        <sample id="configureObjectUsingScript" dir="userguide/tutorial/configureObjectUsingScript" title="Configuring arbitrary objects using a script">
-            <sourcefile file="build.gradle"/>
-            <sourcefile file="other.gradle"/>
-            <output args="-q configure"/>
-        </sample>
-    </section>
-    <section id='sec:caching'>
-        <title>Caching</title>
-        <para>To improve responsiveness Gradle caches all compiled scripts by default. This includes all build scripts,
-            initialization scripts, and other scripts. The first time you run a build for a project, Gradle creates a
-            <filename>.gradle</filename> directory in which it puts the compiled script. The next time you run this
-            build, Gradle uses the compiled script, if the script has not changed since it was compiled.  Otherwise the
-            script gets compiled and the new version is stored in the cache. If you run Gradle with the
-            <option>--recompile-scripts</option> option, the cached script is discarded and the script is compiled and stored
-            in the cache. This way you can force Gradle to rebuild the cache.
-        </para>
-    </section>
-</chapter>
diff --git a/subprojects/docs/src/docs/userguide/troubleshooting.xml b/subprojects/docs/src/docs/userguide/troubleshooting.xml
index 9a2be0a..524cd58 100644
--- a/subprojects/docs/src/docs/userguide/troubleshooting.xml
+++ b/subprojects/docs/src/docs/userguide/troubleshooting.xml
@@ -37,14 +37,14 @@
         </para>
         <para>
             If you are using the Gradle Daemon, try temporarily disabling the daemon (you can pass the command line switch <literal>--no-daemon</literal>).
-            More information about troubleshooting daemon is located in <xref linkend="gradle_daemon"/>.
+            More information about troubleshooting the daemon process is located in <xref linkend="gradle_daemon"/>.
         </para>
     </section>
     <section>
         <title>Getting help</title>
         <para>
             The place to go for help with Gradle is <ulink url="http://forums.gradle.org">http://forums.gradle.org</ulink>.
-            The Gradle Forums is the place where you can report problems and ask questions to the Gradle 
+            The Gradle Forums is the place where you can report problems and ask questions of the Gradle 
             developers and other community members.
         </para>
         <para>
diff --git a/subprojects/docs/src/docs/userguide/userguide.xml b/subprojects/docs/src/docs/userguide/userguide.xml
index db76876..32fdb12 100755
--- a/subprojects/docs/src/docs/userguide/userguide.xml
+++ b/subprojects/docs/src/docs/userguide/userguide.xml
@@ -17,7 +17,7 @@
     <bookinfo>
         <title>Gradle User Guide</title>
         <copyright>
-            <year>2007-2012</year>
+            <year>2007-2015</year>
             <holder>Hans Dockter, Adam Murdoch</holder>
         </copyright>
         <legalnotice>
@@ -40,7 +40,6 @@
     <xi:include href='commandLineTutorial.xml'/>
     <xi:include href='guiTutorial.xml'/>
     <xi:include href='writingBuildScripts.xml'/>
-    <xi:include href='thisAndThat.xml'/>
     <xi:include href='tasks.xml'/>
     <xi:include href='workingWithFiles.xml'/>
     <xi:include href='ant.xml'/>
@@ -76,6 +75,7 @@
     <xi:include href='buildInitPlugin.xml'/>
     <xi:include href='wrapperPlugin.xml'/>
     <xi:include href='buildDashboardPlugin.xml'/>
+    <xi:include href='javaGradlePlugin.xml' />
     <xi:include href='depMngmt.xml'/>
     <xi:include href='artifactMngmt.xml'/>
     <xi:include href='mavenPlugin.xml'/>
@@ -92,6 +92,7 @@
     <xi:include href='comparingBuilds.xml'/>
     <xi:include href='publishingIvy.xml'/>
     <xi:include href='publishingMaven.xml'/>
+    <xi:include href='newModel.xml'/>
     <!-- this is generated -->
     <xi:include href='../../../build/src/samplesList.xml'/>
     <xi:include href='potentialTraps.xml'/>
diff --git a/subprojects/docs/src/docs/userguide/warPlugin.xml b/subprojects/docs/src/docs/userguide/warPlugin.xml
index d8a9029..4b5cb4c 100644
--- a/subprojects/docs/src/docs/userguide/warPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/warPlugin.xml
@@ -21,7 +21,7 @@
 
     <section>
         <title>Usage</title>
-        <para>To use the War plugin, include in your build script:</para>
+        <para>To use the War plugin, include the following in your build script:</para>
         <sample id="useWarPlugin" dir="webApplication/quickstart" title="Using the War plugin">
             <sourcefile file="build.gradle" snippet="use-war-plugin"/>
         </sample>
@@ -92,14 +92,14 @@
 
     <section>
         <title>Dependency management</title>
-        <para>The War plugin adds two dependency configurations: <literal>providedCompile</literal> and
-            <literal>providedRuntime</literal>. Those configurations have the same scope as the respective
+        <para>The War plugin adds two dependency configurations named <literal>providedCompile</literal> and
+            <literal>providedRuntime</literal>. Those two configurations have the same scope as the respective
             <literal>compile</literal> and <literal>runtime</literal> configurations, except that they are not added to
             the WAR archive. It is important to note that those <literal>provided</literal> configurations work
             transitively. Let's say you add <literal>commons-httpclient:commons-httpclient:3.0</literal> to any of the
             provided configurations. This dependency has a dependency on <literal>commons-codec</literal>.
-            This means neither <literal>httpclient</literal> nor <literal>commons-codec</literal> is added to your
-            WAR, even if <literal>commons-codec</literal> were an explicit dependency of your <literal>compile</literal>
+            Because this is a “provided” configuration, this means that neither of these dependencies will be added to your
+            WAR, even if the <literal>commons-codec</literal> library is an explicit dependency of your <literal>compile</literal>
             configuration. If you don't want this transitive behavior, simply declare your <literal>provided</literal>
             dependencies like <literal>commons-httpclient:commons-httpclient:3.0 at jar</literal>.
         </para>
@@ -157,7 +157,7 @@
 
         <para>The default behavior of the War task is to copy the content of <literal>src/main/webapp</literal>
             to the root of the archive. Your <literal>webapp</literal> directory may of course contain a
-            <literal>WEB-INF</literal> sub-directory, which again may contain a <literal>web.xml</literal> file.
+            <literal>WEB-INF</literal> sub-directory, which may contain a <literal>web.xml</literal> file.
             Your compiled classes are compiled to <literal>WEB-INF/classes</literal>. All the dependencies of the
             <literal>runtime</literal>
             <footnote>
@@ -169,7 +169,7 @@
                 </para>
             </footnote>
             configuration are copied to <literal>WEB-INF/lib</literal>.</para>
-        <para>Have also a look at <apilink class="org.gradle.api.tasks.bundling.War"/>.</para>
+        <para>The <apilink class="org.gradle.api.tasks.bundling.War"/> class in the API documentation has additional useful information.</para>
     </section>
     <section id='sec:customizing'>
         <title>Customizing</title>
diff --git a/subprojects/docs/src/docs/userguide/webTutorial.xml b/subprojects/docs/src/docs/userguide/webTutorial.xml
index e4c016c..2f98a48 100644
--- a/subprojects/docs/src/docs/userguide/webTutorial.xml
+++ b/subprojects/docs/src/docs/userguide/webTutorial.xml
@@ -20,7 +20,7 @@
         <para>This chapter is a work in progress.</para>
     </note>
 
-    <para>This chapter introduces some of the Gradle's support for web applications. Gradle provides two plugins for web
+    <para>This chapter introduces the Gradle support for web applications. Gradle provides two plugins for web
         application development: the War plugin and the Jetty plugin. The War plugin extends the Java plugin to build a
         WAR file for your project. The Jetty plugin extends the War plugin to allow you to deploy your web application
         to an embedded Jetty web container.
@@ -34,11 +34,12 @@
         </sample>
         <para>This also applies the Java plugin to your project. Running <userinput>gradle build</userinput> will compile,
             test and WAR your project. Gradle will look for the source files to include in the WAR file in
-            <filename>src/main/webapp</filename>. Your compiled classes, and their runtime dependencies are also
-            included in the WAR file.
+            <filename>src/main/webapp</filename>. Your compiled classes and their runtime dependencies are also
+            included in the WAR file, in the <literal>WEB-INF/classes</literal> and <literal>WEB-INF/lib</literal>
+            directories, respectively.
         </para>
         <tip><title>Groovy web applications</title><para>You can combine multiple plugins in a single project, so you
-            can use the War and Groovy plugins together to build a Groovy based web application. The appropriate groovy
+            can use the War and Groovy plugins together to build a Groovy based web application. The appropriate Groovy
             libraries will be added to the WAR file for you.</para></tip>
     </section>
 
diff --git a/subprojects/docs/src/docs/userguide/workingWithFiles.xml b/subprojects/docs/src/docs/userguide/workingWithFiles.xml
index 1866072..f498b5e 100644
--- a/subprojects/docs/src/docs/userguide/workingWithFiles.xml
+++ b/subprojects/docs/src/docs/userguide/workingWithFiles.xml
@@ -29,8 +29,7 @@
         </sample>
         <para>You can pass any object to the <literal>file()</literal> method, and it will attempt to convert the value
             to an absolute <classname>File</classname> object. Usually, you would pass it a
-            <classname>String</classname> or <classname>File</classname> instance. The supplied object's
-            <literal>toString()</literal> value is used as the file path. If this path is an absolute path, it is used
+            <classname>String</classname> or <classname>File</classname> instance. If this path is an absolute path, it is used
             to construct a <classname>File</classname> instance. Otherwise, a <classname>File</classname> instance is
             constructed by prepending the project directory path to the supplied path. The <literal>file()</literal>
             method also understands URLs, such as <literal>file:/some/path.xml</literal>.
@@ -236,7 +235,7 @@
             <sourcefile file="build.gradle" snippet="copy-method-with-dependency"/>
         </sample>
         <para>
-            It is preferable to use the <literal>Copy</literal> task wherever possible, as it support incremental building and task dependency inference
+            It is preferable to use the <literal>Copy</literal> task wherever possible, as it supports incremental building and task dependency inference
             without any extra effort on your part. The <literal>copy()</literal> method can be used to copy files as <emphasis>part</emphasis> of a task's
             implementation. That is, the copy method is intended to be used by custom tasks (see <xref linkend="custom_tasks" />) that need to copy files
             as part of their function. In such a scenario, the custom task should sufficiently declare the inputs/outputs relevant to the copy action.
@@ -252,6 +251,8 @@
             <sample id="filterOnCopy" dir="userguide/files/copy" title="Filtering files as they are copied">
                 <sourcefile file="build.gradle" snippet="filter-files"/>
             </sample>
+            <para>A “token” in a source file that both the “expand” and “filter” operations look for, is formatted
+            like “@tokenName@” for a token named “tokenName”.</para>
         </section>
         <section>
             <title>Using the <classname>CopySpec</classname> class</title>
@@ -281,7 +282,7 @@
     <section id="sec:archives">
         <title>Creating archives</title>
         <para>
-            A project can have as many as JAR archives as you want. You can also add WAR, ZIP and TAR archives to your project.
+            A project can have as many JAR archives as you want. You can also add WAR, ZIP and TAR archives to your project.
             Archives are created using the various archive tasks:
             <apilink class="org.gradle.api.tasks.bundling.Zip"/>,
             <apilink class="org.gradle.api.tasks.bundling.Tar"/>,
@@ -312,7 +313,8 @@
         <section>
             <title>Archive naming</title>
 
-            <para>The default name for a generated archive is <filename><replaceable>projectName</replaceable>-<replaceable>version</replaceable>.<replaceable>type</replaceable></filename>
+            <para>The format of <filename><replaceable>projectName</replaceable>-<replaceable>version</replaceable>.<replaceable>type</replaceable></filename>
+            is used for generated archive file names.
                 For example:
             </para>
             <sample id="archiveNaming" dir="userguide/files/archiveNaming" title="Creation of ZIP archive">
@@ -367,8 +369,8 @@
                 <tr>
                     <td><literal>destinationDir</literal></td>
                     <td><classname>File</classname></td>
-                    <td>Depends on the archive type. JARs and WARs are generated into <filename><replaceable>project.buildDir</replaceable>/libraries</filename>.
-                        ZIPs and TARs are generated into <filename><replaceable>project.buildDir</replaceable>/distributions</filename>.
+                    <td>Depends on the archive type. JARs and WARs go into <filename><replaceable>project.buildDir</replaceable>/libraries</filename>.
+                        ZIPs and TARs go into <filename><replaceable>project.buildDir</replaceable>/distributions</filename>.
                     </td>
                     <td>The directory to generate the archive into</td>
                 </tr>
diff --git a/subprojects/docs/src/docs/userguide/wrapperPlugin.xml b/subprojects/docs/src/docs/userguide/wrapperPlugin.xml
index 9ad70bc..f436722 100644
--- a/subprojects/docs/src/docs/userguide/wrapperPlugin.xml
+++ b/subprojects/docs/src/docs/userguide/wrapperPlugin.xml
@@ -24,13 +24,13 @@
 
     <para>The Gradle wrapper plugin allows the generation of Gradle wrapper files by adding a <apilink class="org.gradle.api.tasks.wrapper.Wrapper"/>
         task, that generates all files needed to run the build using the Gradle Wrapper.
-        Details about the Gradle Wrapper can be found in the according chapter <xref linkend='gradle_wrapper'/>
+        Details about the Gradle Wrapper can be found in <xref linkend='gradle_wrapper'/>.
     </para>
     <section>
         <title>Usage</title>
     <para>
-        Without modifying the <literal>build.gradle</literal> file, the wrapper plugin can be auto-applied to the rootproject of the current build by running
-        <literal>gradle wrapper</literal> from the commandline. This applies the plugin if no task named <literal>wrapper</literal> is already defined in the build.
+        Without modifying the <literal>build.gradle</literal> file, the wrapper plugin can be auto-applied to the root project of the current build by running
+        “<literal>gradle wrapper</literal>” from the command line. This applies the plugin if no task named <literal>wrapper</literal> is already defined in the build.
     </para>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/userguide/writingBuildScripts.xml b/subprojects/docs/src/docs/userguide/writingBuildScripts.xml
index 4ed644b..ffb48fe 100644
--- a/subprojects/docs/src/docs/userguide/writingBuildScripts.xml
+++ b/subprojects/docs/src/docs/userguide/writingBuildScripts.xml
@@ -22,12 +22,17 @@
         <para>Gradle provides a <firstterm>domain specific language</firstterm>, or DSL, for describing builds.
             This build language is based on Groovy, with some additions to make it easier to describe a build.
         </para>
+        <para>
+            A build script can contain any Groovy language element.
+            <footnote><para>Any language element except for statement labels.</para></footnote>
+            Gradle assumes that each build script is encoded using UTF-8.
+        </para>
     </section>
     <section id='sec:project_api'>
         <title>The Project API</title>
         <para>In the tutorial in <xref linkend='tutorial_java_projects'/> we used, for example, the
             <literal>apply()</literal> method. Where does this method come from? We said earlier that the build script
-            defines a project in Gradle. For each project in the build, Gradle creates an instance of type
+            defines a project in Gradle. For each project in the build, Gradle creates an object of type
             <apilink class='org.gradle.api.Project'/> and associates this <classname>Project</classname> object with
             the build script. As the build script executes, it configures this <classname>Project</classname> object:
         </para>
@@ -41,7 +46,7 @@
         </tip>
         <itemizedlist>
             <listitem>
-                <para>Any method you call in your build script, which <emphasis>is not defined</emphasis>
+                <para>Any method you call in your build script which <emphasis>is not defined</emphasis>
                     in the build script, is delegated to the <classname>Project</classname> object.
                 </para>
             </listitem>
@@ -62,7 +67,7 @@
             the <classname>Project</classname> object, for properties not defined in the build script. The other
             statement uses the <literal>project</literal> property available to any build script, which returns the
             associated <classname>Project</classname> object. Only if you define a property or a method which has the
-            same name as a member of the <classname>Project</classname> object, you need to use the <literal>project</literal>
+            same name as a member of the <classname>Project</classname> object, would you need to use the <literal>project</literal>
             property.
         </para>
         <section>
@@ -164,17 +169,31 @@
             </para>
             <para>By requiring special syntax for adding a property, Gradle can fail fast when an attempt is made to set a (predefined or extra) property
                 but the property is misspelled or does not exist.
-                <footnote>
-                    <para>As of Gradle 1.0-milestone-9, using <literal>ext</literal> to add extra properties is strongly encouraged but not yet enforced.
-                        Therefore, Gradle will not fail when an unknown property is set. However, it will print a warning.
-                    </para>
-                </footnote>
                 Extra properties can be accessed from anywhere their owning object can be accessed, giving them a wider scope than local variables.
-                Extra properties on a parent project are visible from subprojects.
+                Extra properties on a project are visible from its subprojects.
             </para>
-            <para>For further details on extra properties and their API, see <apilink class="org.gradle.api.plugins.ExtraPropertiesExtension"/>.</para>
+            <para>For further details on extra properties and their API, see the <apilink class="org.gradle.api.plugins.ExtraPropertiesExtension"/> class in the API documentation.</para>
         </section>
     </section>
+    <section id='sec:configuring_arbitrary_objects'>
+        <title>Configuring arbitrary objects</title>
+        <para>You can configure arbitrary objects in the following very readable way.
+        </para>
+        <sample id="configureObject" dir="userguide/tutorial/configureObject" title="Configuring arbitrary objects">
+            <sourcefile file="build.gradle"/>
+            <output args="-q configure"/>
+        </sample>
+    </section>
+    <section>
+        <title>Configuring arbitrary objects using an external script</title>
+        <para>You can also configure arbitrary objects using an external script.
+        </para>
+        <sample id="configureObjectUsingScript" dir="userguide/tutorial/configureObjectUsingScript" title="Configuring arbitrary objects using a script">
+            <sourcefile file="build.gradle"/>
+            <sourcefile file="other.gradle"/>
+            <output args="-q configure"/>
+        </sample>
+    </section>
     <section>
         <title>Some Groovy basics</title>
         <para>Groovy provides plenty of features for creating DSLs, and the Gradle build language takes advantage of these.
@@ -184,7 +203,7 @@
         <section>
             <title>Groovy JDK</title>
             <para>
-                Groovy adds lots of useful methods to JVM classes. For example, <classname>Iterable</classname> gets
+                Groovy adds lots of useful methods to the standard Java classes. For example, <classname>Iterable</classname> gets
                 an <literal>each</literal> method, which iterates over the elements of the <classname>Iterable</classname>:
             </para>
             <sample id="groovyBasics" dir="userguide/tutorial/groovy" title="Groovy JDK methods">
@@ -214,8 +233,13 @@
             <title>List and map literals</title>
             <para>
                 Groovy provides some shortcuts for defining <classname>List</classname> and <classname>Map</classname>
-                instances.
+                instances.  Both kinds of literals are straightforward, but map literals have some interesting twists.
             </para>
+            <para>For instance, the “<literal>apply</literal>” method (where you typically apply plugins) actually
+            takes a map parameter.  However, when you have a line like “<literal>apply plugin:'java'</literal>”, you
+            aren't actually using a map literal, you're actually using “named parameters”, which have almost exactly
+            the same syntax as a map literal (without the wrapping brackets). That named parameter list gets converted
+            to a map when the method is called, but it doesn't start out as a map.</para>
             <sample id="groovyBasics" dir="userguide/tutorial/groovy" title="List and map literals">
                 <sourcefile file="build.gradle" snippet="listAndMapLiterals"/>
             </sample>
diff --git a/subprojects/docs/src/samples/antlr/build.gradle b/subprojects/docs/src/samples/antlr/build.gradle
index bd6ad26..d797ee4 100644
--- a/subprojects/docs/src/samples/antlr/build.gradle
+++ b/subprojects/docs/src/samples/antlr/build.gradle
@@ -1,3 +1,4 @@
+
 // START SNIPPET use-plugin
 apply plugin: 'antlr'
 // END SNIPPET use-plugin
@@ -8,9 +9,15 @@ repositories {
 }
 
 dependencies {
-    antlr 'antlr:antlr:2.7.7'
+    antlr "org.antlr:antlr:3.5.2" // use ANTLR version 3
 // END SNIPPET declare-dependency
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 // START SNIPPET declare-dependency
 }
 // END SNIPPET declare-dependency
+
+// START SNIPPET memory-settings
+generateGrammarSource {
+    maxHeapSize = "64m"
+}
+// END SNIPPET memory-settings
diff --git a/subprojects/docs/src/samples/antlr/src/main/antlr/org/gradle/Calculator.g b/subprojects/docs/src/samples/antlr/src/main/antlr/org/gradle/Calculator.g
index 8be161c..7f05a43 100644
--- a/subprojects/docs/src/samples/antlr/src/main/antlr/org/gradle/Calculator.g
+++ b/subprojects/docs/src/samples/antlr/src/main/antlr/org/gradle/Calculator.g
@@ -1,12 +1,23 @@
-header {
+grammar Calculator;
+
+ at lexer::header {
     package org.gradle;
 }
 
-class CalculatorLexer extends Lexer; 
+ at parser::header {
+    package org.gradle;
+}
+
+add
+    :    NUMBER PLUS NUMBER
+    ;
+
+NUMBER
+    :    ('0'..'9')+
+    ;
 
-NUMBER	:	('0'..'9')+;
-PLUS	:	'+';
+PLUS
+    :    ('+')
+    ;
 
-class CalculatorParser extends Parser;
 
-add	:	NUMBER PLUS NUMBER;
diff --git a/subprojects/docs/src/samples/antlr/src/test/java/org/gradle/GrammarTest.java b/subprojects/docs/src/samples/antlr/src/test/java/org/gradle/GrammarTest.java
index a71ab72..4a36b1b 100644
--- a/subprojects/docs/src/samples/antlr/src/test/java/org/gradle/GrammarTest.java
+++ b/subprojects/docs/src/samples/antlr/src/test/java/org/gradle/GrammarTest.java
@@ -3,12 +3,15 @@ package org.gradle;
 import java.io.StringReader;
 import org.junit.Test;
 import static org.junit.Assert.*;
-
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.ANTLRStringStream;
 public class GrammarTest {
     @Test
     public void canUseGeneratedGrammar() throws Exception {
-        CalculatorLexer lexer = new CalculatorLexer(new StringReader("1+2"));
-        CalculatorParser parser = new CalculatorParser(lexer);
+        ANTLRStringStream in = new ANTLRStringStream("1+2");
+        CalculatorLexer lexer = new CalculatorLexer(in);
+        CommonTokenStream tokens = new CommonTokenStream(lexer);
+        CalculatorParser parser = new CalculatorParser(tokens);
         parser.add();
     }
 }
diff --git a/subprojects/docs/src/samples/application/build.gradle b/subprojects/docs/src/samples/application/build.gradle
index 5b30d64..163044f 100644
--- a/subprojects/docs/src/samples/application/build.gradle
+++ b/subprojects/docs/src/samples/application/build.gradle
@@ -1,6 +1,6 @@
-apply plugin:'java'
+apply plugin: 'java'
 // START SNIPPET use-plugin
-apply plugin:'application'
+apply plugin: 'application'
 // END SNIPPET use-plugin
 
 version = '1.0.2'
@@ -23,8 +23,14 @@ task createDocs {
     }
 }
 
-applicationDistribution.from(createDocs) {
-    into "docs"
+distributions {
+    main {
+        contents {
+            from(createDocs) {
+                into "docs"
+            }
+        }
+    }
 }
 // END SNIPPET distribution-spec
 
diff --git a/subprojects/docs/src/samples/buildDashboard/build.gradle b/subprojects/docs/src/samples/buildDashboard/build.gradle
index 486877d..af3b197 100755
--- a/subprojects/docs/src/samples/buildDashboard/build.gradle
+++ b/subprojects/docs/src/samples/buildDashboard/build.gradle
@@ -26,5 +26,5 @@ repositories {
 
 dependencies {
     compile localGroovy()
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/clientModuleDependencies/build.gradle b/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
index 011e524..52fdcfa 100644
--- a/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
+++ b/subprojects/docs/src/samples/clientModuleDependencies/build.gradle
@@ -4,7 +4,7 @@ subprojects {
             mavenCentral()
         }
         dependencies {
-            classpath 'junit:junit:4.11'
+            classpath 'junit:junit:4.12'
         }
     }
     apply plugin: 'java'
diff --git a/subprojects/docs/src/samples/codeQuality/build.gradle b/subprojects/docs/src/samples/codeQuality/build.gradle
index f8a58f3..99a0b27 100755
--- a/subprojects/docs/src/samples/codeQuality/build.gradle
+++ b/subprojects/docs/src/samples/codeQuality/build.gradle
@@ -22,5 +22,5 @@ repositories {
 
 dependencies {
     compile localGroovy()
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/componentMetadataRules/build.gradle b/subprojects/docs/src/samples/componentMetadataRules/build.gradle
new file mode 100644
index 0000000..f56095a
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/build.gradle
@@ -0,0 +1,98 @@
+import org.gradle.model.Mutate
+
+repositories {
+    ivy {
+        url "$projectDir/repo"
+    }
+}
+
+configurations {
+    config1
+    config2
+    config3
+    config4
+    config5
+    config6
+}
+
+// START SNIPPET latest-selector
+dependencies {
+    config1 "org.sample:client:latest.integration"
+    config2 "org.sample:client:latest.release"
+}
+
+task listConfigs << {
+    configurations.config1.each { println it.name }
+    println()
+    configurations.config2.each { println it.name}
+}
+// END SNIPPET latest-selector
+
+// START SNIPPET custom-status-scheme
+dependencies {
+    config3 "org.sample:api:latest.silver"
+    components {
+        all { ComponentMetadataDetails details ->
+            if (details.id.group == "org.sample" && details.id.name == "api") {
+                details.statusScheme = ["bronze", "silver", "gold", "platinum"]
+            }
+        }
+    }
+}
+// END SNIPPET custom-status-scheme
+
+task listApi << {
+    configurations.config3.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET custom-status-scheme-module
+dependencies {
+    config4 "org.sample:lib:latest.prod"
+    components {
+        withModule('org.sample:lib') { ComponentMetadataDetails details ->
+            details.statusScheme = ["int", "rc", "prod"]
+        }
+    }
+}
+// END SNIPPET custom-status-scheme-module
+
+task listLib << {
+    configurations.config4.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET rule-source-component-metadata-rule
+dependencies {
+    config5 "org.sample:api:latest.gold"
+    components {
+        withModule('org.sample:api', new CustomStatusRule())
+    }
+}
+
+class CustomStatusRule {
+    @Mutate
+    void setStatusScheme(ComponentMetadataDetails details) {
+        details.statusScheme = ["bronze", "silver", "gold", "platinum"]
+    }
+}
+// END SNIPPET rule-source-component-metadata-rule
+
+task listWithRule << {
+    configurations.config5.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET ivy-component-metadata-rule
+dependencies {
+    config6 "org.sample:lib:latest.rc"
+    components {
+        withModule("org.sample:lib") { ComponentMetadataDetails details, IvyModuleDescriptor ivyModule ->
+            if (ivyModule.branch == 'testing') {
+                details.status = "rc"
+            }
+        }
+    }
+}
+// END SNIPPET ivy-component-metadata-rule
+
+task listWithIvyRule << {
+    configurations.config6.each { println "Resolved: ${it.name}" }
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/internal/xml/xml-transformer-test.dtd b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/1.9/api-1.9.jar
similarity index 100%
rename from subprojects/core/src/test/resources/org/gradle/api/internal/xml/xml-transformer-test.dtd
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/1.9/api-1.9.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/1.9/ivy-1.9.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/1.9/ivy-1.9.xml
new file mode 100644
index 0000000..4cb511a
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/1.9/ivy-1.9.xml
@@ -0,0 +1,9 @@
+<ivy-module version="2.0">
+    <info organisation="org.sample"
+          module="api"
+          revision="1.9"
+          status="gold"/>
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/1.9/albatros-1.9.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/2.0/api-2.0.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/1.9/albatros-1.9.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/2.0/api-2.0.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/2.0/ivy-2.0.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/2.0/ivy-2.0.xml
new file mode 100644
index 0000000..cd4d747
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/api/2.0/ivy-2.0.xml
@@ -0,0 +1,9 @@
+<ivy-module version="2.0">
+    <info organisation="org.sample"
+          module="api"
+          revision="2.0"
+          status="silver"/>
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/2.0/albatros-2.0.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.3/client-1.3.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/2.0/albatros-2.0.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.3/client-1.3.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.3/ivy-1.3.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.3/ivy-1.3.xml
new file mode 100644
index 0000000..2e8cf32
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.3/ivy-1.3.xml
@@ -0,0 +1,9 @@
+<ivy-module version="2.0">
+    <info organisation="org.sample"
+          module="client"
+          revision="1.3"
+          status="milestone" />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.3/tuna-1.3.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.4/client-1.4.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.3/tuna-1.3.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.4/client-1.4.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.4/ivy-1.4.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.4/ivy-1.4.xml
new file mode 100644
index 0000000..90129fa
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.4/ivy-1.4.xml
@@ -0,0 +1,9 @@
+<ivy-module version="2.0">
+    <info organisation="org.sample"
+          module="client"
+          revision="1.4"
+          status="release" />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.4/tuna-1.4.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.5/client-1.5.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.4/tuna-1.4.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.5/client-1.5.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.5/ivy-1.5.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.5/ivy-1.5.xml
new file mode 100644
index 0000000..07c9364
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/client/1.5/ivy-1.5.xml
@@ -0,0 +1,9 @@
+<ivy-module version="2.0">
+    <info organisation="org.sample"
+          module="client"
+          revision="1.5"
+          status="integration" />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/1.9/ivy-1.9.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/1.9/ivy-1.9.xml
new file mode 100644
index 0000000..1b08f47
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/1.9/ivy-1.9.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="lib"
+          revision="1.9"
+          status="prod"/>
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.5/tuna-1.5.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/1.9/lib-1.9.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.5/tuna-1.5.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/1.9/lib-1.9.jar
diff --git a/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/2.0/ivy-2.0.xml b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/2.0/ivy-2.0.xml
new file mode 100644
index 0000000..bdc5f51
--- /dev/null
+++ b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/2.0/ivy-2.0.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="lib"
+          revision="2.0"
+          status="int"
+          branch="testing"
+          />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/repo/air.birds/albatros-1.0.jar b/subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/2.0/lib-2.0.jar
similarity index 100%
rename from subprojects/docs/src/samples/userguide/artifacts/configurationHandling/repo/air.birds/albatros-1.0.jar
rename to subprojects/docs/src/samples/componentMetadataRules/repo/org.sample/lib/2.0/lib-2.0.jar
diff --git a/subprojects/docs/src/samples/componentSelectionRules/build.gradle b/subprojects/docs/src/samples/componentSelectionRules/build.gradle
new file mode 100644
index 0000000..23e4c2e
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/build.gradle
@@ -0,0 +1,173 @@
+import org.gradle.api.artifacts.ivy.IvyModuleDescriptor
+import org.gradle.model.Mutate
+
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+repositories {
+    ivy {
+        url "file://${projectDir}/repo"
+    }
+}
+
+// Set up the status scheme so that "experimental" is a valid status for "org.sample" artifacts
+dependencies {
+    components {
+        all { ComponentMetadataDetails details ->
+            if (details.id.group == "org.sample") {
+                details.statusScheme = ["experimental", "integration", "milestone", "release"]
+            }
+        }
+    }
+}
+
+// START SNIPPET reject-version-1.1
+configurations {
+    rejectConfig {
+        resolutionStrategy {
+            componentSelection {
+                // Accept the highest version matching the requested version that isn't '1.5'
+                all { ComponentSelection selection ->
+                    if (selection.candidate.group == 'org.sample' && selection.candidate.module == 'api' && selection.candidate.version == '1.5') {
+                        selection.reject("version 1.5 is broken for 'org.sample:api'")
+                    }
+                }
+            }
+        }
+    }
+}
+
+dependencies {
+    rejectConfig "org.sample:api:1.+"
+}
+// END SNIPPET reject-version-1.1
+
+task printRejectConfig << {
+    configurations.rejectConfig.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET component-selection-with-metadata
+configurations {
+    metadataRulesConfig {
+        resolutionStrategy {
+            componentSelection {
+                // Reject any versions with a status of 'experimental'
+                all { ComponentSelection selection, ComponentMetadata metadata ->
+                    if (selection.candidate.group == 'org.sample' && metadata.status == 'experimental') {
+                        selection.reject("don't use experimental candidates from 'org.sample'")
+                    }
+                }
+                // Accept the highest version with either a "release" branch or a status of 'milestone'
+                withModule('org.sample:api') { ComponentSelection selection, IvyModuleDescriptor descriptor, ComponentMetadata metadata ->
+                    if (descriptor.branch != "release" && metadata.status != 'milestone') {
+                        selection.reject("'org.sample:api' must have testing branch or milestone status")
+                    }
+                }
+            }
+        }
+    }
+}
+// END SNIPPET component-selection-with-metadata
+
+dependencies {
+    metadataRulesConfig "org.sample:api:1.+"
+    metadataRulesConfig "org.sample:lib:+"
+}
+
+task printMetadataRulesConfig << {
+    configurations.metadataRulesConfig.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET targeted-component-selection
+configurations {
+    targetConfig {
+        resolutionStrategy {
+            componentSelection {
+                withModule("org.sample:api") { ComponentSelection selection ->
+                    if (selection.candidate.version == "1.5") {
+                        selection.reject("version 1.5 is broken for 'org.sample:api'")
+                    }
+                }
+            }
+        }
+    }
+}
+// END SNIPPET targeted-component-selection
+
+dependencies {
+    targetConfig "org.sample:api:1.+"
+}
+
+task printTargetConfig << {
+    configurations.targetConfig.each { println "Resolved: ${it.name}" }
+}
+
+// START SNIPPET api-component-selection
+class RejectTestBranch {
+    @Mutate
+    void evaluateRule(ComponentSelection selection, IvyModuleDescriptor ivy) {
+        if (ivy.branch == "test") {
+            selection.reject("reject test branch")
+        }
+    }
+}
+
+configurations {
+    ruleSourceConfig {
+        resolutionStrategy {
+            componentSelection {
+                all new RejectTestBranch()
+            }
+        }
+    }
+}
+// END SNIPPET api-component-selection
+
+dependencies {
+    ruleSourceConfig "org.sample:api:1.+"
+}
+
+task printRuleSourceConfig << {
+    configurations.ruleSourceConfig.each { println "Resolved: ${it.name}" }
+}
+
+configurations {
+    sampleConfig {
+        resolutionStrategy {
+            componentSelection {
+                withModule("org.sample:api") { ComponentSelection selection ->
+                    // Veto everything except patch releases
+                    if (selection.candidate.version.matches('\\d+.\\d+\\.\\d+')) {
+                        logger.lifecycle("** Accepted version: ${selection.candidate.version} **")
+                    } else {
+                        logger.lifecycle("Rejected version: ${selection.candidate.version}")
+                        selection.reject("Version is broken")
+                    }
+                }
+            }
+        }
+    }
+}
+
+dependencies {
+    sampleConfig group: 'org.sample', name: 'api', version: '1+'
+}
+
+task resolveConfiguration {
+    doLast {
+        configurations.sampleConfig.files.each { println it }
+    }
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.3.0/api-1.3.0.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.3.0/api-1.3.0.jar
diff --git a/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.3.0/ivy-1.3.0.xml b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.3.0/ivy-1.3.0.xml
new file mode 100644
index 0000000..521db0c
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.3.0/ivy-1.3.0.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="api"
+          revision="1.3.0"
+          status="milestone" />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.4/api-1.4.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.4/api-1.4.jar
diff --git a/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.4/ivy-1.4.xml b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.4/ivy-1.4.xml
new file mode 100644
index 0000000..2c15266
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.4/ivy-1.4.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="api"
+          revision="1.4"
+          status="experimental" />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.5/api-1.5.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.5/api-1.5.jar
diff --git a/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.5/ivy-1.5.xml b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.5/ivy-1.5.xml
new file mode 100644
index 0000000..5eb9f32
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/api/1.5/ivy-1.5.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="api"
+          revision="1.5"
+          status="integration"
+          branch="test"
+          />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/1.9/ivy-1.9.xml b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/1.9/ivy-1.9.xml
new file mode 100644
index 0000000..0bc8b33
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/1.9/ivy-1.9.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="lib"
+          revision="1.9"
+          branch="release"
+          />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/1.9/lib-1.9.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/1.9/lib-1.9.jar
diff --git a/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/2.0/ivy-2.0.xml b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/2.0/ivy-2.0.xml
new file mode 100644
index 0000000..36befd7
--- /dev/null
+++ b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/2.0/ivy-2.0.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="2.0">
+    <info organisation="org.sample"
+          module="lib"
+          revision="2.0"
+          status="experimental"
+          />
+    <publications>
+        <artifact />
+    </publications>
+</ivy-module>
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/2.0/lib-2.0.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/componentSelectionRules/repo/org.sample/lib/2.0/lib-2.0.jar
diff --git a/subprojects/docs/src/samples/customModel/componentType/build.gradle b/subprojects/docs/src/samples/customModel/componentType/build.gradle
new file mode 100644
index 0000000..23493f4
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/componentType/build.gradle
@@ -0,0 +1,103 @@
+
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.gradle.model.RuleSource
+import org.gradle.model.collection.CollectionBuilder
+
+interface ImageComponent extends LibrarySpec {
+    String title
+    List<String> sizes
+}
+
+class DefaultImageComponent extends BaseComponentSpec implements ImageComponent {
+    List<String> sizes
+    String title
+}
+
+interface ImageBinary extends BinarySpec {
+    String getTitle()
+    String getSize()
+}
+
+class DefaultImageBinary extends BaseBinarySpec implements ImageBinary {
+    String title
+    String size
+}
+
+class MyImageRenderingPlugin extends RuleSource {
+    @ComponentType
+    void register(ComponentTypeBuilder<ImageComponent> builder) {
+        builder.defaultImplementation(DefaultImageComponent)
+    }
+
+    @BinaryType
+    void register(BinaryTypeBuilder<ImageBinary> builder) {
+        builder.defaultImplementation(DefaultImageBinary)
+    }
+
+    @ComponentBinaries
+    void createBinariesForBinaryComponent(CollectionBuilder<ImageBinary> binaries, ImageComponent library) {
+        library.sizes.each{ fontSize ->
+            binaries.create("${library.title}${fontSize}Binary"){
+                it.size = fontSize;
+                it.title = library.title
+            }
+        }
+    }
+
+    @BinaryTasks
+    void createRenderingTasks(CollectionBuilder<Task> tasks, ImageBinary binary) {
+        tasks.create("render${binary.title}${binary.size}Svg", RenderSvg){
+            it.content = binary.title;
+            it.fontSize = binary.size;
+            it.outputFile = new File(it.project.buildDir, "renderedSvg/${binary.title}_${binary.size}.svg")
+        }
+    }
+}
+
+
+class RenderSvg extends DefaultTask{
+    @Input String content;
+    @Input String fontSize;
+    @OutputFile File outputFile;
+
+    @TaskAction void paint(){
+        outputFile.write("""
+             <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"
+             xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="16 8 400 100" preserveAspectRatio="xMidYMid meet" zoomAndPan="disable" onload="">
+             <text x="50" y="70" id="e1_texte" style="fill: #000000; font-family: Verdana; font-size: ${fontSize}; stroke: #008000; font-style: normal; font-weight: bold;">$content</text></svg>
+             """.stripIndent());
+
+    }
+}
+
+apply plugin:MyImageRenderingPlugin
+
+model {
+    components {
+        imageA(ImageComponent) {
+            title = "TitleA"
+            sizes = ["14px", "28px", "40px"]
+        }
+        imageB(ImageComponent) {
+            title = "TitleB"
+            sizes = ["14px", "28px", "40px"]
+        }
+    }
+}
+
+
diff --git a/subprojects/docs/src/samples/customModel/languageType/build.gradle b/subprojects/docs/src/samples/customModel/languageType/build.gradle
new file mode 100644
index 0000000..f70aba2
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/build.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import sample.documentation.DocumentationComponent
+import sample.markdown.MarkdownSourceSet
+import sample.markdown.MarkdownPlugin
+
+apply plugin:sample.documentation.DocumentationPlugin
+apply plugin:sample.markdown.MarkdownPlugin
+
+model {
+    components {
+        docs(DocumentationComponent) {
+            sources {
+                userguide(MarkdownSourceSet)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/build.gradle b/subprojects/docs/src/samples/customModel/languageType/buildSrc/build.gradle
new file mode 100644
index 0000000..b54c259
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin:'groovy'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile gradleApi()
+    compile localGroovy()
+    compile "org.pegdown:pegdown:1.1.0"
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationBinary.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationBinary.groovy
new file mode 100644
index 0000000..b345681
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationBinary.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.documentation
+
+import org.gradle.platform.base.binary.BaseBinarySpec
+
+class DefaultDocumentationBinary extends BaseBinarySpec implements DocumentationBinary {}
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationComponent.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationComponent.groovy
new file mode 100644
index 0000000..4bfe19f
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DefaultDocumentationComponent.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.documentation
+
+import org.gradle.platform.base.component.BaseComponentSpec
+
+class DefaultDocumentationComponent extends BaseComponentSpec implements DocumentationComponent {}
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationBinary.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationBinary.groovy
new file mode 100644
index 0000000..c177695
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationBinary.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.documentation
+
+import org.gradle.platform.base.BinarySpec
+
+interface DocumentationBinary extends BinarySpec {}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationComponent.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationComponent.groovy
new file mode 100644
index 0000000..bf89a47
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationComponent.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.documentation
+
+import org.gradle.platform.base.ComponentSpec
+
+interface DocumentationComponent extends ComponentSpec {}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationPlugin.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationPlugin.groovy
new file mode 100644
index 0000000..968132b
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationPlugin.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.documentation
+
+import org.gradle.api.Action
+import org.gradle.api.Task
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.model.Mutate
+import org.gradle.model.Path
+import org.gradle.model.RuleSource
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.*
+
+class DocumentationPlugin extends RuleSource {
+    @ComponentType
+    void register(ComponentTypeBuilder<DocumentationComponent> builder) {
+        builder.defaultImplementation(DefaultDocumentationComponent)
+    }
+
+    @BinaryType
+    void register(BinaryTypeBuilder<DocumentationBinary> builder) {
+        builder.defaultImplementation(DefaultDocumentationBinary)
+    }
+
+    @ComponentBinaries
+    void createBinariesForBinaryComponent(CollectionBuilder<DocumentationBinary> binaries, DocumentationComponent component) {
+        binaries.create("${component.name}Binary")
+    }
+
+    @BinaryTasks
+    void createZip(CollectionBuilder<Task> tasks, final DocumentationBinary binary, @Path("buildDir") final File buildDir) {
+        tasks.create("zip${binary.name.capitalize()}", Zip, new Action<Zip>() {
+            @Override
+            public void execute(Zip zipBinary) {
+                binary.source.withType(DocumentationSourceSet) { source ->
+                    zipBinary.into(source.name) {
+                        from(source.outputDir)
+                    }
+                    zipBinary.dependsOn source.taskName
+                }
+                zipBinary.setDestinationDir(new File(buildDir, binary.name))
+                zipBinary.setArchiveName(binary.name + ".zip")
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationSourceSet.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationSourceSet.groovy
new file mode 100644
index 0000000..906bbec
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/documentation/DocumentationSourceSet.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.documentation
+
+import org.gradle.language.base.LanguageSourceSet
+
+interface DocumentationSourceSet extends LanguageSourceSet {
+    File getOutputDir()
+
+    void setOutputDir(File file)
+
+    String getTaskName()
+
+    void setTaskName(String taskName)
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/DefaultMarkdownSourceSet.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/DefaultMarkdownSourceSet.groovy
new file mode 100644
index 0000000..701c9e2
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/DefaultMarkdownSourceSet.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.markdown
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet
+
+class DefaultMarkdownSourceSet extends BaseLanguageSourceSet implements MarkdownSourceSet {
+    File outputDir
+    String taskName
+}
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownHtmlCompile.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownHtmlCompile.groovy
new file mode 100644
index 0000000..98f1c04
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownHtmlCompile.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.markdown
+
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.SourceTask
+import org.gradle.api.tasks.TaskAction
+import org.pegdown.PegDownProcessor
+
+class MarkdownHtmlCompile extends SourceTask {
+    @OutputDirectory
+    File destinationDir
+
+    @TaskAction
+    void process() {
+        def encoding = "UTF-8"
+        PegDownProcessor processor = new PegDownProcessor()
+
+        getSource().each { sourceFile ->
+            String markdown = sourceFile.getText(encoding)
+            String html = processor.markdownToHtml(markdown)
+            File outputFile = new File(destinationDir, sourceFile.name.replace(".md", ".html"))
+            outputFile.write(html, encoding)
+        }
+        generateIndex()
+    }
+
+    def generateIndex() {
+        File indexFile = new File(destinationDir, "index.html")
+        indexFile.withWriter { writer ->
+            def markup = new groovy.xml.MarkupBuilder(writer)  // the builder
+            markup.html{
+                h1"Sample Userguide"
+                h2"Content"
+                ol {
+                    getSource().each { sourceFile ->
+                        def chapterTitle = sourceFile.name - ".md"
+                        li {
+                            a(href:chapterTitle + ".html", chapterTitle)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownPlugin.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownPlugin.groovy
new file mode 100644
index 0000000..e50407d
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownPlugin.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.markdown
+
+import org.gradle.model.Defaults
+import org.gradle.model.Path
+import org.gradle.model.RuleSource
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.LanguageType
+import org.gradle.platform.base.LanguageTypeBuilder
+import sample.documentation.DocumentationBinary
+
+class MarkdownPlugin extends RuleSource {
+    @LanguageType
+    void declareMarkdownLanguage(LanguageTypeBuilder<MarkdownSourceSet> builder) {
+        builder.setLanguageName("Markdown")
+        builder.defaultImplementation(DefaultMarkdownSourceSet)
+    }
+
+    @Defaults
+    void createMarkdownHtmlCompilerTasks(CollectionBuilder<DocumentationBinary> binaries, @Path("buildDir") File buildDir) {
+        binaries.beforeEach { binary ->
+            source.withType(MarkdownSourceSet.class) { markdownSourceSet ->
+                taskName = binary.name + name.capitalize() + "HtmlCompile"
+                outputDir = new File(buildDir, "${binary.name}/src/${name}")
+                binary.tasks.create(markdownSourceSet.taskName, MarkdownHtmlCompile) {
+                    source = markdownSourceSet.source
+                    destinationDir = markdownSourceSet.outputDir
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownSourceSet.groovy b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownSourceSet.groovy
new file mode 100644
index 0000000..1658362
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/buildSrc/src/main/groovy/sample/markdown/MarkdownSourceSet.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.markdown
+
+import sample.documentation.DocumentationSourceSet
+
+interface MarkdownSourceSet extends DocumentationSourceSet {
+    File getOutputDir()
+
+    void setOutputDir(File file)
+
+    String getTaskName()
+
+    void setTaskName(String taskName)
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter1.md b/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter1.md
new file mode 100644
index 0000000..c947bc6
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter1.md
@@ -0,0 +1,9 @@
+# Chapter 1 - Intoduction
+
+## Hello World
+
+This is a hello world example:
+
+    task hello << {
+        println 'Hello World!'
+    }
diff --git a/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter2.md b/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter2.md
new file mode 100644
index 0000000..820a2ff
--- /dev/null
+++ b/subprojects/docs/src/samples/customModel/languageType/src/docs/userguide/chapter2.md
@@ -0,0 +1,4 @@
+# Chapter 2 - Overview 
+
+* LanguageType
+* ComponentBinaries
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/customPlugin/consumer/build.gradle b/subprojects/docs/src/samples/customPlugin/consumer/build.gradle
index 33e1024..936bb2d 100644
--- a/subprojects/docs/src/samples/customPlugin/consumer/build.gradle
+++ b/subprojects/docs/src/samples/customPlugin/consumer/build.gradle
@@ -7,11 +7,12 @@ buildscript {
         }
     }
     dependencies {
-        classpath group: 'org.gradle', name: 'customPlugin', version: '1.0-SNAPSHOT'
+        classpath group: 'org.gradle', name: 'customPlugin',
+				  version: '1.0-SNAPSHOT'
     }
 }
 // END SNIPPET use-task
-apply plugin: 'greeting'
+apply plugin: 'org.samples.greeting'
 // END SNIPPET use-plugin
 // START SNIPPET use-task
 
diff --git a/subprojects/docs/src/samples/customPlugin/plugin/build.gradle b/subprojects/docs/src/samples/customPlugin/plugin/build.gradle
index 028f3f7..b51e4a0 100644
--- a/subprojects/docs/src/samples/customPlugin/plugin/build.gradle
+++ b/subprojects/docs/src/samples/customPlugin/plugin/build.gradle
@@ -22,7 +22,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 group = 'org.gradle'
diff --git a/subprojects/docs/src/samples/customPlugin/plugin/src/main/resources/META-INF/gradle-plugins/greeting.properties b/subprojects/docs/src/samples/customPlugin/plugin/src/main/resources/META-INF/gradle-plugins/org.samples.greeting.properties
similarity index 100%
rename from subprojects/docs/src/samples/customPlugin/plugin/src/main/resources/META-INF/gradle-plugins/greeting.properties
rename to subprojects/docs/src/samples/customPlugin/plugin/src/main/resources/META-INF/gradle-plugins/org.samples.greeting.properties
diff --git a/subprojects/docs/src/samples/customPlugin/plugin/src/test/groovy/org/gradle/GreetingPluginTest.groovy b/subprojects/docs/src/samples/customPlugin/plugin/src/test/groovy/org/gradle/GreetingPluginTest.groovy
index ab87279..49694ad 100644
--- a/subprojects/docs/src/samples/customPlugin/plugin/src/test/groovy/org/gradle/GreetingPluginTest.groovy
+++ b/subprojects/docs/src/samples/customPlugin/plugin/src/test/groovy/org/gradle/GreetingPluginTest.groovy
@@ -10,7 +10,7 @@ class GreetingPluginTest {
     @Test
     public void greeterPluginAddsGreetingTaskToProject() {
         Project project = ProjectBuilder.builder().build()
-        project.apply plugin: 'greeting'
+        project.pluginManager.apply 'org.samples.greeting'
 
         assertTrue(project.tasks.hello instanceof GreetingTask)
     }
diff --git a/subprojects/docs/src/samples/ear/earCustomized/ear/build.gradle b/subprojects/docs/src/samples/ear/earCustomized/ear/build.gradle
index eaa0c77..0a4092b 100644
--- a/subprojects/docs/src/samples/ear/earCustomized/ear/build.gradle
+++ b/subprojects/docs/src/samples/ear/earCustomized/ear/build.gradle
@@ -4,27 +4,30 @@ apply plugin: 'java'
 repositories { mavenCentral() }
 
 dependencies {
-    //following dependencies will become the ear modules and placed in the ear root
+    // The following dependencies will be the ear modules and
+    // will be placed in the ear root
     deploy project(':war')
 
-    //following dependencies will become ear libs and placed in a dir configured via libDirName property
+    // The following dependencies will become ear libs and will
+    // be placed in a dir configured via the libDirName property
     earlib group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
 }
 
 ear {
     appDirName 'src/main/app'  // use application metadata found in this folder
-    libDirName 'APP-INF/lib'  // put dependency libraries into APP-INF/lib inside the generated EAR;
-                                // also modify the generated deployment descriptor accordingly
+    // put dependent libraries into APP-INF/lib inside the generated EAR
+    libDirName 'APP-INF/lib'
     deploymentDescriptor {  // custom entries for application.xml:
 //      fileName = "application.xml"  // same as the default value
 //      version = "6"  // same as the default value
         applicationName = "customear"
         initializeInOrder = true
         displayName = "Custom Ear"  // defaults to project.name
-        description = "My customized EAR for the Gradle documentation"  // defaults to project.description
-//      libraryDirectory = "APP-INF/lib"  // not needed, because setting libDirName above did this for us
-//      module("my.jar", "java")  // wouldn't deploy since my.jar isn't a deploy dependency
-//      webModule("my.war", "/")  // wouldn't deploy since my.war isn't a deploy dependency
+        // defaults to project.description if not set
+        description = "My customized EAR for the Gradle documentation"
+//      libraryDirectory = "APP-INF/lib"  // not needed, above libDirName setting does this
+//      module("my.jar", "java")  // won't deploy as my.jar isn't deploy dependency
+//      webModule("my.war", "/")  // won't deploy as my.war isn't deploy dependency
         securityRole "admin"
         securityRole "superadmin"
         withXml { provider -> // add a custom node to the XML
diff --git a/subprojects/docs/src/samples/eclipse/build.gradle b/subprojects/docs/src/samples/eclipse/build.gradle
index 317c680..1aae456 100644
--- a/subprojects/docs/src/samples/eclipse/build.gradle
+++ b/subprojects/docs/src/samples/eclipse/build.gradle
@@ -1,7 +1,10 @@
 apply plugin: 'war'
-// START SNIPPET use-plugin
+// START SNIPPET use-eclipse-plugin
 apply plugin: 'eclipse'
-// END SNIPPET use-plugin
+// END SNIPPET use-eclipse-plugin
+// START SNIPPET use-eclipse-wtp-plugin
+apply plugin: 'eclipse-wtp'
+// END SNIPPET use-eclipse-wtp-plugin
 // START SNIPPET module-before-merged
 // START SNIPPET module-when-merged
 eclipse.classpath.file {
diff --git a/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle b/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
index 1c528d2..123c3b4 100644
--- a/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/groovy/customizedLayout/build.gradle
@@ -5,8 +5,8 @@ repositories {
 }
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
-    testCompile 'junit:junit:4.11'
+    compile 'org.codehaus.groovy:groovy:2.3.10'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET custom-source-locations
diff --git a/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle b/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
index 5184826..0b9fccc 100644
--- a/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
+++ b/subprojects/docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
@@ -6,6 +6,6 @@ repositories {
 }
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
-    testCompile 'junit:junit:4.11'
+    compile 'org.codehaus.groovy:groovy-all:2.3.10'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/docs/src/samples/groovy/multiproject/buildSrc/build.gradle b/subprojects/docs/src/samples/groovy/multiproject/buildSrc/build.gradle
index fd954f4..833be32 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/buildSrc/build.gradle
+++ b/subprojects/docs/src/samples/groovy/multiproject/buildSrc/build.gradle
@@ -3,5 +3,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/docs/src/samples/groovy/multiproject/groovycDetector/build.gradle b/subprojects/docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
index e671acf..dad5d30 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
+++ b/subprojects/docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
@@ -3,5 +3,5 @@ apply plugin: 'java'
 version = 'SNAPSHOT'
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
+    compile 'org.codehaus.groovy:groovy-all:2.3.10'
 }
diff --git a/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle b/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
index eb281c8..59f5495 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
+++ b/subprojects/docs/src/samples/groovy/multiproject/testproject/build.gradle
@@ -4,9 +4,9 @@ group = 'org.gradle'
 version = '1.0'
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
+    compile 'org.codehaus.groovy:groovy-all:2.3.10'
     compile project(':groovycDetector')
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 sourceSets {
diff --git a/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy b/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
index f1f0573..abb3d7d 100644
--- a/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
+++ b/subprojects/docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
@@ -7,7 +7,7 @@ class GroovycVersionTest {
   def groovycVersion
 
   @Test
-  void versionShouldBe2_2_0() {
-    assertEquals("2.2.0", groovycVersion)
+  void versionShouldBe2_3_10() {
+    assertEquals("2.3.10", groovycVersion)
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/samples/groovy/quickstart/build.gradle b/subprojects/docs/src/samples/groovy/quickstart/build.gradle
index efc14c7..be6f74e 100644
--- a/subprojects/docs/src/samples/groovy/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/groovy/quickstart/build.gradle
@@ -9,9 +9,9 @@ repositories {
 }
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
+    compile 'org.codehaus.groovy:groovy-all:2.3.10'
 // END SNIPPET groovy-dependency
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 // START SNIPPET groovy-dependency
 }
 // END SNIPPET groovy-dependency
diff --git a/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
index ad7ce48..fe4d460 100644
--- a/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
@@ -15,7 +15,7 @@ class PersonTest {
     }
 
     @Test public void usingCorrectVersionOfGroovy() {
-        assertEquals('2.2.0', GroovySystem.version)
+        assertEquals('2.3.10', GroovySystem.version)
     }
     
     @Test public void testResourcesAreAvailable() {
diff --git a/subprojects/docs/src/samples/idea/build.gradle b/subprojects/docs/src/samples/idea/build.gradle
index ed7269f..6a1d3bf 100644
--- a/subprojects/docs/src/samples/idea/build.gradle
+++ b/subprojects/docs/src/samples/idea/build.gradle
@@ -28,7 +28,9 @@ idea.project.ipr {
 // END SNIPPET project-before-merged
 // START SNIPPET project-with-xml
     withXml { provider ->
-        provider.node.component.find { it. at name == 'VcsDirectoryMappings' }.mapping. at vcs = 'Git'
+        provider.node.component
+                .find { it. at name == 'VcsDirectoryMappings' }
+                .mapping. at vcs = 'Git'
     }
 // START SNIPPET project-before-merged
 }
diff --git a/subprojects/docs/src/samples/ivy-publish/descriptor-customization/build.gradle b/subprojects/docs/src/samples/ivy-publish/descriptor-customization/build.gradle
index c5d852a..6b6b949 100644
--- a/subprojects/docs/src/samples/ivy-publish/descriptor-customization/build.gradle
+++ b/subprojects/docs/src/samples/ivy-publish/descriptor-customization/build.gradle
@@ -8,14 +8,16 @@ publishing {
     publications {
         ivyCustom(IvyPublication) {
             descriptor.withXml {
-                asNode().info[0].appendNode('description', 'A demonstration of ivy descriptor customization')
+                asNode().info[0].appendNode('description',
+                                            'A demonstration of ivy descriptor customization')
             }
         }
     }
 // END SNIPPET customize-descriptor
     repositories {
         ivy {
-            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
+            // change to point to your repo, e.g. http://my.org/repo
+            url "$buildDir/repo"
         }
     }
 }
diff --git a/subprojects/docs/src/samples/ivy-publish/java-multi-project/build.gradle b/subprojects/docs/src/samples/ivy-publish/java-multi-project/build.gradle
index 0d7cfdf..0ff0987 100644
--- a/subprojects/docs/src/samples/ivy-publish/java-multi-project/build.gradle
+++ b/subprojects/docs/src/samples/ivy-publish/java-multi-project/build.gradle
@@ -20,7 +20,7 @@ project(":project1") {
     description = "The first project"
 
     dependencies {
-       compile 'junit:junit:4.11', project(':project2')
+       compile 'junit:junit:4.12', project(':project2')
     }
 }
 
@@ -38,7 +38,8 @@ subprojects {
 // END SNIPPET publish-custom-artifact
         repositories {
             ivy {
-                url "${rootProject.buildDir}/repo" // change to point to your repo, e.g. http://my.org/repo
+                // change to point to your repo, e.g. http://my.org/repo
+                url "${rootProject.buildDir}/repo"
             }
         }
 // START SNIPPET publish-custom-artifact
diff --git a/subprojects/docs/src/samples/ivy-publish/java-multi-project/output-ivy.xml b/subprojects/docs/src/samples/ivy-publish/java-multi-project/output-ivy.xml
index 3d2c271..4abede8 100644
--- a/subprojects/docs/src/samples/ivy-publish/java-multi-project/output-ivy.xml
+++ b/subprojects/docs/src/samples/ivy-publish/java-multi-project/output-ivy.xml
@@ -14,7 +14,7 @@
     <artifact name="project1" type="source" ext="jar" conf="runtime" m:classifier="source" xmlns:m="http://ant.apache.org/ivy/maven"/>
   </publications>
   <dependencies>
-    <dependency org="junit" name="junit" rev="4.11" conf="runtime->default"/>
+    <dependency org="junit" name="junit" rev="4.12" conf="runtime->default"/>
     <dependency org="org.gradle.sample" name="project2" rev="1.0" conf="runtime->default"/>
   </dependencies>
 </ivy-module>
diff --git a/subprojects/docs/src/samples/ivy-publish/multiple-publications/build.gradle b/subprojects/docs/src/samples/ivy-publish/multiple-publications/build.gradle
index 31aae76..89238f1 100644
--- a/subprojects/docs/src/samples/ivy-publish/multiple-publications/build.gradle
+++ b/subprojects/docs/src/samples/ivy-publish/multiple-publications/build.gradle
@@ -17,7 +17,7 @@ subprojects {
 
 project(":project1") {
     dependencies {
-       compile 'junit:junit:4.11'
+       compile 'junit:junit:4.12'
     }
 
 
@@ -29,6 +29,8 @@ project(":project1") {
                 module 'project1-sample'
                 revision '1.1'
                 descriptor.status = 'milestone'
+                descriptor.branch = 'testing'
+                descriptor.extraInfo 'http://my.namespace', 'myElement', 'Some value'
 
                 from components.java
             }
diff --git a/subprojects/docs/src/samples/ivy-publish/multiple-publications/output/project1.ivy.xml b/subprojects/docs/src/samples/ivy-publish/multiple-publications/output/project1.ivy.xml
index f74cff2..2f7d3de 100644
--- a/subprojects/docs/src/samples/ivy-publish/multiple-publications/output/project1.ivy.xml
+++ b/subprojects/docs/src/samples/ivy-publish/multiple-publications/output/project1.ivy.xml
@@ -1,7 +1,9 @@
 <!-- This file is an example of the Ivy module descriptor that this build will produce -->
 <?xml version="1.0" encoding="UTF-8"?>
 <ivy-module version="2.0">
-  <info organisation="org.gradle.sample" module="project1-sample" revision="1.1" status="milestone" publication="«PUBLICATION-TIME-STAMP»"/>
+  <info organisation="org.gradle.sample" module="project1-sample" branch="testing" revision="1.1" status="milestone" publication="«PUBLICATION-TIME-STAMP»">
+    <ns:myElement xmlns:ns="http://my.namespace">Some value</ns:myElement>
+  </info>
   <configurations>
     <conf name="default" visibility="public" extends="runtime"/>
     <conf name="runtime" visibility="public"/>
@@ -10,6 +12,6 @@
     <artifact name="project1-sample" type="jar" ext="jar" conf="runtime"/>
   </publications>
   <dependencies>
-    <dependency org="junit" name="junit" rev="4.11" conf="runtime->default"/>
+    <dependency org="junit" name="junit" rev="4.12" conf="runtime->default"/>
   </dependencies>
 </ivy-module>
diff --git a/subprojects/docs/src/samples/ivy-publish/quickstart/build.gradle b/subprojects/docs/src/samples/ivy-publish/quickstart/build.gradle
index e596ec3..335bf07 100644
--- a/subprojects/docs/src/samples/ivy-publish/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/ivy-publish/quickstart/build.gradle
@@ -17,7 +17,8 @@ publishing {
 // START SNIPPET repositories
     repositories {
         ivy {
-            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
+            // change to point to your repo, e.g. http://my.org/repo
+            url "$buildDir/repo"
         }
     }
 // END SNIPPET repositories
diff --git a/subprojects/docs/src/samples/ivypublish/build.gradle b/subprojects/docs/src/samples/ivypublish/build.gradle
index c0e17f2..434c635 100644
--- a/subprojects/docs/src/samples/ivypublish/build.gradle
+++ b/subprojects/docs/src/samples/ivypublish/build.gradle
@@ -7,7 +7,7 @@ version = '1.0'
 group = 'org.gradle.test'
 
 dependencies {
-   compile 'junit:junit:4.11', project(':subproject')
+   compile 'junit:junit:4.12', project(':subproject')
 }
 
 ant {
@@ -49,7 +49,7 @@ uploadArchives {
         def root = new XmlParser().parse(new File(repoDir, 'ivy.xml'))
         assert root.publications.artifact.find { it. at name == 'ivypublishSource' }.attribute(ns.classifier) == 'src'
         assert (root.configurations.conf.collect { it. at name } as Set) == ['archives', 'compile', 'default', 'runtime', 'testCompile', 'testRuntime'] as Set
-        assert root.dependencies.dependency.find { it. at org == 'junit' }.attributes() == [org: 'junit', name: 'junit', rev: '4.11', conf: 'compile->default']
+        assert root.dependencies.dependency.find { it. at org == 'junit' }.attributes() == [org: 'junit', name: 'junit', rev: '4.12', conf: 'compile->default']
         assert root.dependencies.dependency.find { it. at org == 'ivypublish' }.attributes() == [org: 'ivypublish', name: 'subproject', rev: 'unspecified',
                 conf: 'compile->default']
     }
diff --git a/subprojects/docs/src/samples/java/apiAndImpl/build.gradle b/subprojects/docs/src/samples/java/apiAndImpl/build.gradle
index c3958ec..adc2f14 100644
--- a/subprojects/docs/src/samples/java/apiAndImpl/build.gradle
+++ b/subprojects/docs/src/samples/java/apiAndImpl/build.gradle
@@ -30,7 +30,7 @@ dependencies {
     implCompile sourceSets.api.output
     implCompile 'commons-lang:commons-lang:2.6'
 
-    testCompile 'junit:junit:4.9'
+    testCompile 'junit:junit:4.12'
     testCompile sourceSets.api.output
     testCompile sourceSets.impl.output
     runtime configurations.apiRuntime
diff --git a/subprojects/docs/src/samples/java/base/test/build.gradle b/subprojects/docs/src/samples/java/base/test/build.gradle
index ad435a7..ce4bf11 100644
--- a/subprojects/docs/src/samples/java/base/test/build.gradle
+++ b/subprojects/docs/src/samples/java/base/test/build.gradle
@@ -1,5 +1,5 @@
 dependencies {
-    compile 'junit:junit:4.11', project(':prod')
+    compile 'junit:junit:4.12', project(':prod')
 }
 
 task test(type: Test) {
diff --git a/subprojects/docs/src/samples/java/customizedLayout/build.gradle b/subprojects/docs/src/samples/java/customizedLayout/build.gradle
index 97b2f8b..1a8990d 100644
--- a/subprojects/docs/src/samples/java/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/java/customizedLayout/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET define-main
diff --git a/subprojects/docs/src/samples/java/multiproject/build.gradle b/subprojects/docs/src/samples/java/multiproject/build.gradle
index 50a5f06..1bd386d 100644
--- a/subprojects/docs/src/samples/java/multiproject/build.gradle
+++ b/subprojects/docs/src/samples/java/multiproject/build.gradle
@@ -8,7 +8,7 @@ subprojects {
     }
 
     dependencies {
-        testCompile 'junit:junit:4.11'
+        testCompile 'junit:junit:4.12'
     }
 
     version = '1.0'
diff --git a/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle b/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
index fd954f4..833be32 100644
--- a/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
+++ b/subprojects/docs/src/samples/java/multiproject/buildSrc/build.gradle
@@ -3,5 +3,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/docs/src/samples/java/onlyif/build.gradle b/subprojects/docs/src/samples/java/onlyif/build.gradle
index d0be4ef..d14a579 100644
--- a/subprojects/docs/src/samples/java/onlyif/build.gradle
+++ b/subprojects/docs/src/samples/java/onlyif/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 test {
diff --git a/subprojects/docs/src/samples/java/quickstart/build.gradle b/subprojects/docs/src/samples/java/quickstart/build.gradle
index 8b5fa71..535ca7d 100644
--- a/subprojects/docs/src/samples/java/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/java/quickstart/build.gradle
@@ -10,7 +10,8 @@ sourceCompatibility = 1.5
 version = '1.0'
 jar {
     manifest {
-        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
+        attributes 'Implementation-Title': 'Gradle Quickstart',
+                   'Implementation-Version': version
     }
 }
 // END SNIPPET customization
diff --git a/subprojects/docs/src/samples/java/testListener/build.gradle b/subprojects/docs/src/samples/java/testListener/build.gradle
index c3e49ba..dfde68e 100644
--- a/subprojects/docs/src/samples/java/testListener/build.gradle
+++ b/subprojects/docs/src/samples/java/testListener/build.gradle
@@ -1,9 +1,13 @@
+buildscript {
+  repositories.mavenCentral()
+  dependencies.classpath 'junit:junit:4.+'
+}
 import static org.junit.Assert.*
 
 apply plugin: 'java'
 repositories.mavenCentral()
 dependencies.testCompile 'junit:junit:4.+'
-test.stopAtFailuresOrErrors = false
+test.ignoreFailures = false
 
 def events = []
 // START SNIPPET testListenerImpl
diff --git a/subprojects/docs/src/samples/java/testListener/src/test/java/org/gradle/DoNothingTest.java b/subprojects/docs/src/samples/java/testListener/src/test/java/org/gradle/DoNothingTest.java
index d115a2b..fcf4371 100644
--- a/subprojects/docs/src/samples/java/testListener/src/test/java/org/gradle/DoNothingTest.java
+++ b/subprojects/docs/src/samples/java/testListener/src/test/java/org/gradle/DoNothingTest.java
@@ -1,3 +1,5 @@
+package org.gradle;
+
 import org.junit.Test;
 import static org.junit.Assert.*;
 
diff --git a/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle b/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
index 58643eb..ed8f9fc 100644
--- a/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
+++ b/subprojects/docs/src/samples/java/withIntegrationTests/build.gradle
@@ -12,7 +12,7 @@ sourceSets {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
     integrationTestCompile 'commons-collections:commons-collections:3.2'
     integrationTestCompile sourceSets.main.output
     integrationTestCompile configurations.testCompile
@@ -21,6 +21,8 @@ dependencies {
 }
 
 task integrationTest(type: Test, dependsOn: jar) {
+    group 'Verification'
+    description 'Runs the integration tests.'
     testClassesDir = sourceSets.integrationTest.output.classesDir
     classpath = sourceSets.integrationTest.runtimeClasspath
     systemProperties['jar.path'] = jar.archivePath
diff --git a/subprojects/docs/src/samples/javaGradlePlugin/build.gradle b/subprojects/docs/src/samples/javaGradlePlugin/build.gradle
new file mode 100755
index 0000000..acd1a4d
--- /dev/null
+++ b/subprojects/docs/src/samples/javaGradlePlugin/build.gradle
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// START SNIPPET use-java-gradle-plugin-plugin
+apply plugin: 'java-gradle-plugin'
+// END SNIPPET use-java-gradle-plugin-plugin
diff --git a/subprojects/docs/src/samples/javaGradlePlugin/readme.xml b/subprojects/docs/src/samples/javaGradlePlugin/readme.xml
new file mode 100644
index 0000000..ba429f3
--- /dev/null
+++ b/subprojects/docs/src/samples/javaGradlePlugin/readme.xml
@@ -0,0 +1,7 @@
+<sample>
+    <para>
+        This example demonstrates the use of the java gradle plugin development plugin.  By applying the plugin, the java plugin
+        is automatically applied as well as the gradleApi() dependency.  Furthermore, validations are performed against the
+        plugin metadata during jar execution.
+    </para>
+</sample>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/javaGradlePlugin/src/main/java/org/gradle/sample/SimplePlugin.java b/subprojects/docs/src/samples/javaGradlePlugin/src/main/java/org/gradle/sample/SimplePlugin.java
new file mode 100644
index 0000000..07f6041
--- /dev/null
+++ b/subprojects/docs/src/samples/javaGradlePlugin/src/main/java/org/gradle/sample/SimplePlugin.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.sample;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+
+public class SimplePlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        // Add plugin implementation here
+    }
+}
diff --git a/subprojects/docs/src/samples/javaGradlePlugin/src/main/resources/META-INF/gradle-plugins/simple-plugin.properties b/subprojects/docs/src/samples/javaGradlePlugin/src/main/resources/META-INF/gradle-plugins/simple-plugin.properties
new file mode 100644
index 0000000..721185f
--- /dev/null
+++ b/subprojects/docs/src/samples/javaGradlePlugin/src/main/resources/META-INF/gradle-plugins/simple-plugin.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.sample.SimplePlugin
diff --git a/subprojects/docs/src/samples/jvmComponents/java/build.gradle b/subprojects/docs/src/samples/jvmComponents/java/build.gradle
new file mode 100644
index 0000000..27803a5
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/java/build.gradle
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 {
+    id 'jvm-component'
+    id 'java-lang'
+}
+
+model {
+    components {
+        main(JvmLibrarySpec)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/jvmComponents/java/src/main/java/org/gradle/samples/HelloWorld.java b/subprojects/docs/src/samples/jvmComponents/java/src/main/java/org/gradle/samples/HelloWorld.java
new file mode 100644
index 0000000..9203c34
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/java/src/main/java/org/gradle/samples/HelloWorld.java
@@ -0,0 +1,7 @@
+package org.gradle.samples;
+
+public class HelloWorld {
+    public static void main(String[] args) {
+        System.out.println("Hello World!");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/jvmComponents/scala/build.gradle b/subprojects/docs/src/samples/jvmComponents/scala/build.gradle
new file mode 100644
index 0000000..317bb3c
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/scala/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 {
+    id 'jvm-component'
+    id 'scala-lang'
+}
+
+repositories{
+    jcenter()
+}
+
+model {
+    components {
+        main(JvmLibrarySpec)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Greeter.scala b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Greeter.scala
new file mode 100644
index 0000000..7ed8be6
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Greeter.scala
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.samples
+
+class Greeter {
+  def greet(person: Person) =
+    println("Hello ${person.name}");
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/HelloWorld.scala b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/HelloWorld.scala
new file mode 100644
index 0000000..b4e8086
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/HelloWorld.scala
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.samples
+
+object HelloWorld {
+  def main(args: Array[String]) =
+    new Greeter().greet(new Person("John Doe"));
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Person.java b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Person.java
new file mode 100644
index 0000000..7ee9b81
--- /dev/null
+++ b/subprojects/docs/src/samples/jvmComponents/scala/src/main/scala/org/gradle/samples/Person.java
@@ -0,0 +1,15 @@
+package org.gradle.samples;
+
+import java.lang.String;
+
+public class Person{
+    String name;
+
+    public Person(String name){
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/maven-publish/multiple-publications/build.gradle b/subprojects/docs/src/samples/maven-publish/multiple-publications/build.gradle
index 8d9c491..638b20f 100644
--- a/subprojects/docs/src/samples/maven-publish/multiple-publications/build.gradle
+++ b/subprojects/docs/src/samples/maven-publish/multiple-publications/build.gradle
@@ -17,7 +17,7 @@ subprojects {
 
 project(":project1") {
     dependencies {
-       compile 'org.slf4j:slf4j-api:1.7.5'
+       compile 'org.slf4j:slf4j-api:1.7.10'
     }
 
     // START SNIPPET customize-identity
diff --git a/subprojects/docs/src/samples/maven-publish/multiple-publications/output/project1.pom.xml b/subprojects/docs/src/samples/maven-publish/multiple-publications/output/project1.pom.xml
index d5e2f42..58d4e63 100644
--- a/subprojects/docs/src/samples/maven-publish/multiple-publications/output/project1.pom.xml
+++ b/subprojects/docs/src/samples/maven-publish/multiple-publications/output/project1.pom.xml
@@ -10,7 +10,7 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
-      <version>1.7.5</version>
+      <version>1.7.10</version>
       <scope>runtime</scope>
     </dependency>
   </dependencies>
diff --git a/subprojects/docs/src/samples/maven-publish/pomCustomization/build.gradle b/subprojects/docs/src/samples/maven-publish/pomCustomization/build.gradle
index 9adc23c..0a2dc62 100644
--- a/subprojects/docs/src/samples/maven-publish/pomCustomization/build.gradle
+++ b/subprojects/docs/src/samples/maven-publish/pomCustomization/build.gradle
@@ -8,14 +8,16 @@ publishing {
     publications {
         mavenCustom(MavenPublication) {
             pom.withXml {
-                asNode().appendNode('description', 'A demonstration of maven POM customization')
+                asNode().appendNode('description',
+                                    'A demonstration of maven POM customization')
             }
         }
     }
 // END SNIPPET pom-modification
     repositories {
         maven {
-            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
+            // change to point to your repo, e.g. http://my.org/repo
+            url "$buildDir/repo"
         }
     }
 }
diff --git a/subprojects/docs/src/samples/maven-publish/quickstart/build.gradle b/subprojects/docs/src/samples/maven-publish/quickstart/build.gradle
index 770938e..699f714 100644
--- a/subprojects/docs/src/samples/maven-publish/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/maven-publish/quickstart/build.gradle
@@ -6,20 +6,23 @@ apply plugin: 'maven-publish'
 group = 'org.gradle.sample'
 version = '1.0'
 
-publishing {
 // START SNIPPET publish-component
+publishing {
     publications {
         mavenJava(MavenPublication) {
             from components.java
         }
     }
+}
 // END SNIPPET publish-component
 // START SNIPPET repositories
+publishing {
     repositories {
         maven {
-            url "$buildDir/repo" // change to point to your repo, e.g. http://my.org/repo
+            // change to point to your repo, e.g. http://my.org/repo
+            url "$buildDir/repo"
         }
     }
-// END SNIPPET repositories
 }
+// END SNIPPET repositories
 
diff --git a/subprojects/docs/src/samples/maven/pomGeneration/build.gradle b/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
index 5675d65..ba3cddf 100644
--- a/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
+++ b/subprojects/docs/src/samples/maven/pomGeneration/build.gradle
@@ -58,8 +58,14 @@ uploadArchives {
 
 // Customize the contents of the pom
 
-ext.installer = install.repositories.mavenInstaller
-ext.deployer = uploadArchives.repositories.mavenDeployer
+// START SNIPPET when-configured
+def installer = install.repositories.mavenInstaller
+def deployer = uploadArchives.repositories.mavenDeployer
+
+[installer, deployer]*.pom*.whenConfigured {pom ->
+    pom.dependencies.find {dep -> dep.groupId == 'group3' && dep.artifactId == 'runtime' }.optional = true
+}
+// END SNIPPET when-configured
 
 [installer, deployer]*.pom*.version = '1.0MVN'
 installer.pom.project {
@@ -84,12 +90,6 @@ task writeNewPom << {
 }
 // END SNIPPET new-pom
 
-// START SNIPPET when-configured
-[installer, deployer]*.pom*.whenConfigured {pom ->
-    pom.dependencies.find {dep -> dep.groupId == 'group3' && dep.artifactId == 'runtime' }.optional = true
-}
-// END SNIPPET when-configured
-
 task writeDeployerPom(dependsOn: uploadArchives) << {
     deployer.pom.writeTo("$buildDir/deployerpom.xml")
 }
diff --git a/subprojects/docs/src/samples/modelRules/basicRuleSourcePlugin/build.gradle b/subprojects/docs/src/samples/modelRules/basicRuleSourcePlugin/build.gradle
new file mode 100644
index 0000000..cfec515
--- /dev/null
+++ b/subprojects/docs/src/samples/modelRules/basicRuleSourcePlugin/build.gradle
@@ -0,0 +1,43 @@
+// START SNIPPET managed-type-and-plugin
+// START SNIPPET managed-type
+ at Managed
+interface Person {
+  void setFirstName(String n); String getFirstName()
+  void setLastName(String n); String getLastName()
+}
+// END SNIPPET managed-type
+
+// START SNIPPET rule-source-plugin
+class PersonRules extends RuleSource {
+// START SNIPPET create-rule
+  @Model void person(Person p) {}
+// END SNIPPET create-rule
+
+// START SNIPPET plugin-mutate-rule
+  @Mutate void setFirstName(Person p) {
+    p.firstName = "John"
+  }
+// END SNIPPET plugin-mutate-rule
+
+// START SNIPPET task-create-rule
+ @Mutate void createHelloTask(CollectionBuilder<Task> tasks, Person p) {
+    tasks.create("hello") {
+      doLast {
+        println "Hello $p.firstName $p.lastName!"
+      }
+    }
+  }
+// END SNIPPET task-create-rule
+}
+
+apply plugin: PersonRules
+// END SNIPPET rule-source-plugin
+// END SNIPPET managed-type-and-plugin
+
+// START SNIPPET dsl
+model {
+  person {
+    lastName = "Smith"
+  }
+}
+// END SNIPPET dsl
diff --git a/subprojects/docs/src/samples/modelRules/modelDsl/build.gradle b/subprojects/docs/src/samples/modelRules/modelDsl/build.gradle
new file mode 100644
index 0000000..a5dd007
--- /dev/null
+++ b/subprojects/docs/src/samples/modelRules/modelDsl/build.gradle
@@ -0,0 +1,28 @@
+ at Managed
+interface Person {
+  void setFirstName(String n); String getFirstName()
+  void setLastName(String n); String getLastName()
+}
+
+class PersonRules extends RuleSource {
+ @Mutate void createHelloTask(CollectionBuilder<Task> tasks, Person p) {
+    tasks.create("hello") {
+      doLast {
+        println "Hello $p.firstName $p.lastName!"
+      }
+    }
+  }
+}
+
+apply plugin: PersonRules
+
+model {
+  person {
+    lastName = "Smith"
+  }
+// START SNIPPET create-rule
+  person(Person) {
+    firstName = "John"
+  }
+// END SNIPPET create-rule
+}
diff --git a/subprojects/docs/src/samples/native-binaries/assembler/build.gradle b/subprojects/docs/src/samples/native-binaries/assembler/build.gradle
index fe6803e..3ebdcb4 100644
--- a/subprojects/docs/src/samples/native-binaries/assembler/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/assembler/build.gradle
@@ -27,33 +27,27 @@ model {
     }
 }
 
-sources {
-    i386_masm {
-        asm {
-            source.srcDir "src/main/asm_i386_masm"
-        }
-    }
-    i386_gcc {
-        asm {
-            source.srcDir "src/main/asm_i386_gcc"
-        }
-    }
-}
-
 // START SNIPPET assembler-args
-executables {
-    main {
-        binaries.all {
-            if (toolChain in VisualCpp) {
-// END SNIPPET assembler-args
-                source sources.i386_masm.asm
-// START SNIPPET assembler-args
-                assembler.args "/Zi"
-            } else {
-// END SNIPPET assembler-args
-                source sources.i386_gcc.asm
-// START SNIPPET assembler-args
-                assembler.args "-g"
+model {
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "x86"
+            binaries.all {
+                if (toolChain in VisualCpp) {
+                    sources {
+                        platformAsm(AssemblerSourceSet) {
+                            source.srcDir "src/main/asm_i386_masm"
+                        }
+                    }
+                    assembler.args "/Zi"
+                } else {
+                    sources {
+                        platformAsm(AssemblerSourceSet) {
+                            source.srcDir "src/main/asm_i386_gcc"
+                        }
+                    }
+                    assembler.args "-g"
+                }
             }
         }
     }
diff --git a/subprojects/docs/src/samples/native-binaries/c/build.gradle b/subprojects/docs/src/samples/native-binaries/c/build.gradle
index 83a0d3a..5c662b6 100644
--- a/subprojects/docs/src/samples/native-binaries/c/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/c/build.gradle
@@ -19,21 +19,25 @@ apply plugin: 'c'
 // END SNIPPET apply-plugin
 
 // START SNIPPET libraries
-libraries {
-    hello {}
+model {
+    components {
+        hello(NativeLibrarySpec)
+    }
 }
 // END SNIPPET libraries
 
 // START SNIPPET executables
-executables {
-    main {}
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+               c.lib library: "hello"
+            }
+        }
+    }
 }
 // END SNIPPET executables
 
-// START SNIPPET source-library
-sources.main.c.lib libraries.hello
-// END SNIPPET source-library
-
 // START SNIPPET compiler-args
 binaries.all {
     // Define toolchain-specific compiler and linker options
@@ -49,8 +53,9 @@ binaries.all {
 // END SNIPPET compiler-args
 
 // START SNIPPET all-shared-libraries
-// For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
-binaries.withType(SharedLibraryBinary) {
+// For any shared library binaries built with Visual C++,
+// define the DLL_EXPORT macro
+binaries.withType(SharedLibraryBinarySpec) {
     if (toolChain in VisualCpp) {
         cCompiler.args "/Zi"
         cCompiler.define "DLL_EXPORT"
diff --git a/subprojects/docs/src/samples/native-binaries/c/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/c/src/hello/headers/hello.h
index b4a1ec2..164b66e 100755
--- a/subprojects/docs/src/samples/native-binaries/c/src/hello/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/c/src/hello/headers/hello.h
@@ -1,4 +1,4 @@
-#ifdef DLL_EXPORT
+#if defined(_WIN32) && defined(DLL_EXPORT)
 #define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
diff --git a/subprojects/docs/src/samples/native-binaries/cpp-exe/build.gradle b/subprojects/docs/src/samples/native-binaries/cpp-exe/build.gradle
index 6b9a3c3..d8b6bfd 100644
--- a/subprojects/docs/src/samples/native-binaries/cpp-exe/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/cpp-exe/build.gradle
@@ -1,19 +1,21 @@
 apply plugin: "cpp"
 
-executables {
-    main {
-        binaries.all {
-            // Define a preprocessor macro
-            cppCompiler.define "NDEBUG"
-            // Add some additional compiler arguments
-            if (toolChain in Gcc) {
-                cppCompiler.args "-fno-access-control", "-fconserve-space"
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                // Define a preprocessor macro
+                cppCompiler.define "NDEBUG"
+                // Add some additional compiler arguments
+                if (toolChain in Gcc) {
+                    cppCompiler.args "-fno-access-control", "-fconserve-space"
+                }
             }
         }
     }
 }
 
-binaries.withType(ExecutableBinary) { binary ->
+binaries.withType(NativeExecutableBinary) { binary ->
     def linkTask = binary.tasks.link
     def stripTask = task("strip${binary.name.capitalize()}") {
         dependsOn linkTask
diff --git a/subprojects/docs/src/samples/native-binaries/cpp-lib/build.gradle b/subprojects/docs/src/samples/native-binaries/cpp-lib/build.gradle
index 41079c8..e7d1574 100644
--- a/subprojects/docs/src/samples/native-binaries/cpp-lib/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/cpp-lib/build.gradle
@@ -3,11 +3,13 @@ apply plugin: "cpp"
 // END SNIPPET use-plugin
 
 // START SNIPPET args
-libraries {
-    main {
-        binaries.withType(SharedLibraryBinary) {
-            // Define a preprocessor macro that only applies to shared libraries
-            cppCompiler.define "DLL_EXPORT"
+model {
+    components {
+        main(NativeLibrarySpec) {
+            binaries.withType(SharedLibraryBinarySpec) {
+                // Define a preprocessor macro that only applies to shared libraries
+                cppCompiler.define "DLL_EXPORT"
+            }
         }
     }
 }
diff --git a/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/cpp/hello.cpp b/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/cpp/hello.cpp
index b849636..ae2e337 100644
--- a/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/cpp/hello.cpp
+++ b/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/cpp/hello.cpp
@@ -1,13 +1,5 @@
 #include <iostream>
-#ifdef _WIN32
-#ifdef DLL_EXPORT
-#define LIB_FUNC __declspec(dllexport)
-#endif
-#endif
-#ifndef LIB_FUNC
-#define LIB_FUNC
-#endif
-
+#include "hello.h"
 
 void LIB_FUNC hello () {
   std::cout << "Hello, World!\n";
diff --git a/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/headers/hello.h b/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/headers/hello.h
index 1d3f35d..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/cpp-lib/src/main/headers/hello.h
@@ -1,5 +1,5 @@
-#ifdef _WIN32
-#define LIB_FUNC __declspec(dllimport)
+#if defined(_WIN32) && defined(DLL_EXPORT)
+#define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
 #endif
diff --git a/subprojects/docs/src/samples/native-binaries/cpp/build.gradle b/subprojects/docs/src/samples/native-binaries/cpp/build.gradle
index bd5a4a9..8e0b6c6 100644
--- a/subprojects/docs/src/samples/native-binaries/cpp/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/cpp/build.gradle
@@ -3,26 +3,29 @@ apply plugin: 'cpp'
 // END SNIPPET apply-plugin
 
 // START SNIPPET libraries
-libraries {
-    hello {}
+model {
+    components {
+        hello(NativeLibrarySpec)
+    }
 }
 // END SNIPPET libraries
 
 // START SNIPPET executables
-executables {
-    main {}
-}
-// END SNIPPET executables
-
+model {
+    components {
+        main(NativeExecutableSpec) {
 // START SNIPPET source-library
-sources {
-    main {
-        cpp {
-            lib libraries.hello
+            sources {
+                cpp {
+                    lib library: "hello"
+                }
+            }
+// END SNIPPET source-library
         }
     }
 }
-// END SNIPPET source-library
+
+// END SNIPPET executables
 
 // START SNIPPET all-binaries
 binaries.all {
@@ -42,7 +45,7 @@ binaries.all {
 // END SNIPPET all-binaries
 
 // For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
-binaries.withType(SharedLibraryBinary) {
+binaries.withType(SharedLibraryBinarySpec) {
     if (toolChain in VisualCpp) {
         cppCompiler.define "DLL_EXPORT"
     }
diff --git a/subprojects/docs/src/samples/native-binaries/cpp/src/hello/cpp/hello.cpp b/subprojects/docs/src/samples/native-binaries/cpp/src/hello/cpp/hello.cpp
index dc7b045..26a050a 100755
--- a/subprojects/docs/src/samples/native-binaries/cpp/src/hello/cpp/hello.cpp
+++ b/subprojects/docs/src/samples/native-binaries/cpp/src/hello/cpp/hello.cpp
@@ -1,6 +1,6 @@
 #include <iostream>
 #include "hello.h"
 
-void LIB_FUNC hello () {
-  std::cout << "Hello world!" << std::endl;
+void LIB_FUNC Greeter::hello () {
+    std::cout << "Hello world!" << std::endl;
 }
diff --git a/subprojects/docs/src/samples/native-binaries/cpp/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/cpp/src/hello/headers/hello.h
index 51b48fd..e8e3ee6 100755
--- a/subprojects/docs/src/samples/native-binaries/cpp/src/hello/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/cpp/src/hello/headers/hello.h
@@ -1,7 +1,10 @@
-#ifdef DLL_EXPORT
+#if defined(_WIN32) && defined(DLL_EXPORT)
 #define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
 #endif
 
-void LIB_FUNC hello();
+class Greeter {
+    public:
+    void LIB_FUNC hello();
+};
diff --git a/subprojects/docs/src/samples/native-binaries/cpp/src/main/cpp/main.cpp b/subprojects/docs/src/samples/native-binaries/cpp/src/main/cpp/main.cpp
index 09f8ace..e35c88f 100644
--- a/subprojects/docs/src/samples/native-binaries/cpp/src/main/cpp/main.cpp
+++ b/subprojects/docs/src/samples/native-binaries/cpp/src/main/cpp/main.cpp
@@ -1,6 +1,7 @@
 #include "hello.h"
 
 int main () {
-  hello();
-  return 0;
+    Greeter greeter;
+    greeter.hello();
+    return 0;
 }
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/build.gradle b/subprojects/docs/src/samples/native-binaries/cunit/build.gradle
index 9efb6e2..95d9b45 100644
--- a/subprojects/docs/src/samples/native-binaries/cunit/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/cunit/build.gradle
@@ -7,23 +7,31 @@ model {
         passing
         failing
     }
+    platforms {
+        x86 {
+            architecture "x86"
+        }
+    }
     repositories {
         libs(PrebuiltLibraries) {
             cunit {
-                headers.srcDir "lib/cunit/2.1-2/include"
+                headers.srcDir "libs/cunit/2.1-2/include"
                 binaries.withType(StaticLibraryBinary) {
-                    staticLibraryFile = file("lib/cunit/2.1-2/lib/" + findCUnitLibForPlatform(targetPlatform))
+                    staticLibraryFile =
+                        file("libs/cunit/2.1-2/lib/" +
+                             findCUnitLibForPlatform(targetPlatform))
                 }
             }
         }
     }
-}
-
-libraries {
-    operators {}
+    components {
+        operators(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
 }
 // START SNIPPET configure-test-binary
-binaries.withType(TestSuiteExecutableBinary) {
+binaries.withType(CUnitTestSuiteBinarySpec) {
     lib library: "cunit", linkage: "static"
 
     if (flavor == flavors.failing) {
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Automated.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Automated.h
deleted file mode 100644
index 2acc694..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Automated.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Automated Interface (generates HTML Report Files).
- *
- *  Feb 2002      Initial implementation (AK)
- *
- *  13-Feb-2002   Single interface to automated_run_tests. (AK)
- *
- *  20-Jul-2004   New interface, doxygen comments. (JDS)
- */
-
-/** @file
- * Automated testing interface with xml output (user interface).
- */
-/** @addtogroup Automated
- * @{
- */
-                                       
-#ifndef CUNIT_AUTOMATED_H_SEEN
-#define CUNIT_AUTOMATED_H_SEEN
-
-#include "CUnit.h"
-#include "TestDB.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-CU_EXPORT void CU_automated_run_tests(void);
-/**< 
- *  Runs CUnit tests using the automated interface.
- *  This function sets appropriate callback functions, initializes the 
- *  test output files, and calls the appropriate functions to list the 
- *  tests and run them.  If an output file name root has not been 
- *  specified using CU_set_output_filename(), a generic root will be 
- *  applied.  It is an error to call this function before the CUnit
- *  test registry has been initialized (check by assertion).
- */
-
-CU_EXPORT CU_ErrorCode CU_list_tests_to_file(void);
-/**< 
- *  Generates an xml file containing a list of all tests in all suites 
- *  in the active registry.  The output file will be named according to 
- *  the most recent call to CU_set_output_filename(), or a default if 
- *  not previously set.
- *
- *  @return An error code indicating the error status.
- */
-
-CU_EXPORT void CU_set_output_filename(const char* szFilenameRoot);
-/**< 
- *  Sets the root file name for automated test output files.
- *  The strings "-Listing.xml" and "-Results.xml" are appended to the 
- *  specified root to generate the filenames.  If szFilenameRoot is 
- *  empty, the default root ("CUnitAutomated") is used.
- *
- *  @param szFilenameRoot String containing root to use for file names.
- */
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-/** Deprecated (version 1). @deprecated Use CU_automated_run_tests(). */
-#define automated_run_tests() CU_automated_run_tests()
-/** Deprecated (version 1). @deprecated Use CU_set_output_filename(). */
-#define set_output_filename(x) CU_set_output_filename((x))
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_AUTOMATED_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Basic.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Basic.h
deleted file mode 100644
index d8a638d..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Basic.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2004-2006  Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Interface for simple test runner.
- *
- *  11-Aug-2004   Initial implementation of basic test runner interface. (JDS)
- */
-
-/** @file
- * Basic interface with output to stdout.
- */
-/** @addtogroup Basic
- * @{
- */
-
-#ifndef CUNIT_BASIC_H_SEEN
-#define CUNIT_BASIC_H_SEEN
-
-#include "CUnit.h"
-#include "TestDB.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Run modes for the basic interface. */
-typedef enum {
-  CU_BRM_NORMAL = 0,  /**< Normal mode - failures and run summary are printed [default]. */
-  CU_BRM_SILENT,      /**< Silent mode - no output is printed except framework error messages. */
-  CU_BRM_VERBOSE      /**< Verbose mode - maximum output of run details. */
-} CU_BasicRunMode;
-
-CU_EXPORT CU_ErrorCode CU_basic_run_tests(void);
-/**< 
- *  Runs all registered CUnit tests using the basic interface.
- *  The default CU_BasicRunMode is used unless it has been
- *  previously changed using CU_basic_set_mode().  The CUnit test
- *  registry must have been initialized before calling this function.
- *
- *  @return A CU_ErrorCode indicating the framework error condition, including
- *          CUE_NOREGISTRY - Registry has not been initialized.
- */
-
-CU_EXPORT CU_ErrorCode CU_basic_run_suite(CU_pSuite pSuite);
-/**< 
- *  Runs all tests for a specific suite in the basic interface.
- *  If pSuite is NULL, the function returns without taking any
- *  action. The default CU_BasicRunMode is used unless it has
- *  been changed using CU_basic_set_mode().
- *
- *  @param pSuite The CU_Suite to run.
- *  @return A CU_ErrorCode indicating the framework error condition, including
- *          CUE_NOSUITE - pSuite was NULL.
- */
-
-CU_EXPORT CU_ErrorCode CU_basic_run_test(CU_pSuite pSuite, CU_pTest pTest);
-/**<
- *  Runs a single test in a specific suite in the basic interface.
- *  If pSuite or pTest is NULL, the function returns without
- *  taking any action.  The default CU_BasicRunMode is used unless
- *  it has been changed using CU_basic_set_mode.
- *
- *  @param pSuite The CU_Suite holding the CU_Test to run.
- *  @param pTest  The CU_Test to run.
- *  @return A CU_ErrorCode indicating the framework error condition, including
- *          CUE_NOSUITE - pSuite was NULL.
- *          CUE_NOTEST  - pTest was NULL.
- */
-
-CU_EXPORT void CU_basic_set_mode(CU_BasicRunMode mode);
-/**< Sets the run mode for the basic interface.
- *  @param mode The new CU_BasicRunMode for subsequent test
- *              runs using the basic interface.
- */
-
-CU_EXPORT CU_BasicRunMode CU_basic_get_mode(void);
-/**< Retrieves the current run mode for the basic interface.
- *  @return The current CU_BasicRunMode setting for test
- *              runs using the basic interface.
- */
-
-CU_EXPORT void CU_basic_show_failures(CU_pFailureRecord pFailure);
-/**<
- *  Prints a summary of run failures to stdout.
- *  This is provided for user convenience upon request, and does 
- *  not take into account the current run mode.  The failures are 
- *  printed to stdout independent of the most recent run mode.
- *
- *  @param pFailure List of CU_pFailureRecord's to output.
- */
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_BASIC_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUError.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUError.h
deleted file mode 100644
index c9943c1..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUError.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains CUnit error codes which can be used externally.
- *
- *  Aug 2001      Initial implementation.  (AK)
- *
- *  02/Oct/2001   Added proper Eror Codes. (AK)
- *
- *  13-Oct-2001   Added Error Codes for Duplicate TestGroup and Test. (AK)
- *
- *  03-Aug-2004   Converted error code macros to an enum, doxygen comments, moved
- *                error handing code here, changed file name from Errno.h, added
- *                error codes for file open errors, added error action selection. (JDS)
- *
- *  05-Sep-2004   Added internal test interface. (JDS)
- */
-
-/** @file
- *  Error handling functions (user interface).
- *  CUnit uses a simple (and conventional) error handling strategy.
- *  Functions that can generate errors set (and usually return) an
- *  error code to indicate the run status.  The error code can be
- *  inspected using the CU_get_error() function.  A descriptive
- *  error message can be retrieved using CU_get_error_msg().
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_CUERROR_H_SEEN
-#define CUNIT_CUERROR_H_SEEN
-
-#include <errno.h>
-
-/*------------------------------------------------------------------------*/
-/** CUnit error codes.
- *  If codes are added or removed, be sure to make a change to the
- *  error messages in CUError.c/get_error_desc().
- *  @see CU_set_error()
- *  @see CU_get_error()
- *  @see CU_get_error_msg()
- */
-typedef enum {
-  /* basic errors */
-  CUE_SUCCESS           = 0,  /**< No error condition. */
-  CUE_NOMEMORY          = 1,  /**< Memory allocation failed. */
-
-  /* Test Registry Level Errors */
-  CUE_NOREGISTRY        = 10,  /**< Test registry not initialized. */
-  CUE_REGISTRY_EXISTS   = 11,  /**< Attempt to CU_set_registry() without CU_cleanup_registry(). */
-
-  /* Test Suite Level Errors */
-  CUE_NOSUITE           = 20,  /**< A required CU_pSuite pointer was NULL. */
-  CUE_NO_SUITENAME      = 21,  /**< Required CU_Suite name not provided. */
-  CUE_SINIT_FAILED      = 22,  /**< Suite initialization failed. */
-  CUE_SCLEAN_FAILED     = 23,  /**< Suite cleanup failed. */
-  CUE_DUP_SUITE         = 24,  /**< Duplicate suite name not allowed. */
-  CUE_SUITE_INACTIVE    = 25,  /**< Test run initiated for an inactive suite. */
-
-  /* Test Case Level Errors */
-  CUE_NOTEST            = 30,  /**< A required CU_pTest or CU_TestFunc pointer was NULL. */
-  CUE_NO_TESTNAME       = 31,  /**< Required CU_Test name not provided. */
-  CUE_DUP_TEST          = 32,  /**< Duplicate test case name not allowed. */
-  CUE_TEST_NOT_IN_SUITE = 33,  /**< Test not registered in specified suite. */
-  CUE_TEST_INACTIVE     = 34,  /**< Test run initiated for an inactive test. */
-
-  /* File handling errors */
-  CUE_FOPEN_FAILED      = 40,  /**< An error occurred opening a file. */
-  CUE_FCLOSE_FAILED     = 41,  /**< An error occurred closing a file. */
-  CUE_BAD_FILENAME      = 42,  /**< A bad filename was requested (NULL, empty, nonexistent, etc.). */
-  CUE_WRITE_ERROR       = 43   /**< An error occurred during a write to a file. */
-} CU_ErrorCode;
-
-/*------------------------------------------------------------------------*/
-/** CUnit error action codes.
- *  These are used to set the action desired when an error
- *  condition is detected in the CUnit framework.
- *  @see CU_set_error_action()
- *  @see CU_get_error_action()
- */
-typedef enum CU_ErrorAction {
-  CUEA_IGNORE,    /**< Runs should be continued when an error condition occurs (if possible). */
-  CUEA_FAIL,      /**< Runs should be stopped when an error condition occurs. */
-  CUEA_ABORT      /**< The application should exit() when an error conditions occurs. */
-} CU_ErrorAction;
-
-/* Error handling & reporting functions. */
-
-#include "CUnit.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-CU_EXPORT CU_ErrorCode   CU_get_error(void);
-/**<
- *  Retrieves the current CUnit framework error code.
- *  CUnit implementation functions set the error code to indicate the
- *  status of the most recent operation.  In general, the CUnit functions
- *  will clear the code to CUE_SUCCESS, then reset it to a specific error
- *  code if an exception condition is encountered.  Some functions
- *  return the code, others leave it to the user to inspect if desired.
- *
- *  @return The current error condition code.
- *  @see CU_get_error_msg()
- *  @see CU_ErrorCode
- */
-
-CU_EXPORT const char*    CU_get_error_msg(void);
-/**<
- *  Retrieves a message corresponding to the current framework error code.
- *  CUnit implementation functions set the error code to indicate the
- *  of the most recent operation.  In general, the CUnit functions will
- *  clear the code to CUE_SUCCESS, then reset it to a specific error
- *  code if an exception condition is encountered.  This function allows
- *  the user to retrieve a descriptive error message corresponding to the
- *  error code set by the last operation.
- *
- *  @return A message corresponding to the current error condition.
- *  @see CU_get_error()
- *  @see CU_ErrorCode
- */
-
-CU_EXPORT void           CU_set_error_action(CU_ErrorAction action);
-/**<
- *  Sets the action to take when a framework error condition occurs.
- *  This function should be used to specify the action to take
- *  when an error condition is encountered.  The default action is
- *  CUEA_IGNORE, which results in errors being ignored and test runs
- *  being continued (if possible).  A value of CUEA_FAIL causes test
- *  runs to stop as soon as an error condition occurs, while
- *  CU_ABORT causes the application to exit on any error.
- *
- *  @param action CU_ErrorAction indicating the new error action.
- *  @see CU_get_error_action()
- *  @see CU_set_error()
- *  @see CU_ErrorAction
- */
-
-CU_EXPORT CU_ErrorAction CU_get_error_action(void);
-/**<
- *  Retrieves the current framework error action code.
- *
- *  @return The current error action code.
- *  @see CU_set_error_action()
- *  @see CU_set_error()
- *  @see CU_ErrorAction
- */
-
-#ifdef CUNIT_BUILD_TESTS
-void test_cunit_CUError(void);
-#endif
-
-/* Internal function - users should not generally call this function */
-CU_EXPORT void CU_set_error(CU_ErrorCode error);
-/**<
- *  Sets the CUnit framework error code.
- *  This function is used internally by CUnit implementation functions
- *  when an error condition occurs within the framework.  It should
- *  not generally be called by user code.  NOTE that if the current
- *  error action is CUEA_ABORT, then calling this function will
- *  result in exit() being called for the current application.
- *
- *  @param error CU_ErrorCode indicating the current error condition.
- *  @see CU_get_error()
- *  @see CU_get_error_msg()
- *  @see CU_ErrorCode
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-/** Deprecated (version 1). @deprecated Use CU_get_error_msg(). */
-#define get_error() CU_get_error_msg()
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#endif  /*  CUNIT_CUERROR_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit.h
deleted file mode 100644
index 9592eda..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit.h
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  ASSERT Macro definitions and general CUnit configuration definitions.
- *
- *  09/Aug/2001   ASSERT definitions. (AK)
- *
- *  12/Mar/2003   New Assert definitions. (AK)
- *
- *  27/Jul/2003   Modified ASSERT_XXX Macro definitions. (AK)
- *
- *  15-Jul-2004   New interface, changed action on assert failure to not
- *                return, provided _FATAL versions of assertions to return
- *                from test function on failure. (JDS)
- *
- *  01-Sep-2004   Modified assertions for setjmp/longjmp mechanism of 
- *                aborting test runs, added CU_FAIL and CU_PASS macros. (JDS)
- *
- *  07-May-2005   Added CU_ prefix to remaining CUnit defines (BOOL, TRUE, 
- *                FALSE, MAX_...).  Added CU_UNREFERENCED_PARAMETER() define. (JDS)
- */
-
-/** @file
- * Basic CUnit include file for user and system code.
- * Defines macros for assertions for use in user test cases.
- * Basic system macro definitions also appear here.
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_CUNIT_H_SEEN
-#define CUNIT_CUNIT_H_SEEN
-
-#include <string.h>
-#include <math.h>
-
-/** CUnit version number. */
-#define CU_VERSION "2.1-2"
-
-/*  Max string lengths for names (includes terminating NULL. */
-/** Maximum length of a test name string. */
-#define CU_MAX_TEST_NAME_LENGTH 256
-/** Maximim length of a suite name string. */
-#define CU_MAX_SUITE_NAME_LENGTH 256
-
-/* Global type Definitions to be used for boolean operators. */
-#ifndef CU_BOOL
-  /** Boolean type for CUnit use. */
-  #define CU_BOOL int
-#endif
-
-#ifndef CU_TRUE
-  /** Boolean TRUE for CUnit use. */
-  #define CU_TRUE 1
-#endif
-
-#ifndef CU_FALSE
-  /** Boolean FALSE for CUnit use. */
-  #define CU_FALSE 0
-#endif
-
-#ifndef CU_UNREFERENCED_PARAMETER
-  /** Consistent approach to referencing unused parameters. */
-  #define CU_UNREFERENCED_PARAMETER(x) (void)x
-#endif
-
-#ifndef CU_MAX
-#  define CU_MAX(a,b) (((a) >= (b)) ? (a) : (b))
-#endif
-
-#ifndef CU_MIN
-#  define CU_MIN(a,b) (((a) >= (b)) ? (b) : (a))
-#endif
-
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__)
-#  ifdef CU_DLL
-#    ifdef CU_BUILD_DLL
-#      define CU_EXPORT __declspec(dllexport)
-#    else
-#      define CU_EXPORT __declspec(dllimport)
-#    endif
-#  else
-#    define CU_EXPORT
-#  endif
-#  ifdef _MSC_VER
-#    define snprintf _snprintf
-#  endif
-#else
-#  define CU_EXPORT
-#endif  /* WIN32 */
-
-#include "CUError.h"
-#include "TestDB.h"   /* not needed here - included for user convenience */
-#include "TestRun.h"  /* not needed here - include (after BOOL define) for user convenience */
-
-/** Record a pass condition without performing a logical test. */
-#define CU_PASS(msg) \
-  { CU_assertImplementation(CU_TRUE, __LINE__, ("CU_PASS(" #msg ")"), __FILE__, "", CU_FALSE); }
-
-/** Simple assertion.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT(value) \
-  { CU_assertImplementation((value), __LINE__, #value, __FILE__, "", CU_FALSE); }
-
-/** Simple assertion.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_FATAL(value) \
-  { CU_assertImplementation((value), __LINE__, #value, __FILE__, "", CU_TRUE); }
-
-/** Simple assertion.
- *  Reports failure with no other action.
- */
-#define CU_TEST(value) \
-  { CU_assertImplementation((value), __LINE__, #value, __FILE__, "", CU_FALSE); }
-
-/** Simple assertion.
- *  Reports failure and causes test to abort.
- */
-#define CU_TEST_FATAL(value) \
-  { CU_assertImplementation((value), __LINE__, #value, __FILE__, "", CU_TRUE); }
-
-/** Record a failure without performing a logical test. */
-#define CU_FAIL(msg) \
-  { CU_assertImplementation(CU_FALSE, __LINE__, ("CU_FAIL(" #msg ")"), __FILE__, "", CU_FALSE); }
-
-/** Record a failure without performing a logical test, and abort test. */
-#define CU_FAIL_FATAL(msg) \
-  { CU_assertImplementation(CU_FALSE, __LINE__, ("CU_FAIL_FATAL(" #msg ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that value is CU_TRUE.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_TRUE(value) \
-  { CU_assertImplementation((value), __LINE__, ("CU_ASSERT_TRUE(" #value ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that value is CU_TRUE.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_TRUE_FATAL(value) \
-  { CU_assertImplementation((value), __LINE__, ("CU_ASSERT_TRUE_FATAL(" #value ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that value is CU_FALSE.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_FALSE(value) \
-  { CU_assertImplementation(!(value), __LINE__, ("CU_ASSERT_FALSE(" #value ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that value is CU_FALSE.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_FALSE_FATAL(value) \
-  { CU_assertImplementation(!(value), __LINE__, ("CU_ASSERT_FALSE_FATAL(" #value ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that actual == expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_EQUAL(actual, expected) \
-  { CU_assertImplementation(((actual) == (expected)), __LINE__, ("CU_ASSERT_EQUAL(" #actual "," #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that actual == expected.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation(((actual) == (expected)), __LINE__, ("CU_ASSERT_EQUAL_FATAL(" #actual "," #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that actual != expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_NOT_EQUAL(actual, expected) \
-  { CU_assertImplementation(((actual) != (expected)), __LINE__, ("CU_ASSERT_NOT_EQUAL(" #actual "," #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that actual != expected.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_NOT_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation(((actual) != (expected)), __LINE__, ("CU_ASSERT_NOT_EQUAL_FATAL(" #actual "," #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that pointers actual == expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_PTR_EQUAL(actual, expected) \
-  { CU_assertImplementation(((void*)(actual) == (void*)(expected)), __LINE__, ("CU_ASSERT_PTR_EQUAL(" #actual "," #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that pointers actual == expected.
- * Reports failure and causes test to abort.
- */
-#define CU_ASSERT_PTR_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation(((void*)(actual) == (void*)(expected)), __LINE__, ("CU_ASSERT_PTR_EQUAL_FATAL(" #actual "," #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that pointers actual != expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_PTR_NOT_EQUAL(actual, expected) \
-  { CU_assertImplementation(((void*)(actual) != (void*)(expected)), __LINE__, ("CU_ASSERT_PTR_NOT_EQUAL(" #actual "," #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that pointers actual != expected.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_PTR_NOT_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation(((void*)(actual) != (void*)(expected)), __LINE__, ("CU_ASSERT_PTR_NOT_EQUAL_FATAL(" #actual "," #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that pointer value is NULL.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_PTR_NULL(value) \
-  { CU_assertImplementation((NULL == (void*)(value)), __LINE__, ("CU_ASSERT_PTR_NULL(" #value")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that pointer value is NULL.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_PTR_NULL_FATAL(value) \
-  { CU_assertImplementation((NULL == (void*)(value)), __LINE__, ("CU_ASSERT_PTR_NULL_FATAL(" #value")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that pointer value is not NULL.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_PTR_NOT_NULL(value) \
-  { CU_assertImplementation((NULL != (void*)(value)), __LINE__, ("CU_ASSERT_PTR_NOT_NULL(" #value")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that pointer value is not NULL.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_PTR_NOT_NULL_FATAL(value) \
-  { CU_assertImplementation((NULL != (void*)(value)), __LINE__, ("CU_ASSERT_PTR_NOT_NULL_FATAL(" #value")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that string actual == expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_STRING_EQUAL(actual, expected) \
-  { CU_assertImplementation(!(strcmp((const char*)(actual), (const char*)(expected))), __LINE__, ("CU_ASSERT_STRING_EQUAL(" #actual ","  #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that string actual == expected.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_STRING_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation(!(strcmp((const char*)(actual), (const char*)(expected))), __LINE__, ("CU_ASSERT_STRING_EQUAL_FATAL(" #actual ","  #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that string actual != expected.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_STRING_NOT_EQUAL(actual, expected) \
-  { CU_assertImplementation((strcmp((const char*)(actual), (const char*)(expected))), __LINE__, ("CU_ASSERT_STRING_NOT_EQUAL(" #actual ","  #expected ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that string actual != expected.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_STRING_NOT_EQUAL_FATAL(actual, expected) \
-  { CU_assertImplementation((strcmp((const char*)(actual), (const char*)(expected))), __LINE__, ("CU_ASSERT_STRING_NOT_EQUAL_FATAL(" #actual ","  #expected ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that string actual == expected with length specified.
- *  The comparison is limited to count characters.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_NSTRING_EQUAL(actual, expected, count) \
-  { CU_assertImplementation(!(strncmp((const char*)(actual), (const char*)(expected), (size_t)(count))), __LINE__, ("CU_ASSERT_NSTRING_EQUAL(" #actual ","  #expected "," #count ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that string actual == expected with length specified.
- *  The comparison is limited to count characters.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_NSTRING_EQUAL_FATAL(actual, expected, count) \
-  { CU_assertImplementation(!(strncmp((const char*)(actual), (const char*)(expected), (size_t)(count))), __LINE__, ("CU_ASSERT_NSTRING_EQUAL_FATAL(" #actual ","  #expected "," #count ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that string actual != expected with length specified.
- *  The comparison is limited to count characters.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_NSTRING_NOT_EQUAL(actual, expected, count) \
-  { CU_assertImplementation((strncmp((const char*)(actual), (const char*)(expected), (size_t)(count))), __LINE__, ("CU_ASSERT_NSTRING_NOT_EQUAL(" #actual ","  #expected "," #count ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that string actual != expected with length specified.
- *  The comparison is limited to count characters.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_NSTRING_NOT_EQUAL_FATAL(actual, expected, count) \
-  { CU_assertImplementation((strncmp((const char*)(actual), (const char*)(expected), (size_t)(count))), __LINE__, ("CU_ASSERT_NSTRING_NOT_EQUAL_FATAL(" #actual ","  #expected "," #count ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that double actual == expected within the specified tolerance.
- *  If actual is within granularity of expected, the assertion passes.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_DOUBLE_EQUAL(actual, expected, granularity) \
-  { CU_assertImplementation(((fabs((double)(actual) - (expected)) <= fabs((double)(granularity)))), __LINE__, ("CU_ASSERT_DOUBLE_EQUAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that double actual == expected within the specified tolerance.
- *  If actual is within granularity of expected, the assertion passes.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_DOUBLE_EQUAL_FATAL(actual, expected, granularity) \
-  { CU_assertImplementation(((fabs((double)(actual) - (expected)) <= fabs((double)(granularity)))), __LINE__, ("CU_ASSERT_DOUBLE_EQUAL_FATAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", CU_TRUE); }
-
-/** Asserts that double actual != expected within the specified tolerance.
- *  If actual is within granularity of expected, the assertion fails.
- *  Reports failure with no other action.
- */
-#define CU_ASSERT_DOUBLE_NOT_EQUAL(actual, expected, granularity) \
-  { CU_assertImplementation(((fabs((double)(actual) - (expected)) > fabs((double)(granularity)))), __LINE__, ("CU_ASSERT_DOUBLE_NOT_EQUAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", CU_FALSE); }
-
-/** Asserts that double actual != expected within the specified tolerance.
- *  If actual is within granularity of expected, the assertion fails.
- *  Reports failure and causes test to abort.
- */
-#define CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL(actual, expected, granularity) \
-  { CU_assertImplementation(((fabs((double)(actual) - (expected)) > fabs((double)(granularity)))), __LINE__, ("CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", CU_TRUE); }
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-
-#ifndef BOOL
-  /** Deprecated (version 2.0-2). @deprecated Use CU_BOOL. */
-  #define BOOL int
-#endif
-
-#ifndef TRUE
-  /** Deprecated (version 2.0-2). @deprecated Use CU_TRUE. */
-  #define TRUE 1
-#endif
-
-#ifndef FALSE
-  /** Deprecated (version 2.0-2). @deprecated Use CU_FALSE. */
-  #define FALSE	0
-#endif
-
-/** Deprecated (version 2.0-2). @deprecated Use CU_MAX_TEST_NAME_LENGTH. */
-#define MAX_TEST_NAME_LENGTH	256
-/** Deprecated (version 2.0-2). @deprecated Use CU_MAX_SUITE_NAME_LENGTH. */
-#define MAX_SUITE_NAME_LENGTH	256
-
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_FATAL. */
-#define ASSERT(value) { if (FALSE == (int)(value)) { CU_assertImplementation((BOOL)value, __LINE__, #value, __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_TRUE_FATAL. */
-#define ASSERT_TRUE(value) { if (FALSE == (value)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_TRUE(" #value ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_FALSE_FATAL. */
-#define ASSERT_FALSE(value) { if (FALSE != (value)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_FALSE(" #value ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_EQUAL_FATAL. */
-#define ASSERT_EQUAL(actual, expected) { if ((actual) != (expected)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_EQUAL(" #actual "," #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_NOT_EQUAL_FATAL. */
-#define ASSERT_NOT_EQUAL(actual, expected) { if ((void*)(actual) == (void*)(expected)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_NOT_EQUAL(" #actual "," #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_PTR_EQUAL_FATAL. */
-#define ASSERT_PTR_EQUAL(actual, expected) { if ((void*)(actual) != (void*)(expected)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_PTR_EQUAL(" #actual "," #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_PTR_NOT_EQUAL_FATAL. */
-#define ASSERT_PTR_NOT_EQUAL(actual, expected) { if ((void*)(actual) == (void*)(expected)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_PTR_NOT_EQUAL(" #actual "," #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_PTR_NULL_FATAL. */
-#define ASSERT_PTR_NULL(value)  { if (NULL != (void*)(value)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_PTR_NULL(" #value")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_PTR_NOT_NULL_FATAL. */
-#define ASSERT_PTR_NOT_NULL(value) { if (NULL == (void*)(value)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_PTR_NOT_NULL(" #value")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_STRING_EQUAL_FATAL. */
-#define ASSERT_STRING_EQUAL(actual, expected) { if (strcmp((const char*)actual, (const char*)expected)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_STRING_EQUAL(" #actual ","  #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_STRING_NOT_EQUAL_FATAL. */
-#define ASSERT_STRING_NOT_EQUAL(actual, expected) { if (!strcmp((const char*)actual, (const char*)expected)) { CU_assertImplementation(TRUE, __LINE__, ("ASSERT_STRING_NOT_EQUAL(" #actual ","  #expected ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_NSTRING_EQUAL_FATAL. */
-#define ASSERT_NSTRING_EQUAL(actual, expected, count) { if (strncmp((const char*)actual, (const char*)expected, (size_t)count)) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_NSTRING_EQUAL(" #actual ","  #expected "," #count ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_NSTRING_NOT_EQUAL_FATAL. */
-#define ASSERT_NSTRING_NOT_EQUAL(actual, expected, count) { if (!strncmp((const char*)actual, (const char*)expected, (size_t)count)) { CU_assertImplementation(TRUE, __LINE__, ("ASSERT_NSTRING_NOT_EQUAL(" #actual ","  #expected "," #count ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_DOUBLE_EQUAL_FATAL. */
-#define ASSERT_DOUBLE_EQUAL(actual, expected, granularity) { if ((fabs((double)actual - expected) > fabs((double)granularity))) { CU_assertImplementation(FALSE, __LINE__, ("ASSERT_DOUBLE_EQUAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", FALSE); return; }}
-/** Deprecated (version 1). @deprecated Use CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL. */
-#define ASSERT_DOUBLE_NOT_EQUAL(actual, expected, granularity) { if ((fabs((double)actual - expected) <= fabs((double)granularity))) { CU_assertImplementation(TRUE, __LINE__, ("ASSERT_DOUBLE_NOT_EQUAL(" #actual ","  #expected "," #granularity ")"), __FILE__, "", FALSE); return; }}
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#endif  /*  CUNIT_CUNIT_H_SEEN  */
-
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit_intl.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit_intl.h
deleted file mode 100644
index 813917b..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/CUnit_intl.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2006  Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Internationalization support
- *
- *  05-May-2006   Initial implementation.  (JDS)
- */
-
-/** @file
- *  Internal CUnit header supporting internationalization of
- *  CUnit framework & interfaces.
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_CUNIT_INTL_H_SEEN
-#define CUNIT_CUNIT_INTL_H_SEEN
-
-/* activate these when source preparation is complete
-#include <libintl.h>
-#ifndef _
-#  define _(String) gettext (String)
-#endif
-#ifndef gettext_noop
-#  define gettext_noop(String) String
-#endif
-#ifndef N_
-#  define N_(String) gettext_noop (String)
-#endif
-*/
-
-/* deactivate these when source preparation is complete */
-#undef _
-#define _(String) (String)
-#undef N_
-#define N_(String) String
-#undef textdomain
-#define textdomain(Domain)
-#undef bindtextdomain
-#define bindtextdomain(Package, Directory)
-
-#endif  /*  CUNIT_CUNIT_INTL_H_SEEN  */
-
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Console.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Console.h
deleted file mode 100644
index f5b3769..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Console.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains Interface for console Run tests.
- *
- *  Aug 2001      Initial implementation. (AK)
- *
- *  09/Aug/2001   Single interface to Console_run_tests. (AK)
- *
- *  20-Jul-2004   New interface, doxygen comments. (JDS)
- */
-
-/** @file
- * Console interface with interactive output (user interface).
- */
-/** @addtogroup Console
- * @{
- */
-
-#ifndef CUNIT_CONSOLE_H_SEEN
-#define CUNIT_CONSOLE_H_SEEN
-
-#include "CUnit.h"
-#include "TestDB.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-CU_EXPORT void CU_console_run_tests(void);
-/**< Run registered CUnit tests using the console interface. */
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-/** Deprecated (version 1). @deprecated Use CU_console_run_tests(). */
-#define console_run_tests() CU_console_run_tests()
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_CONSOLE_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/MyMem.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/MyMem.h
deleted file mode 100644
index 88a7e62..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/MyMem.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains Memory Related Defines to use internal routines to detect Memory Leak
- *  in Debug Versions
- *
- *  18/Jun/2002   Memory Debug Functions. (AK)
- *
- *  17-Jul-2004   New interface for global function names. (JDS)
- *
- *  05-Sep-2004   Added internal test interface. (JDS)
- */
-
-/** @file
- *  Memory management functions (user interface).
- *  Two versions of memory allocation/deallocation are available.
- *  If compiled with MEMTRACE defined, CUnit keeps track of all
- *  system allocations & deallocations.  The memory record can
- *  then be reported using CU_CREATE_MEMORY_REPORT.  Otherwise,
- *  standard system memory allocation is used without tracing.
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_MYMEM_H_SEEN
-#define CUNIT_MYMEM_H_SEEN
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef MEMTRACE
-  void* CU_calloc(size_t nmemb, size_t size, unsigned int uiLine, const char* szFileName);
-  void* CU_malloc(size_t size, unsigned int uiLine, const char* szFileName);
-  void  CU_free(void *ptr, unsigned int uiLine, const char* szFileName);
-  void* CU_realloc(void *ptr, size_t size, unsigned int uiLine, const char* szFileName);
-  CU_EXPORT void CU_dump_memory_usage(const char*);
-
-  /** c-allocate with memory tracking. */
-  #define CU_CALLOC(x, y)         CU_calloc((x), (y), __LINE__, __FILE__)
-  /** m-allocate with memory tracking. */
-  #define CU_MALLOC(x)            CU_malloc((x), __LINE__, __FILE__)
-  /** Free with memory tracking. */
-  #define CU_FREE(x)              CU_free((x), __LINE__, __FILE__)
-  /** Reallocate with memory tracking. */
-  #define CU_REALLOC(x, y)        CU_realloc((x), (y), __LINE__, __FILE__)
-  /** Generate report on tracked memory. */
-  #define CU_CREATE_MEMORY_REPORT(x) CU_dump_memory_usage((x))
-  /** Generate report on tracked memory (old macro). */
-  #define CU_DUMP_MEMORY_USAGE(x) CU_dump_memory_usage((x))
-#else   /* MEMTRACE */
-  /** Standard calloc() if MEMTRACE not defined. */
-  #define CU_CALLOC(x, y)         calloc((x), (y))
-  /** Standard malloc() if MEMTRACE not defined. */
-  #define CU_MALLOC(x)            malloc((x))
-  /** Standard free() if MEMTRACE not defined. */
-  #define CU_FREE(x)              free((x))
-  /** Standard realloc() if MEMTRACE not defined. */
-  #define CU_REALLOC(x, y)        realloc((x), (y))
-  /** No-op if MEMTRACE not defined. */
-  #define CU_CREATE_MEMORY_REPORT(x)
-  /** No-op if MEMTRACE not defined. */
-  #define CU_DUMP_MEMORY_USAGE(x)
-#endif  /* MEMTRACE */
-
-#ifdef CUNIT_BUILD_TESTS
-/** Disable memory allocation for testing purposes. */
-void test_cunit_deactivate_malloc(void);
-/** Enable memory allocation for testing purposes. */
-void test_cunit_activate_malloc(void);
-/** Retrieve number of memory events for a given pointer */
-unsigned int test_cunit_get_n_memevents(void* pLocation);
-/** Retrieve number of allocations for a given pointer */
-unsigned int test_cunit_get_n_allocations(void* pLocation);
-/** Retrieve number of deallocations for a given pointer */
-unsigned int test_cunit_get_n_deallocations(void* pLocation);
-
-void test_cunit_MyMem(void);
-#endif  /* CUNIT_BUILD_TESTS */
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_MYMEM_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestDB.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestDB.h
deleted file mode 100644
index ec07f92..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestDB.h
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains all the Type Definitions and functions declarations
- *  for the CUnit test database maintenance.
- *
- *  Aug 2001      Initial implementation. (AK)
- *
- *  09/Aug/2001   Added Preprocessor conditionals for the file. (AK)
- *
- *  24/aug/2001   Made the linked list from SLL to DLL(doubly linked list). (AK)
- *
- *  31-Aug-2004   Restructured to eliminate global variables error_number, 
- *                g_pTestRegistry; new interface, support for deprecated 
- *                version 1 interface, moved error handling code to 
- *                CUError.[ch], moved test run counts and _TestResult out 
- *                of TestRegistry to TestRun.h. (JDS)
- *
- *  01-Sep-2004   Added jmp_buf to CU_Test. (JDS)
- *
- *  05-Sep-2004   Added internal test interface. (JDS)
- *
- *  15-Apr-2006   Removed constraint that suites/tests be uniquely named.
- *                Added ability to turn individual tests/suites on or off.
- *                Moved doxygen comments for public API here to header. (JDS)
- */
-
-/** @file
- *  Management functions for tests, suites, and the test registry.
- *  Unit testing in CUnit follows the common structure of unit
- *  tests aggregated in suites, which are themselves aggregated
- *  in a test registry.  This module provides functions and
- *  typedef's to support the creation, registration, and manipulation
- *  of test cases, suites, and the registry.
- */
-/** @addtogroup Framework
- *  @{
- */
-
-#ifndef CUNIT_TESTDB_H_SEEN
-#define CUNIT_TESTDB_H_SEEN
-
-#include <setjmp.h>   /* jmp_buf */
-
-#include "CUnit.h"
-#include "CUError.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*=================================================================
- *  Typedefs and Data Structures
- *=================================================================*/
-
-typedef int  (*CU_InitializeFunc)(void);  /**< Signature for suite initialization function. */
-typedef int  (*CU_CleanupFunc)(void);     /**< Signature for suite cleanup function. */
-typedef void (*CU_TestFunc)(void);        /**< Signature for a testing function in a test case. */
-
-/*-----------------------------------------------------------------
- * CU_Test, CU_pTest
- *-----------------------------------------------------------------*/
-/** CUnit test case data type.
- *  CU_Test is a double linked list of unit tests.  Each test has
- *  a name, a callable test function, and a flag for whether the 
- *  test is active and thus executed during a  test run.  A test 
- *  also holds links to the next and previous tests in the list, 
- *  as well as a jmp_buf reference for use in implementing fatal 
- *  assertions.<br /><br />
- *
- *  Generally, the linked list includes tests which are associated 
- *  with each other in a CU_Suite.  As a result, tests are run in 
- *  the order in which they are added to a suite (see CU_add_test()).
- *  <br /><br />
- *
- *  It is recommended that the name of each CU_Test in a suite have
- *  a unique name.  Otherwise, only the first-registered test having 
- *  a given name will be accessible by that name.  There are no 
- *  restrictions on the test function.  This means that the same 
- *  function could, in principle, be called more than once from 
- *  different tests.
- *
- *  @see CU_Suite
- *  @see CU_TestRegistry
- */
-typedef struct CU_Test
-{
-  char*           pName;      /**< Test name. */
-  CU_BOOL         fActive;    /**< Flag for whether test is executed during a run. */
-  CU_TestFunc     pTestFunc;  /**< Pointer to the test function. */
-  jmp_buf*        pJumpBuf;   /**< Jump buffer for setjmp/longjmp test abort mechanism. */
-
-  struct CU_Test* pNext;      /**< Pointer to the next test in linked list. */
-  struct CU_Test* pPrev;      /**< Pointer to the previous test in linked list. */
-
-} CU_Test;
-typedef CU_Test* CU_pTest;    /**< Pointer to a CUnit test case. */
-
-/*-----------------------------------------------------------------
- *  CU_Suite, CU_pSuite
- *-----------------------------------------------------------------*/
-/** CUnit suite data type.
- *  CU_Suite is a linked list of CU_Test containers.  Each suite has 
- *  a name, a count of registered unit tests, and a flag for whether 
- *  the suite is active during test runs. It also holds pointers to 
- *  optional initialization and cleanup functions.  If non-NULL, these 
- *  are called before and after running the suite's tests, respectively.
- *  In addition, the suite holds a pointer to the head of the linked 
- *  list of associated CU_Test objects.  Finally, pointers to the next 
- *  and previous suites in the linked list are maintained.<br /><br />
- *
- *  Generally, the linked list includes suites which are associated with 
- *  each other in a CU_TestRegistry.  As a result, suites are run in the 
- *  order in which they are registered (see CU_add_suite()).<br /><br />
- *
- *  It is recommended that name of each CU_Suite in a test registry have 
- *  a unique name.  Otherwise, only the first-registered suite having a 
- *  given name will be accessible by name.  There are no restrictions on 
- *  the contained tests.  This means that the same CU_Test could, in 
- *  principle, be run more than once fron different suites.
- *
- *  @see CU_Test
- *  @see CU_TestRegistry
- */
-typedef struct CU_Suite
-{
-  char*             pName;            /**< Suite name. */
-  CU_BOOL           fActive;          /**< Flag for whether suite is executed during a run. */
-  CU_pTest          pTest;            /**< Pointer to the 1st test in the suite. */
-  CU_InitializeFunc pInitializeFunc;  /**< Pointer to the suite initialization function. */
-  CU_CleanupFunc    pCleanupFunc;     /**< Pointer to the suite cleanup function. */
-
-  unsigned int      uiNumberOfTests;  /**< Number of tests in the suite. */
-  struct CU_Suite*  pNext;            /**< Pointer to the next suite in linked list. */
-  struct CU_Suite*  pPrev;            /**< Pointer to the previous suite in linked list. */
-
-} CU_Suite;
-typedef CU_Suite* CU_pSuite;          /**< Pointer to a CUnit suite. */
-
-/*-----------------------------------------------------------------
- *  CU_TestRegistry, CU_pTestRegistry
- *-----------------------------------------------------------------*/
-/** CUnit test registry data type.
- *  CU_TestRegisty is the repository for suites containing unit tests.  
- *  The test registry maintains a count of the number of CU_Suite 
- *  objects contained in the registry, as well as a count of the total 
- *  number of CU_Test objects associated with those suites.  It also 
- *  holds a pointer to the head of the linked list of CU_Suite objects.
- *  <br /><br />
- *
- *  With this structure, the user will normally add suites implictly to 
- *  the internal test registry using CU_add_suite(), and then add tests 
- *  to each suite using CU_add_test().  Test runs are then initiated 
- *  using one of the appropriate functions in TestRun.c via one of the 
- *  user interfaces.<br /><br />
- *
- *  Automatic creation and destruction of the internal registry and its 
- *  objects is available using CU_initialize_registry() and 
- *  CU_cleanup_registry(), respectively.  For internal and testing 
- *  purposes, the internal registry can be retrieved and assigned.  
- *  Functions are also provided for creating and destroying independent 
- *  registries.<br /><br />
- *
- *  Note that earlier versions of CUnit also contained a pointer to a 
- *  linked list of CU_FailureRecord objects (termed _TestResults).  
- *  This has been removed from theregistry and relocated to TestRun.c.
- *
- *  @see CU_Test
- *  @see CU_Suite
- *  @see CU_initialize_registry()
- *  @see CU_cleanup_registry()
- *  @see CU_get_registry()
- *  @see CU_set_registry()
- *  @see CU_create_new_registry()
- *  @see CU_destroy_existing_registry()
- */
-typedef struct CU_TestRegistry
-{
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-  /** Union to support v1.1-1 member name. */
-  union {
-    unsigned int uiNumberOfSuites;  /**< Number of suites in the test registry. */
-    unsigned int uiNumberOfGroups;  /**< Deprecated (version 1). @deprecated Use uiNumberOfSuites. */
-  };
-  unsigned int uiNumberOfTests;     /**< Number of tests in the test registry. */
-  /** Union to support v1.1-1 member name. */
-  union {
-    CU_pSuite    pSuite;            /**< Pointer to the 1st suite in the test registry. */
-    CU_pSuite    pGroup;            /**< Deprecated (version 1). @deprecated Use pSuite. */
-  };
-#else
-  unsigned int uiNumberOfSuites;    /**< Number of registered suites in the registry. */
-  unsigned int uiNumberOfTests;     /**< Total number of registered tests in the registry. */
-  CU_pSuite    pSuite;              /**< Pointer to the 1st suite in the test registry. */
-#endif
-} CU_TestRegistry;
-typedef CU_TestRegistry* CU_pTestRegistry;  /**< Pointer to a CUnit test registry. */
-
-/*=================================================================
- *  Public interface functions
- *=================================================================*/
-
-CU_EXPORT 
-CU_ErrorCode CU_initialize_registry(void);
-/**<
- *  Initializes the framework test registry.
- *  Any existing registry is freed, including all stored suites 
- *  and associated tests.  It is not necessary to explicitly call
- *  CU_cleanup_registry() before reinitializing the framework.
- *  The most recent stored test results are also cleared.<br /><br />
- *
- *  <B>This function must not be called during a test run (checked
- *  by assertion)</B>
- *
- *  @return  CUE_NOMEMORY if memory for the new registry cannot 
- *           be allocated, CUE_SUCCESS otherwise.
- *  @see CU_cleanup_registry
- *  @see CU_get_registry
- *  @see CU_set_registry
- *  @see CU_registry_initialized
- */
-
-CU_EXPORT 
-void CU_cleanup_registry(void);
-/**<
- *  Clears the test registry.
- *  The active test registry is freed, including all stored suites 
- *  and associated tests.  The most recent stored test results are 
- *  also cleared.  After calling this function, CUnit suites cannot 
- *  be added until CU_initialize_registry() or CU_set_registry() is 
- *  called.  Further, any pointers to suites or test cases held by 
- *  the user will be invalidated by calling this function.<br /><br />
- *
- *  This function may be called multiple times without generating 
- *  an error condition.  However, <B>this function must not be 
- *  called during a test run (checked by assertion)</B></P>.
- *
- *  @see CU_initialize_registry
- *  @see CU_get_registry
- *  @see CU_set_registry
- */
-
-CU_EXPORT CU_BOOL CU_registry_initialized(void);
-/**<
- *  Checks whether the test registry has been initialized.
- *
- *  @return  CU_TRUE if the registry has been initialized,
- *           CU_FALSE otherwise.
- *  @see CU_initialize_registry
- *  @see CU_cleanup_registry
- */
-
-CU_EXPORT 
-CU_pSuite CU_add_suite(const char *strName, 
-                       CU_InitializeFunc pInit, 
-                       CU_CleanupFunc pClean);
-/**<
- *  Creates a new test suite and adds it to the test registry.
- *  This function creates a new test suite having the specified
- *  name and initialization/cleanup functions and adds it to the
- *  test registry.  The new suite will be active and able to be
- *  executed during a test run.  The test registry must be
- *  initialized before calling this function (checked by assertion).
- *  pInit and pClean may be NULL, in which case no corresponding
- *  initialization of cleanup function will be called when the suite
- *  is run.  strName may be empty ("") but may not be NULL.<br /><br />
- *
- *  The return value is a pointer to the newly-created suite, or 
- *  NULL if there was a problem with the suite creation or addition.  
- *  An error code is also set for the framework. Note that if the 
- *  name specified for the new suite is a duplicate, the suite will 
- *  be created and added but the error code will be set to CUE_DUP_SUITE.  
- *  The duplicate suite will not be accessible by name.<br /><br />
- *
- *  NOTE - the CU_pSuite pointer returned should NOT BE FREED BY
- *  THE USER.  The suite is freed by the CUnit system when
- *  CU_cleanup_registry() is called.  <b>This function must not 
- *  be called during a test run (checked by assertion)</b>. <br /><br />
- *
- *  CU_add_suite() sets the following error codes:
- *  - CUE_SUCCESS if no errors occurred.
- *  - CUE_NOREGISTRY if the registry has not been initialized.
- *  - CUE_NO_SUITENAME if strName is NULL.
- *  - CUE_DUP_SUITE if a suite having strName is already registered.
- *  - CUE_NOMEMORY if a memory allocation failed.
- *
- *  @param strName Name for the new test suite (non-NULL).
- *  @param pInit   Initialization function to call before running suite.
- *  @param pClean  Cleanup function to call after running suite.
- *  @return A pointer to the newly-created suite (NULL if creation failed)
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_suite_active(CU_pSuite pSuite, CU_BOOL fNewActive);
-/**<
- *  Activates or deactivates a suite.
- *  Only activated suites can be executed during a test run.  
- *  By default a suite is active upon creation, but can be deactivated
- *  by passing it along with CU_FALSE to this function.  The suite
- *  can be reactivated by passing it along with CU_TRUE.  The current
- *  value of the active flag is available as pSuite->fActive.  If pSuite 
- *  is NULL then error code CUE_NOSUITE is returned.
- *
- *  @param pSuite     Pointer to the suite to modify (non-NULL).
- *  @param fNewActive If CU_TRUE then the suite will be activated; 
- *                    if CU_FALSE it will be deactivated.
- *  @return Returns CUE_NOSUITE if pSuite is NULL, CUE_SUCCESS if all is well.
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_suite_name(CU_pSuite pSuite, const char *strNewName);
-/**<
- *  Modifies the name of a suite.
- *  This function allows the name associated with a suite to
- *  be changed.  It is not recommended that a suite name be changed,
- *  nor should it be necessary under most circumstances.  However,
- *  this function is provided for those clients who need to change
- *  a suite's name.  The current value of the suite's name 
- *  is available as pSuite->pName.  CUE_SUCCESS is returned if the
- *  function succeeds in changing the name.  CUE_NOSUITE is returned if
- *  pSuite is NULL, and CUE_NO_SUITENAME if strNewName is NULL.
- *
- *  @param pSuite     Pointer to the suite to modify (non-NULL).
- *  @param strNewName Pointer to string containing new suite name (non-NULL).
- *  @return Returns CUE_NOSUITE if pSuite is NULL, CUE_NO_SUITENAME if
- *          strNewName is NULL, and CUE_SUCCESS if all is well.
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_suite_initfunc(CU_pSuite pSuite, CU_InitializeFunc pNewInit);
-/**<
- *  Modifies the initialization function of a suite.
- *  This function allows the initialization function associated with 
- *  a suite to be changed.  This is neither recommended nor should it 
- *  be necessary under most circumstances.  However, this function is 
- *  provided for those clients who need to change the function.  The 
- *  current value of the function is available as pSuite->pInitializeFunc.
- *  CUE_SUCCESS is returned if the function succeeds, or CUE_NOSUITE if
- *  pSuite is NULL.  pNewInit may be NULL, which indicates the suite has
- *  no initialization function.
- *
- *  @param pSuite   Pointer to the suite to modify (non-NULL).
- *  @param pNewInit Pointer to function to use to initialize suite.
- *  @return Returns CUE_NOSUITE if pSuite is NULL, and CUE_SUCCESS if 
- *          all is well.
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_suite_cleanupfunc(CU_pSuite pSuite, CU_CleanupFunc pNewClean);
-/**<
- *  Modifies the cleanup function of a suite.
- *  This function allows the cleanup function associated with a suite to 
- *  be changed.  This is neither recommended nor should it be necessary 
- *  under most circumstances.  However, this function is provided for those 
- *  clients who need to change the function.  The current value of the 
- *  function is available as pSuite->pCleanupFunc.  CUE_SUCCESS is returned 
- *  if the function succeeds, or CUE_NOSUITE if pSuite is NULL.  pNewClean 
- *  may be NULL, which indicates the suite has no cleanup function.
- *
- *  @param pSuite    Pointer to the suite to modify (non-NULL).
- *  @param pNewClean Pointer to function to use to clean up suite.
- *  @return Returns CUE_NOSUITE if pSuite is NULL, and CUE_SUCCESS if 
- *          all is well.
- */
-
-CU_EXPORT 
-CU_pSuite CU_get_suite(const char* strName);
-/**<
- *  Retrieves the suite having the specified name.  
- *  Searches the active test registry and returns a pointer to the 1st 
- *  suite found.  NULL is returned if no suite having the specified name 
- *  is found.  In addition, the framework error state is set to CUE_NOREGISTRY 
- *  if the registry is not initialized or to CUE_NO_SUITENAME if strName is NULL.  
- *  If the return value is NULL and framework error state is CUE_SUCCESS, then 
- *  the search simply failed to find the specified name.  
- *  Use CU_get_suite_at_pos() to retrieve a suite by position rather than name.
- *
- *  @param strName The name of the suite to search for (non-NULL).
- *  @return Returns a pointer to the suite, or NULL if not found or an error occurred.
- *  @see CU_get_suite_at_pos()
- */
-
-CU_EXPORT 
-CU_pSuite CU_get_suite_at_pos(unsigned int pos);
-/**<
- *  Retrieves the suite at the specified position.
- *  Iterates the active test registry and returns a pointer to the suite at 
- *  position pos.  pos is a 1-based index having valid values 
- *  [1 .. CU_get_registry()->uiNumberOfSuites] and corresponds to the order in
- *  which suites were registered.  If pos is invalid or an error occurs, 0 is 
- *  returned.  In addition, the framework error state is set to CUE_NOREGISTRY if 
- *  the registry is not initialized, or CUE_SUCCESS if pos was invalid.  Use 
- *  CU_get_suite() to retrieve a suite by name rather than position.
- *
- *  @param pos The 1-based position of the suite to fetch.
- *  @return Returns a pointer to the suite, or 0 if not found or an error occurred.
- *  @see CU_get_suite()
- */
-
-CU_EXPORT 
-unsigned int CU_get_suite_pos(CU_pSuite pSuite);
-/**<
- *  Looks up the position of the specified suite.
- *  The position is a 1-based index of suites in the active test registry which 
- *  corresponds to the order in which suites were registered.  If pSuite is not 
- *  found or an error occurs, 0 is returned.  In addition, the framework error 
- *  state is set to CUE_NOREGISTRY if the registry is not initialized, or 
- *  CUE_NOSUITE if pSuite is NULL.  The returned position may be used to retrieve 
- *  the suite using CU_get_suite_by_pos().
- *
- *  @param pSuite Pointer to the suite to find (non-NULL).
- *  @return Returns the 1-based position of pSuite in the registry, or NULL if
- *         not found or an error occurred.
- *  @see CU_get_suite_by_pos()
- *  @see CU_get_suite_pos_by_name()
- */
-
-CU_EXPORT 
-unsigned int CU_get_suite_pos_by_name(const char* strName);
-/**<
- *  Looks up the position of the suite having the specified name.
- *  The position is a 1-based index of suites in the active test registry which 
- *  corresponds to the order in which suites were registered.  If no suite has the
- *  specified name or an error occurs, 0 is returned.  In addition, the framework error 
- *  state is set to CUE_NOREGISTRY if the registry is not initialized, or 
- *  CUE_NO_SUITENAME if strName is NULL.  The search ends at the 1st suite found having 
- *  name strName.  The returned position may be used to retrieve the suite using 
- *  CU_get_suite_by_pos().
- *
- *  @param strName Name of the suite to find (non-NULL).
- *  @return Returns the 1-based position of pSuite in the registry, or NULL if
- *          not found or an error occurred.
- *  @see CU_get_suite_by_pos()
- *  @see CU_get_suite_pos_by_name()
- */
-
-CU_EXPORT 
-CU_pTest CU_add_test(CU_pSuite pSuite, const char* strName, CU_TestFunc pTestFunc);
-/**<
- *  This function creates a new test having the specified name 
- *  and function, and adds it to the specified suite.  The new test 
- *  is active and able to be executed during a test run.  At present, 
- *  there is no mechanism for creating a test case independent of a 
- *  suite.  Neither pSuite, strName, nor pTestFunc may be NULL.
- *
- *  The return value is a pointer to the newly-created test, or 
- *  NULL if there was a problem with the test creation or addition.  
- *  An error code is also set for the framework. Note that if the 
- *  name specified for the new test is a duplicate within pSuite, 
- *  the test will be created and added but the error code will be 
- *  set to CUE_DUP_TEST.  The duplicate test will not be accessible 
- *  by name.<br /><br />
- *
- *  NOTE - the CU_pTest pointer returned should NOT BE FREED BY
- *  THE USER.  The test is freed by the CUnit system when
- *  CU_cleanup_registry() is called.  <b>This function must not 
- *  be called during a test run (checked by assertion)</b>. <br /><br />
-
- *  CU_add_test() sets the following error codes:
- *  - CUE_SUCCESS if no errors occurred.
- *  - CUE_NOREGISTRY if the registry has not been initialized.
- *  - CUE_NOSUITE if pSuite is NULL.
- *  - CUE_NO_TESTNAME if strName is NULL.
- *  - CUE_NOTEST if pTestFunc is NULL.
- *  - CUE_DUP_TEST if a test having strName is already registered to pSuite.
- *  - CUE_NOMEMORY if a memory allocation failed.<br /><br />
- *
- *  @param pSuite  Test suite to which to add new test (non-NULL).
- *  @param strName Name for the new test case (non-NULL).
- *  @param pTest   Function to call when running the test (non-NULL).
- *  @return A pointer to the newly-created test (NULL if creation failed)
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_test_active(CU_pTest pTest, CU_BOOL fNewActive);
-/**<
- *  Activates or deactivates a specific test.
- *  Only activated tests can be executed during a test run.  
- *  By default a test is active upon creation, but can be deactvated
- *  by passing it along with CU_FALSE to this function.  The test
- *  can be reactivated by passing it along with CU_TRUE.  
- *  The current value of the active flag is available as pTest->fActive.
- *  If pTest is NULL then error code CUE_NOTEST is returned.  Otherwise
- *  CUE_SUCCESS is returned.
- *
- *  @param pTest      Pointer to the test to modify (non-NULL).
- *  @param fNewActive If CU_TRUE then test will be activated; 
- *                    if CU_FALSE it will be deactivated.
- *  @return Returns CUE_NOTEST if pTest is NULL, CUE_SUCCESS if all is well.
-*/
-
-CU_EXPORT 
-CU_ErrorCode CU_set_test_name(CU_pTest pTest, const char *strNewName);
-/**<
- *  Modifies the name of a test.
- *  This function allows the name associated with a test to
- *  be changed.  It is not recommended that a test name be changed,
- *  nor should it be necessary under most circumstances.  However,
- *  this function is provided for those clients who need to change
- *  a test's name.  The current value of the test's name is
- *  available as pTest->pName.  CUE_SUCCESS is returned if the
- *  function succeeds in changing the name.  CUE_NOTEST is returned if
- *  pTest is NULL, and CUE_NO_TESTNAME if strNewName is NULL.
- *
- *  @param pTest      Pointer to the test to modify (non-NULL).
- *  @param strNewName Pointer to string containing new test name (non-NULL).
- *  @return Returns CUE_NOTEST if pTest is NULL, CUE_NO_TESTNAME if
- *          strNewName is NULL, and CUE_SUCCESS if all is well.
- */
-
-CU_EXPORT 
-CU_ErrorCode CU_set_test_func(CU_pTest pTest, CU_TestFunc pNewFunc);
-/**<
- *  Modifies the test function of a test.
- *  This function allows the test function associated with a test to be 
- *  changed.  This is neither recommended nor should it be necessary under 
- *  most circumstances.  However, this function is provided for those 
- *  clients who need to change the test function.  The current value of 
- *  the test function is available as pTest->pTestFunc.  CUE_SUCCESS is 
- *  returned if the function succeeds, or CUE_NOTEST if either pTest or
- *  pNewFunc is NULL.
- *
- *  @param pTest    Pointer to the test to modify (non-NULL).
- *  @param pNewFunc Pointer to function to use for test function (non-NULL).
- *  @return Returns CUE_NOTEST if pTest or pNewFunc is NULL, and CUE_SUCCESS 
- *          if all is well.
- */
-
-CU_EXPORT 
-CU_pTest CU_get_test(CU_pSuite pSuite, const char *strName);
-/**<
- *  Retrieves the test having the specified name.  
- *  Searches pSuite and returns a pointer to the 1st test found named strName.  
- *  NULL is returned if no test having the specified name is found in pSuite.  
- *  In addition, the framework error state is set as follows:
- *    - CUE_NOREGISTRY if the registry is not initialized
- *    - CUE_NOSUITE if pSuite is NULL
- *    - CUE_NO_TESTNAME if strName is NULL.
- *  
- *  If the return value is NULL and framework error state is CUE_SUCCESS, then 
- *  the search simply failed to find the specified name.  Use CU_get_test_at_pos() 
- *  to retrieve a test by position rather than name.
- *
- *  @param pSuite  Pointer to the suite to search (non-NULL).
- *  @param strName The name of the test to search for (non-NULL).
- *  @return Returns a pointer to the test, or NULL if not found or an error occurred.
- *  @see CU_get_test_at_pos()
- */
-
-CU_EXPORT 
-CU_pTest CU_get_test_at_pos(CU_pSuite pSuite, unsigned int pos);
-/**<
- *  Retrieves the test at the specified position in pSuite.
- *  Iterates the tests registered in pSuite and returns a pointer to the 
- *  test at position pos.  pos is a 1-based index having valid values 
- *  [1 .. pSuite->uiNumberOfTests] and corresponds to the order in
- *  which tests were added to pSuite.  If pos is invalid or an error occurs, 0 is 
- *  returned.  In addition, the framework error state is set as follows: 
- *    - CUE_NOREGISTRY if the registry is not initialized
- *    - CUE_NOSUITE if pSuite is NULL
- *  Use CU_get_test() to retrieve a test by name rather than position.
- *
- *  @param pSuite  Pointer to the suite to search (non-NULL).
- *  @param pos     The 1-based position of the test to fetch.
- *  @return Returns a pointer to the test, or 0 if not found or an error occurred.
- *  @see CU_get_test()
- */
-
-CU_EXPORT 
-unsigned int CU_get_test_pos(CU_pSuite pSuite, CU_pTest pTest);
-/**<
- *  Looks up the position of the specified test in pSuite.
- *  The position is a 1-based index of tests in pSuite which corresponds to the 
- *  order in which tests were added.  If pTest is not found or an error occurs, 
- *  0 is returned.  In addition, the framework error state is set as follows:  
- *    - CUE_NOREGISTRY if the registry is not initialized
- *    - CUE_NOSUITE if pSuite is NULL
- *    - CUE_NOTEST if pTest is NULL
- *
- *  The returned position may be used to retrieve the test using CU_get_test_by_pos().
- *
- *  @param pSuite Pointer to the suite to search (non-NULL).
- *  @param pTest  Pointer to the test to find (non-NULL).
- *  @return Returns the 1-based position of pTest in pSuite, or NULL if
- *         not found or an error occurred.
- *  @see CU_get_test_by_pos()
- *  @see CU_get_test_pos_by_name()
- */
-
-CU_EXPORT 
-unsigned int CU_get_test_pos_by_name(CU_pSuite pSuite, const char *strName);
-/**<
- *  Looks up the position of the test having the specified name in pSuite.
- *  The position is a 1-based index of tests in pSuite which corresponds to the order 
- *  in which tests were added.  If no test has the specified name or an error occurs, 
- *  0 is returned.  In addition, the framework error state is set as follows:
- *    - CUE_NOREGISTRY if the registry is not initialized
- *    - CUE_NOSUITE if pSuite is NULL
- *    - CUE_NO_TESTNAME if strName is NULL
- *  The search ends at the 1st test found having name strName.  The returned position 
- *  may be used to retrieve the suite using CU_get_test_by_pos().
- *
- *  @param pSuite  Pointer to the suite to search (non-NULL).
- *  @param strName Name of the test to find (non-NULL).
- *  @return Returns the 1-based position of pTest in pSuite, or NULL if
- *          not found or an error occurred.
- *  @see CU_get_test_by_pos()
- *  @see CU_get_test_pos_by_name()
- */
-
-#define CU_ADD_TEST(suite, test) (CU_add_test(suite, #test, (CU_TestFunc)test))
-/**< Shortcut macro for adding a test to a suite. */
-
-/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
-/*  This section is based conceptually on code
- *  Copyright (C) 2004  Aurema Pty Ltd.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  Derived from code contributed by K. Cheung and Aurema Pty Ltd. (thanks!)
- *    test_case_t, test_group_t, test_suite_t
- */
-
-/** 
- *  Test case parameters structure.
- *  This data type is provided to assist CUnit users manage collections of 
- *  tests and suites.  It is intended to be used to build arrays of test case 
- *  parameters that can be then be referred to in a CU_suite_info_t variable.
- */
-typedef struct CU_TestInfo {
-	char       *pName;      /**< Test name. */
-	CU_TestFunc pTestFunc;  /**< Test function. */
-} CU_TestInfo;
-typedef CU_TestInfo* CU_pTestInfo;  /**< Pointer to CU_TestInfo type. */
-
-/** 
- *  Suite parameters.
- *  This data type is provided to assist CUnit users manage collections of 
- *  tests and suites.  It is intended to be used to build arrays of suite 
- *  parameters that can be passed to a bulk registration function such as 
- *  CU_register_suite() or CU_register_suites().
- */
-typedef struct CU_SuiteInfo {
-	char             *pName;         /**< Suite name. */
-	CU_InitializeFunc pInitFunc;     /**< Suite initialization function. */
-	CU_CleanupFunc    pCleanupFunc;  /**< Suite cleanup function */
-	CU_TestInfo      *pTests;        /**< Test case array - must be NULL terminated. */
-} CU_SuiteInfo;
-typedef CU_SuiteInfo* CU_pSuiteInfo;  /**< Pointer to CU_SuiteInfo type. */
-
-#define CU_TEST_INFO_NULL { NULL, NULL }
-/**< NULL CU_test_info_t to terminate arrays of tests. */
-#define CU_SUITE_INFO_NULL { NULL, NULL, NULL, NULL }
-/**< NULL CU_suite_info_t to terminate arrays of suites. */
-
-
-CU_EXPORT CU_ErrorCode CU_register_suites(CU_SuiteInfo suite_info[]);
-/**<
- *  Registers the suites in a single CU_SuiteInfo array.
- *  Multiple arrays can be registered using CU_register_nsuites().
- *
- *  @param	suite_info NULL-terminated array of CU_SuiteInfo items to register.
- *  @return A CU_ErrorCode indicating the error status.
- *  @see CU_register_suites()
- */
-CU_EXPORT CU_ErrorCode CU_register_nsuites(int suite_count, ...);
-/**<
- *  Registers multiple suite arrays in CU_SuiteInfo format.
- *  The function accepts a variable number of suite arrays to be registered.  
- *  The number of arrays is indicated by the value of the 1st argument, 
- *  suite_count.  Each suite in each array is registered with the CUnit test 
- *  registry, along with all of the associated tests.
- *
- *  @param	suite_count The number of CU_SuiteInfo* arguments to follow.
- *  @param ...          suite_count number of CU_SuiteInfo* arguments.  NULLs are ignored.
- *  @return A CU_ErrorCode indicating the error status.
- *  @see CU_register_suites()
- */
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-typedef CU_TestInfo test_case_t;    /**< Deprecated (version 1). @deprecated Use CU_TestInfo. */
-typedef CU_SuiteInfo test_group_t;  /**< Deprecated (version 1). @deprecated Use CU_SuiteInfo. */
-
-/** Deprecated (version 1). @deprecated Use CU_SuiteInfo and CU_TestInfo. */
-typedef struct test_suite {
-	char *name;            /**< Suite name.  Currently not used. */
-	test_group_t *groups;  /**< Test groups.  This must be a NULL terminated array. */
-} test_suite_t;
-
-/** Deprecated (version 1). @deprecated Use CU_TEST_INFO_NULL. */
-#define TEST_CASE_NULL { NULL, NULL }
-/** Deprecated (version 1). @deprecated Use CU_TEST_GROUP_NULL. */
-#define TEST_GROUP_NULL { NULL, NULL, NULL, NULL }
-
-/** Deprecated (version 1). @deprecated Use CU_register_suites(). */
-#define test_group_register(tg) CU_register_suites(tg)
-
-/** Deprecated (version 1). @deprecated Use CU_SuiteInfo and CU_register_suites(). */
-CU_EXPORT int test_suite_register(test_suite_t *ts)
-{
-	test_group_t *tg;
-	int error;
-
-	for (tg = ts->groups; tg->pName; tg++)
-		if ((error = CU_register_suites(tg)) != CUE_SUCCESS)
-			return error;
-
-	return CUE_SUCCESS;
-}
-#endif    /* USE_DEPRECATED_CUNIT_NAMES */
-/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-typedef CU_InitializeFunc InitializeFunc; /**< Deprecated (version 1). @deprecated Use CU_InitializeFunc. */
-typedef CU_CleanupFunc CleanupFunc;       /**< Deprecated (version 1). @deprecated Use CU_CleanupFunc. */
-typedef CU_TestFunc TestFunc;             /**< Deprecated (version 1). @deprecated Use CU_TestFunc. */
-
-typedef CU_Test _TestCase;                /**< Deprecated (version 1). @deprecated Use CU_Test. */
-typedef CU_pTest PTestCase;               /**< Deprecated (version 1). @deprecated Use CU_pTest. */
-
-typedef CU_Suite  _TestGroup;             /**< Deprecated (version 1). @deprecated Use CU_Suite. */
-typedef CU_pSuite PTestGroup;             /**< Deprecated (version 1). @deprecated Use CU_pSuite. */
-
-typedef CU_TestRegistry  _TestRegistry;   /**< Deprecated (version 1). @deprecated Use CU_TestRegistry. */
-typedef CU_pTestRegistry PTestRegistry;   /**< Deprecated (version 1). @deprecated Use CU_pTestRegistry. */
-
-/* Public interface functions */
-/** Deprecated (version 1). @deprecated Use CU_initialize_registry(). */
-#define initialize_registry() CU_initialize_registry()
-/** Deprecated (version 1). @deprecated Use CU_cleanup_registry(). */
-#define cleanup_registry() CU_cleanup_registry()
-/** Deprecated (version 1). @deprecated Use CU_add_suite(). */
-#define add_test_group(name, init, clean) CU_add_suite(name, init, clean)
-/** Deprecated (version 1). @deprecated Use CU_add_test(). */
-#define add_test_case(group, name, test) CU_add_test(group, name, test)
-
-/* private internal CUnit testing functions */
-/** Deprecated (version 1). @deprecated Use CU_get_registry(). */
-#define get_registry() CU_get_registry()
-/** Deprecated (version 1). @deprecated Use CU_set_registry(). */
-#define set_registry(reg) CU_set_registry((reg))
-
-/** Deprecated (version 1). @deprecated Use CU_get_suite_by_name(). */
-#define get_group_by_name(group, reg) CU_get_suite_by_name(group, reg)
-/** Deprecated (version 1). @deprecated Use CU_get_test_by_name(). */
-#define get_test_by_name(test, group) CU_get_test_by_name(test, group)
-
-/** Deprecated (version 1). @deprecated Use ADD_TEST_TO_SUITE. */
-#define ADD_TEST_TO_GROUP(group, test) (CU_add_test(group, #test, (CU_TestFunc)test))
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-/*=================================================================
- *  Internal CUnit system functions.  
- *  Should not be routinely called by users.
- *=================================================================*/
-
-CU_EXPORT CU_pTestRegistry CU_get_registry(void);
-/**<
- *  Retrieves a pointer to the current test registry.
- *  Returns NULL if the registry has not been initialized using
- *  CU_initialize_registry().  Directly accessing the registry
- *  should not be necessary for most users.  This function is
- *  provided primarily for internal and testing purposes.
- *
- *  @return A pointer to the current registry (NULL if uninitialized).
- *  @see CU_initialize_registry
- *  @see CU_set_registry
- */
-
-CU_EXPORT CU_pTestRegistry CU_set_registry(CU_pTestRegistry pTestRegistry);
-/**<
- *  Sets the registry to an existing CU_pTestRegistry instance.
- *  A pointer to the original registry is returned.  Note that the
- *  original registry is not freed, and it becomes the caller's
- *  responsibility to do so.  Directly accessing the registry
- *  should not be necessary for most users.  This function is
- *  provided primarily for internal and testing purposes.<br /><br />
- *
- *  <B>This function must not be called during a test run (checked
- *  by assertion)</B>.
- *
- *  @return A pointer to the original registry that was replaced.
- *  @see CU_initialize_registry
- *  @see CU_cleanup_registry
- *  @see CU_get_registry
- */
-
-CU_EXPORT CU_pTestRegistry CU_create_new_registry(void);
-/**< 
- *  Creates and initializes a new test registry.
- *  Returns a pointer to a new, initialized registry (NULL if memory could 
- *  not be allocated).  It is the caller's responsibility to destroy and free 
- *  the new registry (unless it is made the active test registry using
- *  CU_set_registry()).
- */
-
-CU_EXPORT 
-void CU_destroy_existing_registry(CU_pTestRegistry* ppRegistry);
-/**< 
- *  Destroys and frees all memory for an existing test registry.
- *  The active test registry is destroyed by the CUnit system in 
- *  CU_cleanup_registry(), so only call this function on registries created
- *  or held independently of the internal CUnit system.<br /><br />
- *
- *  Once a registry is made the active test registry using CU_set_registry(), 
- *  its destruction will be handled by the framework.  ppRegistry may not be 
- *  NULL (checked by assertion), but *ppRegistry can be NULL (in which case the
- *  function has no effect).  Note that *ppRegistry will be set to NULL on return.
- *
- *  @param ppRegistry Address of a pointer to the registry to destroy (non-NULL).
- */
-
-CU_EXPORT 
-CU_pSuite CU_get_suite_by_name(const char *szSuiteName, CU_pTestRegistry pRegistry);
-/**<
- *  Retrieves a pointer to the suite having the specified name.
- *  Scans the pRegistry and returns a pointer to the first suite located 
- *  having the specified name.  Neither szSuiteName nor pRegistry may be
- *  NULL (checked by assertion).  Clients should normally use CU_get_suite()
- *  instead, which automatically searches the active test registry.
- *
- *  @param szSuiteName The name of the suite to locate (non-NULL).
- *  @param pRegistry   The registry to scan (non-NULL).
- *  @return Pointer to the first suite having the specified name,
- *          NULL if not found.
- *  @see CU_get_suite()
- */
-
-CU_EXPORT 
-CU_pSuite CU_get_suite_by_index(unsigned int index, CU_pTestRegistry pRegistry);
-/**<
- *  Retrieves a pointer to the suite at the specified (1-based) index.
- *  Iterates pRegistry and returns a pointer to the suite located at the 
- *  specified index.  pRegistry may not be NULL (checked by assertion).  
- *  Clients should normally use CU_get_suite_at_pos() instead, which 
- *  automatically searches the active test registry.
- *
- *  @param index     The 1-based index of the suite to find.
- *  @param pRegistry The registry to scan (non-NULL).
- *  @return Pointer to the suite at the specified index, or
- *          NULL if index is invalid.
- *  @see CU_get_suite_at_pos()
- */
-
-CU_EXPORT 
-CU_pTest CU_get_test_by_name(const char* szTestName, CU_pSuite pSuite);
-/**< 
- *  Retrieves a pointer to the test case in pSuite having the specified name.
- *  The first test case in pSuite having the specified name is returned, or
- *  NULL if not found.  Neither szSuiteName nor pSuite may be NULL (checked 
- *  by assertion).  Clients should normally use CU_get_test() instead.
- *
- *  @param szTestName The name of the test case to locate (non-NULL).
- *  @param pSuite     The suite to scan (non-NULL).
- *  @return Pointer to the first test case having the specified name,
- *          NULL if not found.
- *  @see CU_get_test()
- */
-
-CU_EXPORT 
-CU_pTest CU_get_test_by_index(unsigned int index, CU_pSuite pSuite);
-/**<
- *  Retrieves a pointer to the test at the specified (1-based) index.
- *  Iterates pSuite and returns a pointer to the test located at the 
- *  specified index.  pSuite may not be NULL (checked by assertion).  
- *  Clients should normally use CU_get_test_at_pos() instead, which 
- *  automatically searches the active test registry.
- *
- *  @param index     The 1-based index of the test to find.
- *  @param pRegistry The registry to scan (non-NULL).
- *  @return Pointer to the test at the specified index, or
- *          NULL if index is invalid.
- *  @see CU_get_test_at_pos()
- */
-
-#ifdef CUNIT_BUILD_TESTS
-void test_cunit_TestDB(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_TESTDB_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestRun.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestRun.h
deleted file mode 100644
index 51072f4..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/TestRun.h
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains Interface to Run tests.
- *
- *  Aug 2001      Initial implementation. (AK)
- *
- *  09/Aug/2001   Contains generic run tests interface which can be used
- *                for any type of frontend interface framework. (AK)
- *
- *  24/Nov/2001   Added Handler for Group Initialization failure condition. (AK)
- *
- *  05-Aug-2004   New interface.  Since these should be internal functions,
- *                no support for deprecated version 1 names provided now,
- *                eliminated global variables for current test & suite,
- *                moved (renamed) _TestResult here from TestDB.h. (JDS)
- *
- *  05-Sep-2004   Added internal test interface. (JDS)
- *
- *  23-Apr-2006   Moved doxygen comments into header.
- *                Added type marker to CU_FailureRecord.
- *                Added support for tracking inactive suites/tests. (JDS)
- *
- *  08-May-2006   Moved CU_print_run_results() functionality from
- *                console/basic test complete handler.  (JDS)
- *
- *  24-May-2006   Added callbacks for suite start and complete events.
- *                Added tracking/reported of elapsed time.  (JDS)
- */
-
-/** @file
- *  Test run management functions (user interface).
- *  The TestRun module implements functions supporting the running
- *  of tests elements (suites and tests).  This includes functions for
- *  running suites and tests, retrieving the number of tests/suites run,
- *  and managing callbacks during the run process.<br /><br />
- *
- *  The callback mechanism works as follows.  The CUnit runtime system
- *  supports the registering and calling of functions at the start and end
- *  of each test, when all tests are complete, and when a suite
- *  initialialization function returns an error.  This allows clients to
- *  perform actions associated with these events such as output formatting
- *  and reporting.
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_TESTRUN_H_SEEN
-#define CUNIT_TESTRUN_H_SEEN
-
-#include "CUnit.h"
-#include "CUError.h"
-#include "TestDB.h"
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Types of failures occurring during test runs. */
-typedef enum CU_FailureTypes
-{
-  CUF_SuiteInactive = 1,    /**< Inactive suite was run. */
-  CUF_SuiteInitFailed,      /**< Suite initialization function failed. */
-  CUF_SuiteCleanupFailed,   /**< Suite cleanup function failed. */
-  CUF_TestInactive,         /**< Inactive test was run. */
-  CUF_AssertFailed          /**< CUnit assertion failed during test run. */
-} CU_FailureType;           /**< Failure type. */
-
-/* CU_FailureRecord type definition. */
-/** Data type for holding assertion failure information (linked list). */
-typedef struct CU_FailureRecord
-{
-  CU_FailureType  type;           /**< Failure type. */
-  unsigned int    uiLineNumber;   /**< Line number of failure. */
-  char*           strFileName;    /**< Name of file where failure occurred. */
-  char*           strCondition;   /**< Test condition which failed. */
-  CU_pTest        pTest;          /**< Test containing failure. */
-  CU_pSuite       pSuite;         /**< Suite containing test having failure. */
-
-  struct CU_FailureRecord* pNext; /**< Pointer to next record in linked list. */
-  struct CU_FailureRecord* pPrev; /**< Pointer to previous record in linked list. */
-
-} CU_FailureRecord;
-typedef CU_FailureRecord* CU_pFailureRecord;  /**< Pointer to CU_FailureRecord. */
-
-/* CU_RunSummary type definition. */
-/** Data type for holding statistics and assertion failures for a test run. */
-typedef struct CU_RunSummary
-{
-  unsigned int nSuitesRun;        /**< Number of suites completed during run. */
-  unsigned int nSuitesFailed;     /**< Number of suites for which initialization failed. */
-  unsigned int nSuitesInactive;   /**< Number of suites which were inactive. */
-  unsigned int nTestsRun;         /**< Number of tests completed during run. */
-  unsigned int nTestsFailed;      /**< Number of tests containing failed assertions. */
-  unsigned int nTestsInactive;    /**< Number of tests which were inactive (in active suites). */
-  unsigned int nAsserts;          /**< Number of assertions tested during run. */
-  unsigned int nAssertsFailed;    /**< Number of failed assertions. */
-  unsigned int nFailureRecords;   /**< Number of failure records generated. */
-  double       ElapsedTime;       /**< Elapsed time for run in seconds. */
-} CU_RunSummary;
-typedef CU_RunSummary* CU_pRunSummary;  /**< Pointer to CU_RunSummary. */
-
-/*-------------------------------------------------------------------- 
- * Type Definitions for Message Handlers.
- *--------------------------------------------------------------------*/
-typedef void (*CU_SuiteStartMessageHandler)(const CU_pSuite pSuite);
-/**< Message handler called at the start of a suite. pSuite will not be null. */
-
-typedef void (*CU_TestStartMessageHandler)(const CU_pTest pTest, const CU_pSuite pSuite);
-/**< Message handler called at the start of a test.
- *  The parameters are the test and suite being run.  The test run is 
- *  considered in progress when the message handler is called.  
- *  Neither pTest nor pSuite may be null.
- */
-
-typedef void (*CU_TestCompleteMessageHandler)(const CU_pTest pTest, const CU_pSuite pSuite,
-                                              const CU_pFailureRecord pFailure);
-/**< Message handler called at the completion of a test.
- *  The parameters are the test and suite being run, plus a pointer to 
- *  the first failure record applicable to this test.  If the test did 
- *  not have any assertion failures, pFailure will be NULL.  The test run 
- *  is considered in progress when the message handler is called.
- */
-
-typedef void (*CU_SuiteCompleteMessageHandler)(const CU_pSuite pSuite,
-                                               const CU_pFailureRecord pFailure);
-/**< Message handler called at the completion of a suite.
- *  The parameters are suite being run, plus a pointer to the first failure 
- *  record applicable to this suite.  If the suite and it's tests did not 
- *  have any failures, pFailure will be NULL.  The test run is considered 
- *  in progress when the message handler is called.
- */
-
-typedef void (*CU_AllTestsCompleteMessageHandler)(const CU_pFailureRecord pFailure);
-/**< Message handler called at the completion of a test run.
- *  The parameter is a pointer to the linked list holding the failure 
- *  records for the test run.  The test run is considered completed 
- *  when the message handler is called.
- */
-
-typedef void (*CU_SuiteInitFailureMessageHandler)(const CU_pSuite pSuite);
-/**< Message handler called when a suite initializer fails.
- *  The test run is considered in progress when the message handler is called.
- */
-
-typedef void (*CU_SuiteCleanupFailureMessageHandler)(const CU_pSuite pSuite);
-/**< Message handler called when a suite cleanup function fails.
- *  The test run is considered in progress when the message handler is called.
- */
-
-/*-------------------------------------------------------------------- 
- * Get/Set functions for Message Handlers
- *--------------------------------------------------------------------*/
-CU_EXPORT void CU_set_suite_start_handler(CU_SuiteStartMessageHandler pSuiteStartMessage);
-/**< Sets the message handler to call before each suite is run. */
-CU_EXPORT void CU_set_test_start_handler(CU_TestStartMessageHandler pTestStartMessage);
-/**< Sets the message handler to call before each test is run. */
-CU_EXPORT void CU_set_test_complete_handler(CU_TestCompleteMessageHandler pTestCompleteMessage);
-/**< Sets the message handler to call after each test is run. */
-CU_EXPORT void CU_set_suite_complete_handler(CU_SuiteCompleteMessageHandler pSuiteCompleteMessage);
-/**< Sets the message handler to call after each suite is run. */
-CU_EXPORT void CU_set_all_test_complete_handler(CU_AllTestsCompleteMessageHandler pAllTestsCompleteMessage);
-/**< Sets the message handler to call after all tests have been run. */
-CU_EXPORT void CU_set_suite_init_failure_handler(CU_SuiteInitFailureMessageHandler pSuiteInitFailureMessage);
-/**< Sets the message handler to call when a suite initialization function returns an error. */
-CU_EXPORT void CU_set_suite_cleanup_failure_handler(CU_SuiteCleanupFailureMessageHandler pSuiteCleanupFailureMessage);
-/**< Sets the message handler to call when a suite cleanup function returns an error. */
-
-CU_EXPORT CU_SuiteStartMessageHandler          CU_get_suite_start_handler(void);
-/**< Retrieves the message handler called before each suite is run. */
-CU_EXPORT CU_TestStartMessageHandler           CU_get_test_start_handler(void);
-/**< Retrieves the message handler called before each test is run. */
-CU_EXPORT CU_TestCompleteMessageHandler        CU_get_test_complete_handler(void);
-/**< Retrieves the message handler called after each test is run. */
-CU_EXPORT CU_SuiteCompleteMessageHandler       CU_get_suite_complete_handler(void);
-/**< Retrieves the message handler called after each suite is run. */
-CU_EXPORT CU_AllTestsCompleteMessageHandler    CU_get_all_test_complete_handler(void);
-/**< Retrieves the message handler called after all tests are run. */
-CU_EXPORT CU_SuiteInitFailureMessageHandler    CU_get_suite_init_failure_handler(void);
-/**< Retrieves the message handler called when a suite initialization error occurs. */
-CU_EXPORT CU_SuiteCleanupFailureMessageHandler CU_get_suite_cleanup_failure_handler(void);
-/**< Retrieves the message handler called when a suite cleanup error occurs. */
-
-/*-------------------------------------------------------------------- 
- * Functions for running registered tests and suites.
- *--------------------------------------------------------------------*/
-CU_EXPORT CU_ErrorCode CU_run_all_tests(void);
-/**< 
- *  Runs all tests in all suites registered in the test registry.
- *  The suites are run in the order registered in the test registry.
- *  For each suite, it is first checked to make sure it is active.
- *  Any initialization function is then called, the suite is run 
- *  using run_single_suite(), and finally any suite cleanup function 
- *  is called.  If an error condition (other than CUE_NOREGISTRY) 
- *  occurs during the run, the action depends on the current error 
- *  action (see CU_set_error_action()).  An inactive suite is not
- *  considered an error for this function.  Note that the run
- *  statistics (counts of tests, successes, failures) are cleared 
- *  each time this function is run, even if it is unsuccessful.
- *
- *  @return A CU_ErrorCode indicating the first error condition
- *          encountered while running the tests.
- *  @see CU_run_suite() to run the tests in a specific suite.
- *  @see CU_run_test() for run a specific test only.
- */
-
-CU_EXPORT CU_ErrorCode CU_run_suite(CU_pSuite pSuite);
-/**< 
- *  Runs all tests in a specified suite.
- *  The suite need not be registered in the test registry to be 
- *  run.  It does, however, need to have its fActive flag set to 
- *  CU_TRUE.<br /><br />
- *
- *  Any initialization function for the suite is first called,
- *  then the suite is run using run_single_suite(), and any suite 
- *  cleanup function is called.  Note that the run statistics 
- *  (counts of tests, successes, failures) are initialized each 
- *  time this function is called even if it is unsuccessful.  If 
- *  an error condition occurs during the run, the action depends 
- *  on the  current error action (see CU_set_error_action()).
- *
- *  @param pSuite The suite containing the test (non-NULL)
- *  @return A CU_ErrorCode indicating the first error condition
- *          encountered while running the suite.  CU_run_suite()
- *          sets and returns CUE_NOSUITE if pSuite is NULL, or 
- *          CUE_SUITE_INACTIVE if the requested suite is not 
- *          activated.  Other error codes can be set during suite 
- *          initialization or cleanup or during test runs.
- *  @see CU_run_all_tests() to run all suites.
- *  @see CU_run_test() to run a single test in a specific suite.
- */
-
-CU_EXPORT CU_ErrorCode CU_run_test(CU_pSuite pSuite, CU_pTest pTest);
-/**< 
- *  Runs a specific test in a specified suite.
- *  The suite need not be registered in the test registry to be run,
- *  although the test must be registered in the specified suite.
- *  Any initialization function for the suite is first
- *  called, then the test is run using run_single_test(), and
- *  any suite cleanup function is called.  Note that the
- *  run statistics (counts of tests, successes, failures)
- *  will be initialized each time this function is called even
- *  if it is not successful.  Both the suite and test specified
- *  must be active for the test to be run.  The suite is not
- *  considered to be run, although it may be counted as a failed
- *  suite if the intialization or cleanup functions fail.
- *
- *  @param pSuite The suite containing the test (non-NULL)
- *  @param pTest  The test to run (non-NULL)
- *  @return A CU_ErrorCode indicating the first error condition
- *          encountered while running the suite.  CU_run_test()
- *          sets and returns CUE_NOSUITE if pSuite is NULL,
- *          CUE_NOTEST if pTest is NULL, CUE_SUITE_INACTIVE if
- *          pSuite is not active, CUE_TEST_NOT_IN_SUITE
- *          if pTest is not registered in pSuite, and CU_TEST_INACTIVE
- *          if pTest is not active.  Other error codes can be set during 
- *          suite initialization or cleanup or during the test run.
- *  @see CU_run_all_tests() to run all tests/suites.
- *  @see CU_run_suite() to run all tests in a specific suite.
- */
-
-/*-------------------------------------------------------------------- 
- * Functions for setting runtime behavior.
- *--------------------------------------------------------------------*/
-CU_EXPORT void CU_set_fail_on_inactive(CU_BOOL new_inactive);
-/**<
- *  Sets whether an inactive suite or test is treated as a failure.
- *  If CU_TRUE, then failure records will be generated for inactive 
- *  suites or tests encountered during a test run.  The default is 
- *  CU_TRUE so that the client is reminded that the framewrork 
- *  contains inactive suites/tests.  Set to CU_FALSE to turn off 
- *  this behavior.
- *
- *  @param new_inactive New setting for whether to treat inactive
- *                      suites and tests as failures during a test 
- *                      run (CU_TRUE) or not (CU_FALSE).
- *  @see CU_get_fail_on_failure()
- */
- 
-CU_EXPORT CU_BOOL CU_get_fail_on_inactive(void);
-/**<
- *  Retrieves the current setting for whether inactive suites/tests 
- *  are treated as failures.  If CU_TRUE then failure records will 
- *  be generated for inactive suites encountered during a test run.
- *
- *  @return CU_TRUE if inactive suites/tests are failures, CU_FALSE if not.
- *  @see CU_set_fail_on_inactive()
- */
-
-/*-------------------------------------------------------------------- 
- * Functions for getting information about the previous test run. 
- *--------------------------------------------------------------------*/
-CU_EXPORT unsigned int CU_get_number_of_suites_run(void);
-/**< Retrieves the number of suites completed during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_suites_failed(void);
-/**< Retrieves the number of suites which failed to initialize during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_suites_inactive(void);
-/**< Retrieves the number of inactive suites found during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_tests_run(void);
-/**< Retrieves the number of tests completed during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_tests_failed(void);
-/**< Retrieves the number of tests containing failed assertions during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_tests_inactive(void);
-/**< Retrieves the number of inactive tests found during the previous run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_asserts(void);
-/**< Retrieves the number of assertions processed during the last run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_successes(void);
-/**< Retrieves the number of successful assertions during the last run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_failures(void);
-/**< Retrieves the number of failed assertions during the last run (reset each run). */
-CU_EXPORT unsigned int CU_get_number_of_failure_records(void);
-/**< 
- *  Retrieves the number failure records created during the previous run (reset each run).  
- *  Note that this may be more than the number of failed assertions, since failure 
- *  records may also be created for failed suite initialization and cleanup.
- */
-CU_EXPORT double CU_get_elapsed_time(void);
-/**< 
- *  Retrieves the elapsed time for the last run in seconds (reset each run).
- *  This function will calculate the current elapsed time if the test run has not
- *  yet completed.  This is in contrast to the run summary returned by
- *  CU_get_run_summary(), for which the elapsed time is not updated until the
- *  end of the run.
- */
-CU_EXPORT CU_pFailureRecord CU_get_failure_list(void);
-/**< 
- *  Retrieves the head of the linked list of failures which occurred during the 
- *  last run (reset each run).  Note that the pointer returned is invalidated 
- *  when the client initiates a run using CU_run_all_tests(), CU_run_suite(), 
- *  or CU_run_test().
- */
-CU_EXPORT CU_pRunSummary CU_get_run_summary(void);
-/**< 
- *  Retrieves the entire run summary for the last test run (reset each run).
- *  The run counts and stats contained in the run summary are updated 
- *  throughout a test run.  Note, however, that the elapsed time is not 
- *  updated until after all suites/tests are run but before the "all tests 
- *  complete"  message handler is called (if any).  To get the elapsed 
- *  time during a test run, use CU_get_elapsed_time() instead.
- */
-
-CU_EXPORT char * CU_get_run_results_string(void);
-/**<
- *  Creates a string and fills it with a summary of the current run results.
- *  The run summary presents data for the suites, tests, and assertions 
- *  encountered during the run, as well as the elapsed time.  The data 
- *  presented include the number of registered, run, passed, failed, and 
- *  inactive entities for each, as well as the elapsed time.  This function 
- *  can be called at any time, although the test registry must have been 
- *  initialized (checked by assertion).  The returned string is owned by 
- *  the caller and should be deallocated using CU_FREE().  NULL is returned 
- *  if there is an error allocating the new string.
- *
- *  @return A new string containing the run summary (owned by caller).
- */
- 
-CU_EXPORT void CU_print_run_results(FILE *file);
-/**<
- *  Prints a summary of the current run results to file.
- *  The run summary is the same as returned by CU_get_run_results_string().
- *  Note that no newlines are printed before or after the report, so any
- *  positioning must be performed before/after calling this function.  The
- *  report itself extends over several lines broken by '\n' characters.
- *  file may not be NULL (checked by assertion).
- *
- *  @param file Pointer to stream to receive the printed summary (non-NULL).
- */
-
-/*-------------------------------------------------------------------- 
- * Functions for internal & testing use.
- *--------------------------------------------------------------------*/
-CU_EXPORT CU_pSuite CU_get_current_suite(void);
-/**< Retrieves a pointer to the currently-running suite (NULL if none). */
-CU_EXPORT CU_pTest  CU_get_current_test(void);
-/**< Retrievea a pointer to the currently-running test (NULL if none). */
-CU_EXPORT CU_BOOL   CU_is_test_running(void);
-/**< Returns <CODE>CU_TRUE</CODE> if a test run is in progress,
- *  <CODE>CU_TRUE</CODE> otherwise.
- */
-
-CU_EXPORT void      CU_clear_previous_results(void);
-/**< 
- *  Initializes the run summary information stored from the previous test run.  
- *  Resets the run counts to zero, and frees any memory associated with 
- *  failure records.  Calling this function multiple times, while inefficient, 
- *  will not cause an error condition.
- *  @see clear_previous_results()
- */
-
-CU_EXPORT CU_BOOL CU_assertImplementation(CU_BOOL bValue,
-                                          unsigned int uiLine,
-                                          const char *strCondition,
-                                          const char *strFile,
-                                          const char *strFunction,
-                                          CU_BOOL bFatal);
-/**< 
- *  Assertion implementation function.
- *  All CUnit assertions reduce to a call to this function.  It should only be
- *  called during an active test run (checked by assertion).  This means that CUnit
- *  assertions should only be used in registered test functions during a test run.
- *
- *  @param bValue        Value of the assertion (CU_TRUE or CU_FALSE).
- *  @param uiLine        Line number of failed test statement.
- *  @param strCondition  String containing logical test that failed.
- *  @param strFile       Source file where test statement failed.
- *  @param strFunction   Function where test statement failed.
- *  @param bFatal        CU_TRUE to abort test (via longjmp()), CU_FALSE to continue test.
- *  @return As a convenience, returns the value of the assertion (i.e. bValue).
- */
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-typedef CU_FailureRecord  _TestResult;  /**< @deprecated Use CU_FailureRecord. */
-typedef CU_pFailureRecord PTestResult;  /**< @deprecated Use CU_pFailureRecord. */
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#ifdef CUNIT_BUILD_TESTS
-void test_cunit_TestRun(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif  /*  CUNIT_TESTRUN_H_SEEN  */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Util.h b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Util.h
deleted file mode 100644
index c03085b..0000000
--- a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/include/CUnit/Util.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- *  CUnit - A Unit testing framework library for C.
- *  Copyright (C) 2001       Anil Kumar
- *  Copyright (C) 2004-2006  Anil Kumar, Jerry St.Clair
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- *  Contains Type Definitions for some generic functions used across
- *  CUnit project files.
- *
- *  13/Oct/2001   Moved some of the generic functions declarations from
- *                other files to this one so as to use the functions 
- *                consitently. This file is not included in the distribution 
- *                headers because it is used internally by CUnit. (AK)
- *
- *  20-Jul-2004   New interface, support for deprecated version 1 names. (JDS)
- *
- *  5-Sep-2004    Added internal test interface. (JDS)
- *
- *  17-Apr-2006   Added CU_translated_strlen() and CU_number_width().
- *                Removed CUNIT_MAX_STRING_LENGTH - dangerous since not enforced.
- *                Fixed off-by-1 error in CU_translate_special_characters(), 
- *                modifying implementation & results in some cases.  User can 
- *                now tell if conversion failed. (JDS)
- */
-
-/** @file
- *  Utility functions (user interface).
- */
-/** @addtogroup Framework
- * @{
- */
-
-#ifndef CUNIT_UTIL_H_SEEN
-#define CUNIT_UTIL_H_SEEN
-
-#include "CUnit.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define CUNIT_MAX_ENTITY_LEN 5
-/**< Maximum number of characters in a translated xml entity. */
-
-CU_EXPORT size_t CU_translate_special_characters(const char *szSrc, char *szDest, size_t maxlen);
-/**< 
- *  Converts special characters in szSrc to xml entity codes and stores 
- *  result in szDest.  Currently conversion of '&', '<', and '>' is supported.
- *  Note that conversion to entities increases the length of the converted 
- *  string.  The greatest conversion size increase would be a string 
- *  consisting entirely of entity characters of converted length 
- *  CUNIT_MAX_ENTITY_LEN.  Neither szSrc nor szDest may be NULL 
- *  (checked by assertion).<br /><br />
- *
- *  maxlen gives the maximum number of characters in the translated string.
- *  If szDest does not have enough room to hold the converted string, the
- *  return value will be zero and szDest will contain an empty string.
- *  If this occurs, the remaining characters in szDest may be overwritten
- *  in an unspecified manner.  It is the caller's responsibility to make 
- *  sure there is sufficient room in szDest to hold the converted string.  
- *  CU_translated_strlen() may be used to calculate the length of buffer 
- *  required (remember to add 1 for the terminating \0).  
- *
- *  @param szSrc  Source string to convert (non-NULL).
- *  @param szDest Location to hold the converted string (non-NULL).
- *  @param maxlen Maximum number of characters szDest can hold.
- *  @return   The number of special characters converted (always 0 if
- *            szDest did not have enough room to hold converted string).
- */
-
-CU_EXPORT size_t CU_translated_strlen(const char *szSrc);
-/**< 
- *  Calculates the length of a translated string.
- *  This function calculates the buffer length required to hold a string
- *  after processing with CU_translate_special_characters().  The returned
- *  length does not include space for the terminating '\0' character.  
- *  szSrc may not be NULL (checked by assertion).
- *
- *  @param szSrc  Source string to analyze (non-NULL).
- *  @return The number of characters szSrc will expand to when converted.
- */
-
-CU_EXPORT int CU_compare_strings(const char *szSrc, const char *szDest);
-/**<
- *  Case-insensitive string comparison.  Neither string pointer
- *  can be NULL (checked by assertion).
- *
- *  @param szSrc  1st string to compare (non-NULL).
- *  @param szDest 2nd string to compare (non-NULL).
- *  @return  0 if the strings are equal, non-zero otherwise.
- */
-
-CU_EXPORT void CU_trim_left(char *szString);
-/**<
- *  Trims leading whitespace from the specified string.
- *  @param szString  The string to trim.
- */
-
-CU_EXPORT void CU_trim_right(char *szString);
-/**< 
- *  Trims trailing whitespace from the specified string.
- *  @param szString  The string to trim.
- */
-
-CU_EXPORT void CU_trim(char *szString);
-/**< 
- *  Trims leading and trailing whitespace from the specified string.
- *  @param szString  The string to trim.
- */
-
-CU_EXPORT size_t CU_number_width(int number);
-/**< 
- *  Calulates the number of places required to display 
- *  number in decimal.
- */
-
-#ifdef CUNIT_BUILD_TESTS
-void test_cunit_Util(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef USE_DEPRECATED_CUNIT_NAMES
-#define CUNIT_MAX_STRING_LENGTH	1024
-/**< Maximum string length. */
-#define translate_special_characters(src, dest, len) CU_translate_special_characters(src, dest, len)
-/**< Deprecated (version 1). @deprecated Use CU_translate_special_characters(). */
-#define compare_strings(src, dest) CU_compare_strings(src, dest)
-/**< Deprecated (version 1). @deprecated Use CU_compare_strings(). */
-
-#define trim_left(str) CU_trim_left(str)
-/**< Deprecated (version 1). @deprecated Use CU_trim_left(). */
-#define trim_right(str) CU_trim_right(str)
-/**< Deprecated (version 1). @deprecated Use CU_trim_right(). */
-#define trim(str) CU_trim(str)
-/**< Deprecated (version 1). @deprecated Use CU_trim(). */
-
-#endif  /* USE_DEPRECATED_CUNIT_NAMES */
-
-#endif /* CUNIT_UTIL_H_SEEN */
-/** @} */
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/cygwin/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/cygwin/cunit.lib
deleted file mode 100755
index 047d5d3..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/cygwin/cunit.lib and /dev/null differ
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/linux/libcunit.a b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/linux/libcunit.a
deleted file mode 100644
index 8cc73ef..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/linux/libcunit.a and /dev/null differ
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/mingw/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/mingw/cunit.lib
deleted file mode 100755
index 411d432..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/mingw/cunit.lib and /dev/null differ
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/osx/libcunit.a b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/osx/libcunit.a
deleted file mode 100644
index 16a84bb..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/osx/libcunit.a and /dev/null differ
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2010/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2010/cunit.lib
deleted file mode 100644
index b4cda57..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2010/cunit.lib and /dev/null differ
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2013/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2013/cunit.lib
deleted file mode 100755
index a5c8b36..0000000
Binary files a/subprojects/docs/src/samples/native-binaries/cunit/lib/cunit/2.1-2/lib/vs2013/cunit.lib and /dev/null differ
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Automated.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Automated.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Automated.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Automated.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Basic.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Basic.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Basic.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Basic.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUError.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUError.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUError.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUError.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUnit.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUnit.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUnit.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUnit.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUnit_intl.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUnit_intl.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/CUnit_intl.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/CUnit_intl.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Console.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Console.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Console.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Console.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/MyMem.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/MyMem.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/MyMem.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/MyMem.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/TestDB.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/TestDB.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/TestDB.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/TestDB.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/TestRun.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/TestRun.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/TestRun.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/TestRun.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Util.h b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Util.h
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/include/CUnit/Util.h
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/include/CUnit/Util.h
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/cygwin/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/cygwin/cunit.lib
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/cygwin/cunit.lib
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/cygwin/cunit.lib
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/linux/libcunit.a b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/linux/libcunit.a
new file mode 100644
index 0000000..60f760d
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/linux/libcunit.a differ
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/mingw/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/mingw/cunit.lib
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/mingw/cunit.lib
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/mingw/cunit.lib
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/osx/libcunit.a b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/osx/libcunit.a
new file mode 100644
index 0000000..cdedee8
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/osx/libcunit.a differ
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/vs2010/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/vs2010/cunit.lib
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/vs2010/cunit.lib
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/vs2010/cunit.lib
diff --git a/subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/vs2013/cunit.lib b/subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/vs2013/cunit.lib
similarity index 100%
rename from subprojects/cpp/src/integTest/resources/org/gradle/nativebinaries/language/cpp/CUnitIntegrationTest/shared/libs/cunit/2.1-2/lib/vs2013/cunit.lib
rename to subprojects/docs/src/samples/native-binaries/cunit/libs/cunit/2.1-2/lib/vs2013/cunit.lib
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/suite_operators.c b/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/suite_operators.c
similarity index 100%
rename from subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/suite_operators.c
rename to subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/suite_operators.c
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/test_minus.c b/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/test_minus.c
similarity index 100%
rename from subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/test_minus.c
rename to subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/test_minus.c
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c b/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/test_plus.c
similarity index 100%
rename from subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/cunit/test_plus.c
rename to subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/c/test_plus.c
diff --git a/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/headers/test_operators.h b/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/headers/test_operators.h
index 29b7dff..9c5a90c 100644
--- a/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/headers/test_operators.h
+++ b/subprojects/docs/src/samples/native-binaries/cunit/src/operatorsTest/headers/test_operators.h
@@ -1,2 +1,2 @@
-void test_plus();
-void test_minus();
\ No newline at end of file
+void test_plus(void);
+void test_minus(void);
diff --git a/subprojects/docs/src/samples/native-binaries/custom-layout/build.gradle b/subprojects/docs/src/samples/native-binaries/custom-layout/build.gradle
index 4b07368..40a41f9 100644
--- a/subprojects/docs/src/samples/native-binaries/custom-layout/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/custom-layout/build.gradle
@@ -17,46 +17,42 @@
 apply plugin: 'cpp'
 apply plugin: 'c'
 
-libraries {
-    hello {}
-}
-
 // START SNIPPET binary-library
-executables {
-    main {
-        binaries.all {
-            // Each executable binary produced uses the 'hello' static library binary
-            lib libraries.hello.static
+model {
+    components {
+        hello(NativeLibrarySpec) {
+// START SNIPPET c-sources
+            sources {
+                c {
+                    source {
+                        srcDir "src/source"
+                        include "**/*.c"
+                    }
+                    exportedHeaders {
+                        srcDir "src/include"
+                    }
+                }
+            }
+// END SNIPPET c-sources
         }
-    }
-}
-// END SNIPPET binary-library
-
+        main(NativeExecutableSpec) {
 // START SNIPPET cpp-sources
-sources {
-    main {
-        cpp {
-            source {
-                srcDir "src/source"
-                include "**/*.cpp"
+            sources {
+                cpp {
+                    source {
+                        srcDir "src/source"
+                        include "**/*.cpp"
+                    }
+                }
             }
-        }
-    }
-}
 // END SNIPPET cpp-sources
-// START SNIPPET c-sources
-sources {
-    hello {
-        c {
-            source {
-                srcDir "src/source"
-                include "**/*.c"
-            }
-            exportedHeaders {
-                srcDir "src/include"
+            binaries.all {
+                // Each executable binary produced uses the 'hello' static library binary
+                lib library: 'hello', linkage: 'static'
             }
         }
     }
 }
-// END SNIPPET c-sources
+// END SNIPPET binary-library
+
 
diff --git a/subprojects/docs/src/samples/native-binaries/custom-layout/src/include/hello.h b/subprojects/docs/src/samples/native-binaries/custom-layout/src/include/hello.h
index 51b48fd..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/custom-layout/src/include/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/custom-layout/src/include/hello.h
@@ -1,4 +1,4 @@
-#ifdef DLL_EXPORT
+#if defined(_WIN32) && defined(DLL_EXPORT)
 #define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
diff --git a/subprojects/docs/src/samples/native-binaries/flavors/build.gradle b/subprojects/docs/src/samples/native-binaries/flavors/build.gradle
index 39328db..f75e68b 100644
--- a/subprojects/docs/src/samples/native-binaries/flavors/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/flavors/build.gradle
@@ -1,37 +1,32 @@
 apply plugin: 'cpp'
 
-sources {
-    exe {}
-    lib {}
-}
 // START SNIPPET flavors
 model {
     flavors {
         english
         french
     }
-}
-
-libraries {
-    hello {
-        binaries.all {
-            if (flavor == flavors.french) {
-                cppCompiler.define "FRENCH"
+    components {
+        hello(NativeLibrarySpec) {
+            binaries.all {
+                if (flavor == flavors.french) {
+                    cppCompiler.define "FRENCH"
+                }
             }
         }
-        source sources.lib
     }
 }
 // END SNIPPET flavors
-binaries.withType(SharedLibraryBinary) {
+binaries.withType(SharedLibraryBinarySpec) {
     cppCompiler.define "DLL_EXPORT"
 }
 
-executables {
-    main {
-        source sources.exe
-        binaries.all {
-            lib libraries.hello
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib library: 'hello'
+            }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/samples/native-binaries/flavors/src/lib/cpp/hello.cpp b/subprojects/docs/src/samples/native-binaries/flavors/src/hello/cpp/hello.cpp
similarity index 100%
rename from subprojects/docs/src/samples/native-binaries/flavors/src/lib/cpp/hello.cpp
rename to subprojects/docs/src/samples/native-binaries/flavors/src/hello/cpp/hello.cpp
diff --git a/subprojects/docs/src/samples/native-binaries/flavors/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/flavors/src/hello/headers/hello.h
new file mode 100755
index 0000000..da8bebf
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/flavors/src/hello/headers/hello.h
@@ -0,0 +1,7 @@
+#if defined(_WIN32) && defined(DLL_EXPORT)
+#define LIB_FUNC __declspec(dllexport)
+#else
+#define LIB_FUNC
+#endif
+
+void LIB_FUNC hello();
diff --git a/subprojects/docs/src/samples/native-binaries/flavors/src/lib/headers/hello.h b/subprojects/docs/src/samples/native-binaries/flavors/src/lib/headers/hello.h
deleted file mode 100755
index 79b608b..0000000
--- a/subprojects/docs/src/samples/native-binaries/flavors/src/lib/headers/hello.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifdef _WIN32
-#ifdef DLL_EXPORT
-#define LIB_FUNC __declspec(dllexport)
-#endif
-#endif
-#ifndef LIB_FUNC
-#define LIB_FUNC
-#endif
-
-void LIB_FUNC hello();
diff --git a/subprojects/docs/src/samples/native-binaries/flavors/src/exe/cpp/main.cpp b/subprojects/docs/src/samples/native-binaries/flavors/src/main/cpp/main.cpp
similarity index 100%
rename from subprojects/docs/src/samples/native-binaries/flavors/src/exe/cpp/main.cpp
rename to subprojects/docs/src/samples/native-binaries/flavors/src/main/cpp/main.cpp
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/README.md b/subprojects/docs/src/samples/native-binaries/google-test/README.md
new file mode 100644
index 0000000..cb6a4bd
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/README.md
@@ -0,0 +1,7 @@
+## GoogleTest Sample Limitations
+
+Currently, the Gradle model for Platform does not allow us to differentiate between different c-runtime, ABI or other binary variants.
+This means that it is not possible to differentiate between a prebuilt library binary compatible with VS2010 vs VS2013.
+
+As such, this sample will only work without modification on Windows with Visual Studio 2010. Uncomment the relevant line in the
+build script to enable building with Visual Studio 2013, Cygwin or MinGW.
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/build.gradle b/subprojects/docs/src/samples/native-binaries/google-test/build.gradle
new file mode 100644
index 0000000..6622836
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/build.gradle
@@ -0,0 +1,59 @@
+// START SNIPPET complete-example
+apply plugin: "cpp"
+apply plugin: "google-test"
+
+model {
+    flavors {
+        passing
+        failing
+    }
+    platforms {
+        x86 {
+            architecture "x86"
+        }
+    }
+    repositories {
+        libs(PrebuiltLibraries) {
+            googleTest {
+                headers.srcDir "libs/googleTest/1.7.0/include"
+                binaries.withType(StaticLibraryBinary) {
+                    staticLibraryFile =
+                        file("libs/googleTest/1.7.0/lib/" +
+                             findGoogleTestCoreLibForPlatform(targetPlatform))
+                }
+            }
+        }
+    }
+    components {
+        operators(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+}
+// START SNIPPET configure-test-binary
+binaries.withType(GoogleTestTestSuiteBinarySpec) {
+    lib library: "googleTest", linkage: "static"
+
+    if (flavor == flavors.failing) {
+        cppCompiler.define "PLUS_BROKEN"
+    }
+}
+// END SNIPPET configure-test-binary
+// END SNIPPET complete-example
+
+tasks.withType(RunTestExecutable) {
+    args "--gtest_output=xml:test_detail.xml"
+}
+
+def findGoogleTestCoreLibForPlatform(Platform platform) {
+    if (platform.operatingSystem.windows) {
+        return "vs2013/gtest.lib"
+//        return "vs2013/gtest-core.lib"
+//        return "cygwin/gtest-core.lib"
+//        return "mingw/gtest-core.lib"
+    } else if (platform.operatingSystem.macOsX) {
+        return "osx/libgtest.a"
+    } else {
+        return "linux/libgtest.a"
+    }
+}
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-death-test.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-death-test.h
new file mode 100644
index 0000000..957a69c
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-death-test.h
@@ -0,0 +1,294 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for death tests.  It is
+// #included by gtest.h so a user doesn't need to include this
+// directly.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+
+#include "gtest/internal/gtest-death-test-internal.h"
+
+namespace testing {
+
+// This flag controls the style of death tests.  Valid values are "threadsafe",
+// meaning that the death test child process will re-execute the test binary
+// from the start, running only a single death test, or "fast",
+// meaning that the child process will execute the test logic immediately
+// after forking.
+GTEST_DECLARE_string_(death_test_style);
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process.  Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests.  IMPORTANT: This is an internal utility.  Using it may break the
+// implementation of death tests.  User code MUST NOT use it.
+GTEST_API_ bool InDeathTestChild();
+
+}  // namespace internal
+
+// The following macros are useful for writing death tests.
+
+// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
+// executed:
+//
+//   1. It generates a warning if there is more than one active
+//   thread.  This is because it's safe to fork() or clone() only
+//   when there is a single thread.
+//
+//   2. The parent process clone()s a sub-process and runs the death
+//   test in it; the sub-process exits with code 0 at the end of the
+//   death test, if it hasn't exited already.
+//
+//   3. The parent process waits for the sub-process to terminate.
+//
+//   4. The parent process checks the exit code and error message of
+//   the sub-process.
+//
+// Examples:
+//
+//   ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
+//   for (int i = 0; i < 5; i++) {
+//     EXPECT_DEATH(server.ProcessRequest(i),
+//                  "Invalid request .* in ProcessRequest()")
+//                  << "Failed to die on request " << i;
+//   }
+//
+//   ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
+//
+//   bool KilledBySIGHUP(int exit_code) {
+//     return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
+//   }
+//
+//   ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
+//
+// On the regular expressions used in death tests:
+//
+//   On POSIX-compliant systems (*nix), we use the <regex.h> library,
+//   which uses the POSIX extended regex syntax.
+//
+//   On other platforms (e.g. Windows), we only support a simple regex
+//   syntax implemented as part of Google Test.  This limited
+//   implementation should be enough most of the time when writing
+//   death tests; though it lacks many features you can find in PCRE
+//   or POSIX extended regex syntax.  For example, we don't support
+//   union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
+//   repetition count ("x{5,7}"), among others.
+//
+//   Below is the syntax that we do support.  We chose it to be a
+//   subset of both PCRE and POSIX extended regex, so it's easy to
+//   learn wherever you come from.  In the following: 'A' denotes a
+//   literal character, period (.), or a single \\ escape sequence;
+//   'x' and 'y' denote regular expressions; 'm' and 'n' are for
+//   natural numbers.
+//
+//     c     matches any literal character c
+//     \\d   matches any decimal digit
+//     \\D   matches any character that's not a decimal digit
+//     \\f   matches \f
+//     \\n   matches \n
+//     \\r   matches \r
+//     \\s   matches any ASCII whitespace, including \n
+//     \\S   matches any character that's not a whitespace
+//     \\t   matches \t
+//     \\v   matches \v
+//     \\w   matches any letter, _, or decimal digit
+//     \\W   matches any character that \\w doesn't match
+//     \\c   matches any literal character c, which must be a punctuation
+//     .     matches any single character except \n
+//     A?    matches 0 or 1 occurrences of A
+//     A*    matches 0 or many occurrences of A
+//     A+    matches 1 or many occurrences of A
+//     ^     matches the beginning of a string (not that of each line)
+//     $     matches the end of a string (not that of each line)
+//     xy    matches x followed by y
+//
+//   If you accidentally use PCRE or POSIX extended regex features
+//   not implemented by us, you will get a run-time failure.  In that
+//   case, please try to rewrite your regular expression within the
+//   above syntax.
+//
+//   This implementation is *not* meant to be as highly tuned or robust
+//   as a compiled regex library, but should perform well enough for a
+//   death test, which already incurs significant overhead by launching
+//   a child process.
+//
+// Known caveats:
+//
+//   A "threadsafe" style death test obtains the path to the test
+//   program from argv[0] and re-executes it in the sub-process.  For
+//   simplicity, the current implementation doesn't search the PATH
+//   when launching the sub-process.  This means that the user must
+//   invoke the test program via a path that contains at least one
+//   path separator (e.g. path/to/foo_test and
+//   /absolute/path/to/bar_test are fine, but foo_test is not).  This
+//   is rarely a problem as people usually don't put the test binary
+//   directory in PATH.
+//
+// TODO(wan at google.com): make thread-safe death tests search the PATH.
+
+// Asserts that a given statement causes the program to exit, with an
+// integer exit status that satisfies predicate, and emitting error output
+// that matches regex.
+# define ASSERT_EXIT(statement, predicate, regex) \
+    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
+
+// Like ASSERT_EXIT, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_EXIT(statement, predicate, regex) \
+    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
+
+// Asserts that a given statement causes the program to exit, either by
+// explicitly exiting with a nonzero exit code or being killed by a
+// signal, and emitting error output that matches regex.
+# define ASSERT_DEATH(statement, regex) \
+    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Like ASSERT_DEATH, but continues on to successive tests in the
+// test case, if any:
+# define EXPECT_DEATH(statement, regex) \
+    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+
+// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
+
+// Tests that an exit code describes a normal exit with a given exit code.
+class GTEST_API_ ExitedWithCode {
+ public:
+  explicit ExitedWithCode(int exit_code);
+  bool operator()(int exit_status) const;
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ExitedWithCode& other);
+
+  const int exit_code_;
+};
+
+# if !GTEST_OS_WINDOWS
+// Tests that an exit code describes an exit due to termination by a
+// given signal.
+class GTEST_API_ KilledBySignal {
+ public:
+  explicit KilledBySignal(int signum);
+  bool operator()(int exit_status) const;
+ private:
+  const int signum_;
+};
+# endif  // !GTEST_OS_WINDOWS
+
+// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
+// The death testing framework causes this to have interesting semantics,
+// since the sideeffects of the call are only visible in opt mode, and not
+// in debug mode.
+//
+// In practice, this can be used to test functions that utilize the
+// LOG(DFATAL) macro using the following style:
+//
+// int DieInDebugOr12(int* sideeffect) {
+//   if (sideeffect) {
+//     *sideeffect = 12;
+//   }
+//   LOG(DFATAL) << "death";
+//   return 12;
+// }
+//
+// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
+//   int sideeffect = 0;
+//   // Only asserts in dbg.
+//   EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
+//
+// #ifdef NDEBUG
+//   // opt-mode has sideeffect visible.
+//   EXPECT_EQ(12, sideeffect);
+// #else
+//   // dbg-mode no visible sideeffect.
+//   EXPECT_EQ(0, sideeffect);
+// #endif
+// }
+//
+// This will assert that DieInDebugReturn12InOpt() crashes in debug
+// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
+// appropriate fallback value (12 in this case) in opt mode. If you
+// need to test that a function has appropriate side-effects in opt
+// mode, include assertions against the side-effects.  A general
+// pattern for this is:
+//
+// EXPECT_DEBUG_DEATH({
+//   // Side-effects here will have an effect after this statement in
+//   // opt mode, but none in debug mode.
+//   EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
+// }, "death");
+//
+# ifdef NDEBUG
+
+#  define EXPECT_DEBUG_DEATH(statement, regex) \
+  GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+#  define ASSERT_DEBUG_DEATH(statement, regex) \
+  GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# else
+
+#  define EXPECT_DEBUG_DEATH(statement, regex) \
+  EXPECT_DEATH(statement, regex)
+
+#  define ASSERT_DEBUG_DEATH(statement, regex) \
+  ASSERT_DEATH(statement, regex)
+
+# endif  // NDEBUG for EXPECT_DEBUG_DEATH
+#endif  // GTEST_HAS_DEATH_TEST
+
+// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
+// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
+// death tests are supported; otherwise they just issue a warning.  This is
+// useful when you are combining death test assertions with normal test
+// assertions in one test.
+#if GTEST_HAS_DEATH_TEST
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+    EXPECT_DEATH(statement, regex)
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+    ASSERT_DEATH(statement, regex)
+#else
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+    GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+    GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+#endif
+
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-message.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-message.h
new file mode 100644
index 0000000..fe879bc
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-message.h
@@ -0,0 +1,250 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the Message class.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+//   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
+// program!
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+
+#include <limits>
+
+#include "gtest/internal/gtest-port.h"
+
+// Ensures that there is at least one operator<< in the global namespace.
+// See Message& operator<<(...) below for why.
+void operator<<(const testing::internal::Secret&, int);
+
+namespace testing {
+
+// The Message class works like an ostream repeater.
+//
+// Typical usage:
+//
+//   1. You stream a bunch of values to a Message object.
+//      It will remember the text in a stringstream.
+//   2. Then you stream the Message object to an ostream.
+//      This causes the text in the Message to be streamed
+//      to the ostream.
+//
+// For example;
+//
+//   testing::Message foo;
+//   foo << 1 << " != " << 2;
+//   std::cout << foo;
+//
+// will print "1 != 2".
+//
+// Message is not intended to be inherited from.  In particular, its
+// destructor is not virtual.
+//
+// Note that stringstream behaves differently in gcc and in MSVC.  You
+// can stream a NULL char pointer to it in the former, but not in the
+// latter (it causes an access violation if you do).  The Message
+// class hides this difference by treating a NULL char pointer as
+// "(null)".
+class GTEST_API_ Message {
+ private:
+  // The type of basic IO manipulators (endl, ends, and flush) for
+  // narrow streams.
+  typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
+
+ public:
+  // Constructs an empty Message.
+  Message();
+
+  // Copy constructor.
+  Message(const Message& msg) : ss_(new ::std::stringstream) {  // NOLINT
+    *ss_ << msg.GetString();
+  }
+
+  // Constructs a Message from a C-string.
+  explicit Message(const char* str) : ss_(new ::std::stringstream) {
+    *ss_ << str;
+  }
+
+#if GTEST_OS_SYMBIAN
+  // Streams a value (either a pointer or not) to this object.
+  template <typename T>
+  inline Message& operator <<(const T& value) {
+    StreamHelper(typename internal::is_pointer<T>::type(), value);
+    return *this;
+  }
+#else
+  // Streams a non-pointer value to this object.
+  template <typename T>
+  inline Message& operator <<(const T& val) {
+    // Some libraries overload << for STL containers.  These
+    // overloads are defined in the global namespace instead of ::std.
+    //
+    // C++'s symbol lookup rule (i.e. Koenig lookup) says that these
+    // overloads are visible in either the std namespace or the global
+    // namespace, but not other namespaces, including the testing
+    // namespace which Google Test's Message class is in.
+    //
+    // To allow STL containers (and other types that has a << operator
+    // defined in the global namespace) to be used in Google Test
+    // assertions, testing::Message must access the custom << operator
+    // from the global namespace.  With this using declaration,
+    // overloads of << defined in the global namespace and those
+    // visible via Koenig lookup are both exposed in this function.
+    using ::operator <<;
+    *ss_ << val;
+    return *this;
+  }
+
+  // Streams a pointer value to this object.
+  //
+  // This function is an overload of the previous one.  When you
+  // stream a pointer to a Message, this definition will be used as it
+  // is more specialized.  (The C++ Standard, section
+  // [temp.func.order].)  If you stream a non-pointer, then the
+  // previous definition will be used.
+  //
+  // The reason for this overload is that streaming a NULL pointer to
+  // ostream is undefined behavior.  Depending on the compiler, you
+  // may get "0", "(nil)", "(null)", or an access violation.  To
+  // ensure consistent result across compilers, we always treat NULL
+  // as "(null)".
+  template <typename T>
+  inline Message& operator <<(T* const& pointer) {  // NOLINT
+    if (pointer == NULL) {
+      *ss_ << "(null)";
+    } else {
+      *ss_ << pointer;
+    }
+    return *this;
+  }
+#endif  // GTEST_OS_SYMBIAN
+
+  // Since the basic IO manipulators are overloaded for both narrow
+  // and wide streams, we have to provide this specialized definition
+  // of operator <<, even though its body is the same as the
+  // templatized version above.  Without this definition, streaming
+  // endl or other basic IO manipulators to Message will confuse the
+  // compiler.
+  Message& operator <<(BasicNarrowIoManip val) {
+    *ss_ << val;
+    return *this;
+  }
+
+  // Instead of 1/0, we want to see true/false for bool values.
+  Message& operator <<(bool b) {
+    return *this << (b ? "true" : "false");
+  }
+
+  // These two overloads allow streaming a wide C string to a Message
+  // using the UTF-8 encoding.
+  Message& operator <<(const wchar_t* wide_c_str);
+  Message& operator <<(wchar_t* wide_c_str);
+
+#if GTEST_HAS_STD_WSTRING
+  // Converts the given wide string to a narrow string using the UTF-8
+  // encoding, and streams the result to this Message object.
+  Message& operator <<(const ::std::wstring& wstr);
+#endif  // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+  // Converts the given wide string to a narrow string using the UTF-8
+  // encoding, and streams the result to this Message object.
+  Message& operator <<(const ::wstring& wstr);
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+  // Gets the text streamed to this object so far as an std::string.
+  // Each '\0' character in the buffer is replaced with "\\0".
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  std::string GetString() const;
+
+ private:
+
+#if GTEST_OS_SYMBIAN
+  // These are needed as the Nokia Symbian Compiler cannot decide between
+  // const T& and const T* in a function template. The Nokia compiler _can_
+  // decide between class template specializations for T and T*, so a
+  // tr1::type_traits-like is_pointer works, and we can overload on that.
+  template <typename T>
+  inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
+    if (pointer == NULL) {
+      *ss_ << "(null)";
+    } else {
+      *ss_ << pointer;
+    }
+  }
+  template <typename T>
+  inline void StreamHelper(internal::false_type /*is_pointer*/,
+                           const T& value) {
+    // See the comments in Message& operator <<(const T&) above for why
+    // we need this using statement.
+    using ::operator <<;
+    *ss_ << value;
+  }
+#endif  // GTEST_OS_SYMBIAN
+
+  // We'll hold the text streamed to this object here.
+  const internal::scoped_ptr< ::std::stringstream> ss_;
+
+  // We declare (but don't implement) this to prevent the compiler
+  // from implementing the assignment operator.
+  void operator=(const Message&);
+};
+
+// Streams a Message to an ostream.
+inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
+  return os << sb.GetString();
+}
+
+namespace internal {
+
+// Converts a streamable value to an std::string.  A NULL pointer is
+// converted to "(null)".  When the input value is a ::string,
+// ::std::string, ::wstring, or ::std::wstring object, each NUL
+// character in it is replaced with "\\0".
+template <typename T>
+std::string StreamableToString(const T& streamable) {
+  return (Message() << streamable).GetString();
+}
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h
new file mode 100644
index 0000000..d6702c8
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h
@@ -0,0 +1,1421 @@
+// This file was GENERATED by command:
+//     pump.py gtest-param-test.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: vladl at google.com (Vlad Losev)
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing Framework (Google Test)
+//
+// This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+  // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+  // Inside a test, access the test parameter with the GetParam() method
+  // of the TestWithParam<T> class:
+  EXPECT_TRUE(foo.Blah(GetParam()));
+  ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+  ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a  summary of them, which
+// are all in the testing namespace:
+//
+//
+//  Range(begin, end [, step]) - Yields values {begin, begin+step,
+//                               begin+step+step, ...}. The values do not
+//                               include end. step defaults to 1.
+//  Values(v1, v2, ..., vN)    - Yields values {v1, v2, ..., vN}.
+//  ValuesIn(container)        - Yields values from a C-style array, an STL
+//  ValuesIn(begin,end)          container, or an iterator range [begin, end).
+//  Bool()                     - Yields sequence {false, true}.
+//  Combine(g1, g2, ..., gN)   - Yields all combinations (the Cartesian product
+//                               for the math savvy) of the values generated
+//                               by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test case
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+                        FooTest,
+                        Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more then once) the first argument to the
+// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
+// actual test case name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+//    * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+//    * InstantiationName/FooTest.DoesBlah/1 for "miny"
+//    * InstantiationName/FooTest.DoesBlah/2 for "moe"
+//    * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+//    * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+//    * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+//    * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
+// in the given test case, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_CASE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+  // You can inherit all the usual members for a non-parameterized test
+  // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+  // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+  // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+  // GetParam works just the same here as if you inherit from TestWithParam.
+  EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif  // 0
+
+#include "gtest/internal/gtest-port.h"
+
+#if !GTEST_OS_SYMBIAN
+# include <utility>
+#endif
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*.  Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-param-util-generated.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test case is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test case FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+//   - returns a generator producing a sequence of values {start, start+1,
+//     start+2, ..., }.
+// Range(start, end, step)
+//   - returns a generator producing a sequence of values {start, start+step,
+//     start+step+step, ..., }.
+// Notes:
+//   * The generated sequences never include end. For example, Range(1, 5)
+//     returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+//     returns a generator producing {1, 3, 5, 7}.
+//   * start and end must have the same type. That type may be any integral or
+//     floating-point type or a user defined type satisfying these conditions:
+//     * It must be assignable (have operator=() defined).
+//     * It must have operator+() (operator+(int-compatible type) for
+//       two-operand version).
+//     * It must have operator<() defined.
+//     Elements in the resulting sequences will also have that type.
+//   * Condition start < end must be satisfied in order for resulting sequences
+//     to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+  return internal::ParamGenerator<T>(
+      new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+  return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+//   - returns a generator producing sequences with elements from
+//     a C-style array.
+// ValuesIn(const Container& container)
+//   - returns a generator producing sequences with elements from
+//     an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+//   - returns a generator producing sequences with elements from
+//     a range [begin, end) defined by a pair of STL-style iterators. These
+//     iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test case StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+//
+// This instantiates tests from test case StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+//   ::std::vector< ::std::string> v;
+//   v.push_back("a");
+//   v.push_back("b");
+//   return v;
+// }
+//
+// INSTANTIATE_TEST_CASE_P(CharSequence,
+//                         StlStringTest,
+//                         ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+//   ::std::list<char> list;
+//   list.push_back('a');
+//   list.push_back('b');
+//   return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_CASE_P(CharSequence2,
+//                         CharTest,
+//                         ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+  typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+  typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
+      ::value_type ParamType;
+  return internal::ParamGenerator<ParamType>(
+      new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+  return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container) {
+  return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+//   - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test case BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+//
+// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+// Currently, Values() supports from 1 to 50 parameters.
+//
+template <typename T1>
+internal::ValueArray1<T1> Values(T1 v1) {
+  return internal::ValueArray1<T1>(v1);
+}
+
+template <typename T1, typename T2>
+internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) {
+  return internal::ValueArray2<T1, T2>(v1, v2);
+}
+
+template <typename T1, typename T2, typename T3>
+internal::ValueArray3<T1, T2, T3> Values(T1 v1, T2 v2, T3 v3) {
+  return internal::ValueArray3<T1, T2, T3>(v1, v2, v3);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+internal::ValueArray4<T1, T2, T3, T4> Values(T1 v1, T2 v2, T3 v3, T4 v4) {
+  return internal::ValueArray4<T1, T2, T3, T4>(v1, v2, v3, v4);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+internal::ValueArray5<T1, T2, T3, T4, T5> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5) {
+  return internal::ValueArray5<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+internal::ValueArray6<T1, T2, T3, T4, T5, T6> Values(T1 v1, T2 v2, T3 v3,
+    T4 v4, T5 v5, T6 v6) {
+  return internal::ValueArray6<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7> Values(T1 v1, T2 v2, T3 v3,
+    T4 v4, T5 v5, T6 v6, T7 v7) {
+  return internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5,
+      v6, v7);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) {
+  return internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4,
+      v5, v6, v7, v8);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) {
+  return internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(v1, v2, v3,
+      v4, v5, v6, v7, v8, v9);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Values(T1 v1,
+    T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) {
+  return internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(v1,
+      v2, v3, v4, v5, v6, v7, v8, v9, v10);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11>
+internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+    T11> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11) {
+  return internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
+      T11>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12>
+internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+    T12> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12) {
+  return internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13>
+internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+    T13> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13) {
+  return internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14>
+internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) {
+  return internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+      v14);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15>
+internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+    T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) {
+  return internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+      v13, v14, v15);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16>
+internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16) {
+  return internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+      v12, v13, v14, v15, v16);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17>
+internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17) {
+  return internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+      v11, v12, v13, v14, v15, v16, v17);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18>
+internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+    T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18) {
+  return internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+      v10, v11, v12, v13, v14, v15, v16, v17, v18);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19>
+internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+    T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+    T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) {
+  return internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19>(v1, v2, v3, v4, v5, v6, v7, v8,
+      v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20>
+internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+    T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) {
+  return internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20>(v1, v2, v3, v4, v5, v6, v7,
+      v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21>
+internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+    T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) {
+  return internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(v1, v2, v3, v4, v5, v6,
+      v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22>
+internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22> Values(T1 v1, T2 v2, T3 v3,
+    T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22) {
+  return internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>(v1, v2, v3, v4,
+      v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+      v20, v21, v22);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23>
+internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22, T23 v23) {
+  return internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>(v1, v2, v3,
+      v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+      v20, v21, v22, v23);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24>
+internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22, T23 v23, T24 v24) {
+  return internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>(v1, v2,
+      v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+      v19, v20, v21, v22, v23, v24);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25>
+internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Values(T1 v1,
+    T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+    T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+    T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) {
+  return internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25>(v1,
+      v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+      v18, v19, v20, v21, v22, v23, v24, v25);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26>
+internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+    T26> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26) {
+  return internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+      v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27>
+internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+    T27> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27) {
+  return internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+      v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28>
+internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+    T28> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28) {
+  return internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+      v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+      v28);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29>
+internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28, T29 v29) {
+  return internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+      v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+      v27, v28, v29);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30>
+internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+    T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+    T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+    T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) {
+  return internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+      v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+      v26, v27, v28, v29, v30);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31>
+internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) {
+  return internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+      v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+      v25, v26, v27, v28, v29, v30, v31);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32>
+internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+    T32 v32) {
+  return internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+      v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+      v24, v25, v26, v27, v28, v29, v30, v31, v32);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33>
+internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+    T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+    T32 v32, T33 v33) {
+  return internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33>(v1, v2, v3, v4, v5, v6, v7, v8,
+      v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+      v24, v25, v26, v27, v28, v29, v30, v31, v32, v33);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34>
+internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+    T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+    T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+    T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+    T31 v31, T32 v32, T33 v33, T34 v34) {
+  return internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34>(v1, v2, v3, v4, v5, v6, v7,
+      v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+      v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35>
+internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+    T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+    T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+    T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) {
+  return internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35>(v1, v2, v3, v4, v5, v6,
+      v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+      v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36>
+internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+    T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+    T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+    T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) {
+  return internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36>(v1, v2, v3, v4,
+      v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+      v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+      v34, v35, v36);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37>
+internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37> Values(T1 v1, T2 v2, T3 v3,
+    T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+    T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+    T37 v37) {
+  return internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37>(v1, v2, v3,
+      v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+      v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+      v34, v35, v36, v37);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38>
+internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+    T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+    T37 v37, T38 v38) {
+  return internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38>(v1, v2,
+      v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18,
+      v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32,
+      v33, v34, v35, v36, v37, v38);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39>
+internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Values(T1 v1, T2 v2,
+    T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12,
+    T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20,
+    T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28,
+    T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36,
+    T37 v37, T38 v38, T39 v39) {
+  return internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39>(v1,
+      v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
+      v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31,
+      v32, v33, v34, v35, v36, v37, v38, v39);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40>
+internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Values(T1 v1,
+    T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11,
+    T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19,
+    T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27,
+    T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35,
+    T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) {
+  return internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15,
+      v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
+      v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41>
+internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+    T41> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+    T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) {
+  return internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14,
+      v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28,
+      v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42>
+internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+    T42> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+    T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+    T42 v42) {
+  return internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+      v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27,
+      v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41,
+      v42);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43>
+internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+    T43> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+    T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+    T42 v42, T43 v43) {
+  return internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+      v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26,
+      v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40,
+      v41, v42, v43);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44>
+internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+    T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+    T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+    T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+    T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+    T42 v42, T43 v43, T44 v44) {
+  return internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,
+      v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
+      v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39,
+      v40, v41, v42, v43, v44);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45>
+internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+    T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+    T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+    T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+    T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+    T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) {
+  return internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10,
+      v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24,
+      v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38,
+      v39, v40, v41, v42, v43, v44, v45);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46>
+internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+    T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+    T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) {
+  return internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45, T46>(v1, v2, v3, v4, v5, v6, v7, v8, v9,
+      v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+      v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+      v38, v39, v40, v41, v42, v43, v44, v45, v46);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47>
+internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46, T47> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+    T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+    T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+    T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) {
+  return internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45, T46, T47>(v1, v2, v3, v4, v5, v6, v7, v8,
+      v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23,
+      v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37,
+      v38, v39, v40, v41, v42, v43, v44, v45, v46, v47);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48>
+internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46, T47, T48> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6,
+    T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15,
+    T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23,
+    T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31,
+    T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39,
+    T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47,
+    T48 v48) {
+  return internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45, T46, T47, T48>(v1, v2, v3, v4, v5, v6, v7,
+      v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
+      v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36,
+      v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49>
+internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46, T47, T48, T49> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5,
+    T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14,
+    T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22,
+    T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30,
+    T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38,
+    T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46,
+    T47 v47, T48 v48, T49 v49) {
+  return internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45, T46, T47, T48, T49>(v1, v2, v3, v4, v5, v6,
+      v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
+      v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35,
+      v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49, typename T50>
+internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46, T47, T48, T49, T50> Values(T1 v1, T2 v2, T3 v3, T4 v4,
+    T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13,
+    T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21,
+    T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29,
+    T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37,
+    T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45,
+    T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) {
+  return internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>(v1, v2, v3, v4,
+      v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+      v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33,
+      v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47,
+      v48, v49, v50);
+}
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+//   - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test case FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+//   virtual void SetUp() {
+//     external_flag = GetParam();
+//   }
+// }
+// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+  return Values(false, true);
+}
+
+# if GTEST_HAS_COMBINE
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+//   - returns a generator producing sequences with elements coming from
+//     the Cartesian product of elements from the sequences generated by
+//     gen1, gen2, ..., genN. The sequence elements will have a type of
+//     tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+//     of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Combine can have up to 10 arguments. This number is currently limited
+// by the maximum number of elements in the tuple implementation used by Google
+// Test.
+//
+// Example:
+//
+// This will instantiate tests in test case AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+//     : public testing::TestWithParam<tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
+//                         Combine(Values("cat", "dog"),
+//                                 Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+//     : public testing::TestWithParam<tuple<bool, bool> > {
+//   virtual void SetUp() {
+//     // Assigns external_flag_1 and external_flag_2 values from the tuple.
+//     tie(external_flag_1, external_flag_2) = GetParam();
+//   }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+//   // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
+//                         Combine(Bool(), Bool()));
+//
+template <typename Generator1, typename Generator2>
+internal::CartesianProductHolder2<Generator1, Generator2> Combine(
+    const Generator1& g1, const Generator2& g2) {
+  return internal::CartesianProductHolder2<Generator1, Generator2>(
+      g1, g2);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3>
+internal::CartesianProductHolder3<Generator1, Generator2, Generator3> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3) {
+  return internal::CartesianProductHolder3<Generator1, Generator2, Generator3>(
+      g1, g2, g3);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4>
+internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+    Generator4> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4) {
+  return internal::CartesianProductHolder4<Generator1, Generator2, Generator3,
+      Generator4>(
+      g1, g2, g3, g4);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5>
+internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+    Generator4, Generator5> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5) {
+  return internal::CartesianProductHolder5<Generator1, Generator2, Generator3,
+      Generator4, Generator5>(
+      g1, g2, g3, g4, g5);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5, typename Generator6>
+internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+    Generator4, Generator5, Generator6> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5, const Generator6& g6) {
+  return internal::CartesianProductHolder6<Generator1, Generator2, Generator3,
+      Generator4, Generator5, Generator6>(
+      g1, g2, g3, g4, g5, g6);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5, typename Generator6,
+    typename Generator7>
+internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+    Generator4, Generator5, Generator6, Generator7> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5, const Generator6& g6,
+        const Generator7& g7) {
+  return internal::CartesianProductHolder7<Generator1, Generator2, Generator3,
+      Generator4, Generator5, Generator6, Generator7>(
+      g1, g2, g3, g4, g5, g6, g7);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5, typename Generator6,
+    typename Generator7, typename Generator8>
+internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+    Generator4, Generator5, Generator6, Generator7, Generator8> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5, const Generator6& g6,
+        const Generator7& g7, const Generator8& g8) {
+  return internal::CartesianProductHolder8<Generator1, Generator2, Generator3,
+      Generator4, Generator5, Generator6, Generator7, Generator8>(
+      g1, g2, g3, g4, g5, g6, g7, g8);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5, typename Generator6,
+    typename Generator7, typename Generator8, typename Generator9>
+internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+    Generator4, Generator5, Generator6, Generator7, Generator8,
+    Generator9> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5, const Generator6& g6,
+        const Generator7& g7, const Generator8& g8, const Generator9& g9) {
+  return internal::CartesianProductHolder9<Generator1, Generator2, Generator3,
+      Generator4, Generator5, Generator6, Generator7, Generator8, Generator9>(
+      g1, g2, g3, g4, g5, g6, g7, g8, g9);
+}
+
+template <typename Generator1, typename Generator2, typename Generator3,
+    typename Generator4, typename Generator5, typename Generator6,
+    typename Generator7, typename Generator8, typename Generator9,
+    typename Generator10>
+internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+    Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+    Generator10> Combine(
+    const Generator1& g1, const Generator2& g2, const Generator3& g3,
+        const Generator4& g4, const Generator5& g5, const Generator6& g6,
+        const Generator7& g7, const Generator8& g8, const Generator9& g9,
+        const Generator10& g10) {
+  return internal::CartesianProductHolder10<Generator1, Generator2, Generator3,
+      Generator4, Generator5, Generator6, Generator7, Generator8, Generator9,
+      Generator10>(
+      g1, g2, g3, g4, g5, g6, g7, g8, g9, g10);
+}
+# endif  // GTEST_HAS_COMBINE
+
+
+
+# define TEST_P(test_case_name, test_name) \
+  class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+      : public test_case_name { \
+   public: \
+    GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
+    virtual void TestBody(); \
+   private: \
+    static int AddToRegistry() { \
+      ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+          GetTestCasePatternHolder<test_case_name>(\
+              #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
+                  #test_case_name, \
+                  #test_name, \
+                  new ::testing::internal::TestMetaFactory< \
+                      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+      return 0; \
+    } \
+    static int gtest_registering_dummy_; \
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+        GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
+  }; \
+  int GTEST_TEST_CLASS_NAME_(test_case_name, \
+                             test_name)::gtest_registering_dummy_ = \
+      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
+  void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+  ::testing::internal::ParamGenerator<test_case_name::ParamType> \
+      gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
+  int gtest_##prefix##test_case_name##_dummy_ = \
+      ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+          GetTestCasePatternHolder<test_case_name>(\
+              #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
+                  #prefix, \
+                  &gtest_##prefix##test_case_name##_EvalGenerator_, \
+                  __FILE__, __LINE__)
+
+}  // namespace testing
+
+#endif  // GTEST_HAS_PARAM_TEST
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h.pump b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h.pump
new file mode 100644
index 0000000..2dc9303
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-param-test.h.pump
@@ -0,0 +1,487 @@
+$$ -*- mode: c++; -*-
+$var n = 50  $$ Maximum length of Values arguments we want to support.
+$var maxtuple = 10  $$ Maximum number of Combine arguments we want to support.
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: vladl at google.com (Vlad Losev)
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing Framework (Google Test)
+//
+// This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+  // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+  // Inside a test, access the test parameter with the GetParam() method
+  // of the TestWithParam<T> class:
+  EXPECT_TRUE(foo.Blah(GetParam()));
+  ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+  ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a  summary of them, which
+// are all in the testing namespace:
+//
+//
+//  Range(begin, end [, step]) - Yields values {begin, begin+step,
+//                               begin+step+step, ...}. The values do not
+//                               include end. step defaults to 1.
+//  Values(v1, v2, ..., vN)    - Yields values {v1, v2, ..., vN}.
+//  ValuesIn(container)        - Yields values from a C-style array, an STL
+//  ValuesIn(begin,end)          container, or an iterator range [begin, end).
+//  Bool()                     - Yields sequence {false, true}.
+//  Combine(g1, g2, ..., gN)   - Yields all combinations (the Cartesian product
+//                               for the math savvy) of the values generated
+//                               by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test case
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_CASE_P(InstantiationName,
+                        FooTest,
+                        Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more then once) the first argument to the
+// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the
+// actual test case name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+//    * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+//    * InstantiationName/FooTest.DoesBlah/1 for "miny"
+//    * InstantiationName/FooTest.DoesBlah/2 for "moe"
+//    * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+//    * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+//    * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+//    * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests
+// in the given test case, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_CASE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+  // You can inherit all the usual members for a non-parameterized test
+  // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+  // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+  // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+  // GetParam works just the same here as if you inherit from TestWithParam.
+  EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif  // 0
+
+#include "gtest/internal/gtest-port.h"
+
+#if !GTEST_OS_SYMBIAN
+# include <utility>
+#endif
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*.  Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-param-util-generated.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test case is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test case FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+//   - returns a generator producing a sequence of values {start, start+1,
+//     start+2, ..., }.
+// Range(start, end, step)
+//   - returns a generator producing a sequence of values {start, start+step,
+//     start+step+step, ..., }.
+// Notes:
+//   * The generated sequences never include end. For example, Range(1, 5)
+//     returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+//     returns a generator producing {1, 3, 5, 7}.
+//   * start and end must have the same type. That type may be any integral or
+//     floating-point type or a user defined type satisfying these conditions:
+//     * It must be assignable (have operator=() defined).
+//     * It must have operator+() (operator+(int-compatible type) for
+//       two-operand version).
+//     * It must have operator<() defined.
+//     Elements in the resulting sequences will also have that type.
+//   * Condition start < end must be satisfied in order for resulting sequences
+//     to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+  return internal::ParamGenerator<T>(
+      new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+  return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+//   - returns a generator producing sequences with elements from
+//     a C-style array.
+// ValuesIn(const Container& container)
+//   - returns a generator producing sequences with elements from
+//     an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+//   - returns a generator producing sequences with elements from
+//     a range [begin, end) defined by a pair of STL-style iterators. These
+//     iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test case StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings));
+//
+// This instantiates tests from test case StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+//   ::std::vector< ::std::string> v;
+//   v.push_back("a");
+//   v.push_back("b");
+//   return v;
+// }
+//
+// INSTANTIATE_TEST_CASE_P(CharSequence,
+//                         StlStringTest,
+//                         ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+//   ::std::list<char> list;
+//   list.push_back('a');
+//   list.push_back('b');
+//   return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_CASE_P(CharSequence2,
+//                         CharTest,
+//                         ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+  typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+  typedef typename ::testing::internal::IteratorTraits<ForwardIterator>
+      ::value_type ParamType;
+  return internal::ParamGenerator<ParamType>(
+      new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+  return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container) {
+  return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+//   - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test case BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three"));
+//
+// This instantiates tests from test case BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+// Currently, Values() supports from 1 to $n parameters.
+//
+$range i 1..n
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename T$j]]>
+internal::ValueArray$i<$for j, [[T$j]]> Values($for j, [[T$j v$j]]) {
+  return internal::ValueArray$i<$for j, [[T$j]]>($for j, [[v$j]]);
+}
+
+]]
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+//   - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test case FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+//   virtual void SetUp() {
+//     external_flag = GetParam();
+//   }
+// }
+// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+  return Values(false, true);
+}
+
+# if GTEST_HAS_COMBINE
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+//   - returns a generator producing sequences with elements coming from
+//     the Cartesian product of elements from the sequences generated by
+//     gen1, gen2, ..., genN. The sequence elements will have a type of
+//     tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+//     of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Combine can have up to $maxtuple arguments. This number is currently limited
+// by the maximum number of elements in the tuple implementation used by Google
+// Test.
+//
+// Example:
+//
+// This will instantiate tests in test case AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+//     : public testing::TestWithParam<tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest,
+//                         Combine(Values("cat", "dog"),
+//                                 Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+//     : public testing::TestWithParam<tuple<bool, bool> > {
+//   virtual void SetUp() {
+//     // Assigns external_flag_1 and external_flag_2 values from the tuple.
+//     tie(external_flag_1, external_flag_2) = GetParam();
+//   }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+//   // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest,
+//                         Combine(Bool(), Bool()));
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename Generator$j]]>
+internal::CartesianProductHolder$i<$for j, [[Generator$j]]> Combine(
+    $for j, [[const Generator$j& g$j]]) {
+  return internal::CartesianProductHolder$i<$for j, [[Generator$j]]>(
+      $for j, [[g$j]]);
+}
+
+]]
+# endif  // GTEST_HAS_COMBINE
+
+
+
+# define TEST_P(test_case_name, test_name) \
+  class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+      : public test_case_name { \
+   public: \
+    GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \
+    virtual void TestBody(); \
+   private: \
+    static int AddToRegistry() { \
+      ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+          GetTestCasePatternHolder<test_case_name>(\
+              #test_case_name, __FILE__, __LINE__)->AddTestPattern(\
+                  #test_case_name, \
+                  #test_name, \
+                  new ::testing::internal::TestMetaFactory< \
+                      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \
+      return 0; \
+    } \
+    static int gtest_registering_dummy_; \
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+        GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \
+  }; \
+  int GTEST_TEST_CLASS_NAME_(test_case_name, \
+                             test_name)::gtest_registering_dummy_ = \
+      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \
+  void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+  ::testing::internal::ParamGenerator<test_case_name::ParamType> \
+      gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \
+  int gtest_##prefix##test_case_name##_dummy_ = \
+      ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \
+          GetTestCasePatternHolder<test_case_name>(\
+              #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\
+                  #prefix, \
+                  &gtest_##prefix##test_case_name##_EvalGenerator_, \
+                  __FILE__, __LINE__)
+
+}  // namespace testing
+
+#endif  // GTEST_HAS_PARAM_TEST
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-printers.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-printers.h
new file mode 100644
index 0000000..0639d9f
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-printers.h
@@ -0,0 +1,855 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Google Test - The Google C++ Testing Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// A user can teach this function how to print a class type T by
+// defining either operator<<() or PrintTo() in the namespace that
+// defines T.  More specifically, the FIRST defined function in the
+// following list will be used (assuming T is defined in namespace
+// foo):
+//
+//   1. foo::PrintTo(const T&, ostream*)
+//   2. operator<<(ostream&, const T&) defined in either foo or the
+//      global namespace.
+//
+// If none of the above is defined, it will print the debug string of
+// the value if it is a protocol buffer, or print the raw bytes in the
+// value otherwise.
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
+// printed.
+//
+// We also provide some convenient wrappers:
+//
+//   // Prints a value to a string.  For a (const or not) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   std::string ::testing::PrintToString(const T& value);
+//
+//   // Prints a value tersely: for a reference type, the referenced
+//   // value (but not the address) is printed; for a (const or not) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+//   // Prints value using the type inferred by the compiler.  The difference
+//   // from UniversalTersePrint() is that this function prints both the
+//   // pointer and the NUL-terminated string for a (const or not) char pointer.
+//   void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//
+//   // Prints the fields of a tuple tersely to a string vector, one
+//   // element for each field. Tuple support must be enabled in
+//   // gtest-port.h.
+//   std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+//       const Tuple& value);
+//
+// Known limitation:
+//
+// The print primitives print the elements of an STL-style container
+// using the compiler-inferred type of *iter where iter is a
+// const_iterator of the container.  When const_iterator is an input
+// iterator but not a forward iterator, this inferred type may not
+// match value_type, and the print output may be incorrect.  In
+// practice, this is rarely a problem as for most containers
+// const_iterator is a forward iterator.  We'll fix this if there's an
+// actual need for it.  Note that this fix cannot rely on value_type
+// being defined as many user-defined container types don't have
+// value_type.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-internal.h"
+
+namespace testing {
+
+// Definitions in the 'internal' and 'internal2' name spaces are
+// subject to change without notice.  DO NOT USE THEM IN USER CODE!
+namespace internal2 {
+
+// Prints the given number of bytes in the given object to the given
+// ostream.
+GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+                                     size_t count,
+                                     ::std::ostream* os);
+
+// For selecting which printer to use when a given type has neither <<
+// nor PrintTo().
+enum TypeKind {
+  kProtobuf,              // a protobuf type
+  kConvertibleToInteger,  // a type implicitly convertible to BiggestInt
+                          // (e.g. a named or unnamed enum type)
+  kOtherType              // anything else
+};
+
+// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
+// by the universal printer to print a value of type T when neither
+// operator<< nor PrintTo() is defined for T, where kTypeKind is the
+// "kind" of T as defined by enum TypeKind.
+template <typename T, TypeKind kTypeKind>
+class TypeWithoutFormatter {
+ public:
+  // This default version is called when kTypeKind is kOtherType.
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
+                         sizeof(value), os);
+  }
+};
+
+// We print a protobuf using its ShortDebugString() when the string
+// doesn't exceed this many characters; otherwise we print it using
+// DebugString() for better readability.
+const size_t kProtobufOneLinerMaxLength = 50;
+
+template <typename T>
+class TypeWithoutFormatter<T, kProtobuf> {
+ public:
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    const ::testing::internal::string short_str = value.ShortDebugString();
+    const ::testing::internal::string pretty_str =
+        short_str.length() <= kProtobufOneLinerMaxLength ?
+        short_str : ("\n" + value.DebugString());
+    *os << ("<" + pretty_str + ">");
+  }
+};
+
+template <typename T>
+class TypeWithoutFormatter<T, kConvertibleToInteger> {
+ public:
+  // Since T has no << operator or PrintTo() but can be implicitly
+  // converted to BiggestInt, we print it as a BiggestInt.
+  //
+  // Most likely T is an enum type (either named or unnamed), in which
+  // case printing it as an integer is the desired behavior.  In case
+  // T is not an enum, printing it as an integer is the best we can do
+  // given that it has no user-defined printer.
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    const internal::BiggestInt kBigInt = value;
+    *os << kBigInt;
+  }
+};
+
+// Prints the given value to the given ostream.  If the value is a
+// protocol message, its debug string is printed; if it's an enum or
+// of a type implicitly convertible to BiggestInt, it's printed as an
+// integer; otherwise the bytes in the value are printed.  This is
+// what UniversalPrinter<T>::Print() does when it knows nothing about
+// type T and T has neither << operator nor PrintTo().
+//
+// A user can override this behavior for a class type Foo by defining
+// a << operator in the namespace where Foo is defined.
+//
+// We put this operator in namespace 'internal2' instead of 'internal'
+// to simplify the implementation, as much code in 'internal' needs to
+// use << in STL, which would conflict with our own << were it defined
+// in 'internal'.
+//
+// Note that this operator<< takes a generic std::basic_ostream<Char,
+// CharTraits> type instead of the more restricted std::ostream.  If
+// we define it to take an std::ostream instead, we'll get an
+// "ambiguous overloads" compiler error when trying to print a type
+// Foo that supports streaming to std::basic_ostream<Char,
+// CharTraits>, as the compiler cannot tell whether
+// operator<<(std::ostream&, const T&) or
+// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
+// specific.
+template <typename Char, typename CharTraits, typename T>
+::std::basic_ostream<Char, CharTraits>& operator<<(
+    ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
+  TypeWithoutFormatter<T,
+      (internal::IsAProtocolMessage<T>::value ? kProtobuf :
+       internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
+       kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
+  return os;
+}
+
+}  // namespace internal2
+}  // namespace testing
+
+// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
+// magic needed for implementing UniversalPrinter won't work.
+namespace testing_internal {
+
+// Used to print a value that is not an STL-style container when the
+// user doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
+  // With the following statement, during unqualified name lookup,
+  // testing::internal2::operator<< appears as if it was declared in
+  // the nearest enclosing namespace that contains both
+  // ::testing_internal and ::testing::internal2, i.e. the global
+  // namespace.  For more details, refer to the C++ Standard section
+  // 7.3.4-1 [namespace.udir].  This allows us to fall back onto
+  // testing::internal2::operator<< in case T doesn't come with a <<
+  // operator.
+  //
+  // We cannot write 'using ::testing::internal2::operator<<;', which
+  // gcc 3.3 fails to compile due to a compiler bug.
+  using namespace ::testing::internal2;  // NOLINT
+
+  // Assuming T is defined in namespace foo, in the next statement,
+  // the compiler will consider all of:
+  //
+  //   1. foo::operator<< (thanks to Koenig look-up),
+  //   2. ::operator<< (as the current namespace is enclosed in ::),
+  //   3. testing::internal2::operator<< (thanks to the using statement above).
+  //
+  // The operator<< whose type matches T best will be picked.
+  //
+  // We deliberately allow #2 to be a candidate, as sometimes it's
+  // impossible to define #1 (e.g. when foo is ::std, defining
+  // anything in it is undefined behavior unless you are a compiler
+  // vendor.).
+  *os << value;
+}
+
+}  // namespace testing_internal
+
+namespace testing {
+namespace internal {
+
+// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
+// value to the given ostream.  The caller must ensure that
+// 'ostream_ptr' is not NULL, or the behavior is undefined.
+//
+// We define UniversalPrinter as a class template (as opposed to a
+// function template), as we need to partially specialize it for
+// reference types, which cannot be done with function templates.
+template <typename T>
+class UniversalPrinter;
+
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
+
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+template <typename C>
+void DefaultPrintTo(IsContainer /* dummy */,
+                    false_type /* is not a pointer */,
+                    const C& container, ::std::ostream* os) {
+  const size_t kMaxCount = 32;  // The maximum number of elements to print.
+  *os << '{';
+  size_t count = 0;
+  for (typename C::const_iterator it = container.begin();
+       it != container.end(); ++it, ++count) {
+    if (count > 0) {
+      *os << ',';
+      if (count == kMaxCount) {  // Enough has been printed.
+        *os << " ...";
+        break;
+      }
+    }
+    *os << ' ';
+    // We cannot call PrintTo(*it, os) here as PrintTo() doesn't
+    // handle *it being a native array.
+    internal::UniversalPrint(*it, os);
+  }
+
+  if (count > 0) {
+    *os << ' ';
+  }
+  *os << '}';
+}
+
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it.  (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space.  Their representation is
+// implementation-defined.  Therefore they will be printed as raw
+// bytes.)
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+                    true_type /* is a pointer */,
+                    T* p, ::std::ostream* os) {
+  if (p == NULL) {
+    *os << "NULL";
+  } else {
+    // C++ doesn't allow casting from a function pointer to any object
+    // pointer.
+    //
+    // IsTrue() silences warnings: "Condition is always true",
+    // "unreachable code".
+    if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
+      // T is not a function type.  We just call << to print p,
+      // relying on ADL to pick up user-defined << for their pointer
+      // types, if any.
+      *os << p;
+    } else {
+      // T is a function type, so '*os << p' doesn't do what we want
+      // (it just prints p as bool).  We want to print p as a const
+      // void*.  However, we cannot cast it to const void* directly,
+      // even using reinterpret_cast, as earlier versions of gcc
+      // (e.g. 3.4.5) cannot compile the cast when p is a function
+      // pointer.  Casting to UInt64 first solves the problem.
+      *os << reinterpret_cast<const void*>(
+          reinterpret_cast<internal::UInt64>(p));
+    }
+  }
+}
+
+// Used to print a non-container, non-pointer value when the user
+// doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+                    false_type /* is not a pointer */,
+                    const T& value, ::std::ostream* os) {
+  ::testing_internal::DefaultPrintNonContainerTo(value, os);
+}
+
+// Prints the given value using the << operator if it has one;
+// otherwise prints the bytes in it.  This is what
+// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
+// or overloaded for type T.
+//
+// A user can override this behavior for a class type Foo by defining
+// an overload of PrintTo() in the namespace where Foo is defined.  We
+// give the user this option as sometimes defining a << operator for
+// Foo is not desirable (e.g. the coding style may prevent doing it,
+// or there is already a << operator but it doesn't do what the user
+// wants).
+template <typename T>
+void PrintTo(const T& value, ::std::ostream* os) {
+  // DefaultPrintTo() is overloaded.  The type of its first two
+  // arguments determine which version will be picked.  If T is an
+  // STL-style container, the version for container will be called; if
+  // T is a pointer, the pointer version will be called; otherwise the
+  // generic version will be called.
+  //
+  // Note that we check for container types here, prior to we check
+  // for protocol message types in our operator<<.  The rationale is:
+  //
+  // For protocol messages, we want to give people a chance to
+  // override Google Mock's format by defining a PrintTo() or
+  // operator<<.  For STL containers, other formats can be
+  // incompatible with Google Mock's format for the container
+  // elements; therefore we check for container types here to ensure
+  // that our format is used.
+  //
+  // The second argument of DefaultPrintTo() is needed to bypass a bug
+  // in Symbian's C++ compiler that prevents it from picking the right
+  // overload between:
+  //
+  //   PrintTo(const T& x, ...);
+  //   PrintTo(T* x, ...);
+  DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+}
+
+// The following list of PrintTo() overloads tells
+// UniversalPrinter<T>::Print() how to print standard types (built-in
+// types, strings, plain arrays, and pointers).
+
+// Overloads for various char types.
+GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
+GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
+inline void PrintTo(char c, ::std::ostream* os) {
+  // When printing a plain char, we always treat it as unsigned.  This
+  // way, the output won't be affected by whether the compiler thinks
+  // char is signed or not.
+  PrintTo(static_cast<unsigned char>(c), os);
+}
+
+// Overloads for other simple built-in types.
+inline void PrintTo(bool x, ::std::ostream* os) {
+  *os << (x ? "true" : "false");
+}
+
+// Overload for wchar_t type.
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
+
+// Overloads for C strings.
+GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
+inline void PrintTo(char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char*>(s), os);
+}
+
+// signed/unsigned char is often used for representing binary data, so
+// we print pointers to it as void* to be safe.
+inline void PrintTo(const signed char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(signed char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(unsigned char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+
+// MSVC can be configured to define wchar_t as a typedef of unsigned
+// short.  It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
+// type.  When wchar_t is a typedef, defining an overload for const
+// wchar_t* would cause unsigned short* be printed as a wide string,
+// possibly causing invalid memory accesses.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Overloads for wide C strings
+GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
+inline void PrintTo(wchar_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const wchar_t*>(s), os);
+}
+#endif
+
+// Overload for C arrays.  Multi-dimensional arrays are printed
+// properly.
+
+// Prints the given number of elements in an array, without printing
+// the curly braces.
+template <typename T>
+void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
+  UniversalPrint(a[0], os);
+  for (size_t i = 1; i != count; i++) {
+    *os << ", ";
+    UniversalPrint(a[i], os);
+  }
+}
+
+// Overloads for ::string and ::std::string.
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
+inline void PrintTo(const ::string& s, ::std::ostream* os) {
+  PrintStringTo(s, os);
+}
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
+inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
+  PrintStringTo(s, os);
+}
+
+// Overloads for ::wstring and ::std::wstring.
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
+  PrintWideStringTo(s, os);
+}
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
+  PrintWideStringTo(s, os);
+}
+#endif  // GTEST_HAS_STD_WSTRING
+
+#if GTEST_HAS_TR1_TUPLE
+// Overload for ::std::tr1::tuple.  Needed for printing function arguments,
+// which are packed as tuples.
+
+// Helper function for printing a tuple.  T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os);
+
+// Overloaded PrintTo() for tuples of various arities.  We support
+// tuples of up-to 10 fields.  The following implementation works
+// regardless of whether tr1::tuple is implemented using the
+// non-standard variadic template feature or not.
+
+inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1>
+void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2>
+void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8, typename T9>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8, typename T9, typename T10>
+void PrintTo(
+    const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
+    ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+#endif  // GTEST_HAS_TR1_TUPLE
+
+// Overload for std::pair.
+template <typename T1, typename T2>
+void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
+  *os << '(';
+  // We cannot use UniversalPrint(value.first, os) here, as T1 may be
+  // a reference type.  The same for printing value.second.
+  UniversalPrinter<T1>::Print(value.first, os);
+  *os << ", ";
+  UniversalPrinter<T2>::Print(value.second, os);
+  *os << ')';
+}
+
+// Implements printing a non-reference type T by letting the compiler
+// pick the right overload of PrintTo() for T.
+template <typename T>
+class UniversalPrinter {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push)          // Saves the current warning state.
+# pragma warning(disable:4180)  // Temporarily disables warning 4180.
+#endif  // _MSC_VER
+
+  // Note: we deliberately don't call this PrintTo(), as that name
+  // conflicts with ::testing::internal::PrintTo in the body of the
+  // function.
+  static void Print(const T& value, ::std::ostream* os) {
+    // By default, ::testing::internal::PrintTo() is used for printing
+    // the value.
+    //
+    // Thanks to Koenig look-up, if T is a class and has its own
+    // PrintTo() function defined in its namespace, that function will
+    // be visible here.  Since it is more specific than the generic ones
+    // in ::testing::internal, it will be picked by the compiler in the
+    // following statement - exactly what we want.
+    PrintTo(value, os);
+  }
+
+#ifdef _MSC_VER
+# pragma warning(pop)           // Restores the warning state.
+#endif  // _MSC_VER
+};
+
+// UniversalPrintArray(begin, len, os) prints an array of 'len'
+// elements, starting at address 'begin'.
+template <typename T>
+void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
+  if (len == 0) {
+    *os << "{}";
+  } else {
+    *os << "{ ";
+    const size_t kThreshold = 18;
+    const size_t kChunkSize = 8;
+    // If the array has more than kThreshold elements, we'll have to
+    // omit some details by printing only the first and the last
+    // kChunkSize elements.
+    // TODO(wan at google.com): let the user control the threshold using a flag.
+    if (len <= kThreshold) {
+      PrintRawArrayTo(begin, len, os);
+    } else {
+      PrintRawArrayTo(begin, kChunkSize, os);
+      *os << ", ..., ";
+      PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
+    }
+    *os << " }";
+  }
+}
+// This overload prints a (const) char array compactly.
+GTEST_API_ void UniversalPrintArray(
+    const char* begin, size_t len, ::std::ostream* os);
+
+// This overload prints a (const) wchar_t array compactly.
+GTEST_API_ void UniversalPrintArray(
+    const wchar_t* begin, size_t len, ::std::ostream* os);
+
+// Implements printing an array type T[N].
+template <typename T, size_t N>
+class UniversalPrinter<T[N]> {
+ public:
+  // Prints the given array, omitting some elements when there are too
+  // many.
+  static void Print(const T (&a)[N], ::std::ostream* os) {
+    UniversalPrintArray(a, N, os);
+  }
+};
+
+// Implements printing a reference type T&.
+template <typename T>
+class UniversalPrinter<T&> {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+#ifdef _MSC_VER
+# pragma warning(push)          // Saves the current warning state.
+# pragma warning(disable:4180)  // Temporarily disables warning 4180.
+#endif  // _MSC_VER
+
+  static void Print(const T& value, ::std::ostream* os) {
+    // Prints the address of the value.  We use reinterpret_cast here
+    // as static_cast doesn't compile when T is a function type.
+    *os << "@" << reinterpret_cast<const void*>(&value) << " ";
+
+    // Then prints the value itself.
+    UniversalPrint(value, os);
+  }
+
+#ifdef _MSC_VER
+# pragma warning(pop)           // Restores the warning state.
+#endif  // _MSC_VER
+};
+
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
+
+template <typename T>
+class UniversalTersePrinter {
+ public:
+  static void Print(const T& value, ::std::ostream* os) {
+    UniversalPrint(value, os);
+  }
+};
+template <typename T>
+class UniversalTersePrinter<T&> {
+ public:
+  static void Print(const T& value, ::std::ostream* os) {
+    UniversalPrint(value, os);
+  }
+};
+template <typename T, size_t N>
+class UniversalTersePrinter<T[N]> {
+ public:
+  static void Print(const T (&value)[N], ::std::ostream* os) {
+    UniversalPrinter<T[N]>::Print(value, os);
+  }
+};
+template <>
+class UniversalTersePrinter<const char*> {
+ public:
+  static void Print(const char* str, ::std::ostream* os) {
+    if (str == NULL) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char*> {
+ public:
+  static void Print(char* str, ::std::ostream* os) {
+    UniversalTersePrinter<const char*>::Print(str, os);
+  }
+};
+
+#if GTEST_HAS_STD_WSTRING
+template <>
+class UniversalTersePrinter<const wchar_t*> {
+ public:
+  static void Print(const wchar_t* str, ::std::ostream* os) {
+    if (str == NULL) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::wstring(str), os);
+    }
+  }
+};
+#endif
+
+template <>
+class UniversalTersePrinter<wchar_t*> {
+ public:
+  static void Print(wchar_t* str, ::std::ostream* os) {
+    UniversalTersePrinter<const wchar_t*>::Print(str, os);
+  }
+};
+
+template <typename T>
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
+  UniversalTersePrinter<T>::Print(value, os);
+}
+
+// Prints a value using the type inferred by the compiler.  The
+// difference between this and UniversalTersePrint() is that for a
+// (const) char pointer, this prints both the pointer and the
+// NUL-terminated string.
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os) {
+  // A workarond for the bug in VC++ 7.1 that prevents us from instantiating
+  // UniversalPrinter with T directly.
+  typedef T T1;
+  UniversalPrinter<T1>::Print(value, os);
+}
+
+#if GTEST_HAS_TR1_TUPLE
+typedef ::std::vector<string> Strings;
+
+// This helper template allows PrintTo() for tuples and
+// UniversalTersePrintTupleFieldsToStrings() to be defined by
+// induction on the number of tuple fields.  The idea is that
+// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
+// fields in tuple t, and can be defined in terms of
+// TuplePrefixPrinter<N - 1>.
+
+// The inductive case.
+template <size_t N>
+struct TuplePrefixPrinter {
+  // Prints the first N fields of a tuple.
+  template <typename Tuple>
+  static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+    TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
+    *os << ", ";
+    UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
+        ::Print(::std::tr1::get<N - 1>(t), os);
+  }
+
+  // Tersely prints the first N fields of a tuple to a string vector,
+  // one element for each field.
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+    TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
+    ::std::stringstream ss;
+    UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+    strings->push_back(ss.str());
+  }
+};
+
+// Base cases.
+template <>
+struct TuplePrefixPrinter<0> {
+  template <typename Tuple>
+  static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
+
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
+};
+// We have to specialize the entire TuplePrefixPrinter<> class
+// template here, even though the definition of
+// TersePrintPrefixToStrings() is the same as the generic version, as
+// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
+// support specializing a method template of a class template.
+template <>
+struct TuplePrefixPrinter<1> {
+  template <typename Tuple>
+  static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+    UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
+        Print(::std::tr1::get<0>(t), os);
+  }
+
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+    ::std::stringstream ss;
+    UniversalTersePrint(::std::tr1::get<0>(t), &ss);
+    strings->push_back(ss.str());
+  }
+};
+
+// Helper function for printing a tuple.  T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os) {
+  *os << "(";
+  TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
+      PrintPrefixTo(t, os);
+  *os << ")";
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field.  See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+  Strings result;
+  TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+      TersePrintPrefixToStrings(value, &result);
+  return result;
+}
+#endif  // GTEST_HAS_TR1_TUPLE
+
+}  // namespace internal
+
+template <typename T>
+::std::string PrintToString(const T& value) {
+  ::std::stringstream ss;
+  internal::UniversalTersePrinter<T>::Print(value, &ss);
+  return ss.str();
+}
+
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-spi.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-spi.h
new file mode 100644
index 0000000..f63fa9a
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-spi.h
@@ -0,0 +1,232 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// Utilities for testing Google Test itself and code that uses Google Test
+// (e.g. frameworks built on top of Google Test).
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// This helper class can be used to mock out Google Test failure reporting
+// so that we can test Google Test or code that builds on Google Test.
+//
+// An object of this class appends a TestPartResult object to the
+// TestPartResultArray object given in the constructor whenever a Google Test
+// failure is reported. It can either intercept only failures that are
+// generated in the same thread that created this object or it can intercept
+// all generated failures. The scope of this mock object can be controlled with
+// the second argument to the two arguments constructor.
+class GTEST_API_ ScopedFakeTestPartResultReporter
+    : public TestPartResultReporterInterface {
+ public:
+  // The two possible mocking modes of this object.
+  enum InterceptMode {
+    INTERCEPT_ONLY_CURRENT_THREAD,  // Intercepts only thread local failures.
+    INTERCEPT_ALL_THREADS           // Intercepts all failures.
+  };
+
+  // The c'tor sets this object as the test part result reporter used
+  // by Google Test.  The 'result' parameter specifies where to report the
+  // results. This reporter will only catch failures generated in the current
+  // thread. DEPRECATED
+  explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
+
+  // Same as above, but you can choose the interception scope of this object.
+  ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
+                                   TestPartResultArray* result);
+
+  // The d'tor restores the previous test part result reporter.
+  virtual ~ScopedFakeTestPartResultReporter();
+
+  // Appends the TestPartResult object to the TestPartResultArray
+  // received in the constructor.
+  //
+  // This method is from the TestPartResultReporterInterface
+  // interface.
+  virtual void ReportTestPartResult(const TestPartResult& result);
+ private:
+  void Init();
+
+  const InterceptMode intercept_mode_;
+  TestPartResultReporterInterface* old_reporter_;
+  TestPartResultArray* const result_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
+};
+
+namespace internal {
+
+// A helper class for implementing EXPECT_FATAL_FAILURE() and
+// EXPECT_NONFATAL_FAILURE().  Its destructor verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring.  If that's not the case, a
+// non-fatal failure will be generated.
+class GTEST_API_ SingleFailureChecker {
+ public:
+  // The constructor remembers the arguments.
+  SingleFailureChecker(const TestPartResultArray* results,
+                       TestPartResult::Type type,
+                       const string& substr);
+  ~SingleFailureChecker();
+ private:
+  const TestPartResultArray* const results_;
+  const TestPartResult::Type type_;
+  const string substr_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
+};
+
+}  // namespace internal
+
+}  // namespace testing
+
+// A set of macros for testing Google Test assertions or code that's expected
+// to generate Google Test fatal failures.  It verifies that the given
+// statement will cause exactly one fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+//   - 'statement' cannot reference local non-static variables or
+//     non-static members of the current object.
+//   - 'statement' cannot return a value.
+//   - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works.  The AcceptsMacroThatExpandsToUnprotectedComma test in
+// gtest_unittest.cc will fail to compile if we do that.
+#define EXPECT_FATAL_FAILURE(statement, substr) \
+  do { \
+    class GTestExpectFatalFailureHelper {\
+     public:\
+      static void Execute() { statement; }\
+    };\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+      GTestExpectFatalFailureHelper::Execute();\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+  do { \
+    class GTestExpectFatalFailureHelper {\
+     public:\
+      static void Execute() { statement; }\
+    };\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ALL_THREADS, &gtest_failures);\
+      GTestExpectFatalFailureHelper::Execute();\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+// A macro for testing Google Test assertions or code that's expected to
+// generate Google Test non-fatal failures.  It asserts that the given
+// statement will cause exactly one non-fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// 'statement' is allowed to reference local variables and members of
+// the current object.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+//   - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works.  If we do that, the code won't compile when the user gives
+// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
+// expands to code containing an unprotected comma.  The
+// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
+// catches that.
+//
+// For the same reason, we have to write
+//   if (::testing::internal::AlwaysTrue()) { statement; }
+// instead of
+//   GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+// to avoid an MSVC warning on unreachable code.
+#define EXPECT_NONFATAL_FAILURE(statement, substr) \
+  do {\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+        (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+      if (::testing::internal::AlwaysTrue()) { statement; }\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+  do {\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+        (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
+          &gtest_failures);\
+      if (::testing::internal::AlwaysTrue()) { statement; }\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-test-part.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-test-part.h
new file mode 100644
index 0000000..77eb844
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-test-part.h
@@ -0,0 +1,179 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: mheule at google.com (Markus Heule)
+//
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+
+#include <iosfwd>
+#include <vector>
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+
+// A copyable object representing the result of a test part (i.e. an
+// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
+//
+// Don't inherit from TestPartResult as its destructor is not virtual.
+class GTEST_API_ TestPartResult {
+ public:
+  // The possible outcomes of a test part (i.e. an assertion or an
+  // explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
+  enum Type {
+    kSuccess,          // Succeeded.
+    kNonFatalFailure,  // Failed but the test can continue.
+    kFatalFailure      // Failed and the test should be terminated.
+  };
+
+  // C'tor.  TestPartResult does NOT have a default constructor.
+  // Always use this constructor (with parameters) to create a
+  // TestPartResult object.
+  TestPartResult(Type a_type,
+                 const char* a_file_name,
+                 int a_line_number,
+                 const char* a_message)
+      : type_(a_type),
+        file_name_(a_file_name == NULL ? "" : a_file_name),
+        line_number_(a_line_number),
+        summary_(ExtractSummary(a_message)),
+        message_(a_message) {
+  }
+
+  // Gets the outcome of the test part.
+  Type type() const { return type_; }
+
+  // Gets the name of the source file where the test part took place, or
+  // NULL if it's unknown.
+  const char* file_name() const {
+    return file_name_.empty() ? NULL : file_name_.c_str();
+  }
+
+  // Gets the line in the source file where the test part took place,
+  // or -1 if it's unknown.
+  int line_number() const { return line_number_; }
+
+  // Gets the summary of the failure message.
+  const char* summary() const { return summary_.c_str(); }
+
+  // Gets the message associated with the test part.
+  const char* message() const { return message_.c_str(); }
+
+  // Returns true iff the test part passed.
+  bool passed() const { return type_ == kSuccess; }
+
+  // Returns true iff the test part failed.
+  bool failed() const { return type_ != kSuccess; }
+
+  // Returns true iff the test part non-fatally failed.
+  bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
+
+  // Returns true iff the test part fatally failed.
+  bool fatally_failed() const { return type_ == kFatalFailure; }
+
+ private:
+  Type type_;
+
+  // Gets the summary of the failure message by omitting the stack
+  // trace in it.
+  static std::string ExtractSummary(const char* message);
+
+  // The name of the source file where the test part took place, or
+  // "" if the source file is unknown.
+  std::string file_name_;
+  // The line in the source file where the test part took place, or -1
+  // if the line number is unknown.
+  int line_number_;
+  std::string summary_;  // The test failure summary.
+  std::string message_;  // The test failure message.
+};
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
+
+// An array of TestPartResult objects.
+//
+// Don't inherit from TestPartResultArray as its destructor is not
+// virtual.
+class GTEST_API_ TestPartResultArray {
+ public:
+  TestPartResultArray() {}
+
+  // Appends the given TestPartResult to the array.
+  void Append(const TestPartResult& result);
+
+  // Returns the TestPartResult at the given index (0-based).
+  const TestPartResult& GetTestPartResult(int index) const;
+
+  // Returns the number of TestPartResult objects in the array.
+  int size() const;
+
+ private:
+  std::vector<TestPartResult> array_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
+};
+
+// This interface knows how to report a test part result.
+class TestPartResultReporterInterface {
+ public:
+  virtual ~TestPartResultReporterInterface() {}
+
+  virtual void ReportTestPartResult(const TestPartResult& result) = 0;
+};
+
+namespace internal {
+
+// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
+// statement generates new fatal failures. To do so it registers itself as the
+// current test part result reporter. Besides checking if fatal failures were
+// reported, it only delegates the reporting to the former result reporter.
+// The original result reporter is restored in the destructor.
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+class GTEST_API_ HasNewFatalFailureHelper
+    : public TestPartResultReporterInterface {
+ public:
+  HasNewFatalFailureHelper();
+  virtual ~HasNewFatalFailureHelper();
+  virtual void ReportTestPartResult(const TestPartResult& result);
+  bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
+ private:
+  bool has_new_fatal_failure_;
+  TestPartResultReporterInterface* original_reporter_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
+};
+
+}  // namespace internal
+
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-typed-test.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-typed-test.h
new file mode 100644
index 0000000..fe1e83b
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest-typed-test.h
@@ -0,0 +1,259 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+
+// This header implements typed tests and type-parameterized tests.
+
+// Typed (aka type-driven) tests repeat the same test for types in a
+// list.  You must know which types you want to test with when writing
+// typed tests. Here's how you do it:
+
+#if 0
+
+// First, define a fixture class template.  It should be parameterized
+// by a type.  Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ public:
+  ...
+  typedef std::list<T> List;
+  static T shared_;
+  T value_;
+};
+
+// Next, associate a list of types with the test case, which will be
+// repeated for each type in the list.  The typedef is necessary for
+// the macro to parse correctly.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+TYPED_TEST_CASE(FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+//   TYPED_TEST_CASE(FooTest, int);
+
+// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
+// tests for this test case as you want.
+TYPED_TEST(FooTest, DoesBlah) {
+  // Inside a test, refer to TypeParam to get the type parameter.
+  // Since we are inside a derived class template, C++ requires use to
+  // visit the members of FooTest via 'this'.
+  TypeParam n = this->value_;
+
+  // To visit static members of the fixture, add the TestFixture::
+  // prefix.
+  n += TestFixture::shared_;
+
+  // To refer to typedefs in the fixture, add the "typename
+  // TestFixture::" prefix.
+  typename TestFixture::List values;
+  values.push_back(n);
+  ...
+}
+
+TYPED_TEST(FooTest, HasPropertyA) { ... }
+
+#endif  // 0
+
+// Type-parameterized tests are abstract test patterns parameterized
+// by a type.  Compared with typed tests, type-parameterized tests
+// allow you to define the test pattern without knowing what the type
+// parameters are.  The defined pattern can be instantiated with
+// different types any number of times, in any number of translation
+// units.
+//
+// If you are designing an interface or concept, you can define a
+// suite of type-parameterized tests to verify properties that any
+// valid implementation of the interface/concept should have.  Then,
+// each implementation can easily instantiate the test suite to verify
+// that it conforms to the requirements, without having to write
+// similar tests repeatedly.  Here's an example:
+
+#if 0
+
+// First, define a fixture class template.  It should be parameterized
+// by a type.  Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+  ...
+};
+
+// Next, declare that you will define a type-parameterized test case
+// (the _P suffix is for "parameterized" or "pattern", whichever you
+// prefer):
+TYPED_TEST_CASE_P(FooTest);
+
+// Then, use TYPED_TEST_P() to define as many type-parameterized tests
+// for this type-parameterized test case as you want.
+TYPED_TEST_P(FooTest, DoesBlah) {
+  // Inside a test, refer to TypeParam to get the type parameter.
+  TypeParam n = 0;
+  ...
+}
+
+TYPED_TEST_P(FooTest, HasPropertyA) { ... }
+
+// Now the tricky part: you need to register all test patterns before
+// you can instantiate them.  The first argument of the macro is the
+// test case name; the rest are the names of the tests in this test
+// case.
+REGISTER_TYPED_TEST_CASE_P(FooTest,
+                           DoesBlah, HasPropertyA);
+
+// Finally, you are free to instantiate the pattern with the types you
+// want.  If you put the above code in a header file, you can #include
+// it in multiple C++ source files and instantiate it multiple times.
+//
+// To distinguish different instances of the pattern, the first
+// argument to the INSTANTIATE_* macro is a prefix that will be added
+// to the actual test case name.  Remember to pick unique prefixes for
+// different instances.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+//   INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
+
+#endif  // 0
+
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Implements typed tests.
+
+#if GTEST_HAS_TYPED_TEST
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the typedef for the type parameters of the
+// given test case.
+# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define TYPED_TEST_CASE(CaseName, Types) \
+  typedef ::testing::internal::TypeList< Types >::type \
+      GTEST_TYPE_PARAMS_(CaseName)
+
+# define TYPED_TEST(CaseName, TestName) \
+  template <typename gtest_TypeParam_> \
+  class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
+      : public CaseName<gtest_TypeParam_> { \
+   private: \
+    typedef CaseName<gtest_TypeParam_> TestFixture; \
+    typedef gtest_TypeParam_ TypeParam; \
+    virtual void TestBody(); \
+  }; \
+  bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
+      ::testing::internal::TypeParameterizedTest< \
+          CaseName, \
+          ::testing::internal::TemplateSel< \
+              GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
+          GTEST_TYPE_PARAMS_(CaseName)>::Register(\
+              "", #CaseName, #TestName, 0); \
+  template <typename gtest_TypeParam_> \
+  void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
+
+#endif  // GTEST_HAS_TYPED_TEST
+
+// Implements type-parameterized tests.
+
+#if GTEST_HAS_TYPED_TEST_P
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the namespace name that the type-parameterized tests for
+// the given type-parameterized test case are defined in.  The exact
+// name of the namespace is subject to change without notice.
+# define GTEST_CASE_NAMESPACE_(TestCaseName) \
+  gtest_case_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the variable used to remember the names of
+// the defined tests in the given test case.
+# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
+  gtest_typed_test_case_p_state_##TestCaseName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
+//
+// Expands to the name of the variable used to remember the names of
+// the registered tests in the given test case.
+# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
+  gtest_registered_test_names_##TestCaseName##_
+
+// The variables defined in the type-parameterized test macros are
+// static as typically these macros are used in a .h file that can be
+// #included in multiple translation units linked together.
+# define TYPED_TEST_CASE_P(CaseName) \
+  static ::testing::internal::TypedTestCasePState \
+      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
+
+# define TYPED_TEST_P(CaseName, TestName) \
+  namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+  template <typename gtest_TypeParam_> \
+  class TestName : public CaseName<gtest_TypeParam_> { \
+   private: \
+    typedef CaseName<gtest_TypeParam_> TestFixture; \
+    typedef gtest_TypeParam_ TypeParam; \
+    virtual void TestBody(); \
+  }; \
+  static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
+      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
+          __FILE__, __LINE__, #CaseName, #TestName); \
+  } \
+  template <typename gtest_TypeParam_> \
+  void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
+
+# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \
+  namespace GTEST_CASE_NAMESPACE_(CaseName) { \
+  typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
+  } \
+  static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
+      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
+          __FILE__, __LINE__, #__VA_ARGS__)
+
+// The 'Types' template argument below must have spaces around it
+// since some compilers may choke on '>>' when passing a template
+// instance (e.g. Types<int>)
+# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
+  bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
+      ::testing::internal::TypeParameterizedTestCase<CaseName, \
+          GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
+          ::testing::internal::TypeList< Types >::type>::Register(\
+              #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
+
+#endif  // GTEST_HAS_TYPED_TEST_P
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest.h
new file mode 100644
index 0000000..6fa0a39
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest.h
@@ -0,0 +1,2291 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines the public API for Google Test.  It should be
+// included by any test program that uses Google Test.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+//   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
+// program!
+//
+// Acknowledgment: Google Test borrowed the idea of automatic test
+// registration from Barthelemy Dagenais' (barthelemy at prologique.com)
+// easyUnit framework.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+#define GTEST_INCLUDE_GTEST_GTEST_H_
+
+#include <limits>
+#include <ostream>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/gtest-death-test.h"
+#include "gtest/gtest-message.h"
+#include "gtest/gtest-param-test.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest_prod.h"
+#include "gtest/gtest-test-part.h"
+#include "gtest/gtest-typed-test.h"
+
+// Depending on the platform, different string classes are available.
+// On Linux, in addition to ::std::string, Google also makes use of
+// class ::string, which has the same interface as ::std::string, but
+// has a different implementation.
+//
+// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that
+// ::string is available AND is a distinct type to ::std::string, or
+// define it to 0 to indicate otherwise.
+//
+// If the user's ::std::string and ::string are the same class due to
+// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0.
+//
+// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined
+// heuristically.
+
+namespace testing {
+
+// Declares the flags.
+
+// This flag temporary enables the disabled tests.
+GTEST_DECLARE_bool_(also_run_disabled_tests);
+
+// This flag brings the debugger on an assertion failure.
+GTEST_DECLARE_bool_(break_on_failure);
+
+// This flag controls whether Google Test catches all test-thrown exceptions
+// and logs them as failures.
+GTEST_DECLARE_bool_(catch_exceptions);
+
+// This flag enables using colors in terminal output. Available values are
+// "yes" to enable colors, "no" (disable colors), or "auto" (the default)
+// to let Google Test decide.
+GTEST_DECLARE_string_(color);
+
+// This flag sets up the filter to select by name using a glob pattern
+// the tests to run. If the filter is not given all tests are executed.
+GTEST_DECLARE_string_(filter);
+
+// This flag causes the Google Test to list tests. None of the tests listed
+// are actually run if the flag is provided.
+GTEST_DECLARE_bool_(list_tests);
+
+// This flag controls whether Google Test emits a detailed XML report to a file
+// in addition to its normal textual output.
+GTEST_DECLARE_string_(output);
+
+// This flags control whether Google Test prints the elapsed time for each
+// test.
+GTEST_DECLARE_bool_(print_time);
+
+// This flag specifies the random number seed.
+GTEST_DECLARE_int32_(random_seed);
+
+// This flag sets how many times the tests are repeated. The default value
+// is 1. If the value is -1 the tests are repeating forever.
+GTEST_DECLARE_int32_(repeat);
+
+// This flag controls whether Google Test includes Google Test internal
+// stack frames in failure stack traces.
+GTEST_DECLARE_bool_(show_internal_stack_frames);
+
+// When this flag is specified, tests' order is randomized on every iteration.
+GTEST_DECLARE_bool_(shuffle);
+
+// This flag specifies the maximum number of stack frames to be
+// printed in a failure message.
+GTEST_DECLARE_int32_(stack_trace_depth);
+
+// When this flag is specified, a failed assertion will throw an
+// exception if exceptions are enabled, or exit the program with a
+// non-zero code otherwise.
+GTEST_DECLARE_bool_(throw_on_failure);
+
+// When this flag is set with a "host:port" string, on supported
+// platforms test results are streamed to the specified port on
+// the specified host machine.
+GTEST_DECLARE_string_(stream_result_to);
+
+// The upper limit for valid stack trace depths.
+const int kMaxStackTraceDepth = 100;
+
+namespace internal {
+
+class AssertHelper;
+class DefaultGlobalTestPartResultReporter;
+class ExecDeathTest;
+class NoExecDeathTest;
+class FinalSuccessChecker;
+class GTestFlagSaver;
+class StreamingListenerTest;
+class TestResultAccessor;
+class TestEventListenersAccessor;
+class TestEventRepeater;
+class UnitTestRecordPropertyTestHelper;
+class WindowsDeathTest;
+class UnitTestImpl* GetUnitTestImpl();
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+                                    const std::string& message);
+
+}  // namespace internal
+
+// The friend relationship of some of these classes is cyclic.
+// If we don't forward declare them the compiler might confuse the classes
+// in friendship clauses with same named classes on the scope.
+class Test;
+class TestCase;
+class TestInfo;
+class UnitTest;
+
+// A class for indicating whether an assertion was successful.  When
+// the assertion wasn't successful, the AssertionResult object
+// remembers a non-empty message that describes how it failed.
+//
+// To create an instance of this class, use one of the factory functions
+// (AssertionSuccess() and AssertionFailure()).
+//
+// This class is useful for two purposes:
+//   1. Defining predicate functions to be used with Boolean test assertions
+//      EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
+//   2. Defining predicate-format functions to be
+//      used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
+//
+// For example, if you define IsEven predicate:
+//
+//   testing::AssertionResult IsEven(int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess();
+//     else
+//       return testing::AssertionFailure() << n << " is odd";
+//   }
+//
+// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
+// will print the message
+//
+//   Value of: IsEven(Fib(5))
+//     Actual: false (5 is odd)
+//   Expected: true
+//
+// instead of a more opaque
+//
+//   Value of: IsEven(Fib(5))
+//     Actual: false
+//   Expected: true
+//
+// in case IsEven is a simple Boolean predicate.
+//
+// If you expect your predicate to be reused and want to support informative
+// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
+// about half as often as positive ones in our tests), supply messages for
+// both success and failure cases:
+//
+//   testing::AssertionResult IsEven(int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess() << n << " is even";
+//     else
+//       return testing::AssertionFailure() << n << " is odd";
+//   }
+//
+// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
+//
+//   Value of: IsEven(Fib(6))
+//     Actual: true (8 is even)
+//   Expected: false
+//
+// NB: Predicates that support negative Boolean assertions have reduced
+// performance in positive ones so be careful not to use them in tests
+// that have lots (tens of thousands) of positive Boolean assertions.
+//
+// To use this class with EXPECT_PRED_FORMAT assertions such as:
+//
+//   // Verifies that Foo() returns an even number.
+//   EXPECT_PRED_FORMAT1(IsEven, Foo());
+//
+// you need to define:
+//
+//   testing::AssertionResult IsEven(const char* expr, int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess();
+//     else
+//       return testing::AssertionFailure()
+//         << "Expected: " << expr << " is even\n  Actual: it's " << n;
+//   }
+//
+// If Foo() returns 5, you will see the following message:
+//
+//   Expected: Foo() is even
+//     Actual: it's 5
+//
+class GTEST_API_ AssertionResult {
+ public:
+  // Copy constructor.
+  // Used in EXPECT_TRUE/FALSE(assertion_result).
+  AssertionResult(const AssertionResult& other);
+  // Used in the EXPECT_TRUE/FALSE(bool_expression).
+  explicit AssertionResult(bool success) : success_(success) {}
+
+  // Returns true iff the assertion succeeded.
+  operator bool() const { return success_; }  // NOLINT
+
+  // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+  AssertionResult operator!() const;
+
+  // Returns the text streamed into this AssertionResult. Test assertions
+  // use it when they fail (i.e., the predicate's outcome doesn't match the
+  // assertion's expectation). When nothing has been streamed into the
+  // object, returns an empty string.
+  const char* message() const {
+    return message_.get() != NULL ?  message_->c_str() : "";
+  }
+  // TODO(vladl at google.com): Remove this after making sure no clients use it.
+  // Deprecated; please use message() instead.
+  const char* failure_message() const { return message(); }
+
+  // Streams a custom failure message into this object.
+  template <typename T> AssertionResult& operator<<(const T& value) {
+    AppendMessage(Message() << value);
+    return *this;
+  }
+
+  // Allows streaming basic output manipulators such as endl or flush into
+  // this object.
+  AssertionResult& operator<<(
+      ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
+    AppendMessage(Message() << basic_manipulator);
+    return *this;
+  }
+
+ private:
+  // Appends the contents of message to message_.
+  void AppendMessage(const Message& a_message) {
+    if (message_.get() == NULL)
+      message_.reset(new ::std::string);
+    message_->append(a_message.GetString().c_str());
+  }
+
+  // Stores result of the assertion predicate.
+  bool success_;
+  // Stores the message describing the condition in case the expectation
+  // construct is not satisfied with the predicate's outcome.
+  // Referenced via a pointer to avoid taking too much stack frame space
+  // with test assertions.
+  internal::scoped_ptr< ::std::string> message_;
+
+  GTEST_DISALLOW_ASSIGN_(AssertionResult);
+};
+
+// Makes a successful assertion result.
+GTEST_API_ AssertionResult AssertionSuccess();
+
+// Makes a failed assertion result.
+GTEST_API_ AssertionResult AssertionFailure();
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << msg.
+GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
+
+// The abstract class that all tests inherit from.
+//
+// In Google Test, a unit test program contains one or many TestCases, and
+// each TestCase contains one or many Tests.
+//
+// When you define a test using the TEST macro, you don't need to
+// explicitly derive from Test - the TEST macro automatically does
+// this for you.
+//
+// The only time you derive from Test is when defining a test fixture
+// to be used a TEST_F.  For example:
+//
+//   class FooTest : public testing::Test {
+//    protected:
+//     virtual void SetUp() { ... }
+//     virtual void TearDown() { ... }
+//     ...
+//   };
+//
+//   TEST_F(FooTest, Bar) { ... }
+//   TEST_F(FooTest, Baz) { ... }
+//
+// Test is not copyable.
+class GTEST_API_ Test {
+ public:
+  friend class TestInfo;
+
+  // Defines types for pointers to functions that set up and tear down
+  // a test case.
+  typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc;
+  typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc;
+
+  // The d'tor is virtual as we intend to inherit from Test.
+  virtual ~Test();
+
+  // Sets up the stuff shared by all tests in this test case.
+  //
+  // Google Test will call Foo::SetUpTestCase() before running the first
+  // test in test case Foo.  Hence a sub-class can define its own
+  // SetUpTestCase() method to shadow the one defined in the super
+  // class.
+  static void SetUpTestCase() {}
+
+  // Tears down the stuff shared by all tests in this test case.
+  //
+  // Google Test will call Foo::TearDownTestCase() after running the last
+  // test in test case Foo.  Hence a sub-class can define its own
+  // TearDownTestCase() method to shadow the one defined in the super
+  // class.
+  static void TearDownTestCase() {}
+
+  // Returns true iff the current test has a fatal failure.
+  static bool HasFatalFailure();
+
+  // Returns true iff the current test has a non-fatal failure.
+  static bool HasNonfatalFailure();
+
+  // Returns true iff the current test has a (either fatal or
+  // non-fatal) failure.
+  static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
+
+  // Logs a property for the current test, test case, or for the entire
+  // invocation of the test program when used outside of the context of a
+  // test case.  Only the last value for a given key is remembered.  These
+  // are public static so they can be called from utility functions that are
+  // not members of the test fixture.  Calls to RecordProperty made during
+  // lifespan of the test (from the moment its constructor starts to the
+  // moment its destructor finishes) will be output in XML as attributes of
+  // the <testcase> element.  Properties recorded from fixture's
+  // SetUpTestCase or TearDownTestCase are logged as attributes of the
+  // corresponding <testsuite> element.  Calls to RecordProperty made in the
+  // global context (before or after invocation of RUN_ALL_TESTS and from
+  // SetUp/TearDown method of Environment objects registered with Google
+  // Test) will be output as attributes of the <testsuites> element.
+  static void RecordProperty(const std::string& key, const std::string& value);
+  static void RecordProperty(const std::string& key, int value);
+
+ protected:
+  // Creates a Test object.
+  Test();
+
+  // Sets up the test fixture.
+  virtual void SetUp();
+
+  // Tears down the test fixture.
+  virtual void TearDown();
+
+ private:
+  // Returns true iff the current test has the same fixture class as
+  // the first test in the current test case.
+  static bool HasSameFixtureClass();
+
+  // Runs the test after the test fixture has been set up.
+  //
+  // A sub-class must implement this to define the test logic.
+  //
+  // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM.
+  // Instead, use the TEST or TEST_F macro.
+  virtual void TestBody() = 0;
+
+  // Sets up, executes, and tears down the test.
+  void Run();
+
+  // Deletes self.  We deliberately pick an unusual name for this
+  // internal method to avoid clashing with names used in user TESTs.
+  void DeleteSelf_() { delete this; }
+
+  // Uses a GTestFlagSaver to save and restore all Google Test flags.
+  const internal::GTestFlagSaver* const gtest_flag_saver_;
+
+  // Often a user mis-spells SetUp() as Setup() and spends a long time
+  // wondering why it is never called by Google Test.  The declaration of
+  // the following method is solely for catching such an error at
+  // compile time:
+  //
+  //   - The return type is deliberately chosen to be not void, so it
+  //   will be a conflict if a user declares void Setup() in his test
+  //   fixture.
+  //
+  //   - This method is private, so it will be another compiler error
+  //   if a user calls it from his test fixture.
+  //
+  // DO NOT OVERRIDE THIS FUNCTION.
+  //
+  // If you see an error about overriding the following function or
+  // about it being private, you have mis-spelled SetUp() as Setup().
+  struct Setup_should_be_spelled_SetUp {};
+  virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+
+  // We disallow copying Tests.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
+};
+
+typedef internal::TimeInMillis TimeInMillis;
+
+// A copyable object representing a user specified test property which can be
+// output as a key/value string pair.
+//
+// Don't inherit from TestProperty as its destructor is not virtual.
+class TestProperty {
+ public:
+  // C'tor.  TestProperty does NOT have a default constructor.
+  // Always use this constructor (with parameters) to create a
+  // TestProperty object.
+  TestProperty(const std::string& a_key, const std::string& a_value) :
+    key_(a_key), value_(a_value) {
+  }
+
+  // Gets the user supplied key.
+  const char* key() const {
+    return key_.c_str();
+  }
+
+  // Gets the user supplied value.
+  const char* value() const {
+    return value_.c_str();
+  }
+
+  // Sets a new value, overriding the one supplied in the constructor.
+  void SetValue(const std::string& new_value) {
+    value_ = new_value;
+  }
+
+ private:
+  // The key supplied by the user.
+  std::string key_;
+  // The value supplied by the user.
+  std::string value_;
+};
+
+// The result of a single Test.  This includes a list of
+// TestPartResults, a list of TestProperties, a count of how many
+// death tests there are in the Test, and how much time it took to run
+// the Test.
+//
+// TestResult is not copyable.
+class GTEST_API_ TestResult {
+ public:
+  // Creates an empty TestResult.
+  TestResult();
+
+  // D'tor.  Do not inherit from TestResult.
+  ~TestResult();
+
+  // Gets the number of all test parts.  This is the sum of the number
+  // of successful test parts and the number of failed test parts.
+  int total_part_count() const;
+
+  // Returns the number of the test properties.
+  int test_property_count() const;
+
+  // Returns true iff the test passed (i.e. no test part failed).
+  bool Passed() const { return !Failed(); }
+
+  // Returns true iff the test failed.
+  bool Failed() const;
+
+  // Returns true iff the test fatally failed.
+  bool HasFatalFailure() const;
+
+  // Returns true iff the test has a non-fatal failure.
+  bool HasNonfatalFailure() const;
+
+  // Returns the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+  // Returns the i-th test part result among all the results. i can range
+  // from 0 to test_property_count() - 1. If i is not in that range, aborts
+  // the program.
+  const TestPartResult& GetTestPartResult(int i) const;
+
+  // Returns the i-th test property. i can range from 0 to
+  // test_property_count() - 1. If i is not in that range, aborts the
+  // program.
+  const TestProperty& GetTestProperty(int i) const;
+
+ private:
+  friend class TestInfo;
+  friend class TestCase;
+  friend class UnitTest;
+  friend class internal::DefaultGlobalTestPartResultReporter;
+  friend class internal::ExecDeathTest;
+  friend class internal::TestResultAccessor;
+  friend class internal::UnitTestImpl;
+  friend class internal::WindowsDeathTest;
+
+  // Gets the vector of TestPartResults.
+  const std::vector<TestPartResult>& test_part_results() const {
+    return test_part_results_;
+  }
+
+  // Gets the vector of TestProperties.
+  const std::vector<TestProperty>& test_properties() const {
+    return test_properties_;
+  }
+
+  // Sets the elapsed time.
+  void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; }
+
+  // Adds a test property to the list. The property is validated and may add
+  // a non-fatal failure if invalid (e.g., if it conflicts with reserved
+  // key names). If a property is already recorded for the same key, the
+  // value will be updated, rather than storing multiple values for the same
+  // key.  xml_element specifies the element for which the property is being
+  // recorded and is used for validation.
+  void RecordProperty(const std::string& xml_element,
+                      const TestProperty& test_property);
+
+  // Adds a failure if the key is a reserved attribute of Google Test
+  // testcase tags.  Returns true if the property is valid.
+  // TODO(russr): Validate attribute names are legal and human readable.
+  static bool ValidateTestProperty(const std::string& xml_element,
+                                   const TestProperty& test_property);
+
+  // Adds a test part result to the list.
+  void AddTestPartResult(const TestPartResult& test_part_result);
+
+  // Returns the death test count.
+  int death_test_count() const { return death_test_count_; }
+
+  // Increments the death test count, returning the new count.
+  int increment_death_test_count() { return ++death_test_count_; }
+
+  // Clears the test part results.
+  void ClearTestPartResults();
+
+  // Clears the object.
+  void Clear();
+
+  // Protects mutable state of the property vector and of owned
+  // properties, whose values may be updated.
+  internal::Mutex test_properites_mutex_;
+
+  // The vector of TestPartResults
+  std::vector<TestPartResult> test_part_results_;
+  // The vector of TestProperties
+  std::vector<TestProperty> test_properties_;
+  // Running count of death tests.
+  int death_test_count_;
+  // The elapsed time, in milliseconds.
+  TimeInMillis elapsed_time_;
+
+  // We disallow copying TestResult.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult);
+};  // class TestResult
+
+// A TestInfo object stores the following information about a test:
+//
+//   Test case name
+//   Test name
+//   Whether the test should be run
+//   A function pointer that creates the test object when invoked
+//   Test result
+//
+// The constructor of TestInfo registers itself with the UnitTest
+// singleton such that the RUN_ALL_TESTS() macro knows which tests to
+// run.
+class GTEST_API_ TestInfo {
+ public:
+  // Destructs a TestInfo object.  This function is not virtual, so
+  // don't inherit from TestInfo.
+  ~TestInfo();
+
+  // Returns the test case name.
+  const char* test_case_name() const { return test_case_name_.c_str(); }
+
+  // Returns the test name.
+  const char* name() const { return name_.c_str(); }
+
+  // Returns the name of the parameter type, or NULL if this is not a typed
+  // or a type-parameterized test.
+  const char* type_param() const {
+    if (type_param_.get() != NULL)
+      return type_param_->c_str();
+    return NULL;
+  }
+
+  // Returns the text representation of the value parameter, or NULL if this
+  // is not a value-parameterized test.
+  const char* value_param() const {
+    if (value_param_.get() != NULL)
+      return value_param_->c_str();
+    return NULL;
+  }
+
+  // Returns true if this test should run, that is if the test is not
+  // disabled (or it is disabled but the also_run_disabled_tests flag has
+  // been specified) and its full name matches the user-specified filter.
+  //
+  // Google Test allows the user to filter the tests by their full names.
+  // The full name of a test Bar in test case Foo is defined as
+  // "Foo.Bar".  Only the tests that match the filter will run.
+  //
+  // A filter is a colon-separated list of glob (not regex) patterns,
+  // optionally followed by a '-' and a colon-separated list of
+  // negative patterns (tests to exclude).  A test is run if it
+  // matches one of the positive patterns and does not match any of
+  // the negative patterns.
+  //
+  // For example, *A*:Foo.* is a filter that matches any string that
+  // contains the character 'A' or starts with "Foo.".
+  bool should_run() const { return should_run_; }
+
+  // Returns true iff this test will appear in the XML report.
+  bool is_reportable() const {
+    // For now, the XML report includes all tests matching the filter.
+    // In the future, we may trim tests that are excluded because of
+    // sharding.
+    return matches_filter_;
+  }
+
+  // Returns the result of the test.
+  const TestResult* result() const { return &result_; }
+
+ private:
+#if GTEST_HAS_DEATH_TEST
+  friend class internal::DefaultDeathTestFactory;
+#endif  // GTEST_HAS_DEATH_TEST
+  friend class Test;
+  friend class TestCase;
+  friend class internal::UnitTestImpl;
+  friend class internal::StreamingListenerTest;
+  friend TestInfo* internal::MakeAndRegisterTestInfo(
+      const char* test_case_name,
+      const char* name,
+      const char* type_param,
+      const char* value_param,
+      internal::TypeId fixture_class_id,
+      Test::SetUpTestCaseFunc set_up_tc,
+      Test::TearDownTestCaseFunc tear_down_tc,
+      internal::TestFactoryBase* factory);
+
+  // Constructs a TestInfo object. The newly constructed instance assumes
+  // ownership of the factory object.
+  TestInfo(const std::string& test_case_name,
+           const std::string& name,
+           const char* a_type_param,   // NULL if not a type-parameterized test
+           const char* a_value_param,  // NULL if not a value-parameterized test
+           internal::TypeId fixture_class_id,
+           internal::TestFactoryBase* factory);
+
+  // Increments the number of death tests encountered in this test so
+  // far.
+  int increment_death_test_count() {
+    return result_.increment_death_test_count();
+  }
+
+  // Creates the test object, runs it, records its result, and then
+  // deletes it.
+  void Run();
+
+  static void ClearTestResult(TestInfo* test_info) {
+    test_info->result_.Clear();
+  }
+
+  // These fields are immutable properties of the test.
+  const std::string test_case_name_;     // Test case name
+  const std::string name_;               // Test name
+  // Name of the parameter type, or NULL if this is not a typed or a
+  // type-parameterized test.
+  const internal::scoped_ptr<const ::std::string> type_param_;
+  // Text representation of the value parameter, or NULL if this is not a
+  // value-parameterized test.
+  const internal::scoped_ptr<const ::std::string> value_param_;
+  const internal::TypeId fixture_class_id_;   // ID of the test fixture class
+  bool should_run_;                 // True iff this test should run
+  bool is_disabled_;                // True iff this test is disabled
+  bool matches_filter_;             // True if this test matches the
+                                    // user-specified filter.
+  internal::TestFactoryBase* const factory_;  // The factory that creates
+                                              // the test object
+
+  // This field is mutable and needs to be reset before running the
+  // test for the second time.
+  TestResult result_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo);
+};
+
+// A test case, which consists of a vector of TestInfos.
+//
+// TestCase is not copyable.
+class GTEST_API_ TestCase {
+ public:
+  // Creates a TestCase with the given name.
+  //
+  // TestCase does NOT have a default constructor.  Always use this
+  // constructor to create a TestCase object.
+  //
+  // Arguments:
+  //
+  //   name:         name of the test case
+  //   a_type_param: the name of the test's type parameter, or NULL if
+  //                 this is not a type-parameterized test.
+  //   set_up_tc:    pointer to the function that sets up the test case
+  //   tear_down_tc: pointer to the function that tears down the test case
+  TestCase(const char* name, const char* a_type_param,
+           Test::SetUpTestCaseFunc set_up_tc,
+           Test::TearDownTestCaseFunc tear_down_tc);
+
+  // Destructor of TestCase.
+  virtual ~TestCase();
+
+  // Gets the name of the TestCase.
+  const char* name() const { return name_.c_str(); }
+
+  // Returns the name of the parameter type, or NULL if this is not a
+  // type-parameterized test case.
+  const char* type_param() const {
+    if (type_param_.get() != NULL)
+      return type_param_->c_str();
+    return NULL;
+  }
+
+  // Returns true if any test in this test case should run.
+  bool should_run() const { return should_run_; }
+
+  // Gets the number of successful tests in this test case.
+  int successful_test_count() const;
+
+  // Gets the number of failed tests in this test case.
+  int failed_test_count() const;
+
+  // Gets the number of disabled tests that will be reported in the XML report.
+  int reportable_disabled_test_count() const;
+
+  // Gets the number of disabled tests in this test case.
+  int disabled_test_count() const;
+
+  // Gets the number of tests to be printed in the XML report.
+  int reportable_test_count() const;
+
+  // Get the number of tests in this test case that should run.
+  int test_to_run_count() const;
+
+  // Gets the number of all tests in this test case.
+  int total_test_count() const;
+
+  // Returns true iff the test case passed.
+  bool Passed() const { return !Failed(); }
+
+  // Returns true iff the test case failed.
+  bool Failed() const { return failed_test_count() > 0; }
+
+  // Returns the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+  // Returns the i-th test among all the tests. i can range from 0 to
+  // total_test_count() - 1. If i is not in that range, returns NULL.
+  const TestInfo* GetTestInfo(int i) const;
+
+  // Returns the TestResult that holds test properties recorded during
+  // execution of SetUpTestCase and TearDownTestCase.
+  const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
+
+ private:
+  friend class Test;
+  friend class internal::UnitTestImpl;
+
+  // Gets the (mutable) vector of TestInfos in this TestCase.
+  std::vector<TestInfo*>& test_info_list() { return test_info_list_; }
+
+  // Gets the (immutable) vector of TestInfos in this TestCase.
+  const std::vector<TestInfo*>& test_info_list() const {
+    return test_info_list_;
+  }
+
+  // Returns the i-th test among all the tests. i can range from 0 to
+  // total_test_count() - 1. If i is not in that range, returns NULL.
+  TestInfo* GetMutableTestInfo(int i);
+
+  // Sets the should_run member.
+  void set_should_run(bool should) { should_run_ = should; }
+
+  // Adds a TestInfo to this test case.  Will delete the TestInfo upon
+  // destruction of the TestCase object.
+  void AddTestInfo(TestInfo * test_info);
+
+  // Clears the results of all tests in this test case.
+  void ClearResult();
+
+  // Clears the results of all tests in the given test case.
+  static void ClearTestCaseResult(TestCase* test_case) {
+    test_case->ClearResult();
+  }
+
+  // Runs every test in this TestCase.
+  void Run();
+
+  // Runs SetUpTestCase() for this TestCase.  This wrapper is needed
+  // for catching exceptions thrown from SetUpTestCase().
+  void RunSetUpTestCase() { (*set_up_tc_)(); }
+
+  // Runs TearDownTestCase() for this TestCase.  This wrapper is
+  // needed for catching exceptions thrown from TearDownTestCase().
+  void RunTearDownTestCase() { (*tear_down_tc_)(); }
+
+  // Returns true iff test passed.
+  static bool TestPassed(const TestInfo* test_info) {
+    return test_info->should_run() && test_info->result()->Passed();
+  }
+
+  // Returns true iff test failed.
+  static bool TestFailed(const TestInfo* test_info) {
+    return test_info->should_run() && test_info->result()->Failed();
+  }
+
+  // Returns true iff the test is disabled and will be reported in the XML
+  // report.
+  static bool TestReportableDisabled(const TestInfo* test_info) {
+    return test_info->is_reportable() && test_info->is_disabled_;
+  }
+
+  // Returns true iff test is disabled.
+  static bool TestDisabled(const TestInfo* test_info) {
+    return test_info->is_disabled_;
+  }
+
+  // Returns true iff this test will appear in the XML report.
+  static bool TestReportable(const TestInfo* test_info) {
+    return test_info->is_reportable();
+  }
+
+  // Returns true if the given test should run.
+  static bool ShouldRunTest(const TestInfo* test_info) {
+    return test_info->should_run();
+  }
+
+  // Shuffles the tests in this test case.
+  void ShuffleTests(internal::Random* random);
+
+  // Restores the test order to before the first shuffle.
+  void UnshuffleTests();
+
+  // Name of the test case.
+  std::string name_;
+  // Name of the parameter type, or NULL if this is not a typed or a
+  // type-parameterized test.
+  const internal::scoped_ptr<const ::std::string> type_param_;
+  // The vector of TestInfos in their original order.  It owns the
+  // elements in the vector.
+  std::vector<TestInfo*> test_info_list_;
+  // Provides a level of indirection for the test list to allow easy
+  // shuffling and restoring the test order.  The i-th element in this
+  // vector is the index of the i-th test in the shuffled test list.
+  std::vector<int> test_indices_;
+  // Pointer to the function that sets up the test case.
+  Test::SetUpTestCaseFunc set_up_tc_;
+  // Pointer to the function that tears down the test case.
+  Test::TearDownTestCaseFunc tear_down_tc_;
+  // True iff any test in this test case should run.
+  bool should_run_;
+  // Elapsed time, in milliseconds.
+  TimeInMillis elapsed_time_;
+  // Holds test properties recorded during execution of SetUpTestCase and
+  // TearDownTestCase.
+  TestResult ad_hoc_test_result_;
+
+  // We disallow copying TestCases.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase);
+};
+
+// An Environment object is capable of setting up and tearing down an
+// environment.  The user should subclass this to define his own
+// environment(s).
+//
+// An Environment object does the set-up and tear-down in virtual
+// methods SetUp() and TearDown() instead of the constructor and the
+// destructor, as:
+//
+//   1. You cannot safely throw from a destructor.  This is a problem
+//      as in some cases Google Test is used where exceptions are enabled, and
+//      we may want to implement ASSERT_* using exceptions where they are
+//      available.
+//   2. You cannot use ASSERT_* directly in a constructor or
+//      destructor.
+class Environment {
+ public:
+  // The d'tor is virtual as we need to subclass Environment.
+  virtual ~Environment() {}
+
+  // Override this to define how to set up the environment.
+  virtual void SetUp() {}
+
+  // Override this to define how to tear down the environment.
+  virtual void TearDown() {}
+ private:
+  // If you see an error about overriding the following function or
+  // about it being private, you have mis-spelled SetUp() as Setup().
+  struct Setup_should_be_spelled_SetUp {};
+  virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
+};
+
+// The interface for tracing execution of tests. The methods are organized in
+// the order the corresponding events are fired.
+class TestEventListener {
+ public:
+  virtual ~TestEventListener() {}
+
+  // Fired before any test activity starts.
+  virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;
+
+  // Fired before each iteration of tests starts.  There may be more than
+  // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration
+  // index, starting from 0.
+  virtual void OnTestIterationStart(const UnitTest& unit_test,
+                                    int iteration) = 0;
+
+  // Fired before environment set-up for each iteration of tests starts.
+  virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;
+
+  // Fired after environment set-up for each iteration of tests ends.
+  virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
+
+  // Fired before the test case starts.
+  virtual void OnTestCaseStart(const TestCase& test_case) = 0;
+
+  // Fired before the test starts.
+  virtual void OnTestStart(const TestInfo& test_info) = 0;
+
+  // Fired after a failed assertion or a SUCCEED() invocation.
+  virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
+
+  // Fired after the test ends.
+  virtual void OnTestEnd(const TestInfo& test_info) = 0;
+
+  // Fired after the test case ends.
+  virtual void OnTestCaseEnd(const TestCase& test_case) = 0;
+
+  // Fired before environment tear-down for each iteration of tests starts.
+  virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
+
+  // Fired after environment tear-down for each iteration of tests ends.
+  virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;
+
+  // Fired after each iteration of tests finishes.
+  virtual void OnTestIterationEnd(const UnitTest& unit_test,
+                                  int iteration) = 0;
+
+  // Fired after all test activities have ended.
+  virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;
+};
+
+// The convenience class for users who need to override just one or two
+// methods and are not concerned that a possible change to a signature of
+// the methods they override will not be caught during the build.  For
+// comments about each method please see the definition of TestEventListener
+// above.
+class EmptyTestEventListener : public TestEventListener {
+ public:
+  virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {}
+  virtual void OnTestIterationStart(const UnitTest& /*unit_test*/,
+                                    int /*iteration*/) {}
+  virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {}
+  virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {}
+  virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
+  virtual void OnTestStart(const TestInfo& /*test_info*/) {}
+  virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {}
+  virtual void OnTestEnd(const TestInfo& /*test_info*/) {}
+  virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
+  virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {}
+  virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {}
+  virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/,
+                                  int /*iteration*/) {}
+  virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {}
+};
+
+// TestEventListeners lets users add listeners to track events in Google Test.
+class GTEST_API_ TestEventListeners {
+ public:
+  TestEventListeners();
+  ~TestEventListeners();
+
+  // Appends an event listener to the end of the list. Google Test assumes
+  // the ownership of the listener (i.e. it will delete the listener when
+  // the test program finishes).
+  void Append(TestEventListener* listener);
+
+  // Removes the given event listener from the list and returns it.  It then
+  // becomes the caller's responsibility to delete the listener. Returns
+  // NULL if the listener is not found in the list.
+  TestEventListener* Release(TestEventListener* listener);
+
+  // Returns the standard listener responsible for the default console
+  // output.  Can be removed from the listeners list to shut down default
+  // console output.  Note that removing this object from the listener list
+  // with Release transfers its ownership to the caller and makes this
+  // function return NULL the next time.
+  TestEventListener* default_result_printer() const {
+    return default_result_printer_;
+  }
+
+  // Returns the standard listener responsible for the default XML output
+  // controlled by the --gtest_output=xml flag.  Can be removed from the
+  // listeners list by users who want to shut down the default XML output
+  // controlled by this flag and substitute it with custom one.  Note that
+  // removing this object from the listener list with Release transfers its
+  // ownership to the caller and makes this function return NULL the next
+  // time.
+  TestEventListener* default_xml_generator() const {
+    return default_xml_generator_;
+  }
+
+ private:
+  friend class TestCase;
+  friend class TestInfo;
+  friend class internal::DefaultGlobalTestPartResultReporter;
+  friend class internal::NoExecDeathTest;
+  friend class internal::TestEventListenersAccessor;
+  friend class internal::UnitTestImpl;
+
+  // Returns repeater that broadcasts the TestEventListener events to all
+  // subscribers.
+  TestEventListener* repeater();
+
+  // Sets the default_result_printer attribute to the provided listener.
+  // The listener is also added to the listener list and previous
+  // default_result_printer is removed from it and deleted. The listener can
+  // also be NULL in which case it will not be added to the list. Does
+  // nothing if the previous and the current listener objects are the same.
+  void SetDefaultResultPrinter(TestEventListener* listener);
+
+  // Sets the default_xml_generator attribute to the provided listener.  The
+  // listener is also added to the listener list and previous
+  // default_xml_generator is removed from it and deleted. The listener can
+  // also be NULL in which case it will not be added to the list. Does
+  // nothing if the previous and the current listener objects are the same.
+  void SetDefaultXmlGenerator(TestEventListener* listener);
+
+  // Controls whether events will be forwarded by the repeater to the
+  // listeners in the list.
+  bool EventForwardingEnabled() const;
+  void SuppressEventForwarding();
+
+  // The actual list of listeners.
+  internal::TestEventRepeater* repeater_;
+  // Listener responsible for the standard result output.
+  TestEventListener* default_result_printer_;
+  // Listener responsible for the creation of the XML output file.
+  TestEventListener* default_xml_generator_;
+
+  // We disallow copying TestEventListeners.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners);
+};
+
+// A UnitTest consists of a vector of TestCases.
+//
+// This is a singleton class.  The only instance of UnitTest is
+// created when UnitTest::GetInstance() is first called.  This
+// instance is never deleted.
+//
+// UnitTest is not copyable.
+//
+// This class is thread-safe as long as the methods are called
+// according to their specification.
+class GTEST_API_ UnitTest {
+ public:
+  // Gets the singleton UnitTest object.  The first time this method
+  // is called, a UnitTest object is constructed and returned.
+  // Consecutive calls will return the same object.
+  static UnitTest* GetInstance();
+
+  // Runs all tests in this UnitTest object and prints the result.
+  // Returns 0 if successful, or 1 otherwise.
+  //
+  // This method can only be called from the main thread.
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  int Run() GTEST_MUST_USE_RESULT_;
+
+  // Returns the working directory when the first TEST() or TEST_F()
+  // was executed.  The UnitTest object owns the string.
+  const char* original_working_dir() const;
+
+  // Returns the TestCase object for the test that's currently running,
+  // or NULL if no test is running.
+  const TestCase* current_test_case() const
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Returns the TestInfo object for the test that's currently running,
+  // or NULL if no test is running.
+  const TestInfo* current_test_info() const
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Returns the random seed used at the start of the current test run.
+  int random_seed() const;
+
+#if GTEST_HAS_PARAM_TEST
+  // Returns the ParameterizedTestCaseRegistry object used to keep track of
+  // value-parameterized tests and instantiate and register them.
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  internal::ParameterizedTestCaseRegistry& parameterized_test_registry()
+      GTEST_LOCK_EXCLUDED_(mutex_);
+#endif  // GTEST_HAS_PARAM_TEST
+
+  // Gets the number of successful test cases.
+  int successful_test_case_count() const;
+
+  // Gets the number of failed test cases.
+  int failed_test_case_count() const;
+
+  // Gets the number of all test cases.
+  int total_test_case_count() const;
+
+  // Gets the number of all test cases that contain at least one test
+  // that should run.
+  int test_case_to_run_count() const;
+
+  // Gets the number of successful tests.
+  int successful_test_count() const;
+
+  // Gets the number of failed tests.
+  int failed_test_count() const;
+
+  // Gets the number of disabled tests that will be reported in the XML report.
+  int reportable_disabled_test_count() const;
+
+  // Gets the number of disabled tests.
+  int disabled_test_count() const;
+
+  // Gets the number of tests to be printed in the XML report.
+  int reportable_test_count() const;
+
+  // Gets the number of all tests.
+  int total_test_count() const;
+
+  // Gets the number of tests that should run.
+  int test_to_run_count() const;
+
+  // Gets the time of the test program start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp() const;
+
+  // Gets the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const;
+
+  // Returns true iff the unit test passed (i.e. all test cases passed).
+  bool Passed() const;
+
+  // Returns true iff the unit test failed (i.e. some test case failed
+  // or something outside of all tests failed).
+  bool Failed() const;
+
+  // Gets the i-th test case among all the test cases. i can range from 0 to
+  // total_test_case_count() - 1. If i is not in that range, returns NULL.
+  const TestCase* GetTestCase(int i) const;
+
+  // Returns the TestResult containing information on test failures and
+  // properties logged outside of individual test cases.
+  const TestResult& ad_hoc_test_result() const;
+
+  // Returns the list of event listeners that can be used to track events
+  // inside Google Test.
+  TestEventListeners& listeners();
+
+ private:
+  // Registers and returns a global test environment.  When a test
+  // program is run, all global test environments will be set-up in
+  // the order they were registered.  After all tests in the program
+  // have finished, all global test environments will be torn-down in
+  // the *reverse* order they were registered.
+  //
+  // The UnitTest object takes ownership of the given environment.
+  //
+  // This method can only be called from the main thread.
+  Environment* AddEnvironment(Environment* env);
+
+  // Adds a TestPartResult to the current TestResult object.  All
+  // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc)
+  // eventually call this to report their results.  The user code
+  // should use the assertion macros instead of calling this directly.
+  void AddTestPartResult(TestPartResult::Type result_type,
+                         const char* file_name,
+                         int line_number,
+                         const std::string& message,
+                         const std::string& os_stack_trace)
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Adds a TestProperty to the current TestResult object when invoked from
+  // inside a test, to current TestCase's ad_hoc_test_result_ when invoked
+  // from SetUpTestCase or TearDownTestCase, or to the global property set
+  // when invoked elsewhere.  If the result already contains a property with
+  // the same key, the value will be updated.
+  void RecordProperty(const std::string& key, const std::string& value);
+
+  // Gets the i-th test case among all the test cases. i can range from 0 to
+  // total_test_case_count() - 1. If i is not in that range, returns NULL.
+  TestCase* GetMutableTestCase(int i);
+
+  // Accessors for the implementation object.
+  internal::UnitTestImpl* impl() { return impl_; }
+  const internal::UnitTestImpl* impl() const { return impl_; }
+
+  // These classes and funcions are friends as they need to access private
+  // members of UnitTest.
+  friend class Test;
+  friend class internal::AssertHelper;
+  friend class internal::ScopedTrace;
+  friend class internal::StreamingListenerTest;
+  friend class internal::UnitTestRecordPropertyTestHelper;
+  friend Environment* AddGlobalTestEnvironment(Environment* env);
+  friend internal::UnitTestImpl* internal::GetUnitTestImpl();
+  friend void internal::ReportFailureInUnknownLocation(
+      TestPartResult::Type result_type,
+      const std::string& message);
+
+  // Creates an empty UnitTest.
+  UnitTest();
+
+  // D'tor
+  virtual ~UnitTest();
+
+  // Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+  // Google Test trace stack.
+  void PushGTestTrace(const internal::TraceInfo& trace)
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Pops a trace from the per-thread Google Test trace stack.
+  void PopGTestTrace()
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Protects mutable state in *impl_.  This is mutable as some const
+  // methods need to lock it too.
+  mutable internal::Mutex mutex_;
+
+  // Opaque implementation object.  This field is never changed once
+  // the object is constructed.  We don't mark it as const here, as
+  // doing so will cause a warning in the constructor of UnitTest.
+  // Mutable state in *impl_ is protected by mutex_.
+  internal::UnitTestImpl* impl_;
+
+  // We disallow copying UnitTest.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest);
+};
+
+// A convenient wrapper for adding an environment for the test
+// program.
+//
+// You should call this before RUN_ALL_TESTS() is called, probably in
+// main().  If you use gtest_main, you need to call this before main()
+// starts for it to take effect.  For example, you can define a global
+// variable like this:
+//
+//   testing::Environment* const foo_env =
+//       testing::AddGlobalTestEnvironment(new FooEnvironment);
+//
+// However, we strongly recommend you to write your own main() and
+// call AddGlobalTestEnvironment() there, as relying on initialization
+// of global variables makes the code harder to read and may cause
+// problems when you register multiple environments from different
+// translation units and the environments have dependencies among them
+// (remember that the compiler doesn't guarantee the order in which
+// global variables from different translation units are initialized).
+inline Environment* AddGlobalTestEnvironment(Environment* env) {
+  return UnitTest::GetInstance()->AddEnvironment(env);
+}
+
+// Initializes Google Test.  This must be called before calling
+// RUN_ALL_TESTS().  In particular, it parses a command line for the
+// flags that Google Test recognizes.  Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned.  Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+GTEST_API_ void InitGoogleTest(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
+
+namespace internal {
+
+// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
+// value of type ToPrint that is an operand of a comparison assertion
+// (e.g. ASSERT_EQ).  OtherOperand is the type of the other operand in
+// the comparison, and is used to help determine the best way to
+// format the value.  In particular, when the value is a C string
+// (char pointer) and the other operand is an STL string object, we
+// want to format the C string as a string, since we know it is
+// compared by value with the string object.  If the value is a char
+// pointer but the other operand is not an STL string object, we don't
+// know whether the pointer is supposed to point to a NUL-terminated
+// string, and thus want to print it as a pointer to be safe.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// The default case.
+template <typename ToPrint, typename OtherOperand>
+class FormatForComparison {
+ public:
+  static ::std::string Format(const ToPrint& value) {
+    return ::testing::PrintToString(value);
+  }
+};
+
+// Array.
+template <typename ToPrint, size_t N, typename OtherOperand>
+class FormatForComparison<ToPrint[N], OtherOperand> {
+ public:
+  static ::std::string Format(const ToPrint* value) {
+    return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
+  }
+};
+
+// By default, print C string as pointers to be safe, as we don't know
+// whether they actually point to a NUL-terminated string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType)                \
+  template <typename OtherOperand>                                      \
+  class FormatForComparison<CharType*, OtherOperand> {                  \
+   public:                                                              \
+    static ::std::string Format(CharType* value) {                      \
+      return ::testing::PrintToString(static_cast<const void*>(value)); \
+    }                                                                   \
+  }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
+
+// If a C string is compared with an STL string object, we know it's meant
+// to point to a NUL-terminated string, and thus can print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+  template <>                                                           \
+  class FormatForComparison<CharType*, OtherStringType> {               \
+   public:                                                              \
+    static ::std::string Format(CharType* value) {                      \
+      return ::testing::PrintToString(value);                           \
+    }                                                                   \
+  }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+
+#if GTEST_HAS_GLOBAL_STRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string);
+#endif
+
+#if GTEST_HAS_GLOBAL_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring);
+#endif
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
+#endif
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
+// operand to be used in a failure message.  The type (but not value)
+// of the other operand may affect the format.  This allows us to
+// print a char* as a raw pointer when it is compared against another
+// char* or void*, and print it as a C string when it is compared
+// against an std::string object, for example.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename T1, typename T2>
+std::string FormatForComparisonFailureMessage(
+    const T1& value, const T2& /* other_operand */) {
+  return FormatForComparison<T1, T2>::Format(value);
+}
+
+// The helper function for {ASSERT|EXPECT}_EQ.
+template <typename T1, typename T2>
+AssertionResult CmpHelperEQ(const char* expected_expression,
+                            const char* actual_expression,
+                            const T1& expected,
+                            const T2& actual) {
+#ifdef _MSC_VER
+# pragma warning(push)          // Saves the current warning state.
+# pragma warning(disable:4389)  // Temporarily disables warning on
+                                // signed/unsigned mismatch.
+#endif
+
+  if (expected == actual) {
+    return AssertionSuccess();
+  }
+
+#ifdef _MSC_VER
+# pragma warning(pop)          // Restores the warning state.
+#endif
+
+  return EqFailure(expected_expression,
+                   actual_expression,
+                   FormatForComparisonFailureMessage(expected, actual),
+                   FormatForComparisonFailureMessage(actual, expected),
+                   false);
+}
+
+// With this overloaded version, we allow anonymous enums to be used
+// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums
+// can be implicitly cast to BiggestInt.
+GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression,
+                                       const char* actual_expression,
+                                       BiggestInt expected,
+                                       BiggestInt actual);
+
+// The helper class for {ASSERT|EXPECT}_EQ.  The template argument
+// lhs_is_null_literal is true iff the first argument to ASSERT_EQ()
+// is a null pointer literal.  The following default implementation is
+// for lhs_is_null_literal being false.
+template <bool lhs_is_null_literal>
+class EqHelper {
+ public:
+  // This templatized version is for the general case.
+  template <typename T1, typename T2>
+  static AssertionResult Compare(const char* expected_expression,
+                                 const char* actual_expression,
+                                 const T1& expected,
+                                 const T2& actual) {
+    return CmpHelperEQ(expected_expression, actual_expression, expected,
+                       actual);
+  }
+
+  // With this overloaded version, we allow anonymous enums to be used
+  // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous
+  // enums can be implicitly cast to BiggestInt.
+  //
+  // Even though its body looks the same as the above version, we
+  // cannot merge the two, as it will make anonymous enums unhappy.
+  static AssertionResult Compare(const char* expected_expression,
+                                 const char* actual_expression,
+                                 BiggestInt expected,
+                                 BiggestInt actual) {
+    return CmpHelperEQ(expected_expression, actual_expression, expected,
+                       actual);
+  }
+};
+
+// This specialization is used when the first argument to ASSERT_EQ()
+// is a null pointer literal, like NULL, false, or 0.
+template <>
+class EqHelper<true> {
+ public:
+  // We define two overloaded versions of Compare().  The first
+  // version will be picked when the second argument to ASSERT_EQ() is
+  // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or
+  // EXPECT_EQ(false, a_bool).
+  template <typename T1, typename T2>
+  static AssertionResult Compare(
+      const char* expected_expression,
+      const char* actual_expression,
+      const T1& expected,
+      const T2& actual,
+      // The following line prevents this overload from being considered if T2
+      // is not a pointer type.  We need this because ASSERT_EQ(NULL, my_ptr)
+      // expands to Compare("", "", NULL, my_ptr), which requires a conversion
+      // to match the Secret* in the other overload, which would otherwise make
+      // this template match better.
+      typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
+    return CmpHelperEQ(expected_expression, actual_expression, expected,
+                       actual);
+  }
+
+  // This version will be picked when the second argument to ASSERT_EQ() is a
+  // pointer, e.g. ASSERT_EQ(NULL, a_pointer).
+  template <typename T>
+  static AssertionResult Compare(
+      const char* expected_expression,
+      const char* actual_expression,
+      // We used to have a second template parameter instead of Secret*.  That
+      // template parameter would deduce to 'long', making this a better match
+      // than the first overload even without the first overload's EnableIf.
+      // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
+      // non-pointer argument" (even a deduced integral argument), so the old
+      // implementation caused warnings in user code.
+      Secret* /* expected (NULL) */,
+      T* actual) {
+    // We already know that 'expected' is a null pointer.
+    return CmpHelperEQ(expected_expression, actual_expression,
+                       static_cast<T*>(NULL), actual);
+  }
+};
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_??.  It is here just to avoid copy-and-paste
+// of similar code.
+//
+// For each templatized helper function, we also define an overloaded
+// version for BiggestInt in order to reduce code bloat and allow
+// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled
+// with gcc 4.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+template <typename T1, typename T2>\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+                                   const T1& val1, const T2& val2) {\
+  if (val1 op val2) {\
+    return AssertionSuccess();\
+  } else {\
+    return AssertionFailure() \
+        << "Expected: (" << expr1 << ") " #op " (" << expr2\
+        << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
+        << " vs " << FormatForComparisonFailureMessage(val2, val1);\
+  }\
+}\
+GTEST_API_ AssertionResult CmpHelper##op_name(\
+    const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2)
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// Implements the helper function for {ASSERT|EXPECT}_NE
+GTEST_IMPL_CMP_HELPER_(NE, !=);
+// Implements the helper function for {ASSERT|EXPECT}_LE
+GTEST_IMPL_CMP_HELPER_(LE, <=);
+// Implements the helper function for {ASSERT|EXPECT}_LT
+GTEST_IMPL_CMP_HELPER_(LT, <);
+// Implements the helper function for {ASSERT|EXPECT}_GE
+GTEST_IMPL_CMP_HELPER_(GE, >=);
+// Implements the helper function for {ASSERT|EXPECT}_GT
+GTEST_IMPL_CMP_HELPER_(GT, >);
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+                                          const char* actual_expression,
+                                          const char* expected,
+                                          const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression,
+                                              const char* actual_expression,
+                                              const char* expected,
+                                              const char* actual);
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const char* s1,
+                                          const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+                                              const char* s2_expression,
+                                              const char* s1,
+                                              const char* s2);
+
+
+// Helper function for *_STREQ on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression,
+                                          const char* actual_expression,
+                                          const wchar_t* expected,
+                                          const wchar_t* actual);
+
+// Helper function for *_STRNE on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const wchar_t* s1,
+                                          const wchar_t* s2);
+
+}  // namespace internal
+
+// IsSubstring() and IsNotSubstring() are intended to be used as the
+// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by
+// themselves.  They check whether needle is a substring of haystack
+// (NULL is considered a substring of itself only), and return an
+// appropriate error message when they fail.
+//
+// The {needle,haystack}_expr arguments are the stringified
+// expressions that generated the two real arguments.
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::string& needle, const ::std::string& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::string& needle, const ::std::string& haystack);
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::wstring& needle, const ::std::wstring& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::wstring& needle, const ::std::wstring& haystack);
+#endif  // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+// Helper template function for comparing floating-points.
+//
+// Template parameter:
+//
+//   RawType: the raw floating-point type (either float or double)
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename RawType>
+AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression,
+                                         const char* actual_expression,
+                                         RawType expected,
+                                         RawType actual) {
+  const FloatingPoint<RawType> lhs(expected), rhs(actual);
+
+  if (lhs.AlmostEquals(rhs)) {
+    return AssertionSuccess();
+  }
+
+  ::std::stringstream expected_ss;
+  expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+              << expected;
+
+  ::std::stringstream actual_ss;
+  actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+            << actual;
+
+  return EqFailure(expected_expression,
+                   actual_expression,
+                   StringStreamToString(&expected_ss),
+                   StringStreamToString(&actual_ss),
+                   false);
+}
+
+// Helper function for implementing ASSERT_NEAR.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1,
+                                                const char* expr2,
+                                                const char* abs_error_expr,
+                                                double val1,
+                                                double val2,
+                                                double abs_error);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+// A class that enables one to stream messages to assertion macros
+class GTEST_API_ AssertHelper {
+ public:
+  // Constructor.
+  AssertHelper(TestPartResult::Type type,
+               const char* file,
+               int line,
+               const char* message);
+  ~AssertHelper();
+
+  // Message assignment is a semantic trick to enable assertion
+  // streaming; see the GTEST_MESSAGE_ macro below.
+  void operator=(const Message& message) const;
+
+ private:
+  // We put our data in a struct so that the size of the AssertHelper class can
+  // be as small as possible.  This is important because gcc is incapable of
+  // re-using stack space even for temporary variables, so every EXPECT_EQ
+  // reserves stack space for another AssertHelper.
+  struct AssertHelperData {
+    AssertHelperData(TestPartResult::Type t,
+                     const char* srcfile,
+                     int line_num,
+                     const char* msg)
+        : type(t), file(srcfile), line(line_num), message(msg) { }
+
+    TestPartResult::Type const type;
+    const char* const file;
+    int const line;
+    std::string const message;
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData);
+  };
+
+  AssertHelperData* const data_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
+};
+
+}  // namespace internal
+
+#if GTEST_HAS_PARAM_TEST
+// The pure interface class that all value-parameterized tests inherit from.
+// A value-parameterized class must inherit from both ::testing::Test and
+// ::testing::WithParamInterface. In most cases that just means inheriting
+// from ::testing::TestWithParam, but more complicated test hierarchies
+// may need to inherit from Test and WithParamInterface at different levels.
+//
+// This interface has support for accessing the test parameter value via
+// the GetParam() method.
+//
+// Use it with one of the parameter generator defining functions, like Range(),
+// Values(), ValuesIn(), Bool(), and Combine().
+//
+// class FooTest : public ::testing::TestWithParam<int> {
+//  protected:
+//   FooTest() {
+//     // Can use GetParam() here.
+//   }
+//   virtual ~FooTest() {
+//     // Can use GetParam() here.
+//   }
+//   virtual void SetUp() {
+//     // Can use GetParam() here.
+//   }
+//   virtual void TearDown {
+//     // Can use GetParam() here.
+//   }
+// };
+// TEST_P(FooTest, DoesBar) {
+//   // Can use GetParam() method here.
+//   Foo foo;
+//   ASSERT_TRUE(foo.DoesBar(GetParam()));
+// }
+// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
+
+template <typename T>
+class WithParamInterface {
+ public:
+  typedef T ParamType;
+  virtual ~WithParamInterface() {}
+
+  // The current parameter value. Is also available in the test fixture's
+  // constructor. This member function is non-static, even though it only
+  // references static data, to reduce the opportunity for incorrect uses
+  // like writing 'WithParamInterface<bool>::GetParam()' for a test that
+  // uses a fixture whose parameter type is int.
+  const ParamType& GetParam() const {
+    GTEST_CHECK_(parameter_ != NULL)
+        << "GetParam() can only be called inside a value-parameterized test "
+        << "-- did you intend to write TEST_P instead of TEST_F?";
+    return *parameter_;
+  }
+
+ private:
+  // Sets parameter value. The caller is responsible for making sure the value
+  // remains alive and unchanged throughout the current test.
+  static void SetParam(const ParamType* parameter) {
+    parameter_ = parameter;
+  }
+
+  // Static value used for accessing parameter during a test lifetime.
+  static const ParamType* parameter_;
+
+  // TestClass must be a subclass of WithParamInterface<T> and Test.
+  template <class TestClass> friend class internal::ParameterizedTestFactory;
+};
+
+template <typename T>
+const T* WithParamInterface<T>::parameter_ = NULL;
+
+// Most value-parameterized classes can ignore the existence of
+// WithParamInterface, and can just inherit from ::testing::TestWithParam.
+
+template <typename T>
+class TestWithParam : public Test, public WithParamInterface<T> {
+};
+
+#endif  // GTEST_HAS_PARAM_TEST
+
+// Macros for indicating success/failure in test code.
+
+// ADD_FAILURE unconditionally adds a failure to the current test.
+// SUCCEED generates a success - it doesn't automatically make the
+// current test successful, as a test is only successful when it has
+// no failure.
+//
+// EXPECT_* verifies that a certain condition is satisfied.  If not,
+// it behaves like ADD_FAILURE.  In particular:
+//
+//   EXPECT_TRUE  verifies that a Boolean condition is true.
+//   EXPECT_FALSE verifies that a Boolean condition is false.
+//
+// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except
+// that they will also abort the current function on failure.  People
+// usually want the fail-fast behavior of FAIL and ASSERT_*, but those
+// writing data-driven tests often find themselves using ADD_FAILURE
+// and EXPECT_* more.
+
+// Generates a nonfatal failure with a generic message.
+#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed")
+
+// Generates a nonfatal failure at the given source file location with
+// a generic message.
+#define ADD_FAILURE_AT(file, line) \
+  GTEST_MESSAGE_AT_(file, line, "Failed", \
+                    ::testing::TestPartResult::kNonFatalFailure)
+
+// Generates a fatal failure with a generic message.
+#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed")
+
+// Define this macro to 1 to omit the definition of FAIL(), which is a
+// generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_FAIL
+# define FAIL() GTEST_FAIL()
+#endif
+
+// Generates a success with a generic message.
+#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")
+
+// Define this macro to 1 to omit the definition of SUCCEED(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_SUCCEED
+# define SUCCEED() GTEST_SUCCEED()
+#endif
+
+// Macros for testing exceptions.
+//
+//    * {ASSERT|EXPECT}_THROW(statement, expected_exception):
+//         Tests that the statement throws the expected exception.
+//    * {ASSERT|EXPECT}_NO_THROW(statement):
+//         Tests that the statement doesn't throw any exception.
+//    * {ASSERT|EXPECT}_ANY_THROW(statement):
+//         Tests that the statement throws an exception.
+
+#define EXPECT_THROW(statement, expected_exception) \
+  GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_NO_THROW(statement) \
+  GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_ANY_THROW(statement) \
+  GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW(statement, expected_exception) \
+  GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
+#define ASSERT_NO_THROW(statement) \
+  GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
+#define ASSERT_ANY_THROW(statement) \
+  GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
+
+// Boolean assertions. Condition can be either a Boolean expression or an
+// AssertionResult. For more information on how to use AssertionResult with
+// these macros see comments on that class.
+#define EXPECT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+                      GTEST_NONFATAL_FAILURE_)
+#define EXPECT_FALSE(condition) \
+  GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+                      GTEST_NONFATAL_FAILURE_)
+#define ASSERT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+                      GTEST_FATAL_FAILURE_)
+#define ASSERT_FALSE(condition) \
+  GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+                      GTEST_FATAL_FAILURE_)
+
+// Includes the auto-generated header that implements a family of
+// generic predicate assertion macros.
+#include "gtest/gtest_pred_impl.h"
+
+// Macros for testing equalities and inequalities.
+//
+//    * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
+//    * {ASSERT|EXPECT}_NE(v1, v2):           Tests that v1 != v2
+//    * {ASSERT|EXPECT}_LT(v1, v2):           Tests that v1 < v2
+//    * {ASSERT|EXPECT}_LE(v1, v2):           Tests that v1 <= v2
+//    * {ASSERT|EXPECT}_GT(v1, v2):           Tests that v1 > v2
+//    * {ASSERT|EXPECT}_GE(v1, v2):           Tests that v1 >= v2
+//
+// When they are not, Google Test prints both the tested expressions and
+// their actual values.  The values must be compatible built-in types,
+// or you will get a compiler error.  By "compatible" we mean that the
+// values can be compared by the respective operator.
+//
+// Note:
+//
+//   1. It is possible to make a user-defined type work with
+//   {ASSERT|EXPECT}_??(), but that requires overloading the
+//   comparison operators and is thus discouraged by the Google C++
+//   Usage Guide.  Therefore, you are advised to use the
+//   {ASSERT|EXPECT}_TRUE() macro to assert that two objects are
+//   equal.
+//
+//   2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on
+//   pointers (in particular, C strings).  Therefore, if you use it
+//   with two C strings, you are testing how their locations in memory
+//   are related, not how their content is related.  To compare two C
+//   strings by content, use {ASSERT|EXPECT}_STR*().
+//
+//   3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to
+//   {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you
+//   what the actual value is when it fails, and similarly for the
+//   other comparisons.
+//
+//   4. Do not depend on the order in which {ASSERT|EXPECT}_??()
+//   evaluate their arguments, which is undefined.
+//
+//   5. These macros evaluate their arguments exactly once.
+//
+// Examples:
+//
+//   EXPECT_NE(5, Foo());
+//   EXPECT_EQ(NULL, a_pointer);
+//   ASSERT_LT(i, array_size);
+//   ASSERT_GT(records.size(), 0) << "There is no record left.";
+
+#define EXPECT_EQ(expected, actual) \
+  EXPECT_PRED_FORMAT2(::testing::internal:: \
+                      EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+                      expected, actual)
+#define EXPECT_NE(expected, actual) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
+#define EXPECT_LE(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define EXPECT_LT(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define EXPECT_GE(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define EXPECT_GT(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+#define GTEST_ASSERT_EQ(expected, actual) \
+  ASSERT_PRED_FORMAT2(::testing::internal:: \
+                      EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
+                      expected, actual)
+#define GTEST_ASSERT_NE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define GTEST_ASSERT_LE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define GTEST_ASSERT_LT(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define GTEST_ASSERT_GE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define GTEST_ASSERT_GT(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of
+// ASSERT_XY(), which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_ASSERT_EQ
+# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_NE
+# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LE
+# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LT
+# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GE
+# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GT
+# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2)
+#endif
+
+// C-string Comparisons.  All tests treat NULL and any non-NULL string
+// as different.  Two NULLs are equal.
+//
+//    * {ASSERT|EXPECT}_STREQ(s1, s2):     Tests that s1 == s2
+//    * {ASSERT|EXPECT}_STRNE(s1, s2):     Tests that s1 != s2
+//    * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case
+//    * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case
+//
+// For wide or narrow string objects, you can use the
+// {ASSERT|EXPECT}_??() macros.
+//
+// Don't depend on the order in which the arguments are evaluated,
+// which is undefined.
+//
+// These macros evaluate their arguments exactly once.
+
+#define EXPECT_STREQ(expected, actual) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define EXPECT_STRNE(s1, s2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define EXPECT_STRCASEEQ(expected, actual) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define EXPECT_STRCASENE(s1, s2)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+#define ASSERT_STREQ(expected, actual) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual)
+#define ASSERT_STRNE(s1, s2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define ASSERT_STRCASEEQ(expected, actual) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual)
+#define ASSERT_STRCASENE(s1, s2)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+// Macros for comparing floating-point numbers.
+//
+//    * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual):
+//         Tests that two float values are almost equal.
+//    * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual):
+//         Tests that two double values are almost equal.
+//    * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error):
+//         Tests that v1 and v2 are within the given distance to each other.
+//
+// Google Test uses ULP-based comparison to automatically pick a default
+// error bound that is appropriate for the operands.  See the
+// FloatingPoint template class in gtest-internal.h if you are
+// interested in the implementation details.
+
+#define EXPECT_FLOAT_EQ(expected, actual)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+                      expected, actual)
+
+#define EXPECT_DOUBLE_EQ(expected, actual)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+                      expected, actual)
+
+#define ASSERT_FLOAT_EQ(expected, actual)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+                      expected, actual)
+
+#define ASSERT_DOUBLE_EQ(expected, actual)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+                      expected, actual)
+
+#define EXPECT_NEAR(val1, val2, abs_error)\
+  EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+                      val1, val2, abs_error)
+
+#define ASSERT_NEAR(val1, val2, abs_error)\
+  ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+                      val1, val2, abs_error)
+
+// These predicate format functions work on floating-point values, and
+// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g.
+//
+//   EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0);
+
+// Asserts that val1 is less than, or almost equal to, val2.  Fails
+// otherwise.  In particular, it fails if either val1 or val2 is NaN.
+GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2,
+                                   float val1, float val2);
+GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
+                                    double val1, double val2);
+
+
+#if GTEST_OS_WINDOWS
+
+// Macros that test for HRESULT failure and success, these are only useful
+// on Windows, and rely on Windows SDK macros and APIs to compile.
+//
+//    * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)
+//
+// When expr unexpectedly fails or succeeds, Google Test prints the
+// expected result and the actual result with both a human-readable
+// string representation of the error, if available, as well as the
+// hex result code.
+# define EXPECT_HRESULT_SUCCEEDED(expr) \
+    EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define ASSERT_HRESULT_SUCCEEDED(expr) \
+    ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define EXPECT_HRESULT_FAILED(expr) \
+    EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+# define ASSERT_HRESULT_FAILED(expr) \
+    ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+#endif  // GTEST_OS_WINDOWS
+
+// Macros that execute statement and check that it doesn't generate new fatal
+// failures in the current thread.
+//
+//   * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement);
+//
+// Examples:
+//
+//   EXPECT_NO_FATAL_FAILURE(Process());
+//   ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed";
+//
+#define ASSERT_NO_FATAL_FAILURE(statement) \
+    GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_)
+#define EXPECT_NO_FATAL_FAILURE(statement) \
+    GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
+
+// Causes a trace (including the source file path, the current line
+// number, and the given message) to be included in every test failure
+// message generated by code in the current scope.  The effect is
+// undone when the control leaves the current scope.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// In the implementation, we include the current line number as part
+// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
+// to appear in the same block - as long as they are on different
+// lines.
+#define SCOPED_TRACE(message) \
+  ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+    __FILE__, __LINE__, ::testing::Message() << (message))
+
+// Compile-time assertion for type equality.
+// StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are
+// the same type.  The value it returns is not interesting.
+//
+// Instead of making StaticAssertTypeEq a class template, we make it a
+// function template that invokes a helper class template.  This
+// prevents a user from misusing StaticAssertTypeEq<T1, T2> by
+// defining objects of that type.
+//
+// CAVEAT:
+//
+// When used inside a method of a class template,
+// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is
+// instantiated.  For example, given:
+//
+//   template <typename T> class Foo {
+//    public:
+//     void Bar() { testing::StaticAssertTypeEq<int, T>(); }
+//   };
+//
+// the code:
+//
+//   void Test1() { Foo<bool> foo; }
+//
+// will NOT generate a compiler error, as Foo<bool>::Bar() is never
+// actually instantiated.  Instead, you need:
+//
+//   void Test2() { Foo<bool> foo; foo.Bar(); }
+//
+// to cause a compiler error.
+template <typename T1, typename T2>
+bool StaticAssertTypeEq() {
+  (void)internal::StaticAssertTypeEqHelper<T1, T2>();
+  return true;
+}
+
+// Defines a test.
+//
+// The first parameter is the name of the test case, and the second
+// parameter is the name of the test within the test case.
+//
+// The convention is to end the test case name with "Test".  For
+// example, a test case for the Foo class can be named FooTest.
+//
+// The user should put his test code between braces after using this
+// macro.  Example:
+//
+//   TEST(FooTest, InitializesCorrectly) {
+//     Foo foo;
+//     EXPECT_TRUE(foo.StatusIsOK());
+//   }
+
+// Note that we call GetTestTypeId() instead of GetTypeId<
+// ::testing::Test>() here to get the type ID of testing::Test.  This
+// is to work around a suspected linker bug when using Google Test as
+// a framework on Mac OS X.  The bug causes GetTypeId<
+// ::testing::Test>() to return different values depending on whether
+// the call is from the Google Test framework itself or from user test
+// code.  GetTestTypeId() is guaranteed to always return the same
+// value, as it always calls GetTypeId<>() from the Google Test
+// framework.
+#define GTEST_TEST(test_case_name, test_name)\
+  GTEST_TEST_(test_case_name, test_name, \
+              ::testing::Test, ::testing::internal::GetTestTypeId())
+
+// Define this macro to 1 to omit the definition of TEST(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_TEST
+# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
+#endif
+
+// Defines a test that uses a test fixture.
+//
+// The first parameter is the name of the test fixture class, which
+// also doubles as the test case name.  The second parameter is the
+// name of the test within the test case.
+//
+// A test fixture class must be declared earlier.  The user should put
+// his test code between braces after using this macro.  Example:
+//
+//   class FooTest : public testing::Test {
+//    protected:
+//     virtual void SetUp() { b_.AddElement(3); }
+//
+//     Foo a_;
+//     Foo b_;
+//   };
+//
+//   TEST_F(FooTest, InitializesCorrectly) {
+//     EXPECT_TRUE(a_.StatusIsOK());
+//   }
+//
+//   TEST_F(FooTest, ReturnsElementCountCorrectly) {
+//     EXPECT_EQ(0, a_.size());
+//     EXPECT_EQ(1, b_.size());
+//   }
+
+#define TEST_F(test_fixture, test_name)\
+  GTEST_TEST_(test_fixture, test_name, test_fixture, \
+              ::testing::internal::GetTypeId<test_fixture>())
+
+}  // namespace testing
+
+// Use this function in main() to run all tests.  It returns 0 if all
+// tests are successful, or 1 otherwise.
+//
+// RUN_ALL_TESTS() should be invoked after the command line has been
+// parsed by InitGoogleTest().
+//
+// This function was formerly a macro; thus, it is in the global
+// namespace and has an all-caps name.
+int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
+
+inline int RUN_ALL_TESTS() {
+  return ::testing::UnitTest::GetInstance()->Run();
+}
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_pred_impl.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_pred_impl.h
new file mode 100644
index 0000000..30ae712
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_pred_impl.h
@@ -0,0 +1,358 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
+// 'gen_gtest_pred_impl.py 5'.  DO NOT EDIT BY HAND!
+//
+// Implements a family of generic predicate assertion macros.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+
+// Makes sure this header is not included before gtest.h.
+#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
+# error Do not include gtest_pred_impl.h directly.  Include gtest.h instead.
+#endif  // GTEST_INCLUDE_GTEST_GTEST_H_
+
+// This header implements a family of generic predicate assertion
+// macros:
+//
+//   ASSERT_PRED_FORMAT1(pred_format, v1)
+//   ASSERT_PRED_FORMAT2(pred_format, v1, v2)
+//   ...
+//
+// where pred_format is a function or functor that takes n (in the
+// case of ASSERT_PRED_FORMATn) values and their source expression
+// text, and returns a testing::AssertionResult.  See the definition
+// of ASSERT_EQ in gtest.h for an example.
+//
+// If you don't care about formatting, you can use the more
+// restrictive version:
+//
+//   ASSERT_PRED1(pred, v1)
+//   ASSERT_PRED2(pred, v1, v2)
+//   ...
+//
+// where pred is an n-ary function or functor that returns bool,
+// and the values v1, v2, ..., must support the << operator for
+// streaming to std::ostream.
+//
+// We also define the EXPECT_* variations.
+//
+// For now we only support predicates whose arity is at most 5.
+// Please email googletestframework at googlegroups.com if you need
+// support for higher arities.
+
+// GTEST_ASSERT_ is the basic statement to which all of the assertions
+// in this file reduce.  Don't use this in your code.
+
+#define GTEST_ASSERT_(expression, on_failure) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (const ::testing::AssertionResult gtest_ar = (expression)) \
+    ; \
+  else \
+    on_failure(gtest_ar.failure_message())
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED1.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1>
+AssertionResult AssertPred1Helper(const char* pred_text,
+                                  const char* e1,
+                                  Pred pred,
+                                  const T1& v1) {
+  if (pred(v1)) return AssertionSuccess();
+
+  return AssertionFailure() << pred_text << "("
+                            << e1 << ") evaluates to false, where"
+                            << "\n" << e1 << " evaluates to " << v1;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, v1), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED1.  Don't use
+// this in your code.
+#define GTEST_PRED1_(pred, v1, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
+                                             #v1, \
+                                             pred, \
+                                             v1), on_failure)
+
+// Unary predicate assertion macros.
+#define EXPECT_PRED_FORMAT1(pred_format, v1) \
+  GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED1(pred, v1) \
+  GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT1(pred_format, v1) \
+  GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED1(pred, v1) \
+  GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED2.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2>
+AssertionResult AssertPred2Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2) {
+  if (pred(v1, v2)) return AssertionSuccess();
+
+  return AssertionFailure() << pred_text << "("
+                            << e1 << ", "
+                            << e2 << ") evaluates to false, where"
+                            << "\n" << e1 << " evaluates to " << v1
+                            << "\n" << e2 << " evaluates to " << v2;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED2.  Don't use
+// this in your code.
+#define GTEST_PRED2_(pred, v1, v2, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             pred, \
+                                             v1, \
+                                             v2), on_failure)
+
+// Binary predicate assertion macros.
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+  GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED2(pred, v1, v2) \
+  GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+  GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED2(pred, v1, v2) \
+  GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED3.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3>
+AssertionResult AssertPred3Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3) {
+  if (pred(v1, v2, v3)) return AssertionSuccess();
+
+  return AssertionFailure() << pred_text << "("
+                            << e1 << ", "
+                            << e2 << ", "
+                            << e3 << ") evaluates to false, where"
+                            << "\n" << e1 << " evaluates to " << v1
+                            << "\n" << e2 << " evaluates to " << v2
+                            << "\n" << e3 << " evaluates to " << v3;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED3.  Don't use
+// this in your code.
+#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3), on_failure)
+
+// Ternary predicate assertion macros.
+#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+  GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED3(pred, v1, v2, v3) \
+  GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+  GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED3(pred, v1, v2, v3) \
+  GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED4.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3,
+          typename T4>
+AssertionResult AssertPred4Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  const char* e4,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3,
+                                  const T4& v4) {
+  if (pred(v1, v2, v3, v4)) return AssertionSuccess();
+
+  return AssertionFailure() << pred_text << "("
+                            << e1 << ", "
+                            << e2 << ", "
+                            << e3 << ", "
+                            << e4 << ") evaluates to false, where"
+                            << "\n" << e1 << " evaluates to " << v1
+                            << "\n" << e2 << " evaluates to " << v2
+                            << "\n" << e3 << " evaluates to " << v3
+                            << "\n" << e4 << " evaluates to " << v4;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED4.  Don't use
+// this in your code.
+#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             #v4, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3, \
+                                             v4), on_failure)
+
+// 4-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+  GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
+  GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+  GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
+  GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED5.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3,
+          typename T4,
+          typename T5>
+AssertionResult AssertPred5Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  const char* e4,
+                                  const char* e5,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3,
+                                  const T4& v4,
+                                  const T5& v5) {
+  if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
+
+  return AssertionFailure() << pred_text << "("
+                            << e1 << ", "
+                            << e2 << ", "
+                            << e3 << ", "
+                            << e4 << ", "
+                            << e5 << ") evaluates to false, where"
+                            << "\n" << e1 << " evaluates to " << v1
+                            << "\n" << e2 << " evaluates to " << v2
+                            << "\n" << e3 << " evaluates to " << v3
+                            << "\n" << e4 << " evaluates to " << v4
+                            << "\n" << e5 << " evaluates to " << v5;
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED5.  Don't use
+// this in your code.
+#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             #v4, \
+                                             #v5, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3, \
+                                             v4, \
+                                             v5), on_failure)
+
+// 5-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+  GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
+  GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+  GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
+  GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+
+
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_prod.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_prod.h
new file mode 100644
index 0000000..da80ddc
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/gtest_prod.h
@@ -0,0 +1,58 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+//
+// Google C++ Testing Framework definitions useful in production code.
+
+#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+
+// When you need to test the private or protected members of a class,
+// use the FRIEND_TEST macro to declare your tests as friends of the
+// class.  For example:
+//
+// class MyClass {
+//  private:
+//   void MyMethod();
+//   FRIEND_TEST(MyClassTest, MyMethod);
+// };
+//
+// class MyClassTest : public testing::Test {
+//   // ...
+// };
+//
+// TEST_F(MyClassTest, MyMethod) {
+//   // Can call MyClass::MyMethod() here.
+// }
+
+#define FRIEND_TEST(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+#endif  // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-death-test-internal.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-death-test-internal.h
new file mode 100644
index 0000000..2b3a78f
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-death-test-internal.h
@@ -0,0 +1,319 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file defines internal utilities needed for implementing
+// death tests.  They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-internal.h"
+
+#include <stdio.h>
+
+namespace testing {
+namespace internal {
+
+GTEST_DECLARE_string_(internal_run_death_test);
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kDeathTestStyleFlag[] = "death_test_style";
+const char kDeathTestUseFork[] = "death_test_use_fork";
+const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
+
+#if GTEST_HAS_DEATH_TEST
+
+// DeathTest is a class that hides much of the complexity of the
+// GTEST_DEATH_TEST_ macro.  It is abstract; its static Create method
+// returns a concrete class that depends on the prevailing death test
+// style, as defined by the --gtest_death_test_style and/or
+// --gtest_internal_run_death_test flags.
+
+// In describing the results of death tests, these terms are used with
+// the corresponding definitions:
+//
+// exit status:  The integer exit information in the format specified
+//               by wait(2)
+// exit code:    The integer code passed to exit(3), _exit(2), or
+//               returned from main()
+class GTEST_API_ DeathTest {
+ public:
+  // Create returns false if there was an error determining the
+  // appropriate action to take for the current death test; for example,
+  // if the gtest_death_test_style flag is set to an invalid value.
+  // The LastMessage method will return a more detailed message in that
+  // case.  Otherwise, the DeathTest pointer pointed to by the "test"
+  // argument is set.  If the death test should be skipped, the pointer
+  // is set to NULL; otherwise, it is set to the address of a new concrete
+  // DeathTest object that controls the execution of the current test.
+  static bool Create(const char* statement, const RE* regex,
+                     const char* file, int line, DeathTest** test);
+  DeathTest();
+  virtual ~DeathTest() { }
+
+  // A helper class that aborts a death test when it's deleted.
+  class ReturnSentinel {
+   public:
+    explicit ReturnSentinel(DeathTest* test) : test_(test) { }
+    ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
+   private:
+    DeathTest* const test_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
+  } GTEST_ATTRIBUTE_UNUSED_;
+
+  // An enumeration of possible roles that may be taken when a death
+  // test is encountered.  EXECUTE means that the death test logic should
+  // be executed immediately.  OVERSEE means that the program should prepare
+  // the appropriate environment for a child process to execute the death
+  // test, then wait for it to complete.
+  enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
+
+  // An enumeration of the three reasons that a test might be aborted.
+  enum AbortReason {
+    TEST_ENCOUNTERED_RETURN_STATEMENT,
+    TEST_THREW_EXCEPTION,
+    TEST_DID_NOT_DIE
+  };
+
+  // Assumes one of the above roles.
+  virtual TestRole AssumeRole() = 0;
+
+  // Waits for the death test to finish and returns its status.
+  virtual int Wait() = 0;
+
+  // Returns true if the death test passed; that is, the test process
+  // exited during the test, its exit status matches a user-supplied
+  // predicate, and its stderr output matches a user-supplied regular
+  // expression.
+  // The user-supplied predicate may be a macro expression rather
+  // than a function pointer or functor, or else Wait and Passed could
+  // be combined.
+  virtual bool Passed(bool exit_status_ok) = 0;
+
+  // Signals that the death test did not die as expected.
+  virtual void Abort(AbortReason reason) = 0;
+
+  // Returns a human-readable outcome message regarding the outcome of
+  // the last death test.
+  static const char* LastMessage();
+
+  static void set_last_death_test_message(const std::string& message);
+
+ private:
+  // A string containing a description of the outcome of the last death test.
+  static std::string last_death_test_message_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
+};
+
+// Factory interface for death tests.  May be mocked out for testing.
+class DeathTestFactory {
+ public:
+  virtual ~DeathTestFactory() { }
+  virtual bool Create(const char* statement, const RE* regex,
+                      const char* file, int line, DeathTest** test) = 0;
+};
+
+// A concrete DeathTestFactory implementation for normal use.
+class DefaultDeathTestFactory : public DeathTestFactory {
+ public:
+  virtual bool Create(const char* statement, const RE* regex,
+                      const char* file, int line, DeathTest** test);
+};
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+
+// Traps C++ exceptions escaping statement and reports them as test
+// failures. Note that trapping SEH exceptions is not implemented here.
+# if GTEST_HAS_EXCEPTIONS
+#  define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+  try { \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+  } catch (const ::std::exception& gtest_exception) { \
+    fprintf(\
+        stderr, \
+        "\n%s: Caught std::exception-derived exception escaping the " \
+        "death test statement. Exception message: %s\n", \
+        ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
+        gtest_exception.what()); \
+    fflush(stderr); \
+    death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+  } catch (...) { \
+    death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+  }
+
+# else
+#  define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+  GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+
+# endif
+
+// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
+// ASSERT_EXIT*, and EXPECT_EXIT*.
+# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    const ::testing::internal::RE& gtest_regex = (regex); \
+    ::testing::internal::DeathTest* gtest_dt; \
+    if (!::testing::internal::DeathTest::Create(#statement, &gtest_regex, \
+        __FILE__, __LINE__, &gtest_dt)) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+    } \
+    if (gtest_dt != NULL) { \
+      ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
+          gtest_dt_ptr(gtest_dt); \
+      switch (gtest_dt->AssumeRole()) { \
+        case ::testing::internal::DeathTest::OVERSEE_TEST: \
+          if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
+            goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
+          } \
+          break; \
+        case ::testing::internal::DeathTest::EXECUTE_TEST: { \
+          ::testing::internal::DeathTest::ReturnSentinel \
+              gtest_sentinel(gtest_dt); \
+          GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
+          gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
+          break; \
+        } \
+        default: \
+          break; \
+      } \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
+      fail(::testing::internal::DeathTest::LastMessage())
+// The symbol "fail" here expands to something into which a message
+// can be streamed.
+
+// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
+// NDEBUG mode. In this case we need the statements to be executed, the regex is
+// ignored, and the macro must accept a streamed message even though the message
+// is never printed.
+# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+     GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+  } else \
+    ::testing::Message()
+
+// A class representing the parsed contents of the
+// --gtest_internal_run_death_test flag, as it existed when
+// RUN_ALL_TESTS was called.
+class InternalRunDeathTestFlag {
+ public:
+  InternalRunDeathTestFlag(const std::string& a_file,
+                           int a_line,
+                           int an_index,
+                           int a_write_fd)
+      : file_(a_file), line_(a_line), index_(an_index),
+        write_fd_(a_write_fd) {}
+
+  ~InternalRunDeathTestFlag() {
+    if (write_fd_ >= 0)
+      posix::Close(write_fd_);
+  }
+
+  const std::string& file() const { return file_; }
+  int line() const { return line_; }
+  int index() const { return index_; }
+  int write_fd() const { return write_fd_; }
+
+ private:
+  std::string file_;
+  int line_;
+  int index_;
+  int write_fd_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
+};
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
+
+#else  // GTEST_HAS_DEATH_TEST
+
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
+// systems that support death tests. This allows one to write such a macro
+// on a system that does not support death tests and be sure that it will
+// compile on a death-test supporting system.
+//
+// Parameters:
+//   statement -  A statement that a macro such as EXPECT_DEATH would test
+//                for program termination. This macro has to make sure this
+//                statement is compiled but not executed, to ensure that
+//                EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+//                parameter iff EXPECT_DEATH compiles with it.
+//   regex     -  A regex that a macro such as EXPECT_DEATH would use to test
+//                the output of statement.  This parameter has to be
+//                compiled but not evaluated by this macro, to ensure that
+//                this macro only accepts expressions that a macro such as
+//                EXPECT_DEATH would accept.
+//   terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+//                and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+//                This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+//                compile inside functions where ASSERT_DEATH doesn't
+//                compile.
+//
+//  The branch that has an always false condition is used to ensure that
+//  statement and regex are compiled (and thus syntactically correct) but
+//  never executed. The unreachable code macro protects the terminator
+//  statement from generating an 'unreachable code' warning in case
+//  statement unconditionally returns or throws. The Message constructor at
+//  the end allows the syntax of streaming additional messages into the
+//  macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+    if (::testing::internal::AlwaysTrue()) { \
+      GTEST_LOG_(WARNING) \
+          << "Death tests are not supported on this platform.\n" \
+          << "Statement '" #statement "' cannot be verified."; \
+    } else if (::testing::internal::AlwaysFalse()) { \
+      ::testing::internal::RE::PartialMatch(".*", (regex)); \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+      terminator; \
+    } else \
+      ::testing::Message()
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-filepath.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-filepath.h
new file mode 100644
index 0000000..7a13b4b
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-filepath.h
@@ -0,0 +1,206 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: keith.ray at gmail.com (Keith Ray)
+//
+// Google Test filepath utilities
+//
+// This header file declares classes and functions used internally by
+// Google Test.  They are subject to change without notice.
+//
+// This file is #included in <gtest/internal/gtest-internal.h>.
+// Do not include this header file separately!
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+
+#include "gtest/internal/gtest-string.h"
+
+namespace testing {
+namespace internal {
+
+// FilePath - a class for file and directory pathname manipulation which
+// handles platform-specific conventions (like the pathname separator).
+// Used for helper functions for naming files in a directory for xml output.
+// Except for Set methods, all methods are const or static, which provides an
+// "immutable value object" -- useful for peace of mind.
+// A FilePath with a value ending in a path separator ("like/this/") represents
+// a directory, otherwise it is assumed to represent a file. In either case,
+// it may or may not represent an actual file or directory in the file system.
+// Names are NOT checked for syntax correctness -- no checking for illegal
+// characters, malformed paths, etc.
+
+class GTEST_API_ FilePath {
+ public:
+  FilePath() : pathname_("") { }
+  FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
+
+  explicit FilePath(const std::string& pathname) : pathname_(pathname) {
+    Normalize();
+  }
+
+  FilePath& operator=(const FilePath& rhs) {
+    Set(rhs);
+    return *this;
+  }
+
+  void Set(const FilePath& rhs) {
+    pathname_ = rhs.pathname_;
+  }
+
+  const std::string& string() const { return pathname_; }
+  const char* c_str() const { return pathname_.c_str(); }
+
+  // Returns the current working directory, or "" if unsuccessful.
+  static FilePath GetCurrentDir();
+
+  // Given directory = "dir", base_name = "test", number = 0,
+  // extension = "xml", returns "dir/test.xml". If number is greater
+  // than zero (e.g., 12), returns "dir/test_12.xml".
+  // On Windows platform, uses \ as the separator rather than /.
+  static FilePath MakeFileName(const FilePath& directory,
+                               const FilePath& base_name,
+                               int number,
+                               const char* extension);
+
+  // Given directory = "dir", relative_path = "test.xml",
+  // returns "dir/test.xml".
+  // On Windows, uses \ as the separator rather than /.
+  static FilePath ConcatPaths(const FilePath& directory,
+                              const FilePath& relative_path);
+
+  // Returns a pathname for a file that does not currently exist. The pathname
+  // will be directory/base_name.extension or
+  // directory/base_name_<number>.extension if directory/base_name.extension
+  // already exists. The number will be incremented until a pathname is found
+  // that does not already exist.
+  // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+  // There could be a race condition if two or more processes are calling this
+  // function at the same time -- they could both pick the same filename.
+  static FilePath GenerateUniqueFileName(const FilePath& directory,
+                                         const FilePath& base_name,
+                                         const char* extension);
+
+  // Returns true iff the path is "".
+  bool IsEmpty() const { return pathname_.empty(); }
+
+  // If input name has a trailing separator character, removes it and returns
+  // the name, otherwise return the name string unmodified.
+  // On Windows platform, uses \ as the separator, other platforms use /.
+  FilePath RemoveTrailingPathSeparator() const;
+
+  // Returns a copy of the FilePath with the directory part removed.
+  // Example: FilePath("path/to/file").RemoveDirectoryName() returns
+  // FilePath("file"). If there is no directory part ("just_a_file"), it returns
+  // the FilePath unmodified. If there is no file part ("just_a_dir/") it
+  // returns an empty FilePath ("").
+  // On Windows platform, '\' is the path separator, otherwise it is '/'.
+  FilePath RemoveDirectoryName() const;
+
+  // RemoveFileName returns the directory path with the filename removed.
+  // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+  // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+  // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+  // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+  // On Windows platform, '\' is the path separator, otherwise it is '/'.
+  FilePath RemoveFileName() const;
+
+  // Returns a copy of the FilePath with the case-insensitive extension removed.
+  // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+  // FilePath("dir/file"). If a case-insensitive extension is not
+  // found, returns a copy of the original FilePath.
+  FilePath RemoveExtension(const char* extension) const;
+
+  // Creates directories so that path exists. Returns true if successful or if
+  // the directories already exist; returns false if unable to create
+  // directories for any reason. Will also return false if the FilePath does
+  // not represent a directory (that is, it doesn't end with a path separator).
+  bool CreateDirectoriesRecursively() const;
+
+  // Create the directory so that path exists. Returns true if successful or
+  // if the directory already exists; returns false if unable to create the
+  // directory for any reason, including if the parent directory does not
+  // exist. Not named "CreateDirectory" because that's a macro on Windows.
+  bool CreateFolder() const;
+
+  // Returns true if FilePath describes something in the file-system,
+  // either a file, directory, or whatever, and that something exists.
+  bool FileOrDirectoryExists() const;
+
+  // Returns true if pathname describes a directory in the file-system
+  // that exists.
+  bool DirectoryExists() const;
+
+  // Returns true if FilePath ends with a path separator, which indicates that
+  // it is intended to represent a directory. Returns false otherwise.
+  // This does NOT check that a directory (or file) actually exists.
+  bool IsDirectory() const;
+
+  // Returns true if pathname describes a root directory. (Windows has one
+  // root directory per disk drive.)
+  bool IsRootDirectory() const;
+
+  // Returns true if pathname describes an absolute path.
+  bool IsAbsolutePath() const;
+
+ private:
+  // Replaces multiple consecutive separators with a single separator.
+  // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+  // redundancies that might be in a pathname involving "." or "..".
+  //
+  // A pathname with multiple consecutive separators may occur either through
+  // user error or as a result of some scripts or APIs that generate a pathname
+  // with a trailing separator. On other platforms the same API or script
+  // may NOT generate a pathname with a trailing "/". Then elsewhere that
+  // pathname may have another "/" and pathname components added to it,
+  // without checking for the separator already being there.
+  // The script language and operating system may allow paths like "foo//bar"
+  // but some of the functions in FilePath will not handle that correctly. In
+  // particular, RemoveTrailingPathSeparator() only removes one separator, and
+  // it is called in CreateDirectoriesRecursively() assuming that it will change
+  // a pathname from directory syntax (trailing separator) to filename syntax.
+  //
+  // On Windows this method also replaces the alternate path separator '/' with
+  // the primary path separator '\\', so that for example "bar\\/\\foo" becomes
+  // "bar\\foo".
+
+  void Normalize();
+
+  // Returns a pointer to the last occurence of a valid path separator in
+  // the FilePath. On Windows, for example, both '/' and '\' are valid path
+  // separators. Returns NULL if no path separator was found.
+  const char* FindLastPathSeparator() const;
+
+  std::string pathname_;
+};  // class FilePath
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-internal.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-internal.h
new file mode 100644
index 0000000..0dcc3a3
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-internal.h
@@ -0,0 +1,1158 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares functions and macros used internally by
+// Google Test.  They are subject to change without notice.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_OS_LINUX
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+#endif  // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <string.h>
+#include <iomanip>
+#include <limits>
+#include <set>
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Due to C++ preprocessor weirdness, we need double indirection to
+// concatenate two tokens when one of them is __LINE__.  Writing
+//
+//   foo ## __LINE__
+//
+// will result in the token foo__LINE__, instead of foo followed by
+// the current line number.  For more details, see
+// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
+#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
+#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
+
+class ProtocolMessage;
+namespace proto2 { class Message; }
+
+namespace testing {
+
+// Forward declarations.
+
+class AssertionResult;                 // Result of an assertion.
+class Message;                         // Represents a failure message.
+class Test;                            // Represents a test.
+class TestInfo;                        // Information about a test.
+class TestPartResult;                  // Result of a test part.
+class UnitTest;                        // A collection of test cases.
+
+template <typename T>
+::std::string PrintToString(const T& value);
+
+namespace internal {
+
+struct TraceInfo;                      // Information about a trace point.
+class ScopedTrace;                     // Implements scoped trace.
+class TestInfoImpl;                    // Opaque implementation of TestInfo
+class UnitTestImpl;                    // Opaque implementation of UnitTest
+
+// How many times InitGoogleTest() has been called.
+GTEST_API_ extern int g_init_gtest_count;
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+GTEST_API_ extern const char kStackTraceMarker[];
+
+// Two overloaded helpers for checking at compile time whether an
+// expression is a null pointer literal (i.e. NULL or any 0-valued
+// compile-time integral constant).  Their return values have
+// different sizes, so we can use sizeof() to test which version is
+// picked by the compiler.  These helpers have no implementations, as
+// we only need their signatures.
+//
+// Given IsNullLiteralHelper(x), the compiler will pick the first
+// version if x can be implicitly converted to Secret*, and pick the
+// second version otherwise.  Since Secret is a secret and incomplete
+// type, the only expression a user can write that has type Secret* is
+// a null pointer literal.  Therefore, we know that x is a null
+// pointer literal if and only if the first version is picked by the
+// compiler.
+char IsNullLiteralHelper(Secret* p);
+char (&IsNullLiteralHelper(...))[2];  // NOLINT
+
+// A compile-time bool constant that is true if and only if x is a
+// null pointer literal (i.e. NULL or any 0-valued compile-time
+// integral constant).
+#ifdef GTEST_ELLIPSIS_NEEDS_POD_
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_IS_NULL_LITERAL_(x) false
+#else
+# define GTEST_IS_NULL_LITERAL_(x) \
+    (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1)
+#endif  // GTEST_ELLIPSIS_NEEDS_POD_
+
+// Appends the user-supplied message to the Google-Test-generated message.
+GTEST_API_ std::string AppendUserMessage(
+    const std::string& gtest_msg, const Message& user_msg);
+
+#if GTEST_HAS_EXCEPTIONS
+
+// This exception is thrown by (and only by) a failed Google Test
+// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
+// are enabled).  We derive it from std::runtime_error, which is for
+// errors presumably detectable only at run time.  Since
+// std::runtime_error inherits from std::exception, many testing
+// frameworks know how to extract and print the message inside it.
+class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
+ public:
+  explicit GoogleTestFailureException(const TestPartResult& failure);
+};
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+// A helper class for creating scoped traces in user programs.
+class GTEST_API_ ScopedTrace {
+ public:
+  // The c'tor pushes the given source file location and message onto
+  // a trace stack maintained by Google Test.
+  ScopedTrace(const char* file, int line, const Message& message);
+
+  // The d'tor pops the info pushed by the c'tor.
+  //
+  // Note that the d'tor is not virtual in order to be efficient.
+  // Don't inherit from ScopedTrace!
+  ~ScopedTrace();
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_;  // A ScopedTrace object does its job in its
+                            // c'tor and d'tor.  Therefore it doesn't
+                            // need to be used otherwise.
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings.  For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+//   expected_expression: "foo"
+//   actual_expression:   "bar"
+//   expected_value:      "5"
+//   actual_value:        "6"
+//
+// The ignoring_case parameter is true iff the assertion is a
+// *_STRCASEEQ*.  When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
+                                     const char* actual_expression,
+                                     const std::string& expected_value,
+                                     const std::string& actual_value,
+                                     bool ignoring_case);
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+GTEST_API_ std::string GetBoolAssertionFailureMessage(
+    const AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value);
+
+// This template class represents an IEEE floating-point number
+// (either single-precision or double-precision, depending on the
+// template parameters).
+//
+// The purpose of this class is to do more sophisticated number
+// comparison.  (Due to round-off error, etc, it's very unlikely that
+// two floating-points will be equal exactly.  Hence a naive
+// comparison by the == operation often doesn't work.)
+//
+// Format of IEEE floating-point:
+//
+//   The most-significant bit being the leftmost, an IEEE
+//   floating-point looks like
+//
+//     sign_bit exponent_bits fraction_bits
+//
+//   Here, sign_bit is a single bit that designates the sign of the
+//   number.
+//
+//   For float, there are 8 exponent bits and 23 fraction bits.
+//
+//   For double, there are 11 exponent bits and 52 fraction bits.
+//
+//   More details can be found at
+//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
+//
+// Template parameter:
+//
+//   RawType: the raw floating-point type (either float or double)
+template <typename RawType>
+class FloatingPoint {
+ public:
+  // Defines the unsigned integer type that has the same size as the
+  // floating point number.
+  typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;
+
+  // Constants.
+
+  // # of bits in a number.
+  static const size_t kBitCount = 8*sizeof(RawType);
+
+  // # of fraction bits in a number.
+  static const size_t kFractionBitCount =
+    std::numeric_limits<RawType>::digits - 1;
+
+  // # of exponent bits in a number.
+  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+  // The mask for the sign bit.
+  static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+  // The mask for the fraction bits.
+  static const Bits kFractionBitMask =
+    ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+  // The mask for the exponent bits.
+  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+  // How many ULP's (Units in the Last Place) we want to tolerate when
+  // comparing two numbers.  The larger the value, the more error we
+  // allow.  A 0 value means that two numbers must be exactly the same
+  // to be considered equal.
+  //
+  // The maximum error of a single floating-point operation is 0.5
+  // units in the last place.  On Intel CPU's, all floating-point
+  // calculations are done with 80-bit precision, while double has 64
+  // bits.  Therefore, 4 should be enough for ordinary use.
+  //
+  // See the following article for more details on ULP:
+  // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+  static const size_t kMaxUlps = 4;
+
+  // Constructs a FloatingPoint from a raw floating-point number.
+  //
+  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
+  // around may change its bits, although the new value is guaranteed
+  // to be also a NAN.  Therefore, don't expect this constructor to
+  // preserve the bits in x when x is a NAN.
+  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }
+
+  // Static methods
+
+  // Reinterprets a bit pattern as a floating-point number.
+  //
+  // This function is needed to test the AlmostEquals() method.
+  static RawType ReinterpretBits(const Bits bits) {
+    FloatingPoint fp(0);
+    fp.u_.bits_ = bits;
+    return fp.u_.value_;
+  }
+
+  // Returns the floating-point number that represent positive infinity.
+  static RawType Infinity() {
+    return ReinterpretBits(kExponentBitMask);
+  }
+
+  // Returns the maximum representable finite floating-point number.
+  static RawType Max();
+
+  // Non-static methods
+
+  // Returns the bits that represents this number.
+  const Bits &bits() const { return u_.bits_; }
+
+  // Returns the exponent bits of this number.
+  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }
+
+  // Returns the fraction bits of this number.
+  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }
+
+  // Returns the sign bit of this number.
+  Bits sign_bit() const { return kSignBitMask & u_.bits_; }
+
+  // Returns true iff this is NAN (not a number).
+  bool is_nan() const {
+    // It's a NAN if the exponent bits are all ones and the fraction
+    // bits are not entirely zeros.
+    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+  }
+
+  // Returns true iff this number is at most kMaxUlps ULP's away from
+  // rhs.  In particular, this function:
+  //
+  //   - returns false if either number is (or both are) NAN.
+  //   - treats really large numbers as almost equal to infinity.
+  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
+  bool AlmostEquals(const FloatingPoint& rhs) const {
+    // The IEEE standard says that any comparison operation involving
+    // a NAN must return false.
+    if (is_nan() || rhs.is_nan()) return false;
+
+    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
+        <= kMaxUlps;
+  }
+
+ private:
+  // The data type used to store the actual floating-point number.
+  union FloatingPointUnion {
+    RawType value_;  // The raw floating-point number.
+    Bits bits_;      // The bits that represent the number.
+  };
+
+  // Converts an integer from the sign-and-magnitude representation to
+  // the biased representation.  More precisely, let N be 2 to the
+  // power of (kBitCount - 1), an integer x is represented by the
+  // unsigned number x + N.
+  //
+  // For instance,
+  //
+  //   -N + 1 (the most negative number representable using
+  //          sign-and-magnitude) is represented by 1;
+  //   0      is represented by N; and
+  //   N - 1  (the biggest number representable using
+  //          sign-and-magnitude) is represented by 2N - 1.
+  //
+  // Read http://en.wikipedia.org/wiki/Signed_number_representations
+  // for more details on signed number representations.
+  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+    if (kSignBitMask & sam) {
+      // sam represents a negative number.
+      return ~sam + 1;
+    } else {
+      // sam represents a positive number.
+      return kSignBitMask | sam;
+    }
+  }
+
+  // Given two numbers in the sign-and-magnitude representation,
+  // returns the distance between them as an unsigned number.
+  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+                                                     const Bits &sam2) {
+    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+  }
+
+  FloatingPointUnion u_;
+};
+
+// We cannot use std::numeric_limits<T>::max() as it clashes with the max()
+// macro defined by <windows.h>.
+template <>
+inline float FloatingPoint<float>::Max() { return FLT_MAX; }
+template <>
+inline double FloatingPoint<double>::Max() { return DBL_MAX; }
+
+// Typedefs the instances of the FloatingPoint template class that we
+// care to use.
+typedef FloatingPoint<float> Float;
+typedef FloatingPoint<double> Double;
+
+// In order to catch the mistake of putting tests that use different
+// test fixture classes in the same test case, we need to assign
+// unique IDs to fixture classes and compare them.  The TypeId type is
+// used to hold such IDs.  The user should treat TypeId as an opaque
+// type: the only operation allowed on TypeId values is to compare
+// them for equality using the == operator.
+typedef const void* TypeId;
+
+template <typename T>
+class TypeIdHelper {
+ public:
+  // dummy_ must not have a const type.  Otherwise an overly eager
+  // compiler (e.g. MSVC 7.1 & 8.0) may try to merge
+  // TypeIdHelper<T>::dummy_ for different Ts as an "optimization".
+  static bool dummy_;
+};
+
+template <typename T>
+bool TypeIdHelper<T>::dummy_ = false;
+
+// GetTypeId<T>() returns the ID of type T.  Different values will be
+// returned for different types.  Calling the function twice with the
+// same type argument is guaranteed to return the same ID.
+template <typename T>
+TypeId GetTypeId() {
+  // The compiler is required to allocate a different
+  // TypeIdHelper<T>::dummy_ variable for each T used to instantiate
+  // the template.  Therefore, the address of dummy_ is guaranteed to
+  // be unique.
+  return &(TypeIdHelper<T>::dummy_);
+}
+
+// Returns the type ID of ::testing::Test.  Always call this instead
+// of GetTypeId< ::testing::Test>() to get the type ID of
+// ::testing::Test, as the latter may give the wrong result due to a
+// suspected linker bug when compiling Google Test as a Mac OS X
+// framework.
+GTEST_API_ TypeId GetTestTypeId();
+
+// Defines the abstract factory interface that creates instances
+// of a Test object.
+class TestFactoryBase {
+ public:
+  virtual ~TestFactoryBase() {}
+
+  // Creates a test instance to run. The instance is both created and destroyed
+  // within TestInfoImpl::Run()
+  virtual Test* CreateTest() = 0;
+
+ protected:
+  TestFactoryBase() {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
+};
+
+// This class provides implementation of TeastFactoryBase interface.
+// It is used in TEST and TEST_F macros.
+template <class TestClass>
+class TestFactoryImpl : public TestFactoryBase {
+ public:
+  virtual Test* CreateTest() { return new TestClass; }
+};
+
+#if GTEST_OS_WINDOWS
+
+// Predicate-formatters for implementing the HRESULT checking macros
+// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}
+// We pass a long instead of HRESULT to avoid causing an
+// include dependency for the HRESULT type.
+GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr,
+                                            long hr);  // NOLINT
+GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
+                                            long hr);  // NOLINT
+
+#endif  // GTEST_OS_WINDOWS
+
+// Types of SetUpTestCase() and TearDownTestCase() functions.
+typedef void (*SetUpTestCaseFunc)();
+typedef void (*TearDownTestCaseFunc)();
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+//   test_case_name:   name of the test case
+//   name:             name of the test
+//   type_param        the name of the test's type parameter, or NULL if
+//                     this is not a typed or a type-parameterized test.
+//   value_param       text representation of the test's value parameter,
+//                     or NULL if this is not a type-parameterized test.
+//   fixture_class_id: ID of the test fixture class
+//   set_up_tc:        pointer to the function that sets up the test case
+//   tear_down_tc:     pointer to the function that tears down the test case
+//   factory:          pointer to the factory that creates a test object.
+//                     The newly created TestInfo instance will assume
+//                     ownership of the factory object.
+GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
+    const char* test_case_name,
+    const char* name,
+    const char* type_param,
+    const char* value_param,
+    TypeId fixture_class_id,
+    SetUpTestCaseFunc set_up_tc,
+    TearDownTestCaseFunc tear_down_tc,
+    TestFactoryBase* factory);
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false.  None of pstr, *pstr, and prefix can be NULL.
+GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// State of the definition of a type-parameterized test case.
+class GTEST_API_ TypedTestCasePState {
+ public:
+  TypedTestCasePState() : registered_(false) {}
+
+  // Adds the given test name to defined_test_names_ and return true
+  // if the test case hasn't been registered; otherwise aborts the
+  // program.
+  bool AddTestName(const char* file, int line, const char* case_name,
+                   const char* test_name) {
+    if (registered_) {
+      fprintf(stderr, "%s Test %s must be defined before "
+              "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n",
+              FormatFileLocation(file, line).c_str(), test_name, case_name);
+      fflush(stderr);
+      posix::Abort();
+    }
+    defined_test_names_.insert(test_name);
+    return true;
+  }
+
+  // Verifies that registered_tests match the test names in
+  // defined_test_names_; returns registered_tests if successful, or
+  // aborts the program otherwise.
+  const char* VerifyRegisteredTestNames(
+      const char* file, int line, const char* registered_tests);
+
+ private:
+  bool registered_;
+  ::std::set<const char*> defined_test_names_;
+};
+
+// Skips to the first non-space char after the first comma in 'str';
+// returns NULL if no comma is found in 'str'.
+inline const char* SkipComma(const char* str) {
+  const char* comma = strchr(str, ',');
+  if (comma == NULL) {
+    return NULL;
+  }
+  while (IsSpace(*(++comma))) {}
+  return comma;
+}
+
+// Returns the prefix of 'str' before the first comma in it; returns
+// the entire string if it contains no comma.
+inline std::string GetPrefixUntilComma(const char* str) {
+  const char* comma = strchr(str, ',');
+  return comma == NULL ? str : std::string(str, comma);
+}
+
+// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
+// registers a list of type-parameterized tests with Google Test.  The
+// return value is insignificant - we just need to return something
+// such that we can call this function in a namespace scope.
+//
+// Implementation note: The GTEST_TEMPLATE_ macro declares a template
+// template parameter.  It's defined in gtest-type-util.h.
+template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
+class TypeParameterizedTest {
+ public:
+  // 'index' is the index of the test in the type list 'Types'
+  // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,
+  // Types).  Valid values for 'index' are [0, N - 1] where N is the
+  // length of Types.
+  static bool Register(const char* prefix, const char* case_name,
+                       const char* test_names, int index) {
+    typedef typename Types::Head Type;
+    typedef Fixture<Type> FixtureClass;
+    typedef typename GTEST_BIND_(TestSel, Type) TestClass;
+
+    // First, registers the first type-parameterized test in the type
+    // list.
+    MakeAndRegisterTestInfo(
+        (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"
+         + StreamableToString(index)).c_str(),
+        GetPrefixUntilComma(test_names).c_str(),
+        GetTypeName<Type>().c_str(),
+        NULL,  // No value parameter.
+        GetTypeId<FixtureClass>(),
+        TestClass::SetUpTestCase,
+        TestClass::TearDownTestCase,
+        new TestFactoryImpl<TestClass>);
+
+    // Next, recurses (at compile time) with the tail of the type list.
+    return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>
+        ::Register(prefix, case_name, test_names, index + 1);
+  }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, class TestSel>
+class TypeParameterizedTest<Fixture, TestSel, Types0> {
+ public:
+  static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+                       const char* /*test_names*/, int /*index*/) {
+    return true;
+  }
+};
+
+// TypeParameterizedTestCase<Fixture, Tests, Types>::Register()
+// registers *all combinations* of 'Tests' and 'Types' with Google
+// Test.  The return value is insignificant - we just need to return
+// something such that we can call this function in a namespace scope.
+template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
+class TypeParameterizedTestCase {
+ public:
+  static bool Register(const char* prefix, const char* case_name,
+                       const char* test_names) {
+    typedef typename Tests::Head Head;
+
+    // First, register the first test in 'Test' for each type in 'Types'.
+    TypeParameterizedTest<Fixture, Head, Types>::Register(
+        prefix, case_name, test_names, 0);
+
+    // Next, recurses (at compile time) with the tail of the test list.
+    return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>
+        ::Register(prefix, case_name, SkipComma(test_names));
+  }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, typename Types>
+class TypeParameterizedTestCase<Fixture, Templates0, Types> {
+ public:
+  static bool Register(const char* /*prefix*/, const char* /*case_name*/,
+                       const char* /*test_names*/) {
+    return true;
+  }
+};
+
+#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag.  The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(
+    UnitTest* unit_test, int skip_count);
+
+// Helpers for suppressing warnings on unreachable code or constant
+// condition.
+
+// Always returns true.
+GTEST_API_ bool AlwaysTrue();
+
+// Always returns false.
+inline bool AlwaysFalse() { return !AlwaysTrue(); }
+
+// Helper for suppressing false warning from Clang on a const char*
+// variable declared in a conditional expression always being NULL in
+// the else branch.
+struct GTEST_API_ ConstCharPtr {
+  ConstCharPtr(const char* str) : value(str) {}
+  operator bool() const { return true; }
+  const char* value;
+};
+
+// A simple Linear Congruential Generator for generating random
+// numbers with a uniform distribution.  Unlike rand() and srand(), it
+// doesn't use global state (and therefore can't interfere with user
+// code).  Unlike rand_r(), it's portable.  An LCG isn't very random,
+// but it's good enough for our purposes.
+class GTEST_API_ Random {
+ public:
+  static const UInt32 kMaxRange = 1u << 31;
+
+  explicit Random(UInt32 seed) : state_(seed) {}
+
+  void Reseed(UInt32 seed) { state_ = seed; }
+
+  // Generates a random number from [0, range).  Crashes if 'range' is
+  // 0 or greater than kMaxRange.
+  UInt32 Generate(UInt32 range);
+
+ private:
+  UInt32 state_;
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
+};
+
+// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
+// compiler error iff T1 and T2 are different types.
+template <typename T1, typename T2>
+struct CompileAssertTypesEqual;
+
+template <typename T>
+struct CompileAssertTypesEqual<T, T> {
+};
+
+// Removes the reference from a type if it is a reference type,
+// otherwise leaves it unchanged.  This is the same as
+// tr1::remove_reference, which is not widely available yet.
+template <typename T>
+struct RemoveReference { typedef T type; };  // NOLINT
+template <typename T>
+struct RemoveReference<T&> { typedef T type; };  // NOLINT
+
+// A handy wrapper around RemoveReference that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_REFERENCE_(T) \
+    typename ::testing::internal::RemoveReference<T>::type
+
+// Removes const from a type if it is a const type, otherwise leaves
+// it unchanged.  This is the same as tr1::remove_const, which is not
+// widely available yet.
+template <typename T>
+struct RemoveConst { typedef T type; };  // NOLINT
+template <typename T>
+struct RemoveConst<const T> { typedef T type; };  // NOLINT
+
+// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above
+// definition to fail to remove the const in 'const int[3]' and 'const
+// char[3][4]'.  The following specialization works around the bug.
+template <typename T, size_t N>
+struct RemoveConst<const T[N]> {
+  typedef typename RemoveConst<T>::type type[N];
+};
+
+#if defined(_MSC_VER) && _MSC_VER < 1400
+// This is the only specialization that allows VC++ 7.1 to remove const in
+// 'const int[3] and 'const int[3][4]'.  However, it causes trouble with GCC
+// and thus needs to be conditionally compiled.
+template <typename T, size_t N>
+struct RemoveConst<T[N]> {
+  typedef typename RemoveConst<T>::type type[N];
+};
+#endif
+
+// A handy wrapper around RemoveConst that works when the argument
+// T depends on template parameters.
+#define GTEST_REMOVE_CONST_(T) \
+    typename ::testing::internal::RemoveConst<T>::type
+
+// Turns const U&, U&, const U, and U all into U.
+#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
+    GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T))
+
+// Adds reference to a type if it is not a reference type,
+// otherwise leaves it unchanged.  This is the same as
+// tr1::add_reference, which is not widely available yet.
+template <typename T>
+struct AddReference { typedef T& type; };  // NOLINT
+template <typename T>
+struct AddReference<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper around AddReference that works when the argument T
+// depends on template parameters.
+#define GTEST_ADD_REFERENCE_(T) \
+    typename ::testing::internal::AddReference<T>::type
+
+// Adds a reference to const on top of T as necessary.  For example,
+// it transforms
+//
+//   char         ==> const char&
+//   const char   ==> const char&
+//   char&        ==> const char&
+//   const char&  ==> const char&
+//
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+    GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T))
+
+// ImplicitlyConvertible<From, To>::value is a compile-time bool
+// constant that's true iff type From can be implicitly converted to
+// type To.
+template <typename From, typename To>
+class ImplicitlyConvertible {
+ private:
+  // We need the following helper functions only for their types.
+  // They have no implementations.
+
+  // MakeFrom() is an expression whose type is From.  We cannot simply
+  // use From(), as the type From may not have a public default
+  // constructor.
+  static From MakeFrom();
+
+  // These two functions are overloaded.  Given an expression
+  // Helper(x), the compiler will pick the first version if x can be
+  // implicitly converted to type To; otherwise it will pick the
+  // second version.
+  //
+  // The first version returns a value of size 1, and the second
+  // version returns a value of size 2.  Therefore, by checking the
+  // size of Helper(x), which can be done at compile time, we can tell
+  // which version of Helper() is used, and hence whether x can be
+  // implicitly converted to type To.
+  static char Helper(To);
+  static char (&Helper(...))[2];  // NOLINT
+
+  // We have to put the 'public' section after the 'private' section,
+  // or MSVC refuses to compile the code.
+ public:
+  // MSVC warns about implicitly converting from double to int for
+  // possible loss of data, so we need to temporarily disable the
+  // warning.
+#ifdef _MSC_VER
+# pragma warning(push)          // Saves the current warning state.
+# pragma warning(disable:4244)  // Temporarily disables warning 4244.
+
+  static const bool value =
+      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+# pragma warning(pop)           // Restores the warning state.
+#elif defined(__BORLANDC__)
+  // C++Builder cannot use member overload resolution during template
+  // instantiation.  The simplest workaround is to use its C++0x type traits
+  // functions (C++Builder 2009 and above only).
+  static const bool value = __is_convertible(From, To);
+#else
+  static const bool value =
+      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+#endif  // _MSV_VER
+};
+template <typename From, typename To>
+const bool ImplicitlyConvertible<From, To>::value;
+
+// IsAProtocolMessage<T>::value is a compile-time bool constant that's
+// true iff T is type ProtocolMessage, proto2::Message, or a subclass
+// of those.
+template <typename T>
+struct IsAProtocolMessage
+    : public bool_constant<
+  ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value ||
+  ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> {
+};
+
+// When the compiler sees expression IsContainerTest<C>(0), if C is an
+// STL-style container class, the first overload of IsContainerTest
+// will be viable (since both C::iterator* and C::const_iterator* are
+// valid types and NULL can be implicitly converted to them).  It will
+// be picked over the second overload as 'int' is a perfect match for
+// the type of argument 0.  If C::iterator or C::const_iterator is not
+// a valid type, the first overload is not viable, and the second
+// overload will be picked.  Therefore, we can determine whether C is
+// a container class by checking the type of IsContainerTest<C>(0).
+// The value of the expression is insignificant.
+//
+// Note that we look for both C::iterator and C::const_iterator.  The
+// reason is that C++ injects the name of a class as a member of the
+// class itself (e.g. you can refer to class iterator as either
+// 'iterator' or 'iterator::iterator').  If we look for C::iterator
+// only, for example, we would mistakenly think that a class named
+// iterator is an STL container.
+//
+// Also note that the simpler approach of overloading
+// IsContainerTest(typename C::const_iterator*) and
+// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
+typedef int IsContainer;
+template <class C>
+IsContainer IsContainerTest(int /* dummy */,
+                            typename C::iterator* /* it */ = NULL,
+                            typename C::const_iterator* /* const_it */ = NULL) {
+  return 0;
+}
+
+typedef char IsNotContainer;
+template <class C>
+IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
+
+// EnableIf<condition>::type is void when 'Cond' is true, and
+// undefined when 'Cond' is false.  To use SFINAE to make a function
+// overload only apply when a particular expression is true, add
+// "typename EnableIf<expression>::type* = 0" as the last parameter.
+template<bool> struct EnableIf;
+template<> struct EnableIf<true> { typedef void type; };  // NOLINT
+
+// Utilities for native arrays.
+
+// ArrayEq() compares two k-dimensional native arrays using the
+// elements' operator==, where k can be any integer >= 0.  When k is
+// 0, ArrayEq() degenerates into comparing a single pair of values.
+
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) {
+  return internal::ArrayEq(lhs, N, rhs);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous ArrayEq() function, arrays with different sizes would
+// lead to different copies of the template code.
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs) {
+  for (size_t i = 0; i != size; i++) {
+    if (!internal::ArrayEq(lhs[i], rhs[i]))
+      return false;
+  }
+  return true;
+}
+
+// Finds the first element in the iterator range [begin, end) that
+// equals elem.  Element may be a native array type itself.
+template <typename Iter, typename Element>
+Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) {
+  for (Iter it = begin; it != end; ++it) {
+    if (internal::ArrayEq(*it, elem))
+      return it;
+  }
+  return end;
+}
+
+// CopyArray() copies a k-dimensional native array using the elements'
+// operator=, where k can be any integer >= 0.  When k is 0,
+// CopyArray() degenerates into copying a single value.
+
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline void CopyArray(const T& from, U* to) { *to = from; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline void CopyArray(const T(&from)[N], U(*to)[N]) {
+  internal::CopyArray(from, N, *to);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous CopyArray() function, arrays with different sizes
+// would lead to different copies of the template code.
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to) {
+  for (size_t i = 0; i != size; i++) {
+    internal::CopyArray(from[i], to + i);
+  }
+}
+
+// The relation between an NativeArray object (see below) and the
+// native array it represents.
+enum RelationToSource {
+  kReference,  // The NativeArray references the native array.
+  kCopy        // The NativeArray makes a copy of the native array and
+               // owns the copy.
+};
+
+// Adapts a native array to a read-only STL-style container.  Instead
+// of the complete STL container concept, this adaptor only implements
+// members useful for Google Mock's container matchers.  New members
+// should be added as needed.  To simplify the implementation, we only
+// support Element being a raw type (i.e. having no top-level const or
+// reference modifier).  It's the client's responsibility to satisfy
+// this requirement.  Element can be an array type itself (hence
+// multi-dimensional arrays are supported).
+template <typename Element>
+class NativeArray {
+ public:
+  // STL-style container typedefs.
+  typedef Element value_type;
+  typedef Element* iterator;
+  typedef const Element* const_iterator;
+
+  // Constructs from a native array.
+  NativeArray(const Element* array, size_t count, RelationToSource relation) {
+    Init(array, count, relation);
+  }
+
+  // Copy constructor.
+  NativeArray(const NativeArray& rhs) {
+    Init(rhs.array_, rhs.size_, rhs.relation_to_source_);
+  }
+
+  ~NativeArray() {
+    // Ensures that the user doesn't instantiate NativeArray with a
+    // const or reference type.
+    static_cast<void>(StaticAssertTypeEqHelper<Element,
+        GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>());
+    if (relation_to_source_ == kCopy)
+      delete[] array_;
+  }
+
+  // STL-style container methods.
+  size_t size() const { return size_; }
+  const_iterator begin() const { return array_; }
+  const_iterator end() const { return array_ + size_; }
+  bool operator==(const NativeArray& rhs) const {
+    return size() == rhs.size() &&
+        ArrayEq(begin(), size(), rhs.begin());
+  }
+
+ private:
+  // Initializes this object; makes a copy of the input array if
+  // 'relation' is kCopy.
+  void Init(const Element* array, size_t a_size, RelationToSource relation) {
+    if (relation == kReference) {
+      array_ = array;
+    } else {
+      Element* const copy = new Element[a_size];
+      CopyArray(array, a_size, copy);
+      array_ = copy;
+    }
+    size_ = a_size;
+    relation_to_source_ = relation;
+  }
+
+  const Element* array_;
+  size_t size_;
+  RelationToSource relation_to_source_;
+
+  GTEST_DISALLOW_ASSIGN_(NativeArray);
+};
+
+}  // namespace internal
+}  // namespace testing
+
+#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
+  ::testing::internal::AssertHelper(result_type, file, line, message) \
+    = ::testing::Message()
+
+#define GTEST_MESSAGE_(message, result_type) \
+  GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
+
+#define GTEST_FATAL_FAILURE_(message) \
+  return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
+
+#define GTEST_NONFATAL_FAILURE_(message) \
+  GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
+
+#define GTEST_SUCCESS_(message) \
+  GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
+
+// Suppresses MSVC warnings 4072 (unreachable code) for the code following
+// statement if it returns or throws (or doesn't return or throw in some
+// situations).
+#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
+  if (::testing::internal::AlwaysTrue()) { statement; }
+
+#define GTEST_TEST_THROW_(statement, expected_exception, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::ConstCharPtr gtest_msg = "") { \
+    bool gtest_caught_expected = false; \
+    try { \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    } \
+    catch (expected_exception const&) { \
+      gtest_caught_expected = true; \
+    } \
+    catch (...) { \
+      gtest_msg.value = \
+          "Expected: " #statement " throws an exception of type " \
+          #expected_exception ".\n  Actual: it throws a different type."; \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+    } \
+    if (!gtest_caught_expected) { \
+      gtest_msg.value = \
+          "Expected: " #statement " throws an exception of type " \
+          #expected_exception ".\n  Actual: it throws nothing."; \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
+      fail(gtest_msg.value)
+
+#define GTEST_TEST_NO_THROW_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    try { \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    } \
+    catch (...) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
+      fail("Expected: " #statement " doesn't throw an exception.\n" \
+           "  Actual: it throws.")
+
+#define GTEST_TEST_ANY_THROW_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    bool gtest_caught_any = false; \
+    try { \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    } \
+    catch (...) { \
+      gtest_caught_any = true; \
+    } \
+    if (!gtest_caught_any) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \
+      fail("Expected: " #statement " throws an exception.\n" \
+           "  Actual: it doesn't.")
+
+
+// Implements Boolean test assertions such as EXPECT_TRUE. expression can be
+// either a boolean expression or an AssertionResult. text is a textual
+// represenation of expression as it was passed into the EXPECT_TRUE.
+#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (const ::testing::AssertionResult gtest_ar_ = \
+      ::testing::AssertionResult(expression)) \
+    ; \
+  else \
+    fail(::testing::internal::GetBoolAssertionFailureMessage(\
+        gtest_ar_, text, #actual, #expected).c_str())
+
+#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \
+      fail("Expected: " #statement " doesn't generate new fatal " \
+           "failures in the current thread.\n" \
+           "  Actual: it does.")
+
+// Expands to the name of the class that implements the given test.
+#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
+  test_case_name##_##test_name##_Test
+
+// Helper macro for defining tests.
+#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
+class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
+ public:\
+  GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
+ private:\
+  virtual void TestBody();\
+  static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+      GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
+};\
+\
+::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
+  ::test_info_ =\
+    ::testing::internal::MakeAndRegisterTestInfo(\
+        #test_case_name, #test_name, NULL, NULL, \
+        (parent_id), \
+        parent_class::SetUpTestCase, \
+        parent_class::TearDownTestCase, \
+        new ::testing::internal::TestFactoryImpl<\
+            GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
+void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-linked_ptr.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-linked_ptr.h
new file mode 100644
index 0000000..b1362cd
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-linked_ptr.h
@@ -0,0 +1,233 @@
+// Copyright 2003 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: Dan Egnor (egnor at google.com)
+//
+// A "smart" pointer type with reference tracking.  Every pointer to a
+// particular object is kept on a circular linked list.  When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is assigned, the entire list of pointers to that
+//   object is traversed.  This class is therefore NOT SUITABLE when there
+//   will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+//   If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+//   will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Bill Gibbons suggested we use something like this.
+//
+// Thread Safety:
+//   Unlike other linked_ptr implementations, in this implementation
+//   a linked_ptr object is thread-safe in the sense that:
+//     - it's safe to copy linked_ptr objects concurrently,
+//     - it's safe to copy *from* a linked_ptr and read its underlying
+//       raw pointer (e.g. via get()) concurrently, and
+//     - it's safe to write to two linked_ptrs that point to the same
+//       shared object concurrently.
+// TODO(wan at google.com): rename this to safe_linked_ptr to avoid
+// confusion with normal linked_ptr.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// Protects copying of all linked_ptr objects.
+GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
+
+// This is used internally by all instances of linked_ptr<>.  It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF.  Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+  // Create a new circle that includes only this instance.
+  void join_new() {
+    next_ = this;
+  }
+
+  // Many linked_ptr operations may change p.link_ for some linked_ptr
+  // variable p in the same circle as this object.  Therefore we need
+  // to prevent two such operations from occurring concurrently.
+  //
+  // Note that different types of linked_ptr objects can coexist in a
+  // circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
+  // linked_ptr<Derived2>).  Therefore we must use a single mutex to
+  // protect all linked_ptr objects.  This can create serious
+  // contention in production code, but is acceptable in a testing
+  // framework.
+
+  // Join an existing circle.
+  void join(linked_ptr_internal const* ptr)
+      GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+    MutexLock lock(&g_linked_ptr_mutex);
+
+    linked_ptr_internal const* p = ptr;
+    while (p->next_ != ptr) p = p->next_;
+    p->next_ = this;
+    next_ = ptr;
+  }
+
+  // Leave whatever circle we're part of.  Returns true if we were the
+  // last member of the circle.  Once this is done, you can join() another.
+  bool depart()
+      GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
+    MutexLock lock(&g_linked_ptr_mutex);
+
+    if (next_ == this) return true;
+    linked_ptr_internal const* p = next_;
+    while (p->next_ != this) p = p->next_;
+    p->next_ = next_;
+    return false;
+  }
+
+ private:
+  mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+  typedef T element_type;
+
+  // Take over ownership of a raw pointer.  This should happen as soon as
+  // possible after the object is created.
+  explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+  ~linked_ptr() { depart(); }
+
+  // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+  template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+  linked_ptr(linked_ptr const& ptr) {  // NOLINT
+    assert(&ptr != this);
+    copy(&ptr);
+  }
+
+  // Assignment releases the old value and acquires the new.
+  template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+    depart();
+    copy(&ptr);
+    return *this;
+  }
+
+  linked_ptr& operator=(linked_ptr const& ptr) {
+    if (&ptr != this) {
+      depart();
+      copy(&ptr);
+    }
+    return *this;
+  }
+
+  // Smart pointer members.
+  void reset(T* ptr = NULL) {
+    depart();
+    capture(ptr);
+  }
+  T* get() const { return value_; }
+  T* operator->() const { return value_; }
+  T& operator*() const { return *value_; }
+
+  bool operator==(T* p) const { return value_ == p; }
+  bool operator!=(T* p) const { return value_ != p; }
+  template <typename U>
+  bool operator==(linked_ptr<U> const& ptr) const {
+    return value_ == ptr.get();
+  }
+  template <typename U>
+  bool operator!=(linked_ptr<U> const& ptr) const {
+    return value_ != ptr.get();
+  }
+
+ private:
+  template <typename U>
+  friend class linked_ptr;
+
+  T* value_;
+  linked_ptr_internal link_;
+
+  void depart() {
+    if (link_.depart()) delete value_;
+  }
+
+  void capture(T* ptr) {
+    value_ = ptr;
+    link_.join_new();
+  }
+
+  template <typename U> void copy(linked_ptr<U> const* ptr) {
+    value_ = ptr->get();
+    if (value_)
+      link_.join(&ptr->link_);
+    else
+      link_.join_new();
+  }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+  return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+  return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+  return linked_ptr<T>(ptr);
+}
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h
new file mode 100644
index 0000000..e805485
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h
@@ -0,0 +1,5143 @@
+// This file was GENERATED by command:
+//     pump.py gtest-param-util-generated.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+// This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+// Currently Google Test supports at most 50 arguments in Values,
+// and at most 10 arguments in Combine. Please contact
+// googletestframework at googlegroups.com if you need more.
+// Please note that the number of arguments to Combine is limited
+// by the maximum arity of the implementation of tr1::tuple which is
+// currently set at 10.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*.  Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <typename ForwardIterator>
+internal::ParamGenerator<
+  typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end);
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container);
+
+namespace internal {
+
+// Used in the Values() function to provide polymorphic capabilities.
+template <typename T1>
+class ValueArray1 {
+ public:
+  explicit ValueArray1(T1 v1) : v1_(v1) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray1& other);
+
+  const T1 v1_;
+};
+
+template <typename T1, typename T2>
+class ValueArray2 {
+ public:
+  ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray2& other);
+
+  const T1 v1_;
+  const T2 v2_;
+};
+
+template <typename T1, typename T2, typename T3>
+class ValueArray3 {
+ public:
+  ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray3& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+class ValueArray4 {
+ public:
+  ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray4& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class ValueArray5 {
+ public:
+  ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray5& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+class ValueArray6 {
+ public:
+  ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray6& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+class ValueArray7 {
+ public:
+  ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray7& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+class ValueArray8 {
+ public:
+  ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7,
+      T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray8& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+class ValueArray9 {
+ public:
+  ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8,
+      T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray9& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+class ValueArray10 {
+ public:
+  ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray10& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11>
+class ValueArray11 {
+ public:
+  ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+      v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray11& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12>
+class ValueArray12 {
+ public:
+  ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+      v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray12& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13>
+class ValueArray13 {
+ public:
+  ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+      v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+      v12_(v12), v13_(v13) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray13& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14>
+class ValueArray14 {
+ public:
+  ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray14& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15>
+class ValueArray15 {
+ public:
+  ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray15& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16>
+class ValueArray16 {
+ public:
+  ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+      v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+      v16_(v16) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray16& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17>
+class ValueArray17 {
+ public:
+  ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16,
+      T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray17& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18>
+class ValueArray18 {
+ public:
+  ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray18& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19>
+class ValueArray19 {
+ public:
+  ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+      v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+      v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray19& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20>
+class ValueArray20 {
+ public:
+  ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+      v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+      v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+      v19_(v19), v20_(v20) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray20& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21>
+class ValueArray21 {
+ public:
+  ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+      v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+      v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+      v18_(v18), v19_(v19), v20_(v20), v21_(v21) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray21& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22>
+class ValueArray22 {
+ public:
+  ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray22& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23>
+class ValueArray23 {
+ public:
+  ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray23& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24>
+class ValueArray24 {
+ public:
+  ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+      v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+      v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+      v22_(v22), v23_(v23), v24_(v24) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray24& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25>
+class ValueArray25 {
+ public:
+  ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24,
+      T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray25& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26>
+class ValueArray26 {
+ public:
+  ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray26& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27>
+class ValueArray27 {
+ public:
+  ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+      v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+      v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+      v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+      v26_(v26), v27_(v27) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray27& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28>
+class ValueArray28 {
+ public:
+  ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+      v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+      v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+      v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+      v25_(v25), v26_(v26), v27_(v27), v28_(v28) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray28& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29>
+class ValueArray29 {
+ public:
+  ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+      v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+      v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+      v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+      v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray29& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30>
+class ValueArray30 {
+ public:
+  ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray30& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31>
+class ValueArray31 {
+ public:
+  ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30), v31_(v31) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray31& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32>
+class ValueArray32 {
+ public:
+  ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+      v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+      v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+      v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+      v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray32& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33>
+class ValueArray33 {
+ public:
+  ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32,
+      T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray33& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34>
+class ValueArray34 {
+ public:
+  ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33), v34_(v34) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray34& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35>
+class ValueArray35 {
+ public:
+  ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+      v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+      v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+      v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+      v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+      v32_(v32), v33_(v33), v34_(v34), v35_(v35) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray35& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36>
+class ValueArray36 {
+ public:
+  ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+      v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+      v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+      v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+      v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+      v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray36& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37>
+class ValueArray37 {
+ public:
+  ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+      v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+      v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+      v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+      v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+      v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+      v36_(v36), v37_(v37) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray37& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38>
+class ValueArray38 {
+ public:
+  ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+      v35_(v35), v36_(v36), v37_(v37), v38_(v38) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray38& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39>
+class ValueArray39 {
+ public:
+  ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+      v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray39& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40>
+class ValueArray40 {
+ public:
+  ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+      v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+      v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+      v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+      v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+      v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+      v40_(v40) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray40& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41>
+class ValueArray41 {
+ public:
+  ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40,
+      T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+      v39_(v39), v40_(v40), v41_(v41) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray41& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42>
+class ValueArray42 {
+ public:
+  ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+      v39_(v39), v40_(v40), v41_(v41), v42_(v42) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray42& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43>
+class ValueArray43 {
+ public:
+  ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6),
+      v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13),
+      v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19),
+      v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25),
+      v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31),
+      v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37),
+      v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray43& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44>
+class ValueArray44 {
+ public:
+  ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5),
+      v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12),
+      v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18),
+      v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24),
+      v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30),
+      v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36),
+      v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42),
+      v43_(v43), v44_(v44) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray44& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45>
+class ValueArray45 {
+ public:
+  ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4),
+      v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11),
+      v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17),
+      v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23),
+      v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29),
+      v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35),
+      v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41),
+      v42_(v42), v43_(v43), v44_(v44), v45_(v45) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray45& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46>
+class ValueArray46 {
+ public:
+  ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3),
+      v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+      v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+      v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_), static_cast<T>(v46_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray46& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+  const T46 v46_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47>
+class ValueArray47 {
+ public:
+  ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2),
+      v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10),
+      v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16),
+      v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22),
+      v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28),
+      v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34),
+      v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40),
+      v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46),
+      v47_(v47) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray47& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+  const T46 v46_;
+  const T47 v47_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48>
+class ValueArray48 {
+ public:
+  ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1),
+      v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9),
+      v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15),
+      v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21),
+      v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27),
+      v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33),
+      v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39),
+      v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45),
+      v46_(v46), v47_(v47), v48_(v48) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+        static_cast<T>(v48_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray48& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+  const T46 v46_;
+  const T47 v47_;
+  const T48 v48_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49>
+class ValueArray49 {
+ public:
+  ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48,
+      T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+      v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+      v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+        static_cast<T>(v48_), static_cast<T>(v49_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray49& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+  const T46 v46_;
+  const T47 v47_;
+  const T48 v48_;
+  const T49 v49_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49, typename T50>
+class ValueArray50 {
+ public:
+  ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9,
+      T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17,
+      T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25,
+      T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33,
+      T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41,
+      T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49,
+      T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7),
+      v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14),
+      v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20),
+      v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26),
+      v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32),
+      v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38),
+      v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44),
+      v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {static_cast<T>(v1_), static_cast<T>(v2_),
+        static_cast<T>(v3_), static_cast<T>(v4_), static_cast<T>(v5_),
+        static_cast<T>(v6_), static_cast<T>(v7_), static_cast<T>(v8_),
+        static_cast<T>(v9_), static_cast<T>(v10_), static_cast<T>(v11_),
+        static_cast<T>(v12_), static_cast<T>(v13_), static_cast<T>(v14_),
+        static_cast<T>(v15_), static_cast<T>(v16_), static_cast<T>(v17_),
+        static_cast<T>(v18_), static_cast<T>(v19_), static_cast<T>(v20_),
+        static_cast<T>(v21_), static_cast<T>(v22_), static_cast<T>(v23_),
+        static_cast<T>(v24_), static_cast<T>(v25_), static_cast<T>(v26_),
+        static_cast<T>(v27_), static_cast<T>(v28_), static_cast<T>(v29_),
+        static_cast<T>(v30_), static_cast<T>(v31_), static_cast<T>(v32_),
+        static_cast<T>(v33_), static_cast<T>(v34_), static_cast<T>(v35_),
+        static_cast<T>(v36_), static_cast<T>(v37_), static_cast<T>(v38_),
+        static_cast<T>(v39_), static_cast<T>(v40_), static_cast<T>(v41_),
+        static_cast<T>(v42_), static_cast<T>(v43_), static_cast<T>(v44_),
+        static_cast<T>(v45_), static_cast<T>(v46_), static_cast<T>(v47_),
+        static_cast<T>(v48_), static_cast<T>(v49_), static_cast<T>(v50_)};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray50& other);
+
+  const T1 v1_;
+  const T2 v2_;
+  const T3 v3_;
+  const T4 v4_;
+  const T5 v5_;
+  const T6 v6_;
+  const T7 v7_;
+  const T8 v8_;
+  const T9 v9_;
+  const T10 v10_;
+  const T11 v11_;
+  const T12 v12_;
+  const T13 v13_;
+  const T14 v14_;
+  const T15 v15_;
+  const T16 v16_;
+  const T17 v17_;
+  const T18 v18_;
+  const T19 v19_;
+  const T20 v20_;
+  const T21 v21_;
+  const T22 v22_;
+  const T23 v23_;
+  const T24 v24_;
+  const T25 v25_;
+  const T26 v26_;
+  const T27 v27_;
+  const T28 v28_;
+  const T29 v29_;
+  const T30 v30_;
+  const T31 v31_;
+  const T32 v32_;
+  const T33 v33_;
+  const T34 v34_;
+  const T35 v35_;
+  const T36 v36_;
+  const T37 v37_;
+  const T38 v38_;
+  const T39 v39_;
+  const T40 v40_;
+  const T41 v41_;
+  const T42 v42_;
+  const T43 v43_;
+  const T44 v44_;
+  const T45 v45_;
+  const T46 v46_;
+  const T47 v47_;
+  const T48 v48_;
+  const T49 v49_;
+  const T50 v50_;
+};
+
+# if GTEST_HAS_COMBINE
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Generates values from the Cartesian product of values produced
+// by the argument generators.
+//
+template <typename T1, typename T2>
+class CartesianProductGenerator2
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2> ParamType;
+
+  CartesianProductGenerator2(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2)
+      : g1_(g1), g2_(g2) {}
+  virtual ~CartesianProductGenerator2() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current2_;
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator2::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator2& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+};  // class CartesianProductGenerator2
+
+
+template <typename T1, typename T2, typename T3>
+class CartesianProductGenerator3
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3> ParamType;
+
+  CartesianProductGenerator3(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3)
+      : g1_(g1), g2_(g2), g3_(g3) {}
+  virtual ~CartesianProductGenerator3() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current3_;
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator3::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator3& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+};  // class CartesianProductGenerator3
+
+
+template <typename T1, typename T2, typename T3, typename T4>
+class CartesianProductGenerator4
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4> ParamType;
+
+  CartesianProductGenerator4(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+  virtual ~CartesianProductGenerator4() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current4_;
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator4::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator4& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+};  // class CartesianProductGenerator4
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class CartesianProductGenerator5
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5> ParamType;
+
+  CartesianProductGenerator5(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+  virtual ~CartesianProductGenerator5() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current5_;
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator5::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator5& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+};  // class CartesianProductGenerator5
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+class CartesianProductGenerator6
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5,
+        T6> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> ParamType;
+
+  CartesianProductGenerator6(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+      const ParamGenerator<T6>& g6)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+  virtual ~CartesianProductGenerator6() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5,
+      const ParamGenerator<T6>& g6,
+      const typename ParamGenerator<T6>::iterator& current6)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+          begin6_(g6.begin()), end6_(g6.end()), current6_(current6)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current6_;
+      if (current6_ == end6_) {
+        current6_ = begin6_;
+        ++current5_;
+      }
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_ &&
+          current6_ == typed_other->current6_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_),
+        begin6_(other.begin6_),
+        end6_(other.end6_),
+        current6_(other.current6_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_ ||
+          current6_ == end6_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    const typename ParamGenerator<T6>::iterator begin6_;
+    const typename ParamGenerator<T6>::iterator end6_;
+    typename ParamGenerator<T6>::iterator current6_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator6::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator6& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+  const ParamGenerator<T6> g6_;
+};  // class CartesianProductGenerator6
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+class CartesianProductGenerator7
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+        T7> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType;
+
+  CartesianProductGenerator7(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+      const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+  virtual ~CartesianProductGenerator7() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+        g7_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5,
+      const ParamGenerator<T6>& g6,
+      const typename ParamGenerator<T6>::iterator& current6,
+      const ParamGenerator<T7>& g7,
+      const typename ParamGenerator<T7>::iterator& current7)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+          begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+          begin7_(g7.begin()), end7_(g7.end()), current7_(current7)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current7_;
+      if (current7_ == end7_) {
+        current7_ = begin7_;
+        ++current6_;
+      }
+      if (current6_ == end6_) {
+        current6_ = begin6_;
+        ++current5_;
+      }
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_ &&
+          current6_ == typed_other->current6_ &&
+          current7_ == typed_other->current7_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_),
+        begin6_(other.begin6_),
+        end6_(other.end6_),
+        current6_(other.current6_),
+        begin7_(other.begin7_),
+        end7_(other.end7_),
+        current7_(other.current7_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_ ||
+          current6_ == end6_ ||
+          current7_ == end7_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    const typename ParamGenerator<T6>::iterator begin6_;
+    const typename ParamGenerator<T6>::iterator end6_;
+    typename ParamGenerator<T6>::iterator current6_;
+    const typename ParamGenerator<T7>::iterator begin7_;
+    const typename ParamGenerator<T7>::iterator end7_;
+    typename ParamGenerator<T7>::iterator current7_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator7::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator7& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+  const ParamGenerator<T6> g6_;
+  const ParamGenerator<T7> g7_;
+};  // class CartesianProductGenerator7
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+class CartesianProductGenerator8
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+        T7, T8> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType;
+
+  CartesianProductGenerator8(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+      const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+      const ParamGenerator<T8>& g8)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+          g8_(g8) {}
+  virtual ~CartesianProductGenerator8() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+        g7_.begin(), g8_, g8_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+        g8_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5,
+      const ParamGenerator<T6>& g6,
+      const typename ParamGenerator<T6>::iterator& current6,
+      const ParamGenerator<T7>& g7,
+      const typename ParamGenerator<T7>::iterator& current7,
+      const ParamGenerator<T8>& g8,
+      const typename ParamGenerator<T8>::iterator& current8)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+          begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+          begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+          begin8_(g8.begin()), end8_(g8.end()), current8_(current8)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current8_;
+      if (current8_ == end8_) {
+        current8_ = begin8_;
+        ++current7_;
+      }
+      if (current7_ == end7_) {
+        current7_ = begin7_;
+        ++current6_;
+      }
+      if (current6_ == end6_) {
+        current6_ = begin6_;
+        ++current5_;
+      }
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_ &&
+          current6_ == typed_other->current6_ &&
+          current7_ == typed_other->current7_ &&
+          current8_ == typed_other->current8_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_),
+        begin6_(other.begin6_),
+        end6_(other.end6_),
+        current6_(other.current6_),
+        begin7_(other.begin7_),
+        end7_(other.end7_),
+        current7_(other.current7_),
+        begin8_(other.begin8_),
+        end8_(other.end8_),
+        current8_(other.current8_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_, *current8_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_ ||
+          current6_ == end6_ ||
+          current7_ == end7_ ||
+          current8_ == end8_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    const typename ParamGenerator<T6>::iterator begin6_;
+    const typename ParamGenerator<T6>::iterator end6_;
+    typename ParamGenerator<T6>::iterator current6_;
+    const typename ParamGenerator<T7>::iterator begin7_;
+    const typename ParamGenerator<T7>::iterator end7_;
+    typename ParamGenerator<T7>::iterator current7_;
+    const typename ParamGenerator<T8>::iterator begin8_;
+    const typename ParamGenerator<T8>::iterator end8_;
+    typename ParamGenerator<T8>::iterator current8_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator8::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator8& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+  const ParamGenerator<T6> g6_;
+  const ParamGenerator<T7> g7_;
+  const ParamGenerator<T8> g8_;
+};  // class CartesianProductGenerator8
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+class CartesianProductGenerator9
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+        T7, T8, T9> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType;
+
+  CartesianProductGenerator9(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+      const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+      const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+          g9_(g9) {}
+  virtual ~CartesianProductGenerator9() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+        g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+        g8_.end(), g9_, g9_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5,
+      const ParamGenerator<T6>& g6,
+      const typename ParamGenerator<T6>::iterator& current6,
+      const ParamGenerator<T7>& g7,
+      const typename ParamGenerator<T7>::iterator& current7,
+      const ParamGenerator<T8>& g8,
+      const typename ParamGenerator<T8>::iterator& current8,
+      const ParamGenerator<T9>& g9,
+      const typename ParamGenerator<T9>::iterator& current9)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+          begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+          begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+          begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+          begin9_(g9.begin()), end9_(g9.end()), current9_(current9)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current9_;
+      if (current9_ == end9_) {
+        current9_ = begin9_;
+        ++current8_;
+      }
+      if (current8_ == end8_) {
+        current8_ = begin8_;
+        ++current7_;
+      }
+      if (current7_ == end7_) {
+        current7_ = begin7_;
+        ++current6_;
+      }
+      if (current6_ == end6_) {
+        current6_ = begin6_;
+        ++current5_;
+      }
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_ &&
+          current6_ == typed_other->current6_ &&
+          current7_ == typed_other->current7_ &&
+          current8_ == typed_other->current8_ &&
+          current9_ == typed_other->current9_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_),
+        begin6_(other.begin6_),
+        end6_(other.end6_),
+        current6_(other.current6_),
+        begin7_(other.begin7_),
+        end7_(other.end7_),
+        current7_(other.current7_),
+        begin8_(other.begin8_),
+        end8_(other.end8_),
+        current8_(other.current8_),
+        begin9_(other.begin9_),
+        end9_(other.end9_),
+        current9_(other.current9_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_, *current8_,
+            *current9_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_ ||
+          current6_ == end6_ ||
+          current7_ == end7_ ||
+          current8_ == end8_ ||
+          current9_ == end9_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    const typename ParamGenerator<T6>::iterator begin6_;
+    const typename ParamGenerator<T6>::iterator end6_;
+    typename ParamGenerator<T6>::iterator current6_;
+    const typename ParamGenerator<T7>::iterator begin7_;
+    const typename ParamGenerator<T7>::iterator end7_;
+    typename ParamGenerator<T7>::iterator current7_;
+    const typename ParamGenerator<T8>::iterator begin8_;
+    const typename ParamGenerator<T8>::iterator end8_;
+    typename ParamGenerator<T8>::iterator current8_;
+    const typename ParamGenerator<T9>::iterator begin9_;
+    const typename ParamGenerator<T9>::iterator end9_;
+    typename ParamGenerator<T9>::iterator current9_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator9::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator9& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+  const ParamGenerator<T6> g6_;
+  const ParamGenerator<T7> g7_;
+  const ParamGenerator<T8> g8_;
+  const ParamGenerator<T9> g9_;
+};  // class CartesianProductGenerator9
+
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+class CartesianProductGenerator10
+    : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+        T7, T8, T9, T10> > {
+ public:
+  typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType;
+
+  CartesianProductGenerator10(const ParamGenerator<T1>& g1,
+      const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3,
+      const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5,
+      const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7,
+      const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9,
+      const ParamGenerator<T10>& g10)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+          g9_(g9), g10_(g10) {}
+  virtual ~CartesianProductGenerator10() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_,
+        g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_,
+        g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin());
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(),
+        g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_,
+        g8_.end(), g9_, g9_.end(), g10_, g10_.end());
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base,
+      const ParamGenerator<T1>& g1,
+      const typename ParamGenerator<T1>::iterator& current1,
+      const ParamGenerator<T2>& g2,
+      const typename ParamGenerator<T2>::iterator& current2,
+      const ParamGenerator<T3>& g3,
+      const typename ParamGenerator<T3>::iterator& current3,
+      const ParamGenerator<T4>& g4,
+      const typename ParamGenerator<T4>::iterator& current4,
+      const ParamGenerator<T5>& g5,
+      const typename ParamGenerator<T5>::iterator& current5,
+      const ParamGenerator<T6>& g6,
+      const typename ParamGenerator<T6>::iterator& current6,
+      const ParamGenerator<T7>& g7,
+      const typename ParamGenerator<T7>::iterator& current7,
+      const ParamGenerator<T8>& g8,
+      const typename ParamGenerator<T8>::iterator& current8,
+      const ParamGenerator<T9>& g9,
+      const typename ParamGenerator<T9>::iterator& current9,
+      const ParamGenerator<T10>& g10,
+      const typename ParamGenerator<T10>::iterator& current10)
+        : base_(base),
+          begin1_(g1.begin()), end1_(g1.end()), current1_(current1),
+          begin2_(g2.begin()), end2_(g2.end()), current2_(current2),
+          begin3_(g3.begin()), end3_(g3.end()), current3_(current3),
+          begin4_(g4.begin()), end4_(g4.end()), current4_(current4),
+          begin5_(g5.begin()), end5_(g5.end()), current5_(current5),
+          begin6_(g6.begin()), end6_(g6.end()), current6_(current6),
+          begin7_(g7.begin()), end7_(g7.end()), current7_(current7),
+          begin8_(g8.begin()), end8_(g8.end()), current8_(current8),
+          begin9_(g9.begin()), end9_(g9.end()), current9_(current9),
+          begin10_(g10.begin()), end10_(g10.end()), current10_(current10)    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current10_;
+      if (current10_ == end10_) {
+        current10_ = begin10_;
+        ++current9_;
+      }
+      if (current9_ == end9_) {
+        current9_ = begin9_;
+        ++current8_;
+      }
+      if (current8_ == end8_) {
+        current8_ = begin8_;
+        ++current7_;
+      }
+      if (current7_ == end7_) {
+        current7_ = begin7_;
+        ++current6_;
+      }
+      if (current6_ == end6_) {
+        current6_ = begin6_;
+        ++current5_;
+      }
+      if (current5_ == end5_) {
+        current5_ = begin5_;
+        ++current4_;
+      }
+      if (current4_ == end4_) {
+        current4_ = begin4_;
+        ++current3_;
+      }
+      if (current3_ == end3_) {
+        current3_ = begin3_;
+        ++current2_;
+      }
+      if (current2_ == end2_) {
+        current2_ = begin2_;
+        ++current1_;
+      }
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         (
+          current1_ == typed_other->current1_ &&
+          current2_ == typed_other->current2_ &&
+          current3_ == typed_other->current3_ &&
+          current4_ == typed_other->current4_ &&
+          current5_ == typed_other->current5_ &&
+          current6_ == typed_other->current6_ &&
+          current7_ == typed_other->current7_ &&
+          current8_ == typed_other->current8_ &&
+          current9_ == typed_other->current9_ &&
+          current10_ == typed_other->current10_);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_),
+        begin1_(other.begin1_),
+        end1_(other.end1_),
+        current1_(other.current1_),
+        begin2_(other.begin2_),
+        end2_(other.end2_),
+        current2_(other.current2_),
+        begin3_(other.begin3_),
+        end3_(other.end3_),
+        current3_(other.current3_),
+        begin4_(other.begin4_),
+        end4_(other.end4_),
+        current4_(other.current4_),
+        begin5_(other.begin5_),
+        end5_(other.end5_),
+        current5_(other.current5_),
+        begin6_(other.begin6_),
+        end6_(other.end6_),
+        current6_(other.current6_),
+        begin7_(other.begin7_),
+        end7_(other.end7_),
+        current7_(other.current7_),
+        begin8_(other.begin8_),
+        end8_(other.end8_),
+        current8_(other.current8_),
+        begin9_(other.begin9_),
+        end9_(other.end9_),
+        current9_(other.current9_),
+        begin10_(other.begin10_),
+        end10_(other.end10_),
+        current10_(other.current10_) {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType(*current1_, *current2_, *current3_,
+            *current4_, *current5_, *current6_, *current7_, *current8_,
+            *current9_, *current10_);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+          current1_ == end1_ ||
+          current2_ == end2_ ||
+          current3_ == end3_ ||
+          current4_ == end4_ ||
+          current5_ == end5_ ||
+          current6_ == end6_ ||
+          current7_ == end7_ ||
+          current8_ == end8_ ||
+          current9_ == end9_ ||
+          current10_ == end10_;
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+    const typename ParamGenerator<T1>::iterator begin1_;
+    const typename ParamGenerator<T1>::iterator end1_;
+    typename ParamGenerator<T1>::iterator current1_;
+    const typename ParamGenerator<T2>::iterator begin2_;
+    const typename ParamGenerator<T2>::iterator end2_;
+    typename ParamGenerator<T2>::iterator current2_;
+    const typename ParamGenerator<T3>::iterator begin3_;
+    const typename ParamGenerator<T3>::iterator end3_;
+    typename ParamGenerator<T3>::iterator current3_;
+    const typename ParamGenerator<T4>::iterator begin4_;
+    const typename ParamGenerator<T4>::iterator end4_;
+    typename ParamGenerator<T4>::iterator current4_;
+    const typename ParamGenerator<T5>::iterator begin5_;
+    const typename ParamGenerator<T5>::iterator end5_;
+    typename ParamGenerator<T5>::iterator current5_;
+    const typename ParamGenerator<T6>::iterator begin6_;
+    const typename ParamGenerator<T6>::iterator end6_;
+    typename ParamGenerator<T6>::iterator current6_;
+    const typename ParamGenerator<T7>::iterator begin7_;
+    const typename ParamGenerator<T7>::iterator end7_;
+    typename ParamGenerator<T7>::iterator current7_;
+    const typename ParamGenerator<T8>::iterator begin8_;
+    const typename ParamGenerator<T8>::iterator end8_;
+    typename ParamGenerator<T8>::iterator current8_;
+    const typename ParamGenerator<T9>::iterator begin9_;
+    const typename ParamGenerator<T9>::iterator end9_;
+    typename ParamGenerator<T9>::iterator current9_;
+    const typename ParamGenerator<T10>::iterator begin10_;
+    const typename ParamGenerator<T10>::iterator end10_;
+    typename ParamGenerator<T10>::iterator current10_;
+    ParamType current_value_;
+  };  // class CartesianProductGenerator10::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator10& other);
+
+  const ParamGenerator<T1> g1_;
+  const ParamGenerator<T2> g2_;
+  const ParamGenerator<T3> g3_;
+  const ParamGenerator<T4> g4_;
+  const ParamGenerator<T5> g5_;
+  const ParamGenerator<T6> g6_;
+  const ParamGenerator<T7> g7_;
+  const ParamGenerator<T8> g8_;
+  const ParamGenerator<T9> g9_;
+  const ParamGenerator<T10> g10_;
+};  // class CartesianProductGenerator10
+
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Helper classes providing Combine() with polymorphic features. They allow
+// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
+// convertible to U.
+//
+template <class Generator1, class Generator2>
+class CartesianProductHolder2 {
+ public:
+CartesianProductHolder2(const Generator1& g1, const Generator2& g2)
+      : g1_(g1), g2_(g2) {}
+  template <typename T1, typename T2>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2> >(
+        new CartesianProductGenerator2<T1, T2>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder2& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+};  // class CartesianProductHolder2
+
+template <class Generator1, class Generator2, class Generator3>
+class CartesianProductHolder3 {
+ public:
+CartesianProductHolder3(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3)
+      : g1_(g1), g2_(g2), g3_(g3) {}
+  template <typename T1, typename T2, typename T3>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >(
+        new CartesianProductGenerator3<T1, T2, T3>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder3& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+};  // class CartesianProductHolder3
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4>
+class CartesianProductHolder4 {
+ public:
+CartesianProductHolder4(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {}
+  template <typename T1, typename T2, typename T3, typename T4>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >(
+        new CartesianProductGenerator4<T1, T2, T3, T4>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder4& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+};  // class CartesianProductHolder4
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5>
+class CartesianProductHolder5 {
+ public:
+CartesianProductHolder5(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >(
+        new CartesianProductGenerator5<T1, T2, T3, T4, T5>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder5& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+};  // class CartesianProductHolder5
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5, class Generator6>
+class CartesianProductHolder6 {
+ public:
+CartesianProductHolder6(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5,
+    const Generator6& g6)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5,
+      typename T6>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >(
+        new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_),
+        static_cast<ParamGenerator<T6> >(g6_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder6& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+  const Generator6 g6_;
+};  // class CartesianProductHolder6
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5, class Generator6, class Generator7>
+class CartesianProductHolder7 {
+ public:
+CartesianProductHolder7(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5,
+    const Generator6& g6, const Generator7& g7)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5,
+      typename T6, typename T7>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6,
+      T7> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> >(
+        new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_),
+        static_cast<ParamGenerator<T6> >(g6_),
+        static_cast<ParamGenerator<T7> >(g7_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder7& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+  const Generator6 g6_;
+  const Generator7 g7_;
+};  // class CartesianProductHolder7
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5, class Generator6, class Generator7,
+    class Generator8>
+class CartesianProductHolder8 {
+ public:
+CartesianProductHolder8(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5,
+    const Generator6& g6, const Generator7& g7, const Generator8& g8)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7),
+          g8_(g8) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5,
+      typename T6, typename T7, typename T8>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7,
+      T8> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >(
+        new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_),
+        static_cast<ParamGenerator<T6> >(g6_),
+        static_cast<ParamGenerator<T7> >(g7_),
+        static_cast<ParamGenerator<T8> >(g8_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder8& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+  const Generator6 g6_;
+  const Generator7 g7_;
+  const Generator8 g8_;
+};  // class CartesianProductHolder8
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5, class Generator6, class Generator7,
+    class Generator8, class Generator9>
+class CartesianProductHolder9 {
+ public:
+CartesianProductHolder9(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5,
+    const Generator6& g6, const Generator7& g7, const Generator8& g8,
+    const Generator9& g9)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+          g9_(g9) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5,
+      typename T6, typename T7, typename T8, typename T9>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+      T9> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+        T9> >(
+        new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_),
+        static_cast<ParamGenerator<T6> >(g6_),
+        static_cast<ParamGenerator<T7> >(g7_),
+        static_cast<ParamGenerator<T8> >(g8_),
+        static_cast<ParamGenerator<T9> >(g9_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder9& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+  const Generator6 g6_;
+  const Generator7 g7_;
+  const Generator8 g8_;
+  const Generator9 g9_;
+};  // class CartesianProductHolder9
+
+template <class Generator1, class Generator2, class Generator3,
+    class Generator4, class Generator5, class Generator6, class Generator7,
+    class Generator8, class Generator9, class Generator10>
+class CartesianProductHolder10 {
+ public:
+CartesianProductHolder10(const Generator1& g1, const Generator2& g2,
+    const Generator3& g3, const Generator4& g4, const Generator5& g5,
+    const Generator6& g6, const Generator7& g7, const Generator8& g8,
+    const Generator9& g9, const Generator10& g10)
+      : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8),
+          g9_(g9), g10_(g10) {}
+  template <typename T1, typename T2, typename T3, typename T4, typename T5,
+      typename T6, typename T7, typename T8, typename T9, typename T10>
+  operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+      T9, T10> >() const {
+    return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8,
+        T9, T10> >(
+        new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+            T10>(
+        static_cast<ParamGenerator<T1> >(g1_),
+        static_cast<ParamGenerator<T2> >(g2_),
+        static_cast<ParamGenerator<T3> >(g3_),
+        static_cast<ParamGenerator<T4> >(g4_),
+        static_cast<ParamGenerator<T5> >(g5_),
+        static_cast<ParamGenerator<T6> >(g6_),
+        static_cast<ParamGenerator<T7> >(g7_),
+        static_cast<ParamGenerator<T8> >(g8_),
+        static_cast<ParamGenerator<T9> >(g9_),
+        static_cast<ParamGenerator<T10> >(g10_)));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder10& other);
+
+  const Generator1 g1_;
+  const Generator2 g2_;
+  const Generator3 g3_;
+  const Generator4 g4_;
+  const Generator5 g5_;
+  const Generator6 g6_;
+  const Generator7 g7_;
+  const Generator8 g8_;
+  const Generator9 g9_;
+  const Generator10 g10_;
+};  // class CartesianProductHolder10
+
+# endif  // GTEST_HAS_COMBINE
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  //  GTEST_HAS_PARAM_TEST
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump
new file mode 100644
index 0000000..009206f
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump
@@ -0,0 +1,301 @@
+$$ -*- mode: c++; -*-
+$var n = 50  $$ Maximum length of Values arguments we want to support.
+$var maxtuple = 10  $$ Maximum number of Combine arguments we want to support.
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+// This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+// Currently Google Test supports at most $n arguments in Values,
+// and at most $maxtuple arguments in Combine. Please contact
+// googletestframework at googlegroups.com if you need more.
+// Please note that the number of arguments to Combine is limited
+// by the maximum arity of the implementation of tr1::tuple which is
+// currently set at $maxtuple.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*.  Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <typename ForwardIterator>
+internal::ParamGenerator<
+  typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end);
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]);
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container);
+
+namespace internal {
+
+// Used in the Values() function to provide polymorphic capabilities.
+template <typename T1>
+class ValueArray1 {
+ public:
+  explicit ValueArray1(T1 v1) : v1_(v1) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray1& other);
+
+  const T1 v1_;
+};
+
+$range i 2..n
+$for i [[
+$range j 1..i
+
+template <$for j, [[typename T$j]]>
+class ValueArray$i {
+ public:
+  ValueArray$i($for j, [[T$j v$j]]) : $for j, [[v$(j)_(v$j)]] {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {
+    const T array[] = {$for j, [[static_cast<T>(v$(j)_)]]};
+    return ValuesIn(array);
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const ValueArray$i& other);
+
+$for j [[
+
+  const T$j v$(j)_;
+]]
+
+};
+
+]]
+
+# if GTEST_HAS_COMBINE
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Generates values from the Cartesian product of values produced
+// by the argument generators.
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+$range k 2..i
+
+template <$for j, [[typename T$j]]>
+class CartesianProductGenerator$i
+    : public ParamGeneratorInterface< ::std::tr1::tuple<$for j, [[T$j]]> > {
+ public:
+  typedef ::std::tr1::tuple<$for j, [[T$j]]> ParamType;
+
+  CartesianProductGenerator$i($for j, [[const ParamGenerator<T$j>& g$j]])
+      : $for j, [[g$(j)_(g$j)]] {}
+  virtual ~CartesianProductGenerator$i() {}
+
+  virtual ParamIteratorInterface<ParamType>* Begin() const {
+    return new Iterator(this, $for j, [[g$(j)_, g$(j)_.begin()]]);
+  }
+  virtual ParamIteratorInterface<ParamType>* End() const {
+    return new Iterator(this, $for j, [[g$(j)_, g$(j)_.end()]]);
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<ParamType> {
+   public:
+    Iterator(const ParamGeneratorInterface<ParamType>* base, $for j, [[
+
+      const ParamGenerator<T$j>& g$j,
+      const typename ParamGenerator<T$j>::iterator& current$(j)]])
+        : base_(base),
+$for j, [[
+
+          begin$(j)_(g$j.begin()), end$(j)_(g$j.end()), current$(j)_(current$j)
+]]    {
+      ComputeCurrentValue();
+    }
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    virtual void Advance() {
+      assert(!AtEnd());
+      ++current$(i)_;
+
+$for k [[
+      if (current$(i+2-k)_ == end$(i+2-k)_) {
+        current$(i+2-k)_ = begin$(i+2-k)_;
+        ++current$(i+2-k-1)_;
+      }
+
+]]
+      ComputeCurrentValue();
+    }
+    virtual ParamIteratorInterface<ParamType>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const ParamType* Current() const { return &current_value_; }
+    virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const Iterator* typed_other =
+          CheckedDowncastToActualType<const Iterator>(&other);
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      return (AtEnd() && typed_other->AtEnd()) ||
+         ($for j  && [[
+
+          current$(j)_ == typed_other->current$(j)_
+]]);
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : base_(other.base_), $for j, [[
+
+        begin$(j)_(other.begin$(j)_),
+        end$(j)_(other.end$(j)_),
+        current$(j)_(other.current$(j)_)
+]] {
+      ComputeCurrentValue();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = ParamType($for j, [[*current$(j)_]]);
+    }
+    bool AtEnd() const {
+      // We must report iterator past the end of the range when either of the
+      // component iterators has reached the end of its range.
+      return
+$for j  || [[
+
+          current$(j)_ == end$(j)_
+]];
+    }
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    // begin[i]_ and end[i]_ define the i-th range that Iterator traverses.
+    // current[i]_ is the actual traversing iterator.
+$for j [[
+
+    const typename ParamGenerator<T$j>::iterator begin$(j)_;
+    const typename ParamGenerator<T$j>::iterator end$(j)_;
+    typename ParamGenerator<T$j>::iterator current$(j)_;
+]]
+
+    ParamType current_value_;
+  };  // class CartesianProductGenerator$i::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductGenerator$i& other);
+
+
+$for j [[
+  const ParamGenerator<T$j> g$(j)_;
+
+]]
+};  // class CartesianProductGenerator$i
+
+
+]]
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Helper classes providing Combine() with polymorphic features. They allow
+// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is
+// convertible to U.
+//
+$range i 2..maxtuple
+$for i [[
+$range j 1..i
+
+template <$for j, [[class Generator$j]]>
+class CartesianProductHolder$i {
+ public:
+CartesianProductHolder$i($for j, [[const Generator$j& g$j]])
+      : $for j, [[g$(j)_(g$j)]] {}
+  template <$for j, [[typename T$j]]>
+  operator ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >() const {
+    return ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >(
+        new CartesianProductGenerator$i<$for j, [[T$j]]>(
+$for j,[[
+
+        static_cast<ParamGenerator<T$j> >(g$(j)_)
+]]));
+  }
+
+ private:
+  // No implementation - assignment is unsupported.
+  void operator=(const CartesianProductHolder$i& other);
+
+
+$for j [[
+  const Generator$j g$(j)_;
+
+]]
+};  // class CartesianProductHolder$i
+
+]]
+
+# endif  // GTEST_HAS_COMBINE
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  //  GTEST_HAS_PARAM_TEST
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util.h
new file mode 100644
index 0000000..d5e1028
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-param-util.h
@@ -0,0 +1,619 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: vladl at google.com (Vlad Losev)
+
+// Type and function utilities for implementing parameterized tests.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+// scripts/fuse_gtest.py depends on gtest's own header being #included
+// *unconditionally*.  Therefore these #includes cannot be moved
+// inside #if GTEST_HAS_PARAM_TEST.
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-linked_ptr.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-printers.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace testing {
+namespace internal {
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Outputs a message explaining invalid registration of different
+// fixture class for the same test case. This may happen when
+// TEST_P macro is used to define two tests with the same name
+// but in different namespaces.
+GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
+                                          const char* file, int line);
+
+template <typename> class ParamGeneratorInterface;
+template <typename> class ParamGenerator;
+
+// Interface for iterating over elements provided by an implementation
+// of ParamGeneratorInterface<T>.
+template <typename T>
+class ParamIteratorInterface {
+ public:
+  virtual ~ParamIteratorInterface() {}
+  // A pointer to the base generator instance.
+  // Used only for the purposes of iterator comparison
+  // to make sure that two iterators belong to the same generator.
+  virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
+  // Advances iterator to point to the next element
+  // provided by the generator. The caller is responsible
+  // for not calling Advance() on an iterator equal to
+  // BaseGenerator()->End().
+  virtual void Advance() = 0;
+  // Clones the iterator object. Used for implementing copy semantics
+  // of ParamIterator<T>.
+  virtual ParamIteratorInterface* Clone() const = 0;
+  // Dereferences the current iterator and provides (read-only) access
+  // to the pointed value. It is the caller's responsibility not to call
+  // Current() on an iterator equal to BaseGenerator()->End().
+  // Used for implementing ParamGenerator<T>::operator*().
+  virtual const T* Current() const = 0;
+  // Determines whether the given iterator and other point to the same
+  // element in the sequence generated by the generator.
+  // Used for implementing ParamGenerator<T>::operator==().
+  virtual bool Equals(const ParamIteratorInterface& other) const = 0;
+};
+
+// Class iterating over elements provided by an implementation of
+// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
+// and implements the const forward iterator concept.
+template <typename T>
+class ParamIterator {
+ public:
+  typedef T value_type;
+  typedef const T& reference;
+  typedef ptrdiff_t difference_type;
+
+  // ParamIterator assumes ownership of the impl_ pointer.
+  ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
+  ParamIterator& operator=(const ParamIterator& other) {
+    if (this != &other)
+      impl_.reset(other.impl_->Clone());
+    return *this;
+  }
+
+  const T& operator*() const { return *impl_->Current(); }
+  const T* operator->() const { return impl_->Current(); }
+  // Prefix version of operator++.
+  ParamIterator& operator++() {
+    impl_->Advance();
+    return *this;
+  }
+  // Postfix version of operator++.
+  ParamIterator operator++(int /*unused*/) {
+    ParamIteratorInterface<T>* clone = impl_->Clone();
+    impl_->Advance();
+    return ParamIterator(clone);
+  }
+  bool operator==(const ParamIterator& other) const {
+    return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
+  }
+  bool operator!=(const ParamIterator& other) const {
+    return !(*this == other);
+  }
+
+ private:
+  friend class ParamGenerator<T>;
+  explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
+  scoped_ptr<ParamIteratorInterface<T> > impl_;
+};
+
+// ParamGeneratorInterface<T> is the binary interface to access generators
+// defined in other translation units.
+template <typename T>
+class ParamGeneratorInterface {
+ public:
+  typedef T ParamType;
+
+  virtual ~ParamGeneratorInterface() {}
+
+  // Generator interface definition
+  virtual ParamIteratorInterface<T>* Begin() const = 0;
+  virtual ParamIteratorInterface<T>* End() const = 0;
+};
+
+// Wraps ParamGeneratorInterface<T> and provides general generator syntax
+// compatible with the STL Container concept.
+// This class implements copy initialization semantics and the contained
+// ParamGeneratorInterface<T> instance is shared among all copies
+// of the original object. This is possible because that instance is immutable.
+template<typename T>
+class ParamGenerator {
+ public:
+  typedef ParamIterator<T> iterator;
+
+  explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
+  ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
+
+  ParamGenerator& operator=(const ParamGenerator& other) {
+    impl_ = other.impl_;
+    return *this;
+  }
+
+  iterator begin() const { return iterator(impl_->Begin()); }
+  iterator end() const { return iterator(impl_->End()); }
+
+ private:
+  linked_ptr<const ParamGeneratorInterface<T> > impl_;
+};
+
+// Generates values from a range of two comparable values. Can be used to
+// generate sequences of user-defined types that implement operator+() and
+// operator<().
+// This class is used in the Range() function.
+template <typename T, typename IncrementT>
+class RangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+  RangeGenerator(T begin, T end, IncrementT step)
+      : begin_(begin), end_(end),
+        step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
+  virtual ~RangeGenerator() {}
+
+  virtual ParamIteratorInterface<T>* Begin() const {
+    return new Iterator(this, begin_, 0, step_);
+  }
+  virtual ParamIteratorInterface<T>* End() const {
+    return new Iterator(this, end_, end_index_, step_);
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<T> {
+   public:
+    Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
+             IncrementT step)
+        : base_(base), value_(value), index_(index), step_(step) {}
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+      return base_;
+    }
+    virtual void Advance() {
+      value_ = value_ + step_;
+      index_++;
+    }
+    virtual ParamIteratorInterface<T>* Clone() const {
+      return new Iterator(*this);
+    }
+    virtual const T* Current() const { return &value_; }
+    virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const int other_index =
+          CheckedDowncastToActualType<const Iterator>(&other)->index_;
+      return index_ == other_index;
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : ParamIteratorInterface<T>(),
+          base_(other.base_), value_(other.value_), index_(other.index_),
+          step_(other.step_) {}
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<T>* const base_;
+    T value_;
+    int index_;
+    const IncrementT step_;
+  };  // class RangeGenerator::Iterator
+
+  static int CalculateEndIndex(const T& begin,
+                               const T& end,
+                               const IncrementT& step) {
+    int end_index = 0;
+    for (T i = begin; i < end; i = i + step)
+      end_index++;
+    return end_index;
+  }
+
+  // No implementation - assignment is unsupported.
+  void operator=(const RangeGenerator& other);
+
+  const T begin_;
+  const T end_;
+  const IncrementT step_;
+  // The index for the end() iterator. All the elements in the generated
+  // sequence are indexed (0-based) to aid iterator comparison.
+  const int end_index_;
+};  // class RangeGenerator
+
+
+// Generates values from a pair of STL-style iterators. Used in the
+// ValuesIn() function. The elements are copied from the source range
+// since the source can be located on the stack, and the generator
+// is likely to persist beyond that stack frame.
+template <typename T>
+class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+  template <typename ForwardIterator>
+  ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
+      : container_(begin, end) {}
+  virtual ~ValuesInIteratorRangeGenerator() {}
+
+  virtual ParamIteratorInterface<T>* Begin() const {
+    return new Iterator(this, container_.begin());
+  }
+  virtual ParamIteratorInterface<T>* End() const {
+    return new Iterator(this, container_.end());
+  }
+
+ private:
+  typedef typename ::std::vector<T> ContainerType;
+
+  class Iterator : public ParamIteratorInterface<T> {
+   public:
+    Iterator(const ParamGeneratorInterface<T>* base,
+             typename ContainerType::const_iterator iterator)
+        : base_(base), iterator_(iterator) {}
+    virtual ~Iterator() {}
+
+    virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+      return base_;
+    }
+    virtual void Advance() {
+      ++iterator_;
+      value_.reset();
+    }
+    virtual ParamIteratorInterface<T>* Clone() const {
+      return new Iterator(*this);
+    }
+    // We need to use cached value referenced by iterator_ because *iterator_
+    // can return a temporary object (and of type other then T), so just
+    // having "return &*iterator_;" doesn't work.
+    // value_ is updated here and not in Advance() because Advance()
+    // can advance iterator_ beyond the end of the range, and we cannot
+    // detect that fact. The client code, on the other hand, is
+    // responsible for not calling Current() on an out-of-range iterator.
+    virtual const T* Current() const {
+      if (value_.get() == NULL)
+        value_.reset(new T(*iterator_));
+      return value_.get();
+    }
+    virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      return iterator_ ==
+          CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
+    }
+
+   private:
+    Iterator(const Iterator& other)
+          // The explicit constructor call suppresses a false warning
+          // emitted by gcc when supplied with the -Wextra option.
+        : ParamIteratorInterface<T>(),
+          base_(other.base_),
+          iterator_(other.iterator_) {}
+
+    const ParamGeneratorInterface<T>* const base_;
+    typename ContainerType::const_iterator iterator_;
+    // A cached value of *iterator_. We keep it here to allow access by
+    // pointer in the wrapping iterator's operator->().
+    // value_ needs to be mutable to be accessed in Current().
+    // Use of scoped_ptr helps manage cached value's lifetime,
+    // which is bound by the lifespan of the iterator itself.
+    mutable scoped_ptr<const T> value_;
+  };  // class ValuesInIteratorRangeGenerator::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const ValuesInIteratorRangeGenerator& other);
+
+  const ContainerType container_;
+};  // class ValuesInIteratorRangeGenerator
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Stores a parameter value and later creates tests parameterized with that
+// value.
+template <class TestClass>
+class ParameterizedTestFactory : public TestFactoryBase {
+ public:
+  typedef typename TestClass::ParamType ParamType;
+  explicit ParameterizedTestFactory(ParamType parameter) :
+      parameter_(parameter) {}
+  virtual Test* CreateTest() {
+    TestClass::SetParam(&parameter_);
+    return new TestClass();
+  }
+
+ private:
+  const ParamType parameter_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactoryBase is a base class for meta-factories that create
+// test factories for passing into MakeAndRegisterTestInfo function.
+template <class ParamType>
+class TestMetaFactoryBase {
+ public:
+  virtual ~TestMetaFactoryBase() {}
+
+  virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactory creates test factories for passing into
+// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
+// ownership of test factory pointer, same factory object cannot be passed
+// into that method twice. But ParameterizedTestCaseInfo is going to call
+// it for each Test/Parameter value combination. Thus it needs meta factory
+// creator class.
+template <class TestCase>
+class TestMetaFactory
+    : public TestMetaFactoryBase<typename TestCase::ParamType> {
+ public:
+  typedef typename TestCase::ParamType ParamType;
+
+  TestMetaFactory() {}
+
+  virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
+    return new ParameterizedTestFactory<TestCase>(parameter);
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfoBase is a generic interface
+// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
+// accumulates test information provided by TEST_P macro invocations
+// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations
+// and uses that information to register all resulting test instances
+// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
+// a collection of pointers to the ParameterizedTestCaseInfo objects
+// and calls RegisterTests() on each of them when asked.
+class ParameterizedTestCaseInfoBase {
+ public:
+  virtual ~ParameterizedTestCaseInfoBase() {}
+
+  // Base part of test case name for display purposes.
+  virtual const string& GetTestCaseName() const = 0;
+  // Test case id to verify identity.
+  virtual TypeId GetTestCaseTypeId() const = 0;
+  // UnitTest class invokes this method to register tests in this
+  // test case right before running them in RUN_ALL_TESTS macro.
+  // This method should not be called more then once on any single
+  // instance of a ParameterizedTestCaseInfoBase derived class.
+  virtual void RegisterTests() = 0;
+
+ protected:
+  ParameterizedTestCaseInfoBase() {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
+// macro invocations for a particular test case and generators
+// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
+// test case. It registers tests with all values generated by all
+// generators when asked.
+template <class TestCase>
+class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
+ public:
+  // ParamType and GeneratorCreationFunc are private types but are required
+  // for declarations of public methods AddTestPattern() and
+  // AddTestCaseInstantiation().
+  typedef typename TestCase::ParamType ParamType;
+  // A function that returns an instance of appropriate generator type.
+  typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
+
+  explicit ParameterizedTestCaseInfo(const char* name)
+      : test_case_name_(name) {}
+
+  // Test case base name for display purposes.
+  virtual const string& GetTestCaseName() const { return test_case_name_; }
+  // Test case id to verify identity.
+  virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
+  // TEST_P macro uses AddTestPattern() to record information
+  // about a single test in a LocalTestInfo structure.
+  // test_case_name is the base name of the test case (without invocation
+  // prefix). test_base_name is the name of an individual test without
+  // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
+  // test case base name and DoBar is test base name.
+  void AddTestPattern(const char* test_case_name,
+                      const char* test_base_name,
+                      TestMetaFactoryBase<ParamType>* meta_factory) {
+    tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
+                                                       test_base_name,
+                                                       meta_factory)));
+  }
+  // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
+  // about a generator.
+  int AddTestCaseInstantiation(const string& instantiation_name,
+                               GeneratorCreationFunc* func,
+                               const char* /* file */,
+                               int /* line */) {
+    instantiations_.push_back(::std::make_pair(instantiation_name, func));
+    return 0;  // Return value used only to run this method in namespace scope.
+  }
+  // UnitTest class invokes this method to register tests in this test case
+  // test cases right before running tests in RUN_ALL_TESTS macro.
+  // This method should not be called more then once on any single
+  // instance of a ParameterizedTestCaseInfoBase derived class.
+  // UnitTest has a guard to prevent from calling this method more then once.
+  virtual void RegisterTests() {
+    for (typename TestInfoContainer::iterator test_it = tests_.begin();
+         test_it != tests_.end(); ++test_it) {
+      linked_ptr<TestInfo> test_info = *test_it;
+      for (typename InstantiationContainer::iterator gen_it =
+               instantiations_.begin(); gen_it != instantiations_.end();
+               ++gen_it) {
+        const string& instantiation_name = gen_it->first;
+        ParamGenerator<ParamType> generator((*gen_it->second)());
+
+        string test_case_name;
+        if ( !instantiation_name.empty() )
+          test_case_name = instantiation_name + "/";
+        test_case_name += test_info->test_case_base_name;
+
+        int i = 0;
+        for (typename ParamGenerator<ParamType>::iterator param_it =
+                 generator.begin();
+             param_it != generator.end(); ++param_it, ++i) {
+          Message test_name_stream;
+          test_name_stream << test_info->test_base_name << "/" << i;
+          MakeAndRegisterTestInfo(
+              test_case_name.c_str(),
+              test_name_stream.GetString().c_str(),
+              NULL,  // No type parameter.
+              PrintToString(*param_it).c_str(),
+              GetTestCaseTypeId(),
+              TestCase::SetUpTestCase,
+              TestCase::TearDownTestCase,
+              test_info->test_meta_factory->CreateTestFactory(*param_it));
+        }  // for param_it
+      }  // for gen_it
+    }  // for test_it
+  }  // RegisterTests
+
+ private:
+  // LocalTestInfo structure keeps information about a single test registered
+  // with TEST_P macro.
+  struct TestInfo {
+    TestInfo(const char* a_test_case_base_name,
+             const char* a_test_base_name,
+             TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
+        test_case_base_name(a_test_case_base_name),
+        test_base_name(a_test_base_name),
+        test_meta_factory(a_test_meta_factory) {}
+
+    const string test_case_base_name;
+    const string test_base_name;
+    const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+  };
+  typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
+  // Keeps pairs of <Instantiation name, Sequence generator creation function>
+  // received from INSTANTIATE_TEST_CASE_P macros.
+  typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> >
+      InstantiationContainer;
+
+  const string test_case_name_;
+  TestInfoContainer tests_;
+  InstantiationContainer instantiations_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
+};  // class ParameterizedTestCaseInfo
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
+// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
+// macros use it to locate their corresponding ParameterizedTestCaseInfo
+// descriptors.
+class ParameterizedTestCaseRegistry {
+ public:
+  ParameterizedTestCaseRegistry() {}
+  ~ParameterizedTestCaseRegistry() {
+    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+         it != test_case_infos_.end(); ++it) {
+      delete *it;
+    }
+  }
+
+  // Looks up or creates and returns a structure containing information about
+  // tests and instantiations of a particular test case.
+  template <class TestCase>
+  ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
+      const char* test_case_name,
+      const char* file,
+      int line) {
+    ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
+    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+         it != test_case_infos_.end(); ++it) {
+      if ((*it)->GetTestCaseName() == test_case_name) {
+        if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
+          // Complain about incorrect usage of Google Test facilities
+          // and terminate the program since we cannot guaranty correct
+          // test case setup and tear-down in this case.
+          ReportInvalidTestCaseType(test_case_name,  file, line);
+          posix::Abort();
+        } else {
+          // At this point we are sure that the object we found is of the same
+          // type we are looking for, so we downcast it to that type
+          // without further checks.
+          typed_test_info = CheckedDowncastToActualType<
+              ParameterizedTestCaseInfo<TestCase> >(*it);
+        }
+        break;
+      }
+    }
+    if (typed_test_info == NULL) {
+      typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
+      test_case_infos_.push_back(typed_test_info);
+    }
+    return typed_test_info;
+  }
+  void RegisterTests() {
+    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
+         it != test_case_infos_.end(); ++it) {
+      (*it)->RegisterTests();
+    }
+  }
+
+ private:
+  typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
+
+  TestCaseInfoContainer test_case_infos_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
+};
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  //  GTEST_HAS_PARAM_TEST
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-port.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-port.h
new file mode 100644
index 0000000..dc4fe0c
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-port.h
@@ -0,0 +1,1947 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan)
+//
+// Low-level types and utilities for porting Google Test to various
+// platforms.  They are subject to change without notice.  DO NOT USE
+// THEM IN USER CODE.
+//
+// This file is fundamental to Google Test.  All other Google Test source
+// files are expected to #include this.  Therefore, it cannot #include
+// any other Google Test header.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+
+// The user can define the following macros in the build script to
+// control Google Test's behavior.  If the user doesn't define a macro
+// in this list, Google Test will define it.
+//
+//   GTEST_HAS_CLONE          - Define it to 1/0 to indicate that clone(2)
+//                              is/isn't available.
+//   GTEST_HAS_EXCEPTIONS     - Define it to 1/0 to indicate that exceptions
+//                              are enabled.
+//   GTEST_HAS_GLOBAL_STRING  - Define it to 1/0 to indicate that ::string
+//                              is/isn't available (some systems define
+//                              ::string, which is different to std::string).
+//   GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string
+//                              is/isn't available (some systems define
+//                              ::wstring, which is different to std::wstring).
+//   GTEST_HAS_POSIX_RE       - Define it to 1/0 to indicate that POSIX regular
+//                              expressions are/aren't available.
+//   GTEST_HAS_PTHREAD        - Define it to 1/0 to indicate that <pthread.h>
+//                              is/isn't available.
+//   GTEST_HAS_RTTI           - Define it to 1/0 to indicate that RTTI is/isn't
+//                              enabled.
+//   GTEST_HAS_STD_WSTRING    - Define it to 1/0 to indicate that
+//                              std::wstring does/doesn't work (Google Test can
+//                              be used where std::wstring is unavailable).
+//   GTEST_HAS_TR1_TUPLE      - Define it to 1/0 to indicate tr1::tuple
+//                              is/isn't available.
+//   GTEST_HAS_SEH            - Define it to 1/0 to indicate whether the
+//                              compiler supports Microsoft's "Structured
+//                              Exception Handling".
+//   GTEST_HAS_STREAM_REDIRECTION
+//                            - Define it to 1/0 to indicate whether the
+//                              platform supports I/O stream redirection using
+//                              dup() and dup2().
+//   GTEST_USE_OWN_TR1_TUPLE  - Define it to 1/0 to indicate whether Google
+//                              Test's own tr1 tuple implementation should be
+//                              used.  Unused when the user sets
+//                              GTEST_HAS_TR1_TUPLE to 0.
+//   GTEST_LANG_CXX11         - Define it to 1/0 to indicate that Google Test
+//                              is building in C++11/C++98 mode.
+//   GTEST_LINKED_AS_SHARED_LIBRARY
+//                            - Define to 1 when compiling tests that use
+//                              Google Test as a shared library (known as
+//                              DLL on Windows).
+//   GTEST_CREATE_SHARED_LIBRARY
+//                            - Define to 1 when compiling Google Test itself
+//                              as a shared library.
+
+// This header defines the following utilities:
+//
+// Macros indicating the current platform (defined to 1 if compiled on
+// the given platform; otherwise undefined):
+//   GTEST_OS_AIX      - IBM AIX
+//   GTEST_OS_CYGWIN   - Cygwin
+//   GTEST_OS_HPUX     - HP-UX
+//   GTEST_OS_LINUX    - Linux
+//     GTEST_OS_LINUX_ANDROID - Google Android
+//   GTEST_OS_MAC      - Mac OS X
+//     GTEST_OS_IOS    - iOS
+//       GTEST_OS_IOS_SIMULATOR - iOS simulator
+//   GTEST_OS_NACL     - Google Native Client (NaCl)
+//   GTEST_OS_OPENBSD  - OpenBSD
+//   GTEST_OS_QNX      - QNX
+//   GTEST_OS_SOLARIS  - Sun Solaris
+//   GTEST_OS_SYMBIAN  - Symbian
+//   GTEST_OS_WINDOWS  - Windows (Desktop, MinGW, or Mobile)
+//     GTEST_OS_WINDOWS_DESKTOP  - Windows Desktop
+//     GTEST_OS_WINDOWS_MINGW    - MinGW
+//     GTEST_OS_WINDOWS_MOBILE   - Windows Mobile
+//   GTEST_OS_ZOS      - z/OS
+//
+// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the
+// most stable support.  Since core members of the Google Test project
+// don't have access to other platforms, support for them may be less
+// stable.  If you notice any problems on your platform, please notify
+// googletestframework at googlegroups.com (patches for fixing them are
+// even more welcome!).
+//
+// Note that it is possible that none of the GTEST_OS_* macros are defined.
+//
+// Macros indicating available Google Test features (defined to 1 if
+// the corresponding feature is supported; otherwise undefined):
+//   GTEST_HAS_COMBINE      - the Combine() function (for value-parameterized
+//                            tests)
+//   GTEST_HAS_DEATH_TEST   - death tests
+//   GTEST_HAS_PARAM_TEST   - value-parameterized tests
+//   GTEST_HAS_TYPED_TEST   - typed tests
+//   GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+//   GTEST_USES_POSIX_RE    - enhanced POSIX regex is used. Do not confuse with
+//                            GTEST_HAS_POSIX_RE (see above) which users can
+//                            define themselves.
+//   GTEST_USES_SIMPLE_RE   - our own simple regex is used;
+//                            the above two are mutually exclusive.
+//   GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ().
+//
+// Macros for basic C++ coding:
+//   GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
+//   GTEST_ATTRIBUTE_UNUSED_  - declares that a class' instances or a
+//                              variable don't have to be used.
+//   GTEST_DISALLOW_ASSIGN_   - disables operator=.
+//   GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+//   GTEST_MUST_USE_RESULT_   - declares that a function's result must be used.
+//
+// Synchronization:
+//   Mutex, MutexLock, ThreadLocal, GetThreadCount()
+//                  - synchronization primitives.
+//   GTEST_IS_THREADSAFE - defined to 1 to indicate that the above
+//                         synchronization primitives have real implementations
+//                         and Google Test is thread-safe; or 0 otherwise.
+//
+// Template meta programming:
+//   is_pointer     - as in TR1; needed on Symbian and IBM XL C/C++ only.
+//   IteratorTraits - partial implementation of std::iterator_traits, which
+//                    is not available in libCstd when compiled with Sun C++.
+//
+// Smart pointers:
+//   scoped_ptr     - as in TR2.
+//
+// Regular expressions:
+//   RE             - a simple regular expression class using the POSIX
+//                    Extended Regular Expression syntax on UNIX-like
+//                    platforms, or a reduced regular exception syntax on
+//                    other platforms, including Windows.
+//
+// Logging:
+//   GTEST_LOG_()   - logs messages at the specified severity level.
+//   LogToStderr()  - directs all log messages to stderr.
+//   FlushInfoLog() - flushes informational log messages.
+//
+// Stdout and stderr capturing:
+//   CaptureStdout()     - starts capturing stdout.
+//   GetCapturedStdout() - stops capturing stdout and returns the captured
+//                         string.
+//   CaptureStderr()     - starts capturing stderr.
+//   GetCapturedStderr() - stops capturing stderr and returns the captured
+//                         string.
+//
+// Integer types:
+//   TypeWithSize   - maps an integer to a int type.
+//   Int32, UInt32, Int64, UInt64, TimeInMillis
+//                  - integers of known sizes.
+//   BiggestInt     - the biggest signed integer type.
+//
+// Command-line utilities:
+//   GTEST_FLAG()       - references a flag.
+//   GTEST_DECLARE_*()  - declares a flag.
+//   GTEST_DEFINE_*()   - defines a flag.
+//   GetInjectableArgvs() - returns the command line as a vector of strings.
+//
+// Environment variable utilities:
+//   GetEnv()             - gets the value of an environment variable.
+//   BoolFromGTestEnv()   - parses a bool environment variable.
+//   Int32FromGTestEnv()  - parses an Int32 environment variable.
+//   StringFromGTestEnv() - parses a string environment variable.
+
+#include <ctype.h>   // for isspace, etc
+#include <stddef.h>  // for ptrdiff_t
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif  // !_WIN32_WCE
+
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+# include <TargetConditionals.h>
+#endif
+
+#include <iostream>  // NOLINT
+#include <sstream>  // NOLINT
+#include <string>  // NOLINT
+
+#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+#define GTEST_FLAG_PREFIX_ "gtest_"
+#define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+#define GTEST_NAME_ "Google Test"
+#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/"
+
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+# define GTEST_GCC_VER_ \
+    (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif  // __GNUC__
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+#elif defined __SYMBIAN32__
+# define GTEST_OS_SYMBIAN 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+#  define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(__MINGW__) || defined(__MINGW32__)
+#  define GTEST_OS_WINDOWS_MINGW 1
+# else
+#  define GTEST_OS_WINDOWS_DESKTOP 1
+# endif  // _WIN32_WCE
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+# if TARGET_OS_IPHONE
+#  define GTEST_OS_IOS 1
+#  if TARGET_IPHONE_SIMULATOR
+#   define GTEST_OS_IOS_SIMULATOR 1
+#  endif
+# endif
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# if defined __ANDROID__
+#  define GTEST_OS_LINUX_ANDROID 1
+# endif
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#elif defined __OpenBSD__
+# define GTEST_OS_OPENBSD 1
+#elif defined __QNX__
+# define GTEST_OS_QNX 1
+#endif  // __CYGWIN__
+
+#ifndef GTEST_LANG_CXX11
+// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when
+// -std={c,gnu}++{0x,11} is passed.  The C++11 standard specifies a
+// value for __cplusplus, and recent versions of clang, gcc, and
+// probably other compilers set that too in C++11 mode.
+# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L
+// Compiling in at least C++11 mode.
+#  define GTEST_LANG_CXX11 1
+# else
+#  define GTEST_LANG_CXX11 0
+# endif
+#endif
+
+// Brings in definitions for functions used in the testing::internal::posix
+// namespace (read, write, close, chdir, isatty, stat). We do not currently
+// use them on Windows Mobile.
+#if !GTEST_OS_WINDOWS
+// This assumes that non-Windows OSes provide unistd.h. For OSes where this
+// is not the case, we need to include headers that provide the functions
+// mentioned above.
+# include <unistd.h>
+# include <strings.h>
+#elif !GTEST_OS_WINDOWS_MOBILE
+# include <direct.h>
+# include <io.h>
+#endif
+
+#if GTEST_OS_LINUX_ANDROID
+// Used to define __ANDROID_API__ matching the target NDK API level.
+#  include <android/api-level.h>  // NOLINT
+#endif
+
+// Defines this to true iff Google Test can use POSIX regular expressions.
+#ifndef GTEST_HAS_POSIX_RE
+# if GTEST_OS_LINUX_ANDROID
+// On Android, <regex.h> is only available starting with Gingerbread.
+#  define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9)
+# else
+#  define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS)
+# endif
+#endif
+
+#if GTEST_HAS_POSIX_RE
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise.  We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+# include <regex.h>  // NOLINT
+
+# define GTEST_USES_POSIX_RE 1
+
+#elif GTEST_OS_WINDOWS
+
+// <regex.h> is not available on Windows.  Use our own simple regex
+// implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#else
+
+// <regex.h> may not be available on this platform.  Use our own
+// simple regex implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#endif  // GTEST_HAS_POSIX_RE
+
+#ifndef GTEST_HAS_EXCEPTIONS
+// The user didn't tell us whether exceptions are enabled, so we need
+// to figure it out.
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
+// macro to enable exceptions, so we'll do the same.
+// Assumes that exceptions are enabled by default.
+#  ifndef _HAS_EXCEPTIONS
+#   define _HAS_EXCEPTIONS 1
+#  endif  // _HAS_EXCEPTIONS
+#  define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__GNUC__) && __EXCEPTIONS
+// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__SUNPRO_CC)
+// Sun Pro CC supports exceptions.  However, there is no compile-time way of
+// detecting whether they are enabled or not.  Therefore, we assume that
+// they are enabled unless the user tells us otherwise.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__IBMCPP__) && __EXCEPTIONS
+// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__HP_aCC)
+// Exception handling is in effect by default in HP aCC compiler. It has to
+// be turned of by +noeh compiler option if desired.
+#  define GTEST_HAS_EXCEPTIONS 1
+# else
+// For other compilers, we assume exceptions are disabled to be
+// conservative.
+#  define GTEST_HAS_EXCEPTIONS 0
+# endif  // defined(_MSC_VER) || defined(__BORLANDC__)
+#endif  // GTEST_HAS_EXCEPTIONS
+
+#if !defined(GTEST_HAS_STD_STRING)
+// Even though we don't use this macro any longer, we keep it in case
+// some clients still depend on it.
+# define GTEST_HAS_STD_STRING 1
+#elif !GTEST_HAS_STD_STRING
+// The user told us that ::std::string isn't available.
+# error "Google Test cannot be used where ::std::string isn't available."
+#endif  // !defined(GTEST_HAS_STD_STRING)
+
+#ifndef GTEST_HAS_GLOBAL_STRING
+// The user didn't tell us whether ::string is available, so we need
+// to figure it out.
+
+# define GTEST_HAS_GLOBAL_STRING 0
+
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+#ifndef GTEST_HAS_STD_WSTRING
+// The user didn't tell us whether ::std::wstring is available, so we need
+// to figure it out.
+// TODO(wan at google.com): uses autoconf to detect whether ::std::wstring
+//   is available.
+
+// Cygwin 1.7 and below doesn't support ::std::wstring.
+// Solaris' libc++ doesn't support it either.  Android has
+// no support for it at least as recent as Froyo (2.2).
+# define GTEST_HAS_STD_WSTRING \
+    (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS))
+
+#endif  // GTEST_HAS_STD_WSTRING
+
+#ifndef GTEST_HAS_GLOBAL_WSTRING
+// The user didn't tell us whether ::wstring is available, so we need
+// to figure it out.
+# define GTEST_HAS_GLOBAL_WSTRING \
+    (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING)
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+# ifdef _MSC_VER
+
+#  ifdef _CPPRTTI  // MSVC defines this macro iff RTTI is enabled.
+#   define GTEST_HAS_RTTI 1
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled.
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302)
+
+#  ifdef __GXX_RTTI
+// When building against STLport with the Android NDK and with
+// -frtti -fno-exceptions, the build fails at link time with undefined
+// references to __cxa_bad_typeid. Note sure if STL or toolchain bug,
+// so disable RTTI when detected.
+#   if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \
+       !defined(__EXCEPTIONS)
+#    define GTEST_HAS_RTTI 0
+#   else
+#    define GTEST_HAS_RTTI 1
+#   endif  // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif  // __GXX_RTTI
+
+// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends
+// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the
+// first version with C++ support.
+# elif defined(__clang__)
+
+#  define GTEST_HAS_RTTI __has_feature(cxx_rtti)
+
+// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if
+// both the typeid and dynamic_cast features are present.
+# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900)
+
+#  ifdef __RTTI_ALL__
+#   define GTEST_HAS_RTTI 1
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif
+
+# else
+
+// For all other compilers, we assume RTTI is enabled.
+#  define GTEST_HAS_RTTI 1
+
+# endif  // _MSC_VER
+
+#endif  // GTEST_HAS_RTTI
+
+// It's this header's responsibility to #include <typeinfo> when RTTI
+// is enabled.
+#if GTEST_HAS_RTTI
+# include <typeinfo>
+#endif
+
+// Determines whether Google Test can use the pthreads library.
+#ifndef GTEST_HAS_PTHREAD
+// The user didn't tell us explicitly, so we assume pthreads support is
+// available on Linux and Mac.
+//
+// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
+// to your compiler flags.
+# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \
+    || GTEST_OS_QNX)
+#endif  // GTEST_HAS_PTHREAD
+
+#if GTEST_HAS_PTHREAD
+// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is
+// true.
+# include <pthread.h>  // NOLINT
+
+// For timespec and nanosleep, used below.
+# include <time.h>  // NOLINT
+#endif
+
+// Determines whether Google Test can use tr1/tuple.  You can define
+// this macro to 0 to prevent Google Test from using tuple (any
+// feature depending on tuple with be disabled in this mode).
+#ifndef GTEST_HAS_TR1_TUPLE
+# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR)
+// STLport, provided with the Android NDK, has neither <tr1/tuple> or <tuple>.
+#  define GTEST_HAS_TR1_TUPLE 0
+# else
+// The user didn't tell us not to do it, so we assume it's OK.
+#  define GTEST_HAS_TR1_TUPLE 1
+# endif
+#endif  // GTEST_HAS_TR1_TUPLE
+
+// Determines whether Google Test's own tr1 tuple implementation
+// should be used.
+#ifndef GTEST_USE_OWN_TR1_TUPLE
+// The user didn't tell us, so we need to figure it out.
+
+// We use our own TR1 tuple if we aren't sure the user has an
+// implementation of it already.  At this time, libstdc++ 4.0.0+ and
+// MSVC 2010 are the only mainstream standard libraries that come
+// with a TR1 tuple implementation.  NVIDIA's CUDA NVCC compiler
+// pretends to be GCC by defining __GNUC__ and friends, but cannot
+// compile GCC's tuple implementation.  MSVC 2008 (9.0) provides TR1
+// tuple in a 323 MB Feature Pack download, which we cannot assume the
+// user has.  QNX's QCC compiler is a modified GCC but it doesn't
+// support TR1 tuple.  libc++ only provides std::tuple, in C++11 mode,
+// and it can be used with some compilers that define __GNUC__.
+# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \
+      && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600
+#  define GTEST_ENV_HAS_TR1_TUPLE_ 1
+# endif
+
+// C++11 specifies that <tuple> provides std::tuple. Use that if gtest is used
+// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6
+// can build with clang but need to use gcc4.2's libstdc++).
+# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325)
+#  define GTEST_ENV_HAS_STD_TUPLE_ 1
+# endif
+
+# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_
+#  define GTEST_USE_OWN_TR1_TUPLE 0
+# else
+#  define GTEST_USE_OWN_TR1_TUPLE 1
+# endif
+
+#endif  // GTEST_USE_OWN_TR1_TUPLE
+
+// To avoid conditional compilation everywhere, we make it
+// gtest-port.h's responsibility to #include the header implementing
+// tr1/tuple.
+#if GTEST_HAS_TR1_TUPLE
+
+# if GTEST_USE_OWN_TR1_TUPLE
+#  include "gtest/internal/gtest-tuple.h"
+# elif GTEST_ENV_HAS_STD_TUPLE_
+#  include <tuple>
+// C++11 puts its tuple into the ::std namespace rather than
+// ::std::tr1.  gtest expects tuple to live in ::std::tr1, so put it there.
+// This causes undefined behavior, but supported compilers react in
+// the way we intend.
+namespace std {
+namespace tr1 {
+using ::std::get;
+using ::std::make_tuple;
+using ::std::tuple;
+using ::std::tuple_element;
+using ::std::tuple_size;
+}
+}
+
+# elif GTEST_OS_SYMBIAN
+
+// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to
+// use STLport's tuple implementation, which unfortunately doesn't
+// work as the copy of STLport distributed with Symbian is incomplete.
+// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to
+// use its own tuple implementation.
+#  ifdef BOOST_HAS_TR1_TUPLE
+#   undef BOOST_HAS_TR1_TUPLE
+#  endif  // BOOST_HAS_TR1_TUPLE
+
+// This prevents <boost/tr1/detail/config.hpp>, which defines
+// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>.
+#  define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED
+#  include <tuple>
+
+# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000)
+// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header.  This does
+// not conform to the TR1 spec, which requires the header to be <tuple>.
+
+#  if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+// Until version 4.3.2, gcc has a bug that causes <tr1/functional>,
+// which is #included by <tr1/tuple>, to not compile when RTTI is
+// disabled.  _TR1_FUNCTIONAL is the header guard for
+// <tr1/functional>.  Hence the following #define is a hack to prevent
+// <tr1/functional> from being included.
+#   define _TR1_FUNCTIONAL 1
+#   include <tr1/tuple>
+#   undef _TR1_FUNCTIONAL  // Allows the user to #include
+                        // <tr1/functional> if he chooses to.
+#  else
+#   include <tr1/tuple>  // NOLINT
+#  endif  // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302
+
+# else
+// If the compiler is not GCC 4.0+, we assume the user is using a
+// spec-conforming TR1 implementation.
+#  include <tuple>  // NOLINT
+# endif  // GTEST_USE_OWN_TR1_TUPLE
+
+#endif  // GTEST_HAS_TR1_TUPLE
+
+// Determines whether clone(2) is supported.
+// Usually it will only be available on Linux, excluding
+// Linux on the Itanium architecture.
+// Also see http://linux.die.net/man/2/clone.
+#ifndef GTEST_HAS_CLONE
+// The user didn't tell us, so we need to figure it out.
+
+# if GTEST_OS_LINUX && !defined(__ia64__)
+#  if GTEST_OS_LINUX_ANDROID
+// On Android, clone() is only available on ARM starting with Gingerbread.
+#    if defined(__arm__) && __ANDROID_API__ >= 9
+#     define GTEST_HAS_CLONE 1
+#    else
+#     define GTEST_HAS_CLONE 0
+#    endif
+#  else
+#   define GTEST_HAS_CLONE 1
+#  endif
+# else
+#  define GTEST_HAS_CLONE 0
+# endif  // GTEST_OS_LINUX && !defined(__ia64__)
+
+#endif  // GTEST_HAS_CLONE
+
+// Determines whether to support stream redirection. This is used to test
+// output correctness and to implement death tests.
+#ifndef GTEST_HAS_STREAM_REDIRECTION
+// By default, we assume that stream redirection is supported on all
+// platforms except known mobile ones.
+# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN
+#  define GTEST_HAS_STREAM_REDIRECTION 0
+# else
+#  define GTEST_HAS_STREAM_REDIRECTION 1
+# endif  // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN
+#endif  // GTEST_HAS_STREAM_REDIRECTION
+
+// Determines whether to support death tests.
+// Google Test does not support death tests for VC 7.1 and earlier as
+// abort() in a VC 7.1 application compiled as GUI in debug config
+// pops up a dialog window that cannot be suppressed programmatically.
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+     (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \
+     (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+     GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \
+     GTEST_OS_OPENBSD || GTEST_OS_QNX)
+# define GTEST_HAS_DEATH_TEST 1
+# include <vector>  // NOLINT
+#endif
+
+// We don't support MSVC 7.1 with exceptions disabled now.  Therefore
+// all the compilers we care about are adequate for supporting
+// value-parameterized tests.
+#define GTEST_HAS_PARAM_TEST 1
+
+// Determines whether to support type-driven tests.
+
+// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
+// Sun Pro CC, IBM Visual Age, and HP aCC support.
+#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \
+    defined(__IBMCPP__) || defined(__HP_aCC)
+# define GTEST_HAS_TYPED_TEST 1
+# define GTEST_HAS_TYPED_TEST_P 1
+#endif
+
+// Determines whether to support Combine(). This only makes sense when
+// value-parameterized tests are enabled.  The implementation doesn't
+// work on Sun Studio since it doesn't understand templated conversion
+// operators.
+#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC)
+# define GTEST_HAS_COMBINE 1
+#endif
+
+// Determines whether the system compiler uses UTF-16 for encoding wide strings.
+#define GTEST_WIDE_STRING_USES_UTF16_ \
+    (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX)
+
+// Determines whether test results can be streamed to a socket.
+#if GTEST_OS_LINUX
+# define GTEST_CAN_STREAM_RESULTS_ 1
+#endif
+
+// Defines some utility macros.
+
+// The GNU compiler emits a warning if nested "if" statements are followed by
+// an "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding.  This leads to problems with code like:
+//
+//   if (gate)
+//     ASSERT_*(condition) << "Some message";
+//
+// The "switch (0) case 0:" idiom is used to suppress this.
+#ifdef __INTEL_COMPILER
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_
+#else
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default:  // NOLINT
+#endif
+
+// Use this annotation at the end of a struct/class definition to
+// prevent the compiler from optimizing away instances that are never
+// used.  This is useful when all interesting logic happens inside the
+// c'tor and / or d'tor.  Example:
+//
+//   struct Foo {
+//     Foo() { ... }
+//   } GTEST_ATTRIBUTE_UNUSED_;
+//
+// Also use it after a variable or parameter declaration to tell the
+// compiler the variable/parameter does not have to be used.
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#else
+# define GTEST_ATTRIBUTE_UNUSED_
+#endif
+
+// A macro to disallow operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_ASSIGN_(type)\
+  void operator=(type const &)
+
+// A macro to disallow copy constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\
+  type(type const &);\
+  GTEST_DISALLOW_ASSIGN_(type)
+
+// Tell the compiler to warn about unused return values for functions declared
+// with this macro.  The macro should be used on function declarations
+// following the argument list:
+//
+//   Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
+#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC)
+# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
+#else
+# define GTEST_MUST_USE_RESULT_
+#endif  // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC
+
+// Determine whether the compiler supports Microsoft's Structured Exception
+// Handling.  This is supported by several Windows compilers but generally
+// does not exist on any other system.
+#ifndef GTEST_HAS_SEH
+// The user didn't tell us, so we need to figure it out.
+
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// These two compilers are known to support SEH.
+#  define GTEST_HAS_SEH 1
+# else
+// Assume no SEH.
+#  define GTEST_HAS_SEH 0
+# endif
+
+#endif  // GTEST_HAS_SEH
+
+#ifdef _MSC_VER
+
+# if GTEST_LINKED_AS_SHARED_LIBRARY
+#  define GTEST_API_ __declspec(dllimport)
+# elif GTEST_CREATE_SHARED_LIBRARY
+#  define GTEST_API_ __declspec(dllexport)
+# endif
+
+#endif  // _MSC_VER
+
+#ifndef GTEST_API_
+# define GTEST_API_
+#endif
+
+#ifdef __GNUC__
+// Ask the compiler to never inline a given function.
+# define GTEST_NO_INLINE_ __attribute__((noinline))
+#else
+# define GTEST_NO_INLINE_
+#endif
+
+// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
+#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
+# define GTEST_HAS_CXXABI_H_ 1
+#else
+# define GTEST_HAS_CXXABI_H_ 0
+#endif
+
+namespace testing {
+
+class Message;
+
+namespace internal {
+
+// A secret type that Google Test users don't know about.  It has no
+// definition on purpose.  Therefore it's impossible to create a
+// Secret object, which is what we want.
+class Secret;
+
+// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+//   GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+//                         content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+//   GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define GTEST_COMPILE_ASSERT_(expr, msg) \
+  typedef ::testing::internal::CompileAssert<(static_cast<bool>(expr))> \
+      msg[static_cast<bool>(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_
+
+// Implementation details of GTEST_COMPILE_ASSERT_:
+//
+// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1
+//   elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+//    #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+//   does not work, as gcc supports variable-length arrays whose sizes
+//   are determined at run-time (this is gcc's extension and not part
+//   of the C++ standard).  As a result, gcc fails to reject the
+//   following code with the simple definition:
+//
+//     int foo;
+//     GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
+//                                      // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+//   expr is a compile-time constant.  (Template arguments must be
+//   determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+//   to work around a bug in gcc 3.4.4 and 4.0.1.  If we had written
+//
+//     CompileAssert<bool(expr)>
+//
+//   instead, these compilers will refuse to compile
+//
+//     GTEST_COMPILE_ASSERT_(5 > 0, some_message);
+//
+//   (They seem to think the ">" in "5 > 0" marks the end of the
+//   template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+//     ((expr) ? 1 : -1).
+//
+//   This is to avoid running into a bug in MS VC 7.1, which
+//   causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h.
+//
+// This template is declared, but intentionally undefined.
+template <typename T1, typename T2>
+struct StaticAssertTypeEqHelper;
+
+template <typename T>
+struct StaticAssertTypeEqHelper<T, T> {};
+
+#if GTEST_HAS_GLOBAL_STRING
+typedef ::string string;
+#else
+typedef ::std::string string;
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+typedef ::wstring wstring;
+#elif GTEST_HAS_STD_WSTRING
+typedef ::std::wstring wstring;
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+// A helper for suppressing warnings on constant condition.  It just
+// returns 'condition'.
+GTEST_API_ bool IsTrue(bool condition);
+
+// Defines scoped_ptr.
+
+// This implementation of scoped_ptr is PARTIAL - it only contains
+// enough stuff to satisfy Google Test's need.
+template <typename T>
+class scoped_ptr {
+ public:
+  typedef T element_type;
+
+  explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
+  ~scoped_ptr() { reset(); }
+
+  T& operator*() const { return *ptr_; }
+  T* operator->() const { return ptr_; }
+  T* get() const { return ptr_; }
+
+  T* release() {
+    T* const ptr = ptr_;
+    ptr_ = NULL;
+    return ptr;
+  }
+
+  void reset(T* p = NULL) {
+    if (p != ptr_) {
+      if (IsTrue(sizeof(T) > 0)) {  // Makes sure T is a complete type.
+        delete ptr_;
+      }
+      ptr_ = p;
+    }
+  }
+
+ private:
+  T* ptr_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr);
+};
+
+// Defines RE.
+
+// A simple C++ wrapper for <regex.h>.  It uses the POSIX Extended
+// Regular Expression syntax.
+class GTEST_API_ RE {
+ public:
+  // A copy constructor is required by the Standard to initialize object
+  // references from r-values.
+  RE(const RE& other) { Init(other.pattern()); }
+
+  // Constructs an RE from a string.
+  RE(const ::std::string& regex) { Init(regex.c_str()); }  // NOLINT
+
+#if GTEST_HAS_GLOBAL_STRING
+
+  RE(const ::string& regex) { Init(regex.c_str()); }  // NOLINT
+
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+  RE(const char* regex) { Init(regex); }  // NOLINT
+  ~RE();
+
+  // Returns the string representation of the regex.
+  const char* pattern() const { return pattern_; }
+
+  // FullMatch(str, re) returns true iff regular expression re matches
+  // the entire str.
+  // PartialMatch(str, re) returns true iff regular expression re
+  // matches a substring of str (including str itself).
+  //
+  // TODO(wan at google.com): make FullMatch() and PartialMatch() work
+  // when str contains NUL characters.
+  static bool FullMatch(const ::std::string& str, const RE& re) {
+    return FullMatch(str.c_str(), re);
+  }
+  static bool PartialMatch(const ::std::string& str, const RE& re) {
+    return PartialMatch(str.c_str(), re);
+  }
+
+#if GTEST_HAS_GLOBAL_STRING
+
+  static bool FullMatch(const ::string& str, const RE& re) {
+    return FullMatch(str.c_str(), re);
+  }
+  static bool PartialMatch(const ::string& str, const RE& re) {
+    return PartialMatch(str.c_str(), re);
+  }
+
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+  static bool FullMatch(const char* str, const RE& re);
+  static bool PartialMatch(const char* str, const RE& re);
+
+ private:
+  void Init(const char* regex);
+
+  // We use a const char* instead of an std::string, as Google Test used to be
+  // used where std::string is not available.  TODO(wan at google.com): change to
+  // std::string.
+  const char* pattern_;
+  bool is_valid_;
+
+#if GTEST_USES_POSIX_RE
+
+  regex_t full_regex_;     // For FullMatch().
+  regex_t partial_regex_;  // For PartialMatch().
+
+#else  // GTEST_USES_SIMPLE_RE
+
+  const char* full_pattern_;  // For FullMatch();
+
+#endif
+
+  GTEST_DISALLOW_ASSIGN_(RE);
+};
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file,
+                                                               int line);
+
+// Defines logging utilities:
+//   GTEST_LOG_(severity) - logs messages at the specified severity level. The
+//                          message itself is streamed into the macro.
+//   LogToStderr()  - directs all log messages to stderr.
+//   FlushInfoLog() - flushes informational log messages.
+
+enum GTestLogSeverity {
+  GTEST_INFO,
+  GTEST_WARNING,
+  GTEST_ERROR,
+  GTEST_FATAL
+};
+
+// Formats log entry severity, provides a stream object for streaming the
+// log message, and terminates the message with a newline when going out of
+// scope.
+class GTEST_API_ GTestLog {
+ public:
+  GTestLog(GTestLogSeverity severity, const char* file, int line);
+
+  // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+  ~GTestLog();
+
+  ::std::ostream& GetStream() { return ::std::cerr; }
+
+ private:
+  const GTestLogSeverity severity_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
+};
+
+#define GTEST_LOG_(severity) \
+    ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
+                                  __FILE__, __LINE__).GetStream()
+
+inline void LogToStderr() {}
+inline void FlushInfoLog() { fflush(NULL); }
+
+// INTERNAL IMPLEMENTATION - DO NOT USE.
+//
+// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
+// is not satisfied.
+//  Synopsys:
+//    GTEST_CHECK_(boolean_condition);
+//     or
+//    GTEST_CHECK_(boolean_condition) << "Additional message";
+//
+//    This checks the condition and if the condition is not satisfied
+//    it prints message about the condition violation, including the
+//    condition itself, plus additional message streamed into it, if any,
+//    and then it aborts the program. It aborts the program irrespective of
+//    whether it is built in the debug mode or not.
+#define GTEST_CHECK_(condition) \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+    if (::testing::internal::IsTrue(condition)) \
+      ; \
+    else \
+      GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success).  Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+  if (const int gtest_error = (posix_call)) \
+    GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+                      << gtest_error
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Use ImplicitCast_ as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*).  When you use ImplicitCast_, the compiler checks that
+// the cast is safe.  Such explicit ImplicitCast_s are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using ImplicitCast_ is the same as for static_cast:
+//
+//   ImplicitCast_<ToType>(expr)
+//
+// ImplicitCast_ would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., implicit_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To>
+inline To ImplicitCast_(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts
+// always succeed.  When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo?  It
+// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
+// when you downcast, you should use this macro.  In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not).  In normal mode, we do the efficient static_cast<>
+// instead.  Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+//    This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., down_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To, typename From>  // use like this: DownCast_<T*>(foo);
+inline To DownCast_(From* f) {  // so we only accept pointers
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  if (false) {
+    const To to = NULL;
+    ::testing::internal::ImplicitCast_<From*>(to);
+  }
+
+#if GTEST_HAS_RTTI
+  // RTTI: debug mode only!
+  GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL);
+#endif
+  return static_cast<To>(f);
+}
+
+// Downcasts the pointer of type Base to Derived.
+// Derived must be a subclass of Base. The parameter MUST
+// point to a class of type Derived, not any subclass of it.
+// When RTTI is available, the function performs a runtime
+// check to enforce this.
+template <class Derived, class Base>
+Derived* CheckedDowncastToActualType(Base* base) {
+#if GTEST_HAS_RTTI
+  GTEST_CHECK_(typeid(*base) == typeid(Derived));
+  return dynamic_cast<Derived*>(base);  // NOLINT
+#else
+  return static_cast<Derived*>(base);  // Poor man's downcast.
+#endif
+}
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Defines the stderr capturer:
+//   CaptureStdout     - starts capturing stdout.
+//   GetCapturedStdout - stops capturing stdout and returns the captured string.
+//   CaptureStderr     - starts capturing stderr.
+//   GetCapturedStderr - stops capturing stderr and returns the captured string.
+//
+GTEST_API_ void CaptureStdout();
+GTEST_API_ std::string GetCapturedStdout();
+GTEST_API_ void CaptureStderr();
+GTEST_API_ std::string GetCapturedStderr();
+
+#endif  // GTEST_HAS_STREAM_REDIRECTION
+
+
+#if GTEST_HAS_DEATH_TEST
+
+const ::std::vector<testing::internal::string>& GetInjectableArgvs();
+void SetInjectableArgvs(const ::std::vector<testing::internal::string>*
+                             new_argvs);
+
+// A copy of all command line arguments.  Set by InitGoogleTest().
+extern ::std::vector<testing::internal::string> g_argvs;
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+// Defines synchronization primitives.
+
+#if GTEST_HAS_PTHREAD
+
+// Sleeps for (roughly) n milli-seconds.  This function is only for
+// testing Google Test's own constructs.  Don't use it in user tests,
+// either directly or indirectly.
+inline void SleepMilliseconds(int n) {
+  const timespec time = {
+    0,                  // 0 seconds.
+    n * 1000L * 1000L,  // And n ms.
+  };
+  nanosleep(&time, NULL);
+}
+
+// Allows a controller thread to pause execution of newly created
+// threads until notified.  Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class Notification {
+ public:
+  Notification() : notified_(false) {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+  }
+  ~Notification() {
+    pthread_mutex_destroy(&mutex_);
+  }
+
+  // Notifies all threads created with this notification to start. Must
+  // be called from the controller thread.
+  void Notify() {
+    pthread_mutex_lock(&mutex_);
+    notified_ = true;
+    pthread_mutex_unlock(&mutex_);
+  }
+
+  // Blocks until the controller thread notifies. Must be called from a test
+  // thread.
+  void WaitForNotification() {
+    for (;;) {
+      pthread_mutex_lock(&mutex_);
+      const bool notified = notified_;
+      pthread_mutex_unlock(&mutex_);
+      if (notified)
+        break;
+      SleepMilliseconds(10);
+    }
+  }
+
+ private:
+  pthread_mutex_t mutex_;
+  bool notified_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+
+// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
+// Consequently, it cannot select a correct instantiation of ThreadWithParam
+// in order to call its Run(). Introducing ThreadWithParamBase as a
+// non-templated base class for ThreadWithParam allows us to bypass this
+// problem.
+class ThreadWithParamBase {
+ public:
+  virtual ~ThreadWithParamBase() {}
+  virtual void Run() = 0;
+};
+
+// pthread_create() accepts a pointer to a function type with the C linkage.
+// According to the Standard (7.5/1), function types with different linkages
+// are different even if they are otherwise identical.  Some compilers (for
+// example, SunStudio) treat them as different types.  Since class methods
+// cannot be defined with C-linkage we need to define a free C-function to
+// pass into pthread_create().
+extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
+  static_cast<ThreadWithParamBase*>(thread)->Run();
+  return NULL;
+}
+
+// Helper class for testing Google Test's multi-threading constructs.
+// To use it, write:
+//
+//   void ThreadFunc(int param) { /* Do things with param */ }
+//   Notification thread_can_start;
+//   ...
+//   // The thread_can_start parameter is optional; you can supply NULL.
+//   ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start);
+//   thread_can_start.Notify();
+//
+// These classes are only for testing Google Test's own constructs. Do
+// not use them in user tests, either directly or indirectly.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+  typedef void (*UserThreadFunc)(T);
+
+  ThreadWithParam(
+      UserThreadFunc func, T param, Notification* thread_can_start)
+      : func_(func),
+        param_(param),
+        thread_can_start_(thread_can_start),
+        finished_(false) {
+    ThreadWithParamBase* const base = this;
+    // The thread can be created only after all fields except thread_
+    // have been initialized.
+    GTEST_CHECK_POSIX_SUCCESS_(
+        pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base));
+  }
+  ~ThreadWithParam() { Join(); }
+
+  void Join() {
+    if (!finished_) {
+      GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0));
+      finished_ = true;
+    }
+  }
+
+  virtual void Run() {
+    if (thread_can_start_ != NULL)
+      thread_can_start_->WaitForNotification();
+    func_(param_);
+  }
+
+ private:
+  const UserThreadFunc func_;  // User-supplied thread function.
+  const T param_;  // User-supplied parameter to the thread function.
+  // When non-NULL, used to block execution until the controller thread
+  // notifies.
+  Notification* const thread_can_start_;
+  bool finished_;  // true iff we know that the thread function has finished.
+  pthread_t thread_;  // The native thread object.
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+
+// MutexBase and Mutex implement mutex on pthreads-based platforms. They
+// are used in conjunction with class MutexLock:
+//
+//   Mutex mutex;
+//   ...
+//   MutexLock lock(&mutex);  // Acquires the mutex and releases it at the end
+//                            // of the current scope.
+//
+// MutexBase implements behavior for both statically and dynamically
+// allocated mutexes.  Do not use MutexBase directly.  Instead, write
+// the following to define a static mutex:
+//
+//   GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+//
+// You can forward declare a static mutex like this:
+//
+//   GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+//
+// To create a dynamic mutex, just define an object of type Mutex.
+class MutexBase {
+ public:
+  // Acquires this mutex.
+  void Lock() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
+    owner_ = pthread_self();
+    has_owner_ = true;
+  }
+
+  // Releases this mutex.
+  void Unlock() {
+    // Since the lock is being released the owner_ field should no longer be
+    // considered valid. We don't protect writing to has_owner_ here, as it's
+    // the caller's responsibility to ensure that the current thread holds the
+    // mutex when this is called.
+    has_owner_ = false;
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
+  }
+
+  // Does nothing if the current thread holds the mutex. Otherwise, crashes
+  // with high probability.
+  void AssertHeld() const {
+    GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self()))
+        << "The current thread is not holding the mutex @" << this;
+  }
+
+  // A static mutex may be used before main() is entered.  It may even
+  // be used before the dynamic initialization stage.  Therefore we
+  // must be able to initialize a static mutex object at link time.
+  // This means MutexBase has to be a POD and its member variables
+  // have to be public.
+ public:
+  pthread_mutex_t mutex_;  // The underlying pthread mutex.
+  // has_owner_ indicates whether the owner_ field below contains a valid thread
+  // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All
+  // accesses to the owner_ field should be protected by a check of this field.
+  // An alternative might be to memset() owner_ to all zeros, but there's no
+  // guarantee that a zero'd pthread_t is necessarily invalid or even different
+  // from pthread_self().
+  bool has_owner_;
+  pthread_t owner_;  // The thread holding the mutex.
+};
+
+// Forward-declares a static mutex.
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+    extern ::testing::internal::MutexBase mutex
+
+// Defines and statically (i.e. at link time) initializes a static mutex.
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+    ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
+
+// The Mutex class can only be used for mutexes created at runtime. It
+// shares its API with MutexBase otherwise.
+class Mutex : public MutexBase {
+ public:
+  Mutex() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+    has_owner_ = false;
+  }
+  ~Mutex() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+// We cannot name this class MutexLock as the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms.  Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+  explicit GTestMutexLock(MutexBase* mutex)
+      : mutex_(mutex) { mutex_->Lock(); }
+
+  ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+  MutexBase* const mutex_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Helpers for ThreadLocal.
+
+// pthread_key_create() requires DeleteThreadLocalValue() to have
+// C-linkage.  Therefore it cannot be templatized to access
+// ThreadLocal<T>.  Hence the need for class
+// ThreadLocalValueHolderBase.
+class ThreadLocalValueHolderBase {
+ public:
+  virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Called by pthread to delete thread-local data stored by
+// pthread_setspecific().
+extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
+  delete static_cast<ThreadLocalValueHolderBase*>(value_holder);
+}
+
+// Implements thread-local storage on pthreads-based systems.
+//
+//   // Thread 1
+//   ThreadLocal<int> tl(100);  // 100 is the default value for each thread.
+//
+//   // Thread 2
+//   tl.set(150);  // Changes the value for thread 2 only.
+//   EXPECT_EQ(150, tl.get());
+//
+//   // Thread 1
+//   EXPECT_EQ(100, tl.get());  // In thread 1, tl has the original value.
+//   tl.set(200);
+//   EXPECT_EQ(200, tl.get());
+//
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor.
+//
+// An object managed for a thread by a ThreadLocal instance is deleted
+// when the thread exits.  Or, if the ThreadLocal instance dies in
+// that thread, when the ThreadLocal dies.  It's the user's
+// responsibility to ensure that all other threads using a ThreadLocal
+// have exited when it dies, or the per-thread objects for those
+// threads will not be deleted.
+//
+// Google Test only uses global ThreadLocal objects.  That means they
+// will die after main() has returned.  Therefore, no per-thread
+// object managed by Google Test will be leaked as long as all threads
+// using Google Test have exited when main() returns.
+template <typename T>
+class ThreadLocal {
+ public:
+  ThreadLocal() : key_(CreateKey()),
+                  default_() {}
+  explicit ThreadLocal(const T& value) : key_(CreateKey()),
+                                         default_(value) {}
+
+  ~ThreadLocal() {
+    // Destroys the managed object for the current thread, if any.
+    DeleteThreadLocalValue(pthread_getspecific(key_));
+
+    // Releases resources associated with the key.  This will *not*
+    // delete managed objects for other threads.
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_));
+  }
+
+  T* pointer() { return GetOrCreateValue(); }
+  const T* pointer() const { return GetOrCreateValue(); }
+  const T& get() const { return *pointer(); }
+  void set(const T& value) { *pointer() = value; }
+
+ private:
+  // Holds a value of type T.
+  class ValueHolder : public ThreadLocalValueHolderBase {
+   public:
+    explicit ValueHolder(const T& value) : value_(value) {}
+
+    T* pointer() { return &value_; }
+
+   private:
+    T value_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+  };
+
+  static pthread_key_t CreateKey() {
+    pthread_key_t key;
+    // When a thread exits, DeleteThreadLocalValue() will be called on
+    // the object managed for that thread.
+    GTEST_CHECK_POSIX_SUCCESS_(
+        pthread_key_create(&key, &DeleteThreadLocalValue));
+    return key;
+  }
+
+  T* GetOrCreateValue() const {
+    ThreadLocalValueHolderBase* const holder =
+        static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_));
+    if (holder != NULL) {
+      return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
+    }
+
+    ValueHolder* const new_holder = new ValueHolder(default_);
+    ThreadLocalValueHolderBase* const holder_base = new_holder;
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
+    return new_holder->pointer();
+  }
+
+  // A key pthreads uses for looking up per-thread values.
+  const pthread_key_t key_;
+  const T default_;  // The default value for each thread.
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# define GTEST_IS_THREADSAFE 1
+
+#else  // GTEST_HAS_PTHREAD
+
+// A dummy implementation of synchronization primitives (mutex, lock,
+// and thread-local variable).  Necessary for compiling Google Test where
+// mutex is not supported - using Google Test in multiple threads is not
+// supported on such platforms.
+
+class Mutex {
+ public:
+  Mutex() {}
+  void Lock() {}
+  void Unlock() {}
+  void AssertHeld() const {}
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+  extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
+
+class GTestMutexLock {
+ public:
+  explicit GTestMutexLock(Mutex*) {}  // NOLINT
+};
+
+typedef GTestMutexLock MutexLock;
+
+template <typename T>
+class ThreadLocal {
+ public:
+  ThreadLocal() : value_() {}
+  explicit ThreadLocal(const T& value) : value_(value) {}
+  T* pointer() { return &value_; }
+  const T* pointer() const { return &value_; }
+  const T& get() const { return value_; }
+  void set(const T& value) { value_ = value; }
+ private:
+  T value_;
+};
+
+// The above synchronization primitives have dummy implementations.
+// Therefore Google Test is not thread-safe.
+# define GTEST_IS_THREADSAFE 0
+
+#endif  // GTEST_HAS_PTHREAD
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+GTEST_API_ size_t GetThreadCount();
+
+// Passing non-POD classes through ellipsis (...) crashes the ARM
+// compiler and generates a warning in Sun Studio.  The Nokia Symbian
+// and the IBM XL C/C++ compiler try to instantiate a copy constructor
+// for objects passed through ellipsis (...), failing for uncopyable
+// objects.  We define this to ensure that only POD is passed through
+// ellipsis on these systems.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC)
+// We lose support for NULL detection where the compiler doesn't like
+// passing non-POD classes through ellipsis (...).
+# define GTEST_ELLIPSIS_NEEDS_POD_ 1
+#else
+# define GTEST_CAN_COMPARE_NULL 1
+#endif
+
+// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between
+// const T& and const T* in a function template.  These compilers
+// _can_ decide between class template specializations for T and T*,
+// so a tr1::type_traits-like is_pointer works.
+#if defined(__SYMBIAN32__) || defined(__IBMCPP__)
+# define GTEST_NEEDS_IS_POINTER_ 1
+#endif
+
+template <bool bool_value>
+struct bool_constant {
+  typedef bool_constant<bool_value> type;
+  static const bool value = bool_value;
+};
+template <bool bool_value> const bool bool_constant<bool_value>::value;
+
+typedef bool_constant<false> false_type;
+typedef bool_constant<true> true_type;
+
+template <typename T>
+struct is_pointer : public false_type {};
+
+template <typename T>
+struct is_pointer<T*> : public true_type {};
+
+template <typename Iterator>
+struct IteratorTraits {
+  typedef typename Iterator::value_type value_type;
+};
+
+template <typename T>
+struct IteratorTraits<T*> {
+  typedef T value_type;
+};
+
+template <typename T>
+struct IteratorTraits<const T*> {
+  typedef T value_type;
+};
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_SEP_ "\\"
+# define GTEST_HAS_ALT_PATH_SEP_ 1
+// The biggest signed integer type the compiler supports.
+typedef __int64 BiggestInt;
+#else
+# define GTEST_PATH_SEP_ "/"
+# define GTEST_HAS_ALT_PATH_SEP_ 0
+typedef long long BiggestInt;  // NOLINT
+#endif  // GTEST_OS_WINDOWS
+
+// Utilities for char.
+
+// isspace(int ch) and friends accept an unsigned char or EOF.  char
+// may be signed, depending on the compiler (or compiler flags).
+// Therefore we need to cast a char to unsigned char before calling
+// isspace(), etc.
+
+inline bool IsAlpha(char ch) {
+  return isalpha(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsAlNum(char ch) {
+  return isalnum(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsDigit(char ch) {
+  return isdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsLower(char ch) {
+  return islower(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsSpace(char ch) {
+  return isspace(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsUpper(char ch) {
+  return isupper(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(char ch) {
+  return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(wchar_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
+
+inline char ToLower(char ch) {
+  return static_cast<char>(tolower(static_cast<unsigned char>(ch)));
+}
+inline char ToUpper(char ch) {
+  return static_cast<char>(toupper(static_cast<unsigned char>(ch)));
+}
+
+// The testing::internal::posix namespace holds wrappers for common
+// POSIX functions.  These wrappers hide the differences between
+// Windows/MSVC and POSIX systems.  Since some compilers define these
+// standard functions as macros, the wrapper cannot have the same name
+// as the wrapped function.
+
+namespace posix {
+
+// Functions with a different name on Windows.
+
+#if GTEST_OS_WINDOWS
+
+typedef struct _stat StatStruct;
+
+# ifdef __BORLANDC__
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+# else  // !__BORLANDC__
+#  if GTEST_OS_WINDOWS_MOBILE
+inline int IsATTY(int /* fd */) { return 0; }
+#  else
+inline int IsATTY(int fd) { return _isatty(fd); }
+#  endif  // GTEST_OS_WINDOWS_MOBILE
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return _stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return _strdup(src); }
+# endif  // __BORLANDC__
+
+# if GTEST_OS_WINDOWS_MOBILE
+inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); }
+// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this
+// time and thus not defined there.
+# else
+inline int FileNo(FILE* file) { return _fileno(file); }
+inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); }
+inline int RmDir(const char* dir) { return _rmdir(dir); }
+inline bool IsDir(const StatStruct& st) {
+  return (_S_IFDIR & st.st_mode) != 0;
+}
+# endif  // GTEST_OS_WINDOWS_MOBILE
+
+#else
+
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int IsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
+#endif  // GTEST_OS_WINDOWS
+
+// Functions deprecated by MSVC 8.0.
+
+#ifdef _MSC_VER
+// Temporarily disable warning 4996 (deprecated function).
+# pragma warning(push)
+# pragma warning(disable:4996)
+#endif
+
+inline const char* StrNCpy(char* dest, const char* src, size_t n) {
+  return strncpy(dest, src, n);
+}
+
+// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and
+// StrError() aren't needed on Windows CE at this time and thus not
+// defined there.
+
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int ChDir(const char* dir) { return chdir(dir); }
+#endif
+inline FILE* FOpen(const char* path, const char* mode) {
+  return fopen(path, mode);
+}
+#if !GTEST_OS_WINDOWS_MOBILE
+inline FILE *FReopen(const char* path, const char* mode, FILE* stream) {
+  return freopen(path, mode, stream);
+}
+inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); }
+#endif
+inline int FClose(FILE* fp) { return fclose(fp); }
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int Read(int fd, void* buf, unsigned int count) {
+  return static_cast<int>(read(fd, buf, count));
+}
+inline int Write(int fd, const void* buf, unsigned int count) {
+  return static_cast<int>(write(fd, buf, count));
+}
+inline int Close(int fd) { return close(fd); }
+inline const char* StrError(int errnum) { return strerror(errnum); }
+#endif
+inline const char* GetEnv(const char* name) {
+#if GTEST_OS_WINDOWS_MOBILE
+  // We are on Windows CE, which has no environment variables.
+  return NULL;
+#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
+  // Environment variables which we programmatically clear will be set to the
+  // empty string rather than unset (NULL).  Handle that case.
+  const char* const env = getenv(name);
+  return (env != NULL && env[0] != '\0') ? env : NULL;
+#else
+  return getenv(name);
+#endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)  // Restores the warning state.
+#endif
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Windows CE has no C library. The abort() function is used in
+// several places in Google Test. This implementation provides a reasonable
+// imitation of standard behaviour.
+void Abort();
+#else
+inline void Abort() { abort(); }
+#endif  // GTEST_OS_WINDOWS_MOBILE
+
+}  // namespace posix
+
+// MSVC "deprecates" snprintf and issues warnings wherever it is used.  In
+// order to avoid these warnings, we need to use _snprintf or _snprintf_s on
+// MSVC-based platforms.  We map the GTEST_SNPRINTF_ macro to the appropriate
+// function in order to achieve that.  We use macro definition here because
+// snprintf is a variadic function.
+#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE
+// MSVC 2005 and above support variadic macros.
+# define GTEST_SNPRINTF_(buffer, size, format, ...) \
+     _snprintf_s(buffer, size, size, format, __VA_ARGS__)
+#elif defined(_MSC_VER)
+// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't
+// complain about _snprintf.
+# define GTEST_SNPRINTF_ _snprintf
+#else
+# define GTEST_SNPRINTF_ snprintf
+#endif
+
+// The maximum number a BiggestInt can represent.  This definition
+// works no matter BiggestInt is represented in one's complement or
+// two's complement.
+//
+// We cannot rely on numeric_limits in STL, as __int64 and long long
+// are not part of standard C++ and numeric_limits doesn't need to be
+// defined for them.
+const BiggestInt kMaxBiggestInt =
+    ~(static_cast<BiggestInt>(1) << (8*sizeof(BiggestInt) - 1));
+
+// This template class serves as a compile-time function from size to
+// type.  It maps a size in bytes to a primitive type with that
+// size. e.g.
+//
+//   TypeWithSize<4>::UInt
+//
+// is typedef-ed to be unsigned int (unsigned integer made up of 4
+// bytes).
+//
+// Such functionality should belong to STL, but I cannot find it
+// there.
+//
+// Google Test uses this class in the implementation of floating-point
+// comparison.
+//
+// For now it only handles UInt (unsigned int) as that's all Google Test
+// needs.  Other types can be easily added in the future if need
+// arises.
+template <size_t size>
+class TypeWithSize {
+ public:
+  // This prevents the user from using TypeWithSize<N> with incorrect
+  // values of N.
+  typedef void UInt;
+};
+
+// The specialization for size 4.
+template <>
+class TypeWithSize<4> {
+ public:
+  // unsigned int has size 4 in both gcc and MSVC.
+  //
+  // As base/basictypes.h doesn't compile on Windows, we cannot use
+  // uint32, uint64, and etc here.
+  typedef int Int;
+  typedef unsigned int UInt;
+};
+
+// The specialization for size 8.
+template <>
+class TypeWithSize<8> {
+ public:
+#if GTEST_OS_WINDOWS
+  typedef __int64 Int;
+  typedef unsigned __int64 UInt;
+#else
+  typedef long long Int;  // NOLINT
+  typedef unsigned long long UInt;  // NOLINT
+#endif  // GTEST_OS_WINDOWS
+};
+
+// Integer types of known sizes.
+typedef TypeWithSize<4>::Int Int32;
+typedef TypeWithSize<4>::UInt UInt32;
+typedef TypeWithSize<8>::Int Int64;
+typedef TypeWithSize<8>::UInt UInt64;
+typedef TypeWithSize<8>::Int TimeInMillis;  // Represents time in milliseconds.
+
+// Utilities for command line flags and environment variables.
+
+// Macro for referencing flags.
+#define GTEST_FLAG(name) FLAGS_gtest_##name
+
+// Macros for declaring flags.
+#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
+#define GTEST_DECLARE_int32_(name) \
+    GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
+#define GTEST_DECLARE_string_(name) \
+    GTEST_API_ extern ::std::string GTEST_FLAG(name)
+
+// Macros for defining flags.
+#define GTEST_DEFINE_bool_(name, default_val, doc) \
+    GTEST_API_ bool GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_int32_(name, default_val, doc) \
+    GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_string_(name, default_val, doc) \
+    GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
+
+// Thread annotations
+#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+#define GTEST_LOCK_EXCLUDED_(locks)
+
+// Parses 'str' for a 32-bit signed integer.  If successful, writes the result
+// to *value and returns true; otherwise leaves *value unchanged and returns
+// false.
+// TODO(chandlerc): Find a better way to refactor flag and environment parsing
+// out of both gtest-port.cc and gtest.cc to avoid exporting this utility
+// function.
+bool ParseInt32(const Message& src_text, const char* str, Int32* value);
+
+// Parses a bool/Int32/string from the environment variable
+// corresponding to the given Google Test flag.
+bool BoolFromGTestEnv(const char* flag, bool default_val);
+GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-string.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-string.h
new file mode 100644
index 0000000..97f1a7f
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-string.h
@@ -0,0 +1,167 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: wan at google.com (Zhanyong Wan), eefacm at gmail.com (Sean Mcafee)
+//
+// The Google C++ Testing Framework (Google Test)
+//
+// This header file declares the String class and functions used internally by
+// Google Test.  They are subject to change without notice. They should not used
+// by code external to Google Test.
+//
+// This header file is #included by <gtest/internal/gtest-internal.h>.
+// It should not be #included by other files.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+
+#ifdef __BORLANDC__
+// string.h is not guaranteed to provide strcpy on C++ Builder.
+# include <mem.h>
+#endif
+
+#include <string.h>
+#include <string>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// String - an abstract class holding static string utilities.
+class GTEST_API_ String {
+ public:
+  // Static utility methods
+
+  // Clones a 0-terminated C string, allocating memory using new.  The
+  // caller is responsible for deleting the return value using
+  // delete[].  Returns the cloned string, or NULL if the input is
+  // NULL.
+  //
+  // This is different from strdup() in string.h, which allocates
+  // memory using malloc().
+  static const char* CloneCString(const char* c_str);
+
+#if GTEST_OS_WINDOWS_MOBILE
+  // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
+  // able to pass strings to Win32 APIs on CE we need to convert them
+  // to 'Unicode', UTF-16.
+
+  // Creates a UTF-16 wide string from the given ANSI string, allocating
+  // memory using new. The caller is responsible for deleting the return
+  // value using delete[]. Returns the wide string, or NULL if the
+  // input is NULL.
+  //
+  // The wide string is created using the ANSI codepage (CP_ACP) to
+  // match the behaviour of the ANSI versions of Win32 calls and the
+  // C runtime.
+  static LPCWSTR AnsiToUtf16(const char* c_str);
+
+  // Creates an ANSI string from the given wide string, allocating
+  // memory using new. The caller is responsible for deleting the return
+  // value using delete[]. Returns the ANSI string, or NULL if the
+  // input is NULL.
+  //
+  // The returned string is created using the ANSI codepage (CP_ACP) to
+  // match the behaviour of the ANSI versions of Win32 calls and the
+  // C runtime.
+  static const char* Utf16ToAnsi(LPCWSTR utf16_str);
+#endif
+
+  // Compares two C strings.  Returns true iff they have the same content.
+  //
+  // Unlike strcmp(), this function can handle NULL argument(s).  A
+  // NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool CStringEquals(const char* lhs, const char* rhs);
+
+  // Converts a wide C string to a String using the UTF-8 encoding.
+  // NULL will be converted to "(null)".  If an error occurred during
+  // the conversion, "(failed to convert from wide string)" is
+  // returned.
+  static std::string ShowWideCString(const wchar_t* wide_c_str);
+
+  // Compares two wide C strings.  Returns true iff they have the same
+  // content.
+  //
+  // Unlike wcscmp(), this function can handle NULL argument(s).  A
+  // NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
+
+  // Compares two C strings, ignoring case.  Returns true iff they
+  // have the same content.
+  //
+  // Unlike strcasecmp(), this function can handle NULL argument(s).
+  // A NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool CaseInsensitiveCStringEquals(const char* lhs,
+                                           const char* rhs);
+
+  // Compares two wide C strings, ignoring case.  Returns true iff they
+  // have the same content.
+  //
+  // Unlike wcscasecmp(), this function can handle NULL argument(s).
+  // A NULL C string is considered different to any non-NULL wide C string,
+  // including the empty string.
+  // NB: The implementations on different platforms slightly differ.
+  // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+  // environment variable. On GNU platform this method uses wcscasecmp
+  // which compares according to LC_CTYPE category of the current locale.
+  // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+  // current locale.
+  static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+                                               const wchar_t* rhs);
+
+  // Returns true iff the given string ends with the given suffix, ignoring
+  // case. Any string is considered to end with an empty suffix.
+  static bool EndsWithCaseInsensitive(
+      const std::string& str, const std::string& suffix);
+
+  // Formats an int value as "%02d".
+  static std::string FormatIntWidth2(int value);  // "%02d" for width == 2
+
+  // Formats an int value as "%X".
+  static std::string FormatHexInt(int value);
+
+  // Formats a byte as "%02X".
+  static std::string FormatByte(unsigned char value);
+
+ private:
+  String();  // Not meant to be instantiated.
+};  // class String
+
+// Gets the content of the stringstream's buffer as an std::string.  Each '\0'
+// character in the buffer is replaced with "\\0".
+GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h
new file mode 100644
index 0000000..7b3dfc3
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h
@@ -0,0 +1,1012 @@
+// This file was GENERATED by command:
+//     pump.py gtest-tuple.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility>  // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined).  This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+    template <GTEST_10_TYPENAMES_(U)> friend class tuple; \
+   private:
+#endif
+
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \
+    void, void, void>
+#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \
+    void, void, void>
+#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \
+    void, void, void>
+#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \
+    void, void, void>
+#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \
+    void, void, void>
+#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \
+    void, void, void>
+#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+    void, void, void>
+#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+    T##7, void, void>
+#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+    T##7, T##8, void>
+#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \
+    T##7, T##8, T##9>
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+#define GTEST_0_TYPENAMES_(T)
+#define GTEST_1_TYPENAMES_(T) typename T##0
+#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1
+#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2
+#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3
+#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4
+#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4, typename T##5
+#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4, typename T##5, typename T##6
+#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4, typename T##5, typename T##6, typename T##7
+#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4, typename T##5, typename T##6, \
+    typename T##7, typename T##8
+#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \
+    typename T##3, typename T##4, typename T##5, typename T##6, \
+    typename T##7, typename T##8, typename T##9
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior.  We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <typename T0 = void, typename T1 = void, typename T2 = void,
+    typename T3 = void, typename T4 = void, typename T5 = void,
+    typename T6 = void, typename T7 = void, typename T8 = void,
+    typename T9 = void>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; };  // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&.  This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; };  // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>.  kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 0, GTEST_10_TUPLE_(T) > {
+  typedef T0 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 1, GTEST_10_TUPLE_(T) > {
+  typedef T1 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 2, GTEST_10_TUPLE_(T) > {
+  typedef T2 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 3, GTEST_10_TUPLE_(T) > {
+  typedef T3 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 4, GTEST_10_TUPLE_(T) > {
+  typedef T4 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 5, GTEST_10_TUPLE_(T) > {
+  typedef T5 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 6, GTEST_10_TUPLE_(T) > {
+  typedef T6 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 7, GTEST_10_TUPLE_(T) > {
+  typedef T7 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 8, GTEST_10_TUPLE_(T) > {
+  typedef T8 type;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct TupleElement<true, 9, GTEST_10_TUPLE_(T) > {
+  typedef T9 type;
+};
+
+}  // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+  tuple() {}
+  tuple(const tuple& /* t */)  {}
+  tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+class GTEST_1_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {}
+
+  tuple(const tuple& t) : f0_(t.f0_) {}
+
+  template <GTEST_1_TYPENAMES_(U)>
+  tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_1_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_1_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_1_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    return *this;
+  }
+
+  T0 f0_;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+class GTEST_2_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0),
+      f1_(f1) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {}
+
+  template <GTEST_2_TYPENAMES_(U)>
+  tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {}
+  template <typename U0, typename U1>
+  tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_2_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_2_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+  template <typename U0, typename U1>
+  tuple& operator=(const ::std::pair<U0, U1>& p) {
+    f0_ = p.first;
+    f1_ = p.second;
+    return *this;
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_2_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+class GTEST_3_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+  template <GTEST_3_TYPENAMES_(U)>
+  tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_3_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_3_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_3_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+class GTEST_4_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2),
+      f3_(f3) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {}
+
+  template <GTEST_4_TYPENAMES_(U)>
+  tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_4_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_4_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_4_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+class GTEST_5_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3,
+      GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_) {}
+
+  template <GTEST_5_TYPENAMES_(U)>
+  tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_5_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_5_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_5_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+class GTEST_6_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+      GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+      f5_(f5) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_), f5_(t.f5_) {}
+
+  template <GTEST_6_TYPENAMES_(U)>
+  tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_6_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_6_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_6_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    f5_ = t.f5_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+  T5 f5_;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+class GTEST_7_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+      GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2),
+      f3_(f3), f4_(f4), f5_(f5), f6_(f6) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+  template <GTEST_7_TYPENAMES_(U)>
+  tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_7_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_7_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_7_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    f5_ = t.f5_;
+    f6_ = t.f6_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+  T5 f5_;
+  T6 f6_;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+class GTEST_8_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+      GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6,
+      GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+      f5_(f5), f6_(f6), f7_(f7) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+  template <GTEST_8_TYPENAMES_(U)>
+  tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_8_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_8_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_8_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    f5_ = t.f5_;
+    f6_ = t.f6_;
+    f7_ = t.f7_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+  T5 f5_;
+  T6 f6_;
+  T7 f7_;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+class GTEST_9_TUPLE_(T) {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+      GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+      GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4),
+      f5_(f5), f6_(f6), f7_(f7), f8_(f8) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+  template <GTEST_9_TYPENAMES_(U)>
+  tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_9_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_9_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_9_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    f5_ = t.f5_;
+    f6_ = t.f6_;
+    f7_ = t.f7_;
+    f8_ = t.f8_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+  T5 f5_;
+  T6 f6_;
+  T7 f7_;
+  T8 f8_;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+class tuple {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(),
+      f9_() {}
+
+  explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1,
+      GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4,
+      GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7,
+      GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2),
+      f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {}
+
+  tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_),
+      f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {}
+
+  template <GTEST_10_TYPENAMES_(U)>
+  tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_),
+      f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_),
+      f9_(t.f9_) {}
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_10_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_10_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_10_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) {
+    f0_ = t.f0_;
+    f1_ = t.f1_;
+    f2_ = t.f2_;
+    f3_ = t.f3_;
+    f4_ = t.f4_;
+    f5_ = t.f5_;
+    f6_ = t.f6_;
+    f7_ = t.f7_;
+    f8_ = t.f8_;
+    f9_ = t.f9_;
+    return *this;
+  }
+
+  T0 f0_;
+  T1 f1_;
+  T2 f2_;
+  T3 f3_;
+  T4 f4_;
+  T5 f5_;
+  T6 f6_;
+  T7 f7_;
+  T8 f8_;
+  T9 f9_;
+};
+
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple().  And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+template <GTEST_1_TYPENAMES_(T)>
+inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) {
+  return GTEST_1_TUPLE_(T)(f0);
+}
+
+template <GTEST_2_TYPENAMES_(T)>
+inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) {
+  return GTEST_2_TUPLE_(T)(f0, f1);
+}
+
+template <GTEST_3_TYPENAMES_(T)>
+inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) {
+  return GTEST_3_TUPLE_(T)(f0, f1, f2);
+}
+
+template <GTEST_4_TYPENAMES_(T)>
+inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3) {
+  return GTEST_4_TUPLE_(T)(f0, f1, f2, f3);
+}
+
+template <GTEST_5_TYPENAMES_(T)>
+inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4) {
+  return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4);
+}
+
+template <GTEST_6_TYPENAMES_(T)>
+inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4, const T5& f5) {
+  return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5);
+}
+
+template <GTEST_7_TYPENAMES_(T)>
+inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4, const T5& f5, const T6& f6) {
+  return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6);
+}
+
+template <GTEST_8_TYPENAMES_(T)>
+inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) {
+  return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7);
+}
+
+template <GTEST_9_TYPENAMES_(T)>
+inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+    const T8& f8) {
+  return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8);
+}
+
+template <GTEST_10_TYPENAMES_(T)>
+inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2,
+    const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7,
+    const T8& f8, const T9& f9) {
+  return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+}
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+template <GTEST_0_TYPENAMES_(T)>
+struct tuple_size<GTEST_0_TUPLE_(T) > {
+  static const int value = 0;
+};
+
+template <GTEST_1_TYPENAMES_(T)>
+struct tuple_size<GTEST_1_TUPLE_(T) > {
+  static const int value = 1;
+};
+
+template <GTEST_2_TYPENAMES_(T)>
+struct tuple_size<GTEST_2_TUPLE_(T) > {
+  static const int value = 2;
+};
+
+template <GTEST_3_TYPENAMES_(T)>
+struct tuple_size<GTEST_3_TUPLE_(T) > {
+  static const int value = 3;
+};
+
+template <GTEST_4_TYPENAMES_(T)>
+struct tuple_size<GTEST_4_TUPLE_(T) > {
+  static const int value = 4;
+};
+
+template <GTEST_5_TYPENAMES_(T)>
+struct tuple_size<GTEST_5_TUPLE_(T) > {
+  static const int value = 5;
+};
+
+template <GTEST_6_TYPENAMES_(T)>
+struct tuple_size<GTEST_6_TUPLE_(T) > {
+  static const int value = 6;
+};
+
+template <GTEST_7_TYPENAMES_(T)>
+struct tuple_size<GTEST_7_TUPLE_(T) > {
+  static const int value = 7;
+};
+
+template <GTEST_8_TYPENAMES_(T)>
+struct tuple_size<GTEST_8_TUPLE_(T) > {
+  static const int value = 8;
+};
+
+template <GTEST_9_TYPENAMES_(T)>
+struct tuple_size<GTEST_9_TUPLE_(T) > {
+  static const int value = 9;
+};
+
+template <GTEST_10_TYPENAMES_(T)>
+struct tuple_size<GTEST_10_TUPLE_(T) > {
+  static const int value = 10;
+};
+
+template <int k, class Tuple>
+struct tuple_element {
+  typedef typename gtest_internal::TupleElement<
+      k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+template <>
+class Get<0> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+  Field(Tuple& t) { return t.f0_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple))
+  ConstField(const Tuple& t) { return t.f0_; }
+};
+
+template <>
+class Get<1> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+  Field(Tuple& t) { return t.f1_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple))
+  ConstField(const Tuple& t) { return t.f1_; }
+};
+
+template <>
+class Get<2> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+  Field(Tuple& t) { return t.f2_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple))
+  ConstField(const Tuple& t) { return t.f2_; }
+};
+
+template <>
+class Get<3> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+  Field(Tuple& t) { return t.f3_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple))
+  ConstField(const Tuple& t) { return t.f3_; }
+};
+
+template <>
+class Get<4> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+  Field(Tuple& t) { return t.f4_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple))
+  ConstField(const Tuple& t) { return t.f4_; }
+};
+
+template <>
+class Get<5> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+  Field(Tuple& t) { return t.f5_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple))
+  ConstField(const Tuple& t) { return t.f5_; }
+};
+
+template <>
+class Get<6> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+  Field(Tuple& t) { return t.f6_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple))
+  ConstField(const Tuple& t) { return t.f6_; }
+};
+
+template <>
+class Get<7> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+  Field(Tuple& t) { return t.f7_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple))
+  ConstField(const Tuple& t) { return t.f7_; }
+};
+
+template <>
+class Get<8> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+  Field(Tuple& t) { return t.f8_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple))
+  ConstField(const Tuple& t) { return t.f8_; }
+};
+
+template <>
+class Get<9> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+  Field(Tuple& t) { return t.f9_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple))
+  ConstField(const Tuple& t) { return t.f9_; }
+};
+
+}  // namespace gtest_internal
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T)))
+get(GTEST_10_TUPLE_(T)& t) {
+  return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_10_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k,  GTEST_10_TUPLE_(T)))
+get(const GTEST_10_TUPLE_(T)& t) {
+  return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+  template <class Tuple1, class Tuple2>
+  static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+    return true;
+  }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+  template <class Tuple1, class Tuple2>
+  static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+    return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+        ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+  }
+};
+
+}  // namespace gtest_internal
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator==(const GTEST_10_TUPLE_(T)& t,
+                       const GTEST_10_TUPLE_(U)& u) {
+  return gtest_internal::SameSizeTuplePrefixComparator<
+      tuple_size<GTEST_10_TUPLE_(T) >::value,
+      tuple_size<GTEST_10_TUPLE_(U) >::value>::Eq(t, u);
+}
+
+template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_10_TUPLE_(T)& t,
+                       const GTEST_10_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+}  // namespace tr1
+}  // namespace std
+
+#undef GTEST_0_TUPLE_
+#undef GTEST_1_TUPLE_
+#undef GTEST_2_TUPLE_
+#undef GTEST_3_TUPLE_
+#undef GTEST_4_TUPLE_
+#undef GTEST_5_TUPLE_
+#undef GTEST_6_TUPLE_
+#undef GTEST_7_TUPLE_
+#undef GTEST_8_TUPLE_
+#undef GTEST_9_TUPLE_
+#undef GTEST_10_TUPLE_
+
+#undef GTEST_0_TYPENAMES_
+#undef GTEST_1_TYPENAMES_
+#undef GTEST_2_TYPENAMES_
+#undef GTEST_3_TYPENAMES_
+#undef GTEST_4_TYPENAMES_
+#undef GTEST_5_TYPENAMES_
+#undef GTEST_6_TYPENAMES_
+#undef GTEST_7_TYPENAMES_
+#undef GTEST_8_TYPENAMES_
+#undef GTEST_9_TYPENAMES_
+#undef GTEST_10_TYPENAMES_
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h.pump b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h.pump
new file mode 100644
index 0000000..c7d9e03
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-tuple.h.pump
@@ -0,0 +1,339 @@
+$$ -*- mode: c++; -*-
+$var n = 10  $$ Maximum number of tuple fields we want to support.
+$$ This meta comment fixes auto-indentation in Emacs. }}
+// Copyright 2009 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Implements a subset of TR1 tuple needed by Google Test and Google Mock.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
+
+#include <utility>  // For ::std::pair.
+
+// The compiler used in Symbian has a bug that prevents us from declaring the
+// tuple template as a friend (it complains that tuple is redefined).  This
+// hack bypasses the bug by declaring the members that should otherwise be
+// private as public.
+// Sun Studio versions < 12 also have the above bug.
+#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590)
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public:
+#else
+# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \
+    template <GTEST_$(n)_TYPENAMES_(U)> friend class tuple; \
+   private:
+#endif
+
+
+$range i 0..n-1
+$range j 0..n
+$range k 1..n
+// GTEST_n_TUPLE_(T) is the type of an n-tuple.
+#define GTEST_0_TUPLE_(T) tuple<>
+
+$for k [[
+$range m 0..k-1
+$range m2 k..n-1
+#define GTEST_$(k)_TUPLE_(T) tuple<$for m, [[T##$m]]$for m2 [[, void]]>
+
+]]
+
+// GTEST_n_TYPENAMES_(T) declares a list of n typenames.
+
+$for j [[
+$range m 0..j-1
+#define GTEST_$(j)_TYPENAMES_(T) $for m, [[typename T##$m]]
+
+
+]]
+
+// In theory, defining stuff in the ::std namespace is undefined
+// behavior.  We can do this as we are playing the role of a standard
+// library vendor.
+namespace std {
+namespace tr1 {
+
+template <$for i, [[typename T$i = void]]>
+class tuple;
+
+// Anything in namespace gtest_internal is Google Test's INTERNAL
+// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code.
+namespace gtest_internal {
+
+// ByRef<T>::type is T if T is a reference; otherwise it's const T&.
+template <typename T>
+struct ByRef { typedef const T& type; };  // NOLINT
+template <typename T>
+struct ByRef<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper for ByRef.
+#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type
+
+// AddRef<T>::type is T if T is a reference; otherwise it's T&.  This
+// is the same as tr1::add_reference<T>::type.
+template <typename T>
+struct AddRef { typedef T& type; };  // NOLINT
+template <typename T>
+struct AddRef<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper for AddRef.
+#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type
+
+// A helper for implementing get<k>().
+template <int k> class Get;
+
+// A helper for implementing tuple_element<k, T>.  kIndexValid is true
+// iff k < the number of fields in tuple type T.
+template <bool kIndexValid, int kIndex, class Tuple>
+struct TupleElement;
+
+
+$for i [[
+template <GTEST_$(n)_TYPENAMES_(T)>
+struct TupleElement<true, $i, GTEST_$(n)_TUPLE_(T) > {
+  typedef T$i type;
+};
+
+
+]]
+}  // namespace gtest_internal
+
+template <>
+class tuple<> {
+ public:
+  tuple() {}
+  tuple(const tuple& /* t */)  {}
+  tuple& operator=(const tuple& /* t */) { return *this; }
+};
+
+
+$for k [[
+$range m 0..k-1
+template <GTEST_$(k)_TYPENAMES_(T)>
+class $if k < n [[GTEST_$(k)_TUPLE_(T)]] $else [[tuple]] {
+ public:
+  template <int k> friend class gtest_internal::Get;
+
+  tuple() : $for m, [[f$(m)_()]] {}
+
+  explicit tuple($for m, [[GTEST_BY_REF_(T$m) f$m]]) : [[]]
+$for m, [[f$(m)_(f$m)]] {}
+
+  tuple(const tuple& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
+
+  template <GTEST_$(k)_TYPENAMES_(U)>
+  tuple(const GTEST_$(k)_TUPLE_(U)& t) : $for m, [[f$(m)_(t.f$(m)_)]] {}
+
+$if k == 2 [[
+  template <typename U0, typename U1>
+  tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {}
+
+]]
+
+  tuple& operator=(const tuple& t) { return CopyFrom(t); }
+
+  template <GTEST_$(k)_TYPENAMES_(U)>
+  tuple& operator=(const GTEST_$(k)_TUPLE_(U)& t) {
+    return CopyFrom(t);
+  }
+
+$if k == 2 [[
+  template <typename U0, typename U1>
+  tuple& operator=(const ::std::pair<U0, U1>& p) {
+    f0_ = p.first;
+    f1_ = p.second;
+    return *this;
+  }
+
+]]
+
+  GTEST_DECLARE_TUPLE_AS_FRIEND_
+
+  template <GTEST_$(k)_TYPENAMES_(U)>
+  tuple& CopyFrom(const GTEST_$(k)_TUPLE_(U)& t) {
+
+$for m [[
+    f$(m)_ = t.f$(m)_;
+
+]]
+    return *this;
+  }
+
+
+$for m [[
+  T$m f$(m)_;
+
+]]
+};
+
+
+]]
+// 6.1.3.2 Tuple creation functions.
+
+// Known limitations: we don't support passing an
+// std::tr1::reference_wrapper<T> to make_tuple().  And we don't
+// implement tie().
+
+inline tuple<> make_tuple() { return tuple<>(); }
+
+$for k [[
+$range m 0..k-1
+
+template <GTEST_$(k)_TYPENAMES_(T)>
+inline GTEST_$(k)_TUPLE_(T) make_tuple($for m, [[const T$m& f$m]]) {
+  return GTEST_$(k)_TUPLE_(T)($for m, [[f$m]]);
+}
+
+]]
+
+// 6.1.3.3 Tuple helper classes.
+
+template <typename Tuple> struct tuple_size;
+
+
+$for j [[
+template <GTEST_$(j)_TYPENAMES_(T)>
+struct tuple_size<GTEST_$(j)_TUPLE_(T) > {
+  static const int value = $j;
+};
+
+
+]]
+template <int k, class Tuple>
+struct tuple_element {
+  typedef typename gtest_internal::TupleElement<
+      k < (tuple_size<Tuple>::value), k, Tuple>::type type;
+};
+
+#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type
+
+// 6.1.3.4 Element access.
+
+namespace gtest_internal {
+
+
+$for i [[
+template <>
+class Get<$i> {
+ public:
+  template <class Tuple>
+  static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
+  Field(Tuple& t) { return t.f$(i)_; }  // NOLINT
+
+  template <class Tuple>
+  static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_($i, Tuple))
+  ConstField(const Tuple& t) { return t.f$(i)_; }
+};
+
+
+]]
+}  // namespace gtest_internal
+
+template <int k, GTEST_$(n)_TYPENAMES_(T)>
+GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_$(n)_TUPLE_(T)))
+get(GTEST_$(n)_TUPLE_(T)& t) {
+  return gtest_internal::Get<k>::Field(t);
+}
+
+template <int k, GTEST_$(n)_TYPENAMES_(T)>
+GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k,  GTEST_$(n)_TUPLE_(T)))
+get(const GTEST_$(n)_TUPLE_(T)& t) {
+  return gtest_internal::Get<k>::ConstField(t);
+}
+
+// 6.1.3.5 Relational operators
+
+// We only implement == and !=, as we don't have a need for the rest yet.
+
+namespace gtest_internal {
+
+// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the
+// first k fields of t1 equals the first k fields of t2.
+// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if
+// k1 != k2.
+template <int kSize1, int kSize2>
+struct SameSizeTuplePrefixComparator;
+
+template <>
+struct SameSizeTuplePrefixComparator<0, 0> {
+  template <class Tuple1, class Tuple2>
+  static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {
+    return true;
+  }
+};
+
+template <int k>
+struct SameSizeTuplePrefixComparator<k, k> {
+  template <class Tuple1, class Tuple2>
+  static bool Eq(const Tuple1& t1, const Tuple2& t2) {
+    return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) &&
+        ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2);
+  }
+};
+
+}  // namespace gtest_internal
+
+template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
+inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t,
+                       const GTEST_$(n)_TUPLE_(U)& u) {
+  return gtest_internal::SameSizeTuplePrefixComparator<
+      tuple_size<GTEST_$(n)_TUPLE_(T) >::value,
+      tuple_size<GTEST_$(n)_TUPLE_(U) >::value>::Eq(t, u);
+}
+
+template <GTEST_$(n)_TYPENAMES_(T), GTEST_$(n)_TYPENAMES_(U)>
+inline bool operator!=(const GTEST_$(n)_TUPLE_(T)& t,
+                       const GTEST_$(n)_TUPLE_(U)& u) { return !(t == u); }
+
+// 6.1.4 Pairs.
+// Unimplemented.
+
+}  // namespace tr1
+}  // namespace std
+
+
+$for j [[
+#undef GTEST_$(j)_TUPLE_
+
+]]
+
+
+$for j [[
+#undef GTEST_$(j)_TYPENAMES_
+
+]]
+
+#undef GTEST_DECLARE_TUPLE_AS_FRIEND_
+#undef GTEST_BY_REF_
+#undef GTEST_ADD_REF_
+#undef GTEST_TUPLE_ELEMENT_
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h
new file mode 100644
index 0000000..e46f7cf
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h
@@ -0,0 +1,3331 @@
+// This file was GENERATED by command:
+//     pump.py gtest-type-util.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests.  This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+// Currently we support at most 50 types in a list, and at most 50
+// type-parameterized tests in one type-parameterized test case.
+// Please contact googletestframework at googlegroups.com if you need
+// more.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here.  It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+#  include <cxxabi.h>
+# elif defined(__HP_aCC)
+#  include <acxx_demangle.h>
+# endif  // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// GetTypeName<T>() returns a human-readable name of type T.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+# if GTEST_HAS_RTTI
+
+  const char* const name = typeid(T).name();
+#  if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+  int status = 0;
+  // gcc's implementation of typeid(T).name() mangles the type name,
+  // so we have to demangle it.
+#   if GTEST_HAS_CXXABI_H_
+  using abi::__cxa_demangle;
+#   endif  // GTEST_HAS_CXXABI_H_
+  char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+  const std::string name_str(status == 0 ? readable_name : name);
+  free(readable_name);
+  return name_str;
+#  else
+  return name;
+#  endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
+
+# else
+
+  return "<type>";
+
+# endif  // GTEST_HAS_RTTI
+}
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
+// type.  This can be used as a compile-time assertion to ensure that
+// two types are equal.
+
+template <typename T1, typename T2>
+struct AssertTypeEq;
+
+template <typename T>
+struct AssertTypeEq<T, T> {
+  typedef bool type;
+};
+
+// A unique type used as the default value for the arguments of class
+// template Types.  This allows us to simulate variadic templates
+// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
+// support directly.
+struct None {};
+
+// The following family of struct and struct templates are used to
+// represent type lists.  In particular, TypesN<T1, T2, ..., TN>
+// represents a type list with N types (T1, T2, ..., and TN) in it.
+// Except for Types0, every struct in the family has two member types:
+// Head for the first type in the list, and Tail for the rest of the
+// list.
+
+// The empty type list.
+struct Types0 {};
+
+// Type lists of length 1, 2, 3, and so on.
+
+template <typename T1>
+struct Types1 {
+  typedef T1 Head;
+  typedef Types0 Tail;
+};
+template <typename T1, typename T2>
+struct Types2 {
+  typedef T1 Head;
+  typedef Types1<T2> Tail;
+};
+
+template <typename T1, typename T2, typename T3>
+struct Types3 {
+  typedef T1 Head;
+  typedef Types2<T2, T3> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types4 {
+  typedef T1 Head;
+  typedef Types3<T2, T3, T4> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types5 {
+  typedef T1 Head;
+  typedef Types4<T2, T3, T4, T5> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+struct Types6 {
+  typedef T1 Head;
+  typedef Types5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+struct Types7 {
+  typedef T1 Head;
+  typedef Types6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+struct Types8 {
+  typedef T1 Head;
+  typedef Types7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+struct Types9 {
+  typedef T1 Head;
+  typedef Types8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types10 {
+  typedef T1 Head;
+  typedef Types9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11>
+struct Types11 {
+  typedef T1 Head;
+  typedef Types10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12>
+struct Types12 {
+  typedef T1 Head;
+  typedef Types11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13>
+struct Types13 {
+  typedef T1 Head;
+  typedef Types12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14>
+struct Types14 {
+  typedef T1 Head;
+  typedef Types13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types15 {
+  typedef T1 Head;
+  typedef Types14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16>
+struct Types16 {
+  typedef T1 Head;
+  typedef Types15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17>
+struct Types17 {
+  typedef T1 Head;
+  typedef Types16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18>
+struct Types18 {
+  typedef T1 Head;
+  typedef Types17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19>
+struct Types19 {
+  typedef T1 Head;
+  typedef Types18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types20 {
+  typedef T1 Head;
+  typedef Types19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21>
+struct Types21 {
+  typedef T1 Head;
+  typedef Types20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22>
+struct Types22 {
+  typedef T1 Head;
+  typedef Types21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23>
+struct Types23 {
+  typedef T1 Head;
+  typedef Types22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24>
+struct Types24 {
+  typedef T1 Head;
+  typedef Types23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types25 {
+  typedef T1 Head;
+  typedef Types24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26>
+struct Types26 {
+  typedef T1 Head;
+  typedef Types25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27>
+struct Types27 {
+  typedef T1 Head;
+  typedef Types26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28>
+struct Types28 {
+  typedef T1 Head;
+  typedef Types27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29>
+struct Types29 {
+  typedef T1 Head;
+  typedef Types28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types30 {
+  typedef T1 Head;
+  typedef Types29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31>
+struct Types31 {
+  typedef T1 Head;
+  typedef Types30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32>
+struct Types32 {
+  typedef T1 Head;
+  typedef Types31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33>
+struct Types33 {
+  typedef T1 Head;
+  typedef Types32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34>
+struct Types34 {
+  typedef T1 Head;
+  typedef Types33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types35 {
+  typedef T1 Head;
+  typedef Types34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36>
+struct Types36 {
+  typedef T1 Head;
+  typedef Types35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37>
+struct Types37 {
+  typedef T1 Head;
+  typedef Types36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38>
+struct Types38 {
+  typedef T1 Head;
+  typedef Types37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39>
+struct Types39 {
+  typedef T1 Head;
+  typedef Types38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types40 {
+  typedef T1 Head;
+  typedef Types39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41>
+struct Types41 {
+  typedef T1 Head;
+  typedef Types40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42>
+struct Types42 {
+  typedef T1 Head;
+  typedef Types41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43>
+struct Types43 {
+  typedef T1 Head;
+  typedef Types42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44>
+struct Types44 {
+  typedef T1 Head;
+  typedef Types43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types45 {
+  typedef T1 Head;
+  typedef Types44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46>
+struct Types46 {
+  typedef T1 Head;
+  typedef Types45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45, T46> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47>
+struct Types47 {
+  typedef T1 Head;
+  typedef Types46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45, T46, T47> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48>
+struct Types48 {
+  typedef T1 Head;
+  typedef Types47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45, T46, T47, T48> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49>
+struct Types49 {
+  typedef T1 Head;
+  typedef Types48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49, typename T50>
+struct Types50 {
+  typedef T1 Head;
+  typedef Types49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+      T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+}  // namespace internal
+
+// We don't want to require the users to write TypesN<...> directly,
+// as that would require them to count the length.  Types<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Types<int>
+// will appear as Types<int, None, None, ..., None> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Types<T1, ..., TN>, and Google Test will translate
+// that to TypesN<T1, ..., TN> internally to make error messages
+// readable.  The translation is done by the 'type' member of the
+// Types template.
+template <typename T1 = internal::None, typename T2 = internal::None,
+    typename T3 = internal::None, typename T4 = internal::None,
+    typename T5 = internal::None, typename T6 = internal::None,
+    typename T7 = internal::None, typename T8 = internal::None,
+    typename T9 = internal::None, typename T10 = internal::None,
+    typename T11 = internal::None, typename T12 = internal::None,
+    typename T13 = internal::None, typename T14 = internal::None,
+    typename T15 = internal::None, typename T16 = internal::None,
+    typename T17 = internal::None, typename T18 = internal::None,
+    typename T19 = internal::None, typename T20 = internal::None,
+    typename T21 = internal::None, typename T22 = internal::None,
+    typename T23 = internal::None, typename T24 = internal::None,
+    typename T25 = internal::None, typename T26 = internal::None,
+    typename T27 = internal::None, typename T28 = internal::None,
+    typename T29 = internal::None, typename T30 = internal::None,
+    typename T31 = internal::None, typename T32 = internal::None,
+    typename T33 = internal::None, typename T34 = internal::None,
+    typename T35 = internal::None, typename T36 = internal::None,
+    typename T37 = internal::None, typename T38 = internal::None,
+    typename T39 = internal::None, typename T40 = internal::None,
+    typename T41 = internal::None, typename T42 = internal::None,
+    typename T43 = internal::None, typename T44 = internal::None,
+    typename T45 = internal::None, typename T46 = internal::None,
+    typename T47 = internal::None, typename T48 = internal::None,
+    typename T49 = internal::None, typename T50 = internal::None>
+struct Types {
+  typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Types<internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types0 type;
+};
+template <typename T1>
+struct Types<T1, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types1<T1> type;
+};
+template <typename T1, typename T2>
+struct Types<T1, T2, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types2<T1, T2> type;
+};
+template <typename T1, typename T2, typename T3>
+struct Types<T1, T2, T3, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types3<T1, T2, T3> type;
+};
+template <typename T1, typename T2, typename T3, typename T4>
+struct Types<T1, T2, T3, T4, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types4<T1, T2, T3, T4> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+struct Types<T1, T2, T3, T4, T5, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types5<T1, T2, T3, T4, T5> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+struct Types<T1, T2, T3, T4, T5, T6, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types6<T1, T2, T3, T4, T5, T6> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+struct Types<T1, T2, T3, T4, T5, T6, T7, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
+      T12> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
+      T26> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
+      T40> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, internal::None,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None, internal::None> {
+  typedef internal::Types43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None, internal::None> {
+  typedef internal::Types44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+    internal::None, internal::None, internal::None, internal::None,
+    internal::None> {
+  typedef internal::Types45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+    T46, internal::None, internal::None, internal::None, internal::None> {
+  typedef internal::Types46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+    T46, T47, internal::None, internal::None, internal::None> {
+  typedef internal::Types47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46, T47> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+    T46, T47, T48, internal::None, internal::None> {
+  typedef internal::Types48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49>
+struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
+    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
+    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+    T46, T47, T48, T49, internal::None> {
+  typedef internal::Types49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+namespace internal {
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type.  TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>.  This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+  template <typename T>
+  struct Bind {
+    typedef Tmpl<T> type;
+  };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+  TmplSel::template Bind<T>::type
+
+// A unique struct template used as the default value for the
+// arguments of class template Templates.  This allows us to simulate
+// variadic templates (e.g. Templates<int>, Templates<int, double>,
+// and etc), which C++ doesn't support directly.
+template <typename T>
+struct NoneT {};
+
+// The following family of struct and struct templates are used to
+// represent template lists.  In particular, TemplatesN<T1, T2, ...,
+// TN> represents a list of N templates (T1, T2, ..., and TN).  Except
+// for Templates0, every struct in the family has two member types:
+// Head for the selector of the first template in the list, and Tail
+// for the rest of the list.
+
+// The empty template list.
+struct Templates0 {};
+
+// Template lists of length 1, 2, 3, and so on.
+
+template <GTEST_TEMPLATE_ T1>
+struct Templates1 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates0 Tail;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates2 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates1<T2> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates3 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates2<T2, T3> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4>
+struct Templates4 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates3<T2, T3, T4> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates5 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates4<T2, T3, T4, T5> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates6 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates5<T2, T3, T4, T5, T6> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7>
+struct Templates7 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates6<T2, T3, T4, T5, T6, T7> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates8 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates7<T2, T3, T4, T5, T6, T7, T8> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates9 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10>
+struct Templates10 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates11 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates12 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13>
+struct Templates13 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates14 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates15 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16>
+struct Templates16 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates17 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates18 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19>
+struct Templates19 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates20 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates21 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22>
+struct Templates22 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates23 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates24 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25>
+struct Templates25 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates26 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates27 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28>
+struct Templates28 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates29 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates30 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31>
+struct Templates31 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates32 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates33 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34>
+struct Templates34 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates35 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates36 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37>
+struct Templates37 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates38 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates39 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40>
+struct Templates40 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates41 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates42 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43>
+struct Templates43 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates44 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates45 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46>
+struct Templates46 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45, T46> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates47 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45, T46, T47> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates48 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45, T46, T47, T48> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+    GTEST_TEMPLATE_ T49>
+struct Templates49 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45, T46, T47, T48, T49> Tail;
+};
+
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+    GTEST_TEMPLATE_ T49, GTEST_TEMPLATE_ T50>
+struct Templates50 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
+      T43, T44, T45, T46, T47, T48, T49, T50> Tail;
+};
+
+
+// We don't want to require the users to write TemplatesN<...> directly,
+// as that would require them to count the length.  Templates<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Templates<list>
+// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Templates<T1, ..., TN>, and Google Test will translate
+// that to TemplatesN<T1, ..., TN> internally to make error messages
+// readable.  The translation is done by the 'type' member of the
+// Templates template.
+template <GTEST_TEMPLATE_ T1 = NoneT, GTEST_TEMPLATE_ T2 = NoneT,
+    GTEST_TEMPLATE_ T3 = NoneT, GTEST_TEMPLATE_ T4 = NoneT,
+    GTEST_TEMPLATE_ T5 = NoneT, GTEST_TEMPLATE_ T6 = NoneT,
+    GTEST_TEMPLATE_ T7 = NoneT, GTEST_TEMPLATE_ T8 = NoneT,
+    GTEST_TEMPLATE_ T9 = NoneT, GTEST_TEMPLATE_ T10 = NoneT,
+    GTEST_TEMPLATE_ T11 = NoneT, GTEST_TEMPLATE_ T12 = NoneT,
+    GTEST_TEMPLATE_ T13 = NoneT, GTEST_TEMPLATE_ T14 = NoneT,
+    GTEST_TEMPLATE_ T15 = NoneT, GTEST_TEMPLATE_ T16 = NoneT,
+    GTEST_TEMPLATE_ T17 = NoneT, GTEST_TEMPLATE_ T18 = NoneT,
+    GTEST_TEMPLATE_ T19 = NoneT, GTEST_TEMPLATE_ T20 = NoneT,
+    GTEST_TEMPLATE_ T21 = NoneT, GTEST_TEMPLATE_ T22 = NoneT,
+    GTEST_TEMPLATE_ T23 = NoneT, GTEST_TEMPLATE_ T24 = NoneT,
+    GTEST_TEMPLATE_ T25 = NoneT, GTEST_TEMPLATE_ T26 = NoneT,
+    GTEST_TEMPLATE_ T27 = NoneT, GTEST_TEMPLATE_ T28 = NoneT,
+    GTEST_TEMPLATE_ T29 = NoneT, GTEST_TEMPLATE_ T30 = NoneT,
+    GTEST_TEMPLATE_ T31 = NoneT, GTEST_TEMPLATE_ T32 = NoneT,
+    GTEST_TEMPLATE_ T33 = NoneT, GTEST_TEMPLATE_ T34 = NoneT,
+    GTEST_TEMPLATE_ T35 = NoneT, GTEST_TEMPLATE_ T36 = NoneT,
+    GTEST_TEMPLATE_ T37 = NoneT, GTEST_TEMPLATE_ T38 = NoneT,
+    GTEST_TEMPLATE_ T39 = NoneT, GTEST_TEMPLATE_ T40 = NoneT,
+    GTEST_TEMPLATE_ T41 = NoneT, GTEST_TEMPLATE_ T42 = NoneT,
+    GTEST_TEMPLATE_ T43 = NoneT, GTEST_TEMPLATE_ T44 = NoneT,
+    GTEST_TEMPLATE_ T45 = NoneT, GTEST_TEMPLATE_ T46 = NoneT,
+    GTEST_TEMPLATE_ T47 = NoneT, GTEST_TEMPLATE_ T48 = NoneT,
+    GTEST_TEMPLATE_ T49 = NoneT, GTEST_TEMPLATE_ T50 = NoneT>
+struct Templates {
+  typedef Templates50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+};
+
+template <>
+struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT> {
+  typedef Templates0 type;
+};
+template <GTEST_TEMPLATE_ T1>
+struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT> {
+  typedef Templates1<T1> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
+struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT> {
+  typedef Templates2<T1, T2> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
+struct Templates<T1, T2, T3, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates3<T1, T2, T3> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4>
+struct Templates<T1, T2, T3, T4, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates4<T1, T2, T3, T4> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
+struct Templates<T1, T2, T3, T4, T5, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates5<T1, T2, T3, T4, T5> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
+struct Templates<T1, T2, T3, T4, T5, T6, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates6<T1, T2, T3, T4, T5, T6> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates7<T1, T2, T3, T4, T5, T6, T7> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates8<T1, T2, T3, T4, T5, T6, T7, T8> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT> {
+  typedef Templates22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT> {
+  typedef Templates23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT> {
+  typedef Templates24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT> {
+  typedef Templates25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT> {
+  typedef Templates26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT> {
+  typedef Templates27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT> {
+  typedef Templates28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT> {
+  typedef Templates29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, NoneT, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, NoneT, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, NoneT, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, NoneT, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, NoneT,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    T45, NoneT, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    T45, T46, NoneT, NoneT, NoneT, NoneT> {
+  typedef Templates46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45, T46> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    T45, T46, T47, NoneT, NoneT, NoneT> {
+  typedef Templates47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45, T46, T47> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    T45, T46, T47, T48, NoneT, NoneT> {
+  typedef Templates48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45, T46, T47, T48> type;
+};
+template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
+    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
+    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
+    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
+    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
+    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
+    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
+    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
+    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
+    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
+    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
+    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
+    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
+    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
+    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
+    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
+    GTEST_TEMPLATE_ T49>
+struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
+    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
+    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
+    T45, T46, T47, T48, T49, NoneT> {
+  typedef Templates49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
+      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
+      T42, T43, T44, T45, T46, T47, T48, T49> type;
+};
+
+// The TypeList template makes it possible to use either a single type
+// or a Types<...> list in TYPED_TEST_CASE() and
+// INSTANTIATE_TYPED_TEST_CASE_P().
+
+template <typename T>
+struct TypeList {
+  typedef Types1<T> type;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10,
+    typename T11, typename T12, typename T13, typename T14, typename T15,
+    typename T16, typename T17, typename T18, typename T19, typename T20,
+    typename T21, typename T22, typename T23, typename T24, typename T25,
+    typename T26, typename T27, typename T28, typename T29, typename T30,
+    typename T31, typename T32, typename T33, typename T34, typename T35,
+    typename T36, typename T37, typename T38, typename T39, typename T40,
+    typename T41, typename T42, typename T43, typename T44, typename T45,
+    typename T46, typename T47, typename T48, typename T49, typename T50>
+struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
+    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
+    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
+    T44, T45, T46, T47, T48, T49, T50> > {
+  typedef typename Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
+      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
+      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
+      T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type;
+};
+
+#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h.pump b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h.pump
new file mode 100644
index 0000000..251fdf0
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/include/gtest/internal/gtest-type-util.h.pump
@@ -0,0 +1,297 @@
+$$ -*- mode: c++; -*-
+$var n = 50  $$ Maximum length of type lists we want to support.
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: wan at google.com (Zhanyong Wan)
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests.  This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
+//
+// Currently we support at most $n types in a list, and at most $n
+// type-parameterized tests in one type-parameterized test case.
+// Please contact googletestframework at googlegroups.com if you need
+// more.
+
+#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here.  It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+#  include <cxxabi.h>
+# elif defined(__HP_aCC)
+#  include <acxx_demangle.h>
+# endif  // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// GetTypeName<T>() returns a human-readable name of type T.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+# if GTEST_HAS_RTTI
+
+  const char* const name = typeid(T).name();
+#  if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+  int status = 0;
+  // gcc's implementation of typeid(T).name() mangles the type name,
+  // so we have to demangle it.
+#   if GTEST_HAS_CXXABI_H_
+  using abi::__cxa_demangle;
+#   endif  // GTEST_HAS_CXXABI_H_
+  char* const readable_name = __cxa_demangle(name, 0, 0, &status);
+  const std::string name_str(status == 0 ? readable_name : name);
+  free(readable_name);
+  return name_str;
+#  else
+  return name;
+#  endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
+
+# else
+
+  return "<type>";
+
+# endif  // GTEST_HAS_RTTI
+}
+
+#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same
+// type.  This can be used as a compile-time assertion to ensure that
+// two types are equal.
+
+template <typename T1, typename T2>
+struct AssertTypeEq;
+
+template <typename T>
+struct AssertTypeEq<T, T> {
+  typedef bool type;
+};
+
+// A unique type used as the default value for the arguments of class
+// template Types.  This allows us to simulate variadic templates
+// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
+// support directly.
+struct None {};
+
+// The following family of struct and struct templates are used to
+// represent type lists.  In particular, TypesN<T1, T2, ..., TN>
+// represents a type list with N types (T1, T2, ..., and TN) in it.
+// Except for Types0, every struct in the family has two member types:
+// Head for the first type in the list, and Tail for the rest of the
+// list.
+
+// The empty type list.
+struct Types0 {};
+
+// Type lists of length 1, 2, 3, and so on.
+
+template <typename T1>
+struct Types1 {
+  typedef T1 Head;
+  typedef Types0 Tail;
+};
+
+$range i 2..n
+
+$for i [[
+$range j 1..i
+$range k 2..i
+template <$for j, [[typename T$j]]>
+struct Types$i {
+  typedef T1 Head;
+  typedef Types$(i-1)<$for k, [[T$k]]> Tail;
+};
+
+
+]]
+
+}  // namespace internal
+
+// We don't want to require the users to write TypesN<...> directly,
+// as that would require them to count the length.  Types<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Types<int>
+// will appear as Types<int, None, None, ..., None> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Types<T1, ..., TN>, and Google Test will translate
+// that to TypesN<T1, ..., TN> internally to make error messages
+// readable.  The translation is done by the 'type' member of the
+// Types template.
+
+$range i 1..n
+template <$for i, [[typename T$i = internal::None]]>
+struct Types {
+  typedef internal::Types$n<$for i, [[T$i]]> type;
+};
+
+template <>
+struct Types<$for i, [[internal::None]]> {
+  typedef internal::Types0 type;
+};
+
+$range i 1..n-1
+$for i [[
+$range j 1..i
+$range k i+1..n
+template <$for j, [[typename T$j]]>
+struct Types<$for j, [[T$j]]$for k[[, internal::None]]> {
+  typedef internal::Types$i<$for j, [[T$j]]> type;
+};
+
+]]
+
+namespace internal {
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type.  TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>.  This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+  template <typename T>
+  struct Bind {
+    typedef Tmpl<T> type;
+  };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+  TmplSel::template Bind<T>::type
+
+// A unique struct template used as the default value for the
+// arguments of class template Templates.  This allows us to simulate
+// variadic templates (e.g. Templates<int>, Templates<int, double>,
+// and etc), which C++ doesn't support directly.
+template <typename T>
+struct NoneT {};
+
+// The following family of struct and struct templates are used to
+// represent template lists.  In particular, TemplatesN<T1, T2, ...,
+// TN> represents a list of N templates (T1, T2, ..., and TN).  Except
+// for Templates0, every struct in the family has two member types:
+// Head for the selector of the first template in the list, and Tail
+// for the rest of the list.
+
+// The empty template list.
+struct Templates0 {};
+
+// Template lists of length 1, 2, 3, and so on.
+
+template <GTEST_TEMPLATE_ T1>
+struct Templates1 {
+  typedef TemplateSel<T1> Head;
+  typedef Templates0 Tail;
+};
+
+$range i 2..n
+
+$for i [[
+$range j 1..i
+$range k 2..i
+template <$for j, [[GTEST_TEMPLATE_ T$j]]>
+struct Templates$i {
+  typedef TemplateSel<T1> Head;
+  typedef Templates$(i-1)<$for k, [[T$k]]> Tail;
+};
+
+
+]]
+
+// We don't want to require the users to write TemplatesN<...> directly,
+// as that would require them to count the length.  Templates<...> is much
+// easier to write, but generates horrible messages when there is a
+// compiler error, as gcc insists on printing out each template
+// argument, even if it has the default value (this means Templates<list>
+// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
+// errors).
+//
+// Our solution is to combine the best part of the two approaches: a
+// user would write Templates<T1, ..., TN>, and Google Test will translate
+// that to TemplatesN<T1, ..., TN> internally to make error messages
+// readable.  The translation is done by the 'type' member of the
+// Templates template.
+
+$range i 1..n
+template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]>
+struct Templates {
+  typedef Templates$n<$for i, [[T$i]]> type;
+};
+
+template <>
+struct Templates<$for i, [[NoneT]]> {
+  typedef Templates0 type;
+};
+
+$range i 1..n-1
+$for i [[
+$range j 1..i
+$range k i+1..n
+template <$for j, [[GTEST_TEMPLATE_ T$j]]>
+struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> {
+  typedef Templates$i<$for j, [[T$j]]> type;
+};
+
+]]
+
+// The TypeList template makes it possible to use either a single type
+// or a Types<...> list in TYPED_TEST_CASE() and
+// INSTANTIATE_TYPED_TEST_CASE_P().
+
+template <typename T>
+struct TypeList {
+  typedef Types1<T> type;
+};
+
+
+$range i 1..n
+template <$for i, [[typename T$i]]>
+struct TypeList<Types<$for i, [[T$i]]> > {
+  typedef typename Types<$for i, [[T$i]]>::type type;
+};
+
+#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/cygwin/gtest.lib b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/cygwin/gtest.lib
new file mode 100644
index 0000000..000a761
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/cygwin/gtest.lib differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/linux/libgtest.a b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/linux/libgtest.a
new file mode 100644
index 0000000..db3290a
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/linux/libgtest.a differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/mingw/gtest.lib b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/mingw/gtest.lib
new file mode 100644
index 0000000..d530506
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/mingw/gtest.lib differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/osx/libgtest.a b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/osx/libgtest.a
new file mode 100644
index 0000000..4863a52
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/osx/libgtest.a differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2010/gtest.lib b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2010/gtest.lib
new file mode 100644
index 0000000..5dd1c48
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2010/gtest.lib differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2013/gtest.lib b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2013/gtest.lib
new file mode 100644
index 0000000..691835d
Binary files /dev/null and b/subprojects/docs/src/samples/native-binaries/google-test/libs/googleTest/1.7.0/lib/vs2013/gtest.lib differ
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/minus.cpp b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/minus.cpp
new file mode 100644
index 0000000..01fb74b
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/minus.cpp
@@ -0,0 +1,5 @@
+#include "operators.h"
+
+int minus(int a, int b) {
+    return a - b;
+}
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/plus.cpp b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/plus.cpp
new file mode 100644
index 0000000..1a2f3d6
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/cpp/plus.cpp
@@ -0,0 +1,9 @@
+#include "operators.h"
+
+int plus(int a, int b) {
+#ifdef PLUS_BROKEN
+    return 2;
+#else
+    return a + b;
+#endif
+}
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operators/headers/operators.h b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/headers/operators.h
new file mode 100644
index 0000000..d27b808
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operators/headers/operators.h
@@ -0,0 +1,2 @@
+int plus(int a, int b);
+int minus(int a, int b);
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_main.cpp b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_main.cpp
new file mode 100644
index 0000000..df510e4
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_main.cpp
@@ -0,0 +1,8 @@
+#include "gtest/gtest.h"
+
+using namespace testing;
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_minus.cpp b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_minus.cpp
new file mode 100644
index 0000000..7c117d3
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_minus.cpp
@@ -0,0 +1,10 @@
+#include "gtest/gtest.h"
+#include "operators.h"
+
+using namespace testing;
+
+TEST(OperatorTests, test_minus) {
+  ASSERT_TRUE(minus(2, 0) == 2);
+  ASSERT_TRUE(minus(0, -2) == 2);
+  ASSERT_TRUE(minus(2, 2) == 0);
+}
diff --git a/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_plus.cpp b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_plus.cpp
new file mode 100644
index 0000000..1977db6
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/google-test/src/operatorsTest/cpp/test_plus.cpp
@@ -0,0 +1,10 @@
+#include "gtest/gtest.h"
+#include "operators.h"
+
+using namespace testing;
+
+TEST(OperatorTests, test_plus) {
+  ASSERT_TRUE(plus(0, 2) == 2);
+  ASSERT_TRUE(plus(0, -2) == -2);
+  ASSERT_TRUE(plus(2, 2) == 4);
+}
diff --git a/subprojects/docs/src/samples/native-binaries/idl/build.gradle b/subprojects/docs/src/samples/native-binaries/idl/build.gradle
index 53e9260..a009ed4 100644
--- a/subprojects/docs/src/samples/native-binaries/idl/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/idl/build.gradle
@@ -6,18 +6,20 @@ task idl(type: DummyIdlCompiler) {
     sourceDir = project.file("${buildDir}/src/generated/c")
 }
 
-sources {
-    main {
-        idlOutput(CSourceSet) {
-            generatedBy tasks.idl
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                idlOutput(CSourceSet) {
+                    generatedBy tasks.idl
+                }
+                c.lib sources.idlOutput
+            }
         }
     }
 }
-sources.main.c.lib sources.main.idlOutput
 
-executables {
-    main {}
-}
 
 class DummyIdlCompiler extends DefaultTask {
     @InputFiles FileCollection idlFiles
diff --git a/subprojects/docs/src/samples/native-binaries/multi-project/build.gradle b/subprojects/docs/src/samples/native-binaries/multi-project/build.gradle
index 2a3964c..d560403 100644
--- a/subprojects/docs/src/samples/native-binaries/multi-project/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/multi-project/build.gradle
@@ -1,22 +1,31 @@
 // START SNIPPET project-dependencies
 project(":lib") {
     apply plugin: "cpp"
-    libraries {
-        main {}
+    model {
+        components {
+            main(NativeLibrarySpec)
+        }
+    }
+    // For any shared library binaries built with Visual C++,
+    // define the DLL_EXPORT macro
+    binaries.withType(SharedLibraryBinarySpec) {
+        if (toolChain in VisualCpp) {
+            cppCompiler.define "DLL_EXPORT"
+        }
     }
 }
 
 project(":exe") {
     apply plugin: "cpp"
 
-    executables {
-        main {}
-    }
-
-    sources {
-        main {
-            cpp {
-                lib project: ':lib', library: 'main'
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp {
+                        lib project: ':lib', library: 'main'
+                    }
+                }
             }
         }
     }
diff --git a/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/cpp/hello.cpp b/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/cpp/hello.cpp
index 5d79bce..ae2e337 100755
--- a/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/cpp/hello.cpp
+++ b/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/cpp/hello.cpp
@@ -1,9 +1,5 @@
 #include <iostream>
-#ifdef _WIN32
-#define LIB_FUNC __declspec(dllexport)
-#else
-#define LIB_FUNC
-#endif
+#include "hello.h"
 
 void LIB_FUNC hello () {
   std::cout << "Hello, World!\n";
diff --git a/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/headers/hello.h b/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/headers/hello.h
index 1d3f35d..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/multi-project/lib/src/main/headers/hello.h
@@ -1,5 +1,5 @@
-#ifdef _WIN32
-#define LIB_FUNC __declspec(dllimport)
+#if defined(_WIN32) && defined(DLL_EXPORT)
+#define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
 #endif
diff --git a/subprojects/docs/src/samples/native-binaries/objective-c/build.gradle b/subprojects/docs/src/samples/native-binaries/objective-c/build.gradle
index 51d7e9b..7e0dbd3 100644
--- a/subprojects/docs/src/samples/native-binaries/objective-c/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/objective-c/build.gradle
@@ -3,20 +3,20 @@ apply plugin: 'objective-c'
 // END SNIPPET apply-plugin
 
 // START SNIPPET executables
-executables {
-    main {}
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
 }
 // END SNIPPET executables
 
-
 // START SNIPPET all-binaries
 binaries.all {
-    objcCompiler.args "-I/usr/include/GNUstep", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-
-    //on osx we need different settings than on linux or windows
-    if(targetPlatform.operatingSystem.isMacOsX()){
+    //on OS X we need different settings than on Linux or Windows
+    if (targetPlatform.operatingSystem.macOsX) {
         linker.args "-framework", "Foundation"
-    }else{
+    } else {
+        objcCompiler.args "-I/usr/include/GNUstep", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
         linker.args "-lgnustep-base", "-lobjc"
     }
 }
diff --git a/subprojects/docs/src/samples/native-binaries/objective-c/src/main/headers/greeter.h b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/headers/greeter.h
new file mode 100644
index 0000000..37e2aec
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/headers/greeter.h
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+
+ at interface Greeter : NSObject
+    - (void)sayHello;
+ at end
diff --git a/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/greeter.m b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/greeter.m
new file mode 100644
index 0000000..edc9be1
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/greeter.m
@@ -0,0 +1,10 @@
+#import "greeter.h"
+
+ at implementation Greeter
+
+- (void) sayHello {
+    NSString *helloWorld = @"Hello world!";
+    fprintf(stdout, "%s\n", [helloWorld UTF8String]);
+}
+
+ at end
diff --git a/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/main.m b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/main.m
index 18d5703..20f0605 100644
--- a/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/main.m
+++ b/subprojects/docs/src/samples/native-binaries/objective-c/src/main/objc/main.m
@@ -1,10 +1,14 @@
-#import <Foundation/Foundation.h>
+#import "greeter.h"
 
 int main (int argc, const char * argv[])
 {
-    NSString *helloWorld = @"Hello world!\n";
-    NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-    NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
-    [stdout writeData: strData];
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    Greeter *greeter = [Greeter new];
+    [greeter sayHello];
+    [greeter release];
+
+    [pool drain];
+
     return 0;
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/native-binaries/objective-cpp/build.gradle b/subprojects/docs/src/samples/native-binaries/objective-cpp/build.gradle
index 16a73de..641fb92 100644
--- a/subprojects/docs/src/samples/native-binaries/objective-cpp/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/objective-cpp/build.gradle
@@ -3,20 +3,20 @@ apply plugin: 'objective-cpp'
 // END SNIPPET apply-plugin
 
 // START SNIPPET executables
-executables {
-    main {}
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
 }
 // END SNIPPET executables
 
-
 // START SNIPPET all-binaries
 binaries.all {
-    objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
-
-    //on osx we need different linker settings than on linux or windows
-    if(targetPlatform.operatingSystem.isMacOsX()){
+    //on OS X we need different linker settings than on Linux or Windows
+    if (targetPlatform.operatingSystem.macOsX) {
         linker.args "-framework", "Foundation"
-    }else{
+    } else {
+        objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
         linker.args "-lgnustep-base", "-lobjc"
     }
 }
diff --git a/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/headers/greeter.h b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/headers/greeter.h
new file mode 100644
index 0000000..2326522
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/headers/greeter.h
@@ -0,0 +1,7 @@
+#define __STDC_LIMIT_MACROS
+#import <stdint.h>
+#import <Foundation/Foundation.h>
+
+ at interface Greeter : NSObject
+    - (void)sayHello;
+ at end
diff --git a/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/greeter.mm b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/greeter.mm
new file mode 100644
index 0000000..661fcda
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/greeter.mm
@@ -0,0 +1,11 @@
+#import <iostream>
+#import "greeter.h"
+
+ at implementation Greeter
+
+- (void) sayHello {
+    NSString *helloWorld = @"Hello world!";
+    std::cout << [helloWorld UTF8String] << std::endl;
+}
+
+ at end
diff --git a/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/main.mm b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/main.mm
index 1c97154..20f0605 100644
--- a/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/main.mm
+++ b/subprojects/docs/src/samples/native-binaries/objective-cpp/src/main/objcpp/main.mm
@@ -1,14 +1,14 @@
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
-#import <Foundation/Foundation.h>
-#include <iostream>
+#import "greeter.h"
 
 int main (int argc, const char * argv[])
 {
-    NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
-    NSData *strData = [@"Hello " dataUsingEncoding: NSASCIIStringEncoding];
-    [stdout writeData: strData];
-    std::cout << "world!" << std::endl;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    Greeter *greeter = [Greeter new];
+    [greeter sayHello];
+    [greeter release];
+
+    [pool drain];
 
     return 0;
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/native-binaries/prebuilt/3rd-party-lib/util/build.gradle b/subprojects/docs/src/samples/native-binaries/prebuilt/3rd-party-lib/util/build.gradle
index 2e6a47b..dda13b1 100644
--- a/subprojects/docs/src/samples/native-binaries/prebuilt/3rd-party-lib/util/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/prebuilt/3rd-party-lib/util/build.gradle
@@ -4,22 +4,19 @@ model {
         debug
         release
     }
-}
-libraries {
-    util {
-        binaries.all {
-            if (buildType == buildTypes.debug) {
-                cppCompiler.define 'DEBUG'
-                if (toolChain in VisualCpp) {
-                    cppCompiler.args '/Zi'
-                    linker.args '/DEBUG'
-                } else {
-                    cppCompiler.args "-g"
+    components {
+        util(NativeLibrarySpec) {
+            binaries.all {
+                if (buildType == buildTypes.debug) {
+                    cppCompiler.define 'DEBUG'
+                    if (toolChain in VisualCpp) {
+                        cppCompiler.args '/Zi'
+                        linker.args '/DEBUG'
+                    } else {
+                        cppCompiler.args "-g"
+                    }
                 }
             }
         }
     }
 }
-task buildLibraries {
-    dependsOn binaries.withType(LibraryBinary)
-}
diff --git a/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle b/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle
index 6d274cf..124da8d 100644
--- a/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle
@@ -33,13 +33,14 @@ model {
             }
         }
     }
-}
-executables {
-    main {}
-}
-sources.main.cpp.lib library: 'boost', linkage: 'api'
-sources.main.cpp.lib library: 'util', linkage: 'static'
-task buildExecutables {
-    dependsOn binaries.withType(ExecutableBinary)
+
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'boost', linkage: 'api'
+                cpp.lib library: 'util', linkage: 'static'
+            }
+        }
+    }
 }
 
diff --git a/subprojects/docs/src/samples/native-binaries/target-platforms/build.gradle b/subprojects/docs/src/samples/native-binaries/target-platforms/build.gradle
new file mode 100644
index 0000000..860a353
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/target-platforms/build.gradle
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'cpp'
+
+// START SNIPPET targetplatforms
+model {
+    toolChains {
+        gcc(Gcc) {
+            target("arm"){
+                cppCompiler.withArguments { args ->
+                    args << "-m32"
+                }
+                linker.withArguments { args ->
+                    args << "-m32"
+                }
+            }
+            target("sparc")
+        }
+    }
+    platforms {
+        arm {
+            architecture "arm"
+        }
+        sparc {
+            architecture "sparc"
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "arm"
+            targetPlatform "sparc"
+        }
+    }
+}
+// END SNIPPET targetplatforms
diff --git a/subprojects/docs/src/samples/native-binaries/target-platforms/src/main/cpp/main.cpp b/subprojects/docs/src/samples/native-binaries/target-platforms/src/main/cpp/main.cpp
new file mode 100644
index 0000000..d0e685c
--- /dev/null
+++ b/subprojects/docs/src/samples/native-binaries/target-platforms/src/main/cpp/main.cpp
@@ -0,0 +1,18 @@
+#include <iostream>
+
+int main () {
+    #if defined(__clang__)
+        std::cout << "Hello from clang!" << std::endl;
+    #elif defined(__GNUC__) && defined(__MINGW32__)
+        std::cout << "Hello from mingw!" << std::endl;
+    #elif defined(__GNUC__) && defined(__CYGWIN__)
+        std::cout << "Hello from gcc cygwin!" << std::endl;
+    #elif defined(__GNUC__)
+        std::cout << "Hello from gcc!" << std::endl;
+    #elif defined(_MSC_VER)
+        std::cout << "Hello from visual c++!" << std::endl;
+    #else
+        std::cout << "Hello from an unrecognised tool chain!" << std::endl;
+    #endif
+  return 0;
+}
diff --git a/subprojects/docs/src/samples/native-binaries/tool-chains/build.gradle b/subprojects/docs/src/samples/native-binaries/tool-chains/build.gradle
index 00e10fe..7674adf 100644
--- a/subprojects/docs/src/samples/native-binaries/tool-chains/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/tool-chains/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'cpp'
 model {
     toolChains {
         visualCpp(VisualCpp) {
-            // Specify the installDir if Visual Studio cannot be located by default
+            // Specify the installDir if Visual Studio cannot be located
             // installDir "C:/Apps/Microsoft Visual Studio 10.0"
         }
         gcc(Gcc) {
@@ -16,13 +16,43 @@ model {
 }
 // END SNIPPET toolChains
 
-executables {
-    main {}
+
+// START SNIPPET withArguments
+model {
+    toolChains {
+        visualCpp(VisualCpp) {
+            eachPlatform {
+                cppCompiler.withArguments { args ->
+                    args << "-DFRENCH"
+                }
+            }
+        }
+        clang(Clang) {
+            eachPlatform {
+                cCompiler.withArguments { args ->
+                    Collections.replaceAll(args, "CUSTOM", "-DFRENCH")
+                }
+                linker.withArguments { args ->
+                    args.remove "CUSTOM"
+                }
+                staticLibArchiver.withArguments { args ->
+                    args.remove "CUSTOM"
+                }
+            }
+        }
+    }
+}
+// END SNIPPET withArguments
+
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
 }
 
 // START SNIPPET buildable
 task buildAllExecutables {
-    dependsOn binaries.withType(ExecutableBinary).matching {
+    dependsOn binaries.withType(NativeExecutableBinary).matching {
         it.buildable
     }
 }
diff --git a/subprojects/docs/src/samples/native-binaries/variants/build.gradle b/subprojects/docs/src/samples/native-binaries/variants/build.gradle
index 96508a0..47f155c 100644
--- a/subprojects/docs/src/samples/native-binaries/variants/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/variants/build.gradle
@@ -26,16 +26,22 @@ model {
 // END SNIPPET build-types
 
 // START SNIPPET target-platforms
-executables {
-    main {
-        targetPlatforms "x86", "x64"
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+            targetPlatform "x64"
+        }
+        main(NativeExecutableSpec) {
+            targetPlatform "x86"
+            targetPlatform "x64"
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
     }
 }
 // END SNIPPET target-platforms
-libraries {
-    hello {}
-}
-sources.main.cpp.lib libraries.hello.static
 
 // Apply arguments for debug binaries (these options are not yet set automatically)
 // START SNIPPET build-type-config
@@ -60,17 +66,12 @@ binaries.all {
 
 // Tasks to build all binaries for a tool chain
 task gccExecutables {
-    dependsOn binaries.withType(ExecutableBinary).matching {
+    dependsOn binaries.withType(NativeExecutableBinary).matching {
         it.toolChain in Gcc
     }
 }
 task visualCppExecutables {
-    dependsOn binaries.withType(ExecutableBinary).matching {
+    dependsOn binaries.withType(NativeExecutableBinary).matching {
         it.toolChain in VisualCpp
     }
 }
-task buildExecutables {
-    dependsOn binaries.withType(ExecutableBinary).matching {
-        it.buildable
-    }
-}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/native-binaries/variants/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/variants/src/hello/headers/hello.h
index 79b608b..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/variants/src/hello/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/variants/src/hello/headers/hello.h
@@ -1,9 +1,6 @@
-#ifdef _WIN32
-#ifdef DLL_EXPORT
+#if defined(_WIN32) && defined(DLL_EXPORT)
 #define LIB_FUNC __declspec(dllexport)
-#endif
-#endif
-#ifndef LIB_FUNC
+#else
 #define LIB_FUNC
 #endif
 
diff --git a/subprojects/docs/src/samples/native-binaries/visual-studio/build.gradle b/subprojects/docs/src/samples/native-binaries/visual-studio/build.gradle
index 38f7465..f17ef53 100644
--- a/subprojects/docs/src/samples/native-binaries/visual-studio/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/visual-studio/build.gradle
@@ -53,16 +53,19 @@ model {
 }
 // END SNIPPET customize-solution-files
 
-executables {
-    main {}
-}
-libraries {
-    hello {}
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello"
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
 }
-sources.main.cpp.lib libraries.hello
 
 // For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
-binaries.withType(SharedLibraryBinary) {
+binaries.withType(SharedLibraryBinarySpec) {
     if (toolChain in VisualCpp) {
         cppCompiler.define "DLL_EXPORT"
     }
diff --git a/subprojects/docs/src/samples/native-binaries/visual-studio/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/visual-studio/src/hello/headers/hello.h
index 51b48fd..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/visual-studio/src/hello/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/visual-studio/src/hello/headers/hello.h
@@ -1,4 +1,4 @@
-#ifdef DLL_EXPORT
+#if defined(_WIN32) && defined(DLL_EXPORT)
 #define LIB_FUNC __declspec(dllexport)
 #else
 #define LIB_FUNC
diff --git a/subprojects/docs/src/samples/native-binaries/windows-resources/build-resource-only-dll.gradle b/subprojects/docs/src/samples/native-binaries/windows-resources/build-resource-only-dll.gradle
index 33a820c..db6c606 100644
--- a/subprojects/docs/src/samples/native-binaries/windows-resources/build-resource-only-dll.gradle
+++ b/subprojects/docs/src/samples/native-binaries/windows-resources/build-resource-only-dll.gradle
@@ -1,27 +1,27 @@
 apply plugin: 'windows-resources'
 
 // START SNIPPET resource-only-library
-libraries {
-    helloRes {
-        binaries.all {
-            rcCompiler.args "/v"
-            linker.args "/noentry", "/machine:x86"
-        }
-    }
-}
-// END SNIPPET resource-only-library
-
-// START SNIPPET windows-resource-set
-sources {
-    helloRes {
-        rc {
-            source {
-                srcDirs "src/hello/rc"
+model {
+    components {
+        helloRes(NativeLibrarySpec) {
+            binaries.all {
+                rcCompiler.args "/v"
+                linker.args "/noentry", "/machine:x86"
             }
-            exportedHeaders {
-                srcDirs "src/hello/headers"
+            // START SNIPPET windows-resource-set
+            sources {
+                rc {
+                    source {
+                        srcDirs "src/hello/rc"
+                    }
+                    exportedHeaders {
+                        srcDirs "src/hello/headers"
+                    }
+                }
             }
+            // END SNIPPET windows-resource-set
         }
     }
 }
-// END SNIPPET windows-resource-set
\ No newline at end of file
+// END SNIPPET resource-only-library
+
diff --git a/subprojects/docs/src/samples/native-binaries/windows-resources/build.gradle b/subprojects/docs/src/samples/native-binaries/windows-resources/build.gradle
index 3a44053..23062bd 100644
--- a/subprojects/docs/src/samples/native-binaries/windows-resources/build.gradle
+++ b/subprojects/docs/src/samples/native-binaries/windows-resources/build.gradle
@@ -3,18 +3,15 @@ apply plugin: 'cpp'
 apply plugin: 'windows-resources'
 // END SNIPPET apply-plugin
 
-libraries {
-    hello {}
-}
-
-executables {
-    main {}
-}
-
-sources {
-    main {
-        cpp {
-            lib libraries.hello
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    lib library: 'hello'
+                }
+            }
         }
     }
 }
@@ -24,7 +21,7 @@ binaries.all {
 }
 
 // For any shared library binaries built with Visual C++, define the DLL_EXPORT macro
-binaries.withType(SharedLibraryBinary) {
+binaries.withType(SharedLibraryBinarySpec) {
     if (toolChain in VisualCpp) {
         cppCompiler.define "DLL_EXPORT"
     }
diff --git a/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/cpp/hello.cpp b/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/cpp/hello.cpp
index 6c2f01e..b95de70 100755
--- a/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/cpp/hello.cpp
+++ b/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/cpp/hello.cpp
@@ -13,7 +13,7 @@ std::string LoadStringFromResource(UINT stringID)
     return std::string(wide.begin(), wide.end());
 }
 
-void DLL_FUNC hello() {
+void hello() {
     std::string hello = LoadStringFromResource(IDS_HELLO);
     std::cout << hello << std::endl;
 }
diff --git a/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/headers/hello.h b/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/headers/hello.h
index e6507ad..da8bebf 100755
--- a/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/headers/hello.h
+++ b/subprojects/docs/src/samples/native-binaries/windows-resources/src/hello/headers/hello.h
@@ -1,7 +1,7 @@
-#ifdef DLL_EXPORT
-#define DLL_FUNC __declspec(dllexport)
+#if defined(_WIN32) && defined(DLL_EXPORT)
+#define LIB_FUNC __declspec(dllexport)
 #else
-#define DLL_FUNC
+#define LIB_FUNC
 #endif
 
-void DLL_FUNC hello();
+void LIB_FUNC hello();
diff --git a/subprojects/docs/src/samples/osgi/build.gradle b/subprojects/docs/src/samples/osgi/build.gradle
index 16c7cec..c8a49ed 100644
--- a/subprojects/docs/src/samples/osgi/build.gradle
+++ b/subprojects/docs/src/samples/osgi/build.gradle
@@ -14,7 +14,7 @@ repositories {
 }
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.2.0'
+    compile 'org.codehaus.groovy:groovy:2.3.10'
     compile 'org.eclipse:osgi:3.5.0.v20090520'
 }
 
diff --git a/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/console.coffee b/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/console.coffee
new file mode 100644
index 0000000..3f93761
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/console.coffee
@@ -0,0 +1 @@
+console.log "This is coffeescript!"
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/squareit.coffee b/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/squareit.coffee
new file mode 100644
index 0000000..3fa7fc8
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/assets/coffeescript/squareit.coffee
@@ -0,0 +1,7 @@
+square = (x) -> x * x
+
+$ ->
+    $('#number').val(2)
+    $('#squareit').click ->
+        console.log("squared!")
+        $('#number').val(square($('#number').val()))
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/assets/javascripts/sample.js b/subprojects/docs/src/samples/play/advanced/app/assets/javascripts/sample.js
new file mode 100644
index 0000000..2e70c10
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/assets/javascripts/sample.js
@@ -0,0 +1,9 @@
+var cubes = (function() {
+    var _i, _len, _results;
+    _results = [];
+    for (_i = 0, _len = list.length; _i < _len; _i++) {
+        num = list[_i];
+        _results.push(math.cube(num));
+    }
+    return _results;
+})();
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/controllers/Application.scala b/subprojects/docs/src/samples/play/advanced/app/controllers/Application.scala
new file mode 100644
index 0000000..f44dc62
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/controllers/Application.scala
@@ -0,0 +1,16 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok(views.html.index("Your new application is ready."))
+  }
+
+  def square = Action {
+    Ok(views.html.square("Square it!"))
+  }
+
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/controllers/QuestionsThreeController.scala b/subprojects/docs/src/samples/play/advanced/app/controllers/QuestionsThreeController.scala
new file mode 100644
index 0000000..20b9753
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/controllers/QuestionsThreeController.scala
@@ -0,0 +1,34 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+import play.api.data._
+import play.api.data.Forms._
+import models.Person
+
+object QuestionsThreeController extends Controller {
+    val personForm = Form(
+        mapping(
+            "name" -> nonEmptyText,
+            "quest" -> nonEmptyText,
+            "favoriteColor" -> text
+        )(Person.apply)(Person.unapply)
+    )
+
+    def submit = Action { implicit request =>
+        personForm.bindFromRequest.fold(
+            formWithErrors => {
+                // binding failure, you retrieve the form containing errors:
+                BadRequest(views.html.person(formWithErrors))
+            },
+            person => {
+                Ok(views.html.pass())
+            }
+        )
+    }
+
+    def index = Action {
+        Ok(views.html.person(personForm))
+    }
+}
+
diff --git a/subprojects/docs/src/samples/play/advanced/app/controllers/hello/HelloController.java b/subprojects/docs/src/samples/play/advanced/app/controllers/hello/HelloController.java
new file mode 100644
index 0000000..a3772a3
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/controllers/hello/HelloController.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers.hello;
+
+import play.*;
+import play.mvc.*;
+import views.html.*;
+
+import org.apache.commons.lang.StringUtils;
+
+public class HelloController extends Controller {
+
+    public static Result index(String name) {
+        return ok(String.format("Hello %s!", StringUtils.capitalize(name)));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/models/Person.scala b/subprojects/docs/src/samples/play/advanced/app/models/Person.scala
new file mode 100644
index 0000000..17b4152
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/models/Person.scala
@@ -0,0 +1,7 @@
+package models
+
+case class Person(
+    firstName: String,
+    quest: String,
+    favoriteColor: String
+)
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/fields.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/fields.scala.html
new file mode 100644
index 0000000..49fee3e
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/fields.scala.html
@@ -0,0 +1,9 @@
+@(elements: helper.FieldElements)
+
+<div class="@if(elements.hasErrors) {error}">
+    <label for="@elements.id">@elements.label(elements.lang)</label>
+    <span class="text-error">@elements.errors(elements.lang).mkString(", ")</span>
+    <div class="input">
+        @elements.input
+    </div>
+</div>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/index.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/index.scala.html
new file mode 100644
index 0000000..d6a6b22
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/index.scala.html
@@ -0,0 +1,7 @@
+@(message: String)
+
+ at main("Welcome to Play") {
+
+    @play20.welcome(message)
+
+}
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/main.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/main.scala.html
new file mode 100644
index 0000000..d4c8d76
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/main.scala.html
@@ -0,0 +1,16 @@
+@(title: String)(content: Html)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
+        <script src="@routes.Assets.at("coffeescript/console.js")" type="text/javascript"></script>
+    </head>
+    <body>
+        @content
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/pass.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/pass.scala.html
new file mode 100644
index 0000000..a87a61b
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/pass.scala.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+    <title>Success</title>
+    <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/bootstrap.min.css")">
+    <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+</head>
+<body>
+<div class="text-center">
+    <h1>You may pass...</h1>
+    (<a href="@routes.QuestionsThreeController.index">Try Again</a>)
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/person.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/person.scala.html
new file mode 100644
index 0000000..2284221
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/person.scala.html
@@ -0,0 +1,29 @@
+@(personForm: Form[Person])
+
+ at implicitField = @{ helper.FieldConstructor(fields.f) }
+
+<html>
+<head>
+    <title>Questions Three</title>
+    <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/bootstrap.min.css")">
+    <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+</head>
+<body>
+<div class="text-center">
+    <h1>Answer me these questions three:</h1>
+    @if(personForm.hasGlobalErrors) {
+        @for(error <- personForm.globalErrors) {
+            <p class="text-error">@error.message</p>
+        }
+    }
+    @helper.form(action = routes.QuestionsThreeController.submit()) {
+        @helper.inputText(personForm("name"), '_label -> "What is your name?", '_showConstraints -> false)
+        @helper.inputText(personForm("quest"), '_label -> "What is your quest?", '_showConstraints -> false)
+        @helper.inputText(personForm("favoriteColor"), '_label -> "What is your favorite color?")
+        <div class="form-actions">
+            <button type="submit" class="btn btn-primary">Validate Form</button>
+        </div>
+    }
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/app/views/square.scala.html b/subprojects/docs/src/samples/play/advanced/app/views/square.scala.html
new file mode 100644
index 0000000..c510c06
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/app/views/square.scala.html
@@ -0,0 +1,23 @@
+@(title: String)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/bootstrap.min.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+        <script src="@routes.Assets.at("javascripts/jquery-2.1.3.min.js")" type="text/javascript"></script>
+        <script src="@routes.Assets.at("coffeescript/squareit.min.js")" type="text/javascript"></script>
+    </head>
+    <body>
+        <div class="text-center">
+            <h1>Square It!</h1>
+            <div id="content" class="lead">
+                <input type="text" id="number"/>
+                <br/>
+                <a class="btn btn-large btn-success" id="squareit">Square it!</a>
+            </div>
+        </div>
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/advanced/build.gradle b/subprojects/docs/src/samples/play/advanced/build.gradle
new file mode 100644
index 0000000..5ab0581
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/build.gradle
@@ -0,0 +1,28 @@
+plugins {
+    id 'play'
+    id 'play-coffeescript'
+}
+
+model {
+    components {
+        play {
+            platform play: '2.3.6', scala: '2.10'
+        }
+    }
+}
+
+dependencies {
+    play "commons-lang:commons-lang:2.6"
+}
+
+repositories{
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+    maven {
+        name = "gradle-js"
+        url = "https://repo.gradle.org/gradle/javascript-public"
+    }
+}
diff --git a/subprojects/docs/src/samples/play/advanced/conf/application.conf b/subprojects/docs/src/samples/play/advanced/conf/application.conf
new file mode 100644
index 0000000..0cf04ae
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/conf/application.conf
@@ -0,0 +1,62 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+application.secret="dxbAjiDdqlIV83LY<:;hSxql?tG`CPNgXEXt2asjk>lYQ<xfR`GsdeFJ at uuYBH=0"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# application.global=Global
+
+# Router
+# ~~~~~
+# Define the Router object to use for this application.
+# This router will be looked up first when the application is starting up,
+# so make sure this is the entry point.
+# Furthermore, it's assumed your route file is named properly.
+# So for an application router like `my.application.Router`,
+# you may need to define a router file `conf/my.application.routes`.
+# Default to Routes in the root package (and conf/routes)
+# application.router=my.application.Routes
+
+# Database configuration
+# ~~~~~
+# You can declare as many datasources as you want.
+# By convention, the default datasource is named `default`
+#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.user=sa
+# db.default.password=""
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+# evolutionplugin=disabled
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/),
+# by providing an application-logger.xml file in the conf directory.
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
+
diff --git a/subprojects/docs/src/samples/play/advanced/conf/hello.routes b/subprojects/docs/src/samples/play/advanced/conf/hello.routes
new file mode 100644
index 0000000..0c7e123
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/conf/hello.routes
@@ -0,0 +1 @@
+GET     /:name                controllers.hello.HelloController.index(name)
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/conf/routes b/subprojects/docs/src/samples/play/advanced/conf/routes
new file mode 100644
index 0000000..79b72e0
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/conf/routes
@@ -0,0 +1,19 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# Home page
+GET     /                           controllers.Application.index
+
+# Square it page
+GET     /square                     controllers.Application.square
+
+# People page
+GET     /questions                     controllers.QuestionsThreeController.index
+POST    /questions                     controllers.QuestionsThreeController.submit
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
+
+# Custom routes
+->     /hello                       hello.Routes
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/public/images/favicon.png b/subprojects/docs/src/samples/play/advanced/public/images/favicon.png
new file mode 100644
index 0000000..c7d92d2
Binary files /dev/null and b/subprojects/docs/src/samples/play/advanced/public/images/favicon.png differ
diff --git a/subprojects/docs/src/samples/play/advanced/public/javascripts/hello.js b/subprojects/docs/src/samples/play/advanced/public/javascripts/hello.js
new file mode 100644
index 0000000..209fbee
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/public/javascripts/hello.js
@@ -0,0 +1,3 @@
+if (window.console) {
+  console.log("Welcome to your Play application's JavaScript!");
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/advanced/public/javascripts/jquery-2.1.3.min.js b/subprojects/docs/src/samples/play/advanced/public/javascripts/jquery-2.1.3.min.js
new file mode 100644
index 0000000..25714ed
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/public/javascripts/jquery-2.1.3.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.3",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r= [...]
+},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J( [...]
+},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$ [...]
diff --git a/subprojects/docs/src/samples/play/advanced/public/stylesheets/bootstrap.min.css b/subprojects/docs/src/samples/play/advanced/public/stylesheets/bootstrap.min.css
new file mode 100644
index 0000000..df96c86
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/public/stylesheets/bootstrap.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Bootstrap v2.3.2
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:in [...]
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/play/advanced/public/stylesheets/main.css
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/play/advanced/public/stylesheets/main.css
diff --git a/subprojects/docs/src/samples/play/advanced/test/ApplicationSpec.scala b/subprojects/docs/src/samples/play/advanced/test/ApplicationSpec.scala
new file mode 100644
index 0000000..6e20bd5
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/test/ApplicationSpec.scala
@@ -0,0 +1,30 @@
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+
+import play.api.test._
+import play.api.test.Helpers._
+
+/**
+ * Add your spec here.
+ * You can mock out a whole application including requests, plugins etc.
+ * For more information, consult the wiki.
+ */
+ at RunWith(classOf[JUnitRunner])
+class ApplicationSpec extends Specification {
+
+  "Application" should {
+
+    "send 404 on a bad request" in new WithApplication{
+      route(FakeRequest(GET, "/boum")) must beNone
+    }
+
+    "render the index page" in new WithApplication{
+      val home = route(FakeRequest(GET, "/")).get
+
+      status(home) must equalTo(OK)
+      contentType(home) must beSome.which(_ == "text/html")
+      contentAsString(home) must contain ("Your new application is ready.")
+    }
+  }
+}
diff --git a/subprojects/docs/src/samples/play/advanced/test/IntegrationSpec.scala b/subprojects/docs/src/samples/play/advanced/test/IntegrationSpec.scala
new file mode 100644
index 0000000..652edde
--- /dev/null
+++ b/subprojects/docs/src/samples/play/advanced/test/IntegrationSpec.scala
@@ -0,0 +1,24 @@
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+
+import play.api.test._
+import play.api.test.Helpers._
+
+/**
+ * add your integration spec here.
+ * An integration test will fire up a whole play application in a real (or headless) browser
+ */
+ at RunWith(classOf[JUnitRunner])
+class IntegrationSpec extends Specification {
+
+  "Application" should {
+
+    "work from within a browser" in new WithBrowser {
+
+      browser.goTo("http://localhost:" + port)
+
+      browser.pageSource must contain("Your new application is ready.")
+    }
+  }
+}
diff --git a/subprojects/docs/src/samples/play/basic/app/controllers/Application.scala b/subprojects/docs/src/samples/play/basic/app/controllers/Application.scala
new file mode 100644
index 0000000..827737e
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/app/controllers/Application.scala
@@ -0,0 +1,14 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+
+import org.apache.commons.lang.StringUtils
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok(views.html.index(StringUtils.trim("   Your new application is ready.   ")))
+  }
+
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/basic/app/views/index.scala.html b/subprojects/docs/src/samples/play/basic/app/views/index.scala.html
new file mode 100644
index 0000000..d6a6b22
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/app/views/index.scala.html
@@ -0,0 +1,7 @@
+@(message: String)
+
+ at main("Welcome to Play") {
+
+    @play20.welcome(message)
+
+}
diff --git a/subprojects/docs/src/samples/play/basic/app/views/main.scala.html b/subprojects/docs/src/samples/play/basic/app/views/main.scala.html
new file mode 100644
index 0000000..5025aa5
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/app/views/main.scala.html
@@ -0,0 +1,15 @@
+@(title: String)(content: Html)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
+    </head>
+    <body>
+        @content
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/basic/build.gradle b/subprojects/docs/src/samples/play/basic/build.gradle
new file mode 100644
index 0000000..eb21fb5
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+    id 'play'
+}
+
+dependencies {
+    play 'commons-lang:commons-lang:2.6'
+    playTest "com.google.guava:guava:17.0"
+}
+
+repositories{
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/basic/conf/application.conf b/subprojects/docs/src/samples/play/basic/conf/application.conf
new file mode 100644
index 0000000..0cf04ae
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/conf/application.conf
@@ -0,0 +1,62 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+application.secret="dxbAjiDdqlIV83LY<:;hSxql?tG`CPNgXEXt2asjk>lYQ<xfR`GsdeFJ at uuYBH=0"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# application.global=Global
+
+# Router
+# ~~~~~
+# Define the Router object to use for this application.
+# This router will be looked up first when the application is starting up,
+# so make sure this is the entry point.
+# Furthermore, it's assumed your route file is named properly.
+# So for an application router like `my.application.Router`,
+# you may need to define a router file `conf/my.application.routes`.
+# Default to Routes in the root package (and conf/routes)
+# application.router=my.application.Routes
+
+# Database configuration
+# ~~~~~
+# You can declare as many datasources as you want.
+# By convention, the default datasource is named `default`
+#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.user=sa
+# db.default.password=""
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+# evolutionplugin=disabled
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/),
+# by providing an application-logger.xml file in the conf directory.
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
+
diff --git a/subprojects/docs/src/samples/play/basic/conf/routes b/subprojects/docs/src/samples/play/basic/conf/routes
new file mode 100644
index 0000000..20fd042
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/conf/routes
@@ -0,0 +1,9 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# Home page
+GET     /                           controllers.Application.index
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
diff --git a/subprojects/docs/src/samples/play/basic/public/images/favicon.png b/subprojects/docs/src/samples/play/basic/public/images/favicon.png
new file mode 100644
index 0000000..c7d92d2
Binary files /dev/null and b/subprojects/docs/src/samples/play/basic/public/images/favicon.png differ
diff --git a/subprojects/docs/src/samples/play/basic/public/javascripts/hello.js b/subprojects/docs/src/samples/play/basic/public/javascripts/hello.js
new file mode 100644
index 0000000..209fbee
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/public/javascripts/hello.js
@@ -0,0 +1,3 @@
+if (window.console) {
+  console.log("Welcome to your Play application's JavaScript!");
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/play/basic/public/stylesheets/main.css
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/play/basic/public/stylesheets/main.css
diff --git a/subprojects/docs/src/samples/play/basic/test/ApplicationSpec.scala b/subprojects/docs/src/samples/play/basic/test/ApplicationSpec.scala
new file mode 100644
index 0000000..599decb
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/test/ApplicationSpec.scala
@@ -0,0 +1,41 @@
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+
+import play.api.test._
+import play.api.test.Helpers._
+
+import org.apache.commons.lang.StringUtils
+import com.google.common.collect.Lists
+
+/**
+ * Add your spec here.
+ * You can mock out a whole application including requests, plugins etc.
+ * For more information, consult the wiki.
+ */
+ at RunWith(classOf[JUnitRunner])
+class ApplicationSpec extends Specification {
+
+  "Application" should {
+
+    "send 404 on a bad request" in new WithApplication{
+      route(FakeRequest(GET, "/boum")) must beNone
+    }
+
+    "render the index page" in new WithApplication{
+      val home = route(FakeRequest(GET, "/")).get
+
+      status(home) must equalTo(OK)
+      contentType(home) must beSome.which(_ == "text/html")
+      contentAsString(home) must contain ("Your new application is ready.")
+    }
+
+    "tests can use commons-lang play dependency" in {
+      StringUtils.reverse("foobar") must equalTo("raboof")
+    }
+
+    "tests can use guava play-test dependency" in {
+      Lists.newArrayList("foo", "bar").size() must equalTo(2)
+    }
+  }
+}
diff --git a/subprojects/docs/src/samples/play/basic/test/IntegrationSpec.scala b/subprojects/docs/src/samples/play/basic/test/IntegrationSpec.scala
new file mode 100644
index 0000000..652edde
--- /dev/null
+++ b/subprojects/docs/src/samples/play/basic/test/IntegrationSpec.scala
@@ -0,0 +1,24 @@
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+
+import play.api.test._
+import play.api.test.Helpers._
+
+/**
+ * add your integration spec here.
+ * An integration test will fire up a whole play application in a real (or headless) browser
+ */
+ at RunWith(classOf[JUnitRunner])
+class IntegrationSpec extends Specification {
+
+  "Application" should {
+
+    "work from within a browser" in new WithBrowser {
+
+      browser.goTo("http://localhost:" + port)
+
+      browser.pageSource must contain("Your new application is ready.")
+    }
+  }
+}
diff --git a/subprojects/docs/src/samples/play/multiproject/app/controllers/Application.scala b/subprojects/docs/src/samples/play/multiproject/app/controllers/Application.scala
new file mode 100644
index 0000000..086f8be
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/app/controllers/Application.scala
@@ -0,0 +1,12 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+
+import org.sample.util.BuiltBy
+
+object Application extends Controller {
+  def index = Action {
+    Ok(views.html.index(BuiltBy.watermark("Here is a multiproject app!")))
+  }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/app/views/index.scala.html b/subprojects/docs/src/samples/play/multiproject/app/views/index.scala.html
new file mode 100644
index 0000000..dc2e5ad
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/app/views/index.scala.html
@@ -0,0 +1,19 @@
+@(message: String)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>Multi-project Play</title>
+        <link rel="icon" type="image/ico" href="/assets/images/gradle.ico">
+        <script type="text/javascript" src="/assets/javascript/timestamp.js"></script>
+    </head>
+    <body>
+        <p>@message</p>
+        <ul>
+            <li><a href="/admin">Click for admin module</a></li>
+            <li><a href="/user">Click for user module</a></li>
+        </ul>
+        <p>Page loaded at <span id="timestamp">TIMESTAMP</span>.</p>
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/multiproject/build.gradle b/subprojects/docs/src/samples/play/multiproject/build.gradle
new file mode 100644
index 0000000..798153a
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/build.gradle
@@ -0,0 +1,19 @@
+plugins {
+    id 'play'
+}
+
+allprojects {
+    repositories{
+        jcenter()
+        maven{
+            name = "typesafe-maven-release"
+            url = "https://repo.typesafe.com/typesafe/maven-releases"
+        }
+    }
+}
+
+dependencies {
+    play project(":admin")
+    play project(":user")
+    play project(":util")
+}
diff --git a/subprojects/docs/src/samples/play/multiproject/conf/application.conf b/subprojects/docs/src/samples/play/multiproject/conf/application.conf
new file mode 100644
index 0000000..6f374a8
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/conf/application.conf
@@ -0,0 +1,28 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+application.secret="changeme"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Logger
+# ~~~~~
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
+
diff --git a/subprojects/docs/src/samples/play/multiproject/conf/routes b/subprojects/docs/src/samples/play/multiproject/conf/routes
new file mode 100644
index 0000000..553ddb7
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/conf/routes
@@ -0,0 +1,11 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# Home page
+GET     /                           controllers.Application.index
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
+
+# Module routes
+->     /                            admin.Routes
+->     /                            user.Routes
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Application.scala b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Application.scala
new file mode 100644
index 0000000..e8f083b
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Application.scala
@@ -0,0 +1,12 @@
+package controllers.admin
+
+import play.api._
+import play.api.mvc._
+
+import org.sample.util.BuiltBy
+
+object Application extends Controller {
+  def index = Action {
+    Ok(views.html.admin.index(BuiltBy.watermark("Here is the ADMIN module.")))
+  }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Assets.scala b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Assets.scala
new file mode 100644
index 0000000..cfae8f2
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/controllers/admin/Assets.scala
@@ -0,0 +1,4 @@
+package controllers.admin
+
+object Assets extends controllers.AssetsBuilder {
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/app/views/admin/index.scala.html b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/views/admin/index.scala.html
new file mode 100644
index 0000000..6b3891f
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/app/views/admin/index.scala.html
@@ -0,0 +1,14 @@
+@(message: String)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>The admin module</title>
+        <script type="text/javascript" src="/admin/assets/javascript/admin.js"></script>
+    </head>
+    <body>
+        <p>@message</p>
+        <p id="adminMessage">REPLACED BY ADMIN MODULE JAVASCRIPT</p>
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/build.gradle b/subprojects/docs/src/samples/play/multiproject/modules/admin/build.gradle
new file mode 100644
index 0000000..2d1e769
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/build.gradle
@@ -0,0 +1,7 @@
+plugins {
+    id 'play'
+}
+
+dependencies {
+    play project(":util")
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/admin.routes b/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/admin.routes
new file mode 100644
index 0000000..39cec06
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/admin.routes
@@ -0,0 +1,2 @@
+GET     /admin                          controllers.admin.Application.index
+GET     /admin/assets/*file             controllers.admin.Assets.at(path="/public", file)
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/application.conf b/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/application.conf
new file mode 100644
index 0000000..9865bb4
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/conf/application.conf
@@ -0,0 +1,3 @@
+include "../../../conf/application.conf"
+ 
+application.router=admin.Routes
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/admin/public/javascript/admin.js b/subprojects/docs/src/samples/play/multiproject/modules/admin/public/javascript/admin.js
new file mode 100644
index 0000000..d7f4fa9
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/admin/public/javascript/admin.js
@@ -0,0 +1,4 @@
+function displayAdminMessage() {
+    document.getElementById("adminMessage").innerHTML = "Loaded admin module javascript at " + new Date();
+}
+window.onload=displayAdminMessage;
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/user/app/controllers/user/Application.scala b/subprojects/docs/src/samples/play/multiproject/modules/user/app/controllers/user/Application.scala
new file mode 100644
index 0000000..48ca1c1
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/user/app/controllers/user/Application.scala
@@ -0,0 +1,12 @@
+package controllers.user
+
+import play.api._
+import play.api.mvc._
+
+import org.sample.util.BuiltBy
+
+object Application extends Controller {
+  def index = Action {
+    Ok(views.html.user.index(BuiltBy.watermark("Here is the USER module.")))
+  }
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/user/app/views/user/index.scala.html b/subprojects/docs/src/samples/play/multiproject/modules/user/app/views/user/index.scala.html
new file mode 100644
index 0000000..bd77ad9
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/user/app/views/user/index.scala.html
@@ -0,0 +1,12 @@
+@(message: String)
+
+<!DOCTYPE html>
+
+<html>
+    <head>
+        <title>The user module</title>
+    </head>
+    <body>
+        <p>@message</p>
+    </body>
+</html>
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/user/build.gradle b/subprojects/docs/src/samples/play/multiproject/modules/user/build.gradle
new file mode 100644
index 0000000..2d1e769
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/user/build.gradle
@@ -0,0 +1,7 @@
+plugins {
+    id 'play'
+}
+
+dependencies {
+    play project(":util")
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/user/conf/application.conf b/subprojects/docs/src/samples/play/multiproject/modules/user/conf/application.conf
new file mode 100644
index 0000000..9f4d389
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/user/conf/application.conf
@@ -0,0 +1,3 @@
+include "../../../conf/application.conf"
+ 
+application.router=user.Routes
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/user/conf/user.routes b/subprojects/docs/src/samples/play/multiproject/modules/user/conf/user.routes
new file mode 100644
index 0000000..c535a58
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/user/conf/user.routes
@@ -0,0 +1 @@
+GET     /user                          controllers.user.Application.index
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/util/build.gradle b/subprojects/docs/src/samples/play/multiproject/modules/util/build.gradle
new file mode 100644
index 0000000..c5fc4ba
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/util/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+    id 'java'
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/play/multiproject/modules/util/src/main/java/org/sample/util/BuiltBy.java b/subprojects/docs/src/samples/play/multiproject/modules/util/src/main/java/org/sample/util/BuiltBy.java
new file mode 100644
index 0000000..3609cea
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/modules/util/src/main/java/org/sample/util/BuiltBy.java
@@ -0,0 +1,7 @@
+package org.sample.util;
+
+public class BuiltBy {
+    public static String watermark(String input) {
+        return input + " (built by Gradle)";
+    }
+}
diff --git a/subprojects/docs/src/samples/play/multiproject/public/images/gradle.ico b/subprojects/docs/src/samples/play/multiproject/public/images/gradle.ico
new file mode 100644
index 0000000..596ca18
Binary files /dev/null and b/subprojects/docs/src/samples/play/multiproject/public/images/gradle.ico differ
diff --git a/subprojects/docs/src/samples/play/multiproject/public/javascript/timestamp.js b/subprojects/docs/src/samples/play/multiproject/public/javascript/timestamp.js
new file mode 100644
index 0000000..fa97160
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/public/javascript/timestamp.js
@@ -0,0 +1,4 @@
+function updateTimestamp() {
+    document.getElementById("timestamp").innerHTML = new Date();
+}
+window.onload=updateTimestamp;
diff --git a/subprojects/docs/src/samples/play/multiproject/settings.gradle b/subprojects/docs/src/samples/play/multiproject/settings.gradle
new file mode 100644
index 0000000..71bfeae
--- /dev/null
+++ b/subprojects/docs/src/samples/play/multiproject/settings.gradle
@@ -0,0 +1,5 @@
+include 'admin', 'user', 'util'
+
+project(':admin').projectDir = new File(settingsDir, 'modules/admin')
+project(':user').projectDir = new File(settingsDir, 'modules/user')
+project(':util').projectDir = new File(settingsDir, 'modules/util')
diff --git a/subprojects/docs/src/samples/plugins/buildscript/build.gradle b/subprojects/docs/src/samples/plugins/buildscript/build.gradle
new file mode 100644
index 0000000..58aa05d
--- /dev/null
+++ b/subprojects/docs/src/samples/plugins/buildscript/build.gradle
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// START SNIPPET buildscript_block
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1"
+    }
+}
+
+apply plugin: "com.jfrog.bintray"
+// END SNIPPET buildscript_block
+
+
diff --git a/subprojects/docs/src/samples/plugins/dsl/build.gradle b/subprojects/docs/src/samples/plugins/dsl/build.gradle
new file mode 100644
index 0000000..ab7cde8
--- /dev/null
+++ b/subprojects/docs/src/samples/plugins/dsl/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// START SNIPPET use-plugin
+plugins {
+    id 'java'
+}
+// END SNIPPET use-plugin
+
+// START SNIPPET use-community-plugin
+plugins {
+    id "com.jfrog.bintray" version "0.4.1"
+}
+// END SNIPPET use-community-plugin
+
+
diff --git a/subprojects/docs/src/samples/scala/customizedLayout/build.gradle b/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
index c65d6d2..8ccf5cc 100644
--- a/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
+++ b/subprojects/docs/src/samples/scala/customizedLayout/build.gradle
@@ -5,8 +5,8 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.2'
-    testCompile 'junit:junit:4.11'
+    compile 'org.scala-lang:scala-library:2.11.1'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET custom-source-locations
diff --git a/subprojects/docs/src/samples/scala/fsc/build.gradle b/subprojects/docs/src/samples/scala/fsc/build.gradle
index 9fbb9de..cf4f291 100644
--- a/subprojects/docs/src/samples/scala/fsc/build.gradle
+++ b/subprojects/docs/src/samples/scala/fsc/build.gradle
@@ -5,12 +5,12 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.2'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
 
 dependencies {
     compile 'commons-collections:commons-collections:3.2'
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET use-fsc
diff --git a/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle b/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
index a36592c..50000ae 100644
--- a/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
+++ b/subprojects/docs/src/samples/scala/mixedJavaAndScala/build.gradle
@@ -7,6 +7,6 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.1'
-    testCompile 'junit:junit:4.11'
+    compile 'org.scala-lang:scala-library:2.11.1'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/docs/src/samples/scala/quickstart/build.gradle b/subprojects/docs/src/samples/scala/quickstart/build.gradle
index 93ab85d..d8c36fd 100644
--- a/subprojects/docs/src/samples/scala/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/scala/quickstart/build.gradle
@@ -9,11 +9,11 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.1'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
 // END SNIPPET scala-dependency
 
 dependencies {
     compile 'commons-collections:commons-collections:3.2'
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/docs/src/samples/scala/zinc/build.gradle b/subprojects/docs/src/samples/scala/zinc/build.gradle
index 4920f5f..e09ae7b 100644
--- a/subprojects/docs/src/samples/scala/zinc/build.gradle
+++ b/subprojects/docs/src/samples/scala/zinc/build.gradle
@@ -5,12 +5,12 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.2'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
 
 dependencies {
     compile 'commons-collections:commons-collections:3.2'
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET use-zinc
diff --git a/subprojects/docs/src/samples/sonarRunner/advanced/build.gradle b/subprojects/docs/src/samples/sonarRunner/advanced/build.gradle
index fb6c347..e6abdca 100644
--- a/subprojects/docs/src/samples/sonarRunner/advanced/build.gradle
+++ b/subprojects/docs/src/samples/sonarRunner/advanced/build.gradle
@@ -22,3 +22,11 @@ sonarRunner {
     }
 }
 // END SNIPPET languages
+
+// START SNIPPET forkoptions
+sonarRunner {
+    forkOptions {
+        maxHeapSize = '512m'
+    }
+}
+// END SNIPPET forkoptions
diff --git a/subprojects/docs/src/samples/sonarRunner/advanced/src/main/java/org/gradle/Person.java b/subprojects/docs/src/samples/sonarRunner/advanced/src/main/java/org/gradle/Person.java
new file mode 100644
index 0000000..f4907b9
--- /dev/null
+++ b/subprojects/docs/src/samples/sonarRunner/advanced/src/main/java/org/gradle/Person.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.apache.commons.collections.list.GrowthList;
+
+public class Person {
+    private final String name;
+
+    public Person(String name) {
+        this.name = name;
+        new GrowthList();
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/subprojects/docs/src/samples/sonarRunner/advanced/src/test/java/org/gradle/PersonTest.java b/subprojects/docs/src/samples/sonarRunner/advanced/src/test/java/org/gradle/PersonTest.java
new file mode 100644
index 0000000..7b4ee92
--- /dev/null
+++ b/subprojects/docs/src/samples/sonarRunner/advanced/src/test/java/org/gradle/PersonTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.junit.Test;
+import static org.junit.Assert.*;
+
+public class PersonTest {
+    @Test
+    public void canConstructAPersonWithAName() {
+        Person person = new Person("Harry");
+        assertEquals("Harry", person.getName());
+    }
+}
diff --git a/subprojects/docs/src/samples/sonarRunner/quickstart/build.gradle b/subprojects/docs/src/samples/sonarRunner/quickstart/build.gradle
index 10e7a73..d0065fd 100644
--- a/subprojects/docs/src/samples/sonarRunner/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/sonarRunner/quickstart/build.gradle
@@ -20,6 +20,12 @@ repositories {
     mavenCentral()
 }
 
+// START SNIPPET version-settings
+sonarRunner {
+    toolVersion = '2.3' // default
+}
+// END SNIPPET version-settings
+
 dependencies {
     compile "commons-collections:commons-collections:3.2"
     testCompile "junit:junit:4.+"
diff --git a/subprojects/docs/src/samples/testing/filtering/build.gradle b/subprojects/docs/src/samples/testing/filtering/build.gradle
index eb4ce7c..7f0bc8b 100644
--- a/subprojects/docs/src/samples/testing/filtering/build.gradle
+++ b/subprojects/docs/src/samples/testing/filtering/build.gradle
@@ -21,7 +21,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET test-filtering
diff --git a/subprojects/docs/src/samples/testing/jacoco/quickstart/build.gradle b/subprojects/docs/src/samples/testing/jacoco/quickstart/build.gradle
index 0c41485..54754aa 100644
--- a/subprojects/docs/src/samples/testing/jacoco/quickstart/build.gradle
+++ b/subprojects/docs/src/samples/testing/jacoco/quickstart/build.gradle
@@ -23,7 +23,7 @@ apply plugin: "jacoco"
 
 // START SNIPPET jacoco-configuration
 jacoco {
-    toolVersion = "0.6.2.201302030002"
+    toolVersion = "0.7.1.201405082137"
     reportsDir = file("$buildDir/customJacocoReportDir")
 }
 // END SNIPPET jacoco-configuration
diff --git a/subprojects/docs/src/samples/testing/junit/categories/build.gradle b/subprojects/docs/src/samples/testing/junit/categories/build.gradle
index 457a88b..dbe7e4d 100644
--- a/subprojects/docs/src/samples/testing/junit/categories/build.gradle
+++ b/subprojects/docs/src/samples/testing/junit/categories/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 // START SNIPPET test-categories
diff --git a/subprojects/docs/src/samples/testing/testReport/build.gradle b/subprojects/docs/src/samples/testing/testReport/build.gradle
index fb83a7d..765be87 100644
--- a/subprojects/docs/src/samples/testing/testReport/build.gradle
+++ b/subprojects/docs/src/samples/testing/testReport/build.gradle
@@ -8,7 +8,7 @@ subprojects {
     }
 
     dependencies {
-        testCompile 'junit:junit:4.11'
+        testCompile 'junit:junit:4.12'
     }
 
 // START SNIPPET test-report
diff --git a/subprojects/docs/src/samples/toolingApi/customModel/tooling/build.gradle b/subprojects/docs/src/samples/toolingApi/customModel/tooling/build.gradle
index 42a9952..e3bbb76 100644
--- a/subprojects/docs/src/samples/toolingApi/customModel/tooling/build.gradle
+++ b/subprojects/docs/src/samples/toolingApi/customModel/tooling/build.gradle
@@ -7,7 +7,7 @@ repositories {
         url '../plugin/build/repo'
     }
     maven {
-        url 'http://repo.gradle.org/gradle/libs-releases-local'
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
     }
     mavenCentral()
 }
@@ -16,7 +16,7 @@ dependencies {
     compile 'org.gradle.sample:plugin:1.0'
     compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
     // Need an SLF4J implementation at runtime
-    runtime 'org.slf4j:slf4j-simple:1.7.5'
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
 }
 
 mainClassName = 'org.gradle.sample.Main'
diff --git a/subprojects/docs/src/samples/toolingApi/eclipse/build.gradle b/subprojects/docs/src/samples/toolingApi/eclipse/build.gradle
index ae4c0fa..a8818a8 100644
--- a/subprojects/docs/src/samples/toolingApi/eclipse/build.gradle
+++ b/subprojects/docs/src/samples/toolingApi/eclipse/build.gradle
@@ -5,7 +5,7 @@ def toolingApiVersion = gradle.gradleVersion
 
 repositories {
     maven {
-        url 'http://repo.gradle.org/gradle/libs-releases-local'
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
     }
     mavenCentral()
 }
@@ -13,7 +13,7 @@ repositories {
 dependencies {
     compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
     // Need an SLF4J implementation at runtime
-    runtime 'org.slf4j:slf4j-simple:1.7.5'
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
 }
 
 mainClassName = 'org.gradle.sample.Main'
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/toolingApi/idea/build.gradle b/subprojects/docs/src/samples/toolingApi/idea/build.gradle
index 764c1f7..020b95f 100644
--- a/subprojects/docs/src/samples/toolingApi/idea/build.gradle
+++ b/subprojects/docs/src/samples/toolingApi/idea/build.gradle
@@ -5,7 +5,7 @@ def toolingApiVersion = gradle.gradleVersion
 
 repositories {
     maven {
-        url 'http://repo.gradle.org/gradle/libs-releases-local'
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
     }
     mavenCentral()
 }
@@ -13,7 +13,7 @@ repositories {
 dependencies {
     compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
     // Need an SLF4J implementation at runtime
-    runtime 'org.slf4j:slf4j-simple:1.7.5'
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
 }
 
 mainClassName = 'org.gradle.sample.Main'
diff --git a/subprojects/docs/src/samples/toolingApi/model/build.gradle b/subprojects/docs/src/samples/toolingApi/model/build.gradle
index 764c1f7..020b95f 100644
--- a/subprojects/docs/src/samples/toolingApi/model/build.gradle
+++ b/subprojects/docs/src/samples/toolingApi/model/build.gradle
@@ -5,7 +5,7 @@ def toolingApiVersion = gradle.gradleVersion
 
 repositories {
     maven {
-        url 'http://repo.gradle.org/gradle/libs-releases-local'
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
     }
     mavenCentral()
 }
@@ -13,7 +13,7 @@ repositories {
 dependencies {
     compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
     // Need an SLF4J implementation at runtime
-    runtime 'org.slf4j:slf4j-simple:1.7.5'
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
 }
 
 mainClassName = 'org.gradle.sample.Main'
diff --git a/subprojects/docs/src/samples/toolingApi/runBuild/build.gradle b/subprojects/docs/src/samples/toolingApi/runBuild/build.gradle
index 764c1f7..020b95f 100644
--- a/subprojects/docs/src/samples/toolingApi/runBuild/build.gradle
+++ b/subprojects/docs/src/samples/toolingApi/runBuild/build.gradle
@@ -5,7 +5,7 @@ def toolingApiVersion = gradle.gradleVersion
 
 repositories {
     maven {
-        url 'http://repo.gradle.org/gradle/libs-releases-local'
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
     }
     mavenCentral()
 }
@@ -13,7 +13,7 @@ repositories {
 dependencies {
     compile "org.gradle:gradle-tooling-api:${toolingApiVersion}"
     // Need an SLF4J implementation at runtime
-    runtime 'org.slf4j:slf4j-simple:1.7.5'
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
 }
 
 mainClassName = 'org.gradle.sample.Main'
diff --git a/subprojects/docs/src/samples/toolingApi/runBuild/src/main/java/org/gradle/sample/Main.java b/subprojects/docs/src/samples/toolingApi/runBuild/src/main/java/org/gradle/sample/Main.java
index 406c04d..c684bcb 100644
--- a/subprojects/docs/src/samples/toolingApi/runBuild/src/main/java/org/gradle/sample/Main.java
+++ b/subprojects/docs/src/samples/toolingApi/runBuild/src/main/java/org/gradle/sample/Main.java
@@ -4,7 +4,6 @@ import org.gradle.tooling.BuildLauncher;
 import org.gradle.tooling.GradleConnector;
 import org.gradle.tooling.ProjectConnection;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 
 public class Main {
@@ -26,9 +25,8 @@ public class Main {
             // Configure the build
             BuildLauncher launcher = connection.newBuild();
             launcher.forTasks("help");
-            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-            launcher.setStandardOutput(outputStream);
-            launcher.setStandardError(outputStream);
+            launcher.setStandardOutput(System.out);
+            launcher.setStandardError(System.err);
 
             // Run the build
             launcher.run();
diff --git a/subprojects/docs/src/samples/userguide/ant/renameTask/build.gradle b/subprojects/docs/src/samples/userguide/ant/renameTask/build.gradle
new file mode 100644
index 0000000..c3c3fa4
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/ant/renameTask/build.gradle
@@ -0,0 +1,3 @@
+ant.importBuild('build.xml') { antTargetName ->
+    'a-' + antTargetName
+}
diff --git a/subprojects/docs/src/samples/userguide/ant/renameTask/build.xml b/subprojects/docs/src/samples/userguide/ant/renameTask/build.xml
new file mode 100644
index 0000000..d48889b
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/ant/renameTask/build.xml
@@ -0,0 +1,5 @@
+<project>
+    <target name="hello">
+        <echo>Hello, from Ant</echo>
+    </target>
+</project>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/ant/useExternalAntTaskWithConfig/build.gradle b/subprojects/docs/src/samples/userguide/ant/useExternalAntTaskWithConfig/build.gradle
index 31ff223..bff29e0 100644
--- a/subprojects/docs/src/samples/userguide/ant/useExternalAntTaskWithConfig/build.gradle
+++ b/subprojects/docs/src/samples/userguide/ant/useExternalAntTaskWithConfig/build.gradle
@@ -14,8 +14,12 @@ dependencies {
 
 // START SNIPPET use-classpath
 task check << {
-    ant.taskdef(name: 'pmd', classname: 'net.sourceforge.pmd.ant.PMDTask', classpath: configurations.pmd.asPath)
-    ant.pmd(shortFilenames: 'true', failonruleviolation: 'true', rulesetfiles: file('pmd-rules.xml').toURI().toString()) {
+    ant.taskdef(name: 'pmd',
+                classname: 'net.sourceforge.pmd.ant.PMDTask',
+                classpath: configurations.pmd.asPath)
+    ant.pmd(shortFilenames: 'true',
+            failonruleviolation: 'true',
+            rulesetfiles: file('pmd-rules.xml').toURI().toString()) {
         formatter(type: 'text', toConsole: 'true')
         fileset(dir: 'src')
     }
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/build.gradle
deleted file mode 100644
index bae2a80..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/build.gradle
+++ /dev/null
@@ -1,41 +0,0 @@
-repositories {
-    ivy {
-        url "$projectDir/repo"
-    }
-}
-
-configurations {
-    config1
-    config2
-    config3
-}
-
-// START SNIPPET latest-selector
-dependencies {
-    config1 "sea.fish:tuna:latest.integration"
-    config2 "sea.fish:tuna:latest.release"
-}
-
-task listFish << {
-    configurations.config1.each { println it.name }
-    println()
-    configurations.config2.each { println it.name}
-}
-// END SNIPPET latest-selector
-
-// START SNIPPET custom-status-scheme
-dependencies {
-    config3 "air.birds:albatros:latest.silver"
-    components {
-        eachComponent { ComponentMetadataDetails details ->
-            if (details.id.group == "air.birds") {
-                details.statusScheme = ["bronze", "silver", "gold", "platinum"]
-            }
-        }
-    }
-}
-
-task listBirds << {
-    configurations.config3.each { println it.name }
-}
-// END SNIPPET custom-status-scheme
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/1.9/ivy-1.9.xml b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/1.9/ivy-1.9.xml
deleted file mode 100644
index 5899994..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/1.9/ivy-1.9.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<ivy-module version="2.0">
-    <info organisation="air.birds"
-          module="albatros"
-          revision="1.9"
-          status="silver"/>
-    <publications>
-        <artifact />
-    </publications>
-</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/2.0/ivy-2.0.xml b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/2.0/ivy-2.0.xml
deleted file mode 100644
index d8accfe..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/air.birds/albatros/2.0/ivy-2.0.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<ivy-module version="2.0">
-    <info organisation="air.birds"
-          module="albatros"
-          revision="2.0"
-          status="gold"/>
-    <publications>
-        <artifact />
-    </publications>
-</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.3/ivy-1.3.xml b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.3/ivy-1.3.xml
deleted file mode 100644
index fc54db0..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.3/ivy-1.3.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<ivy-module version="2.0">
-    <info organisation="sea.fish"
-          module="tuna"
-          revision="1.3"
-          status="milestone" />
-    <publications>
-        <artifact />
-    </publications>
-</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.4/ivy-1.4.xml b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.4/ivy-1.4.xml
deleted file mode 100644
index 3c13bbe..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.4/ivy-1.4.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<ivy-module version="2.0">
-    <info organisation="sea.fish"
-          module="tuna"
-          revision="1.4"
-          status="release" />
-    <publications>
-        <artifact />
-    </publications>
-</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.5/ivy-1.5.xml b/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.5/ivy-1.5.xml
deleted file mode 100644
index fa235ac..0000000
--- a/subprojects/docs/src/samples/userguide/artifacts/componentMetadata/repo/sea.fish/tuna/1.5/ivy-1.5.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<ivy-module version="2.0">
-    <info organisation="sea.fish"
-          module="tuna"
-          revision="1.5"
-          status="integration" />
-    <publications>
-        <artifact />
-    </publications>
-</ivy-module>
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/componentModuleMetadata/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/componentModuleMetadata/build.gradle
new file mode 100644
index 0000000..509daf2
--- /dev/null
+++ b/subprojects/docs/src/samples/userguide/artifacts/componentModuleMetadata/build.gradle
@@ -0,0 +1,11 @@
+apply plugin: 'java'
+
+// START SNIPPET module_replacement_declaration
+dependencies {
+    modules {
+        module("com.google.collections:google-collections") {
+            replacedBy("com.google.guava:guava")
+        }
+    }
+}
+// END SNIPPET module_replacement_declaration
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/build.gradle
index 2c34d15..293c971 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/build.gradle
@@ -14,7 +14,7 @@ configurations {
 dependencies {
     sealife "sea.mammals:orca:1.0", "sea.fish:shark:1.0", "sea.fish:tuna:1.0"
     alllife configurations.sealife
-    alllife "air.birds:albatros:1.0"
+    alllife "air.birds:albatross:1.0"
 }
 //END SNIPPET setup
 
@@ -24,7 +24,8 @@ task dependencies << {
     println()
     configurations.alllife.allDependencies.each { dep -> println dep.name }
     println()
-    configurations.alllife.allDependencies.findAll { dep -> dep.name != 'orca' }.each { dep -> println dep.name }
+    configurations.alllife.allDependencies.findAll { dep -> dep.name != 'orca' }
+        .each { dep -> println dep.name }
 }
 //END SNIPPET dependencies
 
@@ -47,24 +48,20 @@ task files << {
 
 //START SNIPPET copy
 task copy << {
-    configurations.alllife.copyRecursive { dep -> dep.name != 'orca' }.allDependencies.each { dep ->
-        println dep.name
-    }
+    configurations.alllife.copyRecursive { dep -> dep.name != 'orca' }
+        .allDependencies.each { dep -> println dep.name }
     println()
-    configurations.alllife.copy().allDependencies.each { dep ->
-        println dep.name
-    }
+    configurations.alllife.copy().allDependencies
+        .each { dep -> println dep.name }
 }
 //END SNIPPET copy
 
 //START SNIPPET copyVsFiles
 task copyVsFiles << {
-    configurations.sealife.copyRecursive { dep -> dep.name == 'orca' }.each { file ->
-        println file.name
-    }
+    configurations.sealife.copyRecursive { dep -> dep.name == 'orca' }
+        .each { file -> println file.name }
     println()
-    configurations.sealife.files { dep -> dep.name == 'orca' }.each { file ->
-        println file.name
-    }
+    configurations.sealife.files { dep -> dep.name == 'orca' }
+        .each { file -> println file.name }
 }
 //END SNIPPET copyVsFiles
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/docs/src/samples/userguide/artifacts/configurationHandling/repo/air.birds/albatross-1.0.jar
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/docs/src/samples/userguide/artifacts/configurationHandling/repo/air.birds/albatross-1.0.jar
diff --git a/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
index 0fdcd60..402b25a 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/defineRepository/build.gradle
@@ -1,4 +1,54 @@
-import org.gradle.api.artifacts.repositories.IvyArtifactRepository
+//START SNIPPET maven-ivy-repository-no-auth
+repositories {
+    maven {
+        url "http://repo.mycompany.com/maven2"
+    }
+
+    ivy {
+        url "http://repo.mycompany.com/repo"
+    }
+}
+//END SNIPPET maven-ivy-repository-no-auth
+
+//START SNIPPET maven-ivy-repository-auth
+repositories {
+    maven {
+        url "sftp://repo.mycompany.com:22/maven2"
+        credentials {
+            username 'user'
+            password 'password'
+        }
+    }
+
+    ivy {
+        url "sftp://repo.mycompany.com:22/repo"
+        credentials {
+            username 'user'
+            password 'password'
+        }
+    }
+}
+//END SNIPPET maven-ivy-repository-auth
+
+//START SNIPPET maven-ivy-s3-repository
+repositories {
+    maven {
+        url "s3://myCompanyBucket/maven2"
+        credentials(AwsCredentials) {
+            accessKey "someKey"
+            secretKey "someSecret"
+        }
+    }
+
+    ivy {
+        url "s3://myCompanyBucket/ivyrepo"
+        credentials(AwsCredentials) {
+            accessKey "someKey"
+            secretKey "someSecret"
+        }
+    }
+}
+//END SNIPPET maven-ivy-s3-repository
 
 //START SNIPPET maven-central
 repositories {
@@ -12,6 +62,14 @@ repositories {
 }
 //END SNIPPET maven-jcenter
 
+//START SNIPPET maven-jcenter-http
+repositories {
+    jcenter {
+        url "http://jcenter.bintray.com/"
+    }
+}
+//END SNIPPET maven-jcenter-http
+
 //START SNIPPET maven-central-jar-repo
 repositories {
     mavenCentral name: 'single-jar-repo', artifactUrls: ["http://repo.mycompany.com/jars"]
diff --git a/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
index 6e16edc..e324ef5 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
@@ -28,7 +28,8 @@ task listJars << {
 //START SNIPPET module-dependencies
 dependencies {
     runtime group: 'org.springframework', name: 'spring-core', version: '2.5'
-    runtime 'org.springframework:spring-core:2.5', 'org.springframework:spring-aop:2.5'
+    runtime 'org.springframework:spring-core:2.5',
+            'org.springframework:spring-aop:2.5'
     runtime(
         [group: 'org.springframework', name: 'spring-core', version: '2.5'],
         [group: 'org.springframework', name: 'spring-aop', version: '2.5']
@@ -45,7 +46,7 @@ dependencies {
 
 //START SNIPPET dependencies-with-empty-attributes
 dependencies {
-    runtime ":junit:4.10", ":testng"
+    runtime ":junit:4.12", ":testng"
     runtime name: 'testng' 
 }
 //END SNIPPET dependencies-with-empty-attributes
@@ -59,19 +60,20 @@ dependencies {
 
 //START SNIPPET artifact-only
 dependencies {
-	runtime "org.groovy:groovy:2.2.0 at jar"
+    runtime "org.groovy:groovy:2.2.0 at jar"
     runtime group: 'org.groovy', name: 'groovy', version: '2.2.0', ext: 'jar'
 }
 //END SNIPPET artifact-only
 
 //START SNIPPET client-modules
 dependencies {
-    runtime module("org.codehaus.groovy:groovy-all:2.2.0") {
+    runtime module("org.codehaus.groovy:groovy:2.3.10") {
         dependency("commons-cli:commons-cli:1.0") {
             transitive = false
         }
-        module(group: 'org.apache.ant', name: 'ant', version: '1.9.3') {
-            dependencies "org.apache.ant:ant-launcher:1.9.3 at jar", "org.apache.ant:ant-junit:1.9.3"
+        module(group: 'org.apache.ant', name: 'ant', version: '1.9.4') {
+            dependencies "org.apache.ant:ant-launcher:1.9.4 at jar",
+                         "org.apache.ant:ant-junit:1.9.4"
         }
     }
 }
@@ -85,11 +87,12 @@ dependencies {
 //END SNIPPET file-dependencies
 
 //START SNIPPET list-grouping
-List groovy = ["org.codehaus.groovy:groovy-all:2.2.0 at jar",
+List groovy = ["org.codehaus.groovy:groovy-all:2.3.10 at jar",
                "commons-cli:commons-cli:1.0 at jar",
-               "org.apache.ant:ant:1.9.3 at jar"]
-List hibernate = ['org.hibernate:hibernate:3.0.5 at jar', 'somegroup:someorg:1.0 at jar']
+               "org.apache.ant:ant:1.9.4 at jar"]
+List hibernate = ['org.hibernate:hibernate:3.0.5 at jar',
+                  'somegroup:someorg:1.0 at jar']
 dependencies {
-	runtime groovy, hibernate
+    runtime groovy, hibernate
 }
 //END SNIPPET list-grouping
diff --git a/subprojects/docs/src/samples/userguide/artifacts/resolutionStrategy/build.gradle b/subprojects/docs/src/samples/userguide/artifacts/resolutionStrategy/build.gradle
index 16bfeeb..1702439 100644
--- a/subprojects/docs/src/samples/userguide/artifacts/resolutionStrategy/build.gradle
+++ b/subprojects/docs/src/samples/userguide/artifacts/resolutionStrategy/build.gradle
@@ -12,7 +12,7 @@ configurations.all {
 
 //START SNIPPET releasable-unit
 configurations.all {
-    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+    resolutionStrategy.dependencySubstitution.eachModule { ModuleDependencySubstitution details ->
         if (details.requested.group == 'org.gradle') {
             details.useVersion '1.4'
         }
@@ -22,9 +22,9 @@ configurations.all {
 
 //START SNIPPET custom-versioning-scheme
 configurations.all {
-    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+    resolutionStrategy.dependencySubstitution.eachModule { ModuleDependencySubstitution details ->
         if (details.requested.version == 'default') {
-            def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name)
+            def version = findDefaultVersionInCatalog(details.requested.group, details.requested.module)
             details.useVersion version
         }
     }
@@ -38,8 +38,8 @@ def findDefaultVersionInCatalog(String group, String name) {
 
 //START SNIPPET blacklisting_version
 configurations.all {
-    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
-        if (details.requested.group == 'org.software' && details.requested.name == 'some-library' && details.requested.version == '1.2') {
+    resolutionStrategy.dependencySubstitution.eachModule { ModuleDependencySubstitution details ->
+        if (details.requested.group == 'org.software' && details.requested.module == 'some-library' && details.requested.version == '1.2') {
             //prefer different version which contains some necessary fixes
             details.useVersion '1.2.1'
         }
@@ -49,15 +49,15 @@ configurations.all {
 
 //START SNIPPET module_substitution
 configurations.all {
-    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
-        if (details.requested.name == 'groovy-all') {
+    resolutionStrategy.dependencySubstitution.eachModule { ModuleDependencySubstitution details ->
+        if (details.requested.module == 'groovy-all') {
             //prefer 'groovy' over 'groovy-all':
             details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version
         }
-        if (details.requested.name == 'log4j') {
+        if (details.requested.module == 'log4j') {
             //prefer 'log4j-over-slf4j' over 'log4j', with fixed version:
-            details.useTarget "org.slf4j:log4j-over-slf4j:1.7.5"
+            details.useTarget "org.slf4j:log4j-over-slf4j:1.7.10"
         }
     }
 }
-//END SNIPPET module_substitution
\ No newline at end of file
+//END SNIPPET module_substitution
diff --git a/subprojects/docs/src/samples/userguide/buildlifecycle/basic/build.gradle b/subprojects/docs/src/samples/userguide/buildlifecycle/basic/build.gradle
index 44ee863..8892a7b 100644
--- a/subprojects/docs/src/samples/userguide/buildlifecycle/basic/build.gradle
+++ b/subprojects/docs/src/samples/userguide/buildlifecycle/basic/build.gradle
@@ -6,4 +6,14 @@ task configured {
 
 task test << {
     println 'This is executed during the execution phase.'
+}
+
+task testBoth {
+	doFirst {
+	  println 'This is executed first during the execution phase.'
+	}
+	doLast {
+	  println 'This is executed last during the execution phase.'
+	}
+	println 'This is executed during the configuration phase as well.'
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/buildlifecycle/projectEvaluateEvents/build.gradle b/subprojects/docs/src/samples/userguide/buildlifecycle/projectEvaluateEvents/build.gradle
index 977b632..d737e12 100644
--- a/subprojects/docs/src/samples/userguide/buildlifecycle/projectEvaluateEvents/build.gradle
+++ b/subprojects/docs/src/samples/userguide/buildlifecycle/projectEvaluateEvents/build.gradle
@@ -12,5 +12,5 @@ allprojects {
 // END SNIPPET after-evaluate
 
 allprojects {
-    hasTests = false
+    ext.hasTests = false
 }
diff --git a/subprojects/docs/src/samples/userguide/buildlifecycle/taskCreationEvents/build.gradle b/subprojects/docs/src/samples/userguide/buildlifecycle/taskCreationEvents/build.gradle
index 09281cd..6135358 100644
--- a/subprojects/docs/src/samples/userguide/buildlifecycle/taskCreationEvents/build.gradle
+++ b/subprojects/docs/src/samples/userguide/buildlifecycle/taskCreationEvents/build.gradle
@@ -1,5 +1,5 @@
 tasks.whenTaskAdded { task ->
-    task.srcDir = 'src/main/java'
+    task.ext.srcDir = 'src/main/java'
 }
 
 task a
diff --git a/subprojects/docs/src/samples/userguide/distribution/build.gradle b/subprojects/docs/src/samples/userguide/distribution/build.gradle
index e5f30ee..bb2d4b5 100755
--- a/subprojects/docs/src/samples/userguide/distribution/build.gradle
+++ b/subprojects/docs/src/samples/userguide/distribution/build.gradle
@@ -15,6 +15,19 @@ distributions {
         }
     }
 }
+
+// START SNIPPET publish-distribution
+apply plugin:'maven'
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "file://some/repo")
+        }
+    }
+}
+// END SNIPPET publish-distribution
+
 // END SNIPPET configure-distribution
 
 // START SNIPPET custom-distribution
diff --git a/subprojects/docs/src/samples/userguide/files/copy/build.gradle b/subprojects/docs/src/samples/userguide/files/copy/build.gradle
index c1fb029..d51a20d 100644
--- a/subprojects/docs/src/samples/userguide/files/copy/build.gradle
+++ b/subprojects/docs/src/samples/userguide/files/copy/build.gradle
@@ -11,7 +11,8 @@ task copyTaskWithPatterns(type: Copy) {
     into 'build/explodedWar'
     include '**/*.html'
     include '**/*.jsp'
-    exclude { details -> details.file.name.endsWith('.html') && details.file.text.contains('staging') }
+    exclude { details -> details.file.name.endsWith('.html') &&
+                         details.file.text.contains('staging') }
 }
 // END SNIPPET copy-task-with-patterns
 
@@ -49,7 +50,8 @@ task copyMethod << {
 
 // START SNIPPET copy-method-with-dependency
 task copyMethodWithExplicitDependencies{
-    inputs.file copyTask // up-to-date check for inputs, plus add copyTask as dependency
+    // up-to-date check for inputs, plus add copyTask as dependency
+    inputs.file copyTask
     outputs.dir 'some-dir' // up-to-date check for outputs
     doLast{
         copy {
@@ -97,7 +99,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
 task filter(type: Copy) {
     from 'src/main/webapp'
     into 'build/explodedWar'
-    // Substitute property references in files
+    // Substitute property tokens in files
     expand(copyright: '2009', version: '2.3.1')
     expand(project.properties)
     // Use some of the filters provided by Ant
diff --git a/subprojects/docs/src/samples/userguide/files/fileCollections/build.gradle b/subprojects/docs/src/samples/userguide/files/fileCollections/build.gradle
index 719a352..50adf3c 100644
--- a/subprojects/docs/src/samples/userguide/files/fileCollections/build.gradle
+++ b/subprojects/docs/src/samples/userguide/files/fileCollections/build.gradle
@@ -1,5 +1,7 @@
 // START SNIPPET simple-params
-FileCollection collection = files('src/file1.txt', new File('src/file2.txt'), ['src/file3.txt', 'src/file4.txt'])
+FileCollection collection = files('src/file1.txt',
+                                  new File('src/file2.txt'),
+                                  ['src/file3.txt', 'src/file4.txt'])
 // END SNIPPET simple-params
 
 file('src').mkdirs()
diff --git a/subprojects/docs/src/samples/userguide/groovy/groovyDependency/build.gradle b/subprojects/docs/src/samples/userguide/groovy/groovyDependency/build.gradle
index e36b137..6eeb2fd 100644
--- a/subprojects/docs/src/samples/userguide/groovy/groovyDependency/build.gradle
+++ b/subprojects/docs/src/samples/userguide/groovy/groovyDependency/build.gradle
@@ -6,7 +6,7 @@ repositories {
 
 // START SNIPPET groovy-test-dependency
 dependencies {
-    testCompile "org.codehaus.groovy:groovy-all:2.2.0"
+    testCompile "org.codehaus.groovy:groovy:2.3.10"
 }
 // END SNIPPET groovy-test-dependency
 
diff --git a/subprojects/docs/src/samples/userguide/initScripts/plugins/init.gradle b/subprojects/docs/src/samples/userguide/initScripts/plugins/init.gradle
index b9f8f68..33bd70d 100644
--- a/subprojects/docs/src/samples/userguide/initScripts/plugins/init.gradle
+++ b/subprojects/docs/src/samples/userguide/initScripts/plugins/init.gradle
@@ -7,16 +7,17 @@ apply plugin:EnterpriseRepositoryPlugin
 
 class EnterpriseRepositoryPlugin implements Plugin<Gradle> {
 
-    private static String ENTERPRISE_REPOSITORY_URL = "http://repo.gradle.org/gradle/repo"
+    private static String ENTERPRISE_REPOSITORY_URL = "https://repo.gradle.org/gradle/repo"
 
     void apply(Gradle gradle) {
         // ONLY USE ENTERPRISE REPO FOR DEPENDENCIES
         gradle.allprojects{ project ->
             project.repositories {
 
-                //remove all repositories not pointing to the enterprise repository url
+                // Remove all repositories not pointing to the enterprise repository url
                 all { ArtifactRepository repo ->
-                    if (!(repo instanceof MavenArtifactRepository) || repo.url.toString() != ENTERPRISE_REPOSITORY_URL) {
+                    if (!(repo instanceof MavenArtifactRepository) ||
+                          repo.url.toString() != ENTERPRISE_REPOSITORY_URL) {
                         project.logger.lifecycle "Repository ${repo.url} removed. Only $ENTERPRISE_REPOSITORY_URL is allowed"
                         remove repo
                     }
diff --git a/subprojects/docs/src/samples/userguide/java/sourceSets/build.gradle b/subprojects/docs/src/samples/userguide/java/sourceSets/build.gradle
index d0034ba..4e8327b 100644
--- a/subprojects/docs/src/samples/userguide/java/sourceSets/build.gradle
+++ b/subprojects/docs/src/samples/userguide/java/sourceSets/build.gradle
@@ -39,7 +39,7 @@ sourceSets {
 }
 
 dependencies {
-    intTestCompile 'junit:junit:4.11'
+    intTestCompile 'junit:junit:4.12'
     intTestRuntime 'org.ow2.asm:asm-all:4.0'
 }
 // END SNIPPET source-set-dependencies
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
index 8189328..b90118e 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/java/build.gradle
@@ -6,7 +6,7 @@ subprojects {
         mavenCentral()
     }
     dependencies {
-        testCompile "junit:junit:4.11"
+        testCompile "junit:junit:4.12"
     }
 }
 
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
index d6c13df..7d9ecaf 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/javaWithCustomConf/build.gradle
@@ -26,6 +26,6 @@ project(':services:personService') {
     dependencies {
         compile project(':shared')
         compile project(path: ':api', configuration: 'spi')
-        testCompile "junit:junit:4.11", project(':api')
+        testCompile "junit:junit:4.12", project(':api')
     }
 }
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependencies/messages/consumer/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependencies/messages/consumer/build.gradle
index 8cf6c17..04ae21c 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependencies/messages/consumer/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependencies/messages/consumer/build.gradle
@@ -1,6 +1,6 @@
 evaluationDependsOn(':producer')
 
-message = rootProject.producerMessage
+def message = rootProject.producerMessage
 
 task consume << {
     println("Consuming message: " + message)
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependenciesBroken/messages/consumer/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependenciesBroken/messages/consumer/build.gradle
index c4cef37..d3293a1 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependenciesBroken/messages/consumer/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/messagesConfigDependenciesBroken/messages/consumer/build.gradle
@@ -1,4 +1,4 @@
-message = rootProject.producerMessage
+def message = rootProject.producerMessage
 
 task consume << {
     println("Consuming message: " + message)
diff --git a/subprojects/docs/src/samples/userguide/multiproject/dependencies/webDist/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/dependencies/webDist/build.gradle
index 5f5ff41..a22bd80 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/dependencies/webDist/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/dependencies/webDist/build.gradle
@@ -14,14 +14,9 @@ subprojects {
     }
 }
 
-task explodedDist(dependsOn: assemble) << {
-    File explodedDist = mkdir("$buildDir/explodedDist")
-    subprojects.each {project ->
-        project.tasks.withType(Jar).each {archiveTask ->
-            copy {
-                from archiveTask.archivePath
-                into explodedDist
-            }
-        }
+task explodedDist(type: Copy) {
+    into "$buildDir/explodedDist"
+    subprojects {
+        from tasks.withType(War)
     }
 }
diff --git a/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/build.gradle
index 690332b..31b7e09 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/build.gradle
@@ -7,7 +7,9 @@ subprojects {
     hello {
         doLast {println "- I depend on water"}
         afterEvaluate { Project project ->
-            if (project.arctic) { doLast { println '- I love to spend time in the arctic waters.' }}
+            if (project.arctic) {
+                doLast { println '- I love to spend time in the arctic waters.' }
+            }
         }
     }
 }
diff --git a/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/krill/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/krill/build.gradle
index 453c824..0690ccd 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/krill/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/partialTasks/water/krill/build.gradle
@@ -1,5 +1,7 @@
 ext.arctic = true
-hello << { println "- The weight of my species in summer is twice as heavy as all human beings." }
+hello << {
+    println "- The weight of my species in summer is twice as heavy as all human beings."
+}
 
 task distanceToIceberg << {
     println '5 nautical miles'
diff --git a/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/bluewhale/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/bluewhale/build.gradle
index d9ee47e..249bcbb 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/bluewhale/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/bluewhale/build.gradle
@@ -1 +1,3 @@
-hello.doLast { println "- I'm the largest animal that has ever lived on this planet." }
+hello.doLast {
+  println "- I'm the largest animal that has ever lived on this planet."
+}
diff --git a/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/krill/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/krill/build.gradle
index be7e903..6cbe407 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/krill/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/spreadSpecifics/water/krill/build.gradle
@@ -1,3 +1,3 @@
 hello.doLast {
-    println "- The weight of my species in summer is twice as heavy as all human beings."
+  println "- The weight of my species in summer is twice as heavy as all human beings."
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/multiproject/tropicalWithProperties/water/bluewhale/build.gradle b/subprojects/docs/src/samples/userguide/multiproject/tropicalWithProperties/water/bluewhale/build.gradle
index 4d02663..3951751 100644
--- a/subprojects/docs/src/samples/userguide/multiproject/tropicalWithProperties/water/bluewhale/build.gradle
+++ b/subprojects/docs/src/samples/userguide/multiproject/tropicalWithProperties/water/bluewhale/build.gradle
@@ -1,2 +1,4 @@
 ext.arctic = true
-hello.doLast { println "- I'm the largest animal that has ever lived on this planet." }
\ No newline at end of file
+hello.doLast {
+  println "- I'm the largest animal that has ever lived on this planet."
+}
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle b/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
index 736d3f0..bb97f21 100644
--- a/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
+++ b/subprojects/docs/src/samples/userguide/organizeBuildLogic/build.gradle
@@ -3,7 +3,7 @@ configurations {
 }
 
 dependencies {
-    ftpAntTask("org.apache.ant:ant-commons-net:1.9.3") {
+    ftpAntTask("org.apache.ant:ant-commons-net:1.9.4") {
         module("commons-net:commons-net:1.4.1") {
             dependencies "oro:oro:2.0.8:jar"
         }
diff --git a/subprojects/docs/src/samples/userguide/organizeBuildLogic/inherited/build.gradle b/subprojects/docs/src/samples/userguide/organizeBuildLogic/inherited/build.gradle
index 99e47c8..64c0b47 100755
--- a/subprojects/docs/src/samples/userguide/organizeBuildLogic/inherited/build.gradle
+++ b/subprojects/docs/src/samples/userguide/organizeBuildLogic/inherited/build.gradle
@@ -1,5 +1,7 @@
-srcDirName = 'src/java'
+// Define an extra property
+ext.srcDirName = 'src/java'
 
+// Define a method
 def getSrcDir(project) {
     return project.file(srcDirName)
 }
diff --git a/subprojects/docs/src/samples/userguide/organizeBuildLogic/injected/build.gradle b/subprojects/docs/src/samples/userguide/organizeBuildLogic/injected/build.gradle
index 23b538a..ef2ae92 100755
--- a/subprojects/docs/src/samples/userguide/organizeBuildLogic/injected/build.gradle
+++ b/subprojects/docs/src/samples/userguide/organizeBuildLogic/injected/build.gradle
@@ -1,9 +1,11 @@
 subprojects {
-    // Inject a property and method
-    srcDirName = 'src/java'
-    srcDir = { file(srcDirName) }
+    // Define a new property
+    ext.srcDirName = 'src/java'
 
-    // Inject a task
+    // Define a method using a closure as the method body
+    ext.srcDir = { file(srcDirName) }
+
+    // Define a task
     task show << {
         println 'project: ' + project.path
         println 'srcDirName: ' + srcDirName
@@ -14,5 +16,5 @@ subprojects {
 
 // Inject special case configuration into a particular project
 project(':child2') {
-    srcDirName = "$srcDirName/legacy"
+    ext.srcDirName = "$srcDirName/legacy"
 }
diff --git a/subprojects/docs/src/samples/userguide/scala/scalaDependency/build.gradle b/subprojects/docs/src/samples/userguide/scala/scalaDependency/build.gradle
index ec27293..0374d2c 100644
--- a/subprojects/docs/src/samples/userguide/scala/scalaDependency/build.gradle
+++ b/subprojects/docs/src/samples/userguide/scala/scalaDependency/build.gradle
@@ -6,6 +6,6 @@ repositories {
 
 // START SNIPPET scala-test-dependency
 dependencies {
-    testCompile "org.scala-lang:scala-library:2.9.2"
+    testCompile "org.scala-lang:scala-library:2.11.1"
 }
 // END SNIPPET scala-test-dependency
diff --git a/subprojects/docs/src/samples/userguide/tasks/customTaskWithFileProperty/build.gradle b/subprojects/docs/src/samples/userguide/tasks/customTaskWithFileProperty/build.gradle
index 4923cd5..a8b5275 100644
--- a/subprojects/docs/src/samples/userguide/tasks/customTaskWithFileProperty/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tasks/customTaskWithFileProperty/build.gradle
@@ -25,5 +25,5 @@ task sayGreeting(dependsOn: greet) << {
     println file(greetingFile).text
 }
 
-greetingFile = "$buildDir/hello.txt"
+ext.greetingFile = "$buildDir/hello.txt"
 // END SNIPPET config
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tasks/incrementalTask/build.gradle b/subprojects/docs/src/samples/userguide/tasks/incrementalTask/build.gradle
index bb4b1d1..8562960 100644
--- a/subprojects/docs/src/samples/userguide/tasks/incrementalTask/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tasks/incrementalTask/build.gradle
@@ -45,7 +45,8 @@ class IncrementalReverseTask extends DefaultTask {
 
     @TaskAction
     void execute(IncrementalTaskInputs inputs) {
-        println inputs.incremental ? "CHANGED inputs considered out of date" : "ALL inputs considered out of date"
+        println inputs.incremental ? "CHANGED inputs considered out of date"
+                                   : "ALL inputs considered out of date"
         // START SNIPPET out-of-date-inputs
         inputs.outOfDate { change ->
             println "out of date: ${change.file.name}"
diff --git a/subprojects/docs/src/samples/userguide/tutorial/configureObject/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/configureObject/build.gradle
index e5e63b3..377dfb7 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/configureObject/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/configureObject/build.gradle
@@ -1,5 +1,5 @@
 task configure << {
-    pos = configure(new java.text.FieldPosition(10)) {
+    def pos = configure(new java.text.FieldPosition(10)) {
         beginIndex = 1
         endIndex = 5
     }
diff --git a/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/build.gradle
index 5bd4856..7741499 100755
--- a/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/build.gradle
@@ -1,5 +1,5 @@
 task configure << {
-    pos = new java.text.FieldPosition(10)
+    def pos = new java.text.FieldPosition(10)
     // Apply the script
     apply from: 'other.gradle', to: pos
     println pos.beginIndex
diff --git a/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/other.gradle b/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/other.gradle
index 4159bf9..7a99aaa 100755
--- a/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/other.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/configureObjectUsingScript/other.gradle
@@ -1,2 +1,3 @@
-beginIndex = 1;
-endIndex = 5;
+// Set properties.
+beginIndex = 1
+endIndex = 5
diff --git a/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
index 40198d6..37ff904 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/groovy/build.gradle
@@ -29,12 +29,12 @@ list.add('org/gradle/api/**')
 list.add('org/gradle/internal/**')
 test.includes = list
 
-// Map literal
-apply plugin: 'java'
+// Map literal.
+Map<String, String> map = [key1:'value1', key2: 'value2']
 
-Map<String, String> map = new HashMap<String, String>()
-map.put('plugin', 'java')
-apply(map)
+// Groovy will coerce named arguments
+// into a single map argument
+apply plugin: 'java'
 // END SNIPPET listAndMapLiterals
 
 // START SNIPPET closureAsLastParam
@@ -48,7 +48,7 @@ repositories({ println "in a closure" })
 // START SNIPPET closureDelegates
 dependencies {
     assert delegate == project.dependencies
-    compile('junit:junit:4.11')
-    delegate.compile('junit:junit:4.11')
+    testCompile('junit:junit:4.12')
+    delegate.testCompile('junit:junit:4.12')
 }
 // END SNIPPET closureDelegates
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/groovyWithFlatDir/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/groovyWithFlatDir/build.gradle
index 1c2366c..6c15f07 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/groovyWithFlatDir/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/groovyWithFlatDir/build.gradle
@@ -10,12 +10,13 @@ repositories {
 }
 
 dependencies {
-    compile module('org.codehaus.groovy:groovy:1.6.0') {
+    compile module('org.codehaus.groovy:groovy:2.3.10') {
         dependency('asm:asm-all:2.2.3')
         dependency('antlr:antlr:2.7.7')
         dependency('commons-cli:commons-cli:1.2')
-        module('org.apache.ant:ant:1.9.3') {
-            dependencies('org.apache.ant:ant-junit:1.9.3 at jar', 'org.apache.ant:ant-launcher:1.9.3')
+        module('org.apache.ant:ant:1.9.4') {
+            dependencies('org.apache.ant:ant-junit:1.9.4 at jar',
+                         'org.apache.ant:ant-launcher:1.9.4')
         }
     }
 }
diff --git a/subprojects/docs/src/samples/userguide/tutorial/makeDirectory/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/makeDirectory/build.gradle
deleted file mode 100644
index e52c087..0000000
--- a/subprojects/docs/src/samples/userguide/tutorial/makeDirectory/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-classesDir = new File('build/classes')
-task resources << {
-    classesDir.mkdirs()
-    // do something
-}
-task compile(dependsOn: 'resources') << {
-    if (classesDir.isDirectory()) {
-        println 'The class directory exists. I can operate'
-    }
-    // do something
-}
diff --git a/subprojects/docs/src/samples/userguide/tutorial/manifest/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/manifest/build.gradle
index 244222d..15acdc8 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/manifest/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/manifest/build.gradle
@@ -4,14 +4,16 @@ version = '1.0'
 // START SNIPPET add-to-manifest
 jar {
     manifest {
-        attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
+        attributes("Implementation-Title": "Gradle",
+                   "Implementation-Version": version)
     }
 }
 // END SNIPPET add-to-manifest
 
 // START SNIPPET custom-manifest
 ext.sharedManifest = manifest {
-    attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
+    attributes("Implementation-Title": "Gradle",
+               "Implementation-Version": version)
 }
 task fooJar(type: Jar) {
     manifest = project.manifest {
@@ -25,7 +27,8 @@ task barJar(type: Jar) {
     manifest {
         attributes key1: 'value1'
         from sharedManifest, 'src/config/basemanifest.txt'
-        from('src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt') {
+        from('src/config/javabasemanifest.txt',
+             'src/config/libbasemanifest.txt') {
             eachEntry { details ->
                 if (details.baseValue != details.mergeValue) {
                     details.value = baseValue
diff --git a/subprojects/docs/src/samples/userguide/tutorial/mkdirTrap/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/mkdirTrap/build.gradle
index 547ec97..b0b477f 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/mkdirTrap/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/mkdirTrap/build.gradle
@@ -1,4 +1,4 @@
-classesDir = file('build/classes')
+def classesDir = file('build/classes')
 classesDir.mkdirs()
 task clean(type: Delete) {
     delete 'build'
diff --git a/subprojects/docs/src/samples/userguide/tutorial/pluginIntro/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/pluginIntro/build.gradle
index b8ce76e..fded9d1 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/pluginIntro/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/pluginIntro/build.gradle
@@ -7,11 +7,8 @@ task show << {
 }
 // END SNIPPET apply-by-id
 // START SNIPPET apply-by-type
-apply plugin: org.gradle.api.plugins.JavaPlugin
-// END SNIPPET apply-by-type
-// START SNIPPET apply-by-type-with-import
 apply plugin: JavaPlugin
-// END SNIPPET apply-by-type-with-import
+// END SNIPPET apply-by-type
 // START SNIPPET explicit-apply
 apply plugin: 'java'
 apply plugin: 'groovy'
diff --git a/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
index acdab3a..0a297d9 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/projectReports/build.gradle
@@ -64,8 +64,8 @@ project(':api') {
 description = 'The shared API for the application'
 // END SNIPPET project-description
     dependencies {
-        compile "org.codehaus.groovy:groovy-all:2.2.0"
-        testCompile "junit:junit:4.11"
+        compile "org.codehaus.groovy:groovy-all:2.3.10"
+        testCompile "junit:junit:4.12"
     }
 }
 
diff --git a/subprojects/docs/src/samples/userguide/tutorial/properties/gradle.properties b/subprojects/docs/src/samples/userguide/tutorial/properties/gradle.properties
index 898b668..5a1d3e0 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/properties/gradle.properties
+++ b/subprojects/docs/src/samples/userguide/tutorial/properties/gradle.properties
@@ -1,4 +1,4 @@
 gradlePropertiesProp=gradlePropertiesValue
-systemProjectProp=shouldBeOverWrittenBySystemProp
+sysProp=shouldBeOverWrittenBySysProp
 envProjectProp=shouldBeOverWrittenByEnvProp
 systemProp.system=systemValue
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguide/tutorial/scope.groovy b/subprojects/docs/src/samples/userguide/tutorial/scope.groovy
index cd665a0..5867ccf 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/scope.groovy
+++ b/subprojects/docs/src/samples/userguide/tutorial/scope.groovy
@@ -13,8 +13,16 @@ closure = {
 }
 
 def method() {
-    try {localScope1} catch(MissingPropertyException e) {println 'localScope1NotAvailable' }
-    try {localScope2} catch(MissingPropertyException e) {println 'localScope2NotAvailable' }
+    try {
+        localScope1
+    } catch (MissingPropertyException e) {
+        println 'localScope1NotAvailable'
+    }
+    try {
+        localScope2
+    } catch(MissingPropertyException e) {
+        println 'localScope2NotAvailable'
+    }
     println scriptScope
 }
 
diff --git a/subprojects/docs/src/samples/userguide/tutorial/stopExecutionException/build.gradle b/subprojects/docs/src/samples/userguide/tutorial/stopExecutionException/build.gradle
index 8823ebf..8a046a5 100644
--- a/subprojects/docs/src/samples/userguide/tutorial/stopExecutionException/build.gradle
+++ b/subprojects/docs/src/samples/userguide/tutorial/stopExecutionException/build.gradle
@@ -3,7 +3,8 @@ task compile << {
 }
 
 compile.doFirst {
-    // Here you would put arbitrary conditions in real life. But we use this as an integration test, so we want defined behavior.
+    // Here you would put arbitrary conditions in real life.
+    // But this is used in an integration test so we want defined behavior.
     if (true) { throw new StopExecutionException() }
 }
 task myTask(dependsOn: 'compile') << {
diff --git a/subprojects/docs/src/samples/userguide/wrapper/simple/build.gradle b/subprojects/docs/src/samples/userguide/wrapper/simple/build.gradle
index e9e26b3..732328e 100644
--- a/subprojects/docs/src/samples/userguide/wrapper/simple/build.gradle
+++ b/subprojects/docs/src/samples/userguide/wrapper/simple/build.gradle
@@ -1,3 +1,3 @@
 task wrapper(type: Wrapper) {
-    gradleVersion = '1.4'
+    gradleVersion = '2.0'
 }
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-all.out b/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-all.out
new file mode 100644
index 0000000..7352af2
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-all.out
@@ -0,0 +1,6 @@
+:hello
+Hello John Smith!
+
+BUILD SUCCESSFUL
+
+Total time: 1 secs
diff --git a/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-model-task.out b/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-model-task.out
new file mode 100644
index 0000000..41e63f7
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/basicRuleSourcePlugin-model-task.out
@@ -0,0 +1,10 @@
+:model
+
+------------------------------------------------------------
+Root project
+------------------------------------------------------------
+
+model
+    person
+        firstName
+        lastName
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/buildlifecycle.out b/subprojects/docs/src/samples/userguideOutput/buildlifecycle.out
index 98c19f3..fb18c1f 100644
--- a/subprojects/docs/src/samples/userguideOutput/buildlifecycle.out
+++ b/subprojects/docs/src/samples/userguideOutput/buildlifecycle.out
@@ -1,8 +1,12 @@
 This is executed during the initialization phase.
 This is executed during the configuration phase.
 This is also executed during the configuration phase.
+This is executed during the configuration phase as well.
 :test
 This is executed during the execution phase.
+:testBoth
+This is executed first during the execution phase.
+This is executed last during the execution phase.
 
 BUILD SUCCESSFUL
 
diff --git a/subprojects/docs/src/samples/userguideOutput/completeCUnitExample.out b/subprojects/docs/src/samples/userguideOutput/completeCUnitExample.out
new file mode 100644
index 0000000..ee29f64
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/completeCUnitExample.out
@@ -0,0 +1,5 @@
+
+There were test failures:
+  1. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/c/test_plus.c:6  - plus(0, -2) == -2
+  2. /home/user/gradle/samples/native-binaries/cunit/src/operatorsTest/c/test_plus.c:7  - plus(2, 2) == 4
+
diff --git a/subprojects/docs/src/samples/userguideOutput/completeGoogleTestExample.out b/subprojects/docs/src/samples/userguideOutput/completeGoogleTestExample.out
new file mode 100644
index 0000000..e183422
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/completeGoogleTestExample.out
@@ -0,0 +1,22 @@
+Running main() from gtest_main.cc
+[==========] Running 2 tests from 1 test case.
+[----------] Global test environment set-up.
+[----------] 2 tests from OperatorTests
+[ RUN      ] OperatorTests.test_minus
+[       OK ] OperatorTests.test_minus (0 ms)
+[ RUN      ] OperatorTests.test_plus
+/home/user/gradle/samples/native-binaries/google-test/src/operatorsTest/cpp/test_plus.cpp:8: Failure
+Value of: plus(0, -2) == -2
+  Actual: false
+Expected: true
+[  FAILED  ] OperatorTests.test_plus (0 ms)
+[----------] 2 tests from OperatorTests (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 2 tests from 1 test case ran. (0 ms total)
+[  PASSED  ] 1 test.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] OperatorTests.test_plus
+
+ 1 FAILED TEST
+
diff --git a/subprojects/docs/src/samples/userguideOutput/configurationHandlingCopy.out b/subprojects/docs/src/samples/userguideOutput/configurationHandlingCopy.out
index ebabd66..3f75a62 100644
--- a/subprojects/docs/src/samples/userguideOutput/configurationHandlingCopy.out
+++ b/subprojects/docs/src/samples/userguideOutput/configurationHandlingCopy.out
@@ -1,5 +1,5 @@
-albatros
+albatross
 shark
 tuna
 
-albatros
+albatross
diff --git a/subprojects/docs/src/samples/userguideOutput/configurationHandlingDependencies.out b/subprojects/docs/src/samples/userguideOutput/configurationHandlingDependencies.out
index 4cdb766..859c317 100644
--- a/subprojects/docs/src/samples/userguideOutput/configurationHandlingDependencies.out
+++ b/subprojects/docs/src/samples/userguideOutput/configurationHandlingDependencies.out
@@ -1,10 +1,10 @@
-albatros
+albatross
 
-albatros
+albatross
 orca
 shark
 tuna
 
-albatros
+albatross
 shark
 tuna
diff --git a/subprojects/docs/src/samples/userguideOutput/customStatusScheme.out b/subprojects/docs/src/samples/userguideOutput/customStatusScheme.out
deleted file mode 100644
index 79e65a6..0000000
--- a/subprojects/docs/src/samples/userguideOutput/customStatusScheme.out
+++ /dev/null
@@ -1 +0,0 @@
-albatros-2.0.jar
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/dependencyInsightReport.out b/subprojects/docs/src/samples/userguideOutput/dependencyInsightReport.out
index 6d63730..2a00890 100644
--- a/subprojects/docs/src/samples/userguideOutput/dependencyInsightReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/dependencyInsightReport.out
@@ -1,3 +1,3 @@
-org.codehaus.groovy:groovy-all:2.2.0
+org.codehaus.groovy:groovy-all:2.3.10
 \--- project :api
      \--- compile
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out b/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
index 2572e33..833f302 100644
--- a/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/dependencyListReport.out
@@ -10,10 +10,10 @@ Project :api - The shared API for the application
 ------------------------------------------------------------
 
 compile
-\--- org.codehaus.groovy:groovy-all:2.2.0
+\--- org.codehaus.groovy:groovy-all:2.3.10
 
 testCompile
-\--- junit:junit:4.11
+\--- junit:junit:4.12
      \--- org.hamcrest:hamcrest-core:1.3
 
 ------------------------------------------------------------
@@ -22,7 +22,7 @@ Project :webapp - The Web application implementation
 
 compile
 +--- project :api
-|    \--- org.codehaus.groovy:groovy-all:2.2.0
+|    \--- org.codehaus.groovy:groovy-all:2.3.10
 \--- commons-io:commons-io:1.2
 
 testCompile
diff --git a/subprojects/docs/src/samples/userguideOutput/dependencyListReportFiltered.out b/subprojects/docs/src/samples/userguideOutput/dependencyListReportFiltered.out
index e105e73..4d0ce76 100644
--- a/subprojects/docs/src/samples/userguideOutput/dependencyListReportFiltered.out
+++ b/subprojects/docs/src/samples/userguideOutput/dependencyListReportFiltered.out
@@ -4,5 +4,5 @@ Project :api - The shared API for the application
 ------------------------------------------------------------
 
 testCompile
-\--- junit:junit:4.11
+\--- junit:junit:4.12
      \--- org.hamcrest:hamcrest-core:1.3
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/latestSelector.out b/subprojects/docs/src/samples/userguideOutput/latestSelector.out
index 979e2ef..e48bdf6 100644
--- a/subprojects/docs/src/samples/userguideOutput/latestSelector.out
+++ b/subprojects/docs/src/samples/userguideOutput/latestSelector.out
@@ -1,3 +1,3 @@
-tuna-1.5.jar
+client-1.5.jar
 
-tuna-1.4.jar
\ No newline at end of file
+client-1.4.jar
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/makeDirectory.out b/subprojects/docs/src/samples/userguideOutput/makeDirectory.out
deleted file mode 100644
index 67dcb8f..0000000
--- a/subprojects/docs/src/samples/userguideOutput/makeDirectory.out
+++ /dev/null
@@ -1 +0,0 @@
-The class directory exists. I can operate
diff --git a/subprojects/docs/src/samples/userguideOutput/modelDslCreate.out b/subprojects/docs/src/samples/userguideOutput/modelDslCreate.out
new file mode 100644
index 0000000..7352af2
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/modelDslCreate.out
@@ -0,0 +1,6 @@
+:hello
+Hello John Smith!
+
+BUILD SUCCESSFUL
+
+Total time: 1 secs
diff --git a/subprojects/docs/src/samples/userguideOutput/nativeComponentReport.out b/subprojects/docs/src/samples/userguideOutput/nativeComponentReport.out
new file mode 100644
index 0000000..f1590b2
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/nativeComponentReport.out
@@ -0,0 +1,51 @@
+:components
+
+------------------------------------------------------------
+Root project
+------------------------------------------------------------
+
+Native library 'hello'
+----------------------
+
+Source sets
+    C++ source 'hello:cpp'
+        src/hello/cpp
+
+Binaries
+    Shared library 'hello:sharedLibrary'
+        build using task: :helloSharedLibrary
+        platform: current
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/helloSharedLibrary/libhello.dylib
+    Static library 'hello:staticLibrary'
+        build using task: :helloStaticLibrary
+        platform: current
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/helloStaticLibrary/libhello.a
+
+Native executable 'main'
+------------------------
+
+Source sets
+    C++ source 'main:cpp'
+        src/main/cpp
+
+Binaries
+    Executable 'main:executable'
+        build using task: :mainExecutable
+        install using task: :installMainExecutable
+        platform: current
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        executable file: build/binaries/mainExecutable/main
+
+Note: currently not all plugins register their components, so some components may not be visible here.
+
+BUILD SUCCESSFUL
+
+Total time: 1 secs
diff --git a/subprojects/docs/src/samples/userguideOutput/propertyListReport.out b/subprojects/docs/src/samples/userguideOutput/propertyListReport.out
index 2049c5b..e018e69 100644
--- a/subprojects/docs/src/samples/userguideOutput/propertyListReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/propertyListReport.out
@@ -6,7 +6,8 @@ Project :api - The shared API for the application
 allprojects: [project ':api']
 ant: org.gradle.api.internal.project.DefaultAntBuilder at 12345
 antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory at 12345
-artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler at 12345
+artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated at 12345
 asDynamicObject: org.gradle.api.internal.ExtensibleDynamicObject at 12345
+baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope at 12345
 buildDir: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build
 buildFile: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build.gradle
diff --git a/subprojects/docs/src/samples/userguideOutput/renameAntDelegate.out b/subprojects/docs/src/samples/userguideOutput/renameAntDelegate.out
new file mode 100644
index 0000000..ea78572
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/renameAntDelegate.out
@@ -0,0 +1,6 @@
+:a-hello
+[ant:echo] Hello, from Ant
+
+BUILD SUCCESSFUL
+
+Total time: 1 secs
diff --git a/subprojects/docs/src/samples/userguideOutput/taskHelp.out b/subprojects/docs/src/samples/userguideOutput/taskHelp.out
index 64b121c..520ef47 100644
--- a/subprojects/docs/src/samples/userguideOutput/taskHelp.out
+++ b/subprojects/docs/src/samples/userguideOutput/taskHelp.out
@@ -10,3 +10,5 @@ Type
 Description
      Builds the JAR
 
+Group
+     build
diff --git a/subprojects/docs/src/samples/userguideOutput/taskListAllReport.out b/subprojects/docs/src/samples/userguideOutput/taskListAllReport.out
index 31b5846..e1ef380 100644
--- a/subprojects/docs/src/samples/userguideOutput/taskListAllReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/taskListAllReport.out
@@ -24,9 +24,27 @@ wrapper - Generates Gradle wrapper files. [incubating]
 
 Help tasks
 ----------
+components - Displays the components produced by root project 'projectReports'. [incubating]
+api:components - Displays the components produced by project ':api'. [incubating]
+webapp:components - Displays the components produced by project ':webapp'. [incubating]
 dependencies - Displays all dependencies declared in root project 'projectReports'.
+api:dependencies - Displays all dependencies declared in project ':api'.
+webapp:dependencies - Displays all dependencies declared in project ':webapp'.
 dependencyInsight - Displays the insight into a specific dependency in root project 'projectReports'.
-help - Displays a help message
+api:dependencyInsight - Displays the insight into a specific dependency in project ':api'.
+webapp:dependencyInsight - Displays the insight into a specific dependency in project ':webapp'.
+help - Displays a help message.
+api:help - Displays a help message.
+webapp:help - Displays a help message.
+model - Displays the configuration model of root project 'projectReports'. [incubating]
+api:model - Displays the configuration model of project ':api'. [incubating]
+webapp:model - Displays the configuration model of project ':webapp'. [incubating]
 projects - Displays the sub-projects of root project 'projectReports'.
+api:projects - Displays the sub-projects of project ':api'.
+webapp:projects - Displays the sub-projects of project ':webapp'.
 properties - Displays the properties of root project 'projectReports'.
+api:properties - Displays the properties of project ':api'.
+webapp:properties - Displays the properties of project ':webapp'.
 tasks - Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
+api:tasks - Displays the tasks runnable from project ':api'.
+webapp:tasks - Displays the tasks runnable from project ':webapp'.
diff --git a/subprojects/docs/src/samples/userguideOutput/taskListReport.out b/subprojects/docs/src/samples/userguideOutput/taskListReport.out
index 4811131..a62919b 100644
--- a/subprojects/docs/src/samples/userguideOutput/taskListReport.out
+++ b/subprojects/docs/src/samples/userguideOutput/taskListReport.out
@@ -18,11 +18,15 @@ wrapper - Generates Gradle wrapper files. [incubating]
 
 Help tasks
 ----------
+components - Displays the components produced by root project 'projectReports'. [incubating]
 dependencies - Displays all dependencies declared in root project 'projectReports'.
 dependencyInsight - Displays the insight into a specific dependency in root project 'projectReports'.
-help - Displays a help message
+help - Displays a help message.
+model - Displays the configuration model of root project 'projectReports'. [incubating]
 projects - Displays the sub-projects of root project 'projectReports'.
 properties - Displays the properties of root project 'projectReports'.
 tasks - Displays the tasks runnable from root project 'projectReports' (some of the displayed tasks may belong to subprojects).
 
-To see all tasks and more detail, run with --all.
+To see all tasks and more detail, run gradle tasks --all
+
+To see more detail about a task, run gradle help --task <task>
diff --git a/subprojects/docs/src/samples/userguideOutput/usePluginsInInitScripts.out b/subprojects/docs/src/samples/userguideOutput/usePluginsInInitScripts.out
index f58eafb..e3e74c6 100644
--- a/subprojects/docs/src/samples/userguideOutput/usePluginsInInitScripts.out
+++ b/subprojects/docs/src/samples/userguideOutput/usePluginsInInitScripts.out
@@ -1 +1 @@
-repository: STANDARD_ENTERPRISE_REPO ('http://repo.gradle.org/gradle/repo')
\ No newline at end of file
+repository: STANDARD_ENTERPRISE_REPO ('https://repo.gradle.org/gradle/repo')
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/userguideOutput/wrapperCommandLine.out b/subprojects/docs/src/samples/userguideOutput/wrapperCommandLine.out
new file mode 100644
index 0000000..b2965aa
--- /dev/null
+++ b/subprojects/docs/src/samples/userguideOutput/wrapperCommandLine.out
@@ -0,0 +1,5 @@
+:wrapper
+
+BUILD SUCCESSFUL
+
+Total time: 1 secs
diff --git a/subprojects/docs/src/samples/water/bluewhale/build.gradle b/subprojects/docs/src/samples/water/bluewhale/build.gradle
deleted file mode 100644
index d546c8f..0000000
--- a/subprojects/docs/src/samples/water/bluewhale/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-
-    dependsOn(':krill')
-
-    hello.doLast {
-        println "I'm the largets animal which has ever lived on this planet!"
-    }
-    
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/water/build.gradle b/subprojects/docs/src/samples/water/build.gradle
deleted file mode 100644
index e157bde..0000000
--- a/subprojects/docs/src/samples/water/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-childrenDependOnMe()
-
-allprojects {
-    task hello << {Task task ->
-        println "Hello, I'm $task.project.name"
-    }
-}
-
-subprojects*.hello*.doLast {
-    println 'I love water.'
-}
-
-hello << {
-    println 'As you all know, I cover three quarters of this planet!'
-}
-    
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/water/krill/build.gradle b/subprojects/docs/src/samples/water/krill/build.gradle
deleted file mode 100644
index d1c7d4f..0000000
--- a/subprojects/docs/src/samples/water/krill/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-
-    dependsOn(':phytoplankton')
-
-    hello.doLast {
-        println "The weight of my species in summer is twice as heavy as all human beings!"
-    }
-    
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/water/phytoplankton/build.gradle b/subprojects/docs/src/samples/water/phytoplankton/build.gradle
deleted file mode 100644
index ec57a7d..0000000
--- a/subprojects/docs/src/samples/water/phytoplankton/build.gradle
+++ /dev/null
@@ -1,5 +0,0 @@
-
-    hello.doLast {
-        println "I produce as much oxygen as all the other plants on earth together!"
-    }
-    
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/water/settings.gradle b/subprojects/docs/src/samples/water/settings.gradle
deleted file mode 100644
index 8f7c568..0000000
--- a/subprojects/docs/src/samples/water/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include 'bluewhale', 'krill', 'phytoplankton'
\ No newline at end of file
diff --git a/subprojects/docs/src/samples/webApplication/customized/build.gradle b/subprojects/docs/src/samples/webApplication/customized/build.gradle
index fdce6c5..af1f984 100644
--- a/subprojects/docs/src/samples/webApplication/customized/build.gradle
+++ b/subprojects/docs/src/samples/webApplication/customized/build.gradle
@@ -26,7 +26,7 @@ dependencies {
     }
     runtime ":runtime:1.0"
     providedRuntime ":providedRuntime:1.0 at jar"
-    testCompile "junit:junit:4.11"
+    testCompile "junit:junit:4.12"
     moreLibs ":otherLib:1.0"
 }
 
diff --git a/subprojects/docs/src/test/groovy/org/gradle/docs/releasenotes/FunctionalReleaseNotesTest.groovy b/subprojects/docs/src/test/groovy/org/gradle/docs/releasenotes/FunctionalReleaseNotesTest.groovy
index c7608c9..62e9962 100644
--- a/subprojects/docs/src/test/groovy/org/gradle/docs/releasenotes/FunctionalReleaseNotesTest.groovy
+++ b/subprojects/docs/src/test/groovy/org/gradle/docs/releasenotes/FunctionalReleaseNotesTest.groovy
@@ -28,11 +28,11 @@ import spock.lang.Shared
 /**
  * These tests actually open the release notes in a browser and test the JS.
  */
- at IgnoreIf({ !canReachServices() })
+ at IgnoreIf({ !FunctionalReleaseNotesTest.canReachServices() })
 class FunctionalReleaseNotesTest extends GebReportingSpec {
 
-    static private final String FIXED_ISSUES_URL = "http://services.gradle.org/fixed-issues/${GradleVersion.current().baseVersion.version}"
-    static private final String KNOWN_ISSUES_URL = "http://services.gradle.org/known-issues/${GradleVersion.current().baseVersion.version}"
+    static private final String FIXED_ISSUES_URL = "https://services.gradle.org/fixed-issues/${GradleVersion.current().baseVersion.version}"
+    static private final String KNOWN_ISSUES_URL = "https://services.gradle.org/known-issues/${GradleVersion.current().baseVersion.version}"
 
     private String version = GradleVersion.current().baseVersion.version
 
@@ -53,7 +53,6 @@ class FunctionalReleaseNotesTest extends GebReportingSpec {
         to ReleaseNotesPage
     }
 
-    @Override
     ReleaseNotesPage getPage() {
         browser.page as ReleaseNotesPage
     }
@@ -64,11 +63,18 @@ class FunctionalReleaseNotesTest extends GebReportingSpec {
     }
 
     List<Map> fixedIssues() {
-        new JsonSlurper().parseText(new URL(FIXED_ISSUES_URL).text) as List<Map>
+        parseIssues(FIXED_ISSUES_URL)
     }
 
     List<Map> knownIssues() {
-        new JsonSlurper().parseText(new URL(KNOWN_ISSUES_URL).text) as List<Map>
+        parseIssues(KNOWN_ISSUES_URL)
+    }
+
+    private List<Map<String, String>> parseIssues(String url) {
+        def result = new JsonSlurper().parseText(new URL(url).text) as List<Map>
+        result.each { json ->
+            json.summary = json.summary.replace('\n', '').replace('\t', '').replaceAll('\\s+', ' ').trim()
+        }
     }
 
     def "has fixed issues"() {
@@ -82,10 +88,10 @@ class FunctionalReleaseNotesTest extends GebReportingSpec {
             return
         }
 
-        page.fixedIssuesListItems.size() == numFixedIssues
+        waitFor { page.fixedIssuesListItems.size() == numFixedIssues }
         fixed.eachWithIndex { json, i ->
             def issue = page.fixedIssuesListItems[i]
-            assert issue.text() == "[$json.key] - ${json.summary.trim()}"
+            assert issue.text() == "[$json.key] - ${json.summary}"
             assert issue.find("a").attr("href") == json.link
         }
     }
@@ -99,13 +105,13 @@ class FunctionalReleaseNotesTest extends GebReportingSpec {
             waitFor { page.knownIssuesParagraph.text() == "There are no known issues of Gradle ${version} at this time." }
             return
         } else {
-            waitFor { page.knownIssuesParagraph.text() == "There are ${knownIssues.size()} known issues of Gradle $version." }
+            waitFor { page.knownIssuesParagraph.text() == "${knownIssues.size()} issues are known to affect Gradle $version." }
         }
 
         page.knownIssuesListItems.size() == knownIssues.size()
         knownIssues.eachWithIndex { json, i ->
             def issue = page.knownIssuesListItems[i]
-            assert issue.text() == "[$json.key] - ${json.summary.trim()}"
+            assert issue.text() == "[$json.key] - ${json.summary}"
             assert issue.find("a").attr("href") == json.link
         }
     }
diff --git a/subprojects/docs/src/transforms/release-notes.gradle b/subprojects/docs/src/transforms/release-notes.gradle
index ddeb589..627cd58 100644
--- a/subprojects/docs/src/transforms/release-notes.gradle
+++ b/subprojects/docs/src/transforms/release-notes.gradle
@@ -218,7 +218,7 @@ transformDocument {
 // Turn Gradle issue numbers into issue links
 transformDocument {
     def rewritten = body().html().replaceAll(~/GRADLE-\d+/) {
-        "<a href='http://issues.gradle.org/browse/${it}'>${it}</a>"
+        "<a href='https://issues.gradle.org/browse/${it}'>${it}</a>"
     }
     body().html(rewritten)
 }
diff --git a/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy b/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
index e116dc4..b849219 100644
--- a/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
+++ b/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
@@ -16,28 +16,26 @@
 
 package org.gradle.plugins.ear
 
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.util.TextUtil
 import org.hamcrest.Matchers
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
+import spock.lang.Unroll
 
 import static org.testng.Assert.assertEquals
 
-class EarPluginIntegrationTest extends AbstractIntegrationTest {
+class EarPluginIntegrationTest extends AbstractIntegrationSpec {
 
-    @Before
-    void "boring setup"() {
+    void "setup"() {
         file("rootLib.jar").createNewFile()
         file("earLib.jar").createNewFile()
 
         file("settings.gradle").write("rootProject.name='root'")
     }
 
-    @Test
     void "creates ear archive"() {
-        file("build.gradle").write("""
+        buildFile << """
 apply plugin: 'ear'
 
 dependencies {
@@ -45,11 +43,11 @@ dependencies {
     earlib files('earLib.jar')
 }
 
-""")
-        //when
-        executer.withTasks('assemble').run()
+"""
+        when:
+        run 'assemble'
 
-        //then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
         ear.assertContainsFile("META-INF/MANIFEST.MF")
         ear.assertContainsFile("META-INF/application.xml")
@@ -57,9 +55,8 @@ dependencies {
         ear.assertContainsFile("lib/earLib.jar")
     }
 
-    @Test
     void "customizes ear archive"() {
-        file("build.gradle").write("""
+        buildFile << """
 apply plugin: 'ear'
 
 dependencies {
@@ -74,33 +71,32 @@ ear {
     }
 }
 
-""")
-        //when
-        executer.withTasks('assemble').run()
+"""
+        when:
+        run 'assemble'
 
-        //then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
         ear.assertContainsFile("CUSTOM/lib/earLib.jar")
         ear.assertFileContent("META-INF/application.xml", Matchers.containsString("cool ear"))
     }
 
-    @Test
     void "includes modules in deployment descriptor"() {
         file('moduleA.jar').createFile()
         file('moduleB.war').createFile()
 
-        file("build.gradle").write("""
+        buildFile << """
 apply plugin: 'ear'
 
 dependencies {
     deploy files('moduleA.jar', 'moduleB.war')
 }
-""")
-        //when
-        executer.withTasks('assemble').run()
+"""
+        when:
+        run 'assemble'
         file("build/libs/root.ear").unzipTo(file("unzipped"))
 
-        //then
+        then:
         def appXml = new XmlSlurper().parse(
                 file('unzipped/META-INF/application.xml'))
         def modules = appXml.module
@@ -108,94 +104,112 @@ dependencies {
         assertEquals(modules[1].web.'web-uri'.text(), 'moduleB.war')
     }
 
-    @Test
-    void "uses content found in specified app folder"() {
+    @Unroll
+    void "uses content from application xml located #location"() {
+        def xsi = ["xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", "xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd\""]
+
+        if (JavaVersion.current().java8Compatible) {
+            xsi = xsi.reverse()
+        }
+
         def applicationXml = """<?xml version="1.0"?>
-<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" version="6">
+<application xmlns="http://java.sun.com/xml/ns/javaee" ${xsi.join(" ")} version="6">
   <application-name>customear</application-name>
+  <display-name>displayname</display-name>
+  <library-directory>mylib</library-directory>
 </application>
 """
 
-        file('app/META-INF/application.xml').createFile().write(applicationXml)
-        file('app/someOtherFile.txt').createFile()
-        file('app/META-INF/stuff/yetAnotherFile.txt').createFile()
-        file("build.gradle").write("""
+        file('META-INF/application.xml').createFile().write(applicationXml)
+        buildFile << """
 apply plugin: 'ear'
-
 ear {
-  appDirName 'app'
+    $appConfig
 }
-""")
+"""
 
-        //when
-        executer.withTasks('assemble').run()
+        when:
+        run 'assemble'
 
-        //then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
-        ear.assertContainsFile("someOtherFile.txt")
-        ear.assertContainsFile("META-INF/stuff/yetAnotherFile.txt")
-        ear.assertFileContent("META-INF/application.xml", applicationXml)
+        // Since the application.xml file is generated (using the supplied content), it uses platform line separators
+        ear.assertFileContent("META-INF/application.xml", TextUtil.toPlatformLineSeparators(applicationXml))
+
+        where:
+        location                      | metaInfFolder   | appConfig
+        "in root folder"              | "META-INF"      | ""
+        "in specified metaInf folder" | "customMetaInf" | "metaInf { from 'customMetaInf' }"
     }
 
-    @Test
-    void "uses content found in default app folder"() {
+    @Unroll
+    void "uses content found in #location app folder, ignoring descriptor modification"() {
         def applicationXml = """<?xml version="1.0"?>
 <application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" version="6">
   <application-name>customear</application-name>
 </application>
 """
 
-        file('src/main/application/META-INF/application.xml').createFile().write(applicationXml)
-        file('src/main/application/someOtherFile.txt').createFile()
-        file('src/main/application/META-INF/stuff/yetAnotherFile.txt').createFile()
-        file("build.gradle").write("""
+        file("${appDirectory}/META-INF/application.xml").createFile().write(applicationXml)
+        file("${appDirectory}/someOtherFile.txt").createFile()
+        file("${appDirectory}/META-INF/stuff/yetAnotherFile.txt").createFile()
+        buildFile << """
 apply plugin: 'ear'
+
 ear {
+    ${descriptorConfig}
     deploymentDescriptor {
         applicationName = 'descriptor modification will not have any affect when application.xml already exists in source'
     }
 }
-""")
+"""
 
-        //when
-        executer.withTasks('assemble').run()
+        when:
+        run 'assemble'
 
-        //then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
         ear.assertContainsFile("someOtherFile.txt")
         ear.assertContainsFile("META-INF/stuff/yetAnotherFile.txt")
         ear.assertFileContent("META-INF/application.xml", applicationXml)
+
+        where:
+        location    | descriptorConfig   | appDirectory
+        "specified" | "appDirName 'app'" | "app"
+        "default"   | ""                 | "src/main/application"
     }
 
-    @Test @Ignore
-    void "exclude duplicates: deploymentDescriptor has priority over metaInf"() {
-        file('bad-meta-inf/application.xml').createFile().write('bad descriptor')
-        file('build.gradle').write('''
+    void "works with existing descriptor containing a doctype declaration"() {
+        // We serve the DTD locally because the the parser actually pulls on this URL,
+        // and we don't want it reaching out to the Internet in our tests
+        def dtdResource = getClass().getResource("application_1_3.dtd")
+        assert dtdResource != null
+
+        def applicationXml = """<?xml version="1.0"?>
+<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "$dtdResource">
+<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" version="6">
+  <application-name>customear</application-name>
+</application>
+"""
+
+        file('src/main/application/META-INF/application.xml').createFile().write(applicationXml)
+        buildFile << """
 apply plugin: 'ear'
-ear {
-   duplicatesStrategy = 'exclude'
-   metaInf {
-       from 'bad-meta-inf'
-   }
-   deploymentDescriptor {
-       applicationName = 'good'
-   }
-}''')
+"""
 
-        // when
-        executer.withTasks('assemble').run();
+        when:
+        run 'assemble'
 
-        // then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
-        ear.assertFileContent("META-INF/application.xml", Matchers.not(Matchers.containsString("bad descriptor")))
+        ear.assertFileContent("META-INF/application.xml", applicationXml)
     }
 
-    @Test
     void "exclude duplicates: lib has priority over other files"() {
         file('bad-lib/file.txt').createFile().write('bad')
         file('good-lib/file.txt').createFile().write('good')
 
-        file('build.gradle').write('''
+        buildFile << '''
 apply plugin: 'ear'
 ear {
    duplicatesStrategy = 'exclude'
@@ -205,14 +219,50 @@ ear {
    lib {
        from 'good-lib'
    }
-}''')
+}'''
 
-        // when
-        executer.withTasks('assemble').run();
+        when:
+        run 'assemble';
 
-        // then
+        then:
         def ear = new JarTestFixture(file('build/libs/root.ear'))
         ear.assertFileContent("lib/file.txt", "good")
     }
 
+    void "use security role closure"() {
+        file('bad-lib/file.txt').createFile().write('bad')
+        file('good-lib/file.txt').createFile().write('good')
+
+        buildFile << '''
+apply plugin: 'ear'
+ear {
+  deploymentDescriptor {
+
+    securityRole {
+      roleName="superman"
+      description="This is the SUPERMAN role"
+     }
+
+    securityRole {
+      roleName="supergirl"
+      description="This is the SUPERGIRL role"
+    }
+  }
+}'''
+
+        when:
+        run 'assemble';
+
+        file("build/libs/root.ear").unzipTo(file("unzipped"))
+
+        then:
+        def appXml = new XmlSlurper().parse(
+                file('unzipped/META-INF/application.xml'))
+        def roles = appXml."security-role"
+        assertEquals(roles[0]."role-name".text(), 'superman')
+        assertEquals(roles[0].description.text(), 'This is the SUPERMAN role')
+        assertEquals(roles[1]."role-name".text(), 'supergirl')
+        assertEquals(roles[1].description.text(), 'This is the SUPERGIRL role')
+    }
+
 }
diff --git a/subprojects/ear/src/integTest/resources/org/gradle/plugins/ear/application_1_3.dtd b/subprojects/ear/src/integTest/resources/org/gradle/plugins/ear/application_1_3.dtd
new file mode 100644
index 0000000..c0e3b95
--- /dev/null
+++ b/subprojects/ear/src/integTest/resources/org/gradle/plugins/ear/application_1_3.dtd
@@ -0,0 +1,251 @@
+<!--
+  ~ Copyright 2014 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+        <!--
+        This is the XML DTD for the J2EE 1.3 application deployment
+        descriptor.  All J2EE 1.3 application deployment descriptors
+        must include a DOCTYPE of the following form:
+
+          <!DOCTYPE application PUBLIC
+            "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
+            "http://java.sun.com/dtd/application_1_3.dtd">
+
+        -->
+
+        <!--
+        The following conventions apply to all J2EE deployment descriptor
+        elements unless indicated otherwise.
+
+        - In elements that contain PCDATA, leading and trailing whitespace
+          in the data may be ignored.
+
+        - In elements whose value is an "enumerated type", the value is
+          case sensitive.
+
+        - In elements that specify a pathname to a file within the same
+          JAR file, relative filenames (i.e., those not starting with "/")
+          are considered relative to the root of the JAR file's namespace.
+          Absolute filenames (i.e., those starting with "/") also specify
+          names in the root of the JAR file's namespace.  In general, relative
+          names are preferred.  The exception is .war files where absolute
+          names are preferred for consistency with the servlet API.
+        -->
+
+
+        <!--
+        The application element is the root element of a J2EE application
+        deployment descriptor.
+        -->
+        <!ELEMENT application (icon?, display-name, description?, module+,
+                security-role*)>
+
+        <!--
+        The alt-dd element specifies an optional URI to the post-assembly
+        version of the deployment descriptor file for a particular J2EE module.
+        The URI must specify the full pathname of the deployment descriptor
+        file relative to the application's root directory. If alt-dd is not
+        specified, the deployer must read the deployment descriptor from the
+        default location and file name required by the respective component
+        specification.
+
+        Used in: module
+        -->
+        <!ELEMENT alt-dd (#PCDATA)>
+
+        <!--
+        The connector element specifies the URI of a resource adapter archive
+        file, relative to the top level of the application package.
+
+        Used in: module
+        -->
+        <!ELEMENT connector (#PCDATA)>
+
+        <!--
+        The context-root element specifies the context root of a web
+        application.
+
+        Used in: web
+        -->
+        <!ELEMENT context-root (#PCDATA)>
+
+        <!--
+        The description element is used to provide text describing the parent
+        element.  The description element should include any information that
+        the application ear file producer wants to provide to the consumer of
+        the application ear file (i.e., to the Deployer). Typically, the tools
+        used by the application ear file consumer will display the description
+        when processing the parent element that contains the description.
+
+        Used in: application, security-role
+        -->
+        <!ELEMENT description (#PCDATA)>
+
+        <!--
+        The display-name element contains a short name that is intended to be
+        displayed by tools.  The display name need not be unique.
+
+        Used in: application
+
+        Example:
+
+        <display-name>Employee Self Service</display-name>
+        -->
+        <!ELEMENT display-name (#PCDATA)>
+
+        <!--
+        The ejb element specifies the URI of an ejb-jar, relative to the top
+        level of the application package.
+
+        Used in: module
+        -->
+        <!ELEMENT ejb (#PCDATA)>
+
+        <!--
+        The icon element contains small-icon and large-icon elements that
+        specify the file names for small and a large GIF or JPEG icon images
+        used to represent the parent element in a GUI tool.
+
+        Used in: application
+        -->
+        <!ELEMENT icon (small-icon?, large-icon?)>
+
+        <!--
+        The java element specifies the URI of a java application client module,
+        relative to the top level of the application package.
+
+        Used in: module
+        -->
+        <!ELEMENT java (#PCDATA)>
+
+        <!--
+        The large-icon element contains the name of a file
+        containing a large (32 x 32) icon image. The file
+        name is a relative path within the application's
+        ear file.
+
+        The image may be either in the JPEG or GIF format.
+        The icon can be used by tools.
+
+        Used in: icon
+
+        Example:
+
+        <large-icon>employee-service-icon32x32.jpg</large-icon>
+        -->
+        <!ELEMENT large-icon (#PCDATA)>
+
+        <!--
+        The module element represents a single J2EE module and contains a
+        connector, ejb, java, or web element, which indicates the module type
+        and contains a path to the module file, and an optional alt-dd element,
+        which specifies an optional URI to the post-assembly version of the
+        deployment descriptor.
+
+        The application deployment descriptor must have one module element for
+        each J2EE module in the application package.
+
+        Used in: application
+        -->
+        <!ELEMENT module ((connector | ejb | java | web), alt-dd?)>
+
+        <!--
+        The role-name element contains the name of a security role.
+
+        The name must conform to the lexical rules for an NMTOKEN.
+
+        Used in: security-role
+        -->
+        <!ELEMENT role-name (#PCDATA)>
+
+        <!--
+        The security-role element contains the definition of a security
+        role. The definition consists of an optional description of the
+        security role, and the security role name.
+
+        Used in: application
+
+        Example:
+
+            <security-role>
+            <description>
+                This role includes all employees who are authorized
+                to access the employee service application.
+            </description>
+            <role-name>employee</role-name>
+            </security-role>
+        -->
+        <!ELEMENT security-role (description?, role-name)>
+
+        <!--
+        The small-icon element contains the name of a file
+        containing a small (16 x 16) icon image. The file
+        name is a relative path within the application's
+        ear file.
+
+        The image may be either in the JPEG or GIF format.
+        The icon can be used by tools.
+
+        Used in: icon
+
+        Example:
+
+        <small-icon>employee-service-icon16x16.jpg</small-icon>
+        -->
+        <!ELEMENT small-icon (#PCDATA)>
+
+        <!--
+        The web element contains the web-uri and context-root of a web
+        application module.
+
+        Used in: module
+        -->
+        <!ELEMENT web (web-uri, context-root)>
+
+        <!--
+        The web-uri element specifies the URI of a web application file,
+        relative to the top level of the application package.
+
+        Used in: web
+        -->
+        <!ELEMENT web-uri (#PCDATA)>
+
+        <!--
+        The ID mechanism is to allow tools that produce additional deployment
+        information (i.e., information beyond the standard deployment
+        descriptor information) to store the non-standard information in a
+        separate file, and easily refer from these tool-specific files to the
+        information in the standard deployment descriptor.
+
+        Tools are not allowed to add the non-standard information into the
+        standard deployment descriptor.
+        -->
+
+        <!ATTLIST alt-dd id ID #IMPLIED>
+        <!ATTLIST application id ID #IMPLIED>
+        <!ATTLIST connector id ID #IMPLIED>
+        <!ATTLIST context-root id ID #IMPLIED>
+        <!ATTLIST description id ID #IMPLIED>
+        <!ATTLIST display-name id ID #IMPLIED>
+        <!ATTLIST ejb id ID #IMPLIED>
+        <!ATTLIST icon id ID #IMPLIED>
+        <!ATTLIST java id ID #IMPLIED>
+        <!ATTLIST large-icon id ID #IMPLIED>
+        <!ATTLIST module id ID #IMPLIED>
+        <!ATTLIST role-name id ID #IMPLIED>
+        <!ATTLIST security-role id ID #IMPLIED>
+        <!ATTLIST small-icon id ID #IMPLIED>
+        <!ATTLIST web id ID #IMPLIED>
+        <!ATTLIST web-uri id ID #IMPLIED>
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/Ear.groovy b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/Ear.groovy
index a8497b0..3394528 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/Ear.groovy
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/Ear.groovy
@@ -16,12 +16,13 @@
 
 package org.gradle.plugins.ear
 
+import org.gradle.api.Action
 import org.gradle.api.file.CopySpec
 import org.gradle.api.file.FileCopyDetails
 import org.gradle.api.internal.file.collections.FileTreeAdapter
 import org.gradle.api.internal.file.collections.MapFileTree
 import org.gradle.api.tasks.bundling.Jar
-import org.gradle.internal.nativeplatform.filesystem.Chmod
+import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor
 import org.gradle.plugins.ear.descriptor.EarModule
 import org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor
@@ -29,6 +30,8 @@ import org.gradle.plugins.ear.descriptor.internal.DefaultEarModule
 import org.gradle.plugins.ear.descriptor.internal.DefaultEarWebModule
 import org.gradle.util.ConfigureUtil
 
+import javax.inject.Inject
+
 /**
  * Assembles an EAR archive.
  */
@@ -74,21 +77,28 @@ class Ear extends Jar {
         // this allows us to generate the deployment descriptor after recording all modules it contains
         def metaInf = mainSpec.addChild().into('META-INF')
         metaInf.addChild().from {
-            MapFileTree descriptorSource = new MapFileTree(temporaryDirFactory, services.get(Chmod))
+            MapFileTree descriptorSource = new MapFileTree(temporaryDirFactory, fileSystem)
             final DeploymentDescriptor descriptor = deploymentDescriptor
             if (descriptor) {
                 if (!descriptor.libraryDirectory) {
                     descriptor.libraryDirectory = libDirName
                 }
-                descriptorSource.add(descriptor.fileName) {OutputStream outstr ->
-                    descriptor.writeTo(new OutputStreamWriter(outstr))
-                }
+                descriptorSource.add(descriptor.fileName, new Action<OutputStream>() {
+                    void execute(OutputStream outputStream) {
+                        descriptor.writeTo(new OutputStreamWriter(outputStream))
+                    }
+                });
                 return new FileTreeAdapter(descriptorSource)
             }
             return null
         }
     }
 
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * Configures the deployment descriptor for this EAR archive.
      *
@@ -100,7 +110,7 @@ class Ear extends Jar {
      */
     Ear deploymentDescriptor(Closure configureClosure) {
         if (!deploymentDescriptor) {
-            deploymentDescriptor = new DefaultDeploymentDescriptor(project.fileResolver) // implied use of ProjectInternal
+            deploymentDescriptor = instantiator.newInstance(DefaultDeploymentDescriptor, project.fileResolver, getInstantiator()) // implied use of ProjectInternal
         }
         ConfigureUtil.configure(configureClosure, deploymentDescriptor)
         this
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPlugin.java b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPlugin.java
index 87caba6..07aba03 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPlugin.java
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPlugin.java
@@ -58,9 +58,9 @@ public class EarPlugin implements Plugin<Project> {
     }
 
     public void apply(final Project project) {
-        project.getPlugins().apply(BasePlugin.class);
+        project.getPluginManager().apply(BasePlugin.class);
 
-        final EarPluginConvention earPluginConvention = instantiator.newInstance(EarPluginConvention.class, fileResolver);
+        final EarPluginConvention earPluginConvention = instantiator.newInstance(EarPluginConvention.class, fileResolver, instantiator);
         project.getConvention().getPlugins().put("ear", earPluginConvention);
         earPluginConvention.setLibDirName("lib");
         earPluginConvention.setAppDirName("src/main/application");
@@ -158,10 +158,14 @@ public class EarPlugin implements Plugin<Project> {
         project.getTasks().withType(Ear.class, new Action<Ear>() {
             public void execute(Ear task) {
                 task.getConventionMapping().map("libDirName", new Callable<String>() {
-                    public String call() throws Exception { return earConvention.getLibDirName(); }
+                    public String call() throws Exception {
+                        return earConvention.getLibDirName();
+                    }
                 });
                 task.getConventionMapping().map("deploymentDescriptor", new Callable<DeploymentDescriptor>() {
-                    public DeploymentDescriptor call() throws Exception { return earConvention.getDeploymentDescriptor(); }
+                    public DeploymentDescriptor call() throws Exception {
+                        return earConvention.getDeploymentDescriptor();
+                    }
                 });
             }
         });
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPluginConvention.groovy b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPluginConvention.groovy
index 3a74f5a..bdfb522 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPluginConvention.groovy
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/EarPluginConvention.groovy
@@ -15,11 +15,14 @@
  */
 package org.gradle.plugins.ear
 
+import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor
 import org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.util.ConfigureUtil
 
+import javax.inject.Inject
+
 public class EarPluginConvention {
 
     /**
@@ -27,6 +30,11 @@ public class EarPluginConvention {
      */
     String appDirName
 
+    @Inject
+    public EarPluginConvention(Instantiator instantiator) {
+        this.instantiator = instantiator
+    }
+
     public void setAppDirName(String appDirName) {
         this.appDirName = appDirName
         if (deploymentDescriptor) {
@@ -61,10 +69,14 @@ public class EarPluginConvention {
     DeploymentDescriptor deploymentDescriptor
 
     private final FileResolver fileResolver
+    private final Instantiator instantiator
 
-    def EarPluginConvention(FileResolver fileResolver) {
+    @Inject
+    public EarPluginConvention(FileResolver fileResolver, Instantiator instantiator) {
+        this.instantiator = instantiator
         this.fileResolver = fileResolver
-        deploymentDescriptor = new DefaultDeploymentDescriptor(fileResolver)
+        deploymentDescriptor = instantiator.newInstance(DefaultDeploymentDescriptor.class, fileResolver, instantiator)
+        deploymentDescriptor.readFrom "META-INF/application.xml"
         deploymentDescriptor.readFrom "$appDirName/META-INF/$deploymentDescriptor.fileName"
     }
 
@@ -79,7 +91,8 @@ public class EarPluginConvention {
      */
     public EarPluginConvention deploymentDescriptor(Closure configureClosure) {
         if (!deploymentDescriptor) {
-            deploymentDescriptor = new DefaultDeploymentDescriptor(fileResolver)
+            deploymentDescriptor = instantiator.newInstance(DefaultDeploymentDescriptor.class,fileResolver, instantiator)
+            assert deploymentDescriptor != null
         }
         ConfigureUtil.configure(configureClosure, deploymentDescriptor)
         return this
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/DeploymentDescriptor.java b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/DeploymentDescriptor.java
index 3883a87..2eb984d 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/DeploymentDescriptor.java
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/DeploymentDescriptor.java
@@ -147,6 +147,14 @@ public interface DeploymentDescriptor {
     public DeploymentDescriptor securityRole(String role);
 
     /**
+     * Add a security role to the deployment descriptor after configuring it with the given action.
+     *
+     * @param action an action to configure the security role
+     * @return this.
+     */
+    public DeploymentDescriptor securityRole(Action<? extends EarSecurityRole> action);
+
+    /**
      * Mapping of module paths to module types. Non-null by default. For example, to specify that a module is a java
      * module, set <code>moduleTypeMappings["myJavaModule.jar"] = "java"</code>.
      */
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptor.groovy b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptor.groovy
index 2a35435..84fad23 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptor.groovy
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptor.groovy
@@ -16,19 +16,29 @@
 package org.gradle.plugins.ear.descriptor.internal
 
 import groovy.xml.QName
+
 import org.gradle.api.Action
 import org.gradle.api.UncheckedIOException
 import org.gradle.api.XmlProvider
 import org.gradle.api.internal.DomNode
 import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor
 import org.gradle.plugins.ear.descriptor.EarModule
 import org.gradle.plugins.ear.descriptor.EarSecurityRole
 import org.gradle.plugins.ear.descriptor.EarWebModule
+import org.xml.sax.SAXNotRecognizedException;
+
+import javax.inject.Inject
 
 class DefaultDeploymentDescriptor implements DeploymentDescriptor {
 
+    //redefine the constant because it's available in XMLConstants only from 1.7 onwards
+    private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD"
+
+    private static final String ALLOW_ANY_EXTERNAL_DTD = "all"
+
     private String fileName = "application.xml"
     String version = "6"
     String applicationName
@@ -41,20 +51,12 @@ class DefaultDeploymentDescriptor implements DeploymentDescriptor {
     Map<String, String> moduleTypeMappings = new HashMap<String, String>()
     private FileResolver fileResolver
     final XmlTransformer transformer = new XmlTransformer()
+    private final Instantiator instantiator
 
-    public DefaultDeploymentDescriptor(FileResolver fileResolver) {
-        this(new File("META-INF", "application.xml"), fileResolver)
-    }
-
-    public DefaultDeploymentDescriptor(Object descriptorPath, FileResolver fileResolver) {
+    @Inject
+    public DefaultDeploymentDescriptor(FileResolver fileResolver, Instantiator instantiator) {
+        this.instantiator = instantiator
         this.fileResolver = fileResolver
-        if (fileResolver) {
-            File descriptorFile = fileResolver.resolve(descriptorPath)
-            if (descriptorFile) {
-                fileName = descriptorFile.name
-                readFrom descriptorFile
-            }
-        }
     }
 
     public String getFileName() {
@@ -91,6 +93,13 @@ class DefaultDeploymentDescriptor implements DeploymentDescriptor {
         return this
     }
 
+    public DeploymentDescriptor securityRole(Action<? extends EarSecurityRole> action) {
+        EarSecurityRole role = instantiator.newInstance(DefaultEarSecurityRole)
+        action.execute(role)
+        securityRoles.add(role)
+        return this
+    }
+
     public DeploymentDescriptor withXml(Closure closure) {
         transformer.addAction(closure)
         return this
@@ -119,9 +128,22 @@ class DefaultDeploymentDescriptor implements DeploymentDescriptor {
         }
     }
 
+    private XmlParser createParser() {
+        XmlParser parser = new XmlParser(false, true, true)
+        try {
+            // If not set for >= JAXP 1.5 / Java8 won't allow referencing DTDs, e.g.
+            // using http URLs, because Groovy's XmlParser requests FEATURE_SECURE_PROCESSING
+            parser.setProperty(ACCESS_EXTERNAL_DTD, ALLOW_ANY_EXTERNAL_DTD)
+        } catch (SAXNotRecognizedException ignore) {
+            // property requires >= JAXP 1.5 / Java8
+        }
+        parser
+    }
+
     DeploymentDescriptor readFrom(Reader reader) {
         try {
-            def appNode = new XmlParser().parse(reader)
+            def appNode = createParser().parse(reader)
+
             version = appNode. at version
 
             appNode.children().each { child ->
@@ -175,7 +197,7 @@ class DefaultDeploymentDescriptor implements DeploymentDescriptor {
         return this
     }
 
-    private String localNameOf(Node node) {
+    protected String localNameOf(Node node) {
         node.name() instanceof QName ? node.name().localPart : node.name() as String
     }
 
diff --git a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultEarSecurityRole.groovy b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultEarSecurityRole.groovy
index f1168ca..baead94 100644
--- a/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultEarSecurityRole.groovy
+++ b/subprojects/ear/src/main/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultEarSecurityRole.groovy
@@ -17,11 +17,14 @@ package org.gradle.plugins.ear.descriptor.internal
 
 import org.gradle.plugins.ear.descriptor.EarSecurityRole
 
+import javax.inject.Inject
+
 class DefaultEarSecurityRole implements EarSecurityRole {
 
     String description
     String roleName
 
+    @Inject
     public DefaultEarSecurityRole() {
     }
 
diff --git a/subprojects/ear/src/main/resources/META-INF/gradle-plugins/ear.properties b/subprojects/ear/src/main/resources/META-INF/gradle-plugins/org.gradle.ear.properties
similarity index 100%
rename from subprojects/ear/src/main/resources/META-INF/gradle-plugins/ear.properties
rename to subprojects/ear/src/main/resources/META-INF/gradle-plugins/org.gradle.ear.properties
diff --git a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarPluginTest.groovy b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarPluginTest.groovy
index a53e8ea..0da218b 100644
--- a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarPluginTest.groovy
+++ b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarPluginTest.groovy
@@ -58,14 +58,14 @@ class EarPluginTest {
     }
 
     @Test public void appliesBasePluginAndAddsConvention() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         assertTrue(project.getPlugins().hasPlugin(BasePlugin));
         assertThat(project.convention.plugins.ear, instanceOf(EarPluginConvention))
     }
     
     @Test public void createsConfigurations() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         def configuration = project.configurations.getByName(EarPlugin.DEPLOY_CONFIGURATION_NAME)
         assertFalse(configuration.visible)
@@ -77,7 +77,7 @@ class EarPluginTest {
     }
 
     @Test public void addsTasks() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         def task = project.tasks[EarPlugin.EAR_TASK_NAME]
         assertThat(task, instanceOf(Ear))
@@ -88,8 +88,8 @@ class EarPluginTest {
     }
 
     @Test public void addsTasksToJavaProject() {
-        project.plugins.apply(JavaPlugin.class)
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(JavaPlugin.class)
+        project.pluginManager.apply(EarPlugin)
 
         def task = project.tasks[EarPlugin.EAR_TASK_NAME]
         assertThat(task, instanceOf(Ear))
@@ -101,7 +101,7 @@ class EarPluginTest {
     }
 
     @Test public void dependsOnEarlibConfig() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         Project childProject = TestUtil.createChildProject(project, 'child')
         JavaPlugin javaPlugin = new JavaPlugin()
@@ -116,34 +116,32 @@ class EarPluginTest {
     }
 
     @Test public void appliesMappingsToArchiveTasks() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         def task = project.task(type: Ear, 'customEar')
         assertThat(task.destinationDir, equalTo(project.libsDir))
     }
 
     @Test public void worksWithJavaBasePluginAppliedBeforeEarPlugin() {
-        project.plugins.apply(JavaBasePlugin.class)
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(JavaBasePlugin.class)
+        project.pluginManager.apply(EarPlugin)
 
         def task = project.task(type: Ear, 'customEar')
         assertThat(task.destinationDir, equalTo(project.libsDir))
     }
 
     @Test public void appliesMappingsToArchiveTasksForJavaProject() {
-        project.plugins.apply(EarPlugin)
-        project.plugins.apply(JavaPlugin.class)
+        project.pluginManager.apply(EarPlugin)
+        project.pluginManager.apply(JavaPlugin.class)
 
-        def task = project.task(type: Ear, 'customEar') {
-            earModel = new EarPluginConvention(null)
-        }
+        def task = project.task(type: Ear, 'customEar')
         assertThat(task.destinationDir, equalTo(project.libsDir))
 
         assertThat(task, dependsOn(hasItems(JavaPlugin.CLASSES_TASK_NAME)))
     }
 
     @Test public void addsEarAsPublication() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         Configuration archiveConfiguration = project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION);
         assertThat(archiveConfiguration.getAllArtifacts().size(), equalTo(1));
@@ -151,8 +149,8 @@ class EarPluginTest {
     }
 
     @Test public void replacesWarAsPublication() {
-        project.plugins.apply(EarPlugin)
-        project.plugins.apply(WarPlugin)
+        project.pluginManager.apply(EarPlugin)
+        project.pluginManager.apply(WarPlugin)
 
         Configuration archiveConfiguration = project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION);
         assertThat(archiveConfiguration.getAllArtifacts().size(), equalTo(1));
@@ -160,8 +158,8 @@ class EarPluginTest {
     }
 
     @Test public void replacesJarAsPublication() {
-        project.plugins.apply(EarPlugin)
-        project.plugins.apply(JavaPlugin)
+        project.pluginManager.apply(EarPlugin)
+        project.pluginManager.apply(JavaPlugin)
 
         Configuration archiveConfiguration = project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION);
         assertThat(archiveConfiguration.getAllArtifacts().size(), equalTo(1));
@@ -173,7 +171,7 @@ class EarPluginTest {
         project.file("src/main/application/META-INF/test.txt").createNewFile()
         project.file("src/main/application/test2.txt").createNewFile()
 
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
 
         execute project.tasks[EarPlugin.EAR_TASK_NAME]
 
@@ -185,7 +183,7 @@ class EarPluginTest {
         project.file("src/main/myapp").mkdirs()
         project.file("src/main/myapp/test.txt").createNewFile()
 
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         project.convention.plugins.ear.appDirName = "src/main/myapp"
 
         execute project.tasks[EarPlugin.EAR_TASK_NAME]
@@ -199,7 +197,7 @@ class EarPluginTest {
         JavaPlugin javaPlugin = new JavaPlugin()
         javaPlugin.apply(childProject)
 
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         project.convention.plugins.ear.libDirName = "APP-INF/lib"
         project.dependencies {
             earlib project(path: childProject.path, configuration: 'archives')
@@ -211,7 +209,7 @@ class EarPluginTest {
     }
 
     @Test public void supportsGeneratingDeploymentDescriptor() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         execute project.tasks[EarPlugin.EAR_TASK_NAME]
 
         inEar "META-INF/application.xml"
@@ -221,14 +219,14 @@ class EarPluginTest {
         project.file("src/main/application/META-INF").mkdirs()
         project.file("src/main/application/META-INF/application.xml").text = TEST_APP_XML
 
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         execute project.tasks[EarPlugin.EAR_TASK_NAME]
 
         assert inEar("META-INF/application.xml").text == TEST_APP_XML
     }
 
     @Test public void supportsRenamingDeploymentDescriptor() {
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         project.convention.plugins.ear.deploymentDescriptor {
             fileName = "myapp.xml"
         }
@@ -241,7 +239,7 @@ class EarPluginTest {
         project.file("src/main/application/META-INF").mkdirs()
         project.file("src/main/application/META-INF/myapp.xml").text = TEST_APP_XML
 
-        project.plugins.apply(EarPlugin)
+        project.pluginManager.apply(EarPlugin)
         project.convention.plugins.ear.deploymentDescriptor {
             fileName = "myapp.xml"
         }
diff --git a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarTest.groovy b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarTest.groovy
index edb6be7..6bd7aa3 100644
--- a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarTest.groovy
+++ b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/EarTest.groovy
@@ -16,8 +16,10 @@
 
 package org.gradle.plugins.ear
 
+import org.gradle.api.Action
 import org.gradle.api.tasks.bundling.AbstractArchiveTask
 import org.gradle.api.tasks.bundling.AbstractArchiveTaskTest
+import org.gradle.plugins.ear.descriptor.EarSecurityRole
 import org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor
 import org.junit.Before
 import org.junit.Test
@@ -28,7 +30,8 @@ class EarTest extends AbstractArchiveTaskTest {
 
     Ear ear
 
-    @Before public void setUp() {
+    @Before
+    public void setUp() {
         ear = createTask(Ear)
         configure(ear)
     }
@@ -37,21 +40,20 @@ class EarTest extends AbstractArchiveTaskTest {
         ear
     }
 
-    @Test public void testEar() {
+    @Test
+    public void testEar() {
         assertEquals(Ear.EAR_EXTENSION, ear.extension)
     }
 
-    @Test public void testLibDirName() {
-        ear.libDirName = "APP-INF/lib"
-        assertEquals(ear.libDirName, ear.lib.destPath as String)
-    }
 
-    @Test public void testDeploymentDescriptor() {
-        ear.deploymentDescriptor = new DefaultDeploymentDescriptor(null)
+    @Test
+    public void testDeploymentDescriptor() {
+        ear.deploymentDescriptor = new DefaultDeploymentDescriptor(null,instantiator)
         checkDeploymentDescriptor()
     }
 
-    @Test public void testDeploymentDescriptorWithNullManifest() {
+    @Test
+    public void testDeploymentDescriptorWithNullManifest() {
         ear.deploymentDescriptor = null
         checkDeploymentDescriptor()
     }
@@ -68,7 +70,11 @@ class EarTest extends AbstractArchiveTaskTest {
             module("my.jar", "java")
             webModule("my.war", "/")
             securityRole "admin"
-            securityRole "superadmin"
+            securityRole({ role->
+                role.roleName="superadmin"
+                role.description="Super Admin Role"
+            } as Action<EarSecurityRole>)
+
             withXml { provider ->
                 //just adds an action
             }
@@ -90,6 +96,7 @@ class EarTest extends AbstractArchiveTaskTest {
         assertEquals(2, d.securityRoles.size())
         assertEquals("admin", (d.securityRoles as List)[0].roleName)
         assertEquals("superadmin", (d.securityRoles as List)[1].roleName)
+        assertEquals("Super Admin Role", (d.securityRoles as List)[1].description)
         assertEquals(1, d.transformer.actions.size())
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptorTest.groovy b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptorTest.groovy
index 30757fd..38aef6b 100644
--- a/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptorTest.groovy
+++ b/subprojects/ear/src/test/groovy/org/gradle/plugins/ear/descriptor/internal/DefaultDeploymentDescriptorTest.groovy
@@ -16,7 +16,12 @@
 
 package org.gradle.plugins.ear.descriptor.internal
 
+import org.gradle.api.Action
+import org.gradle.api.internal.DependencyInjectingInstantiator
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.DefaultServiceRegistry
+import org.gradle.plugins.ear.descriptor.EarSecurityRole
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
@@ -26,7 +31,12 @@ import javax.xml.parsers.DocumentBuilderFactory
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
 
 class DefaultDeploymentDescriptorTest extends Specification {
-    def descriptor = new DefaultDeploymentDescriptor({ it } as FileResolver)
+
+    private DefaultServiceRegistry serviceRegistry = new DefaultServiceRegistry();
+    private Instantiator instantiator = new DependencyInjectingInstantiator(serviceRegistry);
+
+
+    def descriptor = new DefaultDeploymentDescriptor({ it } as FileResolver, instantiator)
     @Rule TestNameTestDirectoryProvider tmpDir
 
     def "writes default descriptor"() {
@@ -71,7 +81,10 @@ class DefaultDeploymentDescriptorTest extends Specification {
         descriptor.module("my.jar", "java")
         descriptor.webModule("my.war", "/")
         descriptor.securityRole "admin"
-        descriptor.securityRole "superadmin"
+        descriptor.securityRole({ role ->
+            role.roleName = "superadmin"
+            role.description = "Role of super admin"
+        } as Action<EarSecurityRole>)
         descriptor.withXml { it.asNode().appendNode("data-source", "my/data/source") }
 
         when:
@@ -98,6 +111,7 @@ class DefaultDeploymentDescriptorTest extends Specification {
     <role-name>admin</role-name>
   </security-role>
   <security-role>
+    <description>Role of super admin</description>
     <role-name>superadmin</role-name>
   </security-role>
   <library-directory>APP-INF/lib</library-directory>
diff --git a/subprojects/ide-native/ide-native.gradle b/subprojects/ide-native/ide-native.gradle
new file mode 100644
index 0000000..c7a0f37
--- /dev/null
+++ b/subprojects/ide-native/ide-native.gradle
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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(":ide")
+    compile project(':platformNative')
+    compile project(':languageNative')
+}
+
+useTestFixtures()
+useTestFixtures(project: ":platformNative")
+useClassycle()
+
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/AutoTestedSamplesIdeNativeIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/AutoTestedSamplesIdeNativeIntegrationTest.groovy
new file mode 100644
index 0000000..c950519
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/AutoTestedSamplesIdeNativeIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide
+
+import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+class AutoTestedSamplesIdeNativeIntegrationTest extends AbstractAutoTestedSamplesTest{
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/ide-native/src/main")
+    }
+
+}
+
+
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..566f237
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.visualstudio
+
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class NativeIdeSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule public final Sample visualStudio = sample(temporaryFolder, 'visual-studio')
+
+    private static Sample sample(TestDirectoryProvider testDirectoryProvider, String name) {
+        return new Sample(testDirectoryProvider, "native-binaries/${name}", name)
+    }
+
+    def "visual studio"() {
+        given:
+        sample visualStudio
+
+        when:
+        run "mainVisualStudio"
+
+        then:
+        final solutionFile = new SolutionFile(visualStudio.dir.file("vs/mainExe.sln"))
+        solutionFile.assertHasProjects("mainExe", "helloDll")
+        solutionFile.content.contains "GlobalSection(SolutionNotes) = postSolution"
+        solutionFile.content.contains "Text2 = The projects in this solution are [mainExe, helloDll]."
+
+        final projectFile = new ProjectFile(visualStudio.dir.file("vs/helloDll.vcxproj"))
+        projectFile.projectXml.PropertyGroup.find({it.'@Label' == 'Custom'}).ProjectDetails[0].text() == "Project is named helloDll"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.groovy
new file mode 100755
index 0000000..da12d38
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioFileCustomizationIntegrationTest.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.ide.visualstudio
+
+import org.gradle.ide.visualstudio.fixtures.FiltersFile
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+
+class VisualStudioFileCustomizationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def app = new CppHelloWorldApp()
+
+    def setup() {
+        app.writeSources(file("src/main"))
+        buildFile << """
+    apply plugin: 'cpp'
+    apply plugin: 'visual-studio'
+
+    model {
+        platforms {
+            win32 {
+                architecture "i386"
+            }
+        }
+        buildTypes {
+            debug
+            release
+        }
+        components {
+            main(NativeExecutableSpec)
+        }
+    }
+"""
+    }
+
+    def "can specify location of generated files"() {
+        when:
+        file("gradlew.bat") << "dummy wrapper"
+        buildFile << '''
+    model {
+        visualStudio {
+            projects.all { project ->
+                projectFile.location = "very/deeply/nested/${project.name}.vcxproj"
+                filtersFile.location = "other/filters.vcxproj.filters"
+            }
+            solutions.all {
+                solutionFile.location = "vs/${it.name}.solution"
+            }
+        }
+    }
+'''
+        and:
+        run "mainVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainExeVisualStudio"
+
+        and:
+        final projectFile = projectFile("very/deeply/nested/mainExe.vcxproj")
+        assert projectFile.headerFiles == app.headerFiles*.withPath("../../../src/main").sort()
+        assert projectFile.sourceFiles == ['../../../build.gradle'] + app.sourceFiles*.withPath("../../../src/main").sort()
+        projectFile.projectConfigurations.values().each {
+            assert it.buildCommand == "../../../gradlew.bat -p \"../../..\" :install${it.name.capitalize()}MainExecutable"
+            assert it.outputFile == OperatingSystem.current().getExecutableName("../../../build/install/mainExecutable/${it.name}/lib/main")
+        }
+        def filtersFile = filtersFile("other/filters.vcxproj.filters")
+
+        and:
+        final mainSolution = solutionFile("vs/mainExe.solution")
+        mainSolution.assertHasProjects("mainExe")
+        mainSolution.assertReferencesProject(projectFile, ['debug', 'release'])
+
+        // Ensure that clean handles custom file locations
+        when:
+        run "cleanVisualStudio"
+
+        then:
+        projectFile.projectFile.assertDoesNotExist()
+        filtersFile.file.assertDoesNotExist()
+        mainSolution.file.assertDoesNotExist()
+    }
+
+    def "can add xml configuration to generated project files"() {
+        when:
+        buildFile << """
+    model {
+        visualStudio {
+            projects.all { project ->
+                projectFile.withXml { xml ->
+                    Node globals = xml.asNode().PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
+                    globals.appendNode("ExtraInfo", "Some extra info")
+                    globals.appendNode("ProjectName", project.name)
+                }
+            }
+        }
+    }
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.globals.ExtraInfo[0].text() == "Some extra info"
+        projectFile.globals.ProjectName[0].text() == "mainExe"
+    }
+
+    def "can add xml configuration to generated filter files"() {
+        when:
+        buildFile << '''
+    model {
+        visualStudio {
+            projects.all { project ->
+                filtersFile.withXml { xml ->
+                    xml.asNode().appendNode("ExtraContent", "Filter - ${project.name}")
+                }
+            }
+        }
+    }
+'''
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final filtersFile = filtersFile("mainExe.vcxproj.filters")
+        filtersFile.xml.ExtraContent[0].text() == "Filter - mainExe"
+    }
+
+    def "can add text content to generated solution files"() {
+        when:
+        buildFile << '''
+    model {
+        visualStudio {
+            solutions.all { solution ->
+                solution.solutionFile.withContent { content ->
+                    String projectList = solution.projects.collect({it.name}).join(',')
+                    int insertPos = text.lastIndexOf("EndGlobal")
+                    content.text = content.text.replace("EndGlobal", """
+    GlobalSection(MyGlobalSection)
+       Project-list: ${projectList}
+    EndGlobalSection
+EndGlobal
+""")
+                }
+            }
+        }
+    }
+'''
+
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final solutionFile = solutionFile("mainExe.sln")
+        solutionFile.content.contains "GlobalSection(MyGlobalSection)"
+        solutionFile.content.contains "Project-list: mainExe"
+    }
+
+    def "can configure gradle command line"() {
+        when:
+        buildFile << """
+tasks.withType(GenerateProjectFileTask) {
+    it.gradleExe "myCustomGradleExe"
+    it.gradleArgs "--configure-on-demand --another"
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.projectConfigurations.values().each {
+            assert it.buildCommand == "myCustomGradleExe --configure-on-demand --another :install${it.name.capitalize()}MainExecutable"
+        }
+    }
+
+    private SolutionFile solutionFile(String path) {
+        return new SolutionFile(file(path))
+    }
+
+    private ProjectFile projectFile(String path) {
+        return new ProjectFile(file(path))
+    }
+
+    private FiltersFile filtersFile(String path) {
+        return new FiltersFile(file(path))
+    }
+}
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
new file mode 100755
index 0000000..962d94d
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.visualstudio
+
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
+
+class VisualStudioMultiProjectIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    private final Set<String> projectConfigurations = ['debug', 'release'] as Set
+
+    def app = new CppHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+            subprojects {
+                apply plugin: 'cpp'
+                apply plugin: 'visual-studio'
+
+                model {
+                    platforms {
+                        win32 {
+                            architecture "i386"
+                        }
+                    }
+                    buildTypes {
+                        debug
+                        release
+                    }
+                }
+            }
+        """
+    }
+
+    def "create visual studio solution for executable that depends on static library in another project"() {
+        when:
+        app.executable.writeSources(file("exe/src/main"))
+        app.library.writeSources(file("lib/src/hello"))
+
+        settingsFile.text = "include ':exe', ':lib'"
+        file("exe", "build.gradle") << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib project: ':lib', library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+        file("lib", "build.gradle") << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+        and:
+        run ":exe:mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
+        exeProject.assertHasComponentSources(app.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations.values().each {
+            assert it.includePath == filePath("src/main/headers", "../lib/src/hello/headers")
+            assert it.buildCommand == "gradle -p \"..\" :exe:install${it.name.capitalize()}MainExecutable"
+        }
+
+        and:
+        final libProject = projectFile("lib/lib_helloLib.vcxproj")
+        libProject.assertHasComponentSources(app.library, "src/hello")
+        libProject.projectConfigurations.keySet() == projectConfigurations
+        libProject.projectConfigurations.values().each {
+            assert it.includePath == filePath("src/hello/headers")
+            assert it.buildCommand == "gradle -p \"..\" :lib:${it.name}HelloStaticLibrary"
+        }
+
+        and:
+        final mainSolution = solutionFile("exe/exe_mainExe.sln")
+        mainSolution.assertHasProjects("exe_mainExe", "lib_helloLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(libProject, projectConfigurations)
+    }
+
+    def "create visual studio solution for executable that transitively depends on multiple projects"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("greet/src/greetings"))
+
+        and:
+        settingsFile.text = "include ':exe', ':lib', ':greet'"
+        buildFile << """
+project(":exe") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'hello'
+                }
+            }
+        }
+    }
+}
+project(":lib") {
+    apply plugin: "cpp"
+    model {
+        components {
+            hello(NativeLibrarySpec) {
+                sources {
+                    cpp.lib project: ':greet', library: 'greetings', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+project(":greet") {
+    apply plugin: "cpp"
+    model {
+        components {
+            greetings(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        when:
+        succeeds ":exe:mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
+        final helloProject = projectFile("lib/lib_helloDll.vcxproj")
+        final greetProject = projectFile("greet/greet_greetingsLib.vcxproj")
+        final mainSolution = solutionFile("exe/exe_mainExe.sln")
+
+        and:
+        mainSolution.assertHasProjects("exe_mainExe", "lib_helloDll", "greet_greetingsLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
+
+        and:
+        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/hello/headers")
+        helloProject.projectConfigurations['debug'].includePath == filePath("src/hello/headers", "../greet/src/greetings/headers")
+        greetProject.projectConfigurations['debug'].includePath == filePath("src/greetings/headers")
+    }
+
+    def "create visual studio solution where multiple components have same name"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.writeSources(file("exe/src/main"), file("lib/src/main"), file("greet/src/main"))
+
+        and:
+        settingsFile.text = "include ':exe', ':lib', ':greet'"
+        buildFile << """
+project(":exe") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'main'
+                }
+            }
+        }
+    }
+}
+project(":lib") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeLibrarySpec) {
+                sources {
+                    cpp.lib project: ':greet', library: 'main', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+project(":greet") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        when:
+        succeeds ":exe:mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
+        final helloProject = projectFile("lib/lib_mainDll.vcxproj")
+        final greetProject = projectFile("greet/greet_mainLib.vcxproj")
+        final mainSolution = solutionFile("exe/exe_mainExe.sln")
+
+        and:
+        mainSolution.assertHasProjects("exe_mainExe", "lib_mainDll", "greet_mainLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
+
+        and:
+        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/main/headers")
+        helloProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../greet/src/main/headers")
+        greetProject.projectConfigurations['debug'].includePath == filePath("src/main/headers")
+    }
+
+    def "create visual studio solution for executable with project dependency cycle"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("exe/src/greetings"))
+
+        and:
+        settingsFile.text = "include ':exe', ':lib'"
+        buildFile << """
+project(":exe") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'hello'
+                }
+            }
+            greetings(NativeLibrarySpec)
+        }
+    }
+}
+project(":lib") {
+    apply plugin: "cpp"
+    model {
+        components {
+            hello(NativeLibrarySpec) {
+                sources {
+                    cpp.lib project: ':exe', library: 'greetings', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds ":exe:mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
+        final helloProject = projectFile("lib/lib_helloDll.vcxproj")
+        final greetProject = projectFile("exe/exe_greetingsLib.vcxproj")
+        final mainSolution = solutionFile("exe/exe_mainExe.sln")
+
+        and:
+        mainSolution.assertHasProjects("exe_mainExe", "lib_helloDll", "exe_greetingsLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
+
+        and:
+        exeProject.projectConfigurations['debug'].includePath == filePath("src/main/headers", "../lib/src/hello/headers")
+        helloProject.projectConfigurations['debug'].includePath == filePath("src/hello/headers", "../exe/src/greetings/headers")
+        greetProject.projectConfigurations['debug'].includePath == filePath("src/greetings/headers")
+    }
+
+    def "detects gradle wrapper and uses in vs project"() {
+        when:
+        def gradlew = file("gradlew.bat") << "dummy wrapper"
+
+        settingsFile.text = "include ':exe'"
+        buildFile << """
+project(':exe') {
+    model {
+        components {
+            main(NativeExecutableSpec)
+        }
+    }
+}
+"""
+        and:
+        run ":exe:mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("exe/exe_mainExe.vcxproj")
+        exeProject.projectConfigurations.values().each {
+            assert it.buildCommand == "../gradlew.bat -p \"..\" :exe:install${it.name.capitalize()}MainExecutable"
+        }
+    }
+
+    def "cleanVisualStudio removes all generated visual studio files"() {
+        when:
+        settingsFile.text = "include ':exe', ':lib'"
+        buildFile << """
+project(':exe') {
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'main', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+project(':lib') {
+    model {
+        components {
+            main(NativeLibrarySpec)
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        def generatedFiles = [
+                file("exe/exe_mainExe.sln"),
+                file("exe/exe_mainExe.vcxproj"),
+                file("exe/exe_mainExe.vcxproj.filters"),
+                file("lib/lib_mainDll.sln"),
+                file("lib/lib_mainDll.vcxproj"),
+                file("lib/lib_mainDll.vcxproj.filters")
+        ]
+        generatedFiles*.assertExists()
+
+        when:
+        run "cleanVisualStudio"
+
+        then:
+        generatedFiles*.assertDoesNotExist()
+    }
+
+    private SolutionFile solutionFile(String path) {
+        return new SolutionFile(file(path))
+    }
+
+    private ProjectFile projectFile(String path) {
+        return new ProjectFile(file(path))
+    }
+
+    private static String filePath(String... paths) {
+        return (paths as List).join(";")
+    }
+}
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
new file mode 100755
index 0000000..dedf32d
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
@@ -0,0 +1,924 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.visualstudio
+
+import org.gradle.ide.visualstudio.fixtures.FiltersFile
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.*
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+
+class VisualStudioSingleProjectIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    private final Set<String> projectConfigurations = ['win32Debug', 'win32Release', 'x64Debug', 'x64Release'] as Set
+
+    def app = new CppHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: 'cpp'
+apply plugin: 'visual-studio'
+
+model {
+    platforms {
+        win32 {
+            architecture "i386"
+        }
+        x64 {
+            architecture "amd64"
+        }
+    }
+    buildTypes {
+        debug
+        release
+    }
+    components {
+        all {
+            targetPlatform "win32"
+            targetPlatform "x64"
+        }
+    }
+}
+"""
+    }
+
+    def "create visual studio solution for single executable"() {
+        when:
+        app.writeSources(file("src/main"))
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                cppCompiler.define "TEST"
+                cppCompiler.define "foo", "bar"
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainExeVisualStudio"
+
+        and:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.assertHasComponentSources(app, "src/main")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        projectFile.projectConfigurations.values().each {
+            assert it.macros == "TEST;foo=bar"
+            assert it.includePath == filePath("src/main/headers")
+            assert it.buildCommand == "gradle :install${it.name.capitalize()}MainExecutable"
+            assert it.outputFile == OperatingSystem.current().getExecutableName("build/install/mainExecutable/${it.name}/lib/main")
+        }
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe")
+        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
+    }
+
+    def "create visual studio solution for shared and library"() {
+        when:
+        app.library.writeSources(file("src/main"))
+        buildFile << """
+model {
+    components {
+        main(NativeLibrarySpec)
+    }
+}
+"""
+        and:
+        run "mainDllVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainDllVisualStudio"
+
+        and:
+        final projectFile = projectFile("mainDll.vcxproj")
+        projectFile.assertHasComponentSources(app.library, "src/main")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        projectFile.projectConfigurations.values().each {
+            assert it.includePath == filePath("src/main/headers")
+            assert it.buildCommand == "gradle :${it.name}MainSharedLibrary"
+            assert it.outputFile == OperatingSystem.current().getSharedLibraryName("build/binaries/mainSharedLibrary/${it.name}/main")
+        }
+
+        and:
+        final mainSolution = solutionFile("mainDll.sln")
+        mainSolution.assertHasProjects("mainDll")
+        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
+    }
+
+    def "create visual studio solution for static library"() {
+        when:
+        app.library.writeSources(file("src/main"))
+        buildFile << """
+model {
+    components {
+        main(NativeLibrarySpec)
+    }
+}
+"""
+        and:
+        run "mainLibVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainLibVisualStudio"
+
+        and:
+        final projectFile = projectFile("mainLib.vcxproj")
+        projectFile.assertHasComponentSources(app.library, "src/main")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        projectFile.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers")
+
+        and:
+        final mainSolution = solutionFile("mainLib.sln")
+        mainSolution.assertHasProjects("mainLib")
+        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
+    }
+
+    def "lifecycle task creates visual studio solution for buildable static and shared libraries"() {
+        when:
+        app.library.writeSources(file("src/main"))
+        buildFile << """
+model {
+    components {
+        both(NativeLibrarySpec)
+        staticOnly(NativeLibrarySpec) {
+            binaries.withType(SharedLibraryBinarySpec) {
+                buildable false
+            }
+        }
+    }
+}
+"""
+        and:
+        run "bothVisualStudio", "staticOnlyVisualStudio"
+
+        then:
+        executedAndNotSkipped ":bothDllVisualStudio", ":bothLibVisualStudio", ":staticOnlyLibVisualStudio"
+
+        and:
+        file("staticOnlyLib.sln").assertExists()
+        file("staticOnlyDll.sln").assertDoesNotExist()
+    }
+
+    def "create visual studio solution for defined static library"() {
+        when:
+        app.library.writeSources(file("src/main"))
+        buildFile << """
+model {
+    components {
+        main(NativeLibrarySpec) {
+            binaries.withType(SharedLibraryBinarySpec) {
+                buildable = false
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainLibVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainLibVisualStudio"
+
+        and:
+        final projectFile = projectFile("mainLib.vcxproj")
+        projectFile.assertHasComponentSources(app.library, "src/main")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        projectFile.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers")
+
+        and:
+        final mainSolution = solutionFile("mainLib.sln")
+        mainSolution.assertHasProjects("mainLib")
+        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
+    }
+
+    def "create visual studio solution for executable that depends on static library"() {
+        when:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        exeProject.assertHasComponentSources(app.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
+
+        and:
+        final libProject = projectFile("helloLib.vcxproj")
+        libProject.assertHasComponentSources(app.library, "src/hello")
+        libProject.projectConfigurations.keySet() == projectConfigurations
+        libProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(libProject, projectConfigurations)
+    }
+
+    def "create visual studio solution for executable that depends on shared library"() {
+        when:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        exeProject.assertHasComponentSources(app.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
+
+        and:
+        final dllProject = projectFile("helloDll.vcxproj")
+        dllProject.assertHasComponentSources(app.library, "src/hello")
+        dllProject.projectConfigurations.keySet() == projectConfigurations
+        dllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloDll")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(dllProject, projectConfigurations)
+    }
+
+    def "create visual studio solution for executable that depends on library that depends on another library"() {
+        given:
+        def testApp = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        greetings(NativeLibrarySpec)
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'greetings', linkage: 'static'
+            }
+        }
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+        when:
+        run "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        exeProject.assertHasComponentSources(testApp.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers")
+
+        and:
+        final helloDllProject = projectFile("helloDll.vcxproj")
+        helloDllProject.assertHasComponentSources(testApp.library, "src/hello")
+        helloDllProject.projectConfigurations.keySet() == projectConfigurations
+        helloDllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers", "src/greetings/headers")
+
+        and:
+        final greetingsLibProject = projectFile("greetingsLib.vcxproj")
+        greetingsLibProject.assertHasComponentSources(testApp.greetingsLibrary, "src/greetings")
+        greetingsLibProject.projectConfigurations.keySet() == projectConfigurations
+        greetingsLibProject.projectConfigurations['win32Debug'].includePath == filePath("src/greetings/headers")
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloDllProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetingsLibProject, projectConfigurations)
+    }
+
+    def "create visual studio solutions for 2 executables that depend on different linkages of the same library"() {
+        when:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+        mainStatic(NativeExecutableSpec) {
+            sources {
+                cpp.source.srcDirs "src/main/cpp"
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio", "mainStaticVisualStudio"
+
+        then:
+        solutionFile("mainExe.sln").assertHasProjects("mainExe", "helloDll")
+        solutionFile("mainStaticExe.sln").assertHasProjects("mainStaticExe", "helloLib")
+
+        and:
+        final exeProject = projectFile("mainExe.vcxproj")
+        final staticExeProject = projectFile("mainStaticExe.vcxproj")
+        exeProject.sourceFiles == staticExeProject.sourceFiles
+        exeProject.headerFiles == []
+        staticExeProject.headerFiles == []
+
+        and:
+        final dllProject = projectFile("helloDll.vcxproj")
+        final libProject = projectFile("helloLib.vcxproj")
+        dllProject.sourceFiles == libProject.sourceFiles
+        dllProject.headerFiles == libProject.headerFiles
+    }
+
+    def "create visual studio solutions for 2 executables that depend on different build types of the same library"() {
+        when:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+        mainRelease(NativeExecutableSpec) {
+            targetBuildTypes "release"
+            sources {
+                cpp.source.srcDirs "src/main/cpp"
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio", "mainReleaseVisualStudio"
+
+        then:
+        solutionFile("mainExe.sln").assertHasProjects("mainExe", "helloDll")
+        solutionFile("mainReleaseExe.sln").assertHasProjects("mainReleaseExe", "helloDll")
+
+        and:
+        final helloProjectFile = projectFile("helloDll.vcxproj")
+        helloProjectFile.projectConfigurations.keySet() == projectConfigurations
+        final mainProjectFile = projectFile("mainExe.vcxproj")
+        mainProjectFile.projectConfigurations.keySet() == projectConfigurations
+        final mainReleaseProjectFile = projectFile("mainReleaseExe.vcxproj")
+        mainReleaseProjectFile.projectConfigurations.keySet() == ['win32', 'x64'] as Set
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertReferencesProject(helloProjectFile, projectConfigurations)
+        final mainReleaseSolution = solutionFile("mainReleaseExe.sln")
+        mainReleaseSolution.assertReferencesProject(helloProjectFile, [win32: 'win32Release', x64: 'x64Release'])
+    }
+
+    def "create visual studio project for executable that targets multiple platforms with the same architecture"() {
+        when:
+        app.writeSources(file("src/main"))
+        buildFile << """
+model {
+    platforms {
+        otherWin32 {
+            architecture "i386"
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "otherWin32"
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final mainProjectFile = projectFile("mainExe.vcxproj")
+        mainProjectFile.projectConfigurations.keySet() == ['win32Debug', 'otherWin32Debug', 'win32Release', 'otherWin32Release', 'x64Debug', 'x64Release'] as Set
+    }
+
+    def "create visual studio solution for executable that has diamond dependency"() {
+        def testApp = new ExeWithDiamondDependencyHelloWorldApp()
+        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello"
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        greetings(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        succeeds "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        final helloProject = projectFile("helloDll.vcxproj")
+        final greetProject = projectFile("greetingsLib.vcxproj")
+        final mainSolution = solutionFile("mainExe.sln")
+
+        and:
+        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetProject, projectConfigurations)
+
+        and:
+        exeProject.assertHasComponentSources(testApp.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers", "src/greetings/headers")
+    }
+
+    def "create visual studio solution for executable that depends on both static and shared linkage of library"() {
+        given:
+        def testApp = new ExeWithDiamondDependencyHelloWorldApp()
+        testApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello", linkage: "shared"
+                cpp.lib library: "greetings", linkage: "shared"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        greetings(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        succeeds "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        final helloProject = projectFile("helloDll.vcxproj")
+        final greetDllProject = projectFile("greetingsDll.vcxproj")
+        final greetLibProject = projectFile("greetingsLib.vcxproj")
+        final mainSolution = solutionFile("mainExe.sln")
+
+        and:
+        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib", "greetingsDll")
+        mainSolution.assertReferencesProject(exeProject, projectConfigurations)
+        mainSolution.assertReferencesProject(helloProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetDllProject, projectConfigurations)
+        mainSolution.assertReferencesProject(greetLibProject, projectConfigurations)
+
+        and:
+        exeProject.assertHasComponentSources(testApp.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == projectConfigurations
+        exeProject.projectConfigurations['win32Debug'].includePath == filePath("src/main/headers", "src/hello/headers", "src/greetings/headers")
+
+        and:
+        helloProject.assertHasComponentSources(testApp.library, "src/hello")
+        helloProject.projectConfigurations.keySet() == projectConfigurations
+        helloProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers", "src/greetings/headers")
+    }
+
+    def "generate visual studio solution for executable with mixed sources"() {
+        given:
+        def testApp = new MixedLanguageHelloWorldApp(toolChain)
+        testApp.writeSources(file("src/main"))
+
+        and:
+        buildFile << """
+apply plugin: 'c'
+apply plugin: 'assembler'
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        when:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.assertHasComponentSources(testApp, "src/main")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        with (projectFile.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/main/headers")
+        }
+
+        and:
+        solutionFile("mainExe.sln").assertHasProjects("mainExe")
+    }
+
+    @RequiresInstalledToolChain(VisualCpp)
+    def "generate visual studio solution for executable with windows resource files"() {
+        given:
+        def resourceApp = new WindowsResourceHelloWorldApp()
+        resourceApp.writeSources(file("src/main"))
+
+        and:
+        buildFile << """
+apply plugin: 'windows-resources'
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+binaries.all {
+    rcCompiler.define "TEST"
+    rcCompiler.define "foo", "bar"
+}
+"""
+
+        when:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        final List<SourceFile> resources = resourceApp.resourceSources
+        final List<SourceFile> sources = resourceApp.sourceFiles - resources
+        assert projectFile.headerFiles == resourceApp.headerFiles*.withPath("src/main").sort()
+        assert projectFile.sourceFiles == ['build.gradle'] + sources*.withPath("src/main").sort()
+        assert projectFile.resourceFiles == resources*.withPath("src/main").sort()
+
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        with (projectFile.projectConfigurations['win32Debug']) {
+            macros == "TEST;foo=bar"
+            includePath == filePath("src/main/headers")
+        }
+
+        and:
+        solutionFile("mainExe.sln").assertHasProjects("mainExe")
+    }
+
+    def "builds solution for component with no source"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        when:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.sourceFiles == ['build.gradle']
+        projectFile.headerFiles == []
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+        with (projectFile.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/main/headers")
+        }
+
+        and:
+        solutionFile("mainExe.sln").assertHasProjects("mainExe")
+    }
+
+    def "visual studio project includes headers co-located with sources"() {
+        when:
+        // Write headers so they sit with sources
+        app.allFiles.each {
+            it.writeToFile(file("src/main/cpp/${it.name}"))
+        }
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.source.include "**/*.cpp"
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainExeVisualStudio"
+
+        and:
+        final projectFile = projectFile("mainExe.vcxproj")
+        assert projectFile.sourceFiles == ['build.gradle'] + app.sourceFiles.collect({"src/main/cpp/${it.name}"}).sort()
+        assert projectFile.headerFiles == app.headerFiles.collect({"src/main/cpp/${it.name}"}).sort()
+    }
+
+    def "visual studio solution with header-only library"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+
+        app.library.headerFiles*.writeToDir(file("src/helloApi"))
+        app.library.sourceFiles*.writeToDir(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'helloApi', linkage: 'api' // TODO:DAZ This should not be needed
+                cpp.lib library: 'hello'
+            }
+        }
+        helloApi(NativeLibrarySpec)
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'helloApi', linkage: 'api'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainVisualStudio"
+
+        then:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloDll", "helloApiDll")
+
+        and:
+        final mainExeProject = projectFile("mainExe.vcxproj")
+        with (mainExeProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/main/headers", "src/helloApi/headers", "src/hello/headers")
+        }
+
+        and:
+        final helloDllProject = projectFile("helloDll.vcxproj")
+        with (helloDllProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/hello/headers", "src/helloApi/headers")
+        }
+
+        and:
+        final helloApiDllProject = projectFile("helloApiDll.vcxproj")
+        with (helloApiDllProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/helloApi/headers")
+        }
+    }
+
+    def "create visual studio solution for executable with variant conditional sources"() {
+        when:
+        app.writeSources(file("src/win32"))
+        app.alternate.writeSources(file("src/x64"))
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all { binary ->
+                def platformName = binary.targetPlatform.name
+                sources {
+                    platformSources(CppSourceSet) {
+                        source.srcDir "src/\$platformName/cpp"
+                        exportedHeaders.srcDir "src/\$platformName/headers"
+                    }
+                }
+            }
+        }
+    }
+}
+
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final projectFile = projectFile("mainExe.vcxproj")
+        projectFile.assertHasComponentSources(app, "src/win32", app.alternate, "src/x64")
+        projectFile.projectConfigurations.keySet() == projectConfigurations
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe")
+        mainSolution.assertReferencesProject(projectFile, projectConfigurations)
+    }
+
+    def "visual studio solution with pre-built library"() {
+        given:
+        app.writeSources(file("src/main"))
+        buildFile << """
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            test {
+                headers.srcDir "libs/test/include"
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'test', linkage: 'api'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run "mainVisualStudio"
+
+        then:
+        executedAndNotSkipped ":mainExeVisualStudio"
+        and:
+
+        then:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe")
+
+        and:
+        final mainExeProject = projectFile("mainExe.vcxproj")
+        with (mainExeProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/main/headers", "libs/test/include")
+        }
+    }
+
+    def "visual studio solution for component graph with library dependency cycle"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        app.greetingsHeader.writeToDir(file("src/hello"))
+        app.greetingsSources*.writeToDir(file("src/greetings"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'greetings', linkage: 'static'
+            }
+        }
+        greetings(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainVisualStudio"
+
+        then:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloDll", "greetingsLib")
+
+        and:
+        final mainExeProject = projectFile("mainExe.vcxproj")
+        with (mainExeProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/main/headers", "src/hello/headers")
+        }
+
+        and:
+        final helloDllProject = projectFile("helloDll.vcxproj")
+        with (helloDllProject.projectConfigurations['win32Debug']) {
+            includePath == filePath( "src/hello/headers", "src/greetings/headers")
+        }
+
+        and:
+        final greetingsLibProject = projectFile("greetingsLib.vcxproj")
+        with (greetingsLibProject.projectConfigurations['win32Debug']) {
+            includePath == filePath("src/greetings/headers", "src/hello/headers")
+        }
+    }
+
+    def "create visual studio solution where referenced projects have different configurations"() {
+        when:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            targetBuildTypes "release"
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainVisualStudio"
+
+        then:
+        final exeProject = projectFile("mainExe.vcxproj")
+        exeProject.assertHasComponentSources(app.executable, "src/main")
+        exeProject.projectConfigurations.keySet() == ['win32', 'x64'] as Set
+        exeProject.projectConfigurations['win32'].includePath == filePath("src/main/headers", "src/hello/headers")
+
+        and:
+        final dllProject = projectFile("helloDll.vcxproj")
+        dllProject.assertHasComponentSources(app.library, "src/hello")
+        dllProject.projectConfigurations.keySet() == projectConfigurations
+        dllProject.projectConfigurations['win32Debug'].includePath == filePath("src/hello/headers")
+
+        and:
+        final mainSolution = solutionFile("mainExe.sln")
+        mainSolution.assertHasProjects("mainExe", "helloDll")
+        mainSolution.assertReferencesProject(exeProject, ['win32', 'x64'])
+        mainSolution.assertReferencesProject(dllProject, [win32: 'win32Release', x64: 'x64Release'])
+    }
+
+    private SolutionFile solutionFile(String path) {
+        return new SolutionFile(file(path))
+    }
+
+    private ProjectFile projectFile(String path) {
+        return new ProjectFile(file(path))
+    }
+
+    private FiltersFile filtersFile(String path) {
+        return new FiltersFile(file(path))
+    }
+
+    private static String filePath(String... paths) {
+        return (paths as List).join(';')
+    }
+}
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.groovy
new file mode 100644
index 0000000..cd86e58
--- /dev/null
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPluginIntegrationTest.groovy
@@ -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.ide.visualstudio.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class VisualStudioPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy
new file mode 100644
index 0000000..1a0f1ce
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/CdtIdePlugin.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.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.ide.cdt.model.ProjectSettings
+import org.gradle.ide.cdt.model.ProjectDescriptor
+import org.gradle.ide.cdt.model.CprojectSettings
+import org.gradle.ide.cdt.model.CprojectDescriptor
+
+import org.gradle.ide.cdt.tasks.GenerateMetadataFileTask
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin
+
+ at Incubating
+class CdtIdePlugin implements Plugin<Project> {
+
+    void apply(Project project) {
+        project.pluginManager.apply(NativeComponentPlugin)
+        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.componentSpecs]*.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/ide-native/src/main/groovy/org/gradle/ide/cdt/model/CprojectDescriptor.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/CprojectDescriptor.groovy
new file mode 100644
index 0000000..f0d9759
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/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.ide.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.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/ide-native/src/main/groovy/org/gradle/ide/cdt/model/CprojectSettings.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/CprojectSettings.groovy
new file mode 100644
index 0000000..077c291
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/CprojectSettings.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.language.cpp.CppSourceSet
+import org.gradle.nativeplatform.*
+
+/**
+ * Exposes a more logical view of the actual .cproject descriptor file
+ */
+ at Incubating
+class CprojectSettings {
+
+    NativeComponentSpec binary
+    private final ConfigurableFileCollection includeRoots
+    private final ConfigurableFileCollection libs
+
+    CprojectSettings(NativeComponentSpec 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 NativeLibrarySpec) {
+            type = "org.eclipse.cdt.build.core.buildArtefactType.sharedLib"
+        } else if (binary instanceof NativeExecutableSpec) {
+            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/ide-native/src/main/groovy/org/gradle/ide/cdt/model/ProjectDescriptor.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/ProjectDescriptor.groovy
new file mode 100644
index 0000000..15a61fe
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/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.ide.cdt.model
+
+import org.gradle.api.Incubating
+import org.gradle.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/ide/cdt/model/ProjectSettings.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/ProjectSettings.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/model/ProjectSettings.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/model/ProjectSettings.groovy
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/tasks/GenerateMetadataFileTask.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/tasks/GenerateMetadataFileTask.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/cdt/tasks/GenerateMetadataFileTask.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/cdt/tasks/GenerateMetadataFileTask.groovy
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/ConfigFile.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/ConfigFile.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/ConfigFile.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/ConfigFile.java
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/TextConfigFile.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/TextConfigFile.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/TextConfigFile.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/TextConfigFile.java
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/TextProvider.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/TextProvider.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/TextProvider.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/TextProvider.java
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioExtension.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioExtension.java
new file mode 100644
index 0000000..69719b0
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioExtension.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.ide.visualstudio;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectSet;
+
+/**
+ * The configuration for mapping a set of {@link org.gradle.nativeplatform.NativeComponentSpec}s to a Visual Studio project.
+ */
+ at Incubating
+public interface VisualStudioExtension {
+    /**
+     * The {@link VisualStudioProject}s generated.
+     */
+    NamedDomainObjectSet<? extends VisualStudioProject> getProjects();
+
+    /**
+     * The {@link VisualStudioSolution}s generated.
+     */
+    NamedDomainObjectSet<? extends VisualStudioSolution> getSolutions();
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.java
new file mode 100644
index 0000000..5c57d05
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioProject.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.ide.visualstudio;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.api.BuildableModelElement;
+import org.gradle.nativeplatform.NativeComponentSpec;
+
+/**
+ * A visual studio project, created from one or more {@link org.gradle.nativeplatform.NativeBinary} instances.
+ *
+ * <p/>
+ *
+ * The content and location of the generate project file can be modified by the supplied methods:
+ *
+ * <pre autoTested="true">
+ *  apply plugin: "visual-studio"
+ *  model {
+ *      visualStudio {
+ *          projects.all {
+ *              projectFile.location = "vs/${name}.vcxproj"
+ *              projectFile.withXml {
+ *                  asNode().appendNode('PropertyGroup', [Label: 'Custom'])
+ *                          .appendNode('ProjectDetails', "Project is named ${project.name}")
+ *              }
+ *          }
+ *      }
+ *  }
+ * </pre>
+ */
+ at Incubating
+public interface VisualStudioProject extends Named, BuildableModelElement {
+    /**
+     * The component that this project represents.
+     */
+    NativeComponentSpec getComponent();
+
+    /**
+     * Configuration for the generated project file.
+     */
+    XmlConfigFile getProjectFile();
+
+    /**
+     * Configuration for the generated filters file.
+     */
+    XmlConfigFile getFiltersFile();
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.java
new file mode 100644
index 0000000..f366a25
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/VisualStudioSolution.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.ide.visualstudio;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.api.BuildableModelElement;
+import org.gradle.nativeplatform.NativeComponentSpec;
+
+import java.util.Set;
+
+/**
+ * A visual studio solution, representing one or more {@link org.gradle.nativeplatform.NativeBinarySpec} instances
+ * from the same {@link org.gradle.nativeplatform.NativeComponentSpec}.
+ * <p/>
+ *
+ * The content and location of the generate solution file can be modified by the supplied methods:
+ *
+ * <pre autoTested="true">
+ *  apply plugin: "visual-studio"
+ *  model {
+ *      visualStudio {
+ *          solutions.all {
+ *              solutionFile.location = "vs/${name}.sln"
+ *              solutionFile.withContent { TextProvider content ->
+ *                  content.asBuilder().insert(0, "# GENERATED FILE: DO NOT EDIT\n")
+ *                  content.text = content.text.replaceAll("HideSolutionNode = FALSE", "HideSolutionNode = TRUE")
+ *              }
+ *          }
+ *      }
+ *  }
+ * </pre>
+ */
+ at Incubating
+public interface VisualStudioSolution extends Named, BuildableModelElement {
+    /**
+     * The set of projects included in this solution.
+     */
+    Set<VisualStudioProject> getProjects();
+
+    /**
+     * The component that this solution represents.
+     */
+    NativeComponentSpec getComponent();
+
+    /**
+     * Configuration for the generated solution file.
+     */
+    TextConfigFile getSolutionFile();
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/XmlConfigFile.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/XmlConfigFile.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/XmlConfigFile.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/XmlConfigFile.java
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.java
new file mode 100644
index 0000000..19905b3
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioExtension.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.ide.visualstudio.internal;
+
+import org.gradle.api.NamedDomainObjectSet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.ide.visualstudio.VisualStudioProject;
+import org.gradle.ide.visualstudio.VisualStudioSolution;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.internal.resolve.ProjectLocator;
+
+public class DefaultVisualStudioExtension implements VisualStudioExtensionInternal {
+    private final VisualStudioProjectRegistry projectRegistry;
+    private final VisualStudioSolutionRegistry solutionRegistry;
+
+    public DefaultVisualStudioExtension(Instantiator instantiator, ProjectLocator projectLocator, FileResolver fileResolver) {
+        VisualStudioProjectMapper projectMapper = new VisualStudioProjectMapper();
+        projectRegistry = new VisualStudioProjectRegistry(fileResolver, projectMapper, instantiator);
+        VisualStudioProjectResolver projectResolver = new VisualStudioProjectResolver(projectLocator);
+        solutionRegistry = new VisualStudioSolutionRegistry(fileResolver, projectResolver, instantiator);
+    }
+
+    public NamedDomainObjectSet<? extends VisualStudioProject> getProjects() {
+        return projectRegistry;
+    }
+
+    public VisualStudioProjectRegistry getProjectRegistry() {
+        return projectRegistry;
+    }
+
+    public NamedDomainObjectSet<? extends VisualStudioSolution> getSolutions() {
+        return solutionRegistry;
+    }
+
+    public VisualStudioSolutionRegistry getSolutionRegistry() {
+        return solutionRegistry;
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy
new file mode 100644
index 0000000..8395f31
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProject.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+import org.gradle.api.Action
+import org.gradle.api.XmlProvider
+import org.gradle.api.internal.AbstractBuildableModelElement
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.ide.visualstudio.VisualStudioProject
+import org.gradle.ide.visualstudio.XmlConfigFile
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.rc.WindowsResourceSet
+import org.gradle.nativeplatform.NativeBinary
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.NativeComponentSpec
+import org.gradle.util.CollectionUtils
+/**
+ * A VisualStudio project represents a set of binaries for a component that may vary in build type and target platform.
+ */
+class DefaultVisualStudioProject extends AbstractBuildableModelElement implements VisualStudioProject {
+    private final String name
+    private final DefaultConfigFile projectFile
+    private final DefaultConfigFile filtersFile
+    private final NativeComponentSpec component
+    private final List<File> additionalFiles = []
+    final Set<LanguageSourceSet> sources = new LinkedHashSet<LanguageSourceSet>()
+    private final Map<NativeBinary, VisualStudioProjectConfiguration> configurations = [:]
+
+    DefaultVisualStudioProject(String name, NativeComponentSpec component, FileResolver fileResolver, Instantiator instantiator) {
+        this.name = name
+        this.component = component
+        projectFile = instantiator.newInstance(DefaultConfigFile, fileResolver, "${name}.vcxproj" as String)
+        filtersFile = instantiator.newInstance(DefaultConfigFile, fileResolver, "${name}.vcxproj.filters" as String)
+    }
+
+    String getName() {
+        return name
+    }
+
+    DefaultConfigFile getProjectFile() {
+        return projectFile
+    }
+
+    DefaultConfigFile getFiltersFile() {
+        return filtersFile
+    }
+
+    NativeComponentSpec getComponent() {
+        return component
+    }
+
+    void addSourceFile(File sourceFile) {
+        additionalFiles << sourceFile
+    }
+
+    String getUuid() {
+        String projectPath = component.projectPath
+        String vsComponentPath = "${projectPath}:${name}"
+        return '{' + UUID.nameUUIDFromBytes(vsComponentPath.bytes).toString().toUpperCase() + '}'
+    }
+
+    void source(Collection<LanguageSourceSet> sources) {
+        this.sources.addAll(sources)
+        builtBy(sources)
+    }
+
+    List<File> getSourceFiles() {
+        def allSource = [] as Set
+        allSource.addAll additionalFiles
+        sources.each { LanguageSourceSet sourceSet ->
+            if (!(sourceSet instanceof WindowsResourceSet)) {
+                allSource.addAll sourceSet.source.files
+            }
+        }
+        return allSource as List
+    }
+
+    List<File> getResourceFiles() {
+        def allResources = [] as Set
+        sources.each { LanguageSourceSet sourceSet ->
+            if (sourceSet instanceof WindowsResourceSet) {
+                allResources.addAll sourceSet.source.files
+            }
+        }
+        return allResources as List
+    }
+
+    List<File> getHeaderFiles() {
+        def allHeaders = [] as Set
+        sources.each { LanguageSourceSet sourceSet ->
+            if (sourceSet instanceof HeaderExportingSourceSet) {
+                allHeaders.addAll sourceSet.exportedHeaders.files
+                allHeaders.addAll sourceSet.implicitHeaders.files
+            }
+        }
+        return allHeaders as List
+    }
+
+    List<VisualStudioProjectConfiguration> getConfigurations() {
+        return CollectionUtils.toList(configurations.values())
+    }
+
+    void addConfiguration(NativeBinarySpec nativeBinary, VisualStudioProjectConfiguration configuration) {
+        configurations[nativeBinary] = configuration
+        source nativeBinary.source
+    }
+
+    VisualStudioProjectConfiguration getConfiguration(NativeBinarySpec nativeBinary) {
+        return configurations[nativeBinary]
+    }
+
+    public static class DefaultConfigFile implements XmlConfigFile {
+        private final List<Action<? super XmlProvider>> actions = new ArrayList<Action<? super XmlProvider>>();
+        private final FileResolver fileResolver
+        private Object location
+
+        DefaultConfigFile(FileResolver fileResolver, String defaultLocation) {
+            this.fileResolver = fileResolver
+            this.location = defaultLocation
+        }
+
+        File getLocation() {
+            return fileResolver.resolve(location)
+        }
+
+        void setLocation(Object location) {
+            this.location = location
+        }
+
+        void withXml(Action<? super XmlProvider> action) {
+            actions.add(action)
+        }
+
+        List<Action<? super XmlProvider>> getXmlActions() {
+            return actions
+        }
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.groovy
new file mode 100644
index 0000000..a3aaa48
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioSolution.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.ide.visualstudio.internal
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.ide.visualstudio.TextConfigFile
+import org.gradle.ide.visualstudio.TextProvider
+import org.gradle.ide.visualstudio.VisualStudioProject
+import org.gradle.ide.visualstudio.VisualStudioSolution
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.api.internal.AbstractBuildableModelElement
+import org.gradle.nativeplatform.NativeLibraryBinary
+import org.gradle.nativeplatform.NativeComponentSpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+
+class DefaultVisualStudioSolution extends AbstractBuildableModelElement implements VisualStudioSolution {
+    final DefaultVisualStudioProject rootProject
+    private final String name
+    private final SolutionFile solutionFile
+    private final VisualStudioProjectResolver vsProjectResolver
+
+    DefaultVisualStudioSolution(DefaultVisualStudioProject rootProject, FileResolver fileResolver,
+                                VisualStudioProjectResolver vsProjectResolver, Instantiator instantiator) {
+        this.rootProject = rootProject
+        this.name = rootProject.name
+        this.vsProjectResolver = vsProjectResolver
+        this.solutionFile = instantiator.newInstance(SolutionFile, fileResolver, "${name}.sln" as String)
+    }
+
+    String getName() {
+        return name
+    }
+
+    SolutionFile getSolutionFile() {
+        return solutionFile
+    }
+
+    NativeComponentSpec getComponent() {
+        return rootProject.component
+    }
+
+    Set<VisualStudioProject> getProjects() {
+        def projects = [] as Set
+        solutionConfigurations.each { solutionConfig ->
+            getProjectConfigurations(solutionConfig).each { projectConfig ->
+                projects << projectConfig.project
+            }
+        }
+        return projects
+    }
+
+    List<VisualStudioProjectConfiguration> getSolutionConfigurations() {
+        return rootProject.configurations
+    }
+
+    List<VisualStudioProjectConfiguration> getProjectConfigurations(VisualStudioProjectConfiguration solutionConfiguration) {
+        def configurations = [] as Set
+        configurations << solutionConfiguration
+        addDependentConfigurations(configurations, solutionConfiguration)
+        return configurations as List
+    }
+
+    private void addDependentConfigurations(Set configurations, VisualStudioProjectConfiguration configuration) {
+        for (NativeLibraryBinary library : configuration.binary.dependentBinaries) {
+            if (library instanceof NativeBinarySpecInternal) {
+                VisualStudioProjectConfiguration libraryConfiguration = vsProjectResolver.lookupProjectConfiguration(library);
+                if (configurations.add(libraryConfiguration)) {
+                    addDependentConfigurations(configurations, libraryConfiguration)
+                }
+            }
+        }
+    }
+
+    static class SolutionFile implements TextConfigFile {
+        private final List<Action<? super TextProvider>> actions = new ArrayList<Action<? super TextProvider>>();
+        private final FileResolver fileResolver
+        private Object location
+
+        SolutionFile(FileResolver fileResolver, String defaultLocation) {
+            this.fileResolver = fileResolver
+            this.location = defaultLocation
+        }
+
+        File getLocation() {
+            return fileResolver.resolve(location)
+        }
+
+        void setLocation(Object location) {
+            this.location = location
+        }
+
+        void withContent(Action<? super TextProvider> action) {
+            actions.add(action)
+        }
+
+        List<Action<? super TextProvider>> getTextActions() {
+            return actions
+        }
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.groovy
new file mode 100644
index 0000000..24474bb
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/ExecutableVisualStudioProjectConfiguration.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.ide.visualstudio.internal
+
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.tasks.InstallExecutable
+
+class ExecutableVisualStudioProjectConfiguration extends VisualStudioProjectConfiguration {
+    ExecutableVisualStudioProjectConfiguration(DefaultVisualStudioProject vsProject, String configurationName, String platformName, NativeBinarySpec binary) {
+        super(vsProject, configurationName, platformName, binary)
+    }
+
+    @Override
+    String getBuildTask() {
+        InstallExecutable installTask = getInstallTask()
+        return installTask == null ? super.getBuildTask() : installTask.path
+    }
+
+    @Override
+    File getOutputFile() {
+        InstallExecutable installTask = getInstallTask()
+        if (installTask == null) {
+            return super.getOutputFile()
+        } else {
+            return new File(installTask.destinationDir, "lib/" + installTask.executable.name)
+        }
+    }
+
+    private InstallExecutable getInstallTask() {
+        final installTasks = binary.tasks.withType(InstallExecutable)
+        return installTasks.empty ? null : installTasks.iterator().next()
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioExtensionInternal.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioExtensionInternal.java
new file mode 100644
index 0000000..02194ef
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioExtensionInternal.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal;
+
+import org.gradle.ide.visualstudio.VisualStudioExtension;
+
+public interface VisualStudioExtensionInternal extends VisualStudioExtension {
+    VisualStudioProjectRegistry getProjectRegistry();
+
+    VisualStudioSolutionRegistry getSolutionRegistry();
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy
new file mode 100644
index 0000000..03066d9
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfiguration.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.language.PreprocessingTool
+import org.gradle.nativeplatform.toolchain.internal.MacroArgsConverter
+
+class VisualStudioProjectConfiguration {
+    private final DefaultVisualStudioProject vsProject
+    private final String configurationName
+    private final String platformName
+    final NativeBinarySpecInternal binary
+    final String type = "Makefile"
+
+    VisualStudioProjectConfiguration(DefaultVisualStudioProject vsProject, String configurationName, String platformName, NativeBinarySpec binary) {
+        this.vsProject = vsProject
+        this.configurationName = configurationName
+        this.platformName = platformName
+        this.binary = binary as NativeBinarySpecInternal
+    }
+
+    String getName() {
+        return "${configurationName}|${platformName}"
+    }
+
+    String getConfigurationName() {
+        return configurationName
+    }
+
+    String getPlatformName() {
+        return platformName
+    }
+
+    String getBuildTask() {
+        return binary.tasks.build.path
+    }
+
+    String getCleanTask() {
+        return taskPath("clean")
+    }
+
+    private String taskPath(String taskName) {
+        String projectPath = binary.component.projectPath
+        if (projectPath == ":") {
+            return ":${taskName}"
+        }
+        return "${projectPath}:${taskName}"
+    }
+
+    File getOutputFile() {
+        return binary.primaryOutput
+    }
+
+    boolean isDebug() {
+        return binary.buildType.name != 'release'
+    }
+
+    List<String> getCompilerDefines() {
+        List<String> defines = []
+        defines.addAll getDefines('cCompiler')
+        defines.addAll getDefines('cppCompiler')
+        defines.addAll getDefines('rcCompiler')
+        return defines
+    }
+
+    private List<String> getDefines(String tool) {
+        PreprocessingTool rcCompiler = findCompiler(tool)
+        return rcCompiler == null ? [] : new MacroArgsConverter().transform(rcCompiler.macros)
+    }
+
+    private PreprocessingTool findCompiler(String tool) {
+        ExtensionAware extendedBinary = binary as ExtensionAware;
+        return extendedBinary.extensions.findByName(tool) as PreprocessingTool
+    }
+
+    List<File> getIncludePaths() {
+        def includes = [] as Set
+        binary.source.withType(HeaderExportingSourceSet).each { HeaderExportingSourceSet sourceSet ->
+            includes.addAll sourceSet.exportedHeaders.srcDirs
+        }
+        binary.libs*.includeRoots.each {
+            includes.addAll it.files
+        }
+        return includes as List
+    }
+
+    DefaultVisualStudioProject getProject() {
+        return vsProject
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java
new file mode 100644
index 0000000..6c9163f
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeExecutableBinarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.StaticLibraryBinarySpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+
+import java.util.List;
+
+public class VisualStudioProjectMapper {
+
+    public ProjectConfigurationNames mapToConfiguration(NativeBinarySpec nativeBinary) {
+        String projectName = projectPrefix(nativeBinary) + componentName(nativeBinary) + projectSuffix(nativeBinary);
+        String configurationName = getConfigurationName(nativeBinary);
+        return new ProjectConfigurationNames(projectName, configurationName, "Win32");
+    }
+
+    private String getConfigurationName(NativeBinarySpec nativeBinary) {
+        List<String> dimensions = ((NativeBinarySpecInternal) nativeBinary).getNamingScheme().getVariantDimensions();
+        if (dimensions.isEmpty()) {
+            return nativeBinary.getBuildType().getName();
+        }
+        return makeName(dimensions);
+    }
+
+    private String projectPrefix(NativeBinarySpec nativeBinary) {
+        String projectPath = nativeBinary.getComponent().getProjectPath();
+        if (":".equals(projectPath)) {
+            return "";
+        }
+        return projectPath.substring(1).replace(":", "_") + "_";
+    }
+
+    private String componentName(NativeBinarySpec nativeBinary) {
+        return nativeBinary.getComponent().getName();
+    }
+
+    private String projectSuffix(NativeBinarySpec nativeBinary) {
+        return nativeBinary instanceof SharedLibraryBinarySpec ? "Dll"
+                : nativeBinary instanceof StaticLibraryBinarySpec ? "Lib"
+                : nativeBinary instanceof NativeExecutableBinarySpec ? "Exe"
+                : nativeBinary instanceof NativeTestSuiteBinarySpec ? "Exe"
+                : "";
+    }
+
+    private static String makeName(Iterable<String> components) {
+        StringBuilder builder = new StringBuilder();
+        for (String component : components) {
+            if (component != null && component.length() > 0) {
+                if (builder.length() == 0) {
+                    builder.append(component);
+                } else {
+                    builder.append(StringUtils.capitalize(component));
+                }
+            }
+        }
+        return builder.toString();
+    }
+
+    static class ProjectConfigurationNames {
+        public final String project;
+        public final String configuration;
+        public final String platform;
+
+        ProjectConfigurationNames(String project, String configuration, String platform) {
+            this.project = project;
+            this.configuration = configuration;
+            this.platform = platform;
+        }
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.java
new file mode 100644
index 0000000..bb4d003
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistry.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.ide.visualstudio.internal;
+
+import org.gradle.api.internal.DefaultNamedDomainObjectSet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.*;
+
+public class VisualStudioProjectRegistry extends DefaultNamedDomainObjectSet<DefaultVisualStudioProject> {
+    private final FileResolver fileResolver;
+    private final VisualStudioProjectMapper projectMapper;
+
+    public VisualStudioProjectRegistry(FileResolver fileResolver, VisualStudioProjectMapper projectMapper, Instantiator instantiator) {
+        super(DefaultVisualStudioProject.class, instantiator);
+        this.fileResolver = fileResolver;
+        this.projectMapper = projectMapper;
+    }
+
+    public VisualStudioProjectConfiguration getProjectConfiguration(NativeBinarySpec nativeBinary) {
+        String projectName = projectName(nativeBinary);
+        return getByName(projectName).getConfiguration(nativeBinary);
+    }
+
+    public VisualStudioProjectConfiguration addProjectConfiguration(NativeBinarySpec nativeBinary) {
+        VisualStudioProjectMapper.ProjectConfigurationNames names = projectMapper.mapToConfiguration(nativeBinary);
+        DefaultVisualStudioProject project = getOrCreateProject(nativeBinary.getComponent(), names.project);
+        VisualStudioProjectConfiguration configuration = createVisualStudioProjectConfiguration(nativeBinary, project, names.configuration, names.platform);
+        project.addConfiguration(nativeBinary, configuration);
+        return configuration;
+    }
+
+    private VisualStudioProjectConfiguration createVisualStudioProjectConfiguration(NativeBinarySpec nativeBinary, DefaultVisualStudioProject project, String configuration, String platform) {
+        Class<? extends VisualStudioProjectConfiguration> type =
+                nativeBinary instanceof NativeExecutableBinarySpec ? ExecutableVisualStudioProjectConfiguration.class : VisualStudioProjectConfiguration.class;
+        return getInstantiator().newInstance(type, project, configuration, platform, nativeBinary);
+    }
+
+    private DefaultVisualStudioProject getOrCreateProject(NativeComponentSpec nativeComponent, String projectName) {
+        DefaultVisualStudioProject vsProject = findByName(projectName);
+        if (vsProject == null) {
+            vsProject = getInstantiator().newInstance(DefaultVisualStudioProject.class, projectName, nativeComponent, fileResolver, getInstantiator());
+            add(vsProject);
+        }
+        return vsProject;
+    }
+
+    private String projectName(NativeBinarySpec nativeBinary) {
+        return projectMapper.mapToConfiguration(nativeBinary).project;
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.java
new file mode 100644
index 0000000..575b776
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectResolver.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.ide.visualstudio.internal;
+
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.ide.visualstudio.VisualStudioExtension;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.internal.resolve.ProjectLocator;
+
+public class VisualStudioProjectResolver {
+    private final ProjectLocator projectLocator;
+
+    public VisualStudioProjectResolver(ProjectLocator projectLocator) {
+        this.projectLocator = projectLocator;
+    }
+
+    public VisualStudioProjectConfiguration lookupProjectConfiguration(NativeBinarySpec nativeBinary) {
+        // Looks in the correct project registry for this binary
+        ProjectInternal componentProject = getComponentProject(nativeBinary);
+        VisualStudioExtension visualStudioExtension = componentProject.getModelRegistry().realize(ModelPath.path("visualStudio"), ModelType.of(VisualStudioExtension.class));
+        VisualStudioProjectRegistry projectRegistry = ((VisualStudioExtensionInternal) visualStudioExtension).getProjectRegistry();
+        return projectRegistry.getProjectConfiguration(nativeBinary);
+    }
+
+    private ProjectInternal getComponentProject(NativeBinarySpec nativeBinary) {
+        String projectPath = nativeBinary.getComponent().getProjectPath();
+        return projectLocator.locateProject(projectPath);
+    }
+}
+
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioSolutionRegistry.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioSolutionRegistry.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioSolutionRegistry.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/internal/VisualStudioSolutionRegistry.java
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/package-info.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/package-info.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/package-info.java
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.java
new file mode 100644
index 0000000..08fcce2
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/VisualStudioPlugin.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.tasks.Delete;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.ide.visualstudio.VisualStudioProject;
+import org.gradle.ide.visualstudio.VisualStudioSolution;
+import org.gradle.ide.visualstudio.internal.DefaultVisualStudioExtension;
+import org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject;
+import org.gradle.ide.visualstudio.internal.VisualStudioExtensionInternal;
+import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration;
+import org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask;
+import org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask;
+import org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.resolve.ProjectLocator;
+import org.gradle.nativeplatform.plugins.NativeComponentModelPlugin;
+import org.gradle.platform.base.BinaryContainer;
+
+
+/**
+ * A plugin for creating a Visual Studio solution for a gradle project.
+ */
+ at Incubating
+public class VisualStudioPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentModelPlugin.class);
+    }
+
+    static class Rules extends RuleSource {
+        @Model
+        public static VisualStudioExtensionInternal visualStudio(ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            ProjectLocator projectLocator = serviceRegistry.get(ProjectLocator.class);
+            FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+
+            return instantiator.newInstance(DefaultVisualStudioExtension.class, instantiator, projectLocator, fileResolver);
+        }
+
+        @Mutate
+        public static void includeBuildFileInProject(VisualStudioExtensionInternal visualStudio, final ProjectIdentifier projectIdentifier) {
+            visualStudio.getProjects().all(new Action<VisualStudioProject>() {
+                public void execute(VisualStudioProject project) {
+                    if (projectIdentifier.getBuildFile() != null) {
+                        ((DefaultVisualStudioProject) project).addSourceFile(projectIdentifier.getBuildFile());
+                    }
+                }
+            });
+        }
+
+        @Mutate
+        @SuppressWarnings("GroovyUnusedDeclaration")
+        public static void createVisualStudioModelForBinaries(VisualStudioExtensionInternal visualStudioExtension, BinaryContainer binaryContainer) {
+            for (NativeBinarySpec binary : binaryContainer.withType(NativeBinarySpec.class)) {
+                VisualStudioProjectConfiguration configuration = visualStudioExtension.getProjectRegistry().addProjectConfiguration(binary);
+
+                // Only create a solution if one of the binaries is buildable
+                if (binary.isBuildable()) {
+                    DefaultVisualStudioProject visualStudioProject = configuration.getProject();
+                    visualStudioExtension.getSolutionRegistry().addSolution(visualStudioProject);
+                }
+            }
+        }
+
+        @Mutate
+        @SuppressWarnings("GroovyUnusedDeclaration")
+        public static void createTasksForVisualStudio(TaskContainer tasks, VisualStudioExtensionInternal visualStudioExtension) {
+            for (VisualStudioProject vsProject : visualStudioExtension.getProjects()) {
+                vsProject.builtBy(createProjectsFileTask(tasks, vsProject));
+                vsProject.builtBy(createFiltersFileTask(tasks, vsProject));
+            }
+
+            for (VisualStudioSolution vsSolution : visualStudioExtension.getSolutions()) {
+                Task solutionTask = tasks.create(vsSolution.getName() + "VisualStudio");
+                solutionTask.setDescription(String.format("Generates the '%s' Visual Studio solution file.", vsSolution.getName()));
+                vsSolution.setBuildTask(solutionTask);
+                vsSolution.builtBy(createSolutionTask(tasks, vsSolution));
+
+                // Lifecycle task for component
+                NativeComponentSpec component = vsSolution.getComponent();
+                Task lifecycleTask = tasks.maybeCreate(component.getName() + "VisualStudio");
+                lifecycleTask.dependsOn(vsSolution);
+                lifecycleTask.setGroup("IDE");
+                lifecycleTask.setDescription(String.format("Generates the Visual Studio solution for %s.", component));
+            }
+
+            addCleanTask(tasks);
+        }
+
+        private static void addCleanTask(TaskContainer tasks) {
+            Delete cleanTask = tasks.create("cleanVisualStudio", Delete.class);
+            for (Task task : tasks.withType(GenerateSolutionFileTask.class)) {
+                cleanTask.delete(task.getOutputs().getFiles());
+            }
+            for (Task task : tasks.withType(GenerateFiltersFileTask.class)) {
+                cleanTask.delete(task.getOutputs().getFiles());
+            }
+            for (Task task : tasks.withType(GenerateProjectFileTask.class)) {
+                cleanTask.delete(task.getOutputs().getFiles());
+            }
+            cleanTask.setGroup("IDE");
+            cleanTask.setDescription("Removes all generated Visual Studio project and solution files");
+        }
+
+        private static Task createSolutionTask(TaskContainer tasks, VisualStudioSolution solution) {
+            GenerateSolutionFileTask solutionFileTask = tasks.create(solution.getName() + "VisualStudioSolution", GenerateSolutionFileTask.class);
+            solutionFileTask.setVisualStudioSolution(solution);
+            return solutionFileTask;
+        }
+
+        private static Task createProjectsFileTask(TaskContainer tasks, VisualStudioProject vsProject) {
+            GenerateProjectFileTask task = tasks.create(vsProject.getName() + "VisualStudioProject", GenerateProjectFileTask.class);
+            task.setVisualStudioProject(vsProject);
+            task.initGradleCommand();
+            return task;
+        }
+
+        private static Task createFiltersFileTask(TaskContainer tasks, VisualStudioProject vsProject) {
+            GenerateFiltersFileTask task = tasks.create(vsProject.getName() + "VisualStudioFilters", GenerateFiltersFileTask.class);
+            task.setVisualStudioProject(vsProject);
+            return task;
+        }
+    }
+}
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/package-info.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/package-info.java
new file mode 100644
index 0000000..d126c30
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Visual Studio integration.
+ */
+package org.gradle.ide.visualstudio.plugins;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateFiltersFileTask.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateFiltersFileTask.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateFiltersFileTask.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateFiltersFileTask.groovy
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateProjectFileTask.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateProjectFileTask.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateProjectFileTask.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateProjectFileTask.groovy
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateSolutionFileTask.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateSolutionFileTask.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateSolutionFileTask.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/GenerateSolutionFileTask.groovy
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/AbsoluteFileNameTransformer.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/AbsoluteFileNameTransformer.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/AbsoluteFileNameTransformer.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/AbsoluteFileNameTransformer.java
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformer.java b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformer.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformer.java
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformer.java
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
new file mode 100644
index 0000000..d9e34bc
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.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.ide.visualstudio.tasks.internal
+
+import org.gradle.api.Transformer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+class VisualStudioFiltersFile extends XmlPersistableConfigurationObject {
+    private final Transformer<String, File> fileLocationResolver
+
+    VisualStudioFiltersFile(XmlTransformer xmlTransformer, Transformer<String, File> fileLocationResolver) {
+        super(xmlTransformer)
+        this.fileLocationResolver = fileLocationResolver
+    }
+
+    protected String getDefaultResourceName() {
+        'default.vcxproj.filters'
+    }
+
+    def addSource(File sourceFile) {
+        sources.appendNode("ClCompile", [Include: toPath(sourceFile)]).appendNode('Filter', 'Source Files')
+    }
+
+    def addHeader(File headerFile) {
+        headers.appendNode("ClInclude", [Include: toPath(headerFile)]).appendNode('Filter', 'Header Files')
+    }
+
+    def getFilters() {
+        return xml.ItemGroup.findAll({ it.'@Label' == 'Filters' })[0]
+    }
+
+    private Node getSources() {
+        return xml.ItemGroup.find({ it.'@Label' == 'Sources' }) as Node
+    }
+
+    private Node getHeaders() {
+        return xml.ItemGroup.find({ it.'@Label' == 'Headers' }) as Node
+    }
+
+    private String toPath(File it) {
+        fileLocationResolver.transform(it)
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.groovy
new file mode 100644
index 0000000..c72cb9e
--- /dev/null
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFile.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.ide.visualstudio.tasks.internal
+import org.gradle.api.Transformer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
+import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
+
+class VisualStudioProjectFile extends XmlPersistableConfigurationObject {
+    private final Transformer<String, File> fileLocationResolver
+    String gradleCommand = 'gradle'
+
+    VisualStudioProjectFile(XmlTransformer xmlTransformer, Transformer<String, File> fileLocationResolver) {
+        super(xmlTransformer)
+        this.fileLocationResolver = fileLocationResolver
+    }
+
+    protected String getDefaultResourceName() {
+        'default.vcxproj'
+    }
+
+    def setProjectUuid(String uuid) {
+        Node globals = xml.PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
+        globals.appendNode("ProjectGUID", uuid)
+    }
+
+    def addSourceFile(File it) {
+        def sources = xml.ItemGroup.find({ it.'@Label' == 'Sources' }) as Node
+        sources.appendNode("ClCompile", [Include: toPath(it)])
+    }
+
+    def addResource(File it) {
+        def resources = xml.ItemGroup.find({ it.'@Label' == 'References' }) as Node
+        resources.appendNode("ResourceCompile", [Include: toPath(it)])
+    }
+
+    def addHeaderFile(File it) {
+        def headers = xml.ItemGroup.find({ it.'@Label' == 'Headers' }) as Node
+        headers.appendNode("ClInclude", [Include: toPath(it)])
+    }
+
+    def addConfiguration(VisualStudioProjectConfiguration configuration) {
+        def configNode = configurations.appendNode("ProjectConfiguration", [Include: configuration.name])
+        configNode.appendNode("Configuration", configuration.configurationName)
+        configNode.appendNode("Platform", configuration.platformName)
+        final configCondition = "'\$(Configuration)|\$(Platform)'=='${configuration.name}'"
+
+        def vsOutputDir = ".vs\\${configuration.project.name}\\\$(Configuration)"
+        Node defaultProps = xml.Import.find({ it.'@Project' == '$(VCTargetsPath)\\Microsoft.Cpp.Default.props'}) as Node
+        defaultProps + {
+            PropertyGroup(Label: "Configuration", Condition: configCondition) {
+                ConfigurationType(configuration.type)
+                UseDebugLibraries(configuration.debug)
+                OutDir(vsOutputDir)
+                IntDir(vsOutputDir)
+            }
+        }
+
+        final includePath = toPath(configuration.includePaths).join(";")
+        Node userMacros = xml.PropertyGroup.find({ it.'@Label' == 'UserMacros'}) as Node
+        userMacros + {
+            PropertyGroup(Label: "NMakeConfiguration", Condition: configCondition) {
+                NMakeBuildCommandLine("${gradleCommand} ${configuration.buildTask}")
+                NMakeCleanCommandLine("${gradleCommand} ${configuration.cleanTask}")
+                NMakeReBuildCommandLine("${gradleCommand} ${configuration.cleanTask} ${configuration.buildTask}")
+                NMakePreprocessorDefinitions(configuration.compilerDefines.join(";"))
+                NMakeIncludeSearchPath(includePath)
+                NMakeOutput(toPath(configuration.outputFile))
+            }
+        }
+    }
+
+    private Node getConfigurations() {
+        return xml.ItemGroup.find({ it.'@Label' == 'ProjectConfigurations' }) as Node
+    }
+
+    private List<String> toPath(List<File> files) {
+        return files.collect({toPath(it)})
+    }
+
+    private String toPath(File it) {
+        fileLocationResolver.transform(it)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFile.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFile.groovy
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFile.groovy
rename to subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFile.groovy
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/eclipse-cdt.properties b/subprojects/ide-native/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse-cdt.properties
similarity index 100%
rename from subprojects/cpp/src/main/resources/META-INF/gradle-plugins/eclipse-cdt.properties
rename to subprojects/ide-native/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse-cdt.properties
diff --git a/subprojects/cpp/src/main/resources/META-INF/gradle-plugins/visual-studio.properties b/subprojects/ide-native/src/main/resources/META-INF/gradle-plugins/org.gradle.visual-studio.properties
similarity index 100%
rename from subprojects/cpp/src/main/resources/META-INF/gradle-plugins/visual-studio.properties
rename to subprojects/ide-native/src/main/resources/META-INF/gradle-plugins/org.gradle.visual-studio.properties
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-linux.xml b/subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-linux.xml
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-linux.xml
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-linux.xml
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-macos.xml b/subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-macos.xml
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-macos.xml
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultCproject-macos.xml
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultProject.xml b/subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultProject.xml
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/cdt/model/defaultProject.xml
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/cdt/model/defaultProject.xml
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.sln b/subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.sln
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.sln
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.sln
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj b/subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj
diff --git a/subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj.filters b/subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj.filters
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj.filters
rename to subprojects/ide-native/src/main/resources/org/gradle/ide/visualstudio/tasks/internal/default.vcxproj.filters
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy
new file mode 100644
index 0000000..93145bd
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/cdt/model/CprojectSettingsSpec.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.ide.cdt.model
+
+import org.gradle.api.Project
+import org.gradle.util.TestUtil
+import spock.lang.Ignore
+import spock.lang.Specification
+
+// very loose test, but I'm not expecting it to stay around
+ at Ignore
+class CprojectSettingsSpec extends Specification {
+
+    Project project = TestUtil.createRootProject()
+
+    def descriptor = new CprojectDescriptor()
+    def settings = new CprojectSettings()
+
+    def "wire in includes"() {
+        given:
+        project.apply 'org.gradle.cpp-exe'
+        settings.binary = project.componentSpecs.main
+        descriptor.loadDefaults()
+
+        expect:
+        descriptor.getRootCppCompilerTools().each { compiler ->
+            def includePathsOption = descriptor.getOrCreateIncludePathsOption(compiler)
+            assert includePathsOption.listOptionValue.size() == 0
+        }
+
+        when:
+        settings.applyTo(descriptor)
+        def baos = new ByteArrayOutputStream()
+        descriptor.store(baos)
+        descriptor.load(new ByteArrayInputStream(baos.toByteArray()))
+
+        then:
+        descriptor.getRootCppCompilerTools().each { compiler ->
+            def includePathsOption = descriptor.getOrCreateIncludePathsOption(compiler)
+            assert includePathsOption.listOptionValue.size() == 1
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/cdt/model/ProjectDescriptorSpec.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/cdt/model/ProjectDescriptorSpec.groovy
similarity index 100%
rename from subprojects/cpp/src/test/groovy/org/gradle/ide/cdt/model/ProjectDescriptorSpec.groovy
rename to subprojects/ide-native/src/test/groovy/org/gradle/ide/cdt/model/ProjectDescriptorSpec.groovy
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy
new file mode 100644
index 0000000..6aa8e6f
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/DefaultVisualStudioProjectTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.nativeplatform.NativeComponentSpec
+import spock.lang.Specification
+
+class DefaultVisualStudioProjectTest extends Specification {
+    private DirectInstantiator instantiator = DirectInstantiator.INSTANCE
+    def component = Mock(NativeComponentSpec)
+    def fileResolver = Mock(FileResolver)
+    def vsProject = new DefaultVisualStudioProject("projectName", component, fileResolver, instantiator)
+
+    def "names"() {
+        final projectFile = new File("project")
+        final filtersFile = new File("filters")
+        when:
+        fileResolver.resolve("projectName.vcxproj") >> projectFile
+        fileResolver.resolve("projectName.vcxproj.filters") >> filtersFile
+
+        then:
+        vsProject.name == "projectName"
+        vsProject.projectFile.location == projectFile
+        vsProject.filtersFile.location == filtersFile
+    }
+
+    def "includes source files from all source sets"() {
+        when:
+        def file1 = Mock(File)
+        def file2 = Mock(File)
+        def file3 = Mock(File)
+        def sourceSet1 = sourceSet(file1, file2)
+        def sourceSet2 = sourceSet(file3)
+        vsProject.source([sourceSet1, sourceSet2])
+
+        then:
+        vsProject.sourceFiles == [file1, file2, file3]
+    }
+
+    def "includes header files from all source sets"() {
+        when:
+        def file1 = Mock(File)
+        def file2 = Mock(File)
+        def file3 = Mock(File)
+        def file4 = Mock(File)
+        def sourceSet1 = headerSourceSet([file1, file2])
+        def sourceSet2 = headerSourceSet([file3], [file4])
+        vsProject.source([sourceSet1, sourceSet2])
+
+        then:
+        vsProject.headerFiles == [file1, file2, file3, file4]
+    }
+
+    def "has consistent uuid for same mapped component"() {
+        when:
+        def sameComponent = Mock(NativeComponentSpec)
+        def otherComponent = Mock(NativeComponentSpec)
+
+        def sameProject = new DefaultVisualStudioProject("projectName", component, fileResolver, instantiator)
+        def samePath = new DefaultVisualStudioProject("projectName", sameComponent, fileResolver, instantiator)
+        def differentPath = new DefaultVisualStudioProject("projectName", otherComponent, fileResolver, instantiator)
+        def differentName = new DefaultVisualStudioProject("otherProject", component, fileResolver, instantiator)
+
+        and:
+        component.projectPath >> ":projectPath"
+        sameComponent.projectPath >> ":projectPath"
+        otherComponent.projectPath >> ":otherProjectPath"
+
+        then:
+        vsProject.uuid == sameProject.uuid
+        vsProject.uuid == samePath.uuid
+        vsProject.uuid != differentPath.uuid
+        vsProject.uuid != differentName.uuid
+    }
+
+    private LanguageSourceSet sourceSet(File... files) {
+        def allFiles = files as Set
+        def sourceSet = Mock(LanguageSourceSet)
+        def sourceDirs = Mock(SourceDirectorySet)
+        1 * sourceSet.source >> sourceDirs
+        1 * sourceDirs.files >> allFiles
+        return sourceSet
+    }
+
+    private HeaderExportingSourceSet headerSourceSet(List<File> exportedHeaders, List<File> implicitHeaders = []) {
+        def exportedHeaderFiles = exportedHeaders as Set
+        def implicitHeaderFiles = implicitHeaders as Set
+        def sourceSet = Mock(HeaderExportingSourceSet)
+        def sourceDirs = Mock(SourceDirectorySet)
+        1 * sourceSet.exportedHeaders >> sourceDirs
+        1 * sourceDirs.files >> exportedHeaderFiles
+        def implicitHeaderSet = Mock(SourceDirectorySet)
+        1 * sourceSet.implicitHeaders >> implicitHeaderSet
+        1 * implicitHeaderSet.files >> implicitHeaderFiles
+        return sourceSet
+    }
+}
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy
new file mode 100644
index 0000000..0db9442
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectConfigurationTest.groovy
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.plugins.ExtensionContainer
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.PreprocessingTool
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.nativeplatform.NativeDependencySet
+import org.gradle.nativeplatform.NativeExecutableBinarySpec
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.internal.DefaultFlavor
+import org.gradle.nativeplatform.internal.DefaultFlavorContainer
+import org.gradle.nativeplatform.internal.NativeExecutableBinarySpecInternal
+import org.gradle.nativeplatform.platform.NativePlatform
+import spock.lang.Specification
+
+class VisualStudioProjectConfigurationTest extends Specification {
+    final flavor = new DefaultFlavor("flavor1")
+    def flavors = new DefaultFlavorContainer(DirectInstantiator.INSTANCE)
+    def exe = Mock(NativeExecutableSpec) {
+        getFlavors() >> flavors
+    }
+    def extensions = Mock(ExtensionContainer)
+    def platform = Mock(NativePlatform)
+    def exeBinary = Mock(TestExecutableBinary) {
+        getExtensions() >> extensions
+        getFlavor() >> flavor
+        getComponent() >> exe
+        getTargetPlatform() >> platform
+    }
+    def configuration = new VisualStudioProjectConfiguration(null, "configName", "platformName", exeBinary)
+    def cppCompiler = Mock(PreprocessingTool)
+    def cCompiler = Mock(PreprocessingTool)
+    def rcCompiler = Mock(PreprocessingTool)
+
+    def "setup"() {
+        flavors.add(flavor)
+    }
+
+    def "configuration has supplied names"() {
+        expect:
+        configuration.configurationName == "configName"
+        configuration.platformName == "platformName"
+        configuration.name == "configName|platformName"
+    }
+
+    def "configuration tasks are binary tasks"() {
+        given:
+        def tasks = Mock(NativeExecutableBinarySpec.TasksCollection)
+        def lifecycleTask = Mock(Task)
+        when:
+        exeBinary.tasks >> tasks
+        tasks.build >> lifecycleTask
+        lifecycleTask.path >> "lifecycle-task-path"
+        exe.projectPath >> ":project-path"
+
+        then:
+        configuration.buildTask == "lifecycle-task-path"
+        configuration.cleanTask == ":project-path:clean"
+    }
+
+    def "compiler defines are taken from cpp compiler configuration"() {
+        when:
+        1 * extensions.findByName('cCompiler') >> null
+        1 * extensions.findByName('cppCompiler') >> cppCompiler
+        1 * extensions.findByName('rcCompiler') >> null
+        cppCompiler.macros >> [foo: "bar", empty: null]
+
+        then:
+        configuration.compilerDefines == ["foo=bar", "empty"]
+    }
+
+    def "compiler defines are taken from c compiler configuration"() {
+        when:
+        1 * extensions.findByName('cCompiler') >> cCompiler
+        1 * extensions.findByName('cppCompiler') >> null
+        1 * extensions.findByName('rcCompiler') >> null
+        cCompiler.macros >> [foo: "bar", another: null]
+
+        then:
+        configuration.compilerDefines == ["foo=bar", "another"]
+    }
+
+    def "resource defines are taken from rcCompiler config"() {
+        when:
+        1 * extensions.findByName('cCompiler') >> null
+        1 * extensions.findByName('cppCompiler') >> null
+        1 * extensions.findByName('rcCompiler') >> rcCompiler
+        rcCompiler.macros >> [foo: "bar", empty: null]
+
+        then:
+        configuration.compilerDefines == ["foo=bar", "empty"]
+    }
+
+    def "compiler defines are taken from cpp, c and rc compiler configurations combined"() {
+        when:
+        1 * extensions.findByName('cppCompiler') >> null
+        1 * extensions.findByName('cCompiler') >> null
+        1 * extensions.findByName('rcCompiler') >> null
+
+        then:
+        configuration.compilerDefines == []
+
+        when:
+        1 * extensions.findByName('cCompiler') >> cCompiler
+        1 * extensions.findByName('cppCompiler') >> cppCompiler
+        1 * extensions.findByName('rcCompiler') >> rcCompiler
+        cCompiler.macros >> [_c: null]
+        cppCompiler.macros >> [foo: "bar", _cpp: null]
+        rcCompiler.macros >> [rc: "defined", rc_empty: null]
+
+        then:
+        configuration.compilerDefines == ["_c", "foo=bar", "_cpp", "rc=defined", "rc_empty"]
+    }
+
+    def "include paths include component headers"() {
+        final sources = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
+
+        when:
+        exeBinary.source >> sources
+        exeBinary.libs >> []
+
+        then:
+        configuration.includePaths == []
+
+        when:
+        def file1 = Mock(File)
+        def file2 = Mock(File)
+        def file3 = Mock(File)
+        def sourceSet = Mock(LanguageSourceSet)
+        def sourceSet1 = headerSourceSet(file1, file2)
+        def sourceSet2 = headerSourceSet(file3)
+        sources.addAll(sourceSet, sourceSet1, sourceSet2)
+
+        and:
+        exeBinary.source >> sources
+        exeBinary.libs >> []
+
+        then:
+        configuration.includePaths == [file1, file2, file3]
+    }
+
+    def "include paths include library headers"() {
+        when:
+        def file1 = Mock(File)
+        def file2 = Mock(File)
+        def file3 = Mock(File)
+
+        def deps1 = dependencySet(file1, file2)
+        def deps2 = dependencySet(file3)
+
+        exeBinary.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
+        exeBinary.libs >> [deps1, deps2]
+
+        then:
+        configuration.includePaths == [file1, file2, file3]
+    }
+
+    private HeaderExportingSourceSet headerSourceSet(File... files) {
+        def allFiles = files as Set
+        def sourceSet = Mock(HeaderExportingSourceSet)
+        def sourceDirs = Mock(SourceDirectorySet)
+        1 * sourceSet.exportedHeaders >> sourceDirs
+        1 * sourceDirs.srcDirs >> allFiles
+        return sourceSet
+    }
+
+    private NativeDependencySet dependencySet(File... files) {
+        def deps = Mock(NativeDependencySet)
+        def fileCollection = Mock(FileCollection)
+        deps.includeRoots >> fileCollection
+        fileCollection.files >> (files as Set)
+        return deps
+    }
+
+    interface TestExecutableBinary extends NativeExecutableBinarySpecInternal, ExtensionAware {}
+
+}
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy
new file mode 100644
index 0000000..9387ae8
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectMapperTest.groovy
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.Flavor
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.nativeplatform.internal.NativeExecutableBinarySpecInternal
+import org.gradle.nativeplatform.internal.SharedLibraryBinarySpecInternal
+import org.gradle.nativeplatform.internal.StaticLibraryBinarySpecInternal
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.Architectures
+import org.gradle.nativeplatform.test.NativeTestSuiteSpec
+import org.gradle.nativeplatform.test.internal.NativeTestSuiteBinarySpecInternal
+import org.gradle.platform.base.internal.BinaryNamingScheme
+import spock.lang.Specification
+
+class VisualStudioProjectMapperTest extends Specification {
+    def mapper = new VisualStudioProjectMapper()
+
+    def executable = Mock(NativeExecutableSpec)
+    def library = Mock(NativeLibrarySpec)
+    def namingScheme = Mock(BinaryNamingScheme)
+    NativeExecutableBinarySpecInternal executableBinary
+
+    def flavorOne = Mock(Flavor)
+    def buildTypeOne = Mock(BuildType)
+    def buildTypeTwo = Mock(BuildType)
+    def platformOne = Mock(NativePlatform)
+
+    def setup() {
+        executableBinary = createExecutableBinary("exeBinaryName", buildTypeOne, platformOne)
+        executableBinary.namingScheme >> namingScheme
+
+        executable.name >> "exeName"
+        library.name >> "libName"
+
+        flavorOne.name >> "flavorOne"
+        buildTypeOne.name >> "buildTypeOne"
+        buildTypeTwo.name >> "buildTypeTwo"
+        platformOne.name >> "platformOne"
+        platformOne.architecture >> Architectures.forInput("i386")
+    }
+
+    def "maps executable binary to visual studio project"() {
+        when:
+        executable.projectPath >> ":"
+        namingScheme.variantDimensions >> []
+
+        then:
+        checkNames executableBinary, "exeNameExe", 'buildTypeOne', 'Win32'
+    }
+
+    def "maps library binary types to visual studio projects"() {
+        when:
+        def sharedLibraryBinary = libraryBinary(SharedLibraryBinarySpecInternal)
+        def staticLibraryBinary = libraryBinary(StaticLibraryBinarySpecInternal)
+
+        library.projectPath >> ":"
+        namingScheme.variantDimensions >> []
+
+        then:
+        checkNames sharedLibraryBinary, "libNameDll", 'buildTypeOne', 'Win32'
+        checkNames staticLibraryBinary, "libNameLib", 'buildTypeOne', 'Win32'
+    }
+
+    def "maps test binary to visual studio project"() {
+        def testExecutable = Mock(NativeTestSuiteSpec)
+        def binary = Mock(NativeTestSuiteBinarySpecInternal)
+
+        when:
+        testExecutable.name >> "testSuiteName"
+        testExecutable.projectPath >> ":"
+        binary.component >> testExecutable
+        binary.buildType >> buildTypeOne
+        binary.flavor >> flavorOne
+        binary.targetPlatform >> platformOne
+        binary.namingScheme >> namingScheme
+        namingScheme.variantDimensions >> []
+
+        then:
+        checkNames binary, "testSuiteNameExe", 'buildTypeOne', 'Win32'
+    }
+
+    def "includes project path in visual studio project name"() {
+        when:
+        executable.projectPath >> ":subproject:name"
+
+        and:
+        namingScheme.variantDimensions >> []
+
+        then:
+        checkNames executableBinary, "subproject_name_exeNameExe", 'buildTypeOne', 'Win32'
+    }
+
+    def "uses single variant dimension for configuration name where not empty"() {
+        when:
+        executable.projectPath >> ":"
+        namingScheme.variantDimensions >> ["flavorOne"]
+
+        then:
+        checkNames executableBinary, "exeNameExe", 'flavorOne', 'Win32'
+    }
+
+    def "includes variant dimensions in configuration where component has multiple dimensions"() {
+        when:
+        executable.projectPath >> ":"
+        namingScheme.variantDimensions >> ["platformOne", "buildTypeOne", "flavorOne"]
+
+        then:
+        checkNames executableBinary, "exeNameExe", 'platformOneBuildTypeOneFlavorOne', 'Win32'
+    }
+
+    private def createExecutableBinary(String binaryName, def buildType, def platform) {
+        def binary = Mock(NativeExecutableBinarySpecInternal)
+        binary.name >> binaryName
+        binary.component >> executable
+        binary.buildType >> buildType
+        binary.flavor >> flavorOne
+        binary.targetPlatform >> platform
+        return binary
+    }
+
+    private checkNames(def binary, def projectName, def configurationName, def platformName) {
+        def names = mapper.mapToConfiguration(binary)
+        assert names.project == projectName
+        assert names.configuration == configurationName
+        assert names.platform == platformName
+        true
+    }
+
+    private libraryBinary(Class<? extends NativeBinarySpecInternal> type) {
+        def binary = Mock(type)
+        binary.component >> library
+        binary.flavor >> flavorOne
+        binary.targetPlatform >> platformOne
+        binary.buildType >> buildTypeOne
+        binary.namingScheme >> namingScheme
+        return binary
+    }
+}
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy
new file mode 100644
index 0000000..a15c914
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/internal/VisualStudioProjectRegistryTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.internal
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.internal.NativeExecutableBinarySpecInternal
+import spock.lang.Specification
+
+class VisualStudioProjectRegistryTest extends Specification {
+    private DefaultDomainObjectSet<LanguageSourceSet> sources = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet)
+    def fileResolver = Mock(FileResolver)
+    def visualStudioProjectMapper = Mock(VisualStudioProjectMapper)
+    def registry = new VisualStudioProjectRegistry(fileResolver, visualStudioProjectMapper, DirectInstantiator.INSTANCE)
+
+    def executable = Mock(NativeExecutableSpec)
+
+    def "creates a matching visual studio project configuration for NativeBinary"() {
+        def executableBinary = Mock(NativeExecutableBinarySpecInternal)
+        when:
+        visualStudioProjectMapper.mapToConfiguration(executableBinary) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig", "vsPlatform")
+        executableBinary.component >> executable
+        executableBinary.source >> sources
+
+        and:
+        registry.addProjectConfiguration(executableBinary)
+
+        then:
+        def vsConfig = registry.getProjectConfiguration(executableBinary)
+        vsConfig.project.component == executable
+        vsConfig.type == "Makefile"
+        vsConfig.project.name == "vsProject"
+        vsConfig.configurationName == "vsConfig"
+        vsConfig.platformName == "vsPlatform"
+    }
+
+    def "returns same visual studio project configuration for native binaries that share project name"() {
+        def executableBinary1 = Mock(NativeExecutableBinarySpecInternal)
+        def executableBinary2 = Mock(NativeExecutableBinarySpecInternal)
+
+        when:
+        visualStudioProjectMapper.mapToConfiguration(executableBinary1) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig1", "vsPlatform")
+        visualStudioProjectMapper.mapToConfiguration(executableBinary2) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig2", "vsPlatform")
+        executableBinary1.source >> sources
+        executableBinary2.source >> sources
+
+        and:
+        registry.addProjectConfiguration(executableBinary1)
+        registry.addProjectConfiguration(executableBinary2)
+
+        then:
+        def vsConfig1 = registry.getProjectConfiguration(executableBinary1)
+        def vsConfig2 = registry.getProjectConfiguration(executableBinary2)
+        vsConfig1.project == vsConfig2.project
+
+        and:
+        vsConfig1.type == "Makefile"
+        vsConfig1.project.name == "vsProject"
+        vsConfig1.configurationName == "vsConfig1"
+        vsConfig1.platformName == "vsPlatform"
+
+        and:
+        vsConfig2.type == "Makefile"
+        vsConfig2.project.name == "vsProject"
+        vsConfig2.configurationName == "vsConfig2"
+        vsConfig2.platformName == "vsPlatform"
+    }
+
+    def "visual studio project contains sources for native binaries for all configurations"() {
+        def executableBinary1 = Mock(NativeExecutableBinarySpecInternal)
+        def executableBinary2 = Mock(NativeExecutableBinarySpecInternal)
+        def sourceCommon = Mock(LanguageSourceSet)
+        def source1 = Mock(LanguageSourceSet)
+        def source2 = Mock(LanguageSourceSet)
+
+        when:
+        visualStudioProjectMapper.mapToConfiguration(executableBinary1) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig1", "vsPlatform")
+        visualStudioProjectMapper.mapToConfiguration(executableBinary2) >> new VisualStudioProjectMapper.ProjectConfigurationNames("vsProject", "vsConfig2", "vsPlatform")
+        executableBinary1.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet, [sourceCommon, source1])
+        executableBinary2.source >> new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet, [sourceCommon, source2])
+
+        and:
+        registry.addProjectConfiguration(executableBinary1)
+        registry.addProjectConfiguration(executableBinary2)
+
+        then:
+        def vsProject = registry.getProjectConfiguration(executableBinary1).project
+        vsProject.sources as List == [sourceCommon, source1, source2]
+    }
+}
diff --git a/subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformerTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformerTest.groovy
similarity index 100%
rename from subprojects/cpp/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformerTest.groovy
rename to subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/RelativeFileNameTransformerTest.groovy
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.groovy
new file mode 100644
index 0000000..f954bda
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFileTest.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.ide.visualstudio.tasks.internal
+import org.gradle.api.Transformer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import spock.lang.Specification
+
+class VisualStudioFiltersFileTest extends Specification {
+    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    Transformer<String, File> fileNameTransformer = { it.name } as Transformer<String, File>
+    def filtersFile = new VisualStudioFiltersFile(new XmlTransformer(), fileNameTransformer)
+
+    def "empty filters file"() {
+        when:
+        filtersFile.loadDefaults()
+
+        then:
+        Node sourceFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Source Files'}) as Node
+        sourceFiles.Extensions[0].text() == 'cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx'
+
+        Node headerFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Header Files'}) as Node
+        headerFiles.Extensions[0].text() == 'h;hpp;hxx;hm;inl;inc;xsd'
+
+        Node resourceFiles = itemGroup('Filters').Filter.find({it.'@Include' == 'Resource Files'}) as Node
+        resourceFiles.Extensions[0].text() == 'rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav'
+
+        and:
+        itemGroup('Sources').children().isEmpty()
+        itemGroup('Headers').children().isEmpty()
+    }
+
+    def "adds sources and header files"() {
+        when:
+        filtersFile.loadDefaults()
+
+        and:
+        filtersFile.addSource(file("sourceOne"))
+        filtersFile.addSource(file("sourceTwo"))
+
+        filtersFile.addHeader(file("headerOne"))
+        filtersFile.addHeader(file("headerTwo"))
+
+        then:
+        assert sourceFile(0) == "sourceOne"
+        assert sourceFile(1) == "sourceTwo"
+
+        assert headerFile(0) == "headerOne"
+        assert headerFile(1) == "headerTwo"
+    }
+
+    private String sourceFile(int index) {
+        def source = itemGroup('Sources').ClCompile[index]
+        assert source.Filter[0].text() == 'Source Files'
+        return source.'@Include'
+    }
+
+    private String headerFile(int index) {
+        def header = itemGroup('Headers').ClInclude[index]
+        assert header.Filter[0].text() == 'Header Files'
+        return header.'@Include'
+    }
+
+    private Node itemGroup(String label) {
+        return filtersXml.ItemGroup.find({it.'@Label' == label}) as Node
+    }
+
+    private def getFiltersXml() {
+        return new XmlParser().parse(filtersFileContent)
+    }
+
+    private TestFile getFiltersFileContent() {
+        def file = testDirectoryProvider.testDirectory.file("filters.xml")
+        filtersFile.store(file)
+        return file
+    }
+
+    private TestFile file(String name) {
+        testDirectoryProvider.testDirectory.file(name)
+    }
+}
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy
new file mode 100644
index 0000000..f09a458
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioProjectFileTest.groovy
@@ -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.ide.visualstudio.tasks.internal
+import org.gradle.api.Transformer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import spock.lang.Specification
+
+class VisualStudioProjectFileTest extends Specification {
+    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    Transformer<String, File> fileNameTransformer = { it.name } as Transformer<String, File>
+    def generator = new VisualStudioProjectFile(new XmlTransformer(), fileNameTransformer)
+
+    def "setup"() {
+        generator.loadDefaults()
+    }
+
+    def "empty project file"() {
+        expect:
+        projectFile.projectConfigurations.isEmpty()
+        projectFile.sourceFiles == []
+        projectFile.headerFiles == []
+    }
+
+    def "set project uuid"() {
+        when:
+        generator.setProjectUuid("THE_PROJECT_UUID")
+
+        then:
+        projectFile.projectGuid == "THE_PROJECT_UUID"
+    }
+
+    def "add source and headers"() {
+        when:
+        generator.addSourceFile(file("sourceOne"))
+        generator.addSourceFile(file("sourceTwo"))
+
+        generator.addHeaderFile(file("headerOne"))
+        generator.addHeaderFile(file("headerTwo"))
+
+        then:
+        projectFile.sourceFiles == ["sourceOne", "sourceTwo"]
+        projectFile.headerFiles == ["headerOne", "headerTwo"]
+    }
+
+    def "add configurations"() {
+        when:
+        generator.gradleCommand = 'GRADLE'
+        generator.addConfiguration(configuration("debugWin32", "Win32", ["foo", "bar"], ["include1", "include2"]))
+        generator.addConfiguration(configuration("releaseWin32", "Win32", ["foo", "bar"], ["include1", "include2", "include3"]))
+        generator.addConfiguration(configuration("debugX64", "x64", ["foo", "bar"], ["include1", "include2"]))
+
+        then:
+        final configurations = projectFile.projectConfigurations
+        configurations.size() == 3
+        with (configurations['debugWin32']) {
+            name == 'debugWin32'
+            platformName == 'Win32'
+            macros == "foo;bar"
+            includePath == "include1;include2"
+            buildCommand == "GRADLE debugWin32Build"
+        }
+        with (configurations['releaseWin32']) {
+            name == 'releaseWin32'
+            platformName == 'Win32'
+            macros == "foo;bar"
+            includePath == "include1;include2;include3"
+            buildCommand == "GRADLE releaseWin32Build"
+        }
+        with (configurations['debugX64']) {
+            name == 'debugX64'
+            platformName == 'x64'
+            macros == "foo;bar"
+            includePath == "include1;include2"
+            buildCommand == "GRADLE debugX64Build"
+        }
+    }
+
+    private VisualStudioProjectConfiguration configuration(def configName, def platformName, def defines, def includes) {
+        return Stub(VisualStudioProjectConfiguration) {
+            getName() >> "${configName}|${platformName}"
+            getConfigurationName() >> configName
+            getPlatformName() >> platformName
+            getBuildTask() >> "${configName}Build"
+            getCleanTask() >> "${configName}Clean"
+            getCompilerDefines() >> defines
+            getIncludePaths() >> includes.collect { file(it) }
+        }
+    }
+
+    private ProjectFile getProjectFile() {
+        def file = testDirectoryProvider.testDirectory.file("project.xml")
+        generator.store(file)
+        return new ProjectFile(file)
+    }
+
+    private TestFile file(String name) {
+        testDirectoryProvider.testDirectory.file(name)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy
new file mode 100644
index 0000000..68ea4b0
--- /dev/null
+++ b/subprojects/ide-native/src/test/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioSolutionFileTest.groovy
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ide.visualstudio.tasks.internal
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.ide.visualstudio.TextProvider
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject
+import org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.NativeComponentSpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import spock.lang.Specification
+
+class VisualStudioSolutionFileTest extends Specification {
+    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    def fileResolver = Mock(FileResolver)
+    def instantiator = DirectInstantiator.INSTANCE
+    def solutionFile = new VisualStudioSolutionFile()
+    def binary1 = binary("one")
+
+    def "setup"() {
+        solutionFile.loadDefaults()
+    }
+
+    def "empty solution file"() {
+        expect:
+        generatedSolution.content ==
+"""Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
+"""
+    }
+
+    def "create for single project configuration"() {
+        when:
+        def project = createProject("project1")
+        def configuration1 = createProjectConfiguration(project, "projectConfig")
+        solutionFile.addSolutionConfiguration("solutionConfig", [configuration1])
+        solutionFile.mainProject = project
+
+        then:
+        generatedSolution.content ==
+"""Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project1", "${project.projectFile.location.absolutePath}", "${project.uuid}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		solutionConfig=solutionConfig
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		${project.uuid}.solutionConfig.ActiveCfg = projectConfig|Win32
+		${project.uuid}.solutionConfig.Build.0 = projectConfig|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
+"""
+    }
+
+    def "create for multiple configurations"() {
+        when:
+        def project1 = createProject("project1")
+        def project2 = createProject("project2")
+        solutionFile.mainProject = project1
+        solutionFile.addSolutionConfiguration("solutionConfig1", [
+                createProjectConfiguration(project1, "config1"),
+                createProjectConfiguration(project1, "config2"),
+                createProjectConfiguration(project2, "configA")
+        ])
+        solutionFile.addSolutionConfiguration("solutionConfig2", [
+                createProjectConfiguration(project2, "configA")
+        ])
+
+        then:
+        generatedSolution.content ==
+"""Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual C++ Express 2010
+
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project1", "${project1.projectFile.location.absolutePath}", "${project1.uuid}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "project2", "${project2.projectFile.location.absolutePath}", "${project2.uuid}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		solutionConfig1=solutionConfig1
+		solutionConfig2=solutionConfig2
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		${project1.uuid}.solutionConfig1.ActiveCfg = config1|Win32
+		${project1.uuid}.solutionConfig1.Build.0 = config1|Win32
+		${project1.uuid}.solutionConfig1.ActiveCfg = config2|Win32
+		${project1.uuid}.solutionConfig1.Build.0 = config2|Win32
+		${project2.uuid}.solutionConfig1.ActiveCfg = configA|Win32
+		${project2.uuid}.solutionConfig2.ActiveCfg = configA|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
+"""
+    }
+
+    def "applies multiple text actions"() {
+        when:
+        solutionFile.actions << ({ TextProvider text ->
+            text.setText("foo")
+        } as Action)
+        solutionFile.actions << ({ TextProvider text ->
+            text.asBuilder().append("bar")
+        } as Action)
+
+        then:
+        generatedSolutionFile.text == "foobar"
+    }
+
+    def "can get and set text with actions"() {
+        when:
+        solutionFile.actions << ({ TextProvider text ->
+            text.text = "test"
+        } as Action)
+        solutionFile.actions << ({ TextProvider text ->
+            text.text = text.text.reverse()
+        } as Action)
+
+        then:
+        generatedSolutionFile.text == "tset"
+    }
+
+    private VisualStudioProjectConfiguration createProjectConfiguration(DefaultVisualStudioProject project1, String configName) {
+        return new VisualStudioProjectConfiguration(project1, configName, "Win32", binary1)
+    }
+
+    private DefaultVisualStudioProject createProject(String projectName) {
+        final project1File = new File(projectName)
+        fileResolver.resolve("${projectName}.vcxproj") >> project1File
+        return new DefaultVisualStudioProject(projectName, binary1.component, fileResolver, instantiator)
+    }
+
+    private NativeBinarySpec binary(def name) {
+        def component = Mock(NativeComponentSpec)
+        def binary = Mock(NativeBinarySpecInternal)
+        component.name >> "${name}Component"
+        component.projectPath >> "project-path"
+        binary.name >> name
+        binary.component >> component
+        return binary
+    }
+
+    private SolutionFile getGeneratedSolution() {
+        TestFile file = getGeneratedSolutionFile()
+        return new SolutionFile(file)
+    }
+
+    private TestFile getGeneratedSolutionFile() {
+        def file = testDirectoryProvider.testDirectory.file("solution.txt")
+        solutionFile.store(file)
+        file
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/ide.gradle b/subprojects/ide/ide.gradle
index 030a18e..d8f048d 100644
--- a/subprojects/ide/ide.gradle
+++ b/subprojects/ide/ide.gradle
@@ -30,7 +30,7 @@ dependencies {
     compile libraries.inject
 
     testCompile libraries.xmlunit
-    testCompile project(':coreImpl')
+    testCompile project(':dependencyManagement')
 }
 
 useTestFixtures()
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationSpec.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationSpec.groovy
index fd016dd..f8cfb06 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationSpec.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationSpec.groovy
@@ -17,6 +17,7 @@
 package org.gradle.plugins.ide
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.plugins.ide.idea.IdeaModuleFixture
 
 abstract class AbstractIdeIntegrationSpec extends AbstractIntegrationSpec {
 
@@ -30,4 +31,8 @@ abstract class AbstractIdeIntegrationSpec extends AbstractIntegrationSpec {
         def file = getFile(options, filename)
         new XmlSlurper().parse(file)
     }
+
+    protected IdeaModuleFixture parseIml(Map options = [:], String moduleFile) {
+        return new IdeaModuleFixture(parseFile(options, moduleFile))
+    }
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
index c729de0..59589ed 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
@@ -19,6 +19,7 @@ package org.gradle.plugins.ide
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.plugins.ide.idea.IdeaModuleFixture
 import org.gradle.test.fixtures.file.TestFile
 
 abstract class AbstractIdeIntegrationTest extends AbstractIntegrationTest {
@@ -52,6 +53,10 @@ abstract class AbstractIdeIntegrationTest extends AbstractIntegrationTest {
         return runTask("idea", buildScript)
     }
 
+    protected IdeaModuleFixture parseIml(Map options = [:], String moduleFile) {
+        return new IdeaModuleFixture(parseFile(options, moduleFile))
+    }
+
     protected parseImlFile(Map options = [:], String projectName) {
         parseFile(options, "${projectName}.iml")
     }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
index 8d4548f..b112c09 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
@@ -15,11 +15,7 @@
  */
 package org.gradle.plugins.ide
 
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-import org.gradle.test.fixtures.maven.HttpArtifact
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.*
 import org.junit.Rule
 
 abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeIntegrationSpec {
@@ -32,14 +28,6 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         buildFile << baseBuildScript
     }
 
-    private useIvyRepo(def repo) {
-        buildFile << """repositories { ivy { url "$repo.uri" } }"""
-    }
-
-    private useMavenRepo(def repo) {
-        buildFile << """repositories { maven { url "$repo.uri" } }"""
-    }
-
     def "sources and javadoc jars from maven repositories are not downloaded if not required"() {
         def repo = mavenHttpRepo
         def module = repo.module("some", "module", "1.0").withSourceAndJavadoc().publish()
@@ -58,24 +46,33 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
     def "sources and javadoc jars from maven repositories are resolved, attached and cached"() {
         def repo = mavenHttpRepo
         def module = repo.module("some", "module", "1.0")
+        module.artifact(classifier: "api")
         module.artifact(classifier: "sources")
         module.artifact(classifier: "javadoc")
         module.publish()
         module.allowAll()
 
+        buildFile << """
+dependencies {
+    compile 'some:module:1.0:api'
+}
+"""
+
         when:
         useMavenRepo(repo)
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry()
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
+        ideFileContainsEntry("module-1.0-api.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
 
         when:
         server.resetExpectations()
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry()
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
+        ideFileContainsEntry("module-1.0-api.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
     }
 
     def "ignores missing sources and javadoc jars in maven repositories"() {
@@ -90,7 +87,7 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         ideFileContainsNoSourcesAndJavadocEntry()
     }
 
-    void "ignores broken source or javadoc artifacts in maven repository"() {
+    def "ignores broken source or javadoc artifacts in maven repository"() {
         def repo = mavenHttpRepo
         def module = repo.module("some", "module", "1.0")
         final sourceArtifact = module.artifact(classifier: "sources")
@@ -143,14 +140,74 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry("my-sources", "my-javadoc")
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-my-sources.jar", "module-1.0-my-javadoc.jar")
 
         when:
         server.resetExpectations()
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry("my-sources", "my-javadoc")
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-my-sources.jar", "module-1.0-my-javadoc.jar")
+    }
+
+    def "all sources and javadoc jars resolved from ivy repo are attached to all artifacts for module"() {
+        def repo = ivyHttpRepo
+        def module = repo.module("some", "module", "1.0")
+        addCompleteConfigurations(module)
+
+        module.configuration("api")
+        module.configuration("tests")
+        module.artifact(type: "api", classifier: "api", ext: "jar", conf: "api")
+        module.artifact(type: "tests", classifier: "tests", ext: "jar", conf: "tests")
+        module.artifact(name: "other-source", type: "source", ext: "jar", conf: "sources")
+        module.artifact(name: "other-javadoc", type: "javadoc", ext: "jar", conf: "javadoc")
+
+        module.publish()
+        module.allowAll()
+
+        when:
+        useIvyRepo(repo)
+        buildFile << """
+dependencies {
+    compile 'some:module:1.0:api'
+    testCompile 'some:module:1.0:tests'
+}"""
+
+        succeeds ideTask
+
+        then:
+        def sources = ["module-1.0-my-sources.jar", "other-source-1.0.jar"]
+        def javadoc = ["module-1.0-my-javadoc.jar", "other-javadoc-1.0.jar"]
+        ideFileContainsEntry("module-1.0.jar", sources, javadoc)
+        ideFileContainsEntry("module-1.0-api.jar", sources, javadoc)
+        ideFileContainsEntry("module-1.0-tests.jar", sources, javadoc)
+    }
+
+    def "all sources jars from ivy repositories are attached when there are multiple unclassified artifacts"() {
+        def repo = ivyHttpRepo
+
+        def version = "1.0"
+        def module = repo.module("some", "module", version)
+
+        module.configuration("default")
+        module.configuration("sources")
+        module.configuration("javadoc")
+        module.artifact(name: "foo", type: "jar", ext: "jar", conf: "default")
+        module.artifact(name: "foo-sources", type: "jar", ext: "jar", conf: "sources")
+        module.artifact(name: "foo-javadoc", type: "jar", ext: "jar", conf: "javadoc")
+        module.artifact(name: "foo-api", type: "jar", ext: "jar", conf: "default")
+        module.artifact(name: "foo-api-sources", type: "jar", ext: "jar", conf: "sources")
+        module.artifact(name: "foo-api-javadoc", type: "jar", ext: "jar", conf: "javadoc")
+        module.publish()
+        module.allowAll()
+
+        when:
+        useIvyRepo(repo)
+        succeeds ideTask
+
+        then:
+        ideFileContainsEntry("foo-1.0.jar", ["foo-api-sources-1.0.jar", "foo-sources-1.0.jar"], ["foo-api-javadoc-1.0.jar", "foo-javadoc-1.0.jar"])
+        ideFileContainsEntry("foo-api-1.0.jar", ["foo-api-sources-1.0.jar", "foo-sources-1.0.jar"], ["foo-api-javadoc-1.0.jar", "foo-javadoc-1.0.jar"])
     }
 
     def "ignores missing sources and javadoc jars in ivy repositories"() {
@@ -170,7 +227,7 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         ideFileContainsNoSourcesAndJavadocEntry()
     }
 
-    void "ignores broken source or javadoc artifacts in ivy repository"() {
+    def "ignores broken source or javadoc artifacts in ivy repository"() {
         def repo = ivyHttpRepo
         def module = repo.module("some", "module", "1.0")
         addCompleteConfigurations(module)
@@ -210,9 +267,10 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry("sources", "javadoc")
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
     }
 
+
     def "sources and javadoc jars from flatdir repositories are resolved and attached"() {
         file("repo/module-1.0.jar").createFile()
         file("repo/module-1.0-sources.jar").createFile()
@@ -223,9 +281,18 @@ abstract class AbstractSourcesAndJavadocJarsIntegrationTest extends AbstractIdeI
         succeeds ideTask
 
         then:
-        ideFileContainsSourcesAndJavadocEntry()
+        ideFileContainsEntry("module-1.0.jar", "module-1.0-sources.jar", "module-1.0-javadoc.jar")
+    }
+
+    private useIvyRepo(def repo) {
+        buildFile << """repositories { ivy { url "$repo.uri" } }"""
     }
 
+    private useMavenRepo(def repo) {
+        buildFile << """repositories { maven { url "$repo.uri" } }"""
+    }
+
+
     private static void addCompleteConfigurations(IvyHttpModule module) {
         module.configuration("default")
         module.configuration("sources")
@@ -274,8 +341,10 @@ task resolve << {
 
     abstract String getIdeTask()
 
-    abstract void ideFileContainsSourcesAndJavadocEntry()
-    abstract void ideFileContainsSourcesAndJavadocEntry(String sourcesClassifier, String javadocClassifier)
+    void ideFileContainsEntry(String jar, String sources, String javadoc) {
+        ideFileContainsEntry(jar, [sources], [javadoc])
+    }
+    abstract void ideFileContainsEntry(String jar, List<String> sources, List<String> javadoc)
     abstract void ideFileContainsNoSourcesAndJavadocEntry()
     abstract void expectBehaviorAfterBrokenMavenArtifact(HttpArtifact httpArtifact)
     abstract void expectBehaviorAfterBrokenIvyArtifact(HttpArtifact httpArtifact)
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationSpec.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationSpec.groovy
new file mode 100644
index 0000000..2de5ad9
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationSpec.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.eclipse
+
+import org.gradle.plugins.ide.AbstractIdeIntegrationSpec
+
+class AbstractEclipseIntegrationSpec extends AbstractIdeIntegrationSpec {
+    protected EclipseClasspathFixture getClasspath() {
+        return new EclipseClasspathFixture(testDirectory, executer.gradleUserHomeDir)
+    }
+
+    protected EclipseClasspathFixture classpath(String project) {
+        return new EclipseClasspathFixture(testDirectory.file(project), executer.gradleUserHomeDir)
+    }
+
+    protected EclipseWtpComponentFixture getWtpComponent() {
+        return new EclipseWtpComponentFixture(testDirectory)
+    }
+
+    protected EclipseWtpComponentFixture wtpComponent(String project) {
+        return new EclipseWtpComponentFixture(testDirectory.file(project))
+    }
+
+    protected EclipseWtpFacetsFixture getWtpFacets() {
+        return new EclipseWtpFacetsFixture(testDirectory)
+    }
+
+    protected EclipseWtpFacetsFixture wtpFacets(String project) {
+        return new EclipseWtpFacetsFixture(testDirectory.file(project))
+    }
+
+    protected EclipseProjectFixture getProject() {
+        return new EclipseProjectFixture(testDirectory)
+    }
+
+    protected EclipseProjectFixture project(String project) {
+        return new EclipseProjectFixture(testDirectory.file(project))
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy
index 5d66bad..8e1430e 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/AbstractEclipseIntegrationTest.groovy
@@ -69,7 +69,19 @@ class AbstractEclipseIntegrationTest extends AbstractIdeIntegrationTest {
         assert libs*. at path*.text().collect { new File(it).name } as Set == filenames as Set
     }
 
+    protected EclipseWtpComponentFixture getWtpComponent() {
+        return new EclipseWtpComponentFixture(testDirectory)
+    }
+
+    protected EclipseWtpComponentFixture wtpComponent(String project) {
+        return new EclipseWtpComponentFixture(testDirectory.file(project))
+    }
+
     protected EclipseClasspathFixture getClasspath() {
         return new EclipseClasspathFixture(testDirectory, executer.gradleUserHomeDir)
     }
+
+    protected EclipseClasspathFixture classpath(String path) {
+        return new EclipseClasspathFixture(testDirectory.file(path), executer.gradleUserHomeDir)
+    }
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
index 845e9b1..d467f8d 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
@@ -33,8 +33,7 @@ class EclipseClasspathFixture {
     Node getClasspath() {
         if (classpath == null) {
             TestFile file = projectDir.file('.classpath')
-            println "Using .classpath:"
-            println file.text
+            file.assertExists()
             classpath = new XmlParser().parse(file)
         }
         return classpath
@@ -60,6 +59,16 @@ class EclipseClasspathFixture {
         return getClasspath().classpathentry.findAll { it. at kind == 'src' && it. at path.startsWith('/') }.collect { it. at path }
     }
 
+    void assertHasLibs(String... jarNames) {
+        assert libs*.jarName == jarNames as List
+    }
+
+    EclipseLibrary lib(String jarName) {
+        def matches = libs.findAll { it.jarName == jarName } + vars.findAll { it.jarName == jarName }
+        assert matches.size() == 1
+        return matches[0]
+    }
+
     List<EclipseLibrary> getLibs() {
         return getClasspath().classpathentry.findAll { it. at kind == 'lib' }.collect { new EclipseLibrary(it) }
     }
@@ -75,6 +84,14 @@ class EclipseClasspathFixture {
             this.entry = entry
         }
 
+        String getJarName() {
+            jarPath.split('/').last()
+        }
+
+        String getJarPath() {
+            entry. at path
+        }
+
         void assertHasJar(File jar) {
             assert entry. at path == jar.absolutePath.replace(File.separator, '/')
         }
@@ -128,7 +145,23 @@ class EclipseClasspathFixture {
         }
 
         void assertHasNoJavadoc() {
-            assert entry.attributes.size() == 0
+            assert entry.attributes.isEmpty()
+        }
+
+        void assertIsDeployedTo(String path) {
+            assert entry.attributes
+            assert entry.attributes[0].attribute[0]. at name == 'org.eclipse.jst.component.dependency'
+            assert entry.attributes[0].attribute[0]. at value == path
+        }
+
+        void assertIsExcludedFromDeployment() {
+            assert entry.attributes
+            assert entry.attributes[0].attribute[0]. at name == 'org.eclipse.jst.component.nondependency'
+            assert entry.attributes[0].attribute[0]. at value == ''
+        }
+
+        void assertHasNoDeploymentAttributes() {
+            assert entry.attributes.isEmpty()
         }
 
         void assertExported() {
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy
index b427c79..bb89efb 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathIntegrationTest.groovy
@@ -172,7 +172,7 @@ eclipse {
   classpath {
     sourceSets = []
 
-    plusConfigurations += configurations.someConfig
+    plusConfigurations << configurations.someConfig
 
     containers 'someFriendlyContainer', 'andYetAnotherContainer'
 
@@ -202,6 +202,49 @@ eclipse {
         assert classpath.classpath.message[0].text() == 'be cool'
     }
 
+    @Issue("GRADLE-3101")
+    @Test
+    void canCustomizeTheClasspathModelUsingPlusEqual() {
+        def module = mavenRepo.module('coolGroup', 'niceArtifact', '1.0')
+        module.publish()
+        def baseJar = module.artifactFile
+
+        //when
+        runEclipseTask """
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+sourceSets.main.java.srcDirs.each { it.mkdirs() }
+sourceSets.main.resources.srcDirs.each { it.mkdirs() }
+
+configurations {
+  someConfig
+}
+
+eclipse {
+
+  classpath {
+    sourceSets = []
+
+    plusConfigurations += [ configurations.someConfig ]
+  }
+}
+
+dependencies {
+    someConfig 'coolGroup:niceArtifact:1.0'
+}
+"""
+
+        //then
+        def libraries = classpath.libs
+        assert libraries.size() == 1
+        libraries[0].assertHasJar(baseJar)
+    }
+
     @Test
     @Issue("GRADLE-1487")
     void handlesPlusMinusConfigurationsForSelfResolvingDeps() {
@@ -221,8 +264,8 @@ dependencies {
 }
 
 eclipse.classpath {
-    plusConfigurations += configurations.someConfig
-    minusConfigurations += configurations.someOtherConfig
+    plusConfigurations << configurations.someConfig
+    minusConfigurations << configurations.someOtherConfig
 }
 """
 
@@ -256,8 +299,8 @@ dependencies {
 }
 
 eclipse.classpath {
-    plusConfigurations += configurations.someConfig
-    minusConfigurations += configurations.someOtherConfig
+    plusConfigurations << configurations.someConfig
+    minusConfigurations << configurations.someOtherConfig
 }
 """
 
@@ -291,8 +334,8 @@ dependencies {
 }
 
 eclipse.classpath {
-    plusConfigurations += configurations.someConfig
-    minusConfigurations += configurations.someOtherConfig
+    plusConfigurations << configurations.someConfig
+    minusConfigurations << configurations.someOtherConfig
 }
 """
 
@@ -579,8 +622,8 @@ dependencies {
 
 eclipse {
   classpath {
-    plusConfigurations += configurations.provided
-    noExportConfigurations += configurations.provided
+    plusConfigurations << configurations.provided
+    noExportConfigurations << configurations.provided
   }
 }
 """
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseDependencySubstitutionIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseDependencySubstitutionIntegrationTest.groovy
new file mode 100644
index 0000000..264f617
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseDependencySubstitutionIntegrationTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+class EclipseDependencySubstitutionIntegrationTest extends AbstractEclipseIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources(testDirectoryProvider)
+
+    @Test
+    void "external dependency substituted with project dependency"() {
+        runEclipseTask("include 'project1', 'project2'", """
+allprojects {
+    apply plugin: "java"
+    apply plugin: "eclipse"
+}
+
+project(":project2") {
+    dependencies {
+        compile group: "junit", name: "junit", version: "4.7"
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withModule("junit:junit") {
+            it.useTarget project(":project1")
+        }
+    }
+}
+""")
+
+        def classpath = classpath("project2")
+        assert classpath.projects == ["/project1"]
+        assert classpath.libs == []
+    }
+
+    @Test
+    @Ignore("not supported in 2.4 - LD - 14/4/15")
+    void "transitive external dependency substituted with project dependency"() {
+        mavenRepo.module("org.gradle", "module1").dependsOn("module2").publish()
+        mavenRepo.module("org.gradle", "module2").publish()
+
+        runEclipseTask("include 'project1', 'project2'", """
+allprojects {
+    apply plugin: "java"
+    apply plugin: "eclipse"
+}
+
+project(":project2") {
+    repositories {
+        maven { url "${mavenRepo.uri}" }
+    }
+
+    dependencies {
+        compile "org.gradle:module1:1.0"
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withModule("org.gradle:module2") {
+            it.useTarget project(":project1")
+        }
+    }
+}
+""")
+
+        def classpath = classpath("project2")
+        assert classpath.libs*.jarName == ["module1-1.0.jar"]
+        assert classpath.projects == ["/project1"]
+    }
+
+
+    @Test
+    void "project dependency substituted with external dependency"() {
+        runEclipseTask("include 'project1', 'project2'", """
+ allprojects {
+    apply plugin: "java"
+    apply plugin: "eclipse"
+}
+
+project(":project2") {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        compile project(":project1")
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withProject(":project1") {
+            it.useTarget "junit:junit:4.7"
+        }
+    }
+}
+""")
+
+        def classpath = classpath("project2")
+        assert classpath.projects == []
+        assert classpath.libs*.jarName == ["junit-4.7.jar"]
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectFixture.groovy
new file mode 100644
index 0000000..5d20ae9
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseProjectFixture.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.eclipse
+
+import org.gradle.test.fixtures.file.TestFile
+
+class EclipseProjectFixture {
+    private final TestFile projectDir
+    private Node project
+
+    EclipseProjectFixture(TestFile projectDir) {
+        this.projectDir = projectDir
+    }
+
+    private Node getProject() {
+        if (project == null) {
+            TestFile file = projectDir.file('.project')
+            file.assertIsFile()
+            project = new XmlParser().parse(file)
+        }
+        return project
+    }
+
+    void assertHasJavaFacetNatures() {
+        assertHasNatures("org.eclipse.jdt.core.javanature",
+                        "org.eclipse.wst.common.project.facet.core.nature",
+                        "org.eclipse.wst.common.modulecore.ModuleCoreNature",
+                        "org.eclipse.jem.workbench.JavaEMFNature")
+    }
+
+    void assertHasNatures(String... natures) {
+        assert getProject().natures.nature*.text() == natures as List
+    }
+
+    void assertHasJavaFacetBuilders() {
+        assertHasBuilders("org.eclipse.jdt.core.javabuilder",
+                        "org.eclipse.wst.common.project.facet.core.builder",
+                        "org.eclipse.wst.validation.validationbuilder"
+                )
+    }
+
+    void assertHasBuilders(String... builders) {
+        assert getProject().buildSpec.buildCommand.name*.text() == builders as List
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseSourcesAndJavadocJarsIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseSourcesAndJavadocJarsIntegrationTest.groovy
index df46e81..531cc9f 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseSourcesAndJavadocJarsIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseSourcesAndJavadocJarsIntegrationTest.groovy
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 package org.gradle.plugins.ide.eclipse
-
 import org.gradle.plugins.ide.AbstractSourcesAndJavadocJarsIntegrationTest
-import org.gradle.test.fixtures.maven.HttpArtifact
+import org.gradle.test.fixtures.server.http.HttpArtifact
 
 class EclipseSourcesAndJavadocJarsIntegrationTest extends AbstractSourcesAndJavadocJarsIntegrationTest {
     @Override
@@ -24,11 +23,14 @@ class EclipseSourcesAndJavadocJarsIntegrationTest extends AbstractSourcesAndJava
         return "eclipseClasspath"
     }
 
-    void ideFileContainsSourcesAndJavadocEntry(String sourcesClassifier = "sources", String javadocClassifier = "javadoc") {
+    @Override
+    void ideFileContainsEntry(String jar, List<String> sources, List<String> javadoc) {
         def classpath = new EclipseClasspathFixture(testDirectory, executer.gradleUserHomeDir)
-        def lib = classpath.libs[0]
-        assert lib.sourcePath.endsWith("/module-1.0-${sourcesClassifier}.jar")
-        assert lib.javadocLocation.endsWith("/module-1.0-${javadocClassifier}.jar!/")
+        def lib = classpath.lib(jar)
+
+        // Eclipse only retains the first source/javadoc file
+        assert lib.sourcePath.endsWith("/${sources.get(0)}")
+        assert lib.javadocLocation.endsWith("/${javadoc.get(0)}!/")
     }
 
     void ideFileContainsNoSourcesAndJavadocEntry() {
@@ -45,4 +47,4 @@ class EclipseSourcesAndJavadocJarsIntegrationTest extends AbstractSourcesAndJava
 
     @Override
     void expectBehaviorAfterBrokenIvyArtifact(HttpArtifact httpArtifact) {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpComponentFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpComponentFixture.groovy
new file mode 100644
index 0000000..d32a843
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpComponentFixture.groovy
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+import org.gradle.test.fixtures.file.TestFile
+
+class EclipseWtpComponentFixture {
+    private final TestFile projectDir
+    private Node component
+
+    EclipseWtpComponentFixture(TestFile projectDir) {
+        this.projectDir = projectDir
+    }
+
+    private Node getComponent() {
+        if (component == null) {
+            TestFile file = projectDir.file(".settings/org.eclipse.wst.common.component")
+            file.assertIsFile()
+            component = new XmlParser().parse(file)
+        }
+        return component
+    }
+
+    String getDeployName() {
+        return getComponent()."wb-module"."@deploy-name".text()
+    }
+
+    List<WbResource> getResources() {
+        return getComponent()."wb-module"."wb-resource".collect { new WbResource(it) }
+    }
+
+    List<WbModule> getModules() {
+        return getComponent()."wb-module"."dependent-module".collect { new WbModule(it) }
+    }
+
+    WbModule lib(String jarName) {
+        def module = modules.find {
+            def handle = it.node. at handle
+            return handle.startsWith('module:/classpath/') && handle.endsWith(jarName)
+        }
+        assert module != null
+        assert module.node."dependency-type"*.text() == ['uses']
+        return module
+    }
+
+    WbModule project(String projectName) {
+        def module = modules.find {
+            def handle = it.node. at handle
+            return handle == "module:/resource/$projectName/$projectName"
+        }
+        assert module != null
+        assert module.node."dependency-type"*.text() == ['uses']
+        return module
+    }
+
+    WbResource sourceDirectory(String path) {
+        def resource = resources.find { it.node."@source-path" == path }
+        assert resource != null
+        return resource
+    }
+
+    class WbModule {
+        private final Node node
+
+        WbModule(Node node) {
+            this.node = node
+        }
+
+        void assertDeployedAt(String path) {
+            assert node."@deploy-path" == path
+        }
+    }
+
+    class WbResource {
+        private final Node node
+
+        WbResource(Node node) {
+            this.node = node
+        }
+
+        void assertDeployedAt(String path) {
+            assert node."@deploy-path" == path
+        }
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarAndWebAndEjbProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarAndWebAndEjbProjectIntegrationTest.groovy
new file mode 100644
index 0000000..d7c7c84
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarAndWebAndEjbProjectIntegrationTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpEarAndWebAndEjbProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for an ear project and ejb and web projects it bundles"() {
+        settingsFile << "include 'ear', 'web', 'java'"
+
+        file('java/src/main/java').mkdirs()
+        file('web/src/main/java').mkdirs()
+        file('web/src/main/webapp').mkdirs()
+
+        buildFile << """
+subprojects {
+    apply plugin: 'eclipse-wtp'
+
+    repositories {
+        jcenter()
+    }
+}
+project(':ear') {
+    apply plugin: 'ear'
+
+    dependencies {
+        deploy project(':java')
+        deploy project(path: ':web', configuration: 'archives')
+    }
+}
+project(':web') {
+    apply plugin: 'war'
+
+    dependencies {
+        providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
+        testCompile "junit:junit:4.12"
+    }
+}
+project(':java') {
+    apply plugin: 'java'
+
+    dependencies {
+        compile 'com.google.guava:guava:18.0'
+        compile 'javax.servlet:javax.servlet-api:3.1.0'
+        testCompile "junit:junit:4.12"
+    }
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // This test covers actual behaviour, not necessarily desired behaviour
+
+        // Builders and natures
+        def javaProject = project('java')
+        def webProject = project('web')
+        def earProject = project('ear')
+
+        // Classpath
+        def javaClasspath = classpath('java')
+        def webClasspath = classpath('web')
+
+        // Facets
+        def javaFacets = wtpFacets('java')
+        def webFacets = wtpFacets('web')
+        def earFacets = wtpFacets('ear')
+
+        // Deployment
+        def javaComponent = wtpComponent('java')
+        javaComponent.deployName == 'java'
+        javaComponent.resources.size() == 1
+        javaComponent.sourceDirectory('src/main/java').assertDeployedAt('/')
+        javaComponent.modules.isEmpty()
+
+        def webComponent = wtpComponent('web')
+        webComponent.deployName == 'web'
+        webComponent.resources.size() == 2
+        webComponent.sourceDirectory('src/main/webapp').assertDeployedAt('/')
+        webComponent.sourceDirectory('src/main/java').assertDeployedAt('/WEB-INF/classes')
+        webComponent.modules.isEmpty()
+
+        def earComponent = wtpComponent('ear')
+        earComponent.deployName == 'ear'
+        earComponent.resources.isEmpty()
+        earComponent.modules.size() == 2
+        earComponent.project('java').assertDeployedAt('/')
+        earComponent.project('web').assertDeployedAt('/')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy
new file mode 100644
index 0000000..3da91fd
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpEarProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for an ear project"() {
+        settingsFile << "rootProject.name = 'ear'"
+
+        buildFile << """
+apply plugin: 'eclipse-wtp'
+apply plugin: 'ear'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    deploy 'com.google.guava:guava:18.0'
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // This test covers actual behaviour, not necessarily desired behaviour
+
+        // Builders and natures
+        def project = project
+        project.assertHasNatures("org.eclipse.wst.common.project.facet.core.nature",
+                "org.eclipse.wst.common.modulecore.ModuleCoreNature",
+                "org.eclipse.jem.workbench.JavaEMFNature")
+        project.assertHasBuilders("org.eclipse.wst.common.project.facet.core.builder",
+                "org.eclipse.wst.validation.validationbuilder")
+
+        // TODO - classpath
+
+        // Facets
+        def facets = wtpFacets
+        facets.assertHasFixedFacets("jst.ear")
+        facets.assertHasInstalledFacets("jst.ear")
+        facets.assertFacetVersion("jst.ear", "5.0")
+
+        // Deployment
+        def component = wtpComponent
+        component.deployName == 'ear'
+        component.resources.isEmpty()
+        component.modules.size() == 1
+        component.lib('guava-18.0.jar').assertDeployedAt('/')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEmptyProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEmptyProjectIntegrationTest.groovy
new file mode 100644
index 0000000..271ac7f
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEmptyProjectIntegrationTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpEmptyProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for an empty project"() {
+        settingsFile << "rootProject.name = 'empty'"
+
+        buildFile << """
+apply plugin: 'eclipse-wtp'
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // Builders and natures
+        def project = project
+        project.assertHasNatures()
+        project.assertHasBuilders()
+
+        // TODO - Classpath
+
+        // Facets
+        def facets = wtpFacets
+        facets.assertHasFixedFacets()
+        facets.assertHasInstalledFacets()
+
+        // Deployment
+        def component = wtpComponent
+        component.deployName == 'empty'
+        component.resources.isEmpty()
+        component.modules.isEmpty()
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpFacetsFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpFacetsFixture.groovy
new file mode 100644
index 0000000..89d8047
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpFacetsFixture.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.plugins.ide.eclipse
+
+import org.gradle.test.fixtures.file.TestFile
+
+class EclipseWtpFacetsFixture {
+    private final TestFile projectDir
+    private Node facets
+
+    EclipseWtpFacetsFixture(TestFile projectDir) {
+        this.projectDir = projectDir
+    }
+
+    private Node getFacets() {
+        if (facets == null) {
+            TestFile file = projectDir.file(".settings/org.eclipse.wst.common.project.facet.core.xml")
+            file.assertIsFile()
+            facets = new XmlParser().parse(file)
+        }
+        return facets
+    }
+
+    void assertHasFixedFacets(String... facets) {
+        assert getFacets().fixed*. at facet == facets as List
+    }
+
+    void assertHasInstalledFacets(String... facets) {
+        assert getFacets().installed*. at facet == facets as List
+    }
+
+    void assertFacetVersion(String facet, String version) {
+        def facetNode = getFacets().installed.find { it. at facet == facet }
+        assert facetNode != null
+        assert facetNode. at version == version
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
index 22c52e6..dceb9a4 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
@@ -19,70 +19,9 @@ import org.junit.Test
 import spock.lang.Issue
 
 class EclipseWtpIntegrationTest extends AbstractEclipseIntegrationTest {
-    void runSharedBuild() {
-        generateEclipseFilesForWebProject()
-    }
-
-    @Test
-    void projectDependenciesOfWebProjectAreMarkedAsJstUtilityProjects() {
-        useSharedBuild = true;
-        hasUtilityFacet("java1")
-        hasUtilityFacet("java2")
-        hasUtilityFacet("groovy")
-    }
-
-    @Test
-    void projectDependenciesOfWebProjectHaveNecessaryNaturesAdded() {
-        useSharedBuild = true;
-        hasNecessaryNaturesAdded("java1")
-        hasNecessaryNaturesAdded("java2")
-        hasNecessaryNaturesAdded("groovy")
-    }
-
-    @Test
-    void projectDependenciesOfWebProjectHaveNecessaryBuildersAdded() {
-        useSharedBuild = true;
-        hasNecessaryBuildersAdded("java1")
-        hasNecessaryBuildersAdded("java2")
-        hasNecessaryBuildersAdded("groovy")
-    }
-
-    @Test
-    void projectDependenciesOfWebProjectHaveTrimmedDownComponentSettingsFile() {
-        useSharedBuild = true;
-        hasTrimmedDownComponentSettingsFile("java1", "src/main/java", "src/main/resources")
-        hasTrimmedDownComponentSettingsFile("java2", "src/main/java", "src/main/resources")
-        hasTrimmedDownComponentSettingsFile("groovy", "src/main/java", "src/main/groovy", "src/main/resources")
-    }
-
-    @Test
-    void jarDependenciesOfUtilityProjectsAreFlaggedAsRuntimeDependency() {
-        useSharedBuild = true;
-        def classpath = parseClasspathFile(project: "java1")
-
-        def firstLevelDep = classpath.classpathentry.find { it. at path.text().endsWith("myartifact-1.0.jar") }
-        assert firstLevelDep.attributes.attribute.find { it. at name.text() == "org.eclipse.jst.component.dependency" }
-
-        def secondLevelDep = classpath.classpathentry.find { it. at path.text().endsWith("myartifactdep-1.0.jar") }
-        assert secondLevelDep.attributes.attribute.find { it. at name.text() == "org.eclipse.jst.component.dependency" }
-
-    }
-
-    @Test
-    void allProjectDependenciesOfWebProjectAreAddedAsRuntimeDependencies() {
-        useSharedBuild = true;
-        def projectModules = parseComponentFile(project: "web", print: true)
-
-		assert getDeployName(projectModules) == "web"
-		assert getHandleFilenames(projectModules) == ["java1", "java2", "groovy", "myartifact-1.0.jar", "myartifactdep-1.0.jar"] as Set
-		assert getDependencyTypes(projectModules) == ["uses"] * 5 as Set
-    }
-
     @Test
     @Issue("GRADLE-1415")
     void canUseSelfResolvingFiles() {
-        useSharedBuild = false
-
         def buildFile = """
 apply plugin: "war"
 apply plugin: "eclipse"
@@ -106,8 +45,6 @@ dependencies {
     @Test
     @Issue("GRADLE-2526")
     void overwritesDependentModules() {
-        useSharedBuild = false
-
         generateEclipseFilesForWebProject()
         def projectModules = parseComponentFile(project: "web")
         assert getHandleFilenames(projectModules) == ["java1", "java2", "groovy", "myartifact-1.0.jar", "myartifactdep-1.0.jar"] as Set
@@ -194,53 +131,7 @@ apply plugin: "groovy"
         executer.usingSettingsFile(settingsFile).withTasks("eclipse").run()
     }
 
-    private void hasUtilityFacet(String project) {
-        def file = getFacetFile(project: project)
-        def facetedProject = new XmlSlurper().parse(file)
-        assert facetedProject.children().any { it. at facet.text() == "jst.utility" && it. at version.text() == "1.0" }
-    }
-
-    private void hasNecessaryBuildersAdded(String project) {
-        def projectDescription = parseProjectFile(project: project)
-        assert projectDescription.buildSpec.buildCommand.name*.text().containsAll(
-                ["org.eclipse.wst.common.project.facet.core.builder", "org.eclipse.wst.validation.validationbuilder"])
-    }
-
-    private void hasNecessaryNaturesAdded(String project) {
-        def projectDescription = parseProjectFile(project: project)
-        assert projectDescription.natures.nature*.text().containsAll(["org.eclipse.wst.common.project.facet.core.nature",
-                "org.eclipse.jem.workbench.JavaEMFNature", "org.eclipse.wst.common.modulecore.ModuleCoreNature"])
-    }
-
-    private void hasTrimmedDownComponentSettingsFile(String projectName, String... sourcePaths) {
-        def projectModules = parseComponentFile(project: projectName, print: true)
-
-        assert getDeployName(projectModules) == projectName
-        assert getSourcePaths(projectModules) == sourcePaths as Set
-        assert getDeployPaths(projectModules) == ["/"] * sourcePaths.size() as Set
-        assert getHandleFilenames(projectModules) == [] as Set
-        assert getDependencyTypes(projectModules) == [] as Set
-    }
-
-    private String getDeployName(projectModules) {
-		def names = projectModules."wb-module".@"deploy-name"*.text()
-        assert names.size() == 1
-        names[0]
-	}
-
-    private Set getSourcePaths(projectModules) {
-        projectModules."wb-module"."wb-resource".@"source-path"*.text() as Set
-    }
-
-    private Set getDeployPaths(projectModules) {
-        projectModules."wb-module"."wb-resource".@"deploy-path"*.text() as Set
-    }
-
 	private Set getHandleFilenames(projectModules) {
 		projectModules."wb-module"."dependent-module". at handle*.text().collect { it.substring(it.lastIndexOf("/") + 1) } as Set
 	}
-
-	private Set getDependencyTypes(projectModules) {
-		projectModules."wb-module"."dependent-module"."dependency-type"*.text() as Set
-	}
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaEarSingleProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaEarSingleProjectIntegrationTest.groovy
new file mode 100644
index 0000000..45441c1
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaEarSingleProjectIntegrationTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpJavaEarSingleProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for an ear project"() {
+        file('src/main/java').mkdirs()
+
+        settingsFile << "rootProject.name = 'ear'"
+
+        buildFile << """
+apply plugin: 'eclipse-wtp'
+apply plugin: 'ear'
+apply plugin: 'java'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile 'com.google.guava:guava:18.0'
+    testCompile "junit:junit:4.12"
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // This test covers actual behaviour, not necessarily desired behaviour
+
+        // Builders and natures
+        def project = project
+        project.assertHasNatures("org.eclipse.wst.common.project.facet.core.nature",
+                "org.eclipse.wst.common.modulecore.ModuleCoreNature",
+                "org.eclipse.jem.workbench.JavaEMFNature",
+                "org.eclipse.jdt.core.javanature")
+        project.assertHasBuilders("org.eclipse.wst.common.project.facet.core.builder",
+                "org.eclipse.wst.validation.validationbuilder")
+
+        // Classpath
+        def classpath = classpath
+        classpath.assertHasLibs('guava-18.0.jar', 'junit-4.12.jar', 'hamcrest-core-1.3.jar')
+        classpath.lib('guava-18.0.jar').assertHasNoDeploymentAttributes() // Probably not right
+        classpath.lib('junit-4.12.jar').assertHasNoDeploymentAttributes()
+        classpath.lib('hamcrest-core-1.3.jar').assertHasNoDeploymentAttributes()
+
+        // Facets
+        def facets = wtpFacets
+        facets.assertHasFixedFacets("jst.ear")
+        facets.assertHasInstalledFacets("jst.ear") // Probably not right
+        facets.assertFacetVersion("jst.ear", "5.0")
+
+        // Deployment
+        def component = wtpComponent
+        component.deployName == 'ear'
+        component.resources.size() == 1
+        component.sourceDirectory('src/main/java').assertDeployedAt('/') // Probably not right
+        component.modules.size() == 0 // Probably not right
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy
new file mode 100644
index 0000000..f98c496
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpJavaProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for a Java project"() {
+        file('src/main/java').mkdirs()
+        file('src/main/resources').mkdirs()
+
+        settingsFile << "rootProject.name = 'java'"
+
+        buildFile << """
+apply plugin: 'eclipse-wtp'
+apply plugin: 'java'
+
+repositories {
+    jcenter()
+}
+
+sourceCompatibility = 1.6
+
+dependencies {
+    compile 'com.google.guava:guava:18.0'
+    testCompile "junit:junit:4.12"
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // Builders and natures
+        def project = project
+        project.assertHasJavaFacetNatures()
+        project.assertHasJavaFacetBuilders()
+
+        // Classpath
+        def classpath = classpath
+        classpath.assertHasLibs('guava-18.0.jar', 'junit-4.12.jar', 'hamcrest-core-1.3.jar')
+        classpath.lib('guava-18.0.jar').assertIsDeployedTo('../')
+        classpath.lib('junit-4.12.jar').assertIsExcludedFromDeployment()
+        classpath.lib('hamcrest-core-1.3.jar').assertIsExcludedFromDeployment()
+
+        // Facets
+        def facets = wtpFacets
+        facets.assertHasFixedFacets("jst.java")
+        facets.assertHasInstalledFacets("jst.utility", "jst.java")
+        facets.assertFacetVersion("jst.utility", "1.0")
+        facets.assertFacetVersion("jst.java", "6.0")
+
+        // Deployment
+        def component = wtpComponent
+        component.deployName == 'java'
+        component.resources.size() == 2
+        component.sourceDirectory('src/main/java').assertDeployedAt('/')
+        component.sourceDirectory('src/main/resources').assertDeployedAt('/')
+        component.modules.isEmpty() // Deployed via classpath instead
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
index e0cb1d2..0211a7c 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.plugins.ide.eclipse
 
 import org.gradle.integtests.fixtures.TestResources
-import org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry
 import org.junit.Rule
 import org.junit.Test
 import spock.lang.Issue
@@ -69,8 +68,8 @@ eclipse {
 
       sourceDirs += file('someExtraSourceDir')
 
-      plusConfigurations += configurations.configOne
-      minusConfigurations += configurations.configTwo
+      plusConfigurations << configurations.configOne
+      minusConfigurations << configurations.configTwo
 
       deployName = 'someBetterDeployName'
 
@@ -272,8 +271,8 @@ dependencies {
 eclipse {
   wtp {
     component {
-        plusConfigurations += configurations.configOne
-        minusConfigurations += configurations.configTwo
+        plusConfigurations << configurations.configOne
+        minusConfigurations << configurations.configTwo
     }
   }
 }
@@ -286,38 +285,6 @@ eclipse {
     }
 
     @Test
-    void createsTasksOnDependantUponProjectsEvenIfTheyDontHaveWarPlugin() {
-        //given
-        def settings = file('settings.gradle')
-        settings << "include 'impl', 'contrib'"
-
-        def build = file('build.gradle')
-        build << """
-project(':impl') {
-  apply plugin: 'java'
-  apply plugin: 'war'
-  apply plugin: 'eclipse-wtp'
-
-  dependencies { compile project(':contrib') }
-}
-
-project(':contrib') {
-  apply plugin: 'java'
-  apply plugin: 'eclipse-wtp'
-}
-"""
-        //when
-        executer.usingSettingsFile(settings).usingBuildScript(build).withTasks('eclipse').run()
-
-        //then
-        assert getComponentFile(project: 'impl').exists()
-        assert getFacetFile(project: 'impl').exists()
-
-        assert getComponentFile(project: 'contrib').exists()
-        assert getFacetFile(project: 'contrib').exists()
-    }
-
-    @Test
     @Issue("GRADLE-1881")
     void "uses eclipse project name for wtp module dependencies"() {
         //given
@@ -348,12 +315,12 @@ project(':contrib') {
         executer.usingSettingsFile(settings).usingBuildScript(build).withTasks('eclipse').run()
 
         //then
-        //the deploy name is correct:
-        assert getComponentFile(project: 'impl').text.contains('deploy-name="cool-impl"')
-        //the dependent-module name is correct:
-        assert getComponentFile(project: 'impl').text.contains('handle="module:/resource/cool-contrib/cool-contrib"')
-        //the submodule name is correct:
-        assert getComponentFile(project: 'contrib').text.contains('deploy-name="cool-contrib"')
+        def implComponent = wtpComponent('impl')
+        assert implComponent.deployName == 'cool-impl'
+        assert implComponent.project('cool-contrib')
+
+        def contribComponent = wtpComponent('contrib')
+        assert contribComponent.deployName == 'cool-contrib'
     }
 
     @Test
@@ -540,19 +507,15 @@ project(':contrib') {
         executer.withTasks("eclipse").run()
 
         //then
-        def classpath = getClasspathFile(print: true).text
-        def component = getComponentFile().text
+        def classpath = classpath
+        def component = wtpComponent
 
         //the jar dependency is configured in the WTP component file and in the classpath
-        assert classpath.contains('commons-io')
-        assert component.contains('commons-io')
-
-        assert classpath.contains('kind="var" path="MY_LIBS/myFoo.jar"')
-        assert component.contains('myFoo.jar')
+        classpath.lib('commons-io-1.4.jar').assertIsExcludedFromDeployment()
+        component.lib('commons-io-1.4.jar').assertDeployedAt('/WEB-INF/lib')
 
-        //the jar dependencies are configured as non-dependency in the .classpath
-        classpath.count(AbstractClasspathEntry.COMPONENT_NON_DEPENDENCY_ATTRIBUTE) == 2
-        classpath.count(AbstractClasspathEntry.COMPONENT_DEPENDENCY_ATTRIBUTE) == 0
+        classpath.lib('myFoo.jar').assertIsExcludedFromDeployment()
+        component.lib('myFoo.jar').assertDeployedAt('/WEB-INF/lib')
     }
 
     @Test
@@ -586,11 +549,8 @@ project(':contrib') {
         executer.withTasks("eclipse").run()
 
         //then
-        def classpath = getClasspathFile(print: true).text
-        //component dependency wins:
-        assert classpath.contains(AbstractClasspathEntry.COMPONENT_DEPENDENCY_ATTRIBUTE)
-        //non-dependency (our default) loses:
-        assert !classpath.contains(AbstractClasspathEntry.COMPONENT_NON_DEPENDENCY_ATTRIBUTE)
+        def classpath = classpath
+        classpath.lib('commons-io-1.4.jar').assertIsDeployedTo('WEB-INF/lib')
     }
 
     @Test
@@ -627,14 +587,10 @@ project(':contrib') {
         executer.withTasks("eclipse").run()
 
         //then
-        def classpath = getClasspathFile(project: 'someLib', print: true).text
-
-        //contains both entries
-        assert classpath.contains('kind="var" path="MY_LIBS/myFoo.jar"')
-        assert classpath.contains('commons-io')
+        def classpath = classpath('someLib')
 
-        //both var and lib entries have the attribute
-        classpath.count('<attribute name="org.eclipse.jst.component.dependency" value="../"/>') == 2
+        classpath.lib('commons-io-1.4.jar').assertIsDeployedTo('../')
+        classpath.lib('myFoo.jar').assertIsDeployedTo('../')
     }
 
     protected def contains(String ... contents) {
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy
new file mode 100644
index 0000000..f34a574
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpWebAndJavaProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for web project and java project it depends on"() {
+        settingsFile << "include 'web', 'java'"
+
+        file('java/src/main/java').mkdirs()
+        file('web/src/main/java').mkdirs()
+        file('web/src/main/webapp').mkdirs()
+
+        buildFile << """
+subprojects {
+    apply plugin: 'eclipse-wtp'
+
+    repositories {
+        jcenter()
+    }
+}
+project(':web') {
+    apply plugin: 'war'
+
+    sourceCompatibility = 1.6
+
+    dependencies {
+        providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
+        compile 'org.apache.commons:commons-lang3:3.0'
+        compile project(':java')
+        testCompile "junit:junit:4.12"
+    }
+}
+project(':java') {
+    apply plugin: 'java'
+
+    sourceCompatibility = 1.6
+
+    dependencies {
+        compile 'com.google.guava:guava:18.0'
+        compile 'javax.servlet:javax.servlet-api:3.1.0'
+        testCompile "junit:junit:4.12"
+    }
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // Builders and natures
+        def javaProject = project('java')
+        javaProject.assertHasJavaFacetNatures()
+        javaProject.assertHasJavaFacetBuilders()
+
+        def webProject = project('web')
+        webProject.assertHasJavaFacetNatures()
+        webProject.assertHasJavaFacetBuilders()
+
+        // Classpath
+        def javaClasspath = classpath('java')
+        javaClasspath.assertHasLibs('guava-18.0.jar', 'javax.servlet-api-3.1.0.jar', 'junit-4.12.jar', 'hamcrest-core-1.3.jar')
+        javaClasspath.lib('guava-18.0.jar').assertIsDeployedTo('../')
+        javaClasspath.lib('javax.servlet-api-3.1.0.jar').assertIsDeployedTo('../') // TODO - this is not right
+        javaClasspath.lib('junit-4.12.jar').assertIsExcludedFromDeployment()
+        javaClasspath.lib('hamcrest-core-1.3.jar').assertIsExcludedFromDeployment()
+
+        def webClasspath = classpath('web')
+        webClasspath.assertHasLibs('commons-lang3-3.0.jar', 'javax.servlet-api-3.1.0.jar', 'junit-4.12.jar', 'hamcrest-core-1.3.jar')
+        webClasspath.lib('commons-lang3-3.0.jar').assertIsExcludedFromDeployment()
+        webClasspath.lib('javax.servlet-api-3.1.0.jar').assertIsExcludedFromDeployment()
+        webClasspath.lib('junit-4.12.jar').assertIsExcludedFromDeployment()
+        webClasspath.lib('hamcrest-core-1.3.jar').assertIsExcludedFromDeployment()
+
+        // Facets
+        def javaFacets = wtpFacets('java')
+        javaFacets.assertHasFixedFacets("jst.java")
+        javaFacets.assertHasInstalledFacets("jst.utility", "jst.java")
+
+        def webFacets = wtpFacets('web')
+        webFacets.assertHasFixedFacets("jst.java", "jst.web")
+        webFacets.assertHasInstalledFacets("jst.web", "jst.java")
+
+        // Deployment
+        def javaComponent = wtpComponent('java')
+        javaComponent.deployName == 'java'
+        javaComponent.resources.size() == 1
+        javaComponent.sourceDirectory('src/main/java').assertDeployedAt('/')
+        javaComponent.modules.isEmpty()
+
+        def webComponent = wtpComponent('web')
+        webComponent.deployName == 'web'
+        webComponent.resources.size() == 2
+        webComponent.sourceDirectory('src/main/java').assertDeployedAt('/WEB-INF/classes')
+        webComponent.sourceDirectory('src/main/webapp').assertDeployedAt('/')
+        webComponent.modules.size() == 2
+        webComponent.lib('commons-lang3-3.0.jar').assertDeployedAt('/WEB-INF/lib')
+        webComponent.project('java').assertDeployedAt('/WEB-INF/lib')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy
new file mode 100644
index 0000000..ba66ad9
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.plugins.ide.eclipse
+
+class EclipseWtpWebProjectIntegrationTest extends AbstractEclipseIntegrationSpec {
+    def "generates configuration files for a web project"() {
+        file('src/main/java').mkdirs()
+        file('src/main/resources').mkdirs()
+        file('src/main/webapp').mkdirs()
+
+        settingsFile << "rootProject.name = 'web'"
+
+        buildFile << """
+apply plugin: 'eclipse-wtp'
+apply plugin: 'war'
+
+sourceCompatibility = 1.6
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile 'com.google.guava:guava:18.0'
+    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
+    testCompile "junit:junit:4.12"
+}
+"""
+
+        when:
+        run "eclipse"
+
+        then:
+        // Builders and natures
+        def project = project
+        project.assertHasJavaFacetNatures()
+        project.assertHasJavaFacetBuilders()
+
+        // Classpath
+        def classpath = classpath
+        classpath.assertHasLibs('guava-18.0.jar', 'javax.servlet-api-3.1.0.jar', 'junit-4.12.jar', 'hamcrest-core-1.3.jar')
+        classpath.lib('guava-18.0.jar').assertIsExcludedFromDeployment() // Is deployed using component definition instead
+        classpath.lib('javax.servlet-api-3.1.0.jar').assertIsExcludedFromDeployment()
+        classpath.lib('junit-4.12.jar').assertIsExcludedFromDeployment()
+        classpath.lib('hamcrest-core-1.3.jar').assertIsExcludedFromDeployment()
+
+        // Facets
+        def facets = wtpFacets
+        facets.assertHasFixedFacets("jst.java", "jst.web")
+        facets.assertHasInstalledFacets("jst.web", "jst.java")
+        facets.assertFacetVersion("jst.web", "2.4")
+        facets.assertFacetVersion("jst.java", "6.0")
+
+        // Deployment
+        def component = wtpComponent
+        component.deployName == 'web'
+        component.resources.size() == 3
+        component.sourceDirectory('src/main/java').assertDeployedAt('/WEB-INF/classes')
+        component.sourceDirectory('src/main/resources').assertDeployedAt('/WEB-INF/classes')
+        component.sourceDirectory('src/main/webapp').assertDeployedAt('/')
+        component.modules.size() == 1
+        component.lib('guava-18.0.jar').assertDeployedAt('/WEB-INF/lib')
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaDependencySubstitutionIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaDependencySubstitutionIntegrationTest.groovy
new file mode 100644
index 0000000..4937b10
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaDependencySubstitutionIntegrationTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.idea
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.plugins.ide.AbstractIdeIntegrationTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+class IdeaDependencySubstitutionIntegrationTest extends AbstractIdeIntegrationTest {
+    @Rule
+    public final TestResources testResources = new TestResources(testDirectoryProvider)
+
+    @Test
+    void "external dependency substituted with project dependency"() {
+        runTask("idea", "include 'project1', 'project2'", """
+allprojects {
+    apply plugin: "java"
+    apply plugin: "idea"
+}
+
+project(":project2") {
+    dependencies {
+        compile group: "junit", name: "junit", version: "4.7"
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withModule("junit:junit") {
+            it.useTarget project(":project1")
+        }
+    }
+}
+""")
+
+        def dependencies = parseIml("project2/project2.iml").dependencies
+        assert dependencies.libraries.size() == 0
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule('COMPILE', 'project1')
+    }
+
+    @Test
+    @Ignore("not supported in 2.4 - LD - 14/4/15")
+    void "transitive external dependency substituted with project dependency"() {
+        mavenRepo.module("org.gradle", "module1").dependsOn("module2").publish()
+        mavenRepo.module("org.gradle", "module2").publish()
+
+        runTask("idea", "include 'project1', 'project2'", """
+allprojects {
+    apply plugin: "java"
+    apply plugin: "idea"
+}
+
+project(":project2") {
+    repositories {
+        maven { url "${mavenRepo.uri}" }
+    }
+
+    dependencies {
+        compile "org.gradle:module1:1.0"
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withModule("org.gradle:module2") {
+            it.useTarget project(":project1")
+        }
+    }
+}
+""")
+
+        def dependencies = parseIml("project2/project2.iml").dependencies
+        assert dependencies.libraries.size() == 1
+        dependencies.assertHasLibrary("COMPILE", "module1-1.0.jar")
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule('COMPILE', 'project1')
+    }
+
+    @Test
+    void "project dependency substituted with external dependency"() {
+        runTask("idea", "include 'project1', 'project2'", """
+allprojects {
+    apply plugin: "java"
+    apply plugin: "idea"
+}
+
+project(":project2") {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        compile project(":project1")
+    }
+
+    configurations.all {
+        resolutionStrategy.dependencySubstitution.withProject(":project1") {
+            it.useTarget "junit:junit:4.7"
+        }
+    }
+}
+""")
+
+        def dependencies = parseIml("project2/project2.iml").dependencies
+        assert dependencies.libraries.size() == 1
+        dependencies.assertHasLibrary('COMPILE', 'junit-4.7.jar')
+        assert dependencies.modules.size() == 0
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy
index 114017f..4f7d20a 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaIntegrationTest.groovy
@@ -140,11 +140,11 @@ dependencies {
     }
 
     @Test
-        void libraryReferenceSubstitutesPathVariable() {
-            def repoDir = file("repo")
-            def artifact1 = maven(repoDir).module("myGroup", "myArtifact1").publish().artifactFile
+    void libraryReferenceSubstitutesPathVariable() {
+        def repoDir = file("repo")
+        def artifact1 = maven(repoDir).module("myGroup", "myArtifact1").publish().artifactFile
 
-            runIdeaTask """
+        runIdeaTask """
     apply plugin: "java"
     apply plugin: "idea"
 
@@ -161,13 +161,13 @@ dependencies {
     }
             """
 
-            def module = parseImlFile("root")
-            def libs = module.component.orderEntry.library
-            assert libs.size() == 1
-            assert libs.CLASSES.root*. at url*.text().collect { new File(it).name } as Set == [artifact1.name + "!"] as Set
-            assert libs.CLASSES.root*. at url*.text().findAll(){ it.contains("\$GRADLE_REPO\$") }.size() == 1
-            assert libs.CLASSES.root*. at url*.text().collect { it.replace("\$GRADLE_REPO\$", relPath(repoDir))} as Set == ["jar://${relPath(artifact1)}!/"] as Set
-        }
+        def module = parseImlFile("root")
+        def libs = module.component.orderEntry.library
+        assert libs.size() == 1
+        assert libs.CLASSES.root*. at url*.text().collect { new File(it).name } as Set == [artifact1.name + "!"] as Set
+        assert libs.CLASSES.root*. at url*.text().findAll() { it.contains("\$GRADLE_REPO\$") }.size() == 1
+        assert libs.CLASSES.root*. at url*.text().collect { it.replace("\$GRADLE_REPO\$", relPath(repoDir)) } as Set == ["jar://${relPath(artifact1)}!/"] as Set
+    }
 
     @Test
     void onlyAddsSourceDirsThatExistOnFileSystem() {
@@ -437,7 +437,7 @@ idea.project {
         urls.any { it.endsWith(path) }
     }
 
-    private String relPath(File file){
+    private String relPath(File file) {
         return file.absolutePath.replace(File.separator, "/")
     }
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleFixture.groovy
new file mode 100644
index 0000000..890fa5a
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleFixture.groovy
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.idea
+
+import groovy.util.slurpersupport.GPathResult
+
+class IdeaModuleFixture {
+    private GPathResult iml
+
+    IdeaModuleFixture(GPathResult iml) {
+        this.iml = iml
+    }
+
+    ImlDependencies getDependencies() {
+        new ImlDependencies(iml.component.orderEntry.collect { it ->
+            if (it. at type == 'module-library') {
+                def lib = new ImlLibrary()
+                lib.type = 'module-library'
+                lib.url = it.library.CLASSES.root. at url.text()
+                lib.scope = it. at scope != '' ? it. at scope : 'COMPILE'
+                lib.source = it.library.SOURCES.root. at url*.text()
+                lib.javadoc = it.library.JAVADOC.root. at url*.text()
+                return lib
+            } else if (it. at type == 'module') {
+                def module = new ImlModule()
+                module.type = 'module'
+                module.scope = it. at scope != '' ? it. at scope : 'COMPILE'
+                module.moduleName = it.'@module-name'
+                return module
+            } else if (it. at type == 'sourceFolder') {
+                def source = new ImlSource()
+                source.type = 'sourceFolder'
+                source.forTests = it. at forTests
+                return source
+            } else {
+                def dep = new ImlDependency()
+                dep.type = it. at type
+                return dep
+            }
+        })
+
+    }
+
+    class ImlDependencies {
+        List<ImlDependency> dependencies
+
+        private ImlDependencies(List<ImlDependency> dependencies) {
+            this.dependencies = dependencies
+        }
+
+        List<ImlLibrary> getLibraries() {
+            dependencies.findAll { it.type == 'module-library' }
+        }
+
+        List<ImlModule> getModules() {
+            dependencies.findAll { it.type == 'module' }
+        }
+
+        void assertHasInheritedJdk() {
+            assert dependencies.any { ImlDependency it ->
+                it.type == 'inheritedJdk'
+            }
+        }
+
+        void assertHasSource(String forTests) {
+            assert dependencies.findAll { it.type == 'sourceFolder' }.any { ImlSource it -> it.forTests = forTests }
+        }
+
+        void assertHasModule(String scope, String name) {
+            assert modules.any { ImlModule it ->
+                it.scope == scope && it.moduleName == name
+            }
+        }
+
+        void assertHasLibrary(String scope, String name) {
+            assert libraries.any { ImlLibrary it ->
+                it.scope == scope && it.url.contains(name)
+            }
+        }
+    }
+
+    class ImlDependency {
+        String type
+    }
+
+    class ImlSource extends ImlDependency {
+        String forTests
+    }
+
+    class ImlModule extends ImlDependency {
+        String scope
+        String moduleName
+    }
+
+    class ImlLibrary extends ImlDependency {
+        String scope
+        String url
+        List<String> javadoc
+        List<String> source
+
+        @Override
+        public String toString() {
+            return "ImlLibrary{" +
+                    "type='" + type + '\'' +
+                    ", scope='" + scope + '\'' +
+                    ", url='" + url + '\'' +
+                    '}';
+        }
+
+        String getJarName() {
+            return url.replaceFirst('!/$', '').split('/').last()
+        }
+
+        void assertHasSource(List<String> filenames) {
+            assert source.collect { it.replaceFirst('!/$', '').split('/').last() } == filenames
+        }
+
+        void assertHasJavadoc(List<String> filenames) {
+            assert javadoc.collect { it.replaceFirst('!/$', '').split('/').last() } == filenames
+        }
+
+        void assertHasNoJavadoc() {
+            assert javadoc.empty
+        }
+
+        void assertHasNoSource() {
+            assert source.empty
+        }
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy
index 29487ac..97ad347 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaModuleIntegrationTest.groovy
@@ -61,7 +61,7 @@ idea {
         testSourceDirs += file('additionalCustomTestSources')
         excludeDirs += file('excludeMePlease')
 
-        scopes.PROVIDED.plus += configurations.compile
+        scopes.PROVIDED.plus += [ configurations.compile ]
         downloadJavadoc = true
         downloadSources = false
 
@@ -122,7 +122,7 @@ dependencies {
 
 idea {
     module {
-        scopes.COMPILE.plus += configurations.foo
+        scopes.COMPILE.plus << configurations.foo
         scopes.COMPILE.minus += [configurations.bar, configurations.baz]
     }
 }
@@ -137,6 +137,34 @@ idea {
         assert !content.contains('foo3.jar')
     }
 
+    @Issue("GRADLE-3101")
+    @Test
+    void scopesCustomizedUsingPlusEqualOperator() {
+        //when
+        runTask 'idea', '''
+apply plugin: "java"
+apply plugin: "idea"
+
+configurations {
+  bar
+}
+
+idea {
+    module {
+        scopes.COMPILE.plus += [ configurations.bar ]
+    }
+}
+
+dependencies {
+  bar files('bar.jar')
+}
+'''
+        def content = getFile([:], 'root.iml').text
+
+        //then
+        assert content.contains('bar.jar')
+    }
+
     @Test
     void allowsReconfiguringBeforeOrAfterMerging() {
         //given
@@ -190,14 +218,11 @@ apply plugin: "idea"
 sourceSets.main.output.dir "$buildDir/generated/main"
 sourceSets.test.output.dir "$buildDir/ws/test"
 '''
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['RUNTIME', 'TEST'] }
-
-        def classesDirs = iml.component.orderEntry.library.CLASSES.root. at url.collect { it.text() }
-        assert classesDirs.any { it.contains ('generated/main') }
-        assert classesDirs.any { it.contains ('ws/test') }
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.size() == 2
+        dependencies.assertHasLibrary('RUNTIME', 'generated/main')
+        dependencies.assertHasLibrary('TEST', 'ws/test')
     }
 
     @Test
@@ -391,19 +416,12 @@ dependencies {
     runtime 'org.gradle:impl-artifact:1.0'
 }
 """
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['RUNTIME', 'TEST'] }
-
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.findAll { it. at type == 'module-library' }.size() == 3
-        assert orderEntries.any { it. at type == 'module-library' && // no scope means COMPILE
-                it.library.CLASSES.root. at url.text().contains ('api-artifact-1.0.jar') }
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'RUNTIME' &&
-                it.library.CLASSES.root. at url.text().contains ('impl-artifact-1.0.jar') }
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'TEST' &&
-                it.library.CLASSES.root. at url.text().contains ('impl-artifact-1.0.jar') }
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.size() == 3
+        dependencies.assertHasLibrary('COMPILE', 'api-artifact-1.0.jar')
+        dependencies.assertHasLibrary('RUNTIME', 'impl-artifact-1.0.jar')
+        dependencies.assertHasLibrary('TEST', 'impl-artifact-1.0.jar')
     }
 
     @Test
@@ -427,19 +445,14 @@ dependencies {
 
 idea {
   module {
-    scopes.PROVIDED.plus += configurations.compile
+    scopes.PROVIDED.plus << configurations.compile
   }
 }
 """
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['PROVIDED'] }
-
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.findAll { it. at type == 'module-library' }.size() == 1
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'PROVIDED' &&
-                it.library.CLASSES.root. at url.text().contains ('api-artifact-1.0.jar') }
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.size() == 1
+        dependencies.assertHasLibrary('PROVIDED', 'api-artifact-1.0.jar')
     }
 
     @Test
@@ -469,22 +482,16 @@ dependencies {
 
 idea {
   module {
-    scopes.PROVIDED.plus += configurations.myCustom
-    scopes.COMPILE.plus += configurations.myCustom
+    scopes.PROVIDED.plus << configurations.myCustom
+    scopes.COMPILE.plus << configurations.myCustom
   }
 }
 """
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['PROVIDED'] }
-
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.findAll { it. at type == 'module-library' }.size() == 2
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'PROVIDED' &&
-                it.library.CLASSES.root. at url.text().contains ('bar-1.0.jar') }
-        assert orderEntries.any { it. at type == 'module-library' &&
-                it.library.CLASSES.root. at url.text().contains ('api-artifact-1.0.jar') }
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.size() == 2
+        dependencies.assertHasLibrary('PROVIDED', 'bar-1.0.jar')
+        dependencies.assertHasLibrary('COMPILE', 'api-artifact-1.0.jar')
     }
 
     @Test
@@ -521,19 +528,12 @@ idea {
   }
 }
 """
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['TEST', 'RUNTIME'] }
-
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.findAll { it. at type == 'module-library' }.size() == 3
-        assert orderEntries.any { it. at type == 'module-library' &&
-                it.library.CLASSES.root. at url.text().contains ('api-artifact-1.0.jar') }
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'TEST' &&
-                it.library.CLASSES.root. at url.text().contains ('bar-1.0.jar') }
-        assert orderEntries.any { it. at type == 'module-library' && it. at scope == 'RUNTIME' &&
-                it.library.CLASSES.root. at url.text().contains ('bar-1.0.jar') }
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.size() == 3
+        dependencies.assertHasLibrary('COMPILE', 'api-artifact-1.0.jar')
+        dependencies.assertHasLibrary('TEST', 'bar-1.0.jar')
+        dependencies.assertHasLibrary('RUNTIME', 'bar-1.0.jar')
     }
 
     @Test
@@ -559,10 +559,8 @@ dependencies {
     compile 'org.gradle:api-artifact:1.0'
 }
 """
-        def iml = parseFile(print: true, 'root.iml')
-
         //then
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.findAll { it. at type == 'module-library' }.size() == 0
+        def dependencies = parseIml("root.iml").dependencies
+        assert dependencies.libraries.isEmpty()
     }
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy
index 260771f..888057a 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaMultiModuleIntegrationTest.groovy
@@ -66,10 +66,14 @@ project(':shared:model') {
         executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("ideaModule").run()
 
         //then
-        def apiDeps = parseImlDependencies(project: 'master/api', "api.iml")
-        ['shared-api', 'model'].each { assert apiDeps.contains(it) }
-        def modelDeps = parseImlDependencies(project: 'master/shared/model', "model.iml")
-        ['util'].each { assert modelDeps.contains(it) }
+        def dependencies = parseIml("master/api/api.iml").dependencies
+        assert dependencies.modules.size() == 2
+        dependencies.assertHasModule("COMPILE", "shared-api")
+        dependencies.assertHasModule("TEST", "model")
+
+        dependencies = parseIml("master/shared/model/model.iml").dependencies
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule("TEST", "util")
     }
 
     @Test
@@ -136,30 +140,18 @@ project(':services:utilities') {
 
         //then
         assertIprContainsCorrectModules()
-        assertApiModuleContainsCorrectDependencies()
-        assertServicesUtilModuleContainsCorrectDependencies()
-    }
-
-    def assertServicesUtilModuleContainsCorrectDependencies() {
-        List moduleDeps = parseImlDependencies(project: 'master/services/utilities', "services-util.iml")
-
-        assert moduleDeps.contains("very-cool-model")
-        assert moduleDeps.contains("util")
-        assert moduleDeps.contains("shared-api")
-        assert moduleDeps.contains("contrib-services-util")
-    }
-
-    List parseImlDependencies(options, file) {
-        def iml = parseFile(options, file)
-        def moduleDeps = iml.component.orderEntry.'@module-name'.collect { it.text() }
-        return moduleDeps
-    }
-
-    def assertApiModuleContainsCorrectDependencies() {
-        def moduleDeps = parseImlDependencies(project: 'master/api', "api.iml")
 
-        assert moduleDeps.contains("very-cool-model")
-        assert moduleDeps.contains("shared-api")
+        def moduleDeps = parseIml("master/api/api.iml").dependencies
+        assert moduleDeps.modules.size() == 2
+        moduleDeps.assertHasModule("COMPILE", "shared-api")
+        moduleDeps.assertHasModule("COMPILE", "very-cool-model")
+
+        moduleDeps = parseIml("master/services/utilities/services-util.iml").dependencies
+        assert moduleDeps.modules.size() == 4
+        moduleDeps.assertHasModule("COMPILE", "shared-api")
+        moduleDeps.assertHasModule("COMPILE", "very-cool-model")
+        moduleDeps.assertHasModule("COMPILE", "util")
+        moduleDeps.assertHasModule("COMPILE", "contrib-services-util")
     }
 
     def assertIprContainsCorrectModules() {
@@ -222,6 +214,58 @@ project(':api') {
     }
 
     @Test
+    void handlesModuleDependencyCycles() {
+        def settingsFile = file("master/settings.gradle")
+        settingsFile << """
+include 'one'
+include 'two'
+include 'three'
+        """
+
+        def buildFile = file("master/build.gradle")
+        buildFile << """
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
+
+project(':one') {
+    dependencies {
+        compile project(':two')
+    }
+}
+
+project(':two') {
+    dependencies {
+        compile project(':three')
+    }
+}
+
+project(':three') {
+    dependencies {
+        compile project(':one')
+    }
+}
+"""
+
+        //when
+        executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("idea").run()
+
+        //then
+        def dependencies = parseIml("master/one/one.iml").dependencies
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule("COMPILE", "two")
+
+        dependencies = parseIml("master/two/two.iml").dependencies
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule("COMPILE", "three")
+
+        dependencies = parseIml("master/three/three.iml").dependencies
+        assert dependencies.modules.size() == 1
+        dependencies.assertHasModule("COMPILE", "one")
+    }
+
+    @Test
     void cleansCorrectlyWhenModuleNamesAreChangedOrDeduplicated() {
         def settingsFile = file("master/settings.gradle")
         settingsFile << "include 'api', 'shared:api', 'contrib'"
@@ -333,20 +377,14 @@ project(':app') {
 
         //when
         executer.usingBuildScript(buildFile).usingSettingsFile(settingsFile).withTasks("ideaModule").run()
-        def iml = parseFile(project: 'master/app', print: true, 'app.iml')
 
         //then
-        assert iml.component.orderEntry. at scope.collect { it.text() == ['RUNTIME', 'TEST'] }
-
-        def orderEntries = iml.component.orderEntry
-        assert orderEntries.size() == 5
-        assert orderEntries.any { it. at type == 'inheritedJdk' }
-        assert orderEntries.any { it. at type == 'sourceFolder' }
-        assert orderEntries.any { it. at type == 'module' &&
-                it.'@module-name'.text().contains ('api') }
-        assert orderEntries.any { it. at type == 'module' && it. at scope == 'RUNTIME' &&
-                it.'@module-name'.text().contains ('impl') }
-        assert orderEntries.any { it. at type == 'module' && it. at scope == 'TEST' &&
-                it.'@module-name'.text().contains ('impl') }
+        def dependencies = parseIml("master/app/app.iml").dependencies
+        assert dependencies.modules.size() == 3
+        dependencies.assertHasInheritedJdk()
+        dependencies.assertHasSource('false')
+        dependencies.assertHasModule('COMPILE', 'api')
+        dependencies.assertHasModule('TEST', 'impl')
+        dependencies.assertHasModule('RUNTIME', 'impl')
     }
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy
index 5bae9c3..bb59726 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaProjectIntegrationTest.groovy
@@ -64,6 +64,24 @@ idea.project {
     }
 
     @Test
+    void "allows configuring the VCS"() {
+        //when
+        runTask('idea', '''
+apply plugin: "java"
+apply plugin: "idea"
+
+idea.project {
+    vcs = 'Git'
+}
+''')
+
+        //then
+        def ipr = getFile([:], 'root.ipr').text
+
+        assert ipr.contains('<mapping directory="" vcs="Git"/>')
+    }
+
+    @Test
     void enablesCustomizationsOnNewModel() {
         //when
         runTask 'idea', 'include "someProjectThatWillBeExcluded", "api"', '''
@@ -122,6 +140,9 @@ idea {
   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="true" project-jdk-name="1.5">
     <output url="file://$PROJECT_DIR$/out"/>
   </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
 </project>
 '''
 
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaSourcesAndJavadocJarsIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaSourcesAndJavadocJarsIntegrationTest.groovy
index f76d16c..9a8e016 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaSourcesAndJavadocJarsIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaSourcesAndJavadocJarsIntegrationTest.groovy
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 package org.gradle.plugins.ide.idea
-
 import org.gradle.plugins.ide.AbstractSourcesAndJavadocJarsIntegrationTest
-import org.gradle.test.fixtures.maven.HttpArtifact
+import org.gradle.test.fixtures.server.http.HttpArtifact
 
 class IdeaSourcesAndJavadocJarsIntegrationTest extends AbstractSourcesAndJavadocJarsIntegrationTest {
     @Override
@@ -24,22 +23,19 @@ class IdeaSourcesAndJavadocJarsIntegrationTest extends AbstractSourcesAndJavadoc
         return "ideaModule"
     }
 
-    void ideFileContainsSourcesAndJavadocEntry(String sourcesClassifier = "sources", String javadocClassifier = "javadoc") {
-        def iml = parseFile("root.iml")
-
-        def sourcesUrl = iml.component.orderEntry.library.SOURCES.root. at url[0].text()
-        assert sourcesUrl.endsWith("/module-1.0-${sourcesClassifier}.jar!/")
-
-        def javadocUrl = iml.component.orderEntry.library.JAVADOC.root. at url[0].text()
-        assert javadocUrl.endsWith("/module-1.0-${javadocClassifier}.jar!/")
+    void ideFileContainsEntry(String jar, List<String> sources, List<String> javadocs) {
+        IdeaModuleFixture iml =  parseIml("root.iml")
+        def libraryEntry = iml.dependencies.libraries.find { it.jarName == jar }
+        assert libraryEntry != null : "entry for jar ${jar} not found"
+        libraryEntry.assertHasSource(sources)
+        libraryEntry.assertHasJavadoc(javadocs)
     }
 
     void ideFileContainsNoSourcesAndJavadocEntry() {
-        def iml = parseFile("root.iml")
-
-        assert iml.component.orderEntry.library.SOURCES.root.size() == 0
-
-        assert iml.component.orderEntry.library.JAVADOC.root.size() == 0
+        IdeaModuleFixture iml =  parseIml("root.iml")
+        iml.dependencies.libraries.size() == 1
+        iml.dependencies.libraries[0].assertHasNoJavadoc()
+        iml.dependencies.libraries[0].assertHasNoSource()
     }
 
     @Override
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle
new file mode 100644
index 0000000..cdbc5a9
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'java'
+
+sourceCompatibility = 1.7
+
+configurations {
+    provided
+    compile.extendsFrom provided
+}
+
+dependencies {
+    provided 'log4j:log4j:1.2.17'
+    compile project(':api')
+    runtime 'joda-time:joda-time:2.5'
+    testCompile 'junit:junit:4.12'
+}
+
+eclipse {
+    pathVariables GRADLE_USER_HOME: file("${gradle.gradleUserHomeDir}/caches")
+
+    wtp {
+        component {
+            minusConfigurations << configurations.provided
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/main/java/org/gradle/api/PersonList.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/main/java/org/gradle/api/PersonList.java
new file mode 100644
index 0000000..c313e67
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/main/java/org/gradle/api/PersonList.java
@@ -0,0 +1,5 @@
+package org.gradle.api;
+
+public class PersonList {
+   
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/main/resources/someprops.properties
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/main/resources/someprops.properties
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/test/java/org/gradle/shared/PersonTest.java b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/test/java/org/gradle/shared/PersonTest.java
new file mode 100644
index 0000000..1605126
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/src/test/java/org/gradle/shared/PersonTest.java
@@ -0,0 +1,9 @@
+package org.gradle.shared;
+
+import junit.framework.TestCase;
+
+public class PersonTest extends TestCase {
+    public void testTest() {
+        assertTrue(true);
+    }
+}
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
index 33179ec..9f83cec 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiClasspath.xml
@@ -12,9 +12,5 @@
 			<attribute name="org.eclipse.jst.component.dependency" value="../"/>
 		</attributes>
 	</classpathentry>
-	<classpathentry sourcepath="@CACHE_DIR@/junit/junit/4.7/@SHA1@/junit-4.7-sources.jar" kind="lib" path="@CACHE_DIR@/junit/junit/4.7/@SHA1@/junit-4.7.jar" exported="true">
-		<attributes>
-			<attribute name="org.eclipse.jst.component.dependency" value="../"/>
-		</attributes>
-	</classpathentry>
+	<classpathentry sourcepath="@CACHE_DIR@/junit/junit/4.7/@SHA1@/junit-4.7-sources.jar" kind="lib" path="@CACHE_DIR@/junit/junit/4.7/@SHA1@/junit-4.7.jar" exported="true"/>
 </classpath>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpComponent.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpComponent.xml
new file mode 100644
index 0000000..1cb3de4
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpComponent.xml
@@ -0,0 +1,6 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+	<wb-module deploy-name="api">
+		<wb-resource deploy-path="/" source-path="src/main/java"/>
+		<wb-resource deploy-path="/" source-path="src/main/resources"/>
+	</wb-module>
+</project-modules>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpFacet.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpFacet.xml
new file mode 100644
index 0000000..af820b6
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/apiWtpFacet.xml
@@ -0,0 +1,5 @@
+<faceted-project>
+	<fixed facet="jst.java"/>
+	<installed facet="jst.java" version="5.0"/>
+	<installed facet="jst.utility" version="1.0"/>
+</faceted-project>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonClasspath.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonClasspath.xml
new file mode 100644
index 0000000..71c32c9
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonClasspath.xml
@@ -0,0 +1,30 @@
+<classpath>
+	<classpathentry kind="output" path="bin"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="src" path="/api" exported="true"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER" exported="true"/>
+	<classpathentry sourcepath="GRADLE_USER_HOME/@CACHE@/log4j/log4j/1.2.17/@SHA1@/log4j-1.2.17-sources.jar" kind="var" path="GRADLE_USER_HOME/@CACHE@/log4j/log4j/1.2.17/@SHA1@/log4j-1.2.17.jar" exported="true">
+		<attributes>
+			<attribute name="javadoc_location" value="jar:file:@CACHE_DIR@/log4j/log4j/1.2.17/@SHA1@/log4j-1.2.17-javadoc.jar!/"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry sourcepath="GRADLE_USER_HOME/@CACHE@/joda-time/joda-time/2.5/@SHA1@/joda-time-2.5-sources.jar" kind="var"
+					path="GRADLE_USER_HOME/@CACHE@/joda-time/joda-time/2.5/@SHA1@/joda-time-2.5.jar" exported="true">
+		<attributes>
+			<attribute name="javadoc_location" value="jar:file:@CACHE_DIR@/joda-time/joda-time/2.5/@SHA1@/joda-time-2.5-javadoc.jar!/"/>
+			<attribute name="org.eclipse.jst.component.dependency" value="../"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry sourcepath="GRADLE_USER_HOME/@CACHE@/junit/junit/4.12/@SHA1@/junit-4.12-sources.jar" kind="var" path="GRADLE_USER_HOME/@CACHE@/junit/junit/4.12/@SHA1@/junit-4.12.jar" exported="true">
+		<attributes>
+			<attribute name="javadoc_location" value="jar:file:@CACHE_DIR@/junit/junit/4.12/@SHA1@/junit-4.12-javadoc.jar!/"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry sourcepath="GRADLE_USER_HOME/@CACHE@/org.hamcrest/hamcrest-core/1.3/@SHA1@/hamcrest-core-1.3-sources.jar" kind="var" path="GRADLE_USER_HOME/@CACHE@/org.hamcrest/hamcrest-core/1.3/@SHA1@/hamcrest-core-1.3.jar" exported="true">
+		<attributes>
+			<attribute name="javadoc_location" value="jar:file:@CACHE_DIR@/org.hamcrest/hamcrest-core/1.3/@SHA1@/hamcrest-core-1.3-javadoc.jar!/"/>
+		</attributes>
+	</classpathentry>
+</classpath>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonJdt.properties b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonJdt.properties
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonJdt.properties
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonProject.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonProject.xml
new file mode 100644
index 0000000..99b70eb
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonProject.xml
@@ -0,0 +1,26 @@
+<projectDescription>
+	<name>common</name>
+	<comment/>
+	<projects/>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+	</natures>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments/>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments/>
+		</buildCommand>
+	</buildSpec>
+	<linkedResources/>
+</projectDescription>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpComponent.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpComponent.xml
new file mode 100644
index 0000000..f38e1de
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpComponent.xml
@@ -0,0 +1,9 @@
+<project-modules id="moduleCoreId" project-version="2.0">
+	<wb-module deploy-name="common">
+		<wb-resource deploy-path="/" source-path="src/main/java"/>
+		<wb-resource deploy-path="/" source-path="src/main/resources"/>
+		<dependent-module deploy-path="../" handle="module:/resource/api/api">
+			<dependency-type>uses</dependency-type>
+		</dependent-module>
+	</wb-module>
+</project-modules>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpFacet.xml b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpFacet.xml
new file mode 100644
index 0000000..b14a4dc
--- /dev/null
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/commonWtpFacet.xml
@@ -0,0 +1,5 @@
+<faceted-project>
+	<fixed facet="jst.java"/>
+	<installed facet="jst.java" version="1.7"/>
+	<installed facet="jst.utility" version="1.0"/>
+</faceted-project>
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
index 55e556e..5b1e6e7 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/build.gradle
@@ -20,7 +20,7 @@ buildscript {
 defaultTasks 'eclipse', 'cleanEclipse'
 
 allprojects {
-    apply plugin: 'eclipse-wtp'
+    apply plugin: 'eclipse'
 
     group = 'org.gradle'
 
@@ -38,6 +38,10 @@ subprojects {
     version = '1.0'
 }
 
+configure(subprojects.findAll{ it.path in [ ':api', ':common', ':webAppJava6', ':webAppWithVars', ':webservice' ] }){
+    apply plugin: 'eclipse-wtp'
+}
+
 allprojects {
     afterEvaluate { p ->
         configure(p) {
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
index fd56bfc..5786502 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/master/settings.gradle
@@ -1 +1 @@
-includeFlat "api", "webservice", "groovyproject", "javabaseproject", "webAppWithVars", "webAppJava6"
+includeFlat "api", "common", "webservice", "groovyproject", "javabaseproject", "webAppWithVars", "webAppJava6"
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java
index 7058bf0..02178eb 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/GeneratorTask.java
@@ -20,9 +20,11 @@ import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
+import org.gradle.internal.reflect.Instantiator;
 import org.gradle.listener.ActionBroadcast;
 import org.gradle.plugins.ide.internal.generator.generator.Generator;
 
+import javax.inject.Inject;
 import java.io.File;
 
 /**
@@ -79,6 +81,11 @@ public class GeneratorTask<T> extends ConventionTask {
         generator.write(domainObject, getOutputFile());
     }
 
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * The input file to load the initial configuration from. Defaults to the output file. If the specified input file
      * does not exist, this task uses some default initial configuration.
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlFileContentMerger.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlFileContentMerger.groovy
index ad9f0ac..f4a7d5e 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlFileContentMerger.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlFileContentMerger.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.plugins.ide.api
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 
 /**
  * Models the generation/parsing/merging capabilities. Adds XML-related hooks.
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java
index 848b284..5325dd3 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/api/XmlGeneratorTask.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.api;
 
-import org.gradle.api.internal.xml.XmlTransformer;
+import org.gradle.internal.xml.XmlTransformer;
 import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject;
 import org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObjectGenerator;
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy
index 3a14788..d7eecf7 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipsePlugin.groovy
@@ -43,7 +43,6 @@ class EclipsePlugin extends IdePlugin {
     static final String ECLIPSE_JDT_TASK_NAME = "eclipseJdt"
 
     private final Instantiator instantiator
-    EclipseModel model
 
     @Inject
     EclipsePlugin(Instantiator instantiator) {
@@ -51,18 +50,18 @@ class EclipsePlugin extends IdePlugin {
     }
 
     @Override protected String getLifecycleTaskName() {
-        return 'eclipse'
+        return ECLIPSE_TASK_NAME
     }
 
     @Override protected void onApply(Project project) {
         lifecycleTask.description = 'Generates all Eclipse files.'
         cleanTask.description = 'Cleans all Eclipse files.'
 
-        model = project.extensions.create("eclipse", EclipseModel)
+        def model = project.extensions.create("eclipse", EclipseModel)
 
-        configureEclipseProject(project)
-        configureEclipseClasspath(project)
-        configureEclipseJdt(project)
+        configureEclipseProject(project, model)
+        configureEclipseClasspath(project, model)
+        configureEclipseJdt(project, model)
 
         hookDeduplicationToTheRoot(project)
     }
@@ -79,7 +78,7 @@ class EclipsePlugin extends IdePlugin {
         new EclipseNameDeduper().configureRoot(project.rootProject);
     }
 
-    private void configureEclipseProject(Project project) {
+    private void configureEclipseProject(Project project, EclipseModel model) {
         maybeAddTask(project, this, ECLIPSE_PROJECT_TASK_NAME, GenerateEclipseProject) {
             //task properties:
             description = "Generates the Eclipse project file."
@@ -114,7 +113,7 @@ class EclipsePlugin extends IdePlugin {
         }
     }
 
-    private void configureEclipseClasspath(Project project) {
+    private void configureEclipseClasspath(Project project, EclipseModel model) {
         model.classpath = instantiator.newInstance(EclipseClasspath, project)
         model.classpath.conventionMapping.defaultOutputDir = { new File(project.projectDir, 'bin') }
 
@@ -151,7 +150,7 @@ class EclipsePlugin extends IdePlugin {
                         def provided = ["scala-library", "scala-swing", "scala-dbc"]
                         def dependencies = classpath.plusConfigurations.collectMany { it.allDependencies }.findAll { it.name in provided }
                         if (!dependencies.empty) {
-                            classpath.minusConfigurations += project.configurations.detachedConfiguration(dependencies as Dependency[])
+                            classpath.minusConfigurations << project.configurations.detachedConfiguration(dependencies as Dependency[])
                         }
                     }
                 }
@@ -159,7 +158,7 @@ class EclipsePlugin extends IdePlugin {
         }
     }
 
-    private void configureEclipseJdt(Project project) {
+    private void configureEclipseJdt(Project project, EclipseModel model) {
         project.plugins.withType(JavaBasePlugin) {
             maybeAddTask(project, this, ECLIPSE_JDT_TASK_NAME, GenerateEclipseJdt) {
                 //task properties:
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.groovy
index a05c003..cd14de9 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.groovy
@@ -18,7 +18,6 @@ package org.gradle.plugins.ide.eclipse
 
 import org.gradle.api.JavaVersion
 import org.gradle.api.Project
-import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.WarPlugin
 import org.gradle.internal.reflect.Instantiator
@@ -35,12 +34,12 @@ class EclipseWtpPlugin extends IdePlugin {
     static final String ECLIPSE_WTP_FACET_TASK_NAME = "eclipseWtpFacet"
     static final String WEB_LIBS_CONTAINER = 'org.eclipse.jst.j2ee.internal.web.container'
 
-    @Override protected String getLifecycleTaskName() {
+    @Override
+    protected String getLifecycleTaskName() {
         return "eclipseWtp"
     }
 
     private final Instantiator instantiator
-    EclipseWtp eclipseWtpModel
 
     @Inject
     EclipseWtpPlugin(Instantiator instantiator) {
@@ -48,29 +47,65 @@ class EclipseWtpPlugin extends IdePlugin {
     }
 
     @Override protected void onApply(Project project) {
-        EclipsePlugin delegatePlugin = project.getPlugins().apply(EclipsePlugin.class);
-        delegatePlugin.model.wtp = instantiator.newInstance(EclipseWtp, delegatePlugin.model.classpath)
-        eclipseWtpModel = delegatePlugin.model.wtp
+        project.pluginManager.apply(EclipsePlugin)
+        def model = project.extensions.getByType(EclipseModel)
+        model.wtp = instantiator.newInstance(EclipseWtp, model.classpath)
 
         lifecycleTask.description = 'Generates Eclipse wtp configuration files.'
         cleanTask.description = 'Cleans Eclipse wtp configuration files.'
 
-        delegatePlugin.getLifecycleTask().dependsOn(getLifecycleTask())
-        delegatePlugin.getCleanTask().dependsOn(getCleanTask())
+        def delegatePlugin = project.plugins.getPlugin(EclipsePlugin)
+        delegatePlugin.lifecycleTask.dependsOn(lifecycleTask)
+        delegatePlugin.cleanTask.dependsOn(cleanTask)
 
-        configureEclipseProjectForPlugin(project, WarPlugin)
-        configureEclipseProjectForPlugin(project, EarPlugin)
-        configureEclipseClasspathForWarPlugin(project)
+        configureEclipseProject(project)
+        configureEclipseWtpComponent(project, model)
+        configureEclipseWtpFacet(project, model)
 
-        configureEclipseWtpComponent(project)
-        configureEclipseWtpFacet(project)
+        // do this after wtp is configured because wtp config is required to update classpath properly
+        configureEclipseClasspath(project, model)
     }
 
-    private void configureEclipseClasspathForWarPlugin(Project project) {
+    private void configureEclipseClasspath(Project project, EclipseModel eclipseModel) {
+        project.plugins.withType(JavaPlugin) {
+            def deleteWtpDependentModule = []
+            eclipseModel.classpath.file.whenMerged { Classpath classpath ->
+                if (hasWarOrEarPlugin(project)) {
+                    return
+                }
+
+                def minusFiles = eclipseModel.wtp.component.minusConfigurations*.files?.flatten() ?: project.files()
+                def libFiles = eclipseModel.wtp.component.libConfigurations*.files?.flatten() ?: project.files()
+                for (entry in classpath.entries) {
+                    if (!(entry instanceof AbstractLibrary)) {
+                        continue;
+                    }
+                    if (minusFiles.contains(entry.library.file) || !libFiles.contains(entry.library.file)) {
+                        // Mark this library as not required for deployment
+                        entry.entryAttributes[AbstractClasspathEntry.COMPONENT_NON_DEPENDENCY_ATTRIBUTE] = ''
+                    } else {
+                        // '../' and '/WEB-INF/lib' both seem to be correct (and equivalent) values here
+                        //this is necessary so that the depended upon projects will have their dependencies
+                        // deployed to WEB-INF/lib of the main project.
+                        entry.entryAttributes[AbstractClasspathEntry.COMPONENT_DEPENDENCY_ATTRIBUTE] = '../'
+                        deleteWtpDependentModule << entry.path
+                    }
+                }
+            }
+            eclipseModel.wtp.component.file.whenMerged { WtpComponent wtpComponent ->
+                if (hasWarOrEarPlugin(project)) {
+                    return
+                }
+                wtpComponent.wbModuleEntries.removeAll { wbModule ->
+                    wbModule instanceof WbDependentModule && deleteWtpDependentModule.any { lib -> wbModule.handle.contains(lib) }
+                }
+            }
+        }
+
         project.plugins.withType(WarPlugin) {
-            project.eclipse.classpath.containers WEB_LIBS_CONTAINER
+            eclipseModel.classpath.containers WEB_LIBS_CONTAINER
 
-            project.eclipse.classpath.file.whenMerged { Classpath classpath ->
+            eclipseModel.classpath.file.whenMerged { Classpath classpath ->
                 for (entry in classpath.entries) {
                     if (entry instanceof AbstractLibrary) {
                         //this is necessary to avoid annoying warnings upon import to Eclipse
@@ -80,179 +115,113 @@ class EclipseWtpPlugin extends IdePlugin {
                     }
                 }
             }
-
-            doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
-                otherProject.eclipse.classpath.file.whenMerged { Classpath classpath ->
-                    for (entry in classpath.entries) {
-                        if (entry instanceof AbstractLibrary) {
-                            // '../' and '/WEB-INF/lib' both seem to be correct (and equivalent) values here
-                            //this is necessary so that the depended upon projects will have their dependencies
-                            // deployed to WEB-INF/lib of the main project.
-                            entry.entryAttributes[AbstractClasspathEntry.COMPONENT_DEPENDENCY_ATTRIBUTE] = '../'
-                        }
-                    }
-                }
-            }
         }
     }
 
-    private void configureEclipseWtpComponent(Project project) {
-        configureEclipseWtpComponentWithType(project, WarPlugin)
-        configureEclipseWtpComponentWithType(project, EarPlugin)
-    }
+    private void configureEclipseWtpComponent(Project project, EclipseModel model) {
+        maybeAddTask(project, this, ECLIPSE_WTP_COMPONENT_TASK_NAME, GenerateEclipseWtpComponent) {
+            //task properties:
+            description = 'Generates the Eclipse WTP component settings file.'
+            inputFile = project.file('.settings/org.eclipse.wst.common.component')
+            outputFile = project.file('.settings/org.eclipse.wst.common.component')
 
-    private void configureEclipseWtpComponentWithType(Project project, Class<?> type) {
-        project.plugins.withType(type) {
-            maybeAddTask(project, this, ECLIPSE_WTP_COMPONENT_TASK_NAME, GenerateEclipseWtpComponent) {
-                //task properties:
-                description = 'Generates the Eclipse WTP component settings file.'
-                inputFile = project.file('.settings/org.eclipse.wst.common.component')
-                outputFile = project.file('.settings/org.eclipse.wst.common.component')
+            //model properties:
+            model.wtp.component = component
 
-                //model properties:
-                eclipseWtpModel.component = component
-
-                component.conventionMapping.deployName = { project.eclipse.project.name }
-
-                if (WarPlugin.class.isAssignableFrom(type)) {
-                    component.libConfigurations = [project.configurations.runtime]
-                    component.minusConfigurations = [project.configurations.providedRuntime]
-                    component.conventionMapping.contextPath = { project.war.baseName }
-                    component.conventionMapping.resources = { [new WbResource('/', project.convention.plugins.war.webAppDirName)] }
-                    component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
-                } else if (EarPlugin.class.isAssignableFrom(type)) {
-                    component.rootConfigurations = [project.configurations.deploy]
-                    component.libConfigurations = [project.configurations.earlib]
-                    component.minusConfigurations = []
-                    component.classesDeployPath = "/"
-                    component.libDeployPath = "/lib"
-                    component.conventionMapping.sourceDirs = { [project.file { project.appDirName }] as Set }
-                    project.plugins.withType(JavaPlugin) {
-                        component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
-                    }
+            component.conventionMapping.deployName = { model.project.name }
+            project.plugins.withType(JavaPlugin) {
+                if (hasWarOrEarPlugin(project)) {
+                    return
                 }
-            }
-
-            doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
-                def eclipseWtpPlugin = otherProject.plugins.getPlugin(EclipseWtpPlugin)
-                // require Java plugin because we need source set 'main'
-                // (in the absence of 'main', it probably makes no sense to write the file)
-                otherProject.plugins.withType(JavaPlugin) {
-                    maybeAddTask(otherProject, eclipseWtpPlugin, ECLIPSE_WTP_COMPONENT_TASK_NAME, GenerateEclipseWtpComponent) {
-                        //task properties:
-                        description = 'Generates the Eclipse WTP component settings file.'
-                        inputFile = otherProject.file('.settings/org.eclipse.wst.common.component')
-                        outputFile = otherProject.file('.settings/org.eclipse.wst.common.component')
 
-                        //model properties:
-                        eclipseWtpPlugin.eclipseWtpModel.component = component
-
-                        component.conventionMapping.deployName = { otherProject.eclipse.project.name }
-                        component.conventionMapping.resources = {
-                            getMainSourceDirs(otherProject).collect { new WbResource("/", otherProject.relativePath(it)) }
-                        }
-                    }
+                component.libConfigurations = [project.configurations.runtime]
+                component.minusConfigurations = []
+                component.classesDeployPath = "/"
+                component.libDeployPath = "../"
+                component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
+            }
+            project.plugins.withType(WarPlugin) {
+                component.libConfigurations = [project.configurations.runtime]
+                component.minusConfigurations = [project.configurations.providedRuntime]
+                component.classesDeployPath = "/WEB-INF/classes"
+                component.libDeployPath = "/WEB-INF/lib"
+                component.conventionMapping.contextPath = { project.war.baseName }
+                component.conventionMapping.resources = { [new WbResource('/', project.convention.plugins.war.webAppDirName)] }
+                component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
+            }
+            project.plugins.withType(EarPlugin) {
+                component.rootConfigurations = [project.configurations.deploy]
+                component.libConfigurations = [project.configurations.earlib]
+                component.minusConfigurations = []
+                component.classesDeployPath = "/"
+                component.libDeployPath = "/lib"
+                component.conventionMapping.sourceDirs = { [project.file { project.appDirName }] as Set }
+                project.plugins.withType(JavaPlugin) {
+                    component.conventionMapping.sourceDirs = { getMainSourceDirs(project) }
                 }
             }
         }
     }
 
-    private void configureEclipseWtpFacet(Project project) {
-        configureEclipseWtpFacetWithType(project, WarPlugin)
-        configureEclipseWtpFacetWithType(project, EarPlugin)
-    }
+    private void configureEclipseWtpFacet(Project project, EclipseModel eclipseModel) {
+        maybeAddTask(project, this, ECLIPSE_WTP_FACET_TASK_NAME, GenerateEclipseWtpFacet) {
+            //task properties:
+            description = 'Generates the Eclipse WTP facet settings file.'
+            inputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+            outputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
 
-    private void configureEclipseWtpFacetWithType(Project project, Class<?> type) {
-        project.plugins.withType(type) {
-            maybeAddTask(project, this, ECLIPSE_WTP_FACET_TASK_NAME, GenerateEclipseWtpFacet) {
-                //task properties:
-                description = 'Generates the Eclipse WTP facet settings file.'
-                inputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-                outputFile = project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+            //model properties:
+            eclipseModel.wtp.facet = facet
 
-                //model properties:
-                eclipseWtpModel.facet = facet
-                if (WarPlugin.isAssignableFrom(type)) {
-                    facet.conventionMapping.facets = {
-                        [new Facet(FacetType.fixed, "jst.java", null), new Facet(FacetType.fixed, "jst.web", null),
-                                new Facet(FacetType.installed, "jst.web", "2.4"), new Facet(FacetType.installed, "jst.java", toJavaFacetVersion(project.sourceCompatibility))]
-                    }
-                } else if (EarPlugin.isAssignableFrom(type)) {
-                    facet.conventionMapping.facets = { [new Facet(FacetType.fixed, "jst.ear", null), new Facet(FacetType.installed, "jst.ear", "5.0")] }
+            project.plugins.withType(JavaPlugin) {
+                if (hasWarOrEarPlugin(project)) {
+                    return
                 }
-            }
-
-            doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
-                def eclipseWtpPlugin = otherProject.plugins.getPlugin(EclipseWtpPlugin)
-                maybeAddTask(otherProject, eclipseWtpPlugin, ECLIPSE_WTP_FACET_TASK_NAME, GenerateEclipseWtpFacet) {
-                    //task properties:
-                    description = 'Generates the Eclipse WTP facet settings file.'
-                    inputFile = otherProject.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-                    outputFile = otherProject.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
-
-                    //model properties:
-                    eclipseWtpPlugin.eclipseWtpModel.facet = facet
 
-                    facet.conventionMapping.facets = {
-                        [new Facet(FacetType.fixed, "jst.java", null), new Facet(FacetType.fixed, "jst.web", null),
-                                new Facet(FacetType.installed, "jst.utility", "1.0")]
-                    }
-                    otherProject.plugins.withType(JavaPlugin) {
-                        facet.conventionMapping.facets = {
-                            [new Facet(FacetType.fixed, "jst.java", null), new Facet(FacetType.fixed, "jst.web", null),
-                                    new Facet(FacetType.installed, "jst.utility", "1.0"), new Facet(FacetType.installed, "jst.java", toJavaFacetVersion(otherProject.sourceCompatibility))]
-                        }
-                    }
+                facet.conventionMapping.facets = {
+                    [new Facet(FacetType.fixed, "jst.java", null), new Facet(FacetType.installed, "jst.utility", "1.0"),
+                     new Facet(FacetType.installed, "jst.java", toJavaFacetVersion(project.sourceCompatibility))]
+                }
+            }
+            project.plugins.withType(WarPlugin) {
+                facet.conventionMapping.facets = {
+                    [new Facet(FacetType.fixed, "jst.java", null), new Facet(FacetType.fixed, "jst.web", null),
+                     new Facet(FacetType.installed, "jst.web", "2.4"), new Facet(FacetType.installed, "jst.java", toJavaFacetVersion(project.sourceCompatibility))]
+                }
+            }
+            project.plugins.withType(EarPlugin) {
+                facet.conventionMapping.facets = {
+                    [new Facet(FacetType.fixed, "jst.ear", null), new Facet(FacetType.installed, "jst.ear", "5.0")]
                 }
             }
         }
     }
 
     private void maybeAddTask(Project project, IdePlugin plugin, String taskName, Class taskType, Closure action) {
-        if (project.tasks.findByName(taskName)) { return }
+        if (project.tasks.findByName(taskName)) {
+            return
+        }
         def task = project.tasks.create(taskName, taskType)
         project.configure(task, action)
         plugin.addWorker(task)
     }
 
-    private void doLaterWithEachDependedUponEclipseProject(Project project, Closure action) {
-        project.gradle.projectsEvaluated {
-            eachDependedUponEclipseProject(project, action)
-        }
-    }
-
-    private void eachDependedUponEclipseProject(Project project, Closure action) {
-        def runtimeConfig = project.configurations.findByName("runtime")
-        if (runtimeConfig) {
-            def projectDeps = runtimeConfig.allDependencies.withType(ProjectDependency)
-            def dependedUponProjects = projectDeps*.dependencyProject
-            for (dependedUponProject in dependedUponProjects) {
-                dependedUponProject.plugins.withType(EclipseWtpPlugin) { action(dependedUponProject) }
-                eachDependedUponEclipseProject(dependedUponProject, action)
-            }
-        }
-    }
-
-    private void configureEclipseProjectForPlugin(Project project, Class<?> type) {
-        project.plugins.withType(type) {
+    private void configureEclipseProject(Project project) {
+        def configureClosure = {
             project.tasks.withType(GenerateEclipseProject) {
                 projectModel.buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
                 projectModel.buildCommand 'org.eclipse.wst.validation.validationbuilder'
                 projectModel.natures 'org.eclipse.wst.common.project.facet.core.nature'
                 projectModel.natures 'org.eclipse.wst.common.modulecore.ModuleCoreNature'
                 projectModel.natures 'org.eclipse.jem.workbench.JavaEMFNature'
-
-                doLaterWithEachDependedUponEclipseProject(project) { Project otherProject ->
-                    otherProject.tasks.withType(GenerateEclipseProject) {
-                        projectModel.buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
-                        projectModel.buildCommand 'org.eclipse.wst.validation.validationbuilder'
-                        projectModel.natures 'org.eclipse.wst.common.project.facet.core.nature'
-                        projectModel.natures 'org.eclipse.wst.common.modulecore.ModuleCoreNature'
-                        projectModel.natures 'org.eclipse.jem.workbench.JavaEMFNature'
-                    }
-                }
             }
         }
+        project.plugins.withType(JavaPlugin, configureClosure)
+        project.plugins.withType(EarPlugin, configureClosure)
+    }
+
+    private boolean hasWarOrEarPlugin(Project project) {
+        project.plugins.hasPlugin(WarPlugin) || project.plugins.hasPlugin(EarPlugin)
     }
 
     private Set<File> getMainSourceDirs(Project project) {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy
index ba77fd3..5433588 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspath.groovy
@@ -15,12 +15,9 @@
  */
 package org.gradle.plugins.ide.eclipse
 
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.tasks.SourceSet
 import org.gradle.plugins.ide.api.XmlGeneratorTask
 import org.gradle.plugins.ide.eclipse.model.Classpath
 import org.gradle.plugins.ide.eclipse.model.EclipseClasspath
-import org.gradle.util.DeprecationLogger
 
 /**
  * Generates an Eclipse <code>.classpath</code> file. If you want to fine tune the eclipse configuration
@@ -28,170 +25,22 @@ import org.gradle.util.DeprecationLogger
  * At this moment nearly all configuration is done via {@link EclipseClasspath}.
  */
 class GenerateEclipseClasspath extends XmlGeneratorTask<Classpath> {
-
+    /**
+     * The Eclipse Classpath model containing the information required to generate the classpath file.
+     */
     EclipseClasspath classpath
 
     GenerateEclipseClasspath() {
         xmlTransformer.indentation = "\t"
     }
 
-    @Override protected Classpath create() {
+    @Override
+    protected Classpath create() {
         return new Classpath(xmlTransformer, classpath.fileReferenceFactory)
     }
 
-    @Override protected void configure(Classpath xmlClasspath) {
+    @Override
+    protected void configure(Classpath xmlClasspath) {
         classpath.mergeXmlClasspath(xmlClasspath)
     }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.sourceSets. See examples in {@link EclipseClasspath}.
-     * <p>
-     * The source sets to be added to the classpath.
-     */
-    Iterable<SourceSet> getSourceSets() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.sourceSets", "eclipse.classpath.sourceSets")
-        classpath.sourceSets
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.sourceSets. See examples in {@link EclipseClasspath}.
-     * <p>
-     * The source sets to be added to the classpath.
-     */
-    void setSourceSets(Iterable<SourceSet> sourceSets) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.sourceSets", "eclipse.classpath.sourceSets")
-        classpath.sourceSets = sourceSets
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.plusConfigurations. See examples in {@link EclipseClasspath}.
-     * <p>
-     * The configurations which files are to be transformed into classpath entries.
-     */
-    Collection<Configuration> getPlusConfigurations() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.plusConfigurations", "eclipse.classpath.plusConfigurations")
-        classpath.plusConfigurations
-    }
-
-    void setPlusConfigurations(Collection<Configuration> plusConfigurations) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.plusConfigurations", "eclipse.classpath.plusConfigurations")
-        classpath.plusConfigurations = plusConfigurations
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.minusConfigurations. See examples in {@link EclipseClasspath}.
-     * <p>
-     * The configurations which files are to be excluded from the classpath entries.
-     */
-    Collection<Configuration> getMinusConfigurations() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.minusConfigurations", "eclipse.classpath.minusConfigurations")
-        classpath.minusConfigurations
-    }
-
-    void setMinusConfigurations(Collection<Configuration> minusConfigurations) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.minusConfigurations", "eclipse.classpath.minusConfigurations")
-        classpath.minusConfigurations = minusConfigurations
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.pathVariables. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Adds path variables to be used for replacing absolute paths in classpath entries.
-     *
-     * @param pathVariables A map with String->File pairs.
-     */
-    Map<String, File> getVariables() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.variables", "eclipse.pathVariables")
-        classpath.pathVariables
-    }
-
-    void setVariables(Map<String, File> variables) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.variables", "eclipse.pathVariables")
-        classpath.pathVariables = variables
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.containers. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Containers to be added to the classpath
-     */
-    Set<String> getContainers() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.containers", "eclipse.classpath.containers")
-        classpath.containers
-    }
-
-    void setContainers(Set<String> containers) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.containers", "eclipse.classpath.containers")
-        classpath.containers = containers
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.defaultOutputDir. See examples in {@link EclipseClasspath}.
-     * <p>
-     * The default output directory for eclipse generated files, eg classes.
-     */
-    File getDefaultOutputDir() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.defaultOutputDir", "eclipse.classpath.defaultOutputDir")
-        classpath.defaultOutputDir
-    }
-
-    void setDefaultOutputDir(File defaultOutputDir) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.defaultOutputDir", "eclipse.classpath.defaultOutputDir")
-        classpath.defaultOutputDir = defaultOutputDir
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.downloadSources. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Whether to download and add sources associated with the dependency jars. Defaults to true.
-     */
-    boolean getDownloadSources() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.downloadSources", "eclipse.classpath.downloadSources")
-        classpath.downloadSources
-    }
-
-    void setDownloadSources(boolean downloadSources) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.downloadSources", "eclipse.classpath.downloadSources")
-        classpath.downloadSources = downloadSources
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.downloadJavadoc. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Whether to download and add javadocs associated with the dependency jars. Defaults to false.
-     */
-    boolean getDownloadJavadoc() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.downloadJavadoc", "eclipse.classpath.downloadJavadoc")
-        classpath.downloadJavadoc
-    }
-
-    void setDownloadJavadoc(boolean downloadJavadoc) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.downloadJavadoc", "eclipse.classpath.downloadJavadoc")
-        classpath.downloadJavadoc = downloadJavadoc
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.classpath.containers. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Adds containers to the .classpath.
-     *
-     * @param containers the container names to be added to the .classpath.
-     */
-    void containers(String... containers) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.containers", "eclipse.classpath.containers")
-        classpath.containers(containers)
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.pathVariables. See examples in {@link EclipseClasspath}.
-     * <p>
-     * Adds variables to be used for replacing absolute paths in classpath entries.
-     *
-     * @param variables A map where the keys are the variable names and the values are the variable values.
-     */
-    void variables(Map<String, File> variables) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseClasspath.variables", "eclipse.pathVariables")
-        assert variables != null
-        classpath.pathVariables.putAll variables
-    }
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy
index 499be65..501904d 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseJdt.groovy
@@ -15,7 +15,6 @@
  */
 package org.gradle.plugins.ide.eclipse
 
-import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ide.api.PropertiesFileContentMerger
 import org.gradle.plugins.ide.api.PropertiesGeneratorTask
 import org.gradle.plugins.ide.eclipse.model.EclipseJdt
@@ -29,12 +28,12 @@ import org.gradle.plugins.ide.eclipse.model.Jdt
 class GenerateEclipseJdt extends PropertiesGeneratorTask<Jdt> {
 
     /**
-     * Eclipse project model that contains information needed for this task
+     * Eclipse JDT model that contains information needed to generate the JDT file.
      */
     EclipseJdt jdt
 
     GenerateEclipseJdt() {
-        jdt = services.get(Instantiator).newInstance(EclipseJdt, new PropertiesFileContentMerger(getTransformer()))
+        jdt = instantiator.newInstance(EclipseJdt, new PropertiesFileContentMerger(getTransformer()))
     }
     
     protected Jdt create() {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy
index cc1992f..69f9877 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseProject.groovy
@@ -15,7 +15,6 @@
  */
 package org.gradle.plugins.ide.eclipse
 
-import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ide.api.XmlFileContentMerger
 import org.gradle.plugins.ide.api.XmlGeneratorTask
 import org.gradle.plugins.ide.eclipse.model.EclipseProject
@@ -29,13 +28,13 @@ import org.gradle.plugins.ide.eclipse.model.Project
 class GenerateEclipseProject extends XmlGeneratorTask<Project> {
 
     /**
-     * model for eclipse project (.project) generation
+     * The Eclipse project model that contains the details required to generate the project file.
      */
     EclipseProject projectModel
 
     GenerateEclipseProject() {
         xmlTransformer.indentation = "\t"
-        projectModel = services.get(Instantiator).newInstance(EclipseProject, new XmlFileContentMerger(xmlTransformer))
+        projectModel = instantiator.newInstance(EclipseProject, new XmlFileContentMerger(xmlTransformer))
     }
 
     @Override protected Project create() {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy
index 5632f4c..f057792 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponent.groovy
@@ -15,15 +15,10 @@
  */
 package org.gradle.plugins.ide.eclipse
 
-import org.gradle.api.artifacts.Configuration
-import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ide.api.XmlFileContentMerger
 import org.gradle.plugins.ide.api.XmlGeneratorTask
 import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent
-import org.gradle.plugins.ide.eclipse.model.WbProperty
-import org.gradle.plugins.ide.eclipse.model.WbResource
 import org.gradle.plugins.ide.eclipse.model.WtpComponent
-import org.gradle.util.DeprecationLogger
 
 /**
  * Generates the org.eclipse.wst.common.component settings file for Eclipse WTP.
@@ -32,12 +27,14 @@ import org.gradle.util.DeprecationLogger
  * At this moment nearly all configuration is done via {@link EclipseWtpComponent}.
  */
 class GenerateEclipseWtpComponent extends XmlGeneratorTask<WtpComponent> {
-
+    /**
+     * The Eclipse WTP component model that contains details required to generate the settings file.
+     */
     EclipseWtpComponent component
 
     GenerateEclipseWtpComponent() {
         xmlTransformer.indentation = "\t"
-        component = services.get(Instantiator).newInstance(EclipseWtpComponent, project, new XmlFileContentMerger(xmlTransformer))
+        component = instantiator.newInstance(EclipseWtpComponent, project, new XmlFileContentMerger(xmlTransformer))
     }
 
     @Override protected WtpComponent create() {
@@ -47,159 +44,4 @@ class GenerateEclipseWtpComponent extends XmlGeneratorTask<WtpComponent> {
     @Override protected void configure(WtpComponent xmlComponent) {
         component.mergeXmlComponent(xmlComponent)
     }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.sourceDirs. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * The source directories to be transformed into wb-resource elements.
-     */
-    Set<File> getSourceDirs() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.sourceDirs", "eclipse.wtp.component.sourceDirs")
-        component.sourceDirs
-    }
-
-    void setSourceDirs(Set<File> sourceDirs) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.sourceDirs", "eclipse.wtp.component.sourceDirs")
-        component.sourceDirs = sourceDirs
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.plusConfigurations. See examples in {@link EclipseWtpComponent}.
-     */
-    Set<Configuration> getPlusConfigurations() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.plusConfigurations", "eclipse.wtp.component.plusConfigurations")
-        component.plusConfigurations
-    }
-
-    void setPlusConfigurations(Set<Configuration> plusConfigurations) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.plusConfigurations", "eclipse.wtp.component.plusConfigurations")
-        component.plusConfigurations = plusConfigurations
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.minusConfigurations. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * The configurations whose files are to be excluded from dependent-module elements.
-     */
-    Set<Configuration> getMinusConfigurations() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.minusConfigurations", "eclipse.wtp.component.minusConfigurations")
-        component.minusConfigurations
-    }
-
-    void setMinusConfigurations(Set<Configuration> minusConfigurations) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.minusConfigurations", "eclipse.wtp.component.minusConfigurations")
-        component.minusConfigurations = minusConfigurations
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.deployName. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * The deploy name to be used.
-     */
-    String getDeployName() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.deployName", "eclipse.wtp.component.deployName")
-        component.deployName
-    }
-
-    void setDeployName(String deployName) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.deployName", "eclipse.wtp.component.deployName")
-        component.deployName = deployName
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.pathVariables. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * The variables to be used for replacing absolute path in dependent-module elements.
-     */
-    Map<String, File> getVariables() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.variables", "eclipse.pathVariables")
-        component.pathVariables
-    }
-
-    void setVariables(Map<String, File> variables) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.variables", "eclipse.pathVariables")
-        component.pathVariables = variables
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.resources. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * Additional wb-resource elements.
-     */
-    List<WbResource> getResources() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.resources", "eclipse.wtp.component.resources")
-        component.resources
-    }
-
-    void setResources(List<WbResource> resources) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.resources", "eclipse.wtp.component.resources")
-        component.resources = resources
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.properties. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * Additional property elements.
-     */
-    List<WbProperty> getProperties() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.properties", "eclipse.wtp.component.properties")
-        component.properties
-    }
-
-    void setProperties(List<WbProperty> properties) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.properties", "eclipse.wtp.component.properties")
-        component.properties = properties
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.contextPath. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * The context path for the web application
-     */
-    String getContextPath() {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.contextPath", "eclipse.wtp.component.contextPath")
-        component.contextPath
-    }
-
-    void setContextPath(String contextPath) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.contextPath", "eclipse.wtp.component.contextPath")
-        component.contextPath = contextPath
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.pathVariables. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * Adds variables to be used for replacing absolute path in dependent-module elements.
-     *
-     * @param variables A map where the keys are the variable names and the values are the variable values.
-     */
-    void variables(Map<String, File> variables) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.variables", "eclipse.pathVariables")
-        assert variables != null
-        component.pathVariables.putAll variables
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.property. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * Adds a property.
-     *
-     * @param args A map that must contain a name and value key with corresponding values.
-     */
-    void property(Map<String, String> args) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.property", "eclipse.wtp.component.property")
-        component.property(args)
-    }
-
-    /**
-     * Deprecated. Please use #eclipse.wtp.component.resource. See examples in {@link EclipseWtpComponent}.
-     * <p>
-     * Adds a wb-resource.
-     *
-     * @param args A map that must contain a deployPath and sourcePath key with corresponding values.
-     */
-    void resource(Map<String, String> args) {
-        DeprecationLogger.nagUserOfReplacedMethod("eclipseWtpComponent.resource", "eclipse.wtp.component.resource")
-        component.resource(args)
-    }
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy
index 8adf9ef..5bcb77f 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacet.groovy
@@ -15,7 +15,6 @@
  */
 package org.gradle.plugins.ide.eclipse
 
-import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ide.api.XmlFileContentMerger
 import org.gradle.plugins.ide.api.XmlGeneratorTask
 import org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet
@@ -28,12 +27,14 @@ import org.gradle.plugins.ide.eclipse.model.WtpFacet
  * At this moment nearly all configuration is done via {@link EclipseWtpFacet}.
  */
 class GenerateEclipseWtpFacet extends XmlGeneratorTask<WtpFacet> {
-
+    /**
+     * The Eclipse WTP facet model containing the details required to generate the settings file.
+     */
     EclipseWtpFacet facet
 
     GenerateEclipseWtpFacet() {
         xmlTransformer.indentation = "\t"
-        facet = services.get(Instantiator).newInstance(EclipseWtpFacet, new XmlFileContentMerger(xmlTransformer))
+        facet = instantiator.newInstance(EclipseWtpFacet, new XmlFileContentMerger(xmlTransformer))
     }
 
     @Override protected WtpFacet create() {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy
index 40f5993..a1bcded 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Classpath.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.eclipse.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy
index 4e92eb3..4d011fd 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.groovy
@@ -46,13 +46,13 @@ import org.gradle.util.ConfigureUtil
  *
  *   classpath {
  *     //you can tweak the classpath of the Eclipse project by adding extra configurations:
- *     plusConfigurations += configurations.provided
+ *     plusConfigurations += [ configurations.provided ]
  *
  *     //you can also remove configurations from the classpath:
- *     minusConfigurations += configurations.someBoringConfig
+ *     minusConfigurations += [ configurations.someBoringConfig ]
  *
  *     //if you don't want some classpath entries 'exported' in Eclipse
- *     noExportConfigurations += configurations.provided
+ *     noExportConfigurations += [ configurations.provided ]
  *
  *     //if you want to append extra containers:
  *     containers 'someFriendlyContainer', 'andYetAnotherContainer'
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy
index 66a16b7..74f872c 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtp.groovy
@@ -85,12 +85,6 @@ class EclipseWtp {
     }
 
     /**
-     * Deprecated. EclipseWtp needs access to EclipseClasspath. Please use the other constructor.
-     */
-    @Deprecated
-    public EclipseWtp() {}
-
-    /**
      * @param eclipseClasspath - wtp needs access to classpath
      */
     public EclipseWtp(EclipseClasspath eclipseClasspath) {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy
index 89c7680..08034db 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpComponent.groovy
@@ -57,10 +57,10 @@ import org.gradle.util.ConfigureUtil
  *       sourceDirs += file('someExtraFolder')
  *
  *       //you can alter the files are to be transformed into dependent-module elements:
- *       plusConfigurations += configurations.someInterestingConfiguration
+ *       plusConfigurations += [ configurations.someInterestingConfiguration ]
  *
  *       //or whose files are to be excluded from dependent-module elements:
- *       minusConfigurations += configurations.anotherConfiguration
+ *       minusConfigurations << configurations.anotherConfiguration
  *
  *       //you can add a wb-resource elements; mandatory keys: 'sourcePath', 'deployPath':
  *       //if sourcePath points to non-existing folder it will *not* be added.
@@ -184,7 +184,7 @@ class EclipseWtpComponent {
      * @param args A map that must contain a deployPath and sourcePath key with corresponding values.
      */
     void resource(Map<String, String> args) {
-        resources.add(new WbResource(args.deployPath, args.sourcePath))
+        resources = getResources() + new WbResource(args.deployPath, args.sourcePath)
     }
 
     /**
@@ -202,7 +202,7 @@ class EclipseWtpComponent {
      * @param args A map that must contain a 'name' and 'value' key with corresponding values.
      */
     void property(Map<String, String> args) {
-        properties.add(new WbProperty(args.name, args.value))
+        properties = getProperties() + new WbProperty(args.name, args.value)
     }
 
    /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy
index 2b51ce4..8349607 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/EclipseWtpFacet.groovy
@@ -71,7 +71,6 @@ class EclipseWtpFacet {
      * For examples see docs for {@link EclipseWtpFacet}
      */
     List<Facet> facets = []
-    // TODO: What's the difference between fixed and installed facets? Why do we only model the latter?
 
     /**
      * Adds a facet.
@@ -81,7 +80,7 @@ class EclipseWtpFacet {
      * @param args A map that must contain a 'name' and 'version' key with corresponding values.
      */
     void facet(Map<String, ?> args) {
-        facets << ConfigureUtil.configureByMap(args, new Facet())
+        facets = getFacets() + ConfigureUtil.configureByMap(args, new Facet())
     }
 
     /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy
index f028835..0e90239 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Facet.groovy
@@ -18,6 +18,12 @@ package org.gradle.plugins.ide.eclipse.model
 
 class Facet {
 
+    /**
+     * An {@code installed} facet is really present on an Eclipse project whereas facet type {@code fixed} means that
+     * this facet is locked and cannot be simply removed. See also
+     * <a href="https://eclipse.org/articles/Article-BuildingProjectFacets/tutorial.html#defining.presets">here</a>.
+     */
+    @SuppressWarnings("FieldName")
     enum FacetType { installed, fixed }
 
     FacetType type
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy
index e43f0aa..d1c1a85 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/Project.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.eclipse.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
 /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy
index eb61741..044594a 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponent.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.eclipse.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
 /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy
index 0d4355d..ddbf9e1 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacet.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.eclipse.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
 /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/FileReferenceFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/FileReferenceFactory.groovy
index fb6068d..21c7ff4 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/FileReferenceFactory.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/FileReferenceFactory.groovy
@@ -56,7 +56,7 @@ class FileReferenceFactory {
     }
 
     /**
-     * Creates a reference to the given path. Returns null for for null path
+     * Creates a reference to the given path. Returns null for null path
      */
     FileReference fromPath(String path) {
         if (path == null) {
@@ -66,7 +66,7 @@ class FileReferenceFactory {
     }
 
     /**
-     * Creates a reference to the given path. Returns null for for null path
+     * Creates a reference to the given path. Returns null for null path
      */
     FileReference fromJarURI(String jarURI) {
         if (jarURI== null) {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy
index 933a57a..5f51fb9 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.groovy
@@ -25,7 +25,8 @@ import org.gradle.plugins.ide.internal.IdeDependenciesExtractor
 
 class WtpComponentFactory {
     void configure(EclipseWtpComponent wtp, WtpComponent component) {
-        def entries = getEntriesFromSourceDirs(wtp)
+        def entries = []
+        entries.addAll(getEntriesFromSourceDirs(wtp))
         entries.addAll(wtp.resources.findAll { wtp.project.file(it.sourcePath).isDirectory() } )
         entries.addAll(wtp.properties)
         // for ear files root deps are NOT transitive; wars don't use root deps so this doesn't hurt them
@@ -36,19 +37,19 @@ class WtpComponentFactory {
         component.configure(wtp.deployName, wtp.contextPath, entries)
     }
 
-    private List getEntriesFromSourceDirs(EclipseWtpComponent wtp) {
+    private List<WbResource> getEntriesFromSourceDirs(EclipseWtpComponent wtp) {
         wtp.sourceDirs.findAll { it.isDirectory() }.collect { dir ->
             new WbResource(wtp.classesDeployPath, wtp.project.relativePath(dir))
         }
     }
 
-    private List getEntriesFromConfigurations(Set plusConfigurations, Set minusConfigurations, EclipseWtpComponent wtp, String deployPath, boolean transitive) {
+    private List<WbDependentModule> getEntriesFromConfigurations(Set<Configuration> plusConfigurations, Set<Configuration> minusConfigurations, EclipseWtpComponent wtp, String deployPath, boolean transitive) {
         (getEntriesFromProjectDependencies(plusConfigurations, minusConfigurations, deployPath, transitive) as List) +
                 (getEntriesFromLibraries(plusConfigurations, minusConfigurations, wtp, deployPath) as List)
     }
 
     // must include transitive project dependencies
-    private Set getEntriesFromProjectDependencies(Set plusConfigurations, Set minusConfigurations, String deployPath, boolean transitive) {
+    private Set getEntriesFromProjectDependencies(Set<Configuration> plusConfigurations, Set<Configuration> minusConfigurations, String deployPath, boolean transitive) {
         def dependencies = getDependencies(plusConfigurations, minusConfigurations,
                 { it instanceof org.gradle.api.artifacts.ProjectDependency })
 
@@ -67,7 +68,7 @@ class WtpComponentFactory {
     }
 
     // TODO: might have to search all class paths of all source sets for project dependencies, not just runtime configuration
-    private void collectDependedUponProjects(org.gradle.api.Project project, LinkedHashSet result) {
+    private void collectDependedUponProjects(org.gradle.api.Project project, Set<org.gradle.api.Project> result) {
         def runtimeConfig = project.configurations.findByName("runtime")
         if (runtimeConfig) {
             def projectDeps = runtimeConfig.allDependencies.withType(org.gradle.api.artifacts.ProjectDependency)
@@ -80,7 +81,7 @@ class WtpComponentFactory {
     }
 
     // must NOT include transitive library dependencies
-    private Set getEntriesFromLibraries(Set plusConfigurations, Set minusConfigurations, EclipseWtpComponent wtp, String deployPath) {
+    private Set getEntriesFromLibraries(Set<Configuration> plusConfigurations, Set<Configuration> minusConfigurations, EclipseWtpComponent wtp, String deployPath) {
         def extractor = new IdeDependenciesExtractor()
         //below is not perfect because we're skipping the unresolved dependencies completely
         //however, it should be better anyway. Sometime soon we will hopefully change the wtp component stuff
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy
index 6205f28..193bc1a 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModule.groovy
@@ -29,7 +29,7 @@ import org.gradle.plugins.ide.idea.model.Module
 public class GenerateIdeaModule extends XmlGeneratorTask<Module> {
 
     /**
-     * Idea module model
+     * The Idea module model containing the details required to generate the module file.
      */
     IdeaModule module
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy
index c10348a..3e15d22 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaProject.groovy
@@ -27,7 +27,7 @@ import org.gradle.plugins.ide.idea.model.Project
 public class GenerateIdeaProject extends XmlGeneratorTask<Project> {
 
     /**
-     * idea project model
+     * The Idea project model containing the details required to generate the project file.
      */
     IdeaProject ideaProject;
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy
index f9c5de5..cd52f5b 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/GenerateIdeaWorkspace.groovy
@@ -24,7 +24,9 @@ import org.gradle.plugins.ide.idea.model.Workspace
  * There's little you can configure about workspace generation at the moment.
  */
 public class GenerateIdeaWorkspace extends XmlGeneratorTask<Workspace> {
-
+    /**
+     * The Idea workspace model containing the details required to generate the workspace file.
+     */
     IdeaWorkspace workspace
 
     @Override protected Workspace create() {
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy
index 8beec63..d3a10a6 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModule.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.plugins.ide.idea.model
 
+import org.gradle.api.Incubating
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.dsl.ConventionProperty
 import org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider
@@ -53,6 +54,9 @@ import org.gradle.util.ConfigureUtil
  *     //and some extra test source dirs
  *     testSourceDirs += file('some-extra-test-dir')
  *
+ *     //and hint to mark some of existing source dirs as generated sources
+ *     generatedSourceDirs += file('some-extra-source-folder')
+ *
  *     //and some extra dirs that should be excluded by IDEA
  *     excludeDirs += file('some-extra-exclude-dir')
  *
@@ -68,7 +72,7 @@ import org.gradle.util.ConfigureUtil
  *     jdkName = '1.6'
  *
  *     //if you need to put 'provided' dependencies on the classpath
- *     scopes.PROVIDED.plus += configurations.provided
+ *     scopes.PROVIDED.plus += [ configurations.provided ]
  *
  *     //if 'content root' (as IDEA calls it) of the module is different
  *     contentRoot = file('my-module-content-root')
@@ -159,6 +163,14 @@ class IdeaModule {
     Set<File> sourceDirs
 
     /**
+     * The directories containing the generated sources (both production and test sources).
+     * <p>
+     * For example see docs for {@link IdeaModule}
+     */
+    @Incubating
+    Set<File> generatedSourceDirs = []
+
+    /**
      * The keys of this map are the IDEA scopes. Each key points to another map that has two keys, plus and minus.
      * The values of those keys are collections of {@link org.gradle.api.artifacts.Configuration} objects. The files of the
      * plus configurations are added minus the files from the minus configurations. See example below...
@@ -179,7 +191,7 @@ class IdeaModule {
      *
      * idea {
      *   module {
-     *     scopes.PROVIDED.plus += configurations.provided
+     *     scopes.PROVIDED.plus += [ configurations.provided ]
      *   }
      * }
      * </pre>
@@ -326,16 +338,17 @@ class IdeaModule {
     void mergeXmlModule(Module xmlModule) {
         iml.beforeMerged.execute(xmlModule)
 
-        def path = { getPathFactory().path(it) }
-        def contentRoot = path(getContentRoot())
-        Set sourceFolders = getSourceDirs().findAll { it.exists() }.collect { path(it) }
-        Set testSourceFolders = getTestSourceDirs().findAll { it.exists() }.collect { path(it) }
-        Set excludeFolders = getExcludeDirs().collect { path(it) }
-        def outputDir = getOutputDir() ? path(getOutputDir()) : null
-        def testOutputDir = getTestOutputDir() ? path(getTestOutputDir()) : null
-        Set dependencies = resolveDependencies()
+        def path = { File f -> getPathFactory().path(f) }
+        Path contentRoot = path(getContentRoot())
+        Set<Path> sourceFolders = getSourceDirs().findAll { it.exists() }.collect { path(it) }
+        Set<Path> generatedSourceFolders = getGeneratedSourceDirs().findAll { it.exists() }.collect { path(it) }
+        Set<Path> testSourceFolders = getTestSourceDirs().findAll { it.exists() }.collect { path(it) }
+        Set<Path> excludeFolders = getExcludeDirs().collect { path(it) }
+        Path outputDir = getOutputDir() ? path(getOutputDir()) : null
+        Path testOutputDir = getTestOutputDir() ? path(getTestOutputDir()) : null
+        Set<Dependency> dependencies = resolveDependencies()
 
-        xmlModule.configure(contentRoot, sourceFolders, testSourceFolders, excludeFolders,
+        xmlModule.configure(contentRoot, sourceFolders, testSourceFolders, generatedSourceFolders, excludeFolders,
                 getInheritOutputDirs(), outputDir, testOutputDir, dependencies, getJdkName())
 
         iml.whenMerged.execute(xmlModule)
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy
index 8da5886..ca795b0 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaModuleIml.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.plugins.ide.idea.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.api.XmlFileContentMerger
 
 /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy
index 39d456f..99b5ae4 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/IdeaProject.groovy
@@ -41,14 +41,17 @@ import org.gradle.util.ConfigureUtil
  *     //you can update the source wildcards
  *     wildcards += '!?*.ruby'
  *
- *     //you can change the modules of the the *.ipr
+ *     //you can configure the VCS used by the project
+ *     vcs = 'Git'
+ *
+ *     //you can change the modules of the *.ipr
  *     //modules = project(':someProject').idea.module
  *
  *     //you can change the output file
  *     outputFile = new File(outputFile.parentFile, 'someBetterName.ipr')
  *
  *     //you can add project-level libraries
- *     projectLibraries << new ProjectLibrary(name: "my-library", classes: [new Path("path/to/library")])
+ *     projectLibraries << new ProjectLibrary(name: "my-library", classes: [new File("path/to/library")])
  *   }
  * }
  * </pre>
@@ -119,6 +122,16 @@ class IdeaProject {
     }
 
     /**
+     * The vcs for the project.
+     * <p>
+     * Values are the same as used in IDEA's “Version Control” preference window (e.g. 'Git', 'Subversion').
+     * <p>
+     * See the examples in the docs for {@link IdeaProject}.
+     */
+    @Incubating
+    String vcs
+
+    /**
      * The wildcard resource patterns.
      * <p>
      * See the examples in the docs for {@link IdeaProject}.
@@ -172,7 +185,7 @@ class IdeaProject {
         def modulePaths = getModules().collect {
             getPathFactory().relativePath('PROJECT_DIR', it.outputFile)
         }
-        xmlProject.configure(modulePaths, getJdkName(), getLanguageLevel(), getWildcards(), getProjectLibraries())
+        xmlProject.configure(modulePaths, getJdkName(), getLanguageLevel(), getWildcards(), getProjectLibraries(), getVcs())
         ipr.whenMerged.execute(xmlProject)
     }
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy
index 078a54b..2e6a290 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Module.groovy
@@ -15,9 +15,8 @@
  */
 package org.gradle.plugins.ide.idea.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
-import org.gradle.util.DeprecationLogger
 
 /**
  * Represents the customizable elements of an iml (via XML hooks everything of the iml is customizable).
@@ -42,6 +41,11 @@ class Module extends XmlPersistableConfigurationObject {
     Set<Path> testSourceFolders = [] as LinkedHashSet
 
     /**
+     * The directories containing generated the production sources. Must not be null.
+     */
+    Set<Path> generatedSourceFolders = [] as LinkedHashSet
+
+    /**
      * The directories to be excluded. Must not be null.
      */
     Set<Path> excludeFolders = [] as LinkedHashSet
@@ -69,16 +73,6 @@ class Module extends XmlPersistableConfigurationObject {
 
     String jdkName
 
-    String getJavaVersion() {
-        DeprecationLogger.nagUserOfReplacedMethod("javaVersion", "jdkName")
-        jdkName
-    }
-
-    void setJavaVersion(String jdkName) {
-        DeprecationLogger.nagUserOfReplacedMethod("javaVersion", "jdkName")
-        this.jdkName = jdkName
-    }
-
     private final PathFactory pathFactory
 
     Module(XmlTransformer withXmlActions, PathFactory pathFactory) {
@@ -110,6 +104,9 @@ class Module extends XmlPersistableConfigurationObject {
             } else {
                 testSourceFolders.add(pathFactory.path(sourceFolder. at url))
             }
+            if (sourceFolder. at generated == 'true') {
+                generatedSourceFolders.add(pathFactory.path(sourceFolder. at url))
+            }
         }
         findExcludeFolder().each { excludeFolder ->
             excludeFolders.add(pathFactory.path(excludeFolder. at url))
@@ -150,12 +147,13 @@ class Module extends XmlPersistableConfigurationObject {
         }
     }
 
-    protected def configure(Path contentPath, Set sourceFolders, Set testSourceFolders, Set excludeFolders,
+    protected def configure(Path contentPath, Set sourceFolders, Set testSourceFolders, Set generatedSourceFolders, Set excludeFolders,
                             Boolean inheritOutputDirs, Path outputDir, Path testOutputDir, Set dependencies, String jdkName) {
         this.contentPath = contentPath
         this.sourceFolders.addAll(sourceFolders)
-        this.testSourceFolders.addAll(testSourceFolders)
         this.excludeFolders.addAll(excludeFolders)
+        this.testSourceFolders.addAll(testSourceFolders)
+        this.generatedSourceFolders.addAll(generatedSourceFolders)
         if (inheritOutputDirs != null) {
             this.inheritOutputDirs = inheritOutputDirs
         }
@@ -240,11 +238,21 @@ class Module extends XmlPersistableConfigurationObject {
 
     private addSourceAndExcludeFolderToXml() {
         sourceFolders.each { Path path ->
-            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'false'])
+            if (generatedSourceFolders.contains(path)) {
+                findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'false', generated: 'true'])
+            } else {
+                findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'false'])
+            }
         }
+
         testSourceFolders.each { Path path ->
-            findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'true'])
+            if (generatedSourceFolders.contains(path)) {
+                findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'true', generated: 'true'])
+            } else {
+                findContent().appendNode('sourceFolder', [url: path.url, isTestSource: 'true'])
+            }
         }
+
         excludeFolders.each { Path path ->
             findContent().appendNode('excludeFolder', [url: path.url])
         }
@@ -311,6 +319,7 @@ class Module extends XmlPersistableConfigurationObject {
         if (excludeFolders != module.excludeFolders) { return false }
         if (outputDir != module.outputDir) { return false }
         if (sourceFolders != module.sourceFolders) { return false }
+        if (generatedSourceFolders != module.generatedSourceFolders) { return false }
         if (testOutputDir != module.testOutputDir) { return false }
         if (testSourceFolders != module.testSourceFolders) { return false }
 
@@ -321,6 +330,7 @@ class Module extends XmlPersistableConfigurationObject {
         int result;
 
         result = (sourceFolders != null ? sourceFolders.hashCode() : 0)
+        result = 31 * result + (generatedSourceFolders != null ? generatedSourceFolders.hashCode() : 0)
         result = 31 * result + (testSourceFolders != null ? testSourceFolders.hashCode() : 0)
         result = 31 * result + (excludeFolders != null ? excludeFolders.hashCode() : 0)
         result = 31 * result + (inheritOutputDirs != null ? inheritOutputDirs.hashCode() : 0)
@@ -336,6 +346,7 @@ class Module extends XmlPersistableConfigurationObject {
                 "dependencies=" + dependencies +
                 ", sourceFolders=" + sourceFolders +
                 ", testSourceFolders=" + testSourceFolders +
+                ", generatedSourceFolders=" + generatedSourceFolders +
                 ", excludeFolders=" + excludeFolders +
                 ", inheritOutputDirs=" + inheritOutputDirs +
                 ", outputDir=" + outputDir +
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy
index f6724cd..d7fe163 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/ModuleLibrary.groovy
@@ -52,8 +52,8 @@ class ModuleLibrary implements Dependency {
     def ModuleLibrary(Collection<Path> classes, Collection<Path> javadoc, Collection<Path> sources, Collection<JarDirectory> jarDirectories, String scope) {
         this.classes = classes as Set;
         this.jarDirectories = jarDirectories as Set;
-        this.javadoc = javadoc as Set;
-        this.sources = sources as Set;
+        this.javadoc = javadoc as LinkedHashSet;
+        this.sources = sources as LinkedHashSet;
         this.scope = scope
         this.exported = !scope || scope == 'COMPILE' || scope == 'RUNTIME'
     }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy
index c7735e8..27f69e3 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/PathFactory.groovy
@@ -43,7 +43,7 @@ class PathFactory {
         }
 
         if (match) {
-            return relativePath(match.dir, match.name, file)
+            return resolvePath(match.dir, match.name, file)
         }
 
         // IDEA doesn't like the result of file.toURI() so use the absolute path instead
@@ -56,10 +56,10 @@ class PathFactory {
      * Creates a path relative to the given path variable.
      */
     FilePath relativePath(String pathVar, File file) {
-        return relativePath(varsByName[pathVar], "\$$pathVar\$", file)
+        return resolvePath(varsByName[pathVar], "\$$pathVar\$", file)
     }
 
-    private FilePath relativePath(File rootDir, String rootDirName, File file) {
+    private FilePath resolvePath(File rootDir, String rootDirName, File file) {
         def relPath = getRelativePath(rootDir, rootDirName, file)
         def url = relativePathToURI(relPath)
         def canonicalUrl = relativePathToURI(file.absolutePath.replace(File.separator, '/'))
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy
index 94eb950..d441492 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Project.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.idea.model
 
 import org.gradle.api.Incubating
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
 /**
@@ -39,6 +39,12 @@ class Project extends XmlPersistableConfigurationObject {
     Jdk jdk
 
     /**
+     * The vcs used by the project.
+     */
+    @Incubating
+    String vcs
+
+    /**
      * The project-level libraries of the IDEA project.
      */
     @Incubating
@@ -52,7 +58,7 @@ class Project extends XmlPersistableConfigurationObject {
     }
 
     void configure(Collection<Path> modulePaths, String jdkName, IdeaLanguageLevel languageLevel,
-                   Collection<String> wildcards, Collection<ProjectLibrary> projectLibraries) {
+                   Collection<String> wildcards, Collection<ProjectLibrary> projectLibraries, String vcs) {
         if (jdkName) {
             jdk = new Jdk(jdkName, languageLevel)
         }
@@ -60,9 +66,11 @@ class Project extends XmlPersistableConfigurationObject {
         this.wildcards.addAll(wildcards)
         // overwrite rather than append libraries
         this.projectLibraries = projectLibraries
+        this.vcs = vcs
     }
 
-    @Override protected void load(Node xml) {
+    @Override
+    protected void load(Node xml) {
         findModules().module.each { module ->
             this.modulePaths.add(pathFactory.path(module. at fileurl, module. at filepath))
         }
@@ -78,11 +86,13 @@ class Project extends XmlPersistableConfigurationObject {
         loadProjectLibraries()
     }
 
-    @Override protected String getDefaultResourceName() {
+    @Override
+    protected String getDefaultResourceName() {
         return "defaultProject.xml"
     }
 
-    @Override protected void store(Node xml) {
+    @Override
+    protected void store(Node xml) {
         findModules().replaceNode {
             modules {
                 modulePaths.each { Path modulePath ->
@@ -102,19 +112,27 @@ class Project extends XmlPersistableConfigurationObject {
         findProjectRootManager(). at languageLevel = jdk.languageLevel
         findProjectRootManager().@'project-jdk-name' = jdk.projectJdkName
 
+        if (vcs) {
+            findVcsDirectoryMappings(). at vcs = vcs
+        }
+
         storeProjectLibraries()
     }
 
     private findProjectRootManager() {
-        return xml.component.find { it. at name == 'ProjectRootManager'}
+        xml.component.find { it. at name == 'ProjectRootManager' }
     }
 
     private findWildcardResourcePatterns() {
-        xml.component.find { it. at name == 'CompilerConfiguration'}.wildcardResourcePatterns
+        xml.component.find { it. at name == 'CompilerConfiguration' }.wildcardResourcePatterns
+    }
+
+    private findVcsDirectoryMappings() {
+        xml.component.find { it. at name == 'VcsDirectoryMappings' }.mapping
     }
 
     private findModules() {
-        def moduleManager = xml.component.find { it. at name == 'ProjectModuleManager'}
+        def moduleManager = xml.component.find { it. at name == 'ProjectModuleManager' }
         if (!moduleManager.modules) {
             moduleManager.appendNode('modules')
         }
@@ -124,7 +142,7 @@ class Project extends XmlPersistableConfigurationObject {
     private Node findLibraryTable() {
         def libraryTable = xml.component.find { it. at name == 'libraryTable' }
         if (!libraryTable) {
-            libraryTable = xml.appendNode('component', [name:  'libraryTable'])
+            libraryTable = xml.appendNode('component', [name: 'libraryTable'])
         }
         libraryTable
     }
@@ -153,15 +171,28 @@ class Project extends XmlPersistableConfigurationObject {
     }
 
     boolean equals(o) {
-        if (this.is(o)) { return true }
+        if (this.is(o)) {
+            return true
+        }
 
-        if (getClass() != o.class) { return false }
+        if (getClass() != o.class) {
+            return false
+        }
 
         Project project = (Project) o;
 
-        if (jdk != project.jdk) { return false }
-        if (modulePaths != project.modulePaths) { return false }
-        if (wildcards != project.wildcards) { return false }
+        if (jdk != project.jdk) {
+            return false
+        }
+        if (modulePaths != project.modulePaths) {
+            return false
+        }
+        if (wildcards != project.wildcards) {
+            return false
+        }
+        if (vcs != project.vcs) {
+            return false
+        }
 
         return true;
     }
@@ -172,6 +203,7 @@ class Project extends XmlPersistableConfigurationObject {
         result = (modulePaths != null ? modulePaths.hashCode() : 0);
         result = 31 * result + (wildcards != null ? wildcards.hashCode() : 0);
         result = 31 * result + (jdk != null ? jdk.hashCode() : 0);
+        result = 31 * result + (vcs != null ? vcs.hashCode() : 0);
         return result;
     }
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/SingleEntryModuleLibrary.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/SingleEntryModuleLibrary.groovy
index 7d107e9..3bcf4a7 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/SingleEntryModuleLibrary.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/SingleEntryModuleLibrary.groovy
@@ -34,13 +34,26 @@ class SingleEntryModuleLibrary extends ModuleLibrary {
      * Creates single entry module library
      *
      * @param library a path to jar or class folder in idea format
-     * @param javadoc a path to javadoc jar or javadoc folder
-     * @param source a path to source jar or source folder
+     * @param javadoc paths to javadoc jars or javadoc folders
+     * @param source paths to source jars or source folders
      * @param scope scope
      * @return
      */
-    SingleEntryModuleLibrary(FilePath library, FilePath javadoc, FilePath source, String scope) {
-        super([library] as Set, [javadoc] - null as Set, [source] - null as Set, [] as Set, scope)
+    SingleEntryModuleLibrary(FilePath library, Set<FilePath> javadoc, Set<FilePath> source, String scope) {
+        super([library] as Set, javadoc, source, [] as Set, scope)
+    }
+
+    /**
+     * Creates single entry module library
+     *
+     * @param library a path to jar or class folder in idea format
+     * @param javadoc path to javadoc jars or javadoc folders
+     * @param source paths to source jars or source folders
+     * @param scope scope
+     * @return
+     */
+    SingleEntryModuleLibrary(FilePath library, @Nullable FilePath javadoc, @Nullable FilePath source, String scope) {
+        super([library] as Set, javadoc ? [javadoc] : [], source ? [source] : [], [] as Set, scope)
     }
 
     /**
@@ -51,7 +64,7 @@ class SingleEntryModuleLibrary extends ModuleLibrary {
      * @return
      */
     SingleEntryModuleLibrary(FilePath library, String scope) {
-        this(library, null, null, scope)
+        this(library, [] as Set, [] as Set, scope)
     }
 
     /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy
index db14efb..56c0c90 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/Workspace.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.idea.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject
 
 /**
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.java
index e972dea..d5bbd41 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProvider.java
@@ -21,6 +21,7 @@ import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.*;
 import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.plugins.ide.idea.model.Dependency;
 import org.gradle.plugins.ide.idea.model.FilePath;
@@ -31,6 +32,7 @@ import org.gradle.plugins.ide.internal.resolver.model.IdeDependencyKey;
 import org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency;
 import org.gradle.plugins.ide.internal.resolver.model.IdeLocalFileDependency;
 import org.gradle.plugins.ide.internal.resolver.model.IdeProjectDependency;
+import org.gradle.util.CollectionUtils;
 
 import java.io.File;
 import java.util.*;
@@ -38,7 +40,7 @@ import java.util.*;
 public class IdeaDependenciesProvider {
 
     private final IdeDependenciesExtractor dependenciesExtractor;
-    private Function<File, FilePath> getPath;
+    private Transformer<FilePath, File> getPath;
 
     /**
      * List of mappings used to assign IDEA classpath scope to project dependencies.
@@ -69,9 +71,9 @@ public class IdeaDependenciesProvider {
     }
 
     Set<Dependency> provide(final IdeaModule ideaModule) {
-        getPath = new Function<File, FilePath>() {
+        getPath = new Transformer<FilePath, File>() {
             @Nullable
-            public FilePath apply(File file) {
+            public FilePath transform(File file) {
                 return file != null ? ideaModule.getPathFactory().path(file) : null;
             }
         };
@@ -82,7 +84,7 @@ public class IdeaDependenciesProvider {
                 String scope = singleEntryLibrary.getKey();
                 for (File file : singleEntryLibrary.getValue()) {
                     if (file != null && file.isDirectory()) {
-                        result.add(new SingleEntryModuleLibrary(getPath.apply(file), scope));
+                        result.add(new SingleEntryModuleLibrary(getPath.transform(file), scope));
                     }
                 }
             }
@@ -116,8 +118,10 @@ public class IdeaDependenciesProvider {
                             ideRepoFileDependency,
                             new IdeDependencyKey.DependencyBuilder<IdeExtendedRepoFileDependency, Dependency>() {
                                 public Dependency buildDependency(IdeExtendedRepoFileDependency dependency, String scope) {
+                                    Set<FilePath> javadoc = CollectionUtils.collect(dependency.getJavadocFiles(), new LinkedHashSet<FilePath>(), getPath);
+                                    Set<FilePath> source = CollectionUtils.collect(dependency.getSourceFiles(), new LinkedHashSet<FilePath>(), getPath);
                                     SingleEntryModuleLibrary library = new SingleEntryModuleLibrary(
-                                            getPath.apply(dependency.getFile()), getPath.apply(dependency.getJavadocFile()), getPath.apply(dependency.getSourceFile()), scope);
+                                            getPath.transform(dependency.getFile()), javadoc, source, scope);
                                     library.setModuleVersion(dependency.getId());
                                     return library;
                                 }});
@@ -132,7 +136,7 @@ public class IdeaDependenciesProvider {
                         fileDependency,
                         new IdeDependencyKey.DependencyBuilder<IdeLocalFileDependency, Dependency>() {
                             public Dependency buildDependency(IdeLocalFileDependency dependency, String scope) {
-                                return new SingleEntryModuleLibrary(getPath.apply(dependency.getFile()), scope);
+                                return new SingleEntryModuleLibrary(getPath.transform(dependency.getFile()), scope);
                             }});
                 dependencyToConfigurations.put(key, configuration.getName());
             }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.groovy
deleted file mode 100644
index dbff246..0000000
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.groovy
+++ /dev/null
@@ -1,170 +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.ide.internal
-
-import com.google.common.collect.LinkedHashMultimap
-import com.google.common.collect.Multimap
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier
-import org.gradle.api.artifacts.dsl.DependencyHandler
-import org.gradle.api.artifacts.resolution.JvmLibrary
-import org.gradle.api.artifacts.resolution.JvmLibraryJavadocArtifact
-import org.gradle.api.artifacts.resolution.JvmLibrarySourcesArtifact
-import org.gradle.api.internal.artifacts.component.DefaultModuleComponentIdentifier
-import org.gradle.plugins.ide.internal.resolver.DefaultIdeDependencyResolver
-import org.gradle.plugins.ide.internal.resolver.IdeDependencyResolver
-import org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency
-import org.gradle.plugins.ide.internal.resolver.model.IdeLocalFileDependency
-import org.gradle.plugins.ide.internal.resolver.model.IdeProjectDependency
-import org.gradle.plugins.ide.internal.resolver.model.UnresolvedIdeRepoFileDependency
-
-class IdeDependenciesExtractor {
-    private final IdeDependencyResolver ideDependencyResolver = new DefaultIdeDependencyResolver()
-
-    Collection<IdeProjectDependency> extractProjectDependencies(Project project, Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
-        LinkedHashMap<Project, IdeProjectDependency> deps = [:]
-
-        for (plusConfiguration in plusConfigurations) {
-            for(IdeProjectDependency dep in ideDependencyResolver.getIdeProjectDependencies(plusConfiguration, project)) {
-                deps[dep.project] = dep
-            }
-        }
-
-        for (minusConfiguration in minusConfigurations) {
-            for(IdeProjectDependency dep in ideDependencyResolver.getIdeProjectDependencies(minusConfiguration, project)) {
-                deps.remove(dep.project)
-            }
-        }
-
-        deps.values()
-    }
-
-    Collection<IdeExtendedRepoFileDependency> extractRepoFileDependencies(
-            DependencyHandler dependencyHandler,
-            Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations,
-            boolean downloadSources, boolean downloadJavadoc) {
-
-        // can have multiple IDE dependencies with same component identifier (see GRADLE-1622)
-        Multimap<ComponentIdentifier, IdeExtendedRepoFileDependency> resolvedDependencies = LinkedHashMultimap.create()
-        for (dep in resolvedExternalDependencies(plusConfigurations, minusConfigurations)) {
-            resolvedDependencies.put(toComponentIdentifier(dep.id), dep)
-        }
-
-        downloadSourcesAndJavadoc(dependencyHandler, resolvedDependencies, downloadSources, downloadJavadoc)
-
-        def unresolvedDependencies = unresolvedExternalDependencies(plusConfigurations, minusConfigurations)
-        return resolvedDependencies.values() + unresolvedDependencies
-    }
-
-    private ModuleComponentIdentifier toComponentIdentifier(ModuleVersionIdentifier id) {
-        new DefaultModuleComponentIdentifier(id.group, id.name, id.version)
-    }
-
-    private void downloadSourcesAndJavadoc(DependencyHandler dependencyHandler,
-                                           Multimap<ComponentIdentifier, IdeExtendedRepoFileDependency> dependencies,
-                                           boolean downloadSources, boolean downloadJavadoc) {
-
-        if (!downloadSources && !downloadJavadoc) {
-            return
-        }
-
-        def query = dependencyHandler.createArtifactResolutionQuery()
-        query.forComponents(dependencies.keySet());
-        if (downloadSources) {
-            query.withArtifacts(JvmLibrary, JvmLibrarySourcesArtifact)
-        }
-        if (downloadJavadoc) {
-            query.withArtifacts(JvmLibrary, JvmLibraryJavadocArtifact)
-        }
-
-        def jvmLibraries = query.execute().getComponents(JvmLibrary)
-        for (jvmLibrary in jvmLibraries) {
-            for (dependency in dependencies.get(jvmLibrary.id)) {
-                for (sourcesArtifact in jvmLibrary.sourcesArtifacts) {
-                    if (sourcesArtifact.failure == null) {
-                        dependency.sourceFile = sourcesArtifact.file
-                    }
-                }
-                for (javadocArtifact in jvmLibrary.javadocArtifacts) {
-                    if (javadocArtifact.failure == null) {
-                        dependency.javadocFile = javadocArtifact.file
-                    }
-                }
-            }
-        }
-    }
-
-    private Collection<UnresolvedIdeRepoFileDependency> unresolvedExternalDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
-        def unresolved = new LinkedHashMap<File, UnresolvedIdeRepoFileDependency>()
-
-        for (c in plusConfigurations) {
-            def deps = ideDependencyResolver.getUnresolvedIdeRepoFileDependencies(c)
-
-            deps.each {
-                unresolved[it.file] = it
-            }
-        }
-
-        for (c in minusConfigurations) {
-            def deps = ideDependencyResolver.getUnresolvedIdeRepoFileDependencies(c)
-
-            deps.each {
-                unresolved.remove(it.file)
-            }
-        }
-
-        unresolved.values()
-    }
-
-    Collection<IdeLocalFileDependency> extractLocalFileDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
-        LinkedHashMap<File, IdeLocalFileDependency> fileToConf = [:]
-
-        for (plusConfiguration in plusConfigurations) {
-            for(IdeLocalFileDependency localFileDependency in ideDependencyResolver.getIdeLocalFileDependencies(plusConfiguration)) {
-                fileToConf[localFileDependency.file] = localFileDependency
-            }
-        }
-        for (minusConfiguration in minusConfigurations) {
-            for(IdeLocalFileDependency localFileDependency in ideDependencyResolver.getIdeLocalFileDependencies(minusConfiguration)) {
-                fileToConf.remove(localFileDependency.file)
-            }
-        }
-
-        fileToConf.values()
-    }
-
-    Collection<IdeExtendedRepoFileDependency> resolvedExternalDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
-        LinkedHashMap<File, IdeExtendedRepoFileDependency> out = [:]
-
-        for (plusConfiguration in plusConfigurations) {
-            for (artifact in ideDependencyResolver.getIdeRepoFileDependencies(plusConfiguration)) {
-                out[artifact.file] = artifact
-            }
-        }
-
-        for (minusConfiguration in minusConfigurations) {
-            for (artifact in ideDependencyResolver.getIdeRepoFileDependencies(minusConfiguration)) {
-                out.remove(artifact.file)
-            }
-        }
-
-        out.values()
-    }
-}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.java
new file mode 100644
index 0000000..43bd5d5
--- /dev/null
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/IdeDependenciesExtractor.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.ide.internal;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
+import org.gradle.api.artifacts.result.ArtifactResult;
+import org.gradle.api.artifacts.result.ComponentArtifactsResult;
+import org.gradle.api.artifacts.result.ResolvedArtifactResult;
+import org.gradle.api.component.Artifact;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.language.base.artifact.SourcesArtifact;
+import org.gradle.language.java.artifact.JavadocArtifact;
+import org.gradle.plugins.ide.internal.resolver.DefaultIdeDependencyResolver;
+import org.gradle.plugins.ide.internal.resolver.IdeDependencyResolver;
+import org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency;
+import org.gradle.plugins.ide.internal.resolver.model.IdeLocalFileDependency;
+import org.gradle.plugins.ide.internal.resolver.model.IdeProjectDependency;
+import org.gradle.plugins.ide.internal.resolver.model.UnresolvedIdeRepoFileDependency;
+import org.gradle.jvm.JvmLibrary;
+
+import java.io.File;
+import java.util.*;
+
+public class IdeDependenciesExtractor {
+
+    private final IdeDependencyResolver ideDependencyResolver = new DefaultIdeDependencyResolver();
+
+    public Collection<IdeProjectDependency> extractProjectDependencies(Project project, Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        LinkedHashMap<Project, IdeProjectDependency> deps = new LinkedHashMap<Project, IdeProjectDependency>();
+
+        for (Configuration plusConfiguration : plusConfigurations) {
+            for (IdeProjectDependency dep : ideDependencyResolver.getIdeProjectDependencies(plusConfiguration, project)) {
+                deps.put(dep.getProject(), dep);
+            }
+        }
+
+        for (Configuration minusConfiguration : minusConfigurations) {
+            for (IdeProjectDependency dep : ideDependencyResolver.getIdeProjectDependencies(minusConfiguration, project)) {
+                deps.remove(dep.getProject());
+            }
+        }
+
+        return deps.values();
+    }
+
+    public Collection<IdeExtendedRepoFileDependency> extractRepoFileDependencies(DependencyHandler dependencyHandler, Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations, boolean downloadSources, boolean downloadJavadoc) {
+        // can have multiple IDE dependencies with same component identifier (see GRADLE-1622)
+        Multimap<ComponentIdentifier, IdeExtendedRepoFileDependency> resolvedDependenciesComponentMap = LinkedHashMultimap.create();
+        for (IdeExtendedRepoFileDependency dep : resolvedExternalDependencies(plusConfigurations, minusConfigurations)) {
+            resolvedDependenciesComponentMap.put(toComponentIdentifier(dep.getId()), dep);
+        }
+
+        List<Class<? extends Artifact>> artifactTypes = new ArrayList<Class<? extends Artifact>>(2);
+        if (downloadSources) {
+            artifactTypes.add(SourcesArtifact.class);
+        }
+
+        if (downloadJavadoc) {
+            artifactTypes.add(JavadocArtifact.class);
+        }
+
+        downloadAuxiliaryArtifacts(dependencyHandler, resolvedDependenciesComponentMap, artifactTypes);
+
+        Collection<UnresolvedIdeRepoFileDependency> unresolvedDependencies = unresolvedExternalDependencies(plusConfigurations, minusConfigurations);
+        Collection<IdeExtendedRepoFileDependency> resolvedDependencies = resolvedDependenciesComponentMap.values();
+
+        Collection<IdeExtendedRepoFileDependency> resolvedAndUnresolved = new ArrayList<IdeExtendedRepoFileDependency>(unresolvedDependencies.size() + resolvedDependencies.size());
+        resolvedAndUnresolved.addAll(resolvedDependencies);
+        resolvedAndUnresolved.addAll(unresolvedDependencies);
+        return resolvedAndUnresolved;
+    }
+
+    private ModuleComponentIdentifier toComponentIdentifier(ModuleVersionIdentifier id) {
+        return new DefaultModuleComponentIdentifier(id.getGroup(), id.getName(), id.getVersion());
+    }
+
+    private static void downloadAuxiliaryArtifacts(DependencyHandler dependencyHandler, Multimap<ComponentIdentifier, IdeExtendedRepoFileDependency> dependencies, List<Class<? extends Artifact>> artifactTypes) {
+        if (artifactTypes.isEmpty()) {
+            return;
+        }
+
+        ArtifactResolutionQuery query = dependencyHandler.createArtifactResolutionQuery();
+        query.forComponents(dependencies.keySet());
+
+        @SuppressWarnings("unchecked") Class<? extends Artifact>[] artifactTypesArray = (Class<? extends Artifact>[]) new Class<?>[artifactTypes.size()];
+        query.withArtifacts(JvmLibrary.class, artifactTypes.toArray(artifactTypesArray));
+        Set<ComponentArtifactsResult> componentResults = query.execute().getResolvedComponents();
+        for (ComponentArtifactsResult componentResult : componentResults) {
+            for (IdeExtendedRepoFileDependency dependency : dependencies.get(componentResult.getId())) {
+                for (ArtifactResult sourcesResult : componentResult.getArtifacts(SourcesArtifact.class)) {
+                    if (sourcesResult instanceof ResolvedArtifactResult) {
+                        dependency.addSourceFile(((ResolvedArtifactResult) sourcesResult).getFile());
+                    }
+                }
+
+                for (ArtifactResult javadocResult : componentResult.getArtifacts(JavadocArtifact.class)) {
+                    if (javadocResult instanceof ResolvedArtifactResult) {
+                        dependency.addJavadocFile(((ResolvedArtifactResult) javadocResult).getFile());
+                    }
+                }
+            }
+        }
+    }
+
+    private Collection<UnresolvedIdeRepoFileDependency> unresolvedExternalDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        final LinkedHashMap<File, UnresolvedIdeRepoFileDependency> unresolved = new LinkedHashMap<File, UnresolvedIdeRepoFileDependency>();
+
+        for (Configuration c : plusConfigurations) {
+            List<UnresolvedIdeRepoFileDependency> deps = ideDependencyResolver.getUnresolvedIdeRepoFileDependencies(c);
+            for (UnresolvedIdeRepoFileDependency dep : deps) {
+                unresolved.put(dep.getFile(), dep);
+            }
+        }
+
+        for (Configuration c : minusConfigurations) {
+            List<UnresolvedIdeRepoFileDependency> deps = ideDependencyResolver.getUnresolvedIdeRepoFileDependencies(c);
+            for (UnresolvedIdeRepoFileDependency dep : deps) {
+                unresolved.remove(dep.getFile());
+            }
+        }
+
+        return unresolved.values();
+    }
+
+    public Collection<IdeLocalFileDependency> extractLocalFileDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        LinkedHashMap<File, IdeLocalFileDependency> fileToConf = new LinkedHashMap<File, IdeLocalFileDependency>();
+
+        if (plusConfigurations != null) {
+            for (Configuration plusConfiguration : plusConfigurations) {
+                for (IdeLocalFileDependency localFileDependency : ideDependencyResolver.getIdeLocalFileDependencies(plusConfiguration)) {
+                    fileToConf.put(localFileDependency.getFile(), localFileDependency);
+                }
+            }
+        }
+
+        if (minusConfigurations != null) {
+            for (Configuration minusConfiguration : minusConfigurations) {
+                for (IdeLocalFileDependency localFileDependency : ideDependencyResolver.getIdeLocalFileDependencies(minusConfiguration)) {
+                    fileToConf.remove(localFileDependency.getFile());
+                }
+            }
+        }
+
+        return fileToConf.values();
+    }
+
+    public Collection<IdeExtendedRepoFileDependency> resolvedExternalDependencies(Collection<Configuration> plusConfigurations, Collection<Configuration> minusConfigurations) {
+        LinkedHashMap<File, IdeExtendedRepoFileDependency> out = new LinkedHashMap<File, IdeExtendedRepoFileDependency>();
+
+        if (plusConfigurations != null) {
+            for (Configuration plusConfiguration : plusConfigurations) {
+                for (IdeExtendedRepoFileDependency artifact : ideDependencyResolver.getIdeRepoFileDependencies(plusConfiguration)) {
+                    out.put(artifact.getFile(), artifact);
+                }
+            }
+        }
+
+        if (minusConfigurations != null) {
+            for (Configuration minusConfiguration : minusConfigurations) {
+                for (IdeExtendedRepoFileDependency artifact : ideDependencyResolver.getIdeRepoFileDependencies(minusConfiguration)) {
+                    out.remove(artifact.getFile());
+                }
+            }
+        }
+
+        return out.values();
+    }
+}
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy
index f609d5e..1296a71 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObject.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.internal.generator;
 
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 
 /**
  * A {@link org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject} which is stored in an XML file.
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilder.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilder.java
index 0b28697..9538588 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilder.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilder.java
@@ -17,76 +17,114 @@
 package org.gradle.plugins.ide.internal.tooling;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Ordering;
 import com.google.common.collect.Sets;
 import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.tooling.internal.gradle.DefaultBuildInvocations;
+import org.gradle.api.internal.project.ProjectTaskLister;
+import org.gradle.api.internal.tasks.PublicTaskSpecification;
+import org.gradle.tooling.internal.consumer.converters.TaskNameComparator;
+import org.gradle.tooling.internal.impl.DefaultBuildInvocations;
 import org.gradle.tooling.internal.impl.LaunchableGradleTask;
 import org.gradle.tooling.internal.impl.LaunchableGradleTaskSelector;
 import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public class BuildInvocationsBuilder extends ProjectSensitiveToolingModelBuilder {
-    private final GradleProjectBuilder gradleProjectBuilder;
 
-    public BuildInvocationsBuilder(GradleProjectBuilder gradleProjectBuilder) {
-        this.gradleProjectBuilder = gradleProjectBuilder;
+    private final ProjectTaskLister taskLister;
+    private final TaskNameComparator taskNameComparator;
+
+    public BuildInvocationsBuilder(ProjectTaskLister taskLister) {
+        this.taskLister = taskLister;
+        this.taskNameComparator = new TaskNameComparator();
     }
 
     public boolean canBuild(String modelName) {
         return modelName.equals("org.gradle.tooling.model.gradle.BuildInvocations");
     }
 
-    public DefaultBuildInvocations<LaunchableGradleTask> buildAll(String modelName, Project project) {
+    public DefaultBuildInvocations buildAll(String modelName, Project project, boolean implicitProject) {
+        return buildAll(modelName, implicitProject ? project.getRootProject() : project);
+    }
+
+    @SuppressWarnings("StringEquality")
+    public DefaultBuildInvocations buildAll(String modelName, Project project) {
         if (!canBuild(modelName)) {
             throw new GradleException("Unknown model name " + modelName);
         }
+
+        // construct task selectors
         List<LaunchableGradleTaskSelector> selectors = Lists.newArrayList();
-        Set<String> aggregatedTasks = Sets.newHashSet();
-        findTasks(project, aggregatedTasks);
-        for (String selectorName : aggregatedTasks) {
-            selectors.add(new LaunchableGradleTaskSelector().
+        Map<String, LaunchableGradleTaskSelector> selectorsByName = Maps.newTreeMap(Ordering.natural());
+        Set<String> visibleTasks = Sets.newLinkedHashSet();
+        findTasks(project, selectorsByName, visibleTasks);
+        for (String selectorName : selectorsByName.keySet()) {
+            LaunchableGradleTaskSelector selector = selectorsByName.get(selectorName);
+            selectors.add(selector.
                     setName(selectorName).
                     setTaskName(selectorName).
                     setProjectPath(project.getPath()).
-                    setDescription(project.getParent() != null
-                            ? String.format("%s:%s task selector", project.getPath(), selectorName)
-                            : String.format("%s task selector", selectorName)).
-                    setDisplayName(String.format("%s in %s and subprojects.", selectorName, project.toString())));
+                    setDisplayName(String.format("%s in %s and subprojects.", selectorName, project.toString())).
+                    setPublic(visibleTasks.contains(selectorName)));
         }
-        return new DefaultBuildInvocations<LaunchableGradleTask>()
-                .setSelectors(selectors)
-                .setTasks(convertTasks(gradleProjectBuilder.buildAll(project).findByPath(project.getPath()).getTasks()));
-    }
 
-    public DefaultBuildInvocations buildAll(String modelName, Project project, boolean implicitProject) {
-        return buildAll(modelName, implicitProject ? project.getRootProject() : project);
+        // construct project tasks
+        List<LaunchableGradleTask> projectTasks = tasks(project);
+
+        // construct build invocations from task selectors and project tasks
+        return new DefaultBuildInvocations().setSelectors(selectors).setTasks(projectTasks);
     }
 
     // build tasks without project reference
-    private List<LaunchableGradleTask> convertTasks(Iterable<LaunchableGradleTask> tasks) {
-        List<LaunchableGradleTask> convertedTasks = Lists.newArrayList();
-        for (LaunchableGradleTask task : tasks) {
-            convertedTasks.add(new LaunchableGradleTask()
+    private List<LaunchableGradleTask> tasks(Project project) {
+        List<LaunchableGradleTask> tasks = Lists.newArrayList();
+        for (Task task : taskLister.listProjectTasks(project)) {
+            tasks.add(new LaunchableGradleTask()
                     .setPath(task.getPath())
                     .setName(task.getName())
                     .setDisplayName(task.toString())
-                    .setDescription(task.getDescription()));
+                    .setDescription(task.getDescription())
+                    .setPublic(PublicTaskSpecification.INSTANCE.isSatisfiedBy(task)));
         }
-        return convertedTasks;
+        return tasks;
     }
 
-    private void findTasks(Project project, Collection<String> tasks) {
-        for (Project child : project.getSubprojects()) {
-            findTasks(child, tasks);
+    private void findTasks(Project project, Map<String, LaunchableGradleTaskSelector> taskSelectors, Collection<String> visibleTasks) {
+        for (Project child : project.getChildProjects().values()) {
+            findTasks(child, taskSelectors, visibleTasks);
         }
-        for (Task task : project.getTasks()) {
-            tasks.add(task.getName());
+
+        for (Task task : taskLister.listProjectTasks(project)) {
+            // in the map, store a minimally populated LaunchableGradleTaskSelector that contains just the description and the path
+            // replace the LaunchableGradleTaskSelector stored in the map iff we come across a task with the same name whose path has a smaller ordering
+            // this way, for each task selector, its description will be the one from the selected task with the 'smallest' path
+            if (!taskSelectors.containsKey(task.getName())) {
+                LaunchableGradleTaskSelector taskSelector = new LaunchableGradleTaskSelector().
+                        setDescription(task.getDescription()).setProjectPath(task.getPath());
+                taskSelectors.put(task.getName(), taskSelector);
+            } else {
+                LaunchableGradleTaskSelector taskSelector = taskSelectors.get(task.getName());
+                if (hasPathWithLowerOrdering(task, taskSelector)) {
+                    taskSelector.setDescription(task.getDescription()).setProjectPath(task.getPath());
+                }
+            }
+
+            // visible tasks are specified as those that have a non-empty group
+            if (PublicTaskSpecification.INSTANCE.isSatisfiedBy(task)) {
+                visibleTasks.add(task.getName());
+            }
         }
     }
 
+    private boolean hasPathWithLowerOrdering(Task task, LaunchableGradleTaskSelector referenceTaskSelector) {
+        return taskNameComparator.compare(task.getPath(), referenceTaskSelector.getProjectPath()) < 0;
+    }
+
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/EclipseModelBuilder.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/EclipseModelBuilder.java
index 190b8ae..aec1918 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/EclipseModelBuilder.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/EclipseModelBuilder.java
@@ -23,10 +23,6 @@ import org.gradle.plugins.ide.eclipse.EclipsePlugin;
 import org.gradle.plugins.ide.eclipse.model.*;
 import org.gradle.plugins.ide.internal.tooling.eclipse.*;
 import org.gradle.tooling.internal.gradle.DefaultGradleProject;
-import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseTaskVersion1;
 import org.gradle.tooling.provider.model.ToolingModelBuilder;
 import org.gradle.util.GUtil;
 
@@ -69,7 +65,7 @@ public class EclipseModelBuilder implements ToolingModelBuilder {
     private void applyEclipsePlugin(Project root) {
         Set<Project> allProjects = root.getAllprojects();
         for (Project p : allProjects) {
-            p.getPlugins().apply(EclipsePlugin.class);
+            p.getPluginManager().apply(EclipsePlugin.class);
         }
         root.getPlugins().getPlugin(EclipsePlugin.class).makeSureProjectNamesAreUnique();
     }
@@ -82,15 +78,15 @@ public class EclipseModelBuilder implements ToolingModelBuilder {
     }
 
     private void populate(Project project) {
-        EclipseModel eclipseModel = project.getPlugins().getPlugin(EclipsePlugin.class).getModel();
+        EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
         EclipseClasspath classpath = eclipseModel.getClasspath();
 
         classpath.setProjectDependenciesOnly(projectDependenciesOnly);
         List<ClasspathEntry> entries = classpath.resolveDependencies();
 
-        final List<ExternalDependencyVersion1> externalDependencies = new LinkedList<ExternalDependencyVersion1>();
-        final List<EclipseProjectDependencyVersion2> projectDependencies = new LinkedList<EclipseProjectDependencyVersion2>();
-        final List<EclipseSourceDirectoryVersion1> sourceDirectories = new LinkedList<EclipseSourceDirectoryVersion1>();
+        final List<DefaultEclipseExternalDependency> externalDependencies = new LinkedList<DefaultEclipseExternalDependency>();
+        final List<DefaultEclipseProjectDependency> projectDependencies = new LinkedList<DefaultEclipseProjectDependency>();
+        final List<DefaultEclipseSourceDirectory> sourceDirectories = new LinkedList<DefaultEclipseSourceDirectory>();
 
         for (ClasspathEntry entry : entries) {
             //we don't handle Variables at the moment because users didn't request it yet
@@ -117,12 +113,12 @@ public class EclipseModelBuilder implements ToolingModelBuilder {
         eclipseProject.setSourceDirectories(sourceDirectories);
 
         List<DefaultEclipseLinkedResource> linkedResources = new LinkedList<DefaultEclipseLinkedResource>();
-        for(Link r: eclipseModel.getProject().getLinkedResources()) {
+        for (Link r : eclipseModel.getProject().getLinkedResources()) {
             linkedResources.add(new DefaultEclipseLinkedResource(r.getName(), r.getType(), r.getLocation(), r.getLocationUri()));
         }
         eclipseProject.setLinkedResources(linkedResources);
 
-        List<EclipseTaskVersion1> tasks = new ArrayList<EclipseTaskVersion1>();
+        List<DefaultEclipseTask> tasks = new ArrayList<DefaultEclipseTask>();
         for (Task t : tasksFactory.getTasks(project)) {
             tasks.add(new DefaultEclipseTask(eclipseProject, t.getPath(), t.getName(), t.getDescription()));
         }
@@ -139,7 +135,7 @@ public class EclipseModelBuilder implements ToolingModelBuilder {
             children.add(buildHierarchy(child));
         }
 
-        EclipseModel eclipseModel = project.getPlugins().getPlugin(EclipsePlugin.class).getModel();
+        EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
         org.gradle.plugins.ide.eclipse.model.EclipseProject internalProject = eclipseModel.getProject();
         String name = internalProject.getName();
         String description = GUtil.elvis(internalProject.getComment(), null);
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
index bfdbccb..16ba9e9 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
@@ -18,20 +18,22 @@ package org.gradle.plugins.ide.internal.tooling;
 
 import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.internal.tasks.PublicTaskSpecification;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.tooling.internal.gradle.DefaultGradleProject;
 import org.gradle.tooling.internal.impl.LaunchableGradleProjectTask;
 import org.gradle.tooling.internal.impl.LaunchableGradleTask;
 import org.gradle.tooling.provider.model.ToolingModelBuilder;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.SortedSet;
 
 /**
  * Builds the GradleProject that contains the project hierarchy and task information
  */
 public class GradleProjectBuilder implements ToolingModelBuilder {
+
     public boolean canBuild(String modelName) {
         return modelName.equals("org.gradle.tooling.model.GradleProject");
     }
@@ -54,10 +56,12 @@ public class GradleProjectBuilder implements ToolingModelBuilder {
                 .setPath(project.getPath())
                 .setName(project.getName())
                 .setDescription(project.getDescription())
+                .setBuildDirectory(project.getBuildDir())
+                .setProjectDirectory(project.getProjectDir())
                 .setChildren(children);
 
         gradleProject.getBuildScript().setSourceFile(project.getBuildFile());
-        gradleProject.setTasks(tasks(gradleProject, project.getTasks()));
+        gradleProject.setTasks(tasks(gradleProject, (TaskContainerInternal) project.getTasks()));
 
         for (DefaultGradleProject child : children) {
             child.setParent(gradleProject);
@@ -66,17 +70,22 @@ public class GradleProjectBuilder implements ToolingModelBuilder {
         return gradleProject;
     }
 
-    private static List<LaunchableGradleTask> tasks(DefaultGradleProject owner, TaskContainer tasks) {
-        List<LaunchableGradleTask> out = new LinkedList<LaunchableGradleTask>();
-
-        for (Task t : tasks) {
-            out.add(new LaunchableGradleProjectTask()
-                    .setProject(owner)
-                    .setPath(t.getPath())
-                    .setName(t.getName())
-                    .setDisplayName(t.toString())
-                    .setDescription(t.getDescription())
-                    );
+    private static List<LaunchableGradleTask> tasks(DefaultGradleProject owner, TaskContainerInternal tasks) {
+        tasks.discoverTasks();
+        SortedSet<String> taskNames = tasks.getNames();
+        List<LaunchableGradleTask> out = new ArrayList<LaunchableGradleTask>(taskNames.size());
+        for (String taskName : taskNames) {
+            Task t = tasks.findByName(taskName);
+            if (t != null) {
+                out.add(new LaunchableGradleProjectTask()
+                                .setProject(owner)
+                                .setPath(t.getPath())
+                                .setName(t.getName())
+                                .setDisplayName(t.toString())
+                                .setDescription(t.getDescription())
+                                .setPublic(PublicTaskSpecification.INSTANCE.isSatisfiedBy(t))
+                );
+            }
         }
 
         return out;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/IdeaModelBuilder.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/IdeaModelBuilder.java
index a11c6bb..05f2cc5 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/IdeaModelBuilder.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/IdeaModelBuilder.java
@@ -22,7 +22,6 @@ import org.gradle.plugins.ide.idea.model.*;
 import org.gradle.plugins.ide.internal.tooling.idea.*;
 import org.gradle.tooling.internal.gradle.DefaultGradleModuleVersion;
 import org.gradle.tooling.internal.gradle.DefaultGradleProject;
-import org.gradle.tooling.model.idea.IdeaSourceDirectory;
 import org.gradle.tooling.provider.model.ToolingModelBuilder;
 
 import java.io.File;
@@ -51,7 +50,7 @@ public class IdeaModelBuilder implements ToolingModelBuilder {
     private void applyIdeaPlugin(Project root) {
         Set<Project> allProjects = root.getAllprojects();
         for (Project p : allProjects) {
-            p.getPlugins().apply(IdeaPlugin.class);
+            p.getPluginManager().apply(IdeaPlugin.class);
         }
         root.getPlugins().getPlugin(IdeaPlugin.class).makeSureModuleNamesAreUnique();
     }
@@ -110,8 +109,8 @@ public class IdeaModelBuilder implements ToolingModelBuilder {
     private void appendModule(Map<String, DefaultIdeaModule> modules, IdeaModule ideaModule, DefaultIdeaProject ideaProject, DefaultGradleProject rootGradleProject) {
         DefaultIdeaContentRoot contentRoot = new DefaultIdeaContentRoot()
             .setRootDirectory(ideaModule.getContentRoot())
-            .setSourceDirectories(srcDirs(ideaModule.getSourceDirs()))
-            .setTestDirectories(srcDirs(ideaModule.getTestSourceDirs()))
+            .setSourceDirectories(srcDirs(ideaModule.getSourceDirs(), ideaModule.getGeneratedSourceDirs()))
+            .setTestDirectories(srcDirs(ideaModule.getTestSourceDirs(), ideaModule.getGeneratedSourceDirs()))
             .setExcludeDirectories(ideaModule.getExcludeDirs());
 
         DefaultIdeaModule defaultIdeaModule = new DefaultIdeaModule()
@@ -128,10 +127,14 @@ public class IdeaModelBuilder implements ToolingModelBuilder {
         modules.put(ideaModule.getName(), defaultIdeaModule);
     }
 
-    private Set<IdeaSourceDirectory> srcDirs(Set<File> sourceDirs) {
-        Set<IdeaSourceDirectory> out = new LinkedHashSet<IdeaSourceDirectory>();
+    private Set<DefaultIdeaSourceDirectory> srcDirs(Set<File> sourceDirs, Set<File> generatedSourceDirs) {
+        Set<DefaultIdeaSourceDirectory> out = new LinkedHashSet<DefaultIdeaSourceDirectory>();
         for (File s : sourceDirs) {
-            out.add(new DefaultIdeaSourceDirectory().setDirectory(s));
+            DefaultIdeaSourceDirectory sourceDirectory = new DefaultIdeaSourceDirectory().setDirectory(s);
+            if (generatedSourceDirs.contains(s)) {
+                sourceDirectory.setGenerated(true);
+            }
+            out.add(sourceDirectory);
         }
         return out;
     }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/ToolingRegistrationAction.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/ToolingRegistrationAction.java
index bdf0030..8c8ec4e 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/ToolingRegistrationAction.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/ToolingRegistrationAction.java
@@ -18,6 +18,7 @@ package org.gradle.plugins.ide.internal.tooling;
 
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectTaskLister;
 import org.gradle.configuration.project.ProjectConfigureAction;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
 
@@ -25,6 +26,7 @@ public class ToolingRegistrationAction implements ProjectConfigureAction {
     public void execute(ProjectInternal project) {
         ToolingModelBuilderRegistry modelBuilderRegistry = project.getServices().get(ToolingModelBuilderRegistry.class);
         ProjectPublicationRegistry projectPublicationRegistry = project.getServices().get(ProjectPublicationRegistry.class);
+        ProjectTaskLister taskLister = project.getServices().get(ProjectTaskLister.class);
 
         GradleProjectBuilder gradleProjectBuilder  = new GradleProjectBuilder();
         IdeaModelBuilder ideaModelBuilder = new IdeaModelBuilder(gradleProjectBuilder);
@@ -33,7 +35,7 @@ public class ToolingRegistrationAction implements ProjectConfigureAction {
         modelBuilderRegistry.register(gradleProjectBuilder);
         modelBuilderRegistry.register(new GradleBuildBuilder());
         modelBuilderRegistry.register(new BasicIdeaModelBuilder(ideaModelBuilder));
-        modelBuilderRegistry.register(new BuildInvocationsBuilder(gradleProjectBuilder));
+        modelBuilderRegistry.register(new BuildInvocationsBuilder(taskLister));
         modelBuilderRegistry.register(new PublicationsBuilder(projectPublicationRegistry));
     }
 }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseLinkedResource.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseLinkedResource.java
index 0ff1f7b..6e650b2 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseLinkedResource.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseLinkedResource.java
@@ -16,11 +16,9 @@
 
 package org.gradle.plugins.ide.internal.tooling.eclipse;
 
-import org.gradle.tooling.internal.protocol.eclipse.EclipseLinkedResourceVersion1;
-
 import java.io.Serializable;
 
-public class DefaultEclipseLinkedResource implements Serializable, EclipseLinkedResourceVersion1 {
+public class DefaultEclipseLinkedResource implements Serializable {
 
     private String name;
     private String type;
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProject.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProject.java
index 9f13cd0..98aa117 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProject.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProject.java
@@ -18,29 +18,30 @@ package org.gradle.plugins.ide.internal.tooling.eclipse;
 import com.google.common.collect.Lists;
 import org.gradle.tooling.internal.gradle.DefaultGradleProject;
 import org.gradle.tooling.internal.gradle.GradleProjectIdentity;
-import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.*;
 
 import java.io.File;
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.List;
 
-public class DefaultEclipseProject implements EclipseProjectVersion3, Serializable, GradleProjectIdentity {
+/**
+ * An implementation for {@link org.gradle.tooling.model.eclipse.EclipseProject}.
+ */
+public class DefaultEclipseProject implements Serializable, GradleProjectIdentity {
     private final String name;
     private final String path;
-    private EclipseProjectVersion3 parent;
-    private List<ExternalDependencyVersion1> classpath;
-    private final List<EclipseProjectVersion3> children;
-    private List<EclipseSourceDirectoryVersion1> sourceDirectories;
-    private List<EclipseProjectDependencyVersion2> projectDependencies;
+    private DefaultEclipseProject parent;
+    private List<DefaultEclipseExternalDependency> classpath;
+    private final List<DefaultEclipseProject> children;
+    private List<DefaultEclipseSourceDirectory> sourceDirectories;
+    private List<DefaultEclipseProjectDependency> projectDependencies;
     private final String description;
     private final File projectDirectory;
-    private Iterable<? extends EclipseTaskVersion1> tasks;
-    private Iterable<? extends EclipseLinkedResourceVersion1> linkedResources;
+    private Iterable<? extends DefaultEclipseTask> tasks;
+    private Iterable<? extends DefaultEclipseLinkedResource> linkedResources;
     private DefaultGradleProject gradleProject;
 
-    public DefaultEclipseProject(String name, String path, String description, File projectDirectory, Iterable<? extends EclipseProjectVersion3> children) {
+    public DefaultEclipseProject(String name, String path, String description, File projectDirectory, Iterable<? extends DefaultEclipseProject> children) {
         this.name = name;
         this.path = path;
         this.description = description;
@@ -69,7 +70,7 @@ public class DefaultEclipseProject implements EclipseProjectVersion3, Serializab
         return description;
     }
 
-    public EclipseProjectVersion3 getParent() {
+    public DefaultEclipseProject getParent() {
         return parent;
     }
 
@@ -77,50 +78,51 @@ public class DefaultEclipseProject implements EclipseProjectVersion3, Serializab
         return projectDirectory;
     }
 
-    public void setParent(EclipseProjectVersion3 parent) {
+    public void setParent(DefaultEclipseProject parent) {
         this.parent = parent;
     }
 
-    public List<EclipseProjectVersion3> getChildren() {
+    public List<DefaultEclipseProject> getChildren() {
         return children;
     }
 
-    public Iterable<? extends EclipseSourceDirectoryVersion1> getSourceDirectories() {
+    public Iterable<? extends DefaultEclipseSourceDirectory> getSourceDirectories() {
         return sourceDirectories;
     }
 
-    public void setSourceDirectories(List<EclipseSourceDirectoryVersion1> sourceDirectories) {
+    public void setSourceDirectories(List<DefaultEclipseSourceDirectory> sourceDirectories) {
         this.sourceDirectories = sourceDirectories;
     }
 
-    public Iterable<? extends EclipseProjectDependencyVersion2> getProjectDependencies() {
+    public Iterable<? extends DefaultEclipseProjectDependency> getProjectDependencies() {
         return projectDependencies;
     }
 
-    public void setProjectDependencies(List<EclipseProjectDependencyVersion2> projectDependencies) {
+    public void setProjectDependencies(List<DefaultEclipseProjectDependency> projectDependencies) {
         this.projectDependencies = projectDependencies;
     }
 
-    public List<ExternalDependencyVersion1> getClasspath() {
+    public List<DefaultEclipseExternalDependency> getClasspath() {
         return classpath;
     }
-    public void setClasspath(List<ExternalDependencyVersion1> classpath) {
+
+    public void setClasspath(List<DefaultEclipseExternalDependency> classpath) {
         this.classpath = classpath;
     }
 
-    public Iterable<? extends EclipseTaskVersion1> getTasks() {
+    public Iterable<? extends DefaultEclipseTask> getTasks() {
         return tasks;
     }
 
-    public void setTasks(Iterable<? extends EclipseTaskVersion1> tasks) {
+    public void setTasks(Iterable<? extends DefaultEclipseTask> tasks) {
         this.tasks = tasks;
     }
 
-    public Iterable<? extends EclipseLinkedResourceVersion1> getLinkedResources() {
+    public Iterable<? extends DefaultEclipseLinkedResource> getLinkedResources() {
         return linkedResources;
     }
 
-    public void setLinkedResources(Iterable<? extends EclipseLinkedResourceVersion1> linkedResources) {
+    public void setLinkedResources(Iterable<? extends DefaultEclipseLinkedResource> linkedResources) {
         this.linkedResources = linkedResources;
     }
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProjectDependency.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProjectDependency.java
index f042081..73765b9 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProjectDependency.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseProjectDependency.java
@@ -15,21 +15,18 @@
  */
 package org.gradle.plugins.ide.internal.tooling.eclipse;
 
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectDependencyVersion2;
-import org.gradle.tooling.internal.protocol.eclipse.HierarchicalEclipseProjectVersion1;
-
 import java.io.Serializable;
 
-public class DefaultEclipseProjectDependency implements EclipseProjectDependencyVersion2, Serializable {
+public class DefaultEclipseProjectDependency implements Serializable {
     private final String path;
-    private final HierarchicalEclipseProjectVersion1 target;
+    private final DefaultEclipseProject target;
 
-    public DefaultEclipseProjectDependency(String path, HierarchicalEclipseProjectVersion1 target) {
+    public DefaultEclipseProjectDependency(String path, DefaultEclipseProject target) {
         this.target = target;
         this.path = path;
     }
 
-    public HierarchicalEclipseProjectVersion1 getTargetProject() {
+    public DefaultEclipseProject getTargetProject() {
         return target;
     }
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseSourceDirectory.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseSourceDirectory.java
index f6ba5b2..85e37c1 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseSourceDirectory.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseSourceDirectory.java
@@ -15,12 +15,10 @@
  */
 package org.gradle.plugins.ide.internal.tooling.eclipse;
 
-import org.gradle.tooling.internal.protocol.eclipse.EclipseSourceDirectoryVersion1;
-
 import java.io.File;
 import java.io.Serializable;
 
-public class DefaultEclipseSourceDirectory implements EclipseSourceDirectoryVersion1, Serializable {
+public class DefaultEclipseSourceDirectory implements Serializable {
     private final String path;
     private final File directory;
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseTask.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseTask.java
index cebd4bb..7c66d6f 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseTask.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/DefaultEclipseTask.java
@@ -15,18 +15,15 @@
  */
 package org.gradle.plugins.ide.internal.tooling.eclipse;
 
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseTaskVersion1;
-
 import java.io.Serializable;
 
-public class DefaultEclipseTask implements EclipseTaskVersion1, Serializable {
-    private final EclipseProjectVersion3 project;
+public class DefaultEclipseTask implements Serializable {
+    private final DefaultEclipseProject project;
     private final String path;
     private final String name;
     private final String description;
 
-    public DefaultEclipseTask(EclipseProjectVersion3 project, String path, String name, String description) {
+    public DefaultEclipseTask(DefaultEclipseProject project, String path, String name, String description) {
         this.project = project;
         this.path = path;
         this.name = name;
@@ -38,7 +35,7 @@ public class DefaultEclipseTask implements EclipseTaskVersion1, Serializable {
         return String.format("task '%s'", path);
     }
 
-    public EclipseProjectVersion3 getProject() {
+    public DefaultEclipseProject getProject() {
         return project;
     }
 
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRoot.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRoot.java
index b463182..8482207 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRoot.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRoot.java
@@ -16,21 +16,16 @@
 
 package org.gradle.plugins.ide.internal.tooling.idea;
 
-import org.gradle.tooling.model.DomainObjectSet;
-import org.gradle.tooling.model.idea.IdeaContentRoot;
-import org.gradle.tooling.model.idea.IdeaSourceDirectory;
-import org.gradle.tooling.model.internal.ImmutableDomainObjectSet;
-
 import java.io.File;
 import java.io.Serializable;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
-public class DefaultIdeaContentRoot implements IdeaContentRoot, Serializable {
+public class DefaultIdeaContentRoot implements Serializable {
 
     File rootDirectory;
-    Set<IdeaSourceDirectory> sourceDirectories = new LinkedHashSet<IdeaSourceDirectory>();
-    Set<IdeaSourceDirectory> testDirectories = new LinkedHashSet<IdeaSourceDirectory>();
+    Set<DefaultIdeaSourceDirectory> sourceDirectories = new LinkedHashSet<DefaultIdeaSourceDirectory>();
+    Set<DefaultIdeaSourceDirectory> testDirectories = new LinkedHashSet<DefaultIdeaSourceDirectory>();
     Set<File> excludeDirectories = new LinkedHashSet<File>();
 
     public File getRootDirectory() {
@@ -42,24 +37,42 @@ public class DefaultIdeaContentRoot implements IdeaContentRoot, Serializable {
         return this;
     }
 
-    public DomainObjectSet<IdeaSourceDirectory> getSourceDirectories() {
-        return new ImmutableDomainObjectSet<IdeaSourceDirectory>(sourceDirectories);
+    public Set<DefaultIdeaSourceDirectory> getSourceDirectories() {
+        return sourceDirectories;
     }
 
-    public DefaultIdeaContentRoot setSourceDirectories(Set<IdeaSourceDirectory> sourceDirectories) {
+    public DefaultIdeaContentRoot setSourceDirectories(Set<DefaultIdeaSourceDirectory> sourceDirectories) {
         this.sourceDirectories = sourceDirectories;
         return this;
     }
 
-    public DomainObjectSet<IdeaSourceDirectory> getTestDirectories() {
-        return new ImmutableDomainObjectSet<IdeaSourceDirectory>(testDirectories);
+    public Set<DefaultIdeaSourceDirectory> getGeneratedSourceDirectories() {
+        return generated(sourceDirectories);
+    }
+
+    private Set<DefaultIdeaSourceDirectory> generated(Set<DefaultIdeaSourceDirectory> directories) {
+        Set<DefaultIdeaSourceDirectory> generated = new LinkedHashSet<DefaultIdeaSourceDirectory>();
+        for (DefaultIdeaSourceDirectory sourceDirectory : directories) {
+            if (sourceDirectory.isGenerated()) {
+                generated.add(sourceDirectory);
+            }
+        }
+        return generated;
     }
 
-    public DefaultIdeaContentRoot setTestDirectories(Set<IdeaSourceDirectory> testDirectories) {
+    public Set<DefaultIdeaSourceDirectory> getTestDirectories() {
+        return testDirectories;
+    }
+
+    public DefaultIdeaContentRoot setTestDirectories(Set<DefaultIdeaSourceDirectory> testDirectories) {
         this.testDirectories = testDirectories;
         return this;
     }
 
+    public Set<DefaultIdeaSourceDirectory> getGeneratedTestDirectories() {
+        return generated(testDirectories);
+    }
+
     public Set<File> getExcludeDirectories() {
         return excludeDirectories;
     }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaModule.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaModule.java
index 9843782..f02728b 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaModule.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaModule.java
@@ -19,7 +19,6 @@ package org.gradle.plugins.ide.internal.tooling.idea;
 import org.gradle.tooling.internal.gradle.DefaultGradleProject;
 import org.gradle.tooling.internal.gradle.GradleProjectIdentity;
 import org.gradle.tooling.model.idea.IdeaCompilerOutput;
-import org.gradle.tooling.model.idea.IdeaContentRoot;
 
 import java.io.Serializable;
 import java.util.Collection;
@@ -29,7 +28,7 @@ import java.util.List;
 
 public class DefaultIdeaModule implements Serializable, GradleProjectIdentity {
     private String name;
-    private List<? extends IdeaContentRoot> contentRoots = new LinkedList<IdeaContentRoot>();
+    private List<DefaultIdeaContentRoot> contentRoots = new LinkedList<DefaultIdeaContentRoot>();
     private DefaultIdeaProject parent;
 
     private List<DefaultIdeaDependency> dependencies = new LinkedList<DefaultIdeaDependency>();
@@ -46,11 +45,11 @@ public class DefaultIdeaModule implements Serializable, GradleProjectIdentity {
         return this;
     }
 
-    public Collection<? extends IdeaContentRoot> getContentRoots() {
+    public Collection<DefaultIdeaContentRoot> getContentRoots() {
         return contentRoots;
     }
 
-    public DefaultIdeaModule setContentRoots(List<? extends IdeaContentRoot> contentRoots) {
+    public DefaultIdeaModule setContentRoots(List<DefaultIdeaContentRoot> contentRoots) {
         this.contentRoots = contentRoots;
         return this;
     }
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaProject.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaProject.java
index aaf0234..5c5cb4b 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaProject.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaProject.java
@@ -16,16 +16,13 @@
 
 package org.gradle.plugins.ide.internal.tooling.idea;
 
-import org.gradle.tooling.internal.protocol.InternalIdeaProject;
-import org.gradle.tooling.model.HierarchicalElement;
 import org.gradle.tooling.model.idea.IdeaLanguageLevel;
 
-import java.io.File;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.LinkedList;
 
-public class DefaultIdeaProject implements InternalIdeaProject, Serializable {
+public class DefaultIdeaProject implements Serializable {
     private String name;
     private String description;
     private Collection<DefaultIdeaModule> children = new LinkedList<DefaultIdeaModule>();
@@ -68,18 +65,10 @@ public class DefaultIdeaProject implements InternalIdeaProject, Serializable {
         return this;
     }
 
-    public HierarchicalElement getParent() {
+    public Object getParent() {
         return null;
     }
 
-    public File getProjectDirectory() {
-        throw new UnsupportedOperationException("This method should not be used.");
-    }
-
-    public String getPath() {
-        throw new UnsupportedOperationException("This method should not be used.");
-    }
-
     public DefaultIdeaProject setChildren(Collection<? extends DefaultIdeaModule> children) {
         this.children.clear();
         this.children.addAll(children);
diff --git a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaSourceDirectory.java b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaSourceDirectory.java
index d6685cd..f63eeea 100644
--- a/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaSourceDirectory.java
+++ b/subprojects/ide/src/main/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaSourceDirectory.java
@@ -16,28 +16,38 @@
 
 package org.gradle.plugins.ide.internal.tooling.idea;
 
-import org.gradle.tooling.model.idea.IdeaSourceDirectory;
-
 import java.io.File;
 import java.io.Serializable;
 
-public class DefaultIdeaSourceDirectory implements IdeaSourceDirectory, Serializable {
+public class DefaultIdeaSourceDirectory implements Serializable {
 
     private File directory;
 
+    private boolean generated;
+
     public File getDirectory() {
         return directory;
     }
 
+    public boolean isGenerated() {
+        return generated;
+    }
+
     public DefaultIdeaSourceDirectory setDirectory(File directory) {
         this.directory = directory;
         return this;
     }
 
+    public DefaultIdeaSourceDirectory setGenerated(boolean generated) {
+        this.generated = generated;
+        return this;
+    }
+
     @Override
     public String toString() {
         return "DefaultIdeaSourceDirectory{"
                 + "directory=" + directory
+                + ", generated=" + generated
                 + '}';
     }
 }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/DefaultIdeDependencyResolver.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/DefaultIdeDependencyResolver.java
index 42d39d0..6dfd299 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/DefaultIdeDependencyResolver.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/DefaultIdeDependencyResolver.java
@@ -23,7 +23,7 @@ import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.artifacts.result.*;
-import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
 import org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency;
 import org.gradle.plugins.ide.internal.resolver.model.IdeLocalFileDependency;
 import org.gradle.plugins.ide.internal.resolver.model.IdeProjectDependency;
@@ -114,7 +114,8 @@ public class DefaultIdeDependencyResolver implements IdeDependencyResolver {
      */
     public List<IdeExtendedRepoFileDependency> getIdeRepoFileDependencies(Configuration configuration) {
         ResolutionResult result = getIncomingResolutionResult(configuration);
-        List<ResolvedComponentResult> resolvedDependencies = findAllResolvedDependencyResults(result.getAllDependencies(), ModuleComponentIdentifier.class);
+        List<ResolvedComponentResult> resolvedDependencies = new ArrayList<ResolvedComponentResult>();
+        findAllResolvedDependencyResultsAndTheirDependencies(resolvedDependencies, result.getRoot().getDependencies(), ModuleComponentIdentifier.class);
         Set<ModuleVersionIdentifier> mappedResolvedDependencies = mapResolvedDependencies(resolvedDependencies);
         Set<ResolvedArtifact> artifacts = getExternalArtifacts(configuration);
 
@@ -132,6 +133,28 @@ public class DefaultIdeDependencyResolver implements IdeDependencyResolver {
     }
 
     /**
+     * Finds all resolved components of the given type from the given set of dependency edges. If resolved component has dependencies itself, recursively resolve them as well
+     * and add them to the results. This method can handle circular dependencies.
+     *
+     * @param dependencies Dependencies
+     * @return Resolved dependency results
+     */
+    private void findAllResolvedDependencyResultsAndTheirDependencies(List<ResolvedComponentResult> matches, Set<? extends DependencyResult> dependencies, Class<? extends ComponentIdentifier> type) {
+        for (DependencyResult dependencyResult : dependencies) {
+            if (dependencyResult instanceof ResolvedDependencyResult) {
+                ResolvedDependencyResult resolvedResult = (ResolvedDependencyResult) dependencyResult;
+                if (type.isInstance(resolvedResult.getSelected().getId())) {
+                    // avoid circular dependencies by checking whether component result is already added
+                    if (!matches.contains(resolvedResult.getSelected())) {
+                        matches.add(resolvedResult.getSelected());
+                        findAllResolvedDependencyResultsAndTheirDependencies(matches, resolvedResult.getSelected().getDependencies(), type);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Maps resolved dependencies by module version identifier.
      *
      * @param components Resolved dependencies
@@ -238,12 +261,6 @@ public class DefaultIdeDependencyResolver implements IdeDependencyResolver {
      * @return External artifacts
      */
     private Set<ResolvedArtifact> getExternalArtifacts(Configuration configuration) {
-        return configuration.getResolvedConfiguration().getLenientConfiguration().getArtifacts(new ExternalDependencySpec());
-    }
-
-    private class ExternalDependencySpec implements Spec<Dependency> {
-        public boolean isSatisfiedBy(Dependency element) {
-            return element instanceof ExternalDependency;
-        }
+        return configuration.getResolvedConfiguration().getLenientConfiguration().getArtifacts(Specs.SATISFIES_ALL);
     }
 }
\ No newline at end of file
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeDependencyKey.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeDependencyKey.java
index 7f6a500..c843716 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeDependencyKey.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeDependencyKey.java
@@ -37,12 +37,12 @@ public abstract class IdeDependencyKey<T extends IdeDependency, R> {
 
     public static <D> IdeDependencyKey<IdeProjectDependency, D> forProjectDependency(
             IdeProjectDependency dependency, DependencyBuilder<IdeProjectDependency, D> dependencyBuilder) {
-        return new ProjectDependencyKey(dependency, dependencyBuilder);
+        return new ProjectDependencyKey<D>(dependency, dependencyBuilder);
     }
 
     public static <D> IdeDependencyKey<IdeLocalFileDependency, D> forLocalFileDependency(
             IdeLocalFileDependency dependency, DependencyBuilder<IdeLocalFileDependency, D> dependencyBuilder) {
-        return new LocalFileDependencyKey(dependency, dependencyBuilder);
+        return new LocalFileDependencyKey<D>(dependency, dependencyBuilder);
     }
 
     protected final T ideDependency;
@@ -58,6 +58,7 @@ public abstract class IdeDependencyKey<T extends IdeDependency, R> {
     }
 
     protected abstract boolean isSameDependency(IdeDependency otherDependency);
+
     protected abstract int dependencyHashCode();
 
     @Override
@@ -71,11 +72,7 @@ public abstract class IdeDependencyKey<T extends IdeDependency, R> {
 
         IdeDependencyKey that = (IdeDependencyKey) o;
 
-        if (!isSameDependency(that.ideDependency)) {
-            return false;
-        }
-
-        return true;
+        return isSameDependency(that.ideDependency);
     }
 
     @Override
@@ -95,10 +92,7 @@ public abstract class IdeDependencyKey<T extends IdeDependency, R> {
 
         @Override
         protected boolean isSameDependency(IdeDependency otherDependency) {
-            if (!(otherDependency instanceof IdeLocalFileDependency)) {
-                return false;
-            }
-            return Objects.equal(ideDependency.getFile(), ((IdeLocalFileDependency) otherDependency).getFile());
+            return otherDependency instanceof IdeLocalFileDependency && Objects.equal(ideDependency.getFile(), ((IdeLocalFileDependency) otherDependency).getFile());
         }
 
         public String toString() {
@@ -118,10 +112,7 @@ public abstract class IdeDependencyKey<T extends IdeDependency, R> {
 
         @Override
         protected boolean isSameDependency(IdeDependency otherDependency) {
-            if (!(otherDependency instanceof IdeProjectDependency)) {
-                return false;
-            }
-            return Objects.equal(ideDependency.getProject(), ((IdeProjectDependency) otherDependency).getProject());
+            return otherDependency instanceof IdeProjectDependency && Objects.equal(ideDependency.getProject(), ((IdeProjectDependency) otherDependency).getProject());
         }
 
         public String toString() {
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeExtendedRepoFileDependency.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeExtendedRepoFileDependency.java
index 39f0f86..bafd2e8 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeExtendedRepoFileDependency.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/resolver/model/IdeExtendedRepoFileDependency.java
@@ -19,28 +19,47 @@ package org.gradle.plugins.ide.internal.resolver.model;
 import org.gradle.api.artifacts.Configuration;
 
 import java.io.File;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 public class IdeExtendedRepoFileDependency extends IdeRepoFileDependency {
-    private File sourceFile;
-    private File javadocFile;
+    private static final Comparator<File> FILE_COMPARATOR = new FileNameComparator();
+    private final SortedSet<File> sourceFiles = new TreeSet<File>(FILE_COMPARATOR);
+    private final SortedSet<File> javadocFiles = new TreeSet<File>(FILE_COMPARATOR);
 
     public IdeExtendedRepoFileDependency(Configuration declaredConfiguration, File file) {
         super(declaredConfiguration, file);
     }
 
     public File getSourceFile() {
-        return sourceFile;
+        return sourceFiles.isEmpty() ? null : sourceFiles.first();
     }
 
-    public void setSourceFile(File sourceFile) {
-        this.sourceFile = sourceFile;
+    public Set<File> getSourceFiles() {
+        return sourceFiles;
+    }
+
+    public void addSourceFile(File sourceFile) {
+        sourceFiles.add(sourceFile);
     }
 
     public File getJavadocFile() {
-        return javadocFile;
+        return javadocFiles.isEmpty() ? null : javadocFiles.first();
+    }
+
+    public Set<File> getJavadocFiles() {
+        return javadocFiles;
+    }
+
+    public void addJavadocFile(File javadocFile) {
+        javadocFiles.add(javadocFile);
     }
 
-    public void setJavadocFile(File javadocFile) {
-        this.javadocFile = javadocFile;
+    private static class FileNameComparator implements Comparator<File> {
+        public int compare(File o1, File o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse-wtp.properties b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse-wtp.properties
similarity index 100%
rename from subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse-wtp.properties
rename to subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse-wtp.properties
diff --git a/subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse.properties b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse.properties
similarity index 100%
rename from subprojects/ide/src/main/resources/META-INF/gradle-plugins/eclipse.properties
rename to subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.eclipse.properties
diff --git a/subprojects/ide/src/main/resources/META-INF/gradle-plugins/idea.properties b/subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.idea.properties
similarity index 100%
rename from subprojects/ide/src/main/resources/META-INF/gradle-plugins/idea.properties
rename to subprojects/ide/src/main/resources/META-INF/gradle-plugins/org.gradle.idea.properties
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy
index 5d51212..9f993be 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipsePluginTest.groovy
@@ -105,8 +105,8 @@ class EclipsePluginTest extends Specification {
         eclipsePlugin.apply(project)
 
         then:
-        eclipsePlugin.model.classpath
-        eclipsePlugin.model.classpath.defaultOutputDir
+        project.eclipse.classpath
+        project.eclipse.classpath.defaultOutputDir
     }
 
     def "configures internal class folders"() {
@@ -135,15 +135,19 @@ class EclipsePluginTest extends Specification {
     }
 
     private void checkEclipseClasspath(def configurations, def additionalContainers = []) {
-        GenerateEclipseClasspath eclipseClasspath = project.tasks.eclipseClasspath
-        assert eclipseClasspath instanceof GenerateEclipseClasspath
-        assert project.tasks.eclipse.taskDependencies.getDependencies(project.tasks.eclipse).contains(eclipseClasspath)
-        assert eclipseClasspath.sourceSets == project.sourceSets
-        assert eclipseClasspath.plusConfigurations == configurations
-        assert eclipseClasspath.minusConfigurations == []
-        assert eclipseClasspath.containers == ['org.eclipse.jdt.launching.JRE_CONTAINER'] + additionalContainers as Set
-        assert eclipseClasspath.outputFile == project.file('.classpath')
-        assert eclipseClasspath.defaultOutputDir == new File(project.projectDir, 'bin')
+        def classpath = project.eclipse.classpath
+        def classpathTask = project.tasks.eclipseClasspath
+
+        assert classpathTask instanceof GenerateEclipseClasspath
+        assert classpathTask.classpath == classpath
+        assert classpathTask.outputFile == project.file('.classpath')
+        assert project.tasks.eclipse.taskDependencies.getDependencies(project.tasks.eclipse).contains(classpathTask)
+
+        assert classpath.sourceSets == project.sourceSets
+        assert classpath.plusConfigurations == configurations
+        assert classpath.minusConfigurations == []
+        assert classpath.containers == ['org.eclipse.jdt.launching.JRE_CONTAINER'] + additionalContainers as Set
+        assert classpath.defaultOutputDir == new File(project.projectDir, 'bin')
     }
 
     private void checkEclipseJdt() {
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPluginTest.groovy
index 1214ce0..13e66bf 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPluginTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpPluginTest.groovy
@@ -20,10 +20,12 @@ import org.gradle.api.internal.project.DefaultProject
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.plugins.ide.eclipse.model.Facet
 import org.gradle.plugins.ide.eclipse.model.Facet.FacetType
+import org.gradle.plugins.ide.eclipse.model.WbProperty
 import org.gradle.plugins.ide.eclipse.model.WbResource
 import org.gradle.util.TestUtil
 import spock.lang.Issue
 import spock.lang.Specification
+import spock.lang.Unroll
 
 class EclipseWtpPluginTest extends Specification {
 
@@ -54,7 +56,63 @@ class EclipseWtpPluginTest extends Specification {
         project.tasks.cleanEclipse.dependsOn.contains(project.cleanEclipseWtp)
     }
 
-     def applyToWarProject_shouldHaveWebProjectAndClasspathTask() {
+    def applyToJavaProject_shouldHaveWebProjectAndClasspathTask() {
+        when:
+        project.apply(plugin: 'java')
+        project.sourceCompatibility = 1.6
+        wtpPlugin.apply(project)
+
+        then:
+        [project.tasks.cleanEclipseWtpComponent, project.tasks.cleanEclipseWtpFacet].each {
+            assert project.tasks.cleanEclipseWtp.dependsOn.contains(it)
+        }
+        checkEclipseClasspath([project.configurations.testRuntime])
+        checkEclipseWtpComponentForJava()
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, 'jst.java', null),
+                new Facet(FacetType.installed, 'jst.utility', '1.0'),
+                new Facet(FacetType.installed, 'jst.java', '6.0')])
+    }
+
+    def applyFirstToJavaProject_shouldHaveWebProjectAndClasspathTask() {
+        when:
+        wtpPlugin.apply(project)
+        project.apply(plugin: 'java')
+        project.sourceCompatibility = 1.7
+
+        then:
+        [project.tasks.cleanEclipseWtpComponent, project.tasks.cleanEclipseWtpFacet].each {
+            assert project.tasks.cleanEclipseWtp.dependsOn.contains(it)
+        }
+        checkEclipseClasspath([project.configurations.testRuntime])
+        checkEclipseWtpComponentForJava()
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, 'jst.java', null),
+                new Facet(FacetType.installed, 'jst.utility', '1.0'),
+                new Facet(FacetType.installed, 'jst.java', '1.7')])
+    }
+
+    def "can add custom facets to java default facets"() {
+        when:
+        project.apply(plugin: 'java')
+        wtpPlugin.apply(project)
+        project.sourceCompatibility = 1.3
+
+        project.eclipse.wtp {
+            facet {
+                facet name: 'someCoolFacet', version: '1.3'
+            }
+        }
+
+        then:
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, 'jst.java', null),
+                new Facet(FacetType.installed, 'jst.utility', '1.0'),
+                new Facet(FacetType.installed, 'jst.java', '1.3'),
+                new Facet(FacetType.installed, 'someCoolFacet', '1.3')])
+    }
+
+    def applyToWarProject_shouldHaveWebProjectAndClasspathTask() {
         when:
         project.apply(plugin: 'war')
         project.sourceCompatibility = 1.5
@@ -74,6 +132,47 @@ class EclipseWtpPluginTest extends Specification {
                 new Facet(FacetType.installed, "jst.java", "5.0")])
     }
 
+    def applyFirstToWarProject_shouldHaveWebProjectAndClasspathTask() {
+        when:
+        wtpPlugin.apply(project)
+        project.apply(plugin: 'war')
+        project.sourceCompatibility = 1.8
+
+        then:
+        [project.cleanEclipseWtpComponent, project.cleanEclipseWtpFacet].each {
+            assert project.tasks.cleanEclipseWtp.dependsOn.contains(it)
+        }
+
+        checkEclipseClasspath([project.configurations.testRuntime])
+        checkEclipseWtpComponentForWar()
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, "jst.java", null),
+                new Facet(FacetType.fixed, "jst.web", null),
+                new Facet(FacetType.installed, "jst.web", "2.4"),
+                new Facet(FacetType.installed, "jst.java", "1.8")])
+    }
+
+    def "can add custom facets to war default facets"() {
+        when:
+        project.apply(plugin: 'war')
+        wtpPlugin.apply(project)
+        project.sourceCompatibility = 1.4
+
+        project.eclipse.wtp {
+            facet {
+                facet name: 'someCoolFacet', version: '1.4'
+            }
+        }
+
+        then:
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, "jst.java", null),
+                new Facet(FacetType.fixed, "jst.web", null),
+                new Facet(FacetType.installed, "jst.web", "2.4"),
+                new Facet(FacetType.installed, "jst.java", "1.4"),
+                new Facet(FacetType.installed, 'someCoolFacet', '1.4')])
+    }
+
     @Issue("GRADLE-1770")
     def "wb resource honors web app dir even if configured after plugin appliance"() {
         when:
@@ -82,73 +181,175 @@ class EclipseWtpPluginTest extends Specification {
         project.webAppDirName = 'foo'
 
         then:
-        project.eclipseWtpComponent.resources == [new WbResource('/', 'foo')]
+        project.eclipse.wtp.component.resources == [new WbResource('/', 'foo')]
     }
 
-    def applyToEarProject_shouldHaveWebProjectAndClasspathTask() {
+    def "web app dir should not disappear while manually adding a wb resource"() {
         when:
-        project.apply(plugin: 'java')
-        project.apply(plugin: 'ear')
+        project.apply(plugin: 'war')
         wtpPlugin.apply(project)
+        project.webAppDirName = 'foo'
+
+        project.eclipse.wtp {
+            component {
+                resource sourcePath: "common", deployPath: "/common"
+            }
+        }
+
+        then:
+        project.eclipse.wtp.component.resources == [new WbResource('/', 'foo'), new WbResource('/common', 'common')]
+    }
+
+    @Unroll
+    def 'applyToEarProject in order #plugs should have web project and classpath task'() {
+        when:
+        plugs.each { p ->
+            if (p == 'eclipse-wtp') {
+                wtpPlugin.apply(project)
+            } else {
+                project.apply(plugin: p)
+            }
+        }
 
         then:
         [project.cleanEclipseWtpComponent, project.cleanEclipseWtpFacet].each {
             assert project.cleanEclipseWtp.dependsOn.contains(it)
         }
-        checkEclipseClasspath([project.configurations.testRuntime])
-        checkEclipseWtpComponentForEar()
+
+        if (plugs.contains('java')) {
+            checkEclipseClasspath([project.configurations.testRuntime])
+            checkEclipseWtpComponentForEar(project.sourceSets.main.allSource.srcDirs)
+        } else {
+            checkEclipseClasspath([])
+            checkEclipseWtpComponentForEar(project.files(project.appDirName) as Set)
+        }
         checkEclipseWtpFacet([
                 new Facet(FacetType.fixed, "jst.ear", null),
                 new Facet(FacetType.installed, "jst.ear", "5.0")])
+
+        where:
+        plugs << [
+                ['ear', 'eclipse-wtp'],
+                ['eclipse-wtp', 'ear'],
+
+                ['java', 'ear', 'eclipse-wtp'],
+                ['java', 'eclipse-wtp', 'ear'],
+
+                ['ear', 'java', 'eclipse-wtp'],
+                ['ear', 'eclipse-wtp', 'java'],
+
+                ['eclipse-wtp', 'java', 'ear'],
+                ['eclipse-wtp', 'ear', 'java']]
     }
 
-    private void checkEclipseWtpComponentForEar() {
-        def eclipseWtpComponent = project.eclipseWtpComponent
-        assert eclipseWtpComponent instanceof GenerateEclipseWtpComponent
-        assert project.tasks.eclipseWtp.taskDependencies.getDependencies(project.tasks.eclipseWtp).contains(eclipseWtpComponent)
-        assert eclipseWtpComponent.sourceDirs == project.sourceSets.main.allSource.srcDirs
-        assert eclipseWtpComponent.component.rootConfigurations == [project.configurations.deploy] as Set
-        assert eclipseWtpComponent.component.libConfigurations == [project.configurations.earlib] as Set
-        assert eclipseWtpComponent.minusConfigurations == [] as Set
-        assert eclipseWtpComponent.deployName == project.name
-        assert eclipseWtpComponent.contextPath == null
-        assert eclipseWtpComponent.inputFile == project.file('.settings/org.eclipse.wst.common.component')
-        assert eclipseWtpComponent.outputFile == project.file('.settings/org.eclipse.wst.common.component')
-        assert eclipseWtpComponent.variables == [:]
-        assert eclipseWtpComponent.resources == []
-        assert eclipseWtpComponent.component.classesDeployPath == "/"
-        assert eclipseWtpComponent.component.libDeployPath == "/lib"
+    def "can add custom facets to ear project"() {
+        when:
+        project.apply(plugin: 'ear')
+        wtpPlugin.apply(project)
+
+        project.eclipse.wtp {
+            facet {
+                facet name: 'someFancyFacet', version: '2.0'
+            }
+        }
+
+        then:
+        checkEclipseWtpFacet([
+                new Facet(FacetType.fixed, "jst.ear", null),
+                new Facet(FacetType.installed, "jst.ear", "5.0"),
+                new Facet(FacetType.installed, 'someFancyFacet', '2.0')])
+    }
+
+    @Issue(['GRADLE-2186', 'GRADLE-2221'])
+    def "can change WTP components and add facets when java plugin is applied"() {
+        when:
+        project.apply(plugin: 'java')
+        wtpPlugin.apply(project)
+        project.sourceCompatibility = 1.7
+
+        project.eclipse.wtp {
+            component {
+                deployName = 'ejb-jar'
+                property name: 'mood', value: ':-D'
+            }
+            facet {
+                facet name: 'jst.ejb', version: '3.0'
+            }
+        }
+
+        then:
+        project.eclipse.wtp.component.deployName == 'ejb-jar'
+        project.eclipse.wtp.component.properties == [new WbProperty('mood', ':-D')]
+        checkEclipseWtpFacet([new Facet(FacetType.fixed, 'jst.java', null),
+                              new Facet(FacetType.installed, 'jst.utility', '1.0'),
+                              new Facet(FacetType.installed, 'jst.java', '1.7'),
+                              new Facet(FacetType.installed, 'jst.ejb', '3.0')])
     }
 
-    private void checkEclipseWtpFacet(def facets) {
-        GenerateEclipseWtpFacet eclipseWtpFacet = project.eclipseWtpFacet
+
+    private void checkEclipseWtpComponentForEar(def expectedSourceDirs) {
+        def wtp = checkAndGetEclipseWtpComponent()
+        assert wtp.sourceDirs == expectedSourceDirs
+        assert wtp.rootConfigurations == [project.configurations.deploy] as Set
+        assert wtp.libConfigurations == [project.configurations.earlib] as Set
+        assert wtp.minusConfigurations == [] as Set
+        assert wtp.deployName == project.name
+        assert wtp.contextPath == null
+        assert wtp.resources == []
+        assert wtp.classesDeployPath == "/"
+        assert wtp.libDeployPath == "/lib"
+    }
+
+    private void checkEclipseWtpFacet(def expectedFacets) {
+        def wtp = project.eclipse.wtp.facet
+        def eclipseWtpFacet = project.eclipseWtpFacet
         assert eclipseWtpFacet instanceof GenerateEclipseWtpFacet
+        assert eclipseWtpFacet.facet == wtp
         assert project.tasks.eclipseWtp.taskDependencies.getDependencies(project.tasks.eclipse).contains(eclipseWtpFacet)
         assert eclipseWtpFacet.inputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
         assert eclipseWtpFacet.outputFile == project.file('.settings/org.eclipse.wst.common.project.facet.core.xml')
+        assert wtp.facets.sort() == expectedFacets.sort()
+    }
+
+    private void checkEclipseWtpComponentForJava() {
+        def wtp = checkAndGetEclipseWtpComponent()
+        assert wtp.sourceDirs == project.sourceSets.main.allSource.srcDirs
+        assert wtp.rootConfigurations == [] as Set
+        assert wtp.libConfigurations == [project.configurations.runtime] as Set
+        assert wtp.minusConfigurations == [] as Set
+        assert wtp.deployName == project.name
+        assert wtp.contextPath == null
+        assert wtp.resources == []
+        assert wtp.classesDeployPath == "/"
+        assert wtp.libDeployPath == "../"
     }
 
     private void checkEclipseWtpComponentForWar() {
+        def wtp = checkAndGetEclipseWtpComponent()
+        assert wtp.sourceDirs == project.sourceSets.main.allSource.srcDirs
+        assert wtp.rootConfigurations == [] as Set
+        assert wtp.libConfigurations == [project.configurations.runtime] as Set
+        assert wtp.minusConfigurations == [project.configurations.providedRuntime] as Set
+        assert wtp.deployName == project.name
+        assert wtp.contextPath == project.war.baseName
+        assert wtp.resources == [new WbResource('/', project.convention.plugins.war.webAppDirName)]
+        assert wtp.classesDeployPath == "/WEB-INF/classes"
+        assert wtp.libDeployPath == "/WEB-INF/lib"
+    }
+
+    private void checkEclipseClasspath(def configurations) {
+        assert project.eclipse.classpath.plusConfigurations == configurations
+    }
+
+    private def checkAndGetEclipseWtpComponent() {
+        def wtp = project.eclipse.wtp.component
         def eclipseWtpComponent = project.eclipseWtpComponent
         assert eclipseWtpComponent instanceof GenerateEclipseWtpComponent
-        assert project.tasks.eclipseWtp.taskDependencies.getDependencies(project.tasks.eclipse).contains(eclipseWtpComponent)
-        assert eclipseWtpComponent.sourceDirs == project.sourceSets.main.allSource.srcDirs
-        assert eclipseWtpComponent.component.rootConfigurations == [] as Set
-        assert eclipseWtpComponent.component.libConfigurations == [project.configurations.runtime] as Set
-        assert eclipseWtpComponent.minusConfigurations == [project.configurations.providedRuntime] as Set
-        assert eclipseWtpComponent.deployName == project.name
-        assert eclipseWtpComponent.contextPath == project.war.baseName
+        assert project.tasks.eclipseWtp.taskDependencies.getDependencies(project.tasks.eclipseWtp).contains(eclipseWtpComponent)
+        assert eclipseWtpComponent.component == wtp
         assert eclipseWtpComponent.inputFile == project.file('.settings/org.eclipse.wst.common.component')
         assert eclipseWtpComponent.outputFile == project.file('.settings/org.eclipse.wst.common.component')
-        assert eclipseWtpComponent.variables == [:]
-        assert eclipseWtpComponent.resources == [new WbResource('/', project.convention.plugins.war.webAppDirName)]
-        assert eclipseWtpComponent.component.classesDeployPath == "/WEB-INF/classes"
-        assert eclipseWtpComponent.component.libDeployPath == "/WEB-INF/lib"
-    }
-
-    private void checkEclipseClasspath(def configurations) {
-        GenerateEclipseClasspath eclipseClasspath = project.tasks.eclipseClasspath
-        assert eclipseClasspath.plusConfigurations == configurations
+        return wtp
     }
 
     def applyToEarProjectWithoutJavaPlugin_shouldUseAppDirInWtpComponentSource() {
@@ -156,6 +357,6 @@ class EclipseWtpPluginTest extends Specification {
         project.apply(plugin: 'ear')
         wtpPlugin.apply(project)
         then:
-        project.eclipseWtpComponent.sourceDirs == [project.file(project.appDirName)] as Set
+        project.eclipse.wtp.component.sourceDirs == [project.file(project.appDirName)] as Set
     }
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy
index cdad8c7..dcd7eb3 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseClasspathTest.groovy
@@ -32,22 +32,4 @@ public class GenerateEclipseClasspathTest extends AbstractSpockTaskTest {
         eclipseClasspath = createTask(GenerateEclipseClasspath.class);
         eclipseClasspath.classpath = new EclipseClasspath()
     }
-
-    def containers_shouldAdd() {
-        when:
-        eclipseClasspath.containers "container1"
-        eclipseClasspath.containers "container2"
-
-        then:
-        eclipseClasspath.containers == ['container1', 'container2'] as Set
-    }
-
-    def variables_shouldAdd() {
-        when:
-        eclipseClasspath.variables variable1: 'value1'
-        eclipseClasspath.variables variable2: 'value2'
-
-        then:
-        eclipseClasspath.variables == [variable1: 'value1', variable2: 'value2']
-    }
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy
index 7022ecb..e00a038 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpComponentTest.groovy
@@ -18,8 +18,6 @@ package org.gradle.plugins.ide.eclipse
 import org.gradle.api.internal.ConventionTask
 import org.gradle.api.tasks.AbstractSpockTaskTest
 import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent
-import org.gradle.plugins.ide.eclipse.model.WbProperty
-import org.gradle.plugins.ide.eclipse.model.WbResource
 
 public class GenerateEclipseWtpComponentTest extends AbstractSpockTaskTest {
     private eclipseComponent = createTask(GenerateEclipseWtpComponent)
@@ -29,31 +27,4 @@ public class GenerateEclipseWtpComponentTest extends AbstractSpockTaskTest {
     }
 
     ConventionTask getTask() { eclipseComponent }
-
-    def "property should add"() {
-        when:
-        eclipseComponent.property name: 'prop1', value: 'value1'
-        eclipseComponent.property name: 'prop2', value: 'value2'
-
-        then:
-        eclipseComponent.properties == [new WbProperty('prop1', 'value1'), new WbProperty('prop2', 'value2')]
-    }
-
-    def "resource should add"() {
-        when:
-        eclipseComponent.resource deployPath: 'path1', sourcePath: 'sourcepath1'
-        eclipseComponent.resource deployPath: 'path2', sourcePath: 'sourcepath2'
-
-        then:
-        eclipseComponent.resources == [new WbResource('path1', 'sourcepath1'), new WbResource('path2', 'sourcepath2')]
-    }
-
-    def "variables should add"() {
-        when:
-        eclipseComponent.variables variable1: 'value1'
-        eclipseComponent.variables variable2: 'value2'
-
-        then:
-        eclipseComponent.variables == [variable1: 'value1', variable2: 'value2']
-    }
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy
index 11d1384..77c4bd7 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/GenerateEclipseWtpFacetTest.groovy
@@ -17,6 +17,8 @@ package org.gradle.plugins.ide.eclipse
 
 import org.gradle.api.tasks.AbstractSpockTaskTest
 import org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet
+import org.gradle.plugins.ide.eclipse.model.Facet
+import org.gradle.plugins.ide.eclipse.model.Facet.FacetType
 
 public class GenerateEclipseWtpFacetTest extends AbstractSpockTaskTest {
     private eclipseFacet = createTask(GenerateEclipseWtpFacet)
@@ -29,4 +31,22 @@ public class GenerateEclipseWtpFacetTest extends AbstractSpockTaskTest {
         return eclipseFacet
     }
 
+    def "created facets defaults to type 'installed'"() {
+        when:
+        eclipseFacet.facet.facet(name: 'fancyProject', version: '1.3')
+
+        then:
+        eclipseFacet.facet.facets == [new Facet(FacetType.installed, 'fancyProject', '1.3')]
+    }
+
+    def "can explicitly configure facet type'"() {
+        when:
+        eclipseFacet.facet.facet type: facetType, name: 'fancyProject'
+
+        then:
+        eclipseFacet.facet.facets[0].type == FacetType.valueOf(facetType)
+        where:
+
+        facetType << ["fixed", "installed"]
+    }
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy
index 197ef1b..a8acc60 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ClasspathTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.eclipse.model
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy
index 74130f7..fed05a8 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/ProjectTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.eclipse.model;
 
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy
index 7232eb8..f0c945c 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpComponentTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.eclipse.model
 
 import org.custommonkey.xmlunit.XMLUnit
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy
index 86c3bd1..b1bf5b9 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/eclipse/model/WtpFacetTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.eclipse.model
 
 import org.custommonkey.xmlunit.XMLUnit
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.plugins.ide.eclipse.model.Facet.FacetType
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/ GenerateIdeaModuleTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModuleTest.groovy
similarity index 100%
rename from subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/ GenerateIdeaModuleTest.groovy
rename to subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/GenerateIdeaModuleTest.groovy
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy
index 8afaf86..b14a943 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/IdeaPluginTest.groovy
@@ -27,7 +27,7 @@ import spock.lang.Specification
 
 class IdeaPluginTest extends Specification {
     private final DefaultProject project = TestUtil.createRootProject()
-    private final Project childProject = TestUtil.createChildProject(project, "child", new File("."))
+    private final DefaultProject childProject = TestUtil.createChildProject(project, "child", new File("."))
 
     def "adds 'ideaProject' task to root project"() {
         when:
@@ -138,7 +138,7 @@ class IdeaPluginTest extends Specification {
         applyPluginToProjects()
 
         when:
-        childProject.plugins.apply(ScalaPlugin)
+        childProject.pluginManager.apply(ScalaPlugin)
 
         then:
         def parentIdeaProject = project.tasks.ideaProject
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy
index 0470bcd..b10ff7d 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ModuleTest.groovy
@@ -16,14 +16,15 @@
 package org.gradle.plugins.ide.idea.model
 
 import org.gradle.api.JavaVersion
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import spock.lang.Specification
 
 class ModuleTest extends Specification {
     final PathFactory pathFactory = new PathFactory()
     final XmlTransformer xmlTransformer = new XmlTransformer()
-    final customSourceFolders = [path('file://$MODULE_DIR$/src')] as LinkedHashSet
-    final customTestSourceFolders = [path('file://$MODULE_DIR$/srcTest')] as LinkedHashSet
+    final customSourceFolders = [path('file://$MODULE_DIR$/src'), path('file://$MODULE_DIR$/generated-src')] as LinkedHashSet
+    final customTestSourceFolders = [path('file://$MODULE_DIR$/srcTest'), path('file://$MODULE_DIR$/generated-test-src')] as LinkedHashSet
+    final customGeneratedSourceFolders = [path('file://$MODULE_DIR$/generated-src'), path('file://$MODULE_DIR$/generated-test-src')] as LinkedHashSet
     final customExcludeFolders = [path('file://$MODULE_DIR$/target')] as LinkedHashSet
     final customDependencies = [
             new ModuleLibrary([path('file://$MODULE_DIR$/gradle/lib')] as Set,
@@ -43,6 +44,7 @@ class ModuleTest extends Specification {
         module.jdkName == "1.6"
         module.sourceFolders == customSourceFolders
         module.testSourceFolders == customTestSourceFolders
+        module.generatedSourceFolders == customGeneratedSourceFolders
         module.excludeFolders == customExcludeFolders
         module.outputDir == path('file://$MODULE_DIR$/out')
         module.testOutputDir == path('file://$MODULE_DIR$/outTest')
@@ -50,9 +52,10 @@ class ModuleTest extends Specification {
     }
 
     def configureOverwritesDependenciesAndAppendsAllOtherEntries() {
-        def constructorSourceFolders = [path('a')] as Set
-        def constructorTestSourceFolders = [path('b')] as Set
+        def constructorSourceFolders = [path('a'), path('d')] as Set
+        def constructorTestSourceFolders = [path('b'), path('e')] as Set
         def constructorExcludeFolders = [path('c')] as Set
+        def constructorGeneratedSourceFolders = [path('d'), path('e')] as Set
         def constructorInheritOutputDirs = false
         def constructorOutputDir = path('someOut')
         def constructorJavaVersion = JavaVersion.VERSION_1_6.toString()
@@ -63,13 +66,14 @@ class ModuleTest extends Specification {
 
         when:
         module.load(customModuleReader)
-        module.configure(null, constructorSourceFolders, constructorTestSourceFolders, constructorExcludeFolders,
+        module.configure(null, constructorSourceFolders, constructorTestSourceFolders, constructorGeneratedSourceFolders, constructorExcludeFolders,
                 constructorInheritOutputDirs, constructorOutputDir, constructorTestOutputDir, constructorModuleDependencies, constructorJavaVersion)
 
         then:
         module.sourceFolders == customSourceFolders + constructorSourceFolders
         module.testSourceFolders == customTestSourceFolders + constructorTestSourceFolders
         module.excludeFolders == customExcludeFolders + constructorExcludeFolders
+        module.generatedSourceFolders == customGeneratedSourceFolders + constructorGeneratedSourceFolders
         module.outputDir == constructorOutputDir
         module.testOutputDir == constructorTestOutputDir
         module.jdkName == constructorJavaVersion.toString()
@@ -78,7 +82,7 @@ class ModuleTest extends Specification {
 
     def "configures default java version"() {
         when:
-        module.configure(null, [] as Set, [] as Set, [] as Set,
+        module.configure(null, [] as Set, [] as Set, [] as Set, [] as Set,
                 true, null, null, [] as Set, null)
 
         then:
@@ -103,7 +107,7 @@ class ModuleTest extends Specification {
 
         when:
         module.loadDefaults()
-        module.configure(null, constructorSourceFolders, [] as Set, [] as Set, false, constructorOutputDir, constructorTestOutputDir, [] as Set, null)
+        module.configure(null, constructorSourceFolders, [] as Set, [] as Set, [] as Set, false, constructorOutputDir, constructorTestOutputDir, [] as Set, null)
         def xml = toXmlReader
         def newModule = new Module(xmlTransformer, pathFactory)
         newModule.load(xml)
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy
index c8cd474..0f92df5 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/ProjectTest.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.ide.idea.model
 
 import org.gradle.api.JavaVersion
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import spock.lang.Specification
 
 class ProjectTest extends Specification {
@@ -40,7 +40,7 @@ class ProjectTest extends Specification {
 
         when:
         project.load(customProjectReader)
-        project.configure(modules, "1.6", new IdeaLanguageLevel("JDK_1_5"), ['?*.groovy'], [])
+        project.configure(modules, "1.6", new IdeaLanguageLevel("JDK_1_5"), ['?*.groovy'], [], '')
 
         then:
         project.modulePaths as Set == (customModules + modules) as Set
@@ -53,12 +53,21 @@ class ProjectTest extends Specification {
 
         when:
         project.load(customProjectReader)
-        project.configure([], "1.6", new IdeaLanguageLevel("JDK_1_5"), [], libraries)
+        project.configure([], "1.6", new IdeaLanguageLevel("JDK_1_5"), [], libraries, '')
 
         then:
         project.projectLibraries as List == libraries
     }
 
+    def "project vcs is set"() {
+        when:
+        project.load(customProjectReader)
+        project.configure([], "1.6", new IdeaLanguageLevel("JDK_1_5"), [], [], 'Git')
+
+        then:
+        project.vcs == 'Git'
+    }
+
     def loadDefaults() {
         when:
         project.loadDefaults()
@@ -73,7 +82,7 @@ class ProjectTest extends Specification {
     def toXml_shouldContainCustomValues() {
         when:
         project.loadDefaults()
-        project.configure([], "1.6", new IdeaLanguageLevel("JDK_1_5"), ['?*.groovy'], [])
+        project.configure([], "1.6", new IdeaLanguageLevel("JDK_1_5"), ['?*.groovy'], [], '')
         def xml = toXmlReader
         def other = new Project(new XmlTransformer(), pathFactory)
         other.load(xml)
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProviderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProviderTest.groovy
index cc61e67..3653b83 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProviderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/IdeaDependenciesProviderTest.groovy
@@ -80,7 +80,7 @@ public class IdeaDependenciesProviderTest extends Specification {
         when:
         project.dependencies.add('compile', project.files('lib/guava.jar'))
         project.dependencies.add('excluded', project.files('lib/guava.jar'))
-        module.scopes.COMPILE.minus += project.configurations.getByName('excluded')
+        module.scopes.COMPILE.minus << project.configurations.getByName('excluded')
         def result = dependenciesProvider.provide(module)
 
         then:
@@ -102,8 +102,8 @@ public class IdeaDependenciesProviderTest extends Specification {
         project.dependencies.add('compile', project.files('lib/slf4j-api.jar'))
         project.dependencies.add('excluded1', project.files('lib/guava.jar'))
         project.dependencies.add('excluded2', project.files('lib/slf4j-api.jar'))
-        module.scopes.COMPILE.minus += project.configurations.getByName('excluded1')
-        module.scopes.COMPILE.minus += project.configurations.getByName('excluded2')
+        module.scopes.COMPILE.minus << project.configurations.getByName('excluded1')
+        module.scopes.COMPILE.minus << project.configurations.getByName('excluded2')
         def result = dependenciesProvider.provide(module)
 
         then:
@@ -121,7 +121,7 @@ public class IdeaDependenciesProviderTest extends Specification {
         module.offline = true
 
         when:
-        module.scopes.RUNTIME.plus += detachedCfg
+        module.scopes.RUNTIME.plus << detachedCfg
         def result = dependenciesProvider.provide(module)
 
         then:
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy
index 2f48872..2b47fa2 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/generator/XmlPersistableConfigurationObjectTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.ide.internal.generator
 
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.TextUtil
 import org.junit.Rule
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilderTest.groovy
index b816718..a1379cf 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/BuildInvocationsBuilderTest.groovy
@@ -17,7 +17,7 @@
 package org.gradle.plugins.ide.internal.tooling
 
 import org.gradle.api.DefaultTask
-import org.gradle.tooling.internal.impl.LaunchableGradleTaskSelector
+import org.gradle.api.internal.project.DefaultProjectTaskLister
 import org.gradle.tooling.model.gradle.BuildInvocations
 import org.gradle.util.TestUtil
 import spock.lang.Shared
@@ -25,55 +25,115 @@ import spock.lang.Specification
 import spock.lang.Unroll
 
 class BuildInvocationsBuilderTest extends Specification {
-    def builder = new BuildInvocationsBuilder(new GradleProjectBuilder())
-    @Shared def project = TestUtil.builder().withName("root").build()
-    @Shared def child1 = TestUtil.builder().withName("child1").withParent(project).build()
-    @Shared def child1a = TestUtil.builder().withName("child1a").withParent(child1).build()
-    @Shared def child1b = TestUtil.builder().withName("child1b").withParent(child1).build()
+    @Shared
+    def project = TestUtil.builder().withName("root").build()
+    @Shared
+    def child = TestUtil.builder().withName("child").withParent(project).build()
+    @Shared
+    def grandChild1OfChild = TestUtil.builder().withName("grandChild1").withParent(child).build()
+    @Shared
+    def grandChild2OfChild = TestUtil.builder().withName("grandChild2").withParent(child).build()
 
     def setupSpec() {
-        child1a.tasks.create('t1', DefaultTask)
-        child1b.tasks.create('t1', DefaultTask)
-        child1b.tasks.create('t2', DefaultTask)
-        child1.tasks.create('t2', DefaultTask)
-        project.tasks.create('t3', DefaultTask)
+        // create a project/task tree:
+        //   root (t1, t2)
+        //   +--- child 1 (t2, t3)
+        //        +-- grand child (t3, t4)
+        //        +-- grand child (t4)
+
+        // root tasks (one public, one private)
+        def task1OfRoot = project.tasks.create('t1', DefaultTask)
+        task1OfRoot.group = 'build'
+        task1OfRoot.description = 'T1 from root'
+
+        def task2OfRoot = project.tasks.create('t2', DefaultTask)
+        task2OfRoot.group = null
+        task2OfRoot.description = null
+
+        // child tasks (one public, one private)
+        def task1OfChild1 = child.tasks.create('t2', DefaultTask)
+        task1OfChild1.group = 'build'
+        task1OfChild1.description = 'T2 from child'
+
+        def task2OfChild1 = child.tasks.create('t3', DefaultTask)
+        task2OfChild1.group = null
+        task2OfChild1.description = 'T3 from child'
+
+        // grand child tasks (one public, one private)
+        def task1OfGrandChild = grandChild1OfChild.tasks.create('t3', DefaultTask)
+        task1OfGrandChild.group = 'build'
+        task1OfGrandChild.description = null
+
+        def task2OfGrandChild = grandChild1OfChild.tasks.create('t4', DefaultTask)
+        task2OfGrandChild.group = null
+        task2OfGrandChild.description = 'T4 from grand child 1'
+
+        def taskOfGrandChild2 = grandChild2OfChild.tasks.create('t4', DefaultTask)
+        taskOfGrandChild2.group = null
+        taskOfGrandChild2.description = 'T4 from grand child 2'
     }
 
-    def "can build model"() {
-        expect:
-        builder.canBuild(BuildInvocations.name)
+    def "BuildInvocations model is accepted"() {
+        given:
+        def builder = new BuildInvocationsBuilder(new DefaultProjectTaskLister())
+
+        when:
+        def canBuild = builder.canBuild(BuildInvocations.name)
+
+        then:
+        canBuild
     }
 
-    @Unroll("builds model for #startProject")
-    def "builds model"() {
-        expect:
+    @Unroll("tasks and selectors for #startProject")
+    def "BuildInvocations model is created from tasks and task selectors for given project and its subprojects"() {
+        given:
+        def builder = new BuildInvocationsBuilder(new DefaultProjectTaskLister())
+
+        when:
         def model = builder.buildAll("org.gradle.tooling.model.gradle.BuildInvocations", startProject)
-        model.taskSelectors*.name as Set == selectorNames as Set
+
+        then:
         model.taskSelectors*.projectPath as Set == [startProject.path] as Set
-        // model.taskSelectors.find { it.name == 't1' }?.tasks == t1Tasks as Set
+        model.taskSelectors*.name as Set == selectorNames as Set
+        model.tasks*.name as Set == taskNames as Set
+
+        model.taskSelectors.findAll { it.public }*.name as Set == visibleSelectors as Set
+        model.tasks.findAll { it.public }*.name as Set == visibleTasks as Set
 
         where:
-        startProject | selectorNames
-        project      | ['t1', 't2', 't3']
-        child1       | ['t1', 't2']
-        child1a      | ['t1']
+        startProject       | selectorNames            | taskNames     | visibleSelectors   | visibleTasks
+        project            | ['t1', 't2', 't3', 't4'] | ['t1', 't2']  | ['t1', 't2', 't3'] | ['t1']
+        child              | ['t2', 't3', 't4']       | ['t2', 't3',] | ['t2', 't3']       | ['t2']
+        grandChild1OfChild | ['t3', 't4']             | ['t3', 't4',] | ['t3']             | ['t3']
+        grandChild2OfChild | ['t4']                   | ['t4',]       | []                 | []
     }
 
-    def "builds recursive model"() {
+    def "BuildInvocations model is created for root project if implicitProject flag is set"() {
+        given:
+        def builder = new BuildInvocationsBuilder(new DefaultProjectTaskLister())
+
         when:
-        def model = builder.buildAll("org.gradle.tooling.model.gradle.BuildInvocations", project, true)
+        def model = builder.buildAll("org.gradle.tooling.model.gradle.BuildInvocations", child, true)
 
         then:
-        model.taskSelectors.size() == 3
+        model.taskSelectors*.name as Set == ['t1', 't2', 't3', 't4'] as Set
         model.taskSelectors.each { it ->
             assert it.projectPath == ':'
-            assert it.name != null
-            assert it.displayName != null
-            assert it.description != null
-        }
-        def t1Selector = model.taskSelectors.find { LaunchableGradleTaskSelector it ->
-            it.name == 't1' && it.description.startsWith("t1")
         }
-        model.taskSelectors*.name as Set == ['t1', 't2', 't3'] as Set
     }
+
+    def "TaskSelector description is taken from task that TaskNameComparator considers to be of lowest ordering"() {
+        given:
+        def builder = new BuildInvocationsBuilder(new DefaultProjectTaskLister())
+
+        when:
+        def model = builder.buildAll("org.gradle.tooling.model.gradle.BuildInvocations", project)
+
+        then:
+        assert model.taskSelectors.find { it.name == 't1' }.description == 'T1 from root'
+        assert model.taskSelectors.find { it.name == 't2' }.description == null
+        assert model.taskSelectors.find { it.name == 't3' }.description == 'T3 from child'
+        assert model.taskSelectors.find { it.name == 't4' }.description == 'T4 from grand child 1'
+    }
+
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilderTest.groovy
index f4f72a0..400501c 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilderTest.groovy
@@ -16,13 +16,15 @@
 
 package org.gradle.plugins.ide.internal.tooling
 
+import org.gradle.api.DefaultTask
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.TestUtil
 import org.junit.Rule
 import spock.lang.Specification
 
 class GradleProjectBuilderTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir
+    @Rule
+    TestNameTestDirectoryProvider tmpDir
     def builder = new GradleProjectBuilder()
 
     def "builds basics for project"() {
@@ -37,6 +39,31 @@ class GradleProjectBuilderTest extends Specification {
         model.path == ':'
         model.name == 'test'
         model.description == 'a test project'
+        model.buildDirectory == project.buildDir
         model.buildScript.sourceFile == buildFile
     }
+
+    def "handles task placeholders"() {
+        def buildFile = tmpDir.file("build.gradle") << "//empty"
+        def project = TestUtil.builder().withName("test").withProjectDir(tmpDir.testDirectory).build()
+        project.description = 'a test project'
+        project.tasks.addPlaceholderAction("placeholderTask", DefaultTask) {
+            it.description = "some description"
+            it.group = "some group"
+        }
+
+        when:
+        def model = builder.buildAll(project)
+
+        then:
+        model.path == ':'
+        model.name == 'test'
+        model.description == 'a test project'
+        model.buildDirectory == project.buildDir
+        model.buildScript.sourceFile == buildFile
+        model.tasks.size() == 1
+        model.tasks[0].name == "placeholderTask"
+        model.tasks[0].description == "some description"
+        model.tasks[0].path == ":placeholderTask"
+    }
 }
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/TasksFactoryTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/TasksFactoryTest.groovy
index e55ec83..47d77b0 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/TasksFactoryTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/TasksFactoryTest.groovy
@@ -23,7 +23,7 @@ import spock.lang.Specification
 
 class TasksFactoryTest extends Specification {
     final Project project = Mock()
-    final org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3 eclipseProject = new DefaultEclipseProject(null, null, null, null, [])
+    final def eclipseProject = new DefaultEclipseProject(null, null, null, null, [])
     final task = TestUtil.createTask(AbstractTask)
 
     def "does not return tasks"() {
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRootTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRootTest.groovy
new file mode 100644
index 0000000..f358ce7
--- /dev/null
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/idea/DefaultIdeaContentRootTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugins.ide.internal.tooling.idea
+
+import spock.lang.Specification
+
+class DefaultIdeaContentRootTest extends Specification {
+    def contentRoot = new DefaultIdeaContentRoot()
+
+    def "generated source is a subset of source"() {
+        def dir = Stub(DefaultIdeaSourceDirectory)
+        def generated = Stub(DefaultIdeaSourceDirectory)
+
+        given:
+        generated.generated >> true
+        contentRoot.sourceDirectories = [dir, generated]
+
+        expect:
+        contentRoot.generatedSourceDirectories == [generated] as Set
+    }
+
+    def "generated test source is a subset of test source"() {
+        def dir = Stub(DefaultIdeaSourceDirectory)
+        def generated = Stub(DefaultIdeaSourceDirectory)
+
+        given:
+        generated.generated >> true
+        contentRoot.testDirectories = [dir, generated]
+
+        expect:
+        contentRoot.generatedTestDirectories == [generated] as Set
+    }
+}
diff --git a/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml
index ab84573..3191542 100644
--- a/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml
+++ b/subprojects/ide/src/test/resources/org/gradle/plugins/ide/idea/model/customModule.xml
@@ -7,7 +7,9 @@
         <orderEntry type="jdk" jdkName="1.6" jdkType="JavaSDK" />
         <content url="file://$MODULE_DIR$">
             <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false"/>
+            <sourceFolder url="file://$MODULE_DIR$/generated-src" isTestSource="false" generated="true"/>
             <sourceFolder url="file://$MODULE_DIR$/srcTest" isTestSource="true"/>
+            <sourceFolder url="file://$MODULE_DIR$/generated-test-src" isTestSource="true" generated="true"/>
             <excludeFolder url="file://$MODULE_DIR$/target"/>
         </content>
         <orderEntry type="sourceFolder" forTests="false"/>
diff --git a/subprojects/integ-test/integ-test.gradle b/subprojects/integ-test/integ-test.gradle
index 9056c5f..846accd 100644
--- a/subprojects/integ-test/integ-test.gradle
+++ b/subprojects/integ-test/integ-test.gradle
@@ -3,12 +3,20 @@ dependencies {
 
     integTestCompile project(':toolingApi')
     integTestCompile project(':launcher')
-    integTestCompile project(':coreImpl')
+    integTestCompile project(':dependencyManagement')
+    integTestCompile project(':scala')
+    integTestCompile project(':ear')
+    integTestCompile project(':codeQuality')
+    integTestCompile project(':signing')
+    integTestCompile project(':ide')
+    integTestCompile project(':sonar')
     integTestCompile libraries.ant
     integTestCompile libraries.jsoup
 
     integTestRuntime rootProject.configurations.testRuntime.allDependencies
 }
+useTestFixtures(sourceSet: 'integTest', project: ':diagnostics')
+useTestFixtures(sourceSet: 'integTest', project: ':platformNative')
 
 integTestTasks.all {
     dependsOn({ rootProject.getTasksByName('publishLocalArchives', true) }, ':docs:userguideDocbook')
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
index 6b0d396..250698c 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
@@ -24,6 +24,6 @@ import org.gradle.launcher.Main
  */
 class GradleRunConfiguration {
     public static void main(String[] args) {
-        Main.main(args)
+        new Main().run(args)
     }
 }
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
index 9a583a8..b6b8ce3 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntProjectIntegrationTest.groovy
@@ -219,4 +219,48 @@ ant.importBuild('build.xml')
         // Testing that we don't get some obscure error message trying to set c.shouldRunAfter b
         inTestDirectory().withTasks('a').runWithFailure().assertHasCause("Imported Ant target 'a' depends on target or task 'b' which does not exist")
     }
+
+    @Test
+    public void canApplyJavaPluginWithAntBuild() {
+        testFile('build.xml') << """
+<project>
+    <target name='clean'>
+        <echo message='Executing Ant clean'/>
+    </target>
+    <target name='target2' depends='clean'/>
+    <target name='target1' depends='target2'/>
+</project>
+"""
+        testFile('build.gradle') << """
+apply plugin:'java'
+ant.importBuild(file('build.xml')) { antTaskName ->
+    'ant-'+antTaskName
+}
+
+task ant(dependsOn: 'ant-target1')
+"""
+        inTestDirectory().withTasks('clean', 'ant').run().assertTasksExecuted(':clean', ':ant-clean', ':ant-target2', ':ant-target1', ':ant')
+
+    }
+
+    @Test
+    public void canRenameAntDelegateTask() {
+        testFile('build.xml') << """
+<project>
+    <target name='c'/>
+    <target name='b' depends='c'/>
+    <target name='a' depends='b'/>
+</project>
+"""
+        testFile('build.gradle') << """
+
+ant.importBuild(file('build.xml')) { antTaskName ->
+    antTaskName == 'b' ? 'ant-b' : antTaskName
+}
+
+task runAnt(dependsOn: 'a')
+"""
+        inTestDirectory().withTasks('runAnt').run().assertTasksExecuted(':c', ':ant-b', ':a', ':runAnt')
+
+    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrPluginGoodBehaviourTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrPluginGoodBehaviourTest.groovy
deleted file mode 100644
index 900bef1..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/AntlrPluginGoodBehaviourTest.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.integtests
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-
-class AntlrPluginGoodBehaviourTest extends WellBehavedPluginTest {
-    @Override
-    String getMainTask() {
-        return "build"
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
index 9864d0e..188f7da 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
@@ -14,19 +14,347 @@
  * limitations under the License.
  */
 package org.gradle.integtests
-
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ScriptExecuter
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.TextUtil
+import spock.lang.IgnoreIf
+import spock.lang.Unroll
+
+import static org.hamcrest.Matchers.startsWith
 
-class ApplicationIntegrationSpec extends AbstractIntegrationSpec {
+class ApplicationIntegrationSpec extends AbstractIntegrationSpec{
 
     def setup() {
+        file('settings.gradle') << 'rootProject.name = "application"'
+
         buildFile << """
             apply plugin: 'application'
-            applicationName = 'app'
-            mainClassName = "something"
+            mainClassName = 'org.gradle.test.Main'
         """
     }
 
+    def canUseEnvironmentVariableToPassMultipleOptionsToJvmWhenRunningScript() {
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+        if (!"value".equals(System.getProperty("testValue"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"some value".equals(System.getProperty("testValue2"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"some value".equals(System.getProperty("testValue3"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+    }
+}
+'''
+
+        when:
+        run 'installDist'
+
+        def builder = new ScriptExecuter()
+        builder.workingDir file('build/install/application/bin')
+        builder.executable "application"
+        if (OperatingSystem.current().windows) {
+            builder.environment('APPLICATION_OPTS', '-DtestValue=value -DtestValue2="some value" -DtestValue3="some value"')
+        } else {
+            builder.environment('APPLICATION_OPTS', '-DtestValue=value -DtestValue2=\'some value\' -DtestValue3=some\\ value')
+        }
+
+        def result = builder.run()
+
+        then:
+        result.assertNormalExitValue()
+    }
+
+    def canUseDefaultJvmArgsToPassMultipleOptionsToJvmWhenRunningScript() {
+        file("build.gradle") << '''
+applicationDefaultJvmArgs = ['-DtestValue=value', '-DtestValue2=some value', '-DtestValue3=some value']
+'''
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+        if (!"value".equals(System.getProperty("testValue"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"some value".equals(System.getProperty("testValue2"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"some value".equals(System.getProperty("testValue3"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+    }
+}
+'''
+
+        when:
+        run 'installDist'
+
+        def builder = new ScriptExecuter()
+        builder.workingDir file('build/install/application/bin')
+        builder.executable "application"
+
+        def result = builder.run()
+
+        then:
+        result.assertNormalExitValue()
+    }
+
+    def canUseBothDefaultJvmArgsAndEnvironmentVariableToPassOptionsToJvmWhenRunningScript() {
+        file("build.gradle") << '''
+applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=some value2']
+'''
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+        if (!"value1".equals(System.getProperty("var1"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"some value2".equals(System.getProperty("var2"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+        if (!"value3".equals(System.getProperty("var3"))) {
+            throw new RuntimeException("Expected system property not specified");
+        }
+    }
+}
+'''
+
+        when:
+        run 'installDist'
+
+        def builder = new ScriptExecuter()
+        builder.workingDir file('build/install/application/bin')
+        builder.executable "application"
+        builder.environment('APPLICATION_OPTS', '-Dvar3=value3')
+
+        def result = builder.run()
+
+        then:
+        result.assertNormalExitValue()
+    }
+
+    def canUseDefaultJvmArgsToPassMultipleOptionsWithShellMetacharactersToJvmWhenRunningScript() {
+        //even in single-quoted multi-line strings, backslashes must still be quoted
+        file("build.gradle") << '''
+applicationDefaultJvmArgs = ['-DtestValue=value',
+                             /-DtestValue2=s\\o"me val'ue/ + '$PATH',
+                             /-DtestValue3=so\\"me value%PATH%/,
+                            ]
+'''
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+        if (!"value".equals(System.getProperty("testValue"))) {
+            throw new RuntimeException("Expected system property not specified (testValue)");
+        }
+        if (!"s\\\\o\\"me val'ue$PATH".equals(System.getProperty("testValue2"))) {
+            throw new RuntimeException("Expected system property not specified (testValue2)");
+        }
+        if (!"so\\\\\\"me value%PATH%".equals(System.getProperty("testValue3"))) {
+            throw new RuntimeException("Expected system property not specified (testValue3)");
+        }
+    }
+}
+'''
+
+        when:
+        run 'installDist'
+
+        def builder = new ScriptExecuter()
+        builder.workingDir file('build/install/application/bin')
+        builder.executable "application"
+
+        def result = builder.run()
+
+        then:
+        result.assertNormalExitValue()
+    }
+
+    def canUseDefaultJvmArgsInRunTask() {
+            file("build.gradle") << '''
+    applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=value2']
+    '''
+            file('src/main/java/org/gradle/test/Main.java') << '''
+    package org.gradle.test;
+
+    class Main {
+        public static void main(String[] args) {
+            if (!"value1".equals(System.getProperty("var1"))) {
+                throw new RuntimeException("Expected system property not specified (var1)");
+            }
+            if (!"value2".equals(System.getProperty("var2"))) {
+                throw new RuntimeException("Expected system property not specified (var2)");
+            }
+        }
+    }
+    '''
+
+            expect:
+            run 'run'
+        }
+
+
+    def "can customize application name"() {
+        file('build.gradle') << '''
+applicationName = 'mega-app'
+'''
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+    }
+}
+'''
+
+        when:
+        run 'installDist', 'distZip', 'distTar'
+
+        then:
+        def installDir = file('build/install/mega-app')
+        installDir.assertIsDir()
+        checkApplicationImage('mega-app', installDir)
+
+        def distZipFile = file('build/distributions/mega-app.zip')
+        distZipFile.assertIsFile()
+
+        def distZipDir = file('build/unzip')
+        distZipFile.usingNativeTools().unzipTo(distZipDir)
+        checkApplicationImage('mega-app', distZipDir.file('mega-app'))
+
+        def distTarFile = file('build/distributions/mega-app.tar')
+        distTarFile.assertIsFile()
+
+        def distTarDir = file('build/untar')
+        distTarFile.usingNativeTools().untarTo(distTarDir)
+        checkApplicationImage('mega-app', distTarDir.file('mega-app'))
+    }
+
+    def "check distribution contents when all defaults used"() {
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+    }
+}
+'''
+        // ensure we no duplicate files in default setup (GRADLE-3289)
+        buildFile << """
+        [installDist, distZip, distTar]*.configure {
+            it.duplicatesStrategy = "fail"
+        }
+"""
+        when:
+        run 'installDist', 'distZip', 'distTar'
+
+        then:
+        def installDir = file('build/install/application')
+        installDir.assertIsDir()
+        checkApplicationImage('application', installDir)
+        
+        def distZipFile = file('build/distributions/application.zip')
+        distZipFile.assertIsFile()
+        
+        def distZipDir = file('build/unzip')
+        distZipFile.usingNativeTools().unzipTo(distZipDir)
+        checkApplicationImage('application', distZipDir.file('application'))
+        
+        def distTarFile = file('build/distributions/application.tar')
+        distTarFile.assertIsFile()
+		
+        def distTarDir = file('build/untar')
+        distTarFile.usingNativeTools().untarTo(distTarDir)
+        checkApplicationImage('application', distTarDir.file('application'))
+    }
+
+    @Unroll
+    def "#installTask complains if install directory exists and doesn't look like previous install"() {
+        setup:
+        if(deprecatedTask){
+            executer.withDeprecationChecksDisabled()
+        }
+
+        file('build.gradle') << """
+${installTask}.destinationDir = buildDir
+"""
+        when:
+        runAndFail installTask
+
+        then:
+        result.assertThatCause(startsWith("The specified installation directory '${file('build')}' is neither empty nor does it contain an installation"))
+        where:
+        installTask     | deprecatedTask
+        "installApp"    | true
+        "installDist"   | false
+    }
+
+    def "startScripts respect OS dependent line separators"() {
+        file('build.gradle') << '''
+    installDist.destinationDir = buildDir
+    '''
+
+        when:
+        run 'startScripts'
+
+        then:
+        File generatedWindowsStartScript = file("build/scripts/application.bat")
+        generatedWindowsStartScript.exists()
+        assertLineSeparators(generatedWindowsStartScript, TextUtil.windowsLineSeparator, 90);
+
+        File generatedLinuxStartScript = file("build/scripts/application")
+        generatedLinuxStartScript.exists()
+        assertLineSeparators(generatedLinuxStartScript, TextUtil.unixLineSeparator, 164);
+        assertLineSeparators(generatedLinuxStartScript, TextUtil.windowsLineSeparator, 1)
+
+        file("build/scripts/application").exists()
+    }
+
+    def "application packages are built when running the assemble task"() {
+        file('src/main/java/org/gradle/test/Main.java') << '''
+package org.gradle.test;
+
+class Main {
+    public static void main(String[] args) {
+    }
+}
+'''
+
+        when:
+        run 'assemble'
+
+        then:
+        def distributionsDir = file('build/distributions')
+        distributionsDir.assertIsDir()
+
+        def distZipFile = file('build/distributions/application.zip')
+        distZipFile.assertIsFile()
+
+        def distZipDir = file('build/unzip')
+        distZipFile.usingNativeTools().unzipTo(distZipDir)
+        checkApplicationImage('application', distZipDir.file('application'))
+
+        def distTarFile = file('build/distributions/application.tar')
+        distTarFile.assertIsFile()
+
+        def distTarDir = file('build/untar')
+        distTarFile.usingNativeTools().untarTo(distTarDir)
+        checkApplicationImage('application', distTarDir.file('application'))
+    }
+
     def "conventional resources are including in dist"() {
         when:
         file("src/dist/dir").with {
@@ -35,10 +363,10 @@ class ApplicationIntegrationSpec extends AbstractIntegrationSpec {
         }
 
         then:
-        succeeds "installApp"
+        succeeds "installDist"
 
         and:
-        def distBase = file("build/install/app")
+        def distBase = file("build/install/application")
         distBase.file("dir").directory
         distBase.file("dir/r1.txt").text == "r1"
         distBase.file("dir/r2.txt").text == "r2"
@@ -59,15 +387,16 @@ class ApplicationIntegrationSpec extends AbstractIntegrationSpec {
         """
 
         then:
-        succeeds "installApp"
+        succeeds "installDist"
 
         and:
-        def distBase = file("build/install/app")
+        def distBase = file("build/install/application")
         distBase.file("dir").directory
         !distBase.file("dir/r1.txt").exists()
         distBase.file("dir/r2.txt").text == "r2"
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "distribution file producing tasks are run automatically"() {
         when:
         buildFile << """
@@ -89,16 +418,35 @@ class ApplicationIntegrationSpec extends AbstractIntegrationSpec {
         """
 
         then:
-        succeeds "installApp"
+        succeeds "installDist"
 
         and:
         ":createDocs" in nonSkippedTasks
 
         and:
-        def distBase = file("build/install/app")
+        def distBase = file("build/install/application")
         distBase.file("docs").directory
         !distBase.file("docs/readme.txt").exists()
         distBase.file("docs/READ-ME.txt").text == "Read me!!!"
     }
 
-}
\ No newline at end of file
+    private void checkApplicationImage(String applicationName, TestFile installDir) {
+        installDir.file("bin/${applicationName}").assertIsFile()
+        installDir.file("bin/${applicationName}.bat").assertIsFile()
+        installDir.file("lib/application.jar").assertIsFile()
+        
+        def builder = new ScriptExecuter()
+        builder.workingDir installDir.file('bin')
+        builder.executable applicationName
+        builder.standardOutput = new ByteArrayOutputStream()
+        builder.errorOutput = new ByteArrayOutputStream()
+        
+        def result = builder.run()
+        result.assertNormalExitValue()
+    }
+
+    def assertLineSeparators(TestFile testFile, String lineSeparator, expectedLineCount) {
+        assert testFile.text.split(lineSeparator).length == expectedLineCount
+        true
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy
deleted file mode 100644
index d852b53..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationTest.groovy
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ScriptExecuter
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.TextUtil
-
-import static org.hamcrest.Matchers.startsWith
-
-class ApplicationIntegrationTest extends AbstractIntegrationSpec{
-
-    def canUseEnvironmentVariableToPassMultipleOptionsToJvmWhenRunningScript() {
-        file("build.gradle") << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-applicationName = 'application'
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-        if (!"value".equals(System.getProperty("testValue"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"some value".equals(System.getProperty("testValue2"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"some value".equals(System.getProperty("testValue3"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-    }
-}
-'''
-
-        when:
-        run 'install'
-
-        def builder = new ScriptExecuter()
-        builder.workingDir file('build/install/application/bin')
-        builder.executable "application"
-        if (OperatingSystem.current().windows) {
-            builder.environment('APPLICATION_OPTS', '-DtestValue=value -DtestValue2="some value" -DtestValue3="some value"')
-        } else {
-            builder.environment('APPLICATION_OPTS', '-DtestValue=value -DtestValue2=\'some value\' -DtestValue3=some\\ value')
-        }
-
-        def result = builder.run()
-
-        then:
-        result.assertNormalExitValue()
-    }
-
-    def canUseDefaultJvmArgsToPassMultipleOptionsToJvmWhenRunningScript() {
-        file("build.gradle") << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-applicationName = 'application'
-applicationDefaultJvmArgs = ['-DtestValue=value', '-DtestValue2=some value', '-DtestValue3=some value']
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-        if (!"value".equals(System.getProperty("testValue"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"some value".equals(System.getProperty("testValue2"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"some value".equals(System.getProperty("testValue3"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-    }
-}
-'''
-
-        when:
-        run 'install'
-
-        def builder = new ScriptExecuter()
-        builder.workingDir file('build/install/application/bin')
-        builder.executable "application"
-
-        def result = builder.run()
-
-        then:
-        result.assertNormalExitValue()
-    }
-
-    def canUseBothDefaultJvmArgsAndEnvironmentVariableToPassOptionsToJvmWhenRunningScript() {
-        file("build.gradle") << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-applicationName = 'application'
-applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=some value2']
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-        if (!"value1".equals(System.getProperty("var1"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"some value2".equals(System.getProperty("var2"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-        if (!"value3".equals(System.getProperty("var3"))) {
-            throw new RuntimeException("Expected system property not specified");
-        }
-    }
-}
-'''
-
-        when:
-        run 'install'
-
-        def builder = new ScriptExecuter()
-        builder.workingDir file('build/install/application/bin')
-        builder.executable "application"
-        builder.environment('APPLICATION_OPTS', '-Dvar3=value3')
-
-        def result = builder.run()
-
-        then:
-        result.assertNormalExitValue()
-    }
-
-    def canUseDefaultJvmArgsToPassMultipleOptionsWithShellMetacharactersToJvmWhenRunningScript() {
-        //even in single-quoted multi-line strings, backslashes must still be quoted
-        file("build.gradle") << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-applicationName = 'application'
-applicationDefaultJvmArgs = ['-DtestValue=value',
-                             /-DtestValue2=s\\o"me val'ue/ + '$PATH',
-                             /-DtestValue3=so\\"me value%PATH%/,
-                            ]
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-        if (!"value".equals(System.getProperty("testValue"))) {
-            throw new RuntimeException("Expected system property not specified (testValue)");
-        }
-        if (!"s\\\\o\\"me val'ue$PATH".equals(System.getProperty("testValue2"))) {
-            throw new RuntimeException("Expected system property not specified (testValue2)");
-        }
-        if (!"so\\\\\\"me value%PATH%".equals(System.getProperty("testValue3"))) {
-            throw new RuntimeException("Expected system property not specified (testValue3)");
-        }
-    }
-}
-'''
-
-        when:
-        run 'install'
-
-        def builder = new ScriptExecuter()
-        builder.workingDir file('build/install/application/bin')
-        builder.executable "application"
-
-        def result = builder.run()
-
-        then:
-        result.assertNormalExitValue()
-    }
-
-    def canUseDefaultJvmArgsInRunTask() {
-            file("build.gradle") << '''
-    apply plugin: 'application'
-    mainClassName = 'org.gradle.test.Main'
-    applicationName = 'application'
-    applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=value2']
-    '''
-            file('src/main/java/org/gradle/test/Main.java') << '''
-    package org.gradle.test;
-
-    class Main {
-        public static void main(String[] args) {
-            if (!"value1".equals(System.getProperty("var1"))) {
-                throw new RuntimeException("Expected system property not specified (var1)");
-            }
-            if (!"value2".equals(System.getProperty("var2"))) {
-                throw new RuntimeException("Expected system property not specified (var2)");
-            }
-        }
-    }
-    '''
-
-            expect:
-            run 'run'
-        }
-
-
-    def "can customize application name"() {
-        file('settings.gradle') << 'rootProject.name = "application"'
-        file('build.gradle') << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-applicationName = 'mega-app'
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-    }
-}
-'''
-
-        when:
-        run 'install', 'distZip', 'distTar'
-
-        then:
-        def installDir = file('build/install/mega-app')
-        installDir.assertIsDir()
-        checkApplicationImage('mega-app', installDir)
-
-        def distZipFile = file('build/distributions/mega-app.zip')
-        distZipFile.assertIsFile()
-
-        def distZipDir = file('build/unzip')
-        distZipFile.usingNativeTools().unzipTo(distZipDir)
-        checkApplicationImage('mega-app', distZipDir.file('mega-app'))
-
-        def distTarFile = file('build/distributions/mega-app.tar')
-        distTarFile.assertIsFile()
-
-        def distTarDir = file('build/untar')
-        distTarFile.usingNativeTools().untarTo(distTarDir)
-        checkApplicationImage('mega-app', distTarDir.file('mega-app'))
-    }
-
-    def "check distribution contents when all defaults used"() {
-        file('settings.gradle') << 'rootProject.name = "application"'
-        file('build.gradle') << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-'''
-        file('src/main/java/org/gradle/test/Main.java') << '''
-package org.gradle.test;
-
-class Main {
-    public static void main(String[] args) {
-    }
-}
-'''
-
-        when:
-        run 'install', 'distZip', 'distTar'
-
-        then:
-        def installDir = file('build/install/application')
-        installDir.assertIsDir()
-        checkApplicationImage('application', installDir)
-        
-        def distZipFile = file('build/distributions/application.zip')
-        distZipFile.assertIsFile()
-        
-        def distZipDir = file('build/unzip')
-        distZipFile.usingNativeTools().unzipTo(distZipDir)
-        checkApplicationImage('application', distZipDir.file('application'))
-        
-        def distTarFile = file('build/distributions/application.tar')
-        distTarFile.assertIsFile()
-		
-        def distTarDir = file('build/untar')
-        distTarFile.usingNativeTools().untarTo(distTarDir)
-        checkApplicationImage('application', distTarDir.file('application'))
-    }
-
-    def "installApp complains if install directory exists and doesn't look like previous install"() {
-        file('build.gradle') << '''
-apply plugin: 'application'
-mainClassName = 'org.gradle.test.Main'
-installApp.destinationDir = buildDir
-'''
-
-        when:
-        runAndFail 'installApp'
-
-        then:
-        result.assertThatCause(startsWith("The specified installation directory '${file('build')}' is neither empty nor does it contain an installation"))
-    }
-
-    def "startScripts respect OS dependent line separators"() {
-        file('build.gradle') << '''
-    apply plugin: 'application'
-    applicationName = 'mega-app'
-    mainClassName = 'org.gradle.test.Main'
-    installApp.destinationDir = buildDir
-    '''
-
-        when:
-        run 'startScripts'
-
-        then:
-        File generatedWindowsStartScript = file("build/scripts/mega-app.bat")
-        generatedWindowsStartScript.exists()
-        assertLineSeparators(generatedWindowsStartScript, TextUtil.windowsLineSeparator, 90);
-
-        File generatedLinuxStartScript = file("build/scripts/mega-app")
-        generatedLinuxStartScript.exists()
-        assertLineSeparators(generatedLinuxStartScript, TextUtil.unixLineSeparator, 164);
-        assertLineSeparators(generatedLinuxStartScript, TextUtil.windowsLineSeparator, 1)
-
-        file("build/scripts/mega-app").exists()
-    }
-
-    private void checkApplicationImage(String applicationName, TestFile installDir) {
-        installDir.file("bin/${applicationName}").assertIsFile()
-        installDir.file("bin/${applicationName}.bat").assertIsFile()
-        installDir.file("lib/application.jar").assertIsFile()
-        
-        def builder = new ScriptExecuter()
-        builder.workingDir installDir.file('bin')
-        builder.executable applicationName
-        builder.standardOutput = new ByteArrayOutputStream()
-        builder.errorOutput = new ByteArrayOutputStream()
-        
-        def result = builder.run()
-        result.assertNormalExitValue()
-    }
-
-    def assertLineSeparators(TestFile testFile, String lineSeparator, expectedLineCount) {
-        assert testFile.text.split(lineSeparator).length == expectedLineCount
-        true
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
index 7eaaaaa..c06ce3c 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildAggregationIntegrationTest.groovy
@@ -91,22 +91,39 @@ class BuildAggregationIntegrationTest extends AbstractIntegrationTest {
     }
 
     @Test
-    @Issue("http://issues.gradle.org//browse/GRADLE-3052")
+    @Issue("https://issues.gradle.org//browse/GRADLE-3052")
     void buildTaskCanHaveInputsAndOutputs() {
-        def message = "This is from the hello task"
+        file("input") << "foo"
+        file("settings.gradle") << "rootProject.name = 'proj'"
         file("build.gradle") << """
-            task hello << { println "$message" }
+            class UpperFile extends DefaultTask {
+                @InputFile
+                File input
+
+                @OutputFile
+                File output
+
+                @TaskAction
+                void upper() {
+                  output.text = input.text.toUpperCase()
+                }
+            }
+
+            task upper(type: UpperFile) {
+                input = file("input")
+                output = file("output")
+            }
 
             task build(type: GradleBuild) {
-              tasks = ["hello"]
+              dependsOn upper
+              tasks = ["upper"]
               startParameter.searchUpwards = false
               outputs.file "build.gradle" // having an output (or input) triggers the bug
             }
         """
 
         def run = executer.withTasks("build").run()
-        def output = run.output
-
-        assert output.contains(message)
+        assert run.executedTasks == [":upper", ":build", ":proj:upper"]
+        assert run.skippedTasks == [":proj:upper"].toSet()
     }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index 4dbf474..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.Test
-
-import static org.hamcrest.Matchers.containsString
-import static org.hamcrest.Matchers.not
-import static org.junit.Assert.assertThat
-
-class BuildScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-
-    @Test
-    public void executesBuildScriptWithCorrectEnvironment() {
-        def implClassName = 'com.google.common.collect.Multimap'
-        TestFile buildScript = testFile('build.gradle')
-        buildScript << """
-println 'quiet message'
-logging.captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert project != null
-assert "${buildScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
-assert "${buildScript.toURI()}" == buildscript.sourceURI as String
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-Gradle.class.classLoader.loadClass('${implClassName}')
-try {
-    buildscript.classLoader.loadClass('${implClassName}')
-    assert false: 'should fail'
-} catch (ClassNotFoundException e) {
-    // expected
-}
-
-            task doStuff
-"""
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void buildScriptCanContainATaskDefinition() {
-        testFile('build.gradle') << '''
-            task t(type: SomeTask)
-
-            class SomeTask extends DefaultTask {
-            }
-'''
-
-        inTestDirectory().withTaskList().run()
-    }
-
-    @Test
-    public void buildScriptCanContainOnlyClassDefinitions() {
-        testFile('build.gradle') << '''
-            class TestComparable implements Comparable<TestComparable>, SomeInterface {
-                int compareTo(TestComparable t) {
-                    return 0
-                }
-                void main() { }
-            }
-
-            interface SomeInterface {
-                void main()
-            }
-'''
-
-        inTestDirectory().withTaskList().run()
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildSourceBuilderIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildSourceBuilderIntegrationTest.groovy
index 771497c..0895d55 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildSourceBuilderIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/BuildSourceBuilderIntegrationTest.groovy
@@ -22,7 +22,7 @@ import spock.lang.Issue
 
 class BuildSourceBuilderIntegrationTest extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2032")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2032")
     def "can simultaneously run gradle on projects with buildSrc"() {
         given:
         def buildSrcDir = file("buildSrc").createDir()
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
index 84b1522..ff4a877 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CacheProjectIntegrationTest.groovy
@@ -21,7 +21,7 @@ import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.groovy.scripts.UriScriptSource
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.maven.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.gradle.util.GradleVersion
 import org.junit.Before
@@ -116,19 +116,6 @@ public class CacheProjectIntegrationTest extends AbstractIntegrationTest {
     }
 
     @Test
-    public void "does not rebuild artifact cache when run with --cache rebuild"() {
-        createLargeBuildScript()
-        testBuild("hello1", "Hello 1")
-
-        TestFile dependenciesCache = findDependencyCacheDir()
-        assert dependenciesCache.isDirectory() && dependenciesCache.listFiles().length > 0
-
-        modifyLargeBuildScript()
-        testBuild("newTask", "I am new", "-Crebuild")
-        assert dependenciesCache.isDirectory() && dependenciesCache.listFiles().length > 0
-    }
-
-    @Test
     public void "does not rebuild artifact cache when run with --rerun-tasks"() {
         createLargeBuildScript()
         testBuild("hello1", "Hello 1")
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CharacterEncodingIntegTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CharacterEncodingIntegTest.groovy
deleted file mode 100644
index cbb3694..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CharacterEncodingIntegTest.groovy
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import spock.lang.Unroll
-
-import java.nio.charset.Charset
-
-class CharacterEncodingIntegTest extends AbstractIntegrationSpec {
-
-    def executerEncoding(String inputEncoding) {
-        if (inputEncoding) {
-            executer.withDefaultCharacterEncoding(inputEncoding)
-        }
-    }
-
-    @Unroll("build default encoding matches specified - input = #inputEncoding, expectedEncoding: #expectedEncoding")
-    def "build default encoding matches specified"(String inputEncoding, String expectedEncoding) {
-        given:
-        executerEncoding inputEncoding
-
-        and:
-        buildFile.write """
-            task echoDefaultEncoding {
-                doFirst {
-                    println "default encoding: " + java.nio.charset.Charset.defaultCharset().name()
-                }
-            }
-        """, expectedEncoding
-
-        when:
-        run "echoDefaultEncoding"
-
-        then:
-        output.contains "default encoding: $expectedEncoding"
-
-        where:
-        inputEncoding | expectedEncoding
-        "UTF-8"       | "UTF-8"
-        "US-ASCII"    | "US-ASCII"
-        null          | Charset.defaultCharset().name()
-    }
-
-    @Unroll("forked java processes inherit default encoding - input = #inputEncoding, expectedEncoding: #expectedEncoding")
-    def "forked java processes inherit default encoding"() {
-        given:
-        executerEncoding inputEncoding
-
-        and:
-        file("src", "main", "java").mkdirs()
-        file("src", "main", "java", "EchoEncoding.java").write """
-            package echo;
-
-            import java.nio.charset.Charset;
-
-            public class EchoEncoding {
-                public static void main(String[] args) {
-                    System.out.println("default encoding: " + Charset.defaultCharset().name());
-                }
-            }
-        """, executer.getDefaultCharacterEncoding()
-
-        
-        and:
-        buildFile.write """
-            apply plugin: "java"
-
-            task echoDefaultEncoding(type: JavaExec) {
-                classpath = project.files(compileJava)
-                main "echo.EchoEncoding"
-            }
-        """, expectedEncoding
-
-        when:
-        run "echoDefaultEncoding"
-
-        then:
-        output.contains "default encoding: $expectedEncoding"
-
-        where:
-        inputEncoding | expectedEncoding
-        "UTF-8"       | "UTF-8"
-        "US-ASCII"    | "US-ASCII"
-        null          | Charset.defaultCharset().name()
-    }
-
-
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CustomPluginIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CustomPluginIntegrationTest.groovy
index 57d576c..6950f20 100755
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CustomPluginIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CustomPluginIntegrationTest.groovy
@@ -156,7 +156,7 @@ repositories { mavenCentral() }
 dependencies {
     compile gradleApi()
     compile localGroovy()
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 """
 
@@ -197,7 +197,7 @@ repositories { mavenCentral() }
 dependencies {
     compile gradleApi()
     compile localGroovy()
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 """
 
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
index 56d95e2..7208ca2 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
@@ -32,11 +32,12 @@ class DistributionLocatorIntegrationTest extends Specification {
         urlExist(locator.getDistributionFor(GradleVersion.version("0.8")))
         urlExist(locator.getDistributionFor(GradleVersion.version("0.9.1")))
         urlExist(locator.getDistributionFor(GradleVersion.version("1.0-milestone-3")))
+        urlExist(locator.getDistributionFor(GradleVersion.version("1.12")))
     }
 
     def "locates snapshot versions"() {
         expect:
-        urlExist(locator.getDistributionFor(GradleVersion.version("1.6-20130321190822+0000")))
+        urlExist(locator.getDistributionFor(GradleVersion.version("2.5-20150412220016+0000")))
     }
 
     void urlExist(URI url) {
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
deleted file mode 100755
index 973a556..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ExternalScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.executer.ArtifactBuilder
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.gradle.test.matchers.UserAgentMatcher
-import org.gradle.util.GradleVersion
-import org.junit.Rule
-import org.junit.Test
-
-import static org.hamcrest.Matchers.containsString
-import static org.hamcrest.Matchers.not
-import static org.junit.Assert.assertThat
-
-public class ExternalScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Rule
-    public final HttpServer server = new HttpServer()
-
-    @Test
-    public void executesExternalScriptAgainstAProjectWithCorrectEnvironment() {
-        createExternalJar()
-        createBuildSrc()
-
-        def implClassName = 'com.google.common.collect.Multimap'
-        TestFile externalScript = testFile('external.gradle')
-        externalScript << """
-buildscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-new BuildSrcClass()
-println 'quiet message'
-logging.captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert project != null
-assert "${externalScript.absolutePath.replace("\\", "\\\\")}" == buildscript.sourceFile as String
-assert "${externalScript.toURI()}" == buildscript.sourceURI as String
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-assert project.buildscript.classLoader != buildscript.classLoader
-Gradle.class.classLoader.loadClass('${implClassName}')
-try {
-    buildscript.classLoader.loadClass('${implClassName}')
-    assert false: 'should fail'
-} catch (ClassNotFoundException e) {
-    // expected
-}
-
-task doStuff
-ext.someProp = 'value'
-"""
-        testFile('build.gradle') << '''
-apply { from 'external.gradle' }
-assert 'value' == someProp
-'''
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void canExecuteExternalScriptAgainstAnArbitraryObject() {
-        createBuildSrc()
-
-        testFile('external.gradle') << '''
-println 'quiet message'
-getLogging().captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-new BuildSrcClass()
-assert 'doStuff' == name
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-ext.someProp = 'value'
-'''
-        testFile('build.gradle') << '''
-task doStuff
-apply {
-    to doStuff
-    from 'external.gradle'
-}
-assert 'value' == doStuff.someProp
-'''
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromSettingsScript() {
-        testFile('settings.gradle') << ''' apply { from 'other.gradle' } '''
-        testFile('other.gradle') << ''' include 'child' '''
-        testFile('build.gradle') << ''' assert ['child'] == subprojects*.name '''
-
-        inTestDirectory().withTaskList().run()
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromInitScript() {
-        TestFile initScript = testFile('init.gradle') << ''' apply { from 'other.gradle' } '''
-        testFile('other.gradle') << '''
-addListener(new ListenerImpl())
-class ListenerImpl extends BuildAdapter {
-    public void projectsEvaluated(Gradle gradle) {
-        gradle.rootProject.task('doStuff')
-    }
-}
-'''
-        inTestDirectory().usingInitScript(initScript).withTasks('doStuff').run()
-    }
-
-    @Test
-    public void canExecuteExternalScriptFromExternalScript() {
-        testFile('build.gradle') << ''' apply { from 'other1.gradle' } '''
-        testFile('other1.gradle') << ''' apply { from 'other2.gradle' } '''
-        testFile('other2.gradle') << ''' task doStuff '''
-
-        inTestDirectory().withTasks('doStuff').run()
-    }
-
-    @Test
-    public void canFetchScriptViaHttp() {
-        TestFile script = testFile('external.gradle')
-        server.expectUserAgent(UserAgentMatcher.matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
-        server.expectGet('/external.gradle', script)
-        server.start()
-
-        script << """
-            task doStuff
-            assert buildscript.sourceFile == null
-            assert "http://localhost:$server.port/external.gradle" == buildscript.sourceURI as String
-"""
-
-        testFile('build.gradle') << """
-            apply from: 'http://localhost:$server.port/external.gradle'
-            defaultTasks 'doStuff'
-"""
-
-        inTestDirectory().run()
-    }
-
-    @Test
-    public void cachesScriptClassForAGivenScript() {
-        testFile('settings.gradle') << 'include \'a\', \'b\''
-        testFile('external.gradle') << 'ext.appliedScript = this'
-        testFile('build.gradle') << '''
-allprojects {
-   apply from: "$rootDir/external.gradle"
-}
-subprojects {
-    assert appliedScript.class == rootProject.appliedScript.class
-}
-task doStuff
-'''
-        inTestDirectory().withTasks('doStuff').run()
-    }
-
-    private TestFile createBuildSrc() {
-        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
-            public class BuildSrcClass { }
-'''
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(testFile("repo/test-1.3.jar"))
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
deleted file mode 100644
index 0c075fc..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalBuildIntegrationTest.groovy
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.Rule
-import org.junit.Test
-
-import static org.hamcrest.Matchers.equalTo
-import static org.junit.Assert.assertThat
-
-class IncrementalBuildIntegrationTest extends AbstractIntegrationTest {
-    @Rule public final TestResources resource = new TestResources(testDirectoryProvider)
-
-    @Test
-    public void skipsTaskWhenOutputFileIsUpToDate() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('src.a.txt')
-}
-task b(type: org.gradle.integtests.TransformerTask, dependsOn: a) {
-    inputFile = a.outputFile
-    outputFile = file('src.b.txt')
-}
-'''
-        TestFile inputFile = testFile('src.txt')
-        TestFile outputFileA = testFile('src.a.txt')
-        TestFile outputFileB = testFile('src.b.txt')
-
-        inputFile.text = 'content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        TestFile.Snapshot aSnapshot = outputFileA.snapshot()
-        TestFile.Snapshot bSnapshot = outputFileB.snapshot()
-        assertThat(outputFileA.text, equalTo('[content]'))
-        assertThat(outputFileB.text, equalTo('[[content]]'))
-
-        // No changes
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputFileA.assertHasNotChangedSince(aSnapshot)
-        outputFileB.assertHasNotChangedSince(bSnapshot)
-
-        // Update timestamp, no content changes
-
-        inputFile.setLastModified(inputFile.lastModified() - 10000);
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputFileA.assertHasNotChangedSince(aSnapshot)
-        outputFileB.assertHasNotChangedSince(bSnapshot)
-
-        // Change content
-
-        inputFile.text = 'new content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputFileA.assertHasChangedSince(aSnapshot)
-        outputFileB.assertHasChangedSince(bSnapshot)
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Delete intermediate output file
-
-        outputFileA.delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Delete final output file
-
-        outputFileB.delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        assertThat(outputFileA.text, equalTo('[new content]'))
-        assertThat(outputFileB.text, equalTo('[[new content]]'))
-
-        // Change build file in a way which does not affect the task
-
-        testFile('build.gradle').text += '''
-task c
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        // Change an input property of the first task (the content format)
-
-        testFile('build.gradle').text += '''
-a.format = ' %s '
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        assertThat(outputFileA.text, equalTo(' new content '))
-        assertThat(outputFileB.text, equalTo('[ new content ]'))
-
-        // Change final output file destination
-
-        testFile('build.gradle').text += '''
-b.outputFile = file('new-output.txt')
-'''
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-        outputFileB = testFile('new-output.txt')
-        outputFileB.assertIsFile()
-
-        // Run with --no-opt command-line options
-        inTestDirectory().withTasks('b').withArguments('--no-opt').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        // Output files already exist before using this version of Gradle
-        // delete .gradle dir to simulate this
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputFileB.delete()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void skipsTaskWhenOutputDirContentsAreUpToDate() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src')
-    outputDir = file('build/a')
-}
-task b(type: org.gradle.integtests.DirTransformerTask, dependsOn: a) {
-    inputDir = a.outputDir
-    outputDir = file('build/b')
-}
-'''
-
-        testFile('src').createDir()
-        testFile('src/file1.txt').write('content')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        TestFile outputAFile = testFile('build/a/file1.txt')
-        TestFile outputBFile = testFile('build/b/file1.txt')
-        TestFile.Snapshot aSnapshot = outputAFile.snapshot()
-        TestFile.Snapshot bSnapshot = outputBFile.snapshot()
-
-        outputAFile.assertContents(equalTo('[content]'))
-        outputBFile.assertContents(equalTo('[[content]]'))
-
-        // No changes
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        outputAFile.assertHasNotChangedSince(aSnapshot)
-        outputBFile.assertHasNotChangedSince(bSnapshot)
-
-        // Change content
-
-        testFile('src/file1.txt').write('new content')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        outputAFile.assertHasChangedSince(aSnapshot)
-        outputBFile.assertHasChangedSince(bSnapshot)
-        outputAFile.assertContents(equalTo('[new content]'))
-        outputBFile.assertContents(equalTo('[[new content]]'))
-
-        // Add file
-
-        testFile('src/file2.txt').write('content2')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build/a/file2.txt').assertContents(equalTo('[content2]'))
-        testFile('build/b/file2.txt').assertContents(equalTo('[[content2]]'))
-
-        // Remove file
-
-        testFile('src/file1.txt').delete()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        // Output files already exist before using this version of Gradle
-        // delete .gradle dir to simulate this
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build/b').deleteDir()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void skipsTaskWhenInputPropertiesHaveNotChanged() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.GeneratorTask) {
-    text = project.text
-    outputFile = file('dest.txt')
-}
-'''
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped()
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=text').run().assertTasksExecuted(':a').assertTasksSkipped(':a')
-
-        inTestDirectory().withTasks('a').withArguments('-Ptext=newtext').run().assertTasksExecuted(':a').assertTasksSkipped()
-    }
-
-    @Test
-    public void multipleTasksCanGenerateIntoOverlappingOutputDirectories() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src/a')
-    outputDir = file('build')
-}
-task b(type: org.gradle.integtests.DirTransformerTask) {
-    inputDir = file('src/b')
-    outputDir = file('build')
-}
-'''
-
-        testFile('src/a/file1.txt') << 'content'
-        testFile('src/b/file2.txt') << 'content'
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        // No changes
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-
-        // Delete an output file
-
-        testFile('build/file1.txt').delete()
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':b')
-
-        // Change an output file
-
-        testFile('build/file2.txt').write('something else')
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a')
-
-        // Change to new version of Gradle
-        // Simulate this by removing the .gradle dir
-        testFile('.gradle').assertIsDir().deleteDir()
-
-        inTestDirectory().withTasks('a', 'b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-
-        testFile('build').deleteDir()
-
-        inTestDirectory().withTasks('a').run().assertTasksExecuted(':a').assertTasksSkipped()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':b').assertTasksSkipped()
-    }
-
-    @Test
-    public void canUseUpToDatePredicateToForceTaskToExecute() {
-        testFile('build.gradle') << '''
-task inputsAndOutputs {
-    inputs.files 'src.txt'
-    outputs.files 'src.a.txt'
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst {
-        outputs.files.singleFile.text = "[${inputs.files.singleFile.text}]"
-    }
-}
-task noOutputs {
-    inputs.files 'src.txt'
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst { }
-}
-task nothing {
-    outputs.upToDateWhen { project.hasProperty('uptodate') }
-    doFirst { }
-}
-'''
-        TestFile srcFile = testFile('src.txt')
-        srcFile.text = 'content'
-
-        // Task with input files, output files and a predicate
-        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped(':inputsAndOutputs')
-
-        // Changed input file
-        srcFile.text = 'different'
-        inTestDirectory().withArguments('-Puptodate').withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Predicate is false
-        inTestDirectory().withTasks('inputsAndOutputs').run().assertTasksExecuted(':inputsAndOutputs').assertTasksSkipped()
-
-        // Task with input files and a predicate
-        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped(':noOutputs')
-
-        // Changed input file
-        srcFile.text = 'different again'
-        inTestDirectory().withArguments('-Puptodate').withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Predicate is false
-        inTestDirectory().withTasks('noOutputs').run().assertTasksExecuted(':noOutputs').assertTasksSkipped()
-
-        // Task a predicate only
-        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
-
-        // Is up to date
-        inTestDirectory().withArguments('-Puptodate').withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped(':nothing')
-
-        // Predicate is false
-        inTestDirectory().withTasks('nothing').run().assertTasksExecuted(':nothing').assertTasksSkipped()
-    }
-
-    @Test
-    public void lifecycleTaskIsUpToDateWhenAllDependenciesAreSkipped() {
-        testFile('build.gradle') << '''
-task a(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('out.txt')
-}
-task b(dependsOn: a)
-'''
-
-        testFile('src.txt').text = 'content'
-
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped()
-        inTestDirectory().withTasks('b').run().assertTasksExecuted(':a', ':b').assertTasksSkipped(':a', ':b')
-    }
-
-    @Test
-    public void canShareArtifactsBetweenBuilds() {
-        def buildFile = testFile('build.gradle') << '''
-task otherBuild(type: GradleBuild) {
-    buildFile = 'build.gradle'
-    tasks = ['generate']
-    startParameter.searchUpwards = false
-}
-task transform(type: org.gradle.integtests.TransformerTask) {
-    dependsOn otherBuild
-    inputFile = file('generated.txt')
-    outputFile = file('out.txt')
-}
-task generate(type: org.gradle.integtests.TransformerTask) {
-    inputFile = file('src.txt')
-    outputFile = file('generated.txt')
-}
-'''
-        testFile('settings.gradle') << 'rootProject.name = "build"'
-        testFile('src.txt').text = 'content'
-
-        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped()
-        usingBuildFile(buildFile).withTasks('transform').run().assertTasksExecuted(':otherBuild', ':build:generate', ':transform').assertTasksSkipped(':transform', ':build:generate')
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalTasksIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalTasksIntegrationTest.groovy
deleted file mode 100644
index 036cdde..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/IncrementalTasksIntegrationTest.groovy
+++ /dev/null
@@ -1,343 +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.integtests
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class IncrementalTasksIntegrationTest extends AbstractIntegrationSpec {
-    def "setup"() {
-        buildFile << buildFileBase
-        buildFile << """
-    task incremental(type: IncrementalTask) {
-        inputDir = project.mkdir('inputs')
-        outputDir = project.mkdir('outputs')
-        prop = 'foo'
-    }
-"""
-        file('inputs/file0.txt') << "inputFile0"
-        file('inputs/file1.txt') << "inputFile1"
-        file('inputs/file2.txt') << "inputFile2"
-
-        file('outputs/file1.txt') << "outputFile1"
-        file('outputs/file2.txt') << "outputFile2"
-    }
-
-    private static String getBuildFileBase() {
-        """
-    class BaseIncrementalTask extends DefaultTask {
-        @InputDirectory
-        def File inputDir
-
-        @TaskAction
-        void execute(IncrementalTaskInputs inputs) {
-            assert !(inputs instanceof ExtensionAware)
-
-            if (project.hasProperty('forceFail')) {
-                throw new RuntimeException('failed')
-            }
-
-            incrementalExecution = inputs.incremental
-
-            inputs.outOfDate { change ->
-                if (change.added) {
-                    addedFiles << change.file
-                } else {
-                    changedFiles << change.file
-                }
-            }
-
-            inputs.removed { change ->
-                removedFiles << change.file
-            }
-
-            touchOutputs()
-        }
-
-        def touchOutputs() {
-        }
-
-        def addedFiles = []
-        def changedFiles = []
-        def removedFiles = []
-        def incrementalExecution
-    }
-
-    class IncrementalTask extends BaseIncrementalTask {
-        @Input
-        def String prop
-
-        @OutputDirectory
-        def File outputDir
-
-        @Override
-        def touchOutputs() {
-            outputDir.eachFile {
-                it << "more content"
-            }
-        }
-    }
-
-    ext {
-        incrementalExecution = true
-        added = []
-        changed = []
-        removed = []
-    }
-
-    task incrementalCheck(dependsOn: "incremental") << {
-        assert incremental.incrementalExecution == project.ext.incrementalExecution
-        assert incremental.addedFiles.collect({ it.name }).sort() == project.ext.added
-        assert incremental.changedFiles.collect({ it.name }).sort() == project.ext.changed
-        assert incremental.removedFiles.collect({ it.name }).sort() == project.ext.removed
-    }
-"""
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when run for the first time"() {
-        expect:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is skipped when run with no changes since last execution"() {
-        given:
-        previousExecution()
-
-        when:
-        run "incremental"
-
-        then:
-        ":incremental" in skippedTasks
-    }
-
-    def "incremental task is informed of 'out-of-date' files when input file modified"() {
-        given:
-        previousExecution()
-
-        when:
-        file('inputs/file1.txt') << "changed content"
-
-        then:
-        executesWithIncrementalContext("ext.changed = ['file1.txt']");
-    }
-
-    def "incremental task is informed of 'out-of-date' files when input file added"() {
-        given:
-        previousExecution()
-
-        when:
-        file('inputs/file3.txt') << "file3 content"
-
-        then:
-        executesWithIncrementalContext("ext.added = ['file3.txt']")
-    }
-
-    def "incremental task is informed of 'out-of-date' files when input file removed"() {
-        given:
-        previousExecution()
-
-        when:
-        file('inputs/file2.txt').delete()
-
-        then:
-        executesWithIncrementalContext("ext.removed = ['file2.txt']")
-    }
-
-    def "incremental task is informed of 'out-of-date' files when all input files removed"() {
-        given:
-        previousExecution()
-
-        when:
-        file('inputs/file1.txt').delete()
-        file('inputs/file2.txt').delete()
-
-        then:
-        executesWithIncrementalContext("ext.removed = ['file1.txt', 'file2.txt']")
-    }
-
-    def "incremental task is informed of 'out-of-date' files with added, removed and modified files"() {
-        given:
-        previousExecution()
-
-        when:
-        file('inputs/file1.txt') << "changed content"
-        file('inputs/file2.txt').delete()
-        file('inputs/file3.txt') << "new file 3"
-        file('inputs/file4.txt') << "new file 4"
-
-        then:
-        executesWithIncrementalContext("""
-ext.changed = ['file1.txt']
-ext.removed = ['file2.txt']
-ext.added = ['file3.txt', 'file4.txt']
-""")
-    }
-
-    def "incremental task is informed of 'out-of-date' files when task has no declared outputs or properties"() {
-        given:
-        buildFile.text = buildFileBase
-        buildFile << """
-    task incremental(type: BaseIncrementalTask) {
-        inputDir = project.mkdir('inputs')
-    }
-"""
-        and:
-        previousExecution()
-
-        when:
-        file('inputs/file3.txt') << "file3 content"
-
-        then:
-        executesWithIncrementalContext("ext.added = ['file3.txt']")
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when input property has changed"() {
-        given:
-        previousExecution()
-
-        when:
-        buildFile << "incremental.prop = 'changed'"
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when task class has changed"() {
-        given:
-        previousExecution()
-
-        when:
-        buildFile.text = buildFileBase
-        buildFile << """
-    class IncrementalTask2 extends BaseIncrementalTask {}
-    task incremental(type: IncrementalTask2) {
-        inputDir = project.mkdir('inputs')
-    }
-"""
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when output directory is changed"() {
-        given:
-        previousExecution()
-
-        when:
-        buildFile << "incremental.outputDir = project.mkdir('new-outputs')"
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when output file has changed"() {
-        given:
-        previousExecution()
-
-        when:
-        file("outputs/file1.txt") << "further change"
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when output file has been removed"() {
-        given:
-        previousExecution()
-
-        when:
-        file("outputs/file1.txt").delete()
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when all output files have been removed"() {
-        given:
-        previousExecution()
-
-        when:
-        file("outputs").deleteDir()
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when Task.upToDate() is false"() {
-        given:
-        previousExecution()
-
-        when:
-        buildFile << "incremental.outputs.upToDateWhen { false }"
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed that all input files are 'out-of-date' when gradle is executed with --rerun-tasks"() {
-        given:
-        previousExecution()
-
-        when:
-        executer.withArgument("--rerun-tasks")
-
-        then:
-        executesWithRebuildContext()
-    }
-
-    def "incremental task is informed of 'out-of-date' files since previous successful execution"() {
-        given:
-        previousExecution()
-
-        and:
-        file('inputs/file1.txt') << "changed content"
-
-        when:
-        failedExecution()
-
-        then:
-        executesWithIncrementalContext("ext.changed = ['file1.txt']");
-    }
-    /*
-     7. Sad-day cases
-         - Incremental task has input files declared
-         - Incremental task action throws exception
-         - Incremental task action processes outOfDate files multiple times
-         - Attempt to process removed files without first processing outOfDate files
-     */
-
-    def previousExecution() {
-        run "incremental"
-    }
-
-    def failedExecution() {
-        executer.withArgument("-PforceFail=yep")
-        assert fails("incremental")
-        executer.withArguments()
-    }
-
-    def executesWithIncrementalContext(String fileChanges) {
-        buildFile << fileChanges
-        succeeds "incrementalCheck"
-    }
-
-    def executesWithRebuildContext() {
-        buildFile << """
-    ext.changed = ['file0.txt', 'file1.txt', 'file2.txt']
-    ext.incrementalExecution = false
-"""
-        succeeds "incrementalCheck"
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index 9605e00..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/InitScriptExecutionIntegrationTest.groovy
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.executer.ArtifactBuilder
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.test.fixtures.file.TestFile
-
-class InitScriptExecutionIntegrationTest extends AbstractIntegrationSpec {
-    def "executes init.gradle from user home dir"() {
-        given:
-        executer.requireOwnGradleUserHomeDir()
-
-        and:
-        executer.gradleUserHomeDir.file('init.gradle') << 'println "greetings from user home"'
-
-        when:
-        run()
-
-        then:
-        output.contains("greetings from user home")
-    }
-
-    def "executes init scripts from init.d directory in user home dir in alphabetical order"() {
-        given:
-        executer.requireOwnGradleUserHomeDir()
-
-        and:
-        executer.gradleUserHomeDir.file('init.d/a.gradle') << 'println "init #a#"'
-        executer.gradleUserHomeDir.file('init.d/b.gradle') << 'println "init #b#"'
-        executer.gradleUserHomeDir.file('init.d/c.gradle') << 'println "init #c#"'
-
-        when:
-        run()
-
-        then:
-        def a = output.indexOf('init #a#')
-        def b = output.indexOf('init #b#')
-        def c = output.indexOf('init #c#')
-        a < b
-        b < c
-    }
-
-    def "executes init script with correct environment"() {
-        given:
-        def implClassName = 'com.google.common.collect.Multimap'
-        createExternalJar();
-
-        and:
-        TestFile initScript = file('init.gradle')
-        initScript << """
-initscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-println 'quiet message'
-logging.captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert gradle != null
-assert initscript.classLoader == getClass().classLoader.parent
-assert initscript.classLoader == Thread.currentThread().contextClassLoader
-Gradle.class.classLoader.loadClass('${implClassName}')
-try {
-    initscript.classLoader.loadClass('${implClassName}')
-    assert false: 'should fail'
-} catch (ClassNotFoundException e) {
-    // expected
-}
-"""
-
-        and:
-        buildFile << 'task doStuff'
-
-        when:
-        ExecutionResult result = executer.usingInitScript(initScript).withTasks('doStuff').run()
-
-        then:
-        result.output.contains('quiet message')
-        !result.output.contains('error message')
-        result.error.contains('error message')
-        !result.error.contains('quiet message')
-    }
-
-    def "each init script has independent ClassLoader"() {
-        given:
-        createExternalJar()
-
-        and:
-        TestFile initScript1 = file('init1.gradle')
-        initScript1 << '''
-initscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-'''
-        TestFile initScript2 = file('init2.gradle')
-        initScript2 << '''
-try {
-    Class.forName('org.gradle.test.BuildClass')
-    fail()
-} catch (ClassNotFoundException e) {
-}
-'''
-
-        buildFile << 'task doStuff'
-
-        when:
-        executer.usingInitScript(initScript1).usingInitScript(initScript2)
-
-        then:
-        notThrown(Throwable)
-    }
-
-    def "init script can inject configuration into the root project and all projects"() {
-        given:
-        settingsFile << "include 'a', 'b'"
-
-        and:
-        file("init.gradle") << """
-allprojects {
-    task worker
-}
-rootProject {
-    task root(dependsOn: allprojects*.worker)
-}
-        """
-
-        when:
-        executer.withArguments("-I", "init.gradle")
-        run "root"
-
-        then:
-        result.assertTasksExecuted(':worker', ':a:worker', ':b:worker', ':root')
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(file("repo/test-1.3.jar"))
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MavenPluginGoodBehaviourTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MavenPluginGoodBehaviourTest.groovy
index 8db397f..328f558 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MavenPluginGoodBehaviourTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MavenPluginGoodBehaviourTest.groovy
@@ -18,8 +18,4 @@ package org.gradle.integtests
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
 
 class MavenPluginGoodBehaviourTest extends WellBehavedPluginTest {
-    @Override
-    String getMainTask() {
-        return "assemble"
-    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedLegacyAndComponentJvmPluginIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedLegacyAndComponentJvmPluginIntegrationTest.groovy
new file mode 100644
index 0000000..30bd7e6
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedLegacyAndComponentJvmPluginIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+public class MixedLegacyAndComponentJvmPluginIntegrationTest extends AbstractIntegrationSpec {
+
+    def "can combine legacy java and jvm-component plugins in a single project"() {
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "jvm-component"
+            apply plugin: "java-lang"
+
+            model {
+                components {
+                    jvmLib(JvmLibrarySpec)
+                }
+            }
+
+            task checkModel << {
+                assert componentSpecs.size() == 1
+                assert componentSpecs.jvmLib instanceof JvmLibrarySpec
+
+                assert binaries.size() == 3
+                assert binaries.jvmLibJar instanceof JarBinarySpec
+                assert binaries.mainClasses instanceof ClassDirectoryBinarySpec
+                assert binaries.testClasses instanceof ClassDirectoryBinarySpec
+            }
+"""
+        expect:
+        succeeds "checkModel"
+    }
+
+    def "can build legacy java and jvm-component plugins in a single project"() {
+        given:
+        file("src/main/java/org/gradle/test/Legacy.java") << """
+    package org.gradle.test;
+    interface Legacy {}
+"""
+        file("src/main/resources/legacy.txt") << "Resource for the legacy component"
+
+        and:
+        file("src/jvmLib/java/org/gradle/test/Component.java") << """
+    package org.gradle.test;
+    interface Component {}
+"""
+        file("src/jvmLib/resources/component.txt") << "Resource for the jvm component"
+
+        when:
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+            apply plugin: "java"
+            apply plugin: "jvm-component"
+            apply plugin: "java-lang"
+
+            model {
+                components {
+                    jvmLib(JvmLibrarySpec)
+                }
+            }
+"""
+
+        and:
+        succeeds "assemble"
+
+        then:
+        executed ':compileJava', ':processResources', ':classes', ':jar',
+                ':compileJvmLibJarJvmLibJava', ':processJvmLibJarJvmLibResources', ':createJvmLibJar', ':jvmLibJar'
+
+        and:
+        new JarTestFixture(file("build/jars/jvmLibJar/jvmLib.jar")).hasDescendants("org/gradle/test/Component.class", "component.txt");
+        new JarTestFixture(file("build/libs/test.jar")).hasDescendants("org/gradle/test/Legacy.class", "legacy.txt");
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedNativeAndJvmProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedNativeAndJvmProjectIntegrationTest.groovy
index d53e6e7..b352a94 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedNativeAndJvmProjectIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MixedNativeAndJvmProjectIntegrationTest.groovy
@@ -15,27 +15,129 @@
  */
 
 package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec;
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.archive.JarTestFixture
 
 public class MixedNativeAndJvmProjectIntegrationTest extends AbstractIntegrationSpec {
 
-    def "can combine java, cpp-exe and cpp-lib plugins in a single project"() {
+    def "can combine legacy java and cpp plugins in a single project"() {
         settingsFile << "rootProject.name = 'test'"
         buildFile << """
-            apply plugin: "java"
-            apply plugin: "cpp"
+plugins {
+    id 'java'
+    id 'cpp'
+}
 
-            executables { main {} }
-            libraries { main {} }
+model {
+    components {
+        mainExe(NativeExecutableSpec)
+        mainLib(NativeLibrarySpec)
+    }
+}
 
-            task checkBinaries << {
-                assert binaries.mainClasses instanceof ClassDirectoryBinary
-                assert binaries.mainExecutable instanceof ExecutableBinary
-                assert binaries.mainSharedLibrary instanceof SharedLibraryBinary
-            }
+task checkBinaries << {
+    assert binaries.mainClasses instanceof ClassDirectoryBinarySpec
+    assert binaries.mainExeExecutable instanceof NativeExecutableBinarySpec
+    assert binaries.mainLibSharedLibrary instanceof SharedLibraryBinarySpec
+}
 """
         expect:
         succeeds "checkBinaries"
     }
+
+    def "can combine jvm and native components in the same project"() {
+        buildFile << """
+plugins {
+    id 'native-component'
+    id 'jvm-component'
+}
+
+model {
+    components {
+        nativeExe(NativeExecutableSpec)
+        nativeLib(NativeLibrarySpec)
+        jvmLib(JvmLibrarySpec)
+    }
+}
+
+task validate << {
+    assert componentSpecs.size() == 3
+    assert componentSpecs.nativeExe instanceof NativeExecutableSpec
+    assert componentSpecs.nativeLib instanceof NativeLibrarySpec
+    assert componentSpecs.jvmLib instanceof JvmLibrarySpec
+
+    assert binaries.size() == 4
+    assert binaries.jvmLibJar instanceof JarBinarySpec
+    assert binaries.nativeExeExecutable instanceof NativeExecutableBinarySpec
+    assert binaries.nativeLibStaticLibrary instanceof StaticLibraryBinarySpec
+    assert binaries.nativeLibSharedLibrary instanceof SharedLibraryBinarySpec
+}
+"""
+        expect:
+        succeeds "validate"
+    }
+
+    def "build mixed components in one project"() {
+        given:
+        file("src/jvmLib/java/org/gradle/test/Test.java") << """
+package org.gradle.test;
+
+class Test {
+    int val = 4;
+    String name = "foo";
+}
+"""
+        file("src/jvmLib/resources/test.txt") << "Here is a test resource"
+
+        file("src/nativeApp/c/main.c") << """
+#include <stdio.h>
+
+int main () {
+    printf("Hello world!");
+    return 0;
+}
+"""
+
+        and:
+        buildFile << """
+plugins {
+    id 'native-component'
+    id 'c'
+    id 'jvm-component'
+    id 'java-lang'
+}
+
+model {
+    components {
+        nativeApp(NativeExecutableSpec)
+        jvmLib(JvmLibrarySpec)
+    }
+}
+"""
+        when:
+        succeeds "jvmLibJar"
+
+        then:
+        executedAndNotSkipped ":compileJvmLibJarJvmLibJava", ":processJvmLibJarJvmLibResources", ":createJvmLibJar", ":jvmLibJar"
+        notExecuted  ":nativeAppExecutable"
+
+        when:
+        succeeds  "nativeAppExecutable"
+
+        then:
+        executed ":compileNativeAppExecutableNativeAppC", ":linkNativeAppExecutable", ":nativeAppExecutable"
+        notExecuted ":jvmLibJar"
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executed ":jvmLibJar", ":nativeAppExecutable"
+
+        and:
+        new JarTestFixture(file("build/jars/jvmLibJar/jvmLib.jar")).hasDescendants("org/gradle/test/Test.class", "test.txt");
+        def nativeExeName = OperatingSystem.current().getExecutableName("nativeApp")
+        file("build/binaries/nativeAppExecutable/${nativeExeName}").assertExists()
+    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelProjectExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelProjectExecutionIntegrationTest.groovy
index 513b9a0..ffa00d1 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelProjectExecutionIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelProjectExecutionIntegrationTest.groovy
@@ -46,7 +46,8 @@ allprojects {
     }
 }
 """
-        executer.withArgument('--parallel-threads=3') // needs to be set to the maximum number of expectConcurrentExecution() calls
+        executer.withArgument('--parallel')
+        executer.withArgument('--max-workers=3') // needs to be set to the maximum number of expectConcurrentExecution() calls
         executer.withArgument('--info')
     }
 
@@ -55,7 +56,7 @@ allprojects {
 
         expect:
         blockingServer.expectConcurrentExecution(':b:pingServer', ':c:pingServer', ':d:pingServer')
-        blockingServer.expectConcurrentExecution(':a:pingServer')
+        blockingServer.expectSerialExecution(':a:pingServer')
 
         run ':a:pingServer'
     }
@@ -67,9 +68,9 @@ allprojects {
         projectDependency from: 'c', to: ['d']
 
         expect:
-        blockingServer.expectConcurrentExecution(':d:pingServer')
+        blockingServer.expectSerialExecution(':d:pingServer')
         blockingServer.expectConcurrentExecution(':b:pingServer', ':c:pingServer')
-        blockingServer.expectConcurrentExecution(':a:pingServer')
+        blockingServer.expectSerialExecution(':a:pingServer')
 
         run ':a:pingServer'
     }
@@ -98,8 +99,8 @@ allprojects {
         expect:
         //project a and b are both executed even though alphabetically more important task is blocked
         blockingServer.expectConcurrentExecution(':b:pingB', ':a:pingA')
-        blockingServer.expectConcurrentExecution(':b:pingA')
-        blockingServer.expectConcurrentExecution(':b:pingC')
+        blockingServer.expectSerialExecution(':b:pingA')
+        blockingServer.expectSerialExecution(':b:pingC')
 
         run 'b:pingC'
     }
@@ -112,7 +113,7 @@ allprojects {
 
         expect:
         blockingServer.expectConcurrentExecution(':a:pingA', ':b:pingA')
-        blockingServer.expectConcurrentExecution(':b:pingB')
+        blockingServer.expectSerialExecution(':b:pingB')
 
         run 'a:pingA', 'b:pingB'
     }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelTaskExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelTaskExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..1ebede2
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelTaskExecutionIntegrationTest.groovy
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests
+
+import org.gradle.execution.taskgraph.DefaultTaskExecutionPlan
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.test.fixtures.server.http.BlockingHttpServer
+import org.junit.Rule
+import spock.lang.IgnoreIf
+import spock.util.concurrent.PollingConditions
+
+ at IgnoreIf({ GradleContextualExecuter.parallel })
+// no point, always runs in parallel
+class ParallelTaskExecutionIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule
+    public final BlockingHttpServer blockingServer = new BlockingHttpServer()
+
+    def setup() {
+        executer.withArgument("-D${DefaultTaskExecutionPlan.INTRA_PROJECT_TOGGLE}=true")
+        blockingServer.start()
+
+        settingsFile << 'include "a", "b"'
+
+        buildFile << """
+            assert gradle.startParameter.parallelThreadCount != 0
+
+            class SerialPing extends DefaultTask {
+                @TaskAction
+                void ping() {
+                    URL url = new URL("http://localhost:${blockingServer.port}/" + path)
+                    url.openConnection().getHeaderField('RESPONSE')
+                }
+            }
+
+            @ParallelizableTask
+            class Ping extends SerialPing {
+            }
+
+            @ParallelizableTask
+            class FailingPing extends DefaultTask {
+                @TaskAction
+                void ping() {
+                    URL url = new URL("http://localhost:${blockingServer.port}/" + path)
+                    url.openConnection().getHeaderField('RESPONSE')
+                    throw new RuntimeException("task failure")
+                }
+            }
+
+            allprojects {
+                tasks.addRule("<>Ping") { String name ->
+                    if (name.endsWith("Ping") && name.size() == 5) {
+                        tasks.create(name, Ping)
+                    }
+                }
+                tasks.addRule("<>FailingPing") { String name ->
+                    if (name.endsWith("FailingPing")) {
+                        tasks.create(name, FailingPing)
+                    }
+                }
+                tasks.addRule("<>SerialPing") { String name ->
+                    if (name.endsWith("SerialPing")) {
+                        tasks.create(name, SerialPing)
+                    }
+                }
+            }
+        """
+        executer.withArgument('--info')
+    }
+
+    void withParallelThreads(int threadCount) {
+        executer.withArgument("--parallel")
+        executer.withArgument("--max-workers=$threadCount")
+    }
+
+    def "feature toggle is required for tasks to run in parallel"() {
+        given:
+        executer.withArguments() // clears what was set in setup
+        withParallelThreads(2)
+
+        expect:
+        blockingServer.expectSerialExecution(":aPing")
+        blockingServer.expectSerialExecution(":bPing")
+
+        run ":aPing", ":bPing"
+    }
+
+    def "info is logged when overlapping outputs prevent parallel execution"() {
+        given:
+        executer.withArgument("-i")
+        withParallelThreads(2)
+
+        and:
+        buildFile << """
+            aPing.outputs.file "dir"
+            bPing.outputs.file "dir/file"
+        """
+        expect:
+        GradleHandle handle
+        def handleStarter = {  handle = executer.withTasks(":aPing", ":bPing").start() }
+        blockingServer.expectConcurrentExecution([":aPing"]) {
+            new PollingConditions().eventually {
+                assert handle.standardOutput.contains("Cannot execute task :bPing in parallel with task :aPing due to overlapping output: ${file("dir")}")
+            }
+        }
+        blockingServer.expectSerialExecution(":bPing")
+        handleStarter.call()
+        handle.waitForFinish()
+    }
+
+    def "info is logged when task is prevented from executing in parallel due to custom actions"() {
+        given:
+        executer.withArgument("-i")
+        withParallelThreads(2)
+
+        and:
+        buildFile << """
+            bPing.doLast {}
+        """
+        expect:
+        GradleHandle handle
+        def handleStarter = {  handle = executer.withTasks(":aPing", ":bPing").start() }
+        blockingServer.expectConcurrentExecution([":aPing"]) {
+            new PollingConditions().eventually {
+                assert handle.standardOutput.contains("Unable to parallelize task :bPing due to presence of custom actions (e.g. doFirst()/doLast())")
+            }
+        }
+        blockingServer.expectSerialExecution(":bPing")
+        handleStarter.call()
+        handle.waitForFinish()
+    }
+
+    def "two independent parallelizable tasks execute in parallel"() {
+        given:
+        withParallelThreads(2)
+
+        expect:
+        blockingServer.expectConcurrentExecution(":aPing", ":bPing")
+
+        run ":aPing", ":bPing"
+    }
+
+    def "independent parallelizable tasks from multiple projects execute in parallel"() {
+        given:
+        withParallelThreads(3)
+
+        expect:
+        blockingServer.expectConcurrentExecution(":a:aPing", ":a:bPing", ":b:aPing")
+
+        run ":a:aPing", ":a:bPing", ":b:aPing"
+    }
+
+    def "two parallelizable tasks with should run after execute in parallel"() {
+        given:
+        withParallelThreads(2)
+
+        when:
+        buildFile << """
+            bPing.shouldRunAfter aPing
+        """
+
+        then:
+        blockingServer.expectConcurrentExecution(":aPing", ":bPing")
+
+        run ":aPing", ":bPing"
+    }
+
+    def "two parallelizable tasks that are dependencies of another task are executed in parallel"() {
+        given:
+        withParallelThreads(2)
+
+        when:
+        buildFile << """
+            aPing.dependsOn bPing, cPing
+        """
+
+        then:
+        blockingServer.expectConcurrentExecution(":bPing", ":cPing")
+        blockingServer.expectSerialExecution(":aPing")
+
+        run ":aPing"
+    }
+
+    def "task is not executed if one of its dependencies executed in parallel fails"() {
+        given:
+        withParallelThreads(2)
+
+        when:
+        buildFile << """
+            aPing.dependsOn bPing, cFailingPing
+        """
+
+        then:
+        blockingServer.expectConcurrentExecution(":bPing", ":cFailingPing")
+
+        when:
+        fails ":aPing"
+
+        then:
+        notExecuted(":aPing")
+    }
+
+    def "the number of tasks executed in parallel is limited by the number of parallel threads"() {
+        given:
+        withParallelThreads(2)
+
+        expect:
+        blockingServer.expectConcurrentExecution(":aPing", ":bPing")
+        blockingServer.expectConcurrentExecution(":cPing", ":dPing")
+
+        run ":aPing", ":bPing", ":cPing", ":dPing"
+    }
+
+    def "non parallelizable tasks are not started if there are any parallelizable tasks running"() {
+        given:
+        withParallelThreads(3)
+
+        when:
+        buildFile << """
+            aPing.dependsOn bPing, cPing, dSerialPing
+        """
+
+        then:
+        blockingServer.expectConcurrentExecution(":bPing", ":cPing")
+        blockingServer.expectConcurrentExecution(":dSerialPing")
+        blockingServer.expectSerialExecution(":aPing")
+
+        run ":aPing"
+    }
+
+    def "parallelizable tasks are not started if there are any non parallelizable tasks running"() {
+        given:
+        withParallelThreads(3)
+
+        when:
+        buildFile << """
+            aPing.dependsOn bSerialPing, cPing, dPing
+        """
+
+        then:
+        blockingServer.expectConcurrentExecution(":bSerialPing")
+        blockingServer.expectConcurrentExecution(":cPing", ":dPing")
+        blockingServer.expectSerialExecution(":aPing")
+
+        run ":aPing"
+    }
+
+    def "parallelizable tasks are run in parallel even if there are some non parallelizable tasks running in different project"() {
+        given:
+        withParallelThreads(3)
+
+        expect:
+        blockingServer.expectConcurrentExecution(":a:aSerialPing", ":b:aPing", ":b:bPing")
+
+        run ":a:aSerialPing", ":b:aPing", ":b:bPing"
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
index bdef399..b86aa41 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
@@ -17,7 +17,9 @@ package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
 import org.junit.Test
@@ -25,6 +27,9 @@ import org.junit.Test
 class ProjectLayoutIntegrationTest extends AbstractIntegrationTest {
 
     @Rule
+    public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, testDirectoryProvider)
+
+    @Rule
     public final TestResources resources = new TestResources(testDirectoryProvider)
 
     @Test
@@ -39,9 +44,8 @@ repositories {
     mavenCentral()
 }
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.0.5'
-    // scaladoc in Scala 2.9.2 requires Java 1.6
-    compile 'org.scala-lang:scala-library:2.9.1'
+    compile 'org.codehaus.groovy:groovy-all:2.3.10'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
 
 sourceSets.each {
@@ -124,11 +128,20 @@ sourceSets.each {
                 'org/gradle/ScalaClass2.class'
         )
 
-        executer.withTasks('javadoc', 'groovydoc', 'scaladoc').run()
+        def runScalaDoc = !GradleContextualExecuter.daemon
+        def tasks = ['javadoc', 'groovydoc']
+        if (runScalaDoc) {
+            tasks << "scaladoc"
+        }
+
+        executer.withTasks(tasks).run()
 
         buildDir.file('docs/javadoc/index.html').assertIsFile()
         buildDir.file('docs/groovydoc/index.html').assertIsFile()
-        buildDir.file('docs/scaladoc/index.html').assertIsFile()
+
+        if (runScalaDoc) {
+            buildDir.file('docs/scaladoc/index.html').assertIsFile()
+        }
     }
 
     @Test
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
index 4512f37..2482958 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java
@@ -102,13 +102,13 @@ public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
         testFile("build.gradle").write("// empty");
 
         ExecutionFailure result = inTestDirectory().withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
+        result.assertThatDescription(startsWith("Multiple projects in this build have project directory"));
 
         result = usingProjectDir(getTestDirectory()).withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have project directory"));
+        result.assertThatDescription(startsWith("Multiple projects in this build have project directory"));
 
         result = usingBuildFile(testFile("build.gradle")).withTasks("test").runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. Multiple projects in this build have build file"));
+        result.assertThatDescription(startsWith("Multiple projects in this build have build file"));
     }
 
     @Test
@@ -137,18 +137,21 @@ public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
         TestFile settingsFile = testFile("settings.gradle");
         settingsFile.write("// empty");
 
-        TestFile projectdir = testFile("project dir");
-        projectdir.mkdirs();
+        TestFile projectDir = testFile("project dir");
+        TestFile buildFile = projectDir.file("build.gradle").createFile();
 
-        ExecutionFailure result = usingProjectDir(projectdir).usingSettingsFile(settingsFile).runWithFailure();
-        result.assertThatDescription(startsWith("Could not select the default project for this build. No projects in this build have project directory"));
+        ExecutionFailure result = usingProjectDir(projectDir).usingSettingsFile(settingsFile).runWithFailure();
+        result.assertHasDescription(String.format("No projects in this build have project directory '%s'.", projectDir));
+
+        result = usingBuildFile(buildFile).usingSettingsFile(settingsFile).runWithFailure();
+        result.assertHasDescription(String.format("No projects in this build have build file '%s'.", buildFile));
     }
 
     @Test
     public void settingsFileTakesPrecedenceOverBuildFileInSameDirectory() {
         testFile("settings.gradle").write("rootProject.buildFileName = 'root.gradle'");
         testFile("root.gradle").write("task('do-stuff')");
-        
+
         TestFile buildFile = testFile("build.gradle");
         buildFile.write("throw new RuntimeException()");
 
@@ -244,4 +247,68 @@ public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
         usingBuildFile(rootBuildFile).withTasks("do-stuff").run().assertTasksExecuted(":child:task", ":do-stuff", ":child:do-stuff");
         usingBuildFile(childBuildFile).usingSettingsFile(settingsFile).withTasks("do-stuff").run().assertTasksExecuted(":child:do-stuff");
     }
+
+    @Test
+    public void multiProjectBuildCanHaveAllProjectsAsChildrenOfSettingsDir() {
+        TestFile settingsFile = testFile("settings.gradle");
+        settingsFile.writelns(
+            "rootProject.projectDir = new File(settingsDir, 'root')",
+            "include 'sub'",
+            "project(':sub').projectDir = new File(settingsDir, 'root/sub')"
+        );
+
+        getTestDirectory().createDir("root").file("build.gradle").writelns("allprojects { task thing }");
+
+        inTestDirectory().withTasks(":thing").run().assertTasksExecuted(":thing");
+        inTestDirectory().withTasks(":sub:thing").run().assertTasksExecuted(":sub:thing");
+    }
+
+    @Test
+    public void usesRootProjectAsDefaultProjectWhenInSettingsDir() {
+        TestFile settingsDir = testFile("gradle");
+        TestFile settingsFile = settingsDir.file("settings.gradle");
+        settingsFile.writelns(
+            "rootProject.projectDir = new File(settingsDir, '../root')",
+            "include 'sub'",
+            "project(':sub').projectDir = new File(settingsDir, '../root/sub')"
+        );
+        getTestDirectory().createDir("root").file("build.gradle").writelns("allprojects { task thing }");
+
+        inDirectory(settingsDir).withTasks("thing").run().assertTasksExecuted(":thing", ":sub:thing");
+    }
+
+    @Test
+    public void rootProjectDirectoryAndBuildFileDoNotHaveToExistWhenInSettingsDir() {
+        TestFile settingsDir = testFile("gradle");
+        TestFile settingsFile = settingsDir.file("settings.gradle");
+        settingsFile.writelns(
+                "rootProject.projectDir = new File(settingsDir, '../root')",
+                "include 'sub'",
+                "project(':sub').projectDir = new File(settingsDir, '../sub')"
+        );
+        getTestDirectory().createDir("sub").file("build.gradle").writelns("task thing");
+
+        inDirectory(settingsDir).withTasks("thing").run().assertTasksExecuted(":sub:thing");
+    }
+
+    @Test
+    public void settingsFileGetsIgnoredWhenUsingSettingsOnlyDirectoryAsProjectDirectory() {
+        TestFile settingsDir = testFile("gradle");
+        TestFile settingsFile = settingsDir.file("settings.gradle");
+        settingsFile.writelns(
+                "rootProject.projectDir = new File(settingsDir, '../root')"
+        );
+        getTestDirectory().createDir("root").file("build.gradle").writelns("task thing");
+
+        inTestDirectory().withArguments("-p", settingsDir.getAbsolutePath()).withTasks("thing").runWithFailure()
+                .assertHasDescription("Task 'thing' not found in root project 'gradle'.");
+    }
+
+    @Test
+    public void cannotUseDirectoryAsBuildFile() {
+        TestFile settingsDir = testFile("gradle").createDir();
+
+        inTestDirectory().withArguments("-b", settingsDir.getAbsolutePath()).withTasks("thing").runWithFailure()
+                .assertHasDescription(String.format("Build file '%s' is not a file.", settingsDir.getAbsolutePath()));
+    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy
deleted file mode 100644
index 67de4a7..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SettingsScriptExecutionIntegrationTest.groovy
+++ /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.integtests
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.executer.ArtifactBuilder
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.Test
-
-import static org.hamcrest.Matchers.containsString
-import static org.hamcrest.Matchers.not
-import static org.junit.Assert.assertThat
-
-class SettingsScriptExecutionIntegrationTest extends AbstractIntegrationTest {
-    @Test
-    public void executesSettingsScriptWithCorrectEnvironment() {
-        createExternalJar()
-        createBuildSrc()
-        def implClassName = 'com.google.common.collect.Multimap'
-
-        testFile('settings.gradle') << """
-buildscript {
-    dependencies { classpath files('repo/test-1.3.jar') }
-}
-new org.gradle.test.BuildClass()
-new BuildSrcClass();
-println 'quiet message'
-logging.captureStandardOutput(LogLevel.ERROR)
-println 'error message'
-assert settings != null
-assert buildscript.classLoader == getClass().classLoader.parent
-assert buildscript.classLoader == Thread.currentThread().contextClassLoader
-Gradle.class.classLoader.loadClass('${implClassName}')
-try {
-    buildscript.classLoader.loadClass('${implClassName}')
-    assert false: 'should fail'
-} catch (ClassNotFoundException e) {
-    // expected
-}
-"""
-        testFile('build.gradle') << 'task doStuff'
-
-        ExecutionResult result = inTestDirectory().withTasks('doStuff').run()
-        assertThat(result.output, containsString('quiet message'))
-        assertThat(result.output, not(containsString('error message')))
-        assertThat(result.error, containsString('error message'))
-        assertThat(result.error, not(containsString('quiet message')))
-    }
-
-    private TestFile createBuildSrc() {
-        return testFile('buildSrc/src/main/java/BuildSrcClass.java') << '''
-            public class BuildSrcClass { }
-'''
-    }
-
-    private def createExternalJar() {
-        ArtifactBuilder builder = artifactBuilder();
-        builder.sourceFile('org/gradle/test/BuildClass.java') << '''
-            package org.gradle.test;
-            public class BuildClass { }
-'''
-        builder.buildJar(testFile("repo/test-1.3.jar"))
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.groovy
index 02a2166..0654b79 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskExecutionIntegrationTest.groovy
@@ -17,14 +17,13 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.TextUtil
-import org.junit.Test
+import spock.lang.Ignore
 import spock.lang.Issue
 
 import static org.hamcrest.Matchers.startsWith
 
 public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
-    
+
     def taskCanAccessTaskGraph() {
         buildFile << """
     boolean notified = false
@@ -51,7 +50,7 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
 """
         when:
         succeeds "a"
-        
+
         then:
         result.assertTasksExecuted(":b", ":a");
     }
@@ -84,13 +83,12 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         task c(dependsOn: ['b', ':a'])
     };
 """
-        
+
         expect:
         run("a", "c").assertTasksExecuted(":a", ":b", ":c", ":child1:b", ":child1:c", ":child1-2:b", ":child1-2:c", ":child1-2-2:b", ":child1-2-2:c", ":child2:b", ":child2:c");
         run("b", ":child2:c").assertTasksExecuted(":b", ":child1:b", ":child1-2:b", ":child1-2-2:b", ":child2:b", ":a", ":child2:c");
     }
 
-    @Test
     def executesMultiProjectDefaultTasksInASingleBuildAndEachTaskAtMostOnce() {
         settingsFile << "include 'child1', 'child2'"
         buildFile << """
@@ -106,16 +104,6 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         run().assertTasksExecuted(":a", ":child1:a", ":child2:a", ":child1:b", ":child2:b");
     }
 
-    def executesProjectDefaultTasksWhenNoneSpecified() {
-        buildFile << """
-    task a
-    task b(dependsOn: a)
-    defaultTasks 'b'
-"""
-        expect:
-        run().assertTasksExecuted(":a", ":b");
-    }
-    
     def doesNotExecuteTaskActionsWhenDryRunSpecified() {
         buildFile << """
     task a << { fail() }
@@ -185,8 +173,47 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         executer.withTasks("d").withArguments("-x", "unknown").runWithFailure().assertThatDescription(startsWith("Task 'unknown' not found in root project"));
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2974")
-    @Issue("http://issues.gradle.org/browse/GRADLE-3031")
+    def "unqualified exclude task name does not exclude tasks from parent projects"() {
+        settingsFile << "include 'sub'"
+        buildFile << """
+    task a
+"""
+        file("sub/build.gradle") << """
+    task a
+    task b
+    task c(dependsOn: [a, b, ':a'])
+"""
+
+        expect:
+        executer.inDirectory(file('sub')).withTasks('c').withArguments('-x', 'a').run().assertTasksExecuted(':a', ':sub:b', ':sub:c')
+    }
+
+    def 'can use camel-case matching to exclude tasks'() {
+        buildFile << """
+task someDep
+task someOtherDep
+task someTask(dependsOn: [someDep, someOtherDep])
+"""
+
+        expect:
+        executer.withTasks("someTask").withArguments("-x", "sODep").run().assertTasksExecuted(":someDep", ":someTask")
+        executer.withTasks("someTask").withArguments("-x", ":sODep").run().assertTasksExecuted(":someDep", ":someTask")
+    }
+
+    def 'can combine exclude task filters'() {
+        buildFile << """
+task someDep
+task someOtherDep
+task someTask(dependsOn: [someDep, someOtherDep])
+"""
+
+        expect:
+        executer.withTasks("someTask").withArguments("-x", "someDep", "-x", "someOtherDep").run().assertTasksExecuted(":someTask")
+        executer.withTasks("someTask").withArguments("-x", ":someDep", "-x", ":someOtherDep").run().assertTasksExecuted(":someTask")
+        executer.withTasks("someTask").withArguments("-x", "sODep", "-x", "soDep").run().assertTasksExecuted(":someTask")
+    }
+
+    @Issue(["https://issues.gradle.org/browse/GRADLE-3031", "https://issues.gradle.org/browse/GRADLE-2974"])
     def 'excluding a task that is a dependency of multiple tasks'() {
         settingsFile << "include 'sub'"
         buildFile << """
@@ -204,7 +231,7 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         executer.withTasks("b", "a").withArguments("-x", ":a").run().assertTasksExecuted(":b", ":sub:a");
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2022")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2022")
     def tryingToInstantiateTaskDirectlyFailsWithGoodErrorMessage() {
         buildFile << """
     new DefaultTask()
@@ -225,32 +252,37 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         fails 'b'
 
         then:
-        failure.assertHasDescription TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+        failure.assertHasDescription """Circular dependency between the following tasks:
 :a
 \\--- :b
      \\--- :a (*)
 
-(*) - details omitted (listed previously)""")
+(*) - details omitted (listed previously)"""
     }
 
-    def "placeolder actions not triggered when not requested"() {
+    @Ignore("Re-enable when work on realising only the required tasks instead of the whole task container is finished")
+    def "placeholder actions not triggered when not requested"() {
         when:
         buildFile << """
-        task a
-        tasks.addPlaceholderAction("b") {
+        task thing
+        tasks.addPlaceholderAction("b", DefaultTask) {
             throw new RuntimeException()
         }
+        task otherThing { dependsOn tasks.thing }
 """
         then:
-        succeeds 'a'
+        succeeds 'thing'
+        succeeds 'th'
+        succeeds 'otherThing'
+        succeeds 'oTh'
     }
 
-    def "explicit tasks are preferred over placeholder actions"() {
+    def "explicit tasks are preferred over placeholder tasks"() {
         buildFile << """
         task someTask << {println "explicit sometask"}
-        tasks.addPlaceholderAction("someTask"){
+        tasks.addPlaceholderAction("someTask", DefaultTask) {
             println  "placeholder action triggered"
-            task someTask << {println "placeholder sometask"}
+            it.doLast { throw new RuntimeException() }
         }
 """
         when:
@@ -258,17 +290,14 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
 
         then:
         output.contains("explicit sometask")
-        !output.contains("placeholder action triggered")
 
         when:
         succeeds 'someT'
 
         then:
         output.contains("explicit sometask")
-        !output.contains("placeholder action triggered")
     }
 
-
     def "honours mustRunAfter task ordering"() {
         buildFile << """
     task a {
@@ -348,12 +377,12 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         fails 'b'
 
         then:
-        failure.assertHasDescription TextUtil.toPlatformLineSeparators("""Circular dependency between the following tasks:
+        failure.assertHasDescription """Circular dependency between the following tasks:
 :a
 \\--- :b
      \\--- :a (*)
 
-(*) - details omitted (listed previously)""")
+(*) - details omitted (listed previously)"""
     }
 
     def "checked exceptions thrown by tasks are reported correctly"() {
@@ -380,14 +409,17 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
 
     def "honours shouldRunAfter task ordering"() {
         buildFile << """
-    task a {
+
+    class NotParallel extends DefaultTask {}
+
+    task a(type: NotParallel) {
         dependsOn 'b'
     }
-    task b {
+    task b(type: NotParallel) {
         shouldRunAfter 'c'
     }
-    task c
-    task d {
+    task c(type: NotParallel)
+    task d(type: NotParallel) {
         dependsOn 'c'
     }
 """
@@ -400,28 +432,30 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
 
     def "multiple should run after ordering can be ignored for one execution plan"() {
         buildFile << """
-    task a {
+    class NotParallel extends DefaultTask {}
+
+    task a(type: NotParallel) {
         dependsOn 'b', 'h'
     }
-    task b {
+    task b(type: NotParallel) {
         dependsOn 'c'
     }
-    task c {
+    task c(type: NotParallel) {
         dependsOn 'g'
         shouldRunAfter 'd'
     }
-    task d {
+    task d(type: NotParallel) {
         finalizedBy 'e'
         dependsOn 'f'
     }
-    task e
-    task f {
+    task e(type: NotParallel)
+    task f(type: NotParallel) {
         dependsOn 'c'
     }
-    task g {
+    task g(type: NotParallel) {
         shouldRunAfter 'h'
     }
-    task h {
+    task h(type: NotParallel) {
         dependsOn 'b'
     }
 """
@@ -432,4 +466,4 @@ public class TaskExecutionIntegrationTest extends AbstractIntegrationSpec {
         then:
         executedTasks == [':g', ':c', ':b', ':h', ':a', ':f', ':d', ':e']
     }
-}
+}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskSubclassingBinaryCompatibilityCrossVersionSpec.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskSubclassingBinaryCompatibilityCrossVersionSpec.groovy
index 47b3514..157f6ba 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskSubclassingBinaryCompatibilityCrossVersionSpec.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskSubclassingBinaryCompatibilityCrossVersionSpec.groovy
@@ -14,29 +14,62 @@
  * limitations under the License.
  */
 package org.gradle.integtests
-
+import org.gradle.api.DefaultTask
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.plugins.quality.*
+import org.gradle.api.plugins.sonar.SonarAnalyze
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.SourceTask
+import org.gradle.api.tasks.Sync
+import org.gradle.api.tasks.application.CreateStartScripts
+import org.gradle.api.tasks.bundling.Jar
+import org.gradle.api.tasks.bundling.Tar
+import org.gradle.api.tasks.bundling.War
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.api.tasks.compile.GroovyCompile
+import org.gradle.api.tasks.scala.ScalaCompile
+import org.gradle.api.tasks.testing.Test
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.plugins.ear.Ear
+import org.gradle.plugins.signing.Sign
 import org.gradle.util.GradleVersion
-
 /**
  * Tests that task classes compiled against earlier versions of Gradle are still compatible.
  */
- at TargetVersions('0.9-rc-3+')
+ at TargetVersions('1.0+')
 class TaskSubclassingBinaryCompatibilityCrossVersionSpec extends CrossVersionIntegrationSpec {
     def "can use task subclass compiled using previous Gradle version"() {
         given:
         def taskClasses = [
-                "DefaultTask", "SourceTask", "ConventionTask",
-                "Copy", "Sync", "Zip", "Jar", "Tar", "War", "Ear"
+                DefaultTask,
+                SourceTask,
+                ConventionTask,
+                Copy,
+                Sync,
+                Zip,
+                Jar,
+                Tar,
+                War,
+                ScalaCompile,
+                GroovyCompile,
+//                JavaCompile,
+                CodeNarc,
+                Checkstyle,
+                Ear,
+                FindBugs,
+                Pmd,
+                JDepend,
+                Sign,
+                CreateStartScripts,
+                SonarAnalyze,
         ]
-
-        // Ear plugin added in m4.
-        if (previous.version < GradleVersion.version("1.0-milestone-4")) {
-            taskClasses.remove("Ear")
+        if (previous.version >= GradleVersion.version("1.1")) {
+            // Breaking changes were made to Test between 1.0 and 1.1
+            taskClasses << Test
         }
 
-        Map<String, String> subclasses = taskClasses.collectEntries { ["custom" + it, it] }
+        Map<String, String> subclasses = taskClasses.collectEntries { ["custom" + it.simpleName, it.name] }
 
         file("producer/build.gradle") << """
             apply plugin: 'groovy'
@@ -49,21 +82,24 @@ class TaskSubclassingBinaryCompatibilityCrossVersionSpec extends CrossVersionInt
         file("producer/src/main/groovy/SomePlugin.groovy") << """
             import org.gradle.api.Plugin
             import org.gradle.api.Project
-            import org.gradle.api.DefaultTask
-            import org.gradle.api.tasks.*
-            import org.gradle.api.tasks.bundling.*
-            ${taskClasses.contains("Ear") ? "import org.gradle.plugins.ear.Ear" : ""}
-            import org.gradle.api.internal.ConventionTask
 
             class SomePlugin implements Plugin<Project> {
                 void apply(Project p) { """ <<
-                subclasses.collect { "p.tasks.add('${it.key}', ${it.key.capitalize()})" }.join("\n") << """
+                subclasses.collect { "p.tasks.create('${it.key}', ${it.key})" }.join("\n") << """
                 }
             }
             """ <<
 
                 subclasses.collect {
-                    "class ${it.key.capitalize()} extends ${it.value} {}"
+                    def className = it.key
+                    """class ${className} extends ${it.value} {
+    ${className}() {
+        // GRADLE-3185
+        project.logger.lifecycle('task created')
+        // GRADLE-3207
+        super.getServices()
+    }
+}"""
                 }.join("\n")
 
         buildFile << """
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
deleted file mode 100644
index 9b5f2cf..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/WaterProjectIntegrationTest.groovy
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.integtests
-
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.integtests.fixtures.executer.*
-import org.gradle.internal.SystemProperties
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import org.junit.Test
-
-import static org.junit.Assert.assertEquals
-
-class WaterProjectIntegrationTest {
-    final static String NL = SystemProperties.lineSeparator
-
-    final static String HELLO_CLAUSE = "Hello, I'm "
-    final static String CHILDREN_TEXT = 'I love water.'
-    final static String WATER_INFO = 'As you all know, I cover three quarters of this planet!'
-    final static String BLUE_WHALE_INFO = "I'm the largets animal which has ever lived on this planet!"
-    final static String KRILL_INFO = "The weight of my species in summer is twice as heavy as all human beings!"
-    final static String PHYTOPLANKTON_INFO = "I produce as much oxygen as all the other plants on earth together!"
-
-    final static String WATER_NAME = 'water'
-    final static String BLUE_WHALE_NAME = 'bluewhale'
-    final static String KRILL_NAME = 'krill'
-    final static String PHYTOPLANKTON_NAME = 'phytoplankton'
-
-    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider();
-    private final GradleDistribution dist = new UnderDevelopmentGradleDistribution();
-    private final GradleExecuter executer = new GradleContextualExecuter(dist, temporaryFolder);
-
-    @Rule public final Sample sample = new Sample(temporaryFolder, WATER_NAME)
-
-    @Test
-    public void waterProject() {
-        File waterDir = sample.dir
-        ExecutionResult result = executer.inDirectory(waterDir).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
-                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
-                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
-
-        result = executer.inDirectory(new File(waterDir, BLUE_WHALE_NAME)).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO,
-                intro(KRILL_NAME), CHILDREN_TEXT, KRILL_INFO,
-                intro(BLUE_WHALE_NAME), CHILDREN_TEXT, BLUE_WHALE_INFO]))
-
-        result = executer.inDirectory(new File(waterDir, PHYTOPLANKTON_NAME)).withTasks('hello').withQuietLogging().run()
-        assertEquals(result.output, list2text([intro(WATER_NAME), WATER_INFO,
-                intro(PHYTOPLANKTON_NAME), CHILDREN_TEXT, PHYTOPLANKTON_INFO]))
-    }
-
-    static String intro(String projectName) {
-        HELLO_CLAUSE + projectName
-    }
-
-    static String list2text(List list) {
-        list.join(NL) + NL
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
index db712cb..033ea33 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
@@ -127,9 +127,9 @@ assert classesDir.directory
         noExceptionThrown()
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null})
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null})
     def "java home from environment should be used to run build"() {
-        def alternateJavaHome = AvailableJavaHomes.bestAlternative
+        def alternateJavaHome = AvailableJavaHomes.differentJdk.javaHome
 
         file('build.gradle') << "println 'javaHome=' + org.gradle.internal.jvm.Jvm.current().javaHome.canonicalPath"
 
@@ -146,9 +146,9 @@ assert classesDir.directory
         out.contains("javaHome=" + alternateJavaHome.canonicalPath)
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null})
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null})
     def "java home from gradle properties should be used to run build"() {
-        def alternateJavaHome = AvailableJavaHomes.bestAlternative
+        def alternateJavaHome = AvailableJavaHomes.differentJdk.javaHome
 
         file('gradle.properties') << "org.gradle.java.home=${TextUtil.escapeString(alternateJavaHome.canonicalPath)}"
 
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/logging/LoggingIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/logging/LoggingIntegrationTest.groovy
index 68759d2..427c9f5 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/logging/LoggingIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/logging/LoggingIntegrationTest.groovy
@@ -406,8 +406,8 @@ class OutputOccurrence {
         if (index == 0) {
             return
         }
-        int startLine = index - SystemProperties.lineSeparator.length()
-        if (startLine < 0 || !actual.substring(startLine).startsWith(SystemProperties.lineSeparator)) {
+        int startLine = index - SystemProperties.instance.lineSeparator.length()
+        if (startLine < 0 || !actual.substring(startLine).startsWith(SystemProperties.instance.lineSeparator)) {
             throw new AssertionError("Expected content '$expected' is not at the start of a line in output $actual.")
         }
     }
@@ -417,17 +417,17 @@ class OutputOccurrence {
         if (endLine == actual.length()) {
             return
         }
-        if (!actual.substring(endLine).startsWith(SystemProperties.lineSeparator)) {
+        if (!actual.substring(endLine).startsWith(SystemProperties.instance.lineSeparator)) {
             throw new AssertionError("Expected content '$expected' is not at the end of a line in output $actual.")
         }
     }
 
     void assertHasPrefix(String pattern) {
-        int startLine = actual.lastIndexOf(SystemProperties.lineSeparator, index)
+        int startLine = actual.lastIndexOf(SystemProperties.instance.lineSeparator, index)
         if (startLine < 0) {
             startLine = 0
         } else {
-            startLine += SystemProperties.lineSeparator.length()
+            startLine += SystemProperties.instance.lineSeparator.length()
         }
         
         String actualPrefix = actual.substring(startLine, index)
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvySFtpPublishIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvySFtpPublishIntegrationTest.groovy
deleted file mode 100644
index 0995405..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvySFtpPublishIntegrationTest.groovy
+++ /dev/null
@@ -1,101 +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.integtests.publish.ivy
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.gradle.test.fixtures.server.sftp.SFTPServer
-import org.junit.Rule
-
-class IvySFtpPublishIntegrationTest extends AbstractIntegrationSpec {
-
-    @Rule
-    public final SFTPServer sftpServer = new SFTPServer(this)
-    @Rule
-    ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    public void "can publish using SftpResolver"() {
-        given:
-        file("settings.gradle") << 'rootProject.name = "publish"'
-
-        and:
-        buildFile << """
-        apply plugin: 'java'
-        version = '2'
-        group = 'org.gradle'
-        uploadArchives {
-            repositories {
-                add(new org.apache.ivy.plugins.resolver.SFTPResolver()) {
-                    addArtifactPattern "repos/libs/[organisation]/[module]/[artifact]-[revision].[ext]"
-                    host = "${sftpServer.hostAddress}"
-                    port = ${sftpServer.port}
-                    user = "user"
-                    userPassword = "user"
-                }
-            }
-        }
-        """
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        when:
-        run "uploadArchives"
-
-        then:
-        sftpServer.hasFile("repos/libs/org.gradle/publish/publish-2.jar")
-        sftpServer.hasFile("repos/libs/org.gradle/publish/ivy-2.xml");
-        sftpServer.file("repos/libs/org.gradle/publish/publish-2.jar").assertIsCopyOf(file('build/libs/publish-2.jar'))
-
-        and:
-        progressLogging.uploadProgressLogged("repos/libs/org.gradle/publish/ivy-2.xml")
-        progressLogging.uploadProgressLogged("repos/libs/org.gradle/publish/publish-2.jar")
-    }
-
-    public void "reports Authentication Errors"() {
-        given:
-        file("settings.gradle") << 'rootProject.name = "publish"'
-
-        and:
-        buildFile << """
-        apply plugin: 'java'
-        version = '2'
-        group = 'org.gradle'
-        uploadArchives {
-            repositories {
-                add(new org.apache.ivy.plugins.resolver.SFTPResolver()) {
-                    addArtifactPattern "repos/libs/[organisation]/[module]/[artifact]-[revision].[ext]"
-                    host = "${sftpServer.hostAddress}"
-                    port = ${sftpServer.port}
-                    user = "simple"
-                    userPassword = "wrongPassword"
-                }
-            }
-        }
-        """
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        when:
-        fails "uploadArchives"
-
-        then:
-        failure.assertHasDescription('Execution failed for task \':uploadArchives\'.')
-        failure.assertHasCause('Could not publish configuration \'archives\'')
-        failure.assertHasCause("java.io.IOException: Auth fail")
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/JUnitSamplesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/JUnitSamplesIntegrationTest.groovy
index 9e7dcf7..0bff894 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/JUnitSamplesIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/JUnitSamplesIntegrationTest.groovy
@@ -33,7 +33,7 @@ public class JUnitSamplesIntegrationTest extends AbstractIntegrationTest {
         TestFile projectDir = sample.dir.file("categories")
 
         // Build and test projects
-        executer.inDirectory(projectDir).withTasks('clean', 'build').withArguments("--no-opt").run()
+        executer.inDirectory(projectDir).withTasks('clean', 'build').run()
 
         // Check tests have run
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(projectDir)
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy
deleted file mode 100644
index 4c0786f..0000000
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesAntlrIntegrationTest.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.samples
-
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.test.fixtures.file.TestFile
-import org.junit.Rule
-import org.junit.Test
-
-class SamplesAntlrIntegrationTest extends AbstractIntegrationTest {
-
-    @Rule public final Sample sample = new Sample(testDirectoryProvider, 'antlr')
-
-    @Test
-    public void canBuild() {
-        TestFile projectDir = sample.dir
-
-        // Build and test projects
-        executer.inDirectory(projectDir).withTasks('clean', 'build').withArguments("--no-opt").run()
-
-        // Check tests have run
-        def result = new DefaultTestExecutionResult(projectDir)
-        result.assertTestClassesExecuted('org.gradle.GrammarTest')
-    }
-}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
index 79f7e06..dacb367 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
@@ -35,7 +35,7 @@ class SamplesApplicationIntegrationTest extends AbstractIntegrationSpec {
 
     def canBuildAndRunTheInstalledApplication() {
         when:
-        executer.inDirectory(sample.dir).withTasks('installApp').run()
+        executer.inDirectory(sample.dir).withTasks('installDist').run()
 
         then:
         def installDir = sample.dir.file('build/install/application')
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
index a743894..fa4e744 100755
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
@@ -15,23 +15,25 @@
  */
 package org.gradle.integtests.samples
 
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import org.junit.Rule
-import org.junit.Test
 
-class SamplesCodeQualityIntegrationTest extends AbstractIntegrationTest {
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+class SamplesCodeQualityIntegrationTest extends AbstractIntegrationSpec {
+    @Rule public final Sample sample = new Sample(temporaryFolder, 'codeQuality')
 
-    @Rule public final Sample sample = new Sample(testDirectoryProvider, 'codeQuality')
-
-    @Test
-    public void checkReportsGenerated() {
+    def checkReportsGenerated() {
         TestFile projectDir = sample.dir
         TestFile buildDir = projectDir.file('build')
 
+        when:
         executer.inDirectory(projectDir).requireGradleHome().withTasks('check').run()
 
+        then:
         buildDir.file('reports/checkstyle/main.xml').assertIsFile()
         buildDir.file('reports/codenarc/main.html').assertIsFile()
         buildDir.file('reports/codenarc/test.html').assertIsFile()
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentMetadataRulesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..09acfb7
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.samples
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.junit.Rule
+
+class SamplesComponentMetadataRulesIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule public final Sample sample = new Sample(temporaryFolder, 'componentMetadataRules')
+
+    @UsesSample('componentMetadataRules')
+    def "can run custom status scheme sample" () {
+        given:
+        inDirectory("componentMetadataRules")
+
+        when:
+        succeeds "listApi"
+
+        then:
+        output.contains("Resolved: api-2.0.jar")
+    }
+
+    @UsesSample('componentMetadataRules')
+    def "can run custom status scheme with module sample" () {
+        given:
+        inDirectory("componentMetadataRules")
+
+        when:
+        succeeds "listLib"
+
+        then:
+        output.contains("Resolved: lib-1.9.jar")
+    }
+
+    @UsesSample('componentMetadataRules')
+    def "can run custom status scheme with rule source sample" () {
+        given:
+        inDirectory("componentMetadataRules")
+
+        when:
+        succeeds "listWithRule"
+
+        then:
+        output.contains("Resolved: api-1.9.jar")
+    }
+
+    @UsesSample('componentMetadataRules')
+    def "can run ivy metadata rule" () {
+        given:
+        inDirectory("componentMetadataRules")
+
+        when:
+        succeeds "listWithIvyRule"
+
+        then:
+        output.contains("Resolved: lib-2.0.jar")
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentSelectionRulesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentSelectionRulesIntegrationTest.groovy
new file mode 100644
index 0000000..ce8e22c
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesComponentSelectionRulesIntegrationTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.samples
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.junit.Rule
+
+class SamplesComponentSelectionRulesIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule public final Sample sample = new Sample(temporaryFolder, 'componentSelectionRules')
+
+    @UsesSample("componentSelectionRules")
+    def "can run resolveConfiguration sample"() {
+        given:
+        inDirectory "componentSelectionRules"
+
+        when:
+        run "resolveConfiguration"
+
+        then:
+        output.contains "Rejected version: 1.5"
+        output.contains "Rejected version: 1.4"
+        output.contains "** Accepted version: 1.3.0"
+    }
+
+    @UsesSample("componentSelectionRules")
+    def "can run reject sample"() {
+        given:
+        inDirectory "componentSelectionRules"
+
+        when:
+        run "printRejectConfig"
+
+        then:
+        output.contains "Resolved: api-1.4.jar"
+    }
+
+    @UsesSample("componentSelectionRules")
+    def "can run metadata rules sample"() {
+        given:
+        inDirectory "componentSelectionRules"
+
+        when:
+        run "printMetadataRulesConfig"
+
+        then:
+        output.contains "Resolved: api-1.3.0.jar"
+        output.contains "Resolved: lib-1.9.jar"
+    }
+
+    @UsesSample("componentSelectionRules")
+    def "can run targeted rule sample"() {
+        given:
+        inDirectory "componentSelectionRules"
+
+        when:
+        run "printTargetConfig"
+
+        then:
+        output.contains "Resolved: api-1.4.jar"
+    }
+
+    @UsesSample("componentSelectionRules")
+    def "can run rules source sample"() {
+        given:
+        inDirectory "componentSelectionRules"
+
+        when:
+        run "printRuleSourceConfig"
+
+        then:
+        output.contains "Resolved: api-1.4.jar"
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaTestListenerIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaTestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..78b4778
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaTestListenerIntegrationTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.samples
+
+import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.test.fixtures.file.TestFile
+import org.hamcrest.Matchers
+import org.junit.Rule
+import org.junit.Test
+
+class SamplesJavaTestListenerIntegrationTest extends  AbstractIntegrationTest {
+
+    @Rule public final Sample sample = new Sample(testDirectoryProvider, 'java/testListener')
+
+    @Test
+    public void runsBuildAndShowsFailedTests() {
+        TestFile javaprojectDir = sample.dir
+
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('clean', 'build').runWithFailure().assertTestsFailed()
+
+        // Check tests have run
+        def result = new DefaultTestExecutionResult(javaprojectDir)
+        result.assertTestClassesExecuted('org.gradle.DoNothingTest')
+        result.testClass('org.gradle.DoNothingTest').
+                assertTestFailed('doNothingButFail', Matchers.equalTo('java.lang.AssertionError: I always fail')).
+                assertTestFailed('doNothingButError', Matchers.equalTo('java.lang.RuntimeException: I always throw exceptions')).
+                assertTestPassed('doNothing')
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesWebQuickstartIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesWebQuickstartIntegrationTest.groovy
index 7e131e0..3e97f7b 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesWebQuickstartIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/SamplesWebQuickstartIntegrationTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package org.gradle.integtests.samples
-
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.test.fixtures.file.TestFile
@@ -23,6 +22,8 @@ import org.junit.Rule
 import spock.lang.Timeout
 import spock.lang.Unroll
 
+import static org.gradle.integtests.fixtures.UrlValidator.available
+
 class SamplesWebQuickstartIntegrationTest extends AbstractIntegrationSpec {
     @Rule public final Sample sample = new Sample(temporaryFolder, 'webApplication/quickstart')
 
@@ -67,6 +68,9 @@ class SamplesWebQuickstartIntegrationTest extends AbstractIntegrationSpec {
 httpPort = ${httpPort}
 stopPort = ${stopPort}
 
+println "httpPort: \$httpPort"
+println "stopPort: \$stopPort"
+
 task runTest << {
     URL url = new URL("http://localhost:\$httpPort/quickstart")
     println url.text
@@ -83,14 +87,14 @@ task sayHearthyGoodbye << {
 
         //starting jetty
         sample sample
-        def runJetty = executer.withTasks(jettyStartTask, "sayHearthyGoodbye").start()
+        def runJetty = executer.withTasks(jettyStartTask, "sayHearthyGoodbye").withArgument("-i").start()
 
         //jetty is started
-        available("http://localhost:$httpPort/quickstart")
+        available("http://localhost:$httpPort/quickstart", "jetty")
 
         //running web test then stopping jetty
         sample sample
-        def jettyStop = executer.withTasks('runTest', 'jettyStop').run()
+        def jettyStop = executer.withTasks('runTest', 'jettyStop').withArgument("-i").run()
 
         //test has completed
         assert jettyStop.output.contains('hello Gradle')
@@ -99,19 +103,4 @@ task sayHearthyGoodbye << {
         runJetty.waitForFinish()
         assert runJetty.standardOutput.contains("Jetty will miss you!")
     }
-
-    void available(String theUrl) {
-        URL url = new URL(theUrl)
-        long expiry = System.currentTimeMillis() + 30000
-        while (System.currentTimeMillis() <= expiry) {
-            try {
-                url.text
-                return
-            } catch (IOException e) {
-                // continue
-            }
-            Thread.sleep(200)
-        }
-        throw new RuntimeException("Timeout waiting for jetty to become available.")
-    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesIntegrationTest.groovy
index 1679201..3ba1753 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesIntegrationTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.integtests.samples
 
-import org.gradle.integtests.fixtures.UserGuideSamplesRunner
 import org.junit.runner.RunWith
 
 @RunWith(UserGuideSamplesRunner.class)
@@ -27,7 +26,7 @@ class UserGuideSamplesIntegrationTest {
      If you're working in samples area there're gradle tasks that you should know of:
      - gradle intTestImage makes sure that the samples' resources are copied to the right place
      - gradle docs:userguideDocbook makes sure that samples' info is extracted from XMLs
-     - the 'expected' content of the asserting mechanism lives under docs/samples/userguideOutput
+     - the 'expected' content of the asserting mechanism lives under docs/src/samples/userguideOutput
 
     */
-}
\ No newline at end of file
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesRunner.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesRunner.groovy
new file mode 100644
index 0000000..c2058dd
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/samples/UserGuideSamplesRunner.groovy
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.samples
+
+import com.google.common.collect.ArrayListMultimap
+import groovy.io.PlatformLineWriter
+import org.apache.tools.ant.taskdefs.Delete
+import org.gradle.api.Transformer
+import org.gradle.api.reporting.components.ComponentReportOutputFormatter
+import org.gradle.integtests.fixtures.executer.*
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.AntUtil
+import org.gradle.util.TextUtil
+import org.junit.Assert
+import org.junit.runner.Description
+import org.junit.runner.Runner
+import org.junit.runner.notification.Failure
+import org.junit.runner.notification.RunNotifier
+
+import java.util.regex.Pattern
+
+class UserGuideSamplesRunner extends Runner {
+    private static final String NL = SystemProperties.instance.lineSeparator
+
+    private Class<?> testClass
+    private Description description
+    private Map<Description, SampleRun> samples
+    private TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    private GradleDistribution dist = new UnderDevelopmentGradleDistribution()
+    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext()
+    private GradleExecuter executer = new GradleContextualExecuter(dist, temporaryFolder)
+    private Pattern dirFilter
+    private List excludes
+    private TestFile baseExecutionDir = temporaryFolder.testDirectory
+
+    UserGuideSamplesRunner(Class<?> testClass) {
+        this.testClass = testClass
+        this.description = Description.createSuiteDescription(testClass)
+        this.dirFilter = initDirFilterPattern()
+        this.excludes = initExcludes()
+        samples = new LinkedHashMap()
+        for (sample in getScriptsForSamples(buildContext.userGuideInfoDir)) {
+            if (shouldInclude(sample)) {
+                Description childDescription = Description.createTestDescription(testClass, sample.id)
+                description.addChild(childDescription)
+                samples.put(childDescription, sample)
+
+                println "Sample $sample.id dir: $sample.subDir"
+                sample.runs.each { println "    args: $it.args expect: $it.outputFile" }
+            }
+        }
+
+        // have to copy everything upfront because build scripts of some samples refer to files of other samples
+        buildContext.samplesDir.copyTo(baseExecutionDir)
+    }
+
+    private Pattern initDirFilterPattern() {
+        String filter = System.properties["org.gradle.userguide.samples.filter"]
+        filter ? Pattern.compile(filter) : null
+    }
+
+    private List initExcludes() {
+        List excludes = []
+        String excludesString = System.properties["org.gradle.userguide.samples.exclude"] ?: "";
+        excludesString.split(',').each {
+            excludes.add it
+        }
+        return excludes
+    }
+
+    Description getDescription() {
+        description
+    }
+
+    private boolean shouldInclude(SampleRun run) {
+        if (excludes.contains(run.id)) {
+            return false
+        }
+        dirFilter ? run.subDir ==~ dirFilter : true
+    }
+
+    void run(RunNotifier notifier) {
+        for (childDescription in description.children) {
+            notifier.fireTestStarted(childDescription)
+            def sampleRun = samples.get(childDescription)
+            try {
+                cleanup(sampleRun)
+                for (run in sampleRun.runs) {
+                    if (run.brokenForParallel && GradleContextualExecuter.parallel) {
+                        continue
+                    }
+                    runSample(run)
+                }
+            } catch (Throwable t) {
+                notifier.fireTestFailure(new Failure(childDescription, t))
+            }
+            notifier.fireTestFinished(childDescription)
+        }
+        temporaryFolder.testDirectory.deleteDir()
+    }
+
+    private void cleanup(SampleRun run) {
+        // Clean up previous runs in the same subdir
+        File rootProjectDir = temporaryFolder.testDirectory.file(run.subDir)
+        if (rootProjectDir.exists()) {
+            def delete = new Delete()
+            delete.dir = rootProjectDir
+            delete.includes = "**/.gradle/** **/build/**"
+            AntUtil.execute(delete)
+        }
+    }
+
+    private void runSample(GradleRun run) {
+        try {
+            println("Test Id: $run.id, dir: $run.subDir, execution dir: $run.executionDir args: $run.args")
+
+            executer.noExtraLogging()
+                    .inDirectory(run.executionDir)
+                    .withArguments(run.args as String[])
+                    .withEnvironmentVars(run.envs)
+
+            if (!GradleContextualExecuter.longLivingProcess) {
+                //suppress daemon usage suggestions
+                executer.withArgument("--no-daemon")
+            }
+
+            def result = run.expectFailure ? executer.runWithFailure() : executer.run()
+            if (run.outputFile) {
+                def expectedResult = buildContext.userGuideOutputDir.file(run.outputFile).text
+                if (run.outputFormatter) {
+                    expectedResult = run.outputFormatter.transform(expectedResult)
+                }
+                expectedResult = replaceWithPlatformNewLines(expectedResult)
+                expectedResult = replaceWithRealSamplesDir(expectedResult)
+                try {
+                    result.assertOutputEquals(expectedResult, run.ignoreExtraLines, run.ignoreLineOrder)
+                } catch (AssertionError e) {
+                    println 'Expected Result:'
+                    println expectedResult
+                    println 'Actual Result:'
+                    println result.output
+                    println '---'
+                    throw e
+                }
+            }
+
+            run.files.each { path ->
+                println "  checking file '$path' exists"
+                def file = new File(run.executionDir, path).canonicalFile
+                Assert.assertTrue("Expected file '$file' does not exist.", file.exists())
+                Assert.assertTrue("Expected file '$file' is not a file.", file.isFile())
+            }
+            run.dirs.each { path ->
+                println "  checking directory '$path' exists"
+                def file = new File(run.executionDir, path).canonicalFile
+                Assert.assertTrue("Expected directory '$file' does not exist.", file.exists())
+                Assert.assertTrue("Expected directory '$file' is not a directory.", file.isDirectory())
+            }
+        } catch (Throwable e) {
+            throw new AssertionError("Integration test for sample '$run.id' in dir '$run.subDir' with args $run.args failed:${NL}$e.message").initCause(e)
+        }
+    }
+
+    private String replaceWithPlatformNewLines(String text) {
+        def stringWriter = new StringWriter()
+        new PlatformLineWriter(stringWriter).withWriter { it.write(text) }
+        stringWriter.toString()
+    }
+
+    private String replaceWithRealSamplesDir(String text) {
+        def normalisedSamplesDir = TextUtil.normaliseFileSeparators(baseExecutionDir.absolutePath)
+        return text.replaceAll(Pattern.quote('/home/user/gradle/samples'), normalisedSamplesDir)
+    }
+
+    private Collection<SampleRun> getScriptsForSamples(File userguideInfoDir) {
+        def samplesXml = new File(userguideInfoDir, 'samples.xml')
+        assertSamplesGenerated(samplesXml.exists())
+        def samples = new XmlParser().parse(samplesXml)
+        def samplesByDir = ArrayListMultimap.create()
+
+        def children = samples.children()
+        assertSamplesGenerated(!children.isEmpty())
+
+        children.each {Node sample ->
+            def id = sample.'@id'
+            def dir = sample.'@dir'
+            def args = sample.'@args'
+            def outputFile = sample.'@outputFile'
+            boolean ignoreExtraLines = Boolean.valueOf(sample.'@ignoreExtraLines')
+            boolean ignoreLineOrder = Boolean.valueOf(sample.'@ignoreLineOrder')
+            boolean expectFailure = Boolean.valueOf(sample.'@expectFailure')
+
+            def run = new GradleRun(id: id)
+            run.subDir = dir
+            run.args = args ? args.split('\\s+') as List : []
+            run.outputFile = outputFile
+            run.ignoreExtraLines = ignoreExtraLines
+            run.ignoreLineOrder = ignoreLineOrder
+            run.expectFailure = expectFailure
+
+            sample.file.each { file -> run.files << file.'@path' }
+            sample.dir.each { file -> run.dirs << file.'@path' }
+
+            samplesByDir.put(dir, run)
+        }
+
+        // Some custom values
+        samplesByDir.get('userguide/tutorial/properties').each { it.envs['ORG_GRADLE_PROJECT_envProjectProp'] = 'envPropertyValue' }
+        samplesByDir.get('userguide/buildlifecycle/taskExecutionEvents')*.expectFailure = true
+        samplesByDir.get('userguide/buildlifecycle/buildProjectEvaluateEvents')*.expectFailure = true
+        samplesByDir.get('userguide/tasks/finalizersWithFailure')*.expectFailure = true
+        samplesByDir.get('userguide/multiproject/dependencies/firstMessages/messages')*.brokenForParallel = true
+        samplesByDir.get('userguide/multiproject/dependencies/messagesHack/messages')*.brokenForParallel = true
+
+        Map<String, SampleRun> samplesById = new TreeMap<String, SampleRun>()
+
+        // Remove duplicates for a given directory.
+        samplesByDir.asMap().values().collect {List<GradleRun> dirSamples ->
+            def runs = dirSamples.findAll { it.mustRun }
+            if (!runs) {
+                // No samples in this dir have any args, so just run gradle tasks in the dir
+                def sample = dirSamples[0]
+                sample.args = ['tasks']
+                sample
+            } else {
+                return runs
+            }
+        }.flatten().each { GradleRun run ->
+            // Collect up into sample runs
+            def sampleRun = samplesById[run.id]
+            if (!sampleRun) {
+                sampleRun = new SampleRun(id: run.id, subDir: run.subDir)
+                samplesById[run.id] = sampleRun
+            }
+            sampleRun.runs << run
+        }
+
+        samplesById.nativeComponentReport.runs.each { it.outputFormatter = new ComponentReportOutputFormatter() }
+
+        if ("true".equals(System.getProperty("org.gradle.integtest.unknownos"))) {
+            // Ignore for now
+            samplesById.remove('completeCUnitExample')
+        }
+
+        return samplesById.values()
+    }
+
+    private void assertSamplesGenerated(boolean assertion) {
+        assert assertion : """Couldn't find any samples. Most likely, samples.xml was not generated.
+Please run 'gradle docs:userguideDocbook' first"""
+    }
+
+    private class GradleRun {
+        String id
+        List args = []
+        String subDir
+        Map envs = [:]
+        String outputFile
+        Transformer<String, String> outputFormatter
+        boolean expectFailure
+        boolean ignoreExtraLines
+        boolean ignoreLineOrder
+        boolean brokenForParallel
+        List files = []
+        List dirs = []
+
+        boolean getMustRun() {
+            return args || files || dirs
+        }
+
+        File getExecutionDir() {
+            new File(baseExecutionDir, subDir)
+        }
+    }
+
+    private class SampleRun {
+        String id
+        String subDir
+        List<GradleRun> runs = []
+    }
+}
+
+
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
index 1b25483..0770d43 100644
--- a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/ProjectLayoutIntegrationTest/canUseANonStandardBuildDir/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
 
 buildDir = 'target'
diff --git a/subprojects/internal-integ-testing/internal-integ-testing.gradle b/subprojects/internal-integ-testing/internal-integ-testing.gradle
index dff0de9..31425fd 100644
--- a/subprojects/internal-integ-testing/internal-integ-testing.gradle
+++ b/subprojects/internal-integ-testing/internal-integ-testing.gradle
@@ -24,8 +24,10 @@ dependencies {
         dependency libraries.slf4j_api
         dependency "org.jboss.netty:netty:3.2.4.Final"
     }
-    compile "org.apache.sshd:sshd-core:0.6.0"
+    compile libraries.sshd
     compile libraries.gson
+    compile libraries.joda
+    compile libraries.jsch
 }
 
 useTestFixtures(sourceSet: 'main')
@@ -54,4 +56,4 @@ class PrepareVersionsInfo extends DefaultTask {
     }
 }
 
-apply from: "$rootDir/gradle/ideaTestSourcesWorkaround.gradle"
\ No newline at end of file
+apply from: "$rootDir/gradle/ideaTestSourcesWorkaround.gradle"
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractDependencyResolutionTest.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractDependencyResolutionTest.groovy
index ec97881..01bbee0 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractDependencyResolutionTest.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractDependencyResolutionTest.groovy
@@ -17,22 +17,13 @@
 package org.gradle.integtests.fixtures
 
 import org.gradle.test.fixtures.ivy.IvyFileRepository
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
 import org.gradle.test.fixtures.maven.M2Installation
 import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.gradle.util.GradleVersion
-import org.junit.Rule
-
-import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
 
 abstract class AbstractDependencyResolutionTest extends AbstractIntegrationSpec {
-    @Rule public final HttpServer server = new HttpServer()
     private M2Installation m2Installation = new M2Installation(testDirectory)
 
     def setup() {
-        server.expectUserAgent(matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
         requireOwnGradleUserHomeDir()
         executer.beforeExecute m2Installation
     }
@@ -45,29 +36,7 @@ abstract class AbstractDependencyResolutionTest extends AbstractIntegrationSpec
         return ivy(dir)
     }
 
-    IvyHttpRepository getIvyHttpRepo() {
-        return new IvyHttpRepository(server, "/repo", ivyRepo)
-    }
-
-    IvyHttpRepository ivyHttpRepo(String name) {
-        assert !name.startsWith("/")
-        return new IvyHttpRepository(server, "/${name}", ivyRepo(name))
-    }
-
     MavenFileRepository mavenRepo(String name = "repo") {
         return maven(name)
     }
-
-    MavenHttpRepository getMavenHttpRepo() {
-        return new MavenHttpRepository(server, "/repo", mavenRepo)
-    }
-
-    MavenHttpRepository mavenHttpRepo(String name) {
-        assert !name.startsWith("/")
-        return new MavenHttpRepository(server, "/${name}", mavenRepo(name))
-    }
-
-    MavenHttpRepository mavenHttpRepo(String contextPath, MavenFileRepository backingRepo) {
-        return new MavenHttpRepository(server, contextPath, backingRepo)
-    }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractHttpDependencyResolutionTest.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractHttpDependencyResolutionTest.groovy
new file mode 100644
index 0000000..ea9bcdd
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractHttpDependencyResolutionTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures
+
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.util.GradleVersion
+import org.junit.Rule
+
+import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
+
+abstract class AbstractHttpDependencyResolutionTest extends AbstractDependencyResolutionTest {
+    @Rule public final HttpServer server = new HttpServer()
+
+    def setup() {
+        server.expectUserAgent(matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
+        server.start()
+    }
+
+    IvyHttpRepository getIvyHttpRepo() {
+        return new IvyHttpRepository(server, "/repo", ivyRepo)
+    }
+
+    IvyHttpRepository ivyHttpRepo(String name) {
+        assert !name.startsWith("/")
+        return new IvyHttpRepository(server, "/${name}", ivyRepo(name))
+    }
+
+    MavenHttpRepository getMavenHttpRepo() {
+        return new MavenHttpRepository(server, "/repo", mavenRepo)
+    }
+
+    MavenHttpRepository mavenHttpRepo(String name) {
+        assert !name.startsWith("/")
+        return new MavenHttpRepository(server, "/${name}", mavenRepo(name))
+    }
+
+    MavenHttpRepository mavenHttpRepo(String contextPath, MavenFileRepository backingRepo) {
+        return new MavenHttpRepository(server, contextPath, backingRepo)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
index d3cb29c..0a55f15 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
@@ -23,7 +23,10 @@ import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.test.fixtures.ivy.IvyFileRepository
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.test.fixtures.maven.MavenLocalRepository
+import org.hamcrest.CoreMatchers
 import org.junit.Rule
+import org.junit.runners.model.FrameworkMethod
+import org.junit.runners.model.Statement
 import spock.lang.Specification
 
 /**
@@ -32,8 +35,21 @@ import spock.lang.Specification
  * Plan is to bring features over as needed.
  */
 class AbstractIntegrationSpec extends Specification implements TestDirectoryProvider {
-
-    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider() {
+        @Override
+        Statement apply(Statement base, FrameworkMethod method, Object target) {
+            return super.apply(new Statement() {
+                @Override
+                void evaluate() throws Throwable {
+                    try {
+                        base.evaluate()
+                    } finally {
+                        cleanupWhileTestFilesExist()
+                    }
+                }
+            }, method, target)
+        }
+    }
 
     GradleDistribution distribution = new UnderDevelopmentGradleDistribution()
     GradleExecuter executer = new GradleContextualExecuter(distribution, temporaryFolder)
@@ -43,6 +59,9 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
     private MavenFileRepository mavenRepo
     private IvyFileRepository ivyRepo
 
+    protected void cleanupWhileTestFilesExist() {
+    }
+
     protected TestFile getBuildFile() {
         testDirectory.file('build.gradle')
     }
@@ -56,11 +75,15 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
         testDirectory.file('settings.gradle')
     }
 
+    protected TestNameTestDirectoryProvider getTestDirectoryProvider() {
+        temporaryFolder
+    }
+
     TestFile getTestDirectory() {
         temporaryFolder.testDirectory
     }
 
-    protected TestFile file(Object... path) {
+    TestFile file(Object... path) {
         if (path.length == 1 && path[0] instanceof TestFile) {
             return path[0] as TestFile
         }
@@ -88,6 +111,11 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
         executer
     }
 
+    protected GradleExecuter requireGradleHome() {
+        executer.requireGradleHome()
+        executer
+    }
+
     /**
      * Synonym for succeeds()
      */
@@ -100,7 +128,7 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
     }
 
     protected GradleExecuter withDebugLogging() {
-        executer.withArguments("-d")
+        executer.withArgument("-d")
     }
 
     protected ExecutionResult succeeds(String... tasks) {
@@ -131,6 +159,9 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
     }
     
     protected void executedAndNotSkipped(String... tasks) {
+        if (GradleContextualExecuter.parallel) {
+            return
+        }
         tasks.each {
             assert it in executedTasks
             assert !skippedTasks.contains(it)
@@ -138,6 +169,9 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
     }
 
     protected void skipped(String... tasks) {
+        if (GradleContextualExecuter.parallel) {
+            return
+        }
         tasks.each {
             assert it in executedTasks
             assert skippedTasks.contains(it)
@@ -159,6 +193,14 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
     protected void failureHasCause(String cause) {
         failure.assertHasCause(cause)
     }
+
+    protected void failureDescriptionStartsWith(String description) {
+        failure.assertThatDescription(CoreMatchers.startsWith(description))
+    }
+
+    protected void failureDescriptionContains(String description) {
+        failure.assertThatDescription(CoreMatchers.containsString(description))
+    }
     
     private assertHasResult() {
         assert result != null : "result is null, you haven't run succeeds()"
@@ -197,6 +239,19 @@ class AbstractIntegrationSpec extends Specification implements TestDirectoryProv
         return mavenRepo
     }
 
+    public MavenFileRepository publishedMavenModules(String ... modulesToPublish) {
+        modulesToPublish.each { String notation ->
+            def modules = notation.split("->").reverse()
+            def current
+            modules.each { String module ->
+                def s = new TestDependency(module)
+                def m = mavenRepo.module(s.group, s.name, s.version)
+                current = current? m.dependsOn(current.groupId, current.artifactId, current.version).publish() : m.publish()
+            }
+        }
+        mavenRepo
+    }
+
     public IvyFileRepository ivy(TestFile repo) {
         return new IvyFileRepository(repo)
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationTest.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationTest.java
index 0a9f1ce..6eac7e3 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationTest.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationTest.java
@@ -15,57 +15,34 @@
  */
 package org.gradle.integtests.fixtures;
 
-import com.google.common.collect.Sets;
 import org.gradle.integtests.fixtures.executer.*;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 import org.gradle.test.fixtures.file.TestFile;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.gradle.test.fixtures.ivy.IvyFileRepository;
 import org.gradle.test.fixtures.maven.MavenFileRepository;
-import org.junit.Before;
-import org.junit.ClassRule;
 import org.junit.Rule;
 
 import java.io.File;
-import java.util.Set;
 
 public abstract class AbstractIntegrationTest implements TestDirectoryProvider {
     @Rule public final TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider();
     public final GradleDistribution distribution = new UnderDevelopmentGradleDistribution();
     public final GradleExecuter executer = new GradleContextualExecuter(distribution, this);
 
-    @ClassRule public static final TestNameTestDirectoryProvider SHARED_TEST_DIRECTORY_PROVIDER = new TestNameTestDirectoryProvider();
-    public static final GradleDistribution SHARED_DISTRIBUTION = new UnderDevelopmentGradleDistribution();
-    private static final GradleExecuter SHARED_EXECUTER = new GradleContextualExecuter(SHARED_DISTRIBUTION, SHARED_TEST_DIRECTORY_PROVIDER);
-
-    private static final Set<Class<?>> SHARED_BUILD_RUN_CLASSES = Sets.newHashSet();
-
-    protected boolean useSharedBuild;
-
     private MavenFileRepository mavenRepo;
     private IvyFileRepository ivyRepo;
 
     protected GradleDistribution getDistribution() {
-        return useSharedBuild ? SHARED_DISTRIBUTION : distribution;
+        return distribution;
     }
 
     protected GradleExecuter getExecuter() {
-        return useSharedBuild ? SHARED_EXECUTER : executer;
+        return executer;
     }
 
     protected TestNameTestDirectoryProvider getTestDirectoryProvider() {
-        return useSharedBuild ? SHARED_TEST_DIRECTORY_PROVIDER : testDirectoryProvider;
-    }
-
-    protected void runSharedBuild() {}
-
-    @Before
-    public void doRunSharedBuild() {
-        if (SHARED_BUILD_RUN_CLASSES.add(getClass())) {
-            useSharedBuild = true;
-            runSharedBuild();
-            useSharedBuild = false;
-        }
+        return testDirectoryProvider;
     }
 
     public TestFile getTestDirectory() {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AutoTestedSamplesUtil.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AutoTestedSamplesUtil.groovy
index 3f8e99e..0c4270f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AutoTestedSamplesUtil.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AutoTestedSamplesUtil.groovy
@@ -34,7 +34,7 @@ class AutoTestedSamplesUtil {
     }
 
     String findDir(String dir) {
-        def workDir = SystemProperties.currentDir
+        def workDir = SystemProperties.instance.currentDir
         def candidates = [
             "$workDir/$dir",        //when ran from IDEA
             "$workDir/../../$dir"  //when ran from command line
@@ -56,6 +56,7 @@ I tried looking for a root folder here: $candidates
             sample = sample.replace('<', '<')
             sample = sample.replace('>', '>')
             sample = sample.replace('&', '&')
+            sample = sample.replaceAll(/\{@literal ([^}]+)}/, '$1')
             try {
                 runner.call(file, sample)
             } catch (Exception e) {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
index f72b871..9edb481 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
@@ -15,89 +15,105 @@
  */
 package org.gradle.integtests.fixtures;
 
+import org.gradle.api.JavaVersion;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.api.specs.Spec;
+import org.gradle.integtests.fixtures.jvm.InstalledJvmLocator;
+import org.gradle.integtests.fixtures.jvm.JvmInstallation;
+import org.gradle.internal.SystemProperties;
+import org.gradle.internal.jvm.JavaInfo;
 import org.gradle.internal.jvm.Jre;
 import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
 import org.gradle.internal.os.OperatingSystem;
-import org.gradle.util.GFileUtils;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
+import org.gradle.util.CollectionUtils;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Allows the tests to get hold of an alternative Java installation when needed.
  */
 abstract public class AvailableJavaHomes {
+    private static List<JvmInstallation> jvms;
 
-    private static File getJavaHome(String label) {
-        String value = System.getenv().get(String.format("JDK_%s", label));
-        return value == null ? null : GFileUtils.canonicalise(new File(value));
+    @Nullable
+    public static JavaInfo getJava5() {
+        return getJdk(JavaVersion.VERSION_1_5);
     }
 
     /**
-     * Locates a JVM installation that is different to the current JVM.
+     * Locates a JDK installation for the given version.
+     *
+     * @return null if not found.
      */
-    public static File getBestAlternative() {
-        Jvm jvm = Jvm.current();
-
-        // Use environment variables
-        File javaHome = null;
-        if (jvm.getJavaVersion().isJava6Compatible()) {
-            javaHome = firstAvailable("15", "17");
-        } else if (jvm.getJavaVersion().isJava5Compatible()) {
-            javaHome = firstAvailable("16", "17");
-        }
-        if (javaHome != null) {
-            return javaHome;
+    @Nullable
+    public static JavaInfo getJdk(JavaVersion version) {
+        for (JvmInstallation candidate : getJvms()) {
+            if (candidate.getJavaVersion().equals(version) && candidate.isJdk()) {
+                return Jvm.forHome(candidate.getJavaHome());
+            }
         }
+        return null;
+    }
 
-        if (OperatingSystem.current().isMacOsX()) {
-            // Search in the install dir used by the Apple jvms, followed by the install dir used by the OpenJDK jvms
-            List<File> installDirs = Arrays.asList(new File("/System/Library/Java/JavaVirtualMachines"), new File("/Library/Java/JavaVirtualMachines"));
-            for (File installDir : installDirs) {
-                if (installDir.isDirectory()) {
-                    for (File candidate : installDir.listFiles()) {
-                        javaHome = GFileUtils.canonicalise(new File(candidate, "Contents/Home"));
-                        if (!javaHome.equals(jvm.getJavaHome()) && javaHome.isDirectory() && new File(javaHome, "bin/java").isFile()) {
-                            return javaHome;
-                        }
-                    }
-                }
+    /**
+     * Provides all available JDK installations.
+     *
+     * @return empty list if no JDK can be found.
+     */
+    public static List<JavaInfo> getAvailableJdks() {
+        return CollectionUtils.collect(getJvms(), new Transformer<JavaInfo, JvmInstallation>() {
+            public JavaInfo transform(JvmInstallation candidate) {
+                return Jvm.forHome(candidate.getJavaHome());
             }
-        } else if (OperatingSystem.current().isLinux()) {
-            // Ubuntu specific
-            File installedJvms = new File("/usr/lib/jvm");
-            if (installedJvms.isDirectory()) {
-                for (File candidate : installedJvms.listFiles()) {
-                    javaHome = GFileUtils.canonicalise(candidate);
-                    if (!javaHome.equals(jvm.getJavaHome()) && javaHome.isDirectory() && new File(javaHome, "bin/java").isFile()) {
-                        return javaHome;
-                    }
-                }
+        });
+    }
+
+    /**
+     * Locates a JDK installation that is different to the current JVM, ie for which java.home is different.
+     *
+     * @return null if not found.
+     */
+    @Nullable
+    public static JavaInfo getDifferentJdk() {
+        Jvm jvm = Jvm.current();
+        for (JvmInstallation candidate : getJvms()) {
+            if (candidate.getJavaHome().equals(jvm.getJavaHome())) {
+                continue;
             }
-        } else if (OperatingSystem.current().isWindows()) {
-            //very simple algorithm trying to find java on windows
-            List<File> installDirs = new ArrayList<File>();
-            File candidate = new File("c:/Program Files/Java");
-            if (candidate.isDirectory()) {
-                installDirs.add(candidate);
+
+            // Currently tests implicitly assume a JDK
+            if (!candidate.isJdk()) {
+                continue;
             }
-            // Attempt to look for 32-bit version under 64-bit OS
-            candidate = new File("c:/Program Files (x86)/Java");
-            if (candidate.isDirectory()) {
-                installDirs.add(candidate);
+            return Jvm.forHome(candidate.getJavaHome());
+        }
+
+        return null;
+    }
+
+    /**
+     * Locates a JDK installation that has a different version to the current JVM, ie for which java.version is different.
+     *
+     * @return null if not found.
+     */
+    @Nullable
+    public static JavaInfo getDifferentVersion() {
+        Jvm jvm = Jvm.current();
+        for (JvmInstallation candidate : getJvms()) {
+            if (candidate.getJavaVersion().equals(jvm.getJavaVersion())) {
+                continue;
             }
-            for (File installDir : installDirs) {
-                for (File file : installDir.listFiles()) {
-                    if (file.getName().startsWith("jdk")) {
-                        javaHome = GFileUtils.canonicalise(file);
-                        if (!javaHome.equals(jvm.getJavaHome()) && javaHome.isDirectory() && new File(javaHome, "bin/java.exe").isFile()) {
-                            return javaHome;
-                        }
-                    }
-                }
+            // Currently tests implicitly assume a JDK
+            if (!candidate.isJdk()) {
+                continue;
             }
+            return Jvm.forHome(candidate.getJavaHome());
         }
 
         return null;
@@ -121,13 +137,85 @@ abstract public class AvailableJavaHomes {
         return null;
     }
 
-    public static File firstAvailable(String... labels) {
-        for (String label : labels) {
-            File found = getJavaHome(label);
-            if (found != null) {
-                return found;
+    private static List<JvmInstallation> getJvms() {
+        if (jvms == null) {
+            FileCanonicalizer fileCanonicalizer = NativeServicesTestFixture.getInstance().get(FileCanonicalizer.class);
+            jvms = new ArrayList<JvmInstallation>();
+            jvms.addAll(new DevInfrastructureJvmLocator(fileCanonicalizer).findJvms());
+            jvms.addAll(new InstalledJvmLocator().findJvms());
+            jvms.addAll(new HomeDirJvmLocator(fileCanonicalizer).findJvms());
+            // Order from most recent to least recent
+            Collections.sort(jvms, new Comparator<JvmInstallation>() {
+                public int compare(JvmInstallation o1, JvmInstallation o2) {
+                    return o2.getVersion().compareTo(o1.getVersion());
+                }
+            });
+        }
+        System.out.println("Found the following JVMs:");
+        for (JvmInstallation jvm : jvms) {
+            System.out.println("    " + jvm);
+        }
+        return jvms;
+    }
+
+    private static class DevInfrastructureJvmLocator {
+        final FileCanonicalizer fileCanonicalizer;
+
+        private DevInfrastructureJvmLocator(FileCanonicalizer fileCanonicalizer) {
+            this.fileCanonicalizer = fileCanonicalizer;
+        }
+
+        public List<JvmInstallation> findJvms() {
+            List<JvmInstallation> jvms = new ArrayList<JvmInstallation>();
+            if (OperatingSystem.current().isLinux()) {
+                jvms = addJvm(jvms, JavaVersion.VERSION_1_5, "1.5.0", new File("/opt/jdk/sun-jdk-5"), true, JvmInstallation.Arch.i386);
+                jvms = addJvm(jvms, JavaVersion.VERSION_1_6, "1.6.0", new File("/opt/jdk/sun-jdk-6"), true, JvmInstallation.Arch.x86_64);
+                jvms = addJvm(jvms, JavaVersion.VERSION_1_6, "1.6.0", new File("/opt/jdk/ibm-jdk-6"), true, JvmInstallation.Arch.x86_64);
+                jvms = addJvm(jvms, JavaVersion.VERSION_1_7, "1.7.0", new File("/opt/jdk/oracle-jdk-7"), true, JvmInstallation.Arch.x86_64);
+                jvms = addJvm(jvms, JavaVersion.VERSION_1_8, "1.8.0", new File("/opt/jdk/oracle-jdk-8"), true, JvmInstallation.Arch.x86_64);
             }
+            return CollectionUtils.filter(jvms, new Spec<JvmInstallation>() {
+                public boolean isSatisfiedBy(JvmInstallation element) {
+                    return element.getJavaHome().isDirectory();
+                }
+            });
+        }
+
+        private List<JvmInstallation> addJvm(List<JvmInstallation> jvms, JavaVersion javaVersion, String versionString, File javaHome, boolean jdk, JvmInstallation.Arch arch) {
+            if(javaHome.exists()) {
+                jvms.add(new JvmInstallation(javaVersion, versionString, fileCanonicalizer.canonicalize(javaHome), jdk, arch));
+            }
+            return jvms;
+        }
+    }
+
+    private static class HomeDirJvmLocator {
+        private static final Pattern JDK_DIR = Pattern.compile("jdk(\\d+\\.\\d+\\.\\d+(_\\d+)?)");
+        final FileCanonicalizer fileCanonicalizer;
+
+        private HomeDirJvmLocator(FileCanonicalizer fileCanonicalizer) {
+            this.fileCanonicalizer = fileCanonicalizer;
+        }
+
+        public List<JvmInstallation> findJvms() {
+            Set<File> javaHomes = new HashSet<File>();
+            List<JvmInstallation> jvms = new ArrayList<JvmInstallation>();
+            for (File file : new File(SystemProperties.getInstance().getUserHome()).listFiles()) {
+                Matcher matcher = JDK_DIR.matcher(file.getName());
+                if (!matcher.matches()) {
+                    continue;
+                }
+                File javaHome = fileCanonicalizer.canonicalize(file);
+                if (!javaHomes.add(javaHome)) {
+                    continue;
+                }
+                if (!new File(file, "bin/javac").isFile()) {
+                    continue;
+                }
+                String version = matcher.group(1);
+                jvms.add(new JvmInstallation(JavaVersion.toVersion(version), version, file, true, JvmInstallation.Arch.Unknown));
+            }
+            return jvms;
         }
-        return null;
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ClassFile.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ClassFile.groovy
deleted file mode 100644
index 53fd66b..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ClassFile.groovy
+++ /dev/null
@@ -1,69 +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.integtests.fixtures
-
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.Label
-import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.ClassReader
-
-class ClassFile {
-    final File file
-    boolean hasSourceFile
-    boolean hasLineNumbers
-    boolean hasLocalVars
-
-    ClassFile(File file) {
-        this.file = file
-        def methodVisitor = new MethodVisitor(Opcodes.ASM4) {
-            @Override
-            void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
-                hasLocalVars = true
-            }
-
-            @Override
-            void visitLineNumber(int line, Label start) {
-                hasLineNumbers = true
-            }
-        }
-        def visitor = new ClassVisitor(Opcodes.ASM4) {
-            @Override
-            MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
-                return methodVisitor
-            }
-
-            @Override
-            void visitSource(String source, String debug) {
-                hasSourceFile = true
-            }
-        }
-        new ClassReader(file.bytes).accept(visitor, 0)
-    }
-
-    boolean getDebugIncludesSourceFile() {
-        return hasSourceFile
-    }
-
-    boolean getDebugIncludesLineNumbers() {
-        return hasLineNumbers
-    }
-
-    boolean getDebugIncludesLocalVariables() {
-        return hasLocalVars
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CompilationOutputsFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CompilationOutputsFixture.groovy
new file mode 100644
index 0000000..b07a4e5
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CompilationOutputsFixture.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.integtests.fixtures
+
+import groovy.io.FileType
+
+import static org.apache.commons.io.FilenameUtils.removeExtension
+import static org.spockframework.util.CollectionUtil.asSet
+
+class CompilationOutputsFixture {
+
+    private final File targetDir
+
+    //Tracks outputs in given target dir
+    CompilationOutputsFixture(File targetDir) {
+        assert targetDir != null
+        this.targetDir = targetDir
+    }
+
+    private List<File> snapshot = []
+
+    // Executes optional operation and makes a snapshot of outputs (sets the last modified timestamp to zero for all files)
+    public <T> T snapshot(Closure<T> operation = null) {
+        T result = operation?.call()
+        snapshot.clear()
+        targetDir.eachFileRecurse(FileType.FILES) {
+            it.lastModified = 0
+            snapshot << it
+        }
+        result
+    }
+
+    //asserts none of the files changed/added since last snapshot
+    void noneRecompiled() {
+        recompiledFiles([])
+    }
+
+    //asserts file changed/added since last snapshot
+    void recompiledFile(File file) {
+        recompiledFiles([file])
+    }
+
+    //asserts files changed/added since last snapshot
+    void recompiledFiles(Collection<File> files) {
+        def expectedNames = files.collect({ removeExtension(it.name) }) as Set
+        assert changedFileNames == expectedNames
+    }
+
+    //asserts classes changed/added since last snapshot. Class means file name without extension.
+    void recompiledClasses(String ... classNames) {
+        assert changedFileNames == asSet(classNames)
+    }
+
+    //asserts classes deleted since last snapshot. Class means file name without extension.
+    void deletedClasses(String ... classNames) {
+        def deleted = snapshot.findAll { !it.exists() }.collect { removeExtension(it.name) } as Set
+        assert deleted == asSet(classNames)
+    }
+
+    private Set<String> getChangedFileNames() {
+        // Get all of the files that do not have a zero last modified timestamp
+        def changed = new HashSet()
+        targetDir.eachFileRecurse(FileType.FILES) {
+            if (it.lastModified() > 0) {
+                changed << removeExtension(it.name)
+            }
+        }
+        changed
+    }
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy
index b6cc760..c6943eb 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy
@@ -16,12 +16,12 @@
 package org.gradle.integtests.fixtures
 
 import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.test.fixtures.maven.MavenRepository
 import org.junit.Rule
 import org.junit.runner.RunWith
 import spock.lang.Specification
@@ -32,6 +32,11 @@ abstract class CrossVersionIntegrationSpec extends Specification implements Test
     final GradleDistribution current = new UnderDevelopmentGradleDistribution()
     static GradleDistribution previous
     private MavenFileRepository mavenRepo
+    private TestFile gradleUserHomeDir
+
+    void requireOwnGradleUserHomeDir() {
+        gradleUserHomeDir = file("user-home-dir")
+    }
 
     GradleDistribution getPrevious() {
         return previous
@@ -53,17 +58,19 @@ abstract class CrossVersionIntegrationSpec extends Specification implements Test
         testDirectory.file(path);
     }
 
-    protected MavenRepository getMavenRepo() {
+    protected MavenFileRepository getMavenRepo() {
         if (mavenRepo == null) {
             mavenRepo = new MavenFileRepository(file("maven-repo"))
         }
         return mavenRepo
     }
 
-    def version(GradleDistribution dist) {
+    GradleExecuter version(GradleDistribution dist) {
         def executer = dist.executer(temporaryFolder)
+        if (gradleUserHomeDir) {
+            executer.withGradleUserHomeDir(gradleUserHomeDir)
+        }
         executer.withDeprecationChecksDisabled()
         executer.inDirectory(testDirectory)
-        return executer;
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/EnableModelDsl.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/EnableModelDsl.java
new file mode 100644
index 0000000..ae6a81e
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/EnableModelDsl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures;
+
+import org.gradle.api.Action;
+import org.gradle.integtests.fixtures.executer.GradleExecuter;
+
+public class EnableModelDsl {
+
+    public static void enable(GradleExecuter executer) {
+        executer.beforeExecute(new Action<GradleExecuter>() {
+            public void execute(GradleExecuter gradleExecuter) {
+                gradleExecuter.withArgument("-Dorg.gradle.model.dsl=true");
+            }
+        });
+    }
+
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ForkScalaCompileInDaemonModeFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ForkScalaCompileInDaemonModeFixture.groovy
new file mode 100644
index 0000000..743b862
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ForkScalaCompileInDaemonModeFixture.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.integtests.fixtures.executer.InitScriptExecuterFixture
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.junit.runners.model.FrameworkMethod
+import org.junit.runners.model.Statement
+
+
+class ForkScalaCompileInDaemonModeFixture extends InitScriptExecuterFixture {
+    ForkScalaCompileInDaemonModeFixture(GradleExecuter executer, TestDirectoryProvider testDir) {
+        super(executer, testDir)
+    }
+
+    @Override
+    String initScriptContent() {
+        return """
+allprojects {
+    tasks.withType(ScalaCompile) {
+        scalaCompileOptions.fork = true
+        scalaCompileOptions.useAnt = false
+    }
+    tasks.withType(ScalaDoc) {
+        doFirst {
+            throw new GradleException("Can't execute scaladoc while testing with the daemon due to permgen exhaustion")
+        }
+    }
+}
+"""
+    }
+
+    @Override
+    Statement apply(Statement base, FrameworkMethod method, Object target) {
+        if (GradleContextualExecuter.isDaemon()) {
+            return super.apply(base, method, target)
+        } else {
+            return base;
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionIntegrationSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionIntegrationSpec.groovy
index 93d8ba1..e8d552b 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionIntegrationSpec.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionIntegrationSpec.groovy
@@ -21,9 +21,9 @@ import org.gradle.util.VersionNumber
 
 @RunWith(MultiVersionSpecRunner)
 abstract class MultiVersionIntegrationSpec extends AbstractIntegrationSpec {
-    static String version
+    static def version
 
     static VersionNumber getVersionNumber() {
-        VersionNumber.parse(version)
+        VersionNumber.parse(version.toString())
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionSpecRunner.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionSpecRunner.groovy
index 0d22fe2..fa0da0f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionSpecRunner.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/MultiVersionSpecRunner.groovy
@@ -20,33 +20,41 @@ package org.gradle.integtests.fixtures
  * Runs the target test class against the versions specified in a {@link TargetVersions} or {@link TargetCoverage}
  */
 class MultiVersionSpecRunner extends AbstractMultiTestRunner {
+
+    public static final String MULTI_VERSION_SYS_PROP = "org.gradle.integtest.multiversion"
+
     MultiVersionSpecRunner(Class<?> target) {
         super(target)
     }
 
     @Override
     protected void createExecutions() {
+        boolean enableAllVersions = "all".equals(System.getProperty(MULTI_VERSION_SYS_PROP, "default"))
         def versions = target.getAnnotation(TargetVersions)
         def coverage = target.getAnnotation(TargetCoverage)
+        def versionUnderTest
+
         if (versions != null) {
-            versions.value().each { add(new VersionExecution(it)) }
+            versionUnderTest = enableAllVersions ? versions.value() : [versions.value().last()]
         } else if (coverage != null) {
-            coverage.value().newInstance(target, target).call().each { add(new VersionExecution(it)) }
+            def coverageTargets = coverage.value().newInstance(target, target).call()
+            versionUnderTest = enableAllVersions ? coverageTargets : [coverageTargets.last()]
         } else {
             throw new RuntimeException("Target class '$target' is not annotated with @${TargetVersions.simpleName} nor with @${TargetCoverage.simpleName}.")
         }
+        versionUnderTest.each { add(new VersionExecution(it)) }
     }
 
     private static class VersionExecution extends AbstractMultiTestRunner.Execution {
-        final String version
+        final def version
 
-        VersionExecution(String version) {
+        VersionExecution(def version) {
             this.version = version
         }
 
         @Override
         protected String getDisplayName() {
-            return version
+            return version.toString()
         }
 
         @Override
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/Sample.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/Sample.java
index fe92819..ccca452 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/Sample.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/Sample.java
@@ -33,22 +33,32 @@ import org.slf4j.LoggerFactory;
 public class Sample implements MethodRule {
     private final Logger logger = LoggerFactory.getLogger(Sample.class);
     private final String defaultSampleName;
+    private final String testSampleDirName;
 
     private TestFile sampleDir;
     private TestDirectoryProvider testDirectoryProvider;
 
+    public Sample(TestDirectoryProvider testDirectoryProvider) {
+        this(testDirectoryProvider, null);
+    }
+
     public Sample(TestDirectoryProvider testDirectoryProvider, String defaultSampleName) {
-        this.testDirectoryProvider = testDirectoryProvider;
-        this.defaultSampleName = defaultSampleName;
+        this(testDirectoryProvider, defaultSampleName, null);
     }
 
-    public Sample(TestDirectoryProvider testDirectoryProvider) {
-        this(testDirectoryProvider, null);
+    public Sample(TestDirectoryProvider testDirectoryProvider, String defaultSampleName, String testSampleDirName) {
+        this.testDirectoryProvider = testDirectoryProvider;
+        this.defaultSampleName = defaultSampleName;
+        this.testSampleDirName = testSampleDirName;
     }
 
     public Statement apply(final Statement base, FrameworkMethod method, Object target) {
         final String sampleName = getSampleName(method);
-        sampleDir = sampleName == null ? null : testDirectoryProvider.getTestDirectory().file(sampleName);
+        if (testSampleDirName != null) {
+            sampleDir = testDirectoryProvider.getTestDirectory().file(testSampleDirName);
+        } else {
+            sampleDir = sampleName == null ? null : testDirectoryProvider.getTestDirectory().file(sampleName);
+        }
 
         return new Statement() {
             @Override
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SourceFile.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SourceFile.java
new file mode 100644
index 0000000..8ecafc7
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SourceFile.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.integtests.fixtures;
+
+import com.google.common.base.Joiner;
+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);
+        writeToFile(file);
+        return file;
+    }
+
+    public void writeToFile(TestFile file) {
+        if (file.exists()) {
+            file.write("");
+        }
+        file.write(content);
+    }
+
+    public String withPath(String basePath) {
+        return Joiner.on('/').join(basePath, path, name);
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestDependency.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestDependency.groovy
new file mode 100644
index 0000000..5bd1f5d
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestDependency.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+
+
+
+package org.gradle.integtests.fixtures
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.api.Nullable
+
+class TestDependency {
+
+    final String group
+    final String name
+    final String version
+    final TestDependency pointsTo
+
+    TestDependency(String notation) {
+        def n = notation.split("->")
+        assert n.length <= 2
+
+        def s = n[0].split(":")
+        if (s.length == 3) {
+            this.group = s[0]
+            this.name = s[1]
+            this.version = s[2]
+        } else if (s.length == 2) {
+            if (StringUtils.isNumeric(s[1])) {
+                this.group = "org"
+                this.name = s[0]
+                this.version = s[1]
+            } else {
+                this.group = s[0]
+                this.name = s[1]
+                this.version = "1"
+            }
+        } else if (s.length == 1) {
+            this.group = "org"
+            this.name = s[0]
+            this.version = "1"
+        } else {
+            throw new IllegalArgumentException("Unable to create test dependency for input '$notation'")
+        }
+
+        this.pointsTo = (n.length > 1)? new TestDependency(n[1]) : null
+    }
+
+    String getNotation() {
+        "$group:$name:$version"
+    }
+
+    String getJarName() {
+        "$name-${version}.jar"
+    }
+
+    @Nullable TestDependency getPointsTo() {
+        this.pointsTo
+    }
+
+    @Nullable TestDependency getDependsOn() {
+        this.pointsTo
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
index d57594b..fec83f2 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
@@ -86,7 +86,7 @@ class TestNGExecutionResult implements TestExecutionResult {
     }
 }
 
-private class TestNgTestClassExecutionResult implements TestClassExecutionResult {
+class TestNgTestClassExecutionResult implements TestClassExecutionResult {
     def String testClass
     def GPathResult testClassNode
 
@@ -150,6 +150,11 @@ private class TestNgTestClassExecutionResult implements TestClassExecutionResult
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher) {
+        throw new UnsupportedOperationException();
+    }
+
     TestClassExecutionResult assertConfigMethodPassed(String name) {
         def testMethodNode = findConfigMethod(name)
         assert testMethodNode. at status as String == 'PASS'
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/UserGuideSamplesRunner.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/UserGuideSamplesRunner.groovy
deleted file mode 100644
index f39943b..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/UserGuideSamplesRunner.groovy
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.fixtures
-
-import com.google.common.collect.ArrayListMultimap
-import groovy.io.PlatformLineWriter
-import org.apache.tools.ant.taskdefs.Delete
-import org.gradle.integtests.fixtures.executer.*
-import org.gradle.internal.SystemProperties
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.AntUtil
-import org.gradle.util.TextUtil
-import org.junit.Assert
-import org.junit.runner.Description
-import org.junit.runner.Runner
-import org.junit.runner.notification.Failure
-import org.junit.runner.notification.RunNotifier
-
-import java.util.regex.Pattern
-
-class UserGuideSamplesRunner extends Runner {
-    private static final String NL = SystemProperties.lineSeparator
-
-    private Class<?> testClass
-    private Description description
-    private Map<Description, SampleRun> samples
-    private TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-    private GradleDistribution dist = new UnderDevelopmentGradleDistribution()
-    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext()
-    private GradleExecuter executer = new GradleContextualExecuter(dist, temporaryFolder)
-    private Pattern dirFilter
-    private List excludes
-    private TestFile baseExecutionDir = temporaryFolder.testDirectory
-
-    UserGuideSamplesRunner(Class<?> testClass) {
-        this.testClass = testClass
-        this.description = Description.createSuiteDescription(testClass)
-        this.dirFilter = initDirFilterPattern()
-        this.excludes = initExcludes()
-        samples = new LinkedHashMap()
-        for (sample in getScriptsForSamples(buildContext.userGuideInfoDir)) {
-            if (shouldInclude(sample)) {
-                Description childDescription = Description.createTestDescription(testClass, sample.id)
-                description.addChild(childDescription)
-                samples.put(childDescription, sample)
-
-                println "Sample $sample.id dir: $sample.subDir"
-                sample.runs.each { println "    args: $it.args expect: $it.outputFile" }
-            }
-        }
-
-        // have to copy everything upfront because build scripts of some samples refer to files of other samples
-        buildContext.samplesDir.copyTo(baseExecutionDir)
-    }
-
-    private Pattern initDirFilterPattern() {
-        String filter = System.properties["org.gradle.userguide.samples.filter"]
-        filter ? Pattern.compile(filter) : null
-    }
-
-    private List initExcludes() {
-        List excludes = []
-        String excludesString = System.properties["org.gradle.userguide.samples.exclude"] ?: "";
-        excludesString.split(',').each {
-            excludes.add it
-        }
-        return excludes
-    }
-
-    Description getDescription() {
-        description
-    }
-
-    private boolean shouldInclude(SampleRun run) {
-        if (excludes.contains(run.id)) {
-            return false
-        }
-        dirFilter ? run.subDir ==~ dirFilter : true
-    }
-
-    void run(RunNotifier notifier) {
-        for (childDescription in description.children) {
-            notifier.fireTestStarted(childDescription)
-            def sampleRun = samples.get(childDescription)
-            try {
-                cleanup(sampleRun)
-                for (run in sampleRun.runs) {
-                    if (run.brokenForParallel && GradleContextualExecuter.parallel) {
-                        continue
-                    }
-                    runSample(run)
-                }
-            } catch (Throwable t) {
-                notifier.fireTestFailure(new Failure(childDescription, t))
-            }
-            notifier.fireTestFinished(childDescription)
-        }
-    }
-
-    private void cleanup(SampleRun run) {
-        // Clean up previous runs in the same subdir
-        File rootProjectDir = temporaryFolder.testDirectory.file(run.subDir)
-        if (rootProjectDir.exists()) {
-            def delete = new Delete()
-            delete.dir = rootProjectDir
-            delete.includes = "**/.gradle/** **/build/**"
-            AntUtil.execute(delete)
-        }
-    }
-
-    private void runSample(GradleRun run) {
-        try {
-            println("Test Id: $run.id, dir: $run.subDir, execution dir: $run.executionDir args: $run.args")
-
-            executer.noExtraLogging().inDirectory(run.executionDir).withArguments(run.args as String[]).withEnvironmentVars(run.envs)
-
-            def result = run.expectFailure ? executer.runWithFailure() : executer.run()
-            if (run.outputFile) {
-                def expectedResult = replaceWithPlatformNewLines(buildContext.userGuideOutputDir.file(run.outputFile).text)
-                expectedResult = replaceWithRealSamplesDir(expectedResult)
-                try {
-                    result.assertOutputEquals(expectedResult, run.ignoreExtraLines, run.ignoreLineOrder)
-                } catch (AssertionError e) {
-                    println 'Expected Result:'
-                    println expectedResult
-                    println 'Actual Result:'
-                    println result.output
-                    println '---'
-                    throw e
-                }
-            }
-
-            run.files.each { path ->
-                println "  checking file '$path' exists"
-                def file = new File(run.executionDir, path).canonicalFile
-                Assert.assertTrue("Expected file '$file' does not exist.", file.exists())
-                Assert.assertTrue("Expected file '$file' is not a file.", file.isFile())
-            }
-            run.dirs.each { path ->
-                println "  checking directory '$path' exists"
-                def file = new File(run.executionDir, path).canonicalFile
-                Assert.assertTrue("Expected directory '$file' does not exist.", file.exists())
-                Assert.assertTrue("Expected directory '$file' is not a directory.", file.isDirectory())
-            }
-        } catch (Throwable e) {
-            throw new AssertionError("Integration test for sample '$run.id' in dir '$run.subDir' with args $run.args failed:${NL}$e.message").initCause(e)
-        }
-    }
-
-    private String replaceWithPlatformNewLines(String text) {
-        def stringWriter = new StringWriter()
-        new PlatformLineWriter(stringWriter).withWriter { it.write(text) }
-        stringWriter.toString()
-    }
-
-    private String replaceWithRealSamplesDir(String text) {
-        def normalisedSamplesDir = TextUtil.normaliseFileSeparators(baseExecutionDir.absolutePath)
-        return text.replaceAll(Pattern.quote('/home/user/gradle/samples'), normalisedSamplesDir)
-    }
-
-    private Collection<SampleRun> getScriptsForSamples(File userguideInfoDir) {
-        def samplesXml = new File(userguideInfoDir, 'samples.xml')
-        assertSamplesGenerated(samplesXml.exists())
-        def samples = new XmlParser().parse(samplesXml)
-        def samplesByDir = ArrayListMultimap.create()
-
-        def children = samples.children()
-        assertSamplesGenerated(!children.isEmpty())
-
-        children.each {Node sample ->
-            def id = sample.'@id'
-            def dir = sample.'@dir'
-            def args = sample.'@args'
-            def outputFile = sample.'@outputFile'
-            boolean ignoreExtraLines = Boolean.valueOf(sample.'@ignoreExtraLines')
-            boolean ignoreLineOrder = Boolean.valueOf(sample.'@ignoreLineOrder')
-            boolean expectFailure = Boolean.valueOf(sample.'@expectFailure')
-
-            def run = new GradleRun(id: id)
-            run.subDir = dir
-            run.args = args ? args.split('\\s+') as List : []
-            run.outputFile = outputFile
-            run.ignoreExtraLines = ignoreExtraLines as boolean
-            run.ignoreLineOrder = ignoreLineOrder as boolean
-            run.expectFailure = expectFailure as boolean
-
-            sample.file.each { file -> run.files << file.'@path' }
-            sample.dir.each { file -> run.dirs << file.'@path' }
-
-            samplesByDir.put(dir, run)
-        }
-
-        // Some custom values
-        samplesByDir.get('userguide/tutorial/properties').each { it.envs['ORG_GRADLE_PROJECT_envProjectProp'] = 'envPropertyValue' }
-        samplesByDir.get('userguide/buildlifecycle/taskExecutionEvents')*.expectFailure = true
-        samplesByDir.get('userguide/buildlifecycle/buildProjectEvaluateEvents')*.expectFailure = true
-        samplesByDir.get('userguide/tasks/finalizersWithFailure')*.expectFailure = true
-        samplesByDir.get('userguide/multiproject/dependencies/firstMessages/messages')*.brokenForParallel = true
-        samplesByDir.get('userguide/multiproject/dependencies/messagesHack/messages')*.brokenForParallel = true
-
-        Map<String, SampleRun> samplesById = new TreeMap<String, SampleRun>()
-
-        // Remove duplicates for a given directory.
-        samplesByDir.asMap().values().collect {List<GradleRun> dirSamples ->
-            def runs = dirSamples.findAll { it.mustRun }
-            if (!runs) {
-                // No samples in this dir have any args, so just run gradle tasks in the dir
-                def sample = dirSamples[0]
-                sample.args = ['tasks']
-                sample
-            } else {
-                return runs
-            }
-        }.flatten().each { GradleRun run ->
-            // Collect up into sample runs
-            def sampleRun = samplesById[run.id]
-            if (!sampleRun) {
-                sampleRun = new SampleRun(id: run.id, subDir: run.subDir)
-                samplesById[run.id] = sampleRun
-            }
-            sampleRun.runs << run
-        }
-
-        return samplesById.values()
-    }
-
-    private void assertSamplesGenerated(boolean assertion) {
-        assert assertion : """Couldn't find any samples. Most likely, samples.xml was not generated.
-Please run 'gradle docs:userguideDocbook' first"""
-    }
-
-    private class GradleRun {
-        String id
-        List args = []
-        String subDir
-        Map envs = [:]
-        String outputFile
-        boolean expectFailure
-        boolean ignoreExtraLines
-        boolean ignoreLineOrder
-        boolean brokenForParallel
-        List files = []
-        List dirs = []
-
-        boolean getMustRun() {
-            return args || files || dirs
-        }
-
-        File getExecutionDir() {
-            new File(baseExecutionDir, subDir)
-        }
-    }
-
-    private class SampleRun {
-        String id
-        String subDir
-        List<GradleRun> runs = []
-    }
-}
-
-
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/WellBehavedPluginTest.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/WellBehavedPluginTest.groovy
index 7f16a68..9347f71 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/WellBehavedPluginTest.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/WellBehavedPluginTest.groovy
@@ -16,22 +16,37 @@
 
 package org.gradle.integtests.fixtures
 
+import org.gradle.api.internal.plugins.DefaultPluginManager
+import org.gradle.util.GUtil
+
 import java.util.regex.Pattern
 
 abstract class WellBehavedPluginTest extends AbstractIntegrationSpec {
 
-    String getPluginId() {
+    String getPluginName() {
         def matcher = Pattern.compile("(\\w+)Plugin(GoodBehaviour)?(Integ(ration)?)?Test").matcher(getClass().simpleName)
         if (matcher.matches()) {
-            return matcher.group(1).toLowerCase()
+            return GUtil.toWords(matcher.group(1), (char) '-')
         }
         throw new UnsupportedOperationException("Cannot determine plugin id from class name '${getClass().simpleName}.")
     }
-    
+
+    String getQualifiedPluginId() {
+        DefaultPluginManager.CORE_PLUGIN_PREFIX + getPluginName()
+    }
+
     String getMainTask() {
         return "assemble"
     }
 
+    def "can apply plugin unqualified"() {
+        given:
+        applyPluginUnqualified()
+
+        expect:
+        succeeds mainTask
+    }
+
     def "plugin does not force creation of build dir during configuration"() {
         given:
         applyPlugin()
@@ -52,6 +67,11 @@ abstract class WellBehavedPluginTest extends AbstractIntegrationSpec {
     }
 
     protected applyPlugin(File target = buildFile) {
-        target << "apply plugin: '${getPluginId()}'\n"
+        target << "apply plugin: '${getQualifiedPluginId()}'\n"
     }
+
+    protected applyPluginUnqualified(File target = buildFile) {
+        target << "apply plugin: '${getPluginName()}'\n"
+    }
+
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractDelegatingGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractDelegatingGradleExecuter.java
index c1c3f86..8d98a77 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractDelegatingGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractDelegatingGradleExecuter.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.integtests.fixtures.executer;
 
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 
 public abstract class AbstractDelegatingGradleExecuter extends AbstractGradleExecuter {
@@ -34,10 +33,6 @@ public abstract class AbstractDelegatingGradleExecuter extends AbstractGradleExe
         return configureExecuter().runWithFailure();
     }
 
-    public DaemonRegistry getDaemonRegistry() {
-        return configureExecuter().getDaemonRegistry();
-    }
-
     public void assertCanExecute() throws AssertionError {
         configureExecuter().assertCanExecute();
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
index 28e7093..598a0ec 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
@@ -65,11 +65,14 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
     private File settingsFile;
     private InputStream stdin;
     private String defaultCharacterEncoding;
+    private String tmpDir;
+    private Locale defaultLocale;
     private int daemonIdleTimeoutSecs = 60;
     private File daemonBaseDir = buildContext.getDaemonBaseDir();
     private final List<String> gradleOpts = new ArrayList<String>();
     private boolean noDefaultJvmArgs;
     private boolean requireGradleHome;
+    private boolean daemonStartingMessageDisabled = true;
 
     private boolean deprecationChecksOn = true;
     private boolean eagerClassLoaderCreationChecksOn = true;
@@ -108,6 +111,8 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         environmentVars.clear();
         stdin = null;
         defaultCharacterEncoding = null;
+        tmpDir = null;
+        defaultLocale = null;
         noDefaultJvmArgs = false;
         deprecationChecksOn = true;
         stackTraceChecksOn = true;
@@ -194,6 +199,12 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         if (defaultCharacterEncoding != null) {
             executer.withDefaultCharacterEncoding(defaultCharacterEncoding);
         }
+        if (tmpDir != null) {
+            executer.withTmpDir(tmpDir);
+        }
+        if (defaultLocale != null) {
+            executer.withDefaultLocale(defaultLocale);
+        }
         executer.withGradleOpts(gradleOpts.toArray(new String[gradleOpts.size()]));
         if (noDefaultJvmArgs) {
             executer.withNoDefaultJvmArgs();
@@ -212,6 +223,9 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         if (requireGradleHome) {
             executer.requireGradleHome();
         }
+        if (!daemonStartingMessageDisabled) {
+            executer.withDaemonStartingMessageEnabled();
+        }
 
         return executer;
     }
@@ -302,10 +316,24 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         return this;
     }
 
+    public GradleExecuter withTmpDir(String tmpDir) {
+        this.tmpDir = tmpDir;
+        return this;
+    }
+
     public String getDefaultCharacterEncoding() {
         return defaultCharacterEncoding == null ? Charset.defaultCharset().name() : defaultCharacterEncoding;
     }
 
+    public GradleExecuter withDefaultLocale(Locale defaultLocale) {
+        this.defaultLocale = defaultLocale;
+        return this;
+    }
+
+    public Locale getDefaultLocale() {
+        return defaultLocale;
+    }
+
     public GradleExecuter withSearchUpwards() {
         searchUpwards = true;
         return this;
@@ -374,7 +402,7 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
                 result.append(" ");
             }
             if (jvmArg.contains(" ")) {
-                assert !jvmArg.contains("\"");
+                assert !jvmArg.contains("\"") : "jvmArg '" + jvmArg + "' contains '\"'";
                 result.append('"');
                 result.append(jvmArg);
                 result.append('"');
@@ -507,12 +535,21 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         properties.put(GradleProperties.DAEMON_BASE_DIR_PROPERTY, daemonBaseDir.getAbsolutePath());
         properties.put(DeprecationLogger.ORG_GRADLE_DEPRECATION_TRACE_PROPERTY_NAME, "true");
 
-        String tmpDirPath = getTmpDir().createDir().getAbsolutePath();
+        String tmpDirPath = tmpDir;
+        if (tmpDirPath == null) {
+            tmpDirPath = getDefaultTmpDir().createDir().getAbsolutePath();
+        }
         if (!tmpDirPath.contains(" ") || getDistribution().isSupportsSpacesInGradleAndJavaOpts()) {
             properties.put("java.io.tmpdir", tmpDirPath);
         }
 
         properties.put("file.encoding", getDefaultCharacterEncoding());
+        Locale locale = getDefaultLocale();
+        if (locale != null) {
+            properties.put("user.language", locale.getLanguage());
+            properties.put("user.country", locale.getCountry());
+            properties.put("user.variant", locale.getVariant());
+        }
 
         if (eagerClassLoaderCreationChecksOn) {
             properties.put(DefaultClassLoaderScope.STRICT_MODE_PROPERTY, "true");
@@ -644,7 +681,7 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         return this;
     }
 
-    protected TestFile getTmpDir() {
+    protected TestFile getDefaultTmpDir() {
         return new TestFile(getTestDirectoryProvider().getTestDirectory(), "tmp");
     }
 
@@ -665,4 +702,13 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
         this.requireGradleHome = true;
         return this;
     }
+
+    public GradleExecuter withDaemonStartingMessageEnabled() {
+        daemonStartingMessageDisabled = false;
+        return this;
+    }
+
+    public boolean isDaemonStartingMessageDisabled() {
+        return daemonStartingMessageDisabled;
+    }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AnyOrderOutputMatcher.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AnyOrderOutputMatcher.groovy
index d5a710f..38f83f1 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AnyOrderOutputMatcher.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AnyOrderOutputMatcher.groovy
@@ -25,7 +25,7 @@ import org.gradle.internal.SystemProperties
  * Checks that all lines contained in the expected output are present in the actual output, in any order.
  */
 class AnyOrderOutputMatcher extends SequentialOutputMatcher {
-    private static final String NL = SystemProperties.lineSeparator
+    private static final String NL = SystemProperties.instance.lineSeparator
 
     protected void assertOutputLinesMatch(List<String> expectedLines, List<String> actualLines, boolean ignoreExtraLines, String actual) {
         List<String> unmatchedLines = new ArrayList<String>(actualLines)
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DaemonGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DaemonGradleExecuter.java
index ac52891..fe30ff8 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DaemonGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DaemonGradleExecuter.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.integtests.fixtures.executer;
 
-import org.gradle.api.JavaVersion;
 import org.gradle.api.Transformer;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 
@@ -24,6 +23,7 @@ import java.util.List;
 
 import static java.util.Arrays.asList;
 import static org.apache.commons.collections.CollectionUtils.containsAny;
+import static org.gradle.launcher.daemon.client.DefaultDaemonConnector.DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY;
 import static org.gradle.util.CollectionUtils.collect;
 import static org.gradle.util.CollectionUtils.join;
 
@@ -44,7 +44,7 @@ public class DaemonGradleExecuter extends ForkingGradleExecuter {
             }
         }
 
-        // Workaround for http://issues.gradle.org/browse/GRADLE-2625
+        // Workaround for https://issues.gradle.org/browse/GRADLE-2625
         if (getUserHomeDir() != null) {
             args.add(String.format("-Duser.home=%s", getUserHomeDir().getPath()));
         }
@@ -54,20 +54,20 @@ public class DaemonGradleExecuter extends ForkingGradleExecuter {
 
     @Override
     protected List<String> getGradleOpts() {
+        List<String> gradleOpts = new ArrayList<String>(super.getGradleOpts());
+        if (isDaemonStartingMessageDisabled()) {
+            gradleOpts.add("-D" + DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY + "=true");
+        }
         if (isNoDefaultJvmArgs()) {
-            return super.getGradleOpts();
+            return gradleOpts;
         } else {
-            // Workaround for http://issues.gradle.org/browse/GRADLE-2629
+            // Workaround for https://issues.gradle.org/browse/GRADLE-2629
             // Instead of just adding these as standalone opts, we need to add to
             // -Dorg.gradle.jvmArgs in order for them to be effectual
             List<String> jvmArgs = new ArrayList<String>(4);
             jvmArgs.add("-XX:MaxPermSize=320m");
             jvmArgs.add("-XX:+HeapDumpOnOutOfMemoryError");
             jvmArgs.add("-XX:HeapDumpPath=" + buildContext.getGradleUserHomeDir().getAbsolutePath());
-            if (JavaVersion.current().isJava5()) {
-                jvmArgs.add("-XX:+CMSPermGenSweepingEnabled");
-                jvmArgs.add("-Dcom.sun.management.jmxremote");
-            }
 
             String quotedArgs = join(" ", collect(jvmArgs, new Transformer<String, String>() {
                 public String transform(String input) {
@@ -75,7 +75,6 @@ public class DaemonGradleExecuter extends ForkingGradleExecuter {
                 }
             }));
 
-            List<String> gradleOpts = new ArrayList<String>(super.getGradleOpts());
             gradleOpts.add("-Dorg.gradle.jvmArgs=" +  quotedArgs);
             return gradleOpts;
         }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.java
index 0fd5660..7a4329e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.java
@@ -65,8 +65,10 @@ public class DefaultGradleDistribution implements GradleDistribution {
         if (isVersion("0.9-rc-1")) {
             return jvm.getJavaVersion().isJava6Compatible();
         }
-
-        return jvm.getJavaVersion().isJava5Compatible();
+        if (isSameOrOlder("1.12")) {
+            return jvm.getJavaVersion().isJava5Compatible();
+        }
+        return jvm.getJavaVersion().isJava6Compatible();
     }
 
     public boolean worksWith(OperatingSystem os) {
@@ -99,7 +101,7 @@ public class DefaultGradleDistribution implements GradleDistribution {
     }
 
     public boolean isOpenApiSupported() {
-        return isSameOrNewer("0.9-rc-1");
+        return isSameOrNewer("0.9-rc-1") && !isSameOrNewer("2.0-rc-1");
     }
 
     public boolean isToolingApiSupported() {
@@ -113,8 +115,20 @@ public class DefaultGradleDistribution implements GradleDistribution {
         return true;
     }
 
+    public boolean isToolingApiDaemonBaseDirSupported() {
+        return isSameOrNewer("2.2-rc-1");
+    }
+
     public VersionNumber getArtifactCacheLayoutVersion() {
-        if (isSameOrNewer("1.12-rc-1")) {
+        if (isSameOrNewer("2.4-rc-1")) {
+            return VersionNumber.parse("2.15");
+        } else if (isSameOrNewer("2.2-rc-1")) {
+            return VersionNumber.parse("2.14");
+        } else if (isSameOrNewer("2.1-rc-3")) {
+            return VersionNumber.parse("2.13");
+        } else if (isSameOrNewer("2.0-rc-1")) {
+            return VersionNumber.parse("2.12");
+        } else if (isSameOrNewer("1.12-rc-1")) {
             return VersionNumber.parse("2.6");
         } else if (isSameOrNewer("1.11-rc-1")) {
             return VersionNumber.parse("2.2");
@@ -153,6 +167,10 @@ public class DefaultGradleDistribution implements GradleDistribution {
         return true;
     }
 
+    public boolean isWrapperSupportsGradleUserHomeCommandLineOption() {
+        return isSameOrNewer("1.7");
+    }
+
     public boolean isSupportsSpacesInGradleAndJavaOpts() {
         return isSameOrNewer("1.0-milestone-5");
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DownloadableGradleDistribution.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DownloadableGradleDistribution.groovy
index b945d79..67f1f5f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DownloadableGradleDistribution.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DownloadableGradleDistribution.groovy
@@ -16,13 +16,12 @@
 
 package org.gradle.integtests.fixtures.executer
 
-import org.gradle.CacheUsage
 import org.gradle.api.Action
 import org.gradle.cache.PersistentCache
 import org.gradle.cache.internal.*
 import org.gradle.cache.internal.locklistener.NoOpFileLockContentionHandler
-import org.gradle.internal.nativeplatform.services.NativeServices
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.gradle.util.GradleVersion
 
 import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode
@@ -35,7 +34,7 @@ abstract class DownloadableGradleDistribution extends DefaultGradleDistribution
         return new DefaultCacheFactory(
                 new DefaultFileLockManager(
                         new DefaultProcessMetaDataProvider(
-                                NativeServices.getInstance().get(org.gradle.internal.nativeplatform.ProcessEnvironment)),
+                                NativeServicesTestFixture.getInstance().get(org.gradle.internal.nativeintegration.ProcessEnvironment)),
                         20 * 60 * 1000 // allow up to 20 minutes to download a distribution
                 , new NoOpFileLockContentionHandler()))
     }
@@ -67,7 +66,7 @@ abstract class DownloadableGradleDistribution extends DefaultGradleDistribution
                 super.binDistribution.usingNativeTools().unzipTo(versionDir)
             }
             //noinspection GrDeprecatedAPIUsage
-            cache = CACHE_FACTORY.open(versionDir, version.version, CacheUsage.ON, null, [:], mode(FileLockManager.LockMode.Shared).useCrossVersionImplementation(), downloadAction as Action)
+            cache = CACHE_FACTORY.open(versionDir, version.version, null, [:], mode(FileLockManager.LockMode.Shared).useCrossVersionImplementation(), downloadAction as Action)
         }
 
         super.binDistribution.assertIsFile()
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/EmbeddedDaemonGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/EmbeddedDaemonGradleExecuter.java
deleted file mode 100644
index 9a2f3f6..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/EmbeddedDaemonGradleExecuter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.fixtures.executer;
-
-import org.gradle.StartParameter;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.initialization.DefaultCommandLineConverter;
-import org.gradle.launcher.cli.ExecuteBuildAction;
-import org.gradle.launcher.daemon.client.DaemonClient;
-import org.gradle.launcher.daemon.client.EmbeddedDaemonClientServices;
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
-import org.gradle.launcher.exec.BuildActionParameters;
-import org.gradle.launcher.exec.DefaultBuildActionParameters;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.logging.LoggingServiceRegistry;
-import org.gradle.logging.internal.StreamBackedStandardOutputListener;
-import org.gradle.test.fixtures.file.TestDirectoryProvider;
-
-import java.lang.management.ManagementFactory;
-
-class EmbeddedDaemonGradleExecuter extends AbstractGradleExecuter {
-    private final EmbeddedDaemonClientServices daemonClientServices = new EmbeddedDaemonClientServices(LoggingServiceRegistry.newEmbeddableLogging());
-
-    EmbeddedDaemonGradleExecuter(GradleDistribution distribution, TestDirectoryProvider testDirectoryProvider) {
-        super(distribution, testDirectoryProvider);
-    }
-
-    public DaemonRegistry getDaemonRegistry() {
-        return daemonClientServices.get(DaemonRegistry.class);
-    }
-
-    public void assertCanExecute() throws AssertionError {
-    }
-
-    protected ExecutionResult doRun() {
-        return doRun(false);
-    }
-
-    protected ExecutionFailure doRunWithFailure() {
-        return (ExecutionFailure)doRun(true);
-    }
-
-    protected ExecutionResult doRun(boolean expectFailure) {
-        StringBuilder output = new StringBuilder();
-        StringBuilder error = new StringBuilder();
-
-        LoggingManagerInternal loggingManager = createLoggingManager(output, error);
-        loggingManager.start();
-
-        ExecuteBuildAction buildAction = createBuildAction();
-        BuildActionParameters buildActionParameters = createBuildActionParameters();
-        DaemonClient daemonClient = daemonClientServices.get(DaemonClient.class);
-
-        Exception failure = null;
-        try {
-            daemonClient.execute(buildAction, buildActionParameters);
-        } catch (Exception e) {
-            failure = e;
-        } finally {
-            daemonClient.stop();
-            loggingManager.stop();
-        }
-
-        boolean didFail = failure != null;
-        if (expectFailure != didFail) {
-            String didOrDidntSnippet = didFail ? "DID fail" : "DID NOT fail";
-            throw new RuntimeException(String.format("Gradle execution in %s %s with: %nOutput:%n%s%nError:%n%s%n-----%n", getWorkingDir(), didOrDidntSnippet, output, error), failure);
-        }
-
-        if (expectFailure) {
-            return new OutputScrapingExecutionFailure(output.toString(), error.toString());
-        } else {
-            return new OutputScrapingExecutionResult(output.toString(), error.toString());
-        }
-    }
-
-    private LoggingManagerInternal createLoggingManager(StringBuilder output, StringBuilder error) {
-        LoggingManagerInternal loggingManager = daemonClientServices.newInstance(LoggingManagerInternal.class);
-        loggingManager.addStandardOutputListener(new StreamBackedStandardOutputListener(output));
-        loggingManager.addStandardErrorListener(new StreamBackedStandardOutputListener(error));
-        return loggingManager;
-    }
-
-    private ExecuteBuildAction createBuildAction() {
-        DefaultCommandLineConverter commandLineConverter = new DefaultCommandLineConverter();
-        StartParameter startParameter = new StartParameter();
-        startParameter.setCurrentDir(getWorkingDir());
-        commandLineConverter.convert(getAllArgs(), startParameter);
-        return new ExecuteBuildAction(startParameter);
-    }
-
-    private BuildActionParameters createBuildActionParameters() {
-        return new DefaultBuildActionParameters(daemonClientServices.get(BuildClientMetaData.class), getStartTime(), System.getProperties(), getEnvironmentVars(), getWorkingDir(), LogLevel.LIFECYCLE);
-    }
-
-    private long getStartTime() {
-        return ManagementFactory.getRuntimeMXBean().getStartTime();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExecutionFailure.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExecutionFailure.java
index fa1e4f3..8135333 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExecutionFailure.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExecutionFailure.java
@@ -51,5 +51,8 @@ public interface ExecutionFailure extends ExecutionResult {
 
     ExecutionFailure assertTestsFailed();
 
-    DependencyResolutionFailure assertResolutionFailure(String configuration);
+    /**
+     * @param configurationPath, for example ':compile'
+     */
+    DependencyResolutionFailure assertResolutionFailure(String configurationPath);
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ForkingGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ForkingGradleExecuter.java
index 4db0351..5b654c8 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ForkingGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ForkingGradleExecuter.java
@@ -17,19 +17,14 @@
 package org.gradle.integtests.fixtures.executer;
 
 import org.gradle.api.Action;
-import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.internal.Factory;
 import org.gradle.internal.os.OperatingSystem;
-import org.gradle.launcher.daemon.client.DaemonClientServices;
-import org.gradle.launcher.daemon.configuration.DaemonParameters;
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
-import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.process.internal.ExecHandleBuilder;
 import org.gradle.process.internal.JvmOptions;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -44,12 +39,6 @@ class ForkingGradleExecuter extends AbstractGradleExecuter {
         super(distribution, testDirectoryProvider);
     }
 
-    public DaemonRegistry getDaemonRegistry() {
-        DaemonParameters parameters = new DaemonParameters(new BuildLayoutParameters());
-        parameters.setBaseDir(getDaemonBaseDir());
-        return new DaemonClientServices(LoggingServiceRegistry.newEmbeddableLogging(), parameters, new ByteArrayInputStream(new byte[0])).get(DaemonRegistry.class);
-    }
-
     public void assertCanExecute() throws AssertionError {
         if (!getDistribution().isSupportsSpacesInGradleAndJavaOpts()) {
             Map<String, String> mergedEnvironmentVars = getMergedEnvironmentVars();
@@ -82,6 +71,7 @@ class ForkingGradleExecuter extends AbstractGradleExecuter {
                     + "If you are running tests from IDE make sure that gradle tasks that prepare the test image were executed. Last time it was 'intTestImage' task.");
         }
 
+        NativeServicesTestFixture.initialize();
         ExecHandleBuilder builder = new ExecHandleBuilder() {
             @Override
             public File getWorkingDir() {
@@ -177,7 +167,9 @@ class ForkingGradleExecuter extends AbstractGradleExecuter {
             if (path == null) {
                 path = builder.getEnvironment().get("Path");
             }
-            builder.environment("Path", String.format("%s\\bin;%s", gradleHome, path));
+            path = String.format("%s\\bin;%s", gradleHome, path);
+            builder.environment("PATH", path);
+            builder.environment("Path", path);
             builder.environment("GRADLE_EXIT_CONSOLE", "true");
         }
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleContextualExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleContextualExecuter.java
index 8c0c50d..4be7e49 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleContextualExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleContextualExecuter.java
@@ -33,7 +33,6 @@ public class GradleContextualExecuter extends AbstractDelegatingGradleExecuter {
         embedded(false),
         forking(true),
         daemon(true),
-        embeddedDaemon(false),
         parallel(true, true);
 
         final public boolean forks;
@@ -61,6 +60,10 @@ public class GradleContextualExecuter extends AbstractDelegatingGradleExecuter {
         return getSystemPropertyExecuter() == Executer.daemon;
     }
 
+    public static boolean isLongLivingProcess() {
+        return isEmbedded() || isDaemon();
+    }
+
     public static boolean isParallel() {
         return getSystemPropertyExecuter().executeParallel;
     }
@@ -97,8 +100,6 @@ public class GradleContextualExecuter extends AbstractDelegatingGradleExecuter {
 
     private GradleExecuter createExecuter(Executer executerType) {
         switch (executerType) {
-            case embeddedDaemon:
-                return new EmbeddedDaemonGradleExecuter(getDistribution(), getTestDirectoryProvider());
             case embedded:
                 return new InProcessGradleExecuter(getDistribution(), getTestDirectoryProvider());
             case daemon:
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
index 00eaa31..b66581b 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
@@ -69,11 +69,16 @@ public interface GradleDistribution {
     boolean isToolingApiSupported();
 
     /**
-     * Returns true if the tooling API provider of this distribution correctly handles non-ASCII characters in logging output.
+     * Returns true if the tooling API of this distribution correctly handles non-ASCII characters in logging output.
      */
     boolean isToolingApiNonAsciiOutputSupported();
 
     /**
+     * Returns true if the tooling API of this distribution supports specifying the daemon base dir.
+     */
+    boolean isToolingApiDaemonBaseDirSupported();
+
+    /**
      * Returns the version of the artifact cache layout
      */
     VersionNumber getArtifactCacheLayoutVersion();
@@ -85,14 +90,13 @@ public interface GradleDistribution {
 
     /**
      * Returns true if the wrapper from this distribution can execute a build using the specified version.
-     * @param version
      */
     boolean wrapperCanExecute(GradleVersion version);
 
     /**
      * Early versions had bugs that prevented any values having spaces in them in GRADLE_OPTS or JAVA_OPTS.
      *
-     * See http://issues.gradle.org/browse/GRADLE-1730
+     * See https://issues.gradle.org/browse/GRADLE-1730
      */
     boolean isSupportsSpacesInGradleAndJavaOpts();
 
@@ -100,4 +104,9 @@ public interface GradleDistribution {
      * The 'ivy' repository was introduced in Milestone-3, but early versions didn't work with spaces in the artifact pattern.
      */
     boolean isFullySupportsIvyRepository();
+
+    /**
+     * Returns true if the wrapper for this version honours the --gradle-user-home command-line option.
+     */
+    boolean isWrapperSupportsGradleUserHomeCommandLineOption();
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
index 650ae74..e7d619e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
@@ -16,14 +16,15 @@
 package org.gradle.integtests.fixtures.executer;
 
 import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
 import org.gradle.api.Action;
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
 import org.gradle.test.fixtures.file.TestFile;
 
 import java.io.File;
 import java.io.InputStream;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 public interface GradleExecuter {
@@ -138,13 +139,6 @@ public interface GradleExecuter {
     ExecutionFailure runWithFailure();
 
     /**
-     * Provides a daemon registry for any daemons started by this executer, which may be none.
-     *
-     * @return the daemon registry, never null.
-     */
-    DaemonRegistry getDaemonRegistry();
-
-    /**
      * Starts executing the build asynchronously.
      *
      * @return the handle, never null.
@@ -170,6 +164,24 @@ public interface GradleExecuter {
     GradleExecuter withDefaultCharacterEncoding(String defaultCharacterEncoding);
 
     /**
+     * Sets the temp dir to use.
+     *
+     * Only makes sense for forking executers, and is optional.
+     *
+     * @return this executer
+     */
+    GradleExecuter withTmpDir(String tmpDir);
+
+    /**
+     * Sets the default locale to use.
+     *
+     * Only makes sense for forking executers.
+     *
+     * @return this executer
+     */
+    GradleExecuter withDefaultLocale(Locale defaultLocale);
+
+    /**
      * Set the number of seconds an idle daemon should live for.
      *
      * @param secs
@@ -202,7 +214,7 @@ public interface GradleExecuter {
     /**
      * Adds an action to be called immediately before execution, to allow extra configuration to be injected.
      */
-    void beforeExecute(Closure action);
+    void beforeExecute(@DelegatesTo(GradleExecuter.class) Closure action);
 
     /**
      * Adds an action to be called immediately after execution
@@ -212,7 +224,7 @@ public interface GradleExecuter {
     /**
      * Adds an action to be called immediately after execution
      */
-    void afterExecute(Closure action);
+    void afterExecute(@DelegatesTo(GradleExecuter.class) Closure action);
 
     /**
      * The directory that the executer will use for any test specific storage.
@@ -279,4 +291,6 @@ public interface GradleExecuter {
      * @return The passed in executer
      */
     GradleExecuter copyTo(GradleExecuter executer);
+
+    GradleExecuter withDaemonStartingMessageEnabled();
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleHandle.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleHandle.java
index 643c273..5d2f36f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleHandle.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleHandle.java
@@ -16,13 +16,33 @@
 package org.gradle.integtests.fixtures.executer;
 
 public interface GradleHandle {
+    /**
+     * Returns the stdout output currently received from the build. This is live.
+     */
     String getStandardOutput();
+
+    /**
+     * Returns the stderr output currently received from the build. This is live.
+     */
     String getErrorOutput();
 
+    /**
+     * Forcefully kills the build and returns immediately. Does not block until the build has finished.
+     */
     GradleHandle abort();
 
+    /**
+     * Blocks until the build is complete and assert that the build completed successfully.
+     */
     ExecutionResult waitForFinish();
+
+    /**
+     * Blocks until the build is complete and assert that the build completed with a failure.
+     */
     ExecutionFailure waitForFailure();
 
+    /**
+     * Returns true if the build is currently running.
+     */
     boolean isRunning();
 }
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
index 4eab245..3043cb3 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuter.java
@@ -17,7 +17,6 @@
 package org.gradle.integtests.fixtures.executer;
 
 import org.gradle.BuildResult;
-import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
 import org.gradle.api.GradleException;
 import org.gradle.api.Task;
@@ -34,18 +33,23 @@ import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.initialization.DefaultGradleLauncherFactory;
+import org.gradle.initialization.GradleLauncher;
 import org.gradle.internal.Factory;
 import org.gradle.internal.exceptions.LocationAwareException;
 import org.gradle.internal.jvm.Jvm;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.ServiceRegistryBuilder;
+import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.launcher.Main;
 import org.gradle.launcher.cli.converter.LayoutToPropertiesConverter;
 import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter;
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
+import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.logging.ShowStacktrace;
 import org.gradle.process.internal.JavaExecHandleBuilder;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
 import org.gradle.util.DeprecationLogger;
 import org.hamcrest.Matcher;
 import org.hamcrest.Matchers;
@@ -59,14 +63,19 @@ import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.regex.Pattern;
 
-import static org.gradle.util.Matchers.hasMessage;
-import static org.gradle.util.Matchers.isEmpty;
+import static org.gradle.util.Matchers.*;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
 class InProcessGradleExecuter extends AbstractGradleExecuter {
-
-    private final ProcessEnvironment processEnvironment = NativeServices.getInstance().get(ProcessEnvironment.class);
+    private static final ServiceRegistry GLOBAL_SERVICES = ServiceRegistryBuilder.builder()
+            .displayName("Global services")
+            .parent(LoggingServiceRegistry.newCommandLineProcessLogging())
+            .parent(NativeServicesTestFixture.getInstance())
+            .provider(new GlobalScopeServices(true))
+            .build();
+    private final ProcessEnvironment processEnvironment = GLOBAL_SERVICES.get(ProcessEnvironment.class);
+    public static final TestFile COMMON_TMP = new TestFile(new File("build/tmp"));
 
     InProcessGradleExecuter(GradleDistribution distribution, TestDirectoryProvider testDirectoryProvider) {
         super(distribution, testDirectoryProvider);
@@ -122,6 +131,7 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
                 builder.classpath(classpath);
                 builder.setMain(Main.class.getName());
                 builder.args(getAllArgs());
+                builder.setStandardInput(getStdin());
                 return builder;
             }
         }).start();
@@ -153,7 +163,7 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         converter.configure(parser);
         ParsedCommandLine parsedCommandLine = parser.parse(getAllArgs());
 
-        BuildLayoutParameters layout = converter.getLayoutConverter().convert(parsedCommandLine);
+        BuildLayoutParameters layout = converter.getLayoutConverter().convert(parsedCommandLine, new BuildLayoutParameters());
 
         Map<String, String> properties = new HashMap<String, String>();
         new LayoutToPropertiesConverter().convert(layout, properties);
@@ -162,22 +172,22 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         new PropertiesToStartParameterConverter().convert(properties, parameter);
         converter.convert(parsedCommandLine, parameter);
 
-        DefaultGradleLauncherFactory factory = DeprecationLogger.whileDisabled(new Factory<DefaultGradleLauncherFactory>() {
-            public DefaultGradleLauncherFactory create() {
-                return (DefaultGradleLauncherFactory) GradleLauncher.getFactory();
-            }
-        });
+        DefaultGradleLauncherFactory factory = GLOBAL_SERVICES.get(DefaultGradleLauncherFactory.class);
         factory.addListener(listener);
-        GradleLauncher gradleLauncher = factory.newInstance(parameter);
-        gradleLauncher.addStandardOutputListener(outputListener);
-        gradleLauncher.addStandardErrorListener(errorListener);
         try {
-            return gradleLauncher.run();
+            GradleLauncher gradleLauncher = factory.newInstance(parameter);
+            try {
+                gradleLauncher.addStandardOutputListener(outputListener);
+                gradleLauncher.addStandardErrorListener(errorListener);
+                return gradleLauncher.run();
+            } finally {
+                gradleLauncher.stop();
+            }
         } finally {
             // Restore the environment
             System.setProperties(originalSysProperties);
             processEnvironment.maybeSetProcessDir(originalUserDir);
-            for (String envVar: getEnvironmentVars().keySet()) {
+            for (String envVar : getEnvironmentVars().keySet()) {
                 String oldValue = originalEnv.get(envVar);
                 if (oldValue != null) {
                     processEnvironment.maybeSetEnvironmentVariable(envVar, oldValue);
@@ -190,10 +200,6 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
     }
 
-    public DaemonRegistry getDaemonRegistry() {
-        throw new UnsupportedOperationException();
-    }
-
     public void assertCanExecute() {
         assertNull(getExecutable());
         assertEquals(getJavaHome(), Jvm.current().getJavaHome());
@@ -201,9 +207,22 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         if (defaultEncoding != null) {
             assertEquals(Charset.forName(defaultEncoding), Charset.defaultCharset());
         }
+        Locale defaultLocale = getDefaultLocale();
+        if (defaultLocale != null) {
+            assertEquals(defaultLocale, Locale.getDefault());
+        }
         assertFalse(isRequireGradleHome());
     }
 
+    @Override
+    protected TestFile getDefaultTmpDir() {
+        // File.createTempFile sets the location of the temp directory to a static variable on the first call.  This prevents future
+        // changes to java.io.tmpdir from having any effect in the same process.  We set this to use a common tmp directory for all
+        // tests running in the same process so that we don't have a situation where one process initializes with a tmp directory
+        // that it then removes, causing an IOException for any future tests that run in the same process and call File.createTempFile.
+        return COMMON_TMP;
+    }
+
     private static class BuildListenerImpl implements TaskExecutionGraphListener {
         private final List<String> executedTasks = new CopyOnWriteArrayList<String>();
         private final Set<String> skippedTasks = new CopyOnWriteArraySet<String>();
@@ -305,6 +324,9 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionResult assertTasksSkipped(String... taskPaths) {
+            if (GradleContextualExecuter.isParallel()) {
+                return this;
+            }
             Set<String> expected = new HashSet<String>(Arrays.asList(taskPaths));
             assertThat(skippedTasks, equalTo(expected));
             outputResult.assertTasksSkipped(taskPaths);
@@ -312,12 +334,18 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionResult assertTaskSkipped(String taskPath) {
+            if (GradleContextualExecuter.isParallel()) {
+                return this;
+            }
             assertThat(skippedTasks, hasItem(taskPath));
             outputResult.assertTaskSkipped(taskPath);
             return this;
         }
 
         public ExecutionResult assertTasksNotSkipped(String... taskPaths) {
+            if (GradleContextualExecuter.isParallel()) {
+                return this;
+            }
             Set<String> expected = new HashSet<String>(Arrays.asList(taskPaths));
             Set<String> notSkipped = getNotSkippedTasks();
             assertThat(notSkipped, equalTo(expected));
@@ -326,6 +354,9 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionResult assertTaskNotSkipped(String taskPath) {
+            if (GradleContextualExecuter.isParallel()) {
+                return this;
+            }
             assertThat(getNotSkippedTasks(), hasItem(taskPath));
             outputResult.assertTaskNotSkipped(taskPath);
             return this;
@@ -383,7 +414,7 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionFailure assertHasCause(String description) {
-            assertThatCause(startsWith(description));
+            assertThatCause(normalizedLineSeparators(startsWith(description)));
             return this;
         }
 
@@ -404,7 +435,7 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
             } else if (failure instanceof LocationAwareException) {
                 causes.addAll(((LocationAwareException) failure).getReportableCauses());
             } else {
-                causes.add(failure.getCause());
+                causes.add(failure);
             }
         }
 
@@ -420,12 +451,12 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
 
         public ExecutionFailure assertHasDescription(String context) {
-            assertThatDescription(equalTo(context));
+            assertThatDescription(startsWith(context));
             return this;
         }
 
         public ExecutionFailure assertThatDescription(Matcher<String> matcher) {
-            assertThat(description, matcher);
+            assertThat(description, normalizedLineSeparators(matcher));
             outputFailure.assertThatDescription(matcher);
             return this;
         }
@@ -435,8 +466,8 @@ class InProcessGradleExecuter extends AbstractGradleExecuter {
             return this;
         }
 
-        public DependencyResolutionFailure assertResolutionFailure(String configuration) {
-            return new DependencyResolutionFailure(this, configuration);
+        public DependencyResolutionFailure assertResolutionFailure(String configurationPath) {
+            return new DependencyResolutionFailure(this, configurationPath);
         }
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InitScriptExecuterFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InitScriptExecuterFixture.groovy
index c4b1c73..40a5d1e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InitScriptExecuterFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/InitScriptExecuterFixture.groovy
@@ -31,9 +31,11 @@ abstract class InitScriptExecuterFixture implements MethodRule {
         this.testDir = testDir
     }
 
-    abstract String initScriptContent()
+    String initScriptContent() {
+    }
 
-    abstract void afterBuild()
+    void afterBuild() {
+    }
 
     Statement apply(Statement base, FrameworkMethod method, Object target) {
         def temporaryFolder = testDir.testDirectory
@@ -46,10 +48,6 @@ abstract class InitScriptExecuterFixture implements MethodRule {
             afterBuild()
         }
 
-        return new Statement() {
-            public void evaluate() throws Throwable {
-                base.evaluate();
-            }
-        }
+        return base
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
index 3bb8f00..0ee73b4 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.integtests.fixtures.executer;
 
+import org.gradle.util.TextUtil;
 import org.hamcrest.Matcher;
 
 import java.util.ArrayList;
@@ -28,10 +29,11 @@ import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResult implements ExecutionFailure {
+    private static final Pattern FAILURE_PATTERN = Pattern.compile("(?m)FAILURE: .+$");
     private static final Pattern CAUSE_PATTERN = Pattern.compile("(?m)(^\\s*> )");
-    private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("(?ms)^\\* What went wrong:$(.+)^\\* Try:$");
+    private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("(?ms)^\\* What went wrong:$(.+?)^\\* Try:$");
     private static final Pattern LOCATION_PATTERN = Pattern.compile("(?ms)^\\* Where:((.+)'.+') line: (\\d+)$");
-    private static final Pattern RESOLUTION_PATTERN = Pattern.compile("(?ms)^\\* Try:$(.+)^\\* Exception is:$");
+    private static final Pattern RESOLUTION_PATTERN = Pattern.compile("(?ms)^\\* Try:$(.+?)^\\* Exception is:$");
     private final String description;
     private final String lineNumber;
     private final String fileName;
@@ -41,7 +43,14 @@ public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResul
     public OutputScrapingExecutionFailure(String output, String error) {
         super(output, error);
 
-        java.util.regex.Matcher matcher = LOCATION_PATTERN.matcher(error);
+        java.util.regex.Matcher matcher = FAILURE_PATTERN.matcher(error);
+        if (matcher.find()) {
+            if (matcher.find()) {
+                throw new AssertionError("Found multiple failure sections in build error output.");
+            }
+        }
+
+        matcher = LOCATION_PATTERN.matcher(error);
         if (matcher.find()) {
             fileName = matcher.group(1).trim();
             lineNumber = matcher.group(3);
@@ -51,39 +60,51 @@ public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResul
         }
 
         matcher = DESCRIPTION_PATTERN.matcher(error);
-        String problem;
         if (matcher.find()) {
-            problem = matcher.group(1);
+            String problemStr = matcher.group(1);
+            Problem problem = extract(problemStr);
+            description = problem.description;
+            causes.addAll(problem.causes);
+            while (matcher.find()) {
+                problemStr = matcher.group(1);
+                problem = extract(problemStr);
+                causes.addAll(problem.causes);
+            }
+        } else {
+            description = "";
+        }
+
+        matcher = RESOLUTION_PATTERN.matcher(error);
+        if (!matcher.find()) {
+            resolution = "";
         } else {
-            problem = "";
+            resolution = matcher.group(1).trim();
         }
+    }
 
-        matcher = CAUSE_PATTERN.matcher(problem);
+    private Problem extract(String problem) {
+        java.util.regex.Matcher matcher = CAUSE_PATTERN.matcher(problem);
+        String description;
+        List<String> causes = new ArrayList<String>();
         if (!matcher.find()) {
-            description = problem.trim();
+            description = TextUtil.normaliseLineSeparators(problem.trim());
         } else {
-            description = problem.substring(0, matcher.start()).trim();
+            description = TextUtil.normaliseLineSeparators(problem.substring(0, matcher.start()).trim());
             while (true) {
                 int pos = matcher.end();
                 int prefix = matcher.group(1).length();
                 String prefixPattern = toPrefixPattern(prefix);
                 if (matcher.find(pos)) {
-                    String cause = problem.substring(pos, matcher.start()).trim().replaceAll(prefixPattern, "");
+                    String cause = TextUtil.normaliseLineSeparators(problem.substring(pos, matcher.start()).trim().replaceAll(prefixPattern, ""));
                     causes.add(cause);
                 } else {
-                    String cause = problem.substring(pos).trim().replaceAll(prefixPattern, "");
+                    String cause = TextUtil.normaliseLineSeparators(problem.substring(pos).trim().replaceAll(prefixPattern, ""));
                     causes.add(cause);
                     break;
                 }
             }
         }
-
-        matcher = RESOLUTION_PATTERN.matcher(error);
-        if (!matcher.find()) {
-            resolution = "";
-        } else {
-            resolution = matcher.group(1).trim();
-        }
+        return new Problem(description, causes);
     }
 
     private String toPrefixPattern(int prefix) {
@@ -130,7 +151,7 @@ public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResul
     }
 
     public ExecutionFailure assertHasDescription(String context) {
-        assertThatDescription(equalTo(context));
+        assertThatDescription(startsWith(context));
         return this;
     }
 
@@ -144,7 +165,17 @@ public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResul
         return this;
     }
 
-    public DependencyResolutionFailure assertResolutionFailure(String configuration) {
-        return new DependencyResolutionFailure(this, configuration);
+    public DependencyResolutionFailure assertResolutionFailure(String configurationPath) {
+        return new DependencyResolutionFailure(this, configurationPath);
+    }
+
+    private static class Problem {
+        final String description;
+        final List<String> causes;
+
+        private Problem(String description, List<String> causes) {
+            this.description = description;
+            this.causes = causes;
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionResult.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionResult.java
index 298814f..98dc64d 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionResult.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionResult.java
@@ -79,18 +79,27 @@ public class OutputScrapingExecutionResult implements ExecutionResult {
     }
 
     public ExecutionResult assertTasksSkipped(String... taskPaths) {
+        if (GradleContextualExecuter.isParallel()) {
+            return this;
+        }
         Set<String> expectedTasks = new HashSet<String>(Arrays.asList(taskPaths));
         assertThat(String.format("Expected skipped tasks %s not found in process output:%n%s", expectedTasks, getOutput()), getSkippedTasks(), equalTo(expectedTasks));
         return this;
     }
 
     public ExecutionResult assertTaskSkipped(String taskPath) {
+        if (GradleContextualExecuter.isParallel()) {
+            return this;
+        }
         Set<String> tasks = new HashSet<String>(getSkippedTasks());
         assertThat(String.format("Expected skipped task %s not found in process output:%n%s", taskPath, getOutput()), tasks, hasItem(taskPath));
         return this;
     }
 
     public ExecutionResult assertTasksNotSkipped(String... taskPaths) {
+        if (GradleContextualExecuter.isParallel()) {
+            return this;
+        }
         Set<String> tasks = new HashSet<String>(getNotSkippedTasks());
         Set<String> expectedTasks = new HashSet<String>(Arrays.asList(taskPaths));
         assertThat(String.format("Expected executed tasks %s not found in process output:%n%s", expectedTasks, getOutput()), tasks, equalTo(expectedTasks));
@@ -104,6 +113,9 @@ public class OutputScrapingExecutionResult implements ExecutionResult {
     }
 
     public ExecutionResult assertTaskNotSkipped(String taskPath) {
+        if (GradleContextualExecuter.isParallel()) {
+            return this;
+        }
         Set<String> tasks = new HashSet<String>(getNotSkippedTasks());
         assertThat(String.format("Expected executed task %s not found in process output:%n%s", taskPath, getOutput()), tasks, hasItem(taskPath));
         return this;
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ParallelForkingGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ParallelForkingGradleExecuter.java
index 172e7fc..8af59c3 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ParallelForkingGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ParallelForkingGradleExecuter.java
@@ -17,9 +17,11 @@
 package org.gradle.integtests.fixtures.executer;
 
 import org.gradle.api.Action;
+import org.gradle.execution.taskgraph.DefaultTaskExecutionPlan;
 import org.gradle.internal.Factory;
 import org.gradle.process.internal.ExecHandleBuilder;
 import org.gradle.test.fixtures.file.TestDirectoryProvider;
+import org.gradle.util.GradleVersion;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -33,7 +35,13 @@ class ParallelForkingGradleExecuter extends ForkingGradleExecuter {
     protected List<String> getAllArgs() {
         List<String> args = new ArrayList<String>();
         args.addAll(super.getAllArgs());
-        args.add("--parallel-threads=4");
+        if (getDistribution().getVersion().compareTo(GradleVersion.version("2.3")) <= 0) {
+            args.add("--parallel-threads=4");
+        } else {
+            args.add("--parallel");
+            args.add("--max-workers=4");
+        }
+        args.add("-D" + DefaultTaskExecutionPlan.INTRA_PROJECT_TOGGLE + "=true");
         return args;
     }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProgressLoggingFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProgressLoggingFixture.groovy
index 9f98db9..1aad1d5 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProgressLoggingFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProgressLoggingFixture.groovy
@@ -63,7 +63,7 @@ class ProgressLoggingFixture extends InitScriptExecuterFixture {
     }
 
     boolean downloadProgressLogged(URI url) {
-        downloadProgressLogged(url.toString())
+        downloadProgressLogged(url.toASCIIString())
     }
 
     boolean downloadProgressLogged(String url) {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProjectLifecycleFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProjectLifecycleFixture.groovy
index 8187711..235df46 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProjectLifecycleFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ProjectLifecycleFixture.groovy
@@ -34,6 +34,7 @@ class ProjectLifecycleFixture extends InitScriptExecuterFixture {
     String initScriptContent() {
         fixtureData = testDir.testDirectory.file("lifecycle-fixture-data.txt")
         """File outputFile = file("${fixtureData.toURI()}")
+           outputFile.text = ''
            def listener = new ProjectEvaluationListener() {
                 void afterEvaluate(Project project, ProjectState state) {
                     outputFile << project.path + ";"
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/RedirectMavenCentral.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/RedirectMavenCentral.groovy
deleted file mode 100644
index 7a01edf..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/RedirectMavenCentral.groovy
+++ /dev/null
@@ -1,46 +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.integtests.fixtures.executer
-
-import org.gradle.api.Action
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-
-class RedirectMavenCentral implements Action<GradleExecuter> {
-    private final TestDirectoryProvider testWorkDirProvider
-
-    RedirectMavenCentral(TestDirectoryProvider testWorkDirProvider) {
-        this.testWorkDirProvider = testWorkDirProvider
-    }
-
-    void execute(GradleExecuter executer) {
-        if (OperatingSystem.current().isWindows()) {
-            return
-        }
-
-        def file = testWorkDirProvider.testDirectory.createFile("redirect-maven-central-init.gradle")
-        file.text = """
-allprojects {
-    repositories.withType(org.gradle.api.artifacts.repositories.MavenArtifactRepository) {
-        if (url == new URI('http://repo1.maven.org/maven2/')) {
-            url = "http://repo.gradle.org/gradle/repo1"
-        }
-    }
-}
-"""
-        executer.withArgument("-I$file.absolutePath")
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/SequentialOutputMatcher.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/SequentialOutputMatcher.groovy
index af77303..5b88a67 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/SequentialOutputMatcher.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/SequentialOutputMatcher.groovy
@@ -19,14 +19,15 @@
 package org.gradle.integtests.fixtures.executer
 
 import org.gradle.internal.SystemProperties
-import org.junit.Assert
+import org.gradle.internal.jvm.Jvm
 import org.gradle.util.TextUtil
+import org.junit.Assert
 
 /**
  * Check that the actual output lines match the expected output lines in content and order.
  */
 class SequentialOutputMatcher {
-    private static final String NL = SystemProperties.lineSeparator
+    private static final String NL = SystemProperties.instance.lineSeparator
 
     public void assertOutputMatches(String expected, String actual, boolean ignoreExtraLines) {
         List actualLines = normaliseOutput(actual.readLines())
@@ -62,10 +63,14 @@ class SequentialOutputMatcher {
         if (lines.empty) {
             return lines;
         }
+        boolean seenWarning = false
         List<String> result = new ArrayList<String>()
         for (String line : lines) {
             if (line.matches('Download .+')) {
                 // ignore
+            } else if (!seenWarning && !Jvm.current().javaVersion.java7Compatible && line == 'Support for reading or changing file permissions is only available on this platform using Java 7 or later.') {
+                // ignore this warning once only on java < 7
+                seenWarning = true
             } else {
                 result << line
             }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/UnderDevelopmentGradleDistribution.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/UnderDevelopmentGradleDistribution.java
index 3767723..ec69cfd 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/UnderDevelopmentGradleDistribution.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/UnderDevelopmentGradleDistribution.java
@@ -16,6 +16,8 @@
 
 package org.gradle.integtests.fixtures.executer;
 
+import org.gradle.test.fixtures.file.TestDirectoryProvider;
+
 public class UnderDevelopmentGradleDistribution extends DefaultGradleDistribution {
 
     public UnderDevelopmentGradleDistribution() {
@@ -30,5 +32,9 @@ public class UnderDevelopmentGradleDistribution extends DefaultGradleDistributio
         );
     }
 
+    @Override
+    public GradleExecuter executer(TestDirectoryProvider testDirectoryProvider) {
+        return new GradleContextualExecuter(this, testDirectoryProvider);
+    }
 }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/InstalledJvmLocator.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/InstalledJvmLocator.java
new file mode 100644
index 0000000..f469a36
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/InstalledJvmLocator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import net.rubygrapefruit.platform.SystemInfo;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
+
+import java.io.File;
+import java.util.*;
+
+public class InstalledJvmLocator {
+    private final OperatingSystem operatingSystem = OperatingSystem.current();
+    private final WindowsRegistry windowsRegistry = NativeServicesTestFixture.getInstance().get(WindowsRegistry.class);
+    private final SystemInfo systemInfo = NativeServicesTestFixture.getInstance().get(SystemInfo.class);
+    private final FileCanonicalizer fileCanonicalizer = NativeServicesTestFixture.getInstance().get(FileCanonicalizer.class);
+    private final Jvm currentJvm = Jvm.current();
+
+    /**
+     * Discovers JVMs installed on the local machine. Returns the details of each JVM that can be determined efficiently, without running the JVM.
+     *
+     * @return The JVMs, ordered from highest to lowest Java version. Will include the current JVM.
+     */
+    public List<JvmInstallation> findJvms() {
+        Map<File, JvmInstallation> installs = new HashMap<File, JvmInstallation>();
+        Collection<JvmInstallation> jvms;
+        if (operatingSystem.isMacOsX()) {
+            jvms = new OsXInstalledJvmLocator().findJvms();
+        } else if (operatingSystem.isWindows()) {
+            jvms = new WindowsOracleJvmLocator(windowsRegistry, systemInfo).findJvms();
+        } else if (operatingSystem.isLinux()) {
+            jvms = new UbuntuJvmLocator(fileCanonicalizer).findJvms();
+        } else {
+            jvms = Collections.emptySet();
+        }
+        for (JvmInstallation jvm : jvms) {
+            if (!installs.containsKey(jvm.getJavaHome())) {
+                installs.put(jvm.getJavaHome(), jvm);
+            }
+        }
+        if (!installs.containsKey(currentJvm.getJavaHome())) {
+            // TODO - this isn't quite right
+            boolean isJdk = !currentJvm.getJre().getHomeDir().equals(currentJvm.getJavaHome());
+            installs.put(currentJvm.getJavaHome(), new JvmInstallation(currentJvm.getJavaVersion(), System.getProperty("java.version"), currentJvm.getJavaHome(), isJdk, toArch(System.getProperty("os.arch"))));
+        }
+
+        List<JvmInstallation> result = new ArrayList<JvmInstallation>(installs.values());
+        Collections.sort(result, new Comparator<JvmInstallation>() {
+            public int compare(JvmInstallation o1, JvmInstallation o2) {
+                return o2.getVersion().compareTo(o1.getVersion());
+            }
+        });
+        return result;
+    }
+
+    private JvmInstallation.Arch toArch(String arch) {
+        if (arch.equals("amd64") || arch.equals("x86_64")) {
+            return JvmInstallation.Arch.x86_64;
+        }
+        if (arch.equals("i386")) {
+            return JvmInstallation.Arch.i386;
+        }
+        return JvmInstallation.Arch.Unknown;
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JvmInstallation.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JvmInstallation.java
new file mode 100644
index 0000000..9b2ed4f
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JvmInstallation.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+
+public class JvmInstallation {
+    public enum Arch { i386, x86_64, Unknown }
+
+    private final JavaVersion javaVersion;
+    private final VersionNumber version;
+    private final File javaHome;
+    private final boolean jdk;
+    private final Arch arch;
+
+    public JvmInstallation(JavaVersion javaVersion, String version, File javaHome, boolean jdk, Arch arch) {
+        this.javaVersion = javaVersion;
+        this.version = VersionNumber.withPatchNumber().parse(version);
+        this.javaHome = javaHome;
+        this.jdk = jdk;
+        this.arch = arch;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s %s (%s) %s %s", jdk ? "JDK" : "JRE", javaVersion, version, arch, javaHome);
+    }
+
+    public Arch getArch() {
+        return arch;
+    }
+
+    public boolean isJdk() {
+        return jdk;
+    }
+
+    public VersionNumber getVersion() {
+        return version;
+    }
+
+    public JavaVersion getJavaVersion() {
+        return javaVersion;
+    }
+
+    public File getJavaHome() {
+        return javaHome;
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXInstalledJvmLocator.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXInstalledJvmLocator.java
new file mode 100644
index 0000000..109b0c4
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXInstalledJvmLocator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import org.gradle.api.GradleException;
+import org.gradle.process.internal.ExecHandleBuilder;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.util.Collection;
+
+/**
+ * Uses `java_home -V` to find JVM installations
+ */
+class OsXInstalledJvmLocator {
+    public Collection<JvmInstallation> findJvms() {
+        try {
+            ExecHandleBuilder execHandleBuilder = new ExecHandleBuilder();
+            execHandleBuilder.workingDir(new File(".").getAbsoluteFile());
+            execHandleBuilder.commandLine("/usr/libexec/java_home", "-V");
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            // verbose output is written to stderr for some reason
+            execHandleBuilder.setErrorOutput(outputStream);
+            execHandleBuilder.setStandardOutput(new ByteArrayOutputStream());
+            execHandleBuilder.build().start().waitForFinish().assertNormalExitValue();
+            return new OsXJavaHomeParser().parse(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())));
+        } catch (Exception e) {
+            throw new GradleException("Could not locate installed JVMs.", e);
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParser.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParser.java
new file mode 100644
index 0000000..0c07bcb
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParser.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import org.gradle.api.JavaVersion;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Parses the output of `java_home -V` to extract JVM installation details.
+ */
+class OsXJavaHomeParser {
+    private static final Pattern NEW_FORMAT_PATTERN = Pattern.compile("\\s+(\\S+),\\s+(\\S+):\\s+\".*?\"\\s+(.+)");
+    private static final Pattern OLD_FORMAT_PATTERN = Pattern.compile("\\s+(\\S+)\\s+\\((.*?)\\):\\s+(.+)");
+
+    public List<JvmInstallation> parse(Reader output) throws IOException {
+        ArrayList<JvmInstallation> result = new ArrayList<JvmInstallation>();
+        BufferedReader reader = new BufferedReader(output);
+        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+            Matcher matcher = NEW_FORMAT_PATTERN.matcher(line);
+            if (matcher.matches()) {
+                String version = matcher.group(1);
+                String arch = matcher.group(2);
+                String javaHome = matcher.group(3);
+                result.add(new JvmInstallation(JavaVersion.toVersion(version), version, new File(javaHome), true, toArch(arch)));
+            } else {
+                matcher = OLD_FORMAT_PATTERN.matcher(line);
+                if (matcher.matches()) {
+                    String version = matcher.group(1);
+                    String arch = matcher.group(2);
+                    String javaHome = matcher.group(3);
+                    result.add(new JvmInstallation(JavaVersion.toVersion(version), version, new File(javaHome), true, toArch(arch)));
+                }
+            }
+        }
+        return result;
+    }
+
+    private JvmInstallation.Arch toArch(String arch) {
+        if (arch.equals("i386")) {
+            return JvmInstallation.Arch.i386;
+        } else if (arch.equals("x86_64")) {
+            return JvmInstallation.Arch.x86_64;
+        }
+        return JvmInstallation.Arch.Unknown;
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocator.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocator.java
new file mode 100644
index 0000000..89e309a
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Searches for JVM installations in /usr/lib/jvm. Currently assumes the Ubuntu OpenJDK convention.
+ */
+public class UbuntuJvmLocator {
+    private static final Pattern JAVA_HOME_DIR_PATTERN = Pattern.compile("java-(\\d+.\\d+(?:\\.\\d+)?)-\\w+-(\\w+)");
+    private final FileCanonicalizer fileCanonicalizer;
+    private final File libDir;
+
+    public UbuntuJvmLocator(FileCanonicalizer fileCanonicalizer) {
+        this(new File("/usr/lib/jvm"), fileCanonicalizer);
+    }
+
+    UbuntuJvmLocator(File libDir, FileCanonicalizer fileCanonicalizer) {
+        this.libDir = libDir;
+        this.fileCanonicalizer = fileCanonicalizer;
+    }
+
+    public Collection<JvmInstallation> findJvms() {
+        List<JvmInstallation> jvms = new ArrayList<JvmInstallation>();
+        if (libDir.isDirectory()) {
+            for (File javaHome : libDir.listFiles()) {
+                Matcher matcher = JAVA_HOME_DIR_PATTERN.matcher(javaHome.getName());
+                if (!matcher.matches()) {
+                    continue;
+                }
+                if (!new File(javaHome, "jre/bin/java").isFile()) {
+                    continue;
+                }
+                String version = matcher.group(1);
+                String arch = matcher.group(2);
+                boolean jdk = new File(javaHome, "bin/javac").isFile();
+                jvms.add(new JvmInstallation(JavaVersion.toVersion(version), version, fileCanonicalizer.canonicalize(javaHome), jdk, toArch(arch)));
+            }
+        }
+        return jvms;
+    }
+
+    private JvmInstallation.Arch toArch(String arch) {
+        if (arch.equals("amd64")) {
+            return JvmInstallation.Arch.x86_64;
+        } else if (arch.equals("i386")) {
+            return JvmInstallation.Arch.i386;
+        } else {
+            return JvmInstallation.Arch.Unknown;
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/WindowsOracleJvmLocator.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/WindowsOracleJvmLocator.java
new file mode 100644
index 0000000..d4f672c
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/WindowsOracleJvmLocator.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException;
+import net.rubygrapefruit.platform.SystemInfo;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import org.gradle.api.JavaVersion;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Uses windows registry to find installed Sun/Oracle JVMs
+ */
+class WindowsOracleJvmLocator {
+    private final WindowsRegistry windowsRegistry;
+    private final SystemInfo systemInfo;
+
+    WindowsOracleJvmLocator(WindowsRegistry windowsRegistry, SystemInfo systemInfo) {
+        this.windowsRegistry = windowsRegistry;
+        this.systemInfo = systemInfo;
+    }
+
+    public Collection<JvmInstallation> findJvms() {
+        JvmInstallation.Arch defaultArch = systemInfo.getArchitecture() == SystemInfo.Architecture.i386 ? JvmInstallation.Arch.i386 : JvmInstallation.Arch.x86_64;
+        List<JvmInstallation> jvms = new ArrayList<JvmInstallation>();
+        findJvms(windowsRegistry, "SOFTWARE\\JavaSoft\\Java Development Kit", jvms, true, defaultArch);
+        findJvms(windowsRegistry, "SOFTWARE\\JavaSoft\\Java Runtime Environment", jvms, false, defaultArch);
+        findJvms(windowsRegistry, "SOFTWARE\\Wow6432Node\\JavaSoft\\Java Development Kit", jvms, true, JvmInstallation.Arch.i386);
+        findJvms(windowsRegistry, "SOFTWARE\\Wow6432Node\\JavaSoft\\Java Runtime Environment", jvms, false, JvmInstallation.Arch.i386);
+        return jvms;
+    }
+
+    private void findJvms(WindowsRegistry windowsRegistry, String sdkSubkey, Collection<JvmInstallation> jvms, boolean jdk, JvmInstallation.Arch arch) {
+        List<String> versions;
+        try {
+            versions = windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, sdkSubkey);
+        } catch (MissingRegistryEntryException e) {
+            // Ignore
+            return;
+        }
+
+        for (String version : versions) {
+            if (version.matches("\\d+\\.\\d+")) {
+                continue;
+            }
+            String javaHome = windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, sdkSubkey + '\\' + version, "JavaHome");
+            jvms.add(new JvmInstallation(JavaVersion.toVersion(version), version, new File(javaHome), jdk, arch));
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/ClasspathVersionSource.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/ClasspathVersionSource.java
index d1b230b..5ca6b78 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/ClasspathVersionSource.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/ClasspathVersionSource.java
@@ -44,7 +44,7 @@ public class ClasspathVersionSource implements Factory<Properties> {
             throw new RuntimeException(
                     "Unable to find the released versions information.\n"
                             + "The resource '" + resourceName + "' was not found.\n"
-                            + "Most likely, you haven't ran the 'prepareVersionsInfo' task.\n"
+                            + "Most likely, you haven't run the 'prepareVersionsInfo' task.\n"
                             + "If you have trouble running tests from your IDE, please run gradlew idea|eclipse first."
             );
         }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/AbstractModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/AbstractModule.groovy
index 142e53d..f811e47 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/AbstractModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/AbstractModule.groovy
@@ -24,6 +24,7 @@ abstract class AbstractModule {
      * @param cl A closure that is passed a writer to use to generate the content.
      */
     protected void publish(TestFile file, Closure cl) {
+        file.parentFile.mkdirs()
         def hashBefore = file.exists() ? getHash(file, "sha1") : null
         def tmpFile = file.parentFile.file("${file.name}.tmp")
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/HttpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/HttpModule.groovy
index de33167..135f79d 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/HttpModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/HttpModule.groovy
@@ -16,5 +16,5 @@
 package org.gradle.test.fixtures
 
 public interface HttpModule extends Module {
-    void allowAll()
+    HttpModule allowAll()
 }
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayApi.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayApi.groovy
deleted file mode 100644
index ce24f54..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayApi.groovy
+++ /dev/null
@@ -1,83 +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.test.fixtures.bintray
-
-import org.gradle.test.fixtures.server.http.HttpServer
-
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
-
-import static org.gradle.plugin.resolve.internal.JCenterPluginMapper.*
-import static org.gradle.test.fixtures.server.http.HttpServer.Utils.json
-
-class BintrayApi {
-
-    final HttpServer httpServer
-    private final String path
-
-    BintrayApi(HttpServer httpServer) {
-        // The bintray client seems hardcoded to issue requests relative to the root
-        // therefore we have to use the root as the path
-        // Waiting for one of the following issues to be resolved so we can debug into it:
-        // https://github.com/bintray/bintray-client-java/pull/2
-        // https://github.com/bintray/bintray-client-java/issues/3
-        this.path = ""
-        this.httpServer = httpServer
-    }
-
-    String getAddress() {
-        httpServer.address + path
-    }
-
-    void expectPackageSearch(String pluginId, FoundPackage... packages) {
-        httpServer.expect("$path/search/attributes/$GRADLE_PLUGINS_ORG/$GRADLE_PLUGINS_REPO", ["POST"], new HttpServer.ActionSupport("search action") {
-            void handle(HttpServletRequest request, HttpServletResponse response) {
-                def requestBody = json(request)
-                assert requestBody == json("[{\"${PLUGIN_ID_ATTRIBUTE_NAME}\":[\"$pluginId\"]}]")
-                json(response, packages*.asStruct())
-            }
-        })
-    }
-
-    static class FoundPackage {
-        List<String> systemIds
-        String latestVersion
-
-        FoundPackage(String latestVersion, String... systemIds) {
-            this.systemIds = systemIds.toList()
-            this.latestVersion = latestVersion
-        }
-
-        Map<String, Object> asStruct() {
-            [
-                    "name": "not-used",
-                    "repo": "not-used",
-                    "owner": "not-used",
-                    "desc": "not-used",
-                    "labels": [],
-                    "attribute_names": ["not-used"],
-                    "followers_count": 0,
-                    "created": "2013-11-18T12:31:31.147Z",
-                    "versions": [],
-                    "latest_version": latestVersion,
-                    "updated": "2013-11-18T14:39:20.023Z",
-                    "rating_count": 0,
-                    "system_ids": systemIds
-            ]
-        }
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayTestServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayTestServer.groovy
deleted file mode 100644
index a4af182..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/bintray/BintrayTestServer.groovy
+++ /dev/null
@@ -1,62 +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.test.fixtures.bintray
-
-import org.gradle.api.Action
-import org.gradle.api.internal.artifacts.BaseRepositoryFactory
-import org.gradle.integtests.fixtures.executer.GradleExecuter
-import org.gradle.plugin.resolve.internal.JCenterPluginMapper
-import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.test.fixtures.maven.MavenHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.junit.rules.ExternalResource
-
-class BintrayTestServer extends ExternalResource {
-
-    private final HttpServer http
-
-    final MavenHttpRepository jcenter
-    final MavenFileRepository repo
-    final BintrayApi api
-
-    BintrayTestServer(GradleExecuter executer, MavenFileRepository repo) {
-        this.http = new HttpServer()
-        this.repo = repo
-        this.jcenter = new MavenHttpRepository(http, repo)
-        this.api = new BintrayApi(http)
-
-        executer.beforeExecute(new Action<GradleExecuter>() {
-            void execute(GradleExecuter e) {
-                if (http.running) {
-                    e.withArguments(
-                            "-D$JCenterPluginMapper.BINTRAY_API_OVERRIDE_URL_PROPERTY=$api.address",
-                            "-D$BaseRepositoryFactory.JCENTER_REPO_OVERRIDE_URL_PROPERTY=$jcenter.uri"
-                    )
-                }
-            }
-        })
-    }
-
-    void start() {
-        http.start()
-    }
-
-    @Override
-    protected void after() {
-        http.stop()
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyDescriptor.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyDescriptor.groovy
index 1eb1373..83b89d3 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyDescriptor.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyDescriptor.groovy
@@ -27,6 +27,9 @@ class IvyDescriptor {
     String revision
     String status
     String description
+    String branch
+    String resolver
+    Map<QName, String> extraInfo
 
     IvyDescriptor(File ivyFile) {
         def ivy = new XmlParser().parse(ivyFile)
@@ -34,8 +37,15 @@ class IvyDescriptor {
         module = ivy.info[0]. at module
         revision = ivy.info[0]. at revision
         status = ivy.info[0]. at status
+        branch = ivy.info[0]. at branch
+        resolver = ivy.info[0]. at resolver
         description = ivy.info[0].description[0]?.text()
 
+        extraInfo = [:]
+        ivy.info[0].children().findAll { it.name() instanceof QName }.each {
+            extraInfo[new javax.xml.namespace.QName(it.name().namespaceURI, it.name().localPart)] = it.text()
+        }
+
         ivy.configurations.conf.each {
             configurations[it. at name] = new IvyDescriptorConfiguration(
                     name: it. at name, visibility: it. at visibility, description: it. at description,
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileModule.groovy
index fad1431..0c1bfde 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileModule.groovy
@@ -15,11 +15,9 @@
  */
 package org.gradle.test.fixtures.ivy
 
-import org.apache.ivy.core.IvyPatternHelper
-import org.apache.ivy.core.module.id.ModuleId
-import org.apache.ivy.core.module.id.ModuleRevisionId
+import groovy.xml.MarkupBuilder
 import org.gradle.api.Action
-import org.gradle.api.internal.xml.XmlTransformer
+import org.gradle.internal.xml.XmlTransformer
 import org.gradle.test.fixtures.AbstractModule
 import org.gradle.test.fixtures.file.TestFile
 
@@ -30,22 +28,27 @@ class IvyFileModule extends AbstractModule implements IvyModule {
     final String organisation
     final String module
     final String revision
+    final boolean m2Compatible
     final List dependencies = []
     final Map<String, Map> configurations = [:]
     final List artifacts = []
     final Map extendsFrom = [:]
+    final Map extraAttributes = [:]
+    final Map extraInfo = [:]
+    String branch = null
     String status = "integration"
     boolean noMetaData
     int publishCount = 1
     XmlTransformer transformer = new XmlTransformer()
 
-    IvyFileModule(String ivyPattern, String artifactPattern, TestFile moduleDir, String organisation, String module, String revision) {
+    IvyFileModule(String ivyPattern, String artifactPattern, TestFile moduleDir, String organisation, String module, String revision, boolean m2Compatible) {
         this.ivyPattern = ivyPattern
         this.artifactPattern = artifactPattern
         this.moduleDir = moduleDir
         this.organisation = organisation
         this.module = module
         this.revision = revision
+        this.m2Compatible = m2Compatible
         configurations['runtime'] = [extendsFrom: [], transitive: true, visibility: 'public']
         configurations['default'] = [extendsFrom: ['runtime'], transitive: true, visibility: 'public']
     }
@@ -75,7 +78,8 @@ class IvyFileModule extends AbstractModule implements IvyModule {
     }
 
     IvyFileModule undeclaredArtifact(Map<String, ?> options) {
-        artifact(options + [undeclared: true])
+        def undeclaredArtifact = toArtifact(options) + [undeclared: true]
+        artifacts << undeclaredArtifact
         return this
     }
 
@@ -111,23 +115,57 @@ class IvyFileModule extends AbstractModule implements IvyModule {
     }
 
     IvyFileModule withStatus(String status) {
-        this.status = status;
+        this.status = status
+        return this
+    }
+
+    IvyFileModule withBranch(String branch) {
+        this.branch = branch
         return this
     }
 
     IvyFileModule withNoMetaData() {
-        noMetaData = true;
+        noMetaData = true
+        return this
+    }
+
+    IvyFileModule withExtraAttributes(Map extraAttributes) {
+        this.extraAttributes.putAll(extraAttributes)
+        return this
+    }
+
+    /**
+     * Keys in extra info will be prefixed with namespace prefix "ns" in this fixture.
+     */
+    IvyFileModule withExtraInfo(Map extraInfo) {
+        this.extraInfo.putAll(extraInfo)
         return this
     }
 
+    protected String getIvyFilePath() {
+        getArtifactFilePath(name: "ivy", type: "ivy", ext: "xml")
+    }
+
     TestFile getIvyFile() {
-        def path = IvyPatternHelper.substitute(ivyPattern, new ModuleRevisionId(new ModuleId(organisation, module), revision))
-        return moduleDir.file(path)
+        return moduleDir.file(ivyFilePath)
     }
 
     TestFile getJarFile() {
-        def path = IvyPatternHelper.substitute(artifactPattern, new ModuleRevisionId(new ModuleId(organisation, module), revision), null, "jar", "jar")
-        return moduleDir.file(path)
+        return moduleDir.file(jarFilePath)
+    }
+
+    protected String getJarFilePath() {
+        getArtifactFilePath(name: module, type: "jar", ext: "jar")
+    }
+
+    TestFile file(Map<String, ?> options) {
+        return moduleDir.file(getArtifactFilePath(options))
+    }
+
+    protected String getArtifactFilePath(Map<String, ?> options) {
+        def artifact = toArtifact(options)
+        def tokens = [organisation: organisation, module: module, revision: revision, artifact: artifact.name, type: artifact.type, ext: artifact.ext, classifier: artifact.classifier]
+        M2CompatibleIvyPatternHelper.substitute(artifactPattern, m2Compatible, tokens)
     }
 
     /**
@@ -163,69 +201,71 @@ class IvyFileModule extends AbstractModule implements IvyModule {
         }
 
         publish(ivyFile) { Writer writer ->
-            transformer.transform(writer, new Action<Writer>() {
-                void execute(Writer ivyFileWriter) {
-                    ivyFileWriter << """<?xml version="1.0" encoding="UTF-8"?>
-<ivy-module version="1.0" xmlns:m="http://ant.apache.org/ivy/maven">
-    <!-- ${getArtifactContent()} -->
-	<info organisation="${organisation}"
-		module="${module}"
-		revision="${revision}"
-		status="${status}"
-        publication="${getPublicationDate()}"
-	>"""
-        if (extendsFrom) {
-            ivyFileWriter << "<extends organisation='${extendsFrom.organisation}' module='${extendsFrom.module}' revision='${extendsFrom.revision}'"
-            if (extendsFrom.location) {
-                ivyFileWriter << " location='${extendsFrom.location}'"
+            transformer.transform(writer, { writeTo(it) } as Action)
+        }
+
+        return this
+    }
+
+    private writeTo(Writer ivyFileWriter) {
+        ivyFileWriter << """<?xml version="1.0" encoding="UTF-8"?>
+<ivy-module version="1.0" xmlns:m="http://ant.apache.org/ivy/maven" """
+        if (extraAttributes) {
+            ivyFileWriter << ' xmlns:e="http://ant.apache.org/ivy/extra"'
+        }
+        ivyFileWriter << "><!--" + artifactContent + "-->"
+
+        def builder = new MarkupBuilder(ivyFileWriter)
+        def infoAttrs = [organisation: organisation, module: module, revision: revision, status: status, publication: getPublicationDate()]
+        if (branch) {
+            infoAttrs.branch = branch
+        }
+        infoAttrs += extraAttributes.collectEntries {key, value -> ["e:$key", value]}
+        builder.info(infoAttrs) {
+            if (extendsFrom) {
+                "extends"(extendsFrom)
+            }
+            extraInfo.each { key, value ->
+                "ns:${key.name}"('xmlns:ns': "${key.namespace}", value)
             }
-            ivyFileWriter << "/>"
         }
-                    ivyFileWriter << """</info>
-	<configurations>"""
+        builder.configurations {
             configurations.each { name, config ->
-                ivyFileWriter << "<conf name='$name'"
+                def confAttrs = [name: name, visibility: config.visibility]
                 if (config.extendsFrom) {
-                    ivyFileWriter << " extends='${config.extendsFrom.join(',')}'"
+                    confAttrs.extends=config.extendsFrom.join(',')
                 }
                 if (!config.transitive) {
-                    ivyFileWriter << " transitive='false'"
+                    confAttrs.transitive='false'
                 }
-                ivyFileWriter << " visibility='$config.visibility'"
-                ivyFileWriter << "/>"
+                conf(confAttrs)
             }
-            ivyFileWriter << """</configurations>
-	<publications>
-"""
-            artifacts.each { artifact ->
-                if (!artifact.undeclared) {
-                    ivyFileWriter << """<artifact name="${artifact.name}" type="${artifact.type}" ext="${artifact.ext}" conf="${artifact.conf}" m:classifier="${artifact.classifier ?: ''}"/>
-"""
+        }
+        builder.publications {
+            artifacts.each { art ->
+                if (!art.undeclared) {
+                    def attrs = [name: art.name, type:art.type, ext: art.ext, conf:art.conf]
+                    if (art.classifier) {
+                        attrs["m:classifier"] = art.classifier
+                    }
+                    builder.artifact(attrs)
                 }
             }
-            ivyFileWriter << """
-	</publications>
-	<dependencies>
-"""
+        }
+        builder.dependencies {
             dependencies.each { dep ->
-                def confAttribute = dep.conf == null ? "" : """ conf="${dep.conf}" """
-                def revConstraint = dep.revConstraint == null ? "" : """ revConstraint="${dep.revConstraint}" """
-                ivyFileWriter << """<dependency org="${dep.organisation}" name="${dep.module}" rev="${dep.revision}" ${confAttribute} ${revConstraint}/>
-"""
-            }
-            ivyFileWriter << """
-    </dependencies>
-</ivy-module>
-        """
+                def depAttrs = [org: dep.organisation, name: dep.module, rev: dep.revision]
+                if (dep.conf) {
+                    depAttrs.conf = dep.conf
                 }
-            })
+                if (dep.revConstraint) {
+                    depAttrs.revConstraint = dep.revConstraint
+                }
+                dependency(depAttrs)
+            }
         }
-        return this
-    }
 
-    TestFile file(Map<String, ?> options) {
-        def artifact = toArtifact(options)
-        return moduleDir.file("${artifact.name}-${revision}${artifact.classifier ? '-' + artifact.classifier : ''}.${artifact.ext}")
+ivyFileWriter << '</ivy-module>'
     }
 
     @Override
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileRepository.groovy
index 9fd679f..b0ea17f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileRepository.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyFileRepository.groovy
@@ -15,16 +15,21 @@
  */
 package org.gradle.test.fixtures.ivy
 
-import org.apache.ivy.core.IvyPatternHelper
-import org.apache.ivy.core.module.id.ModuleId
-import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.gradle.test.fixtures.file.TestFile
 
 class IvyFileRepository implements IvyRepository {
     final TestFile rootDir
+    final boolean m2Compatible
+    final String dirPattern
+    final String ivyFilePattern
+    final String artifactFilePattern
 
-    IvyFileRepository(TestFile rootDir) {
+    IvyFileRepository(TestFile rootDir, boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
         this.rootDir = rootDir
+        this.m2Compatible = m2Compatible
+        this.dirPattern = dirPattern ?: "[organisation]/[module]/[revision]"
+        this.ivyFilePattern = ivyFilePattern ?: "ivy-[revision].xml"
+        this.artifactFilePattern = artifactFilePattern ?: "[artifact]-[revision](-[classifier])(.[ext])"
     }
 
     URI getUri() {
@@ -39,24 +44,20 @@ class IvyFileRepository implements IvyRepository {
         return "${uri}/${baseArtifactPattern}"
     }
 
-    String getIvyFilePattern() {
-        "ivy-[revision].xml"
-    }
-
     String getBaseIvyPattern() {
         "$dirPattern/$ivyFilePattern"
     }
 
-    String getArtifactFilePattern() {
-        "[artifact]-[revision](.[ext])"
-    }
-
     String getBaseArtifactPattern() {
         "$dirPattern/$artifactFilePattern"
     }
 
-    String getDirPattern() {
-        "[organisation]/[module]/[revision]"
+    String getDirPath(String organisation, String module, String revision) {
+        M2CompatibleIvyPatternHelper.substitute(dirPattern, organisation, module, revision, m2Compatible)
+    }
+
+    TestFile moduleDir(String organisation, String module) {
+        return this.module(organisation, module).moduleDir.parentFile
     }
 
     IvyFileModule module(String organisation, String module, Object revision = '1.0') {
@@ -69,9 +70,8 @@ class IvyFileRepository implements IvyRepository {
 
     private IvyFileModule createModule(String organisation, String module, String revision) {
         def revisionString = revision.toString()
-        def path = IvyPatternHelper.substitute(dirPattern, new ModuleRevisionId(new ModuleId(organisation, module), revisionString))
-        def moduleDir = rootDir.file(path)
-        return new IvyFileModule(ivyFilePattern, artifactFilePattern, moduleDir, organisation, module, revisionString)
+        def moduleDir = rootDir.file(getDirPath(organisation, module, revision))
+        return new IvyFileModule(ivyFilePattern, artifactFilePattern, moduleDir, organisation, module, revisionString, m2Compatible)
     }
 }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpModule.groovy
deleted file mode 100644
index eb9bae7..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpModule.groovy
+++ /dev/null
@@ -1,161 +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.test.fixtures.ivy
-
-import org.gradle.test.fixtures.HttpModule
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.maven.HttpArtifact
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class IvyHttpModule implements IvyModule, HttpModule {
-    public final IvyHttpRepository repository
-    private final IvyFileModule backingModule
-    private final HttpServer server
-    private final String prefix
-
-    IvyHttpModule(IvyHttpRepository repository, HttpServer server, String prefix, IvyFileModule backingModule) {
-        this.repository = repository
-        this.prefix = prefix
-        this.server = server
-        this.backingModule = backingModule
-    }
-
-    String getOrganisation() {
-        return backingModule.organisation
-    }
-
-    String getModule() {
-        return backingModule.module
-    }
-
-    String getRevision() {
-        return backingModule.revision
-    }
-
-    IvyDescriptor getParsedIvy() {
-        return backingModule.parsedIvy
-    }
-
-    IvyHttpModule publish() {
-        backingModule.publish()
-        return this
-    }
-
-    IvyHttpModule publishWithChangedContent() {
-        backingModule.publishWithChangedContent()
-        return this
-    }
-
-    IvyHttpModule withNoMetaData() {
-        backingModule.withNoMetaData()
-        return this
-    }
-
-    IvyHttpModule withStatus(String status) {
-        backingModule.withStatus(status)
-        return this
-    }
-
-    IvyHttpModule dependsOn(String organisation, String module, String revision) {
-        backingModule.dependsOn(organisation, module, revision)
-        return this
-    }
-
-    IvyHttpModule dependsOn(Map<String, ?> attributes) {
-        backingModule.dependsOn(attributes)
-        return this
-    }
-
-    IvyHttpModule artifact(Map<String, ?> options = [:]) {
-        backingModule.artifact(options)
-        return this
-    }
-
-    IvyHttpModule undeclaredArtifact(Map<String, ?> options = [:]) {
-        backingModule.undeclaredArtifact(options)
-        return this
-    }
-
-    IvyHttpModule extendsFrom(Map<String, ?> attributes) {
-        backingModule.extendsFrom(attributes)
-        return this
-    }
-
-    IvyHttpModule configuration(Map<String, ?> options = [:], String name) {
-        backingModule.configuration(options, name)
-        return this
-    }
-
-    IvyHttpModule withXml(Closure action) {
-        backingModule.withXml(action)
-        return this
-    }
-
-    TestFile getIvyFile() {
-        return backingModule.ivyFile
-    }
-
-    TestFile getJarFile() {
-        return backingModule.jarFile
-    }
-
-    IvyModuleHttpArtifact getArtifact(Map<String, ?> options) {
-        return new IvyModuleHttpArtifact(server, prefix, backingModule.file(options))
-    }
-
-    void allowAll() {
-        server.allowGetOrHead(prefix, backingModule.moduleDir)
-    }
-
-    HttpArtifact getIvy() {
-        return new IvyModuleHttpArtifact(server, prefix, ivyFile)
-    }
-
-    HttpArtifact getJar() {
-        return new IvyModuleHttpArtifact(server, prefix, jarFile)
-    }
-
-    void assertIvyAndJarFilePublished() {
-        backingModule.assertIvyAndJarFilePublished()
-    }
-
-    private class IvyModuleHttpArtifact extends HttpArtifact {
-        final TestFile backingFile
-
-        IvyModuleHttpArtifact(HttpServer server, String modulePath, TestFile backingFile) {
-            super(server, modulePath)
-            this.backingFile = backingFile
-        }
-
-        @Override
-        TestFile getFile() {
-            return backingFile
-        }
-
-        @Override
-        protected TestFile getSha1File() {
-            return backingModule.getSha1File(backingFile)
-        }
-
-        @Override
-        protected TestFile getMd5File() {
-            return backingModule.getMd5File(backingFile)
-        }
-    }
-}
-
-
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpRepository.groovy
deleted file mode 100644
index 4f05bce..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyHttpRepository.groovy
+++ /dev/null
@@ -1,67 +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.test.fixtures.ivy
-
-import org.gradle.test.fixtures.HttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class IvyHttpRepository implements IvyRepository, HttpRepository {
-    private final HttpServer server
-    private final IvyFileRepository backingRepository
-    private final String contextPath
-
-    IvyHttpRepository(HttpServer server, String contextPath = "/repo", IvyFileRepository backingRepository) {
-        if (!contextPath.startsWith("/")) {
-            throw new IllegalArgumentException("Context path must start with '/'")
-        }
-        this.contextPath = contextPath
-        this.server = server
-        this.backingRepository = backingRepository
-    }
-
-    URI getUri() {
-        return new URI("http://localhost:${server.port}${contextPath}")
-    }
-
-    String getIvyPattern() {
-        return "$uri/${backingRepository.baseIvyPattern}"
-    }
-
-    String getArtifactPattern() {
-        return "$uri/${backingRepository.baseArtifactPattern}"
-    }
-
-    void allowDirectoryListGet(String organisation, String module) {
-        server.allowGetDirectoryListing("$contextPath/$organisation/$module/", backingRepository.module(organisation, module, "1.0").moduleDir.parentFile)
-    }
-
-    void expectDirectoryListGet(String organisation, String module) {
-        server.expectGetDirectoryListing("$contextPath/$organisation/$module/", backingRepository.module(organisation, module, "1.0").moduleDir.parentFile)
-    }
-
-    void expectDirectoryListGetMissing(String organisation, String module) {
-        server.expectGetMissing("$contextPath/$organisation/$module/")
-    }
-
-    void expectDirectoryListGetBroken(String organisation, String module) {
-        server.expectGetBroken("$contextPath/$organisation/$module/")
-    }
-
-    IvyHttpModule module(String organisation, String module, Object revision = "1.0") {
-        return new IvyHttpModule(this, server, "$contextPath/$organisation/$module/$revision", backingRepository.module(organisation, module, revision))
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyModule.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyModule.java
index 76e43f7..631f395 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyModule.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyModule.java
@@ -71,7 +71,19 @@ public interface IvyModule extends Module {
     IvyDescriptor getParsedIvy();
 
     /**
+     * Asserts that an ivy.xml is present
+     */
+    void assertPublished();
+
+    /**
+     * Asserts that exactly the given artifacts, plus checksum files, have been published.
+     */
+    void assertArtifactsPublished(String... names);
+
+    /**
      * Assert that exactly the ivy.xml and jar file for this module, plus checksum files, have been published.
      */
     void assertIvyAndJarFilePublished();
+
+    void assertPublishedAsJavaModule();
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyRepository.groovy
index b33b0cc..eb93f49 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyRepository.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/IvyRepository.groovy
@@ -18,8 +18,6 @@ package org.gradle.test.fixtures.ivy
 import org.gradle.test.fixtures.Repository
 
 interface IvyRepository extends Repository {
-    URI getUri()
-
     String getArtifactPattern()
 
     String getIvyPattern()
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/M2CompatibleIvyPatternHelper.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/M2CompatibleIvyPatternHelper.groovy
new file mode 100644
index 0000000..e9e948d
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/M2CompatibleIvyPatternHelper.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.test.fixtures.ivy
+
+import org.apache.ivy.core.IvyPatternHelper
+import org.apache.ivy.core.module.id.ModuleId
+import org.apache.ivy.core.module.id.ModuleRevisionId
+
+class M2CompatibleIvyPatternHelper {
+
+    static String substitute(String pattern, String organisation, String module, String revision, boolean m2Compatible) {
+        def organisationToken = organisationToken(m2Compatible, organisation)
+        IvyPatternHelper.substitute(pattern, new ModuleRevisionId(new ModuleId(organisationToken, module), revision))
+    }
+
+    private static String organisationToken(boolean m2Compatible, String organisation) {
+        m2Compatible ? organisation.replaceAll(/\./, '/') : organisation
+    }
+
+    static String substitute(String pattern, boolean m2Compatible, Map<String, String> tokens) {
+        if (tokens.containsKey('organisation')) {
+            tokens.put('organisation', organisationToken(m2Compatible, tokens.get('organisation')))
+        }
+        IvyPatternHelper.substituteTokens(pattern, tokens)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyModule.groovy
new file mode 100644
index 0000000..4246a05
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyModule.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.ivy
+
+import org.gradle.test.fixtures.resource.RemoteArtifact
+
+interface RemoteIvyModule extends IvyModule {
+    RemoteArtifact getIvy()
+    RemoteArtifact getJar()
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyRepository.groovy
new file mode 100644
index 0000000..c891210
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/ivy/RemoteIvyRepository.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.ivy
+
+import org.gradle.test.fixtures.resource.RemoteResource
+
+interface RemoteIvyRepository extends IvyRepository {
+
+    RemoteIvyModule module(String organisation, String module)
+
+    RemoteIvyModule module(String organisation, String module, Object revision)
+
+    String getBaseIvyPattern()
+
+    String getBaseArtifactPattern()
+
+    RemoteResource directoryList(String organisation, String module)
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/keystore/TestKeyStore.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/keystore/TestKeyStore.groovy
new file mode 100644
index 0000000..7b107f5
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/keystore/TestKeyStore.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.keystore
+import org.apache.commons.io.FileUtils
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.server.http.HttpServer
+
+class TestKeyStore {
+    TestFile trustStore
+    String trustStorePassword = "asdfgh"
+    TestFile keyStore
+    String keyStorePassword = "asdfgh"
+
+    static TestKeyStore init(TestFile rootDir) {
+        new TestKeyStore(rootDir)
+    }
+
+    private TestKeyStore(TestFile rootDir) {
+        keyStore = rootDir.file("clientStore")
+        trustStore = rootDir.file("serverStore")
+
+        copyCertFile("test-key-store/keyStore", keyStore)
+        copyCertFile("test-key-store/trustStore", trustStore)
+    }
+
+    private static void copyCertFile(String s, TestFile clientStore) {
+        URL fileUrl = ClassLoader.getSystemResource(s);
+        FileUtils.copyURLToFile(fileUrl, clientStore);
+    }
+
+    void enableSslWithServerCert(HttpServer server) {
+        server.enableSsl(trustStore.path, trustStorePassword)
+    }
+
+    void enableSslWithServerAndClientCerts(HttpServer server) {
+        server.enableSsl(trustStore.path, trustStorePassword, keyStore.path, keyStorePassword)
+    }
+
+    void enableSslWithServerAndBadClientCert(HttpServer server) {
+        // intentionally use wrong trust store for server
+        server.enableSsl(trustStore.path, trustStorePassword, trustStore.path, keyStorePassword)
+    }
+
+    void configureServerCert(GradleExecuter executer) {
+        executer.withArgument("-Djavax.net.ssl.trustStore=$trustStore.path")
+                .withArgument("-Djavax.net.ssl.trustStorePassword=$trustStorePassword")
+    }
+
+    void configureIncorrectServerCert(GradleExecuter executer) {
+        // intentionally use wrong trust store
+        executer.withArgument("-Djavax.net.ssl.trustStore=$keyStore.path")
+                .withArgument("-Djavax.net.ssl.trustStorePassword=$trustStorePassword")
+    }
+
+    void configureServerAndClientCerts(GradleExecuter executer) {
+        executer.withArgument("-Djavax.net.ssl.trustStore=$trustStore.path")
+                .withArgument("-Djavax.net.ssl.trustStorePassword=$trustStorePassword")
+                .withArgument("-Djavax.net.ssl.keyStore=$keyStore.path")
+                .withArgument("-Djavax.net.ssl.keyStorePassword=$keyStorePassword")
+    }
+
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy
index bc4080b..e474813 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/AbstractMavenModule.groovy
@@ -28,10 +28,11 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
     final String groupId
     final String artifactId
     final String version
-    String parentPomSection
+    Map<String, String> parentPom
     String type = 'jar'
     String packaging
     int publishCount = 1
+    boolean noMetaData
     private final List dependencies = []
     private final List artifacts = []
     final updateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
@@ -45,13 +46,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
     }
 
     MavenModule parent(String group, String artifactId, String version) {
-        parentPomSection = """
-<parent>
-  <groupId>${group}</groupId>
-  <artifactId>${artifactId}</artifactId>
-  <version>${version}</version>
-</parent>
-"""
+        parentPom = [groupId: group, artifactId: artifactId, version: version]
         return this
     }
 
@@ -90,6 +85,11 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
         return this
     }
 
+    @Override
+    MavenModule dependsOn(MavenModule module) {
+        return dependsOn(module.groupId, module.artifactId, module.version)
+    }
+
     MavenModule dependsOn(String group, String artifactId, String version, String type = null) {
         this.dependencies << [groupId: group, artifactId: artifactId, version: version, type: type]
         return this
@@ -168,24 +168,25 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
      * Asserts that exactly the given artifacts have been deployed, along with their checksum files
      */
     void assertArtifactsPublished(String... names) {
-        def artifactNames = names as Set
-        if (publishesMetaDataFile()) {
-            artifactNames.add(MAVEN_METADATA_FILE)
+        Set allFileNames = []
+        for (name in names) {
+            allFileNames.addAll([name, "${name}.sha1", "${name}.md5"])
         }
-        assert moduleDir.isDirectory()
-        Set actual = moduleDir.list() as Set
-        for (name in artifactNames) {
-            assert actual.remove(name)
-
-            if(publishesHashFiles()) {
-                assert actual.remove("${name}.md5" as String)
-                assert actual.remove("${name}.sha1" as String)
-            }
+
+        assert moduleDir.list() as Set == allFileNames
+        for (name in names) {
+            assertChecksumsPublishedFor(moduleDir.file(name))
         }
-        assert actual.isEmpty()
     }
 
-    //abstract String getPublishArtifactVersion()
+    void assertChecksumsPublishedFor(TestFile testFile) {
+        def sha1File = sha1File(testFile)
+        sha1File.assertIsFile()
+        assert new BigInteger(sha1File.text, 16) == getHash(testFile, "SHA1")
+        def md5File = md5File(testFile)
+        md5File.assertIsFile()
+        assert new BigInteger(md5File.text, 16) == getHash(testFile, "MD5")
+    }
 
     MavenPom getParsedPom() {
         return new MavenPom(pomFile)
@@ -196,6 +197,10 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
     }
 
     TestFile getPomFile() {
+        return getArtifactFile(type: 'pom')
+    }
+
+    TestFile getPomFileForPublish() {
         return moduleDir.file("$artifactId-${publishArtifactVersion}.pom")
     }
 
@@ -244,44 +249,38 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
             }
         }
 
-        publish(pomFile) { Writer writer ->
+        publish(pomFileForPublish) { Writer writer ->
             def pomPackaging = packaging ?: type;
-            writer << """
-<project xmlns="http://maven.apache.org/POM/4.0.0">
-  <!-- ${getArtifactContent()} -->
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>$groupId</groupId>
-  <artifactId>$artifactId</artifactId>
-  <packaging>$pomPackaging</packaging>
-  <version>$version</version>
-  <description>Published on $publishTimestamp</description>"""
-
-            if (parentPomSection) {
-                writer << "\n$parentPomSection\n"
-            }
-
-            if (!dependencies.empty) {
-                writer << """
-  <dependencies>"""
-            }
-
-            dependencies.each { dependency ->
-                def typeAttribute = dependency['type'] == null ? "" : "<type>$dependency.type</type>"
-                writer << """
-    <dependency>
-      <groupId>$dependency.groupId</groupId>
-      <artifactId>$dependency.artifactId</artifactId>
-      <version>$dependency.version</version>
-      $typeAttribute
-    </dependency>"""
-            }
-
-            if (!dependencies.empty) {
-                writer << """
-  </dependencies>"""
+            new MarkupBuilder(writer).project {
+                mkp.comment(artifactContent)
+                modelVersion("4.0.0")
+                groupId(groupId)
+                artifactId(artifactId)
+                version(version)
+                packaging(pomPackaging)
+                description("Published on ${publishTimestamp}")
+                if (parentPom) {
+                    parent {
+                        groupId(parentPom.groupId)
+                        artifactId(parentPom.artifactId)
+                        version(parentPom.version)
+                    }
+                }
+                if (dependencies) {
+                    dependencies {
+                        dependencies.each { dep ->
+                            dependency {
+                                groupId(dep.groupId)
+                                artifactId(dep.artifactId)
+                                version(dep.version)
+                                if (dep.type) {
+                                    type(dep.type)
+                                }
+                            }
+                        }
+                    }
+                }
             }
-
-            writer << "\n</project>"
         }
         return this
     }
@@ -308,9 +307,16 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule
 
     abstract String getMetaDataFileContent()
 
-    MavenModule publish() {
 
-        publishPom()
+    MavenModule withNoMetaData() {
+        noMetaData = true
+        return this
+    }
+
+    MavenModule publish() {
+        if(!noMetaData) {
+            publishPom()
+        }
 
         artifacts.each { artifact ->
             publishArtifact(artifact)
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/BasicHttpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/BasicHttpResource.groovy
deleted file mode 100644
index b0a7f3b..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/BasicHttpResource.groovy
+++ /dev/null
@@ -1,41 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class BasicHttpResource extends HttpResource {
-    private final String path
-    private final File file
-
-    BasicHttpResource(HttpServer httpServer, File file, String path) {
-        super(httpServer)
-        this.file = file
-        this.path = path
-    }
-
-    @Override
-    TestFile getFile() {
-        return file
-    }
-
-    @Override
-    protected String getPath() {
-        return path
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/DelegatingMavenModule.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/DelegatingMavenModule.java
new file mode 100644
index 0000000..4a6f521
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/DelegatingMavenModule.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.maven;
+
+import org.gradle.test.fixtures.file.TestFile;
+
+public abstract class DelegatingMavenModule<T extends MavenModule> implements MavenModule {
+    private final MavenModule backingModule;
+
+    public DelegatingMavenModule(MavenModule backingModule) {
+        this.backingModule = backingModule;
+    }
+
+    private T t() {
+        return (T) this;
+    }
+
+    @Override
+    public void assertPublishedAsJavaModule() {
+        backingModule.assertPublishedAsJavaModule();
+    }
+
+    @Override
+    public T dependsOn(String group, String artifactId, String version) {
+        backingModule.dependsOn(group, artifactId, version);
+        return t();
+    }
+
+    @Override
+    public T dependsOn(String group, String artifactId, String version, String type) {
+        backingModule.dependsOn(group, artifactId, version, type);
+        return t();
+    }
+
+    @Override
+    public T dependsOn(MavenModule module) {
+        backingModule.dependsOn(module);
+        return t();
+    }
+
+    @Override
+    public TestFile getArtifactFile() {
+        return backingModule.getArtifactFile();
+    }
+
+    @Override
+    public String getArtifactId() {
+        return backingModule.getArtifactId();
+    }
+
+    @Override
+    public String getGroupId() {
+        return backingModule.getGroupId();
+    }
+
+    @Override
+    public TestFile getMetaDataFile() {
+        return backingModule.getMetaDataFile();
+    }
+
+    @Override
+    public MavenMetaData getRootMetaData() {
+        return backingModule.getRootMetaData();
+    }
+
+    @Override
+    public MavenPom getParsedPom() {
+        return backingModule.getParsedPom();
+    }
+
+    @Override
+    public TestFile getPomFile() {
+        return backingModule.getPomFile();
+    }
+
+    @Override
+    public String getPublishArtifactVersion() {
+        return backingModule.getPublishArtifactVersion();
+    }
+
+    @Override
+    public String getVersion() {
+        return backingModule.getVersion();
+    }
+
+    @Override
+    public T hasPackaging(String packaging) {
+        backingModule.hasPackaging(packaging);
+        return t();
+    }
+
+    @Override
+    public T hasType(String type) {
+        backingModule.hasType(type);
+        return t();
+    }
+
+    @Override
+    public T parent(String group, String artifactId, String version) {
+        backingModule.parent(group, artifactId, version);
+        return t();
+    }
+
+    @Override
+    public T publish() {
+        backingModule.publish();
+        return t();
+    }
+
+    @Override
+    public T publishPom() {
+        backingModule.publishPom();
+        return t();
+    }
+
+    @Override
+    public T publishWithChangedContent() {
+        backingModule.publishWithChangedContent();
+        return t();
+    }
+
+    @Override
+    public T withNonUniqueSnapshots() {
+        backingModule.withNonUniqueSnapshots();
+        return t();
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpArtifact.groovy
deleted file mode 100644
index 14fc453..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpArtifact.groovy
+++ /dev/null
@@ -1,66 +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.test.fixtures.maven
-
-import org.gradle.internal.hash.HashUtil
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-abstract class HttpArtifact extends HttpResource {
-
-    String modulePath
-
-    public HttpArtifact(HttpServer server, String modulePath) {
-        super(server)
-        this.modulePath = modulePath
-    }
-
-    HttpResource getMd5() {
-        return new BasicHttpResource(server, getMd5File(), "${path}.md5")
-    }
-
-    HttpResource getSha1() {
-        return new BasicHttpResource(server, getSha1File(), "${path}.sha1")
-    }
-
-    URI getUri() {
-        return new URI("http://localhost:${server.port}${path}")
-    }
-
-    protected String getPath() {
-        "${modulePath}/${file.name}"
-    }
-
-    protected abstract TestFile getSha1File();
-
-    protected abstract TestFile getMd5File();
-
-    abstract TestFile getFile();
-
-    void verifyChecksums() {
-        def sha1File = getSha1File()
-        sha1File.assertIsFile()
-        assert new BigInteger(sha1File.text, 16) == new BigInteger(getHash(getFile(), "sha1"), 16)
-        def md5File = getMd5File()
-        md5File.assertIsFile()
-        assert new BigInteger(md5File.text, 16) == new BigInteger(getHash(getFile(), "md5"), 16)
-    }
-
-    protected String getHash(TestFile file, String algorithm) {
-        HashUtil.createHash(file, algorithm.toUpperCase()).asHexString()
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpResource.groovy
deleted file mode 100644
index 734dcb8..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/HttpResource.groovy
+++ /dev/null
@@ -1,74 +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.test.fixtures.maven
-
-import org.gradle.api.artifacts.repositories.PasswordCredentials
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-abstract class HttpResource {
-
-    protected HttpServer server
-
-    public HttpResource(HttpServer server) {
-        this.server = server
-    }
-
-    void allowGetOrHead() {
-        server.allowGetOrHead(getPath(), file)
-
-    }
-    void expectGet() {
-        server.expectGet(getPath(), file)
-    }
-
-    void expectGetBroken() {
-        server.expectGetBroken(getPath())
-    }
-
-    void expectGetMissing(PasswordCredentials credentials = null) {
-        server.expectGetMissing(getPath(), credentials)
-    }
-
-    void expectHead() {
-        server.expectHead(getPath(), file)
-    }
-
-    void expectHeadMissing() {
-        server.expectHeadMissing(path)
-    }
-
-    void expectHeadBroken() {
-        server.expectHeadBroken(path)
-    }
-
-    void expectPut(PasswordCredentials credentials) {
-        expectPut(200, credentials)
-    }
-
-    void expectPut(String username, String password) {
-        server.expectPut(getPath(), username, password, getFile())
-    }
-
-    void expectPut(Integer statusCode = 200, PasswordCredentials credentials = null) {
-        server.expectPut(getPath(), getFile(), statusCode, credentials)
-    }
-
-    abstract TestFile getFile();
-
-    abstract protected String getPath();
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependency.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependency.groovy
index 45ee411..7bf9bb2 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependency.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependency.groovy
@@ -22,6 +22,7 @@ class MavenDependency {
     String version
     String classifier
     String type
+    Collection<MavenDependencyExclusion> exclusions = []
 
     MavenDependency hasType(def type) {
         assert this.type == type
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependencyExclusion.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependencyExclusion.groovy
new file mode 100644
index 0000000..d7590d5
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenDependencyExclusion.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.maven
+
+class MavenDependencyExclusion {
+    String groupId
+    String artifactId
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpArtifact.groovy
deleted file mode 100644
index ca35fbf..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpArtifact.groovy
+++ /dev/null
@@ -1,45 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class MavenHttpArtifact extends HttpArtifact {
-    private final MavenFileModule backingModule;
-    private final Map options
-
-    public MavenHttpArtifact(HttpServer server, String modulePath, MavenFileModule backingModule, Map<String, ?> options = [:]) {
-        super(server, modulePath)
-        this.options = options
-        this.backingModule = backingModule;
-    }
-
-    @Override
-    TestFile getSha1File() {
-        backingModule.getSha1File(file)
-    }
-
-    @Override
-    TestFile getMd5File() {
-        backingModule.getMd5File(file)
-    }
-
-    TestFile getFile() {
-        return backingModule.getArtifactFile(options)
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpModule.groovy
deleted file mode 100644
index dfe0ecb..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpModule.groovy
+++ /dev/null
@@ -1,143 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.HttpModule
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class MavenHttpModule implements MavenModule, HttpModule {
-    private final HttpServer server
-    private final String moduleRootPath
-    private final MavenFileModule backingModule
-
-    MavenHttpModule(HttpServer server, String repoRoot, MavenFileModule backingModule) {
-        this.backingModule = backingModule
-        this.server = server
-        this.moduleRootPath = "${repoRoot}/${backingModule.groupId.replace('.', '/')}/${backingModule.artifactId}"
-    }
-
-    protected String getModuleVersionPath() {
-        "${moduleRootPath}/${backingModule.version}"
-    }
-
-    HttpArtifact getArtifact(Map options = [:]) {
-        return new MavenHttpArtifact(server, "${moduleRootPath}/${backingModule.version}", backingModule, options)
-    }
-
-    /**
-     * Adds an additional artifact to this module.
-     * @param options Can specify any of: type or classifier
-     */
-    HttpArtifact artifact(Map<String, ?> options = [:]) {
-        backingModule.artifact(options)
-        return new MavenHttpArtifact(server, "${moduleRootPath}/${backingModule.version}", backingModule, options)
-    }
-
-    MavenHttpModule withSourceAndJavadoc() {
-        artifact(classifier: "sources")
-        artifact(classifier: "javadoc")
-        return this
-    }
-
-    MavenHttpModule publish() {
-        backingModule.publish()
-        return this
-    }
-
-    MavenHttpModule publishPom() {
-        backingModule.publishPom()
-        return this
-    }
-
-    MavenHttpModule publishWithChangedContent() {
-        backingModule.publishWithChangedContent()
-        return this
-    }
-
-    MavenHttpModule withNonUniqueSnapshots() {
-        backingModule.withNonUniqueSnapshots()
-        return this
-    }
-
-    MavenHttpModule parent(String group, String artifactId, String version) {
-        backingModule.parent(group, artifactId, version)
-        return this
-    }
-
-    MavenHttpModule dependsOn(String group, String artifactId, String version) {
-        backingModule.dependsOn(group, artifactId, version)
-        return this
-    }
-
-    MavenHttpModule dependsOn(String group, String artifactId, String version, String type) {
-        backingModule.dependsOn(group, artifactId, version, type)
-        return this
-    }
-
-    MavenHttpModule hasPackaging(String packaging) {
-        backingModule.hasPackaging(packaging)
-        return this
-    }
-
-    MavenHttpModule hasType(String type) {
-        backingModule.hasType(type)
-        return this
-    }
-
-    TestFile getPomFile() {
-        return backingModule.pomFile
-    }
-
-    TestFile getArtifactFile(Map options = [:]) {
-        return backingModule.getArtifactFile(options)
-    }
-
-    TestFile getMetaDataFile() {
-        return backingModule.metaDataFile
-    }
-
-    MavenPom getParsedPom() {
-        return backingModule.parsedPom;
-    }
-
-    PomHttpArtifact getPom() {
-        return new PomHttpArtifact(server, getModuleVersionPath(), backingModule)
-    }
-
-    MetaDataArtifact getRootMetaData() {
-        return new MetaDataArtifact(server, "$moduleRootPath", backingModule)
-    }
-
-    TestFile getRootMetaDataFile() {
-        return backingModule.rootMetaDataFile
-    }
-
-    void allowAll() {
-        server.allowGetOrHead(moduleVersionPath, backingModule.moduleDir)
-    }
-
-    HttpResource getMetaData() {
-        return new BasicHttpResource(server, metaDataFile, getMetaDataPath())
-    }
-
-    String getMetaDataPath() {
-        "$moduleVersionPath/$metaDataFile.name"
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpRepository.groovy
deleted file mode 100644
index 2b087e2..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenHttpRepository.groovy
+++ /dev/null
@@ -1,60 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.HttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-
-/**
- * A fixture for dealing with remote HTTP Maven repositories.
- */
-class MavenHttpRepository implements MavenRepository, HttpRepository {
-    private final HttpServer server
-    private final MavenFileRepository backingRepository
-    private final String contextPath
-
-    MavenHttpRepository(HttpServer server, String contextPath = "/repo", MavenFileRepository backingRepository) {
-        if (!contextPath.startsWith("/")) {
-            throw new IllegalArgumentException("Context path must start with '/'")
-        }
-        this.contextPath = contextPath
-        this.server = server
-        this.backingRepository = backingRepository
-    }
-
-    URI getUri() {
-        return new URI("http://localhost:${server.port}${contextPath}")
-    }
-
-    HttpResource getModuleMetaData(String groupId, String artifactId) {
-        return module(groupId, artifactId).rootMetaData
-    }
-
-    void expectDirectoryListGet(String groupId, String artifactId) {
-        def path = "${groupId.replace('.', '/')}/$artifactId/"
-        server.expectGetDirectoryListing("$contextPath/$path", backingRepository.getRootDir().file(path))
-    }
-
-    MavenHttpModule module(String groupId, String artifactId) {
-        return module(groupId, artifactId, "1.0")
-    }
-
-    MavenHttpModule module(String groupId, String artifactId, Object version) {
-        def backingModule = backingRepository.module(groupId, artifactId, version)
-        return new MavenHttpModule(server, contextPath, backingModule)
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenLocalModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenLocalModule.groovy
index a0a8117..67d273a 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenLocalModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenLocalModule.groovy
@@ -64,4 +64,10 @@ class MavenLocalModule extends AbstractMavenModule {
     protected boolean publishesHashFiles() {
         false
     }
+
+    /* No checksums published for local modules */
+    @Override
+    void assertArtifactsPublished(String... names) {
+        assert moduleDir.list() as Set == names as Set
+    }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenModule.groovy
index 07e4118..3ad3c30 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenModule.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenModule.groovy
@@ -21,7 +21,7 @@ import org.gradle.test.fixtures.file.TestFile
 interface MavenModule extends Module {
     /**
      * Publishes the pom.xml plus main artifact, plus any additional artifacts for this module. Publishes only those artifacts whose content has
-     * changed since the last call to {@code #publish()}.
+     * changed since the last call to {@code # publish ( )}.
      */
     MavenModule publish()
 
@@ -40,6 +40,8 @@ interface MavenModule extends Module {
 
     MavenModule parent(String group, String artifactId, String version)
 
+    MavenModule dependsOn(MavenModule module)
+
     MavenModule dependsOn(String group, String artifactId, String version)
 
     MavenModule dependsOn(String group, String artifactId, String version, String type)
@@ -51,6 +53,19 @@ interface MavenModule extends Module {
      */
     MavenModule hasType(String type)
 
+    /**
+     * Asserts exactly pom and jar published, along with checksums.
+     */
+    void assertPublishedAsJavaModule()
+
+    String getPublishArtifactVersion()
+
+    String getGroupId()
+
+    String getArtifactId()
+
+    String getVersion()
+
     TestFile getPomFile()
 
     TestFile getArtifactFile()
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenPom.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenPom.groovy
index ac38347..019761f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenPom.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenPom.groovy
@@ -38,16 +38,28 @@ class MavenPom {
                 def scopeElement = dep.scope
                 def scopeName = scopeElement ? scopeElement.text() : "runtime"
                 def scope = scopes[scopeName]
+                def exclusions = []
                 if (!scope) {
                     scope = new MavenScope()
                     scopes[scopeName] = scope
                 }
+                if (dep.exclusions){
+                    dep.exclusions.exclusion.each { excl ->
+                        MavenDependencyExclusion exclusion = new MavenDependencyExclusion(
+                            groupId: excl.groupId.text(),
+                            artifactId: excl.artifactId.text(),
+                        )
+                        exclusions << exclusion
+                    }
+
+                }
                 MavenDependency mavenDependency = new MavenDependency(
                         groupId: dep.groupId.text(),
                         artifactId: dep.artifactId.text(),
                         version: dep.version.text(),
                         classifier: dep.classifier ? dep.classifier.text() : null,
-                        type: dep.type ? dep.type.text() : null
+                        type: dep.type ? dep.type.text() : null,
+                        exclusions: exclusions,
                 )
                 def key = "${mavenDependency.groupId}:${mavenDependency.artifactId}:${mavenDependency.version}"
                 key += mavenDependency.classifier ? ":${mavenDependency.classifier}" : ""
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenRepository.groovy
index 86a7952..a86c32c 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenRepository.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MavenRepository.groovy
@@ -21,8 +21,6 @@ import org.gradle.test.fixtures.Repository
  * A fixture for dealing with Maven repositories.
  */
 interface MavenRepository extends Repository {
-    URI getUri()
-
     MavenModule module(String groupId, String artifactId)
 
     MavenModule module(String groupId, String artifactId, Object version)
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MetaDataArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MetaDataArtifact.groovy
deleted file mode 100644
index 47fdf61..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/MetaDataArtifact.groovy
+++ /dev/null
@@ -1,48 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class MetaDataArtifact extends HttpArtifact implements MavenMetaData {
-    MavenFileModule backingModule
-
-    MetaDataArtifact(HttpServer httpServer, String path, MavenFileModule backingModule) {
-        super(httpServer, path)
-        this.backingModule = backingModule
-    }
-
-    @Override
-    TestFile getSha1File() {
-        backingModule.getSha1File(file)
-    }
-
-    @Override
-    TestFile getMd5File() {
-        backingModule.getMd5File(file)
-    }
-
-    @Override
-    TestFile getFile() {
-        return backingModule.rootMetaDataFile
-    }
-
-    List<String> getVersions() {
-        backingModule.rootMetaData.versions
-    }
-}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/PomHttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/PomHttpArtifact.groovy
deleted file mode 100644
index 41ee404..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/maven/PomHttpArtifact.groovy
+++ /dev/null
@@ -1,57 +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.test.fixtures.maven
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.HttpServer
-
-class PomHttpArtifact extends HttpArtifact {
-    MavenFileModule backingModule
-
-    PomHttpArtifact(HttpServer httpServer, String path, MavenFileModule backingModule) {
-        super(httpServer, path)
-        this.backingModule = backingModule
-    }
-
-    @Override
-    void expectGetMissing() {
-        server.expectGetMissing(getPath() - getFile().name + getMissingPomName());
-    }
-
-    @Override
-    TestFile getSha1File() {
-        backingModule.getSha1File(file)
-    }
-
-    @Override
-    TestFile getMd5File() {
-        backingModule.getMd5File(file)
-    }
-
-    @Override
-    TestFile getFile() {
-        return backingModule.pomFile
-    }
-
-    private String getMissingPomName() {
-        if (backingModule.version.endsWith("-SNAPSHOT")) {
-            return "${backingModule.artifactId}-${backingModule.version}.pom"
-        } else {
-            return getFile().name
-        }
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/plugin/PluginBuilder.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/plugin/PluginBuilder.groovy
index c23af4d..e89d910 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/plugin/PluginBuilder.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/plugin/PluginBuilder.groovy
@@ -18,7 +18,11 @@ package org.gradle.test.fixtures.plugin
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.Task
 import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.model.Mutate
+import org.gradle.model.RuleSource
+import org.gradle.model.collection.CollectionBuilder
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.util.TextUtil
 
@@ -29,10 +33,8 @@ class PluginBuilder {
     String packageName = "org.gradle.test"
 
     final Map<String, String> pluginIds = [:]
-    private final GradleExecuter executer
 
-    PluginBuilder(GradleExecuter executer, TestFile projectDir) {
-        this.executer = executer
+    PluginBuilder(TestFile projectDir) {
         this.projectDir = projectDir
     }
 
@@ -59,7 +61,7 @@ class PluginBuilder {
         file("build.gradle").text = (generateManagedBuildScript() + additions)
     }
 
-    void publishTo(TestFile testFile) {
+    void publishTo(GradleExecuter executer, TestFile testFile) {
         generateBuildScript """
             jar {
                 archiveName = "$testFile.name"
@@ -71,6 +73,11 @@ class PluginBuilder {
         executer.inDirectory(projectDir).withTasks("jar").run()
     }
 
+    void generateForBuildSrc() {
+        generateBuildScript()
+        writePluginDescriptors(pluginIds)
+    }
+
     protected void writePluginDescriptors(Map<String, String> pluginIds) {
         descriptorsDir.deleteDir()
         pluginIds.each { id, className ->
@@ -82,10 +89,14 @@ class PluginBuilder {
         file("src/main/resources/META-INF/gradle-plugins")
     }
 
-    PluginBuilder addPlugin(String impl, String id = "test-plugin", String className = "TestPlugin") {
+    private addPluginSource(String id, String className, String impl) {
         pluginIds[id] = className
 
-        groovy("${className}.groovy") << """
+        groovy("${className}.groovy") << impl
+    }
+
+    PluginBuilder addPlugin(String impl, String id = "test-plugin", String className = "TestPlugin") {
+        addPluginSource(id, className, """
             package $packageName
 
             class $className implements $Plugin.name<$Project.name> {
@@ -93,7 +104,20 @@ class PluginBuilder {
                     $impl
                 }
             }
-        """
+        """)
+        this
+    }
+
+    PluginBuilder addUnloadablePlugin(String id = "test-plugin", String className = "TestPlugin") {
+        addPluginSource(id, className, """
+            package $packageName
+
+            class $className implements $Plugin.name<$Project.name> {
+                static { throw new Exception("unloadable plugin class") }
+                void apply($Project.name project) {
+                }
+            }
+        """)
         this
     }
 
@@ -101,4 +125,23 @@ class PluginBuilder {
         addPlugin("project.task(\"$taskName\") << { println \"$message\" }", id, className)
         this
     }
+
+    PluginBuilder addRuleSource(String pluginId) {
+        String className = "TestRuleSource"
+        addPluginSource(pluginId, className, """
+            package $packageName
+
+            class $className extends $RuleSource.name {
+                @$Mutate.name
+                void addTask($CollectionBuilder.name<$Task.name> tasks) {
+                    tasks.create("fromModelRule") {
+                        it.doLast {
+                            println "Model rule provided task executed"
+                        }
+                    }
+                }
+            }
+        """)
+        this
+    }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteArtifact.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteArtifact.java
new file mode 100644
index 0000000..b2ca3cc
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteArtifact.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.resource;
+
+public interface RemoteArtifact extends RemoteResource {
+    RemoteResource getMd5();
+
+    RemoteResource getSha1();
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteResource.groovy
new file mode 100644
index 0000000..3785e2c
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/resource/RemoteResource.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.resource
+
+public interface RemoteResource {
+
+    URI getUri()
+
+    void expectDownload()
+
+    void expectDownloadMissing()
+
+    void expectDownloadBroken()
+
+    void expectMetadataRetrieve()
+
+    void expectMetadataRetrieveMissing()
+
+    void expectMetadataRetrieveBroken()
+
+    /**
+     * Expects that the parent directories of this resource be created, for those resources where this is required. May be a no-op.
+     */
+    void expectParentMkdir()
+
+    /**
+     * Expects that the parent directories of this resource be checked, for those resources where this is required. May be a no-op.
+     */
+    void expectParentCheckdir()
+
+    void expectUpload()
+
+    void expectUploadBroken()
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy
new file mode 100644
index 0000000..0534688
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ExpectOne.groovy
@@ -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.test.fixtures.server
+
+abstract class ExpectOne implements ServerExpectation {
+    boolean run
+
+    void assertMet() {
+        if (!run) {
+            throw new AssertionError(notMetMessage)
+        }
+    }
+
+    abstract String getNotMetMessage()
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy
new file mode 100644
index 0000000..0715956
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server
+
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+
+public interface RepositoryServer {
+
+    RemoteIvyRepository getRemoteIvyRepo()
+
+    RemoteIvyRepository getRemoteIvyRepo(boolean m2Compatible, String dirPattern)
+
+    RemoteIvyRepository getRemoteIvyRepo(boolean m2Compatible, String dirPattern, String ivyFilePattern, String artifactFilePattern)
+
+    RemoteIvyRepository getRemoteIvyRepo(String contextPath)
+
+    void resetExpectations()
+
+    String getValidCredentials()
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerExpectation.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerExpectation.groovy
new file mode 100644
index 0000000..755ce15
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerExpectation.groovy
@@ -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.test.fixtures.server
+
+interface ServerExpectation {
+    void assertMet()
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerWithExpectations.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerWithExpectations.groovy
new file mode 100644
index 0000000..84f1524
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/ServerWithExpectations.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.test.fixtures.server
+
+import org.junit.rules.ExternalResource
+import org.slf4j.Logger
+
+abstract class ServerWithExpectations extends ExternalResource {
+
+    protected Throwable failure
+
+    void resetExpectations() {
+        try {
+            if (failure != null) {
+                throw failure
+            }
+            for (ServerExpectation e in expectations) {
+                e.assertMet()
+            }
+        } finally {
+            failure = null
+            expectations.clear()
+        }
+    }
+
+    @Override
+    protected void after() {
+        stop()
+        resetExpectations()
+    }
+
+    protected void onFailure(Throwable failure) {
+        logger.error(failure.message)
+        if (this.failure == null) {
+            this.failure = failure
+        }
+    }
+
+    abstract protected List<? extends ServerExpectation> getExpectations()
+    abstract protected void stop()
+    abstract protected Logger getLogger()
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/AbstractHttpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/AbstractHttpResource.groovy
new file mode 100644
index 0000000..4d257a7
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/AbstractHttpResource.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.http
+
+import org.gradle.test.fixtures.resource.RemoteResource
+
+abstract class AbstractHttpResource implements RemoteResource {
+
+    protected HttpServer server
+
+    public AbstractHttpResource(HttpServer server) {
+        this.server = server
+    }
+
+    @Override
+    URI getUri() {
+        return new URI(server.uri.scheme, server.uri.authority, path, null, null)
+    }
+
+    abstract protected String getPath();
+
+    abstract void expectGet()
+
+    abstract void expectGetBroken()
+
+    abstract void expectGetMissing()
+
+    abstract void expectHead()
+
+    abstract void expectHeadBroken()
+
+    abstract void expectHeadMissing()
+
+    abstract void expectPut()
+
+    abstract void expectPutBroken()
+
+    @Override
+    void expectDownload() {
+        expectGet()
+    }
+
+    @Override
+    void expectDownloadBroken() {
+        expectGetBroken()
+    }
+
+    @Override
+    void expectDownloadMissing() {
+        expectGetMissing()
+    }
+
+    @Override
+    void expectMetadataRetrieve() {
+        expectHead()
+    }
+
+    @Override
+    void expectMetadataRetrieveBroken() {
+        expectHeadBroken()
+    }
+
+    @Override
+    void expectMetadataRetrieveMissing() {
+        expectHeadMissing()
+    }
+
+    @Override
+    void expectParentMkdir() {
+        // Not required
+    }
+
+    @Override
+    void expectParentCheckdir() {
+        // Not required
+    }
+
+    @Override
+    void expectUpload() {
+        expectPut()
+    }
+
+    @Override
+    void expectUploadBroken() {
+        expectPutBroken()
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BasicHttpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BasicHttpResource.groovy
new file mode 100644
index 0000000..1ab28e7
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BasicHttpResource.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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestFile
+
+class BasicHttpResource extends HttpResource {
+    private final String path
+    private final TestFile file
+
+    BasicHttpResource(HttpServer httpServer, TestFile file, String path) {
+        super(httpServer)
+        this.file = file
+        this.path = path
+    }
+
+    @Override
+    TestFile getFile() {
+        return file
+    }
+
+    @Override
+    protected String getPath() {
+        return path
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServer.groovy
index 5ef6e9e..ec56016 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServer.groovy
@@ -15,20 +15,18 @@
  */
 
 
-
-package org.gradle.test.fixtures.server.http;
-
+package org.gradle.test.fixtures.server.http
 
 import org.junit.rules.ExternalResource
 import org.mortbay.jetty.Server
 import org.mortbay.jetty.handler.AbstractHandler
 import org.mortbay.jetty.handler.HandlerCollection
 
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
 import java.util.concurrent.locks.Condition
 import java.util.concurrent.locks.Lock
 import java.util.concurrent.locks.ReentrantLock
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
 
 public class BlockingHttpServer extends ExternalResource {
     private final Server server = new Server(0)
@@ -53,8 +51,18 @@ public class BlockingHttpServer extends ExternalResource {
         server.setHandler(handlers)
     }
 
-    void expectConcurrentExecution(String... expectedCalls) {
-        def handler = new CyclicBarrierRequestHandler(expectedCalls)
+    void expectConcurrentExecution(String expectedCall, String... additionalExpectedCalls) {
+        def handler = new CyclicBarrierRequestHandler((additionalExpectedCalls.toList() + expectedCall) as Set, {})
+        collection.addHandler(handler)
+    }
+
+    void expectSerialExecution(String expectedCall) {
+        def handler = new CyclicBarrierRequestHandler(expectedCall, {})
+        collection.addHandler(handler)
+    }
+
+    void expectConcurrentExecution(Iterable<String> tasks, Runnable latch) {
+        def handler = new CyclicBarrierRequestHandler(tasks as Set, latch)
         collection.addHandler(handler)
     }
 
@@ -91,9 +99,17 @@ server state: ${server.dump()}
         final Condition condition = lock.newCondition()
         final List<String> received = []
         final Set<String> pending
+        boolean shortCircuit
+        private final Runnable latch
+
+        CyclicBarrierRequestHandler(Set calls, Runnable latch) {
+            this.latch = latch
+            pending = calls
+        }
 
-        CyclicBarrierRequestHandler(String... calls) {
-            pending = calls as Set
+        CyclicBarrierRequestHandler(String call, Runnable latch) {
+            this.latch = latch
+            pending = [call] as Set
         }
 
         void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
@@ -104,23 +120,35 @@ server state: ${server.dump()}
             Date expiry = new Date(System.currentTimeMillis() + 30000)
             lock.lock()
             try {
+                if (shortCircuit) {
+                    request.handled = true
+                    return
+                }
                 def path = target.replaceFirst('/', '')
                 if (!pending.remove(path)) {
-                    // Unexpected call - let it travel on
+                    if (!pending.empty) {
+                        shortCircuit = true
+                        condition.signalAll()
+                        throw new AssertionError("Unexpected call to '$target' received. Waiting for $pending, already received $received.")
+                    }
+                    // barrier open, let it travel on
                     return
                 }
+                latch.run()
                 received.add(path)
                 condition.signalAll()
-                while (!pending.empty) {
+                while (!pending.empty && !shortCircuit) {
                     if (!condition.awaitUntil(expiry)) {
                         throw new AssertionError("Timeout waiting for all concurrent requests. Waiting for $pending, received $received.")
                     }
                 }
+                if (!shortCircuit) {
+                    response.addHeader("RESPONSE", "target: done")
+                }
             } finally {
                 lock.unlock()
             }
 
-            response.addHeader("RESPONSE", "target: done")
             request.handled = true
         }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/CyclicBarrierHttpServer.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/CyclicBarrierHttpServer.java
new file mode 100644
index 0000000..e789d6e
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/CyclicBarrierHttpServer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.http;
+
+import junit.framework.AssertionFailedError;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.os.OperatingSystem;
+import org.junit.rules.ExternalResource;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.channels.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Allows the test process and a single build process to synchronize.
+ */
+public class CyclicBarrierHttpServer extends ExternalResource {
+    private ExecutorService executor;
+    private ServerSocketChannel serverSocket;
+    private final Object lock = new Object();
+    private boolean connected;
+    private boolean released;
+    private boolean stopped;
+
+    @Override
+    protected void before() {
+        start();
+    }
+
+    @Override
+    protected void after() {
+        stop();
+    }
+
+    void start() {
+        // Note: this is implemented using raw sockets. Originally implemented using Jetty, but some concurrency problems there caused Jetty to hang
+        try {
+            serverSocket = ServerSocketChannel.open();
+            serverSocket.socket().bind(new InetSocketAddress(0));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+
+        executor = Executors.newCachedThreadPool();
+        executor.execute(new Runnable() {
+            public void run() {
+                int i = 0;
+
+                while (true) {
+                    try {
+                        SocketChannel connection;
+                        try {
+                            connection = serverSocket.accept();
+                        } catch (AsynchronousCloseException e) {
+                            // Socket has been closed, so we're stopping
+                            return;
+                        } catch (ClosedChannelException e) {
+                            // Socket has been closed, so we're stopping
+                            return;
+                        }
+                        try {
+                            OutputStream outputStream = Channels.newOutputStream(connection);
+                            System.out.println("Handle connection request no." + (++i));
+                            handleConnection(outputStream);
+                            outputStream.flush();
+                        } finally {
+                            connection.close();
+                        }
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            private void handleConnection(OutputStream outputStream) throws IOException {
+                System.out.println("Handling HTTP request");
+
+                synchronized (lock) {
+                    if (connected) {
+                        System.out.println("Received unexpected connection.");
+                        outputStream.write("HTTP/1.1 500 Received an unexpected connection.\r\nConnection: close\r\nContent-length: 0\r\n\r\n".getBytes());
+                        return;
+                    }
+
+                    System.out.println("Connection received");
+                    connected = true;
+                    lock.notifyAll();
+
+                    long expiry = System.currentTimeMillis() + 30000;
+                    while (!released && !stopped) {
+                        long delay = expiry - System.currentTimeMillis();
+                        if (delay <= 0) {
+                            System.out.println("Timeout waiting for client to be released.");
+                            outputStream.write("HTTP/1.1 500 Timeout waiting for client to be released.\r\nConnection: close\r\nContent-length: 0\r\n\r\n".getBytes());
+                            return;
+                        }
+                        try {
+                            lock.wait(delay);
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                    if (stopped) {
+                        System.out.println("Releasing client on stop.");
+                        outputStream.write("HTTP/1.1 500 Server stopped.\r\nConnection: close\r\nContent-length: 0\r\n\r\n".getBytes());
+                        return;
+                    }
+
+                    connected = false;
+                    released = false;
+                }
+
+                System.out.println("Sending response to client");
+                outputStream.write("HTTP/1.1 200 Ok.\r\nConnection: close\r\nContent-length: 0\r\n\r\n".getBytes());
+            }
+        });
+    }
+
+    void stop() {
+        System.out.println("Stopping server");
+        synchronized (lock) {
+            stopped = true;
+            lock.notifyAll();
+        }
+        try {
+            serverSocket.close();
+            executor.shutdown();
+            executor.awaitTermination(30, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public URI getUri() {
+        int port = serverSocket.socket().getLocalPort();
+        if (port <= 0) {
+            throw new IllegalStateException(String.format("Unexpected port %s for HTTP server.", port));
+        }
+        try {
+            return new URI(String.format("http://localhost:%s", port));
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Blocks until a connection to the URI has been received. No response is returned to the client until
+     * {@link #release()} is called.
+     */
+    public void waitFor() {
+        long expiry = System.currentTimeMillis() + 20000;
+        synchronized (lock) {
+            while (!connected && !stopped) {
+                long delay = expiry - System.currentTimeMillis();
+                if (delay <= 0) {
+                    throw new AssertionFailedError(String.format("Timeout waiting for client to connect to %s.", getUri()));
+                }
+                System.out.println("waiting for client to connect");
+                try {
+                    lock.wait(delay);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            if (stopped) {
+                throw new AssertionFailedError(String.format("Server was stopped while waiting for client to connect to %s.", getUri()));
+            }
+            System.out.println("client connected - unblocking");
+        }
+    }
+
+    /**
+     * Sends a response back on the connection.
+     */
+    public void release() {
+        // TODO(radim): quick socket operation on Windows is not noticed by client
+        // and it re-opens the connection immediately. Need to find a better way here.
+        if (OperatingSystem.current().isWindows()) {
+            try {
+                Thread.sleep(2000L);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        synchronized (lock) {
+            released = true;
+            lock.notifyAll();
+        }
+    }
+
+    /**
+     * Blocks until a connection to the URI has been received, then sends a response back to the client and returns.
+     *
+     * <p>Note that this method will generally return before the client has received the response.
+     */
+    public void sync() {
+        synchronized (lock) {
+            waitFor();
+            release();
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpArtifact.groovy
new file mode 100644
index 0000000..d42c396
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpArtifact.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.test.fixtures.server.http
+
+import org.gradle.internal.hash.HashUtil
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteArtifact
+
+abstract class HttpArtifact extends HttpResource implements RemoteArtifact {
+
+    String modulePath
+
+    public HttpArtifact(HttpServer server, String modulePath) {
+        super(server)
+        this.modulePath = modulePath
+    }
+
+    HttpResource getMd5() {
+        return new BasicHttpResource(server, getMd5File(), "${path}.md5")
+    }
+
+    HttpResource getSha1() {
+        return new BasicHttpResource(server, getSha1File(), "${path}.sha1")
+    }
+
+    protected String getPath() {
+        "${modulePath}/${file.name}"
+    }
+
+    protected abstract TestFile getSha1File();
+
+    protected abstract TestFile getMd5File();
+
+    abstract TestFile getFile();
+
+    void verifyChecksums() {
+        def sha1File = getSha1File()
+        sha1File.assertIsFile()
+        assert new BigInteger(sha1File.text, 16) == new BigInteger(getHash(getFile(), "sha1"), 16)
+        def md5File = getMd5File()
+        md5File.assertIsFile()
+        assert new BigInteger(md5File.text, 16) == new BigInteger(getHash(getFile(), "md5"), 16)
+    }
+
+    protected String getHash(TestFile file, String algorithm) {
+        HashUtil.createHash(file, algorithm.toUpperCase()).asHexString()
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpDirectoryResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpDirectoryResource.groovy
new file mode 100644
index 0000000..4fee010
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpDirectoryResource.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestFile
+
+class HttpDirectoryResource extends AbstractHttpResource {
+    final String path
+    private final TestFile directory
+
+    HttpDirectoryResource(HttpServer server, String path, TestFile directory) {
+        super(server)
+        this.directory = directory
+        this.path = path
+        this.server = server
+    }
+
+    @Override
+    public void expectGet() {
+        server.expectGetDirectoryListing(path, directory)
+    }
+
+    public void allowGet() {
+        server.allowGetDirectoryListing(path, directory)
+    }
+
+    @Override
+    public void expectGetMissing() {
+        server.expectGetMissing(path)
+    }
+
+    @Override
+    public void expectGetBroken() {
+        server.expectGetBroken(path)
+    }
+
+    @Override
+    void expectHead() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectHeadBroken() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectHeadMissing() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectPut() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectPutBroken() {
+        throw new UnsupportedOperationException()
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResource.groovy
new file mode 100644
index 0000000..391edb7
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResource.groovy
@@ -0,0 +1,81 @@
+/*
+ * 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.test.fixtures.server.http
+
+import org.gradle.api.artifacts.repositories.PasswordCredentials
+import org.gradle.test.fixtures.file.TestFile
+
+abstract class HttpResource extends AbstractHttpResource {
+    public HttpResource(HttpServer server) {
+        super(server)
+    }
+
+    void allowGetOrHead() {
+        server.allowGetOrHead(getPath(), file)
+    }
+
+    @Override
+    void expectGet() {
+        server.expectGet(getPath(), file)
+    }
+
+    void allowGetOrHead(String userName, String password) {
+        server.allowGetOrHead(getPath(), userName, password, file)
+    }
+
+    void expectGet(String userName, String password) {
+        server.expectGet(getPath(), userName, password, file)
+    }
+
+    void expectGetBroken() {
+        server.expectGetBroken(getPath())
+    }
+
+    void expectGetMissing(PasswordCredentials credentials = null) {
+        server.expectGetMissing(getPath(), credentials)
+    }
+
+    void expectHead() {
+        server.expectHead(getPath(), file)
+    }
+
+    void expectHeadMissing() {
+        server.expectHeadMissing(path)
+    }
+
+    void expectHeadBroken() {
+        server.expectHeadBroken(path)
+    }
+
+    void expectPut(PasswordCredentials credentials) {
+        expectPut(200, credentials)
+    }
+
+    void expectPut(String username, String password) {
+        server.expectPut(getPath(), username, password, getFile())
+    }
+
+    void expectPut(Integer statusCode = 200, PasswordCredentials credentials = null) {
+        server.expectPut(getPath(), getFile(), statusCode, credentials)
+    }
+
+    void expectPutBroken(PasswordCredentials credentials = null) {
+        server.expectPut(getPath(), getFile(), 500, credentials)
+    }
+
+    abstract TestFile getFile();
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResourceInteraction.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResourceInteraction.java
new file mode 100644
index 0000000..b18d1d2
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpResourceInteraction.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.http;
+
+public interface HttpResourceInteraction {
+    HttpResourceInteraction contentType(String encoding);
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy
index c7cd0c2..1471352 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpServer.groovy
@@ -15,14 +15,18 @@
  */
 package org.gradle.test.fixtures.server.http
 
+import com.google.common.net.UrlEscapers
 import com.google.gson.Gson
 import com.google.gson.JsonElement
+import groovy.xml.MarkupBuilder
 import org.gradle.api.artifacts.repositories.PasswordCredentials
 import org.gradle.internal.hash.HashUtil
+import org.gradle.test.fixtures.server.ExpectOne
+import org.gradle.test.fixtures.server.ServerExpectation
+import org.gradle.test.fixtures.server.ServerWithExpectations
 import org.gradle.test.matchers.UserAgentMatcher
 import org.gradle.util.GFileUtils
 import org.hamcrest.Matcher
-import org.junit.rules.ExternalResource
 import org.mortbay.jetty.*
 import org.mortbay.jetty.bio.SocketConnector
 import org.mortbay.jetty.handler.AbstractHandler
@@ -36,9 +40,9 @@ import javax.servlet.http.HttpServletResponse
 import java.security.Principal
 import java.util.zip.GZIPOutputStream
 
-class HttpServer extends ExternalResource {
+class HttpServer extends ServerWithExpectations {
 
-    private static Logger logger = LoggerFactory.getLogger(HttpServer.class)
+    private final static Logger logger = LoggerFactory.getLogger(HttpServer.class)
 
     private final Server server = new Server(0)
     private final HandlerCollection collection = new HandlerCollection()
@@ -48,9 +52,9 @@ class HttpServer extends ExternalResource {
     private SslSocketConnector sslConnector
     AuthScheme authenticationScheme = AuthScheme.BASIC
 
-    private Throwable failure
-    private final List<Expection> expections = []
-    private Matcher expectedUserAgent = null
+    protected Matcher expectedUserAgent = null
+
+    List<ServerExpectation> expectations = []
 
     enum AuthScheme {
         BASIC(new BasicAuthHandler()), DIGEST(new DigestAuthHandler())
@@ -104,55 +108,52 @@ class HttpServer extends ExternalResource {
         server.setHandler(handlers)
     }
 
+    protected Logger getLogger() {
+        logger
+    }
+
     String getAddress() {
         if (!server.started) {
             server.start()
         }
-        "http://localhost:${port}"
+        getUri().toString()
     }
 
-    void start() {
-        start(0)
+    URI getUri() {
+        return sslConnector ? URI.create("https://localhost:${sslConnector.localPort}") : URI.create("http://localhost:${connector.localPort}")
     }
 
     boolean isRunning() {
         server.running
     }
 
-    void start(int port) {
+    void start() {
         connector = new SocketConnector()
-        connector.port = port
+        connector.port = 0
         server.addConnector(connector)
-        try {
-            server.start()
-        } catch (java.net.BindException e) {
-            //without this, it is not possible to retry starting the server on the same port
-            //retrying is useful if we need to start server on a specific port
-            //and the OS forces us to wait until it is available.
+        server.start()
+        for (int i = 0; i < 5; i++) {
+            if (connector.localPort > 0) {
+                return;
+            }
+            // Has failed to start for some reason - try again
             server.removeConnector(connector)
-            throw e
-        }
-        def localPort = connector.localPort
-        if (localPort <= 0) {
-            throw new AssertionError("SocketConnector.localPort returned $localPort after starting server");
+            connector.stop()
+            connector = new SocketConnector()
+            connector.port = 0
+            server.addConnector(connector)
+            connector.start()
         }
+        throw new AssertionError("SocketConnector failed to start.");
     }
 
     void stop() {
-        resetExpectations()
         server?.stop()
         if (connector) {
             server?.removeConnector(connector)
         }
     }
 
-    private void onFailure(Throwable failure) {
-        logger.error(failure.message)
-        if (this.failure == null) {
-            this.failure = failure
-        }
-    }
-
     void enableSsl(String keyStore, String keyPassword, String trustStore = null, String trustPassword = null) {
         sslConnector = new SslSocketConnector()
         sslConnector.keystore = keyStore
@@ -163,6 +164,9 @@ class HttpServer extends ExternalResource {
             sslConnector.trustPassword = trustPassword
         }
         server.addConnector(sslConnector)
+        if (server.started) {
+            sslConnector.start()
+        }
     }
 
     int getSslPort() {
@@ -175,24 +179,19 @@ class HttpServer extends ExternalResource {
 
     void resetExpectations() {
         try {
-            if (failure != null) {
-                throw failure
-            }
-            for (Expection e in expections) {
-                e.assertMet()
-            }
+            super.resetExpectations()
         } finally {
             realm = null
-            failure = null
             expectedUserAgent = null
-            expections.clear()
             collection.setHandlers()
         }
     }
 
-    @Override
-    protected void after() {
-        stop()
+    /**
+     * Adds a given file at the given URL. The source file can be either a file or a directory.
+     */
+    void allowHead(String path, File srcFile) {
+        allow(path, true, ['HEAD'], fileHandler(path, srcFile))
     }
 
     /**
@@ -209,35 +208,49 @@ class HttpServer extends ExternalResource {
         allow(path, true, ['GET', 'HEAD'], withAuthentication(path, username, password, fileHandler(path, srcFile)))
     }
 
-    private Action fileHandler(String path, File srcFile, Long lastModified = null, Long contentLength = null) {
-        return new Action() {
-            String getDisplayName() {
-                return "return contents of $srcFile.name"
-            }
+    /**
+     * Allows one GET request for the given URL, which return 404 status code
+     */
+    void allowGetMissing(String path) {
+        allow(path, false, ['GET'], notFound())
+    }
 
-            void handle(HttpServletRequest request, HttpServletResponse response) {
-                if (HttpServer.this.expectedUserAgent != null) {
-                    String receivedUserAgent = request.getHeader("User-Agent")
-                    if (!expectedUserAgent.matches(receivedUserAgent)) {
-                        response.sendError(412, String.format("Precondition Failed: Expected User-Agent: '%s' but was '%s'", expectedUserAgent, receivedUserAgent));
-                        return;
-                    }
-                }
-                def file
-                if (request.pathInfo == path) {
-                    file = srcFile
-                } else {
-                    def relativePath = request.pathInfo.substring(path.length() + 1)
-                    file = new File(srcFile, relativePath)
-                }
-                if (file.isFile()) {
-                    sendFile(response, file, lastModified, contentLength)
-                } else if (file.isDirectory()) {
-                    sendDirectoryListing(response, file)
-                } else {
-                    response.sendError(404, "'$request.pathInfo' does not exist")
+    private Action fileHandler(String path, File srcFile) {
+        return new SendFileAction(path, srcFile)
+    }
+
+    class SendFileAction extends ActionSupport {
+        private final String path
+        private final File srcFile
+
+        SendFileAction(String path, File srcFile) {
+            super("return contents of $srcFile.name")
+            this.srcFile = srcFile
+            this.path = path
+        }
+
+        void handle(HttpServletRequest request, HttpServletResponse response) {
+            if (expectedUserAgent != null) {
+                String receivedUserAgent = request.getHeader("User-Agent")
+                if (!expectedUserAgent.matches(receivedUserAgent)) {
+                    response.sendError(412, String.format("Precondition Failed: Expected User-Agent: '%s' but was '%s'", expectedUserAgent, receivedUserAgent));
+                    return;
                 }
             }
+            def file
+            if (request.pathInfo == path) {
+                file = srcFile
+            } else {
+                def relativePath = request.pathInfo.substring(path.length() + 1)
+                file = new File(srcFile, relativePath)
+            }
+            if (file.isFile()) {
+                sendFile(response, file, null, null, interaction.contentType)
+            } else if (file.isDirectory()) {
+                sendDirectoryListing(response, file)
+            } else {
+                response.sendError(404, "'$request.pathInfo' does not exist")
+            }
         }
     }
 
@@ -249,39 +262,39 @@ class HttpServer extends ExternalResource {
     }
 
     /**
-     * Allows one GET request, which fails with a 500 status code
+     * Expects one GET request, which fails with a 500 status code
      */
     void expectGetBroken(String path) {
         expect(path, false, ['GET'], broken())
     }
 
     /**
-     * Allows one GET request for the given URL, which return 404 status code
+     * Expects one GET request for the given URL, which return 404 status code
      */
     void expectGetMissing(String path, PasswordCredentials passwordCredentials = null) {
         expect(path, false, ['GET'], notFound(), passwordCredentials)
     }
 
+    void allowGetOrHeadMissing(String path) {
+        allow(path, false, ['GET', 'HEAD'], notFound())
+    }
+
     /**
-     * Allows one HEAD request for the given URL, which return 404 status code
+     * Expects one HEAD request for the given URL, which return 404 status code
      */
     void expectHeadMissing(String path) {
         expect(path, false, ['HEAD'], notFound())
     }
 
     /**
-     * Allows one HEAD request for the given URL, which returns a 500 status code
+     * Expects one HEAD request for the given URL, which returns a 500 status code
      */
     void expectHeadBroken(String path) {
         expect(path, false, ['HEAD'], broken())
     }
 
     private Action notFound() {
-        new Action() {
-            String getDisplayName() {
-                return "return 404 not found"
-            }
-
+        new ActionSupport("return 404 not found") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 response.sendError(404, "not found")
             }
@@ -289,11 +302,7 @@ class HttpServer extends ExternalResource {
     }
 
     private Action broken() {
-        new Action() {
-            String getDisplayName() {
-                return "return 500 broken"
-            }
-
+        new ActionSupport("return 500 broken") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 response.sendError(500, "broken")
             }
@@ -301,10 +310,10 @@ class HttpServer extends ExternalResource {
     }
 
     /**
-     * Allows one HEAD request for the given URL.
+     * Expects one HEAD request for the given URL.
      */
-    void expectHead(String path, File srcFile, Long lastModified = null, Long contentLength = null) {
-        expect(path, false, ['HEAD'], fileHandler(path, srcFile, lastModified, contentLength))
+    void expectHead(String path, File srcFile) {
+        expect(path, false, ['HEAD'], fileHandler(path, srcFile))
     }
 
     /**
@@ -317,26 +326,22 @@ class HttpServer extends ExternalResource {
     /**
      * Allows one GET request for the given URL. Reads the request content from the given file.
      */
-    void expectGet(String path, File srcFile, Long lastModified = null, Long contentLength = null) {
-        expect(path, false, ['GET'], fileHandler(path, srcFile, lastModified, contentLength))
+    HttpResourceInteraction expectGet(String path, File srcFile) {
+        return expect(path, false, ['GET'], fileHandler(path, srcFile))
     }
 
     /**
-     * Allows one GET request for the given URL, with the given credentials. Reads the request content from the given file.
+     * Expects one GET request for the given URL, with the given credentials. Reads the request content from the given file.
      */
-    void expectGet(String path, String username, String password, File srcFile) {
-        expect(path, false, ['GET'], withAuthentication(path, username, password, fileHandler(path, srcFile)))
+    HttpResourceInteraction expectGet(String path, String username, String password, File srcFile) {
+        return expect(path, false, ['GET'], withAuthentication(path, username, password, fileHandler(path, srcFile)))
     }
 
     /**
-     * Allows one GET request for the given URL, with the response being GZip encoded.
+     * Expects one GET request for the given URL, with the response being GZip encoded.
      */
     void expectGetGZipped(String path, File srcFile) {
-        expect(path, false, ['GET'], new Action() {
-            String getDisplayName() {
-                return "return gzipped $srcFile.name"
-            }
-
+        expect(path, false, ['GET'], new ActionSupport("return gzipped $srcFile.name") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 def file = srcFile
                 if (file.isFile()) {
@@ -353,25 +358,21 @@ class HttpServer extends ExternalResource {
     }
 
     /**
-     * Allow one GET request for the given URL, responding with a redirect.
+     * Expects one GET request for the given URL, responding with a redirect.
      */
     void expectGetRedirected(String path, String location) {
         expectRedirected('GET', path, location)
     }
 
     /**
-     * Allow one HEAD request for the given URL, responding with a redirect.
+     * Expects one HEAD request for the given URL, responding with a redirect.
      */
     void expectHeadRedirected(String path, String location) {
         expectRedirected('HEAD', path, location)
     }
 
     private void expectRedirected(String method, String path, String location) {
-        expect(path, false, [method], new Action() {
-            String getDisplayName() {
-                return "redirect to $location"
-            }
-
+        expect(path, false, [method], new ActionSupport("redirect to $location") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 response.sendRedirect(location)
             }
@@ -382,11 +383,7 @@ class HttpServer extends ExternalResource {
      * Allows GET requests for the given URL, returning an apache-compatible directory listing with the given File names.
      */
     void allowGetDirectoryListing(String path, File directory) {
-        allow(path, false, ['GET'], new Action() {
-            String getDisplayName() {
-                return "return listing of directory $directory.name"
-            }
-
+        allow(path, false, ['GET'], new ActionSupport("return listing of directory $directory.name") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 sendDirectoryListing(response, directory)
             }
@@ -397,11 +394,7 @@ class HttpServer extends ExternalResource {
      * Expects one GET request for the given URL, returning an apache-compatible directory listing with the given File names.
      */
     void expectGetDirectoryListing(String path, File directory) {
-        expect(path, false, ['GET'], new Action() {
-            String getDisplayName() {
-                return "return listing of directory $directory.name"
-            }
-
+        expect(path, false, ['GET'], new ActionSupport("return listing of directory $directory.name") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 sendDirectoryListing(response, directory)
             }
@@ -409,28 +402,23 @@ class HttpServer extends ExternalResource {
     }
 
     /**
-     * Allows one GET request for the given URL, returning an apache-compatible directory listing with the given File names.
+     * Expects one GET request for the given URL, returning an apache-compatible directory listing with the given File names.
      */
     void expectGetDirectoryListing(String path, String username, String password, File directory) {
-        expect(path, false, ['GET'], withAuthentication(path, username, password, new Action() {
-            String getDisplayName() {
-                return "return listing of directory $directory.name"
-            }
-
+        expect(path, false, ['GET'], withAuthentication(path, username, password, new ActionSupport("return listing of directory $directory.name") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 sendDirectoryListing(response, directory)
             }
         }));
     }
 
-
-    private sendFile(HttpServletResponse response, File file, Long lastModified, Long contentLength) {
+    private sendFile(HttpServletResponse response, File file, Long lastModified, Long contentLength, String contentType) {
         if (sendLastModified) {
             response.setDateHeader(HttpHeaders.LAST_MODIFIED, lastModified ?: file.lastModified())
         }
         def content = file.bytes
         response.setContentLength((contentLength ?: content.length) as int)
-        response.setContentType(new MimeTypes().getMimeByExtension(file.name).toString())
+        response.setContentType(contentType ?: new MimeTypes().getMimeByExtension(file.name).toString())
         if (sendSha1Header) {
             response.addHeader("X-Checksum-Sha1", HashUtil.sha1(content).asHexString())
         }
@@ -457,25 +445,29 @@ class HttpServer extends ExternalResource {
     }
 
     private sendDirectoryListing(HttpServletResponse response, File directory) {
-        def directoryListing = ""
-        for (String fileName : directory.list()) {
-            directoryListing += "<a href=\"$fileName\">$fileName</a>"
+        def writer = new StringWriter()
+        def markupBuilder = new MarkupBuilder(writer)
+        markupBuilder.doubleQuotes = true // for Ivy
+        markupBuilder.html {
+            for (String fileName : directory.list()) {
+                def uri = UrlEscapers.urlPathSegmentEscaper().escape(fileName).replaceAll(':', '%3A')
+                a(href: uri, fileName)
+            }
+
         }
+        def directoryListing = writer.toString().getBytes("utf8")
 
-        response.setContentLength(directoryListing.length())
+        response.setContentLength(directoryListing.length)
         response.setContentType("text/html")
-        response.outputStream.bytes = directoryListing.bytes
+        response.setCharacterEncoding("utf8")
+        response.outputStream.bytes = directoryListing
     }
 
     /**
-     * Allows one PUT request for the given URL. Writes the request content to the given file.
+     * Expects one PUT request for the given URL. Writes the request content to the given file.
      */
     void expectPut(String path, File destFile, int statusCode = HttpStatus.ORDINAL_200_OK, PasswordCredentials credentials = null) {
-        def action = new Action() {
-            String getDisplayName() {
-                return "write request to $destFile.name and return status $statusCode"
-            }
-
+        def action = new ActionSupport("write request to $destFile.name and return status $statusCode") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 if (HttpServer.this.expectedUserAgent != null) {
                     String receivedUserAgent = request.getHeader("User-Agent")
@@ -494,14 +486,10 @@ class HttpServer extends ExternalResource {
     }
 
     /**
-     * Allows one PUT request for the given URL, with the given credentials. Writes the request content to the given file.
+     * Expects one PUT request for the given URL, with the given credentials. Writes the request content to the given file.
      */
     void expectPut(String path, String username, String password, File destFile) {
-        expect(path, false, ['PUT'], withAuthentication(path, username, password, new Action() {
-            String getDisplayName() {
-                return "write request to $destFile.name"
-            }
-
+        expect(path, false, ['PUT'], withAuthentication(path, username, password, new ActionSupport("write request to $destFile.name") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
 
                 if (request.remoteUser != username) {
@@ -518,11 +506,7 @@ class HttpServer extends ExternalResource {
      * Allows PUT requests with the given credentials.
      */
     void allowPut(String path, String username, String password) {
-        allow(path, false, ['PUT'], withAuthentication(path, username, password, new Action() {
-            String getDisplayName() {
-                return "return 500"
-            }
-
+        allow(path, false, ['PUT'], withAuthentication(path, username, password, new ActionSupport("return 500") {
             void handle(HttpServletRequest request, HttpServletResponse response) {
                 response.sendError(500, "unexpected username '${request.remoteUser}'")
             }
@@ -543,6 +527,11 @@ class HttpServer extends ExternalResource {
         }
 
         return new Action() {
+            @Override
+            HttpResourceInteraction getInteraction() {
+                return action.interaction
+            }
+
             String getDisplayName() {
                 return action.displayName
             }
@@ -561,13 +550,13 @@ class HttpServer extends ExternalResource {
         expect(path, false, methods, action, passwordCredentials)
     }
 
-    void expect(String path, boolean matchPrefix, Collection<String> methods, Action action, PasswordCredentials credentials = null) {
+    HttpResourceInteraction expect(String path, boolean matchPrefix, Collection<String> methods, Action action, PasswordCredentials credentials = null) {
         if (credentials != null) {
             action = withAuthentication(path, credentials.username, credentials.password, action)
         }
 
-        ExpectOne expectation = new ExpectOne(action, methods, path)
-        expections << expectation
+        HttpExpectOne expectation = new HttpExpectOne(action, methods, path)
+        expectations << expectation
         add(path, matchPrefix, methods, new AbstractHandler() {
             void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
                 if (expectation.run) {
@@ -578,6 +567,8 @@ class HttpServer extends ExternalResource {
                 request.handled = true
             }
         })
+
+        return action.interaction
     }
 
     private void allow(String path, boolean matchPrefix, Collection<String> methods, Action action) {
@@ -591,7 +582,6 @@ class HttpServer extends ExternalResource {
 
     private void add(String path, boolean matchPrefix, Collection<String> methods, Handler handler) {
         assert path.startsWith('/')
-//        assert path == '/' || !path.endsWith('/')
         def prefix = path == '/' ? '/' : path + '/'
         collection.addHandler(new AbstractHandler() {
             void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
@@ -606,34 +596,33 @@ class HttpServer extends ExternalResource {
         })
     }
 
-    int getPort() {
-        return server.connectors[0].localPort
+    void addHandler(Handler handler){
+        collection.addHandler(handler)
     }
 
-    interface Expection {
-        void assertMet()
+    int getPort() {
+        return connector.localPort
     }
 
-    static class ExpectOne implements Expection {
-        boolean run
+    static class HttpExpectOne extends ExpectOne {
         final Action action
         final Collection<String> methods
         final String path
 
-        ExpectOne(Action action, Collection<String> methods, String path) {
+        HttpExpectOne(Action action, Collection<String> methods, String path) {
             this.action = action
             this.methods = methods
             this.path = path
         }
 
-        void assertMet() {
-            if (!run) {
-                throw new AssertionError("Expected HTTP request not received: ${methods.size() == 1 ? methods[0] : methods} $path and $action.displayName")
-            }
+        String getNotMetMessage() {
+            "Expected HTTP request not received: ${methods.size() == 1 ? methods[0] : methods} $path and $action.displayName"
         }
     }
 
     interface Action {
+        HttpResourceInteraction getInteraction()
+
         String getDisplayName()
 
         void handle(HttpServletRequest request, HttpServletResponse response)
@@ -641,12 +630,23 @@ class HttpServer extends ExternalResource {
 
     static abstract class ActionSupport implements Action {
         final String displayName
+        final HttpResourceInteraction interaction = new DefaultResourceInteraction()
 
         ActionSupport(String displayName) {
             this.displayName = displayName
         }
     }
 
+    static class DefaultResourceInteraction implements HttpResourceInteraction {
+        String contentType
+
+        @Override
+        HttpResourceInteraction contentType(String encoding) {
+            this.contentType = encoding
+            return this
+        }
+    }
+
     static class Utils {
         static JsonElement json(HttpServletRequest request) {
             new Gson().fromJson(request.reader, JsonElement.class)
@@ -660,7 +660,11 @@ class HttpServer extends ExternalResource {
             if (!response.contentType) {
                 response.setContentType("application/json")
             }
-            new Gson().toJson(data, response.writer)
+            StringBuilder sb = new StringBuilder()
+            new Gson().toJson(data, sb)
+            response.outputStream.withStream {
+                it << sb.toString().getBytes("utf8")
+            }
         }
     }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpModule.groovy
new file mode 100644
index 0000000..0221a4a
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpModule.groovy
@@ -0,0 +1,209 @@
+/*
+ * 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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.HttpModule
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.ivy.IvyDescriptor
+import org.gradle.test.fixtures.ivy.IvyFileModule
+import org.gradle.test.fixtures.ivy.RemoteIvyModule
+
+class IvyHttpModule implements RemoteIvyModule, HttpModule {
+    public final IvyHttpRepository repository
+    private final IvyFileModule backingModule
+    private final HttpServer server
+    private final String prefix
+
+    IvyHttpModule(IvyHttpRepository repository, HttpServer server, String prefix, IvyFileModule backingModule) {
+        this.repository = repository
+        this.prefix = prefix
+        this.server = server
+        this.backingModule = backingModule
+    }
+
+    String getOrganisation() {
+        return backingModule.organisation
+    }
+
+    String getModule() {
+        return backingModule.module
+    }
+
+    String getRevision() {
+        return backingModule.revision
+    }
+
+    IvyDescriptor getParsedIvy() {
+        return backingModule.parsedIvy
+    }
+
+    @Override
+    void assertPublished() {
+        backingModule.assertPublished()
+    }
+
+    @Override
+    void assertArtifactsPublished(String... names) {
+        backingModule.assertArtifactsPublished(names)
+    }
+
+    @Override
+    void assertPublishedAsJavaModule() {
+        backingModule.assertPublishedAsJavaModule()
+    }
+
+    IvyHttpModule publish() {
+        backingModule.publish()
+        return this
+    }
+
+    IvyHttpModule publishWithChangedContent() {
+        backingModule.publishWithChangedContent()
+        return this
+    }
+
+    IvyHttpModule withNoMetaData() {
+        backingModule.withNoMetaData()
+        return this
+    }
+
+    IvyHttpModule withStatus(String status) {
+        backingModule.withStatus(status)
+        return this
+    }
+
+    IvyHttpModule dependsOn(String organisation, String module, String revision) {
+        backingModule.dependsOn(organisation, module, revision)
+        return this
+    }
+
+    IvyHttpModule dependsOn(Map<String, ?> attributes) {
+        backingModule.dependsOn(attributes)
+        return this
+    }
+
+    IvyHttpModule artifact(Map<String, ?> options = [:]) {
+        backingModule.artifact(options)
+        return this
+    }
+
+    IvyHttpModule undeclaredArtifact(Map<String, ?> options = [:]) {
+        backingModule.undeclaredArtifact(options)
+        return this
+    }
+
+    IvyHttpModule extendsFrom(Map<String, ?> attributes) {
+        backingModule.extendsFrom(attributes)
+        return this
+    }
+
+    IvyHttpModule configuration(Map<String, ?> options = [:], String name) {
+        backingModule.configuration(options, name)
+        return this
+    }
+
+    IvyHttpModule withXml(Closure action) {
+        backingModule.withXml(action)
+        return this
+    }
+
+    IvyHttpModule withExtraAttributes(Map extraAttributes) {
+        backingModule.withExtraAttributes(extraAttributes)
+        return this
+    }
+
+    IvyHttpModule withExtraInfo(Map extraInfo) {
+        backingModule.withExtraInfo(extraInfo)
+        return this
+    }
+
+    IvyHttpModule withBranch(String branch) {
+        backingModule.withBranch(branch)
+        return this
+    }
+
+    TestFile getIvyFile() {
+        return backingModule.ivyFile
+    }
+
+    TestFile getJarFile() {
+        return backingModule.jarFile
+    }
+
+    IvyHttpModule allowAll() {
+        server.allowGetOrHead(prefix, backingModule.moduleDir)
+        return this
+    }
+
+    IvyModuleHttpArtifact getArtifact(Map<String, ?> options = [:]) {
+        def backingFile = backingModule.file(options)
+        return new IvyModuleHttpArtifact(server, prefix, backingModule.getArtifactFilePath(options), backingFile)
+    }
+
+    IvyModuleHttpArtifact getIvy() {
+        return new IvyModuleHttpArtifact(server, prefix, backingModule.ivyFilePath, ivyFile)
+    }
+
+    IvyModuleHttpArtifact getJar() {
+        return new IvyModuleHttpArtifact(server, prefix, backingModule.jarFilePath, jarFile)
+    }
+
+    void assertIvyAndJarFilePublished() {
+        backingModule.assertIvyAndJarFilePublished()
+    }
+
+    private class IvyModuleHttpArtifact extends HttpArtifact {
+        final TestFile backingFile
+        final String filePath
+
+        IvyModuleHttpArtifact(HttpServer server, String modulePath, String filePath, TestFile backingFile) {
+            super(server, modulePath)
+            this.filePath = filePath
+            this.backingFile = backingFile
+        }
+
+        @Override
+        protected String getPath() {
+            "${modulePath}/${filePath}"
+        }
+
+        @Override
+        TestFile getFile() {
+            return backingFile
+        }
+
+        @Override
+        protected TestFile getSha1File() {
+            return backingModule.getSha1File(backingFile)
+        }
+
+        @Override
+        protected TestFile getMd5File() {
+            return backingModule.getMd5File(backingFile)
+        }
+
+        @Override
+        void verifyChecksums() {
+            // MD5 not published for ivy modules
+            def sha1File = getSha1File()
+            sha1File.assertIsFile()
+            assert new BigInteger(sha1File.text, 16) == new BigInteger(getHash(getFile(), "sha1"), 16)
+        }
+    }
+}
+
+
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpRepository.groovy
new file mode 100644
index 0000000..bc22fa9
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/IvyHttpRepository.groovy
@@ -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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.HttpRepository
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+
+class IvyHttpRepository implements RemoteIvyRepository, HttpRepository {
+    private final HttpServer server
+    private final IvyFileRepository backingRepository
+    private final String contextPath
+    private final boolean m2Compatible
+
+    IvyHttpRepository(HttpServer server, String contextPath = "/repo", IvyFileRepository backingRepository, boolean m2Compatible = false) {
+        if (!contextPath.startsWith("/")) {
+            throw new IllegalArgumentException("Context path must start with '/'")
+        }
+        this.server = server
+        this.contextPath = contextPath
+        this.backingRepository = backingRepository
+        this.m2Compatible = m2Compatible
+    }
+
+    URI getUri() {
+        return new URI("${server.uri}${contextPath}")
+    }
+
+    String getIvyPattern() {
+        return "$uri/${backingRepository.baseIvyPattern}"
+    }
+
+    String getArtifactPattern() {
+        return "$uri/${backingRepository.baseArtifactPattern}"
+    }
+
+    HttpDirectoryResource directoryList(String organisation, String module) {
+        return new HttpDirectoryResource(server, "$contextPath/$organisation/$module/", backingRepository.moduleDir(organisation, module))
+    }
+
+    IvyHttpModule module(String organisation, String module, Object revision = "1.0") {
+        def path = backingRepository.getDirPath(organisation, module, revision.toString())
+        return new IvyHttpModule(this, server, "$contextPath/$path", backingRepository.module(organisation, module, revision))
+    }
+
+    String getBaseIvyPattern() {
+        backingRepository.baseIvyPattern
+    }
+
+    String getBaseArtifactPattern() {
+        backingRepository.baseArtifactPattern
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpArtifact.groovy
new file mode 100644
index 0000000..dd9260e
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpArtifact.groovy
@@ -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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.maven.MavenFileModule
+
+class MavenHttpArtifact extends HttpArtifact {
+    private final MavenFileModule backingModule;
+    private final Map options
+
+    public MavenHttpArtifact(HttpServer server, String modulePath, MavenFileModule backingModule, Map<String, ?> options = [:]) {
+        super(server, modulePath)
+        this.options = options
+        this.backingModule = backingModule;
+    }
+
+    @Override
+    TestFile getSha1File() {
+        backingModule.getSha1File(file)
+    }
+
+    @Override
+    TestFile getMd5File() {
+        backingModule.getMd5File(file)
+    }
+
+    TestFile getFile() {
+        return backingModule.getArtifactFile(options)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpModule.groovy
new file mode 100644
index 0000000..853eca2
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpModule.groovy
@@ -0,0 +1,102 @@
+/*
+ * 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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.HttpModule
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.maven.DelegatingMavenModule
+import org.gradle.test.fixtures.maven.MavenFileModule
+import org.gradle.test.fixtures.maven.MavenModule
+
+class MavenHttpModule extends DelegatingMavenModule<MavenHttpModule> implements MavenModule, HttpModule {
+    private final HttpServer server
+    private final String moduleRootPath
+    private final MavenFileModule backingModule
+
+    MavenHttpModule(HttpServer server, String repoRoot, MavenFileModule backingModule) {
+        super(backingModule)
+        this.backingModule = backingModule
+        this.server = server
+        this.moduleRootPath = "${repoRoot}/${backingModule.groupId.replace('.', '/')}/${backingModule.artifactId}"
+    }
+
+    protected String getModuleVersionPath() {
+        "${moduleRootPath}/${backingModule.version}"
+    }
+
+    HttpArtifact getArtifact(Map options = [:]) {
+        return new MavenHttpArtifact(server, "${moduleRootPath}/${backingModule.version}", backingModule, options)
+    }
+
+    /**
+     * Adds an additional artifact to this module.
+     * @param options Can specify any of: type or classifier
+     */
+    HttpArtifact artifact(Map<String, ?> options = [:]) {
+        backingModule.artifact(options)
+        return new MavenHttpArtifact(server, "${moduleRootPath}/${backingModule.version}", backingModule, options)
+    }
+
+    MavenHttpModule withSourceAndJavadoc() {
+        artifact(classifier: "sources")
+        artifact(classifier: "javadoc")
+        return this
+    }
+
+    MavenHttpModule withNoMetaData() {
+        backingModule.withNoMetaData()
+        return this
+    }
+
+    PomHttpArtifact getPom() {
+        return new PomHttpArtifact(server, getModuleVersionPath(), backingModule)
+    }
+
+    MetaDataArtifact getRootMetaData() {
+        return new MetaDataArtifact(server, "$moduleRootPath", backingModule)
+    }
+
+    TestFile getRootMetaDataFile() {
+        return backingModule.rootMetaDataFile
+    }
+
+    MavenHttpModule allowAll() {
+        server.allowGetOrHead(moduleVersionPath, backingModule.moduleDir)
+        return this
+    }
+
+    void missing() {
+        server.allowGetOrHeadMissing(pomPath)
+        server.allowGetOrHeadMissing(metaDataPath)
+        server.allowGetOrHeadMissing(artifactPath)
+    }
+
+    HttpResource getMetaData() {
+        return new BasicHttpResource(server, metaDataFile, getMetaDataPath())
+    }
+
+    String getMetaDataPath() {
+        "$moduleVersionPath/$metaDataFile.name"
+    }
+
+    String getArtifactPath() {
+        "$moduleVersionPath/$artifactFile.name"
+    }
+
+    String getPomPath() {
+        "$moduleVersionPath/$pomFile.name"
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpRepository.groovy
new file mode 100644
index 0000000..7cb9133
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MavenHttpRepository.groovy
@@ -0,0 +1,61 @@
+/*
+ * 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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.HttpRepository
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.maven.MavenRepository
+
+/**
+ * A fixture for dealing with remote HTTP Maven repositories.
+ */
+class MavenHttpRepository implements MavenRepository, HttpRepository {
+    private final HttpServer server
+    private final MavenFileRepository backingRepository
+    private final String contextPath
+
+    MavenHttpRepository(HttpServer server, String contextPath = "/repo", MavenFileRepository backingRepository) {
+        if (!contextPath.startsWith("/")) {
+            throw new IllegalArgumentException("Context path must start with '/'")
+        }
+        this.contextPath = contextPath
+        this.server = server
+        this.backingRepository = backingRepository
+    }
+
+    URI getUri() {
+        return new URI("${server.uri}${contextPath}")
+    }
+
+    HttpResource getModuleMetaData(String groupId, String artifactId) {
+        return module(groupId, artifactId).rootMetaData
+    }
+
+    HttpDirectoryResource directory(String groupId, String artifactId) {
+        def path = "${groupId.replace('.', '/')}/$artifactId/"
+        return new HttpDirectoryResource(server, "$contextPath/$path", backingRepository.getRootDir().file(path))
+    }
+
+    MavenHttpModule module(String groupId, String artifactId) {
+        return module(groupId, artifactId, "1.0")
+    }
+
+    MavenHttpModule module(String groupId, String artifactId, Object version) {
+        def backingModule = backingRepository.module(groupId, artifactId, version)
+        return new MavenHttpModule(server, contextPath, backingModule)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MetaDataArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MetaDataArtifact.groovy
new file mode 100644
index 0000000..7751db0
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/MetaDataArtifact.groovy
@@ -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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.maven.MavenFileModule
+import org.gradle.test.fixtures.maven.MavenMetaData
+
+class MetaDataArtifact extends HttpArtifact implements MavenMetaData {
+    MavenFileModule backingModule
+
+    MetaDataArtifact(HttpServer httpServer, String path, MavenFileModule backingModule) {
+        super(httpServer, path)
+        this.backingModule = backingModule
+    }
+
+    @Override
+    TestFile getSha1File() {
+        backingModule.getSha1File(file)
+    }
+
+    @Override
+    TestFile getMd5File() {
+        backingModule.getMd5File(file)
+    }
+
+    @Override
+    TestFile getFile() {
+        return backingModule.rootMetaDataFile
+    }
+
+    List<String> getVersions() {
+        backingModule.rootMetaData.versions
+    }
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/PomHttpArtifact.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/PomHttpArtifact.groovy
new file mode 100644
index 0000000..def1dd0
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/PomHttpArtifact.groovy
@@ -0,0 +1,44 @@
+/*
+ * 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.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.maven.MavenFileModule
+
+class PomHttpArtifact extends HttpArtifact {
+    MavenFileModule backingModule
+
+    PomHttpArtifact(HttpServer httpServer, String path, MavenFileModule backingModule) {
+        super(httpServer, path)
+        this.backingModule = backingModule
+    }
+
+    @Override
+    TestFile getSha1File() {
+        backingModule.getSha1File(file)
+    }
+
+    @Override
+    TestFile getMd5File() {
+        backingModule.getMd5File(file)
+    }
+
+    @Override
+    TestFile getFile() {
+        return backingModule.pomFile
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/RepositoryHttpServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/RepositoryHttpServer.groovy
new file mode 100644
index 0000000..4fa4c24
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/RepositoryHttpServer.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.http
+
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.gradle.util.GradleVersion
+
+import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
+
+class RepositoryHttpServer extends HttpServer implements RepositoryServer {
+
+    private TestDirectoryProvider testDirectoryProvider
+
+    RepositoryHttpServer(TestDirectoryProvider testDirectoryProvider) {
+        this.testDirectoryProvider = testDirectoryProvider
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        start()
+        expectUserAgent(matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
+    }
+
+    private IvyFileRepository getBackingRepository(boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
+        new IvyFileRepository(testDirectoryProvider.testDirectory.file('ivy-repo'), m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern)
+    }
+
+    RemoteIvyRepository getRemoteIvyRepo(boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
+        return new IvyHttpRepository(this, '/repo', getBackingRepository(m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern), m2Compatible)
+    }
+
+    RemoteIvyRepository getRemoteIvyRepo(String contextPath) {
+        new IvyHttpRepository(this, contextPath, backingRepository)
+    }
+
+    String getValidCredentials() {
+        return ""
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/TestProxyServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/TestProxyServer.groovy
index dd0cebb..3b255d9 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/TestProxyServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/TestProxyServer.groovy
@@ -15,12 +15,13 @@
  */
 package org.gradle.test.fixtures.server.http
 
-import ch.qos.logback.classic.Level
 import org.gradle.util.AvailablePortFinder
 import org.jboss.netty.handler.codec.http.HttpRequest
 import org.junit.rules.ExternalResource
-import org.slf4j.LoggerFactory
-import org.littleshoot.proxy.*
+import org.littleshoot.proxy.DefaultHttpProxyServer
+import org.littleshoot.proxy.HttpProxyServer
+import org.littleshoot.proxy.HttpRequestFilter
+import org.littleshoot.proxy.ProxyAuthorizationHandler
 
 /**
  * A Proxy Server used for testing that http proxies are correctly supported.
@@ -44,9 +45,6 @@ class TestProxyServer extends ExternalResource {
     }
 
     void start() {
-        // Ignore warnings from this class
-        LoggerFactory.getLogger(HttpRequestHandler).level = Level.ERROR
-
         port = AvailablePortFinder.createPrivate().nextAvailable
         String remote = "localhost:${httpServer.port}"
         proxyServer = new DefaultHttpProxyServer(port, [:], remote, null, new HttpRequestFilter() {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpModule.groovy
new file mode 100644
index 0000000..fe88607
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpModule.groovy
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.ivy.IvyDescriptor
+import org.gradle.test.fixtures.ivy.IvyFileModule
+import org.gradle.test.fixtures.ivy.IvyModule
+import org.gradle.test.fixtures.ivy.RemoteIvyModule
+
+class IvySftpModule implements RemoteIvyModule {
+
+    public final IvySftpRepository repository
+    private SFTPServer server
+    private IvyFileModule backingModule
+    
+    IvySftpModule(IvySftpRepository repository, SFTPServer server, IvyFileModule backingModule) {
+        this.repository = repository
+        this.server = server
+        this.backingModule = backingModule
+    }
+
+    @Override
+    void assertPublished() {
+        backingModule.assertPublished()
+    }
+
+    @Override
+    void assertArtifactsPublished(String... names) {
+        backingModule.assertArtifactsPublished(names)
+    }
+
+    @Override
+    void assertPublishedAsJavaModule() {
+        backingModule.assertPublishedAsJavaModule()
+    }
+
+    TestFile getIvyFile() {
+        return backingModule.ivyFile
+    }
+
+    TestFile getJarFile() {
+        return backingModule.jarFile
+    }
+
+    IvyModule withNoMetaData() {
+        return backingModule.withNoMetaData()
+    }
+
+    IvyModule withStatus(String status) {
+        return backingModule.withStatus(status)
+    }
+
+    IvyModule dependsOn(String organisation, String module, String revision) {
+        return backingModule.dependsOn(organisation, module, revision)
+    }
+
+    IvyModule extendsFrom(Map<String, ?> attributes) {
+        return backingModule.extendsFrom(attributes)
+    }
+
+    IvyModule dependsOn(Map<String, ?> attributes) {
+        return backingModule.dependsOn(attributes)
+    }
+
+    IvyModule artifact(Map<String, ?> options) {
+        return backingModule.artifact(options)
+    }
+
+    IvyModule undeclaredArtifact(Map<String, ?> options) {
+        return backingModule.undeclaredArtifact(options)
+    }
+
+    IvyModule withXml(Closure action) {
+        return backingModule.withXml(action)
+    }
+
+    IvyModule configuration(String name) {
+        return backingModule.configuration(name)
+    }
+
+    IvyModule configuration(Map<String, ?> options, String name) {
+        return backingModule.configuration(options, name)
+    }
+
+    IvyModule publishWithChangedContent() {
+        return backingModule.publishWithChangedContent()
+    }
+
+    IvyModule publish() {
+        return backingModule.publish()
+    }
+
+    IvyDescriptor getParsedIvy() {
+        return backingModule.parsedIvy
+    }
+
+    void assertIvyAndJarFilePublished() {
+        backingModule.assertIvyAndJarFilePublished()
+    }
+
+    String getOrganisation() {
+        return backingModule.organisation
+    }
+
+    String getModule() {
+        return backingModule.module
+    }
+
+    String getRevision() {
+        return backingModule.revision
+    }
+
+    SftpArtifact getIvy() {
+        return new SftpArtifact(server, ivyFile)
+    }
+
+    SftpArtifact getJar() {
+        return new SftpArtifact(server, jarFile)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpRepository.groovy
new file mode 100644
index 0000000..f7338aa
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/IvySftpRepository.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+
+class IvySftpRepository implements RemoteIvyRepository {
+
+    private final SFTPServer server
+    private final IvyFileRepository backingRepository
+    private final String contextPath
+
+    IvySftpRepository(SFTPServer server, String contextPath, boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
+        if (!contextPath.startsWith("/")) {
+            throw new IllegalArgumentException("Context path must start with '/'")
+        }
+        this.contextPath = contextPath
+        this.server = server
+        this.backingRepository = new IvyFileRepository(server.file(contextPath.substring(1)), m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern)
+    }
+
+    URI getUri() {
+        return new URI("${serverUri}${contextPath}")
+    }
+
+    URI getServerUri() {
+        server.uri
+    }
+
+    String getIvyPattern() {
+        return "$uri/${backingRepository.baseIvyPattern}"
+    }
+
+    String getArtifactPattern() {
+        return "$uri/${backingRepository.baseArtifactPattern}"
+    }
+
+    String getBaseIvyPattern() {
+        return backingRepository.baseIvyPattern
+    }
+
+    String getBaseArtifactPattern() {
+        return backingRepository.baseArtifactPattern
+    }
+
+    @Override
+    SftpDirectoryResource directoryList(String organisation, String module) {
+        return new SftpDirectoryResource(server, backingRepository.moduleDir(organisation, module))
+    }
+
+    IvySftpModule module(String organisation, String module, Object revision = "1.0") {
+        return new IvySftpModule(this, server, backingRepository.module(organisation, module, revision))
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpModule.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpModule.groovy
new file mode 100644
index 0000000..d538b25
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpModule.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.gradle.test.fixtures.maven.DelegatingMavenModule
+import org.gradle.test.fixtures.maven.MavenFileModule
+import org.gradle.test.fixtures.maven.MavenModule
+
+class MavenSftpModule extends DelegatingMavenModule<MavenSftpModule> implements MavenModule {
+    MavenFileModule backingModule
+    SFTPServer server
+
+    MavenSftpModule(SFTPServer server, MavenFileModule backingModule) {
+        super(backingModule)
+        this.server = server
+        this.backingModule = backingModule
+    }
+
+    SftpArtifact getPom() {
+        return new SftpArtifact(server, pomFile)
+    }
+
+    SftpArtifact getArtifact() {
+        return new SftpArtifact(server, artifactFile)
+    }
+
+    SftpArtifact getMavenMetadata() {
+        return new SftpArtifact(server, metaDataFile)
+    }
+
+    SftpArtifact getRootMavenMetadata() {
+        return new SftpArtifact(server, backingModule.rootMetaDataFile)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpRepository.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpRepository.groovy
new file mode 100644
index 0000000..0c2ce78
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/MavenSftpRepository.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.maven.MavenRepository
+
+class MavenSftpRepository implements MavenRepository {
+    private final SFTPServer server
+    private final MavenFileRepository backingRepository
+    private final String contextPath
+
+    MavenSftpRepository(SFTPServer server, String contextPath) {
+        this.server = server
+        this.backingRepository = new MavenFileRepository(server.file(contextPath.substring(1)))
+        this.contextPath = contextPath
+    }
+
+    URI getUri() {
+        new URI("sftp://${server.hostAddress}:${server.port}${contextPath}")
+    }
+
+    MavenSftpModule module(String organisation, String module, Object revision = "1.0") {
+        new MavenSftpModule(server, backingRepository.module(organisation, module, revision))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy
index 65d22a8..34ba403 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SFTPServer.groovy
@@ -19,50 +19,100 @@ package org.gradle.test.fixtures.server.sftp
 import org.apache.commons.io.FileUtils
 import org.apache.sshd.SshServer
 import org.apache.sshd.common.NamedFactory
-import org.apache.sshd.common.Session
-import org.apache.sshd.server.*
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider
+import org.apache.sshd.common.util.Buffer
+import org.apache.sshd.server.Command
+import org.apache.sshd.server.PasswordAuthenticator
+import org.apache.sshd.server.PublickeyAuthenticator
 import org.apache.sshd.server.command.ScpCommandFactory
-import org.apache.sshd.server.filesystem.NativeFileSystemFactory
-import org.apache.sshd.server.filesystem.NativeSshFile
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
 import org.apache.sshd.server.session.ServerSession
 import org.apache.sshd.server.sftp.SftpSubsystem
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+import org.gradle.test.fixtures.server.ExpectOne
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.gradle.test.fixtures.server.ServerExpectation
+import org.gradle.test.fixtures.server.ServerWithExpectations
 import org.gradle.util.AvailablePortFinder
-import org.junit.rules.ExternalResource
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
 
+import java.security.KeyPair
+import java.security.KeyPairGenerator
 import java.security.PublicKey
+import java.security.SecureRandom
 
-class SFTPServer extends ExternalResource {
-    final String hostAddress;
+class SFTPServer extends ServerWithExpectations implements RepositoryServer {
+
+    private final static Logger logger = LoggerFactory.getLogger(SFTPServer)
+    final String hostAddress
     int port
 
     private final TestDirectoryProvider testDirectoryProvider
-    private TestFile baseDir
     private TestFile configDir
+    private SshServer sshd
 
-    private SshServer sshd;
-
-    def fileRequests = [] as Set
+    TestFile baseDir
+    Map<Integer, String> handleCreatedByRequest = [:]
+    Map<String, Integer> openingRequestIdForPath = [:]
+    List<SftpExpectation> expectations = []
+    private boolean passwordAuthenticationEnabled = true;
 
     public SFTPServer(TestDirectoryProvider testDirectoryProvider) {
         this.testDirectoryProvider = testDirectoryProvider;
-        def portFinder = AvailablePortFinder.createPrivate()
-        port = portFinder.nextAvailable
         this.hostAddress = "127.0.0.1"
     }
 
+    protected Logger getLogger() {
+        logger
+    }
+
+    @Override
+    void resetExpectations() {
+        try {
+            super.resetExpectations()
+        } finally {
+            handleCreatedByRequest.clear()
+            openingRequestIdForPath.clear()
+            allowInit()
+        }
+    }
+
+    /**
+     * this basically restarts the sftpserver without
+     * registering a password authentication
+     * */
+    public withPasswordAuthenticationDisabled(){
+        passwordAuthenticationEnabled = false;
+        restart()
+    }
+
     protected void before() throws Throwable {
         baseDir = testDirectoryProvider.getTestDirectory().createDir("sshd/files")
         configDir = testDirectoryProvider.getTestDirectory().createDir("sshd/config")
 
-        sshd = setupConfiguredTestSshd();
-        sshd.start();
+        def portFinder = AvailablePortFinder.createPrivate()
+        port = portFinder.nextAvailable
+        sshd = setupConfiguredTestSshd()
+        sshd.start()
+        allowInit()
+    }
+
+    public void stop(boolean immediately = false) {
+        sshd?.stop(immediately);
     }
 
+    public void restart() {
+        stop(true)
+        before()
+    }
+
+    @Override
     protected void after() {
-        sshd?.stop()
+        super.after();
+        passwordAuthenticationEnabled = true
     }
 
     private SshServer setupConfiguredTestSshd() {
@@ -72,15 +122,19 @@ class SFTPServer extends ExternalResource {
 
         SshServer sshServer = SshServer.setUpDefaultServer();
         sshServer.setPort(port);
-        sshServer.setFileSystemFactory(new TestNativeFileSystemFactory(baseDir.absolutePath, new FileRequestLogger() {
-            void logRequest(String message) {
-                fileRequests << message;
+        sshServer.setFileSystemFactory(new TestVirtualFileSystemFactory());
+        sshServer.setSubsystemFactories(Arrays.<NamedFactory<Command>> asList(new SftpSubsystem.Factory() {
+            Command create() {
+                new TestSftpSubsystem()
             }
         }));
-        sshServer.setSubsystemFactories(Arrays.<NamedFactory<Command>> asList(new SftpSubsystem.Factory()));
         sshServer.setCommandFactory(new ScpCommandFactory());
-        sshServer.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("${configDir}/test-dsa.key"));
-        sshServer.setPasswordAuthenticator(new DummyPasswordAuthenticator());
+        sshServer.setKeyPairProvider(new GeneratingKeyPairProvider());
+
+        if(passwordAuthenticationEnabled){
+            sshServer.setPasswordAuthenticator(new DummyPasswordAuthenticator());
+        }
+
         sshServer.setPublickeyAuthenticator(new PublickeyAuthenticator() {
             boolean authenticate(String username, PublicKey key, ServerSession session) {
                 return true
@@ -89,6 +143,7 @@ class SFTPServer extends ExternalResource {
         return sshServer;
     }
 
+
     boolean hasFile(String filePathToCheck) {
         new File(baseDir, filePathToCheck).exists()
     }
@@ -97,12 +152,115 @@ class SFTPServer extends ExternalResource {
         new TestFile(new File(baseDir, expectedPath))
     }
 
-    public Set<String> getFileRequests() {
-        return fileRequests
+    URI getUri() {
+        return new URI("sftp://${hostAddress}:${port}")
+    }
+
+    void allowAll() {
+        expectations << new SftpAllowAll()
+    }
+
+    void allowInit() {
+        expectations << new SftpAllow(SftpSubsystem.SSH_FXP_INIT)
+    }
+
+    void expectLstat(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_LSTAT, "LSTAT", path)
+    }
+
+    void expectMetadataRetrieve(String path) {
+        expectLstat(path)
     }
 
-    public void clearRequests() {
-        fileRequests.clear();
+    void expectOpen(String path) {
+        expectations << new SftpExpectOneOpen(SftpSubsystem.SSH_FXP_OPEN, "OPEN", path)
+    }
+
+    void allowRead(String path) {
+        expectations << new SftpAllowHandle(SftpSubsystem.SSH_FXP_READ, path)
+    }
+
+    void expectClose(String path) {
+        expectations << new SftpExpectOneHandle(SftpSubsystem.SSH_FXP_CLOSE, "CLOSE", path)
+    }
+
+    void expectFileDownload(String path) {
+        expectOpen(path)
+        allowRead(path)
+        expectClose(path)
+    }
+
+    void expectFileUpload(String path) {
+        expectOpen(path)
+        allowWrite(path)
+        expectClose(path)
+    }
+
+    void expectRealpath(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_REALPATH, "REALPATH", path)
+    }
+
+    void expectStat(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_STAT, "STAT", path)
+    }
+
+    void expectMkdir(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_MKDIR, "MKDIR", path)
+    }
+
+    void expectOpendir(String path) {
+        expectations << new SftpExpectOneOpen(SftpSubsystem.SSH_FXP_OPENDIR, "OPENDIR", path)
+    }
+
+    void allowReaddir(String path) {
+        expectations << new SftpAllowHandle(SftpSubsystem.SSH_FXP_READDIR, path)
+    }
+
+    void allowWrite(String path) {
+        expectations << new SftpAllowHandle(SftpSubsystem.SSH_FXP_WRITE, path)
+    }
+
+    void expectDirectoryList(String path) {
+        expectOpendir(path)
+        allowReaddir(path)
+        expectClose(path)
+    }
+
+    void expectLstatBroken(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_LSTAT, "LSTAT", path, true)
+    }
+
+    void expectMkdirBroken(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_MKDIR, "MKDIR", path, true)
+    }
+
+    void expectMetadataRetrieveBroken(String path) {
+        expectLstatBroken(path)
+    }
+
+    void expectWriteBroken(String path) {
+        expectations << new SftpExpectOneHandle(SftpSubsystem.SSH_FXP_WRITE, "WRITE", path, true)
+    }
+
+    void expectLstatMissing(String path) {
+        expectations << new SftpExpectOnePath(SftpSubsystem.SSH_FXP_LSTAT, "LSTAT", path, false, true)
+    }
+
+    RemoteIvyRepository getRemoteIvyRepo(boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
+        new IvySftpRepository(this, '/repo', m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern)
+    }
+
+    RemoteIvyRepository getRemoteIvyRepo(String contextPath) {
+        new IvySftpRepository(this, contextPath, false, null)
+    }
+
+    String getValidCredentials() {
+        return """
+            credentials {
+                username 'sftp'
+                password 'sftp'
+            }
+        """
     }
 
     static class DummyPasswordAuthenticator implements PasswordAuthenticator {
@@ -112,96 +270,239 @@ class SFTPServer extends ExternalResource {
         }
     }
 
-    static abstract class FileRequestLogger {
-        abstract void logRequest(String message)
+    class TestVirtualFileSystemFactory extends VirtualFileSystemFactory {
+        TestVirtualFileSystemFactory() {
+            setDefaultHomeDir(baseDir.absolutePath)
+        }
     }
 
-    static class TestNativeFileSystemFactory extends NativeFileSystemFactory {
+    class TestSftpSubsystem extends SftpSubsystem {
+
+        @Override
+        protected void process(Buffer buffer) throws IOException {
+            int originalBufferPosition = buffer.rpos()
+            int length = buffer.getInt()
+            int type = buffer.getByte()
+            int id = buffer.getInt()
+
+            int pos = buffer.rpos()
+            def command = commandMessage(buffer, type)
+            println ("Handling $command")
+            buffer.rpos(pos)
+
+            def matched = expectations.find { it.matches(buffer, type, id) }
+            if (matched) {
+                if (matched.failing) {
+                    sendStatus(id, SSH_FX_FAILURE, "Failure")
+                    buffer.rpos(originalBufferPosition + length)
+                } else if (matched.missing) {
+                    sendStatus(id, SSH_FX_NO_SUCH_FILE, "No such file")
+                    buffer.rpos(originalBufferPosition + length)
+                } else {
+                    buffer.rpos(originalBufferPosition)
+                    super.process(buffer)
+                }
+            } else {
+                onFailure(new AssertionError("Unexpected SFTP command: $command"))
+                sendStatus(id, SSH_FX_FAILURE, "Unexpected command")
+                buffer.rpos(originalBufferPosition + length)
+            }
+        }
 
-        String rootPath
+        @Override
+        protected void sendHandle(int id, String handle) throws IOException {
+            super.sendHandle(id, handle)
+            handleCreatedByRequest[id] = handle
+        }
 
-        List<FileRequestLogger> logger
+        private String commandMessage(Buffer buffer, int type) {
+            switch (type) {
+                case SSH_FXP_INIT:
+                    return "INIT"
+                case SSH_FXP_LSTAT:
+                    return "LSTAT for ${buffer.getString()}"
+                case SSH_FXP_OPEN:
+                    return "OPEN for ${buffer.getString()}"
+                case SSH_FXP_READ:
+                    return "READ"
+                case SSH_FXP_CLOSE:
+                    return "CLOSE"
+                case SSH_FXP_REALPATH:
+                    return "REALPATH for ${buffer.getString()}"
+                case SSH_FXP_STAT:
+                    return "STAT for ${buffer.getString()}"
+                case SSH_FXP_OPENDIR:
+                    return "OPENDIR for ${buffer.getString()}"
+                case SSH_FXP_READDIR:
+                    return "READDIR for ${buffer.getString()}"
+                case SSH_FXP_MKDIR:
+                    return "MKDIR for ${buffer.getString()}"
+                case SSH_FXP_WRITE:
+                    return "WRITE"
+            }
+            return type;
+        }
+    }
+
+    static interface SftpExpectation extends ServerExpectation {
+        boolean matches(Buffer buffer, int type, int id)
+
+        boolean isFailing()
+        boolean isMissing()
+    }
+
+    static class SftpExpectOne extends ExpectOne implements SftpExpectation {
 
-        public TestNativeFileSystemFactory(String rootPath, FileRequestLogger... logger) {
-            this.rootPath = rootPath
-            this.logger = Arrays.asList(logger)
+        final int expectedType
+        final String notMetMessage
+        final Closure matcher
+        final boolean failing
+        final boolean missing
+
+        SftpExpectOne(int type, String notMetMessage, boolean failing = false, boolean missing = false) {
+            this.expectedType = type
+            this.notMetMessage = "Expected SFTP command not received: $notMetMessage"
+            this.matcher = matcher
+            this.failing = failing
+            this.missing = missing
+        }
+
+        boolean matches(Buffer buffer, int type, int id) {
+            if (!run && type == expectedType) {
+                int originalBufferPosition = buffer.rpos()
+                run = bufferMatches(buffer, id)
+                buffer.rpos(originalBufferPosition)
+                return run
+            } else {
+                return false
+            }
         }
 
-        /**
-         * Create the appropriate user file system view.
-         */
-        public FileSystemView createFileSystemView(Session session) {
-            String userName = session.getUsername();
-            FileSystemView fsView = new TestNativeFileSystemView(rootPath, userName, logger, caseInsensitive);
-            return fsView;
+        protected boolean bufferMatches(Buffer buffer, int id) {
+            true
         }
     }
 
-    static class TestNativeFileSystemView implements FileSystemView {
-        // the first and the last character will always be '/'
-        // It is always with respect to the root directory.
-        private String currDir;
+    static class SftpExpectOnePath extends SftpExpectOne {
 
-        private String userName;
+        final String path
 
-        private boolean caseInsensitive = false;
+        SftpExpectOnePath(int type, String commandName, String path, boolean failing = false, boolean missing = false) {
+            super(type, "$commandName for $path", failing, missing)
+            this.path = path
+        }
 
-        List<FileRequestLogger> logger
+        protected boolean bufferMatches(Buffer buffer, int id) {
+            buffer.getString() == path
+        }
+    }
 
-        /**
-         * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
-         */
-        public TestNativeFileSystemView(String rootpath, String userName, List<FileRequestLogger> requestLoggerList, boolean caseInsensitive) {
-            if (!rootpath) {
-                throw new IllegalArgumentException("rootPath must be set");
-            }
+    class SftpExpectOneOpen extends SftpExpectOnePath {
+
+        SftpExpectOneOpen(int type, String commandName, String path, boolean failing = false) {
+            super(type, commandName, path, failing)
+        }
 
-            if (!userName) {
-                throw new IllegalArgumentException("user can not be null");
+        protected boolean bufferMatches(Buffer buffer, int id) {
+            def matched = buffer.getString() == path
+            if (matched) {
+                openingRequestIdForPath[path] = id
             }
+            return matched
+        }
+    }
 
-            this.logger = requestLoggerList;
-            this.caseInsensitive = caseInsensitive;
+    class SftpExpectOneHandle extends SftpExpectOnePath {
 
-            currDir = rootpath;
-            this.userName = userName;
+        SftpExpectOneHandle(int type, String commandName, String path, boolean failing = false) {
+            super(type, commandName, path, failing)
         }
 
-        /**
-         * Get file object.
-         */
-        public SshFile getFile(String file) {
-            return getFile(currDir, file);
+        protected boolean bufferMatches(Buffer buffer, int id) {
+            def handle = buffer.getString()
+            def openingRequestId = openingRequestIdForPath[path]
+            return openingRequestId && handle == handleCreatedByRequest[openingRequestId]
         }
+    }
+
+    class SftpAllow implements SftpExpectation {
 
-        public SshFile getFile(SshFile baseDir, String file) {
-            return getFile(baseDir.getAbsolutePath(), file);
+        final boolean failing = false
+        final boolean missing = false
+        final int expectedType
+
+        SftpAllow(int expectedType) {
+            this.expectedType = expectedType
         }
 
-        protected SshFile getFile(String dir, String file) {
-            // get actual file object
+        boolean matches(Buffer buffer, int type, int id) {
+            return type == expectedType
+        }
 
-            String physicalName = NativeSshFile.getPhysicalName("/", dir, file, caseInsensitive);
-            File fileObj = new File(physicalName);
-            logFileRequest(dir, fileObj.absolutePath);
-            // strip the root directory and return
-            String userFileName = physicalName.substring("/".length() - 1);
-            return new NativeSshFile(userFileName, fileObj, userName);
+        void assertMet() {
+            //can never be not met
         }
+    }
 
-        void logFileRequest(String dir, String file) {
-            //log xml and jar requests only
-            if (file.endsWith("xml") || file.endsWith(".jar")) {
-                String normalizedPath = (file - dir).replaceAll("\\\\", '/') - "/"
-                logger.each {
-                    it.logRequest(normalizedPath)
-                }
+    class SftpAllowAll implements SftpExpectation {
+
+        final boolean failing = false
+        final boolean missing = false
+
+        boolean matches(Buffer buffer, int type, int id) {
+            return true
+        }
+
+        void assertMet() {
+            //can never be not met
+        }
+    }
+
+    class SftpAllowHandle implements SftpExpectation {
+
+        final int expectedType
+        final boolean failing = false
+        final boolean missing = false
+        final String path
+
+        SftpAllowHandle(int type, String path) {
+            this.expectedType = type
+            this.path = path
+        }
+
+        void assertMet() {
+            //can never be not met
+        }
+
+        boolean matches(Buffer buffer, int type, int id) {
+            if (type == expectedType) {
+                int originalBufferPosition = buffer.rpos()
+                def handle = buffer.getString()
+                def openingRequestId = openingRequestIdForPath[path]
+                def matched = openingRequestId && handle == handleCreatedByRequest[openingRequestId]
+                buffer.rpos(originalBufferPosition)
+                return matched
+            } else {
+                return false
             }
         }
     }
 
+    class GeneratingKeyPairProvider extends AbstractKeyPairProvider {
 
+        KeyPair keyPair
 
+        GeneratingKeyPairProvider() {
+            KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA")
+            generator.initialize(1024, SecureRandom.getInstance("SHA1PRNG"))
+            keyPair = generator.generateKeyPair()
+        }
+
+        @Override
+        Iterable<KeyPair> loadKeys() {
+            [keyPair]
+        }
+    }
 }
 
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpArtifact.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpArtifact.java
new file mode 100644
index 0000000..71b42a0
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpArtifact.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp;
+
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.test.fixtures.resource.RemoteArtifact;
+
+public class SftpArtifact extends SftpResource implements RemoteArtifact {
+    public SftpArtifact(SFTPServer server, TestFile file) {
+        super(server, file);
+    }
+
+    @Override
+    public SftpResource getMd5() {
+        return new SftpResource(getServer(), getFile().getParentFile().file(getFile().getName() + ".md5"));
+    }
+
+    @Override
+    public SftpResource getSha1() {
+        return new SftpResource(getServer(), getFile().getParentFile().file(getFile().getName() + ".sha1"));
+    }
+}
+
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpDirectoryResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpDirectoryResource.groovy
new file mode 100644
index 0000000..094624e
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpDirectoryResource.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteResource
+import org.gradle.util.GFileUtils
+
+class SftpDirectoryResource implements RemoteResource {
+
+    SFTPServer server
+    TestFile file
+
+    SftpDirectoryResource(SFTPServer server, TestFile file) {
+        this.server = server
+        this.file = file
+    }
+
+    String getPathOnServer() {
+        return "/${GFileUtils.relativePath(server.baseDir, file)}/"
+    }
+
+    URI getUri() {
+        return new URI("${server.uri}${pathOnServer}")
+    }
+
+    void expectMetadataRetrieveBroken() {
+        throw new UnsupportedOperationException()
+    }
+
+    void expectMetadataRetrieveMissing() {
+        throw new UnsupportedOperationException()
+    }
+
+    void expectDownload() {
+        server.expectStat(pathOnServer)
+        server.expectDirectoryList(pathOnServer)
+    }
+
+    void expectDownloadMissing() {
+        throw new UnsupportedOperationException()
+    }
+
+    void expectMetadataRetrieve() {
+        throw new UnsupportedOperationException()
+    }
+
+    void expectDownloadBroken() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectParentMkdir() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectParentCheckdir() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectUpload() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectUploadBroken() {
+        throw new UnsupportedOperationException()
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpResource.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpResource.groovy
new file mode 100644
index 0000000..9259c12
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/sftp/SftpResource.groovy
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.server.sftp
+
+import org.apache.commons.io.FilenameUtils
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteResource
+import org.gradle.util.GFileUtils
+
+class SftpResource implements RemoteResource {
+
+    SFTPServer server
+    TestFile file
+
+    SftpResource(SFTPServer server, TestFile file) {
+        this.server = server
+        this.file = file
+    }
+
+    String getPathOnServer() {
+        return "/${GFileUtils.relativePath(server.baseDir, file)}"
+    }
+
+    URI getUri() {
+        return new URI("${server.uri}${pathOnServer}")
+    }
+
+    void expectLstat() {
+        server.expectLstat(pathOnServer)
+    }
+
+    void expectLstatMissing() {
+        server.expectLstatMissing(pathOnServer)
+    }
+
+    void expectStat() {
+        server.expectStat(pathOnServer)
+    }
+
+    void expectOpen() {
+        server.expectOpen(pathOnServer)
+    }
+
+    def allowWrite() {
+        server.allowWrite(pathOnServer)
+    }
+
+    void expectClose() {
+        server.expectClose(pathOnServer)
+    }
+
+    void expectFileDownload() {
+        // TODO - should not do this stat request
+        server.expectStat(pathOnServer)
+        server.expectFileDownload(pathOnServer)
+    }
+
+    void expectFileUpload() {
+        server.expectFileUpload(pathOnServer)
+    }
+
+    @Override
+    void expectMetadataRetrieveBroken() {
+        server.expectMetadataRetrieveBroken(pathOnServer)
+    }
+
+    @Override
+    void expectMetadataRetrieveMissing() {
+        server.expectLstatMissing(pathOnServer)
+    }
+
+    @Override
+    void expectParentMkdir() {
+        withEachDirectory { String path ->
+            server.expectLstat(path)
+            server.expectMkdir(path)
+        }
+    }
+
+    @Override
+    void expectParentCheckdir() {
+        server.expectLstat(FilenameUtils.getFullPathNoEndSeparator(pathOnServer))
+    }
+
+    void withEachDirectory(Closure action) {
+        def directory = FilenameUtils.getFullPathNoEndSeparator(pathOnServer)
+        directory.tokenize('/').findAll().inject('') { path, token ->
+            def currentPath = "$path/$token"
+            action(currentPath)
+            currentPath
+        }
+    }
+
+    @Override
+    void expectDownload() {
+        expectMetadataRetrieve()
+        expectFileDownload()
+    }
+
+    @Override
+    void expectDownloadMissing() {
+        expectMetadataRetrieveMissing()
+    }
+
+    @Override
+    void expectMetadataRetrieve() {
+        expectLstat()
+    }
+
+    @Override
+    void expectDownloadBroken() {
+        expectMetadataRetrieveBroken()
+    }
+
+    @Override
+    void expectUpload() {
+        expectFileUpload()
+    }
+
+    @Override
+    void expectUploadBroken() {
+        server.expectOpen(pathOnServer)
+        server.expectWriteBroken(pathOnServer)
+        // TODO - should be a CLOSE
+        server.expectStat(pathOnServer)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/resources/logback.xml b/subprojects/internal-integ-testing/src/main/resources/logback.xml
deleted file mode 100644
index dc5db3a..0000000
--- a/subprojects/internal-integ-testing/src/main/resources/logback.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<configuration>
-
-    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-        <!-- encoders are assigned the type
-     ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
-        <encoder>
-            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
-        </encoder>
-    </appender>
-
-    <root level="warn">
-        <appender-ref ref="STDOUT" />
-    </root>
-</configuration>
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest/shared/clientStore b/subprojects/internal-integ-testing/src/main/resources/test-key-store/keyStore
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest/shared/clientStore
rename to subprojects/internal-integ-testing/src/main/resources/test-key-store/keyStore
diff --git a/subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest/shared/serverStore b/subprojects/internal-integ-testing/src/main/resources/test-key-store/trustStore
similarity index 100%
rename from subprojects/core-impl/src/integTest/resources/org/gradle/integtests/resolve/http/AbstractHttpsRepoResolveIntegrationTest/shared/serverStore
rename to subprojects/internal-integ-testing/src/main/resources/test-key-store/trustStore
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParserTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParserTest.groovy
new file mode 100644
index 0000000..335a8a5
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/OsXJavaHomeParserTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures.jvm
+
+import org.gradle.api.JavaVersion
+import org.gradle.util.VersionNumber
+import spock.lang.Specification
+
+class OsXJavaHomeParserTest extends Specification {
+    def parser = new OsXJavaHomeParser()
+
+    def "parses new format output"() {
+        def output = """Matching Java Virtual Machines (10):
+    1.8.0, x86_64:\t"Java SE 8"\t/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home
+    1.7.0_17, x86_64:\t"Java SE 7"\t/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home
+    1.7.0_07, x86_64:\t"Java SE 7"\t/Library/Java/JavaVirtualMachines/jdk1.7.0_07.jdk/Contents/Home
+    1.7.0_06, x86_64:\t"Java SE 7"\t/Library/Java/JavaVirtualMachines/jdk1.7.0_06.jdk/Contents/Home
+    1.7.0-ea-b223-ea-b223-ea-b223, x86_64:\t"Java SE 7 Developer Preview"\t/Library/Java/JavaVirtualMachines/JDK 1.7.0 Developer Preview.jdk/Contents/Home
+    1.7.0-ea-b223-ea-b223-ea-b223, i386:\t"Java SE 7 Developer Preview"\t/Library/Java/JavaVirtualMachines/JDK 1.7.0 Developer Preview.jdk/Contents/Home
+    1.7.0, x86_64:\t"OpenJDK 7"\t/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home
+    1.7.0, i386:\t"OpenJDK 7"\t/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home
+    1.6.0_65-b14-462, x86_64:\t"Java SE 6"\t/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
+    1.6.0_65-b14-462, i386:\t"Java SE 6"\t/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
+
+"""
+
+        expect:
+        def result = parser.parse(new StringReader(output))
+        result.size() == 10
+        result[0].version == parse("1.8.0")
+        result[0].javaVersion == JavaVersion.VERSION_1_8
+        result[0].jdk
+        result[0].arch == JvmInstallation.Arch.x86_64
+        result[0].javaHome == new File("/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home")
+
+        result[1].version == parse("1.7.0_17")
+        result[1].javaVersion == JavaVersion.VERSION_1_7
+        result[1].javaHome == new File("/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home")
+
+        result[4].version == parse("1.7.0-ea-b223-ea-b223-ea-b223")
+        result[4].javaVersion == JavaVersion.VERSION_1_7
+        result[4].javaHome == new File("/Library/Java/JavaVirtualMachines/JDK 1.7.0 Developer Preview.jdk/Contents/Home")
+
+        result[6].version == parse("1.7.0")
+        result[6].javaVersion == JavaVersion.VERSION_1_7
+        result[6].javaHome == new File("/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home")
+
+        result[8].version == parse("1.6.0_65-b14-462")
+        result[8].javaVersion == JavaVersion.VERSION_1_6
+        result[8].jdk
+        result[8].arch == JvmInstallation.Arch.x86_64
+        result[8].javaHome == new File("/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home")
+
+        result[9].version == parse("1.6.0_65-b14-462")
+        result[9].javaVersion == JavaVersion.VERSION_1_6
+        result[9].jdk
+        result[9].arch == JvmInstallation.Arch.i386
+        result[9].javaHome == new File("/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home")
+    }
+
+    def "parses old format output"() {
+        def output ="""Matching Java Virtual Machines (2):
+    1.6.0_17 (x86_64):\t/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
+    1.6.0_17 (i386):\t/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
+
+"""
+
+        expect:
+        def result = parser.parse(new StringReader(output))
+        result.size() == 2
+        result[0].version == parse("1.6.0_17")
+        result[0].javaVersion == JavaVersion.VERSION_1_6
+        result[0].jdk
+        result[0].arch == JvmInstallation.Arch.x86_64
+        result[0].javaHome == new File("/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home")
+
+        result[1].version == parse("1.6.0_17")
+        result[1].javaVersion == JavaVersion.VERSION_1_6
+        result[1].jdk
+        result[1].arch == JvmInstallation.Arch.i386
+        result[1].javaHome == new File("/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home")
+    }
+
+    def parse(String version) {
+        return VersionNumber.withPatchNumber().parse(version)
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocatorTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocatorTest.groovy
new file mode 100644
index 0000000..0707d21
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/jvm/UbuntuJvmLocatorTest.groovy
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures.jvm
+
+import org.gradle.api.JavaVersion
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.VersionNumber
+import org.junit.Rule
+import spock.lang.Specification
+
+class UbuntuJvmLocatorTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def libDir = tmpDir.file("lib")
+    def fileCanonicalizer = Stub(FileCanonicalizer) {
+        canonicalize(_) >> { File f -> f.canonicalFile }
+    }
+    def locator = new UbuntuJvmLocator(new File(libDir.absolutePath), fileCanonicalizer)
+
+    def "finds no JVMs when lib directory does not exist"() {
+        expect:
+        locator.findJvms().empty
+    }
+
+    def "locates JREs installed in lib directory"() {
+        given:
+        jre("java-1.6.0-openjdk-amd64")
+        jre("java-1.6.0-openjdk-i386")
+        jre("java-1.7.0-openjdk-amd64")
+        jre("java-1.7.0-openjdk-i386")
+        libDir.createDir("not-a-jre")
+        libDir.createDir("java-1.5.0-openjdk-amd64")
+
+        expect:
+        def jvms = locator.findJvms()
+        jvms.size() == 4
+        jvms.sort { it.javaHome.name }
+
+        jvms[0].javaVersion == JavaVersion.VERSION_1_6
+        jvms[0].version == VersionNumber.parse("1.6.0")
+        jvms[0].arch == JvmInstallation.Arch.x86_64
+        !jvms[0].jdk
+        jvms[0].javaHome == libDir.file("java-1.6.0-openjdk-amd64")
+
+        jvms[3].javaVersion == JavaVersion.VERSION_1_7
+        jvms[3].version == VersionNumber.parse("1.7.0")
+        jvms[3].arch == JvmInstallation.Arch.i386
+        !jvms[3].jdk
+        jvms[3].javaHome == libDir.file("java-1.7.0-openjdk-i386")
+    }
+
+    def "locates JDKs installed in lib directory"() {
+        given:
+        jre("java-1.6.0-openjdk-amd64")
+        jdk("java-1.7.0-openjdk-amd64")
+        libDir.createDir("not-a-jre")
+        libDir.createDir("java-1.5.0-openjdk-amd64")
+
+        expect:
+        def jvms = locator.findJvms()
+        jvms.size() == 2
+        jvms.sort { it.javaHome.name }
+
+        jvms[0].javaVersion == JavaVersion.VERSION_1_6
+        !jvms[0].jdk
+
+        jvms[1].javaVersion == JavaVersion.VERSION_1_7
+        jvms[1].jdk
+    }
+
+    @Requires(TestPrecondition.SYMLINKS)
+    def "locates JDK in canonicalized directory"() {
+        given:
+        jdk("real-install/java-1.7-openjdk-amd64")
+        NativeServicesTestFixture.initialize(tmpDir.testDirectory)
+        libDir.file("java-1.7.0-openjdk-amd64").createLink("real-install/java-1.7-openjdk-amd64")
+
+        expect:
+        def jvms = locator.findJvms()
+        jvms.size() == 1
+
+        jvms[0].javaVersion == JavaVersion.VERSION_1_7
+        jvms[0].jdk
+        jvms[0].javaHome == libDir.file("real-install/java-1.7-openjdk-amd64")
+    }
+
+    def jre(String name) {
+        libDir.createFile("${name}/bin/java")
+        libDir.createFile("${name}/jre/bin/java")
+    }
+
+    def jdk(String name) {
+        jre(name)
+        libDir.createFile("${name}/bin/javac")
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenFileModuleTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenFileModuleTest.groovy
index 278da44..bfbbbc6 100644
--- a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenFileModuleTest.groovy
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenFileModuleTest.groovy
@@ -16,39 +16,23 @@
 
 package org.gradle.test.fixtures.maven
 
-import org.gradle.api.Project
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.TestUtil
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
 import spock.lang.Specification
 
 class MavenFileModuleTest extends Specification {
-    Project project
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     TestFile testFile
     MavenModule mavenFileModule
     MavenModule snapshotMavenFileModule
 
     def setup() {
-        project = TestUtil.createRootProject()
-        testFile = new TestFile(project.projectDir, "build/test")
+        testFile = tmpDir.file("file")
         mavenFileModule = new MavenFileModule(testFile, "my-company", "my-artifact", "1.0")
         snapshotMavenFileModule = new MavenFileModule(testFile, "my-company", "my-artifact", "1.0-SNAPSHOT")
     }
 
-    def "Get parent and its POM section"() {
-        when:
-        MavenFileModule parent = mavenFileModule.parent("my-company", "parent", "0.1")
-
-        then:
-        parent != null
-"""
-<parent>
-  <groupId>my-company</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1</version>
-</parent>
-""" == parent.parentPomSection
-    }
-
     def "Add multiple dependencies without type"() {
         when:
         List dependencies = mavenFileModule.dependsOn("dep1", "dep2").dependencies
@@ -129,8 +113,7 @@ class MavenFileModuleTest extends Specification {
 
     def "On publishing SHA1 and MD5 files are created"() {
         given:
-        TestFile pomTestFile = new TestFile(project.projectDir, "build/test/pom.xml")
-        pomTestFile.createFile()
+        TestFile pomTestFile = tmpDir.createFile("build/test/pom.xml")
 
         when:
         mavenFileModule.onPublish(pomTestFile)
@@ -208,7 +191,7 @@ class MavenFileModuleTest extends Specification {
         publishedFiles.find { it.name == 'maven-metadata.xml' }.exists()
         new XmlSlurper().parseText(publishedFiles.find { it.name == 'maven-metadata.xml' }.text).versioning.snapshot.timestamp.text() == '20100101.120001'
         new XmlSlurper().parseText(publishedFiles.find { it.name == 'maven-metadata.xml' }.text).versioning.snapshot.buildNumber.text() == '1'
-        snapshotMavenFileModule.assertArtifactsPublished('my-artifact-1.0-20100101.120001-1.jar', 'my-artifact-1.0-20100101.120001-1.pom')
+        snapshotMavenFileModule.assertArtifactsPublished('maven-metadata.xml', 'my-artifact-1.0-20100101.120001-1.jar', 'my-artifact-1.0-20100101.120001-1.pom')
     }
 
     def "Publish artifacts for non-unique snapshot"() {
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenLocalModuleTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenLocalModuleTest.groovy
index 8abc8e9..773c9ed 100644
--- a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenLocalModuleTest.groovy
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/maven/MavenLocalModuleTest.groovy
@@ -16,39 +16,23 @@
 
 package org.gradle.test.fixtures.maven
 
-import org.gradle.api.Project
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.TestUtil
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
 import spock.lang.Specification
 
 class MavenLocalModuleTest extends Specification {
-    Project project
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     TestFile testFile
     MavenModule mavenLocalModule
     MavenModule snapshotMavenLocalModule
 
     def setup() {
-        project = TestUtil.createRootProject()
-        testFile = new TestFile(project.projectDir, "build/test")
+        testFile = tmpDir.file("file")
         mavenLocalModule = new MavenLocalModule(testFile, "my-company", "my-artifact", "1.0")
         snapshotMavenLocalModule = new MavenLocalModule(testFile, "my-company", "my-artifact", "1.0-SNAPSHOT")
     }
 
-    def "Get parent and its POM section"() {
-        when:
-        MavenLocalModule parent = mavenLocalModule.parent("my-company", "parent", "0.1")
-
-        then:
-        parent != null
-        """
-<parent>
-  <groupId>my-company</groupId>
-  <artifactId>parent</artifactId>
-  <version>0.1</version>
-</parent>
-""" == parent.parentPomSection
-    }
-
     def "Add multiple dependencies without type"() {
         when:
         List dependencies = mavenLocalModule.dependsOn("dep1", "dep2").dependencies
@@ -119,10 +103,9 @@ class MavenLocalModuleTest extends Specification {
         mavenModule != null
     }
 
-    def "On publishing SHA1 and MD5 files are created"() {
+    def "On publishing SHA1 and MD5 files are not created"() {
         given:
-        TestFile pomTestFile = new TestFile(project.projectDir, "build/test/pom.xml")
-        pomTestFile.createFile()
+        def pomTestFile = tmpDir.createFile("build/test/pom.xml")
 
         when:
         mavenLocalModule.onPublish(pomTestFile)
@@ -199,7 +182,7 @@ class MavenLocalModuleTest extends Specification {
         publishedFiles*.name.containsAll('my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
         publishedFiles.find { it.name == 'maven-metadata.xml' }.exists()
         new XmlSlurper().parseText(publishedFiles.find { it.name == 'maven-metadata.xml' }.text).versioning.snapshot.localCopy.text() == 'true'
-        snapshotMavenLocalModule.assertArtifactsPublished('my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
+        snapshotMavenLocalModule.assertArtifactsPublished('maven-metadata.xml', 'my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
     }
 
     def "Publish artifacts for non-unique snapshot"() {
@@ -215,6 +198,6 @@ class MavenLocalModuleTest extends Specification {
         publishedFiles*.name.containsAll('my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
         publishedFiles.find { it.name == 'maven-metadata.xml' }.exists()
         new XmlSlurper().parseText(publishedFiles.find { it.name == 'maven-metadata.xml' }.text).versioning.snapshot.localCopy.text() == 'true'
-        snapshotMavenLocalModule.assertArtifactsPublished('my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
+        snapshotMavenLocalModule.assertArtifactsPublished('maven-metadata.xml', 'my-artifact-1.0-SNAPSHOT.jar', 'my-artifact-1.0-SNAPSHOT.pom')
     }
 }
diff --git a/subprojects/internal-testing/internal-testing.gradle b/subprojects/internal-testing/internal-testing.gradle
index e8a9410..432378b 100644
--- a/subprojects/internal-testing/internal-testing.gradle
+++ b/subprojects/internal-testing/internal-testing.gradle
@@ -26,6 +26,7 @@ dependencies {
     compile libraries.commons_lang
     compile libraries.commons_io
     compile libraries.ant
+    compile libraries.asm
     compile libraries.junit
     compile libraries.jmock
     compile libraries.spock
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
index 31aeb78..b9b94ea 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
@@ -25,7 +25,7 @@ class DefaultTestExecutionResult implements TestExecutionResult {
 
     public DefaultTestExecutionResult(TestFile projectDir, String buildDirName = 'build') {
         results << new HtmlTestExecutionResult(projectDir, "$buildDirName/reports/tests")
-        results << new JUnitXmlTestExecutionResult(projectDir, buildDirName)
+        results << new JUnitXmlTestExecutionResult(projectDir, "$buildDirName/test-results")
     }
 
     TestExecutionResult assertTestClassesExecuted(String... testClasses) {
@@ -105,5 +105,10 @@ class DefaultTestExecutionResult implements TestExecutionResult {
             testClassResults*.assertTestCaseStderr(testCaseName, matcher)
             this
         }
+
+        TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher) {
+            testClassResults*.assertExecutionFailedWithCause(causeMatcher)
+            this
+        }
     }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
index d654f43..939ca3a 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
@@ -19,6 +19,7 @@ import org.gradle.internal.FileUtils
 import org.hamcrest.Matcher
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
+import org.junit.Assert
 
 class HtmlTestExecutionResult implements TestExecutionResult {
 
@@ -88,7 +89,7 @@ class HtmlTestExecutionResult implements TestExecutionResult {
         }
 
         List<String> getFailureMessages(String testmethod) {
-            html.select("div.test:has(a[name=$testmethod]) > span > pre").collect { it.text().readLines().first() }
+            html.select("div.test:has(a[name=$testmethod]) > span > pre").collect { it.text() }
         }
 
         TestClassExecutionResult assertTestsExecuted(String... testNames) {
@@ -114,7 +115,7 @@ class HtmlTestExecutionResult implements TestExecutionResult {
 
         TestClassExecutionResult assertTestFailed(String name, Matcher<? super String>... messageMatchers) {
             assert testsFailures.containsKey(name)
-            def messages = testsFailures[name]
+            def messages = testsFailures[name].collect { it.readLines().first() }
             assert messages.size() == messageMatchers.length
             for (int i = 0; i < messageMatchers.length; i++) {
                 assert messageMatchers[i].matches(messages[i])
@@ -122,6 +123,17 @@ class HtmlTestExecutionResult implements TestExecutionResult {
             return this
         }
 
+        TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher) {
+            String failureMethodName = "execution failure"
+            assert testsFailures.containsKey(failureMethodName)
+
+            String causeLinePrefix = "Caused by: "
+            def cause = testsFailures[failureMethodName].first().readLines().find { it.startsWith causeLinePrefix }?.substring(causeLinePrefix.length())
+
+            Assert.assertThat(cause, causeMatcher)
+            this
+        }
+
         TestClassExecutionResult assertTestSkipped(String name) {
             assert testsSkipped.contains(name);
             return this
@@ -156,6 +168,5 @@ class HtmlTestExecutionResult implements TestExecutionResult {
         TestClassExecutionResult assertTestCaseStderr(String testCaseName, Matcher<? super String> matcher) {
             throw new UnsupportedOperationException()
         }
-
     }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
index 40a5943..6cbe72c 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
@@ -86,6 +86,19 @@ class JUnitTestClassExecutionResult implements TestClassExecutionResult {
         this
     }
 
+    TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher) {
+        Map<String, Node> testMethods = findTests()
+        String failureMethodName = "execution failure"
+        Assert.assertThat(testMethods.keySet(), Matchers.hasItem(failureMethodName))
+
+        String causeLinePrefix = "Caused by: "
+        def failures = testMethods[failureMethodName].failure
+        def cause = failures[0].text().readLines().find { it.startsWith causeLinePrefix }?.substring(causeLinePrefix.length())
+
+        Assert.assertThat(cause, causeMatcher)
+        this
+    }
+
     TestClassExecutionResult assertTestSkipped(String name) {
         throw new UnsupportedOperationException()
     }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitXmlTestExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitXmlTestExecutionResult.groovy
index aa92cda..fa8603d 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitXmlTestExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitXmlTestExecutionResult.groovy
@@ -21,20 +21,21 @@ import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 
 class JUnitXmlTestExecutionResult implements TestExecutionResult {
-    private final TestFile buildDir
+    private final TestFile testResultsDir
     private final TestResultOutputAssociation outputAssociation
 
-    def JUnitXmlTestExecutionResult(TestFile projectDir, String buildDirName = 'build') {
-        this(projectDir, TestResultOutputAssociation.WITH_SUITE, buildDirName)
+    def JUnitXmlTestExecutionResult(TestFile projectDir, String testResultsDir = 'build/test-results') {
+        this(projectDir, TestResultOutputAssociation.WITH_SUITE, testResultsDir)
     }
 
-    def JUnitXmlTestExecutionResult(TestFile projectDir, TestResultOutputAssociation outputAssociation, String buildDirName = 'build') {
+    def JUnitXmlTestExecutionResult(TestFile projectDir, TestResultOutputAssociation outputAssociation, String testResultsDir = 'build/test-results') {
         this.outputAssociation = outputAssociation
-        this.buildDir = projectDir.file(buildDirName)
+        this.testResultsDir = projectDir.file(testResultsDir)
     }
 
     boolean hasJUnitXmlResults() {
-        xmlResultsDir().list().length > 0
+        testResultsDir.assertIsDir()
+        testResultsDir.list().length > 0
     }
 
     TestExecutionResult assertTestClassesExecuted(String... testClasses) {
@@ -62,10 +63,10 @@ class JUnitXmlTestExecutionResult implements TestExecutionResult {
     }
 
     private def findClasses() {
-        xmlResultsDir().assertIsDir()
+        testResultsDir.assertIsDir()
 
         Map<String, File> classes = [:]
-        buildDir.file('test-results').eachFile { File file ->
+        testResultsDir.eachFile { File file ->
             def matcher = (file.name=~/TEST-(.+)\.xml/)
             if (matcher.matches()) {
                 classes[fromFileToTestClass(matcher.group(1))] = file
@@ -73,9 +74,5 @@ class JUnitXmlTestExecutionResult implements TestExecutionResult {
         }
         return classes
     }
-
-    private TestFile xmlResultsDir() {
-        buildDir.file('test-results')
-    }
 }
 
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
index 270ca53..2342d1f 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
@@ -63,4 +63,6 @@ public interface TestClassExecutionResult {
     TestClassExecutionResult assertStderr(Matcher<? super String> matcher);
 
     TestClassExecutionResult assertTestCaseStderr(String testCaseName, Matcher<? super String> matcher);
+
+    TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher);
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/UrlValidator.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/UrlValidator.groovy
new file mode 100644
index 0000000..954ecbc
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/UrlValidator.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures
+
+import org.gradle.internal.hash.HashUtil
+import org.gradle.util.TextUtil
+import org.junit.Assert
+
+class UrlValidator {
+
+    static void available(String theUrl, String application = null, int timeout = 30000) {
+        URL url = new URL(theUrl)
+        long expiry = System.currentTimeMillis() + timeout
+        while (System.currentTimeMillis() <= expiry) {
+            try {
+                url.text
+                return
+            } catch (IOException e) {
+                // continue
+            }
+            Thread.sleep(200)
+        }
+        throw new RuntimeException(String.format("Timeout waiting for %s to become available.", application != null ? application : theUrl));
+    }
+
+    static void notAvailable(String theUrl) {
+        try {
+            String content = new URL(theUrl).text
+            Assert.fail(String.format("Expected url '%s' to be unavailable instead we got:\n%s", theUrl, content));
+        } catch (SocketException ex) {
+        }
+    }
+
+    /**
+     * Asserts that the content at the specified url matches the content in the provided String
+     */
+    static void assertUrlContentContains(URL url, String contents) {
+        assert url.text.contains(contents)
+    }
+
+    /**
+     * Asserts that the content at the specified url matches the content in the provided String
+     */
+    static void assertUrlContent(URL url, String contents) {
+        assert TextUtil.normaliseLineSeparators(url.text) == TextUtil.normaliseLineSeparators(contents)
+    }
+
+    /**
+     * Asserts that the content at the specified url matches the content in the specified File
+     */
+    static void assertUrlContent(URL url, File file) {
+        assertUrlContent(url, file.text)
+    }
+
+    /**
+     * Asserts that the binary content at the specified url matches the content in the specified File
+     */
+    static void assertBinaryUrlContent(URL url, File file) {
+        assert compareHashes(url.openStream(), file.newInputStream())
+    }
+
+    private static boolean compareHashes(InputStream a, InputStream b) {
+        return HashUtil.createHash(a, "MD5").equals(HashUtil.createHash(b, "MD5"))
+    }
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/ArchiveTestFixture.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/ArchiveTestFixture.groovy
index c6b8b21..b10908d 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/ArchiveTestFixture.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/ArchiveTestFixture.groovy
@@ -32,8 +32,16 @@ class ArchiveTestFixture {
         filesByRelativePath.put(relativePath, content)
     }
 
-    def assertContainsFile(String relativePath, int occurrences = 1) {
-        assertEquals(occurrences, filesByRelativePath.get(relativePath).size())
+    def assertContainsFile(String relativePath) {
+        assert filesByRelativePath.keySet().contains(relativePath)
+        this
+    }
+
+    def assertContainsFile(String relativePath, int occurrences) {
+        assertContainsFile(relativePath)
+        def actualOccurrences = filesByRelativePath.get(relativePath).size()
+        def failureMessage = String.format("Incorrect count for file '%s': expected %s, got %s", relativePath, occurrences, actualOccurrences)
+        assertEquals(failureMessage, occurrences, actualOccurrences)
         this
     }
 
@@ -48,7 +56,7 @@ class ArchiveTestFixture {
     }
 
     def hasDescendants(String... relativePaths) {
-        assertThat(relativePaths as Set, equalTo(filesByRelativePath.keySet()))
+        assertThat(filesByRelativePath.keySet(), equalTo(relativePaths as Set))
         def expectedCounts = ArrayListMultimap.create()
         for (String fileName : relativePaths) {
             expectedCounts.put(fileName, fileName)
@@ -59,6 +67,13 @@ class ArchiveTestFixture {
         this
     }
 
+    def containsDescendants(String... relativePaths) {
+        for (String path : relativePaths) {
+            assertContainsFile(path)
+        }
+        this
+    }
+
     /**
      * Asserts that there is exactly one file present with the given path, and that this file has the given content.
      */
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/JarTestFixture.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/JarTestFixture.groovy
index 6864ee2..c423c48 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/JarTestFixture.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/archive/JarTestFixture.groovy
@@ -17,18 +17,24 @@
 package org.gradle.test.fixtures.archive
 
 import org.apache.commons.io.IOUtils
+import org.gradle.api.JavaVersion
+import org.gradle.test.fixtures.file.ClassFile
 
+import java.util.jar.JarEntry
 import java.util.jar.JarFile
 import java.util.zip.ZipEntry
 import java.util.zip.ZipInputStream
 
 class JarTestFixture extends ZipTestFixture {
+    final int classFileDescriptor = 0xCAFEBABE
+
     File file
 
     /**
      * Asserts that the Jar file is well-formed
      */
-     JarTestFixture(File file) {super(file)
+     JarTestFixture(File file) {
+         super(file)
          this.file = file
          isManifestPresentAndFirstEntry()
      }
@@ -59,4 +65,21 @@ class JarTestFixture extends ZipTestFixture {
             IOUtils.closeQuietly(zip)
         }
     }
+
+    @Override
+    def hasDescendants(String... relativePaths) {
+        String[] allDescendants = relativePaths + JarFile.MANIFEST_NAME
+        return super.hasDescendants(allDescendants)
+    }
+
+    def getJavaVersion() {
+        JarFile jarFile = new JarFile(file)
+        //take the first class file
+        JarEntry classEntry = jarFile.entries().find { entry -> entry.name.endsWith(".class") }
+        if (classEntry == null) {
+            throw new Exception("Could not find a class entry for: " + file)
+        }
+        ClassFile classFile = new ClassFile(jarFile.getInputStream(classEntry))
+        return JavaVersion.forClassVersion(classFile.classFileVersion)
+    }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
index 83cf08b..52a0e5d 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
@@ -29,4 +29,9 @@ class TestExecutorFactory implements ExecutorFactory {
     StoppableExecutor create(String displayName) {
         return new TestStoppableExecutor(executor)
     }
+
+    StoppableExecutor create(String displayName, int fixedSize) {
+        // Ignores size of thread pool
+        return new TestStoppableExecutor(executor)
+    }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestStoppableExecutor.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestStoppableExecutor.groovy
index 39e75ec..32909ae 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestStoppableExecutor.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestStoppableExecutor.groovy
@@ -18,12 +18,13 @@ package org.gradle.test.fixtures.concurrent
 
 import org.gradle.internal.concurrent.StoppableExecutor
 
+import java.util.concurrent.AbstractExecutorService
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.locks.Condition
 import java.util.concurrent.locks.Lock
 import java.util.concurrent.locks.ReentrantLock
 
-class TestStoppableExecutor implements StoppableExecutor {
+class TestStoppableExecutor extends AbstractExecutorService implements StoppableExecutor {
     private final Lock lock = new ReentrantLock()
     private final Condition condition = lock.newCondition()
     private int count
@@ -73,4 +74,24 @@ class TestStoppableExecutor implements StoppableExecutor {
     void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
         throw new UnsupportedOperationException()
     }
+
+    void shutdown() {
+        throw new UnsupportedOperationException()
+    }
+
+    List<Runnable> shutdownNow() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean isShutdown() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean isTerminated() {
+        throw new UnsupportedOperationException()
+    }
+
+    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        throw new UnsupportedOperationException()
+    }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/encoding/Identifier.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/encoding/Identifier.java
index a781534..4b62807 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/encoding/Identifier.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/encoding/Identifier.java
@@ -18,36 +18,26 @@ package org.gradle.test.fixtures.encoding;
 
 import org.gradle.internal.os.OperatingSystem;
 
+import java.util.Arrays;
+import java.util.List;
+
 public class Identifier {
     private static final String PUNCTUATION_CHARS = "-'!@#$%^&*()_+=,.?{}[]<>";
     private static final String NON_ASCII_CHARS = "-√æず∫ʙぴ₦ガき∆ç√∫";
+    private static final String NON_PRECOMPOSED_NON_ASCII = "-√æ∫ʙ₦∆√∫";
     private static final String FILESYSTEM_RESERVED_CHARS = "-./\\?%*:|\"<>";
     private static final String XML_MARKUP_CHARS = "-<with>some<xml-markup/></with>";
 
     private final String suffix;
+    private final String displayName;
 
-    public Identifier(String suffix) {
+    private Identifier(String suffix, String displayName) {
+        this.displayName = displayName;
         this.suffix = suffix == null ? "" : suffix;
     }
 
-    public Identifier withPunctuation() {
-        return new Identifier(suffix + PUNCTUATION_CHARS);
-    }
-
-    public Identifier withNonAscii() {
-        return new Identifier(suffix + NON_ASCII_CHARS);
-    }
-
-    public Identifier withReservedFileSystemChars() {
-        return new Identifier(suffix + FILESYSTEM_RESERVED_CHARS);
-    }
-
-    public Identifier withMarkup() {
-        return new Identifier(suffix + XML_MARKUP_CHARS);
-    }
-
-    public Identifier withWhiteSpace() {
-        return new Identifier(suffix + " with white space");
+    public Identifier safeForBranch() {
+        return without(getUnsupportedFileNameCharacters().replace("/", ""));
     }
 
     public Identifier safeForFileName() {
@@ -59,7 +49,7 @@ public class Identifier {
         for (char c : toRemove.toCharArray()) {
             newSuffix = newSuffix.replace(c, '-');
         }
-        return new Identifier(newSuffix);
+        return new Identifier(newSuffix, displayName);
     }
 
     private static String getUnsupportedFileNameCharacters() {
@@ -73,28 +63,40 @@ public class Identifier {
         return prefix + suffix;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
     @Override
     public String toString() {
-        return suffix;
+        return displayName;
+    }
+
+    public static List<Identifier> getAll() {
+        return Arrays.asList(getPunctuation(), getNonAscii(), getFileSystemReserved(), getXmlMarkup(), getWhiteSpace());
     }
 
     public static Identifier getPunctuation() {
-        return new Identifier("").withPunctuation();
+        return new Identifier(PUNCTUATION_CHARS, "punctuation");
     }
 
     public static Identifier getNonAscii() {
-        return new Identifier("").withNonAscii();
+        if (OperatingSystem.current().isMacOsX()) {
+            // The hfs+ file system stores file names in decomposed form. Don't use precomposed characters on OS X, as way too few things normalise text correctly
+            return new Identifier(NON_PRECOMPOSED_NON_ASCII, "non-ascii");
+        }
+        return new Identifier(NON_ASCII_CHARS, "non-ascii");
     }
 
     public static Identifier getFileSystemReserved() {
-        return new Identifier("").withReservedFileSystemChars();
+        return new Identifier(FILESYSTEM_RESERVED_CHARS, "filesystem");
     }
 
     public static Identifier getXmlMarkup() {
-        return new Identifier("").withMarkup();
+        return new Identifier(XML_MARKUP_CHARS, "xml markup");
     }
 
     public static Identifier getWhiteSpace() {
-        return new Identifier("").withWhiteSpace();
+        return new Identifier(" with white space", "whitespace");
     }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/AbstractTestDirectoryProvider.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/AbstractTestDirectoryProvider.java
new file mode 100644
index 0000000..94b55fc
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/AbstractTestDirectoryProvider.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.file;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.rules.MethodRule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.util.Random;
+
+
+/**
+ * A JUnit rule which provides a unique temporary folder for the test.
+ */
+abstract class AbstractTestDirectoryProvider implements MethodRule, TestRule, TestDirectoryProvider {
+    private TestFile dir;
+    private String prefix;
+    protected static TestFile root;
+    private static final Random RANDOM = new Random();
+    public static final int ALL_DIGITS_AND_LETTERS_RADIX = 36;
+    private static final int MAX_RANDOM_PART_VALUE = Integer.valueOf("zzzzz", ALL_DIGITS_AND_LETTERS_RADIX);
+
+    private String determinePrefix() {
+        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
+        for (StackTraceElement element : stackTrace) {
+            if (element.getClassName().endsWith("Test") || element.getClassName().endsWith("Spec")) {
+                return StringUtils.substringAfterLast(element.getClassName(), ".") + "/unknown-test";
+            }
+        }
+        return "unknown-test-class";
+    }
+
+    protected Statement doApply(final Statement base, FrameworkMethod method, Object target) {
+        init(method.getName(), target.getClass().getSimpleName());
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                getTestDirectory().maybeDeleteDir();
+                // Don't delete on failure
+            }
+        };
+    }
+
+    public Statement apply(final Statement base, Description description) {
+        init(description.getMethodName(), description.getTestClass().getSimpleName());
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                getTestDirectory().deleteDir();
+                // Don't delete on failure
+            }
+        };
+    }
+
+    protected void init(String methodName, String className) {
+        if (methodName == null) {
+            // must be a @ClassRule; use the rule's class name instead
+            methodName = getClass().getSimpleName();
+        }
+        if (prefix == null) {
+            String safeMethodName = methodName.replaceAll("\\s", "_").replace(File.pathSeparator, "_").replace(":", "_").replace('"', '_');
+            if (safeMethodName.length() > 60) {
+                safeMethodName = safeMethodName.substring(0, 29) + "..." + safeMethodName.substring(safeMethodName.length() - 29);
+            }
+            prefix = String.format("%s/%s", className, safeMethodName);
+        }
+    }
+
+    public TestFile getTestDirectory() {
+        if (dir == null) {
+            if (prefix == null) {
+                // This can happen if this is used in a constructor or a @Before method. It also happens when using
+                // @RunWith(SomeRunner) when the runner does not support rules.
+                prefix = determinePrefix();
+            }
+            while (true) {
+                // Use a random prefix to avoid reusing test directories
+                dir = root.file(prefix, Integer.toString(RANDOM.nextInt(MAX_RANDOM_PART_VALUE), ALL_DIGITS_AND_LETTERS_RADIX));
+                if (dir.mkdirs()) {
+                    break;
+                }
+            }
+        }
+        return dir;
+    }
+
+    public TestFile file(Object... path) {
+        return getTestDirectory().file((Object[]) path);
+    }
+
+    public TestFile createFile(Object... path) {
+        return file((Object[]) path).createFile();
+    }
+
+    public TestFile createDir(Object... path) {
+        return file((Object[]) path).createDir();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/ClassFile.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/ClassFile.groovy
new file mode 100644
index 0000000..78ef1f6
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/ClassFile.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.file
+
+import org.objectweb.asm.*
+
+class ClassFile {
+    boolean hasSourceFile
+    boolean hasLineNumbers
+    boolean hasLocalVars
+    int classFileVersion
+
+    ClassFile(File file) {
+        this(file.newInputStream())
+    }
+
+    ClassFile(InputStream inputStream) {
+        def methodVisitor = new MethodVisitor(Opcodes.ASM5) {
+            @Override
+            void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+                hasLocalVars = true
+            }
+
+            @Override
+            void visitLineNumber(int line, Label start) {
+                hasLineNumbers = true
+            }
+        }
+        def visitor = new ClassVisitor(Opcodes.ASM5) {
+            @Override
+            void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+                classFileVersion = version
+            }
+
+            @Override
+            MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+                return methodVisitor
+            }
+
+            @Override
+            void visitSource(String source, String debug) {
+                hasSourceFile = true
+            }
+        }
+        new ClassReader(inputStream).accept(visitor, 0)
+    }
+
+    boolean getDebugIncludesSourceFile() {
+        return hasSourceFile
+    }
+
+    boolean getDebugIncludesLineNumbers() {
+        return hasLineNumbers
+    }
+
+    boolean getDebugIncludesLocalVariables() {
+        return hasLocalVars
+    }
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestDistributionDirectoryProvider.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestDistributionDirectoryProvider.java
new file mode 100644
index 0000000..49f4d68
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestDistributionDirectoryProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.file;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+
+public class TestDistributionDirectoryProvider extends AbstractTestDirectoryProvider {
+    static {
+        // NOTE: the space in the directory name is intentional
+        root = new TestFile(new File("build/tmp/test distros"));
+    }
+
+    public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
+        return doApply(base, method, target);
+    }
+
+    public static TestDistributionDirectoryProvider newInstance() {
+        return new TestDistributionDirectoryProvider();
+    }
+
+    public static TestDistributionDirectoryProvider newInstance(FrameworkMethod method, Object target) {
+        TestDistributionDirectoryProvider testDirectoryProvider = new TestDistributionDirectoryProvider();
+        testDirectoryProvider.init(method.getName(), target.getClass().getSimpleName());
+        return testDirectoryProvider;
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestFile.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestFile.java
index a144917..fe53c6f 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestFile.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestFile.java
@@ -17,6 +17,7 @@
 package org.gradle.test.fixtures.file;
 
 import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
 import org.apache.commons.io.FileUtils;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.Task;
@@ -24,8 +25,8 @@ import org.apache.tools.ant.taskdefs.Tar;
 import org.apache.tools.ant.taskdefs.Zip;
 import org.apache.tools.ant.types.EnumeratedAttribute;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.gradle.internal.nativeplatform.filesystem.*;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.hamcrest.Matcher;
 
 import java.io.*;
@@ -252,10 +253,10 @@ public class TestFile extends File {
             throw new RuntimeException(e);
         }
     }
-    
+
     public void moveToDirectory(File target) {
         if (target.exists() && !target.isDirectory()) {
-                throw new RuntimeException(String.format("Target '%s' is not a directory", target));
+            throw new RuntimeException(String.format("Target '%s' is not a directory", target));
         }
         try {
             FileUtils.moveFileToDirectory(this, target, true);
@@ -285,7 +286,7 @@ public class TestFile extends File {
      * }
      * </pre>
      */
-    public TestFile create(Closure structure) {
+    public TestFile create(@DelegatesTo(TestWorkspaceBuilder.class) Closure structure) {
         assertTrue(isDirectory() || mkdirs());
         new TestWorkspaceBuilder(this).apply(structure);
         return this;
@@ -342,6 +343,13 @@ public class TestFile extends File {
         return this;
     }
 
+    public TestFile assertIsDifferentFrom(TestFile other) {
+        assertIsFile();
+        other.assertIsFile();
+        assertHasChangedSince(other.snapshot());
+        return this;
+    }
+
     private byte[] getHash(String algorithm) {
         try {
             MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
@@ -357,11 +365,7 @@ public class TestFile extends File {
     }
 
     public void createLink(String target) {
-        try {
-            NativeServices.getInstance().get(FileSystem.class).createSymbolicLink(this, new File(target));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        NativeServices.getInstance().get(FileSystem.class).createSymbolicLink(this, new File(target));
     }
 
     public String readLink() {
@@ -410,6 +414,24 @@ public class TestFile extends File {
         return this;
     }
 
+    /**
+     * Asserts that this file contains the given set of descendants (and possibly other files).
+     */
+    public TestFile assertContainsDescendants(String... descendants) {
+        assertIsDir();
+        Set<String> actual = new TreeSet<String>();
+        visit(actual, "", this);
+
+        Set<String> expected = new TreeSet<String>(Arrays.asList(descendants));
+
+        Set<String> missing = new TreeSet<String>(expected);
+        missing.removeAll(actual);
+
+        assertTrue(String.format("For dir: %s, missing files: %s, expected: %s, actual: %s", this, missing, expected, actual), missing.isEmpty());
+
+        return this;
+    }
+
     public TestFile assertIsEmptyDir() {
         if (exists()) {
             assertIsDir();
@@ -493,7 +515,7 @@ public class TestFile extends File {
         return zipFile;
     }
 
-    public TestFile zipTo(TestFile zipFile){
+    public TestFile zipTo(TestFile zipFile) {
         new TestFileHelper(this).zipTo(zipFile, useNativeTools);
         return this;
     }
@@ -566,7 +588,7 @@ public class TestFile extends File {
             throw new RuntimeException(e);
         }
     }
-    
+
     public ExecOutput exec(Object... args) {
         return new TestFileHelper(this).execute(Arrays.asList(args), null);
     }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestNameTestDirectoryProvider.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestNameTestDirectoryProvider.java
index 0209421..1db5ce2 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestNameTestDirectoryProvider.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestNameTestDirectoryProvider.java
@@ -15,76 +15,22 @@
  */
 package org.gradle.test.fixtures.file;
 
-import org.apache.commons.lang.StringUtils;
-import org.junit.rules.MethodRule;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.Statement;
 
 import java.io.File;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * A JUnit rule which provides a unique temporary folder for the test.
  */
-public class TestNameTestDirectoryProvider implements MethodRule, TestRule, TestDirectoryProvider {
-    private TestFile dir;
-    private String prefix;
-    private static TestFile root;
-    private static AtomicInteger testCounter = new AtomicInteger(1);
-
+public class TestNameTestDirectoryProvider extends AbstractTestDirectoryProvider {
     static {
         // NOTE: the space in the directory name is intentional
         root = new TestFile(new File("build/tmp/test files"));
     }
 
-    private String determinePrefix() {
-        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
-        for (StackTraceElement element : stackTrace) {
-            if (element.getClassName().endsWith("Test") || element.getClassName().endsWith("Spec")) {
-                return StringUtils.substringAfterLast(element.getClassName(), ".") + "/unknown-test-" + testCounter.getAndIncrement();
-            }
-        }
-        return "unknown-test-class-" + testCounter.getAndIncrement();
-    }
-
     public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
-        init(method.getName(), target.getClass().getSimpleName());
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                base.evaluate();
-                getTestDirectory().maybeDeleteDir();
-                // Don't delete on failure
-            }
-        };
-    }
-
-    public Statement apply(final Statement base, Description description) {
-        init(description.getMethodName(), description.getTestClass().getSimpleName());
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                base.evaluate();
-                getTestDirectory().maybeDeleteDir();
-                // Don't delete on failure
-            }
-        };
-    }
-
-    private void init(String methodName, String className) {
-        if (methodName == null) {
-            // must be a @ClassRule; use the rule's class name instead
-            methodName = getClass().getSimpleName();
-        }
-        if (prefix == null) {
-            String safeMethodName = methodName.replaceAll("\\s", "_").replace(File.pathSeparator, "_").replace(":", "_");
-            if (safeMethodName.length() > 64) {
-                safeMethodName = safeMethodName.substring(0, 32) + "..." + safeMethodName.substring(safeMethodName.length() - 32);
-            }
-            prefix = String.format("%s/%s", className, safeMethodName);
-        }
+        return doApply(base, method, target);
     }
 
     public static TestNameTestDirectoryProvider newInstance() {
@@ -96,33 +42,4 @@ public class TestNameTestDirectoryProvider implements MethodRule, TestRule, Test
         testDirectoryProvider.init(method.getName(), target.getClass().getSimpleName());
         return testDirectoryProvider;
     }
-
-    public TestFile getTestDirectory() {
-        if (dir == null) {
-            if (prefix == null) {
-                // This can happen if this is used in a constructor or a @Before method. It also happens when using
-                // @RunWith(SomeRunner) when the runner does not support rules.
-                prefix = determinePrefix();
-            }
-            for (int counter = 1; true; counter++) {
-                dir = root.file(counter == 1 ? prefix : String.format("%s%d", prefix, counter));
-                if (dir.mkdirs()) {
-                    break;
-                }
-            }
-        }
-        return dir;
-    }
-
-    public TestFile file(Object... path) {
-        return getTestDirectory().file((Object[]) path);
-    }
-
-    public TestFile createFile(Object... path) {
-        return file((Object[]) path).createFile();
-    }
-
-    public TestFile createDir(Object... path) {
-        return file((Object[]) path).createDir();
-    }
 }
\ No newline at end of file
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestWorkspaceBuilder.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestWorkspaceBuilder.groovy
index d97b03f..5a71975 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestWorkspaceBuilder.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/file/TestWorkspaceBuilder.groovy
@@ -35,7 +35,7 @@ class TestWorkspaceBuilder {
 
     def file(String name) {
         TestFile file = baseDir.file(name)
-        file.write('some content')
+        file.createFile()
         file
     }
 
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/testfixtures/internal/NativeServicesTestFixture.java b/subprojects/internal-testing/src/main/groovy/org/gradle/testfixtures/internal/NativeServicesTestFixture.java
new file mode 100644
index 0000000..2a7a97c
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/testfixtures/internal/NativeServicesTestFixture.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.services.NativeServices;
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
+
+import java.io.File;
+
+public class NativeServicesTestFixture {
+    static NativeServices nativeServices;
+    static boolean initialized;
+
+    public static void initialize(File gradleUserHomeDir) {
+        if (!initialized) {
+            NativeServices.initialize(gradleUserHomeDir);
+        }
+    }
+
+    public static void initialize() {
+        if (!initialized) {
+            File nativeDir = TestNameTestDirectoryProvider.newInstance().getTestDirectory();
+            NativeServices.initialize(nativeDir);
+        }
+    }
+
+    public static NativeServices getInstance() {
+        if (nativeServices == null) {
+            initialize();
+            nativeServices = NativeServices.getInstance();
+        }
+        return nativeServices;
+    }
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Assertions.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Assertions.groovy
deleted file mode 100644
index c27fa81..0000000
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Assertions.groovy
+++ /dev/null
@@ -1,42 +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;
-
-class Assertions {
-
-    private final Object source
-
-    static Assertions assertThat(Object source) {
-        new Assertions(source)
-    }
-
-    Assertions(Object source) {
-        this.source = source
-    }
-
-    Assertions doesNotShareStateWith(Object target) {
-        //this is really basic - does not work well with primitives, immutables like String, etc
-        //grow it if needed
-        source.getClass().getDeclaredFields().each {
-            if (!it.name.startsWith('$') && it.name != 'metaClass') { //filter out groovy stuff
-                it.setAccessible(true)
-                assert !it.get(source).is(it.get(target)) : "field value '$it.name' should not be shared between the instances."
-            }
-        }
-        this
-    }
-}
\ No newline at end of file
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Matchers.java b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Matchers.java
index 45d54f9..8695c8a 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Matchers.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Matchers.java
@@ -16,6 +16,7 @@
 
 package org.gradle.util;
 
+import org.gradle.internal.SystemProperties;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Factory;
@@ -265,6 +266,21 @@ public class Matchers {
         };
     }
 
+    @Factory
+    public static Matcher<String> normalizedLineSeparators(final Matcher<String> matcher) {
+        return new BaseMatcher<String>() {
+            public boolean matches(Object o) {
+                String string = (String) o;
+                return matcher.matches(string.replace(SystemProperties.getInstance().getLineSeparator(), "\n"));
+            }
+
+            public void describeTo(Description description) {
+                matcher.describeTo(description);
+                description.appendText(" (normalize line separators)");
+            }
+        };
+    }
+
     /**
      * Returns a placeholder for a mock method parameter.
      */
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.groovy
index 35d7f36..9c9045d 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.groovy
@@ -16,6 +16,7 @@
 package org.gradle.util
 
 import java.lang.annotation.ElementType
+import java.lang.annotation.Inherited
 import java.lang.annotation.Target
 import java.lang.annotation.RetentionPolicy
 import java.lang.annotation.Retention
@@ -24,8 +25,8 @@ import org.spockframework.runtime.extension.ExtensionAnnotation
 
 @Retention(RetentionPolicy.RUNTIME)
 @Target([ElementType.METHOD, ElementType.TYPE])
+ at Inherited
 @ExtensionAnnotation(TestPreconditionExtension.class)
 public @interface Requires {
     TestPrecondition[] value()
 }
-
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy
old mode 100755
new mode 100644
index 98367f6..c133643
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy
@@ -76,33 +76,45 @@ enum TestPrecondition {
     UNIX({
         OperatingSystem.current().unix
     }),
+    UNIX_DERIVATIVE({
+        MAC_OS_X.fulfilled || LINUX.fulfilled || UNIX.fulfilled
+    }),
     UNKNOWN_OS({
         OperatingSystem.current().name == "unknown operating system"
     }),
     NOT_UNKNOWN_OS({
         !UNKNOWN_OS.fulfilled
     }),
-    JDK5({
-        JavaVersion.current().java5
+    JDK6({
+        JavaVersion.current() == JavaVersion.VERSION_1_6
     }),
     JDK6_OR_LATER({
         JavaVersion.current() >= JavaVersion.VERSION_1_6
     }),
-    JDK6_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_1_6
-    }),
     JDK7_OR_LATER({
         JavaVersion.current() >= JavaVersion.VERSION_1_7
     }),
+    JDK7_OR_EARLIER({
+        JavaVersion.current() <= JavaVersion.VERSION_1_7
+    }),
+    JDK8_OR_LATER({
+        JavaVersion.current() >= JavaVersion.VERSION_1_8
+    }),
+    JDK8_OR_EARLIER({
+        JavaVersion.current() <= JavaVersion.VERSION_1_8
+    }),
     JDK7_POSIX({
         JDK7_OR_LATER.fulfilled && NOT_WINDOWS.fulfilled
     }),
     NOT_JDK_IBM({
         System.getProperty('java.vm.vendor') != 'IBM Corporation'
     }),
+    JDK_ORACLE({
+        System.getProperty('java.vm.vendor') == 'Oracle Corporation'
+    }),
     ONLINE({
         try {
-            new URL("http://google.com").openConnection().openStream()
+            new URL("http://google.com").openConnection().getInputStream().close()
             true
         } catch (IOException) {
             false
@@ -110,6 +122,10 @@ enum TestPrecondition {
     }),
     CAN_INSTALL_EXECUTABLE({
         FILE_PERMISSIONS.fulfilled || WINDOWS.fulfilled
+    }),
+    // TODO:DAZ Should be detecting this based on tool chain, not OS
+    OBJECTIVE_C_SUPPORT({
+        NOT_WINDOWS.fulfilled && NOT_UNKNOWN_OS.fulfilled
     });
 
     /**
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy
new file mode 100644
index 0000000..cd7adc0
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spockframework.runtime.extension.ExtensionAnnotation
+
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target;
+
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target([ElementType.TYPE])
+ at ExtensionAnnotation(UsesNativeServicesExtension)
+ at interface UsesNativeServices {
+}
\ No newline at end of file
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServicesExtension.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServicesExtension.groovy
new file mode 100644
index 0000000..4f8adfe
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServicesExtension.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.testfixtures.internal.NativeServicesTestFixture
+import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
+import org.spockframework.runtime.extension.IMethodInterceptor
+import org.spockframework.runtime.extension.IMethodInvocation
+import org.spockframework.runtime.model.SpecInfo
+
+class UsesNativeServicesExtension extends AbstractAnnotationDrivenExtension<UsesNativeServices> {
+    @Override
+    void visitSpecAnnotation(UsesNativeServices annotation, SpecInfo spec) {
+        spec.addInterceptor(new NativeServicesInitializationInterceptor())
+    }
+
+    private class NativeServicesInitializationInterceptor implements IMethodInterceptor {
+        @Override
+        void intercept(IMethodInvocation invocation) throws Throwable {
+            NativeServicesTestFixture.initialize()
+            invocation.proceed()
+        }
+    }
+}
diff --git a/subprojects/internal-testing/src/test/groovy/org/gradle/util/AssertionsTest.groovy b/subprojects/internal-testing/src/test/groovy/org/gradle/util/AssertionsTest.groovy
deleted file mode 100644
index 99aa43c..0000000
--- a/subprojects/internal-testing/src/test/groovy/org/gradle/util/AssertionsTest.groovy
+++ /dev/null
@@ -1,58 +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 spock.lang.Specification
-
-import static org.gradle.util.Assertions.assertThat
-
-class AssertionsTest extends Specification {
-
-    static class Foo {
-        List listField;
-        Foo fooField;
-    }
-
-    def "knows if instances share state"() {
-        given:
-        def foo = new Foo()
-        def list = [1]
-
-        and:
-        def root = new Foo(listField: list, fooField: foo)
-        def equal = new Foo(listField: [1], fooField: new Foo())
-
-        and:
-        def sameList = new Foo(listField: list, fooField: new Foo())
-        def sameFoo = new Foo(listField: [1], fooField: foo)
-
-        expect:
-        assertThat(root).doesNotShareStateWith(equal)
-
-        when:
-        assertThat(root).doesNotShareStateWith(sameList)
-        then:
-        def ex = thrown(AssertionError)
-        ex.message.contains('listField')
-
-        when:
-        assertThat(root).doesNotShareStateWith(sameFoo)
-        then:
-        def ex2 = thrown(AssertionError)
-        ex2.message.contains('fooField')
-    }
-}
diff --git a/subprojects/ivy/ivy.gradle b/subprojects/ivy/ivy.gradle
index 7edbb63..6e70135 100644
--- a/subprojects/ivy/ivy.gradle
+++ b/subprojects/ivy/ivy.gradle
@@ -20,11 +20,15 @@ dependencies {
     compile project(':core')
     compile project(':publish')
     compile project(':plugins') // for base plugin to get archives conf
-    compile project(':coreImpl')
+    compile project(':dependencyManagement')
 
     testCompile libraries.groovy
     integTestCompile project(":ear")
+    integTestRuntime project(":resourcesS3")
+    integTestRuntime project(":resourcesSftp")
+    testFixturesCompile project(":internalIntegTesting")
 }
 
 useTestFixtures()
+useTestFixtures(project: ":modelCore")
 useClassycle()
\ No newline at end of file
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.groovy
deleted file mode 100644
index b55f53b..0000000
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.groovy
+++ /dev/null
@@ -1,67 +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.publish.ivy
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.ivy.IvyFileModule
-
-class AbstractIvyPublishIntegTest extends AbstractIntegrationSpec {
-
-    protected def resolveArtifacts(IvyFileModule module) {
-        doResolveArtifacts("group: '${sq(module.organisation)}', name: '${sq(module.module)}', version: '${sq(module.revision)}'")
-    }
-
-    protected def resolveArtifacts(IvyFileModule module, def configuration) {
-        doResolveArtifacts("group: '${sq(module.organisation)}', name: '${sq(module.module)}', version: '${sq(module.revision)}', configuration: '${sq(configuration)}'")
-    }
-
-    private def doResolveArtifacts(def dependency) {
-        // Replace the existing buildfile with one for resolving the published module
-        // TODO:DAZ Use a separate directory for resolving
-        settingsFile.text = "rootProject.name = 'resolve'"
-        buildFile.text = """
-            configurations {
-                resolve
-            }
-            repositories {
-                ivy { url "${ivyRepo.uri}" }
-                mavenCentral()
-            }
-            dependencies {
-                resolve $dependency
-            }
-
-            task resolveArtifacts(type: Sync) {
-                from configurations.resolve
-                into "artifacts"
-            }
-
-"""
-
-        run "resolveArtifacts"
-        def artifactsList = file("artifacts").exists() ? file("artifacts").list() : []
-        return artifactsList.sort()
-    }
-
-    String sq(String input) {
-        return escapeForSingleQuoting(input)
-    }
-
-    String escapeForSingleQuoting(String input) {
-        return input.replace('\\', '\\\\').replace('\'', '\\\'')
-    }
-}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyHttpsLegacyPublishIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyHttpsLegacyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..bd4b830
--- /dev/null
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyHttpsLegacyPublishIntegrationTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy
+
+import org.gradle.integtests.fixtures.executer.ExecutionFailure
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.keystore.TestKeyStore
+import org.gradle.test.fixtures.server.http.RepositoryHttpServer
+import org.junit.Rule
+
+class IvyHttpsLegacyPublishIntegrationTest extends AbstractIvyRemoteLegacyPublishIntegrationTest {
+    TestKeyStore keyStore
+    @Rule RepositoryHttpServer server = new RepositoryHttpServer(temporaryFolder)
+
+    def setup() {
+        keyStore = TestKeyStore.init(file("keystore"))
+        keyStore.enableSslWithServerCert(server)
+    }
+
+    @Override
+    protected ExecutionResult run(String... tasks) {
+        keyStore.configureServerCert(executer)
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        return super.run(tasks)
+    }
+
+    @Override
+    protected ExecutionFailure fails(String... tasks) {
+        keyStore.configureServerCert(executer)
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        return super.fails(tasks)
+    }
+}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishArtifactCustomizationIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishArtifactCustomizationIntegTest.groovy
index d8e7830..9750c42 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishArtifactCustomizationIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishArtifactCustomizationIntegTest.groovy
@@ -289,6 +289,48 @@ class IvyPublishArtifactCustomizationIntegTest extends AbstractIvyPublishIntegTe
         failure.assertHasCause("Invalid publication 'ivy': artifact file is a directory")
     }
 
+    def "cannot publish when artifact does not exist"() {
+        given:
+        createBuildScripts("""
+            publications {
+                ivy(IvyPublication) {
+                    artifact source: "no-exist", type: "jar"
+                }
+            }
+""")
+        when:
+        fails 'publish'
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':publishIvyPublicationToIvyRepository'.")
+        failure.assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+        failure.assertHasCause("Invalid publication 'ivy': artifact file does not exist: '${file('no-exist')}'")
+    }
+
+    def "reports failure to convert artifact notation"() {
+        given:
+        file("a-directory.dir").createDir()
+
+        createBuildScripts("""
+            publications {
+                ivy(IvyPublication) {
+                    artifact 12
+                }
+            }
+""")
+        when:
+        fails 'publish'
+
+        then:
+        failure.assertHasCause("""Cannot convert the provided notation to an object of type IvyArtifact: 12.
+The following types/formats are supported:
+  - Instances of IvyArtifact.
+  - Instances of AbstractArchiveTask.
+  - Instances of PublishArtifact.
+  - Maps containing a 'source' entry, for example [source: '/path/to/file', extension: 'zip'].
+  - Anything that can be converted to a file, as per Project.file()""")
+    }
+
     private createBuildScripts(def publications, def append = "") {
         file("customFile.txt") << "some content"
         settingsFile << "rootProject.name = 'ivyPublish'"
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishBasicIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishBasicIntegTest.groovy
index ac662fd..67a234c 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishBasicIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishBasicIntegTest.groovy
@@ -149,7 +149,7 @@ public class IvyPublishBasicIntegTest extends AbstractIvyPublishIntegTest {
         fails 'publish'
 
         then:
-        failure.assertHasDescription("A problem occurred configuring root project 'bad-project'.")
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin\$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)")
         failure.assertHasCause("Ivy publication 'ivy' cannot include multiple components")
     }
 
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishCrossVersionIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishCrossVersionIntegrationTest.groovy
index 1a2c2cd..6042b46 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishCrossVersionIntegrationTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishCrossVersionIntegrationTest.groovy
@@ -26,6 +26,10 @@ class IvyPublishCrossVersionIntegrationTest extends CrossVersionIntegrationSpec
     final TestFile repoDir = file("ivy-repo")
     final IvyFileRepository repo = new IvyFileRepository(repoDir)
 
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
     def "ivy java publication generated by ivy-publish plugin can be consumed by previous versions of Gradle"() {
         given:
         projectPublishedUsingMavenPublishPlugin('java')
@@ -82,33 +86,24 @@ publishing {
     def consumePublicationWithPreviousVersion() {
         settingsFile.text = "rootProject.name = 'consumer'"
 
-        def repositoryDefinition
-        if (previous.fullySupportsIvyRepository) {
-            repositoryDefinition = """
-                ivy {
-                    url "${repo.uri}"
-                }
-"""
-        } else {
-            def repoPath = TextUtil.normaliseFileSeparators(repoDir.absolutePath)
-            repositoryDefinition = """
-                println "Adding resolver directly due to no 'ivy' repository support"
-                add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
-                    name = 'repo'
-                    addIvyPattern("${repoPath}/[organisation]/[module]/[revision]/ivy-[revision].xml")
-                    addArtifactPattern("${repoPath}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]")
-                    descriptor = 'required'
-                    checkmodified = true
-                }
-"""
-        }
+        def repoPath = TextUtil.normaliseFileSeparators(repoDir.absolutePath)
 
         buildFile.text = """
 configurations {
     lib
 }
 repositories {
-    $repositoryDefinition
+    if (${previous.fullySupportsIvyRepository}) {
+        ivy { url "${repo.uri}" }
+    } else {
+        add(Class.forName('org.apache.ivy.plugins.resolver.FileSystemResolver').newInstance()) {
+            name = 'repo'
+            addIvyPattern("${repoPath}/[organisation]/[module]/[revision]/ivy-[revision].xml")
+            addArtifactPattern("${repoPath}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]")
+            descriptor = 'required'
+            checkmodified = true
+        }
+    }
 }
 dependencies {
     lib 'org.gradle.crossversion:published:1.9'
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishDescriptorCustomizationIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishDescriptorCustomizationIntegTest.groovy
index 53c8957..15f3121 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishDescriptorCustomizationIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishDescriptorCustomizationIntegTest.groovy
@@ -16,10 +16,14 @@
 
 package org.gradle.api.publish.ivy
 
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+import spock.lang.Unroll
+
+import javax.xml.namespace.QName
 import org.gradle.test.fixtures.ivy.IvyDescriptor
 
-class IvyPublishDescriptorCustomizationIntegTest extends AbstractIntegrationSpec {
+class IvyPublishDescriptorCustomizationIntegTest extends AbstractIvyPublishIntegTest {
 
     def module = ivyRepo.module("org.gradle", "publish", "2")
 
@@ -48,6 +52,7 @@ class IvyPublishDescriptorCustomizationIntegTest extends AbstractIntegrationSpec
         """
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "can customize descriptor xml during publication"() {
         when:
         succeeds 'publish'
@@ -65,8 +70,11 @@ class IvyPublishDescriptorCustomizationIntegTest extends AbstractIntegrationSpec
                     ivy {
                         descriptor {
                             status "custom-status"
+                            branch "custom-branch"
+                            extraInfo 'http://my.extra.info1', 'foo', 'fooValue'
+                            extraInfo 'http://my.extra.info2', 'bar', 'barValue'
                             withXml {
-                                asNode().info[0].appendNode('description', 'Customized descriptor')
+                                asNode().info[0]. at resolver = 'test'
                             }
                         }
                     }
@@ -80,8 +88,12 @@ class IvyPublishDescriptorCustomizationIntegTest extends AbstractIntegrationSpec
         ":jar" in skippedTasks
 
         and:
-        module.parsedIvy.description == "Customized descriptor"
+        module.parsedIvy.resolver == "test"
         module.parsedIvy.status == "custom-status"
+        module.parsedIvy.branch == "custom-branch"
+        module.parsedIvy.extraInfo.size() == 2
+        module.parsedIvy.extraInfo[new QName('http://my.extra.info1', 'foo')] == 'fooValue'
+        module.parsedIvy.extraInfo[new QName('http://my.extra.info2', 'bar')] == 'barValue'
     }
 
     def "can generate ivy.xml without publishing"() {
@@ -148,4 +160,63 @@ class IvyPublishDescriptorCustomizationIntegTest extends AbstractIntegrationSpec
         failure.assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
         failure.assertHasCause("Invalid publication 'ivy': supplied revision does not match ivy descriptor (cannot edit revision directly in the ivy descriptor file).")
     }
+
+    @Unroll
+    def "produces sensible error with invalid extra info elements" () {
+        buildFile << """
+            publishing {
+                publications {
+                    ivy {
+                        descriptor {
+                            extraInfo 'http://my.extra.info', '${sq(name)}', 'fooValue'
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        fails 'publish'
+
+        then:
+        failure.assertHasDescription("A problem occurred configuring root project 'publish'.")
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin\$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)")
+        failure.assertHasCause("Invalid ivy extra info element name: '${name}'")
+
+        where:
+        name        | _
+        ''          | _
+        'foo\\n'    | _
+        'foo<'      | _
+        '1foo'      | _
+     }
+
+    @Unroll
+    def "produces sensible error with extra info containing null values" () {
+        buildFile << """
+            publishing {
+                publications {
+                    ivy {
+                        descriptor {
+                            extraInfo ${namespace}, ${name}, 'fooValue'
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        fails 'publish'
+
+        then:
+        failure.assertHasDescription("A problem occurred configuring root project 'publish'.")
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertHasLineNumber(23)
+        failure.assertHasCause("Cannot add an extra info element with null ")
+
+        where:
+        namespace                | name
+        null                     | "'foo'"
+        "'http://my.extra.info'" | null
+    }
 }
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpIntegTest.groovy
index 4d2bc24..fa252db 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpIntegTest.groovy
@@ -15,14 +15,12 @@
  */
 
 package org.gradle.api.publish.ivy
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
 import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
 import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
 import org.gradle.util.GradleVersion
 import org.hamcrest.Matchers
 import org.junit.Rule
@@ -31,7 +29,7 @@ import spock.lang.Unroll
 
 import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
 
-public class IvyPublishHttpIntegTest extends AbstractIntegrationSpec {
+public class IvyPublishHttpIntegTest extends AbstractIvyPublishIntegTest {
     private static final String BAD_CREDENTIALS = '''
 credentials {
     username 'testuser'
@@ -326,6 +324,7 @@ credentials {
         run 'publish'
 
         then:
+        module.assertIvyAndJarFilePublished()
         module.ivyFile.assertIsFile()
         module.jarFile.assertIsCopyOf(new TestFile(toolsJar))
     }
@@ -357,7 +356,7 @@ credentials {
         """
 
         and:
-        module.jar.expectPut(HttpStatus.ORDINAL_500_Internal_Server_Error)
+        module.jar.expectPutBroken()
 
         when:
         fails ':publish'
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpsIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpsIntegTest.groovy
new file mode 100644
index 0000000..3991579
--- /dev/null
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishHttpsIntegTest.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.api.publish.ivy
+import org.gradle.test.fixtures.keystore.TestKeyStore
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
+import org.junit.Rule
+
+class IvyPublishHttpsIntegTest extends AbstractIvyPublishIntegTest {
+    TestKeyStore keyStore
+
+    @Rule public final HttpServer server = new HttpServer()
+
+    IvyHttpRepository ivyRemoteRepo
+    IvyHttpModule module
+
+    def setup() {
+        keyStore = TestKeyStore.init(file("keystore"))
+        server.start()
+
+        ivyRemoteRepo = new IvyHttpRepository(server, "/repo", ivyRepo)
+        module = ivyRemoteRepo.module('org.gradle', 'publish', '2').allowAll()
+    }
+
+    def "publish with server certificate"() {
+        given:
+        keyStore.enableSslWithServerCert(server)
+        initBuild()
+
+        when:
+        expectPublication()
+        keyStore.configureServerCert(executer)
+        succeeds 'publish'
+
+        then:
+        verifyPublications()
+    }
+
+    def "publish with server and client certificate"() {
+        given:
+        keyStore.enableSslWithServerAndClientCerts(server)
+        initBuild()
+
+        when:
+        expectPublication()
+        keyStore.configureServerAndClientCerts(executer)
+        succeeds 'publish'
+
+        then:
+        verifyPublications()
+    }
+
+    def "decent error message when client can't authenticate server"() {
+        keyStore.enableSslWithServerCert(server)
+        initBuild()
+
+        when:
+        keyStore.configureIncorrectServerCert(executer)
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        fails 'publish'
+
+        then:
+        failure.assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+        failure.assertHasCause("javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated")
+    }
+
+    def "decent error message when server can't authenticate client"() {
+        keyStore.enableSslWithServerAndBadClientCert(server)
+        initBuild()
+
+        when:
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        keyStore.configureServerAndClientCerts(executer)
+
+        fails 'publish'
+
+        then:
+        failure.assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+        failure.assertHasCause("javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated")
+    }
+
+    def expectPublication() {
+        module.jar.expectPut()
+        module.jar.sha1.expectPut()
+        module.ivy.expectPut()
+        module.ivy.sha1.expectPut()
+    }
+
+    def verifyPublications() {
+        def localIvyXml = file("build/publications/ivy/ivy.xml").assertIsFile()
+        def localArtifact = file("build/libs/publish-2.jar").assertIsFile()
+
+        module.ivyFile.assertIsCopyOf(localIvyXml)
+        module.ivy.verifyChecksums()
+        module.jarFile.assertIsCopyOf(localArtifact)
+        module.jar.verifyChecksums()
+        true
+    }
+
+    def initBuild() {
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'ivy-publish'
+            group = 'org.gradle'
+            version = '2'
+
+            publishing {
+                repositories {
+                    ivy {
+                        url '${ivyRemoteRepo.uri}'
+                    }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+    }
+
+}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishIdentifierValidationIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishIdentifierValidationIntegTest.groovy
deleted file mode 100644
index edafd59..0000000
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishIdentifierValidationIntegTest.groovy
+++ /dev/null
@@ -1,160 +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.publish.ivy
-import org.gradle.test.fixtures.encoding.Identifier
-import spock.lang.Unroll
-
-class IvyPublishIdentifierValidationIntegTest extends AbstractIvyPublishIntegTest {
-
-    @Unroll
-    def "can publish with project coordinates containing #title characters"() {
-        given:
-        file("content-file") << "some content"
-        def organisation = identifier.safeForFileName().decorate("org")
-        def moduleName = identifier.safeForFileName().decorate("module")
-        def version = identifier.safeForFileName().decorate("revision")
-        def description = identifier.decorate("description")
-        def module = ivyRepo.module(organisation, moduleName, version)
-
-        settingsFile.text = "rootProject.name = '${sq(moduleName)}'"
-        buildFile.text = """
-            apply plugin: 'ivy-publish'
-            apply plugin: 'java'
-
-            group = '${sq(organisation)}'
-            version = '${sq(version)}'
-
-            println project.version
-
-            publishing {
-                repositories {
-                    ivy { url "${ivyRepo.uri}" }
-                }
-                publications {
-                    ivy(IvyPublication) {
-                        from components.java
-                        descriptor.withXml {
-                            asNode().info[0].appendNode('description', '${sq(description)}')
-                        }
-                    }
-                }
-            }
-        """
-        when:
-        succeeds 'publish'
-
-        then:
-        module.assertPublished()
-        module.parsedIvy.description == description.toString()
-
-        and:
-        resolveArtifacts(module) == [moduleName + '-' + version + '.jar']
-
-        where:
-        title        | identifier
-        "punctuation"| Identifier.punctuation
-        "non-ascii"  | Identifier.nonAscii
-        "whitespace" | Identifier.whiteSpace
-        "filesystem" | Identifier.fileSystemReserved
-        "xml markup" | Identifier.xmlMarkup
-    }
-
-    @Unroll
-    def "can publish artifacts with attributes containing #title characters"() {
-        given:
-        file("content-file") << "some content"
-
-        def organisation = identifier.safeForFileName().decorate("org")
-        def moduleName = identifier.safeForFileName().decorate("module")
-        def version = identifier.safeForFileName().decorate("revision")
-        def module = ivyRepo.module(organisation, moduleName, version)
-
-        def artifact = identifier.safeForFileName().decorate("artifact")
-        def extension = identifier.safeForFileName().decorate("extension")
-        def type = identifier.safeForFileName().decorate("type")
-        def conf = identifier.safeForFileName().decorate("conf").replace(",", "")
-        def classifier = identifier.safeForFileName().decorate("classifier")
-
-        settingsFile.text = "rootProject.name = '${sq(moduleName)}'"
-        buildFile.text = """
-            apply plugin: 'ivy-publish'
-
-            group = '${sq(organisation)}'
-            version = '${sq(version)}'
-
-            println "test build vm: file.encoding=" + System.getProperty("file.encoding")
-            println "test build vm: LANG=" + System.getenv("LANG")
-            println "test build vm: LC_ALL=" + System.getenv("LC_ALL")
-            println "test build vm: LC_CTYPE=" + System.getenv("LC_CTYPE")
-
-            publishing {
-                repositories {
-                    ivy { url "${ivyRepo.uri}" }
-                }
-                publications {
-                    ivy(IvyPublication) {
-                        configurations.create('${sq(conf)}')
-                        artifact source: 'content-file', name: '${sq(artifact)}', extension: '${sq(extension)}', type: '${sq(type)}', conf: '${sq(conf)}', classifier: '${sq(classifier)}'
-                    }
-                }
-            }
-        """
-        when:
-        succeeds 'publish'
-
-        then:
-        module.assertPublished()
-        module.assertArtifactsPublished("ivy-${version}.xml", "${artifact}-${version}-${classifier}.${extension}")
-
-        and:
-        resolveArtifacts(module, conf) == ["${artifact}-${version}-${classifier}.${extension}"]
-
-        where:
-        title        | identifier
-        "punctuation"| Identifier.punctuation
-        "non-ascii"  | Identifier.nonAscii
-        "whitespace" | Identifier.whiteSpace
-        "filesystem" | Identifier.fileSystemReserved
-        "xml markup" | Identifier.xmlMarkup
-    }
-
-    def "fails with reasonable error message for invalid identifier value"() {
-        buildFile << """
-            apply plugin: 'ivy-publish'
-
-            group = ''
-            version = ''
-
-            publishing {
-                repositories {
-                    ivy { url "${mavenRepo.uri}" }
-                }
-                publications {
-                    ivy(IvyPublication)
-                }
-            }
-        """
-        when:
-        fails 'publish'
-
-        then:
-        failure.assertHasDescription "Execution failed for task ':publishIvyPublicationToIvyRepository'."
-        failure.assertHasCause "Failed to publish publication 'ivy' to repository 'ivy'"
-        failure.assertHasCause "Invalid publication 'ivy': organisation cannot be empty."
-    }
-
-}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
index 203a1e2..c5348e2 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
@@ -141,7 +141,7 @@ $append
             dependencies {
                 compile "commons-collections:commons-collections:3.2.1"
                 runtime "commons-io:commons-io:1.4"
-                testCompile "junit:junit:4.11"
+                testCompile "junit:junit:4.12"
             }
 """
 
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishValidationIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishValidationIntegTest.groovy
new file mode 100644
index 0000000..4193fb1
--- /dev/null
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishValidationIntegTest.groovy
@@ -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.publish.ivy
+
+import javax.xml.namespace.QName
+import org.gradle.test.fixtures.encoding.Identifier
+import spock.lang.Unroll
+
+class IvyPublishValidationIntegTest extends AbstractIvyPublishIntegTest {
+
+    @Unroll
+    def "can publish with metadata containing #identifier characters"() {
+        given:
+        file("content-file") << "some content"
+        def organisation = identifier.safeForFileName().decorate("org")
+        def moduleName = identifier.safeForFileName().decorate("module")
+        def version = identifier.safeForFileName().decorate("revision")
+        def extraValue = identifier.decorate("extra")
+        def resolver = identifier.decorate("description")
+        def branch = identifier.safeForBranch().decorate("branch")
+        def status = identifier.safeForFileName().decorate("status")
+        def module = ivyRepo.module(organisation, moduleName, version)
+
+        settingsFile.text = "rootProject.name = '${sq(moduleName)}'"
+        buildFile.text = """
+            apply plugin: 'ivy-publish'
+            apply plugin: 'java'
+
+            group = '${sq(organisation)}'
+            version = '${sq(version)}'
+
+            println project.version
+            println '${sq(branch)}'
+
+            publishing {
+                repositories {
+                    ivy { url "${ivyRepo.uri}" }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        from components.java
+                        descriptor.branch = '${sq(branch)}'
+                        descriptor.status = '${sq(status)}'
+                        descriptor.extraInfo 'http://my.extra.info1', 'foo', '${sq(extraValue)}'
+                        descriptor.extraInfo 'http://my.extra.info2', 'bar', '${sq(extraValue)}'
+                        descriptor.withXml {
+                            asNode().info[0]. at resolver = '${sq(resolver)}'
+                        }
+                    }
+                }
+            }
+        """
+        when:
+        succeeds 'publish'
+
+        then:
+        module.assertPublished()
+        module.parsedIvy.resolver == resolver.toString()
+        module.parsedIvy.extraInfo == [
+                (new QName('http://my.extra.info1', 'foo')): extraValue.toString(),
+                (new QName('http://my.extra.info2', 'bar')): extraValue.toString(),
+        ]
+
+        and:
+        resolveArtifactsWithStatus(module, status) == [moduleName + '-' + version + '.jar']
+
+        where:
+        identifier << Identifier.all
+    }
+
+    @Unroll
+    def "can publish artifacts with attributes containing #identifier characters"() {
+        given:
+        file("content-file") << "some content"
+
+        def organisation = identifier.safeForFileName().decorate("org")
+        def moduleName = identifier.safeForFileName().decorate("module")
+        def version = identifier.safeForFileName().decorate("revision")
+        def module = ivyRepo.module(organisation, moduleName, version)
+
+        def artifact = identifier.safeForFileName().decorate("artifact")
+        def extension = identifier.safeForFileName().decorate("extension")
+        def type = identifier.safeForFileName().decorate("type")
+        def conf = identifier.safeForFileName().decorate("conf").replace(",", "")
+        def classifier = identifier.safeForFileName().decorate("classifier")
+
+        settingsFile.text = "rootProject.name = '${sq(moduleName)}'"
+        buildFile.text = """
+            apply plugin: 'ivy-publish'
+
+            group = '${sq(organisation)}'
+            version = '${sq(version)}'
+
+            publishing {
+                repositories {
+                    ivy { url "${ivyRepo.uri}" }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        configurations.create('${sq(conf)}')
+                        artifact source: 'content-file', name: '${sq(artifact)}', extension: '${sq(extension)}', type: '${sq(type)}', conf: '${sq(conf)}', classifier: '${sq(classifier)}'
+                    }
+                }
+            }
+        """
+        when:
+        succeeds 'publish'
+
+        then:
+        module.assertPublished()
+        module.assertArtifactsPublished("ivy-${version}.xml", "${artifact}-${version}-${classifier}.${extension}")
+
+        and:
+        resolveArtifacts(module, conf) == ["${artifact}-${version}-${classifier}.${extension}"]
+
+        where:
+        identifier << Identifier.all
+    }
+
+    def "fails with reasonable error message for invalid identifier value"() {
+        buildFile << """
+            apply plugin: 'ivy-publish'
+
+            group = ''
+            version = ''
+
+            publishing {
+                repositories {
+                    ivy { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    ivy(IvyPublication)
+                }
+            }
+        """
+        when:
+        fails 'publish'
+
+        then:
+        failure.assertHasDescription "Execution failed for task ':publishIvyPublicationToIvyRepository'."
+        failure.assertHasCause "Failed to publish publication 'ivy' to repository 'ivy'"
+        failure.assertHasCause "Invalid publication 'ivy': organisation cannot be empty."
+    }
+
+    @Unroll
+    def "fails with reasonable error message for invalid metadata value" () {
+        when:
+        buildFile << """
+            apply plugin: 'ivy-publish'
+
+            group = 'org'
+            version = '2'
+
+            publishing {
+                repositories {
+                    ivy { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        descriptor {
+                            ${metadata}
+                        }
+                    }
+                }
+            }
+        """
+        fails 'publish'
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':publishIvyPublicationToIvyRepository'.")
+        failure.assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+        failure.assertHasCause(message)
+
+        where:
+        metadata        | message
+        "branch ''"     | "Invalid publication 'ivy': branch cannot be an empty string. Use null instead"
+        "branch 'a\tb'" | "Invalid publication 'ivy': branch cannot contain ISO control character '\\u0009'"
+        "status ''"     | "Invalid publication 'ivy': status cannot be an empty string. Use null instead"
+        "status 'a\tb'" | "Invalid publication 'ivy': status cannot contain ISO control character '\\u0009'"
+        "status 'a/b'"  | "Invalid publication 'ivy': status cannot contain '/'"
+    }
+}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishWarIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishWarIntegTest.groovy
index fd6e503..505c5f2 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishWarIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishWarIntegTest.groovy
@@ -39,7 +39,7 @@ class IvyPublishWarIntegTest extends AbstractIvyPublishIntegTest {
                 runtime "commons-io:commons-io:1.4"
                 providedCompile "commons-lang:commons-lang:2.6"
                 providedRuntime "commons-cli:commons-cli:1.2"
-                testCompile "junit:junit:4.11"
+                testCompile "junit:junit:4.12"
             }
 
             publishing {
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/SamplesIvyPublishIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/SamplesIvyPublishIntegrationTest.groovy
index 93b39a2..d0c25fe 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/SamplesIvyPublishIntegrationTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/SamplesIvyPublishIntegrationTest.groovy
@@ -59,7 +59,7 @@ public class SamplesIvyPublishIntegrationTest extends AbstractIntegrationSpec {
 
         project1module.parsedIvy.configurations.keySet() == ['default', 'runtime'] as Set
         project1module.parsedIvy.description == "The first project"
-        project1module.parsedIvy.assertDependsOn("junit:junit:4.11 at runtime", "org.gradle.sample:project2:1.0 at runtime")
+        project1module.parsedIvy.assertDependsOn("junit:junit:4.12 at runtime", "org.gradle.sample:project2:1.0 at runtime")
 
         and:
         project2module.assertPublished()
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginIntegTest.groovy
index afb301f..635b87d 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginIntegTest.groovy
@@ -19,12 +19,6 @@ package org.gradle.api.publish.ivy.plugins
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
 
 class IvyPublishPluginIntegTest extends WellBehavedPluginTest {
-
-    @Override
-    String getPluginId() {
-        "ivy-publish"
-    }
-
     @Override
     String getMainTask() {
         "publish"
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpLegacyPublishIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpLegacyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..b186875
--- /dev/null
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpLegacyPublishIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.publish.ivy
+
+import org.gradle.api.publish.ivy.AbstractIvyRemoteLegacyPublishIntegrationTest
+import org.gradle.test.fixtures.server.http.RepositoryHttpServer
+import org.junit.Rule
+
+class IvyHttpLegacyPublishIntegrationTest extends AbstractIvyRemoteLegacyPublishIntegrationTest {
+    @Rule RepositoryHttpServer server = new RepositoryHttpServer(temporaryFolder)
+}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpPublishIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpPublishIntegrationTest.groovy
index d77bb23..4bec820 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpPublishIntegrationTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyHttpPublishIntegrationTest.groovy
@@ -19,15 +19,14 @@ package org.gradle.integtests.publish.ivy
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
+import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
 import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.IvyHttpModule
+import org.gradle.test.fixtures.server.http.IvyHttpRepository
 import org.gradle.util.GradleVersion
-import org.gradle.util.Jvm
 import org.hamcrest.Matchers
 import org.junit.Rule
-import org.mortbay.jetty.HttpStatus
 import spock.lang.Unroll
 
 import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
@@ -51,41 +50,6 @@ credentials {
         server.expectUserAgent(matchesNameAndVersion("Gradle", GradleVersion.current().getVersion()))
     }
 
-    public void canPublishToUnauthenticatedHttpRepository() {
-        given:
-        server.start()
-        settingsFile << 'rootProject.name = "publish"'
-        buildFile << """
-apply plugin: 'java'
-version = '2'
-group = 'org.gradle'
-
-uploadArchives {
-    repositories {
-        ivy {
-            url "${ivyHttpRepo.uri}"
-        }
-    }
-}
-"""
-        and:
-        module.jar.expectPut()
-        module.jar.sha1.expectPut()
-        module.ivy.expectPut(HttpStatus.ORDINAL_201_Created)
-        module.ivy.sha1.expectPut(HttpStatus.ORDINAL_201_Created)
-
-        when:
-        run 'uploadArchives'
-
-        then:
-        module.assertIvyAndJarFilePublished()
-        module.jarFile.assertIsCopyOf(file('build/libs/publish-2.jar'))
-
-        and:
-        progressLogging.uploadProgressLogged(module.ivy.uri)
-        progressLogging.uploadProgressLogged(module.jar.uri)
-    }
-
     @Unroll
     def "can publish to authenticated repository using #authScheme auth"() {
         given:
@@ -292,32 +256,4 @@ uploadTools {
         module.assertIvyAndJarFilePublished()
         module.jarFile.assertIsCopyOf(new TestFile(toolsJar))
     }
-
-    public void "does not upload meta-data file if artifact upload fails"() {
-        given:
-        server.start()
-
-        settingsFile << 'rootProject.name = "publish"'
-        buildFile << """
-apply plugin: 'java'
-version = '2'
-group = 'org.gradle'
-uploadArchives {
-    repositories {
-        ivy {
-            url "${ivyHttpRepo.uri}"
-        }
-    }
-}
-"""
-        and:
-        module.jar.expectPut(HttpStatus.ORDINAL_500_Internal_Server_Error)
-
-        when:
-        fails 'uploadArchives'
-
-        then:
-        module.jarFile.assertExists()
-        module.ivyFile.assertDoesNotExist()
-    }
 }
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyUrlResolverPublishIntegrationTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyUrlResolverPublishIntegrationTest.groovy
deleted file mode 100644
index f162e41..0000000
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/integtests/publish/ivy/IvyUrlResolverPublishIntegrationTest.groovy
+++ /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.integtests.publish.ivy
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.gradle.test.fixtures.ivy.IvyHttpModule
-import org.gradle.test.fixtures.ivy.IvyHttpRepository
-import org.gradle.test.fixtures.server.http.HttpServer
-import org.junit.Rule
-
-public class IvyUrlResolverPublishIntegrationTest extends AbstractIntegrationSpec {
-    @Rule ProgressLoggingFixture progressLogging = new ProgressLoggingFixture(executer, temporaryFolder)
-    @Rule HttpServer server = new HttpServer()
-
-    private IvyHttpModule module
-    private IvyHttpRepository ivyHttpRepo
-
-    def setup() {
-        ivyHttpRepo = new IvyHttpRepository(server, ivyRepo)
-        module = ivyHttpRepo.module("org.gradle", "publish", "2")
-    }
-
-    public void canPublishToAnHttpRepository() {
-        given:
-        server.start()
-
-        settingsFile << 'rootProject.name = "publish"'
-        buildFile << """
-apply plugin: 'java'
-version = '2'
-group = 'org.gradle'
-
-uploadArchives {
-    repositories {
-        add(new org.apache.ivy.plugins.resolver.URLResolver()) {
-            addIvyPattern("${ivyHttpRepo.ivyPattern}")
-            addArtifactPattern("${ivyHttpRepo.artifactPattern}")
-        }
-    }
-}
-"""
-
-        and:
-        module.jar.expectPut()
-        module.ivy.expectPut()
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        when:
-        run 'uploadArchives'
-
-        then:
-        module.ivyFile.assertIsFile()
-        module.jarFile.assertIsCopyOf(file('build/libs/publish-2.jar'))
-
-        and:
-        progressLogging.uploadProgressLogged(module.jar.uri)
-        progressLogging.uploadProgressLogged(module.ivy.uri)
-    }
-}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyExtraInfoSpec.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyExtraInfoSpec.java
new file mode 100644
index 0000000..f866150
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyExtraInfoSpec.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.ivy.IvyExtraInfo;
+
+/**
+ * Represents a modifiable form of IvyExtraInfo so that "extra" info elements
+ * can be configured on an Ivy publication.
+ */
+ at Incubating
+public interface IvyExtraInfoSpec extends IvyExtraInfo {
+
+    /**
+     * Puts the specified extra element into the list of extra info elements.
+     *
+     * @param namespace The namespace of the element to add
+     * @param name The name of the element to add
+     * @param value The value of the element to add
+     */
+    void add(String namespace, String name, String value);
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptor.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptor.java
deleted file mode 100644
index 1203950..0000000
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptor.java
+++ /dev/null
@@ -1,82 +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.publish.ivy;
-
-import org.gradle.api.Action;
-import org.gradle.api.Incubating;
-import org.gradle.api.XmlProvider;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * The descriptor of any Ivy publication.
- * <p>
- * Corresponds to the <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile.html">XML version of the Ivy Module Descriptor</a>.
- * <p>
- * The {@link #withXml(org.gradle.api.Action)} method can be used to modify the descriptor after it has been generated according to the publication data.
- *
- * @since 1.3
- */
- at Incubating
- at HasInternalProtocol
-public interface IvyModuleDescriptor {
-
-    /**
-     * Allow configuration of the descriptor, after it has been generated according to the input data.
-     *
-     * <pre autoTested="">
-     * apply plugin: "ivy-publish"
-     *
-     * publishing {
-     *   publications {
-     *     ivy(IvyPublication) {
-     *       descriptor {
-     *         withXml {
-     *           asNode().dependencies.dependency.find { it. at org == "junit" }. at rev = "4.10"
-     *         }
-     *       }
-     *     }
-     *   }
-     * }
-     * </pre>
-     *
-     * Note that due to Gradle's internal type conversion system, you can pass a Groovy closure to this method and
-     * it will be automatically converted to an {@code Action}.
-     * <p>
-     * Each action/closure passed to this method will be stored as a callback, and executed when the publication
-     * that this descriptor is attached to is published.
-     * <p>
-     * For details on the structure of the XML to be modified, see <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile.html">the
-     * Ivy Module Descriptor reference</a>.
-     *
-     *
-     * @param action The configuration action.
-     * @see IvyPublication
-     * @see XmlProvider
-     */
-    void withXml(Action<? super XmlProvider> action);
-
-    /**
-     * Returns the status for this publication.
-     */
-    String getStatus();
-
-    /**
-     * Sets the status for this publication.
-     */
-    void setStatus(String status);
-
-}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptorSpec.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptorSpec.java
new file mode 100644
index 0000000..975dc63
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyModuleDescriptorSpec.java
@@ -0,0 +1,104 @@
+/*
+ * 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.publish.ivy;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+import org.gradle.api.XmlProvider;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * The descriptor of any Ivy publication.
+ * <p>
+ * Corresponds to the <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile.html">XML version of the Ivy Module Descriptor</a>.
+ * <p>
+ * The {@link #withXml(org.gradle.api.Action)} method can be used to modify the descriptor after it has been generated according to the publication data.
+ *
+ * @since 1.3
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface IvyModuleDescriptorSpec {
+
+    /**
+     * Allow configuration of the descriptor, after it has been generated according to the input data.
+     *
+     * <pre autoTested="">
+     * apply plugin: "ivy-publish"
+     *
+     * publishing {
+     *   publications {
+     *     ivy(IvyPublication) {
+     *       descriptor {
+     *         withXml {
+     *           asNode().dependencies.dependency.find { it. at org == "junit" }. at rev = "4.10"
+     *         }
+     *       }
+     *     }
+     *   }
+     * }
+     * </pre>
+     *
+     * Note that due to Gradle's internal type conversion system, you can pass a Groovy closure to this method and
+     * it will be automatically converted to an {@code Action}.
+     * <p>
+     * Each action/closure passed to this method will be stored as a callback, and executed when the publication
+     * that this descriptor is attached to is published.
+     * <p>
+     * For details on the structure of the XML to be modified, see <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile.html">the
+     * Ivy Module Descriptor reference</a>.
+     *
+     *
+     * @param action The configuration action.
+     * @see IvyPublication
+     * @see XmlProvider
+     */
+    void withXml(Action<? super XmlProvider> action);
+
+    /**
+     * Returns the status for this publication.
+     */
+    @Nullable
+    String getStatus();
+
+    /**
+     * Sets the status for this publication.
+     */
+    void setStatus(@Nullable String status);
+
+    /**
+     * Returns the branch for this publication
+     */
+    @Nullable
+    String getBranch();
+
+    /**
+     * Sets the branch for this publication
+     */
+    void setBranch(@Nullable String branch);
+
+    /**
+     * Returns the extra info element spec for this publication
+     */
+    IvyExtraInfoSpec getExtraInfo();
+
+    /**
+     * Adds a new extra info element to the publication
+     */
+    void extraInfo(String namespace, String elementName, String value);
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
index 63ec200..f748c58 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
@@ -57,7 +57,7 @@ import org.gradle.api.publish.Publication;
  * Together, these methods give you full control over the artifacts to be published.
  * </p><p>
  * For any other tweaks to the publication, it is possible to modify the generated Ivy descriptor file prior to publication. This is done using
- * the {@link IvyModuleDescriptor#withXml(org.gradle.api.Action)} method, normally via a Closure passed to the {@link #descriptor(org.gradle.api.Action)} method.
+ * the {@link IvyModuleDescriptorSpec#withXml(org.gradle.api.Action)} method, normally via a Closure passed to the {@link #descriptor(org.gradle.api.Action)} method.
  * </p>
  * <h4>Example of publishing a java component with an added source jar and custom module description</h4>
  *
@@ -98,16 +98,16 @@ public interface IvyPublication extends Publication {
      *
      * @return The module descriptor that will be published.
      */
-    IvyModuleDescriptor getDescriptor();
+    IvyModuleDescriptorSpec getDescriptor();
 
     /**
      * Configures the descriptor that will be published.
      * <p>
-     * The descriptor XML can be modified by using the {@link IvyModuleDescriptor#withXml(org.gradle.api.Action)} method.
+     * The descriptor XML can be modified by using the {@link IvyModuleDescriptorSpec#withXml(org.gradle.api.Action)} method.
      *
      * @param configure The configuration action.
      */
-    void descriptor(Action<? super IvyModuleDescriptor> configure);
+    void descriptor(Action<? super IvyModuleDescriptorSpec> configure);
 
     /**
      * Provides the software component that should be published.
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublicationTasksModelRule.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublicationTasksModelRule.java
deleted file mode 100644
index 78314bb..0000000
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublicationTasksModelRule.java
+++ /dev/null
@@ -1,82 +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.publish.ivy.internal;
-
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.publish.PublicationContainer;
-import org.gradle.api.publish.PublishingExtension;
-import org.gradle.api.publish.ivy.internal.publication.IvyPublicationInternal;
-import org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor;
-import org.gradle.api.publish.ivy.tasks.PublishToIvyRepository;
-import org.gradle.api.publish.plugins.PublishingPlugin;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.model.ModelRule;
-
-import java.io.File;
-import java.util.concurrent.Callable;
-
-import static org.apache.commons.lang.StringUtils.capitalize;
-
-public class IvyPublicationTasksModelRule extends ModelRule {
-    private final Project project;
-
-    public IvyPublicationTasksModelRule(Project project) {
-        this.project = project;
-    }
-
-    public void createTasks(TaskContainer tasks, PublishingExtension publishingExtension) {
-        PublicationContainer publications = publishingExtension.getPublications();
-        RepositoryHandler repositories = publishingExtension.getRepositories();
-
-        for (final IvyPublicationInternal publication : publications.withType(IvyPublicationInternal.class)) {
-
-            final String publicationName = publication.getName();
-            final String descriptorTaskName = String.format("generateDescriptorFileFor%sPublication", capitalize(publicationName));
-
-            GenerateIvyDescriptor descriptorTask = tasks.create(descriptorTaskName, GenerateIvyDescriptor.class);
-            descriptorTask.setDescription(String.format("Generates the Ivy Module Descriptor XML file for publication '%s'.", publication.getName()));
-            descriptorTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
-            descriptorTask.setDescriptor(publication.getDescriptor());
-
-            ConventionMapping descriptorTaskConventionMapping = new DslObject(descriptorTask).getConventionMapping();
-            descriptorTaskConventionMapping.map("destination", new Callable<Object>() {
-                public Object call() throws Exception {
-                    return new File(project.getBuildDir(), "publications/" + publication.getName() + "/ivy.xml");
-                }
-            });
-
-            publication.setDescriptorFile(descriptorTask.getOutputs().getFiles());
-
-            for (IvyArtifactRepository repository : repositories.withType(IvyArtifactRepository.class)) {
-                final String repositoryName = repository.getName();
-                final String publishTaskName = String.format("publish%sPublicationTo%sRepository", capitalize(publicationName), capitalize(repositoryName));
-
-                PublishToIvyRepository publishTask = tasks.create(publishTaskName, PublishToIvyRepository.class);
-                publishTask.setPublication(publication);
-                publishTask.setRepository(repository);
-                publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
-                publishTask.setDescription(String.format("Publishes Ivy publication '%s' to Ivy repository '%s'.", publicationName, repositoryName));
-
-                tasks.getByName(PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME).dependsOn(publishTask);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublishServices.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublishServices.java
new file mode 100644
index 0000000..77b3867
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/IvyPublishServices.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy.internal;
+
+import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.api.publish.ivy.internal.publisher.ContextualizingIvyPublisher;
+import org.gradle.api.publish.ivy.internal.publisher.DependencyResolverIvyPublisher;
+import org.gradle.api.publish.ivy.internal.publisher.IvyPublisher;
+import org.gradle.api.publish.ivy.internal.publisher.ValidatingIvyPublisher;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.ivy.IvyDescriptorArtifact;
+import org.gradle.ivy.IvyModule;
+
+public class IvyPublishServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new GlobalServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new ComponentRegistrationAction());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class GlobalServices {
+        IvyPublisher createIvyPublisher(IvyContextManager ivyContextManager) {
+            IvyPublisher publisher = new DependencyResolverIvyPublisher();
+            publisher = new ValidatingIvyPublisher(publisher);
+            return new ContextualizingIvyPublisher(publisher, ivyContextManager);
+        }
+    }
+
+    private static class ComponentRegistrationAction {
+        public void configure(ServiceRegistration registration, ComponentTypeRegistry componentTypeRegistry) {
+            // TODO There should be a more explicit way to execute an action against existing services
+            // TODO:DAZ Dependency Management should be able to extract this from the plugin, without explicit registration
+            componentTypeRegistry.maybeRegisterComponentType(IvyModule.class)
+                    .registerArtifactType(IvyDescriptorArtifact.class, ArtifactType.IVY_DESCRIPTOR);
+        }
+    }
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactory.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactory.java
index b007992..0312dd7 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactory.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactory.java
@@ -19,21 +19,16 @@ package org.gradle.api.publish.ivy.internal.artifact;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.internal.typeconversion.TypedNotationParser;
 import org.gradle.api.internal.plugins.DslObject;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
 import org.gradle.api.tasks.bundling.AbstractArchiveTask;
 import org.gradle.internal.Factory;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.*;
 
 import java.io.File;
-import java.util.Collection;
 import java.util.concurrent.Callable;
 
 public class IvyArtifactNotationParserFactory implements Factory<NotationParser<Object, IvyArtifact>> {
@@ -48,25 +43,25 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
     }
 
     public NotationParser<Object, IvyArtifact> create() {
-        FileNotationParser fileNotationParser = new FileNotationParser(fileResolver);
-        ArchiveTaskNotationParser archiveTaskNotationParser = new ArchiveTaskNotationParser();
-        PublishArtifactNotationParser publishArtifactNotationParser = new PublishArtifactNotationParser();
-
-        NotationParser<Object, IvyArtifact> sourceNotationParser = new NotationParserBuilder<IvyArtifact>()
-                .resultingType(IvyArtifact.class)
-                .parser(archiveTaskNotationParser)
-                .parser(publishArtifactNotationParser)
-                .parser(fileNotationParser)
+        FileNotationConverter fileNotationConverter = new FileNotationConverter(fileResolver);
+        ArchiveTaskNotationConverter archiveTaskNotationConverter = new ArchiveTaskNotationConverter();
+        PublishArtifactNotationConverter publishArtifactNotationConverter = new PublishArtifactNotationConverter();
+
+        NotationParser<Object, IvyArtifact> sourceNotationParser = NotationParserBuilder
+                .toType(IvyArtifact.class)
+                .converter(archiveTaskNotationConverter)
+                .converter(publishArtifactNotationConverter)
+                .converter(fileNotationConverter)
                 .toComposite();
 
-        IvyArtifactMapNotationParser ivyArtifactMapNotationParser = new IvyArtifactMapNotationParser(sourceNotationParser);
+        IvyArtifactMapNotationConverter ivyArtifactMapNotationConverter = new IvyArtifactMapNotationConverter(sourceNotationParser);
 
-        NotationParserBuilder<IvyArtifact> parserBuilder = new NotationParserBuilder<IvyArtifact>()
-                .resultingType(IvyArtifact.class)
-                .parser(archiveTaskNotationParser)
-                .parser(publishArtifactNotationParser)
-                .parser(ivyArtifactMapNotationParser)
-                .parser(fileNotationParser);
+        NotationParserBuilder<IvyArtifact> parserBuilder = NotationParserBuilder
+                .toType(IvyArtifact.class)
+                .converter(archiveTaskNotationConverter)
+                .converter(publishArtifactNotationConverter)
+                .converter(ivyArtifactMapNotationConverter)
+                .converter(fileNotationConverter);
 
         return parserBuilder.toComposite();
     }
@@ -76,7 +71,6 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
                 DefaultIvyArtifact.class,
                 file, null, extension, type, classifier
         );
-        // TODO:DAZ Find a good way to handle this with lazy configuration
         new DslObject(ivyArtifact).getConventionMapping().map("name", new Callable<String>() {
             public String call() throws Exception {
                 return publicationIdentity.getModule();
@@ -85,8 +79,8 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
         return ivyArtifact;
     }
 
-    private class ArchiveTaskNotationParser extends TypedNotationParser<AbstractArchiveTask, IvyArtifact> {
-        private ArchiveTaskNotationParser() {
+    private class ArchiveTaskNotationConverter extends TypedNotationConverter<AbstractArchiveTask, IvyArtifact> {
+        private ArchiveTaskNotationConverter() {
             super(AbstractArchiveTask.class);
         }
 
@@ -99,8 +93,8 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
         }
     }
 
-    private class PublishArtifactNotationParser extends TypedNotationParser<PublishArtifact, IvyArtifact> {
-        private PublishArtifactNotationParser() {
+    private class PublishArtifactNotationConverter extends TypedNotationConverter<PublishArtifact, IvyArtifact> {
+        private PublishArtifactNotationConverter() {
             super(PublishArtifact.class);
         }
 
@@ -113,16 +107,16 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
         }
     }
 
-    private class FileNotationParser implements NotationParser<Object, IvyArtifact> {
+    private class FileNotationConverter implements NotationConverter<Object, IvyArtifact> {
         private final NotationParser<Object, File> fileResolverNotationParser;
 
-        private FileNotationParser(FileResolver fileResolver) {
+        private FileNotationConverter(FileResolver fileResolver) {
             this.fileResolverNotationParser = fileResolver.asNotationParser();
         }
 
-        public IvyArtifact parseNotation(Object notation) throws UnsupportedNotationException {
+        public void convert(Object notation, NotationConvertResult<? super IvyArtifact> result) throws TypeConversionException {
             File file = fileResolverNotationParser.parseNotation(notation);
-            return parseFile(file);
+            result.converted(parseFile(file));
         }
 
         protected IvyArtifact parseFile(File file) {
@@ -130,15 +124,16 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
             return createDefaultIvyArtifact(file, extension, extension, null);
         }
 
-        public void describe(Collection<String> candidateFormats) {
-            fileResolverNotationParser.describe(candidateFormats);
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            fileResolverNotationParser.describe(visitor);
         }
     }
 
-    private class IvyArtifactMapNotationParser extends MapNotationParser<IvyArtifact> {
+    private class IvyArtifactMapNotationConverter extends MapNotationConverter<IvyArtifact> {
         private final NotationParser<Object, IvyArtifact> sourceNotationParser;
 
-        private IvyArtifactMapNotationParser(NotationParser<Object, IvyArtifact> sourceNotationParser) {
+        private IvyArtifactMapNotationConverter(NotationParser<Object, IvyArtifact> sourceNotationParser) {
             this.sourceNotationParser = sourceNotationParser;
         }
 
@@ -147,8 +142,8 @@ public class IvyArtifactNotationParserFactory implements Factory<NotationParser<
         }
 
         @Override
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Maps containing a 'source' entry, e.g. [source: '/path/to/file', extension: 'zip'].");
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Maps containing a 'source' entry").example("[source: '/path/to/file', extension: 'zip']");
         }
     }
 }
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpec.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpec.java
new file mode 100644
index 0000000..f09fa5b
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpec.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy.internal.publication;
+
+import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyExtraInfo;
+import org.gradle.api.internal.artifacts.ivyservice.NamespaceId;
+import org.gradle.api.publish.ivy.IvyExtraInfoSpec;
+import org.gradle.internal.xml.XmlValidation;
+
+public class DefaultIvyExtraInfoSpec extends DefaultIvyExtraInfo implements IvyExtraInfoSpec {
+    public DefaultIvyExtraInfoSpec() {
+        super();
+    }
+
+    public void add(String namespace, String name, String value) {
+        if (XmlValidation.isValidXmlName(name)) {
+            extraInfo.put(new NamespaceId(namespace, name), value);
+        } else {
+            throw new IllegalArgumentException(String.format("Invalid ivy extra info element name: '%s'", name));
+        }
+    }
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptor.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptor.java
deleted file mode 100644
index 60da380..0000000
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptor.java
+++ /dev/null
@@ -1,72 +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.publish.ivy.internal.publication;
-
-import org.gradle.api.Action;
-import org.gradle.api.XmlProvider;
-import org.gradle.api.artifacts.Module;
-import org.gradle.api.internal.UserCodeAction;
-import org.gradle.api.publish.ivy.IvyArtifact;
-import org.gradle.api.publish.ivy.IvyConfiguration;
-import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
-import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
-import org.gradle.listener.ActionBroadcast;
-
-import java.util.Set;
-
-public class DefaultIvyModuleDescriptor implements IvyModuleDescriptorInternal {
-
-    private final ActionBroadcast<XmlProvider> xmlActions = new ActionBroadcast<XmlProvider>();
-    private final IvyPublicationInternal ivyPublication;
-    private String status = Module.DEFAULT_STATUS;
-
-    public DefaultIvyModuleDescriptor(IvyPublicationInternal ivyPublication) {
-        this.ivyPublication = ivyPublication;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public IvyPublicationIdentity getProjectIdentity() {
-        return ivyPublication.getIdentity();
-    }
-
-    public void withXml(Action<? super XmlProvider> action) {
-        xmlActions.add(new UserCodeAction<XmlProvider>("Could not apply withXml() to Ivy module descriptor", action));
-    }
-
-    public Action<XmlProvider> getXmlAction() {
-        return xmlActions;
-    }
-
-    public Set<IvyConfiguration> getConfigurations() {
-        return ivyPublication.getConfigurations();
-    }
-
-    public Set<IvyArtifact> getArtifacts() {
-        return ivyPublication.getArtifacts();
-    }
-
-    public Set<IvyDependencyInternal> getDependencies() {
-        return ivyPublication.getDependencies();
-    }
-}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpec.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpec.java
new file mode 100644
index 0000000..3a3a081
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpec.java
@@ -0,0 +1,99 @@
+/*
+ * 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.publish.ivy.internal.publication;
+
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.XmlProvider;
+import org.gradle.api.artifacts.Module;
+import org.gradle.api.internal.UserCodeAction;
+import org.gradle.api.publish.ivy.IvyArtifact;
+import org.gradle.api.publish.ivy.IvyConfiguration;
+
+import org.gradle.api.publish.ivy.IvyExtraInfoSpec;
+import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
+import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
+import org.gradle.listener.ActionBroadcast;
+
+import java.util.Set;
+
+public class DefaultIvyModuleDescriptorSpec implements IvyModuleDescriptorSpecInternal {
+
+    private final ActionBroadcast<XmlProvider> xmlActions = new ActionBroadcast<XmlProvider>();
+    private final IvyPublicationInternal ivyPublication;
+    private String status = Module.DEFAULT_STATUS;
+    private String branch;
+    private IvyExtraInfoSpec extraInfo = new DefaultIvyExtraInfoSpec();
+
+    public DefaultIvyModuleDescriptorSpec(IvyPublicationInternal ivyPublication) {
+        this.ivyPublication = ivyPublication;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getBranch() {
+        return branch;
+    }
+
+    public void setBranch(String branch) {
+        this.branch = branch;
+    }
+
+    public IvyExtraInfoSpec getExtraInfo() {
+        return extraInfo;
+    }
+
+    public void extraInfo(String namespace, String elementName, String value) {
+        if (elementName == null) {
+            throw new InvalidUserDataException("Cannot add an extra info element with null element name");
+        }
+        if (namespace == null) {
+            throw new InvalidUserDataException("Cannot add an extra info element with null namespace");
+        }
+        extraInfo.add(namespace, elementName, value);
+    }
+
+    public IvyPublicationIdentity getProjectIdentity() {
+        return ivyPublication.getIdentity();
+    }
+
+    public void withXml(Action<? super XmlProvider> action) {
+        xmlActions.add(new UserCodeAction<XmlProvider>("Could not apply withXml() to Ivy module descriptor", action));
+    }
+
+    public Action<XmlProvider> getXmlAction() {
+        return xmlActions;
+    }
+
+    public Set<IvyConfiguration> getConfigurations() {
+        return ivyPublication.getConfigurations();
+    }
+
+    public Set<IvyArtifact> getArtifacts() {
+        return ivyPublication.getArtifacts();
+    }
+
+    public Set<IvyDependencyInternal> getDependencies() {
+        return ivyPublication.getDependencies();
+    }
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublication.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublication.java
index 0a4ddbe..6be4694 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublication.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublication.java
@@ -32,7 +32,7 @@ import org.gradle.internal.typeconversion.NotationParser;
 import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.api.publish.ivy.IvyConfigurationContainer;
-import org.gradle.api.publish.ivy.IvyModuleDescriptor;
+import org.gradle.api.publish.ivy.IvyModuleDescriptorSpec;
 import org.gradle.api.publish.ivy.internal.artifact.DefaultIvyArtifactSet;
 import org.gradle.api.publish.ivy.internal.dependency.DefaultIvyDependency;
 import org.gradle.api.publish.ivy.internal.dependency.DefaultIvyDependencySet;
@@ -47,7 +47,7 @@ import java.util.Set;
 public class DefaultIvyPublication implements IvyPublicationInternal {
 
     private final String name;
-    private final IvyModuleDescriptorInternal descriptor;
+    private final IvyModuleDescriptorSpecInternal descriptor;
     private final IvyPublicationIdentity publicationIdentity;
     private final IvyConfigurationContainer configurations;
     private final DefaultIvyArtifactSet ivyArtifacts;
@@ -66,14 +66,14 @@ public class DefaultIvyPublication implements IvyPublicationInternal {
         configurations = instantiator.newInstance(DefaultIvyConfigurationContainer.class, instantiator);
         ivyArtifacts = instantiator.newInstance(DefaultIvyArtifactSet.class, name, ivyArtifactNotationParser);
         ivyDependencies = instantiator.newInstance(DefaultIvyDependencySet.class);
-        descriptor = instantiator.newInstance(DefaultIvyModuleDescriptor.class, this);
+        descriptor = instantiator.newInstance(DefaultIvyModuleDescriptorSpec.class, this);
     }
 
     public String getName() {
         return name;
     }
 
-    public IvyModuleDescriptorInternal getDescriptor() {
+    public IvyModuleDescriptorSpecInternal getDescriptor() {
         return descriptor;
     }
 
@@ -81,7 +81,7 @@ public class DefaultIvyPublication implements IvyPublicationInternal {
         this.descriptorFile = descriptorFile;
     }
 
-    public void descriptor(Action<? super IvyModuleDescriptor> configure) {
+    public void descriptor(Action<? super IvyModuleDescriptorSpec> configure) {
         configure.execute(descriptor);
     }
 
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorInternal.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorInternal.java
deleted file mode 100644
index b075184..0000000
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorInternal.java
+++ /dev/null
@@ -1,40 +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.publish.ivy.internal.publication;
-
-import org.gradle.api.Action;
-import org.gradle.api.XmlProvider;
-import org.gradle.api.publish.ivy.IvyArtifact;
-import org.gradle.api.publish.ivy.IvyConfiguration;
-import org.gradle.api.publish.ivy.IvyModuleDescriptor;
-import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
-import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
-
-import java.util.Set;
-
-public interface IvyModuleDescriptorInternal extends IvyModuleDescriptor {
-
-    IvyPublicationIdentity getProjectIdentity();
-
-    Set<IvyConfiguration> getConfigurations();
-
-    Set<IvyArtifact> getArtifacts();
-
-    Set<IvyDependencyInternal> getDependencies();
-
-    Action<XmlProvider> getXmlAction();
-}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorSpecInternal.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorSpecInternal.java
new file mode 100644
index 0000000..b4fa37f
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyModuleDescriptorSpecInternal.java
@@ -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.api.publish.ivy.internal.publication;
+
+import org.gradle.api.Action;
+import org.gradle.api.XmlProvider;
+import org.gradle.api.publish.ivy.IvyArtifact;
+import org.gradle.api.publish.ivy.IvyConfiguration;
+import org.gradle.api.publish.ivy.IvyModuleDescriptorSpec;
+import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
+import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
+
+import java.util.Set;
+
+public interface IvyModuleDescriptorSpecInternal extends IvyModuleDescriptorSpec {
+
+    IvyPublicationIdentity getProjectIdentity();
+
+    Set<IvyConfiguration> getConfigurations();
+
+    Set<IvyArtifact> getArtifacts();
+
+    Set<IvyDependencyInternal> getDependencies();
+
+    Action<XmlProvider> getXmlAction();
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyPublicationInternal.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyPublicationInternal.java
index c3e5926..a9924bc 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyPublicationInternal.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publication/IvyPublicationInternal.java
@@ -29,7 +29,7 @@ public interface IvyPublicationInternal extends IvyPublication, PublicationInter
 
     IvyPublicationIdentity getIdentity();
 
-    IvyModuleDescriptorInternal getDescriptor();
+    IvyModuleDescriptorSpecInternal getDescriptor();
 
     void setDescriptorFile(FileCollection descriptorFile);
 
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/DependencyResolverIvyPublisher.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/DependencyResolverIvyPublisher.java
index 034c49d..9fcd514 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/DependencyResolverIvyPublisher.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/DependencyResolverIvyPublisher.java
@@ -25,7 +25,7 @@ import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
-import org.gradle.api.internal.artifacts.metadata.DefaultModuleVersionPublishMetaData;
+import org.gradle.internal.component.external.model.DefaultIvyModulePublishMetaData;
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.util.GUtil;
@@ -41,7 +41,7 @@ public class DependencyResolverIvyPublisher implements IvyPublisher {
         IvyPublicationIdentity projectIdentity = publication.getProjectIdentity();
         ModuleRevisionId moduleRevisionId = IvyUtil.createModuleRevisionId(projectIdentity.getOrganisation(), projectIdentity.getModule(), projectIdentity.getRevision());
         ModuleVersionIdentifier moduleVersionIdentifier = DefaultModuleVersionIdentifier.newId(moduleRevisionId);
-        DefaultModuleVersionPublishMetaData publishMetaData = new DefaultModuleVersionPublishMetaData(moduleVersionIdentifier);
+        DefaultIvyModulePublishMetaData publishMetaData = new DefaultIvyModulePublishMetaData(moduleVersionIdentifier);
 
         try {
             for (IvyArtifact publishArtifact : publication.getArtifacts()) {
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGenerator.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGenerator.java
index 8207cfc..2c9fce9 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGenerator.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGenerator.java
@@ -16,12 +16,11 @@
 
 package org.gradle.api.publish.ivy.internal.publisher;
 
-import org.gradle.api.Action;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.XmlProvider;
+import javax.xml.namespace.QName;
+import org.gradle.api.*;
 import org.gradle.api.artifacts.DependencyArtifact;
-import org.gradle.api.internal.xml.SimpleXmlWriter;
-import org.gradle.api.internal.xml.XmlTransformer;
+import org.gradle.internal.xml.SimpleXmlWriter;
+import org.gradle.internal.xml.XmlTransformer;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.api.publish.ivy.IvyConfiguration;
 import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
@@ -34,6 +33,7 @@ import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 public class IvyDescriptorFileGenerator {
     private static final String IVY_FILE_ENCODING = "UTF-8";
@@ -41,7 +41,9 @@ public class IvyDescriptorFileGenerator {
 
     private final SimpleDateFormat ivyDateFormat = new SimpleDateFormat(IVY_DATE_PATTERN);
     private final IvyPublicationIdentity projectIdentity;
+    private String branch;
     private String status;
+    private Map<QName, String> extraInfo;
     private XmlTransformer xmlTransformer = new XmlTransformer();
     private List<IvyConfiguration> configurations = new ArrayList<IvyConfiguration>();
     private List<IvyArtifact> artifacts = new ArrayList<IvyArtifact>();
@@ -55,6 +57,18 @@ public class IvyDescriptorFileGenerator {
         this.status = status;
     }
 
+    public void setBranch(String branch) {
+        this.branch = branch;
+    }
+
+    public Map<QName, String> getExtraInfo() {
+        return extraInfo;
+    }
+
+    public void setExtraInfo(Map<QName, String> extraInfo) {
+        this.extraInfo = extraInfo;
+    }
+
     public IvyDescriptorFileGenerator addConfiguration(IvyConfiguration ivyConfiguration) {
         configurations.add(ivyConfiguration);
         return this;
@@ -98,10 +112,23 @@ public class IvyDescriptorFileGenerator {
         xmlWriter.startElement("info")
                 .attribute("organisation", projectIdentity.getOrganisation())
                 .attribute("module", projectIdentity.getModule())
+                .attribute("branch", branch)
                 .attribute("revision", projectIdentity.getRevision())
                 .attribute("status", status)
-                .attribute("publication", ivyDateFormat.format(new Date()))
-                .endElement();
+                .attribute("publication", ivyDateFormat.format(new Date()));
+
+        if (extraInfo != null) {
+            for (Map.Entry<QName, String> entry : extraInfo.entrySet()) {
+                if (entry.getKey() != null) {
+                    xmlWriter.startElement(String.format("ns:%s", entry.getKey().getLocalPart()))
+                            .attribute("xmlns:ns", entry.getKey().getNamespaceURI())
+                            .characters(entry.getValue())
+                            .endElement();
+                }
+            }
+        }
+
+        xmlWriter.endElement();
 
         writeConfigurations(xmlWriter);
         writePublications(xmlWriter);
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisher.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisher.java
index 7756965..c434810 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisher.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisher.java
@@ -17,13 +17,14 @@
 package org.gradle.api.publish.ivy.internal.publisher;
 
 import org.apache.commons.lang.ObjectUtils;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DisconnectedDescriptorParseContext;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DisconnectedIvyXmlModuleDescriptorParser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParseException;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ResolverStrategy;
+import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetaData;
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
 import org.gradle.api.publish.internal.PublicationFieldValidator;
 import org.gradle.api.publish.ivy.InvalidIvyPublicationException;
@@ -43,13 +44,13 @@ public class ValidatingIvyPublisher implements IvyPublisher {
     }
 
     public void publish(IvyNormalizedPublication publication, PublicationAwareRepository repository) {
-        validateIdentity(publication);
+        validateMetadata(publication);
         validateArtifacts(publication);
         checkNoDuplicateArtifacts(publication);
         delegate.publish(publication, repository);
     }
 
-    private void validateIdentity(IvyNormalizedPublication publication) {
+    private void validateMetadata(IvyNormalizedPublication publication) {
         IvyPublicationIdentity identity = publication.getProjectIdentity();
 
         IvyFieldValidator organisation = field(publication, "organisation", identity.getOrganisation())
@@ -62,15 +63,24 @@ public class ValidatingIvyPublisher implements IvyPublisher {
                 .notEmpty()
                 .validInFileName();
 
-        ModuleRevisionId moduleId = parseIvyFile(publication);
-        organisation.matches(moduleId.getOrganisation());
+        MutableModuleComponentResolveMetaData metadata = parseIvyFile(publication);
+        ModuleVersionIdentifier moduleId = metadata.getId();
+        organisation.matches(moduleId.getGroup());
         moduleName.matches(moduleId.getName());
-        revision.matches(moduleId.getRevision());
+        revision.matches(moduleId.getVersion());
+
+        field(publication, "branch", metadata.getDescriptor().getModuleRevisionId().getBranch())
+                .optionalNotEmpty()
+                .doesNotContainSpecialCharacters(true);
+
+        field(publication, "status", metadata.getStatus())
+                .optionalNotEmpty()
+                .validInFileName();
     }
 
-    private ModuleRevisionId parseIvyFile(IvyNormalizedPublication publication) {
+    private MutableModuleComponentResolveMetaData parseIvyFile(IvyNormalizedPublication publication) {
         try {
-            return moduleDescriptorParser.parseMetaData(parserSettings, publication.getDescriptorFile()).getDescriptor().getModuleRevisionId();
+            return moduleDescriptorParser.parseMetaData(parserSettings, publication.getDescriptorFile());
         } catch (MetaDataParseException pe) {
             throw new InvalidIvyPublicationException(publication.getName(), pe.getLocalizedMessage(), pe);
         }
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
index 3e7f0d8..e7c0d04 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
@@ -18,23 +18,34 @@ package org.gradle.api.publish.ivy.plugins;
 
 import org.gradle.api.*;
 import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
-import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.api.publish.PublicationContainer;
 import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.api.publish.ivy.IvyPublication;
-import org.gradle.api.publish.ivy.internal.IvyPublicationTasksModelRule;
 import org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory;
 import org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication;
 import org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublicationIdentity;
+import org.gradle.api.publish.ivy.internal.publication.IvyPublicationInternal;
 import org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity;
+import org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor;
+import org.gradle.api.publish.ivy.tasks.PublishToIvyRepository;
 import org.gradle.api.publish.plugins.PublishingPlugin;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.model.ModelRules;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
 
 import javax.inject.Inject;
+import java.io.File;
+
+import static org.apache.commons.lang.StringUtils.capitalize;
 
 /**
  * Adds the ability to publish in the Ivy format to Ivy repositories.
@@ -47,21 +58,19 @@ public class IvyPublishPlugin implements Plugin<Project> {
     private final Instantiator instantiator;
     private final DependencyMetaDataProvider dependencyMetaDataProvider;
     private final FileResolver fileResolver;
-    private final ModelRules modelRules;
     private final ProjectDependencyPublicationResolver projectDependencyResolver;
 
     @Inject
-    public IvyPublishPlugin(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, FileResolver fileResolver, ModelRules modelRules,
+    public IvyPublishPlugin(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, FileResolver fileResolver,
                             ProjectDependencyPublicationResolver projectDependencyResolver) {
         this.instantiator = instantiator;
         this.dependencyMetaDataProvider = dependencyMetaDataProvider;
         this.fileResolver = fileResolver;
-        this.modelRules = modelRules;
         this.projectDependencyResolver = projectDependencyResolver;
     }
 
     public void apply(final Project project) {
-        project.getPlugins().apply(PublishingPlugin.class);
+        project.getPluginManager().apply(PublishingPlugin.class);
 
         // Can't move this to rules yet, because it has to happen before user deferred configurable actions
         project.getExtensions().configure(PublishingExtension.class, new Action<PublishingExtension>() {
@@ -70,8 +79,46 @@ public class IvyPublishPlugin implements Plugin<Project> {
                 extension.getPublications().registerFactory(IvyPublication.class, new IvyPublicationFactory(dependencyMetaDataProvider, instantiator, fileResolver));
             }
         });
+    }
+
+    static class Rules extends RuleSource {
+        @Mutate
+        @SuppressWarnings("UnusedDeclaration")
+        public void createTasks(CollectionBuilder<Task> tasks, PublishingExtension publishingExtension, @Path("buildDir") final File buildDir) {
+            PublicationContainer publications = publishingExtension.getPublications();
+            RepositoryHandler repositories = publishingExtension.getRepositories();
 
-        modelRules.rule(new IvyPublicationTasksModelRule(project));
+            for (final IvyPublicationInternal publication : publications.withType(IvyPublicationInternal.class)) {
+
+                final String publicationName = publication.getName();
+                final String descriptorTaskName = String.format("generateDescriptorFileFor%sPublication", capitalize(publicationName));
+
+                tasks.create(descriptorTaskName, GenerateIvyDescriptor.class, new Action<GenerateIvyDescriptor>() {
+                    public void execute(final GenerateIvyDescriptor descriptorTask) {
+                        descriptorTask.setDescription(String.format("Generates the Ivy Module Descriptor XML file for publication '%s'.", publication.getName()));
+                        descriptorTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
+                        descriptorTask.setDescriptor(publication.getDescriptor());
+                        descriptorTask.setDestination(new File(buildDir, "publications/" + publication.getName() + "/ivy.xml"));
+                    }
+                });
+                publication.setDescriptorFile(tasks.get(descriptorTaskName).getOutputs().getFiles());
+
+                for (final IvyArtifactRepository repository : repositories.withType(IvyArtifactRepository.class)) {
+                    final String repositoryName = repository.getName();
+                    final String publishTaskName = String.format("publish%sPublicationTo%sRepository", capitalize(publicationName), capitalize(repositoryName));
+
+                    tasks.create(publishTaskName, PublishToIvyRepository.class, new Action<PublishToIvyRepository>() {
+                        public void execute(PublishToIvyRepository publishTask) {
+                            publishTask.setPublication(publication);
+                            publishTask.setRepository(repository);
+                            publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
+                            publishTask.setDescription(String.format("Publishes Ivy publication '%s' to Ivy repository '%s'.", publicationName, repositoryName));
+                        }
+                    });
+                    tasks.get(PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME).dependsOn(publishTaskName);
+                }
+            }
+        }
     }
 
     private class IvyPublicationFactory implements NamedDomainObjectFactory<IvyPublication> {
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/GenerateIvyDescriptor.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/GenerateIvyDescriptor.java
index de374b8..ba0c351 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/GenerateIvyDescriptor.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/GenerateIvyDescriptor.java
@@ -22,9 +22,9 @@ import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.publish.ivy.IvyArtifact;
 import org.gradle.api.publish.ivy.IvyConfiguration;
-import org.gradle.api.publish.ivy.IvyModuleDescriptor;
+import org.gradle.api.publish.ivy.IvyModuleDescriptorSpec;
 import org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal;
-import org.gradle.api.publish.ivy.internal.publication.IvyModuleDescriptorInternal;
+import org.gradle.api.publish.ivy.internal.publication.IvyModuleDescriptorSpecInternal;
 import org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator;
 import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.OutputFile;
@@ -41,29 +41,29 @@ import java.io.File;
 @Incubating
 public class GenerateIvyDescriptor extends DefaultTask {
 
-    private IvyModuleDescriptor descriptor;
+    private IvyModuleDescriptorSpec descriptor;
     private Object destination;
 
-    private final FileResolver fileResolver;
-
-    @Inject
-    public GenerateIvyDescriptor(FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-
+    public GenerateIvyDescriptor() {
         // Never up to date; we don't understand the data structures.
         getOutputs().upToDateWhen(Specs.satisfyNone());
     }
 
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * The module descriptor metadata.
      *
      * @return The module descriptor.
      */
-    public IvyModuleDescriptor getDescriptor() {
+    public IvyModuleDescriptorSpec getDescriptor() {
         return descriptor;
     }
 
-    public void setDescriptor(IvyModuleDescriptor descriptor) {
+    public void setDescriptor(IvyModuleDescriptorSpec descriptor) {
         this.descriptor = descriptor;
     }
 
@@ -74,7 +74,7 @@ public class GenerateIvyDescriptor extends DefaultTask {
      */
     @OutputFile
     public File getDestination() {
-        return destination == null ? null : fileResolver.resolve(destination);
+        return destination == null ? null : getFileResolver().resolve(destination);
     }
 
     /**
@@ -90,10 +90,13 @@ public class GenerateIvyDescriptor extends DefaultTask {
 
     @TaskAction
     public void doGenerate() {
-        IvyModuleDescriptorInternal descriptorInternal = toIvyModuleDescriptorInternal(getDescriptor());
+        IvyModuleDescriptorSpecInternal descriptorInternal = toIvyModuleDescriptorInternal(getDescriptor());
 
         IvyDescriptorFileGenerator ivyGenerator = new IvyDescriptorFileGenerator(descriptorInternal.getProjectIdentity());
         ivyGenerator.setStatus(descriptorInternal.getStatus());
+        ivyGenerator.setBranch(descriptorInternal.getBranch());
+        ivyGenerator.setExtraInfo(descriptorInternal.getExtraInfo().asMap());
+
         for (IvyConfiguration ivyConfiguration : descriptorInternal.getConfigurations()) {
             ivyGenerator.addConfiguration(ivyConfiguration);
         }
@@ -109,17 +112,17 @@ public class GenerateIvyDescriptor extends DefaultTask {
         ivyGenerator.withXml(descriptorInternal.getXmlAction()).writeTo(getDestination());
     }
 
-    private static IvyModuleDescriptorInternal toIvyModuleDescriptorInternal(IvyModuleDescriptor ivyModuleDescriptor) {
-        if (ivyModuleDescriptor == null) {
+    private static IvyModuleDescriptorSpecInternal toIvyModuleDescriptorInternal(IvyModuleDescriptorSpec ivyModuleDescriptorSpec) {
+        if (ivyModuleDescriptorSpec == null) {
             return null;
-        } else if (ivyModuleDescriptor instanceof IvyModuleDescriptorInternal) {
-            return (IvyModuleDescriptorInternal) ivyModuleDescriptor;
+        } else if (ivyModuleDescriptorSpec instanceof IvyModuleDescriptorSpecInternal) {
+            return (IvyModuleDescriptorSpecInternal) ivyModuleDescriptorSpec;
         } else {
             throw new InvalidUserDataException(
                     String.format(
                             "ivyModuleDescriptor implementations must implement the '%s' interface, implementation '%s' does not",
-                            IvyModuleDescriptorInternal.class.getName(),
-                            ivyModuleDescriptor.getClass().getName()
+                            IvyModuleDescriptorSpecInternal.class.getName(),
+                            ivyModuleDescriptorSpec.getClass().getName()
                     )
             );
         }
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/PublishToIvyRepository.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/PublishToIvyRepository.java
index 205a1a5..4f51d98 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/PublishToIvyRepository.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/tasks/PublishToIvyRepository.java
@@ -21,15 +21,16 @@ import org.gradle.api.Incubating;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
 import org.gradle.api.file.FileCollection;
-import org.gradle.internal.Cast;
-import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager;
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
 import org.gradle.api.publish.internal.PublishOperation;
 import org.gradle.api.publish.ivy.IvyPublication;
 import org.gradle.api.publish.ivy.internal.publication.IvyPublicationInternal;
-import org.gradle.api.publish.ivy.internal.publisher.*;
+import org.gradle.api.publish.ivy.internal.publisher.IvyNormalizedPublication;
+import org.gradle.api.publish.ivy.internal.publisher.IvyPublisher;
 import org.gradle.api.tasks.TaskAction;
+import org.gradle.internal.Cast;
 
+import javax.inject.Inject;
 import java.util.concurrent.Callable;
 
 /**
@@ -131,14 +132,17 @@ public class PublishToIvyRepository extends DefaultTask {
         doPublish(publicationInternal, repository);
     }
 
+    @Inject
+    protected IvyPublisher getIvyPublisher() {
+        throw new UnsupportedOperationException();
+    }
+
     private void doPublish(final IvyPublicationInternal publication, final IvyArtifactRepository repository) {
-        new PublishOperation(publication, repository) {
+        new PublishOperation(publication, repository.getName()) {
             @Override
             protected void publish() throws Exception {
                 IvyNormalizedPublication normalizedPublication = publication.asNormalisedPublication();
-                IvyPublisher publisher = new DependencyResolverIvyPublisher();
-                publisher = new ValidatingIvyPublisher(publisher);
-                publisher = new ContextualizingIvyPublisher(publisher, getServices().get(IvyContextManager.class));
+                IvyPublisher publisher = getIvyPublisher();
                 publisher.publish(normalizedPublication, Cast.cast(PublicationAwareRepository.class, repository));
             }
         }.run();
diff --git a/subprojects/ivy/src/main/java/org/gradle/ivy/IvyDescriptorArtifact.java b/subprojects/ivy/src/main/java/org/gradle/ivy/IvyDescriptorArtifact.java
new file mode 100644
index 0000000..a64b28f
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/ivy/IvyDescriptorArtifact.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ivy;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+/**
+ * An Ivy descriptor artifact.
+ */
+ at Incubating
+public interface IvyDescriptorArtifact extends Artifact {
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/ivy/IvyModule.java b/subprojects/ivy/src/main/java/org/gradle/ivy/IvyModule.java
new file mode 100644
index 0000000..b69a28f
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/ivy/IvyModule.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.ivy;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Component;
+
+/**
+ * An Ivy Module component.
+ */
+ at Incubating
+public interface IvyModule extends Component {
+}
diff --git a/subprojects/ivy/src/main/java/org/gradle/ivy/package-info.java b/subprojects/ivy/src/main/java/org/gradle/ivy/package-info.java
new file mode 100644
index 0000000..9e0e87b
--- /dev/null
+++ b/subprojects/ivy/src/main/java/org/gradle/ivy/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Component types for Ivy modules.
+ */
+ at Incubating
+package org.gradle.ivy;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/ivy/src/main/resources/META-INF/gradle-plugins/ivy-publish.properties b/subprojects/ivy/src/main/resources/META-INF/gradle-plugins/org.gradle.ivy-publish.properties
similarity index 100%
rename from subprojects/ivy/src/main/resources/META-INF/gradle-plugins/ivy-publish.properties
rename to subprojects/ivy/src/main/resources/META-INF/gradle-plugins/org.gradle.ivy-publish.properties
diff --git a/subprojects/ivy/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/ivy/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..84cbbf9
--- /dev/null
+++ b/subprojects/ivy/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.api.publish.ivy.internal.IvyPublishServices
\ No newline at end of file
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactoryTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactoryTest.groovy
index 387e024..00c8344 100644
--- a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactoryTest.groovy
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/artifact/IvyArtifactNotationParserFactoryTest.groovy
@@ -31,7 +31,7 @@ import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 public class IvyArtifactNotationParserFactoryTest extends Specification {
-    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
     def fileNotationParser = Mock(NotationParser)
     def taskDependency = Mock(TaskDependency)
     def publishArtifact = Stub(PublishArtifact) {
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpecTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpecTest.groovy
new file mode 100644
index 0000000..cd4b583
--- /dev/null
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyExtraInfoSpecTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy.internal.publication
+
+import javax.xml.namespace.QName
+import spock.lang.Specification
+
+class DefaultIvyExtraInfoSpecTest extends Specification {
+    def "can add extra info elements" () {
+        def DefaultIvyExtraInfoSpec extraInfo = new DefaultIvyExtraInfoSpec()
+
+        when:
+        extraInfo.add("http://my.extra.info", "foo", "fooValue")
+
+        then:
+        extraInfo.asMap() == [ (new QName("http://my.extra.info", "foo")): "fooValue" ]
+    }
+}
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpecTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpecTest.groovy
new file mode 100644
index 0000000..95286ca
--- /dev/null
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyModuleDescriptorSpecTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy.internal.publication
+
+import javax.xml.namespace.QName
+import org.gradle.api.InvalidUserDataException
+import spock.lang.Specification
+
+class DefaultIvyModuleDescriptorSpecTest extends Specification {
+    def "getExtraInfo returns IvyExtraInfo with immutable map" () {
+        def spec = new DefaultIvyModuleDescriptorSpec(Stub(IvyPublicationInternal))
+
+        when:
+        spec.getExtraInfo().asMap().put(new QName('http://some.namespace', 'foo'), 'fooValue')
+
+        then:
+        thrown(UnsupportedOperationException)
+    }
+
+    def "can add extra info elements" () {
+        def spec = new DefaultIvyModuleDescriptorSpec(Stub(IvyPublicationInternal))
+
+        when:
+        spec.extraInfo 'http://some.namespace', 'foo', 'fooValue'
+
+        then:
+        spec.getExtraInfo().get('foo') == 'fooValue'
+    }
+
+    def "cannot add extra info elements with null values" () {
+        def spec = new DefaultIvyModuleDescriptorSpec(Stub(IvyPublicationInternal))
+
+        when:
+        spec.extraInfo namespace, name, 'fooValue'
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.getMessage().startsWith("Cannot add an extra info element with null ")
+
+        where:
+        namespace                | name
+        null                     | "foo"
+        "http://my.extra.info"   | null
+    }
+}
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublicationTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublicationTest.groovy
index c74bbb8..46a0f71 100644
--- a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublicationTest.groovy
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publication/DefaultIvyPublicationTest.groovy
@@ -36,13 +36,14 @@ import org.gradle.internal.reflect.Instantiator
 import org.gradle.internal.typeconversion.NotationParser
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import spock.lang.Shared
 import spock.lang.Specification
 
+ at UsesNativeServices
 class DefaultIvyPublicationTest extends Specification {
-
     @Shared TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
-    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
     def projectIdentity = Mock(IvyPublicationIdentity)
     def notationParser = Mock(NotationParser)
     def projectDependencyResolver = Mock(ProjectDependencyPublicationResolver)
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGeneratorTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGeneratorTest.groovy
index 268d700..15c3b10 100644
--- a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGeneratorTest.groovy
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/IvyDescriptorFileGeneratorTest.groovy
@@ -15,6 +15,8 @@
  */
 
 package org.gradle.api.publish.ivy.internal.publisher
+
+import javax.xml.namespace.QName
 import org.gradle.api.Action
 import org.gradle.api.XmlProvider
 import org.gradle.api.artifacts.DependencyArtifact
@@ -48,6 +50,7 @@ class IvyDescriptorFileGeneratorTest extends Specification {
             info. at module == "my-name"
             info. at revision == "my-version"
             info. at status.isEmpty()
+            info. at branch.isEmpty()
             configurations.size() == 1
             configurations.conf.isEmpty()
             publications.size() == 1
@@ -79,6 +82,27 @@ class IvyDescriptorFileGeneratorTest extends Specification {
         ivyXml.info. at status == "my-status"
     }
 
+    def "writes supplied branch"() {
+        when:
+        generator.setBranch("someBranch")
+
+        then:
+        ivyXml.info. at branch == "someBranch"
+    }
+
+    def "writes supplied extra info elements" () {
+        when:
+        generator.setExtraInfo([(ns('foo')): 'fooValue', (ns('bar')): 'barValue'])
+
+        then:
+        ivyXml.info."foo".size() == 1
+        ivyXml.info."foo"[0].namespaceURI() == ns('foo').namespaceURI
+        ivyXml.info."foo"[0].text() == 'fooValue'
+        ivyXml.info."bar".size() == 1
+        ivyXml.info."bar"[0].namespaceURI() == ns('bar').namespaceURI
+        ivyXml.info."bar"[0].text() == 'barValue'
+    }
+
     def "writes supplied configurations"() {
         when:
         def config1 = new DefaultIvyConfiguration("config1")
@@ -119,18 +143,19 @@ class IvyDescriptorFileGeneratorTest extends Specification {
                 it. at name == "artifact1"
                 it. at type == "type1"
                 it. at ext == "ext1"
-                it. at classifier == "classy"
+                it."@m:classifier" == "classy"
                 it. at conf.isEmpty()
             }
             with (publications[0].artifact[1]) {
                 it. at name.isEmpty()
                 it. at type.isEmpty()
                 it. at ext == ""
-                it. at classifier.isEmpty()
+                it."@m:classifier".isEmpty()
                 it. at conf == "runtime"
             }
         }
     }
+
     def "writes supplied dependencies"() {
         when:
         generator.addDependency(new DefaultIvyDependency('dep-group', 'dep-name-1', 'dep-version', "confMappingProject"))
@@ -187,13 +212,13 @@ class IvyDescriptorFileGeneratorTest extends Specification {
                     it. at name == "artifact-1"
                     it. at type == "type-1"
                     it. at ext == "ext-1"
-                    it. at classifier.isEmpty()
+                    it."@m:classifier".isEmpty()
                 }
                 with (artifact[1]) {
                     it. at name == "artifact-2"
                     it. at type.isEmpty()
                     it. at ext.isEmpty()
-                    it. at classifier == "classy"
+                    it."@m:classifier" == "classy"
                 }
             }
         }
@@ -236,4 +261,8 @@ class IvyDescriptorFileGeneratorTest extends Specification {
         generator.writeTo(ivyFile)
         return ivyFile
     }
+
+    private QName ns(String name) {
+        return new QName("http://my.extra.info/${name}", name)
+    }
 }
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisherTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisherTest.groovy
index f6fa348..f204a9c 100644
--- a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisherTest.groovy
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/internal/publisher/ValidatingIvyPublisherTest.groovy
@@ -15,6 +15,8 @@
  */
 
 package org.gradle.api.publish.ivy.internal.publisher
+
+import javax.xml.namespace.QName
 import org.gradle.api.Action
 import org.gradle.api.XmlProvider
 import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository
@@ -38,7 +40,10 @@ public class ValidatingIvyPublisherTest extends Specification {
     def "delegates when publication is valid"() {
         when:
         def projectIdentity = this.projectIdentity("the-group", "the-artifact", "the-version")
-        def publication = new IvyNormalizedPublication("pub-name", projectIdentity, ivyFile("the-group", "the-artifact", "the-version"), emptySet())
+        def generator = ivyGenerator("the-group", "the-artifact", "the-version")
+                            .withBranch("the-branch")
+                            .withStatus("release")
+        def publication = new IvyNormalizedPublication("pub-name", projectIdentity, ivyFile(generator), emptySet())
         def repository = Mock(PublicationAwareRepository)
 
         and:
@@ -50,11 +55,12 @@ public class ValidatingIvyPublisherTest extends Specification {
 
     def "does not attempt to resolve extended ivy descriptor when validating"() {
         when:
-        def ivyFile = ivyFile("the-group", "the-artifact", "the-version", new Action<XmlProvider>() {
-            void execute(XmlProvider t) {
-                t.asNode().info[0].appendNode("extends", ["organisation": "parent-org", "module": "parent-module", "revision": "parent-revision"])
-            }
-        })
+        def ivyFile = ivyFile(ivyGenerator("the-group", "the-artifact", "the-version")
+                .withAction(new Action<XmlProvider>() {
+                    void execute(XmlProvider t) {
+                        t.asNode().info[0].appendNode("extends", ["organisation": "parent-org", "module": "parent-module", "revision": "parent-revision"])
+                    }
+                }))
 
         and:
         def publication = new IvyNormalizedPublication("pub-name", projectIdentity("the-group", "the-artifact", "the-version"), ivyFile, emptySet())
@@ -93,6 +99,81 @@ public class ValidatingIvyPublisherTest extends Specification {
         "organisation" | "module"   | "version\u0085" | "revision cannot contain ISO control character '\\u0085'"
     }
 
+    def "validates ivy metadata"() {
+        given:
+        def projectIdentity = projectIdentity("org", "module", "version")
+        def generator = ivyGenerator("org", "module", "version")
+                            .withBranch(branch)
+                            .withStatus(status)
+        def publication = new IvyNormalizedPublication("pub-name", projectIdentity, ivyFile(generator), emptySet())
+        def repository = Stub(PublicationAwareRepository)
+
+        when:
+        publisher.publish(publication, repository)
+
+        then:
+        def e = thrown InvalidIvyPublicationException
+        e.message == "Invalid publication 'pub-name': $message."
+
+        where:
+        branch             | status          | message
+        ""                 | "release"       | "branch cannot be an empty string. Use null instead"
+        "someBranch"       | ""              | "status cannot be an empty string. Use null instead"
+        "someBranch\t"     | "release"       | "branch cannot contain ISO control character '\\u0009'"
+        "someBranch"       | "release\t"     | "status cannot contain ISO control character '\\u0009'"
+        "someBranch\n"     | "release"       | "branch cannot contain ISO control character '\\u000a'"
+        "someBranch"       | "release\n"     | "status cannot contain ISO control character '\\u000a'"
+        "someBranch\u0085" | "release"       | "branch cannot contain ISO control character '\\u0085'"
+        "someBranch"       | "release\u0085" | "status cannot contain ISO control character '\\u0085'"
+        "someBran\\ch"     | "release"       | "branch cannot contain '\\'"
+        "someBranch"       | "relea\\se"     | "status cannot contain '\\'"
+        "someBranch"       | "relea/se"      | "status cannot contain '/'"
+    }
+
+    def "delegates with valid ivy metadata" () {
+        given:
+        def projectIdentity = projectIdentity("org", "module", "version")
+        def generator = ivyGenerator("org", "module", "version")
+                            .withBranch(branch)
+                            .withStatus(status)
+        def publication = new IvyNormalizedPublication("pub-name", projectIdentity, ivyFile(generator), emptySet())
+        def repository = Stub(PublicationAwareRepository)
+
+        when:
+        publisher.publish(publication, repository)
+
+        then:
+        delegate.publish(publication, repository)
+
+        where:
+        branch                  | status
+        null                    | null
+        "someBranch"            | "release"
+        "feature/someBranch"    | "release"
+        "someBranch_ぴ₦ガき∆ç√∫" | "release_ぴ₦ガき∆ç√∫"
+    }
+
+    def "delegates with valid extra info elements" () {
+        given:
+        def projectIdentity = projectIdentity("org", "module", "version")
+        def generator = ivyGenerator("org", "module", "version")
+        elements.each { generator.withExtraInfo(it, "${it}Value") }
+        def publication = new IvyNormalizedPublication("pub-name", projectIdentity, ivyFile(generator), emptySet())
+        def repository = Stub(PublicationAwareRepository)
+
+        when:
+        publisher.publish(publication, repository)
+
+        then:
+        delegate.publish(publication, repository)
+
+        where:
+        elements             | _
+        [ ]                  | _
+        [ 'foo' ]            | _
+        [ 'foo', 'bar' ]     | _
+    }
+
     def "project coordinates must match ivy descriptor file"() {
         given:
         def projectIdentity = projectIdentity("org", "module", "version")
@@ -246,13 +327,52 @@ public class ValidatingIvyPublisherTest extends Specification {
         }
     }
 
-    private def ivyFile(def group, def moduleName, def version, Action<XmlProvider> action = null) {
+    private TestIvyDescriptorFileGenerator ivyGenerator(def group, def moduleName, def version) {
+        return new TestIvyDescriptorFileGenerator(new DefaultIvyPublicationIdentity(group, moduleName, version))
+    }
+
+    private def ivyFile(def group, def moduleName, def version) {
+        return ivyFile(ivyGenerator(group, moduleName, version))
+    }
+
+    private def ivyFile(IvyDescriptorFileGenerator ivyFileGenerator) {
         def ivyXmlFile = testDir.file("ivy")
-        IvyDescriptorFileGenerator ivyFileGenerator = new IvyDescriptorFileGenerator(new DefaultIvyPublicationIdentity(group, moduleName, version))
-        if (action != null) {
-            ivyFileGenerator.withXml(action)
-        }
         ivyFileGenerator.writeTo(ivyXmlFile)
         return ivyXmlFile
     }
+
+    private QName ns(String name) {
+        return new QName("http://my.extra.info/${name}", name)
+    }
+
+    class TestIvyDescriptorFileGenerator extends IvyDescriptorFileGenerator {
+        TestIvyDescriptorFileGenerator(IvyPublicationIdentity projectIdentity) {
+            super(projectIdentity)
+        }
+
+        TestIvyDescriptorFileGenerator withBranch(String branch) {
+            this.branch = branch
+            return this
+        }
+
+        TestIvyDescriptorFileGenerator withStatus(String status) {
+            this.status = status
+            return this
+        }
+
+        TestIvyDescriptorFileGenerator withAction(Action<XmlProvider> action) {
+            this.withXml(action)
+            return this
+        }
+
+        TestIvyDescriptorFileGenerator withExtraInfo(String name, String value) {
+            Map<QName, String> extraInfo = this.getExtraInfo()
+            if (extraInfo == null) {
+                extraInfo = new LinkedHashMap<QName, String>()
+                this.setExtraInfo(extraInfo)
+            }
+            extraInfo.put(ns(name), value)
+            return this
+        }
+    }
 }
diff --git a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginTest.groovy b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginTest.groovy
index a789ab0..5efcbd2 100644
--- a/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginTest.groovy
+++ b/subprojects/ivy/src/test/groovy/org/gradle/api/publish/ivy/plugins/IvyPublishPluginTest.groovy
@@ -17,12 +17,13 @@
 package org.gradle.api.publish.ivy.plugins
 
 import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.internal.tasks.TaskContainerInternal
-import org.gradle.api.internal.xml.XmlTransformer
 import org.gradle.api.publish.PublishingExtension
 import org.gradle.api.publish.ivy.IvyPublication
 import org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication
 import org.gradle.api.publish.ivy.internal.publication.IvyPublicationInternal
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.internal.xml.XmlTransformer
+import org.gradle.model.internal.fixture.ModelRegistryHelper
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
@@ -32,7 +33,7 @@ class IvyPublishPluginTest extends Specification {
     PublishingExtension publishing
 
     def setup() {
-        project.plugins.apply(IvyPublishPlugin)
+        project.pluginManager.apply(IvyPublishPlugin)
         publishing = project.extensions.getByType(PublishingExtension)
     }
 
@@ -50,16 +51,20 @@ class IvyPublishPluginTest extends Specification {
         publishing.publications.test instanceof DefaultIvyPublication
     }
 
+    void closeTaskContainer() {
+        new ModelRegistryHelper(project.modelRegistry).get("tasks", TaskContainer)
+    }
+
     def "creates publish task for publication and repository"() {
         when:
         publishing.publications.create("test", IvyPublication)
         publishing.repositories { ivy { url = "http://foo.com" } }
-        project.modelRegistry.get(TaskContainerInternal.MODEL_PATH, Object)
+        closeTaskContainer()
         def publishTask = project.tasks["publishTestPublicationToIvyRepository"]
 
         then:
         publishTask != null
-        project.tasks["publish"].dependsOn.contains publishTask
+        project.tasks["publish"].dependsOn.contains publishTask.name
     }
 
     def "ivy publication coordinates are a snapshot of project identity"() {
@@ -72,7 +77,7 @@ class IvyPublishPluginTest extends Specification {
         publishing.publications.create("test", IvyPublication)
 
         then:
-        with (publishing.publications.test) {
+        with(publishing.publications.test) {
             identity.module == project.name
             identity.organisation == "foo"
             identity.revision == "1.0"
@@ -84,7 +89,7 @@ class IvyPublishPluginTest extends Specification {
         project.version = "changed-version"
 
         then:
-        with (publishing.publications.test) {
+        with(publishing.publications.test) {
             identity.organisation == "foo"
             identity.revision == "1.0"
         }
diff --git a/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.groovy b/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.groovy
new file mode 100644
index 0000000..cda169e
--- /dev/null
+++ b/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyPublishIntegTest.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.publish.ivy
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.ivy.IvyFileModule
+
+class AbstractIvyPublishIntegTest extends AbstractIntegrationSpec {
+
+    protected def resolveArtifacts(IvyFileModule module) {
+        doResolveArtifacts("group: '${sq(module.organisation)}', name: '${sq(module.module)}', version: '${sq(module.revision)}'")
+    }
+
+    protected def resolveArtifacts(IvyFileModule module, def configuration) {
+        doResolveArtifacts("group: '${sq(module.organisation)}', name: '${sq(module.module)}', version: '${sq(module.revision)}', configuration: '${sq(configuration)}'")
+    }
+
+    protected def resolveArtifactsWithStatus(IvyFileModule module, def status) {
+        doResolveArtifacts("group: '${sq(module.organisation)}', name: '${sq(module.module)}', version: '${sq(module.revision)}'", status)
+    }
+
+    private def doResolveArtifacts(def dependency, def status=null) {
+        // Replace the existing buildfile with one for resolving the published module
+        settingsFile.text = "rootProject.name = 'resolve'"
+        buildFile.text = """
+            configurations {
+                resolve
+            }
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+                mavenCentral()
+            }
+            dependencies {
+                resolve $dependency
+            }
+
+            task resolveArtifacts(type: Sync) {
+                from configurations.resolve
+                into "artifacts"
+            }
+        """
+
+        if (status != null) {
+            buildFile.text = buildFile.text + """
+
+                dependencies.components.all { ComponentMetadataDetails details, IvyModuleDescriptor ivyModule ->
+                    details.statusScheme = [ '${sq(status)}' ]
+                }
+            """
+        }
+
+        run "resolveArtifacts"
+        def artifactsList = file("artifacts").exists() ? file("artifacts").list() : []
+        return artifactsList.sort()
+    }
+
+    String sq(String input) {
+        return escapeForSingleQuoting(input)
+    }
+
+    String escapeForSingleQuoting(String input) {
+        return input.replace('\\', '\\\\').replace('\'', '\\\'')
+    }
+}
diff --git a/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyRemoteLegacyPublishIntegrationTest.groovy b/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyRemoteLegacyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..9042c21
--- /dev/null
+++ b/subprojects/ivy/src/testFixtures/groovy/org/gradle/api/publish/ivy/AbstractIvyRemoteLegacyPublishIntegrationTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.api.publish.ivy
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
+import org.gradle.test.fixtures.ivy.RemoteIvyModule
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.junit.Rule
+
+public abstract class AbstractIvyRemoteLegacyPublishIntegrationTest extends AbstractIntegrationSpec {
+    abstract RepositoryServer getServer()
+
+    @Rule ProgressLoggingFixture progressLogger = new ProgressLoggingFixture(executer, temporaryFolder)
+
+    private RemoteIvyModule module
+    private RemoteIvyRepository ivyRepo
+
+    def setup() {
+        requireOwnGradleUserHomeDir()
+        ivyRepo = server.remoteIvyRepo
+        module = ivyRepo.module("org.gradle", "publish", "2")
+    }
+
+    public void "can publish using uploadArchives"() {
+        given:
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+apply plugin: 'java'
+version = '2'
+group = 'org.gradle'
+
+dependencies {
+    compile "commons-collections:commons-collections:3.2.1"
+    runtime "commons-io:commons-io:1.4"
+}
+
+uploadArchives {
+    repositories {
+        ivy {
+            url "${ivyRepo.uri}"
+            ${server.validCredentials}
+        }
+    }
+}
+"""
+        and:
+        module.jar.expectParentMkdir()
+        module.jar.expectUpload()
+        // TODO - should not check on each upload to a particular directory
+        module.jar.sha1.expectParentCheckdir()
+        module.jar.sha1.expectUpload()
+        module.ivy.expectParentCheckdir()
+        module.ivy.expectUpload()
+        module.ivy.sha1.expectParentCheckdir()
+        module.ivy.sha1.expectUpload()
+
+        when:
+        run 'uploadArchives'
+
+        then:
+        module.assertIvyAndJarFilePublished()
+        module.jarFile.assertIsCopyOf(file('build/libs/publish-2.jar'))
+        module.parsedIvy.expectArtifact("publish", "jar").hasAttributes("jar", "jar", ["archives", "runtime"], null)
+
+        with (module.parsedIvy) {
+            dependencies.size() == 2
+            dependencies["commons-collections:commons-collections:3.2.1"].hasConf("compile->default")
+            dependencies["commons-io:commons-io:1.4"].hasConf("runtime->default")
+        }
+
+        and:
+        progressLogger.uploadProgressLogged(module.jar.uri)
+        progressLogger.uploadProgressLogged(module.ivy.uri)
+    }
+
+    public void "does not upload meta-data file when artifact upload fails"() {
+        given:
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+apply plugin: 'java'
+version = '2'
+group = 'org.gradle'
+uploadArchives {
+    repositories {
+        ivy {
+            url "${ivyRepo.uri}"
+            ${server.validCredentials}
+        }
+    }
+}
+"""
+        and:
+        module.jar.expectParentMkdir()
+        module.jar.expectUploadBroken()
+
+        when:
+        fails 'uploadArchives'
+
+        then:
+        module.ivyFile.assertDoesNotExist()
+
+        and:
+        progressLogger.uploadProgressLogged(module.jar.uri)
+    }
+}
diff --git a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginIntegrationTest.groovy b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginIntegrationTest.groovy
index 3e440d1..8b7a414 100644
--- a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginIntegrationTest.groovy
+++ b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginIntegrationTest.groovy
@@ -19,7 +19,8 @@ package org.gradle.testing.jacoco.plugins
 import org.gradle.api.Project
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.testing.jacoco.tasks.JacocoMerge
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
@@ -38,7 +39,7 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
                 mavenCentral()
             }
             dependencies {
-                testCompile 'junit:junit:4.11'
+                testCompile 'junit:junit:4.12'
             }
         """
         createTestFiles()
@@ -78,6 +79,7 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         file(REPORT_HTML_DEFAULT_PATH).exists()
         file("${REPORTING_BASE}/jacoco/test").listFiles().collect { it.name } == ["html"]
         file("${REPORTING_BASE}/jacoco/test/html/org.gradle/Class1.java.html").exists()
+        htmlReport().totalCoverage() == 100
     }
 
     void canConfigureReportsInJacocoTestReport() {
@@ -96,7 +98,7 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         succeeds('test', 'jacocoTestReport')
 
         then:
-        file("build/jacocoHtml/index.html").exists()
+        htmlReport("build/jacocoHtml").totalCoverage() == 100
         file(REPORT_XML_DEFAULT_PATH).exists()
         file(REPORT_CSV_DEFAULT_REPORT).exists()
     }
@@ -116,7 +118,7 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         succeeds('test', 'jacocoTestReport')
 
         then:
-        file("build/customReports/jacoco/test/html/index.html").exists()
+        htmlReport("build/customReports/jacoco/test/html").totalCoverage() == 100
         file("build/customReports/jacoco/test/jacocoTestReport.xml").exists()
         file("build/customReports/jacoco/test/jacocoTestReport.csv").exists()
     }
@@ -138,32 +140,38 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         succeeds('test', 'jacocoTestReport')
 
         then:
-        file("build/${customReportDirectory}/test/html/index.html").exists()
+        htmlReport("build/${customReportDirectory}/test/html").totalCoverage() == 100
         file("build/${customReportDirectory}/test/jacocoTestReport.xml").exists()
         file("build/${customReportDirectory}/test/jacocoTestReport.csv").exists()
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void jacocoReportIsIncremental() {
+        def reportResourceDir = file("${REPORTING_BASE}/jacoco/test/html/.resources")
+
         when:
         succeeds('test', 'jacocoTestReport')
 
         then:
-        file(REPORT_HTML_DEFAULT_PATH).exists()
+        htmlReport().exists()
+        reportResourceDir.exists()
 
         when:
         succeeds('jacocoTestReport')
 
         then:
         skippedTasks.contains(":jacocoTestReport")
-        file(REPORT_HTML_DEFAULT_PATH).exists()
+        htmlReport().exists()
+        reportResourceDir.exists()
 
         when:
-        file("${REPORTING_BASE}/jacoco/test/html/.resources").deleteDir()
+        reportResourceDir.deleteDir()
         succeeds('test', 'jacocoTestReport')
 
         then:
         !skippedTasks.contains(":jacocoTestReport")
-        file(REPORT_HTML_DEFAULT_PATH).exists()
+        htmlReport().exists()
+        reportResourceDir.exists()
     }
 
     void jacocoTestReportIsSkippedIfNoCoverageDataAvailable() {
@@ -173,6 +181,7 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         executionResult.assertTaskSkipped(':jacocoTestReport')
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void canUseCoverageDataFromPreviousRunForCoverageReport() {
         when:
         succeeds('jacocoTestReport')
@@ -189,31 +198,59 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
 
         then:
         executedTasks.contains(":jacocoTestReport")
-        file(REPORT_HTML_DEFAULT_PATH).exists()
+        htmlReport().totalCoverage() == 100
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void canMergeCoverageData() {
         given:
+        file("src/otherMain/java/Thing.java") << """
+public class Thing {
+    Thing() { System.out.println("hi"); }
+    Thing(String msg) { System.out.println(msg); }
+}
+"""
+        file("src/otherTest/java/ThingTest.java") << """
+public class ThingTest {
+    @org.junit.Test public void someTest() { new Thing(); }
+}
+"""
+
         buildFile << """
+            sourceSets {
+                otherMain
+                otherTest
+            }
+            sourceSets.otherTest.compileClasspath = configurations.testCompile + sourceSets.otherMain.output
+            sourceSets.otherTest.runtimeClasspath = sourceSets.otherTest.compileClasspath + sourceSets.otherTest.output
+
             task otherTests(type: Test) {
                 binResultsDir file("bin")
-                testSrcDirs = test.testSrcDirs
-                testClassesDir = test.testClassesDir
-                classpath = test.classpath
+                testSrcDirs = sourceSets.otherTest.java.srcDirs as List
+                testClassesDir = sourceSets.otherTest.output.classesDir
+                classpath = sourceSets.otherTest.runtimeClasspath
             }
 
-            task jacocoMerge(type: ${JacocoMerge.name}) {
+            task jacocoMerge(type: JacocoMerge) {
                 executionData test, otherTests
             }
+
+            task mergedReport(type: JacocoReport) {
+                executionData jacocoMerge.destinationFile
+                dependsOn jacocoMerge
+                sourceDirectories = files(sourceSets.main.java.srcDirs, sourceSets.otherMain.java.srcDirs)
+                classDirectories = files(sourceSets.main.output.classesDir, sourceSets.otherMain.output.classesDir)
+            }
         """
         when:
-        succeeds 'jacocoMerge'
+        succeeds 'mergedReport'
 
         then:
         ":jacocoMerge" in nonSkippedTasks
         ":test" in nonSkippedTasks
         ":otherTests" in nonSkippedTasks
         file("build/jacoco/jacocoMerge.exec").exists()
+        htmlReport("build/reports/jacoco/mergedReport/html").totalCoverage() == 65
     }
 
     @Issue("GRADLE-2917")
@@ -223,6 +260,10 @@ class JacocoPluginIntegrationTest extends AbstractIntegrationSpec {
         succeeds "dependencies", "test", "jacocoTestReport"
     }
 
+    private JacocoReportFixture htmlReport(String basedir = "${REPORTING_BASE}/jacoco/test/html") {
+        return new JacocoReportFixture(file(basedir))
+    }
+
     private void createTestFiles() {
         file("src/main/java/org/gradle/Class1.java") <<
                 "package org.gradle; public class Class1 { public boolean isFoo(Object arg) { return true; } }"
diff --git a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoVersionIntegTest.groovy b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoVersionIntegTest.groovy
index 86b29e9..bb956e9 100644
--- a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoVersionIntegTest.groovy
+++ b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoVersionIntegTest.groovy
@@ -17,13 +17,12 @@ package org.gradle.testing.jacoco.plugins
 
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetVersions
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Document
-import org.jsoup.select.Elements
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import org.junit.Test
-import static org.junit.Assert.*
 
- at TargetVersions(['0.6.0.201210061924', '0.6.2.201302030002'])
+ at Requires(TestPrecondition.JDK7_OR_EARLIER)
+ at TargetVersions(['0.6.0.201210061924', '0.6.2.201302030002', '0.7.1.201405082137'])
 class JacocoVersionIntegTest extends MultiVersionIntegrationSpec {
 
     @Test
@@ -38,19 +37,25 @@ class JacocoVersionIntegTest extends MultiVersionIntegrationSpec {
         }
 
         dependencies {
-            testCompile 'junit:junit:4.10'
+            testCompile 'junit:junit:4.12'
         }
         jacoco {
             toolVersion = '$version'
         }
         """
         createTestFiles();
+
         when:
-        executer.withArgument("-d")
         succeeds('test', 'jacocoTestReport')
+
         then:
-        correctJacocoVersionUsed()
-        file("build/reports/jacoco/test/html/index.html").exists()
+        def report = htmlReport()
+        report.totalCoverage() == 100
+        report.jacocoVersion() == version
+    }
+
+    private JacocoReportFixture htmlReport(String basedir = "build/reports/jacoco/test/html") {
+        return new JacocoReportFixture(file(basedir))
     }
 
     private void createTestFiles() {
@@ -59,11 +64,4 @@ class JacocoVersionIntegTest extends MultiVersionIntegrationSpec {
         file("src/test/java/org/gradle/Class1Test.java") <<
                 "package org.gradle; import org.junit.Test; public class Class1Test { @Test public void someTest() { new Class1().isFoo(\"test\"); } }"
     }
-
-    def correctJacocoVersionUsed() {
-        Document parsedHtmlReport = Jsoup.parse(file("build/reports/jacoco/test/html/index.html"), "UTF-8")
-        Elements footer = parsedHtmlReport.select("div.footer:has(a[href=http://www.eclemma.org/jacoco])")
-        assertTrue footer.text().contains(version)
-        true
-    }
 }
diff --git a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPlugin.groovy b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPlugin.groovy
index 35c1d1f..9a334fd 100644
--- a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPlugin.groovy
+++ b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPlugin.groovy
@@ -18,6 +18,7 @@ package org.gradle.testing.jacoco.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.JavaPlugin
 import org.gradle.api.plugins.ReportingBasePlugin
 import org.gradle.api.reporting.Report
@@ -35,7 +36,7 @@ import javax.inject.Inject
  * Plugin that provides support for generating Jacoco coverage data.
  */
 @Incubating
-class JacocoPlugin implements Plugin<Project> {
+class JacocoPlugin implements Plugin<ProjectInternal> {
     static final String AGENT_CONFIGURATION_NAME = 'jacocoAgent'
     static final String ANT_CONFIGURATION_NAME = 'jacocoAnt'
     static final String PLUGIN_EXTENSION_NAME = 'jacoco'
@@ -52,8 +53,8 @@ class JacocoPlugin implements Plugin<Project> {
         this.instantiator = instantiator
     }
 
-    void apply(Project project) {
-        project.plugins.apply(ReportingBasePlugin)
+    void apply(ProjectInternal project) {
+        project.pluginManager.apply(ReportingBasePlugin)
         this.project = project
         addJacocoConfigurations()
         JacocoAgentJar agent = instantiator.newInstance(JacocoAgentJar, project)
diff --git a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginExtension.groovy b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginExtension.groovy
index b7b8fe9..30d6ca6 100644
--- a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginExtension.groovy
+++ b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginExtension.groovy
@@ -32,10 +32,11 @@ class JacocoPluginExtension {
     static final String TASK_EXTENSION_NAME = 'jacoco'
 
     Logger logger = getLogger(getClass())
+
     /**
      * Version of Jacoco JARs to use.
      */
-    String toolVersion = '0.6.2.201302030002'
+    String toolVersion = '0.7.1.201405082137'
 
     protected final Project project
 
diff --git a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoMerge.groovy b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoMerge.groovy
index 43df564..bdbe74a 100644
--- a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoMerge.groovy
+++ b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoMerge.groovy
@@ -18,12 +18,15 @@ package org.gradle.testing.jacoco.tasks
 import org.gradle.api.Incubating
 import org.gradle.api.Task
 import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.TaskCollection
 import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
 
+import javax.inject.Inject
+
 /**
  * Task to merge multiple execution data files into one.
  */
@@ -41,11 +44,18 @@ class JacocoMerge extends JacocoBase {
     @OutputFile
     File destinationFile
 
+    @Inject
+    protected IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     void merge() {
-        getAnt().taskdef(name: 'jacocoMerge', classname: 'org.jacoco.ant.MergeTask', classpath: getJacocoClasspath().asPath)
-        getAnt().jacocoMerge(destfile: getDestinationFile()) {
-            getExecutionData().addToAntBuilder(ant, 'resources')
+        antBuilder.withClasspath(getJacocoClasspath()).execute {
+            ant.taskdef(name: 'jacocoMerge', classname: 'org.jacoco.ant.MergeTask')
+            ant.jacocoMerge(destfile: getDestinationFile()) {
+                getExecutionData().addToAntBuilder(ant, 'resources')
+            }
         }
     }
 
diff --git a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReport.groovy b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReport.groovy
index d55ea27..aac1a38 100644
--- a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReport.groovy
+++ b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReport.groovy
@@ -18,6 +18,7 @@ package org.gradle.testing.jacoco.tasks
 import org.gradle.api.Incubating
 import org.gradle.api.Task
 import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.reporting.Reporting
 import org.gradle.api.tasks.*
 import org.gradle.internal.jacoco.JacocoReportsContainerImpl
@@ -66,34 +67,46 @@ class JacocoReport extends JacocoBase implements Reporting<JacocoReportsContaine
     @Nested
     private final JacocoReportsContainerImpl reports
 
-    @Inject JacocoReport(Instantiator instantiator) {
+    JacocoReport() {
         reports = instantiator.newInstance(JacocoReportsContainerImpl, this)
-        onlyIf { getExecutionData().every { it.exists() } }
+        onlyIf { getExecutionData().every { it.exists() } } //TODO SF it should be 'any' instead of 'every'
+    }
+
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     @TaskAction
     void generate() {
-        getAnt().taskdef(name: 'jacocoReport', classname: 'org.jacoco.ant.ReportTask', classpath: getJacocoClasspath().asPath)
-        getAnt().jacocoReport {
-            executiondata {
-                getExecutionData().addToAntBuilder(getAnt(), 'resources')
-            }
-            structure(name: getProject().getName()) {
-                classfiles {
-                    getAllClassDirs().filter { it.exists() }.addToAntBuilder(getAnt(), 'resources')
+        antBuilder.withClasspath(getJacocoClasspath()).execute {
+            ant.taskdef(name: 'jacocoReport', classname: 'org.jacoco.ant.ReportTask')
+            ant.jacocoReport {
+                executiondata {
+                    getExecutionData().addToAntBuilder(ant, 'resources')
                 }
-                sourcefiles {
-                    getAllSourceDirs().filter { it.exists() }.addToAntBuilder(getAnt(), 'resources')
+                structure(name: getProject().getName()) {
+                    classfiles {
+                        getAllClassDirs().filter { it.exists() }.addToAntBuilder(ant, 'resources')
+                    }
+                    sourcefiles {
+                        getAllSourceDirs().filter { it.exists() }.addToAntBuilder(ant, 'resources')
+                    }
+                }
+                if(reports.html.isEnabled()) {
+                    html(destdir: reports.html.destination)
+                }
+                if(reports.xml.isEnabled()) {
+                    xml(destfile: reports.xml.destination)
+                }
+                if(reports.csv.isEnabled()) {
+                    csv(destfile: reports.csv.destination)
                 }
-            }
-            if(reports.html.isEnabled()) {
-                html(destdir: reports.html.destination)
-            }
-            if(reports.xml.isEnabled()) {
-                xml(destfile: reports.xml.destination)
-            }
-            if(reports.csv.isEnabled()) {
-                csv(destfile: reports.csv.destination)
             }
         }
     }
diff --git a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReportsContainer.java b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReportsContainer.java
index 907af98..9f8d5a8 100644
--- a/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReportsContainer.java
+++ b/subprojects/jacoco/src/main/groovy/org/gradle/testing/jacoco/tasks/JacocoReportsContainer.java
@@ -20,7 +20,7 @@ import org.gradle.api.Incubating;
 import org.gradle.api.reporting.*;
 
 /**
- * The reporting configuration for the the {@link JacocoReport} task.
+ * The reporting configuration for the {@link JacocoReport} task.
  */
 @Incubating
 public interface JacocoReportsContainer extends ReportContainer<Report> {
diff --git a/subprojects/jacoco/src/main/resources/META-INF/gradle-plugins/jacoco.properties b/subprojects/jacoco/src/main/resources/META-INF/gradle-plugins/org.gradle.jacoco.properties
similarity index 100%
rename from subprojects/jacoco/src/main/resources/META-INF/gradle-plugins/jacoco.properties
rename to subprojects/jacoco/src/main/resources/META-INF/gradle-plugins/org.gradle.jacoco.properties
diff --git a/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoReportFixture.groovy b/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoReportFixture.groovy
new file mode 100644
index 0000000..801fd36
--- /dev/null
+++ b/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoReportFixture.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.jacoco.plugins
+
+import org.gradle.test.fixtures.file.TestFile
+import org.jsoup.Jsoup
+
+class JacocoReportFixture {
+    private final TestFile htmlDir
+
+    JacocoReportFixture(TestFile htmlDir) {
+        this.htmlDir = htmlDir
+    }
+
+    public boolean exists() {
+        htmlDir.file("index.html").exists()
+    }
+
+    public String jacocoVersion() {
+        def parsedHtmlReport = Jsoup.parse(htmlDir.file("index.html"), "UTF-8")
+        def footer = parsedHtmlReport.select("div.footer:has(a[href=http://www.eclemma.org/jacoco])")
+        String text = footer.text()
+        return text.startsWith("Created with JaCoCo ") ? text.substring(20) : text
+    }
+
+    public BigDecimal totalCoverage() {
+        def parsedHtmlReport = Jsoup.parse(htmlDir.file("index.html"), "UTF-8")
+        def table = parsedHtmlReport.select("table#coveragetable").first()
+        def td = table.select("tfoot td:eq(2)").first()
+        String totalCoverage = td.text()
+        return totalCoverage.endsWith("%") ? totalCoverage.subSequence(0, totalCoverage.length() -1) as BigDecimal : null
+    }
+}
diff --git a/subprojects/javascript/javascript.gradle b/subprojects/javascript/javascript.gradle
index ba7fa2c..0f60c8c 100644
--- a/subprojects/javascript/javascript.gradle
+++ b/subprojects/javascript/javascript.gradle
@@ -24,7 +24,7 @@ dependencies {
     compile libraries.inject
 
     // Required by JavaScriptExtension#getGoogleApisRepository()
-    compile project(':coreImpl')
+    compile project(':dependencyManagement')
 }
 
 useTestFixtures()
diff --git a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginIntegrationTest.groovy b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginIntegrationTest.groovy
index e6192ff..9ee1740 100644
--- a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginIntegrationTest.groovy
+++ b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginIntegrationTest.groovy
@@ -23,7 +23,7 @@ import static org.gradle.plugins.javascript.base.JavaScriptBasePluginTestFixture
 class JavaScriptBasePluginIntegrationTest extends WellBehavedPluginTest {
 
     @Override
-    String getPluginId() {
+    String getPluginName() {
         "javascript-base"
     }
 
diff --git a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePluginIntegrationTest.groovy b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePluginIntegrationTest.groovy
index 4d1ed60..1858a2d 100644
--- a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePluginIntegrationTest.groovy
+++ b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePluginIntegrationTest.groovy
@@ -19,6 +19,8 @@
 package org.gradle.plugins.javascript.coffeescript
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 
 import static org.gradle.plugins.javascript.base.JavaScriptBasePluginTestFixtures.*
 import static org.gradle.plugins.javascript.coffeescript.CoffeeScriptBasePluginTestFixtures.*
@@ -26,7 +28,7 @@ import static org.gradle.plugins.javascript.coffeescript.CoffeeScriptBasePluginT
 class CoffeeScriptBasePluginIntegrationTest extends WellBehavedPluginTest {
 
     @Override
-    String getPluginId() {
+    String getPluginName() {
         "coffeescript-base"
     }
 
@@ -53,6 +55,7 @@ class CoffeeScriptBasePluginIntegrationTest extends WellBehavedPluginTest {
         js.text.contains("CoffeeScript Compiler")
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "can compile coffeescript"() {
         given:
         file("src/main/coffeescript/dir1/thing1.coffee") << "number = 1"
diff --git a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/envjs/EnvJsPluginIntegrationTest.groovy b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/envjs/EnvJsPluginIntegrationTest.groovy
index e6f37d2..42e6564 100644
--- a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/envjs/EnvJsPluginIntegrationTest.groovy
+++ b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/envjs/EnvJsPluginIntegrationTest.groovy
@@ -24,6 +24,10 @@ import org.gradle.plugins.javascript.envjs.browser.BrowserEvaluate
 import static org.gradle.plugins.javascript.base.JavaScriptBasePluginTestFixtures.addGoogleRepoScript
 
 class EnvJsPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        return "envjs"
+    }
 
     def setup() {
         applyPlugin()
diff --git a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/jshint/JsHintPluginIntegrationTest.groovy b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/jshint/JsHintPluginIntegrationTest.groovy
index 99f2515..4290121 100644
--- a/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/jshint/JsHintPluginIntegrationTest.groovy
+++ b/subprojects/javascript/src/integTest/groovy/org/gradle/plugins/javascript/jshint/JsHintPluginIntegrationTest.groovy
@@ -17,11 +17,17 @@
 package org.gradle.plugins.javascript.jshint
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 
 import static org.gradle.plugins.javascript.base.JavaScriptBasePluginTestFixtures.addGradlePublicJsRepoScript
 import groovy.json.JsonSlurper
 
 class JsHintPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        return "jshint"
+    }
 
     def setup() {
         applyPlugin()
@@ -69,6 +75,7 @@ class JsHintPluginIntegrationTest extends WellBehavedPluginTest {
         json[file("src/main/js/dir1/f1.js").absolutePath] instanceof Map
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "can analyse good javascript"() {
         given:
         file("src/main/js/dir1/f1.js") << """
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePlugin.groovy b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePlugin.groovy
index 8477618..2088613 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePlugin.groovy
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePlugin.groovy
@@ -24,7 +24,7 @@ class JavaScriptBasePlugin implements Plugin<Project> {
 
 
     void apply(Project project) {
-        project.apply(plugin: BasePlugin)
+        project.pluginManager.apply(BasePlugin)
         project.extensions.create(JavaScriptExtension.NAME, JavaScriptExtension)
         project.repositories.extensions.create(JavaScriptRepositoriesExtension.NAME, JavaScriptRepositoriesExtension, project.repositories)
     }
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptRepositoriesExtension.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptRepositoriesExtension.java
index 82dbf63..6e4be70 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptRepositoriesExtension.java
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/JavaScriptRepositoriesExtension.java
@@ -16,20 +16,19 @@
 
 package org.gradle.plugins.javascript.base;
 
-import groovy.lang.Closure;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.artifacts.repositories.ArtifactRepository;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout;
 import org.gradle.internal.Actions;
-import org.gradle.api.internal.artifacts.repositories.layout.PatternRepositoryLayout;
 
 public class JavaScriptRepositoriesExtension {
 
     public static final String NAME = "javaScript";
 
-    public static final String GRADLE_PUBLIC_JAVASCRIPT_REPO_URL = "http://repo.gradle.org/gradle/javascript-public";
+    public static final String GRADLE_PUBLIC_JAVASCRIPT_REPO_URL = "https://repo.gradle.org/gradle/javascript-public";
     public static final String GOOGLE_APIS_REPO_URL = "http://ajax.googleapis.com/ajax/libs";
 
     private final RepositoryHandler repositories;
@@ -61,9 +60,8 @@ public class JavaScriptRepositoriesExtension {
             public void execute(IvyArtifactRepository repo) {
                 repo.setName("googleApisJs");
                 repo.setUrl(GOOGLE_APIS_REPO_URL);
-                repo.layout("pattern", new Closure(this) {
-                    public void doCall() {
-                        PatternRepositoryLayout layout = (PatternRepositoryLayout) getDelegate();
+                repo.layout("pattern", new Action<IvyPatternRepositoryLayout>() {
+                    public void execute(IvyPatternRepositoryLayout layout) {
                         layout.artifact("[organization]/[revision]/[module].[ext]");
                         layout.ivy("[organization]/[revision]/[module].xml");
                     }
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/SourceTransformationException.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/SourceTransformationException.java
new file mode 100644
index 0000000..d7ec0ec
--- /dev/null
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/base/SourceTransformationException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.javascript.base;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+
+/**
+ * Thrown when a source code transformation fails.
+ */
+ at Contextual
+public class SourceTransformationException extends GradleException {
+    public SourceTransformationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePlugin.groovy b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePlugin.groovy
index 922e817..4839fae 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePlugin.groovy
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptBasePlugin.groovy
@@ -28,13 +28,14 @@ import org.gradle.api.artifacts.ResolvableDependencies
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.plugins.javascript.base.JavaScriptExtension
 import org.gradle.plugins.javascript.rhino.RhinoExtension
+import org.gradle.plugins.javascript.rhino.RhinoPlugin
 
 import static org.gradle.plugins.javascript.coffeescript.CoffeeScriptExtension.*
 
 class CoffeeScriptBasePlugin implements Plugin<Project> {
 
     void apply(Project project) {
-        project.apply(plugin: "rhino")
+        project.pluginManager.apply(RhinoPlugin)
 
         JavaScriptExtension jsExtension = project.extensions.getByType(JavaScriptExtension)
         CoffeeScriptExtension csExtension = jsExtension.extensions.create(CoffeeScriptExtension.NAME, CoffeeScriptExtension)
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptCompile.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptCompile.java
index 839cd94..9dce963 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptCompile.java
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/CoffeeScriptCompile.java
@@ -40,11 +40,10 @@ public class CoffeeScriptCompile extends SourceTask {
     private Object destinationDir;
     private Object rhinoClasspath;
     private CoffeeScriptCompileOptions options = new CoffeeScriptCompileOptions();
-    private final Factory<WorkerProcessBuilder> workerProcessBuilderFactory;
 
     @Inject
-    public CoffeeScriptCompile(Factory<WorkerProcessBuilder> workerProcessBuilderFactory) {
-        this.workerProcessBuilderFactory = workerProcessBuilderFactory;
+    protected Factory<WorkerProcessBuilder> getWorkerProcessBuilderFactory() {
+        throw new UnsupportedOperationException();
     }
 
     @InputFiles
@@ -56,15 +55,6 @@ public class CoffeeScriptCompile extends SourceTask {
         this.coffeeScriptJs = coffeeScriptJs;
     }
 
-    @OutputDirectory
-    public File getDestinationDir() {
-        return getProject().file(destinationDir);
-    }
-
-    public void setDestinationDir(Object destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
     @InputFiles
     public FileCollection getRhinoClasspath() {
         return getProject().files(rhinoClasspath);
@@ -74,6 +64,15 @@ public class CoffeeScriptCompile extends SourceTask {
         this.rhinoClasspath = rhinoClasspath;
     }
 
+    @OutputDirectory
+    public File getDestinationDir() {
+        return getProject().file(destinationDir);
+    }
+
+    public void setDestinationDir(Object destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
     public CoffeeScriptCompileOptions getOptions() {
         return options;
     }
@@ -88,7 +87,7 @@ public class CoffeeScriptCompile extends SourceTask {
 
     @TaskAction
     public void doCompile() {
-        RhinoWorkerHandleFactory handleFactory = new DefaultRhinoWorkerHandleFactory(workerProcessBuilderFactory);
+        RhinoWorkerHandleFactory handleFactory = new DefaultRhinoWorkerHandleFactory(getWorkerProcessBuilderFactory());
 
         CoffeeScriptCompileSpec spec = new DefaultCoffeeScriptCompileSpec();
         spec.setCoffeeScriptJs(getCoffeeScriptJs().getSingleFile());
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/compile/internal/rhino/CoffeeScriptCompilerWorker.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/compile/internal/rhino/CoffeeScriptCompilerWorker.java
index 3a7e6ab..f8c5553 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/compile/internal/rhino/CoffeeScriptCompilerWorker.java
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/coffeescript/compile/internal/rhino/CoffeeScriptCompilerWorker.java
@@ -18,10 +18,12 @@ package org.gradle.plugins.javascript.coffeescript.compile.internal.rhino;
 
 import org.gradle.api.Action;
 import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.plugins.javascript.base.SourceTransformationException;
 import org.gradle.plugins.javascript.coffeescript.compile.internal.CoffeeScriptCompileDestinationCalculator;
 import org.gradle.plugins.javascript.coffeescript.compile.internal.SerializableCoffeeScriptCompileSpec;
 import org.gradle.plugins.javascript.rhino.worker.RhinoWorker;
 import org.mozilla.javascript.Context;
+import org.mozilla.javascript.JavaScriptException;
 import org.mozilla.javascript.RhinoException;
 import org.mozilla.javascript.Scriptable;
 
@@ -58,7 +60,11 @@ public class CoffeeScriptCompilerWorker implements RhinoWorker<Boolean, Serializ
         return childScope(rootScope, new DefaultScopeOperation<String>() {
             public String action(Scriptable compileScope, Context context) {
                 compileScope.put("coffeeScriptSource", compileScope, source);
-                return (String)context.evaluateString(compileScope, "CoffeeScript.compile(coffeeScriptSource, {});", sourceName, 0, null);
+                try {
+                    return (String) context.evaluateString(compileScope, "CoffeeScript.compile(coffeeScriptSource, {});", sourceName, 0, null);
+                } catch (JavaScriptException jse) {
+                    throw new SourceTransformationException(String.format("Failed to compile coffeescript file: %s", sourceName), jse);
+                }
             }
         });
     }
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/envjs/EnvJsPlugin.groovy b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/envjs/EnvJsPlugin.groovy
index 301bf91..3a8957d 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/envjs/EnvJsPlugin.groovy
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/envjs/EnvJsPlugin.groovy
@@ -48,8 +48,8 @@ class EnvJsPlugin implements Plugin<Project> {
     }
 
     void apply(Project project) {
-        project.plugins.apply(RhinoPlugin)
-        project.plugins.apply(ReportingBasePlugin)
+        project.pluginManager.apply(RhinoPlugin)
+        project.pluginManager.apply(ReportingBasePlugin)
 
         JavaScriptExtension jsExtension = project.extensions.getByType(JavaScriptExtension)
         EnvJsExtension envJsExtension = jsExtension.extensions.create(EnvJsExtension.NAME, EnvJsExtension)
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHint.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHint.java
index 6cec11d..5031079 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHint.java
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHint.java
@@ -49,11 +49,10 @@ public class JsHint extends SourceTask {
     private Object jsHint;
     private String encoding = "UTF-8";
     private Object jsonReport;
-    private final Factory<WorkerProcessBuilder> workerProcessBuilderFactory;
 
     @Inject
-    public JsHint(Factory<WorkerProcessBuilder> workerProcessBuilderFactory) {
-        this.workerProcessBuilderFactory = workerProcessBuilderFactory;
+    protected Factory<WorkerProcessBuilder> getWorkerProcessBuilderFactory() {
+        throw new UnsupportedOperationException();
     }
 
     @InputFiles
@@ -94,7 +93,7 @@ public class JsHint extends SourceTask {
 
     @TaskAction
     public void doJsHint() {
-        RhinoWorkerHandleFactory handleFactory = new DefaultRhinoWorkerHandleFactory(workerProcessBuilderFactory);
+        RhinoWorkerHandleFactory handleFactory = new DefaultRhinoWorkerHandleFactory(getWorkerProcessBuilderFactory());
 
         LogLevel logLevel = getProject().getGradle().getStartParameter().getLogLevel();
         RhinoWorkerHandle<JsHintResult, JsHintSpec> rhinoHandle = handleFactory.create(getRhinoClasspath(), createWorkerSpec(), logLevel, new Action<JavaExecSpec>() {
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHintPlugin.groovy b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHintPlugin.groovy
index 767ce78..2c98a0c 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHintPlugin.groovy
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/jshint/JsHintPlugin.groovy
@@ -35,8 +35,8 @@ import org.gradle.api.reporting.ReportingExtension
 class JsHintPlugin implements Plugin<Project> {
 
     void apply(Project project) {
-        project.plugins.apply(RhinoPlugin)
-        project.plugins.apply(ReportingBasePlugin)
+        project.pluginManager.apply(RhinoPlugin)
+        project.pluginManager.apply(ReportingBasePlugin)
 
         JavaScriptExtension jsExtension = project.extensions.getByType(JavaScriptExtension)
         JsHintExtension jsHintExtension = jsExtension.extensions.create(JsHintExtension.NAME, JsHintExtension)
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/RhinoPlugin.groovy b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/RhinoPlugin.groovy
index a232a0e..cda7805 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/RhinoPlugin.groovy
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/RhinoPlugin.groovy
@@ -22,6 +22,7 @@ import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.plugins.javascript.base.JavaScriptBasePlugin
 import org.gradle.plugins.javascript.base.JavaScriptExtension
 
 import static org.gradle.plugins.javascript.rhino.RhinoExtension.*
@@ -29,7 +30,7 @@ import static org.gradle.plugins.javascript.rhino.RhinoExtension.*
 class RhinoPlugin implements Plugin<Project> {
 
     void apply(Project project) {
-        project.apply(plugin: "javascript-base")
+        project.pluginManager.apply(JavaScriptBasePlugin)
 
         JavaScriptExtension jsExtension = project.extensions.findByType(JavaScriptExtension)
         RhinoExtension rhinoExtension = jsExtension.extensions.create(RhinoExtension.NAME, RhinoExtension)
diff --git a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/worker/RhinoWorkerUtils.java b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/worker/RhinoWorkerUtils.java
index 28d7c15..abe085d 100644
--- a/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/worker/RhinoWorkerUtils.java
+++ b/subprojects/javascript/src/main/groovy/org/gradle/plugins/javascript/rhino/worker/RhinoWorkerUtils.java
@@ -36,7 +36,9 @@ public abstract class RhinoWorkerUtils {
 
     public static class DefaultScopeOperation<T> implements ScopeOperation<T> {
         public void initContext(Context context) {}
-        public T action(Scriptable scope, Context context) { return null; }
+        public T action(Scriptable scope, Context context) {
+            return null;
+        }
     }
 
     public static String readFile(File file, String encoding) {
diff --git a/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/coffeescript-base.properties b/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.coffeescript-base.properties
similarity index 100%
rename from subprojects/javascript/src/main/resources/META-INF/gradle-plugins/coffeescript-base.properties
rename to subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.coffeescript-base.properties
diff --git a/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/envjs.properties b/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.envjs.properties
similarity index 100%
rename from subprojects/javascript/src/main/resources/META-INF/gradle-plugins/envjs.properties
rename to subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.envjs.properties
diff --git a/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/javascript-base.properties b/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.javascript-base.properties
similarity index 100%
rename from subprojects/javascript/src/main/resources/META-INF/gradle-plugins/javascript-base.properties
rename to subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.javascript-base.properties
diff --git a/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/jshint.properties b/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.jshint.properties
similarity index 100%
rename from subprojects/javascript/src/main/resources/META-INF/gradle-plugins/jshint.properties
rename to subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.jshint.properties
diff --git a/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/rhino.properties b/subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.rhino.properties
similarity index 100%
rename from subprojects/javascript/src/main/resources/META-INF/gradle-plugins/rhino.properties
rename to subprojects/javascript/src/main/resources/META-INF/gradle-plugins/org.gradle.rhino.properties
diff --git a/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginTest.groovy b/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginTest.groovy
index d7fdcd4..913857a 100644
--- a/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginTest.groovy
+++ b/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/base/JavaScriptBasePluginTest.groovy
@@ -26,7 +26,7 @@ class JavaScriptBasePluginTest extends Specification {
 
     def "extension is available"() {
         when:
-        project.apply(plugin: JavaScriptBasePlugin)
+        project.pluginManager.apply(JavaScriptBasePlugin)
 
         then:
         project.javaScript != null
@@ -34,7 +34,7 @@ class JavaScriptBasePluginTest extends Specification {
 
     def "can get public repo"() {
         when:
-        project.apply(plugin: JavaScriptBasePlugin)
+        project.pluginManager.apply(JavaScriptBasePlugin)
 
         then:
         project.repositories.javaScript.gradle()
diff --git a/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/rhino/RhinoPluginTest.groovy b/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/rhino/RhinoPluginTest.groovy
index 4cd55cf..556bedc 100644
--- a/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/rhino/RhinoPluginTest.groovy
+++ b/subprojects/javascript/src/test/groovy/org/gradle/plugins/javascript/rhino/RhinoPluginTest.groovy
@@ -18,17 +18,18 @@ package org.gradle.plugins.javascript.rhino
 
 import org.gradle.api.Project
 import org.gradle.testfixtures.ProjectBuilder
-import spock.lang.Specification
 import org.gradle.util.DynamicDelegate
+import spock.lang.Specification
 
 class RhinoPluginTest extends Specification {
 
     Project project = ProjectBuilder.builder().build()
-    @Delegate DynamicDelegate delegate = new DynamicDelegate(project)
+    @Delegate
+    DynamicDelegate delegate = new DynamicDelegate(project)
     RhinoExtension extension
 
     def setup() {
-        apply(plugin: RhinoPlugin)
+        project.pluginManager.apply(RhinoPlugin)
         extension = javaScript.rhino
     }
 
diff --git a/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPlugin.java b/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPlugin.java
index b21d1e8..563f2c0 100644
--- a/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPlugin.java
+++ b/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPlugin.java
@@ -41,7 +41,7 @@ public class JettyPlugin implements Plugin<Project> {
     public static final String RELOAD_MANUAL = "manual";
 
     public void apply(Project project) {
-        project.getPlugins().apply(WarPlugin.class);
+        project.getPluginManager().apply(WarPlugin.class);
         JettyPluginConvention jettyConvention = new JettyPluginConvention();
         Convention convention = project.getConvention();
         convention.getPlugins().put("jetty", jettyConvention);
diff --git a/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/internal/JettyConfiguration.java b/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/internal/JettyConfiguration.java
index 03ca8ac..e834678 100644
--- a/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/internal/JettyConfiguration.java
+++ b/subprojects/jetty/src/main/java/org/gradle/api/plugins/jetty/internal/JettyConfiguration.java
@@ -100,7 +100,7 @@ public class JettyConfiguration extends Configuration {
             //TODO it would be nice to be able to re-use the parseAnnotations() method on 
             //the org.mortbay.jetty.annotations.Configuration class, but it's too difficult?
 
-            //able to use annotations on on jdk1.5 and above
+            //able to use annotations on jdk1.5 and above
             Class<?> annotationParserClass = Thread.currentThread().getContextClassLoader().loadClass(
                     "org.mortbay.jetty.annotations.AnnotationParser");
             Method parseAnnotationsMethod = annotationParserClass.getMethod("parseAnnotations", WebAppContext.class,
diff --git a/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties b/subprojects/jetty/src/main/resources/META-INF/gradle-plugins/org.gradle.jetty.properties
similarity index 100%
rename from subprojects/jetty/src/main/resources/META-INF/gradle-plugins/jetty.properties
rename to subprojects/jetty/src/main/resources/META-INF/gradle-plugins/org.gradle.jetty.properties
diff --git a/subprojects/language-base/language-base.gradle b/subprojects/language-base/language-base.gradle
deleted file mode 100644
index 290970f..0000000
--- a/subprojects/language-base/language-base.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-dependencies {
-    compile libraries.groovy
-    compile project(":core")
-}
-
-useClassycle()
-useTestFixtures()
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/Binary.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/Binary.java
deleted file mode 100644
index 7838a17..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/Binary.java
+++ /dev/null
@@ -1,33 +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.language.base;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A physical binary artifact, which can run on a particular platform or runtime.
- */
- at Incubating
- at HasInternalProtocol
-public interface Binary extends BuildableModelElement, Named {
-    /**
-     * Returns a human-consumable display name for this binary.
-     */
-    String getDisplayName();
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/BinaryContainer.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/BinaryContainer.java
deleted file mode 100644
index 20d7f1a..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/BinaryContainer.java
+++ /dev/null
@@ -1,25 +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.language.base;
-
-import org.gradle.api.*;
-
-/**
- * A container for project binaries, which represent physical artifacts that can run on a particular platform or runtime.
- * Added to a project by the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
- */
- at Incubating
-public interface BinaryContainer extends ExtensiblePolymorphicDomainObjectContainer<Binary> {}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/BuildableModelElement.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/BuildableModelElement.java
deleted file mode 100644
index 6233355..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/BuildableModelElement.java
+++ /dev/null
@@ -1,41 +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.language.base;
-
-import org.gradle.api.Buildable;
-import org.gradle.api.Incubating;
-import org.gradle.api.Task;
-
-/**
- * A model element that is directly buildable.
- * Such an element mirrors a specified lifecycle task in the DAG, and can accept dependencies which are then associated with the lifecycle task.
- */
- at Incubating
-public interface BuildableModelElement extends Buildable {
-    /**
-     * Associates a 'lifecycle' task with the construction of this element.
-     */
-    void setLifecycleTask(Task lifecycleTask);
-
-    /**
-     * Adds a task that is required for the construction of this element.
-     * A task added this way is then added as a dependency of the associated lifecycle task.
-     */
-    void builtBy(Object... tasks);
-
-    boolean hasBuildDependencies();
-}
\ No newline at end of file
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/FunctionalSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/FunctionalSourceSet.java
deleted file mode 100644
index 93a59eb..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/FunctionalSourceSet.java
+++ /dev/null
@@ -1,27 +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.language.base;
-
-import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-
-/**
- * A container holding {@link LanguageSourceSet}s with a similar function
- * (production code, test code, etc.).
- */
- at Incubating
-public interface FunctionalSourceSet extends ExtensiblePolymorphicDomainObjectContainer<LanguageSourceSet>, Named {}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/LanguageSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/LanguageSourceSet.java
deleted file mode 100644
index e10639c..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/LanguageSourceSet.java
+++ /dev/null
@@ -1,47 +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.language.base;
-
-import org.gradle.api.Action;
-import org.gradle.api.Incubating;
-import org.gradle.api.Named;
-import org.gradle.api.Task;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A set of sources for a programming language.
- */
- at Incubating
- at HasInternalProtocol
-public interface LanguageSourceSet extends Named, BuildableModelElement {
-    // TODO: do we want to keep using SourceDirectorySet in the new API?
-    // would feel more natural if dirs could be added directly to LanguageSourceSet
-    // could also think about extending SourceDirectorySet
-
-    /**
-     * The source files.
-     */
-    SourceDirectorySet getSource();
-
-    /**
-     * Configure the sources
-     */
-    void source(Action<? super SourceDirectorySet> config);
-
-    // TODO:DAZ Maybe add this as an extension property, and only in domains where it is handled (currently in native-binaries)
-    void generatedBy(Task generatorTask);
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/ProjectSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/ProjectSourceSet.java
deleted file mode 100644
index fabe493..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/ProjectSourceSet.java
+++ /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.language.base;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.NamedDomainObjectContainer;
-
-/**
- * A container of {@link FunctionalSourceSet}s. Added to a project by the
- * {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
- */
- at Incubating
-public interface ProjectSourceSet extends NamedDomainObjectContainer<FunctionalSourceSet> {}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractBuildableModelElement.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractBuildableModelElement.java
deleted file mode 100644
index 206a2b8..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractBuildableModelElement.java
+++ /dev/null
@@ -1,55 +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.language.base.internal;
-
-import org.gradle.api.Task;
-import org.gradle.api.internal.tasks.DefaultTaskDependency;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.language.base.BuildableModelElement;
-
-import java.util.Collections;
-import java.util.Set;
-
-public class AbstractBuildableModelElement implements BuildableModelElement {
-    private final DefaultTaskDependency buildDependencies = new DefaultTaskDependency();
-    private Task lifecycleTask;
-
-    public void setLifecycleTask(Task lifecycleTask) {
-        this.lifecycleTask = lifecycleTask;
-        lifecycleTask.dependsOn(buildDependencies);
-    }
-
-    public TaskDependency getBuildDependencies() {
-        return new TaskDependency() {
-            public Set<? extends Task> getDependencies(Task other) {
-                if (lifecycleTask == null) {
-                    return buildDependencies.getDependencies(other);
-                }
-                return Collections.singleton(lifecycleTask);
-            }
-        };
-    }
-
-    public void builtBy(Object... tasks) {
-        buildDependencies.add(tasks);
-    }
-
-    public boolean hasBuildDependencies() {
-        // TODO:DAZ There must be a better way to get independent of a Task
-        return buildDependencies.getDependencies(lifecycleTask).size() > 0;
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractLanguageSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractLanguageSourceSet.java
deleted file mode 100644
index 800ddcb..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/AbstractLanguageSourceSet.java
+++ /dev/null
@@ -1,81 +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.language.base.internal;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Action;
-import org.gradle.api.Task;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.language.base.FunctionalSourceSet;
-
-public abstract class AbstractLanguageSourceSet extends AbstractBuildableModelElement implements LanguageSourceSetInternal {
-    private final String name;
-    private final String fullName;
-    private final String displayName;
-    private final SourceDirectorySet source;
-    private boolean generated;
-    private Task generatorTask;
-
-    public AbstractLanguageSourceSet(String name, FunctionalSourceSet parent, String typeName, SourceDirectorySet source) {
-        this.name = name;
-        this.fullName = parent.getName() + StringUtils.capitalize(name);
-        this.displayName = String.format("%s '%s:%s'", typeName, parent.getName(), name);
-        this.source = source;
-        super.builtBy(source.getBuildDependencies());
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getFullName() {
-        return fullName;
-    }
-
-    @Override
-    public void builtBy(Object... tasks) {
-        generated = true;
-        super.builtBy(tasks);
-    }
-
-    public void generatedBy(Task generatorTask) {
-        this.generatorTask = generatorTask;
-    }
-
-    public Task getGeneratorTask() {
-        return generatorTask;
-    }
-
-    public boolean getMayHaveSources() {
-        // TODO:DAZ This doesn't take into account build dependencies of the SourceDirectorySet.
-        // Should just ditch SourceDirectorySet from here since it's not really a great model, and drags in too much baggage.
-        return generated || !source.isEmpty();
-    }
-
-    @Override
-    public String toString() {
-        return displayName;
-    }
-
-    public void source(Action<? super SourceDirectorySet> config) {
-        config.execute(getSource());
-    }
-
-    public SourceDirectorySet getSource() {
-        return source;
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryInternal.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryInternal.java
deleted file mode 100644
index d45b4a5..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryInternal.java
+++ /dev/null
@@ -1,23 +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.language.base.internal;
-
-import org.gradle.language.base.Binary;
-
-public interface BinaryInternal extends Binary {
-    BinaryNamingScheme getNamingScheme();
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingScheme.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingScheme.java
deleted file mode 100644
index ff588bd..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingScheme.java
+++ /dev/null
@@ -1,35 +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.language.base.internal;
-
-import org.gradle.api.Nullable;
-
-import java.util.List;
-
-public interface BinaryNamingScheme {
-    String getLifecycleTaskName();
-
-    String getTaskName(@Nullable String verb);
-
-    String getTaskName(@Nullable String verb, @Nullable String target);
-
-    String getOutputDirectoryBase();
-
-    String getDescription();
-
-    List<String> getVariantDimensions();
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingSchemeBuilder.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingSchemeBuilder.java
deleted file mode 100644
index b805814..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/BinaryNamingSchemeBuilder.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.language.base.internal;
-
-public interface BinaryNamingSchemeBuilder {
-    BinaryNamingSchemeBuilder withComponentName(String name);
-
-    BinaryNamingSchemeBuilder withTypeString(String newTypeString);
-
-    BinaryNamingSchemeBuilder withVariantDimension(String dimension);
-
-    BinaryNamingScheme build();
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryContainer.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryContainer.java
deleted file mode 100644
index 33a9ed0..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryContainer.java
+++ /dev/null
@@ -1,27 +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.language.base.internal;
-
-import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.Binary;
-
-public class DefaultBinaryContainer extends DefaultPolymorphicDomainObjectContainer<Binary> implements BinaryContainer {
-    public DefaultBinaryContainer(Instantiator instantiator) {
-        super(Binary.class, instantiator);
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingScheme.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingScheme.java
deleted file mode 100644
index 9c8ce21..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingScheme.java
+++ /dev/null
@@ -1,112 +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.language.base.internal;
-
-import org.gradle.api.Nullable;
-import org.gradle.util.GUtil;
-
-import java.util.List;
-
-public class DefaultBinaryNamingScheme implements BinaryNamingScheme {
-    final String parentName;
-    final String typeString;
-    final String dimensionPrefix;
-    final List<String> dimensions;
-
-    public DefaultBinaryNamingScheme(String parentName, String typeString, List<String> dimensions) {
-        this.parentName = parentName;
-        this.typeString = typeString;
-        this.dimensions = dimensions;
-        this.dimensionPrefix = createPrefix(dimensions);
-    }
-
-    private String createPrefix(List<String> dimensions) {
-        if (dimensions.isEmpty()) {
-            return "";
-        }
-        return makeName(dimensions.toArray(new String[dimensions.size()]));
-    }
-
-    public String getLifecycleTaskName() {
-        return getTaskName(null, null);
-    }
-
-    public String getOutputDirectoryBase() {
-        StringBuilder builder = new StringBuilder(makeName(parentName, typeString));
-        if (dimensionPrefix.length() > 0) {
-            builder.append('/');
-            builder.append(dimensionPrefix);
-        }
-        return builder.toString();
-    }
-
-    public String getDescription() {
-        StringBuilder builder = new StringBuilder();
-        builder.append(GUtil.toWords(typeString));
-        builder.append(" '");
-        builder.append(parentName);
-        for (String dimension : dimensions) {
-            builder.append(':');
-            builder.append(dimension);
-        }
-        builder.append(':');
-        appendUncapitalized(builder, typeString);
-        builder.append("'");
-        return builder.toString();
-    }
-
-    public List<String> getVariantDimensions() {
-        return dimensions;
-    }
-
-    public String getTaskName(@Nullable String verb) {
-        return getTaskName(verb, null);
-    }
-
-    public String getTaskName(@Nullable String verb, @Nullable String target) {
-        return makeName(verb, dimensionPrefix, parentName, typeString, target);
-    }
-
-    public String makeName(String... words) {
-        StringBuilder builder = new StringBuilder();
-        for (String word : words) {
-            if (word == null || word.length() == 0) {
-                continue;
-            }
-            if (builder.length() == 0) {
-                appendUncapitalized(builder, word);
-            } else {
-                appendCapitalized(builder, word);
-            }
-        }
-        return builder.toString();
-    }
-
-    private void appendCapitalized(StringBuilder builder, String word) {
-        if (word.length() == 0) {
-            return;
-        }
-        builder.append(Character.toTitleCase(word.charAt(0))).append(word.substring(1));
-    }
-
-    private void appendUncapitalized(StringBuilder builder, String word) {
-        if (word.length() == 0) {
-            return;
-        }
-        builder.append(Character.toLowerCase(word.charAt(0))).append(word.substring(1));
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeBuilder.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeBuilder.java
deleted file mode 100644
index 92eb49d..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeBuilder.java
+++ /dev/null
@@ -1,64 +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.language.base.internal;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DefaultBinaryNamingSchemeBuilder implements BinaryNamingSchemeBuilder {
-    private final String parentName;
-    private final String typeString;
-    private final List<String> dimensions;
-
-    public DefaultBinaryNamingSchemeBuilder() {
-        this.parentName = null;
-        this.typeString = "";
-        this.dimensions = new ArrayList<String>();
-    }
-
-    public DefaultBinaryNamingSchemeBuilder(BinaryNamingScheme basis) {
-        assert basis instanceof DefaultBinaryNamingScheme;
-        DefaultBinaryNamingScheme clone = (DefaultBinaryNamingScheme) basis;
-        this.parentName = clone.parentName;
-        this.typeString = clone.typeString;
-        this.dimensions = clone.dimensions;
-    }
-
-    private DefaultBinaryNamingSchemeBuilder(String parentName, String typeString, List<String> dimensions) {
-        this.parentName = parentName;
-        this.typeString = typeString;
-        this.dimensions = dimensions;
-    }
-
-    public BinaryNamingSchemeBuilder withComponentName(String name) {
-        return new DefaultBinaryNamingSchemeBuilder(name, typeString, dimensions);
-    }
-
-    public BinaryNamingSchemeBuilder withTypeString(String newTypeString) {
-        return new DefaultBinaryNamingSchemeBuilder(parentName, newTypeString, dimensions);
-    }
-
-    public BinaryNamingSchemeBuilder withVariantDimension(String dimension) {
-        List<String> newDimensions = new ArrayList<String>(dimensions);
-        newDimensions.add(dimension);
-        return new DefaultBinaryNamingSchemeBuilder(parentName, typeString, newDimensions);
-    }
-
-    public BinaryNamingScheme build() {
-        return new DefaultBinaryNamingScheme(parentName, typeString, dimensions);
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSet.java
deleted file mode 100644
index f00b8c3..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSet.java
+++ /dev/null
@@ -1,39 +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.language.base.internal;
-
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
-
-public class DefaultFunctionalSourceSet extends DefaultPolymorphicDomainObjectContainer<LanguageSourceSet> implements FunctionalSourceSet {
-    private final String name;
-
-    public DefaultFunctionalSourceSet(String name, Instantiator instantiator) {
-        super(LanguageSourceSet.class, instantiator);
-        this.name = name;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("source set '%s'", name);
-    }
-
-    public String getName() {
-        return name;
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultProjectSourceSet.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultProjectSourceSet.java
deleted file mode 100644
index 4e7d9b4..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/DefaultProjectSourceSet.java
+++ /dev/null
@@ -1,32 +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.language.base.internal;
-
-import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.internal.reflect.Instantiator;
-
-public class DefaultProjectSourceSet extends AbstractNamedDomainObjectContainer<FunctionalSourceSet> implements ProjectSourceSet {
-    public DefaultProjectSourceSet(Instantiator instantiator) {
-        super(FunctionalSourceSet.class, instantiator);
-    }
-
-    @Override
-    protected FunctionalSourceSet doCreate(String name) {
-        return getInstantiator().newInstance(DefaultFunctionalSourceSet.class, name, getInstantiator());
-    }
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/LanguageSourceSetInternal.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/LanguageSourceSetInternal.java
deleted file mode 100644
index ecde133..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/internal/LanguageSourceSetInternal.java
+++ /dev/null
@@ -1,37 +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.language.base.internal;
-
-import org.gradle.api.Task;
-import org.gradle.language.base.LanguageSourceSet;
-
-public interface LanguageSourceSetInternal extends LanguageSourceSet {
-
-    // TODO:DAZ This doesn't feel right. Need some better modelling.
-    // TODO:DAZ Use this for ClassDirectoryBinary task names as well: need to hack the naming scheme to maintain back-compatibility
-    /**
-     * A unique name for this source set across all functional source sets.
-     */
-    String getFullName();
-
-    /**
-     * Return true if the source set contains sources, or if the source set is generated.
-     */
-    boolean getMayHaveSources();
-
-    // TODO:DAZ Maybe use an extension property
-    Task getGeneratorTask();
-}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/plugins/LanguageBasePlugin.java b/subprojects/language-base/src/main/groovy/org/gradle/language/base/plugins/LanguageBasePlugin.java
deleted file mode 100644
index 1a65640..0000000
--- a/subprojects/language-base/src/main/groovy/org/gradle/language/base/plugins/LanguageBasePlugin.java
+++ /dev/null
@@ -1,67 +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.language.base.plugins;
-
-import org.gradle.api.*;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.internal.BinaryInternal;
-import org.gradle.language.base.internal.DefaultBinaryContainer;
-import org.gradle.language.base.internal.DefaultProjectSourceSet;
-import org.gradle.model.ModelRules;
-
-import javax.inject.Inject;
-
-/**
- * Base plugin for language support.
- *
- * Adds a {@link org.gradle.language.base.BinaryContainer} named {@code binaries} to the project.
- * Adds a {@link org.gradle.language.base.ProjectSourceSet} named {@code sources} to the project.
- */
- at Incubating
-public class LanguageBasePlugin implements Plugin<Project> {
-    public static final String BUILD_GROUP = "build";
-
-    private final Instantiator instantiator;
-    private final ModelRules modelRules;
-
-    @Inject
-    public LanguageBasePlugin(Instantiator instantiator, ModelRules modelRules) {
-        this.instantiator = instantiator;
-        this.modelRules = modelRules;
-    }
-
-    public void apply(final Project target) {
-        target.getExtensions().create("sources", DefaultProjectSourceSet.class, instantiator);
-        final BinaryContainer binaries = target.getExtensions().create("binaries", DefaultBinaryContainer.class, instantiator);
-
-        modelRules.register("binaries", BinaryContainer.class, new Factory<BinaryContainer>() {
-            public BinaryContainer create() {
-                return binaries;
-            }
-        });
-
-        binaries.withType(BinaryInternal.class).all(new Action<BinaryInternal>() {
-            public void execute(BinaryInternal binary) {
-                Task binaryLifecycleTask = target.task(binary.getNamingScheme().getLifecycleTaskName());
-                binaryLifecycleTask.setGroup(BUILD_GROUP);
-                binaryLifecycleTask.setDescription(String.format("Assembles %s.", binary));
-                binary.setLifecycleTask(binaryLifecycleTask);
-            }
-        });
-    }
-}
diff --git a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/BuildableModelElementTest.groovy b/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/BuildableModelElementTest.groovy
deleted file mode 100644
index e4a582e..0000000
--- a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/BuildableModelElementTest.groovy
+++ /dev/null
@@ -1,54 +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.language.base.internal
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.Task
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-public class BuildableModelElementTest extends Specification {
-
-    def element = new TestBuildableModelElement()
-    def dependedOn1 = Stub(Task)
-    def dependedOn2 = Stub(Task)
-    def lifecycleTask = TestUtil.createTask(DefaultTask)
-
-    def "has direct dependencies with no lifecycle task set"() {
-        when:
-        element.builtBy(dependedOn1, dependedOn2)
-
-        then:
-        element.getBuildDependencies().getDependencies(Stub(Task)) == [dependedOn1, dependedOn2] as Set
-    }
-
-    def "has intervening lifecycle task as dependency when set"() {
-        when:
-        element.builtBy(dependedOn1)
-        element.setLifecycleTask(lifecycleTask)
-        element.builtBy(dependedOn2)
-
-        then:
-        element.getBuildDependencies().getDependencies(Stub(Task)) == [lifecycleTask] as Set
-
-        and:
-        lifecycleTask.getTaskDependencies().getDependencies(Stub(Task)) == [dependedOn1, dependedOn2] as Set
-    }
-
-    class TestBuildableModelElement extends AbstractBuildableModelElement {
-
-    }
-}
diff --git a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeTest.groovy b/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeTest.groovy
deleted file mode 100644
index 6f843d0..0000000
--- a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultBinaryNamingSchemeTest.groovy
+++ /dev/null
@@ -1,79 +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.language.base.internal
-
-import spock.lang.Specification
-
-class DefaultBinaryNamingSchemeTest extends Specification {
-    def "generates task names for native binaries"() {
-        expect:
-        def namingScheme = createNamingScheme(parentName, type, dimensions)
-        namingScheme.getTaskName(verb, target) == taskName
-
-        where:
-        parentName | type   | dimensions     | verb       | target    | taskName
-        "test"     | ""     | []             | null       | null      | "test"
-        "test"     | "type" | []             | null       | null      | "testType"
-        "test"     | "type" | []             | null       | "classes" | "testTypeClasses"
-        "test"     | ""     | []             | null       | "classes" | "testClasses"
-        "test"     | "type" | []             | "assemble" | null      | "assembleTestType"
-        "test"     | "type" | []             | "compile"  | "java"    | "compileTestTypeJava"
-        "test"     | "type" | ["one", "two"] | null       | null      | "oneTwoTestType"
-        "test"     | "type" | ["one", "two"] | null       | "classes" | "oneTwoTestTypeClasses"
-        "test"     | "type" | ["one", "two"] | "assemble" | null      | "assembleOneTwoTestType"
-        "test"     | "type" | ["one", "two"] | "compile"  | "java"    | "compileOneTwoTestTypeJava"
-    }
-
-    def "generates task name with extended inputs"() {
-        expect:
-        def namingScheme = createNamingScheme("theBinary", "theType", ['firstDimension', 'secondDimension'])
-        namingScheme.getTaskName("theVerb", "theTarget") == "theVerbFirstDimensionSecondDimensionTheBinaryTheTypeTheTarget"
-    }
-
-    def "generates base name and output directory"() {
-        def namingScheme = createNamingScheme(parentName, "", dimensions)
-
-        expect:
-        namingScheme.getLifecycleTaskName() == lifecycleName
-        namingScheme.getOutputDirectoryBase() == outputDir
-
-        where:
-        parentName    | dimensions                                 | lifecycleName                               | outputDir
-        "test"        | []                                         | "test"                                      | "test"
-        "test"        | ["one", "two"]                             | "oneTwoTest"                                | "test/oneTwo"
-        "mainLibrary" | ["enterpriseEdition", "osx_x64", "static"] | "enterpriseEditionOsx_x64StaticMainLibrary" | "mainLibrary/enterpriseEditionOsx_x64Static"
-        "mainLibrary" | ["EnterpriseEdition", "Osx_x64", "Static"] | "enterpriseEditionOsx_x64StaticMainLibrary" | "mainLibrary/enterpriseEditionOsx_x64Static"
-    }
-
-    def "generates description"() {
-        def namingScheme = createNamingScheme(parentName, typeName, dimensions)
-
-        expect:
-        namingScheme.getDescription() == lifecycleName
-
-        where:
-        parentName | typeName        | dimensions     | lifecycleName
-        "parent"   | "Executable"    | []             | "executable 'parent:executable'"
-        "parent"   | "SharedLibrary" | []             | "shared library 'parent:sharedLibrary'"
-        "parent"   | "SharedLibrary" | ["one"]        | "shared library 'parent:one:sharedLibrary'"
-        "parent"   | "SharedLibrary" | ["one", "two"] | "shared library 'parent:one:two:sharedLibrary'"
-    }
-
-    private BinaryNamingScheme createNamingScheme(def parentName, def type, def dimensions) {
-        return new DefaultBinaryNamingScheme(parentName, type, dimensions)
-    }
-}
diff --git a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy b/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy
deleted file mode 100644
index 0b861b7..0000000
--- a/subprojects/language-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy
+++ /dev/null
@@ -1,29 +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.language.base.internal
-
-import org.gradle.internal.reflect.Instantiator
-import spock.lang.Specification
-
-class DefaultFunctionalSourceSetTest extends Specification {
-    def "has reasonable string representation"() {
-        def sourceSet = new DefaultFunctionalSourceSet("main", Stub(Instantiator))
-
-        expect:
-        sourceSet.toString() == /source set 'main'/
-    }
-}
diff --git a/subprojects/language-groovy/language-groovy.gradle b/subprojects/language-groovy/language-groovy.gradle
new file mode 100644
index 0000000..ae89a7d
--- /dev/null
+++ b/subprojects/language-groovy/language-groovy.gradle
@@ -0,0 +1,13 @@
+dependencies {
+    compile project(":platformJvm")
+    compile project(":languageJava")
+
+    testCompile libraries.groovy
+
+    // TODO - get rid of this cycle
+    integTestRuntime project(':plugins')
+}
+
+strictCompile()
+useClassycle(exclude: ["org/gradle/api/internal/tasks/compile/**"])
+useTestFixtures()
diff --git a/subprojects/language-groovy/src/integTest/groovy/org/gradle/groovy/GroovyDocIntegrationTest.groovy b/subprojects/language-groovy/src/integTest/groovy/org/gradle/groovy/GroovyDocIntegrationTest.groovy
new file mode 100644
index 0000000..760872d
--- /dev/null
+++ b/subprojects/language-groovy/src/integTest/groovy/org/gradle/groovy/GroovyDocIntegrationTest.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.groovy
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetVersions
+import spock.lang.Issue
+
+ at TargetVersions(['1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.2.2', '2.3.9', '2.4.0'])
+class GroovyDocIntegrationTest extends MultiVersionIntegrationSpec {
+
+    @Issue("https://issues.gradle.org//browse/GRADLE-3116")
+    def "can run groovydoc"() {
+        when:
+        buildFile << """
+            apply plugin: "groovy"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                compile "org.codehaus.groovy:${module}:${version}"
+            }
+        """
+
+        file("src/main/groovy/pkg/Thing.groovy") << """
+            package pkg
+
+            class Thing {}
+        """
+
+        then:
+        succeeds "groovydoc"
+
+        and:
+        def text = file('build/docs/groovydoc/pkg/Thing.html').text
+        def generatedBy = (text =~ /Generated by groovydoc \((.+?)\)/)
+
+        generatedBy // did match
+        generatedBy[0][1] == version
+
+        where:
+        module << ['groovy']
+    }
+
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
new file mode 100644
index 0000000..46f0666
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
@@ -0,0 +1,242 @@
+/*
+ * 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.tasks.compile;
+
+import com.google.common.collect.Iterables;
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyShell;
+import groovy.lang.GroovySystem;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
+import org.codehaus.groovy.tools.javac.JavaCompiler;
+import org.codehaus.groovy.tools.javac.JavaCompilerFactory;
+import org.gradle.api.GradleException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.classloader.DefaultClassLoaderFactory;
+import org.gradle.internal.classloader.FilteringClassLoader;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ApiGroovyCompiler implements org.gradle.language.base.internal.compile.Compiler<GroovyJavaJointCompileSpec>, Serializable {
+    private final Compiler<JavaCompileSpec> javaCompiler;
+
+    public ApiGroovyCompiler(Compiler<JavaCompileSpec> javaCompiler) {
+        this.javaCompiler = javaCompiler;
+    }
+
+    public WorkResult execute(final GroovyJavaJointCompileSpec spec) {
+        CompilerConfiguration configuration = new CompilerConfiguration();
+        configuration.setVerbose(spec.getGroovyCompileOptions().isVerbose());
+        configuration.setSourceEncoding(spec.getGroovyCompileOptions().getEncoding());
+        configuration.setTargetBytecode(spec.getTargetCompatibility());
+        configuration.setTargetDirectory(spec.getDestinationDir());
+        canonicalizeValues(spec.getGroovyCompileOptions().getOptimizationOptions());
+        if (spec.getGroovyCompileOptions().getConfigurationScript() != null) {
+            applyConfigurationScript(spec.getGroovyCompileOptions().getConfigurationScript(), configuration);
+        }
+        try {
+            configuration.setOptimizationOptions(spec.getGroovyCompileOptions().getOptimizationOptions());
+        } catch (NoSuchMethodError ignored) { /* method was only introduced in Groovy 1.8 */ }
+        Map<String, Object> jointCompilationOptions = new HashMap<String, Object>();
+        final File stubDir = spec.getGroovyCompileOptions().getStubDir();
+        jointCompilationOptions.put("stubDir", stubDir);
+        jointCompilationOptions.put("keepStubs", spec.getGroovyCompileOptions().isKeepStubs());
+        configuration.setJointCompilationOptions(jointCompilationOptions);
+
+        URLClassLoader classPathLoader = new GroovyCompileTransformingClassLoader(getExtClassLoader(), new DefaultClassPath(spec.getClasspath()));
+        GroovyClassLoader compileClasspathClassLoader = new GroovyClassLoader(classPathLoader, null);
+
+        FilteringClassLoader groovyCompilerClassLoader = new FilteringClassLoader(GroovyClassLoader.class.getClassLoader());
+        groovyCompilerClassLoader.allowPackage("org.codehaus.groovy");
+        groovyCompilerClassLoader.allowPackage("groovy");
+        // Disallow classes from Groovy Jar that reference external classes. Such classes must be loaded from astTransformClassLoader,
+        // or a NoClassDefFoundError will occur. Essentially this is drawing a line between the Groovy compiler and the Groovy
+        // library, albeit only for selected classes that run a high risk of being statically referenced from a transform.
+        groovyCompilerClassLoader.disallowClass("groovy.util.GroovyTestCase");
+        groovyCompilerClassLoader.disallowPackage("groovy.servlet");
+
+        // AST transforms need their own class loader that shares compiler classes with the compiler itself
+        final GroovyClassLoader astTransformClassLoader = new GroovyClassLoader(groovyCompilerClassLoader, null);
+        // can't delegate to compileClasspathLoader because this would result in ASTTransformation interface
+        // (which is implemented by the transform class) being loaded by compileClasspathClassLoader (which is
+        // where the transform class is loaded from)
+        for (File file : spec.getClasspath()) {
+            astTransformClassLoader.addClasspath(file.getPath());
+        }
+
+        JavaAwareCompilationUnit unit = new JavaAwareCompilationUnit(configuration, compileClasspathClassLoader) {
+            @Override
+            public GroovyClassLoader getTransformLoader() {
+                return astTransformClassLoader;
+            }
+        };
+
+        final boolean shouldProcessAnnotations = shouldProcessAnnotations(astTransformClassLoader, spec);
+        if (shouldProcessAnnotations) {
+            // If an annotation processor is detected, we need to force Java stub generation, so the we can process annotations on Groovy classes
+            // We are forcing stub generation by tricking the groovy compiler into thinking there are java files to compile.
+            // All java files are just passed to the compile method of the JavaCompiler and aren't processed internally by the Groovy Compiler.
+            // Since we're maintaining our own list of Java files independent what's passed by the Groovy compiler, adding a non-existant java file
+            // to the sources won't cause any issues.
+            unit.addSources(new File[] {new File("ForceStubGeneration.java")});
+        }
+
+        unit.addSources(Iterables.toArray(spec.getSource(), File.class));
+        unit.setCompilerFactory(new JavaCompilerFactory() {
+            public JavaCompiler createCompiler(final CompilerConfiguration config) {
+                return new JavaCompiler() {
+                    public void compile(List<String> files, CompilationUnit cu) {
+                        if (shouldProcessAnnotations) {
+                            // In order for the Groovy stubs to have annotation processors invoked against them, they must be compiled as source.
+                            // Classes compiled as a result of being on the -sourcepath do not have the annotation processor run against them
+                            spec.setSource(spec.getSource().plus(new SimpleFileCollection(stubDir).getAsFileTree()));
+                        } else {
+                            // When annotation processing isn't required, it's better to add the Groovy stubs as part of the source path.
+                            // This allows compilations to complete faster, because only the Groovy stubs that are needed by the java source are compiled.
+                            FileCollection sourcepath = new SimpleFileCollection(stubDir);
+                            if (spec.getCompileOptions().getSourcepath() != null) {
+                                sourcepath = spec.getCompileOptions().getSourcepath().plus(sourcepath);
+                            }
+                            spec.getCompileOptions().setSourcepath(sourcepath);
+                        }
+
+                        spec.setSource(spec.getSource().filter(new Spec<File>() {
+                            public boolean isSatisfiedBy(File file) {
+                                return file.getName().endsWith(".java");
+                            }
+                        }));
+
+                        try {
+                            javaCompiler.execute(spec);
+                        } catch (CompilationFailedException e) {
+                            cu.getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(), cu));
+                        }
+                    }
+                };
+            }
+        });
+
+        try {
+            unit.compile();
+        } catch (org.codehaus.groovy.control.CompilationFailedException e) {
+            System.err.println(e.getMessage());
+            throw new CompilationFailedException();
+        }
+
+        return new SimpleWorkResult(true);
+    }
+
+    private boolean shouldProcessAnnotations(ClassLoader classLoader, GroovyJavaJointCompileSpec spec) {
+        return !isAnnotationProcessingDisabled(spec)
+               && (isAnnotationProcessorOnClasspath(classLoader) || isDefaultAnnotationProcessorDiscoveryOverridden(spec));
+    }
+
+    private boolean isAnnotationProcessingDisabled(GroovyJavaJointCompileSpec spec) {
+        List<String> compilerArgs = spec.getCompileOptions().getCompilerArgs();
+        return compilerArgs.contains("-proc:none");
+    }
+
+    private boolean isAnnotationProcessorOnClasspath(ClassLoader classLoader) {
+        try {
+            Enumeration<URL> processorEntries = classLoader.getResources("META-INF/services/javax.annotation.processing.Processor");
+            return processorEntries.hasMoreElements();
+        } catch (IOException e) {
+            throw new GradleException("Failed to retrieve annotation processor metadata from classpath", e);
+        }
+    }
+
+    private boolean isDefaultAnnotationProcessorDiscoveryOverridden(GroovyJavaJointCompileSpec spec) {
+        List<String> compilerArgs = spec.getCompileOptions().getCompilerArgs();
+        return !Collections.disjoint(compilerArgs, Arrays.asList("-processorpath", "-processor"));
+    }
+
+    private void applyConfigurationScript(File configScript, CompilerConfiguration configuration) {
+        VersionNumber version = parseGroovyVersion();
+        if (version.compareTo(VersionNumber.parse("2.1")) < 0) {
+            throw new GradleException("Using a Groovy compiler configuration script requires Groovy 2.1+ but found Groovy " + version + "");
+        }
+        Binding binding = new Binding();
+        binding.setVariable("configuration", configuration);
+
+        CompilerConfiguration configuratorConfig = new CompilerConfiguration();
+        ImportCustomizer customizer = new ImportCustomizer();
+        customizer.addStaticStars("org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder");
+        configuratorConfig.addCompilationCustomizers(customizer);
+
+        GroovyShell shell = new GroovyShell(binding, configuratorConfig);
+        try {
+            shell.evaluate(configScript);
+        } catch (Exception e) {
+            throw new GradleException("Could not execute Groovy compiler configuration script: " + configScript.getAbsolutePath(), e);
+        }
+    }
+
+    private VersionNumber parseGroovyVersion() {
+        String version;
+        try {
+            version = GroovySystem.getVersion();
+        } catch (NoSuchMethodError e) {
+            // for Groovy <1.6, we need to call org.codehaus.groovy.runtime.InvokerHelper#getVersion
+            try {
+                Class<?> ih = Class.forName("org.codehaus.groovy.runtime.InvokerHelper");
+                Method getVersion = ih.getDeclaredMethod("getVersion");
+                version = (String) getVersion.invoke(ih);
+            } catch (Exception e1) {
+                throw new GradleException("Unable to determine Groovy version.", e1);
+            }
+        }
+        return VersionNumber.parse(version);
+    }
+
+    // Make sure that map only contains Boolean.TRUE and Boolean.FALSE values and no other Boolean instances.
+    // This is necessary because:
+    // 1. serialization/deserialization of the compile spec doesn't preserve Boolean.TRUE/Boolean.FALSE but creates new instances
+    // 1. org.codehaus.groovy.classgen.asm.WriterController makes identity comparisons
+    private void canonicalizeValues(Map<String, Boolean> options) {
+        for (String key : options.keySet()) {
+            // unboxing and boxing does the trick
+            boolean value = options.get(key);
+            options.put(key, value);
+        }
+    }
+
+    private ClassLoader getExtClassLoader() {
+        return new DefaultClassLoaderFactory().getIsolatedSystemClassLoader();
+    }
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java
new file mode 100644
index 0000000..79cbe72
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+public class CleaningGroovyCompiler extends CleaningJavaCompilerSupport<GroovyJavaJointCompileSpec> {
+    private final Compiler<GroovyJavaJointCompileSpec> compiler;
+    private final TaskOutputsInternal taskOutputs;
+
+    public CleaningGroovyCompiler(Compiler<GroovyJavaJointCompileSpec> compiler, TaskOutputsInternal taskOutputs) {
+        this.compiler = compiler;
+        this.taskOutputs = taskOutputs;
+    }
+
+    @Override
+    protected Compiler<GroovyJavaJointCompileSpec> getCompiler() {
+        return compiler;
+    }
+
+    @Override
+    protected StaleClassCleaner createCleaner(GroovyJavaJointCompileSpec spec) {
+        return new SimpleStaleClassCleaner(taskOutputs);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpec.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpec.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpec.java
rename to subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpec.java
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactory.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactory.java
new file mode 100644
index 0000000..8da2523
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.tasks.compile.CompileOptions;
+
+public class DefaultGroovyJavaJointCompileSpecFactory extends AbstractJavaCompileSpecFactory<DefaultGroovyJavaJointCompileSpec> {
+    public DefaultGroovyJavaJointCompileSpecFactory(CompileOptions compileOptions) {
+        super(compileOptions);
+    }
+
+    @Override
+    protected DefaultGroovyJavaJointCompileSpec getCommandLineSpec() {
+        return new DefaultCommandLineGroovyJavaJointCompileSpec();
+    }
+
+    @Override
+    protected DefaultGroovyJavaJointCompileSpec getForkingSpec() {
+        return new DefaultForkingGroovyJavaJointCompileSpec();
+    }
+
+    @Override
+    protected DefaultGroovyJavaJointCompileSpec getDefaultSpec() {
+        return new DefaultGroovyJavaJointCompileSpec();
+    }
+
+    private static class DefaultCommandLineGroovyJavaJointCompileSpec extends DefaultGroovyJavaJointCompileSpec implements CommandLineJavaCompileSpec {
+    }
+
+    private static class DefaultForkingGroovyJavaJointCompileSpec extends DefaultGroovyJavaJointCompileSpec implements ForkingJavaCompileSpec {
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileSpec.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompileSpec.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileSpec.java
rename to subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompileSpec.java
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java
new file mode 100644
index 0000000..01de2f5
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tasks.compile;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+import org.gradle.internal.classloader.TransformingClassLoader;
+import org.gradle.internal.classpath.ClassPath;
+import org.objectweb.asm.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Transforms @GroovyASTTransformationClass(classes = {classLiterals}) into @GroovyASTTransformationClass([classNames]),
+ * to work around GROOVY-5416.
+ */
+class GroovyCompileTransformingClassLoader extends TransformingClassLoader {
+    private static final String ANNOTATION_DESCRIPTOR = Type.getType(GroovyASTTransformationClass.class).getDescriptor();
+
+    public GroovyCompileTransformingClassLoader(ClassLoader parent, ClassPath classPath) {
+        super(parent, classPath);
+    }
+
+    @Override
+    protected byte[] transform(byte[] bytes) {
+        // First scan for annotation, and short circuit transformation if not present
+        ClassReader classReader = new ClassReader(bytes);
+
+        AnnotationDetector detector = new AnnotationDetector();
+        classReader.accept(detector, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
+        if (!detector.found) {
+            return bytes;
+        }
+
+        ClassWriter classWriter = new ClassWriter(0);
+        classReader.accept(new TransformingAdapter(classWriter), 0);
+        bytes = classWriter.toByteArray();
+        return bytes;
+    }
+
+    private static class AnnotationDetector extends ClassVisitor {
+        private boolean found;
+
+        private AnnotationDetector() {
+            super(Opcodes.ASM5);
+        }
+
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            if (desc.equals(ANNOTATION_DESCRIPTOR)) {
+                found = true;
+            }
+            return null;
+        }
+    }
+
+    private static class TransformingAdapter extends ClassVisitor {
+        public TransformingAdapter(ClassWriter classWriter) {
+            super(Opcodes.ASM5, classWriter);
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+            if (desc.equals(ANNOTATION_DESCRIPTOR)) {
+                return new AnnotationTransformingVisitor(super.visitAnnotation(desc, visible));
+            }
+            return super.visitAnnotation(desc, visible);
+        }
+
+        private static class AnnotationTransformingVisitor extends AnnotationVisitor {
+            private final List<String> names = new ArrayList<String>();
+
+            public AnnotationTransformingVisitor(AnnotationVisitor annotationVisitor) {
+                super(Opcodes.ASM5, annotationVisitor);
+            }
+
+            public AnnotationVisitor visitArray(String name) {
+                if (name.equals("classes")) {
+                    return new AnnotationVisitor(Opcodes.ASM5){
+                        @Override
+                        public void visit(String name, Object value) {
+                            Type type = (Type) value;
+                            names.add(type.getClassName());
+                        }
+                    };
+                } else if (name.equals("value")) {
+                    return new AnnotationVisitor(Opcodes.ASM5) {
+                        @Override
+                        public void visit(String name, Object value) {
+                            String type = (String) value;
+                            names.add(type);
+                        }
+                    };
+                } else {
+                    return super.visitArray(name);
+                }
+            }
+
+            public void visitEnd() {
+                if (!names.isEmpty()) {
+                    AnnotationVisitor visitor = super.visitArray("value");
+                    for (String name : names) {
+                        visitor.visit(null, name);
+                    }
+                    visitor.visitEnd();
+                }
+                super.visitEnd();
+            }
+        }
+    }
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java
new file mode 100644
index 0000000..f6045f7
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.tasks.compile;
+
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.api.internal.tasks.compile.daemon.DaemonGroovyCompiler;
+import org.gradle.api.internal.tasks.compile.daemon.InProcessCompilerDaemonFactory;
+import org.gradle.api.tasks.compile.GroovyCompileOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerFactory;
+
+public class GroovyCompilerFactory implements CompilerFactory<GroovyJavaJointCompileSpec> {
+    private final ProjectInternal project;
+    private final JavaCompilerFactory javaCompilerFactory;
+    private final CompilerDaemonManager compilerDaemonFactory;
+    private final InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory;
+
+    public GroovyCompilerFactory(ProjectInternal project, JavaCompilerFactory javaCompilerFactory, CompilerDaemonManager compilerDaemonManager,
+                                 InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory) {
+        this.project = project;
+        this.javaCompilerFactory = javaCompilerFactory;
+        this.compilerDaemonFactory = compilerDaemonManager;
+        this.inProcessCompilerDaemonFactory = inProcessCompilerDaemonFactory;
+    }
+
+    public Compiler<GroovyJavaJointCompileSpec> newCompiler(GroovyJavaJointCompileSpec spec) {
+        GroovyCompileOptions groovyOptions = spec.getGroovyCompileOptions();
+        Compiler<JavaCompileSpec> javaCompiler = javaCompilerFactory.createForJointCompilation(spec.getClass());
+        Compiler<GroovyJavaJointCompileSpec> groovyCompiler = new ApiGroovyCompiler(javaCompiler);
+        CompilerDaemonFactory daemonFactory;
+        if (groovyOptions.isFork()) {
+            daemonFactory = compilerDaemonFactory;
+        } else {
+            daemonFactory = inProcessCompilerDaemonFactory;
+        }
+        groovyCompiler = new DaemonGroovyCompiler(project.getRootProject().getProjectDir(), groovyCompiler, project.getServices().get(ClassPathRegistry.class), daemonFactory);
+        return new NormalizingGroovyCompiler(groovyCompiler);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompileSpec.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompileSpec.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompileSpec.java
rename to subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompileSpec.java
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompiler.java
new file mode 100644
index 0000000..6bb631f
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/GroovyJavaJointCompiler.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+/**
+ * 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.
+ */
+ at Deprecated
+public interface GroovyJavaJointCompiler {
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java
new file mode 100644
index 0000000..0777de3
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java
@@ -0,0 +1,123 @@
+/*
+ * 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.tasks.compile;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Groovy {@link Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
+ */
+public class NormalizingGroovyCompiler implements Compiler<GroovyJavaJointCompileSpec> {
+    private static final Logger LOGGER = Logging.getLogger(NormalizingGroovyCompiler.class);
+    private final Compiler<GroovyJavaJointCompileSpec> delegate;
+
+    public NormalizingGroovyCompiler(Compiler<GroovyJavaJointCompileSpec> delegate) {
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(GroovyJavaJointCompileSpec spec) {
+        resolveAndFilterSourceFiles(spec);
+        resolveClasspath(spec);
+        resolveNonStringsInCompilerArgs(spec);
+        logSourceFiles(spec);
+        logCompilerArguments(spec);
+        return delegateAndHandleErrors(spec);
+    }
+
+    private void resolveAndFilterSourceFiles(final GroovyJavaJointCompileSpec spec) {
+        FileCollection filtered = spec.getSource().filter(new Spec<File>() {
+            public boolean isSatisfiedBy(File element) {
+                for (String fileExtension : spec.getGroovyCompileOptions().getFileExtensions()) {
+                    if (element.getName().endsWith("." + fileExtension)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
+
+        spec.setSource(new SimpleFileCollection(filtered.getFiles()));
+    }
+
+    private void resolveClasspath(GroovyJavaJointCompileSpec spec) {
+        // Necessary for Groovy compilation to pick up output of regular and joint Java compilation,
+        // and for joint Java compilation to pick up the output of regular Java compilation.
+        // Assumes that output of regular Java compilation (which is not under this task's control) also goes
+        // into spec.getDestinationDir(). We could configure this on source set level, but then spec.getDestinationDir()
+        // would end up on the compile class path of every compile task for that source set, which may not be desirable.
+        ArrayList<File> classPath = Lists.newArrayList(spec.getClasspath());
+        classPath.add(spec.getDestinationDir());
+        spec.setClasspath(classPath);
+
+        spec.setGroovyClasspath(Lists.newArrayList(spec.getGroovyClasspath()));
+    }
+
+    private void resolveNonStringsInCompilerArgs(GroovyJavaJointCompileSpec spec) {
+        // in particular, this is about GStrings
+        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
+    }
+
+    private void logSourceFiles(GroovyJavaJointCompileSpec spec) {
+        if (!spec.getGroovyCompileOptions().isListFiles()) {
+            return;
+        }
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("Source files to be compiled:");
+        for (File file : spec.getSource()) {
+            builder.append('\n');
+            builder.append(file);
+        }
+
+        LOGGER.quiet(builder.toString());
+    }
+
+    private void logCompilerArguments(GroovyJavaJointCompileSpec spec) {
+        if (!LOGGER.isDebugEnabled()) {
+            return;
+        }
+
+        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
+        String joinedArgs = Joiner.on(' ').join(compilerArgs);
+        LOGGER.debug("Java compiler arguments: {}", joinedArgs);
+    }
+
+    private WorkResult delegateAndHandleErrors(GroovyJavaJointCompileSpec spec) {
+        try {
+            return delegate.execute(spec);
+        } catch (CompilationFailedException e) {
+            if (spec.getCompileOptions().isFailOnError()) {
+                throw e;
+            }
+            LOGGER.debug("Ignoring compilation failure.");
+            return new SimpleWorkResult(false);
+        }
+    }
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.java
new file mode 100644
index 0000000..5fd2d89
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.java
@@ -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.api.internal.tasks.compile.daemon;
+
+import com.google.common.collect.Iterables;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.tasks.compile.GroovyJavaJointCompileSpec;
+import org.gradle.api.tasks.compile.ForkOptions;
+import org.gradle.api.tasks.compile.GroovyForkOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class DaemonGroovyCompiler extends AbstractDaemonCompiler<GroovyJavaJointCompileSpec> {
+    private final ClassPathRegistry classPathRegistry;
+
+    public DaemonGroovyCompiler(File daemonWorkingDir, Compiler<GroovyJavaJointCompileSpec> delegate, ClassPathRegistry classPathRegistry, CompilerDaemonFactory daemonFactory) {
+        super(daemonWorkingDir, delegate, daemonFactory);
+        this.classPathRegistry = classPathRegistry;
+    }
+
+    @Override
+    protected DaemonForkOptions toDaemonOptions(GroovyJavaJointCompileSpec spec) {
+        return createJavaForkOptions(spec).mergeWith(createGroovyForkOptions(spec));
+    }
+    
+    private DaemonForkOptions createJavaForkOptions(GroovyJavaJointCompileSpec spec) {
+        ForkOptions options = spec.getCompileOptions().getForkOptions();
+        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(), options.getJvmArgs());
+    }
+
+    private DaemonForkOptions createGroovyForkOptions(GroovyJavaJointCompileSpec spec) {
+        GroovyForkOptions options = spec.getGroovyCompileOptions().getForkOptions();
+        // Ant is optional dependency of groovy(-all) module but mandatory dependency of Groovy compiler;
+        // that's why we add it here. The following assumes that any Groovy compiler version supported by Gradle
+        // is compatible with Gradle's current Ant version.
+        Collection<File> antFiles = classPathRegistry.getClassPath("ANT").getAsFiles();
+        Iterable<File> groovyFiles = Iterables.concat(spec.getGroovyClasspath(), antFiles);
+        List<String> groovyPackages = Arrays.asList("groovy", "org.codehaus.groovy", "groovyjarjarantlr", "groovyjarjarasm", "groovyjarjarcommonscli", "org.apache.tools.ant", "com.sun.tools.javac");
+        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(),
+                options.getJvmArgs(), groovyFiles, groovyPackages);
+    }
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompile.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompile.java
new file mode 100644
index 0000000..8674341
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompile.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.compile.*;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.api.internal.tasks.compile.daemon.InProcessCompilerDaemonFactory;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+
+/**
+ * Compiles Groovy source files, and optionally, Java source files.
+ */
+public class GroovyCompile extends AbstractCompile {
+    private Compiler<GroovyJavaJointCompileSpec> compiler;
+    private FileCollection groovyClasspath;
+    private final CompileOptions compileOptions = new CompileOptions();
+    private final GroovyCompileOptions groovyCompileOptions = new GroovyCompileOptions();
+
+    @TaskAction
+    protected void compile() {
+        checkGroovyClasspathIsNonEmpty();
+        DefaultGroovyJavaJointCompileSpec spec = createSpec();
+        WorkResult result = getCompiler(spec).execute(spec);
+        setDidWork(result.getDidWork());
+    }
+
+    private Compiler<GroovyJavaJointCompileSpec> getCompiler(GroovyJavaJointCompileSpec spec) {
+        if (compiler == null) {
+            ProjectInternal projectInternal = (ProjectInternal) getProject();
+            CompilerDaemonManager compilerDaemonManager = getServices().get(CompilerDaemonManager.class);
+            InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory = getServices().get(InProcessCompilerDaemonFactory.class);
+            JavaCompilerFactory javaCompilerFactory = getServices().get(JavaCompilerFactory.class);
+            GroovyCompilerFactory groovyCompilerFactory = new GroovyCompilerFactory(projectInternal, javaCompilerFactory, compilerDaemonManager, inProcessCompilerDaemonFactory);
+            Compiler<GroovyJavaJointCompileSpec> delegatingCompiler = groovyCompilerFactory.newCompiler(spec);
+            compiler = new CleaningGroovyCompiler(delegatingCompiler, getOutputs());
+        }
+        return compiler;
+    }
+
+    private DefaultGroovyJavaJointCompileSpec createSpec() {
+        DefaultGroovyJavaJointCompileSpec spec = new DefaultGroovyJavaJointCompileSpecFactory(compileOptions).create();
+        spec.setSource(getSource());
+        spec.setDestinationDir(getDestinationDir());
+        spec.setWorkingDir(getProject().getProjectDir());
+        spec.setTempDir(getTemporaryDir());
+        spec.setClasspath(getClasspath());
+        spec.setSourceCompatibility(getSourceCompatibility());
+        spec.setTargetCompatibility(getTargetCompatibility());
+        spec.setGroovyClasspath(getGroovyClasspath());
+        spec.setCompileOptions(compileOptions);
+        spec.setGroovyCompileOptions(groovyCompileOptions);
+        if (spec.getGroovyCompileOptions().getStubDir() == null) {
+            File dir = new File(getTemporaryDir(), "groovy-java-stubs");
+            GFileUtils.mkdirs(dir);
+            spec.getGroovyCompileOptions().setStubDir(dir);
+        }
+        return spec;
+    }
+
+    private void checkGroovyClasspathIsNonEmpty() {
+        if (getGroovyClasspath().isEmpty()) {
+            throw new InvalidUserDataException("'" + getName() + ".groovyClasspath' must not be empty. If a Groovy compile dependency is provided, "
+                    + "the 'groovy-base' plugin will attempt to configure 'groovyClasspath' automatically. Alternatively, you may configure 'groovyClasspath' explicitly.");
+        }
+    }
+
+    /**
+     * Gets the options for the Groovy compilation. To set specific options for the nested Java compilation, use {@link
+     * #getOptions()}.
+     *
+     * @return The Groovy compile options. Never returns null.
+     */
+    @Nested
+    public GroovyCompileOptions getGroovyOptions() {
+        return groovyCompileOptions;
+    }
+
+    /**
+     * Returns the options for Java compilation.
+     *
+     * @return The Java compile options. Never returns null.
+     */
+    @Nested
+    public CompileOptions getOptions() {
+        return compileOptions;
+    }
+
+    /**
+     * Returns the classpath containing the version of Groovy to use for compilation.
+     *
+     * @return The classpath.
+     */
+    @InputFiles
+    public FileCollection getGroovyClasspath() {
+        return groovyClasspath;
+    }
+
+    /**
+     * Sets the classpath containing the version of Groovy to use for compilation.
+     *
+     * @param groovyClasspath The classpath. Must not be null.
+     */
+    public void setGroovyClasspath(FileCollection groovyClasspath) {
+        this.groovyClasspath = groovyClasspath;
+    }
+
+    public Compiler<GroovyJavaJointCompileSpec> getCompiler() {
+        return getCompiler(createSpec());
+    }
+
+    public void setCompiler(Compiler<GroovyJavaJointCompileSpec> compiler) {
+        this.compiler = compiler;
+    }
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompileOptions.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompileOptions.java
new file mode 100644
index 0000000..b87ad2f
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyCompileOptions.java
@@ -0,0 +1,308 @@
+/*
+ * 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.tasks.compile;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Compilation options to be passed to the Groovy compiler.
+ */
+public class GroovyCompileOptions extends AbstractOptions {
+    private static final long serialVersionUID = 0;
+    private static final ImmutableSet<String> EXCLUDE_FROM_ANT_PROPERTIES =
+            ImmutableSet.of("forkOptions", "optimizationOptions", "stubDir", "keepStubs", "fileExtensions");
+
+    private boolean failOnError = true;
+
+    private boolean verbose;
+
+    private boolean listFiles;
+
+    private String encoding = "UTF-8";
+
+    private boolean fork = true;
+
+    private boolean keepStubs;
+
+    private List<String> fileExtensions = ImmutableList.of("java", "groovy");
+
+    private GroovyForkOptions forkOptions = new GroovyForkOptions();
+
+    private Map<String, Boolean> optimizationOptions = Maps.newHashMap();
+
+    private File stubDir;
+
+    private File configurationScript;
+
+    /**
+     * Tells whether the compilation task should fail if compile errors occurred. Defaults to {@code true}.
+     */
+    public boolean isFailOnError() {
+        return failOnError;
+    }
+
+    /**
+     * Sets whether the compilation task should fail if compile errors occurred. Defaults to {@code true}.
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Tells whether to turn on verbose output. Defaults to {@code false}.
+     */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Sets whether to turn on verbose output. Defaults to {@code false}.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Tells whether to print which source files are to be compiled. Defaults to {@code false}.
+     */
+    public boolean isListFiles() {
+        return listFiles;
+    }
+
+    /**
+     * Sets whether to print which source files are to be compiled. Defaults to {@code false}.
+     */
+    public void setListFiles(boolean listFiles) {
+        this.listFiles = listFiles;
+    }
+
+    /**
+     * Tells the source encoding. Defaults to {@code UTF-8}.
+     */
+    @Input
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Sets the source encoding. Defaults to {@code UTF-8}.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Tells whether to run the Groovy compiler in a separate process. Defaults to {@code true}.
+     */
+    public boolean isFork() {
+        return fork;
+    }
+
+    /**
+     * Sets whether to run the Groovy compiler in a separate process. Defaults to {@code true}.
+     */
+    public void setFork(boolean fork) {
+        this.fork = fork;
+    }
+
+    /**
+     * A Groovy script file that configures the compiler, allowing extensive control over how the code is compiled.
+     * <p>
+     * The script is executed as Groovy code, with the following context:
+     * <ul>
+     * <li>The instance of <a href="http://docs.groovy-lang.org/latest/html/gapi/org/codehaus/groovy/control/CompilerConfiguration.html">CompilerConfiguration</a> available as the {@code configuration} variable.</li>
+     * <li>All static members of <a href="http://docs.groovy-lang.org/latest/html/gapi/org/codehaus/groovy/control/customizers/builder/CompilerCustomizationBuilder.html">CompilerCustomizationBuilder</a> pre imported.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * This facilitates the following pattern:
+     * <pre>
+     * withConfig(configuration) {
+     *   // use compiler configuration DSL here
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * For example, to activate type checking for all Groovy classes…
+     * <pre>
+     * import groovy.transform.TypeChecked
+     *
+     * withConfig(configuration) {
+     *     ast(TypeChecked)
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * Please see <a href="http://groovy.codehaus.org/Advanced+compiler+configuration#Advancedcompilerconfiguration-Thecustomizationbuilder">the Groovy compiler customization builder documentation</a>
+     * for more information about the compiler configuration DSL.
+     * </p>
+     * <p>
+     * <b>This feature is only available if compiling with Groovy 2.1 or later.</b>
+     * </p>
+     * @see <a href="http://docs.groovy-lang.org/latest/html/gapi/org/codehaus/groovy/control/CompilerConfiguration.html">CompilerConfiguration</a>
+     * @see <a href="http://docs.groovy-lang.org/latest/html/gapi/org/codehaus/groovy/control/customizers/builder/CompilerCustomizationBuilder.html">CompilerCustomizationBuilder</a>
+     */
+    @InputFile
+    @Incubating
+    @Optional
+    public File getConfigurationScript() {
+        return configurationScript;
+    }
+
+    /**
+     * Sets the path to the groovy configuration file.
+     *
+     * @see #getConfigurationScript()
+     */
+    @Incubating
+    public void setConfigurationScript(@Nullable File configurationFile) {
+        this.configurationScript = configurationFile;
+    }
+
+    /**
+     * Returns options for running the Groovy compiler in a separate process. These options only take effect
+     * if {@code fork} is set to {@code true}.
+     */
+    public GroovyForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    /**
+     * Sets options for running the Groovy compiler in a separate process. These options only take effect
+     * if {@code fork} is set to {@code true}.
+     */
+    public void setForkOptions(GroovyForkOptions forkOptions) {
+        this.forkOptions = forkOptions;
+    }
+
+    /**
+     * Returns optimization options for the Groovy compiler. Allowed values for an option are {@code true} and {@code false}.
+     * Only takes effect when compiling against Groovy 1.8 or higher.
+     *
+     * <p>Known options are:
+     *
+     * <dl>
+     *     <dt>indy
+     *     <dd>Use the invokedynamic bytecode instruction. Requires JDK7 or higher and Groovy 2.0 or higher. Disabled by default.
+     *     <dt>int
+     *     <dd>Optimize operations on primitive types (e.g. integers). Enabled by default.
+     *     <dt>all
+     *     <dd>Enable or disable all optimizations. Note that some optimizations might be mutually exclusive.
+     * </dl>
+     */
+    public Map<String, Boolean> getOptimizationOptions() {
+        return optimizationOptions;
+    }
+
+    /**
+     * Sets optimization options for the Groovy compiler. Allowed values for an option are {@code true} and {@code false}.
+     * Only takes effect when compiling against Groovy 1.8 or higher.
+     */
+    public void setOptimizationOptions(Map<String, Boolean> optimizationOptions) {
+        this.optimizationOptions = optimizationOptions;
+    }
+
+    /**
+     * Returns the directory where Java stubs for Groovy classes will be stored during Java/Groovy joint
+     * compilation. Defaults to {@code null}, in which case a temporary directory will be used.
+     */
+    public File getStubDir() {
+        return stubDir;
+    }
+
+    /**
+     * Sets the directory where Java stubs for Groovy classes will be stored during Java/Groovy joint
+     * compilation. Defaults to {@code null}, in which case a temporary directory will be used.
+     */
+    public void setStubDir(File stubDir) {
+        this.stubDir = stubDir;
+    }
+
+    /**
+     * Returns the list of acceptable source file extensions. Only takes effect when compiling against
+     * Groovy 1.7 or higher. Defaults to {@code ImmutableList.of("java", "groovy")}.
+     */
+    @Input
+    @Incubating
+    public List<String> getFileExtensions() {
+        return fileExtensions;
+    }
+
+    /**
+     * Sets the list of acceptable source file extensions. Only takes effect when compiling against
+     * Groovy 1.7 or higher. Defaults to {@code ImmutableList.of("java", "groovy")}.
+     */
+    @Incubating
+    public void setFileExtensions(List<String> fileExtensions) {
+        this.fileExtensions = fileExtensions;
+    }
+
+    /**
+     * Tells whether Java stubs for Groovy classes generated during Java/Groovy joint compilation
+     * should be kept after compilation has completed. Useful for joint compilation debugging purposes.
+     * Defaults to {@code false}.
+     */
+    public boolean isKeepStubs() {
+        return keepStubs;
+    }
+
+    /**
+     * Sets whether Java stubs for Groovy classes generated during Java/Groovy joint compilation
+     * should be kept after compilation has completed. Useful for joint compilation debugging purposes.
+     * Defaults to {@code false}.
+     */
+    public void setKeepStubs(boolean keepStubs) {
+        this.keepStubs = keepStubs;
+    }
+
+    /**
+     * Convenience method to set {@link GroovyForkOptions} with named parameter syntax.
+     * Calling this method will set {@code fork} to {@code true}.
+     */
+    public GroovyCompileOptions fork(Map<String, Object> forkArgs) {
+        fork = true;
+        forkOptions.define(forkArgs);
+        return this;
+    }
+
+    @Override
+    protected boolean excludeFromAntProperties(String fieldName) {
+        return EXCLUDE_FROM_ANT_PROPERTIES.contains(fieldName);
+    }
+
+    /**
+     * Internal method.
+     */
+    public Map<String, Object> optionMap() {
+        Map<String, Object> map = super.optionMap();
+        map.putAll(forkOptions.optionMap());
+        if (optimizationOptions.containsKey("indy")) {
+            map.put("indy", optimizationOptions.get("indy"));
+        }
+        return map;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyForkOptions.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyForkOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyForkOptions.java
rename to subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/GroovyForkOptions.java
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/package-info.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/package-info.java
new file mode 100644
index 0000000..6b99da1
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/compile/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The compile tasks and options for Groovy.
+ */
+package org.gradle.api.tasks.compile;
\ No newline at end of file
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/AntGroovydoc.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/AntGroovydoc.java
new file mode 100644
index 0000000..abee629
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/AntGroovydoc.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.javadoc;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.project.IsolatedAntBuilder;
+import org.gradle.api.internal.project.ProjectInternal;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Generates groovy doc using Ant.
+ */
+public class AntGroovydoc {
+
+    private final IsolatedAntBuilder ant;
+
+    public AntGroovydoc(IsolatedAntBuilder ant, @SuppressWarnings("UnusedParameters") ClassPathRegistry ignored) {
+        this.ant = ant;
+    }
+
+    public void execute(final FileCollection source, File destDir, boolean use, String windowTitle, String docTitle, String header, String footer, String overview, boolean includePrivate, final Set<Groovydoc.Link> links, final Iterable<File> groovyClasspath, Iterable<File> classpath, Project project) {
+
+        final File tmpDir = new File(project.getBuildDir(), "tmp/groovydoc");
+        FileOperations fileOperations = (ProjectInternal) project;
+        fileOperations.delete(tmpDir);
+        fileOperations.copy(new Action<CopySpec>() {
+            public void execute(CopySpec copySpec) {
+                copySpec.from(source).into(tmpDir);
+            }
+        });
+
+        final Map<String, Object> args = Maps.newLinkedHashMap();
+        args.put("sourcepath", tmpDir.toString());
+        args.put("destdir", destDir);
+        args.put("use", use);
+        args.put("private", includePrivate);
+        putIfNotNull(args, "windowtitle", windowTitle);
+        putIfNotNull(args, "doctitle", docTitle);
+        putIfNotNull(args, "header", header);
+        putIfNotNull(args, "footer", footer);
+        putIfNotNull(args, "overview", overview);
+
+        List<File> combinedClasspath = ImmutableList.<File>builder()
+                .addAll(classpath)
+                .addAll(groovyClasspath)
+                .build();
+
+        ant.withClasspath(combinedClasspath).execute(new Closure<Object>(this, this) {
+            @SuppressWarnings("UnusedDeclaration")
+            public Object doCall(Object it) {
+                final GroovyObjectSupport antBuilder = (GroovyObjectSupport) it;
+
+                antBuilder.invokeMethod("taskdef", ImmutableMap.of(
+                        "name", "groovydoc",
+                        "classname", "org.codehaus.groovy.ant.Groovydoc"
+                ));
+
+                antBuilder.invokeMethod("groovydoc", new Object[]{args, new Closure<Object>(this, this) {
+                    public Object doCall(Object ignore) {
+                        for (Groovydoc.Link link : links) {
+                            antBuilder.invokeMethod("link", new Object[]{
+                                    ImmutableMap.of(
+                                            "packages", Joiner.on(",").join(link.getPackages()),
+                                            "href", link.getUrl()
+                                    )
+                            });
+                        }
+
+                        return null;
+                    }
+                }});
+
+                return null;
+            }
+        });
+    }
+
+    private static void putIfNotNull(Map<String, Object> map, String key, Object value) {
+        if (value != null) {
+            map.put(key, value);
+        }
+    }
+
+}
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/Groovydoc.java b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/Groovydoc.java
new file mode 100644
index 0000000..45d5988
--- /dev/null
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/tasks/javadoc/Groovydoc.java
@@ -0,0 +1,366 @@
+/*
+ * 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.javadoc;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.project.IsolatedAntBuilder;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.tasks.*;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.*;
+
+// This import must be here due to a clash in Java 8 between this and java.util.Optional.
+// Be careful running “Optimize Imports” as it will wipe this out.
+// If there's no import below this comment, this has happened.
+import org.gradle.api.tasks.Optional;
+
+/**
+ * <p>Generates HTML API documentation for Groovy source, and optionally, Java source.
+ *
+ * <p>This task uses Groovy's Groovydoc tool to generate the API documentation. Please note that the Groovydoc tool has
+ * some severe limitations at the moment (for example no doc for properties comments). The version of the Groovydoc that
+ * is used, is the one from the Groovy defined in the build script. Please note also, that the Groovydoc tool prints to
+ * System.out for many of its statements and does circumvents our logging currently.
+ */
+public class Groovydoc extends SourceTask {
+    private FileCollection groovyClasspath;
+
+    private FileCollection classpath;
+
+    private File destinationDir;
+
+    private AntGroovydoc antGroovydoc;
+
+    private boolean use;
+
+    private String windowTitle;
+
+    private String docTitle;
+
+    private String header;
+
+    private String footer;
+
+    private String overview;
+
+    private Set<Link> links = new HashSet<Link>();
+
+    boolean includePrivate;
+
+    public Groovydoc() {
+        getLogging().captureStandardOutput(LogLevel.INFO);
+    }
+
+    @TaskAction
+    protected void generate() {
+        checkGroovyClasspathNonEmpty(getGroovyClasspath().getFiles());
+        getAntGroovydoc().execute(getSource(), getDestinationDir(), isUse(), getWindowTitle(), getDocTitle(), getHeader(),
+                getFooter(), getOverview(), isIncludePrivate(), getLinks(), getGroovyClasspath(), getClasspath(), getProject());
+    }
+
+    private void checkGroovyClasspathNonEmpty(Collection<File> classpath) {
+        if (classpath.isEmpty()) {
+            throw new InvalidUserDataException("You must assign a Groovy library to the groovy configuration!");
+        }
+    }
+
+    /**
+     * Returns the directory to generate the documentation into.
+     *
+     * @return The directory to generate the documentation into
+     */
+    @OutputDirectory
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+
+    /**
+     * Sets the directory to generate the documentation into.
+     */
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    /**
+     * Returns the classpath containing the Groovy library to be used.
+     *
+     * @return The classpath containing the Groovy library to be used
+     */
+    @InputFiles
+    public FileCollection getGroovyClasspath() {
+        return groovyClasspath;
+    }
+
+    /**
+     * Sets the classpath containing the Groovy library to be used.
+     */
+    public void setGroovyClasspath(FileCollection groovyClasspath) {
+        this.groovyClasspath = groovyClasspath;
+    }
+
+    /**
+     * Returns the classpath used to locate classes referenced by the documented sources.
+     *
+     * @return The classpath used to locate classes referenced by the documented sources
+     */
+    @InputFiles
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Sets the classpath used to locate classes referenced by the documented sources.
+     */
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+    public AntGroovydoc getAntGroovydoc() {
+        if (antGroovydoc == null) {
+            IsolatedAntBuilder antBuilder = getServices().get(IsolatedAntBuilder.class);
+            ClassPathRegistry classPathRegistry = getServices().get(ClassPathRegistry.class);
+            antGroovydoc = new AntGroovydoc(antBuilder, classPathRegistry);
+        }
+        return antGroovydoc;
+    }
+
+    public void setAntGroovydoc(AntGroovydoc antGroovydoc) {
+        this.antGroovydoc = antGroovydoc;
+    }
+
+    /**
+     * Returns whether to create class and package usage pages.
+     */
+    @Input
+    public boolean isUse() {
+        return use;
+    }
+
+    /**
+     * Sets whether to create class and package usage pages.
+     */
+    public void setUse(boolean use) {
+        this.use = use;
+    }
+
+    /**
+     * Returns the browser window title for the documentation. Set to {@code null} when there is no window title.
+     */
+    @Input
+    @Optional
+    public String getWindowTitle() {
+        return windowTitle;
+    }
+
+    /**
+     * Sets the browser window title for the documentation.
+     *
+     * @param windowTitle A text for the windows title
+     */
+    public void setWindowTitle(String windowTitle) {
+        this.windowTitle = windowTitle;
+    }
+
+    /**
+     * Returns the title for the package index(first) page. Set to {@code null} when there is no document title.
+     */
+    @Input
+    @Optional
+    public String getDocTitle() {
+        return docTitle;
+    }
+
+    /**
+     * Sets title for the package index(first) page (optional).
+     *
+     * @param docTitle the docTitle as HTML
+     */
+    public void setDocTitle(String docTitle) {
+        this.docTitle = docTitle;
+    }
+
+    /**
+     * Returns the HTML header for each page. Set to {@code null} when there is no header.
+     */
+    @Input
+    @Optional
+    public String getHeader() {
+        return header;
+    }
+
+    /**
+     * Sets header text for each page (optional).
+     *
+     * @param header the header as HTML
+     */
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    /**
+     * Returns the HTML footer for each page. Set to {@code null} when there is no footer.
+     */
+    @Input
+    @Optional
+    public String getFooter() {
+        return footer;
+    }
+
+    /**
+     * Sets footer text for each page (optional).
+     *
+     * @param footer the footer as HTML
+     */
+    public void setFooter(String footer) {
+        this.footer = footer;
+    }
+
+    /**
+     * Returns a HTML file to be used for overview documentation. Set to {@code null} when there is no overview file.
+     */
+    public String getOverview() {
+        return overview;
+    }
+
+    /**
+     * Sets a HTML file to be used for overview documentation (optional).
+     */
+    public void setOverview(String overview) {
+        this.overview = overview;
+    }
+
+    /**
+     * Returns whether to include all classes and members (i.e. including private ones).
+     */
+    @Input
+    public boolean isIncludePrivate() {
+        return includePrivate;
+    }
+
+    /**
+     * Sets whether to include all classes and members (i.e. including private ones) if set to true.
+     */
+    public void setIncludePrivate(boolean includePrivate) {
+        this.includePrivate = includePrivate;
+    }
+
+    /**
+     * Returns the links to groovydoc/javadoc output at the given URL.
+     */
+    @Input
+    public Set<Link> getLinks() {
+        return Collections.unmodifiableSet(links);
+    }
+
+    /**
+     * Sets links to groovydoc/javadoc output at the given URL.
+     *
+     * @param links The links to set
+     * @see #link(String, String...)
+     */
+    public void setLinks(Set<Link> links) {
+        this.links = links;
+    }
+
+    /**
+     * Add links to groovydoc/javadoc output at the given URL.
+     *
+     * @param url Base URL of external site
+     * @param packages list of package prefixes
+     */
+    public void link(String url, String... packages) {
+        links.add(new Link(url, packages));
+    }
+
+    /**
+     * A Link class represent a link between groovydoc/javadoc output and url.
+     */
+    public static class Link implements Serializable {
+        private List<String> packages = new ArrayList<String>();
+        private String url;
+
+        /**
+         * Constructs a {@code Link}.
+         *
+         * @param url Base URL of external site
+         * @param packages list of package prefixes
+         */
+        public Link(String url, String... packages) {
+            throwExceptionIfNull(url, "Url must not be null");
+            if (packages.length == 0) {
+                throw new InvalidUserDataException("You must specify at least one package!");
+            }
+            for (String aPackage : packages) {
+                throwExceptionIfNull(aPackage, "A package must not be null");
+            }
+            this.packages = Arrays.asList(packages);
+            this.url = url;
+        }
+
+        private void throwExceptionIfNull(String value, String message) {
+            if (value == null) {
+                throw new InvalidUserDataException(message);
+            }
+        }
+
+        /**
+         * Returns a list of package prefixes to be linked with an external site.
+         */
+        public List<String> getPackages() {
+            return Collections.unmodifiableList(packages);
+        }
+
+        /**
+         * Returns the base url for the external site.
+         */
+        public String getUrl() {
+            return url;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Link link = (Link) o;
+
+            if (packages != null ? !packages.equals(link.packages) : link.packages != null) {
+                return false;
+            }
+            if (url != null ? !url.equals(link.url) : link.url != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = packages != null ? packages.hashCode() : 0;
+            result = 31 * result + (url != null ? url.hashCode() : 0);
+            return result;
+        }
+    }
+}
diff --git a/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactoryTest.groovy b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactoryTest.groovy
new file mode 100644
index 0000000..193781e
--- /dev/null
+++ b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultGroovyJavaJointCompileSpecFactoryTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.api.tasks.compile.CompileOptions
+import spock.lang.Specification
+
+
+class DefaultGroovyJavaJointCompileSpecFactoryTest extends Specification {
+    def "produces correct spec type" () {
+        CompileOptions options = new CompileOptions()
+        options.fork = fork
+        options.forkOptions.executable = executable
+        DefaultGroovyJavaJointCompileSpecFactory factory = new DefaultGroovyJavaJointCompileSpecFactory(options)
+
+        when:
+        def spec = factory.create()
+
+        then:
+        spec instanceof DefaultGroovyJavaJointCompileSpec
+        ForkingJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsForking
+        CommandLineJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsCommandLine
+
+        where:
+        fork  | executable | implementsForking | implementsCommandLine
+        false | null       | false             | false
+        true  | null       | true              | false
+        true  | "X"        | false             | true
+    }
+}
diff --git a/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy
new file mode 100644
index 0000000..725e04f
--- /dev/null
+++ b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy
@@ -0,0 +1,96 @@
+/*
+ * 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.tasks.compile
+
+import spock.lang.Specification
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+import org.gradle.internal.classloader.ClasspathUtil
+import org.gradle.internal.classpath.DefaultClassPath
+
+class GroovyCompileTransformingClassLoaderTest extends Specification {
+    GroovyCompileTransformingClassLoader loader
+    Class<?> classAnnotation
+
+    def setup() {
+        def classPath = new DefaultClassPath(ClasspathUtil.getClasspathForClass(getClass()), ClasspathUtil.getClasspathForClass(GroovyASTTransformationClass))
+        loader = new GroovyCompileTransformingClassLoader(null, classPath)
+        classAnnotation = loader.loadClass(GroovyASTTransformationClass.name)
+    }
+
+    def "loads class annotated with transformer name"() {
+        expect:
+        def cl = loader.loadClass(WithNameSpecified.name)
+        def annotation = cl.getAnnotation(classAnnotation)
+        annotation.value() == ['some-type'] as String[]
+        annotation.classes() == [] as Class[]
+    }
+
+    def "loads class annotated with transformer names"() {
+        expect:
+        def cl = loader.loadClass(WithNamesSpecified.name)
+        def annotation = cl.getAnnotation(classAnnotation)
+        annotation.value() == ['some-type', 'some-other-type'] as String[]
+        annotation.classes() == [] as Class[]
+    }
+
+    def "loads class annotated with transformer class"() {
+        expect:
+        def cl = loader.loadClass(WithClassSpecified.name)
+        def annotation = cl.getAnnotation(classAnnotation)
+        annotation.value() == [Transformer.name] as String[]
+        annotation.classes() == [] as Class[]
+    }
+
+    def "loads class annotated with transformer classes"() {
+        expect:
+        def cl = loader.loadClass(WithClassesSpecified.name)
+        def annotation = cl.getAnnotation(classAnnotation)
+        annotation.value() == [Transformer.name, Runnable.name] as String[]
+        annotation.classes() == [] as Class[]
+    }
+
+    def "loads class annotated with transformer names and classes"() {
+        expect:
+        def cl = loader.loadClass(WithBothSpecified.name)
+        def annotation = cl.getAnnotation(classAnnotation)
+        annotation.value() as Set == ["some-type", Transformer.name, Runnable.name] as Set
+        annotation.classes() == [] as Class[]
+    }
+
+    static class Transformer {
+    }
+}
+
+ at GroovyASTTransformationClass("some-type")
+ at interface WithNameSpecified {
+}
+
+ at GroovyASTTransformationClass(["some-type", "some-other-type"])
+ at interface WithNamesSpecified {
+}
+
+ at GroovyASTTransformationClass(classes = [GroovyCompileTransformingClassLoaderTest.Transformer])
+ at interface WithClassSpecified {
+}
+
+ at GroovyASTTransformationClass(classes = [GroovyCompileTransformingClassLoaderTest.Transformer, Runnable])
+ at interface WithClassesSpecified {
+}
+
+ at GroovyASTTransformationClass(value = "some-type", classes = [GroovyCompileTransformingClassLoaderTest.Transformer, Runnable])
+ at interface WithBothSpecified {
+}
diff --git a/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy
new file mode 100644
index 0000000..27a27e3
--- /dev/null
+++ b/subprojects/language-groovy/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy
@@ -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.tasks.compile
+import groovy.transform.InheritConstructors
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.tasks.compile.CompileOptions
+import org.gradle.api.tasks.compile.GroovyCompileOptions
+import spock.lang.Specification
+
+class NormalizingGroovyCompilerTest extends Specification {
+    org.gradle.language.base.internal.compile.Compiler<GroovyJavaJointCompileSpec> target = Mock()
+    DefaultGroovyJavaJointCompileSpec spec = new DefaultGroovyJavaJointCompileSpec()
+    NormalizingGroovyCompiler compiler = new NormalizingGroovyCompiler(target)
+    
+    def setup() {
+        spec.classpath = files('Dep1.jar', 'Dep2.jar', 'Dep3.jar')
+        spec.groovyClasspath = spec.classpath
+        spec.source = files('House.scala', 'Person1.java', 'package.html', 'Person2.groovy')
+        spec.destinationDir = new File("destinationDir")
+        spec.compileOptions = new CompileOptions()
+        spec.groovyCompileOptions = new GroovyCompileOptions()
+    }
+
+    def "silently excludes source files not ending in .java or .groovy by default"() {
+        when:
+        compiler.execute(spec)
+
+        then:
+        1 * target.execute(spec) >> {
+            assert spec.source.files == files('Person1.java', 'Person2.groovy').files
+        }
+    }
+
+    def "excludes source files that have extension different from specified by fileExtensions option"() {
+        spec.groovyCompileOptions.fileExtensions = ['html']
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        1 * target.execute(spec) >> {
+            assert spec.source.files == files('package.html').files
+        }
+    }
+
+    private files(String... paths) {
+        new TestFileCollection(paths.collect { new File(it) })
+    }
+
+    // file collection whose type is distinguishable from SimpleFileCollection
+    @InheritConstructors
+    static class TestFileCollection extends SimpleFileCollection {} 
+}
diff --git a/subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy b/subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy
new file mode 100644
index 0000000..c9d675b
--- /dev/null
+++ b/subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * 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.tasks.compile
+
+import org.junit.Before
+import org.junit.Test
+
+import static org.junit.Assert.*
+
+class GroovyCompileOptionsTest {
+    static final Map TEST_FORK_OPTION_MAP = [someForkOption: 'someForkOptionValue']
+
+    GroovyCompileOptions compileOptions
+
+    @Before public void setUp()  {
+        compileOptions = new GroovyCompileOptions()
+        compileOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as GroovyForkOptions
+    }
+
+    @Test public void testCompileOptions() {
+        assertTrue(compileOptions.failOnError)
+        assertFalse(compileOptions.listFiles)
+        assertFalse(compileOptions.verbose)
+        assertTrue(compileOptions.fork)
+        assertEquals(['java', 'groovy'], compileOptions.fileExtensions)
+        assertEquals('UTF-8', compileOptions.encoding)
+        assertNotNull(compileOptions.forkOptions)
+        assertNull(compileOptions.configurationScript)
+    }
+
+    @Test public void testOptionMapForForkOptions() {
+        Map optionMap = compileOptions.optionMap()
+        assertEquals(optionMap.subMap(TEST_FORK_OPTION_MAP.keySet()), TEST_FORK_OPTION_MAP)
+    }
+
+    @Test public void testOptionMapWithTrueFalseValues() {
+        Map booleans = [
+                failOnError: 'failOnError',
+                verbose: 'verbose',
+                listFiles: 'listFiles',
+                fork: 'fork'
+        ]
+        booleans.keySet().each {compileOptions."$it" = true}
+        Map optionMap = compileOptions.optionMap()
+        booleans.values().each {
+            if (it.equals('nowarn')) {
+                assertEquals(false, optionMap[it])
+            } else {
+                assertEquals(true, optionMap[it])
+            }
+        }
+        booleans.keySet().each {compileOptions."$it" = false}
+        optionMap = compileOptions.optionMap()
+        booleans.values().each {
+            if (it.equals('nowarn')) {
+                assertEquals(true, optionMap[it])
+            } else {
+                assertEquals(false, optionMap[it])
+            }
+        }
+    }
+
+    @Test public void testFork() {
+        compileOptions.fork = false
+        boolean forkUseCalled = false
+        compileOptions.forkOptions = [define: {Map args ->
+            forkUseCalled = true
+            assertEquals(TEST_FORK_OPTION_MAP, args)
+        }] as GroovyForkOptions
+        assert compileOptions.fork(TEST_FORK_OPTION_MAP).is(compileOptions)
+        assertTrue(compileOptions.fork)
+        assertTrue(forkUseCalled)
+    }
+
+    @Test public void testDefine() {
+        compileOptions.verbose = false
+        compileOptions.encoding = 'xxxx'
+        compileOptions.fork = false
+        compileOptions.define( encoding: 'encoding')
+        assertEquals('encoding', compileOptions.encoding)
+        assertFalse(compileOptions.verbose)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyForkOptionsTest.groovy b/subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/compile/GroovyForkOptionsTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyForkOptionsTest.groovy
rename to subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/compile/GroovyForkOptionsTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java b/subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
rename to subprojects/language-groovy/src/test/groovy/org/gradle/api/tasks/javadoc/GroovydocTest.java
diff --git a/subprojects/language-java/language-java.gradle b/subprojects/language-java/language-java.gradle
new file mode 100644
index 0000000..1d1272f
--- /dev/null
+++ b/subprojects/language-java/language-java.gradle
@@ -0,0 +1,17 @@
+dependencies {
+    compile libraries.groovy
+    compile project(":core")
+    compile project(":platformJvm")
+    compile project(":languageJvm")
+
+    // TODO - get rid of this cycle
+    integTestRuntime project(':plugins')
+}
+
+// These public packages have classes that are tangled with the corresponding internal package.
+useClassycle(exclude: ["org/gradle/api/tasks/compile/**", "org/gradle/external/javadoc/**", ])
+
+strictCompile()
+useTestFixtures()
+useTestFixtures(project: ":languageJvm", sourceSet: 'testFixtures')
+useTestFixtures(project: ":platformBase")
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy
new file mode 100644
index 0000000..f07467c
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+
+class JavaCompileIntegrationTest extends AbstractIntegrationSpec {
+
+    @Issue("GRADLE-3152")
+    def "can use the task without applying java-base plugin"() {
+        buildFile << """
+            task compile(type: JavaCompile) {
+                classpath = files()
+                sourceCompatibility = JavaVersion.current()
+                targetCompatibility = JavaVersion.current()
+                destinationDir = file("build/classes")
+                dependencyCacheDir = file("build/dependency-cache")
+                source "src/main/java"
+            }
+        """
+
+        file("src/main/java/Foo.java") << "public class Foo {}"
+
+        when:
+        run("compile")
+
+        then:
+        file("build/classes/Foo.class").exists()
+    }
+
+    def "uses default platform settings when applying java plugin"() {
+        buildFile << """
+            apply plugin:"java"
+        """
+
+        file("src/main/java/Foo.java") << "public class Foo {}"
+
+        when:
+        run("compileJava")
+        then:
+        file("build/classes/main/Foo.class").exists()
+    }
+
+    def "don't implicitly compile source files from classpath"() {
+        settingsFile << "include 'a', 'b'"
+        buildFile << """
+            subprojects {
+                apply plugin: 'java'
+                tasks.withType(JavaCompile) {
+                    options.compilerArgs << '-Xlint:all' << '-Werror'
+                }
+            }
+            project(':b') {
+                dependencies {
+                    compile project(':a')
+                }
+            }
+"""
+
+        file("a/src/main/resources/Foo.java") << "public class Foo {}"
+
+        file("b/src/main/java/Bar.java") << "public class Bar extends Foo {}"
+
+        expect:
+        fails("compileJava")
+        failure.assertHasDescription("Execution failed for task ':b:compileJava'.")
+
+        // This makes sure the test above is correct AND you can get back javac's default behavior if needed
+        when:
+        buildFile << "project(':b').compileJava { options.sourcepath = classpath }"
+        run("compileJava")
+        then:
+        file("b/build/classes/main/Bar.class").exists()
+        file("b/build/classes/main/Foo.class").exists()
+    }
+}
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileParallelIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileParallelIntegrationTest.groovy
new file mode 100644
index 0000000..4dbe8bc
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileParallelIntegrationTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile
+
+import com.google.common.collect.Iterables
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.jvm.JavaHomeException
+import org.gradle.internal.jvm.JavaInfo
+import org.gradle.util.TextUtil
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+
+ at IgnoreIf({ //noinspection UnnecessaryQualifiedReference
+    GradleContextualExecuter.parallel || JavaCompileParallelIntegrationTest.availableJdksWithJavac().size() < 2
+})
+class JavaCompileParallelIntegrationTest extends AbstractIntegrationSpec {
+
+    static List<JavaInfo> availableJdksWithJavac() {
+        AvailableJavaHomes.availableJdks.findAll {
+            try {
+                if (it.javacExecutable) {
+                    return true
+                }
+            }
+            catch (JavaHomeException ignore) {
+                // ignore
+            }
+            false
+        }
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3029")
+    def "system property java.home is not modified across compile task boundaries"() {
+        def projectNames = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
+        def jdks = Iterables.cycle(availableJdksWithJavac()*.javacExecutable.collect(TextUtil.&escapeString)).iterator()
+
+        settingsFile << "include ${projectNames.collect { "'$it'" }.join(', ')}"
+        buildFile << """
+            subprojects {
+                apply plugin: 'java'
+
+                repositories {
+                    mavenCentral()
+                }
+
+                dependencies {
+                    compile 'commons-lang:commons-lang:2.5'
+                }
+            }
+"""
+
+        projectNames.each { projectName ->
+            buildFile << """
+project(':$projectName') {
+    tasks.withType(JavaCompile) {
+        options.with {
+            fork = true
+            forkOptions.executable = "${jdks.next()}"
+        }
+    }
+}
+"""
+            file("${projectName}/src/main/java/Foo.java") << """
+import org.apache.commons.lang.StringUtils;
+
+public class Foo {
+    public String capitalize(String str) {
+        return StringUtils.capitalize(str);
+    }
+}
+"""
+        }
+
+        when:
+        args('--parallel')
+        args('--max-workers=4')
+        run('compileJava')
+
+        then:
+        noExceptionThrown()
+
+        projectNames.each { projectName ->
+            file("${projectName}/build/classes/main/Foo.class").exists()
+        }
+    }
+}
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy
new file mode 100644
index 0000000..09375db
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.java.compile.incremental
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.CompilationOutputsFixture
+
+public class CrossTaskIncrementalJavaCompilationIntegrationTest extends AbstractIntegrationSpec {
+
+    CompilationOutputsFixture impl
+
+    def setup() {
+        impl = new CompilationOutputsFixture(file("impl/build/classes"))
+
+        buildFile << """
+            subprojects {
+                apply plugin: 'java'
+                tasks.withType(JavaCompile) {
+                    options.incremental = true
+                    options.fork = true
+                }
+                repositories { mavenCentral() }
+            }
+            project(':impl') {
+                dependencies { compile project(':api') }
+            }
+        """
+        settingsFile << "include 'api', 'impl'"
+    }
+
+    private File java(Map projectToClassBodies) {
+        File out
+        projectToClassBodies.each { project, bodies ->
+            bodies.each { body ->
+                def className = (body =~ /(?s).*?class (\w+) .*/)[0][1]
+                assert className: "unable to find class name"
+                def f = file("$project/src/main/java/${className}.java")
+                f.createFile()
+                f.text = body
+                out = f
+            }
+        }
+        out
+    }
+
+    def "detects changed class in an upstream project"() {
+        java api: ["class A {}", "class B {}"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class A { String change; }"]
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses("ImplA")
+    }
+
+    def "detects change to transitive dependency in an upstream project"() {
+        java api: ["class A {}", "class B extends A {}"]
+        java impl: ["class SomeImpl {}", "class ImplB extends B {}", "class ImplB2 extends ImplB {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class A { String change; }"]
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses("ImplB", "ImplB2")
+    }
+
+    def "deletion of jar without dependents does not recompile any classes"() {
+        java api: ["class A {}"], impl: ["class SomeImpl {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        buildFile << """
+            project(':impl') {
+                configurations.compile.dependencies.clear() //so that api jar is no longer on classpath
+            }
+        """
+        run "impl:compileJava"
+
+        then:
+        impl.noneRecompiled()
+    }
+
+    def "deletion of jar with dependents causes compilation failure"() {
+        java api: ["class A {}"], impl: ["class ImplA extends A {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        buildFile << """
+            project(':impl') {
+                configurations.compile.dependencies.clear() //so that api jar is no longer on classpath
+            }
+        """
+        fails "impl:compileJava"
+
+        then:
+        impl.noneRecompiled()
+    }
+
+    def "detects change to dependency and ensures class dependency info refreshed"() {
+        java api: ["class A {}", "class B extends A {}"]
+        java impl: ["class SomeImpl {}", "class ImplB extends B {}", "class ImplB2 extends ImplB {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class B { /* remove extends */ }"]
+        run "impl:compileJava"
+
+        then: impl.recompiledClasses("ImplB", "ImplB2")
+
+        when:
+        impl.snapshot()
+        java api: ["class A { /* change */ }"]
+        run "impl:compileJava"
+
+        then: impl.noneRecompiled() //because after earlier change to B, class A is no longer a dependency
+    }
+
+    def "detects deleted class in an upstream project and fails compilation"() {
+        def b = java(api: ["class A {}", "class B {}"])
+        java impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        assert b.delete()
+        fails "impl:compileJava"
+
+        then:
+        impl.noneRecompiled()
+    }
+
+    def "recompilation not necessary when upstream does not change any of the actual dependencies"() {
+        java api: ["class A {}", "class B {}"], impl: ["class ImplA extends A {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class B { String change; }"]
+        run "impl:compileJava"
+
+        then:
+        impl.noneRecompiled()
+    }
+
+    def "deletion of jar with non-private constant causes full rebuild"() {
+        java api: ["class A { final static int x = 1; }"], impl: ["class X {}", "class Y {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        buildFile << """
+            project(':impl') {
+                configurations.compile.dependencies.clear() //so that api jar is no longer on classpath
+            }
+        """
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses("X", "Y")
+    }
+
+    def "change in an upstream class with non-private constant causes full rebuild"() {
+        java api: ["class A {}", "class B { final static int x = 1; }"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class B { /* change */ }"]
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses('ImplA', 'ImplB')
+    }
+
+    def "change in an upstream transitive class with non-private constant does not cause full rebuild"() {
+        java api: ["class A { final static int x = 1; }", "class B extends A {}"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class B { /* change */ }"]
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses('ImplB')
+    }
+
+    def "private constant in upstream project does not trigger full rebuild"() {
+        java api: ["class A {}", "class B { private final static int x = 1; }"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class B { /* change */ }"]
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses('ImplB')
+    }
+
+    def "detects changed classes when upstream project was built in isolation"() {
+        java api: ["class A {}", "class B {}"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class A { String change; }"]
+        run "api:compileJava"
+        run "impl:compileJava"
+
+        then:
+        impl.recompiledClasses("ImplA")
+    }
+
+    def "detects class changes in subsequent runs ensuring the jar snapshots are refreshed"() {
+        java api: ["class A {}", "class B {}"], impl: ["class ImplA extends A {}", "class ImplB extends B {}"]
+        impl.snapshot { run "compileJava" }
+
+        when:
+        java api: ["class A { String change; }"]
+        run "api:compileJava"
+        run "impl:compileJava" //different build invocation
+
+        then: impl.recompiledClasses("ImplA")
+
+        when:
+        impl.snapshot()
+        java api: ["class B { String change; }"]
+        run "compileJava"
+
+        then: impl.recompiledClasses("ImplB")
+    }
+
+    def "changes to resources in jar do not incur recompilation"() {
+        java impl: ["class A {}"]
+        impl.snapshot { run "impl:compileJava" }
+
+        when:
+        file("api/src/main/resources/some-resource.txt") << "xxx"
+        java api: ["class A { String change; }"]
+        run "impl:compileJava"
+
+        then: impl.noneRecompiled()
+    }
+
+    def "handles multiple compile tasks in the same project"() {
+        settingsFile << "\n include 'other'" //add an extra project
+        java impl: ["class ImplA extends A {}"], api: ["class A {}"], other: ["class Other {}"]
+
+        //new separate compile task (integTestCompile) depends on class from the extra project
+        file("impl/build.gradle") << """
+            sourceSets { integTest.java.srcDir 'src/integTest/java' }
+            dependencies { integTestCompile project(":other") }
+        """
+        file("impl/src/integTest/java/SomeIntegTest.java") << "class SomeIntegTest extends Other {}"
+
+        impl.snapshot { run "compileIntegTestJava", "compileJava" }
+
+        when: //when api class is changed
+        java api: ["class A { String change; }"]
+        run "compileIntegTestJava", "compileJava"
+
+        then: //only impl class is recompiled
+        impl.recompiledClasses("ImplA")
+
+        when: //when other class is changed
+        impl.snapshot()
+        java other: ["class Other { String change; }"]
+        run "compileIntegTestJava", "compileJava"
+
+        then: //only integTest class is recompiled
+        impl.recompiledClasses("SomeIntegTest")
+    }
+
+    def "the order of classpath items is unchanged"() {
+        java api: ["class A {}"], impl: ["class B {}"]
+        file("impl/build.gradle") << """
+            dependencies { compile "org.mockito:mockito-core:1.9.5", "junit:junit:4.12" }
+            compileJava.doFirst {
+                file("classpath.txt").createNewFile(); file("classpath.txt").text = classpath.files*.name.join(', ')
+            }
+        """
+
+        when: run("impl:compileJava") //initial run
+        then: file("impl/classpath.txt").text == "api.jar, mockito-core-1.9.5.jar, junit-4.12.jar, objenesis-1.0.jar, hamcrest-core-1.3.jar"
+
+        when: //project dependency changes
+        java api: ["class A { String change; }"]
+        run("impl:compileJava")
+
+        then: file("impl/classpath.txt").text == "api.jar, mockito-core-1.9.5.jar, junit-4.12.jar, objenesis-1.0.jar, hamcrest-core-1.3.jar"
+
+        when: //transitive dependency is excluded
+        file("impl/build.gradle") << "configurations.compile.exclude module: 'hamcrest-core' \n"
+        run("impl:compileJava")
+
+        then: file("impl/classpath.txt").text == "api.jar, mockito-core-1.9.5.jar, junit-4.12.jar, objenesis-1.0.jar"
+
+        when: //direct dependency is excluded
+        file("impl/build.gradle") << "configurations.compile.exclude module: 'junit' \n"
+        run("impl:compileJava")
+
+        then: file("impl/classpath.txt").text == "api.jar, mockito-core-1.9.5.jar, objenesis-1.0.jar"
+
+        when: //new dependency is added
+        file("impl/build.gradle") << "dependencies { compile 'org.testng:testng:6.8.7' } \n"
+        run("impl:compileJava")
+
+        then: file("impl/classpath.txt").text == "api.jar, mockito-core-1.9.5.jar, testng-6.8.7.jar, objenesis-1.0.jar, bsh-2.0b4.jar, jcommander-1.27.jar, snakeyaml-1.12.jar"
+    }
+
+    def "handles duplicate class found in jar"() {
+        java api: ["class A extends B {}", "class B {}"], impl: ["class A extends C {}", "class C {}"]
+
+        impl.snapshot { run("impl:compileJava") }
+
+        when:
+        //change to source dependency duplicate triggers recompilation
+        java impl: ["class C { String change; }"]
+        run("impl:compileJava")
+
+        then: impl.recompiledClasses("A", "C")
+
+        when:
+        //change to jar dependency duplicate is ignored because source duplicate wins
+        impl.snapshot()
+        java api: ["class B { String change; } "]
+        run("impl:compileJava")
+
+        then: impl.noneRecompiled()
+    }
+
+    def "new jar with duplicate class appearing earlier on classpath must trigger compilation"() {
+        java impl: ["class A extends org.junit.Assert {}"]
+        file("impl/build.gradle") << """
+            configurations.compile.dependencies.clear()
+            dependencies { compile 'junit:junit:4.12' }
+        """
+
+        impl.snapshot { run("impl:compileJava") }
+
+        when:
+        //add new jar with duplicate class that will be earlier on the classpath (project dependencies are earlier on classpath)
+        file("api/src/main/java/org/junit/Assert.java") << "package org.junit; public class Assert {}"
+        file("impl/build.gradle") << "dependencies { compile project(':api') }"
+        run("impl:compileJava")
+
+        then: impl.recompiledClasses("A")
+    }
+
+    def "new jar without duplicate class does not trigger compilation"() {
+        java impl: ["class A {}"]
+        impl.snapshot { run("impl:compileJava") }
+
+        when:
+        file("impl/build.gradle") << "dependencies { compile 'junit:junit:4.12' }"
+        run("impl:compileJava")
+
+        then: impl.noneRecompiled()
+    }
+
+    def "changed jar with duplicate class appearing earlier on classpath must trigger compilation"() {
+        java impl: ["class A extends org.junit.Assert {}"]
+        file("impl/build.gradle") << """
+            dependencies { compile 'junit:junit:4.12' }
+        """
+
+        impl.snapshot { run("impl:compileJava") }
+
+        when:
+        //update existing jar with duplicate class that will be earlier on the classpath (project dependencies are earlier on classpath)
+        file("api/src/main/java/org/junit/Assert.java") << "package org.junit; public class Assert {}"
+        run("impl:compileJava")
+
+        then: impl.recompiledClasses("A")
+    }
+
+    def "deletion of a jar with duplicate class causes recompilation"() {
+        file("api/src/main/java/org/junit/Assert.java") << "package org.junit; public class Assert {}"
+        java impl: ["class A extends org.junit.Assert {}"]
+
+        file("impl/build.gradle") << "dependencies { compile 'junit:junit:4.12' }"
+
+        impl.snapshot { run("impl:compileJava") }
+
+        when:
+        file("impl/build.gradle").text = """
+            configurations.compile.dependencies.clear()  //kill project dependency
+            dependencies { compile 'junit:junit:4.11' }  //leave only junit
+        """
+        run("impl:compileJava")
+
+        then: impl.recompiledClasses("A")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/SourceIncrementalJavaCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/SourceIncrementalJavaCompilationIntegrationTest.groovy
new file mode 100644
index 0000000..0fef15e
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/SourceIncrementalJavaCompilationIntegrationTest.groovy
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.java.compile.incremental;
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.CompilationOutputsFixture;
+
+public class SourceIncrementalJavaCompilationIntegrationTest extends AbstractIntegrationSpec {
+
+    CompilationOutputsFixture outputs
+
+    def setup() {
+        outputs = new CompilationOutputsFixture(file("build/classes"))
+
+        buildFile << """
+            apply plugin: 'java'
+            compileJava.options.incremental = true
+        """
+    }
+
+    private File java(String ... classBodies) {
+        File out
+        for (String body : classBodies) {
+            def className = (body =~ /(?s).*?class (\w+) .*/)[0][1]
+            assert className: "unable to find class name"
+            def f = file("src/main/java/${className}.java")
+            f.createFile()
+            f.text = body
+            out = f
+        }
+        out
+    }
+
+    def "detects deletion of an isolated source class with an inner class"() {
+        def a = java """class A {
+            class InnerA {}
+        }"""
+        java "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        assert a.delete()
+        run "compileJava"
+
+        then:
+        outputs.noneRecompiled() //B is not recompiled
+        outputs.deletedClasses 'A', 'A$InnerA' //inner class is also deleted
+    }
+
+    def "detects deletion of a source base class that leads to compilation failure"() {
+        def a = java "class A {}"
+        java "class B extends A {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when: assert a.delete()
+        then:
+        fails "compileJava"
+        outputs.noneRecompiled()
+        outputs.deletedClasses 'A', 'B'
+    }
+
+    def "detects change of an isolated source class with an inner class"() {
+        java """class A {
+            class InnerA {}
+        }"""
+        java "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java """class A {
+            class InnerA { /* change */ }
+        }"""
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses 'A', 'A$InnerA'
+    }
+
+    def "detects change of an isolated class"() {
+        java "class A {}", "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses 'A'
+    }
+
+    def "detects deletion of an inner class"() {
+        java """class A {
+            class InnerA {}
+        }"""
+        java "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A {}"
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses 'A'
+        outputs.deletedClasses 'A$InnerA'
+    }
+
+    def "detects rename of an inner class"() {
+        java """class A {
+            class InnerA {}
+        }"""
+        java "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java """class A {
+            class InnerA2 {}
+        }"""
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses 'A', 'A$InnerA2'
+        outputs.deletedClasses 'A$InnerA'
+    }
+
+    def "detects addition af a new class with an inner class"() {
+        java "class B {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java """class A {
+            class InnerA {}
+        }"""
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses 'A', 'A$InnerA'
+    }
+
+    def "detects transitive dependencies"() {
+        java "class A {}", "class B extends A {}", "class C extends B {}", "class D {}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'A', 'B', 'C'
+
+        when:
+        outputs.snapshot()
+        java "class B { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'B', 'C'
+    }
+
+    def "detects transitive dependencies with inner classes"() {
+        java "class A {}", "class B extends A {}", "class D {}"
+        java """class C extends B {
+            class InnerC {}
+        }
+        """
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'A', 'B', 'C', 'C$InnerC'
+    }
+
+    def "handles cycles in class dependencies"() {
+        java "class A {}", "class D {}"
+        java "class B extends A { C c; }", "class C extends B {}" //cycle
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'A', 'B', 'C'
+    }
+
+    def "change to an annotation class triggers full rebuild"() {
+        def annotationClass = file("src/main/java/SourceAnnotation.java") << """import java.lang.annotation.*;
+            @Retention(RetentionPolicy.SOURCE) public @interface SourceAnnotation {}
+        """
+        java "class A {}", "class B {}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        annotationClass.text += "/* change */"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'A', 'B', 'SourceAnnotation'
+    }
+
+    def "changed class with private constant does not incur full rebuild"() {
+        java "class A {}", "class B { private final static int x = 1;}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class B { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'B'
+    }
+
+    def "changed class with non-private constant incurs full rebuild"() {
+        java "class A {}", "class B { final static int x = 1;}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class B { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'B', 'A'
+    }
+
+    def "dependent class with non-private constant does not incur full rebuild"() {
+        java "class A {}", "class B extends A { final static int x = 1;}", "class C {}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses 'B', 'A'
+    }
+
+    def "detects class changes in subsequent runs ensuring the class dependency data is refreshed"() {
+        java "class A {}", "class B {}", "class C {}"
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java "class B extends A {}"
+        run "compileJava"
+
+        then: outputs.recompiledClasses('B')
+
+        when:
+        outputs.snapshot()
+        java "class A { /* change */ }"
+        run "compileJava"
+
+        then: outputs.recompiledClasses('A', 'B')
+    }
+
+    def "handles multiple compile tasks within a single project"() {
+        java "class A {}", "class B extends A {}"
+        file("src/integTest/java/X.java") << "class X {}"
+        file("src/integTest/java/Y.java") << "class Y extends X {}"
+
+        //new separate compile task (integTestCompile)
+        file("build.gradle") << """
+            sourceSets { integTest.java.srcDir 'src/integTest/java' }
+        """
+
+        outputs.snapshot { run "compileIntegTestJava", "compileJava" }
+
+        when: //when A class is changed
+        java "class A { String change; }"
+        run "compileIntegTestJava", "compileJava", "-i"
+
+        then: //only B and A are recompiled
+        outputs.recompiledClasses("A", "B")
+
+        when: //when X class is changed
+        outputs.snapshot()
+        file("src/integTest/java/X.java").text = "class X { String change;}"
+        run "compileIntegTestJava", "compileJava", "-i"
+
+        then: //only X and Y are recompiled
+        outputs.recompiledClasses("X", "Y")
+    }
+
+    def "recompiles classes from extra source directories"() {
+        buildFile << "sourceSets.main.java.srcDir 'java'"
+
+        java("class B {}")
+        file("java/A.java") << "class A extends B {}"
+        file("java/C.java") << "class C {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        java("class B { String change; } ")
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses("B", "A")
+    }
+
+    def "detects changes to source in extra source directories"() {
+        buildFile << "sourceSets.main.java.srcDir 'java'"
+
+        java("class A extends B {}")
+        file("java/B.java") << "class B {}"
+        file("java/C.java") << "class C {}"
+
+        outputs.snapshot { run "compileJava" }
+
+        when:
+        file("java/B.java").text = "class B { String change; }"
+        run "compileJava"
+
+        then:
+        outputs.recompiledClasses("B", "A")
+    }
+
+    def "handles duplicate class across source directories"() {
+        //compiler does not allow this scenario, documenting it here
+        buildFile << "sourceSets.main.java.srcDir 'java'"
+
+        java("class A {}")
+        file("java/A.java") << "class A {}"
+
+        when: fails "compileJava"
+        then: failure.assertHasCause("Compilation failed")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy
new file mode 100644
index 0000000..0e4548a
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.javadoc
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.Issue
+
+class JavadocIntegrationTest extends AbstractIntegrationSpec {
+    @Rule TestResources testResources = new TestResources(temporaryFolder)
+
+    @Issue("GRADLE-1563")
+    def handlesTagsAndTaglets() {
+        when:
+        run("javadoc")
+
+        then:
+        def javadoc = testResources.dir.file("build/docs/javadoc/Person.html")
+        javadoc.text =~ /(?ms)This is the Person class.*Author.*author value.*Deprecated.*deprecated value.*Custom Tag.*custom tag value/
+        // we can't currently control the order between tags and taglets (limitation on our side)
+        javadoc.text =~ /(?ms)Custom Taglet.*custom taglet value/
+    }
+
+    @Issue("GRADLE-2520")
+    def canCombineLocalOptionWithOtherOptions() {
+        when:
+        run("javadoc")
+
+        then:
+        def javadoc = testResources.dir.file("build/docs/javadoc/Person.html")
+        javadoc.text =~ /(?ms)USED LOCALE=de_DE/
+        javadoc.text =~ /(?ms)Serial no. is valid javadoc!/
+    }
+
+    def "writes header"() {
+        buildFile << """
+            apply plugin: "java"
+            javadoc.options.header = "<!-- Hey Joe! -->"
+        """
+
+        file("src/main/java/Foo.java") << "public class Foo {}"
+
+        when: run("javadoc", "-i")
+        then:
+        file("build/docs/javadoc/Foo.html").text.contains("""Hey Joe!""")
+    }
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Issue("GRADLE-3099")
+    def "writes multiline header"() {
+        buildFile << """
+            apply plugin: "java"
+            javadoc.options.header = \"\"\"
+                <!-- Hey
+Joe! -->
+            \"\"\"
+        """
+
+        file("src/main/java/Foo.java") << "public class Foo {}"
+
+        when: run("javadoc", "-i")
+        then:
+        file("build/docs/javadoc/Foo.html").text.contains("""Hey
+Joe!""")
+    }
+
+    @Issue("GRADLE-3152")
+    def "can use the task without applying java-base plugin"() {
+        buildFile << """
+            task javadoc(type: Javadoc) {
+                destinationDir = file("build/javadoc")
+                source "src/main/java"
+            }
+        """
+
+        file("src/main/java/Foo.java") << "public class Foo {}"
+
+        when:
+        run("javadoc")
+
+        then:
+        file("build/javadoc/Foo.html").exists()
+    }
+}
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..45c12bf
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java
+import org.gradle.integtests.language.AbstractJvmLanguageIncrementalBuildIntegrationTest
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.fixtures.TestJavaComponent
+
+class JavaLanguageIncrementalBuildIntegrationTest extends AbstractJvmLanguageIncrementalBuildIntegrationTest {
+    TestJvmComponent testComponent = new TestJavaComponent()
+
+    def "rebuilds jar when input property changed"() {
+        given:
+        run "mainJar"
+
+        when:
+        buildFile << """
+    tasks.withType(JavaCompile) {
+        options.debug = false
+    }
+"""
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped ":compileMainJarMainJava", ":createMainJar", ":mainJar"
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..10039cf
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/JavaLanguageIntegrationTest.groovy
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.integtests.language.AbstractJvmLanguageIntegrationTest
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform
+import org.gradle.language.fixtures.BadJavaComponent
+import org.gradle.language.fixtures.TestJavaComponent
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.hamcrest.Matchers
+
+class JavaLanguageIntegrationTest extends AbstractJvmLanguageIntegrationTest {
+    TestJvmComponent app = new TestJavaComponent()
+
+    def "reports failure to compile bad java sources"() {
+        when:
+        def badApp = new BadJavaComponent()
+        badApp.sources*.writeToDir(file("src/myLib/java"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+"""
+        then:
+        fails "assemble"
+
+        and:
+        badApp.compilerErrors.each {
+            assert errorOutput.contains(it)
+        }
+    }
+
+    @Requires(TestPrecondition.JDK6_OR_LATER)
+    def "target should produce in the correct bytecode"() {
+        when:
+        app.sources*.writeToDir(file("src/myLib/java"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "java6"
+            }
+        }
+    }
+"""
+        then:
+        succeeds "assemble"
+
+        and:
+        jarFile("build/jars/myLibJar/myLib.jar").getJavaVersion() == JavaVersion.VERSION_1_6
+        and:
+        jarFile("build/jars/myLibJar/myLib.jar").hasDescendants(app.sources*.classFile.fullPath as String[])
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "multiple targets should produce in the correct bytecode"() {
+        when:
+        app.writeSources(file("src/myLib"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "java5"
+                targetPlatform "java6"
+                targetPlatform "java7"
+                targetPlatform "java8"
+            }
+        }
+    }
+"""
+        then:
+        succeeds "assemble"
+
+        and:
+        jarFile("build/jars/java5MyLibJar/myLib.jar").javaVersion == JavaVersion.VERSION_1_5
+        and:
+        jarFile("build/jars/java6MyLibJar/myLib.jar").javaVersion == JavaVersion.VERSION_1_6
+        and:
+        jarFile("build/jars/java7MyLibJar/myLib.jar").javaVersion == JavaVersion.VERSION_1_7
+        and:
+        jarFile("build/jars/java8MyLibJar/myLib.jar").javaVersion == JavaVersion.VERSION_1_8
+    }
+
+    def "erroneous target should produce reasonable error message"() {
+        String badName = "bornYesterday";
+        when:
+        def badApp = new BadJavaComponent()
+        badApp.sources*.writeToDir(file("src/myLib/java"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "$badName"
+            }
+        }
+    }
+"""
+        then:
+        fails "assemble"
+
+        and:
+        assert failure.assertHasCause("Invalid JavaPlatform: $badName")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    def "builds all buildable and skips non-buildable platforms when assembling"() {
+        def current = new DefaultJavaPlatform(JavaVersion.current())
+        when:
+        app.sources*.writeToDir(file("src/myLib/java"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "${current.name}"
+                targetPlatform "java9"
+            }
+        }
+    }
+"""
+        then:
+        succeeds "assemble"
+
+        and:
+        jarFile("build/jars/${current.name}MyLibJar/myLib.jar").javaVersion == current.targetCompatibility
+    }
+
+
+    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    def "too high JDK target should produce reasonable error message"() {
+        when:
+        app.sources*.writeToDir(file("src/myLib/java"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "java9"
+            }
+        }
+    }
+"""
+        then:
+        // TODO:DAZ Would like to use 'assemble' here, but it currently ignores non-buildable binaries
+        fails "myLibJar"
+
+        and:
+        failure.assertHasCause("No tool chains can provide a compiler for type DefaultJavaCompileSpec:")
+        failure.assertThatCause(Matchers.containsString("Could not target platform: 'Java SE 9' using tool chain: 'JDK ${JavaVersion.current().majorVersion} (${JavaVersion.current()})'"))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/SampleJavaLanguageIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/SampleJavaLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..1d3ac81
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/SampleJavaLanguageIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.junit.Rule
+
+class SampleJavaLanguageIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule
+    Sample sample = new Sample(temporaryFolder, "jvmComponents/java")
+
+    def "can build java based jvm component"() {
+        setup:
+        executer.inDirectory(sample.dir)
+
+        when:
+        succeeds("assemble")
+
+        then:
+        new JarTestFixture(sample.dir.file("build/jars/mainJar/main.jar")).hasDescendants(
+                "org/gradle/samples/HelloWorld.class"
+        )
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginGoodBehaviourTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..c066ec5
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginGoodBehaviourTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.plugins
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class JavaLanguagePluginGoodBehaviourTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        "java-lang"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginIntegrationTest.groovy
new file mode 100644
index 0000000..ecce2d7
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/language/java/plugins/JavaLanguagePluginIntegrationTest.groovy
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.plugins
+import org.gradle.integtests.language.AbstractJvmPluginLanguageIntegrationTest
+import org.gradle.language.java.JavaSourceSet
+
+class JavaLanguagePluginIntegrationTest extends AbstractJvmPluginLanguageIntegrationTest{
+    String sourceSetTypeName = JavaSourceSet.class.simpleName
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/build.gradle b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/build.gradle
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/build.gradle
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/build.gradle
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/main/java/Person.java b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/main/java/Person.java
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/main/java/Person.java
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/main/java/Person.java
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/taglet/java/LocaleAwareTaglet.java b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/taglet/java/LocaleAwareTaglet.java
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/taglet/java/LocaleAwareTaglet.java
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/canCombineLocalOptionWithOtherOptions/src/taglet/java/LocaleAwareTaglet.java
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/build.gradle b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/build.gradle
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/build.gradle
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/build.gradle
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/main/java/Person.java b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/main/java/Person.java
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/main/java/Person.java
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/main/java/Person.java
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/taglet/java/CustomTaglet.java b/subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/taglet/java/CustomTaglet.java
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/taglet/java/CustomTaglet.java
rename to subprojects/language-java/src/integTest/resources/org/gradle/javadoc/JavadocIntegrationTest/handlesTagsAndTaglets/src/taglet/java/CustomTaglet.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/DefaultJavaToolChain.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/DefaultJavaToolChain.java
new file mode 100644
index 0000000..107343a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/DefaultJavaToolChain.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.JavaVersion;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.JavaCompilerFactory;
+import org.gradle.api.tasks.javadoc.internal.JavadocGenerator;
+import org.gradle.api.tasks.javadoc.internal.JavadocSpec;
+import org.gradle.jvm.internal.toolchain.JavaToolChainInternal;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.util.TreeVisitor;
+
+public class DefaultJavaToolChain implements JavaToolChainInternal {
+    private final JavaCompilerFactory compilerFactory;
+    private final ExecActionFactory execActionFactory;
+    private final JavaVersion javaVersion;
+
+    public DefaultJavaToolChain(JavaCompilerFactory compilerFactory, ExecActionFactory execActionFactory) {
+        this.compilerFactory = compilerFactory;
+        this.execActionFactory = execActionFactory;
+        this.javaVersion = JavaVersion.current();
+    }
+
+    public String getName() {
+        return String.format("JDK%s", javaVersion);
+    }
+
+    public String getDisplayName() {
+        return String.format("JDK %s (%s)", javaVersion.getMajorVersion(), javaVersion);
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public ToolProvider select(JavaPlatform targetPlatform) {
+        // TODO:DAZ Remove all of the calls to this method with null platform
+        if (targetPlatform != null && targetPlatform.getTargetCompatibility().compareTo(javaVersion) > 0) {
+            return new UnavailableToolProvider(targetPlatform);
+        }
+        return new JavaToolProvider();
+    }
+
+    private class JavaToolProvider implements ToolProvider {
+        public <T extends CompileSpec> Compiler<T> newCompiler(Class<T> spec) {
+            if (JavaCompileSpec.class.isAssignableFrom(spec)) {
+                @SuppressWarnings("unchecked") Compiler<T> compiler = (Compiler<T>) compilerFactory.create(spec);
+                return compiler;
+            }
+            if (JavadocSpec.class.isAssignableFrom(spec)) {
+                @SuppressWarnings("unchecked") Compiler<T> compiler = (Compiler<T>) new JavadocGenerator(execActionFactory);
+                return compiler;
+            }
+
+            throw new IllegalArgumentException(String.format("Don't know how to compile using spec of type %s.", spec.getClass().getSimpleName()));
+        }
+
+        @Override
+        public <T> T get(Class<T> toolType) {
+            throw new IllegalArgumentException(String.format("Don't know how to provide tool of type %s.", toolType.getSimpleName()));
+        }
+
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+        }
+    }
+
+    private class UnavailableToolProvider implements ToolProvider {
+        private final JavaPlatform targetPlatform;
+
+        private UnavailableToolProvider(JavaPlatform targetPlatform) {
+            this.targetPlatform = targetPlatform;
+        }
+
+        public <T extends CompileSpec> Compiler<T> newCompiler(Class<T> spec) {
+            throw new IllegalArgumentException(getMessage());
+        }
+
+        @Override
+        public <T> T get(Class<T> toolType) {
+            throw new IllegalArgumentException(getMessage());
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(getMessage());
+        }
+
+        private String getMessage() {
+            return String.format("Could not target platform: '%s' using tool chain: '%s'.", targetPlatform.getDisplayName(), DefaultJavaToolChain.this.getDisplayName());
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java
new file mode 100644
index 0000000..81181fb
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.tasks.compile.CompileOptions;
+import org.gradle.internal.Factory;
+
+public abstract class AbstractJavaCompileSpecFactory<T extends JavaCompileSpec> implements Factory<T> {
+    private final CompileOptions compileOptions;
+
+    public AbstractJavaCompileSpecFactory(CompileOptions compileOptions) {
+        this.compileOptions = compileOptions;
+    }
+
+    @Override
+    public T create() {
+        if (compileOptions.isFork()) {
+            if (compileOptions.getForkOptions().getExecutable() != null) {
+                return getCommandLineSpec();
+            } else {
+                return getForkingSpec();
+            }
+        } else {
+            return getDefaultSpec();
+        }
+    }
+
+    abstract protected T getCommandLineSpec();
+
+    abstract protected T getForkingSpec();
+
+    abstract protected T getDefaultSpec();
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDepend.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AntDepend.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDepend.java
rename to subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AntDepend.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.java
new file mode 100644
index 0000000..69d0f8f
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+import com.google.common.collect.ImmutableMap;
+import groovy.lang.Closure;
+import org.gradle.api.AntBuilder;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.compile.CompileOptions;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+import java.io.File;
+
+public class AntDependsStaleClassCleaner extends StaleClassCleaner {
+
+    private final Factory<AntBuilder> antBuilderFactory;
+    private final CompileOptions compileOptions;
+    private File dependencyCacheDir;
+
+    public AntDependsStaleClassCleaner(Factory<AntBuilder> antBuilderFactory, CompileOptions compileOptions) {
+        this.antBuilderFactory = antBuilderFactory;
+        this.compileOptions = compileOptions;
+    }
+
+    public void setDependencyCacheDir(File dependencyCacheDir) {
+        this.dependencyCacheDir = dependencyCacheDir;
+    }
+
+    public void execute() {
+        ImmutableMap.Builder<String, Object> options = ImmutableMap.builder();
+        options.put("destDir", getDestinationDir());
+        options.putAll(compileOptions.getDependOptions().optionMap());
+        if (compileOptions.getDependOptions().isUseCache()) {
+            options.put("cache", dependencyCacheDir);
+        }
+
+        final AntBuilder ant = antBuilderFactory.create();
+        ant.getProject().addTaskDefinition("gradleDepend", AntDepend.class);
+        ant.invokeMethod("gradleDepend", new Object[]{options.build(), new Closure<Object>(this, this) {
+            @SuppressWarnings("UnusedDeclaration")
+            public void doCall(Object ignore) {
+                getSource().addToAntBuilder(ant, "src", FileCollection.AntType.MatchingTask);
+            }
+        }});
+    }
+
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java
new file mode 100644
index 0000000..fe69acf
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.AntBuilder;
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+public class CleaningJavaCompiler extends CleaningJavaCompilerSupport<JavaCompileSpec> implements org.gradle.language.base.internal.compile.Compiler<JavaCompileSpec> {
+    private final Compiler<JavaCompileSpec> compiler;
+    private final Factory<AntBuilder> antBuilderFactory;
+    private final TaskOutputsInternal taskOutputs;
+
+    public CleaningJavaCompiler(Compiler<JavaCompileSpec> compiler, Factory<AntBuilder> antBuilderFactory,
+                                TaskOutputsInternal taskOutputs) {
+        this.compiler = compiler;
+        this.antBuilderFactory = antBuilderFactory;
+        this.taskOutputs = taskOutputs;
+    }
+
+    @Override
+    public Compiler<JavaCompileSpec> getCompiler() {
+        return compiler;
+    }
+
+    protected StaleClassCleaner createCleaner(JavaCompileSpec spec) {
+        //TODO SF do we want to keep useDepend? The docs advertise that this option makes sense only when useAnt is on
+        //but the latter has been removed in 2.* Either we need to fix the the docs or deprecate useDepend
+        if (spec.getCompileOptions().isUseDepend()) {
+            AntDependsStaleClassCleaner cleaner = new AntDependsStaleClassCleaner(antBuilderFactory, spec.getCompileOptions());
+            cleaner.setDependencyCacheDir(spec.getDependencyCacheDir());
+            return cleaner;
+        } else {
+            return new SimpleStaleClassCleaner(taskOutputs);
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java
new file mode 100644
index 0000000..68c36cf
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+/**
+ * Deletes stale classes before invoking the actual compiler
+ */
+public abstract class CleaningJavaCompilerSupport<T extends JavaCompileSpec> implements org.gradle.language.base.internal.compile.Compiler<T> {
+    public WorkResult execute(T spec) {
+        StaleClassCleaner cleaner = createCleaner(spec);
+
+        cleaner.setDestinationDir(spec.getDestinationDir());
+        cleaner.setSource(spec.getSource());
+        cleaner.execute();
+
+        Compiler<? super T> compiler = getCompiler();
+        return compiler.execute(spec);
+    }
+
+    protected abstract Compiler<T> getCompiler();
+
+    protected abstract StaleClassCleaner createCleaner(T spec);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompileSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompileSpec.java
new file mode 100644
index 0000000..b0cfc17
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompileSpec.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+public interface CommandLineJavaCompileSpec {
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.java
new file mode 100644
index 0000000..dd67a66
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.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.api.internal.tasks.compile;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+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.Serializable;
+
+/**
+ * Executes the Java command line compiler specified in {@code JavaCompileSpec.forkOptions.getExecutable()}.
+ */
+public class CommandLineJavaCompiler implements Compiler<JavaCompileSpec>, Serializable {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CommandLineJavaCompiler.class);
+
+    private final CompileSpecToArguments<JavaCompileSpec> argumentsGenerator = new CommandLineJavaCompilerArgumentsGenerator();
+
+    public WorkResult execute(JavaCompileSpec spec) {
+        String executable = spec.getCompileOptions().getForkOptions().getExecutable();
+        LOGGER.info("Compiling with Java command line compiler '{}'.", executable);
+
+        ExecHandle handle = createCompilerHandle(executable, spec);
+        executeCompiler(handle);
+
+        return new SimpleWorkResult(true);
+    }
+
+    private ExecHandle createCompilerHandle(String executable, JavaCompileSpec spec) {
+        ExecHandleBuilder builder = new ExecHandleBuilder();
+        builder.setWorkingDir(spec.getWorkingDir());
+        builder.setExecutable(executable);
+        argumentsGenerator.collectArguments(spec, new ExecSpecBackedArgCollector(builder));
+        builder.setIgnoreExitValue(true);
+        return builder.build();
+    }
+
+    private void executeCompiler(ExecHandle handle) {
+        handle.start();
+        ExecResult result = handle.waitForFinish();
+        if (result.getExitValue() != 0) {
+            throw new CompilationFailedException(result.getExitValue());
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.java
new file mode 100644
index 0000000..bf16b36
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.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.api.internal.tasks.compile;
+
+import com.google.common.collect.Iterables;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.platform.base.internal.toolchain.ArgCollector;
+import org.gradle.platform.base.internal.toolchain.ArgWriter;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.List;
+
+public class CommandLineJavaCompilerArgumentsGenerator implements CompileSpecToArguments<JavaCompileSpec>, Serializable {
+    public void collectArguments(JavaCompileSpec spec, ArgCollector collector) {
+        for (String arg : generate(spec)) {
+            collector.args(arg);
+        }
+    }
+
+    public Iterable<String> generate(JavaCompileSpec spec) {
+        List<String> launcherOptions = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeMainOptions(false).includeClasspath(false).includeCustomizations(false).build();
+        List<String> remainingArgs = new JavaCompilerArgumentsBuilder(spec).includeSourceFiles(true).build();
+        Iterable<String> allArgs = Iterables.concat(launcherOptions, remainingArgs);
+        if (exceedsWindowsCommandLineLengthLimit(allArgs)) {
+            return Iterables.concat(launcherOptions, shortenArgs(spec.getTempDir(), remainingArgs));
+        }
+        return allArgs;
+    }
+
+    private boolean exceedsWindowsCommandLineLengthLimit(Iterable<String> args) {
+        int length = 0;
+        for (String arg : args) {
+            length += arg.length() + 1;
+            // limit is 2047 on older Windows systems, and 8191 on newer ones
+            // http://support.microsoft.com/kb/830473
+            // let's play it safe, no need to optimize
+            if (length > 1500) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Iterable<String> shortenArgs(File tempDir, List<String> args) {
+        File file = new File(tempDir, "java-compiler-args.txt");
+        // for command file format, see http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#commandlineargfile
+        // use platform character and line encoding
+        try {
+            PrintWriter writer = new PrintWriter(new FileWriter(file));
+            try {
+                ArgWriter argWriter = ArgWriter.unixStyle(writer);
+                for (String arg : args) {
+                    argWriter.args(arg);
+                }
+            } finally {
+                writer.close();
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return Collections.singleton("@" + file.getPath());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java
new file mode 100644
index 0000000..204e54a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
+import org.gradle.api.internal.tasks.compile.daemon.DaemonForkOptions;
+import org.gradle.api.tasks.compile.ForkOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+import java.util.Collections;
+
+public class DaemonJavaCompiler extends AbstractDaemonCompiler<JavaCompileSpec> {
+    public DaemonJavaCompiler(File daemonWorkingDir, Compiler<JavaCompileSpec> delegate, CompilerDaemonFactory compilerDaemonFactory) {
+        super(daemonWorkingDir, delegate, compilerDaemonFactory);
+    }
+
+    @Override
+    protected DaemonForkOptions toDaemonOptions(JavaCompileSpec spec) {
+        ForkOptions forkOptions = spec.getCompileOptions().getForkOptions();
+        return new DaemonForkOptions(
+                forkOptions.getMemoryInitialSize(), forkOptions.getMemoryMaximumSize(), forkOptions.getJvmArgs(),
+                Collections.<File>emptyList(), Collections.singleton("com.sun.tools.javac"));
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.java
new file mode 100644
index 0000000..fde6e2d
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.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.internal.tasks.compile;
+
+import org.gradle.api.tasks.compile.CompileOptions;
+
+import java.io.File;
+
+public class DefaultJavaCompileSpec extends DefaultJvmLanguageCompileSpec implements JavaCompileSpec {
+    private CompileOptions compileOptions;
+    private File dependencyCacheDir;
+
+    public CompileOptions getCompileOptions() {
+        return compileOptions;
+    }
+
+    public void setCompileOptions(CompileOptions compileOptions) {
+        this.compileOptions = compileOptions;
+    }
+
+    public File getDependencyCacheDir() {
+        return dependencyCacheDir;
+    }
+
+    public void setDependencyCacheDir(File dependencyCacheDir) {
+        this.dependencyCacheDir = dependencyCacheDir;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactory.java
new file mode 100644
index 0000000..c70757c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.tasks.compile.CompileOptions;
+
+public class DefaultJavaCompileSpecFactory extends AbstractJavaCompileSpecFactory<DefaultJavaCompileSpec> {
+    public DefaultJavaCompileSpecFactory(CompileOptions compileOptions) {
+        super(compileOptions);
+    }
+
+    protected DefaultJavaCompileSpec getCommandLineSpec() {
+        return new DefaultCommandLineJavaSpec();
+    }
+
+    protected DefaultJavaCompileSpec getForkingSpec() {
+        return new DefaultForkingJavaCompileSpec();
+    }
+
+    protected DefaultJavaCompileSpec getDefaultSpec() {
+        return new DefaultJavaCompileSpec();
+    }
+
+    private static class DefaultCommandLineJavaSpec extends DefaultJavaCompileSpec implements CommandLineJavaCompileSpec {
+    }
+
+    private static class DefaultForkingJavaCompileSpec extends DefaultJavaCompileSpec implements ForkingJavaCompileSpec {
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java
new file mode 100644
index 0000000..b0c2b3a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import javax.tools.JavaCompiler;
+import java.io.File;
+
+public class DefaultJavaCompilerFactory implements JavaCompilerFactory {
+    private final File daemonWorkingDir;
+    private final CompilerDaemonFactory compilerDaemonFactory;
+    private final Factory<JavaCompiler> javaHomeBasedJavaCompilerFactory;
+
+    public DefaultJavaCompilerFactory(File daemonWorkingDir, CompilerDaemonFactory compilerDaemonFactory, Factory<JavaCompiler> javaHomeBasedJavaCompilerFactory) {
+        this.daemonWorkingDir = daemonWorkingDir;
+        this.compilerDaemonFactory = compilerDaemonFactory;
+        this.javaHomeBasedJavaCompilerFactory = javaHomeBasedJavaCompilerFactory;
+    }
+
+    public Compiler<JavaCompileSpec> createForJointCompilation(Class<? extends CompileSpec> type) {
+        return createTargetCompiler(type, true);
+    }
+
+    public Compiler<JavaCompileSpec> create(Class<? extends CompileSpec> type) {
+        Compiler<JavaCompileSpec> result = createTargetCompiler(type, false);
+        return new NormalizingJavaCompiler(result);
+    }
+
+    private Compiler<JavaCompileSpec> createTargetCompiler(Class<? extends CompileSpec> type, boolean jointCompilation) {
+        if (!JavaCompileSpec.class.isAssignableFrom(type)) {
+            throw new IllegalArgumentException(String.format("Cannot create a compiler for a spec with type %s", type.getSimpleName()));
+        }
+
+        if (CommandLineJavaCompileSpec.class.isAssignableFrom(type)) {
+            return new CommandLineJavaCompiler();
+        }
+
+        Compiler<JavaCompileSpec> compiler = new JdkJavaCompiler(javaHomeBasedJavaCompilerFactory);
+        if (ForkingJavaCompileSpec.class.isAssignableFrom(type) && !jointCompilation) {
+            return new DaemonJavaCompiler(daemonWorkingDir, compiler, compilerDaemonFactory);
+        }
+
+        return compiler;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/ForkingJavaCompileSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/ForkingJavaCompileSpec.java
new file mode 100644
index 0000000..a8bf1c9
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/ForkingJavaCompileSpec.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+public interface ForkingJavaCompileSpec {
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java
new file mode 100644
index 0000000..439796c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tasks.compile;
+
+import org.gradle.api.tasks.compile.CompileOptions;
+
+import java.io.File;
+
+public interface JavaCompileSpec extends JvmLanguageCompileSpec {
+    CompileOptions getCompileOptions();
+
+    File getDependencyCacheDir();
+
+    void setDependencyCacheDir(File dependencyCacheDir);
+
+    File getDestinationDir();
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java
new file mode 100644
index 0000000..e062a54
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java
@@ -0,0 +1,212 @@
+/*
+ * 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.tasks.compile;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.JavaVersion;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.tasks.compile.CompileOptions;
+import org.gradle.api.tasks.compile.ForkOptions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JavaCompilerArgumentsBuilder {
+    public static final String USE_UNSHARED_COMPILER_TABLE_OPTION = "-XDuseUnsharedTable=true";
+    public static final String EMPTY_SOURCE_PATH_REF_DIR = "emptySourcePathRef";
+
+    private final JavaCompileSpec spec;
+
+    private boolean includeLauncherOptions;
+    private boolean includeMainOptions = true;
+    private boolean includeClasspath = true;
+    private boolean includeSourceFiles;
+    private boolean includeCustomizations = true;
+
+    private List<String> args;
+
+    public JavaCompilerArgumentsBuilder(JavaCompileSpec spec) {
+        this.spec = spec;
+    }
+
+    public JavaCompilerArgumentsBuilder includeLauncherOptions(boolean flag) {
+        includeLauncherOptions = flag;
+        return this;
+    }
+
+    public JavaCompilerArgumentsBuilder includeMainOptions(boolean flag) {
+        includeMainOptions = flag;
+        return this;
+    }
+
+    public JavaCompilerArgumentsBuilder includeClasspath(boolean flag) {
+        includeClasspath = flag;
+        return this;
+    }
+
+    public JavaCompilerArgumentsBuilder includeSourceFiles(boolean flag) {
+        includeSourceFiles = flag;
+        return this;
+    }
+
+    public JavaCompilerArgumentsBuilder includeCustomizations(boolean flag) {
+        includeCustomizations = flag;
+        return this;
+    }
+
+    public List<String> build() {
+        args = new ArrayList<String>();
+
+        addLauncherOptions();
+        addMainOptions();
+        addClasspath();
+        addSourceFiles();
+        addCustomizations();
+
+        return args;
+    }
+
+    private void addCustomizations() {
+        if (includeCustomizations) {
+            /*This is an internal option, it's used in com.sun.tools.javac.util.Names#createTable(Options options). The -XD backdoor switch is used to set it, as described in a comment
+            in com.sun.tools.javac.main.RecognizedOptions#getAll(OptionHelper helper). This option was introduced in JDK 7 and controls if compiler's name tables should be reused.
+            Without this option being set they are stored in a static list using soft references which can lead to memory pressure and performance deterioration
+            when using the daemon, especially when using small heap and building a large project.
+            Due to a bug (https://builds.gradle.org/viewLog.html?buildId=284033&tab=buildResultsDiv&buildTypeId=Gradle_Master_Performance_PerformanceExperimentsLinux) no instances of
+            SharedNameTable are actually ever reused. It has been fixed for JDK9 and we should consider not using this option with JDK9 as not using it  will quite probably improve the
+            performance of compilation.
+            Using this option leads to significant performance improvements when using daemon and compiling java sources with JDK7 and JDK8.*/
+            args.add(USE_UNSHARED_COMPILER_TABLE_OPTION);
+        }
+    }
+
+    private void addLauncherOptions() {
+        if (!includeLauncherOptions) {
+            return;
+        }
+
+        ForkOptions forkOptions = spec.getCompileOptions().getForkOptions();
+        if (forkOptions.getMemoryInitialSize() != null) {
+            args.add("-J-Xms" + forkOptions.getMemoryInitialSize().trim());
+        }
+        if (forkOptions.getMemoryMaximumSize() != null) {
+            args.add("-J-Xmx" + forkOptions.getMemoryMaximumSize().trim());
+        }
+        if (forkOptions.getJvmArgs() != null) {
+            args.addAll(forkOptions.getJvmArgs());
+        }
+    }
+
+    private void addMainOptions() {
+        if (!includeMainOptions) {
+            return;
+        }
+
+        String sourceCompatibility = spec.getSourceCompatibility();
+        if (sourceCompatibility != null && !JavaVersion.current().equals(JavaVersion.toVersion(sourceCompatibility))) {
+            args.add("-source");
+            args.add(sourceCompatibility);
+        }
+        String targetCompatibility = spec.getTargetCompatibility();
+        if (targetCompatibility != null && !JavaVersion.current().equals(JavaVersion.toVersion(targetCompatibility))) {
+            args.add("-target");
+            args.add(targetCompatibility);
+        }
+        File destinationDir = spec.getDestinationDir();
+        if (destinationDir != null) {
+            args.add("-d");
+            args.add(destinationDir.getPath());
+        }
+        CompileOptions compileOptions = spec.getCompileOptions();
+        if (compileOptions.isVerbose()) {
+            args.add("-verbose");
+        }
+        if (compileOptions.isDeprecation()) {
+            args.add("-deprecation");
+        }
+        if (!compileOptions.isWarnings()) {
+            args.add("-nowarn");
+        }
+        if (compileOptions.isDebug()) {
+            if (compileOptions.getDebugOptions().getDebugLevel() != null) {
+                args.add("-g:" + compileOptions.getDebugOptions().getDebugLevel().trim());
+            } else {
+                args.add("-g");
+            }
+        } else {
+            args.add("-g:none");
+        }
+        if (compileOptions.getEncoding() != null) {
+            args.add("-encoding");
+            args.add(compileOptions.getEncoding());
+        }
+        if (compileOptions.getBootClasspath() != null) { //TODO: move bootclasspath to platform
+            args.add("-bootclasspath");
+            args.add(compileOptions.getBootClasspath());
+        }
+        if (compileOptions.getExtensionDirs() != null) {
+            args.add("-extdirs");
+            args.add(compileOptions.getExtensionDirs());
+        }
+        FileCollection sourcepath = compileOptions.getSourcepath();
+        Iterable<File> classpath = spec.getClasspath();
+        if ((sourcepath != null && !sourcepath.isEmpty()) || (includeClasspath && (classpath != null && classpath.iterator().hasNext()))) {
+            args.add("-sourcepath");
+            args.add(sourcepath == null ? emptyFolder(spec.getTempDir()) : sourcepath.getAsPath());
+        }
+        if (compileOptions.getCompilerArgs() != null) {
+            args.addAll(compileOptions.getCompilerArgs());
+        }
+    }
+
+    private String emptyFolder(File parent) {
+        File emptySourcePath = new File(parent, EMPTY_SOURCE_PATH_REF_DIR);
+        emptySourcePath.mkdirs();
+        return emptySourcePath.getAbsolutePath();
+    }
+
+    private void addClasspath() {
+        if (!includeClasspath) {
+            return;
+        }
+
+        Iterable<File> classpath = spec.getClasspath();
+        if (classpath != null && classpath.iterator().hasNext()) {
+            args.add("-classpath");
+            args.add(toFileCollection(classpath).getAsPath());
+        }
+    }
+
+    private void addSourceFiles() {
+        if (!includeSourceFiles) {
+            return;
+        }
+
+        for (File file : spec.getSource()) {
+            args.add(file.getPath());
+        }
+    }
+
+    private FileCollection toFileCollection(Iterable<File> classpath) {
+        if (classpath instanceof FileCollection) {
+            return (FileCollection) classpath;
+        }
+        return new SimpleFileCollection(Lists.newArrayList(classpath));
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.java
new file mode 100644
index 0000000..31af473
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+
+/**
+ * Creates Java compilers based on the provided compile options.
+ */
+public interface JavaCompilerFactory {
+    Compiler<JavaCompileSpec> create(Class<? extends CompileSpec> type);
+
+    Compiler<JavaCompileSpec> createForJointCompilation(Class<? extends CompileSpec> type);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactory.java
new file mode 100644
index 0000000..4e1d1fd
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.internal.Factory;
+import org.gradle.internal.SystemProperties;
+import org.gradle.internal.jvm.JdkTools;
+import org.gradle.internal.jvm.Jvm;
+
+import javax.tools.JavaCompiler;
+import java.io.File;
+import java.io.Serializable;
+
+public class JavaHomeBasedJavaCompilerFactory implements Factory<JavaCompiler>, Serializable {
+    private final Factory<? extends File> currentJvmJavaHomeFactory;
+    private final Factory<? extends File> systemPropertiesJavaHomeFactory;
+    private final Factory<? extends JavaCompiler> systemJavaCompilerFactory;
+
+    public JavaHomeBasedJavaCompilerFactory() {
+        this(new CurrentJvmJavaHomeFactory(), new SystemPropertiesJavaHomeFactory(), new SystemJavaCompilerFactory());
+    }
+
+    JavaHomeBasedJavaCompilerFactory(Factory<? extends File> currentJvmJavaHomeFactory, Factory<? extends File> systemPropertiesJavaHomeFactory, Factory<? extends JavaCompiler> systemJavaCompilerFactory) {
+        this.currentJvmJavaHomeFactory = currentJvmJavaHomeFactory;
+        this.systemPropertiesJavaHomeFactory = systemPropertiesJavaHomeFactory;
+        this.systemJavaCompilerFactory = systemJavaCompilerFactory;
+    }
+
+    public JavaCompiler create() {
+        JavaCompiler compiler = findCompiler();
+
+        if (compiler == null) {
+            throw new RuntimeException("Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory.");
+        }
+
+        return compiler;
+    }
+
+    private JavaCompiler findCompiler() {
+        File realJavaHome = currentJvmJavaHomeFactory.create();
+        File javaHomeFromToolProvidersPointOfView = systemPropertiesJavaHomeFactory.create();
+        if (realJavaHome.equals(javaHomeFromToolProvidersPointOfView)) {
+            return systemJavaCompilerFactory.create();
+        }
+
+        return SystemProperties.getInstance().withJavaHome(realJavaHome, systemJavaCompilerFactory);
+    }
+
+    public static class CurrentJvmJavaHomeFactory implements Factory<File>, Serializable {
+        public File create() {
+            return Jvm.current().getJavaHome();
+        }
+    }
+
+    public static class SystemPropertiesJavaHomeFactory implements Factory<File>, Serializable {
+        public File create() {
+            return SystemProperties.getInstance().getJavaHomeDir();
+        }
+    }
+
+    public static class SystemJavaCompilerFactory implements Factory<JavaCompiler>, Serializable {
+        public JavaCompiler create() {
+            return JdkTools.current().getSystemJavaCompiler();
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JdkJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JdkJavaCompiler.java
new file mode 100644
index 0000000..42a0d2a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/JdkJavaCompiler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.api.tasks.compile.CompileOptions;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.util.List;
+
+public class JdkJavaCompiler implements Compiler<JavaCompileSpec>, Serializable {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JdkJavaCompiler.class);
+    private final Factory<JavaCompiler> javaHomeBasedJavaCompilerFactory;
+
+    public JdkJavaCompiler(Factory<JavaCompiler> javaHomeBasedJavaCompilerFactory) {
+        this.javaHomeBasedJavaCompilerFactory = javaHomeBasedJavaCompilerFactory;
+    }
+
+    public WorkResult execute(JavaCompileSpec spec) {
+        LOGGER.info("Compiling with JDK Java compiler API.");
+
+        JavaCompiler.CompilationTask task = createCompileTask(spec);
+        boolean success = task.call();
+        if (!success) {
+            throw new CompilationFailedException();
+        }
+
+        return new SimpleWorkResult(true);
+    }
+
+    private JavaCompiler.CompilationTask createCompileTask(JavaCompileSpec spec) {
+        List<String> options = new JavaCompilerArgumentsBuilder(spec).build();
+        JavaCompiler compiler = javaHomeBasedJavaCompilerFactory.create();
+        CompileOptions compileOptions = spec.getCompileOptions();
+        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, compileOptions.getEncoding() != null ? Charset.forName(compileOptions.getEncoding()) : null);
+        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(spec.getSource());
+        return compiler.getTask(null, null, null, options, null, compilationUnits);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java
new file mode 100644
index 0000000..784d690
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A Java {@link Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
+ */
+public class NormalizingJavaCompiler implements Compiler<JavaCompileSpec> {
+    private static final Logger LOGGER = Logging.getLogger(NormalizingJavaCompiler.class);
+    private final Compiler<JavaCompileSpec> delegate;
+
+    public NormalizingJavaCompiler(Compiler<JavaCompileSpec> delegate) {
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(JavaCompileSpec spec) {
+        resolveAndFilterSourceFiles(spec);
+        resolveClasspath(spec);
+        resolveNonStringsInCompilerArgs(spec);
+        logSourceFiles(spec);
+        logCompilerArguments(spec);
+        return delegateAndHandleErrors(spec);
+    }
+
+    private void resolveAndFilterSourceFiles(JavaCompileSpec spec) {
+        // this mimics the behavior of the Ant javac task (and therefore AntJavaCompiler),
+        // which silently excludes files not ending in .java
+        FileCollection javaOnly = spec.getSource().filter(new Spec<File>() {
+            public boolean isSatisfiedBy(File element) {
+                return element.getName().endsWith(".java");
+            }
+        });
+
+        spec.setSource(new SimpleFileCollection(javaOnly.getFiles()));
+    }
+
+    private void resolveClasspath(JavaCompileSpec spec) {
+        spec.setClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getClasspath())));
+    }
+
+    private void resolveNonStringsInCompilerArgs(JavaCompileSpec spec) {
+        // in particular, this is about GStrings
+        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
+    }
+
+    private void logSourceFiles(JavaCompileSpec spec) {
+        if (!spec.getCompileOptions().isListFiles()) {
+            return;
+        }
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("Source files to be compiled:");
+        for (File file : spec.getSource()) {
+            builder.append('\n');
+            builder.append(file);
+        }
+
+        LOGGER.quiet(builder.toString());
+    }
+
+    private void logCompilerArguments(JavaCompileSpec spec) {
+        if (!LOGGER.isDebugEnabled()) {
+            return;
+        }
+
+        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
+        String joinedArgs = Joiner.on(' ').join(compilerArgs);
+        LOGGER.debug("Compiler arguments: {}", joinedArgs);
+    }
+
+    private WorkResult delegateAndHandleErrors(JavaCompileSpec spec) {
+        try {
+            return delegate.execute(spec);
+        } catch (CompilationFailedException e) {
+            if (spec.getCompileOptions().isFailOnError()) {
+                throw e;
+            }
+            LOGGER.debug("Ignoring compilation failure.");
+            return new SimpleWorkResult(false);
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdater.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdater.java
new file mode 100644
index 0000000..a77591a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdater.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.tasks.compile.incremental;
+
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.cache.Stash;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassFilesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.util.Clock;
+
+public class ClassSetAnalysisUpdater {
+
+    private final static Logger LOG = Logging.getLogger(ClassSetAnalysisUpdater.class);
+
+    private final Stash<ClassSetAnalysisData> stash;
+    private final FileOperations fileOperations;
+    private ClassDependenciesAnalyzer analyzer;
+
+    public ClassSetAnalysisUpdater(Stash<ClassSetAnalysisData> stash, FileOperations fileOperations, ClassDependenciesAnalyzer analyzer) {
+        this.stash = stash;
+        this.fileOperations = fileOperations;
+        this.analyzer = analyzer;
+    }
+
+    public void updateAnalysis(JavaCompileSpec spec) {
+        Clock clock = new Clock();
+        FileTree tree = fileOperations.fileTree(spec.getDestinationDir());
+        ClassFilesAnalyzer analyzer = new ClassFilesAnalyzer(this.analyzer);
+        tree.visit(analyzer);
+        ClassSetAnalysisData data = analyzer.getAnalysis();
+        stash.put(data);
+        LOG.info("Class dependency analysis for incremental compilation took {}.", clock.getTime());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/CompilationSourceDirs.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/CompilationSourceDirs.java
new file mode 100644
index 0000000..d6d58c0
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/CompilationSourceDirs.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.tasks.compile.incremental;
+
+import org.gradle.api.file.SourceDirectorySet;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CompilationSourceDirs {
+
+    private List<Object> source;
+
+    public CompilationSourceDirs(List<Object> source) {
+        this.source = source;
+    }
+
+    List<File> getSourceDirs() {
+        List<File> sourceDirs = new LinkedList<File>();
+        for (Object s : source) {
+            if (s instanceof SourceDirectorySet) {
+                sourceDirs.addAll(((SourceDirectorySet) s).getSrcDirs());
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        }
+        return sourceDirs;
+    }
+
+    public boolean areSourceDirsKnown() {
+        for (Object s : source) {
+            if (!(s instanceof SourceDirectorySet)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizer.java
new file mode 100644
index 0000000..d32895e
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizer.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.internal.tasks.compile.incremental;
+
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshotWriter;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+
+class IncrementalCompilationFinalizer implements Compiler<JavaCompileSpec> {
+
+    private final Compiler<JavaCompileSpec> delegate;
+    private final JarClasspathSnapshotWriter writer;
+    private final ClassSetAnalysisUpdater updater;
+
+    public IncrementalCompilationFinalizer(Compiler<JavaCompileSpec> delegate, JarClasspathSnapshotWriter writer,
+                                           ClassSetAnalysisUpdater updater) {
+        this.delegate = delegate;
+        this.writer = writer;
+        this.updater = updater;
+    }
+
+    public WorkResult execute(JavaCompileSpec spec) {
+        WorkResult out = delegate.execute(spec);
+
+        if (!(out instanceof RecompilationNotNecessary)) {
+            //if recompilation was skipped
+            //there's no point in updating because we have exactly the same output classes)
+            updater.updateAnalysis(spec);
+        }
+
+        writer.storeJarSnapshots(spec.getClasspath());
+
+        return out;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializer.java
new file mode 100644
index 0000000..b2eada7
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializer.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental;
+
+import com.google.common.collect.Iterables;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.tasks.util.PatternSet;
+
+import java.util.Collection;
+
+import static java.util.Arrays.asList;
+
+class IncrementalCompilationInitializer {
+    private final FileOperations fileOperations;
+
+    public IncrementalCompilationInitializer(FileOperations fileOperations) {
+        this.fileOperations = fileOperations;
+    }
+
+    public void initializeCompilation(JavaCompileSpec spec, Collection<String> staleClasses) {
+        if (staleClasses.isEmpty()) {
+            spec.setSource(new SimpleFileCollection());
+            return; //do nothing. No classes need recompilation.
+        }
+
+        PatternSet classesToDelete = new PatternSet();
+        PatternSet sourceToCompile = new PatternSet();
+
+        preparePatterns(staleClasses, classesToDelete, sourceToCompile);
+
+        //selectively configure the source
+        spec.setSource(spec.getSource().getAsFileTree().matching(sourceToCompile));
+        //since we're compiling selectively we need to include the classes compiled previously
+        spec.setClasspath(Iterables.concat(spec.getClasspath(), asList(spec.getDestinationDir())));
+        //get rid of stale files
+        FileTree deleteMe = fileOperations.fileTree(spec.getDestinationDir()).matching(classesToDelete);
+        fileOperations.delete(deleteMe);
+    }
+
+    void preparePatterns(Collection<String> staleClasses, PatternSet classesToDelete, PatternSet sourceToCompile) {
+        assert !staleClasses.isEmpty(); //if stale classes are empty (e.g. nothing to recompile), the patterns will not have any includes and will match all (e.g. recompile everything).
+        for (String staleClass : staleClasses) {
+            String path = staleClass.replaceAll("\\.", "/");
+            classesToDelete.include(path.concat(".class"));
+            classesToDelete.include(path.concat("$*.class"));
+
+            //the stale class might be a source class that was deleted
+            //it's no harm to include it in sourceToCompile anyway
+            sourceToCompile.include(path.concat(".java"));
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerDecorator.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerDecorator.java
new file mode 100644
index 0000000..f94915b
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerDecorator.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental;
+
+import org.gradle.api.internal.tasks.compile.CleaningJavaCompiler;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.cache.CompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysis;
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshotMaker;
+import org.gradle.api.internal.tasks.compile.incremental.jar.PreviousCompilation;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.language.base.internal.compile.Compiler;
+
+public class IncrementalCompilerDecorator {
+
+    private static final Logger LOG = Logging.getLogger(IncrementalCompilerDecorator.class);
+    private final JarClasspathSnapshotMaker jarClasspathSnapshotMaker;
+    private final CompileCaches compileCaches;
+    private final CleaningJavaCompiler cleaningCompiler;
+    private final String displayName;
+    private final RecompilationSpecProvider staleClassDetecter;
+    private final ClassSetAnalysisUpdater classSetAnalysisUpdater;
+    private final CompilationSourceDirs sourceDirs;
+    private final IncrementalCompilationInitializer compilationInitializer;
+
+    public IncrementalCompilerDecorator(JarClasspathSnapshotMaker jarClasspathSnapshotMaker, CompileCaches compileCaches,
+                                        IncrementalCompilationInitializer compilationInitializer, CleaningJavaCompiler cleaningCompiler, String displayName,
+                                        RecompilationSpecProvider staleClassDetecter, ClassSetAnalysisUpdater classSetAnalysisUpdater,
+                                        CompilationSourceDirs sourceDirs) {
+        this.jarClasspathSnapshotMaker = jarClasspathSnapshotMaker;
+        this.compileCaches = compileCaches;
+        this.compilationInitializer = compilationInitializer;
+        this.cleaningCompiler = cleaningCompiler;
+        this.displayName = displayName;
+        this.staleClassDetecter = staleClassDetecter;
+        this.classSetAnalysisUpdater = classSetAnalysisUpdater;
+        this.sourceDirs = sourceDirs;
+    }
+
+    public Compiler<JavaCompileSpec> prepareCompiler(final IncrementalTaskInputs inputs) {
+        final Compiler<JavaCompileSpec> compiler = getCompiler(inputs, sourceDirs);
+        return new IncrementalCompilationFinalizer(compiler, jarClasspathSnapshotMaker, classSetAnalysisUpdater);
+    }
+
+    private Compiler<JavaCompileSpec> getCompiler(IncrementalTaskInputs inputs, CompilationSourceDirs sourceDirs) {
+        if (!inputs.isIncremental()) {
+            LOG.lifecycle("{} - is not incremental (e.g. outputs have changed, no previous execution, etc.).", displayName);
+            return cleaningCompiler;
+        }
+        if (!sourceDirs.areSourceDirsKnown()) {
+            LOG.lifecycle("{} - is not incremental. Unable to infer the source directories.", displayName);
+            return cleaningCompiler;
+        }
+        ClassSetAnalysisData data = compileCaches.getLocalClassSetAnalysisStore().get();
+        if (data == null) {
+            LOG.lifecycle("{} - is not incremental. No class analysis data available from the previous build.", displayName);
+            return cleaningCompiler;
+        }
+        PreviousCompilation previousCompilation = new PreviousCompilation(new ClassSetAnalysis(data), compileCaches.getLocalJarClasspathSnapshotStore(), compileCaches.getJarSnapshotCache());
+        return new SelectiveCompiler(inputs, previousCompilation, cleaningCompiler, staleClassDetecter, compilationInitializer, jarClasspathSnapshotMaker);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerFactory.java
new file mode 100644
index 0000000..f74cd44
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilerFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental;
+
+import org.gradle.api.internal.changedetection.changes.IncrementalTaskInputsInternal;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.hash.DefaultHasher;
+import org.gradle.api.internal.hash.Hasher;
+import org.gradle.api.internal.tasks.compile.CleaningJavaCompiler;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.CachingClassDependenciesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.DefaultClassDependenciesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.cache.CompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.jar.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.util.List;
+
+public class IncrementalCompilerFactory {
+
+    private final IncrementalCompilerDecorator incrementalSupport;
+    private final IncrementalTaskInputs inputs;
+
+    public IncrementalCompilerFactory(FileOperations fileOperations, String compileDisplayName, CleaningJavaCompiler cleaningJavaCompiler,
+                                      List<Object> source, CompileCaches compileCaches, IncrementalTaskInputsInternal inputs) {
+        this.inputs = inputs;
+        //bunch of services that enable incremental java compilation.
+        Hasher hasher = new DefaultHasher(); //TODO SF use caching hasher
+        ClassDependenciesAnalyzer analyzer = new CachingClassDependenciesAnalyzer(new DefaultClassDependenciesAnalyzer(), hasher, compileCaches.getClassAnalysisCache());
+        JarSnapshotter jarSnapshotter = new CachingJarSnapshotter(hasher, analyzer, compileCaches.getJarSnapshotCache(), inputs.getInputFilesSnapshot());
+
+        JarClasspathSnapshotMaker jarClasspathSnapshotMaker = new JarClasspathSnapshotMaker(compileCaches.getLocalJarClasspathSnapshotStore(), new JarClasspathSnapshotFactory(jarSnapshotter), new ClasspathJarFinder(fileOperations));
+        CompilationSourceDirs sourceDirs = new CompilationSourceDirs(source);
+        SourceToNameConverter sourceToNameConverter = new SourceToNameConverter(sourceDirs); //TODO SF replace with converter that parses input source class
+        RecompilationSpecProvider recompilationSpecProvider = new RecompilationSpecProvider(sourceToNameConverter, fileOperations);
+        ClassSetAnalysisUpdater classSetAnalysisUpdater = new ClassSetAnalysisUpdater(compileCaches.getLocalClassSetAnalysisStore(), fileOperations, analyzer);
+        IncrementalCompilationInitializer compilationInitializer = new IncrementalCompilationInitializer(fileOperations);
+        incrementalSupport = new IncrementalCompilerDecorator(jarClasspathSnapshotMaker, compileCaches, compilationInitializer,
+                cleaningJavaCompiler, compileDisplayName, recompilationSpecProvider, classSetAnalysisUpdater, sourceDirs);
+    }
+
+    public Compiler<JavaCompileSpec> createCompiler() {
+        return incrementalSupport.prepareCompiler(inputs);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/JavaChangeProcessor.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/JavaChangeProcessor.java
new file mode 100644
index 0000000..4ca7205
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/JavaChangeProcessor.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.api.internal.tasks.compile.incremental;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependentsSet;
+import org.gradle.api.internal.tasks.compile.incremental.jar.PreviousCompilation;
+import org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpec;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+class JavaChangeProcessor {
+
+    private final SourceToNameConverter sourceToNameConverter;
+    private final PreviousCompilation previousCompilation;
+
+    public JavaChangeProcessor(PreviousCompilation previousCompilation, SourceToNameConverter sourceToNameConverter) {
+        this.previousCompilation = previousCompilation;
+        this.sourceToNameConverter = sourceToNameConverter;
+    }
+
+    public void processChange(InputFileDetails input, RecompilationSpec spec) {
+        String className = sourceToNameConverter.getClassName(input.getFile());
+        spec.getClassNames().add(className);
+        DependentsSet actualDependents = previousCompilation.getDependents(className);
+        if (actualDependents.isDependencyToAll()) {
+            spec.setFullRebuildCause(actualDependents.getDescription(), input.getFile());
+            return;
+        }
+        spec.getClassNames().addAll(actualDependents.getDependentClasses());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationNotNecessary.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationNotNecessary.java
new file mode 100644
index 0000000..6c1242a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationNotNecessary.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.api.internal.tasks.compile.incremental;
+
+import org.gradle.api.tasks.WorkResult;
+
+public class RecompilationNotNecessary implements WorkResult {
+
+    public boolean getDidWork() {
+        return true;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationSpecProvider.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationSpecProvider.java
new file mode 100644
index 0000000..a5b4839
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/RecompilationSpecProvider.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.tasks.compile.incremental;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarChangeProcessor;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshot;
+import org.gradle.api.internal.tasks.compile.incremental.jar.PreviousCompilation;
+import org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpec;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+public class RecompilationSpecProvider {
+
+    private final SourceToNameConverter sourceToNameConverter;
+    private final FileOperations fileOperations;
+
+    public RecompilationSpecProvider(SourceToNameConverter sourceToNameConverter, FileOperations fileOperations) {
+        this.sourceToNameConverter = sourceToNameConverter;
+        this.fileOperations = fileOperations;
+    }
+
+    public RecompilationSpec provideRecompilationSpec(IncrementalTaskInputs inputs, PreviousCompilation previousCompilation, JarClasspathSnapshot jarClasspathSnapshot) {
+        //creating an action that will be executed against all changes
+        RecompilationSpec spec = new RecompilationSpec();
+        JavaChangeProcessor javaChangeProcessor = new JavaChangeProcessor(previousCompilation, sourceToNameConverter);
+        JarChangeProcessor jarChangeProcessor = new JarChangeProcessor(fileOperations, jarClasspathSnapshot, previousCompilation);
+        InputChangeAction action = new InputChangeAction(spec, javaChangeProcessor, jarChangeProcessor);
+
+        //go!
+        inputs.outOfDate(action);
+        if (action.spec.getFullRebuildCause() != null) {
+            //short circuit in case we already know that that full rebuild is needed
+            return action.spec;
+        }
+        inputs.removed(action);
+        return action.spec;
+    }
+
+    private static class InputChangeAction implements Action<InputFileDetails> {
+        private final RecompilationSpec spec;
+        private final JavaChangeProcessor javaChangeProcessor;
+        private final JarChangeProcessor jarChangeProcessor;
+
+        public InputChangeAction(RecompilationSpec spec, JavaChangeProcessor javaChangeProcessor, JarChangeProcessor jarChangeProcessor) {
+            this.spec = spec;
+            this.javaChangeProcessor = javaChangeProcessor;
+            this.jarChangeProcessor = jarChangeProcessor;
+        }
+
+        public void execute(InputFileDetails input) {
+            if (spec.getFullRebuildCause() != null) {
+                return;
+            }
+            if (input.getFile().getName().endsWith(".java")) {
+                javaChangeProcessor.processChange(input, spec);
+            }
+            if (input.getFile().getName().endsWith(".jar")) {
+                jarChangeProcessor.processChange(input, spec);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompiler.java
new file mode 100644
index 0000000..dc83ac1
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompiler.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.tasks.compile.incremental;
+
+import org.gradle.api.internal.tasks.compile.CleaningJavaCompiler;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshot;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshotProvider;
+import org.gradle.api.internal.tasks.compile.incremental.jar.PreviousCompilation;
+import org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpec;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.util.Clock;
+
+class SelectiveCompiler implements org.gradle.language.base.internal.compile.Compiler<JavaCompileSpec> {
+    private static final Logger LOG = Logging.getLogger(SelectiveCompiler.class);
+    private final IncrementalTaskInputs inputs;
+    private final PreviousCompilation previousCompilation;
+    private final CleaningJavaCompiler cleaningCompiler;
+    private final RecompilationSpecProvider recompilationSpecProvider;
+    private final IncrementalCompilationInitializer incrementalCompilationInitilizer;
+    private final JarClasspathSnapshotProvider jarClasspathSnapshotProvider;
+
+    public SelectiveCompiler(IncrementalTaskInputs inputs, PreviousCompilation previousCompilation, CleaningJavaCompiler cleaningCompiler,
+                             RecompilationSpecProvider recompilationSpecProvider, IncrementalCompilationInitializer compilationInitializer, JarClasspathSnapshotProvider jarClasspathSnapshotProvider) {
+        this.inputs = inputs;
+        this.previousCompilation = previousCompilation;
+        this.cleaningCompiler = cleaningCompiler;
+        this.recompilationSpecProvider = recompilationSpecProvider;
+        this.incrementalCompilationInitilizer = compilationInitializer;
+        this.jarClasspathSnapshotProvider = jarClasspathSnapshotProvider;
+    }
+
+    public WorkResult execute(JavaCompileSpec spec) {
+        Clock clock = new Clock();
+        JarClasspathSnapshot jarClasspathSnapshot = jarClasspathSnapshotProvider.getJarClasspathSnapshot(spec.getClasspath());
+        RecompilationSpec recompilationSpec = recompilationSpecProvider.provideRecompilationSpec(inputs, previousCompilation, jarClasspathSnapshot);
+
+        if (recompilationSpec.isFullRebuildNeeded()) {
+            LOG.lifecycle("Full recompilation is required because {}. Analysis took {}.", recompilationSpec.getFullRebuildCause(), clock.getTime());
+            return cleaningCompiler.execute(spec);
+        }
+
+        incrementalCompilationInitilizer.initializeCompilation(spec, recompilationSpec.getClassNames());
+        if (spec.getSource().isEmpty()) {
+            LOG.lifecycle("None of the classes needs to compiled! Analysis took {}. ", clock.getTime());
+            return new RecompilationNotNecessary();
+        }
+
+        try {
+            //use the original compiler to avoid cleaning up all the files
+            return cleaningCompiler.getCompiler().execute(spec);
+        } finally {
+            LOG.lifecycle("Incremental compilation of {} classes completed in {}.", recompilationSpec.getClassNames().size(), clock.getTime());
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverter.java
new file mode 100644
index 0000000..3ca5131
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverter.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.tasks.compile.incremental;
+
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.util.List;
+
+import static java.lang.String.format;
+
+public class SourceToNameConverter {
+
+    private CompilationSourceDirs sourceDirs;
+
+    public SourceToNameConverter(CompilationSourceDirs sourceDirs) {
+        this.sourceDirs = sourceDirs;
+    }
+
+    public String getClassName(File javaSourceClass) {
+        List<File> dirs = sourceDirs.getSourceDirs();
+        for (File sourceDir : dirs) {
+            if (javaSourceClass.getAbsolutePath().startsWith(sourceDir.getAbsolutePath())) { //perf tweak only
+                String relativePath = GFileUtils.relativePath(sourceDir, javaSourceClass);
+                if (!relativePath.startsWith("..")) {
+                    return relativePath.replaceAll("/", ".").replaceAll("\\.java$", "");
+                }
+            }
+        }
+        throw new IllegalArgumentException(format("Unable to find source java class: '%s' because it does not belong to any of the source dirs: '%s'",
+                javaSourceClass, dirs));
+
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/CachingClassDependenciesAnalyzer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/CachingClassDependenciesAnalyzer.java
new file mode 100644
index 0000000..b29e9e4
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/CachingClassDependenciesAnalyzer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.analyzer;
+
+import org.gradle.api.internal.hash.Hasher;
+import org.gradle.internal.Factory;
+
+import java.io.File;
+
+public class CachingClassDependenciesAnalyzer implements ClassDependenciesAnalyzer {
+
+    private final ClassDependenciesAnalyzer analyzer;
+    private final Hasher hasher;
+    private final ClassAnalysisCache cache;
+
+    public CachingClassDependenciesAnalyzer(ClassDependenciesAnalyzer analyzer, Hasher hasher, ClassAnalysisCache cache) {
+        this.analyzer = analyzer;
+        this.hasher = hasher;
+        this.cache = cache;
+    }
+
+    public ClassAnalysis getClassAnalysis(final String className, final File classFile) {
+        byte[] hash = hasher.hash(classFile);
+        return cache.get(hash, new Factory<ClassAnalysis>() {
+            public ClassAnalysis create() {
+                return analyzer.getClassAnalysis(className, classFile);
+            }
+        });
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.java
new file mode 100644
index 0000000..953cfcb
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.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.tasks.compile.incremental.analyzer;
+
+import java.util.Set;
+
+public class ClassAnalysis {
+
+    private final Set<String> classDependencies;
+    private final boolean dependencyToAll;
+
+    public ClassAnalysis(Set<String> classDependencies, boolean dependencyToAll) {
+        this.classDependencies = classDependencies;
+        this.dependencyToAll = dependencyToAll;
+    }
+
+    public Set<String> getClassDependencies() {
+        return classDependencies;
+    }
+
+    public boolean isDependencyToAll() {
+        return dependencyToAll;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisCache.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisCache.java
new file mode 100644
index 0000000..efeead4
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisCache.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.analyzer;
+
+import org.gradle.api.internal.cache.Cache;
+
+public interface ClassAnalysisCache extends Cache<byte[], ClassAnalysis> {
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisSerializer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisSerializer.java
new file mode 100644
index 0000000..5b8150e
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysisSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.analyzer;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.internal.serialize.SetSerializer;
+
+import java.util.Set;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.STRING_SERIALIZER;
+
+public class ClassAnalysisSerializer implements Serializer<ClassAnalysis> {
+
+    private SetSerializer<String> setSerializer = new SetSerializer<String>(STRING_SERIALIZER, false);
+
+    public ClassAnalysis read(Decoder decoder) throws Exception {
+        boolean relatedToAll = decoder.readBoolean();
+        Set<String> classes = setSerializer.read(decoder);
+        return new ClassAnalysis(classes, relatedToAll);
+    }
+
+    public void write(Encoder encoder, ClassAnalysis value) throws Exception {
+        encoder.writeBoolean(value.isDependencyToAll());
+        setSerializer.write(encoder, value.getClassDependencies());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java
new file mode 100644
index 0000000..1ae344d
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.analyzer;
+
+import java.io.File;
+
+public interface ClassDependenciesAnalyzer {
+    ClassAnalysis getClassAnalysis(String className, File classFile);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.java
new file mode 100644
index 0000000..d05a6ce
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.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.tasks.compile.incremental.analyzer;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Opcodes;
+
+class ClassDependenciesVisitor extends ClassVisitor {
+
+    private final static int API = Opcodes.ASM5;
+    boolean dependentToAll;
+
+    public ClassDependenciesVisitor() {
+        super(API);
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+        if (isAnnotationType(interfaces)) {
+            dependentToAll = true;
+        }
+    }
+
+    private boolean isAnnotationType(String[] interfaces) {
+        return interfaces.length == 1 && interfaces[0].equals("java/lang/annotation/Annotation");
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+        if (isConstant(access) && !isPrivate(access)) {
+            dependentToAll = true; //non-private const
+        }
+        return null;
+    }
+
+    private static boolean isPrivate(int access) {
+        return (access & Opcodes.ACC_PRIVATE) != 0;
+    }
+
+    private static boolean isConstant(int access) {
+        return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassFilesAnalyzer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassFilesAnalyzer.java
new file mode 100644
index 0000000..9d53f94
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassFilesAnalyzer.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.tasks.compile.incremental.analyzer;
+
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassDependentsAccumulator;
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData;
+
+import java.io.File;
+
+public class ClassFilesAnalyzer implements FileVisitor {
+
+    private final ClassDependenciesAnalyzer analyzer;
+    private final String packagePrefix;
+    private final ClassDependentsAccumulator accumulator;
+
+    public ClassFilesAnalyzer(ClassDependenciesAnalyzer analyzer) {
+        this(analyzer, "", new ClassDependentsAccumulator(""));
+    }
+
+    ClassFilesAnalyzer(ClassDependenciesAnalyzer analyzer, String packagePrefix, ClassDependentsAccumulator accumulator) {
+        this.analyzer = analyzer;
+        this.packagePrefix = packagePrefix;
+        this.accumulator = accumulator;
+    }
+
+    public void visitDir(FileVisitDetails dirDetails) {}
+
+    public void visitFile(FileVisitDetails fileDetails) {
+        File file = fileDetails.getFile();
+        if (!file.getName().endsWith(".class")) {
+            return;
+        }
+        String className = fileDetails.getPath().replaceAll("/", ".").replaceAll("\\.class$", "");
+        if (!className.startsWith(packagePrefix)) {
+            return;
+        }
+
+        ClassAnalysis analysis = analyzer.getClassAnalysis(className, file);
+        accumulator.addClass(className, analysis.isDependencyToAll(), analysis.getClassDependencies());
+    }
+
+    public ClassSetAnalysisData getAnalysis() {
+        return new ClassSetAnalysisData(accumulator.getDependentsMap());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.java
new file mode 100644
index 0000000..ea19ca8
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.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.tasks.compile.incremental.analyzer;
+
+class ClassRelevancyFilter {
+
+    private String excludedClassName;
+
+    public ClassRelevancyFilter(String excludedClassName) {
+        this.excludedClassName = excludedClassName;
+    }
+
+    boolean isRelevant(String className) {
+        return !className.startsWith("java.") && !excludedClassName.equals(className);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassAnalysisCache.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassAnalysisCache.java
new file mode 100644
index 0000000..b02ce31
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassAnalysisCache.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.analyzer;
+
+import org.gradle.api.internal.cache.MinimalPersistentCache;
+import org.gradle.cache.CacheRepository;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.BYTE_ARRAY_SERIALIZER;
+
+/**
+ * Cross-process, global cache of class bytecode/dependency analysis. Required to make incremental java compilation fast.
+ * The class analysis results are cached globally, so if one project caches ClassA, it can be used by some other project.
+ */
+public class DefaultClassAnalysisCache extends MinimalPersistentCache<byte[], ClassAnalysis> implements ClassAnalysisCache {
+
+    public DefaultClassAnalysisCache(CacheRepository cacheRepository) {
+        super(cacheRepository, "class analysis", BYTE_ARRAY_SERIALIZER, new ClassAnalysisSerializer());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzer.java
new file mode 100644
index 0000000..0f5d5e4
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzer.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.tasks.compile.incremental.analyzer;
+
+import org.gradle.util.GFileUtils;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Type;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+public class DefaultClassDependenciesAnalyzer implements ClassDependenciesAnalyzer {
+
+    public ClassAnalysis getClassAnalysis(String className, InputStream input) throws IOException {
+        ClassRelevancyFilter filter = new ClassRelevancyFilter(className);
+        ClassReader reader = new ClassReader(input);
+        ClassDependenciesVisitor visitor = new ClassDependenciesVisitor();
+        reader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+        Set<String> classDependencies = getClassDependencies(filter, reader);
+        return new ClassAnalysis(classDependencies, visitor.dependentToAll);
+    }
+
+    private Set<String> getClassDependencies(ClassRelevancyFilter filter, ClassReader reader) {
+        Set<String> out = new HashSet<String>();
+        char[] charBuffer = new char[reader.getMaxStringLength()];
+        for (int i = 1; i < reader.getItemCount(); i++) {
+            int itemOffset = reader.getItem(i);
+            if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
+                // A CONSTANT_Class entry, read the class descriptor
+                String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
+                Type type = Type.getObjectType(classDescriptor);
+                while (type.getSort() == Type.ARRAY) {
+                    type = type.getElementType();
+                }
+                if (type.getSort() != Type.OBJECT) {
+                    // A primitive type
+                    continue;
+                }
+                String name = type.getClassName();
+                if (filter.isRelevant(name)) {
+                    out.add(name);
+                }
+            }
+        }
+        return out;
+    }
+
+    public ClassAnalysis getClassAnalysis(String className, File classFile) {
+        FileInputStream input = GFileUtils.openInputStream(classFile);
+        try {
+            return getClassAnalysis(className, input);
+        } catch (IOException e) {
+            throw new RuntimeException("Problems loading class analysis for '" + className + "' from file: " + classFile);
+        } finally {
+            GFileUtils.closeInputStream(input);
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/CompileCaches.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/CompileCaches.java
new file mode 100644
index 0000000..9d6197a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/CompileCaches.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.cache;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.LocalClassSetAnalysisStore;
+import org.gradle.api.internal.tasks.compile.incremental.jar.LocalJarClasspathSnapshotStore;
+
+public interface CompileCaches extends GeneralCompileCaches {
+    LocalJarClasspathSnapshotStore getLocalJarClasspathSnapshotStore();
+    LocalClassSetAnalysisStore getLocalClassSetAnalysisStore();
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/DefaultGeneralCompileCaches.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/DefaultGeneralCompileCaches.java
new file mode 100644
index 0000000..efe55e5
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/DefaultGeneralCompileCaches.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.cache;
+
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysisCache;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarSnapshotCache;
+
+public class DefaultGeneralCompileCaches implements GeneralCompileCaches {
+
+    private final ClassAnalysisCache classAnalysisCache;
+    private final JarSnapshotCache jarSnapshotCache;
+
+    public DefaultGeneralCompileCaches(ClassAnalysisCache classAnalysisCache, JarSnapshotCache jarSnapshotCache) {
+        this.classAnalysisCache = classAnalysisCache;
+        this.jarSnapshotCache = jarSnapshotCache;
+    }
+
+    public ClassAnalysisCache getClassAnalysisCache() {
+        return classAnalysisCache;
+    }
+
+    public JarSnapshotCache getJarSnapshotCache() {
+        return jarSnapshotCache;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/GeneralCompileCaches.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/GeneralCompileCaches.java
new file mode 100644
index 0000000..181e16f
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/cache/GeneralCompileCaches.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.cache;
+
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysisCache;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarSnapshotCache;
+
+public interface GeneralCompileCaches {
+    ClassAnalysisCache getClassAnalysisCache();
+    JarSnapshotCache getJarSnapshotCache();
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/AffectedClasses.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/AffectedClasses.java
new file mode 100644
index 0000000..9bd2764
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/AffectedClasses.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps;
+
+import java.util.Set;
+
+public class AffectedClasses {
+
+    private final DependentsSet altered;
+    private final Set<String> addedClasses;
+
+    public AffectedClasses(DependentsSet altered, Set<String> addedClasses) {
+        this.altered = altered;
+        this.addedClasses = addedClasses;
+    }
+
+    public DependentsSet getAltered() {
+        return altered;
+    }
+
+    public Set<String> getAdded() {
+        return addedClasses;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulator.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulator.java
new file mode 100644
index 0000000..eb2ce2c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulator.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.tasks.compile.incremental.deps;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ClassDependentsAccumulator {
+
+    private final Map<String, DependentsSet> dependents = new HashMap<String, DependentsSet>();
+    private final String packagePrefix;
+
+    public ClassDependentsAccumulator(String packagePrefix) {
+        this.packagePrefix = packagePrefix;
+    }
+
+    public void addClass(String className, boolean dependencyToAll, Iterable<String> classDependencies) {
+        if (className.startsWith(packagePrefix)) {
+            rememberClass(className).setDependencyToAll(dependencyToAll);
+        }
+        for (String dependency : classDependencies) {
+            if (!dependency.equals(className) && dependency.startsWith(packagePrefix)) {
+                DefaultDependentsSet d = rememberClass(dependency);
+                if (className.startsWith(packagePrefix)) {
+                    d.addDependent(className);
+                }
+            }
+        }
+    }
+
+    private DefaultDependentsSet rememberClass(String className) {
+        DependentsSet d = dependents.get(className);
+        if (d == null) {
+            d = new DefaultDependentsSet();
+            dependents.put(className, d);
+        }
+        return (DefaultDependentsSet) d;
+    }
+
+    public Map<String, DependentsSet> getDependentsMap() {
+        return dependents;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysis.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysis.java
new file mode 100644
index 0000000..b581628
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysis.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class ClassSetAnalysis {
+
+    private final ClassSetAnalysisData data;
+
+    public ClassSetAnalysis(ClassSetAnalysisData data) {
+        this.data = data;
+    }
+
+    public DependentsSet getRelevantDependents(Iterable<String> classes) {
+        List<String> result = new LinkedList<String>();
+        for (String cls : classes) {
+            DependentsSet d = getRelevantDependents(cls);
+            if (d.isDependencyToAll()) {
+                return d;
+            }
+            result.addAll(d.getDependentClasses());
+        }
+        return new DefaultDependentsSet(result);
+    }
+
+    public DependentsSet getRelevantDependents(String className) {
+        DependentsSet deps = data.getDependents(className);
+        if (deps == null) {
+            return new DefaultDependentsSet();
+        }
+        if (deps.isDependencyToAll()) {
+            return new DependencyToAll();
+        }
+        Set<String> result = new HashSet<String>();
+        recurseDependents(new HashSet<String>(), result, deps.getDependentClasses());
+        result.remove(className);
+        return new DefaultDependentsSet(result);
+    }
+
+    public boolean isDependencyToAll(String className) {
+        DependentsSet deps = data.getDependents(className);
+        return deps != null && deps.isDependencyToAll();
+    }
+
+    private void recurseDependents(Set<String> visited, Set<String> result, Set<String> dependentClasses) {
+        for (String d : dependentClasses) {
+            if (!visited.add(d)) {
+                continue;
+            }
+            if (!d.contains("$")) { //filter out the inner classes
+                result.add(d);
+            }
+            DependentsSet currentDependents = data.getDependents(d);
+            recurseDependents(visited, result, currentDependents.getDependentClasses());
+        }
+    }
+
+    public ClassSetAnalysisData getData() {
+        return data;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisData.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisData.java
new file mode 100644
index 0000000..82d3b1c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisData.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.MapSerializer;
+import org.gradle.internal.serialize.SetSerializer;
+
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.STRING_SERIALIZER;
+
+public class ClassSetAnalysisData {
+
+    final Map<String, DependentsSet> dependents;
+
+    public ClassSetAnalysisData(Map<String, DependentsSet> dependents) {
+        this.dependents = dependents;
+    }
+
+    public DependentsSet getDependents(String className) {
+        return dependents.get(className);
+    }
+
+    public static class Serializer implements org.gradle.internal.serialize.Serializer<ClassSetAnalysisData> {
+
+        private final MapSerializer<String, DependentsSet> serializer = new MapSerializer<String, DependentsSet>(
+                STRING_SERIALIZER, new DependentsSetSerializer());
+
+        public ClassSetAnalysisData read(Decoder decoder) throws Exception {
+            //we only support one kind of data
+            return new ClassSetAnalysisData(serializer.read(decoder));
+        }
+
+        public void write(Encoder encoder, ClassSetAnalysisData value) throws Exception {
+            //we only support one kind of data
+            serializer.write(encoder, value.dependents);
+        }
+
+        private static class DependentsSetSerializer implements org.gradle.internal.serialize.Serializer<DependentsSet> {
+
+            private SetSerializer<String> setSerializer = new SetSerializer<String>(STRING_SERIALIZER, false);
+
+            public DependentsSet read(Decoder decoder) throws Exception {
+                int control = decoder.readSmallInt();
+                if (control == 0) {
+                    return new DependencyToAll();
+                }
+                if (control != 1 && control != 2) {
+                    throw new IllegalArgumentException("Unable to read the data. Unexpected control value: " + control);
+                }
+                Set<String> classes = setSerializer.read(decoder);
+                return new DefaultDependentsSet(control == 1, classes);
+            }
+
+            public void write(Encoder encoder, DependentsSet value) throws Exception {
+                if (value instanceof DependencyToAll) {
+                    encoder.writeSmallInt(0);
+                } else if (value instanceof DefaultDependentsSet) {
+                    encoder.writeSmallInt(value.isDependencyToAll() ? 1 : 2);
+                    setSerializer.write(encoder, value.getDependentClasses());
+                } else {
+                    throw new IllegalArgumentException("Don't know how to serialize value of type: " + value.getClass() + ", value: " + value);
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DefaultDependentsSet.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DefaultDependentsSet.java
new file mode 100644
index 0000000..27d41e2
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DefaultDependentsSet.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.tasks.compile.incremental.deps;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+
+public class DefaultDependentsSet implements DependentsSet {
+
+    private final Set<String> dependentClasses = new HashSet<String>();
+    private boolean dependencyToAll;
+
+    public DefaultDependentsSet(Collection<String> dependentClasses) {
+        this(false, dependentClasses);
+    }
+
+    public DefaultDependentsSet(boolean dependencyToAll, Collection<String> dependentClasses) {
+        this.dependencyToAll = dependencyToAll;
+        this.dependentClasses.addAll(dependentClasses);
+    }
+
+    public DefaultDependentsSet() {}
+
+    public Set<String> getDependentClasses() {
+        return dependentClasses;
+    }
+
+    public boolean isDependencyToAll() {
+        return dependencyToAll;
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public DefaultDependentsSet addDependent(String className) {
+        dependentClasses.add(className);
+        return this;
+    }
+
+    public static DefaultDependentsSet dependents(String ... dependentClasses) {
+        return new DefaultDependentsSet(asList(dependentClasses));
+    }
+
+    public void setDependencyToAll(boolean dependencyToAll) {
+        this.dependencyToAll = dependencyToAll;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependencyToAll.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependencyToAll.java
new file mode 100644
index 0000000..3785823
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependencyToAll.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.api.internal.tasks.compile.incremental.deps;
+
+import java.util.Set;
+
+public class DependencyToAll implements DependentsSet {
+
+    private final String reason;
+
+    public DependencyToAll(String reason) {
+        this.reason = reason;
+    }
+
+    public DependencyToAll() {
+        this(null);
+    }
+
+    public Set<String> getDependentClasses() {
+        throw new UnsupportedOperationException("This instance of dependents set does not have dependent classes information.");
+    }
+
+    public boolean isDependencyToAll() {
+        return true;
+    }
+
+    public String getDescription() {
+        return reason;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependentsSet.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependentsSet.java
new file mode 100644
index 0000000..72512e5
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/DependentsSet.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.api.internal.tasks.compile.incremental.deps;
+
+import org.gradle.api.Nullable;
+
+import java.util.Set;
+
+public interface DependentsSet {
+    Set<String> getDependentClasses();
+    boolean isDependencyToAll();
+    @Nullable String getDescription();
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/LocalClassSetAnalysisStore.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/LocalClassSetAnalysisStore.java
new file mode 100644
index 0000000..5d9cb35
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/LocalClassSetAnalysisStore.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps;
+
+import org.gradle.api.internal.cache.Loader;
+import org.gradle.api.internal.cache.SingleOperationPersistentStore;
+import org.gradle.api.internal.cache.Stash;
+import org.gradle.cache.CacheRepository;
+
+//Keeps the class set analysis of the given JavaCompile task
+public class LocalClassSetAnalysisStore implements Loader<ClassSetAnalysisData>, Stash<ClassSetAnalysisData> {
+
+    private SingleOperationPersistentStore<ClassSetAnalysisData> store;
+
+    public LocalClassSetAnalysisStore(CacheRepository cacheRepository, Object scope) {
+        //Single operation store that we throw away after the operation makes the implementation simpler.
+        this.store = new SingleOperationPersistentStore<ClassSetAnalysisData>(cacheRepository, scope, "local class set analysis", new ClassSetAnalysisData.Serializer());
+    }
+
+    public void put(ClassSetAnalysisData analysis) {
+        store.putAndClose(analysis);
+    }
+
+    public ClassSetAnalysisData get() {
+        return store.getAndClose();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverter.java
new file mode 100644
index 0000000..d112b42
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverter.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.tasks.compile.incremental.deps;
+
+import java.io.File;
+
+import static org.gradle.util.GFileUtils.relativePath;
+
+class OutputToNameConverter {
+
+    private final File compiledClassesDir;
+
+    public OutputToNameConverter(File compiledClassesDir) {
+        this.compiledClassesDir = compiledClassesDir;
+    }
+
+    public String getClassName(File classFile) {
+        String path = relativePath(compiledClassesDir, classFile);
+        if (path.startsWith("/") || path.startsWith(".")) {
+            throw new IllegalArgumentException("Given input class file: '" + classFile + "' is not located inside of '" + compiledClassesDir + "'.");
+        }
+        return path.replaceAll("/", ".").replaceAll("\\.class", "");
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/CachingJarSnapshotter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/CachingJarSnapshotter.java
new file mode 100644
index 0000000..ca0441d
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/CachingJarSnapshotter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.changedetection.state.FileSnapshot;
+import org.gradle.api.internal.changedetection.state.FilesSnapshotSet;
+import org.gradle.api.internal.hash.Hasher;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer;
+import org.gradle.internal.Factory;
+
+public class CachingJarSnapshotter implements JarSnapshotter {
+
+    private final DefaultJarSnapshotter snapshotter;
+    private final Hasher hasher;
+    private final JarSnapshotCache cache;
+    private final FilesSnapshotSet inputFilesSnapshot;
+
+    public CachingJarSnapshotter(Hasher hasher, ClassDependenciesAnalyzer analyzer, JarSnapshotCache cache, FilesSnapshotSet inputFilesSnapshot) {
+        this.inputFilesSnapshot = inputFilesSnapshot;
+        this.snapshotter = new DefaultJarSnapshotter(hasher, analyzer);
+        this.hasher = hasher;
+        this.cache = cache;
+    }
+
+    public JarSnapshot createSnapshot(final JarArchive jarArchive) {
+        final byte[] hash = getHash(jarArchive);
+        return cache.get(hash, new Factory<JarSnapshot>() {
+            public JarSnapshot create() {
+                return snapshotter.createSnapshot(hash, jarArchive);
+            }
+        });
+    }
+
+    private byte[] getHash(JarArchive jarArchive) {
+        FileSnapshot s = inputFilesSnapshot.findSnapshot(jarArchive.file);
+        if (s != null) {
+            return s.getHash();
+        }
+        return hasher.hash(jarArchive.file);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/ClasspathJarFinder.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/ClasspathJarFinder.java
new file mode 100644
index 0000000..0d728ee
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/ClasspathJarFinder.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.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.file.FileOperations;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ClasspathJarFinder {
+    private final FileOperations fileOperations;
+
+    public ClasspathJarFinder(FileOperations fileOperations) {
+        this.fileOperations = fileOperations;
+    }
+
+    public Iterable<JarArchive> findJarArchives(Iterable<File> classpath) {
+        List<JarArchive> out = new LinkedList<JarArchive>();
+        for (File file : classpath) {
+            if (file.getName().endsWith(".jar")) {
+                out.add(new JarArchive(file, fileOperations.zipTree(file))); //TODO SF only create zip tree when needed, limit usages of JarArchive
+            }
+        }
+        return out;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotCache.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotCache.java
new file mode 100644
index 0000000..07e6472
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotCache.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.cache.MinimalPersistentCache;
+import org.gradle.cache.CacheRepository;
+import org.gradle.internal.Factory;
+import org.gradle.internal.serialize.BaseSerializerFactory;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Cross-process, global cache of jar snapshots. Required to make incremental java compilation fast.
+ * Jar snapshots are cached globally, so if one project caches the groovy jar, it can be used by some other project.
+ */
+public class DefaultJarSnapshotCache implements JarSnapshotCache {
+
+    private final MinimalPersistentCache<byte[], JarSnapshotData> cache;
+
+    public DefaultJarSnapshotCache(CacheRepository cacheRepository) {
+        cache = new MinimalPersistentCache<byte[], JarSnapshotData>(cacheRepository, "jar snapshots", BaseSerializerFactory.BYTE_ARRAY_SERIALIZER, new JarSnapshotDataSerializer());
+    }
+
+    public Map<File, JarSnapshot> getJarSnapshots(final Map<File, byte[]> jarHashes) {
+        return cache.getCacheAccess().useCache("loading jar snapshots", new Factory<Map<File, JarSnapshot>>() {
+            public Map<File, JarSnapshot> create() {
+                final Map<File, JarSnapshot> out = new HashMap<File, JarSnapshot>();
+                for (Map.Entry<File, byte[]> entry : jarHashes.entrySet()) {
+                    JarSnapshot snapshot = new JarSnapshot(cache.getCache().get(entry.getValue()));
+                    out.put(entry.getKey(), snapshot);
+                }
+                return out;
+            }
+        });
+    }
+
+    public JarSnapshot get(byte[] key, final Factory<JarSnapshot> factory) {
+        return new JarSnapshot(cache.get(key, new Factory<JarSnapshotData>() {
+            public JarSnapshotData create() {
+                return factory.create().getData();
+            }
+        }));
+    }
+
+    public void stop() {
+        cache.stop();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotter.java
new file mode 100644
index 0000000..7790014
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotter.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.tasks.compile.incremental.jar;
+
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.hash.Hasher;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassFilesAnalyzer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class DefaultJarSnapshotter {
+
+    private final Hasher hasher;
+    private final ClassDependenciesAnalyzer analyzer;
+
+    public DefaultJarSnapshotter(Hasher hasher, ClassDependenciesAnalyzer analyzer) {
+        this.hasher = hasher;
+        this.analyzer = analyzer;
+    }
+
+    public JarSnapshot createSnapshot(byte[] hash, JarArchive jarArchive) {
+        return createSnapshot(hash, jarArchive.contents, new ClassFilesAnalyzer(analyzer));
+    }
+
+    JarSnapshot createSnapshot(byte[] hash, FileTree classes, final ClassFilesAnalyzer analyzer) {
+        final Map<String, byte[]> hashes = new HashMap<String, byte[]>();
+        classes.visit(new FileVisitor() {
+            public void visitDir(FileVisitDetails dirDetails) {
+            }
+
+            public void visitFile(FileVisitDetails fileDetails) {
+                analyzer.visitFile(fileDetails);
+                String className = fileDetails.getPath().replaceAll("/", ".").replaceAll("\\.class$", "");
+                byte[] classHash = hasher.hash(fileDetails.getFile());
+                hashes.put(className, classHash);
+            }
+        });
+
+        return new JarSnapshot(new JarSnapshotData(hash, hashes, analyzer.getAnalysis()));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarArchive.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarArchive.java
new file mode 100644
index 0000000..6869f59
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarArchive.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.tasks.compile.incremental.jar;
+
+import org.gradle.api.file.FileTree;
+import org.gradle.api.tasks.util.PatternSet;
+
+import java.io.File;
+
+public class JarArchive {
+    final File file;
+    final FileTree contents;
+    public JarArchive(File jar, FileTree contents) {
+        this.file = jar;
+        this.contents = contents.matching(new PatternSet().include("**/*.class"));
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeDependentsFinder.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeDependentsFinder.java
new file mode 100644
index 0000000..82512f9
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeDependentsFinder.java
@@ -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.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.AffectedClasses;
+import org.gradle.api.internal.tasks.compile.incremental.deps.DefaultDependentsSet;
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependencyToAll;
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependentsSet;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+public class JarChangeDependentsFinder {
+
+    private final JarClasspathSnapshot jarClasspathSnapshot;
+    private final PreviousCompilation previousCompilation;
+
+    public JarChangeDependentsFinder(JarClasspathSnapshot jarClasspathSnapshot, PreviousCompilation previousCompilation) {
+        this.jarClasspathSnapshot = jarClasspathSnapshot;
+        this.previousCompilation = previousCompilation;
+    }
+
+    public DependentsSet getActualDependents(InputFileDetails jarChangeDetails, JarArchive jarArchive) {
+        if (jarChangeDetails.isAdded()) {
+            if (jarClasspathSnapshot.isAnyClassDuplicated(jarArchive)) {
+                //at least one of the classes from the new jar is already present in jar classpath
+                //to avoid calculation which class gets on the classpath first, rebuild all
+                return new DependencyToAll("at least one of the classes of '" + jarArchive.file.getName() + "' is already present in classpath");
+            } else {
+                //none of the new classes in the jar are duplicated on classpath, don't rebuild
+                return new DefaultDependentsSet();
+            }
+        }
+        JarSnapshot previous = previousCompilation.getJarSnapshot(jarChangeDetails.getFile());
+
+        if (previous == null) {
+            //we don't know what classes were dependents of the jar in the previous build
+            //for example, a class (in jar) with a constant might have changed into a class without a constant - we need to rebuild everything
+            return new DependencyToAll("missing jar snapshot of '" + jarArchive.file.getName()  + "' from previous build");
+        }
+
+        if (jarChangeDetails.isRemoved()) {
+            DependentsSet allClasses = previous.getAllClasses();
+            if (allClasses.isDependencyToAll()) {
+                return new DependencyToAll("at least one of the classes of removed jar '" + jarArchive.file.getName() + "' requires it");
+            }
+            //recompile all dependents of all the classes from jar
+            return previousCompilation.getDependents(allClasses.getDependentClasses());
+        }
+
+        if (jarChangeDetails.isModified()) {
+            JarSnapshot currentSnapshot = jarClasspathSnapshot.getSnapshot(jarArchive);
+            AffectedClasses affected = currentSnapshot.getAffectedClassesSince(previous);
+            if (affected.getAltered().isDependencyToAll()) {
+                //at least one of the classes changed in the jar is a 'dependency-to-all'
+                return affected.getAltered();
+            }
+
+            if (jarClasspathSnapshot.isAnyClassDuplicated(affected.getAdded())) {
+                //A new duplicate class on classpath. As we don't fancy-handle classpath order right now, we don't know which class is on classpath first.
+                //For safe measure rebuild everything
+                return new DependencyToAll("at least one of the classes of modified jar '" + jarArchive.file.getName() + "' is already present in the classpath");
+            }
+
+            //recompile all dependents of the classes changed in the jar
+            return previousCompilation.getDependents(affected.getAltered().getDependentClasses());
+        }
+
+        throw new IllegalArgumentException("Unknown input file details provided: " + jarChangeDetails);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeProcessor.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeProcessor.java
new file mode 100644
index 0000000..4109303
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarChangeProcessor.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.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependentsSet;
+import org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpec;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+public class JarChangeProcessor {
+
+    private final FileOperations fileOperations;
+    private final JarClasspathSnapshot jarClasspathSnapshot;
+    private final PreviousCompilation previousCompilation;
+
+    public JarChangeProcessor(FileOperations fileOperations, JarClasspathSnapshot jarClasspathSnapshot, PreviousCompilation previousCompilation) {
+        this.fileOperations = fileOperations;
+        this.jarClasspathSnapshot = jarClasspathSnapshot;
+        this.previousCompilation = previousCompilation;
+    }
+
+    public void processChange(InputFileDetails input, RecompilationSpec spec) {
+        JarArchive jarArchive = new JarArchive(input.getFile(), fileOperations.zipTree(input.getFile()));
+        JarChangeDependentsFinder dependentsFinder = new JarChangeDependentsFinder(jarClasspathSnapshot, previousCompilation);
+        DependentsSet actualDependents = dependentsFinder.getActualDependents(input, jarArchive);
+        if (actualDependents.isDependencyToAll()) {
+            spec.setFullRebuildCause(actualDependents.getDescription(), input.getFile());
+            return;
+        }
+        spec.getClassNames().addAll(actualDependents.getDependentClasses());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshot.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshot.java
new file mode 100644
index 0000000..a0ba643
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshot.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+public class JarClasspathSnapshot {
+
+    private final Map<File, JarSnapshot> jarSnapshots;
+    private final JarClasspathSnapshotData data;
+
+    public JarClasspathSnapshot(Map<File, JarSnapshot> jarSnapshots, JarClasspathSnapshotData data) {
+        this.jarSnapshots = jarSnapshots;
+        this.data = data;
+    }
+
+    public JarSnapshot getSnapshot(JarArchive jarArchive) {
+        return jarSnapshots.get(jarArchive.file);
+    }
+
+    public boolean isAnyClassDuplicated(Set<String> classNames) {
+        boolean noCommonElements = Collections.disjoint(data.getDuplicateClasses(), classNames);
+        return !noCommonElements;
+    }
+
+    public JarClasspathSnapshotData getData() {
+        return data;
+    }
+
+    public boolean isAnyClassDuplicated(JarArchive jarArchive) {
+        JarSnapshot snapshot = getSnapshot(jarArchive);
+        return isAnyClassDuplicated(snapshot.getClasses());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotData.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotData.java
new file mode 100644
index 0000000..5ca4a1b
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotData.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+public class JarClasspathSnapshotData {
+
+    private final Map<File, byte[]> jarHashes;
+    private final Set<String> duplicateClasses;
+
+    public JarClasspathSnapshotData(Map<File, byte[]> jarHashes, Set<String> duplicateClasses) {
+        this.jarHashes = jarHashes;
+        this.duplicateClasses = duplicateClasses;
+    }
+
+    public Set<String> getDuplicateClasses() {
+        return duplicateClasses;
+    }
+
+    public Map<File, byte[]> getJarHashes() {
+        return jarHashes;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotDataSerializer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotDataSerializer.java
new file mode 100644
index 0000000..7b4ac66
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotDataSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.internal.serialize.*;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.*;
+
+public class JarClasspathSnapshotDataSerializer implements Serializer<JarClasspathSnapshotData> {
+
+    private final MapSerializer<File, byte[]> mapSerializer = new MapSerializer<File, byte[]>(FILE_SERIALIZER, BYTE_ARRAY_SERIALIZER);
+    private final SetSerializer<String> setSerializer = new SetSerializer<String>(STRING_SERIALIZER, false);
+
+    public JarClasspathSnapshotData read(Decoder decoder) throws Exception {
+        Set<String> duplicates = setSerializer.read(decoder);
+        Map<File, byte[]> hashes = mapSerializer.read(decoder);
+        return new JarClasspathSnapshotData(hashes, duplicates);
+    }
+
+    public void write(Encoder encoder, JarClasspathSnapshotData value) throws Exception {
+        setSerializer.write(encoder, value.getDuplicateClasses());
+        mapSerializer.write(encoder, value.getJarHashes());
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactory.java
new file mode 100644
index 0000000..f4dd4f4
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class JarClasspathSnapshotFactory {
+
+    private final JarSnapshotter jarSnapshotter;
+
+    public JarClasspathSnapshotFactory(JarSnapshotter jarSnapshotter) {
+        this.jarSnapshotter = jarSnapshotter;
+    }
+
+    JarClasspathSnapshot createSnapshot(Iterable<JarArchive> jarArchives) {
+        Map<File, JarSnapshot> jarSnapshots = new HashMap<File, JarSnapshot>();
+        Map<File, byte[]> jarHashes = new HashMap<File, byte[]>();
+        Set<String> allClasses = new HashSet<String>();
+        Set<String> duplicateClasses = new HashSet<String>();
+
+        for (JarArchive jar : jarArchives) {
+            JarSnapshot snapshot = jarSnapshotter.createSnapshot(jar);
+            jarSnapshots.put(jar.file, snapshot);
+            jarHashes.put(jar.file, snapshot.getHash());
+            for (String c : snapshot.getClasses()) {
+                if (!allClasses.add(c)) {
+                    duplicateClasses.add(c);
+                }
+            }
+        }
+        JarClasspathSnapshotData jarClasspathSnapshotData = new JarClasspathSnapshotData(jarHashes, duplicateClasses);
+        return new JarClasspathSnapshot(jarSnapshots, jarClasspathSnapshotData);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMaker.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMaker.java
new file mode 100644
index 0000000..78e4d7d
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMaker.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.tasks.compile.incremental.jar;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.util.Clock;
+
+import java.io.File;
+
+public class JarClasspathSnapshotMaker implements JarClasspathSnapshotProvider, JarClasspathSnapshotWriter {
+
+    private static final Logger LOG = Logging.getLogger(JarClasspathSnapshotMaker.class);
+
+    private final LocalJarClasspathSnapshotStore classpathSnapshotStore;
+    private final ClasspathJarFinder classpathJarFinder;
+    private final JarClasspathSnapshotFactory classpathSnapshotFactory;
+
+    private JarClasspathSnapshot jarClasspathSnapshot;
+
+    public JarClasspathSnapshotMaker(LocalJarClasspathSnapshotStore classpathSnapshotStore, JarClasspathSnapshotFactory classpathSnapshotFactory, ClasspathJarFinder classpathJarFinder) {
+        this.classpathSnapshotStore = classpathSnapshotStore;
+        this.classpathSnapshotFactory = classpathSnapshotFactory;
+        this.classpathJarFinder = classpathJarFinder;
+    }
+
+    public void storeJarSnapshots(Iterable<File> classpath) {
+        maybeInitialize(classpath); //clients may or may not have already created jar classpath snapshot
+        Clock clock = new Clock();
+        classpathSnapshotStore.put(jarClasspathSnapshot.getData());
+        LOG.info("Written jar classpath snapshot for incremental compilation in {}.", clock.getTime());
+    }
+
+    public JarClasspathSnapshot getJarClasspathSnapshot(Iterable<File> classpath) {
+        maybeInitialize(classpath); //clients may or may not have already created jar classpath snapshot
+        return jarClasspathSnapshot;
+    }
+
+    private void maybeInitialize(Iterable<File> classpath) {
+        if (jarClasspathSnapshot != null) {
+            return;
+        }
+        Clock clock = new Clock();
+        Iterable<JarArchive> jarArchives = classpathJarFinder.findJarArchives(classpath);
+
+        jarClasspathSnapshot = classpathSnapshotFactory.createSnapshot(jarArchives);
+        int duplicatesCount = jarClasspathSnapshot.getData().getDuplicateClasses().size();
+        String duplicateClassesMessage = duplicatesCount == 0? "" : ". " + duplicatesCount + " duplicate classes found in classpath (see all with --debug)";
+        LOG.info("Created jar classpath snapshot for incremental compilation in {}{}.", clock.getTime(), duplicateClassesMessage);
+        LOG.debug("While calculating jar classpath snapshot {} duplicate classes were found: {}.", duplicatesCount, jarClasspathSnapshot.getData().getDuplicateClasses());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotProvider.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotProvider.java
new file mode 100644
index 0000000..61e7453
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotProvider.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import java.io.File;
+
+public interface JarClasspathSnapshotProvider {
+    JarClasspathSnapshot getJarClasspathSnapshot(Iterable<File> classpath);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotWriter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotWriter.java
new file mode 100644
index 0000000..6a1ec43
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotWriter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import java.io.File;
+
+public interface JarClasspathSnapshotWriter {
+    void storeJarSnapshots(Iterable<File> classpath);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshot.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshot.java
new file mode 100644
index 0000000..2c75eda
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshot.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.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.*;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class JarSnapshot {
+
+    private final JarSnapshotData data;
+
+    public JarSnapshot(JarSnapshotData data) {
+        this.data = data;
+    }
+
+    public DependentsSet getAllClasses() {
+        final Set<String> result = new HashSet<String>();
+        for (Map.Entry<String, byte[]> cls : getHashes().entrySet()) {
+            String className = cls.getKey();
+            if (getAnalysis().isDependencyToAll(className)) {
+                return new DependencyToAll();
+            }
+            result.add(className);
+        }
+        return new DefaultDependentsSet(result);
+    }
+
+    public AffectedClasses getAffectedClassesSince(JarSnapshot other) {
+        DependentsSet affectedClasses = affectedSince(other);
+        Set<String> addedClasses = addedSince(other);
+        return new AffectedClasses(affectedClasses, addedClasses);
+    }
+
+    private DependentsSet affectedSince(JarSnapshot other) {
+        final Set<String> affected = new HashSet<String>();
+        for (Map.Entry<String, byte[]> otherClass : other.getHashes().entrySet()) {
+            String otherClassName = otherClass.getKey();
+            byte[] otherClassBytes = otherClass.getValue();
+            byte[] thisClsBytes = getHashes().get(otherClassName);
+            if (thisClsBytes == null || !Arrays.equals(thisClsBytes, otherClassBytes)) {
+                //removed since or changed since
+                affected.add(otherClassName);
+                DependentsSet dependents = other.getAnalysis().getRelevantDependents(otherClassName);
+                if (dependents.isDependencyToAll()) {
+                    return dependents;
+                }
+                affected.addAll(dependents.getDependentClasses());
+            }
+        }
+        return new DefaultDependentsSet(affected);
+    }
+
+    private Set<String> addedSince(JarSnapshot other) {
+        Set<String> addedClasses = new HashSet<String>(getClasses());
+        addedClasses.removeAll(other.getClasses());
+        return addedClasses;
+    }
+
+    public byte[] getHash() {
+        return data.hash;
+    }
+
+    public Map<String, byte[]> getHashes() {
+        return data.hashes;
+    }
+
+    public ClassSetAnalysis getAnalysis() {
+        return new ClassSetAnalysis(data.data);
+    }
+
+    public Set<String> getClasses() {
+        return data.hashes.keySet();
+    }
+
+    public JarSnapshotData getData() {
+        return data;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotCache.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotCache.java
new file mode 100644
index 0000000..c6e8edf
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotCache.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.cache.Cache;
+import org.gradle.internal.concurrent.Stoppable;
+
+import java.io.File;
+import java.util.Map;
+
+public interface JarSnapshotCache extends Cache<byte[], JarSnapshot>, Stoppable {
+    Map<File, JarSnapshot> getJarSnapshots(Map<File, byte[]> jarHashes);
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotData.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotData.java
new file mode 100644
index 0000000..031b698
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotData.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData;
+
+import java.util.Map;
+
+public class JarSnapshotData {
+
+    final Map<String, byte[]> hashes;
+    final ClassSetAnalysisData data;
+    final byte[] hash;
+
+    /**
+     * @param hash of this jar
+     * @param hashes hashes of all classes from the jar
+     * @param data of classes analysis in this jar
+     */
+    public JarSnapshotData(byte[] hash, Map<String, byte[]> hashes, ClassSetAnalysisData data) {
+        assert hash != null;
+        assert hashes != null;
+        assert data != null;
+
+        this.hash = hash;
+        this.hashes = hashes;
+        this.data = data;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotDataSerializer.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotDataSerializer.java
new file mode 100644
index 0000000..4763c83
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotDataSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.MapSerializer;
+import org.gradle.internal.serialize.Serializer;
+
+import java.util.Map;
+
+import static org.gradle.internal.serialize.BaseSerializerFactory.BYTE_ARRAY_SERIALIZER;
+import static org.gradle.internal.serialize.BaseSerializerFactory.STRING_SERIALIZER;
+
+public class JarSnapshotDataSerializer implements Serializer<JarSnapshotData> {
+
+    private final MapSerializer<String, byte[]> mapSerializer;
+    private final Serializer<ClassSetAnalysisData> analysisSerializer;
+
+    public JarSnapshotDataSerializer() {
+        mapSerializer = new MapSerializer<String, byte[]>(STRING_SERIALIZER, BYTE_ARRAY_SERIALIZER);
+        analysisSerializer = new ClassSetAnalysisData.Serializer();
+    }
+
+    public JarSnapshotData read(Decoder decoder) throws Exception {
+        byte[] hash = decoder.readBinary();
+        Map<String, byte[]> hashes = mapSerializer.read(decoder);
+        ClassSetAnalysisData data = analysisSerializer.read(decoder);
+        return new JarSnapshotData(hash, hashes, data);
+    }
+
+    public void write(Encoder encoder, JarSnapshotData value) throws Exception {
+        encoder.writeBinary(value.hash);
+        mapSerializer.write(encoder, value.hashes);
+        analysisSerializer.write(encoder, value.data);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotter.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotter.java
new file mode 100644
index 0000000..9e74cfe
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotter.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+public interface JarSnapshotter {
+    JarSnapshot createSnapshot(JarArchive jarArchive);
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/LocalJarClasspathSnapshotStore.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/LocalJarClasspathSnapshotStore.java
new file mode 100644
index 0000000..c47fe8c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/LocalJarClasspathSnapshotStore.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.cache.SingleOperationPersistentStore;
+import org.gradle.cache.CacheRepository;
+
+//Keeps the jar classpath snapshot of given compile task
+public class LocalJarClasspathSnapshotStore {
+
+    private final SingleOperationPersistentStore<JarClasspathSnapshotData> store;
+
+    public LocalJarClasspathSnapshotStore(CacheRepository cacheRepository, Object scope) {
+        //Single operation store that we throw away after the operation makes the implementation simpler.
+        store = new SingleOperationPersistentStore<JarClasspathSnapshotData>(cacheRepository, scope, "local jar classpath snapshot", new JarClasspathSnapshotDataSerializer());
+    }
+
+    public void put(JarClasspathSnapshotData data) {
+        store.putAndClose(data);
+    }
+
+    public JarClasspathSnapshotData get() {
+        return store.getAndClose();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/PreviousCompilation.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/PreviousCompilation.java
new file mode 100644
index 0000000..cc0b045
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/jar/PreviousCompilation.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar;
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysis;
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependentsSet;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+public class PreviousCompilation {
+
+    private ClassSetAnalysis analysis;
+    private LocalJarClasspathSnapshotStore classpathSnapshotStore;
+    private final JarSnapshotCache jarSnapshotCache;
+    private Map<File, JarSnapshot> jarSnapshots;
+
+    public PreviousCompilation(ClassSetAnalysis analysis, LocalJarClasspathSnapshotStore classpathSnapshotStore, JarSnapshotCache jarSnapshotCache) {
+        this.analysis = analysis;
+        this.classpathSnapshotStore = classpathSnapshotStore;
+        this.jarSnapshotCache = jarSnapshotCache;
+    }
+
+    public DependentsSet getDependents(Set<String> allClasses) {
+        return analysis.getRelevantDependents(allClasses);
+    }
+
+    public JarSnapshot getJarSnapshot(File file) {
+        if (jarSnapshots == null) {
+            JarClasspathSnapshotData data = classpathSnapshotStore.get();
+            jarSnapshots = jarSnapshotCache.getJarSnapshots(data.getJarHashes());
+        }
+        return jarSnapshots.get(file);
+    }
+
+    public DependentsSet getDependents(String className) {
+        return analysis.getRelevantDependents(className);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/recomp/RecompilationSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/recomp/RecompilationSpec.java
new file mode 100644
index 0000000..3c528a0
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/recomp/RecompilationSpec.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.tasks.compile.incremental.recomp;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+public class RecompilationSpec {
+
+    private final Collection<String> classesToCompile = new LinkedHashSet<String>();
+    private String fullRebuildCause;
+
+    public Collection<String> getClassNames() {
+        return classesToCompile;
+    }
+
+    public boolean isFullRebuildNeeded() {
+        return fullRebuildCause != null;
+    }
+
+    public String getFullRebuildCause() {
+        return fullRebuildCause;
+    }
+
+    public void setFullRebuildCause(String description, File file) {
+        fullRebuildCause = description != null? description : "'" + file.getName() + "' was changed";
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java
new file mode 100644
index 0000000..6e5a01b
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.util.SingleMessageLogger;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Main options for Java compilation.
+ */
+public class CompileOptions extends AbstractOptions {
+    private static final long serialVersionUID = 0;
+
+    private static final ImmutableSet<String> EXCLUDE_FROM_ANT_PROPERTIES =
+            ImmutableSet.of("debugOptions", "forkOptions", "compilerArgs", "dependOptions", "useDepend", "incremental");
+
+    private boolean failOnError = true;
+
+    private boolean verbose;
+
+    private boolean listFiles;
+
+    private boolean deprecation;
+
+    private boolean warnings = true;
+
+    private String encoding;
+
+    private boolean debug = true;
+
+    private DebugOptions debugOptions = new DebugOptions();
+
+    private boolean fork;
+
+    private ForkOptions forkOptions = new ForkOptions();
+
+    private boolean useDepend;
+
+    private DependOptions dependOptions = new DependOptions();
+
+    private String bootClasspath;
+
+    private String extensionDirs;
+
+    private List<String> compilerArgs = Lists.newArrayList();
+
+    private boolean incremental;
+
+    private FileCollection sourcepath;
+
+    /**
+     * Tells whether to fail the build when compilation fails. Defaults to {@code true}.
+     */
+    @Input
+    public boolean isFailOnError() {
+        return failOnError;
+    }
+
+    /**
+     * Sets whether to fail the build when compilation fails. Defaults to {@code true}.
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Tells whether to produce verbose output. Defaults to {@code false}.
+     */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Sets whether to produce verbose output. Defaults to {@code false}.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Tells whether to log the files to be compiled. Defaults to {@code false}.
+     */
+    public boolean isListFiles() {
+        return listFiles;
+    }
+
+    /**
+     * Sets whether to log the files to be compiled. Defaults to {@code false}.
+     */
+    public void setListFiles(boolean listFiles) {
+        this.listFiles = listFiles;
+    }
+
+    /**
+     * Tells whether to log details of usage of deprecated members or classes. Defaults to {@code false}.
+     */
+    public boolean isDeprecation() {
+        return deprecation;
+    }
+
+    /**
+     * Sets whether to log details of usage of deprecated members or classes. Defaults to {@code false}.
+     */
+    public void setDeprecation(boolean deprecation) {
+        this.deprecation = deprecation;
+    }
+
+    /**
+     * Tells whether to log warning messages. The default is {@code true}.
+     */
+    public boolean isWarnings() {
+        return warnings;
+    }
+
+    /**
+     * Sets whether to log warning messages. The default is {@code true}.
+     */
+    public void setWarnings(boolean warnings) {
+        this.warnings = warnings;
+    }
+
+    /**
+     * Returns the character encoding to be used when reading source files. Defaults to {@code null}, in which
+     * case the platform default encoding will be used.
+     */
+    @Input
+    @Optional
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Sets the character encoding to be used when reading source files. Defaults to {@code null}, in which
+     * case the platform default encoding will be used.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Tells whether to include debugging information in the generated class files. Defaults
+     * to {@code true}. See {@link DebugOptions#getDebugLevel()} for which debugging information will be generated.
+     */
+    @Input
+    public boolean isDebug() {
+        return debug;
+    }
+
+    /**
+     * Sets whether to include debugging information in the generated class files. Defaults
+     * to {@code true}. See {@link DebugOptions#getDebugLevel()} for which debugging information will be generated.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Returns options for generating debugging information.
+     */
+    @Nested
+    public DebugOptions getDebugOptions() {
+        return debugOptions;
+    }
+
+    /**
+     * Sets options for generating debugging information.
+     */
+    public void setDebugOptions(DebugOptions debugOptions) {
+        this.debugOptions = debugOptions;
+    }
+
+    /**
+     * Tells whether to run the compiler in its own process. Note that this does
+     * not necessarily mean that a new process will be created for each compile task.
+     * Defaults to {@code false}.
+     */
+    public boolean isFork() {
+        return fork;
+    }
+
+    /**
+     * Sets whether to run the compiler in its own process. Note that this does
+     * not necessarily mean that a new process will be created for each compile task.
+     * Defaults to {@code false}.
+     */
+    public void setFork(boolean fork) {
+        this.fork = fork;
+    }
+
+    /**
+     * Returns options for running the compiler in a child process.
+     */
+    @Nested
+    public ForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    /**
+     * Sets options for running the compiler in a child process.
+     */
+    public void setForkOptions(ForkOptions forkOptions) {
+        this.forkOptions = forkOptions;
+    }
+
+    /**
+     * Tells whether to use the Ant {@code <depend>} task.
+     * Only takes effect if {@code useAnt} is {@code true}. Defaults to
+     * {@code false}.
+     */
+    public boolean isUseDepend() {
+        return useDepend;
+    }
+
+    /**
+     * Sets whether to use the Ant {@code <depend>} task.
+     * Only takes effect if {@code useAnt} is {@code true}. Defaults to
+     * {@code false}.
+     */
+    public void setUseDepend(boolean useDepend) {
+        this.useDepend = useDepend;
+    }
+
+    /**
+     * Returns options for using the Ant {@code <depend>} task.
+     */
+    public DependOptions getDependOptions() {
+        return dependOptions;
+    }
+
+    /**
+     * Sets options for using the Ant {@code <depend>} task.
+     */
+    public void setDependOptions(DependOptions dependOptions) {
+        this.dependOptions = dependOptions;
+    }
+
+    /**
+     * Returns the bootstrap classpath to be used for the compiler process.
+     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
+     */
+    @Input
+    @Optional
+    public String getBootClasspath() {
+        return bootClasspath;
+    }
+
+    /**
+     * Sets the bootstrap classpath to be used for the compiler process.
+     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
+     */
+    public void setBootClasspath(String bootClasspath) {
+        this.bootClasspath = bootClasspath;
+    }
+
+    /**
+     * Returns the extension dirs to be used for the compiler process.
+     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
+     */
+    @Input
+    @Optional
+    public String getExtensionDirs() {
+        return extensionDirs;
+    }
+
+    /**
+     * Sets the extension dirs to be used for the compiler process.
+     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
+     */
+    public void setExtensionDirs(String extensionDirs) {
+        this.extensionDirs = extensionDirs;
+    }
+
+    /**
+     * Returns any additional arguments to be passed to the compiler.
+     * Defaults to the empty list.
+     */
+    @Input
+    public List<String> getCompilerArgs() {
+        return compilerArgs;
+    }
+
+    /**
+     * Sets any additional arguments to be passed to the compiler.
+     * Defaults to the empty list.
+     */
+    public void setCompilerArgs(List<String> compilerArgs) {
+        this.compilerArgs = compilerArgs;
+    }
+
+    /**
+     * Convenience method to set {@link ForkOptions} with named parameter syntax.
+     * Calling this method will set {@code fork} to {@code true}.
+     */
+    public CompileOptions fork(Map<String, Object> forkArgs) {
+        fork = true;
+        forkOptions.define(forkArgs);
+        return this;
+    }
+
+    /**
+     * Convenience method to set {@link DebugOptions} with named parameter syntax.
+     * Calling this method will set {@code debug} to {@code true}.
+     */
+    public CompileOptions debug(Map<String, Object> debugArgs) {
+        debug = true;
+        debugOptions.define(debugArgs);
+        return this;
+    }
+
+    /**
+     * Convenience method to set {@link DependOptions} with named parameter syntax.
+     * Calling this method will set {@code useDepend} to {@code true}.
+     */
+    public CompileOptions depend(Map<String, Object> dependArgs) {
+        useDepend = true;
+        dependOptions.define(dependArgs);
+        return this;
+    }
+
+    @Incubating
+    /**
+     * Configure the java compilation to be incremental (e.g. compiles only those java classes that were changed or that are dependencies to the changed classes).
+     * The feature is incubating and does not yet satisfies all compilation scenarios.
+     */
+    public CompileOptions setIncremental(boolean incremental) {
+        SingleMessageLogger.incubatingFeatureUsed("Incremental java compilation");
+        this.incremental = incremental;
+        return this;
+    }
+
+    /**
+     * Internal method.
+     */
+    public Map<String, Object> optionMap() {
+        Map<String, Object> map = super.optionMap();
+        map.putAll(debugOptions.optionMap());
+        map.putAll(forkOptions.optionMap());
+        return map;
+    }
+
+    @Override
+    protected boolean excludeFromAntProperties(String fieldName) {
+        return EXCLUDE_FROM_ANT_PROPERTIES.contains(fieldName);
+    }
+
+    @Override
+    protected String getAntPropertyName(String fieldName) {
+        if (fieldName.equals("warnings")) {
+            return "nowarn";
+        }
+        if (fieldName.equals("extensionDirs")) {
+            return "extdirs";
+        }
+        return fieldName;
+    }
+
+    @Override
+    protected Object getAntPropertyValue(String fieldName, Object value) {
+        if (fieldName.equals("warnings")) {
+            return !warnings;
+        }
+        return value;
+    }
+
+    /**
+     * informs whether to use experimental incremental compilation feature. See {@link #setIncremental(boolean)}
+     */
+    @Incubating
+    public boolean isIncremental() {
+        return incremental;
+    }
+
+    /**
+     * The source path to use for the compilation.
+     * <p>
+     * The source path indicates the location of source files that <i>may</i> be compiled if necessary.
+     * It is effectively a complement to the class path, where the classes to be compiled against are in source form.
+     * It does <b>not</b> indicate the actual primary source being compiled.
+     * <p>
+     * The source path feature of the Java compiler is rarely needed for modern builds that use dependency management.
+     * <p>
+     * The default value for the source path is {@code null}, which indicates an <i>empty</i> source path.
+     * Note that this is different to the default value for the {@code -sourcepath} option for {@code javac}, which is to use the value specified by {@code -classpath}.
+     * If you wish to use any source path, it must be explicitly set.
+     *
+     * @return the source path
+     * @see #setSourcepath(FileCollection)
+     */
+    @Input
+    @Optional
+    @Incubating
+    public FileCollection getSourcepath() {
+        return sourcepath;
+    }
+
+    /**
+     * Sets the source path to use for the compilation.
+     *
+     * @param sourcepath the source path
+     */
+    @Incubating
+    public void setSourcepath(FileCollection sourcepath) {
+        this.sourcepath = sourcepath;
+    }
+}
+
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/DebugOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/DebugOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/DebugOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/DebugOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/DependOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/DependOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/DependOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/DependOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/ForkOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/ForkOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/ForkOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/ForkOptions.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java
new file mode 100644
index 0000000..e9b1a58
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java
@@ -0,0 +1,193 @@
+/*
+ * 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.compile;
+
+import org.gradle.api.AntBuilder;
+import org.gradle.api.Incubating;
+import org.gradle.api.JavaVersion;
+import org.gradle.api.internal.changedetection.changes.IncrementalTaskInputsInternal;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.tasks.compile.CleaningJavaCompiler;
+import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpecFactory;
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilerFactory;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysisCache;
+import org.gradle.api.internal.tasks.compile.incremental.cache.CompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.cache.GeneralCompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.deps.LocalClassSetAnalysisStore;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarSnapshotCache;
+import org.gradle.api.internal.tasks.compile.incremental.jar.LocalJarClasspathSnapshotStore;
+import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.cache.CacheRepository;
+import org.gradle.internal.Factory;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerUtil;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.util.SingleMessageLogger;
+
+import javax.inject.Inject;
+import java.io.File;
+
+/**
+ * Compiles Java source files.
+ *
+ * <pre autoTested=''>
+ *     apply plugin: 'java'
+ *     compileJava {
+ *         //enable compilation in a separate daemon process
+ *         options.fork = true
+ *
+ *         //enable incremental compilation
+ *         options.incremental = true
+ *     }
+ * </pre>
+ */
+ at ParallelizableTask
+public class JavaCompile extends AbstractCompile {
+    private File dependencyCacheDir;
+    private final CompileOptions compileOptions = new CompileOptions();
+
+    /**
+     * Returns the tool resolver that will be used to find the tool to compile the Java source.
+     *
+     * @return The tool resolver.
+     */
+    @Incubating @Inject
+    public ToolResolver getToolResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Sets the tool resolver that should be used to find the tool to compile the Java source.
+     *
+     * @param toolResolver The tool resolver.
+     */
+    @Incubating
+    public void setToolResolver(ToolResolver toolResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    protected void compile(IncrementalTaskInputs inputs) {
+        if (!compileOptions.isIncremental()) {
+            compile();
+            return;
+        }
+
+        SingleMessageLogger.incubatingFeatureUsed("Incremental java compilation");
+
+        DefaultJavaCompileSpec spec = createSpec();
+        final CacheRepository repository1 = getCacheRepository();
+        final JavaCompile javaCompile1 = this;
+        final GeneralCompileCaches generalCaches1 = getGeneralCompileCaches();
+        CompileCaches compileCaches = new CompileCaches() {
+            private final CacheRepository repository = repository1;
+            private final JavaCompile javaCompile = javaCompile1;
+            private final GeneralCompileCaches generalCaches = generalCaches1;
+
+            public ClassAnalysisCache getClassAnalysisCache() {
+                return generalCaches.getClassAnalysisCache();
+            }
+
+            public JarSnapshotCache getJarSnapshotCache() {
+                return generalCaches.getJarSnapshotCache();
+            }
+
+            public LocalJarClasspathSnapshotStore getLocalJarClasspathSnapshotStore() {
+                return new LocalJarClasspathSnapshotStore(repository, javaCompile);
+            }
+
+            public LocalClassSetAnalysisStore getLocalClassSetAnalysisStore() {
+                return new LocalClassSetAnalysisStore(repository, javaCompile);
+            }
+        };
+        IncrementalCompilerFactory factory = new IncrementalCompilerFactory(
+                (FileOperations) getProject(), getPath(), createCompiler(spec), source, compileCaches, (IncrementalTaskInputsInternal) inputs);
+        Compiler<JavaCompileSpec> compiler = factory.createCompiler();
+        performCompilation(spec, compiler);
+    }
+
+    @Inject protected GeneralCompileCaches getGeneralCompileCaches() {
+        throw new UnsupportedOperationException();
+    }
+    @Inject protected CacheRepository getCacheRepository() {
+        throw new UnsupportedOperationException();
+    }
+
+    protected void compile() {
+        DefaultJavaCompileSpec spec = createSpec();
+        performCompilation(spec, createCompiler(spec));
+    }
+
+    @Inject
+    protected Factory<AntBuilder> getAntBuilderFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    private CleaningJavaCompiler createCompiler(JavaCompileSpec spec) {
+        // TODO:DAZ Supply the target platform to the task, using the compatibility flags as overrides
+        // Or maybe split the legacy compile task from the new one
+        Compiler<JavaCompileSpec> javaCompiler = CompilerUtil.castCompiler(getToolResolver().resolveCompiler(spec.getClass(), getPlatform()).get());
+        return new CleaningJavaCompiler(javaCompiler, getAntBuilderFactory(), getOutputs());
+    }
+
+    protected JavaPlatform getPlatform() {
+        return new DefaultJavaPlatform(JavaVersion.current());
+    }
+
+    private void performCompilation(JavaCompileSpec spec, Compiler<JavaCompileSpec> compiler) {
+        WorkResult result = compiler.execute(spec);
+        setDidWork(result.getDidWork());
+    }
+
+    private DefaultJavaCompileSpec createSpec() {
+        DefaultJavaCompileSpec spec = new DefaultJavaCompileSpecFactory(compileOptions).create();
+        spec.setSource(getSource());
+        spec.setDestinationDir(getDestinationDir());
+        spec.setWorkingDir(getProject().getProjectDir());
+        spec.setTempDir(getTemporaryDir());
+        spec.setClasspath(getClasspath());
+        spec.setDependencyCacheDir(getDependencyCacheDir());
+        spec.setTargetCompatibility(getTargetCompatibility());
+        spec.setSourceCompatibility(getSourceCompatibility());
+        spec.setCompileOptions(compileOptions);
+        return spec;
+    }
+
+    @OutputDirectory
+    public File getDependencyCacheDir() {
+        return dependencyCacheDir;
+    }
+
+    public void setDependencyCacheDir(File dependencyCacheDir) {
+        this.dependencyCacheDir = dependencyCacheDir;
+    }
+
+    /**
+     * Returns the compilation options.
+     *
+     * @return The compilation options.
+     */
+    @Nested
+    public CompileOptions getOptions() {
+        return compileOptions;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/package-info.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/package-info.java
new file mode 100644
index 0000000..8152d0c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The compile tasks and options for Java.
+ */
+package org.gradle.api.tasks.compile;
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/Javadoc.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/Javadoc.java
new file mode 100644
index 0000000..31189a0
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/Javadoc.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.javadoc;
+
+import groovy.lang.Closure;
+import org.gradle.api.Incubating;
+import org.gradle.api.JavaVersion;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.javadoc.internal.JavadocSpec;
+import org.gradle.external.javadoc.MinimalJavadocOptions;
+import org.gradle.external.javadoc.StandardJavadocDocletOptions;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.Platform;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.util.GUtil;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * <p>Generates HTML API documentation for Java classes.</p>
+ * <p>
+ * If you create your own Javadoc tasks remember to specify the 'source' property!
+ * Without source the Javadoc task will not create any documentation. Example:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ *
+ * task myJavadocs(type: Javadoc) {
+ *   source = sourceSets.main.allJava
+ * }
+ * </pre>
+ *
+ * <p>
+ * An example how to create a task that runs a custom doclet implementation:
+ * <pre autoTested=''>
+ * apply plugin: 'java'
+ *
+ * configurations {
+ *   jaxDoclet
+ * }
+ *
+ * dependencies {
+ *   //jaxDoclet "some.interesting:Dependency:1.0"
+ * }
+ *
+ * task generateRestApiDocs(type: Javadoc) {
+ *   source = sourceSets.main.allJava
+ *   destinationDir = reporting.file("rest-api-docs")
+ *   options.docletpath = configurations.jaxDoclet.files.asType(List)
+ *   options.doclet = "com.lunatech.doclets.jax.jaxrs.JAXRSDoclet"
+ *   options.addStringOption("jaxrscontext", "http://localhost:8080/myapp")
+ * }
+ * </pre>
+ */
+ at ParallelizableTask
+public class Javadoc extends SourceTask {
+    private File destinationDir;
+
+    private boolean failOnError = true;
+
+    private String title;
+
+    private String maxMemory;
+
+    private MinimalJavadocOptions options = new StandardJavadocDocletOptions();
+
+    private FileCollection classpath = getProject().files();
+
+    private String executable;
+
+    @TaskAction
+    protected void generate() {
+        final File destinationDir = getDestinationDir();
+
+        if (options.getDestinationDirectory() == null) {
+            options.destinationDirectory(destinationDir);
+        }
+
+        options.classpath(new ArrayList<File>(getClasspath().getFiles()));
+
+        if (!GUtil.isTrue(options.getWindowTitle()) && GUtil.isTrue(getTitle())) {
+            options.windowTitle(getTitle());
+        }
+        if (options instanceof StandardJavadocDocletOptions) {
+            StandardJavadocDocletOptions docletOptions = (StandardJavadocDocletOptions) options;
+            if (!GUtil.isTrue(docletOptions.getDocTitle()) && GUtil.isTrue(getTitle())) {
+                docletOptions.setDocTitle(getTitle());
+            }
+        }
+
+        if (maxMemory != null) {
+            final List<String> jFlags = options.getJFlags();
+            final Iterator<String> jFlagsIt = jFlags.iterator();
+            boolean containsXmx = false;
+            while (!containsXmx && jFlagsIt.hasNext()) {
+                final String jFlag = jFlagsIt.next();
+                if (jFlag.startsWith("-Xmx")) {
+                    containsXmx = true;
+                }
+            }
+            if (!containsXmx) {
+                options.jFlags("-Xmx" + maxMemory);
+            }
+        }
+
+        List<String> sourceNames = new ArrayList<String>();
+        for (File sourceFile : getSource()) {
+            sourceNames.add(sourceFile.getAbsolutePath());
+        }
+        options.setSourceNames(sourceNames);
+
+        executeExternalJavadoc();
+    }
+
+    private void executeExternalJavadoc() {
+        JavadocSpec spec = new JavadocSpec();
+        spec.setExecutable(executable);
+        spec.setOptions(options);
+        spec.setIgnoreFailures(!failOnError);
+        spec.setWorkingDir(getProject().getProjectDir());
+        spec.setOptionsFile(getOptionsFile());
+
+        Compiler<JavadocSpec> generator = getToolResolver().resolveCompiler(JavadocSpec.class, getPlatform()).get();
+        generator.execute(spec);
+    }
+
+    /**
+     * <p>Returns the the tool resolver which will be used to find the tool to generate the documention.</p>
+     *
+     * @return the tool resolver
+     */
+    @Incubating @Inject
+    public ToolResolver getToolResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Sets the tool resolver which will be used to find the tool to generate the Javadoc.
+     */
+    @Incubating
+    public void setToolResolver(ToolResolver toolResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    private Platform getPlatform() {
+        return new DefaultJavaPlatform(JavaVersion.current());
+    }
+
+    /**
+     * <p>Returns the directory to generate the documentation into.</p>
+     *
+     * @return The directory.
+     */
+    @OutputDirectory
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+
+    /**
+     * <p>Sets the directory to generate the documentation into.</p>
+     */
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    /**
+     * Returns the amount of memory allocated to this task.
+     */
+    public String getMaxMemory() {
+        return maxMemory;
+    }
+
+    /**
+     * Sets the amount of memory allocated to this task.
+     *
+     * @param maxMemory The amount of memory
+     */
+    public void setMaxMemory(String maxMemory) {
+        this.maxMemory = maxMemory;
+    }
+
+    /**
+     * <p>Returns the title for the generated documentation.</p>
+     *
+     * @return The title, possibly null.
+     */
+    @Input
+    @Optional
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * <p>Sets the title for the generated documentation.</p>
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Returns whether Javadoc generation is accompanied by verbose output.
+     *
+     * @see #setVerbose(boolean)
+     */
+    public boolean isVerbose() {
+        return options.isVerbose();
+    }
+
+    /**
+     * Sets whether Javadoc generation is accompanied by verbose output or not. The verbose output is done via println
+     * (by the underlying Ant task). Thus it is not handled by our logging.
+     *
+     * @param verbose Whether the output should be verbose.
+     */
+    public void setVerbose(boolean verbose) {
+        if (verbose) {
+            options.verbose();
+        }
+    }
+
+    /**
+     * Returns the classpath to use to resolve type references in the source code.
+     *
+     * @return The classpath.
+     */
+    @InputFiles
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Sets the classpath to use to resolve type references in this source code.
+     *
+     * @param classpath The classpath. Must not be null.
+     */
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * Returns the Javadoc generation options.
+     *
+     * @return The options. Never returns null.
+     */
+    @Nested
+    public MinimalJavadocOptions getOptions() {
+        return options;
+    }
+
+    /**
+     * Sets the Javadoc generation options.
+     *
+     * @param options The options. Must not be null.
+     */
+    public void setOptions(MinimalJavadocOptions options) {
+        this.options = options;
+    }
+
+    /**
+     * Convenience method for configuring Javadoc generation options.
+     *
+     * @param block The configuration block for Javadoc generation options.
+     */
+    public void options(Closure<?> block) {
+        getProject().configure(getOptions(), block);
+    }
+
+    /**
+     * Specifies whether this task should fail when errors are encountered during Javadoc generation. When {@code true},
+     * this task will fail on Javadoc error. When {@code false}, this task will ignore Javadoc errors.
+     */
+    @Input
+    public boolean isFailOnError() {
+        return failOnError;
+    }
+
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    public File getOptionsFile() {
+        return new File(getTemporaryDir(), "javadoc.options");
+    }
+
+    /**
+     * Returns the Javadoc executable to use to generate the Javadoc. When {@code null}, the Javadoc executable for
+     * the current JVM is used.
+     *
+     * @return The executable. May be null.
+     */
+    @Input @Optional
+    public String getExecutable() {
+        return executable;
+    }
+
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocGenerator.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocGenerator.java
new file mode 100644
index 0000000..c930187
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocGenerator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.javadoc.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.process.internal.ExecException;
+import org.gradle.util.GFileUtils;
+
+public class JavadocGenerator implements Compiler<JavadocSpec> {
+
+    private final static Logger LOG = Logging.getLogger(JavadocGenerator.class);
+
+    private final ExecActionFactory execActionFactory;
+
+    public JavadocGenerator(ExecActionFactory execActionFactory) {
+        this.execActionFactory = execActionFactory;
+    }
+
+    public WorkResult execute(JavadocSpec spec) {
+        JavadocExecHandleBuilder javadocExecHandleBuilder = new JavadocExecHandleBuilder(execActionFactory);
+        javadocExecHandleBuilder.setExecutable(spec.getExecutable());
+        javadocExecHandleBuilder.execDirectory(spec.getWorkingDir()).options(spec.getOptions()).optionsFile(spec.getOptionsFile());
+
+        ExecAction execAction = javadocExecHandleBuilder.getExecHandle();
+        if (spec.isIgnoreFailures()) {
+            execAction.setIgnoreExitValue(true);
+        }
+
+        try {
+            execAction.execute();
+        } catch (ExecException e) {
+            LOG.info("Problems generating Javadoc."
+                    + "\n  Command line issued: " + execAction.getCommandLine()
+                    + "\n  Generated Javadoc options file has following contents:\n------\n{}------", GFileUtils.readFileQuietly(spec.getOptionsFile()));
+            throw new GradleException(String.format("Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting): '%s'", spec.getOptionsFile()), e);
+        }
+
+        return new SimpleWorkResult(true);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocSpec.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocSpec.java
new file mode 100644
index 0000000..c05781e
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/internal/JavadocSpec.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.javadoc.internal;
+
+import org.gradle.external.javadoc.MinimalJavadocOptions;
+import org.gradle.language.base.internal.compile.CompileSpec;
+
+import java.io.File;
+
+public class JavadocSpec implements CompileSpec {
+    private MinimalJavadocOptions options;
+    private boolean ignoreFailures;
+    private File workingDir;
+    private File optionsFile;
+    private String executable;
+
+    public void setOptions(MinimalJavadocOptions options) {
+        this.options = options;
+    }
+
+    public MinimalJavadocOptions getOptions() {
+        return options;
+    }
+
+    public void setIgnoreFailures(boolean ignoreFailures) {
+        this.ignoreFailures = ignoreFailures;
+    }
+
+    public boolean isIgnoreFailures() {
+        return ignoreFailures;
+    }
+
+    public void setWorkingDir(File workingDir) {
+        this.workingDir = workingDir;
+    }
+
+    public File getWorkingDir() {
+        return workingDir;
+    }
+
+    public void setOptionsFile(File optionsFile) {
+        this.optionsFile = optionsFile;
+    }
+
+    public File getOptionsFile() {
+        return optionsFile;
+    }
+
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+
+    public String getExecutable() {
+        return executable;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/package-info.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/package-info.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/package-info.java
rename to subprojects/language-java/src/main/java/org/gradle/api/tasks/javadoc/package-info.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/CoreJavadocOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/CoreJavadocOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocMemberLevel.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocMemberLevel.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocMemberLevel.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocMemberLevel.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOfflineLink.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOfflineLink.java
new file mode 100755
index 0000000..bc21f87
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOfflineLink.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc;
+
+
+/**
+ * This class is used to hold the information that can be provided to the javadoc executable via the -linkoffline
+ * option.
+ */
+public class JavadocOfflineLink {
+    private final String extDocUrl;
+    private final String packagelistLoc;
+
+    public JavadocOfflineLink(String extDocUrl, String packagelistLoc) {
+        this.extDocUrl = extDocUrl;
+        this.packagelistLoc = packagelistLoc;
+    }
+
+    public String getExtDocUrl() {
+        return extDocUrl;
+    }
+
+    public String getPackagelistLoc() {
+        return packagelistLoc;
+    }
+
+    public String toString() {
+        return extDocUrl + "' '" + packagelistLoc;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOutputLevel.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOutputLevel.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOutputLevel.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/JavadocOutputLevel.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/MinimalJavadocOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/MinimalJavadocOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
new file mode 100644
index 0000000..edefe22
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A base class for {@link org.gradle.external.javadoc.JavadocOptionFileOption} implementations whose value is a {@code List}.
+ *
+ * @param <T> The type which this option represents.
+ */
+public abstract class AbstractListJavadocOptionFileOption<T extends List<?>> extends AbstractJavadocOptionFileOption<T> {
+    protected String joinBy;
+
+    protected AbstractListJavadocOptionFileOption(String option, String joinBy) {
+        super(option);
+        this.joinBy = joinBy;
+    }
+
+    protected AbstractListJavadocOptionFileOption(String option, T value, String joinBy) {
+        super(option, value);
+        this.joinBy = joinBy;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public void setValue(T value) {
+        if (value == null) {
+            this.value.clear();
+        } else {
+            this.value = value;
+        }
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if (value != null && !value.isEmpty()) {
+            writeCollectionValue(writerContext);
+        }
+    }
+
+    protected abstract void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException;
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFile.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFile.java
new file mode 100644
index 0000000..87bbfcb
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFile.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+import org.gradle.external.javadoc.OptionLessJavadocOptionFileOption;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+public class JavadocOptionFile {
+    private final Map<String, JavadocOptionFileOption<?>> options;
+
+    private final OptionLessJavadocOptionFileOption<List<String>> sourceNames;
+
+    public JavadocOptionFile() {
+        options = new HashMap<String, JavadocOptionFileOption<?>>();
+        sourceNames = new OptionLessStringsJavadocOptionFileOption();
+    }
+
+    public OptionLessJavadocOptionFileOption<List<String>> getSourceNames() {
+        return sourceNames;
+    }
+
+    Map<String, JavadocOptionFileOption<?>> getOptions() {
+        return Collections.unmodifiableMap(options);
+    }
+
+    public <T> JavadocOptionFileOption<T> addOption(JavadocOptionFileOption<T> option) {
+        if (option == null) {
+            throw new IllegalArgumentException("option == null!");
+        }
+
+        options.put(option.getOption(), option);
+
+        return option;
+    }
+
+    public JavadocOptionFileOption<String> addStringOption(String option) {
+        return addStringOption(option, null);
+    }
+
+    public JavadocOptionFileOption<String> addStringOption(String option, String value) {
+        return addOption(new StringJavadocOptionFileOption(option, value));
+    }
+
+    public <T> JavadocOptionFileOption<T> addEnumOption(String option) {
+        return addEnumOption(option, null);
+    }
+
+    public <T> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
+        return addOption(new EnumJavadocOptionFileOption<T>(option, value));
+    }
+
+    public JavadocOptionFileOption<List<File>> addPathOption(String option) {
+        return addPathOption(option, System.getProperty("path.separator"));
+    }
+
+    public JavadocOptionFileOption<List<File>> addPathOption(String option, String joinBy) {
+        return addOption(new PathJavadocOptionFileOption(option, joinBy));
+    }
+
+    public JavadocOptionFileOption<List<String>> addStringsOption(String option) {
+        return addStringsOption(option, System.getProperty("path.separator"));
+    }
+
+    public JavadocOptionFileOption<List<String>> addStringsOption(String option, String joinBy) {
+        return addOption(new StringsJavadocOptionFileOption(option, new ArrayList<String>(), joinBy));
+    }
+
+    public JavadocOptionFileOption<List<String>> addMultilineStringsOption(String option) {
+        return addOption(new MultilineStringsJavadocOptionFileOption(option, new ArrayList<String>()));
+    }
+
+    public JavadocOptionFileOption<Boolean> addBooleanOption(String option) {
+        return addBooleanOption(option, false);
+    }
+
+    public JavadocOptionFileOption<Boolean> addBooleanOption(String option, boolean value) {
+        return addOption(new BooleanJavadocOptionFileOption(option, value));
+    }
+
+    public JavadocOptionFileOption<File> addFileOption(String option) {
+        return addFileOption(option, null);
+    }
+
+    public JavadocOptionFileOption<File> addFileOption(String option, File value) {
+        return addOption(new FileJavadocOptionFileOption(option, value));
+    }
+
+    public void write(File optionFile) throws IOException {
+        if (optionFile == null) {
+            throw new IllegalArgumentException("optionFile == null!");
+        }
+
+        final JavadocOptionFileWriter optionFileWriter = new JavadocOptionFileWriter(this);
+
+        optionFileWriter.write(optionFile);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
new file mode 100644
index 0000000..1c8387b
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.internal.ErroringAction;
+import org.gradle.internal.IoActions;
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class JavadocOptionFileWriter {
+    private final JavadocOptionFile optionFile;
+
+    public JavadocOptionFileWriter(JavadocOptionFile optionFile) {
+        if (optionFile == null) {
+            throw new IllegalArgumentException("optionFile == null!");
+        }
+        this.optionFile = optionFile;
+    }
+
+    void write(File outputFile) throws IOException {
+        IoActions.writeTextFile(outputFile, new ErroringAction<BufferedWriter>() {
+            @Override
+            protected void doExecute(BufferedWriter writer) throws Exception {
+                final Map<String, JavadocOptionFileOption<?>> options = new TreeMap<String, JavadocOptionFileOption<?>>(optionFile.getOptions());
+                JavadocOptionFileWriterContext writerContext = new JavadocOptionFileWriterContext(writer);
+
+                JavadocOptionFileOption<?> localeOption = options.remove("locale");
+                if (localeOption != null) {
+                    localeOption.write(writerContext);
+                }
+
+                for (final String option : options.keySet()) {
+                    options.get(option).write(writerContext);
+                }
+
+                optionFile.getSourceNames().write(writerContext);
+            }
+        });
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
new file mode 100644
index 0000000..01662b7
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.internal.SystemProperties;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class JavadocOptionFileWriterContext {
+    private final Writer writer;
+
+    public JavadocOptionFileWriterContext(Writer writer) {
+        this.writer = writer;
+    }
+
+    public JavadocOptionFileWriterContext write(String string) throws IOException {
+        writer.write(string);
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext newLine() throws IOException {
+        writer.write(SystemProperties.getInstance().getLineSeparator());
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeOptionHeader(String option) throws IOException {
+        write("-");
+        write(option);
+        write(" ");
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeOption(String option) throws IOException {
+        writeOptionHeader(option);
+        newLine();
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValueOption(String option, String value) throws IOException {
+        writeOptionHeader(option);
+        writeValue(value);
+        newLine();
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValue(String value) throws IOException {
+        write("\'");
+        //First, we replace slashes because they have special meaning in the javadoc options file
+        //Then, we replace every linebreak with slash+linebreak. Slash is needed according to javadoc options file format
+        write(value.replaceAll("\\\\", "\\\\\\\\")
+                //below does not help on windows environments. I was unable to get plain javadoc utility to work successfully with multiline options _in_ the options file.
+                //at least, it will work out of the box on linux or mac environments.
+                //on windows, the options file will have correct contents according to the javadoc spec but it may not work (the failure will be exactly the same as if we didn't replace line breaks)
+                .replaceAll(SystemProperties.getInstance().getLineSeparator(), "\\\\" + SystemProperties.getInstance().getLineSeparator()));
+        write("\'");
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValuesOption(String option, Collection<String> values, String joinValuesBy) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        Iterator<String> valuesIt = values.iterator();
+        while (valuesIt.hasNext()) {
+            builder.append(valuesIt.next());
+            if (valuesIt.hasNext()) {
+                builder.append(joinValuesBy);
+            }
+        }
+        writeValueOption(option, builder.toString());
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeMultilineValuesOption(String option, Collection<String> values) throws IOException {
+        for (String value : values) {
+            writeValueOption(option, value);
+        }
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writePathOption(String option, Collection<File> files, String joinValuesBy) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        Iterator<File> filesIt = files.iterator();
+        while (filesIt.hasNext()) {
+            builder.append(filesIt.next().getAbsolutePath());
+            if (filesIt.hasNext()) {
+                builder.append(joinValuesBy);
+            }
+        }
+        writeValueOption(option, builder.toString());
+        return this;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java b/subprojects/language-java/src/main/java/org/gradle/external/javadoc/package-info.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java
rename to subprojects/language-java/src/main/java/org/gradle/external/javadoc/package-info.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/JavaSourceSet.java b/subprojects/language-java/src/main/java/org/gradle/language/java/JavaSourceSet.java
new file mode 100644
index 0000000..aa0372a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/JavaSourceSet.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.language.java;
+
+import org.gradle.api.Incubating;
+import org.gradle.jvm.Classpath;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of sources passed to the Java compiler.
+ */
+ at Incubating
+public interface JavaSourceSet extends LanguageSourceSet {
+    Classpath getCompileClasspath();
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/JavadocArtifact.java b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/JavadocArtifact.java
new file mode 100644
index 0000000..fe6e9e6
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/JavadocArtifact.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.java.artifact;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+/**
+ * An artifact containing Javadoc documentation.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface JavadocArtifact extends Artifact {
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java
new file mode 100644
index 0000000..3db466c
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 representing artifacts relevant to the Java language.
+ */
+package org.gradle.language.java.artifact;
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/internal/DefaultJavaLanguageSourceSet.java b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/DefaultJavaLanguageSourceSet.java
new file mode 100644
index 0000000..62374c4
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/DefaultJavaLanguageSourceSet.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.java.internal;
+
+import org.gradle.jvm.Classpath;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.java.JavaSourceSet;
+import org.gradle.language.jvm.internal.EmptyClasspath;
+
+public class DefaultJavaLanguageSourceSet extends BaseLanguageSourceSet implements JavaSourceSet {
+    private final Classpath compileClasspath = new EmptyClasspath();
+
+    @Override
+    protected String getTypeName() {
+        return "Java source";
+    }
+
+    public Classpath getCompileClasspath() {
+        return compileClasspath;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaLanguagePluginServiceRegistry.java b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaLanguagePluginServiceRegistry.java
new file mode 100644
index 0000000..810c51a
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaLanguagePluginServiceRegistry.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.internal;
+
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.jvm.JvmLibrary;
+import org.gradle.language.java.artifact.JavadocArtifact;
+
+public class JavaLanguagePluginServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new ComponentRegistrationAction());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class ComponentRegistrationAction {
+        public void configure(ServiceRegistration registration, ComponentTypeRegistry componentTypeRegistry) {
+            // TODO There should be a more explicit way to execute an action against existing services
+            // TODO:DAZ Dependency Management should be able to extract this from the plugin, without explicit registration
+            componentTypeRegistry.maybeRegisterComponentType(JvmLibrary.class)
+                    .registerArtifactType(JavadocArtifact.class, ArtifactType.JAVADOC);
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaToolChainServiceRegistry.java b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaToolChainServiceRegistry.java
new file mode 100644
index 0000000..be974d5
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/internal/JavaToolChainServiceRegistry.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.internal;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.tasks.DefaultJavaToolChain;
+import org.gradle.api.internal.tasks.compile.DefaultJavaCompilerFactory;
+import org.gradle.api.internal.tasks.compile.JavaCompilerFactory;
+import org.gradle.api.internal.tasks.compile.JavaHomeBasedJavaCompilerFactory;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerClientsManager;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonStarter;
+import org.gradle.internal.Factory;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.jvm.internal.toolchain.JavaToolChainInternal;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
+
+import javax.tools.JavaCompiler;
+
+public class JavaToolChainServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new BuildScopeCompileServices());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.addProvider(new ProjectScopeCompileServices());
+    }
+
+    private static class BuildScopeCompileServices {
+        CompilerDaemonManager createCompilerDaemonManager(Factory<WorkerProcessBuilder> workerFactory, StartParameter startParameter) {
+            return new CompilerDaemonManager(new CompilerClientsManager(new CompilerDaemonStarter(workerFactory, startParameter)));
+        }
+
+        Factory<JavaCompiler> createJavaHomeBasedJavaCompilerFactory() {
+            return new JavaHomeBasedJavaCompilerFactory();
+        }
+    }
+
+    private static class ProjectScopeCompileServices {
+        JavaCompilerFactory createJavaCompilerFactory(GradleInternal gradle, CompilerDaemonManager compilerDaemonManager, Factory<JavaCompiler> javaHomeBasedJavaCompilerFactory) {
+            return new DefaultJavaCompilerFactory(gradle.getRootProject().getProjectDir(), compilerDaemonManager, javaHomeBasedJavaCompilerFactory);
+        }
+
+        JavaToolChainInternal createJavaToolChain(JavaCompilerFactory compilerFactory, ExecActionFactory execActionFactory) {
+            return new DefaultJavaToolChain(compilerFactory, execActionFactory);
+        }
+    }
+}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/package-info.java b/subprojects/language-java/src/main/java/org/gradle/language/java/package-info.java
similarity index 100%
rename from subprojects/language-jvm/src/main/groovy/org/gradle/language/java/package-info.java
rename to subprojects/language-java/src/main/java/org/gradle/language/java/package-info.java
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/JavaLanguagePlugin.java b/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/JavaLanguagePlugin.java
new file mode 100644
index 0000000..d1e5fba
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/JavaLanguagePlugin.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.plugins;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.JvmBinarySpec;
+import org.gradle.jvm.JvmByteCode;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.java.JavaSourceSet;
+import org.gradle.language.java.internal.DefaultJavaLanguageSourceSet;
+import org.gradle.language.java.tasks.PlatformJavaCompile;
+import org.gradle.language.jvm.plugins.JvmResourcesPlugin;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Plugin for compiling Java code. Applies the {@link org.gradle.language.base.plugins.ComponentModelBasePlugin} and {@link org.gradle.language.jvm.plugins.JvmResourcesPlugin}. Registers "java"
+ * language support with the {@link JavaSourceSet}.
+ */
+public class JavaLanguagePlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+        project.getPluginManager().apply(JvmResourcesPlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<JavaSourceSet> builder) {
+            builder.setLanguageName("java");
+            builder.defaultImplementation(DefaultJavaLanguageSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new Java());
+        }
+    }
+
+    private static class Java implements LanguageTransform<JavaSourceSet, JvmByteCode> {
+        public Class<JavaSourceSet> getSourceSetType() {
+            return JavaSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            return Collections.emptyMap();
+        }
+
+        public Class<JvmByteCode> getOutputType() {
+            return JvmByteCode.class;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new SourceTransformTaskConfig() {
+                public String getTaskPrefix() {
+                    return "compile";
+                }
+
+                public Class<? extends DefaultTask> getTaskType() {
+                    return PlatformJavaCompile.class;
+                }
+
+                public void configureTask(Task task, BinarySpec binarySpec, LanguageSourceSet sourceSet) {
+                    PlatformJavaCompile compile = (PlatformJavaCompile) task;
+                    JavaSourceSet javaSourceSet = (JavaSourceSet) sourceSet;
+                    JvmBinarySpec binary = (JvmBinarySpec) binarySpec;
+
+                    compile.setDescription(String.format("Compiles %s.", javaSourceSet));
+                    compile.setDestinationDir(binary.getClassesDir());
+                    compile.setPlatform(binary.getTargetPlatform());
+
+                    compile.setSource(javaSourceSet.getSource());
+                    compile.setClasspath(javaSourceSet.getCompileClasspath().getFiles());
+                    compile.setTargetCompatibility(binary.getTargetPlatform().getTargetCompatibility().toString());
+                    compile.setSourceCompatibility(binary.getTargetPlatform().getTargetCompatibility().toString());
+
+                    compile.setDependencyCacheDir(new File(compile.getProject().getBuildDir(), "jvm-dep-cache"));
+                    compile.dependsOn(javaSourceSet);
+                    binary.getTasks().getJar().dependsOn(compile);
+                }
+            };
+        }
+
+        public boolean applyToBinary(BinarySpec binary) {
+            return binary instanceof JvmBinarySpec;
+        }
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/package-info.java b/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/package-info.java
new file mode 100644
index 0000000..ae805a3
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/plugins/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base plugins that add support for Java language.
+ */
+ at Incubating
+package org.gradle.language.java.plugins;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/PlatformJavaCompile.java b/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/PlatformJavaCompile.java
new file mode 100644
index 0000000..b939c62
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/PlatformJavaCompile.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.jvm.platform.JavaPlatform;
+
+/**
+ * A platform-aware Java compile task.
+ */
+ at Incubating
+public class PlatformJavaCompile extends JavaCompile {
+    private JavaPlatform platform;
+
+    public JavaPlatform getPlatform() {
+        return platform;
+    }
+
+    public void setPlatform(JavaPlatform platform) {
+        this.platform = platform;
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/package-info.java b/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/package-info.java
new file mode 100644
index 0000000..c6b49d0
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/tasks/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that add support for Java language.
+ */
+ at Incubating
+package org.gradle.language.java.tasks;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-java/src/main/resources/META-INF/gradle-plugins/org.gradle.java-lang.properties b/subprojects/language-java/src/main/resources/META-INF/gradle-plugins/org.gradle.java-lang.properties
new file mode 100644
index 0000000..d96ab43
--- /dev/null
+++ b/subprojects/language-java/src/main/resources/META-INF/gradle-plugins/org.gradle.java-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.java.plugins.JavaLanguagePlugin
diff --git a/subprojects/language-java/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/language-java/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..bb799a1
--- /dev/null
+++ b/subprojects/language-java/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1,2 @@
+org.gradle.language.java.internal.JavaToolChainServiceRegistry
+org.gradle.language.java.internal.JavaLanguagePluginServiceRegistry
\ No newline at end of file
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/DefaultJavaToolChainTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/DefaultJavaToolChainTest.groovy
new file mode 100644
index 0000000..5619741
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/DefaultJavaToolChainTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.JavaVersion
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec
+import org.gradle.api.internal.tasks.compile.JavaCompilerFactory
+import org.gradle.api.tasks.javadoc.internal.JavadocGenerator
+import org.gradle.api.tasks.javadoc.internal.JavadocSpec
+import org.gradle.jvm.platform.JavaPlatform
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class DefaultJavaToolChainTest extends Specification {
+    def javaCompilerFactory = Stub(JavaCompilerFactory)
+    def execActionFactory = Stub(ExecActionFactory)
+    def toolChain = new DefaultJavaToolChain(javaCompilerFactory, execActionFactory)
+    def JavaVersion currentJvm = JavaVersion.current()
+    def currentPlatform = platform(currentJvm)
+
+    def "has reasonable string representation"() {
+        expect:
+        toolChain.name == "JDK${currentJvm}"
+        toolChain.displayName == "JDK ${currentJvm.majorVersion} (${currentJvm})"
+        toolChain.toString() == toolChain.displayName
+    }
+
+    def "creates compiler for JavaCompileSpec"() {
+        def compiler = Stub(Compiler)
+
+        given:
+        javaCompilerFactory.create(JavaCompileSpec.class) >> compiler
+
+        expect:
+        toolChain.select(currentPlatform).newCompiler(JavaCompileSpec.class) == compiler
+    }
+
+    def "creates compiler for JavadocSpec"() {
+        expect:
+        toolChain.select(currentPlatform).newCompiler(JavadocSpec.class) instanceof JavadocGenerator
+    }
+
+    def "creates available tool provider for earlier platform"() {
+        def earlierPlatform = platform(JavaVersion.VERSION_1_5)
+
+        when:
+        def toolProvider = toolChain.select(earlierPlatform)
+
+        then:
+        toolProvider.available
+
+        when:
+        TreeVisitor<String> visitor = Mock()
+        toolProvider.explain(visitor)
+
+        then:
+        0 * _
+    }
+
+    def "creates unavailable tool provider for incompatible platform"() {
+        def futurePlatform = platform(JavaVersion.VERSION_1_9)
+        TreeVisitor<String> visitor = Mock()
+
+        when:
+        def toolProvider = toolChain.select(futurePlatform)
+
+        then:
+        !toolProvider.available
+
+        when:
+        toolProvider.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not target platform: '${futurePlatform}' using tool chain: '${toolChain}'.")
+        0 * _
+    }
+
+    private static JavaPlatform platform(JavaVersion javaVersion) {
+        return new DefaultJavaPlatform(javaVersion)
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy
new file mode 100644
index 0000000..0fd76b8
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.language.base.internal.tasks.StaleClassCleaner
+import spock.lang.Specification
+import org.gradle.api.tasks.WorkResult
+import org.gradle.api.file.FileCollection
+
+class CleaningJavaCompilerTest extends Specification {
+    private final org.gradle.language.base.internal.compile.Compiler<JavaCompileSpec> target = Mock()
+    private final JavaCompileSpec spec = Mock()
+    private final StaleClassCleaner cleaner = Mock()
+    private final CleaningJavaCompilerSupport<JavaCompileSpec> compiler = new CleaningJavaCompilerSupport<JavaCompileSpec>() {
+        @Override
+        protected org.gradle.language.base.internal.compile.Compiler<JavaCompileSpec> getCompiler() {
+            return target
+        }
+
+        protected StaleClassCleaner createCleaner(JavaCompileSpec spec) {
+            return cleaner
+        }
+    }
+    
+    def cleansStaleClassesAndThenInvokesCompiler() {
+        WorkResult result = Mock()
+        File destDir = new File('dest')
+        FileCollection source = Mock()
+        _ * spec.destinationDir >> destDir
+        _ * spec.source >> source
+
+        when:
+        def r = compiler.execute(spec)
+
+        then:
+        r == result
+
+        and:
+        1 * cleaner.setDestinationDir(destDir)
+        1 * cleaner.setSource(source)
+
+        and:
+        1 * cleaner.execute()
+
+        and:
+        1 * target.execute(spec) >> result
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.groovy
new file mode 100644
index 0000000..8f816dd
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * 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.tasks.compile
+
+import com.google.common.collect.Lists
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.tasks.compile.CompileOptions
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder.USE_UNSHARED_COMPILER_TABLE_OPTION
+
+class CommandLineJavaCompilerArgumentsGeneratorTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tempDir
+
+    CommandLineJavaCompilerArgumentsGenerator argsGenerator = new CommandLineJavaCompilerArgumentsGenerator()
+
+    def "inlines arguments if they are short enough"() {
+        def spec = createCompileSpec(25)
+
+        when:
+        def args = argsGenerator.generate(spec)
+        then:
+        Lists.newArrayList(args) == ["-J-Xmx256m", "-g", "-sourcepath", defaultEmptySourcePathRefFolder(), "-classpath", spec.classpath.asPath, *spec.source*.path, USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "creates arguments file if arguments get too long"() {
+        def spec = createCompileSpec(100)
+        def argsFile = tempDir.createFile("java-compiler-args.txt")
+
+        when:
+        def args = argsGenerator.generate(spec)
+
+        then: "argument list only contains launcher args and reference to args file"
+        Lists.newArrayList(args) == ["-J-Xmx256m", "@$argsFile"]
+        println argsFile.text
+
+        and: "args file contains remaining arguments (one per line, quoted)"
+        argsFile.readLines() == ["-g", "-sourcepath", quote(defaultEmptySourcePathRefFolder()), "-classpath", quote("$spec.classpath.asPath"), *(spec.source*.path.collect { quote(it) }), USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    String defaultEmptySourcePathRefFolder() {
+       return tempDir.testDirectory.file(JavaCompilerArgumentsBuilder.EMPTY_SOURCE_PATH_REF_DIR).absolutePath
+    }
+
+    def createCompileSpec(numFiles) {
+        def sources = createFiles(numFiles)
+        def classpath = createFiles(numFiles)
+        def spec = new DefaultJavaCompileSpec()
+        spec.compileOptions = new CompileOptions()
+        spec.compileOptions.forkOptions.memoryMaximumSize = "256m"
+        spec.source = new SimpleFileCollection(sources)
+        spec.classpath = new SimpleFileCollection(classpath)
+        spec.tempDir = tempDir.testDirectory
+        spec
+    }
+
+    def createFiles(numFiles) {
+        (1..numFiles).collect { new File("/foo bar/File$it") }
+    }
+
+    def quote(arg) {
+        "\"${arg.replace("\\", "\\\\")}\""
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactoryTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactoryTest.groovy
new file mode 100644
index 0000000..7b55519
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpecFactoryTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.api.tasks.compile.CompileOptions
+import spock.lang.Specification
+
+class DefaultJavaCompileSpecFactoryTest extends Specification {
+    def "produces correct spec type" () {
+        CompileOptions options = new CompileOptions()
+        options.fork = fork
+        options.forkOptions.executable = executable
+        DefaultJavaCompileSpecFactory factory = new DefaultJavaCompileSpecFactory(options)
+
+        when:
+        def spec = factory.create()
+
+        then:
+        spec instanceof DefaultJavaCompileSpec
+        ForkingJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsForking
+        CommandLineJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsCommandLine
+
+        where:
+        fork  | executable | implementsForking | implementsCommandLine
+        false | null       | false             | false
+        true  | null       | true              | false
+        true  | "X"        | false             | true
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy
new file mode 100644
index 0000000..50167c8
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory
+import org.gradle.internal.Factory
+import spock.lang.Specification
+
+import javax.tools.JavaCompiler
+
+class DefaultJavaCompilerFactoryTest extends Specification {
+    Factory<JavaCompiler> javaCompilerFinder = Mock()
+    def factory = new DefaultJavaCompilerFactory(new File("daemon-work-dir"), Mock(CompilerDaemonFactory), javaCompilerFinder)
+    
+    def "creates in-process compiler when JavaCompileSpec is provided"() {
+        expect:
+        def compiler = factory.create(JavaCompileSpec.class)
+        compiler instanceof NormalizingJavaCompiler
+        compiler.delegate instanceof JdkJavaCompiler
+    }
+
+    def "creates in-process compiler when JavaCompileSpec is provided and joint compilation"() {
+        expect:
+        def compiler = factory.createForJointCompilation(JavaCompileSpec.class)
+        compiler instanceof JdkJavaCompiler
+    }
+
+    def "creates command line compiler when CommandLineJavaCompileSpec is provided"() {
+        expect:
+        def compiler = factory.create(TestCommandLineJavaSpec.class)
+        compiler instanceof NormalizingJavaCompiler
+        compiler.delegate instanceof CommandLineJavaCompiler
+    }
+
+    def "creates command line compiler when CommandLineJavaCompileSpec is provided and joint compilation"() {
+        expect:
+        def compiler = factory.createForJointCompilation(TestCommandLineJavaSpec)
+        compiler instanceof CommandLineJavaCompiler
+    }
+
+    def "creates daemon compiler when ForkingJavaCompileSpec"() {
+        expect:
+        def compiler = factory.create(TestForkingJavaCompileSpec)
+        compiler instanceof NormalizingJavaCompiler
+        compiler.delegate instanceof DaemonJavaCompiler
+        compiler.delegate.delegate instanceof JdkJavaCompiler
+    }
+
+    def "creates in-process compiler when ForkingJavaCompileSpec is provided and joint compilation"() {
+        expect:
+        def compiler = factory.createForJointCompilation(TestForkingJavaCompileSpec)
+        compiler instanceof JdkJavaCompiler
+    }
+
+    private static class TestCommandLineJavaSpec extends DefaultJavaCompileSpec implements CommandLineJavaCompileSpec {
+    }
+
+    private static class TestForkingJavaCompileSpec extends DefaultJavaCompileSpec implements ForkingJavaCompileSpec {
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy
new file mode 100644
index 0000000..e9994b0
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.tasks.compile.CompileOptions
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder.USE_UNSHARED_COMPILER_TABLE_OPTION
+
+class JavaCompilerArgumentsBuilderTest extends Specification {
+
+    @Rule
+    TestNameTestDirectoryProvider tempDir = new TestNameTestDirectoryProvider()
+
+    def spec = new DefaultJavaCompileSpec()
+    def builder = new JavaCompilerArgumentsBuilder(spec)
+
+    def setup() {
+        spec.tempDir = tempDir.file("tmp")
+        spec.compileOptions = new CompileOptions()
+    }
+
+    def "generates options for an unconfigured spec"() {
+        expect:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates no -source option when current Jvm Version is used"() {
+        spec.sourceCompatibility = JavaVersion.current().toString();
+
+        expect:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -source option when compatibility differs from current Jvm version"() {
+        spec.sourceCompatibility = "1.4"
+
+        expect:
+        builder.build() == ["-source", "1.4", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates no -target option when current Jvm Version is used"() {
+        spec.targetCompatibility = JavaVersion.current().toString();
+
+        expect:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -target option when compatibility differs current Jvm version"() {
+        spec.targetCompatibility = "1.4"
+
+        expect:
+        builder.build() == ["-target", "1.4", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -d option"() {
+        def file = new File("/project/build")
+        spec.destinationDir = file
+
+        expect:
+        builder.build() == ["-d", file.path, "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -verbose option"() {
+        when:
+        spec.compileOptions.verbose = true
+
+        then:
+        builder.build() == ["-verbose", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.verbose = false
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -deprecation option"() {
+        when:
+        spec.compileOptions.deprecation = true
+
+        then:
+        builder.build() == ["-deprecation", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.deprecation = false
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -nowarn option"() {
+        when:
+        spec.compileOptions.warnings = true
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.warnings = false
+
+        then:
+        builder.build() == ["-nowarn", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -g option"() {
+        when:
+        spec.compileOptions.debug = true
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.debugOptions.debugLevel = "source,vars"
+
+        then:
+        builder.build() == ["-g:source,vars", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.debug = false
+
+        then:
+        builder.build() == ["-g:none", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -encoding option"() {
+        spec.compileOptions.encoding = "some-encoding"
+
+        expect:
+        builder.build() == ["-g", "-encoding", "some-encoding", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -bootclasspath option"() {
+        spec.compileOptions.bootClasspath = "/lib/lib1.jar:/lib/lib2.jar"
+
+        expect:
+        builder.build() == ["-g", "-bootclasspath", "/lib/lib1.jar:/lib/lib2.jar", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -extdirs option"() {
+        spec.compileOptions.extensionDirs = "/dir1:/dir2"
+
+        expect:
+        builder.build() == ["-g", "-extdirs", "/dir1:/dir2", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -classpath option"() {
+        def file1 = new File("/lib/lib1.jar")
+        def file2 = new File("/lib/lib2.jar")
+        spec.classpath = [file1, file2]
+
+        expect:
+        builder.build() == ["-g", "-sourcepath", defaultEmptySourcePathRefFolder(), "-classpath", "$file1$File.pathSeparator$file2", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "adds custom compiler args"() {
+        spec.compileOptions.compilerArgs = ["-a", "value-a", "-b", "value-b"]
+
+        expect:
+        builder.build() == ["-g", "-a", "value-a", "-b", "value-b", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "can include/exclude main options"() {
+        spec.sourceCompatibility = "1.4"
+
+        when:
+        builder.includeMainOptions(true)
+
+        then:
+        builder.build() == ["-source", "1.4", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        builder.includeMainOptions(false)
+
+        then:
+        builder.build() == [USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "includes main options by default"() {
+        spec.sourceCompatibility = "1.4"
+
+        expect:
+        builder.build() == ["-source", "1.4", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "can include/exclude classpath"() {
+        def file1 = new File("/lib/lib1.jar")
+        def file2 = new File("/lib/lib2.jar")
+        spec.classpath = [file1, file2]
+
+        when:
+        builder.includeClasspath(true)
+
+        then:
+        builder.build() == ["-g", "-sourcepath", defaultEmptySourcePathRefFolder(), "-classpath", "$file1$File.pathSeparator$file2", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        builder.includeClasspath(false)
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "includes classpath by default"() {
+        def file1 = new File("/lib/lib1.jar")
+        def file2 = new File("/lib/lib2.jar")
+        spec.classpath = [file1, file2]
+
+        expect:
+        builder.build() == ["-g", "-sourcepath", defaultEmptySourcePathRefFolder(), "-classpath", "$file1$File.pathSeparator$file2", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "can include/exclude launcher options"() {
+        spec.compileOptions.forkOptions.with {
+            memoryInitialSize = "64m"
+            memoryMaximumSize = "1g"
+        }
+
+        when:
+        builder.includeLauncherOptions(true)
+
+        then:
+        builder.build() == ["-J-Xms64m", "-J-Xmx1g", "-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        builder.includeLauncherOptions(false)
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "does not include launcher options by default"() {
+        spec.compileOptions.forkOptions.with {
+            memoryInitialSize = "64m"
+            memoryMaximumSize = "1g"
+        }
+
+        expect:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "can include/exclude source files"() {
+        def file1 = new File("/src/Person.java")
+        def file2 = new File("Computer.java")
+        spec.source = new SimpleFileCollection(file1, file2)
+
+        when:
+        builder.includeSourceFiles(true)
+
+        then:
+        builder.build() == ["-g", file1.path, file2.path, USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        builder.includeSourceFiles(false)
+
+        then:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "does not include source files by default"() {
+        def file1 = new File("/src/Person.java")
+        def file2 = new File("Computer.java")
+        spec.source = new SimpleFileCollection(file1, file2)
+
+        expect:
+        builder.build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -sourcepath option"() {
+        def file1 = new File("/lib/lib1.jar")
+        def file2 = new File("/lib/lib2.jar")
+        def fc = new SimpleFileCollection(file1, file2)
+        spec.compileOptions.sourcepath = fc
+
+        expect:
+        builder.build() == ["-g", "-sourcepath", fc.asPath, USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    def "generates -sourcepath option when classpath not included and explicit value for source path"() {
+        def file1 = new File("/lib/lib1.jar")
+        def file2 = new File("/lib/lib2.jar")
+        spec.compileOptions.sourcepath = new SimpleFileCollection(file1)
+        spec.classpath = new SimpleFileCollection(file2)
+
+        expect:
+        builder.build() == ["-g", "-sourcepath", "$file1", "-classpath", asPath(file2), USE_UNSHARED_COMPILER_TABLE_OPTION]
+        builder.includeClasspath(false).build() == ["-g", "-sourcepath", asPath(file1), USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.compileOptions.sourcepath = null
+
+        then:
+        builder.includeClasspath(true).build() == ["-g", "-sourcepath", defaultEmptySourcePathRefFolder(), "-classpath", asPath(file2), USE_UNSHARED_COMPILER_TABLE_OPTION]
+        builder.includeClasspath(false).build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+
+        when:
+        spec.classpath = null
+
+        then:
+        builder.includeClasspath(true).build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+        builder.includeClasspath(false).build() == ["-g", USE_UNSHARED_COMPILER_TABLE_OPTION]
+    }
+
+    String defaultEmptySourcePathRefFolder() {
+        new File(spec.tempDir, JavaCompilerArgumentsBuilder.EMPTY_SOURCE_PATH_REF_DIR).absolutePath
+    }
+
+    def "can exclude customizations"() {
+        expect:
+        builder.includeCustomizations(false).build() == ["-g"]
+    }
+
+    String asPath(File... files) {
+        new SimpleFileCollection(files).asPath
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactoryTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactoryTest.groovy
new file mode 100644
index 0000000..04a269e
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaHomeBasedJavaCompilerFactoryTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile
+
+import org.gradle.internal.Factory
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+import javax.tools.JavaCompiler
+
+class JavaHomeBasedJavaCompilerFactoryTest extends Specification {
+    Factory<? extends File> currentJvmJavaHomeFactory = Mock()
+    Factory<? extends File> systemPropertiesJavaHomeFactory = Mock()
+    Factory<? extends JavaCompiler> systemJavaCompilerFactory = Mock()
+    JavaHomeBasedJavaCompilerFactory factory = new JavaHomeBasedJavaCompilerFactory(currentJvmJavaHomeFactory, systemPropertiesJavaHomeFactory, systemJavaCompilerFactory)
+    JavaCompiler javaCompiler = Mock()
+    @Rule TestNameTestDirectoryProvider temporaryFolder
+
+    def "creates Java compiler for matching Java home directory"() {
+        TestFile javaHome = temporaryFolder.file('my/test/java/home')
+
+        when:
+        JavaCompiler expectedJavaCompiler = factory.create()
+
+        then:
+        1 * currentJvmJavaHomeFactory.create() >> javaHome
+        1 * systemPropertiesJavaHomeFactory.create() >> javaHome
+        1 * systemJavaCompilerFactory.create() >> javaCompiler
+        javaCompiler == expectedJavaCompiler
+    }
+
+    def "cannot find Java compiler for matching Java home directory"() {
+        TestFile javaHome = temporaryFolder.file('my/test/java/home')
+
+        when:
+        factory.create()
+
+        then:
+        1 * currentJvmJavaHomeFactory.create() >> javaHome
+        1 * systemPropertiesJavaHomeFactory.create() >> javaHome
+        1 * systemJavaCompilerFactory.create() >> null
+        Throwable t = thrown(RuntimeException)
+        t.message == 'Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory.'
+    }
+
+    def "creates Java compiler for mismatching Java home directory"() {
+        File originalJavaHomeDir = SystemProperties.instance.javaHomeDir
+        TestFile realJavaHome = temporaryFolder.file('my/test/java/home/real')
+        TestFile javaHomeFromToolProvidersPointOfView = temporaryFolder.file('my/test/java/home/toolprovider')
+
+        when:
+        JavaCompiler expectedJavaCompiler = factory.create()
+
+        then:
+        1 * currentJvmJavaHomeFactory.create() >> realJavaHome
+        1 * systemPropertiesJavaHomeFactory.create() >> javaHomeFromToolProvidersPointOfView
+        1 * systemJavaCompilerFactory.create() >> {
+            assert SystemProperties.instance.javaHomeDir == realJavaHome
+            javaCompiler
+        }
+        javaCompiler == expectedJavaCompiler
+        originalJavaHomeDir == SystemProperties.instance.javaHomeDir
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy
new file mode 100644
index 0000000..c75fd37
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy
@@ -0,0 +1,125 @@
+/*
+ * 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.tasks.compile
+
+import org.gradle.api.tasks.WorkResult
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+
+import groovy.transform.InheritConstructors
+import org.gradle.api.tasks.compile.CompileOptions
+import spock.lang.Specification
+
+class NormalizingJavaCompilerTest extends Specification {
+    org.gradle.language.base.internal.compile.Compiler<JavaCompileSpec> target = Mock()
+    DefaultJavaCompileSpec spec = new DefaultJavaCompileSpec()
+    NormalizingJavaCompiler compiler = new NormalizingJavaCompiler(target)
+
+    def setup() {
+        spec.source = files("Source1.java", "Source2.java", "Source3.java")
+        spec.classpath = files("Dep1.jar", "Dep2.jar", "Dep3.jar")
+        spec.compileOptions = new CompileOptions()
+    }
+
+    def "delegates to target compiler after resolving source and classpath"() {
+        WorkResult workResult = Mock()
+
+        when:
+        def result = compiler.execute(spec)
+
+        then:
+        1 * target.execute(spec) >> {
+            assert spec.source.getClass() == SimpleFileCollection
+            assert spec.source.files == old(spec.source.files)
+            assert spec.classpath.getClass() == SimpleFileCollection
+            assert spec.classpath.files == old(spec.classpath.files)
+            workResult
+        }
+        result == workResult
+    }
+
+    def "silently excludes source files not ending in .java"() {
+        spec.source = files("House.scala", "Person1.java", "package.html", "Person2.java")
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        1 * target.execute(spec) >> {
+            assert spec.source.files == files("Person1.java", "Person2.java").files
+        }
+    }
+
+    def "propagates compile failure when failOnError is true"() {
+        def failure
+        target.execute(spec) >> { throw failure = new CompilationFailedException() }
+
+        spec.compileOptions.failOnError = true
+
+        when:
+        compiler.execute(spec)
+        
+        then:
+        CompilationFailedException e = thrown()
+        e == failure
+    }
+
+    def "ignores compile failure when failOnError is false"() {
+        target.execute(spec) >> { throw new CompilationFailedException() }
+
+        spec.compileOptions.failOnError = false
+
+        when:
+        def result = compiler.execute(spec)
+
+        then:
+        noExceptionThrown()
+        !result.didWork
+    }
+
+    def "propagates other failure"() {
+        def failure
+        target.execute(spec) >> { throw failure = new RuntimeException() }
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+    }
+
+    def "resolves any non-strings that make it into custom compiler args"() {
+        spec.compileOptions.compilerArgs << "a dreaded ${"GString"}"
+        spec.compileOptions.compilerArgs << 42
+        assert !spec.compileOptions.compilerArgs.any { it instanceof String }
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        1 * target.execute(_) >> { JavaCompileSpec spec ->
+            assert spec.compileOptions.compilerArgs.every { it instanceof String }
+        }
+    }
+
+    private files(String... paths) {
+        new TestFileCollection(paths.collect { new File(it) })
+    }
+
+    // file collection whose type is distinguishable from SimpleFileCollection
+    @InheritConstructors
+    static class TestFileCollection extends SimpleFileCollection {}
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdaterTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdaterTest.groovy
new file mode 100644
index 0000000..dab71e7
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassSetAnalysisUpdaterTest.groovy
@@ -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.tasks.compile.incremental
+
+import org.gradle.api.file.ConfigurableFileTree
+import org.gradle.api.internal.cache.Stash
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer
+import spock.lang.Specification
+import spock.lang.Subject
+
+class ClassSetAnalysisUpdaterTest extends Specification {
+
+    def stash = Mock(Stash)
+    def operations = Mock(FileOperations)
+    def analyzer = Mock(ClassDependenciesAnalyzer)
+
+    @Subject updater = new ClassSetAnalysisUpdater(stash, operations, analyzer)
+
+    def "updates"() {
+        when: updater.updateAnalysis(Stub(JavaCompileSpec))
+
+        then:
+        1 * operations.fileTree(_) >> Mock(ConfigurableFileTree)
+        1 * stash.put(_)
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizerTest.groovy
new file mode 100644
index 0000000..daa98d2
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationFinalizerTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental
+
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarClasspathSnapshotWriter
+import org.gradle.api.tasks.WorkResult
+import org.gradle.language.base.internal.compile.Compiler
+import spock.lang.Specification
+import spock.lang.Subject
+
+class IncrementalCompilationFinalizerTest extends Specification {
+
+    def compiler = Mock(Compiler)
+    def writer = Mock(JarClasspathSnapshotWriter)
+    def infoUpdater = Mock(ClassSetAnalysisUpdater)
+    def compileSpec = Stub(JavaCompileSpec)
+
+    @Subject finalizer = new IncrementalCompilationFinalizer(compiler, writer, infoUpdater)
+
+    def "performs finalization"() {
+        when:
+        finalizer.execute(compileSpec)
+
+        then:
+        1 * compiler.execute(compileSpec) >> Mock(WorkResult)
+        1 * infoUpdater.updateAnalysis(compileSpec)
+        1 * writer.storeJarSnapshots(_)
+        0 * _
+    }
+
+    def "does not update if rebuild was not required"() {
+        when:
+        finalizer.execute(compileSpec)
+
+        then:
+        1 * compiler.execute(compileSpec) >> Mock(RecompilationNotNecessary)
+        1 * writer.storeJarSnapshots(_)
+        0 * _
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializerTest.groovy
new file mode 100644
index 0000000..5aa0612
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationInitializerTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.api.internal.tasks.compile.incremental
+
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.tasks.compile.JavaCompileSpec
+import org.gradle.api.tasks.util.PatternSet
+import spock.lang.Specification
+import spock.lang.Subject
+
+class IncrementalCompilationInitializerTest extends Specification {
+
+    def fileOperations = Mock(FileOperations)
+    @Subject initializer = new IncrementalCompilationInitializer(fileOperations)
+
+    def "prepares patterns"() {
+        PatternSet classesToDelete = Mock(PatternSet)
+        PatternSet sourceToCompile = Mock(PatternSet)
+
+        when:
+        initializer.preparePatterns(["com.Foo", "Bar"], classesToDelete, sourceToCompile)
+
+        then:
+        1 * classesToDelete.include('com/Foo.class')
+        1 * classesToDelete.include('com/Foo$*.class')
+        1 * classesToDelete.include('Bar.class')
+        1 * classesToDelete.include('Bar$*.class')
+
+        1 * sourceToCompile.include('Bar.java')
+        1 * sourceToCompile.include('com/Foo.java')
+
+        0 * _
+    }
+
+    def "does not prepare patterns when stale classes empty"() {
+        when: initializer.preparePatterns([], Mock(PatternSet), Mock(PatternSet))
+        then: thrown(AssertionError)
+    }
+
+    def "configures empty source when stale classes empty"() {
+        def compileSpec = Mock(JavaCompileSpec)
+        when: initializer.initializeCompilation(compileSpec, [])
+        then:
+        1 * compileSpec.setSource { it.files.empty }
+        0 * _
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverterTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverterTest.groovy
new file mode 100644
index 0000000..7b21b5c
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/SourceToNameConverterTest.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.api.internal.tasks.compile.incremental
+
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+class SourceToNameConverterTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+    def srcDirs = Stub(CompilationSourceDirs) {
+        getSourceDirs() >> [temp.file("src/main/java"), temp.file("src/main/java2")]
+    }
+    @Subject converter = new SourceToNameConverter(srcDirs)
+
+    def "knows java source class relative path"() {
+        expect:
+        converter.getClassName(temp.file("src/main/java/Foo.java")) == "Foo"
+        converter.getClassName(temp.file("src/main/java/org/bar/Bar.java")) == "org.bar.Bar"
+        converter.getClassName(temp.file("src/main/java2/com/Com.java")) == "com.Com"
+
+        when: converter.getClassName(temp.file("src/main/unknown/Xxx.java"))
+        then: thrown(IllegalArgumentException)
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzerTest.groovy
new file mode 100644
index 0000000..8b862da
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/DefaultClassDependenciesAnalyzerTest.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.api.internal.tasks.compile.incremental.analyzer
+
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.annotations.*
+import org.gradle.api.internal.tasks.compile.incremental.test.*
+import spock.lang.Specification
+import spock.lang.Subject
+
+class DefaultClassDependenciesAnalyzerTest extends Specification {
+
+    @Subject analyzer = new DefaultClassDependenciesAnalyzer()
+
+    private ClassAnalysis analyze(Class foo) {
+        analyzer.getClassAnalysis(foo.name, classStream(foo))
+    }
+
+    def "knows dependencies of a java class"() {
+        expect:
+        analyze(SomeOtherClass).classDependencies == [YetAnotherClass.name, SomeClass.name] as Set
+    }
+
+    def "knows basic class dependencies of a groovy class"() {
+        def deps = analyze(DefaultClassDependenciesAnalyzerTest).classDependencies
+
+        expect:
+        deps.contains(Specification.class.name)
+        //deps.contains(DefaultClassDependenciesAnalyzer.class.name) // why this does not work (is it because of groovy)?
+    }
+
+    def "knows if a class have non-private constants"() {
+        expect:
+        analyze(HasNonPrivateConstants).classDependencies == [UsedByNonPrivateConstantsClass.name] as Set
+        analyze(HasNonPrivateConstants).dependencyToAll
+
+        analyze(HasPublicConstants).classDependencies.isEmpty()
+        analyze(HasPublicConstants).dependencyToAll
+
+        analyze(HasPrivateConstants).classDependencies == [HasNonPrivateConstants.name] as Set
+        !analyze(HasPrivateConstants).dependencyToAll
+    }
+
+    def "knows if a class uses annotations"() {
+        expect:
+        analyze(UsesRuntimeAnnotation).classDependencies.isEmpty()
+        analyze(SomeRuntimeAnnotation).classDependencies.isEmpty()
+        analyze(SomeRuntimeAnnotation).dependencyToAll
+
+        analyze(UsesClassAnnotation).classDependencies.isEmpty()
+        analyze(SomeClassAnnotation).classDependencies.isEmpty()
+        analyze(SomeClassAnnotation).dependencyToAll
+
+        analyze(UsesSourceAnnotation).classDependencies.isEmpty() //source annotations are wiped from the bytecode
+        analyze(SomeSourceAnnotation).classDependencies.isEmpty()
+        analyze(SomeSourceAnnotation).dependencyToAll
+    }
+
+    InputStream classStream(Class aClass) {
+        aClass.getResourceAsStream(aClass.getSimpleName() + ".class")
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeClassAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeClassAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeClassAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeClassAnnotation.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeRuntimeAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeRuntimeAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeRuntimeAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeRuntimeAnnotation.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeSourceAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeSourceAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeSourceAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/SomeSourceAnnotation.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesAnnotationInField.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesAnnotationInField.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesAnnotationInField.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesAnnotationInField.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesClassAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesClassAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesClassAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesClassAnnotation.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesRuntimeAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesRuntimeAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesRuntimeAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesRuntimeAnnotation.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesSourceAnnotation.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesSourceAnnotation.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesSourceAnnotation.java
rename to subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/annotations/UsesSourceAnnotation.java
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulatorTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulatorTest.groovy
new file mode 100644
index 0000000..3ae2c7e
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassDependentsAccumulatorTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps
+
+import spock.lang.Specification
+
+class ClassDependentsAccumulatorTest extends Specification {
+
+    def accumulator = new ClassDependentsAccumulator("")
+
+    def "dependents map is empty by default"() {
+        expect:
+        accumulator.dependentsMap == [:]
+    }
+
+    def "remembers if class is dependency to all"() {
+        // a -> b -> c
+        accumulator.addClass("a", false, ["b"])
+        accumulator.addClass("b", true,  ["c"])
+        accumulator.addClass("c", false, [])
+
+        expect:
+        !accumulator.dependentsMap.a.dependencyToAll
+        accumulator.dependentsMap.b.dependencyToAll
+        !accumulator.dependentsMap.c.dependencyToAll
+    }
+
+    def "accumulates dependents"() {
+        accumulator.addClass("d", true, ['x'])
+        accumulator.addClass("a", false, ["b", "c"])
+        accumulator.addClass("b", true,  ["c", "a"])
+        accumulator.addClass("c", false, [])
+
+        expect:
+        accumulator.dependentsMap.a.dependentClasses == ['b'] as Set
+        accumulator.dependentsMap.b.dependentClasses == ['a'] as Set
+        accumulator.dependentsMap.c.dependentClasses == ['b', 'a'] as Set
+        accumulator.dependentsMap.d.dependentClasses == [] as Set
+        accumulator.dependentsMap.x.dependentClasses == ['d'] as Set
+    }
+
+    def "creates keys for all encountered classes"() {
+        accumulator.addClass("a", false, ["x"])
+        accumulator.addClass("b", true,  ["a", "b"])
+        accumulator.addClass("c", true,  [])
+
+        expect:
+        accumulator.dependentsMap.keySet() == ["a", "b", "c", "x"] as Set
+    }
+
+    def "knows when class is dependent to all if that class is added first"() {
+        accumulator.addClass("b", true,  [])
+        accumulator.addClass("a", false, ["b"])
+
+        expect:
+        accumulator.dependentsMap.b.dependencyToAll
+    }
+
+    def "knows when class is dependent to all even if that class is added last"() {
+        accumulator.addClass("a", false, ["b"])
+        accumulator.addClass("b", true,  [])
+
+        expect:
+        accumulator.dependentsMap.b.dependencyToAll
+    }
+
+    def "uses package prefix filter for classes"() {
+        accumulator = new ClassDependentsAccumulator("org.gradle")
+        accumulator.addClass("gradle.Foo", true, ["org.gradle.Foo"])
+
+        expect:
+        accumulator.dependentsMap["gradle.Foo"] == null
+        accumulator.dependentsMap["org.gradle.Foo"].dependentClasses.isEmpty()
+    }
+
+    def "uses package prefix filter for dependencies"() {
+        accumulator = new ClassDependentsAccumulator("org.gradle")
+        accumulator.addClass("org.gradle.Foo", false, ["gradle.Bar"])
+
+        expect:
+        accumulator.dependentsMap["gradle.Bar"] == null
+        accumulator.dependentsMap["org.gradle.Foo"].dependentClasses.isEmpty()
+    }
+
+    def "filters out self dependencies"() {
+        accumulator.addClass("a", false, ["a", "b"])
+
+        expect:
+        accumulator.dependentsMap["b"].dependentClasses == ["a"] as Set
+        accumulator.dependentsMap["a"].dependentClasses.isEmpty()
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassFilesAnalyzerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassFilesAnalyzerTest.groovy
new file mode 100644
index 0000000..36d0316
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassFilesAnalyzerTest.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.api.internal.tasks.compile.incremental.deps
+
+import org.gradle.api.file.FileVisitDetails
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysis
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassFilesAnalyzer
+import spock.lang.Specification
+import spock.lang.Subject
+
+class ClassFilesAnalyzerTest extends Specification {
+
+    def classAnalyzer = Mock(ClassDependenciesAnalyzer)
+    def accumulator = Mock(ClassDependentsAccumulator)
+    @Subject analyzer = new ClassFilesAnalyzer(classAnalyzer, "org.foo", accumulator)
+
+    def "does not visit dirs"() {
+        when: analyzer.visitDir(null)
+        then: 0 * _
+    }
+
+    def "does not visit non .class files"() {
+        def details = Stub(FileVisitDetails) { getName() >> "foo.xml"}
+        when: analyzer.visitFile(details)
+        then: 0 * _
+    }
+
+    def "is sensitive to package prefix"() {
+        def details = Stub(FileVisitDetails) { getPath() >> "com/foo/Foo.class"}
+        when: analyzer.visitFile(details)
+        then: 0 * _
+    }
+
+    def "accumulates dependencies"() {
+        def details = Stub(FileVisitDetails) {
+            getPath() >> "org/foo/Foo.class"
+            getFile() >> new File("Foo.class")
+        }
+        when: analyzer.visitFile(details)
+        then:
+        1 * classAnalyzer.getClassAnalysis("org.foo.Foo", new File("Foo.class")) >> new ClassAnalysis(new HashSet(["A"]), true)
+        1 * accumulator.addClass("org.foo.Foo", true, new HashSet(["A"]))
+        0 * _
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisDataSerializerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisDataSerializerTest.groovy
new file mode 100644
index 0000000..518cc03
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisDataSerializerTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps
+
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.serialize.OutputStreamBackedEncoder
+import spock.lang.Specification
+import spock.lang.Subject
+
+import static org.gradle.api.internal.tasks.compile.incremental.deps.DefaultDependentsSet.dependents
+
+class ClassSetAnalysisDataSerializerTest extends Specification {
+
+    @Subject serializer = new ClassSetAnalysisData.Serializer()
+
+    def "serializes"() {
+        def data = new ClassSetAnalysisData(
+                ["A": dependents("B", "C"), "B": new DefaultDependentsSet(true, ["C"]), "C": dependents(), "D": new DependencyToAll(), ])
+        def os = new ByteArrayOutputStream()
+        def e = new OutputStreamBackedEncoder(os)
+
+        when:
+        serializer.write(e, data)
+        ClassSetAnalysisData read = serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(os.toByteArray())))
+
+        then:
+        read.dependents.keySet() == data.dependents.keySet()
+
+        ["A", "B", "C"].each {
+            assert read.dependents[it].dependentClasses == data.dependents[it].dependentClasses
+            assert read.dependents[it].dependencyToAll == data.dependents[it].dependencyToAll
+        }
+
+        read.dependents["D"] instanceof DependencyToAll
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisTest.groovy
new file mode 100644
index 0000000..78e0c1d
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/ClassSetAnalysisTest.groovy
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.deps
+
+import spock.lang.Specification
+
+import static org.gradle.api.internal.tasks.compile.incremental.deps.DefaultDependentsSet.dependents
+
+class ClassSetAnalysisTest extends Specification {
+
+    ClassSetAnalysis analysis(Map<String, DependentsSet> dependents) {
+        new ClassSetAnalysis(new ClassSetAnalysisData(dependents))
+    }
+
+    def "returns empty analysis"() {
+        def a = analysis([:])
+        expect: a.getRelevantDependents("Foo").dependentClasses.isEmpty()
+    }
+
+    def "does not recurse if root class is a dependency to all"() {
+        def a = analysis(["Foo": new DefaultDependentsSet(true, ["Bar"])])
+        def deps = a.getRelevantDependents("Foo")
+
+        expect:
+        deps.dependencyToAll
+
+        when: deps.getDependentClasses()
+        then: thrown(UnsupportedOperationException)
+    }
+
+    def "marks as dependency to all only if root class is a dependency to all"() {
+        def a = analysis([
+                "a":   new DefaultDependentsSet(false, ['b']),
+                'b': new DefaultDependentsSet(true, ['c']),
+                "c": new DefaultDependentsSet(true, [])
+        ])
+        def deps = a.getRelevantDependents("a")
+
+        expect:
+        deps.dependentClasses == ['b', 'c'] as Set
+        !deps.dependencyToAll
+    }
+
+    def "recurses nested dependencies"() {
+        def a = analysis([
+                "Foo": dependents("Bar"),
+                "Bar": dependents("Baz"),
+                "Baz": dependents(),
+        ])
+        def deps = a.getRelevantDependents("Foo")
+
+        expect:
+        deps.dependentClasses == ["Bar", "Baz"] as Set
+        a.getRelevantDependents("Bar").dependentClasses == ["Baz"] as Set
+        a.getRelevantDependents("Baz").dependentClasses == [] as Set
+    }
+
+    def "recurses multiple dependencies"() {
+        def a = analysis([
+                "a": dependents("b", "c"),
+                "b": dependents("d"),
+                "c": dependents("e"),
+                "d": dependents(),
+                "e": dependents()
+        ])
+        def deps = a.getRelevantDependents("a")
+
+        expect:
+        deps.dependentClasses == ["b", "c", "d", "e"] as Set
+    }
+
+    def "removes self from dependents"() {
+        def a = analysis([
+                "Foo": dependents("Foo")
+        ])
+        def deps = a.getRelevantDependents("Foo")
+
+        expect:
+        deps.dependentClasses == [] as Set
+    }
+
+    def "handles dependency cycles"() {
+        def a = analysis([
+                "Foo": dependents("Bar"),
+                "Bar": dependents("Baz"),
+                "Baz": dependents("Foo"),
+        ])
+        def deps = a.getRelevantDependents("Foo")
+
+        expect:
+        deps.dependentClasses == ["Bar", "Baz"] as Set
+    }
+
+    def "recurses but filters out inner classes"() {
+        def a = analysis([
+                "a":   dependents('a$b', 'c'),
+                'a$b': dependents('d'),
+                "c": dependents(),
+                "d": dependents(),
+        ])
+        def deps = a.getRelevantDependents("a")
+
+        expect:
+        deps.dependentClasses == ["c", "d"] as Set
+    }
+
+    def "handles cycles with inner classes"() {
+        def a = analysis([
+                "a":   dependents('a$b'),
+                'a$b': dependents('a$b', 'c'),
+                "c": dependents()
+        ])
+        def deps = a.getRelevantDependents("a")
+
+        expect:
+        deps.dependentClasses == ["c"] as Set
+    }
+
+    def "provides dependents of all input classes"() {
+        def a = analysis([
+                "A": dependents("B"), "B": dependents(),
+                "C": dependents("D"), "D": dependents(),
+                "E": dependents("D"), "F": dependents(),
+        ])
+        def deps = a.getRelevantDependents(["A", "E"])
+
+        expect:
+        deps.dependentClasses == ["D", "B"] as Set
+    }
+
+    def "provides recursive dependents of all input classes"() {
+        def a = analysis([
+                "A": dependents("B"), "B": dependents("C"), "C": dependents(),
+                "D": dependents("E"), "E": dependents(),
+                "F": dependents("G"), "G": dependents(),
+        ])
+        def deps = a.getRelevantDependents(["A", "D"])
+
+        expect:
+        deps.dependentClasses == ["E", "B", "C"] as Set
+    }
+
+    def "knows when any of the input classes is a dependency to all"() {
+        def a = analysis([
+                "A": dependents("B"), "B": dependents(),
+                "C": new DefaultDependentsSet(true, []),
+                "D": dependents("E"), "E": dependents(),
+        ])
+        def deps = a.getRelevantDependents(["A", "C", "will not be reached"])
+
+        expect:
+        deps.dependencyToAll
+    }
+
+    def "knows when input class is a dependency to all"() {
+        def a = analysis([
+                "A": dependents("B"), "B": dependents(),
+                "C": new DefaultDependentsSet(true, []),
+        ])
+        expect:
+        !a.isDependencyToAll("A")
+        a.isDependencyToAll("C")
+        !a.isDependencyToAll("Unknown")
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverterTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverterTest.groovy
new file mode 100644
index 0000000..14aea76
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/deps/OutputToNameConverterTest.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.internal.tasks.compile.incremental.deps
+
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+class OutputToNameConverterTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+    @Subject provider = new OutputToNameConverter(temp.createDir("root/dir"))
+
+    def "provides class name"() {
+        expect:
+        "foo.bar.Foo" == provider.getClassName(temp.file("root/dir/foo/bar/Foo.class"))
+        "Foo" == provider.getClassName(temp.file("root/dir/Foo.class"))
+        'Foo$Bar' == provider.getClassName(temp.file('root/dir/Foo$Bar.class'))
+    }
+
+    def "fails when class is outside of root"() {
+        when:
+        provider.getClassName(temp.file("foo/Foo.class"))
+        then:
+        thrown(IllegalArgumentException)
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotterTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotterTest.groovy
new file mode 100644
index 0000000..6dffe8f
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/DefaultJarSnapshotterTest.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.tasks.compile.incremental.jar
+
+import org.gradle.api.internal.file.collections.DirectoryFileTree
+import org.gradle.api.internal.file.collections.FileTreeAdapter
+import org.gradle.api.internal.hash.Hasher
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassFilesAnalyzer
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+ at UsesNativeServices
+class DefaultJarSnapshotterTest extends Specification {
+
+    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+    def hasher = Mock(Hasher)
+
+    @Subject snapshotter = new DefaultJarSnapshotter(hasher, Mock(ClassDependenciesAnalyzer))
+
+    def "creates snapshot for an empty jar"() {
+        expect:
+        def snapshot = snapshotter.createSnapshot(new byte[0], new JarArchive(new File("a.jar"), new FileTreeAdapter(new DirectoryFileTree(new File("missing")))))
+        snapshot.hashes.isEmpty()
+        snapshot.analysis
+    }
+
+    def "creates snapshot of a jar with classes"() {
+        def f1 = temp.createFile("foo/Foo.class")
+        def f2 = temp.createFile("foo/com/Foo2.class")
+        def analyzer = Mock(ClassFilesAnalyzer)
+
+        when:
+        def snapshot = snapshotter.createSnapshot(new byte[0], new FileTreeAdapter(new DirectoryFileTree(temp.file("foo"))), analyzer)
+
+        then:
+        2 * analyzer.visitFile(_)
+        1 * hasher.hash(f1)
+        1 * hasher.hash(f2)
+        1 * analyzer.getAnalysis() >> Stub(ClassSetAnalysisData)
+        0 * _._
+
+        and:
+        snapshot.hashes.keySet() == ["Foo", "com.Foo2"] as Set
+        snapshot.analysis
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactoryTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactoryTest.groovy
new file mode 100644
index 0000000..e2c21a0
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotFactoryTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile.incremental.jar
+
+import org.gradle.api.file.FileTree
+import spock.lang.Specification
+import spock.lang.Subject
+
+class JarClasspathSnapshotFactoryTest extends Specification {
+
+    def snapshotter = Mock(JarSnapshotter)
+    @Subject factory = new JarClasspathSnapshotFactory(snapshotter)
+
+    def "creates classpath snapshot with correct duplicate classes"() {
+        def jar1 = Stub(JarArchive); def jar2 = Stub(JarArchive); def jar3 = Stub(JarArchive)
+
+        def sn1 = Stub(JarSnapshot) { getClasses() >> ["A", "B", "C"] }
+        def sn2 = Stub(JarSnapshot) { getClasses() >> ["C", "D"] }
+        def sn3 = Stub(JarSnapshot) { getClasses() >> ["B", "E"] }
+
+        when:
+        def s = factory.createSnapshot([jar1, jar2, jar3])
+
+        then:
+        1 * snapshotter.createSnapshot(jar1) >> sn1
+        1 * snapshotter.createSnapshot(jar2) >> sn2
+        1 * snapshotter.createSnapshot(jar3) >> sn3
+        0 * _
+
+        s.data.duplicateClasses == ["B", "C"] as Set
+    }
+
+    def "creates classpath snapshot with correct hashes"() {
+        def jar1 = new JarArchive(new File("f1"), Stub(FileTree))
+        def jar2 = new JarArchive(new File("f2"), Stub(FileTree))
+
+        def sn1 = Stub(JarSnapshot) { getHash() >> new byte[1] }
+        def sn2 = Stub(JarSnapshot) { getHash() >> new byte[2] }
+
+        when:
+        def s = factory.createSnapshot([jar1, jar2])
+
+        then:
+        1 * snapshotter.createSnapshot(jar1) >> sn1
+        1 * snapshotter.createSnapshot(jar2) >> sn2
+
+        s.data.jarHashes.size() == 2
+        s.data.jarHashes[new File("f1")] == new byte[1]
+        s.data.jarHashes[new File("f2")] == new byte[2]
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMakerTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMakerTest.groovy
new file mode 100644
index 0000000..7345a25
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarClasspathSnapshotMakerTest.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.api.internal.tasks.compile.incremental.jar
+
+import org.gradle.api.file.FileTree
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysis
+import spock.lang.Specification
+import spock.lang.Subject
+
+class JarClasspathSnapshotMakerTest extends Specification {
+
+    def store = Mock(LocalJarClasspathSnapshotStore)
+    def analysis = Mock(ClassSetAnalysis)
+    def factory = Mock(JarClasspathSnapshotFactory)
+    def finder = Mock(ClasspathJarFinder)
+
+    @Subject maker = new JarClasspathSnapshotMaker(store, factory, finder)
+
+    def "stores jar snapshots"() {
+        def jar1 = new JarArchive(new File("jar1.jar"), Mock(FileTree));
+        def jar2 = new JarArchive(new File("jar2.jar"), Mock(FileTree))
+
+        def snapshotData = Stub(JarClasspathSnapshotData)
+        def classpathSnapshot = Stub(JarClasspathSnapshot) { getData() >> snapshotData }
+        def filesDummy = [new File("f")]
+
+        when:
+        maker.storeJarSnapshots(filesDummy)
+
+        then:
+        maker.getJarClasspathSnapshot(filesDummy) == classpathSnapshot
+        maker.getJarClasspathSnapshot(filesDummy) == classpathSnapshot
+
+        and:
+        1 * finder.findJarArchives(filesDummy) >> [jar1, jar2]
+        1 * factory.createSnapshot([jar1, jar2]) >> classpathSnapshot
+        1 * store.put(snapshotData)
+        0 * _
+    }
+
+    def "gets classpath snapshot"() {
+        def jar1 = new JarArchive(new File("jar1.jar"), Mock(FileTree));
+
+        def classpathSnapshot = Stub(JarClasspathSnapshot)
+        def filesDummy = [new File("f")]
+
+        when:
+        maker.getJarClasspathSnapshot(filesDummy) == classpathSnapshot
+        maker.getJarClasspathSnapshot(filesDummy) == classpathSnapshot
+
+        then:
+        1 * finder.findJarArchives(filesDummy) >> [jar1]
+        1 * factory.createSnapshot([jar1]) >> classpathSnapshot
+        0 * _
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotTest.groovy
new file mode 100644
index 0000000..c734c43
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/jar/JarSnapshotTest.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.internal.tasks.compile.incremental.jar
+
+import org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependencyToAll
+import org.gradle.api.internal.tasks.compile.incremental.deps.DependentsSet
+import spock.lang.Specification
+
+import static org.gradle.api.internal.tasks.compile.incremental.deps.DefaultDependentsSet.dependents
+
+class JarSnapshotTest extends Specification {
+
+    def analysis = Stub(ClassSetAnalysisData)
+
+    private JarSnapshot snapshot(Map<String, byte[]> hashes, ClassSetAnalysisData a) {
+        new JarSnapshot(new JarSnapshotData(new byte[0], hashes, a))
+    }
+
+    private DependentsSet altered(JarSnapshot s1, JarSnapshot s2) {
+        s1.getAffectedClassesSince(s2).altered
+    }
+
+    def "knows when there are no affected classes since some other snapshot"() {
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes, "B": "B".bytes], analysis)
+
+        expect:
+        altered(s1, s2).dependentClasses.isEmpty()
+    }
+
+    def "knows when there are extra/missing classes since some other snapshot"() {
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "C".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes], analysis)
+
+        expect:
+        altered(s1, s2).dependentClasses.isEmpty() //ignore class additions
+        altered(s2, s1).dependentClasses == ["B", "C"] as Set
+    }
+
+    def "knows when there are changed classes since other snapshot"() {
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "C".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes, "B": "BB".bytes], analysis)
+
+        expect:
+        altered(s1, s2).dependentClasses == ["B"] as Set
+        altered(s2, s1).dependentClasses == ["B", "C"] as Set
+    }
+
+    def "knows when transitive class is affected transitively via class change"() {
+        def analysis = Mock(ClassSetAnalysisData)
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "C".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "CC".bytes], analysis)
+
+        analysis.getDependents("C") >> dependents("B")
+        analysis.getDependents("B") >> dependents()
+
+        expect:
+        altered(s1, s2).dependentClasses == ["B", "C"] as Set
+        altered(s2, s1).dependentClasses == ["B", "C"] as Set
+    }
+
+    def "knows when transitive class is affected transitively via class removal"() {
+        def analysis = Mock(ClassSetAnalysisData)
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "C".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes, "B": "B".bytes], analysis)
+
+        analysis.getDependents("C") >> dependents("B")
+        analysis.getDependents("B") >> dependents()
+
+        expect:
+        altered(s1, s2).dependentClasses.isEmpty()
+        altered(s2, s1).dependentClasses == ["B", "C"] as Set
+    }
+
+    def "knows when class is dependency to all"() {
+        def analysis = Mock(ClassSetAnalysisData)
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes, "B": "BB".bytes], analysis)
+
+        analysis.getDependents("B") >> new DependencyToAll()
+
+        expect:
+        altered(s1, s2).isDependencyToAll()
+        altered(s2, s1).isDependencyToAll()
+    }
+
+    def "knows added classes"() {
+        JarSnapshot s1 = snapshot(["A": "A".bytes, "B": "B".bytes, "C": "C".bytes], analysis)
+        JarSnapshot s2 = snapshot(["A": "A".bytes], analysis)
+        JarSnapshot s3 = snapshot([:], analysis)
+
+        expect:
+        s1.getAffectedClassesSince(s2).added == ["B", "C"] as Set
+        s2.getAffectedClassesSince(s1).added == [] as Set
+        s1.getAffectedClassesSince(s3).added == ["A", "B", "C"] as Set
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/AccessedFromPrivateClass.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/AccessedFromPrivateClass.java
new file mode 100644
index 0000000..6e2a18c
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/AccessedFromPrivateClass.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.compile.incremental.test;
+
+public class AccessedFromPrivateClass {
+
+    public String toString() {
+        return "foo";
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasNonPrivateConstants.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasNonPrivateConstants.java
new file mode 100644
index 0000000..bc79e66
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasNonPrivateConstants.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.tasks.compile.incremental.test;
+
+public class HasNonPrivateConstants extends UsedByNonPrivateConstantsClass {
+    final static int X = 1;
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPrivateConstants.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPrivateConstants.java
new file mode 100644
index 0000000..3327311
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPrivateConstants.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.tasks.compile.incremental.test;
+
+public class HasPrivateConstants {
+    private final static int X = 1;
+    private final static HasNonPrivateConstants C = new HasNonPrivateConstants();
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPublicConstants.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPublicConstants.java
new file mode 100644
index 0000000..ec6e059
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/HasPublicConstants.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.tasks.compile.incremental.test;
+
+public class HasPublicConstants {
+    public final static int X = 1;
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeClass.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeClass.java
new file mode 100644
index 0000000..94acdd7
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeClass.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.tasks.compile.incremental.test;
+
+import java.util.*;
+
+public class SomeClass {
+
+    List<Integer> field = new LinkedList<Integer>();
+
+    private Set<String> stuff(HashMap<String, String> map) {
+        System.out.println(new Foo());
+        return new HashSet<String>();
+    }
+
+    private class Foo {
+        public String toString() {
+            return "" + new AccessedFromPrivateClass();
+        }
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeOtherClass.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeOtherClass.java
new file mode 100644
index 0000000..dce29d8
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/SomeOtherClass.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.compile.incremental.test;
+
+public class SomeOtherClass extends SomeClass {
+
+    void foo() {
+        System.out.println(new YetAnotherClass());
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/UsedByNonPrivateConstantsClass.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/UsedByNonPrivateConstantsClass.java
new file mode 100644
index 0000000..c22f4c0
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/UsedByNonPrivateConstantsClass.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.tasks.compile.incremental.test;
+
+public class UsedByNonPrivateConstantsClass {
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/YetAnotherClass.java b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/YetAnotherClass.java
new file mode 100644
index 0000000..2612052
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/test/YetAnotherClass.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.tasks.compile.incremental.test;
+
+public class YetAnotherClass {
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy
new file mode 100644
index 0000000..bb40f3a
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile
+
+import org.junit.Before
+import org.junit.Test
+
+import static org.gradle.util.Matchers.isEmpty
+import static org.junit.Assert.*
+
+class CompileOptionsTest {
+    static final Map TEST_DEBUG_OPTION_MAP = [someDebugOption: 'someDebugOptionValue']
+    static final Map TEST_FORK_OPTION_MAP = [someForkOption: 'someForkOptionValue']
+    static final Map TEST_DEPEND_OPTION_MAP = [someDependOption: 'someDependOptionValue']
+
+    CompileOptions compileOptions
+
+    @Before public void setUp()  {
+        compileOptions = new CompileOptions()
+        compileOptions.debugOptions = [optionMap: {TEST_DEBUG_OPTION_MAP}] as DebugOptions
+        compileOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as ForkOptions
+    }
+
+    @Test public void testCompileOptions() {
+        assertTrue(compileOptions.debug)
+        assertTrue(compileOptions.failOnError)
+        assertTrue(compileOptions.warnings)
+
+        assertFalse(compileOptions.deprecation)
+        assertFalse(compileOptions.listFiles)
+        assertFalse(compileOptions.verbose)
+        assertFalse(compileOptions.fork)
+
+        assertThat(compileOptions.compilerArgs, isEmpty())
+        assertNull(compileOptions.encoding)
+        assertNull(compileOptions.bootClasspath)
+        assertNull(compileOptions.extensionDirs)
+
+        assertNotNull(compileOptions.forkOptions)
+        assertNotNull(compileOptions.debugOptions)
+    }
+
+    @Test public void testOptionMapForDebugOptions() {
+        Map optionMap = compileOptions.optionMap()
+        assertEquals(optionMap.subMap(TEST_DEBUG_OPTION_MAP.keySet()), TEST_DEBUG_OPTION_MAP)
+        assertEquals(optionMap.subMap(TEST_FORK_OPTION_MAP.keySet()), TEST_FORK_OPTION_MAP)
+    }
+
+    @Test public void testOptionMapWithNullables() {
+        Map optionMap = compileOptions.optionMap()
+        Map nullables = [
+                encoding: 'encoding',
+                bootClasspath: 'bootClasspath',
+                extensionDirs: 'extdirs'
+        ]
+        nullables.each {String field, String antProperty ->
+            assertFalse(optionMap.keySet().contains(antProperty))
+        }
+
+        nullables.keySet().each {compileOptions."$it" = "${it}Value"}
+        optionMap = compileOptions.optionMap()
+        nullables.each {String field, String antProperty ->
+            assertEquals("${field}Value" as String, optionMap[antProperty])
+        }
+    }
+
+    @Test public void testOptionMapWithTrueFalseValues() {
+        Map booleans = [
+                failOnError: 'failOnError',
+                verbose: 'verbose',
+                listFiles: 'listFiles',
+                deprecation: 'deprecation',
+                warnings: 'nowarn',
+                debug: 'debug'
+        ]
+        booleans.keySet().each {compileOptions."$it" = true}
+        Map optionMap = compileOptions.optionMap()
+        booleans.values().each {
+            if (it.equals('nowarn')) {
+                assertEquals(false, optionMap[it])
+            } else {
+                assertEquals(true, optionMap[it])
+            }
+        }
+        booleans.keySet().each {compileOptions."$it" = false}
+        optionMap = compileOptions.optionMap()
+        booleans.values().each {
+            if (it.equals('nowarn')) {
+                assertEquals(true, optionMap[it])
+            } else {
+                assertEquals(false, optionMap[it])
+            }
+        }
+    }
+
+    @Test public void testWithExcludeFieldsFromOptionMap() {
+      compileOptions.compilerArgs = [[value: 'something']]
+        Map optionMap = compileOptions.optionMap()
+        ['debugOptions', 'forkOptions', 'compilerArgs'].each {
+            assertFalse(optionMap.containsKey(it))
+        }
+    }
+
+    @Test public void testFork() {
+        compileOptions.fork = false
+        boolean forkUseCalled = false
+        compileOptions.forkOptions = [define: {Map args ->
+            forkUseCalled = true
+            assertEquals(TEST_FORK_OPTION_MAP, args)
+        }] as ForkOptions
+        assert compileOptions.fork(TEST_FORK_OPTION_MAP).is(compileOptions)
+        assertTrue(compileOptions.fork)
+        assertTrue(forkUseCalled)
+    }
+
+    @Test public void testDebug() {
+        compileOptions.debug = false
+        boolean debugUseCalled = false
+        compileOptions.debugOptions = [define: {Map args ->
+            debugUseCalled = true
+            assertEquals(TEST_DEBUG_OPTION_MAP, args)
+        }] as DebugOptions
+        assert compileOptions.debug(TEST_DEBUG_OPTION_MAP).is(compileOptions)
+        assertTrue(compileOptions.debug)
+        assertTrue(debugUseCalled)
+    }
+
+    @Test public void testDepend() {
+        compileOptions.useDepend = false
+        boolean dependUseCalled = false
+        compileOptions.dependOptions = [define: {Map args ->
+            dependUseCalled = true
+            assertEquals(TEST_DEPEND_OPTION_MAP, args)
+        }] as DependOptions
+        assert compileOptions.depend(TEST_DEPEND_OPTION_MAP).is(compileOptions)
+        assertTrue(compileOptions.useDepend)
+        assertTrue(dependUseCalled)
+    }
+
+    @Test public void testDefine() {
+        compileOptions.debug = false
+        compileOptions.bootClasspath = 'xxxx'
+        compileOptions.fork = false
+        compileOptions.useDepend = false
+        compileOptions.define(debug: true, bootClasspath: null)
+        assertTrue(compileOptions.debug)
+        assertNull(compileOptions.bootClasspath)
+        assertFalse(compileOptions.fork)
+        assertFalse(compileOptions.useDepend)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/DebugOptionsTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/DebugOptionsTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/DebugOptionsTest.groovy
rename to subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/DebugOptionsTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/ForkOptionsTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/ForkOptionsTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/ForkOptionsTest.groovy
rename to subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/ForkOptionsTest.groovy
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.groovy
new file mode 100644
index 0000000..6ce2e0d
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile
+
+import org.gradle.api.internal.TaskExecutionHistory
+import org.gradle.api.tasks.WorkResult
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.platform.base.internal.toolchain.ResolvedTool
+import org.gradle.platform.base.internal.toolchain.ToolResolver
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class JavaCompileTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def toolResolver = Mock(ToolResolver)
+    def resolvedTool = Mock(ResolvedTool)
+    def compiler = Mock(Compiler)
+    def task = TestUtil.createTask(JavaCompile)
+
+    def "uses specified ToolResolver to create a Compiler to do the work"() {
+        given:
+        task.outputs.history = Stub(TaskExecutionHistory)
+        task.destinationDir = tmpDir.file("classes")
+        task.toolResolver = toolResolver
+
+        when:
+        task.compile()
+
+        then:
+        1 * toolResolver.resolveCompiler(_, {!null}) >> resolvedTool
+        1 * resolvedTool.get() >> compiler
+        1 * compiler.execute(!null) >> Stub(WorkResult)
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.groovy
new file mode 100644
index 0000000..57361e9
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.javadoc
+
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.external.javadoc.StandardJavadocDocletOptions
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.platform.base.internal.toolchain.ResolvedTool
+import org.gradle.platform.base.internal.toolchain.ToolResolver
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.GFileUtils
+import org.gradle.util.TestUtil
+import org.gradle.util.WrapUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertThat
+
+public class JavadocTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def testDir = tmpDir.getTestDirectory()
+    def destDir = new File(testDir, "dest");
+    def srcDir = new File(testDir, "srcdir");
+    def classpath = WrapUtil.toSet(new File("classpath"));
+    def toolResolver = Mock(ToolResolver)
+    def resolvedTool = Mock(ResolvedTool)
+    def generator = Mock(Compiler);
+    def configurationMock = new SimpleFileCollection(classpath);
+    def executable = "somepath";
+    private Javadoc task = TestUtil.createTask(Javadoc)
+
+    public void setup() {
+        task.setClasspath(configurationMock);
+        task.setExecutable(executable);
+        task.setToolResolver(toolResolver);
+        GFileUtils.touch(new File(srcDir, "file.java"));
+    }
+
+    def defaultExecution() {
+        when:
+        task.setDestinationDir(destDir);
+        task.source(srcDir);
+
+        and:
+        task.execute();
+
+        then:
+        1 * toolResolver.resolveCompiler(_,_) >> resolvedTool
+        1 * resolvedTool.get() >> generator
+        1 * generator.execute(_)
+    }
+
+    def executionWithOptionalAttributes() {
+        when:
+        task.setDestinationDir(destDir);
+        task.source(srcDir);
+        task.setMaxMemory("max-memory");
+        task.setVerbose(true);
+
+        and:
+        task.execute()
+
+        then:
+        1 * toolResolver.resolveCompiler(_,_) >> resolvedTool
+        1 * resolvedTool.get() >> generator
+        1 * generator.execute(_)
+    }
+
+    def setsTheWindowAndDocTitleIfNotSet() {
+        when:
+        task.setDestinationDir(destDir);
+        task.source(srcDir);
+        task.setTitle("title");
+
+        task.execute();
+
+        then:
+        1 * toolResolver.resolveCompiler(_,_) >> resolvedTool
+        1 * resolvedTool.get() >> generator
+        1 * generator.execute(_)
+
+        and:
+        StandardJavadocDocletOptions options = (StandardJavadocDocletOptions) task.getOptions();
+        assertThat(options.getDocTitle(), equalTo("title"));
+        assertThat(options.getWindowTitle(), equalTo("title"));
+    }
+}
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
new file mode 100644
index 0000000..142eecc
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc;
+
+import org.gradle.external.javadoc.internal.GroupsJavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.JavadocOptionFile;
+import org.gradle.external.javadoc.internal.LinksOfflineJavadocOptionFileOption;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+public class StandardJavadocDocletOptionsTest {
+
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private StandardJavadocDocletOptions options;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+
+        options = new StandardJavadocDocletOptions();
+    }
+
+    @Test
+    public void testDefaults() {
+        // core javadoc options
+        assertNull(options.getOverview());
+        assertNull(options.getMemberLevel());
+        assertNull(options.getDoclet());
+        assertEmpty(options.getDocletpath());
+        assertNull(options.getSource());
+        assertEmpty(options.getClasspath());
+        assertEmpty(options.getBootClasspath());
+        assertEmpty(options.getExtDirs());
+        assertEquals(options.getOutputLevel(), JavadocOutputLevel.QUIET);
+        assertFalse(options.isBreakIterator());
+        assertNull(options.getLocale());
+        assertNull(options.getEncoding());
+        assertEmpty(options.getJFlags());
+        assertEmpty(options.getSourceNames());
+        assertEmpty(options.getOptionFiles());
+        // standard doclet options
+        assertNull(options.getDestinationDirectory());
+        assertFalse(options.isUse());
+        assertFalse(options.isVersion());
+        assertFalse(options.isAuthor());
+        assertFalse(options.isSplitIndex());
+        assertNull(options.getWindowTitle());
+        assertNull(options.getDocTitle());
+        assertNull(options.getFooter());
+        assertNull(options.getBottom());
+        assertEmpty(options.getLinks());
+        assertEmpty(options.getLinksOffline());
+        assertFalse(options.isLinkSource());
+        assertEmpty(options.getGroups());
+        assertFalse(options.isNoDeprecated());
+        assertFalse(options.isNoDeprecatedList());
+        assertFalse(options.isNoSince());
+        assertFalse(options.isNoTree());
+        assertFalse(options.isNoIndex());
+        assertFalse(options.isNoHelp());
+        assertFalse(options.isNoNavBar());
+        assertNull(options.getHelpFile());
+        assertNull(options.getStylesheetFile());
+        assertFalse(options.isSerialWarn());
+        assertNull(options.getCharSet());
+        assertNull(options.getDocEncoding());
+        assertFalse(options.isKeyWords());
+        assertEmpty(options.getTags());
+        assertEmpty(options.getTaglets());
+        assertEmpty(options.getTagletPath());
+        assertFalse(options.isDocFilesSubDirs());
+        assertEmpty(options.getExcludeDocFilesSubDir());
+        assertEmpty(options.getNoQualifiers());
+        assertFalse(options.isNoTimestamp());
+        assertFalse(options.isNoComment());
+    }
+
+    @Test
+    public void testConstructor() {
+        final JavadocOptionFile optionFileMock = context.mock(JavadocOptionFile.class);
+
+        context.checking(new Expectations(){{
+            // core options
+            one(optionFileMock).addStringOption("overview");
+            one(optionFileMock).addEnumOption("memberLevel");
+            one(optionFileMock).addStringOption("doclet");
+            one(optionFileMock).addPathOption("docletclasspath");
+            one(optionFileMock).addStringOption("source");
+            one(optionFileMock).addPathOption("sourcepath");
+            one(optionFileMock).addPathOption("classpath");
+            one(optionFileMock).addStringsOption("subpackages", ";");
+            one(optionFileMock).addStringsOption("exclude", ":");
+            one(optionFileMock).addPathOption("bootclasspath");
+            one(optionFileMock).addPathOption("extdirs");
+            one(optionFileMock).addEnumOption("outputLevel", JavadocOutputLevel.QUIET);
+            one(optionFileMock).addBooleanOption("breakiterator");
+            one(optionFileMock).addStringOption("locale");
+            one(optionFileMock).addStringOption("encoding");
+            // standard doclet options
+            one(optionFileMock).addFileOption("d");
+            one(optionFileMock).addBooleanOption("use");
+            one(optionFileMock).addBooleanOption("version");
+            one(optionFileMock).addBooleanOption("author");
+            one(optionFileMock).addBooleanOption("splitindex");
+            one(optionFileMock).addStringOption("windowtitle");
+            one(optionFileMock).addStringOption("doctitle");
+            one(optionFileMock).addStringOption("footer");
+            one(optionFileMock).addStringOption("bottom");
+            one(optionFileMock).addStringOption("link");
+            allowing(optionFileMock).addOption(new LinksOfflineJavadocOptionFileOption("linkoffline"));
+            one(optionFileMock).addBooleanOption("linksource");
+            one(optionFileMock).addOption(new GroupsJavadocOptionFileOption("group"));
+            one(optionFileMock).addBooleanOption("nodeprecated");
+            one(optionFileMock).addBooleanOption("nodeprecatedlist");
+            one(optionFileMock).addBooleanOption("nosince");
+            one(optionFileMock).addBooleanOption("notree");
+            one(optionFileMock).addBooleanOption("noindex");
+            one(optionFileMock).addBooleanOption("nohelp");
+            one(optionFileMock).addBooleanOption("nonavbar");
+            one(optionFileMock).addFileOption("helpfile");
+            one(optionFileMock).addFileOption("stylesheetfile");
+            one(optionFileMock).addBooleanOption("serialwarn");
+            one(optionFileMock).addStringOption("charset");
+            one(optionFileMock).addStringOption("docencoding");
+            one(optionFileMock).addBooleanOption("keywords");
+            one(optionFileMock).addStringOption("tags");
+            one(optionFileMock).addStringOption("taglets");
+            one(optionFileMock).addPathOption("tagletpath");
+            one(optionFileMock).addBooleanOption("docfilessubdirs");
+            one(optionFileMock).addStringsOption("excludedocfilessubdir", ":");
+            one(optionFileMock).addStringsOption("noqualifier", ":");
+            one(optionFileMock).addBooleanOption("notimestamp");
+            one(optionFileMock).addBooleanOption("nocomment");
+        }});
+
+        options = new StandardJavadocDocletOptions();
+    }
+
+    @Test
+    public void testFluentOverview() {
+        final String overviewValue = "overview";
+        assertEquals(options, options.overview(overviewValue));
+        assertEquals(overviewValue, options.getOverview());
+    }
+
+    @Test
+    public void testShowAll() {
+        assertEquals(options, options.showAll());
+        assertEquals(JavadocMemberLevel.PRIVATE, options.getMemberLevel());
+    }
+
+    @Test
+    public void testShowFromPublic() {
+        assertEquals(options, options.showFromPublic());
+        assertEquals(JavadocMemberLevel.PUBLIC, options.getMemberLevel());
+    }
+
+    @Test
+    public void testShowFromPackage() {
+        assertEquals(options, options.showFromPackage());
+        assertEquals(JavadocMemberLevel.PACKAGE, options.getMemberLevel());
+    }
+
+    @Test
+    public void testShowFromProtected() {
+        assertEquals(options, options.showFromProtected());
+        assertEquals(JavadocMemberLevel.PROTECTED, options.getMemberLevel());
+    }
+
+    @Test
+    public void testShowFromPrivate() {
+        assertEquals(options, options.showFromPrivate());
+        assertEquals(JavadocMemberLevel.PRIVATE, options.getMemberLevel());
+    }
+
+    @Test
+    public void testFluentDocletClass() {
+        final String docletValue = "org.gradle.CustomDocletClass";
+        assertEquals(options, options.doclet(docletValue));
+        assertEquals(docletValue, options.getDoclet());
+    }
+
+    @Test
+    public void testFluentDocletClasspath() {
+        final File[] docletClasspathValue = new File[]{new File("doclet.jar"), new File("doclet-dep.jar")};
+        assertEquals(options, options.docletpath(docletClasspathValue));
+        assertArrayEquals(docletClasspathValue, options.getDocletpath().toArray());
+    }
+
+    @Test
+    public void testFluentSource() {
+        final String sourceValue = "1.5";
+        assertEquals(options, options.source(sourceValue));
+        assertEquals(sourceValue, options.getSource());
+    }
+
+    @Test
+    public void testFluentClasspath() {
+        final File[] classpathValue = new File[]{new File("classpath.jar"), new File("classpath-dir")};
+        assertEquals(options, options.classpath(classpathValue));
+        assertArrayEquals(classpathValue, options.getClasspath().toArray());
+    }
+
+    @Test
+    public void testFluentBootclasspath() {
+        final File[] bootClasspathValue = new File[]{new File("bootclasspath.jar"), new File("bootclasspath2.jar")};
+        assertEquals(options, options.bootClasspath(bootClasspathValue));
+        assertArrayEquals(bootClasspathValue, options.getBootClasspath().toArray());
+    }
+
+    @Test
+    public void testFluentExtDirs() {
+        final File[] extDirsValue = new File[]{new File("extDirOne"), new File("extDirTwo")};
+        assertEquals(options, options.extDirs(extDirsValue));
+        assertArrayEquals(extDirsValue, options.getExtDirs().toArray());
+    }
+
+    @Test
+    public void testQuietOutputLevel() {
+        assertEquals(options, options.quiet());
+        assertEquals(JavadocOutputLevel.QUIET, options.getOutputLevel());
+    }
+
+    @Test
+    public void testVerboseOutputLevel() {
+        assertEquals(options, options.verbose());
+        assertEquals(JavadocOutputLevel.VERBOSE, options.getOutputLevel());
+        assertTrue(options.isVerbose());
+    }
+
+    @Test
+    public void testFluentBreakIterator() {
+        assertEquals(options, options.breakIterator());
+        assertTrue(options.isBreakIterator());
+    }
+
+    @Test
+    public void testFluentLocale() {
+        final String localeValue = "nl";
+        assertEquals(options, options.locale(localeValue));
+        assertEquals(localeValue, options.getLocale());
+    }
+
+    @Test
+    public void testFluentEncoding() {
+        final String encodingValue = "UTF-8";
+        assertEquals(options, options.encoding(encodingValue));
+        assertEquals(encodingValue, options.getEncoding());
+    }
+
+    @Test
+    public void testFluentDirectory() {
+        final File directoryValue = new File("testOutput");
+        assertEquals(options, options.destinationDirectory(directoryValue));
+        assertEquals(directoryValue, options.getDestinationDirectory());
+    }
+
+    @Test
+    public void testFluentUse() {
+        assertEquals(options, options.use());
+        assertTrue(options.isUse());
+    }
+
+    @Test
+    public void testFluentVersion() {
+        assertEquals(options, options.version());
+        assertTrue(options.isVersion());
+    }
+
+    @Test
+    public void testFluentAuthor() {
+        assertEquals(options, options.author());
+        assertTrue(options.isAuthor());
+    }
+
+    @Test
+    public void testFluentSplitIndex() {
+        assertEquals(options, options.splitIndex());
+        assertTrue(options.isSplitIndex());
+    }
+
+    @Test
+    public void testFluentWindowTitle() {
+        final String windowTitleValue = "windowTitleValue";
+        assertEquals(options, options.windowTitle(windowTitleValue));
+        assertEquals(windowTitleValue, options.getWindowTitle());
+    }
+
+    @Test
+    public void testFluentDocTitle() {
+        final String docTitleValue = "docTitleValue";
+        assertEquals(options, options.docTitle(docTitleValue));
+        assertEquals(docTitleValue, options.getDocTitle());
+    }
+
+    @Test
+    public void testFluentFooter() {
+        final String footerValue = "footerValue";
+        assertEquals(options, options.footer(footerValue));
+        assertEquals(footerValue, options.getFooter());
+    }
+
+    @Test
+    public void testFluentBottom() {
+        final String bottomValue = "bottomValue";
+        assertEquals(options, options.bottom(bottomValue));
+        assertEquals(bottomValue, options.getBottom());
+    }
+
+    @Test
+    public void testFluentLink() {
+        final String[] linkValue = new String[]{"http://otherdomain.org/javadoc"};
+        assertEquals(options, options.links(linkValue));
+        assertArrayEquals(linkValue, options.getLinks().toArray());
+    }
+
+    @Test
+    public void testFluentLinkOffline() {
+        final String extDocUrl = "http://otherdomain.org/javadoc";
+        final String packageListLoc = "/home/someuser/used-lib-local-javadoc-list";
+        assertEquals(options, options.linksOffline(extDocUrl, packageListLoc));
+        assertEquals(extDocUrl, options.getLinksOffline().get(0).getExtDocUrl());
+        assertEquals(packageListLoc, options.getLinksOffline().get(0).getPackagelistLoc());
+    }
+
+    @Test
+    public void testFluentLinkSource() {
+        assertEquals(options, options.linkSource());
+        assertTrue(options.isLinkSource());
+    }
+
+    @Test
+    public void testFluentGroup() {
+        final String groupOneName = "groupOneName";
+        final String[] groupOnePackages = new String[]{"java.lang", "java.io"};
+
+        final String groupTwoName = "gradle";
+        final String[] groupTwoPackages = new String[]{"org.gradle"};
+
+        assertEquals(options, options.group(groupOneName, groupOnePackages));
+        assertEquals(options, options.group(groupTwoName, groupTwoPackages));
+        assertEquals(2, options.getGroups().size());
+        assertArrayEquals(groupOnePackages, options.getGroups().get(groupOneName).toArray());
+        assertArrayEquals(groupTwoPackages, options.getGroups().get(groupTwoName).toArray());
+    }
+
+    @Test
+    public void testFluentNoDeprecated() {
+        assertEquals(options, options.noDeprecated());
+        assertTrue(options.isNoDeprecated());
+    }
+
+    @Test
+    public void testFluentNoDeprecatedList() {
+        assertEquals(options, options.noDeprecatedList());
+        assertTrue(options.isNoDeprecatedList());
+    }
+
+    @Test
+    public void testFluentNoSince() {
+        assertEquals(options, options.noSince());
+        assertTrue(options.isNoSince());
+    }
+
+    @Test
+    public void testFluentNoTree() {
+        assertEquals(options, options.noTree());
+        assertTrue(options.isNoTree());
+    }
+
+    @Test
+    public void testFluentNoIndex() {
+        assertEquals(options, options.noIndex());
+        assertTrue(options.isNoIndex());
+    }
+
+    @Test
+    public void testFluentNoNavBar() {
+        assertEquals(options, options.noNavBar());
+        assertTrue(options.isNoNavBar());
+    }
+
+    @Test
+    public void testFluentHelpFile() {
+        final File helpFileValue = new File("help-file.txt");
+        assertEquals(options, options.helpFile(helpFileValue));
+        assertEquals(helpFileValue, options.getHelpFile());
+    }
+
+    @Test
+    public void testFluentStylesheetFile() {
+        final File stylesheetFileValue = new File("stylesheet.css");
+        assertEquals(options, options.stylesheetFile(stylesheetFileValue));
+        assertEquals(stylesheetFileValue, options.getStylesheetFile());
+    }
+
+    @Test
+    public void testFluentSerialWarn() {
+        assertEquals(options, options.serialWarn());
+        assertTrue(options.isSerialWarn());
+    }
+
+    @Test
+    public void testFluentCharset() {
+        final String charsetValue = "dummy-charset";
+        assertEquals(options, options.charSet(charsetValue));
+        assertEquals(charsetValue, options.getCharSet());
+    }
+
+    @Test
+    public void testFluentDocEncoding() {
+        final String docEncodingValue = "UTF-16";
+        assertEquals(options, options.docEncoding(docEncodingValue));
+        assertEquals(docEncodingValue, options.getDocEncoding());
+    }
+
+    @Test
+    public void testFluentKeywords() {
+        assertEquals(options, options.keyWords());
+        assertTrue(options.isKeyWords());
+    }
+
+    @Test
+    public void testFluentTags() {
+        final String[] tagsValue = new String[]{"param", "return", "todo:a:\"To Do:\""};
+
+        final List<String> tempList = new ArrayList<String>();
+        tempList.addAll(Arrays.asList(tagsValue));
+
+        final Object[] totalTagsValue = tempList.toArray();
+        assertEquals(options, options.tags(tagsValue));
+        assertArrayEquals(totalTagsValue, options.getTags().toArray());
+    }
+
+    @Test
+    public void testFluentTaglets() {
+        final String[] tagletsValue = new String[]{"com.sun.tools.doclets.ToDoTaglet"};
+
+        final List<String> tempList = new ArrayList<String>();
+        tempList.addAll(Arrays.asList(tagletsValue));
+
+        final Object[] totalTagletsValue = tempList.toArray();
+        assertEquals(options, options.taglets(tagletsValue));
+        assertArrayEquals(totalTagletsValue, options.getTaglets().toArray());
+    }
+
+    @Test
+    public void testFluentTagletPath() {
+        final File[] tagletPathValue = new File[]{new File("tagletOne.jar"), new File("tagletTwo.jar")};
+        assertEquals(options, options.tagletPath(tagletPathValue));
+        assertArrayEquals(tagletPathValue, options.getTagletPath().toArray());
+    }
+
+    @Test
+    public void testFluentDocFilesSubDirs() {
+        assertEquals(options, options.docFilesSubDirs());
+        assertTrue(options.isDocFilesSubDirs());
+    }
+
+    @Test
+    public void testFluentExcludeDocFilesSubDir() {
+        final String[] excludeDocFilesSubDirValue = new String[]{".hg", ".svn", ".bzr", ".git"};
+        assertEquals(options, options.excludeDocFilesSubDir(excludeDocFilesSubDirValue));
+        assertArrayEquals(excludeDocFilesSubDirValue, options.getExcludeDocFilesSubDir().toArray());
+    }
+
+    @Test
+    public void testFluentNoQualifier() {
+        String[] noQualifierValue = new String[]{"java.lang", "java.io"};
+        assertEquals(options, options.noQualifiers(noQualifierValue));
+        assertArrayEquals(noQualifierValue, options.getNoQualifiers().toArray());
+    }
+
+    @Test
+    public void testFluentNoTimestamp() {
+        assertEquals(options, options.noTimestamp());
+        assertTrue(options.isNoTimestamp());
+    }
+
+    @Test
+    public void testFluentNoComment() {
+        assertEquals(options, options.noComment());
+        assertTrue(options.isNoComment());
+    }
+
+    @After
+    public void tearDown() {
+        options = null;
+    }
+
+    public static void assertEmpty(Collection<?> shouldBeEmptyCollection) {
+        assertNotNull(shouldBeEmptyCollection);
+        assertTrue(shouldBeEmptyCollection.isEmpty());
+    }
+
+    public static void assertEmpty(Map<?, ?> shouldBeEmptyMap) {
+        assertNotNull(shouldBeEmptyMap);
+        assertTrue(shouldBeEmptyMap.isEmpty());
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.groovy
new file mode 100644
index 0000000..0821c54
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal
+
+import org.gradle.internal.SystemProperties
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+import spock.lang.Subject
+
+public class JavadocOptionFileWriterContextTest extends Specification {
+
+    def writer = new StringWriter()
+    @Subject context = new JavadocOptionFileWriterContext(writer)
+
+    def "writes"() {
+        when: context.write("dummy")
+        then: writer.toString() == "dummy"
+    }
+
+    def "writes new line"() {
+        when: context.write("a").newLine().write("b")
+        then: writer.toString() == "a${SystemProperties.instance.getLineSeparator()}b"
+    }
+
+    def "quotes and escapes"() {
+        when: context.writeValueOption("key", "1\\2\\")
+        then: writer.toString() == "-key '1\\\\2\\\\'${SystemProperties.instance.getLineSeparator()}"
+    }
+
+    def "quotes and escapes multiple values"() {
+        when:
+        context.writeValuesOption("key", WrapUtil.toList("a\\b", "c"), ":")
+
+        then: writer.toString() == "-key 'a\\\\b:c'${SystemProperties.instance.getLineSeparator()}"
+    }
+
+    def "writes multiline value"() {
+        when:
+        context.writeValueOption("key", "Hey${SystemProperties.instance.getLineSeparator()}Joe!")
+
+        then: writer.toString() == "-key 'Hey\\${SystemProperties.instance.getLineSeparator()}Joe!'${SystemProperties.instance.getLineSeparator()}"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterTest.groovy
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java b/subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java
rename to subprojects/language-java/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/language/java/plugins/JavaLanguagePluginTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/language/java/plugins/JavaLanguagePluginTest.groovy
new file mode 100644
index 0000000..7171b1e
--- /dev/null
+++ b/subprojects/language-java/src/test/groovy/org/gradle/language/java/plugins/JavaLanguagePluginTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.java.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.java.JavaSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class JavaLanguagePluginTest extends AbstractLanguagePluginSpec {
+
+    @Override
+    Class<Plugin> getPluginClass() {
+        return JavaLanguagePlugin
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getLanguageSourceSet() {
+        return JavaSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "java"
+    }
+}
diff --git a/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/BadJavaComponent.groovy b/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/BadJavaComponent.groovy
new file mode 100644
index 0000000..b9b7e63
--- /dev/null
+++ b/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/BadJavaComponent.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.language.fixtures
+
+import org.gradle.integtests.fixtures.jvm.JvmSourceFile
+
+class BadJavaComponent extends TestJavaComponent {
+    List<JvmSourceFile> sources = [
+        new JvmSourceFile("compile/test", "Person.java", '''
+package compile.test;
+
+import java.util.Arrays;
+
+public class Person {
+    String name;
+    int age;
+
+    void hello() {
+        return nothing
+    }
+}'''),
+        new JvmSourceFile("compile/test", "Person2.java", '''
+Not a java source file at all...
+''')
+    ]
+
+    List<String> compilerErrors = [
+            "';' expected",
+            "class, interface, or enum expected"
+    ]
+}
diff --git a/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/TestJavaComponent.groovy b/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/TestJavaComponent.groovy
new file mode 100644
index 0000000..6b248bd
--- /dev/null
+++ b/subprojects/language-java/src/testFixtures/groovy/org/gradle/language/fixtures/TestJavaComponent.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures
+
+import org.gradle.integtests.fixtures.jvm.IncrementalTestJvmComponent
+import org.gradle.integtests.fixtures.jvm.JvmSourceFile
+import org.gradle.language.java.JavaSourceSet
+import org.gradle.test.fixtures.file.TestFile
+
+class TestJavaComponent extends IncrementalTestJvmComponent{
+    String languageName = "java"
+    String sourceSetTypeName = JavaSourceSet.class.name
+
+    List<JvmSourceFile> sources = [
+        new JvmSourceFile("compile/test", "Person.java", '''
+package compile.test;
+
+import java.util.Arrays;
+
+public class Person {
+    String name;
+    int age;
+
+    void hello() {
+        Iterable<Integer> vars = Arrays.asList(3, 1, 2);
+    }
+}'''),
+        new JvmSourceFile("compile/test", "Person2.java", '''
+package compile.test;
+
+class Person2 {
+}
+''')
+    ]
+
+
+    @Override
+    void changeSources(List<TestFile> sourceFiles){
+        def personJavaFile = sourceFiles.find { it.name == "Person.java" }
+        personJavaFile.text = personJavaFile.text.replace("String name;", "String name; String anotherName;")
+    }
+
+    @Override
+    void writeAdditionalSources(TestFile sourceDir) {
+        sourceDir.file("java/Extra.java") << """
+interface Extra {
+    String whatever();
+}
+"""
+    }
+
+    List<JvmSourceFile> expectedOutputs = [
+            sources[0].classFile,
+            sources[1].classFile,
+            resources[0],
+            resources[1]
+    ]
+}
diff --git a/subprojects/language-jvm/language-jvm.gradle b/subprojects/language-jvm/language-jvm.gradle
index 19b6518..a7ec475 100644
--- a/subprojects/language-jvm/language-jvm.gradle
+++ b/subprojects/language-jvm/language-jvm.gradle
@@ -1,7 +1,16 @@
 dependencies {
-    compile libraries.groovy
-    compile project(":languageBase")
-    compile project(":core")
+    compile project(":platformJvm")
+    compile project(":platformBase")
+
+    testCompile libraries.groovy
+
+    testRuntime project(":languageJava")
+
+    testFixturesCompile project(":internalIntegTesting")
 }
 
 useClassycle()
+strictCompile()
+useTestFixtures()
+useTestFixtures(sourceSet: 'testFixtures')
+
diff --git a/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/JvmResourcesPluginIntegrationTest.groovy b/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/JvmResourcesPluginIntegrationTest.groovy
new file mode 100644
index 0000000..9d446ed
--- /dev/null
+++ b/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/JvmResourcesPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.jvm
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class JvmResourcesPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/ResourceOnlyJvmLibraryIntegrationTest.groovy b/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/ResourceOnlyJvmLibraryIntegrationTest.groovy
new file mode 100644
index 0000000..bcba3b2
--- /dev/null
+++ b/subprojects/language-jvm/src/integTest/groovy/org/gradle/language/jvm/ResourceOnlyJvmLibraryIntegrationTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.jvm
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+class ResourceOnlyJvmLibraryIntegrationTest extends AbstractIntegrationSpec {
+    def "can define a library containing resources only"() {
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'jvm-resources'
+}
+model {
+    components {
+        myLib(JvmLibrarySpec)
+    }
+}
+
+task validate << {
+    def myLib = componentSpecs.myLib
+    assert myLib instanceof JvmLibrarySpec
+
+    assert myLib.sources.size() == 1
+    assert myLib.sources.resources instanceof JvmResourceSet
+
+    assert sources as Set == myLib.sources as Set
+
+    binaries.withType(JarBinarySpec) { jvmBinary ->
+        assert jvmBinary.source == myLib.source
+    }
+}
+"""
+
+        expect:
+        run 'validate'
+    }
+
+    def "can build a library containing resources only"() {
+        file("src/myLib/resources/org/gradle/thing.txt") << "hi"
+
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'jvm-resources'
+}
+model {
+    components {
+        myLib(JvmLibrarySpec)
+    }
+}
+"""
+
+        when:
+        run 'assemble'
+
+        then:
+        def jar = jarFile('build/jars/myLibJar/myLib.jar')
+        jar.hasDescendants("org/gradle/thing.txt")
+        jar.assertFilePresent("org/gradle/thing.txt", "hi")
+    }
+
+    def "generated binary includes resources from all resource sets"() {
+        file("src/myLib/resources/thing.txt") << "hi"
+        file("src/myLib/other/org/gradle/thing.txt") << "hi"
+
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'jvm-resources'
+}
+model {
+    components {
+        myLib(JvmLibrarySpec) {
+            sources {
+                other(JvmResourceSet)
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run 'assemble'
+
+        then:
+        def jar = jarFile('build/jars/myLibJar/myLib.jar')
+        jar.hasDescendants("thing.txt", "org/gradle/thing.txt")
+        jar.assertFilePresent("thing.txt", "hi")
+        jar.assertFilePresent("org/gradle/thing.txt", "hi")
+    }
+
+    private JarTestFixture jarFile(String s) {
+        new JarTestFixture(file(s))
+    }
+}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/JavaSourceSet.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/JavaSourceSet.java
deleted file mode 100644
index 1b698a1..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/JavaSourceSet.java
+++ /dev/null
@@ -1,28 +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.language.java;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.jvm.Classpath;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * A set of sources passed to the Java compiler.
- */
- at Incubating
-public interface JavaSourceSet extends LanguageSourceSet {
-    Classpath getCompileClasspath();
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/internal/DefaultJavaSourceSet.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/internal/DefaultJavaSourceSet.java
deleted file mode 100644
index 1d52355..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/java/internal/DefaultJavaSourceSet.java
+++ /dev/null
@@ -1,52 +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.language.java.internal;
-
-import org.gradle.api.Task;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.tasks.TaskDependency;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.internal.AbstractLanguageSourceSet;
-import org.gradle.language.java.JavaSourceSet;
-import org.gradle.language.jvm.Classpath;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class DefaultJavaSourceSet extends AbstractLanguageSourceSet implements JavaSourceSet {
-    private final Classpath compileClasspath;
-
-    public DefaultJavaSourceSet(String name, SourceDirectorySet source, Classpath compileClasspath, FunctionalSourceSet parent) {
-        super(name, parent, "Java source", source);
-        this.compileClasspath = compileClasspath;
-    }
-
-    public Classpath getCompileClasspath() {
-        return compileClasspath;
-    }
-
-
-    public TaskDependency getBuildDependencies() {
-        return new TaskDependency() {
-            public Set<? extends Task> getDependencies(Task task) {
-                Set<Task> dependencies = new HashSet<Task>();
-                dependencies.addAll(compileClasspath.getBuildDependencies().getDependencies(task));
-                dependencies.addAll(getSource().getBuildDependencies().getDependencies(task));
-                return dependencies;
-            }
-        };
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ClassDirectoryBinary.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ClassDirectoryBinary.java
deleted file mode 100644
index e8b852b..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ClassDirectoryBinary.java
+++ /dev/null
@@ -1,40 +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.language.jvm;
-
-import org.gradle.api.DomainObjectCollection;
-import org.gradle.api.Incubating;
-import org.gradle.language.base.Binary;
-import org.gradle.language.base.LanguageSourceSet;
-
-import java.io.File;
-
-/**
- * An exploded binary containing resources and compiled class files.
- */
-// TODO: maybe we need to allow additional dirs like SourceSetOutput does
-// (esp. for backwards compatibility). Wonder if it's still necessary to distinguish
-// between classes and resources dirs, instead of just maintaining a collection of dirs.
-// As far as generated resources are concerned, it might be better to model
-// them as an additional (Buildable) ResourceSet.
- at Incubating
-public interface ClassDirectoryBinary extends Binary {
-    File getClassesDir();
-    void setClassesDir(File dir);
-    File getResourcesDir();
-    void setResourcesDir(File dir);
-    DomainObjectCollection<LanguageSourceSet> getSource();
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/Classpath.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/Classpath.java
deleted file mode 100644
index 3603ba3..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/Classpath.java
+++ /dev/null
@@ -1,28 +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.language.jvm;
-
-import org.gradle.api.Buildable;
-import org.gradle.api.Incubating;
-import org.gradle.api.file.FileCollection;
-
-/**
- * A collection of files to be used as a class path.
- */
- at Incubating
-public interface Classpath extends Buildable {
-    FileCollection getFiles();
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ResourceSet.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ResourceSet.java
deleted file mode 100644
index 8e6a489..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/ResourceSet.java
+++ /dev/null
@@ -1,25 +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.language.jvm;
-
-import org.gradle.api.Incubating;
-import org.gradle.language.base.LanguageSourceSet;
-
-/**
- * A set of resource files.
- */
- at Incubating
-public interface ResourceSet extends LanguageSourceSet {}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingScheme.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingScheme.java
deleted file mode 100644
index e7fd064..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingScheme.java
+++ /dev/null
@@ -1,70 +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.language.jvm.internal;
-
-import org.gradle.api.Nullable;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.util.GUtil;
-
-import java.util.Collections;
-import java.util.List;
-
-class ClassDirectoryBinaryNamingScheme implements BinaryNamingScheme {
-    private final String baseName;
-    private final String collapsedName;
-
-    public ClassDirectoryBinaryNamingScheme(String baseName) {
-        this.baseName = baseName;
-        this.collapsedName = collapseMain(this.baseName);
-    }
-
-    private static String collapseMain(String name) {
-        return name.equals("main") ? "" : name;
-    }
-
-    public String getDescription() {
-        return String.format("classes '%s'", baseName);
-    }
-
-    public List<String> getVariantDimensions() {
-        return Collections.emptyList();
-    }
-
-    public String getLifecycleTaskName() {
-        return getTaskName(null, "classes");
-    }
-
-    public String getTaskName(@Nullable String verb) {
-        return getTaskName(verb, null);
-    }
-
-    public String getTaskName(@Nullable String verb, @Nullable String target) {
-        String name = baseName;
-        if (target != null) {
-            name = collapsedName;
-        }
-        return GUtil.toLowerCamelCase(String.format("%s %s %s", nullToEmpty(verb), name, nullToEmpty(target)));
-    }
-
-    private String nullToEmpty(String input) {
-        return input == null ? "" : input;
-    }
-
-    public String getOutputDirectoryBase() {
-        return baseName;
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinary.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinary.java
deleted file mode 100644
index c7485c0..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinary.java
+++ /dev/null
@@ -1,83 +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.language.jvm.internal;
-
-import org.gradle.api.DomainObjectCollection;
-import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.language.base.LanguageSourceSet;
-import org.gradle.language.base.internal.AbstractBuildableModelElement;
-import org.gradle.language.base.internal.BinaryInternal;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.jvm.ClassDirectoryBinary;
-
-import java.io.File;
-
-public class DefaultClassDirectoryBinary extends AbstractBuildableModelElement implements ClassDirectoryBinary, BinaryInternal {
-    private final BinaryNamingScheme namingScheme;
-    private final DomainObjectCollection<LanguageSourceSet> source = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
-    private final String name;
-    private File classesDir;
-    private File resourcesDir;
-
-    public DefaultClassDirectoryBinary(String name) {
-        this.name = name;
-        this.namingScheme = new ClassDirectoryBinaryNamingScheme(removeClassesSuffix(name));
-    }
-
-    private String removeClassesSuffix(String name) {
-        if (name.endsWith("Classes")) {
-            return name.substring(0, name.length() - 7);
-        }
-        return name;
-    }
-
-    public BinaryNamingScheme getNamingScheme() {
-        return namingScheme;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public File getClassesDir() {
-        return classesDir;
-    }
-
-    public void setClassesDir(File classesDir) {
-        this.classesDir = classesDir;
-    }
-
-    public File getResourcesDir() {
-        return resourcesDir;
-    }
-
-    public void setResourcesDir(File resourcesDir) {
-        this.resourcesDir = resourcesDir;
-    }
-
-    public DomainObjectCollection<LanguageSourceSet> getSource() {
-        return source;
-    }
-
-    public String getDisplayName() {
-        return namingScheme.getDescription();
-    }
-
-    public String toString() {
-        return namingScheme.getDescription();
-    }
-
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClasspath.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClasspath.java
deleted file mode 100644
index 3c8385b..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultClasspath.java
+++ /dev/null
@@ -1,39 +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.language.jvm.internal;
-
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
-import org.gradle.api.internal.tasks.TaskResolver;
-import org.gradle.language.jvm.Classpath;
-import org.gradle.api.tasks.TaskDependency;
-
-public class DefaultClasspath implements Classpath {
-    private final FileCollection files;
-
-    public DefaultClasspath(FileResolver fileResolver, TaskResolver taskResolver) {
-        files = new DefaultConfigurableFileCollection(fileResolver, taskResolver);
-    }
-
-    public FileCollection getFiles() {
-        return files;
-    }
-
-    public TaskDependency getBuildDependencies() {
-        return files.getBuildDependencies();
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultResourceSet.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultResourceSet.java
deleted file mode 100644
index 8cc82ad..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/DefaultResourceSet.java
+++ /dev/null
@@ -1,28 +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.language.jvm.internal;
-
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.internal.AbstractLanguageSourceSet;
-import org.gradle.language.jvm.ResourceSet;
-
-public class DefaultResourceSet extends AbstractLanguageSourceSet implements ResourceSet {
-
-    public DefaultResourceSet(String name, SourceDirectorySet source, FunctionalSourceSet parent) {
-        super(name, parent, "resources", source);
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/SimpleStaleClassCleaner.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/SimpleStaleClassCleaner.java
deleted file mode 100644
index 297da22..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/SimpleStaleClassCleaner.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.language.jvm.internal;
-
-import org.gradle.api.internal.TaskOutputsInternal;
-
-import java.io.File;
-
-public class SimpleStaleClassCleaner extends StaleClassCleaner {
-    private final TaskOutputsInternal taskOutputs;
-    private boolean didWork;
-
-    public SimpleStaleClassCleaner(TaskOutputsInternal taskOutputs) {
-        this.taskOutputs = taskOutputs;
-    }
-
-    @Override
-    public void execute() {
-        String prefix = getDestinationDir().getAbsolutePath() + File.separator;
-        for (File f : taskOutputs.getPreviousFiles()) {
-            if (f.getAbsolutePath().startsWith(prefix)) {
-                didWork |= f.delete();
-            }
-        }
-    }
-
-    public boolean getDidWork() {
-        return didWork;
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/StaleClassCleaner.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/StaleClassCleaner.java
deleted file mode 100644
index e781dec..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/internal/StaleClassCleaner.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.language.jvm.internal;
-
-import org.gradle.api.file.FileCollection;
-
-import java.io.File;
-
-public abstract class StaleClassCleaner {
-    private File destinationDir;
-    FileCollection source;
-
-    public abstract void execute();
-
-    public FileCollection getSource() {
-        return source;
-    }
-
-    public void setSource(FileCollection source) {
-        this.source = source;
-    }
-
-    public void setDestinationDir(File destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
-    public File getDestinationDir() {
-        return destinationDir;
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/JvmLanguagePlugin.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/JvmLanguagePlugin.java
deleted file mode 100644
index 56c415f..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/JvmLanguagePlugin.java
+++ /dev/null
@@ -1,107 +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.language.jvm.plugins;
-
-import org.gradle.api.*;
-import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.tasks.Copy;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.language.base.internal.BinaryInternal;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.base.plugins.LanguageBasePlugin;
-import org.gradle.language.jvm.ClassDirectoryBinary;
-import org.gradle.language.jvm.ResourceSet;
-import org.gradle.language.jvm.internal.DefaultClassDirectoryBinary;
-import org.gradle.language.jvm.internal.DefaultResourceSet;
-import org.gradle.language.jvm.tasks.ProcessResources;
-
-import javax.inject.Inject;
-import java.io.File;
-import java.util.concurrent.Callable;
-
-/**
- * Base plugin for JVM language support. Applies the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
- * Registers the {@link org.gradle.language.jvm.ClassDirectoryBinary} element type for the {@link org.gradle.language.base.BinaryContainer}.
- * Adds a lifecycle task named {@code classes} for each {@link org.gradle.language.jvm.ClassDirectoryBinary}.
- * Registers the {@link org.gradle.language.jvm.ResourceSet} element type for each {@link org.gradle.language.base.FunctionalSourceSet} added to {@link org.gradle.language.base.ProjectSourceSet}.
- * Adds a {@link Copy} task named {@code processXYZResources} for each {@link org.gradle.language.jvm.ResourceSet} added to a {@link org.gradle.language.jvm.ClassDirectoryBinary}.
- */
- at Incubating
-public class JvmLanguagePlugin implements Plugin<Project> {
-    private final Instantiator instantiator;
-    private final FileResolver fileResolver;
-
-    @Inject
-    public JvmLanguagePlugin(Instantiator instantiator, FileResolver fileResolver) {
-        this.instantiator = instantiator;
-        this.fileResolver = fileResolver;
-    }
-
-    public void apply(final Project target) {
-        target.getPlugins().apply(LanguageBasePlugin.class);
-
-        ProjectSourceSet projectSourceSet = target.getExtensions().getByType(ProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(ResourceSet.class, new NamedDomainObjectFactory<ResourceSet>() {
-                    public ResourceSet create(String name) {
-                        return instantiator.newInstance(DefaultResourceSet.class, name,
-                                instantiator.newInstance(DefaultSourceDirectorySet.class, name, fileResolver), functionalSourceSet);
-                    }
-                });
-            }
-        });
-
-        BinaryContainer binaryContainer = target.getExtensions().getByType(BinaryContainer.class);
-        binaryContainer.registerFactory(ClassDirectoryBinary.class, new NamedDomainObjectFactory<ClassDirectoryBinary>() {
-            public ClassDirectoryBinary create(String name) {
-                return instantiator.newInstance(DefaultClassDirectoryBinary.class, name);
-            };
-        });
-
-        binaryContainer.withType(ClassDirectoryBinary.class).all(new Action<ClassDirectoryBinary>() {
-            public void execute(final ClassDirectoryBinary binary) {
-                final BinaryNamingScheme namingScheme = ((BinaryInternal) binary).getNamingScheme();
-                ConventionMapping conventionMapping = new DslObject(binary).getConventionMapping();
-                conventionMapping.map("classesDir", new Callable<File>() {
-                    public File call() throws Exception {
-                        return new File(new File(target.getBuildDir(), "classes"), namingScheme.getOutputDirectoryBase());
-                    }
-                });
-                binary.getSource().withType(ResourceSet.class).all(new Action<ResourceSet>() {
-                    public void execute(ResourceSet resourceSet) {
-                        // TODO: handle case where binary has multiple ResourceSet's
-                        Copy resourcesTask = target.getTasks().create(namingScheme.getTaskName("process", "resources"), ProcessResources.class);
-                        resourcesTask.setDescription(String.format("Processes %s.", resourceSet));
-                        new DslObject(resourcesTask).getConventionMapping().map("destinationDir", new Callable<File>() {
-                            public File call() throws Exception {
-                                return binary.getResourcesDir();
-                            }
-                        });
-                        binary.builtBy(resourcesTask);
-                        resourcesTask.from(resourceSet.getSource());
-                    }
-                });
-            }
-        });
-    }
-}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/package-info.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/package-info.java
deleted file mode 100644
index 4465713..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/plugins/package-info.java
+++ /dev/null
@@ -1,23 +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.
- */
-
-/**
- * Base plugins that add support for JVM languages.
- */
- at Incubating
-package org.gradle.language.jvm.plugins;
-
-import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/tasks/ProcessResources.java b/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/tasks/ProcessResources.java
deleted file mode 100644
index a41e9de..0000000
--- a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/tasks/ProcessResources.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.language.jvm.tasks;
-
-import org.gradle.api.tasks.Copy;
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner;
-import org.gradle.language.jvm.internal.StaleClassCleaner;
-
-/**
- * Copies resources from their source to their target directory, potentially processing them.
- * Makes sure no stale resources remain in the target directory.
- */
-public class ProcessResources extends Copy {
-
-    @Override
-    protected void copy() {
-        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
-        cleaner.setDestinationDir(getDestinationDir());
-        cleaner.execute();
-        super.copy();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompilationFailedException.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.java
new file mode 100644
index 0000000..ebf45f7
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.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.api.internal.tasks.compile;
+
+import org.gradle.platform.base.internal.toolchain.ArgCollector;
+
+public interface CompileSpecToArguments<T> {
+    public void collectArguments(T spec, ArgCollector collector);
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java
new file mode 100644
index 0000000..b03b11e
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java
@@ -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.internal.tasks.compile;
+
+import org.gradle.api.file.FileCollection;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultJvmLanguageCompileSpec implements JvmLanguageCompileSpec, Serializable {
+    private File workingDir;
+    private File tempDir;
+    private Iterable<File> classpath;
+    private File destinationDir;
+    private FileCollection source;
+    private String sourceCompatibility;
+    private String targetCompatibility;
+
+    public File getWorkingDir() {
+        return workingDir;
+    }
+
+    public void setWorkingDir(File workingDir) {
+        this.workingDir = workingDir;
+    }
+
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    public File getTempDir() {
+        return tempDir;
+    }
+
+    public void setTempDir(File tempDir) {
+        this.tempDir = tempDir;
+    }
+
+    public FileCollection getSource() {
+        return source;
+    }
+
+    public void setSource(FileCollection source) {
+        this.source = source;
+    }
+
+    public Iterable<File> getClasspath() {
+        return classpath;
+    }
+
+    public void setClasspath(Iterable<File> classpath) {
+        this.classpath = classpath;
+    }
+
+    public String getSourceCompatibility() {
+        return sourceCompatibility;
+    }
+
+    public void setSourceCompatibility(String sourceCompatibility) {
+        this.sourceCompatibility = sourceCompatibility;
+    }
+
+    public String getTargetCompatibility() {
+        return targetCompatibility;
+    }
+
+    public void setTargetCompatibility(String targetCompatibility) {
+        this.targetCompatibility = targetCompatibility;
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.java
new file mode 100644
index 0000000..ae8abac
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.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.tasks.compile;
+
+import org.gradle.process.ExecSpec;
+import org.gradle.platform.base.internal.toolchain.ArgCollector;
+
+public class ExecSpecBackedArgCollector implements ArgCollector {
+    private final ExecSpec action;
+
+    public ExecSpecBackedArgCollector(ExecSpec action) {
+        this.action = action;
+    }
+
+    public ArgCollector args(Object... args) {
+        action.args(args);
+        return this;
+    }
+
+    public ArgCollector args(Iterable<?> args) {
+        action.args(args);
+        return this;
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.java
new file mode 100644
index 0000000..41011e9
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.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.api.internal.tasks.compile;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.language.base.internal.compile.CompileSpec;
+
+import java.io.File;
+
+public interface JvmLanguageCompileSpec extends CompileSpec {
+    File getTempDir();
+
+    void setTempDir(File tempDir);
+
+    File getWorkingDir();
+
+    void setWorkingDir(File workingDir);
+
+    File getDestinationDir();
+
+    void setDestinationDir(File destinationDir);
+
+    FileCollection getSource();
+
+    void setSource(FileCollection source);
+
+    Iterable<File> getClasspath();
+
+    void setClasspath(Iterable<File> classpath);
+
+    String getSourceCompatibility();
+
+    void setSourceCompatibility(String sourceCompatibility);
+
+    String getTargetCompatibility();
+
+    void setTargetCompatibility(String targetCompatibility);
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java
new file mode 100644
index 0000000..a90dd4a
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java
@@ -0,0 +1,26 @@
+/*
+ * 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.tasks.compile;
+
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+public class NoOpStaleClassCleaner extends StaleClassCleaner {
+    @Override
+    public void execute() {
+        // do nothing
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/AbstractDaemonCompiler.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/AbstractDaemonCompiler.java
new file mode 100644
index 0000000..b2c73e0
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/AbstractDaemonCompiler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile.daemon;
+
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.UncheckedException;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+
+public abstract class AbstractDaemonCompiler<T extends CompileSpec> implements Compiler<T> {
+    private final Compiler<T> delegate;
+    private final CompilerDaemonFactory compilerDaemonFactory;
+    private final File daemonWorkingDir;
+
+    public AbstractDaemonCompiler(File daemonWorkingDir, Compiler<T> delegate, CompilerDaemonFactory compilerDaemonFactory) {
+        this.daemonWorkingDir = daemonWorkingDir;
+        this.delegate = delegate;
+        this.compilerDaemonFactory = compilerDaemonFactory;
+    }
+
+    public Compiler<T> getDelegate() {
+        return delegate;
+    }
+
+    public WorkResult execute(T spec) {
+        DaemonForkOptions daemonForkOptions = toDaemonOptions(spec);
+        CompilerDaemon daemon = compilerDaemonFactory.getDaemon(daemonWorkingDir, daemonForkOptions);
+        CompileResult result = daemon.execute(delegate, spec);
+        if (result.isSuccess()) {
+            return result;
+        }
+        throw UncheckedException.throwAsUncheckedException(result.getException());
+    }
+
+    protected abstract DaemonForkOptions toDaemonOptions(T spec);
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompileResult.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompileResult.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompileResult.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompileResult.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManager.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManager.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManager.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManager.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java
new file mode 100644
index 0000000..db5e568
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile.daemon;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+
+/**
+ * A service that executes compilers in a (potentially) long-lived process.
+ */
+public interface CompilerDaemon {
+    <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec);
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.java
new file mode 100644
index 0000000..aba8f21
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile.daemon;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.UncheckedException;
+import org.gradle.process.internal.WorkerProcess;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+
+class CompilerDaemonClient implements CompilerDaemon, CompilerDaemonClientProtocol, Stoppable {
+    private final DaemonForkOptions forkOptions;
+    private final WorkerProcess workerProcess;
+    private final CompilerDaemonServerProtocol server;
+    private final BlockingQueue<CompileResult> compileResults = new SynchronousQueue<CompileResult>();
+
+    public CompilerDaemonClient(DaemonForkOptions forkOptions, WorkerProcess workerProcess, CompilerDaemonServerProtocol server) {
+        this.forkOptions = forkOptions;
+        this.workerProcess = workerProcess;
+        this.server = server;
+    }
+
+    public <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec) {
+        // currently we just allow a single compilation thread at a time (per compiler daemon)
+        // one problem to solve when allowing multiple threads is how to deal with memory requirements specified by compile tasks
+        try {
+            server.execute(compiler, spec);
+            return compileResults.take();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public boolean isCompatibleWith(DaemonForkOptions required) {
+        return forkOptions.isCompatibleWith(required);
+    }
+
+    public void stop() {
+        server.stop();
+        workerProcess.waitForStop();
+    }
+
+    public void executed(CompileResult result) {
+        try {
+            compileResults.put(result);
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClientProtocol.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClientProtocol.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClientProtocol.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClientProtocol.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonFactory.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonFactory.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonFactory.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonFactory.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java
new file mode 100644
index 0000000..b8224e0
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile.daemon;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.language.base.internal.compile.CompileSpec;
+
+import java.io.File;
+
+/**
+ * Controls the lifecycle of the compiler daemon and provides access to it.
+ */
+ at ThreadSafe
+public class CompilerDaemonManager implements CompilerDaemonFactory, Stoppable {
+
+    private CompilerClientsManager clientsManager;
+
+    public CompilerDaemonManager(CompilerClientsManager clientsManager) {
+        this.clientsManager = clientsManager;
+    }
+
+    public CompilerDaemon getDaemon(final File workingDir, final DaemonForkOptions forkOptions) {
+        return new CompilerDaemon() {
+            public <T extends CompileSpec> CompileResult execute(org.gradle.language.base.internal.compile.Compiler<T> compiler, T spec) {
+                CompilerDaemonClient client = clientsManager.reserveIdleClient(forkOptions);
+                if (client == null) {
+                    client = clientsManager.reserveNewClient(workingDir, forkOptions);
+                }
+                try {
+                    return client.execute(compiler, spec);
+                } finally {
+                    clientsManager.release(client);
+                }
+            }
+        };
+    }
+
+    public void stop() {
+        clientsManager.stop();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.java
new file mode 100644
index 0000000..5dedce9
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile.daemon;
+
+import org.gradle.api.Action;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.UncheckedException;
+import org.gradle.process.internal.WorkerProcessContext;
+
+import java.io.Serializable;
+import java.util.concurrent.CountDownLatch;
+
+
+public class CompilerDaemonServer implements Action<WorkerProcessContext>, CompilerDaemonServerProtocol, Serializable {
+    private static final Logger LOGGER = Logging.getLogger(CompilerDaemonServer.class);
+    
+    private volatile CompilerDaemonClientProtocol client;
+    private volatile CountDownLatch stop;
+    
+    public void execute(WorkerProcessContext context) {
+        stop = new CountDownLatch(1);
+        client = context.getServerConnection().addOutgoing(CompilerDaemonClientProtocol.class);
+        context.getServerConnection().addIncoming(CompilerDaemonServerProtocol.class, this);
+        context.getServerConnection().connect();
+        try {
+            stop.await();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+
+    }
+
+    public <T extends CompileSpec> void execute(Compiler<T> compiler, T spec) {
+        try {
+            LOGGER.info("Executing {} in compiler daemon.", compiler);
+            WorkResult result = compiler.execute(spec);
+            LOGGER.info("Successfully executed {} in compiler daemon.", compiler);
+            client.executed(new CompileResult(result.getDidWork(), null));
+        } catch (Throwable t) {
+            LOGGER.info("Exception executing {} in compiler daemon: {}.", compiler, t);
+            client.executed(new CompileResult(true, t));
+        }
+    }
+
+    public void stop() {
+        stop.countDown();
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.java
new file mode 100644
index 0000000..aeb9bb2
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.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.tasks.compile.daemon;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.internal.concurrent.Stoppable;
+
+/**
+ * Server part of the compiler daemon protocol. Used to submit compilation jobs.
+ */
+public interface CompilerDaemonServerProtocol extends Stoppable {
+    <T extends CompileSpec> void execute(Compiler<T> compiler, T spec);
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.java
new file mode 100644
index 0000000..afe6f2f
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.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.api.internal.tasks.compile.daemon;
+
+import org.gradle.StartParameter;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.Factory;
+import org.gradle.process.internal.JavaExecHandleBuilder;
+import org.gradle.process.internal.WorkerProcess;
+import org.gradle.process.internal.WorkerProcessBuilder;
+import org.gradle.util.Clock;
+
+import java.io.File;
+
+public class CompilerDaemonStarter {
+    private final static Logger LOG = Logging.getLogger(CompilerDaemonStarter.class);
+    private final Factory<WorkerProcessBuilder> workerFactory;
+    private final StartParameter startParameter;
+
+    public CompilerDaemonStarter(Factory<WorkerProcessBuilder> workerFactory, StartParameter startParameter) {
+        this.workerFactory = workerFactory;
+        this.startParameter = startParameter;
+    }
+
+    public CompilerDaemonClient startDaemon(File workingDir, DaemonForkOptions forkOptions) {
+        LOG.debug("Starting Gradle compiler daemon with fork options {}.", forkOptions);
+        Clock clock = new Clock();
+        WorkerProcessBuilder builder = workerFactory.create();
+        builder.setLogLevel(startParameter.getLogLevel()); // NOTE: might make sense to respect per-compile-task log level
+        builder.applicationClasspath(forkOptions.getClasspath());
+        builder.sharedPackages(forkOptions.getSharedPackages());
+        JavaExecHandleBuilder javaCommand = builder.getJavaCommand();
+        javaCommand.setMinHeapSize(forkOptions.getMinHeapSize());
+        javaCommand.setMaxHeapSize(forkOptions.getMaxHeapSize());
+        javaCommand.setJvmArgs(forkOptions.getJvmArgs());
+        javaCommand.setWorkingDir(workingDir);
+        WorkerProcess process = builder.worker(new CompilerDaemonServer()).setBaseName("Gradle Compiler Daemon").build();
+        process.start();
+
+        CompilerDaemonServerProtocol server = process.getConnection().addOutgoing(CompilerDaemonServerProtocol.class);
+        CompilerDaemonClient client = new CompilerDaemonClient(forkOptions, process, server);
+        process.getConnection().addIncoming(CompilerDaemonClientProtocol.class, client);
+        process.getConnection().connect();
+
+        LOG.info("Started Gradle compiler daemon ({}) with fork options {}.", clock.getTime(), forkOptions);
+
+        return client;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptions.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptions.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptions.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java
new file mode 100644
index 0000000..4637bf0
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java
@@ -0,0 +1,94 @@
+/*
+ * 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.tasks.compile.daemon;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classloader.*;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.io.ClassLoaderObjectInputStream;
+import org.gradle.internal.nativeintegration.services.NativeServices;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.GUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.Serializable;
+import java.util.concurrent.Callable;
+
+public class InProcessCompilerDaemonFactory implements CompilerDaemonFactory {
+    private final ClassLoaderFactory classLoaderFactory;
+    private final File gradleUserHomeDir;
+
+    public InProcessCompilerDaemonFactory(ClassLoaderFactory classLoaderFactory, File gradleUserHomeDir) {
+        this.classLoaderFactory = classLoaderFactory;
+        this.gradleUserHomeDir = gradleUserHomeDir;
+    }
+
+    public CompilerDaemon getDaemon(File workingDir, final DaemonForkOptions forkOptions) {
+        return new CompilerDaemon() {
+            public <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec) {
+                ClassLoader groovyClassLoader = classLoaderFactory.createIsolatedClassLoader(new DefaultClassPath(forkOptions.getClasspath()));
+                FilteringClassLoader filteredGroovy = classLoaderFactory.createFilteringClassLoader(groovyClassLoader);
+                for (String packageName : forkOptions.getSharedPackages()) {
+                    filteredGroovy.allowPackage(packageName);
+                }
+
+                FilteringClassLoader loggingClassLoader = classLoaderFactory.createFilteringClassLoader(compiler.getClass().getClassLoader());
+                loggingClassLoader.allowPackage("org.slf4j");
+                loggingClassLoader.allowClass(Logger.class);
+                loggingClassLoader.allowClass(LogLevel.class);
+
+                ClassLoader groovyAndLoggingClassLoader = new CachingClassLoader(new MultiParentClassLoader(loggingClassLoader, filteredGroovy));
+
+                ClassLoader workerClassLoader = new MutableURLClassLoader(groovyAndLoggingClassLoader, ClasspathUtil.getClasspath(compiler.getClass().getClassLoader()));
+
+                try {
+                    byte[] serializedWorker = GUtil.serialize(new Worker<T>(compiler, spec, gradleUserHomeDir));
+                    ClassLoaderObjectInputStream inputStream = new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedWorker), workerClassLoader);
+                    Callable<?> worker = (Callable<?>) inputStream.readObject();
+                    Object result = worker.call();
+                    byte[] serializedResult = GUtil.serialize(result);
+                    inputStream = new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedResult), getClass().getClassLoader());
+                    return (CompileResult) inputStream.readObject();
+                } catch (Exception e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+        };
+    }
+
+    private static class Worker<T extends CompileSpec> implements Callable<Object>, Serializable {
+        private final Compiler<T> compiler;
+        private final T spec;
+        private final File gradleUserHome;
+
+        private Worker(Compiler<T> compiler, T spec, File gradleUserHome) {
+            this.compiler = compiler;
+            this.spec = spec;
+            this.gradleUserHome = gradleUserHome;
+        }
+
+        public Object call() throws Exception {
+            // We have to initialize this here because we're in an isolated classloader
+            NativeServices.initialize(gradleUserHome);
+            return new CompileResult(compiler.execute(spec).getDidWork(), null);
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/AbstractCompile.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/AbstractCompile.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/AbstractOptions.java b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/AbstractOptions.java
new file mode 100644
index 0000000..7abd4f9
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/AbstractOptions.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.compile;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Nullable;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+/**
+ * Base class for compilation-related options.
+ */
+public abstract class AbstractOptions implements Serializable {
+    private static final long serialVersionUID = 0;
+
+    public void define(@Nullable Map<String, Object> args) {
+        if (args == null) {
+            return;
+        }
+        for (Map.Entry<String, Object> arg: args.entrySet()) {
+            JavaReflectionUtil.writeableProperty(getClass(), arg.getKey()).setValue(this, arg.getValue());
+        }
+    }
+
+    public Map<String, Object> optionMap() {
+        final Class<?> thisClass = getClass();
+        Map<String, Object> map = Maps.newHashMap();
+        Class<?> currClass = thisClass;
+        if (currClass.getName().endsWith("_Decorated")) {
+            currClass = currClass.getSuperclass();
+        }
+        while (currClass != AbstractOptions.class) {
+            for (Field field : currClass.getDeclaredFields()) {
+                if (isOptionField(field)) {
+                    addValueToMapIfNotNull(map, field);
+                }
+            }
+            currClass = currClass.getSuperclass();
+        }
+        return map;
+    }
+
+    protected boolean excludeFromAntProperties(String fieldName) {
+        return false;
+    }
+
+    protected String getAntPropertyName(String fieldName) {
+        return fieldName;
+    }
+
+    protected Object getAntPropertyValue(String fieldName, Object value) {
+        return value;
+    }
+
+    private void addValueToMapIfNotNull(Map<String, Object> map, Field field) {
+        Object value = JavaReflectionUtil.readableProperty(this, Object.class, field.getName()).getValue(this);
+        if (value != null) {
+            map.put(getAntPropertyName(field.getName()), getAntPropertyValue(field.getName(), value));
+        }
+    }
+
+    private boolean isOptionField(Field field) {
+        return ((field.getModifiers() & Modifier.STATIC) == 0)
+                && (!field.getName().equals("metaClass"))
+                && (!excludeFromAntProperties(field.getName()));
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/BaseForkOptions.java b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/BaseForkOptions.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/BaseForkOptions.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/BaseForkOptions.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/package-info.java
rename to subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/JvmResourceSet.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/JvmResourceSet.java
new file mode 100644
index 0000000..63f16a0
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/JvmResourceSet.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.language.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of resource files.
+ */
+ at Incubating
+public interface JvmResourceSet extends LanguageSourceSet {}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/DefaultJvmResourceLanguageSourceSet.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/DefaultJvmResourceLanguageSourceSet.java
new file mode 100644
index 0000000..ec26313
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/DefaultJvmResourceLanguageSourceSet.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.jvm.internal;
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.jvm.JvmResourceSet;
+
+public class DefaultJvmResourceLanguageSourceSet extends BaseLanguageSourceSet implements JvmResourceSet {
+    @Override
+    protected String getTypeName() {
+        return "JVM resources";
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/EmptyClasspath.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/EmptyClasspath.java
new file mode 100644
index 0000000..071c4e9
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/EmptyClasspath.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.jvm.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.jvm.Classpath;
+
+// Temporary Classpath implementation for new jvm component model
+public class EmptyClasspath implements Classpath {
+    public FileCollection getFiles() {
+        return new SimpleFileCollection();
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return new DefaultTaskDependency();
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java
new file mode 100644
index 0000000..2ee8f29
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.jvm.internal;
+
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.jvm.JvmLibrary;
+import org.gradle.language.base.artifact.SourcesArtifact;
+
+public class JvmPluginServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new ComponentRegistrationAction());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class ComponentRegistrationAction {
+        public void configure(ServiceRegistration registration, ComponentTypeRegistry componentTypeRegistry) {
+            // TODO There should be a more explicit way to execute an action against existing services
+            // TODO:DAZ Dependency Management should be able to extract this from the plugin, without explicit registration
+            componentTypeRegistry.maybeRegisterComponentType(JvmLibrary.class)
+                    .registerArtifactType(SourcesArtifact.class, ArtifactType.SOURCES);
+        }
+    }
+}
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/package-info.java
similarity index 100%
rename from subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/package-info.java
rename to subprojects/language-jvm/src/main/java/org/gradle/language/jvm/package-info.java
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/JvmResourcesPlugin.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/JvmResourcesPlugin.java
new file mode 100644
index 0000000..0f426d9
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/JvmResourcesPlugin.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.jvm.plugins;
+
+import org.gradle.api.*;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.JvmBinarySpec;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.jvm.JvmResourceSet;
+import org.gradle.language.jvm.internal.DefaultJvmResourceLanguageSourceSet;
+import org.gradle.language.jvm.tasks.ProcessResources;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Plugin for packaging JVM resources. Applies the {@link org.gradle.language.base.plugins.ComponentModelBasePlugin}. Registers "resources" language support with the {@link
+ * org.gradle.language.jvm.JvmResourceSet}.
+ */
+ at Incubating
+public class JvmResourcesPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<JvmResourceSet> builder) {
+            builder.setLanguageName("resources");
+            builder.defaultImplementation(DefaultJvmResourceLanguageSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new JvmResources());
+        }
+    }
+
+    private static class JvmResources implements LanguageTransform<JvmResourceSet, org.gradle.jvm.JvmResources> {
+        public Class<JvmResourceSet> getSourceSetType() {
+            return JvmResourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            return Collections.emptyMap();
+        }
+
+        public Class<org.gradle.jvm.JvmResources> getOutputType() {
+            return org.gradle.jvm.JvmResources.class;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new SourceTransformTaskConfig() {
+                public String getTaskPrefix() {
+                    return "process";
+                }
+
+                public Class<? extends DefaultTask> getTaskType() {
+                    return ProcessResources.class;
+                }
+
+                public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
+                    ProcessResources resourcesTask = (ProcessResources) task;
+                    JvmResourceSet resourceSet = (JvmResourceSet) sourceSet;
+                    JvmBinarySpec jvmBinary = (JvmBinarySpec) binary;
+                    resourcesTask.from(resourceSet.getSource());
+                    resourcesTask.setDestinationDir(jvmBinary.getResourcesDir());
+                    jvmBinary.getTasks().getJar().dependsOn(resourcesTask);
+                }
+            };
+        }
+        public boolean applyToBinary(BinarySpec binary) {
+            return binary instanceof JvmBinarySpec;
+        }
+    }
+}
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/package-info.java
new file mode 100644
index 0000000..b9a1eec
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/plugins/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base plugins that add language support for JVM resources.
+ */
+ at Incubating
+package org.gradle.language.jvm.plugins;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/ProcessResources.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/ProcessResources.java
new file mode 100644
index 0000000..deb4422
--- /dev/null
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/ProcessResources.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.language.jvm.tasks;
+
+import org.gradle.api.tasks.Copy;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+
+/**
+ * Copies resources from their source to their target directory, potentially processing them.
+ * Makes sure no stale resources remain in the target directory.
+ */
+public class ProcessResources extends Copy {
+
+    @Override
+    protected void copy() {
+        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
+        cleaner.setDestinationDir(getDestinationDir());
+        cleaner.execute();
+        super.copy();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/tasks/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java
similarity index 100%
rename from subprojects/language-jvm/src/main/groovy/org/gradle/language/jvm/tasks/package-info.java
rename to subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java
diff --git a/subprojects/language-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-resources.properties b/subprojects/language-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-resources.properties
new file mode 100644
index 0000000..bfa60f2
--- /dev/null
+++ b/subprojects/language-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-resources.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.jvm.plugins.JvmResourcesPlugin
diff --git a/subprojects/language-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/language-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..97ba4fd
--- /dev/null
+++ b/subprojects/language-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.language.jvm.internal.JvmPluginServiceRegistry
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManagerTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManagerTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManagerTest.groovy
rename to subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerClientsManagerTest.groovy
diff --git a/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy
new file mode 100644
index 0000000..d19f99c
--- /dev/null
+++ b/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * 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.tasks.compile.daemon
+
+import org.gradle.language.base.internal.compile.CompileSpec
+import org.gradle.language.base.internal.compile.Compiler
+import spock.lang.Specification
+import spock.lang.Subject
+
+class CompilerDaemonManagerTest extends Specification {
+
+    def clientsManager = Mock(CompilerClientsManager)
+    def client = Mock(CompilerDaemonClient)
+
+    @Subject manager = new CompilerDaemonManager(clientsManager)
+
+    def workingDir = new File("some-dir")
+    def compiler = Stub(Compiler)
+    def options = Stub(DaemonForkOptions)
+    def compileSpec = Stub(CompileSpec)
+
+    def "getting a compiler daemon does not assume client use"() {
+        when:
+        manager.getDaemon(workingDir, options);
+
+        then:
+        0 * clientsManager._
+    }
+
+    def "new client is created when daemon is executed and no idle clients found"() {
+        when:
+        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
+
+        then:
+        1 * clientsManager.reserveIdleClient(options) >> null
+
+        then:
+        1 * clientsManager.reserveNewClient(workingDir, options) >> client
+
+        then:
+        1 * client.execute(compiler, compileSpec)
+
+        then:
+        1 * clientsManager.release(client)
+        0 * _._
+    }
+
+    def "idle client is reused when daemon is executed"() {
+        when:
+        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
+
+        then:
+        1 * clientsManager.reserveIdleClient(options) >> client
+
+        then:
+        1 * client.execute(compiler, compileSpec)
+
+        then:
+        1 * clientsManager.release(client)
+        0 * _._
+    }
+
+    def "client is released even if execution fails"() {
+        when:
+        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
+
+        then:
+        1 * clientsManager.reserveIdleClient(options) >> client
+
+        then:
+        1 * client.execute(compiler, compileSpec) >> { throw new RuntimeException("Boo!") }
+
+        then:
+        thrown(RuntimeException)
+        1 * clientsManager.release(client)
+        0 * _._
+    }
+
+    def "stops clients"() {
+        when:
+        manager.stop()
+
+        then:
+        clientsManager.stop()
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsMergeTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsMergeTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsMergeTest.groovy
rename to subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsMergeTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsTest.groovy
rename to subprojects/language-jvm/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonForkOptionsTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractOptionsTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/api/tasks/compile/AbstractOptionsTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/AbstractOptionsTest.groovy
rename to subprojects/language-jvm/src/test/groovy/org/gradle/api/tasks/compile/AbstractOptionsTest.groovy
diff --git a/subprojects/language-jvm/src/test/groovy/org/gradle/language/java/internal/DefaultJavaSourceSetTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/language/java/internal/DefaultJavaSourceSetTest.groovy
deleted file mode 100644
index d55403e..0000000
--- a/subprojects/language-jvm/src/test/groovy/org/gradle/language/java/internal/DefaultJavaSourceSetTest.groovy
+++ /dev/null
@@ -1,33 +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.language.java.internal
-
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.language.jvm.Classpath
-import org.gradle.language.base.FunctionalSourceSet
-import spock.lang.Specification
-
-class DefaultJavaSourceSetTest extends Specification {
-    def "has useful String representation"() {
-        def functionalSourceSet = Stub(FunctionalSourceSet) {
-            getName() >> "mainX"
-        }
-        def resourceSet = new DefaultJavaSourceSet("javaX", Stub(SourceDirectorySet), Stub(Classpath), functionalSourceSet)
-
-        expect:
-        resourceSet.toString() == "Java source 'mainX:javaX'"
-    }
-}
diff --git a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingSchemeTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingSchemeTest.groovy
deleted file mode 100644
index c00313e..0000000
--- a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/ClassDirectoryBinaryNamingSchemeTest.groovy
+++ /dev/null
@@ -1,55 +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.language.jvm.internal
-
-import spock.lang.Specification
-
-class ClassDirectoryBinaryNamingSchemeTest extends Specification {
-
-    def "generates task names for class directory binaries"() {
-        expect:
-        def namer = new ClassDirectoryBinaryNamingScheme(name)
-        namer.getTaskName(verb, target) == taskName
-
-        where:
-        name   | verb      | target      | taskName
-        "main" | null      | null        | "main"
-        "main" | "compile" | null        | "compileMain"
-        "main" | null      | "resources" | "resources"
-        "main" | "compile" | "java"      | "compileJava"
-
-        "test" | null      | null        | "test"
-        "test" | "compile" | null        | "compileTest"
-        "test" | null      | "resources" | "testResources"
-        "test" | "compile" | "java"      | "compileTestJava"
-    }
-
-    def "generates base name and output directory"() {
-        def namer = new ClassDirectoryBinaryNamingScheme(baseName)
-
-        expect:
-        namer.getLifecycleTaskName() == lifecycleName
-        namer.getOutputDirectoryBase() == outputDir
-
-        where:
-        baseName | lifecycleName | outputDir
-        "main"   | "classes"     | "main"
-        "test"   | "testClasses" | "test"
-    }
-}
diff --git a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinaryTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinaryTest.groovy
deleted file mode 100644
index 19fcb12..0000000
--- a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultClassDirectoryBinaryTest.groovy
+++ /dev/null
@@ -1,79 +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.language.jvm.internal;
-
-import spock.lang.Specification;
-
-public class DefaultClassDirectoryBinaryTest extends Specification {
-    def "uses short task names for binary with name 'mainClasses'"() {
-        when:
-        def binary = new DefaultClassDirectoryBinary("mainClasses")
-
-        then:
-        binary.name == 'mainClasses'
-
-        and:
-        binary.namingScheme.lifecycleTaskName == 'classes'
-        binary.namingScheme.getTaskName(null, null) == 'main'
-        binary.namingScheme.getTaskName("compile", null) == 'compileMain'
-        binary.namingScheme.getTaskName(null, "groovy") == 'groovy'
-        binary.namingScheme.getTaskName("compile", "groovy") == 'compileGroovy'
-    }
-
-    def "uses medium task names for binary with name 'otherClasses'"() {
-        when:
-        def binary = new DefaultClassDirectoryBinary("otherClasses")
-
-        then:
-        binary.name == 'otherClasses'
-
-        and:
-        binary.namingScheme.lifecycleTaskName == 'otherClasses'
-        binary.namingScheme.getTaskName(null, null) == 'other'
-        binary.namingScheme.getTaskName("compile", null) == 'compileOther'
-        binary.namingScheme.getTaskName(null, "groovy") == 'otherGroovy'
-        binary.namingScheme.getTaskName("compile", "groovy") == 'compileOtherGroovy'
-    }
-
-    def "uses long task names for binary with name 'otherBinary'"() {
-        when:
-        def binary = new DefaultClassDirectoryBinary("otherBinary")
-
-        then:
-        binary.name == 'otherBinary'
-
-        and:
-        binary.namingScheme.lifecycleTaskName == 'otherBinaryClasses'
-        binary.namingScheme.getTaskName(null, null) == 'otherBinary'
-        binary.namingScheme.getTaskName("compile", null) == 'compileOtherBinary'
-        binary.namingScheme.getTaskName(null, "groovy") == 'otherBinaryGroovy'
-        binary.namingScheme.getTaskName("compile", "groovy") == 'compileOtherBinaryGroovy'
-    }
-
-    def "has a useful toString() representation"() {
-        expect:
-        def binary = new DefaultClassDirectoryBinary(name)
-        binary.toString() == displayName
-        binary.displayName == displayName
-
-        where:
-        name    | displayName
-        'mainClasses'  | 'classes \'main\''
-        'otherClasses' | 'classes \'other\''
-        'otherBinary' | 'classes \'otherBinary\''
-    }
-}
diff --git a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultResourceSetTest.groovy b/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultResourceSetTest.groovy
deleted file mode 100644
index 04cfd61..0000000
--- a/subprojects/language-jvm/src/test/groovy/org/gradle/language/jvm/internal/DefaultResourceSetTest.groovy
+++ /dev/null
@@ -1,32 +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.language.jvm.internal
-
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.language.base.FunctionalSourceSet
-import spock.lang.Specification
-
-class DefaultResourceSetTest extends Specification {
-    def "has useful String representation"() {
-        def functionalSourceSet = Stub(FunctionalSourceSet) {
-            getName() >> "mainX"
-        }
-        def resourceSet = new DefaultResourceSet("resourcesX", Stub(SourceDirectorySet), functionalSourceSet)
-
-        expect:
-        resourceSet.toString() == "resources 'mainX:resourcesX'"
-    }
-}
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/IncrementalTestJvmComponent.groovy b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/IncrementalTestJvmComponent.groovy
new file mode 100644
index 0000000..86f5f06
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/IncrementalTestJvmComponent.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm
+
+import org.gradle.test.fixtures.file.TestFile
+
+abstract class IncrementalTestJvmComponent extends TestJvmComponent {
+
+    abstract void changeSources(List<TestFile> testFiles)
+
+    abstract void writeAdditionalSources(TestFile testFile)
+}
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/JvmSourceFile.java b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/JvmSourceFile.java
new file mode 100644
index 0000000..775b8e4
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/JvmSourceFile.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm;
+
+import com.google.common.base.Joiner;
+import org.gradle.test.fixtures.file.TestFile;
+
+public class JvmSourceFile {
+    private final String path;
+    private final String name;
+    private final String content;
+
+    public JvmSourceFile(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 String getFullPath() {
+        return path.isEmpty() ? name : path + "/" + name;
+    }
+
+    public JvmSourceFile getClassFile() {
+        return new JvmSourceFile(path, name.replaceAll("(\\.java|\\.scala)", ".class"), null);
+    }
+
+    public TestFile writeToDir(TestFile base) {
+        TestFile file = base.file(path, name);
+        writeToFile(file);
+        return file;
+    }
+
+    public void writeToFile(TestFile file) {
+        if (file.exists()) {
+            file.write("");
+        }
+        file.write(content);
+    }
+
+    public String withPath(String basePath) {
+        return Joiner.on('/').join(basePath, path, name);
+    }
+}
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/TestJvmComponent.groovy b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/TestJvmComponent.groovy
new file mode 100644
index 0000000..99664ad
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/fixtures/jvm/TestJvmComponent.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures.jvm
+
+import org.gradle.test.fixtures.file.TestFile
+
+abstract class TestJvmComponent {
+    abstract List<JvmSourceFile> getSources()
+
+    abstract String getLanguageName()
+
+    List<JvmSourceFile> getExpectedOutputs(){
+        return getSources().collect{it.classFile}.plus(resources);
+    }
+
+    List<TestFile> writeSources(TestFile sourceDir, String sourceSetName = languageName) {
+        return sources*.writeToDir(sourceDir.file(sourceSetName))
+    }
+
+    List<JvmSourceFile> resources = [
+            new JvmSourceFile("", "one.txt", "Here is a resource"),
+            new JvmSourceFile("sub-dir", "two.txt", "Here is another resource")
+    ]
+
+    List<TestFile> writeResources(TestFile testFile) {
+        return resources*.writeToDir(testFile)
+    }
+
+    abstract String getSourceSetTypeName()
+
+    def getSourceFileExtensions() {
+        return [getLanguageName()]
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..4072c57
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.language
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.file.TestFile
+import spock.lang.IgnoreIf
+
+abstract class AbstractJvmLanguageIncrementalBuildIntegrationTest extends AbstractIntegrationSpec {
+    abstract TestJvmComponent getTestComponent();
+
+    List<TestFile> sourceFiles
+    List<TestFile> resourceFiles
+
+    String mainCompileTaskName
+    def setup() {
+
+        mainCompileTaskName = ":compileMainJarMain${StringUtils.capitalize(getTestComponent().languageName)}"
+        sourceFiles = testComponent.writeSources(file("src/main"))
+        resourceFiles = testComponent.writeResources(file("src/main/resources"))
+
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+        id '${testComponent.languageName}-lang'
+    }
+
+    repositories {
+        mavenCentral()
+    }
+
+    model {
+        components {
+            main(JvmLibrarySpec)
+        }
+    }
+"""
+    }
+
+    def "builds jar"() {
+        when:
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped mainCompileTaskName, ":processMainJarMainResources", ":createMainJar", ":mainJar"
+
+        and:
+        jarFile("build/jars/mainJar/main.jar").hasDescendants(testComponent.expectedOutputs*.fullPath as String[])
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "does not re-execute build with no change"() {
+        given:
+        run "mainJar"
+
+        when:
+        run "mainJar"
+
+        then:
+        nonSkippedTasks.empty
+    }
+
+    def "rebuilds jar and classfile is removed when source file removed"() {
+        given:
+        run "mainJar"
+
+        when:
+        sourceFiles[1].delete()
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped mainCompileTaskName, ":createMainJar", ":mainJar"
+
+        and:
+        String[] expectedClasses = [testComponent.sources[0].classFile.fullPath, testComponent.resources[0].fullPath, testComponent.resources[1].fullPath]
+        file("build/classes/mainJar").assertHasDescendants(expectedClasses)
+        jarFile("build/jars/mainJar/main.jar").hasDescendants(expectedClasses)
+    }
+
+    def "rebuilds jar without resource when resource removed"() {
+        given:
+        run "mainJar"
+
+        when:
+        resourceFiles[1].delete()
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped ":processMainJarMainResources", ":createMainJar", ":mainJar"
+
+        and:
+        String[] expectedClasses = [testComponent.sources[0].classFile.fullPath, testComponent.sources[1].classFile.fullPath, testComponent.resources[0].fullPath]
+        file("build/classes/mainJar").assertHasDescendants(expectedClasses)
+        jarFile("build/jars/mainJar/main.jar").hasDescendants(expectedClasses)
+    }
+
+    def "rebuilds jar when source file changed"() {
+        given:
+        run "mainJar"
+
+        when:
+        testComponent.changeSources(sourceFiles)
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped mainCompileTaskName, ":createMainJar", ":mainJar"
+    }
+
+    def "rebuilds jar when resource file changed"() {
+        given:
+        run "mainJar"
+
+        when:
+        resourceFiles[0].text = "Some different text"
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped ":processMainJarMainResources", ":createMainJar", ":mainJar"
+    }
+
+    def "rebuilds jar when source file added"() {
+        given:
+        run "mainJar"
+
+        when:
+        testComponent.writeAdditionalSources(file("src/main"))
+
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped mainCompileTaskName, ":createMainJar", ":mainJar"
+
+        and:
+        file("build/classes/mainJar/Extra.class").assertExists()
+        jarFile("build/jars/mainJar/main.jar").assertContainsFile("Extra.class")
+    }
+
+    def "rebuilds jar when resource file added"() {
+        given:
+        run "mainJar"
+
+        when:
+        file("src/main/resources/Extra.txt") << "an extra resource"
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped ":processMainJarMainResources", ":createMainJar", ":mainJar"
+
+        and:
+        file("build/classes/mainJar/Extra.txt").assertExists()
+        jarFile("build/jars/mainJar/main.jar").assertContainsFile("Extra.txt")
+    }
+
+    def "recompiles but does not rebuild jar when source file changed such that bytecode is the same"() {
+        given:
+        run "mainJar"
+
+        when:
+        sourceFiles[0].text = sourceFiles[0].text + "// Line trailing comment"
+        run "mainJar"
+
+        then:
+        executedAndNotSkipped mainCompileTaskName
+        skipped ":createMainJar", ":mainJar"
+    }
+
+
+    private JarTestFixture jarFile(String s) {
+        new JarTestFixture(file(s))
+    }
+}
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIntegrationTest.groovy b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..8bb1138
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmLanguageIntegrationTest.groovy
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.language
+import com.sun.xml.internal.ws.util.StringUtils
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+abstract class AbstractJvmLanguageIntegrationTest extends AbstractIntegrationSpec{
+
+    abstract TestJvmComponent getApp()
+
+    def setup() {
+        buildFile << """
+        plugins {
+            id 'jvm-component'
+            id '${app.languageName}-lang'
+        }
+        repositories{
+            mavenCentral()
+        }
+    """
+    }
+
+    def "can build binary with sources in conventional location"() {
+        when:
+        app.writeSources(file("src/myLib"))
+        app.writeResources(file("src/myLib/resources"))
+        def expectedOutputs = app.expectedOutputs*.fullPath as String[]
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+
+"""
+        and:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":processMyLibJarMyLibResources", ":compileMyLibJarMyLib${StringUtils.capitalize(app.languageName)}", ":createMyLibJar", ":myLibJar"
+
+        and:
+        file("build/classes/myLibJar").assertHasDescendants(expectedOutputs)
+        jarFile("build/jars/myLibJar/myLib.jar").hasDescendants(expectedOutputs)
+    }
+
+    def "generated binary includes compiled classes from all language source sets"() {
+        setup:
+        def extraSourceSetName = "extra${app.languageName}"
+
+        when:
+        def source1 = app.sources[0]
+        def source2 = app.sources[1]
+
+        source1.writeToDir(file("src/myLib/${app.languageName}"))
+        source2.writeToDir(file("src/myLib/$extraSourceSetName"))
+
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                sources {
+                    $extraSourceSetName(${app.sourceSetTypeName})
+                }
+            }
+        }
+    }
+"""
+        and:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":compileMyLibJarMyLib${StringUtils.capitalize(app.languageName)}", ":compileMyLibJarMyLib${StringUtils.capitalize(extraSourceSetName)}", ":createMyLibJar", ":myLibJar"
+
+        and:
+        file("build/classes/myLibJar").assertHasDescendants(source1.classFile.fullPath, source2.classFile.fullPath)
+
+        and:
+        def jar = jarFile("build/jars/myLibJar/myLib.jar")
+        jar.hasDescendants(source1.classFile.fullPath, source2.classFile.fullPath)
+    }
+
+    def "can configure source locations for language and resource source sets"() {
+        setup:
+        def customSourceSetName = "my${app.languageName}"
+        app.writeSources(file("src/myLib"), customSourceSetName)
+        app.writeResources(file("src/myLib/myResources"))
+
+        // Conventional locations are ignore with explicit configuration
+        file("src/myLib/${app.languageName}/Ignored.${app.languageName}") << "IGNORE ME"
+        file("src/myLib/resources/Ignored.txt") << "IGNORE ME"
+
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                sources {
+                    ${app.languageName} {
+                        source.srcDir "src/myLib/$customSourceSetName"
+                    }
+                    resources {
+                        source.srcDir "src/myLib/myResources"
+                    }
+                }
+            }
+        }
+    }
+"""
+        when:
+        succeeds "assemble"
+
+        then:
+        file("build/classes/myLibJar").assertHasDescendants(app.expectedOutputs*.fullPath as String[])
+        jarFile("build/jars/myLibJar/myLib.jar").hasDescendants(app.expectedOutputs*.fullPath as String[])
+    }
+
+    def "can combine resources and sources in a single source directory"() {
+        when:
+        app.writeSources(file("src/myLib"))
+        app.writeResources(file("src/myLib"))
+
+        String[] expectedOutputs = [app.sources[0].classFile.fullPath, app.sources[1].classFile.fullPath, app.resources[0].fullPath, app.resources[1].fullPath]
+
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                sources {
+                    ${app.languageName}.source {
+                        srcDir "src/myLib"
+                        exclude "**/*.txt"
+                    }
+                    resources.source {
+                        srcDir "src/myLib"
+                        ${exluceStatementFor(app.sourceFileExtensions)}
+                    }
+                }
+            }
+        }
+    }
+"""
+        and:
+        succeeds "assemble"
+
+        then:
+        file("build/classes/myLibJar").assertHasDescendants(expectedOutputs)
+        jarFile("build/jars/myLibJar/myLib.jar").hasDescendants(expectedOutputs)
+    }
+
+    def exluceStatementFor(List<String> fileExtensions) {
+        fileExtensions.collect{"exclude '**/*.${it}'"}.join(SystemProperties.instance.lineSeparator)
+    }
+
+    def "can configure output directories for classes and resources"() {
+        when:
+        app.writeSources(file("src/myLib"))
+        app.writeResources(file("src/myLib/resources"))
+        def expectedOutputs = app.expectedOutputs*.fullPath as String[]
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+        jvm {
+            allBinaries {
+                classesDir = file("\${project.buildDir}/custom-classes")
+                resourcesDir = file("\${project.buildDir}/custom-resources")
+            }
+        }
+    }
+"""
+        and:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":processMyLibJarMyLibResources", ":compileMyLibJarMyLib${StringUtils.capitalize(app.languageName)}", ":createMyLibJar", ":myLibJar"
+
+        and:
+        file("build/custom-classes").assertHasDescendants(app.sources*.classFile.fullPath as String[])
+        file("build/custom-resources").assertHasDescendants(app.resources*.fullPath as String[])
+
+        and:
+        jarFile("build/jars/myLibJar/myLib.jar").hasDescendants(expectedOutputs)
+    }
+
+    protected JarTestFixture jarFile(String s) {
+        new JarTestFixture(file(s))
+    }
+
+}
diff --git a/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmPluginLanguageIntegrationTest.groovy b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmPluginLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..bb5d051
--- /dev/null
+++ b/subprojects/language-jvm/src/testFixtures/groovy/org/gradle/integtests/language/AbstractJvmPluginLanguageIntegrationTest.groovy
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.language
+
+import com.sun.xml.internal.ws.util.StringUtils
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.util.TextUtil
+
+import java.util.regex.Pattern
+
+abstract class AbstractJvmPluginLanguageIntegrationTest extends AbstractIntegrationSpec {
+
+    abstract String getSourceSetTypeName();
+
+    String getLanguageName() {
+        def matcher = Pattern.compile("(\\w+)LanguagePluginIntegrationTest").matcher(getClass().simpleName)
+        if (matcher.matches()) {
+            return matcher.group(1).toLowerCase()
+        }
+        throw new UnsupportedOperationException("Cannot determine language name from class name '${getClass().simpleName}.")
+    }
+
+    def setup(){
+        buildFile << """
+        plugins {
+            id 'jvm-component'
+            id '${languageName}-lang'
+        }"""
+    }
+
+    def "creates default source sets"() {
+        when:
+        buildFile << """
+
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+
+    task validate << {
+        def myLib = componentSpecs.myLib
+        assert myLib instanceof JvmLibrarySpec
+
+        assert myLib.sources.size() == 2
+        assert myLib.sources.${languageName} instanceof ${sourceSetTypeName}
+        assert myLib.sources.resources instanceof JvmResourceSet
+
+        assert sources as Set == myLib.sources as Set
+
+        binaries.withType(JarBinarySpec) { jvmBinary ->
+            assert jvmBinary.source == myLib.source
+        }
+    }
+"""
+        then:
+        succeeds "validate"
+
+        and:
+        !file("build").exists()
+    }
+
+    def "can configure additional language source sets for library"() {
+        when:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                sources {
+                    extra${languageName}(${sourceSetTypeName})
+                    extraResources(JvmResourceSet)
+                }
+            }
+        }
+    }
+
+    task validate << {
+        def myLib = componentSpecs.myLib
+        assert myLib instanceof JvmLibrarySpec
+
+        assert myLib.sources.size() == 4
+        assert myLib.sources.${languageName} instanceof ${sourceSetTypeName}
+        assert myLib.sources.extra${languageName} instanceof ${sourceSetTypeName}
+        assert myLib.sources.resources instanceof JvmResourceSet
+        assert myLib.sources.extraResources instanceof JvmResourceSet
+
+        assert sources as Set == myLib.sources as Set
+
+        binaries.withType(JarBinarySpec) { jvmBinary ->
+            assert jvmBinary.source == myLib.source
+        }
+    }
+"""
+        then:
+        succeeds "validate"
+
+        and:
+        !file("build").exists()
+    }
+
+    def "creates empty jar when library has empty sources"() {
+        given:
+        file("src/myLib/${languageName}").mkdirs()
+        file('src/myLib/resources').mkdirs()
+
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+"""
+        when:
+        succeeds "myLibJar"
+
+        then:
+        executed ":createMyLibJar", ":myLibJar"
+
+        and:
+        def jar = new JarTestFixture(file("build/jars/myLibJar/myLib.jar"))
+        jar.hasDescendants()
+    }
+
+    def "source sets and locations are visible in the components report"() {
+        when:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec){
+                sources {
+                    extra${languageName}(${sourceSetTypeName})
+                    extraResources(JvmResourceSet)
+                }
+            }
+        }
+    }
+"""
+        then:
+        succeeds "components"
+
+        and:
+        output.contains(TextUtil.toPlatformLineSeparators("""
+    JVM resources 'myLib:extraResources'
+        src${File.separator}myLib${File.separator}extraResources"""))
+
+        output.contains(TextUtil.toPlatformLineSeparators("""
+    ${StringUtils.capitalize(languageName)} source 'myLib:extra${languageName}'
+        src${File.separator}myLib${File.separator}extra${languageName}"""))
+
+        output.contains(TextUtil.toPlatformLineSeparators("""
+    JVM resources 'myLib:resources'
+        src${File.separator}myLib${File.separator}resources"""))
+
+        output.contains(TextUtil.toPlatformLineSeparators("""
+    ${StringUtils.capitalize(languageName)} source 'myLib:${languageName}'
+        src${File.separator}myLib${File.separator}${languageName}"""))
+    }
+
+}
diff --git a/subprojects/language-native/language-native.gradle b/subprojects/language-native/language-native.gradle
new file mode 100644
index 0000000..33836b0
--- /dev/null
+++ b/subprojects/language-native/language-native.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 project(':core')
+    compile project(':platformNative')
+
+    testCompile libraries.groovy
+
+    integTestRuntime project(":ideNative")
+}
+
+useTestFixtures()
+useTestFixtures(project: ":platformNative")
+useTestFixtures(project: ":platformBase")
+useTestFixtures(project: ":messaging")
+
+useClassycle()
+strictCompile()
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..a74c9c8
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.GUtil
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Assume
+import spock.lang.Ignore
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GccCompatible
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+import static org.gradle.util.TextUtil.escapeString
+
+abstract class AbstractNativeLanguageIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    IncrementalHelloWorldApp app
+    String mainCompileTask
+    String libraryCompileTask
+    TestFile sourceFile
+    TestFile headerFile
+    List<TestFile> librarySourceFiles = []
+
+    boolean isCanBuildForMultiplePlatforms() {
+        return !(toolChain instanceof AvailableToolChains.InstalledWindowsGcc)
+    }
+
+    abstract IncrementalHelloWorldApp getHelloWorldApp();
+
+    String getCompilerTool() {
+        "${app.sourceType}Compiler"
+    }
+
+    String getSourceType() {
+        GUtil.toCamelCase(app.sourceType)
+    }
+
+    def "setup"() {
+        app = getHelloWorldApp()
+        mainCompileTask = ":compileMainExecutableMain${sourceType}"
+        libraryCompileTask = ":compileHelloSharedLibraryHello${sourceType}"
+
+        buildFile << app.pluginScript
+        buildFile << app.extraConfiguration
+
+        buildFile << """
+        model {
+            components {
+                main(NativeExecutableSpec) {
+                    binaries.all {
+                        lib library: 'hello'
+                    }
+                }
+                hello(NativeLibrarySpec) {
+                    binaries.withType(SharedLibraryBinarySpec) {
+                        ${app.compilerDefine("DLL_EXPORT")}
+                    }
+                }
+            }
+        }
+        """
+        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"))
+        }
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "does not re-execute build with no change"() {
+        given:
+        run "installMainExecutable"
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        nonSkippedTasks.empty
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel || !TestPrecondition.CAN_INSTALL_EXECUTABLE.fulfilled})
+    def "rebuilds executable with source file change"() {
+        given:
+        run "installMainExecutable"
+
+        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
+    }
+
+    def "recompiles but does not relink executable with source comment change"() {
+        given:
+        run "installMainExecutable"
+        maybeWait()
+
+        when:
+        sourceFile.text = sourceFile.text.replaceFirst("// Simple hello world app", "// Comment is changed")
+        run "mainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+
+        executedAndNotSkipped mainCompileTask
+
+        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
+        if (AbstractInstalledToolChainIntegrationSpec.toolChain.visualCpp) {
+            executedAndNotSkipped ":linkMainExecutable"
+            executedAndNotSkipped ":mainExecutable"
+        } else if(objectiveCWithAslr()){
+            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executed ":linkMainExecutable", ":mainExecutable"
+        } else {
+            skipped ":linkMainExecutable"
+            skipped ":mainExecutable"
+        }
+    }
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "recompiles library and relinks executable with library source file change"() {
+        given:
+        run "installMainExecutable"
+        maybeWait()
+        def install = installation("build/install/mainExecutable")
+
+        when:
+        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"() {
+        given:
+        run "installMainExecutable"
+        maybeWait()
+
+        when:
+        headerFile << """
+            int unused();
+"""
+
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped mainCompileTask
+
+        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
+        if (AbstractInstalledToolChainIntegrationSpec.toolChain.visualCpp) {
+            executedAndNotSkipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executedAndNotSkipped ":linkMainExecutable", ":mainExecutable"
+        } else if(objectiveCWithAslr()){
+            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executed ":linkMainExecutable", ":mainExecutable"
+        } else {
+            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            skipped ":linkMainExecutable", ":mainExecutable"
+        }
+    }
+
+    def "recompiles binary when header file changes in a way that does not affect the object files"() {
+        given:
+        run "installMainExecutable"
+        maybeWait()
+
+        when:
+        headerFile << """
+// Comment added to the end of the header file
+"""
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped mainCompileTask
+
+        // Visual C++ compiler embeds a timestamp in every object file, so relinking is always required after recompiling
+        if (AbstractInstalledToolChainIntegrationSpec.toolChain.visualCpp) {
+            executedAndNotSkipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executedAndNotSkipped ":linkMainExecutable", ":mainExecutable"
+        } else if(objectiveCWithAslr()){
+            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executed ":linkMainExecutable", ":mainExecutable"
+        } else {
+            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            skipped ":linkMainExecutable", ":mainExecutable"
+        }
+    }
+
+    // compiling Objective-C and Objective-Cpp with clang generates
+    // random different object files (related to ASLR settings) 
+    // We saw this behaviour only on linux so far. 
+    boolean objectiveCWithAslr() {
+        return (sourceType == "Objc" || sourceType == "Objcpp") &&
+                OperatingSystem.current().isLinux() &&
+                AbstractInstalledToolChainIntegrationSpec.toolChain.displayName == "clang"
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "rebuilds binary with compiler option change"() {
+        given:
+        run "installMainExecutable"
+
+        def install = installation("build/install/mainExecutable")
+
+        when:
+        buildFile << """
+        model {
+            components {
+                hello {
+                    binaries.all {
+                        ${helloWorldApp.compilerArgs("-DFRENCH")}
+                    }
+                }
+            }
+        }
+"""
+
+        maybeWait()
+        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
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "rebuilds binary with target platform change"() {
+        Assume.assumeTrue(canBuildForMultiplePlatforms)
+        given:
+        buildFile << """
+    model {
+        platforms {
+            platform_x86 {
+                architecture 'x86'
+            }
+            platform_x64 {
+                architecture 'x86-64'
+            }
+        }
+        components {
+            main.targetPlatform "platform_x86"
+            hello.targetPlatform "platform_x86"
+        }
+    }
+"""
+        run "mainExecutable"
+
+        when:
+        buildFile.text = buildFile.text.replace("platform_x86", "platform_x64")
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask, mainCompileTask
+        executedAndNotSkipped ":linkHelloSharedLibrary"
+        executedAndNotSkipped ":helloSharedLibrary", ":mainExecutable"
+    }
+
+    def "relinks binary when set of input libraries changes"() {
+        given:
+        run "mainExecutable", "helloStaticLibrary"
+
+        def executable = executable("build/binaries/mainExecutable/main")
+        def snapshot = executable.snapshot()
+
+        when:
+        buildFile.text = buildFile.text.replace("lib library: 'hello'", "lib library: 'hello', linkage: 'static'")
+        run "mainExecutable"
+
+        then:
+        skipped ":helloStaticLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        executable.assertHasChangedSince(snapshot)
+    }
+
+    def "relinks binary but does not recompile when linker option changed"() {
+        given:
+        run "mainExecutable"
+
+        when:
+        def executable = executable("build/binaries/mainExecutable/main")
+        def snapshot = executable.snapshot()
+
+        and:
+        def linkerArgs = toolChain.isVisualCpp() ? "'/DEBUG'" : OperatingSystem.current().isMacOsX() ? "'-Xlinker', '-no_pie'" : "'-Xlinker', '-q'";
+        linkerArgs = escapeString(linkerArgs)
+        buildFile << """
+        model {
+            components {
+                main {
+                    binaries.all {
+                        linker.args ${escapeString(linkerArgs)}
+                    }
+                }
+            }
+        }
+"""
+
+        run "mainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        skipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        executable.assertExists()
+
+        if (toolChain.id != "mingw") { // Identical binary is produced on mingw
+            executable.assertHasChangedSince(snapshot)
+        }
+    }
+
+    def "cleans up stale object files when executable source file renamed"() {
+        given:
+        run "installMainExecutable"
+
+        def oldObjFile = objectFileFor(sourceFile)
+        def newObjFile = objectFileFor(sourceFile.getParentFile().file("changed_${sourceFile.name}"))
+        assert oldObjFile.file
+        assert !newObjFile.file
+
+        final source = sourceFile
+
+        when:
+        rename(source)
+        run "mainExecutable"
+
+        then:
+        skipped libraryCompileTask
+        skipped ":linkHelloSharedLibrary"
+        skipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        !oldObjFile.file
+        newObjFile.file
+    }
+
+    def "cleans up stale object files when library source file renamed"() {
+        when:
+        run "helloStaticLibrary"
+
+        then:
+        String objectFilesPath = "build/objs/helloStaticLibrary/hello${sourceType}"
+        def oldObjFile = objectFileFor(librarySourceFiles[0], objectFilesPath)
+        def newObjFile = objectFileFor( librarySourceFiles[0].getParentFile().file("changed_${librarySourceFiles[0].name}"), objectFilesPath)
+        assert oldObjFile.file
+        assert !newObjFile.file
+
+        assert staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(oldObjFile.name)
+
+        when:
+        librarySourceFiles.each { rename(it) }
+        run "helloStaticLibrary"
+
+        then:
+        executedAndNotSkipped libraryCompileTask.replace("Shared", "Static")
+        executedAndNotSkipped ":createHelloStaticLibrary"
+        executedAndNotSkipped ":helloStaticLibrary"
+
+        and:
+        !oldObjFile.file
+        newObjFile.file
+
+        and:
+        assert staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(newObjFile.name)
+        assert !staticLibrary("build/binaries/helloStaticLibrary/hello").listObjectFiles().contains(oldObjFile.name)
+    }
+
+    @RequiresInstalledToolChain(GccCompatible)
+    def "recompiles binary when imported header file changes"() {
+        sourceFile.text = sourceFile.text.replaceFirst('#include "hello.h"', "#import \"hello.h\"")
+        if(buildingCorCppWithGcc()){
+            buildFile << """
+                //support for #import on c/cpp is deprecated in gcc
+                binaries.all { ${compilerTool}.args '-Wno-deprecated'; }
+            """
+        }
+
+        given:
+        run "installMainExecutable"
+
+
+        when:
+        headerFile << """
+            int unused();
+"""
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped mainCompileTask
+
+        if(objectiveCWithAslr()){
+            executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            executed ":linkMainExecutable", ":mainExecutable"
+        } else {
+            skipped ":linkHelloSharedLibrary", ":helloSharedLibrary"
+            skipped ":linkMainExecutable", ":mainExecutable"
+        }
+    }
+
+    @RequiresInstalledToolChain(VisualCpp)
+    def "cleans up stale debug files when changing from debug to non-debug"() {
+
+        given:
+        buildFile << """
+            binaries.all { ${compilerTool}.args '/Zi'; linker.args '/DEBUG'; }
+        """
+        run "mainExecutable"
+
+        def executable = executable("build/binaries/mainExecutable/main")
+        executable.assertDebugFileExists()
+
+        when:
+        buildFile << """
+            binaries.all { ${compilerTool}.args.clear(); linker.args.clear(); }
+        """
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped libraryCompileTask
+        executedAndNotSkipped ":helloSharedLibrary"
+        executedAndNotSkipped mainCompileTask
+        executedAndNotSkipped ":linkMainExecutable"
+        executedAndNotSkipped ":mainExecutable"
+
+        and:
+        executable.assertDebugFileDoesNotExist()
+    }
+
+    @Issue("GRADLE-3248")
+    def "incremental compilation isn't considered up-to-date when compilation fails"() {
+        expect:
+        succeeds mainCompileTask
+
+        when:
+        app.brokenFile.writeToDir(file("src/main"))
+
+        then:
+        fails mainCompileTask
+
+        when:
+        // nothing changes
+
+        expect:
+        // build should still fail
+        fails mainCompileTask
+    }
+
+    @Ignore("Test demonstrates missing functionality in incremental build with C++")
+    def "recompiles binary when header file with relative path changes"() {
+        when:
+        buildFile << """
+plugins {
+    id 'cpp'
+}
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        file("src/main/cpp/main.cpp") << """
+            #include "../not_included/hello.h"
+
+            int main () {
+              sayHello();
+              return 0;
+            }
+"""
+
+        def headerFile = file("src/main/not_included/hello.h") << """
+            void sayHello();
+"""
+
+        file("src/main/cpp/hello.cpp") << """
+            #include <iostream>
+
+            void sayHello() {
+                std::cout << "HELLO" << std::endl;
+            }
+"""
+        then:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == "HELLO\n"
+
+        when:
+        headerFile.text = """
+            NOT A VALID HEADER FILE
+"""
+        then:
+        fails "mainExecutable"
+        and:
+        executedAndNotSkipped "compileMainExecutableMainCpp"
+    }
+
+
+    def buildingCorCppWithGcc() {
+        return toolChain.meets(ToolChainRequirement.Gcc) && (sourceType == "C" || sourceType == "Cpp")
+    }
+
+    private void maybeWait() {
+        if (toolChain.visualCpp) {
+            def now = System.currentTimeMillis()
+            def nextSecond = now % 1000
+            Thread.sleep(1200 - nextSecond)
+        }
+    }
+
+    static boolean rename(TestFile sourceFile) {
+        final newFile = new File(sourceFile.getParentFile(), "changed_${sourceFile.name}")
+        newFile << sourceFile.text
+        sourceFile.delete()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalCompileIntegrationTest.groovy
new file mode 100755
index 0000000..5a17a3b
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalCompileIntegrationTest.groovy
@@ -0,0 +1,520 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language
+
+import groovy.io.FileType
+import org.gradle.integtests.fixtures.CompilationOutputsFixture
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.GUtil
+import spock.lang.Unroll
+
+abstract class AbstractNativeLanguageIncrementalCompileIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    IncrementalHelloWorldApp app
+    String compileTask
+    TestFile sourceFile
+    TestFile sharedHeaderFile
+    TestFile otherHeaderFile
+    List<TestFile> otherSourceFiles = []
+    TestFile objectFileDir
+    CompilationOutputsFixture outputs
+
+    abstract IncrementalHelloWorldApp getHelloWorldApp();
+
+    String getSourceType() {
+        GUtil.toCamelCase(app.sourceType)
+    }
+
+    def "setup"() {
+        app = getHelloWorldApp()
+        compileTask = ":compileMainExecutableMain${sourceType}"
+
+        buildFile << app.pluginScript
+        buildFile << app.extraConfiguration
+        buildFile << """
+    model {
+        components {
+            main(NativeExecutableSpec)
+        }
+    }
+        """
+
+        and:
+        sourceFile = app.mainSource.writeToDir(file("src/main"))
+        sharedHeaderFile = app.libraryHeader.writeToDir(file("src/main"))
+        app.librarySources.each {
+            otherSourceFiles << it.writeToDir(file("src/main"))
+        }
+        otherHeaderFile = file("src/main/headers/other.h") << """
+            // Dummy header file
+"""
+        objectFileDir = file("build/objs/mainExecutable")
+        outputs = new CompilationOutputsFixture(objectFileDir)
+    }
+
+    def "recompiles changed source file only"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        sourceFile << """
+// Changed source file
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "recompiles all source files that include changed header file"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        sharedHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+    }
+
+    def "recompiles only source file that includes changed header file"() {
+        given:
+        sourceFile << """
+            #include "${otherHeaderFile.name}"
+"""
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "source is always recompiled if it includes header via macro"() {
+        given:
+        sourceFile << """
+            #define MY_HEADER "${otherHeaderFile.name}"
+            #include MY_HEADER
+"""
+
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+
+        when: "Header that is NOT included is changed"
+        file("src/main/headers/notIncluded.h") << """
+            // Dummy header file
+"""
+        and:
+        run "mainExecutable"
+
+        then: "Source is still recompiled"
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "recompiles source file when transitively included header file is changed"() {
+        given:
+        def transitiveHeaderFile = file("src/main/headers/transitive.h") << """
+           // Dummy header file
+"""
+        otherHeaderFile << """
+            #include "${transitiveHeaderFile.name}"
+"""
+        sourceFile << """
+            #include "${otherHeaderFile.name}"
+"""
+
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        transitiveHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "recompiles source file when an included header file is renamed"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        and:
+        final newFile = file("src/main/headers/changed.h")
+        newFile << sharedHeaderFile.text
+        sharedHeaderFile.delete()
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+        failure.assertHasDescription("Execution failed for task '${compileTask}'.");
+    }
+
+    def "does not recompile any sources when unused header file is changed"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        skipped compileTask
+        outputs.noneRecompiled()
+    }
+
+    @Unroll
+    def "does not recompile when include path has #testCase"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        file("src/additional-headers/other.h") << """
+    // extra header file that is not included in source
+"""
+        file("src/replacement-headers/${sharedHeaderFile.name}") << """
+    // replacement header file that is included in source
+"""
+
+        when:
+        buildFile << """
+    model {
+        components {
+            main {
+                sources {
+                    ${app.sourceType} {
+                        exportedHeaders {
+                            srcDirs ${headerDirs}
+                        }
+                    }
+                }
+            }
+        }
+    }
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        skipped compileTask
+        outputs.noneRecompiled()
+
+        where:
+        testCase                       | headerDirs
+        "extra header dir after"       | '"src/main/headers", "src/additional-headers"'
+        "extra header dir before"      | '"src/additional-headers", "src/main/headers"'
+        "replacement header dir after" | '"src/main/headers", "src/replacement-headers"'
+    }
+
+    def "recompiles when include path is changed so that replacement header file occurs before previous header"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        file("src/replacement-headers/${sharedHeaderFile.name}") << sharedHeaderFile.text
+
+        when:
+        buildFile << """
+model {
+    components {
+        main {
+            sources {
+                ${app.sourceType}  {
+                    exportedHeaders {
+                        srcDirs "src/replacement-headers", "src/main/headers"
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+    }
+
+    def "recompiles when replacement header file is added before previous header to existing include path"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main {
+            sources {
+                ${app.sourceType} {
+                    exportedHeaders {
+                        srcDirs "src/replacement-headers", "src/main/headers"
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        file("src/replacement-headers/${sharedHeaderFile.name}") << sharedHeaderFile.text
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+    }
+
+    def "recompiles when replacement header file is added to source directory"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        sourceFile.parentFile.file(sharedHeaderFile.name) << sharedHeaderFile.text
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+    }
+
+    def "recompiles all source files and removes stale outputs when compiler arg changes"() {
+        given:
+        def extraSource = file("src/main/${app.sourceType}/extra.${app.sourceExtension}")
+        extraSource << sourceFile.text.replaceAll("main", "main2")
+
+        outputs.snapshot { run "mainExecutable" }
+
+        objectFileFor(extraSource).assertExists()
+
+        when:
+        sourceFile << """
+            // Changed source file
+"""
+        buildFile << """
+        model {
+            components {
+                main {
+                    binaries.all {
+                        ${helloWorldApp.compilerDefine("MY_DEF")}
+                    }
+                }
+            }
+        }
+"""
+        extraSource.delete()
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+        objectFileFor(extraSource).assertDoesNotExist()
+    }
+
+    def "recompiles all source files when generated object files are removed"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        objectFileDir.deleteDir()
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFiles allSources
+    }
+
+    def "removes output file when source file is renamed"() {
+        given:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        final newFile = file("src/main/${app.sourceType}/changed.${app.sourceExtension}")
+        newFile << sourceFile.text
+        sourceFile.delete()
+
+        and:
+        run "mainExecutable"
+
+        then:
+        outputs.recompiledFile newFile
+        objectFileFor(sourceFile).assertDoesNotExist()
+    }
+
+    def "removes output file when source file is removed"() {
+        given:
+        def extraSource = file("src/main/${app.sourceType}/extra.${app.sourceExtension}")
+        extraSource << sourceFile.text.replaceAll("main", "main2")
+
+        outputs.snapshot { run "mainExecutable" }
+
+        and:
+        objectFileFor(extraSource).assertExists()
+
+        when:
+        extraSource.delete()
+
+        and:
+        run "mainExecutable"
+
+        then:
+        objectFileFor(extraSource).assertDoesNotExist()
+        outputs.noneRecompiled()
+    }
+
+    def "removes output files when all source files are removed"() {
+        given:
+        run "mainExecutable"
+
+        def executable = executable("build/binaries/mainExecutable/main")
+        executable.assertExists()
+
+        when:
+        file("src/main").eachFileRecurse(FileType.FILES) {
+            println "deleting ${it}"
+            it.delete()
+        }
+
+        and:
+        run "mainExecutable"
+
+        then: "linker output file is removed"
+        executable.assertDoesNotExist()
+
+        // Stale object files are removed when a new file is added to the source set
+        when:
+        def newSource = file("src/main/${app.sourceType}/newfile.${app.sourceExtension}") << """
+            #include <stdio.h>
+
+            int main () {
+                printf("hello");
+                return 0;
+            }
+"""
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executable.exec().out == "hello"
+        objectFileFor(newSource).assertExists()
+
+        and: "Previous object files are removed"
+        objectFileFor(sourceFile).assertDoesNotExist()
+        otherSourceFiles.each {
+            objectFileFor(it).assertDoesNotExist()
+        }
+    }
+
+    def "incremental compile is not effected by other compile tasks"() {
+        given:
+        buildFile << """
+model {
+    components {
+        other(NativeExecutableSpec)
+    }
+}
+"""
+        app.writeSources(file("src/other"))
+
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        and:
+        // Completely independent compile task (state should be independent)
+        run "otherExecutable"
+
+        when:
+        sourceFile << """
+// Changed source file
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    List getAllSources() {
+        return [sourceFile] + otherSourceFiles
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..5bb9354
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.language
+
+import org.apache.commons.lang.RandomStringUtils
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Ignore
+
+abstract class AbstractNativeLanguageIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    abstract HelloWorldApp getHelloWorldApp()
+
+    def "setup"() {
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+    }
+
+    def "compile and link executable"() {
+        given:
+        buildFile << """
+            model {
+                components {
+                    main(NativeExecutableSpec)
+                }
+            }
+        """
+
+        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 << """
+            model {
+                components {
+                    main(NativeExecutableSpec) {
+                        binaries.all {
+                            ${helloWorldApp.compilerArgs("-DFRENCH")}
+                        }
+                    }
+                }
+            }
+        """
+
+        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 << """
+            model {
+                components {
+                    main(NativeExecutableSpec) {
+                        binaries.all {
+                            ${helloWorldApp.compilerDefine("FRENCH")}
+                        }
+                    }
+                }
+            }
+        """
+
+        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 << """
+            model {
+                components {
+                    main(NativeExecutableSpec) {
+                        sources {
+                            ${helloWorldApp.sourceType}.lib library: "hello"
+                        }
+                    }
+                    hello(NativeLibrarySpec)
+                }
+            }
+        """
+
+        and:
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(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 << """
+            model {
+                components {
+                    main(NativeExecutableSpec) {
+                        sources {
+                            ${helloWorldApp.sourceType}.lib library: "hello", linkage: "static"
+                        }
+                    }
+                    hello(NativeLibrarySpec) {
+                        binaries.withType(StaticLibraryBinarySpec) {
+                            ${helloWorldApp.compilerDefine("FRENCH")}
+                        }
+                    }
+                }
+            }
+        """
+
+        and:
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(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
+    }
+
+    @Ignore
+    def "can run project in extended nested file paths"() {
+        // windows can't handle a path up to 260 characters
+        // we create a path that ends up with build folder longer than is 260
+        def projectPathOffset = 180 - testDirectory.getAbsolutePath().length()
+        def nestedProjectPath = RandomStringUtils.randomAlphanumeric(projectPathOffset-10) + "/123456789"
+
+        setup:
+        def deepNestedProjectFolder = file(nestedProjectPath)
+        executer.usingProjectDirectory(deepNestedProjectFolder)
+        def TestFile buildFile = deepNestedProjectFolder.file("build.gradle")
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+        buildFile << """
+            model {
+                components {
+                    main(NativeExecutableSpec)
+                }
+            }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("$nestedProjectPath/src/main"));
+
+        expect:
+        succeeds "mainExecutable"
+        def mainExecutable = executable("$nestedProjectPath/build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy
new file mode 100644
index 0000000..1cbbea8
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.PCHHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.spockframework.util.TextUtil
+
+abstract class AbstractNativePreCompiledHeaderIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    abstract PCHHelloWorldApp getApp()
+
+    def "setup"() {
+        buildFile << app.pluginScript
+        buildFile << app.extraConfiguration
+    }
+
+    def "can set a precompiled header on a source set for a relative source header" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader(path).writeToDir(file("src/hello"))
+        app.getLibrarySources(path).each {
+            it.writeToDir(file("src/hello"))
+        }
+        assert file("src/hello/headers/${path}hello.h").exists()
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "${path}hello.h"
+                        }
+                        binaries.all {
+                            if (toolChain.name == "visualCpp") {
+                                ${app.compilerArgs("/showIncludes")}
+                            } else {
+                                ${app.compilerArgs("-H")}
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling hello.h ====>")
+        def outputDirectories = file(PCHHeaderDirName).listFiles().findAll { it.isDirectory() }
+        assert outputDirectories.size() == 1
+        assert outputDirectories[0].assertContainsDescendants("prefix-headers.${getSuffix()}")
+
+        and:
+        args("--info")
+        succeeds libraryCompileTaskName
+        skipped ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        ! output.contains("<==== compiling hello.h ====>")
+
+        where:
+        path << [ "", "subdir/to/header/" ]
+    }
+
+    def "can set a precompiled header on a source set for a source header in include path" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader(path).writeToDir(file("src/include"))
+        app.getLibrarySources(path).each {
+            it.writeToDir(file("src/hello"))
+        }
+        assert file("src/include/headers/${path}hello.h").exists()
+
+        when:
+        def headerDir = file("src/include/headers")
+        def safeHeaderDirPath = TextUtil.escape(headerDir.absolutePath)
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "${path}hello.h"
+                        }
+                        binaries.all {
+                            if (toolChain.name == "visualCpp") {
+                                ${app.sourceType}Compiler.args "/I${safeHeaderDirPath}", "/showIncludes"
+                            } else {
+                                ${app.sourceType}Compiler.args "-I${safeHeaderDirPath}", "-H"
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling hello.h ====>")
+        def outputDirectories = file(PCHHeaderDirName).listFiles().findAll { it.isDirectory() }
+        assert outputDirectories.size() == 1
+        assert outputDirectories[0].assertContainsDescendants("prefix-headers.${getSuffix()}")
+
+        and:
+        args("--info")
+        succeeds libraryCompileTaskName
+        skipped ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        ! output.contains("<==== compiling hello.h ====>")
+
+        where:
+        path << [ "", "subdir/" ]
+    }
+
+    def "can set a precompiled header on a source set for a system header" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.libraryHeader.writeToDir(file("src/hello"))
+        app.getSystemHeader(path).writeToDir(file("src/systemHeader"))
+        app.librarySources.each {
+            SourceFile library = new SourceFile(it.path, it.name, "#include <${path}systemHeader.h>\n" + it.content)
+            library.writeToDir(file("src/hello"))
+        }
+        assert file("src/systemHeader/headers/${path}systemHeader.h").exists()
+
+        when:
+        def systemHeaderDir = file("src/systemHeader/headers")
+        def safeHeaderDirPath = TextUtil.escape(systemHeaderDir.absolutePath)
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "<${path}systemHeader.h>"
+                        }
+                        binaries.all {
+                            println toolChain
+                            if (toolChain.name == "visualCpp") {
+                                ${app.sourceType}Compiler.args "/I${safeHeaderDirPath}", "/showIncludes"
+                            } else {
+                                ${app.sourceType}Compiler.args "-I${safeHeaderDirPath}", "-H"
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling systemHeader.h ====>")
+        def outputDirectories = file(PCHHeaderDirName).listFiles().findAll { it.isDirectory() }
+        assert outputDirectories.size() == 1
+        assert outputDirectories[0].assertContainsDescendants("prefix-headers.${getSuffix()}")
+
+        and:
+        args("--info")
+        succeeds libraryCompileTaskName
+        skipped ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        ! output.contains("<==== compiling systemHeader.h ====>")
+
+        where:
+        path << [ "", "subdir/" ]
+    }
+
+    def "can set multiple precompiled headers on a source set" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader().writeToDir(file("src/hello"))
+        app.getLibrarySources().each {
+            it.writeToDir(file("src/hello"))
+        }
+        assert file("src/hello/headers/hello.h").exists()
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "hello.h"
+                            ${app.sourceType}.preCompiledHeader "<${app.IOHeader}>"
+                        }
+                        binaries.all {
+                            if (toolChain.name == "visualCpp") {
+                                ${app.compilerArgs("/showIncludes")}
+                            } else {
+                                ${app.compilerArgs("-H")}
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds libraryCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        // once for PCH compile, once for compile of sum.c, but not for hello.c
+        output.count(getUniquePragmaOutput("<==== compiling hello.h ====>")) == 2
+    }
+
+    def "can have source sets both with and without precompiled headers" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader().writeToDir(file("src/hello"))
+        app.getLibrarySources().find { it.name.startsWith("hello") }.writeToDir(file("src/hello"))
+        assert file("src/hello/headers/hello.h").exists()
+
+        app.getLibraryHeader().writeToDir(file("src/hello2"))
+        app.getLibrarySources().find { it.name.startsWith("hello") }.writeToDir(file("src/hello2"))
+        assert file("src/hello2/headers/hello.h").exists()
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "hello.h"
+                        }
+                        binaries.all {
+                            if (toolChain.name == "visualCpp") {
+                                ${app.compilerArgs("/showIncludes")}
+                            } else {
+                                ${app.compilerArgs("-H")}
+                            }
+                        }
+                    }
+                    hello2(NativeLibrarySpec)
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling hello.h ====>")
+
+        and:
+        args("--info")
+        succeeds libraryCompileTaskName, libraryCompileTaskName.replaceAll("Hello", "Hello2")
+        executed ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        // once for hello2.c only
+        output.count(getUniquePragmaOutput("<==== compiling hello.h ====>")) == 1
+    }
+
+    def "compiler arguments set on the binary get used for the precompiled header" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader().writeToDir(file("src/hello"))
+        app.getLibrarySources().find { it.name.startsWith("hello") }.writeToDir(file("src/hello"))
+        assert file("src/hello/headers/hello.h").exists()
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "hello.h"
+                        }
+                        binaries.all {
+                            ${app.compilerDefine("FRENCH")}
+                            if (toolChain.name == "visualCpp") {
+                                ${app.compilerArgs("/showIncludes")}
+                            } else {
+                                ${app.compilerArgs("-H")}
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling bonjour.h ====>")
+
+        and:
+        args("--info")
+        succeeds libraryCompileTaskName
+        skipped ":${generatePrefixHeaderTaskName}", ":${PCHCompileTaskName}"
+        ! output.contains("<==== compiling bonjour.h ====>")
+        ! output.contains("<==== compiling hello.h ====>")
+    }
+    
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "precompiled header compile detects changes in header files" () {
+        given:
+        settingsFile << "rootProject.name = 'test'"
+        app.getLibraryHeader().writeToDir(file("src/hello"))
+        app.getLibrarySources().find { it.name.startsWith("hello") }.writeToDir(file("src/hello"))
+        assert file("src/hello/headers/hello.h").exists()
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello(NativeLibrarySpec) {
+                        sources {
+                            ${app.sourceType}.preCompiledHeader "hello.h"
+                            ${app.sourceType}.preCompiledHeader "<${app.IOHeader}>"
+                        }
+                        binaries.all {
+                            if (toolChain.name == "visualCpp") {
+                                ${app.compilerArgs("/showIncludes")}
+                            } else {
+                                ${app.compilerArgs("-H")}
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling hello.h ====>")
+
+        when:
+        app.alternate.libraryHeader.writeToDir(file("src/hello"))
+
+        then:
+        args("--info")
+        succeeds PCHCompileTaskName
+        executed ":${generatePrefixHeaderTaskName}"
+        output.contains("<==== compiling althello.h ====>")
+    }
+
+    String getSuffix() {
+        return toolChain.displayName == "visual c++" ? "pch" : "h.gch"
+    }
+
+    String getUniquePragmaOutput(String message) {
+        if (toolChain.displayName == "clang") {
+            return "warning: ${message}"
+        } else if (toolChain.displayName.startsWith("gcc") || toolChain.displayName == "mingw") {
+            return "message: ${message}"
+        } else {
+            return message
+        }
+    }
+
+    String getPCHCompileTaskName() {
+        return "compileHelloSharedLibrary${StringUtils.capitalize(app.sourceType)}PreCompiledHeader"
+    }
+
+    String getGeneratePrefixHeaderTaskName() {
+        return "generate${StringUtils.capitalize(app.sourceType)}PrefixHeaderFile"
+    }
+
+    String getLibraryCompileTaskName() {
+        return "compileHelloSharedLibraryHello${StringUtils.capitalize(app.sourceType)}"
+    }
+
+    String getPCHHeaderDirName() {
+        return "build/objs/helloSharedLibrary/hello${StringUtils.capitalize(app.sourceType)}PreCompiledHeader"
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AutoTestedSamplesLanguageNativeIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AutoTestedSamplesLanguageNativeIntegrationTest.groovy
new file mode 100644
index 0000000..773b2fd
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AutoTestedSamplesLanguageNativeIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.language
+
+import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+class AutoTestedSamplesLanguageNativeIntegrationTest extends AbstractAutoTestedSamplesTest{
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/language-native/src/main")
+    }
+
+}
+
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy
new file mode 100644
index 0000000..6ec59ce
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.language.fixtures.app.*
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+
+// TODO add coverage for mixed sources
+class DuplicateBaseNamesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def "can have sourcefiles with same base name but different directories"() {
+        setup:
+        testApp.writeSources(file("src/main"))
+        buildFile.text = ""
+        testApp.plugins.each{ plugin ->
+            buildFile << "apply plugin: '$plugin'\n"
+        }
+
+        buildFile << """
+model {
+    platforms {
+        x86 {
+            architecture "i386"
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "x86"
+            binaries.all {
+                linker.args "-v"
+            }
+        }
+    }
+}
+            """
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == expectedOutput
+        where:
+        testApp                                              |   expectedOutput
+        new DuplicateCBaseNamesTestApp()                     |    "foo1foo2"
+        new DuplicateCppBaseNamesTestApp()                   |    "foo1foo2"
+        new DuplicateAssemblerBaseNamesTestApp(toolChain)    |    "foo1foo2"
+        new DuplicateMixedSameBaseNamesTestApp(toolChain)    |    "fooFromC\nfooFromCpp\nfooFromAsm\n"
+    }
+
+    /**
+     * TODO: need filter declaration to get this passed. Remove filter once
+     * story-language-source-sets-filter-source-files-by-file-extension
+     * is implemented
+     * */
+    def "can have sourcefiles with same base name in same directory"() {
+        setup:
+        def testApp = new DuplicateMixedSameBaseNamesTestApp(AbstractInstalledToolChainIntegrationSpec.toolChain)
+
+
+        testApp.getSourceFiles().each {  SourceFile sourceFile ->
+            file("src/main/all/${sourceFile.name}") << sourceFile.content
+        }
+
+        testApp.headerFiles.each { SourceFile sourceFile ->
+            file("src/main/headers/${sourceFile.name}") << sourceFile.content
+        }
+
+        buildFile.text = ""
+        testApp.plugins.each { plugin ->
+            buildFile << "apply plugin: '$plugin'\n"
+        }
+
+        buildFile << """
+model {
+    platforms {
+        x86 {
+            architecture "i386"
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "x86"
+            binaries.all {
+                linker.args "-v"
+            }
+            sources {"""
+
+        testApp.functionalSourceSets.each { name, filterPattern ->
+                buildFile << """
+                $name {
+                    source {
+                        include '$filterPattern'
+                        srcDirs "src/main/all"
+                    }
+                }"""
+        }
+
+        buildFile << """
+            }
+        }
+    }
+}
+"""
+
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == "fooFromC\nfooFromCpp\nfooFromAsm\n"
+    }
+
+    @Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+    def "can have objectiveC and objectiveCpp source files with same name in different directories"(){
+        setup:
+        testApp.writeSources(file("src/main"))
+        buildFile.text = ""
+        testApp.plugins.each{ plugin ->
+            buildFile << "apply plugin: '$plugin'\n"
+        }
+        buildFile << testApp.extraConfiguration
+
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+            """
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == "foo1foo2"
+        where:
+        testApp << [ new DuplicateObjectiveCBaseNamesTestApp(), new DuplicateObjectiveCppBaseNamesTestApp() ]
+    }
+
+    @RequiresInstalledToolChain(VisualCpp)
+    def "windows-resources can have sourcefiles with same base name but different directories"() {
+        setup:
+        def testApp = new DuplicateWindowsResourcesBaseNamesTestApp();
+        testApp.writeSources(file("src/main"))
+        buildFile.text = ""
+        testApp.plugins.each{ plugin ->
+            buildFile << "apply plugin: '$plugin'\n"
+        }
+        buildFile <<"""
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                linker.args "user32.lib"
+            }
+        }
+    }
+}
+            """
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == "foo1foo2"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..d20d628
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.assembler
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.IgnoreIf
+
+class AssemblyLanguageIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    HelloWorldApp app = new MixedLanguageHelloWorldApp(AbstractInstalledToolChainIntegrationSpec.toolChain)
+    TestFile asmSourceFile
+    def install
+
+    def "setup"() {
+        buildFile << """
+            plugins {
+                id 'assembler'
+                id 'c'
+                id 'cpp'
+            }
+
+            $app.extraConfiguration
+
+            model {
+                components {
+                    hello(NativeLibrarySpec)
+                    main(NativeExecutableSpec) {
+                        binaries.all {
+                            lib library: 'hello'
+                        }
+                    }
+                }
+            }
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        asmSourceFile = file("src/hello/asm/sum.s")
+
+        run "installMainExecutable"
+
+        install = installation("build/install/mainExecutable")
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "does not re-execute build with no change"() {
+        when:
+        run "mainExecutable"
+
+        then:
+        nonSkippedTasks.empty
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "reassembles binary with assembler option change"() {
+        when:
+        buildFile << """
+            model {
+                components {
+                    hello {
+                        binaries.all {
+                            if (toolChain in VisualCpp) {
+                                assembler.args '/Zf'
+                            } else {
+                                assembler.args '-W'
+                            }
+                        }
+                    }
+                }
+            }
+"""
+
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
+
+        and:
+        install.exec().out == app.englishOutput
+    }
+
+    @Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.CAN_INSTALL_EXECUTABLE])
+    def "reassembles binary with target platform change"() {
+        when:
+        buildFile.text = buildFile.text.replace("i386", "x86-64")
+
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
+
+        // TODO:DAZ Need to have valid x86-64 sources, so that we can verify the output: currently we're producing a binary that won't work on x86-64
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "cleans up stale object files when source file renamed"() {
+        def oldObjFile = objectFileFor(asmSourceFile, "build/objs/helloSharedLibrary/helloAsm")
+        def newObjFile = objectFileFor(file('src/hello/asm/changed_sum.s'), "build/objs/helloSharedLibrary/helloAsm")
+        assert oldObjFile.file
+        assert !newObjFile.file
+
+        when:
+        asmSourceFile.renameTo(file("src/hello/asm/changed_sum.s"))
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
+
+        and:
+        !oldObjFile.file
+        newObjFile.file
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "reassembles binary with source comment change"() {
+        when:
+        asmSourceFile << "# A comment at the end of the file\n"
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":assembleHelloSharedLibraryHelloAsm"
+    }
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..d0c9b8f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIntegrationTest.groovy
@@ -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.language.assembler
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
+
+import static org.gradle.util.Matchers.containsText
+
+class AssemblyLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new AssemblerWithCHelloWorldApp(AbstractInstalledToolChainIntegrationSpec.toolChain)
+
+    def "build fails when assemble fails"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        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("A build operation failed.")
+        failure.assertThatCause(containsText("Assembler failed while compiling broken.s"))
+    }
+
+    def "can manually define Assembler source sets"() {
+        given:
+        helloWorldApp.mainSource.writeToDir(file("src/main"))
+        helloWorldApp.getLibraryHeader().writeToDir(file("src/main"))
+        helloWorldApp.librarySources[0].writeToDir(file("src/main"))
+        file("src/main/sum-sources/sum.s") << helloWorldApp.librarySources[1].content
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                sumAsm(AssemblerSourceSet) {
+                    source {
+                        srcDir "src/main/sum-sources"
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+
+    static class AssemblerWithCHelloWorldApp extends MixedLanguageHelloWorldApp {
+        AssemblerWithCHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
+            super(toolChain)
+        }
+
+        @Override
+        List<String> getPluginList() {
+            return ['c', 'assembler']
+        }
+
+        @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/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginIntegrationTest.groovy
new file mode 100644
index 0000000..9b8d9d9
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.assembler.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class AssemblerLangPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy
new file mode 100644
index 0000000..8eb30b2
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/plugins/AssemblerPluginIntegrationTest.groovy
@@ -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.language.assembler.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class AssemblerPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CCallingMixedCAndCppLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CCallingMixedCAndCppLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..494f380
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CCallingMixedCAndCppLanguageIntegrationTest.groovy
@@ -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.language.c
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CCallingMixedCAndCppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+
+class CCallingMixedCAndCppLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+    HelloWorldApp helloWorldApp = new CCallingMixedCAndCppHelloWorldApp()
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..1fc44df
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/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.language.c
+import org.gradle.language.AbstractNativeLanguageIncrementalBuildIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+
+class CLanguageIncrementalBuildIntegrationTest extends AbstractNativeLanguageIncrementalBuildIntegrationTest {
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        new CHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIncrementalCompileIntegrationTest.groovy
new file mode 100644
index 0000000..bde0405
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIncrementalCompileIntegrationTest.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.language.c
+
+import org.gradle.language.AbstractNativeLanguageIncrementalCompileIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import spock.lang.Issue
+
+class CLanguageIncrementalCompileIntegrationTest extends AbstractNativeLanguageIncrementalCompileIntegrationTest {
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        return new CHelloWorldApp()
+    }
+
+    @Issue("GRADLE-3109")
+    def "recompiles source file that includes header file on first line"() {
+        given:
+        sourceFile << """#include "${otherHeaderFile.name}"
+"""
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..b00d082
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.c
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CCompilerDetectingTestApp
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import spock.lang.Issue
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.containsText
+// TODO:DAZ Some of these tests should apply to all single-language integration tests
+class CLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new CHelloWorldApp()
+
+    def "sources are compiled with C compiler"() {
+        def app = new CCompilerDetectingTestApp()
+
+        given:
+        app.writeSources(file('src/main'))
+
+        and:
+        buildFile << """
+            model {
+                components {
+                    main(NativeExecutableSpec)
+                }
+            }
+         """
+
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == app.expectedOutput(toolChain)
+    }
+
+    def "can manually define C source sets"() {
+        given:
+        helloWorldApp.getLibraryHeader().writeToDir(file("src/shared"))
+
+        file("src/main/c/main.c") << helloWorldApp.mainSource.content
+        file("src/main/c2/hello.c") << helloWorldApp.librarySources[0].content
+        file("src/main/sum-sources/sum.c") << helloWorldApp.librarySources[1].content
+
+        and:
+        buildFile << """
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    c {
+                        exportedHeaders {
+                            srcDirs "src/shared/headers"
+                        }
+                    }
+                    c2(CSourceSet) {
+                        exportedHeaders {
+                            srcDirs "src/shared/headers"
+                        }
+                    }
+                    c3(CSourceSet) {
+                        source {
+                            srcDir "src/main/sum-sources"
+                        }
+                        exportedHeaders {
+                            srcDirs "src/shared/headers"
+                        }
+                    }
+                }
+            }
+        }
+    }
+"""
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+    def "uses headers co-located with sources"() {
+        given:
+        // Write headers so they sit with sources
+        helloWorldApp.allFiles.each {
+            it.writeToFile(file("src/main/c/${it.name}"))
+        }
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.source.include "**/*.c"
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+    @Issue("GRADLE-2943")
+    @Unroll
+    def "can define macro #output"() {
+        given:
+        buildFile << """
+        model {
+            components {
+                main(NativeExecutableSpec) {
+                    binaries.all {
+                        ${helloWorldApp.compilerDefine('CUSTOM', inString)}
+                    }
+                }
+            }
+        }
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.getCustomOutput(output)
+
+        where:
+        inString                           | output
+        '"quoted"'                         | 'quoted'
+        '"with space"'                     | 'with space'
+        '"with\\\\"quote\\\\"internal"'    | 'with"quote"internal'
+        '"with \\\\"quote\\\\" and space"' | 'with "quote" and space'
+    }
+
+    def "compiler and linker args can contain quotes and spaces"() {
+        given:
+        buildFile << '''
+        model {
+            components {
+                main(NativeExecutableSpec) {
+                    binaries.all {
+                        // These are just some dummy arguments to test we don't blow up. Their effects are not verified.
+                        if (toolChain in VisualCpp) {
+                            cCompiler.args '/DVERSION="The version is \\'1.0\\'"'
+                            linker.args '/MANIFESTUAC:level=\\'asInvoker\\' uiAccess=\\'false\\''
+                        } else if (toolChain in Clang) {
+                            cCompiler.args '-frandom-seed="here is the \\'random\\' seed"'
+                            // TODO:DAZ Find something that works here (for all our CI machines)
+                            // linker.args '-Wl,-client_name,"a \\'client\\' name"'
+                        } else {
+                            cCompiler.args '-frandom-seed="here is the \\'random\\' seed"'
+                            // TODO:DAZ Find something that works on linux
+                            // linker.args '-Wl,--auxiliary,"an \\'auxiliary\\' name"'
+                        }
+                    }
+                }
+            }
+        }
+        '''
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        expect:
+        succeeds "mainExecutable"
+    }
+
+    def "build fails when compilation fails"() {
+        given:
+        buildFile << """
+            model {
+                components {
+                    main(NativeExecutableSpec)
+                }
+            }
+         """
+
+        and:
+        file("src/main/c/broken.c") << """
+        #include <stdio.h>
+
+        'broken
+"""
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.");
+        failure.assertHasCause("A build operation failed.")
+        failure.assertThatCause(containsText("C compiler failed while compiling broken.c"))
+    }
+
+    def "build fails when multiple compilations fail"() {
+        given:
+        def brokenFileCount = 5
+        buildFile << """
+            model {
+                components {
+                    main(NativeExecutableSpec)
+                }
+            }
+         """
+
+        and:
+        (1..brokenFileCount).each {
+            file("src/main/c/broken${it}.c") << """
+        #include <stdio.h>
+
+        'broken
+"""
+        }
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.");
+        failure.assertHasCause("Multiple build operations failed.")
+        (1..brokenFileCount).each {
+            failure.assertThatCause(containsText("C compiler failed while compiling broken${it}.c"))
+        }
+    }
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy
new file mode 100644
index 0000000..3b6da67
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c
+
+import org.gradle.nativeplatform.fixtures.app.CPCHHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.PCHHelloWorldApp
+import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
+
+class CPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
+    @Override
+    PCHHelloWorldApp getApp() {
+        return new CPCHHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CppCallingCLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CppCallingCLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..f454264
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CppCallingCLanguageIntegrationTest.groovy
@@ -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.language.c
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CppCallingCHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+
+class CppCallingCLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+    HelloWorldApp helloWorldApp = new CppCallingCHelloWorldApp()
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/MixedLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/MixedLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..7670e6f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/MixedLanguageIntegrationTest.groovy
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
+
+class MixedLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new MixedLanguageHelloWorldApp(toolChain)
+
+    def "can have all source files co-located in a common directory"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                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"
+                    }
+                }
+            }
+        }
+    }
+}
+        """
+
+        and:
+        helloWorldApp.allFiles.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
+    }
+
+    def "build and execute program with non-conventional source layout"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    source {
+                        srcDirs "source"
+                        include "**/*.cpp"
+                    }
+                    exportedHeaders {
+                        srcDirs "source/hello", "include"
+                    }
+                }
+                c {
+                    source {
+                        srcDirs "source", "include"
+                        include "**/*.c"
+                    }
+                    exportedHeaders {
+                        srcDirs "source/hello", "include"
+                    }
+                }
+            }
+        }
+    }
+}
+        """
+        settingsFile << "rootProject.name = 'test'"
+
+        and:
+        file("source", "hello", "hello.cpp") << """
+            #include <iostream>
+
+            void hello () {
+              std::cout << "${HelloWorldApp.HELLO_WORLD}";
+            }
+        """
+
+        and:
+        file("source", "hello", "french", "bonjour.c") << """
+            #include <stdio.h>
+            #include "otherProject/bonjour.h"
+
+            void bonjour() {
+                printf("${HelloWorldApp.HELLO_WORLD_FRENCH}");
+            }
+        """
+
+        and:
+        file("source", "hello", "hello.h") << """
+            void hello();
+        """
+
+        and:
+        file("source", "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
+        """
+        file("src", "main", "cpp", "bad.cpp") << """
+            THIS FILE WON'T BE COMPILED
+        """
+        file("src", "main", "headers", "hello.h") << """
+            THIS FILE WON'T BE INCLUDED
+        """
+
+        when:
+        run "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == HelloWorldApp.HELLO_WORLD + HelloWorldApp.HELLO_WORLD_FRENCH
+    }
+
+
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CLangPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CLangPluginIntegrationTest.groovy
new file mode 100644
index 0000000..c3ef5ab
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CLangPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CLangPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CPluginIntegrationTest.groovy
new file mode 100644
index 0000000..4f3f1af
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/plugins/CPluginIntegrationTest.groovy
@@ -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.language.c.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100755
index 0000000..b763dd9
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalBuildIntegrationTest.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.language.cpp
+
+import org.gradle.language.AbstractNativeLanguageIncrementalBuildIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+
+class CppLanguageIncrementalBuildIntegrationTest extends AbstractNativeLanguageIncrementalBuildIntegrationTest {
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        new CppHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalCompileIntegrationTest.groovy
new file mode 100644
index 0000000..ab9c039
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIncrementalCompileIntegrationTest.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.language.cpp
+
+import org.gradle.language.AbstractNativeLanguageIncrementalCompileIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+
+class CppLanguageIncrementalCompileIntegrationTest extends AbstractNativeLanguageIncrementalCompileIntegrationTest {
+     @Override
+     IncrementalHelloWorldApp getHelloWorldApp() {
+         return new CppHelloWorldApp()
+     }
+ }
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy
new file mode 100755
index 0000000..fd5b17e
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppCompilerDetectingTestApp
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+
+import static org.gradle.util.Matchers.containsText
+
+class CppLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new CppHelloWorldApp()
+
+    def "build fails when compilation fails"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+         """
+
+        and:
+        file("src/main/cpp/broken.cpp") << """
+        #include <iostream>
+
+        'broken
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainCpp'.");
+        failure.assertHasCause("A build operation failed.")
+        failure.assertThatCause(containsText("C\\+\\+ compiler failed while compiling broken.cpp"))
+    }
+
+    def "sources are compiled with C++ compiler"() {
+        def app = new CppCompilerDetectingTestApp()
+
+        given:
+        app.writeSources(file('src/main'))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+         """
+
+        expect:
+        succeeds "mainExecutable"
+        executable("build/binaries/mainExecutable/main").exec().out == app.expectedOutput(AbstractInstalledToolChainIntegrationSpec.toolChain)
+    }
+
+    def "can manually define C++ source sets"() {
+        given:
+        helloWorldApp.getLibraryHeader().writeToDir(file("src/shared"))
+
+        file("src/main/cpp/main.cpp") << helloWorldApp.mainSource.content
+        file("src/main/cpp2/hello.cpp") << helloWorldApp.librarySources[0].content
+        file("src/main/sum-sources/sum.cpp") << helloWorldApp.librarySources[1].content
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    exportedHeaders {
+                        srcDirs "src/shared/headers"
+                    }
+                }
+                cpp2(CppSourceSet) {
+                    exportedHeaders {
+                        srcDirs "src/shared/headers"
+                    }
+                }
+                cpp3(CppSourceSet) {
+                    source {
+                        srcDir "src/main/sum-sources"
+                    }
+                    exportedHeaders {
+                        srcDirs "src/shared/headers"
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.englishOutput
+    }
+
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy
new file mode 100644
index 0000000..2e99012
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp
+
+import org.gradle.nativeplatform.fixtures.app.CppPCHHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.PCHHelloWorldApp
+import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
+
+class CppPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
+    @Override
+    PCHHelloWorldApp getApp() {
+        return new CppPCHHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppLangPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppLangPluginIntegrationTest.groovy
new file mode 100644
index 0000000..1e48fd8
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppLangPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CppLangPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppPluginIntegrationTest.groovy
new file mode 100644
index 0000000..14860a8
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/plugins/CppPluginIntegrationTest.groovy
@@ -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.language.cpp.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CppPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy
new file mode 100644
index 0000000..0ad587e
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateAssemblerBaseNamesTestApp.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateAssemblerBaseNamesTestApp extends TestNativeComponent{
+
+    AvailableToolChains.InstalledToolChain toolChain
+
+    DuplicateAssemblerBaseNamesTestApp(AvailableToolChains.InstalledToolChain toolChain) {
+        this.toolChain = toolChain
+    }
+
+    def plugins = ["c", "assembler"]
+
+    @Override
+    List<SourceFile> getSourceFiles() {
+        return  [
+            sourceFile("c", "main.c", """
+            #include <stdio.h>
+
+            // Ensure consistent asm name mapping on all platforms
+            #if !defined(_MSC_VER)
+            extern int sum1(int a, int b) asm("_sum1");
+            extern int sum2(int a, int b) asm("_sum2");
+            #endif
+
+            int main () {
+                printf("foo%dfoo%d", sum1(1, 0), sum2(1, 1));
+                fflush(stdout);
+                return 0;
+            }
+
+            """),
+            sourceFile("asm", "foo1/sum.s", getAsmSource("sum1")),
+            sourceFile("asm", "foo2/sum.s", getAsmSource("sum2"))
+        ]
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        []
+    }
+
+    protected def getAsmSource(String methodName) {
+        if (toolChain.isVisualCpp()) {
+            return """
+.386
+.model    flat
+
+PUBLIC    _${methodName}
+_TEXT     SEGMENT
+_${methodName}    PROC
+mov    eax, DWORD PTR 4[esp]
+add    eax, DWORD PTR 8[esp]
+ret    0
+_${methodName}    ENDP
+_TEXT   ENDS
+END
+"""
+        }else{
+            return """
+.text
+.globl  _${methodName}
+_${methodName}:
+movl    8(%esp), %eax
+addl    4(%esp), %eax
+ret
+"""
+
+        }
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCBaseNamesTestApp.groovy
new file mode 100644
index 0000000..8e50dd3
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCBaseNamesTestApp.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateCBaseNamesTestApp extends TestNativeComponent {
+
+    def plugins = ["c"]
+
+    @Override
+    List<SourceFile> getSourceFiles() {
+        [sourceFile("c", "main.c", """
+            #include <stdio.h>
+            #include "foo.h"
+            int main () {
+               foo1();
+               foo2();
+               return 0;
+            }
+        """),
+
+        sourceFile("c/foo1", "foo.c", """
+            #include <stdio.h>
+            #include "foo.h"
+
+            void foo1() {
+                printf("foo1");
+            }
+        """),
+
+        sourceFile("c/foo2", "foo.c", """
+            #include <stdio.h>
+            #include "foo.h"
+
+            void foo2() {
+                printf("foo2");
+            }
+        """)]
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        [sourceFile("headers", "foo.h", """
+           void foo1();
+           void foo2();
+           """)]
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCppBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCppBaseNamesTestApp.groovy
new file mode 100644
index 0000000..7c6ac4f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateCppBaseNamesTestApp.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateCppBaseNamesTestApp extends TestNativeComponent {
+
+    def plugins = ["cpp"]
+
+    @Override
+    List<SourceFile> getSourceFiles() {
+        [sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            #include "foo.h"
+            using namespace std;
+            int main () {
+               foo1();
+               foo2();
+               return 0;
+            }
+        """),
+         sourceFile("cpp/foo1", "foo.cpp", """
+            #include <iostream>
+            #include "foo.h"
+            using namespace std;
+
+            void foo1() {
+                cout << "foo1";
+            }
+        """),
+
+         sourceFile("cpp/foo2", "foo.cpp", """
+            #include <iostream>
+            #include "foo.h"
+            using namespace std;
+
+            void foo2() {
+                cout << "foo2";
+            }
+        """)]
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        [sourceFile("headers", "foo.h", """
+           void foo1();
+           void foo2();
+           """)
+         ]
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy
new file mode 100644
index 0000000..5dcf7c7
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateMixedSameBaseNamesTestApp.groovy
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent;
+
+
+// TODO integrate objective-c/cpp we have coverage on windows
+public class DuplicateMixedSameBaseNamesTestApp extends TestNativeComponent {
+
+    AvailableToolChains.InstalledToolChain toolChain
+
+    public DuplicateMixedSameBaseNamesTestApp(AvailableToolChains.InstalledToolChain toolChain) {
+        this.toolChain = toolChain
+    }
+
+    def plugins = ["assembler", "c", "cpp"]
+
+    def functionalSourceSets = [asm:'**/*.s', c:'**/*.c', cpp:'**/*.cpp']
+
+    @Override
+    public List<SourceFile> getSourceFiles() {
+        [sourceFile("c", "main.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            // Ensure consistent asm name mapping on all platforms
+            #if !defined(_MSC_VER)
+            extern void sayFooFromAsm() asm("_sayFooFromAsm");
+            #endif
+
+            int main () {
+                sayFooFromC();
+                sayFooFromCpp();
+                //sayFooFromObjectiveC //TODO RG
+                //sayFooFromObjectiveCpp //TODO RG
+                sayFooFromAsm();
+                return 0;
+            }"""),
+
+                sourceFile("c", "foo.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            void sayFooFromC() {
+                printf("fooFromC\\n");
+            }
+        """),
+           sourceFile("cpp", "foo.cpp", """
+            #include "hello.h"
+            #include <iostream>
+            using namespace std;
+
+            void sayFooFromCpp() {
+                cout << "fooFromCpp" << std::endl;;
+            }
+
+        """),
+                sourceFile("asm", "foo.s", asmSource())
+        ];
+    }
+
+    String asmSource() {
+        if (toolChain.isVisualCpp()) {
+            return """
+.386
+;.model flat
+.model small,c
+.data
+msg db "fooFromAsm", 10, 0
+.code
+includelib MSVCRT
+extrn printf:near
+extrn exit:NEAR
+public sayFooFromAsm
+sayFooFromAsm proc
+push    offset msg
+call    printf
+mov eax,0
+push eax
+call exit
+sayFooFromAsm endp
+end
+"""
+        } else {
+            return """
+.text
+
+LC0:
+.ascii "fooFromAsm\\12\\0"
+.globl _sayFooFromAsm
+
+_sayFooFromAsm:
+        pushl   %ebp
+        movl    %esp, %ebp
+        subl    \$8, %esp
+        andl    \$-16, %esp
+        movl    \$0, %eax
+        movl    %eax, -4(%ebp)
+        movl    -4(%ebp), %eax
+        movl    \$LC0, (%esp)
+        call    ${(OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) ? '_printf' : 'printf'}
+        movl    \$0, %eax
+        leave
+        ret
+        """
+        }
+    }
+
+    @Override
+    public List<SourceFile> getHeaderFiles() {
+        [sourceFile("headers", "hello.h", """
+            #ifdef __cplusplus
+            extern "C" {
+            #endif
+            void sayFooFromCpp();
+            #ifdef __cplusplus
+            }
+            #endif
+            void sayFooFromC();
+            void sayFooFromAsm();
+            //void sayFooFromObjectiveC();
+            //void sayFooFromObjectiveCpp();
+           """)
+        ]
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy
new file mode 100644
index 0000000..372a87a
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCBaseNamesTestApp.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateObjectiveCBaseNamesTestApp extends TestNativeComponent{
+
+    def plugins = ["objective-c"]
+    @Override
+    List<SourceFile> getSourceFiles() {
+        [sourceFile("objc", "main.m", """
+            #import <Foundation/Foundation.h>
+            #import "foo.h"
+
+            int main (int argc, const char * argv[])
+            {
+                sayFoo1();
+                sayFoo2();
+                return 0;
+            }
+        """),
+          fooSource(1),
+          fooSource(2)]
+    }
+
+    public String getExtraConfiguration() {
+        return """
+            binaries.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        [sourceFile("headers", "foo.h", """
+            void sayFoo1();
+            void sayFoo2();
+           """)
+        ]
+    }
+
+    SourceFile fooSource(int index) {
+        sourceFile("objc/foo$index", "foo.m", """
+            #import <Foundation/Foundation.h>
+            #import "foo.h"
+
+            void sayFoo$index()
+            {
+                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+                NSData *strData = [@"foo$index" dataUsingEncoding: NSASCIIStringEncoding];
+                [stdout writeData: strData];
+            }
+        """)
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy
new file mode 100644
index 0000000..5a9fe5f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateObjectiveCppBaseNamesTestApp.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateObjectiveCppBaseNamesTestApp extends TestNativeComponent{
+    def plugins = ["objective-cpp"]
+    @Override
+    List<SourceFile> getSourceFiles() {
+        [sourceFile("objcpp", "main.mm", """
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #import <Foundation/Foundation.h>
+            #import "foo.h"
+
+            int main (int argc, const char * argv[])
+            {
+                sayFoo1();
+                sayFoo2();
+                return 0;
+            }
+        """),
+            sourceFile("objcpp/foo1", "foo.mm", """
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #import <Foundation/Foundation.h>
+            #import "foo.h"
+
+            void sayFoo1()
+            {
+                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+                NSData *strData = [@"foo1" dataUsingEncoding: NSASCIIStringEncoding];
+                [stdout writeData: strData];
+            }
+        """),
+                sourceFile("objcpp/foo2", "foo.mm", """
+            #import <iostream>
+            #import "foo.h"
+
+            void sayFoo2()
+            {
+                std::cout << "foo2";
+            }
+        """)]
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        [sourceFile("headers", "foo.h", """
+            void sayFoo1();
+            void sayFoo2();
+           """)
+        ]
+    }
+
+    public String getExtraConfiguration() {
+        return """
+            binaries.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy
new file mode 100644
index 0000000..c97a566
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/fixtures/app/DuplicateWindowsResourcesBaseNamesTestApp.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+
+class DuplicateWindowsResourcesBaseNamesTestApp extends TestNativeComponent {
+
+    def plugins = ["cpp","windows-resources"]
+
+    @Override
+    List<SourceFile> getSourceFiles() {
+        [sourceFile("cpp", "main.cpp", """
+#include "hello.h"
+
+int main () {
+    hello();
+    return 0;
+}
+"""),
+         sourceFile("cpp", "hello.cpp", """
+#include <iostream>
+#include <windows.h>
+#include <string>
+#include "hello.h"
+
+std::string LoadStringFromResource(UINT stringID)
+{
+    HINSTANCE instance = GetModuleHandle("hello");
+    WCHAR * pBuf = NULL;
+    int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
+    std::wstring wide = std::wstring(pBuf, len);
+    return std::string(wide.begin(), wide.end());
+}
+
+void hello() {
+    std::string foo1 = LoadStringFromResource(IDS_FOO1);
+    std::string foo2 = LoadStringFromResource(IDS_FOO2);
+    std::cout << foo1;
+    std::cout << foo2;
+}
+"""),
+        sourceFile("rc/dir1", "resources.rc", """
+#include "hello.h"
+
+STRINGTABLE
+{
+    IDS_FOO1, "foo1"
+}
+"""),
+        sourceFile("rc/dir2", "resources.rc", """
+#include "hello.h"
+
+STRINGTABLE
+{
+    IDS_FOO2, "foo2"
+}
+""")]
+
+    }
+
+    @Override
+    List<SourceFile> getHeaderFiles() {
+        return [sourceFile("headers", "hello.h", """
+#define IDS_FOO1    111
+#define IDS_FOO2    1000
+
+void hello();
+""")]
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..e32f665
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.IgnoreIf
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class NativeLanguageSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule final TestNameTestDirectoryProvider testDirProvider = new TestNameTestDirectoryProvider()
+    @Rule public final Sample assembler = sample(testDirProvider, 'assembler')
+    @Rule public final Sample c = sample(testDirProvider, 'c')
+    @Rule public final Sample cpp = sample(testDirProvider, 'cpp')
+    @Rule public final Sample objectiveC = sample(testDirProvider, 'objective-c')
+    @Rule public final Sample objectiveCpp = sample(testDirProvider, 'objective-cpp')
+    @Rule public final Sample customLayout = sample(testDirProvider, 'custom-layout')
+    @Rule public final Sample windowsResources = sample(testDirProvider, 'windows-resources')
+    @Rule public final Sample idl = sample(testDirProvider, 'idl')
+    @Rule public final Sample cunit = sample(testDirProvider, 'cunit')
+
+    private static Sample sample(TestDirectoryProvider testDirectoryProvider, String name) {
+        return new Sample(testDirectoryProvider, "native-binaries/${name}", name)
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "assembler"() {
+        given:
+        sample assembler
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        nonSkippedTasks.count { it.startsWith(":assembleMainExecutable") } == 1
+        executedAndNotSkipped ":compileMainExecutableMainC", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(assembler.dir.file("build/install/mainExecutable")).exec().out == "5 + 7 = 12\n"
+    }
+
+    def "c"() {
+        given:
+        sample c
+        
+        when:
+        run "installMainExecutable"
+        
+        then:
+        executedAndNotSkipped ":compileHelloSharedLibraryHelloC", ":linkHelloSharedLibrary", ":helloSharedLibrary",
+                              ":compileMainExecutableMainC", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(c.dir.file("build/install/mainExecutable")).exec().out == "Hello world!"
+    }
+
+    def "cpp"() {
+        given:
+        sample cpp
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileHelloSharedLibraryHelloCpp", ":linkHelloSharedLibrary", ":helloSharedLibrary",
+                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(cpp.dir.file("build/install/mainExecutable")).exec().out == "Hello world!\n"
+    }
+
+    @Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+    def "objectiveC"() {
+        given:
+        sample objectiveC
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainObjc", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        executable(objectiveC.dir.file("build/binaries/mainExecutable/main")).exec().out == "Hello world!\n"
+    }
+
+    @Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+    def "objectiveCpp"() {
+        given:
+        sample objectiveCpp
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainObjcpp", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        executable(objectiveCpp.dir.file("build/binaries/mainExecutable/main")).exec().out == "Hello world!\n"
+    }
+
+    @RequiresInstalledToolChain(VisualCpp)
+    def "win rc"() {
+        given:
+        sample windowsResources
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileHelloSharedLibraryHelloCpp", ":compileHelloSharedLibraryHelloRc",
+                              ":linkHelloSharedLibrary", ":helloSharedLibrary",
+                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(windowsResources.dir.file("build/install/mainExecutable")).exec().out == "Hello world!\n"
+
+        when:
+        executer.usingBuildScript(windowsResources.dir.file('build-resource-only-dll.gradle'))
+        run "helloResSharedLibrary"
+
+        then:
+        file(windowsResources.dir.file("build/binaries/helloResSharedLibrary/helloRes.dll")).assertExists()
+    }
+
+    def "custom layout"() {
+        given:
+        sample customLayout
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileHelloStaticLibraryHelloC", ":createHelloStaticLibrary", ":helloStaticLibrary",
+                              ":compileMainExecutableMainCpp", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(customLayout.dir.file("build/install/mainExecutable")).exec().out == "Hello world!"
+    }
+
+    def "idl"() {
+        given:
+        sample idl
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executedAndNotSkipped ":idl", ":compileMainExecutableMainC", ":compileMainExecutableMainIdlOutput",
+                              ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        installation(idl.dir.file("build/install/mainExecutable")).exec().out == "Hello from generated source!!\n"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/ParallelNativePluginsIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/ParallelNativePluginsIntegrationTest.groovy
new file mode 100644
index 0000000..3ada7b3
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/ParallelNativePluginsIntegrationTest.groovy
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform
+
+import org.gradle.execution.taskgraph.DefaultTaskExecutionPlan
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.ExecutableFixture
+import org.gradle.nativeplatform.fixtures.NativeInstallationFixture
+import org.gradle.nativeplatform.fixtures.app.*
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.IgnoreIf
+
+ at IgnoreIf({ GradleContextualExecuter.parallel })
+// no point, always runs in parallel
+class ParallelNativePluginsIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def setup() {
+        executer.withArgument("--parallel")
+                .withArgument("--max-workers=4")
+                .withArgument("-D${DefaultTaskExecutionPlan.INTRA_PROJECT_TOGGLE}=true")
+    }
+
+    @Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+    def "can produce multiple executables from a single project in parallel"() {
+        given:
+        Map<String, HelloWorldApp> apps = [
+                c              : new CHelloWorldApp(),
+                cpp            : new CppHelloWorldApp(),
+                objectiveC     : new ObjectiveCHelloWorldApp(),
+                objectiveCpp   : new ObjectiveCppHelloWorldApp(),
+                mixedObjectiveC: new MixedObjectiveCHelloWorldApp(),
+        ]
+
+        apps.each { name, app ->
+            buildFile << app.pluginScript
+            buildFile << app.getExtraConfiguration("${name}Executable")
+            buildFile << """
+                model {
+                    components {
+                        ${name}(NativeExecutableSpec)
+                    }
+                }
+            """
+
+            app.writeSources(file("src/$name"))
+        }
+
+        when:
+        run(*apps.keySet().collect { "${it}Executable" })
+
+        then:
+        Map<ExecutableFixture, HelloWorldApp> executables = apps.collectEntries { name, app ->
+            def executable = executable("build/binaries/${name}Executable/$name")
+            executable.assertExists()
+            [executable, app]
+        }
+        executables.every { executable, app ->
+            executable.exec().out == app.englishOutput
+        }
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "can produce multiple executables that use a library from a single project in parallel"() {
+        given:
+        Map<String, ExeWithLibraryUsingLibraryHelloWorldApp> apps = [
+                first : new ExeWithLibraryUsingLibraryHelloWorldApp(),
+                second: new ExeWithLibraryUsingLibraryHelloWorldApp(),
+                third : new ExeWithLibraryUsingLibraryHelloWorldApp(),
+        ]
+
+        apps.each { name, app ->
+            buildFile << app.pluginScript
+            buildFile << app.extraConfiguration
+
+            app.executable.writeSources(file("src/${name}Main"))
+            app.library.writeSources(file("src/${name}Hello"))
+            app.greetingsHeader.writeToDir(file("src/${name}Hello"))
+            app.greetingsSources*.writeToDir(file("src/${name}Greetings"))
+
+            buildFile << """
+                // Allow static libraries to be linked into shared
+                binaries.withType(StaticLibraryBinarySpec) {
+                    if (toolChain in Gcc || toolChain in Clang) {
+                        cppCompiler.args '-fPIC'
+                    }
+                }
+
+                model {
+                    components {
+                        ${name}Main(NativeExecutableSpec) {
+                            sources {
+                                cpp.lib library: '${name}Hello'
+                            }
+                        }
+                        ${name}Hello(NativeLibrarySpec) {
+                            sources {
+                                cpp.lib library: '${name}Greetings', linkage: 'static'
+                            }
+                        }
+                        ${name}Greetings(NativeLibrarySpec) {
+                            sources {
+                                cpp.lib library: '${name}Hello', linkage: 'api'
+                            }
+                        }
+                    }
+                }
+            """
+        }
+
+        when:
+        run(*apps.keySet().collect { "install${it.capitalize()}MainExecutable" })
+
+        then:
+        Map<NativeInstallationFixture, HelloWorldApp> installations = apps.collectEntries { name, app ->
+            def installation = installation("build/install/${name}MainExecutable")
+            [installation, app]
+        }
+        installations.every { installation, app ->
+            installation.exec().out == app.englishOutput
+        }
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy
new file mode 100644
index 0000000..89c56de
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.MixedObjectiveCHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+//TODO find a better name
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class MixedObjectiveCIntegrationTest extends AbstractNativeLanguageIntegrationTest{
+
+    @Override
+    HelloWorldApp getHelloWorldApp() {
+        return new MixedObjectiveCHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..26c156b
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.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.language.objectivec
+import org.gradle.internal.hash.HashUtil
+import org.gradle.language.AbstractNativeLanguageIncrementalBuildIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Ignore
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCLanguageIncrementalBuildIntegrationTest extends AbstractNativeLanguageIncrementalBuildIntegrationTest{
+
+    @Override
+    boolean isCanBuildForMultiplePlatforms() {
+        false
+    }
+
+    @Ignore("Demos a problem with clang on ubuntu creating randomly different object files")
+    def "generates always exactly same object file"() {
+        setup:
+        def recordings = []
+        def invocation =  10
+        when:
+        invocation.times{
+            run "cleanCompileHelloSharedLibraryHello$sourceType", "compileHelloSharedLibraryHello$sourceType"
+            def oldHash= HashUtil.sha1(objectFileFor(librarySourceFiles[0], "build/objs/helloSharedLibrary/hello$sourceType")).asCompactString()
+
+            //to ensure it's not a timestamp issue
+            sleep(1000)
+            run "cleanCompileHelloSharedLibraryHello$sourceType", "compileHelloSharedLibraryHello$sourceType"
+            def newHash = HashUtil.sha1(objectFileFor(librarySourceFiles[0], "build/objs/helloSharedLibrary/hello$sourceType")).asCompactString()
+            recordings << (oldHash == newHash)
+        }
+        then:
+        recordings.findAll{ it }.size() != 0 // not everytime the .o file differs -> not a timestamp issue
+        recordings.findAll{ it }.size() == invocation
+    }
+
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
new file mode 100644
index 0000000..c5c070b
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec
+
+import org.gradle.language.AbstractNativeLanguageIncrementalCompileIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCLanguageIncrementalCompileIntegrationTest extends AbstractNativeLanguageIncrementalCompileIntegrationTest {
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCHelloWorldApp()
+    }
+
+    def "recompiles only source file that imported changed header file"() {
+        given:
+        sourceFile << """
+            #import "${otherHeaderFile.name}"
+"""
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "source is always recompiled if it imported header via macro"() {
+        given:
+        sourceFile << """
+            #define MY_HEADER "${otherHeaderFile.name}"
+            #import MY_HEADER
+"""
+
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        otherHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+
+        when: "Header that is NOT included is changed"
+        file("src/main/headers/notIncluded.h") << """
+            // Dummy header file
+"""
+        and:
+        run "mainExecutable"
+
+        then: "Source is still recompiled"
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+
+    def "recompiles source file when transitively imported header file is changed"() {
+        given:
+        def transitiveHeaderFile = file("src/main/headers/transitive.h") << """
+           // Dummy header file
+"""
+        otherHeaderFile << """
+            #import "${transitiveHeaderFile.name}"
+"""
+        sourceFile << """
+            #import "${otherHeaderFile.name}"
+"""
+
+        and:
+        outputs.snapshot { run "mainExecutable" }
+
+        when:
+        transitiveHeaderFile << """
+            // Some extra content
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped compileTask
+
+        and:
+        outputs.recompiledFile sourceFile
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..1c6c355
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.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.language.objectivec
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest{
+
+    @Override
+    HelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCHelloWorldApp()
+    }
+
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy
new file mode 100644
index 0000000..97752c0
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec
+
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCPCHHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.PCHHelloWorldApp
+import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
+    @Override
+    PCHHelloWorldApp getApp() {
+        return new ObjectiveCPCHHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy
new file mode 100644
index 0000000..667551f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCUnsupportedIntegrationTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
+
+import static org.hamcrest.CoreMatchers.containsString
+
+ at RequiresInstalledToolChain(ToolChainRequirement.VisualCpp)
+class ObjectiveCUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec{
+
+    def helloWorldApp = new ObjectiveCHelloWorldApp();
+
+    def "setup"() {
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+    }
+
+    def "fails with decent error message with visual studio toolchain"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        fails "compileMainExecutableMainObjc"
+
+        then:
+        failure.assertThatCause(containsString("Objective-C is not available on the Visual C++ toolchain"))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginIntegrationTest.groovy
new file mode 100644
index 0000000..449362a
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class ObjectiveCLangPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.groovy
new file mode 100644
index 0000000..019b322
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginIntegrationTest.groovy
@@ -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.language.objectivec.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class ObjectiveCPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..ae605f0
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp
+
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
+import org.gradle.language.objectivec.ObjectiveCLanguageIncrementalBuildIntegrationTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCppLanguageIncrementalBuildIntegrationTest  extends ObjectiveCLanguageIncrementalBuildIntegrationTest{
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCppHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
new file mode 100644
index 0000000..4ebf162
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp
+
+import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
+import org.gradle.language.objectivec.ObjectiveCLanguageIncrementalCompileIntegrationTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCppLanguageIncrementalCompileIntegrationTest extends ObjectiveCLanguageIncrementalCompileIntegrationTest {
+    @Override
+    IncrementalHelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCppHelloWorldApp()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..829081d
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.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.language.objectivecpp
+
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCppLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    @Override
+    HelloWorldApp getHelloWorldApp() {
+        return new ObjectiveCppHelloWorldApp()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy
new file mode 100644
index 0000000..6ea1866
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp
+
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCppPCHHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.PCHHelloWorldApp
+import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.OBJECTIVE_C_SUPPORT)
+class ObjectiveCppPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
+    @Override
+    PCHHelloWorldApp getApp() {
+        return new ObjectiveCppPCHHelloWorldApp()
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy
new file mode 100644
index 0000000..97a3c5c
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppUnsupportedIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
+
+import static org.hamcrest.CoreMatchers.containsString
+
+
+ at RequiresInstalledToolChain(ToolChainRequirement.VisualCpp)
+class ObjectiveCppUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec{
+
+    def helloWorldApp = new ObjectiveCppHelloWorldApp();
+
+    def "setup"() {
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+    }
+
+    def "fails with decent error message with visual studio toolchain"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        fails "compileMainExecutableMainObjcpp"
+
+        then:
+        failure.assertThatCause(containsString("Objective-C++ is not available on the Visual C++ toolchain"))
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginIntegrationTest.groovy
new file mode 100644
index 0000000..eeeeecd
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class ObjectiveCppLangPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.groovy
new file mode 100644
index 0000000..d2ee898
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginIntegrationTest.groovy
@@ -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.language.objectivecpp.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class ObjectiveCppPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..868898f
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.internal.CompilerOutputFileNamingScheme
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.ExecutableFixture
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.WindowsResourceHelloWorldApp
+import spock.lang.IgnoreIf
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+
+ at RequiresInstalledToolChain(VisualCpp)
+class WindowsResourcesIncrementalBuildIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    HelloWorldApp helloWorldApp = new WindowsResourceHelloWorldApp()
+    ExecutableFixture mainExe
+    def mainResourceFile
+    def unusedHeaderFile
+
+    def "setup"() {
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        helloWorldApp.writeSources(file("src/main"))
+        unusedHeaderFile = file("src/main/headers/unused.h") << """
+    #define DUMMY_HEADER_FILE
+"""
+
+        run "mainExecutable"
+
+        mainExe = executable("build/binaries/mainExecutable/main")
+        mainResourceFile = file("src/main/rc/resources.rc")
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "does not re-compile sources with no change"() {
+        when:
+        run "mainExecutable"
+
+        then:
+        nonSkippedTasks.empty
+    }
+
+    def "compiles and links when resource source changes"() {
+        when:
+        file("src/main/rc/resources.rc").text = """
+#include "hello.h"
+
+STRINGTABLE
+{
+    IDS_HELLO, "Goodbye"
+}
+"""
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainRc", ":linkMainExecutable", ":mainExecutable"
+
+        and:
+        mainExe.exec().out == "Goodbye"
+    }
+
+    def "compiles and but does not link when resource source changes with comment only"() {
+        when:
+        file("src/main/rc/resources.rc") << """
+// Comment added to the end of the resource file
+"""
+
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainRc"
+        skipped ":linkMainExecutable", ":mainExecutable"
+    }
+
+    def "compiles and links when resource compiler arg changes"() {
+        when:
+        buildFile << """
+model {
+    components {
+        main {
+            binaries.all {
+                // Use a compiler arg that will change the generated .res file
+                rcCompiler.args "-DFRENCH"
+            }
+        }
+    }
+}
+"""
+        and:
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainRc", ":linkMainExecutable", ":mainExecutable"
+    }
+
+    def "stale .res files are removed when a resource source file is renamed"() {
+        setup:
+        def outputFileNameScheme = new CompilerOutputFileNamingScheme()
+                .withOutputBaseFolder(file("build/objs/mainExecutable/mainRc"))
+                .withObjectFileNameSuffix(".res")
+        def oldResFile = outputFileNameScheme.map(mainResourceFile)
+        def newResFile = outputFileNameScheme.map(file('src/main/rc/changed_resources.rc'))
+        assert oldResFile.file
+        assert !newResFile.file
+
+        when:
+        mainResourceFile.renameTo(file("src/main/rc/changed_resources.rc"))
+        run "mainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainRc"
+
+        and:
+        !oldResFile.file
+        newResFile.file
+    }
+
+    def "recompiles resource when included header is changed"() {
+
+        given: "set the generated res file timestamp to zero"
+        def outputFileNameScheme = new CompilerOutputFileNamingScheme()
+                .withOutputBaseFolder(file("build/objs/mainExecutable/mainRc"))
+                .withObjectFileNameSuffix(".res")
+        def resourceFile = outputFileNameScheme.map(mainResourceFile)
+
+        resourceFile.lastModified = 0
+        when: "Unused header is changed"
+        unusedHeaderFile << """
+    #define EXTRA_DEFINE
+"""
+        and:
+        run "mainExecutable"
+
+        then: "No resource compilation"
+        skipped ":compileMainExecutableMainRc"
+        resourceFile.lastModified() == 0
+
+        when:
+        file("src/main/headers/hello.h") << """
+    #define EXTRA_DEFINE
+"""
+        and:
+        run "mainExecutable"
+
+        then: "Resource is recompiled"
+        executedAndNotSkipped ":compileMainExecutableMainRc"
+        resourceFile.lastModified() > 0
+    }
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIntegrationTest.groovy
new file mode 100755
index 0000000..8087ce9
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesIntegrationTest.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc
+
+import org.apache.commons.lang.RandomStringUtils
+import org.gradle.language.AbstractNativeLanguageIntegrationTest
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.WindowsResourceHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import spock.lang.Ignore
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+import static org.gradle.util.Matchers.containsText
+
+ at RequiresInstalledToolChain(VisualCpp)
+class WindowsResourcesIntegrationTest extends AbstractNativeLanguageIntegrationTest {
+
+    HelloWorldApp helloWorldApp = new WindowsResourceHelloWorldApp()
+
+    def "user receives a reasonable error message when resource compilation fails"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+         """
+
+        and:
+        file("src/main/rc/broken.rc") << """
+        #include <stdio.h>
+
+        NOT A VALID RESOURCE
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainRc'.");
+        failure.assertHasCause("A build operation failed.")
+        failure.assertThatCause(containsText("Windows resource compiler failed while compiling broken.rc"))
+    }
+
+    def "can create resources-only shared library"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "resources"
+            }
+        }
+        resources(NativeLibrarySpec) {
+            binaries.all {
+                linker.args "/noentry", "/machine:x86"
+            }
+        }
+    }
+}
+"""
+
+        and:
+        file("src/resources/rc/resources.rc") << """
+            #include "resources.h"
+
+            STRINGTABLE
+            {
+                IDS_HELLO, "Hello!"
+            }
+        """
+
+        and:
+        file("src/resources/headers/resources.h") << """
+            #define IDS_HELLO    111
+        """
+
+        and:
+        file("src/main/cpp/main.cpp") << """
+            #include <iostream>
+            #include <windows.h>
+            #include <string>
+            #include "resources.h"
+
+            std::string LoadStringFromResource(UINT stringID)
+            {
+                HMODULE instance = LoadLibraryEx("resources.dll", NULL, LOAD_LIBRARY_AS_DATAFILE);
+                WCHAR * pBuf = NULL;
+                int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
+                std::wstring wide = std::wstring(pBuf, len);
+                return std::string(wide.begin(), wide.end());
+            }
+
+            int main() {
+                std::string hello = LoadStringFromResource(IDS_HELLO);
+                std::cout << hello;
+                return 0;
+            }
+        """
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        resourceOnlyLibrary("build/binaries/resourcesSharedLibrary/resources").assertExists()
+        installation("build/install/mainExecutable").exec().out == "Hello!"
+    }
+
+    @Ignore
+    def "windows resources compiler can use long file paths"() {
+        // windows can't handle a path up to 260 characters
+        // we create a project path that is ~180 characters to end up
+        // with a path for the compiled resources.res > 260 chars
+        def projectPathOffset = 180 - testDirectory.getAbsolutePath().length()
+        def nestedProjectPath = RandomStringUtils.randomAlphanumeric(projectPathOffset-10) + "/123456789"
+
+        setup:
+        def deepNestedProjectFolder = file(nestedProjectPath)
+        executer.usingProjectDirectory(deepNestedProjectFolder)
+        def TestFile buildFile = deepNestedProjectFolder.file("build.gradle")
+        buildFile << helloWorldApp.pluginScript
+        buildFile << helloWorldApp.extraConfiguration
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("$nestedProjectPath/src/main"));
+
+        expect:
+        // this test is just for verifying explicitly the behaviour of the windows resource compiler
+        // that's why we explicitly trigger this task instead of main.
+        succeeds "mainExecutable"
+    }
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy
new file mode 100755
index 0000000..1d92421
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class WindowsResourcesUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    HelloWorldApp helloWorldApp = new CppHelloWorldApp()
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "resource files are ignored on unsupported platforms"() {
+        given:
+        buildFile << """
+plugins {
+    id 'cpp'
+    id 'windows-resources'
+}
+
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+         """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+        file("src/main/rc/broken.rc") << """
+        #include <stdio.h>
+
+        NOT A VALID RESOURCE
+"""
+
+        when:
+        run "mainExecutable"
+
+        then:
+        !executedTasks.contains(":compileMainExecutableMainRc")
+    }
+
+    @Requires(TestPrecondition.WINDOWS)
+    @RequiresInstalledToolChain(ToolChainRequirement.GccCompatible)
+    def "reasonable error message when attempting to compile resource files with unsupported tool chain"() {
+        given:
+        buildFile << """
+plugins {
+    id 'cpp'
+    id 'windows-resources'
+}
+
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+         """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+        file("src/main/rc/broken.rc") << """
+        #include <stdio.h>
+
+        NOT A VALID RESOURCE
+"""
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainRc'.")
+        failure.assertHasCause("Windows resource compiler is not available")
+    }
+}
+
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPluginIntegrationTest.groovy
new file mode 100644
index 0000000..91220b6
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourceScriptPluginIntegrationTest.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.rc.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class WindowsResourceScriptPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourcesPluginIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourcesPluginIntegrationTest.groovy
new file mode 100644
index 0000000..e0765a9
--- /dev/null
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/plugins/WindowsResourcesPluginIntegrationTest.groovy
@@ -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.language.rc.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class WindowsResourcesPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/PreprocessingTool.java b/subprojects/language-native/src/main/java/org/gradle/language/PreprocessingTool.java
new file mode 100644
index 0000000..91e98d0
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/PreprocessingTool.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.language;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.Tool;
+
+import java.util.Map;
+
+/**
+ * A tool that permits configuration of the C preprocessor.
+ */
+ at Incubating
+public interface PreprocessingTool extends Tool {
+    /**
+     * The set of preprocessor macros to define when compiling this binary.
+     */
+    Map<String, String> getMacros();
+
+    /**
+     * Defines a named preprocessor macros to use when compiling this binary.
+     * The macro will be supplied to the compiler as '-D name'.
+     */
+    void define(String name);
+
+    /**
+     * Defines a named preprocessor macro with a value, which will be used when compiling this binary.
+     * The macro will be supplied to the compiler as '-D name=definition'.
+     */
+    void define(String name, String definition);
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/AssemblerSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/AssemblerSourceSet.java
new file mode 100644
index 0000000..d419dcf
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/AssemblerSourceSet.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.language.assembler;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of assembly language sources.
+ *
+ * <pre autoTested="true">
+ * apply plugin: "assembler"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 asm {
+ *                     source {
+ *                         srcDirs "src/main/i386", "src/shared/asm"
+ *                         include "**{@literal /}*.s"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface AssemblerSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssembleSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssembleSpec.java
new file mode 100644
index 0000000..3d694af
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssembleSpec.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.language.assembler.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
+
+public class DefaultAssembleSpec extends AbstractNativeCompileSpec implements AssembleSpec {
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.java
new file mode 100644
index 0000000..ed54b30
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/internal/DefaultAssemblerSourceSet.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.language.assembler.internal;
+
+import org.gradle.language.assembler.AssemblerSourceSet;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+
+public class DefaultAssemblerSourceSet extends BaseLanguageSourceSet implements AssemblerSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "Assembler source";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/assembler/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/assembler/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/assembler/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerLangPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerLangPlugin.java
new file mode 100644
index 0000000..69191ac
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerLangPlugin.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.assembler.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.assembler.AssemblerSourceSet;
+import org.gradle.language.assembler.internal.DefaultAssemblerSourceSet;
+import org.gradle.language.assembler.plugins.internal.AssembleTaskConfig;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.internal.DefaultTool;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core Assembler language support.
+ */
+ at Incubating
+public class AssemblerLangPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<AssemblerSourceSet> builder) {
+            builder.setLanguageName("asm");
+            builder.defaultImplementation(DefaultAssemblerSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new Assembler());
+        }
+    }
+
+    private static class Assembler extends NativeLanguageTransform<AssemblerSourceSet> {
+        public Class<AssemblerSourceSet> getSourceSetType() {
+            return AssemblerSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("assembler", DefaultTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new AssembleTaskConfig();
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerPlugin.java
new file mode 100644
index 0000000..8ff058e
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/AssemblerPlugin.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.assembler.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from Assembly language sources.
+ *
+ * <p>Automatically includes the {@link AssemblerLangPlugin} for core Assembler support and the {@link NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.assembler.tasks.Assemble} task for each {@link org.gradle.language.assembler.AssemblerSourceSet} to assemble the sources.</li>
+ */
+ at Incubating
+public class AssemblerPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(AssemblerLangPlugin.class);
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/internal/AssembleTaskConfig.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/internal/AssembleTaskConfig.java
new file mode 100644
index 0000000..74545b1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/internal/AssembleTaskConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.assembler.plugins.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.nativeplatform.Tool;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.language.assembler.tasks.Assemble;
+import org.gradle.platform.base.BinarySpec;
+
+public class AssembleTaskConfig implements SourceTransformTaskConfig {
+    public String getTaskPrefix() {
+        return "assemble";
+    }
+
+    public Class<? extends DefaultTask> getTaskType() {
+        return Assemble.class;
+    }
+
+    public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
+        configureAssembleTask((Assemble) task, (NativeBinarySpecInternal) binary, (LanguageSourceSetInternal) sourceSet);
+    }
+
+    private void configureAssembleTask(Assemble task, final NativeBinarySpecInternal binary, final LanguageSourceSetInternal sourceSet) {
+        task.setDescription(String.format("Assembles the %s of %s", sourceSet, binary));
+
+        task.setToolChain(binary.getToolChain());
+        task.setTargetPlatform(binary.getTargetPlatform());
+
+        task.source(sourceSet.getSource());
+
+        final Project project = task.getProject();
+        task.setObjectFileDir(project.file(project.getBuildDir() + "/objs/" + binary.getNamingScheme().getOutputDirectoryBase() + "/" + sourceSet.getFullName()));
+
+        Tool assemblerTool = (Tool) ((ExtensionAware) binary).getExtensions().getByName("assembler");
+        task.setAssemblerArgs(assemblerTool.getArgs());
+
+        binary.binaryInputs(task.getOutputs().getFiles().getAsFileTree().matching(new PatternSet().include("**/*.obj", "**/*.o")));
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/package-info.java
new file mode 100644
index 0000000..356bec0
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from Assembler language sources.
+ */
+package org.gradle.language.assembler.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/Assemble.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/Assemble.java
new file mode 100644
index 0000000..0482fd8
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/Assemble.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.assembler.tasks;
+
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.*;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.internal.operations.logging.BuildOperationLoggerFactory;
+import org.gradle.language.assembler.internal.DefaultAssembleSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Translates Assembly language source files into object files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class Assemble extends DefaultTask {
+    private FileCollection source;
+    private NativeToolChainInternal toolChain;
+    private NativePlatformInternal targetPlatform;
+    private File objectFileDir;
+    private List<String> assemblerArgs;
+
+    @Inject
+    public Assemble() {
+        source = getProject().files();
+        getInputs().property("outputType", new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return NativeToolChainInternal.Identifier.identify(toolChain, targetPlatform);
+            }
+        });
+    }
+
+    @Inject
+    public BuildOperationLoggerFactory getOperationLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    public void assemble() {
+        BuildOperationLogger operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir());
+        SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
+        cleaner.setDestinationDir(getObjectFileDir());
+        cleaner.execute();
+
+        DefaultAssembleSpec spec = new DefaultAssembleSpec();
+        spec.setTempDir(getTemporaryDir());
+
+        spec.setObjectFileDir(getObjectFileDir());
+        spec.source(getSource());
+        spec.args(getAssemblerArgs());
+        spec.setOperationLogger(operationLogger);
+
+        Compiler<AssembleSpec> compiler = toolChain.select(targetPlatform).newCompiler(AssembleSpec.class);
+        WorkResult result = BuildOperationLoggingCompilerDecorator.wrap(compiler).execute(spec);
+        setDidWork(result.getDidWork());
+    }
+
+    @InputFiles
+    @SkipWhenEmpty
+    public FileCollection getSource() {
+        return source;
+    }
+
+    /**
+     * 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...)}.
+     */
+    public void source(Object sourceFiles) {
+        DefaultGroovyMethods.invokeMethod(source, "from", new Object[]{sourceFiles});
+    }
+
+    /**
+     * Additional arguments to provide to the assembler.
+     */
+    @Input
+    public List<String> getAssemblerArgs() {
+        return assemblerArgs;
+    }
+
+    public void setAssemblerArgs(List<String> assemblerArgs) {
+        this.assemblerArgs = assemblerArgs;
+    }
+
+    /**
+     * The tool chain being used to build.
+     */
+    public NativeToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public void setToolChain(NativeToolChain toolChain) {
+        this.toolChain = (NativeToolChainInternal) toolChain;
+    }
+
+    /**
+     * The platform being targeted.
+     */
+    public NativePlatform getTargetPlatform() {
+        return targetPlatform;
+    }
+
+    public void setTargetPlatform(NativePlatform targetPlatform) {
+        this.targetPlatform = (NativePlatformInternal) targetPlatform;
+    }
+
+    /**
+     * The directory where object files will be generated.
+     */
+    @OutputDirectory
+    public File getObjectFileDir() {
+        return objectFileDir;
+    }
+
+    public void setObjectFileDir(File objectFileDir) {
+        this.objectFileDir = objectFileDir;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/package-info.java
new file mode 100644
index 0000000..c6b3c6d
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/assembler/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 assembling Assembler sources for a native runtime.
+ */
+package org.gradle.language.assembler.tasks;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/CSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/c/CSourceSet.java
new file mode 100644
index 0000000..27166ad
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/CSourceSet.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.language.c;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+
+/**
+ * A set of C source files.
+ *
+ * <p>A C source set contains a set of source files, together with an optional set of exported header files.</p>
+ *
+ * <pre autoTested="true">
+ * apply plugin: "c"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 c {
+ *                     source {
+ *                         srcDirs "src/main/cpp", "src/shared/c++"
+ *                         include "**{@literal /}*.c"
+ *                     }
+ *                     exportedHeaders {
+ *                         srcDirs "src/main/include"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface CSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCCompileSpec.java
new file mode 100644
index 0000000..59ee5c1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCCompileSpec.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.language.c.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
+
+public class DefaultCCompileSpec extends AbstractNativeCompileSpec implements CCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCPCHCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCPCHCompileSpec.java
new file mode 100644
index 0000000..55ea640
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCPCHCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec;
+
+public class DefaultCPCHCompileSpec extends AbstractNativeCompileSpec implements CPCHCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCSourceSet.java
new file mode 100644
index 0000000..673d9b2
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/internal/DefaultCSourceSet.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.language.c.internal;
+
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.nativeplatform.internal.AbstractHeaderExportingDependentSourceSet;
+
+public class DefaultCSourceSet extends AbstractHeaderExportingDependentSourceSet implements CSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "C source";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/c/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/c/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/c/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/c/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPCHPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPCHPlugin.java
new file mode 100644
index 0000000..959c885
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPCHPlugin.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.c.tasks.CPreCompiledHeaderCompile;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.internal.pch.PreCompiledHeaderTransformContainer;
+
+import java.util.Map;
+
+/**
+ * Adds support for compiling C pre-compiled headers.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+public class CLangPCHPlugin extends RuleSource {
+    @Mutate
+    void registerPreCompiledHeaderTask(PreCompiledHeaderTransformContainer pchTransformContainer) {
+        pchTransformContainer.add(new CPCH());
+    }
+
+    private static class CPCH extends NativeLanguageTransform<CSourceSet> {
+        public Class<CSourceSet> getSourceSetType() {
+            return CSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("cCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        @Override
+        public SourceTransformTaskConfig getTransformTask() {
+            return new PCHCompileTaskConfig(this, CPreCompiledHeaderCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPlugin.java
new file mode 100644
index 0000000..323773b
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CLangPlugin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.c.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.c.internal.DefaultCSourceSet;
+import org.gradle.language.c.tasks.CCompile;
+import org.gradle.language.nativeplatform.internal.CompileTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core C language support.
+ */
+ at Incubating
+public class CLangPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<CSourceSet> builder) {
+            builder.setLanguageName("c");
+            builder.defaultImplementation(DefaultCSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new C());
+        }
+    }
+
+    private static class C extends NativeLanguageTransform<CSourceSet> {
+        public Class<CSourceSet> getSourceSetType() {
+            return CSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("cCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new CompileTaskConfig(this, CCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CPlugin.java
new file mode 100644
index 0000000..b6caaa1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/CPlugin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from C sources.
+ *
+ * <p>Automatically includes the {@link CLangPlugin} for core C++ support and the {@link org.gradle.nativeplatform.plugins.NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.c.tasks.CCompile} task for each {@link org.gradle.language.c.CSourceSet} to compile the C sources.</li>
+ */
+ at Incubating
+public class CPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(CLangPlugin.class);
+        project.getPluginManager().apply(CLangPCHPlugin.class);
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/package-info.java
new file mode 100644
index 0000000..34acea3
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from C language sources.
+ */
+package org.gradle.language.c.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CCompile.java
new file mode 100644
index 0000000..bed2dbf
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CCompile.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.c.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.language.c.internal.DefaultCCompileSpec;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles C source files into object files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class CCompile extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultCCompileSpec();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CPreCompiledHeaderCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CPreCompiledHeaderCompile.java
new file mode 100644
index 0000000..7e6fa88
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/CPreCompiledHeaderCompile.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.c.tasks;
+
+import org.gradle.language.c.internal.DefaultCPCHCompileSpec;
+import org.gradle.language.nativeplatform.tasks.AbstractNativePCHCompileTask;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles C header source files into object files.
+ */
+public class CPreCompiledHeaderCompile extends AbstractNativePCHCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultCPCHCompileSpec();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/package-info.java
new file mode 100644
index 0000000..b07a200
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/c/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 compiling C sources for a native runtime.
+ */
+package org.gradle.language.c.tasks;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/CppSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/CppSourceSet.java
new file mode 100644
index 0000000..7b4f78c
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/CppSourceSet.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.language.cpp;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+
+/**
+ * A set of C++ source files.
+ *
+ * <p>A C++ source set contains a set of source files, together with an optional set of exported header files.</p>
+ *
+ * <pre autoTested="true">
+ * apply plugin: "cpp"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 cpp {
+ *                     source {
+ *                         srcDirs "src/main/cpp", "src/shared/c++"
+ *                         include "**{@literal /}*.cpp"
+ *                     }
+ *                     exportedHeaders {
+ *                         srcDirs "src/main/include", "src/shared/include"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface CppSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppCompileSpec.java
new file mode 100644
index 0000000..7ebfcd1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppCompileSpec.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.language.cpp.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec;
+
+public class DefaultCppCompileSpec extends AbstractNativeCompileSpec implements CppCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppPCHCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppPCHCompileSpec.java
new file mode 100644
index 0000000..e93ea0f
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppPCHCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec;
+
+public class DefaultCppPCHCompileSpec extends AbstractNativeCompileSpec implements CppPCHCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSourceSet.java
new file mode 100644
index 0000000..cbf3b56
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSourceSet.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.language.cpp.internal;
+
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.nativeplatform.internal.AbstractHeaderExportingDependentSourceSet;
+
+public class DefaultCppSourceSet extends AbstractHeaderExportingDependentSourceSet implements CppSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "C++ source";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/cpp/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/cpp/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/cpp/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPCHPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPCHPlugin.java
new file mode 100644
index 0000000..0356558
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPCHPlugin.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.cpp.tasks.CppPreCompiledHeaderCompile;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.internal.pch.PreCompiledHeaderTransformContainer;
+
+import java.util.Map;
+
+/**
+ * Adds support for compiling C++ pre-compiled headers.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+public class CppLangPCHPlugin extends RuleSource {
+    @Mutate
+    void registerPreCompiledHeaderTask(PreCompiledHeaderTransformContainer pchTransformContainer) {
+        pchTransformContainer.add(new CppPCH());
+    }
+
+
+    private static class CppPCH extends NativeLanguageTransform<CppSourceSet> {
+        public Class<CppSourceSet> getSourceSetType() {
+            return CppSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("cppCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        @Override
+        public SourceTransformTaskConfig getTransformTask() {
+            return new PCHCompileTaskConfig(this, CppPreCompiledHeaderCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPlugin.java
new file mode 100644
index 0000000..ea922bb
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppLangPlugin.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.cpp.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.cpp.internal.DefaultCppSourceSet;
+import org.gradle.language.cpp.tasks.CppCompile;
+import org.gradle.language.nativeplatform.internal.CompileTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core C++ language support.
+ */
+ at Incubating
+public class CppLangPlugin implements Plugin<Project> {
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<CppSourceSet> builder) {
+            builder.setLanguageName("cpp");
+            builder.defaultImplementation(DefaultCppSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new Cpp());
+        }
+    }
+
+    private static class Cpp extends NativeLanguageTransform<CppSourceSet> {
+        public Class<CppSourceSet> getSourceSetType() {
+            return CppSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("cppCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new CompileTaskConfig(this, CppCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppPlugin.java
new file mode 100644
index 0000000..75d5fe9
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/CppPlugin.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from C++ sources.
+ *
+ * <p>Automatically includes the {@link CppLangPlugin} for core C++ support and the {@link NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.cpp.tasks.CppCompile} task for each {@link org.gradle.language.cpp.CppSourceSet} to compile the C++ sources.</li>
+ */
+ at Incubating
+public class CppPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(CppLangPlugin.class);
+        project.getPluginManager().apply(CppLangPCHPlugin.class);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/package-info.java
new file mode 100644
index 0000000..6c34c7b
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from C++ language sources.
+ */
+package org.gradle.language.cpp.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppCompile.java
new file mode 100644
index 0000000..4577670
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppCompile.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.cpp.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.language.cpp.internal.DefaultCppCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles C++ source files into object files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class CppCompile extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultCppCompileSpec();
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppPreCompiledHeaderCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppPreCompiledHeaderCompile.java
new file mode 100644
index 0000000..48c7c69
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/CppPreCompiledHeaderCompile.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.cpp.tasks;
+
+import org.gradle.language.cpp.internal.DefaultCppPCHCompileSpec;
+import org.gradle.language.nativeplatform.tasks.AbstractNativePCHCompileTask;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles C++ header source files into object files.
+ */
+public class CppPreCompiledHeaderCompile extends AbstractNativePCHCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultCppPCHCompileSpec();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/tasks/package-info.java
new file mode 100644
index 0000000..7ca0f73
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/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 compiling C++ sources for a native runtime.
+ */
+package org.gradle.language.cpp.tasks;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingDependentSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingDependentSourceSet.java
new file mode 100644
index 0000000..b1d1ecc
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingDependentSourceSet.java
@@ -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.language.nativeplatform.internal;
+
+import com.google.common.collect.Sets;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A convenience base class for implementing language source sets with dependencies and exported headers.
+ */
+public abstract class AbstractHeaderExportingDependentSourceSet extends AbstractHeaderExportingSourceSet
+        implements HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+
+    private final List<Object> libs = new ArrayList<Object>();
+    private Set<String> preCompiledHeaders = Sets.newLinkedHashSet();
+    private File prefixHeaderFile;
+
+    public Collection<?> getLibs() {
+        return libs;
+    }
+
+    public void lib(Object library) {
+        if (library instanceof Iterable<?>) {
+            Iterable<?> iterable = (Iterable) library;
+            CollectionUtils.addAll(libs, iterable);
+        } else {
+            libs.add(library);
+        }
+    }
+
+    @Override
+    public Set<String> getPreCompiledHeaders() {
+        return preCompiledHeaders;
+    }
+
+    @Override
+    public void preCompiledHeader(String header) {
+        this.preCompiledHeaders.add(header);
+    }
+
+    @Override
+    public File getPrefixHeaderFile() {
+        return prefixHeaderFile;
+    }
+
+    @Override
+    public void setPrefixHeaderFile(File prefixHeaderFile) {
+        this.prefixHeaderFile = prefixHeaderFile;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingSourceSet.java
new file mode 100644
index 0000000..02785d7
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractHeaderExportingSourceSet.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.language.nativeplatform.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+
+/**
+ * A convenience base class for implementing language source sets with dependencies and exported headers.
+ */
+public abstract class AbstractHeaderExportingSourceSet extends BaseLanguageSourceSet
+        implements HeaderExportingSourceSet, LanguageSourceSet {
+
+    private final DefaultSourceDirectorySet exportedHeaders;
+    private final DefaultSourceDirectorySet implicitHeaders;
+
+    public AbstractHeaderExportingSourceSet() {
+        this.exportedHeaders = new DefaultSourceDirectorySet("exported headers", fileResolver);
+        this.implicitHeaders = new DefaultSourceDirectorySet("implicit headers", fileResolver);
+    }
+
+    public SourceDirectorySet getExportedHeaders() {
+        return exportedHeaders;
+    }
+
+    public void exportedHeaders(Action<? super SourceDirectorySet> config) {
+        config.execute(getExportedHeaders());
+    }
+
+    public SourceDirectorySet getImplicitHeaders() {
+        return implicitHeaders;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractNativeCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractNativeCompileSpec.java
new file mode 100644
index 0000000..483007e
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/AbstractNativeCompileSpec.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal;
+
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.nativeplatform.internal.AbstractBinaryToolSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+import java.io.File;
+import java.util.*;
+
+public abstract class AbstractNativeCompileSpec extends AbstractBinaryToolSpec implements NativeCompileSpec {
+
+    private List<File> includeRoots = new ArrayList<File>();
+    private List<File> sourceFiles = new ArrayList<File>();
+    private List<File> removedSourceFiles = new ArrayList<File>();
+    private boolean incrementalCompile;
+    private Map<String, String> macros = new LinkedHashMap<String, String>();
+    private File objectFileDir;
+    private boolean positionIndependentCode;
+    private BuildOperationLogger oplogger;
+    private File prefixHeaderFile;
+    private File preCompiledHeaderObjectFile;
+    private Map<File, SourceIncludes> sourceFileIncludes;
+    private Set<String> preCompiledHeaders;
+
+    public List<File> getIncludeRoots() {
+        return includeRoots;
+    }
+
+    public void include(File... includeRoots) {
+        Collections.addAll(this.includeRoots, includeRoots);
+    }
+
+    public void include(Iterable<File> includeRoots) {
+        addAll(this.includeRoots, includeRoots);
+    }
+
+    public List<File> getSourceFiles() {
+        return sourceFiles;
+    }
+
+    public void source(Iterable<File> sources) {
+        addAll(sourceFiles, sources);
+    }
+
+    public void setSourceFiles(Collection<File> sources) {
+        sourceFiles.clear();
+        sourceFiles.addAll(sources);
+    }
+
+    public List<File> getRemovedSourceFiles() {
+        return removedSourceFiles;
+    }
+
+    public void removedSource(Iterable<File> sources) {
+        addAll(removedSourceFiles, sources);
+    }
+
+    public void setRemovedSourceFiles(Collection<File> sources) {
+        removedSourceFiles.clear();
+        removedSourceFiles.addAll(sources);
+    }
+
+    public boolean isIncrementalCompile() {
+        return incrementalCompile;
+    }
+
+    public void setIncrementalCompile(boolean flag) {
+        incrementalCompile = flag;
+    }
+
+    public File getObjectFileDir() {
+        return objectFileDir;
+    }
+
+    public void setObjectFileDir(File objectFileDir) {
+        this.objectFileDir = objectFileDir;
+    }
+
+    public Map<String, String> getMacros() {
+        return macros;
+    }
+
+    public void setMacros(Map<String, String> macros) {
+        this.macros = macros;
+    }
+
+    public void define(String name) {
+        macros.put(name, null);
+    }
+
+    public void define(String name, String value) {
+        macros.put(name, value);
+    }
+
+    public boolean isPositionIndependentCode() {
+        return positionIndependentCode;
+    }
+
+    public void setPositionIndependentCode(boolean positionIndependentCode) {
+        this.positionIndependentCode = positionIndependentCode;
+    }
+
+    @Override
+    public File getPreCompiledHeaderObjectFile() {
+        return preCompiledHeaderObjectFile;
+    }
+
+    @Override
+    public void setPreCompiledHeaderObjectFile(File preCompiledHeaderObjectFile) {
+        this.preCompiledHeaderObjectFile = preCompiledHeaderObjectFile;
+    }
+
+    @Override
+    public File getPrefixHeaderFile() {
+        return prefixHeaderFile;
+    }
+
+    @Override
+    public void setPrefixHeaderFile(File pchFile) {
+        this.prefixHeaderFile = pchFile;
+    }
+
+    @Override
+    public Set<String> getPreCompiledHeaders() {
+        return preCompiledHeaders;
+    }
+
+    public void setPreCompiledHeaders(Set<String> preCompiledHeaders) {
+        this.preCompiledHeaders = preCompiledHeaders;
+    }
+
+    private void addAll(List<File> list, Iterable<File> iterable) {
+        for (File file : iterable) {
+            list.add(file);
+        }
+    }
+
+    public BuildOperationLogger getOperationLogger() {
+        return oplogger;
+    }
+
+    public void setOperationLogger(BuildOperationLogger oplogger) {
+        this.oplogger = oplogger;
+    }
+
+    @Override
+    public Map<File, SourceIncludes> getSourceFileIncludes() {
+        return sourceFileIncludes;
+    }
+
+    @Override
+    public void setSourceFileIncludes(Map<File, SourceIncludes> map) {
+        this.sourceFileIncludes = map;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/CompileTaskConfig.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/CompileTaskConfig.java
new file mode 100644
index 0000000..a263f00
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/CompileTaskConfig.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.Transformer;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.language.PreprocessingTool;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.language.nativeplatform.tasks.AbstractNativePCHCompileTask;
+import org.gradle.nativeplatform.NativeDependencySet;
+import org.gradle.nativeplatform.ObjectFile;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.Tool;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+public class CompileTaskConfig implements SourceTransformTaskConfig {
+
+    private final LanguageTransform<? extends LanguageSourceSet, ObjectFile> languageTransform;
+    private final Class<? extends DefaultTask> taskType;
+
+    public CompileTaskConfig(LanguageTransform<? extends LanguageSourceSet, ObjectFile> languageTransform, Class<? extends DefaultTask> taskType) {
+        this.languageTransform = languageTransform;
+        this.taskType = taskType;
+    }
+
+    public String getTaskPrefix() {
+        return "compile";
+    }
+
+    public Class<? extends DefaultTask> getTaskType() {
+        return taskType;
+    }
+
+    public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
+        configureCompileTaskCommon((AbstractNativeCompileTask) task, (NativeBinarySpecInternal) binary, (LanguageSourceSetInternal) sourceSet);
+        configureCompileTask((AbstractNativeCompileTask) task, (NativeBinarySpecInternal) binary, (LanguageSourceSetInternal) sourceSet);
+    }
+
+    private void configureCompileTaskCommon(AbstractNativeCompileTask task, final NativeBinarySpecInternal binary, final LanguageSourceSetInternal sourceSet) {
+        task.setToolChain(binary.getToolChain());
+        task.setTargetPlatform(binary.getTargetPlatform());
+        task.setPositionIndependentCode(binary instanceof SharedLibraryBinarySpec);
+
+        // TODO:DAZ Not sure if these both need to be lazy
+        task.includes(new Callable<Set<File>>() {
+            public Set<File> call() throws Exception {
+                return ((HeaderExportingSourceSet) sourceSet).getExportedHeaders().getSrcDirs();
+            }
+        });
+        task.includes(new Callable<List<FileCollection>>() {
+            public List<FileCollection> call() {
+                Collection<NativeDependencySet> libs = binary.getLibs((DependentSourceSet) sourceSet);
+                return CollectionUtils.collect(libs, new Transformer<FileCollection, NativeDependencySet>() {
+                    public FileCollection transform(NativeDependencySet original) {
+                        return original.getIncludeRoots();
+                    }
+                });
+            }
+        });
+
+        for (String toolName : languageTransform.getBinaryTools().keySet()) {
+            Tool tool = (Tool) ((ExtensionAware) binary).getExtensions().getByName(toolName);
+            if (tool instanceof PreprocessingTool) {
+                task.setMacros(((PreprocessingTool) tool).getMacros());
+            }
+
+            task.setCompilerArgs(tool.getArgs());
+        }
+    }
+
+    protected void configureCompileTask(AbstractNativeCompileTask task, final NativeBinarySpecInternal binary, final LanguageSourceSetInternal sourceSet) {
+        task.setDescription(String.format("Compiles the %s of %s", sourceSet, binary));
+
+        task.source(sourceSet.getSource());
+
+        final Project project = task.getProject();
+        task.setObjectFileDir(project.file(String.valueOf(project.getBuildDir()) + "/objs/" + binary.getNamingScheme().getOutputDirectoryBase() + "/" + sourceSet.getFullName()));
+
+        // If this task uses a pre-compiled header
+        if (sourceSet instanceof DependentSourceSet && !((DependentSourceSet) sourceSet).getPreCompiledHeaders().isEmpty()) {
+            task.setPrefixHeaderFile(((DependentSourceSet)sourceSet).getPrefixHeaderFile());
+            task.setPreCompiledHeaders(((DependentSourceSet) sourceSet).getPreCompiledHeaders());
+            task.preCompiledHeaderInclude(new Callable<FileCollection>() {
+                public FileCollection call() {
+                    Set<AbstractNativePCHCompileTask> pchTasks = binary.getTasks().withType(AbstractNativePCHCompileTask.class).matching(new Spec<AbstractNativePCHCompileTask>() {
+                        @Override
+                        public boolean isSatisfiedBy(AbstractNativePCHCompileTask pchCompileTask) {
+                            return ((DependentSourceSet) sourceSet).getPrefixHeaderFile().equals(pchCompileTask.getPrefixHeaderFile());
+                        }
+                    });
+                    if (!pchTasks.isEmpty()) {
+                        return pchTasks.iterator().next().getOutputs().getFiles().getAsFileTree().matching(new PatternSet().include("**/*.pch", "**/*.gch"));
+                    } else {
+                        return new SimpleFileCollection();
+                    }
+                }
+            });
+        }
+
+        binary.binaryInputs(task.getOutputs().getFiles().getAsFileTree().matching(new PatternSet().include("**/*.obj", "**/*.o")));
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/DefaultPreprocessingTool.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/DefaultPreprocessingTool.java
new file mode 100644
index 0000000..ae1ce45
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/DefaultPreprocessingTool.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.language.nativeplatform.internal;
+
+import org.gradle.nativeplatform.internal.DefaultTool;
+import org.gradle.language.PreprocessingTool;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultPreprocessingTool extends DefaultTool implements PreprocessingTool {
+    private final Map<String, String> definitions = new LinkedHashMap<String, String>();
+
+    public Map<String, String> getMacros() {
+        return definitions;
+    }
+
+    public void define(String name) {
+        definitions.put(name, null);
+    }
+
+    public void define(String name, String definition) {
+        definitions.put(name, definition);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/NativeLanguageTransform.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/NativeLanguageTransform.java
new file mode 100644
index 0000000..50237b1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/NativeLanguageTransform.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal;
+
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.ObjectFile;
+import org.gradle.platform.base.BinarySpec;
+
+public abstract class NativeLanguageTransform<U extends LanguageSourceSet> implements LanguageTransform<U, ObjectFile> {
+
+    public boolean applyToBinary(BinarySpec binary) {
+        return binary instanceof NativeBinarySpec;
+    }
+
+    public Class<ObjectFile> getOutputType() {
+        return ObjectFile.class;
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/PCHCompileTaskConfig.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/PCHCompileTaskConfig.java
new file mode 100644
index 0000000..7a1da86
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/PCHCompileTaskConfig.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.specs.Spec;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.nativeplatform.ObjectFile;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask;
+
+public class PCHCompileTaskConfig extends CompileTaskConfig {
+    public PCHCompileTaskConfig(LanguageTransform<? extends LanguageSourceSet, ObjectFile> languageTransform, Class<? extends DefaultTask> taskType) {
+        super(languageTransform, taskType);
+    }
+
+    @Override
+    protected void configureCompileTask(AbstractNativeCompileTask task, final NativeBinarySpecInternal binary, final LanguageSourceSetInternal languageSourceSet) {
+        // Note that the sourceSet is the sourceSet this pre-compiled header will be used with - it's not an
+        // input sourceSet to the compile task.
+        final DependentSourceSet sourceSet = (DependentSourceSet) languageSourceSet;
+
+        task.setDescription(String.format("Compiles a pre-compiled header for the %s of %s", sourceSet, binary));
+
+        final Project project = task.getProject();
+        task.setPrefixHeaderFile(sourceSet.getPrefixHeaderFile());
+        task.setPreCompiledHeaders(sourceSet.getPreCompiledHeaders());
+        task.source(sourceSet.getPrefixHeaderFile());
+
+        task.setObjectFileDir(project.file(String.valueOf(project.getBuildDir()) + "/objs/" + binary.getNamingScheme().getOutputDirectoryBase() + "/" + languageSourceSet.getFullName() + "PreCompiledHeader"));
+
+        task.dependsOn(project.getTasks().withType(PrefixHeaderFileGenerateTask.class).matching(new Spec<PrefixHeaderFileGenerateTask>() {
+            @Override
+            public boolean isSatisfiedBy(PrefixHeaderFileGenerateTask prefixHeaderFileGenerateTask) {
+                return prefixHeaderFileGenerateTask.getPrefixHeaderFile().equals(sourceSet.getPrefixHeaderFile());
+            }
+        }));
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationFileState.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationFileState.java
new file mode 100644
index 0000000..94ee4ba
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationFileState.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class CompilationFileState implements Serializable {
+    private byte[] hash;
+    private SourceIncludes sourceIncludes = new DefaultSourceIncludes();
+    private Set<ResolvedInclude> resolvedIncludes = new HashSet<ResolvedInclude>();
+
+    public CompilationFileState(byte[] hash) {
+        this.hash = hash;
+    }
+
+    public byte[] getHash() {
+        return hash;
+    }
+
+    public SourceIncludes getSourceIncludes() {
+        return sourceIncludes;
+    }
+
+    public void setSourceIncludes(SourceIncludes sourceIncludes) {
+        this.sourceIncludes = sourceIncludes;
+    }
+
+    public Set<ResolvedInclude> getResolvedIncludes() {
+        return resolvedIncludes;
+    }
+
+    public void setResolvedIncludes(Set<ResolvedInclude> resolvedIncludes) {
+        this.resolvedIncludes = resolvedIncludes;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationState.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationState.java
new file mode 100644
index 0000000..821e1f2
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationState.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.language.nativeplatform.internal.incremental;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.*;
+
+public class CompilationState implements Serializable {
+    List<File> sourceInputs = new ArrayList<File>();
+    Map<File, CompilationFileState> fileStates = new HashMap<File, CompilationFileState>();
+
+    public List<File> getSourceInputs() {
+        return sourceInputs;
+    }
+
+    public void addSourceInput(File file) {
+        sourceInputs.add(file);
+    }
+
+    public CompilationFileState getState(File file) {
+        return fileStates.get(file);
+    }
+
+    public void setState(File file, CompilationFileState compilationFileState) {
+        fileStates.put(file, compilationFileState);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateCacheFactory.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateCacheFactory.java
new file mode 100644
index 0000000..1e39bdb
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateCacheFactory.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal.incremental;
+
+import org.gradle.cache.PersistentStateCache;
+
+public interface CompilationStateCacheFactory {
+    PersistentStateCache<CompilationState> create(String taskPath);
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializer.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializer.java
new file mode 100644
index 0000000..c8a27f7
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializer.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.internal.serialize.*;
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.File;
+import java.util.Set;
+
+public class CompilationStateSerializer implements Serializer<CompilationState> {
+    
+    private static final int SERIAL_VERSION = 1;
+    private final BaseSerializerFactory serializerFactory = new BaseSerializerFactory();
+    private final Serializer<File> fileSerializer;
+    private final ListSerializer<File> fileListSerializer;
+    private final MapSerializer<File, CompilationFileState> stateMapSerializer;
+
+    public CompilationStateSerializer() {
+        fileSerializer = serializerFactory.getSerializerFor(File.class);
+        fileListSerializer = new ListSerializer<File>(fileSerializer);
+        stateMapSerializer = new MapSerializer<File, CompilationFileState>(fileSerializer, new CompilationFileStateSerializer());
+    }
+
+    public CompilationState read(Decoder decoder) throws Exception {
+        CompilationState compilationState = new CompilationState();
+        int version = decoder.readInt();
+        if (version != SERIAL_VERSION) {
+            return compilationState;
+        }
+
+        compilationState.sourceInputs.addAll(fileListSerializer.read(decoder));
+        compilationState.fileStates.putAll(stateMapSerializer.read(decoder));
+        return compilationState;
+    }
+
+    public void write(Encoder encoder, CompilationState value) throws Exception {
+        encoder.writeInt(SERIAL_VERSION);
+        fileListSerializer.write(encoder, value.sourceInputs);
+        stateMapSerializer.write(encoder, value.fileStates);
+    }
+
+    private class CompilationFileStateSerializer implements Serializer<CompilationFileState> {
+        private final Serializer<byte[]> hashSerializer = new HashSerializer();
+        private final Serializer<Set<ResolvedInclude>> resolveIncludesSerializer = new SetSerializer<ResolvedInclude>(new ResolvedIncludeSerializer());
+        private final Serializer<SourceIncludes> sourceIncludesSerializer = new SourceIncludesSerializer();
+
+        public CompilationFileState read(Decoder decoder) throws Exception {
+            CompilationFileState fileState = new CompilationFileState(hashSerializer.read(decoder));
+            fileState.setResolvedIncludes(resolveIncludesSerializer.read(decoder));
+            fileState.setSourceIncludes(sourceIncludesSerializer.read(decoder));
+            return fileState;
+        }
+
+        public void write(Encoder encoder, CompilationFileState value) throws Exception {
+            hashSerializer.write(encoder, value.getHash());
+            resolveIncludesSerializer.write(encoder, value.getResolvedIncludes());
+            sourceIncludesSerializer.write(encoder, value.getSourceIncludes());
+        }
+    }
+
+    private class HashSerializer implements Serializer<byte[]> {
+        public byte[] read(Decoder decoder) throws Exception {
+            int size = decoder.readSmallInt();
+            byte[] value = new byte[size];
+            decoder.readBytes(value);
+            return value;
+        }
+
+        public void write(Encoder encoder, byte[] value) throws Exception {
+            encoder.writeSmallInt(value.length);
+            encoder.writeBytes(value);
+        }
+    }
+
+    private class ResolvedIncludeSerializer implements Serializer<ResolvedInclude> {
+        public ResolvedInclude read(Decoder decoder) throws Exception {
+            String include = decoder.readString();
+            File included = null;
+            if (decoder.readBoolean()) {
+                included = fileSerializer.read(decoder);
+            }
+            return new ResolvedInclude(include, included);
+        }
+
+        public void write(Encoder encoder, ResolvedInclude value) throws Exception {
+            encoder.writeString(value.getInclude());
+            if (value.getFile() == null) {
+                encoder.writeBoolean(false);
+            } else {
+                encoder.writeBoolean(true);
+                fileSerializer.write(encoder, value.getFile());
+            }
+        }
+    }
+
+    private class SourceIncludesSerializer implements Serializer<SourceIncludes> {
+        private final Serializer<String> stringSerializer = serializerFactory.getSerializerFor(String.class);
+        private final ListSerializer<String> stringListSerializer = new ListSerializer<String>(stringSerializer);
+
+        public SourceIncludes read(Decoder decoder) throws Exception {
+            SourceIncludes sourceIncludes = new DefaultSourceIncludes();
+            sourceIncludes.getQuotedIncludes().addAll(stringListSerializer.read(decoder));
+            sourceIncludes.getSystemIncludes().addAll(stringListSerializer.read(decoder));
+            sourceIncludes.getMacroIncludes().addAll(stringListSerializer.read(decoder));
+            return sourceIncludes;
+        }
+
+        public void write(Encoder encoder, SourceIncludes value) throws Exception {
+            stringListSerializer.write(encoder, value.getQuotedIncludes());
+            stringListSerializer.write(encoder, value.getSystemIncludes());
+            stringListSerializer.write(encoder, value.getMacroIncludes());
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultCompilationStateCacheFactory.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultCompilationStateCacheFactory.java
new file mode 100644
index 0000000..fd92780
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultCompilationStateCacheFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal.incremental;
+
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.cache.PersistentStateCache;
+
+public class DefaultCompilationStateCacheFactory implements CompilationStateCacheFactory {
+
+    private final PersistentIndexedCache<String, CompilationState> compilationStateIndexedCache;
+
+    public DefaultCompilationStateCacheFactory(TaskArtifactStateCacheAccess cacheAccess) {
+        compilationStateIndexedCache = cacheAccess.createCache("compilationState", String.class, new CompilationStateSerializer());
+    }
+
+    public PersistentStateCache<CompilationState> create(final String taskPath) {
+        return new PersistentCompilationStateCache(taskPath, compilationStateIndexedCache);
+    }
+
+    private static class PersistentCompilationStateCache implements PersistentStateCache<CompilationState> {
+        private final String taskPath;
+        private final PersistentIndexedCache<String, CompilationState> compilationStateIndexedCache;
+
+        public PersistentCompilationStateCache(String taskPath, PersistentIndexedCache<String, CompilationState> compilationStateIndexedCache) {
+            this.taskPath = taskPath;
+            this.compilationStateIndexedCache = compilationStateIndexedCache;
+        }
+
+        public CompilationState get() {
+            return compilationStateIndexedCache.get(taskPath);
+        }
+
+        public void set(CompilationState newValue) {
+            compilationStateIndexedCache.put(taskPath, newValue);
+        }
+
+        public void update(UpdateAction<CompilationState> updateAction) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultIncrementalCompilation.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultIncrementalCompilation.java
new file mode 100644
index 0000000..af778b1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultIncrementalCompilation.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultIncrementalCompilation implements IncrementalCompilation {
+    private final List<File> recompile;
+    private final List<File> removed;
+    private Map<File, SourceIncludes> sourceIncludes;
+    private CompilationState finalState;
+
+    public DefaultIncrementalCompilation(CompilationState finalState, List<File> recompile, List<File> removed, Map<File, SourceIncludes> sourceIncludes) {
+        this.finalState = finalState;
+        this.sourceIncludes = sourceIncludes;
+        this.recompile = recompile;
+        this.removed = removed;
+    }
+
+    public List<File> getRecompile() {
+        return recompile;
+    }
+
+    public List<File> getRemoved() {
+        return removed;
+    }
+
+    public Map<File, SourceIncludes> getSourceFileIncludes() {
+        return sourceIncludes;
+    }
+
+    public CompilationState getFinalState() {
+        return finalState;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludes.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludes.java
new file mode 100644
index 0000000..d7f7c53
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludes.java
@@ -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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+class DefaultSourceIncludes implements SourceIncludes, Serializable {
+    private final List<String> quotedIncludes = new ArrayList<String>();
+    private final List<String> systemIncludes = new ArrayList<String>();
+    private final List<String> macroIncludes = new ArrayList<String>();
+
+    public void addAll(List<String> includes) {
+        for (String value : includes) {
+            if (value.startsWith("<") && value.endsWith(">")) {
+                systemIncludes.add(strip(value));
+            } else if (value.startsWith("\"") && value.endsWith("\"")) {
+                quotedIncludes.add(strip(value));
+            } else {
+                macroIncludes.add(value);
+            }
+        }
+    }
+
+    private String strip(String include) {
+        return include.substring(1, include.length() - 1);
+    }
+
+    public List<String> getQuotedIncludes() {
+        return quotedIncludes;
+    }
+
+    public List<String> getSystemIncludes() {
+        return systemIncludes;
+    }
+
+    public List<String> getMacroIncludes() {
+        return macroIncludes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DefaultSourceIncludes)) {
+            return false;
+        }
+
+        DefaultSourceIncludes that = (DefaultSourceIncludes) o;
+
+        return macroIncludes.equals(that.macroIncludes)
+                && quotedIncludes.equals(that.quotedIncludes)
+                && systemIncludes.equals(that.systemIncludes);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = quotedIncludes.hashCode();
+        result = 31 * result + systemIncludes.hashCode();
+        result = 31 * result + macroIncludes.hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParser.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParser.java
new file mode 100644
index 0000000..911c96c
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParser.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+import org.gradle.language.nativeplatform.internal.incremental.sourceparser.CSourceParser;
+
+import java.io.File;
+
+public class DefaultSourceIncludesParser implements SourceIncludesParser {
+    private final CSourceParser sourceParser;
+    private final boolean importAware;
+
+    public DefaultSourceIncludesParser(CSourceParser sourceParser, boolean importAware) {
+        this.sourceParser = sourceParser;
+        this.importAware = importAware;
+    }
+
+    public SourceIncludes parseIncludes(File sourceFile) {
+        CSourceParser.SourceDetails sourceDetails = sourceParser.parseSource(sourceFile);
+        DefaultSourceIncludes defaultIncludes = new DefaultSourceIncludes();
+        defaultIncludes.addAll(sourceDetails.getIncludes());
+        if (importAware) {
+            defaultIncludes.addAll(sourceDetails.getImports());
+        }
+
+        return defaultIncludes;
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolver.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolver.java
new file mode 100644
index 0000000..f556654
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolver.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultSourceIncludesResolver implements SourceIncludesResolver {
+    private final List<File> includePaths;
+
+    public DefaultSourceIncludesResolver(List<File> includePaths) {
+        this.includePaths = includePaths;
+    }
+
+    public Set<ResolvedInclude> resolveIncludes(File sourceFile, SourceIncludes includes) {
+        Set<ResolvedInclude> dependencies = new LinkedHashSet<ResolvedInclude>();
+        searchForDependencies(dependencies, prependSourceDir(sourceFile, includePaths), includes.getQuotedIncludes());
+        searchForDependencies(dependencies, includePaths, includes.getSystemIncludes());
+        if (!includes.getMacroIncludes().isEmpty()) {
+            dependencies.add(new ResolvedInclude(includes.getMacroIncludes().get(0), null));
+        }
+
+        return dependencies;
+    }
+
+    private List<File> prependSourceDir(File sourceFile, List<File> includePaths) {
+        List<File> quotedSearchPath = new ArrayList<File>(includePaths.size() + 1);
+        quotedSearchPath.add(sourceFile.getParentFile());
+        quotedSearchPath.addAll(includePaths);
+        return quotedSearchPath;
+    }
+
+    private void searchForDependencies(Set<ResolvedInclude> dependencies, List<File> searchPath, List<String> includes) {
+        for (String include : includes) {
+            searchForDependency(dependencies, searchPath, include);
+        }
+    }
+
+    private void searchForDependency(Set<ResolvedInclude> dependencies, List<File> searchPath, String include) {
+        for (File searchDir : searchPath) {
+            File candidate = new File(searchDir, include);
+            if (candidate.isFile()) {
+                dependencies.add(new ResolvedInclude(include, GFileUtils.canonicalise(candidate)));
+                return;
+            }
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilation.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilation.java
new file mode 100644
index 0000000..238e660
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilation.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public interface IncrementalCompilation {
+    List<File> getRecompile();
+
+    List<File> getRemoved();
+
+    Map<File, SourceIncludes> getSourceFileIncludes();
+
+    CompilationState getFinalState();
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessor.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessor.java
new file mode 100644
index 0000000..a2b8545
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessor.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.cache.PersistentStateCache;
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+
+public class IncrementalCompileProcessor {
+    private static final Logger LOGGER = LoggerFactory.getLogger(IncrementalCompileProcessor.class);
+
+    private final PersistentStateCache<CompilationState> previousCompileStateCache;
+    private final SourceIncludesParser sourceIncludesParser;
+    private final SourceIncludesResolver sourceIncludesResolver;
+    private final FileSnapshotter snapshotter;
+
+    public IncrementalCompileProcessor(PersistentStateCache<CompilationState> previousCompileStateCache, SourceIncludesResolver sourceIncludesResolver, SourceIncludesParser sourceIncludesParser,
+                                       FileSnapshotter snapshotter) {
+        this.previousCompileStateCache = previousCompileStateCache;
+        this.sourceIncludesResolver = sourceIncludesResolver;
+        this.sourceIncludesParser = sourceIncludesParser;
+        this.snapshotter = snapshotter;
+    }
+
+    public IncrementalCompilation processSourceFiles(Collection<File> sourceFiles) {
+        CompilationState previousCompileState = previousCompileStateCache.get();
+        final IncrementalCompileFiles result = new IncrementalCompileFiles(previousCompileState);
+
+        for (File sourceFile : sourceFiles) {
+            result.processSource(sourceFile);
+        }
+
+        return new DefaultIncrementalCompilation(result.current, result.getModifiedSources(), result.getRemovedSources(), mapIncludes(sourceFiles, result.current));
+    }
+
+    private Map<File, SourceIncludes> mapIncludes(Collection<File> files, CompilationState compilationState) {
+        Map<File, SourceIncludes> map = Maps.newHashMap();
+        for (File file : files) {
+            map.put(file, compilationState.getState(file).getSourceIncludes());
+        }
+        return map;
+    }
+
+    private class IncrementalCompileFiles {
+
+        private final List<File> recompile = new ArrayList<File>();
+
+        private final CompilationState previous;
+        private final CompilationState current = new CompilationState();
+        private final Map<File, Boolean> processed = new HashMap<File, Boolean>();
+
+        public IncrementalCompileFiles(CompilationState previousCompileState) {
+            this.previous = previousCompileState == null ? new CompilationState() : previousCompileState;
+        }
+
+        public void processSource(File sourceFile) {
+            current.addSourceInput(sourceFile);
+            if (checkChangedAndUpdateState(sourceFile) || !previous.getSourceInputs().contains(sourceFile)) {
+                recompile.add(sourceFile);
+            }
+        }
+
+        public boolean checkChangedAndUpdateState(File file) {
+            boolean changed = false;
+
+            if (processed.containsKey(file)) {
+                return processed.get(file);
+            }
+
+            if (!file.exists()) {
+                return true;
+            }
+
+            // Assume unchanged if we recurse to the same file due to dependency cycle
+            processed.put(file, false);
+
+            CompilationFileState previousState = previous.getState(file);
+            CompilationFileState newState = new CompilationFileState(snapshotter.snapshot(file).getHash());
+
+            if (!sameHash(previousState, newState)) {
+                changed = true;
+                newState.setSourceIncludes(sourceIncludesParser.parseIncludes(file));
+            } else {
+                newState.setSourceIncludes(previousState.getSourceIncludes());
+            }
+
+            newState.setResolvedIncludes(resolveIncludes(file, newState.getSourceIncludes()));
+            // Compare the previous resolved includes with resolving now.
+            if (!sameResolved(previousState, newState)) {
+                changed = true;
+            }
+
+            current.setState(file, newState);
+
+            for (ResolvedInclude dep : newState.getResolvedIncludes()) {
+                if (dep.isUnknown()) {
+                    LOGGER.info(String.format("Cannot determine changed state of included '%s' in source file '%s'. Assuming changed.", dep.getInclude(), file.getName()));
+                    changed = true;
+                } else {
+                    boolean depChanged = checkChangedAndUpdateState(dep.getFile());
+                    changed = changed || depChanged;
+                }
+            }
+
+            processed.put(file, changed);
+
+            return changed;
+        }
+
+        private boolean sameHash(CompilationFileState previousState, CompilationFileState newState) {
+            return previousState != null && Arrays.equals(newState.getHash(), previousState.getHash());
+        }
+
+        private boolean sameResolved(CompilationFileState previousState, CompilationFileState newState) {
+            return previousState != null && newState.getResolvedIncludes().equals(previousState.getResolvedIncludes());
+        }
+
+        private Set<ResolvedInclude> resolveIncludes(File file, SourceIncludes sourceIncludes) {
+            return sourceIncludesResolver.resolveIncludes(file, sourceIncludes);
+        }
+
+        public List<File> getModifiedSources() {
+            return recompile;
+        }
+
+        public List<File> getRemovedSources() {
+            List<File> removed = new ArrayList<File>();
+            for (File previousSource : previous.getSourceInputs()) {
+                if (!current.getSourceInputs().contains(previousSource)) {
+                    removed.add(previousSource);
+                }
+            }
+            return removed;
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilerBuilder.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilerBuilder.java
new file mode 100644
index 0000000..53806cd
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompilerBuilder.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public class IncrementalCompilerBuilder {
+    private final TaskArtifactStateCacheAccess cacheAccess;
+    private final FileSnapshotter fileSnapshotter;
+    private final CompilationStateCacheFactory compilationStateCacheFactory;
+
+    public IncrementalCompilerBuilder(TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter, CompilationStateCacheFactory compilationStateCacheFactory) {
+        this.cacheAccess = cacheAccess;
+        this.fileSnapshotter = fileSnapshotter;
+        this.compilationStateCacheFactory = compilationStateCacheFactory;
+    }
+
+    public <T extends NativeCompileSpec> Compiler<T> createIncrementalCompiler(TaskInternal task, Compiler<T> compiler, NativeToolChain toolchain) {
+        return new IncrementalNativeCompiler<T>(task, cacheAccess, fileSnapshotter, compilationStateCacheFactory, compiler, toolchain);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompiler.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompiler.java
new file mode 100644
index 0000000..ed3578f
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompiler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.changedetection.state.FileSnapshotter;
+import org.gradle.api.internal.changedetection.state.TaskArtifactStateCacheAccess;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.cache.PersistentStateCache;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.nativeplatform.internal.incremental.sourceparser.CSourceParser;
+import org.gradle.language.nativeplatform.internal.incremental.sourceparser.RegexBackedCSourceParser;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+
+public class IncrementalNativeCompiler<T extends NativeCompileSpec> implements Compiler<T> {
+    private final Compiler<T> delegateCompiler;
+    private final boolean importsAreIncludes;
+    private final TaskInternal task;
+    private final TaskArtifactStateCacheAccess cacheAccess;
+    private final FileSnapshotter fileSnapshotter;
+    private final CompilationStateCacheFactory compilationStateCacheFactory;
+
+    private final CSourceParser sourceParser = new RegexBackedCSourceParser();
+
+    public IncrementalNativeCompiler(TaskInternal task, TaskArtifactStateCacheAccess cacheAccess, FileSnapshotter fileSnapshotter, CompilationStateCacheFactory compilationStateCacheFactory,
+                                     Compiler<T> delegateCompiler, NativeToolChain toolChain) {
+        this.task = task;
+        this.cacheAccess = cacheAccess;
+        this.fileSnapshotter = fileSnapshotter;
+        this.compilationStateCacheFactory = compilationStateCacheFactory;
+        this.delegateCompiler = delegateCompiler;
+        this.importsAreIncludes = Clang.class.isAssignableFrom(toolChain.getClass()) || Gcc.class.isAssignableFrom(toolChain.getClass());
+    }
+
+    public WorkResult execute(final T spec) {
+        final PersistentStateCache<CompilationState> compileStateCache = compilationStateCacheFactory.create(task.getPath());
+        final IncrementalCompilation compilation = cacheAccess.useCache("process source files", new Factory<IncrementalCompilation>() {
+            public IncrementalCompilation create() {
+                DefaultSourceIncludesParser sourceIncludesParser = new DefaultSourceIncludesParser(sourceParser, importsAreIncludes);
+                IncrementalCompileProcessor processor = createProcessor(compileStateCache, sourceIncludesParser, spec.getIncludeRoots());
+                // TODO - do not hold the lock while processing the source files - this prevents other tasks from executing concurrently
+                IncrementalCompilation incrementalCompilation = processor.processSourceFiles(spec.getSourceFiles());
+                spec.setSourceFileIncludes(incrementalCompilation.getSourceFileIncludes());
+                return incrementalCompilation;
+            }
+        });
+
+        WorkResult workResult;
+        if (spec.isIncrementalCompile()) {
+            workResult = doIncrementalCompile(compilation, spec);
+        } else {
+            workResult = doCleanIncrementalCompile(spec);
+        }
+
+        cacheAccess.useCache("update compilation state", new Factory<Void>() {
+            public Void create() {
+                compileStateCache.set(compilation.getFinalState());
+                return null;
+            }
+        });
+
+        return workResult;
+    }
+
+    protected WorkResult doIncrementalCompile(IncrementalCompilation compilation, T spec) {
+        // Determine the actual sources to clean/compile
+        spec.setSourceFiles(compilation.getRecompile());
+        spec.setRemovedSourceFiles(compilation.getRemoved());
+        return delegateCompiler.execute(spec);
+    }
+
+    protected WorkResult doCleanIncrementalCompile(T spec) {
+        boolean deleted = cleanPreviousOutputs(spec);
+        WorkResult compileResult = delegateCompiler.execute(spec);
+        if (deleted && !compileResult.getDidWork()) {
+            return new SimpleWorkResult(deleted);
+        }
+        return compileResult;
+    }
+
+    private boolean cleanPreviousOutputs(NativeCompileSpec spec) {
+        SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(getTask().getOutputs());
+        cleaner.setDestinationDir(spec.getObjectFileDir());
+        cleaner.execute();
+        return cleaner.getDidWork();
+    }
+
+    protected TaskInternal getTask() {
+        return task;
+    }
+
+    private IncrementalCompileProcessor createProcessor(PersistentStateCache<CompilationState> compileStateCache, SourceIncludesParser sourceIncludesParser, Iterable<File> includes) {
+        DefaultSourceIncludesResolver dependencyParser = new DefaultSourceIncludesResolver(CollectionUtils.toList(includes));
+
+        return new IncrementalCompileProcessor(compileStateCache, dependencyParser, sourceIncludesParser, fileSnapshotter);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/ResolvedInclude.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/ResolvedInclude.java
new file mode 100644
index 0000000..6d50ea5
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/ResolvedInclude.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.language.nativeplatform.internal.incremental;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class ResolvedInclude implements Serializable {
+    private final String include;
+    private final File dependencyFile;
+
+    public ResolvedInclude(String include, File dependencyFile) {
+        this.include = include;
+        this.dependencyFile = dependencyFile;
+    }
+
+    public boolean isUnknown() {
+        return dependencyFile == null;
+    }
+
+    public String getInclude() {
+        return include;
+    }
+
+    public File getFile() {
+        return dependencyFile;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Resolved include '%s'", include);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ResolvedInclude)) {
+            return false;
+        }
+
+        ResolvedInclude that = (ResolvedInclude) o;
+
+        return include.equals(that.include)
+                && (dependencyFile == null ? that.dependencyFile == null : dependencyFile.equals(that.dependencyFile));
+
+    }
+
+    @Override
+    public int hashCode() {
+        return include.hashCode();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesParser.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesParser.java
new file mode 100644
index 0000000..b8f8c23
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesParser.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.File;
+
+public interface SourceIncludesParser {
+
+    SourceIncludes parseIncludes(File sourceFile);
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesResolver.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesResolver.java
new file mode 100644
index 0000000..6574abb
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/SourceIncludesResolver.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.language.nativeplatform.internal.incremental;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+
+import java.io.File;
+import java.util.Set;
+
+public interface SourceIncludesResolver {
+    Set<ResolvedInclude> resolveIncludes(File sourceFile, SourceIncludes includes);
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/CSourceParser.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/CSourceParser.java
new file mode 100644
index 0000000..7908e86
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/CSourceParser.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.language.nativeplatform.internal.incremental.sourceparser;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A parser to extract information from C-compatible source files.
+ */
+public interface CSourceParser {
+
+    SourceDetails parseSource(File sourceFile);
+
+    interface SourceDetails {
+        List<String> getIncludes();
+        List<String> getImports();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReader.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReader.java
new file mode 100644
index 0000000..305ce85
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReader.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental.sourceparser;
+
+import org.apache.tools.ant.filters.BaseFilterReader;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Replaces c-style comments with a single space, and removes line-continuation characters.
+ * This code is largely adopted from org.apache.tools.ant.filters.StripJavaComments.
+ *
+ * This avoids the synchronisation overhead of PushbackReader and is _not_ threadsafe.
+ */
+public class PreprocessingReader extends BaseFilterReader {
+    /**
+     * The read-ahead characters, used for reading ahead up to 2 characters and pushing back into stream.
+     * A value of -1 indicates that no character is in the buffer.
+     */
+    private int[] readAheadChars = new int[2];
+
+    /**
+     * Whether or not the parser is currently in the middle of a string literal.
+     */
+    private boolean inString;
+
+    /**
+     * Whether or not the last char has been a backslash.
+     */
+    private boolean quoted;
+
+    public PreprocessingReader(Reader in) {
+        super(in);
+        readAheadChars[0] = -1;
+        readAheadChars[1] = -1;
+    }
+
+    /**
+     * Returns the next character in the filtered stream:
+     * <ul>
+     *     <li>Comments will be replaced by a single space</li>
+     *     <li>Line continuation (backslash-newline) will be removed</li>
+     * </ul>
+     */
+    public int read() throws IOException {
+        int ch = next();
+
+        if (ch == '\\') {
+            if (discardNewLine()) {
+                return read();
+            }
+        }
+
+        if (ch == '"' && !quoted) {
+            inString = !inString;
+            quoted = false;
+        } else if (ch == '\\') {
+            quoted = !quoted;
+        } else {
+            quoted = false;
+            if (!inString) {
+                if (ch == '/') {
+                    ch = next();
+                    if (ch == '/') {
+                        while (ch != '\n' && ch != -1 && ch != '\r') {
+                            ch = next();
+                        }
+                    } else if (ch == '*') {
+                        while (ch != -1) {
+                            ch = next();
+                            if (ch == '*') {
+                                ch = next();
+                                while (ch == '*') {
+                                    ch = next();
+                                }
+
+                                if (ch == '/') {
+                                    ch = ' ';
+                                    break;
+                                }
+                            }
+                        }
+                    } else {
+                        pushBack(ch);
+                        ch = '/';
+                    }
+                }
+            }
+        }
+
+        return ch;
+    }
+
+    private boolean discardNewLine() throws IOException {
+        int nextChar = next();
+        if (nextChar == '\n') {
+            return true; // '\\\n' discarded from stream
+        } else if (nextChar == '\r') {
+            int followingChar = next();
+            if (followingChar == '\n') {
+                return true; // '\\\r\n' discarded from stream
+            }
+            pushBack(nextChar);
+            pushBack(followingChar);
+            return false;
+        } else {
+            pushBack(nextChar);
+            return false;
+        }
+    }
+
+    private int next() throws IOException {
+        if (readAheadChars[0] != -1) {
+            int ch = readAheadChars[0];
+            readAheadChars[0] = readAheadChars[1];
+            readAheadChars[1] = -1;
+            return ch;
+        }
+
+        return in.read();
+    }
+
+    private void pushBack(int ch) {
+        if (readAheadChars[1] != -1) {
+            throw new IllegalStateException();
+        }
+        if (readAheadChars[0] != -1) {
+            readAheadChars[1] = ch;
+        } else {
+            readAheadChars[0] = ch;
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParser.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParser.java
new file mode 100644
index 0000000..8782c10
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParser.java
@@ -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.language.nativeplatform.internal.incremental.sourceparser;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexBackedCSourceParser implements CSourceParser {
+    private static final String INCLUDE_IMPORT_PATTERN = "#\\s*(include|import)\\s*((<[^>]+>)|(\"[^\"]+\")|(\\w+))";
+    private final Pattern includePattern;
+
+    public RegexBackedCSourceParser() {
+        this.includePattern = Pattern.compile(INCLUDE_IMPORT_PATTERN, Pattern.CASE_INSENSITIVE);
+    }
+
+    public SourceDetails parseSource(File sourceFile) {
+        DefaultSourceDetails sourceDetails = new DefaultSourceDetails();
+        parseFile(sourceFile, sourceDetails);
+        return sourceDetails;
+    }
+
+    private void parseFile(File file, DefaultSourceDetails sourceDetails) {
+        try {
+            BufferedReader bf = new BufferedReader(new PreprocessingReader(new BufferedReader(new FileReader(file))));
+
+            try {
+                String line;
+                while ((line = bf.readLine()) != null) {
+                    Matcher m = includePattern.matcher(line.trim());
+
+                    if (m.matches()) {
+                        boolean isImport = "import".equals(m.group(1));
+                        String value = m.group(2);
+                        if (isImport) {
+                            sourceDetails.getImports().add(value);
+                        } else {
+                            sourceDetails.getIncludes().add(value);
+                        }
+                    }
+                }
+            } finally {
+                IOUtils.closeQuietly(bf);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private static class DefaultSourceDetails implements SourceDetails {
+        private final List<String> includes = new ArrayList<String>();
+        private final List<String> imports = new ArrayList<String>();
+
+        public List<String> getIncludes() {
+            return includes;
+        }
+
+        public List<String> getImports() {
+            return imports;
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/registry/NativeLanguageServices.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/registry/NativeLanguageServices.java
new file mode 100644
index 0000000..e2803b1
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/internal/registry/NativeLanguageServices.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal.registry;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.language.nativeplatform.internal.incremental.DefaultCompilationStateCacheFactory;
+import org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder;
+
+public class NativeLanguageServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+        registration.add(DefaultCompilationStateCacheFactory.class);
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.add(IncrementalCompilerBuilder.class);
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativeCompileTask.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativeCompileTask.java
new file mode 100644
index 0000000..1ed42d4
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativeCompileTask.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.tasks;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.internal.Cast;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.internal.operations.logging.BuildOperationLoggerFactory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder;
+import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.PCHObjectDirectoryGeneratorUtil;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * Compiles native source files into object files.
+ */
+ at Incubating
+public abstract class AbstractNativeCompileTask extends DefaultTask {
+    private NativeToolChainInternal toolChain;
+    private NativePlatformInternal targetPlatform;
+    private boolean positionIndependentCode;
+    private File objectFileDir;
+    private ConfigurableFileCollection includes;
+    private ConfigurableFileCollection source;
+    private Map<String, String> macros;
+    private List<String> compilerArgs;
+    private File prefixHeaderFile;
+    private Set<String> preCompiledHeaders;
+    private ConfigurableFileCollection preCompiledHeaderInclude;
+
+    public AbstractNativeCompileTask() {
+        includes = getProject().files();
+        source = getProject().files();
+        preCompiledHeaderInclude = getProject().files();
+        getInputs().property("outputType", new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return NativeToolChainInternal.Identifier.identify(toolChain, targetPlatform);
+            }
+        });
+    }
+
+    @Inject
+    public IncrementalCompilerBuilder getIncrementalCompilerBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    public BuildOperationLoggerFactory getOperationLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    public void compile(IncrementalTaskInputs inputs) {
+        BuildOperationLogger operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir());
+        NativeCompileSpec spec = createCompileSpec();
+        spec.setTargetPlatform(targetPlatform);
+        spec.setTempDir(getTemporaryDir());
+        spec.setObjectFileDir(getObjectFileDir());
+        spec.include(getIncludes());
+        spec.source(getSource());
+        spec.setMacros(getMacros());
+        spec.args(getCompilerArgs());
+        spec.setPositionIndependentCode(isPositionIndependentCode());
+        spec.setIncrementalCompile(inputs.isIncremental());
+        spec.setOperationLogger(operationLogger);
+
+        if (!preCompiledHeaderInclude.isEmpty()) {
+            File pchDir = PCHObjectDirectoryGeneratorUtil.generatePCHObjectDirectory(spec.getTempDir(), getPrefixHeaderFile(), preCompiledHeaderInclude.getSingleFile());
+            spec.setPrefixHeaderFile(new File(pchDir, prefixHeaderFile.getName()));
+            spec.setPreCompiledHeaderObjectFile(new File(pchDir, preCompiledHeaderInclude.getSingleFile().getName()));
+            spec.setPreCompiledHeaders(getPreCompiledHeaders());
+        }
+
+        PlatformToolProvider platformToolProvider = toolChain.select(targetPlatform);
+        setDidWork(doCompile(spec, platformToolProvider).getDidWork());
+    }
+
+    private <T extends NativeCompileSpec> WorkResult doCompile(T spec, PlatformToolProvider platformToolProvider) {
+        Class<T> specType = Cast.uncheckedCast(spec.getClass());
+        Compiler<T> baseCompiler = platformToolProvider.newCompiler(specType);
+        Compiler<T> incrementalCompiler = getIncrementalCompilerBuilder().createIncrementalCompiler(this, baseCompiler, toolChain);
+        Compiler<T> loggingCompiler = BuildOperationLoggingCompilerDecorator.wrap(incrementalCompiler);
+        return loggingCompiler.execute(spec);
+    }
+
+    protected abstract NativeCompileSpec createCompileSpec();
+
+    /**
+     * The tool chain used for compilation.
+     */
+    public NativeToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public void setToolChain(NativeToolChain toolChain) {
+        this.toolChain = (NativeToolChainInternal) toolChain;
+    }
+
+    /**
+     * The platform being targeted.
+     */
+    public NativePlatform getTargetPlatform() {
+        return targetPlatform;
+    }
+
+    public void setTargetPlatform(NativePlatform targetPlatform) {
+        this.targetPlatform = (NativePlatformInternal) targetPlatform;
+    }
+
+    /**
+     * Should the compiler generate position independent code?
+     */
+    @Input
+    public boolean isPositionIndependentCode() {
+        return positionIndependentCode;
+    }
+
+    public void setPositionIndependentCode(boolean positionIndependentCode) {
+        this.positionIndependentCode = positionIndependentCode;
+    }
+
+    /**
+     * The directory where object files will be generated.
+     */
+    @OutputDirectory
+    public File getObjectFileDir() {
+        return objectFileDir;
+    }
+
+    public void setObjectFileDir(File objectFileDir) {
+        this.objectFileDir = objectFileDir;
+    }
+
+    /**
+     * Returns the header directories to be used for compilation.
+     */
+    @InputFiles
+    public FileCollection getIncludes() {
+        return includes;
+    }
+
+    /**
+     * Add directories where the compiler should search for header files.
+     */
+    public void includes(Object includeRoots) {
+        includes.from(includeRoots);
+    }
+
+    /**
+     * Returns the source files to be compiled.
+     */
+    @InputFiles
+    public FileCollection getSource() {
+        return source;
+    }
+
+    /**
+     * Adds a set of source files to be compiled. The provided sourceFiles object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    public void source(Object sourceFiles) {
+        source.from(sourceFiles);
+    }
+
+    /**
+     * Macros that should be defined for the compiler.
+     */
+    @Input
+    public Map<String, String> getMacros() {
+        return macros;
+    }
+
+    public void setMacros(Map<String, String> macros) {
+        this.macros = macros;
+    }
+
+    /**
+     * Additional arguments to provide to the compiler.
+     */
+    @Input
+    public List<String> getCompilerArgs() {
+        return compilerArgs;
+    }
+
+    public void setCompilerArgs(List<String> compilerArgs) {
+        this.compilerArgs = compilerArgs;
+    }
+
+    /**
+     * Returns the pre-compiled header file to be used during compilation
+     */
+    public File getPrefixHeaderFile() {
+        return prefixHeaderFile;
+    }
+
+    public void setPrefixHeaderFile(File prefixHeaderFile) {
+        this.prefixHeaderFile = prefixHeaderFile;
+    }
+
+    /**
+     * Returns the pre-compiled header object file to be used during compilation
+     */
+    @InputFiles
+    public FileCollection getPreCompiledHeaderInclude() {
+        return preCompiledHeaderInclude;
+    }
+
+    /**
+     * Set the pre-compiled header the compiler should use.
+     */
+    public void preCompiledHeaderInclude(Object preCompiledHeader) {
+        preCompiledHeaderInclude.from(preCompiledHeader);
+    }
+
+    public Set<String> getPreCompiledHeaders() {
+        return preCompiledHeaders;
+    }
+
+    public void setPreCompiledHeaders(Set<String> preCompiledHeaders) {
+        this.preCompiledHeaders = preCompiledHeaders;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativePCHCompileTask.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativePCHCompileTask.java
new file mode 100644
index 0000000..d9b8eeb
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/AbstractNativePCHCompileTask.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.tasks;
+
+/**
+ * Compiles native header source files into object files.
+ */
+abstract public class AbstractNativePCHCompileTask extends AbstractNativeCompileTask {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/package-info.java
new file mode 100644
index 0000000..6d12ba2
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/nativeplatform/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base classes for native language compile tasks.
+ */
+package org.gradle.language.nativeplatform.tasks;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/ObjectiveCSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/ObjectiveCSourceSet.java
new file mode 100644
index 0000000..d335085
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/ObjectiveCSourceSet.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+
+/**
+ * A set of Objective-C source files.
+ *
+ * <p>An ObjectiveC source set contains a set of source files, together with an optional set of exported header files.</p>
+ *
+ * <pre autoTested="true">
+ * apply plugin: "objective-c"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 objc {
+ *                     source {
+ *                         srcDirs "src/main/objectiveC", "src/shared/objectiveC"
+ *                         include "**{@literal /}*.m"
+ *                     }
+ *                     exportedHeaders {
+ *                         srcDirs "src/main/include"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface ObjectiveCSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCCompileSpec.java
new file mode 100644
index 0000000..dc7f593
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCCompileSpec.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.language.objectivec.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCCompileSpec;
+
+public class DefaultObjectiveCCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCCompileSpec {
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCPCHCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCPCHCompileSpec.java
new file mode 100644
index 0000000..3998442
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCPCHCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCPCHCompileSpec;
+
+public class DefaultObjectiveCPCHCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCPCHCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.java
new file mode 100644
index 0000000..78666a5
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/internal/DefaultObjectiveCSourceSet.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.language.objectivec.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractHeaderExportingDependentSourceSet;
+import org.gradle.language.objectivec.ObjectiveCSourceSet;
+
+public class DefaultObjectiveCSourceSet extends AbstractHeaderExportingDependentSourceSet implements ObjectiveCSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "Objective-C source";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/objectivec/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/objectivec/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPCHPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPCHPlugin.java
new file mode 100644
index 0000000..a78f6d8
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPCHPlugin.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig;
+import org.gradle.language.objectivec.ObjectiveCSourceSet;
+import org.gradle.language.objectivec.tasks.ObjectiveCPreCompiledHeaderCompile;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.internal.pch.PreCompiledHeaderTransformContainer;
+
+import java.util.Map;
+
+/**
+ * Adds support for compiling Objective C pre-compiled headers.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+public class ObjectiveCLangPCHPlugin extends RuleSource {
+    @Mutate
+    void registerPreCompiledHeaderTask(PreCompiledHeaderTransformContainer pchTransformContainer) {
+        pchTransformContainer.add(new ObjectiveCPCH());
+    }
+
+    private static class ObjectiveCPCH extends NativeLanguageTransform<ObjectiveCSourceSet> {
+        public Class<ObjectiveCSourceSet> getSourceSetType() {
+            return ObjectiveCSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("objcCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        @Override
+        public SourceTransformTaskConfig getTransformTask() {
+            return new PCHCompileTaskConfig(this, ObjectiveCPreCompiledHeaderCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.java
new file mode 100644
index 0000000..31f11a3
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCLangPlugin.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.objectivec.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.nativeplatform.internal.CompileTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.objectivec.ObjectiveCSourceSet;
+import org.gradle.language.objectivec.internal.DefaultObjectiveCSourceSet;
+import org.gradle.language.objectivec.tasks.ObjectiveCCompile;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core Objective-C language support.
+ */
+ at Incubating
+public class ObjectiveCLangPlugin implements Plugin<Project> {
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<ObjectiveCSourceSet> builder) {
+            builder.setLanguageName("objc");
+            builder.defaultImplementation(DefaultObjectiveCSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new ObjectiveC());
+        }
+    }
+
+    private static class ObjectiveC extends NativeLanguageTransform<ObjectiveCSourceSet> {
+        public Class<ObjectiveCSourceSet> getSourceSetType() {
+            return ObjectiveCSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("objcCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new CompileTaskConfig(this, ObjectiveCCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCPlugin.java
new file mode 100644
index 0000000..532ab77
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/ObjectiveCPlugin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from Objective-C sources.
+ *
+ * <p>Automatically includes the {@link ObjectiveCLangPlugin} for core Objective-C support and the {@link org.gradle.nativeplatform.plugins.NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.objectivec.tasks.ObjectiveCCompile} task for each {@link org.gradle.language.objectivec.ObjectiveCSourceSet} to compile the Objective-C sources.</li>
+ */
+ at Incubating
+public class ObjectiveCPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(ObjectiveCLangPlugin.class);
+        project.getPluginManager().apply(ObjectiveCLangPCHPlugin.class);
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/package-info.java
new file mode 100644
index 0000000..e1e021e
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from Objective-C language sources.
+ */
+package org.gradle.language.objectivec.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCCompile.java
new file mode 100644
index 0000000..4d93f21
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCCompile.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.objectivec.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.language.objectivec.internal.DefaultObjectiveCCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles Objective-C source files into object files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class ObjectiveCCompile extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultObjectiveCCompileSpec();
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCPreCompiledHeaderCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCPreCompiledHeaderCompile.java
new file mode 100644
index 0000000..5e05e43
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/ObjectiveCPreCompiledHeaderCompile.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.tasks;
+
+import org.gradle.language.nativeplatform.tasks.AbstractNativePCHCompileTask;
+import org.gradle.language.objectivec.internal.DefaultObjectiveCPCHCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles Objective C header source files into object files.
+ */
+public class ObjectiveCPreCompiledHeaderCompile extends AbstractNativePCHCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultObjectiveCPCHCompileSpec();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/tasks/package-info.java
new file mode 100644
index 0000000..3d39c55
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivec/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 compiling Objective-C sources for a native runtime.
+ */
+package org.gradle.language.objectivec.tasks;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.java
new file mode 100644
index 0000000..a09aca4
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/ObjectiveCppSourceSet.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.language.objectivecpp;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of Objective-C++ source files.
+ *
+ * <p>An Objective-C++ source set contains a set of source files, together with an optional set of exported header files.</p>
+ *
+ * <pre autoTested="true">
+ * apply plugin: "objective-cpp"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 objcpp {
+ *                     source {
+ *                         srcDirs "src/main/objectiveCpp", "src/shared/objectiveCpp"
+ *                         include "**{@literal /}*.mm"
+ *                     }
+ *                     exportedHeaders {
+ *                         srcDirs "src/main/include"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface ObjectiveCppSourceSet extends HeaderExportingSourceSet, LanguageSourceSet, DependentSourceSet {
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.java
new file mode 100644
index 0000000..aac0234
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppCompileSpec.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.language.objectivecpp.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppCompileSpec;
+
+public class DefaultObjectiveCppCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCppCompileSpec {
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppPCHCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppPCHCompileSpec.java
new file mode 100644
index 0000000..57668b6
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppPCHCompileSpec.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppPCHCompileSpec;
+
+public class DefaultObjectiveCppPCHCompileSpec extends AbstractNativeCompileSpec implements ObjectiveCppPCHCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.java
new file mode 100644
index 0000000..f89116d
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/internal/DefaultObjectiveCppSourceSet.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.language.objectivecpp.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractHeaderExportingDependentSourceSet;
+import org.gradle.language.objectivecpp.ObjectiveCppSourceSet;
+
+public class DefaultObjectiveCppSourceSet extends AbstractHeaderExportingDependentSourceSet implements ObjectiveCppSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "Objective-C++ source";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/objectivecpp/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPCHPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPCHPlugin.java
new file mode 100644
index 0000000..1b489e2
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPCHPlugin.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig;
+import org.gradle.language.objectivecpp.ObjectiveCppSourceSet;
+import org.gradle.language.objectivecpp.tasks.ObjectiveCppPreCompiledHeaderCompile;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.internal.pch.PreCompiledHeaderTransformContainer;
+
+import java.util.Map;
+
+/**
+ * Adds support for compiling Objective C++ pre-compiled headers.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+public class ObjectiveCppLangPCHPlugin extends RuleSource {
+    @Mutate
+    void registerPreCompiledHeaderTask(PreCompiledHeaderTransformContainer pchTransformContainer) {
+        pchTransformContainer.add(new ObjectiveCppPCH());
+    }
+
+    private static class ObjectiveCppPCH extends NativeLanguageTransform<ObjectiveCppSourceSet> {
+        public Class<ObjectiveCppSourceSet> getSourceSetType() {
+            return ObjectiveCppSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("objcppCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        @Override
+        public SourceTransformTaskConfig getTransformTask() {
+            return new PCHCompileTaskConfig(this, ObjectiveCppPreCompiledHeaderCompile.class);
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.java
new file mode 100644
index 0000000..09b3177
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPlugin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.objectivecpp.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.nativeplatform.internal.CompileTaskConfig;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.objectivecpp.ObjectiveCppSourceSet;
+import org.gradle.language.objectivecpp.internal.DefaultObjectiveCppSourceSet;
+import org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core Objective-Cpp language support.
+ */
+ at Incubating
+public class ObjectiveCppLangPlugin implements Plugin<Project> {
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<ObjectiveCppSourceSet> builder) {
+            builder.setLanguageName("objcpp");
+            builder.defaultImplementation(DefaultObjectiveCppSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new ObjectiveCpp());
+        }
+    }
+
+    private static class ObjectiveCpp extends NativeLanguageTransform<ObjectiveCppSourceSet> {
+        public Class<ObjectiveCppSourceSet> getSourceSetType() {
+            return ObjectiveCppSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("objcppCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new CompileTaskConfig(this, ObjectiveCppCompile.class);
+        }
+
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppPlugin.java
new file mode 100644
index 0000000..96a70d7
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/ObjectiveCppPlugin.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from Objective-C++ sources.
+ *
+ * <p>Automatically includes the {@link ObjectiveCppLangPlugin} for core Objective-C++ support and the {@link NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile} task for each {@link org.gradle.language.objectivecpp.ObjectiveCppSourceSet} to compile the Objective-C++ sources.</li>
+ */
+ at Incubating
+public class ObjectiveCppPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(ObjectiveCppLangPlugin.class);
+        project.getPluginManager().apply(ObjectiveCppLangPCHPlugin.class);
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/package-info.java
new file mode 100644
index 0000000..05e884b
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from Objective-C++ language sources.
+ */
+package org.gradle.language.objectivecpp.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppCompile.java
new file mode 100644
index 0000000..87cdc82
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppCompile.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.objectivecpp.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask;
+import org.gradle.language.objectivecpp.internal.DefaultObjectiveCppCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles Objective-C++ source files into object files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class ObjectiveCppCompile extends AbstractNativeCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultObjectiveCppCompileSpec();
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppPreCompiledHeaderCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppPreCompiledHeaderCompile.java
new file mode 100644
index 0000000..f3abfe9
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/ObjectiveCppPreCompiledHeaderCompile.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.tasks;
+
+import org.gradle.language.nativeplatform.tasks.AbstractNativePCHCompileTask;
+import org.gradle.language.objectivecpp.internal.DefaultObjectiveCppPCHCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * Compiles Objective C++ header source files into object files.
+ */
+public class ObjectiveCppPreCompiledHeaderCompile extends AbstractNativePCHCompileTask {
+    @Override
+    protected NativeCompileSpec createCompileSpec() {
+        return new DefaultObjectiveCppPCHCompileSpec();
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/tasks/package-info.java
new file mode 100644
index 0000000..137473d
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/objectivecpp/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 compiling Objective-C++ sources for a native runtime.
+ */
+package org.gradle.language.objectivecpp.tasks;
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/WindowsResourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/WindowsResourceSet.java
new file mode 100644
index 0000000..2f8b8a8
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/WindowsResourceSet.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.language.rc;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.language.nativeplatform.NativeResourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of Windows Resource definition files.
+ *
+ * <p>A Windows Resource set contains a set of script files, together with an optional set of header files.</p>
+ *
+ * <pre autoTested="true">
+ * apply plugin: "windows-resources"
+ *
+ * model {
+ *     components {
+ *         main(NativeLibrarySpec) {
+ *             sources {
+ *                 rc {
+ *                     source {
+ *                         srcDirs "src/main/rc"
+ *                         include "**{@literal /}*.rc"
+ *                     }
+ *                     exportedHeaders {
+ *                         srcDirs "src/main/include"
+ *                     }
+ *                 }
+ *             }
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface WindowsResourceSet extends LanguageSourceSet, HeaderExportingSourceSet, NativeResourceSet {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceCompileSpec.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceCompileSpec.java
new file mode 100644
index 0000000..e001092
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceCompileSpec.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.language.rc.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.WindowsResourceCompileSpec;
+
+public class DefaultWindowsResourceCompileSpec extends AbstractNativeCompileSpec implements WindowsResourceCompileSpec {
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceSet.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceSet.java
new file mode 100644
index 0000000..1a5cdb3
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/internal/DefaultWindowsResourceSet.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.language.rc.internal;
+
+import org.gradle.language.nativeplatform.internal.AbstractHeaderExportingSourceSet;
+import org.gradle.language.rc.WindowsResourceSet;
+
+public class DefaultWindowsResourceSet extends AbstractHeaderExportingSourceSet implements WindowsResourceSet {
+    @Override
+    protected String getTypeName() {
+        return "windows resources";
+    }
+}
diff --git a/subprojects/cpp/src/main/groovy/org/gradle/language/rc/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/package-info.java
similarity index 100%
rename from subprojects/cpp/src/main/groovy/org/gradle/language/rc/package-info.java
rename to subprojects/language-native/src/main/java/org/gradle/language/rc/package-info.java
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.java
new file mode 100644
index 0000000..a356eda
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourceScriptPlugin.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc.plugins;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.language.nativeplatform.internal.NativeLanguageTransform;
+import org.gradle.language.rc.WindowsResourceSet;
+import org.gradle.language.rc.internal.DefaultWindowsResourceSet;
+import org.gradle.language.rc.plugins.internal.WindowsResourcesCompileTaskConfig;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.util.Map;
+
+/**
+ * Adds core language support for Windows resource script files.
+ */
+ at Incubating
+public class WindowsResourceScriptPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<WindowsResourceSet> builder) {
+            builder.setLanguageName("rc");
+            builder.defaultImplementation(DefaultWindowsResourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new WindowsResources());
+        }
+    }
+
+    private static class WindowsResources extends NativeLanguageTransform<WindowsResourceSet> {
+        public Class<WindowsResourceSet> getSourceSetType() {
+            return WindowsResourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            Map<String, Class<?>> tools = Maps.newLinkedHashMap();
+            tools.put("rcCompiler", DefaultPreprocessingTool.class);
+            return tools;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new WindowsResourcesCompileTaskConfig();
+        }
+
+        @Override
+        public boolean applyToBinary(BinarySpec binary) {
+            return binary instanceof NativeBinarySpec && shouldProcessResources((NativeBinarySpec) binary);
+        }
+
+        private boolean shouldProcessResources(NativeBinarySpec binary) {
+            return binary.getTargetPlatform().getOperatingSystem().isWindows();
+        }
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourcesPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourcesPlugin.java
new file mode 100644
index 0000000..4e0759f
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/WindowsResourcesPlugin.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.rc.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+
+/**
+ * A plugin for projects wishing to build native binary components from Windows Resource sources.
+ *
+ * <p>Automatically includes the {@link WindowsResourceScriptPlugin} for core Windows Resource source support and the {@link NativeComponentPlugin} for native component support.</p>
+ *
+ * <li>Creates a {@link org.gradle.language.rc.tasks.WindowsResourceCompile} task for each {@link org.gradle.language.rc.WindowsResourceSet} to compile the sources.</li>
+ */
+ at Incubating
+public class WindowsResourcesPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+        project.getPluginManager().apply(WindowsResourceScriptPlugin.class);
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/internal/WindowsResourcesCompileTaskConfig.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/internal/WindowsResourcesCompileTaskConfig.java
new file mode 100644
index 0000000..b81b4ec
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/internal/WindowsResourcesCompileTaskConfig.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc.plugins.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.rc.WindowsResourceSet;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.internal.StaticLibraryBinarySpecInternal;
+import org.gradle.language.PreprocessingTool;
+import org.gradle.language.rc.tasks.WindowsResourceCompile;
+import org.gradle.platform.base.BinarySpec;
+
+import java.io.File;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+public class WindowsResourcesCompileTaskConfig implements SourceTransformTaskConfig {
+    public String getTaskPrefix() {
+        return "compile";
+    }
+
+    public Class<? extends DefaultTask> getTaskType() {
+        return WindowsResourceCompile.class;
+    }
+
+    public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
+        configureResourceCompileTask((WindowsResourceCompile) task, (NativeBinarySpecInternal) binary, (WindowsResourceSet) sourceSet);
+    }
+
+    private void configureResourceCompileTask(WindowsResourceCompile task, final NativeBinarySpecInternal binary, final WindowsResourceSet sourceSet) {
+        task.setDescription(String.format("Compiles resources of the %s of %s", sourceSet, binary));
+
+        task.setToolChain(binary.getToolChain());
+        task.setTargetPlatform(binary.getTargetPlatform());
+
+        task.includes(new Callable<Set<File>>() {
+            public Set<File> call() {
+                return sourceSet.getExportedHeaders().getSrcDirs();
+            }
+        });
+        task.source(sourceSet.getSource());
+
+        final Project project = task.getProject();
+        task.setOutputDir(project.file(String.valueOf(project.getBuildDir()) + "/objs/" + binary.getNamingScheme().getOutputDirectoryBase() + "/" + ((LanguageSourceSetInternal) sourceSet).getFullName()));
+
+        PreprocessingTool rcCompiler = (PreprocessingTool) ((ExtensionAware) binary).getExtensions().getByName("rcCompiler");
+        task.setMacros(rcCompiler.getMacros());
+        task.setCompilerArgs(rcCompiler.getArgs());
+
+        FileTree resourceOutputs = task.getOutputs().getFiles().getAsFileTree().matching(new PatternSet().include("**/*.res"));
+        binary.binaryInputs(resourceOutputs);
+        if (binary instanceof StaticLibraryBinarySpecInternal) {
+            ((StaticLibraryBinarySpecInternal) binary).additionalLinkFiles(resourceOutputs);
+        }
+    }
+
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/package-info.java
new file mode 100644
index 0000000..c342c3b
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 from Windows Resource scripts.
+ */
+package org.gradle.language.rc.plugins;
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/WindowsResourceCompile.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/WindowsResourceCompile.java
new file mode 100644
index 0000000..3ac3c06
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/WindowsResourceCompile.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.rc.tasks;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.*;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.internal.Cast;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.internal.operations.logging.BuildOperationLoggerFactory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerUtil;
+import org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder;
+import org.gradle.language.rc.internal.DefaultWindowsResourceCompileSpec;
+import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Compiles Windows Resource scripts into .res files.
+ */
+ at Incubating
+ at ParallelizableTask
+public class WindowsResourceCompile extends DefaultTask {
+
+    private NativeToolChainInternal toolChain;
+    private NativePlatformInternal targetPlatform;
+    private File outputDir;
+    private ConfigurableFileCollection includes;
+    private ConfigurableFileCollection source;
+    private Map<String, String> macros = new LinkedHashMap<String, String>();
+    private List<String> compilerArgs = new ArrayList<String>();
+
+    public WindowsResourceCompile() {
+        includes = getProject().files();
+        source = getProject().files();
+        getInputs().property("outputType", new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return NativeToolChainInternal.Identifier.identify(toolChain, targetPlatform);
+            }
+        });
+    }
+
+    @Inject
+    public IncrementalCompilerBuilder getIncrementalCompilerBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    public BuildOperationLoggerFactory getOperationLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    public void compile(IncrementalTaskInputs inputs) {
+        BuildOperationLogger operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir());
+
+        NativeCompileSpec spec = new DefaultWindowsResourceCompileSpec();
+        spec.setTempDir(getTemporaryDir());
+        spec.setObjectFileDir(getOutputDir());
+        spec.include(getIncludes());
+        spec.source(getSource());
+        spec.setMacros(getMacros());
+        spec.args(getCompilerArgs());
+        spec.setIncrementalCompile(inputs.isIncremental());
+        spec.setOperationLogger(operationLogger);
+
+        PlatformToolProvider platformToolProvider = toolChain.select(targetPlatform);
+        WorkResult result = doCompile(spec, platformToolProvider);
+        setDidWork(result.getDidWork());
+    }
+
+    private <T extends NativeCompileSpec> WorkResult doCompile(T spec, PlatformToolProvider platformToolProvider) {
+        Class<T> specType = Cast.uncheckedCast(spec.getClass());
+        Compiler<T> baseCompiler = platformToolProvider.newCompiler(specType);
+        Compiler<T> incrementalCompiler = getIncrementalCompilerBuilder().createIncrementalCompiler(this, baseCompiler, toolChain);
+        Compiler<T> loggingCompiler = BuildOperationLoggingCompilerDecorator.wrap(incrementalCompiler);
+        return CompilerUtil.castCompiler(loggingCompiler).execute(spec);
+    }
+
+    /**
+     * The tool chain used for compilation.
+     */
+    public NativeToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public void setToolChain(NativeToolChain toolChain) {
+        this.toolChain = (NativeToolChainInternal) toolChain;
+    }
+
+    /**
+     * The platform being targeted.
+     */
+    public NativePlatform getTargetPlatform() {
+        return targetPlatform;
+    }
+
+    public void setTargetPlatform(NativePlatform targetPlatform) {
+        this.targetPlatform = (NativePlatformInternal) targetPlatform;
+    }
+
+    /**
+     * The directory where object files will be generated.
+     */
+    @OutputDirectory
+    public File getOutputDir() {
+        return outputDir;
+    }
+
+    public void setOutputDir(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+    /**
+     * Returns the header directories to be used for compilation.
+     */
+    @InputFiles
+    public FileCollection getIncludes() {
+        return includes;
+    }
+
+    /**
+     * Add directories where the compiler should search for header files.
+     */
+    public void includes(Object includeRoots) {
+        includes.from(includeRoots);
+    }
+
+    /**
+     * Returns the source files to be compiled.
+     */
+    @InputFiles
+    public FileCollection getSource() {
+        return source;
+    }
+
+    /**
+     * Adds a set of source files to be compiled. The provided sourceFiles object is evaluated as per {@link org.gradle.api.Project#files(Object...)}.
+     */
+    public void source(Object sourceFiles) {
+        source.from(sourceFiles);
+    }
+
+    /**
+     * Macros that should be defined for the compiler.
+     */
+    @Input
+    public Map<String, String> getMacros() {
+        return macros;
+    }
+
+    public void setMacros(Map<String, String> macros) {
+        this.macros = macros;
+    }
+
+    /**
+     * Additional arguments to provide to the compiler.
+     */
+    @Input
+    public List<String> getCompilerArgs() {
+        return compilerArgs;
+    }
+
+    public void setCompilerArgs(List<String> compilerArgs) {
+        this.compilerArgs = compilerArgs;
+    }
+}
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/package-info.java b/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/package-info.java
new file mode 100644
index 0000000..d3908bb
--- /dev/null
+++ b/subprojects/language-native/src/main/java/org/gradle/language/rc/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 compiling Windows resources for a native runtime.
+ */
+package org.gradle.language.rc.tasks;
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler-lang.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler-lang.properties
new file mode 100644
index 0000000..d648157
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.assembler.plugins.AssemblerLangPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler.properties
new file mode 100644
index 0000000..abc9ed7
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.assembler.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.assembler.plugins.AssemblerPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c-lang.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c-lang.properties
new file mode 100644
index 0000000..ef75a26
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.c.plugins.CLangPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c.properties
new file mode 100644
index 0000000..19f5c10
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.c.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.c.plugins.CPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp-lang.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp-lang.properties
new file mode 100644
index 0000000..719a15b
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.cpp.plugins.CppLangPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp.properties
new file mode 100644
index 0000000..baa4a98
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cpp.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.cpp.plugins.CppPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c-lang.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c-lang.properties
new file mode 100644
index 0000000..a117c56
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.objectivec.plugins.ObjectiveCLangPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c.properties
new file mode 100644
index 0000000..210a3a7
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-c.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.objectivec.plugins.ObjectiveCPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp-lang.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp-lang.properties
new file mode 100644
index 0000000..c738acc
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp-lang.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.objectivecpp.plugins.ObjectiveCppLangPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp.properties
new file mode 100644
index 0000000..cf1b91e
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.objective-cpp.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.objectivecpp.plugins.ObjectiveCppPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resource-script.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resource-script.properties
new file mode 100644
index 0000000..6716660
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resource-script.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.rc.plugins.WindowsResourceScriptPlugin
diff --git a/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resources.properties b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resources.properties
new file mode 100644
index 0000000..a4c5922
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/gradle-plugins/org.gradle.windows-resources.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.language.rc.plugins.WindowsResourcesPlugin
\ No newline at end of file
diff --git a/subprojects/language-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/language-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..041600b
--- /dev/null
+++ b/subprojects/language-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.language.nativeplatform.internal.registry.NativeLanguageServices
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/AbstractNativeComponentPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/AbstractNativeComponentPluginTest.groovy
new file mode 100644
index 0000000..4ec23bc
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/AbstractNativeComponentPluginTest.groovy
@@ -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.language
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskDependencyMatchers
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.nativeplatform.NativeBinary
+import org.gradle.nativeplatform.NativeExecutableBinarySpec
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.util.GFileUtils
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+abstract class AbstractNativeComponentPluginTest extends Specification {
+    final def project = TestUtil.createRootProject()
+
+    abstract Class<? extends Plugin> getPluginClass();
+
+    abstract Class<? extends LanguageSourceSet> getSourceSetClass();
+
+    abstract Class<? extends Task> getCompileTaskClass();
+
+    abstract String getPluginName();
+
+    def "creates source set with conventional locations for components"() {
+        when:
+        dsl {
+            pluginManager.apply pluginClass
+
+            model {
+                components {
+                    exe(NativeExecutableSpec)
+                    lib(NativeLibrarySpec)
+                }
+            }
+        }
+
+        then:
+        def components = project.componentSpecs
+        components.size() == 2
+        components*.name == ["exe", "lib"]
+
+        and:
+        def exe = components.exe
+        exe.sources instanceof FunctionalSourceSet
+        sourceSetClass.isInstance(exe.sources."$pluginName")
+        exe.sources."$pluginName".source.srcDirs == [project.file("src/exe/$pluginName")] as Set
+        exe.sources."$pluginName".exportedHeaders.srcDirs == [project.file("src/exe/headers")] as Set
+
+        and:
+        def lib = components.lib
+        lib.sources instanceof FunctionalSourceSet
+        sourceSetClass.isInstance(lib.sources."$pluginName")
+        lib.sources."$pluginName".source.srcDirs == [project.file("src/lib/$pluginName")] as Set
+        lib.sources."$pluginName".exportedHeaders.srcDirs == [project.file("src/lib/headers")] as Set
+
+        and:
+        project.sources as Set == lib.sources + exe.sources
+    }
+
+    def "can configure source set locations"() {
+        given:
+        dsl {
+            pluginManager.apply pluginClass
+
+            model {
+                components {
+                    lib(NativeLibrarySpec) {
+                        sources {
+                            "$pluginName" {
+                                source {
+                                    srcDirs "d3"
+                                }
+                                exportedHeaders {
+                                    srcDirs "h3"
+                                }
+                            }
+                        }
+                    }
+                    exe(NativeExecutableSpec) {
+                        sources {
+                            "$pluginName" {
+                                source {
+                                    srcDirs "d1", "d2"
+                                }
+                                exportedHeaders {
+                                    srcDirs "h1", "h2"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        expect:
+        def exe = project.componentSpecs.exe
+        with(exe.sources."$pluginName") {
+            source.srcDirs*.name == ["d1", "d2"]
+            exportedHeaders.srcDirs*.name == ["h1", "h2"]
+        }
+
+        def lib = project.componentSpecs.lib
+        with(lib.sources."$pluginName") {
+            source.srcDirs*.name == ["d3"]
+            exportedHeaders.srcDirs*.name == ["h3"]
+        }
+    }
+
+    def "creates compile tasks for each non-empty executable source set"() {
+        when:
+        touch("src/test/$pluginName/file.o")
+        touch("src/test/anotherOne/file.o")
+        dsl {
+            pluginManager.apply pluginClass
+            model {
+                components {
+                    test(NativeExecutableSpec) {
+                        binaries.all { NativeBinary binary ->
+                            binary."${pluginName}Compiler".define "NDEBUG"
+                            binary."${pluginName}Compiler".define "LEVEL", "1"
+                            binary."${pluginName}Compiler".args "ARG1", "ARG2"
+                        }
+                        sources {
+                            anotherOne(sourceSetClass) {}
+                            emptyOne(sourceSetClass) {}
+                        }
+                    }
+                }
+            }
+        }
+
+        then:
+        NativeExecutableBinarySpec binary = project.binaries.testExecutable
+        binary.tasks.withType(compileTaskClass)*.name as Set == ["compileTestExecutableTestAnotherOne", "compileTestExecutableTest${StringUtils.capitalize(pluginName)}"] as Set
+
+        and:
+        binary.tasks.withType(compileTaskClass).each { compile ->
+            compile.toolChain == binary.toolChain
+            compile.macros == [NDEBUG: null, LEVEL: "1"]
+            compile.compilerArgs == ["ARG1", "ARG2"]
+        }
+
+        and:
+        def linkTask = binary.tasks.link
+        linkTask TaskDependencyMatchers.dependsOn("compileTestExecutableTestAnotherOne", "compileTestExecutableTest${StringUtils.capitalize(pluginName)}")
+    }
+
+
+    def touch(String filePath) {
+        GFileUtils.touch(project.file(filePath))
+    }
+
+    def dsl(@DelegatesTo(Project) Closure closure) {
+        closure.delegate = project
+        closure()
+        project.tasks.realize()
+        project.bindAllModelRules()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.groovy
new file mode 100644
index 0000000..2e6e1bc
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerLangPluginTest.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.language.assembler.plugins
+
+import org.gradle.language.assembler.AssemblerSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class AssemblerLangPluginTest extends AbstractLanguagePluginSpec {
+    @Override
+    def getPluginClass() {
+        return AssemblerLangPlugin
+    }
+
+    @Override
+    def getLanguageSourceSet() {
+        return AssemblerSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "asm"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerPluginTest.groovy
new file mode 100644
index 0000000..3e7c876
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/plugins/AssemblerPluginTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.assembler.plugins
+
+import org.gradle.api.Project
+import org.gradle.api.tasks.TaskDependencyMatchers
+import org.gradle.language.assembler.AssemblerSourceSet
+import org.gradle.language.assembler.tasks.Assemble
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.nativeplatform.*
+import org.gradle.util.GFileUtils
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class AssemblerPluginTest extends Specification {
+    final def project = TestUtil.createRootProject()
+
+    def "creates asm source set with conventional locations for components"() {
+        when:
+        dsl {
+            pluginManager.apply AssemblerPlugin
+            model {
+                components {
+                    exe(NativeExecutableSpec)
+                }
+            }
+        }
+
+        then:
+        def exe = project.componentSpecs.exe
+        exe.sources instanceof FunctionalSourceSet
+        exe.sources.asm instanceof AssemblerSourceSet
+        exe.sources.asm.source.srcDirs == [project.file("src/exe/asm")] as Set
+
+        and:
+        project.sources as Set == exe.sources as Set
+    }
+
+    def "can configure source set locations"() {
+        given:
+        dsl {
+            pluginManager.apply AssemblerPlugin
+            model {
+                components {
+                    exe(NativeExecutableSpec) {
+                        sources {
+                            asm {
+                                source {
+                                    srcDirs "d1", "d2"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        expect:
+        project.componentSpecs.exe.sources.asm.source.srcDirs*.name == ["d1", "d2"]
+    }
+
+    def "creates assemble tasks for each non-empty executable source set "() {
+        when:
+        touch("src/test/asm/dummy.s")
+        touch("src/test/anotherOne/dummy.s")
+        dsl {
+            pluginManager.apply AssemblerPlugin
+
+            model {
+                components {
+                    test(NativeExecutableSpec) {
+                        sources {
+                            anotherOne(AssemblerSourceSet) {}
+                            emptyOne(AssemblerSourceSet) {}
+                        }
+                        binaries.all { NativeBinary binary ->
+                            binary.assembler.args "ARG1", "ARG2"
+                        }
+                    }
+                }
+            }
+        }
+
+        then:
+        NativeExecutableBinarySpec binary = project.binaries.testExecutable
+        binary.tasks.withType(Assemble)*.name == ["assembleTestExecutableTestAnotherOne", "assembleTestExecutableTestAsm"]
+
+        and:
+        binary.tasks.withType(Assemble).each { compile ->
+            compile instanceof Assemble
+            compile.toolChain == binary.toolChain
+            compile.assemblerArgs == ["ARG1", "ARG2"]
+        }
+
+        and:
+        def linkTask = binary.tasks.link
+        linkTask TaskDependencyMatchers.dependsOn("assembleTestExecutableTestAnotherOne", "assembleTestExecutableTestAsm")
+    }
+
+    def "creates assemble tasks for each library source set"() {
+        when:
+        touch("src/test/asm/dummy.s")
+        touch("src/test/anotherOne/dummy.s")
+        dsl {
+            pluginManager.apply AssemblerPlugin
+            model {
+                components {
+                    test(NativeLibrarySpec) {
+                        sources {
+                            anotherOne(AssemblerSourceSet) {}
+                            emptyOne(AssemblerSourceSet) {}
+                        }
+                        binaries.all {
+                            assembler.args "ARG1", "ARG2"
+                        }
+                        binaries.withType(SharedLibraryBinarySpec) {
+                            assembler.args "SHARED1", "SHARED2"
+                        }
+                        binaries.withType(StaticLibraryBinarySpec) {
+                            assembler.args "STATIC1", "STATIC2"
+                        }
+                    }
+                }
+            }
+        }
+
+        then:
+        SharedLibraryBinarySpec sharedLib = project.binaries.testSharedLibrary
+        sharedLib.tasks.withType(Assemble)*.name == ["assembleTestSharedLibraryTestAnotherOne", "assembleTestSharedLibraryTestAsm"]
+        sharedLib.tasks.withType(Assemble).each { compile ->
+            compile.toolChain == sharedLib.toolChain
+            compile.assemblerArgs == ["ARG1", "ARG2", "SHARED1", "SHARED2"]
+        }
+        def sharedLinkTask = sharedLib.tasks.link
+        sharedLinkTask TaskDependencyMatchers.dependsOn("assembleTestSharedLibraryTestAnotherOne", "assembleTestSharedLibraryTestAsm")
+
+        and:
+        StaticLibraryBinarySpec staticLib = project.binaries.testStaticLibrary
+        staticLib.tasks.withType(Assemble)*.name == ["assembleTestStaticLibraryTestAnotherOne", "assembleTestStaticLibraryTestAsm"]
+        staticLib.tasks.withType(Assemble).each { compile ->
+            compile.toolChain == sharedLib.toolChain
+            compile.assemblerArgs == ["ARG1", "ARG2", "STATIC1", "STATIC2"]
+        }
+        def staticLibTask = staticLib.tasks.createStaticLib
+        staticLibTask TaskDependencyMatchers.dependsOn("assembleTestStaticLibraryTestAnotherOne", "assembleTestStaticLibraryTestAsm")
+    }
+
+    def touch(String filePath) {
+        GFileUtils.touch(project.file(filePath))
+    }
+
+    def dsl(@DelegatesTo(Project) Closure closure) {
+        closure.delegate = project
+        closure()
+        project.tasks.realize()
+        project.bindAllModelRules()
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/tasks/AssemblerTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/tasks/AssemblerTest.groovy
new file mode 100644
index 0000000..9e57672
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/assembler/tasks/AssemblerTest.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.language.assembler.tasks
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.api.tasks.WorkResult
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class AssemblerTest extends Specification {
+    def testDir = new TestNameTestDirectoryProvider().testDirectory
+    Assemble assembleTask = TestUtil.createTask(Assemble)
+    def toolChain = Mock(NativeToolChainInternal)
+    def platform = Mock(NativePlatformInternal)
+    def platformToolChain = Mock(PlatformToolProvider)
+    Compiler<AssembleSpec> assembler = Mock(Compiler)
+
+    def "executes using the Assembler"() {
+        def inputDir = testDir.file("sourceFile")
+        def result = Mock(WorkResult)
+        when:
+        assembleTask.toolChain = toolChain
+        assembleTask.targetPlatform = platform
+        assembleTask.assemblerArgs = ["arg"]
+        assembleTask.objectFileDir = testDir.file("outputFile")
+        assembleTask.source inputDir
+        assembleTask.execute()
+
+        then:
+        _ * toolChain.outputType >> "c"
+        platform.getArchitecture() >> Mock(ArchitectureInternal) { getName() >> "arch" }
+        platform.getOperatingSystem() >> Mock(OperatingSystemInternal) { getName() >> "os" }
+        1 * toolChain.select(platform) >> platformToolChain
+        1 * platformToolChain.newCompiler({AssembleSpec.class.isAssignableFrom(it)}) >> assembler
+        1 * assembler.execute({ AssembleSpec spec ->
+            assert spec.sourceFiles*.name == ["sourceFile"]
+            assert spec.args == ['arg']
+            assert spec.allArgs == ['arg']
+            assert spec.objectFileDir.name == "outputFile"
+            true
+        }) >> result
+        1 * result.didWork >> true
+        0 * _._
+
+        and:
+        assembleTask.didWork
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.groovy
new file mode 100644
index 0000000..f7e5f02
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CLangPluginTest.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.language.c.plugins
+
+import org.gradle.language.c.CSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class CLangPluginTest extends AbstractLanguagePluginSpec {
+    @Override
+    def getPluginClass() {
+        return CLangPlugin
+    }
+
+    @Override
+    def getLanguageSourceSet() {
+        return CSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "c"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CPluginTest.groovy
new file mode 100644
index 0000000..f090970
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/c/plugins/CPluginTest.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.language.c.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.api.Task
+import org.gradle.language.AbstractNativeComponentPluginTest
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.c.CSourceSet
+import org.gradle.language.c.tasks.CCompile
+import org.gradle.util.TestUtil
+
+class CPluginTest extends AbstractNativeComponentPluginTest {
+    final def project = TestUtil.createRootProject()
+
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        return CPlugin
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getSourceSetClass() {
+        return CSourceSet
+    }
+
+    @Override
+    Class<? extends Task> getCompileTaskClass() {
+        return CCompile
+    }
+
+    @Override
+    String getPluginName() {
+        return "c"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/c/tasks/CCompileTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/c/tasks/CCompileTest.groovy
new file mode 100644
index 0000000..45e57d0
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/c/tasks/CCompileTest.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.language.c.tasks
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.api.tasks.WorkResult
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class CCompileTest extends Specification {
+    def testDir = new TestNameTestDirectoryProvider().testDirectory
+    CCompile cCompile = TestUtil.createTask(CCompile)
+    def toolChain = Mock(NativeToolChainInternal)
+    def platform = Mock(NativePlatformInternal)
+    def platformToolChain = Mock(PlatformToolProvider)
+    Compiler<CppCompileSpec> cCompiler = Mock(Compiler)
+
+    def "executes using the C Compiler"() {
+        def sourceFile = testDir.createFile("sourceFile")
+        def result = Mock(WorkResult)
+        when:
+        cCompile.toolChain = toolChain
+        cCompile.targetPlatform = platform
+        cCompile.compilerArgs = ["arg"]
+        cCompile.macros = [def: "value"]
+        cCompile.objectFileDir = testDir.file("outputFile")
+        cCompile.source sourceFile
+        cCompile.execute()
+
+        then:
+        _ * toolChain.outputType >> "c"
+        platform.getArchitecture() >> Mock(ArchitectureInternal) { getName() >> "arch" }
+        platform.getOperatingSystem() >> Mock(OperatingSystemInternal) { getName() >> "os" }
+        1 * toolChain.select(platform) >> platformToolChain
+        1 * platformToolChain.newCompiler({CCompileSpec.class.isAssignableFrom(it)}) >> cCompiler
+        1 * cCompiler.execute({ CCompileSpec spec ->
+            assert spec.sourceFiles*.name== ["sourceFile"]
+            assert spec.args == ['arg']
+            assert spec.allArgs == ['arg']
+            assert spec.macros == [def: 'value']
+            assert spec.objectFileDir.name == "outputFile"
+            true
+        }) >> result
+        1 * result.didWork >> true
+        0 * _._
+
+        and:
+        cCompile.didWork
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppSourceSetTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppSourceSetTest.groovy
new file mode 100644
index 0000000..2a06648
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppSourceSetTest.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.language.cpp.internal
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.sources.BaseLanguageSourceSet
+import org.gradle.nativeplatform.NativeDependencySet
+import org.gradle.nativeplatform.NativeLibraryBinary
+import org.gradle.nativeplatform.NativeLibrarySpec
+import spock.lang.Specification
+
+class DefaultCppSourceSetTest extends Specification {
+    def parent = "main"
+    def fileResolver = Mock(FileResolver)
+    def sourceSet = BaseLanguageSourceSet.create(DefaultCppSourceSet, "cpp", parent, fileResolver, DirectInstantiator.INSTANCE)
+
+    def "has useful string representation"() {
+        expect:
+        sourceSet.displayName == "C++ source 'main:cpp'"
+        sourceSet.toString() == "C++ source 'main:cpp'"
+    }
+
+    def "can add a library as a dependency of the source set"() {
+        def library = Mock(NativeLibrarySpec)
+
+        when:
+        sourceSet.lib(library)
+
+        then:
+        sourceSet.libs.contains(library)
+    }
+
+    def "can add a library binary as a dependency of the binary"() {
+        def library = Mock(NativeLibraryBinary)
+
+        when:
+        sourceSet.lib(library)
+
+        then:
+        sourceSet.libs.contains(library)
+    }
+
+    def "can add a native dependency as a dependency of the binary"() {
+        def dependency = Stub(NativeDependencySet)
+
+        when:
+        sourceSet.lib(dependency)
+
+        then:
+        sourceSet.libs.contains(dependency)
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.groovy
new file mode 100644
index 0000000..b25aa7e
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLangPluginTest.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.language.cpp.plugins
+
+import org.gradle.language.cpp.CppSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class CppLangPluginTest extends AbstractLanguagePluginSpec {
+
+    @Override
+    def getPluginClass() {
+        return CppLangPlugin
+    }
+
+    @Override
+    def getLanguageSourceSet() {
+        return CppSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "cpp"
+    }
+
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppPluginTest.groovy
new file mode 100644
index 0000000..8d07d61
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppPluginTest.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.language.cpp.plugins
+import org.gradle.api.Plugin
+import org.gradle.api.Task
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.cpp.CppSourceSet
+import org.gradle.language.AbstractNativeComponentPluginTest
+import org.gradle.language.cpp.tasks.CppCompile
+import org.gradle.util.TestUtil
+
+class CppPluginTest extends AbstractNativeComponentPluginTest {
+    final def project = TestUtil.createRootProject()
+
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        return CppPlugin
+    }
+
+    @Override
+    Class<? extends Task> getCompileTaskClass() {
+        return CppCompile
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getSourceSetClass() {
+        return CppSourceSet
+    }
+
+    @Override
+    String getPluginName() {
+        return "cpp"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/tasks/CppCompileTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/tasks/CppCompileTest.groovy
new file mode 100644
index 0000000..6b29bb7
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/tasks/CppCompileTest.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.language.cpp.tasks
+
+import org.gradle.api.tasks.WorkResult
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class CppCompileTest extends Specification {
+    def testDir = new TestNameTestDirectoryProvider().testDirectory
+    CppCompile cppCompile = TestUtil.createTask(CppCompile)
+    def toolChain = Mock(NativeToolChainInternal)
+    def platform = Mock(NativePlatformInternal)
+    def platformToolChain = Mock(PlatformToolProvider)
+    Compiler<CppCompileSpec> cppCompiler = Mock(Compiler)
+
+    def "executes using the CppCompiler"() {
+        def sourceFile = testDir.createFile("sourceFile")
+        def result = Mock(WorkResult)
+        when:
+        cppCompile.toolChain = toolChain
+        cppCompile.targetPlatform = platform
+        cppCompile.compilerArgs = ["arg"]
+        cppCompile.macros = [def: "value"]
+        cppCompile.objectFileDir = testDir.file("outputFile")
+        cppCompile.source sourceFile
+        cppCompile.execute()
+
+        then:
+        _ * toolChain.outputType >> "cpp"
+        platform.getArchitecture() >> Mock(ArchitectureInternal) { getName() >> "arch" }
+        platform.getOperatingSystem() >> Mock(OperatingSystemInternal) { getName() >> "os" }
+        1 * toolChain.select(platform) >> platformToolChain
+        1 * platformToolChain.newCompiler({ CppCompileSpec.class.isAssignableFrom(it) }) >> cppCompiler
+        1 * cppCompiler.execute({ CppCompileSpec spec ->
+            assert spec.sourceFiles*.name == ["sourceFile"]
+            assert spec.args == ['arg']
+            assert spec.allArgs == ['arg']
+            assert spec.macros == [def: 'value']
+            assert spec.objectFileDir.name == "outputFile"
+            true
+        }) >> result
+        1 * result.didWork >> true
+        0 * _._
+
+        and:
+        cppCompile.didWork
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializerTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializerTest.groovy
new file mode 100644
index 0000000..62cac46
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/CompilationStateSerializerTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.nativeplatform.internal.incremental
+
+import org.gradle.internal.serialize.SerializerSpec
+
+class CompilationStateSerializerTest extends SerializerSpec {
+    def state = new CompilationState()
+    private CompilationStateSerializer serializer = new CompilationStateSerializer()
+
+    def "serializes empty state"() {
+        expect:
+        with (serialized) {
+            sourceInputs.empty
+            fileStates.isEmpty()
+        }
+    }
+
+    def "serializes source inputs"() {
+        when:
+        def fileOne = new File("one")
+        def fileTwo = new File("two")
+        state.sourceInputs << fileOne << fileTwo
+
+        then:
+        with (serialized) {
+            sourceInputs == [fileOne, fileTwo]
+            fileStates.isEmpty()
+        }
+    }
+
+    def "serializes file state"() {
+        when:
+        def fileEmpty = new File("empty")
+        state.fileStates.put(fileEmpty, new CompilationFileState(new byte[0]))
+
+        def fileTwo = new File("two")
+        def stateTwo = new CompilationFileState("FooBar".getBytes())
+        stateTwo.sourceIncludes = createSourceIncludes("<system>", '"quoted"', "MACRO")
+        stateTwo.resolvedIncludes = [resolvedInclude("ONE"), resolvedInclude("TWO")]
+        state.fileStates.put(fileTwo, stateTwo)
+
+        then:
+        def newState = serialized
+        newState.sourceInputs.empty
+        newState.fileStates.size() == 2
+
+        def emptyCompileState = newState.getState(fileEmpty)
+        emptyCompileState.hash.length == 0
+        emptyCompileState.sourceIncludes.macroIncludes.empty
+        emptyCompileState.sourceIncludes.quotedIncludes.empty
+        emptyCompileState.sourceIncludes.systemIncludes.empty
+        emptyCompileState.resolvedIncludes.empty
+
+        def otherCompileState = newState.getState(fileTwo)
+        new String(otherCompileState.hash) == "FooBar"
+        otherCompileState.sourceIncludes.systemIncludes == ["system"]
+        otherCompileState.sourceIncludes.quotedIncludes == ["quoted"]
+        otherCompileState.sourceIncludes.macroIncludes == ["MACRO"]
+        otherCompileState.resolvedIncludes == [resolvedInclude("ONE"), resolvedInclude("TWO")] as Set
+    }
+
+    private static DefaultSourceIncludes createSourceIncludes(String... strings) {
+        final DefaultSourceIncludes sourceIncludes = new DefaultSourceIncludes()
+        sourceIncludes.addAll(strings as List<String>)
+        sourceIncludes
+    }
+
+    private static ResolvedInclude resolvedInclude(String value) {
+        return new ResolvedInclude(value, new File(value))
+    }
+
+    private CompilationState getSerialized() {
+        serialize(state, serializer) as CompilationState
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParserTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParserTest.groovy
new file mode 100644
index 0000000..b8e3950
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesParserTest.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.language.nativeplatform.internal.incremental
+
+import org.gradle.language.nativeplatform.internal.incremental.sourceparser.CSourceParser
+import spock.lang.Specification
+
+class DefaultSourceIncludesParserTest extends Specification {
+    def sourceParser = Mock(CSourceParser)
+    def sourceDetails = Mock(CSourceParser.SourceDetails)
+
+    def "imports are not included in includes"() {
+        given:
+        def file = new File("test")
+
+        when:
+        def includesParser = new DefaultSourceIncludesParser(sourceParser, false)
+
+        1 * sourceParser.parseSource(file) >> sourceDetails
+        1 * sourceDetails.includes >> ['"quoted"', '<system>', 'DEFINED']
+        0 * sourceDetails._
+
+        and:
+        def includes = includesParser.parseIncludes(file)
+
+        then:
+        includes.quotedIncludes == ["quoted"]
+        includes.systemIncludes == ["system"]
+        includes.macroIncludes == ["DEFINED"]
+    }
+
+
+    def "imports are included in includes"() {
+        given:
+        def file = new File("test")
+
+        when:
+        def includesParser = new DefaultSourceIncludesParser(sourceParser, true)
+
+        1 * sourceParser.parseSource(file) >> sourceDetails
+        1 * sourceDetails.includes >> ['"quoted"', '<system>', 'DEFINED']
+        1 * sourceDetails.imports >> ['"quotedImport"', '<systemImport>', 'DEFINED_IMPORT']
+        0 * sourceDetails._
+
+        and:
+        def includes = includesParser.parseIncludes(file)
+
+        then:
+        includes.quotedIncludes == ["quoted", "quotedImport"]
+        includes.systemIncludes == ["system", "systemImport"]
+        includes.macroIncludes == ["DEFINED", "DEFINED_IMPORT"]
+    }
+
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolverTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolverTest.groovy
new file mode 100644
index 0000000..1355350
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/DefaultSourceIncludesResolverTest.groovy
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultSourceIncludesResolverTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    def testDirectory = temporaryFolder.testDirectory
+    def sourceDirectory = testDirectory.createDir("sources")
+    def quotedIncludes = []
+    def systemIncludes = []
+    def macroIncludes = []
+    def includesParser = Mock(SourceIncludesParser)
+    def includes
+    def includePaths = []
+
+    def setup() {
+        includes = Mock(SourceIncludes)
+        includesParser.parseIncludes(sourceFile) >> includes
+        includes.getQuotedIncludes() >> quotedIncludes
+        includes.getSystemIncludes() >> systemIncludes
+        includes.getMacroIncludes() >> macroIncludes
+    }
+
+    protected TestFile getSourceFile() {
+        sourceDirectory.file('source.c')
+    }
+
+    def getDependencies() {
+        return new DefaultSourceIncludesResolver(includePaths).resolveIncludes(sourceFile, includes) as List
+    }
+
+    def "handles source file with no includes"() {
+        expect:
+        dependencies == []
+    }
+
+    def "ignores include files that do not exist"() {
+        when:
+        quotedIncludes << "test.h"
+        systemIncludes << "system"
+
+        then:
+        dependencies == []
+    }
+
+    def "locates quoted includes in same directory"() {
+        when:
+        final header1 = sourceDirectory.createFile("test1.h")
+        final header2 = sourceDirectory.createFile("test2.h")
+
+        and:
+        quotedIncludes << "test1.h" << "test2.h"
+
+        then:
+        dependencies == deps(header1, header2)
+    }
+
+    def "locates quoted includes relative to source directory"() {
+        when:
+        final header1 = sourceDirectory.createFile("test1.h")
+        final header2 = sourceDirectory.file("nested", "test2.h").createFile()
+        final header3 = sourceDirectory.file("..", "sibling", "test3.h").createFile()
+
+        and:
+        quotedIncludes << "test1.h" << "nested/test2.h" << "../sibling/test3.h"
+
+        then:
+        dependencies.collect {it.file} == [header1, header2, header3]
+    }
+
+    def "does not locate system includes in same directory"() {
+        when:
+        sourceDirectory.file("system.h").createFile()
+
+        and:
+        systemIncludes << "system.h"
+
+        then:
+        dependencies == []
+    }
+
+    def "locates includes in path"() {
+        when:
+        def includeDir1 = testDirectory.file("include1")
+        final header11 = includeDir1.file("test11.h").createFile()
+        final header12 = includeDir1.file("test12.h").createFile()
+        def includeDir2 = testDirectory.file("include2")
+        final header21 = includeDir1.file("test21.h").createFile()
+        final header22 = includeDir1.file("test22.h").createFile()
+
+        and:
+        includePaths << includeDir1 << includeDir2
+        quotedIncludes << "test11.h" << "test21.h"
+        systemIncludes << "test12.h" << "test22.h"
+
+        then:
+        dependencies == deps(header11, header21, header12, header22)
+    }
+
+    def "searches relative before searching include path"() {
+        when:
+        final relativeHeader = sourceDirectory.createFile("test.h")
+        final includeDir = testDirectory.file("include")
+        includeDir.createFile("test.h")
+        final otherHeader = includeDir.createFile("other.h")
+
+        and:
+        includePaths << includeDir
+        quotedIncludes << "test.h" << "other.h"
+
+        then:
+        dependencies == deps(relativeHeader, otherHeader)
+    }
+
+    def "includes unknown source dependency for first macro include"() {
+        when:
+        macroIncludes << 'DEFINE_1' << 'DEFINE_2'
+
+        then:
+        dependencies.size() == 1
+        with (dependencies[0]) {
+            unknown
+            include == 'DEFINE_1'
+            file == null
+        }
+    }
+
+    def deps(File... files) {
+        return files.collect {dep(it)}
+    }
+
+    def dep(File dependencyFile) {
+        return new ResolvedInclude(dependencyFile.name, dependencyFile)
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessorTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessorTest.groovy
new file mode 100644
index 0000000..db8ed5a
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalCompileProcessorTest.groovy
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental
+
+import org.gradle.api.internal.changedetection.state.FileSnapshot
+import org.gradle.api.internal.changedetection.state.FileSnapshotter
+import org.gradle.cache.PersistentStateCache
+import org.gradle.internal.hash.HashUtil
+import org.gradle.language.nativeplatform.internal.SourceIncludes
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class IncrementalCompileProcessorTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    def includesParser = Mock(SourceIncludesParser)
+    def dependencyParser = Mock(SourceIncludesResolver)
+    def fileSnapshotter = Stub(FileSnapshotter)
+    def stateCache = new DummyPersistentStateCache()
+    def incrementalCompileProcessor = new IncrementalCompileProcessor(stateCache, dependencyParser, includesParser, fileSnapshotter)
+
+    def source1 = sourceFile("source1")
+    def source2 = sourceFile("source2")
+    def dep1 = sourceFile("dep1")
+    def dep2 = sourceFile("dep2")
+    def dep3 = sourceFile("dep3")
+    def dep4 = sourceFile("dep4")
+    def sourceFiles
+
+    Map<TestFile, List<ResolvedInclude>> graph = [:]
+    List<TestFile> modified = []
+
+    def setup() {
+        fileSnapshotter.snapshot(_) >> { File file ->
+            return Stub(FileSnapshot) {
+                getHash() >> HashUtil.sha1(file).asByteArray()
+            }
+        }
+
+        // S1 - D1 \
+        //    \ D2  \
+        //           D3
+        // S2 ------/
+        //    \ D4
+
+        graph[source1] = deps(dep1, dep2)
+        graph[source2] = deps(dep3, dep4)
+        graph[dep1] = deps(dep3)
+        graph[dep2] = []
+        graph[dep3] = []
+        graph[dep4] = []
+    }
+
+    def initialFiles() {
+
+        graph.keySet().each { TestFile sourceFile ->
+            parse(sourceFile)
+            resolve(sourceFile)
+        }
+
+        sourceFiles = [source1, source2]
+        with (state) {
+            assert recompile == [source1, source2]
+            assert removed == []
+        }
+    }
+
+    def parse(TestFile sourceFile) {
+        final Set<ResolvedInclude> deps = graph[sourceFile]
+        SourceIncludes includes = includes(deps)
+        1 * includesParser.parseIncludes(sourceFile) >> includes
+    }
+
+    def resolve(TestFile sourceFile) {
+        Set<ResolvedInclude> deps = graph[sourceFile]
+        SourceIncludes includes = includes(deps)
+        1 * dependencyParser.resolveIncludes(sourceFile, includes) >> deps
+    }
+
+    private static SourceIncludes includes(Set<ResolvedInclude> deps) {
+        def includes = new DefaultSourceIncludes()
+        includes.addAll(deps.collect { '<' + it.file.name + '>' })
+        return includes
+    }
+
+    def added(TestFile sourceFile) {
+        modified << sourceFile
+        graph[sourceFile] = []
+    }
+
+    def sourceAdded(TestFile sourceFile, def deps = []) {
+        sourceFiles << sourceFile
+        modified << sourceFile
+        graph[sourceFile] = deps
+    }
+
+    def modified(TestFile sourceFile, def deps = null) {
+        modified << sourceFile
+        sourceFile << "More text"
+        if (deps != null) {
+            graph[sourceFile] = deps
+        }
+    }
+
+    def sourceRemoved(TestFile sourceFile) {
+        sourceFiles.remove(sourceFile)
+        graph.remove(sourceFile)
+    }
+
+    def dependencyRemoved(TestFile sourceFile) {
+        graph.remove(sourceFile)
+        sourceFile.delete()
+    }
+
+    def "detects unchanged source files"() {
+        given:
+        initialFiles()
+
+        expect:
+        checkCompile recompiled: [], removed: []
+    }
+
+    def "detects new source files"() {
+        given:
+        initialFiles()
+
+        when:
+        def file3 = sourceFile("file3")
+        sourceAdded(file3)
+
+        then:
+        checkCompile recompiled: [file3], removed: []
+    }
+
+    def "detects removed source file"() {
+        given:
+        initialFiles()
+
+        when:
+        sourceRemoved(source2)
+        graph.remove(dep4)
+
+        then:
+        checkCompile recompiled: [], removed: [source2]
+    }
+
+    def "detects removed source file with multiple dependencies"() {
+        given:
+        initialFiles()
+
+        when:
+        sourceRemoved(source1)
+        graph.remove(dep1)
+        graph.remove(dep2)
+
+        then:
+        checkCompile recompiled: [], removed: [source1]
+    }
+
+    def "detects source file changed"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(source2)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "detects dependency file changed"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(dep4)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "detects dependency file removed"() {
+        given:
+        initialFiles()
+
+        when:
+        dependencyRemoved(dep4)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "detects shared dependency file changed"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(dep3)
+
+        then:
+        checkCompile recompiled: [source1, source2], removed: []
+    }
+
+    def "detects source file change with new dependencies"() {
+        given:
+        initialFiles()
+
+        when:
+        def dep5 = sourceFile("dep5")
+        added(dep5)
+        modified(source2, deps(dep3, dep4, dep5))
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+
+        when:
+        modified(dep5)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "detects unchanged source file change with different resolved dependencies"() {
+        given:
+        initialFiles()
+
+        when:
+        def dep5 = sourceFile("dep5")
+        graph[dep5] = []
+
+        resolve(source1)
+        resolve(dep1)
+        resolve(dep2)
+        resolve(dep3)
+        parse(dep5)
+        resolve(dep5)
+
+        1 * dependencyParser.resolveIncludes(source2, includes(deps(dep3, dep4))) >> deps(dep3, dep5)
+
+        then:
+        with (state) {
+            recompile == [source2]
+            removed == []
+        }
+    }
+
+    def "detects dependency file change with new dependencies"() {
+        given:
+        initialFiles()
+
+        when:
+        def dep5 = sourceFile("dep5")
+        modified(dep4, deps(dep5))
+        added(dep5)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+
+        when:
+        modified(dep5)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "detects dependency file change adding dependency cycle"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(dep3, deps(dep1))
+
+        then:
+        checkCompile recompiled: [source1, source2], removed: []
+    }
+
+    def "detects shared dependency file changed with new dependency"() {
+        given:
+        initialFiles()
+
+        when:
+        def dep5 = sourceFile("dep5")
+        modified(dep3, deps(dep5))
+        added(dep5)
+
+        then:
+        checkCompile recompiled: [source1, source2], removed: []
+
+        when:
+        modified(dep5)
+
+        then:
+        checkCompile recompiled: [source1, source2], removed: []
+    }
+
+    def "detects changed dependency with new source file including that dependency"() {
+        given:
+        initialFiles()
+
+        when:
+        def file3 = sourceFile("file3")
+        sourceAdded(file3, deps(dep4))
+        modified(dep4)
+
+        then:
+        checkCompile recompiled: [source2, file3], removed: []
+    }
+
+    def "detects source file removed then readded"() {
+        given:
+        initialFiles()
+
+        when:
+        sourceRemoved(source2)
+        graph.remove(dep4)
+
+        then:
+        checkCompile recompiled: [], removed: [source2]
+
+        when:
+        sourceAdded(source2, [])
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def "handles source file that is also a dependency"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(dep2, deps(source2))
+
+        then:
+        checkCompile recompiled: [source1], removed: []
+
+        when:
+        modified(dep4)
+
+        then:
+        checkCompile recompiled: [source1, source2], removed: []
+    }
+
+    def "reports source file changed to dependency as removed"() {
+        given:
+        initialFiles()
+
+        when:
+        modified(dep2, deps(source2))
+        sourceFiles.remove(source2)
+
+        then:
+        checkCompile recompiled: [source1], removed: [source2]
+
+        when:
+        sourceFiles.add(source2)
+
+        then:
+        checkCompile recompiled: [source2], removed: []
+    }
+
+    def checkCompile(Map<String, List<File>> args) {
+        parseAndResolve()
+        with (state) {
+            assert recompile == args['recompiled']
+            assert removed == args['removed']
+        }
+        return true
+    }
+
+    def parseAndResolve() {
+        modified.each {
+            parse(it)
+        }
+        modified.clear()
+        graph.keySet().each { TestFile sourceFile ->
+            resolve(sourceFile)
+        }
+        true
+    }
+
+    def getState() {
+        def incrementalState = incrementalCompileProcessor.processSourceFiles(sourceFiles)
+        stateCache.set(incrementalState.finalState)
+        return incrementalState
+    }
+
+    def sourceFile(def name) {
+        tmpDir.createFile(name) << "initial text"
+    }
+
+    Set<ResolvedInclude> deps(File... dep) {
+        dep.collect {new ResolvedInclude(it.name, it)} as Set
+    }
+
+    class DummyPersistentStateCache implements PersistentStateCache<CompilationState> {
+        private CompilationState compilationState
+
+        CompilationState get() {
+            return compilationState
+        }
+
+        void set(CompilationState newValue) {
+            this.compilationState = newValue
+        }
+
+        void update(PersistentStateCache.UpdateAction<CompilationState> updateAction) {
+
+        }
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompilerTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompilerTest.groovy
new file mode 100644
index 0000000..13f9fcf
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/IncrementalNativeCompilerTest.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.language.nativeplatform.internal.incremental
+
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.TaskOutputsInternal
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.internal.tasks.SimpleWorkResult
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.nativeplatform.toolchain.Clang
+import org.gradle.nativeplatform.toolchain.Gcc
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class IncrementalNativeCompilerTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    def delegateCompiler = Mock(Compiler)
+    def toolChain = Mock(NativeToolChain)
+    def task = Mock(TaskInternal)
+    def compiler = new IncrementalNativeCompiler(task, null, null, null, delegateCompiler, toolChain)
+
+    def outputs = Mock(TaskOutputsInternal)
+
+    def "updates spec for incremental compilation"() {
+        def spec = Mock(NativeCompileSpec)
+        def newSource = temporaryFolder.file("new")
+        def removedSource = temporaryFolder.file("removed")
+
+        def compilation = Mock(IncrementalCompilation)
+
+        when:
+        compilation.getRecompile() >> [newSource]
+        compilation.getRemoved() >> [removedSource]
+
+        and:
+        compiler.doIncrementalCompile(compilation, spec)
+
+        then:
+        1 * spec.setSourceFiles([newSource])
+        1 * spec.setRemovedSourceFiles([removedSource])
+        1 * delegateCompiler.execute(spec)
+    }
+
+    def "cleans outputs and delegates spec for clean compilation"() {
+        def spec = Mock(NativeCompileSpec)
+        def existingSource = temporaryFolder.file("existing")
+        def newSource = temporaryFolder.file("new")
+        def outputFile = temporaryFolder.createFile("output", "previous")
+
+        def sources = [existingSource, newSource]
+
+        when:
+        outputFile.assertExists()
+
+        and:
+        spec.incrementalCompile >> false
+        task.outputs >> outputs
+        spec.getSourceFiles() >> sources
+
+        and:
+        def result = compiler.doCleanIncrementalCompile(spec)
+
+        then:
+        1 * spec.getObjectFileDir() >> outputFile.parentFile
+        1 * outputs.previousFiles >> new SimpleFileCollection(outputFile)
+        0 * spec._
+        1 * delegateCompiler.execute(spec) >> new SimpleWorkResult(false)
+
+        and:
+        result.didWork
+        outputFile.assertDoesNotExist()
+    }
+
+    @Unroll
+    def "imports are includes for toolchain #tcName"() {
+       when:
+       def compiler = new IncrementalNativeCompiler(task, null, null, null, delegateCompiler, toolChain)
+       then:
+       compiler.importsAreIncludes
+       where:
+       tcName   | toolChain
+       "clang"  | Mock(Clang)
+       "gcc"    | Mock(Gcc)
+
+    }
+
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReaderTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReaderTest.groovy
new file mode 100644
index 0000000..6385d6b
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/PreprocessingReaderTest.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.language.nativeplatform.internal.incremental.sourceparser
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class PreprocessingReaderTest extends Specification {
+    private static final String BN = "\\" + System.getProperty("line.separator")
+    String input
+
+    def getOutput() {
+        def reader = new PreprocessingReader(new StringReader(input))
+        return reader.text
+    }
+
+    def "removes line continuation characters"() {
+        when:
+        input = """Here is a ${BN}single line ${BN}with continuations \\${BN}and \\ slashes\\\t\n too\\ \n."""
+
+        then:
+        output == "Here is a single line with continuations \\and \\ slashes\\\t\n too\\ \n."
+    }
+
+    def "replaces inline comments with space"() {
+        when:
+        input = """
+Here/* comment */is a string/*
+multiline
+comment
+here */that contains/**
+ comment /* containing ** / ${BN} characters */several inline comments.
+"""
+
+        then:
+        output == "\nHere is a string that contains several inline comments.\n"
+    }
+
+    def "replaces line comments with space"() {
+        when:
+        input = """
+Here is a // comment
+string
+// Line comment
+with interspersed // comment /* with inline comment */ and // other comment */ chars
+line comments.
+"""
+
+        then:
+        output == "\nHere is a \nstring\n\nwith interspersed \nline comments.\n"
+    }
+
+    def "can cope with multiple unescaped and escaped \\r characters"() {
+        when:
+        input = "Here \r\r\\\r\\\r${BN}\\\r\\\r\\\r\\\r."
+        then:
+        output == "Here \r\r\\\r\\\r\\\r\\\r\\\r\\\r."
+    }
+
+    @Unroll
+    def "replaces #description at the start of content"() {
+        when:
+        input = testIn
+
+        then:
+        output == testOut
+
+        where:
+        description | testIn | testOut
+        "line comment" | "// Commment on first line\nAnother line" | "\nAnother line"
+        "inline comment" | "/* inline comment at the start */of the line" | " of the line"
+        "line continuation" | "${BN} at the start of the content" | " at the start of the content"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy
new file mode 100644
index 0000000..405c405
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/nativeplatform/internal/incremental/sourceparser/RegexBackedCSourceParserTest.groovy
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform.internal.incremental.sourceparser
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class RegexBackedCSourceParserTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    CSourceParser parser = new RegexBackedCSourceParser()
+
+    protected TestFile getSourceFile() {
+        testDirectory.file('source.c')
+    }
+
+    TestFile getTestDirectory() {
+        temporaryFolder.testDirectory
+    }
+
+    def getParsedSource() {
+        parser.parseSource(sourceFile)
+    }
+
+    def getIncludes() {
+        return parsedSource.includes
+    }
+
+    def getImports() {
+        return parsedSource.imports
+    }
+
+    def getFound() {
+        return includes + imports
+    }
+    
+    def noIncludes() {
+        assert includes == []
+        true
+    }
+    
+    def noImports() {
+        assert imports == []
+        true
+    }
+
+    def useDirective(String directive) {
+        sourceFile.text = sourceFile.text.replace("include", directive)
+    }
+
+    def "parses file with no includes"() {
+        when:
+        sourceFile << ""
+
+        then:
+        noIncludes()
+        noImports()
+    }
+
+    def "finds quoted include"() {
+        when:
+        sourceFile << """
+    #include "test.h"
+"""
+
+        then:
+        includes == ['"test.h"']
+
+        and:
+        noImports()
+    }
+
+    def "finds quoted include on first line of file"() {
+        when:
+        sourceFile << '#include "test.h"'
+
+        then:
+        includes == ['"test.h"']
+
+        and:
+        noImports()
+    }
+
+    def "finds system include"() {
+        when:
+        sourceFile << """
+    #include <test.h>
+"""
+
+        then:
+        includes == ['<test.h>']
+        
+        and:
+        noImports()
+    }
+
+    def "finds system include on first line of file"() {
+        when:
+        sourceFile << '#include <test.h>'
+
+        then:
+        includes == ['<test.h>']
+
+        and:
+        noImports()
+    }
+
+    def "finds defined include"() {
+        when:
+        sourceFile << """
+    #include DEFINED
+"""
+
+        then:
+        includes == ['DEFINED']
+
+        and:
+        noImports()
+    }
+
+    def "finds multiple includes"() {
+        when:
+        sourceFile << """
+    #include "test1"
+    #include "test2"
+    #include <system1>
+    #include <system2>
+    #include DEFINED
+"""
+        then:
+        includes == ['"test1"', '"test2"', '<system1>', '<system2>', 'DEFINED']
+        
+        and:
+        noImports()
+    }
+
+    def "finds quoted import"() {
+        when:
+        sourceFile << """
+            #import "test.h"
+        """
+
+        then:
+        imports == ['"test.h"']
+
+        and:
+        noIncludes()
+    }
+
+    def "finds system import"() {
+        when:
+        sourceFile << """
+            #import <test.h>
+        """
+
+        then:
+        imports == ['<test.h>']
+        
+        and:
+        noIncludes()
+    }
+
+    def "finds defined import"() {
+        when:
+        sourceFile << """
+            #import DEFINED
+        """
+
+        then:
+        imports == ['DEFINED']
+
+        and:
+        noIncludes()
+    }
+
+    def "finds multiple imports"() {
+        when:
+        sourceFile << """
+    #import "test1"
+    #import "test2"
+    #import <system1>
+    #import <system2>
+    #import DEFINED
+"""
+        then:
+        imports == ['"test1"', '"test2"', '<system1>', '<system2>', 'DEFINED']
+
+        and:
+        noIncludes()
+    }
+
+    def "finds mixed import include statement imports"() {
+        when:
+        sourceFile << """
+    #import "test1"
+    #include "test2"
+    #import "test3"
+    #include "test4"
+    #import <system1>
+    #import <system2>
+    #include <system3>
+    #import <system4>
+    #include DEFINED1
+    #import DEFINED2
+"""
+        then:
+        includes == ['"test2"', '"test4"', '<system3>', 'DEFINED1']
+        imports == ['"test1"', '"test3"', '<system1>', '<system2>', '<system4>', 'DEFINED2']
+    }
+
+    @Unroll
+    def "finds #directive surrounded by different whitespace"() {
+        when:
+        sourceFile << """
+#include     "test1"
+#include\t"test2"\t
+\t#include\t"test3"
+#include"test4"
+
+#include     <system1>
+#include\t<system2>\t
+\t#include\t<system3>
+#include<system4>
+"""
+        and:
+        useDirective(directive)
+
+        then:
+        found == ['"test1"', '"test2"', '"test3"', '"test4"',
+                  '<system1>', '<system2>', '<system3>', '<system4>']
+
+        where:
+        directive << ["include", "import"]
+    }
+
+    @Unroll
+    def "finds #directive where whitespace surrounds the # character"() {
+        when:
+        sourceFile << """
+  #  include   "test1"
+\t#\tinclude "test2"
+\u0000#include "test3"
+
+  #  include   <system1>
+\t#\tinclude <system2>
+\u0000#include <system3>
+"""
+        and:
+        useDirective(directive)
+
+        then:
+        found == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', '<system3>']
+
+        where:
+        directive << ["include", "import"]
+    }
+
+    def "ignores comment after directive"() {
+        when:
+        sourceFile << """
+#include "test1"  // A comment here
+#include "test2" /* A comment here */
+#include "test3" /*
+   A comment here
+*/
+#include <system1>  // A comment here
+#include <system2> /* A comment here */
+#include <system3> /*
+   A comment here
+*/
+"""
+        then:
+        includes == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', '<system3>']
+    }
+
+    @Unroll
+    def "finds #directive where comment in place of whitespace"() {
+        when:
+        sourceFile << """
+#include/* a comment here*/"test1"
+#/* a
+    comment
+    here*/include "test2"
+/* a comment here*/#include/* a comment here*/"test3"
+#include/* a comment here*/<system1>
+#/* a comment here*/include <system2>
+/* a comment here*/#include/* a comment here*/DEFINED
+"""
+        useDirective(directive)
+
+        then:
+        found == ['"test1"', '"test2"', '"test3"', '<system1>', '<system2>', 'DEFINED']
+
+        where:
+        directive << ["include", "import"]
+    }
+
+    def "find quoted include with special characters"() {
+        when:
+        sourceFile << """
+    #include "$included"
+    #import "$included"
+"""
+        then:
+        includes == ['"' + included + '"']
+        imports == ['"' + included + '"']
+
+        where:
+        included << ["test'file", "testfile'", "'testfile'", "test<>file", "test>file", "<testFile>", "test<file", "test file"]
+    }
+
+    def "find system include with special characters"() {
+        when:
+        sourceFile << """
+    #include <$included>
+    #import <$included>
+"""
+        then:
+        includes == ['<' + included + '>']
+        imports == ['<' + included + '>']
+
+        where:
+        included << ["test'file", "testfile'", "'testfile'", "test<file", "test\"file", "\"testFile\"", "test file"]
+    }
+
+    def "find various defined includes"() {
+        when:
+        sourceFile << """
+    #include $included
+    #import $included
+"""
+        then:
+        includes == [included]
+        imports == [included]
+
+        where:
+        included << ["DEFINED", "mixedDefined", "DEF_INED", "_DEFINED", "__DEFINED__"]
+    }
+
+    @Unroll
+    def "ignores #directive inside a quoted string"() {
+        when:
+        sourceFile << """
+    printf("use #include <stdio.h>");
+    printf("use #include \\"test1\\");
+"""
+        and:
+        useDirective(directive)
+
+        then:
+        noIncludes()
+        noImports()
+
+        where:
+        directive << ["include", "import"]
+    }
+
+    @Unroll
+    def "ignores #directive that is commented out"() {
+        when:
+        sourceFile << """
+/*
+    #include "test1"
+    #include <system1>
+*/
+/* #include "test2" */
+/* #include <system3> */
+
+//    #include "test3"
+//    #include <system3>
+"""
+        and:
+        useDirective(directive)
+
+        then:
+        noIncludes()
+        noImports()
+
+        where:
+        directive << ["include", "import"]
+    }
+
+    def "ignores badly formed directives"() {
+        when:
+        sourceFile << """
+include
+#
+# include
+# import
+
+void # include <thing>
+
+#import <
+
+# inklude <thing.h>
+
+#import thing.h
+#import thing.h"
+#import "thing.h>
+#include <thing.h
+#include "thing.h
+
+#include
+<thing.h>
+
+#include 'thing.h' extra stuff
+
+"""
+
+        then:
+        noIncludes()
+        noImports()
+    }
+
+    def "detects imports with line=continuation"() {
+        when:
+        sourceFile << """
+#include \\
+"test1"
+#\\
+include\\
+ "test2"
+#incl\\
+ude "te\\
+st3"
+"""
+
+        then:
+        includes == ['"test1"', '"test2"', '"test3"']
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginTest.groovy
new file mode 100644
index 0000000..1e15a8d
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCLangPluginTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivec.plugins
+
+import org.gradle.language.objectivec.ObjectiveCSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class ObjectiveCLangPluginTest extends AbstractLanguagePluginSpec {
+
+    @Override
+    def getPluginClass() {
+        return ObjectiveCLangPlugin
+    }
+
+    @Override
+    def getLanguageSourceSet() {
+        return ObjectiveCSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "objc"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginTest.groovy
new file mode 100644
index 0000000..423fce6
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivec/plugins/ObjectiveCPluginTest.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.language.objectivec.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.api.Task
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.objectivec.ObjectiveCSourceSet
+import org.gradle.language.AbstractNativeComponentPluginTest
+import org.gradle.language.objectivec.tasks.ObjectiveCCompile
+
+class ObjectiveCPluginTest extends AbstractNativeComponentPluginTest {
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        return ObjectiveCPlugin
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getSourceSetClass() {
+        return ObjectiveCSourceSet
+    }
+
+    @Override
+    Class<? extends Task> getCompileTaskClass() {
+        return ObjectiveCCompile
+    }
+
+    @Override
+    String getPluginName() {
+        return "objc"
+    }
+
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginTest.groovy
new file mode 100644
index 0000000..441a0b1
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppLangPluginTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.objectivecpp.plugins
+
+import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class ObjectiveCppLangPluginTest extends AbstractLanguagePluginSpec {
+    @Override
+    def getPluginClass() {
+        return ObjectiveCppLangPlugin
+    }
+
+    @Override
+    def getLanguageSourceSet() {
+        return ObjectiveCppSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "objcpp"
+    }
+}
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy
new file mode 100644
index 0000000..bcf9222
--- /dev/null
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.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.language.objectivecpp.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.api.Task
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
+import org.gradle.language.AbstractNativeComponentPluginTest
+import org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile
+
+class ObjectiveCppPluginTest extends AbstractNativeComponentPluginTest {
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        return ObjectiveCppPlugin
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getSourceSetClass() {
+        return ObjectiveCppSourceSet
+    }
+
+    @Override
+    Class<? extends Task> getCompileTaskClass() {
+        return ObjectiveCppCompile
+    }
+
+    @Override
+    String getPluginName() {
+        return "objcpp"
+    }
+}
diff --git a/subprojects/language-scala/language-scala.gradle b/subprojects/language-scala/language-scala.gradle
new file mode 100644
index 0000000..9d2e0b1
--- /dev/null
+++ b/subprojects/language-scala/language-scala.gradle
@@ -0,0 +1,17 @@
+apply from: "$rootDir/gradle/providedConfiguration.gradle"
+
+dependencies {
+    compile project(":core")
+    compile project(":platformJvm")
+    compile project(":languageJava")
+    compile project(":languageJvm")
+    // keep in sync with ScalaLanguagePlugin code
+    provided("com.typesafe.zinc:zinc:0.3.0")
+
+    testCompile libraries.groovy
+}
+
+strictCompile()
+useTestFixtures()
+useTestFixtures(project: ":languageJvm", sourceSet: 'testFixtures')
+useTestFixtures(project: ":platformBase")
\ No newline at end of file
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/JointScalaLangIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/JointScalaLangIntegrationTest.groovy
new file mode 100644
index 0000000..f0ef6b3
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/JointScalaLangIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala
+
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.integtests.language.AbstractJvmLanguageIntegrationTest
+import org.gradle.language.scala.fixtures.TestJointCompiledComponent
+
+class JointScalaLangIntegrationTest extends AbstractJvmLanguageIntegrationTest {
+    TestJvmComponent app = new TestJointCompiledComponent()
+}
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/SampleScalaLanguageIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/SampleScalaLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..0f32c88
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/SampleScalaLanguageIntegrationTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.junit.Rule
+
+class SampleScalaLanguageIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule
+    Sample sample = new Sample(temporaryFolder, "jvmComponents/scala")
+
+    def "can build scala based jvm component"() {
+        setup:
+        executer.inDirectory(sample.dir)
+
+        when:
+        succeeds("assemble")
+
+        then:
+        new JarTestFixture(sample.dir.file("build/jars/mainJar/main.jar")).hasDescendants(
+                "org/gradle/samples/HelloWorld.class",
+                "org/gradle/samples/HelloWorld\$.class",
+                "org/gradle/samples/Greeter.class",
+                "org/gradle/samples/Person.class"
+        )
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIncrementalBuildIntegrationTest.groovy
new file mode 100644
index 0000000..d176f89
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIncrementalBuildIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala
+
+import org.gradle.integtests.language.AbstractJvmLanguageIncrementalBuildIntegrationTest
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.scala.fixtures.TestScalaComponent
+
+class ScalaLanguageIncrementalBuildIntegrationTest extends AbstractJvmLanguageIncrementalBuildIntegrationTest {
+    TestJvmComponent testComponent = new TestScalaComponent()
+}
\ No newline at end of file
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIntegrationTest.groovy
new file mode 100644
index 0000000..88dbd23
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaLanguageIntegrationTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala
+
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.integtests.language.AbstractJvmLanguageIntegrationTest
+import org.gradle.language.scala.fixtures.BadScalaLibrary
+import org.gradle.language.scala.fixtures.TestScalaComponent
+
+class ScalaLanguageIntegrationTest extends AbstractJvmLanguageIntegrationTest {
+    TestJvmComponent app = new TestScalaComponent()
+
+    def "reports failure to compile bad scala sources"() {
+        when:
+        def badApp = new BadScalaLibrary()
+        badApp.sources*.writeToDir(file("src/myLib/scala"))
+
+        and:
+        buildFile << """
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+"""
+        then:
+        fails "assemble"
+
+        and:
+        badApp.compilerErrors.each {
+            assert errorOutput.contains(it)
+        }
+    }
+}
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaToolProviderNotAvailableIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaToolProviderNotAvailableIntegrationTest.groovy
new file mode 100644
index 0000000..ac5b688
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/ScalaToolProviderNotAvailableIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.scala.fixtures.TestScalaComponent
+
+class ScalaToolProviderNotAvailableIntegrationTest extends AbstractIntegrationSpec {
+    TestJvmComponent app = new TestScalaComponent()
+
+    def setup() {
+        buildFile << """
+        plugins {
+            id 'jvm-component'
+            id '${app.languageName}-lang'
+        }
+        model {
+            components {
+                myLib(JvmLibrarySpec)
+            }
+        }
+    """
+    }
+
+    def "provide decent error message when scala tools not available"() {
+        given:
+        app.writeSources(file("src/myLib"))
+        app.writeResources(file("src/myLib/resources"))
+        when:
+        fails("assemble")
+        then:
+        errorOutput.contains("Cannot provide Scala Compiler: Cannot resolve external dependency org.scala-lang:scala-compiler:2.10.4 because no repositories are defined.")
+
+    }
+}
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginGoodBehaviourTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..d6a51ab
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginGoodBehaviourTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class ScalaLanguagePluginGoodBehaviourTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        "scala-lang"
+    }
+}
diff --git a/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginIntegrationTest.groovy b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginIntegrationTest.groovy
new file mode 100644
index 0000000..4bfe597
--- /dev/null
+++ b/subprojects/language-scala/src/integTest/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginIntegrationTest.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.plugins
+
+import org.gradle.integtests.language.AbstractJvmPluginLanguageIntegrationTest
+import org.gradle.language.scala.ScalaLanguageSourceSet
+
+class ScalaLanguagePluginIntegrationTest extends AbstractJvmPluginLanguageIntegrationTest {
+    String sourceSetTypeName = ScalaLanguageSourceSet.class.simpleName
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java
new file mode 100644
index 0000000..a9f9333
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tasks.scala;
+
+/*
+ * 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.
+ */
+
+import org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
+import org.gradle.api.internal.tasks.compile.daemon.DaemonForkOptions;
+import org.gradle.api.tasks.compile.ForkOptions;
+import org.gradle.api.tasks.scala.ScalaForkOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+public class DaemonScalaCompiler<T extends ScalaJavaJointCompileSpec> extends AbstractDaemonCompiler<T> {
+    private final Iterable<File> zincClasspath;
+
+    public DaemonScalaCompiler(File daemonWorkingDir, Compiler<T> delegate, CompilerDaemonFactory daemonFactory, Iterable<File> zincClasspath) {
+        super(daemonWorkingDir, delegate, daemonFactory);
+        this.zincClasspath = zincClasspath;
+    }
+
+    @Override
+    protected DaemonForkOptions toDaemonOptions(T spec) {
+        return createJavaForkOptions(spec).mergeWith(createScalaForkOptions(spec));
+    }
+
+    private DaemonForkOptions createJavaForkOptions(T spec) {
+        ForkOptions options = spec.getCompileOptions().getForkOptions();
+        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(), options.getJvmArgs());
+    }
+
+    private DaemonForkOptions createScalaForkOptions(T spec) {
+        ScalaForkOptions options = spec.getScalaCompileOptions().getForkOptions();
+        List<String> sharedPackages = Arrays.asList("scala", "com.typesafe.zinc", "xsbti", "com.sun.tools.javac");
+        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(),
+                options.getJvmArgs(), zincClasspath, sharedPackages);
+    }
+}
+
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java
new file mode 100644
index 0000000..a751da1
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.scala;
+
+import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec;
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions;
+
+import java.io.File;
+import java.util.Map;
+
+public class DefaultScalaJavaJointCompileSpec extends DefaultJavaCompileSpec implements ScalaJavaJointCompileSpec {
+    private BaseScalaCompileOptions options;
+    private Iterable<File> scalaClasspath;
+    private Iterable<File> zincClasspath;
+    private Map<File, File> analysisMap;
+
+    public BaseScalaCompileOptions getScalaCompileOptions() {
+        return options;
+    }
+
+    public void setScalaCompileOptions(BaseScalaCompileOptions options) {
+        this.options = options;
+    }
+
+    public Iterable<File> getScalaClasspath() {
+        return scalaClasspath;
+    }
+
+    public void setScalaClasspath(Iterable<File> scalaClasspath) {
+        this.scalaClasspath = scalaClasspath;
+    }
+
+    public Iterable<File> getZincClasspath() {
+        return zincClasspath;
+    }
+
+    public void setZincClasspath(Iterable<File> zincClasspath) {
+        this.zincClasspath = zincClasspath;
+    }
+
+    public Map<File, File> getAnalysisMap() {
+        return analysisMap;
+    }
+
+    public void setAnalysisMap(Map<File, File> analysisMap) {
+        this.analysisMap = analysisMap;
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactory.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactory.java
new file mode 100644
index 0000000..53e161f
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.scala;
+
+import org.gradle.api.internal.tasks.compile.AbstractJavaCompileSpecFactory;
+import org.gradle.api.internal.tasks.compile.CommandLineJavaCompileSpec;
+import org.gradle.api.internal.tasks.compile.ForkingJavaCompileSpec;
+import org.gradle.api.tasks.compile.CompileOptions;
+
+public class DefaultScalaJavaJointCompileSpecFactory extends AbstractJavaCompileSpecFactory<DefaultScalaJavaJointCompileSpec> {
+    public DefaultScalaJavaJointCompileSpecFactory(CompileOptions compileOptions) {
+        super(compileOptions);
+    }
+
+    @Override
+    protected DefaultScalaJavaJointCompileSpec getCommandLineSpec() {
+        return new DefaultCommandLineScalaJavaJointCompileSpec();
+    }
+
+    @Override
+    protected DefaultScalaJavaJointCompileSpec getForkingSpec() {
+        return new DefaultForkingScalaJavaJointCompileSpec();
+    }
+
+    @Override
+    protected DefaultScalaJavaJointCompileSpec getDefaultSpec() {
+        return new DefaultScalaJavaJointCompileSpec();
+    }
+
+    private static class DefaultCommandLineScalaJavaJointCompileSpec extends DefaultScalaJavaJointCompileSpec implements CommandLineJavaCompileSpec {
+    }
+
+    private static class DefaultForkingScalaJavaJointCompileSpec extends DefaultScalaJavaJointCompileSpec implements ForkingJavaCompileSpec {
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java
new file mode 100644
index 0000000..49d6ca0
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java
@@ -0,0 +1,110 @@
+/*
+ * 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.tasks.scala;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.internal.tasks.compile.CompilationFailedException;
+import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Scala {@link org.gradle.language.base.internal.compile.Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
+ */
+public class NormalizingScalaCompiler implements Compiler<ScalaJavaJointCompileSpec> {
+    private static final Logger LOGGER = Logging.getLogger(NormalizingScalaCompiler.class);
+    private final Compiler<ScalaJavaJointCompileSpec> delegate;
+
+    public NormalizingScalaCompiler(Compiler<ScalaJavaJointCompileSpec> delegate) {
+        this.delegate = delegate;
+    }
+
+    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
+        resolveAndFilterSourceFiles(spec);
+        resolveClasspath(spec);
+        resolveNonStringsInCompilerArgs(spec);
+        logSourceFiles(spec);
+        logCompilerArguments(spec);
+        return delegateAndHandleErrors(spec);
+    }
+
+    private void resolveAndFilterSourceFiles(final ScalaJavaJointCompileSpec spec) {
+        spec.setSource(new SimpleFileCollection(spec.getSource().getFiles()));
+    }
+
+    private void resolveClasspath(ScalaJavaJointCompileSpec spec) {
+        ArrayList<File> classPath = Lists.newArrayList(spec.getClasspath());
+        classPath.add(spec.getDestinationDir());
+        spec.setClasspath(classPath);
+
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Class path: {}", spec.getClasspath());
+        }
+    }
+
+    private void resolveNonStringsInCompilerArgs(ScalaJavaJointCompileSpec spec) {
+        // in particular, this is about GStrings
+        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
+    }
+
+    private void logSourceFiles(ScalaJavaJointCompileSpec spec) {
+        if (!spec.getScalaCompileOptions().isListFiles()) {
+            return;
+        }
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("Source files to be compiled:");
+        for (File file : spec.getSource()) {
+            builder.append('\n');
+            builder.append(file);
+        }
+
+        LOGGER.quiet(builder.toString());
+    }
+
+    private void logCompilerArguments(ScalaJavaJointCompileSpec spec) {
+        if (!LOGGER.isDebugEnabled()) {
+            return;
+        }
+
+        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
+        String joinedArgs = Joiner.on(' ').join(compilerArgs);
+        LOGGER.debug("Java compiler arguments: {}", joinedArgs);
+    }
+
+    private WorkResult delegateAndHandleErrors(ScalaJavaJointCompileSpec spec) {
+        try {
+            return delegate.execute(spec);
+        } catch (CompilationFailedException e) {
+            if (spec.getScalaCompileOptions().isFailOnError()) {
+                throw e;
+            }
+            LOGGER.debug("Ignoring compilation failure.");
+            return new SimpleWorkResult(false);
+        }
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java
new file mode 100644
index 0000000..971b6a2
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java
@@ -0,0 +1,31 @@
+/*
+ * 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.tasks.scala;
+
+import org.gradle.api.internal.tasks.compile.JvmLanguageCompileSpec;
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions;
+
+import java.io.File;
+import java.util.Map;
+
+public interface ScalaCompileSpec extends JvmLanguageCompileSpec {
+    BaseScalaCompileOptions getScalaCompileOptions();
+
+    Map<File, File> getAnalysisMap();
+
+    void setAnalysisMap(Map<File, File> analysisMap);
+}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompileSpec.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompileSpec.java
similarity index 100%
rename from subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompileSpec.java
rename to subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompileSpec.java
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java
new file mode 100644
index 0000000..fad4962
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java
@@ -0,0 +1,124 @@
+/*
+ * 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.tasks.scala;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.typesafe.zinc.*;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.internal.tasks.compile.CompilationFailedException;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.jvm.Jvm;
+import scala.Option;
+import xsbti.F0;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+public class ZincScalaCompiler implements Compiler<ScalaJavaJointCompileSpec>, Serializable {
+    private static final Logger LOGGER = Logging.getLogger(ZincScalaCompiler.class);
+    private final Iterable<File> scalaClasspath;
+    private Iterable<File> zincClasspath;
+
+    public ZincScalaCompiler(Iterable<File> scalaClasspath, Iterable<File> zincClasspath) {
+        this.scalaClasspath = scalaClasspath;
+        this.zincClasspath = zincClasspath;
+    }
+
+    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
+        return Compiler.execute(scalaClasspath, zincClasspath, spec);
+    }
+
+    // need to defer loading of Zinc/sbt/Scala classes until we are
+    // running in the compiler daemon and have them on the class path
+    private static class Compiler {
+        static WorkResult execute(Iterable<File> scalaClasspath, Iterable<File> zincClasspath, ScalaJavaJointCompileSpec spec) {
+            LOGGER.info("Compiling with Zinc Scala compiler.");
+
+            xsbti.Logger logger = new SbtLoggerAdapter();
+
+            com.typesafe.zinc.Compiler compiler = createCompiler(scalaClasspath, zincClasspath, logger);
+            List<String> scalacOptions = new ZincScalaCompilerArgumentsGenerator().generate(spec);
+            List<String> javacOptions = new JavaCompilerArgumentsBuilder(spec).includeClasspath(false).build();
+            Inputs inputs = Inputs.create(ImmutableList.copyOf(spec.getClasspath()), ImmutableList.copyOf(spec.getSource()), spec.getDestinationDir(),
+                    scalacOptions, javacOptions, spec.getScalaCompileOptions().getIncrementalOptions().getAnalysisFile(), spec.getAnalysisMap(), "mixed", getIncOptions(), true);
+            if (LOGGER.isDebugEnabled()) {
+                Inputs.debug(inputs, logger);
+            }
+
+            try {
+                compiler.compile(inputs, logger);
+            } catch (xsbti.CompileFailed e) {
+                throw new CompilationFailedException(e);
+            }
+
+            return new SimpleWorkResult(true);
+        }
+
+        private static IncOptions getIncOptions() {
+            //The values are based on what I have found in sbt-compiler-maven-plugin.googlecode.com and zinc documentation
+            //Hard to say what effect they have on the incremental build
+            int transitiveStep = 3;
+            double recompileAllFraction = 0.5d;
+            boolean relationsDebug = false;
+            boolean apiDebug = false;
+            int apiDiffContextSize = 5;
+            Option<File> apiDumpDirectory = Option.empty();
+            boolean transactional = false;
+            Option<File> backup = Option.empty();
+
+            return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, transactional, backup);
+        }
+
+        static com.typesafe.zinc.Compiler createCompiler(Iterable<File> scalaClasspath, Iterable<File> zincClasspath, xsbti.Logger logger) {
+            ScalaLocation scalaLocation = ScalaLocation.fromPath(Lists.newArrayList(scalaClasspath));
+            SbtJars sbtJars = SbtJars.fromPath(Lists.newArrayList(zincClasspath));
+            Setup setup = Setup.create(scalaLocation, sbtJars, Jvm.current().getJavaHome(), true);
+            if (LOGGER.isDebugEnabled()) {
+                Setup.debug(setup, logger);
+            }
+            return com.typesafe.zinc.Compiler.getOrCreate(setup, logger);
+        }
+    }
+
+    private static class SbtLoggerAdapter implements xsbti.Logger {
+        public void error(F0<String> msg) {
+            LOGGER.error(msg.apply());
+        }
+
+        public void warn(F0<String> msg) {
+            LOGGER.warn(msg.apply());
+        }
+
+        public void info(F0<String> msg) {
+            LOGGER.info(msg.apply());
+        }
+
+        public void debug(F0<String> msg) {
+            LOGGER.debug(msg.apply());
+        }
+
+        public void trace(F0<Throwable> exception) {
+            LOGGER.trace(exception.apply().toString());
+        }
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGenerator.java b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGenerator.java
new file mode 100644
index 0000000..32ab679
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGenerator.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.tasks.scala;
+
+import com.google.common.collect.Lists;
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions;
+
+import java.util.List;
+
+public class ZincScalaCompilerArgumentsGenerator {
+    public List<String> generate(ScalaCompileSpec spec) {
+        List<String> result = Lists.newArrayList();
+
+        BaseScalaCompileOptions options = spec.getScalaCompileOptions();
+        addFlag("-deprecation", options.isDeprecation(), result);
+        addFlag("-unchecked", options.isUnchecked(), result);
+        addConcatenatedOption("-g:", options.getDebugLevel(), result);
+        addFlag("-optimise", options.isOptimize(), result);
+        addOption("-encoding", options.getEncoding(), result);
+        addFlag("-verbose", "verbose".equals(options.getDebugLevel()), result);
+        addFlag("-Ydebug", "debug".equals(options.getDebugLevel()), result);
+        if (options.getLoggingPhases() != null) {
+            for (String phase : options.getLoggingPhases()) {
+                addConcatenatedOption("-Ylog:", phase, result);
+            }
+        }
+        if (options.getAdditionalParameters() != null) {
+            result.addAll(options.getAdditionalParameters());
+        }
+
+        return result;
+    }
+
+    private void addFlag(String name, boolean value, List<String> result) {
+        if (value) {
+            result.add(name);
+        }
+    }
+
+    private void addOption(String name, Object value, List<String> result) {
+        if (value != null) {
+            result.add(name);
+            result.add(value.toString());
+        }
+    }
+
+    private void addConcatenatedOption(String name, Object value, List<String> result) {
+        if (value != null) {
+            result.add(name + value.toString());
+        }
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/IncrementalCompileOptions.java b/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/IncrementalCompileOptions.java
new file mode 100644
index 0000000..78cd20e
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/IncrementalCompileOptions.java
@@ -0,0 +1,71 @@
+/*
+ * 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.scala;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.Input;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Options for incremental compilation of Scala code. Only used for compilation with Zinc.
+ */
+ at Incubating
+public class IncrementalCompileOptions implements Serializable {
+    private static final long serialVersionUID = 0;
+
+    private File analysisFile;
+    private File publishedCode;
+
+    /**
+     * Returns the file path where results of code analysis are to be stored.
+     *
+     * @return the file path where which results of code analysis are to be stored
+     */
+    @Input
+    public File getAnalysisFile() {
+        return analysisFile;
+    }
+
+    /**
+     * Sets the file path where results of code analysis are to be stored.
+     */
+    public void setAnalysisFile(File analysisFile) {
+        this.analysisFile = analysisFile;
+    }
+
+    /**
+     * Returns the directory or archive path by which the code produced by this task
+     * is published to other {@code ScalaCompile} tasks.
+     *
+     * @return the directory or archive path by which the code produced by this task
+     * is published to other {@code ScalaCompile} tasks
+     */
+    // only an input for other task instances
+    public File getPublishedCode() {
+        return publishedCode;
+    }
+
+    /**
+     * Sets the directory or archive path by which the code produced by this task
+     * is published to other {@code ScalaCompile} tasks.
+     */
+    public void setPublishedCode(File publishedCode) {
+        this.publishedCode = publishedCode;
+    }
+}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaForkOptions.java b/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/ScalaForkOptions.java
similarity index 100%
rename from subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaForkOptions.java
rename to subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/ScalaForkOptions.java
diff --git a/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/package-info.java b/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/package-info.java
new file mode 100644
index 0000000..d28c467
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/api/tasks/scala/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Scala {@link org.gradle.api.Task} implementations.
+ */
+package org.gradle.api.tasks.scala;
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaLanguageSourceSet.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaLanguageSourceSet.java
new file mode 100644
index 0000000..7d39638
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaLanguageSourceSet.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala;
+
+import org.gradle.api.Incubating;
+import org.gradle.jvm.Classpath;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A set of sources passed to the Scala compiler.
+ */
+ at Incubating
+public interface ScalaLanguageSourceSet extends LanguageSourceSet {
+    Classpath getCompileClasspath();
+}
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaPlatform.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaPlatform.java
new file mode 100644
index 0000000..6e1cf90
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/ScalaPlatform.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala;
+
+import org.gradle.platform.base.Platform;
+
+/**
+ * Defines and configures a Scala Platform.
+ */
+public interface ScalaPlatform extends Platform {
+    String getScalaVersion();
+
+    String getScalaCompatibilityVersion();
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaLanguageSourceSet.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaLanguageSourceSet.java
new file mode 100644
index 0000000..f67db5c
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaLanguageSourceSet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal;
+
+import org.gradle.jvm.Classpath;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.jvm.internal.EmptyClasspath;
+import org.gradle.language.scala.ScalaLanguageSourceSet;
+
+public class DefaultScalaLanguageSourceSet extends BaseLanguageSourceSet implements ScalaLanguageSourceSet {
+
+    private final EmptyClasspath compileClasspath = new EmptyClasspath();
+
+    @Override
+    protected String getTypeName() {
+        return "Scala source";
+    }
+
+    public Classpath getCompileClasspath() {
+        return compileClasspath;
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaPlatform.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaPlatform.java
new file mode 100644
index 0000000..2bbff70
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/DefaultScalaPlatform.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal;
+
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.util.VersionNumber;
+
+public class DefaultScalaPlatform implements ScalaPlatform {
+    private final String scalaCompatibilityVersion;
+    private final String scalaVersion;
+
+    public DefaultScalaPlatform(String scalaVersion) {
+        this(VersionNumber.parse(scalaVersion));
+    }
+
+    public DefaultScalaPlatform(VersionNumber versionNumber) {
+        this.scalaVersion = versionNumber.getMajor() + "." + versionNumber.getMinor() + "." + versionNumber.getMicro();
+        this.scalaCompatibilityVersion = versionNumber.getMajor() + "." + versionNumber.getMinor();
+    }
+
+    public String getScalaVersion() {
+        return scalaVersion;
+    }
+
+    public String getScalaCompatibilityVersion() {
+        return scalaCompatibilityVersion;
+    }
+
+    public String getDisplayName() {
+        return String.format("Scala Platform (Scala %s)", scalaVersion);
+    }
+
+    public String getName() {
+        return String.format("ScalaPlatform%s", scalaVersion);
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProvider.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProvider.java
new file mode 100644
index 0000000..cde6b3d
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProvider.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain;
+
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.api.internal.tasks.scala.DaemonScalaCompiler;
+import org.gradle.api.internal.tasks.scala.NormalizingScalaCompiler;
+import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
+import org.gradle.api.internal.tasks.scala.ZincScalaCompiler;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+import org.gradle.util.TreeVisitor;
+
+import java.io.File;
+import java.util.Set;
+
+public class DefaultScalaToolProvider implements ToolProvider {
+    public static final String DEFAULT_ZINC_VERSION = "0.3.5.3";
+
+    private ProjectFinder projectFinder;
+    private final CompilerDaemonManager compilerDaemonManager;
+    private final Set<File> resolvedScalaClasspath;
+    private final Set<File> resolvedZincClasspath;
+
+    public DefaultScalaToolProvider(ProjectFinder projectFinder, CompilerDaemonManager compilerDaemonManager, Set<File> resolvedScalaClasspath, Set<File> resolvedZincClasspath) {
+        this.projectFinder = projectFinder;
+        this.compilerDaemonManager = compilerDaemonManager;
+        this.resolvedScalaClasspath = resolvedScalaClasspath;
+        this.resolvedZincClasspath = resolvedZincClasspath;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends CompileSpec> org.gradle.language.base.internal.compile.Compiler<T> newCompiler(Class<T> spec) {
+        if (ScalaJavaJointCompileSpec.class.isAssignableFrom(spec)) {
+            File projectDir = projectFinder.getProject(":").getProjectDir();
+            Compiler<ScalaJavaJointCompileSpec> scalaCompiler = new ZincScalaCompiler(resolvedScalaClasspath, resolvedZincClasspath);
+            return (Compiler<T>) new NormalizingScalaCompiler(new DaemonScalaCompiler<ScalaJavaJointCompileSpec>(projectDir, scalaCompiler, compilerDaemonManager, resolvedZincClasspath));
+        }
+        throw new IllegalArgumentException(String.format("Cannot create Compiler for unsupported CompileSpec type '%s'", spec.getSimpleName()));
+    }
+
+    @Override
+    public <T> T get(Class<T> toolType) {
+        throw new IllegalArgumentException(String.format("Don't know how to provide tool of type %s.", toolType.getSimpleName()));
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public void explain(TreeVisitor<? super String> visitor) {
+
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChain.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChain.java
new file mode 100644
index 0000000..21e7af4
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChain.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+
+import java.io.File;
+import java.util.Set;
+
+public class DownloadingScalaToolChain implements ScalaToolChainInternal {
+    public static final String DEFAULT_ZINC_VERSION = "0.3.0";
+
+    private ProjectFinder projectFinder;
+    private CompilerDaemonManager compilerDaemonManager;
+    private final ConfigurationContainer configurationContainer;
+    private final DependencyHandler dependencyHandler;
+    private final JavaVersion javaVersion;
+
+    public DownloadingScalaToolChain(ProjectFinder projectFinder, CompilerDaemonManager compilerDaemonManager, ConfigurationContainer configurationContainer, DependencyHandler dependencyHandler) {
+        this.projectFinder = projectFinder;
+        this.compilerDaemonManager = compilerDaemonManager;
+        this.configurationContainer = configurationContainer;
+        this.dependencyHandler = dependencyHandler;
+        this.javaVersion = JavaVersion.current();
+    }
+
+    public String getName() {
+        return String.format("Scala Toolchain");
+    }
+
+    public String getDisplayName() {
+        return String.format("Scala Toolchain (JDK %s (%s))", javaVersion.getMajorVersion(), javaVersion);
+    }
+
+    public ToolProvider select(ScalaPlatform targetPlatform) {
+        try {
+            Configuration scalaClasspath = resolveDependency(String.format("org.scala-lang:scala-compiler:%s", targetPlatform.getScalaVersion()));
+            Configuration zincClasspath = resolveDependency(String.format("com.typesafe.zinc:zinc:%s", DEFAULT_ZINC_VERSION));
+            Set<File> resolvedScalaClasspath = scalaClasspath.resolve();
+            Set<File> resolvedZincClasspath = zincClasspath.resolve();
+            return new DefaultScalaToolProvider(projectFinder, compilerDaemonManager, resolvedScalaClasspath, resolvedZincClasspath);
+
+        } catch(ResolveException resolveException) {
+            return new NotFoundScalaToolProvider(resolveException);
+        }
+    }
+
+    private Configuration resolveDependency(Object dependencyNotation) {
+        Dependency dependency = dependencyHandler.create(dependencyNotation);
+        return configurationContainer.detachedConfiguration(dependency);
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/NotFoundScalaToolProvider.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/NotFoundScalaToolProvider.java
new file mode 100644
index 0000000..6584209
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/NotFoundScalaToolProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+import org.gradle.util.TreeVisitor;
+
+public class NotFoundScalaToolProvider implements ToolProvider {
+    private Exception exception;
+
+    public NotFoundScalaToolProvider(Exception moduleVersionNotFoundException) {
+        this.exception = moduleVersionNotFoundException;
+    }
+
+    @Override
+    public <T extends CompileSpec> org.gradle.language.base.internal.compile.Compiler<T> newCompiler(Class<T> spec) {
+        throw failure();
+    }
+
+    @Override
+    public <T> T get(Class<T> toolType) {
+        throw failure();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    private RuntimeException failure() {
+        TreeFormatter formatter = new TreeFormatter();
+        this.explain(formatter);
+        return new GradleException(formatter.toString());
+    }
+
+    @Override
+    public void explain(TreeVisitor<? super String> visitor) {
+        visitor.node("Cannot provide Scala Compiler");
+        visitor.startChildren();
+        visitor.node(exception.getCause().getMessage());
+        visitor.endChildren();
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainInternal.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainInternal.java
new file mode 100644
index 0000000..b1d1d12
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain;
+
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.language.scala.toolchain.ScalaToolChain;
+import org.gradle.platform.base.internal.toolchain.ToolChainInternal;
+
+public interface ScalaToolChainInternal extends ScalaToolChain, ToolChainInternal<ScalaPlatform> {
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainServiceRegistry.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainServiceRegistry.java
new file mode 100644
index 0000000..02d2192
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/internal/toolchain/ScalaToolChainServiceRegistry.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain;
+
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class ScalaToolChainServiceRegistry implements PluginServiceRegistry {
+
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.addProvider(new ProjectScopeCompileServices());
+    }
+
+
+    private static class ProjectScopeCompileServices {
+        ScalaToolChainInternal createScalaToolChain(ProjectFinder projectFinder, CompilerDaemonManager compilerDaemonManager, ConfigurationContainer configurationContainer, DependencyHandler dependencyHandler) {
+            return new DownloadingScalaToolChain(projectFinder, compilerDaemonManager, configurationContainer, dependencyHandler);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/package-info.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/package-info.java
new file mode 100644
index 0000000..418ea5e
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Types for Scala language support.
+ */
+package org.gradle.language.scala;
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/ScalaLanguagePlugin.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/ScalaLanguagePlugin.java
new file mode 100644
index 0000000..ff77a5a
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/ScalaLanguagePlugin.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.plugins;
+
+import org.gradle.api.*;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.JvmBinarySpec;
+import org.gradle.jvm.JvmByteCode;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.jvm.plugins.JvmResourcesPlugin;
+import org.gradle.language.scala.ScalaLanguageSourceSet;
+import org.gradle.language.scala.internal.DefaultScalaLanguageSourceSet;
+import org.gradle.language.scala.internal.DefaultScalaPlatform;
+import org.gradle.language.scala.tasks.PlatformScalaCompile;
+import org.gradle.language.scala.toolchain.ScalaToolChain;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+
+
+/**
+ * Plugin for compiling Scala code. Applies the {@link org.gradle.language.base.plugins.ComponentModelBasePlugin} and {@link org.gradle.language.jvm.plugins.JvmResourcesPlugin}.
+ * Registers "scala" language support with the {@link org.gradle.language.scala.ScalaLanguageSourceSet}.
+ */
+ at Incubating
+public class ScalaLanguagePlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+        project.getPluginManager().apply(JvmResourcesPlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+
+        @Model
+        ScalaToolChain scalaToolChain(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(ScalaToolChain.class);
+        }
+
+        @LanguageType
+        void registerLanguage(LanguageTypeBuilder<ScalaLanguageSourceSet> builder) {
+            builder.setLanguageName("scala");
+            builder.defaultImplementation(DefaultScalaLanguageSourceSet.class);
+        }
+
+        @Mutate
+        void registerLanguageTransform(LanguageTransformContainer languages, ServiceRegistry serviceRegistry) {
+            languages.add(new Scala());
+        }
+    }
+
+    private static class Scala implements LanguageTransform<ScalaLanguageSourceSet, JvmByteCode> {
+        public Class<ScalaLanguageSourceSet> getSourceSetType() {
+            return ScalaLanguageSourceSet.class;
+        }
+
+        public Map<String, Class<?>> getBinaryTools() {
+            return Collections.emptyMap();
+        }
+
+        public Class<JvmByteCode> getOutputType() {
+            return JvmByteCode.class;
+        }
+
+        public SourceTransformTaskConfig getTransformTask() {
+            return new SourceTransformTaskConfig() {
+                public String getTaskPrefix() {
+                    return "compile";
+                }
+
+                public Class<? extends DefaultTask> getTaskType() {
+                    return PlatformScalaCompile.class;
+                }
+
+                public void configureTask(Task task, BinarySpec binarySpec, LanguageSourceSet sourceSet) {
+                    PlatformScalaCompile compile = (PlatformScalaCompile) task;
+                    ScalaLanguageSourceSet scalaSourceSet = (ScalaLanguageSourceSet) sourceSet;
+                    JvmBinarySpec binary = (JvmBinarySpec) binarySpec;
+                    JavaPlatform javaPlatform = binary.getTargetPlatform();
+                    // TODO RG resolve the scala platform from the binary
+
+                    compile.setPlatform(new DefaultScalaPlatform("2.10.4"));
+                    File analysisFile = new File(task.getTemporaryDir(), String.format("compilerAnalysis/%s.analysis", task.getName()));
+                    compile.getScalaCompileOptions().getIncrementalOptions().setAnalysisFile(analysisFile);
+
+                    compile.setDescription(String.format("Compiles %s.", scalaSourceSet));
+                    compile.setDestinationDir(binary.getClassesDir());
+
+                    compile.setSource(scalaSourceSet.getSource());
+                    compile.setClasspath(scalaSourceSet.getCompileClasspath().getFiles());
+                    compile.setTargetCompatibility(javaPlatform.getTargetCompatibility().toString());
+                    compile.setSourceCompatibility(javaPlatform.getTargetCompatibility().toString());
+
+                    compile.dependsOn(scalaSourceSet);
+                    binary.getTasks().getJar().dependsOn(compile);
+                }
+            };
+        }
+
+        public boolean applyToBinary(BinarySpec binary) {
+            return binary instanceof JvmBinarySpec;
+        }
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/package-info.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/package-info.java
new file mode 100644
index 0000000..f69ff47
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/plugins/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base plugins that add support for Scala language.
+ */
+ at Incubating
+package org.gradle.language.scala.plugins;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/AbstractScalaCompile.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/AbstractScalaCompile.java
new file mode 100644
index 0000000..4cc3f8d
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/AbstractScalaCompile.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.tasks;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.gradle.api.Project;
+import org.gradle.api.internal.tasks.scala.DefaultScalaJavaJointCompileSpec;
+import org.gradle.api.internal.tasks.scala.DefaultScalaJavaJointCompileSpecFactory;
+import org.gradle.api.internal.tasks.scala.ScalaCompileSpec;
+import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.plugins.ExtraPropertiesExtension;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.CompileOptions;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An abstract Scala compile task sharing common functionality for compiling scala.
+ */
+abstract public class AbstractScalaCompile extends AbstractCompile {
+    protected static final Logger LOGGER = Logging.getLogger(AbstractScalaCompile.class);
+    private final BaseScalaCompileOptions scalaCompileOptions;
+    private final CompileOptions compileOptions = new CompileOptions();
+
+    protected AbstractScalaCompile(BaseScalaCompileOptions scalaCompileOptions) {
+        this.scalaCompileOptions = scalaCompileOptions;
+    }
+
+    /**
+     * Returns the Scala compilation options.
+     */
+    @Nested
+    public BaseScalaCompileOptions getScalaCompileOptions() {
+        return scalaCompileOptions;
+    }
+
+    /**
+     * Returns the Java compilation options.
+     */
+    @Nested
+    public CompileOptions getOptions() {
+        return compileOptions;
+    }
+
+    abstract protected org.gradle.language.base.internal.compile.Compiler<ScalaJavaJointCompileSpec> getCompiler(ScalaJavaJointCompileSpec spec);
+
+    @TaskAction
+    protected void compile() {
+        ScalaJavaJointCompileSpec spec = createSpec();
+        configureIncrementalCompilation(spec);
+        getCompiler(spec).execute(spec);
+    }
+
+    protected ScalaJavaJointCompileSpec createSpec() {
+        DefaultScalaJavaJointCompileSpec spec = new DefaultScalaJavaJointCompileSpecFactory(compileOptions).create();
+        spec.setSource(getSource());
+        spec.setDestinationDir(getDestinationDir());
+        spec.setWorkingDir(getProject().getProjectDir());
+        spec.setTempDir(getTemporaryDir());
+        spec.setClasspath(getClasspath());
+        spec.setSourceCompatibility(getSourceCompatibility());
+        spec.setTargetCompatibility(getTargetCompatibility());
+        spec.setCompileOptions(getOptions());
+        spec.setScalaCompileOptions(scalaCompileOptions);
+        return spec;
+    }
+
+    protected void configureIncrementalCompilation(ScalaCompileSpec spec) {
+
+        Map<File, File> globalAnalysisMap = getOrCreateGlobalAnalysisMap();
+        HashMap<File, File> filteredMap = filterForClasspath(globalAnalysisMap, spec.getClasspath());
+        spec.setAnalysisMap(filteredMap);
+
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Analysis file: {}", scalaCompileOptions.getIncrementalOptions().getAnalysisFile());
+            LOGGER.debug("Published code: {}", scalaCompileOptions.getIncrementalOptions().getPublishedCode());
+            LOGGER.debug("Analysis map: {}", filteredMap);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Map<File, File> getOrCreateGlobalAnalysisMap() {
+        ExtraPropertiesExtension extraProperties = getProject().getRootProject().getExtensions().getExtraProperties();
+        Map<File, File> analysisMap;
+
+        if (extraProperties.has("scalaCompileAnalysisMap")) {
+            analysisMap = (Map) extraProperties.get("scalaCompileAnalysisMap");
+        } else {
+            analysisMap = Maps.newHashMap();
+            for (Project project : getProject().getRootProject().getAllprojects()) {
+                for (AbstractScalaCompile task : project.getTasks().withType(AbstractScalaCompile.class)) {
+                    File publishedCode = task.getScalaCompileOptions().getIncrementalOptions().getPublishedCode();
+                    File analysisFile = task.getScalaCompileOptions().getIncrementalOptions().getAnalysisFile();
+                    analysisMap.put(publishedCode, analysisFile);
+                }
+            }
+            extraProperties.set("scalaCompileAnalysisMap", Collections.unmodifiableMap(analysisMap));
+        }
+        return analysisMap;
+    }
+
+
+    protected HashMap<File, File> filterForClasspath(Map<File, File> analysisMap, Iterable<File> classpath) {
+        final Set<File> classpathLookup = Sets.newHashSet(classpath);
+        return Maps.newHashMap(Maps.filterEntries(analysisMap, new Predicate<Map.Entry<File, File>>() {
+            public boolean apply(Map.Entry<File, File> entry) {
+                return classpathLookup.contains(entry.getKey());
+            }
+        }));
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/BaseScalaCompileOptions.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/BaseScalaCompileOptions.java
new file mode 100644
index 0000000..7848b00
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/BaseScalaCompileOptions.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.compile.AbstractOptions;
+import org.gradle.api.tasks.scala.IncrementalCompileOptions;
+import org.gradle.api.tasks.scala.ScalaForkOptions;
+
+import java.util.List;
+
+/**
+ * Options for Scala platform compilation, excluding any options for compilation with Ant.
+ */
+ at Incubating
+public class BaseScalaCompileOptions extends AbstractOptions {
+
+    private static final long serialVersionUID = 0;
+
+    private boolean failOnError = true;
+
+    private boolean deprecation = true;
+
+    private boolean unchecked = true;
+
+    private String debugLevel;
+
+    private boolean optimize;
+
+    private String encoding;
+
+    private String force = "never";
+
+    private List<String> additionalParameters;
+
+    private boolean listFiles;
+
+    private String loggingLevel;
+
+    private List<String> loggingPhases;
+
+    private ScalaForkOptions forkOptions = new ScalaForkOptions();
+
+    private IncrementalCompileOptions incrementalOptions = new IncrementalCompileOptions();
+
+    /**
+     * Fail the build on compilation errors.
+     */
+    public boolean isFailOnError() {
+        return failOnError;
+    }
+
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Generate deprecation information.
+     */
+    public boolean isDeprecation() {
+        return deprecation;
+    }
+
+    public void setDeprecation(boolean deprecation) {
+        this.deprecation = deprecation;
+    }
+
+    /**
+     * Generate unchecked information.
+     */
+    public boolean isUnchecked() {
+        return unchecked;
+    }
+
+    public void setUnchecked(boolean unchecked) {
+        this.unchecked = unchecked;
+    }
+
+    /**
+     * Generate debugging information.
+     * Legal values: none, source, line, vars, notailcalls
+     */
+    @Input
+    @Optional
+    public String getDebugLevel() {
+        return debugLevel;
+    }
+
+    public void setDebugLevel(String debugLevel) {
+        this.debugLevel = debugLevel;
+    }
+
+    /**
+     * Run optimizations.
+     */
+    @Input
+    public boolean isOptimize() {
+        return optimize;
+    }
+
+    public void setOptimize(boolean optimize) {
+        this.optimize = optimize;
+    }
+
+    /**
+     * Encoding of source files.
+     */
+    @Input @Optional
+    public String getEncoding() {
+        return encoding;
+    }
+
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Whether to force the compilation of all files.
+     * Legal values:
+     * - never (only compile modified files)
+     * - changed (compile all files when at least one file is modified)
+     * - always (always recompile all files)
+     */
+    public String getForce() {
+        return force;
+    }
+
+    public void setForce(String force) {
+        this.force = force;
+    }
+
+    /**
+     * Additional parameters passed to the compiler.
+     * Each parameter must start with '-'.
+     */
+    public List<String> getAdditionalParameters() {
+        return additionalParameters;
+    }
+
+    public void setAdditionalParameters(List<String> additionalParameters) {
+        this.additionalParameters = additionalParameters;
+    }
+
+    /**
+     * List files to be compiled.
+     */
+    public boolean isListFiles() {
+        return listFiles;
+    }
+
+    public void setListFiles(boolean listFiles) {
+        this.listFiles = listFiles;
+    }
+
+    /**
+     * Specifies the amount of logging.
+     * Legal values:  none, verbose, debug
+     */
+    public String getLoggingLevel() {
+        return loggingLevel;
+    }
+
+    public void setLoggingLevel(String loggingLevel) {
+        this.loggingLevel = loggingLevel;
+    }
+
+    /**
+     * Phases of the compiler to log.
+     * Legal values: namer, typer, pickler, uncurry, tailcalls, transmatch, explicitouter, erasure,
+     *               lambdalift, flatten, constructors, mixin, icode, jvm, terminal.
+     */
+    public List<String> getLoggingPhases() {
+        return loggingPhases;
+    }
+
+    public void setLoggingPhases(List<String> loggingPhases) {
+        this.loggingPhases = loggingPhases;
+    }
+
+    /**
+     * Options for running the Scala compiler in a separate process. These options only take effect
+     * if {@code fork} is set to {@code true}.
+     */
+    public ScalaForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    public void setForkOptions(ScalaForkOptions forkOptions) {
+        this.forkOptions = forkOptions;
+    }
+
+
+    @Nested
+    public IncrementalCompileOptions getIncrementalOptions() {
+        return incrementalOptions;
+    }
+
+    public void setIncrementalOptions(IncrementalCompileOptions incrementalOptions) {
+        this.incrementalOptions = incrementalOptions;
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/PlatformScalaCompile.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/PlatformScalaCompile.java
new file mode 100644
index 0000000..a012362
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/PlatformScalaCompile.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerUtil;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+import javax.inject.Inject;
+
+/**
+ * A platform-aware Scala compile task.
+ */
+ at Incubating
+public class PlatformScalaCompile extends AbstractScalaCompile {
+
+    private ScalaPlatform platform;
+
+    @Inject
+    public PlatformScalaCompile() {
+        super(new BaseScalaCompileOptions());
+    }
+
+    public ScalaPlatform getPlatform() {
+        return platform;
+    }
+
+    public void setPlatform(ScalaPlatform platform) {
+        this.platform = platform;
+    }
+
+    @Inject
+    protected ToolResolver getToolResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Compiler<ScalaJavaJointCompileSpec> getCompiler(ScalaJavaJointCompileSpec spec) {
+        return CompilerUtil.castCompiler(getToolResolver().resolveCompiler(spec.getClass(), getPlatform()).get());
+    }
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/package-info.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/package-info.java
new file mode 100644
index 0000000..30be37f
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/tasks/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that add support for Scala language.
+ */
+ at Incubating
+package org.gradle.language.scala.tasks;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/ScalaToolChain.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/ScalaToolChain.java
new file mode 100644
index 0000000..d873008
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/ScalaToolChain.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.toolchain;
+
+import org.gradle.platform.base.ToolChain;
+
+/**
+ * A set of tools for building Scala applications
+ */
+public interface ScalaToolChain extends ToolChain {
+}
diff --git a/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/package-info.java b/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/package-info.java
new file mode 100644
index 0000000..fc1bceb
--- /dev/null
+++ b/subprojects/language-scala/src/main/java/org/gradle/language/scala/toolchain/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Defines tools that can build scala applications.
+ */
+ at Incubating
+package org.gradle.language.scala.toolchain;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala-lang.properties b/subprojects/language-scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala-lang.properties
new file mode 100644
index 0000000..02c6313
--- /dev/null
+++ b/subprojects/language-scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala-lang.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.language.scala.plugins.ScalaLanguagePlugin
diff --git a/subprojects/language-scala/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/language-scala/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..b0074a2
--- /dev/null
+++ b/subprojects/language-scala/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1,16 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+org.gradle.language.scala.internal.toolchain.ScalaToolChainServiceRegistry
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactoryTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactoryTest.groovy
new file mode 100644
index 0000000..ee8b0da
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpecFactoryTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.scala
+
+import org.gradle.api.internal.tasks.compile.CommandLineJavaCompileSpec
+import org.gradle.api.internal.tasks.compile.ForkingJavaCompileSpec
+import org.gradle.api.tasks.compile.CompileOptions
+import spock.lang.Specification
+
+class DefaultScalaJavaJointCompileSpecFactoryTest extends Specification {
+    def "produces correct spec type" () {
+        CompileOptions options = new CompileOptions()
+        options.fork = fork
+        options.forkOptions.executable = executable
+        DefaultScalaJavaJointCompileSpecFactory factory = new DefaultScalaJavaJointCompileSpecFactory(options)
+
+        when:
+        def spec = factory.create()
+
+        then:
+        spec instanceof DefaultScalaJavaJointCompileSpec
+        ForkingJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsForking
+        CommandLineJavaCompileSpec.isAssignableFrom(spec.getClass()) == implementsCommandLine
+
+        where:
+        fork  | executable | implementsForking | implementsCommandLine
+        false | null       | false             | false
+        true  | null       | true              | false
+        true  | "X"        | false             | true
+    }
+}
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy
new file mode 100644
index 0000000..67a9036
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy
@@ -0,0 +1,116 @@
+/*
+ * 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.tasks.scala
+import groovy.transform.InheritConstructors
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.api.internal.tasks.compile.CompilationFailedException
+import org.gradle.api.tasks.WorkResult
+import org.gradle.api.tasks.compile.CompileOptions
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions
+import spock.lang.Specification
+
+class NormalizingScalaCompilerTest extends Specification {
+    Compiler<ScalaJavaJointCompileSpec> target = Mock()
+    DefaultScalaJavaJointCompileSpec spec = new DefaultScalaJavaJointCompileSpec()
+    NormalizingScalaCompiler compiler = new NormalizingScalaCompiler(target)
+
+    def setup() {
+        spec.destinationDir = new File("dest")
+        spec.source = files("Source1.java", "Source2.java", "Source3.java")
+        spec.classpath = files("Dep1.jar", "Dep2.jar")
+        spec.zincClasspath = files("zinc.jar", "zinc-dep.jar")
+        spec.compileOptions = new CompileOptions()
+        spec.scalaCompileOptions = new BaseScalaCompileOptions()
+    }
+
+    def "delegates to target compiler after resolving source and classpaths"() {
+        def workResult = Mock(WorkResult)
+
+        when:
+        def result = compiler.execute(spec)
+
+        then:
+        1 * target.execute(spec) >> {
+            assert spec.source as List == old(spec.source as List)
+
+            assert spec.classpath as List == files("Dep1.jar", "Dep2.jar", "dest") as List
+
+            workResult
+        }
+        result == workResult
+    }
+
+    def "propagates compile failure"() {
+        def failure
+        target.execute(spec) >> { throw failure = new CompilationFailedException() }
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        CompilationFailedException e = thrown()
+        e == failure
+    }
+
+    def "ignores compile failure when failOnError is false"() {
+        target.execute(spec) >> { throw new CompilationFailedException() }
+
+        spec.scalaCompileOptions.failOnError = false
+
+        when:
+        def result = compiler.execute(spec)
+
+        then:
+        noExceptionThrown()
+        !result.didWork
+    }
+
+    def "propagates other failure"() {
+        def failure
+        target.execute(spec) >> { throw failure = new RuntimeException() }
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        RuntimeException e = thrown()
+        e == failure
+    }
+
+    def "resolves any non-strings that make it into custom compiler args"() {
+        spec.compileOptions.compilerArgs << "a dreaded ${"GString"}"
+        spec.compileOptions.compilerArgs << 42
+        assert !spec.compileOptions.compilerArgs.any { it instanceof String }
+
+        when:
+        compiler.execute(spec)
+
+        then:
+        1 * target.execute(_) >> { ScalaJavaJointCompileSpec spec ->
+            assert spec.compileOptions.compilerArgs.every { it instanceof String }
+        }
+    }
+
+    private files(String... paths) {
+        new TestFileCollection(paths.collect { new File(it) })
+    }
+
+    // file collection whose type is distinguishable from SimpleFileCollection
+    @InheritConstructors
+    static class TestFileCollection extends SimpleFileCollection {}
+}
+
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGeneratorTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGeneratorTest.groovy
new file mode 100644
index 0000000..ea4c7fe
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ZincScalaCompilerArgumentsGeneratorTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * 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.tasks.scala
+
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions
+import spock.lang.Specification
+
+class ZincScalaCompilerArgumentsGeneratorTest extends Specification {
+    def generator = new ZincScalaCompilerArgumentsGenerator()
+    def spec = new DefaultScalaJavaJointCompileSpec()
+
+    def setup() {
+        spec.setScalaCompileOptions(new BaseScalaCompileOptions())
+    }
+
+    def "default options"() {
+        expect:
+        generator.generate(spec) as Set == ["-deprecation", "-unchecked"] as Set
+    }
+
+    def "can suppress deprecation flag"() {
+        spec.scalaCompileOptions.deprecation = false
+
+        expect:
+        !generator.generate(spec).contains("-deprecation")
+    }
+
+    def "can suppress unchecked flag"() {
+        spec.scalaCompileOptions.unchecked = false
+
+        expect:
+        !generator.generate(spec).contains("-unchecked")
+    }
+
+    def "generates debug level option"() {
+        spec.scalaCompileOptions.debugLevel = "someLevel"
+
+        expect:
+        generator.generate(spec).contains("-g:someLevel")
+    }
+
+    def "generates optimize flag"() {
+        spec.scalaCompileOptions.optimize = true
+
+        expect:
+        generator.generate(spec).contains("-optimise")
+    }
+
+    def "generates encoding option"() {
+        spec.scalaCompileOptions.encoding = "some encoding"
+
+        when:
+        def args = generator.generate(spec)
+
+        then:
+        args.contains("-encoding")
+        args.contains("some encoding")
+    }
+
+    def "generates verbose flag"() {
+        spec.scalaCompileOptions.debugLevel = "verbose"
+
+        expect:
+        generator.generate(spec).contains("-verbose")
+    }
+
+    def "generates debug flag"() {
+        spec.scalaCompileOptions.debugLevel = "debug"
+
+        expect:
+        generator.generate(spec).contains("-Ydebug")
+    }
+
+    def "generates logging phases options"() {
+        spec.scalaCompileOptions.loggingPhases = ["foo", "bar", "baz"]
+
+        when:
+        def args = generator.generate(spec)
+
+        then:
+        args.contains("-Ylog:foo")
+        args.contains("-Ylog:bar")
+        args.contains("-Ylog:baz")
+    }
+
+    def "adds any additional parameters"() {
+        spec.scalaCompileOptions.additionalParameters = ["-other", "value"]
+
+        when:
+        def args = generator.generate(spec)
+
+        then:
+        args.contains("-other")
+        args.contains("value")
+    }
+}
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProviderTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProviderTest.groovy
new file mode 100644
index 0000000..015d7b8
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DefaultScalaToolProviderTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain
+
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager
+import org.gradle.language.base.internal.compile.CompileSpec
+import spock.lang.Specification
+
+class DefaultScalaToolProviderTest extends Specification {
+    FileResolver fileResolver = Mock()
+    CompilerDaemonManager compilerDaemonManager = Mock()
+    Set<File> scalacClasspath = Mock()
+    Set<File> zincClasspath = Mock()
+    ProjectFinder projectFinder = Mock()
+
+    def "newCompiler provides decent error for unsupported CompileSpec"() {
+        setup:
+        DefaultScalaToolProvider scalaToolProvider = new DefaultScalaToolProvider(projectFinder, compilerDaemonManager, scalacClasspath, zincClasspath)
+
+        when:
+        scalaToolProvider.newCompiler(UnknownCompileSpec.class)
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Cannot create Compiler for unsupported CompileSpec type 'UnknownCompileSpec'"
+    }
+}
+
+class UnknownCompileSpec implements CompileSpec {}
+
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChainTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChainTest.groovy
new file mode 100644
index 0000000..02261bf
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/internal/toolchain/DownloadingScalaToolChainTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.internal.toolchain
+
+import org.gradle.api.GradleException
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.ResolveException
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager
+import org.gradle.api.internal.tasks.scala.ScalaCompileSpec
+import org.gradle.internal.text.TreeFormatter
+import org.gradle.language.scala.ScalaPlatform
+import spock.lang.Specification
+
+class DownloadingScalaToolChainTest extends Specification {
+
+    ConfigurationContainer configurationContainer = Mock()
+    CompilerDaemonManager compilerDaemonManager = Mock()
+    DependencyHandler dependencyHandler = Mock()
+    ProjectFinder projectFinder = Mock()
+    DownloadingScalaToolChain scalaToolChain = new DownloadingScalaToolChain(projectFinder, compilerDaemonManager, configurationContainer, dependencyHandler)
+    ScalaPlatform scalaPlatform = Mock()
+
+    def setup() {
+        _ * scalaPlatform.getScalaVersion() >> "2.10.4"
+    }
+
+    def "tools available when compiler dependencies can be resolved"() {
+        when:
+        dependencyAvailable("scala-compiler")
+        dependencyAvailable("zinc")
+        then:
+        scalaToolChain.select(scalaPlatform).isAvailable()
+    }
+
+    def "tools not available when compiler dependencies cannot be resolved"() {
+        when:
+        dependencyNotAvailable("scala-compiler")
+        def toolProvider = scalaToolChain.select(scalaPlatform)
+        toolProvider.newCompiler(ScalaCompileSpec.class)
+
+        then:
+        !toolProvider.isAvailable()
+        TreeFormatter scalacErrorFormatter = new TreeFormatter()
+        toolProvider.explain(scalacErrorFormatter)
+        scalacErrorFormatter.toString() == "Cannot provide Scala Compiler: Cannot resolve 'scala-compiler'."
+        def e = thrown(GradleException)
+        e.message == "Cannot provide Scala Compiler: Cannot resolve 'scala-compiler'."
+
+        when:
+        dependencyAvailable("scala-compiler")
+        dependencyNotAvailable("zinc")
+        toolProvider = scalaToolChain.select(scalaPlatform)
+        toolProvider.newCompiler(ScalaCompileSpec.class)
+
+        then:
+        def zincErrorFormatter = new TreeFormatter()
+        !toolProvider.isAvailable()
+        toolProvider.explain(zincErrorFormatter)
+        zincErrorFormatter.toString() == "Cannot provide Scala Compiler: Cannot resolve 'zinc'."
+        e = thrown(GradleException)
+        e.message == "Cannot provide Scala Compiler: Cannot resolve 'zinc'."
+    }
+
+    private void dependencyAvailable(String dependency) {
+        Dependency someDependency = Mock()
+        Configuration someConfiguration = Mock()
+        (_..1) * dependencyHandler.create({ it =~ dependency }) >> someDependency
+        (_..1) * configurationContainer.detachedConfiguration(someDependency) >> someConfiguration
+        (_..1) * someConfiguration.resolve() >> new HashSet<File>()
+    }
+
+    private void dependencyNotAvailable(String dependency) {
+        Dependency someDependency = Mock()
+        ResolveException resolveException = Mock()
+        Exception resolveExceptionCause = Mock()
+        Configuration someConfiguration = Mock()
+
+        _ * resolveException.cause >> resolveExceptionCause
+        _ * resolveExceptionCause.getMessage() >> "Cannot resolve '$dependency'."
+        1 * dependencyHandler.create({ it =~ dependency }) >> someDependency
+        1 * configurationContainer.detachedConfiguration(someDependency) >> someConfiguration
+        1 * someConfiguration.resolve() >> { throw resolveException }
+    }
+
+}
diff --git a/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginTest.groovy b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginTest.groovy
new file mode 100644
index 0000000..edbfc3e
--- /dev/null
+++ b/subprojects/language-scala/src/test/groovy/org/gradle/language/scala/plugins/ScalaLanguagePluginTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.scala.ScalaLanguageSourceSet
+import org.gradle.test.fixtures.plugin.AbstractLanguagePluginSpec
+
+class ScalaLanguagePluginTest extends AbstractLanguagePluginSpec {
+
+    @Override
+    Class<Plugin> getPluginClass() {
+        return ScalaLanguagePlugin
+    }
+
+    @Override
+    Class<? extends LanguageSourceSet> getLanguageSourceSet() {
+        return ScalaLanguageSourceSet
+    }
+
+    @Override
+    String getLanguageId() {
+        return "scala"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/BadScalaLibrary.groovy b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/BadScalaLibrary.groovy
new file mode 100644
index 0000000..31f717c
--- /dev/null
+++ b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/BadScalaLibrary.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.fixtures
+
+import org.gradle.integtests.fixtures.jvm.JvmSourceFile
+
+class BadScalaLibrary {
+    List<JvmSourceFile> sources = [
+            new JvmSourceFile("compile/test", "Person.scala", '''
+package compile.test;
+
+class Person(name: String, age: Integer) {
+    def toString(): String = name + ", " + age;
+}'''),
+            new JvmSourceFile("compile/test", "Person2.scala", '''
+package compile.test;
+
+class Person2 {
+    def test;
+}
+''')
+    ]
+
+    List<String> compilerErrors = [
+            "Person.scala:5: overriding method toString in class Object of type ()String",
+            "Person2.scala:4: class Person2 needs to be abstract, since method test is not defined"
+
+    ]
+}
diff --git a/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestJointCompiledComponent.groovy b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestJointCompiledComponent.groovy
new file mode 100644
index 0000000..1619089
--- /dev/null
+++ b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestJointCompiledComponent.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.fixtures
+
+import org.gradle.integtests.fixtures.jvm.JvmSourceFile
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.scala.ScalaLanguageSourceSet
+
+class TestJointCompiledComponent extends TestJvmComponent {
+
+    String languageName = "scala"
+    String sourceSetTypeName = ScalaLanguageSourceSet.class.name
+
+    List<JvmSourceFile> sources = [
+            new JvmSourceFile("compile/test", "Person.scala", '''
+package compile.test;
+
+class Person(name: String, age: Integer) {
+    override def toString(): String = name + ", " + age;
+}'''),
+            new JvmSourceFile("compile/test", "Person2.java", '''
+package compile.test;
+
+class Person2 {
+    String name;
+    String age;
+}
+''')
+    ]
+
+    @Override
+    List<String> getSourceFileExtensions() {
+        return ["java", "scala"]
+    }
+
+}
diff --git a/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestScalaComponent.groovy b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestScalaComponent.groovy
new file mode 100644
index 0000000..2d2c55b
--- /dev/null
+++ b/subprojects/language-scala/src/testFixtures/groovy/org/gradle/language/scala/fixtures/TestScalaComponent.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.scala.fixtures
+
+import org.gradle.integtests.fixtures.jvm.IncrementalTestJvmComponent
+import org.gradle.integtests.fixtures.jvm.JvmSourceFile
+import org.gradle.language.scala.ScalaLanguageSourceSet
+import org.gradle.test.fixtures.file.TestFile
+
+class TestScalaComponent extends IncrementalTestJvmComponent {
+
+    String languageName = "scala"
+    String sourceSetTypeName = ScalaLanguageSourceSet.class.name
+
+    List<JvmSourceFile> sources = [
+            new JvmSourceFile("compile/test", "Person.scala", '''
+package compile.test;
+
+class Person(name: String, age: Integer) {
+    override def toString(): String = name + ", " + age;
+}'''),
+            new JvmSourceFile("compile/test", "Person2.scala", '''
+package compile.test;
+
+class Person2 {
+}
+''')
+    ]
+
+    @Override
+    void changeSources(List<TestFile> sourceFiles) {
+        def personScalaFile = sourceFiles.find { it.name == "Person.scala" }
+        personScalaFile.text = personScalaFile.text.replace("name", "lastName")
+    }
+
+    @Override
+    void writeAdditionalSources(TestFile testFile) {
+        testFile.file("scala/Extra.scala") << """
+object Extra {
+  def someMethod(args: Array[String]) {
+  }
+}
+"""
+
+    }
+}
diff --git a/subprojects/launcher/launcher.gradle b/subprojects/launcher/launcher.gradle
index b8c43d9..b045bac 100644
--- a/subprojects/launcher/launcher.gradle
+++ b/subprojects/launcher/launcher.gradle
@@ -14,11 +14,21 @@ dependencies {
 
     testCompile libraries.groovy
 
+    integTestRuntime project(':plugins')
+
     startScriptGenerator project(':plugins')
+
+    testFixturesCompile project(':internalTesting')
 }
 
 useTestFixtures()
 
+integTestTasks.all {
+    if (isCiServer) {
+        maxParallelForks = Math.min(3, rootProject.maxParallelForks)
+    }
+}
+
 jar {
     manifest.mainAttributes('Main-Class': "org.gradle.launcher.GradleMain")
     doFirst {
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/BuildEnvironmentIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/BuildEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..17c046d
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/BuildEnvironmentIntegrationTest.groovy
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+import spock.lang.Unroll
+
+import java.nio.charset.Charset
+
+class BuildEnvironmentIntegrationTest extends AbstractIntegrationSpec {
+
+    @Unroll("default locale for gradle build switched to #locale")
+    def "builds can be executed with different default locales"() {
+        given:
+        executer.withDefaultLocale(locale)
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert Locale.getDefault().toString() == "${locale}"
+}
+""", "UTF-8")
+
+        expect:
+        succeeds 'check'
+
+        where:
+        locale << [nonDefaultLocale, Locale.default]
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3145")
+    def "locale props given on the command line are respected"() {
+        given:
+        def nonDefaultLocale = getNonDefaultLocale()
+        executer.requireGradleHome()
+        executer.withArguments("-Duser.language=$nonDefaultLocale.language", "-Duser.country=$nonDefaultLocale.country")
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert Locale.getDefault().toString() == "${nonDefaultLocale}"
+}
+""", "UTF-8")
+
+        expect:
+        succeeds 'check'
+    }
+
+    def "locale props given in gradle.properties are respected"() {
+        given:
+        def nonDefaultLocale = getNonDefaultLocale()
+        executer.requireGradleHome()
+        file("gradle.properties") << "org.gradle.jvmargs=-Duser.language=$nonDefaultLocale.language -Duser.country=$nonDefaultLocale.country"
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert Locale.getDefault().toString() == "${nonDefaultLocale}"
+}
+""", "UTF-8")
+
+        expect:
+        succeeds 'check'
+    }
+
+    def "default file encoding set in gradle.properties is respected"() {
+        given:
+        def nonDefaultEncoding = ["UTF-8", "US-ASCII"].collect { Charset.forName(it) }.find { it != Charset.defaultCharset() }
+
+        executer.requireGradleHome()
+        file("gradle.properties") << "org.gradle.jvmargs=-Dfile.encoding=${nonDefaultEncoding.name()}"
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert ${Charset.class.name}.defaultCharset().name() == "${nonDefaultEncoding}"
+}
+""", "UTF-8")
+
+        expect:
+        succeeds 'check'
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3145")
+    def "default file encoding set on command line is respected"() {
+        given:
+        def nonDefaultEncoding = ["UTF-8", "US-ASCII"].collect { Charset.forName(it) }.find { it != Charset.defaultCharset() }
+
+        executer.requireGradleHome()
+        executer.withArgument("-Dfile.encoding=${nonDefaultEncoding.name()}")
+
+        and:
+        buildFile.setText("""
+task check << {
+    assert ${Charset.class.name}.defaultCharset().name() == "${nonDefaultEncoding}"
+}
+""", "UTF-8")
+
+        expect:
+        succeeds 'check'
+    }
+
+    Locale getNonDefaultLocale() {
+        [new Locale('de'), new Locale('en')].find { it != Locale.default }
+    }
+
+    def executerEncoding(String inputEncoding) {
+        if (inputEncoding) {
+            executer.withDefaultCharacterEncoding(inputEncoding)
+        }
+    }
+
+    @Unroll("build default encoding matches specified - input = #inputEncoding, expectedEncoding: #expectedEncoding")
+    def "build default encoding matches specified"(String inputEncoding, String expectedEncoding) {
+        given:
+        executerEncoding inputEncoding
+
+        and:
+        buildFile.write """
+            task echoDefaultEncoding {
+                doFirst {
+                    println "default encoding: " + java.nio.charset.Charset.defaultCharset().name()
+                }
+            }
+        """, expectedEncoding
+
+        when:
+        run "echoDefaultEncoding"
+
+        then:
+        output.contains "default encoding: $expectedEncoding"
+
+        where:
+        inputEncoding | expectedEncoding
+        "UTF-8"       | "UTF-8"
+        "US-ASCII"    | "US-ASCII"
+        null          | Charset.defaultCharset().name()
+    }
+
+    @Unroll("forked java processes inherit default encoding - input = #inputEncoding, expectedEncoding: #expectedEncoding")
+    def "forked java processes inherit default encoding"() {
+        given:
+        executerEncoding inputEncoding
+
+        and:
+        file("src", "main", "java").mkdirs()
+        file("src", "main", "java", "EchoEncoding.java").write """
+            package echo;
+
+            import java.nio.charset.Charset;
+
+            public class EchoEncoding {
+                public static void main(String[] args) {
+                    System.out.println("default encoding: " + Charset.defaultCharset().name());
+                }
+            }
+        """, executer.getDefaultCharacterEncoding()
+
+
+        and:
+        buildFile.write """
+            apply plugin: "java"
+
+            task echoDefaultEncoding(type: JavaExec) {
+                classpath = project.files(compileJava)
+                main "echo.EchoEncoding"
+            }
+        """, expectedEncoding
+
+        when:
+        run "echoDefaultEncoding"
+
+        then:
+        output.contains "default encoding: $expectedEncoding"
+
+        where:
+        inputEncoding | expectedEncoding
+        "UTF-8"       | "UTF-8"
+        "US-ASCII"    | "US-ASCII"
+        null          | Charset.defaultCharset().name()
+    }
+
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy
new file mode 100644
index 0000000..d204628
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.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.launcher
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.util.GradleVersion
+import spock.lang.IgnoreIf
+import spock.lang.Unroll
+
+class CommandLineIntegrationSpec extends AbstractIntegrationSpec {
+    @IgnoreIf({ AvailableJavaHomes.java5 == null })
+    def "provides reasonable failure message when attempting to run under java 5"() {
+        def jdk = AvailableJavaHomes.java5
+
+        given:
+        executer.withJavaHome(jdk.javaHome)
+
+        expect:
+        fails("help")
+        failure.assertHasDescription("Gradle ${GradleVersion.current().version} requires Java 6 or later to run. You are currently using Java 5.")
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.parallel })
+    @Unroll
+    def "reasonable failure message when --max-workers=#value"() {
+        given:
+        requireGradleHome() // otherwise exception gets thrown in testing infrastructure
+
+        when:
+        args("--max-workers=$value")
+
+        then:
+        fails "help"
+
+        and:
+        errorOutput.trim().readLines()[0] == "Argument value '$value' given for --max-workers option is invalid (must be a positive, non-zero, integer)"
+
+        where:
+        value << ["-1", "0", "foo", " 1"]
+    }
+
+    @Unroll
+    def "reasonable failure message when org.gradle.workers.max=#value"() {
+        given:
+        requireGradleHome() // otherwise exception gets thrown in testing infrastructure
+
+        when:
+        args("-Dorg.gradle.workers.max=$value")
+
+        then:
+        fails "help"
+
+        and:
+        failure.assertHasDescription "Value '$value' given for org.gradle.workers.max system property is invalid (must be a positive, non-zero, integer)"
+
+        where:
+        value << ["-1", "0", "foo", " 1"]
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/EnablingParallelExecutionIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/EnablingParallelExecutionIntegrationTest.groovy
index eb4e769..18bf2f3 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/EnablingParallelExecutionIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/EnablingParallelExecutionIntegrationTest.groovy
@@ -16,14 +16,18 @@
 
 
 package org.gradle.launcher
-
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.Ignore
 import spock.lang.IgnoreIf
 
 @IgnoreIf({ GradleContextualExecuter.parallel })
 class EnablingParallelExecutionIntegrationTest extends AbstractIntegrationSpec {
 
+    def setup() {
+        executer.withDeprecationChecksDisabled()
+    }
+
     def "parallel mode enabled from command line"() {
         buildFile << "assert gradle.startParameter.parallelThreadCount == 15"
         expect:
@@ -32,7 +36,7 @@ class EnablingParallelExecutionIntegrationTest extends AbstractIntegrationSpec {
 
     def "parallel mode enabled via gradle.properties"() {
         file("gradle.properties") << "org.gradle.parallel=true"
-        buildFile << "assert gradle.startParameter.parallelThreadCount == -1"
+        buildFile << "assert gradle.startParameter.parallelProjectExecutionEnabled"
         expect:
         run()
     }
@@ -43,4 +47,12 @@ class EnablingParallelExecutionIntegrationTest extends AbstractIntegrationSpec {
         expect:
         run("--parallel-threads=15")
     }
+
+    @Ignore
+    def "parallel-threads is deprecated"() {
+        when:
+        run("--parallel-threads=15")
+        then:
+        output.contains("--parallel-threads option is deprecated")
+    }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
index c8cfaba..269a022 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
@@ -18,17 +18,18 @@ package org.gradle.launcher
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.jvm.JavaInfo
 import org.gradle.internal.jvm.Jvm
+import org.gradle.util.GradleVersion
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
 import org.gradle.util.TextUtil
 import spock.lang.IgnoreIf
 
- at IgnoreIf( { GradleContextualExecuter.embedded })
 class GradleConfigurabilityIntegrationSpec extends AbstractIntegrationSpec {
 
     def setup() {
+        executer.requireGradleHome()
         executer.requireIsolatedDaemons()
     }
 
@@ -48,8 +49,22 @@ assert java.lang.management.ManagementFactory.runtimeMXBean.inputArguments.conta
         """
     }
 
+    def "shows decent message when awkward java home used"() {
+        def dummyJdk = file("dummyJdk").createDir()
+        assert dummyJdk.isDirectory()
+
+        when:
+        file("gradle.properties").writeProperties(["org.gradle.java.home": dummyJdk.absolutePath])
+
+        then:
+        fails()
+
+        and:
+        failure.assertHasDescription("Java home supplied via 'org.gradle.java.home' seems to be invalid: ${dummyJdk.absolutePath}")
+    }
+
     @Requires(TestPrecondition.SYMLINKS)
-    def "connects to the daemon if java home is a symlink"() {
+    def "handles java home that is a symlink"() {
         given:
         def javaHome = Jvm.current().javaHome
         def javaLink = file("javaLink")
@@ -91,31 +106,39 @@ assert inputArgs.find { it.contains('-XX:HeapDumpPath=') }
 """
     }
 
-    def String useAlternativeJavaPath() {
-        File javaHome = AvailableJavaHomes.bestAlternative
-        String javaPath = TextUtil.escapeString(javaHome.canonicalPath)
-        file("gradle.properties") << "org.gradle.java.home=$javaPath"
-
-        return javaPath
+    def String useAlternativeJavaPath(JavaInfo jvm = AvailableJavaHomes.differentJdk) {
+        File javaHome = jvm.javaHome
+        file("gradle.properties").writeProperties("org.gradle.java.home": javaHome.canonicalPath)
+        return javaHome.canonicalPath
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null })
     def "honours java home specified in gradle.properties"() {
         given:
         String javaPath = useAlternativeJavaPath()
 
         expect:
-        buildSucceeds "assert System.getProperty('java.home').startsWith('$javaPath')"
+        buildSucceeds "assert System.getProperty('java.home').startsWith('${TextUtil.escapeString(javaPath)}')"
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null || System.getProperty('java.runtime.version') == null})
+    @IgnoreIf({ AvailableJavaHomes.differentVersion == null || System.getProperty('java.runtime.version') == null})
     def "does not alter java.runtime.version"() {
         given:
 
-        useAlternativeJavaPath()
+        useAlternativeJavaPath(AvailableJavaHomes.differentVersion)
         String javaRuntimeVersion = System.getProperty('java.runtime.version')
 
         expect:
         buildSucceeds "assert System.getProperty('java.runtime.version') != '${javaRuntimeVersion}'"
     }
+
+    @IgnoreIf({ AvailableJavaHomes.java5 == null })
+    def "fails when configured to use Java 5"() {
+        given:
+        file("gradle.properties").writeProperties("org.gradle.java.home": AvailableJavaHomes.java5.javaHome.canonicalPath)
+
+        expect:
+        fails()
+        failure.assertHasDescription("Gradle ${GradleVersion.current().version} requires Java 6 or later to run. Your build is currently configured to use Java 5.")
+    }
 }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleNativeIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleNativeIntegrationTest.groovy
new file mode 100644
index 0000000..c5226ac
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleNativeIntegrationTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.NOT_UNKNOWN_OS)
+class GradleNativeIntegrationTest extends AbstractIntegrationSpec {
+    def "caches native binaries in specified user home"() {
+        given:
+        executer.requireOwnGradleUserHomeDir()
+        executer.requireGradleHome()
+
+        when:
+        succeeds "help"
+
+        then:
+        executer.gradleUserHomeDir.file("native").assertIsDir()
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
index 891f339..3803c31 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
@@ -16,19 +16,14 @@
 
 package org.gradle.launcher.daemon
 
-import org.gradle.launcher.daemon.client.DaemonDisappearedException
 import org.gradle.launcher.daemon.logging.DaemonMessages
-import org.gradle.util.TextUtil
+import org.gradle.util.GradleVersion
 import spock.lang.Timeout
 
 import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
 
 class DaemonFeedbackIntegrationSpec extends DaemonIntegrationSpec {
-    def setup() {
-        executer.requireIsolatedDaemons()
-    }
-
-    def "daemon keeps logging to the file even if the build is started"() {
+    def "daemon keeps logging to the file even if the build is stopped"() {
         given:
         file("build.gradle") << """
 task sleep << {
@@ -68,21 +63,6 @@ task sleep << {
         ex.message.contains("-Xyz")
     }
 
-    @Timeout(25)
-    def "promptly shows decent message when awkward java home used"() {
-        def dummyJdk = file("dummyJdk").createDir()
-        assert dummyJdk.isDirectory()
-        def jdkPath = TextUtil.escapeString(dummyJdk.canonicalPath)
-        
-        when:
-        executer.withArguments("-Dorg.gradle.java.home=$jdkPath").run()
-
-        then:
-        def ex = thrown(Exception)
-        ex.message.contains('org.gradle.java.home')
-        ex.message.contains(jdkPath)
-    }
-
     def "daemon log contains all necessary logging"() {
         given:
         file("build.gradle") << "println 'Hello build!'"
@@ -169,18 +149,6 @@ task sleep << {
         log.count('error me!') == 1
     }
 
-    def "disappearing daemon makes client log useful information"() {
-        given:
-        file("build.gradle") << "System.exit(0)"
-
-        when:
-        def failure = executer.withArguments("-q").runWithFailure()
-
-        then:
-        failure.error.contains(DaemonDisappearedException.MESSAGE)
-        failure.error.contains(DaemonMessages.DAEMON_VM_SHUTTING_DOWN)
-    }
-
     def "foreground daemon log honors log levels for logging"() {
         given:
         file("build.gradle") << """
@@ -218,8 +186,9 @@ task sleep << {
 
     List<File> getLogs(baseDir) {
         //the gradle version dir
-        assert baseDir.listFiles().length == 1
-        def daemonFiles = baseDir.listFiles()[0].listFiles()
+        def daemonDir = new File(baseDir, GradleVersion.current().version)
+        assert daemonDir.exists()
+        def daemonFiles = daemonDir.listFiles()
 
         daemonFiles.findAll { it.name.endsWith('.log') }
     }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonHealthLoggingIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonHealthLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..56dfc71
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonHealthLoggingIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+class DaemonHealthLoggingIntegrationTest extends DaemonIntegrationSpec {
+
+    def setup() {
+        executer.requireIsolatedDaemons()
+    }
+
+    def "health information is present in build log"() {
+        file("gradle.properties") << "org.gradle.jvmargs=-Dorg.gradle.daemon.performance.logging=true"
+        when: def r = executer.noExtraLogging().run()
+        then: r.output.contains("Starting build in new daemon [memory: ")
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitScriptHandlingIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitScriptHandlingIntegrationTest.groovy
index d2507f7..2786117 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitScriptHandlingIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitScriptHandlingIntegrationTest.groovy
@@ -27,7 +27,7 @@ import spock.lang.Issue
 /**
  * Tests that init scripts are used from the _clients_ GRADLE_HOME, not the daemon server's.
  */
- at Issue("http://issues.gradle.org/browse/GRADLE-2408")
+ at Issue("https://issues.gradle.org/browse/GRADLE-2408")
 class DaemonInitScriptHandlingIntegrationTest extends DaemonIntegrationSpec {
 
     TestFile createDistribution(int i) {
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitialCommunicationFailureIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitialCommunicationFailureIntegrationSpec.groovy
index 8204eab..9eef334 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitialCommunicationFailureIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonInitialCommunicationFailureIntegrationSpec.groovy
@@ -18,7 +18,6 @@ package org.gradle.launcher.daemon
 
 import org.gradle.integtests.fixtures.KillProcessAvailability
 import org.gradle.launcher.daemon.logging.DaemonMessages
-import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
 import org.junit.Rule
 import org.junit.rules.ExternalResource
 import spock.lang.IgnoreIf
@@ -35,10 +34,6 @@ class DaemonInitialCommunicationFailureIntegrationSpec extends DaemonIntegration
 
     @Rule TestServer server = new TestServer()
 
-    def cleanup() {
-        stopDaemonsNow()
-    }
-
     @Issue("GRADLE-2444")
     def "behaves if the registry contains connectable port without daemon on the other end"() {
         when:
@@ -46,12 +41,12 @@ class DaemonInitialCommunicationFailureIntegrationSpec extends DaemonIntegration
 
         then:
         //there should be one idle daemon
-        def daemon = new DaemonLogsAnalyzer(executer.daemonBaseDir).daemon
+        def daemon = daemons.daemon
 
         when:
-        // Wait until the daemon has finished updating the registry. Killing it halfway through the registry update will leave the registry corrupted,
+        // Ensure that the daemon has finished updating the registry. Killing it halfway through the registry update will leave the registry corrupted,
         // and the client will just throw the registry away and replace it with an empty one
-        daemon.waitUntilIdle()
+        daemon.assertIdle()
         daemon.kill()
 
         and:
@@ -82,10 +77,10 @@ class DaemonInitialCommunicationFailureIntegrationSpec extends DaemonIntegration
         buildSucceeds()
 
         then:
-        def daemon = new DaemonLogsAnalyzer(executer.daemonBaseDir).daemon
+        def daemon = daemons.daemon
 
         when:
-        daemon.waitUntilIdle()
+        daemon.assertIdle()
         daemon.kill()
         poll {
             server.tryStart(daemon.port)
@@ -112,19 +107,19 @@ class DaemonInitialCommunicationFailureIntegrationSpec extends DaemonIntegration
         buildSucceeds()
 
         then:
-        def daemon = new DaemonLogsAnalyzer(executer.daemonBaseDir).daemon
+        def daemon = daemons.daemon
 
         when:
-        daemon.waitUntilIdle()
+        daemon.assertIdle()
         daemon.kill()
 
         then:
         buildSucceeds()
 
         and:
-        def analyzer = new DaemonLogsAnalyzer(executer.daemonBaseDir)
+        def analyzer = daemons
         analyzer.daemons.size() == 2        //2 daemon participated
-        analyzer.registry.all.size() == 1   //only one address in the registry
+        analyzer.visible.size() == 1        //only one address in the registry
     }
 
     private static class TestServer extends ExternalResource {
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonIntegrationSpec.groovy
index f3b7ebc..bdd2b01 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonIntegrationSpec.groovy
@@ -16,13 +16,12 @@
 
 package org.gradle.launcher.daemon
 
-import ch.qos.logback.classic.Level
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.DaemonGradleExecuter
-import org.slf4j.LoggerFactory
-
-class DaemonIntegrationSpec extends AbstractIntegrationSpec {
+import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
+import org.gradle.launcher.daemon.testing.DaemonsFixture
 
+abstract class DaemonIntegrationSpec extends AbstractIntegrationSpec {
     String output
 
     @Override
@@ -33,7 +32,13 @@ class DaemonIntegrationSpec extends AbstractIntegrationSpec {
     def setup() {
         executer = new DaemonGradleExecuter(distribution, temporaryFolder)
         executer.requireIsolatedDaemons()
-        LoggerFactory.getLogger("org.gradle.cache.internal.DefaultFileLockManager").level = Level.INFO
+    }
+
+    @Override
+    protected void cleanupWhileTestFilesExist() {
+        // Need to kill daemons before test files are cleaned up, as the log files and registry are used to locate the daemons and these live under
+        // the test file directory.
+        daemons.killAll()
     }
 
     void stopDaemonsNow() {
@@ -47,7 +52,7 @@ class DaemonIntegrationSpec extends AbstractIntegrationSpec {
         output = result.output
     }
 
-    def cleanup() {
-        stopDaemonsNow()
+    DaemonsFixture getDaemons() {
+        new DaemonLogsAnalyzer(executer.daemonBaseDir)
     }
 }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
index 141a5a3..f2cd692 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
@@ -19,10 +19,9 @@ package org.gradle.launcher.daemon
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.executer.GradleHandle
 import org.gradle.internal.jvm.Jvm
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.launcher.daemon.client.DaemonDisappearedException
 import org.gradle.launcher.daemon.testing.DaemonContextParser
 import org.gradle.launcher.daemon.testing.DaemonEventSequenceBuilder
+import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
 import spock.lang.IgnoreIf
 
 import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
@@ -110,6 +109,10 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
         }
     }
 
+    @Override
+    protected void cleanupWhileTestFilesExist() {
+    }
+
     void stopDaemons() {
         run { stopDaemonsNow() }
     }
@@ -128,7 +131,7 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
 
     void startForegroundDaemonWithAlternateJavaHome() {
         run {
-            javaHome = AvailableJavaHomes.bestAlternative
+            javaHome = AvailableJavaHomes.differentJdk.javaHome
             startForegroundDaemonNow()
             javaHome = null
         }
@@ -169,22 +172,14 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
         run { failed builds[num] }
     }
 
-    void foregroundDaemonFailed(int num = 0) {
-        run { failed foregroundDaemons[num] }
+    void foregroundDaemonCompleted(int num = 0) {
+        run { foregroundDaemons[num].waitForFinish() }
     }
 
     void failed(GradleHandle handle) {
         assert handle.waitForFailure()
     }
 
-    void buildFailedWithDaemonDisappearedMessage(num = 0) {
-        run {
-            def build = builds[num]
-            failed build
-            assert build.errorOutput.contains(DaemonDisappearedException.MESSAGE)
-        }
-    }
-
     void daemonContext(num = 0, Closure assertions) {
         run { doDaemonContext(builds[num], assertions) }
     }
@@ -194,7 +189,7 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
     }
 
     void doDaemonContext(gradleHandle, Closure assertions) {
-        DaemonContextParser.parseFrom(gradleHandle.standardOutput).with(assertions)
+        DaemonContextParser.parseFromString(gradleHandle.standardOutput).with(assertions)
     }
 
     def "daemons do some work - sit idle - then timeout and die"() {
@@ -234,28 +229,6 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
         busy()
     }
 
-    def "existing idle background daemons are used"() {
-        when:
-        startBuild()
-        waitForBuildToWait()
-
-        then:
-        busy()
-        
-        when:
-        completeBuild()
-        
-        then:
-        idle()
-
-        when:
-        startBuild()
-        waitForBuildToWait()
-
-        then:
-        busy()
-    }
-
     def "a new daemon is started if all existing are busy"() {
         when:
         startBuild()
@@ -290,121 +263,7 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
         stopped()
     }
 
-    def "sending stop to busy daemons causes them to disappear from the registry"() {
-        when:
-        startBuild()
-
-        then:
-        busy()
-
-        when:
-        stopDaemons()
-
-        then:
-        stopped()
-    }
-
-    def "sending stop to busy daemons cause them to disappear from the registry and disconnect from the client, and terminates the daemon process"() {
-        when:
-        startForegroundDaemon()
-
-        then:
-        idle()
-
-        when:
-        startBuild()
-        waitForBuildToWait()
-
-        then:
-        busy()
-
-        when:
-        stopDaemons()
-
-        then:
-        stopped() // just means the daemon has disappeared from the registry
-
-        then:
-        buildFailedWithDaemonDisappearedMessage()
-
-        and:
-        foregroundDaemonFailed()
-    }
-
-    @IgnoreIf({OperatingSystem.current().windows})
-    //(SF) On windows at the moment, we cannot reliably kill the client without waiting for the daemon to complete
-    //It's because of the way windows handles pipes for child processes.
-    //basically, process.waitFor() completes and you can get hold of the exit value,
-    //however, the process still sits there blocked on reading the child process' outputs.
-    //Next steps:
-    // 1. We can revisit this problem once we solve the daemon feedback story and we have a jna process starter that is able to consume the inputs
-    // 2. We can make this test working on java7 (because processbuilder in jre7 is more powerful)
-    def "tearing down client while daemon is building tears down daemon"() {
-        when:
-        startBuild()
-        waitForBuildToWait()
-
-        then:
-        busy()
-
-        when:
-        killBuild()
-
-        then:
-        stopped()
-    }
-
-    @IgnoreIf({OperatingSystem.current().windows})
-    //See the comment in the previous test
-    def "tearing down client while daemon is building tears down daemon _process_"() {
-        when:
-        startForegroundDaemon()
-
-        then:
-        idle()
-
-        when:
-        startBuild()
-        waitForBuildToWait()
-
-        then:
-        busy()
-
-        when:
-        killBuild()
-
-        then:
-        stopped() // just means the daemon has disappeared from the registry
-
-        and:
-        foregroundDaemonFailed()
-    }
-
-    def "tearing down daemon process produces nice error message for client"() {
-        when:
-        startForegroundDaemon()
-
-        then:
-        idle()
-
-        when:
-        startBuild()
-
-        then:
-        busy()
-
-        when:
-        disappearDaemon()
-
-        then:
-        buildFailedWithDaemonDisappearedMessage()
-
-        and:
-        // The daemon attempts to remove its address on shutdown
-        run { assert executer.daemonRegistry.all.empty }
-    }
-
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null})
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null})
     def "if a daemon exists but is using a different java home, a new compatible daemon will be created and used"() {
         when:
         startForegroundDaemonWithAlternateJavaHome()
@@ -414,7 +273,7 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
 
         and:
         foregroundDaemonContext {
-            assert javaHome == AvailableJavaHomes.bestAlternative
+            assert javaHome == AvailableJavaHomes.differentJdk.javaHome
         }
 
         when:
@@ -434,21 +293,6 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
         }
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null})
-    def "can stop a daemon that is using a different java home"() {
-        when:
-        startForegroundDaemonWithAlternateJavaHome()
-
-        then:
-        idle()
-
-        when:
-        stopDaemons()
-
-        then:
-        stopped()
-    }
-
     def "if a daemon exists but is using a file encoding, a new compatible daemon will be created and used"() {
         when:
         startBuild(null, "US-ASCII")
@@ -485,7 +329,8 @@ class DaemonLifecycleSpec extends DaemonIntegrationSpec {
     
     def cleanup() {
         try {
-            sequenceBuilder.build(executer.daemonRegistry).run()
+            def registry = new DaemonLogsAnalyzer(executer.daemonBaseDir).registry
+            sequenceBuilder.build(registry).run()
         } finally {
             stopDaemonsNow()
         }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonNativeServicesIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonNativeServicesIntegrationTest.groovy
new file mode 100644
index 0000000..1a856c2
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonNativeServicesIntegrationTest.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+class DaemonNativeServicesIntegrationTest extends DaemonIntegrationSpec {
+    def "native services use daemon base dir" () {
+        given:
+        executer.requireOwnGradleUserHomeDir()
+        def nativeDir = new File(executer.gradleUserHomeDir, "native")
+
+        expect:
+        !nativeDir.exists()
+
+        when:
+        executer.withArguments("-q").run()
+
+        then:
+        nativeDir.directory
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonOutputToggleIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonOutputToggleIntegrationTest.groovy
new file mode 100644
index 0000000..b8c0df8
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonOutputToggleIntegrationTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.gradle.launcher.daemon.server.exec.LogToClient
+
+class DaemonOutputToggleIntegrationTest extends DaemonIntegrationSpec {
+
+    def "output is received when toggle is off"() {
+        when:
+        run "help"
+
+        then:
+        !result.output.empty
+    }
+
+    def "output is not received when toggle is on"() {
+        when:
+        executer.withArgument("-D$LogToClient.DISABLE_OUTPUT=true").noExtraLogging()
+        run "help"
+
+        then:
+        result.output.empty
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPerformanceMonitoringIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPerformanceMonitoringIntegrationTest.groovy
new file mode 100644
index 0000000..76cecba
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPerformanceMonitoringIntegrationTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.launcher.daemon
+
+import org.gradle.launcher.daemon.server.health.DaemonStatus
+
+class DaemonPerformanceMonitoringIntegrationTest extends DaemonIntegrationSpec {
+
+    def setup() {
+        executer
+                .requireIsolatedDaemons()
+                .withGradleOpts("-D${DaemonStatus.EXPIRE_AT_PROPERTY}=80")
+    }
+
+    def "when build leaks more than available memory the daemon is expired eagerly"() {
+        expect:
+        daemonIsExpiredEagerly("-Xmx30m")
+    }
+
+    def "when build leaks within available memory the daemon is not expired"() {
+        expect:
+        !daemonIsExpiredEagerly("-Xmx500m")
+    }
+
+    private boolean daemonIsExpiredEagerly(String xmx) {
+        file("gradle.properties") << ("org.gradle.jvmargs=$xmx"
+                + " -Dorg.gradle.daemon.performance.logging=true")
+
+        setupLeakyBuild()
+        int newDaemons = 0
+        for (int i = 0; i < 10; i++) {
+            def r = run()
+            if (r.output.contains("Starting build in new daemon [memory: ")) {
+                newDaemons++;
+            }
+            if (newDaemons > 1) {
+                return true
+            }
+        }
+        return false
+    }
+
+    private void setupLeakyBuild() {
+        executer.noExtraLogging()
+
+        buildFile << """
+            class State {
+                static int x
+                static map = [:]
+            }
+            State.x++
+
+            //simulate the leak
+            (State.x * 1000).times {
+                State.map.put(it, "foo" * 300)
+            }
+
+            println "Build: " + State.x
+        """
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy
new file mode 100644
index 0000000..793ac5b
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+class DaemonReuseIntegrationTest extends DaemonIntegrationSpec {
+
+    def "idle daemon is reused in preference to starting a new daemon"() {
+        given:
+        executer.requireIsolatedDaemons()
+        executer.run()
+        daemons.daemon.assertIdle()
+
+        when:
+        5.times {
+            executer.run()
+        }
+
+        then:
+        daemons.daemons.size() == 1
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonStartupMessageIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonStartupMessageIntegrationTest.groovy
new file mode 100644
index 0000000..b8c26dd
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonStartupMessageIntegrationTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.launcher.daemon.client.DefaultDaemonConnector
+import spock.lang.IgnoreIf
+
+import static org.gradle.launcher.daemon.client.DefaultDaemonConnector.DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY
+
+ at IgnoreIf({ !GradleContextualExecuter.daemon })
+class DaemonStartupMessageIntegrationTest extends IsolatedDaemonSpec {
+
+    def setup() {
+        executer.withDaemonStartingMessageEnabled()
+    }
+
+    def "a message is logged when a new daemon is started"() {
+        when:
+        succeeds()
+
+        then:
+        output.contains DefaultDaemonConnector.STARTING_DAEMON_MESSAGE
+    }
+
+    def "the message is not shown when quiet log level is requested"() {
+        given:
+        executer.withArgument("-q")
+
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DefaultDaemonConnector.STARTING_DAEMON_MESSAGE)
+    }
+
+    def "daemon starting message can be disabled"() {
+        given:
+        executer.withGradleOpts("-D${DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY}=true")
+
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DefaultDaemonConnector.STARTING_DAEMON_MESSAGE)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonUsageSuggestionIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonUsageSuggestionIntegrationTest.groovy
new file mode 100644
index 0000000..1694da4
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonUsageSuggestionIntegrationTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.util.Requires
+import spock.lang.IgnoreIf
+
+import static org.gradle.util.TestPrecondition.WINDOWS
+
+class DaemonUsageSuggestionIntegrationTest extends AbstractIntegrationSpec {
+
+    public static final String DAEMON_USAGE_SUGGESTION_MESSAGE = "This build could be faster, please consider using the Gradle Daemon"
+
+    @IgnoreIf({ GradleContextualExecuter.longLivingProcess || WINDOWS.fulfilled })
+    def "prints a suggestion to use the daemon when daemon usage has not been explicitly configured"() {
+        when:
+        succeeds()
+
+        then:
+        output.contains DAEMON_USAGE_SUGGESTION_MESSAGE
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.longLivingProcess || WINDOWS.fulfilled })
+    def "does not print the suggestion to use the daemon if the daemon is explicitly disabled"() {
+        given:
+        executer.withArguments("--no-daemon")
+
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DAEMON_USAGE_SUGGESTION_MESSAGE)
+    }
+
+    @IgnoreIf({ !GradleContextualExecuter.longLivingProcess || WINDOWS.fulfilled })
+    def "does not print the suggestion to use the daemon when running as part of a long living process"() {
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DAEMON_USAGE_SUGGESTION_MESSAGE)
+    }
+
+    @Requires(WINDOWS)
+    def "does not print suggestion to use the daemon when on windows even if daemon usage has not been explicitly configured"() {
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DAEMON_USAGE_SUGGESTION_MESSAGE)
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/EmbeddedDaemonSmokeTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/EmbeddedDaemonSmokeTest.groovy
deleted file mode 100644
index c4652d8..0000000
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/EmbeddedDaemonSmokeTest.groovy
+++ /dev/null
@@ -1,71 +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.launcher.daemon
-
-import org.gradle.api.logging.LogLevel
-import org.gradle.configuration.GradleLauncherMetaData
-import org.gradle.launcher.daemon.client.DaemonClient
-import org.gradle.launcher.daemon.client.EmbeddedDaemonClientServices
-import org.gradle.launcher.exec.DefaultBuildActionParameters
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.tooling.internal.provider.ConfiguringBuildAction
-import org.junit.Rule
-import spock.lang.Specification
-
-/**
- * Exercises the basic mechanics using an embedded daemon.
- * 
- * @todo Test stdio (what should println do in the daemon threads?)
- * @todo launching multiple embedded daemons with the same registry
- */
-class EmbeddedDaemonSmokeTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp
-
-    def daemonClientServices = new EmbeddedDaemonClientServices()
-
-    def "run build"() {
-        given:
-        def action = new ConfiguringBuildAction(projectDirectory: temp.testDirectory, searchUpwards: false, tasks: ['echo'],
-                gradleUserHomeDir: temp.createDir("user-home"), action: new ExecuteBuildAction())
-        def parameters = new DefaultBuildActionParameters(new GradleLauncherMetaData(), new Date().time, System.properties, System.getenv(), temp.testDirectory, LogLevel.LIFECYCLE)
-        
-        and:
-        def outputFile = temp.file("output.txt")
-        
-        expect:
-        !outputFile.exists()
-        
-        and:
-        temp.file("build.gradle") << """
-            task echo << {
-                file("output.txt").write "Hello!"
-            }
-        """
-        
-        when:
-        daemonClientServices.get(DaemonClient).execute(action, parameters)
-        
-        then:
-        outputFile.exists() && outputFile.text == "Hello!"
-    }
-    
-    def cleanup() {
-        // TODO:ADAM - switch this back on
-//        daemonClientServices?.close()
-    }
-
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ExecuteBuildAction.java b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ExecuteBuildAction.java
deleted file mode 100644
index e2d9871..0000000
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ExecuteBuildAction.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.launcher.daemon;
-
-import org.gradle.initialization.BuildAction;
-import org.gradle.initialization.BuildController;
-
-import java.io.Serializable;
-
-public class ExecuteBuildAction implements BuildAction<Void>, Serializable {
-    public Void run(BuildController buildController) {
-        buildController.run();
-        return null;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/IsolatedDaemonSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/IsolatedDaemonSpec.groovy
new file mode 100644
index 0000000..1668bf7
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/IsolatedDaemonSpec.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
+import org.gradle.launcher.daemon.testing.DaemonsFixture
+
+abstract class IsolatedDaemonSpec extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer.requireIsolatedDaemons()
+    }
+
+    @Override
+    protected void cleanupWhileTestFilesExist() {
+        daemons.killAll()
+    }
+
+    DaemonsFixture getDaemons() {
+        new DaemonLogsAnalyzer(executer.daemonBaseDir)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/LocaleSupportDaemonIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/LocaleSupportDaemonIntegrationTest.groovy
new file mode 100644
index 0000000..0abd23b
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/LocaleSupportDaemonIntegrationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.apache.commons.lang.LocaleUtils
+import spock.lang.Issue
+
+ at Issue("https://issues.gradle.org/browse/GRADLE-3142")
+class LocaleSupportDaemonIntegrationTest extends DaemonIntegrationSpec {
+
+    def locales = [
+            LocaleUtils.toLocale("es_MX"),
+            LocaleUtils.toLocale("ja_JP"),
+            LocaleUtils.toLocale("hr_HR")
+    ].findAll {
+        it != Locale.default
+    }
+
+    def "custom locale is applied to daemon"() {
+
+        buildScript """
+            task printLocale {
+                doFirst {
+                    println "defaultLocale: " + Locale.default
+                }
+            }
+        """
+
+        when:
+        runWithLocale locales[0]
+
+        then:
+        ranWithLocale locales[0]
+        daemons.daemons.size() == 1
+
+        when:
+        runWithLocale locales[1]
+
+        then:
+        ranWithLocale locales[1]
+        daemons.daemons.size() == 2
+    }
+
+    def "locale is restored after build"() {
+        def startLocale = locales[0]
+        def changeLocale = locales[1]
+
+        buildScript """
+            task printLocale {
+                doFirst {
+                    Locale.setDefault(new Locale("$changeLocale.language", "$changeLocale.country", "$changeLocale.variant"))
+                    println "defaultLocale: " + Locale.default
+                }
+            }
+        """
+
+        when:
+        runWithLocale startLocale
+
+        then:
+        ranWithLocale changeLocale
+        daemons.daemons.size() == 1
+
+        when:
+        runWithLocale startLocale
+
+        then:
+        ranWithLocale changeLocale
+        daemons.daemons.size() == 1
+    }
+
+    void ranWithLocale(Locale locale) {
+        assert result.output.contains("defaultLocale: " + locale)
+    }
+
+    void runWithLocale(Locale locale) {
+        executer.withDefaultLocale(locale)
+        run "printLocale"
+    }
+
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy
new file mode 100644
index 0000000..c2e36f5
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon
+
+import org.gradle.launcher.daemon.client.DaemonDisappearedException
+import org.gradle.launcher.daemon.logging.DaemonMessages
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+class ProcessCrashHandlingIntegrationTest extends DaemonIntegrationSpec {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "tears down the daemon process when the client disconnects"() {
+        buildFile << """
+task block << {
+    new URL("$server.uri").text
+}
+"""
+
+        when:
+        def build = executer.withTasks("block").start()
+        server.waitFor()
+        daemons.daemon.assertBusy()
+        build.abort().waitForFailure()
+
+        then:
+        daemons.daemon.stops()
+    }
+
+    def "client logs useful information when daemon crashes"() {
+        buildFile << """
+task block << {
+    new URL("$server.uri").text
+}
+"""
+
+        when:
+        def build = executer.withTasks("block").start()
+        server.waitFor()
+        daemons.daemon.kill()
+        def failure = build.waitForFailure()
+
+        then:
+        failure.error.contains("----- Last  20 lines from daemon log file")
+        failure.assertHasDescription(DaemonDisappearedException.MESSAGE)
+    }
+
+    def "client logs useful information when daemon exits"() {
+        given:
+        file("build.gradle") << "System.exit(0)"
+
+        when:
+        def failure = executer.runWithFailure()
+
+        then:
+        failure.error.contains("----- Last  20 lines from daemon log file")
+        failure.error.contains(DaemonMessages.DAEMON_VM_SHUTTING_DOWN)
+        failure.assertHasDescription(DaemonDisappearedException.MESSAGE)
+
+        and:
+        daemons.daemon.stops()
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/SingleUseDaemonIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/SingleUseDaemonIntegrationTest.groovy
index 5a193d2..77e3d22 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/SingleUseDaemonIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/SingleUseDaemonIntegrationTest.groovy
@@ -19,16 +19,16 @@ package org.gradle.launcher.daemon
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.launcher.daemon.client.DefaultDaemonConnector
 import org.gradle.launcher.daemon.client.SingleUseDaemonClient
-import org.gradle.util.TextUtil
-import org.spockframework.runtime.SpockAssertionError
-import org.spockframework.runtime.SpockTimeoutError
+import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
+import org.gradle.util.GradleVersion
 import spock.lang.IgnoreIf
-import spock.util.concurrent.PollingConditions
+
+import java.nio.charset.Charset
 
 @IgnoreIf({ GradleContextualExecuter.isDaemon() })
 class SingleUseDaemonIntegrationTest extends AbstractIntegrationSpec {
-    PollingConditions pollingConditions = new PollingConditions()
 
     def setup() {
         // Need forking executer
@@ -49,20 +49,7 @@ class SingleUseDaemonIntegrationTest extends AbstractIntegrationSpec {
         wasForked()
 
         and:
-        noDaemonsRunning()
-    }
-
-    protected void noDaemonsRunning() {
-        // Because of GRADLE-2630, we need to use a spin assert here
-        // This should be removed when this bug is fixed.
-        try {
-            pollingConditions.eventually {
-                executer.getDaemonRegistry().all.empty
-            }
-        } catch (SpockTimeoutError e) {
-            // Spock swallows the inner exception, this is just to give a more helpful error message
-            throw new SpockAssertionError("The daemon registry is not empty after timeout (means daemons are still running)", e)
-        }
+        daemons.daemon.stops()
     }
 
     def "stops single use daemon when build fails"() {
@@ -78,23 +65,23 @@ class SingleUseDaemonIntegrationTest extends AbstractIntegrationSpec {
         failureHasCause "bad"
 
         and:
-        noDaemonsRunning()
+        daemons.daemon.stops()
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null })
     def "does not fork build if java home from gradle properties matches current process"() {
-        def alternateJavaHome = AvailableJavaHomes.bestAlternative
+        def javaHome = AvailableJavaHomes.differentJdk.javaHome
 
-        file('gradle.properties') << "org.gradle.java.home=${TextUtil.escapeString(alternateJavaHome.canonicalPath)}"
+        file('gradle.properties').writeProperties("org.gradle.java.home": javaHome.canonicalPath)
 
         file('build.gradle') << "println 'javaHome=' + org.gradle.internal.jvm.Jvm.current().javaHome.absolutePath"
 
         when:
-        executer.withJavaHome(alternateJavaHome)
+        executer.withJavaHome(javaHome)
         succeeds()
 
         then:
-        !wasForked();
+        wasNotForked()
     }
 
     def "forks build to run when immutable jvm args set regardless of the environment"() {
@@ -127,7 +114,61 @@ assert System.getProperty('some-prop') == 'some-value'
         succeeds()
 
         and:
-        !wasForked()
+        wasNotForked()
+    }
+
+    @IgnoreIf({ AvailableJavaHomes.java5 == null })
+    def "fails when using Java 5 as the target JVM"() {
+        def java5 = AvailableJavaHomes.java5
+
+        file('gradle.properties').writeProperties("org.gradle.java.home": java5.javaHome.absolutePath)
+
+        when:
+        fails()
+
+        then:
+        failure.assertHasDescription("Gradle ${GradleVersion.current().version} requires Java 6 or later to run. Your build is currently configured to use Java 5.")
+    }
+
+    def "single use daemon is not used if immutable system property is set on command line with non different value"() {
+        def encoding = Charset.defaultCharset().name()
+
+        given:
+        buildScript """
+            task encoding {
+                doFirst { println "encoding = " + java.nio.charset.Charset.defaultCharset().name() }
+            }
+        """
+        when:
+        run "encoding", "-Dfile.encoding=$encoding"
+
+        then:
+        output.contains "encoding = $encoding"
+
+        and:
+        wasNotForked()
+    }
+
+    def "does not print suggestion to use the daemon for a single use daemon"() {
+        given:
+        requireJvmArg('-Xmx32m')
+
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DaemonUsageSuggestionIntegrationTest.DAEMON_USAGE_SUGGESTION_MESSAGE)
+    }
+
+    def "does not print daemon startup message for a single use daemon"() {
+        given:
+        requireJvmArg('-Xmx32m')
+
+        when:
+        succeeds()
+
+        then:
+        !output.contains(DefaultDaemonConnector.STARTING_DAEMON_MESSAGE)
     }
 
     private def requireJvmArg(String jvmArg) {
@@ -138,7 +179,17 @@ assert System.getProperty('some-prop') == 'some-value'
         executer.withEnvironmentVars(["JAVA_OPTS": "$jvmArg -ea"])
     }
 
-    private def wasForked() {
-        result.output.contains(SingleUseDaemonClient.MESSAGE)
+    private void wasForked() {
+        assert result.output.contains(SingleUseDaemonClient.MESSAGE)
+        assert daemons.daemons.size() == 1
+    }
+
+    private void wasNotForked() {
+        assert !result.output.contains(SingleUseDaemonClient.MESSAGE)
+        assert daemons.daemons.size() == 0
+    }
+
+    private def getDaemons() {
+        return new DaemonLogsAnalyzer(executer.daemonBaseDir)
     }
 }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/StoppingDaemonIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/StoppingDaemonIntegrationSpec.groovy
index 48d8bb7..7262df8 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/StoppingDaemonIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/StoppingDaemonIntegrationSpec.groovy
@@ -16,20 +16,46 @@
 
 package org.gradle.launcher.daemon
 
+import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.launcher.daemon.logging.DaemonMessages
-import org.gradle.test.fixtures.ConcurrentTestUtil
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
 import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.IgnoreIf
+
 class StoppingDaemonIntegrationSpec extends DaemonIntegrationSpec {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    def "daemon process exits and client logs nice error message when daemon stopped"() {
+        buildFile << """
+task block << {
+    new URL("$server.uri").text
+}
+"""
+
+        when:
+        def build = executer.withTasks("block").start()
+        server.waitFor()
+        daemons.daemon.assertBusy()
+        executer.withArguments("--stop").run()
+        def failure = build.waitForFailure()
+
+        then:
+        daemons.daemon.stops()
+        failure.assertHasDescription(DaemonStoppedException.MESSAGE)
+    }
+
     def "can handle multiple concurrent stop requests"() {
-        given:
-        file('build.gradle') << '''
-file('marker.txt') << 'waiting'
-Thread.sleep(60000)
-'''
+        buildFile << """
+task block << {
+    new URL("$server.uri").text
+}
+"""
 
         when:
-        def build = executer.start()
-        ConcurrentTestUtil.poll(20) { assert file('marker.txt').file }
+        def build = executer.withTasks("block").start()
+        server.waitFor()
 
         def stopExecutions = []
         5.times { idx ->
@@ -40,9 +66,24 @@ Thread.sleep(60000)
         def out = executer.withArguments("--stop").run().output
 
         then:
+        daemons.daemon.stops()
         out.contains(DaemonMessages.NO_DAEMONS_RUNNING)
     }
 
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null})
+    def "can stop a daemon that is using a different java home"() {
+        given:
+        succeeds()
+        daemons.daemon.assertIdle()
+
+        when:
+        executer.withJavaHome(AvailableJavaHomes.differentJdk.javaHome)
+        executer.withArguments("--stop").run()
+
+        then:
+        daemons.daemon.stops()
+    }
+
     def "reports exact number of daemons stopped and keeps console output clean"() {
         given:
         executer.noExtraLogging()
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java
deleted file mode 100644
index c44a7c4..0000000
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.launcher.daemon.testing;
-
-import com.google.common.base.Splitter;
-import com.google.common.collect.Lists;
-import org.gradle.launcher.daemon.context.DaemonContext;
-import org.gradle.launcher.daemon.context.DefaultDaemonContext;
-
-import java.io.File;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DaemonContextParser {
-    public static DaemonContext parseFrom(String source) {
-        Pattern pattern = Pattern.compile("^.*DefaultDaemonContext\\[uid=([^\\n]+),javaHome=([^\\n]+),daemonRegistryDir=([^\\n]+),pid=([^\\n]+),idleTimeout=(.+?),daemonOpts=([^\\n]+)].*",
-                Pattern.MULTILINE + Pattern.DOTALL);
-        Matcher matcher = pattern.matcher(source);
-
-        if (matcher.matches()) {
-            String uid = matcher.group(1);
-            String javaHome = matcher.group(2);
-            String daemonRegistryDir = matcher.group(3);
-            String pidStr = matcher.group(4);
-            Long pid = pidStr.equals("null") ? null : Long.parseLong(pidStr);
-            Integer idleTimeout = Integer.decode(matcher.group(5));
-            List<String> jvmOpts = Lists.newArrayList(Splitter.on(',').split(matcher.group(6)));
-            return new DefaultDaemonContext(uid, new File(javaHome), new File(daemonRegistryDir), pid, idleTimeout, jvmOpts);
-        } else {
-            throw new IllegalStateException("unable to parse DefaultDaemonContext from source: [" + source + "].");
-        }
-    }
-}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy
deleted file mode 100644
index 943c49d..0000000
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy
+++ /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.launcher.daemon.testing
-
-import org.gradle.initialization.BuildLayoutParameters
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.launcher.daemon.client.DaemonClientServices
-import org.gradle.launcher.daemon.configuration.DaemonParameters
-import org.gradle.launcher.daemon.registry.DaemonRegistry
-import org.gradle.logging.LoggingServiceRegistry
-
-class DaemonLogsAnalyzer {
-
-    private List<File> daemonLogs
-    private File daemonBaseDir
-    private ServiceRegistry services
-
-    DaemonLogsAnalyzer(File daemonBaseDir) {
-        this.daemonBaseDir = daemonBaseDir
-        assert daemonBaseDir.listFiles().length == 1
-        def daemonFiles = daemonBaseDir.listFiles()[0].listFiles()
-        daemonLogs = daemonFiles.findAll { it.name.endsWith('.log') }
-        DaemonParameters daemonParameters = new DaemonParameters(new BuildLayoutParameters())
-        daemonParameters.setBaseDir(daemonBaseDir)
-        services = new DaemonClientServices(LoggingServiceRegistry.newEmbeddableLogging(), daemonParameters, new ByteArrayInputStream(new byte[0]))
-    }
-
-    List<TestableDaemon> getDaemons() {
-        return daemonLogs.collect { new TestableDaemon(it, registry) }
-    }
-
-    TestableDaemon getDaemon() {
-        def daemons = getDaemons()
-        assert daemons.size() == 1: "Expected only a single daemon."
-        daemons[0]
-    }
-
-    DaemonRegistry getRegistry() {
-        services.get(DaemonRegistry)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsEventSequence.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsEventSequence.groovy
index 5cf7090..c737d7a 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsEventSequence.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsEventSequence.groovy
@@ -82,7 +82,7 @@ class DaemonsEventSequence implements Stoppable, Runnable {
                     checkForDaemonsStateChange()
                     sleep(pollRegistryMs)
                 }
-                putOnChangeQueue(null) // sentinel that no more is comming
+                putOnChangeQueue(null) // sentinel that no more is coming
             } catch (Exception e) {
                 e.printStackTrace()
             }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy
deleted file mode 100644
index 0eb3065..0000000
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy
+++ /dev/null
@@ -1,116 +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.launcher.daemon.testing
-
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.launcher.daemon.context.DaemonContext
-import org.gradle.launcher.daemon.logging.DaemonMessages
-import org.gradle.launcher.daemon.registry.DaemonRegistry
-import org.gradle.process.internal.ExecHandleBuilder
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-class TestableDaemon {
-
-    final DaemonContext context
-    final String logContent
-    private final DaemonRegistry registry
-
-    TestableDaemon(File daemonLog, DaemonRegistry registry) {
-        this.logContent = daemonLog.text
-        this.context = DaemonContextParser.parseFrom(logContent)
-        this.registry = registry
-    }
-
-    void waitUntilIdle() {
-        def expiry = System.currentTimeMillis() + 20000
-        while (expiry > System.currentTimeMillis()) {
-            if (registry.idle.find { it.context.pid == context.pid } != null) {
-                return
-            }
-            Thread.sleep(200)
-        }
-        throw new AssertionError("Timeout waiting for daemon with pid ${context.pid} to become idle.")
-    }
-
-    void kill() {
-        println "Killing daemon with pid: $context.pid"
-        def output = new ByteArrayOutputStream()
-        def e = new ExecHandleBuilder()
-                .commandLine(killArgs(context.pid))
-                .redirectErrorStream()
-                .setStandardOutput(output)
-                .workingDir(new File(".").absoluteFile) //does not matter
-                .build()
-        e.start()
-        def result = e.waitForFinish()
-        result.rethrowFailure()
-    }
-
-    private static Object[] killArgs(Long pid) {
-        if (pid == null) {
-            throw new RuntimeException("Unable to force kill the daemon because provided pid is null!")
-        }
-        if (OperatingSystem.current().isUnix()) {
-            return ["kill", "-9", pid]
-        } else if (OperatingSystem.current().isWindows()) {
-            return ["taskkill.exe", "/F", "/T", "/PID", pid]
-        }
-        else {
-            throw new RuntimeException("This implementation does not know how to forcefully kill the daemon on os: " + OperatingSystem.current())
-        }
-    }
-
-    enum State {
-        busy, idle
-    }
-
-    boolean isIdle() {
-        getStates()[-1] == State.idle
-    }
-
-    boolean isBusy() {
-        !isIdle()
-    }
-
-    List<State> getStates() {
-        def states = new LinkedList<State>()
-        logContent.eachLine {
-            if (it.contains(DaemonMessages.DAEMON_IDLE)) {
-                states << State.idle
-            } else if (it.contains(DaemonMessages.DAEMON_BUSY)) {
-                states << State.busy
-            }
-        }
-        states
-    }
-
-    int getPort() {
-        Pattern pattern = Pattern.compile("^.*" + DaemonMessages.ADVERTISING_DAEMON + ".*port:(\\d+).*",
-                Pattern.MULTILINE + Pattern.DOTALL);
-
-        Matcher matcher = pattern.matcher(logContent);
-        assert matcher.matches(): "Unable to find daemon address in the daemon log. Daemon: $context"
-
-        try {
-            return Integer.parseInt(matcher.group(1))
-        } catch (NumberFormatException e) {
-            throw new RuntimeException("Unexpected format of the port number found in the daemon log. Daemon: $context")
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/Main.java b/subprojects/launcher/src/main/java/org/gradle/launcher/Main.java
index 001bead..478bdb9 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/Main.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/Main.java
@@ -25,24 +25,11 @@ import java.util.Arrays;
  * The main command-line entry-point for Gradle.
  */
 public class Main extends EntryPoint {
-
-    private final String[] args;
-    
-    public Main(String[] args) {
-        this.args = args;
-    }
-
     public static void main(String[] args) {
-        try {
-            new Main(args).run();
-            System.exit(0);
-        } catch (Throwable throwable) {
-            throwable.printStackTrace();
-            System.exit(1);
-        }
+        new Main().run(args);
     }
 
-    protected void doAction(ExecutionListener listener) {
+    protected void doAction(String[] args, ExecutionListener listener) {
         createActionFactory().convert(Arrays.asList(args)).execute(listener);
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/EntryPoint.java b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/EntryPoint.java
index a2d6fe2..08a953f 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/EntryPoint.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/EntryPoint.java
@@ -34,15 +34,15 @@ import org.gradle.logging.internal.StreamingStyledTextOutputFactory;
  * by subclasses as they define our entry point behaviour, but they are protected to enable
  * testing as it's difficult to test something that will call System.exit().
  */
-abstract public class EntryPoint implements Runnable {
+abstract public class EntryPoint {
 
     /**
      * Unless the createCompleter() method is overridden, the JVM will exit before returning from this method.
      */
-    public void run() {
+    public void run(String[] args) {
         RecordingExecutionListener listener = new RecordingExecutionListener();
         try {
-            doAction(listener);
+            doAction(args, listener);
         } catch (Throwable e) {
             createErrorHandler().execute(e);
             listener.onFailure(e);
@@ -65,7 +65,7 @@ abstract public class EntryPoint implements Runnable {
         return new BuildExceptionReporter(new StreamingStyledTextOutputFactory(System.err), new LoggingConfiguration(), new GradleLauncherMetaData());
     }
 
-    protected abstract void doAction(ExecutionListener listener);
+    protected abstract void doAction(String[] args, ExecutionListener listener);
 
     private static class RecordingExecutionListener implements ExecutionListener {
         private Throwable failure;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
index c147c81..83b3bd1 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
@@ -27,6 +27,9 @@ import org.gradle.internal.classloader.MutableURLClassLoader;
 import java.lang.reflect.Method;
 
 public class ProcessBootstrap {
+    /**
+     * Sets up the ClassLoader structure for the given class, creates an instance and invokes {@link EntryPoint#run(String[])} on it.
+     */
     public void run(String mainClassName, String[] args) {
         try {
             runNoExit(mainClassName, args);
@@ -46,7 +49,8 @@ public class ProcessBootstrap {
         ClassLoader runtimeClassLoader = new MutableURLClassLoader(antClassLoader, runtimeClasspath);
         Thread.currentThread().setContextClassLoader(runtimeClassLoader);
         Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
-        Method mainMethod = mainClass.getMethod("main", String[].class);
-        mainMethod.invoke(null, new Object[]{args});
+        Object entryPoint = mainClass.newInstance();
+        Method mainMethod = mainClass.getMethod("run", String[].class);
+        mainMethod.invoke(entryPoint, new Object[]{args});
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
index f4dbd76..018e319 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
@@ -17,34 +17,34 @@
 package org.gradle.launcher.cli;
 
 import org.gradle.StartParameter;
-import org.gradle.api.Action;
-import org.gradle.internal.Actions;
+import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
 import org.gradle.cli.SystemPropertiesCommandLineConverter;
 import org.gradle.configuration.GradleLauncherMetaData;
 import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.initialization.DefaultCommandLineConverter;
-import org.gradle.initialization.GradleLauncherFactory;
 import org.gradle.initialization.LayoutCommandLineConverter;
 import org.gradle.internal.SystemProperties;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.gradle.internal.service.scopes.GlobalScopeServices;
-import org.gradle.launcher.bootstrap.ExecutionListener;
-import org.gradle.launcher.cli.converter.*;
-import org.gradle.launcher.daemon.bootstrap.ForegroundDaemonMain;
+import org.gradle.launcher.cli.converter.DaemonCommandLineConverter;
+import org.gradle.launcher.cli.converter.LayoutToPropertiesConverter;
+import org.gradle.launcher.cli.converter.PropertiesToDaemonParametersConverter;
+import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter;
+import org.gradle.launcher.daemon.bootstrap.ForegroundDaemonAction;
 import org.gradle.launcher.daemon.client.DaemonClient;
-import org.gradle.launcher.daemon.client.DaemonClientServices;
-import org.gradle.launcher.daemon.client.SingleUseDaemonClientServices;
-import org.gradle.launcher.daemon.client.StopDaemonClientServices;
+import org.gradle.launcher.daemon.client.DaemonClientFactory;
+import org.gradle.launcher.daemon.client.DaemonClientGlobalServices;
+import org.gradle.launcher.daemon.client.DaemonStopClient;
 import org.gradle.launcher.daemon.configuration.CurrentProcess;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 import org.gradle.launcher.daemon.configuration.ForegroundDaemonConfiguration;
-import org.gradle.launcher.exec.BuildActionExecuter;
-import org.gradle.launcher.exec.BuildActionParameters;
-import org.gradle.launcher.exec.InProcessBuildActionExecuter;
+import org.gradle.launcher.exec.*;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.logging.internal.OutputEventListener;
 
 import java.lang.management.ManagementFactory;
 import java.util.HashMap;
@@ -101,7 +101,7 @@ class BuildActionsFactory implements CommandLineAction {
         parser.option(STOP).hasDescription("Stops the Gradle daemon if it is running.");
     }
 
-    public Action<? super ExecutionListener> createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
+    public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
         BuildLayoutParameters layout = new BuildLayoutParameters();
         layoutConverter.convert(commandLine, layout);
 
@@ -113,7 +113,7 @@ class BuildActionsFactory implements CommandLineAction {
         propertiesToStartParameterConverter.convert(properties, startParameter);
         commandLineConverter.convert(commandLine, startParameter);
 
-        DaemonParameters daemonParameters = new DaemonParameters(layout);
+        DaemonParameters daemonParameters = new DaemonParameters(layout, startParameter.getSystemPropertiesArgs());
         propertiesToDaemonParametersConverter.convert(properties, daemonParameters);
         daemonConverter.convert(commandLine, daemonParameters);
 
@@ -123,9 +123,9 @@ class BuildActionsFactory implements CommandLineAction {
         if (commandLine.hasOption(FOREGROUND)) {
             ForegroundDaemonConfiguration conf = new ForegroundDaemonConfiguration(
                     daemonParameters.getUid(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout());
-            return Actions.toAction(new ForegroundDaemonMain(conf));
+            return new ForegroundDaemonAction(loggingServices, conf);
         }
-        if (daemonParameters.isEnabled()) {
+        if (daemonParameters.getDaemonUsage().isEnabled()) {
             return runBuildWithDaemon(startParameter, daemonParameters, loggingServices);
         }
         if (canUseCurrentProcess(daemonParameters)) {
@@ -134,17 +134,19 @@ class BuildActionsFactory implements CommandLineAction {
         return runBuildInSingleUseDaemon(startParameter, daemonParameters, loggingServices);
     }
 
-    private Action<? super ExecutionListener> stopAllDaemons(DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
-        DaemonClientServices clientServices = new StopDaemonClientServices(loggingServices, daemonParameters, System.in);
-        DaemonClient stopClient = clientServices.get(DaemonClient.class);
-        return Actions.toAction(new StopDaemonAction(stopClient));
+    private Runnable stopAllDaemons(DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
+        ServiceRegistry clientSharedServices = createGlobalClientServices();
+        ServiceRegistry clientServices = clientSharedServices.get(DaemonClientFactory.class).createStopDaemonServices(loggingServices.get(OutputEventListener.class), daemonParameters);
+        DaemonStopClient stopClient = clientServices.get(DaemonStopClient.class);
+        return new StopDaemonAction(stopClient);
     }
 
-    private Action<? super ExecutionListener> runBuildWithDaemon(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
+    private Runnable runBuildWithDaemon(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
         // Create a client that will match based on the daemon startup parameters.
-        DaemonClientServices clientServices = new DaemonClientServices(loggingServices, daemonParameters, System.in);
+        ServiceRegistry clientSharedServices = createGlobalClientServices();
+        ServiceRegistry clientServices = clientSharedServices.get(DaemonClientFactory.class).createBuildClientServices(loggingServices.get(OutputEventListener.class), daemonParameters, System.in);
         DaemonClient client = clientServices.get(DaemonClient.class);
-        return daemonBuildAction(startParameter, daemonParameters, client);
+        return runBuild(startParameter, daemonParameters, client);
     }
 
     private boolean canUseCurrentProcess(DaemonParameters requiredBuildParameters) {
@@ -152,18 +154,21 @@ class BuildActionsFactory implements CommandLineAction {
         return currentProcess.configureForBuild(requiredBuildParameters);
     }
 
-    private Action<? super ExecutionListener> runBuildInProcess(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
+    private Runnable runBuildInProcess(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
         ServiceRegistry globalServices = ServiceRegistryBuilder.builder()
                 .displayName("Global services")
                 .parent(loggingServices)
                 .parent(NativeServices.getInstance())
                 .provider(new GlobalScopeServices(false))
                 .build();
-        InProcessBuildActionExecuter executer = new InProcessBuildActionExecuter(globalServices.get(GradleLauncherFactory.class));
-        return daemonBuildAction(startParameter, daemonParameters, executer);
+        InProcessBuildActionExecuter executer = globalServices.get(InProcessBuildActionExecuter.class);
+        StyledTextOutputFactory textOutputFactory = globalServices.get(StyledTextOutputFactory.class);
+        DocumentationRegistry documentationRegistry = globalServices.get(DocumentationRegistry.class);
+        DaemonUsageSuggestingBuildActionExecuter daemonUsageSuggestingExecuter = new DaemonUsageSuggestingBuildActionExecuter(executer, textOutputFactory, documentationRegistry);
+        return runBuild(startParameter, daemonParameters, daemonUsageSuggestingExecuter);
     }
 
-    private Action<? super ExecutionListener> runBuildInSingleUseDaemon(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
+    private Runnable runBuildInSingleUseDaemon(StartParameter startParameter, DaemonParameters daemonParameters, ServiceRegistry loggingServices) {
         //(SF) this is a workaround until this story is completed. I'm hardcoding setting the idle timeout to be max X mins.
         //this way we avoid potential runaway daemons that steal resources on linux and break builds on windows.
         //We might leave that in if we decide it's a good idea for an extra safety net.
@@ -174,14 +179,29 @@ class BuildActionsFactory implements CommandLineAction {
         //end of workaround.
 
         // Create a client that will not match any existing daemons, so it will always startup a new one
-        DaemonClientServices clientServices = new SingleUseDaemonClientServices(loggingServices, daemonParameters, System.in);
+        ServiceRegistry clientSharedServices = createGlobalClientServices();
+        ServiceRegistry clientServices = clientSharedServices.get(DaemonClientFactory.class).createSingleUseDaemonClientServices(loggingServices.get(OutputEventListener.class), daemonParameters, System.in);
         DaemonClient client = clientServices.get(DaemonClient.class);
-        return daemonBuildAction(startParameter, daemonParameters, client);
+        return runBuild(startParameter, daemonParameters, client);
     }
 
-    private Action<? super ExecutionListener> daemonBuildAction(StartParameter startParameter, DaemonParameters daemonParameters, BuildActionExecuter<BuildActionParameters> executer) {
-        return Actions.toAction(
-                new RunBuildAction(executer, startParameter, SystemProperties.getCurrentDir(), clientMetaData(), getBuildStartTime(), daemonParameters.getEffectiveSystemProperties(), System.getenv()));
+    private ServiceRegistry createGlobalClientServices() {
+        return ServiceRegistryBuilder.builder()
+                .displayName("Daemon client global services")
+                .parent(NativeServices.getInstance())
+                .provider(new GlobalScopeServices(false))
+                .provider(new DaemonClientGlobalServices())
+                .build();
+    }
+
+    private Runnable runBuild(StartParameter startParameter, DaemonParameters daemonParameters, BuildActionExecuter<BuildActionParameters> executer) {
+        BuildActionParameters parameters = new DefaultBuildActionParameters(
+                daemonParameters.getEffectiveSystemProperties(),
+                System.getenv(),
+                SystemProperties.getInstance().getCurrentDir(),
+                startParameter.getLogLevel(),
+                daemonParameters.getDaemonUsage());
+        return new RunBuildAction(executer, startParameter, clientMetaData(), getBuildStartTime(), parameters);
     }
 
     private long getBuildStartTime() {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineAction.java
index 82e6082..6e2bccb 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineAction.java
@@ -16,10 +16,8 @@
 
 package org.gradle.launcher.cli;
 
-import org.gradle.api.Action;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
-import org.gradle.launcher.bootstrap.ExecutionListener;
 
 public interface CommandLineAction {
     /**
@@ -31,5 +29,5 @@ public interface CommandLineAction {
      * Creates an executable action from the given command-line args. Returns null if this action was not selected by the given
      * command-line args.
      */
-    Action<? super ExecutionListener> createAction(CommandLineParser parser, ParsedCommandLine commandLine);
+    Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine);
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineActionFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineActionFactory.java
index 0dfcce3..53999da 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineActionFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/CommandLineActionFactory.java
@@ -15,20 +15,28 @@
  */
 package org.gradle.launcher.cli;
 
+import groovy.lang.GroovySystem;
+import org.apache.tools.ant.Main;
 import org.gradle.BuildExceptionReporter;
 import org.gradle.api.Action;
-import org.gradle.internal.Actions;
 import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineConverter;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
 import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.initialization.BuildLayoutParameters;
+import org.gradle.initialization.LayoutCommandLineConverter;
+import org.gradle.internal.Actions;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.nativeintegration.services.NativeServices;
+import org.gradle.internal.os.OperatingSystem;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.launcher.bootstrap.ExecutionListener;
 import org.gradle.logging.LoggingConfiguration;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.logging.internal.LoggingCommandLineConverter;
 import org.gradle.util.GradleVersion;
 
 import java.io.PrintStream;
@@ -57,7 +65,8 @@ public class CommandLineActionFactory {
 
         return new ExceptionReportingAction(
                 new WithLogging(loggingServices, args, loggingConfiguration,
-                        new ParseAndBuildAction(loggingServices, args)),
+                        new JavaRuntimeValidationAction(
+                            new ParseAndBuildAction(loggingServices, args))),
                 new BuildExceptionReporter(loggingServices.get(StyledTextOutputFactory.class), loggingConfiguration, clientMetaData()));
     }
 
@@ -90,12 +99,12 @@ public class CommandLineActionFactory {
             parser.option(VERSION, "version").hasDescription("Print version info.");
         }
 
-        public Action<? super ExecutionListener> createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
+        public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
             if (commandLine.hasOption(HELP)) {
-                return Actions.toAction(new ShowUsageAction(parser));
+                return new ShowUsageAction(parser);
             }
             if (commandLine.hasOption(VERSION)) {
-                return Actions.toAction(new ShowVersionAction());
+                return new ShowVersionAction();
             }
             return null;
         }
@@ -132,7 +141,28 @@ public class CommandLineActionFactory {
 
     private static class ShowVersionAction implements Runnable {
         public void run() {
-            System.out.println(GradleVersion.current().prettyPrint());
+            GradleVersion currentVersion = GradleVersion.current();
+
+            final StringBuilder sb = new StringBuilder();
+            sb.append("\n------------------------------------------------------------\nGradle ");
+            sb.append(currentVersion.getVersion());
+            sb.append("\n------------------------------------------------------------\n\nBuild time:   ");
+            sb.append(currentVersion.getBuildTime());
+            sb.append("\nBuild number: ");
+            sb.append(currentVersion.getBuildNumber());
+            sb.append("\nRevision:     ");
+            sb.append(currentVersion.getRevision());
+            sb.append("\n\nGroovy:       ");
+            sb.append(GroovySystem.getVersion());
+            sb.append("\nAnt:          ");
+            sb.append(Main.getAntVersion());
+            sb.append("\nJVM:          ");
+            sb.append(Jvm.current());
+            sb.append("\nOS:           ");
+            sb.append(OperatingSystem.current());
+            sb.append("\n");
+
+            System.out.println(sb.toString());
         }
     }
 
@@ -150,22 +180,28 @@ public class CommandLineActionFactory {
         }
 
         public void execute(ExecutionListener executionListener) {
-            CommandLineConverter<LoggingConfiguration> loggingConfigurationConverter = (CommandLineConverter<LoggingConfiguration>)loggingServices.get(CommandLineConverter.class);
+            CommandLineConverter<LoggingConfiguration> loggingConfigurationConverter = new LoggingCommandLineConverter();
+            CommandLineConverter<BuildLayoutParameters> buildLayoutConverter = new LayoutCommandLineConverter();
+            BuildLayoutParameters buildLayout = new BuildLayoutParameters();
             CommandLineParser parser = new CommandLineParser();
             loggingConfigurationConverter.configure(parser);
+            buildLayoutConverter.configure(parser);
             parser.allowUnknownOptions();
             parser.allowMixedSubcommandsAndOptions();
             try {
                 ParsedCommandLine parsedCommandLine = parser.parse(args);
                 loggingConfigurationConverter.convert(parsedCommandLine, loggingConfiguration);
+                buildLayoutConverter.convert(parsedCommandLine, buildLayout);
             } catch (CommandLineArgumentException e) {
-                // Ignore
+                // Ignore, deal with this problem later
             }
 
             LoggingManagerInternal loggingManager = loggingServices.getFactory(LoggingManagerInternal.class).create();
             loggingManager.setLevel(loggingConfiguration.getLogLevel());
             loggingManager.start();
-            loggingManager.attachConsole(loggingConfiguration.isColorOutput());
+
+            NativeServices.initialize(buildLayout.getGradleUserHomeDir());
+            loggingManager.attachProcessConsole(loggingConfiguration.getConsoleOutput());
 
             action.execute(executionListener);
         }
@@ -203,9 +239,9 @@ public class CommandLineActionFactory {
 
         private Action<? super ExecutionListener> createAction(Iterable<CommandLineAction> factories, CommandLineParser parser, ParsedCommandLine commandLine) {
             for (CommandLineAction factory : factories) {
-                Action<? super ExecutionListener> action = factory.createAction(parser, commandLine);
+                Runnable action = factory.createAction(parser, commandLine);
                 if (action != null) {
-                    return action;
+                    return Actions.toAction(action);
                 }
             }
             throw new UnsupportedOperationException("No action factory for specified command-line arguments.");
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/ExecuteBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/ExecuteBuildAction.java
index 078237d..841555a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/ExecuteBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/ExecuteBuildAction.java
@@ -16,21 +16,19 @@
 package org.gradle.launcher.cli;
 
 import org.gradle.StartParameter;
-import org.gradle.initialization.BuildAction;
-import org.gradle.initialization.BuildController;
+import org.gradle.internal.invocation.BuildAction;
 
 import java.io.Serializable;
 
-public class ExecuteBuildAction implements BuildAction<Void>, Serializable {
+public class ExecuteBuildAction implements BuildAction, Serializable {
     private final StartParameter startParameter;
 
     public ExecuteBuildAction(StartParameter startParameter) {
         this.startParameter = startParameter;
     }
 
-    public Void run(BuildController buildController) {
-        buildController.setStartParameter(startParameter);
-        buildController.run();
-        return null;
+    @Override
+    public StartParameter getStartParameter() {
+        return startParameter;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/GuiActionsFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/GuiActionsFactory.java
index 93abb47..09d6fd1 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/GuiActionsFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/GuiActionsFactory.java
@@ -16,12 +16,9 @@
 
 package org.gradle.launcher.cli;
 
-import org.gradle.api.Action;
-import org.gradle.internal.Actions;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
 import org.gradle.gradleplugin.userinterface.swing.standalone.BlockingApplication;
-import org.gradle.launcher.bootstrap.ExecutionListener;
 
 class GuiActionsFactory implements CommandLineAction {
     private static final String GUI = "gui";
@@ -30,9 +27,9 @@ class GuiActionsFactory implements CommandLineAction {
         parser.option(GUI).hasDescription("Launches the Gradle GUI.");
     }
 
-    public Action<? super ExecutionListener> createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
+    public Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine) {
         if (commandLine.hasOption(GUI)) {
-            return Actions.toAction(new ShowGuiAction());
+            return new ShowGuiAction();
         }
         return null;
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/JavaRuntimeValidationAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/JavaRuntimeValidationAction.java
new file mode 100644
index 0000000..74f5d54
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/JavaRuntimeValidationAction.java
@@ -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.launcher.cli;
+
+import org.gradle.api.Action;
+import org.gradle.api.JavaVersion;
+import org.gradle.internal.jvm.UnsupportedJavaRuntimeException;
+import org.gradle.launcher.bootstrap.ExecutionListener;
+
+public class JavaRuntimeValidationAction implements Action<ExecutionListener> {
+    private final Action<? super ExecutionListener> action;
+
+    public JavaRuntimeValidationAction(Action<? super ExecutionListener> action) {
+        this.action = action;
+    }
+
+    public void execute(ExecutionListener executionListener) {
+        if (!JavaVersion.current().isJava6Compatible()) {
+            throw UnsupportedJavaRuntimeException.usingUnsupportedVersion("Gradle", JavaVersion.VERSION_1_6);
+        }
+        action.execute(executionListener);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
index 4b98551..a4cab50 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/RunBuildAction.java
@@ -16,39 +16,30 @@
 package org.gradle.launcher.cli;
 
 import org.gradle.StartParameter;
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.launcher.exec.BuildActionParameters;
-import org.gradle.launcher.exec.DefaultBuildActionParameters;
+import org.gradle.initialization.*;
 import org.gradle.launcher.exec.BuildActionExecuter;
-import org.gradle.util.GUtil;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
+import org.gradle.launcher.exec.BuildActionParameters;
 
 public class RunBuildAction implements Runnable {
     private final BuildActionExecuter<BuildActionParameters> executer;
     private final StartParameter startParameter;
-    private final File currentDir;
     private final BuildClientMetaData clientMetaData;
     private final long startTime;
-    private final Map<String, String> systemProperties;
-    private final Map<String, String> envVariables;
+    private final BuildActionParameters buildActionParameters;
 
-    public RunBuildAction(BuildActionExecuter<BuildActionParameters> executer, StartParameter startParameter, File currentDir, BuildClientMetaData clientMetaData, long startTime, Map<?, ?> systemProperties, Map<String, String> envVariables) {
+    public RunBuildAction(BuildActionExecuter<BuildActionParameters> executer, StartParameter startParameter, BuildClientMetaData clientMetaData, long startTime,
+                          BuildActionParameters buildActionParameters) {
         this.executer = executer;
         this.startParameter = startParameter;
-        this.currentDir = currentDir;
         this.clientMetaData = clientMetaData;
         this.startTime = startTime;
-        this.systemProperties = new HashMap<String, String>();
-        GUtil.addToMap(this.systemProperties, systemProperties);
-        this.envVariables = envVariables;
+        this.buildActionParameters = buildActionParameters;
     }
 
     public void run() {
         executer.execute(
                 new ExecuteBuildAction(startParameter),
-                new DefaultBuildActionParameters(clientMetaData, startTime, systemProperties, envVariables, currentDir, startParameter.getLogLevel()));
+                new DefaultBuildRequestContext(new DefaultBuildRequestMetaData(clientMetaData, startTime), new FixedBuildCancellationToken(), new NoOpBuildEventConsumer()),
+                buildActionParameters);
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/StopDaemonAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/StopDaemonAction.java
index f5a90bb..046adcf 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/StopDaemonAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/StopDaemonAction.java
@@ -15,12 +15,12 @@
  */
 package org.gradle.launcher.cli;
 
-import org.gradle.launcher.daemon.client.DaemonClient;
+import org.gradle.launcher.daemon.client.DaemonStopClient;
 
 public class StopDaemonAction implements Runnable {
-    private final DaemonClient client;
+    private final DaemonStopClient client;
 
-    public StopDaemonAction(DaemonClient client) {
+    public StopDaemonAction(DaemonStopClient client) {
         this.client = client;
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/DaemonCommandLineConverter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/DaemonCommandLineConverter.java
index 80166cf..a5ce78c 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/DaemonCommandLineConverter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/DaemonCommandLineConverter.java
@@ -20,7 +20,6 @@ import org.gradle.cli.AbstractCommandLineConverter;
 import org.gradle.cli.CommandLineArgumentException;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
-import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 
 public class DaemonCommandLineConverter extends AbstractCommandLineConverter<DaemonParameters> {
@@ -28,10 +27,6 @@ public class DaemonCommandLineConverter extends AbstractCommandLineConverter<Dae
     private static final String DAEMON = "daemon";
     private static final String NO_DAEMON = "no-daemon";
 
-    protected DaemonParameters newInstance() {
-        return new DaemonParameters(new BuildLayoutParameters());
-    }
-
     public DaemonParameters convert(ParsedCommandLine args, DaemonParameters target) throws CommandLineArgumentException {
         if (args.hasOption(NO_DAEMON)) {
             return target.setEnabled(false);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
index cc31c85..c30d2c9 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
@@ -16,6 +16,8 @@
 
 package org.gradle.launcher.cli.converter;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps;
 import org.gradle.api.Project;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.initialization.BuildLayoutParameters;
@@ -26,14 +28,20 @@ import org.gradle.launcher.daemon.configuration.GradleProperties;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.Serializable;
 import java.util.Map;
 import java.util.Properties;
 
 public class LayoutToPropertiesConverter {
     public Map<String, String> convert(BuildLayoutParameters layout, Map<String, String> properties) {
-        configureFromBuildDir(layout.getProjectDir(), layout.getSearchUpwards(), properties);
+        configureFromBuildDir(layout.getSearchDir(), layout.getSearchUpwards(), properties);
         configureFromGradleUserHome(layout.getGradleUserHomeDir(), properties);
-        properties.putAll((Map) System.getProperties());
+        properties.putAll(Maps.filterEntries((Map) System.getProperties(), new Predicate<Map.Entry<?, ?>>() {
+            public boolean apply(Map.Entry<?, ?> input) {
+                return input.getKey() instanceof Serializable
+                        && (input.getValue() instanceof Serializable || input.getValue() == null);
+            }
+        }));
         return properties;
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverter.java
index 33c425b..e4b55e4 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverter.java
@@ -62,7 +62,10 @@ public class PropertiesToDaemonParametersConverter {
             target.setBaseDir(new File(prop));
         }
 
-        target.setEnabled(isTrue(properties.get(DAEMON_ENABLED_PROPERTY)));
+        String daemonEnabledPropertyValue = properties.get(DAEMON_ENABLED_PROPERTY);
+        if (daemonEnabledPropertyValue != null) {
+            target.setEnabled(isTrue(daemonEnabledPropertyValue));
+        }
         target.setDebug(isTrue(properties.get(DEBUG_MODE_PROPERTY)));
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverter.java
index 361b87c..c67d809 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverter.java
@@ -29,8 +29,26 @@ public class PropertiesToStartParameterConverter {
 
         String parallel = properties.get(GradleProperties.PARALLEL_PROPERTY);
         if (isTrue(parallel)) {
-            startParameter.setParallelThreadCount(-1);
+            startParameter.setParallelProjectExecutionEnabled(true);
         }
+
+        String workers = properties.get(GradleProperties.WORKERS_PROPERTY);
+        if (workers != null) {
+            try {
+                int workerCount = Integer.parseInt(workers);
+                if (workerCount < 1) {
+                    invalidMaxWorkersPropValue(workers);
+                }
+                startParameter.setMaxWorkerCount(workerCount);
+            } catch (NumberFormatException e) {
+                invalidMaxWorkersPropValue(workers);
+            }
+        }
+
         return startParameter;
     }
+
+    private StartParameter invalidMaxWorkersPropValue(String value) {
+        throw new IllegalArgumentException(String.format("Value '%s' given for %s system property is invalid (must be a positive, non-zero, integer)", value, GradleProperties.WORKERS_PROPERTY));
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonGreeter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonGreeter.java
index eb49e9b..d0c6340 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonGreeter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonGreeter.java
@@ -18,7 +18,7 @@ package org.gradle.launcher.daemon.bootstrap;
 
 import org.gradle.api.GradleException;
 import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
+import org.gradle.launcher.daemon.diagnostics.DaemonStartupInfo;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.process.ExecResult;
 
@@ -29,31 +29,25 @@ public class DaemonGreeter {
         this.documentationRegistry = documentationRegistry;
     }
 
-    public DaemonDiagnostics parseDaemonOutput(String output, ExecResult result) {
-        if (!new DaemonStartupCommunication().containsGreeting(output)) {
+    public DaemonStartupInfo parseDaemonOutput(String output, ExecResult result) {
+        DaemonStartupCommunication startupCommunication = new DaemonStartupCommunication();
+        if (!startupCommunication.containsGreeting(output)) {
             throw new GradleException(prepareMessage(output, result));
         }
         String[] lines = output.split("\n");
-        //TODO SF don't assume it is the last line
+        //Assuming that the diagnostics were printed out to the last line. It's not bullet-proof but seems to be doing fine.
         String lastLine = lines[lines.length-1];
-        return new DaemonStartupCommunication().readDiagnostics(lastLine);
+        return startupCommunication.readDiagnostics(lastLine);
     }
 
     private String prepareMessage(String output, ExecResult result) {
         StringBuilder sb = new StringBuilder();
         sb.append(DaemonMessages.UNABLE_TO_START_DAEMON);
-        //TODO SF if possible, include the exit value.
-//        if (result.getExitValue()) {
-//            sb.append("\nThe process has exited with value: ");
-//            sb.append(result.getExecResult().getExitValue()).append(".");
-//        } else {
-//            sb.append("\nThe process may still be running.");
-//        }
         sb.append("\nThis problem might be caused by incorrect configuration of the daemon.");
         sb.append("\nFor example, an unrecognized jvm option is used.");
         sb.append("\nPlease refer to the user guide chapter on the daemon at ");
         sb.append(documentationRegistry.getDocumentationFor("gradle_daemon"));
-        sb.append("\nPlease read below process output to find out more:");
+        sb.append("\nPlease read the following process output to find out more:");
         sb.append("\n-----------------------\n");
         sb.append(output);
         return sb.toString();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
index 54aa641..5d34589 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
@@ -19,6 +19,7 @@ import com.google.common.io.Files;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.launcher.bootstrap.EntryPoint;
 import org.gradle.launcher.bootstrap.ExecutionListener;
 import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
@@ -29,12 +30,13 @@ import org.gradle.launcher.daemon.server.Daemon;
 import org.gradle.launcher.daemon.server.DaemonServices;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.messaging.remote.Address;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -49,81 +51,68 @@ public class DaemonMain extends EntryPoint {
 
     private static final Logger LOGGER = Logging.getLogger(DaemonMain.class);
 
-    private final DaemonServerConfiguration configuration;
     private PrintStream originalOut;
     private PrintStream originalErr;
 
-    public static void main(String[] args) {
+    @Override
+    protected void doAction(String[] args, ExecutionListener listener) {
         //The first argument is not really used but it is very useful in diagnosing, i.e. running 'jps -m'
         if (args.length < 4) {
-            invalidArgs("Following arguments are required: <gradle-version> <daemon-dir> <timeout-millis> <daemonUid> <optional startup jvm opts>");
+            invalidArgs("Following arguments are required: <gradle-version> <gradle-home-dir> <daemon-dir> <timeout-millis> <daemonUid> <optional startup jvm opts>");
         }
-        File daemonBaseDir = new File(args[1]);
+
+        File gradleHomeDir = new File(args[1]);
+        File daemonBaseDir = new File(args[2]);
 
         int idleTimeoutMs = 0;
         try {
-            idleTimeoutMs = Integer.parseInt(args[2]);
+            idleTimeoutMs = Integer.parseInt(args[3]);
         } catch (NumberFormatException e) {
             invalidArgs("Second argument must be a whole number (i.e. daemon idle timeout in ms)");
         }
 
-        String daemonUid = args[3];
+        String daemonUid = args[4];
 
-        List<String> startupOpts = new LinkedList<String>();
-        for (int i = 4; i < args.length; i++) {
+        List<String> startupOpts = new ArrayList<String>(args.length - 5);
+        //noinspection ManualArrayToCollectionCopy
+        for (int i = 5; i < args.length; i++) {
             startupOpts.add(args[i]);
         }
         LOGGER.debug("Assuming the daemon was started with following jvm opts: {}", startupOpts);
 
-        DaemonServerConfiguration parameters = new DefaultDaemonServerConfiguration(
-                daemonUid, daemonBaseDir, idleTimeoutMs, startupOpts);
-        DaemonMain daemonMain = new DaemonMain(parameters);
-
-        daemonMain.run();
-    }
-
-    private static void invalidArgs(String message) {
-        System.out.println("USAGE: <gradle version> <path to registry base dir> <idle timeout in milliseconds>");
-        System.out.println(message);
-        System.exit(1);
-    }
-
-    public DaemonMain(DaemonServerConfiguration configuration) {
-        this.configuration = configuration;
-    }
-
-    protected void doAction(ExecutionListener listener) {
-        LoggingServiceRegistry loggingRegistry = LoggingServiceRegistry.newProcessLogging();
+        NativeServices.initialize(gradleHomeDir);
+        DaemonServerConfiguration parameters = new DefaultDaemonServerConfiguration(daemonUid, daemonBaseDir, idleTimeoutMs, startupOpts);
+        LoggingServiceRegistry loggingRegistry = LoggingServiceRegistry.newCommandLineProcessLogging();
         LoggingManagerInternal loggingManager = loggingRegistry.newInstance(LoggingManagerInternal.class);
-        DaemonServices daemonServices = new DaemonServices(configuration, loggingRegistry, loggingManager);
+        DaemonServices daemonServices = new DaemonServices(parameters, loggingRegistry, loggingManager);
         File daemonLog = daemonServices.getDaemonLogFile();
-        final DaemonContext daemonContext = daemonServices.get(DaemonContext.class);
 
         initialiseLogging(loggingManager, daemonLog);
 
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            public void run() {
-                LOGGER.info("Daemon[pid = {}] process has finished.", daemonContext.getPid());
-            }
-        });
-
-        Daemon daemon = startDaemon(daemonServices);
-
-        Long pid = daemonContext.getPid();
-        LOGGER.lifecycle(DaemonMessages.PROCESS_STARTED + ((pid == null)? "":" Pid: " + pid + "."));
-        daemonStarted(pid, daemonLog);
+        Daemon daemon = daemonServices.get(Daemon.class);
+        daemon.start();
 
         try {
-            daemon.requestStopOnIdleTimeout(configuration.getIdleTimeout(), TimeUnit.MILLISECONDS);
-            LOGGER.info("Daemon hit idle timeout (" + configuration.getIdleTimeout() + "ms), stopping...");
+            DaemonContext daemonContext = daemonServices.get(DaemonContext.class);
+            Long pid = daemonContext.getPid();
+            daemonStarted(pid, daemon.getUid(), daemon.getAddress(), daemonLog);
+
+            // Block until idle
+            daemon.requestStopOnIdleTimeout(parameters.getIdleTimeout(), TimeUnit.MILLISECONDS);
         } finally {
             daemon.stop();
         }
     }
 
-    protected void daemonStarted(Long pid, File daemonLog) {
+    private static void invalidArgs(String message) {
+        System.out.println("USAGE: <gradle version> <path to registry base dir> <idle timeout in milliseconds>");
+        System.out.println(message);
+        System.exit(1);
+    }
+
+    protected void daemonStarted(Long pid, String uid, Address address, File daemonLog) {
         //directly printing to the stream to avoid log level filtering.
-        new DaemonStartupCommunication().printDaemonStarted(originalOut, pid, daemonLog);
+        new DaemonStartupCommunication().printDaemonStarted(originalOut, pid, uid, address, daemonLog);
         try {
             originalOut.close();
             originalErr.close();
@@ -160,7 +149,7 @@ public class DaemonMain extends EntryPoint {
 
         //after redirecting we need to add the new std out/err to the renderer singleton
         //so that logging gets its way to the daemon log:
-        loggingManager.addStandardOutputAndError();
+        loggingManager.attachSystemOutAndErr();
 
         //Making the daemon infrastructure log with DEBUG. This is only for the infrastructure!
         //Each build request carries it's own log level and it is used during the execution of the build (see LogToClient)
@@ -169,12 +158,6 @@ public class DaemonMain extends EntryPoint {
         loggingManager.start();
     }
 
-    protected Daemon startDaemon(DaemonServices daemonServices) {
-        Daemon daemon = daemonServices.get(Daemon.class);
-        daemon.start();
-        return daemon;
-    }
-
     private void redirectOutputsAndInput(PrintStream printStream) {
         this.originalOut = System.out;
         this.originalErr = System.err;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunication.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunication.java
index 3a8e61c..0e41e50 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunication.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunication.java
@@ -16,21 +16,56 @@
 
 package org.gradle.launcher.daemon.bootstrap;
 
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
+import org.gradle.launcher.daemon.diagnostics.DaemonStartupInfo;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
+import org.gradle.messaging.remote.Address;
+import org.gradle.messaging.remote.internal.inet.MultiChoiceAddress;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.FlushableEncoder;
+import org.gradle.internal.serialize.InputStreamBackedDecoder;
+import org.gradle.internal.serialize.OutputStreamBackedEncoder;
+import org.gradle.process.internal.child.EncodedStream;
 
-import java.io.File;
-import java.io.PrintStream;
+import java.io.*;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
 
 public class DaemonStartupCommunication {
 
-    private static final String DELIM = ";:"; //this very simple delim should be safe for any kind of path.
     private static final Logger LOGGER = Logging.getLogger(DaemonStartupCommunication.class);
 
-    public void printDaemonStarted(PrintStream target, Long pid, File daemonLog) {
-        target.println(daemonStartedMessage(pid, daemonLog));
+    public void printDaemonStarted(PrintStream target, Long pid, String uid, Address address, File daemonLog) {
+        target.print(daemonGreeting());
+
+        // Encode as ascii
+        try {
+            OutputStream outputStream = new EncodedStream.EncodedOutput(target);
+            FlushableEncoder encoder = new OutputStreamBackedEncoder(outputStream);
+            encoder.writeNullableString(pid == null ? null : pid.toString());
+            encoder.writeString(uid);
+            MultiChoiceAddress multiChoiceAddress = (MultiChoiceAddress) address;
+            UUID canonicalAddress = (UUID) multiChoiceAddress.getCanonicalAddress();
+            encoder.writeLong(canonicalAddress.getMostSignificantBits());
+            encoder.writeLong(canonicalAddress.getLeastSignificantBits());
+            encoder.writeInt(multiChoiceAddress.getPort());
+            encoder.writeSmallInt(multiChoiceAddress.getCandidates().size());
+            for (InetAddress inetAddress : multiChoiceAddress.getCandidates()) {
+                encoder.writeBinary(inetAddress.getAddress());
+            }
+            encoder.writeString(daemonLog.getPath());
+            encoder.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+
+        target.println();
+
         //ibm vm 1.6 + windows XP gotchas:
         //we need to print something else to the stream after we print the daemon greeting.
         //without it, the parent hangs without receiving the message above (flushing does not help).
@@ -38,17 +73,32 @@ public class DaemonStartupCommunication {
         //btw. the ibm vm+winXP also has some issues detecting closed streams by the child but we handle this problem differently.
     }
 
-    String daemonStartedMessage(Long pid, File daemonLog) {
-        return daemonGreeting() + DELIM + pid + DELIM + daemonLog;
-    }
-
-    public DaemonDiagnostics readDiagnostics(String message) {
-        //TODO SF dont assume the message has correct format
-        String[] split = message.split(DELIM);
-        String pidString = split[1];
-        Long pid = pidString.equals("null")? null : Long.valueOf(pidString);
-        File daemonLog = new File(split[2]);
-        return new DaemonDiagnostics(daemonLog, pid);
+    public DaemonStartupInfo readDiagnostics(String message) {
+        //Assuming the message has correct format. Not bullet proof, but seems to work ok for now.
+        if (!message.startsWith(daemonGreeting())) {
+            throw new IllegalArgumentException(String.format("Unexpected daemon startup message: %s", message));
+        }
+        try {
+            String encoded = message.substring(daemonGreeting().length()).trim();
+            InputStream inputStream = new EncodedStream.EncodedInput(new ByteArrayInputStream(encoded.getBytes()));
+            Decoder decoder = new InputStreamBackedDecoder(inputStream);
+            String pidString = decoder.readNullableString();
+            String uid = decoder.readString();
+            Long pid = pidString == null ? null : Long.valueOf(pidString);
+            UUID canonicalAddress = new UUID(decoder.readLong(), decoder.readLong());
+            int port = decoder.readInt();
+            int addressCount = decoder.readSmallInt();
+            List<InetAddress> addresses = new ArrayList<InetAddress>(addressCount);
+            for (int i = 0; i < addressCount; i++) {
+                InetAddress address = InetAddress.getByAddress(decoder.readBinary());
+                addresses.add(address);
+            }
+            Address address = new MultiChoiceAddress(canonicalAddress, port, addresses);
+            File daemonLog = new File(decoder.readString());
+            return new DaemonStartupInfo(uid, address, new DaemonDiagnostics(daemonLog, pid));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
     }
 
     public boolean containsGreeting(String message) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java
new file mode 100644
index 0000000..94d2ce3
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.bootstrap;
+
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
+import org.gradle.launcher.daemon.registry.DaemonRegistry;
+import org.gradle.launcher.daemon.server.Daemon;
+import org.gradle.launcher.daemon.server.DaemonServices;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.util.concurrent.TimeUnit;
+
+public class ForegroundDaemonAction implements Runnable {
+
+    private final ServiceRegistry loggingRegistry;
+    private final DaemonServerConfiguration configuration;
+
+    public ForegroundDaemonAction(ServiceRegistry loggingRegistry, DaemonServerConfiguration configuration) {
+        this.loggingRegistry = loggingRegistry;
+        this.configuration = configuration;
+    }
+
+    public void run() {
+        LoggingManagerInternal loggingManager = loggingRegistry.newInstance(LoggingManagerInternal.class);
+        loggingManager.start();
+
+        DaemonServices daemonServices = new DaemonServices(configuration, loggingRegistry, loggingManager);
+
+        Daemon daemon = daemonServices.get(Daemon.class);
+        daemon.start();
+
+        try {
+            daemonServices.get(DaemonRegistry.class).markIdle(daemon.getAddress());
+            daemon.requestStopOnIdleTimeout(configuration.getIdleTimeout(), TimeUnit.MILLISECONDS);
+        } finally {
+            daemon.stop();
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonMain.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonMain.java
deleted file mode 100644
index 2ea6f52..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonMain.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.launcher.daemon.bootstrap;
-
-import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
-import org.gradle.launcher.daemon.registry.DaemonRegistry;
-import org.gradle.launcher.daemon.server.Daemon;
-import org.gradle.launcher.daemon.server.DaemonServices;
-import org.gradle.logging.LoggingManagerInternal;
-
-import java.io.File;
-
-public class ForegroundDaemonMain extends DaemonMain {
-
-    public ForegroundDaemonMain(DaemonServerConfiguration configuration) {
-        super(configuration);
-    }
-
-    @Override
-    protected void initialiseLogging(LoggingManagerInternal loggingManager, File daemonLog) {
-        // Don't redirect IO for foreground daemon
-        loggingManager.start();
-    }
-
-    @Override
-    protected void daemonStarted(Long pid, File daemonLog) {
-        //don't do anything
-    }
-
-    @Override
-    protected Daemon startDaemon(DaemonServices daemonServices) {
-        Daemon daemon = super.startDaemon(daemonServices);
-        daemonServices.get(DaemonRegistry.class).markIdle(daemon.getAddress());
-        return daemon;
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonCancelForwarder.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonCancelForwarder.java
new file mode 100644
index 0000000..03c7951
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonCancelForwarder.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.launcher.daemon.protocol.Cancel;
+import org.gradle.messaging.dispatch.Dispatch;
+
+public class DaemonCancelForwarder implements Stoppable {
+    private static final Logger LOGGER = Logging.getLogger(DaemonCancelForwarder.class);
+
+    private final Runnable cancellationCallback;
+    private final BuildCancellationToken cancellationToken;
+
+    public DaemonCancelForwarder(final Dispatch<? super Cancel> dispatch, BuildCancellationToken cancellationToken, final IdGenerator<?> idGenerator) {
+        this.cancellationToken = cancellationToken;
+        cancellationCallback = new Runnable() {
+            public void run() {
+                LOGGER.info("Request daemon to cancel build...");
+                dispatch.dispatch(new Cancel(idGenerator.generateId()));
+            }
+        };
+    }
+
+    public void start() {
+        cancellationToken.addCallback(cancellationCallback);
+    }
+
+    public void stop() {
+        cancellationToken.removeCallback(cancellationCallback);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
index 24958eb..f9e9daa 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
@@ -15,18 +15,22 @@
  */
 package org.gradle.launcher.daemon.client;
 
-import org.gradle.api.GradleException;
+import org.gradle.api.BuildCancelledException;
 import org.gradle.api.internal.specs.ExplainingSpec;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.initialization.BuildEventConsumer;
+import org.gradle.initialization.BuildRequestContext;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
-import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.launcher.daemon.protocol.*;
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.logging.internal.OutputEvent;
@@ -34,13 +38,11 @@ import org.gradle.logging.internal.OutputEventListener;
 import org.gradle.messaging.remote.internal.Connection;
 
 import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * The client piece of the build daemon.
  *
- * <p>To execute a build:</p>
+ * <p>To execute a build action:</p>
  *
  * <ul>
  * <li>The client creates a connection to daemon.</li>
@@ -48,6 +50,7 @@ import java.util.Set;
  * <li>The daemon sends exactly one {@link BuildStarted}, {@link Failure} or {@link DaemonUnavailable} message.</li>
  * <li>If the build is started, the daemon may send zero or more {@link OutputEvent} messages.</li>
  * <li>If the build is started, the client may send zero or more {@link ForwardInput} messages followed by exactly one {@link CloseInput} message.</li>
+ * <li>If the build is started, the client may send {@link org.gradle.launcher.daemon.protocol.Cancel} message before {@link CloseInput} message.</li>
  * <li>The daemon sends exactly one {@link Result} message. It may no longer send any messages.</li>
  * <li>The client sends a {@link CloseInput} message, if not already sent. It may no longer send any {@link ForwardInput} messages.</li>
  * <li>The client sends a {@link Finished} message once it has received the {@link Result} message.
@@ -73,7 +76,6 @@ import java.util.Set;
  */
 public class DaemonClient implements BuildActionExecuter<BuildActionParameters> {
     private static final Logger LOGGER = Logging.getLogger(DaemonClient.class);
-    private static final int STOP_TIMEOUT_SECONDS = 30;
     private final DaemonConnector connector;
     private final OutputEventListener outputEventListener;
     private final ExplainingSpec<DaemonContext> compatibilitySpec;
@@ -102,53 +104,19 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
     }
 
     /**
-     * Stops all daemons, if any is running.
-     */
-    public void stop() {
-        long start = System.currentTimeMillis();
-        long expiry = start + STOP_TIMEOUT_SECONDS * 1000;
-        Set<String> stopped = new HashSet<String>();
-
-        // TODO - only connect to daemons that we have not yet sent a stop request to
-        DaemonClientConnection connection = connector.maybeConnect(compatibilitySpec);
-        if (connection == null) {
-            LOGGER.lifecycle(DaemonMessages.NO_DAEMONS_RUNNING);
-            return;
-        }
-
-        LOGGER.lifecycle("Stopping daemon(s).");
-
-        //iterate and stop all daemons
-        while (connection != null && System.currentTimeMillis() < expiry) {
-            try {
-                if (stopped.add(connection.getUid())) {
-                    new StopDispatcher(idGenerator).dispatch(connection);
-                    LOGGER.lifecycle("Gradle daemon stopped.");
-                }
-            } finally {
-                connection.stop();
-            }
-            connection = connector.maybeConnect(compatibilitySpec);
-        }
-
-        if (connection != null) {
-            throw new GradleException(String.format("Timeout waiting for all daemons to stop. Waited %s seconds.", (System.currentTimeMillis() - start) / 1000));
-        }
-    }
-
-    /**
      * Executes the given action in the daemon. The action and parameters must be serializable.
      *
      * @param action The action
      * @throws org.gradle.launcher.exec.ReportedException On failure, when the failure has already been logged/reported.
      */
-    public <T> T execute(BuildAction<T> action, BuildActionParameters parameters) {
-        Build build = new Build(idGenerator.generateId(), action, parameters);
+    public Object execute(BuildAction action, BuildRequestContext requestContext, BuildActionParameters parameters) {
+        Object buildId = idGenerator.generateId();
+        Build build = new Build(buildId, action, requestContext.getClient(), requestContext.getBuildTimeClock().getStartTime(), parameters);
         int saneNumberOfAttempts = 100; //is it sane enough?
         for (int i = 1; i < saneNumberOfAttempts; i++) {
-            DaemonClientConnection connection = connector.connect(compatibilitySpec);
+            final DaemonClientConnection connection = connector.connect(compatibilitySpec);
             try {
-                return (T) executeBuild(build, connection);
+                return executeBuild(build, connection, requestContext.getCancellationToken(), requestContext.getEventConsumer());
             } catch (DaemonInitialConnectException e) {
                 //this exception means that we want to try again.
                 LOGGER.info(e.getMessage() + " Trying a different daemon...");
@@ -161,10 +129,10 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
                 + saneNumberOfAttempts + " different daemons but I could not use any of them to run build: " + build + ".");
     }
 
-    protected Object executeBuild(Build build, DaemonClientConnection connection) throws DaemonInitialConnectException {
+    protected Object executeBuild(Build build, DaemonClientConnection connection, BuildCancellationToken cancellationToken, BuildEventConsumer buildEventConsumer) throws DaemonInitialConnectException {
         Object result;
         try {
-            LOGGER.info("Connected to the daemon. Dispatching {} request.", build);
+            LOGGER.info("Connected to daemon {}. Dispatching request {}.", connection.getDaemon(), build);
             connection.dispatch(build);
             result = connection.receive();
         } catch (StaleDaemonAddressException e) {
@@ -179,14 +147,21 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
 
         if (result instanceof BuildStarted) {
             DaemonDiagnostics diagnostics = ((BuildStarted) result).getDiagnostics();
-            result = monitorBuild(build, diagnostics, connection);
+            result = monitorBuild(build, diagnostics, connection, cancellationToken, buildEventConsumer);
         }
 
+        LOGGER.info("Received result {} from daemon {}.", result, connection.getDaemon());
+
         connection.dispatch(new Finished());
 
         if (result instanceof Failure) {
             // Could potentially distinguish between CommandFailure and DaemonFailure here.
-            throw UncheckedException.throwAsUncheckedException(((Failure) result).getValue());
+            Throwable failure = ((Failure) result).getValue();
+            if (failure instanceof DaemonStoppedException && cancellationToken.isCancellationRequested()) {
+                LOGGER.error("Daemon was stopped to handle build cancel request.");
+                throw new BuildCancelledException();
+            }
+            throw UncheckedException.throwAsUncheckedException(failure);
         } else if (result instanceof DaemonUnavailable) {
             throw new DaemonInitialConnectException("The daemon we connected to was unavailable: " + ((DaemonUnavailable) result).getReason());
         } else if (result instanceof Result) {
@@ -196,9 +171,11 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
         }
     }
 
-    private Object monitorBuild(Build build, DaemonDiagnostics diagnostics, Connection<Object> connection) {
+    private Object monitorBuild(Build build, DaemonDiagnostics diagnostics, Connection<Object> connection, BuildCancellationToken cancellationToken, BuildEventConsumer buildEventConsumer) {
         DaemonClientInputForwarder inputForwarder = new DaemonClientInputForwarder(buildStandardInput, connection, executorFactory, idGenerator);
+        DaemonCancelForwarder cancelForwarder = new DaemonCancelForwarder(connection, cancellationToken, idGenerator);
         try {
+            cancelForwarder.start();
             inputForwarder.start();
             int objectsReceived = 0;
 
@@ -210,12 +187,15 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
                     return handleDaemonDisappearance(build, diagnostics);
                 } else if (object instanceof OutputEvent) {
                     outputEventListener.onOutput((OutputEvent) object);
+                } else if (object instanceof BuildEvent) {
+                    buildEventConsumer.dispatch(((BuildEvent)object).getPayload());
                 } else {
                     return object;
                 }
             }
         } finally {
-            inputForwarder.stop();
+            // Stop cancelling before sending end-of-input
+            CompositeStoppable.stoppable(cancelForwarder, inputForwarder).stop();
         }
     }
 
@@ -224,11 +204,10 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
         //if he's really dead we should deregister it if it is not already deregistered.
         //if the daemon is not dead we might continue receiving from him (and try to find the bug in messaging infrastructure)
         LOGGER.error("The message received from the daemon indicates that the daemon has disappeared."
-                + "\nBuild request sent: " + build
-                + "\nAttempting to read last messages from the daemon log...");
+                     + "\nBuild request sent: " + build
+                     + "\nAttempting to read last messages from the daemon log...");
 
         LOGGER.error(diagnostics.describe());
-
         throw new DaemonDisappearedException();
     }
 
@@ -236,6 +215,6 @@ public class DaemonClient implements BuildActionExecuter<BuildActionParameters>
         //TODO diagnostics could be included in the exception (they might be available).
         return new IllegalStateException(String.format(
                 "Received invalid response from the daemon: '%s' is a result of a type we don't have a strategy to handle."
-                        + "Earlier, '%s' request was sent to the daemon.", response, command));
+                + "Earlier, '%s' request was sent to the daemon.", response, command));
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientConnection.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientConnection.java
index 42842f2..5f1af51 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientConnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientConnection.java
@@ -18,21 +18,30 @@ package org.gradle.launcher.daemon.client;
 import org.gradle.api.Nullable;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
 import org.gradle.messaging.remote.internal.Connection;
+import org.gradle.messaging.remote.internal.MessageIOException;
+import org.gradle.messaging.remote.internal.RemoteConnection;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * A simple wrapper for the connection to a daemon plus its password.
+ * A simple wrapper for the connection to a daemon.
+ *
+ * <p>Currently, dispatch is thread safe, and receive is not.
  */
 public class DaemonClientConnection implements Connection<Object> {
     private final static Logger LOG = Logging.getLogger(DaemonClientConnection.class);
-    private final Connection<Object> connection;
-    private final String uid;
+    private final RemoteConnection<Object> connection;
+    private final DaemonInstanceDetails daemon;
     private final StaleAddressDetector staleAddressDetector;
     private boolean hasReceived;
+    private final Lock dispatchLock = new ReentrantLock();
 
-    public DaemonClientConnection(Connection<Object> connection, String uid, StaleAddressDetector staleAddressDetector) {
+    public DaemonClientConnection(RemoteConnection<Object> connection, DaemonInstanceDetails daemon, StaleAddressDetector staleAddressDetector) {
         this.connection = connection;
-        this.uid = uid;
+        this.daemon = daemon;
         this.staleAddressDetector = staleAddressDetector;
     }
 
@@ -41,15 +50,20 @@ public class DaemonClientConnection implements Connection<Object> {
         connection.requestStop();
     }
 
-    public String getUid() {
-        return uid;
+    public DaemonInstanceDetails getDaemon() {
+        return daemon;
     }
 
     public void dispatch(Object message) throws DaemonConnectionException {
         LOG.debug("thread {}: dispatching {}", Thread.currentThread().getId(), message.getClass());
         try {
-            connection.dispatch(message);
-        } catch (Exception e) {
+            dispatchLock.lock();
+            try {
+                connection.dispatch(message);
+            } finally {
+                dispatchLock.unlock();
+            }
+        } catch (MessageIOException e) {
             LOG.debug("Problem dispatching message to the daemon. Performing 'on failure' operation...");
             if (!hasReceived && staleAddressDetector.maybeStaleAddress(e)) {
                 throw new StaleDaemonAddressException("Could not dispatch a message to the daemon.", e);
@@ -62,7 +76,7 @@ public class DaemonClientConnection implements Connection<Object> {
     public Object receive() throws DaemonConnectionException {
         try {
             return connection.receive();
-        } catch (Exception e) {
+        } catch (MessageIOException e) {
             LOG.debug("Problem receiving message to the daemon. Performing 'on failure' operation...");
             if (!hasReceived && staleAddressDetector.maybeStaleAddress(e)) {
                 throw new StaleDaemonAddressException("Could not receive a message from the daemon.", e);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientFactory.java
new file mode 100644
index 0000000..07dacde
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.launcher.daemon.configuration.DaemonParameters;
+import org.gradle.logging.internal.OutputEventListener;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+public class DaemonClientFactory {
+    private final ServiceRegistry sharedServices;
+
+    public DaemonClientFactory(ServiceRegistry sharedServices) {
+        this.sharedServices = sharedServices;
+    }
+
+    /**
+     * Creates the services for a {@link DaemonClient} that can be used to run builds.
+     */
+    public ServiceRegistry createBuildClientServices(OutputEventListener loggingReceiver, DaemonParameters daemonParameters, InputStream stdin) {
+        DefaultServiceRegistry loggingServices = new DefaultServiceRegistry(sharedServices);
+        loggingServices.add(OutputEventListener.class, loggingReceiver);
+        return new DaemonClientServices(loggingServices, daemonParameters, stdin);
+    }
+
+    /**
+     * Creates the services for a {@link DaemonClient} that can be used to run a build in a single-use daemon.
+     */
+    public ServiceRegistry createSingleUseDaemonClientServices(OutputEventListener loggingReceiver, DaemonParameters daemonParameters, InputStream stdin) {
+        DefaultServiceRegistry loggingServices = new DefaultServiceRegistry(sharedServices);
+        loggingServices.add(OutputEventListener.class, loggingReceiver);
+        return new SingleUseDaemonClientServices(loggingServices, daemonParameters, stdin);
+    }
+
+    /**
+     * Creates the services for a {@link DaemonStopClient} that can be used to stop builds.
+     */
+    public ServiceRegistry createStopDaemonServices(OutputEventListener loggingReceiver, DaemonParameters daemonParameters) {
+        DefaultServiceRegistry loggingServices = new DefaultServiceRegistry(sharedServices);
+        loggingServices.add(OutputEventListener.class, loggingReceiver);
+        return new DaemonClientServices(loggingServices, daemonParameters, new ByteArrayInputStream(new byte[0]));
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java
new file mode 100644
index 0000000..4ad35b5
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientGlobalServices.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.internal.service.ServiceRegistry;
+
+/**
+ * Global services shared by all Gradle daemon clients in a given process.
+ */
+public class DaemonClientGlobalServices {
+    DaemonClientFactory createClientFactory(ServiceRegistry sharedServices) {
+        return new DaemonClientFactory(sharedServices);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInputForwarder.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInputForwarder.java
index bc51be4..25fe7d0 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInputForwarder.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInputForwarder.java
@@ -18,8 +18,8 @@ package org.gradle.launcher.daemon.client;
 import org.gradle.api.Nullable;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.io.TextStream;
 import org.gradle.launcher.daemon.protocol.CloseInput;
@@ -31,44 +31,54 @@ import java.io.InputStream;
 
 /**
  * Eagerly consumes from an input stream, sending line by line ForwardInput
- * commands over the connection and finishing with a CloseInput command.»
+ * commands over the connection and finishing with a CloseInput command.
+ * It also listens to cancel requests and forwards it too as Cancel command.
  */
 public class DaemonClientInputForwarder implements Stoppable {
-
     private static final Logger LOGGER = Logging.getLogger(DaemonClientInputForwarder.class);
 
     public static final int DEFAULT_BUFFER_SIZE = 1024;
     private final InputForwarder forwarder;
 
-    public DaemonClientInputForwarder(InputStream inputStream, Dispatch<? super IoCommand> dispatch, ExecutorFactory executorFactory, IdGenerator<?> idGenerator) {
+    public DaemonClientInputForwarder(InputStream inputStream, Dispatch<? super IoCommand> dispatch,
+                                      ExecutorFactory executorFactory, IdGenerator<?> idGenerator) {
         this(inputStream, dispatch, executorFactory, idGenerator, DEFAULT_BUFFER_SIZE);
     }
 
-    public DaemonClientInputForwarder(InputStream inputStream, final Dispatch<? super IoCommand> dispatch, ExecutorFactory executorFactory, final IdGenerator<?> idGenerator, int bufferSize) {
-        TextStream handler = new TextStream() {
-            public void text(String input) {
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("Forwarding input to daemon: '{}'", input.replace("\n", "\\n"));
-                }
-                dispatch.dispatch(new ForwardInput(idGenerator.generateId(), input.getBytes()));
-            }
-
-            public void endOfStream(@Nullable Throwable failure) {
-                CloseInput message = new CloseInput(idGenerator.generateId());
-                LOGGER.debug("Dispatching close input message: {}", message);
-                dispatch.dispatch(message);
-            }
-        };
-
+    public DaemonClientInputForwarder(InputStream inputStream, Dispatch<? super IoCommand> dispatch,
+                                      ExecutorFactory executorFactory, IdGenerator<?> idGenerator, int bufferSize) {
+        TextStream handler = new ForwardTextStreamToConnection(dispatch, idGenerator);
         forwarder = new InputForwarder(inputStream, handler, executorFactory, bufferSize);
     }
 
-    public DaemonClientInputForwarder start() {
+    public void start() {
         forwarder.start();
-        return this;
     }
 
     public void stop() {
         forwarder.stop();
     }
+
+    private static class ForwardTextStreamToConnection implements TextStream {
+        private final Dispatch<? super IoCommand> dispatch;
+        private final IdGenerator<?> idGenerator;
+
+        public ForwardTextStreamToConnection(Dispatch<? super IoCommand> dispatch, IdGenerator<?> idGenerator) {
+            this.dispatch = dispatch;
+            this.idGenerator = idGenerator;
+        }
+
+        public void text(String input) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Forwarding input to daemon: '{}'", input.replace("\n", "\\n"));
+            }
+            dispatch.dispatch(new ForwardInput(idGenerator.generateId(), input.getBytes()));
+        }
+
+        public void endOfStream(@Nullable Throwable failure) {
+            CloseInput message = new CloseInput(idGenerator.generateId());
+            LOGGER.debug("Dispatching close input message: {}", message);
+            dispatch.dispatch(message);
+        }
+    }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInterruptedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInterruptedException.java
new file mode 100644
index 0000000..bd3b05e
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientInterruptedException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+/**
+ * Thrown when there is some problem using a daemon connection.
+ */
+public class DaemonClientInterruptedException extends RuntimeException {
+    public DaemonClientInterruptedException(String message) {
+        super(message);
+    }
+
+    public DaemonClientInterruptedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServices.java
index 7dc6ff1..6fd7f16 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServices.java
@@ -16,22 +16,13 @@
 package org.gradle.launcher.daemon.client;
 
 import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.cache.internal.DefaultFileLockManager;
-import org.gradle.cache.internal.DefaultProcessMetaDataProvider;
-import org.gradle.cache.internal.FileLockManager;
-import org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler;
-import org.gradle.cache.internal.locklistener.FileLockContentionHandler;
-import org.gradle.internal.concurrent.DefaultExecutorFactory;
-import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.launcher.daemon.bootstrap.DaemonGreeter;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 import org.gradle.launcher.daemon.context.DaemonContextBuilder;
 import org.gradle.launcher.daemon.registry.DaemonDir;
 import org.gradle.launcher.daemon.registry.DaemonRegistryServices;
-import org.gradle.messaging.remote.internal.MessagingServices;
-import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
+import org.gradle.internal.event.ListenerManager;
 
 import java.io.InputStream;
 
@@ -41,41 +32,18 @@ import java.io.InputStream;
 public class DaemonClientServices extends DaemonClientServicesSupport {
     private final DaemonParameters daemonParameters;
 
-    public DaemonClientServices(ServiceRegistry loggingServices, DaemonParameters daemonParameters, InputStream buildStandardInput) {
-        super(loggingServices, buildStandardInput);
+    public DaemonClientServices(ServiceRegistry parent, DaemonParameters daemonParameters, InputStream buildStandardInput) {
+        super(parent, buildStandardInput);
         this.daemonParameters = daemonParameters;
         addProvider(new DaemonRegistryServices(daemonParameters.getBaseDir()));
     }
 
-    protected FileLockManager createFileLockManager(ProcessEnvironment processEnvironment, FileLockContentionHandler fileLockContentionHandler) {
-        return new DefaultFileLockManager(new DefaultProcessMetaDataProvider(processEnvironment), fileLockContentionHandler);
+    DaemonStarter createDaemonStarter(DaemonDir daemonDir, DaemonParameters daemonParameters, ListenerManager listenerManager, DaemonGreeter daemonGreeter) {
+        return new DefaultDaemonStarter(daemonDir, daemonParameters, daemonGreeter, listenerManager.getBroadcaster(DaemonStartListener.class));
     }
 
-    protected MessagingServices createMessagingServices() {
-        return new MessagingServices(getClass().getClassLoader());
-    }
-
-    protected FileLockContentionHandler createFileLockContentionHandler(ExecutorFactory executorFactory, MessagingServices messagingServices) {
-        return new DefaultFileLockContentionHandler(
-                executorFactory,
-                messagingServices.get(InetAddressFactory.class)
-        );
-    }
-
-    protected ExecutorFactory createExecutorFactory() {
-        return new DefaultExecutorFactory();
-    }
-
-    protected DocumentationRegistry createDocumentationRegistry() {
-        return new DocumentationRegistry();
-    }
-
-    public DaemonStarter createDaemonStarter() {
-        return new DefaultDaemonStarter(get(DaemonDir.class), daemonParameters, get(DaemonGreeter.class));
-    }
-
-    protected DaemonGreeter createDaemonGreeter() {
-        return new DaemonGreeter(get(DocumentationRegistry.class));
+    DaemonGreeter createDaemonGreeter(DocumentationRegistry documentationRegistry) {
+        return new DaemonGreeter(documentationRegistry);
     }
 
     protected void configureDaemonContextBuilder(DaemonContextBuilder builder) {
@@ -83,7 +51,7 @@ public class DaemonClientServices extends DaemonClientServicesSupport {
         builder.useDaemonParameters(daemonParameters);
     }
 
-    public DaemonParameters getDaemonParameters() {
+    DaemonParameters createDaemonParameters() {
         return daemonParameters;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServicesSupport.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServicesSupport.java
index 61642f2..acb7d84 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServicesSupport.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClientServicesSupport.java
@@ -20,8 +20,7 @@ import org.gradle.internal.id.CompositeIdGenerator;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.id.LongIdGenerator;
 import org.gradle.internal.id.UUIDGenerator;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.launcher.daemon.context.DaemonCompatibilitySpec;
@@ -43,8 +42,8 @@ import java.io.InputStream;
 abstract public class DaemonClientServicesSupport extends DefaultServiceRegistry {
     private final InputStream buildStandardInput;
 
-    public DaemonClientServicesSupport(ServiceRegistry loggingServices, InputStream buildStandardInput) {
-        super(NativeServices.getInstance(), loggingServices);
+    public DaemonClientServicesSupport(ServiceRegistry parent, InputStream buildStandardInput) {
+        super(parent);
         this.buildStandardInput = buildStandardInput;
     }
 
@@ -52,6 +51,10 @@ abstract public class DaemonClientServicesSupport extends DefaultServiceRegistry
         return buildStandardInput;
     }
 
+    DaemonStopClient createDaemonStopClient(DaemonConnector connector, IdGenerator idGenerator) {
+        return new DaemonStopClient(connector, idGenerator);
+    }
+
     protected DaemonClient createDaemonClient() {
         DaemonCompatibilitySpec matchingContextSpec = new DaemonCompatibilitySpec(get(DaemonContext.class));
         return new DaemonClient(
@@ -63,8 +66,8 @@ abstract public class DaemonClientServicesSupport extends DefaultServiceRegistry
                 get(IdGenerator.class));
     }
 
-    protected DaemonContext createDaemonContext() {
-        DaemonContextBuilder builder = new DaemonContextBuilder(get(ProcessEnvironment.class));
+    DaemonContext createDaemonContext(ProcessEnvironment processEnvironment) {
+        DaemonContextBuilder builder = new DaemonContextBuilder(processEnvironment);
         configureDaemonContextBuilder(builder);
         return builder.create();
     }
@@ -74,15 +77,15 @@ abstract public class DaemonClientServicesSupport extends DefaultServiceRegistry
         
     }
 
-    protected IdGenerator<?> createIdGenerator() {
+    IdGenerator<?> createIdGenerator() {
         return new CompositeIdGenerator(new UUIDGenerator().generateId(), new LongIdGenerator());
     }
 
-    protected OutgoingConnector createOutgoingConnector() {
+    OutgoingConnector createOutgoingConnector() {
         return new TcpOutgoingConnector();
     }
 
-    protected DaemonConnector createDaemonConnector() {
-        return new DefaultDaemonConnector(get(DaemonRegistry.class), get(OutgoingConnector.class), get(DaemonStarter.class));
+    DaemonConnector createDaemonConnector(DaemonRegistry daemonRegistry, OutgoingConnector outgoingConnector, DaemonStarter daemonStarter) {
+        return new DefaultDaemonConnector(daemonRegistry, outgoingConnector, daemonStarter);
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonConnector.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonConnector.java
index e509f33..99f71e2 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonConnector.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonConnector.java
@@ -18,6 +18,7 @@ package org.gradle.launcher.daemon.client;
 import org.gradle.api.Nullable;
 import org.gradle.api.internal.specs.ExplainingSpec;
 import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
 
 /**
  * A daemon connector establishes a connection to a daemon.
@@ -25,6 +26,14 @@ import org.gradle.launcher.daemon.context.DaemonContext;
 public interface DaemonConnector {
 
     /**
+     * Attempts to connect to a daemon at the given address.
+     *
+     * @return A connection to a matching daemon, or null if not running.
+     */
+    @Nullable
+    public DaemonClientConnection maybeConnect(DaemonInstanceDetails daemonAddress);
+
+    /**
      * Attempts to connect to a daemon that matches the given constraint.
      *
      * @return A connection to a matching daemon, or null if none running.
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
index 0650906..8fa3d77 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
@@ -22,7 +22,7 @@ import org.gradle.api.GradleException;
  */
 public class DaemonDisappearedException extends GradleException {
 
-    public static final String MESSAGE = "Gradle build daemon disappeared unexpectedly (it may have been stopped, killed or may have crashed)";
+    public static final String MESSAGE = "Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed)";
 
     public DaemonDisappearedException() {
         super(MESSAGE);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStartListener.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStartListener.java
new file mode 100644
index 0000000..4798193
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStartListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
+
+/**
+ * Notified when a daemon is started.
+ */
+public interface DaemonStartListener {
+    void daemonStarted(DaemonInstanceDetails daemonInfo);
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStopClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStopClient.java
new file mode 100644
index 0000000..b0bdf18
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonStopClient.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.specs.ExplainingSpec;
+import org.gradle.api.internal.specs.ExplainingSpecs;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.id.IdGenerator;
+import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
+import org.gradle.launcher.daemon.logging.DaemonMessages;
+import org.gradle.launcher.daemon.protocol.Stop;
+import org.gradle.launcher.daemon.protocol.StopWhenIdle;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <p>To stop a daemon:</p>
+ *
+ * <ul>
+ * <li>The client creates a connection to daemon.</li>
+ * <li>The client sends exactly one {@link org.gradle.launcher.daemon.protocol.Stop} message.</li>
+ * <li>The daemon sends exactly one {@link org.gradle.launcher.daemon.protocol.Result} message. It may no longer send any messages.</li>
+ * <li>The client sends a {@link org.gradle.launcher.daemon.protocol.Finished} message once it has received the {@link org.gradle.launcher.daemon.protocol.Result} message.
+ *     It may no longer send any messages.</li>
+ * <li>The client closes the connection.</li>
+ * <li>The daemon closes the connection once it has received the {@link org.gradle.launcher.daemon.protocol.Finished} message.</li>
+ * </ul>
+ */
+public class DaemonStopClient {
+    private static final Logger LOGGER = Logging.getLogger(DaemonClient.class);
+    private static final int STOP_TIMEOUT_SECONDS = 30;
+    private final DaemonConnector connector;
+    private final IdGenerator<?> idGenerator;
+    private final ExplainingSpec<DaemonContext> compatibilitySpec = ExplainingSpecs.satisfyAll();
+    private final StopDispatcher stopDispatcher;
+
+    public DaemonStopClient(DaemonConnector connector, IdGenerator<?> idGenerator) {
+        this.connector = connector;
+        this.idGenerator = idGenerator;
+        this.stopDispatcher = new StopDispatcher();
+    }
+
+    /**
+     * Requests that the given daemons stop when idle. Does not block and returns before the daemons have all stopped.
+     */
+    public void gracefulStop(Collection<DaemonInstanceDetails> daemons) {
+        for (DaemonInstanceDetails daemon : daemons) {
+            DaemonClientConnection connection = connector.maybeConnect(daemon);
+            if (connection == null) {
+                continue;
+            }
+            try {
+                LOGGER.debug("Requesting daemon {} stop when idle", daemon);
+                stopDispatcher.dispatch(connection, new StopWhenIdle(idGenerator.generateId()));
+                LOGGER.lifecycle("Gradle daemon stopped.");
+            } finally {
+                connection.stop();
+            }
+        }
+    }
+
+    /**
+     * Stops all daemons, blocking until all have completed.
+     */
+    public void stop() {
+        long start = System.currentTimeMillis();
+        long expiry = start + STOP_TIMEOUT_SECONDS * 1000;
+        Set<String> stopped = new HashSet<String>();
+
+        // TODO - only connect to daemons that we have not yet sent a stop request to
+        DaemonClientConnection connection = connector.maybeConnect(compatibilitySpec);
+        if (connection == null) {
+            LOGGER.lifecycle(DaemonMessages.NO_DAEMONS_RUNNING);
+            return;
+        }
+
+        LOGGER.lifecycle("Stopping daemon(s).");
+
+        //iterate and stop all daemons
+        while (connection != null && System.currentTimeMillis() < expiry) {
+            try {
+                if (stopped.add(connection.getDaemon().getUid())) {
+                    LOGGER.debug("Requesting daemon {} stop now", connection.getDaemon());
+                    stopDispatcher.dispatch(connection, new Stop(idGenerator.generateId()));
+                    LOGGER.lifecycle("Gradle daemon stopped.");
+                }
+            } finally {
+                connection.stop();
+            }
+            connection = connector.maybeConnect(compatibilitySpec);
+        }
+
+        if (connection != null) {
+            throw new GradleException(String.format("Timeout waiting for all daemons to stop. Waited %s seconds.", (System.currentTimeMillis() - start) / 1000));
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonConnector.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonConnector.java
index 1721131..740e740 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonConnector.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonConnector.java
@@ -20,13 +20,14 @@ import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.UncheckedException;
 import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
 import org.gradle.launcher.daemon.diagnostics.DaemonStartupInfo;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.launcher.daemon.registry.DaemonInfo;
 import org.gradle.launcher.daemon.registry.DaemonRegistry;
 import org.gradle.messaging.remote.internal.ConnectException;
-import org.gradle.messaging.remote.internal.Connection;
 import org.gradle.messaging.remote.internal.OutgoingConnector;
+import org.gradle.messaging.remote.internal.RemoteConnection;
 
 import java.util.List;
 
@@ -36,6 +37,8 @@ import java.util.List;
 public class DefaultDaemonConnector implements DaemonConnector {
     private static final Logger LOGGER = Logging.getLogger(DefaultDaemonConnector.class);
     public static final int DEFAULT_CONNECT_TIMEOUT = 30000;
+    public static final String STARTING_DAEMON_MESSAGE = "Starting a new Gradle Daemon for this build (subsequent builds will be faster).";
+    public static final String DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY = "org.gradle.daemon.disable-starting-message";
     private final DaemonRegistry daemonRegistry;
     protected final OutgoingConnector connector;
     private final DaemonStarter daemonStarter;
@@ -63,37 +66,48 @@ public class DefaultDaemonConnector implements DaemonConnector {
         return findConnection(daemonRegistry.getAll(), constraint);
     }
 
+    public DaemonClientConnection maybeConnect(DaemonInstanceDetails daemon) {
+        try {
+            return connectToDaemon(daemon, new CleanupOnStaleAddress(daemon, true));
+        } catch (ConnectException e) {
+            LOGGER.debug("Cannot connect to daemon {} due to {}. Ignoring.", daemon, e);
+            return null;
+        }
+    }
+
     public DaemonClientConnection connect(ExplainingSpec<DaemonContext> constraint) {
         DaemonClientConnection connection = findConnection(daemonRegistry.getIdle(), constraint);
         if (connection != null) {
             return connection;
         }
 
+        if (!Boolean.getBoolean(DISABLE_STARTING_DAEMON_MESSAGE_PROPERTY)) {
+            LOGGER.lifecycle(STARTING_DAEMON_MESSAGE);
+        }
         return startDaemon(constraint);
     }
 
-    private DaemonClientConnection findConnection(List<DaemonInfo> daemonInfos, ExplainingSpec<DaemonContext> constraint) {
-        for (final DaemonInfo daemonInfo : daemonInfos) {
-            if (!constraint.isSatisfiedBy(daemonInfo.getContext())) {
-                LOGGER.debug("Found daemon (address: {}, idle: {}) however its context does not match the desired criteria.\n"
-                        + constraint.whyUnsatisfied(daemonInfo.getContext()) + "\n"
-                        + "  Looking for a different daemon...", daemonInfo.getAddress(), daemonInfo.isIdle());
+    private DaemonClientConnection findConnection(List<DaemonInfo> daemons, ExplainingSpec<DaemonContext> constraint) {
+        for (DaemonInfo daemon : daemons) {
+            if (!constraint.isSatisfiedBy(daemon.getContext())) {
+                LOGGER.debug("Found daemon {} however its context does not match the desired criteria.\n"
+                        + constraint.whyUnsatisfied(daemon.getContext()) + "\n"
+                        + "  Looking for a different daemon...", daemon);
                 continue;
             }
 
             try {
-                return connectToDaemon(daemonInfo, new CleanupOnStaleAddress(daemonInfo, true));
+                return connectToDaemon(daemon, new CleanupOnStaleAddress(daemon, true));
             } catch (ConnectException e) {
-                LOGGER.debug("Cannot connect to the daemon at " + daemonInfo.getAddress() + " due to " + e + ". Trying a different daemon...");
+                LOGGER.debug("Cannot connect to daemon {} due to {}. Trying a different daemon...", daemon, e);
             }
         }
         return null;
     }
 
     public DaemonClientConnection startDaemon(ExplainingSpec<DaemonContext> constraint) {
-        LOGGER.info("Starting Gradle daemon");
         final DaemonStartupInfo startupInfo = daemonStarter.startDaemon();
-        LOGGER.debug("Started Gradle Daemon: {}", startupInfo);
+        LOGGER.debug("Started Gradle daemon {}", startupInfo);
         long expiry = System.currentTimeMillis() + connectTimeout;
         do {
             DaemonClientConnection daemonConnection = connectToDaemonWithId(startupInfo, constraint);
@@ -110,10 +124,10 @@ public class DefaultDaemonConnector implements DaemonConnector {
         throw new DaemonConnectionException("Timeout waiting to connect to the Gradle daemon.\n" + startupInfo.describe());
     }
 
-    private DaemonClientConnection connectToDaemonWithId(DaemonStartupInfo startupInfo, ExplainingSpec<DaemonContext> constraint) throws ConnectException {
+    private DaemonClientConnection connectToDaemonWithId(DaemonStartupInfo daemon, ExplainingSpec<DaemonContext> constraint) throws ConnectException {
         // Look for 'our' daemon among the busy daemons - a daemon will start in busy state so that nobody else will grab it.
         for (DaemonInfo daemonInfo : daemonRegistry.getBusy()) {
-            if (daemonInfo.getContext().getUid().equals(startupInfo.getUid())) {
+            if (daemonInfo.getUid().equals(daemon.getUid())) {
                 try {
                     if (!constraint.isSatisfiedBy(daemonInfo.getContext())) {
                         throw new DaemonConnectionException("The newly created daemon process has a different context than expected."
@@ -122,36 +136,36 @@ public class DefaultDaemonConnector implements DaemonConnector {
                     }
                     return connectToDaemon(daemonInfo, new CleanupOnStaleAddress(daemonInfo, false));
                 } catch (ConnectException e) {
-                    throw new DaemonConnectionException("Could not connect to the Gradle daemon.\n" + startupInfo.describe(), e);
+                    throw new DaemonConnectionException("Could not connect to the Gradle daemon.\n" + daemon.describe(), e);
                 }
             }
         }
         return null;
     }
 
-    private DaemonClientConnection connectToDaemon(DaemonInfo daemonInfo, DaemonClientConnection.StaleAddressDetector staleAddressDetector) throws ConnectException {
-        Connection<Object> connection;
+    private DaemonClientConnection connectToDaemon(DaemonInstanceDetails daemon, DaemonClientConnection.StaleAddressDetector staleAddressDetector) throws ConnectException {
+        RemoteConnection<Object> connection;
         try {
-            connection = connector.connect(daemonInfo.getAddress()).create(getClass().getClassLoader());
+            connection = connector.connect(daemon.getAddress()).create(getClass().getClassLoader());
         } catch (ConnectException e) {
             staleAddressDetector.maybeStaleAddress(e);
             throw e;
         }
-        return new DaemonClientConnection(connection, daemonInfo.getContext().getUid(), staleAddressDetector);
+        return new DaemonClientConnection(connection, daemon, staleAddressDetector);
     }
 
     private class CleanupOnStaleAddress implements DaemonClientConnection.StaleAddressDetector {
-        private final DaemonInfo daemonInfo;
+        private final DaemonInstanceDetails daemon;
         private final boolean exposeAsStale;
 
-        public CleanupOnStaleAddress(DaemonInfo daemonInfo, boolean exposeAsStale) {
-            this.daemonInfo = daemonInfo;
+        public CleanupOnStaleAddress(DaemonInstanceDetails daemon, boolean exposeAsStale) {
+            this.daemon = daemon;
             this.exposeAsStale = exposeAsStale;
         }
 
         public boolean maybeStaleAddress(Exception failure) {
-            LOGGER.info(DaemonMessages.REMOVING_DAEMON_ADDRESS_ON_FAILURE + daemonInfo);
-            daemonRegistry.remove(daemonInfo.getAddress());
+            LOGGER.info(DaemonMessages.REMOVING_DAEMON_ADDRESS_ON_FAILURE + daemon);
+            daemonRegistry.remove(daemon.getAddress());
             return exposeAsStale;
         }
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
index 40196ae..4144ef5 100755
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
@@ -24,7 +24,6 @@ import org.gradle.launcher.daemon.bootstrap.DaemonGreeter;
 import org.gradle.launcher.daemon.bootstrap.DaemonOutputConsumer;
 import org.gradle.launcher.daemon.bootstrap.GradleDaemon;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
-import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
 import org.gradle.launcher.daemon.diagnostics.DaemonStartupInfo;
 import org.gradle.launcher.daemon.registry.DaemonDir;
 import org.gradle.process.ExecResult;
@@ -46,12 +45,14 @@ public class DefaultDaemonStarter implements DaemonStarter {
 
     private final DaemonDir daemonDir;
     private final DaemonParameters daemonParameters;
-    private DaemonGreeter daemonGreeter;
+    private final DaemonGreeter daemonGreeter;
+    private final DaemonStartListener listener;
 
-    public DefaultDaemonStarter(DaemonDir daemonDir, DaemonParameters daemonParameters, DaemonGreeter daemonGreeter) {
+    public DefaultDaemonStarter(DaemonDir daemonDir, DaemonParameters daemonParameters, DaemonGreeter daemonGreeter, DaemonStartListener listener) {
         this.daemonDir = daemonDir;
         this.daemonParameters = daemonParameters;
         this.daemonGreeter = daemonGreeter;
+        this.listener = listener;
     }
 
     public DaemonStartupInfo startDaemon() {
@@ -66,6 +67,8 @@ public class DefaultDaemonStarter implements DaemonStarter {
             throw new IllegalStateException("Unable to construct a bootstrap classpath when starting the daemon");
         }
 
+        new JvmVersionValidator().validate(daemonParameters);
+
         List<String> daemonArgs = new ArrayList<String>();
         daemonArgs.add(daemonParameters.getEffectiveJavaExecutable());
 
@@ -79,6 +82,7 @@ public class DefaultDaemonStarter implements DaemonStarter {
         daemonArgs.add(CollectionUtils.join(File.pathSeparator, bootstrapClasspath));
         daemonArgs.add(GradleDaemon.class.getName());
         daemonArgs.add(GradleVersion.current().getVersion());
+        daemonArgs.add(daemonParameters.getGradleUserHomeDir().getAbsolutePath());
         daemonArgs.add(daemonDir.getBaseDir().getAbsolutePath());
         daemonArgs.add(String.valueOf(daemonParameters.getIdleTimeout()));
         daemonArgs.add(daemonParameters.getUid());
@@ -87,12 +91,12 @@ public class DefaultDaemonStarter implements DaemonStarter {
         //we need to pass them as *program* arguments to avoid problems with getInputArguments().
         daemonArgs.addAll(daemonOpts);
 
-        DaemonDiagnostics diagnostics = startProcess(daemonArgs, daemonDir.getVersionedDir());
-
-        return new DaemonStartupInfo(daemonParameters.getUid(), diagnostics);
+        DaemonStartupInfo daemonInfo = startProcess(daemonArgs, daemonDir.getVersionedDir());
+        listener.daemonStarted(daemonInfo);
+        return daemonInfo;
     }
 
-    private DaemonDiagnostics startProcess(final List<String> args, final File workingDir) {
+    private DaemonStartupInfo startProcess(final List<String> args, final File workingDir) {
         LOGGER.info("Starting daemon process: workingDir = {}, daemonArgs: {}", workingDir, args);
         Clock clock = new Clock();
         try {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonClientServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonClientServices.java
index a76c464..77aadd6 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonClientServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonClientServices.java
@@ -16,11 +16,12 @@
 package org.gradle.launcher.daemon.client;
 
 import org.gradle.initialization.BuildLayoutParameters;
-import org.gradle.initialization.GradleLauncherFactory;
 import org.gradle.internal.Factory;
 import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 import org.gradle.launcher.daemon.context.DaemonContext;
@@ -33,11 +34,14 @@ import org.gradle.launcher.daemon.server.DaemonServerConnector;
 import org.gradle.launcher.daemon.server.DaemonTcpServerConnector;
 import org.gradle.launcher.daemon.server.exec.DaemonCommandExecuter;
 import org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter;
-import org.gradle.launcher.daemon.server.exec.NoOpDaemonCommandAction;
+import org.gradle.launcher.daemon.server.exec.StopHandlingCommandExecuter;
+import org.gradle.launcher.exec.InProcessBuildActionExecuter;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.logging.internal.OutputEvent;
 import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.messaging.remote.internal.MessagingServices;
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
 
 import java.io.File;
 import java.util.UUID;
@@ -47,7 +51,7 @@ import java.util.UUID;
  */
 public class EmbeddedDaemonClientServices extends DaemonClientServicesSupport {
     public EmbeddedDaemonClientServices() {
-        this(LoggingServiceRegistry.newProcessLogging());
+        this(LoggingServiceRegistry.newCommandLineProcessLogging());
     }
 
     private class EmbeddedDaemonFactory implements Factory<Daemon> {
@@ -65,13 +69,18 @@ public class EmbeddedDaemonClientServices extends DaemonClientServicesSupport {
 
     protected DaemonCommandExecuter createDaemonCommandExecuter() {
         LoggingManagerInternal mgr = newInstance(LoggingManagerInternal.class);
-        return new DefaultDaemonCommandExecuter(get(GradleLauncherFactory.class),
-                get(ProcessEnvironment.class), mgr, new File("dummy"), new NoOpDaemonCommandAction());
+        return new StopHandlingCommandExecuter(
+                new DefaultDaemonCommandExecuter(
+                        get(InProcessBuildActionExecuter.class),
+                        get(ProcessEnvironment.class),
+                        mgr,
+                        new File("dummy"),
+                        new StubDaemonHealthServices()));
     }
 
     public EmbeddedDaemonClientServices(ServiceRegistry loggingServices) {
-        super(loggingServices, System.in);
-        addProvider(new GlobalScopeServices(false));
+        super(ServiceRegistryBuilder.builder().parent(loggingServices).parent(NativeServices.getInstance()).build(), System.in);
+        addProvider(new GlobalScopeServices(true));
         add(EmbeddedDaemonFactory.class, new EmbeddedDaemonFactory());
     }
 
@@ -90,7 +99,7 @@ public class EmbeddedDaemonClientServices extends DaemonClientServicesSupport {
     }
 
     protected DaemonServerConnector createDaemonServerConnector() {
-        return new DaemonTcpServerConnector();
+        return new DaemonTcpServerConnector(get(ExecutorFactory.class), get(MessagingServices.class).get(InetAddressFactory.class));
     }
 
     protected DaemonStarter createDaemonStarter() {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonStarter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonStarter.java
index 533e1ad..076c4dc 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonStarter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/EmbeddedDaemonStarter.java
@@ -38,7 +38,7 @@ class EmbeddedDaemonStarter implements DaemonStarter, Stoppable {
     public DaemonStartupInfo startDaemon() {
         Daemon daemon = daemonFactory.create();
         startDaemon(daemon);
-        return new DaemonStartupInfo(daemon.getUid(), null);
+        return new DaemonStartupInfo(daemon.getUid(), daemon.getAddress(), null);
     }
 
     public void startDaemon(Daemon daemon) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/InputForwarder.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/InputForwarder.java
index 0c5533d..8279aab 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/InputForwarder.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/InputForwarder.java
@@ -15,9 +15,9 @@
  */
 package org.gradle.launcher.daemon.client;
 
-import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.concurrent.StoppableExecutor;
 import org.gradle.internal.io.TextStream;
 import org.gradle.util.DisconnectableInputStream;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/JvmVersionValidator.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/JvmVersionValidator.java
new file mode 100644
index 0000000..4db188a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/JvmVersionValidator.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.launcher.daemon.client;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.jvm.UnsupportedJavaRuntimeException;
+import org.gradle.launcher.daemon.configuration.DaemonParameters;
+import org.gradle.process.internal.ExecHandleBuilder;
+
+import java.io.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class JvmVersionValidator {
+    void validate(DaemonParameters parameters) {
+        if (parameters.getEffectiveJavaHome().equals(Jvm.current().getJavaHome())) {
+            return;
+        }
+
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+        ExecHandleBuilder builder = new ExecHandleBuilder();
+        builder.setWorkingDir(new File(".").getAbsolutePath());
+        builder.setCommandLine(parameters.getEffectiveJavaExecutable(), "-version");
+        builder.setStandardOutput(new ByteArrayOutputStream());
+        builder.setErrorOutput(outputStream);
+        builder.build().start().waitForFinish().assertNormalExitValue();
+
+        JavaVersion javaVersion = parseJavaVersionCommandOutput(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray()))));
+        if (!javaVersion.isJava6Compatible()) {
+            throw UnsupportedJavaRuntimeException.configuredWithUnsupportedVersion("Gradle", JavaVersion.VERSION_1_6, javaVersion);
+        }
+    }
+
+    static JavaVersion parseJavaVersionCommandOutput(BufferedReader reader) {
+        try {
+            String versionStr = reader.readLine();
+            while (versionStr != null) {
+                Matcher matcher = Pattern.compile("(?:java|openjdk) version \"(.+?)\"").matcher(versionStr);
+                if (matcher.matches()) {
+                    return JavaVersion.toVersion(matcher.group(1));
+                }
+                versionStr = reader.readLine();
+            }
+        } catch (IOException e) {
+            throw new org.gradle.api.UncheckedIOException(e);
+        }
+
+        throw new RuntimeException("Could not determine Java version.");
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
index 748e2f1..7da6ead 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
@@ -21,7 +21,8 @@ import org.gradle.api.internal.specs.ExplainingSpec;
 import org.gradle.api.internal.specs.ExplainingSpecs;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildRequestContext;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.launcher.daemon.context.DaemonContext;
@@ -44,12 +45,12 @@ public class SingleUseDaemonClient extends DaemonClient {
     }
 
     @Override
-    public <T> T execute(BuildAction<T> action, BuildActionParameters parameters) {
+    public Object execute(BuildAction action, BuildRequestContext buildRequestContext, BuildActionParameters parameters) {
         LOGGER.lifecycle("{} Please consider using the daemon: {}.", MESSAGE, documentationRegistry.getDocumentationFor("gradle_daemon"));
-        Build build = new BuildAndStop(getIdGenerator().generateId(), action, parameters);
+        Build build = new BuildAndStop(getIdGenerator().generateId(), action, buildRequestContext.getClient(), buildRequestContext.getBuildTimeClock().getStartTime(), parameters);
 
         DaemonClientConnection daemonConnection = getConnector().startDaemon(ExplainingSpecs.<DaemonContext>satisfyAll());
 
-        return (T) executeBuild(build, daemonConnection);
+        return executeBuild(build, daemonConnection, buildRequestContext.getCancellationToken(), buildRequestContext.getEventConsumer());
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDaemonClientServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDaemonClientServices.java
deleted file mode 100644
index 2c0801b..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDaemonClientServices.java
+++ /dev/null
@@ -1,46 +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.launcher.daemon.client;
-
-import org.gradle.api.internal.specs.ExplainingSpec;
-import org.gradle.api.internal.specs.ExplainingSpecs;
-import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.id.IdGenerator;
-import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.launcher.daemon.configuration.DaemonParameters;
-import org.gradle.launcher.daemon.context.DaemonContext;
-import org.gradle.logging.internal.OutputEventListener;
-
-import java.io.InputStream;
-
-public class StopDaemonClientServices extends DaemonClientServices {
-    public StopDaemonClientServices(ServiceRegistry loggingServices, DaemonParameters daemonParameters, InputStream buildStandardInput) {
-        super(loggingServices, daemonParameters, buildStandardInput);
-    }
-
-    @Override
-    protected DaemonClient createDaemonClient() {
-        ExplainingSpec<DaemonContext> matchAll = ExplainingSpecs.satisfyAll();
-        return new DaemonClient(
-                get(DaemonConnector.class),
-                get(OutputEventListener.class),
-                matchAll,
-                getBuildStandardInput(),
-                get(ExecutorFactory.class),
-                get(IdGenerator.class));
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDispatcher.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDispatcher.java
index 7c665fc..62c4ee8 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDispatcher.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StopDispatcher.java
@@ -18,25 +18,19 @@ package org.gradle.launcher.daemon.client;
 
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.id.IdGenerator;
+import org.gradle.launcher.daemon.protocol.Command;
 import org.gradle.launcher.daemon.protocol.Failure;
 import org.gradle.launcher.daemon.protocol.Finished;
 import org.gradle.launcher.daemon.protocol.Result;
-import org.gradle.launcher.daemon.protocol.Stop;
 import org.gradle.messaging.remote.internal.Connection;
 
 public class StopDispatcher {
     private static final Logger LOGGER = Logging.getLogger(StopDispatcher.class);
-    private final IdGenerator<?> idGenerator;
 
-    public StopDispatcher(IdGenerator<?> idGenerator) {
-        this.idGenerator = idGenerator;
-    }
-
-    public void dispatch(Connection<Object> connection) {
+    public void dispatch(Connection<Object> connection, Command stopCommand) {
         Throwable failure = null;
         try {
-            connection.dispatch(new Stop(idGenerator.generateId()));
+            connection.dispatch(stopCommand);
             Result result = (Result) connection.receive();
             if (result instanceof Failure) {
                 failure = ((Failure) result).getValue();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StubDaemonHealthServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StubDaemonHealthServices.java
new file mode 100644
index 0000000..78bc8cd
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/StubDaemonHealthServices.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.client;
+
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.exec.NoOpDaemonCommandAction;
+import org.gradle.launcher.daemon.server.health.DaemonHealthServices;
+
+public class StubDaemonHealthServices implements DaemonHealthServices {
+
+    private final NoOpDaemonCommandAction noOp = new NoOpDaemonCommandAction();
+
+    public DaemonCommandAction getGCHintAction() {
+        return noOp;
+    }
+
+    public DaemonCommandAction getHealthTrackerAction() {
+        return noOp;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/CurrentProcess.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/CurrentProcess.java
index f5bbc5e..a8e88ea 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/CurrentProcess.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/CurrentProcess.java
@@ -16,12 +16,16 @@
 
 package org.gradle.launcher.daemon.configuration;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import org.gradle.api.internal.file.IdentityFileResolver;
-import org.gradle.process.internal.JvmOptions;
 import org.gradle.internal.jvm.Jvm;
+import org.gradle.process.internal.JvmOptions;
 
 import java.io.File;
 import java.lang.management.ManagementFactory;
+import java.util.List;
 import java.util.Properties;
 
 public class CurrentProcess {
@@ -52,9 +56,10 @@ public class CurrentProcess {
     public boolean configureForBuild(DaemonParameters requiredBuildParameters) {
         boolean javaHomeMatch = getJavaHome().equals(requiredBuildParameters.getEffectiveJavaHome());
 
-        JvmOptions optionsWithNoArgsSet = new JvmOptions(new IdentityFileResolver());
-        boolean noImmutableJvmArgsRequired = requiredBuildParameters.isUsingDefaultJvmArgs()
-                || requiredBuildParameters.getEffectiveJvmArgs().equals(optionsWithNoArgsSet.getAllImmutableJvmArgs());
+        List<String> currentImmutable = new JvmOptions(new IdentityFileResolver()).getAllImmutableJvmArgs();
+        List<String> requiredImmutable = requiredBuildParameters.getEffectiveJvmArgs();
+        List<String> requiredImmutableMinusDefaults = removeDefaults(requiredImmutable);
+        boolean noImmutableJvmArgsRequired = requiredImmutableMinusDefaults.equals(currentImmutable);
 
         if (javaHomeMatch && noImmutableJvmArgsRequired) {
             // Set the system properties and use this process
@@ -66,6 +71,14 @@ public class CurrentProcess {
         return false;
     }
 
+    private List<String> removeDefaults(List<String> effectiveJvmArgs) {
+        return Lists.newArrayList(Iterables.filter(effectiveJvmArgs, new Predicate<String>() {
+            public boolean apply(String input) {
+                return !DaemonParameters.DEFAULT_JVM_ARGS.contains(input);
+            }
+        }));
+    }
+
     private static JvmOptions inferJvmOptions() {
         // Try to infer the effective jvm options for the currently running process.
         // We only care about 'managed' jvm args, anything else is unimportant to the running build
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
index 6f2f025..e6eaf1a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.launcher.daemon.configuration;
 
-import org.gradle.StartParameter;
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.initialization.BuildLayoutParameters;
 import org.gradle.internal.jvm.Jvm;
@@ -25,37 +25,36 @@ import org.gradle.util.GUtil;
 import java.io.File;
 import java.util.*;
 
-import static java.util.Arrays.asList;
 import static org.gradle.util.GFileUtils.canonicalise;
 
 public class DaemonParameters {
     static final int DEFAULT_IDLE_TIMEOUT = 3 * 60 * 60 * 1000;
 
+    public static final List<String> DEFAULT_JVM_ARGS = ImmutableList.of("-Xmx1024m", "-XX:MaxPermSize=256m", "-XX:+HeapDumpOnOutOfMemoryError");
+
     private final String uid;
+    private final File gradleUserHomeDir;
 
-    private File baseDir = new File(StartParameter.DEFAULT_GRADLE_USER_HOME, "daemon");
+    private File baseDir;
     private int idleTimeout = DEFAULT_IDLE_TIMEOUT;
     private final JvmOptions jvmOptions = new JvmOptions(new IdentityFileResolver());
-    private boolean usingDefaultJvmArgs = true;
-    private boolean enabled;
+    private DaemonUsage daemonUsage = DaemonUsage.IMPLICITLY_DISABLED;
     private File javaHome;
 
     public DaemonParameters(BuildLayoutParameters layout) {
-        this.uid = UUID.randomUUID().toString();
-        jvmOptions.setAllJvmArgs(getDefaultJvmArgs());
-        baseDir = new File(layout.getGradleUserHomeDir(), "daemon");
+        this(layout, Collections.<String, String>emptyMap());
     }
 
-    List<String> getDefaultJvmArgs() {
-        return new LinkedList<String>(asList("-Xmx1024m", "-XX:MaxPermSize=256m", "-XX:+HeapDumpOnOutOfMemoryError"));
-    }
-
-    public boolean isEnabled() {
-        return enabled;
+    public DaemonParameters(BuildLayoutParameters layout, Map<String, String> extraSystemProperties) {
+        this.uid = UUID.randomUUID().toString();
+        jvmOptions.setAllJvmArgs(DEFAULT_JVM_ARGS);
+        jvmOptions.systemProperties(extraSystemProperties);
+        baseDir = new File(layout.getGradleUserHomeDir(), "daemon");
+        gradleUserHomeDir = layout.getGradleUserHomeDir();
     }
 
     public DaemonParameters setEnabled(boolean enabled) {
-        this.enabled = enabled;
+        daemonUsage = enabled ? DaemonUsage.EXPLICITLY_ENABLED : DaemonUsage.EXPLICITLY_DISABLED;
         return this;
     }
 
@@ -67,6 +66,10 @@ public class DaemonParameters {
         return baseDir;
     }
 
+    public File getGradleUserHomeDir() {
+        return gradleUserHomeDir;
+    }
+
     public int getIdleTimeout() {
         return idleTimeout;
     }
@@ -83,17 +86,13 @@ public class DaemonParameters {
         return jvmOptions.getAllJvmArgs();
     }
 
-    public boolean isUsingDefaultJvmArgs() {
-        return usingDefaultJvmArgs;
-    }
-
     public File getEffectiveJavaHome() {
         if (javaHome == null) {
             return canonicalise(Jvm.current().getJavaHome());
         }
         return javaHome;
     }
-    
+
     public String getEffectiveJavaExecutable() {
         if (javaHome == null) {
             return Jvm.current().getJavaExecutable().getAbsolutePath();
@@ -120,7 +119,6 @@ public class DaemonParameters {
     }
 
     public void setJvmArgs(Iterable<String> jvmArgs) {
-        usingDefaultJvmArgs = false;
         jvmOptions.setAllJvmArgs(jvmArgs);
     }
 
@@ -136,4 +134,8 @@ public class DaemonParameters {
     public boolean getDebug() {
         return jvmOptions.getDebug();
     }
+
+    public DaemonUsage getDaemonUsage() {
+        return daemonUsage;
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonUsage.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonUsage.java
new file mode 100644
index 0000000..2d68403
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonUsage.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.configuration;
+
+public enum DaemonUsage {
+    IMPLICITLY_DISABLED(false, false),
+    EXPLICITLY_DISABLED(true, false),
+    EXPLICITLY_ENABLED(true, true);
+
+    private final boolean explicitlySet;
+    private final boolean enabled;
+
+    DaemonUsage(boolean explicitlySet, boolean enabled) {
+        this.explicitlySet = explicitlySet;
+        this.enabled = enabled;
+    }
+
+    public boolean isExplicitlySet() {
+        return explicitlySet;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/GradleProperties.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/GradleProperties.java
index dc5bc84..b824aca 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/GradleProperties.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/GradleProperties.java
@@ -30,9 +30,10 @@ public class GradleProperties {
     public static final String DEBUG_MODE_PROPERTY = "org.gradle.debug";
     public static final String CONFIGURE_ON_DEMAND_PROPERTY = "org.gradle.configureondemand";
     public static final String PARALLEL_PROPERTY = "org.gradle.parallel";
+    public static final String WORKERS_PROPERTY = "org.gradle.workers.max";
 
     public static final Set<String> ALL = newHashSet(IDLE_TIMEOUT_PROPERTY, DAEMON_BASE_DIR_PROPERTY, JVM_ARGS_PROPERTY,
-            JAVA_HOME_PROPERTY, DAEMON_ENABLED_PROPERTY, DEBUG_MODE_PROPERTY, CONFIGURE_ON_DEMAND_PROPERTY, PARALLEL_PROPERTY);
+            JAVA_HOME_PROPERTY, DAEMON_ENABLED_PROPERTY, DEBUG_MODE_PROPERTY, CONFIGURE_ON_DEMAND_PROPERTY, PARALLEL_PROPERTY, WORKERS_PROPERTY);
 
     public static boolean isTrue(Object propertyValue) {
         return propertyValue != null && propertyValue.toString().equalsIgnoreCase("true");
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonCompatibilitySpec.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonCompatibilitySpec.java
index 41fca77..039d87a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonCompatibilitySpec.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonCompatibilitySpec.java
@@ -22,7 +22,7 @@ import static org.gradle.util.GFileUtils.canonicalise;
 public class DaemonCompatibilitySpec implements ExplainingSpec<DaemonContext> {
 
     private final DaemonContext desiredContext;
-    
+
     public DaemonCompatibilitySpec(DaemonContext desiredContext) {
         this.desiredContext = desiredContext;
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContext.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContext.java
index a6830e9..3c2aba5 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContext.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContext.java
@@ -15,8 +15,8 @@
  */
 package org.gradle.launcher.daemon.context;
 
-import java.io.Serializable;
 import java.io.File;
+import java.io.Serializable;
 import java.util.List;
 
 /**
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContextBuilder.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContextBuilder.java
index aef3381..21cea24 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContextBuilder.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonContextBuilder.java
@@ -18,11 +18,12 @@ package org.gradle.launcher.daemon.context;
 import com.google.common.collect.Lists;
 import org.gradle.internal.Factory;
 import org.gradle.internal.jvm.Jvm;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 
 import java.io.File;
 import java.util.List;
+import java.util.Locale;
 
 import static org.gradle.util.GFileUtils.canonicalise;
 
@@ -39,6 +40,7 @@ public class DaemonContextBuilder implements Factory<DaemonContext> {
     private File daemonRegistryDir;
     private Long pid;
     private Integer idleTimeout;
+    private Locale locale = Locale.getDefault();
     private List<String> daemonOpts = Lists.newArrayList();
 
     public DaemonContextBuilder(ProcessEnvironment processEnvironment) {
@@ -86,6 +88,14 @@ public class DaemonContextBuilder implements Factory<DaemonContext> {
         this.idleTimeout = idleTimeout;
     }
 
+    public Locale getLocale() {
+        return locale;
+    }
+
+    public void setLocale(Locale locale) {
+        this.locale = locale;
+    }
+
     public List<String> getDaemonOpts() {
         return daemonOpts;
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonInstanceDetails.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonInstanceDetails.java
new file mode 100644
index 0000000..e2ba740
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/context/DaemonInstanceDetails.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.context;
+
+import org.gradle.messaging.remote.Address;
+
+/**
+ * Some basic meta-data required to identify and connect to a daemon.
+ */
+public interface DaemonInstanceDetails {
+    String getUid();
+
+    Address getAddress();
+
+    Long getPid();
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonStartupInfo.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonStartupInfo.java
index a1a8a6d..78489db 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonStartupInfo.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/diagnostics/DaemonStartupInfo.java
@@ -16,13 +16,17 @@
 
 package org.gradle.launcher.daemon.diagnostics;
 
-public class DaemonStartupInfo {
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
+import org.gradle.messaging.remote.Address;
 
-    private String uid;
-    private DaemonDiagnostics diagnostics;
+public class DaemonStartupInfo implements DaemonInstanceDetails {
+    private final String uid;
+    private final Address address;
+    private final DaemonDiagnostics diagnostics;
 
-    public DaemonStartupInfo(String uid, DaemonDiagnostics diagnostics) {
+    public DaemonStartupInfo(String uid, Address address, DaemonDiagnostics diagnostics) {
         this.uid = uid;
+        this.address = address;
         this.diagnostics = diagnostics;
     }
 
@@ -30,6 +34,14 @@ public class DaemonStartupInfo {
         return uid;
     }
 
+    public Address getAddress() {
+        return address;
+    }
+
+    public Long getPid() {
+        return diagnostics.getPid();
+    }
+
     /**
      * @return the diagnostics. Can be null, this means the existing daemon hasn't yet provided the diagnostics.
      */
@@ -39,10 +51,7 @@ public class DaemonStartupInfo {
 
     @Override
     public String toString() {
-        return "{"
-                + "uid='" + uid + '\''
-                + ", diagnostics=" + diagnostics
-                + '}';
+        return String.format("DaemonStartupInfo{pid=%s, uid=%s, address=%s, diagnostics=%s}", diagnostics.getPid(), uid, address, diagnostics);
     }
 
     public String describe() {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/logging/DaemonMessages.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/logging/DaemonMessages.java
index 3f784cb..f533107 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/logging/DaemonMessages.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/logging/DaemonMessages.java
@@ -23,6 +23,7 @@ public abstract class DaemonMessages {
     public final static String UNABLE_TO_START_DAEMON = "Unable to start the daemon process.";
     public final static String STARTED_EXECUTING_COMMAND = "Starting executing command: ";
     public final static String FINISHED_EXECUTING_COMMAND = "Finishing executing command: ";
+    public final static String STARTED_BUILD = "The daemon has started executing the build.";
     public final static String FINISHED_BUILD = "The daemon has finished executing the build.";
     public final static String NO_DAEMONS_RUNNING = "No Gradle daemons are running.";
     public final static String ABOUT_TO_START_RELAYING_LOGS = "About to start relaying all logs to the client via the connection.";
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Build.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Build.java
index 4c7115f..913d15a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Build.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Build.java
@@ -15,20 +15,31 @@
  */
 package org.gradle.launcher.daemon.protocol;
 
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildClientMetaData;
+import org.gradle.initialization.BuildRequestMetaData;
+import org.gradle.initialization.DefaultBuildRequestMetaData;
 import org.gradle.launcher.exec.BuildActionParameters;
 
 public class Build extends Command {
-    private final BuildAction<?> action;
+    private final BuildAction action;
+    private final BuildClientMetaData buildClientMetaData;
+    private final long startTime;
     private final BuildActionParameters parameters;
 
-    public Build(Object identifier, BuildAction<?> action, BuildActionParameters parameters) {
+    public Build(Object identifier, BuildAction action, BuildClientMetaData buildClientMetaData, long startTime, BuildActionParameters parameters) {
         super(identifier);
         this.action = action;
+        this.buildClientMetaData = buildClientMetaData;
+        this.startTime = startTime;
         this.parameters = parameters;
     }
 
-    public BuildAction<?> getAction() {
+    public BuildRequestMetaData getBuildRequestMetaData() {
+        return new DefaultBuildRequestMetaData(buildClientMetaData, startTime);
+    }
+
+    public BuildAction getAction() {
         return action;
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildAndStop.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildAndStop.java
index 314a07c..3fbccc9 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildAndStop.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildAndStop.java
@@ -16,11 +16,12 @@
 
 package org.gradle.launcher.daemon.protocol;
 
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildClientMetaData;
 import org.gradle.launcher.exec.BuildActionParameters;
 
 public class BuildAndStop extends Build {
-    public BuildAndStop(Object identifier, BuildAction<?> action, BuildActionParameters parameters) {
-        super(identifier, action, parameters);
+    public BuildAndStop(Object identifier, BuildAction action, BuildClientMetaData buildClientMetaData, long startTime, BuildActionParameters parameters) {
+        super(identifier, action, buildClientMetaData, startTime, parameters);
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildEvent.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildEvent.java
new file mode 100644
index 0000000..f781f2c
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.protocol;
+
+/**
+ * Some arbitrary build event sent from build logic back to the build requester.
+ */
+public class BuildEvent extends Message {
+    private final Object payload;
+
+    public BuildEvent(Object payload) {
+        this.payload = payload;
+    }
+
+    public Object getPayload() {
+        return payload;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s[event=%s]", getClass().getSimpleName(), payload);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
index 02621fa..71f3960 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
@@ -18,12 +18,10 @@ package org.gradle.launcher.daemon.protocol;
 
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
 
-import java.io.Serializable;
-
 /**
  * Returned when the daemon starts a build command, signifying that it has begun processing it.
  */
-public class BuildStarted implements Serializable {
+public class BuildStarted extends Message {
 
     private final DaemonDiagnostics diagnostics;
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Cancel.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Cancel.java
new file mode 100644
index 0000000..105b690
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Cancel.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.protocol;
+
+public class Cancel extends Command {
+
+    public Cancel(Object identifier) {
+        super(identifier);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Command.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Command.java
index 7088235..235442d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Command.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Command.java
@@ -15,9 +15,7 @@
  */
 package org.gradle.launcher.daemon.protocol;
 
-import java.io.Serializable;
-
-public class Command implements Serializable {
+public class Command extends Message {
     private final Object identifier;
 
     public Command(Object identifier) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonUnavailable.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonUnavailable.java
index 9ec7fde..b34044f 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonUnavailable.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/DaemonUnavailable.java
@@ -16,12 +16,10 @@
 
 package org.gradle.launcher.daemon.protocol;
 
-import java.io.Serializable;
-
 /**
  * Returned when the daemon is busy running a different command.
  */
-public class DaemonUnavailable implements Serializable {
+public class DaemonUnavailable extends Message {
     private final String reason;
 
     public DaemonUnavailable(String reason) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Finished.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Finished.java
index 715cbe5..8fdc319 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Finished.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Finished.java
@@ -16,7 +16,9 @@
 
 package org.gradle.launcher.daemon.protocol;
 
-import java.io.Serializable;
-
-public class Finished implements Serializable {
+/**
+ * Sent from the daemon client to the daemon to indicate it has finished with the connection. This is the last
+ * message sent from the client to the daemon.
+ */
+public class Finished extends Message {
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Message.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Message.java
new file mode 100644
index 0000000..d54f968
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Message.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.protocol;
+
+import java.io.Serializable;
+
+/**
+ * A message sent between daemon client and daemon server.
+ */
+public class Message implements Serializable {
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Result.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Result.java
index 61220ae..07a8488 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Result.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Result.java
@@ -15,8 +15,6 @@
  */
 package org.gradle.launcher.daemon.protocol;
 
-import java.io.Serializable;
-
 /**
  * The supertype of all objects sent from the daemon server back to the client.
  * <p>
@@ -24,8 +22,10 @@ import java.io.Serializable;
  * <p>
  * The meaning of the value parameter is specific to each concrete subclass. The validity of {@code null}
  * is also to be defined by each subclass. This implementation does allow null values.
+ *
+ * <p>The result is the last message sent from the daemon back to the daemon client.
  */
-abstract public class Result<T> implements Serializable {
+abstract public class Result<T> extends Message {
 
     private final T value;
 
@@ -39,6 +39,6 @@ abstract public class Result<T> implements Serializable {
     
     @Override
     public String toString() {
-        return String.format("DaemonCommandResult[type=%s, value=%s]", getClass().getSimpleName(), getValue());
+        return String.format("%s[value=%s]", getClass().getSimpleName(), getValue());
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/StopWhenIdle.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/StopWhenIdle.java
new file mode 100644
index 0000000..966bf89
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/StopWhenIdle.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.protocol;
+
+public class StopWhenIdle extends Command {
+    public StopWhenIdle(Object identifier) {
+        super(identifier);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonInfo.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonInfo.java
index 2ab1587..bcc1c56 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonInfo.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonInfo.java
@@ -16,15 +16,16 @@
 
 package org.gradle.launcher.daemon.registry;
 
-import org.gradle.messaging.remote.Address;
 import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
+import org.gradle.messaging.remote.Address;
 
 import java.io.Serializable;
 
 /**
  * Provides information about a daemon that is potentially available to do some work.
  */
-public class DaemonInfo implements Serializable {
+public class DaemonInfo implements Serializable, DaemonInstanceDetails {
 
     private final Address address;
     private final DaemonContext context;
@@ -43,6 +44,14 @@ public class DaemonInfo implements Serializable {
         return this;
     }
 
+    public String getUid() {
+        return context.getUid();
+    }
+
+    public Long getPid() {
+        return context.getPid();
+    }
+
     public Address getAddress() {
         return address;
     }
@@ -61,7 +70,7 @@ public class DaemonInfo implements Serializable {
 
     @Override
     public String toString() {
-        return String.format("DaemonInfo{address=%s, idle=%s, context=%s}", address, idle, context);
+        return String.format("DaemonInfo{pid=%s, address=%s, idle=%s, context=%s}", context.getPid(), address, idle, context);
     }
 
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonRegistry.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonRegistry.java
index b7ee57b..cbb4757 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonRegistry.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/DaemonRegistry.java
@@ -16,6 +16,7 @@
 
 package org.gradle.launcher.daemon.registry;
 
+import net.jcip.annotations.ThreadSafe;
 import org.gradle.messaging.remote.Address;
 import org.gradle.launcher.daemon.context.DaemonContext;
 
@@ -23,7 +24,10 @@ import java.util.List;
 
 /**
  * Provides access to existing daemons.
+ *
+ * Implementations should be thread-safe.
  */
+ at ThreadSafe
 public interface DaemonRegistry {
 
     List<DaemonInfo> getAll();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/PersistentDaemonRegistry.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/PersistentDaemonRegistry.java
index 13677e6..ed38386 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/PersistentDaemonRegistry.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/registry/PersistentDaemonRegistry.java
@@ -25,7 +25,7 @@ import org.gradle.cache.internal.OnDemandFileAccess;
 import org.gradle.cache.internal.SimpleStateCache;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.messaging.remote.Address;
-import org.gradle.messaging.serialize.DefaultSerializer;
+import org.gradle.internal.serialize.DefaultSerializer;
 
 import java.io.File;
 import java.util.LinkedList;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/Daemon.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/Daemon.java
index 7a303bd..13304df 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/Daemon.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/Daemon.java
@@ -122,7 +122,7 @@ public class Daemon implements Stoppable {
             // 3. start accepting incoming connections
             // 4. advertise presence in registry
 
-            stateCoordinator = new DaemonStateCoordinator(onStartCommand, onFinishCommand);
+            stateCoordinator = new DaemonStateCoordinator(executorFactory, onStartCommand, onFinishCommand);
             connectionHandler = new DefaultIncomingConnectionHandler(commandExecuter, daemonContext, stateCoordinator, executorFactory);
             connectorAddress = connector.start(connectionHandler);
             LOGGER.debug("Daemon starting at: " + new Date() + ", with address: " + connectorAddress);
@@ -130,6 +130,8 @@ public class Daemon implements Stoppable {
         } finally {
             lifecyleLock.unlock();
         }
+
+        LOGGER.lifecycle(DaemonMessages.PROCESS_STARTED);
     }
 
     /**
@@ -169,10 +171,10 @@ public class Daemon implements Stoppable {
 
     /**
      * Waits for the daemon to be idle for the specified number of milliseconds, then requests that the daemon stop.
-     * 
-     * @throws DaemonStoppedException if the daemon is explicitly stopped instead of idling out.
+     *
+     * <p>May return earlier if the daemon is stopped before the idle timeout is reached.</p>
      */
-    public void requestStopOnIdleTimeout(int idleTimeout, TimeUnit idleTimeoutUnits) throws DaemonStoppedException {
+    public void requestStopOnIdleTimeout(int idleTimeout, TimeUnit idleTimeoutUnits) {
         LOGGER.debug("requestStopOnIdleTimeout({} {}) called on daemon", idleTimeout, idleTimeoutUnits);
         DaemonStateCoordinator stateCoordinator;
         lifecyleLock.lock();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
index 6302684..68424ec 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonServices.java
@@ -15,24 +15,28 @@
  */
 package org.gradle.launcher.daemon.server;
 
-import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.initialization.GradleLauncherFactory;
 import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.GlobalScopeServices;
 import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.context.DaemonContextBuilder;
 import org.gradle.launcher.daemon.registry.DaemonDir;
 import org.gradle.launcher.daemon.registry.DaemonRegistry;
 import org.gradle.launcher.daemon.registry.DaemonRegistryServices;
-import org.gradle.launcher.daemon.server.exec.DaemonHygieneAction;
 import org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter;
+import org.gradle.launcher.daemon.server.exec.StopHandlingCommandExecuter;
+import org.gradle.launcher.daemon.server.health.DaemonHealthServices;
+import org.gradle.launcher.daemon.server.health.DefaultDaemonHealthServices;
+import org.gradle.launcher.exec.InProcessBuildActionExecuter;
 import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.messaging.remote.internal.MessagingServices;
+import org.gradle.messaging.remote.internal.inet.InetAddressFactory;
 
 import java.io.File;
 import java.util.UUID;
@@ -74,17 +78,25 @@ public class DaemonServices extends DefaultServiceRegistry {
         return new File(get(DaemonDir.class).getVersionedDir(), fileName);
     }
 
-    protected Daemon createDaemon() {
+    protected DaemonHealthServices createDaemonHealthServices() {
+        return new DefaultDaemonHealthServices();
+    }
+
+    protected Daemon createDaemon(InProcessBuildActionExecuter buildActionExecuter) {
         return new Daemon(
-                new DaemonTcpServerConnector(),
+                new DaemonTcpServerConnector(
+                    get(ExecutorFactory.class),
+                    get(MessagingServices.class).get(InetAddressFactory.class)),
                 get(DaemonRegistry.class),
                 get(DaemonContext.class),
                 "password",
-                new DefaultDaemonCommandExecuter(
-                        get(GradleLauncherFactory.class),
-                        get(ProcessEnvironment.class),
-                        loggingManager,
-                        getDaemonLogFile(), new DaemonHygieneAction()),
+                new StopHandlingCommandExecuter(
+                        new DefaultDaemonCommandExecuter(
+                                buildActionExecuter,
+                                get(ProcessEnvironment.class),
+                                loggingManager,
+                                getDaemonLogFile(),
+                                get(DaemonHealthServices.class))),
                 get(ExecutorFactory.class));
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStateCoordinator.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStateCoordinator.java
index 81f3434..e043c3a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStateCoordinator.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStateCoordinator.java
@@ -17,11 +17,16 @@
 package org.gradle.launcher.daemon.server;
 
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.initialization.DefaultBuildCancellationToken;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.concurrent.StoppableExecutor;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
-import org.gradle.launcher.daemon.server.exec.DaemonStateControl;
-import org.gradle.launcher.daemon.server.exec.DaemonUnavailableException;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException;
+import org.gradle.launcher.daemon.server.api.DaemonUnavailableException;
 import org.slf4j.Logger;
 
 import java.util.Date;
@@ -44,19 +49,29 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
 
     private final Lock lock = new ReentrantLock();
     private final Condition condition = lock.newCondition();
+    private final long cancelTimeoutMs;
 
     private State state = State.Running;
     private long lastActivityAt = -1;
     private String currentCommandExecution;
+    private Object result;
+    private volatile DefaultBuildCancellationToken cancellationToken;
 
+    private final StoppableExecutor executor;
     private final Runnable onStartCommand;
     private final Runnable onFinishCommand;
-    private Runnable onDisconnect;
 
-    public DaemonStateCoordinator(Runnable onStartCommand, Runnable onFinishCommand) {
+    public DaemonStateCoordinator(ExecutorFactory executorFactory, Runnable onStartCommand, Runnable onFinishCommand) {
+        this(executorFactory, onStartCommand, onFinishCommand, 10 * 1000L);
+    }
+
+    DaemonStateCoordinator(ExecutorFactory executorFactory, Runnable onStartCommand, Runnable onFinishCommand, long cancelTimeoutMs) {
+        executor = executorFactory.create("Daemon worker");
         this.onStartCommand = onStartCommand;
         this.onFinishCommand = onFinishCommand;
+        this.cancelTimeoutMs = cancelTimeoutMs;
         updateActivityTimestamp();
+        cancellationToken = new DefaultBuildCancellationToken();
     }
 
     private void setState(State state) {
@@ -67,7 +82,7 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
     private boolean awaitStop(long timeoutMs) {
         lock.lock();
         try {
-            LOGGER.debug("waiting for daemon to stop or be idle for {}ms", timeoutMs);
+            LOGGER.debug("Idle timeout: waiting for daemon to stop or be idle for {}ms", timeoutMs);
             while (true) {
                 try {
                     switch (state) {
@@ -76,8 +91,8 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
                                 LOGGER.debug(DaemonMessages.DAEMON_BUSY);
                                 condition.await();
                             } else if (hasBeenIdleFor(timeoutMs)) {
-                                LOGGER.debug("Daemon has been idle for requested period.");
-                                stop();
+                                LOGGER.debug("Idle timeout: daemon has been idle for requested period. Stopping now.");
+                                stopNow("idle timeout");
                                 return false;
                             } else {
                                 Date waitUntil = new Date(lastActivityAt + timeoutMs);
@@ -88,11 +103,11 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
                         case Broken:
                             throw new IllegalStateException("This daemon is in a broken state.");
                         case StopRequested:
-                            LOGGER.debug("Daemon is stopping, sleeping until state changes.");
+                            LOGGER.debug("Idle timeout: daemon stop has been requested. Sleeping until state changes.");
                             condition.await();
                             break;
                         case Stopped:
-                            LOGGER.debug("Daemon has stopped.");
+                            LOGGER.debug("Idle timeout: daemon has stopped.");
                             return true;
                     }
                 } catch (InterruptedException e) {
@@ -104,20 +119,18 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
         }
     }
 
-    public void stopOnIdleTimeout(int timeout, TimeUnit timeoutUnits) throws DaemonStoppedException {
-        if (awaitStop(timeoutUnits.toMillis(timeout))) {
-            throw new DaemonStoppedException(currentCommandExecution);
-        }
+    public void stopOnIdleTimeout(int timeout, TimeUnit timeoutUnits) {
+        awaitStop(timeoutUnits.toMillis(timeout));
     }
 
     public void requestStop() {
         lock.lock();
         try {
-            LOGGER.debug("Stop as soon as idle requested. The daemon is busy: " + isBusy());
+            LOGGER.debug("Stop as soon as idle requested. The daemon is busy: {}", isBusy());
             if (isBusy()) {
                 beginStopping();
             } else {
-                stop();
+                stopNow("stop requested and daemon idle");
             }
         } finally {
             lock.unlock();
@@ -132,13 +145,17 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
      * @see #requestStop()
      */
     public void stop() {
+        stopNow("service stop");
+    }
+
+    private void stopNow(String reason) {
         lock.lock();
         try {
-            LOGGER.debug("Stop requested. The daemon is running a build: " + isBusy());
             switch (state) {
                 case Running:
                 case Broken:
                 case StopRequested:
+                    LOGGER.debug("Marking daemon stopped due to {}. The daemon is running a build: {}", reason, isBusy());
                     setState(State.Stopped);
                     break;
                 case Stopped:
@@ -166,30 +183,130 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
     }
 
     public void requestForcefulStop() {
+        LOGGER.debug("Daemon stop requested.");
+        stopNow("forceful stop requested");
+    }
+
+    public BuildCancellationToken getCancellationToken() {
+        return cancellationToken;
+    }
+
+    private void updateCancellationToken() {
+        cancellationToken = new DefaultBuildCancellationToken();
+    }
+
+    public void cancelBuild() {
+        long waitUntil = System.currentTimeMillis() + cancelTimeoutMs;
+        Date expiry = new Date(waitUntil);
+        LOGGER.debug("Cancel requested: will wait for daemon to become idle.");
+        try {
+            cancellationToken.doCancel();
+        } catch (Exception ex) {
+            LOGGER.error("Cancel processing failed. Will continue.", ex);
+        }
+
         lock.lock();
         try {
-            try {
-                if (onDisconnect != null) {
-                    onDisconnect.run();
+            while (System.currentTimeMillis() < waitUntil) {
+                try {
+                    switch (state) {
+                        case Running:
+                            if (isIdle()) {
+                                LOGGER.debug("Cancel: daemon is idle now.");
+                                return;
+                            }
+                            // fall-through
+                        case StopRequested:
+                            LOGGER.debug("Cancel: daemon is busy, sleeping until state changes.");
+                            condition.awaitUntil(expiry);
+                            break;
+                        case Broken:
+                            throw new IllegalStateException("This daemon is in a broken state.");
+                        case Stopped:
+                            LOGGER.debug("Cancel: daemon has stopped.");
+                            return;
+                    }
+                } catch (InterruptedException e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
                 }
-            } finally {
-                stop();
             }
+            LOGGER.debug("Cancel: daemon is still busy after grace period. Will force stop.");
+            stopNow("cancel requested");
         } finally {
             lock.unlock();
         }
     }
 
-    public void runCommand(Runnable command, String commandDisplayName, Runnable onCommandAbandoned) throws DaemonUnavailableException {
-        onStartCommand(commandDisplayName, onCommandAbandoned);
+    public void runCommand(final Runnable command, String commandDisplayName) throws DaemonUnavailableException {
+        onStartCommand(commandDisplayName);
         try {
-            command.run();
+            executor.execute(new Runnable() {
+                public void run() {
+                    try {
+                        command.run();
+                        onCommandSuccessful();
+                    } catch (Throwable t) {
+                        onCommandFailed(t);
+                    }
+                }
+            });
+            waitForCommandCompletion();
         } finally {
             onFinishCommand();
         }
     }
 
-    private void onStartCommand(String commandDisplayName, Runnable onDisconnect) {
+    private void waitForCommandCompletion() {
+        lock.lock();
+        try {
+            while ((state == State.Running || state == State.StopRequested) && result == null) {
+                try {
+                    condition.await();
+                } catch (InterruptedException e) {
+                    throw UncheckedException.throwAsUncheckedException(e);
+                }
+            }
+            LOGGER.debug("Command execution: finished waiting for {}. Result {} with state {}", currentCommandExecution, result, state);
+            if (result instanceof Throwable) {
+                throw UncheckedException.throwAsUncheckedException((Throwable) result);
+            }
+            if (result != null) {
+                return;
+            }
+            switch (state) {
+                case Stopped:
+                    throw new DaemonStoppedException();
+                case Broken:
+                    throw new DaemonUnavailableException("This daemon is broken and will stop.");
+                default:
+                    throw new IllegalStateException("Daemon is in unexpected state: " + state);
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void onCommandFailed(Throwable failure) {
+        lock.lock();
+        try {
+            result = failure;
+            condition.signalAll();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void onCommandSuccessful() {
+        lock.lock();
+        try {
+            result = this;
+            condition.signalAll();
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void onStartCommand(String commandDisplayName) {
         lock.lock();
         try {
             switch (state) {
@@ -204,12 +321,13 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
                 throw new DaemonUnavailableException(String.format("This daemon is currently executing: %s", currentCommandExecution));
             }
 
-            LOGGER.debug("onStartCommand({}) called after {} minutes of idle", commandDisplayName, getIdleMinutes());
+            LOGGER.debug("Command execution: started {} after {} minutes of idle", commandDisplayName, getIdleMinutes());
             try {
                 onStartCommand.run();
                 currentCommandExecution = commandDisplayName;
-                this.onDisconnect = onDisconnect;
+                result = null;
                 updateActivityTimestamp();
+                updateCancellationToken();
                 condition.signalAll();
             } catch (Throwable throwable) {
                 setState(State.Broken);
@@ -223,10 +341,9 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
     private void onFinishCommand() {
         lock.lock();
         try {
-            String execution = currentCommandExecution;
-            LOGGER.debug("onFinishCommand() called while execution = {}", execution);
+            LOGGER.debug("Command execution: completed {}", currentCommandExecution);
             currentCommandExecution = null;
-            onDisconnect = null;
+            result = null;
             updateActivityTimestamp();
             switch (state) {
                 case Running:
@@ -239,7 +356,7 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
                     }
                     break;
                 case StopRequested:
-                    stop();
+                    stopNow("command completed and stop requested");
                     break;
                 case Stopped:
                     break;
@@ -274,15 +391,15 @@ public class DaemonStateCoordinator implements Stoppable, DaemonStateControl {
         return state == State.Stopped;
     }
 
-    boolean isStoppingOrStopped() {
-        return state == State.Stopped || state == State.StopRequested;
+    boolean isWillRefuseNewCommands() {
+        return state != State.Running;
     }
 
     boolean isIdle() {
-        return currentCommandExecution == null;
+        return state == State.Running && currentCommandExecution == null;
     }
 
     boolean isBusy() {
-        return !isIdle();
+        return state == State.Running && currentCommandExecution != null;
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStoppedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStoppedException.java
deleted file mode 100644
index bd097c2..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonStoppedException.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.launcher.daemon.server;
-
-public class DaemonStoppedException extends RuntimeException {
-    public DaemonStoppedException(String operationDisplayName) {
-        super(toMessage(operationDisplayName));
-    }
-
-    private static String toMessage(String operationDisplayName) {
-        if (operationDisplayName == null) {
-            return "daemon explicitly stopped while idle";
-        } else {
-            return String.format("daemon explicitly stopped while busy, execution when stopped = %s", operationDisplayName);
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonTcpServerConnector.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonTcpServerConnector.java
index cc8f745..2cb2340 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonTcpServerConnector.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DaemonTcpServerConnector.java
@@ -17,7 +17,7 @@ package org.gradle.launcher.daemon.server;
 
 import org.gradle.api.Action;
 import org.gradle.internal.concurrent.CompositeStoppable;
-import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.id.UUIDGenerator;
 import org.gradle.messaging.remote.Address;
 import org.gradle.messaging.remote.ConnectionAcceptor;
@@ -40,10 +40,10 @@ public class DaemonTcpServerConnector implements DaemonServerConnector {
     private final Lock lifecycleLock = new ReentrantLock();
     private ConnectionAcceptor acceptor;
 
-    public DaemonTcpServerConnector() {
+    public DaemonTcpServerConnector(ExecutorFactory executorFactory, InetAddressFactory inetAddressFactory) {
         this.incomingConnector = new TcpIncomingConnector(
-                new DefaultExecutorFactory(),
-                new InetAddressFactory(),
+                executorFactory,
+                inetAddressFactory,
                 new UUIDGenerator()
         );
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultDaemonConnection.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultDaemonConnection.java
index 1bb1daf..0bd94c0 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultDaemonConnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultDaemonConnection.java
@@ -22,8 +22,8 @@ import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.concurrent.StoppableExecutor;
 import org.gradle.launcher.daemon.protocol.*;
-import org.gradle.launcher.daemon.server.exec.DaemonConnection;
-import org.gradle.launcher.daemon.server.exec.StdinHandler;
+import org.gradle.launcher.daemon.server.api.DaemonConnection;
+import org.gradle.launcher.daemon.server.api.StdinHandler;
 import org.gradle.logging.internal.OutputEvent;
 import org.gradle.messaging.remote.internal.Connection;
 import org.slf4j.Logger;
@@ -43,12 +43,14 @@ public class DefaultDaemonConnection implements DaemonConnection {
     private final StoppableExecutor executor;
     private final StdinQueue stdinQueue;
     private final DisconnectQueue disconnectQueue;
+    private final CancelQueue cancelQueue;
     private final ReceiveQueue receiveQueue;
 
     public DefaultDaemonConnection(final Connection<Object> connection, ExecutorFactory executorFactory) {
         this.connection = connection;
         stdinQueue = new StdinQueue(executorFactory);
         disconnectQueue = new DisconnectQueue();
+        cancelQueue = new CancelQueue(executorFactory);
         receiveQueue = new ReceiveQueue();
         executor = executorFactory.create("Handler for " + connection.toString());
         executor.execute(new Runnable() {
@@ -69,16 +71,20 @@ public class DefaultDaemonConnection implements DaemonConnection {
                             return;
                         }
 
-                        if (!(message instanceof IoCommand)) {
-                            LOGGER.debug("Received non-IO message from client: {}", message);
-                            receiveQueue.add(message);
-                        } else {
+                        if (message instanceof IoCommand) {
                             LOGGER.debug("Received IO message from client: {}", message);
                             stdinQueue.add((IoCommand) message);
+                        } else if (message instanceof Cancel) {
+                            LOGGER.debug("Received cancel message from client: {}", message);
+                            cancelQueue.add((Cancel) message);
+                        } else {
+                            LOGGER.debug("Received non-IO message from client: {}", message);
+                            receiveQueue.add(message);
                         }
                     }
                 } finally {
                     stdinQueue.disconnect();
+                    cancelQueue.disconnect();
                     disconnectQueue.disconnect();
                     receiveQueue.disconnect(failure);
                 }
@@ -94,6 +100,10 @@ public class DefaultDaemonConnection implements DaemonConnection {
         disconnectQueue.useHandler(handler);
     }
 
+    public void onCancel(Runnable handler) {
+        cancelQueue.useHandler(handler);
+    }
+
     public Object receive(long timeoutValue, TimeUnit timeoutUnits) {
         return receiveQueue.take(timeoutValue, timeoutUnits);
     }
@@ -110,6 +120,11 @@ public class DefaultDaemonConnection implements DaemonConnection {
         connection.dispatch(logEvent);
     }
 
+    @Override
+    public void event(Object event) {
+        connection.dispatch(new BuildEvent(event));
+    }
+
     public void completed(Result result) {
         connection.dispatch(result);
     }
@@ -120,19 +135,21 @@ public class DefaultDaemonConnection implements DaemonConnection {
         // 3. Stop receiving incoming messages. Blocks until the receive thread has finished. This will notify the stdin and receive queues to signal end of input.
         // 4. Stop the receive queue, to unblock any threads blocked in receive().
         // 5. Stop handling stdin. Blocks until the handler has finished. Discards any queued input.
-        CompositeStoppable.stoppable(disconnectQueue, connection, executor, receiveQueue, stdinQueue).stop();
+        CompositeStoppable.stoppable(disconnectQueue, connection, executor, receiveQueue, stdinQueue, cancelQueue).stop();
     }
 
-    private static class StdinQueue implements Stoppable {
+    private static abstract class CommandQueue<C extends Command, H> implements Stoppable {
         private final Lock lock = new ReentrantLock();
         private final Condition condition = lock.newCondition();
-        private final LinkedList<IoCommand> stdin = new LinkedList<IoCommand>();
+        protected final LinkedList<C> queue = new LinkedList<C>();
+        private final String name;
         private StoppableExecutor executor;
         private boolean removed;
         private final ExecutorFactory executorFactory;
 
-        private StdinQueue(ExecutorFactory executorFactory) {
+        private CommandQueue(ExecutorFactory executorFactory, String name) {
             this.executorFactory = executorFactory;
+            this.name = name;
         }
 
         public void stop() {
@@ -148,17 +165,17 @@ public class DefaultDaemonConnection implements DaemonConnection {
             }
         }
 
-        public void add(IoCommand command) {
+        public void add(C command) {
             lock.lock();
             try {
-                stdin.add(command);
+                queue.add(command);
                 condition.signalAll();
             } finally {
                 lock.unlock();
             }
         }
 
-        public void useHandler(final StdinHandler handler) {
+        public void useHandler(final H handler) {
             if (handler != null) {
                 startConsuming(handler);
             } else {
@@ -166,11 +183,11 @@ public class DefaultDaemonConnection implements DaemonConnection {
             }
         }
 
-        private void stopConsuming() {
+        protected void stopConsuming() {
             StoppableExecutor executor;
             lock.lock();
             try {
-                stdin.clear();
+                queue.clear();
                 removed = true;
                 condition.signalAll();
                 executor = this.executor;
@@ -182,20 +199,20 @@ public class DefaultDaemonConnection implements DaemonConnection {
             }
         }
 
-        private void startConsuming(final StdinHandler handler) {
+        protected void startConsuming(final H handler) {
             lock.lock();
             try {
                 if (executor != null) {
-                    throw new UnsupportedOperationException("Multiple stdin handlers not supported.");
+                    throw new UnsupportedOperationException("More instances of " + name + " not supported.");
                 }
-                executor = executorFactory.create("Stdin handler");
+                executor = executorFactory.create(name);
                 executor.execute(new Runnable() {
                     public void run() {
                         while (true) {
-                            IoCommand command;
+                            C command;
                             lock.lock();
                             try {
-                                while (!removed && stdin.isEmpty()) {
+                                while (!removed && queue.isEmpty()) {
                                     try {
                                         condition.await();
                                     } catch (InterruptedException e) {
@@ -205,19 +222,11 @@ public class DefaultDaemonConnection implements DaemonConnection {
                                 if (removed) {
                                     return;
                                 }
-                                command = stdin.removeFirst();
+                                command = queue.removeFirst();
                             } finally {
                                 lock.unlock();
                             }
-                            try {
-                                if (command instanceof CloseInput) {
-                                    handler.onEndOfInput();
-                                    return;
-                                } else {
-                                    handler.onInput((ForwardInput) command);
-                                }
-                            } catch (Exception e) {
-                                LOGGER.warn("Could not forward client stdin.", e);
+                            if (doHandleCommand(handler, command)) {
                                 return;
                             }
                         }
@@ -228,11 +237,15 @@ public class DefaultDaemonConnection implements DaemonConnection {
             }
         }
 
+        /** @return true if the queue should stop processing. */
+        protected abstract boolean doHandleCommand(final H handler, C command);
+        // Called under lock
+        protected abstract void doHandleDisconnect();
+
         public void disconnect() {
             lock.lock();
             try {
-                stdin.clear();
-                stdin.add(new CloseInput("<disconnected>"));
+                doHandleDisconnect();
                 condition.signalAll();
             } finally {
                 lock.unlock();
@@ -240,6 +253,56 @@ public class DefaultDaemonConnection implements DaemonConnection {
         }
     }
 
+    private static class StdinQueue extends CommandQueue<IoCommand, StdinHandler> {
+
+        private StdinQueue(ExecutorFactory executorFactory) {
+            super(executorFactory, "Stdin handler");
+        }
+
+        protected boolean doHandleCommand(final StdinHandler handler, IoCommand command) {
+            try {
+                if (command instanceof CloseInput) {
+                    handler.onEndOfInput();
+                    return true;
+                } else {
+                    handler.onInput((ForwardInput) command);
+                }
+            } catch (Exception e) {
+                LOGGER.warn("Could not forward client stdin.", e);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        protected void doHandleDisconnect() {
+            queue.clear();
+            queue.add(new CloseInput("<disconnected>"));
+        }
+    }
+
+    private static class CancelQueue extends CommandQueue<Cancel, Runnable> {
+
+        private CancelQueue(ExecutorFactory executorFactory) {
+            super(executorFactory, "Cancel handler");
+        }
+
+        @Override
+        protected boolean doHandleCommand(Runnable handler, Cancel command) {
+            try {
+                handler.run();
+            } catch (Exception e) {
+                LOGGER.warn("Could not process cancel request from client.", e);
+            }
+            return true;
+        }
+
+        @Override
+        protected void doHandleDisconnect() {
+            queue.clear();
+        }
+    }
+
     private static class DisconnectQueue implements Stoppable {
         private final Lock lock = new ReentrantLock();
         private final Condition condition = lock.newCondition();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultIncomingConnectionHandler.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultIncomingConnectionHandler.java
index 076b3cb..3bf4d35 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultIncomingConnectionHandler.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/DefaultIncomingConnectionHandler.java
@@ -18,17 +18,17 @@ package org.gradle.launcher.daemon.server;
 
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.concurrent.StoppableExecutor;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.launcher.daemon.protocol.Command;
 import org.gradle.launcher.daemon.protocol.DaemonFailure;
 import org.gradle.launcher.daemon.server.exec.DaemonCommandExecuter;
-import org.gradle.launcher.daemon.server.exec.DaemonConnection;
-import org.gradle.launcher.daemon.server.exec.DaemonStateControl;
+import org.gradle.launcher.daemon.server.api.DaemonConnection;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
 import org.gradle.messaging.remote.internal.Connection;
 
 import java.util.HashSet;
@@ -141,10 +141,8 @@ public class DefaultIncomingConnectionHandler implements IncomingConnectionHandl
                 LOGGER.info("Received command: {}.", command);
                 return command;
             } catch (Throwable e) {
-                String message = String.format("Unable to receive command from connection: '%s'", connection);
-                LOGGER.warn(message + ". Dispatching the failure to the daemon client...", e);
-                daemonConnection.completed(new DaemonFailure(new RuntimeException(message, e)));
-                //TODO SF exception handling / send typed exception / refactor / unit test and apply the same for below
+                LOGGER.warn(String.format("Unable to receive command from %s. Dispatching the failure to the daemon client", connection), e);
+                daemonConnection.completed(new DaemonFailure(e));
                 return null;
             }
         }
@@ -152,15 +150,10 @@ public class DefaultIncomingConnectionHandler implements IncomingConnectionHandl
         private void handleCommand(Command command, DaemonConnection daemonConnection) {
             LOGGER.debug(DaemonMessages.STARTED_EXECUTING_COMMAND + command + " with connection: " + connection + ".");
             try {
-                commandExecuter.executeCommand(daemonConnection, command, daemonContext, daemonStateControl, new Runnable() {
-                    public void run() {
-                        onFinishHandling(connection);
-                    }
-                });
+                commandExecuter.executeCommand(daemonConnection, command, daemonContext, daemonStateControl);
             } catch (Throwable e) {
-                String message = String.format("Uncaught exception when executing command: '%s' from connection: '%s'.", command, connection);
-                LOGGER.warn(message + ". Dispatching the failure to the daemon client...", e);
-                daemonConnection.completed(new DaemonFailure(new RuntimeException(message, e)));
+                LOGGER.warn(String.format("Unable to execute command %s from %s. Dispatching the failure to the daemon client", command, connection), e);
+                daemonConnection.completed(new DaemonFailure(e));
             } finally {
                 LOGGER.debug(DaemonMessages.FINISHED_EXECUTING_COMMAND + command);
             }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandAction.java
new file mode 100644
index 0000000..567440a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandAction.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.launcher.daemon.server.api;
+
+/**
+ * An action that operations as part of a command execution.
+ * <p>
+ * Implementations must be multiple use and threadsafe.
+ */
+public interface DaemonCommandAction {
+
+    /**
+     * Executes this action.
+     * <p>
+     * The execution object is a kind of continuation and also carries the “result” of the action.
+     * For example, if an exception arises as part of actioning the command, the exception should be
+     * set on the execution object and not thrown. The implication of this is that any exceptions
+     * thrown by DaemonCommandAction implementations are programming errors in the implementation
+     * and not something like a build failure if the daemon command is to run a build.
+     */
+    void execute(DaemonCommandExecution execution);
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandExecution.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandExecution.java
new file mode 100644
index 0000000..994288a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonCommandExecution.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.server.api;
+
+import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.protocol.BuildAndStop;
+import org.gradle.launcher.daemon.protocol.Command;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A continuation style object used to model the execution of a command.
+ * <p>
+ * Facilitates processing “chains”, making it easier to break up processing logic into discrete {@link org.gradle.launcher.daemon.server.api.DaemonCommandAction actions}.
+ * <p>
+ * The given actions will be executed in the order given to the constructor, and should use the {@link #proceed()} method to allow
+ * the next action to run. If an action does not call {@code proceed()}, it will be the last action that executes.
+ */
+public class DaemonCommandExecution {
+
+    final private DaemonConnection connection;
+    final private Command command;
+    final private DaemonContext daemonContext;
+    final private DaemonStateControl daemonStateControl;
+    final private List<DaemonCommandAction> actions;
+
+    private Throwable exception;
+    private Object result;
+
+    public DaemonCommandExecution(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl, List<DaemonCommandAction> actions) {
+        this.connection = connection;
+        this.command = command;
+        this.daemonContext = daemonContext;
+        this.daemonStateControl = daemonStateControl;
+
+        this.actions = new LinkedList<DaemonCommandAction>(actions);
+    }
+
+    public DaemonConnection getConnection() {
+        return connection;
+    }
+
+    /**
+     * The command to execute.
+     * <p>
+     * If the client disconnects before sending a command, this <b>will</b> be {@code null}.
+     */
+    public Command getCommand() {
+        return command;
+    }
+
+    public DaemonContext getDaemonContext() {
+        return daemonContext;
+    }
+
+    public DaemonStateControl getDaemonStateControl() {
+        return daemonStateControl;
+    }
+
+    /**
+     * Sets what is to be considered the result of executing the command.
+     * <p>
+     * This may be called multiple times to do things like wrap the result in another type.
+     */
+    public void setResult(Object result) {
+        this.result = result;
+    }
+
+    /**
+     * The currently nominated result for the execution.
+     * <p>
+     * If {@link #getException()} returns non null, the actual “result” of executing the command should be considered
+     * to be that exception and not what is returned by this method.
+     * <p>
+     * May be null if no action has set the result yet.
+     */
+    public Object getResult() {
+        return this.result;
+    }
+
+    /**
+     * If an exception happens in actioning the command that is to be expected (e.g. a build failure or error in the build)
+     */
+    public void setException(Throwable exception) {
+        this.exception = exception;
+    }
+
+    /**
+     * The currently nominated error that occurred during executing the commmand.
+     */
+    public Throwable getException() {
+        return this.exception;
+    }
+
+    /**
+     * Continues (or starts) execution.
+     * <p>
+     * Each action should call this method if it determines that execution should continue.
+     *
+     * @return true if execution did occur, false if this execution has already occurred.
+     */
+    public boolean proceed() {
+        if (actions.isEmpty()) {
+            return false;
+        } else {
+            actions.remove(0).execute(this);
+            return true;
+        }
+    }
+
+    /**
+     * Informs if this execution is of single-use-daemon type
+     */
+    public boolean isSingleUseDaemon() {
+        return getCommand() instanceof BuildAndStop;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("DaemonCommandExecution[command = %s, connection = %s]", command, connection);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonConnection.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonConnection.java
new file mode 100644
index 0000000..89b94d3
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonConnection.java
@@ -0,0 +1,109 @@
+/*
+ * 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.launcher.daemon.server.api;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.launcher.daemon.protocol.BuildStarted;
+import org.gradle.launcher.daemon.protocol.DaemonUnavailable;
+import org.gradle.launcher.daemon.protocol.Result;
+import org.gradle.logging.internal.OutputEvent;
+
+import java.util.concurrent.TimeUnit;
+
+public interface DaemonConnection extends Stoppable {
+    /**
+     * Registers a handler for incoming client stdin. The handler is notified from at most one thread at a time.
+     *
+     * The following events trigger an end of input:
+     * <ul>
+     * <li>A {@link org.gradle.launcher.daemon.protocol.CloseInput} event received from the client.</li>
+     * <li>When the client connection disconnects unexpectedly.</li>
+     * <li>When the connection is closed using {@link #stop()}.</li>
+     * </ul>
+     *
+     * Note: the end of input may be signalled from another thread before this method returns.
+     *
+     * @param handler the handler. Use null to remove the current handler.
+     */
+    void onStdin(@Nullable StdinHandler handler);
+
+    /**
+     * Registers a handler for when this connection is disconnected unexpectedly.. The handler is notified at most once.
+     *
+     * The handler is not notified after any of the following occurs:
+     * <ul>
+     * <li>When the connection is closed using {@link #stop()}.</li>
+     * </ul>
+     *
+     * Note: the handler may be run from another thread before this method returns.
+     *
+     * @param handler the handler. Use null to remove the current handler.
+     */
+    void onDisconnect(@Nullable Runnable handler);
+
+    /**
+     * Registers a handler for when this connection receives cancel command. The handler is notified at most once.
+     *
+     * The handler is not notified after any of the following occurs:
+     * <ul>
+     * <li>When the connection is closed using {@link #stop()}.</li>
+     * </ul>
+     *
+     * Note: the handler may be run from another thread before this method returns.
+     *
+     * @param handler the handler. Use null to remove the current handler.
+     */
+    void onCancel(@Nullable Runnable handler);
+
+    /**
+     * Dispatches a daemon unavailable message to the client.
+     */
+    void daemonUnavailable(DaemonUnavailable unavailable);
+
+    /**
+     * Dispatches a build started message to the client.
+     */
+    void buildStarted(BuildStarted buildStarted);
+
+    /**
+     * Dispatches a log event message to the client.
+     */
+    void logEvent(OutputEvent logEvent);
+
+    /**
+     * Dispatches some build event to the client.
+     */
+    void event(Object event);
+
+    /**
+     * Dispatches the given result to the client.
+     */
+    void completed(Result result);
+
+    /**
+     * Receives a message from the client. Does not include any stdin messages. Blocks until a message is received or the connection is closed or the timeout is reached.
+     *
+     * @return null On end of connection or timeout.
+     */
+    Object receive(long timeoutValue, TimeUnit timeoutUnits);
+
+    /**
+     * Blocks until all handlers have been notified and any queued messages have been dispatched to the client.
+     */
+    void stop();
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStateControl.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStateControl.java
new file mode 100644
index 0000000..d4b0678
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStateControl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.launcher.daemon.server.api;
+
+import org.gradle.initialization.BuildCancellationToken;
+
+public interface DaemonStateControl {
+    /**
+     * <p>Requests that the daemon stop, but wait until the daemon is idle. The stop will happen asynchronously, and this method does not block.
+     *
+     * <p>The daemon will stop accepting new work, so that subsequent calls to {@link #runCommand} will fail with {@link DaemonUnavailableException}.
+     */
+    void requestStop();
+
+    /**
+     * Requests a forceful stops of the daemon. Does not wait until the daemon is idle to begin stopping. The stop will happen asynchronously, and this method does not block.
+     *
+     * <p>If any long running command is currently running, the blocked call to {@link #runCommand} will fail with {@link DaemonStoppedException}.</p>
+     *
+     * <p>The daemon will stop accepting new work, so that subsequent calls to {@link #runCommand} will failing with {@link DaemonUnavailableException}.
+     */
+    void requestForcefulStop();
+
+    /**
+     * Communicates a request for build cancellation. Note that this method blocks until the operation has been cancelled.
+     *
+     * <p>If any long running command is currently running, this method does block for certain time to give chance to perform cancellation, and if the command
+     * doesn't finnish in a timely manner a request for forceful stop will be issued ({@link #requestForcefulStop()}.</p>
+     */
+    void cancelBuild();
+
+    /**
+     * Returns a cancellation token used to communicate cancel requests to commands processed in this daemon.
+     *
+     * @return Created cancellation token associated with currently running command or an arbitrary instance if no command is running.
+     */
+    BuildCancellationToken getCancellationToken();
+
+    /**
+     * Runs the given long running command. No more than 1 command may be running at any given time.
+     *
+     * @param command The command to run
+     * @param commandDisplayName The command's display name, used for logging and error messages.
+     *
+     * @throws DaemonUnavailableException When this daemon is unable to run the command, either because it is currently executing another command
+     * or is currently stopping.
+     * @throws DaemonStoppedException When this daemon started executing the command but was unable to complete it because the daemon is about to stop.
+     * The caller should note that the command may still be running at the time the method returns but should consider the command as abandoned.
+     */
+    void runCommand(Runnable command, String commandDisplayName) throws DaemonUnavailableException, DaemonStoppedException;
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStoppedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStoppedException.java
new file mode 100644
index 0000000..3b60c12
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonStoppedException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.api;
+
+/**
+ * Thrown when the daemon is stopped while running a command.
+ */
+public class DaemonStoppedException extends RuntimeException {
+    public static final String MESSAGE = "Gradle build daemon has been stopped.";
+
+    public DaemonStoppedException() {
+        super(MESSAGE);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonUnavailableException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonUnavailableException.java
new file mode 100644
index 0000000..693aae7
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/DaemonUnavailableException.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.launcher.daemon.server.api;
+
+/**
+ * Thrown when the daemon is unavailable to run any commands.
+ */
+public class DaemonUnavailableException extends RuntimeException {
+    public DaemonUnavailableException(String message) {
+        super(message);
+    }
+}
+
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/StdinHandler.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/StdinHandler.java
new file mode 100644
index 0000000..684e88a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/api/StdinHandler.java
@@ -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.launcher.daemon.server.api;
+
+import org.gradle.launcher.daemon.protocol.ForwardInput;
+
+public interface StdinHandler {
+    void onInput(ForwardInput input);
+
+    void onEndOfInput();
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/BuildCommandOnly.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/BuildCommandOnly.java
index 3e9518e..047698a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/BuildCommandOnly.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/BuildCommandOnly.java
@@ -17,6 +17,8 @@ package org.gradle.launcher.daemon.server.exec;
 
 import org.gradle.launcher.daemon.protocol.Build;
 import org.gradle.launcher.daemon.protocol.Command;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 
 /**
  * Superclass template for actions that only work for Build.
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/CatchAndForwardDaemonFailure.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/CatchAndForwardDaemonFailure.java
deleted file mode 100644
index 15a3ec0..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/CatchAndForwardDaemonFailure.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.launcher.daemon.server.exec;
-
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.launcher.daemon.protocol.DaemonFailure;
-
-/**
- * Wraps the rest of the command execution in a try catch in order to forward any internal
- * errors back to the client as DaemonFailure results.
- */
-public class CatchAndForwardDaemonFailure implements DaemonCommandAction {
-
-    private static final Logger LOGGER = Logging.getLogger(CatchAndForwardDaemonFailure.class);
-
-    public void execute(DaemonCommandExecution execution) {
-        try {
-            execution.proceed();
-        } catch (Throwable e) {
-            LOGGER.error(String.format("Daemon failure during execution of %s - ", execution.getCommand()), e);
-            execution.getConnection().completed(new DaemonFailure(e));
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandAction.java
deleted file mode 100644
index a023e98..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandAction.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.launcher.daemon.server.exec;
-
-/**
- * An action that operations as part of a command execution.
- * <p>
- * Implementations must be multiple use and threadsafe.
- */
-public interface DaemonCommandAction {
-
-    /**
-     * Executes this action.
-     * <p>
-     * The execution object is a kind of continuation and also carries the “result” of the action.
-     * For example, if an exception arises as part of actioning the command, the exception should be
-     * set on the execution object and not thrown. The implication of this is that any exceptions
-     * thrown by DaemonCommandAction implementations are programming errors in the implementation
-     * and not something like a build failure if the daemon command is to run a build.
-     */
-    void execute(DaemonCommandExecution execution);
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecuter.java
index 512e4e7..33b1dfd 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecuter.java
@@ -17,6 +17,8 @@ package org.gradle.launcher.daemon.server.exec;
 
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.protocol.Command;
+import org.gradle.launcher.daemon.server.api.DaemonConnection;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
 
 /**
  * An object capable of responding to commands sent to a daemon.
@@ -35,5 +37,5 @@ public interface DaemonCommandExecuter {
      * <p>
      * The {@code command} param may be {@code null}, which means the client disconnected before sending a command.
      */
-    void executeCommand(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl, Runnable commandAbandoned);
+    void executeCommand(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl);
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecution.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecution.java
deleted file mode 100644
index e93fe23..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonCommandExecution.java
+++ /dev/null
@@ -1,134 +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.launcher.daemon.server.exec;
-
-import org.gradle.launcher.daemon.context.DaemonContext;
-import org.gradle.launcher.daemon.protocol.Command;
-
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * A continuation style object used to model the execution of a command.
- * <p>
- * Facilitates processing “chains”, making it easier to break up processing logic into discrete {@link DaemonCommandAction actions}.
- * <p>
- * The given actions will be executed in the order given to the constructor, and should use the {@link #proceed()} method to allow
- * the next action to run. If an action does not call {@code proceed()}, it will be the last action that executes.
- */
-public class DaemonCommandExecution {
-
-    final private DaemonConnection connection;
-    final private Command command;
-    final private DaemonContext daemonContext;
-    final private DaemonStateControl daemonStateControl;
-    final private Runnable commandAbandoned;
-    final private List<DaemonCommandAction> actions;
-
-    private Throwable exception;
-    private Object result;
-
-    public DaemonCommandExecution(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl, Runnable commandAbandoned, List<DaemonCommandAction> actions) {
-        this.connection = connection;
-        this.command = command;
-        this.daemonContext = daemonContext;
-        this.daemonStateControl = daemonStateControl;
-        this.commandAbandoned = commandAbandoned;
-
-        this.actions = new LinkedList<DaemonCommandAction>(actions);
-    }
-
-    public DaemonConnection getConnection() {
-        return connection;
-    }
-
-    /**
-     * The command to execute.
-     * <p>
-     * If the client disconnects before sending a command, this <b>will</b> be {@code null}.
-     */
-    public Command getCommand() {
-        return command;
-    }
-
-    public DaemonContext getDaemonContext() {
-        return daemonContext;
-    }
-
-    public DaemonStateControl getDaemonStateControl() {
-        return daemonStateControl;
-    }
-
-    public Runnable getCommandAbandonedHandler() {
-        return commandAbandoned;
-    }
-
-    /**
-     * Sets what is to be considered the result of executing the command.
-     * <p>
-     * This may be called multiple times to do things like wrap the result in another type.
-     */
-    public void setResult(Object result) {
-        this.result = result;
-    }
-
-    /**
-     * The currently nominated result for the execution.
-     * <p>
-     * If {@link #getException()} returns non null, the actual “result” of executing the command should be considered
-     * to be that exception and not what is returned by this method.
-     * <p>
-     * May be null if no action has set the result yet.
-     */
-    public Object getResult() {
-        return this.result;
-    }
-
-    /**
-     * If an exception happens in actioning the command that is to be expected (e.g. a build failure or error in the build)
-     */
-    public void setException(Throwable exception) {
-        this.exception = exception;
-    }
-
-    /**
-     * The currently nominated error that occurred during executing the commmand.
-     */
-    public Throwable getException() {
-        return this.exception;
-    }
-
-    /**
-     * Continues (or starts) execution.
-     * <p>
-     * Each action should call this method if it determines that execution should continue.
-     *
-     * @return true if execution did occur, false if this execution has already occurred.
-     */
-    public boolean proceed() {
-        if (actions.isEmpty()) {
-            return false;
-        } else {
-            actions.remove(0).execute(this);
-            return true;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return String.format("DaemonCommandExecution[command = %s, connection = %s]", command, connection);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonConnection.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonConnection.java
deleted file mode 100644
index ec3df45..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonConnection.java
+++ /dev/null
@@ -1,90 +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.launcher.daemon.server.exec;
-
-import org.gradle.api.Nullable;
-import org.gradle.internal.concurrent.Stoppable;
-import org.gradle.launcher.daemon.protocol.BuildStarted;
-import org.gradle.launcher.daemon.protocol.DaemonUnavailable;
-import org.gradle.launcher.daemon.protocol.Result;
-import org.gradle.logging.internal.OutputEvent;
-
-import java.util.concurrent.TimeUnit;
-
-public interface DaemonConnection extends Stoppable {
-    /**
-     * Registers a handler for incoming client stdin. The handler is notified from at most one thread at a time.
-     *
-     * The following events trigger and end of input:
-     * <ul>
-     * <li>A {@link org.gradle.launcher.daemon.protocol.CloseInput} event received from the client.</li>
-     * <li>When the client connection disconnects unexpectedly.</li>
-     * <li>When the connection is closed using {@link #stop()}.</li>
-     * </ul>
-     *
-     * Note: the end of input may be signalled from another thread before this method returns.
-     *
-     * @param handler the handler. Use null to remove the current handler.
-     */
-    void onStdin(@Nullable StdinHandler handler);
-
-    /**
-     * Registers a handler for when this connection is disconnected unexpectedly.. The handler is notified at most once.
-     *
-     * The handler is not notified after any of the following occurs:
-     * <ul>
-     * <li>When the connection is closed using {@link #stop()}.</li>
-     * </ul>
-     *
-     * Note: the handler may be run from another thread before this method returns.
-     *
-     * @param handler the handler. Use null to remove the current handler.
-     */
-    void onDisconnect(@Nullable Runnable handler);
-
-    /**
-     * Dispatches a daemon unavailable message to the client.
-     */
-    void daemonUnavailable(DaemonUnavailable unavailable);
-
-    /**
-     * Dispatches a build started message to the client.
-     */
-    void buildStarted(BuildStarted buildStarted);
-
-    /**
-     * Dispatches a log event message to the client.
-     */
-    void logEvent(OutputEvent logEvent);
-
-    /**
-     * Dispatches the given result to the client.
-     */
-    void completed(Result result);
-
-    /**
-     * Receives a message from the client. Does not include any stdin messages. Blocks until a message is received or the connection is closed or the timeout is reached.
-     *
-     * @return null On end of connection or timeout.
-     */
-    Object receive(long timeoutValue, TimeUnit timeoutUnits);
-
-    /**
-     * Blocks until all handlers have been notified and any queued messages have been dispatched to the client.
-     */
-    void stop();
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonHygieneAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonHygieneAction.java
deleted file mode 100644
index fb9b40d..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonHygieneAction.java
+++ /dev/null
@@ -1,50 +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.launcher.daemon.server.exec;
-
-import org.gradle.internal.TimeProvider;
-import org.gradle.internal.TrueTimeProvider;
-
-public class DaemonHygieneAction implements DaemonCommandAction {
-
-    private final long gcDelay;
-    private TimeProvider timeProvider;
-    private long nextGcHint;
-
-    public DaemonHygieneAction() {
-        //by default, don't hint for gc more often than once per 2 minutes
-        //because it is a full scan
-        this(1000 * 60 * 2, new TrueTimeProvider());
-    }
-
-    DaemonHygieneAction(long gcDelay, TimeProvider timeProvider) {
-        this.gcDelay = gcDelay;
-        this.timeProvider = timeProvider;
-    }
-
-    public void execute(DaemonCommandExecution execution) {
-        execution.proceed();
-        long time = timeProvider.getCurrentTime();
-        if (time > nextGcHint) {
-            gc();
-            nextGcHint = time + gcDelay;
-        }
-    }
-
-    void gc() {
-        System.gc();
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonStateControl.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonStateControl.java
deleted file mode 100644
index 6ea21a2..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonStateControl.java
+++ /dev/null
@@ -1,46 +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.launcher.daemon.server.exec;
-
-public interface DaemonStateControl {
-    /**
-     * <p>Requests that the daemon stop, but wait until the daemon is idle. The stop will happen asynchronously, and this method does not block.
-     *
-     * <p>The daemon will stop accepting new work, so that subsequent calls to {@link #runCommand} will failing with {@link DaemonUnavailableException}.
-     */
-    void requestStop();
-
-    /**
-     * Requests a forceful stops of the daemon. Does not wait until the daemon is idle to begin stopping. The stop will happen asynchronously, and this method does not block.
-     *
-     * <p>If any long running command is currently running, the operation's abandoned command handler will be executed.</p>
-     *
-     * <p>The daemon will stop accepting new work, so that subsequent calls to {@link #runCommand} will failing with {@link DaemonUnavailableException}.
-     */
-    void requestForcefulStop();
-
-    /**
-     * Runs the given long running command. No more than 1 command may be running at any given time.
-     *
-     * @param command The command to run
-     * @param commandDisplayName The command's display name, used for logging and error messages.
-     * @param onCommandAbandoned An action to run with a forceful stop is requested using {@link #requestForcefulStop()}, to notify the caller that the operation is being abandoned.
-     *
-     * @throws DaemonUnavailableException If this daemon is currently executing another command or a stop has been requested.
-     */
-    void runCommand(Runnable command, String commandDisplayName, Runnable onCommandAbandoned) throws DaemonUnavailableException;
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonUnavailableException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonUnavailableException.java
deleted file mode 100644
index 0855bc9..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DaemonUnavailableException.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.launcher.daemon.server.exec;
-
-/**
- * Thrown when the daemon is unavailable to run any commands.
- */
-public class DaemonUnavailableException extends RuntimeException {
-    public DaemonUnavailableException(String message) {
-        super(message);
-    }
-}
-
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DefaultDaemonCommandExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DefaultDaemonCommandExecuter.java
index c3e0adb..78967f2 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DefaultDaemonCommandExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/DefaultDaemonCommandExecuter.java
@@ -15,17 +15,22 @@
  */
 package org.gradle.launcher.daemon.server.exec;
 
-import org.gradle.initialization.GradleLauncherFactory;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import com.google.common.collect.ImmutableList;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
 import org.gradle.launcher.daemon.context.DaemonContext;
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
 import org.gradle.launcher.daemon.protocol.Command;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+import org.gradle.launcher.daemon.server.api.DaemonConnection;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
+import org.gradle.launcher.daemon.server.health.DaemonHealthServices;
+import org.gradle.launcher.exec.BuildActionExecuter;
+import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.internal.LoggingOutputInternal;
 
 import java.io.File;
-import java.util.Arrays;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -33,46 +38,45 @@ import java.util.List;
  */
 public class DefaultDaemonCommandExecuter implements DaemonCommandExecuter {
     private final LoggingOutputInternal loggingOutput;
-    private final GradleLauncherFactory launcherFactory;
-    private DaemonCommandAction hygieneAction;
+    private final BuildActionExecuter<BuildActionParameters> actionExecuter;
+    private final DaemonHealthServices healthServices;
     private final ProcessEnvironment processEnvironment;
     private final File daemonLog;
 
-    public DefaultDaemonCommandExecuter(GradleLauncherFactory launcherFactory, ProcessEnvironment processEnvironment,
-                                        LoggingManagerInternal loggingOutput, File daemonLog, DaemonCommandAction hygieneAction) {
+    public DefaultDaemonCommandExecuter(BuildActionExecuter<BuildActionParameters> actionExecuter, ProcessEnvironment processEnvironment,
+                                        LoggingManagerInternal loggingOutput, File daemonLog, DaemonHealthServices healthServices) {
         this.processEnvironment = processEnvironment;
         this.daemonLog = daemonLog;
         this.loggingOutput = loggingOutput;
-        this.launcherFactory = launcherFactory;
-        this.hygieneAction = hygieneAction;
+        this.actionExecuter = actionExecuter;
+        this.healthServices = healthServices;
     }
 
-    public void executeCommand(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl, Runnable commandAbandoned) {
+    public void executeCommand(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl) {
         new DaemonCommandExecution(
             connection,
             command,
             daemonContext,
             daemonStateControl,
-            commandAbandoned,
             createActions(daemonContext)
         ).proceed();
     }
 
     protected List<DaemonCommandAction> createActions(DaemonContext daemonContext) {
         DaemonDiagnostics daemonDiagnostics = new DaemonDiagnostics(daemonLog, daemonContext.getPid());
-        return new LinkedList<DaemonCommandAction>(Arrays.asList(
-            new CatchAndForwardDaemonFailure(),
-            hygieneAction,
-            new HandleStop(),
-            new StartBuildOrRespondWithBusy(daemonDiagnostics),
-            new EstablishBuildEnvironment(processEnvironment),
-            new LogToClient(loggingOutput, daemonDiagnostics), // from this point down, logging is sent back to the client
-            new ForwardClientInput(),
-            new ReturnResult(),
-            new StartStopIfBuildAndStop(),
-            new ResetDeprecationLogger(),
-            new WatchForDisconnection(),
-            new ExecuteBuild(launcherFactory)
-        ));
+        return ImmutableList.of(
+                new HandleCancel(),
+                new ReturnResult(),
+                new StartBuildOrRespondWithBusy(daemonDiagnostics), // from this point down, the daemon is 'busy'
+                healthServices.getGCHintAction(), //TODO SF needs to happen after the result is returned to the client
+                new EstablishBuildEnvironment(processEnvironment),
+                new LogToClient(loggingOutput, daemonDiagnostics), // from this point down, logging is sent back to the client
+                healthServices.getHealthTrackerAction(),
+                new ForwardClientInput(),
+                new RequestStopIfSingleUsedDaemon(),
+                new ResetDeprecationLogger(),
+                new WatchForDisconnection(),
+                new ExecuteBuild(actionExecuter)
+        );
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/EstablishBuildEnvironment.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/EstablishBuildEnvironment.java
index 8fde07a..f50beb5 100755
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/EstablishBuildEnvironment.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/EstablishBuildEnvironment.java
@@ -18,12 +18,14 @@ package org.gradle.launcher.daemon.server.exec;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.SystemProperties;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
 import org.gradle.launcher.daemon.protocol.Build;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 import org.gradle.util.GFileUtils;
 
 import java.io.File;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 
@@ -46,9 +48,15 @@ public class EstablishBuildEnvironment extends BuildCommandOnly {
         File originalProcessDir = GFileUtils.canonicalise(new File("."));
 
         for (Map.Entry<String, String> entry : build.getParameters().getSystemProperties().entrySet()) {
-            if (SystemProperties.getStandardProperties().contains(entry.getKey())) { continue; }
-            if (SystemProperties.getNonStandardImportantProperties().contains(entry.getKey())) { continue; }
-            if (entry.getKey().startsWith("sun.")) { continue; }
+            if (SystemProperties.getInstance().getStandardProperties().contains(entry.getKey())) {
+                continue;
+            }
+            if (SystemProperties.getInstance().getNonStandardImportantProperties().contains(entry.getKey())) {
+                continue;
+            }
+            if (entry.getKey().startsWith("sun.")) {
+                continue;
+            }
             System.setProperty(entry.getKey(), entry.getValue());
         }
 
@@ -56,12 +64,16 @@ public class EstablishBuildEnvironment extends BuildCommandOnly {
         processEnvironment.maybeSetEnvironment(build.getParameters().getEnvVariables());
         processEnvironment.maybeSetProcessDir(build.getParameters().getCurrentDir());
 
+        // Capture and restore this in case the build code calls Locale.setDefault()
+        Locale locale = Locale.getDefault();
+
         try {
             execution.proceed();
         } finally {
             System.setProperties(originalSystemProperties);
             processEnvironment.maybeSetEnvironment(originalEnv);
             processEnvironment.maybeSetProcessDir(originalProcessDir);
+            Locale.setDefault(locale);
         }
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ExecuteBuild.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ExecuteBuild.java
index bf7deb3..4d51e0a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ExecuteBuild.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ExecuteBuild.java
@@ -17,10 +17,12 @@ package org.gradle.launcher.daemon.server.exec;
 
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.initialization.*;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.launcher.daemon.protocol.Build;
-import org.gradle.launcher.exec.InProcessBuildActionExecuter;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+import org.gradle.launcher.exec.BuildActionExecuter;
+import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.launcher.exec.ReportedException;
 
 /**
@@ -32,17 +34,20 @@ public class ExecuteBuild extends BuildCommandOnly {
 
     private static final Logger LOGGER = Logging.getLogger(ExecuteBuild.class);
     
-    final private GradleLauncherFactory launcherFactory;
+    final private BuildActionExecuter<BuildActionParameters> actionExecuter;
 
-    public ExecuteBuild(GradleLauncherFactory launcherFactory) {
-        this.launcherFactory = launcherFactory;
+    public ExecuteBuild(BuildActionExecuter<BuildActionParameters> actionExecuter) {
+        this.actionExecuter = actionExecuter;
     }
 
-    protected void doBuild(DaemonCommandExecution execution, Build build) {
+    protected void doBuild(final DaemonCommandExecution execution, Build build) {
+        LOGGER.debug(DaemonMessages.STARTED_BUILD);
         LOGGER.info("Executing build with daemon context: {}", execution.getDaemonContext());
-        InProcessBuildActionExecuter executer = new InProcessBuildActionExecuter(launcherFactory);
         try {
-            execution.setResult(executer.execute(build.getAction(), build.getParameters()));
+            BuildCancellationToken cancellationToken = execution.getDaemonStateControl().getCancellationToken();
+            BuildRequestContext buildRequestContext = new DefaultBuildRequestContext(build.getBuildRequestMetaData(), cancellationToken, new DaemonConnectionBackedEventConsumer(execution));
+            Object result = actionExecuter.execute(build.getAction(), buildRequestContext, build.getParameters());
+            execution.setResult(result);
         } catch (ReportedException e) {
             /*
                 We have to wrap in a ReportedException so the other side doesn't re-log this exception, because it's already
@@ -59,4 +64,16 @@ public class ExecuteBuild extends BuildCommandOnly {
         execution.proceed(); // ExecuteBuild should be the last action, but in case we want to decorate the result in the future
     }
 
+    private static class DaemonConnectionBackedEventConsumer implements BuildEventConsumer {
+        private final DaemonCommandExecution execution;
+
+        public DaemonConnectionBackedEventConsumer(DaemonCommandExecution execution) {
+            this.execution = execution;
+        }
+
+        @Override
+        public void dispatch(Object event) {
+            execution.getConnection().event(event);
+        }
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ForwardClientInput.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ForwardClientInput.java
index f36d81a..acb8889 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ForwardClientInput.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ForwardClientInput.java
@@ -20,6 +20,9 @@ import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.UncheckedException;
 import org.gradle.launcher.daemon.protocol.ForwardInput;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+import org.gradle.launcher.daemon.server.api.StdinHandler;
 import org.gradle.util.StdinSwapper;
 
 import java.io.IOException;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleCancel.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleCancel.java
new file mode 100644
index 0000000..1a84917
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleCancel.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.server.exec;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+
+/**
+ * Install a handler for cancel request processed asynchronously while the daemon proceeds to build execution.
+ */
+public class HandleCancel implements DaemonCommandAction {
+    private static final Logger LOGGER = Logging.getLogger(HandleCancel.class);
+
+    public void execute(final DaemonCommandExecution execution) {
+        execution.getConnection().onCancel(new Runnable() {
+            public void run() {
+                LOGGER.info("HandleCancel processing {}", execution.getCommand());
+                execution.getDaemonStateControl().cancelBuild();
+            }
+        });
+        try {
+            execution.proceed();
+        } finally {
+            execution.getConnection().onCancel(null);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleStop.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleStop.java
deleted file mode 100644
index 9434c8b..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/HandleStop.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.launcher.daemon.server.exec;
-
-import org.gradle.launcher.daemon.protocol.Stop;
-import org.gradle.launcher.daemon.protocol.Success;
-
-/**
- * If the command is a Stop, asks the daemon to stop asynchronously and does not proceed with execution.
- */
-public class HandleStop implements DaemonCommandAction {
-    public void execute(DaemonCommandExecution execution) {
-        if (execution.getCommand() instanceof Stop) {
-            /*
-                When the daemon was started through the DaemonMain entry point, this will cause the entire
-                JVM to exit with code 1 (which is what we want) because the call to requestStopOnIdleTimeout() in
-                DaemonMain#doAction will throw a DaemonStoppedException. Note that at this point we will also 
-                immediately tear down the client connection and remove the daemon from the registry.
-            */
-            execution.getDaemonStateControl().requestForcefulStop();
-            execution.getConnection().completed(new Success(null));
-        } else {
-            execution.proceed();
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
index 9d6c795..fb8e3f4 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
@@ -21,12 +21,14 @@ import org.gradle.api.logging.Logging;
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
 import org.gradle.launcher.daemon.logging.DaemonMessages;
 import org.gradle.launcher.daemon.protocol.Build;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 import org.gradle.logging.internal.LoggingOutputInternal;
 import org.gradle.logging.internal.OutputEvent;
 import org.gradle.logging.internal.OutputEventListener;
 
-class LogToClient extends BuildCommandOnly {
+public class LogToClient extends BuildCommandOnly {
 
+    public static final String DISABLE_OUTPUT = "org.gradle.daemon.disable-output";
     private static final Logger LOGGER = Logging.getLogger(LogToClient.class);
 
     private final LoggingOutputInternal loggingOutput;
@@ -38,6 +40,11 @@ class LogToClient extends BuildCommandOnly {
     }
 
     protected void doBuild(final DaemonCommandExecution execution, Build build) {
+        if (Boolean.getBoolean(DISABLE_OUTPUT)) {
+            execution.proceed();
+            return;
+        }
+
         final LogLevel buildLogLevel = build.getParameters().getLogLevel();
         OutputEventListener listener = new OutputEventListener() {
             public void onOutput(OutputEvent event) {
@@ -52,15 +59,14 @@ class LogToClient extends BuildCommandOnly {
             }
         };
 
-        LOGGER.info(DaemonMessages.ABOUT_TO_START_RELAYING_LOGS);
+        LOGGER.debug(DaemonMessages.ABOUT_TO_START_RELAYING_LOGS);
         loggingOutput.addOutputEventListener(listener);
-        LOGGER.info(DaemonMessages.STARTED_RELAYING_LOGS + diagnostics.getPid() + "). The daemon log file: " + diagnostics.getDaemonLog());
-
         try {
+            LOGGER.info(DaemonMessages.STARTED_RELAYING_LOGS + diagnostics.getPid() + "). The daemon log file: " + diagnostics.getDaemonLog());
             execution.proceed();
         } finally {
             loggingOutput.removeOutputEventListener(listener);
         }
-    } 
+    }
 }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/NoOpDaemonCommandAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/NoOpDaemonCommandAction.java
index 411a9b1..c7c8ebd 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/NoOpDaemonCommandAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/NoOpDaemonCommandAction.java
@@ -15,6 +15,9 @@
  */
 package org.gradle.launcher.daemon.server.exec;
 
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+
 public class NoOpDaemonCommandAction implements DaemonCommandAction {
     public void execute(DaemonCommandExecution execution) {
         execution.proceed();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/RequestStopIfSingleUsedDaemon.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/RequestStopIfSingleUsedDaemon.java
new file mode 100644
index 0000000..95b10b6
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/RequestStopIfSingleUsedDaemon.java
@@ -0,0 +1,36 @@
+/*
+ * 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.launcher.daemon.server.exec;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+
+public class RequestStopIfSingleUsedDaemon implements DaemonCommandAction {
+
+    private static final Logger LOGGER = Logging.getLogger(RequestStopIfSingleUsedDaemon.class);
+    
+    public void execute(DaemonCommandExecution execution) {
+        if (execution.isSingleUseDaemon()) {
+            LOGGER.debug("Requesting daemon stop after processing {}", execution.getCommand());
+            // Does not take effect until after execution has completed
+            execution.getDaemonStateControl().requestStop();
+        }
+        execution.proceed();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ResetDeprecationLogger.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ResetDeprecationLogger.java
index deba7a6..5ca9964 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ResetDeprecationLogger.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ResetDeprecationLogger.java
@@ -16,6 +16,8 @@
 
 package org.gradle.launcher.daemon.server.exec;
 
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 import org.gradle.util.DeprecationLogger;
 
 public class ResetDeprecationLogger implements DaemonCommandAction {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ReturnResult.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ReturnResult.java
index e311dd0..2205fdd 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ReturnResult.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/ReturnResult.java
@@ -20,6 +20,8 @@ import org.gradle.api.logging.Logging;
 import org.gradle.launcher.daemon.protocol.CommandFailure;
 import org.gradle.launcher.daemon.protocol.Result;
 import org.gradle.launcher.daemon.protocol.Success;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 
 /**
  * Handles sending the result of the execution back to the client.
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartBuildOrRespondWithBusy.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartBuildOrRespondWithBusy.java
index eee36f6..b9f19df 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartBuildOrRespondWithBusy.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartBuildOrRespondWithBusy.java
@@ -20,7 +20,12 @@ import org.gradle.api.logging.Logging;
 import org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics;
 import org.gradle.launcher.daemon.protocol.Build;
 import org.gradle.launcher.daemon.protocol.BuildStarted;
+import org.gradle.launcher.daemon.protocol.CommandFailure;
 import org.gradle.launcher.daemon.protocol.DaemonUnavailable;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException;
+import org.gradle.launcher.daemon.server.api.DaemonUnavailableException;
 
 /**
  * Updates the daemon idle/busy status, sending a DaemonUnavailable result back to the client if the daemon is busy.
@@ -40,16 +45,18 @@ public class StartBuildOrRespondWithBusy extends BuildCommandOnly {
         try {
             Runnable command = new Runnable() {
                 public void run() {
-                    LOGGER.info("Daemon is about to start building: " + build + ". Dispatching build started information...");
+                    LOGGER.info("Daemon is about to start building {}. Dispatching build started information...", build);
                     execution.getConnection().buildStarted(new BuildStarted(diagnostics));
                     execution.proceed();
                 }
             };
 
-            stateCoordinator.runCommand(command, execution.toString(), execution.getCommandAbandonedHandler());
+            stateCoordinator.runCommand(command, execution.toString());
         } catch (DaemonUnavailableException e) {
-            LOGGER.info("Daemon will not handle the request: {} because is unavailable: {}", build, e.getMessage());
+            LOGGER.info("Daemon will not handle the command {} because is unavailable: {}", build, e.getMessage());
             execution.getConnection().daemonUnavailable(new DaemonUnavailable(e.getMessage()));
+        } catch (DaemonStoppedException e) {
+            execution.getConnection().completed(new CommandFailure(e));
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartStopIfBuildAndStop.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartStopIfBuildAndStop.java
deleted file mode 100644
index b7c2c5b..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StartStopIfBuildAndStop.java
+++ /dev/null
@@ -1,35 +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.launcher.daemon.server.exec;
-
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.launcher.daemon.protocol.BuildAndStop;
-
-public class StartStopIfBuildAndStop implements DaemonCommandAction {
-
-    private static final Logger LOGGER = Logging.getLogger(StartStopIfBuildAndStop.class);
-    
-    public void execute(DaemonCommandExecution execution) {
-        if (execution.getCommand() instanceof BuildAndStop) {
-            LOGGER.debug("Requesting daemon stop after processing {}", execution.getCommand());
-            // Does not take effect until after execution has completed
-            execution.getDaemonStateControl().requestStop();
-        }
-        execution.proceed();
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StdinHandler.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StdinHandler.java
deleted file mode 100644
index 2151378..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StdinHandler.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.launcher.daemon.server.exec;
-
-import org.gradle.launcher.daemon.protocol.ForwardInput;
-
-public interface StdinHandler {
-    void onInput(ForwardInput input);
-
-    void onEndOfInput();
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StopHandlingCommandExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StopHandlingCommandExecuter.java
new file mode 100644
index 0000000..bedf326
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/StopHandlingCommandExecuter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.exec;
+
+import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.protocol.Command;
+import org.gradle.launcher.daemon.protocol.Stop;
+import org.gradle.launcher.daemon.protocol.StopWhenIdle;
+import org.gradle.launcher.daemon.protocol.Success;
+import org.gradle.launcher.daemon.server.api.DaemonConnection;
+import org.gradle.launcher.daemon.server.api.DaemonStateControl;
+
+public class StopHandlingCommandExecuter implements DaemonCommandExecuter {
+    private final DaemonCommandExecuter executer;
+
+    public StopHandlingCommandExecuter(DaemonCommandExecuter executer) {
+        this.executer = executer;
+    }
+
+    public void executeCommand(DaemonConnection connection, Command command, DaemonContext daemonContext, DaemonStateControl daemonStateControl) {
+        if (command instanceof Stop) {
+            daemonStateControl.requestForcefulStop();
+            connection.completed(new Success(null));
+        } else if (command instanceof StopWhenIdle) {
+            daemonStateControl.requestStop();
+            connection.completed(new Success(null));
+        } else {
+            executer.executeCommand(connection, command, daemonContext, daemonStateControl);
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/WatchForDisconnection.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/WatchForDisconnection.java
index 1f8fb1a..b8366d4 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/WatchForDisconnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/WatchForDisconnection.java
@@ -17,6 +17,8 @@ package org.gradle.launcher.daemon.server.exec;
 
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
 
 public class WatchForDisconnection implements DaemonCommandAction {
 
@@ -27,13 +29,6 @@ public class WatchForDisconnection implements DaemonCommandAction {
         execution.getConnection().onDisconnect(new Runnable() {
             public void run() {
                 LOGGER.warn("client disconnection detected, stopping the daemon");
-                
-                /*
-                    When the daemon was started through the DaemonMain entry point, this will cause the entire
-                    JVM to exit with code 1 (which is what we want) because the call to requestStopOnIdleTimeout() in
-                    DaemonMain#doAction will throw a DaemonStoppedException. Note that at this point we will also 
-                    immediately remove the daemon from the registry.
-                */
                 execution.getDaemonStateControl().requestForcefulStop();
             }
         });
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthServices.java
new file mode 100644
index 0000000..ca9e957
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthServices.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+
+public interface DaemonHealthServices {
+
+    /**
+     * gets the action that can perform gc hint after the build
+     */
+    DaemonCommandAction getGCHintAction();
+
+    /**
+     * gets the action that tracks daemon's health
+     */
+    DaemonCommandAction getHealthTrackerAction();
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthTracker.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthTracker.java
new file mode 100644
index 0000000..ba9a80c
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonHealthTracker.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+
+class DaemonHealthTracker implements DaemonCommandAction {
+
+    private final static Logger LOG = Logging.getLogger(DaemonHealthTracker.class);
+
+    private final DaemonStats stats;
+    private final DaemonStatus status;
+    private final HealthLogger logger;
+
+    DaemonHealthTracker(DaemonStats stats, DaemonStatus status, HealthLogger logger) {
+        this.stats = stats;
+        this.status = status;
+        this.logger = logger;
+    }
+
+    public void execute(DaemonCommandExecution execution) {
+        if (execution.isSingleUseDaemon()) {
+            execution.proceed();
+            return;
+        }
+
+        stats.buildStarted();
+        logger.logHealth(stats, LOG);
+        try {
+            execution.proceed();
+        } finally {
+            stats.buildFinished();
+        }
+
+        if(status.isDaemonTired(stats)) {
+            execution.getDaemonStateControl().requestStop();
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStats.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStats.java
new file mode 100644
index 0000000..a291c89
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStats.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.internal.TimeProvider;
+import org.gradle.internal.TrueTimeProvider;
+import org.gradle.internal.util.NumberUtil;
+import org.gradle.util.Clock;
+
+import static java.lang.String.format;
+
+class DaemonStats {
+
+    private final Clock totalTime;
+    private final TimeProvider timeProvider;
+    private final MemoryInfo memory;
+
+    private int buildCount;
+    private long currentBuildStart;
+    private long allBuildsTime;
+    private int currentPerformance;
+
+    DaemonStats() {
+        this(new Clock(), new TrueTimeProvider(), new MemoryInfo());
+    }
+
+    DaemonStats(Clock startTime, TimeProvider timeProvider, MemoryInfo memory) {
+        this.totalTime = startTime;
+        this.timeProvider = timeProvider;
+        this.memory = memory;
+    }
+
+    /**
+     * Informs the stats that build started
+     */
+    void buildStarted() {
+        ++buildCount;
+        currentBuildStart = timeProvider.getCurrentTime();
+    }
+
+    /**
+     * Informs the stats that the build finished
+     */
+    void buildFinished() {
+        allBuildsTime += timeProvider.getCurrentTime() - currentBuildStart;
+        currentPerformance = performance(allBuildsTime, memory);
+    }
+
+    private static int performance(long totalTime, MemoryInfo memoryInfo) {
+        //TODO SF consider not showing (or show '-') when getCollectionTime() returns 0
+        return 100 - NumberUtil.percentOf(memoryInfo.getCollectionTime(), totalTime);
+    }
+
+    /**
+     * 0-100, the percentage of time spent on doing the work vs time spent in gc
+     */
+    int getCurrentPerformance() {
+        return currentPerformance;
+    }
+
+    /**
+     * elegant description of daemon's health
+     */
+    String getHealthInfo() {
+        if (buildCount == 1) {
+            return format("Starting build in new daemon [memory: %s]", NumberUtil.formatBytes(memory.getMaxMemory()));
+        } else {
+            return format("Starting %s build in daemon [uptime: %s, performance: %s%%, memory: %s%% of %s]",
+                    NumberUtil.ordinal(buildCount), totalTime.getTime(), currentPerformance, getMemoryUsed(), NumberUtil.formatBytes(memory.getMaxMemory()));
+        }
+    }
+
+    /**
+     * 0-100, the percentage of memory used of total memory available to the process
+     */
+    int getMemoryUsed() {
+        return NumberUtil.percentOf(memory.getCommittedMemory(), memory.getMaxMemory());
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStatus.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStatus.java
new file mode 100644
index 0000000..b3951f6
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DaemonStatus.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.api.GradleException;
+
+import static java.lang.String.format;
+
+class DaemonStatus {
+
+    public static final String EXPIRE_AT_PROPERTY = "org.gradle.daemon.performance.expire-at";
+    static final int DEFAULT_EXPIRE_AT = 0;
+
+    boolean isDaemonTired(DaemonStats stats) {
+        String expireAt = System.getProperty(EXPIRE_AT_PROPERTY);
+        int threshold = parseValue(expireAt, DEFAULT_EXPIRE_AT);
+        return threshold != 0 //zero means the feature is off
+                && stats.getMemoryUsed() > 85 //the daemon is not tired if the memory is not sufficiently exhausted
+                && stats.getCurrentPerformance() <= threshold; //performance below threshold
+    }
+
+    private static int parseValue(String expireAt, int defaultValue) {
+        if (expireAt == null) {
+            return defaultValue;
+        }
+        try {
+            return Integer.parseInt(expireAt);
+        } catch (Exception e) {
+            throw new GradleException(format(
+                    "System property '%s' has incorrect value: '%s'. The value needs to be integer.",
+                    EXPIRE_AT_PROPERTY, expireAt));
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DefaultDaemonHealthServices.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DefaultDaemonHealthServices.java
new file mode 100644
index 0000000..6000b5f
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/DefaultDaemonHealthServices.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+
+public class DefaultDaemonHealthServices implements DaemonHealthServices {
+
+    private final HintGCAfterBuild hygieneAction = new HintGCAfterBuild();
+    private final DaemonStats stats = new DaemonStats();
+    private final DaemonStatus status = new DaemonStatus();
+    private final HealthLogger logger = new HealthLogger();
+    private final DaemonHealthTracker tracker = new DaemonHealthTracker(stats, status, logger);
+
+    /**
+     * {@inheritDoc}
+     */
+    public DaemonCommandAction getGCHintAction() {
+        return hygieneAction;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DaemonCommandAction getHealthTrackerAction() {
+        return tracker;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthLogger.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthLogger.java
new file mode 100644
index 0000000..88a2d18
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthLogger.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import org.gradle.api.logging.Logger;
+import org.gradle.launcher.daemon.configuration.GradleProperties;
+
+class HealthLogger {
+
+    static final String HEALTH_MESSAGE_PROPERTY = "org.gradle.daemon.performance.logging";
+
+    void logHealth(DaemonStats stats, Logger logger) {
+        if (GradleProperties.isTrue(System.getProperty(HEALTH_MESSAGE_PROPERTY))) {
+            logger.lifecycle(stats.getHealthInfo());
+        } else {
+            //the default
+            logger.info(stats.getHealthInfo());
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HintGCAfterBuild.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HintGCAfterBuild.java
new file mode 100644
index 0000000..4f5258f
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HintGCAfterBuild.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.launcher.daemon.server.health;
+
+import org.gradle.internal.TimeProvider;
+import org.gradle.internal.TrueTimeProvider;
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction;
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution;
+
+class HintGCAfterBuild implements DaemonCommandAction {
+
+    private final long gcDelay;
+    private TimeProvider timeProvider;
+    private long nextGcHint;
+
+    HintGCAfterBuild() {
+        //by default, don't hint for gc more often than once per 2 minutes
+        //because it is a full scan
+        this(1000 * 60 * 2, new TrueTimeProvider());
+    }
+
+    HintGCAfterBuild(long gcDelay, TimeProvider timeProvider) {
+        this.gcDelay = gcDelay;
+        this.timeProvider = timeProvider;
+    }
+
+    public void execute(DaemonCommandExecution execution) {
+        execution.proceed();
+        long time = timeProvider.getCurrentTime();
+        if (time > nextGcHint) {
+            gc();
+            nextGcHint = time + gcDelay;
+        }
+    }
+
+    void gc() {
+        System.gc();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/MemoryInfo.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/MemoryInfo.java
new file mode 100644
index 0000000..649494a
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/MemoryInfo.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health;
+
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+
+class MemoryInfo {
+
+    private final long totalMemory; //this does not change
+
+    MemoryInfo() {
+        totalMemory = Runtime.getRuntime().maxMemory();
+    }
+
+    /**
+     * Approx. time spent in gc. See {@link GarbageCollectorMXBean}
+     */
+    long getCollectionTime() {
+        long garbageCollectionTime = 0;
+        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+            long time = gc.getCollectionTime();
+            if (time >= 0) {
+                garbageCollectionTime += time;
+            }
+        }
+        return garbageCollectionTime;
+    }
+
+    /**
+     * Max memory that this process can commit in bytes.
+     * Always returns the same value because maximum memory is determined at jvm start.
+     */
+    long getMaxMemory() {
+        return totalMemory;
+    }
+
+    /**
+     * Currently committed memory of this process in bytes.
+     * May return different value depending on how the heap has expanded.
+     * The returned value is <= {@link #getMaxMemory()}
+     */
+    long getCommittedMemory() {
+        //querying runtime for each invocation
+        return Runtime.getRuntime().totalMemory();
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionExecuter.java
index d6cb1f7..e63f158 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionExecuter.java
@@ -15,15 +15,15 @@
  */
 package org.gradle.launcher.exec;
 
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildRequestContext;
 
 public interface BuildActionExecuter<P> {
     /**
      * Executes the given action, and returns the result.
      *
      * @param action The action
-     * @param <T> The result type
      * @return The result.
      */
-    <T> T execute(BuildAction<T> action, P actionParameters);
+    Object execute(BuildAction action, BuildRequestContext requestContext, P actionParameters);
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionParameters.java
index 7ec80d0..6f21f67 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/BuildActionParameters.java
@@ -16,15 +16,12 @@
 package org.gradle.launcher.exec;
 
 import org.gradle.api.logging.LogLevel;
-import org.gradle.initialization.BuildRequestMetaData;
+import org.gradle.launcher.daemon.configuration.DaemonUsage;
 
 import java.io.File;
-import java.io.Serializable;
 import java.util.Map;
 
-public interface BuildActionParameters extends Serializable {
-    BuildRequestMetaData getBuildRequestMetaData();
-
+public interface BuildActionParameters {
     Map<String, String> getSystemProperties();
 
     Map<String, String> getEnvVariables();
@@ -32,4 +29,6 @@ public interface BuildActionParameters extends Serializable {
     File getCurrentDir();
 
     LogLevel getLogLevel();
+
+    DaemonUsage getDaemonUsage();
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/ChainingBuildActionRunner.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/ChainingBuildActionRunner.java
new file mode 100644
index 0000000..bfaee22
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/ChainingBuildActionRunner.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.exec;
+
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.invocation.BuildActionRunner;
+import org.gradle.internal.invocation.BuildController;
+
+import java.util.List;
+
+public class ChainingBuildActionRunner implements BuildActionRunner {
+    private final List<? extends BuildActionRunner> runners;
+
+    public ChainingBuildActionRunner(List<? extends BuildActionRunner> runners) {
+        this.runners = runners;
+    }
+
+    @Override
+    public void run(BuildAction action, BuildController buildController) {
+        for (BuildActionRunner runner : runners) {
+            runner.run(action, buildController);
+            if (buildController.hasResult()) {
+                return;
+            }
+        }
+        throw new UnsupportedOperationException(String.format("Don't know how to run a build action of type %s.", action.getClass().getSimpleName()));
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuter.java
new file mode 100644
index 0000000..a3a24d8
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.exec;
+
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.initialization.BuildRequestContext;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+
+public class DaemonUsageSuggestingBuildActionExecuter implements BuildActionExecuter<BuildActionParameters> {
+    public static final String PLEASE_USE_DAEMON_MESSAGE_PREFIX = "This build could be faster, please consider using the Gradle Daemon: ";
+
+    private final BuildActionExecuter<BuildActionParameters> executer;
+    private final StyledTextOutputFactory textOutputFactory;
+    private final DocumentationRegistry documentationRegistry;
+    private final OperatingSystem operatingSystem;
+
+    public DaemonUsageSuggestingBuildActionExecuter(BuildActionExecuter<BuildActionParameters> executer, StyledTextOutputFactory textOutputFactory,
+                                                    DocumentationRegistry documentationRegistry) {
+        this(executer, textOutputFactory, documentationRegistry, OperatingSystem.current());
+    }
+
+    DaemonUsageSuggestingBuildActionExecuter(BuildActionExecuter<BuildActionParameters> executer, StyledTextOutputFactory textOutputFactory,
+                                                    DocumentationRegistry documentationRegistry, OperatingSystem operatingSystem) {
+        this.executer = executer;
+        this.textOutputFactory = textOutputFactory;
+        this.documentationRegistry = documentationRegistry;
+        this.operatingSystem = operatingSystem;
+    }
+
+    @Override
+    public Object execute(BuildAction action, BuildRequestContext requestContext, BuildActionParameters actionParameters) {
+        Object result = executer.execute(action, requestContext, actionParameters);
+        possiblySuggestUsingDaemon(actionParameters);
+        return result;
+    }
+
+    private void possiblySuggestUsingDaemon(BuildActionParameters actionParameters) {
+        if (actionParameters.getDaemonUsage().isExplicitlySet()
+                || operatingSystem.isWindows()
+                || actionParameters.getEnvVariables().get("CI") != null) {
+            return;
+        }
+        StyledTextOutput styledTextOutput = textOutputFactory.create(DaemonUsageSuggestingBuildActionExecuter.class, LogLevel.LIFECYCLE);
+        styledTextOutput.println();
+        styledTextOutput.println(PLEASE_USE_DAEMON_MESSAGE_PREFIX + documentationRegistry.getDocumentationFor("gradle_daemon"));
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DefaultBuildActionParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DefaultBuildActionParameters.java
index 9c8c234..4868e31 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DefaultBuildActionParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/DefaultBuildActionParameters.java
@@ -16,9 +16,7 @@
 package org.gradle.launcher.exec;
 
 import org.gradle.api.logging.LogLevel;
-import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.initialization.BuildRequestMetaData;
-import org.gradle.initialization.DefaultBuildRequestMetaData;
+import org.gradle.launcher.daemon.configuration.DaemonUsage;
 import org.gradle.util.GUtil;
 
 import java.io.File;
@@ -27,16 +25,13 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class DefaultBuildActionParameters implements BuildActionParameters, Serializable {
-    private final BuildClientMetaData clientMetaData;
-    private final long startTime;
     private final File currentDir;
     private final LogLevel logLevel;
     private final Map<String, String> systemProperties;
     private final Map<String, String> envVariables;
+    private final DaemonUsage daemonUsage;
 
-    public DefaultBuildActionParameters(BuildClientMetaData clientMetaData, long startTime, Map<?, ?> systemProperties, Map<String, String> envVariables, File currentDir, LogLevel logLevel) {
-        this.clientMetaData = clientMetaData;
-        this.startTime = startTime;
+    public DefaultBuildActionParameters(Map<?, ?> systemProperties, Map<String, String> envVariables, File currentDir, LogLevel logLevel, DaemonUsage daemonUsage) {
         this.currentDir = currentDir;
         this.logLevel = logLevel;
         assert systemProperties != null;
@@ -44,10 +39,7 @@ public class DefaultBuildActionParameters implements BuildActionParameters, Seri
         this.systemProperties = new HashMap<String, String>();
         GUtil.addToMap(this.systemProperties, systemProperties);
         this.envVariables = new HashMap<String, String>(envVariables);
-    }
-
-    public BuildRequestMetaData getBuildRequestMetaData() {
-        return new DefaultBuildRequestMetaData(clientMetaData, startTime);
+        this.daemonUsage = daemonUsage;
     }
 
     public Map<String, String> getSystemProperties() {
@@ -69,11 +61,14 @@ public class DefaultBuildActionParameters implements BuildActionParameters, Seri
     @Override
     public String toString() {
         return "DefaultBuildActionParameters{"
-                + "clientMetaData=" + clientMetaData
-                + ", startTime=" + startTime
                 + ", currentDir=" + currentDir
                 + ", systemProperties size=" + systemProperties.size()
                 + ", envVariables size=" + envVariables.size()
                 + '}';
     }
+
+    @Override
+    public DaemonUsage getDaemonUsage() {
+        return daemonUsage;
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/InProcessBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/InProcessBuildActionExecuter.java
index 4e07adf..93488c7 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/exec/InProcessBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/exec/InProcessBuildActionExecuter.java
@@ -17,61 +17,89 @@
 package org.gradle.launcher.exec;
 
 import org.gradle.BuildResult;
-import org.gradle.GradleLauncher;
-import org.gradle.StartParameter;
-import org.gradle.initialization.BuildController;
-import org.gradle.initialization.BuildAction;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.initialization.BuildRequestContext;
+import org.gradle.initialization.DefaultGradleLauncher;
 import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.invocation.BuildActionRunner;
+import org.gradle.internal.invocation.BuildController;
 
 public class InProcessBuildActionExecuter implements BuildActionExecuter<BuildActionParameters> {
     private final GradleLauncherFactory gradleLauncherFactory;
+    private final BuildActionRunner buildActionRunner;
 
-    public InProcessBuildActionExecuter(GradleLauncherFactory gradleLauncherFactory) {
+    public InProcessBuildActionExecuter(GradleLauncherFactory gradleLauncherFactory, BuildActionRunner buildActionRunner) {
         this.gradleLauncherFactory = gradleLauncherFactory;
+        this.buildActionRunner = buildActionRunner;
     }
 
-    public <T> T execute(BuildAction<T> action, BuildActionParameters actionParameters) {
-        DefaultBuildController buildController = new DefaultBuildController(gradleLauncherFactory, actionParameters);
-        return action.run(buildController);
+    public Object execute(BuildAction action, BuildRequestContext buildRequestContext, BuildActionParameters actionParameters) {
+        DefaultGradleLauncher gradleLauncher = (DefaultGradleLauncher) gradleLauncherFactory.newInstance(action.getStartParameter(), buildRequestContext);
+        try {
+            DefaultBuildController buildController = new DefaultBuildController(gradleLauncher);
+            buildActionRunner.run(action, buildController);
+            return buildController.result;
+        } finally {
+            gradleLauncher.stop();
+        }
     }
 
     private static class DefaultBuildController implements BuildController {
-        private final BuildActionParameters actionParameters;
-        private final GradleLauncherFactory gradleLauncherFactory;
-        private GradleLauncher gradleLauncher;
-        private StartParameter startParameter = new StartParameter();
+        private enum State { Created, Completed }
+        private State state = State.Created;
+        private boolean hasResult;
+        private Object result;
+        private final DefaultGradleLauncher gradleLauncher;
 
-        private DefaultBuildController(GradleLauncherFactory gradleLauncherFactory, BuildActionParameters actionParameters) {
-            this.gradleLauncherFactory = gradleLauncherFactory;
-            this.actionParameters = actionParameters;
+        public DefaultBuildController(DefaultGradleLauncher gradleLauncher) {
+            this.gradleLauncher = gradleLauncher;
         }
 
-        public void setStartParameter(StartParameter startParameter) {
-            if (gradleLauncher != null) {
-                throw new IllegalStateException("Cannot change start parameter after launcher has been created.");
+        public DefaultGradleLauncher getLauncher() {
+            if (state == State.Completed) {
+                throw new IllegalStateException("Cannot use launcher after build has completed.");
             }
-            this.startParameter = startParameter;
+            return gradleLauncher;
         }
 
-        public GradleLauncher getLauncher() {
-            if (gradleLauncher == null) {
-                gradleLauncher = gradleLauncherFactory.newInstance(startParameter, actionParameters.getBuildRequestMetaData());
+        @Override
+        public boolean hasResult() {
+            return hasResult;
+        }
+
+        @Override
+        public Object getResult() {
+            if (!hasResult) {
+                throw new IllegalStateException("No result has been provided for this build action.");
             }
-            return gradleLauncher;
+            return result;
+        }
+
+        @Override
+        public void setResult(Object result) {
+            this.hasResult = true;
+            this.result = result;
+        }
+
+        public GradleInternal getGradle() {
+            return getLauncher().getGradle();
         }
 
-        public void run() {
-            check(getLauncher().run());
+        public GradleInternal run() {
+            return check(getLauncher().run());
         }
 
-        public void configure() {
-            check(getLauncher().getBuildAnalysis());
+        public GradleInternal configure() {
+            return check(getLauncher().getBuildAnalysis());
         }
 
-        private void check(BuildResult buildResult) {
+        private GradleInternal check(BuildResult buildResult) {
+            state = State.Completed;
             if (buildResult.getFailure() != null) {
                 throw new ReportedException(buildResult.getFailure());
             }
+            return (GradleInternal) buildResult.getGradle();
         }
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/DefaultBuildInvocations.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/DefaultBuildInvocations.java
new file mode 100644
index 0000000..acb48db
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/DefaultBuildInvocations.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.impl;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.gradle.tooling.model.gradle.BuildInvocations}
+ */
+public class DefaultBuildInvocations implements Serializable {
+    private List<? extends LaunchableGradleTaskSelector> selectors;
+    private List<? extends LaunchableGradleTask> tasks;
+
+    public DefaultBuildInvocations setSelectors(List<? extends LaunchableGradleTaskSelector> selectors) {
+        this.selectors = selectors;
+        return this;
+    }
+
+    public List<? extends LaunchableGradleTaskSelector> getTaskSelectors() {
+        return selectors;
+    }
+
+    public DefaultBuildInvocations setTasks(List<? extends LaunchableGradleTask> tasks) {
+        this.tasks = tasks;
+        return this;
+    }
+
+    public List<? extends LaunchableGradleTask> getTasks() {
+        return tasks;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTask.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTask.java
index 84752e7..dce444e 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTask.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTask.java
@@ -16,14 +16,20 @@
 
 package org.gradle.tooling.internal.impl;
 
+import org.gradle.TaskExecutionRequest;
+import org.gradle.tooling.internal.protocol.InternalLaunchable;
+
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 
-public class LaunchableGradleTask implements Serializable, LaunchableImplementation {
+public class LaunchableGradleTask implements Serializable, InternalLaunchable, TaskExecutionRequest {
 
     private String path;
     private String name;
     private String description;
     private String displayName;
+    private boolean isPublic;
 
     public String getPath() {
         return path;
@@ -61,16 +67,25 @@ public class LaunchableGradleTask implements Serializable, LaunchableImplementat
         return this;
     }
 
-    public String getTaskName() {
-        return path;
+    public List<String> getArgs() {
+        return Collections.singletonList(path);
     }
 
     public String getProjectPath() {
         return null;
     }
 
+    public boolean isPublic() {
+        return isPublic;
+    }
+
+    public LaunchableGradleTask setPublic(boolean isPublic) {
+        this.isPublic = isPublic;
+        return this;
+    }
+
     @Override
     public String toString() {
-        return getClass().getSimpleName() + "{path='" + path + "'}";
+        return getClass().getSimpleName() + "{path='" + path + "',public=" + isPublic + "}";
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTaskSelector.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTaskSelector.java
index 1a35459..0136502 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTaskSelector.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableGradleTaskSelector.java
@@ -16,20 +16,25 @@
 
 package org.gradle.tooling.internal.impl;
 
+import org.gradle.TaskExecutionRequest;
 import org.gradle.api.Nullable;
+import org.gradle.tooling.internal.protocol.InternalLaunchable;
 import org.gradle.tooling.model.TaskSelector;
 
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Data used for {@link org.gradle.tooling.model.TaskSelector}.
  */
-public class LaunchableGradleTaskSelector implements TaskSelector, LaunchableImplementation, Serializable {
+public class LaunchableGradleTaskSelector implements TaskSelector, InternalLaunchable, TaskExecutionRequest, Serializable {
     private String name;
     private String displayName;
     private String description;
     private String taskName;
     private String projectPath;
+    private boolean isPublic;
 
     public String getName() {
         return name;
@@ -59,8 +64,8 @@ public class LaunchableGradleTaskSelector implements TaskSelector, LaunchableImp
         return this;
     }
 
-    public String getTaskName() {
-        return taskName;
+    public List<String> getArgs() {
+        return Collections.singletonList(taskName);
     }
 
     public LaunchableGradleTaskSelector setTaskName(String taskName) {
@@ -77,6 +82,15 @@ public class LaunchableGradleTaskSelector implements TaskSelector, LaunchableImp
         return this;
     }
 
+    public boolean isPublic() {
+        return isPublic;
+    }
+
+    public LaunchableGradleTaskSelector setPublic(boolean isPublic) {
+        this.isPublic = isPublic;
+        return this;
+    }
+
     @Override
     public String toString() {
         return "LaunchableGradleTaskSelector{"
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableImplementation.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableImplementation.java
deleted file mode 100644
index 52dd3dc..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/impl/LaunchableImplementation.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.impl;
-
-import org.gradle.api.Nullable;
-import org.gradle.tooling.internal.protocol.InternalLaunchable;
-
-/**
- * SPI for launchables providing information necessary to initiate the build.
- *
- * @since 1.12
- */
-public interface LaunchableImplementation extends InternalLaunchable {
-    /** Task path for real tasks, selector name for task selectors. */
-    String getTaskName();
-    @Nullable String getProjectPath();
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java
index badc54b..1b3b946 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/BuildModelAction.java
@@ -15,91 +15,38 @@
  */
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.api.Action;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.initialization.*;
-import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException;
-import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder;
-import org.gradle.tooling.provider.model.ToolingModelBuilder;
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
-import org.gradle.tooling.provider.model.UnknownModelException;
+import org.gradle.StartParameter;
+import org.gradle.internal.invocation.BuildAction;
 
 import java.io.Serializable;
-import java.util.concurrent.atomic.AtomicReference;
 
-public class BuildModelAction implements BuildAction<BuildActionResult>, Serializable {
-    private final boolean runTasks;
+public class BuildModelAction implements BuildAction, Serializable {
+    private final StartParameter startParameter;
     private final String modelName;
+    private final boolean runTasks;
+    private final boolean sendTestProgressEvents;
 
-    public BuildModelAction(String modelName, boolean runTasks) {
+    public BuildModelAction(StartParameter startParameter, String modelName, boolean runTasks, boolean sendTestProgressEvents) {
+        this.startParameter = startParameter;
         this.modelName = modelName;
         this.runTasks = runTasks;
+        this.sendTestProgressEvents = sendTestProgressEvents;
     }
 
-    public BuildActionResult run(BuildController buildController) {
-        DefaultGradleLauncher launcher = (DefaultGradleLauncher) buildController.getLauncher();
-        final PayloadSerializer payloadSerializer = launcher.getGradle().getServices().get(PayloadSerializer.class);
-
-        // The following is all very awkward because the contract for BuildController is still just a
-        // rough wrapper around GradleLauncher, which means we can only get at the model and various
-        // services by using listeners.
-
-        final AtomicReference<Object> model = new AtomicReference<Object>();
-        final AtomicReference<RuntimeException> failure = new AtomicReference<RuntimeException>();
-        final Action<GradleInternal> action = new Action<GradleInternal>() {
-            public void execute(GradleInternal gradle) {
-                ToolingModelBuilderRegistry builderRegistry = getToolingModelBuilderRegistry(gradle);
-                ToolingModelBuilder builder;
-                try {
-                    builder = builderRegistry.getBuilder(modelName);
-                } catch (UnknownModelException e) {
-                    failure.set((InternalUnsupportedModelException) (new InternalUnsupportedModelException().initCause(e)));
-                    return;
-                }
-                Object result;
-                if (builder instanceof ProjectSensitiveToolingModelBuilder) {
-                    result = ((ProjectSensitiveToolingModelBuilder) builder).buildAll(modelName, gradle.getDefaultProject(), true);
-                } else {
-                    result = builder.buildAll(modelName, gradle.getDefaultProject());
-                }
-                model.set(result);
-            }
-        };
-
-        if (runTasks) {
-            launcher.addListener(new TasksCompletionListener() {
-                public void onTasksFinished(GradleInternal gradle) {
-                    action.execute(gradle);
-                }
-            });
-            buildController.run();
-        } else {
-            launcher.addListener(new ModelConfigurationListener() {
-                public void onConfigure(GradleInternal gradle) {
-                    // Currently need to force everything to be configured
-                    ensureAllProjectsEvaluated(gradle);
-                    action.execute(gradle);
-                }
-            });
-            buildController.configure();
-        }
+    @Override
+    public StartParameter getStartParameter() {
+        return startParameter;
+    }
 
-        if (failure.get() != null) {
-            throw failure.get();
-        }
-        return new BuildActionResult(payloadSerializer.serialize(model.get()), null);
+    public String getModelName() {
+        return modelName;
     }
 
-    private ToolingModelBuilderRegistry getToolingModelBuilderRegistry(GradleInternal gradle) {
-        return gradle.getDefaultProject().getServices().get(ToolingModelBuilderRegistry.class);
+    public boolean isRunTasks() {
+        return runTasks;
     }
 
-    private void ensureAllProjectsEvaluated(GradleInternal gradle) {
-        gradle.getRootProject().allprojects((Action) new Action<ProjectInternal>() {
-            public void execute(ProjectInternal projectInternal) {
-                projectInternal.evaluate();
-            }
-        });
+    public boolean isSendTestProgressEvents() {
+        return sendTestProgressEvents;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClassLoaderCache.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClassLoaderCache.java
new file mode 100644
index 0000000..e2693e2
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClassLoaderCache.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Transformer;
+
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+ at ThreadSafe
+public class ClassLoaderCache {
+    private final Lock lock = new ReentrantLock();
+    private final Cache<ClassLoader, ClassLoaderDetails> classLoaderDetails;
+    private final Cache<UUID, ClassLoader> classLoaderIds;
+
+    public ClassLoaderCache() {
+        classLoaderDetails = CacheBuilder.newBuilder().weakKeys().build();
+        classLoaderIds = CacheBuilder.newBuilder().softValues().build();
+    }
+
+    public ClassLoader getClassLoader(ClassLoaderDetails details, Transformer<ClassLoader, ClassLoaderDetails> factory) {
+        lock.lock();
+        try {
+            ClassLoader classLoader = classLoaderIds.getIfPresent(details.uuid);
+            if (classLoader != null) {
+                return classLoader;
+            }
+
+            classLoader = factory.transform(details);
+            classLoaderIds.put(details.uuid, classLoader);
+            classLoaderDetails.put(classLoader, details);
+            return classLoader;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public ClassLoaderDetails getDetails(ClassLoader classLoader, Transformer<ClassLoaderDetails, ClassLoader> factory) {
+        lock.lock();
+        try {
+            ClassLoaderDetails details = classLoaderDetails.getIfPresent(classLoader);
+            if (details != null) {
+                return details;
+            }
+
+            details = factory.transform(classLoader);
+            classLoaderDetails.put(classLoader, details);
+            classLoaderIds.put(details.uuid, classLoader);
+            return details;
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClasspathInferer.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClasspathInferer.java
index cd2c1f6..cbd546e 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClasspathInferer.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClasspathInferer.java
@@ -80,7 +80,7 @@ public class ClasspathInferer {
                 return;
             }
 
-            File classPathRoot = ClasspathUtil.getClasspathForResource(resource, resourceName);
+            File classPathRoot = ClasspathUtil.getClasspathForClass(target);
             dest.add(classPathRoot.toURI().toURL());
 
             // To determine the dependencies of the class, load up the byte code and look for CONSTANT_Class entries in the constant pool
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java
index 004925e..6e6dfa3 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientProvidedBuildAction.java
@@ -16,66 +16,26 @@
 
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.api.Action;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.initialization.BuildAction;
-import org.gradle.initialization.BuildController;
-import org.gradle.initialization.DefaultGradleLauncher;
-import org.gradle.initialization.ModelConfigurationListener;
-import org.gradle.tooling.internal.protocol.InternalBuildAction;
-import org.gradle.tooling.internal.protocol.InternalBuildActionFailureException;
-import org.gradle.tooling.internal.protocol.InternalBuildController;
+import org.gradle.StartParameter;
+import org.gradle.internal.invocation.BuildAction;
 
 import java.io.Serializable;
-import java.util.concurrent.atomic.AtomicReference;
 
-class ClientProvidedBuildAction implements BuildAction<BuildActionResult>, Serializable {
+public class ClientProvidedBuildAction implements BuildAction, Serializable {
     private final SerializedPayload action;
+    private final StartParameter startParameter;
 
-    public ClientProvidedBuildAction(SerializedPayload action) {
+    public ClientProvidedBuildAction(StartParameter startParameter, SerializedPayload action) {
+        this.startParameter = startParameter;
         this.action = action;
     }
 
-    public BuildActionResult run(final BuildController buildController) {
-        final DefaultGradleLauncher gradleLauncher = (DefaultGradleLauncher) buildController.getLauncher();
-        final PayloadSerializer payloadSerializer = gradleLauncher.getGradle().getServices().get(PayloadSerializer.class);
-        final InternalBuildAction<?> action = (InternalBuildAction<?>) payloadSerializer.deserialize(this.action);
-
-        // The following is all very awkward because the contract for BuildController is still just a
-        // rough wrapper around GradleLauncher, which means we can only get at the model and various
-        // services by using listeners.
-
-        final AtomicReference<Object> result = new AtomicReference<Object>();
-        final AtomicReference<RuntimeException> failure = new AtomicReference<RuntimeException>();
-
-        gradleLauncher.addListener(new ModelConfigurationListener() {
-            public void onConfigure(final GradleInternal gradle) {
-                // Currently need to force everything to be configured
-                ensureAllProjectsEvaluated(gradle);
-                InternalBuildController internalBuildController = new DefaultBuildController(gradle);
-                Object model = null;
-                try {
-                    model = action.execute(internalBuildController);
-                } catch (RuntimeException e) {
-                    failure.set(new InternalBuildActionFailureException(e));
-                }
-                result.set(model);
-            }
-        });
-        buildController.configure();
-
-        if (failure.get() != null) {
-            return new BuildActionResult(null, payloadSerializer.serialize(failure.get()));
-        }
-        return new BuildActionResult(payloadSerializer.serialize(result.get()), null);
+    @Override
+    public StartParameter getStartParameter() {
+        return startParameter;
     }
 
-    private void ensureAllProjectsEvaluated(GradleInternal gradle) {
-        gradle.getRootProject().allprojects((Action) new Action<ProjectInternal>() {
-            public void execute(ProjectInternal projectInternal) {
-                projectInternal.evaluate();
-            }
-        });
+    public SerializedPayload getAction() {
+        return action;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactory.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactory.java
new file mode 100644
index 0000000..82182be
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactory.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.internal.classloader.ClassLoaderSpec;
+import org.gradle.internal.classloader.MutableURLClassLoader;
+import org.gradle.internal.classloader.TransformingClassLoader;
+import org.gradle.tooling.provider.model.internal.LegacyConsumerInterface;
+import org.objectweb.asm.*;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ClientSidePayloadClassLoaderFactory implements PayloadClassLoaderFactory {
+    private final PayloadClassLoaderFactory classLoaderFactory;
+
+    public ClientSidePayloadClassLoaderFactory(PayloadClassLoaderFactory classLoaderFactory) {
+        this.classLoaderFactory = classLoaderFactory;
+    }
+
+    public ClassLoader getClassLoaderFor(ClassLoaderSpec spec, List<? extends ClassLoader> parents) {
+        if (spec instanceof MutableURLClassLoader.Spec) {
+            MutableURLClassLoader.Spec clSpec = (MutableURLClassLoader.Spec) spec;
+            if (parents.size() != 1) {
+                throw new IllegalStateException("Expected exactly one parent ClassLoader");
+            }
+            return new MixInClassLoader(parents.get(0), clSpec.getClasspath());
+        }
+        return classLoaderFactory.getClassLoaderFor(spec, parents);
+    }
+
+    private static class MixInClassLoader extends TransformingClassLoader {
+        public MixInClassLoader(ClassLoader parent, List<URL> classPath) {
+            super(parent, classPath);
+        }
+
+        @Override
+        protected byte[] transform(byte[] bytes) {
+            // First scan for annotation, and short circuit transformation if not present
+            ClassReader classReader = new ClassReader(bytes);
+
+            AnnotationDetector detector = new AnnotationDetector();
+            classReader.accept(detector, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
+            if (!detector.found) {
+                return bytes;
+            }
+
+            if (findLoadedClass(detector.interfaceName) == null) {
+                // TODO:ADAM - need to do this earlier
+                ClassWriter emptyWriter = new ClassWriter(0);
+                emptyWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, detector.interfaceName.replace(".", "/"), null, Type.getType(Object.class).getInternalName(), null);
+                emptyWriter.visitEnd();
+                byte[] emptyBytecode = emptyWriter.toByteArray();
+                defineClass(detector.interfaceName, emptyBytecode, 0, emptyBytecode.length);
+            }
+
+            ClassWriter classWriter = new ClassWriter(0);
+            classReader.accept(new TransformingAdapter(classWriter, detector.interfaceName), 0);
+            bytes = classWriter.toByteArray();
+            return bytes;
+        }
+
+        private static class AnnotationDetector extends ClassVisitor {
+            private static final String ANNOTATION_DESCRIPTOR = Type.getType(LegacyConsumerInterface.class).getDescriptor();
+            String interfaceName;
+            private boolean found;
+
+            private AnnotationDetector() {
+                super(Opcodes.ASM5);
+            }
+
+            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+                if (desc.equals(ANNOTATION_DESCRIPTOR)) {
+                    found = true;
+                }
+                return new AnnotationVisitor(Opcodes.ASM5) {
+
+                    @Override
+                    public void visit(String name, Object value) {
+                        if (name.equals("value")) {
+                            interfaceName = value.toString();
+                        }
+                    }
+                };
+            }
+        }
+
+        private static class TransformingAdapter extends ClassVisitor {
+            private final String mixInInterface;
+
+            public TransformingAdapter(ClassWriter classWriter, String mixInInterface) {
+                super(Opcodes.ASM5, classWriter);
+                this.mixInInterface = mixInInterface;
+            }
+
+            @Override
+            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+                Set<String> allInterfaces = new LinkedHashSet<String>(Arrays.asList(interfaces));
+                allInterfaces.add(mixInInterface.replace(".", "/"));
+                super.visit(version, access, name, signature, superName, allInterfaces.toArray(new String[allInterfaces.size()]));
+            }
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java
deleted file mode 100644
index dce07f0..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConfiguringBuildAction.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.provider;
-
-import org.gradle.StartParameter;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.cli.CommandLineArgumentException;
-import org.gradle.initialization.BuildAction;
-import org.gradle.initialization.BuildController;
-import org.gradle.initialization.DefaultCommandLineConverter;
-import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter;
-import org.gradle.tooling.internal.impl.LaunchableImplementation;
-import org.gradle.tooling.internal.protocol.InternalLaunchable;
-import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException;
-import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
-import org.gradle.tooling.model.UnsupportedMethodException;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class ConfiguringBuildAction<T> implements BuildAction<T>, Serializable {
-    private static List<InternalLaunchable> getLaunchables(ProviderOperationParameters parameters) {
-        try {
-            return parameters.getLaunchables();
-        } catch (UnsupportedMethodException ume) {
-            // older consumer version
-            return null;
-        }
-    }
-
-    private LogLevel buildLogLevel;
-    private List<String> arguments;
-    private List<String> tasks;
-    private List<InternalLaunchable> launchables;
-    private BuildAction<? extends T> action;
-    private File projectDirectory;
-    private File gradleUserHomeDir;
-    private Boolean searchUpwards;
-    private Map<String, String> properties = new HashMap<String, String>();
-
-    // Important that this is constructed on the client so that it has the right gradleHomeDir internally
-    private final StartParameter startParameterTemplate = new StartParameter();
-
-    public ConfiguringBuildAction() {}
-
-    public ConfiguringBuildAction(ProviderOperationParameters parameters, BuildAction<? extends T> action, Map<String, String> properties) {
-        this.properties.putAll(properties);
-        this.gradleUserHomeDir = parameters.getGradleUserHomeDir();
-        this.projectDirectory = parameters.getProjectDir();
-        this.searchUpwards = parameters.isSearchUpwards();
-        this.buildLogLevel = parameters.getBuildLogLevel();
-        this.arguments = parameters.getArguments(Collections.<String>emptyList());
-        this.tasks = parameters.getTasks();
-        this.launchables = getLaunchables(parameters);
-        this.action = action;
-    }
-
-    StartParameter configureStartParameter() {
-        return configureStartParameter(new PropertiesToStartParameterConverter());
-    }
-
-    StartParameter configureStartParameter(PropertiesToStartParameterConverter propertiesToStartParameterConverter) {
-        StartParameter startParameter = startParameterTemplate.newInstance();
-
-        startParameter.setProjectDir(projectDirectory);
-        if (gradleUserHomeDir != null) {
-            startParameter.setGradleUserHomeDir(gradleUserHomeDir);
-        }
-
-        if (launchables != null) {
-            List<String> allTasks = new ArrayList<String>();
-            String projectPath = null;
-            for (InternalLaunchable launchable : launchables) {
-                if (launchable instanceof LaunchableImplementation) {
-                    LaunchableImplementation launchableImpl = (LaunchableImplementation) launchable;
-                    allTasks.add(launchableImpl.getTaskName());
-                    if (launchableImpl.getProjectPath() != null) {
-                        if (projectPath != null && !projectPath.equals(launchableImpl.getProjectPath())) {
-                            throw new InternalUnsupportedBuildArgumentException(
-                                    "Problem with provided launchable arguments: " + launchables + ". "
-                                            + "\nOnly selector from the same Gradle project can be built."
-                            );
-                        }
-                        projectPath = launchableImpl.getProjectPath();
-                    }
-                } else {
-                    throw new InternalUnsupportedBuildArgumentException(
-                            "Problem with provided launchable arguments: " + launchables + ". "
-                                    + "\nOnly objects from this provider can be built."
-                    );
-                }
-            }
-            if (projectPath != null) {
-                startParameter.setProjectPath(projectPath);
-            }
-            startParameter.setTaskNames(allTasks);
-        } else if (tasks != null) {
-            startParameter.setTaskNames(tasks);
-        }
-
-        propertiesToStartParameterConverter.convert(properties, startParameter);
-
-        if (arguments != null) {
-            DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
-            try {
-                converter.convert(arguments, startParameter);
-            } catch (CommandLineArgumentException e) {
-                throw new InternalUnsupportedBuildArgumentException(
-                    "Problem with provided build arguments: " + arguments + ". "
-                    + "\n" + e.getMessage()
-                    + "\nEither it is not a valid build option or it is not supported in the target Gradle version."
-                    + "\nNot all of the Gradle command line options are supported build arguments."
-                    + "\nExamples of supported build arguments: '--info', '-u', '-p'."
-                    + "\nExamples of unsupported build options: '--daemon', '-?', '-v'."
-                    + "\nPlease find more information in the javadoc for the BuildLauncher class.", e);
-            }
-        }
-
-        if (searchUpwards != null) {
-            startParameter.setSearchUpwards(searchUpwards);
-        }
-
-        if (buildLogLevel != null) {
-            startParameter.setLogLevel(buildLogLevel);
-        }
-
-        return startParameter;
-    }
-
-    public T run(BuildController buildController) {
-        buildController.setStartParameter(configureStartParameter());
-        return action.run(buildController);
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
index 28055fd..6681268 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
@@ -16,10 +16,15 @@
 
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.scopes.GlobalScopeServices;
+import org.gradle.launcher.daemon.client.DaemonClientFactory;
+import org.gradle.launcher.daemon.client.DaemonClientGlobalServices;
+import org.gradle.launcher.exec.InProcessBuildActionExecuter;
 import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.logging.internal.OutputEventRenderer;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 
 /**
@@ -34,17 +39,29 @@ public class ConnectionScopeServices {
 
     void configure(ServiceRegistration serviceRegistration) {
         serviceRegistration.add(LoggingServiceRegistry.class, loggingServices);
-        serviceRegistration.addProvider(new GlobalScopeServices(false));
+        serviceRegistration.addProvider(new GlobalScopeServices(true));
+        serviceRegistration.addProvider(new DaemonClientGlobalServices());
     }
 
-    ProviderConnection createProviderConnection(GradleLauncherFactory gradleLauncherFactory) {
+    ShutdownCoordinator createShutdownCoordinator(ListenerManager listenerManager, DaemonClientFactory daemonClientFactory, OutputEventRenderer outputEventRenderer) {
+        ShutdownCoordinator shutdownCoordinator = new ShutdownCoordinator(daemonClientFactory, outputEventRenderer);
+        listenerManager.addListener(shutdownCoordinator);
+        return shutdownCoordinator;
+    }
+
+    ProviderConnection createProviderConnection(InProcessBuildActionExecuter buildActionExecuter, DaemonClientFactory daemonClientFactory,
+                                                ClassLoaderFactory classLoaderFactory, ClassLoaderCache classLoaderCache, ShutdownCoordinator shutdownCoordinator) {
         return new ProviderConnection(
                 loggingServices,
-                gradleLauncherFactory,
+                daemonClientFactory,
+                buildActionExecuter,
                 new PayloadSerializer(
                         new ClientSidePayloadClassLoaderRegistry(
                                 new DefaultPayloadClassLoaderRegistry(
-                                        new ModelClassLoaderFactory()),
+                                        new ClassLoaderCache(),
+                                        new ClientSidePayloadClassLoaderFactory(
+                                                new ModelClassLoaderFactory(
+                                                        classLoaderFactory))),
                                 new ClasspathInferer()))
         );
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
index 5c0086b..31acb68 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
@@ -15,8 +15,9 @@
  */
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.configuration.GradleLauncherMetaData;
-import org.gradle.initialization.BuildAction;
+import org.gradle.api.BuildCancelledException;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildRequestContext;
 import org.gradle.internal.SystemProperties;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 import org.gradle.launcher.exec.BuildActionExecuter;
@@ -24,23 +25,31 @@ import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.launcher.exec.DefaultBuildActionParameters;
 import org.gradle.launcher.exec.ReportedException;
 import org.gradle.tooling.internal.protocol.BuildExceptionVersion1;
+import org.gradle.tooling.internal.protocol.InternalBuildCancelledException;
 import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
 
 public class DaemonBuildActionExecuter implements BuildActionExecuter<ProviderOperationParameters> {
     private final BuildActionExecuter<BuildActionParameters> executer;
-    private final DaemonParameters parameters;
+    private final DaemonParameters daemonParameters;
 
-    public DaemonBuildActionExecuter(BuildActionExecuter<BuildActionParameters> executer, DaemonParameters parameters) {
+    public DaemonBuildActionExecuter(BuildActionExecuter<BuildActionParameters> executer, DaemonParameters daemonParameters) {
         this.executer = executer;
-        this.parameters = parameters;
+        this.daemonParameters = daemonParameters;
     }
 
-    public <T> T execute(BuildAction<T> action, ProviderOperationParameters actionParameters) {
-        BuildActionParameters parameters = new DefaultBuildActionParameters(new GradleLauncherMetaData(), actionParameters.getStartTime(),
-                this.parameters.getEffectiveSystemProperties(), System.getenv(), SystemProperties.getCurrentDir(), actionParameters.getBuildLogLevel());
+    public Object execute(BuildAction action, BuildRequestContext buildRequestContext, ProviderOperationParameters parameters) {
+        BuildActionParameters actionParameters = new DefaultBuildActionParameters(daemonParameters.getEffectiveSystemProperties(),
+                System.getenv(), SystemProperties.getInstance().getCurrentDir(), parameters.getBuildLogLevel(), daemonParameters.getDaemonUsage());
         try {
-            return executer.execute(action, parameters);
+            return executer.execute(action, buildRequestContext, actionParameters);
         } catch (ReportedException e) {
+            Throwable t = e.getCause();
+            while (t != null) {
+                if (t instanceof BuildCancelledException) {
+                    throw new InternalBuildCancelledException(e.getCause());
+                }
+                t = t.getCause();
+            }
             throw new BuildExceptionVersion1(e.getCause());
         }
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactory.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactory.java
new file mode 100644
index 0000000..f7adfb5
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.internal.Factories;
+import org.gradle.internal.Factory;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classloader.ClassLoaderSpec;
+import org.gradle.internal.classloader.MutableURLClassLoader;
+
+import java.io.Closeable;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
+
+public class DaemonSidePayloadClassLoaderFactory implements PayloadClassLoaderFactory, Closeable {
+    private final PayloadClassLoaderFactory delegate;
+    private final JarCache jarCache;
+    private final PersistentCache cache;
+
+    public DaemonSidePayloadClassLoaderFactory(PayloadClassLoaderFactory delegate, JarCache jarCache, CacheRepository cacheRepository) {
+        this.delegate = delegate;
+        this.jarCache = jarCache;
+        cache = cacheRepository
+                .cache("jars-1")
+                .withDisplayName("jars")
+                .withCrossVersionCache()
+                .withLockOptions(mode(FileLockManager.LockMode.None))
+                .open();
+    }
+
+    public void close() {
+        cache.close();
+    }
+
+    public ClassLoader getClassLoaderFor(ClassLoaderSpec spec, List<? extends ClassLoader> parents) {
+        if (spec instanceof MutableURLClassLoader.Spec) {
+            MutableURLClassLoader.Spec urlSpec = (MutableURLClassLoader.Spec) spec;
+            if (parents.size() != 1) {
+                throw new IllegalStateException("Expected exactly one parent ClassLoader");
+            }
+            List<URL> cachedClassPath = new ArrayList<URL>(urlSpec.getClasspath().size());
+            for (URL url : urlSpec.getClasspath()) {
+                if (url.getProtocol().equals("file")) {
+                    try {
+                        final File file = new File(url.toURI());
+                        if (file.isFile()) {
+                            File cached = cache.useCache("Locate Jar file", new Factory<File>() {
+                                public File create() {
+                                    return jarCache.getCachedJar(file, Factories.constant(cache.getBaseDir()));
+                                }
+                            });
+                            cachedClassPath.add(cached.toURI().toURL());
+                            continue;
+                        }
+                    } catch (MalformedURLException e) {
+                        throw UncheckedException.throwAsUncheckedException(e);
+                    } catch (URISyntaxException e) {
+                        throw UncheckedException.throwAsUncheckedException(e);
+                    }
+                }
+                cachedClassPath.add(url);
+            }
+
+            return new MutableURLClassLoader(parents.get(0), cachedClassPath);
+        }
+        return delegate.getClassLoaderFor(spec, parents);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultBuildController.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultBuildController.java
deleted file mode 100644
index 38476a5..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultBuildController.java
+++ /dev/null
@@ -1,70 +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.tooling.internal.provider;
-
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.tooling.internal.gradle.GradleProjectIdentity;
-import org.gradle.tooling.internal.protocol.*;
-import org.gradle.tooling.internal.provider.connection.ProviderBuildResult;
-import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder;
-import org.gradle.tooling.provider.model.ToolingModelBuilder;
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
-import org.gradle.tooling.provider.model.UnknownModelException;
-
-class DefaultBuildController implements InternalBuildController {
-    private final GradleInternal gradle;
-
-    public DefaultBuildController(GradleInternal gradle) {
-        this.gradle = gradle;
-    }
-
-    public BuildResult<?> getBuildModel() throws BuildExceptionVersion1 {
-        return new ProviderBuildResult<Object>(gradle);
-    }
-
-    public BuildResult<?> getModel(Object target, ModelIdentifier modelIdentifier) throws BuildExceptionVersion1, InternalUnsupportedModelException {
-        ToolingModelBuilderRegistry modelBuilderRegistry;
-        ProjectInternal project;
-        boolean isImplicitProject;
-        if (target == null) {
-            project = gradle.getDefaultProject();
-            isImplicitProject = true;
-        } else if (target instanceof GradleProjectIdentity) {
-            GradleProjectIdentity gradleProject = (GradleProjectIdentity) target;
-            project = gradle.getRootProject().project(gradleProject.getPath());
-            isImplicitProject = false;
-        } else {
-            throw new IllegalArgumentException("Don't know how to build models for " + target);
-        }
-        modelBuilderRegistry = project.getServices().get(ToolingModelBuilderRegistry.class);
-
-        ToolingModelBuilder builder;
-        try {
-            builder = modelBuilderRegistry.getBuilder(modelIdentifier.getName());
-        } catch (UnknownModelException e) {
-            throw (InternalUnsupportedModelException) (new InternalUnsupportedModelException()).initCause(e);
-        }
-        Object model;
-        if (builder instanceof ProjectSensitiveToolingModelBuilder) {
-            model = ((ProjectSensitiveToolingModelBuilder) builder).buildAll(modelIdentifier.getName(), project, isImplicitProject);
-        } else {
-            model = builder.buildAll(modelIdentifier.getName(), project);
-        }
-        return new ProviderBuildResult<Object>(model);
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
index a2c19c8..19d37c0 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultConnection.java
@@ -15,34 +15,60 @@
  */
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.internal.nativeplatform.services.NativeServices;
+import org.gradle.api.JavaVersion;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.initialization.BuildLayoutParameters;
+import org.gradle.initialization.FixedBuildCancellationToken;
+import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.jvm.UnsupportedJavaRuntimeException;
+import org.gradle.internal.nativeintegration.services.NativeServices;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.tooling.UnsupportedVersionException;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
 import org.gradle.tooling.internal.protocol.*;
 import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException;
-import org.gradle.tooling.internal.provider.connection.*;
-import org.gradle.util.DeprecationLogger;
+import org.gradle.tooling.internal.provider.connection.BuildLogLevelMixIn;
+import org.gradle.tooling.internal.provider.connection.ProviderBuildResult;
+import org.gradle.tooling.internal.provider.connection.ProviderConnectionParameters;
+import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
 import org.gradle.util.GradleVersion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.io.OutputStream;
+import java.io.File;
 
-public class DefaultConnection implements InternalConnection, BuildActionRunner, ConfigurableConnection, ModelBuilder, InternalBuildActionExecutor {
+public class DefaultConnection implements InternalConnection, BuildActionRunner,
+        ConfigurableConnection, ModelBuilder, InternalBuildActionExecutor, InternalCancellableConnection, StoppableConnection {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultConnection.class);
-    private final ProtocolToModelAdapter adapter;
-    private final ServiceRegistry services;
-    private final ProviderConnection connection;
+    private ProtocolToModelAdapter adapter;
+    private ServiceRegistry services;
+    private ProviderConnection connection;
 
     /**
      * This is used by consumers 1.0-milestone-3 and later
      */
     public DefaultConnection() {
         LOGGER.debug("Tooling API provider {} created.", GradleVersion.current().getVersion());
+    }
+
+    /**
+     * This is used by consumers 1.2-rc-1 and later.
+     */
+    public void configure(ConnectionParameters parameters) {
+        ProviderConnectionParameters providerConnectionParameters = new ProtocolToModelAdapter().adapt(ProviderConnectionParameters.class, parameters);
+        File gradleUserHomeDir = providerConnectionParameters.getGradleUserHomeDir(null);
+        if (gradleUserHomeDir == null) {
+            gradleUserHomeDir = new BuildLayoutParameters().getGradleUserHomeDir();
+        }
+        initializeServices(gradleUserHomeDir);
+        connection.configure(providerConnectionParameters);
+    }
+
+    private void initializeServices(File gradleUserHomeDir) {
+        NativeServices.initialize(gradleUserHomeDir);
         LoggingServiceRegistry loggingServices = LoggingServiceRegistry.newEmbeddableLogging();
         services = ServiceRegistryBuilder.builder()
                 .displayName("Connection services")
@@ -54,19 +80,10 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
     }
 
     /**
-     * This is used by consumers 1.2-rc-1 and later.
-     */
-    public void configure(ConnectionParameters parameters) {
-        ProviderConnectionParameters providerConnectionParameters = adapter.adapt(ProviderConnectionParameters.class, parameters);
-        connection.configure(providerConnectionParameters);
-    }
-
-    /**
-     * This method was used by consumers 1.0-rc-1 through to 1.1. Later consumers use {@link #configure(org.gradle.tooling.internal.protocol.ConnectionParameters)} instead.
+     * This method was used by consumers 1.0-rc-1 through to 1.1. Later consumers use {@link #configure(ConnectionParameters)} instead.
      */
     public void configureLogging(final boolean verboseLogging) {
-        ProviderConnectionParameters providerConnectionParameters = adapter.adapt(ProviderConnectionParameters.class, new VerboseLoggingOnlyConnectionParameters(verboseLogging));
-        connection.configure(providerConnectionParameters);
+        // Ignore - we don't support these consumer versions any more
     }
 
     /**
@@ -79,9 +96,16 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
     /**
      * This is used by consumers 1.0-milestone-3 and later
      */
+    @Deprecated
     public void stop() {
-        // TODO:ADAM - switch this on again. Need to add a new protocol method, as older consumers call `stop()` at the end of every operation.
-//        services.close();
+        // We don't do anything here, as older consumers call this method when the project connection is closed but then later attempt to reuse the connection
+    }
+
+    /**
+     * This is used by consumers 2.2-rc-1 and later
+     */
+    public void shutdown(ShutdownParameters parameters) {
+        CompositeStoppable.stoppable(services).stop();
     }
 
     /**
@@ -89,9 +113,7 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      */
     @Deprecated
     public void executeBuild(BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters) {
-        sendDeprecationMessage(operationParameters);
-        logTargetVersion();
-        connection.run(ModelIdentifier.NULL_MODEL, new AdaptedOperationParameters(operationParameters, buildParameters.getTasks()));
+        throw unsupportedConnectionException();
     }
 
     /**
@@ -99,9 +121,7 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      */
     @Deprecated
     public ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 parameters) {
-        sendDeprecationMessage(parameters);
-        logTargetVersion();
-        return run(type, parameters);
+        throw unsupportedConnectionException();
     }
 
     /**
@@ -109,14 +129,7 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      */
     @Deprecated
     public <T> T getTheModel(Class<T> type, BuildOperationParametersVersion1 parameters) {
-        sendDeprecationMessage(parameters);
-        logTargetVersion();
-        return run(type, parameters);
-    }
-
-    private <T> T run(Class<T> type, BuildOperationParametersVersion1 parameters) {
-        String modelName = new ModelMapping().getModelNameFromProtocolType(type);
-        return (T) connection.run(modelName, new AdaptedOperationParameters(parameters));
+        throw unsupportedConnectionException();
     }
 
     /**
@@ -124,10 +137,10 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      */
     @Deprecated
     public <T> BuildResult<T> run(Class<T> type, BuildParameters buildParameters) throws UnsupportedOperationException, IllegalStateException {
-        logTargetVersion();
+        validateCanRun();
         ProviderOperationParameters providerParameters = toProviderParameters(buildParameters);
         String modelName = new ModelMapping().getModelNameFromProtocolType(type);
-        T result = (T) connection.run(modelName, providerParameters);
+        T result = (T) connection.run(modelName, new FixedBuildCancellationToken(), providerParameters);
         return new ProviderBuildResult<T>(result);
     }
 
@@ -135,9 +148,20 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      * This is used by consumers 1.6-rc-1 and later
      */
     public BuildResult<?> getModel(ModelIdentifier modelIdentifier, BuildParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        logTargetVersion();
+        validateCanRun();
+        ProviderOperationParameters providerParameters = toProviderParameters(operationParameters);
+        Object result = connection.run(modelIdentifier.getName(), new FixedBuildCancellationToken(), providerParameters);
+        return new ProviderBuildResult<Object>(result);
+    }
+
+    /**
+     * This is used by consumers 2.1-rc-1 and later
+     */
+    public BuildResult<?> getModel(ModelIdentifier modelIdentifier, InternalCancellationToken cancellationToken, BuildParameters operationParameters) throws BuildExceptionVersion1, InternalUnsupportedModelException, InternalUnsupportedBuildArgumentException, IllegalStateException {
+        validateCanRun();
         ProviderOperationParameters providerParameters = toProviderParameters(operationParameters);
-        Object result = connection.run(modelIdentifier.getName(), providerParameters);
+        BuildCancellationToken buildCancellationToken = new InternalCancellationTokenAdapter(cancellationToken);
+        Object result = connection.run(modelIdentifier.getName(), buildCancellationToken, providerParameters);
         return new ProviderBuildResult<Object>(result);
     }
 
@@ -145,40 +169,36 @@ public class DefaultConnection implements InternalConnection, BuildActionRunner,
      * This is used by consumers 1.8-rc-1 and later.
      */
     public <T> BuildResult<T> run(InternalBuildAction<T> action, BuildParameters operationParameters) throws BuildExceptionVersion1, InternalUnsupportedBuildArgumentException, IllegalStateException {
-        logTargetVersion();
+        validateCanRun();
         ProviderOperationParameters providerParameters = toProviderParameters(operationParameters);
-        Object results = connection.run(action, providerParameters);
+        Object results = connection.run(action, new FixedBuildCancellationToken(), providerParameters);
         return new ProviderBuildResult<T>((T) results);
     }
 
-    private void logTargetVersion() {
-        LOGGER.info("Tooling API is using target Gradle version: {}.", GradleVersion.current().getVersion());
+    /**
+     * This is used by consumers 2.1-rc-1 and later.
+     */
+    public <T> BuildResult<T> run(InternalBuildAction<T> action, InternalCancellationToken cancellationToken, BuildParameters operationParameters)
+            throws BuildExceptionVersion1, InternalUnsupportedBuildArgumentException, IllegalStateException {
+        validateCanRun();
+        ProviderOperationParameters providerParameters = toProviderParameters(operationParameters);
+        BuildCancellationToken buildCancellationToken = new InternalCancellationTokenAdapter(cancellationToken);
+        Object results = connection.run(action, buildCancellationToken, providerParameters);
+        return new ProviderBuildResult<T>((T) results);
     }
 
-    private void sendDeprecationMessage(BuildOperationParametersVersion1 parameters) {
-        OutputStream out = parameters.getStandardOutput();
-        if (out != null) {
-            try {
-                out.write(String.format("Connection from tooling API older than version 1.2 %s%n", DeprecationLogger.getDeprecationMessage()).getBytes());
-            } catch (IOException e) {
-                throw new RuntimeException("Cannot write to stream", e);
-            }
+    private void validateCanRun() {
+        LOGGER.info("Tooling API is using target Gradle version: {}.", GradleVersion.current().getVersion());
+        if (!JavaVersion.current().isJava6Compatible()) {
+            throw UnsupportedJavaRuntimeException.usingUnsupportedVersion("Gradle", JavaVersion.VERSION_1_6);
         }
     }
 
-    private ProviderOperationParameters toProviderParameters(BuildParameters buildParameters) {
-        return adapter.adapt(ProviderOperationParameters.class, buildParameters, BuildLogLevelMixIn.class);
+    private UnsupportedVersionException unsupportedConnectionException() {
+        return new UnsupportedVersionException("Support for clients using a tooling API version older than 1.2 was removed in Gradle 2.0. You should upgrade your tooling API client to version 1.2 or later.");
     }
 
-    private static class VerboseLoggingOnlyConnectionParameters {
-        private final boolean verboseLogging;
-
-        public VerboseLoggingOnlyConnectionParameters(boolean verboseLogging) {
-            this.verboseLogging = verboseLogging;
-        }
-
-        public boolean getVerboseLogging() {
-            return verboseLogging;
-        }
+    private ProviderOperationParameters toProviderParameters(BuildParameters buildParameters) {
+        return adapter.adapt(ProviderOperationParameters.class, buildParameters, BuildLogLevelMixIn.class);
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultPayloadClassLoaderRegistry.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultPayloadClassLoaderRegistry.java
index 4148ddc..efe9f9e 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultPayloadClassLoaderRegistry.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DefaultPayloadClassLoaderRegistry.java
@@ -16,8 +16,8 @@
 
 package org.gradle.tooling.internal.provider;
 
-import com.google.common.collect.MapMaker;
 import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Transformer;
 import org.gradle.internal.classloader.ClassLoaderSpec;
 import org.gradle.internal.classloader.ClassLoaderVisitor;
 import org.gradle.internal.classloader.MutableURLClassLoader;
@@ -27,21 +27,18 @@ import org.slf4j.LoggerFactory;
 
 import java.net.URL;
 import java.util.*;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 
 @ThreadSafe
 public class DefaultPayloadClassLoaderRegistry implements PayloadClassLoaderRegistry {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPayloadClassLoaderRegistry.class);
-    private final Lock lock = new ReentrantLock();
-    private final ModelClassLoaderFactory classLoaderFactory;
-    private Map<ClassLoader, ClassLoaderDetails> classLoaderDetails;
-    private Map<UUID, ClassLoader> classLoaderIds;
-
-    public DefaultPayloadClassLoaderRegistry(ModelClassLoaderFactory modelClassLoaderFactory) {
-        classLoaderDetails = new MapMaker().weakKeys().makeMap();
-        classLoaderIds = new MapMaker().weakValues().makeMap();
-        this.classLoaderFactory = modelClassLoaderFactory;
+    private final PayloadClassLoaderFactory classLoaderFactory;
+    private final ClassLoaderCache cache;
+    private final ClassLoaderToDetailsTransformer detailsToClassLoader = new ClassLoaderToDetailsTransformer();
+    private final DetailsToClassLoaderTransformer classLoaderToDetails = new DetailsToClassLoaderTransformer();
+
+    public DefaultPayloadClassLoaderRegistry(ClassLoaderCache cache, PayloadClassLoaderFactory payloadClassLoaderFactory) {
+        this.cache = cache;
+        this.classLoaderFactory = payloadClassLoaderFactory;
     }
 
     public SerializeMap newSerializeSession() {
@@ -83,63 +80,11 @@ public class DefaultPayloadClassLoaderRegistry implements PayloadClassLoaderRegi
     }
 
     private ClassLoader getClassLoader(ClassLoaderDetails details) {
-        lock.lock();
-        try {
-            ClassLoader classLoader = classLoaderIds.get(details.uuid);
-            if (classLoader != null) {
-                return classLoader;
-            }
-
-            List<ClassLoader> parents = new ArrayList<ClassLoader>();
-            for (ClassLoaderDetails parentDetails : details.parents) {
-                parents.add(getClassLoader(parentDetails));
-            }
-            if (parents.isEmpty()) {
-                parents.add(classLoaderFactory.getClassLoaderFor(ClassLoaderSpec.SYSTEM_CLASS_LOADER, null));
-            }
-
-            LOGGER.info("Creating ClassLoader {} from {} and {}.", details.uuid, details.spec, parents);
-
-            classLoader = classLoaderFactory.getClassLoaderFor(details.spec, parents);
-            classLoaderIds.put(details.uuid, classLoader);
-            classLoaderDetails.put(classLoader, details);
-            return classLoader;
-        } finally {
-            lock.unlock();
-        }
+        return cache.getClassLoader(details, detailsToClassLoader);
     }
 
     private ClassLoaderDetails getDetails(ClassLoader classLoader) {
-        lock.lock();
-        try {
-            ClassLoaderDetails details = classLoaderDetails.get(classLoader);
-            if (details != null) {
-                return details;
-            }
-
-            ClassLoaderSpecVisitor visitor = new ClassLoaderSpecVisitor(classLoader);
-            visitor.visit(classLoader);
-
-            if (visitor.spec == null) {
-                if (visitor.classPath == null) {
-                    visitor.spec = ClassLoaderSpec.SYSTEM_CLASS_LOADER;
-                } else {
-                    visitor.spec = new MutableURLClassLoader.Spec(CollectionUtils.toList(visitor.classPath));
-                }
-            }
-
-            UUID uuid = UUID.randomUUID();
-            details = new ClassLoaderDetails(uuid, visitor.spec);
-            for (ClassLoader parent : visitor.parents) {
-                details.parents.add(getDetails(parent));
-            }
-
-            classLoaderDetails.put(classLoader, details);
-            classLoaderIds.put(details.uuid, classLoader);
-            return details;
-        } finally {
-            lock.unlock();
-        }
+        return cache.getDetails(classLoader, classLoaderToDetails);
     }
 
     private static class ClassLoaderSpecVisitor extends ClassLoaderVisitor {
@@ -171,4 +116,42 @@ public class DefaultPayloadClassLoaderRegistry implements PayloadClassLoaderRegi
             this.spec = spec;
         }
     }
+
+    private class ClassLoaderToDetailsTransformer implements Transformer<ClassLoader, ClassLoaderDetails> {
+        public ClassLoader transform(ClassLoaderDetails details) {
+            List<ClassLoader> parents = new ArrayList<ClassLoader>();
+            for (ClassLoaderDetails parentDetails : details.parents) {
+                parents.add(getClassLoader(parentDetails));
+            }
+            if (parents.isEmpty()) {
+                parents.add(classLoaderFactory.getClassLoaderFor(ClassLoaderSpec.SYSTEM_CLASS_LOADER, null));
+            }
+
+            LOGGER.info("Creating ClassLoader {} from {} and {}.", details.uuid, details.spec, parents);
+
+            return classLoaderFactory.getClassLoaderFor(details.spec, parents);
+        }
+    }
+
+    private class DetailsToClassLoaderTransformer implements Transformer<ClassLoaderDetails, ClassLoader> {
+        public ClassLoaderDetails transform(ClassLoader classLoader) {
+            ClassLoaderSpecVisitor visitor = new ClassLoaderSpecVisitor(classLoader);
+            visitor.visit(classLoader);
+
+            if (visitor.spec == null) {
+                if (visitor.classPath == null) {
+                    visitor.spec = ClassLoaderSpec.SYSTEM_CLASS_LOADER;
+                } else {
+                    visitor.spec = new MutableURLClassLoader.Spec(CollectionUtils.toList(visitor.classPath));
+                }
+            }
+
+            UUID uuid = UUID.randomUUID();
+            ClassLoaderDetails details = new ClassLoaderDetails(uuid, visitor.spec);
+            for (ClassLoader parent : visitor.parents) {
+                details.parents.add(getDetails(parent));
+            }
+            return details;
+        }
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java
new file mode 100644
index 0000000..8abeb4c
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ExecuteBuildActionRunner.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.invocation.BuildController;
+import org.gradle.launcher.cli.ExecuteBuildAction;
+import org.gradle.internal.invocation.BuildActionRunner;
+
+public class ExecuteBuildActionRunner implements BuildActionRunner {
+    @Override
+    public void run(BuildAction action, BuildController buildController) {
+        if (action instanceof ExecuteBuildAction) {
+            buildController.run();
+            buildController.setResult(null);
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/InternalCancellationTokenAdapter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/InternalCancellationTokenAdapter.java
new file mode 100644
index 0000000..79b1194
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/InternalCancellationTokenAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.tooling.internal.protocol.InternalCancellationToken;
+
+public class InternalCancellationTokenAdapter implements BuildCancellationToken {
+    private final InternalCancellationToken cancellationToken;
+
+    public InternalCancellationTokenAdapter(InternalCancellationToken cancellationToken) {
+        this.cancellationToken = cancellationToken;
+    }
+
+    public boolean isCancellationRequested() {
+        return cancellationToken.isCancellationRequested();
+    }
+
+    public boolean addCallback(Runnable cancellationHandler) {
+        return cancellationToken.addCallback(cancellationHandler);
+    }
+
+    public void removeCallback(Runnable cancellationHandler) {
+        cancellationToken.removeCallback(cancellationHandler);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/JarCache.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/JarCache.java
new file mode 100644
index 0000000..58bc782
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/JarCache.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.Factory;
+import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+ at ThreadSafe
+public class JarCache {
+    private final Lock lock = new ReentrantLock();
+    private final Cache<File, FileInfo> cachedFiles;
+
+    public JarCache() {
+        this.cachedFiles = CacheBuilder.newBuilder().maximumSize(200).build();
+    }
+
+    /**
+     * Returns a cached copy of the given file. The cached copy is guaranteed to not be modified or removed.
+     *
+     * @param original The source file.
+     * @param baseDirFactory A factory that can provide a base directory for the file cache.
+     * @return The cached file.
+     */
+    public File getCachedJar(File original, Factory<File> baseDirFactory) {
+        File source = GFileUtils.canonicalise(original);
+        FileInfo fileInfo;
+        // TODO - do some of this work concurrently
+        lock.lock();
+        try {
+            fileInfo = cachedFiles.getIfPresent(source);
+            if (fileInfo == null || !fileInfo.cachedFile.exists()) {
+                // TODO - use the hashing service for this
+                long lastModified = source.lastModified();
+                long length = source.length();
+                HashValue hashValue = HashUtil.createHash(source, "sha1");
+                fileInfo = copyIntoCache(baseDirFactory, source, lastModified, length, hashValue);
+            } else {
+                // TODO - use the change detection service for this
+                long lastModified = source.lastModified();
+                long length = source.length();
+                if (lastModified != fileInfo.lastModified || length != fileInfo.length) {
+                    HashValue hashValue = HashUtil.createHash(source, "sha1");
+                    if (!hashValue.equals(fileInfo.hashValue)) {
+                        fileInfo = copyIntoCache(baseDirFactory, source, lastModified, length, hashValue);
+                    }
+                }
+            }
+        } finally {
+            lock.unlock();
+        }
+        return fileInfo.cachedFile;
+    }
+
+    private FileInfo copyIntoCache(Factory<File> baseDirFactory, File source, long lastModified, long length, HashValue hashValue) {
+        File baseDir = baseDirFactory.create();
+        File cachedFile = new File(baseDir, hashValue.asCompactString() + '/' + source.getName());
+        if (!cachedFile.isFile()) {
+            // Has previously been added to the cache directory
+            GFileUtils.copyFile(source, cachedFile);
+        }
+        FileInfo fileInfo = new FileInfo(lastModified, length, hashValue, cachedFile);
+        cachedFiles.put(source, fileInfo);
+        return fileInfo;
+    }
+
+    private static class FileInfo {
+        final long lastModified;
+        final long length;
+        final HashValue hashValue;
+        final File cachedFile;
+
+        private FileInfo(long lastModified, long length, HashValue hashValue, File cachedFile) {
+            this.lastModified = lastModified;
+            this.length = length;
+            this.hashValue = hashValue;
+            this.cachedFile = cachedFile;
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java
new file mode 100644
index 0000000..da2bea7
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LauncherServices.java
@@ -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.tooling.internal.provider;
+
+import org.gradle.cache.CacheRepository;
+import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.invocation.BuildActionRunner;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.launcher.exec.ChainingBuildActionRunner;
+import org.gradle.launcher.exec.InProcessBuildActionExecuter;
+
+import java.util.List;
+
+public class LauncherServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new ToolingGlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new ToolingBuildScopeServices());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    static class ToolingGlobalScopeServices {
+        InProcessBuildActionExecuter createBuildActionExecuter(GradleLauncherFactory gradleLauncherFactory, ServiceRegistry services) {
+            List<BuildActionRunner> buildActionRunners = services.getAll(BuildActionRunner.class);
+            return new InProcessBuildActionExecuter(gradleLauncherFactory, new ChainingBuildActionRunner(buildActionRunners));
+        }
+
+        ExecuteBuildActionRunner createExecuteBuildActionRunner() {
+            return new ExecuteBuildActionRunner();
+        }
+
+        ClassLoaderCache createClassLoaderCache() {
+            return new ClassLoaderCache();
+        }
+
+        JarCache createJarCache() {
+            return new JarCache();
+        }
+    }
+
+    static class ToolingBuildScopeServices {
+        PayloadClassLoaderFactory createClassLoaderFactory(ClassLoaderFactory classLoaderFactory, JarCache jarCache, CacheRepository cacheRepository) {
+            return new DaemonSidePayloadClassLoaderFactory(
+                    new ModelClassLoaderFactory(
+                            classLoaderFactory),
+                    jarCache,
+                    cacheRepository);
+        }
+
+        PayloadSerializer createPayloadSerializer(ClassLoaderCache classLoaderCache, PayloadClassLoaderFactory classLoaderFactory) {
+            return new PayloadSerializer(
+                    new DefaultPayloadClassLoaderRegistry(
+                            classLoaderCache,
+                            classLoaderFactory)
+            );
+        }
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
index 7ebe715..62b10e7 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
@@ -15,11 +15,15 @@
  */
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.initialization.BuildAction;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.initialization.BuildRequestContext;
 import org.gradle.internal.Factory;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.logging.internal.*;
+import org.gradle.logging.internal.OutputEvent;
+import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.logging.internal.ProgressCompleteEvent;
+import org.gradle.logging.internal.ProgressStartEvent;
 import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
 import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
 
@@ -36,13 +40,18 @@ public class LoggingBridgingBuildActionExecuter implements BuildActionExecuter<P
         this.loggingManagerFactory = loggingManagerFactory;
     }
 
-    public <T> T execute(BuildAction<T> action, ProviderOperationParameters actionParameters) {
+    public Object execute(BuildAction action, BuildRequestContext buildRequestContext, ProviderOperationParameters actionParameters) {
         LoggingManagerInternal loggingManager = loggingManagerFactory.create();
-        if (actionParameters.getStandardOutput() != null) {
-            loggingManager.addStandardOutputListener(new StreamBackedStandardOutputListener(actionParameters.getStandardOutput()));
-        }
-        if (actionParameters.getStandardError() != null) {
-            loggingManager.addStandardErrorListener(new StreamBackedStandardOutputListener(actionParameters.getStandardError()));
+        loggingManager.removeAllOutputEventListeners();
+        if (Boolean.TRUE.equals(actionParameters.isColorOutput(null)) && actionParameters.getStandardOutput() != null) {
+            loggingManager.attachAnsiConsole(actionParameters.getStandardOutput());
+        } else {
+            if (actionParameters.getStandardOutput() != null) {
+                loggingManager.addStandardOutputListener(actionParameters.getStandardOutput());
+            }
+            if (actionParameters.getStandardError() != null) {
+                loggingManager.addStandardErrorListener(actionParameters.getStandardError());
+            }
         }
         ProgressListenerVersion1 progressListener = actionParameters.getProgressListener();
         OutputEventListenerAdapter listener = new OutputEventListenerAdapter(progressListener);
@@ -50,8 +59,9 @@ public class LoggingBridgingBuildActionExecuter implements BuildActionExecuter<P
         loggingManager.setLevel(actionParameters.getBuildLogLevel());
         loggingManager.start();
         try {
-            return executer.execute(action, actionParameters);
+            return executer.execute(action, buildRequestContext, actionParameters);
         } finally {
+            loggingManager.removeAllOutputEventListeners();
             loggingManager.stop();
         }
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ModelClassLoaderFactory.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ModelClassLoaderFactory.java
index 5315005..8c34461 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ModelClassLoaderFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ModelClassLoaderFactory.java
@@ -16,24 +16,23 @@
 
 package org.gradle.tooling.internal.provider;
 
-import org.gradle.internal.classloader.*;
-import org.gradle.tooling.provider.model.internal.LegacyConsumerInterface;
-import org.objectweb.asm.*;
+import org.gradle.TaskExecutionRequest;
+import org.gradle.internal.classloader.ClassLoaderFactory;
+import org.gradle.internal.classloader.ClassLoaderSpec;
+import org.gradle.internal.classloader.FilteringClassLoader;
 
-import java.net.URL;
-import java.util.Arrays;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 
-public class ModelClassLoaderFactory {
+public class ModelClassLoaderFactory implements PayloadClassLoaderFactory {
     private final ClassLoader rootClassLoader;
-    private final ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
+    private final ClassLoaderFactory classLoaderFactory;
 
-    public ModelClassLoaderFactory() {
+    public ModelClassLoaderFactory(ClassLoaderFactory classLoaderFactory) {
+        this.classLoaderFactory = classLoaderFactory;
         ClassLoader parent = getClass().getClassLoader();
         FilteringClassLoader filter = new FilteringClassLoader(parent);
         filter.allowPackage("org.gradle.tooling.internal.protocol");
+        filter.allowClass(TaskExecutionRequest.class);
         rootClassLoader = filter;
     }
 
@@ -41,86 +40,6 @@ public class ModelClassLoaderFactory {
         if (spec.equals(ClassLoaderSpec.SYSTEM_CLASS_LOADER)) {
             return rootClassLoader;
         }
-        if (spec instanceof MutableURLClassLoader.Spec) {
-            MutableURLClassLoader.Spec clSpec = (MutableURLClassLoader.Spec) spec;
-            if (parents.size() != 1) {
-                throw new IllegalStateException("Expected exactly one parent ClassLoader");
-            }
-            return new MixInClassLoader(parents.get(0), clSpec.getClasspath());
-        }
         return classLoaderFactory.createClassLoader(spec, parents);
     }
-
-    private static class MixInClassLoader extends TransformingClassLoader {
-        public MixInClassLoader(ClassLoader parent, List<URL> classPath) {
-            super(parent, classPath);
-        }
-
-        @Override
-        protected byte[] transform(byte[] bytes) {
-            // First scan for annotation, and short circuit transformation if not present
-            ClassReader classReader = new ClassReader(bytes);
-
-            AnnotationDetector detector = new AnnotationDetector();
-            classReader.accept(detector, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
-            if (!detector.found) {
-                return bytes;
-            }
-
-            if (findLoadedClass(detector.interfaceName) == null) {
-                // TODO:ADAM - need to do this earlier
-                ClassWriter emptyWriter = new ClassWriter(0);
-                emptyWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, detector.interfaceName.replace(".", "/"), null, Type.getType(Object.class).getInternalName(), null);
-                emptyWriter.visitEnd();
-                byte[] emptyBytecode = emptyWriter.toByteArray();
-                defineClass(detector.interfaceName, emptyBytecode, 0, emptyBytecode.length);
-            }
-
-            ClassWriter classWriter = new ClassWriter(0);
-            classReader.accept(new TransformingAdapter(classWriter, detector.interfaceName), 0);
-            bytes = classWriter.toByteArray();
-            return bytes;
-        }
-
-        private static class AnnotationDetector extends ClassVisitor {
-            private static final String ANNOTATION_DESCRIPTOR = Type.getType(LegacyConsumerInterface.class).getDescriptor();
-            String interfaceName;
-            private boolean found;
-
-            private AnnotationDetector() {
-                super(Opcodes.ASM4);
-            }
-
-            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-                if (desc.equals(ANNOTATION_DESCRIPTOR)) {
-                    found = true;
-                }
-                return new AnnotationVisitor(Opcodes.ASM4) {
-
-                    @Override
-                    public void visit(String name, Object value) {
-                        if (name.equals("value")) {
-                            interfaceName = value.toString();
-                        }
-                    }
-                };
-            }
-        }
-
-        private static class TransformingAdapter extends ClassVisitor {
-            private final String mixInInterface;
-
-            public TransformingAdapter(ClassWriter classWriter, String mixInInterface) {
-                super(Opcodes.ASM4, classWriter);
-                this.mixInInterface = mixInInterface;
-            }
-
-            @Override
-            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
-                Set<String> allInterfaces = new LinkedHashSet<String>(Arrays.asList(interfaces));
-                allInterfaces.add(mixInInterface.replace(".", "/"));
-                super.visit(version, access, name, signature, superName, allInterfaces.toArray(new String[allInterfaces.size()]));
-            }
-        }
-    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadClassLoaderFactory.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadClassLoaderFactory.java
new file mode 100644
index 0000000..a70215e
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadClassLoaderFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.internal.classloader.ClassLoaderSpec;
+
+import java.util.List;
+
+/**
+ * Used to create ClassLoaders used to serialize objects between the tooling api provider and daemon.
+ *
+ * <p>Implementations are not required to be thread-safe.</p>
+ */
+public interface PayloadClassLoaderFactory {
+    ClassLoader getClassLoaderFor(ClassLoaderSpec spec, List<? extends ClassLoader> parents);
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadSerializer.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadSerializer.java
index ab4405c..89930d0 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadSerializer.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/PayloadSerializer.java
@@ -19,7 +19,8 @@ package org.gradle.tooling.internal.provider;
 import net.jcip.annotations.ThreadSafe;
 import org.gradle.api.Transformer;
 import org.gradle.internal.UncheckedException;
-import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.classloader.DefaultClassLoaderFactory;
+import org.gradle.tooling.internal.provider.jdk6.Jdk6ClassLookup;
 
 import java.io.*;
 import java.lang.reflect.Proxy;
@@ -30,32 +31,20 @@ import java.util.Set;
 @ThreadSafe
 public class PayloadSerializer {
     private static final short SYSTEM_CLASS_LOADER_ID = (short) -1;
+    private static final ClassLoader SYSTEM_CLASS_LOADER = new DefaultClassLoaderFactory().getIsolatedSystemClassLoader();
     private static final Set<ClassLoader> SYSTEM_CLASS_LOADERS = new HashSet<ClassLoader>();
     private final Transformer<ObjectStreamClass, Class<?>> classLookup;
     private final PayloadClassLoaderRegistry classLoaderRegistry;
 
     static {
-        for (ClassLoader cl = ClassLoader.getSystemClassLoader().getParent(); cl != null; cl = cl.getParent()) {
+        for (ClassLoader cl = SYSTEM_CLASS_LOADER; cl != null; cl = cl.getParent()) {
             SYSTEM_CLASS_LOADERS.add(cl);
         }
     }
 
     public PayloadSerializer(PayloadClassLoaderRegistry registry) {
         classLoaderRegistry = registry;
-
-        // On Java 6, there is a public method to lookup a class descriptor given a class. On Java 5, we have to use reflection
-        // TODO:ADAM - move this into the service registry
-        if (Jvm.current().getJavaVersion().isJava6Compatible()) {
-            // Use the public method
-            try {
-                classLookup = (Transformer<ObjectStreamClass, Class<?>>) getClass().getClassLoader().loadClass("org.gradle.tooling.internal.provider.jdk6.Jdk6ClassLookup").newInstance();
-            } catch (Exception e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        } else {
-            // Use Java 5 fallback which uses reflection
-            classLookup = new ReflectionClassLookup();
-        }
+        classLookup = new Jdk6ClassLookup();
     }
 
     public SerializedPayload serialize(Object payload) {
@@ -108,7 +97,7 @@ public class PayloadSerializer {
     public Object deserialize(SerializedPayload payload) {
         final DeserializeMap map = classLoaderRegistry.newDeserializeSession();
         try {
-            final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader().getParent();
+            final ClassLoader systemClassLoader = SYSTEM_CLASS_LOADER;
             final Map<Short, ClassLoaderDetails> classLoaderDetails = (Map<Short, ClassLoaderDetails>) payload.getHeader();
 
             final ObjectInputStream objectStream = new ObjectInputStream(new ByteArrayInputStream(payload.getSerializedModel())) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
index 2fd6489..05f10ad 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderConnection.java
@@ -18,30 +18,28 @@ package org.gradle.tooling.internal.provider;
 
 import org.gradle.StartParameter;
 import org.gradle.api.logging.LogLevel;
-import org.gradle.initialization.BuildAction;
-import org.gradle.initialization.BuildLayoutParameters;
-import org.gradle.initialization.GradleLauncherFactory;
+import org.gradle.initialization.*;
 import org.gradle.internal.Factory;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.launcher.cli.converter.LayoutToPropertiesConverter;
 import org.gradle.launcher.cli.converter.PropertiesToDaemonParametersConverter;
 import org.gradle.launcher.daemon.client.DaemonClient;
-import org.gradle.launcher.daemon.client.DaemonClientServices;
+import org.gradle.launcher.daemon.client.DaemonClientFactory;
 import org.gradle.launcher.daemon.configuration.DaemonParameters;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.launcher.exec.BuildActionParameters;
-import org.gradle.launcher.exec.InProcessBuildActionExecuter;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.logging.internal.OutputEventListener;
 import org.gradle.logging.internal.OutputEventRenderer;
 import org.gradle.process.internal.streams.SafeStreams;
 import org.gradle.tooling.internal.build.DefaultBuildEnvironment;
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
-import org.gradle.tooling.internal.protocol.InternalBuildAction;
-import org.gradle.tooling.internal.protocol.InternalBuildEnvironment;
-import org.gradle.tooling.internal.protocol.ModelIdentifier;
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.tooling.internal.protocol.events.InternalTestProgressEvent;
 import org.gradle.tooling.internal.provider.connection.ProviderConnectionParameters;
 import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
-import org.gradle.util.GUtil;
 import org.gradle.util.GradleVersion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,11 +53,13 @@ public class ProviderConnection {
     private static final Logger LOGGER = LoggerFactory.getLogger(ProviderConnection.class);
     private final PayloadSerializer payloadSerializer;
     private final LoggingServiceRegistry loggingServices;
-    private final GradleLauncherFactory gradleLauncherFactory;
+    private final DaemonClientFactory daemonClientFactory;
+    private final BuildActionExecuter<BuildActionParameters> embeddedExecutor;
 
-    public ProviderConnection(LoggingServiceRegistry loggingServices, GradleLauncherFactory gradleLauncherFactory, PayloadSerializer payloadSerializer) {
+    public ProviderConnection(LoggingServiceRegistry loggingServices, DaemonClientFactory daemonClientFactory, BuildActionExecuter<BuildActionParameters> embeddedExecutor, PayloadSerializer payloadSerializer) {
         this.loggingServices = loggingServices;
-        this.gradleLauncherFactory = gradleLauncherFactory;
+        this.daemonClientFactory = daemonClientFactory;
+        this.embeddedExecutor = embeddedExecutor;
         this.payloadSerializer = payloadSerializer;
     }
 
@@ -71,7 +71,7 @@ public class ProviderConnection {
         loggingManager.start();
     }
 
-    public Object run(String modelName, ProviderOperationParameters providerParameters) {
+    public Object run(String modelName, BuildCancellationToken cancellationToken, ProviderOperationParameters providerParameters) {
         List<String> tasks = providerParameters.getTasks();
         if (modelName.equals(ModelIdentifier.NULL_MODEL) && tasks == null) {
             throw new IllegalArgumentException("No model type or tasks specified.");
@@ -84,43 +84,49 @@ public class ProviderConnection {
                 throw new IllegalArgumentException("Cannot run tasks and fetch the build environment model.");
             }
             return new DefaultBuildEnvironment(
+                    params.gradleUserhome,
                     GradleVersion.current().getVersion(),
                     params.daemonParams.getEffectiveJavaHome(),
                     params.daemonParams.getEffectiveJvmArgs());
         }
 
-        BuildAction<BuildActionResult> action = new BuildModelAction(modelName, tasks != null);
-        return run(action, providerParameters, params.properties);
+        StartParameter startParameter = new ProviderStartParameterConverter().toStartParameter(providerParameters, params.properties);
+        InternalBuildProgressListener buildProgressListener = providerParameters.getBuildProgressListener(null);
+        boolean listenToTestProgress = buildProgressListener != null && buildProgressListener.getSubscribedOperations().contains(InternalBuildProgressListener.TEST_EXECUTION);
+        BuildEventConsumer buildEventConsumer = listenToTestProgress ? new BuildProgressListenerInvokingBuildEventConsumer(buildProgressListener) : new NoOpBuildEventConsumer();
+        BuildAction action = new BuildModelAction(startParameter, modelName, tasks != null, listenToTestProgress);
+        return run(action, cancellationToken, buildEventConsumer, providerParameters, params);
     }
 
-    public Object run(InternalBuildAction<?> clientAction, ProviderOperationParameters providerParameters) {
+    public Object run(InternalBuildAction<?> clientAction, BuildCancellationToken cancellationToken, ProviderOperationParameters providerParameters) {
         SerializedPayload serializedAction = payloadSerializer.serialize(clientAction);
         Parameters params = initParams(providerParameters);
-        BuildAction<BuildActionResult> action = new ClientProvidedBuildAction(serializedAction);
-        return run(action, providerParameters, params.properties);
+        StartParameter startParameter = new ProviderStartParameterConverter().toStartParameter(providerParameters, params.properties);
+        NoOpBuildEventConsumer buildEventConsumer = new NoOpBuildEventConsumer();
+        BuildAction action = new ClientProvidedBuildAction(startParameter, serializedAction);
+        return run(action, cancellationToken, buildEventConsumer, providerParameters, params);
     }
 
-    private Object run(BuildAction<? extends BuildActionResult> action, ProviderOperationParameters operationParameters, Map<String, String> properties) {
-        BuildActionExecuter<ProviderOperationParameters> executer = createExecuter(operationParameters);
-        ConfiguringBuildAction<BuildActionResult> configuringAction = new ConfiguringBuildAction<BuildActionResult>(operationParameters, action, properties);
-        BuildActionResult result = executer.execute(configuringAction, operationParameters);
+    private Object run(BuildAction action, BuildCancellationToken cancellationToken, BuildEventConsumer buildEventConsumer, ProviderOperationParameters providerParameters, Parameters parameters) {
+        BuildActionExecuter<ProviderOperationParameters> executer = createExecuter(providerParameters, parameters);
+        BuildRequestContext buildRequestContext = new DefaultBuildRequestContext(new DefaultBuildRequestMetaData(providerParameters.getStartTime()), cancellationToken, buildEventConsumer);
+        BuildActionResult result = (BuildActionResult) executer.execute(action, buildRequestContext, providerParameters);
         if (result.failure != null) {
             throw (RuntimeException) payloadSerializer.deserialize(result.failure);
         }
         return payloadSerializer.deserialize(result.result);
     }
 
-    private BuildActionExecuter<ProviderOperationParameters> createExecuter(ProviderOperationParameters operationParameters) {
+    private BuildActionExecuter<ProviderOperationParameters> createExecuter(ProviderOperationParameters operationParameters, Parameters params) {
         LoggingServiceRegistry loggingServices;
-        Parameters params = initParams(operationParameters);
         BuildActionExecuter<BuildActionParameters> executer;
         if (Boolean.TRUE.equals(operationParameters.isEmbedded())) {
             loggingServices = this.loggingServices;
-            executer = new InProcessBuildActionExecuter(gradleLauncherFactory);
+            executer = embeddedExecutor;
         } else {
-            loggingServices = this.loggingServices.newLogging();
+            loggingServices = LoggingServiceRegistry.newNestedLogging();
             loggingServices.get(OutputEventRenderer.class).configure(operationParameters.getBuildLogLevel());
-            DaemonClientServices clientServices = new DaemonClientServices(loggingServices, params.daemonParams, operationParameters.getStandardInput(SafeStreams.emptyInput()));
+            ServiceRegistry clientServices = daemonClientFactory.createBuildClientServices(loggingServices.get(OutputEventListener.class), params.daemonParams, operationParameters.getStandardInput(SafeStreams.emptyInput()));
             executer = clientServices.get(DaemonClient.class);
         }
         Factory<LoggingManagerInternal> loggingManagerFactory = loggingServices.getFactory(LoggingManagerInternal.class);
@@ -128,16 +134,21 @@ public class ProviderConnection {
     }
 
     private Parameters initParams(ProviderOperationParameters operationParameters) {
-        BuildLayoutParameters layout = new BuildLayoutParameters()
-                .setGradleUserHomeDir(GUtil.elvis(operationParameters.getGradleUserHomeDir(), StartParameter.DEFAULT_GRADLE_USER_HOME))
-                .setSearchUpwards(operationParameters.isSearchUpwards() != null ? operationParameters.isSearchUpwards() : true)
-                .setProjectDir(operationParameters.getProjectDir());
+        BuildLayoutParameters layout = new BuildLayoutParameters();
+        if (operationParameters.getGradleUserHomeDir() != null) {
+            layout.setGradleUserHomeDir(operationParameters.getGradleUserHomeDir());
+        }
+        layout.setSearchUpwards(operationParameters.isSearchUpwards() != null ? operationParameters.isSearchUpwards() : true);
+        layout.setProjectDir(operationParameters.getProjectDir());
 
         Map<String, String> properties = new HashMap<String, String>();
         new LayoutToPropertiesConverter().convert(layout, properties);
 
         DaemonParameters daemonParams = new DaemonParameters(layout);
         new PropertiesToDaemonParametersConverter().convert(properties, daemonParams);
+        if (operationParameters.getDaemonBaseDir(null) != null) {
+            daemonParams.setBaseDir(operationParameters.getDaemonBaseDir(null));
+        }
 
         //override the params with the explicit settings provided by the tooling api
         List<String> defaultJvmArgs = daemonParams.getAllJvmArgs();
@@ -149,17 +160,36 @@ public class ProviderConnection {
             int idleTimeout = (int) operationParameters.getDaemonMaxIdleTimeUnits().toMillis(operationParameters.getDaemonMaxIdleTimeValue());
             daemonParams.setIdleTimeout(idleTimeout);
         }
-        return new Parameters(daemonParams, properties);
+
+        return new Parameters(daemonParams, properties, layout.getGradleUserHomeDir());
     }
 
     private static class Parameters {
         DaemonParameters daemonParams;
         Map<String, String> properties;
+        File gradleUserhome;
 
-        public Parameters(DaemonParameters daemonParams, Map<String, String> properties) {
+        public Parameters(DaemonParameters daemonParams, Map<String, String> properties, File gradleUserhome) {
             this.daemonParams = daemonParams;
             this.properties = properties;
+            this.gradleUserhome = gradleUserhome;
+        }
+    }
+
+    private static final class BuildProgressListenerInvokingBuildEventConsumer implements BuildEventConsumer {
+
+        private final InternalBuildProgressListener buildProgressListener;
+
+        private BuildProgressListenerInvokingBuildEventConsumer(InternalBuildProgressListener buildProgressListener) {
+            this.buildProgressListener = buildProgressListener;
+        }
+
+        @Override
+        public void dispatch(Object event) {
+            if (event instanceof InternalTestProgressEvent) {
+                this.buildProgressListener.onEvent(event);
+            }
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderStartParameterConverter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderStartParameterConverter.java
new file mode 100644
index 0000000..fb16830
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ProviderStartParameterConverter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.StartParameter;
+import org.gradle.TaskExecutionRequest;
+import org.gradle.cli.CommandLineArgumentException;
+import org.gradle.initialization.DefaultCommandLineConverter;
+import org.gradle.internal.DefaultTaskExecutionRequest;
+import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter;
+import org.gradle.tooling.internal.protocol.InternalLaunchable;
+import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException;
+import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+class ProviderStartParameterConverter {
+
+    private List<TaskExecutionRequest> unpack(final List<InternalLaunchable> launchables) {
+        // Important that the launchables are unpacked on the client side, to avoid sending back any additional internal state that
+        // the launchable may hold onto. For example, GradleTask implementations hold onto every task for every project in the build
+        List<TaskExecutionRequest> requests = new ArrayList<TaskExecutionRequest>(launchables.size());
+        for (InternalLaunchable launchable : launchables) {
+            if (launchable instanceof TaskExecutionRequest) {
+                TaskExecutionRequest originalLaunchable = (TaskExecutionRequest) launchable;
+                TaskExecutionRequest launchableImpl = new DefaultTaskExecutionRequest(originalLaunchable.getArgs(), originalLaunchable.getProjectPath());
+                requests.add(launchableImpl);
+            } else {
+                throw new InternalUnsupportedBuildArgumentException(
+                        "Problem with provided launchable arguments: " + launchables + ". "
+                                + "\nOnly objects from this provider can be built."
+                );
+            }
+        }
+        return requests;
+    }
+
+    public StartParameter toStartParameter(ProviderOperationParameters parameters, Map<String, String> properties) {
+        // Important that this is constructed on the client so that it has the right gradleHomeDir and other state internally
+        StartParameter startParameter = new StartParameter();
+
+        startParameter.setProjectDir(parameters.getProjectDir());
+        if (parameters.getGradleUserHomeDir() != null) {
+            startParameter.setGradleUserHomeDir(parameters.getGradleUserHomeDir());
+        }
+
+        List<InternalLaunchable> launchables = parameters.getLaunchables(null);
+        if (launchables != null) {
+            startParameter.setTaskRequests(unpack(launchables));
+        } else if (parameters.getTasks() != null) {
+            startParameter.setTaskNames(parameters.getTasks());
+        }
+
+        new PropertiesToStartParameterConverter().convert(properties, startParameter);
+
+        List<String> arguments = parameters.getArguments(Collections.<String>emptyList());
+        if (arguments != null) {
+            DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
+            try {
+                converter.convert(arguments, startParameter);
+            } catch (CommandLineArgumentException e) {
+                throw new InternalUnsupportedBuildArgumentException(
+                    "Problem with provided build arguments: " + arguments + ". "
+                    + "\n" + e.getMessage()
+                    + "\nEither it is not a valid build option or it is not supported in the target Gradle version."
+                    + "\nNot all of the Gradle command line options are supported build arguments."
+                    + "\nExamples of supported build arguments: '--info', '-u', '-p'."
+                    + "\nExamples of unsupported build options: '--daemon', '-?', '-v'."
+                    + "\nPlease find more information in the javadoc for the BuildLauncher class.", e);
+            }
+        }
+
+        if (parameters.isSearchUpwards() != null) {
+            startParameter.setSearchUpwards(parameters.isSearchUpwards());
+        }
+
+        if (parameters.getBuildLogLevel() != null) {
+            startParameter.setLogLevel(parameters.getBuildLogLevel());
+        }
+
+        return startParameter;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ReflectionClassLookup.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ReflectionClassLookup.java
deleted file mode 100644
index abeca6c..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ReflectionClassLookup.java
+++ /dev/null
@@ -1,44 +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.tooling.internal.provider;
-
-import org.gradle.api.Transformer;
-import org.gradle.internal.UncheckedException;
-
-import java.io.ObjectStreamClass;
-import java.lang.reflect.Method;
-
-public class ReflectionClassLookup implements Transformer<ObjectStreamClass, Class<?>> {
-    private final Method lookupMethod;
-
-    public ReflectionClassLookup() {
-        try {
-            lookupMethod = ObjectStreamClass.class.getDeclaredMethod("lookup", Class.class, Boolean.TYPE);
-            lookupMethod.setAccessible(true);
-        } catch (NoSuchMethodException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public ObjectStreamClass transform(Class<?> original) {
-        try {
-            return (ObjectStreamClass) lookupMethod.invoke(null, original, true);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ShutdownCoordinator.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ShutdownCoordinator.java
new file mode 100644
index 0000000..a77a7c6
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ShutdownCoordinator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider;
+
+import org.gradle.initialization.BuildLayoutParameters;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.launcher.daemon.client.DaemonClientFactory;
+import org.gradle.launcher.daemon.client.DaemonStartListener;
+import org.gradle.launcher.daemon.client.DaemonStopClient;
+import org.gradle.launcher.daemon.configuration.DaemonParameters;
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails;
+import org.gradle.logging.internal.OutputEventRenderer;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class ShutdownCoordinator implements DaemonStartListener, Stoppable {
+    private final Set<DaemonInstanceDetails> daemons = new CopyOnWriteArraySet<DaemonInstanceDetails>();
+    private final DaemonClientFactory clientFactory;
+    private final OutputEventRenderer outputEventRenderer;
+
+    public ShutdownCoordinator(DaemonClientFactory clientFactory, OutputEventRenderer outputEventRenderer) {
+        this.clientFactory = clientFactory;
+        this.outputEventRenderer = outputEventRenderer;
+    }
+
+    public void daemonStarted(DaemonInstanceDetails daemon) {
+        daemons.add(daemon);
+    }
+
+    public void stop() {
+        ServiceRegistry clientServices = clientFactory.createStopDaemonServices(outputEventRenderer, new DaemonParameters(new BuildLayoutParameters()));
+        DaemonStopClient client = clientServices.get(DaemonStopClient.class);
+        client.gracefulStop(daemons);
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingGlobalScopeServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingGlobalScopeServices.java
deleted file mode 100644
index 4c6738d..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingGlobalScopeServices.java
+++ /dev/null
@@ -1,25 +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.tooling.internal.provider;
-
-class ToolingGlobalScopeServices {
-    PayloadSerializer createPayloadSerializer() {
-        return new PayloadSerializer(
-                new DefaultPayloadClassLoaderRegistry(
-                        new ModelClassLoaderFactory()));
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingServices.java
deleted file mode 100644
index d494d7f..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ToolingServices.java
+++ /dev/null
@@ -1,32 +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.tooling.internal.provider;
-
-import org.gradle.internal.service.ServiceRegistration;
-import org.gradle.internal.service.scopes.PluginServiceRegistry;
-
-public class ToolingServices implements PluginServiceRegistry {
-    public void registerGlobalServices(ServiceRegistration registration) {
-        registration.addProvider(new ToolingGlobalScopeServices());
-    }
-
-    public void registerBuildServices(ServiceRegistration registration) {
-    }
-
-    public void registerProjectServices(ServiceRegistration registration) {
-    }
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/AdaptedOperationParameters.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/AdaptedOperationParameters.java
deleted file mode 100644
index 92443de..0000000
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/AdaptedOperationParameters.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.provider.connection;
-
-import org.gradle.api.logging.LogLevel;
-import org.gradle.tooling.internal.adapter.CompatibleIntrospector;
-import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
-import org.gradle.tooling.internal.protocol.InternalLaunchable;
-import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-public class AdaptedOperationParameters implements ProviderOperationParameters {
-
-    private final BuildOperationParametersVersion1 delegate;
-    private final List<String> tasks;
-    
-    CompatibleIntrospector introspector;
-
-    public AdaptedOperationParameters(BuildOperationParametersVersion1 operationParameters) {
-        this(operationParameters, null);
-    }
-
-    public AdaptedOperationParameters(BuildOperationParametersVersion1 delegate, List<String> tasks) {
-        this.delegate = delegate;
-        this.introspector = new CompatibleIntrospector(delegate);
-        this.tasks = tasks == null ? null : new LinkedList<String>(tasks);
-    }
-
-    public InputStream getStandardInput(InputStream defaultInput) {
-        return maybeGet(defaultInput, "getStandardInput");
-    }
-
-    public LogLevel getBuildLogLevel() {
-        return new BuildLogLevelMixIn(this).getBuildLogLevel();
-    }
-
-    public boolean getVerboseLogging(boolean defaultValue) {
-        return introspector.getSafely(defaultValue, "getVerboseLogging");
-    }
-
-    public File getJavaHome(File defaultJavaHome) {
-        return maybeGet(defaultJavaHome, "getJavaHome");
-    }
-
-    public List<String> getJvmArguments(List<String> defaultJvmArgs) {
-        return maybeGet(defaultJvmArgs, "getJvmArguments");
-    }
-
-    private <T> T maybeGet(T defaultValue, String methodName) {
-        T out = introspector.getSafely(defaultValue, methodName);
-        if (out == null) {
-            return defaultValue;
-        }
-        return out;
-    }
-
-    public File getProjectDir() {
-        return delegate.getProjectDir();
-    }
-
-    public Boolean isSearchUpwards() {
-        return delegate.isSearchUpwards();
-    }
-
-    public File getGradleUserHomeDir() {
-        return delegate.getGradleUserHomeDir();
-    }
-
-    public Boolean isEmbedded() {
-        return delegate.isEmbedded();
-    }
-
-    public Integer getDaemonMaxIdleTimeValue() {
-        return delegate.getDaemonMaxIdleTimeValue();
-    }
-
-    public TimeUnit getDaemonMaxIdleTimeUnits() {
-        return delegate.getDaemonMaxIdleTimeUnits();
-    }
-
-    public long getStartTime() {
-        return delegate.getStartTime();
-    }
-
-    public OutputStream getStandardOutput() {
-        return delegate.getStandardOutput();
-    }
-
-    public OutputStream getStandardError() {
-        return delegate.getStandardError();
-    }
-
-    public ProgressListenerVersion1 getProgressListener() {
-        return delegate.getProgressListener();
-    }
-
-    public List<String> getArguments(List<String> defaultArguments) {
-        return maybeGet(defaultArguments, "getArguments");
-    }
-    
-    public List<String> getTasks() {
-        return tasks;
-    }
-
-    public List<InternalLaunchable> getLaunchables() {
-        return maybeGet(null, "getLaunchables");
-    }
-
-}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
index b799440..3b9c7c7 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
@@ -41,7 +41,7 @@ public class BuildLogLevelMixIn {
             return LogLevel.DEBUG;
         }
 
-        LoggingConfiguration loggingConfiguration = converter.convert(parsedCommandLine);
+        LoggingConfiguration loggingConfiguration = converter.convert(parsedCommandLine, new LoggingConfiguration());
         return loggingConfiguration.getLogLevel();
     }
 }
\ No newline at end of file
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderConnectionParameters.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderConnectionParameters.java
index ff457fe..8788d9d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderConnectionParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderConnectionParameters.java
@@ -16,8 +16,15 @@
 
 package org.gradle.tooling.internal.provider.connection;
 
+import org.gradle.api.Nullable;
+
+import java.io.File;
+
 public interface ProviderConnectionParameters {
     boolean getVerboseLogging();
 
     String getConsumerVersion();
+
+    @Nullable
+    File getGradleUserHomeDir(File defaultValue);
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderOperationParameters.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderOperationParameters.java
index 350be3e..26d59f0 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderOperationParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/ProviderOperationParameters.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.gradle.tooling.internal.provider.connection;
 
 import org.gradle.api.logging.LogLevel;
 import org.gradle.tooling.internal.protocol.InternalLaunchable;
 import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
+import org.gradle.tooling.internal.protocol.InternalBuildProgressListener;
 
 import java.io.File;
 import java.io.InputStream;
@@ -50,6 +50,8 @@ public interface ProviderOperationParameters {
 
     Boolean isEmbedded();
 
+    Boolean isColorOutput(Boolean defaultValue);
+
     OutputStream getStandardOutput();
 
     OutputStream getStandardError();
@@ -58,11 +60,15 @@ public interface ProviderOperationParameters {
 
     TimeUnit getDaemonMaxIdleTimeUnits();
 
+    File getDaemonBaseDir(File defaultDaemonBaseDir);
+
     ProgressListenerVersion1 getProgressListener();
 
+    InternalBuildProgressListener getBuildProgressListener(InternalBuildProgressListener defaultListener);
+
     List<String> getArguments(List<String> defaultArguments);
 
     List<String> getTasks();
 
-    List<InternalLaunchable> getLaunchables();
+    List<InternalLaunchable> getLaunchables(List<InternalLaunchable> defaultLaunchables);
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestProgressEvent.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestProgressEvent.java
new file mode 100644
index 0000000..aff5870
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestProgressEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestProgressEvent;
+
+import java.io.Serializable;
+
+public abstract class AbstractTestProgressEvent implements Serializable, InternalTestProgressEvent {
+    private final long eventTime;
+    private final DefaultTestDescriptor descriptor;
+
+    protected AbstractTestProgressEvent(long eventTime, DefaultTestDescriptor descriptor) {
+        this.eventTime = eventTime;
+        this.descriptor = descriptor;
+    }
+
+    public long getEventTime() {
+        return eventTime;
+    }
+
+    public DefaultTestDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestResult.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestResult.java
new file mode 100644
index 0000000..d966d10
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/AbstractTestResult.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestResult;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class AbstractTestResult implements Serializable, InternalTestResult {
+    private final long startTime;
+    private final long endTime;
+
+    public AbstractTestResult(long startTime, long endTime) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+    }
+
+    public abstract String getOutcomeDescription();
+
+    public long getStartTime() {
+        return startTime;
+    }
+
+    public long getEndTime() {
+        return endTime;
+    }
+
+    public List<DefaultFailure> getFailures() {
+        return Collections.emptyList();
+    }
+
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultFailure.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultFailure.java
new file mode 100644
index 0000000..064e164
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultFailure.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.InternalFailure;
+
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultFailure implements Serializable, InternalFailure {
+
+    private final String message;
+    private final String description;
+    private final DefaultFailure cause;
+
+    public DefaultFailure(String message, String description, DefaultFailure cause) {
+        this.message = message;
+        this.description = description;
+        this.cause = cause;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public List<? extends InternalFailure> getCauses() {
+        return cause == null ? Collections.<InternalFailure>emptyList() : Collections.singletonList(cause);
+    }
+
+    public static DefaultFailure fromThrowable(Throwable t) {
+        StringWriter out = new StringWriter();
+        PrintWriter wrt = new PrintWriter(out);
+        t.printStackTrace(wrt);
+        Throwable cause = t.getCause();
+        DefaultFailure causeFailure = cause != null && cause != t ? fromThrowable(cause) : null;
+        return new DefaultFailure(t.getMessage(), out.toString(), causeFailure);
+    }
+
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestDescriptor.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestDescriptor.java
new file mode 100644
index 0000000..3fea295
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestDescriptor.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalJvmTestDescriptor;
+
+import java.io.Serializable;
+
+public class DefaultTestDescriptor implements Serializable, InternalJvmTestDescriptor {
+
+    private final Object id;
+    private final String name;
+    private final String displayName;
+    private final String testKind;
+    private final String suiteName;
+    private final String className;
+    private final String methodName;
+    private final Object parentId;
+
+    public DefaultTestDescriptor(Object id, String name, String displayName, String testKind, String suiteName, String className, String methodName, Object parentId) {
+        this.id = id;
+        this.name = name;
+        this.displayName = displayName;
+        this.testKind = testKind;
+        this.suiteName = suiteName;
+        this.className = className;
+        this.methodName = methodName;
+        this.parentId = parentId;
+    }
+
+    @Override
+    public Object getId() {
+        return id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getTestKind() {
+        return testKind;
+    }
+
+    @Override
+    public String getSuiteName() {
+        return suiteName;
+    }
+
+    @Override
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public String getMethodName() {
+        return methodName;
+    }
+
+    @Override
+    public Object getParentId() {
+        return parentId;
+    }
+
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFailureResult.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFailureResult.java
new file mode 100644
index 0000000..8c4accf
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFailureResult.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestFailureResult;
+
+import java.util.List;
+
+public class DefaultTestFailureResult extends AbstractTestResult implements InternalTestFailureResult {
+    private final List<DefaultFailure> failures;
+
+    public DefaultTestFailureResult(long startTime, long endTime, List<DefaultFailure> failures) {
+        super(startTime, endTime);
+        this.failures = failures;
+    }
+
+    @Override
+    public String getOutcomeDescription() {
+        return "failed";
+    }
+
+    @Override
+    public List<DefaultFailure> getFailures() {
+        return failures;
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFinishedProgressEvent.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFinishedProgressEvent.java
new file mode 100644
index 0000000..2e90d75
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestFinishedProgressEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestFinishedProgressEvent;
+
+public class DefaultTestFinishedProgressEvent extends AbstractTestProgressEvent implements InternalTestFinishedProgressEvent {
+    private final AbstractTestResult result;
+
+    public DefaultTestFinishedProgressEvent(long eventTime, DefaultTestDescriptor descriptor, AbstractTestResult result) {
+        super(eventTime, descriptor);
+        this.result = result;
+    }
+
+    @Override
+    public AbstractTestResult getResult() {
+        return result;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return String.format("%s %s", getDescriptor().getDisplayName(), result.getOutcomeDescription());
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSkippedResult.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSkippedResult.java
new file mode 100644
index 0000000..29bec84
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSkippedResult.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestSkippedResult;
+
+public class DefaultTestSkippedResult extends AbstractTestResult implements InternalTestSkippedResult {
+    public DefaultTestSkippedResult(long startTime, long endTime) {
+        super(startTime, endTime);
+    }
+
+    @Override
+    public String getOutcomeDescription() {
+        return "skipped";
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestStartedProgressEvent.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestStartedProgressEvent.java
new file mode 100644
index 0000000..23add28
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestStartedProgressEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestStartedProgressEvent;
+
+public class DefaultTestStartedProgressEvent extends AbstractTestProgressEvent implements InternalTestStartedProgressEvent {
+    public DefaultTestStartedProgressEvent(long eventTime, DefaultTestDescriptor descriptor) {
+        super(eventTime, descriptor);
+    }
+
+    @Override
+    public String getDisplayName() {
+        return String.format("%s started", getDescriptor().getDisplayName());
+    }
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSuccessResult.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSuccessResult.java
new file mode 100644
index 0000000..fe56ba5
--- /dev/null
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/events/DefaultTestSuccessResult.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.events;
+
+import org.gradle.tooling.internal.protocol.events.InternalTestSuccessResult;
+
+public class DefaultTestSuccessResult extends AbstractTestResult implements InternalTestSuccessResult {
+    public DefaultTestSuccessResult(long startTime, long endTime) {
+        super(startTime, endTime);
+    }
+
+    @Override
+    public String getOutcomeDescription() {
+        return "succeeded";
+    }
+}
diff --git a/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
index af8bd56..c4d4482 100644
--- a/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
+++ b/subprojects/launcher/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -1 +1 @@
-org.gradle.tooling.internal.provider.ToolingServices
+org.gradle.tooling.internal.provider.LauncherServices
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy
index 0a0d202..dd3fe05 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy
@@ -44,7 +44,7 @@ class MainTest extends Specification {
         
     final String[] args = ['arg']
     
-    final Main main = new Main(args) {
+    final Main main = new Main() {
         protected ExecutionCompleter createCompleter() {
             [complete: { completedSuccessfully = true }, completeWithFailure: { completedWithFailure = true; failure = it }] as ExecutionCompleter
         }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/bootstrap/EntryPointTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/bootstrap/EntryPointTest.groovy
index c67e417..3348697 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/bootstrap/EntryPointTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/bootstrap/EntryPointTest.groovy
@@ -29,7 +29,7 @@ class EntryPointTest extends Specification {
         }
 
         @Override
-        protected void doAction(ExecutionListener listener) {
+        protected void doAction(String[] args, ExecutionListener listener) {
             action.execute(listener)
         }
     }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionsFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionsFactoryTest.groovy
index 7aebba1..f853608 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionsFactoryTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/BuildActionsFactoryTest.groovy
@@ -15,27 +15,32 @@
  */
 package org.gradle.launcher.cli
 
+import org.gradle.StartParameter
 import org.gradle.cli.CommandLineParser
 import org.gradle.cli.SystemPropertiesCommandLineConverter
 import org.gradle.initialization.DefaultCommandLineConverter
 import org.gradle.initialization.LayoutCommandLineConverter
+import org.gradle.internal.invocation.BuildActionRunner
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.launcher.cli.converter.DaemonCommandLineConverter
 import org.gradle.launcher.cli.converter.LayoutToPropertiesConverter
 import org.gradle.launcher.cli.converter.PropertiesToDaemonParametersConverter
 import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter
-import org.gradle.launcher.daemon.bootstrap.DaemonMain
+import org.gradle.launcher.daemon.bootstrap.ForegroundDaemonAction
 import org.gradle.launcher.daemon.client.DaemonClient
 import org.gradle.launcher.daemon.client.SingleUseDaemonClient
-import org.gradle.launcher.exec.InProcessBuildActionExecuter
+import org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter
 import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.logging.StyledTextOutputFactory
 import org.gradle.logging.internal.OutputEventListener
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.SetSystemProperties
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
+ at UsesNativeServices
 class BuildActionsFactoryTest extends Specification {
     @Rule
     public final SetSystemProperties sysProperties = new SetSystemProperties();
@@ -43,16 +48,31 @@ class BuildActionsFactoryTest extends Specification {
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     ServiceRegistry loggingServices = Mock()
     PropertiesToDaemonParametersConverter propertiesToDaemonParametersConverter = Stub()
+    PropertiesToStartParameterConverter propertiesToStartParameterConverter = Stub()
 
     BuildActionsFactory factory = new BuildActionsFactory(
-            loggingServices, Stub(DefaultCommandLineConverter), new DaemonCommandLineConverter(),
+            loggingServices, new DefaultCommandLineConverter(), new DaemonCommandLineConverter(),
             Stub(LayoutCommandLineConverter), Stub(SystemPropertiesCommandLineConverter),
-            Stub(LayoutToPropertiesConverter), Stub(PropertiesToStartParameterConverter),
+            Stub(LayoutToPropertiesConverter), propertiesToStartParameterConverter,
             propertiesToDaemonParametersConverter)
 
     def setup() {
         _ * loggingServices.get(OutputEventListener) >> Mock(OutputEventListener)
         _ * loggingServices.get(ProgressLoggerFactory) >> Mock(ProgressLoggerFactory)
+        _ * loggingServices.getAll(BuildActionRunner) >> []
+        _ * loggingServices.get(StyledTextOutputFactory) >> Mock(StyledTextOutputFactory)
+    }
+
+    def "check that --max-workers overrides org.gradle.workers.max"() {
+        when:
+        propertiesToStartParameterConverter.convert(_, _) >> { args ->
+            def startParameter = (StartParameter) args[1]
+            startParameter.setMaxWorkerCount(3)
+        }
+        RunBuildAction action = convert('--max-workers=5')
+
+        then:
+        action.startParameter.maxWorkerCount == 5
     }
 
     def "executes build"() {
@@ -92,8 +112,7 @@ class BuildActionsFactoryTest extends Specification {
         def action = convert('--stop')
 
         then:
-        // Relying on impl of Actions.toAction(Runnable)
-        action.runnable instanceof StopDaemonAction
+        action instanceof StopDaemonAction
     }
 
     def "runs daemon in foreground"() {
@@ -101,8 +120,7 @@ class BuildActionsFactoryTest extends Specification {
         def action = convert('--foreground')
 
         then:
-        // Relying on impl of Actions.toAction(Runnable)
-        action.runnable instanceof DaemonMain
+        action instanceof ForegroundDaemonAction
     }
 
     def "executes with single use daemon if java home is not current"() {
@@ -126,20 +144,17 @@ class BuildActionsFactoryTest extends Specification {
     }
 
     void isDaemon(def action) {
-        // Relying on impl of Actions.toAction(Runnable)
-        assert action.runnable instanceof RunBuildAction
-        assert action.runnable.executer instanceof DaemonClient
+        assert action instanceof RunBuildAction
+        assert action.executer instanceof DaemonClient
     }
 
     void isInProcess(def action) {
-        // Relying on impl of Actions.toAction(Runnable)
-        assert action.runnable instanceof RunBuildAction
-        assert action.runnable.executer instanceof InProcessBuildActionExecuter
+        assert action instanceof RunBuildAction
+        assert action.executer instanceof DaemonUsageSuggestingBuildActionExecuter
     }
 
     void isSingleUseDaemon(def action) {
-        // Relying on impl of Actions.toAction(Runnable)
-        assert action.runnable instanceof RunBuildAction
-        assert action.runnable.executer instanceof SingleUseDaemonClient
+        assert action instanceof RunBuildAction
+        assert action.executer instanceof SingleUseDaemonClient
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/CommandLineActionFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/CommandLineActionFactoryTest.groovy
index ea9f046..5a2fbc8 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/CommandLineActionFactoryTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/CommandLineActionFactoryTest.groovy
@@ -15,14 +15,19 @@
  */
 package org.gradle.launcher.cli
 
+import org.apache.tools.ant.Main
 import org.gradle.api.Action
 import org.gradle.cli.CommandLineArgumentException
-import org.gradle.cli.CommandLineConverter
 import org.gradle.cli.CommandLineParser
 import org.gradle.internal.Factory
+import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.os.OperatingSystem
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.launcher.bootstrap.ExecutionListener
-import org.gradle.logging.*
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.logging.StyledTextOutput
+import org.gradle.logging.StyledTextOutputFactory
 import org.gradle.logging.internal.OutputEventListener
 import org.gradle.logging.internal.StreamingStyledTextOutput
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -41,7 +46,6 @@ class CommandLineActionFactoryTest extends Specification {
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
     final ExecutionListener executionListener = Mock()
     final ServiceRegistry loggingServices = Mock()
-    final CommandLineConverter<LoggingConfiguration> loggingConfigurationConverter = Mock()
     final LoggingManagerInternal loggingManager = Mock()
     final CommandLineAction actionFactory1 = Mock()
     final CommandLineAction actionFactory2 = Mock()
@@ -61,7 +65,6 @@ class CommandLineActionFactoryTest extends Specification {
     def setup() {
         ProgressLoggerFactory progressLoggerFactory = Mock()
         _ * loggingServices.get(ProgressLoggerFactory) >> progressLoggerFactory
-        _ * loggingServices.get(CommandLineConverter) >> loggingConfigurationConverter
         _ * loggingServices.get(OutputEventListener) >> Mock(OutputEventListener)
         Factory<LoggingManagerInternal> loggingManagerFactory = Mock()
         _ * loggingServices.getFactory(LoggingManagerInternal) >> loggingManagerFactory
@@ -73,7 +76,7 @@ class CommandLineActionFactoryTest extends Specification {
     }
 
     def "delegates to each action factory to configure the command-line parser and create the action"() {
-        Action<ExecutionListener> rawAction = Mock()
+        def rawAction = Mock(Runnable)
 
         when:
         def action = factory.convert(["--some-option"])
@@ -88,7 +91,7 @@ class CommandLineActionFactoryTest extends Specification {
         1 * actionFactory1.configureCommandLineParser(!null) >> { CommandLineParser parser -> parser.option("some-option") }
         1 * actionFactory2.configureCommandLineParser(!null)
         1 * actionFactory1.createAction(!null, !null) >> rawAction
-        1 * rawAction.execute(executionListener)
+        1 * rawAction.run()
     }
 
     def "configures logging before parsing command-line"() {
@@ -149,7 +152,7 @@ class CommandLineActionFactoryTest extends Specification {
         0 * executionListener._
     }
 
-    def "ignores failure to parse logging configuration"() {
+    def "continues on failure to parse logging configuration"() {
         when:
         def action = factory.convert(["--logging=broken"])
         action.execute(executionListener)
@@ -161,7 +164,6 @@ class CommandLineActionFactoryTest extends Specification {
         outputs.stdErr.contains('--some-option')
 
         and:
-        1 * loggingConfigurationConverter.configure(!null) >> {CommandLineParser parser -> parser.option("logging").hasArgument(String)}
         1 * actionFactory1.configureCommandLineParser(!null) >> {CommandLineParser parser -> parser.option('some-option')}
         1 * executionListener.onFailure({it instanceof CommandLineArgumentException})
         0 * executionListener._
@@ -213,12 +215,28 @@ class CommandLineActionFactoryTest extends Specification {
     }
 
     def "displays version message"() {
+        def version = GradleVersion.current()
+        def expectedText = """
+------------------------------------------------------------
+Gradle ${version.version}
+------------------------------------------------------------
+
+Build time:   $version.buildTime
+Build number: $version.buildNumber
+Revision:     $version.revision
+
+Groovy:       $GroovySystem.version
+Ant:          $Main.antVersion
+JVM:          ${Jvm.current()}
+OS:           ${OperatingSystem.current()}
+"""
+
         when:
         def action = factory.convert([option])
         action.execute(executionListener)
 
         then:
-        outputs.stdOut.contains(GradleVersion.current().prettyPrint())
+        outputs.stdOut.contains(expectedText)
 
         and:
         1 * loggingManager.start()
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/GuiActionsFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/GuiActionsFactoryTest.groovy
index 62b70b6..df1b7b9 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/GuiActionsFactoryTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/GuiActionsFactoryTest.groovy
@@ -31,7 +31,7 @@ class GuiActionsFactoryTest extends Specification {
 
         then:
         // Relying on impl of Actions.toAction(Runnable)
-        action.runnable instanceof GuiActionsFactory.ShowGuiAction
+        action instanceof GuiActionsFactory.ShowGuiAction
     }
 
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
index 93e275c..af2847e 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/RunBuildActionTest.groovy
@@ -16,11 +16,13 @@
 package org.gradle.launcher.cli
 
 import org.gradle.StartParameter
+import org.gradle.api.logging.LogLevel
 import org.gradle.initialization.BuildClientMetaData
-import org.gradle.launcher.exec.BuildActionParameters
+import org.gradle.initialization.BuildRequestContext
+import org.gradle.initialization.FixedBuildCancellationToken
 import org.gradle.launcher.exec.BuildActionExecuter
+import org.gradle.launcher.exec.BuildActionParameters
 import spock.lang.Specification
-import org.gradle.api.logging.LogLevel
 
 class RunBuildActionTest extends Specification {
     final BuildActionExecuter<BuildActionParameters> client = Mock()
@@ -29,8 +31,8 @@ class RunBuildActionTest extends Specification {
     final File currentDir = new File('current-dir')
     final long startTime = 90
     final Map<String, String> systemProperties = [key: 'value']
-    final Map<String, String> envVariables = [key2: 'value2']
-    final RunBuildAction action = new RunBuildAction(client, startParameter, currentDir, clientMetaData, startTime, systemProperties, envVariables)
+    final BuildActionParameters parameters = Mock()
+    final RunBuildAction action = new RunBuildAction(client, startParameter, clientMetaData, startTime, parameters)
 
     def runsBuildUsingDaemon() {
         when:
@@ -38,13 +40,12 @@ class RunBuildActionTest extends Specification {
 
         then:
         startParameter.logLevel >> LogLevel.ERROR
-        1 * client.execute({!null}, {!null}) >> { args ->
-            ExecuteBuildAction action = args[0]
+        1 * client.execute({!null}, {!null}, {!null}) >> { ExecuteBuildAction action, BuildRequestContext context, BuildActionParameters build ->
             assert action.startParameter == startParameter
-            BuildActionParameters build = args[1]
-            assert build.clientMetaData == clientMetaData
-            assert build.startTime == startTime
-            assert build.systemProperties == systemProperties
+            assert context.cancellationToken instanceof FixedBuildCancellationToken
+            assert context.client == clientMetaData
+            assert context.buildTimeClock.startTime == startTime
+            assert build == parameters
         }
         0 * _._
     }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/StopDaemonActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/StopDaemonActionTest.groovy
index 5816783..1850854 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/StopDaemonActionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/StopDaemonActionTest.groovy
@@ -15,11 +15,11 @@
  */
 package org.gradle.launcher.cli
 
+import org.gradle.launcher.daemon.client.DaemonStopClient
 import spock.lang.Specification
-import org.gradle.launcher.daemon.client.DaemonClient
 
 class StopDaemonActionTest extends Specification {
-    final DaemonClient client = Mock()
+    final DaemonStopClient client = Mock()
     final StopDaemonAction action = new StopDaemonAction(client)
 
     def executesStopCommand() {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/DaemonCommandLineConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/DaemonCommandLineConverterTest.groovy
index 05873ef..db46bfc 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/DaemonCommandLineConverterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/DaemonCommandLineConverterTest.groovy
@@ -17,23 +17,36 @@
 package org.gradle.launcher.cli.converter
 
 import org.gradle.cli.CommandLineParser
+import org.gradle.initialization.BuildLayoutParameters
 import org.gradle.launcher.daemon.configuration.DaemonParameters
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
+import spock.lang.Unroll
 
+import static org.gradle.launcher.daemon.configuration.DaemonUsage.*
+
+ at UsesNativeServices
 class DaemonCommandLineConverterTest extends Specification {
+    @Unroll
+    def "converts daemon options - #options"() {
+        when:
+        def converted = convert(options)
+
+        then:
+        converted.daemonUsage == usage
 
-    def "converts daemon options"() {
-        expect:
-        !convert([]).enabled
-        !convert(['--no-daemon']).enabled
-        convert(['--daemon']).enabled
-        convert(['--no-daemon', '--daemon']).enabled
+        where:
+        options                     | usage
+        []                          | IMPLICITLY_DISABLED
+        ['--no-daemon']             | EXPLICITLY_DISABLED
+        ['--daemon']                | EXPLICITLY_ENABLED
+        ['--no-daemon', '--daemon'] | EXPLICITLY_ENABLED
     }
 
     private DaemonParameters convert(Iterable args) {
         CommandLineParser parser = new CommandLineParser()
         def converter = new DaemonCommandLineConverter()
         converter.configure(parser)
-        converter.convert(args)
+        converter.convert(args, new DaemonParameters(new BuildLayoutParameters()))
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/LayoutToPropertiesConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/LayoutToPropertiesConverterTest.groovy
index 4327a2f..2ea0c58 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/LayoutToPropertiesConverterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/LayoutToPropertiesConverterTest.groovy
@@ -92,4 +92,16 @@ class LayoutToPropertiesConverterTest extends Specification {
         then:
         converter.convert(layout, props).get(JVM_ARGS_PROPERTY) == '-Xmx2048m'
     }
+
+    def "non-serializable system properties are ignored"() {
+        when:
+        System.getProperties().put('foo', NULL_OBJECT)
+
+        then:
+        converter.convert(layout, props).foo == null
+    }
+
+    static class NotSerializable {
+    }
+    static final NULL_OBJECT = new NotSerializable()
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverterTest.groovy
index 29c7085..51df6ef 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToDaemonParametersConverterTest.groovy
@@ -22,14 +22,18 @@ import org.gradle.internal.jvm.Jvm
 import org.gradle.launcher.daemon.configuration.DaemonParameters
 import org.gradle.launcher.daemon.configuration.GradleProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
+import spock.lang.Unroll
 
+import static org.gradle.launcher.daemon.configuration.DaemonUsage.*
 import static org.gradle.launcher.daemon.configuration.GradleProperties.*
 
+ at UsesNativeServices
 class PropertiesToDaemonParametersConverterTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
 
     def converter = new PropertiesToDaemonParametersConverter()
     def params = new DaemonParameters(new BuildLayoutParameters())
@@ -56,26 +60,26 @@ class PropertiesToDaemonParametersConverterTest extends Specification {
     def "configures from gradle properties"() {
         when:
         converter.convert([
-                (JVM_ARGS_PROPERTY): '-Xmx256m',
-                (JAVA_HOME_PROPERTY): Jvm.current().javaHome.absolutePath,
-                (DAEMON_ENABLED_PROPERTY): "true",
+                (JVM_ARGS_PROPERTY)       : '-Xmx256m',
+                (JAVA_HOME_PROPERTY)      : Jvm.current().javaHome.absolutePath,
+                (DAEMON_ENABLED_PROPERTY) : "true",
                 (DAEMON_BASE_DIR_PROPERTY): new File("baseDir").absolutePath,
-                (IDLE_TIMEOUT_PROPERTY): "115",
-                (DEBUG_MODE_PROPERTY): "true",
+                (IDLE_TIMEOUT_PROPERTY)   : "115",
+                (DEBUG_MODE_PROPERTY)     : "true",
         ], params)
 
         then:
         params.effectiveJvmArgs.contains("-Xmx256m")
         params.debug
         params.effectiveJavaHome == Jvm.current().javaHome
-        params.enabled
+        params.daemonUsage == EXPLICITLY_ENABLED
         params.baseDir == new File("baseDir").absoluteFile
         params.idleTimeout == 115
     }
 
     def "shows nice message for dummy java home"() {
         when:
-        converter.convert([(JAVA_HOME_PROPERTY) : "/invalid/path"], params)
+        converter.convert([(JAVA_HOME_PROPERTY): "/invalid/path"], params)
 
         then:
         def ex = thrown(GradleException)
@@ -86,7 +90,7 @@ class PropertiesToDaemonParametersConverterTest extends Specification {
     def "shows nice message for invalid java home"() {
         def dummyDir = temp.createDir("foobar")
         when:
-        converter.convert([(GradleProperties.JAVA_HOME_PROPERTY) : dummyDir.absolutePath], params)
+        converter.convert([(GradleProperties.JAVA_HOME_PROPERTY): dummyDir.absolutePath], params)
 
         then:
         def ex = thrown(GradleException)
@@ -103,4 +107,26 @@ class PropertiesToDaemonParametersConverterTest extends Specification {
         ex.message.contains 'org.gradle.daemon.idletimeout'
         ex.message.contains 'asdf'
     }
+
+    def "does not explicitly set daemon usage if daemon system property is not specified"() {
+        when:
+        converter.convert([:], params)
+
+        then:
+        params.daemonUsage == IMPLICITLY_DISABLED
+    }
+
+    @Unroll
+    def "explicitly sets daemon usage if daemon system property is specified"() {
+        when:
+        converter.convert((GradleProperties.DAEMON_ENABLED_PROPERTY): enabled.toString(), params)
+
+        then:
+        params.daemonUsage == usage
+
+        where:
+        enabled | usage
+        true    | EXPLICITLY_ENABLED
+        false   | EXPLICITLY_DISABLED
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverterTest.groovy
index 5c93357..fb6a846 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/PropertiesToStartParameterConverterTest.groovy
@@ -19,8 +19,7 @@ package org.gradle.launcher.cli.converter
 import org.gradle.StartParameter
 import spock.lang.Specification
 
-import static org.gradle.launcher.daemon.configuration.GradleProperties.CONFIGURE_ON_DEMAND_PROPERTY
-import static org.gradle.launcher.daemon.configuration.GradleProperties.PARALLEL_PROPERTY
+import static org.gradle.launcher.daemon.configuration.GradleProperties.*
 
 class PropertiesToStartParameterConverterTest extends Specification {
 
@@ -28,9 +27,17 @@ class PropertiesToStartParameterConverterTest extends Specification {
 
     def "converts"() {
         expect:
-        converter.convert([(PARALLEL_PROPERTY): "true"], new StartParameter()).parallelThreadCount == -1
+        converter.convert([(WORKERS_PROPERTY): "37"], new StartParameter()).maxWorkerCount == 37
+        converter.convert([(PARALLEL_PROPERTY): "true"], new StartParameter()).parallelProjectExecutionEnabled
         converter.convert([(PARALLEL_PROPERTY): "false"], new StartParameter()).parallelThreadCount == 0
         converter.convert([(CONFIGURE_ON_DEMAND_PROPERTY): "TRUE"], new StartParameter()).configureOnDemand
         !converter.convert([(CONFIGURE_ON_DEMAND_PROPERTY): "xxx"], new StartParameter()).configureOnDemand
     }
+
+    def invalidMaxWorkersProperty() {
+        when:
+        converter.convert([(WORKERS_PROPERTY): "invalid"], new StartParameter())
+        then:
+        thrown(IllegalArgumentException)
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/DaemonExecHandleBuilderSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/DaemonExecHandleBuilderSpec.groovy
index 8303cb3..ad8dc79 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/DaemonExecHandleBuilderSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/DaemonExecHandleBuilderSpec.groovy
@@ -18,10 +18,11 @@ package org.gradle.launcher.daemon
 
 import org.gradle.launcher.daemon.bootstrap.DaemonOutputConsumer
 import org.gradle.process.internal.ExecHandleBuilder
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
+ at UsesNativeServices
 class DaemonExecHandleBuilderSpec extends Specification {
-
     def builder = Mock(ExecHandleBuilder)
     def daemonBuilder = new DaemonExecHandleBuilder(builder: builder)
 
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonGreeterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonGreeterTest.groovy
index 686dd4a..b84ebae 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonGreeterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonGreeterTest.groovy
@@ -19,25 +19,36 @@ package org.gradle.launcher.daemon.bootstrap
 import org.gradle.api.GradleException
 import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.launcher.daemon.logging.DaemonMessages
+import org.gradle.messaging.remote.internal.inet.MultiChoiceAddress
 import org.gradle.process.ExecResult
 import spock.lang.Specification
 
 class DaemonGreeterTest extends Specification {
 
-    DocumentationRegistry registry = Mock()
+    def registry = Mock(DocumentationRegistry)
 
     def "parses the process output"() {
         given:
-        def output = """hey joe!
+        def address = new MultiChoiceAddress(UUID.randomUUID(), 123, [])
+
+        def outputStream = new ByteArrayOutputStream()
+        def printStream = new PrintStream(outputStream)
+        printStream.print("""hey joe!
 another line of output...
-${new DaemonStartupCommunication().daemonStartedMessage(12, new File("12.log"))}"""
+""")
+
+        new DaemonStartupCommunication().printDaemonStarted(printStream, 12, "uid", address, new File("12.log"))
+        def output = new String(outputStream.toByteArray())
 
         when:
-        def diagnostics = new DaemonGreeter(registry).parseDaemonOutput(output, Mock(ExecResult))
+        def daemonStartupInfo = new DaemonGreeter(registry).parseDaemonOutput(output, Mock(ExecResult))
 
         then:
-        diagnostics.pid == 12
-        diagnostics.daemonLog == new File("12.log")
+        daemonStartupInfo.address == address
+        daemonStartupInfo.uid == "uid"
+        daemonStartupInfo.pid == 12
+        daemonStartupInfo.diagnostics.pid == 12
+        daemonStartupInfo.diagnostics.daemonLog == new File("12.log")
     }
 
     def "shouts if daemon did not start"() {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunicationSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunicationSpec.groovy
index 9afa0fc..630a25b 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunicationSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/bootstrap/DaemonStartupCommunicationSpec.groovy
@@ -16,46 +16,56 @@
 
 package org.gradle.launcher.daemon.bootstrap
 
+import org.gradle.messaging.remote.internal.inet.MultiChoiceAddress
 import spock.lang.Specification
 
 class DaemonStartupCommunicationSpec extends Specification {
-
     def comm = new DaemonStartupCommunication()
+    def dummyFile = new File("C:\\foo;;\\daemon-123.log\n\r\n\u03b1")
+    def uuid = UUID.randomUUID()
+    def addresses = [InetAddress.getByName(null)]
 
     def "can simply communicate diagnostics"() {
-        given:
-        def dummyFile = new File("C:\\foo;;\\daemon-123.log")
-
         when:
-        def message = comm.daemonStartedMessage(123, dummyFile)
-        def diagnostics = comm.readDiagnostics(message)
+        def message = message(123, "1234", uuid, 123, addresses, dummyFile)
+        def startupInfo = comm.readDiagnostics(message)
 
         then:
-        diagnostics.pid == 123
-        diagnostics.daemonLog == dummyFile
+        startupInfo.uid == "1234"
+        startupInfo.address.canonicalAddress == uuid
+        startupInfo.address.port == 123
+        startupInfo.address.candidates == addresses
+        startupInfo.pid == 123
+        startupInfo.diagnostics.pid == 123
+        startupInfo.diagnostics.daemonLog == dummyFile
     }
 
     def "null pid is supported"() {
-        given:
-        def dummyFile = new File("C:\\foo;;\\daemon-123.log")
-
         when:
-        def message = comm.daemonStartedMessage(null, dummyFile)
-        def diagnostics = comm.readDiagnostics(message)
+        def message = message(null, "1234", uuid, 123, addresses, dummyFile)
+        def startupInfo = comm.readDiagnostics(message)
 
         then:
-        diagnostics.pid == null
-        diagnostics.daemonLog == dummyFile
+        startupInfo.diagnostics.pid == null
     }
 
     def "knows if a message contains a greeting"() {
         expect:
         !comm.containsGreeting("foo")
-        comm.containsGreeting(comm.daemonStartedMessage(null, new File("foo")))
+        comm.containsGreeting(message(null, "id", uuid, 123, addresses, new File("foo")))
 
         when:
         comm.containsGreeting(null)
+
         then:
         thrown(IllegalArgumentException)
     }
+
+    def message(Long pid, String daemonId, UUID addressId, int port, List<InetAddress> addresses, File logFile) {
+        def outputStream = new ByteArrayOutputStream()
+        def printStream = new PrintStream(outputStream)
+        def address = new MultiChoiceAddress(addressId, port, addresses)
+        comm.printDaemonStarted(printStream, pid, daemonId, address, logFile)
+        return new String(outputStream.toByteArray())
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonCancelForwarderTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonCancelForwarderTest.groovy
new file mode 100644
index 0000000..a0bf318
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonCancelForwarderTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.client
+
+import org.gradle.initialization.DefaultBuildCancellationToken
+import org.gradle.internal.id.IdGenerator
+import org.gradle.launcher.daemon.protocol.Cancel
+import org.gradle.messaging.dispatch.Dispatch
+import org.gradle.util.ConcurrentSpecification
+
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+class DaemonCancelForwarderTest extends ConcurrentSpecification {
+
+    def cancellationToken = new DefaultBuildCancellationToken()
+
+    def received = new LinkedBlockingQueue()
+    def dispatch = { received << it } as Dispatch
+
+    def receivedCommand() {
+        received.poll(5, TimeUnit.SECONDS)
+    }
+
+    boolean receiveCancel() {
+        receivedCommand() instanceof Cancel
+    }
+
+    def forwarder
+
+    def createForwarder() {
+        forwarder = new DaemonCancelForwarder(dispatch, cancellationToken, { 12 } as IdGenerator)
+        forwarder.start()
+    }
+
+    def setup() {
+        createForwarder()
+    }
+
+    def "cancel is forwarded when received before stop"() {
+        when:
+        cancellationToken.doCancel()
+        forwarder.stop()
+
+        then:
+        receiveCancel()
+    }
+
+    def "cancel is ignored after stop"() {
+        when:
+        forwarder.stop()
+        cancellationToken.doCancel()
+
+        then:
+        0 * dispatch._
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientConnectionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientConnectionTest.groovy
index 44f51c9..c3e9b25 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientConnectionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientConnectionTest.groovy
@@ -16,14 +16,16 @@
 
 package org.gradle.launcher.daemon.client
 
-import org.gradle.messaging.remote.internal.Connection
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails
+import org.gradle.messaging.remote.internal.MessageIOException
+import org.gradle.messaging.remote.internal.RemoteConnection
 import spock.lang.Specification
 
 class DaemonClientConnectionTest extends Specification {
-
-    final delegate = Mock(Connection)
+    final delegate = Mock(RemoteConnection)
+    final daemon = Mock(DaemonInstanceDetails)
     final staleAddressDetector = Mock(DaemonClientConnection.StaleAddressDetector)
-    final connection = new DaemonClientConnection(delegate, 'id', staleAddressDetector)
+    final connection = new DaemonClientConnection(delegate, daemon, staleAddressDetector)
 
     def "stops"() {
         when:
@@ -130,5 +132,9 @@ class DaemonClientConnectionTest extends Specification {
         0 * staleAddressDetector._
     }
 
-    class FooException extends RuntimeException {}
+    class FooException extends MessageIOException {
+        FooException() {
+            super("broken", null)
+        }
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientInputForwarderTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientInputForwarderTest.groovy
index 4866444..7cd2a18 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientInputForwarderTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientInputForwarderTest.groovy
@@ -15,6 +15,7 @@
  */
 package org.gradle.launcher.daemon.client
 
+import org.gradle.internal.id.IdGenerator
 import org.gradle.launcher.daemon.protocol.CloseInput
 import org.gradle.launcher.daemon.protocol.ForwardInput
 import org.gradle.messaging.dispatch.Dispatch
@@ -24,7 +25,6 @@ import java.util.concurrent.LinkedBlockingQueue
 import java.util.concurrent.TimeUnit
 
 import static org.gradle.util.TextUtil.toPlatformLineSeparators
-import org.gradle.internal.id.IdGenerator
 
 class DaemonClientInputForwarderTest extends ConcurrentSpecification {
 
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientServicesTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientServicesTest.groovy
index b693af5..77e08ea 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientServicesTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientServicesTest.groovy
@@ -15,19 +15,30 @@
  */
 package org.gradle.launcher.daemon.client
 
+import org.gradle.initialization.BuildLayoutParameters
+import org.gradle.internal.service.ServiceRegistryBuilder
+import org.gradle.internal.service.scopes.GlobalScopeServices
 import org.gradle.launcher.daemon.configuration.DaemonParameters
+import org.gradle.launcher.daemon.context.DaemonContext
 import org.gradle.launcher.daemon.registry.DaemonRegistry
 import org.gradle.launcher.daemon.registry.PersistentDaemonRegistry
 import org.gradle.logging.LoggingServiceRegistry
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
-import org.gradle.initialization.BuildLayoutParameters
 
+ at UsesNativeServices
 class DaemonClientServicesTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
     final DaemonParameters parameters = new DaemonParameters(new BuildLayoutParameters()).setBaseDir(tmp.testDirectory)
-    final DaemonClientServices services = new DaemonClientServices(LoggingServiceRegistry.newEmbeddableLogging(), parameters, System.in)
+    final parentServices = ServiceRegistryBuilder.builder()
+            .parent(LoggingServiceRegistry.newEmbeddableLogging())
+            .parent(NativeServicesTestFixture.instance)
+            .provider(new GlobalScopeServices(false))
+            .build()
+    final services = new DaemonClientServices(parentServices, parameters, System.in)
 
     def "makes a DaemonRegistry available"() {
         expect:
@@ -43,4 +54,10 @@ class DaemonClientServicesTest extends Specification {
         expect:
         services.get(DaemonClient) != null
     }
+
+    def "context includes locale"() {
+        expect:
+        services.get(DaemonContext).daemonOpts.contains("-Duser.language=${Locale.default.language}".toString())
+    }
+
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
index 256437c..fce0ffa 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
@@ -15,13 +15,17 @@
  */
 package org.gradle.launcher.daemon.client
 
-import org.gradle.initialization.BuildAction
+import org.gradle.api.BuildCancelledException
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.initialization.BuildCancellationToken
+import org.gradle.initialization.BuildRequestContext
 import org.gradle.internal.id.IdGenerator
 import org.gradle.launcher.daemon.context.DaemonCompatibilitySpec
+import org.gradle.launcher.daemon.protocol.*
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException
 import org.gradle.launcher.exec.BuildActionParameters
 import org.gradle.logging.internal.OutputEventListener
 import org.gradle.util.ConcurrentSpecification
-import org.gradle.launcher.daemon.protocol.*
 
 class DaemonClientTest extends ConcurrentSpecification {
     final DaemonConnector connector = Mock()
@@ -31,107 +35,113 @@ class DaemonClientTest extends ConcurrentSpecification {
     final IdGenerator<?> idGenerator = {12} as IdGenerator
     final DaemonClient client = new DaemonClient(connector, outputEventListener, compatibilitySpec, new ByteArrayInputStream(new byte[0]), executorFactory, idGenerator)
 
-    def stopsTheDaemonWhenRunning() {
+    def executesAction() {
         when:
-        client.stop()
+        def result = client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
-        _ * connection.uid >> '1'
-        2 * connector.maybeConnect(compatibilitySpec) >>> [connection, null]
-        1 * connection.dispatch({it instanceof Stop})
-        1 * connection.receive() >> new Success(null)
+        result == '[result]'
+        1 * connector.connect(compatibilitySpec) >> connection
+        _ * connection.daemon
+        1 * connection.dispatch({it instanceof Build})
+        2 * connection.receive() >>> [Stub(BuildStarted), new Success('[result]')]
+        1 * connection.dispatch({it instanceof CloseInput})
         1 * connection.dispatch({it instanceof Finished})
         1 * connection.stop()
         0 * _
     }
 
-    def stopsTheDaemonWhenNotRunning() {
-        when:
-        client.stop()
-
-        then:
-        1 * connector.maybeConnect(compatibilitySpec) >> null
-        0 * _
-    }
-
-    def "stops all compatible daemons"() {
-        DaemonClientConnection connection2 = Mock()
+    def rethrowsFailureToExecuteAction() {
+        RuntimeException failure = new RuntimeException()
 
         when:
-        client.stop()
+        client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
-        _ * connection.uid >> '1'
-        _ * connection2.uid >> '2'
-        3 * connector.maybeConnect(compatibilitySpec) >>> [connection, connection2, null]
-        1 * connection.dispatch({it instanceof Stop})
-        1 * connection.receive() >> new Success(null)
+        RuntimeException e = thrown()
+        e == failure
+        1 * connector.connect(compatibilitySpec) >> connection
+        _ * connection.daemon
+        1 * connection.dispatch({it instanceof Build})
+        2 * connection.receive() >>> [Stub(BuildStarted), new CommandFailure(failure)]
+        1 * connection.dispatch({it instanceof CloseInput})
         1 * connection.dispatch({it instanceof Finished})
         1 * connection.stop()
-        1 * connection2.dispatch({it instanceof Stop})
-        1 * connection2.receive() >> new Success(null)
-        1 * connection2.dispatch({it instanceof Finished})
-        1 * connection2.stop()
         0 * _
     }
 
-    def "stops each connection at most once"() {
-        when:
-        client.stop()
-
-        then:
-        _ * connection.uid >> '1'
-        3 * connector.maybeConnect(compatibilitySpec) >>> [connection, connection, null]
-        1 * connection.dispatch({it instanceof Stop})
-        1 * connection.receive() >> new Success(null)
-        1 * connection.dispatch({it instanceof Finished})
-        2 * connection.stop()
-        0 * _
-    }
+    def "throws an exception when build is cancelled and daemon is forcefully stopped"() {
+        def cancellationToken = Mock(BuildCancellationToken)
+        def buildRequestContext = Stub(BuildRequestContext) {
+            getCancellationToken() >> cancellationToken
+        }
 
-    def executesAction() {
         when:
-        def result = client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), buildRequestContext, Stub(BuildActionParameters))
 
         then:
-        result == '[result]'
+        BuildCancelledException gce = thrown()
         1 * connector.connect(compatibilitySpec) >> connection
+        _ * connection.daemon
+        1 * cancellationToken.addCallback(_) >> { Runnable callback ->
+            callback.run()
+            return false
+        }
+
         1 * connection.dispatch({it instanceof Build})
-        2 * connection.receive() >>> [Stub(BuildStarted), new Success('[result]')]
+        2 * connection.receive() >>> [ Stub(BuildStarted), new CommandFailure(new DaemonStoppedException())]
+        1 * connection.dispatch({it instanceof Cancel})
         1 * connection.dispatch({it instanceof CloseInput})
         1 * connection.dispatch({it instanceof Finished})
+        1 * cancellationToken.cancellationRequested >> true
+        1 * cancellationToken.removeCallback(_)
         1 * connection.stop()
         0 * _
     }
 
-    def rethrowsFailureToExecuteAction() {
-        RuntimeException failure = new RuntimeException()
+    def "throws an exception when build is cancelled and correctly finishes build"() {
+        def cancellationToken = Mock(BuildCancellationToken)
+        def cancelledException = new BuildCancelledException()
+        def buildRequestContext = Stub(BuildRequestContext) {
+            getCancellationToken() >> cancellationToken
+        }
 
         when:
-        client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), buildRequestContext, Stub(BuildActionParameters))
 
         then:
-        RuntimeException e = thrown()
-        e == failure
+        BuildCancelledException gce = thrown()
+        gce == cancelledException
         1 * connector.connect(compatibilitySpec) >> connection
+        _ * connection.daemon
+        1 * cancellationToken.addCallback(_) >> { Runnable callback ->
+            // simulate cancel request processing
+            callback.run()
+            return false
+        }
+        1 * cancellationToken.removeCallback(_)
+
         1 * connection.dispatch({it instanceof Build})
-        2 * connection.receive() >>> [Stub(BuildStarted), new CommandFailure(failure)]
+        2 * connection.receive() >>> [ Stub(BuildStarted), new CommandFailure(cancelledException)]
+        1 * connection.dispatch({it instanceof Cancel})
         1 * connection.dispatch({it instanceof CloseInput})
         1 * connection.dispatch({it instanceof Finished})
         1 * connection.stop()
         0 * _
     }
-    
+
     def "tries to find a different daemon if connected to a stale daemon address"() {
         DaemonClientConnection connection2 = Mock()
 
         when:
-        client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
         2 * connector.connect(compatibilitySpec) >>> [connection, connection2]
+        _ * connection.daemon
         1 * connection.dispatch({it instanceof Build}) >> { throw new StaleDaemonAddressException("broken", new RuntimeException())}
         1 * connection.stop()
+        _ * connection2.daemon
         2 * connection2.receive() >>> [Stub(BuildStarted), new Success('')]
         0 * connection._
     }
@@ -140,14 +150,16 @@ class DaemonClientTest extends ConcurrentSpecification {
         DaemonClientConnection connection2 = Mock()
 
         when:
-        client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
         2 * connector.connect(compatibilitySpec) >>> [connection, connection2]
+        _ * connection.daemon
         1 * connection.dispatch({it instanceof Build})
         1 * connection.receive() >> Stub(DaemonUnavailable)
         1 * connection.dispatch({it instanceof Finished})
         1 * connection.stop()
+        _ * connection2.daemon
         2 * connection2.receive() >>> [Stub(BuildStarted), new Success('')]
         0 * connection._
     }
@@ -156,13 +168,15 @@ class DaemonClientTest extends ConcurrentSpecification {
         DaemonClientConnection connection2 = Mock()
 
         when:
-        client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
         2 * connector.connect(compatibilitySpec) >>> [connection, connection2]
+        _ * connection.daemon
         1 * connection.dispatch({it instanceof Build})
         1 * connection.receive() >> null
         1 * connection.stop()
+        _ * connection2.daemon
         2 * connection2.receive() >>> [Stub(BuildStarted), new Success('')]
         0 * connection._
     }
@@ -173,7 +187,7 @@ class DaemonClientTest extends ConcurrentSpecification {
         connection.receive() >> Mock(DaemonUnavailable)
 
         when:
-        client.execute(Stub(BuildAction), Stub(BuildActionParameters))
+        client.execute(Stub(BuildAction), Stub(BuildRequestContext), Stub(BuildActionParameters))
 
         then:
         thrown(NoUsableDaemonFoundException)
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonStopClientTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonStopClientTest.groovy
new file mode 100644
index 0000000..f642e15
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonStopClientTest.groovy
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher.daemon.client
+
+import org.gradle.internal.id.IdGenerator
+import org.gradle.launcher.daemon.context.DaemonInstanceDetails
+import org.gradle.launcher.daemon.protocol.Finished
+import org.gradle.launcher.daemon.protocol.Stop
+import org.gradle.launcher.daemon.protocol.StopWhenIdle
+import org.gradle.launcher.daemon.protocol.Success
+import org.gradle.util.ConcurrentSpecification
+
+class DaemonStopClientTest extends ConcurrentSpecification {
+    final DaemonConnector connector = Mock()
+    final DaemonClientConnection connection = Mock()
+    final IdGenerator<?> idGenerator = {12} as IdGenerator
+    final def client = new DaemonStopClient(connector, idGenerator)
+
+    def "requests daemons stop gracefully"() {
+        def daemon1 = Stub(DaemonInstanceDetails)
+        def daemon2 = Stub(DaemonInstanceDetails)
+
+        when:
+        client.gracefulStop([daemon1, daemon2])
+
+        then:
+        1 * connector.maybeConnect(daemon1) >>> connection
+        1 * connection.dispatch({it instanceof StopWhenIdle})
+        1 * connection.receive() >> new Success(null)
+        1 * connection.dispatch({it instanceof Finished})
+        1 * connection.stop()
+
+        and:
+        1 * connector.maybeConnect(daemon2) >>> connection
+        1 * connection.dispatch({it instanceof StopWhenIdle})
+        1 * connection.receive() >> new Success(null)
+        1 * connection.dispatch({it instanceof Finished})
+        1 * connection.stop()
+        0 * _
+    }
+
+    def stopsTheDaemonWhenRunning() {
+        when:
+        client.stop()
+
+        then:
+        _ * connection.daemon >> daemon('1')
+        2 * connector.maybeConnect(_) >>> [connection, null]
+        1 * connection.dispatch({it instanceof Stop})
+        1 * connection.receive() >> new Success(null)
+        1 * connection.dispatch({it instanceof Finished})
+        1 * connection.stop()
+        0 * _
+    }
+
+    def stopsTheDaemonWhenNotRunning() {
+        when:
+        client.stop()
+
+        then:
+        1 * connector.maybeConnect(_) >> null
+        0 * _
+    }
+
+    def "stops all compatible daemons"() {
+        DaemonClientConnection connection2 = Mock()
+
+        when:
+        client.stop()
+
+        then:
+        _ * connection.daemon >> daemon('1')
+        _ * connection2.daemon >> daemon('2')
+        3 * connector.maybeConnect(_) >>> [connection, connection2, null]
+        1 * connection.dispatch({it instanceof Stop})
+        1 * connection.receive() >> new Success(null)
+        1 * connection.dispatch({it instanceof Finished})
+        1 * connection.stop()
+        1 * connection2.dispatch({it instanceof Stop})
+        1 * connection2.receive() >> new Success(null)
+        1 * connection2.dispatch({it instanceof Finished})
+        1 * connection2.stop()
+        0 * _
+    }
+
+    def "stops each connection at most once"() {
+        when:
+        client.stop()
+
+        then:
+        _ * connection.daemon >> daemon('1')
+        3 * connector.maybeConnect(_) >>> [connection, connection, null]
+        1 * connection.dispatch({it instanceof Stop})
+        1 * connection.receive() >> new Success(null)
+        1 * connection.dispatch({it instanceof Finished})
+        2 * connection.stop()
+        0 * _
+    }
+
+    private DaemonInstanceDetails daemon(String id) {
+        Stub(DaemonInstanceDetails) {
+            getUid() >> id
+        }
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DefaultDaemonConnectorTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DefaultDaemonConnectorTest.groovy
index f2c8522..3d04317 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DefaultDaemonConnectorTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DefaultDaemonConnectorTest.groovy
@@ -24,8 +24,8 @@ import org.gradle.launcher.daemon.registry.EmbeddedDaemonRegistry
 import org.gradle.messaging.remote.Address
 import org.gradle.messaging.remote.internal.ConnectCompletion
 import org.gradle.messaging.remote.internal.ConnectException
-import org.gradle.messaging.remote.internal.Connection
 import org.gradle.messaging.remote.internal.OutgoingConnector
+import org.gradle.messaging.remote.internal.RemoteConnection
 import spock.lang.Specification
 
 class DefaultDaemonConnectorTest extends Specification {
@@ -36,7 +36,7 @@ class DefaultDaemonConnectorTest extends Specification {
 
     class OutgoingConnectorStub implements OutgoingConnector {
         ConnectCompletion connect(Address address) throws ConnectException {
-            def connection = [:] as Connection
+            def connection = [:] as RemoteConnection
             // unsure why I can't add this as property in the map-mock above
             connection.metaClass.num = address.num
             return { connection } as ConnectCompletion
@@ -67,7 +67,7 @@ class DefaultDaemonConnectorTest extends Specification {
         def address = createAddress(daemonNum)
         registry.store(address, context, "password", false)
         registry.markBusy(address)
-        return new DaemonStartupInfo(daemonNum.toString(), null);
+        return new DaemonStartupInfo(daemonNum.toString(), null, null);
     }
 
     def startIdleDaemon() {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/InputForwarderTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/InputForwarderTest.groovy
index 22ab607..9370743 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/InputForwarderTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/InputForwarderTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.launcher.daemon.client
 
 import org.gradle.api.Nullable
+import org.gradle.initialization.DefaultBuildCancellationToken
 import org.gradle.internal.concurrent.DefaultExecutorFactory
 import org.gradle.internal.io.TextStream
 import spock.lang.Specification
@@ -29,6 +30,7 @@ import static org.gradle.util.TextUtil.toPlatformLineSeparators
 class InputForwarderTest extends Specification {
 
     def bufferSize = 1024
+    def cancellationToken = new DefaultBuildCancellationToken()
     def executerFactory = new DefaultExecutorFactory()
 
     def source = new PipedOutputStream()
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/JvmVersionValidatorTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/JvmVersionValidatorTest.groovy
new file mode 100644
index 0000000..c7a11d7
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/JvmVersionValidatorTest.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.launcher.daemon.client
+
+import org.gradle.api.JavaVersion
+import spock.lang.Specification
+
+class JvmVersionValidatorTest extends Specification {
+
+    def "can parse version number"() {
+        expect:
+        JvmVersionValidator.parseJavaVersionCommandOutput(new BufferedReader(new StringReader(output))) == JavaVersion.toVersion("1.5")
+
+        where:
+        output << [
+                'java version "1.5"',
+                'java version "1.5"\ntrailers',
+                'headers\njava version "1.5"',
+                'java version "1.5"\r\ntrailers',
+                'headers\r\njava version "1.5"',
+        ]
+    }
+
+    def "can parse version number for Java 8 "() {
+        expect:
+        JvmVersionValidator.parseJavaVersionCommandOutput(new BufferedReader(new StringReader(output))) == JavaVersion.toVersion("1.8")
+
+        where:
+        output << [
+                'java version "1.8.0_11"',
+                'openjdk version "1.8.0-internal"',
+        ]
+    }
+
+    def "fails to parse version number"() {
+        when:
+        JvmVersionValidator.parseJavaVersionCommandOutput(new BufferedReader(new StringReader(output)))
+
+        then:
+        thrown RuntimeException
+
+        where:
+        output << [
+                '',
+                '\n',
+                'foo',
+                'foo\nbar',
+                'foo\r\nbar'
+        ]
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/StopDispatcherTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/StopDispatcherTest.groovy
index 180e0cd..f4686d1 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/StopDispatcherTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/StopDispatcherTest.groovy
@@ -16,21 +16,22 @@
 
 package org.gradle.launcher.daemon.client
 
-import org.gradle.internal.id.IdGenerator
+import org.gradle.launcher.daemon.protocol.Stop
 import org.gradle.messaging.remote.internal.Connection
 import spock.lang.Specification
 
 public class StopDispatcherTest extends Specification {
 
-    def dispatcher = new StopDispatcher({12} as IdGenerator)
+    def dispatcher = new StopDispatcher()
     def connection = Mock(Connection)
 
     def "ignores failed dispatch and does not receive"() {
         given:
-        connection.dispatch(_) >> { throw new RuntimeException("Cannot dispatch") }
+        def message = new Stop("12")
+        connection.dispatch(message) >> { throw new RuntimeException("Cannot dispatch") }
 
         when:
-        dispatcher.dispatch(connection)
+        dispatcher.dispatch(connection, message)
 
         then:
         0 * connection.receive()
@@ -39,10 +40,11 @@ public class StopDispatcherTest extends Specification {
 
     def "ignores failed receive"() {
         given:
+        def message = new Stop("12")
         connection.receive() >> { throw new RuntimeException("Cannot dispatch") }
 
         when:
-        dispatcher.dispatch(connection)
+        dispatcher.dispatch(connection, message)
 
         then:
         1 * connection.dispatch(_)
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/CurrentProcessTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/CurrentProcessTest.groovy
index 8f8f529..efef3c2 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/CurrentProcessTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/CurrentProcessTest.groovy
@@ -17,20 +17,26 @@
 package org.gradle.launcher.daemon.configuration
 
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.initialization.BuildLayoutParameters
 import org.gradle.process.internal.JvmOptions
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.SetSystemProperties
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
-import org.gradle.initialization.BuildLayoutParameters
 
+import java.nio.charset.Charset
+
+ at UsesNativeServices
 public class CurrentProcessTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    @Rule final SetSystemProperties systemPropertiesSet = new SetSystemProperties()
+    @Rule
+    final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule
+    final SetSystemProperties systemPropertiesSet = new SetSystemProperties()
+
     private FileResolver fileResolver = Mock()
     private def currentJavaHome = tmpDir.file('java_home')
     private JvmOptions currentJvmOptions = new JvmOptions(fileResolver)
-    private DaemonParameters parameters = new DaemonParameters(new BuildLayoutParameters())
 
     def "can only run build with identical java home"() {
         when:
@@ -46,6 +52,7 @@ public class CurrentProcessTest extends Specification {
         currentJvmOptions.setAllJvmArgs(["-Xmx100m", "-XX:SomethingElse", "-Dfoo=bar", "-Dbaz"])
         CurrentProcess currentProcess = new CurrentProcess(currentJavaHome, currentJvmOptions)
 
+
         then:
         currentProcess.configureForBuild(buildParameters([]))
         currentProcess.configureForBuild(buildParameters(['-Dfoo=bar']))
@@ -54,26 +61,45 @@ public class CurrentProcessTest extends Specification {
         !currentProcess.configureForBuild(buildParameters(["-Xms10m"]))
         !currentProcess.configureForBuild(buildParameters(["-XX:SomethingElse"]))
         !currentProcess.configureForBuild(buildParameters(["-Xmx100m", "-XX:SomethingElse", "-Dfoo=bar", "-Dbaz"]))
-        !currentProcess.configureForBuild(buildParameters(['-Dfile.encoding=UTF8']))
+        def notDefaultEncoding = ["UTF-8", "US-ASCII"].collect { Charset.forName(it) } find { it != Charset.defaultCharset() }
+        !currentProcess.configureForBuild(buildParameters(["-Dfile.encoding=$notDefaultEncoding"]))
+        def notDefaultLanguage = ["es", "jp"].find { it != Locale.default.language }
+        !currentProcess.configureForBuild(buildParameters(["-Duser.language=$notDefaultLanguage"]))
+        currentProcess.configureForBuild(buildParameters(["-Dfile.encoding=${Charset.defaultCharset().name()}"]))
+        currentProcess.configureForBuild(buildParameters(["-Duser.language=${Locale.default.language}"]))
     }
 
     def "sets all mutable system properties before running build"() {
         when:
         CurrentProcess currentProcess = new CurrentProcess(tmpDir.file('java_home'), currentJvmOptions)
+        def parameters = buildParameters(["-Dfoo=bar", "-Dbaz"])
 
         then:
-        currentProcess.configureForBuild(buildParameters(["-Dfoo=bar", "-Dbaz"]))
+        currentProcess.configureForBuild(parameters)
 
         and:
         System.getProperty('foo') == 'bar'
         System.getProperty('baz') != null
     }
 
+    def "when required opts contain an immutable default setting ignore it"() {
+        //if the user does not configure any jvm args Gradle uses some defaults
+        //however, we don't want those defaults to influence the decision whether to use existing process or not
+        //e.g. those defaults should only be used for launching a new process
+        //TODO SF this is a bit messy, let's try to clean this up
+        when:
+        CurrentProcess currentProcess = new CurrentProcess(currentJavaHome, currentJvmOptions)
+
+        then:
+        currentProcess.configureForBuild(buildParameters(["-Xmx1024m"]))
+    }
+
     private DaemonParameters buildParameters(Iterable<String> jvmArgs) {
         return buildParameters(currentJavaHome, jvmArgs)
     }
 
-    private DaemonParameters buildParameters(File javaHome, Iterable<String> jvmArgs = []) {
+    private static DaemonParameters buildParameters(File javaHome, Iterable<String> jvmArgs = []) {
+        def parameters = new DaemonParameters(new BuildLayoutParameters())
         parameters.setJavaHome(javaHome)
         parameters.setJvmArgs(jvmArgs)
         return parameters
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/DaemonParametersTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/DaemonParametersTest.groovy
index be8f20b..1958913 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/DaemonParametersTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/DaemonParametersTest.groovy
@@ -15,35 +15,29 @@
  */
 package org.gradle.launcher.daemon.configuration
 
-import org.gradle.StartParameter
 import org.gradle.initialization.BuildLayoutParameters
+import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
 import static java.lang.Boolean.parseBoolean
 
+ at UsesNativeServices
 class DaemonParametersTest extends Specification {
-    final DaemonParameters parameters = new DaemonParameters(new BuildLayoutParameters())
+    final DaemonParameters parameters = parameters()
 
-    def "has reasonable default values"() {
-        expect:
-        assertDefaultValues()
+    private DaemonParameters parameters() {
+        new DaemonParameters(new BuildLayoutParameters())
     }
 
-    def "uses default values when no specific gradle properties provided"() {
+    def "has reasonable default values"() {
         expect:
-        assertDefaultValues()
-    }
-
-    void assertDefaultValues() {
-        assert !parameters.enabled
-        assert parameters.idleTimeout == DaemonParameters.DEFAULT_IDLE_TIMEOUT
-        def baseDir = new File(StartParameter.DEFAULT_GRADLE_USER_HOME, "daemon")
-        assert parameters.baseDir == baseDir
-        assert parameters.systemProperties.isEmpty()
-        assert parameters.effectiveJvmArgs.containsAll(parameters.defaultJvmArgs)
-        assert parameters.effectiveJvmArgs.size() == parameters.defaultJvmArgs.size() + 1 // + 1 because effective JVM args contains -Dfile.encoding
-        assert parameters.idleTimeout == DaemonParameters.DEFAULT_IDLE_TIMEOUT
-        assert parameters.usingDefaultJvmArgs
+        parameters.daemonUsage == DaemonUsage.IMPLICITLY_DISABLED
+        parameters.idleTimeout == DaemonParameters.DEFAULT_IDLE_TIMEOUT
+        parameters.baseDir == new File(new BuildLayoutParameters().getGradleUserHomeDir(), "daemon")
+        parameters.systemProperties.isEmpty()
+        parameters.effectiveJvmArgs.containsAll(parameters.DEFAULT_JVM_ARGS)
+        parameters.effectiveJvmArgs.size() == parameters.DEFAULT_JVM_ARGS.size() + 1 + 3 // + 1 because effective JVM args contains -Dfile.encoding, +3 for locale props
+        parameters.idleTimeout == DaemonParameters.DEFAULT_IDLE_TIMEOUT
     }
 
     def "configuring jvmargs replaces the defaults"() {
@@ -51,18 +45,7 @@ class DaemonParametersTest extends Specification {
         parameters.setJvmArgs(["-Xmx17m"])
 
         then:
-        parameters.effectiveJvmArgs.each { assert !parameters.defaultJvmArgs.contains(it) }
-    }
-
-    def "knows if uses default jvm args"() {
-        given:
-        assert parameters.usingDefaultJvmArgs
-
-        when:
-        parameters.setJvmArgs(["-Dfoo= -Dbar"])
-
-        then:
-        !parameters.usingDefaultJvmArgs
+        parameters.effectiveJvmArgs.each { assert !parameters.DEFAULT_JVM_ARGS.contains(it) }
     }
 
     def "can configure debug mode"() {
@@ -76,4 +59,20 @@ class DaemonParametersTest extends Specification {
         where:
         flag << ["true", "false"]
     }
+
+    def "can enable the daemon"() {
+        when:
+        def parametersWithEnabledDaemon = parameters().setEnabled(true)
+
+        then:
+        parametersWithEnabledDaemon.daemonUsage == DaemonUsage.EXPLICITLY_ENABLED
+    }
+
+    def "can explicitly disable the daemon"() {
+        when:
+        def parametersWithDisabledDaemon = parameters().setEnabled(false)
+
+        then:
+        parametersWithDisabledDaemon.daemonUsage == DaemonUsage.EXPLICITLY_DISABLED
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
index f480549..5b6dda3 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.launcher.daemon.context
 
-import org.gradle.internal.nativeplatform.ProcessEnvironment
+import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.ConfigureUtil
 import org.gradle.util.Requires
@@ -30,11 +30,11 @@ class DaemonCompatibilitySpecSpec extends Specification {
     def clientConfigure = {}
     def serverConfigure = {}
 
-    def client(Closure c) {
+    def client(@DelegatesTo(DaemonContextBuilder) Closure c) {
         clientConfigure = c
     }
 
-    def server(Closure c) {
+    def server(@DelegatesTo(DaemonContextBuilder) Closure c) {
         serverConfigure = c
     }
 
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
index 2ed71c7..fdc83a7 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
@@ -16,7 +16,6 @@
 package org.gradle.launcher.daemon.registry
 
 import org.gradle.cache.internal.DefaultFileLockManager
-import org.gradle.cache.internal.FileLock
 import org.gradle.cache.internal.FileLockManager
 import org.gradle.cache.internal.ProcessMetaDataProvider
 import org.gradle.cache.internal.locklistener.FileLockContentionHandler
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/PersistentDaemonRegistryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/PersistentDaemonRegistryTest.groovy
index 49d0d25..abd3752 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/PersistentDaemonRegistryTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/PersistentDaemonRegistryTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.launcher.daemon.registry
 
-import org.gradle.internal.nativeplatform.ProcessEnvironment
+import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.launcher.daemon.context.DaemonContext
 import org.gradle.launcher.daemon.context.DaemonContextBuilder
 import org.gradle.messaging.remote.Address
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServerExceptionHandlingTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServerExceptionHandlingTest.groovy
index 078f6b2..a03560c 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServerExceptionHandlingTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServerExceptionHandlingTest.groovy
@@ -16,32 +16,42 @@
 
 package org.gradle.launcher.daemon.server
 
+import org.gradle.StartParameter
 import org.gradle.api.logging.LogLevel
 import org.gradle.configuration.GradleLauncherMetaData
-import org.gradle.initialization.BuildAction
-import org.gradle.initialization.BuildController
-import org.gradle.initialization.GradleLauncherFactory
-import org.gradle.internal.nativeplatform.ProcessEnvironment
+import org.gradle.initialization.BuildRequestContext
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.internal.invocation.BuildController
+import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.launcher.daemon.client.DaemonClient
 import org.gradle.launcher.daemon.client.EmbeddedDaemonClientServices
+import org.gradle.launcher.daemon.client.StubDaemonHealthServices
 import org.gradle.launcher.daemon.context.DaemonContext
-import org.gradle.launcher.daemon.server.exec.DaemonCommandAction
+import org.gradle.launcher.daemon.server.api.DaemonCommandAction
 import org.gradle.launcher.daemon.server.exec.DaemonCommandExecuter
 import org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter
 import org.gradle.launcher.daemon.server.exec.ForwardClientInput
-import org.gradle.launcher.daemon.server.exec.NoOpDaemonCommandAction
 import org.gradle.launcher.exec.DefaultBuildActionParameters
+import org.gradle.launcher.exec.InProcessBuildActionExecuter
 import org.gradle.logging.LoggingManagerInternal
+import org.gradle.messaging.remote.internal.MessageIOException
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
-class DaemonServerExceptionHandlingTest extends Specification {
+import static org.gradle.launcher.daemon.configuration.DaemonUsage.IMPLICITLY_DISABLED
 
+ at UsesNativeServices
+class DaemonServerExceptionHandlingTest extends Specification {
     @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-    def parameters = new DefaultBuildActionParameters(new GradleLauncherMetaData(), 0, new HashMap(System.properties), [:], temp.testDirectory, LogLevel.ERROR)
+    def buildRequestContext = Stub(BuildRequestContext) {
+        getClient() >> new GradleLauncherMetaData()
+    }
+    def parameters = new DefaultBuildActionParameters(new HashMap(System.properties), [:], temp.testDirectory, LogLevel.ERROR, IMPLICITLY_DISABLED)
 
     static class DummyLauncherAction implements BuildAction, Serializable {
+        StartParameter startParameter
         Object someState
         Object run(BuildController buildController) { null }
     }
@@ -58,20 +68,21 @@ class DaemonServerExceptionHandlingTest extends Specification {
         def action = new DummyLauncherAction(someState: unloadableClass)
 
         when:
-        client.execute(action, parameters)
+        client.execute(action, buildRequestContext, parameters)
 
         then:
-        def ex = thrown(Exception)
-        ex.message.contains("Unable to receive command from connection")
+        def ex = thrown(MessageIOException)
+        ex.message.contains("Could not read message from")
+        ex.cause instanceof ClassNotFoundException
     }
 
     EmbeddedDaemonClientServices servicesWith(Closure configureDeamonActions) {
         //we need to override some methods to inject a failure action into the sequence
         def services = new EmbeddedDaemonClientServices() {
             DaemonCommandExecuter createDaemonCommandExecuter() {
-                return new DefaultDaemonCommandExecuter(get(GradleLauncherFactory),
+                return new DefaultDaemonCommandExecuter(get(InProcessBuildActionExecuter),
                         get(ProcessEnvironment), getFactory(LoggingManagerInternal.class).create(),
-                        new File("dummy"), new NoOpDaemonCommandAction()) {
+                        new File("dummy"), new StubDaemonHealthServices()) {
                     List<DaemonCommandAction> createActions(DaemonContext daemonContext) {
                         def actions = new LinkedList(super.createActions(daemonContext));
                         configureDeamonActions(actions);
@@ -93,7 +104,7 @@ class DaemonServerExceptionHandlingTest extends Specification {
         }
 
         when:
-        services.get(DaemonClient).execute(new DummyLauncherAction(), parameters)
+        services.get(DaemonClient).execute(new DummyLauncherAction(), buildRequestContext, parameters)
 
         then:
         def ex = thrown(Throwable)
@@ -109,10 +120,10 @@ class DaemonServerExceptionHandlingTest extends Specification {
         }
 
         when:
-        services.get(DaemonClient).execute(new DummyLauncherAction(), parameters)
+        services.get(DaemonClient).execute(new DummyLauncherAction(), buildRequestContext, parameters)
 
         then:
-        def ex = thrown(RuntimeException)
-        ex.cause.message.contains 'Buy more ram'
+        def ex = thrown(OutOfMemoryError)
+        ex.message.contains 'Buy more ram'
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServicesTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServicesTest.groovy
index 2ba0ce1..f7b7e05 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServicesTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonServicesTest.groovy
@@ -15,17 +15,19 @@
  */
 package org.gradle.launcher.daemon.server
 
-import org.gradle.internal.nativeplatform.ProcessEnvironment
+import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.launcher.daemon.configuration.DefaultDaemonServerConfiguration
 import org.gradle.launcher.daemon.registry.DaemonDir
 import org.gradle.logging.LoggingManagerInternal
 import org.gradle.logging.LoggingServiceRegistry
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
 
 import static java.util.Arrays.asList
 
+ at UsesNativeServices
 class DaemonServicesTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
     final DaemonServices services = new DaemonServices(new DefaultDaemonServerConfiguration("uid", tmp.testDirectory, 100, asList()),
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonStateCoordinatorTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonStateCoordinatorTest.groovy
index bf0b2db..2ec2800 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonStateCoordinatorTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DaemonStateCoordinatorTest.groovy
@@ -15,15 +15,16 @@
  */
 package org.gradle.launcher.daemon.server
 
-import org.gradle.launcher.daemon.server.exec.DaemonUnavailableException
-import spock.lang.Specification
+import org.gradle.launcher.daemon.server.api.DaemonStoppedException
+import org.gradle.launcher.daemon.server.api.DaemonUnavailableException
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 
 import java.util.concurrent.TimeUnit
-class DaemonStateCoordinatorTest extends Specification {
+
+class DaemonStateCoordinatorTest extends ConcurrentSpec {
     final Runnable onStartCommand = Mock(Runnable)
     final Runnable onFinishCommand = Mock(Runnable)
-    final Runnable onDisconnect = Mock(Runnable)
-    final coordinator = new DaemonStateCoordinator(onStartCommand, onFinishCommand)
+    final coordinator = new DaemonStateCoordinator(executorFactory, onStartCommand, onFinishCommand, 2000)
 
     def "can stop multiple times"() {
         expect:
@@ -43,7 +44,7 @@ class DaemonStateCoordinatorTest extends Specification {
         0 * _._
     }
 
-    def "await idle timeout throws exception when already stopped"() {
+    def "await idle timeout does nothing when already stopped"() {
         given:
         coordinator.stop()
 
@@ -51,15 +52,20 @@ class DaemonStateCoordinatorTest extends Specification {
         coordinator.stopOnIdleTimeout(10000, TimeUnit.SECONDS)
 
         then:
-        DaemonStoppedException e = thrown()
+        coordinator.stopped
     }
 
     def "await idle timeout waits for specified time and then stops"() {
         when:
-        coordinator.stopOnIdleTimeout(100, TimeUnit.MILLISECONDS)
+        operation.waitForIdle {
+            coordinator.stopOnIdleTimeout(100, TimeUnit.MILLISECONDS)
+        }
 
         then:
         coordinator.stopped
+        operation.waitForIdle.duration in approx(100)
+
+        and:
         0 * _._
     }
 
@@ -67,7 +73,7 @@ class DaemonStateCoordinatorTest extends Specification {
         Runnable command = Mock()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         1 * onStartCommand.run()
@@ -76,12 +82,35 @@ class DaemonStateCoordinatorTest extends Specification {
         0 * _._
     }
 
+    def "runs actions when more commands are run"() {
+        Runnable command = Mock()
+        Runnable command2 = Mock()
+
+        when:
+        coordinator.runCommand(command, "command")
+
+        then:
+        1 * onStartCommand.run()
+        1 * command.run()
+        1 * onFinishCommand.run()
+        0 * _._
+
+        when:
+        coordinator.runCommand(command2, "command")
+
+        then:
+        1 * onStartCommand.run()
+        1 * command2.run()
+        1 * onFinishCommand.run()
+        0 * _._
+    }
+
     def "runs actions when command fails"() {
         Runnable command = Mock()
         def failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -96,10 +125,10 @@ class DaemonStateCoordinatorTest extends Specification {
         Runnable command = Mock()
 
         given:
-        command.run() >> { coordinator.runCommand(Mock(Runnable), "other", Mock(Runnable)) }
+        command.run() >> { coordinator.runCommand(Mock(Runnable), "other") }
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException e = thrown()
@@ -113,7 +142,7 @@ class DaemonStateCoordinatorTest extends Specification {
         coordinator.requestStop()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException e = thrown()
@@ -127,7 +156,7 @@ class DaemonStateCoordinatorTest extends Specification {
         coordinator.requestForcefulStop()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException e = thrown()
@@ -141,7 +170,7 @@ class DaemonStateCoordinatorTest extends Specification {
         coordinator.requestStop()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException e = thrown()
@@ -153,7 +182,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -163,7 +192,7 @@ class DaemonStateCoordinatorTest extends Specification {
         0 * _._
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException unavailableException = thrown()
@@ -175,7 +204,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -188,7 +217,7 @@ class DaemonStateCoordinatorTest extends Specification {
         0 * _._
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         DaemonUnavailableException unavailableException = thrown()
@@ -200,7 +229,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -221,7 +250,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -241,7 +270,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -263,7 +292,7 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        coordinator.runCommand(command, "command")
 
         then:
         RuntimeException e = thrown()
@@ -290,21 +319,21 @@ class DaemonStateCoordinatorTest extends Specification {
 
         then:
         coordinator.stopped
-        coordinator.stoppingOrStopped
+        coordinator.willRefuseNewCommands
     }
 
-    def "requestStop stops once current command has completed"() {
+    def "requestStop stops after current command has completed"() {
         Runnable command = Mock()
 
         when:
-        coordinator.runCommand(command, "some command", onDisconnect)
+        coordinator.runCommand(command, "some command")
 
         then:
         1 * command.run() >> {
             assert coordinator.busy
             coordinator.requestStop()
             assert !coordinator.stopped
-            assert coordinator.stoppingOrStopped
+            assert coordinator.willRefuseNewCommands
         }
 
         and:
@@ -320,14 +349,14 @@ class DaemonStateCoordinatorTest extends Specification {
         RuntimeException failure = new RuntimeException()
 
         when:
-        coordinator.runCommand(command, "some command", onDisconnect)
+        coordinator.runCommand(command, "some command")
 
         then:
         1 * command.run() >> {
             assert coordinator.busy
             coordinator.requestStop()
             assert !coordinator.stopped
-            assert coordinator.stoppingOrStopped
+            assert coordinator.willRefuseNewCommands
             throw failure
         }
 
@@ -344,19 +373,29 @@ class DaemonStateCoordinatorTest extends Specification {
     }
 
     def "await idle time returns after command has finished and stop requested"() {
-        Runnable command = Mock()
+        def command = Mock(Runnable)
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
-        coordinator.stopOnIdleTimeout(10000, TimeUnit.SECONDS)
+        start {
+            coordinator.runCommand(command, "command")
+        }
+        async {
+            thread.blockUntil.actionStarted
+            coordinator.requestStop()
+            coordinator.stopOnIdleTimeout(10000, TimeUnit.SECONDS)
+            instant.idle
+        }
 
         then:
-        DaemonStoppedException e = thrown()
+        coordinator.stopped
+        instant.idle > instant.actionFinished
 
         and:
         1 * onStartCommand.run()
         1 * command.run() >> {
-            coordinator.requestStop()
+            instant.actionStarted
+            thread.block()
+            instant.actionFinished
         }
         0 * _._
     }
@@ -369,75 +408,189 @@ class DaemonStateCoordinatorTest extends Specification {
         coordinator.requestForcefulStop()
 
         then:
-        coordinator.stoppingOrStopped
+        coordinator.willRefuseNewCommands
         coordinator.stopped
         0 * _._
     }
 
-    def "requestForcefulStop notifies disconnect handler and stops immediately when command running"() {
-        Runnable command = Mock()
+    def "requestForcefulStop causes command to be abandoned immediately"() {
+        def command = Mock(Runnable)
 
         expect:
         !coordinator.stopped
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        operation.run {
+            coordinator.runCommand(command, "command")
+        }
 
         then:
-        coordinator.stoppingOrStopped
+        DaemonStoppedException e = thrown()
+        e.message == "Gradle build daemon has been stopped."
+
+        and:
+        coordinator.willRefuseNewCommands
         coordinator.stopped
+
+        and:
         1 * onStartCommand.run()
         1 * command.run() >> {
             assert !coordinator.stopped
             coordinator.requestForcefulStop()
             assert coordinator.stopped
+            thread.blockUntil.run
         }
-        1 * onDisconnect.run()
         0 * _._
     }
 
-    def "requestForcefulStop stops after disconnect action fails"() {
-        Runnable command = Mock()
-        RuntimeException failure = new RuntimeException()
+    def "await idle time returns immediately when forceful stop requested and command running"() {
+        def command = Mock(Runnable)
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
+        start {
+            coordinator.runCommand(command, "command")
+        }
+        async {
+            thread.blockUntil.startAction
+            coordinator.requestForcefulStop()
+            coordinator.stopOnIdleTimeout(10000, TimeUnit.SECONDS)
+            instant.idle
+        }
 
         then:
-        RuntimeException e = thrown()
-        e == failure
+        thrown(DaemonStoppedException)
+        coordinator.stopped
+        instant.idle < instant.finishAction
 
         and:
-        coordinator.stopped
+        1 * onStartCommand.run()
+        1 * command.run() >> {
+            instant.startAction
+            thread.block()
+            instant.finishAction
+        }
+
+        0 * _._
+    }
+
+    def "cancelBuild when running command completes in short time"() {
+        def command = Mock(Runnable)
+
+        expect:
+        !coordinator.stopped
+
+        when:
+        coordinator.runCommand(command, "command")
+        start {
+            thread.blockUntil.running
+            coordinator.cancelBuild()
+        }
+
+        then:
+        !coordinator.willRefuseNewCommands
+        !coordinator.stopped
+        coordinator.idle
 
         and:
         1 * onStartCommand.run()
         1 * command.run() >> {
-            coordinator.requestForcefulStop()
+            instant.running
+            thread.block()
         }
-        1 * onDisconnect.run() >> {
-            throw failure
+        1 * onFinishCommand.run()
+        0 * _._
+    }
+
+    def "cancelBuild stops daemon when cancel callback fails and command completes in short time"() {
+        def command = Mock(Runnable)
+
+        expect:
+        !coordinator.stopped
+
+        when:
+        coordinator.runCommand(command, "command")
+        start {
+            thread.blockUntil.running
+            coordinator.cancelBuild()
+        }
+
+        then:
+        !coordinator.willRefuseNewCommands
+        !coordinator.stopped
+        coordinator.idle
+
+        and:
+        1 * onStartCommand.run()
+        1 * command.run() >> {
+            assert !coordinator.stopped
+            coordinator.cancellationToken.addCallback { throw new RuntimeException('failing cancel callback') }
+            instant.running
+            thread.block()
         }
+        1 * onFinishCommand.run()
         0 * _._
     }
 
-    def "await idle time returns immediately when forceful stop requested and command running"() {
-        Runnable command = Mock()
+    def "cancelBuild stops daemon when running command does not complete in short time"() {
+        def command = Mock(Runnable)
+
+        expect:
+        !coordinator.stopped
 
         when:
-        coordinator.runCommand(command, "command", onDisconnect)
-        coordinator.stopOnIdleTimeout(10000, TimeUnit.SECONDS)
+        operation.run {
+            coordinator.runCommand(command, "command")
+        }
 
         then:
         DaemonStoppedException e = thrown()
 
         and:
+        coordinator.willRefuseNewCommands
+        coordinator.stopped
+
+        and:
         1 * onStartCommand.run()
         1 * command.run() >> {
-            coordinator.requestForcefulStop()
+            coordinator.cancelBuild()
+            thread.blockUntil.run
         }
-        1 * onDisconnect.run()
         0 * _._
     }
 
+    def "canceled build does not affect next build"() {
+        def command1 = Mock(Runnable)
+        def command2 = Mock(Runnable)
+
+        expect:
+        !coordinator.stopped
+
+        when:
+        coordinator.runCommand(command1, "command1")
+        start {
+            thread.blockUntil.running
+            coordinator.cancelBuild()
+            instant.cancelled
+        }
+        thread.blockUntil.cancelled
+        coordinator.runCommand(command2, "command2")
+
+        then:
+        !coordinator.willRefuseNewCommands
+        !coordinator.stopped
+        coordinator.idle
+
+        and:
+        2 * onStartCommand.run()
+        1 * command1.run() >> {
+            instant.running
+            thread.block()
+        }
+        1 * command2.run() >> {
+            assert !coordinator.stopped
+            assert !coordinator.cancellationToken.cancellationRequested
+        }
+        2 * onFinishCommand.run()
+        0 * _._
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DefaultDaemonConnectionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DefaultDaemonConnectionTest.groovy
index 388bb32..f7510d3 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DefaultDaemonConnectionTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DefaultDaemonConnectionTest.groovy
@@ -18,12 +18,11 @@ package org.gradle.launcher.daemon.server
 
 import org.gradle.launcher.daemon.protocol.CloseInput
 import org.gradle.launcher.daemon.protocol.ForwardInput
-import org.gradle.launcher.daemon.server.exec.StdinHandler
+import org.gradle.launcher.daemon.server.api.StdinHandler
 import org.gradle.messaging.remote.internal.Connection
 import org.gradle.util.ConcurrentSpecification
 
 import java.util.concurrent.CountDownLatch
-import org.gradle.launcher.daemon.protocol.Stop
 import java.util.concurrent.TimeUnit
 
 class DefaultDaemonConnectionTest extends ConcurrentSpecification {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/exec/DaemonHygieneActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/exec/DaemonHygieneActionTest.groovy
deleted file mode 100644
index 25fef4e..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/exec/DaemonHygieneActionTest.groovy
+++ /dev/null
@@ -1,52 +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.launcher.daemon.server.exec
-
-import org.gradle.internal.TimeProvider
-import spock.lang.Specification
-
-
-class DaemonHygieneActionTest extends Specification {
-
-    def "performs hygiene action"() {
-        def a = Spy(DaemonHygieneAction)
-
-        when:
-        a.execute(Mock(DaemonCommandExecution))
-        a.execute(Mock(DaemonCommandExecution))
-
-        then:
-        1 * a.gc()
-    }
-
-    def "does not trigger gc too often"() {
-        def timeProvider = Stub(TimeProvider) {
-            getCurrentTime() >>> [10, 100, 200]
-        }
-        def a = Spy(DaemonHygieneAction, constructorArgs: [100, timeProvider])
-
-        when:
-        //executed X3
-        a.execute(Mock(DaemonCommandExecution))
-        a.execute(Mock(DaemonCommandExecution))
-        a.execute(Mock(DaemonCommandExecution))
-
-        then:
-        //gc() called X2
-        2 * a.gc()
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonHealthTrackerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonHealthTrackerTest.groovy
new file mode 100644
index 0000000..e2c628f
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonHealthTrackerTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health
+
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution
+import org.gradle.launcher.daemon.server.api.DaemonStateControl
+import spock.lang.Specification
+
+class DaemonHealthTrackerTest extends Specification {
+
+    def control = Mock(DaemonStateControl)
+    def exec = Mock(DaemonCommandExecution) {
+        getDaemonStateControl() >> control
+    }
+    def stats = Mock(DaemonStats)
+    def status = Mock(DaemonStatus)
+    def logger = Mock(HealthLogger)
+    def tracker = new DaemonHealthTracker(stats, status, logger)
+
+    def "tracks start and complete events"() {
+        when: tracker.execute(exec)
+
+        then: 1 * stats.buildStarted()
+        then: 1 * logger.logHealth(stats, _)
+        then: 1 * exec.proceed()
+        then: 1 * stats.buildFinished()
+    }
+
+    def "does not track single use daemon"() {
+        when: tracker.execute(exec)
+
+        then:
+        1 * exec.isSingleUseDaemon() >> true
+        1 * exec.proceed()
+        0 * _
+    }
+
+    def "stops after the build when performance goes down"() {
+        1 * status.isDaemonTired(stats) >> true
+
+        when: tracker.execute(exec)
+
+        then:
+        1 * control.requestStop()
+    }
+
+    def "does not stop after the build when performance is acceptable"() {
+        1 * status.isDaemonTired(stats) >> false
+
+        when: tracker.execute(exec)
+
+        then:
+        0 * control.requestStop()
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatsTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatsTest.groovy
new file mode 100644
index 0000000..fcdaf81
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatsTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health
+
+import org.gradle.internal.TimeProvider
+import org.gradle.util.Clock
+import spock.lang.Specification
+
+class DaemonStatsTest extends Specification {
+
+    def clock = Stub(Clock)
+    def time = Stub(TimeProvider)
+    def memory = Stub(MemoryInfo)
+
+    def "consumes first build"() {
+        def stats = new DaemonStats(clock, Stub(TimeProvider), memory)
+        memory.getCommittedMemory() >> 5000000
+        memory.getMaxMemory() >> 10000000
+
+        when:
+        stats.buildStarted()
+        stats.buildFinished()
+
+        then:
+        stats.healthInfo == String.format("Starting build in new daemon [memory: %.1f MB]", 10.0)
+    }
+
+    def "consumes subsequent builds"() {
+        clock.getTime() >> "3 mins"
+        time.getCurrentTime() >>> [1, 1001]
+
+        memory.getCollectionTime() >> 25
+        memory.getCommittedMemory() >> 5000000
+        memory.getMaxMemory() >> 10000000
+
+        def stats = new DaemonStats(clock, time, memory)
+
+        when:
+        stats.buildStarted()
+        stats.buildFinished()
+        stats.buildStarted()
+        stats.buildFinished()
+
+        then:
+        stats.healthInfo == String.format("Starting 2nd build in daemon [uptime: 3 mins, performance: 98%%, memory: 50%% of %.1f MB]", 10.0)
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatusTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatusTest.groovy
new file mode 100644
index 0000000..483ecdf
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/DaemonStatusTest.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health
+
+import org.gradle.api.GradleException
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+import spock.lang.Unroll
+
+import static org.gradle.launcher.daemon.server.health.DaemonStatus.EXPIRE_AT_PROPERTY
+
+class DaemonStatusTest extends Specification {
+
+    @Subject status = new DaemonStatus()
+    def stats = Mock(DaemonStats)
+
+    @Rule SetSystemProperties props = new SetSystemProperties()
+
+    def "validates supplied threshold value"() {
+        System.setProperty(EXPIRE_AT_PROPERTY, "foo")
+
+        when:
+        status.isDaemonTired(stats)
+
+        then:
+        def ex = thrown(GradleException)
+        ex.message == "System property 'org.gradle.daemon.performance.expire-at' has incorrect value: 'foo'. The value needs to be integer."
+    }
+
+    @Unroll
+    def "knows when daemon is tired"() {
+        when:
+        System.setProperty(EXPIRE_AT_PROPERTY, threshold.toString())
+        stats.getCurrentPerformance() >> perf
+        stats.getMemoryUsed() >> mem
+
+        then:
+        status.isDaemonTired(stats) == tired
+
+        where:
+        threshold | perf | mem   | tired
+        90        | 89   | 100   | true
+        90        | 90   | 90    | true
+        90        | 91   | 100   | false
+        0         | 0    | 100   | false
+        0         | 1    | 100   | false
+        100       | 100  | 100   | true
+        100       | 100  | 60    | false
+        75        | 80   | 0     | false
+    }
+
+    def "daemon hygiene is disabled by default"() {
+        when:
+        stats.getCurrentPerformance() >> 0
+        stats.getMemoryUsed() >> 100
+
+        then:
+        !status.isDaemonTired(stats)
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HealthLoggerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HealthLoggerTest.groovy
new file mode 100644
index 0000000..4919fa9
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HealthLoggerTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.server.health
+
+import org.gradle.api.logging.Logger
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Subject
+
+class HealthLoggerTest extends Specification {
+
+    @Rule SetSystemProperties props = new SetSystemProperties()
+    @Subject healthLogger = new HealthLogger()
+    def stats = Mock(DaemonStats)
+    def logger = Mock(Logger)
+
+    def "logs at info level by default"() {
+        stats.healthInfo >> "xxx"
+        when: healthLogger.logHealth(stats, logger)
+        then: logger.info("xxx")
+    }
+
+    def "may log at lifecycle level"() {
+        System.setProperty(HealthLogger.HEALTH_MESSAGE_PROPERTY, "true")
+        stats.healthInfo >> "yyy"
+        when: healthLogger.logHealth(stats, logger)
+        then: logger.lifecycle("yyy")
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HintGCAfterBuildTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HintGCAfterBuildTest.groovy
new file mode 100644
index 0000000..8c23c86
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/health/HintGCAfterBuildTest.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.launcher.daemon.server.health
+
+import org.gradle.internal.TimeProvider
+import org.gradle.launcher.daemon.server.api.DaemonCommandExecution
+import spock.lang.Specification
+
+class HintGCAfterBuildTest extends Specification {
+
+    def "performs hygiene action"() {
+        def a = Spy(HintGCAfterBuild)
+
+        when:
+        a.execute(Mock(DaemonCommandExecution))
+        a.execute(Mock(DaemonCommandExecution))
+
+        then:
+        1 * a.gc()
+    }
+
+    def "does not trigger gc too often"() {
+        def timeProvider = Stub(TimeProvider) {
+            getCurrentTime() >>> [10, 100, 200]
+        }
+        def a = Spy(HintGCAfterBuild, constructorArgs: [100, timeProvider])
+
+        when:
+        //executed X3
+        a.execute(Mock(DaemonCommandExecution))
+        a.execute(Mock(DaemonCommandExecution))
+        a.execute(Mock(DaemonCommandExecution))
+
+        then:
+        //gc() called X2
+        2 * a.gc()
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/ChainingBuildActionRunnerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/ChainingBuildActionRunnerTest.groovy
new file mode 100644
index 0000000..42bcc92
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/ChainingBuildActionRunnerTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.exec
+
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.internal.invocation.BuildActionRunner
+import org.gradle.internal.invocation.BuildController
+import spock.lang.Specification
+
+class ChainingBuildActionRunnerTest extends Specification {
+    def runner1 = Mock(BuildActionRunner)
+    def runner2 = Mock(BuildActionRunner)
+    def runner3 = Mock(BuildActionRunner)
+    def runner = new ChainingBuildActionRunner([runner1, runner2, runner3])
+
+    def "invokes runners until a result is produced"() {
+        def action = Stub(BuildAction)
+        def controller = Mock(BuildController)
+
+        when:
+        runner.run(action, controller)
+
+        then:
+        1 * runner1.run(action, controller)
+        1 * controller.hasResult() >> false
+        1 * runner2.run(action, controller)
+        1 * controller.hasResult() >> true
+        0 * runner3._
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuterTest.groovy
new file mode 100644
index 0000000..5bc3f36
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DaemonUsageSuggestingBuildActionExecuterTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.exec
+
+import org.gradle.StartParameter
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.logging.LogLevel
+import org.gradle.initialization.BuildRequestContext
+import org.gradle.internal.environment.GradleBuildEnvironment
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.logging.StyledTextOutput
+import org.gradle.logging.StyledTextOutputFactory
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.launcher.daemon.configuration.DaemonUsage.*
+
+class DaemonUsageSuggestingBuildActionExecuterTest extends Specification {
+    static final String DAEMON_DOCS_URL = "gradle-daemon-docs-url"
+
+    final BuildActionExecuter<BuildActionParameters> delegate = Mock(BuildActionExecuter)
+    final StyledTextOutput textOutput = Mock()
+    final StyledTextOutputFactory textOutputFactory = Mock() {
+        create(DaemonUsageSuggestingBuildActionExecuter, LogLevel.LIFECYCLE) >> textOutput
+    }
+    final GradleBuildEnvironment buildEnvironment = Mock()
+    final DocumentationRegistry documentationRegistry = Mock() {
+        getDocumentationFor("gradle_daemon") >> DAEMON_DOCS_URL
+    }
+
+    final OperatingSystem os = Mock(OperatingSystem)
+    final DaemonUsageSuggestingBuildActionExecuter executer = new DaemonUsageSuggestingBuildActionExecuter(delegate, textOutputFactory, documentationRegistry, os)
+    final StartParameter startParameter = Mock()
+    final BuildAction action = Mock() {
+        getStartParameter() >> startParameter
+    }
+    final BuildRequestContext buildRequestContext = Mock()
+    final BuildActionParameters params = Mock()
+
+    def "delegates execution to the underlying executer"() {
+        given:
+        def executionResult = new Object()
+        delegate.execute(action, buildRequestContext, params) >> executionResult
+        params.daemonUsage >> EXPLICITLY_ENABLED
+
+        when:
+        def result = executer.execute(action, buildRequestContext, params)
+
+        then:
+        result == executionResult
+    }
+
+    def "suggests using daemon when not on windows, daemon usage is not explicitly specified and CI env var is not specified"() {
+        given:
+        params.daemonUsage >> IMPLICITLY_DISABLED
+        params.envVariables >> [CI: null]
+        os.windows >> false
+
+        when:
+        executer.execute(action, buildRequestContext, params)
+
+        then:
+        1 * textOutput.println()
+
+        and:
+        1 * textOutput.println(DaemonUsageSuggestingBuildActionExecuter.PLEASE_USE_DAEMON_MESSAGE_PREFIX + DAEMON_DOCS_URL)
+    }
+
+    @Unroll
+    def "does not suggest using daemon [#daemonUsage, #ciEnvValue, #isWindows]"() {
+        given:
+        params.daemonUsage >> daemonUsage
+        params.getEnvVariables() >> [CI: ciEnvValue]
+        os.windows >> isWindows
+
+        when:
+        executer.execute(action, buildRequestContext, params)
+
+        then:
+        0 * textOutput._
+
+        where:
+        daemonUsage         | ciEnvValue | isWindows
+        IMPLICITLY_DISABLED | null       | true
+        IMPLICITLY_DISABLED | "true"     | true
+        IMPLICITLY_DISABLED | "true"     | false
+        EXPLICITLY_DISABLED | null       | true
+        EXPLICITLY_DISABLED | null       | false
+        EXPLICITLY_DISABLED | "true"     | true
+        EXPLICITLY_DISABLED | "true"     | false
+        EXPLICITLY_ENABLED  | null       | true
+        EXPLICITLY_ENABLED  | null       | false
+        EXPLICITLY_ENABLED  | "true"     | true
+        EXPLICITLY_ENABLED  | "true"     | false
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DefaultBuildActionParametersTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DefaultBuildActionParametersTest.groovy
index 6d55dca..5a93196 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DefaultBuildActionParametersTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/DefaultBuildActionParametersTest.groovy
@@ -17,14 +17,15 @@
 package org.gradle.launcher.exec
 
 import org.gradle.api.logging.LogLevel
-import org.gradle.configuration.GradleLauncherMetaData
 import spock.lang.Specification
 
+import static org.gradle.launcher.daemon.configuration.DaemonUsage.IMPLICITLY_DISABLED
+
 public class DefaultBuildActionParametersTest extends Specification {
 
     def "is serializable"() {
         given:
-        def params = new DefaultBuildActionParameters(new GradleLauncherMetaData(), System.currentTimeMillis(), System.properties, System.getenv(), new File("."), LogLevel.ERROR)
+        def params = new DefaultBuildActionParameters(System.properties, System.getenv(), new File("."), LogLevel.ERROR, IMPLICITLY_DISABLED)
         ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream());
 
         when:
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/InProcessBuildActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/InProcessBuildActionExecuterTest.groovy
index a3768a3..60ab42b 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/InProcessBuildActionExecuterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/exec/InProcessBuildActionExecuterTest.groovy
@@ -16,141 +16,158 @@
 
 package org.gradle.launcher.exec
 
-import org.gradle.initialization.BuildController
-import spock.lang.Specification
-import org.gradle.initialization.GradleLauncherFactory
-import org.gradle.initialization.BuildAction
-
-import org.gradle.initialization.BuildRequestMetaData
-import org.gradle.GradleLauncher
 import org.gradle.BuildResult
-
 import org.gradle.StartParameter
+import org.gradle.api.internal.GradleInternal
+import org.gradle.initialization.BuildRequestContext
+import org.gradle.initialization.BuildRequestMetaData
+import org.gradle.initialization.DefaultGradleLauncher
+import org.gradle.initialization.GradleLauncherFactory
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.internal.invocation.BuildActionRunner
+import org.gradle.internal.invocation.BuildController
+import spock.lang.Specification
 
 class InProcessBuildActionExecuterTest extends Specification {
     final GradleLauncherFactory factory = Mock()
-    final GradleLauncher launcher = Mock()
+    final DefaultGradleLauncher launcher = Mock()
+    final BuildRequestContext buildRequestContext = Mock()
     final BuildActionParameters param = Mock()
     final BuildRequestMetaData metaData = Mock()
     final BuildResult buildResult = Mock()
-    final InProcessBuildActionExecuter executer = new InProcessBuildActionExecuter(factory)
+    final GradleInternal gradle = Mock()
+    final BuildActionRunner actionRunner = Mock()
+    final StartParameter startParameter = Mock()
+    BuildAction action = Mock() {
+        getStartParameter() >> startParameter
+    }
+    final InProcessBuildActionExecuter executer = new InProcessBuildActionExecuter(factory, actionRunner)
 
     def setup() {
         _ * param.buildRequestMetaData >> metaData
     }
 
-    def "creates a launcher using a default StartParameter when the action does not specify any"() {
-        BuildAction<String> action = Mock()
+    def "creates launcher and forwards action to action runner"() {
+        given:
+        param.envVariables >> [:]
 
         when:
-        def result = executer.execute(action, param)
+        def result = executer.execute(action, buildRequestContext, param)
 
         then:
         result == '<result>'
 
         and:
-        1 * factory.newInstance(!null, metaData) >> launcher
-        1 * action.run(!null) >> { BuildController controller ->
-            assert controller.launcher == launcher
-            return '<result>'
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
+        1 * actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
+            controller.result = '<result>'
         }
+        1 * launcher.stop()
     }
 
-    def "creates a launcher using StartParameter specified by the action"() {
-        BuildAction<String> action = Mock()
-        def startParam = new StartParameter()
+    def "can have null result"() {
+        given:
+        param.envVariables >> [:]
 
         when:
-        def result = executer.execute(action, param)
+        def result = executer.execute(action, buildRequestContext, param)
 
         then:
-        result == '<result>'
+        result == null
 
         and:
-        1 * factory.newInstance(startParam, metaData) >> launcher
-        1 * action.run(!null) >> { BuildController controller ->
-            controller.startParameter = startParam
-            assert controller.launcher == launcher
-            return '<result>'
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
+        1 * actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
+            assert !controller.hasResult()
+            controller.result = null
+            assert controller.hasResult()
         }
-    }
-
-    def "cannot set start parameters after launcher created"() {
-        BuildAction<String> action = Mock()
-        def startParam = new StartParameter()
-
-        given:
-        _ * action.run(!null) >> { BuildController controller ->
-            controller.launcher
-            controller.startParameter = startParam
-        }
-        _ * factory.newInstance(!null, metaData) >> launcher
-
-        when:
-        executer.execute(action, param)
-
-        then:
-        IllegalStateException e = thrown()
-        e.message == 'Cannot change start parameter after launcher has been created.'
+        1 * launcher.stop()
     }
 
     def "runs build when requested by action"() {
-        BuildAction<String> action = Mock()
+        given:
+        param.envVariables >> [:]
 
         when:
-        def result = executer.execute(action, param)
+        def result = executer.execute(action, buildRequestContext, param)
 
         then:
         result == '<result>'
 
         and:
-        1 * factory.newInstance(!null, metaData) >> launcher
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
         1 * launcher.run() >> buildResult
         _ * buildResult.failure >> null
-        1 * action.run(!null) >> { BuildController controller ->
-            controller.run()
-            return '<result>'
+        _ * buildResult.gradle >> gradle
+        _ * actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
+            assert controller.run() == gradle
+            controller.result = '<result>'
         }
+        1 * launcher.stop()
     }
 
     def "configures build when requested by action"() {
-        BuildAction<String> action = Mock()
+        given:
+        param.envVariables >> [:]
 
         when:
-        def result = executer.execute(action, param)
+        def result = executer.execute(action, buildRequestContext, param)
 
         then:
         result == '<result>'
 
         and:
-        1 * factory.newInstance(!null, metaData) >> launcher
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
         1 * launcher.getBuildAnalysis() >> buildResult
         _ * buildResult.failure >> null
-        1 * action.run(!null) >> { BuildController controller ->
+        _ * buildResult.gradle >> gradle
+        _ * actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
+            assert controller.configure() == gradle
+            controller.result = '<result>'
+        }
+        1 * launcher.stop()
+    }
+
+    def "cannot request configuration after build has been run"() {
+        given:
+        actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
+            controller.run()
             controller.configure()
-            return '<result>'
         }
+
+        when:
+        executer.execute(action, buildRequestContext, param)
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == 'Cannot use launcher after build has completed.'
+
+        and:
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
+        1 * launcher.run() >> buildResult
+        1 * launcher.stop()
     }
 
-    def "wraps build failure"() {
+    def "wraps build failure and cleans up"() {
         def failure = new RuntimeException()
-        BuildAction<String> action = Mock()
 
         given:
         buildResult.failure >> failure
 
         when:
-        executer.execute(action, param)
+        executer.execute(action, buildRequestContext, param)
 
         then:
         ReportedException e = thrown()
         e.cause == failure
 
         and:
-        1 * factory.newInstance(!null, metaData) >> launcher
+        1 * factory.newInstance(startParameter, buildRequestContext) >> launcher
         1 * launcher.run() >> buildResult
-        1 * action.run(!null) >> { BuildController controller ->
+        _ * actionRunner.run(action, !null) >> { BuildAction a, BuildController controller ->
             controller.run()
         }
+        1 * launcher.stop()
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/AbstractClassGraphSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/AbstractClassGraphSpec.groovy
index 246d80b..f10d03a 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/AbstractClassGraphSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/AbstractClassGraphSpec.groovy
@@ -22,6 +22,9 @@ import org.gradle.util.TestClassLoader
 import org.junit.Rule
 import spock.lang.Specification
 
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+
 abstract class AbstractClassGraphSpec extends Specification {
     @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
@@ -32,6 +35,7 @@ abstract class AbstractClassGraphSpec extends Specification {
         return classes.collect { ClasspathUtil.getClasspathForClass(it) }
     }
 
+
     /**
      * Makes a copy of the given classes and returns the classpath for these copies. Each class is added to its own classpath root.
      */
@@ -48,14 +52,30 @@ abstract class AbstractClassGraphSpec extends Specification {
     }
 
     /**
-     * Returns a URLClassLoader with the given classpath and parent. Parent defaults to system ClassLoader.
+     * Copies the given classes to a jar file and returns the file.
+     */
+    File isolatedClassesInJar(String filename = "test.jar", Class<?>... classes) {
+        File zipFile = tmpDir.file(filename)
+        ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(zipFile))
+        classes.each {
+            def name = it.name.replace('.', '/') + '.class'
+            def resource = it.classLoader.getResource(name)
+            zip.putNextEntry(new ZipEntry(name))
+            zip.write(resource.bytes)
+        }
+        zip.close()
+        return zipFile
+    }
+
+    /**
+     * Returns a URLClassLoader with the given classpath and root. Parent defaults to system ClassLoader.
      */
     URLClassLoader urlClassLoader(ClassLoader parent = ClassLoader.systemClassLoader.parent, List<File> classpath) {
         return new URLClassLoader(classpath.collect { it.toURI().toURL() } as URL[], parent)
     }
 
     /**
-     * Returns a custom ClassLoader with the given classpath and parent. Parent defaults to system ClassLoader.
+     * Returns a custom ClassLoader with the given classpath and root. Parent defaults to system ClassLoader.
      */
     ClassLoader customClassLoader(ClassLoader parent = ClassLoader.systemClassLoader.parent, List<File> classpath) {
         return new TestClassLoader(parent, classpath)
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClasspathInfererTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClasspathInfererTest.groovy
index fcd2cc3..7c3f5df 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClasspathInfererTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClasspathInfererTest.groovy
@@ -18,6 +18,14 @@ package org.gradle.tooling.internal.provider
 
 import org.gradle.internal.classloader.MutableURLClassLoader
 import org.gradle.tooling.BuildAction
+import org.gradle.util.TestClassLoader
+import spock.lang.Issue
+
+import java.security.CodeSource
+import java.security.Permissions
+import java.security.ProtectionDomain
+import java.security.cert.Certificate
+
 
 class ClasspathInfererTest extends AbstractClassGraphSpec {
     def factory = new ClasspathInferer()
@@ -59,8 +67,54 @@ class ClasspathInfererTest extends AbstractClassGraphSpec {
         action.execute(null)
     }
 
+    @Issue("GRADLE-3245")
+    def "determines action and tooling API classpath when loaded from a jar via a non-standard ClassLoader"() {
+        def cl = new NetBeansLikeClassLoader(ClassLoader.systemClassLoader.parent, [isolatedClassesInJar(CustomAction, CustomModel)] + toolingApiClassPath)
+        def actionClass = cl.loadClass(CustomAction.name)
+
+        expect:
+        def classpath = []
+        factory.getClassPathFor(actionClass, classpath)
+        def loader = new MutableURLClassLoader(ClassLoader.systemClassLoader.parent, classpath)
+        def action = loader.loadClass(CustomAction.name).newInstance()
+        action.execute(null)
+    }
+
     private List<File> getToolingApiClassPath() {
         originalClassPath(BuildAction)
     }
 
+    /**
+     * A classloader that produces classes with CodeSource objects containing the full resource URL,
+     * similar to how the Netbeans JarClassLoader works.
+     */
+    class NetBeansLikeClassLoader extends TestClassLoader {
+        NetBeansLikeClassLoader(ClassLoader classLoader, List<File> classpath) {
+            super(classLoader, classpath)
+        }
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            String resource = name.replace('.', '/') + '.class'
+            URL url = findResource(resource)
+            if (url == null) {
+                throw new ClassNotFoundException("Could not find class '${name}'")
+            }
+            def byteCode = url.bytes
+            CodeSource codeSource = new CodeSource(getCodeBaseUrl(url, resource), null as Certificate[])
+            return defineClass(name, byteCode, 0, byteCode.length, new ProtectionDomain(codeSource, new Permissions(), this, null))
+        }
+
+        URL getCodeBaseUrl(URL url, String resource) {
+            def uri = url.toURI().toString()
+            if (uri.startsWith("jar:")) {
+                int pos = uri.indexOf('!')
+                def newURI = uri.substring(0, pos + 2)
+                return new URI(newURI).toURL()
+            } else {
+                def newURI = uri - resource
+                return new URI(newURI).toURL()
+            }
+        }
+    }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactoryTest.groovy
new file mode 100644
index 0000000..5368ee2
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ClientSidePayloadClassLoaderFactoryTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider
+
+import org.gradle.internal.classloader.MutableURLClassLoader
+import spock.lang.Specification
+
+class ClientSidePayloadClassLoaderFactoryTest extends Specification {
+    def registry = new ClientSidePayloadClassLoaderFactory(Mock(PayloadClassLoaderFactory))
+
+    def "creates ClassLoader for classpath"() {
+        def url1 = new URL("http://localhost/file1.jar")
+        def url2 = new URL("http://localhost/file2.jar")
+
+        when:
+        def cl = registry.getClassLoaderFor(new MutableURLClassLoader.Spec([url1, url2]), [null])
+
+        then:
+        cl instanceof MutableURLClassLoader
+        cl.URLs == [url1, url2] as URL[]
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ConfiguringBuildActionTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ConfiguringBuildActionTest.groovy
deleted file mode 100644
index 0bcd189..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ConfiguringBuildActionTest.groovy
+++ /dev/null
@@ -1,122 +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.tooling.internal.provider
-
-import org.gradle.launcher.cli.converter.PropertiesToStartParameterConverter
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.tooling.internal.impl.LaunchableImplementation
-import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters
-import org.junit.Rule
-import spock.lang.Specification
-
-import static org.gradle.util.Matchers.isSerializable
-import static org.hamcrest.MatcherAssert.assertThat
-
-class ConfiguringBuildActionTest extends Specification {
-    @Rule TestNameTestDirectoryProvider temp
-
-    def "allows configuring the start parameter with build arguments"() {
-        when:
-        def action = new ConfiguringBuildAction(arguments: ['-PextraProperty=foo', '-m'])
-        def start = action.configureStartParameter()
-
-        then:
-        start.projectProperties['extraProperty'] == 'foo'
-        start.dryRun
-    }
-
-    def "can overwrite project dir via build arguments"() {
-        given:
-        def projectDir = temp.createDir('projectDir')
-
-        when:
-        def action = new ConfiguringBuildAction(projectDirectory: projectDir, arguments: ['-p', 'otherDir'])
-        def start = action.configureStartParameter()
-
-        then:
-        start.projectDir == new File(projectDir, "otherDir")
-    }
-
-    def "can overwrite gradle user home via build arguments"() {
-        given:
-        def dotGradle = temp.createDir('.gradle')
-        def projectDir = temp.createDir('projectDir')
-
-        when:
-        def action = new ConfiguringBuildAction(gradleUserHomeDir: dotGradle, projectDirectory: projectDir, 
-                arguments: ['-g', 'otherDir'])
-        def start = action.configureStartParameter()
-
-        then:
-        start.gradleUserHomeDir == new File(projectDir, "otherDir")
-    }
-
-    def "can overwrite searchUpwards via build arguments"() {
-        when:
-        def action = new ConfiguringBuildAction(arguments: ['-u'])
-        def start = action.configureStartParameter()
-
-        then:
-        !start.searchUpwards
-    }
-
-    def "searchUpwards configured directly on the action wins over the command line setting"() {
-        when:
-        def action = new ConfiguringBuildAction(arguments: ['-u'], searchUpwards: true)
-        def start = action.configureStartParameter()
-
-        then:
-        start.searchUpwards
-    }
-
-    def "the start parameter is configured from properties"() {
-        given:
-        def converter = Mock(PropertiesToStartParameterConverter)
-        def action = new ConfiguringBuildAction(properties: [foo: 'bar'])
-
-        when:
-        action.configureStartParameter(converter)
-
-        then:
-        1 * converter.convert([foo: 'bar'], _)
-    }
-
-    def "is serializable"() {
-        expect:
-        assertThat(new ConfiguringBuildAction({} as ProviderOperationParameters, null, [foo: 'bar']), isSerializable())
-    }
-
-    def "accepts launchables from consumer"() {
-        given:
-        def selector = Mock(LaunchableImplementation)
-        _ * selector.taskName >> 'myTask'
-        _ * selector.projectPath >> ':child'
-
-        ProviderOperationParameters providerParameters = Mock(ProviderOperationParameters)
-        _ * providerParameters.launchables >> [selector]
-        _ * providerParameters.tasks >> []
-        def action = new ConfiguringBuildAction(providerParameters, null, [:])
-
-        when:
-        def start = action.configureStartParameter()
-
-        then:
-        start.projectPath == ':child'
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonBuildActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonBuildActionExecuterTest.groovy
index 0f6c1ca..90e93a7 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonBuildActionExecuterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonBuildActionExecuterTest.groovy
@@ -15,7 +15,8 @@
  */
 package org.gradle.tooling.internal.provider
 
-import org.gradle.initialization.BuildAction
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.initialization.BuildRequestContext
 import org.gradle.launcher.daemon.client.DaemonClient
 import org.gradle.launcher.daemon.configuration.DaemonParameters
 import org.gradle.launcher.exec.ReportedException
@@ -25,7 +26,8 @@ import spock.lang.Specification
 
 class DaemonBuildActionExecuterTest extends Specification {
     final DaemonClient client = Mock()
-    final BuildAction<String> action = Mock()
+    final BuildAction action = Mock()
+    final BuildRequestContext buildRequestContext = Mock()
     final ProviderOperationParameters parameters = Mock()
     final DaemonParameters daemonParameters = Mock()
     final DaemonBuildActionExecuter executer = new DaemonBuildActionExecuter(client, daemonParameters)
@@ -34,12 +36,12 @@ class DaemonBuildActionExecuterTest extends Specification {
         def failure = new RuntimeException()
 
         when:
-        executer.execute(action, parameters)
+        executer.execute(action, buildRequestContext, parameters)
 
         then:
         BuildExceptionVersion1 e = thrown()
         e.cause == failure
-        1 * client.execute(action, !null) >> { throw new ReportedException(failure) }
+        1 * client.execute(action, buildRequestContext, !null) >> { throw new ReportedException(failure) }
         _ * daemonParameters.effectiveSystemProperties >> [:]
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactoryTest.groovy
new file mode 100644
index 0000000..2ac2598
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DaemonSidePayloadClassLoaderFactoryTest.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.tooling.internal.provider
+
+import org.gradle.internal.Factory
+import org.gradle.cache.CacheBuilder
+import org.gradle.cache.CacheRepository
+import org.gradle.cache.PersistentCache
+import org.gradle.internal.classloader.MutableURLClassLoader
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class DaemonSidePayloadClassLoaderFactoryTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def factory = Mock(PayloadClassLoaderFactory)
+    def jarCache = Mock(JarCache)
+    def cache = Stub(PersistentCache)
+    def cacheBuilder = Stub(CacheBuilder) {
+        open() >> cache
+        withDisplayName(_) >> { cacheBuilder }
+        withCrossVersionCache() >> { cacheBuilder }
+        withLockOptions(_) >> { cacheBuilder }
+    }
+    def cacheRepository = Stub(CacheRepository) {
+        cache(_) >> cacheBuilder
+    }
+
+    def registry = new DaemonSidePayloadClassLoaderFactory(factory, jarCache, cacheRepository)
+
+    def "creates ClassLoader for classpath"() {
+        def url1 = new URL("http://localhost/file1.jar")
+        def url2 = new URL("http://localhost/file2.jar")
+
+        when:
+        def cl = registry.getClassLoaderFor(new MutableURLClassLoader.Spec([url1, url2]), [null])
+
+        then:
+        cl instanceof MutableURLClassLoader
+        cl.URLs == [url1, url2] as URL[]
+    }
+
+    def "creates ClassLoader for jar classpath"() {
+        def jarFile = tmpDir.createFile("file1.jar")
+        def cachedJar = tmpDir.createFile("cached/file1.jar")
+        def url1 = jarFile.toURI().toURL()
+        def cached = cachedJar.toURI().toURL()
+        def url2 = tmpDir.createDir("classes-dir").toURI().toURL()
+
+        given:
+        cache.useCache(_, _) >> { String display, Factory f -> f.create() }
+        jarCache.getCachedJar(jarFile, _) >> cachedJar
+
+        when:
+        def cl = registry.getClassLoaderFor(new MutableURLClassLoader.Spec([url1, url2]), [null])
+
+        then:
+        cl instanceof MutableURLClassLoader
+        cl.URLs == [cached, url2] as URL[]
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DefaultBuildControllerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DefaultBuildControllerTest.groovy
deleted file mode 100644
index acc466d..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/DefaultBuildControllerTest.groovy
+++ /dev/null
@@ -1,129 +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.tooling.internal.provider
-
-import org.gradle.api.internal.GradleInternal
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.tooling.internal.gradle.GradleProjectIdentity
-import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException
-import org.gradle.tooling.internal.protocol.ModelIdentifier
-import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder
-import org.gradle.tooling.provider.model.ToolingModelBuilder
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-import org.gradle.tooling.provider.model.UnknownModelException
-import spock.lang.Specification
-
-class DefaultBuildControllerTest extends Specification {
-    def gradle = Stub(GradleInternal)
-    def registry = Stub(ToolingModelBuilderRegistry)
-    def project = Stub(ProjectInternal) {
-        getServices() >> Stub(ServiceRegistry) {
-            get(ToolingModelBuilderRegistry) >> registry
-        }
-    }
-    def modelId = Stub(ModelIdentifier) {
-        getName() >> 'some.model'
-    }
-    def modelBuilder = Stub(ToolingModelBuilder)
-    def controller = new DefaultBuildController(gradle)
-
-    def "adapts model not found exception to protocol exception"() {
-        def failure = new UnknownModelException("not found")
-
-        given:
-        _ * gradle.defaultProject >> project
-        _ * registry.getBuilder('some.model') >> { throw failure }
-
-        when:
-        controller.getModel(null, modelId)
-
-        then:
-        InternalUnsupportedModelException e = thrown()
-        e.cause == failure
-    }
-
-    def "uses builder for specified project"() {
-        def target = Stub(GradleProjectIdentity)
-        def rootProject = Stub(ProjectInternal)
-        def model = new Object()
-
-        given:
-        _ * target.path >> ":some:path"
-        _ * gradle.rootProject >> rootProject
-        _ * rootProject.project(":some:path") >> project
-        _ * registry.getBuilder("some.model") >> modelBuilder
-        _ * modelBuilder.buildAll("some.model", project) >> model
-
-        when:
-        def result = controller.getModel(target, modelId)
-
-        then:
-        result.getModel() == model
-    }
-
-    def "uses builder for default project when none specified"() {
-        def model = new Object()
-
-        given:
-        _ * gradle.defaultProject >> project
-        _ * registry.getBuilder("some.model") >> modelBuilder
-        _ * modelBuilder.buildAll("some.model", project) >> model
-
-        when:
-        def result = controller.getModel(null, modelId)
-
-        then:
-        result.getModel() == model
-    }
-
-    def "passes information about default project when context sensitive builder is used"() {
-        def contextModelBuilder = Stub(ProjectSensitiveToolingModelBuilder)
-        def model = new Object()
-
-        given:
-        _ * gradle.defaultProject >> project
-        _ * registry.getBuilder("some.model") >> contextModelBuilder
-        _ * contextModelBuilder.buildAll("some.model", project, true) >> model
-
-        when:
-        def result = controller.getModel(null, modelId)
-
-        then:
-        result.getModel() == model
-    }
-
-    def "passes information about specified project when context sensitive builder is used"() {
-        def contextModelBuilder = Stub(ProjectSensitiveToolingModelBuilder)
-        def model = new Object()
-        def target = Stub(GradleProjectIdentity)
-        def rootProject = Stub(ProjectInternal)
-
-        given:
-        _ * target.path >> ":some:path"
-        _ * gradle.rootProject >> rootProject
-        _ * rootProject.project(":some:path") >> project
-        _ * registry.getBuilder("some.model") >> contextModelBuilder
-        _ * contextModelBuilder.buildAll("some.model", project, false) >> model
-
-        when:
-        def result = controller.getModel(target, modelId)
-
-        then:
-        result.getModel() == model
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/JarCacheTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/JarCacheTest.groovy
new file mode 100644
index 0000000..21810e6
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/JarCacheTest.groovy
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider
+
+import org.gradle.internal.Factory
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class JarCacheTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def baseDirFactory = Mock(Factory)
+    def original = tmpDir.createFile("original.txt")
+    def cacheDir = tmpDir.createDir("cache")
+    def cache = new JarCache()
+
+    def "copies file into cache when it has not been seen before"() {
+        given:
+        baseDirFactory.create() >> cacheDir
+
+        expect:
+        def cached = cache.getCachedJar(original, baseDirFactory)
+        cached != original
+        cached.text == original.text
+        cached.name == original.name
+    }
+
+    def "reuses cached file when it has not changed"() {
+        given:
+        def copy = cache(original)
+
+        when:
+        def result = cache.getCachedJar(original, baseDirFactory)
+
+        then:
+        result == copy
+
+        and:
+        0 * _
+    }
+
+    def "reuses cached file when its content has not changed"() {
+        given:
+        def copy = cache(original)
+        original.lastModified = original.lastModified() + 2000
+
+        when:
+        def result = cache.getCachedJar(original, baseDirFactory)
+
+        then:
+        result == copy
+
+        and:
+        0 * _
+    }
+
+    def "copies file into cache when its content has changed"() {
+        def originalLastModified = original.lastModified()
+
+        given:
+        def copy = cache(original)
+        original.text = "this is some new content"
+        original.lastModified = originalLastModified
+
+        when:
+        def result = cache.getCachedJar(original, baseDirFactory)
+
+        then:
+        result != copy
+        result != original
+        result.text == original.text
+
+        and:
+        1 * baseDirFactory.create() >> cacheDir
+        0 * _
+    }
+
+    def "does not copy file into cache when it already exists in the cache directory"() {
+        given:
+        def copy = new JarCache().getCachedJar(original, { cacheDir} as Factory)
+        original.lastModified = original.lastModified() + 2000
+
+        when:
+        def result = cache.getCachedJar(original, baseDirFactory)
+
+        then:
+        result == copy
+
+        and:
+        1 * baseDirFactory.create() >> cacheDir
+        0 * _
+    }
+
+    def "copies file again when it has been deleted from the cache directory"() {
+        given:
+        def copy = cache(original)
+        copy.delete()
+
+        when:
+        def result = cache.getCachedJar(original, baseDirFactory)
+
+        then:
+        result == copy
+        copy.text == original.text
+
+        and:
+        1 * baseDirFactory.create() >> cacheDir
+        0 * _
+    }
+
+    def cache(TestFile original)  {
+        return cache.getCachedJar(original, { cacheDir } as Factory)
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuterTest.groovy
index 09beacb..b12bbeb 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuterTest.groovy
@@ -16,7 +16,8 @@
 package org.gradle.tooling.internal.provider
 
 import org.gradle.api.logging.LogLevel
-import org.gradle.initialization.BuildAction
+import org.gradle.internal.invocation.BuildAction
+import org.gradle.initialization.BuildRequestContext
 import org.gradle.internal.Factory
 import org.gradle.launcher.exec.BuildActionExecuter
 import org.gradle.logging.LoggingManagerInternal
@@ -27,7 +28,8 @@ class LoggingBridgingBuildActionExecuterTest extends Specification {
     final BuildActionExecuter<ProviderOperationParameters> target = Mock()
     final Factory<LoggingManagerInternal> loggingManagerFactory = Mock()
     final LoggingManagerInternal loggingManager = Mock()
-    final BuildAction<String> action = Mock()
+    final BuildAction action = Mock()
+    final BuildRequestContext buildRequestContext = Mock()
     final ProviderOperationParameters parameters = Mock()
 
     //declared type-lessly to work around groovy eclipse plugin bug
@@ -35,13 +37,13 @@ class LoggingBridgingBuildActionExecuterTest extends Specification {
 
     def configuresLoggingWhileActionIsExecuting() {
         when:
-        executer.execute(action, parameters)
+        executer.execute(action, buildRequestContext, parameters)
 
         then:
         1 * loggingManagerFactory.create() >> loggingManager
         1 * loggingManager.addOutputEventListener(!null)
         1 * loggingManager.start()
-        1 * target.execute(action, parameters)
+        1 * target.execute(action, buildRequestContext, parameters)
         1 * loggingManager.stop()
     }
 
@@ -49,14 +51,14 @@ class LoggingBridgingBuildActionExecuterTest extends Specification {
         def failure = new RuntimeException()
 
         when:
-        executer.execute(action, parameters)
+        executer.execute(action, buildRequestContext, parameters)
 
         then:
         RuntimeException e = thrown()
         e == failure
         1 * loggingManagerFactory.create() >> loggingManager
         1 * loggingManager.start()
-        1 * target.execute(action, parameters) >> {throw failure}
+        1 * target.execute(action, buildRequestContext, parameters) >> {throw failure}
         1 * loggingManager.stop()
     }
 
@@ -66,7 +68,7 @@ class LoggingBridgingBuildActionExecuterTest extends Specification {
         parameters.getBuildLogLevel() >> LogLevel.QUIET
 
         when:
-        executer.execute(action, parameters)
+        executer.execute(action, buildRequestContext, parameters)
         
         then:
         1 * loggingManager.setLevel(LogLevel.QUIET)
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ModelClassLoaderFactoryTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ModelClassLoaderFactoryTest.groovy
deleted file mode 100644
index 6be72e1..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ModelClassLoaderFactoryTest.groovy
+++ /dev/null
@@ -1,36 +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.tooling.internal.provider
-
-import org.gradle.internal.classloader.MutableURLClassLoader
-import spock.lang.Specification
-
-class ModelClassLoaderFactoryTest extends Specification {
-    final ModelClassLoaderFactory registry = new ModelClassLoaderFactory()
-
-    def "creates ClassLoader for classpath"() {
-        def url1 = new URL("http://localhost/file1.jar")
-        def url2 = new URL("http://localhost/file2.jar")
-
-        when:
-        def cl = registry.getClassLoaderFor(new MutableURLClassLoader.Spec([url1, url2]), [null])
-
-        then:
-        cl instanceof MutableURLClassLoader
-        cl.URLs == [url1, url2] as URL[]
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/PayloadSerializerTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/PayloadSerializerTest.groovy
index 9d21b53..669f50e 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/PayloadSerializerTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/PayloadSerializerTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.tooling.internal.provider
 
+import org.gradle.internal.classloader.DefaultClassLoaderFactory
 import org.gradle.internal.classloader.FilteringClassLoader
 import org.junit.Assert
 import spock.lang.Ignore
@@ -25,8 +26,8 @@ import java.lang.reflect.Method
 import java.lang.reflect.Proxy
 
 class PayloadSerializerTest extends AbstractClassGraphSpec {
-    final PayloadSerializer originator = new PayloadSerializer(new DefaultPayloadClassLoaderRegistry(new ModelClassLoaderFactory()))
-    final PayloadSerializer receiver = new PayloadSerializer(new DefaultPayloadClassLoaderRegistry(new ModelClassLoaderFactory()))
+    final PayloadSerializer originator = new PayloadSerializer(new DefaultPayloadClassLoaderRegistry(new ClassLoaderCache(), new ModelClassLoaderFactory(new DefaultClassLoaderFactory())))
+    final PayloadSerializer receiver = new PayloadSerializer(new DefaultPayloadClassLoaderRegistry(new ClassLoaderCache(), new ModelClassLoaderFactory(new DefaultClassLoaderFactory())))
 
     def "can send an object between two parties"() {
         expect:
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ProviderStartParameterConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ProviderStartParameterConverterTest.groovy
new file mode 100644
index 0000000..b38bc9a
--- /dev/null
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ProviderStartParameterConverterTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * 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.tooling.internal.provider
+
+import org.gradle.TaskExecutionRequest
+import org.gradle.launcher.daemon.configuration.GradleProperties
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.tooling.internal.protocol.InternalLaunchable
+import org.gradle.tooling.internal.provider.connection.ProviderOperationParameters
+import org.junit.Rule
+import spock.lang.Specification
+
+class ProviderStartParameterConverterTest extends Specification {
+    @Rule TestNameTestDirectoryProvider temp
+    def params = Stub(ProviderOperationParameters)
+
+    def "allows configuring the start parameter with build arguments"() {
+        params.getArguments(_) >> ['-PextraProperty=foo', '-m']
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        start.projectProperties['extraProperty'] == 'foo'
+        start.dryRun
+    }
+
+    def "can overwrite project dir via build arguments"() {
+        given:
+        def projectDir = temp.createDir('projectDir')
+        params.getProjectDir() >> projectDir
+        params.getArguments(_) >> ['-p', 'otherDir']
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        start.projectDir == new File(projectDir, "otherDir")
+    }
+
+    def "can overwrite gradle user home via build arguments"() {
+        given:
+        def dotGradle = temp.createDir('.gradle')
+        def projectDir = temp.createDir('projectDir')
+        params.getGradleUserHomeDir() >> dotGradle
+        params.getProjectDir() >> projectDir
+        params.getArguments(_) >> ['-g', 'otherDir']
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        start.gradleUserHomeDir == new File(projectDir, "otherDir")
+    }
+
+    def "can overwrite searchUpwards via build arguments"() {
+        given:
+        params.getArguments(_) >> ['-u']
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        !start.searchUpwards
+    }
+
+    def "searchUpwards configured directly on the action wins over the command line setting"() {
+        given:
+        params.getArguments(_) >> ['-u']
+        params.isSearchUpwards() >> true
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        start.searchUpwards
+    }
+
+    def "the start parameter is configured from properties"() {
+        when:
+        def properties = [
+                (GradleProperties.CONFIGURE_ON_DEMAND_PROPERTY): "true",
+        ]
+
+        def start = new ProviderStartParameterConverter().toStartParameter(params, properties)
+
+        then:
+        start.configureOnDemand
+    }
+
+    abstract class LaunchableExecutionRequest implements InternalLaunchable, TaskExecutionRequest {}
+
+    def "accepts launchables from consumer"() {
+        given:
+        def selector = Mock(LaunchableExecutionRequest)
+        _ * selector.args >> ['myTask']
+        _ * selector.projectPath >> ':child'
+
+        params.getLaunchables(_) >> [selector]
+
+        when:
+        def start = new ProviderStartParameterConverter().toStartParameter(params, [:])
+
+        then:
+        start.taskRequests.size() == 1
+        start.taskRequests[0].projectPath == ':child'
+        start.taskRequests[0].args == ['myTask']
+    }
+}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ToolingGlobalScopeServicesTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ToolingGlobalScopeServicesTest.groovy
deleted file mode 100644
index e9f8894..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/ToolingGlobalScopeServicesTest.groovy
+++ /dev/null
@@ -1,29 +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.tooling.internal.provider
-
-import org.gradle.internal.service.DefaultServiceRegistry
-import spock.lang.Specification
-
-class ToolingGlobalScopeServicesTest extends Specification {
-    def services = DefaultServiceRegistry.create(new ToolingGlobalScopeServices())
-
-    def "provides a PayloadSerializer"() {
-        expect:
-        services.get(PayloadSerializer) instanceof PayloadSerializer
-    }
-}
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/AdaptedOperationParametersTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/AdaptedOperationParametersTest.groovy
deleted file mode 100644
index aa9da02..0000000
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/AdaptedOperationParametersTest.groovy
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.provider.connection
-
-import org.gradle.api.logging.LogLevel
-import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1
-import spock.lang.Specification
-
-class AdaptedOperationParametersTest extends Specification {
-
-    interface BuildOperationParametersStub extends BuildOperationParametersVersion1 {
-        List<String> getArguments()
-    } 
-    
-    def delegate = Mock(BuildOperationParametersStub)
-    def params = new AdaptedOperationParameters(delegate)
-
-    def "configures build log level to debug if verbose logging requested"() {
-        given:
-        delegate.getVerboseLogging() >> true
-
-        when:
-        def level = params.getBuildLogLevel()
-
-        then:
-        level == LogLevel.DEBUG
-    }
-
-    def "uses log level from the arguments if verbose logging not configured"() {
-        given:
-        delegate.getArguments() >> ['--info']
-        delegate.getVerboseLogging() >> false
-
-        when:
-        def level = params.getBuildLogLevel()
-
-        then:
-        level == LogLevel.INFO
-    }
-
-    def "uses lifecycle log level if verbose logging not configured"() {
-        given:
-        delegate.getArguments() >> []
-        delegate.getVerboseLogging() >> false
-
-        when:
-        def level = params.getBuildLogLevel()
-
-        then:
-        //depends on implementation of the CommandLineConverter and the global default
-        //but if feels important to validate it
-        level == LogLevel.LIFECYCLE
-    }
-}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/AbstractDaemonFixture.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/AbstractDaemonFixture.groovy
new file mode 100644
index 0000000..948c23c
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/AbstractDaemonFixture.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.launcher.daemon.context.DaemonContext
+import org.gradle.process.internal.ExecHandleBuilder
+
+abstract class AbstractDaemonFixture implements DaemonFixture {
+    public static final int STATE_CHANGE_TIMEOUT = 20000
+    final DaemonContext context
+
+    AbstractDaemonFixture(File daemonLog) {
+        this.context = DaemonContextParser.parseFrom(daemonLog.text)
+        if (this.context.pid == null) {
+            println "PID in daemon log ($daemonLog.absolutePath) is null."
+            println "daemon.log exists: ${daemonLog.exists()}"
+
+            println "start daemon.log content: "
+            println "{daemonLog.text.isEmpty()}) = ${daemonLog.text.isEmpty()})"
+            println daemonLog.text;
+            println "end daemon.log content"
+
+        }
+    }
+
+    void becomesIdle() {
+        waitForState(State.idle)
+    }
+
+    void stops() {
+        waitForState(State.stopped)
+    }
+
+    @Override
+    void assertIdle() {
+        assertHasState(State.idle)
+    }
+
+    @Override
+    void assertBusy() {
+        assertHasState(State.busy)
+    }
+
+    @Override
+    void assertStopped() {
+        assertHasState(State.stopped)
+    }
+
+    protected abstract void waitForState(State state)
+
+    protected abstract void assertHasState(State state)
+
+    /**
+     * Forcefully kills this daemon.
+     */
+    void kill() {
+        println "Killing daemon with pid: $context.pid"
+        def output = new ByteArrayOutputStream()
+        def e = new ExecHandleBuilder()
+                .commandLine(killArgs(context.pid))
+                .redirectErrorStream()
+                .setStandardOutput(output)
+                .workingDir(new File(".").absoluteFile) //does not matter
+                .build()
+        e.start()
+        def result = e.waitForFinish()
+        result.rethrowFailure()
+    }
+
+    private static Object[] killArgs(Long pid) {
+        if (pid == null) {
+            throw new RuntimeException("Unable to force kill the daemon because provided pid is null!")
+        }
+        if (OperatingSystem.current().unix) {
+            return ["kill", "-9", pid]
+        } else if (OperatingSystem.current().windows) {
+            return ["taskkill.exe", "/F", "/T", "/PID", pid]
+        } else {
+            throw new RuntimeException("This implementation does not know how to forcefully kill the daemon on os: " + OperatingSystem.current())
+        }
+    }
+
+    @SuppressWarnings("FieldName")
+    enum State {
+        busy, idle, stopped
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java
new file mode 100644
index 0000000..b86d763
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonContextParser.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.gradle.launcher.daemon.context.DaemonContext;
+import org.gradle.launcher.daemon.context.DefaultDaemonContext;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DaemonContextParser {
+    public static DaemonContext parseFromFile(File file) {
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(file));
+            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+                DaemonContext context = parseFrom(line);
+                if (context != null) {
+                    return context;
+                }
+            }
+        } catch(FileNotFoundException e) {
+            throw new IllegalStateException("unable to parse DefaultDaemonContext from source: [" + file.getAbsolutePath() + "].", e);
+        } catch (IOException e) {
+            throw new IllegalStateException("unable to parse DefaultDaemonContext from source: [" + file.getAbsolutePath() + "].", e);
+        }
+        throw new IllegalStateException("unable to parse DefaultDaemonContext from source: [" + file.getAbsolutePath() + "].");
+    }
+
+    public static DaemonContext parseFromString(String source) {
+        DaemonContext context = parseFrom(source);
+        if (context == null) {
+            throw new IllegalStateException("unable to parse DefaultDaemonContext from source: [" + source + "].");
+        }
+        return context;
+    }
+
+    private static DaemonContext parseFrom(String source) {
+        Pattern pattern = Pattern.compile("^.*DefaultDaemonContext\\[(uid=[^\\n,]+)?,?javaHome=([^\\n]+),daemonRegistryDir=([^\\n]+),pid=([^\\n]+),idleTimeout=(.+?),daemonOpts=([^\\n]+)].*",
+                Pattern.MULTILINE + Pattern.DOTALL);
+        Matcher matcher = pattern.matcher(source);
+
+        if (matcher.matches()) {
+            String uid = matcher.group(1) == null ? null : matcher.group(1).substring("uid=".length());
+            String javaHome = matcher.group(2);
+            String daemonRegistryDir = matcher.group(3);
+            String pidStr = matcher.group(4);
+            Long pid = pidStr.equals("null") ? null : Long.parseLong(pidStr);
+            Integer idleTimeout = Integer.decode(matcher.group(5));
+            List<String> jvmOpts = Lists.newArrayList(Splitter.on(',').split(matcher.group(6)));
+            return new DefaultDaemonContext(uid, new File(javaHome), new File(daemonRegistryDir), pid, idleTimeout, jvmOpts);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonFixture.java b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonFixture.java
new file mode 100644
index 0000000..c2f2c4d
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonFixture.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing;
+
+public interface DaemonFixture {
+    /**
+     * Returns the TCP port used by this daemon.
+     */
+    int getPort();
+
+    /**
+     * Forcefully kills this daemon.
+     */
+    void kill();
+
+    /**
+     * Asserts that this daemon becomes idle within a short timeout. Blocks until this has happened.
+     */
+    void becomesIdle();
+
+    /**
+     * Asserts that this daemon stops and is no longer visible to any clients within a short timeout. Blocks until this has happened.
+     */
+    void stops();
+
+    /**
+     * Asserts that this daemon is currently idle.
+     */
+    void assertIdle();
+
+    /**
+     * Asserts that this daemon is currently busy.
+     */
+    void assertBusy();
+
+    /**
+     * Asserts that this daemon has stopped and is no longer visible to any clients.
+     */
+    void assertStopped();
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogFileStateProbe.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogFileStateProbe.groovy
new file mode 100644
index 0000000..757c367
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogFileStateProbe.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing
+
+import org.gradle.launcher.daemon.context.DaemonContext
+import org.gradle.launcher.daemon.logging.DaemonMessages
+import org.gradle.launcher.daemon.testing.AbstractDaemonFixture.State
+
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+class DaemonLogFileStateProbe implements DaemonStateProbe {
+    private final DaemonContext context
+    private final File log
+    private final String startBuildMessage
+    private final String finishBuildMessage
+
+    DaemonLogFileStateProbe(File daemonLog, DaemonContext context, String startBuildMessage = DaemonMessages.STARTED_BUILD, String finishBuildMessage = DaemonMessages.FINISHED_BUILD) {
+        this.finishBuildMessage = finishBuildMessage
+        this.startBuildMessage = startBuildMessage
+        this.log = daemonLog
+        this.context = context
+    }
+
+    @Override
+    String toString() {
+        return "DaemonLogFile{file: ${log}, context: ${context}}"
+    }
+
+    DaemonContext getContext() {
+        return context
+    }
+
+    State getCurrentState() {
+        getStates().last()
+    }
+
+    List<State> getStates() {
+        def states = new LinkedList<State>()
+        states << State.idle
+        log.eachLine {
+            if (it.contains(startBuildMessage)) {
+                states << State.busy
+            } else if (it.contains(finishBuildMessage)) {
+                states << State.idle
+            } else if (it.contains(DaemonMessages.DAEMON_VM_SHUTTING_DOWN)) {
+                states << State.stopped
+            }
+        }
+        states
+    }
+
+    String getLog() {
+        return log.text
+    }
+
+    int getPort() {
+        Pattern pattern = Pattern.compile("^.*" + DaemonMessages.ADVERTISING_DAEMON + ".*port:(\\d+).*",
+                Pattern.MULTILINE + Pattern.DOTALL);
+
+        Matcher matcher = pattern.matcher(log.text);
+        assert matcher.matches(): "Unable to find daemon address in the daemon log. Daemon: $context"
+
+        try {
+            return Integer.parseInt(matcher.group(1))
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("Unexpected format of the port number found in the daemon log. Daemon: $context")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy
new file mode 100644
index 0000000..5b163cd
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonLogsAnalyzer.groovy
@@ -0,0 +1,81 @@
+/*
+ * 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.launcher.daemon.testing
+
+import org.gradle.internal.service.ServiceRegistryBuilder
+import org.gradle.internal.service.scopes.GlobalScopeServices
+import org.gradle.launcher.daemon.client.DaemonClientGlobalServices
+import org.gradle.launcher.daemon.registry.DaemonRegistry
+import org.gradle.launcher.daemon.registry.DaemonRegistryServices
+import org.gradle.logging.LoggingServiceRegistry
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.GradleVersion
+
+class DaemonLogsAnalyzer implements DaemonsFixture {
+    private final File daemonLogsDir
+    private final File daemonBaseDir
+    private final DaemonRegistry registry
+    private final String version
+
+    DaemonLogsAnalyzer(File daemonBaseDir, String version = GradleVersion.current().version) {
+        this.version = version
+        this.daemonBaseDir = daemonBaseDir
+        daemonLogsDir = new File(daemonBaseDir, version)
+        def services = ServiceRegistryBuilder.builder()
+                .parent(LoggingServiceRegistry.newEmbeddableLogging())
+                .parent(NativeServicesTestFixture.getInstance())
+                .provider(new GlobalScopeServices(false))
+                .provider(new DaemonClientGlobalServices())
+                .provider(new DaemonRegistryServices(daemonBaseDir))
+                .build()
+        registry = services.get(DaemonRegistry)
+    }
+
+    static DaemonsFixture newAnalyzer(File daemonBaseDir, String version = GradleVersion.current().version) {
+        return new DaemonLogsAnalyzer(daemonBaseDir, version)
+    }
+
+    DaemonRegistry getRegistry() {
+        return registry
+    }
+
+    void killAll() {
+        daemons*.kill()
+    }
+
+    List<DaemonFixture> getDaemons() {
+        assert daemonLogsDir.isDirectory()
+        return daemonLogsDir.listFiles().findAll { it.name.endsWith('.log') }.collect { daemonForLogFile(it) }
+    }
+
+    List<DaemonFixture> getVisible() {
+        return registry.all.collect { daemonForLogFile(new File(daemonLogsDir, "daemon-${it.pid}.out.log")) }
+    }
+
+    DaemonFixture daemonForLogFile(File logFile) {
+        if (version == GradleVersion.current().version) {
+            return new TestableDaemon(logFile, registry)
+        }
+        return new LegacyDaemon(logFile, version)
+    }
+
+    DaemonFixture getDaemon() {
+        def daemons = getDaemons()
+        assert daemons.size() == 1
+        daemons[0]
+    }
+}
\ No newline at end of file
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonRegistryStateProbe.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonRegistryStateProbe.groovy
new file mode 100644
index 0000000..9c6acda
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonRegistryStateProbe.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing
+
+import org.gradle.launcher.daemon.context.DaemonContext
+import org.gradle.launcher.daemon.registry.DaemonRegistry
+import org.gradle.launcher.daemon.testing.AbstractDaemonFixture.State
+
+class DaemonRegistryStateProbe implements DaemonStateProbe {
+    private final DaemonRegistry registry
+    private final DaemonContext context
+
+    DaemonRegistryStateProbe(DaemonRegistry registry, DaemonContext context) {
+        this.context = context
+        this.registry = registry
+    }
+
+    @Override
+    State getCurrentState() {
+        def daemonInfo = registry.all.find { it.context.pid == context.pid }
+        if (daemonInfo == null) {
+            return State.stopped
+        }
+        return daemonInfo.idle ? State.idle : State.busy
+    }
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonStateProbe.java b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonStateProbe.java
new file mode 100644
index 0000000..f84957f
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonStateProbe.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing;
+
+public interface DaemonStateProbe {
+    TestableDaemon.State getCurrentState();
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonsFixture.java b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonsFixture.java
new file mode 100644
index 0000000..be9a90a
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/DaemonsFixture.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing;
+
+import java.util.List;
+
+public interface DaemonsFixture {
+    /**
+     * Kills all daemons.
+     */
+    void killAll();
+
+    /**
+     * Returns all known daemons. Includes any daemons that are no longer running.
+     */
+    List<? extends DaemonFixture> getDaemons();
+
+    /**
+     * Returns all daemons that are visible to clients. May include daemons that are no longer running (eg they have crashed).
+     */
+    List<? extends DaemonFixture> getVisible();
+
+    /**
+     * Convenience to get a single daemon. Fails if there is not exactly 1 daemon.
+     */
+    DaemonFixture getDaemon();
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/LegacyDaemon.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/LegacyDaemon.groovy
new file mode 100644
index 0000000..d8d3bbc
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/LegacyDaemon.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.launcher.daemon.testing
+
+import org.gradle.launcher.daemon.testing.AbstractDaemonFixture.State
+import org.gradle.util.GradleVersion
+
+class LegacyDaemon extends AbstractDaemonFixture {
+    private final DaemonLogFileStateProbe logFileProbe
+
+    LegacyDaemon(File daemonLog, String version) {
+        super(daemonLog)
+        if (GradleVersion.version(version).baseVersion >= GradleVersion.version("2.2")) {
+            logFileProbe = new DaemonLogFileStateProbe(daemonLog, context)
+        } else {
+            logFileProbe = new DaemonLogFileStateProbe(daemonLog, context, "Daemon is busy, sleeping until state changes", "Daemon is idle, sleeping until state change")
+        }
+    }
+
+    protected void waitForState(State state) {
+        def expiry = System.currentTimeMillis() + STATE_CHANGE_TIMEOUT
+        def lastLogState = logFileProbe.currentState
+        while (expiry > System.currentTimeMillis() && lastLogState != state) {
+            Thread.sleep(200)
+            lastLogState = logFileProbe.currentState
+        }
+        if (lastLogState == state) {
+            return
+        }
+        throw new AssertionError("""Timeout waiting for daemon with pid ${context.pid} to reach state ${state}.
+Current state is ${lastLogState}.""")
+    }
+
+    @Override
+    protected void assertHasState(State state) {
+        assert logFileProbe.currentState == state
+    }
+
+    @Override
+    int getPort() {
+        throw new UnsupportedOperationException()
+    }
+}
diff --git a/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy
new file mode 100644
index 0000000..164e589
--- /dev/null
+++ b/subprojects/launcher/src/testFixtures/groovy/org/gradle/launcher/daemon/testing/TestableDaemon.groovy
@@ -0,0 +1,61 @@
+/*
+ * 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.launcher.daemon.testing
+
+import org.gradle.launcher.daemon.registry.DaemonRegistry
+import org.gradle.launcher.daemon.testing.AbstractDaemonFixture.State
+
+class TestableDaemon extends AbstractDaemonFixture {
+    private final DaemonLogFileStateProbe logFileProbe
+    private final DaemonRegistryStateProbe registryProbe
+
+    TestableDaemon(File daemonLog, DaemonRegistry registry) {
+        super(daemonLog)
+        this.logFileProbe = new DaemonLogFileStateProbe(daemonLog, context)
+        this.registryProbe = new DaemonRegistryStateProbe(registry, context)
+    }
+
+    protected void waitForState(State state) {
+        def expiry = System.currentTimeMillis() + STATE_CHANGE_TIMEOUT
+        def lastRegistryState = registryProbe.currentState
+        def lastLogState = logFileProbe.currentState
+        while (expiry > System.currentTimeMillis() && (lastRegistryState != state || lastLogState != state)) {
+            Thread.sleep(200)
+            lastRegistryState = registryProbe.currentState
+            lastLogState = logFileProbe.currentState
+        }
+        if (lastRegistryState == state && lastLogState == state) {
+            return
+        }
+        throw new AssertionError("""Timeout waiting for daemon with pid ${context.pid} to reach state ${state}.
+Current registry state is ${lastRegistryState} and current log state is ${lastLogState}.""")
+    }
+
+    @Override
+    protected void assertHasState(State state) {
+        assert logFileProbe.currentState == state
+        assert registryProbe.currentState == state
+    }
+
+    String getLog() {
+        return logFileProbe.log
+    }
+
+    int getPort() {
+        return logFileProbe.port
+    }
+}
\ No newline at end of file
diff --git a/subprojects/maven/maven.gradle b/subprojects/maven/maven.gradle
index fbe3fec..3132f63 100644
--- a/subprojects/maven/maven.gradle
+++ b/subprojects/maven/maven.gradle
@@ -18,12 +18,12 @@ dependencies {
     compile libraries.groovy
 
     compile project(':core')
-    compile project(':coreImpl')
+    compile project(':dependencyManagement')
     compile project(':plugins')
     compile project(':publish')
     compile libraries.slf4j_api
 
-    compile libraries.maven_ant_tasks
+    compile libraries.maven_publish
     compile "org.sonatype.pmaven:pmaven-common:0.8-20100325 at jar"
     compile "org.sonatype.pmaven:pmaven-groovy:0.8-20100325 at jar"
     compile "org.codehaus.plexus:plexus-component-annotations:1.5.2 at jar"
@@ -31,6 +31,11 @@ dependencies {
     testCompile libraries.xmlunit
 
     integTestCompile project(":ear")
+    integTestRuntime project(":resourcesS3")
+    integTestRuntime project(":resourcesSftp")
+
+    testFixturesCompile project(":internalIntegTesting")
 }
 
 useTestFixtures()
+useTestFixtures(project: ":modelCore")
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/AbstractMavenPublishIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/AbstractMavenPublishIntegTest.groovy
deleted file mode 100644
index 16c3321..0000000
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/AbstractMavenPublishIntegTest.groovy
+++ /dev/null
@@ -1,98 +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.publish.maven
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.maven.MavenFileModule
-
-class AbstractMavenPublishIntegTest extends AbstractIntegrationSpec {
-
-    protected def resolveArtifact(MavenFileModule module, def extension, def classifier) {
-        doResolveArtifacts("""
-    dependencies {
-        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}', classifier: '${sq(classifier)}', ext: '${sq(extension)}'
-    }
-""")
-    }
-
-    protected def resolveArtifacts(MavenFileModule module) {
-        doResolveArtifacts("""
-    dependencies {
-        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}'
-    }
-""")
-    }
-
-    protected def resolveArtifacts(MavenFileModule module, Map... additionalArtifacts) {
-        def dependencies = """
-    dependencies {
-        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}'
-        resolve(group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}') {
-"""
-        additionalArtifacts.each {
-            // TODO:DAZ Docs say type defaults to 'jar', but seems it must be set explicitly
-            def type = it.type == null ? 'jar' : it.type
-            dependencies += """
-            artifact {
-                name = '${sq(module.artifactId)}' // TODO:DAZ Get NPE if name isn't set
-                classifier = '${it.classifier}'
-                type = '${type}'
-            }
-"""
-        }
-        dependencies += """
-        }
-    }
-"""
-        doResolveArtifacts(dependencies)
-    }
-
-    protected def doResolveArtifacts(def dependencies) {
-        // Replace the existing buildfile with one for resolving the published module
-        // TODO:DAZ Use a separate directory for resolving
-        settingsFile.text = "rootProject.name = 'resolve'"
-        buildFile.text = """
-            configurations {
-                resolve
-            }
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-                mavenCentral()
-            }
-            $dependencies
-            task resolveArtifacts(type: Sync) {
-                from configurations.resolve
-                into "artifacts"
-            }
-
-"""
-
-        // TODO:DAZ Remove this requirement (by always publishing a jar/war/ear in tests?: Maven doesn't really support other file types as main artifact)
-        executer.withDeprecationChecksDisabled()
-        run "resolveArtifacts"
-        def artifactsList = file("artifacts").exists() ? file("artifacts").list() : []
-        return artifactsList.sort()
-    }
-
-
-    String sq(String input) {
-        return escapeForSingleQuoting(input)
-    }
-
-    String escapeForSingleQuoting(String input) {
-        return input.replace('\\', '\\\\').replace('\'', '\\\'')
-    }
-}
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublicationVersionRangeIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublicationVersionRangeIntegTest.groovy
new file mode 100644
index 0000000..cda45aa
--- /dev/null
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublicationVersionRangeIntegTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven
+
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
+class MavenPublicationVersionRangeIntegTest extends AbstractMavenPublishIntegTest {
+    def mavenModule = mavenRepo.module("org.gradle.test", "publishTest", "1.9")
+
+    public void "version range is mapped to maven syntax in published pom file"() {
+        given:
+
+        mavenRepo.module('group', 'projectA', '1.0').publish()
+        mavenRepo.module('group', 'projectA', '2.0').publish()
+        mavenRepo.module('group', 'projectB', '1.1.1').publish()
+
+        settingsFile << "rootProject.name = 'publishTest' "
+        buildFile << """
+            apply plugin: 'maven-publish'
+            apply plugin: 'java'
+
+            group = 'org.gradle.test'
+            version = '1.9'
+
+            publishing {
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+
+            dependencies {
+                compile "group:projectA:latest.release"
+                compile "group:projectB:latest.integration"
+            }"""
+
+        when:
+        run "publish"
+
+        then:
+        mavenModule.assertPublishedAsJavaModule()
+
+        mavenModule.parsedPom.scopes.keySet() == ["runtime"] as Set
+        mavenModule.parsedPom.scopes.runtime.assertDependsOn("group:projectA:RELEASE", "group:projectB:LATEST")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
index 9bad371..9ef0c89 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
 class MavenPublishArtifactCustomizationIntegTest extends AbstractMavenPublishIntegTest {
 
     def "can attach custom artifacts"() {
@@ -194,7 +196,7 @@ class MavenPublishArtifactCustomizationIntegTest extends AbstractMavenPublishInt
         module.assertPublished()
         module.assertArtifactsPublished("projectText-1.0.pom", "projectText-1.0.jar", "projectText-1.0-classified")
 
-        // TODO:DAZ Find a way to resolve Maven artifact with no extension
+        // TODO Find a way to resolve Maven artifact with no extension
 //        and:
 //        resolveArtifact(module, '', 'classified') == ["projectText-1.0-classifier"]
     }
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishBasicIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishBasicIntegTest.groovy
index 85a36b9..299e070 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishBasicIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishBasicIntegTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.test.fixtures.maven.M2Installation
 import org.gradle.test.fixtures.maven.MavenLocalRepository
 import org.gradle.util.SetSystemProperties
@@ -83,6 +84,9 @@ class MavenPublishBasicIntegTest extends AbstractMavenPublishIntegTest {
         def module = mavenRepo.module('org.gradle.test', 'empty-project', '1.0')
         module.assertPublishedAsPomModule()
         module.parsedPom.scopes.isEmpty()
+
+        and:
+        resolveArtifacts(module) == []
     }
 
     def "can publish simple jar"() {
@@ -225,7 +229,7 @@ class MavenPublishBasicIntegTest extends AbstractMavenPublishIntegTest {
         fails 'publish'
 
         then:
-        failure.assertHasDescription("A problem occurred configuring root project 'bad-project'.")
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin\$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)")
         failure.assertHasCause("Maven publication 'maven' cannot include multiple components")
     }
 
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCoordinatesIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCoordinatesIntegTest.groovy
index 9ae28e6..89f913c 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCoordinatesIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCoordinatesIntegTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.test.fixtures.maven.M2Installation
 import org.gradle.test.fixtures.maven.MavenLocalRepository
 
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCrossVersionIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCrossVersionIntegrationTest.groovy
index 6c3cc88..2d7436a 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCrossVersionIntegrationTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishCrossVersionIntegrationTest.groovy
@@ -21,9 +21,12 @@ import org.gradle.test.fixtures.maven.MavenFileRepository
 
 @TargetVersions('0.9+')
 class MavenPublishCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
-
     final MavenFileRepository repo = new MavenFileRepository(file("maven-repo"))
 
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
     def "maven java publication generated by maven-publish plugin can be consumed by previous versions of Gradle"() {
         given:
         projectPublishedUsingMavenPublishPlugin('java')
@@ -85,7 +88,11 @@ configurations {
     lib
 }
 repositories {
-    mavenRepo(urls: ['${repo.uri}'])
+    if (repositories.metaClass.respondsTo(repositories, 'maven')) {
+        maven { url "${repo.uri}" }
+    } else {
+        mavenRepo urls: ["${repo.uri}"]
+    }
 }
 dependencies {
     lib 'org.gradle.crossversion:published:1.9'
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishDependenciesIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishDependenciesIntegTest.groovy
new file mode 100644
index 0000000..fa7870a
--- /dev/null
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishDependenciesIntegTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class MavenPublishDependenciesIntegTest extends AbstractIntegrationSpec {
+
+    public void "version range is mapped to maven syntax in published pom file"() {
+        given:
+        def repoModule = mavenRepo.module('group', 'root', '1.0')
+
+        and:
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+            apply plugin: 'maven-publish'
+            apply plugin: 'java'
+
+            group = 'group'
+            version = '1.0'
+
+            dependencies {
+                compile "group:projectA:latest.release"
+                runtime "group:projectB:latest.integration"
+            }
+
+            publishing {
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        repoModule.assertPublishedAsJavaModule()
+        repoModule.parsedPom.scopes.runtime.expectDependency('group:projectA:RELEASE')
+        repoModule.parsedPom.scopes.runtime.expectDependency('group:projectB:LATEST')
+    }
+
+    @Issue("GRADLE-3233")
+    @Unroll
+    def "publishes POM dependency with #versionType version for Gradle dependency with null version"() {
+        given:
+        def repoModule = mavenRepo.module('group', 'root', '1.0')
+
+        and:
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+            apply plugin: 'maven-publish'
+            apply plugin: 'java'
+
+            group = 'group'
+            version = '1.0'
+
+            dependencies {
+                compile $dependencyNotation
+            }
+
+            publishing {
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds 'publish'
+
+        then:
+        repoModule.assertPublishedAsJavaModule()
+        repoModule.parsedPom.scopes.runtime.assertDependsOn("group:projectA:")
+        def dependency = repoModule.parsedPom.scopes.runtime.dependencies.get("group:projectA:")
+        dependency.groupId == "group"
+        dependency.artifactId == "projectA"
+        dependency.version == ""
+
+        where:
+        versionType | dependencyNotation
+        "empty"     | "'group:projectA'"
+        "null"      | "group:'group', name:'projectA', version:null"
+    }
+}
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishEarIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishEarIntegTest.groovy
index 48ad12a..0b1ad62 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishEarIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishEarIntegTest.groovy
@@ -15,6 +15,8 @@
  */
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
 class MavenPublishEarIntegTest extends AbstractMavenPublishIntegTest {
     public void "can publish ear module"() {
         def earModule = mavenRepo.module("org.gradle.test", "publishEar", "1.9")
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
index 3ece02b..5b0c818 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
@@ -15,17 +15,15 @@
  */
 
 package org.gradle.api.publish.maven
-
 import org.gradle.api.internal.artifacts.repositories.DefaultPasswordCredentials
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.maven.MavenHttpModule
-import org.gradle.test.fixtures.maven.MavenHttpRepository
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.test.fixtures.server.http.HttpServer
-import org.hamcrest.Matchers
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.junit.Rule
 import spock.lang.Unroll
 
-class MavenPublishHttpIntegTest extends AbstractIntegrationSpec {
+class MavenPublishHttpIntegTest extends AbstractMavenPublishIntegTest {
 
     @Rule HttpServer server
 
@@ -161,7 +159,9 @@ class MavenPublishHttpIntegTest extends AbstractIntegrationSpec {
         then:
         failure.assertHasDescription('Execution failed for task \':publishMavenPublicationToMavenRepository\'.')
         failure.assertHasCause('Failed to publish publication \'maven\' to repository \'maven\'')
-        failure.assertThatCause(Matchers.containsString('Return code is: 401'))
+        failure.assertHasCause("Error deploying artifact 'org.gradle:publish:jar': Error deploying artifact: Could not write to resource 'org/gradle/publish/2/publish-2.jar'")
+        // Cause goes missing through the maven classes, but does end up logged to stderr
+        failure.error.contains("Could not PUT '${module.artifact.uri}'. Received status code 401 from server: Unauthorized")
 
         where:
         authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
@@ -179,7 +179,9 @@ class MavenPublishHttpIntegTest extends AbstractIntegrationSpec {
         then:
         failure.assertHasDescription('Execution failed for task \':publishMavenPublicationToMavenRepository\'.')
         failure.assertHasCause('Failed to publish publication \'maven\' to repository \'maven\'')
-        failure.assertThatCause(Matchers.containsString('Return code is: 401'))
+        failure.assertHasCause("Error deploying artifact 'org.gradle:publish:jar': Error deploying artifact: Could not write to resource 'org/gradle/publish/2/publish-2.jar'")
+        // Cause goes missing through the maven classes, but does end up logged to stderr
+        failure.error.contains("Could not PUT '${module.artifact.uri}'. Received status code 401 from server: Unauthorized")
 
         where:
         authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpsIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpsIntegTest.groovy
new file mode 100644
index 0000000..21c1c85
--- /dev/null
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpsIntegTest.groovy
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+import org.gradle.test.fixtures.keystore.TestKeyStore
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.junit.Rule
+
+class MavenPublishHttpsIntegTest extends AbstractMavenPublishIntegTest {
+    TestKeyStore keyStore
+
+    @Rule public final HttpServer server = new HttpServer()
+
+    MavenHttpRepository mavenRemoteRepo
+    MavenHttpModule module
+
+    def setup() {
+        keyStore = TestKeyStore.init(file("keystore"))
+        server.start()
+
+        mavenRemoteRepo = new MavenHttpRepository(server, "/repo", mavenRepo)
+        module = mavenRemoteRepo.module('org.gradle', 'publish', '2')
+    }
+
+    def "publish with server certificate"() {
+        given:
+        keyStore.enableSslWithServerCert(server)
+        initBuild()
+
+        when:
+        expectPublication()
+        keyStore.configureServerCert(executer)
+        succeeds 'publish'
+
+        then:
+        verifyPublications()
+    }
+
+    def "publish with server and client certificate"() {
+        given:
+        keyStore.enableSslWithServerAndClientCerts(server)
+        initBuild()
+
+        when:
+        expectPublication()
+        keyStore.configureServerAndClientCerts(executer)
+        succeeds 'publish'
+
+        then:
+        verifyPublications()
+    }
+
+    def "decent error message when client can't authenticate server"() {
+        keyStore.enableSslWithServerCert(server)
+        initBuild()
+
+        when:
+        keyStore.configureIncorrectServerCert(executer)
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        fails 'publish'
+
+        then:
+        failure.assertHasCause("Failed to publish publication 'maven' to repository 'maven'")
+        failure.assertHasCause("Error deploying artifact 'org.gradle:publish:jar': Error deploying artifact: Could not write to resource 'org/gradle/publish/2/publish-2.jar'")
+        // TODO:DAZ Get this exception into the cause
+        failure.error.contains("peer not authenticated")
+    }
+
+    def "decent error message when server can't authenticate client"() {
+        keyStore.enableSslWithServerAndBadClientCert(server)
+        initBuild()
+
+        when:
+        executer.withStackTraceChecksDisabled() // Jetty logs stuff to console
+        keyStore.configureServerAndClientCerts(executer)
+
+        fails 'publish'
+
+        then:
+        failure.assertHasCause("Failed to publish publication 'maven' to repository 'maven'")
+        failure.assertHasCause("Error deploying artifact 'org.gradle:publish:jar': Error deploying artifact: Could not write to resource 'org/gradle/publish/2/publish-2.jar'")
+        // TODO:DAZ Get this exception into the cause
+        failure.error.contains("peer not authenticated")
+    }
+
+    def expectPublication() {
+        module.artifact.expectPut()
+        module.artifact.sha1.expectPut()
+        module.artifact.md5.expectPut()
+        module.rootMetaData.expectGetMissing()
+        module.rootMetaData.expectPut()
+        module.rootMetaData.sha1.expectPut()
+        module.rootMetaData.md5.expectPut()
+        module.pom.expectPut()
+        module.pom.sha1.expectPut()
+        module.pom.md5.expectPut()
+    }
+
+    def verifyPublications() {
+        def localPom = file("build/publications/maven/pom-default.xml").assertIsFile()
+        def localArtifact = file("build/libs/publish-2.jar").assertIsFile()
+
+        module.pomFile.assertIsCopyOf(localPom)
+        module.pom.verifyChecksums()
+        module.artifactFile.assertIsCopyOf(localArtifact)
+        module.artifact.verifyChecksums()
+
+        module.rootMetaData.verifyChecksums()
+        assert module.rootMetaData.versions == ["2"]
+        true
+    }
+
+    def initBuild() {
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'maven-publish'
+            group = 'org.gradle'
+            version = '2'
+
+            publishing {
+                repositories {
+                    maven {
+                        url '${mavenRemoteRepo.uri}'
+                    }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+    }
+
+}
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIdentifierValidationIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIdentifierValidationIntegTest.groovy
index 4fcca2f..ece45e7 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIdentifierValidationIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIdentifierValidationIntegTest.groovy
@@ -15,6 +15,8 @@
  */
 
 package org.gradle.api.publish.maven
+
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.test.fixtures.encoding.Identifier
 import spock.lang.Unroll
 
@@ -25,7 +27,7 @@ class MavenPublishIdentifierValidationIntegTest extends AbstractMavenPublishInte
     def artifactId = 'valid_artifact.name'
 
     @Unroll
-    def "can publish with version and description containing #title characters"() {
+    def "can publish with version and description containing #identifier characters"() {
         given:
         def version = identifier.safeForFileName().decorate("version")
         def description = identifier.decorate("description")
@@ -63,16 +65,11 @@ class MavenPublishIdentifierValidationIntegTest extends AbstractMavenPublishInte
         resolveArtifacts(module) == ["${artifactId}-${version}.jar"]
 
         where:
-        title        | identifier
-        "punctuation"| Identifier.punctuation
-        "non-ascii"  | Identifier.nonAscii
-        "whitespace" | Identifier.whiteSpace
-        "filesystem" | Identifier.fileSystemReserved
-        "xml markup" | Identifier.xmlMarkup
+        identifier << Identifier.all
     }
 
     @Unroll
-    def "can publish artifacts with version, extension and classifier containing #title characters"() {
+    def "can publish artifacts with version, extension and classifier containing #identifier characters"() {
         given:
         file("content-file") << "some content"
         def version = identifier.safeForFileName().decorate("version")
@@ -113,12 +110,7 @@ class MavenPublishIdentifierValidationIntegTest extends AbstractMavenPublishInte
         resolveArtifact(module, extension, classifier) == ["${artifactId}-${version}-${classifier}.${extension}"]
 
         where:
-        title        | identifier
-        "punctuation"| Identifier.punctuation
-        "non-ascii"  | Identifier.nonAscii
-        "whitespace" | Identifier.whiteSpace
-        "filesystem" | Identifier.fileSystemReserved
-        "xml markup" | Identifier.xmlMarkup
+        identifier << Identifier.all
     }
 
     def "fails with reasonable error message for invalid identifier value"() {
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIssuesIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIssuesIntegTest.groovy
index 2c25eae..f9cabe1 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIssuesIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishIssuesIntegTest.groovy
@@ -15,11 +15,13 @@
  */
 
 package org.gradle.api.publish.maven
+
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.maven.M2Installation
 import org.spockframework.util.TextUtil
 import spock.lang.Issue
+
 /**
  * Tests for bugfixes to maven publishing scenarios
  */
@@ -108,15 +110,15 @@ publishing {
         succeeds "publish"
     }
 
-   @Issue("GRADLE-2837")
-   def "project is properly configured when it is the target of a project dependency"() {
-       given:
-       mavenRepo.module("org.gradle", "dep", "1.1").publish()
+    @Issue("GRADLE-2837")
+    def "project is properly configured when it is the target of a project dependency"() {
+        given:
+        mavenRepo.module("org.gradle", "dep", "1.1").publish()
 
-       and:
-       settingsFile << "include ':main', ':util'"
+        and:
+        settingsFile << "include ':main', ':util'"
 
-       buildFile << """
+        buildFile << """
 subprojects {
     apply plugin: 'java'
     apply plugin: 'maven-publish'
@@ -137,13 +139,13 @@ subprojects {
     }
 }
 """
-       file("main", "build.gradle") << """
+        file("main", "build.gradle") << """
     dependencies {
         compile project(':util')
     }
 """
 
-       file("util", "build.gradle") << """
+        file("util", "build.gradle") << """
     dependencies {
         compile 'org.gradle:dep:1.1'
     }
@@ -159,4 +161,56 @@ subprojects {
         def utilPom = mavenRepo.module('my.org', 'util', '1.0').parsedPom
         utilPom.scopes.runtime.expectDependency('org.gradle:dep:1.1')
     }
+
+    @Issue("GRADLE-2945")
+    def "maven-publish plugin adds excludes to pom"() {
+
+        given:
+        mavenRepo.module("org.gradle", "pom-excludes", "0.1").publish()
+
+        and:
+        settingsFile << 'rootProject.name = "root"'
+        buildFile << """
+    apply plugin: "java"
+    apply plugin: "maven-publish"
+
+    group = "org.gradle"
+    version = "1.0"
+
+    repositories {
+        maven { url "${mavenRepo.uri}" }
+    }
+    dependencies {
+        compile ("org.gradle:pom-excludes:0.1"){
+           exclude group: "org.opensource1", module: "dep1"
+           exclude group: "org.opensource2"
+           exclude module: "dep2"
+        }
+    }
+    publishing {
+        repositories {
+            maven { url "${mavenRepo.uri}" }
+        }
+        publications {
+            pub(MavenPublication) {
+                from components.java
+            }
+        }
+    }
+    """
+
+        when:
+        succeeds 'publish'
+
+        then:
+        def mainPom = mavenRepo.module('org.gradle', 'root', '1.0').parsedPom
+        def dependency = mainPom.scopes.runtime.expectDependency('org.gradle:pom-excludes:0.1')
+        dependency.exclusions.size() == 3
+        dependency.exclusions[0].groupId == "org.opensource1"
+        dependency.exclusions[0].artifactId == "dep1"
+        dependency.exclusions[1].groupId == "org.opensource2"
+        dependency.exclusions[1].artifactId == "*"
+        dependency.exclusions[2].groupId == "*"
+        dependency.exclusions[2].artifactId == "dep2"
+    }
 }
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
index 14ea632..1f6f18a 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
 class MavenPublishJavaIntegTest extends AbstractMavenPublishIntegTest {
     def mavenModule = mavenRepo.module("org.gradle.test", "publishTest", "1.9")
 
@@ -99,7 +101,7 @@ $append
             dependencies {
                 compile "commons-collections:commons-collections:3.2.1"
                 runtime "commons-io:commons-io:1.4"
-                testCompile "junit:junit:4.11"
+                testCompile "junit:junit:4.12"
             }
 """
 
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishMultiProjectIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishMultiProjectIntegTest.groovy
index 10cfcc9..4841878 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishMultiProjectIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishMultiProjectIntegTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
 class MavenPublishMultiProjectIntegTest extends AbstractMavenPublishIntegTest {
     def project1 = mavenRepo.module("org.gradle.test", "project1", "1.0")
     def project2 = mavenRepo.module("org.gradle.test", "project2", "2.0")
@@ -83,7 +85,7 @@ project(":project3") {
         fails "publish"
 
         then:
-        failure.assertHasDescription "A problem occurred configuring project ':project1'."
+        failure.assertHasCause "Exception thrown while executing model rule: org.gradle.api.publish.plugins.PublishingPlugin\$Rules#publishing(org.gradle.api.plugins.ExtensionContainer)"
         failure.assertHasCause "Publishing is not yet able to resolve a dependency on a project with multiple different publications."
     }
 
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomCustomizationIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomCustomizationIntegTest.groovy
index 52ad0ca..617cb00 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomCustomizationIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomCustomizationIntegTest.groovy
@@ -48,7 +48,7 @@ class MavenPublishPomCustomizationIntegTest extends AbstractIntegrationSpec {
                             def dependency = asNode().appendNode('dependencies').appendNode('dependency')
                             dependency.appendNode('groupId', 'junit')
                             dependency.appendNode('artifactId', 'junit')
-                            dependency.appendNode('version', '4.11')
+                            dependency.appendNode('version', '4.12')
                             dependency.appendNode('scope', 'runtime')
                         }
                     }
@@ -63,7 +63,7 @@ class MavenPublishPomCustomizationIntegTest extends AbstractIntegrationSpec {
         module.assertPublished()
         module.parsedPom.description == 'custom-description'
         module.parsedPom.packaging == 'custom-packaging'
-        module.parsedPom.scopes.runtime.assertDependsOn("junit:junit:4.11")
+        module.parsedPom.scopes.runtime.assertDependsOn("junit:junit:4.12")
     }
 
     def "can generate pom file without publishing"() {
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomPackagingIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomPackagingIntegTest.groovy
new file mode 100644
index 0000000..79fcfbd
--- /dev/null
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishPomPackagingIntegTest.groovy
@@ -0,0 +1,179 @@
+/*
+ * 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.publish.maven
+
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+import spock.lang.Issue
+
+class MavenPublishPomPackagingIntegTest extends AbstractMavenPublishIntegTest {
+    def mavenModule = mavenRepo.module("org.gradle.test", "publishTest", "1.9")
+
+    def "uses 'pom' packaging when no artifact is unclassified"() {
+        given:
+        createBuildScripts """
+            artifact("content.txt") {
+                classifier "custom"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'pom'
+        mavenModule.assertArtifactsPublished("publishTest-1.9-custom.txt", "publishTest-1.9.pom")
+    }
+
+    def "uses 'pom' packaging where multiple artifacts are unclassified"() {
+        given:
+        createBuildScripts """
+            artifact("content.txt")
+            artifact("content.txt") {
+                extension "rtf"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'pom'
+        mavenModule.assertArtifactsPublished("publishTest-1.9.txt", "publishTest-1.9.rtf", "publishTest-1.9.pom")
+    }
+
+    def "uses extension of single unclassified artifact as pom packaging"() {
+        given:
+        createBuildScripts """
+            artifact("content.txt")
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'txt'
+        mavenModule.assertArtifactsPublished("publishTest-1.9.txt", "publishTest-1.9.pom")
+    }
+
+    @Issue("GRADLE-3211")
+    def "can specify packaging when no artifact is unclassified"() {
+        given:
+        createBuildScripts """
+            pom.packaging "foo"
+
+            artifact("content.txt") {
+                classifier "custom"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'foo'
+        mavenModule.assertArtifactsPublished("publishTest-1.9-custom.txt", "publishTest-1.9.pom")
+    }
+
+    @Issue("GRADLE-3211")
+    def "can set packaging to the extension of an unclassified artifact"() {
+        given:
+        createBuildScripts """
+            pom.packaging "txt"
+
+            artifact("content.txt") {
+                classifier "custom"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'txt'
+        mavenModule.assertArtifactsPublished("publishTest-1.9-custom.txt", "publishTest-1.9.pom")
+    }
+
+    @Issue("GRADLE-3211")
+    def "can override packaging with single unclassified artifact"() {
+        given:
+        createBuildScripts """
+            pom.packaging "foo"
+
+            artifact("content.txt") {
+                extension "txt"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'foo'
+         // Ideally, the '.foo' artifact would be '.txt' as specified
+        mavenModule.assertArtifactsPublished("publishTest-1.9.foo", "publishTest-1.9.pom")
+    }
+
+    def "can specify packaging with multiple unclassified artifacts"() {
+        given:
+        createBuildScripts """
+            pom.packaging "other"
+
+            artifact("content.txt")
+            artifact("content.txt") {
+                extension "other"
+            }
+"""
+
+        when:
+        succeeds "publish"
+
+        then:
+        mavenModule.assertPublished()
+        mavenModule.parsedPom.packaging == 'other'
+        mavenModule.assertArtifactsPublished("publishTest-1.9.txt", "publishTest-1.9.other", "publishTest-1.9.pom")
+    }
+
+    def createBuildScripts(artifacts) {
+        settingsFile << "rootProject.name = 'publishTest' "
+
+        buildFile << """
+            apply plugin: 'maven-publish'
+
+            group = 'org.gradle.test'
+            version = '1.9'
+
+            file("content.txt") << 'some content'
+
+            publishing {
+                repositories {
+                    maven { url "${mavenRepo.uri}" }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        $artifacts
+                    }
+                }
+            }
+"""
+    }
+}
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishWarProjectIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishWarProjectIntegTest.groovy
index ab13ab2..01dba60 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishWarProjectIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishWarProjectIntegTest.groovy
@@ -15,6 +15,8 @@
  */
 package org.gradle.api.publish.maven
 
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+
 class MavenPublishWarProjectIntegTest extends AbstractMavenPublishIntegTest {
     public void "publishes war and meta-data for web component with external dependencies"() {
         def webModule = mavenRepo.module("org.gradle.test", "project1", "1.9")
@@ -37,7 +39,7 @@ class MavenPublishWarProjectIntegTest extends AbstractMavenPublishIntegTest {
             dependencies {
                 compile "commons-collections:commons-collections:3.2.1"
                 runtime "commons-io:commons-io:1.4"
-                testRuntime "junit:junit:4.11"
+                testRuntime "junit:junit:4.12"
             }
 
             publishing {
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginIntegTest.groovy
index 7007690..cbadc93 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginIntegTest.groovy
@@ -21,11 +21,6 @@ import org.gradle.integtests.fixtures.WellBehavedPluginTest
 class MavenPublishPluginIntegTest extends WellBehavedPluginTest {
 
     @Override
-    String getPluginId() {
-        "maven-publish"
-    }
-
-    @Override
     String getMainTask() {
         "publish"
     }
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPomGenerationIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPomGenerationIntegrationTest.groovy
index 7715115..42c007a 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPomGenerationIntegrationTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPomGenerationIntegrationTest.groovy
@@ -17,8 +17,12 @@
 package org.gradle.integtests.publish.maven
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Unroll
+
 // this spec documents the status quo, not a desired behavior
 class MavenPomGenerationIntegrationTest extends AbstractIntegrationSpec {
+
+    @Unroll
     def "how configuration of archive task affects generated POM"() {
         buildFile << """
 apply plugin: "java"
@@ -60,6 +64,7 @@ uploadArchives {
         "myBaseName" | "2.3"      | "war"        | null          | "myBaseName"  | "1.9"      | "war"
     }
 
+    @Unroll
     def "how configuration of mavenDeployer.pom object affects generated POM"() {
         buildFile << """
 apply plugin: "java"
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIgnoresMavenSettingsTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIgnoresMavenSettingsTest.groovy
index 102b10d..30b0fc4 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIgnoresMavenSettingsTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIgnoresMavenSettingsTest.groovy
@@ -15,19 +15,16 @@
  */
 
 package org.gradle.integtests.publish.maven
-
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.junit.Rule
-import spock.lang.Ignore
 import spock.lang.Issue
 
 class MavenPublishIgnoresMavenSettingsTest extends AbstractIntegrationSpec {
     @Rule
     public final HttpServer server = new HttpServer()
 
-    @Ignore
     @Issue("GRADLE-2681")
     def "gradle ignores maven mirror configuration for uploading archives"() {
         given:
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIntegrationTest.groovy
index dbb5de0..95deb61 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIntegrationTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishIntegrationTest.groovy
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 package org.gradle.integtests.publish.maven
-
+import org.apache.commons.lang.RandomStringUtils
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.maven.M2Installation
-import org.gradle.test.fixtures.maven.MavenHttpRepository
 import org.gradle.test.fixtures.maven.MavenLocalRepository
 import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.gradle.util.GradleVersion
 import org.junit.Rule
 import org.spockframework.util.TextUtil
@@ -61,6 +61,36 @@ uploadArchives {
         module.assertArtifactsPublished('root-1.0.jar', 'root-1.0.pom')
     }
 
+    def "upload status is logged on on info level"() {
+        given:
+        def resourceFile = file("src/main/resources/testfile.properties")
+        resourceFile << RandomStringUtils.random(5000)
+        and:
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'maven'
+group = 'group'
+version = '1.0'
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "${mavenRepo.uri}")
+        }
+    }
+}
+"""
+        when:
+        executer.withArgument("-i")
+        succeeds 'uploadArchives'
+
+        then:
+        output.contains("Uploading: group/root/1.0/root-1.0.jar to repository remote at ${mavenRepo.uri.toString()[0..-2]}")
+        output.contains("Transferring 12K from remote")
+        output.contains("Uploaded 12K")
+    }
+
     @Issue("GRADLE-2456")
     public void generatesSHA1FileWithLeadingZeros() {
         given:
@@ -125,7 +155,8 @@ uploadArchives {
 
         then:
         def module = mavenRepo.module('group', 'root', 1.0)
-        module.assertArtifactsPublished('root-1.0-source.jar')
+        module.assertPublished()
+        module.assertArtifactsPublished('root-1.0.pom', 'root-1.0-source.jar')
     }
 
     def "can publish a project with metadata artifacts"() {
@@ -203,7 +234,7 @@ uploadArchives {
 
         then:
         def module = mavenRepo.module('org.gradle', 'test', '1.0-SNAPSHOT')
-        module.assertArtifactsPublished("test-${module.publishArtifactVersion}.jar", "test-${module.publishArtifactVersion}.pom")
+        module.assertArtifactsPublished("maven-metadata.xml", "test-${module.publishArtifactVersion}.jar", "test-${module.publishArtifactVersion}.pom")
     }
 
     def "can publish multiple deployments with attached artifacts"() {
@@ -385,4 +416,31 @@ uploadArchives {
         authScheme << [HttpServer.AuthScheme.BASIC, HttpServer.AuthScheme.DIGEST]
         // TODO: Does not work with DIGEST authentication
     }
+
+    @Issue('GRADLE-3272')
+    def "can publish to custom maven local repo defined with system property"() {
+        given:
+        def m2Installation = new M2Installation(testDirectory)
+        def localM2Repo = m2Installation.mavenRepo()
+        def customLocalRepo = mavenLocal("customMavenLocal")
+        executer.beforeExecute(m2Installation)
+
+        and:
+        settingsFile << "rootProject.name = 'root'"
+        buildFile << """
+            apply plugin: 'maven'
+            apply plugin: 'java'
+
+            group = 'group'
+            version = '1.0'
+        """
+
+        when:
+        args "-Dmaven.repo.local=${customLocalRepo.rootDir.getAbsolutePath()}"
+        succeeds 'install'
+
+        then:
+        !localM2Repo.module("group", "root", "1.0").artifactFile(type: "jar").exists()
+        customLocalRepo.module("group", "root", "1.0").assertPublishedAsJavaModule()
+    }
 }
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishVersionRangeIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishVersionRangeIntegrationTest.groovy
new file mode 100644
index 0000000..6068f9b
--- /dev/null
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/MavenPublishVersionRangeIntegrationTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.publish.maven
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class MavenPublishVersionRangeIntegrationTest extends AbstractIntegrationSpec {
+
+    public void "version range is mapped to maven syntax in published pom file"() {
+        given:
+        file("settings.gradle") << "rootProject.name = 'publishTest' "
+        and:
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'maven'
+
+group = 'org.gradle.test'
+version = '1.9'
+
+dependencies {
+    compile "group:projectA:latest.release"
+    runtime "group:projectB:latest.integration"
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "${mavenRepo.uri}")
+        }
+    }
+}
+"""
+
+        when:
+        run "uploadArchives"
+
+        then:
+        def mavenModule = mavenRepo.module("org.gradle.test", "publishTest", "1.9")
+        mavenModule.assertArtifactsPublished("publishTest-1.9.pom", "publishTest-1.9.jar")
+        mavenModule.parsedPom.scopes.compile.assertDependsOn("group:projectA:RELEASE")
+        mavenModule.parsedPom.scopes.runtime.assertDependsOn("group:projectB:LATEST")
+    }
+
+    @Issue("GRADLE-3233")
+    @Unroll
+    def "publishes POM dependency with #versionType version for Gradle dependency with null version"() {
+        given:
+        file("settings.gradle") << "rootProject.name = 'publishTest' "
+        and:
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'maven'
+
+group = 'org.gradle.test'
+version = '1.9'
+
+dependencies {
+    compile $dependencyNotation
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "${mavenRepo.uri}")
+        }
+    }
+}
+"""
+
+        when:
+        run "uploadArchives"
+
+        then:
+        def mavenModule = mavenRepo.module("org.gradle.test", "publishTest", "1.9")
+        mavenModule.parsedPom.scopes.compile.assertDependsOn("group:projectA:")
+        where:
+        versionType | dependencyNotation
+        "empty"     | "'group:projectA'"
+        "null"      | "group:'group', name:'projectA', version:null"
+    }
+}
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenPomGenerationIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenPomGenerationIntegrationTest.groovy
index 17612a4..1d5b6ab 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenPomGenerationIntegrationTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenPomGenerationIntegrationTest.groovy
@@ -57,7 +57,7 @@ class SamplesMavenPomGenerationIntegrationTest extends AbstractIntegrationTest {
 
     @Test
     void "can install to local repository"() {
-        def repo = maven(new TestFile("$SystemProperties.userHome/.m2/repository"))
+        def repo = maven(new TestFile("$SystemProperties.instance.userHome/.m2/repository"))
         def module = repo.module('installGroup', 'mywar', '1.0MVN')
         module.moduleDir.deleteDir()
 
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenQuickstartIntegrationTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenQuickstartIntegrationTest.groovy
index 5259bac..2ae5dfd 100755
--- a/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenQuickstartIntegrationTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/integtests/publish/maven/SamplesMavenQuickstartIntegrationTest.groovy
@@ -52,7 +52,7 @@ class SamplesMavenQuickstartIntegrationTest extends AbstractIntegrationTest {
 
     @Test
     void "can install to local repository"() {
-        def repo = maven(new TestFile("$SystemProperties.userHome/.m2/repository"))
+        def repo = maven(new TestFile("$SystemProperties.instance.userHome/.m2/repository"))
         def module = repo.module('gradle', 'quickstart', '1.0')
         module.moduleDir.deleteDir()
 
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Authentication.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Authentication.java
new file mode 100644
index 0000000..96b0d0f
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Authentication.java
@@ -0,0 +1,44 @@
+//CHECKSTYLE:OFF
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.maven.artifact.ant;
+
+import org.apache.maven.settings.Server;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+
+/**
+ * Ant Wrapper for wagon authentication.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: Authentication.java 524632 2007-04-01 17:05:24Z jvanzyl $
+ */
+public class Authentication
+    extends AuthenticationInfo
+{
+    public Authentication()
+    {
+        super();
+    }
+
+    public Authentication(Server server)
+    {
+        setUserName( server.getUsername() );
+        setPassword( server.getPassword() );
+        setPassphrase( server.getPassphrase() );
+        setPrivateKey( server.getPrivateKey() );
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Proxy.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Proxy.java
new file mode 100644
index 0000000..9ed1264
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Proxy.java
@@ -0,0 +1,45 @@
+//CHECKSTYLE:OFF
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.maven.artifact.ant;
+
+import org.apache.maven.wagon.proxy.ProxyInfo;
+
+/**
+ * Ant Wrapper for wagon proxy.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: Proxy.java 524632 2007-04-01 17:05:24Z jvanzyl $
+ */
+public class Proxy
+    extends ProxyInfo
+{
+    public Proxy()
+    {
+        super();
+    }
+
+    public Proxy(org.apache.maven.settings.Proxy proxy)
+    {
+        setHost( proxy.getHost() );
+        setPort( proxy.getPort() );
+        setNonProxyHosts( proxy.getNonProxyHosts() );
+        setUserName( proxy.getUsername() );
+        setPassword( proxy.getPassword() );
+        setType( proxy.getProtocol() );
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RemoteRepository.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RemoteRepository.java
new file mode 100644
index 0000000..e53d004
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RemoteRepository.java
@@ -0,0 +1,97 @@
+//CHECKSTYLE:OFF
+package org.apache.maven.artifact.ant;
+
+/*
+ * 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.
+ */
+
+/**
+ * Remote repository type.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: RemoteRepository.java 615614 2008-01-27 16:06:31Z hboutemy $
+ */
+public class RemoteRepository
+    extends Repository
+{
+    private String url;
+
+    private Authentication authentication;
+
+    private Proxy proxy;
+
+    private RepositoryPolicy snapshots;
+
+    private RepositoryPolicy releases;
+
+    public String getUrl()
+    {
+        return ( (RemoteRepository) getInstance() ).url;
+    }
+
+    public void setUrl( String url )
+    {
+        this.url = url;
+    }
+
+    public Authentication getAuthentication()
+    {
+        return ( (RemoteRepository) getInstance() ).authentication;
+    }
+
+    public void addAuthentication( Authentication authentication )
+    {
+        this.authentication = authentication;
+    }
+
+    public void addProxy( Proxy proxy )
+    {
+        this.proxy = proxy;
+    }
+
+    public Proxy getProxy()
+    {
+        return ( (RemoteRepository) getInstance() ).proxy;
+    }
+
+    public RepositoryPolicy getSnapshots()
+    {
+        return ( (RemoteRepository) getInstance() ).snapshots;
+    }
+
+    public void addSnapshots( RepositoryPolicy snapshots )
+    {
+        this.snapshots = snapshots;
+    }
+
+    public RepositoryPolicy getReleases()
+    {
+        return ( (RemoteRepository) getInstance() ).releases;
+    }
+
+    public void addReleases( RepositoryPolicy releases )
+    {
+        this.releases = releases;
+    }
+
+    protected String getDefaultId()
+    {
+        return getUrl();
+    }
+
+}
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Repository.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Repository.java
new file mode 100644
index 0000000..2f21c3c
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/Repository.java
@@ -0,0 +1,85 @@
+//CHECKSTYLE:OFF
+package org.apache.maven.artifact.ant;
+
+/*
+ * 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.
+ */
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Base class for a repository.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: Repository.java 586615 2007-10-19 21:04:45Z hboutemy $
+ */
+public abstract class Repository
+    extends ProjectComponent
+{
+    private String id;
+    
+    private String refid;
+
+    private String layout = "default";
+    
+    protected abstract String getDefaultId();
+
+    public String getId()
+    {
+        if ( getInstance().id == null )
+        {
+            getInstance().setId( getDefaultId() );
+        }
+        return getInstance().id;
+    }
+    
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+    
+    public String getRefid()
+    {
+        return refid;
+    }
+
+    public void setRefid( String refid )
+    {
+        this.refid = refid;
+    }
+
+    protected Repository getInstance()
+    {
+        Repository instance = this;
+        if ( refid != null )
+        {
+            instance = (Repository) getProject().getReference( refid );
+        }
+        return instance;
+    }
+
+    public String getLayout()
+    {
+        return getInstance().layout;
+    }
+
+    public void setLayout( String layout )
+    {
+        this.layout = layout;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RepositoryPolicy.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RepositoryPolicy.java
new file mode 100644
index 0000000..e4ff7cb
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/RepositoryPolicy.java
@@ -0,0 +1,69 @@
+//CHECKSTYLE:OFF
+package org.apache.maven.artifact.ant;
+
+/*
+ * 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.
+ */
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Base class for a repository policy.
+ *
+ * @author <a href="mailto:brett at apache.org">Brett Porter</a>
+ * @version $Id: RepositoryPolicy.java 649782 2008-04-19 09:42:38Z hboutemy $
+ */
+public class RepositoryPolicy
+    extends ProjectComponent
+{
+    private String updatePolicy;
+
+    private String checksumPolicy;
+
+    private boolean enabled = true;
+
+    public String getUpdatePolicy()
+    {
+        return updatePolicy;
+    }
+
+    public void setUpdatePolicy( String updatePolicy )
+    {
+        this.updatePolicy = updatePolicy;
+    }
+
+    public boolean isEnabled()
+    {
+        return enabled;
+    }
+
+    public void setEnabled( boolean enabled )
+    {
+        this.enabled = enabled;
+    }
+
+    public String getChecksumPolicy()
+    {
+        return checksumPolicy;
+    }
+
+    public void setChecksumPolicy( String checksumPolicy )
+    {
+        this.checksumPolicy = checksumPolicy;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/package-info.java b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/package-info.java
new file mode 100644
index 0000000..594f6a1
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/apache/maven/artifact/ant/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * NOTE: The sources in this package have been adopted unchanged from 'org.apache.maven:maven-ant-tasks:2.1.3'.
+ *
+ * These classes form part of the public Gradle API, via {@link org.gradle.api.artifacts.maven.MavenDeployer#getRepository()}.
+ * We no longer use the actual Maven Ant Tasks for publishing, and these classes are only available publicly in a fat-jar that
+ * bundles a version of Maven as well as the Ant tasks themselves.
+ * For this reason, these classes have been 'adopted' here so they can remain in the Gradle API without bloating the Gradle distribution.
+ */
+package org.apache.maven.artifact.ant;
\ No newline at end of file
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMappingContainer.java b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMappingContainer.java
index 8859781..e1cd1ec 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMappingContainer.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMappingContainer.java
@@ -48,7 +48,7 @@ public interface Conf2ScopeMappingContainer {
     /**
      * Returns a scope that corresponds to the given configurations. Dependencies of different configurations can
      * be equal. But only one of those equals dependencies (which might differ in content) can be mapped to a POM
-     * (due to the the nature of a Maven POM).
+     * (due to the nature of a Maven POM).
      *
      * <p>Which scope is returned depends on the existing mappings. See {@link #addMapping(int, Configuration, String)}. If
      * only one configuration is mapped, this mapping is used to choose the scope. If more than one configuration of a
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
index f9cdd38..fddcf03 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenDeployer.java
@@ -42,7 +42,7 @@ public interface MavenDeployer extends MavenResolver {
     Object getRepository();
 
     /**
-     * Sets the repository to be used for uploading artifacts. If {@link #getRepository()} is not set, this repository
+     * Sets the repository to be used for uploading artifacts. If {@link #getSnapshotRepository()} is not set, this repository
      * is also used for uploading snapshot artifacts.
      *
      * @param repository The repository to be used
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
index 2816660..52a3ca8 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
@@ -25,7 +25,7 @@ import org.gradle.api.artifacts.repositories.ArtifactRepository;
 public interface MavenResolver extends ArtifactRepository, PomFilterContainer {
     /**
      * Returns a Maven settings object. This can be used for example to figure out where the local repository is
-     * located. This property is filled after publishing. Before this property is null.
+     * located. This property is populated on demand.
      */
     Object getSettings();
 
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPlugin.java b/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPlugin.java
index eaa9fef..bca4eb6 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPlugin.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/plugins/MavenPlugin.java
@@ -33,11 +33,12 @@ import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublication;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.plugins.DslObject;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.publication.maven.internal.DefaultDeployerFactory;
-import org.gradle.api.publication.maven.internal.DefaultMavenFactory;
 import org.gradle.api.publication.maven.internal.DefaultMavenRepositoryHandlerConvention;
 import org.gradle.api.publication.maven.internal.MavenFactory;
 import org.gradle.api.tasks.Upload;
@@ -66,23 +67,28 @@ public class MavenPlugin implements Plugin<ProjectInternal> {
     private final FileResolver fileResolver;
     private final ProjectPublicationRegistry publicationRegistry;
     private final ProjectConfigurationActionContainer configurationActionContainer;
+    private final MavenSettingsProvider mavenSettingsProvider;
+    private final LocalMavenRepositoryLocator mavenRepositoryLocator;
 
     private Project project;
 
     @Inject
     public MavenPlugin(Factory<LoggingManagerInternal> loggingManagerFactory, FileResolver fileResolver,
-                       ProjectPublicationRegistry publicationRegistry, ProjectConfigurationActionContainer configurationActionContainer) {
+                       ProjectPublicationRegistry publicationRegistry, ProjectConfigurationActionContainer configurationActionContainer,
+                       MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
         this.loggingManagerFactory = loggingManagerFactory;
         this.fileResolver = fileResolver;
         this.publicationRegistry = publicationRegistry;
         this.configurationActionContainer = configurationActionContainer;
+        this.mavenSettingsProvider = mavenSettingsProvider;
+        this.mavenRepositoryLocator = mavenRepositoryLocator;
     }
 
     public void apply(final ProjectInternal project) {
         this.project = project;
-        project.getPlugins().apply(BasePlugin.class);
+        project.getPluginManager().apply(BasePlugin.class);
 
-        DefaultMavenFactory mavenFactory = new DefaultMavenFactory();
+        MavenFactory mavenFactory = project.getServices().get(MavenFactory.class);
         final MavenPluginConvention pluginConvention = addConventionObject(project, mavenFactory);
         final DefaultDeployerFactory deployerFactory = new DefaultDeployerFactory(
                 mavenFactory,
@@ -90,7 +96,9 @@ public class MavenPlugin implements Plugin<ProjectInternal> {
                 fileResolver,
                 pluginConvention,
                 project.getConfigurations(),
-                pluginConvention.getConf2ScopeMappings());
+                pluginConvention.getConf2ScopeMappings(),
+                mavenSettingsProvider,
+                mavenRepositoryLocator);
 
         configureUploadTasks(deployerFactory);
         configureUploadArchivesTask();
@@ -124,7 +132,9 @@ public class MavenPlugin implements Plugin<ProjectInternal> {
         configurationActionContainer.add(new Action<Project>() {
             public void execute(Project project) {
                 Upload uploadArchives = project.getTasks().withType(Upload.class).findByName(BasePlugin.UPLOAD_ARCHIVES_TASK_NAME);
-                if (uploadArchives == null) { return; }
+                if (uploadArchives == null) {
+                    return;
+                }
 
                 ConfigurationInternal configuration = (ConfigurationInternal) uploadArchives.getConfiguration();
                 ModuleInternal module = configuration.getModule();
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/CustomModelBuilder.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/CustomModelBuilder.java
deleted file mode 100644
index 395033a..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/CustomModelBuilder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal;
-
-import groovy.util.FactoryBuilderSupport;
-import org.apache.maven.model.Model;
-import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
-import org.gradle.api.internal.artifacts.PlexusLoggerAdapter;
-import org.slf4j.LoggerFactory;
-import org.sonatype.maven.polyglot.execute.ExecuteManager;
-import org.sonatype.maven.polyglot.execute.ExecuteManagerImpl;
-import org.sonatype.maven.polyglot.groovy.builder.ModelBuilder;
-
-import java.lang.reflect.Field;
-import java.util.Map;
-
-/**
- * This is a slightly modified version as shipped with polyglot Maven.
- */
-public class CustomModelBuilder extends ModelBuilder {
-
-    public CustomModelBuilder(Model model) {
-        ExecuteManager executeManager = new ExecuteManagerImpl();
-        setProp(executeManager.getClass(), executeManager, "log",
-                new PlexusLoggerAdapter(LoggerFactory.getLogger(ExecuteManagerImpl.class)));
-        setProp(ModelBuilder.class, this, "executeManager", executeManager);
-        setProp(ModelBuilder.class, this, "log",
-                new PlexusLoggerAdapter(LoggerFactory.getLogger(ModelBuilder.class)));
-        try {
-            initialize();
-        } catch (InitializationException e) {
-            throw new RuntimeException(e);
-        }
-        Map factories = (Map) getProp(FactoryBuilderSupport.class, this, "factories");
-        factories.remove("project");
-        ModelFactory modelFactory = new ModelFactory(model);
-        registerFactory(modelFactory.getName(), null, modelFactory);
-    }
-
-    public static void setProp(Class c, Object obj, String fieldName, Object value) {
-        try {
-            Field f = c.getDeclaredField(fieldName);
-            f.setAccessible(true); // solution
-            f.set(obj, value); // IllegalAccessException
-            // production code should handle these exceptions more gracefully
-        } catch (NoSuchFieldException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalArgumentException e) {
-           throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-           throw new RuntimeException(e);
-        }
-    }
-
-    public static Object getProp(Class c, Object obj, String fieldName) {
-        try {
-            Field f = c.getDeclaredField(fieldName);
-            f.setAccessible(true); // solution
-            return f.get(obj); // IllegalAccessException
-            // production code should handle these exceptions more gracefully
-        } catch (NoSuchFieldException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalArgumentException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainer.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainer.java
deleted file mode 100644
index 71db496..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainer.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.util.WrapUtil;
-
-import java.util.*;
-
-public class DefaultConf2ScopeMappingContainer implements Conf2ScopeMappingContainer {
-    private Map<Configuration, Conf2ScopeMapping> mappings = new HashMap<Configuration, Conf2ScopeMapping>();
-
-    private boolean skipUnmappedConfs = true;
-
-    public DefaultConf2ScopeMappingContainer() {
-    }
-
-    public DefaultConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings) {
-        this.mappings.putAll(mappings);
-    }
-
-    public Conf2ScopeMapping getMapping(Collection<Configuration> configurations) {
-        Set<Conf2ScopeMapping> result = getMappingsWithHighestPriority(configurations);
-        if (result.size() > 1) {
-            throw new InvalidUserDataException(
-                    "The configuration to scope mapping is not unique. The following configurations "
-                            + "have the same priority: " + result);
-        }
-        return result.size() == 0 ? null : result.iterator().next();
-    }
-
-    private Set<Conf2ScopeMapping> getMappingsWithHighestPriority(Collection<Configuration> configurations) {
-        Integer lastPriority = null;
-        Set<Conf2ScopeMapping> result = new HashSet<Conf2ScopeMapping>();
-        for (Conf2ScopeMapping conf2ScopeMapping : getMappingsForConfigurations(configurations)) {
-            Integer thisPriority = conf2ScopeMapping.getPriority();
-            if (lastPriority != null && lastPriority.equals(thisPriority)) {
-                result.add(conf2ScopeMapping);
-            } else if (lastPriority == null || (thisPriority != null && lastPriority < thisPriority)) {
-                lastPriority = thisPriority;
-                result = WrapUtil.toSet(conf2ScopeMapping);
-            }
-        }
-        return result;
-    }
-
-    private List<Conf2ScopeMapping> getMappingsForConfigurations(Collection<Configuration> configurations) {
-        List<Conf2ScopeMapping> existingMappings = new ArrayList<Conf2ScopeMapping>();
-        for (Configuration configuration : configurations) {
-            if (mappings.get(configuration) != null) {
-                existingMappings.add(mappings.get(configuration));
-            } else {
-                existingMappings.add(new Conf2ScopeMapping(null, configuration, null));
-            }
-        }
-        return existingMappings;
-    }
-
-    public Conf2ScopeMappingContainer addMapping(int priority, Configuration configuration, String scope) {
-        mappings.put(configuration, new Conf2ScopeMapping(priority, configuration, scope));
-        return this;
-    }
-
-    public Map<Configuration, Conf2ScopeMapping> getMappings() {
-        return mappings;
-    }
-
-    public boolean isSkipUnmappedConfs() {
-        return skipUnmappedConfs;
-    }
-
-    public void setSkipUnmappedConfs(boolean skipUnmappedConfs) {
-        this.skipUnmappedConfs = skipUnmappedConfs;
-    }
-
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultConf2ScopeMappingContainer that = (DefaultConf2ScopeMappingContainer) o;
-
-        if (!mappings.equals(that.mappings)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public int hashCode() {
-        return mappings.hashCode();
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultDeployerFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultDeployerFactory.java
index 92d3737..383f94e 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultDeployerFactory.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultDeployerFactory.java
@@ -20,9 +20,11 @@ import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.MavenPom;
 import org.gradle.api.artifacts.maven.MavenResolver;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.publication.maven.internal.ant.BaseMavenInstaller;
-import org.gradle.api.publication.maven.internal.ant.DefaultGroovyMavenDeployer;
+import org.gradle.api.publication.maven.internal.deployer.BaseMavenInstaller;
+import org.gradle.api.publication.maven.internal.deployer.DefaultGroovyMavenDeployer;
 import org.gradle.internal.Factory;
 import org.gradle.logging.LoggingManagerInternal;
 
@@ -33,29 +35,36 @@ public class DefaultDeployerFactory implements DeployerFactory {
     private final MavenPomMetaInfoProvider pomMetaInfoProvider;
     private final ConfigurationContainer configurationContainer;
     private final Conf2ScopeMappingContainer scopeMapping;
+    private final MavenSettingsProvider mavenSettingsProvider;
+    private final LocalMavenRepositoryLocator mavenRepositoryLocator;
 
     public DefaultDeployerFactory(MavenFactory mavenFactory, Factory<LoggingManagerInternal> loggingManagerFactory, FileResolver fileResolver, MavenPomMetaInfoProvider pomMetaInfoProvider,
-                                  ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMapping) {
+                                  ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMapping, 
+                                  MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
         this.mavenFactory = mavenFactory;
         this.loggingManagerFactory = loggingManagerFactory;
         this.fileResolver = fileResolver;
         this.pomMetaInfoProvider = pomMetaInfoProvider;
         this.configurationContainer = configurationContainer;
         this.scopeMapping = scopeMapping;
+        this.mavenSettingsProvider = mavenSettingsProvider;
+        this.mavenRepositoryLocator = mavenRepositoryLocator;
     }
 
     public DefaultGroovyMavenDeployer createMavenDeployer() {
         PomFilterContainer pomFilterContainer = createPomFilterContainer(
                 mavenFactory.createMavenPomFactory(configurationContainer, scopeMapping, fileResolver));
         return new DefaultGroovyMavenDeployer(pomFilterContainer, createArtifactPomContainer(
-                pomMetaInfoProvider, pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create());
+                pomMetaInfoProvider, pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create(),
+                mavenSettingsProvider, mavenRepositoryLocator);
     }
 
     public MavenResolver createMavenInstaller() {
         PomFilterContainer pomFilterContainer = createPomFilterContainer(
                 mavenFactory.createMavenPomFactory(configurationContainer, scopeMapping, fileResolver));
         return new BaseMavenInstaller(pomFilterContainer, createArtifactPomContainer(pomMetaInfoProvider,
-                pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create());
+                pomFilterContainer, createArtifactPomFactory()), loggingManagerFactory.create(),
+                mavenSettingsProvider, mavenRepositoryLocator);
     }
 
     private PomFilterContainer createPomFilterContainer(Factory<MavenPom> mavenPomFactory) {
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenFactory.java
deleted file mode 100644
index 3496e47..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenFactory.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.publication.maven.internal;
-
-import org.gradle.api.artifacts.maven.*;
-import org.gradle.internal.Factory;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.publication.maven.internal.ant.DefaultExcludeRuleConverter;
-import org.gradle.api.publication.maven.internal.ant.DefaultPomDependenciesConverter;
-
-import java.util.Map;
-
-public class DefaultMavenFactory implements MavenFactory {
-
-    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, FileResolver fileResolver) {
-        return new DefaultMavenPomFactory(configurationContainer, conf2ScopeMappingContainer, createPomDependenciesConverter(), fileResolver);
-    }
-
-    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Map<Configuration, Conf2ScopeMapping> mappings, FileResolver fileResolver) {
-        return new DefaultMavenPomFactory(configurationContainer, createConf2ScopeMappingContainer(mappings), createPomDependenciesConverter(), fileResolver);
-    }
-
-    private PomDependenciesConverter createPomDependenciesConverter() {
-        return new DefaultPomDependenciesConverter(new DefaultExcludeRuleConverter());
-    }
-
-    public Conf2ScopeMappingContainer createConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings) {
-        return new DefaultConf2ScopeMappingContainer(mappings);
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPom.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPom.java
deleted file mode 100644
index dea4fd9..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPom.java
+++ /dev/null
@@ -1,236 +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.publication.maven.internal;
-
-import groovy.lang.Closure;
-import org.apache.maven.model.Dependency;
-import org.apache.maven.model.Model;
-import org.apache.maven.project.MavenProject;
-import org.codehaus.groovy.runtime.InvokerHelper;
-import org.gradle.api.Action;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.XmlProvider;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.artifacts.maven.MavenPom;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.internal.ErroringAction;
-import org.gradle.internal.IoActions;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.xml.XmlTransformer;
-import org.gradle.listener.ActionBroadcast;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.List;
-
-public class DefaultMavenPom implements MavenPom {
-
-    private PomDependenciesConverter pomDependenciesConverter;
-    private FileResolver fileResolver;
-    private MavenProject mavenProject = new MavenProject();
-    private Conf2ScopeMappingContainer scopeMappings;
-    private ActionBroadcast<MavenPom> whenConfiguredActions = new ActionBroadcast<MavenPom>();
-    private XmlTransformer withXmlActions = new XmlTransformer();
-    private ConfigurationContainer configurations;
-
-    public DefaultMavenPom(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMappings, PomDependenciesConverter pomDependenciesConverter,
-                           FileResolver fileResolver) {
-        this.configurations = configurationContainer;
-        this.scopeMappings = scopeMappings;
-        this.pomDependenciesConverter = pomDependenciesConverter;
-        this.fileResolver = fileResolver;
-        mavenProject.setModelVersion("4.0.0");
-    }
-
-    public Conf2ScopeMappingContainer getScopeMappings() {
-        return scopeMappings;
-    }
-
-    public ConfigurationContainer getConfigurations() {
-        return configurations;
-    }
-
-    public DefaultMavenPom setConfigurations(ConfigurationContainer configurations) {
-        this.configurations = configurations;
-        return this;
-    }
-
-    public DefaultMavenPom setGroupId(String groupId) {
-        getModel().setGroupId(groupId);
-        return this;
-    }
-
-    public String getGroupId() {
-        return getModel().getGroupId();
-    }
-
-    public DefaultMavenPom setArtifactId(String artifactId) {
-        getModel().setArtifactId(artifactId);
-        return this;
-    }
-
-    public String getArtifactId() {
-        return getModel().getArtifactId();
-    }
-
-    @SuppressWarnings("unchecked")
-    public DefaultMavenPom setDependencies(List<?> dependencies) {
-        getModel().setDependencies((List<Dependency>) dependencies);
-        return this;
-    }
-
-    public List<Dependency> getDependencies() {
-        return getModel().getDependencies();
-    }
-
-    public DefaultMavenPom setName(String name) {
-        getModel().setName(name);
-        return this;
-    }
-
-    public String getName() {
-        return getModel().getName();
-    }
-
-    public DefaultMavenPom setVersion(String version) {
-        getModel().setVersion(version);
-        return this;
-    }
-
-    public String getVersion() {
-        return getModel().getVersion();
-    }
-
-    public String getPackaging() {
-        return getModel().getPackaging();
-    }
-
-    public DefaultMavenPom setPackaging(String packaging) {
-        getModel().setPackaging(packaging);
-        return this;
-    }
-
-    public DefaultMavenPom project(Closure cl) {
-        CustomModelBuilder pomBuilder = new CustomModelBuilder(getModel());
-        InvokerHelper.invokeMethod(pomBuilder, "project", cl);
-        return this;
-    }
-
-    public Model getModel() {
-        return mavenProject.getModel();
-    }
-
-    public DefaultMavenPom setModel(Object model) {
-        this.mavenProject = new MavenProject((Model) model);
-        return this;
-    }
-
-    public MavenProject getMavenProject() {
-        return mavenProject;
-    }
-
-    public DefaultMavenPom setMavenProject(MavenProject mavenProject) {
-        this.mavenProject = mavenProject;
-        return this;
-    }
-
-    @SuppressWarnings("unchecked")
-    public List<Dependency> getGeneratedDependencies() {
-        if (configurations == null) {
-            return Collections.emptyList();
-        }
-        return (List<Dependency>) pomDependenciesConverter.convert(getScopeMappings(), configurations);
-    }
-
-    public DefaultMavenPom getEffectivePom() {
-        DefaultMavenPom effectivePom = new DefaultMavenPom(null, this.scopeMappings, pomDependenciesConverter, fileResolver);
-        try {
-            effectivePom.setMavenProject((MavenProject) mavenProject.clone());
-        } catch (CloneNotSupportedException e) {
-            throw new RuntimeException(e);
-        }
-        effectivePom.getDependencies().addAll(getGeneratedDependencies());
-        effectivePom.withXmlActions = withXmlActions;
-        whenConfiguredActions.execute(effectivePom);
-        return effectivePom;
-    }
-
-    public PomDependenciesConverter getPomDependenciesConverter() {
-        return pomDependenciesConverter;
-    }
-
-    public FileResolver getFileResolver() {
-        return fileResolver;
-    }
-
-    public DefaultMavenPom setFileResolver(FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-        return this;
-    }
-
-    public DefaultMavenPom writeTo(final Writer pomWriter) {
-        try {
-            getEffectivePom().writeNonEffectivePom(pomWriter);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        return this;
-    }
-
-    public DefaultMavenPom writeTo(Object path) {
-        IoActions.writeTextFile(fileResolver.resolve(path), POM_FILE_ENCODING, new Action<BufferedWriter>() {
-            public void execute(BufferedWriter writer) {
-                writeTo(writer);
-            }
-        });
-        return this;
-    }
-
-    private void writeNonEffectivePom(final Writer pomWriter) throws IOException {
-        try {
-            withXmlActions.transform(pomWriter, POM_FILE_ENCODING, new ErroringAction<Writer>() {
-                protected void doExecute(Writer writer) throws IOException {
-                    mavenProject.writeModel(writer);
-                }
-            });
-        } finally {
-            pomWriter.close();
-        }
-    }
-
-    public DefaultMavenPom whenConfigured(final Closure closure) {
-        whenConfiguredActions.add(new ClosureBackedAction<MavenPom>(closure));
-        return this;
-    }
-
-    public DefaultMavenPom whenConfigured(final Action<MavenPom> action) {
-        whenConfiguredActions.add(action);
-        return this;
-    }
-
-    public DefaultMavenPom withXml(final Closure closure) {
-        withXmlActions.addAction(closure);
-        return this;
-    }
-
-    public DefaultMavenPom withXml(final Action<XmlProvider> action) {
-        withXmlActions.addAction(action);
-        return this;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactory.java
deleted file mode 100644
index 146f9b1..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactory.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal;
-
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.artifacts.maven.MavenPom;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.Factory;
-
-public class DefaultMavenPomFactory implements Factory<MavenPom> {
-    private ConfigurationContainer configurationContainer;
-    private Conf2ScopeMappingContainer conf2ScopeMappingContainer;
-    private PomDependenciesConverter pomDependenciesConverter;
-    private FileResolver fileResolver;
-
-
-    public DefaultMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, PomDependenciesConverter pomDependenciesConverter,
-                                  FileResolver fileResolver) {
-        this.configurationContainer = configurationContainer;
-        this.conf2ScopeMappingContainer = conf2ScopeMappingContainer;
-        this.pomDependenciesConverter = pomDependenciesConverter;
-        this.fileResolver = fileResolver;
-    }
-
-    public MavenPom create() {
-        return new DefaultMavenPom(configurationContainer,
-                new DefaultConf2ScopeMappingContainer(conf2ScopeMappingContainer.getMappings()), pomDependenciesConverter, fileResolver);
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ExcludeRuleConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ExcludeRuleConverter.java
deleted file mode 100644
index 1702005..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ExcludeRuleConverter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal;
-
-import org.gradle.api.artifacts.ExcludeRule;
-
-
-public interface ExcludeRuleConverter {
-    Object convert(ExcludeRule excludeRule);
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapper.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapper.java
new file mode 100644
index 0000000..84984cc
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.MavenVersionSelectorScheme;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+
+public class MavenVersionRangeMapper implements VersionRangeMapper {
+    private final VersionSelectorScheme defaultVersionSelectorScheme;
+    private final VersionSelectorScheme mavenVersionSelectorScheme;
+
+    public MavenVersionRangeMapper(VersionSelectorScheme defaultVersionSelector) {
+        this.defaultVersionSelectorScheme = defaultVersionSelector;
+        mavenVersionSelectorScheme = new MavenVersionSelectorScheme(defaultVersionSelectorScheme);
+    }
+
+    public String map(String version) {
+        if(version == null) {
+            return null;
+        }
+        return mavenVersionSelectorScheme.renderSelector(defaultVersionSelectorScheme.parseSelector(version));
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/PomDependenciesConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/PomDependenciesConverter.java
deleted file mode 100644
index 04c38b3..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/PomDependenciesConverter.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.publication.maven.internal;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-
-import java.util.List;
-import java.util.Set;
-
-public interface PomDependenciesConverter {
-    public List<?> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations);
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/VersionRangeMapper.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/VersionRangeMapper.java
new file mode 100644
index 0000000..eea7d1e
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/VersionRangeMapper.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal;
+
+public interface VersionRangeMapper {
+    String map(String version);
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/AbstractMavenPublishAction.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/AbstractMavenPublishAction.java
new file mode 100644
index 0000000..f8a4ad9
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/AbstractMavenPublishAction.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.action;
+
+import com.beust.jcommander.internal.Lists;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
+import org.apache.maven.artifact.manager.WagonManager;
+import org.apache.maven.artifact.metadata.ArtifactMetadata;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.repository.DefaultArtifactRepository;
+import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.project.artifact.AttachedArtifact;
+import org.apache.maven.project.artifact.ProjectArtifactMetadata;
+import org.codehaus.classworlds.ClassWorld;
+import org.codehaus.classworlds.DuplicateRealmException;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.PlexusContainerException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.embed.Embedder;
+import org.gradle.api.GradleException;
+import org.gradle.internal.UncheckedException;
+
+import java.io.File;
+import java.util.List;
+
+abstract class AbstractMavenPublishAction implements MavenPublishAction {
+    private static ClassLoader plexusClassLoader;
+
+    protected final WagonManager wagonManager;
+    private final File pomFile;
+    private final List<AdditionalArtifact> additionalArtifacts = Lists.newArrayList();
+    private File mainArtifact;
+
+    private File localMavenRepository;
+    private PlexusContainer container;
+
+    protected AbstractMavenPublishAction(File pomFile) {
+        this.pomFile = pomFile;
+        this.wagonManager = lookup(WagonManager.class);
+        wagonManager.setDownloadMonitor(new LoggingMavenTransferListener());
+    }
+
+    public void setLocalMavenRepositoryLocation(File localMavenRepository) {
+        this.localMavenRepository = localMavenRepository;
+    }
+
+    public void setMainArtifact(File file) {
+        this.mainArtifact = file;
+    }
+
+    @Override
+    public void addAdditionalArtifact(File file, String type, String classifier) {
+        AdditionalArtifact artifact = new AdditionalArtifact();
+        artifact.setFile(file);
+        artifact.setType(type);
+        artifact.setClassifier(classifier);
+
+        additionalArtifacts.add(artifact);
+    }
+
+    public void publish() {
+        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        try {
+            if (plexusClassLoader != null) {
+                Thread.currentThread().setContextClassLoader(plexusClassLoader);
+            }
+            doPublish();
+        } finally {
+            plexusClassLoader = Thread.currentThread().getContextClassLoader();
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
+        }
+    }
+
+    private void doPublish() {
+        ArtifactRepository localRepo = createLocalArtifactRepository();
+        ParsedMavenPom parsedMavenPom = new ParsedMavenPom(pomFile);
+
+        Artifact parentArtifact;
+        if (mainArtifact == null) {
+            Artifact pomArtifact = createPomArtifact(parsedMavenPom);
+            publishArtifact(pomArtifact, pomFile, localRepo);
+            parentArtifact = pomArtifact;
+        } else {
+            Artifact artifact = createMainArtifact(parsedMavenPom);
+            ArtifactMetadata metadata = new ProjectArtifactMetadata(artifact, pomFile);
+            artifact.addMetadata(metadata);
+            publishArtifact(artifact, mainArtifact, localRepo);
+            parentArtifact = artifact;
+        }
+
+        for (AdditionalArtifact attachedArtifact : additionalArtifacts) {
+            Artifact attach = createAttachedArtifact(parentArtifact, attachedArtifact.getType(), attachedArtifact.getClassifier());
+            publishArtifact(attach, attachedArtifact.getFile(), localRepo);
+        }
+    }
+
+    protected abstract void publishArtifact(Artifact artifact, File artifactFile, ArtifactRepository localRepo);
+
+    private ArtifactRepository createLocalArtifactRepository() {
+        String localRepositoryLocation = localMavenRepository.toURI().toString();
+        return new DefaultArtifactRepository("local", localRepositoryLocation, new DefaultRepositoryLayout());
+    }
+
+    protected <T> T lookup(Class<T> type) {
+        String role = type.getName();
+        try {
+            @SuppressWarnings("unchecked")
+            T lookup1 = (T) getContainer().lookup(role);
+            return lookup1;
+        } catch (ComponentLookupException e) {
+            throw new GradleException("Unable to find component: " + role, e);
+        }
+    }
+
+    protected synchronized PlexusContainer getContainer() {
+        if (container == null) {
+            try {
+                ClassWorld classWorld = new ClassWorld();
+                classWorld.newRealm("plexus.core", getClass().getClassLoader());
+
+                Embedder embedder = new Embedder();
+                embedder.start(classWorld);
+
+                container = embedder.getContainer();
+            } catch (PlexusContainerException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            } catch (DuplicateRealmException e) {
+                throw UncheckedException.throwAsUncheckedException(e);
+            }
+        }
+
+        return container;
+    }
+
+    private Artifact createMainArtifact(ParsedMavenPom pom) {
+        return new DefaultArtifact(pom.getGroup(), pom.getArtifactId(), VersionRange.createFromVersion(pom.getVersion()),
+                null, pom.getPackaging(), null, artifactHandler(pom.getPackaging()));
+    }
+
+    private Artifact createPomArtifact(ParsedMavenPom pom) {
+        return new DefaultArtifact(pom.getGroup(), pom.getArtifactId(), VersionRange.createFromVersion(pom.getVersion()),
+                null, "pom", null, artifactHandler("pom"));
+    }
+
+    private Artifact createAttachedArtifact(Artifact mainArtifact, String type, String classifier) {
+        return new AttachedArtifact(mainArtifact, type, classifier, artifactHandler(type));
+    }
+
+    private DefaultArtifactHandler artifactHandler(String type) {
+        return new DefaultArtifactHandler(type);
+    }
+
+    private static class AdditionalArtifact {
+        File file;
+        String type;
+        String classifier;
+
+        public void setFile(File file) {
+            this.file = file;
+        }
+
+        public File getFile() {
+            return file;
+        }
+
+        public void setType(String type) {
+            this.type = type;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void setClassifier(String classifier) {
+            this.classifier = classifier;
+        }
+
+        public String getClassifier() {
+            return classifier;
+        }
+    }
+
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/LoggingMavenTransferListener.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/LoggingMavenTransferListener.java
new file mode 100644
index 0000000..9179df5
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/LoggingMavenTransferListener.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.action;
+
+import org.apache.maven.wagon.events.TransferEvent;
+import org.apache.maven.wagon.events.TransferListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class LoggingMavenTransferListener implements TransferListener {
+    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingMavenTransferListener.class);
+    private static final int KILO = 1024;
+
+    protected void log(String message) {
+        LOGGER.info(message);
+    }
+
+    public void debug(String s) {
+        LOGGER.debug(s);
+    }
+
+    public void transferError(TransferEvent event) {
+        LOGGER.error(event.getException().getMessage());
+    }
+
+    public void transferInitiated(TransferEvent event) {
+        String message = event.getRequestType() == TransferEvent.REQUEST_PUT ? "Uploading" : "Downloading";
+        String dest = event.getRequestType() == TransferEvent.REQUEST_PUT ? " to " : " from ";
+
+        LOGGER.info(message + ": " + event.getResource().getName() + dest + "repository "
+                + event.getWagon().getRepository().getId() + " at " + event.getWagon().getRepository().getUrl());
+    }
+
+    public void transferStarted(TransferEvent event) {
+        long contentLength = event.getResource().getContentLength();
+        if (contentLength > 0) {
+            LOGGER.info("Transferring " + ((contentLength + KILO / 2) / KILO) + "K from "
+                    + event.getWagon().getRepository().getId());
+        }
+    }
+
+    public void transferProgress(TransferEvent event, byte[] bytes, int i) {
+    }
+
+    public void transferCompleted(TransferEvent event) {
+        long contentLength = event.getResource().getContentLength();
+        if ((contentLength > 0) && (event.getRequestType() == TransferEvent.REQUEST_PUT)) {
+            LOGGER.info("Uploaded " + ((contentLength + KILO / 2) / KILO) + "K");
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenDeployAction.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenDeployAction.java
new file mode 100644
index 0000000..450c2b4
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenDeployAction.java
@@ -0,0 +1,97 @@
+/*
+ * 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.publication.maven.internal.action;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.ant.Authentication;
+import org.apache.maven.artifact.ant.Proxy;
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.apache.maven.artifact.deployer.ArtifactDeployer;
+import org.apache.maven.artifact.deployer.ArtifactDeploymentException;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.repository.DefaultArtifactRepository;
+import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
+import org.gradle.api.GradleException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class MavenDeployAction extends AbstractMavenPublishAction {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MavenDeployAction.class);
+
+    private RemoteRepository remoteRepository;
+
+    private RemoteRepository remoteSnapshotRepository;
+
+    private boolean uniqueVersion = true;
+
+    public MavenDeployAction(File pomFile) {
+        super(pomFile);
+    }
+
+    public void setRepositories(RemoteRepository repository, RemoteRepository snapshotRepository) {
+        this.remoteRepository = repository;
+        this.remoteSnapshotRepository = snapshotRepository;
+    }
+
+    public void setUniqueVersion(boolean uniqueVersion) {
+        this.uniqueVersion = uniqueVersion;
+    }
+
+    protected void publishArtifact(Artifact artifact, File artifactFile, ArtifactRepository localRepo) {
+        ArtifactDeployer deployer = lookup(ArtifactDeployer.class);
+        ArtifactRepository deploymentRepository = getRemoteArtifactRepository(artifact);
+
+        LOGGER.info("Deploying to " + deploymentRepository.getUrl());
+
+        try {
+            deployer.deploy(artifactFile, artifact, deploymentRepository, localRepo);
+        } catch (ArtifactDeploymentException e) {
+            throw new GradleException("Error deploying artifact '" + artifact.getDependencyConflictId() + "': " + e.getMessage(), e);
+        }
+    }
+
+    private ArtifactRepository getRemoteArtifactRepository(Artifact artifact) {
+        RemoteRepository deploymentRepository = remoteRepository;
+        if (artifact.isSnapshot() && remoteSnapshotRepository != null) {
+            deploymentRepository = remoteSnapshotRepository;
+        }
+
+        if (deploymentRepository == null) {
+            throw new GradleException("Must specify a repository for deployment");
+        }
+
+        // The repository id is used for `maven-metadata-${repository.id}.xml`, and to match credentials to repository.
+        initWagonManagerWithRepositorySettings("remote", deploymentRepository);
+        return new DefaultArtifactRepository("remote", deploymentRepository.getUrl(), new DefaultRepositoryLayout(), uniqueVersion);
+    }
+
+    private void initWagonManagerWithRepositorySettings(String repositoryId, RemoteRepository repository) {
+        Authentication authentication = repository.getAuthentication();
+        if (authentication != null) {
+            wagonManager.addAuthenticationInfo(repositoryId, authentication.getUserName(),
+                    authentication.getPassword(), authentication.getPrivateKey(),
+                    authentication.getPassphrase());
+        }
+
+        Proxy proxy = repository.getProxy();
+        if (proxy != null) {
+            wagonManager.addProxy(proxy.getType(), proxy.getHost(), proxy.getPort(), proxy.getUserName(),
+                    proxy.getPassword(), proxy.getNonProxyHosts());
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenInstallAction.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenInstallAction.java
new file mode 100644
index 0000000..51a452a
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenInstallAction.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.action;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.installer.ArtifactInstallationException;
+import org.apache.maven.artifact.installer.ArtifactInstaller;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.gradle.api.GradleException;
+
+import java.io.File;
+
+public class MavenInstallAction extends AbstractMavenPublishAction {
+    public MavenInstallAction(File pomFile) {
+        super(pomFile);
+    }
+
+    @Override
+    protected void publishArtifact(Artifact artifact, File artifactFile, ArtifactRepository localRepo) {
+        ArtifactInstaller installer = lookup(ArtifactInstaller.class);
+        try {
+            installer.install(artifactFile, artifact, localRepo);
+        } catch (ArtifactInstallationException e) {
+            throw new GradleException("Error installing artifact '" + artifact.getDependencyConflictId() + "': " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenPublishAction.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenPublishAction.java
new file mode 100644
index 0000000..008ed11
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenPublishAction.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.publication.maven.internal.action;
+
+import java.io.File;
+
+public interface MavenPublishAction {
+    void setMainArtifact(File file);
+
+    void addAdditionalArtifact(File file, String type, String classifier);
+
+    void publish();
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenWagonDeployAction.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenWagonDeployAction.java
new file mode 100644
index 0000000..316904c
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/MavenWagonDeployAction.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.action;
+
+import org.codehaus.plexus.PlexusContainerException;
+
+import java.io.File;
+
+/**
+ * A deploy action that uses the baked in Maven wagon implementations, or a custom user-provided wagon implemented.
+ */
+public class MavenWagonDeployAction extends MavenDeployAction {
+    public MavenWagonDeployAction(File pomFile) {
+        super(pomFile);
+    }
+
+    public void addWagonJar(File jar) {
+        try {
+            getContainer().addJarResource(jar);
+        } catch (PlexusContainerException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/ParsedMavenPom.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/ParsedMavenPom.java
new file mode 100644
index 0000000..225eac5
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/action/ParsedMavenPom.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.action;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.gradle.api.GradleException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+class ParsedMavenPom {
+    private final Model model;
+
+    public ParsedMavenPom(File pomFile) {
+        try {
+            model = parsePom(pomFile);
+        } catch (Exception e) {
+            throw new GradleException("Cannot read generated POM!", e);
+        }
+    }
+
+    private Model parsePom(File pomFile) throws IOException, XmlPullParserException {
+        FileReader reader = new FileReader(pomFile);
+        try {
+            return new MavenXpp3Reader().read(reader, false);
+        } finally {
+            IOUtils.closeQuietly(reader);
+        }
+    }
+
+    public String getGroup() {
+        return model.getGroupId();
+    }
+
+    public String getArtifactId() {
+        return model.getArtifactId();
+    }
+
+    public String getVersion() {
+        return model.getVersion();
+    }
+
+    public String getPackaging() {
+        return model.getPackaging();
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/AbstractMavenResolver.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/AbstractMavenResolver.java
deleted file mode 100644
index 3ba0bd5..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/AbstractMavenResolver.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import groovy.lang.Closure;
-import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.cache.RepositoryCacheManager;
-import org.apache.ivy.core.module.descriptor.Artifact;
-import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
-import org.apache.ivy.core.module.id.ModuleRevisionId;
-import org.apache.ivy.core.report.ArtifactDownloadReport;
-import org.apache.ivy.core.report.DownloadReport;
-import org.apache.ivy.core.resolve.DownloadOptions;
-import org.apache.ivy.core.resolve.ResolveData;
-import org.apache.ivy.core.resolve.ResolvedModuleRevision;
-import org.apache.ivy.core.search.ModuleEntry;
-import org.apache.ivy.core.search.OrganisationEntry;
-import org.apache.ivy.core.search.RevisionEntry;
-import org.apache.ivy.core.settings.IvySettings;
-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.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.Pom;
-import org.apache.maven.settings.Settings;
-import org.apache.tools.ant.Project;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.maven.*;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleVersionRepository;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.NoOpRepositoryCacheManager;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionArtifactPublishMetaData;
-import org.gradle.api.internal.artifacts.metadata.ModuleVersionPublishMetaData;
-import org.gradle.api.internal.artifacts.repositories.AbstractArtifactRepository;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
-import org.gradle.api.publication.maven.internal.PomFilter;
-import org.gradle.listener.ActionBroadcast;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.AntUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.Map;
-import java.util.Set;
-
-public abstract class AbstractMavenResolver extends AbstractArtifactRepository implements MavenResolver, DependencyResolver, ModuleVersionPublisher {
-    
-    private ArtifactPomContainer artifactPomContainer;
-
-    private PomFilterContainer pomFilterContainer;
-
-    private Settings settings;
-
-    private LoggingManagerInternal loggingManager;
-
-    private final ActionBroadcast<MavenDeployment> beforeDeploymentActions = new ActionBroadcast<MavenDeployment>();
-
-    protected MavenSettingsSupplier mavenSettingsSupplier = new EmptyMavenSettingsSupplier();
-
-    public AbstractMavenResolver(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        this.pomFilterContainer = pomFilterContainer;
-        this.artifactPomContainer = artifactPomContainer;
-        this.loggingManager = loggingManager;
-    }
-
-    public ConfiguredModuleVersionRepository createResolver() {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public ModuleVersionPublisher createPublisher() {
-        return this;
-    }
-
-    public DependencyResolver createLegacyDslObject() {
-        return this;
-    }
-
-    protected abstract InstallDeployTaskSupport createPreConfiguredTask(Project project);
-
-    public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public boolean exists(Artifact artifact) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public ArtifactOrigin locate(Artifact artifact) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public void reportFailure() {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public void reportFailure(Artifact art) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public String[] listTokenValues(String token, Map otherTokenValues) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public Map[] listTokenValues(String[] tokens, Map criteria) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public OrganisationEntry[] listOrganisations() {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public ModuleEntry[] listModules(OrganisationEntry org) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public RevisionEntry[] listRevisions(ModuleEntry module) {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public Namespace getNamespace() {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public void dumpSettings() {
-        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
-    }
-
-    public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    public void abortPublishTransaction() throws IOException {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    public void commitPublishTransaction() throws IOException {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    public void publish(ModuleVersionPublishMetaData moduleVersion) {
-        ModuleVersionIdentifier id = moduleVersion.getId();
-        for (ModuleVersionArtifactPublishMetaData artifact : moduleVersion.getArtifacts()) {
-            collectArtifact(artifact.toIvyArtifact(), artifact.getFile());
-        }
-        publish();
-    }
-
-    private void collectArtifact(Artifact artifact, File src) {
-        if (isIgnorable(artifact)) {
-            return;
-        }
-        getArtifactPomContainer().addArtifact(artifact, src);
-    }
-
-    private boolean isIgnorable(Artifact artifact) {
-        return artifact.getType().equals("ivy");
-    }
-
-    private void publish() {
-        InstallDeployTaskSupport installDeployTaskSupport = createPreConfiguredTask(AntUtil.createProject());
-        Set<MavenDeployment> mavenDeployments = getArtifactPomContainer().createDeployableFilesInfos();
-        mavenSettingsSupplier.supply(installDeployTaskSupport);
-        for (MavenDeployment mavenDeployment : mavenDeployments) {
-            ((CustomInstallDeployTaskSupport) installDeployTaskSupport).clearAttachedArtifactsList();
-            beforeDeploymentActions.execute(mavenDeployment);
-            addPomAndArtifact(installDeployTaskSupport, mavenDeployment);
-            execute(installDeployTaskSupport);
-        }
-        mavenSettingsSupplier.done();
-        settings = ((CustomInstallDeployTaskSupport) installDeployTaskSupport).getSettings();
-    }
-
-    private void execute(InstallDeployTaskSupport deployTask) {
-        loggingManager.captureStandardOutput(LogLevel.INFO).start();
-        try {
-            deployTask.execute();
-        } finally {
-            loggingManager.stop();
-        }
-    }
-
-    private void addPomAndArtifact(InstallDeployTaskSupport installOrDeployTask, MavenDeployment mavenDeployment) {
-        Pom pom = new Pom();
-        pom.setProject(installOrDeployTask.getProject());
-        pom.setFile(mavenDeployment.getPomArtifact().getFile());
-        installOrDeployTask.addPom(pom);
-        if (mavenDeployment.getMainArtifact() != null) {
-            installOrDeployTask.setFile(mavenDeployment.getMainArtifact().getFile());
-        }
-        for (PublishArtifact classifierArtifact : mavenDeployment.getAttachedArtifacts()) {
-            AttachedArtifact attachedArtifact = installOrDeployTask.createAttach();
-            attachedArtifact.setClassifier(classifierArtifact.getClassifier());
-            attachedArtifact.setFile(classifierArtifact.getFile());
-            attachedArtifact.setType(classifierArtifact.getType());
-        }
-    }
-
-    public void setSettings(ResolverSettings settings) {
-        // do nothing
-    }
-
-    public void setSettings(IvySettings settings) {
-        // do nothing
-    }
-
-    public RepositoryCacheManager getRepositoryCacheManager() {
-        return new NoOpRepositoryCacheManager(getName());
-    }
-
-    public ArtifactPomContainer getArtifactPomContainer() {
-        return artifactPomContainer;
-    }
-
-    public Settings getSettings() {
-        return settings;
-    }
-
-    public PublishFilter getFilter() {
-        return pomFilterContainer.getFilter();
-    }
-
-    public void setFilter(PublishFilter defaultFilter) {
-        pomFilterContainer.setFilter(defaultFilter);
-    }
-
-    public MavenPom getPom() {
-        return pomFilterContainer.getPom();
-    }
-
-    public void setPom(MavenPom defaultPom) {
-        pomFilterContainer.setPom(defaultPom);
-    }
-
-    public MavenPom addFilter(String name, PublishFilter publishFilter) {
-        return pomFilterContainer.addFilter(name, publishFilter);
-    }
-
-    public MavenPom addFilter(String name, Closure filter) {
-        return pomFilterContainer.addFilter(name, filter);
-    }
-
-    public void filter(Closure filter) {
-        pomFilterContainer.filter(filter);
-    }
-
-    public PublishFilter filter(String name) {
-        return pomFilterContainer.filter(name);
-    }
-
-    public MavenPom pom(String name) {
-        return pomFilterContainer.pom(name);
-    }
-
-    public MavenPom pom(Closure configureClosure) {
-        return pomFilterContainer.pom(configureClosure);
-    }
-
-    public MavenPom pom(String name, Closure configureClosure) {
-        return pomFilterContainer.pom(name, configureClosure);
-    }
-
-    public Iterable<PomFilter> getActivePomFilters() {
-        return pomFilterContainer.getActivePomFilters();
-    }
-
-    public PomFilterContainer getPomFilterContainer() {
-        return pomFilterContainer;
-    }
-
-    public void beforeDeployment(Action<? super MavenDeployment> action) {
-        beforeDeploymentActions.add(action);
-    }
-
-    public void beforeDeployment(Closure action) {
-        beforeDeploymentActions.add(new ClosureBackedAction<MavenDeployment>(action));
-    }
-
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenDeployer.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenDeployer.java
deleted file mode 100644
index 35b0e95..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenDeployer.java
+++ /dev/null
@@ -1,119 +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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.DeployTask;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.tools.ant.Project;
-import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.PlexusContainerException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.MavenDeployer;
-import org.gradle.api.artifacts.maven.PomFilterContainer;
-import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
-import org.gradle.logging.LoggingManagerInternal;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDeployer {
-    private RemoteRepository remoteRepository;
-
-    private RemoteRepository remoteSnapshotRepository;
-
-    private Configuration configuration;
-
-    // todo remove this property once configuration can handle normal file system dependencies
-    private List<File> protocolProviderJars = new ArrayList<File>();
-
-    private boolean uniqueVersion = true;
-
-    public BaseMavenDeployer(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        super(pomFilterContainer, artifactPomContainer, loggingManager);
-    }
-
-    protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
-        CustomDeployTask deployTask = createTask();
-        deployTask.setProject(project);
-        deployTask.setUniqueVersion(isUniqueVersion());
-        addProtocolProvider(deployTask);
-        addRemoteRepositories(deployTask);
-        return deployTask;
-    }
-
-    protected CustomDeployTask createTask() {
-        return new CustomDeployTask();
-    }
-
-    private void addProtocolProvider(CustomDeployTask deployTask) {
-        PlexusContainer plexusContainer = deployTask.getContainer();
-        for (File wagonProviderJar : getJars()) {
-            try {
-                plexusContainer.addJarResource(wagonProviderJar);
-            } catch (PlexusContainerException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private List<File> getJars() {
-        return configuration != null ? new ArrayList<File>(configuration.resolve()) : protocolProviderJars;
-    }
-
-    private void addRemoteRepositories(DeployTask deployTask) {
-        deployTask.addRemoteRepository(remoteRepository);
-        deployTask.addRemoteSnapshotRepository(remoteSnapshotRepository);
-    }
-
-    public RemoteRepository getRepository() {
-        return remoteRepository;
-    }
-
-    public void setRepository(Object remoteRepository) {
-        this.remoteRepository = (RemoteRepository) remoteRepository;
-    }
-
-    public RemoteRepository getSnapshotRepository() {
-        return remoteSnapshotRepository;
-    }
-
-    public void setSnapshotRepository(Object remoteSnapshotRepository) {
-        this.remoteSnapshotRepository = (RemoteRepository) remoteSnapshotRepository;
-    }
-
-    public void addProtocolProviderJars(Collection<File> jars) {
-        protocolProviderJars.addAll(jars);
-    }
-
-    public Configuration getConfiguration() {
-        return configuration;
-    }
-
-    public void setConfiguration(Configuration configuration) {
-        this.configuration = configuration;
-    }
-
-    public boolean isUniqueVersion() {
-        return uniqueVersion;
-    }
-
-    public void setUniqueVersion(boolean uniqueVersion) {
-        this.uniqueVersion = uniqueVersion;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenInstaller.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenInstaller.java
deleted file mode 100644
index a901694..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/BaseMavenInstaller.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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.InstallTask;
-import org.apache.tools.ant.Project;
-import org.gradle.api.artifacts.maven.PomFilterContainer;
-import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
-import org.gradle.logging.LoggingManagerInternal;
-
-public class BaseMavenInstaller extends AbstractMavenResolver {
-    public BaseMavenInstaller(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        super(pomFilterContainer, artifactPomContainer, loggingManager);
-        mavenSettingsSupplier = new MaybeUserMavenSettingsSupplier();
-    }
-
-    protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
-        InstallTask installTask = createTask();
-        installTask.setProject(project);
-        return installTask;
-    }
-
-    protected CustomInstallTask createTask() {
-        return new CustomInstallTask();
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomDeployTask.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomDeployTask.java
deleted file mode 100644
index 85521bf..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomDeployTask.java
+++ /dev/null
@@ -1,45 +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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.DeployTask;
-import org.apache.maven.settings.Settings;
-import org.codehaus.plexus.PlexusContainer;
-
-/**
- * We could also use reflection to get hold of the container property. But this would make it harder to use a Mock for this class.
- */
-public class CustomDeployTask extends DeployTask implements CustomInstallDeployTaskSupport {
-    @Override
-    public synchronized Settings getSettings() {
-        return super.getSettings();
-    }
-
-    @Override
-    public synchronized PlexusContainer getContainer() {
-        return super.getContainer();
-    }
-
-    @Override
-    public void doExecute() {
-        LoggingHelper.injectLogger(getContainer(), getProject());
-        super.doExecute();
-    }
-
-    public void clearAttachedArtifactsList() {
-        attachedArtifacts.clear();
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallDeployTaskSupport.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallDeployTaskSupport.java
deleted file mode 100644
index 40c3ea9..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallDeployTaskSupport.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.settings.Settings;
-import org.apache.tools.ant.Project;
-
-public interface CustomInstallDeployTaskSupport {
-    Settings getSettings();
-    Project getProject();
-    AttachedArtifact createAttach();
-    void clearAttachedArtifactsList();
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallTask.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallTask.java
deleted file mode 100644
index 15079c4..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/CustomInstallTask.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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.InstallTask;
-import org.apache.maven.settings.Settings;
-
-public class CustomInstallTask extends InstallTask implements CustomInstallDeployTaskSupport {
-    @Override
-    public synchronized Settings getSettings() {
-        return super.getSettings();   
-    }
-
-    public void clearAttachedArtifactsList() {
-        attachedArtifacts.clear();
-    }
-
-    @Override
-    public void doExecute() {
-        LoggingHelper.injectLogger(getContainer(), getProject());
-        super.doExecute();
-    }
-
-
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverter.java
deleted file mode 100644
index 2f0efef..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverter.java
+++ /dev/null
@@ -1,37 +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.publication.maven.internal.ant;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.api.publication.maven.internal.ExcludeRuleConverter;
-
-
-public class DefaultExcludeRuleConverter implements ExcludeRuleConverter {
-    public Exclusion convert(ExcludeRule excludeRule) {
-        if (isConvertable(excludeRule)) {
-            Exclusion exclusion = new Exclusion();
-            exclusion.setGroupId(excludeRule.getGroup());
-            exclusion.setArtifactId(excludeRule.getModule());
-            return exclusion;
-        }
-        return null;
-    }
-
-    private boolean isConvertable(ExcludeRule excludeRule) {
-        return excludeRule.getGroup()!=null && excludeRule.getModule()!=null;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployer.groovy b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployer.groovy
deleted file mode 100644
index 819de15..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployer.groovy
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant
-
-import org.codehaus.groovy.runtime.InvokerHelper
-import org.gradle.api.artifacts.maven.GroovyMavenDeployer
-import org.gradle.api.artifacts.maven.PomFilterContainer
-import org.gradle.api.publication.maven.internal.ArtifactPomContainer
-import org.gradle.logging.LoggingManagerInternal
-
-class DefaultGroovyMavenDeployer extends BaseMavenDeployer implements GroovyMavenDeployer, PomFilterContainer {
-    public static final String REPOSITORY_BUILDER = "repository"
-    public static final String SNAPSHOT_REPOSITORY_BUILDER = 'snapshotRepository'
-    
-    private RepositoryBuilder repositoryBuilder = new RepositoryBuilder()
-
-    DefaultGroovyMavenDeployer(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
-        super(pomFilterContainer, artifactPomContainer, loggingManager)
-    }
-    
-    def methodMissing(String name, args) {
-        if (name == REPOSITORY_BUILDER || name == SNAPSHOT_REPOSITORY_BUILDER) {
-            Object repository = InvokerHelper.invokeMethod(repositoryBuilder, REPOSITORY_BUILDER, args)
-            if (name == REPOSITORY_BUILDER) {
-                setRepository(repository)
-            } else {
-                setSnapshotRepository(repository)
-            }
-            return repository;
-        } else {
-            throw new MissingMethodException(name, this.class, args)
-        }
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverter.java
deleted file mode 100644
index e1a00b7..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverter.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.maven.model.Dependency;
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.publication.maven.internal.ExcludeRuleConverter;
-import org.gradle.api.publication.maven.internal.PomDependenciesConverter;
-
-import java.util.*;
-
-public class DefaultPomDependenciesConverter implements PomDependenciesConverter {
-    private ExcludeRuleConverter excludeRuleConverter;
-
-    public DefaultPomDependenciesConverter(ExcludeRuleConverter excludeRuleConverter) {
-        this.excludeRuleConverter = excludeRuleConverter;
-    }
-
-    public List<org.apache.maven.model.Dependency> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations) {
-        Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations = createDependencyToConfigurationsMap(configurations);
-        Map<ModuleDependency, String> dependenciesMap = createDependencyToScopeMap(conf2ScopeMappingContainer, dependencyToConfigurations);
-        List<org.apache.maven.model.Dependency> mavenDependencies = new ArrayList<org.apache.maven.model.Dependency>();
-        for (ModuleDependency dependency : dependenciesMap.keySet()) {
-            String scope = dependenciesMap.get(dependency);
-            Set<Configuration> dependencyConfigurations = dependencyToConfigurations.get(dependency);
-            if (dependency.getArtifacts().size() == 0) {
-                addFromDependencyDescriptor(mavenDependencies, dependency, scope, dependencyConfigurations);
-            } else {
-                addFromArtifactDescriptor(mavenDependencies, dependency, scope, dependencyConfigurations);
-            }
-        }
-        return mavenDependencies;
-    }
-    
-    private Map<ModuleDependency, String> createDependencyToScopeMap(Conf2ScopeMappingContainer conf2ScopeMappingContainer, 
-            Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations) {
-        Map<ModuleDependency, String> dependencyToScope = new HashMap<ModuleDependency, String>();
-        for (ModuleDependency dependency : dependencyToConfigurations.keySet()) {
-            Conf2ScopeMapping conf2ScopeDependencyMapping = conf2ScopeMappingContainer.getMapping(dependencyToConfigurations.get(dependency));
-            if (!useScope(conf2ScopeMappingContainer, conf2ScopeDependencyMapping)) {
-                continue;
-            }
-            dependencyToScope.put(findDependency(dependency, conf2ScopeDependencyMapping.getConfiguration()),
-                    conf2ScopeDependencyMapping.getScope());
-        }
-        return dependencyToScope;
-    }
-
-    private ModuleDependency findDependency(ModuleDependency dependency, Configuration configuration) {
-        for (ModuleDependency configurationDependency : configuration.getDependencies().withType(ModuleDependency.class)) {
-            if (dependency.equals(configurationDependency)) {
-                return configurationDependency;
-            }
-        }
-        throw new GradleException("Dependency could not be found. We should never get here!");
-    }
-
-    private boolean useScope(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Conf2ScopeMapping conf2ScopeMapping) {
-        return conf2ScopeMapping.getScope() != null || !conf2ScopeMappingContainer.isSkipUnmappedConfs();
-    }
-
-    private Map<ModuleDependency, Set<Configuration>> createDependencyToConfigurationsMap(Set<Configuration> configurations) {
-        Map<ModuleDependency, Set<Configuration>> dependencySetMap = new HashMap<ModuleDependency, Set<Configuration>>();
-        for (Configuration configuration : configurations) {
-            for (ModuleDependency dependency : configuration.getDependencies().withType(ModuleDependency.class)) {
-                if (dependencySetMap.get(dependency) == null) {
-                    dependencySetMap.put(dependency, new HashSet<Configuration>());
-                }
-                dependencySetMap.get(dependency).add(configuration);
-            }
-        }
-        return dependencySetMap;
-    }
-
-    private void addFromArtifactDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
-            Set<Configuration> configurations) {
-        for (DependencyArtifact artifact : dependency.getArtifacts()) {
-            mavenDependencies.add(createMavenDependencyFromArtifactDescriptor(dependency, artifact, scope, configurations));
-        }
-    }
-
-    private void addFromDependencyDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
-            Set<Configuration> configurations) {
-        mavenDependencies.add(createMavenDependencyFromDependencyDescriptor(dependency, scope, configurations));
-    }
-
-    private Dependency createMavenDependencyFromArtifactDescriptor(ModuleDependency dependency, DependencyArtifact artifact, String scope,
-            Set<Configuration> configurations) {
-        return createMavenDependency(dependency, artifact.getName(), artifact.getType(), scope, artifact.getClassifier(), configurations);
-    }
-
-    private Dependency createMavenDependencyFromDependencyDescriptor(ModuleDependency dependency, String scope, Set<Configuration> configurations) {
-        return createMavenDependency(dependency, dependency.getName(), null, scope, null, configurations);
-    }
-
-    private Dependency createMavenDependency(ModuleDependency dependency, String name, String type, String scope, String classifier,
-            Set<Configuration> configurations) {
-        Dependency mavenDependency =  new Dependency();
-        mavenDependency.setGroupId(dependency.getGroup());
-        if (dependency instanceof ProjectDependency) {
-            mavenDependency.setArtifactId(determineProjectDependencyArtifactId((ProjectDependency) dependency));
-        } else {
-            mavenDependency.setArtifactId(name);
-        }
-        mavenDependency.setVersion(dependency.getVersion());
-        mavenDependency.setType(type);
-        mavenDependency.setScope(scope);
-        mavenDependency.setOptional(false);
-        mavenDependency.setClassifier(classifier);
-        mavenDependency.setExclusions(getExclusions(dependency, configurations));
-        return mavenDependency;
-    }
-
-    protected String determineProjectDependencyArtifactId(ProjectDependency dependency) {
-        return new ProjectDependencyArtifactIdExtractorHack(dependency).extract();
-    }
-
-    private List<Exclusion> getExclusions(ModuleDependency dependency, Set<Configuration> configurations) {
-        List<Exclusion> mavenExclusions = new ArrayList<Exclusion>();
-        Set<ExcludeRule> excludeRules = new HashSet<ExcludeRule>(dependency.getExcludeRules());
-        for (Configuration configuration : configurations) {
-            excludeRules.addAll(configuration.getExcludeRules());
-        }
-        for (ExcludeRule excludeRule : excludeRules) {
-            Exclusion mavenExclusion = (Exclusion) excludeRuleConverter.convert(excludeRule);
-            if (mavenExclusion != null) {
-                mavenExclusions.add(mavenExclusion);
-            }
-        }
-        return mavenExclusions;
-    }
-
-    public ExcludeRuleConverter getExcludeRuleConverter() {
-        return excludeRuleConverter;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplier.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplier.java
deleted file mode 100644
index 5549dcb..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplier.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.file.TmpDirTemporaryFileProvider;
-
-import java.io.File;
-import java.io.IOException;
-
-public class EmptyMavenSettingsSupplier implements MavenSettingsSupplier {
-
-    private final TemporaryFileProvider temporaryFileProvider = new TmpDirTemporaryFileProvider();
-    private File settingsXml;
-
-    public void supply(InstallDeployTaskSupport installDeployTaskSupport) {
-        try {
-            settingsXml = temporaryFileProvider.createTemporaryFile("gradle_empty_settings", ".xml");
-            FileUtils.writeStringToFile(settingsXml, "<settings/>");
-            settingsXml.deleteOnExit();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        installDeployTaskSupport.setSettingsFile(settingsXml);
-    }
-
-    public void done() {
-        if (settingsXml != null) {
-            settingsXml.delete();
-        }
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/LoggingHelper.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/LoggingHelper.java
deleted file mode 100644
index 248f839..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/LoggingHelper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.AntDownloadMonitor;
-import org.apache.maven.artifact.manager.DefaultWagonManager;
-import org.apache.maven.artifact.manager.WagonManager;
-import org.apache.tools.ant.Project;
-import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
-
-import java.lang.reflect.Field;
-
-public class LoggingHelper {
-    public static void injectLogger(PlexusContainer container, Project project) {
-        try {
-            WagonManager wagonManager = (WagonManager) container.lookup(WagonManager.ROLE);
-            Field field = DefaultWagonManager.class.getDeclaredField("downloadMonitor");
-            field.setAccessible(true);
-            AntDownloadMonitor antDownloadMonitor = (AntDownloadMonitor) field.get(wagonManager);
-            antDownloadMonitor.setProject(project);
-        } catch (ComponentLookupException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (NoSuchFieldException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MavenSettingsSupplier.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MavenSettingsSupplier.java
deleted file mode 100644
index a8fb1c2..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MavenSettingsSupplier.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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-
-public interface MavenSettingsSupplier {
-    void done();
-    void supply(InstallDeployTaskSupport installDeployTaskSupport);
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplier.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplier.java
deleted file mode 100644
index af4020b..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplier.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.publication.maven.internal.ant;
-
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenFileLocations;
-import org.gradle.api.internal.artifacts.mvnsettings.MavenFileLocations;
-
-import java.io.File;
-
-public class MaybeUserMavenSettingsSupplier implements MavenSettingsSupplier {
-
-    MavenSettingsSupplier emptySettingsSupplier = new EmptyMavenSettingsSupplier();
-    MavenFileLocations mavenFileLocations = new DefaultMavenFileLocations();
-
-    public void supply(InstallDeployTaskSupport installDeployTaskSupport) {
-        File userSettings = mavenFileLocations.getUserSettingsFile();
-        if (userSettings.exists()) {
-            installDeployTaskSupport.setSettingsFile(userSettings);
-            return;
-        }
-
-        emptySettingsSupplier.supply(installDeployTaskSupport);
-    }
-
-    public void done() {
-        emptySettingsSupplier.done();
-    }
-
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHack.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHack.java
deleted file mode 100644
index 988270d..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHack.java
+++ /dev/null
@@ -1,109 +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.publication.maven.internal.ant;
-
-import com.google.common.collect.Lists;
-import org.apache.maven.project.MavenProject;
-import org.gradle.api.Nullable;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.artifacts.maven.MavenDeployer;
-import org.gradle.api.artifacts.maven.MavenResolver;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
-import org.gradle.api.plugins.BasePluginConvention;
-import org.gradle.api.tasks.Upload;
-import org.testng.internal.annotations.Sets;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Given a project dependency, determines the artifact ID that the depended-on project
- * can be referred to from a Maven POM. Falls back to project.name if the artifact ID
- * used for publishing the depended-on project cannot be determined with certainty.
- * 
- * The main goal of this class is to fix GRADLE-443 without changing any other existing
- * behavior (e.g. when a project that gets published to a Maven repo depends on a
- * project published to an Ivy repo).
- *
- * This class should be removed as soon as we have proper support for publications.
- */
-public class ProjectDependencyArtifactIdExtractorHack {
-    private final Project project;
-
-    public ProjectDependencyArtifactIdExtractorHack(ProjectDependency dependency) {
-        this.project = dependency.getDependencyProject();
-    }
-
-    public String extract() {
-        Collection<Upload> tasks = project.getTasks().withType(Upload.class);
-        Collection<ArtifactRepository> repositories = getRepositories(tasks);
-        if (!onlyContainsMavenResolvers(repositories)) { return project.getName(); }
-
-        Collection<MavenDeployer> deployers = getMavenDeployers(repositories);
-        Set<String> artifactIds = getArtifactIds(deployers);
-        if (artifactIds.size() == 1) {
-            String artifactId = artifactIds.iterator().next();
-            if (artifactId != null && !artifactId.equals(MavenProject.EMPTY_PROJECT_ARTIFACT_ID)) {
-                return artifactId;
-            }
-        }
-        String baseName = getArchivesBaseName();
-        return baseName != null ? baseName : project.getName();
-    }
-
-    private Collection<ArtifactRepository> getRepositories(Collection<Upload> tasks) {
-        Collection<ArtifactRepository> result = Lists.newArrayList();
-        for (Upload task : tasks) {
-            result.addAll(task.getRepositories());
-        }
-        return result;
-    }
-
-    private boolean onlyContainsMavenResolvers(Collection<ArtifactRepository> repositories) {
-        for (ArtifactRepository repository : repositories) {
-            if (!(repository instanceof MavenResolver)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private Collection<MavenDeployer> getMavenDeployers(Collection<ArtifactRepository> repositories) {
-        Collection<MavenDeployer> result = Lists.newArrayList();
-        for (ArtifactRepository repository : repositories) {
-            if (repository instanceof MavenDeployer) {
-                result.add((MavenDeployer) repository);
-            }
-        }
-        return result;
-    }
-
-    private Set<String> getArtifactIds(Collection<MavenDeployer> deployers) {
-        Set<String> result = Sets.newHashSet();
-        for (MavenDeployer deployer : deployers) {
-            result.add(deployer.getPom().getArtifactId());
-        }
-        return result;
-    }
-
-    @Nullable
-    private String getArchivesBaseName() {
-        BasePluginConvention convention = project.getConvention().findPlugin(BasePluginConvention.class);
-        return convention != null ? convention.getArchivesBaseName() : null;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryBuilder.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryBuilder.java
deleted file mode 100644
index bff4576..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryBuilder.java
+++ /dev/null
@@ -1,32 +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.publication.maven.internal.ant;
-
-import groovy.util.FactoryBuilderSupport;
-import org.apache.maven.artifact.ant.Authentication;
-import org.apache.maven.artifact.ant.Proxy;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.maven.artifact.ant.RepositoryPolicy;
-
-public class RepositoryBuilder extends FactoryBuilderSupport {
-    public RepositoryBuilder() {
-        registerFactory("repository", new RepositoryFactory(RemoteRepository.class));
-        registerBeanFactory("authentication", Authentication.class);
-        registerBeanFactory("proxy", Proxy.class);
-        registerBeanFactory("snapshots", RepositoryPolicy.class);
-        registerBeanFactory("releases", RepositoryPolicy.class);
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryFactory.java
deleted file mode 100644
index e3517aa..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/ant/RepositoryFactory.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.api.publication.maven.internal.ant;
-
-import groovy.swing.factory.BeanFactory;
-import groovy.util.FactoryBuilderSupport;
-import org.apache.maven.artifact.ant.Authentication;
-import org.apache.maven.artifact.ant.Proxy;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.maven.artifact.ant.RepositoryPolicy;
-
-public class RepositoryFactory extends BeanFactory {
-    public RepositoryFactory(Class klass) {
-        super(klass);
-    }
-
-    public RepositoryFactory(Class klass, boolean leaf) {
-        super(klass, leaf);
-    }
-
-    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
-        if (child instanceof Authentication) {
-            getRepository(parent).addAuthentication((Authentication) child);
-        } else if (child instanceof Proxy) {
-            getRepository(parent).addProxy((Proxy) child);
-        } else if (child instanceof RepositoryPolicy) {
-            if (builder.getCurrentName().equals("snapshots")) {
-                getRepository(parent).addSnapshots((RepositoryPolicy) child);
-            } else {
-                getRepository(parent).addReleases((RepositoryPolicy) child);
-            }
-        }
-    }
-
-    private RemoteRepository getRepository(Object parent) {
-        return (RemoteRepository) parent;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/AbstractMavenResolver.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/AbstractMavenResolver.java
new file mode 100644
index 0000000..d9041ca
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/AbstractMavenResolver.java
@@ -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.publication.maven.internal.deployer;
+
+import groovy.lang.Closure;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.maven.*;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.artifacts.ModuleVersionPublisher;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider;
+import org.gradle.api.internal.artifacts.repositories.AbstractArtifactRepository;
+import org.gradle.api.internal.artifacts.repositories.PublicationAwareRepository;
+import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
+import org.gradle.api.publication.maven.internal.PomFilter;
+import org.gradle.api.publication.maven.internal.action.MavenPublishAction;
+import org.gradle.internal.component.external.model.IvyModuleArtifactPublishMetaData;
+import org.gradle.internal.component.external.model.IvyModulePublishMetaData;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.mvn3.org.apache.maven.settings.building.SettingsBuildingException;
+
+import java.io.File;
+import java.util.Set;
+
+abstract class AbstractMavenResolver extends AbstractArtifactRepository implements MavenResolver, ModuleVersionPublisher, ResolutionAwareRepository, PublicationAwareRepository {
+
+    private ArtifactPomContainer artifactPomContainer;
+
+    private PomFilterContainer pomFilterContainer;
+
+    private LoggingManagerInternal loggingManager;
+
+    private final ActionBroadcast<MavenDeployment> beforeDeploymentActions = new ActionBroadcast<MavenDeployment>();
+
+    private final MavenSettingsProvider mavenSettingsProvider;
+    private final LocalMavenRepositoryLocator mavenRepositoryLocator;
+
+    public AbstractMavenResolver(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer,
+                                 LoggingManagerInternal loggingManager, MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        this.pomFilterContainer = pomFilterContainer;
+        this.artifactPomContainer = artifactPomContainer;
+        this.loggingManager = loggingManager;
+        this.mavenSettingsProvider = mavenSettingsProvider;
+        this.mavenRepositoryLocator = mavenRepositoryLocator;
+    }
+
+    public ConfiguredModuleComponentRepository createResolver() {
+        throw new UnsupportedOperationException("A Maven deployer cannot be used to resolve dependencies. It can only be used to publish artifacts.");
+    }
+
+    public ModuleVersionPublisher createPublisher() {
+        return this;
+    }
+
+    protected abstract MavenPublishAction createPublishAction(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator);
+
+    public void publish(IvyModulePublishMetaData moduleVersion) {
+        for (IvyModuleArtifactPublishMetaData artifact : moduleVersion.getArtifacts()) {
+            collectArtifact(artifact.toIvyArtifact(), artifact.getFile());
+        }
+        publish();
+    }
+
+    private void collectArtifact(Artifact artifact, File src) {
+        if (isIgnorable(artifact)) {
+            return;
+        }
+        getArtifactPomContainer().addArtifact(artifact, src);
+    }
+
+    private boolean isIgnorable(Artifact artifact) {
+        return artifact.getType().equals("ivy");
+    }
+
+    private void publish() {
+        Set<MavenDeployment> mavenDeployments = getArtifactPomContainer().createDeployableFilesInfos();
+        for (MavenDeployment mavenDeployment : mavenDeployments) {
+            File pomFile = mavenDeployment.getPomArtifact().getFile();
+            MavenPublishAction publishAction = createPublishAction(pomFile, mavenRepositoryLocator);
+            beforeDeploymentActions.execute(mavenDeployment);
+            addArtifacts(publishAction, mavenDeployment);
+            execute(publishAction);
+        }
+    }
+
+    private void execute(MavenPublishAction publishAction) {
+        loggingManager.captureStandardOutput(LogLevel.INFO).start();
+        try {
+            publishAction.publish();
+        } finally {
+            loggingManager.stop();
+        }
+    }
+
+    private void addArtifacts(MavenPublishAction publishAction, MavenDeployment mavenDeployment) {
+        if (mavenDeployment.getMainArtifact() != null) {
+            publishAction.setMainArtifact(mavenDeployment.getMainArtifact().getFile());
+        }
+        for (PublishArtifact classifierArtifact : mavenDeployment.getAttachedArtifacts()) {
+            publishAction.addAdditionalArtifact(classifierArtifact.getFile(), classifierArtifact.getType(), classifierArtifact.getClassifier());
+        }
+    }
+
+    public ArtifactPomContainer getArtifactPomContainer() {
+        return artifactPomContainer;
+    }
+
+    public Object getSettings() {
+        try {
+            return mavenSettingsProvider.buildSettings();
+        } catch (SettingsBuildingException e) {
+            throw new GradleException("Could not load Maven Settings", e);
+        }
+    }
+
+    public PublishFilter getFilter() {
+        return pomFilterContainer.getFilter();
+    }
+
+    public void setFilter(PublishFilter defaultFilter) {
+        pomFilterContainer.setFilter(defaultFilter);
+    }
+
+    public MavenPom getPom() {
+        return pomFilterContainer.getPom();
+    }
+
+    public void setPom(MavenPom defaultPom) {
+        pomFilterContainer.setPom(defaultPom);
+    }
+
+    public MavenPom addFilter(String name, PublishFilter publishFilter) {
+        return pomFilterContainer.addFilter(name, publishFilter);
+    }
+
+    public MavenPom addFilter(String name, Closure filter) {
+        return pomFilterContainer.addFilter(name, filter);
+    }
+
+    public void filter(Closure filter) {
+        pomFilterContainer.filter(filter);
+    }
+
+    public PublishFilter filter(String name) {
+        return pomFilterContainer.filter(name);
+    }
+
+    public MavenPom pom(String name) {
+        return pomFilterContainer.pom(name);
+    }
+
+    public MavenPom pom(Closure configureClosure) {
+        return pomFilterContainer.pom(configureClosure);
+    }
+
+    public MavenPom pom(String name, Closure configureClosure) {
+        return pomFilterContainer.pom(name, configureClosure);
+    }
+
+    public Iterable<PomFilter> getActivePomFilters() {
+        return pomFilterContainer.getActivePomFilters();
+    }
+
+    public PomFilterContainer getPomFilterContainer() {
+        return pomFilterContainer;
+    }
+
+    public void beforeDeployment(Action<? super MavenDeployment> action) {
+        beforeDeploymentActions.add(action);
+    }
+
+    public void beforeDeployment(Closure action) {
+        beforeDeploymentActions.add(new ClosureBackedAction<MavenDeployment>(action));
+    }
+
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenDeployer.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenDeployer.java
new file mode 100644
index 0000000..df37170
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenDeployer.java
@@ -0,0 +1,101 @@
+/*
+ * 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.publication.maven.internal.deployer;
+
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.MavenDeployer;
+import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider;
+import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
+import org.gradle.api.publication.maven.internal.action.MavenPublishAction;
+import org.gradle.api.publication.maven.internal.action.MavenWagonDeployAction;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDeployer {
+    private RemoteRepository remoteRepository;
+
+    private RemoteRepository remoteSnapshotRepository;
+
+    private Configuration configuration;
+
+    // todo remove this property once configuration can handle normal file system dependencies
+    private List<File> protocolProviderJars = new ArrayList<File>();
+
+    private boolean uniqueVersion = true;
+
+    public BaseMavenDeployer(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager,
+                             MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        super(pomFilterContainer, artifactPomContainer, loggingManager, mavenSettingsProvider, mavenRepositoryLocator);
+    }
+
+    protected MavenPublishAction createPublishAction(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        MavenWagonDeployAction deployAction = new MavenWagonDeployAction(pomFile);
+        deployAction.setLocalMavenRepositoryLocation(mavenRepositoryLocator.getLocalMavenRepository());
+        deployAction.setUniqueVersion(isUniqueVersion());
+        deployAction.setRepositories(remoteRepository, remoteSnapshotRepository);
+        for (File wagonProviderJar : getJars()) {
+            deployAction.addWagonJar(wagonProviderJar);
+        }
+        return deployAction;
+    }
+
+    private List<File> getJars() {
+        return configuration != null ? new ArrayList<File>(configuration.resolve()) : protocolProviderJars;
+    }
+
+    public RemoteRepository getRepository() {
+        return remoteRepository;
+    }
+
+    public void setRepository(Object remoteRepository) {
+        this.remoteRepository = (RemoteRepository) remoteRepository;
+    }
+
+    public RemoteRepository getSnapshotRepository() {
+        return remoteSnapshotRepository;
+    }
+
+    public void setSnapshotRepository(Object remoteSnapshotRepository) {
+        this.remoteSnapshotRepository = (RemoteRepository) remoteSnapshotRepository;
+    }
+
+    public void addProtocolProviderJars(Collection<File> jars) {
+        protocolProviderJars.addAll(jars);
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    public boolean isUniqueVersion() {
+        return uniqueVersion;
+    }
+
+    public void setUniqueVersion(boolean uniqueVersion) {
+        this.uniqueVersion = uniqueVersion;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenInstaller.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenInstaller.java
new file mode 100644
index 0000000..a8ab7b2
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/BaseMavenInstaller.java
@@ -0,0 +1,39 @@
+/*
+ * 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.publication.maven.internal.deployer;
+
+import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider;
+import org.gradle.api.publication.maven.internal.ArtifactPomContainer;
+import org.gradle.api.publication.maven.internal.action.MavenInstallAction;
+import org.gradle.api.publication.maven.internal.action.MavenPublishAction;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+
+public class BaseMavenInstaller extends AbstractMavenResolver {
+    public BaseMavenInstaller(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager,
+                              MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        super(pomFilterContainer, artifactPomContainer, loggingManager, mavenSettingsProvider, mavenRepositoryLocator);
+    }
+
+    protected MavenPublishAction createPublishAction(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        MavenInstallAction installAction = new MavenInstallAction(pomFile);
+        installAction.setLocalMavenRepositoryLocation(mavenRepositoryLocator.getLocalMavenRepository());
+        return installAction;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployer.groovy b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployer.groovy
new file mode 100644
index 0000000..4d67e1c
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployer.groovy
@@ -0,0 +1,49 @@
+/*
+ * 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.publication.maven.internal.deployer
+import org.codehaus.groovy.runtime.InvokerHelper
+import org.gradle.api.artifacts.maven.GroovyMavenDeployer
+import org.gradle.api.artifacts.maven.PomFilterContainer
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.publication.maven.internal.ArtifactPomContainer
+import org.gradle.logging.LoggingManagerInternal
+
+class DefaultGroovyMavenDeployer extends BaseMavenDeployer implements GroovyMavenDeployer, PomFilterContainer {
+    public static final String REPOSITORY_BUILDER = "repository"
+    public static final String SNAPSHOT_REPOSITORY_BUILDER = 'snapshotRepository'
+    
+    private RepositoryBuilder repositoryBuilder = new RepositoryBuilder()
+
+    DefaultGroovyMavenDeployer(PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager,
+                               MavenSettingsProvider mavenSettingsProvider, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        super(pomFilterContainer, artifactPomContainer, loggingManager, mavenSettingsProvider, mavenRepositoryLocator)
+    }
+    
+    def methodMissing(String name, args) {
+        if (name == REPOSITORY_BUILDER || name == SNAPSHOT_REPOSITORY_BUILDER) {
+            Object repository = InvokerHelper.invokeMethod(repositoryBuilder, REPOSITORY_BUILDER, args)
+            if (name == REPOSITORY_BUILDER) {
+                setRepository(repository)
+            } else {
+                setSnapshotRepository(repository)
+            }
+            return repository;
+        } else {
+            throw new MissingMethodException(name, this.class, args)
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryBuilder.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryBuilder.java
new file mode 100644
index 0000000..5424a99
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.publication.maven.internal.deployer;
+
+import groovy.util.FactoryBuilderSupport;
+import org.apache.maven.artifact.ant.Authentication;
+import org.apache.maven.artifact.ant.Proxy;
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.apache.maven.artifact.ant.RepositoryPolicy;
+
+class RepositoryBuilder extends FactoryBuilderSupport {
+    public RepositoryBuilder() {
+        registerFactory("repository", new RepositoryFactory(RemoteRepository.class));
+        registerBeanFactory("authentication", Authentication.class);
+        registerBeanFactory("proxy", Proxy.class);
+        registerBeanFactory("snapshots", RepositoryPolicy.class);
+        registerBeanFactory("releases", RepositoryPolicy.class);
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryFactory.java
new file mode 100644
index 0000000..482d249
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/deployer/RepositoryFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.deployer;
+
+import groovy.swing.factory.BeanFactory;
+import groovy.util.FactoryBuilderSupport;
+import org.apache.maven.artifact.ant.Authentication;
+import org.apache.maven.artifact.ant.Proxy;
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.apache.maven.artifact.ant.RepositoryPolicy;
+
+public class RepositoryFactory extends BeanFactory {
+    public RepositoryFactory(Class klass) {
+        super(klass);
+    }
+
+    public RepositoryFactory(Class klass, boolean leaf) {
+        super(klass, leaf);
+    }
+
+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
+        if (child instanceof Authentication) {
+            getRepository(parent).addAuthentication((Authentication) child);
+        } else if (child instanceof Proxy) {
+            getRepository(parent).addProxy((Proxy) child);
+        } else if (child instanceof RepositoryPolicy) {
+            if (builder.getCurrentName().equals("snapshots")) {
+                getRepository(parent).addSnapshots((RepositoryPolicy) child);
+            } else {
+                getRepository(parent).addReleases((RepositoryPolicy) child);
+            }
+        }
+    }
+
+    private RemoteRepository getRepository(Object parent) {
+        return (RemoteRepository) parent;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/CustomModelBuilder.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/CustomModelBuilder.java
new file mode 100644
index 0000000..0f5a0dc
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/CustomModelBuilder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import groovy.util.FactoryBuilderSupport;
+import org.apache.maven.model.Model;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
+import org.gradle.api.publication.maven.internal.ModelFactory;
+import org.slf4j.LoggerFactory;
+import org.sonatype.maven.polyglot.execute.ExecuteManager;
+import org.sonatype.maven.polyglot.execute.ExecuteManagerImpl;
+import org.sonatype.maven.polyglot.groovy.builder.ModelBuilder;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+/**
+ * This is a slightly modified version as shipped with polyglot Maven.
+ */
+public class CustomModelBuilder extends ModelBuilder {
+
+    public CustomModelBuilder(Model model) {
+        ExecuteManager executeManager = new ExecuteManagerImpl();
+        setProp(executeManager.getClass(), executeManager, "log",
+                new PlexusLoggerAdapter(LoggerFactory.getLogger(ExecuteManagerImpl.class)));
+        setProp(ModelBuilder.class, this, "executeManager", executeManager);
+        setProp(ModelBuilder.class, this, "log",
+                new PlexusLoggerAdapter(LoggerFactory.getLogger(ModelBuilder.class)));
+        try {
+            initialize();
+        } catch (InitializationException e) {
+            throw new RuntimeException(e);
+        }
+        Map factories = (Map) getProp(FactoryBuilderSupport.class, this, "factories");
+        factories.remove("project");
+        ModelFactory modelFactory = new ModelFactory(model);
+        registerFactory(modelFactory.getName(), null, modelFactory);
+    }
+
+    public static void setProp(Class c, Object obj, String fieldName, Object value) {
+        try {
+            Field f = c.getDeclaredField(fieldName);
+            f.setAccessible(true); // solution
+            f.set(obj, value); // IllegalAccessException
+            // production code should handle these exceptions more gracefully
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalArgumentException e) {
+           throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+           throw new RuntimeException(e);
+        }
+    }
+
+    public static Object getProp(Class c, Object obj, String fieldName) {
+        try {
+            Field f = c.getDeclaredField(fieldName);
+            f.setAccessible(true); // solution
+            return f.get(obj); // IllegalAccessException
+            // production code should handle these exceptions more gracefully
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalArgumentException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainer.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainer.java
new file mode 100644
index 0000000..46476df
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainer.java
@@ -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.publication.maven.internal.pom;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.util.WrapUtil;
+
+import java.util.*;
+
+public class DefaultConf2ScopeMappingContainer implements Conf2ScopeMappingContainer {
+    private Map<Configuration, Conf2ScopeMapping> mappings = new HashMap<Configuration, Conf2ScopeMapping>();
+
+    private boolean skipUnmappedConfs = true;
+
+    public DefaultConf2ScopeMappingContainer() {
+    }
+
+    public DefaultConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings) {
+        this.mappings.putAll(mappings);
+    }
+
+    public Conf2ScopeMapping getMapping(Collection<Configuration> configurations) {
+        Set<Conf2ScopeMapping> result = getMappingsWithHighestPriority(configurations);
+        if (result.size() > 1) {
+            throw new InvalidUserDataException(
+                    "The configuration to scope mapping is not unique. The following configurations "
+                            + "have the same priority: " + result);
+        }
+        return result.size() == 0 ? null : result.iterator().next();
+    }
+
+    private Set<Conf2ScopeMapping> getMappingsWithHighestPriority(Collection<Configuration> configurations) {
+        Integer lastPriority = null;
+        Set<Conf2ScopeMapping> result = new HashSet<Conf2ScopeMapping>();
+        for (Conf2ScopeMapping conf2ScopeMapping : getMappingsForConfigurations(configurations)) {
+            Integer thisPriority = conf2ScopeMapping.getPriority();
+            if (lastPriority != null && lastPriority.equals(thisPriority)) {
+                result.add(conf2ScopeMapping);
+            } else if (lastPriority == null || (thisPriority != null && lastPriority < thisPriority)) {
+                lastPriority = thisPriority;
+                result = WrapUtil.toSet(conf2ScopeMapping);
+            }
+        }
+        return result;
+    }
+
+    private List<Conf2ScopeMapping> getMappingsForConfigurations(Collection<Configuration> configurations) {
+        List<Conf2ScopeMapping> existingMappings = new ArrayList<Conf2ScopeMapping>();
+        for (Configuration configuration : configurations) {
+            if (mappings.get(configuration) != null) {
+                existingMappings.add(mappings.get(configuration));
+            } else {
+                existingMappings.add(new Conf2ScopeMapping(null, configuration, null));
+            }
+        }
+        return existingMappings;
+    }
+
+    public Conf2ScopeMappingContainer addMapping(int priority, Configuration configuration, String scope) {
+        mappings.put(configuration, new Conf2ScopeMapping(priority, configuration, scope));
+        return this;
+    }
+
+    public Map<Configuration, Conf2ScopeMapping> getMappings() {
+        return mappings;
+    }
+
+    public boolean isSkipUnmappedConfs() {
+        return skipUnmappedConfs;
+    }
+
+    public void setSkipUnmappedConfs(boolean skipUnmappedConfs) {
+        this.skipUnmappedConfs = skipUnmappedConfs;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultConf2ScopeMappingContainer that = (DefaultConf2ScopeMappingContainer) o;
+
+        if (!mappings.equals(that.mappings)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public int hashCode() {
+        return mappings.hashCode();
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverter.java
new file mode 100644
index 0000000..1268de8
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.publication.maven.internal.pom;
+
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.artifacts.ExcludeRule;
+
+class DefaultExcludeRuleConverter implements ExcludeRuleConverter {
+    public Exclusion convert(ExcludeRule excludeRule) {
+        if (isConvertable(excludeRule)) {
+            Exclusion exclusion = new Exclusion();
+            exclusion.setGroupId(excludeRule.getGroup());
+            exclusion.setArtifactId(excludeRule.getModule());
+            return exclusion;
+        }
+        return null;
+    }
+
+    private boolean isConvertable(ExcludeRule excludeRule) {
+        return excludeRule.getGroup()!=null && excludeRule.getModule()!=null;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenFactory.java
new file mode 100644
index 0000000..f39dae1
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.publication.maven.internal.*;
+import org.gradle.internal.Factory;
+
+import java.util.Map;
+
+public class DefaultMavenFactory implements MavenFactory {
+    private final VersionRangeMapper versionRangeMapper;
+
+    public DefaultMavenFactory(VersionRangeMapper versionRangeMapper) {
+        this.versionRangeMapper = versionRangeMapper;
+    }
+
+    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, FileResolver fileResolver) {
+        return new DefaultMavenPomFactory(configurationContainer, conf2ScopeMappingContainer, createPomDependenciesConverter(), fileResolver);
+    }
+
+    public Factory<MavenPom> createMavenPomFactory(ConfigurationContainer configurationContainer, Map<Configuration, Conf2ScopeMapping> mappings, FileResolver fileResolver) {
+        return new DefaultMavenPomFactory(configurationContainer, createConf2ScopeMappingContainer(mappings), createPomDependenciesConverter(), fileResolver);
+    }
+
+    private PomDependenciesConverter createPomDependenciesConverter() {
+        return new DefaultPomDependenciesConverter(new DefaultExcludeRuleConverter(), versionRangeMapper);
+    }
+
+    public Conf2ScopeMappingContainer createConf2ScopeMappingContainer(Map<Configuration, Conf2ScopeMapping> mappings) {
+        return new DefaultConf2ScopeMappingContainer(mappings);
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPom.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPom.java
new file mode 100644
index 0000000..2c6a4d6
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPom.java
@@ -0,0 +1,236 @@
+/*
+ * 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.publication.maven.internal.pom;
+
+import groovy.lang.Closure;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.XmlProvider;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.internal.ErroringAction;
+import org.gradle.internal.IoActions;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.xml.XmlTransformer;
+import org.gradle.listener.ActionBroadcast;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultMavenPom implements MavenPom {
+
+    private PomDependenciesConverter pomDependenciesConverter;
+    private FileResolver fileResolver;
+    private MavenProject mavenProject = new MavenProject();
+    private Conf2ScopeMappingContainer scopeMappings;
+    private ActionBroadcast<MavenPom> whenConfiguredActions = new ActionBroadcast<MavenPom>();
+    private XmlTransformer withXmlActions = new XmlTransformer();
+    private ConfigurationContainer configurations;
+
+    public DefaultMavenPom(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMappings, PomDependenciesConverter pomDependenciesConverter,
+                           FileResolver fileResolver) {
+        this.configurations = configurationContainer;
+        this.scopeMappings = scopeMappings;
+        this.pomDependenciesConverter = pomDependenciesConverter;
+        this.fileResolver = fileResolver;
+        mavenProject.setModelVersion("4.0.0");
+    }
+
+    public Conf2ScopeMappingContainer getScopeMappings() {
+        return scopeMappings;
+    }
+
+    public ConfigurationContainer getConfigurations() {
+        return configurations;
+    }
+
+    public DefaultMavenPom setConfigurations(ConfigurationContainer configurations) {
+        this.configurations = configurations;
+        return this;
+    }
+
+    public DefaultMavenPom setGroupId(String groupId) {
+        getModel().setGroupId(groupId);
+        return this;
+    }
+
+    public String getGroupId() {
+        return getModel().getGroupId();
+    }
+
+    public DefaultMavenPom setArtifactId(String artifactId) {
+        getModel().setArtifactId(artifactId);
+        return this;
+    }
+
+    public String getArtifactId() {
+        return getModel().getArtifactId();
+    }
+
+    @SuppressWarnings("unchecked")
+    public DefaultMavenPom setDependencies(List<?> dependencies) {
+        getModel().setDependencies((List<Dependency>) dependencies);
+        return this;
+    }
+
+    public List<Dependency> getDependencies() {
+        return getModel().getDependencies();
+    }
+
+    public DefaultMavenPom setName(String name) {
+        getModel().setName(name);
+        return this;
+    }
+
+    public String getName() {
+        return getModel().getName();
+    }
+
+    public DefaultMavenPom setVersion(String version) {
+        getModel().setVersion(version);
+        return this;
+    }
+
+    public String getVersion() {
+        return getModel().getVersion();
+    }
+
+    public String getPackaging() {
+        return getModel().getPackaging();
+    }
+
+    public DefaultMavenPom setPackaging(String packaging) {
+        getModel().setPackaging(packaging);
+        return this;
+    }
+
+    public DefaultMavenPom project(Closure cl) {
+        CustomModelBuilder pomBuilder = new CustomModelBuilder(getModel());
+        InvokerHelper.invokeMethod(pomBuilder, "project", cl);
+        return this;
+    }
+
+    public Model getModel() {
+        return mavenProject.getModel();
+    }
+
+    public DefaultMavenPom setModel(Object model) {
+        this.mavenProject = new MavenProject((Model) model);
+        return this;
+    }
+
+    public MavenProject getMavenProject() {
+        return mavenProject;
+    }
+
+    public DefaultMavenPom setMavenProject(MavenProject mavenProject) {
+        this.mavenProject = mavenProject;
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<Dependency> getGeneratedDependencies() {
+        if (configurations == null) {
+            return Collections.emptyList();
+        }
+        return (List<Dependency>) pomDependenciesConverter.convert(getScopeMappings(), configurations);
+    }
+
+    public DefaultMavenPom getEffectivePom() {
+        DefaultMavenPom effectivePom = new DefaultMavenPom(null, this.scopeMappings, pomDependenciesConverter, fileResolver);
+        try {
+            effectivePom.setMavenProject((MavenProject) mavenProject.clone());
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+        effectivePom.getDependencies().addAll(getGeneratedDependencies());
+        effectivePom.withXmlActions = withXmlActions;
+        whenConfiguredActions.execute(effectivePom);
+        return effectivePom;
+    }
+
+    public PomDependenciesConverter getPomDependenciesConverter() {
+        return pomDependenciesConverter;
+    }
+
+    public FileResolver getFileResolver() {
+        return fileResolver;
+    }
+
+    public DefaultMavenPom setFileResolver(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+        return this;
+    }
+
+    public DefaultMavenPom writeTo(final Writer pomWriter) {
+        try {
+            getEffectivePom().writeNonEffectivePom(pomWriter);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return this;
+    }
+
+    public DefaultMavenPom writeTo(Object path) {
+        IoActions.writeTextFile(fileResolver.resolve(path), POM_FILE_ENCODING, new Action<BufferedWriter>() {
+            public void execute(BufferedWriter writer) {
+                writeTo(writer);
+            }
+        });
+        return this;
+    }
+
+    private void writeNonEffectivePom(final Writer pomWriter) throws IOException {
+        try {
+            withXmlActions.transform(pomWriter, POM_FILE_ENCODING, new ErroringAction<Writer>() {
+                protected void doExecute(Writer writer) throws IOException {
+                    mavenProject.writeModel(writer);
+                }
+            });
+        } finally {
+            pomWriter.close();
+        }
+    }
+
+    public DefaultMavenPom whenConfigured(final Closure closure) {
+        whenConfiguredActions.add(new ClosureBackedAction<MavenPom>(closure));
+        return this;
+    }
+
+    public DefaultMavenPom whenConfigured(final Action<MavenPom> action) {
+        whenConfiguredActions.add(action);
+        return this;
+    }
+
+    public DefaultMavenPom withXml(final Closure closure) {
+        withXmlActions.addAction(closure);
+        return this;
+    }
+
+    public DefaultMavenPom withXml(final Action<XmlProvider> action) {
+        withXmlActions.addAction(action);
+        return this;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactory.java
new file mode 100644
index 0000000..53f032f
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.Factory;
+
+public class DefaultMavenPomFactory implements Factory<MavenPom> {
+    private ConfigurationContainer configurationContainer;
+    private Conf2ScopeMappingContainer conf2ScopeMappingContainer;
+    private PomDependenciesConverter pomDependenciesConverter;
+    private FileResolver fileResolver;
+
+
+    public DefaultMavenPomFactory(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer conf2ScopeMappingContainer, PomDependenciesConverter pomDependenciesConverter,
+                                  FileResolver fileResolver) {
+        this.configurationContainer = configurationContainer;
+        this.conf2ScopeMappingContainer = conf2ScopeMappingContainer;
+        this.pomDependenciesConverter = pomDependenciesConverter;
+        this.fileResolver = fileResolver;
+    }
+
+    public MavenPom create() {
+        return new DefaultMavenPom(configurationContainer,
+                new DefaultConf2ScopeMappingContainer(conf2ScopeMappingContainer.getMappings()), pomDependenciesConverter, fileResolver);
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverter.java
new file mode 100644
index 0000000..fde1945
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverter.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.*;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+import org.gradle.api.publication.maven.internal.VersionRangeMapper;
+
+import java.util.*;
+
+class DefaultPomDependenciesConverter implements PomDependenciesConverter {
+    private ExcludeRuleConverter excludeRuleConverter;
+    private VersionRangeMapper versionRangeMapper;
+
+    public DefaultPomDependenciesConverter(ExcludeRuleConverter excludeRuleConverter, VersionRangeMapper versionRangeMapper) {
+        this.excludeRuleConverter = excludeRuleConverter;
+        this.versionRangeMapper = versionRangeMapper;
+    }
+
+    public List<org.apache.maven.model.Dependency> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations) {
+        Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations = createDependencyToConfigurationsMap(configurations);
+        Map<ModuleDependency, String> dependenciesMap = createDependencyToScopeMap(conf2ScopeMappingContainer, dependencyToConfigurations);
+        List<org.apache.maven.model.Dependency> mavenDependencies = new ArrayList<org.apache.maven.model.Dependency>();
+        for (ModuleDependency dependency : dependenciesMap.keySet()) {
+            String scope = dependenciesMap.get(dependency);
+            Set<Configuration> dependencyConfigurations = dependencyToConfigurations.get(dependency);
+            if (dependency.getArtifacts().size() == 0) {
+                addFromDependencyDescriptor(mavenDependencies, dependency, scope, dependencyConfigurations);
+            } else {
+                addFromArtifactDescriptor(mavenDependencies, dependency, scope, dependencyConfigurations);
+            }
+        }
+        return mavenDependencies;
+    }
+    
+    private Map<ModuleDependency, String> createDependencyToScopeMap(Conf2ScopeMappingContainer conf2ScopeMappingContainer, 
+            Map<ModuleDependency, Set<Configuration>> dependencyToConfigurations) {
+        Map<ModuleDependency, String> dependencyToScope = new HashMap<ModuleDependency, String>();
+        for (ModuleDependency dependency : dependencyToConfigurations.keySet()) {
+            Conf2ScopeMapping conf2ScopeDependencyMapping = conf2ScopeMappingContainer.getMapping(dependencyToConfigurations.get(dependency));
+            if (!useScope(conf2ScopeMappingContainer, conf2ScopeDependencyMapping)) {
+                continue;
+            }
+            dependencyToScope.put(findDependency(dependency, conf2ScopeDependencyMapping.getConfiguration()),
+                    conf2ScopeDependencyMapping.getScope());
+        }
+        return dependencyToScope;
+    }
+
+    private ModuleDependency findDependency(ModuleDependency dependency, Configuration configuration) {
+        for (ModuleDependency configurationDependency : configuration.getDependencies().withType(ModuleDependency.class)) {
+            if (dependency.equals(configurationDependency)) {
+                return configurationDependency;
+            }
+        }
+        throw new GradleException("Dependency could not be found. We should never get here!");
+    }
+
+    private boolean useScope(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Conf2ScopeMapping conf2ScopeMapping) {
+        return conf2ScopeMapping.getScope() != null || !conf2ScopeMappingContainer.isSkipUnmappedConfs();
+    }
+
+    private Map<ModuleDependency, Set<Configuration>> createDependencyToConfigurationsMap(Set<Configuration> configurations) {
+        Map<ModuleDependency, Set<Configuration>> dependencySetMap = new HashMap<ModuleDependency, Set<Configuration>>();
+        for (Configuration configuration : configurations) {
+            for (ModuleDependency dependency : configuration.getDependencies().withType(ModuleDependency.class)) {
+                if (dependencySetMap.get(dependency) == null) {
+                    dependencySetMap.put(dependency, new HashSet<Configuration>());
+                }
+                dependencySetMap.get(dependency).add(configuration);
+            }
+        }
+        return dependencySetMap;
+    }
+
+    private void addFromArtifactDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
+            Set<Configuration> configurations) {
+        for (DependencyArtifact artifact : dependency.getArtifacts()) {
+            mavenDependencies.add(createMavenDependencyFromArtifactDescriptor(dependency, artifact, scope, configurations));
+        }
+    }
+
+    private void addFromDependencyDescriptor(List<Dependency> mavenDependencies, ModuleDependency dependency, String scope, 
+            Set<Configuration> configurations) {
+        mavenDependencies.add(createMavenDependencyFromDependencyDescriptor(dependency, scope, configurations));
+    }
+
+    private Dependency createMavenDependencyFromArtifactDescriptor(ModuleDependency dependency, DependencyArtifact artifact, String scope,
+            Set<Configuration> configurations) {
+        return createMavenDependency(dependency, artifact.getName(), artifact.getType(), scope, artifact.getClassifier(), configurations);
+    }
+
+    private Dependency createMavenDependencyFromDependencyDescriptor(ModuleDependency dependency, String scope, Set<Configuration> configurations) {
+        return createMavenDependency(dependency, dependency.getName(), null, scope, null, configurations);
+    }
+
+    private Dependency createMavenDependency(ModuleDependency dependency, String name, String type, String scope, String classifier,
+            Set<Configuration> configurations) {
+        Dependency mavenDependency =  new Dependency();
+        mavenDependency.setGroupId(dependency.getGroup());
+        if (dependency instanceof ProjectDependency) {
+            mavenDependency.setArtifactId(determineProjectDependencyArtifactId((ProjectDependency) dependency));
+        } else {
+            mavenDependency.setArtifactId(name);
+        }
+        mavenDependency.setVersion(mapToMavenSyntax(dependency.getVersion()));
+        mavenDependency.setType(type);
+        mavenDependency.setScope(scope);
+        mavenDependency.setOptional(false);
+        mavenDependency.setClassifier(classifier);
+        mavenDependency.setExclusions(getExclusions(dependency, configurations));
+        return mavenDependency;
+    }
+
+    private String mapToMavenSyntax(String version) {
+       return versionRangeMapper.map(version);
+    }
+
+    protected String determineProjectDependencyArtifactId(ProjectDependency dependency) {
+        return new ProjectDependencyArtifactIdExtractorHack(dependency).extract();
+    }
+
+    private List<Exclusion> getExclusions(ModuleDependency dependency, Set<Configuration> configurations) {
+        List<Exclusion> mavenExclusions = new ArrayList<Exclusion>();
+        Set<ExcludeRule> excludeRules = new HashSet<ExcludeRule>(dependency.getExcludeRules());
+        for (Configuration configuration : configurations) {
+            excludeRules.addAll(configuration.getExcludeRules());
+        }
+        for (ExcludeRule excludeRule : excludeRules) {
+            Exclusion mavenExclusion = (Exclusion) excludeRuleConverter.convert(excludeRule);
+            if (mavenExclusion != null) {
+                mavenExclusions.add(mavenExclusion);
+            }
+        }
+        return mavenExclusions;
+    }
+
+    public ExcludeRuleConverter getExcludeRuleConverter() {
+        return excludeRuleConverter;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ExcludeRuleConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ExcludeRuleConverter.java
new file mode 100644
index 0000000..69a5bdc
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ExcludeRuleConverter.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.api.publication.maven.internal.pom;
+
+import org.gradle.api.artifacts.ExcludeRule;
+
+
+public interface ExcludeRuleConverter {
+    Object convert(ExcludeRule excludeRule);
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PlexusLoggerAdapter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PlexusLoggerAdapter.java
new file mode 100644
index 0000000..ff4e09a
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PlexusLoggerAdapter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import org.codehaus.plexus.logging.Logger;
+
+public class PlexusLoggerAdapter implements Logger {
+    org.slf4j.Logger logger;
+
+    public PlexusLoggerAdapter(org.slf4j.Logger logger) {
+        this.logger = logger;
+    }
+
+    public void debug(String s) {
+        logger.debug(s);
+    }
+
+    public void debug(String s, Throwable throwable) {
+        logger.debug(s, throwable);
+    }
+
+    public boolean isDebugEnabled() {
+        return logger.isDebugEnabled();
+    }
+
+    public void info(String s) {
+        logger.info(s);
+    }
+
+    public void info(String s, Throwable throwable) {
+        logger.info(s, throwable);
+    }
+
+    public boolean isInfoEnabled() {
+        return logger.isInfoEnabled();
+    }
+
+    public void warn(String s) {
+        logger.warn(s);
+    }
+
+    public void warn(String s, Throwable throwable) {
+        logger.warn(s, throwable);
+    }
+
+    public boolean isWarnEnabled() {
+        return logger.isWarnEnabled();
+    }
+
+    public void error(String s) {
+        logger.error(s);
+    }
+
+    public void error(String s, Throwable throwable) {
+        logger.error(s, throwable);
+    }
+
+    public boolean isErrorEnabled() {
+        return logger.isErrorEnabled();
+    }
+
+    public void fatalError(String s) {
+        logger.error(s);
+    }
+
+    public void fatalError(String s, Throwable throwable) {
+        logger.error(s, throwable);
+    }
+
+    public boolean isFatalErrorEnabled() {
+        return logger.isErrorEnabled();
+    }
+
+    public Logger getChildLogger(String s) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getThreshold() {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getName() {
+        return logger.getName();
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PomDependenciesConverter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PomDependenciesConverter.java
new file mode 100644
index 0000000..9003f87
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/PomDependenciesConverter.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.publication.maven.internal.pom;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
+
+import java.util.List;
+import java.util.Set;
+
+public interface PomDependenciesConverter {
+    public List<?> convert(Conf2ScopeMappingContainer conf2ScopeMappingContainer, Set<Configuration> configurations);
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHack.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHack.java
new file mode 100644
index 0000000..f050b5a
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHack.java
@@ -0,0 +1,111 @@
+/*
+ * 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.publication.maven.internal.pom;
+
+import com.google.common.collect.Lists;
+import org.apache.maven.project.MavenProject;
+import org.gradle.api.Nullable;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.artifacts.maven.MavenDeployer;
+import org.gradle.api.artifacts.maven.MavenResolver;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+import org.gradle.api.plugins.BasePluginConvention;
+import org.gradle.api.tasks.Upload;
+import org.testng.internal.annotations.Sets;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Given a project dependency, determines the artifact ID that the depended-on project
+ * can be referred to from a Maven POM. Falls back to project.name if the artifact ID
+ * used for publishing the depended-on project cannot be determined with certainty.
+ * 
+ * The main goal of this class is to fix GRADLE-443 without changing any other existing
+ * behavior (e.g. when a project that gets published to a Maven repo depends on a
+ * project published to an Ivy repo).
+ *
+ * This class should be removed as soon as we have proper support for publications.
+ */
+class ProjectDependencyArtifactIdExtractorHack {
+    private final Project project;
+
+    public ProjectDependencyArtifactIdExtractorHack(ProjectDependency dependency) {
+        this.project = dependency.getDependencyProject();
+    }
+
+    public String extract() {
+        Collection<Upload> tasks = project.getTasks().withType(Upload.class);
+        Collection<ArtifactRepository> repositories = getRepositories(tasks);
+        if (!onlyContainsMavenResolvers(repositories)) {
+            return project.getName();
+        }
+
+        Collection<MavenDeployer> deployers = getMavenDeployers(repositories);
+        Set<String> artifactIds = getArtifactIds(deployers);
+        if (artifactIds.size() == 1) {
+            String artifactId = artifactIds.iterator().next();
+            if (artifactId != null && !artifactId.equals(MavenProject.EMPTY_PROJECT_ARTIFACT_ID)) {
+                return artifactId;
+            }
+        }
+        String baseName = getArchivesBaseName();
+        return baseName != null ? baseName : project.getName();
+    }
+
+    private Collection<ArtifactRepository> getRepositories(Collection<Upload> tasks) {
+        Collection<ArtifactRepository> result = Lists.newArrayList();
+        for (Upload task : tasks) {
+            result.addAll(task.getRepositories());
+        }
+        return result;
+    }
+
+    private boolean onlyContainsMavenResolvers(Collection<ArtifactRepository> repositories) {
+        for (ArtifactRepository repository : repositories) {
+            if (!(repository instanceof MavenResolver)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private Collection<MavenDeployer> getMavenDeployers(Collection<ArtifactRepository> repositories) {
+        Collection<MavenDeployer> result = Lists.newArrayList();
+        for (ArtifactRepository repository : repositories) {
+            if (repository instanceof MavenDeployer) {
+                result.add((MavenDeployer) repository);
+            }
+        }
+        return result;
+    }
+
+    private Set<String> getArtifactIds(Collection<MavenDeployer> deployers) {
+        Set<String> result = Sets.newHashSet();
+        for (MavenDeployer deployer : deployers) {
+            result.add(deployer.getPom().getArtifactId());
+        }
+        return result;
+    }
+
+    @Nullable
+    private String getArchivesBaseName() {
+        BasePluginConvention convention = project.getConvention().findPlugin(BasePluginConvention.class);
+        return convention != null ? convention.getArchivesBaseName() : null;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagon.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagon.java
new file mode 100644
index 0000000..4ce29f0
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagon.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.wagon;
+
+
+import org.apache.maven.wagon.ConnectionException;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.Wagon;
+import org.apache.maven.wagon.authentication.AuthenticationException;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.events.*;
+import org.apache.maven.wagon.proxy.ProxyInfo;
+import org.apache.maven.wagon.proxy.ProxyInfoProvider;
+import org.apache.maven.wagon.repository.Repository;
+import org.apache.maven.wagon.resource.Resource;
+import org.gradle.api.GradleException;
+import org.gradle.internal.resource.local.FileLocalResource;
+import org.gradle.internal.resource.local.LocalResource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.List;
+
+import static org.apache.maven.wagon.events.SessionEvent.*;
+import static org.apache.maven.wagon.events.TransferEvent.*;
+
+/**
+ * A maven wagon intended to work with {@link org.apache.maven.artifact.manager.DefaultWagonManager} Maven uses reflection to initialize instances of this wagon see: {@link
+ * org.codehaus.plexus.component.factory.java.JavaComponentFactory#newInstance(org.codehaus.plexus.component.repository.ComponentDescriptor, org.codehaus.classworlds.ClassRealm,
+ * org.codehaus.plexus.PlexusContainer)}
+ */
+public class RepositoryTransportDeployWagon implements Wagon {
+
+    private static ThreadLocal<RepositoryTransportWagonAdapter> currentDelegate = new InheritableThreadLocal<RepositoryTransportWagonAdapter>();
+
+    private SessionEventSupport sessionEventSupport = new SessionEventSupport();
+    private TransferEventSupport transferEventSupport = new TransferEventSupport();
+    private Repository mutatingRepository;
+
+    public static void contextualize(RepositoryTransportWagonAdapter adapter) {
+        currentDelegate.set(adapter);
+    }
+
+    public static void decontextualize() {
+        currentDelegate.remove();
+    }
+
+    @Override
+    public final void get(String resourceName, File destination) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
+        Resource resource = new Resource(resourceName);
+        this.transferEventSupport.fireTransferInitiated(transferEvent(resource, TRANSFER_INITIATED, REQUEST_GET));
+        this.transferEventSupport.fireTransferStarted(transferEvent(resource, TRANSFER_STARTED, REQUEST_GET));
+        try {
+            if (!destination.exists()) {
+                destination.getParentFile().mkdirs();
+                destination.createNewFile();
+            }
+            if (!getDelegate().getRemoteFile(destination, resourceName)) {
+                throw new ResourceDoesNotExistException(String.format("Resource '%s' does not exist", resourceName));
+            }
+            this.transferEventSupport.fireTransferCompleted(transferEvent(resource, TRANSFER_COMPLETED, REQUEST_GET));
+        } catch (ResourceDoesNotExistException e) {
+            this.transferEventSupport.fireTransferError(transferEvent(resource, e, REQUEST_GET));
+            throw e;
+        } catch (Exception e) {
+            this.transferEventSupport.fireTransferError(transferEvent(resource, e, REQUEST_GET));
+            throw new TransferFailedException(String.format("Could not get resource '%s'", resourceName), e);
+        }
+    }
+
+    @Override
+    public final void put(File file, String resourceName) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
+        Resource resource = new Resource(resourceName);
+        this.transferEventSupport.fireTransferInitiated(transferEvent(resource, TRANSFER_INITIATED, REQUEST_PUT));
+        try {
+            LocalResource localResource = new MavenTransferLoggingFileResource(file, resource);
+            getDelegate().putRemoteFile(localResource, resourceName);
+        } catch (Exception e) {
+            this.transferEventSupport.fireTransferError(transferEvent(resource, e, REQUEST_PUT));
+            throw new TransferFailedException(String.format("Could not write to resource '%s'", resourceName), e);
+        }
+        this.transferEventSupport.fireTransferCompleted(transferEvent(resource, TRANSFER_COMPLETED, REQUEST_PUT));
+    }
+
+    private RepositoryTransportWagonAdapter getDelegate() {
+        return currentDelegate.get();
+    }
+
+    @Override
+    public final boolean resourceExists(String resourceName) throws TransferFailedException, AuthorizationException {
+        throwNotImplemented("getIfNewer(String resourceName, File file, long timestamp)");
+        return false;
+    }
+
+    @Override
+    public final boolean getIfNewer(String resourceName, File file, long timestamp) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
+        throwNotImplemented("getIfNewer(String resourceName, File file, long timestamp)");
+        return false;
+    }
+
+    @Override
+    public final void putDirectory(File file, String resourceName) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
+        throwNotImplemented("putDirectory(File file, String resourceName)");
+    }
+
+    @Override
+    public final List getFileList(String resourceName) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
+        throwNotImplemented("getFileList(String resourceName)");
+        return null;
+    }
+
+    @Override
+    public final boolean supportsDirectoryCopy() {
+        return false;
+    }
+
+    @Override
+    public final Repository getRepository() {
+        return this.mutatingRepository;
+    }
+
+    @Override
+    public final void openConnection() throws ConnectionException, AuthenticationException {
+    }
+
+    @Override
+    public final void connect(Repository repository) throws ConnectionException, AuthenticationException {
+        this.mutatingRepository = repository;
+        this.sessionEventSupport.fireSessionLoggedIn(sessionEvent(SESSION_LOGGED_IN));
+        this.sessionEventSupport.fireSessionOpened(sessionEvent(SESSION_OPENED));
+    }
+
+    @Override
+    public final void connect(Repository repository, ProxyInfo proxyInfo) throws ConnectionException, AuthenticationException {
+        connect(repository);
+    }
+
+    @Override
+    public final void connect(Repository repository, ProxyInfoProvider proxyInfoProvider) throws ConnectionException, AuthenticationException {
+        connect(repository);
+    }
+
+    @Override
+    public final void connect(Repository repository, AuthenticationInfo authenticationInfo) throws ConnectionException, AuthenticationException {
+        connect(repository);
+    }
+
+    @Override
+    public final void connect(Repository repository, AuthenticationInfo authenticationInfo, ProxyInfo proxyInfo) throws ConnectionException, AuthenticationException {
+        connect(repository);
+    }
+
+    @Override
+    public final void connect(Repository repository, AuthenticationInfo authenticationInfo, ProxyInfoProvider proxyInfoProvider) throws ConnectionException, AuthenticationException {
+        connect(repository);
+    }
+
+    @Override
+    public final void disconnect() throws ConnectionException {
+        this.sessionEventSupport.fireSessionDisconnecting(sessionEvent(SESSION_DISCONNECTING));
+        this.sessionEventSupport.fireSessionLoggedOff(sessionEvent(SESSION_LOGGED_OFF));
+        this.sessionEventSupport.fireSessionDisconnected(sessionEvent(SESSION_LOGGED_OFF));
+    }
+
+    @Override
+    public final void addSessionListener(SessionListener sessionListener) {
+        this.sessionEventSupport.addSessionListener(sessionListener);
+    }
+
+    @Override
+    public final void removeSessionListener(SessionListener sessionListener) {
+        this.sessionEventSupport.removeSessionListener(sessionListener);
+    }
+
+    @Override
+    public final boolean hasSessionListener(SessionListener sessionListener) {
+        return this.sessionEventSupport.hasSessionListener(sessionListener);
+    }
+
+    @Override
+    public final void addTransferListener(TransferListener transferListener) {
+        this.transferEventSupport.addTransferListener(transferListener);
+    }
+
+    @Override
+    public final void removeTransferListener(TransferListener transferListener) {
+        this.transferEventSupport.removeTransferListener(transferListener);
+    }
+
+    @Override
+    public final boolean hasTransferListener(TransferListener transferListener) {
+        return this.transferEventSupport.hasTransferListener(transferListener);
+    }
+
+    @Override
+    public final boolean isInteractive() {
+        return false;
+    }
+
+    @Override
+    public final void setInteractive(boolean b) {
+
+    }
+
+    @Override
+    public final void setTimeout(int i) {
+
+    }
+
+    @Override
+    public final int getTimeout() {
+        return 0;
+    }
+
+    private SessionEvent sessionEvent(int e) {
+        return new SessionEvent(this, e);
+    }
+
+    private void throwNotImplemented(String s) {
+        throw new GradleException("This wagon does not yet support the method:" + s);
+    }
+
+    private TransferEvent transferEvent(Resource resource, int eventType, int requestType) {
+        TransferEvent transferEvent = new TransferEvent(this, resource, eventType, requestType);
+        transferEvent.setTimestamp(new Date().getTime());
+        return transferEvent;
+    }
+
+    private TransferEvent transferEvent(Resource resource, Exception e, int requestType) {
+        return new TransferEvent(this, resource, e, requestType);
+    }
+
+    private class MavenTransferLoggingFileResource extends FileLocalResource {
+        private final Resource resource;
+
+        private MavenTransferLoggingFileResource(File file, Resource resource) {
+            super(file);
+            this.resource = resource;
+        }
+
+        @Override
+        public InputStream open() {
+            // Need to do this here, so that the transfer is 'restarted' when HttpClient reopens the resource (DIGEST AUTH only)
+            transferEventSupport.fireTransferStarted(transferEvent(resource, TRANSFER_STARTED, REQUEST_PUT));
+            return new ObservingInputStream(super.open(), resource);
+        }
+
+        protected class ObservingInputStream extends InputStream {
+            private final InputStream inputStream;
+            private final TransferEvent transferEvent;
+            private final byte[] singleByteBuffer = new byte[1];
+
+            public ObservingInputStream(InputStream inputStream, Resource resource) {
+                this.inputStream = inputStream;
+                this.transferEvent = transferEvent(resource, TransferEvent.TRANSFER_PROGRESS, REQUEST_PUT);
+            }
+
+            @Override
+            public void close() throws IOException {
+                inputStream.close();
+            }
+
+            @Override
+            public int read() throws IOException {
+                int result = inputStream.read();
+                if (result >= 0) {
+                    singleByteBuffer[0] = (byte) result;
+                    logTransfer(singleByteBuffer, 1);
+                }
+                return result;
+            }
+
+            public int read(byte[] b, int off, int len) throws IOException {
+                int read = inputStream.read(b, off, len);
+                if (read > 0) {
+                    logTransfer(b, read);
+                }
+                return read;
+            }
+
+            private void logTransfer(byte[] bytes, int read) {
+                transferEventSupport.fireTransferProgress(transferEvent, bytes, read);
+            }
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapter.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapter.java
new file mode 100644
index 0000000..b3c2e9b
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.wagon;
+
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.ExternalResourceName;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.local.LocalResource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+
+public class RepositoryTransportWagonAdapter {
+    private final RepositoryTransport transport;
+    private final URI rootUri;
+
+    public RepositoryTransportWagonAdapter(RepositoryTransport transport, URI rootUri) {
+        this.transport = transport;
+        this.rootUri = rootUri;
+    }
+
+    public boolean getRemoteFile(File destination, String resourceName) throws ResourceException, ResourceDoesNotExistException {
+        URI uriForResource = getUriForResource(resourceName);
+        ExternalResource resource = transport.getRepository().getResource(uriForResource);
+        if (resource == null) {
+            return false;
+        }
+        try {
+            resource.writeTo(destination);
+        } finally {
+            resource.close();
+        }
+        return true;
+    }
+
+    public void putRemoteFile(LocalResource localResource, String resourceName) throws IOException {
+        transport.getRepository().withProgressLogging().put(localResource, getUriForResource(resourceName));
+    }
+
+    private URI getUriForResource(String resource) {
+        ExternalResourceName resourceName = new ExternalResourceName(rootUri, resource);
+        return resourceName.getUri();
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/WagonRegistry.java b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/WagonRegistry.java
new file mode 100644
index 0000000..c32c4da
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publication/maven/internal/wagon/WagonRegistry.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.wagon;
+
+import org.apache.maven.wagon.Wagon;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.component.repository.ComponentDescriptor;
+import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException;
+import org.gradle.api.GradleException;
+
+public class WagonRegistry {
+    private static final String FAILED_TO_REGISTER_WAGON = "Failed to register wagon";
+    private PlexusContainer plexusContainer;
+
+    public WagonRegistry(PlexusContainer plexusContainer) {
+        this.plexusContainer = plexusContainer;
+    }
+
+    public void registerProtocol(String protocol) {
+        try {
+            ComponentDescriptor componentDescriptor = new ComponentDescriptor();
+            componentDescriptor.setRole(Wagon.ROLE);
+            componentDescriptor.setRoleHint(protocol);
+            componentDescriptor.setImplementation(RepositoryTransportDeployWagon.class.getCanonicalName());
+
+            plexusContainer.addComponentDescriptor(componentDescriptor);
+        } catch (ComponentRepositoryException e) {
+            throw new GradleException(FAILED_TO_REGISTER_WAGON, e);
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/MavenPublication.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/MavenPublication.java
index 8b3e83b..16a289e 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/MavenPublication.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/MavenPublication.java
@@ -19,8 +19,8 @@ package org.gradle.api.publish.maven;
 import org.gradle.api.Action;
 import org.gradle.api.Incubating;
 import org.gradle.api.component.SoftwareComponent;
-import org.gradle.internal.HasInternalProtocol;
 import org.gradle.api.publish.Publication;
+import org.gradle.internal.HasInternalProtocol;
 
 /**
  * A {@code MavenPublication} is the representation/configuration of how Gradle should publish something in Maven format.
@@ -67,8 +67,8 @@ import org.gradle.api.publish.Publication;
  *   publications {
  *     myPublication(MavenPublication) {
  *       from components.java
- *       artifact sourceJar {
- *         classifier "source"
+ *       artifact(sourceJar) {
+ *         classifier "sources"
  *       }
  *       pom.withXml {
  *         asNode().appendNode('description', 'A demonstration of Maven POM customization')
@@ -147,7 +147,7 @@ public interface MavenPublication extends Publication {
      * apply plugin: "maven-publish"
      *
      * task sourceJar(type: Jar) {
-     *   classifier "source"
+     *   classifier "sources"
      * }
      *
      * publishing {
@@ -176,13 +176,13 @@ public interface MavenPublication extends Publication {
      * apply plugin: "maven-publish"
      *
      * task sourceJar(type: Jar) {
-     *   classifier "source"
+     *   classifier "sources"
      * }
      *
      * publishing {
      *   publications {
      *     maven(MavenPublication) {
-     *       artifact sourceJar {
+     *       artifact(sourceJar) {
      *         // These values will be used instead of the values from the task. The task values will not be updated.
      *         classifier "src"
      *         extension "zip"
@@ -211,7 +211,7 @@ public interface MavenPublication extends Publication {
      * apply plugin: "maven-publish"
      *
      * task sourceJar(type: Jar) {
-     *   classifier "source"
+     *   classifier "sources"
      * }
 
      * publishing {
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishServices.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishServices.java
new file mode 100644
index 0000000..8866c1f
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishServices.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven.internal;
+
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.api.internal.component.ComponentTypeRegistry;
+import org.gradle.api.publication.maven.internal.pom.DefaultMavenFactory;
+import org.gradle.api.publication.maven.internal.MavenFactory;
+import org.gradle.api.publication.maven.internal.MavenVersionRangeMapper;
+import org.gradle.api.publication.maven.internal.VersionRangeMapper;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.maven.MavenModule;
+import org.gradle.maven.MavenPomArtifact;
+
+public class MavenPublishServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new ComponentRegistrationAction());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class ComponentRegistrationAction {
+        public void configure(ServiceRegistration registration, ComponentTypeRegistry componentTypeRegistry) {
+            // TODO There should be a more explicit way to execute an action against existing services
+            // TODO:DAZ Dependency Management should be able to extract this from the plugin, without explicit registration
+            componentTypeRegistry.maybeRegisterComponentType(MavenModule.class)
+                    .registerArtifactType(MavenPomArtifact.class, ArtifactType.MAVEN_POM);
+        }
+
+        public MavenFactory createMavenFactory(VersionRangeMapper versionRangeMapper) {
+            return new DefaultMavenFactory(versionRangeMapper);
+        }
+
+        public VersionRangeMapper createVersionRangeMapper(VersionSelectorScheme versionSelectorScheme) {
+            return new MavenVersionRangeMapper(versionSelectorScheme);
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishTaskModelRule.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishTaskModelRule.java
deleted file mode 100644
index 72f9f71..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/MavenPublishTaskModelRule.java
+++ /dev/null
@@ -1,109 +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.publish.maven.internal;
-
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.publish.PublicationContainer;
-import org.gradle.api.publish.PublishingExtension;
-import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
-import org.gradle.api.publish.maven.tasks.GenerateMavenPom;
-import org.gradle.api.publish.maven.tasks.PublishToMavenLocal;
-import org.gradle.api.publish.maven.tasks.PublishToMavenRepository;
-import org.gradle.api.publish.plugins.PublishingPlugin;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.model.ModelRule;
-
-import java.io.File;
-import java.util.concurrent.Callable;
-
-import static org.apache.commons.lang.StringUtils.capitalize;
-
-public class MavenPublishTaskModelRule extends ModelRule {
-
-    private final Project project;
-    private final Task publishLifecycleTask;
-    private final Task publishLocalLifecycleTask;
-
-    public MavenPublishTaskModelRule(Project project, Task publishLifecycleTask, Task publishLocalLifecycleTask) {
-        this.project = project;
-        this.publishLifecycleTask = publishLifecycleTask;
-        this.publishLocalLifecycleTask = publishLocalLifecycleTask;
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    public void realizePublishingTasks(TaskContainer tasks, PublishingExtension extension) {
-        // Create generatePom tasks for any Maven publication
-        PublicationContainer publications = extension.getPublications();
-
-        for (final MavenPublicationInternal publication : publications.withType(MavenPublicationInternal.class)) {
-            String publicationName = publication.getName();
-
-            createGeneratePomTask(publication, publicationName);
-            createLocalInstallTask(tasks, publication, publicationName);
-            createPublishTasksForEachMavenRepo(tasks, extension, publication, publicationName);
-        }
-    }
-
-    private void createPublishTasksForEachMavenRepo(TaskContainer tasks, PublishingExtension extension, MavenPublicationInternal publication, String publicationName) {
-        for (MavenArtifactRepository repository : extension.getRepositories().withType(MavenArtifactRepository.class)) {
-            String repositoryName = repository.getName();
-
-            String publishTaskName = String.format("publish%sPublicationTo%sRepository", capitalize(publicationName), capitalize(repositoryName));
-
-            PublishToMavenRepository publishTask = tasks.create(publishTaskName, PublishToMavenRepository.class);
-            publishTask.setPublication(publication);
-            publishTask.setRepository(repository);
-            publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
-            publishTask.setDescription(String.format("Publishes Maven publication '%s' to Maven repository '%s'.", publicationName, repositoryName));
-
-            publishLifecycleTask.dependsOn(publishTask);
-        }
-    }
-
-    private void createLocalInstallTask(TaskContainer tasks, MavenPublicationInternal publication, String publicationName) {
-        String installTaskName = String.format("publish%sPublicationToMavenLocal", capitalize(publicationName));
-
-        PublishToMavenLocal publishLocalTask = tasks.create(installTaskName, PublishToMavenLocal.class);
-        publishLocalTask.setPublication(publication);
-        publishLocalTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
-        publishLocalTask.setDescription(String.format("Publishes Maven publication '%s' to the local Maven repository.", publicationName));
-
-        publishLocalLifecycleTask.dependsOn(installTaskName);
-    }
-
-    private void createGeneratePomTask(final MavenPublicationInternal publication, String publicationName) {
-        String descriptorTaskName = String.format("generatePomFileFor%sPublication", capitalize(publicationName));
-        GenerateMavenPom generatePomTask = project.getTasks().create(descriptorTaskName, GenerateMavenPom.class);
-        generatePomTask.setDescription(String.format("Generates the Maven POM file for publication '%s'.", publication.getName()));
-        generatePomTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
-        generatePomTask.setPom(publication.getPom());
-
-        ConventionMapping descriptorTaskConventionMapping = new DslObject(generatePomTask).getConventionMapping();
-        descriptorTaskConventionMapping.map("destination", new Callable<Object>() {
-            public Object call() throws Exception {
-                return new File(project.getBuildDir(), "publications/" + publication.getName() + "/pom-default.xml");
-            }
-        });
-
-        // Wire the generated pom into the publication.
-        publication.setPomFile(generatePomTask.getOutputs().getFiles());
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactory.java
index 5cb9ed0..dcbb3aa 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactory.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactory.java
@@ -19,19 +19,14 @@ package org.gradle.api.publish.maven.internal.artifact;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.internal.typeconversion.NotationParserBuilder;
-import org.gradle.internal.typeconversion.NotationParser;
-import org.gradle.internal.typeconversion.UnsupportedNotationException;
-import org.gradle.internal.typeconversion.MapKey;
-import org.gradle.internal.typeconversion.MapNotationParser;
-import org.gradle.internal.typeconversion.TypedNotationParser;
 import org.gradle.api.publish.maven.MavenArtifact;
 import org.gradle.api.tasks.bundling.AbstractArchiveTask;
 import org.gradle.internal.Factory;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
 import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.typeconversion.*;
 
 import java.io.File;
-import java.util.Collection;
 
 public class MavenArtifactNotationParserFactory implements Factory<NotationParser<Object, MavenArtifact>> {
     private final Instantiator instantiator;
@@ -43,69 +38,69 @@ public class MavenArtifactNotationParserFactory implements Factory<NotationParse
     }
 
     public NotationParser<Object, MavenArtifact> create() {
-        FileNotationParser fileNotationParser = new FileNotationParser(fileResolver);
-        ArchiveTaskNotationParser archiveTaskNotationParser = new ArchiveTaskNotationParser();
-        PublishArtifactNotationParser publishArtifactNotationParser = new PublishArtifactNotationParser();
-
-        NotationParser<Object, MavenArtifact> sourceNotationParser = new NotationParserBuilder<MavenArtifact>()
-                .resultingType(MavenArtifact.class)
-                .parser(archiveTaskNotationParser)
-                .parser(publishArtifactNotationParser)
-                .parser(fileNotationParser)
+        FileNotationConverter fileNotationConverter = new FileNotationConverter(fileResolver);
+        ArchiveTaskNotationConverter archiveTaskNotationConverter = new ArchiveTaskNotationConverter();
+        PublishArtifactNotationConverter publishArtifactNotationConverter = new PublishArtifactNotationConverter();
+
+        NotationParser<Object, MavenArtifact> sourceNotationParser = NotationParserBuilder
+                .toType(MavenArtifact.class)
+                .fromType(AbstractArchiveTask.class, archiveTaskNotationConverter)
+                .fromType(PublishArtifact.class, publishArtifactNotationConverter)
+                .converter(fileNotationConverter)
                 .toComposite();
 
-        MavenArtifactMapNotationParser mavenArtifactMapNotationParser = new MavenArtifactMapNotationParser(sourceNotationParser);
+        MavenArtifactMapNotationConverter mavenArtifactMapNotationConverter = new MavenArtifactMapNotationConverter(sourceNotationParser);
 
-        NotationParserBuilder<MavenArtifact> parserBuilder = new NotationParserBuilder<MavenArtifact>()
-                .resultingType(MavenArtifact.class)
-                .parser(archiveTaskNotationParser)
-                .parser(publishArtifactNotationParser)
-                .parser(mavenArtifactMapNotationParser)
-                .parser(fileNotationParser);
+        NotationParserBuilder<MavenArtifact> parserBuilder = NotationParserBuilder
+                .toType(MavenArtifact.class)
+                .fromType(AbstractArchiveTask.class, archiveTaskNotationConverter)
+                .fromType(PublishArtifact.class, publishArtifactNotationConverter)
+                .converter(mavenArtifactMapNotationConverter)
+                .converter(fileNotationConverter);
 
         return parserBuilder.toComposite();
     }
 
-    private class ArchiveTaskNotationParser extends TypedNotationParser<AbstractArchiveTask, MavenArtifact> {
-        private ArchiveTaskNotationParser() {
-            super(AbstractArchiveTask.class);
-        }
-
-        @Override
-        protected MavenArtifact parseType(AbstractArchiveTask archiveTask) {
+    private class ArchiveTaskNotationConverter implements NotationConverter<AbstractArchiveTask, MavenArtifact> {
+        public void convert(AbstractArchiveTask archiveTask, NotationConvertResult<? super MavenArtifact> result) throws TypeConversionException {
             DefaultMavenArtifact artifact = instantiator.newInstance(
                     DefaultMavenArtifact.class,
                     archiveTask.getArchivePath(), archiveTask.getExtension(), archiveTask.getClassifier());
             artifact.builtBy(archiveTask);
-            return artifact;
+            result.converted(artifact);
         }
-    }
 
-    private class PublishArtifactNotationParser extends TypedNotationParser<PublishArtifact, MavenArtifact> {
-        private PublishArtifactNotationParser() {
-            super(PublishArtifact.class);
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Instances of AbstractArchiveTask").example("jar");
         }
+    }
 
-        @Override
-        protected MavenArtifact parseType(PublishArtifact publishArtifact) {
+    private class PublishArtifactNotationConverter implements NotationConverter<PublishArtifact, MavenArtifact> {
+        public void convert(PublishArtifact publishArtifact, NotationConvertResult<? super MavenArtifact> result) throws TypeConversionException {
             DefaultMavenArtifact artifact = instantiator.newInstance(
                     DefaultMavenArtifact.class,
                     publishArtifact.getFile(), publishArtifact.getExtension(), publishArtifact.getClassifier());
             artifact.builtBy(publishArtifact.getBuildDependencies());
-            return artifact;
+            result.converted(artifact);
+        }
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Instances of PublishArtifact");
         }
     }
 
-    private class FileNotationParser implements NotationParser<Object, MavenArtifact> {
+    private class FileNotationConverter implements NotationConverter<Object, MavenArtifact> {
         private final NotationParser<Object, File> fileResolverNotationParser;
 
-        private FileNotationParser(FileResolver fileResolver) {
+        private FileNotationConverter(FileResolver fileResolver) {
             this.fileResolverNotationParser = fileResolver.asNotationParser();
         }
 
-        public MavenArtifact parseNotation(Object notation) throws UnsupportedNotationException {
+        public void convert(Object notation, NotationConvertResult<? super MavenArtifact> result) throws TypeConversionException {
             File file = fileResolverNotationParser.parseNotation(notation);
-            return parseFile(file);
+            result.converted(parseFile(file));
         }
 
         protected MavenArtifact parseFile(File file) {
@@ -113,15 +108,16 @@ public class MavenArtifactNotationParserFactory implements Factory<NotationParse
             return instantiator.newInstance(DefaultMavenArtifact.class, file, extension, null);
         }
 
-        public void describe(Collection<String> candidateFormats) {
-            fileResolverNotationParser.describe(candidateFormats);
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            fileResolverNotationParser.describe(visitor);
         }
     }
 
-    private class MavenArtifactMapNotationParser extends MapNotationParser<MavenArtifact> {
+    private class MavenArtifactMapNotationConverter extends MapNotationConverter<MavenArtifact> {
         private final NotationParser<Object, MavenArtifact> sourceNotationParser;
 
-        private MavenArtifactMapNotationParser(NotationParser<Object, MavenArtifact> sourceNotationParser) {
+        private MavenArtifactMapNotationConverter(NotationParser<Object, MavenArtifact> sourceNotationParser) {
             this.sourceNotationParser = sourceNotationParser;
         }
 
@@ -130,8 +126,8 @@ public class MavenArtifactNotationParserFactory implements Factory<NotationParse
         }
 
         @Override
-        public void describe(Collection<String> candidateFormats) {
-            candidateFormats.add("Maps containing a 'source' entry, e.g. [source: '/path/to/file', extension: 'zip'].");
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Maps containing a 'source' entry").example("[source: '/path/to/file', extension: 'zip']");
         }
     }
 }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/DefaultMavenDependency.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/DefaultMavenDependency.java
index 8327c1a..18e696a 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/DefaultMavenDependency.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/DefaultMavenDependency.java
@@ -16,6 +16,7 @@
 package org.gradle.api.publish.maven.internal.dependencies;
 
 import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExcludeRule;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -26,6 +27,7 @@ public class DefaultMavenDependency implements MavenDependencyInternal {
     private final String artifactId;
     private final String version;
     private final List<DependencyArtifact> artifacts = new ArrayList<DependencyArtifact>();
+    private final List<ExcludeRule> excludeRules = new ArrayList<ExcludeRule>(); //exclude rules for a dependency specified in gradle DSL
 
     public DefaultMavenDependency(String groupId, String artifactId, String version) {
         this.groupId = groupId;
@@ -38,6 +40,11 @@ public class DefaultMavenDependency implements MavenDependencyInternal {
         this.artifacts.addAll(artifacts);
     }
 
+    public DefaultMavenDependency(String groupId, String artifactId, String version, Collection<DependencyArtifact> artifacts, Collection<ExcludeRule> excludeRules) {
+        this(groupId, artifactId, version, artifacts);
+        this.excludeRules.addAll(excludeRules);
+    }
+
     public String getGroupId() {
         return groupId;
     }
@@ -53,4 +60,8 @@ public class DefaultMavenDependency implements MavenDependencyInternal {
     public Collection<DependencyArtifact> getArtifacts() {
         return artifacts;
     }
+    
+    public Collection<ExcludeRule> getExcludeRules() {
+        return excludeRules;
+    }
 }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/MavenDependencyInternal.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/MavenDependencyInternal.java
index 5153941..183cf8f 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/MavenDependencyInternal.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/dependencies/MavenDependencyInternal.java
@@ -16,10 +16,12 @@
 package org.gradle.api.publish.maven.internal.dependencies;
 
 import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.publish.maven.MavenDependency;
 
 import java.util.Collection;
 
 public interface MavenDependencyInternal extends MavenDependency {
     Collection<DependencyArtifact> getArtifacts();
+    Collection<ExcludeRule> getExcludeRules();
 }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPom.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPom.java
index 0beb2e9..30edb64 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPom.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPom.java
@@ -22,7 +22,6 @@ import org.gradle.api.internal.UserCodeAction;
 import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal;
 import org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity;
 import org.gradle.listener.ActionBroadcast;
-import org.gradle.util.GUtil;
 
 import java.util.Set;
 
@@ -45,7 +44,10 @@ public class DefaultMavenPom implements MavenPomInternal {
     }
 
     public String getPackaging() {
-        return GUtil.elvis(packaging, mavenPublication.determinePackagingFromArtifacts());
+        if (packaging == null) {
+            return mavenPublication.determinePackagingFromArtifacts();
+        }
+        return packaging;
     }
 
     public void setPackaging(String packaging) {
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublication.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublication.java
index 8d349ce..255e260 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublication.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublication.java
@@ -28,7 +28,6 @@ import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
 import org.gradle.api.internal.component.SoftwareComponentInternal;
 import org.gradle.api.internal.component.Usage;
 import org.gradle.api.internal.file.UnionFileCollection;
-import org.gradle.internal.typeconversion.NotationParser;
 import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
 import org.gradle.api.publish.maven.MavenArtifact;
 import org.gradle.api.publish.maven.MavenArtifactSet;
@@ -38,8 +37,10 @@ import org.gradle.api.publish.maven.internal.dependencies.DefaultMavenDependency
 import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal;
 import org.gradle.api.publish.maven.internal.publisher.MavenNormalizedPublication;
 import org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity;
+import org.gradle.api.specs.Spec;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.GUtil;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.util.CollectionUtils;
 
 import java.io.File;
 import java.util.LinkedHashSet;
@@ -113,7 +114,7 @@ public class DefaultMavenPublication implements MavenPublicationInternal {
     }
 
     private void addModuleDependency(ModuleDependency dependency) {
-        runtimeDependencies.add(new DefaultMavenDependency(dependency.getGroup(), dependency.getName(), dependency.getVersion(), dependency.getArtifacts()));
+        runtimeDependencies.add(new DefaultMavenDependency(dependency.getGroup(), dependency.getName(), dependency.getVersion(), dependency.getArtifacts(), dependency.getExcludeRules()));
      }
 
     public MavenArtifact artifact(Object source) {
@@ -172,7 +173,7 @@ public class DefaultMavenPublication implements MavenPublicationInternal {
     }
 
     public MavenNormalizedPublication asNormalisedPublication() {
-        return new MavenNormalizedPublication(name, getPomFile(), projectIdentity, getArtifacts());
+        return new MavenNormalizedPublication(name, getPomFile(), projectIdentity, getArtifacts(), determineMainArtifact());
     }
 
     private File getPomFile() {
@@ -183,14 +184,48 @@ public class DefaultMavenPublication implements MavenPublicationInternal {
     }
 
     public String determinePackagingFromArtifacts() {
-        for (MavenArtifact mavenArtifact : mavenArtifacts) {
-            if (!GUtil.isTrue(mavenArtifact.getClassifier()) && GUtil.isTrue(mavenArtifact.getExtension())) {
-                return mavenArtifact.getExtension();
-            }
+        Set<MavenArtifact> unclassifiedArtifacts = getUnclassifiedArtifactsWithExtension();
+        if (unclassifiedArtifacts.size() == 1) {
+            return unclassifiedArtifacts.iterator().next().getExtension();
         }
         return "pom";
     }
 
+    private MavenArtifact determineMainArtifact() {
+        Set<MavenArtifact> unclassifiedArtifacts = getUnclassifiedArtifactsWithExtension();
+        if (unclassifiedArtifacts.isEmpty()) {
+            return null;
+        }
+        if (unclassifiedArtifacts.size() == 1) {
+            // Pom packaging doesn't matter when we have a single unclassified artifact
+            return unclassifiedArtifacts.iterator().next();
+        }
+        for (MavenArtifact unclassifiedArtifact : unclassifiedArtifacts) {
+            // With multiple unclassified artifacts, choose the one with extension matching pom packaging
+            String packaging = pom.getPackaging();
+            if (unclassifiedArtifact.getExtension().equals(packaging)) {
+                return unclassifiedArtifact;
+            }
+        }
+        return null;
+    }
+
+    private Set<MavenArtifact> getUnclassifiedArtifactsWithExtension() {
+        return CollectionUtils.filter(mavenArtifacts, new Spec<MavenArtifact>() {
+            public boolean isSatisfiedBy(MavenArtifact mavenArtifact) {
+                return hasNoClassifier(mavenArtifact) && hasExtension(mavenArtifact);
+            }
+        });
+    }
+
+    private boolean hasNoClassifier(MavenArtifact element) {
+        return element.getClassifier() == null || element.getClassifier().length() == 0;
+    }
+
+    private boolean hasExtension(MavenArtifact element) {
+        return element.getExtension() != null && element.getExtension().length() > 0;
+    }
+
     public ModuleVersionIdentifier getCoordinates() {
         return new DefaultModuleVersionIdentifier(getGroupId(), getArtifactId(), getVersion());
     }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/MavenPublicationInternal.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/MavenPublicationInternal.java
index 7d30dee..5a6445e 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/MavenPublicationInternal.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publication/MavenPublicationInternal.java
@@ -39,7 +39,7 @@ public interface MavenPublicationInternal extends MavenPublication, PublicationI
 
     MavenNormalizedPublication asNormalisedPublication();
 
-    // TODO:DAZ Remove this attempt to guess packaging from artifacts. Packaging should come from component, or be explicitly set.
+    // TODO Remove this attempt to guess packaging from artifacts. Packaging should come from component, or be explicitly set.
     String determinePackagingFromArtifacts();
 
 }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractAntTaskBackedMavenPublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractAntTaskBackedMavenPublisher.java
deleted file mode 100644
index 6930e63..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractAntTaskBackedMavenPublisher.java
+++ /dev/null
@@ -1,116 +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.publish.maven.internal.publisher;
-
-import org.apache.maven.artifact.ant.AttachedArtifact;
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
-import org.apache.maven.artifact.ant.Pom;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.publication.maven.internal.ant.EmptyMavenSettingsSupplier;
-import org.gradle.api.publication.maven.internal.ant.MavenSettingsSupplier;
-import org.gradle.api.publish.maven.InvalidMavenPublicationException;
-import org.gradle.api.publish.maven.MavenArtifact;
-import org.gradle.api.specs.Spec;
-import org.gradle.internal.Factory;
-import org.gradle.logging.LoggingManagerInternal;
-import org.gradle.util.AntUtil;
-import org.gradle.util.CollectionUtils;
-import org.gradle.util.GUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.Set;
-
-abstract public class AbstractAntTaskBackedMavenPublisher<T extends InstallDeployTaskSupport> implements MavenPublisher {
-    private final Factory<LoggingManagerInternal> loggingManagerFactory;
-
-    private static Logger logger = LoggerFactory.getLogger(AbstractAntTaskBackedMavenPublisher.class);
-    protected final Factory<File> temporaryDirFactory;
-
-    public AbstractAntTaskBackedMavenPublisher(Factory<LoggingManagerInternal> loggingManagerFactory, Factory<File> temporaryDirFactory) {
-        this.loggingManagerFactory = loggingManagerFactory;
-        this.temporaryDirFactory = temporaryDirFactory;
-    }
-
-    public void publish(MavenNormalizedPublication publication, MavenArtifactRepository artifactRepository) {
-        logger.info("Publishing to repository {}", artifactRepository);
-        T deployTask = createDeployTask();
-        deployTask.setProject(AntUtil.createProject());
-
-        MavenSettingsSupplier mavenSettingsSupplier = new EmptyMavenSettingsSupplier();
-        mavenSettingsSupplier.supply(deployTask);
-
-        postConfigure(deployTask, artifactRepository);
-        addPomAndArtifacts(deployTask, publication);
-        execute(deployTask);
-
-        mavenSettingsSupplier.done();
-    }
-
-    abstract protected void postConfigure(T task, MavenArtifactRepository artifactRepository);
-
-    abstract protected T createDeployTask();
-
-    private void addPomAndArtifacts(InstallDeployTaskSupport installOrDeployTask, MavenNormalizedPublication publication) {
-        Pom pom = new Pom();
-        pom.setProject(installOrDeployTask.getProject());
-        pom.setFile(publication.getPomFile());
-        installOrDeployTask.addPom(pom);
-
-        MavenArtifact mainArtifact = determineMainArtifact(publication.getName(), publication.getArtifacts());
-        installOrDeployTask.setFile(mainArtifact == null ? publication.getPomFile() : mainArtifact.getFile());
-
-        for (MavenArtifact mavenArtifact : publication.getArtifacts()) {
-            if (mavenArtifact == mainArtifact) {
-                continue;
-            }
-            AttachedArtifact attachedArtifact = installOrDeployTask.createAttach();
-            attachedArtifact.setClassifier(GUtil.elvis(mavenArtifact.getClassifier(), ""));
-            attachedArtifact.setType(GUtil.elvis(mavenArtifact.getExtension(), ""));
-            attachedArtifact.setFile(mavenArtifact.getFile());
-        }
-    }
-
-    private MavenArtifact determineMainArtifact(String publicationName, Set<MavenArtifact> mavenArtifacts) {
-        Set<MavenArtifact> candidateMainArtifacts = CollectionUtils.filter(mavenArtifacts, new Spec<MavenArtifact>() {
-            public boolean isSatisfiedBy(MavenArtifact element) {
-                return element.getClassifier() == null || element.getClassifier().length() == 0;
-            }
-        });
-        if (candidateMainArtifacts.isEmpty()) {
-            return null;
-        }
-        if (candidateMainArtifacts.size() > 1) {
-            throw new InvalidMavenPublicationException(publicationName, "Cannot determine main artifact - multiple artifacts found with empty classifier.");
-        }
-        return candidateMainArtifacts.iterator().next();
-    }
-
-
-    private void execute(InstallDeployTaskSupport deployTask) {
-        LoggingManagerInternal loggingManager = loggingManagerFactory.create();
-        loggingManager.captureStandardOutput(LogLevel.INFO).start();
-        try {
-            deployTask.execute();
-        } finally {
-            loggingManager.stop();
-        }
-    }
-
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.java
new file mode 100644
index 0000000..d88f91e
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AbstractMavenPublisher.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.publish.maven.internal.publisher;
+
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.publication.maven.internal.action.MavenPublishAction;
+import org.gradle.api.publish.maven.MavenArtifact;
+import org.gradle.internal.Factory;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.util.GUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+abstract public class AbstractMavenPublisher implements MavenPublisher {
+    private final Factory<LoggingManagerInternal> loggingManagerFactory;
+
+    private static Logger logger = LoggerFactory.getLogger(AbstractMavenPublisher.class);
+    private final LocalMavenRepositoryLocator mavenRepositoryLocator;
+
+    public AbstractMavenPublisher(Factory<LoggingManagerInternal> loggingManagerFactory, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        this.loggingManagerFactory = loggingManagerFactory;
+        this.mavenRepositoryLocator = mavenRepositoryLocator;
+    }
+
+    public void publish(MavenNormalizedPublication publication, MavenArtifactRepository artifactRepository) {
+        logger.info("Publishing to repository {}", artifactRepository);
+        MavenPublishAction deployTask = createDeployTask(publication.getPomFile(), mavenRepositoryLocator, artifactRepository);
+        addPomAndArtifacts(deployTask, publication);
+        execute(deployTask);
+    }
+
+    abstract protected MavenPublishAction createDeployTask(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator, MavenArtifactRepository artifactRepository);
+
+    private void addPomAndArtifacts(MavenPublishAction publishAction, MavenNormalizedPublication publication) {
+        MavenArtifact mainArtifact = publication.getMainArtifact();
+        if (mainArtifact != null) {
+            publishAction.setMainArtifact(mainArtifact.getFile());
+        }
+
+        for (MavenArtifact mavenArtifact : publication.getArtifacts()) {
+            if (mavenArtifact == mainArtifact) {
+                continue;
+            }
+            publishAction.addAdditionalArtifact(mavenArtifact.getFile(), GUtil.elvis(mavenArtifact.getExtension(), ""), GUtil.elvis(mavenArtifact.getClassifier(), ""));
+        }
+    }
+
+    private void execute(MavenPublishAction publishAction) {
+        LoggingManagerInternal loggingManager = loggingManagerFactory.create();
+        loggingManager.captureStandardOutput(LogLevel.INFO).start();
+        try {
+            publishAction.publish();
+        } finally {
+            loggingManager.stop();
+        }
+    }
+
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenLocalPublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenLocalPublisher.java
deleted file mode 100644
index 2be6c47..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenLocalPublisher.java
+++ /dev/null
@@ -1,64 +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.publish.maven.internal.publisher;
-
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.maven.artifact.repository.ArtifactRepository;
-import org.apache.maven.artifact.repository.DefaultArtifactRepository;
-import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.publication.maven.internal.ant.CustomInstallTask;
-import org.gradle.internal.Factory;
-import org.gradle.logging.LoggingManagerInternal;
-
-import java.io.File;
-
-public class AntTaskBackedMavenLocalPublisher extends AbstractAntTaskBackedMavenPublisher<AntTaskBackedMavenLocalPublisher.MavenLocalInstallTask> {
-    public AntTaskBackedMavenLocalPublisher(Factory<LoggingManagerInternal> loggingManagerFactory, Factory<File> temporaryDirFactory) {
-        super(loggingManagerFactory, temporaryDirFactory);
-    }
-
-    @Override
-    protected void postConfigure(MavenLocalInstallTask task, MavenArtifactRepository artifactRepository) {
-        task.setRepoLocation(artifactRepository.getUrl().toString());
-    }
-
-    @Override
-    protected MavenLocalInstallTask createDeployTask() {
-        return new MavenLocalInstallTask();
-    }
-
-    public static class MavenLocalInstallTask extends CustomInstallTask {
-
-        private String repoLocation;
-
-        private void setRepoLocation(String repoLocation) {
-            this.repoLocation = repoLocation;
-        }
-
-        @Override
-        protected ArtifactRepository createLocalArtifactRepository() {
-            ArtifactRepositoryLayout repositoryLayout = (ArtifactRepositoryLayout) lookup(ArtifactRepositoryLayout.ROLE, getLocalRepository().getLayout());
-            return new DefaultArtifactRepository("local", repoLocation, repositoryLayout);
-        }
-
-        @Override
-        protected void updateRepositoryWithSettings(RemoteRepository repository) {
-            // Do nothing
-        }
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenPublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenPublisher.java
deleted file mode 100644
index a22dbf4..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/AntTaskBackedMavenPublisher.java
+++ /dev/null
@@ -1,68 +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.publish.maven.internal.publisher;
-
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.apache.maven.artifact.repository.ArtifactRepository;
-import org.apache.maven.artifact.repository.DefaultArtifactRepository;
-import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.publication.maven.internal.ant.CustomDeployTask;
-import org.gradle.internal.Factory;
-import org.gradle.logging.LoggingManagerInternal;
-
-import java.io.File;
-
-public class AntTaskBackedMavenPublisher extends AbstractAntTaskBackedMavenPublisher<CustomDeployTask> {
-    public AntTaskBackedMavenPublisher(Factory<LoggingManagerInternal> loggingManagerFactory, Factory<File> temporaryDirFactory) {
-        super(loggingManagerFactory, temporaryDirFactory);
-    }
-
-    protected void postConfigure(CustomDeployTask task, MavenArtifactRepository artifactRepository) {
-        addRepository(task, artifactRepository);
-    }
-
-    protected CustomDeployTask createDeployTask() {
-        CustomDeployTask deployTask = new DeployTask(temporaryDirFactory);
-        deployTask.setUniqueVersion(true);
-        return deployTask;
-    }
-
-    private void addRepository(CustomDeployTask deployTask, MavenArtifactRepository artifactRepository) {
-        RemoteRepository mavenRepository = new MavenRemoteRepositoryFactory(artifactRepository).create();
-        deployTask.addRemoteRepository(mavenRepository);
-    }
-
-    private static class DeployTask extends CustomDeployTask {
-        private final Factory<File> tmpDirFactory;
-
-        public DeployTask(Factory<File> tmpDirFactory) {
-            this.tmpDirFactory = tmpDirFactory;
-        }
-
-        @Override
-        protected ArtifactRepository createLocalArtifactRepository() {
-            ArtifactRepositoryLayout repositoryLayout = (ArtifactRepositoryLayout) lookup(ArtifactRepositoryLayout.ROLE, getLocalRepository().getLayout());
-            return new DefaultArtifactRepository("local", tmpDirFactory.create().toURI().toString(), repositoryLayout);
-        }
-
-        @Override
-        protected void updateRepositoryWithSettings(RemoteRepository repository) {
-            // Do nothing
-        }
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenLocalPublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenLocalPublisher.java
new file mode 100644
index 0000000..5e88dce
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenLocalPublisher.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.publish.maven.internal.publisher;
+
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.publication.maven.internal.action.MavenInstallAction;
+import org.gradle.internal.Factory;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+
+public class MavenLocalPublisher extends AbstractMavenPublisher {
+    public MavenLocalPublisher(Factory<LoggingManagerInternal> loggingManagerFactory, LocalMavenRepositoryLocator mavenRepositoryLocator) {
+        super(loggingManagerFactory, mavenRepositoryLocator);
+    }
+
+    @Override
+    protected MavenInstallAction createDeployTask(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator, MavenArtifactRepository artifactRepository) {
+        MavenInstallAction mavenInstallTask = new MavenInstallAction(pomFile);
+        mavenInstallTask.setLocalMavenRepositoryLocation(mavenRepositoryLocator.getLocalMavenRepository());
+        return mavenInstallTask;
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenNormalizedPublication.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenNormalizedPublication.java
index ca44bbf..4919db4 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenNormalizedPublication.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenNormalizedPublication.java
@@ -27,12 +27,14 @@ public class MavenNormalizedPublication {
     private final File pomFile;
     private final MavenProjectIdentity projectIdentity;
     private final Set<MavenArtifact> artifacts;
+    private final MavenArtifact mainArtifact;
 
-    public MavenNormalizedPublication(String name, File pomFile, MavenProjectIdentity projectIdentity, Set<MavenArtifact> artifacts) {
+    public MavenNormalizedPublication(String name, File pomFile, MavenProjectIdentity projectIdentity, Set<MavenArtifact> artifacts, MavenArtifact mainArtifact) {
         this.name = name;
         this.pomFile = pomFile;
         this.projectIdentity = projectIdentity;
         this.artifacts = artifacts;
+        this.mainArtifact = mainArtifact;
     }
 
     public String getName() {
@@ -50,4 +52,8 @@ public class MavenNormalizedPublication {
     public MavenProjectIdentity getProjectIdentity() {
         return projectIdentity;
     }
+
+    public MavenArtifact getMainArtifact() {
+        return mainArtifact;
+    }
 }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java
new file mode 100644
index 0000000..884862f
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemotePublisher.java
@@ -0,0 +1,103 @@
+/*
+ * 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.publish.maven.internal.publisher;
+
+import org.apache.maven.artifact.ant.RemoteRepository;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.credentials.Credentials;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
+import org.gradle.api.publication.maven.internal.action.MavenDeployAction;
+import org.gradle.api.publication.maven.internal.action.MavenPublishAction;
+import org.gradle.api.publication.maven.internal.wagon.RepositoryTransportDeployWagon;
+import org.gradle.api.publication.maven.internal.wagon.RepositoryTransportWagonAdapter;
+import org.gradle.api.publication.maven.internal.wagon.WagonRegistry;
+import org.gradle.internal.Factory;
+import org.gradle.internal.artifacts.repositories.AuthenticationSupportedInternal;
+import org.gradle.logging.LoggingManagerInternal;
+
+import java.io.File;
+import java.net.URI;
+
+public class MavenRemotePublisher extends AbstractMavenPublisher {
+    private final Factory<File> temporaryDirFactory;
+    private final RepositoryTransportFactory repositoryTransportFactory;
+
+    public MavenRemotePublisher(Factory<LoggingManagerInternal> loggingManagerFactory, LocalMavenRepositoryLocator mavenRepositoryLocator, Factory<File> temporaryDirFactory, RepositoryTransportFactory repositoryTransportFactory) {
+        super(loggingManagerFactory, mavenRepositoryLocator);
+        this.temporaryDirFactory = temporaryDirFactory;
+        this.repositoryTransportFactory = repositoryTransportFactory;
+    }
+
+    protected MavenPublishAction createDeployTask(File pomFile, LocalMavenRepositoryLocator mavenRepositoryLocator, MavenArtifactRepository artifactRepository) {
+        GradleWagonMavenDeployAction deployTask = new GradleWagonMavenDeployAction(pomFile, artifactRepository, repositoryTransportFactory);
+        deployTask.setLocalMavenRepositoryLocation(temporaryDirFactory.create());
+        deployTask.setRepositories(createMavenRemoteRepository(artifactRepository), null);
+        deployTask.setUniqueVersion(true);
+        return deployTask;
+    }
+
+    private RemoteRepository createMavenRemoteRepository(MavenArtifactRepository repository) {
+        RemoteRepository remoteRepository = new RemoteRepository();
+        remoteRepository.setUrl(repository.getUrl().toString());
+        return remoteRepository;
+    }
+
+    /**
+     * A deploy action that uses a Gradle provided wagon implementation.
+     */
+    private static class GradleWagonMavenDeployAction extends MavenDeployAction {
+        private final MavenArtifactRepository artifactRepository;
+        private final RepositoryTransportFactory repositoryTransportFactory;
+        private final WagonRegistry wagonRegistry;
+
+        public GradleWagonMavenDeployAction(File pomFile, MavenArtifactRepository artifactRepository, RepositoryTransportFactory repositoryTransportFactory) {
+            super(pomFile);
+            this.artifactRepository = artifactRepository;
+            this.repositoryTransportFactory = repositoryTransportFactory;
+            this.wagonRegistry = new WagonRegistry(getContainer());
+
+            registerWagonProtocols();
+        }
+
+        private void registerWagonProtocols() {
+            for (String protocol : repositoryTransportFactory.getRegisteredProtocols()) {
+                wagonRegistry.registerProtocol(protocol);
+            }
+        }
+
+        @Override
+        public void publish() {
+            String protocol = artifactRepository.getUrl().getScheme().toLowerCase();
+            RepositoryTransportWagonAdapter adapter = createAdapter(protocol, artifactRepository, repositoryTransportFactory);
+            RepositoryTransportDeployWagon.contextualize(adapter);
+            try {
+                super.publish();
+            } finally {
+                RepositoryTransportDeployWagon.decontextualize();
+            }
+        }
+
+        private RepositoryTransportWagonAdapter createAdapter(String protocol, MavenArtifactRepository artifactRepository, RepositoryTransportFactory repositoryTransportFactory) {
+            Credentials credentials = ((AuthenticationSupportedInternal) artifactRepository).getConfiguredCredentials();
+            RepositoryTransport transport = repositoryTransportFactory.createTransport(protocol, artifactRepository.getName(), credentials);
+            URI rootUri = artifactRepository.getUrl();
+            return new RepositoryTransportWagonAdapter(transport, rootUri);
+        }
+    }
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemoteRepositoryFactory.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemoteRepositoryFactory.java
deleted file mode 100644
index 1f07241..0000000
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/publisher/MavenRemoteRepositoryFactory.java
+++ /dev/null
@@ -1,50 +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.publish.maven.internal.publisher;
-
-import org.apache.maven.artifact.ant.Authentication;
-import org.apache.maven.artifact.ant.RemoteRepository;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.artifacts.repositories.PasswordCredentials;
-import org.gradle.internal.Factory;
-
-class MavenRemoteRepositoryFactory implements Factory<RemoteRepository> {
-
-    private final MavenArtifactRepository artifactRepository;
-
-    public MavenRemoteRepositoryFactory(MavenArtifactRepository artifactRepository) {
-        this.artifactRepository = artifactRepository;
-    }
-
-    public RemoteRepository create() {
-        RemoteRepository remoteRepository = new RemoteRepository();
-        remoteRepository.setUrl(artifactRepository.getUrl().toString());
-
-        PasswordCredentials credentials = artifactRepository.getCredentials();
-        String username = credentials.getUsername();
-        String password = credentials.getPassword();
-
-        if (username != null || password != null) {
-            Authentication authentication = new Authentication();
-            authentication.setUserName(username);
-            authentication.setPassword(password);
-            remoteRepository.addAuthentication(authentication);
-        }
-
-        return remoteRepository;
-    }
-}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGenerator.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGenerator.java
index fc1a786..07ea134 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGenerator.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGenerator.java
@@ -17,15 +17,19 @@
 package org.gradle.api.publish.maven.internal.tasks;
 
 import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
 import org.apache.maven.model.Model;
 import org.apache.maven.project.MavenProject;
 import org.gradle.api.Action;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.XmlProvider;
 import org.gradle.api.artifacts.DependencyArtifact;
-import org.gradle.api.internal.xml.XmlTransformer;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.api.publication.maven.internal.VersionRangeMapper;
 import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal;
 import org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity;
+import org.gradle.internal.xml.XmlTransformer;
+import org.gradle.util.GUtil;
 
 import java.io.File;
 import java.io.IOException;
@@ -38,8 +42,10 @@ public class MavenPomFileGenerator {
 
     private MavenProject mavenProject = new MavenProject();
     private XmlTransformer xmlTransformer = new XmlTransformer();
+    private final VersionRangeMapper versionRangeMapper;
 
-    public MavenPomFileGenerator(MavenProjectIdentity identity) {
+    public MavenPomFileGenerator(MavenProjectIdentity identity, VersionRangeMapper versionRangeMapper) {
+        this.versionRangeMapper = versionRangeMapper;
         mavenProject.setModelVersion(POM_VERSION);
         Model model = getModel();
         model.setGroupId(identity.getGroupId());
@@ -74,14 +80,25 @@ public class MavenPomFileGenerator {
         Dependency mavenDependency = new Dependency();
         mavenDependency.setGroupId(dependency.getGroupId());
         mavenDependency.setArtifactId(artifactId);
-        mavenDependency.setVersion(dependency.getVersion());
+        mavenDependency.setVersion(mapToMavenSyntax(dependency.getVersion()));
         mavenDependency.setType(type);
         mavenDependency.setScope(scope);
         mavenDependency.setClassifier(classifier);
 
+        for (ExcludeRule excludeRule : dependency.getExcludeRules()) {
+            Exclusion exclusion = new Exclusion();
+            exclusion.setGroupId(GUtil.elvis(excludeRule.getGroup(), "*"));
+            exclusion.setArtifactId(GUtil.elvis(excludeRule.getModule(), "*"));
+            mavenDependency.addExclusion(exclusion);
+        }
+
         getModel().addDependency(mavenDependency);
     }
 
+    private String mapToMavenSyntax(String version) {
+        return versionRangeMapper.map(version);
+    }
+
     public MavenPomFileGenerator withXml(final Action<XmlProvider> action) {
         xmlTransformer.addAction(action);
         return this;
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
index 6ce973a..ab2e030 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
@@ -18,24 +18,35 @@ package org.gradle.api.publish.maven.plugins;
 
 import org.gradle.api.*;
 import org.gradle.api.artifacts.Module;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
-import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.api.publish.PublicationContainer;
 import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.internal.ProjectDependencyPublicationResolver;
 import org.gradle.api.publish.maven.MavenArtifact;
 import org.gradle.api.publish.maven.MavenPublication;
-import org.gradle.api.publish.maven.internal.MavenPublishTaskModelRule;
 import org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory;
 import org.gradle.api.publish.maven.internal.publication.DefaultMavenProjectIdentity;
 import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication;
+import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
 import org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity;
+import org.gradle.api.publish.maven.tasks.GenerateMavenPom;
+import org.gradle.api.publish.maven.tasks.PublishToMavenLocal;
+import org.gradle.api.publish.maven.tasks.PublishToMavenRepository;
 import org.gradle.api.publish.plugins.PublishingPlugin;
 import org.gradle.api.tasks.TaskContainer;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.model.ModelRules;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
 
 import javax.inject.Inject;
+import java.io.File;
+
+import static org.apache.commons.lang.StringUtils.capitalize;
 
 /**
  * Adds the ability to publish in the Maven format to Maven repositories.
@@ -50,24 +61,21 @@ public class MavenPublishPlugin implements Plugin<Project> {
     private final Instantiator instantiator;
     private final DependencyMetaDataProvider dependencyMetaDataProvider;
     private final FileResolver fileResolver;
-    private final ModelRules modelRules;
     private final ProjectDependencyPublicationResolver projectDependencyResolver;
 
     @Inject
-    public MavenPublishPlugin(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, FileResolver fileResolver, ModelRules modelRules,
+    public MavenPublishPlugin(Instantiator instantiator, DependencyMetaDataProvider dependencyMetaDataProvider, FileResolver fileResolver,
                               ProjectDependencyPublicationResolver projectDependencyResolver) {
         this.instantiator = instantiator;
         this.dependencyMetaDataProvider = dependencyMetaDataProvider;
         this.fileResolver = fileResolver;
-        this.modelRules = modelRules;
         this.projectDependencyResolver = projectDependencyResolver;
     }
 
     public void apply(final Project project) {
-        project.getPlugins().apply(PublishingPlugin.class);
+        project.getPluginManager().apply(PublishingPlugin.class);
 
         final TaskContainer tasks = project.getTasks();
-        final Task publishLifecycleTask = tasks.getByName(PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME);
         final Task publishLocalLifecycleTask = tasks.create(PUBLISH_LOCAL_LIFECYCLE_TASK_NAME);
         publishLocalLifecycleTask.setDescription("Publishes all Maven publications produced by this project to the local Maven cache.");
         publishLocalLifecycleTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
@@ -79,8 +87,72 @@ public class MavenPublishPlugin implements Plugin<Project> {
                 extension.getPublications().registerFactory(MavenPublication.class, new MavenPublicationFactory(dependencyMetaDataProvider, instantiator, fileResolver));
             }
         });
+    }
+
+    static class Rules extends RuleSource {
+        @Mutate
+        @SuppressWarnings("UnusedDeclaration")
+        public void realizePublishingTasks(CollectionBuilder<Task> tasks, PublishingExtension extension, @Path("buildDir") File buildDir) {
+            // Create generatePom tasks for any Maven publication
+            PublicationContainer publications = extension.getPublications();
+            Task publishLifecycleTask = tasks.get(PublishingPlugin.PUBLISH_LIFECYCLE_TASK_NAME);
+            Task publishLocalLifecycleTask = tasks.get(PUBLISH_LOCAL_LIFECYCLE_TASK_NAME);
+
+            for (final MavenPublicationInternal publication : publications.withType(MavenPublicationInternal.class)) {
+                String publicationName = publication.getName();
+
+                createGeneratePomTask(tasks, publication, publicationName, buildDir);
+                createLocalInstallTask(tasks, publishLocalLifecycleTask, publication, publicationName);
+                createPublishTasksForEachMavenRepo(tasks, extension, publishLifecycleTask, publication, publicationName);
+            }
+        }
 
-        modelRules.rule(new MavenPublishTaskModelRule(project, publishLifecycleTask, publishLocalLifecycleTask));
+        private void createPublishTasksForEachMavenRepo(CollectionBuilder<Task> tasks, PublishingExtension extension, final Task publishLifecycleTask, final MavenPublicationInternal publication,
+                                                        final String publicationName) {
+            for (final MavenArtifactRepository repository : extension.getRepositories().withType(MavenArtifactRepository.class)) {
+                final String repositoryName = repository.getName();
+
+                String publishTaskName = String.format("publish%sPublicationTo%sRepository", capitalize(publicationName), capitalize(repositoryName));
+
+                tasks.create(publishTaskName, PublishToMavenRepository.class, new Action<PublishToMavenRepository>() {
+                    public void execute(PublishToMavenRepository publishTask) {
+                        publishTask.setPublication(publication);
+                        publishTask.setRepository(repository);
+                        publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
+                        publishTask.setDescription(String.format("Publishes Maven publication '%s' to Maven repository '%s'.", publicationName, repositoryName));
+
+                    }
+                });
+                publishLifecycleTask.dependsOn(publishTaskName);
+            }
+        }
+
+        private void createLocalInstallTask(CollectionBuilder<Task> tasks, final Task publishLocalLifecycleTask, final MavenPublicationInternal publication, final String publicationName) {
+            final String installTaskName = String.format("publish%sPublicationToMavenLocal", capitalize(publicationName));
+
+            tasks.create(installTaskName, PublishToMavenLocal.class, new Action<PublishToMavenLocal>() {
+                public void execute(PublishToMavenLocal publishLocalTask) {
+                    publishLocalTask.setPublication(publication);
+                    publishLocalTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
+                    publishLocalTask.setDescription(String.format("Publishes Maven publication '%s' to the local Maven repository.", publicationName));
+                }
+            });
+            publishLocalLifecycleTask.dependsOn(installTaskName);
+        }
+
+        private void createGeneratePomTask(CollectionBuilder<Task> tasks, final MavenPublicationInternal publication, String publicationName, final File buildDir) {
+            String descriptorTaskName = String.format("generatePomFileFor%sPublication", capitalize(publicationName));
+            tasks.create(descriptorTaskName, GenerateMavenPom.class, new Action<GenerateMavenPom>() {
+                public void execute(final GenerateMavenPom generatePomTask) {
+                    generatePomTask.setDescription(String.format("Generates the Maven POM file for publication '%s'.", publication.getName()));
+                    generatePomTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
+                    generatePomTask.setPom(publication.getPom());
+                    generatePomTask.setDestination(new File(buildDir, "publications/" + publication.getName() + "/pom-default.xml"));
+                }
+            });
+            // Wire the generated pom into the publication.
+            publication.setPomFile(tasks.get(descriptorTaskName).getOutputs().getFiles());
+        }
     }
 
     private class MavenPublicationFactory implements NamedDomainObjectFactory<MavenPublication> {
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/AbstractPublishToMaven.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/AbstractPublishToMaven.java
new file mode 100644
index 0000000..e328a6e
--- /dev/null
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/AbstractPublishToMaven.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven.tasks;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
+import org.gradle.api.publish.maven.MavenPublication;
+import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
+import org.gradle.internal.Factory;
+import org.gradle.logging.LoggingManagerInternal;
+
+import javax.inject.Inject;
+import java.util.concurrent.Callable;
+
+/**
+ * Base class for tasks that publish a {@link org.gradle.api.publish.maven.MavenPublication}.
+ *
+ * @since 2.4
+ */
+ at Incubating
+abstract public class AbstractPublishToMaven extends DefaultTask {
+
+    private MavenPublicationInternal publication;
+
+    public AbstractPublishToMaven() {
+        // Allow the publication to participate in incremental build
+        getInputs().files(new Callable<FileCollection>() {
+            public FileCollection call() throws Exception {
+                MavenPublicationInternal publicationInternal = getPublicationInternal();
+                return publicationInternal == null ? null : publicationInternal.getPublishableFiles();
+            }
+        });
+
+        // Should repositories be able to participate in incremental?
+        // At the least, they may be able to express themselves as output files
+        // They *might* have input files and other dependencies as well though
+        // Inputs: The credentials they need may be expressed in a file
+        // Dependencies: Can't think of a case here
+    }
+
+
+    /**
+     * The publication to be published.
+     *
+     * @return The publication to be published
+     */
+    public MavenPublication getPublication() {
+        return publication;
+    }
+
+    /**
+     * Sets the publication to be published.
+     *
+     * @param publication The publication to be published
+     */
+    public void setPublication(MavenPublication publication) {
+        this.publication = toPublicationInternal(publication);
+    }
+
+    protected MavenPublicationInternal getPublicationInternal() {
+        return toPublicationInternal(getPublication());
+    }
+
+    private static MavenPublicationInternal toPublicationInternal(MavenPublication publication) {
+        if (publication == null) {
+            return null;
+        } else if (publication instanceof MavenPublicationInternal) {
+            return (MavenPublicationInternal) publication;
+        } else {
+            throw new InvalidUserDataException(
+                    String.format(
+                            "publication objects must implement the '%s' interface, implementation '%s' does not",
+                            MavenPublicationInternal.class.getName(),
+                            publication.getClass().getName()
+                    )
+            );
+        }
+    }
+
+    @Inject
+    protected Factory<LoggingManagerInternal> getLoggingManagerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected LocalMavenRepositoryLocator getMavenRepositoryLocator() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/GenerateMavenPom.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/GenerateMavenPom.java
index e821dd4..6c65cf8 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/GenerateMavenPom.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/GenerateMavenPom.java
@@ -19,6 +19,7 @@ package org.gradle.api.publish.maven.tasks;
 import org.gradle.api.DefaultTask;
 import org.gradle.api.Incubating;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.publication.maven.internal.VersionRangeMapper;
 import org.gradle.api.publish.maven.MavenPom;
 import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal;
 import org.gradle.api.publish.maven.internal.publication.MavenPomInternal;
@@ -38,18 +39,24 @@ import java.io.File;
 @Incubating
 public class GenerateMavenPom extends DefaultTask {
 
-    private final FileResolver fileResolver;
     private MavenPom pom;
     private Object destination;
 
-    @Inject
-    public GenerateMavenPom(FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-
+    public GenerateMavenPom() {
         // Never up to date; we don't understand the data structures.
         getOutputs().upToDateWhen(Specs.satisfyNone());
     }
 
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected VersionRangeMapper getVersionRangeMapper() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * The Maven POM.
      *
@@ -70,7 +77,7 @@ public class GenerateMavenPom extends DefaultTask {
      */
     @OutputFile
     public File getDestination() {
-        return destination == null ? null : fileResolver.resolve(destination);
+        return destination == null ? null : getFileResolver().resolve(destination);
     }
 
     /**
@@ -88,7 +95,7 @@ public class GenerateMavenPom extends DefaultTask {
     public void doGenerate() {
         MavenPomInternal pomInternal = (MavenPomInternal) getPom();
 
-        MavenPomFileGenerator pomGenerator = new MavenPomFileGenerator(pomInternal.getProjectIdentity());
+        MavenPomFileGenerator pomGenerator = new MavenPomFileGenerator(pomInternal.getProjectIdentity(), getVersionRangeMapper());
         pomGenerator.setPackaging(pomInternal.getPackaging());
 
         for (MavenDependencyInternal runtimeDependency : pomInternal.getRuntimeDependencies()) {
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenLocal.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenLocal.java
index 64e87f8..594e3d8 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenLocal.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenLocal.java
@@ -17,19 +17,14 @@
 package org.gradle.api.publish.maven.tasks;
 
 import org.gradle.api.Incubating;
-import org.gradle.api.artifacts.ArtifactRepositoryContainer;
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.internal.artifacts.BaseRepositoryFactory;
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.publish.internal.PublishOperation;
 import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
-import org.gradle.api.publish.maven.internal.publisher.AntTaskBackedMavenLocalPublisher;
+import org.gradle.api.publish.maven.internal.publisher.MavenLocalPublisher;
 import org.gradle.api.publish.maven.internal.publisher.MavenPublisher;
 import org.gradle.api.publish.maven.internal.publisher.StaticLockingMavenPublisher;
 import org.gradle.api.publish.maven.internal.publisher.ValidatingMavenPublisher;
-import org.gradle.internal.Factory;
-import org.gradle.logging.LoggingManagerInternal;
-
-import javax.inject.Inject;
+import org.gradle.api.tasks.TaskAction;
 
 /**
  * Publishes a {@link org.gradle.api.publish.maven.MavenPublication} to the Maven Local repository.
@@ -37,37 +32,22 @@ import javax.inject.Inject;
  * @since 1.4
  */
 @Incubating
-public class PublishToMavenLocal extends PublishToMavenRepository {
-
-    private final BaseRepositoryFactory baseRepositoryFactory;
-
-    @Inject
-    public PublishToMavenLocal(Factory<LoggingManagerInternal> loggingManagerFactory, BaseRepositoryFactory baseRepositoryFactory) {
-        super(loggingManagerFactory);
-        this.baseRepositoryFactory = baseRepositoryFactory;
-    }
+public class PublishToMavenLocal extends AbstractPublishToMaven {
 
-    @Override
-    public MavenArtifactRepository getRepository() {
-        if (super.getRepository() == null) {
-            // Instantiate the default MavenLocal repository if none has been set explicitly
-            MavenArtifactRepository mavenLocalRepository = baseRepositoryFactory.createMavenLocalRepository();
-            mavenLocalRepository.setName(ArtifactRepositoryContainer.DEFAULT_MAVEN_LOCAL_REPO_NAME);
-            setRepository(mavenLocalRepository);
+    @TaskAction
+    public void publish() {
+        final MavenPublicationInternal publication = getPublicationInternal();
+        if (publication == null) {
+            throw new InvalidUserDataException("The 'publication' property is required");
         }
 
-        return super.getRepository();
-    }
-
-    @Override
-    protected void doPublish(final MavenPublicationInternal publication, final MavenArtifactRepository repository) {
-        new PublishOperation(publication, repository) {
+        new PublishOperation(publication, "mavenLocal") {
             @Override
             protected void publish() throws Exception {
-                MavenPublisher antBackedPublisher = new AntTaskBackedMavenLocalPublisher(getLoggingManagerFactory(), getTemporaryDirFactory());
-                MavenPublisher staticLockingPublisher = new StaticLockingMavenPublisher(antBackedPublisher);
+                MavenPublisher localPublisher = new MavenLocalPublisher(getLoggingManagerFactory(), getMavenRepositoryLocator());
+                MavenPublisher staticLockingPublisher = new StaticLockingMavenPublisher(localPublisher);
                 MavenPublisher validatingPublisher = new ValidatingMavenPublisher(staticLockingPublisher);
-                validatingPublisher.publish(publication.asNormalisedPublication(), repository);
+                validatingPublisher.publish(publication.asNormalisedPublication(), null);
             }
         }.run();
     }
diff --git a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenRepository.java b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenRepository.java
index 8544184..8c94ba6 100644
--- a/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenRepository.java
+++ b/subprojects/maven/src/main/groovy/org/gradle/api/publish/maven/tasks/PublishToMavenRepository.java
@@ -16,24 +16,19 @@
 
 package org.gradle.api.publish.maven.tasks;
 
-import org.gradle.api.DefaultTask;
 import org.gradle.api.Incubating;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
-import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
 import org.gradle.api.publish.internal.PublishOperation;
-import org.gradle.api.publish.maven.MavenPublication;
 import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
-import org.gradle.api.publish.maven.internal.publisher.AntTaskBackedMavenPublisher;
 import org.gradle.api.publish.maven.internal.publisher.MavenPublisher;
+import org.gradle.api.publish.maven.internal.publisher.MavenRemotePublisher;
 import org.gradle.api.publish.maven.internal.publisher.StaticLockingMavenPublisher;
 import org.gradle.api.publish.maven.internal.publisher.ValidatingMavenPublisher;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.internal.Factory;
-import org.gradle.logging.LoggingManagerInternal;
 
 import javax.inject.Inject;
-import java.util.concurrent.Callable;
 
 /**
  * Publishes a {@link org.gradle.api.publish.maven.MavenPublication} to a {@link MavenArtifactRepository}.
@@ -41,71 +36,10 @@ import java.util.concurrent.Callable;
  * @since 1.4
  */
 @Incubating
-public class PublishToMavenRepository extends DefaultTask {
+public class PublishToMavenRepository extends AbstractPublishToMaven {
 
-    private MavenPublicationInternal publication;
     private MavenArtifactRepository repository;
 
-    private final Factory<LoggingManagerInternal> loggingManagerFactory;
-
-    @Inject
-    public PublishToMavenRepository(Factory<LoggingManagerInternal> loggingManagerFactory) {
-        this.loggingManagerFactory = loggingManagerFactory;
-
-
-        // Allow the publication to participate in incremental build
-        getInputs().files(new Callable<FileCollection>() {
-            public FileCollection call() throws Exception {
-                MavenPublicationInternal publicationInternal = getPublicationInternal();
-                return publicationInternal == null ? null : publicationInternal.getPublishableFiles();
-            }
-        });
-
-        // Should repositories be able to participate in incremental?
-        // At the least, they may be able to express themselves as output files
-        // They *might* have input files and other dependencies as well though
-        // Inputs: The credentials they need may be expressed in a file
-        // Dependencies: Can't think of a case here
-    }
-
-    /**
-     * The publication to be published.
-     *
-     * @return The publication to be published
-     */
-    public MavenPublication getPublication() {
-        return publication;
-    }
-
-    /**
-     * Sets the publication to be published.
-     *
-     * @param publication The publication to be published
-     */
-    public void setPublication(MavenPublication publication) {
-        this.publication = toPublicationInternal(publication);
-    }
-
-    private MavenPublicationInternal getPublicationInternal() {
-        return toPublicationInternal(getPublication());
-    }
-
-    private static MavenPublicationInternal toPublicationInternal(MavenPublication publication) {
-        if (publication == null) {
-            return null;
-        } else if (publication instanceof MavenPublicationInternal) {
-            return (MavenPublicationInternal) publication;
-        } else {
-            throw new InvalidUserDataException(
-                    String.format(
-                            "publication objects must implement the '%s' interface, implementation '%s' does not",
-                            MavenPublicationInternal.class.getName(),
-                            publication.getClass().getName()
-                    )
-            );
-        }
-    }
-
     /**
      * The repository to publish to.
      *
@@ -139,19 +73,21 @@ public class PublishToMavenRepository extends DefaultTask {
         doPublish(publicationInternal, repository);
     }
 
-    protected Factory<LoggingManagerInternal> getLoggingManagerFactory() {
-        return loggingManagerFactory;
-    }
-
-    protected void doPublish(final MavenPublicationInternal publication, final MavenArtifactRepository repository) {
-        new PublishOperation(publication, repository) {
+    private void doPublish(final MavenPublicationInternal publication, final MavenArtifactRepository repository) {
+        new PublishOperation(publication, repository.getName()) {
             @Override
             protected void publish() throws Exception {
-                MavenPublisher antBackedPublisher = new AntTaskBackedMavenPublisher(loggingManagerFactory, getTemporaryDirFactory());
-                MavenPublisher staticLockingPublisher = new StaticLockingMavenPublisher(antBackedPublisher);
+                MavenPublisher remotePublisher = new MavenRemotePublisher(getLoggingManagerFactory(), getMavenRepositoryLocator(), getTemporaryDirFactory(), getRepositoryTransportFactory());
+                MavenPublisher staticLockingPublisher = new StaticLockingMavenPublisher(remotePublisher);
                 MavenPublisher validatingPublisher = new ValidatingMavenPublisher(staticLockingPublisher);
                 validatingPublisher.publish(publication.asNormalisedPublication(), repository);
             }
         }.run();
     }
+
+
+    @Inject
+    protected RepositoryTransportFactory getRepositoryTransportFactory() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/subprojects/maven/src/main/java/org/gradle/maven/MavenModule.java b/subprojects/maven/src/main/java/org/gradle/maven/MavenModule.java
new file mode 100644
index 0000000..6cc1168
--- /dev/null
+++ b/subprojects/maven/src/main/java/org/gradle/maven/MavenModule.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.maven;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Component;
+
+/**
+ * A Maven Module component.
+ */
+ at Incubating
+public interface MavenModule extends Component {
+}
diff --git a/subprojects/maven/src/main/java/org/gradle/maven/MavenPomArtifact.java b/subprojects/maven/src/main/java/org/gradle/maven/MavenPomArtifact.java
new file mode 100644
index 0000000..b4fc76d
--- /dev/null
+++ b/subprojects/maven/src/main/java/org/gradle/maven/MavenPomArtifact.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.maven;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+/**
+ * A Maven POM artifact.
+ */
+ at Incubating
+public interface MavenPomArtifact extends Artifact {
+}
diff --git a/subprojects/maven/src/main/java/org/gradle/maven/package-info.java b/subprojects/maven/src/main/java/org/gradle/maven/package-info.java
new file mode 100644
index 0000000..db33ed5
--- /dev/null
+++ b/subprojects/maven/src/main/java/org/gradle/maven/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Component types for Maven modules.
+ */
+ at Incubating
+package org.gradle.maven;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven-publish.properties b/subprojects/maven/src/main/resources/META-INF/gradle-plugins/org.gradle.maven-publish.properties
similarity index 100%
rename from subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven-publish.properties
rename to subprojects/maven/src/main/resources/META-INF/gradle-plugins/org.gradle.maven-publish.properties
diff --git a/subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties b/subprojects/maven/src/main/resources/META-INF/gradle-plugins/org.gradle.maven.properties
similarity index 100%
rename from subprojects/maven/src/main/resources/META-INF/gradle-plugins/maven.properties
rename to subprojects/maven/src/main/resources/META-INF/gradle-plugins/org.gradle.maven.properties
diff --git a/subprojects/maven/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/maven/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..bc40635
--- /dev/null
+++ b/subprojects/maven/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.api.publish.maven.internal.MavenPublishServices
\ No newline at end of file
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
index cab7d04..fffcef4 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginConventionTest.groovy
@@ -17,7 +17,7 @@ package org.gradle.api.plugins
 
 import org.gradle.api.artifacts.maven.MavenPom
 import org.gradle.api.internal.project.DefaultProject
-import org.gradle.api.publication.maven.internal.DefaultMavenFactory
+import org.gradle.api.publication.maven.internal.pom.DefaultMavenFactory
 import org.gradle.api.publication.maven.internal.MavenFactory
 import org.gradle.util.TestUtil
 import spock.lang.Specification
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginTest.java
index f2e03b1..93ce00b 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginTest.java
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/plugins/MavenPluginTest.java
@@ -38,14 +38,14 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void addsConventionToProject() {
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         assertThat(project.getConvention().getPlugin(MavenPluginConvention.class), notNullValue());
     }
     
     @org.junit.Test
     public void defaultConventionValues() {
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         MavenPluginConvention convention = project.getConvention().getPlugin(MavenPluginConvention.class);
         assertThat(convention.getMavenPomDir(), equalTo(new File(project.getBuildDir(), "poms")));
@@ -54,8 +54,8 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void applyWithWarPlugin() {
-        project.getPlugins().apply(WarPlugin.class);
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(WarPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         assertHasConfigurationAndMapping(project, WarPlugin.PROVIDED_COMPILE_CONFIGURATION_NAME, Conf2ScopeMappingContainer.PROVIDED,
                 MavenPlugin.PROVIDED_COMPILE_PRIORITY);
@@ -77,8 +77,8 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void applyWithJavaPlugin() {
-        project.getPlugins().apply(JavaPlugin.class);
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(JavaPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         assertHasConfigurationAndMapping(project, JavaPlugin.COMPILE_CONFIGURATION_NAME, Conf2ScopeMappingContainer.COMPILE,
                 MavenPlugin.COMPILE_PRIORITY);
@@ -96,8 +96,8 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void addsAndConfiguresAnInstallTask() {
-        project.getPlugins().apply(JavaPlugin.class);
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(JavaPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         Upload task = project.getTasks().withType(Upload.class).getByName(MavenPlugin.INSTALL_TASK_NAME);
         assertThat(task.getRepositories().get(0), instanceOf(MavenResolver.class));
@@ -105,8 +105,8 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void addsConventionMappingToTheRepositoryContainerOfEachUploadTask() {
-        project.getPlugins().apply(JavaPlugin.class);
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(JavaPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         Upload task = project.getTasks().withType(Upload.class).getByName(MavenPlugin.INSTALL_TASK_NAME);
         MavenRepositoryHandlerConvention convention = new DslObject(task.getRepositories()).getConvention().getPlugin(MavenRepositoryHandlerConvention.class);
@@ -119,7 +119,7 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void applyWithoutWarPlugin() {
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         assertThat(project.getConfigurations().findByName(WarPlugin.PROVIDED_COMPILE_CONFIGURATION_NAME),
                 nullValue());
@@ -127,7 +127,7 @@ public class MavenPluginTest {
 
     @org.junit.Test
     public void applyWithoutJavaPlugin() {
-        project.getPlugins().apply(MavenPlugin.class);
+        project.getPluginManager().apply(MavenPlugin.class);
 
         assertThat(project.getConfigurations().findByName(JavaPlugin.COMPILE_CONFIGURATION_NAME),
                 nullValue());
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultArtifactPomTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultArtifactPomTest.java
index 32ef920..c1a7b95 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultArtifactPomTest.java
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultArtifactPomTest.java
@@ -25,6 +25,8 @@ import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.MavenPom;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.publication.maven.internal.pom.DefaultMavenPom;
+import org.gradle.api.publication.maven.internal.pom.PomDependenciesConverter;
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainerTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainerTest.java
deleted file mode 100644
index aed0e20..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultConf2ScopeMappingContainerTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static java.util.Arrays.asList;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.*;
-
- at RunWith(JUnit4.class)
-public class DefaultConf2ScopeMappingContainerTest {
-    private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private DefaultConf2ScopeMappingContainer conf2ScopeMappingContainer;
-    private final Configuration testConf1 = context.mock(Configuration.class);
-    private final Configuration testConf2 = context.mock(Configuration.class);
-    private final Configuration testConf3 = context.mock(Configuration.class);
-    private static final String TEST_SCOPE_1 = "test";
-    private static final String TEST_SCOPE_2 = "test2";
-    private static final int TEST_PRIORITY_1 = 10;
-    private static final int TEST_PRIORITY_2 = 20;
-
-    @Before
-    public void setUp() {
-        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer();
-        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1);
-    }
-
-    @Test
-    public void init() {
-        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer();
-        assertTrue(conf2ScopeMappingContainer.isSkipUnmappedConfs());
-        assertEquals(0, conf2ScopeMappingContainer.getMappings().size());
-        Map<Configuration, Conf2ScopeMapping> testMappings = createTestMappings();
-        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer(testMappings);
-        assertNotSame(testMappings, conf2ScopeMappingContainer.getMappings());
-        assertEquals(testMappings, conf2ScopeMappingContainer.getMappings());
-    }
-
-    @Test
-    public void equalsAndHashCode() {
-        Map<Configuration, Conf2ScopeMapping> testMappings = createTestMappings();
-        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer(testMappings);
-        assertTrue(conf2ScopeMappingContainer.equals(new DefaultConf2ScopeMappingContainer(testMappings)));
-        assertEquals(conf2ScopeMappingContainer.hashCode(), new DefaultConf2ScopeMappingContainer(testMappings).hashCode());
-        conf2ScopeMappingContainer.addMapping(10, context.mock(Configuration.class), "scope");
-        assertFalse(conf2ScopeMappingContainer.equals(new DefaultConf2ScopeMappingContainer(testMappings)));
-    }
-
-    private Map<Configuration, Conf2ScopeMapping> createTestMappings() {
-        Map<Configuration, Conf2ScopeMapping> testMappings = new HashMap<Configuration, Conf2ScopeMapping>() {{
-            Configuration configuration = context.mock(Configuration.class);
-            put(configuration, new Conf2ScopeMapping(10, configuration, "scope"));
-        }};
-        return testMappings;
-    }
-
-    @Test
-    public void addGetMapping() {
-        assertEquals(new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1),
-                conf2ScopeMappingContainer.getMapping(asList(testConf1)));
-    }
-
-    @Test
-    public void singleMappedConfiguration() {
-        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1)), equalTo(
-                new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1)));
-    }
-
-    @Test
-    public void unmappedConfiguration() {
-        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf2)), equalTo(
-                new Conf2ScopeMapping(null, testConf2, null)));
-    }
-
-    @Test
-    public void mappedConfigurationAndUnmappedConfiguration() {
-        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2)), equalTo(
-                new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1)));
-    }
-
-    @Test
-    public void mappingWithDifferentPrioritiesDifferentConfsDifferentScopes() {
-        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_2, testConf2, TEST_SCOPE_2);
-        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2)), equalTo(
-                new Conf2ScopeMapping(TEST_PRIORITY_2, testConf2, TEST_SCOPE_2)));
-    }
-    
-    @Test(expected = InvalidUserDataException.class)
-    public void mappingWithSamePrioritiesDifferentConfsSameScope() {
-        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf2, TEST_SCOPE_1);
-        conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2));
-    }
-
-    @Test(expected = InvalidUserDataException.class)
-    public void mappingWithSamePrioritiesDifferentConfsDifferentScopes() {
-        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf2, TEST_SCOPE_1);
-        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf3, TEST_SCOPE_2);
-        conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2, testConf3));
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactoryTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactoryTest.groovy
deleted file mode 100644
index 74b7278..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomFactoryTest.groovy
+++ /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.publication.maven.internal
-
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.internal.file.FileResolver
-import spock.lang.Specification
-
-public class DefaultMavenPomFactoryTest extends Specification {
-    def createMavenPom() {
-        DefaultConf2ScopeMappingContainer scopeMappings = new DefaultConf2ScopeMappingContainer();
-        PomDependenciesConverter pomDependenciesConverter = Mock(PomDependenciesConverter);
-        ConfigurationContainer configurationContainer = Mock(ConfigurationContainer); 
-        FileResolver fileResolver = Mock(FileResolver); 
-        DefaultMavenPomFactory mavenPomFactory = new DefaultMavenPomFactory(configurationContainer, scopeMappings,
-                pomDependenciesConverter, fileResolver);
-        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.create();
-
-        expect:
-        !scopeMappings.is(mavenPom.scopeMappings)
-        scopeMappings == mavenPom.scopeMappings
-        mavenPom.mavenProject != null
-        mavenPom.pomDependenciesConverter.is(pomDependenciesConverter)
-        mavenPom.configurations.is(configurationContainer)
-        mavenPom.fileResolver == fileResolver
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomTest.groovy
deleted file mode 100644
index 9609dcb..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/DefaultMavenPomTest.groovy
+++ /dev/null
@@ -1,186 +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.publication.maven.internal
-
-import org.apache.commons.lang.builder.EqualsBuilder
-import org.apache.maven.model.Dependency
-import org.apache.maven.model.Model
-import org.gradle.api.Action
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.TextUtil
-import org.junit.Rule
-import spock.lang.Specification
-
-import static org.gradle.api.artifacts.maven.MavenPom.POM_FILE_ENCODING
-
-class DefaultMavenPomTest extends Specification {
-    static final String EXPECTED_PACKAGING = "something";
-    static final String EXPECTED_GROUP_ID = "someGroup";
-    static final String EXPECTED_ARTIFACT_ID = "artifactId";
-    static final String EXPECTED_VERSION = "v\u00E9rsi\u00F8n"; // note the utf-8 chars
-
-    @Rule
-    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-
-    Conf2ScopeMappingContainer conf2ScopeMappingContainer = Mock()
-    PomDependenciesConverter pomDependenciesConverterStub = Mock()
-    ConfigurationContainer configurationContainerStub = Mock()
-    FileResolver fileResolver = Mock()
-    DefaultMavenPom mavenPom = new DefaultMavenPom(configurationContainerStub, conf2ScopeMappingContainer, pomDependenciesConverterStub,
-            fileResolver)
-
-    void setup() {
-        mavenPom.packaging = EXPECTED_PACKAGING
-        mavenPom.groupId = EXPECTED_GROUP_ID
-        mavenPom.artifactId = EXPECTED_ARTIFACT_ID
-        mavenPom.version = EXPECTED_VERSION
-    }
-
-    def init() {
-        expect:
-        mavenPom.scopeMappings.is(conf2ScopeMappingContainer)
-        mavenPom.configurations.is(configurationContainerStub)
-        mavenPom.fileResolver.is(fileResolver)
-        mavenPom.mavenProject.modelVersion == "4.0.0"
-    }
-
-    def setModel() {
-        def newModel = new Model()
-
-        when:
-        mavenPom.model = newModel
-
-        then:
-        mavenPom.model.is(newModel)
-    }
-
-    def effectivePomShouldHaveGeneratedDependencies() {
-        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
-        List manuallyAddedDependencies = [new Dependency()]
-        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurationContainerStub) >> generatedDependencies
-
-        when:
-        mavenPom.dependencies = manuallyAddedDependencies.clone()
-
-        then:
-        EqualsBuilder.reflectionEquals(mavenPom.getEffectivePom().getMavenProject().getDependencies(), manuallyAddedDependencies + generatedDependencies)
-
-        when:
-        mavenPom.dependencies = []
-
-        then:
-        mavenPom.getEffectivePom().getMavenProject().getDependencies() == generatedDependencies
-    }
-
-    def configureActionsShouldBeAppliedAgainstEffectivePom() {
-        mavenPom.configurations = null
-        when:
-        mavenPom.whenConfigured(new Action() {
-            void execute(def mavenPom) {
-                mavenPom.mavenProject.inceptionYear = '1999'
-            }
-        })
-
-        then:
-        mavenPom.effectivePom.mavenProject.inceptionYear == '1999'
-        mavenPom.mavenProject.inceptionYear == null
-    }
-
-
-    def writeShouldUseEffectivePom() {
-        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
-        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurationContainerStub) >> generatedDependencies
-
-        when:
-        StringWriter pomWriter = new StringWriter()
-        mavenPom.writeTo pomWriter
-
-        then:
-        pomWriter.toString().contains('someGroup')
-    }
-
-    def effectivePomWithNullConfigurationsShouldWork() {
-        when:
-        mavenPom.configurations = null
-
-        then:
-        mavenPom.getEffectivePom().getMavenProject().getDependencies() == []
-    }
-
-    void projectBuilder() {
-        mavenPom.mavenProject.inceptionYear = '2007'
-        mavenPom.mavenProject.description = 'some description'
-        mavenPom.project {
-            inceptionYear '2008'
-            licenses {
-                license {
-                    name 'The Apache Software License, Version 2.0'
-                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-                    distribution 'repo'
-                }
-            }
-        }
-
-        expect:
-        mavenPom.mavenProject.modelVersion == "4.0.0"
-        mavenPom.version == EXPECTED_VERSION
-        mavenPom.mavenProject.description == 'some description'
-        mavenPom.mavenProject.inceptionYear == '2008'
-        mavenPom.mavenProject.licenses.size() == 1
-        mavenPom.mavenProject.licenses[0].name == 'The Apache Software License, Version 2.0'
-        mavenPom.mavenProject.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
-        mavenPom.mavenProject.licenses[0].distribution == 'repo'
-    }
-
-    void writeToShouldApplyXmlActions() {
-        mavenPom.configurations = null
-        StringWriter pomWriter = new StringWriter()
-
-        when:
-        mavenPom.withXml {xmlProvider ->
-            xmlProvider.asString().append('someAppendix')
-        }
-        mavenPom.writeTo(pomWriter);
-
-        then:
-        pomWriter.toString().endsWith("someAppendix")
-    }
-
-    void writeToWritesCorrectPom() {
-        mavenPom.configurations = null
-        TestFile pomFile = tmpDir.file('someNonexistingDir').file('someFile')
-        fileResolver.resolve('file') >> pomFile
-
-        when:
-        mavenPom.writeTo('file');
-
-        then:
-        pomFile.getText(POM_FILE_ENCODING) == TextUtil.toPlatformLineSeparators("""<?xml version="1.0" encoding="${POM_FILE_ENCODING}"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>${EXPECTED_GROUP_ID}</groupId>
-  <artifactId>${EXPECTED_ARTIFACT_ID}</artifactId>
-  <version>${EXPECTED_VERSION}</version>
-  <packaging>${EXPECTED_PACKAGING}</packaging>
-</project>
-""")
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapperTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapperTest.groovy
new file mode 100644
index 0000000..d4ccff0
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/MavenVersionRangeMapperTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal
+
+import spock.lang.Specification
+
+class MavenVersionRangeMapperTest extends Specification {
+    def "null version is mapped to null"() {
+        expect:
+        new MavenVersionRangeMapper().map(null) == null
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverterTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverterTest.java
deleted file mode 100644
index 8746c9f..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultExcludeRuleConverterTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.internal.artifacts.DefaultExcludeRule;
-import org.junit.Before;
-import org.junit.Test;
-
-import static junit.framework.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-public class DefaultExcludeRuleConverterTest {
-    private static final String TEST_ORG = "org";
-    private static final String TEST_MODULE = "module";
-
-    private DefaultExcludeRuleConverter excludeRuleConverter;
-
-    @Before
-    public void setUp() {
-        excludeRuleConverter = new DefaultExcludeRuleConverter();   
-    }
-    
-    @Test
-    public void convertableRule() {
-        DefaultExcludeRule excludeRule = new DefaultExcludeRule(TEST_ORG, TEST_MODULE);
-        Exclusion mavenExclude = excludeRuleConverter.convert(excludeRule);
-        assertEquals(TEST_ORG, mavenExclude.getGroupId());
-        assertEquals(TEST_MODULE, mavenExclude.getArtifactId());
-    }
-    
-    @Test
-    public void unconvertableRules() {
-        checkForNull(new DefaultExcludeRule(TEST_ORG, null));
-        checkForNull(new DefaultExcludeRule(null, TEST_MODULE));
-    }
-
-    private void checkForNull(DefaultExcludeRule excludeRule) {
-        assertNull(excludeRuleConverter.convert(excludeRule));
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployerTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployerTest.groovy
deleted file mode 100644
index dc3ac19..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyMavenDeployerTest.groovy
+++ /dev/null
@@ -1,110 +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.publication.maven.internal.ant
-
-import org.gradle.api.artifacts.maven.PomFilterContainer
-import org.gradle.api.publication.maven.internal.ArtifactPomContainer
-import org.gradle.logging.LoggingManagerInternal
-import org.gradle.util.JUnit4GroovyMockery
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import static org.junit.Assert.assertEquals
-
- at RunWith (org.jmock.integration.junit4.JMock.class)
-class DefaultGroovyMavenDeployerTest {
-    protected JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    protected ArtifactPomContainer artifactPomContainerMock = context.mock(ArtifactPomContainer)
-    protected PomFilterContainer pomFilterContainerMock = context.mock(PomFilterContainer);
-    protected LoggingManagerInternal loggingManagerMock = context.mock(LoggingManagerInternal);
-    private DefaultGroovyMavenDeployer groovyMavenDeployer = new DefaultGroovyMavenDeployer(pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock)
-
-    protected PomFilterContainer createPomFilterContainerMock() {
-        context.mock(PomFilterContainer.class);
-    }
-
-    @Test
-    void repositoryBuilder() {
-        checkRepositoryBuilder(DefaultGroovyMavenDeployer.REPOSITORY_BUILDER)
-    }
-
-    @Test
-    void snapshotRepositoryBuilder() {
-        checkRepositoryBuilder(DefaultGroovyMavenDeployer.SNAPSHOT_REPOSITORY_BUILDER)
-    }
-
-
-    void checkRepositoryBuilder(String repositoryName) {
-        String testUrl = 'testUrl'
-        String testProxyHost = 'hans'
-        String testUserName = 'userId'
-        String testSnapshotUpdatePolicy = 'always'
-        String testReleaseUpdatePolicy = 'never'
-        groovyMavenDeployer."$repositoryName"(url: testUrl) {
-            authentication(userName: testUserName)
-            proxy(host: testProxyHost)
-            releases(updatePolicy: testReleaseUpdatePolicy)
-            snapshots(updatePolicy: testSnapshotUpdatePolicy)
-        }
-        assertEquals(testUrl, groovyMavenDeployer."$repositoryName".url)
-        assertEquals(testUserName, groovyMavenDeployer."$repositoryName".authentication.userName)
-        assertEquals(testProxyHost, groovyMavenDeployer."$repositoryName".proxy.host)
-        assertEquals(testReleaseUpdatePolicy, groovyMavenDeployer."$repositoryName".releases.updatePolicy)
-        assertEquals(testSnapshotUpdatePolicy, groovyMavenDeployer."$repositoryName".snapshots.updatePolicy)
-    }
-
-    @Test
-    void filter() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).filter(testClosure)
-        }
-        groovyMavenDeployer.filter(testClosure)
-    }
-
-    @Test
-    void pom() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).pom(testClosure)
-        }
-        groovyMavenDeployer.pom(testClosure)
-    }
-
-    @Test
-    void pomWithName() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).pom(testName, testClosure)
-        }
-        groovyMavenDeployer.pom(testName, testClosure)
-    }
-
-    @Test
-    void addFilter() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).addFilter(testName, testClosure)
-        }
-        groovyMavenDeployer.addFilter(testName, testClosure)
-    }
-}
-
-
-
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyPomFilterContainerTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyPomFilterContainerTest.groovy
deleted file mode 100644
index 117fd73..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultGroovyPomFilterContainerTest.groovy
+++ /dev/null
@@ -1,120 +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.publication.maven.internal.ant
-
-import org.gradle.api.artifacts.maven.MavenPom
-import org.gradle.api.artifacts.maven.PomFilterContainer
-import org.gradle.api.artifacts.maven.PublishFilter
-import org.gradle.api.publication.maven.internal.BasePomFilterContainer
-import org.gradle.api.publication.maven.internal.BasePomFilterContainerTest
-import org.hamcrest.BaseMatcher
-import org.hamcrest.Description
-import org.hamcrest.Factory
-import org.hamcrest.Matcher
-import org.jmock.integration.junit4.JMock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import java.lang.reflect.Proxy
-
-import static org.junit.Assert.assertSame
-
- at RunWith(JMock)
-class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
-    static final String TEST_NAME = "somename"
-    PomFilterContainer groovyPomFilterContainer
-
-    @Before
-    public void setUp() {
-        super.setUp()
-    }
-
-    protected BasePomFilterContainer createPomFilterContainer() {
-        return groovyPomFilterContainer = new BasePomFilterContainer(mavenPomFactoryMock);
-    }
-
-    @Test
-    public void addFilterWithClosure() {
-        Closure closureFilter = {}
-        MavenPom pom = groovyPomFilterContainer.addFilter(TEST_NAME, closureFilter)
-        assertSame(pomMock, pom);
-        assertSame(pomMock, groovyPomFilterContainer.pom(TEST_NAME));
-        assertSame(closureFilter, getClosureFromProxy(groovyPomFilterContainer.filter(TEST_NAME)));
-    }
-
-    private Closure getClosureFromProxy(PublishFilter filter) {
-        Proxy.getInvocationHandler(filter).delegate
-    }
-
-    @Test
-    public void filterWithClosure() {
-        Closure closureFilter = {}
-        context.checking {
-            one(pomFilterMock).setFilter(withParam(FilterMatcher.equalsFilter(closureFilter)))
-        }
-        groovyPomFilterContainer.filter(closureFilter)
-    }
-
-    @Test
-    public void defaultPomWithClosure() {
-        String testGroup = "testGroup"
-        context.checking {
-            one(pomFilterMock).getPomTemplate(); will(returnValue(pomMock))
-            one(pomMock).setGroupId(testGroup);
-        }
-        groovyPomFilterContainer.pom {
-            groupId = testGroup
-        }
-    }
-
-    @Test
-    public void pomWithClosure() {
-        groovyPomFilterContainer.addFilter(TEST_NAME, {})
-        String testGroup = "testGroup"
-        context.checking {
-            one(pomMock).setGroupId(testGroup);
-        }
-        groovyPomFilterContainer.pom(TEST_NAME) {
-            groupId = testGroup
-        }
-    }
-}
-
-public class FilterMatcher extends BaseMatcher {
-    Closure filter
-
-    public void describeTo(Description description) {
-        description.appendText("matching filter");
-    }
-
-    public boolean matches(Object actual) {
-        return getClosureFromProxy(actual) == filter;
-    }
-
-    private Closure getClosureFromProxy(PublishFilter filter) {
-        Proxy.getInvocationHandler(filter).delegate
-    }
-
-
-    @Factory
-    public static Matcher<PublishFilter> equalsFilter(Closure filter) {
-        return new FilterMatcher(filter: filter);
-    }
-
-}
-
-
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverterTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverterTest.java
deleted file mode 100644
index 9a668fa..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/DefaultPomDependenciesConverterTest.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.publication.maven.internal.ant;
-
-import org.apache.maven.model.Exclusion;
-import org.gradle.api.artifacts.*;
-import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
-import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
-import org.gradle.api.internal.artifacts.DefaultExcludeRule;
-import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact;
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
-import org.gradle.api.publication.maven.internal.ExcludeRuleConverter;
-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.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static java.util.Arrays.asList;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class DefaultPomDependenciesConverterTest {
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-    
-    private DefaultPomDependenciesConverter dependenciesConverter;
-    private Conf2ScopeMappingContainer conf2ScopeMappingContainerMock = context.mock(Conf2ScopeMappingContainer.class);
-    private ExcludeRuleConverter excludeRuleConverterMock = context.mock(ExcludeRuleConverter.class);
-
-    private ModuleDependency dependency1;
-    private ModuleDependency dependency2;
-    private ModuleDependency dependency31;
-    private ModuleDependency dependency32;
-    private Configuration compileConfStub;
-    private Configuration testCompileConfStub;
-
-    @Before
-    public void setUp() {
-        setUpCommonDependenciesAndConfigurations();
-        dependenciesConverter = new DefaultPomDependenciesConverter(excludeRuleConverterMock);
-    }
-
-    private void setUpCommonDependenciesAndConfigurations() {
-        dependency1 = createDependency("org1", "name1", "rev1");
-        dependency2 = createDependency("org2", "name2", "rev2");
-        dependency2.addArtifact(new DefaultDependencyArtifact("name2", null, null, null, null));
-        dependency31 = createDependency("org3", "name3", "rev3");
-        dependency32 = createDependency("org3", "name3", "rev3");
-        dependency32.addArtifact(new DefaultDependencyArtifact("artifactName32", "type32", "ext", "classifier32", null));
-        compileConfStub = createNamedConfigurationStubWithDependencies("compile", dependency1, dependency31);
-        testCompileConfStub = createNamedConfigurationStubWithDependencies("testCompile", dependency2, dependency32);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub, compileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub, testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(testCompileConfStub)); will(returnValue(createMapping(testCompileConfStub, "test")));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(compileConfStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-        }});
-    }
-
-    private Conf2ScopeMapping createMapping(Configuration configuration, String scope) {
-        return new Conf2ScopeMapping(10, configuration, scope);
-    }
-
-    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final ModuleDependency... dependencies) {
-        return createNamedConfigurationStubWithDependencies(confName, new HashSet<ExcludeRule>(), dependencies);
-    }
-    
-    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final Set<ExcludeRule> excludeRules, final ModuleDependency... dependencies) {
-        final Configuration configurationStub = context.mock(Configuration.class, confName);
-        final DependencySet dependencySet = context.mock(DependencySet.class);
-
-        context.checking(new Expectations() {{
-            allowing(configurationStub).getName();
-            will(returnValue(confName));
-            allowing(configurationStub).getDependencies();
-            will(returnValue(dependencySet));
-            allowing(dependencySet).withType(ModuleDependency.class);
-            will(returnValue(toDomainObjectSet(ModuleDependency.class, dependencies)));
-            allowing(configurationStub).getExcludeRules();
-            will(returnValue(excludeRules));
-        }});
-        return configurationStub;
-    }
-
-    private ModuleDependency createDependency(final String group, final String name, final String version) {
-        return new DefaultExternalModuleDependency(group, name, version);
-    }
-
-    @Test
-    public void init() {
-        assertSame(excludeRuleConverterMock, dependenciesConverter.getExcludeRuleConverter());
-    }
-
-    @Test
-    public void convert() {
-        Set<Configuration> configurations = toSet(compileConfStub, testCompileConfStub);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, configurations);
-        assertEquals(4, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-    }
-
-    @Test
-    public void convertWithUnMappedConfAndSkipTrue() {
-        final Dependency dependency4 = createDependency("org4", "name4", "rev4");
-        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf");
-        context.checking(new Expectations() {{
-            allowing(unmappedConfigurationStub).getDependencies();
-            will(returnValue(toSet(dependency4)));
-        }});
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(true));
-            allowing(conf2ScopeMappingContainerMock).getMapping(asList(unmappedConfigurationStub)); will(returnValue(null));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
-                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
-        assertEquals(4, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-    }
-
-    @Test
-    public void convertWithUnMappedConfAndSkipFalse() {
-        final ModuleDependency dependency4 = createDependency("org4", "name4", "rev4");
-        final Configuration unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf", dependency4);
-        context.checking(new Expectations() {{
-            allowing(conf2ScopeMappingContainerMock).isSkipUnmappedConfs(); will(returnValue(false));
-            allowing(conf2ScopeMappingContainerMock).getMapping(toSet(unmappedConfigurationStub)); will(returnValue(new Conf2ScopeMapping(null, unmappedConfigurationStub, null)));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(
-                compileConfStub, testCompileConfStub, unmappedConfigurationStub));
-        assertEquals(5, actualMavenDependencies.size());
-        checkCommonMavenDependencies(actualMavenDependencies);
-        assertTrue(hasDependency(actualMavenDependencies, "org4", "name4", "rev4", null, null, null, false));
-    }
-
-    private void checkCommonMavenDependencies(List<org.apache.maven.model.Dependency> actualMavenDependencies) {
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        assertTrue(hasDependency(actualMavenDependencies, "org2", "name2", "rev2", null, "test", null, false));
-        assertTrue(hasDependency(actualMavenDependencies, "org3", "name3", "rev3", null, "test", null, false));
-        assertTrue(hasDependency(actualMavenDependencies, "org3", "artifactName32", "rev3", "type32", "test", "classifier32", false));
-    }
-
-    private boolean hasDependency(List<org.apache.maven.model.Dependency> mavenDependencies,
-                                  String group, String artifactId, String version, String type, String scope,
-                                  String classifier, boolean optional) {
-        org.apache.maven.model.Dependency expectedDependency = new org.apache.maven.model.Dependency();
-        expectedDependency.setGroupId(group);
-        expectedDependency.setArtifactId(artifactId);
-        expectedDependency.setVersion(version);
-        expectedDependency.setType(type);
-        expectedDependency.setScope(scope);
-        expectedDependency.setClassifier(classifier);
-        expectedDependency.setOptional(optional);
-        for (org.apache.maven.model.Dependency mavenDependency : mavenDependencies) {
-            if (equals(mavenDependency, expectedDependency)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean equals(org.apache.maven.model.Dependency lhs, org.apache.maven.model.Dependency rhs) {
-        if (!lhs.getGroupId().equals(lhs.getGroupId())) {
-            return false;
-        }
-        if (!lhs.getArtifactId().equals(lhs.getArtifactId())) {
-            return false;
-        }
-        if (!lhs.getVersion().equals(lhs.getVersion())) {
-            return false;
-        }
-        if (lhs.getType() != null ? !lhs.getType().equals(lhs.getType()) : rhs.getType() != null) {
-            return false;
-        }
-        if (lhs.getScope() != null ? !lhs.getScope().equals(lhs.getScope()) : rhs.getScope() != null) {
-            return false;
-        }
-        if (!lhs.isOptional() == lhs.isOptional()) {
-            return false;
-        }
-        if (lhs.getClassifier() != null ? !lhs.getClassifier().equals(rhs.getClassifier()) : rhs.getClassifier() != null) {
-            return false;
-        }
-        return true;
-    }
-
-    @Test
-    public void convertWithConvertableDependencyExcludes() {
-        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", dependency1);
-        final Exclusion mavenExclude = new Exclusion();
-        mavenExclude.setGroupId("a");
-        mavenExclude.setArtifactId("b");
-        dependency1.exclude(toMap(ExcludeRule.GROUP_KEY, "value"));
-        context.checking(new Expectations() {{
-           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-           allowing(excludeRuleConverterMock).convert(dependency1.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
-        assertEquals(1, actualMavenDependencies.size());
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
-        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
-    }
-    
-    @Test
-    public void convertWithConvertableConfigurationExcludes() {
-        final Configuration someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", 
-                WrapUtil.<ExcludeRule>toSet(new DefaultExcludeRule("value", null)), dependency1);
-        final Exclusion mavenExclude = new Exclusion();
-        mavenExclude.setGroupId("a");
-        mavenExclude.setArtifactId("b");
-        context.checking(new Expectations() {{
-           allowing(conf2ScopeMappingContainerMock).getMapping(toSet(someConfigurationStub)); will(returnValue(createMapping(compileConfStub, "compile")));
-           allowing(excludeRuleConverterMock).convert(someConfigurationStub.getExcludeRules().iterator().next()); will(returnValue(mavenExclude));
-        }});
-        List<org.apache.maven.model.Dependency> actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub));
-        assertEquals(1, actualMavenDependencies.size());
-        assertTrue(hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false));
-        org.apache.maven.model.Dependency mavenDependency = (org.apache.maven.model.Dependency) actualMavenDependencies.get(0);
-        assertThat(mavenDependency.getExclusions().size(), equalTo(1));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getGroupId(), equalTo(mavenExclude.getGroupId()));
-        assertThat(((Exclusion) mavenDependency.getExclusions().get(0)).getArtifactId(), equalTo(mavenExclude.getArtifactId()));
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplierTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplierTest.groovy
deleted file mode 100644
index 6c92ac7..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/EmptyMavenSettingsSupplierTest.groovy
+++ /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.publication.maven.internal.ant
-
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport
-import spock.lang.Specification
-
-class EmptyMavenSettingsSupplierTest extends Specification {
-
-    def EmptyMavenSettingsSupplier supplier = new EmptyMavenSettingsSupplier()
-    InstallDeployTaskSupport support = Mock()
-
-    def "supplies empty settings"() {
-        when:
-        supplier.supply(support)
-
-        then:
-        supplier.settingsXml.text == '<settings/>'
-//        1 * support.setSettingsFile(supplier.settingsXml) //not sure why it doesn't work
-    }
-
-    def "deletes file when done"() {
-        when:
-        supplier.supply(support)
-        supplier.done()
-
-        then:
-        !supplier.settingsXml.exists()
-    }
-
-    def "done operation must be safe"() {
-        when:
-        supplier.done()
-
-        then:
-        noExceptionThrown()
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplierTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplierTest.groovy
deleted file mode 100644
index eb24887..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/MaybeUserMavenSettingsSupplierTest.groovy
+++ /dev/null
@@ -1,61 +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.publication.maven.internal.ant
-
-import org.apache.maven.artifact.ant.InstallDeployTaskSupport
-import org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenFileLocations
-import spock.lang.Specification
-
-class MaybeUserMavenSettingsSupplierTest extends Specification {
-
-    InstallDeployTaskSupport support = Mock()
-    def supplier = new MaybeUserMavenSettingsSupplier()
-
-    def "supplies empty settings when user settings not found"() {
-        given:
-        supplier.emptySettingsSupplier = Mock(EmptyMavenSettingsSupplier)
-        supplier.mavenFileLocations = Mock(DefaultMavenFileLocations)
-
-        supplier.mavenFileLocations.getUserSettingsFile() >> { new File('does not exist') }
-
-        when:
-        supplier.supply(support)
-        supplier.done()
-
-        then:
-        1 * supplier.emptySettingsSupplier.supply(support)
-        1 * supplier.emptySettingsSupplier.done()
-    }
-
-    def "supplies user settings when file exists"() {
-        given:
-        supplier.emptySettingsSupplier = Mock(EmptyMavenSettingsSupplier)
-        supplier.mavenFileLocations = Mock(DefaultMavenFileLocations)
-
-        def concreteFile = File.createTempFile('I exist', ', really')
-        concreteFile.deleteOnExit()
-        supplier.mavenFileLocations.getUserSettingsFile() >> { concreteFile }
-
-        when:
-        supplier.supply(support)
-        supplier.done()
-
-        then:
-        1 * support.setSettingsFile(concreteFile)
-        0 * supplier.emptySettingsSupplier.supply(support)
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHackTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHackTest.groovy
deleted file mode 100644
index 0f3a272..0000000
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/ant/ProjectDependencyArtifactIdExtractorHackTest.groovy
+++ /dev/null
@@ -1,90 +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.publication.maven.internal.ant
-
-import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.api.plugins.MavenPlugin
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-import spock.lang.Issue
-
- at Issue("GRADLE-443")
-class ProjectDependencyArtifactIdExtractorHackTest extends Specification {
-    def project = TestUtil.createRootProject()
-    def extractor = new ProjectDependencyArtifactIdExtractorHack(new DefaultProjectDependency(project, null, true))
-
-    def "artifact ID defaults to project name if neither archivesBaseName nor mavenDeployer.pom.artifactId is configured"() {
-        expect:
-        extractor.extract() == project.name
-    }
-
-    def "artifact ID honors archivesBaseName"() {
-        project.plugins.apply(BasePlugin)
-        project.archivesBaseName = "changed"
-
-        expect:
-        extractor.extract() == "changed"
-    }
-
-    def "artifact ID honors mavenDeployer.pom.artifactId over archivesBaseName"() {
-        project.plugins.apply(MavenPlugin)
-
-        project.archivesBaseName = "changed"
-        project.uploadArchives {
-            repositories.mavenDeployer {
-                pom.artifactId = "changed2"
-            }
-        }
-
-        expect:
-        extractor.extract() == "changed2"
-    }
-
-    def "artifact ID defaults to project name if Ivy repository is configured"() {
-        project.plugins.apply(BasePlugin)
-        project.archivesBaseName = "changed"
-
-        project.uploadArchives {
-            repositories {
-                ivy {}
-            }
-        }
-
-        expect:
-        extractor.extract() == project.name
-    }
-
-    def "artifact ID defaults to project name if different mavenDeployer.pom.artifactId's are configured"() {
-        project.plugins.apply(MavenPlugin)
-
-        project.configurations { other }
-        project.uploadArchives {
-            repositories.mavenDeployer {
-                pom.artifactId = "changed"
-            }
-        }
-        project.uploadOther {
-            repositories.mavenDeployer {
-                pom.artifactId = "changed2"
-            }
-        }
-
-        expect:
-        extractor.extract() == project.name
-    }
-}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployerTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployerTest.groovy
new file mode 100644
index 0000000..88cbb76
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/deployer/DefaultGroovyMavenDeployerTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * 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.publication.maven.internal.deployer
+
+import org.gradle.api.artifacts.maven.PomFilterContainer
+import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator
+import org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider
+import org.gradle.api.publication.maven.internal.ArtifactPomContainer
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.util.JUnit4GroovyMockery
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import static org.junit.Assert.assertEquals
+
+ at RunWith (org.jmock.integration.junit4.JMock.class)
+class DefaultGroovyMavenDeployerTest {
+    protected JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    protected ArtifactPomContainer artifactPomContainerMock = context.mock(ArtifactPomContainer)
+    protected PomFilterContainer pomFilterContainerMock = context.mock(PomFilterContainer);
+    protected LoggingManagerInternal loggingManagerMock = context.mock(LoggingManagerInternal);
+    protected MavenSettingsProvider mavenSettingsProvider = context.mock(MavenSettingsProvider)
+    protected LocalMavenRepositoryLocator mavenRepositoryLocator = context.mock(LocalMavenRepositoryLocator)
+    private DefaultGroovyMavenDeployer groovyMavenDeployer = new DefaultGroovyMavenDeployer(pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock, mavenSettingsProvider, mavenRepositoryLocator)
+
+    protected PomFilterContainer createPomFilterContainerMock() {
+        context.mock(PomFilterContainer.class);
+    }
+
+    @Test
+    void repositoryBuilder() {
+        checkRepositoryBuilder(DefaultGroovyMavenDeployer.REPOSITORY_BUILDER)
+    }
+
+    @Test
+    void snapshotRepositoryBuilder() {
+        checkRepositoryBuilder(DefaultGroovyMavenDeployer.SNAPSHOT_REPOSITORY_BUILDER)
+    }
+
+
+    void checkRepositoryBuilder(String repositoryName) {
+        String testUrl = 'testUrl'
+        String testProxyHost = 'hans'
+        String testUserName = 'userId'
+        String testSnapshotUpdatePolicy = 'always'
+        String testReleaseUpdatePolicy = 'never'
+        groovyMavenDeployer."$repositoryName"(url: testUrl) {
+            authentication(userName: testUserName)
+            proxy(host: testProxyHost)
+            releases(updatePolicy: testReleaseUpdatePolicy)
+            snapshots(updatePolicy: testSnapshotUpdatePolicy)
+        }
+        assertEquals(testUrl, groovyMavenDeployer."$repositoryName".url)
+        assertEquals(testUserName, groovyMavenDeployer."$repositoryName".authentication.userName)
+        assertEquals(testProxyHost, groovyMavenDeployer."$repositoryName".proxy.host)
+        assertEquals(testReleaseUpdatePolicy, groovyMavenDeployer."$repositoryName".releases.updatePolicy)
+        assertEquals(testSnapshotUpdatePolicy, groovyMavenDeployer."$repositoryName".snapshots.updatePolicy)
+    }
+
+    @Test
+    void filter() {
+        Closure testClosure = {}
+        context.checking {
+            one(pomFilterContainerMock).filter(testClosure)
+        }
+        groovyMavenDeployer.filter(testClosure)
+    }
+
+    @Test
+    void pom() {
+        Closure testClosure = {}
+        context.checking {
+            one(pomFilterContainerMock).pom(testClosure)
+        }
+        groovyMavenDeployer.pom(testClosure)
+    }
+
+    @Test
+    void pomWithName() {
+        Closure testClosure = {}
+        String testName = 'somename'
+        context.checking {
+            one(pomFilterContainerMock).pom(testName, testClosure)
+        }
+        groovyMavenDeployer.pom(testName, testClosure)
+    }
+
+    @Test
+    void addFilter() {
+        Closure testClosure = {}
+        String testName = 'somename'
+        context.checking {
+            one(pomFilterContainerMock).addFilter(testName, testClosure)
+        }
+        groovyMavenDeployer.addFilter(testName, testClosure)
+    }
+}
+
+
+
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainerTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainerTest.java
new file mode 100644
index 0000000..9b201d5
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultConf2ScopeMappingContainerTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.*;
+
+ at RunWith(JUnit4.class)
+public class DefaultConf2ScopeMappingContainerTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private DefaultConf2ScopeMappingContainer conf2ScopeMappingContainer;
+    private final Configuration testConf1 = context.mock(Configuration.class);
+    private final Configuration testConf2 = context.mock(Configuration.class);
+    private final Configuration testConf3 = context.mock(Configuration.class);
+    private static final String TEST_SCOPE_1 = "test";
+    private static final String TEST_SCOPE_2 = "test2";
+    private static final int TEST_PRIORITY_1 = 10;
+    private static final int TEST_PRIORITY_2 = 20;
+
+    @Before
+    public void setUp() {
+        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer();
+        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1);
+    }
+
+    @Test
+    public void init() {
+        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer();
+        assertTrue(conf2ScopeMappingContainer.isSkipUnmappedConfs());
+        assertEquals(0, conf2ScopeMappingContainer.getMappings().size());
+        Map<Configuration, Conf2ScopeMapping> testMappings = createTestMappings();
+        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer(testMappings);
+        assertNotSame(testMappings, conf2ScopeMappingContainer.getMappings());
+        assertEquals(testMappings, conf2ScopeMappingContainer.getMappings());
+    }
+
+    @Test
+    public void equalsAndHashCode() {
+        Map<Configuration, Conf2ScopeMapping> testMappings = createTestMappings();
+        conf2ScopeMappingContainer = new DefaultConf2ScopeMappingContainer(testMappings);
+        assertTrue(conf2ScopeMappingContainer.equals(new DefaultConf2ScopeMappingContainer(testMappings)));
+        assertEquals(conf2ScopeMappingContainer.hashCode(), new DefaultConf2ScopeMappingContainer(testMappings).hashCode());
+        conf2ScopeMappingContainer.addMapping(10, context.mock(Configuration.class), "scope");
+        assertFalse(conf2ScopeMappingContainer.equals(new DefaultConf2ScopeMappingContainer(testMappings)));
+    }
+
+    private Map<Configuration, Conf2ScopeMapping> createTestMappings() {
+        Map<Configuration, Conf2ScopeMapping> testMappings = new HashMap<Configuration, Conf2ScopeMapping>() {{
+            Configuration configuration = context.mock(Configuration.class);
+            put(configuration, new Conf2ScopeMapping(10, configuration, "scope"));
+        }};
+        return testMappings;
+    }
+
+    @Test
+    public void addGetMapping() {
+        assertEquals(new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1),
+                conf2ScopeMappingContainer.getMapping(asList(testConf1)));
+    }
+
+    @Test
+    public void singleMappedConfiguration() {
+        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1)), equalTo(
+                new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1)));
+    }
+
+    @Test
+    public void unmappedConfiguration() {
+        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf2)), equalTo(
+                new Conf2ScopeMapping(null, testConf2, null)));
+    }
+
+    @Test
+    public void mappedConfigurationAndUnmappedConfiguration() {
+        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2)), equalTo(
+                new Conf2ScopeMapping(TEST_PRIORITY_1, testConf1, TEST_SCOPE_1)));
+    }
+
+    @Test
+    public void mappingWithDifferentPrioritiesDifferentConfsDifferentScopes() {
+        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_2, testConf2, TEST_SCOPE_2);
+        assertThat(conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2)), equalTo(
+                new Conf2ScopeMapping(TEST_PRIORITY_2, testConf2, TEST_SCOPE_2)));
+    }
+    
+    @Test(expected = InvalidUserDataException.class)
+    public void mappingWithSamePrioritiesDifferentConfsSameScope() {
+        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf2, TEST_SCOPE_1);
+        conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2));
+    }
+
+    @Test(expected = InvalidUserDataException.class)
+    public void mappingWithSamePrioritiesDifferentConfsDifferentScopes() {
+        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf2, TEST_SCOPE_1);
+        conf2ScopeMappingContainer.addMapping(TEST_PRIORITY_1, testConf3, TEST_SCOPE_2);
+        conf2ScopeMappingContainer.getMapping(asList(testConf1, testConf2, testConf3));
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverterTest.java b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverterTest.java
new file mode 100644
index 0000000..3d26987
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultExcludeRuleConverterTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.publication.maven.internal.pom;
+
+import org.apache.maven.model.Exclusion;
+import org.gradle.api.internal.artifacts.DefaultExcludeRule;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class DefaultExcludeRuleConverterTest {
+    private static final String TEST_ORG = "org";
+    private static final String TEST_MODULE = "module";
+
+    private DefaultExcludeRuleConverter excludeRuleConverter;
+
+    @Before
+    public void setUp() {
+        excludeRuleConverter = new DefaultExcludeRuleConverter();   
+    }
+    
+    @Test
+    public void convertableRule() {
+        DefaultExcludeRule excludeRule = new DefaultExcludeRule(TEST_ORG, TEST_MODULE);
+        Exclusion mavenExclude = excludeRuleConverter.convert(excludeRule);
+        assertEquals(TEST_ORG, mavenExclude.getGroupId());
+        assertEquals(TEST_MODULE, mavenExclude.getArtifactId());
+    }
+    
+    @Test
+    public void unconvertableRules() {
+        checkForNull(new DefaultExcludeRule(TEST_ORG, null));
+        checkForNull(new DefaultExcludeRule(null, TEST_MODULE));
+    }
+
+    private void checkForNull(DefaultExcludeRule excludeRule) {
+        assertNull(excludeRuleConverter.convert(excludeRule));
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultGroovyPomFilterContainerTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultGroovyPomFilterContainerTest.groovy
new file mode 100644
index 0000000..2ef7775
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultGroovyPomFilterContainerTest.groovy
@@ -0,0 +1,120 @@
+/*
+ * 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.publication.maven.internal.pom
+
+import org.gradle.api.artifacts.maven.MavenPom
+import org.gradle.api.artifacts.maven.PomFilterContainer
+import org.gradle.api.artifacts.maven.PublishFilter
+import org.gradle.api.publication.maven.internal.BasePomFilterContainer
+import org.gradle.api.publication.maven.internal.BasePomFilterContainerTest
+import org.hamcrest.BaseMatcher
+import org.hamcrest.Description
+import org.hamcrest.Factory
+import org.hamcrest.Matcher
+import org.jmock.integration.junit4.JMock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import java.lang.reflect.Proxy
+
+import static org.junit.Assert.assertSame
+
+ at RunWith(JMock)
+class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
+    static final String TEST_NAME = "somename"
+    PomFilterContainer groovyPomFilterContainer
+
+    @Before
+    public void setUp() {
+        super.setUp()
+    }
+
+    protected BasePomFilterContainer createPomFilterContainer() {
+        return groovyPomFilterContainer = new BasePomFilterContainer(mavenPomFactoryMock);
+    }
+
+    @Test
+    public void addFilterWithClosure() {
+        Closure closureFilter = {}
+        MavenPom pom = groovyPomFilterContainer.addFilter(TEST_NAME, closureFilter)
+        assertSame(pomMock, pom);
+        assertSame(pomMock, groovyPomFilterContainer.pom(TEST_NAME));
+        assertSame(closureFilter, getClosureFromProxy(groovyPomFilterContainer.filter(TEST_NAME)));
+    }
+
+    private Closure getClosureFromProxy(PublishFilter filter) {
+        Proxy.getInvocationHandler(filter).delegate
+    }
+
+    @Test
+    public void filterWithClosure() {
+        Closure closureFilter = {}
+        context.checking {
+            one(pomFilterMock).setFilter(withParam(FilterMatcher.equalsFilter(closureFilter)))
+        }
+        groovyPomFilterContainer.filter(closureFilter)
+    }
+
+    @Test
+    public void defaultPomWithClosure() {
+        String testGroup = "testGroup"
+        context.checking {
+            one(pomFilterMock).getPomTemplate(); will(returnValue(pomMock))
+            one(pomMock).setGroupId(testGroup);
+        }
+        groovyPomFilterContainer.pom {
+            groupId = testGroup
+        }
+    }
+
+    @Test
+    public void pomWithClosure() {
+        groovyPomFilterContainer.addFilter(TEST_NAME, {})
+        String testGroup = "testGroup"
+        context.checking {
+            one(pomMock).setGroupId(testGroup);
+        }
+        groovyPomFilterContainer.pom(TEST_NAME) {
+            groupId = testGroup
+        }
+    }
+}
+
+public class FilterMatcher extends BaseMatcher {
+    Closure filter
+
+    public void describeTo(Description description) {
+        description.appendText("matching filter");
+    }
+
+    public boolean matches(Object actual) {
+        return getClosureFromProxy(actual) == filter;
+    }
+
+    private Closure getClosureFromProxy(PublishFilter filter) {
+        Proxy.getInvocationHandler(filter).delegate
+    }
+
+
+    @Factory
+    public static Matcher<PublishFilter> equalsFilter(Closure filter) {
+        return new FilterMatcher(filter: filter);
+    }
+
+}
+
+
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactoryTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactoryTest.groovy
new file mode 100644
index 0000000..e9085e9
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomFactoryTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom
+
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.internal.file.FileResolver
+import spock.lang.Specification
+
+public class DefaultMavenPomFactoryTest extends Specification {
+    def createMavenPom() {
+        DefaultConf2ScopeMappingContainer scopeMappings = new DefaultConf2ScopeMappingContainer();
+        PomDependenciesConverter pomDependenciesConverter = Mock(PomDependenciesConverter);
+        ConfigurationContainer configurationContainer = Mock(ConfigurationContainer); 
+        FileResolver fileResolver = Mock(FileResolver); 
+        DefaultMavenPomFactory mavenPomFactory = new DefaultMavenPomFactory(configurationContainer, scopeMappings,
+                pomDependenciesConverter, fileResolver);
+        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.create();
+
+        expect:
+        !scopeMappings.is(mavenPom.scopeMappings)
+        scopeMappings == mavenPom.scopeMappings
+        mavenPom.mavenProject != null
+        mavenPom.pomDependenciesConverter.is(pomDependenciesConverter)
+        mavenPom.configurations.is(configurationContainer)
+        mavenPom.fileResolver == fileResolver
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomTest.groovy
new file mode 100644
index 0000000..7d9d33e
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultMavenPomTest.groovy
@@ -0,0 +1,186 @@
+/*
+ * 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.publication.maven.internal.pom
+
+import org.apache.commons.lang.builder.EqualsBuilder
+import org.apache.maven.model.Dependency
+import org.apache.maven.model.Model
+import org.gradle.api.Action
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.api.artifacts.maven.MavenPom.POM_FILE_ENCODING
+
+class DefaultMavenPomTest extends Specification {
+    static final String EXPECTED_PACKAGING = "something";
+    static final String EXPECTED_GROUP_ID = "someGroup";
+    static final String EXPECTED_ARTIFACT_ID = "artifactId";
+    static final String EXPECTED_VERSION = "v\u00E9rsi\u00F8n"; // note the utf-8 chars
+
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    Conf2ScopeMappingContainer conf2ScopeMappingContainer = Mock()
+    PomDependenciesConverter pomDependenciesConverterStub = Mock()
+    ConfigurationContainer configurationContainerStub = Mock()
+    FileResolver fileResolver = Mock()
+    DefaultMavenPom mavenPom = new DefaultMavenPom(configurationContainerStub, conf2ScopeMappingContainer, pomDependenciesConverterStub,
+            fileResolver)
+
+    void setup() {
+        mavenPom.packaging = EXPECTED_PACKAGING
+        mavenPom.groupId = EXPECTED_GROUP_ID
+        mavenPom.artifactId = EXPECTED_ARTIFACT_ID
+        mavenPom.version = EXPECTED_VERSION
+    }
+
+    def init() {
+        expect:
+        mavenPom.scopeMappings.is(conf2ScopeMappingContainer)
+        mavenPom.configurations.is(configurationContainerStub)
+        mavenPom.fileResolver.is(fileResolver)
+        mavenPom.mavenProject.modelVersion == "4.0.0"
+    }
+
+    def setModel() {
+        def newModel = new Model()
+
+        when:
+        mavenPom.model = newModel
+
+        then:
+        mavenPom.model.is(newModel)
+    }
+
+    def effectivePomShouldHaveGeneratedDependencies() {
+        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
+        List manuallyAddedDependencies = [new Dependency()]
+        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurationContainerStub) >> generatedDependencies
+
+        when:
+        mavenPom.dependencies = manuallyAddedDependencies.clone()
+
+        then:
+        EqualsBuilder.reflectionEquals(mavenPom.getEffectivePom().getMavenProject().getDependencies(), manuallyAddedDependencies + generatedDependencies)
+
+        when:
+        mavenPom.dependencies = []
+
+        then:
+        mavenPom.getEffectivePom().getMavenProject().getDependencies() == generatedDependencies
+    }
+
+    def configureActionsShouldBeAppliedAgainstEffectivePom() {
+        mavenPom.configurations = null
+        when:
+        mavenPom.whenConfigured(new Action() {
+            void execute(def mavenPom) {
+                mavenPom.mavenProject.inceptionYear = '1999'
+            }
+        })
+
+        then:
+        mavenPom.effectivePom.mavenProject.inceptionYear == '1999'
+        mavenPom.mavenProject.inceptionYear == null
+    }
+
+
+    def writeShouldUseEffectivePom() {
+        List generatedDependencies = [new Dependency(groupId: 'someGroup')]
+        pomDependenciesConverterStub.convert(conf2ScopeMappingContainer, configurationContainerStub) >> generatedDependencies
+
+        when:
+        StringWriter pomWriter = new StringWriter()
+        mavenPom.writeTo pomWriter
+
+        then:
+        pomWriter.toString().contains('someGroup')
+    }
+
+    def effectivePomWithNullConfigurationsShouldWork() {
+        when:
+        mavenPom.configurations = null
+
+        then:
+        mavenPom.getEffectivePom().getMavenProject().getDependencies() == []
+    }
+
+    void projectBuilder() {
+        mavenPom.mavenProject.inceptionYear = '2007'
+        mavenPom.mavenProject.description = 'some description'
+        mavenPom.project {
+            inceptionYear '2008'
+            licenses {
+                license {
+                    name 'The Apache Software License, Version 2.0'
+                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    distribution 'repo'
+                }
+            }
+        }
+
+        expect:
+        mavenPom.mavenProject.modelVersion == "4.0.0"
+        mavenPom.version == EXPECTED_VERSION
+        mavenPom.mavenProject.description == 'some description'
+        mavenPom.mavenProject.inceptionYear == '2008'
+        mavenPom.mavenProject.licenses.size() == 1
+        mavenPom.mavenProject.licenses[0].name == 'The Apache Software License, Version 2.0'
+        mavenPom.mavenProject.licenses[0].url == 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+        mavenPom.mavenProject.licenses[0].distribution == 'repo'
+    }
+
+    void writeToShouldApplyXmlActions() {
+        mavenPom.configurations = null
+        StringWriter pomWriter = new StringWriter()
+
+        when:
+        mavenPom.withXml {xmlProvider ->
+            xmlProvider.asString().append('someAppendix')
+        }
+        mavenPom.writeTo(pomWriter);
+
+        then:
+        pomWriter.toString().endsWith("someAppendix")
+    }
+
+    void writeToWritesCorrectPom() {
+        mavenPom.configurations = null
+        TestFile pomFile = tmpDir.file('someNonexistingDir').file('someFile')
+        fileResolver.resolve('file') >> pomFile
+
+        when:
+        mavenPom.writeTo('file');
+
+        then:
+        pomFile.getText(POM_FILE_ENCODING) == TextUtil.toPlatformLineSeparators("""<?xml version="1.0" encoding="${POM_FILE_ENCODING}"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>${EXPECTED_GROUP_ID}</groupId>
+  <artifactId>${EXPECTED_ARTIFACT_ID}</artifactId>
+  <version>${EXPECTED_VERSION}</version>
+  <packaging>${EXPECTED_PACKAGING}</packaging>
+</project>
+""")
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverterTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverterTest.groovy
new file mode 100644
index 0000000..9e857ee
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/DefaultPomDependenciesConverterTest.groovy
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.publication.maven.internal.pom
+import org.apache.maven.model.Dependency
+import org.apache.maven.model.Exclusion
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.ExcludeRule
+import org.gradle.api.artifacts.ModuleDependency
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping
+import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
+import org.gradle.api.internal.artifacts.DefaultExcludeRule
+import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyArtifact
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.api.publication.maven.internal.VersionRangeMapper
+import spock.lang.Issue
+import spock.lang.Specification
+
+import static org.gradle.util.WrapUtil.*
+
+public class DefaultPomDependenciesConverterTest extends Specification {
+    
+    private DefaultPomDependenciesConverter dependenciesConverter;
+    private conf2ScopeMappingContainerMock = Mock(Conf2ScopeMappingContainer)
+    private excludeRuleConverterMock = Mock(ExcludeRuleConverter)
+    private versionRangeMapper = Mock(VersionRangeMapper)
+
+    private ModuleDependency dependency1
+    private ModuleDependency dependency2
+    private ModuleDependency dependency31
+    private ModuleDependency dependency32
+    private Configuration compileConfStub
+    private Configuration testCompileConfStub
+
+    public void setup() {
+        setUpCommonDependenciesAndConfigurations();
+        dependenciesConverter = new DefaultPomDependenciesConverter(excludeRuleConverterMock, versionRangeMapper);
+    }
+
+    private void setUpCommonDependenciesAndConfigurations() {
+        dependency1 = createDependency("org1", "name1", "rev1");
+        dependency2 = createDependency("org2", "name2", "rev2");
+        dependency2.addArtifact(new DefaultDependencyArtifact("name2", null, null, null, null));
+        dependency31 = createDependency("org3", "name3", "rev3");
+        dependency32 = createDependency("org3", "name3", "rev3");
+        dependency32.addArtifact(new DefaultDependencyArtifact("artifactName32", "type32", "ext", "classifier32", null));
+        compileConfStub = createNamedConfigurationStubWithDependencies("compile", dependency1, dependency31);
+        testCompileConfStub = createNamedConfigurationStubWithDependencies("testCompile", dependency2, dependency32);
+
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(testCompileConfStub, compileConfStub)) >> createMapping(testCompileConfStub, "test")
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(compileConfStub, testCompileConfStub)) >> createMapping(testCompileConfStub, "test")
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(testCompileConfStub)) >> createMapping(testCompileConfStub, "test")
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(compileConfStub)) >> createMapping(compileConfStub, "compile")
+        _ * versionRangeMapper.map("rev1") >> "rev1"
+        _ * versionRangeMapper.map("rev2") >> "rev2"
+        _ * versionRangeMapper.map("rev3") >> "rev3"
+    }
+
+    private static createMapping(Configuration configuration, String scope) {
+        return new Conf2ScopeMapping(10, configuration, scope);
+    }
+
+    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final ModuleDependency... dependencies) {
+        return createNamedConfigurationStubWithDependencies(confName, new HashSet<ExcludeRule>(), dependencies);
+    }
+    
+    private Configuration createNamedConfigurationStubWithDependencies(final String confName, final Set<ExcludeRule> excludeRules, final ModuleDependency... dependencies) {
+        final DependencySet dependencySet = Stub(DependencySet) {
+            withType(ModuleDependency) >> toDomainObjectSet(ModuleDependency, dependencies)
+        }
+        final Configuration configurationStub = Stub(Configuration) {
+            getName() >> confName
+            getDependencies() >> dependencySet
+            getExcludeRules() >> excludeRules
+        }
+        return configurationStub;
+    }
+
+    private static createDependency(final String group, final String name, final String version) {
+        return new DefaultExternalModuleDependency(group, name, version);
+    }
+
+    def init() {
+        expect:
+        excludeRuleConverterMock == dependenciesConverter.getExcludeRuleConverter()
+    }
+
+    def convert() {
+        Set<Configuration> configurations = toSet(compileConfStub, testCompileConfStub);
+
+        when:
+        def actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, configurations)
+
+        then:
+        actualMavenDependencies.size() == 4
+        checkCommonMavenDependencies(actualMavenDependencies)
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3233")
+    def convertsDependencyWithNullVersion() {
+        def dependency4 = createDependency("org4", "name4", null)
+
+        when:
+        def stubbedConfiguration = createNamedConfigurationStubWithDependencies("conf", dependency4)
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(stubbedConfiguration)) >> createMapping(stubbedConfiguration, null)
+
+        then:
+        def  actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(compileConfStub, testCompileConfStub, stubbedConfiguration));
+
+        and:
+        actualMavenDependencies.find { it.artifactId == "name4" }.version == null
+    }
+
+    def convertWithUnMappedConfAndSkipTrue() {
+        def dependency4 = createDependency("org4", "name4", "rev4")
+
+        when:
+        def unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf", dependency4)
+        _ * conf2ScopeMappingContainerMock.skipUnmappedConfs >> true
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(unmappedConfigurationStub)) >> createMapping(unmappedConfigurationStub, null)
+
+        then:
+        def  actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(compileConfStub, testCompileConfStub, unmappedConfigurationStub));
+
+        and:
+        actualMavenDependencies.size() == 4
+        checkCommonMavenDependencies(actualMavenDependencies);
+    }
+
+    def convertWithUnMappedConfAndSkipFalse() {
+        final ModuleDependency dependency4 = createDependency("org4", "name4", "rev4")
+
+        when:
+        def unmappedConfigurationStub = createNamedConfigurationStubWithDependencies("unmappedConf", dependency4)
+        _ * conf2ScopeMappingContainerMock.skipUnmappedConfs >> false
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(unmappedConfigurationStub)) >> createMapping(unmappedConfigurationStub, null)
+        1 * versionRangeMapper.map("rev4") >> "rev4"
+
+        then:
+        def  actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(compileConfStub, testCompileConfStub, unmappedConfigurationStub));
+
+        and:
+        actualMavenDependencies.size() == 5
+        checkCommonMavenDependencies(actualMavenDependencies);
+        hasDependency(actualMavenDependencies, "org4", "name4", "rev4", null, null, null, false)
+    }
+
+    def convertWithConvertibleDependencyExcludes() {
+        when:
+        def someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", dependency1)
+        def mavenExclude = new Exclusion()
+        mavenExclude.groupId = "a"
+        mavenExclude.artifactId = "b"
+
+        dependency1.exclude(toMap(ExcludeRule.GROUP_KEY, "value"))
+
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(someConfigurationStub)) >> createMapping(compileConfStub, "compile")
+        _ * excludeRuleConverterMock.convert(dependency1.excludeRules.iterator().next()) >> mavenExclude
+
+        then:
+        def actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub))
+
+        and:
+        actualMavenDependencies.size() == 1
+        hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false)
+
+        def mavenDependency = actualMavenDependencies.get(0);
+        mavenDependency.exclusions.size() == 1
+        mavenDependency.exclusions[0].groupId == "a"
+        mavenDependency.exclusions[0].artifactId == "b"
+    }
+
+    def convertWithConvertibleConfigurationExcludes() {
+        when:
+        def excludeRule = new DefaultExcludeRule("value", null)
+        def someConfigurationStub = createNamedConfigurationStubWithDependencies("someConfiguration", toSet(excludeRule), dependency1)
+        def mavenExclude = new Exclusion()
+        mavenExclude.groupId = "a"
+        mavenExclude.artifactId = "b"
+
+        _ * conf2ScopeMappingContainerMock.getMapping(toSet(someConfigurationStub)) >> createMapping(compileConfStub, "compile")
+        _ * excludeRuleConverterMock.convert(excludeRule) >> mavenExclude
+
+        then:
+        def actualMavenDependencies = dependenciesConverter.convert(conf2ScopeMappingContainerMock, toSet(someConfigurationStub))
+
+        and:
+        actualMavenDependencies.size() == 1
+        hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false)
+
+        def mavenDependency = actualMavenDependencies.get(0);
+        mavenDependency.exclusions.size() == 1
+        mavenDependency.exclusions[0].groupId == "a"
+        mavenDependency.exclusions[0].artifactId == "b"
+    }
+
+    private static void checkCommonMavenDependencies(List<Dependency> actualMavenDependencies) {
+        assert hasDependency(actualMavenDependencies, "org1", "name1", "rev1", null, "compile", null, false)
+        assert hasDependency(actualMavenDependencies, "org2", "name2", "rev2", null, "test", null, false)
+        assert hasDependency(actualMavenDependencies, "org3", "name3", "rev3", null, "test", null, false)
+        assert hasDependency(actualMavenDependencies, "org3", "artifactName32", "rev3", "type32", "test", "classifier32", false)
+    }
+
+    private static hasDependency(List<Dependency> mavenDependencies,
+                                  String group, String artifactId, String version, String type, String scope,
+                                  String classifier, boolean optional) {
+        Dependency expectedDependency = new Dependency();
+        expectedDependency.setGroupId(group);
+        expectedDependency.setArtifactId(artifactId);
+        expectedDependency.setVersion(version);
+        expectedDependency.setType(type);
+        expectedDependency.setScope(scope);
+        expectedDependency.setClassifier(classifier);
+        expectedDependency.setOptional(optional);
+        for (Dependency mavenDependency : mavenDependencies) {
+            if (equals(mavenDependency, expectedDependency)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean equals(Dependency lhs, Dependency rhs) {
+        if (!lhs.getGroupId().equals(lhs.getGroupId())) {
+            return false;
+        }
+        if (!lhs.getArtifactId().equals(lhs.getArtifactId())) {
+            return false;
+        }
+        if (!lhs.getVersion().equals(lhs.getVersion())) {
+            return false;
+        }
+        if (lhs.getType() != null ? !lhs.getType().equals(lhs.getType()) : rhs.getType() != null) {
+            return false;
+        }
+        if (lhs.getScope() != null ? !lhs.getScope().equals(lhs.getScope()) : rhs.getScope() != null) {
+            return false;
+        }
+        if (!lhs.isOptional() == lhs.isOptional()) {
+            return false;
+        }
+        if (lhs.getClassifier() != null ? !lhs.getClassifier().equals(rhs.getClassifier()) : rhs.getClassifier() != null) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHackTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHackTest.groovy
new file mode 100644
index 0000000..8b669aa
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/pom/ProjectDependencyArtifactIdExtractorHackTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * 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.publication.maven.internal.pom
+
+import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
+import org.gradle.api.plugins.BasePlugin
+import org.gradle.api.plugins.MavenPlugin
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+import spock.lang.Issue
+
+ at Issue("GRADLE-443")
+class ProjectDependencyArtifactIdExtractorHackTest extends Specification {
+    def project = TestUtil.createRootProject()
+    def extractor = new ProjectDependencyArtifactIdExtractorHack(new DefaultProjectDependency(project, null, true))
+
+    def "artifact ID defaults to project name if neither archivesBaseName nor mavenDeployer.pom.artifactId is configured"() {
+        expect:
+        extractor.extract() == project.name
+    }
+
+    def "artifact ID honors archivesBaseName"() {
+        project.pluginManager.apply(BasePlugin)
+        project.archivesBaseName = "changed"
+
+        expect:
+        extractor.extract() == "changed"
+    }
+
+    def "artifact ID honors mavenDeployer.pom.artifactId over archivesBaseName"() {
+        project.pluginManager.apply(MavenPlugin)
+
+        project.archivesBaseName = "changed"
+        project.uploadArchives {
+            repositories.mavenDeployer {
+                pom.artifactId = "changed2"
+            }
+        }
+
+        expect:
+        extractor.extract() == "changed2"
+    }
+
+    def "artifact ID defaults to project name if Ivy repository is configured"() {
+        project.pluginManager.apply(BasePlugin)
+        project.archivesBaseName = "changed"
+
+        project.uploadArchives {
+            repositories {
+                ivy {}
+            }
+        }
+
+        expect:
+        extractor.extract() == project.name
+    }
+
+    def "artifact ID defaults to project name if different mavenDeployer.pom.artifactId's are configured"() {
+        project.pluginManager.apply(MavenPlugin)
+
+        project.configurations { other }
+        project.uploadArchives {
+            repositories.mavenDeployer {
+                pom.artifactId = "changed"
+            }
+        }
+        project.uploadOther {
+            repositories.mavenDeployer {
+                pom.artifactId = "changed2"
+            }
+        }
+
+        expect:
+        extractor.extract() == project.name
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagonTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagonTest.groovy
new file mode 100644
index 0000000..3226623
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportDeployWagonTest.groovy
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.wagon
+import org.apache.maven.wagon.ResourceDoesNotExistException
+import org.apache.maven.wagon.TransferFailedException
+import org.apache.maven.wagon.authentication.AuthenticationInfo
+import org.apache.maven.wagon.events.SessionListener
+import org.apache.maven.wagon.events.TransferListener
+import org.apache.maven.wagon.proxy.ProxyInfo
+import org.apache.maven.wagon.proxy.ProxyInfoProvider
+import org.apache.maven.wagon.repository.Repository
+import org.gradle.api.GradleException
+import org.gradle.internal.resource.local.LocalResource
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class RepositoryTransportDeployWagonTest extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider testDirectory = new TestNameTestDirectoryProvider()
+
+    def "wagon connections attempts should set a repository and signal session opening events"() {
+        setup:
+        SessionListener sessionListener = Mock()
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        wagon.addSessionListener(sessionListener)
+
+        when:
+        wagon."$method"(*args)
+
+        then:
+        wagon.getRepository()
+        1 * sessionListener.sessionLoggedIn(_)
+        1 * sessionListener.sessionOpened(_)
+
+        where:
+        method    | args
+        'connect' | [Mock(Repository)]
+        'connect' | [Mock(Repository), Mock(ProxyInfo)]
+        'connect' | [Mock(Repository), Mock(ProxyInfoProvider)]
+        'connect' | [Mock(Repository), Mock(AuthenticationInfo)]
+        'connect' | [Mock(Repository), Mock(AuthenticationInfo), Mock(ProxyInfo)]
+        'connect' | [Mock(Repository), Mock(AuthenticationInfo), Mock(ProxyInfoProvider)]
+    }
+
+    def "waggon disconnect should signal disconnection events"() {
+        setup:
+        SessionListener sessionListener = Mock()
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        wagon.addSessionListener(sessionListener)
+
+        when:
+        wagon.disconnect()
+
+        then:
+        1 * sessionListener.sessionDisconnecting(_)
+        1 * sessionListener.sessionLoggedOff(_)
+        1 * sessionListener.sessionDisconnected(_);
+    }
+
+    def "should throw GradleException for a bunch of unused wagon methods"() {
+        setup:
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+
+        when:
+        wagon."$method"(*args)
+
+        then:
+        thrown(GradleException)
+
+        where:
+        method           | args
+        'getFileList'    | ['s']
+        'getIfNewer'     | ['a', Mock(File), 0]
+        'putDirectory'   | [Mock(File), 'a']
+        'resourceExists' | ['a']
+    }
+
+    def "should provide defaults which ignore maven centric stuff"() {
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+
+        expect:
+        !wagon.supportsDirectoryCopy()
+        wagon.getTimeout() == 0
+        !wagon.isInteractive()
+    }
+
+    def "should signal progress events when input stream is read"() {
+        setup:
+        def transferListener = Mock(TransferListener)
+        RepositoryTransportWagonAdapter delegate = Mock()
+        delegate.putRemoteFile(*_) >> { LocalResource resource, String resourceName ->
+            def is = resource.open()
+            // 3 reads >> 3 events
+            is.read()
+            is.read(new byte[3])
+            is.read(new byte[3], 1, 2)
+            is.close()
+        }
+
+        def file = testDirectory.createFile('target.jar')
+        file << "here is some file content"
+
+        def resourceName = '/some/resource.jar'
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+
+        when:
+        wagon.put(file, resourceName)
+
+        //Order matters
+        then:
+        1 * transferListener.transferInitiated(_)
+        then:
+        1 * transferListener.transferStarted(_)
+        then:
+        3 * transferListener.transferProgress(*_)
+        then:
+        1 * transferListener.transferCompleted(_)
+        then:
+        0 * transferListener._
+    }
+
+    def "should signal correct events on a failed upload"() {
+        setup:
+        SessionListener sessionListener = Mock()
+        TransferListener transferListener = Mock()
+        def failure = new IOException("failed")
+        RepositoryTransportWagonAdapter delegate = Mock()
+        delegate.putRemoteFile(*_) >> { LocalResource resource, String resourceName ->
+            resource.open()
+            throw failure
+        }
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        def file = testDirectory.createFile('target.jar')
+        def resourceName = '/some/resource.jar'
+
+        wagon.addSessionListener(sessionListener)
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+
+        when:
+        wagon.put(file, resourceName)
+
+        then:
+        1 * transferListener.transferInitiated(_)
+        1 * transferListener.transferStarted(_)
+        1 * transferListener.transferError(_)
+
+        then:
+        0 * transferListener._
+
+        then:
+        def ex = thrown(TransferFailedException)
+        ex.cause == failure
+    }
+
+    def "should signal the correct events on a successful retrieval"() {
+        setup:
+        TransferListener transferListener = Mock()
+        RepositoryTransportWagonAdapter delegate = Mock()
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        def file = testDirectory.createFile('target.jar')
+        file << "someText"
+        def resourceName = '/some/resource.jar'
+
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+
+        when:
+        wagon.get(resourceName, file)
+
+        then:
+        1 * transferListener.transferInitiated(_)
+        then:
+        1 * transferListener.transferStarted(_)
+        then:
+        1 * delegate.getRemoteFile(file, resourceName) >> true
+        then:
+        1 * transferListener.transferCompleted(*_)
+        then:
+        0 * transferListener._
+    }
+
+    def "should create the destination file if the deployer supplies a file which does not exist"() {
+        setup:
+        TransferListener transferListener = Mock()
+        RepositoryTransportWagonAdapter delegate = Mock()
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        def resourceName = '/some/resource.jar'
+
+        TestFile file = testDirectory.createFile('target.jar')
+        file.delete()
+
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+
+        when:
+        assert !file.exists()
+        wagon.get(resourceName, file)
+
+        then:
+        1 * transferListener.transferInitiated(_)
+        then:
+        1 * transferListener.transferStarted(_)
+        then:
+        1 * delegate.getRemoteFile(file, resourceName) >> true
+        then:
+        1 * transferListener.transferCompleted(*_)
+        then:
+        0 * transferListener._
+
+        and:
+        file.exists()
+    }
+
+    def "should throw ResourceDoesNotExistException and signal events when the remote resource does not exist"() {
+        setup:
+        TransferListener transferListener = Mock()
+        RepositoryTransportWagonAdapter delegate = Mock()
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        def file = testDirectory.createFile('target.jar')
+        def resourceName = '/some/resource.jar'
+
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+
+        when:
+        wagon.get(resourceName, file)
+
+        then:
+        1 * transferListener.transferInitiated(_)
+        then:
+        1 * transferListener.transferStarted(_)
+
+        then:
+        delegate.getRemoteFile(file, resourceName) >> false
+
+        then: "Normally indicates to the deployer that it's a first time snapshot publish"
+        thrown(ResourceDoesNotExistException)
+    }
+
+    def "should throw TransferFailedException and signal events when failed to download a remote resource"() {
+        setup:
+        TransferListener transferListener = Mock()
+        RepositoryTransportWagonAdapter delegate = Mock()
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        def file = testDirectory.createFile('target.jar')
+        def resourceName = '/some/resource.jar'
+
+        wagon.addTransferListener(transferListener)
+        wagon.contextualize(delegate)
+        delegate.getRemoteFile(*_) >> { throw new IOException("Explode!") }
+
+        when:
+        wagon.get(resourceName, file)
+
+        then:
+        1 * transferListener.transferInitiated(_)
+        then:
+        1 * transferListener.transferStarted(_)
+
+        then:
+        thrown(TransferFailedException)
+
+        then:
+        1 * transferListener.transferError(_)
+    }
+
+    def "should add and remove wagon listeners"() {
+        TransferListener transferListener = Mock()
+        SessionListener sessionListener = Mock()
+
+        RepositoryTransportDeployWagon wagon = new RepositoryTransportDeployWagon()
+        wagon.addTransferListener(transferListener)
+        wagon.addSessionListener(sessionListener)
+
+        when:
+        wagon.removeSessionListener(sessionListener)
+        wagon.removeTransferListener(transferListener)
+
+        then:
+        !wagon.hasSessionListener(sessionListener)
+        !wagon.hasTransferListener(transferListener)
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapterTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapterTest.groovy
new file mode 100644
index 0000000..1428896
--- /dev/null
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publication/maven/internal/wagon/RepositoryTransportWagonAdapterTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publication.maven.internal.wagon
+
+import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport
+import org.gradle.internal.resource.ExternalResource
+import org.gradle.internal.resource.transport.ExternalResourceRepository
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class RepositoryTransportWagonAdapterTest extends Specification {
+
+    public static final URI S3_URI = new URI("s3://somewhere/maven")
+
+    @Unroll
+    def "should determine the correct remote resource uri"() {
+        given:
+        RepositoryTransport repositoryTransport = Mock()
+        ExternalResourceRepository externalResourceRepo = Mock()
+        repositoryTransport.getRepository() >> externalResourceRepo
+
+        RepositoryTransportWagonAdapter delegate = new RepositoryTransportWagonAdapter(repositoryTransport, repoUrl)
+
+        when:
+        delegate.getRemoteFile(null, resourceName)
+
+        then:
+        1 * repositoryTransport.getRepository().getResource({ it.toString() == expected })
+        where:
+        repoUrl                          | resourceName    | expected
+        S3_URI                           | 'a/b/some.jar'  | 's3://somewhere/maven/a/b/some.jar'
+        new URI("s3://somewhere/maven/") | 'a/b/some.jar'  | 's3://somewhere/maven/a/b/some.jar'
+        S3_URI                           | '/a/b/some.jar' | 's3://somewhere/maven/a/b/some.jar'
+    }
+
+    def "returns true when remote resource was retrieved and written"() {
+        given:
+        RepositoryTransport repositoryTransport = Mock()
+        ExternalResourceRepository externalResourceRepo = Mock()
+        repositoryTransport.getRepository() >> externalResourceRepo
+        externalResourceRepo.getResource(_) >> Mock(ExternalResource)
+
+        RepositoryTransportWagonAdapter delegate = new RepositoryTransportWagonAdapter(repositoryTransport, S3_URI)
+
+        expect:
+        delegate.getRemoteFile(null, 'a/b/some.jar')
+    }
+
+    def "returns false when the remote resource does not exist"() {
+        given:
+        RepositoryTransport repositoryTransport = Mock()
+        ExternalResourceRepository externalResourceRepo = Mock()
+        repositoryTransport.getRepository() >> externalResourceRepo
+        externalResourceRepo.getResource(_) >> null
+
+        RepositoryTransportWagonAdapter delegate = new RepositoryTransportWagonAdapter(repositoryTransport, S3_URI)
+
+        expect:
+        !delegate.getRemoteFile(null, 'a/b/some.jar')
+    }
+
+    def "should put a file to the correct uri"() {
+        given:
+        RepositoryTransport repositoryTransport = Mock()
+        ExternalResourceRepository externalResourceRepo = Mock()
+        repositoryTransport.getRepository() >> externalResourceRepo
+        externalResourceRepo.withProgressLogging() >> externalResourceRepo
+
+        RepositoryTransportWagonAdapter delegate = new RepositoryTransportWagonAdapter(repositoryTransport, S3_URI)
+
+        when:
+        delegate.putRemoteFile(null, 'something.jar')
+
+        then:
+        1 * externalResourceRepo.put(_, { it.toString() == 's3://somewhere/maven/something.jar'})
+    }
+}
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactoryTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactoryTest.groovy
index 73f5094..b2a4005 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactoryTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/artifact/MavenArtifactNotationParserFactoryTest.groovy
@@ -29,7 +29,7 @@ import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 public class MavenArtifactNotationParserFactoryTest extends Specification {
-    Instantiator instantiator = new DirectInstantiator()
+    Instantiator instantiator = DirectInstantiator.INSTANCE
     def taskDependency = Mock(TaskDependency)
     def fileNotationParser = Mock(NotationParser)
     def publishArtifact = Stub(PublishArtifact) {
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublicationTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublicationTest.groovy
index f9a9987..b9da433 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublicationTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublicationTest.groovy
@@ -18,10 +18,7 @@ package org.gradle.api.publish.maven.internal.publication
 import org.gradle.api.Action
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Task
-import org.gradle.api.artifacts.DependencyArtifact
-import org.gradle.api.artifacts.ModuleDependency
-import org.gradle.api.artifacts.ProjectDependency
-import org.gradle.api.artifacts.PublishArtifact
+import org.gradle.api.artifacts.*
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.component.SoftwareComponentInternal
 import org.gradle.api.internal.component.Usage
@@ -36,9 +33,11 @@ import org.gradle.internal.typeconversion.NotationParser
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
 import spock.lang.Shared
 import spock.lang.Specification
 
+ at UsesNativeServices
 public class DefaultMavenPublicationTest extends Specification {
     @Shared TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
     def module = Mock(MavenProjectIdentity)
@@ -116,6 +115,42 @@ public class DefaultMavenPublicationTest extends Specification {
         publication.pom.packaging == "ext"
     }
 
+    def "packaging determines main artifact"() {
+        when:
+        def mavenArtifact = Mock(MavenArtifact)
+        notationParser.parseNotation("artifact") >> mavenArtifact
+        mavenArtifact.extension >> "ext"
+        def attachedMavenArtifact = Mock(MavenArtifact)
+        notationParser.parseNotation("attached") >> attachedMavenArtifact
+        attachedMavenArtifact.extension >> "jar"
+
+        and:
+        def publication = createPublication()
+        publication.artifact("artifact")
+        publication.artifact("attached")
+        publication.pom.packaging = "ext"
+
+        then:
+        publication.asNormalisedPublication().mainArtifact.extension == "ext"
+        publication.pom.packaging == "ext"
+    }
+
+    def 'if there is only one artifact it is the main artifact even if packaging is different'() {
+        when:
+        def mavenArtifact = Mock(MavenArtifact)
+        notationParser.parseNotation("artifact") >> mavenArtifact
+        mavenArtifact.extension >> "ext"
+
+        and:
+        def publication = createPublication()
+        publication.artifact("artifact")
+        publication.pom.packaging = "otherext"
+
+        then:
+        publication.asNormalisedPublication().mainArtifact.extension == "ext"
+        publication.pom.packaging == "otherext"
+    }
+
     def "empty publishableFiles and artifacts when no component is added"() {
         when:
         def publication = createPublication()
@@ -160,12 +195,14 @@ public class DefaultMavenPublicationTest extends Specification {
         def publication = createPublication()
         def moduleDependency = Mock(ModuleDependency)
         def artifact = Mock(DependencyArtifact)
+        def excludeRule = Mock(ExcludeRule)
 
         when:
         moduleDependency.group >> "group"
         moduleDependency.name >> "name"
         moduleDependency.version >> "version"
         moduleDependency.artifacts >> [artifact]
+        moduleDependency.excludeRules >> [excludeRule]
 
         and:
         publication.from(componentWithDependency(moduleDependency))
@@ -177,6 +214,7 @@ public class DefaultMavenPublicationTest extends Specification {
             artifactId == "name"
             version == "version"
             artifacts == [artifact]
+            excludeRules == [excludeRule]
         }
     }
 
@@ -283,7 +321,7 @@ public class DefaultMavenPublicationTest extends Specification {
     }
 
     def createPublication() {
-        def publication = new DefaultMavenPublication("pub-name", module, notationParser, new DirectInstantiator(), projectDependencyResolver)
+        def publication = new DefaultMavenPublication("pub-name", module, notationParser, DirectInstantiator.INSTANCE, projectDependencyResolver)
         publication.setPomFile(new SimpleFileCollection(pomFile))
         return publication;
     }
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publisher/ValidatingMavenPublisherTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publisher/ValidatingMavenPublisherTest.groovy
index 092c4bf..419dc0d 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publisher/ValidatingMavenPublisherTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/publisher/ValidatingMavenPublisherTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.api.publish.maven.internal.publisher
 import org.gradle.api.Action
 import org.gradle.api.XmlProvider
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository
+import org.gradle.api.publication.maven.internal.VersionRangeMapper
 import org.gradle.api.publish.maven.InvalidMavenPublicationException
 import org.gradle.api.publish.maven.MavenArtifact
 import org.gradle.api.publish.maven.internal.tasks.MavenPomFileGenerator
@@ -34,12 +35,12 @@ public class ValidatingMavenPublisherTest extends Specification {
     @Shared TestNameTestDirectoryProvider testDir = new TestNameTestDirectoryProvider()
     def delegate = Mock(MavenPublisher)
     def publisher = new ValidatingMavenPublisher(delegate)
+    def repository = Mock(MavenArtifactRepository)
 
     def "delegates when publication is valid"() {
         when:
         def projectIdentity = makeProjectIdentity("the-group", "the-artifact", "the-version")
-        def publication = new MavenNormalizedPublication("pub-name", createPomFile(projectIdentity), projectIdentity, emptySet())
-        def repository = Mock(MavenArtifactRepository)
+        def publication = new MavenNormalizedPublication("pub-name", createPomFile(projectIdentity), projectIdentity, emptySet(), null)
 
         and:
         publisher.publish(publication, repository)
@@ -51,9 +52,7 @@ public class ValidatingMavenPublisherTest extends Specification {
     def "validates project coordinates"() {
         given:
         def projectIdentity = makeProjectIdentity(groupId, artifactId, version)
-        def publication = new MavenNormalizedPublication("pub-name", createPomFile(projectIdentity), projectIdentity, emptySet())
-
-        def repository = Mock(MavenArtifactRepository)
+        def publication = new MavenNormalizedPublication("pub-name", createPomFile(projectIdentity), projectIdentity, emptySet(), null)
 
         when:
         publisher.publish(publication, repository)
@@ -80,9 +79,7 @@ public class ValidatingMavenPublisherTest extends Specification {
         given:
         def projectIdentity = makeProjectIdentity("group", "artifact", "version")
         def pomFile = createPomFile(makeProjectIdentity(groupId, artifactId, version))
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, emptySet())
-
-        def repository = Mock(MavenArtifactRepository)
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, emptySet(), null)
 
         when:
         publisher.publish(publication, repository)
@@ -105,10 +102,10 @@ public class ValidatingMavenPublisherTest extends Specification {
             getExtension() >> extension
             getClassifier() >> classifier
         }
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([mavenArtifact]))
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([mavenArtifact]), null)
 
         when:
-        publisher.publish(publication, Mock(MavenArtifactRepository))
+        publisher.publish(publication, repository)
 
         then:
         def t = thrown InvalidMavenPublicationException
@@ -129,10 +126,10 @@ public class ValidatingMavenPublisherTest extends Specification {
         def projectIdentity = makeProjectIdentity("group", "artifact", "version")
         def pomFile = createPomFile(projectIdentity)
         def mavenArtifact = Mock(MavenArtifact)
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([mavenArtifact]))
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([mavenArtifact]), null)
 
         when:
-        publisher.publish(publication, Mock(MavenArtifactRepository))
+        publisher.publish(publication, repository)
 
         then:
         mavenArtifact.extension >> "ext"
@@ -162,10 +159,10 @@ public class ValidatingMavenPublisherTest extends Specification {
         }
         def projectIdentity = makeProjectIdentity("group", "artifact", "version")
         def pomFile = createPomFile(projectIdentity)
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([artifact1, artifact2]))
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([artifact1, artifact2]), null)
 
         when:
-        publisher.publish(publication, Mock(MavenArtifactRepository))
+        publisher.publish(publication, repository)
 
         then:
         def t = thrown InvalidMavenPublicationException
@@ -181,10 +178,10 @@ public class ValidatingMavenPublisherTest extends Specification {
         }
         def projectIdentity = makeProjectIdentity("group", "artifact", "version")
         def pomFile = createPomFile(projectIdentity)
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([artifact1]))
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, toSet([artifact1]), null)
 
         when:
-        publisher.publish(publication, Mock(MavenArtifactRepository))
+        publisher.publish(publication, repository)
 
         then:
         def t = thrown InvalidMavenPublicationException
@@ -199,9 +196,7 @@ public class ValidatingMavenPublisherTest extends Specification {
                 xml.asNode().appendNode("invalid", "This is not a valid pomFile element")
             }
         })
-        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, emptySet())
-
-        def repository = Mock(MavenArtifactRepository)
+        def publication = new MavenNormalizedPublication("pub-name", pomFile, projectIdentity, emptySet(), null)
 
         when:
         publisher.publish(publication, repository)
@@ -223,7 +218,8 @@ public class ValidatingMavenPublisherTest extends Specification {
 
     private def createPomFile(MavenProjectIdentity projectIdentity, Action<XmlProvider> withXmlAction = null) {
         def pomFile = testDir.file("pom")
-        MavenPomFileGenerator pomFileGenerator = new MavenPomFileGenerator(projectIdentity);
+        def mapper = Stub(VersionRangeMapper)
+        MavenPomFileGenerator pomFileGenerator = new MavenPomFileGenerator(projectIdentity, mapper)
         if (withXmlAction != null) {
             pomFileGenerator.withXml(withXmlAction)
         }
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGeneratorTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGeneratorTest.groovy
index baf67ab..2d446d7 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGeneratorTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/internal/tasks/MavenPomFileGeneratorTest.groovy
@@ -15,22 +15,27 @@
  */
 
 package org.gradle.api.publish.maven.internal.tasks
+
 import org.gradle.api.Action
 import org.gradle.api.XmlProvider
 import org.gradle.api.artifacts.DependencyArtifact
+import org.gradle.api.artifacts.ExcludeRule
+import org.gradle.api.publication.maven.internal.VersionRangeMapper
 import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal
 import org.gradle.api.publish.maven.internal.publication.DefaultMavenProjectIdentity
-import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.CollectionUtils
 import org.gradle.util.TextUtil
+import org.junit.Rule
 import spock.lang.Specification
 
 class MavenPomFileGeneratorTest extends Specification {
-    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
     def projectIdentity = new DefaultMavenProjectIdentity("group-id", "artifact-id", "1.0")
-    MavenPomFileGenerator generator = new MavenPomFileGenerator(projectIdentity)
+    def rangeMapper = Stub(VersionRangeMapper)
+    def generator = new MavenPomFileGenerator(projectIdentity, rangeMapper)
 
     def "writes correct prologue and schema declarations"() {
         expect:
@@ -66,7 +71,7 @@ class MavenPomFileGeneratorTest extends Specification {
         def groupId = 'group-ぴ₦ガき∆ç√∫'
         def artifactId = 'artifact-<tag attrib="value"/>-markup'
         def version = 'version-&"'
-        generator = new MavenPomFileGenerator(new DefaultMavenProjectIdentity(groupId, artifactId, version))
+        generator = new MavenPomFileGenerator(new DefaultMavenProjectIdentity(groupId, artifactId, version), Stub(VersionRangeMapper))
 
         then:
         with (pom) {
@@ -86,6 +91,8 @@ class MavenPomFileGeneratorTest extends Specification {
         dependency.groupId >> "dep-group"
         dependency.artifactId >> "dep-name"
         dependency.version >> "dep-version"
+        dependency.excludeRules >> []
+        rangeMapper.map("dep-version") >> "maven-dep-version"
 
         and:
         with (pom) {
@@ -93,12 +100,71 @@ class MavenPomFileGeneratorTest extends Specification {
             with (dependencies[0].dependency[0]) {
                 groupId == "dep-group"
                 artifactId == "dep-name"
-                version == "dep-version"
+                version == "maven-dep-version"
                 scope == "runtime"
             }
         }
     }
 
+    def "writes regular dependency without exclusions"() {
+        def dependency = Mock(MavenDependencyInternal)
+        when:
+        generator.addRuntimeDependency(dependency)
+
+        then:
+        dependency.artifacts >> new HashSet<DependencyArtifact>()
+        dependency.groupId >> "dep-group"
+        dependency.artifactId >> "dep-name"
+        dependency.version >> "dep-version"
+        dependency.excludeRules >> []
+
+        and:
+        with (pom) {
+            dependencies.dependency.exclusions.size() == 0
+        }
+    }
+
+    def "writes dependency with excludes"() {
+        given:
+        def dependency = Mock(MavenDependencyInternal)
+        def exclude1 = Mock(ExcludeRule)
+        def exclude2 = Mock(ExcludeRule)
+        def exclude3 = Mock(ExcludeRule)
+
+        when:
+        generator.addRuntimeDependency(dependency)
+
+        then:
+        dependency.artifacts >> new HashSet<DependencyArtifact>()
+        dependency.groupId >> "dep-group"
+        dependency.artifactId >> "dep-name"
+        dependency.version >> "dep-version"
+        dependency.excludeRules >> CollectionUtils.toSet([exclude1, exclude2, exclude3])
+        exclude1.group >> "excl-1-group"
+        exclude1.module >> "excl-1-module"
+        exclude2.group >> "excl-2-group"
+        exclude2.module >> null
+        exclude3.group >> null
+        exclude3.module >> "excl-3-module"
+
+        and:
+        with (pom) {
+            dependencies.dependency.exclusions.exclusion.size() == 3
+            with (dependencies[0].dependency[0].exclusions[0].exclusion[0]) {
+                groupId == "excl-1-group"
+                artifactId == "excl-1-module"
+            }
+            with (dependencies[0].dependency[0].exclusions[0].exclusion[1]) {
+                groupId == "excl-2-group"
+                artifactId == "*"
+            }
+            with (dependencies[0].dependency[0].exclusions[0].exclusion[2]) {
+                groupId == "*"
+                artifactId == "excl-3-module"
+            }
+        }
+    }
+
     def "writes dependency with artifacts"() {
         def dependency = Mock(MavenDependencyInternal)
         def artifact1 = Mock(DependencyArtifact)
@@ -111,6 +177,8 @@ class MavenPomFileGeneratorTest extends Specification {
         dependency.artifacts >> CollectionUtils.toSet([artifact1, artifact2])
         dependency.groupId >> "dep-group"
         dependency.version >> "dep-version"
+        dependency.excludeRules >> []
+        rangeMapper.map("dep-version") >> "maven-dep-version"
         artifact1.name >> "artifact-1"
         artifact1.type >> "type-1"
         artifact1.classifier >> "classifier-1"
@@ -124,7 +192,7 @@ class MavenPomFileGeneratorTest extends Specification {
             with (dependencies[0].dependency[0]) {
                 groupId == "dep-group"
                 artifactId == "artifact-1"
-                version == "dep-version"
+                version == "maven-dep-version"
                 type == "type-1"
                 classifier == "classifier-1"
                 scope == "runtime"
@@ -132,7 +200,7 @@ class MavenPomFileGeneratorTest extends Specification {
             with (dependencies[0].dependency[1]) {
                 groupId == "dep-group"
                 artifactId == "artifact-2"
-                version == "dep-version"
+                version == "maven-dep-version"
                 type.empty
                 classifier.empty
                 scope == "runtime"
diff --git a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginTest.groovy b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginTest.groovy
index dba3720..f1dee77 100644
--- a/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginTest.groovy
+++ b/subprojects/maven/src/test/groovy/org/gradle/api/publish/maven/plugins/MavenPublishPluginTest.groovy
@@ -15,17 +15,16 @@
  */
 
 package org.gradle.api.publish.maven.plugins
-
-import org.gradle.api.artifacts.ArtifactRepositoryContainer
 import org.gradle.api.artifacts.PublishArtifactSet
 import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.artifacts.BaseRepositoryFactory
 import org.gradle.api.internal.component.SoftwareComponentInternal
 import org.gradle.api.publish.PublishingExtension
 import org.gradle.api.publish.maven.MavenPublication
 import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
 import org.gradle.api.publish.maven.tasks.PublishToMavenLocal
 import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.model.internal.fixture.ModelRegistryHelper
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
@@ -37,7 +36,7 @@ class MavenPublishPluginTest extends Specification {
     def component = Stub(SoftwareComponentInternal)
 
     def setup() {
-        project.plugins.apply(MavenPublishPlugin)
+        project.pluginManager.apply(MavenPublishPlugin)
         publishing = project.extensions.getByType(PublishingExtension)
         project.components.add(component)
 
@@ -83,8 +82,6 @@ class MavenPublishPluginTest extends Specification {
         expect:
         publishLocalTasks.size() == 1
         publishLocalTasks.first().name == "publishTestPublicationToMavenLocal"
-        publishLocalTasks.first().repository.name == ArtifactRepositoryContainer.DEFAULT_MAVEN_LOCAL_REPO_NAME
-        publishLocalTasks.first().repository.url == project.getServices().get(BaseRepositoryFactory).createMavenLocalRepository().url
     }
 
     def "can explicitly add mavenLocal as a publishing repository"() {
@@ -100,7 +97,6 @@ class MavenPublishPluginTest extends Specification {
         publishTasks.first().repository.is(mavenLocal)
 
         publishLocalTasks.size() == 1
-        publishTasks.first().repository.url == publishLocalTasks.first().repository.url
     }
 
     def "tasks are created for compatible publication / repo"() {
@@ -126,7 +122,7 @@ class MavenPublishPluginTest extends Specification {
     }
 
     void closeTaskContainer() {
-        project.modelRegistry.get("tasks", Object)
+        new ModelRegistryHelper(project.modelRegistry).get("tasks", TaskContainer)
     }
 
     List<PublishToMavenRepository> getPublishTasks() {
@@ -163,14 +159,9 @@ class MavenPublishPluginTest extends Specification {
     def "pom dir moves with build dir"() {
         when:
         publishing.publications.create("test", MavenPublication)
-        closeTaskContainer()
-
-        then:
-        project.tasks["generatePomFileForTestPublication"].destination == new File(project.buildDir, "publications/test/pom-default.xml")
-
-        when:
         def newBuildDir = project.file("changed")
         project.buildDir = newBuildDir
+        closeTaskContainer()
 
         then:
         project.tasks["generatePomFileForTestPublication"].destination == new File(newBuildDir, "publications/test/pom-default.xml")
diff --git a/subprojects/maven/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/maven/AbstractMavenPublishIntegTest.groovy b/subprojects/maven/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/maven/AbstractMavenPublishIntegTest.groovy
new file mode 100644
index 0000000..024b6cf
--- /dev/null
+++ b/subprojects/maven/src/testFixtures/groovy/org/gradle/integtests/fixtures/publish/maven/AbstractMavenPublishIntegTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures.publish.maven
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.maven.MavenFileModule
+
+class AbstractMavenPublishIntegTest extends AbstractIntegrationSpec {
+
+    protected def resolveArtifact(MavenFileModule module, def extension, def classifier) {
+        doResolveArtifacts("""
+    dependencies {
+        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}', classifier: '${sq(classifier)}', ext: '${sq(extension)}'
+    }
+""")
+    }
+
+    protected def resolveArtifacts(MavenFileModule module) {
+        doResolveArtifacts("""
+    dependencies {
+        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}'
+    }
+""")
+    }
+
+    protected def resolveArtifacts(MavenFileModule module, Map... additionalArtifacts) {
+        def dependencies = """
+    dependencies {
+        resolve group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}'
+        resolve(group: '${sq(module.groupId)}', name: '${sq(module.artifactId)}', version: '${sq(module.version)}') {
+"""
+        additionalArtifacts.each {
+            // Docs say type defaults to 'jar', but seems it must be set explicitly
+            def type = it.type == null ? 'jar' : it.type
+            dependencies += """
+            artifact {
+                name = '${sq(module.artifactId)}' // TODO:DAZ Get NPE if name isn't set
+                classifier = '${it.classifier}'
+                type = '${type}'
+            }
+"""
+        }
+        dependencies += """
+        }
+    }
+"""
+        doResolveArtifacts(dependencies)
+    }
+
+    protected def doResolveArtifacts(def dependencies) {
+        // Replace the existing buildfile with one for resolving the published module
+        settingsFile.text = "rootProject.name = 'resolve'"
+        buildFile.text = """
+            configurations {
+                resolve
+            }
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+                mavenCentral()
+            }
+            $dependencies
+            task resolveArtifacts(type: Sync) {
+                from configurations.resolve
+                into "artifacts"
+            }
+
+"""
+
+        run "resolveArtifacts"
+        def artifactsList = file("artifacts").exists() ? file("artifacts").list() : []
+        return artifactsList.sort()
+    }
+
+
+    String sq(String input) {
+        return escapeForSingleQuoting(input)
+    }
+
+    String escapeForSingleQuoting(String input) {
+        return input.replace('\\', '\\\\').replace('\'', '\\\'')
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/event/BroadcastDispatch.java b/subprojects/messaging/src/main/java/org/gradle/internal/event/BroadcastDispatch.java
new file mode 100755
index 0000000..68a647a
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/event/BroadcastDispatch.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.event;
+
+import org.gradle.api.Action;
+import org.gradle.internal.UncheckedException;
+import org.gradle.messaging.dispatch.Dispatch;
+import org.gradle.messaging.dispatch.MethodInvocation;
+import org.gradle.messaging.dispatch.ReflectionDispatch;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BroadcastDispatch<T> implements Dispatch<MethodInvocation> {
+    private final Class<T> type;
+    private final Map<Object, Dispatch<MethodInvocation>> handlers = new LinkedHashMap<Object, Dispatch<MethodInvocation>>();
+
+    public BroadcastDispatch(Class<T> type) {
+        this.type = type;
+    }
+
+    public Class<T> getType() {
+        return type;
+    }
+
+    public boolean isEmpty() {
+        return handlers.isEmpty();
+    }
+
+    public void add(Dispatch<MethodInvocation> dispatch) {
+        handlers.put(dispatch, dispatch);
+    }
+
+    public void add(T listener) {
+        handlers.put(listener, new ReflectionDispatch(listener));
+    }
+
+    public void add(String methodName, Action<?> action) {
+        assertIsMethod(methodName);
+        handlers.put(action, new ActionInvocationHandler(methodName, action));
+    }
+
+    private void assertIsMethod(String methodName) {
+        for (Method method : type.getMethods()) {
+            if (method.getName().equals(methodName)) {
+                return;
+            }
+        }
+        throw new IllegalArgumentException(String.format("Method %s() not found for listener type %s.", methodName,
+                type.getSimpleName()));
+    }
+
+    public void remove(Object listener) {
+        handlers.remove(listener);
+    }
+
+    public void removeAll() {
+        handlers.clear();
+    }
+
+    private String getErrorMessage() {
+        String typeDescription = type.getSimpleName().replaceAll("(\\p{Upper})", " $1").trim().toLowerCase();
+        return String.format("Failed to notify %s.", typeDescription);
+    }
+
+    public void dispatch(MethodInvocation invocation) {
+        List<Throwable> failures = new ArrayList<Throwable>();
+        for (Dispatch<MethodInvocation> handler : new ArrayList<Dispatch<MethodInvocation>>(handlers.values())) {
+            try {
+                handler.dispatch(invocation);
+            } catch (UncheckedException e) {
+                failures.add(e.getCause());
+            } catch (Throwable t) {
+                failures.add(t);
+            }
+        }
+        if (failures.size() == 1 && failures.get(0) instanceof RuntimeException) {
+            throw (RuntimeException) failures.get(0);
+        }
+        if (!failures.isEmpty()) {
+            throw new ListenerNotificationException(getErrorMessage(), failures);
+        }
+    }
+
+    private class ActionInvocationHandler implements Dispatch<MethodInvocation> {
+        private final String methodName;
+        private final Action action;
+
+        public ActionInvocationHandler(String methodName, Action action) {
+            this.methodName = methodName;
+            this.action = action;
+        }
+
+        public void dispatch(MethodInvocation message) {
+            if (message.getMethod().getName().equals(methodName)) {
+                action.execute(message.getArguments()[0]);
+            }
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/event/DefaultListenerManager.java b/subprojects/messaging/src/main/java/org/gradle/internal/event/DefaultListenerManager.java
new file mode 100644
index 0000000..7cfc921
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/event/DefaultListenerManager.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.event;
+
+import org.gradle.messaging.dispatch.Dispatch;
+import org.gradle.messaging.dispatch.MethodInvocation;
+import org.gradle.messaging.dispatch.ReflectionDispatch;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+ at SuppressWarnings({"unchecked"})
+public class DefaultListenerManager implements ListenerManager {
+    private final Set<Object> allListeners = new LinkedHashSet<Object>();
+    private final Set<Object> allLoggers = new LinkedHashSet<Object>();
+    private final Map<Class<?>, ListenerBroadcast> broadcasters = new HashMap<Class<?>, ListenerBroadcast>();
+    private final Map<Class<?>, LoggerDispatch> loggers = new HashMap<Class<?>, LoggerDispatch>();
+    private final Map<Class<?>, BroadcastDispatch> dispatchers = new HashMap<Class<?>, BroadcastDispatch>();
+    private final Object lock = new Object();
+    private final DefaultListenerManager parent;
+
+    public DefaultListenerManager() {
+        this(null);
+    }
+
+    private DefaultListenerManager(DefaultListenerManager parent) {
+        this.parent = parent;
+    }
+
+    public void addListener(Object listener) {
+        synchronized (lock) {
+            if (allListeners.add(listener)) {
+                for (BroadcastDispatch<?> broadcaster : dispatchers.values()) {
+                    maybeAddToDispatcher(broadcaster, listener);
+                }
+            }
+        }
+    }
+
+    public void removeListener(Object listener) {
+        synchronized (lock) {
+            if (allListeners.remove(listener)) {
+                for (BroadcastDispatch<?> broadcaster : dispatchers.values()) {
+                    broadcaster.remove(listener);
+                }
+            }
+        }
+    }
+
+    public void useLogger(Object logger) {
+        synchronized (lock) {
+            if (allLoggers.add(logger)) {
+                for (LoggerDispatch dispatch : loggers.values()) {
+                    dispatch.maybeSetLogger(logger);
+                }
+            }
+        }
+    }
+
+    public <T> T getBroadcaster(Class<T> listenerClass) {
+        return getBroadcasterInternal(listenerClass).getSource();
+    }
+
+    public <T> ListenerBroadcast<T> createAnonymousBroadcaster(Class<T> listenerClass) {
+        ListenerBroadcast<T> broadcast = new ListenerBroadcast(listenerClass);
+        broadcast.add(getBroadcasterInternal(listenerClass).getSource());
+        return broadcast;
+    }
+
+    private <T> ListenerBroadcast<T> getBroadcasterInternal(Class<T> listenerClass) {
+        synchronized (lock) {
+            ListenerBroadcast<T> broadcaster = broadcasters.get(listenerClass);
+            if (broadcaster == null) {
+                broadcaster = new ListenerBroadcast<T>(listenerClass);
+                broadcaster.add(getLogger(listenerClass));
+                broadcaster.add(getDispatcher(listenerClass));
+                if (parent != null) {
+                    broadcaster.add(parent.getDispatcher(listenerClass));
+                }
+                broadcasters.put(listenerClass, broadcaster);
+            }
+
+            return broadcaster;
+        }
+    }
+
+    private <T> BroadcastDispatch<T> getDispatcher(Class<T> listenerClass) {
+        synchronized (lock) {
+            BroadcastDispatch<T> dispatcher = dispatchers.get(listenerClass);
+            if (dispatcher == null) {
+                dispatcher = new BroadcastDispatch<T>(listenerClass);
+                dispatchers.put(listenerClass, dispatcher);
+                for (Object listener : allListeners) {
+                    maybeAddToDispatcher(dispatcher, listener);
+                }
+            }
+            return dispatcher;
+        }
+    }
+
+    private LoggerDispatch getLogger(Class<?> listenerClass) {
+        synchronized (lock) {
+            LoggerDispatch dispatch = loggers.get(listenerClass);
+            if (dispatch == null) {
+                dispatch = new LoggerDispatch(listenerClass, parent == null ? null : parent.getLogger(listenerClass));
+                for (Object logger : allLoggers) {
+                    dispatch.maybeSetLogger(logger);
+                }
+                loggers.put(listenerClass, dispatch);
+            }
+            return dispatch;
+        }
+    }
+
+    private void maybeAddToDispatcher(BroadcastDispatch broadcaster, Object listener) {
+        if (broadcaster.getType().isInstance(listener)) {
+            broadcaster.add(listener);
+        }
+    }
+
+    public ListenerManager createChild() {
+        return new DefaultListenerManager(this);
+    }
+
+    private static class LoggerDispatch implements Dispatch<MethodInvocation> {
+        private final Class<?> type;
+        private Dispatch<MethodInvocation> dispatch;
+
+        private LoggerDispatch(Class<?> type, LoggerDispatch parentDispatch) {
+            this.type = type;
+            this.dispatch = parentDispatch;
+        }
+
+        public void dispatch(MethodInvocation message) {
+            if (dispatch != null) {
+                dispatch.dispatch(message);
+            }
+        }
+
+        public void maybeSetLogger(Object logger) {
+            if (type.isInstance(logger)) {
+                dispatch = new ReflectionDispatch(logger);
+            }
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerBroadcast.java b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerBroadcast.java
new file mode 100644
index 0000000..9bf9c3d
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerBroadcast.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.event;
+
+import org.gradle.api.Action;
+import org.gradle.messaging.dispatch.Dispatch;
+import org.gradle.messaging.dispatch.MethodInvocation;
+import org.gradle.messaging.dispatch.ProxyDispatchAdapter;
+
+/**
+ * <p>Manages a set of listeners of type T. Provides an implementation of T which can be used to broadcast to all
+ * registered listeners.</p>
+ *
+ * <p>Ordering is maintained for events, so that events are delivered to listeners in the order they are generated.
+ * Events are delivered to listeners in the order that listeners are added to this broadcaster.</p>
+ *
+ * @param <T> The listener type.
+ */
+public class ListenerBroadcast<T> implements Dispatch<MethodInvocation> {
+    private final ProxyDispatchAdapter<T> source;
+    private final BroadcastDispatch<T> broadcast;
+    private final Class<T> type;
+
+    public ListenerBroadcast(Class<T> type) {
+        this.type = type;
+        broadcast = new BroadcastDispatch<T>(type);
+        source = new ProxyDispatchAdapter<T>(broadcast, type);
+    }
+
+    /**
+     * Returns the broadcaster. Any method call on this object is broadcast to all listeners.
+     *
+     * @return The broadcaster.
+     */
+    public T getSource() {
+        return source.getSource();
+    }
+
+    /**
+     * Returns the type of listener to which this class broadcasts.
+     *
+     * @return The type of the broadcaster.
+     */
+    public Class<T> getType() {
+        return type;
+    }
+
+    /**
+     * Returns {@code true} if no listeners are registered with this object.
+     *
+     * @return {@code true} if no listeners are registered with this object, {@code false} otherwise
+     */
+    public boolean isEmpty() {
+        return broadcast.isEmpty();
+    }
+
+    /**
+     * Adds a listener.
+     *
+     * @param listener The listener.
+     */
+    public void add(T listener) {
+        broadcast.add(listener);
+    }
+
+    /**
+     * Adds the given listeners.
+     *
+     * @param listeners The listeners
+     */
+    public void addAll(Iterable<? extends T> listeners) {
+        for (T listener : listeners) {
+            broadcast.add(listener);
+        }
+    }
+
+    /**
+     * Adds a {@link org.gradle.messaging.dispatch.Dispatch} to receive events from this broadcast.
+     */
+    public void add(Dispatch<MethodInvocation> dispatch) {
+        broadcast.add(dispatch);
+    }
+
+    /**
+     * Adds an action to be executed when the given method is called.
+     */
+    public void add(String methodName, Action<?> action) {
+        broadcast.add(methodName, action);
+    }
+
+    /**
+     * Removes the given listener.
+     *
+     * @param listener The listener.
+     */
+    public void remove(Object listener) {
+        broadcast.remove(listener);
+    }
+
+    /**
+     * Removes the given listeners.
+     *
+     * @param listeners The listeners
+     */
+    public void removeAll(Iterable<?> listeners) {
+        for (Object listener : listeners) {
+            remove(listener);
+        }
+    }
+
+    /**
+     * Removes all listeners.
+     */
+    public void removeAll() {
+        broadcast.removeAll();
+    }
+
+    /**
+     * Broadcasts the given event to all listeners.
+     *
+     * @param event The event
+     */
+    public void dispatch(MethodInvocation event) {
+        broadcast.dispatch(event);
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerManager.java b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerManager.java
new file mode 100644
index 0000000..14b83b0
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.event;
+
+/**
+ * Unified manager for all listeners for Gradle.  Provides a simple way to find all listeners of a given type in the
+ * system.
+ *
+ * While the methods work with any Object, in general only interfaces should be used as listener types.  Also, due to
+ * implementation details, any listener method with a non-void return type will return a null.
+ */
+public interface ListenerManager {
+    /**
+     * Added a listener.  A single object can implement multiple interfaces, and all interfaces are registered by a
+     * single invocation of this method.  There is no order dependency: if a broadcaster has already been made for type
+     * T, the listener will be registered with it if <code>(listener instanceof T)</code> returns true.
+     *
+     * @param listener the listener to add.
+     */
+    void addListener(Object listener);
+
+    /**
+     * Removes a listener.  A single object can implement multiple interfaces, and all interfaces are unregistered by a
+     * single invocation of this method.  There is no order dependency: if a broadcaster has already been made for type
+     * T, the listener will be unregistered with it if <code>(listener instanceof T)</code> returns true.
+     *
+     * @param listener the listener to remove.
+     */
+    void removeListener(Object listener);
+
+    /**
+     * Returns a broadcaster for the given listenerClass.  If there are no registered listeners for that type, a
+     * broadcaster is returned which does not forward method calls to any listeners.  The returned broadcasters are
+     * live, that is their list of listeners can be updated by calls to {@link #addListener(Object)} and {@link
+     * #removeListener(Object)} after they have been returned.  Broadcasters are also cached, so that repeatedly calling
+     * this method with the same listenerClass returns the same broadcaster object.
+     *
+     * @param listenerClass The type of listener for which to return a broadcaster.
+     * @return The broadcaster that forwards method calls to all listeners of the same type that have been (or will be)
+     *         registered with this manager.
+     */
+    <T> T getBroadcaster(Class<T> listenerClass);
+
+    /**
+     * Returns a broadcaster for the given listenerClass.  The returned broadcaster will delegate to the canonical
+     * broadcaster returned by {@link #getBroadcaster(Class)} for the given listener type.  However, it can also have
+     * listeners assigned/removed directly to/from it.  This allows these "anonymous" broadcasters to specialize what
+     * listeners receive messages.  Each call creates a new broadcaster, so that client code can create as many "facets"
+     * of the listener as they need.  The client code must provide some way for its users to register listeners on the
+     * specialized broadcasters.
+     *
+     * @param listenerClass The type of listener for which to create a broadcaster.
+     * @return A broadcaster that forwards method calls to all listeners assigned to it, or of the same type that have
+     *         been (or will be) registered with this manager.
+     */
+    <T> ListenerBroadcast<T> createAnonymousBroadcaster(Class<T> listenerClass);
+
+    /**
+     * Uses the given object as a logger. Each listener class has exactly one logger associated with it. Any existing
+     * logger for the listener class is discarded.
+     *
+     * @param logger The new logger to use.
+     */
+    void useLogger(Object logger);
+
+    /**
+     * Creates a child {@code ListenerManager}. All events broadcast in the child will be received by the listeners
+     * registered in the parent. However, the reverse is not true: events broadcast in the parent are not received
+     * by the listeners in the children. The child inherits the loggers of its parent, though these can be replaced.
+     *
+     * @return The child
+     */
+    ListenerManager createChild();
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerNotificationException.java b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerNotificationException.java
new file mode 100755
index 0000000..7802e0d
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/event/ListenerNotificationException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.event;
+
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
+import org.gradle.internal.exceptions.Contextual;
+
+/**
+ * A {@code ListenerNotificationException} is thrown when a listener cannot be notified of an event.
+ */
+ at Contextual
+public class ListenerNotificationException extends DefaultMultiCauseException {
+    public ListenerNotificationException(String message, Iterable<? extends Throwable> causes) {
+        super(message, causes);
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractCollectionSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractCollectionSerializer.java
new file mode 100644
index 0000000..71f6de0
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractCollectionSerializer.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.internal.serialize;
+
+import java.util.Collection;
+
+public class AbstractCollectionSerializer<T> {
+    protected final Serializer<T> entrySerializer;
+
+    public AbstractCollectionSerializer(Serializer<T> entrySerializer) {
+        this.entrySerializer = entrySerializer;
+    }
+
+    protected void readValues(Decoder decoder, Collection<T> values) throws Exception {
+        int size = decoder.readInt();
+        for (int i = 0; i < size; i++) {
+            values.add(entrySerializer.read(decoder));
+        }
+    }
+
+    protected void writeValues(Encoder encoder, Collection<T> value) throws Exception {
+        encoder.writeInt(value.size());
+        for (T t : value) {
+            entrySerializer.write(encoder, t);
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractDecoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractDecoder.java
new file mode 100644
index 0000000..ed11770
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractDecoder.java
@@ -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.internal.serialize;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class AbstractDecoder implements Decoder {
+    private DecoderStream stream;
+
+    public InputStream getInputStream() {
+        if (stream == null) {
+            stream = new DecoderStream();
+        }
+        return stream;
+    }
+
+    public void readBytes(byte[] buffer) throws IOException {
+        readBytes(buffer, 0, buffer.length);
+    }
+
+    public byte[] readBinary() throws EOFException, IOException {
+        int size = readSmallInt();
+        byte[] result = new byte[size];
+        readBytes(result);
+        return result;
+    }
+
+    public int readSmallInt() throws EOFException, IOException {
+        return readInt();
+    }
+
+    public long readSmallLong() throws EOFException, IOException {
+        return readLong();
+    }
+
+    public String readNullableString() throws EOFException, IOException {
+        if (readBoolean()) {
+            return readString();
+        } else {
+            return null;
+        }
+    }
+
+    public void skipBytes(long count) throws EOFException, IOException {
+        long remaining = count;
+        while (remaining > 0) {
+            long skipped = maybeSkip(remaining);
+            if (skipped <= 0) {
+                break;
+            }
+            remaining -= skipped;
+        }
+        if (remaining > 0) {
+            throw new EOFException();
+        }
+    }
+
+    protected abstract int maybeReadBytes(byte[] buffer, int offset, int count) throws IOException;
+
+    protected abstract long maybeSkip(long count) throws IOException;
+
+    private class DecoderStream extends InputStream {
+        byte[] buffer = new byte[1];
+
+        @Override
+        public long skip(long n) throws IOException {
+            return maybeSkip(n);
+        }
+
+        @Override
+        public int read() throws IOException {
+            int read = maybeReadBytes(buffer, 0, 1);
+            if (read <= 0) {
+                return read;
+            }
+            return buffer[0] & 0xff;
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return maybeReadBytes(buffer, 0, buffer.length);
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int count) throws IOException {
+            return maybeReadBytes(buffer, offset, count);
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractEncoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractEncoder.java
new file mode 100644
index 0000000..d3ed6de
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/AbstractEncoder.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.serialize;
+
+import org.gradle.api.Nullable;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public abstract class AbstractEncoder implements Encoder {
+    private EncoderStream stream;
+
+    public OutputStream getOutputStream() {
+        if (stream == null) {
+            stream = new EncoderStream();
+        }
+        return stream;
+    }
+
+    public void writeBytes(byte[] bytes) throws IOException {
+        writeBytes(bytes, 0, bytes.length);
+    }
+
+    public void writeBinary(byte[] bytes) throws IOException {
+        writeBinary(bytes, 0, bytes.length);
+    }
+
+    public void writeBinary(byte[] bytes, int offset, int count) throws IOException {
+        writeSmallInt(count);
+        writeBytes(bytes, offset, count);
+    }
+
+    public void writeSmallInt(int value) throws IOException {
+        writeInt(value);
+    }
+
+    public void writeSmallLong(long value) throws IOException {
+        writeLong(value);
+    }
+
+    public void writeNullableString(@Nullable CharSequence value) throws IOException {
+        if (value == null) {
+            writeBoolean(false);
+        } else {
+            writeBoolean(true);
+            writeString(value.toString());
+        }
+    }
+
+    private class EncoderStream extends OutputStream {
+        @Override
+        public void write(byte[] buffer) throws IOException {
+            writeBytes(buffer);
+        }
+
+        @Override
+        public void write(byte[] buffer, int offset, int length) throws IOException {
+            writeBytes(buffer, offset, length);
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            writeByte((byte)b);
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/BaseSerializerFactory.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/BaseSerializerFactory.java
new file mode 100644
index 0000000..1ebc2e3
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/BaseSerializerFactory.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.io.File;
+import java.util.Map;
+
+public class BaseSerializerFactory {
+    public static final Serializer<String> STRING_SERIALIZER = new StringSerializer();
+    public static final Serializer<Boolean> BOOLEAN_SERIALIZER = new BooleanSerializer();
+    public static final Serializer<Long> LONG_SERIALIZER = new LongSerializer();
+    public static final Serializer<File> FILE_SERIALIZER = new FileSerializer();
+    public static final Serializer<byte[]> BYTE_ARRAY_SERIALIZER = new ByteArraySerializer();
+    public static final Serializer<Map<String, String>> NO_NULL_STRING_MAP_SERIALIZER = new StringMapSerializer();
+
+    public <T> Serializer<T> getSerializerFor(Class<T> type) {
+        if (type.equals(String.class)) {
+            return (Serializer<T>) STRING_SERIALIZER;
+        }
+        if (type.equals(Long.class)) {
+            return (Serializer) LONG_SERIALIZER;
+        }
+        if (type.equals(File.class)) {
+            return (Serializer) FILE_SERIALIZER;
+        }
+        if (type.equals(byte[].class)) {
+            return (Serializer) BYTE_ARRAY_SERIALIZER;
+        }
+        if (type.isEnum()) {
+            return new EnumSerializer(type);
+        }
+        if (type.equals(Boolean.class)) {
+            return (Serializer<T>) BOOLEAN_SERIALIZER;
+        }
+        return new DefaultSerializer<T>(type.getClassLoader());
+    }
+
+    private static class EnumSerializer<T extends Enum> implements Serializer<T> {
+        private final Class<T> type;
+
+        private EnumSerializer(Class<T> type) {
+            this.type = type;
+        }
+
+        public T read(Decoder decoder) throws Exception {
+            return type.getEnumConstants()[decoder.readSmallInt()];
+        }
+
+        public void write(Encoder encoder, T value) throws Exception {
+            encoder.writeSmallInt((byte) value.ordinal());
+        }
+    }
+
+    private static class LongSerializer implements Serializer<Long> {
+        public Long read(Decoder decoder) throws Exception {
+            return decoder.readLong();
+        }
+
+        public void write(Encoder encoder, Long value) throws Exception {
+            encoder.writeLong(value);
+        }
+    }
+
+    private static class StringSerializer implements Serializer<String> {
+        public String read(Decoder decoder) throws Exception {
+            return decoder.readString();
+        }
+
+        public void write(Encoder encoder, String value) throws Exception {
+            encoder.writeString(value);
+        }
+    }
+
+    private static class FileSerializer implements Serializer<File> {
+        public File read(Decoder decoder) throws Exception {
+            return new File(decoder.readString());
+        }
+
+        public void write(Encoder encoder, File value) throws Exception {
+            encoder.writeString(value.getPath());
+        }
+    }
+
+    private static class ByteArraySerializer implements Serializer<byte[]> {
+        public byte[] read(Decoder decoder) throws Exception {
+            return decoder.readBinary();
+        }
+
+        public void write(Encoder encoder, byte[] value) throws Exception {
+            encoder.writeBinary(value);
+        }
+    }
+
+    private static class StringMapSerializer implements Serializer<Map<String, String>> {
+        public Map<String, String> read(Decoder decoder) throws Exception {
+            int pairs = decoder.readSmallInt();
+            ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+            for (int i = 0; i < pairs; ++i) {
+                builder.put(decoder.readString(), decoder.readString());
+            }
+            return builder.build();
+        }
+
+        public void write(Encoder encoder, Map<String, String> value) throws Exception {
+            encoder.writeSmallInt(value.size());
+            for (Map.Entry<String, String> entry : value.entrySet()) {
+                encoder.writeString(entry.getKey());
+                encoder.writeString(entry.getValue());
+            }
+        }
+    }
+
+    private static class BooleanSerializer implements Serializer<Boolean> {
+        @Override
+        public Boolean read(Decoder decoder) throws Exception {
+            return decoder.readBoolean();
+        }
+
+        @Override
+        public void write(Encoder encoder, Boolean value) throws Exception {
+            encoder.writeBoolean(value);
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Decoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Decoder.java
new file mode 100644
index 0000000..9096b4e
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Decoder.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.serialize;
+
+import org.gradle.api.Nullable;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides a way to decode structured data from a backing byte stream.
+ */
+public interface Decoder {
+    /**
+     * Returns an InputStream which can be used to read raw bytes.
+     */
+    InputStream getInputStream();
+
+    /**
+     * Reads a signed 64 bit long value. Can read any value that was written using {@link Encoder#writeLong(long)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the long value can be fully read.
+     */
+    long readLong() throws EOFException, IOException;
+
+    /**
+     * Reads a signed 64 bit int value. Can read any value that was written using {@link Encoder#writeSmallLong(int)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
+     */
+    long readSmallLong() throws EOFException, IOException;
+
+    /**
+     * Reads a signed 32 bit int value. Can read any value that was written using {@link Encoder#writeInt(int)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
+     */
+    int readInt() throws EOFException, IOException;
+
+    /**
+     * Reads a signed 32 bit int value. Can read any value that was written using {@link Encoder#writeSmallInt(int)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
+     */
+    int readSmallInt() throws EOFException, IOException;
+
+    /**
+     * Reads a boolean value. Can read any value that was written using {@link Encoder#writeBoolean(boolean)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the boolean value can be fully read.
+     */
+    boolean readBoolean() throws EOFException, IOException;
+
+    /**
+     * Reads a non-null string value. Can read any value that was written using {@link Encoder#writeString(CharSequence)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the string can be fully read.
+     */
+    String readString() throws EOFException, IOException;
+
+    /**
+     * Reads a nullable string value. Can reads any value that was written using {@link Encoder#writeNullableString(CharSequence)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the string can be fully read.
+     */
+    @Nullable
+    String readNullableString() throws EOFException, IOException;
+
+    /**
+     * Reads a byte value. Can read any byte value that was written using one of the raw byte methods on {@link Encoder}, such as {@link Encoder#writeByte(byte)} or {@link Encoder#getOutputStream()}
+     *
+     * @throws EOFException when the end of the byte stream is reached.
+     */
+    byte readByte() throws EOFException, IOException;
+
+    /**
+     * Reads bytes into the given buffer, filling the buffer. Can read any byte values that were written using one of the raw byte methods on {@link Encoder}, such as {@link
+     * Encoder#writeBytes(byte[])} or {@link Encoder#getOutputStream()}
+     *
+     * @throws EOFException when the end of the byte stream is reached before the buffer is full.
+     */
+    void readBytes(byte[] buffer) throws EOFException, IOException;
+
+    /**
+     * Reads the specified number of bytes into the given buffer. Can read any byte values that were written using one of the raw byte methods on {@link Encoder}, such as {@link
+     * Encoder#writeBytes(byte[])} or {@link Encoder#getOutputStream()}
+     *
+     * @throws EOFException when the end of the byte stream is reached before the specified number of bytes were read.
+     */
+    void readBytes(byte[] buffer, int offset, int count) throws EOFException, IOException;
+
+    /**
+     * Reads a byte array. Can read any byte array written using {@link Encoder#writeBinary(byte[])} or {@link Encoder#writeBinary(byte[], int, int)}.
+     *
+     * @throws EOFException when the end of the byte stream is reached before the byte array was fully read.
+     */
+    byte[] readBinary() throws EOFException, IOException;
+
+    /**
+     * Skips the given number of bytes. Can skip over any byte values that were written using one of the raw byte methods on {@link Encoder}.
+     */
+    void skipBytes(long count) throws EOFException, IOException;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializer.java
new file mode 100644
index 0000000..230c718
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.serialize;
+
+import org.gradle.internal.io.ClassLoaderObjectInputStream;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.StreamCorruptedException;
+
+public class DefaultSerializer<T> implements Serializer<T> {
+    private ClassLoader classLoader;
+
+    public DefaultSerializer() {
+        classLoader = getClass().getClassLoader();
+    }
+
+    public DefaultSerializer(ClassLoader classLoader) {
+        this.classLoader = classLoader != null ? classLoader : getClass().getClassLoader();
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public T read(Decoder decoder) throws Exception {
+        try {
+            return (T) new ClassLoaderObjectInputStream(decoder.getInputStream(), classLoader).readObject();
+        } catch (StreamCorruptedException e) {
+            return null;
+        }
+    }
+
+    public void write(Encoder encoder, T value) throws IOException {
+        ObjectOutputStream objectStr = new ObjectOutputStream(encoder.getOutputStream());
+        objectStr.writeObject(value);
+        objectStr.flush();
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializerRegistry.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializerRegistry.java
new file mode 100644
index 0000000..e25e26a
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/DefaultSerializerRegistry.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class DefaultSerializerRegistry<T> implements SerializerRegistry<T> {
+    private final Map<Class<?>, Serializer<?>> serializerMap = new TreeMap<Class<?>, Serializer<?>>(new Comparator<Class<?>>() {
+        public int compare(Class<?> o1, Class<?> o2) {
+            return o1.getName().compareTo(o2.getName());
+        }
+    });
+
+    public <U extends T> void register(Class<U> implementationType, Serializer<U> serializer) {
+        serializerMap.put(implementationType, serializer);
+    }
+
+    public Serializer<T> build() {
+        if (serializerMap.size() == 1) {
+            return (Serializer<T>) serializerMap.values().iterator().next();
+        }
+        TaggedTypeSerializer<T> serializer = new TaggedTypeSerializer<T>();
+        for (Map.Entry<Class<?>, Serializer<?>> entry : serializerMap.entrySet()) {
+            serializer.add(entry.getKey(), entry.getValue());
+        }
+        return serializer;
+    }
+
+    private static class TypeInfo {
+        final byte tag;
+        final Serializer serializer;
+
+        private TypeInfo(byte tag, Serializer serializer) {
+            this.tag = tag;
+            this.serializer = serializer;
+        }
+    }
+
+    private static class TaggedTypeSerializer<T> implements Serializer<T> {
+        private final Map<Class<?>, TypeInfo> serializersByType = new HashMap<Class<?>, TypeInfo>();
+        private final Map<Byte, TypeInfo> serializersByTag = new HashMap<Byte, TypeInfo>();
+
+        private <T> void add(Class<?> type, Serializer<?> serializer) {
+            TypeInfo typeInfo = new TypeInfo((byte) serializersByTag.size(), serializer);
+            serializersByType.put(type, typeInfo);
+            serializersByTag.put(typeInfo.tag, typeInfo);
+        }
+
+        public T read(Decoder decoder) throws Exception {
+            byte tag = decoder.readByte();
+            TypeInfo typeInfo = serializersByTag.get(tag);
+            if (typeInfo == null) {
+                throw new IllegalArgumentException(String.format("Unexpected type tag %d found.", tag));
+            }
+            return (T) typeInfo.serializer.read(decoder);
+        }
+
+        public void write(Encoder encoder, T value) throws Exception {
+            Class<?> targetType = value instanceof Throwable ? Throwable.class : value.getClass();
+            TypeInfo typeInfo = serializersByType.get(targetType);
+            if (typeInfo == null) {
+                throw new IllegalArgumentException(String.format("Don't know how to serialize an object of type %s.", value.getClass().getName()));
+            }
+            encoder.writeByte(typeInfo.tag);
+            typeInfo.serializer.write(encoder, value);
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Encoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Encoder.java
new file mode 100644
index 0000000..4cc9aac
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Encoder.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.serialize;
+
+import org.gradle.api.Nullable;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public interface Encoder {
+    /**
+     * Returns an OutputStream that can be used to write byte content to the stream.
+     */
+    OutputStream getOutputStream();
+
+    /**
+     * Writes a raw byte value to the stream.
+     */
+    void writeByte(byte value) throws IOException;
+
+    /**
+     * Writes the given raw bytes to the stream. Does not encode any length information.
+     */
+    void writeBytes(byte[] bytes) throws IOException;
+
+    /**
+     * Writes the given raw bytes to the stream. Does not encode any length information.
+     */
+    void writeBytes(byte[] bytes, int offset, int count) throws IOException;
+
+    /**
+     * Writes the given byte array to the stream. Encodes the bytes and length information.
+     */
+    void writeBinary(byte[] bytes) throws IOException;
+
+    /**
+     * Writes the given byte array to the stream. Encodes the bytes and length information.
+     */
+    void writeBinary(byte[] bytes, int offset, int count) throws IOException;
+
+    /**
+     * Writes a signed 64 bit long value. The implementation may encode the value as a variable number of bytes, not necessarily as 8 bytes.
+     */
+    void writeLong(long value) throws IOException;
+
+    /**
+     * Writes a signed 64 bit long value whose value is likely to be small and positive but may not be. The implementation may encode the value in a way that is more efficient for small positive
+     * values.
+     */
+    void writeSmallLong(long value) throws IOException;
+
+    /**
+     * Writes a signed 32 bit int value. The implementation may encode the value as a variable number of bytes, not necessarily as 4 bytes.
+     */
+    void writeInt(int value) throws IOException;
+
+    /**
+     * Writes a signed 32 bit int value whose value is likely to be small and positive but may not be. The implementation may encode the value in a way that
+     * is more efficient for small positive values.
+     */
+    void writeSmallInt(int value) throws IOException;
+
+    /**
+     * Writes a boolean value.
+     */
+    void writeBoolean(boolean value) throws IOException;
+
+    /**
+     * Writes a non-null string value.
+     */
+    void writeString(CharSequence value) throws IOException;
+
+    /**
+     * Writes a nullable string value.
+     */
+    void writeNullableString(@Nullable CharSequence value) throws IOException;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/FlushableEncoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/FlushableEncoder.java
new file mode 100644
index 0000000..63a9a50
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/FlushableEncoder.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.internal.serialize;
+
+import java.io.Flushable;
+import java.io.IOException;
+
+public interface FlushableEncoder extends Encoder, Flushable {
+    void flush() throws IOException;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/InputStreamBackedDecoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/InputStreamBackedDecoder.java
new file mode 100644
index 0000000..985963d
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/InputStreamBackedDecoder.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.internal.serialize;
+
+import java.io.*;
+
+public class InputStreamBackedDecoder extends AbstractDecoder implements Decoder, Closeable {
+    private final DataInputStream inputStream;
+
+    public InputStreamBackedDecoder(InputStream inputStream) {
+        this.inputStream = new DataInputStream(inputStream);
+    }
+
+    @Override
+    protected int maybeReadBytes(byte[] buffer, int offset, int count) throws IOException {
+        return inputStream.read(buffer, offset, count);
+    }
+
+    @Override
+    protected long maybeSkip(long count) throws IOException {
+        return inputStream.skip(count);
+    }
+
+    public long readLong() throws IOException {
+        return inputStream.readLong();
+    }
+
+    public int readInt() throws EOFException, IOException {
+        return inputStream.readInt();
+    }
+
+    public boolean readBoolean() throws EOFException, IOException {
+        return inputStream.readBoolean();
+    }
+
+    public String readString() throws EOFException, IOException {
+        return inputStream.readUTF();
+    }
+
+    public byte readByte() throws IOException {
+        return (byte)(inputStream.readByte() & 0xff);
+    }
+
+    public void readBytes(byte[] buffer, int offset, int count) throws IOException {
+        inputStream.readFully(buffer, offset, count);
+    }
+
+    public void close() throws IOException {
+        inputStream.close();
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ListSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ListSerializer.java
new file mode 100644
index 0000000..83d9527
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ListSerializer.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.internal.serialize;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListSerializer<T> extends AbstractCollectionSerializer<T> implements Serializer<List<T>> {
+
+    public ListSerializer(Serializer<T> entrySerializer) {
+        super(entrySerializer);
+    }
+
+    public List<T> read(Decoder decoder) throws Exception {
+        List<T> values = new ArrayList<T>();
+        readValues(decoder, values);
+        return values;
+    }
+
+    public void write(Encoder encoder, List<T> value) throws Exception {
+        writeValues(encoder, value);
+    }
+
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/LongSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/LongSerializer.java
new file mode 100644
index 0000000..5c93a7c
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/LongSerializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.serialize;
+
+public class LongSerializer implements Serializer<Long> {
+    public Long read(Decoder decoder) throws Exception {
+        return decoder.readLong();
+    }
+
+    public void write(Encoder encoder, Long value) throws Exception {
+        if (value == null) {
+            throw new IllegalArgumentException("This serializer does not serialize null values.");
+        }
+        encoder.writeLong(value);
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/MapSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/MapSerializer.java
new file mode 100644
index 0000000..64f3e52
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/MapSerializer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static java.lang.String.format;
+
+public class MapSerializer<U, V> implements Serializer<Map<U, V>> {
+    private final Serializer<U> keySerializer;
+    private final Serializer<V> valueSerializer;
+
+    public MapSerializer(Serializer<U> keySerializer, Serializer<V> valueSerializer) {
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+    }
+
+    public Map<U, V> read(Decoder decoder) throws Exception {
+        int size = decoder.readInt();
+        Map<U, V> valueMap = new LinkedHashMap<U, V>(size);
+        for (int i = 0; i < size; i++) {
+            U key = keySerializer.read(decoder);
+            V value = valueSerializer.read(decoder);
+            valueMap.put(key, value);
+        }
+        return valueMap;
+    }
+
+    public void write(Encoder encoder, Map<U, V> value) throws Exception {
+        encoder.writeInt(value.size());
+        for (Map.Entry<U, V> entry : value.entrySet()) {
+            try {
+                keySerializer.write(encoder, entry.getKey());
+                valueSerializer.write(encoder, entry.getValue());
+            } catch (Exception e) {
+                throw new EntrySerializationException(entry.getKey(), entry.getValue(), e);
+            }
+        }
+    }
+
+    public static class EntrySerializationException extends RuntimeException {
+
+        private final Object key;
+        private final Object value;
+
+        EntrySerializationException(Object key, Object value, Exception cause) {
+            super(format("Unable to write entry with key: '%s' and value: '%s'.", key, value), cause);
+            this.key = key;
+            this.value = value;
+        }
+
+        public Object getKey() {
+            return key;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/NullSafeStringSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/NullSafeStringSerializer.java
new file mode 100644
index 0000000..f0248a5
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/NullSafeStringSerializer.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.serialize;
+
+public class NullSafeStringSerializer implements Serializer<String> {
+    public String read(Decoder decoder) throws Exception {
+        return decoder.readNullableString();
+    }
+
+    public void write(Encoder encoder, String value) throws Exception {
+        encoder.writeNullableString(value);
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectReader.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectReader.java
new file mode 100644
index 0000000..d9666c7
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectReader.java
@@ -0,0 +1,21 @@
+/*
+ * 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.serialize;
+
+public interface ObjectReader<T> {
+    T read() throws Exception;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectWriter.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectWriter.java
new file mode 100644
index 0000000..e19529d
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/ObjectWriter.java
@@ -0,0 +1,21 @@
+/*
+ * 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.serialize;
+
+public interface ObjectWriter<T> {
+    void write(T value) throws Exception;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/OutputStreamBackedEncoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/OutputStreamBackedEncoder.java
new file mode 100644
index 0000000..84ecb33
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/OutputStreamBackedEncoder.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.internal.serialize;
+
+import java.io.Closeable;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class OutputStreamBackedEncoder extends AbstractEncoder implements Closeable, FlushableEncoder {
+    private final DataOutputStream outputStream;
+
+    public OutputStreamBackedEncoder(OutputStream outputStream) {
+        this.outputStream = new DataOutputStream(outputStream);
+    }
+
+    public void writeLong(long value) throws IOException {
+        outputStream.writeLong(value);
+    }
+
+    public void writeInt(int value) throws IOException {
+        outputStream.writeInt(value);
+    }
+
+    public void writeBoolean(boolean value) throws IOException {
+        outputStream.writeBoolean(value);
+    }
+
+    public void writeString(CharSequence value) throws IOException {
+        if (value == null) {
+            throw new IllegalArgumentException("Cannot encode a null string.");
+        }
+        outputStream.writeUTF(value.toString());
+    }
+
+    public void writeByte(byte value) throws IOException {
+        outputStream.writeByte(value);
+    }
+
+    public void writeBytes(byte[] bytes, int offset, int count) throws IOException {
+        outputStream.write(bytes, offset, count);
+    }
+
+    public void flush() throws IOException {
+        outputStream.flush();
+    }
+
+    public void close() throws IOException {
+        outputStream.close();
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Serializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Serializer.java
new file mode 100644
index 0000000..7f503be
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Serializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.serialize;
+
+public interface Serializer<T> {
+    /**
+     * Reads the next object from the given stream. The implementation must not perform any buffering, so that it reads only those bytes from the input stream that are
+     * required to deserialize the next object.
+     */
+    T read(Decoder decoder) throws Exception;
+
+    /**
+     * Writes the given object to the given stream. The implementation must not perform any buffering.
+     */
+    void write(Encoder encoder, T value) throws Exception;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SerializerRegistry.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SerializerRegistry.java
new file mode 100644
index 0000000..5d39fd4
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SerializerRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize;
+
+public interface SerializerRegistry<T> {
+    <U extends T> void register(Class<U> implementationType, Serializer<U> serializer);
+
+    Serializer<T> build();
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SetSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SetSerializer.java
new file mode 100644
index 0000000..8da54fb
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/SetSerializer.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.internal.serialize;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class SetSerializer<T> extends AbstractCollectionSerializer<T> implements Serializer<Set<T>> {
+
+    private final boolean linkedHashSet;
+
+    public SetSerializer(Serializer<T> entrySerializer) {
+        this(entrySerializer, true);
+    }
+
+    public SetSerializer(Serializer<T> entrySerializer, boolean linkedHashSet) {
+        super(entrySerializer);
+        this.linkedHashSet = linkedHashSet;
+    }
+
+    public Set<T> read(Decoder decoder) throws Exception {
+        Set<T> values = linkedHashSet? new LinkedHashSet<T>() : new HashSet<T>();
+        readValues(decoder, values);
+        return values;
+    }
+
+    public void write(Encoder encoder, Set<T> value) throws Exception {
+        writeValues(encoder, value);
+    }
+
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/JavaSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/JavaSerializer.java
new file mode 100644
index 0000000..02bf471
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/JavaSerializer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.serialize.kryo;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
+import org.gradle.messaging.remote.internal.Message;
+
+public class JavaSerializer<T> implements StatefulSerializer<T> {
+    private final ClassLoader classLoader;
+
+    public JavaSerializer(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public ObjectReader<T> newReader(Decoder decoder) {
+        return new JavaReader<T>(decoder, classLoader);
+    }
+
+    public ObjectWriter<T> newWriter(Encoder encoder) {
+        return new JavaWriter<T>(encoder);
+    }
+
+    private static class JavaReader<T> implements ObjectReader<T> {
+        private final Decoder decoder;
+        private final ClassLoader classLoader;
+
+        private JavaReader(Decoder decoder, ClassLoader classLoader) {
+            this.decoder = decoder;
+            this.classLoader = classLoader;
+        }
+
+        public T read() throws Exception {
+            return (T) Message.receive(decoder.getInputStream(), classLoader);
+        }
+    }
+
+    private class JavaWriter<T> implements ObjectWriter<T> {
+        private final Encoder encoder;
+
+        public JavaWriter(Encoder encoder) {
+            this.encoder = encoder;
+        }
+
+        public void write(T value) throws Exception {
+            Message.send(value, encoder.getOutputStream());
+        }
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedDecoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedDecoder.java
new file mode 100644
index 0000000..99ba2bc
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedDecoder.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize.kryo;
+
+import com.esotericsoftware.kryo.KryoException;
+import com.esotericsoftware.kryo.io.Input;
+import org.gradle.internal.serialize.AbstractDecoder;
+import org.gradle.internal.serialize.Decoder;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Note that this decoder uses buffering, so will attempt to read beyond the end of the encoded data. This means you should use this type only when this decoder will be used to decode the entire
+ * stream.
+ */
+public class KryoBackedDecoder extends AbstractDecoder implements Decoder, Closeable {
+    private final Input input;
+    private final InputStream inputStream;
+    private long extraSkipped;
+
+    public KryoBackedDecoder(InputStream inputStream) {
+        this(inputStream, 4096);
+    }
+
+    public KryoBackedDecoder(InputStream inputStream, int bufferSize) {
+        this.inputStream = inputStream;
+        input = new Input(this.inputStream, bufferSize);
+    }
+
+    @Override
+    protected int maybeReadBytes(byte[] buffer, int offset, int count) {
+        return input.read(buffer, offset, count);
+    }
+
+    @Override
+    protected long maybeSkip(long count) throws IOException {
+        // Work around some bugs in Input.skip()
+        int remaining = input.limit() - input.position();
+        if (remaining == 0) {
+            long skipped = inputStream.skip(count);
+            if (skipped > 0) {
+                extraSkipped += skipped;
+            }
+            return skipped;
+        } else if (count <= remaining) {
+            input.setPosition(input.position() + (int) count);
+            return count;
+        } else {
+            input.setPosition(input.limit());
+            return remaining;
+        }
+    }
+
+    private RuntimeException maybeEndOfStream(KryoException e) throws EOFException {
+        if (e.getMessage().equals("Buffer underflow.")) {
+            throw (EOFException) (new EOFException().initCause(e));
+        }
+        throw e;
+    }
+
+    public byte readByte() throws EOFException {
+        try {
+            return input.readByte();
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public void readBytes(byte[] buffer, int offset, int count) throws EOFException {
+        try {
+            input.readBytes(buffer, offset, count);
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public long readLong() throws EOFException {
+        try {
+            return input.readLong();
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public long readSmallLong() throws EOFException, IOException {
+        try {
+            return input.readLong(true);
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public int readInt() throws EOFException {
+        try {
+            return input.readInt();
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public int readSmallInt() throws EOFException {
+        try {
+            return input.readInt(true);
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public boolean readBoolean() throws EOFException {
+        try {
+            return input.readBoolean();
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    public String readString() throws EOFException {
+        return readNullableString();
+    }
+
+    public String readNullableString() throws EOFException {
+        try {
+            return input.readString();
+        } catch (KryoException e) {
+            throw maybeEndOfStream(e);
+        }
+    }
+
+    /**
+     * Returns the total number of bytes consumed by this decoder. Some additional bytes may also be buffered by this decoder but have not been consumed.
+     */
+    public long getReadPosition() {
+        return input.total() + extraSkipped;
+    }
+
+    public void close() throws IOException {
+        input.close();
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedEncoder.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedEncoder.java
new file mode 100644
index 0000000..17bdf2d
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/KryoBackedEncoder.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.serialize.kryo;
+
+import com.esotericsoftware.kryo.io.Output;
+import org.gradle.api.Nullable;
+import org.gradle.internal.serialize.AbstractEncoder;
+import org.gradle.internal.serialize.FlushableEncoder;
+
+import java.io.Closeable;
+import java.io.OutputStream;
+
+public class KryoBackedEncoder extends AbstractEncoder implements FlushableEncoder, Closeable {
+    private final Output output;
+
+    public KryoBackedEncoder(OutputStream outputStream) {
+        this(outputStream, 4096);
+    }
+
+    public KryoBackedEncoder(OutputStream outputStream, int bufferSize) {
+        output = new Output(outputStream, bufferSize);
+    }
+
+    public void writeByte(byte value) {
+        output.writeByte(value);
+    }
+
+    public void writeBytes(byte[] bytes, int offset, int count) {
+        output.writeBytes(bytes, offset, count);
+    }
+
+    public void writeLong(long value) {
+        output.writeLong(value);
+    }
+
+    public void writeSmallLong(long value) {
+        output.writeLong(value, true);
+    }
+
+    public void writeInt(int value) {
+        output.writeInt(value);
+    }
+
+    public void writeSmallInt(int value) {
+        output.writeInt(value, true);
+    }
+
+    public void writeBoolean(boolean value) {
+        output.writeBoolean(value);
+    }
+
+    public void writeString(CharSequence value) {
+        if (value == null) {
+            throw new IllegalArgumentException("Cannot encode a null string.");
+        }
+        output.writeString(value);
+    }
+
+    public void writeNullableString(@Nullable CharSequence value) {
+        output.writeString(value);
+    }
+
+    /**
+     * Returns the total number of bytes written by this encoder, some of which is may still be buffered.
+     */
+    public int getWritePosition() {
+        return output.total();
+    }
+
+    public void flush() {
+        output.flush();
+    }
+
+    public void close() {
+        output.close();
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/StatefulSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/StatefulSerializer.java
new file mode 100644
index 0000000..d9550cc
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/StatefulSerializer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.serialize.kryo;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
+
+public interface StatefulSerializer<T> {
+    ObjectReader<T> newReader(Decoder decoder);
+
+    ObjectWriter<T> newWriter(Encoder encoder);
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/TypeSafeSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/TypeSafeSerializer.java
new file mode 100644
index 0000000..68790f3
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/kryo/TypeSafeSerializer.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.internal.serialize.kryo;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
+
+public class TypeSafeSerializer<T> implements StatefulSerializer<Object> {
+    private final Class<T> type;
+    private final StatefulSerializer<T> serializer;
+
+    public TypeSafeSerializer(Class<T> type, StatefulSerializer<T> serializer) {
+        this.type = type;
+        this.serializer = serializer;
+    }
+
+    public ObjectReader<Object> newReader(Decoder decoder) {
+        final ObjectReader<T> reader = serializer.newReader(decoder);
+        return new ObjectReader<Object>() {
+            public Object read() throws Exception {
+                return reader.read();
+            }
+        };
+    }
+
+    public ObjectWriter<Object> newWriter(Encoder encoder) {
+        final ObjectWriter<T> writer = serializer.newWriter(encoder);
+        return new ObjectWriter<Object>() {
+            public void write(Object value) throws Exception {
+                writer.write(type.cast(value));
+            }
+        };
+    }
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/ContextClassLoaderProxy.java b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/ContextClassLoaderProxy.java
new file mode 100644
index 0000000..3894824
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/ContextClassLoaderProxy.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.messaging.dispatch;
+
+/**
+ * Creates a proxy object which sets the context ClassLoader when invoking methods on the target object.
+ *
+ * @param <T>
+ */
+public class ContextClassLoaderProxy<T> {
+    private final ProxyDispatchAdapter<T> adapter;
+
+    /**
+     * Creates a proxy which dispatches to the given target object.
+     */
+    public ContextClassLoaderProxy(Class<T> type, T target, ClassLoader contextClassLoader) {
+        adapter = new ProxyDispatchAdapter<T>(new ContextClassLoaderDispatch<MethodInvocation>(new ReflectionDispatch(target), contextClassLoader), type);
+    }
+
+    public T getSource() {
+        return adapter.getSource();
+    }
+}
\ No newline at end of file
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/MethodInvocation.java b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/MethodInvocation.java
index 68e51cc..878df4a 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/MethodInvocation.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/MethodInvocation.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.messaging.dispatch;
 
+import com.google.common.base.Joiner;
+
 import java.lang.reflect.Method;
 import java.util.Arrays;
 
@@ -60,7 +62,7 @@ public class MethodInvocation {
 
     @Override
     public String toString() {
-        return String.format("[MethodInvocation method: %s()]", method.getName());
+        return String.format("[MethodInvocation method: %s(%s)]", method.getName(), Joiner.on(", ").join(arguments));
     }
 }
 
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/Receive.java b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/Receive.java
index a6021b9..7d3d32a 100755
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/Receive.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/dispatch/Receive.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.messaging.dispatch;
 
+import org.gradle.api.Nullable;
+
 /**
  * A source for messages. Implementations do not have to be thread-safe.
  */
@@ -24,5 +26,6 @@ public interface Receive<T> {
      *
      * @return The next message, or null when the end of the stream has been reached.
      */
+    @Nullable
     T receive();
 }
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/ObjectConnectionBuilder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/ObjectConnectionBuilder.java
index 47da994..148fe05 100755
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/ObjectConnectionBuilder.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/ObjectConnectionBuilder.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.messaging.remote;
 
-import org.gradle.messaging.serialize.kryo.StatefulSerializer;
+import org.gradle.internal.serialize.kryo.StatefulSerializer;
 
 public interface ObjectConnectionBuilder {
     /**
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/ConnectCompletion.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/ConnectCompletion.java
index 6dc5663..72c9984 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/ConnectCompletion.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/ConnectCompletion.java
@@ -26,12 +26,12 @@ public interface ConnectCompletion {
      *
      * @param messageClassLoader The ClassLoader to use to deserialize incoming messages.
      */
-    <T> Connection<T> create(ClassLoader messageClassLoader);
+    <T> RemoteConnection<T> create(ClassLoader messageClassLoader);
 
     /**
      * Creates the connection. Uses the specified serializer for all messages.
      *
      * @return The serializer to use.
      */
-    <T> Connection<T> create(MessageSerializer<T> serializer);
+    <T> RemoteConnection<T> create(MessageSerializer<T> serializer);
 }
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/DefaultMessageSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/DefaultMessageSerializer.java
index c2706f7..bf16344 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/DefaultMessageSerializer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/DefaultMessageSerializer.java
@@ -16,8 +16,8 @@
 package org.gradle.messaging.remote.internal;
 
 import org.gradle.messaging.remote.Address;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
 
 import java.io.InputStream;
 import java.io.OutputStream;
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/EagerReceiveBuffer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/EagerReceiveBuffer.java
index 0bcc410..688c4c7 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/EagerReceiveBuffer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/EagerReceiveBuffer.java
@@ -34,7 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
  * Continuously consumes from on or more receivers, serialising to an in memory buffer for synchronous consumption.
  * <p>
  * Messages from the same receive instance are guaranteed to always be returned from {@link #receive()} in sequence. However, no
- * guarantee is made to deliver messages from different sources in chronological order when multiple multiple receive instances
+ * guarantee is made to deliver messages from different sources in chronological order when multiple receive instances
  * are being consumed from.
  * <p>
  * The buffer is bounded, the size of which is specified at construction or defaulting to {@value #DEFAULT_BUFFER_SIZE}.
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessageSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessageSerializer.java
index 74e94ca..7f3c66c 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessageSerializer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessageSerializer.java
@@ -16,8 +16,8 @@
 package org.gradle.messaging.remote.internal;
 
 import org.gradle.messaging.remote.Address;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
 
 import java.io.InputStream;
 import java.io.OutputStream;
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessagingServices.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessagingServices.java
index dde040e..a77534f 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessagingServices.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/MessagingServices.java
@@ -143,8 +143,8 @@ public class MessagingServices extends DefaultServiceRegistry implements Stoppab
                 messageClassLoader);
     }
 
-    protected AsyncConnection<DiscoveryMessage> createMulticastConnection(ExecutorFactory executorFactory) {
-        MulticastConnection<DiscoveryMessage> connection = new MulticastConnection<DiscoveryMessage>(broadcastAddress, new DiscoveryProtocolSerializer());
+    protected AsyncConnection<DiscoveryMessage> createMulticastConnection(ExecutorFactory executorFactory, InetAddressFactory addressFactory) {
+        MulticastConnection<DiscoveryMessage> connection = new MulticastConnection<DiscoveryMessage>(broadcastAddress, new DiscoveryProtocolSerializer(), addressFactory);
         return new AsyncConnectionAdapter<DiscoveryMessage>(
                 connection,
                 new DiscardingFailureHandler<DiscoveryMessage>(LoggerFactory.getLogger(MulticastConnection.class)),
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/RemoteConnection.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/RemoteConnection.java
new file mode 100644
index 0000000..a1b1f01
--- /dev/null
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/RemoteConnection.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.messaging.remote.internal;
+
+import org.gradle.api.Nullable;
+
+/**
+ * <p>A messaging end-point with some remote, or otherwise unreliable, peer.</p>
+ *
+ * <p>This interface simply specializes the exceptions thrown by the methods of this connection.</p>
+ */
+public interface RemoteConnection<T> extends Connection<T> {
+    /**
+     * {@inheritDoc}
+     *
+     * @throws MessageIOException On failure to dispatch the message to the peer.
+     */
+    void dispatch(T message) throws MessageIOException;
+
+    /**
+     * {@inheritDoc}
+     * @throws MessageIOException On failure to receive the message from the peer.
+     */
+    @Nullable
+    T receive() throws MessageIOException;
+}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializer.java
index 93508b0..c7c4193 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializer.java
@@ -22,13 +22,13 @@ import org.gradle.messaging.remote.internal.hub.protocol.ChannelIdentifier;
 import org.gradle.messaging.remote.internal.hub.protocol.ChannelMessage;
 import org.gradle.messaging.remote.internal.hub.protocol.EndOfStream;
 import org.gradle.messaging.remote.internal.hub.protocol.InterHubMessage;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.FlushableEncoder;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
-import org.gradle.messaging.serialize.kryo.StatefulSerializer;
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.FlushableEncoder;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
+import org.gradle.internal.serialize.kryo.StatefulSerializer;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHub.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHub.java
index 85438cf..2050fc0 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHub.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHub.java
@@ -32,6 +32,10 @@ import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * A multi-channel message router.
+ *
+ * Use {@link #getOutgoing(String, Class)} to create a {@link Dispatch} to send unicast messages on a given channel.
+ * Use {@link #addHandler(String, Object)} to create a worker for incoming messages on a given channel.
+ * Use {@link #addConnection(Connection)} to attach another router to this router.
  */
 public class MessageHub implements AsyncStoppable {
     private enum State {Running, Stopping, Stopped}
@@ -56,9 +60,10 @@ public class MessageHub implements AsyncStoppable {
     }
 
     /**
-     * <p>Adds a {@link Dispatch} implementation that can be used to send outgoing messages on the given channel. The returned value is thread-safe.</p>
+     * <p>Adds a {@link Dispatch} implementation that can be used to send outgoing unicast messages on the given channel. Messages are queued in the order that they are
+     * dispatched, and are forwarded to at most one handler.</p>
      *
-     * <p>All messages sent via the dispatch are forwarded to exactly one connection.</p>
+     * <p>The returned value is thread-safe.</p>
      */
     public <T> Dispatch<T> getOutgoing(final String channelName, final Class<T> type) {
         lock.lock();
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHubBackedObjectConnection.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHubBackedObjectConnection.java
index de5aea3..e1a3428 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHubBackedObjectConnection.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MessageHubBackedObjectConnection.java
@@ -28,9 +28,9 @@ import org.gradle.messaging.remote.internal.ConnectCompletion;
 import org.gradle.messaging.remote.internal.Connection;
 import org.gradle.messaging.remote.internal.MessageSerializer;
 import org.gradle.messaging.remote.internal.hub.protocol.InterHubMessage;
-import org.gradle.messaging.serialize.kryo.JavaSerializer;
-import org.gradle.messaging.serialize.kryo.StatefulSerializer;
-import org.gradle.messaging.serialize.kryo.TypeSafeSerializer;
+import org.gradle.internal.serialize.kryo.JavaSerializer;
+import org.gradle.internal.serialize.kryo.StatefulSerializer;
+import org.gradle.internal.serialize.kryo.TypeSafeSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializer.java
index 462d998..cb53750 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializer.java
@@ -17,11 +17,11 @@
 package org.gradle.messaging.remote.internal.hub;
 
 import org.gradle.messaging.dispatch.MethodInvocation;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
-import org.gradle.messaging.serialize.kryo.StatefulSerializer;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
+import org.gradle.internal.serialize.kryo.StatefulSerializer;
 
 import java.io.IOException;
 import java.lang.reflect.Method;
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/InetAddressFactory.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/InetAddressFactory.java
index 83e3acc..b47df71 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/InetAddressFactory.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/InetAddressFactory.java
@@ -15,16 +15,17 @@
  */
 package org.gradle.messaging.remote.internal.inet;
 
+import org.gradle.api.Transformer;
+import org.gradle.internal.UncheckedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
 
 /**
  * Provides some information about the network addresses of the local machine.
@@ -34,6 +35,8 @@ public class InetAddressFactory {
     private final Object lock = new Object();
     private List<InetAddress> localAddresses;
     private List<InetAddress> remoteAddresses;
+    private List<NetworkInterface> multicastInterfaces;
+    private InetAddress localBindingAddress;
 
     /**
      * Determines the name of the local machine.
@@ -67,12 +70,7 @@ public class InetAddressFactory {
         try {
             synchronized (lock) {
                 init();
-                if (!localAddresses.isEmpty()) {
-                    return localAddresses;
-                }
-                InetAddress fallback = InetAddress.getByName(null);
-                LOGGER.debug("No loopback addresses, using fallback {}", fallback);
-                return Collections.singletonList(fallback);
+                return localAddresses;
             }
         } catch (Exception e) {
             throw new RuntimeException("Could not determine the local IP addresses for this machine.", e);
@@ -86,49 +84,75 @@ public class InetAddressFactory {
         try {
             synchronized (lock) {
                 init();
-                if (!remoteAddresses.isEmpty()) {
-                    return remoteAddresses;
-                }
-                InetAddress fallback = InetAddress.getLocalHost();
-                LOGGER.debug("No remote addresses, using fallback {}", fallback);
-                return Collections.singletonList(fallback);
+                return remoteAddresses;
             }
         } catch (Exception e) {
             throw new RuntimeException("Could not determine the remote IP addresses for this machine.", e);
         }
     }
 
+    /**
+     * Locates the network interfaces that should be used for multicast, in order of preference.
+     */
+    public List<NetworkInterface> findMulticastInterfaces() {
+        try {
+            synchronized (lock) {
+                init();
+                return multicastInterfaces;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Could not determine the multicast network interfaces for this machine.", e);
+        }
+    }
+
+    public InetAddress findLocalBindingAddress() {
+        try {
+            synchronized (lock) {
+                init();
+                return localBindingAddress;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Could not determine a usable local IP for this machine.", e);
+        }
+    }
+
+    private InetAddress findOpenshiftAddresses() {
+        for (String key : System.getenv().keySet()) {
+            if (key.startsWith("OPENSHIFT_") && key.endsWith("_IP")) {
+                String ipAddress = System.getenv(key);
+                LOGGER.debug("OPENSHIFT IP environment variable {} detected. Using IP address {}.", key, ipAddress);
+                try {
+                    return InetAddress.getByName(ipAddress);
+                } catch (UnknownHostException e) {
+                    throw new RuntimeException(String.format("Unable to use OPENSHIFT IP - invalid IP address '%s' specified in environment variable %s.", ipAddress, key), e);
+                }
+            }
+        }
+        return null;
+    }
+
     private void init() throws Exception {
         if (localAddresses != null) {
             return;
         }
 
-        Method loopbackMethod;
-        try {
-            loopbackMethod = NetworkInterface.class.getMethod("isLoopback");
-        } catch (NoSuchMethodException e) {
-            loopbackMethod = null;
-        }
+        Transformer<Boolean, NetworkInterface> loopback = loopback();
+        Transformer<Boolean, NetworkInterface> multicast = multicast();
 
         localAddresses = new ArrayList<InetAddress>();
         remoteAddresses = new ArrayList<InetAddress>();
+        multicastInterfaces = new ArrayList<NetworkInterface>();
 
         Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
         while (interfaces.hasMoreElements()) {
             NetworkInterface networkInterface = interfaces.nextElement();
-            LOGGER.debug("Adding IP addresses for network interface {}", networkInterface.getName());
+            LOGGER.debug("Adding IP addresses for network interface {}", networkInterface.getDisplayName());
             try {
-                Boolean isLoopbackInterface;
-                try {
-                    isLoopbackInterface = loopbackMethod == null ? null : (Boolean) loopbackMethod.invoke(networkInterface);
-                } catch (InvocationTargetException e) {
-                    if (!(e.getCause() instanceof SocketException)) {
-                        throw e.getCause();
-                    }
-                    // Ignore - treat as if we don't know
-                    isLoopbackInterface = null;
-                }
+                Boolean isLoopbackInterface = loopback.transform(networkInterface);
                 LOGGER.debug("Is this a loopback interface? {}", isLoopbackInterface);
+                Boolean isMulticast = multicast.transform(networkInterface);
+                LOGGER.debug("Is this a multicast interface? {}", isMulticast);
+                boolean isRemote = false;
 
                 Enumeration<InetAddress> candidates = networkInterface.getInetAddresses();
                 while (candidates.hasMoreElements()) {
@@ -139,28 +163,122 @@ public class InetAddressFactory {
                             LOGGER.debug("Adding loopback address {}", candidate);
                             localAddresses.add(candidate);
                         } else {
-                            LOGGER.debug("Adding non-loopback address {}", candidate);
+                            LOGGER.debug("Adding remote address {}", candidate);
                             remoteAddresses.add(candidate);
+                            isRemote = true;
                         }
                     } else if (isLoopbackInterface) {
                         if (candidate.isLoopbackAddress()) {
                             LOGGER.debug("Adding loopback address {}", candidate);
                             localAddresses.add(candidate);
                         } else {
-                            LOGGER.debug("Ignoring non-loopback address on loopback interface {}", candidate);
+                            LOGGER.debug("Ignoring remote address on loopback interface {}", candidate);
                         }
                     } else {
                         if (candidate.isLoopbackAddress()) {
-                            LOGGER.debug("Ignoring loopback address on non-loopback interface {}", candidate);
+                            LOGGER.debug("Ignoring loopback address on remote interface {}", candidate);
                         } else {
-                            LOGGER.debug("Adding non-loopback address {}", candidate);
+                            LOGGER.debug("Adding remote address {}", candidate);
                             remoteAddresses.add(candidate);
+                            isRemote = true;
                         }
                     }
                 }
+
+                if (!Boolean.FALSE.equals(isMulticast)) {
+                    // Prefer remotely reachable interfaces over loopback interfaces for multicast
+                    if (isRemote) {
+                        LOGGER.debug("Adding remote multicast interface {}", networkInterface.getDisplayName());
+                        multicastInterfaces.add(0, networkInterface);
+                    } else {
+                        LOGGER.debug("Adding loopback multicast interface {}", networkInterface.getDisplayName());
+                        multicastInterfaces.add(networkInterface);
+                    }
+                }
             } catch (Throwable e) {
                 throw new RuntimeException(String.format("Could not determine the IP addresses for network interface %s", networkInterface.getName()), e);
             }
         }
+
+        if (localAddresses.isEmpty()) {
+            InetAddress fallback = InetAddress.getByName(null);
+            LOGGER.debug("No loopback addresses, using fallback {}", fallback);
+            localAddresses.add(fallback);
+        }
+        if (remoteAddresses.isEmpty()) {
+            try {
+                InetAddress fallback = InetAddress.getLocalHost();
+                LOGGER.debug("No remote addresses, using fallback {}", fallback);
+                remoteAddresses.add(fallback);
+            } catch (UnknownHostException e) {
+                LOGGER.debug("Could not map local host name to remote address, using local addresses instead.");
+                remoteAddresses.addAll(localAddresses);
+            }
+        }
+        if (multicastInterfaces.isEmpty()) {
+            LOGGER.debug("No multicast interfaces, using fallbacks");
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            while (networkInterfaces.hasMoreElements()) {
+                multicastInterfaces.add(networkInterfaces.nextElement());
+            }
+        }
+
+        // Detect Openshift IP environment variable.
+        InetAddress openshiftBindAddress = findOpenshiftAddresses();
+        if (openshiftBindAddress != null) {
+            localBindingAddress = openshiftBindAddress;
+            localAddresses.add(openshiftBindAddress);
+        } else {
+            localBindingAddress = new InetSocketAddress(0).getAddress();
+        }
+    }
+
+    private Transformer<Boolean, NetworkInterface> loopback() {
+        try {
+            Method method = NetworkInterface.class.getMethod("isLoopback");
+            return new MethodBackedTransformer(method);
+        } catch (NoSuchMethodException e) {
+            return new Unknown();
+        }
+    }
+
+    private Transformer<Boolean, NetworkInterface> multicast() {
+        try {
+            Method method = NetworkInterface.class.getMethod("supportsMulticast");
+            return new MethodBackedTransformer(method);
+        } catch (NoSuchMethodException e) {
+            return new Unknown();
+        }
+
+    }
+
+    private static class Unknown implements Transformer<Boolean, NetworkInterface> {
+        public Boolean transform(NetworkInterface original) {
+            return null;
+        }
+    }
+
+    private static class MethodBackedTransformer implements Transformer<Boolean, NetworkInterface> {
+        private final Method method;
+
+        public MethodBackedTransformer(Method method) {
+            this.method = method;
+        }
+
+        public Boolean transform(NetworkInterface original) {
+            try {
+                try {
+                    return (Boolean) method.invoke(original);
+                } catch (InvocationTargetException e) {
+                    if (!(e.getCause() instanceof SocketException)) {
+                        throw e.getCause();
+                    }
+                    // Ignore - treat as if we don't know
+                    return null;
+                }
+            } catch (Throwable throwable) {
+                throw UncheckedException.throwAsUncheckedException(throwable);
+            }
+        }
     }
 }
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/MulticastConnection.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/MulticastConnection.java
index 2a55df2..805738e 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/MulticastConnection.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/MulticastConnection.java
@@ -15,31 +15,50 @@
  */
 package org.gradle.messaging.remote.internal.inet;
 
-import org.gradle.internal.UncheckedException;
 import org.gradle.messaging.remote.internal.Connection;
 import org.gradle.messaging.remote.internal.MessageIOException;
 import org.gradle.messaging.remote.internal.MessageSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.io.*;
-import java.net.DatagramPacket;
-import java.net.MulticastSocket;
-import java.net.SocketException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.*;
 
 public class MulticastConnection<T> implements Connection<T> {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MulticastConnection.class);
     private static final int MAX_MESSAGE_SIZE = 32*1024;
     private final MulticastSocket socket;
     private final SocketInetAddress address;
     private final MessageSerializer<T> serializer;
     private final SocketInetAddress localAddress;
 
-    public MulticastConnection(SocketInetAddress address, MessageSerializer<T> serializer) {
+    public MulticastConnection(SocketInetAddress address, MessageSerializer<T> serializer, InetAddressFactory addressFactory) {
         this.address = address;
         this.serializer = serializer;
         try {
             socket = new MulticastSocket(address.getPort());
-            socket.joinGroup(address.getAddress());
+            boolean bound = false;
+            SocketException bindFailure = null;
+            // Should attempt both ip4 and ip6 multicast addresses, as some interfaces don't accept ipv4
+            for (NetworkInterface networkInterface : addressFactory.findMulticastInterfaces()) {
+                try {
+                    socket.joinGroup(new InetSocketAddress(address.getAddress(), address.getPort()), networkInterface);
+                    LOGGER.debug("Joined multicast address {} on network interface {}.", address, networkInterface.getDisplayName());
+                    bound = true;
+                } catch (SocketException e) {
+                    LOGGER.debug("Failed to join multicast address {} on network interface {}.", address, networkInterface.getDisplayName());
+                    if (bindFailure == null) {
+                        bindFailure = e;
+                    }
+                }
+            }
+            if (!bound) {
+                throw bindFailure;
+            }
         } catch (IOException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
+            throw new RuntimeException(String.format("Could not create multicast socket for %s", address.getDisplayName()), e);
         }
         localAddress = new SocketInetAddress(socket.getInetAddress(), socket.getLocalPort());
     }
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnectCompletion.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnectCompletion.java
index 56e1ac1..f70a3fb 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnectCompletion.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnectCompletion.java
@@ -16,10 +16,7 @@
 
 package org.gradle.messaging.remote.internal.inet;
 
-import org.gradle.messaging.remote.internal.ConnectCompletion;
-import org.gradle.messaging.remote.internal.Connection;
-import org.gradle.messaging.remote.internal.DefaultMessageSerializer;
-import org.gradle.messaging.remote.internal.MessageSerializer;
+import org.gradle.messaging.remote.internal.*;
 
 import java.nio.channels.SocketChannel;
 
@@ -35,11 +32,11 @@ class SocketConnectCompletion implements ConnectCompletion {
         return String.format("%s to %s", socket.socket().getLocalSocketAddress(), socket.socket().getRemoteSocketAddress());
     }
 
-    public <T> Connection<T> create(ClassLoader messageClassLoader) {
+    public <T> RemoteConnection<T> create(ClassLoader messageClassLoader) {
         return new SocketConnection<T>(socket, new DefaultMessageSerializer<T>(messageClassLoader));
     }
 
-    public <T> Connection<T> create(MessageSerializer<T> serializer) {
+    public <T> RemoteConnection<T> create(MessageSerializer<T> serializer) {
         return new SocketConnection<T>(socket, serializer);
     }
 }
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnection.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnection.java
index ed15ca9..e1e1a09 100755
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnection.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/SocketConnection.java
@@ -17,14 +17,13 @@
 package org.gradle.messaging.remote.internal.inet;
 
 import com.google.common.base.Objects;
-import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.UncheckedException;
-import org.gradle.messaging.remote.Address;
-import org.gradle.messaging.remote.internal.Connection;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.messaging.remote.internal.MessageIOException;
 import org.gradle.messaging.remote.internal.MessageSerializer;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
+import org.gradle.messaging.remote.internal.RemoteConnection;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -37,7 +36,7 @@ import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
 
-public class SocketConnection<T> implements Connection<T> {
+public class SocketConnection<T> implements RemoteConnection<T> {
     private final SocketChannel socket;
     private final SocketInetAddress localAddress;
     private final SocketInetAddress remoteAddress;
@@ -67,18 +66,10 @@ public class SocketConnection<T> implements Connection<T> {
 
     @Override
     public String toString() {
-        return String.format("socket connection at %s with %s", localAddress, remoteAddress);
-    }
-
-    public Address getLocalAddress() {
-        return localAddress;
-    }
-
-    public Address getRemoteAddress() {
-        return remoteAddress;
+        return String.format("socket connection from %s to %s", localAddress, remoteAddress);
     }
 
-    public T receive() {
+    public T receive() throws MessageIOException {
         try {
             return objectReader.read();
         } catch (Exception e) {
@@ -107,7 +98,7 @@ public class SocketConnection<T> implements Connection<T> {
         return false;
     }
 
-    public void dispatch(T message) {
+    public void dispatch(T message) throws MessageIOException {
         try {
             objectWriter.write(message);
             outstr.flush();
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/TcpIncomingConnector.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/TcpIncomingConnector.java
index 5e0c4cd..1d9dab7 100755
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/TcpIncomingConnector.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/inet/TcpIncomingConnector.java
@@ -52,7 +52,7 @@ public class TcpIncomingConnector implements IncomingConnector {
         int localPort;
         try {
             serverSocket = ServerSocketChannel.open();
-            serverSocket.socket().bind(new InetSocketAddress(0));
+            serverSocket.socket().bind(new InetSocketAddress(addressFactory.findLocalBindingAddress(), 0));
             localPort = serverSocket.socket().getLocalPort();
         } catch (Exception e) {
             throw UncheckedException.throwAsUncheckedException(e);
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/protocol/DiscoveryProtocolSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/protocol/DiscoveryProtocolSerializer.java
index bc17f4a..4b48d7f 100644
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/protocol/DiscoveryProtocolSerializer.java
+++ b/subprojects/messaging/src/main/java/org/gradle/messaging/remote/internal/protocol/DiscoveryProtocolSerializer.java
@@ -22,8 +22,8 @@ import org.gradle.messaging.remote.internal.MessageOriginator;
 import org.gradle.messaging.remote.internal.MessageSerializer;
 import org.gradle.messaging.remote.internal.inet.InetEndpoint;
 import org.gradle.messaging.remote.internal.inet.MultiChoiceAddress;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
+import org.gradle.internal.serialize.ObjectReader;
+import org.gradle.internal.serialize.ObjectWriter;
 
 import java.io.*;
 import java.net.InetAddress;
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractCollectionSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractCollectionSerializer.java
deleted file mode 100644
index 6ad1c56..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractCollectionSerializer.java
+++ /dev/null
@@ -1,40 +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.messaging.serialize;
-
-import java.util.Collection;
-
-public class AbstractCollectionSerializer<T> {
-    protected final Serializer<T> entrySerializer;
-
-    public AbstractCollectionSerializer(Serializer<T> entrySerializer) {
-        this.entrySerializer = entrySerializer;
-    }
-
-    protected void readValues(Decoder decoder, Collection<T> values) throws Exception {
-        int size = decoder.readInt();
-        for (int i = 0; i < size; i++) {
-            values.add(entrySerializer.read(decoder));
-        }
-    }
-
-    protected void writeValues(Encoder encoder, Collection<T> value) throws Exception {
-        encoder.writeInt(value.size());
-        for (T t : value) {
-            entrySerializer.write(encoder, t);
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractDecoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractDecoder.java
deleted file mode 100644
index 8a32b56..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractDecoder.java
+++ /dev/null
@@ -1,105 +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.messaging.serialize;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-public abstract class AbstractDecoder implements Decoder {
-    private DecoderStream stream;
-
-    public InputStream getInputStream() {
-        if (stream == null) {
-            stream = new DecoderStream();
-        }
-        return stream;
-    }
-
-    public void readBytes(byte[] buffer) throws IOException {
-        readBytes(buffer, 0, buffer.length);
-    }
-
-    public byte[] readBinary() throws EOFException, IOException {
-        int size = readSmallInt();
-        byte[] result = new byte[size];
-        readBytes(result);
-        return result;
-    }
-
-    public int readSmallInt() throws EOFException, IOException {
-        return readInt();
-    }
-
-    public long readSmallLong() throws EOFException, IOException {
-        return readLong();
-    }
-
-    public String readNullableString() throws EOFException, IOException {
-        if (readBoolean()) {
-            return readString();
-        } else {
-            return null;
-        }
-    }
-
-    public void skipBytes(long count) throws EOFException, IOException {
-        long remaining = count;
-        while (remaining > 0) {
-            long skipped = maybeSkip(remaining);
-            if (skipped <= 0) {
-                break;
-            }
-            remaining -= skipped;
-        }
-        if (remaining > 0) {
-            throw new EOFException();
-        }
-    }
-
-    protected abstract int maybeReadBytes(byte[] buffer, int offset, int count) throws IOException;
-
-    protected abstract long maybeSkip(long count) throws IOException;
-
-    private class DecoderStream extends InputStream {
-        byte[] buffer = new byte[1];
-
-        @Override
-        public long skip(long n) throws IOException {
-            return maybeSkip(n);
-        }
-
-        @Override
-        public int read() throws IOException {
-            int read = maybeReadBytes(buffer, 0, 1);
-            if (read <= 0) {
-                return read;
-            }
-            return buffer[0] & 0xff;
-        }
-
-        @Override
-        public int read(byte[] buffer) throws IOException {
-            return maybeReadBytes(buffer, 0, buffer.length);
-        }
-
-        @Override
-        public int read(byte[] buffer, int offset, int count) throws IOException {
-            return maybeReadBytes(buffer, offset, count);
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractEncoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractEncoder.java
deleted file mode 100644
index 9e1b92c..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/AbstractEncoder.java
+++ /dev/null
@@ -1,80 +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.messaging.serialize;
-
-import org.gradle.api.Nullable;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-public abstract class AbstractEncoder implements Encoder {
-    private EncoderStream stream;
-
-    public OutputStream getOutputStream() {
-        if (stream == null) {
-            stream = new EncoderStream();
-        }
-        return stream;
-    }
-
-    public void writeBytes(byte[] bytes) throws IOException {
-        writeBytes(bytes, 0, bytes.length);
-    }
-
-    public void writeBinary(byte[] bytes) throws IOException {
-        writeBinary(bytes, 0, bytes.length);
-    }
-
-    public void writeBinary(byte[] bytes, int offset, int count) throws IOException {
-        writeSmallInt(count);
-        writeBytes(bytes, offset, count);
-    }
-
-    public void writeSmallInt(int value) throws IOException {
-        writeInt(value);
-    }
-
-    public void writeSmallLong(long value) throws IOException {
-        writeLong(value);
-    }
-
-    public void writeNullableString(@Nullable CharSequence value) throws IOException {
-        if (value == null) {
-            writeBoolean(false);
-        } else {
-            writeBoolean(true);
-            writeString(value.toString());
-        }
-    }
-
-    private class EncoderStream extends OutputStream {
-        @Override
-        public void write(byte[] buffer) throws IOException {
-            writeBytes(buffer);
-        }
-
-        @Override
-        public void write(byte[] buffer, int offset, int length) throws IOException {
-            writeBytes(buffer, offset, length);
-        }
-
-        @Override
-        public void write(int b) throws IOException {
-            writeByte((byte)b);
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/BaseSerializerFactory.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/BaseSerializerFactory.java
deleted file mode 100644
index 1ee5d01..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/BaseSerializerFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.messaging.serialize;
-
-import java.io.File;
-
-public class BaseSerializerFactory {
-    public static final Serializer<String> STRING_SERIALIZER = new StringSerializer();
-    public static final Serializer LONG_SERIALIZER = new LongSerializer();
-    public static final Serializer FILE_SERIALIZER = new FileSerializer();
-
-    public <T> Serializer<T> getSerializerFor(Class<T> type) {
-        if (type.equals(String.class)) {
-            @SuppressWarnings("unchecked")
-            Serializer<T> stringSerializer = (Serializer<T>) STRING_SERIALIZER;
-            return stringSerializer;
-        }
-        if (type.equals(Long.class)) {
-            return LONG_SERIALIZER;
-        }
-        if (type.equals(File.class)) {
-            return FILE_SERIALIZER;
-        }
-        return new DefaultSerializer<T>(type.getClassLoader());
-    }
-
-    private static class LongSerializer implements Serializer<Long> {
-        public Long read(Decoder decoder) throws Exception {
-            return decoder.readLong();
-        }
-
-        public void write(Encoder encoder, Long value) throws Exception {
-            encoder.writeLong(value);
-        }
-    }
-
-    private static class StringSerializer implements Serializer<String> {
-        public String read(Decoder decoder) throws Exception {
-            return decoder.readString();
-        }
-
-        public void write(Encoder encoder, String value) throws Exception {
-            encoder.writeString(value);
-        }
-    }
-
-    private static class FileSerializer implements Serializer<File> {
-        public File read(Decoder decoder) throws Exception {
-            return new File(decoder.readString());
-        }
-
-        public void write(Encoder encoder, File value) throws Exception {
-            encoder.writeString(value.getPath());
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Decoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Decoder.java
deleted file mode 100644
index 1233a66..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Decoder.java
+++ /dev/null
@@ -1,118 +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.messaging.serialize;
-
-import org.gradle.api.Nullable;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Provides a way to decode structured data from a backing byte stream.
- */
-public interface Decoder {
-    /**
-     * Returns an InputStream which can be used to read raw bytes.
-     */
-    InputStream getInputStream();
-
-    /**
-     * Reads a signed 64 bit long value. Can read any value that was written using {@link Encoder#writeLong(long)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the long value can be fully read.
-     */
-    long readLong() throws EOFException, IOException;
-
-    /**
-     * Reads a signed 64 bit int value. Can read any value that was written using {@link Encoder#writeSmallLong(int)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
-     */
-    long readSmallLong() throws EOFException, IOException;
-
-    /**
-     * Reads a signed 32 bit int value. Can read any value that was written using {@link Encoder#writeInt(int)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
-     */
-    int readInt() throws EOFException, IOException;
-
-    /**
-     * Reads a signed 32 bit int value. Can read any value that was written using {@link Encoder#writeSmallInt(int)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the int value can be fully read.
-     */
-    int readSmallInt() throws EOFException, IOException;
-
-    /**
-     * Reads a boolean value. Can read any value that was written using {@link Encoder#writeBoolean(boolean)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the boolean value can be fully read.
-     */
-    boolean readBoolean() throws EOFException, IOException;
-
-    /**
-     * Reads a non-null string value. Can read any value that was written using {@link Encoder#writeString(CharSequence)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the string can be fully read.
-     */
-    String readString() throws EOFException, IOException;
-
-    /**
-     * Reads a nullable string value. Can reads any value that was written using {@link Encoder#writeNullableString(CharSequence)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the string can be fully read.
-     */
-    @Nullable
-    String readNullableString() throws EOFException, IOException;
-
-    /**
-     * Reads a byte value. Can read any byte value that was written using one of the raw byte methods on {@link Encoder}, such as {@link Encoder#writeByte(byte)} or {@link Encoder#getOutputStream()}
-     *
-     * @throws EOFException when the end of the byte stream is reached.
-     */
-    byte readByte() throws EOFException, IOException;
-
-    /**
-     * Reads bytes into the given buffer, filling the buffer. Can read any byte values that were written using one of the raw byte methods on {@link Encoder}, such as {@link
-     * Encoder#writeBytes(byte[])} or {@link Encoder#getOutputStream()}
-     *
-     * @throws EOFException when the end of the byte stream is reached before the buffer is full.
-     */
-    void readBytes(byte[] buffer) throws EOFException, IOException;
-
-    /**
-     * Reads the specified number of bytes into the given buffer. Can read any byte values that were written using one of the raw byte methods on {@link Encoder}, such as {@link
-     * Encoder#writeBytes(byte[])} or {@link Encoder#getOutputStream()}
-     *
-     * @throws EOFException when the end of the byte stream is reached before the specified number of bytes were read.
-     */
-    void readBytes(byte[] buffer, int offset, int count) throws EOFException, IOException;
-
-    /**
-     * Reads a byte array. Can read any byte array written using {@link Encoder#writeBinary(byte[])} or {@link Encoder#writeBinary(byte[], int, int)}.
-     *
-     * @throws EOFException when the end of the byte stream is reached before the byte array was fully read.
-     */
-    byte[] readBinary() throws EOFException, IOException;
-
-    /**
-     * Skips the given number of bytes. Can skip over any byte values that were written using one of the raw byte methods on {@link Encoder}.
-     */
-    void skipBytes(long count) throws EOFException, IOException;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializer.java
deleted file mode 100644
index 2a3c7ef..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.messaging.serialize;
-
-import org.gradle.internal.io.ClassLoaderObjectInputStream;
-
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.StreamCorruptedException;
-
-public class DefaultSerializer<T> implements Serializer<T> {
-    private ClassLoader classLoader;
-
-    public DefaultSerializer() {
-        classLoader = getClass().getClassLoader();
-    }
-
-    public DefaultSerializer(ClassLoader classLoader) {
-        this.classLoader = classLoader != null ? classLoader : getClass().getClassLoader();
-    }
-
-    public ClassLoader getClassLoader() {
-        return classLoader;
-    }
-
-    public void setClassLoader(ClassLoader classLoader) {
-        this.classLoader = classLoader;
-    }
-
-    public T read(Decoder decoder) throws Exception {
-        try {
-            return (T) new ClassLoaderObjectInputStream(decoder.getInputStream(), classLoader).readObject();
-        } catch (StreamCorruptedException e) {
-            return null;
-        }
-    }
-
-    public void write(Encoder encoder, T value) throws IOException {
-        ObjectOutputStream objectStr = new ObjectOutputStream(encoder.getOutputStream());
-        objectStr.writeObject(value);
-        objectStr.flush();
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializerRegistry.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializerRegistry.java
deleted file mode 100644
index 8b9be4d..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/DefaultSerializerRegistry.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.messaging.serialize;
-
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class DefaultSerializerRegistry<T> implements SerializerRegistry<T> {
-    private final Map<Class<?>, Serializer<?>> serializerMap = new TreeMap<Class<?>, Serializer<?>>(new Comparator<Class<?>>() {
-        public int compare(Class<?> o1, Class<?> o2) {
-            return o1.getName().compareTo(o2.getName());
-        }
-    });
-
-    public <U extends T> void register(Class<U> implementationType, Serializer<U> serializer) {
-        serializerMap.put(implementationType, serializer);
-    }
-
-    public Serializer<T> build() {
-        if (serializerMap.size() == 1) {
-            return (Serializer<T>) serializerMap.values().iterator().next();
-        }
-        TaggedTypeSerializer<T> serializer = new TaggedTypeSerializer<T>();
-        for (Map.Entry<Class<?>, Serializer<?>> entry : serializerMap.entrySet()) {
-            serializer.add(entry.getKey(), entry.getValue());
-        }
-        return serializer;
-    }
-
-    private static class TypeInfo {
-        final byte tag;
-        final Serializer serializer;
-
-        private TypeInfo(byte tag, Serializer serializer) {
-            this.tag = tag;
-            this.serializer = serializer;
-        }
-    }
-
-    private static class TaggedTypeSerializer<T> implements Serializer<T> {
-        private final Map<Class<?>, TypeInfo> serializersByType = new HashMap<Class<?>, TypeInfo>();
-        private final Map<Byte, TypeInfo> serializersByTag = new HashMap<Byte, TypeInfo>();
-
-        private <T> void add(Class<?> type, Serializer<?> serializer) {
-            TypeInfo typeInfo = new TypeInfo((byte) serializersByTag.size(), serializer);
-            serializersByType.put(type, typeInfo);
-            serializersByTag.put(typeInfo.tag, typeInfo);
-        }
-
-        public T read(Decoder decoder) throws Exception {
-            byte tag = decoder.readByte();
-            TypeInfo typeInfo = serializersByTag.get(tag);
-            if (typeInfo == null) {
-                throw new IllegalArgumentException(String.format("Unexpected type tag %d found.", tag));
-            }
-            return (T) typeInfo.serializer.read(decoder);
-        }
-
-        public void write(Encoder encoder, T value) throws Exception {
-            Class<?> targetType = value instanceof Throwable ? Throwable.class : value.getClass();
-            TypeInfo typeInfo = serializersByType.get(targetType);
-            if (typeInfo == null) {
-                throw new IllegalArgumentException(String.format("Don't know how to serialize an object of type %s.", value.getClass().getName()));
-            }
-            encoder.writeByte(typeInfo.tag);
-            typeInfo.serializer.write(encoder, value);
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Encoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Encoder.java
deleted file mode 100644
index c1b753e..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Encoder.java
+++ /dev/null
@@ -1,91 +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.messaging.serialize;
-
-import org.gradle.api.Nullable;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-public interface Encoder {
-    /**
-     * Returns an OutputStream that can be used to write byte content to the stream.
-     */
-    OutputStream getOutputStream();
-
-    /**
-     * Writes a raw byte value to the stream.
-     */
-    void writeByte(byte value) throws IOException;
-
-    /**
-     * Writes the given raw bytes to the stream. Does not encode any length information.
-     */
-    void writeBytes(byte[] bytes) throws IOException;
-
-    /**
-     * Writes the given raw bytes to the stream. Does not encode any length information.
-     */
-    void writeBytes(byte[] bytes, int offset, int count) throws IOException;
-
-    /**
-     * Writes the given byte array to the stream. Encodes the bytes and length information.
-     */
-    void writeBinary(byte[] bytes) throws IOException;
-
-    /**
-     * Writes the given byte array to the stream. Encodes the bytes and length information.
-     */
-    void writeBinary(byte[] bytes, int offset, int count) throws IOException;
-
-    /**
-     * Writes a signed 64 bit long value. The implementation may encode the value as a variable number of bytes, not necessarily as 8 bytes.
-     */
-    void writeLong(long value) throws IOException;
-
-    /**
-     * Writes a signed 64 bit long value whose value is likely to be small and positive but may not be. The implementation may encode the value in a way that is more efficient for small positive
-     * values.
-     */
-    void writeSmallLong(long value) throws IOException;
-
-    /**
-     * Writes a signed 32 bit int value. The implementation may encode the value as a variable number of bytes, not necessarily as 4 bytes.
-     */
-    void writeInt(int value) throws IOException;
-
-    /**
-     * Writes a signed 32 bit int value whose value is likely to be small and positive but may not be. The implementation may encode the value in a way that
-     * is more efficient for small positive values.
-     */
-    void writeSmallInt(int value) throws IOException;
-
-    /**
-     * Writes a boolean value.
-     */
-    void writeBoolean(boolean value) throws IOException;
-
-    /**
-     * Writes a non-null string value.
-     */
-    void writeString(CharSequence value) throws IOException;
-
-    /**
-     * Writes a nullable string value.
-     */
-    void writeNullableString(@Nullable CharSequence value) throws IOException;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/FlushableEncoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/FlushableEncoder.java
deleted file mode 100644
index ce0e7a7..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/FlushableEncoder.java
+++ /dev/null
@@ -1,24 +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.messaging.serialize;
-
-import java.io.Flushable;
-import java.io.IOException;
-
-public interface FlushableEncoder extends Encoder, Flushable {
-    void flush() throws IOException;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/InputStreamBackedDecoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/InputStreamBackedDecoder.java
deleted file mode 100644
index 8e762a9..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/InputStreamBackedDecoder.java
+++ /dev/null
@@ -1,65 +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.messaging.serialize;
-
-import java.io.*;
-
-public class InputStreamBackedDecoder extends AbstractDecoder implements Decoder, Closeable {
-    private final DataInputStream inputStream;
-
-    public InputStreamBackedDecoder(InputStream inputStream) {
-        this.inputStream = new DataInputStream(inputStream);
-    }
-
-    @Override
-    protected int maybeReadBytes(byte[] buffer, int offset, int count) throws IOException {
-        return inputStream.read(buffer, offset, count);
-    }
-
-    @Override
-    protected long maybeSkip(long count) throws IOException {
-        return inputStream.skip(count);
-    }
-
-    public long readLong() throws IOException {
-        return inputStream.readLong();
-    }
-
-    public int readInt() throws EOFException, IOException {
-        return inputStream.readInt();
-    }
-
-    public boolean readBoolean() throws EOFException, IOException {
-        return inputStream.readBoolean();
-    }
-
-    public String readString() throws EOFException, IOException {
-        return inputStream.readUTF();
-    }
-
-    public byte readByte() throws IOException {
-        return (byte)(inputStream.readByte() & 0xff);
-    }
-
-    public void readBytes(byte[] buffer, int offset, int count) throws IOException {
-        inputStream.readFully(buffer, offset, count);
-    }
-
-    public void close() throws IOException {
-        inputStream.close();
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ListSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ListSerializer.java
deleted file mode 100644
index f9f3c83..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ListSerializer.java
+++ /dev/null
@@ -1,37 +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.messaging.serialize;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ListSerializer<T> extends AbstractCollectionSerializer<T> implements Serializer<List<T>> {
-
-    public ListSerializer(Serializer<T> entrySerializer) {
-        super(entrySerializer);
-    }
-
-    public List<T> read(Decoder decoder) throws Exception {
-        List<T> values = new ArrayList<T>();
-        readValues(decoder, values);
-        return values;
-    }
-
-    public void write(Encoder encoder, List<T> value) throws Exception {
-        writeValues(encoder, value);
-    }
-
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/LongSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/LongSerializer.java
deleted file mode 100644
index 48a8a1d..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/LongSerializer.java
+++ /dev/null
@@ -1,29 +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.messaging.serialize;
-
-public class LongSerializer implements Serializer<Long> {
-    public Long read(Decoder decoder) throws Exception {
-        return decoder.readLong();
-    }
-
-    public void write(Encoder encoder, Long value) throws Exception {
-        if (value == null) {
-            throw new IllegalArgumentException("This serializer does not serialize null values.");
-        }
-        encoder.writeLong(value);
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/MapSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/MapSerializer.java
deleted file mode 100644
index 6764ab2..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/MapSerializer.java
+++ /dev/null
@@ -1,48 +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.messaging.serialize;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class MapSerializer<U, V> implements Serializer<Map<U, V>> {
-    private final Serializer<U> keySerializer;
-    private final Serializer<V> valueSerializer;
-
-    public MapSerializer(Serializer<U> keySerializer, Serializer<V> valueSerializer) {
-        this.keySerializer = keySerializer;
-        this.valueSerializer = valueSerializer;
-    }
-
-    public Map<U, V> read(Decoder decoder) throws Exception {
-        int size = decoder.readInt();
-        Map<U, V> valueMap = new LinkedHashMap<U, V>(size);
-        for (int i = 0; i < size; i++) {
-            U key = keySerializer.read(decoder);
-            V value = valueSerializer.read(decoder);
-            valueMap.put(key, value);
-        }
-        return valueMap;
-    }
-
-    public void write(Encoder encoder, Map<U, V> value) throws Exception {
-        encoder.writeInt(value.size());
-        for (Map.Entry<U, V> entry : value.entrySet()) {
-            keySerializer.write(encoder, entry.getKey());
-            valueSerializer.write(encoder, entry.getValue());
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/NullSafeStringSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/NullSafeStringSerializer.java
deleted file mode 100644
index a81747a..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/NullSafeStringSerializer.java
+++ /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.messaging.serialize;
-
-public class NullSafeStringSerializer implements Serializer<String> {
-    public String read(Decoder decoder) throws Exception {
-        return decoder.readNullableString();
-    }
-
-    public void write(Encoder encoder, String value) throws Exception {
-        encoder.writeNullableString(value);
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectReader.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectReader.java
deleted file mode 100644
index 58e7927..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectReader.java
+++ /dev/null
@@ -1,21 +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.messaging.serialize;
-
-public interface ObjectReader<T> {
-    T read() throws Exception;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectWriter.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectWriter.java
deleted file mode 100644
index 341cde8..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/ObjectWriter.java
+++ /dev/null
@@ -1,21 +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.messaging.serialize;
-
-public interface ObjectWriter<T> {
-    void write(T value) throws Exception;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/OutputStreamBackedEncoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/OutputStreamBackedEncoder.java
deleted file mode 100644
index a70cbba..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/OutputStreamBackedEncoder.java
+++ /dev/null
@@ -1,65 +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.messaging.serialize;
-
-import java.io.Closeable;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class OutputStreamBackedEncoder extends AbstractEncoder implements Closeable, FlushableEncoder {
-    private final DataOutputStream outputStream;
-
-    public OutputStreamBackedEncoder(OutputStream outputStream) {
-        this.outputStream = new DataOutputStream(outputStream);
-    }
-
-    public void writeLong(long value) throws IOException {
-        outputStream.writeLong(value);
-    }
-
-    public void writeInt(int value) throws IOException {
-        outputStream.writeInt(value);
-    }
-
-    public void writeBoolean(boolean value) throws IOException {
-        outputStream.writeBoolean(value);
-    }
-
-    public void writeString(CharSequence value) throws IOException {
-        if (value == null) {
-            throw new IllegalArgumentException("Cannot encode a null string.");
-        }
-        outputStream.writeUTF(value.toString());
-    }
-
-    public void writeByte(byte value) throws IOException {
-        outputStream.writeByte(value);
-    }
-
-    public void writeBytes(byte[] bytes, int offset, int count) throws IOException {
-        outputStream.write(bytes, offset, count);
-    }
-
-    public void flush() throws IOException {
-        outputStream.flush();
-    }
-
-    public void close() throws IOException {
-        outputStream.close();
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Serializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Serializer.java
deleted file mode 100644
index 9dbf770..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/Serializer.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.messaging.serialize;
-
-public interface Serializer<T> {
-    /**
-     * Reads the next object from the given stream. The implementation must not perform any buffering, so that it reads only those bytes from the input stream that are
-     * required to deserialize the next object.
-     */
-    T read(Decoder decoder) throws Exception;
-
-    /**
-     * Writes the given object to the given stream. The implementation must not perform any buffering.
-     */
-    void write(Encoder encoder, T value) throws Exception;
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SerializerRegistry.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SerializerRegistry.java
deleted file mode 100644
index 1d8c05c..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SerializerRegistry.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.messaging.serialize;
-
-public interface SerializerRegistry<T> {
-    <U extends T> void register(Class<U> implementationType, Serializer<U> serializer);
-
-    Serializer<T> build();
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SetSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SetSerializer.java
deleted file mode 100644
index e7c1b74..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/SetSerializer.java
+++ /dev/null
@@ -1,38 +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.messaging.serialize;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class SetSerializer<T> extends AbstractCollectionSerializer<T> implements Serializer<Set<T>> {
-
-    public SetSerializer(Serializer<T> entrySerializer) {
-        super(entrySerializer);
-    }
-
-    public Set<T> read(Decoder decoder) throws Exception {
-        Set<T> values = new LinkedHashSet<T>();
-        readValues(decoder, values);
-        return values;
-    }
-
-    public void write(Encoder encoder, Set<T> value) throws Exception {
-        writeValues(encoder, value);
-    }
-
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/JavaSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/JavaSerializer.java
deleted file mode 100644
index cdbae50..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/JavaSerializer.java
+++ /dev/null
@@ -1,65 +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.messaging.serialize.kryo;
-
-import org.gradle.messaging.remote.internal.Message;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
-
-public class JavaSerializer<T> implements StatefulSerializer<T> {
-    private final ClassLoader classLoader;
-
-    public JavaSerializer(ClassLoader classLoader) {
-        this.classLoader = classLoader;
-    }
-
-    public ObjectReader<T> newReader(Decoder decoder) {
-        return new JavaReader<T>(decoder, classLoader);
-    }
-
-    public ObjectWriter<T> newWriter(Encoder encoder) {
-        return new JavaWriter<T>(encoder);
-    }
-
-    private static class JavaReader<T> implements ObjectReader<T> {
-        private final Decoder decoder;
-        private final ClassLoader classLoader;
-
-        private JavaReader(Decoder decoder, ClassLoader classLoader) {
-            this.decoder = decoder;
-            this.classLoader = classLoader;
-        }
-
-        public T read() throws Exception {
-            return (T) Message.receive(decoder.getInputStream(), classLoader);
-        }
-    }
-
-    private class JavaWriter<T> implements ObjectWriter<T> {
-        private final Encoder encoder;
-
-        public JavaWriter(Encoder encoder) {
-            this.encoder = encoder;
-        }
-
-        public void write(T value) throws Exception {
-            Message.send(value, encoder.getOutputStream());
-        }
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedDecoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedDecoder.java
deleted file mode 100644
index 2888262..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedDecoder.java
+++ /dev/null
@@ -1,156 +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.messaging.serialize.kryo;
-
-import com.esotericsoftware.kryo.KryoException;
-import com.esotericsoftware.kryo.io.Input;
-import org.gradle.messaging.serialize.AbstractDecoder;
-import org.gradle.messaging.serialize.Decoder;
-
-import java.io.Closeable;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Note that this decoder uses buffering, so will attempt to read beyond the end of the encoded data. This means you should use this type only when this decoder will be used to decode the entire
- * stream.
- */
-public class KryoBackedDecoder extends AbstractDecoder implements Decoder, Closeable {
-    private final Input input;
-    private final InputStream inputStream;
-    private long extraSkipped;
-
-    public KryoBackedDecoder(InputStream inputStream) {
-        this(inputStream, 4096);
-    }
-
-    public KryoBackedDecoder(InputStream inputStream, int bufferSize) {
-        this.inputStream = inputStream;
-        input = new Input(this.inputStream, bufferSize);
-    }
-
-    @Override
-    protected int maybeReadBytes(byte[] buffer, int offset, int count) {
-        return input.read(buffer, offset, count);
-    }
-
-    @Override
-    protected long maybeSkip(long count) throws IOException {
-        // Work around some bugs in Input.skip()
-        int remaining = input.limit() - input.position();
-        if (remaining == 0) {
-            long skipped = inputStream.skip(count);
-            if (skipped > 0) {
-                extraSkipped += skipped;
-            }
-            return skipped;
-        } else if (count <= remaining) {
-            input.setPosition(input.position() + (int) count);
-            return count;
-        } else {
-            input.setPosition(input.limit());
-            return remaining;
-        }
-    }
-
-    private RuntimeException maybeEndOfStream(KryoException e) throws EOFException {
-        if (e.getMessage().equals("Buffer underflow.")) {
-            throw (EOFException) (new EOFException().initCause(e));
-        }
-        throw e;
-    }
-
-    public byte readByte() throws EOFException {
-        try {
-            return input.readByte();
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public void readBytes(byte[] buffer, int offset, int count) throws EOFException {
-        try {
-            input.readBytes(buffer, offset, count);
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public long readLong() throws EOFException {
-        try {
-            return input.readLong();
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public long readSmallLong() throws EOFException, IOException {
-        try {
-            return input.readLong(true);
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public int readInt() throws EOFException {
-        try {
-            return input.readInt();
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public int readSmallInt() throws EOFException {
-        try {
-            return input.readInt(true);
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public boolean readBoolean() throws EOFException {
-        try {
-            return input.readBoolean();
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    public String readString() throws EOFException {
-        return readNullableString();
-    }
-
-    public String readNullableString() throws EOFException {
-        try {
-            return input.readString();
-        } catch (KryoException e) {
-            throw maybeEndOfStream(e);
-        }
-    }
-
-    /**
-     * Returns the total number of bytes consumed by this decoder. Some additional bytes may also be buffered by this decoder but have not been consumed.
-     */
-    public long getReadPosition() {
-        return input.total() + extraSkipped;
-    }
-
-    public void close() throws IOException {
-        input.close();
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedEncoder.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedEncoder.java
deleted file mode 100644
index 86c4c46..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/KryoBackedEncoder.java
+++ /dev/null
@@ -1,91 +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.messaging.serialize.kryo;
-
-import com.esotericsoftware.kryo.io.Output;
-import org.gradle.api.Nullable;
-import org.gradle.messaging.serialize.AbstractEncoder;
-import org.gradle.messaging.serialize.FlushableEncoder;
-
-import java.io.Closeable;
-import java.io.OutputStream;
-
-public class KryoBackedEncoder extends AbstractEncoder implements FlushableEncoder, Closeable {
-    private final Output output;
-
-    public KryoBackedEncoder(OutputStream outputStream) {
-        this(outputStream, 4096);
-    }
-
-    public KryoBackedEncoder(OutputStream outputStream, int bufferSize) {
-        output = new Output(outputStream, bufferSize);
-    }
-
-    public void writeByte(byte value) {
-        output.writeByte(value);
-    }
-
-    public void writeBytes(byte[] bytes, int offset, int count) {
-        output.writeBytes(bytes, offset, count);
-    }
-
-    public void writeLong(long value) {
-        output.writeLong(value);
-    }
-
-    public void writeSmallLong(long value) {
-        output.writeLong(value, true);
-    }
-
-    public void writeInt(int value) {
-        output.writeInt(value);
-    }
-
-    public void writeSmallInt(int value) {
-        output.writeInt(value, true);
-    }
-
-    public void writeBoolean(boolean value) {
-        output.writeBoolean(value);
-    }
-
-    public void writeString(CharSequence value) {
-        if (value == null) {
-            throw new IllegalArgumentException("Cannot encode a null string.");
-        }
-        output.writeString(value);
-    }
-
-    public void writeNullableString(@Nullable CharSequence value) {
-        output.writeString(value);
-    }
-
-    /**
-     * Returns the total number of bytes written by this encoder, some of which is may still be buffered.
-     */
-    public int getWritePosition() {
-        return output.total();
-    }
-
-    public void flush() {
-        output.flush();
-    }
-
-    public void close() {
-        output.close();
-    }
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/StatefulSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/StatefulSerializer.java
deleted file mode 100644
index 308fa67..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/StatefulSerializer.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.messaging.serialize.kryo;
-
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
-
-public interface StatefulSerializer<T> {
-    ObjectReader<T> newReader(Decoder decoder);
-
-    ObjectWriter<T> newWriter(Encoder encoder);
-}
diff --git a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/TypeSafeSerializer.java b/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/TypeSafeSerializer.java
deleted file mode 100644
index efe179c..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/messaging/serialize/kryo/TypeSafeSerializer.java
+++ /dev/null
@@ -1,50 +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.messaging.serialize.kryo;
-
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.ObjectReader;
-import org.gradle.messaging.serialize.ObjectWriter;
-
-public class TypeSafeSerializer<T> implements StatefulSerializer<Object> {
-    private final Class<T> type;
-    private final StatefulSerializer<T> serializer;
-
-    public TypeSafeSerializer(Class<T> type, StatefulSerializer<T> serializer) {
-        this.type = type;
-        this.serializer = serializer;
-    }
-
-    public ObjectReader<Object> newReader(Decoder decoder) {
-        final ObjectReader<T> reader = serializer.newReader(decoder);
-        return new ObjectReader<Object>() {
-            public Object read() throws Exception {
-                return reader.read();
-            }
-        };
-    }
-
-    public ObjectWriter<Object> newWriter(Encoder encoder) {
-        final ObjectWriter<T> writer = serializer.newWriter(encoder);
-        return new ObjectWriter<Object>() {
-            public void write(Object value) throws Exception {
-                writer.write(type.cast(value));
-            }
-        };
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/event/DefaultListenerManagerTest.java b/subprojects/messaging/src/test/groovy/org/gradle/internal/event/DefaultListenerManagerTest.java
new file mode 100644
index 0000000..97a618e
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/event/DefaultListenerManagerTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.event;
+
+import org.jmock.Expectations;
+import org.jmock.Sequence;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertSame;
+
+ at RunWith(JMock.class)
+public class DefaultListenerManagerTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final ListenerManager manager = new DefaultListenerManager();
+
+    private final TestFooListener fooListener1 = context.mock(TestFooListener.class, "foo listener 1");
+    private final TestFooListener fooListener2 = context.mock(TestFooListener.class, "foo listener 2");
+    private final TestFooListener fooListener3 = context.mock(TestFooListener.class, "foo listener 3");
+    private final TestFooListener fooListener4 = context.mock(TestFooListener.class, "foo listener 4");
+    private final TestBarListener barListener1 = context.mock(TestBarListener.class, "bar listener 1");
+
+    @Test
+    public void canAddListenerBeforeObtainingBroadcaster() {
+        manager.addListener(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        manager.getBroadcaster(TestFooListener.class).foo("param");
+    }
+
+    @Test
+    public void canAddListenerAfterObtainingBroadcaster() {
+        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
+
+        manager.addListener(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        broadcaster.foo("param");
+    }
+
+    @Test
+    public void canAddLoggerBeforeObtainingBroadcaster() {
+        manager.useLogger(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        manager.getBroadcaster(TestFooListener.class).foo("param");
+    }
+
+    @Test
+    public void canAddLoggerAfterObtainingBroadcaster() {
+        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
+
+        manager.useLogger(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        broadcaster.foo("param");
+    }
+
+    @Test
+    public void addedListenersGetMessagesInOrderAdded() {
+        context.checking(new Expectations() {{
+            Sequence sequence = context.sequence("sequence");
+            one(fooListener1).foo("param"); inSequence(sequence);
+            one(fooListener2).foo("param"); inSequence(sequence);
+            one(fooListener3).foo("param"); inSequence(sequence);
+            one(fooListener4).foo("param"); inSequence(sequence);
+        }});
+
+        manager.addListener(fooListener1);
+        manager.addListener(barListener1);
+        manager.addListener(fooListener2);
+        manager.addListener(fooListener3);
+
+        // get the broadcaster and then add more listeners (because broadcasters
+        // are cached and so must be maintained correctly after getting defined
+        TestFooListener broadcaster = manager.getBroadcaster(TestFooListener.class);
+
+        manager.addListener(fooListener4);
+
+        broadcaster.foo("param");
+    }
+
+    @Test
+    public void cachesBroadcasters() {
+        assertSame(manager.getBroadcaster(TestFooListener.class), manager.getBroadcaster(TestFooListener.class));
+    }
+
+    @Test
+    public void removedListenersDontGetMessages() {
+        manager.addListener(fooListener1);
+        manager.addListener(fooListener2);
+
+        manager.removeListener(fooListener2);
+
+        TestFooListener testFooListener = manager.getBroadcaster(TestFooListener.class);
+
+        manager.removeListener(fooListener1);
+
+        testFooListener.foo("param");
+    }
+
+    @Test
+    public void replacedLoggersDontGetMessages() {
+        context.checking(new Expectations() {{
+            one(fooListener4).foo("param");
+        }});
+
+        manager.useLogger(fooListener1);
+        manager.useLogger(fooListener2);
+
+        TestFooListener testFooListener = manager.getBroadcaster(TestFooListener.class);
+
+        manager.useLogger(fooListener3);
+        manager.useLogger(fooListener4);
+
+        testFooListener.foo("param");
+    }
+
+    @Test
+    public void listenerReceivesEventsFromAnonymousBroadcasters() {
+        manager.addListener(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        manager.createAnonymousBroadcaster(TestFooListener.class).getSource().foo("param");
+    }
+
+    @Test
+    public void listenerReceivesEventsFromChildren() {
+        manager.addListener(fooListener1);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+
+        manager.createChild().getBroadcaster(TestFooListener.class).foo("param");
+    }
+    
+    @Test
+    public void listenerDoesNotReceiveEventsFromParent() {
+        manager.createChild().addListener(fooListener1);
+
+        manager.getBroadcaster(TestFooListener.class).foo("param");
+    }
+
+    @Test
+    public void loggerReceivesEventsFromChildren() {
+        manager.useLogger(fooListener1);
+
+        ListenerManager child = manager.createChild();
+        TestFooListener broadcaster = child.getBroadcaster(TestFooListener.class);
+
+        context.checking(new Expectations() {{
+            one(fooListener1).foo("param");
+        }});
+        broadcaster.foo("param");
+
+        manager.useLogger(fooListener2);
+
+        context.checking(new Expectations() {{
+            one(fooListener2).foo("param");
+        }});
+        broadcaster.foo("param");
+    }
+
+    @Test
+    public void loggerDoesNotReceiveEventsFromParent() {
+        manager.createChild().useLogger(fooListener1);
+
+        manager.getBroadcaster(TestFooListener.class).foo("param");
+    }
+
+    @Test
+    public void loggerInChildHasPrecedenceOverLoggerInParent() {
+        manager.useLogger(fooListener1);
+
+        ListenerManager child = manager.createChild();
+        TestFooListener broadcaster = child.getBroadcaster(TestFooListener.class);
+
+        child.useLogger(fooListener2);
+
+        context.checking(new Expectations() {{
+            one(fooListener2).foo("param");
+        }});
+
+        broadcaster.foo("param");
+    }
+
+    public interface TestFooListener {
+        void foo(String param);
+    }
+
+    public interface TestBarListener {
+        void bar(int value);
+    }
+}
+
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/event/ListenerBroadcastTest.java b/subprojects/messaging/src/test/groovy/org/gradle/internal/event/ListenerBroadcastTest.java
new file mode 100644
index 0000000..638bae5
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/event/ListenerBroadcastTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.event;
+
+import org.gradle.api.Action;
+import org.gradle.messaging.dispatch.Dispatch;
+import org.gradle.messaging.dispatch.MethodInvocation;
+import org.gradle.util.JUnit4GroovyMockery;
+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.Test;
+import org.junit.runner.RunWith;
+
+import static org.gradle.util.Matchers.strictlyEqual;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+ at RunWith(JMock.class)
+public class ListenerBroadcastTest {
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final ListenerBroadcast<TestListener> broadcast = new ListenerBroadcast<TestListener>(TestListener.class);
+
+    @Test
+    public void createsSourceObject() {
+        assertThat(broadcast.getSource(), notNullValue());
+        assertThat(broadcast.getSource(), strictlyEqual(broadcast.getSource()));
+        assertFalse(broadcast.getSource().equals(new ListenerBroadcast<TestListener>(TestListener.class).getSource()));
+        assertEquals(broadcast.getSource().hashCode(), broadcast.getSource().hashCode());
+        assertThat(broadcast.getSource().toString(), equalTo("TestListener broadcast"));
+    }
+
+    @Test
+    public void getTypeIsCorrect() {
+        assertThat(broadcast.getType(), equalTo(TestListener.class));
+    }
+
+    @Test
+    public void sourceObjectDoesNothingWhenNoListenersAdded() {
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void sourceObjectNotifiesEachListenerInOrderAdded() {
+        final TestListener listener1 = context.mock(TestListener.class, "listener1");
+        final TestListener listener2 = context.mock(TestListener.class, "listener2");
+
+        context.checking(new Expectations() {{
+            one(listener1).event1("param");
+            one(listener2).event1("param");
+        }});
+
+        broadcast.add(listener1);
+        broadcast.add(listener2);
+
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void canDispatchEventToListeners() throws NoSuchMethodException {
+        final TestListener listener1 = context.mock(TestListener.class, "listener1");
+        final TestListener listener2 = context.mock(TestListener.class, "listener2");
+
+        context.checking(new Expectations() {{
+            one(listener1).event1("param");
+            one(listener2).event1("param");
+        }});
+
+        broadcast.add(listener1);
+        broadcast.add(listener2);
+
+        MethodInvocation invocation = new MethodInvocation(TestListener.class.getMethod("event1", String.class), new Object[]{"param"});
+        broadcast.dispatch(invocation);
+    }
+
+    @Test
+    public void listenerIsNotUsedAfterItIsRemoved() {
+        TestListener listener = context.mock(TestListener.class);
+
+        broadcast.add(listener);
+        broadcast.remove(listener);
+
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void canUseDispatchToReceiveNotifications() throws NoSuchMethodException {
+        final Dispatch<MethodInvocation> dispatch1 = context.mock(Dispatch.class, "listener1");
+        final Dispatch<MethodInvocation> dispatch2 = context.mock(Dispatch.class, "listener2");
+        final MethodInvocation invocation = new MethodInvocation(TestListener.class.getMethod("event1", String.class), new Object[]{"param"});
+
+        context.checking(new Expectations() {{
+            one(dispatch1).dispatch(invocation);
+            one(dispatch2).dispatch(invocation);
+        }});
+
+        broadcast.add(dispatch1);
+        broadcast.add(dispatch2);
+
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void dispatchIsNotUsedAfterItIsRemoved() {
+        Dispatch<MethodInvocation> dispatch = context.mock(Dispatch.class);
+
+        broadcast.add(dispatch);
+        broadcast.remove(dispatch);
+
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void canUseActionForSingleEventMethod() {
+        final Action<String> action = context.mock(Action.class);
+        context.checking(new Expectations() {{
+            one(action).execute("param");
+        }});
+
+        broadcast.add("event1", action);
+        broadcast.getSource().event1("param");
+    }
+
+    @Test
+    public void doesNotNotifyActionForOtherEventMethods() {
+        final Action<String> action = context.mock(Action.class);
+
+        broadcast.add("event1", action);
+        broadcast.getSource().event2(9, "param");
+    }
+
+    @Test
+    public void actionCanHaveFewerParametersThanEventMethod() {
+        final Action<Integer> action = context.mock(Action.class);
+        context.checking(new Expectations() {{
+            one(action).execute(1);
+            one(action).execute(2);
+        }});
+        broadcast.add("event2", action);
+        broadcast.getSource().event2(1, "param");
+        broadcast.getSource().event2(2, null);
+    }
+
+    @Test
+    public void listenerCanAddAnotherListener() {
+        final TestListener listener1 = context.mock(TestListener.class, "listener1");
+        final TestListener listener2 = context.mock(TestListener.class, "listener2");
+        final TestListener listener3 = context.mock(TestListener.class, "listener3");
+
+        broadcast.add(listener1);
+        broadcast.add(listener2);
+
+        context.checking(new Expectations() {{
+            ignoring(listener2);
+            one(listener1).event1("event");
+            will(new org.jmock.api.Action() {
+                public void describeTo(Description description) {
+                    description.appendText("add listener");
+                }
+
+                public Object invoke(Invocation invocation) throws Throwable {
+                    broadcast.add(listener3);
+                    return null;
+                }
+            });
+        }});
+
+        broadcast.getSource().event1("event");
+    }
+
+    @Test
+    public void wrapsCheckedExceptionThrownByListener() throws Exception {
+        final TestListener listener = context.mock(TestListener.class);
+        final Exception failure = new Exception();
+
+        context.checking(new Expectations() {{
+            one(listener).event3();
+            will(throwException(failure));
+        }});
+
+        broadcast.add(listener);
+
+        try {
+            broadcast.getSource().event3();
+            fail();
+        } catch (ListenerNotificationException e) {
+            assertThat(e.getMessage(), equalTo("Failed to notify test listener."));
+            assertThat(e.getCause(), sameInstance((Throwable) failure));
+        }
+    }
+
+    @Test
+    public void attemptsToNotifyAllOtherListenersWhenOneThrowsException() {
+        final TestListener listener1 = context.mock(TestListener.class);
+        final TestListener listener2 = context.mock(TestListener.class);
+        final RuntimeException failure = new RuntimeException();
+
+        context.checking(new Expectations() {{
+            one(listener1).event1("param");
+            will(throwException(failure));
+            one(listener2).event1("param");
+        }});
+
+        broadcast.add(listener1);
+        broadcast.add(listener2);
+
+        try {
+            broadcast.getSource().event1("param");
+            fail();
+        } catch (RuntimeException e) {
+            assertThat(e, sameInstance(failure));
+        }
+    }
+
+    @Test
+    public void attemptsToNotifyAllOtherListenersWhenMultipleThrowException() {
+        final TestListener listener1 = context.mock(TestListener.class);
+        final TestListener listener2 = context.mock(TestListener.class);
+        final TestListener listener3 = context.mock(TestListener.class);
+        final RuntimeException failure1 = new RuntimeException();
+        final RuntimeException failure2 = new RuntimeException();
+
+        context.checking(new Expectations() {{
+            one(listener1).event1("param");
+            will(throwException(failure1));
+            one(listener2).event1("param");
+            will(throwException(failure2));
+            one(listener3).event1("param");
+        }});
+
+        broadcast.add(listener1);
+        broadcast.add(listener2);
+        broadcast.add(listener3);
+
+        try {
+            broadcast.getSource().event1("param");
+            fail();
+        } catch (ListenerNotificationException e) {
+            assertThat(e.getCauses().size(), equalTo(2));
+            assertThat(e.getCauses().get(0), sameInstance((Throwable) failure1));
+            assertThat(e.getCauses().get(1), sameInstance((Throwable) failure2));
+        }
+    }
+
+    public interface TestListener {
+        void event1(String param);
+
+        void event2(int value, String other);
+
+        void event3() throws Exception;
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/AbstractCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/AbstractCodecTest.groovy
new file mode 100644
index 0000000..0d3e462
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/AbstractCodecTest.groovy
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+abstract class AbstractCodecTest extends Specification {
+    def "can encode and decode raw bytes using Stream view"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.outputStream.write(Byte.MIN_VALUE)
+            encoder.outputStream.write(Byte.MAX_VALUE)
+            encoder.outputStream.write(-1)
+            encoder.outputStream.write([1, 2, 3, 4] as byte[])
+            encoder.outputStream.write([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 4)
+        }
+        decode(bytes) { Decoder decoder ->
+            def buffer = new byte[3]
+            assert decoder.inputStream.read(buffer) == 3
+            assert buffer == [Byte.MIN_VALUE, Byte.MAX_VALUE, -1] as byte[]
+            assert decoder.inputStream.read(buffer, 1, 2) == 2
+            assert buffer[1] == 1
+            assert buffer[2] == 2
+            assert decoder.inputStream.read() == 3
+            assert decoder.inputStream.read() == 4
+            assert decoder.inputStream.read() == 0xc3
+            def content = decoder.inputStream.bytes
+            assert content == [0xc4, 0xc5, 0xc6] as byte[]
+
+            assert decoder.inputStream.read() == -1
+            assert decoder.inputStream.read(buffer) == -1
+            assert decoder.inputStream.read(buffer, 0, 1) == -1
+        }
+    }
+
+    def "ignores close on InputStream"() {
+        def inputStream = Mock(InputStream)
+
+        when:
+        decodeFrom(inputStream) { Decoder decoder ->
+            decoder.inputStream.close()
+        }
+
+        then:
+        0 * inputStream.close()
+    }
+
+    def "ignores close or flush on OutputStream"() {
+        def outputStream = Mock(OutputStream)
+
+        when:
+        encodeTo(outputStream) { Encoder encoder ->
+            encoder.outputStream.flush()
+            encoder.outputStream.close()
+        }
+
+        then:
+        0 * outputStream.close()
+        0 * outputStream.flush()
+    }
+
+    def "can encode and decode raw bytes"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeByte(Byte.MIN_VALUE)
+            encoder.writeByte(Byte.MAX_VALUE)
+            encoder.writeByte(-1 as byte)
+            encoder.writeBytes([1, 2, 3, 4] as byte[])
+            encoder.writeBytes([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 1)
+        }
+        decode(bytes) { Decoder decoder ->
+            def buffer = new byte[2]
+            decoder.readBytes(buffer)
+            assert buffer == [Byte.MIN_VALUE, Byte.MAX_VALUE] as byte[]
+            assert decoder.readByte() == -1 as byte
+            decoder.readBytes(buffer, 0, 2)
+            assert buffer[0] == 1
+            assert buffer[1] == 2
+            assert decoder.readByte() == 3
+            assert decoder.readByte() == 4
+            assert decoder.readByte() == 0xc3 as byte
+        }
+    }
+
+    def "can encode and decode many bytes"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            10000.times {
+                encoder.writeByte(it as byte)
+            }
+        }
+        decode(bytes) { Decoder decoder ->
+            10000.times {
+                assert decoder.readByte() == it as byte
+            }
+        }
+    }
+
+    def "decode fails when requested number of raw bytes are not available"() {
+        given:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBytes([0xc1, 0xc2, 0xc3] as byte[])
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readBytes(new byte[4])
+        }
+
+        then:
+        thrown(EOFException)
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readBytes(new byte[10], 0, 5)
+        }
+
+        then:
+        thrown(EOFException)
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readBytes(new byte[3], 0, 3)
+            decoder.readByte()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can skip raw bytes"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBytes([1, 2, 3, 4, 5, 6] as byte[])
+            encoder.writeBytes(new byte[4096])
+            encoder.writeBytes([7, 8] as byte[])
+        }
+        decode(bytes) { Decoder decoder ->
+            def buffer = new byte[2]
+            decoder.readBytes(buffer)
+            assert buffer == [1, 2] as byte[]
+            decoder.skipBytes(2)
+            assert decoder.readByte() == 5 as byte
+            assert decoder.readByte() == 6 as byte
+            decoder.skipBytes(2000)
+            decoder.skipBytes(2096)
+            assert decoder.readByte() == 7 as byte
+            assert decoder.readByte() == 8 as byte
+        }
+    }
+
+    def "can skip raw bytes using InputStream"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBytes([1, 2, 3, 4, 5, 6] as byte[])
+        }
+        decode(bytes) { Decoder decoder ->
+            def buffer = new byte[2]
+            decoder.readBytes(buffer)
+            assert buffer == [1, 2] as byte[]
+            assert decoder.inputStream.skip(2) == 2
+            assert decoder.readByte() == 5 as byte
+            assert decoder.readByte() == 6 as byte
+            assert decoder.inputStream.skip(2) == 0
+        }
+    }
+
+    def "decode fails when too few bytes are available to skip"() {
+        when:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBytes([1, 2] as byte[])
+        }
+        decode(bytes) { Decoder decoder ->
+            decoder.skipBytes(4)
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can encode and decode byte array"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBinary([] as byte[])
+            encoder.writeBinary([1, 2, 3, 4] as byte[])
+            encoder.writeBinary([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 3)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readBinary() == [] as byte[]
+            assert decoder.readBinary() == [1, 2, 3, 4] as byte[]
+            assert decoder.readBinary() == [0xc3, 0xc4, 0xc5] as byte[]
+        }
+    }
+
+    def "decode fails when byte array cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeBinary([1, 2, 3, 4] as byte[])
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readBinary()
+        }
+
+        then:
+        thrown(EOFException)
+
+        when:
+        decode([] as byte[]) { Decoder decoder ->
+            decoder.readBinary()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    @Unroll
+    def "can encode and decode long #value"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeLong(value)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readLong() == value
+        }
+
+        where:
+        value               | _
+        0                   | _
+        12                  | _
+        -1                  | _
+        0xff                | _
+        0xffdd              | _
+        0xffddcc            | _
+        0xffddccbb          | _
+        0xffddccbbaa        | _
+        0xffddccbbaa99      | _
+        0xffddccbbaa9988    | _
+        0x7fddccbbaa998877  | _
+        -0xff               | _
+        -0xffdd             | _
+        -0xffddcc           | _
+        -0xffddccbb         | _
+        -0xffddccbbaa       | _
+        -0xffddccbbaa99     | _
+        -0xffddccbbaa9988   | _
+        -0x7fddccbbaa998877 | _
+        Long.MAX_VALUE      | _
+        Long.MIN_VALUE      | _
+    }
+
+    def "decode fails when long cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeLong(0xa40745f3L)
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readLong()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can encode and decode a small long"() {
+        expect:
+        def bytesA = encode { Encoder encoder ->
+            encoder.writeSmallLong(a as long)
+        }
+        def bytesB = encode { Encoder encoder ->
+            encoder.writeSmallLong(b as long)
+        }
+        decode(bytesA) { Decoder decoder ->
+            assert decoder.readSmallLong() == a
+        }
+        decode(bytesB) { Decoder decoder ->
+            assert decoder.readSmallLong() == b
+        }
+        bytesA.length <= bytesB.length
+
+        where:
+        a                 | b
+        0                 | 0x1ff
+        0x2ff             | 0x1000
+        0x1000            | -1
+        Integer.MAX_VALUE | -1
+        Long.MAX_VALUE    | -1
+        Long.MAX_VALUE    | -0xc3412
+        Integer.MAX_VALUE | Integer.MIN_VALUE
+        Long.MAX_VALUE    | Long.MIN_VALUE
+    }
+
+    def "decode fails when small long cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeSmallLong(0xa40745f)
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readSmallLong()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    @Unroll
+    def "can encode and decode int #value"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeInt(value as int)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readInt() == value
+        }
+
+        where:
+        value             | _
+        0                 | _
+        12                | _
+        -1                | _
+        0xF               | _
+        0xFD              | _
+        0xFDD             | _
+        0xFFDD            | _
+        0xFFDDCC          | _
+        0x7FDDCCBB        | _
+        -0xFF             | _
+        Integer.MAX_VALUE | _
+        Integer.MIN_VALUE | _
+    }
+
+    def "decode fails when int cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeInt(0xa40745f)
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readInt()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can encode and decode a small int"() {
+        expect:
+        def bytesA = encode { Encoder encoder ->
+            encoder.writeSmallInt(a as int)
+        }
+        def bytesB = encode { Encoder encoder ->
+            encoder.writeSmallInt(b as int)
+        }
+        decode(bytesA) { Decoder decoder ->
+            assert decoder.readSmallInt() == a
+        }
+        decode(bytesB) { Decoder decoder ->
+            assert decoder.readSmallInt() == b
+        }
+        bytesA.length <= bytesB.length
+
+        where:
+        a                 | b
+        0                 | 0x1ff
+        0x2ff             | 0x1000
+        0x1000            | -1
+        Integer.MAX_VALUE | -1
+        Integer.MAX_VALUE | -0xc3412
+        Integer.MAX_VALUE | Integer.MIN_VALUE
+    }
+
+    def "decode fails when small int cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeSmallInt(0xa40745f)
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readSmallInt()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can encode and decode a boolean"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeBoolean(true)
+            encoder.writeBoolean(false)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readBoolean()
+            assert !decoder.readBoolean()
+        }
+    }
+
+    def "decode fails when boolean cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeBoolean(true)
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readBoolean()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "can encode and decode a string"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeString(value)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readString() == value.toString()
+        }
+
+        where:
+        value                            | _
+//        ""                               | _
+//        "all ascii"                      | _
+        "\u0000\u0101\u3100"             | _
+//        "${1 + 2}"                       | _
+//        new StringBuilder("some string") | _
+//        CharBuffer.wrap("a string")      | _
+//        (0..1000).join("-")              | _
+    }
+
+    def "decode fails when string cannot be fully read"() {
+        given:
+        def bytes = truncate { Encoder encoder ->
+            encoder.writeString("hi")
+        }
+
+        when:
+        decode(bytes) { Decoder decoder ->
+            decoder.readString()
+        }
+
+        then:
+        thrown(EOFException)
+    }
+
+    def "cannot encode a null string"() {
+        when:
+        encode { Encoder encoder ->
+            encoder.writeString(null)
+        }
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+
+    def "can encode and decode a nullable string"() {
+        expect:
+        def bytes = encode { Encoder encoder ->
+            encoder.writeNullableString(value)
+        }
+        decode(bytes) { Decoder decoder ->
+            assert decoder.readNullableString() == value?.toString()
+        }
+
+        where:
+        value                            | _
+        null                             | _
+        ""                               | _
+        "all ascii"                      | _
+        "\u0000\u0101\u3100"             | _
+        "${1 + 2}"                       | _
+        new StringBuilder("some string") | _
+    }
+
+    abstract void encodeTo(OutputStream outputStream, Closure<Encoder> closure)
+
+    byte[] encode(Closure<Encoder> closure) {
+        def bytes = new ByteArrayOutputStream()
+        encodeTo(bytes, closure)
+        return bytes.toByteArray()
+    }
+
+    byte[] truncate(Closure<Encoder> closure) {
+        def bytes = new ByteArrayOutputStream()
+        encodeTo(bytes, closure)
+        def result = bytes.toByteArray()
+        if (result.length < 2) {
+            return [] as byte[]
+        }
+        return result[0..result.length - 2] as byte[]
+    }
+
+    abstract void decodeFrom(InputStream inputStream, Closure<Decoder> closure)
+
+    void decode(byte[] bytes, Closure<Decoder> closure) {
+        decodeFrom(new ByteArrayInputStream(bytes), closure)
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/BaseSerializerFactoryTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/BaseSerializerFactoryTest.groovy
new file mode 100644
index 0000000..23c2159
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/BaseSerializerFactoryTest.groovy
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize
+
+class BaseSerializerFactoryTest extends SerializerSpec {
+    def factory = new BaseSerializerFactory()
+
+    def "uses efficient serialization for Strings"() {
+        def encoder = Mock(Encoder)
+        def decoder = Mock(Decoder)
+
+        when:
+        def serializer = factory.getSerializerFor(String)
+        serializer.write(encoder, "hi")
+        def result = serializer.read(decoder)
+
+        then:
+        result == "bye"
+        1 * encoder.writeString("hi")
+        1 * decoder.readString() >> "bye"
+        0 * _
+    }
+
+    def "uses efficient serialization for Files"() {
+        def encoder = Mock(Encoder)
+        def decoder = Mock(Decoder)
+
+        when:
+        def serializer = factory.getSerializerFor(File)
+        serializer.write(encoder, new File("some-file"))
+        def result = serializer.read(decoder)
+
+        then:
+        result == new File("some-file")
+        1 * encoder.writeString("some-file")
+        1 * decoder.readString() >> "some-file"
+        0 * _
+    }
+
+    def "uses efficient serialization for Long"() {
+        def encoder = Mock(Encoder)
+        def decoder = Mock(Decoder)
+
+        when:
+        def serializer = factory.getSerializerFor(Long)
+        serializer.write(encoder, 123L)
+        def result = serializer.read(decoder)
+
+        then:
+        result == 456L
+        1 * encoder.writeLong(123L)
+        1 * decoder.readLong() >> 456L
+        0 * _
+    }
+
+    enum Letters {
+        A, B, C
+    }
+
+    def "uses efficient serialization for Enum"() {
+        def encoder = Mock(Encoder)
+        def decoder = Mock(Decoder)
+
+        when:
+        def serializer = factory.getSerializerFor(Letters)
+        serializer.write(encoder, Letters.B)
+        def result = serializer.read(decoder)
+
+        then:
+        result == Letters.C
+        1 * encoder.writeSmallInt(1)
+        1 * decoder.readSmallInt() >> 2
+        0 * _
+    }
+
+    def "uses efficient serialization for byte arrays"() {
+        def s = factory.getSerializerFor(byte[])
+        def os = new ByteArrayOutputStream()
+        s.write(new OutputStreamBackedEncoder(os), new byte[5])
+
+        expect:
+        def result = serialize(new byte[5], s)
+        result instanceof byte[]
+        result.length == 5
+    }
+
+    def "can serialize string maps"() {
+        def s = BaseSerializerFactory.NO_NULL_STRING_MAP_SERIALIZER
+
+        expect:
+        serialize(map, s) == map
+
+        where:
+        map << [
+                [:], ["foo": "bar"], [a: "a", "b": "b"]
+        ]
+    }
+
+    def "uses Java serialization for unknown type"() {
+        expect:
+        factory.getSerializerFor(Thing) instanceof DefaultSerializer
+    }
+
+    class Thing {}
+
+    def "serialize booleans"() {
+        expect:
+        serialize(true, factory.getSerializerFor(Boolean))
+        !serialize(false, factory.getSerializerFor(Boolean))
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerRegistryTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerRegistryTest.groovy
new file mode 100644
index 0000000..c675778
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerRegistryTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.serialize
+
+class DefaultSerializerRegistryTest extends SerializerSpec {
+    def longSerializer = Stub(Serializer) {
+        read(_) >> { Decoder decoder ->
+            return decoder.readSmallLong()
+        }
+        write(_, _) >> { Encoder encoder, Long value ->
+            encoder.writeSmallLong(value)
+        }
+    }
+    def intSerializer = Stub(Serializer) {
+        read(_) >> { Decoder decoder ->
+            return decoder.readSmallInt()
+        }
+        write(_, _) >> { Encoder encoder, Integer value ->
+            encoder.writeSmallInt(value)
+        }
+    }
+
+    def "serializes type information with a value"() {
+        given:
+        def registry = new DefaultSerializerRegistry()
+        registry.register(Long, longSerializer)
+        registry.register(Integer, intSerializer)
+        def serializer = registry.build()
+
+        expect:
+        serialize(123L, serializer) == 123L
+        serialize(123, serializer) == 123
+    }
+
+    def "does not write type tag when there is only one registered type"() {
+        given:
+        def registry = new DefaultSerializerRegistry()
+        registry.register(Long, longSerializer)
+        def serializer1 = registry.build()
+        registry.register(Integer, intSerializer)
+        def serializer2 = registry.build()
+
+        expect:
+        toBytes(123L, serializer1).length + 1 == toBytes(123L, serializer2).length
+    }
+
+    def "type information is independent of the order that types are registered"() {
+        given:
+        def registry = new DefaultSerializerRegistry()
+        registry.register(Long, longSerializer)
+        registry.register(Integer, intSerializer)
+        def serializer1 = registry.build()
+
+        and:
+        registry = new DefaultSerializerRegistry()
+        registry.register(Integer, intSerializer)
+        registry.register(Long, longSerializer)
+        def serializer2 = registry.build()
+
+        expect:
+        fromBytes(toBytes(123L, serializer1), serializer2) == 123L
+        fromBytes(toBytes(123, serializer1), serializer2) == 123
+    }
+
+    def "cannot write value with type that has not been registered"() {
+        given:
+        def registry = new DefaultSerializerRegistry()
+        registry.register(Long, longSerializer)
+        registry.register(Integer, intSerializer)
+        def serializer = registry.build()
+
+        when:
+        toBytes(123.4, serializer)
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerTest.groovy
new file mode 100644
index 0000000..a11282f
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/DefaultSerializerTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.serialize
+
+class DefaultSerializerTest extends SerializerSpec {
+    def canSerializeAndDeserializeObject() {
+        GroovyClassLoader classLoader = new GroovyClassLoader(getClass().classLoader)
+        DefaultSerializer serializer = new DefaultSerializer(classLoader)
+
+        Class cl = classLoader.parseClass('package org.gradle.cache; class TestObj implements Serializable { }')
+        Object o = cl.newInstance()
+
+        when:
+        def r = serialize(o, serializer)
+
+        then:
+        cl.isInstance(r)
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/ListSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/ListSerializerTest.groovy
new file mode 100644
index 0000000..7b8e643
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/ListSerializerTest.groovy
@@ -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.serialize
+
+class ListSerializerTest extends SerializerSpec {
+
+    def stringSerializer = new NullSafeStringSerializer()
+
+    def "serialize list of strings"() {
+        when:
+        def serializer = new ListSerializer(stringSerializer)
+
+        then:
+        serialize(["one", "two", "three"], serializer) == ["one", "two", "three"]
+    }
+
+    def "serialize list of longs"() {
+        when:
+        def serializer = new ListSerializer(BaseSerializerFactory.LONG_SERIALIZER)
+
+        then:
+        serialize([10L, 5L, 99L], serializer) == [10L, 5L, 99L]
+    }
+
+    def "serialize null entry"() {
+        when:
+        def serializer = new ListSerializer(stringSerializer)
+
+        then:
+        serialize(["one", null, "three"], serializer) == ["one", null, "three"]
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/LongSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/LongSerializerTest.groovy
new file mode 100644
index 0000000..1f2d4ba
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/LongSerializerTest.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.serialize
+
+class LongSerializerTest extends SerializerSpec {
+
+    def serializer = new LongSerializer()
+
+    def "writes and reads Longs"() {
+        expect:
+        serialize(144L, serializer) == 144L
+    }
+
+    def "does not permit null"() {
+        when:
+        serializer.write(new OutputStreamBackedEncoder(new ByteArrayOutputStream()), null)
+
+        then:
+        thrown(IllegalArgumentException)
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/MapSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/MapSerializerTest.groovy
new file mode 100644
index 0000000..6b3a965
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/MapSerializerTest.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.internal.serialize
+
+class MapSerializerTest extends SerializerSpec {
+
+    def stringSerializer = new NullSafeStringSerializer()
+
+    def "retains order of serialized entries"() {
+        when:
+        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
+        Map values = serialize([10L: "one", 2L: "two", 30L: "three"], serializer) as Map
+
+        then:
+        values.keySet() as List == [10L, 2L, 30L]
+    }
+
+    def "serialize map"() {
+        when:
+        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
+
+        then:
+        serialize([1L: "one", 2L: "two", 3L: "three"], serializer) == [1L: "one", 2L: "two", 3L: "three"]
+    }
+
+    def "serialize null value"() {
+        when:
+        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
+
+        then:
+        serialize([1L: "one", 2L: null], serializer) == [1L: "one", 2L: null]
+    }
+
+    def "informs which value are not serializable"() {
+        def encoder = Mock(Encoder); def keySerializer = Mock(Serializer); def valueSerializer = Mock(Serializer)
+        def serializer = new MapSerializer(keySerializer, valueSerializer)
+
+        when: serializer.write(encoder, [a: 1, b: 2])
+
+        then:
+        1 * valueSerializer.write(encoder, 2) >> { throw new RuntimeException("Boom!")}
+
+        and:
+        def ex = thrown(MapSerializer.EntrySerializationException)
+        ex.key == 'b'
+        ex.value == 2
+        ex.message == "Unable to write entry with key: 'b' and value: '2'."
+        ex.cause.message == "Boom!"
+    }
+
+    def "informs which key is not serializable"() {
+        def encoder = Mock(Encoder); def keySerializer = Mock(Serializer); def valueSerializer = Mock(Serializer)
+        def serializer = new MapSerializer(keySerializer, valueSerializer)
+
+        when: serializer.write(encoder, [a: 1, b: 2])
+
+        then:
+        1 * keySerializer.write(encoder, 'a') >> { throw new RuntimeException("Boom!")}
+
+        and:
+        def ex = thrown(MapSerializer.EntrySerializationException)
+        ex.key == 'a'
+        ex.value == 1
+        ex.cause.message == "Boom!"
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy
new file mode 100644
index 0000000..835f1fc
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.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.internal.serialize
+
+class SetSerializerTest extends SerializerSpec {
+
+    def stringSerializer = new NullSafeStringSerializer()
+
+    def "serialize set of strings"() {
+        when:
+        def serializer = new SetSerializer(stringSerializer)
+
+        then:
+        serialize(["one", "two", "three"] as Set, serializer) as List == ["one", "two", "three"]
+    }
+
+    def "serialize set of longs"() {
+        when:
+        def serializer = new SetSerializer(BaseSerializerFactory.LONG_SERIALIZER)
+
+        then:
+        serialize([1L, 5L, 99L] as Set, serializer) as List == [1L, 5L, 99L]
+    }
+
+    def "serialize null entry"() {
+        when:
+        def serializer = new SetSerializer(stringSerializer)
+
+        then:
+        serialize(["one", null, "three"] as Set, serializer) as List == ["one", null, "three"]
+    }
+
+    def "serializes with HasSet"() {
+        when:
+        def serializer = new SetSerializer(stringSerializer, false)
+
+        then:
+        serialize(['1', '2'] as Set, serializer) instanceof HashSet
+    }
+}
\ No newline at end of file
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/StreamBackedCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/StreamBackedCodecTest.groovy
new file mode 100644
index 0000000..ac9302e
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/StreamBackedCodecTest.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.internal.serialize
+
+class StreamBackedCodecTest extends AbstractCodecTest {
+    @Override
+    void encodeTo(OutputStream outputStream, Closure<Encoder> closure) {
+        def encoder = new OutputStreamBackedEncoder(outputStream)
+        closure.call(encoder)
+    }
+
+    @Override
+    void decodeFrom(InputStream inputStream, Closure<Decoder> closure) {
+        def decoder = new InputStreamBackedDecoder(inputStream)
+        closure.call(decoder)
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/kryo/KryoBackedCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/kryo/KryoBackedCodecTest.groovy
new file mode 100644
index 0000000..f9bc28e
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/kryo/KryoBackedCodecTest.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.internal.serialize.kryo
+
+import org.gradle.internal.serialize.AbstractCodecTest
+import org.gradle.internal.serialize.Decoder
+import org.gradle.internal.serialize.Encoder
+
+class KryoBackedCodecTest extends AbstractCodecTest {
+    @Override
+    void encodeTo(OutputStream outputStream, Closure<Encoder> closure) {
+        def encoder = new KryoBackedEncoder(outputStream, 10)
+        closure.call(encoder)
+        encoder.flush()
+    }
+
+    @Override
+    void decodeFrom(InputStream inputStream, Closure<Decoder> closure) {
+        def decoder = new KryoBackedDecoder(inputStream, 10)
+        closure.call(decoder)
+    }
+
+    def "can query write and read positions"() {
+        def outstr = new ByteArrayOutputStream()
+        def encoder = new KryoBackedEncoder(outstr)
+
+        expect:
+        encoder.writePosition == 0
+
+        when:
+        encoder.writeBoolean(true)
+        encoder.writeByte(12 as byte)
+        encoder.writeLong(1234)
+
+        then:
+        encoder.writePosition == 10
+        outstr.size() == 0
+
+        when:
+        encoder.flush()
+
+        then:
+        encoder.writePosition == 10
+        outstr.size() == 10
+
+        when:
+        encoder.writeBytes(new byte[4098])
+
+        then:
+        encoder.writePosition == 4108
+        outstr.size() == 4106
+
+        when:
+        encoder.close()
+
+        then:
+        encoder.writePosition == 4108
+        outstr.size() == 4108
+
+        when:
+        def instr = new ByteArrayInputStream(outstr.toByteArray())
+        def decoder = new KryoBackedDecoder(instr)
+
+        then:
+        instr.available() == 4108
+        decoder.readPosition == 0
+
+        when:
+        decoder.readBoolean()
+        decoder.readByte()
+        decoder.readLong()
+
+        then:
+        instr.available() == 12 // decoder has buffered from instr
+        decoder.readPosition == 10
+
+        when:
+        decoder.skipBytes(4098)
+
+        then:
+        instr.available() == 0
+        decoder.readPosition == 4108
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.groovy
new file mode 100755
index 0000000..2806445
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.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.messaging.dispatch
+
+import spock.lang.Specification
+
+import static org.gradle.util.Matchers.strictlyEqual
+import static org.junit.Assert.assertThat
+
+class MethodInvocationTest extends Specification {
+
+    def "equality"() {
+        def invocation = new MethodInvocation(String.class.getMethod("length"), ["param"] as Object[])
+        def equalInvocation = new MethodInvocation(String.class.getMethod("length"), ["param"] as Object[])
+        def differentMethod = new MethodInvocation(String.class.getMethod("getBytes"), ["param"] as Object[])
+        def differentArgs = new MethodInvocation(String.class.getMethod("length"), ["a", "b"] as Object[])
+        def nullArgs = new MethodInvocation(String.class.getMethod("length"), null)
+
+        expect:
+        assertThat(invocation, strictlyEqual(equalInvocation))
+        invocation != differentMethod
+        invocation != differentArgs
+        invocation != nullArgs
+        nullArgs != invocation
+    }
+
+    def "string representation"() {
+        expect:
+        new MethodInvocation(String.class.getMethod("length"), ["1", "2"] as Object[]).toString() == "[MethodInvocation method: length(1, 2)]"
+        new MethodInvocation(String.class.getMethod("length"), null).toString() == "[MethodInvocation method: length()]"
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.java b/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.java
deleted file mode 100755
index 71919c2..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/dispatch/MethodInvocationTest.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.messaging.dispatch;
-
-import org.junit.Test;
-
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-public class MethodInvocationTest {
-    @Test
-    public void equalsAndHashCode() throws Exception {
-        MethodInvocation invocation = new MethodInvocation(String.class.getMethod("length"), new Object[]{"param"});
-        MethodInvocation equalInvocation = new MethodInvocation(String.class.getMethod("length"), new Object[]{"param"});
-        MethodInvocation differentMethod = new MethodInvocation(String.class.getMethod("getBytes"), new Object[]{"param"});
-        MethodInvocation differentArgs = new MethodInvocation(String.class.getMethod("length"), new Object[]{"a", "b"});
-        assertThat(invocation, strictlyEqual(equalInvocation));
-        assertThat(invocation, not(equalTo(differentMethod)));
-        assertThat(invocation, not(equalTo(differentArgs)));
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy
index 267125a..99b82c8 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.messaging.remote.internal
 
-import org.gradle.internal.exceptions.AbstractMultiCauseException
+import org.gradle.internal.exceptions.DefaultMultiCauseException
 import spock.lang.Issue
 import spock.lang.Specification
 
@@ -26,7 +26,7 @@ class MessageTest extends Specification {
     def "can transport graph of exceptions"() {
         def cause1 = new ExceptionWithState("nested-1", ["a", 1])
         def cause2 = new IOException("nested-2")
-        def cause = new AbstractMultiCauseException("nested", cause1, cause2)
+        def cause = new DefaultMultiCauseException("nested", cause1, cause2)
         def original = new ExceptionWithExceptionField("message", cause)
 
         when:
@@ -37,7 +37,7 @@ class MessageTest extends Specification {
         transported.payload.message == "message"
 
         and:
-        transported.payload.throwable.class == AbstractMultiCauseException
+        transported.payload.throwable.class == DefaultMultiCauseException
         transported.payload.throwable.message == "nested"
 
         and:
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/ProtocolStackTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/ProtocolStackTest.groovy
index 8ff9afe..7430a9d 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/ProtocolStackTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/ProtocolStackTest.groovy
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 package org.gradle.messaging.remote.internal
-
-import java.util.concurrent.TimeUnit
 import org.gradle.messaging.dispatch.Dispatch
 import org.gradle.messaging.dispatch.DispatchFailureHandler
 import org.gradle.util.ConcurrentSpecification
+import spock.lang.Ignore
 import spock.lang.Timeout
 
+import java.util.concurrent.TimeUnit
+
 class ProtocolStackTest extends ConcurrentSpecification {
     final Protocol<String> top = Mock()
     final Protocol<String> bottom = Mock()
@@ -67,6 +68,7 @@ class ProtocolStackTest extends ConcurrentSpecification {
         stack?.stop()
     }
 
+    @Ignore("Breaks TC often times. Ignore until stabilized.")
     @Timeout(10)
     def "top protocol can dispatch incoming message during start"() {
         Protocol<String> protocol = Mock()
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializerTest.groovy
index 828be41..929f6f1 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializerTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/InterHubMessageSerializerTest.groovy
@@ -20,7 +20,7 @@ import org.gradle.messaging.remote.internal.hub.protocol.ChannelIdentifier
 import org.gradle.messaging.remote.internal.hub.protocol.ChannelMessage
 import org.gradle.messaging.remote.internal.hub.protocol.EndOfStream
 import org.gradle.messaging.remote.internal.hub.protocol.InterHubMessage
-import org.gradle.messaging.serialize.kryo.JavaSerializer
+import org.gradle.internal.serialize.kryo.JavaSerializer
 import spock.lang.Specification
 
 class InterHubMessageSerializerTest extends Specification {
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedClientTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedClientTest.groovy
index 663ef88..5e51b02 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedClientTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedClientTest.groovy
@@ -22,8 +22,8 @@ import org.gradle.internal.concurrent.ExecutorFactory
 import org.gradle.internal.concurrent.StoppableExecutor
 import org.gradle.messaging.remote.Address
 import org.gradle.messaging.remote.internal.ConnectCompletion
-import org.gradle.messaging.remote.internal.Connection
 import org.gradle.messaging.remote.internal.OutgoingConnector
+import org.gradle.messaging.remote.internal.RemoteConnection
 import org.gradle.messaging.remote.internal.hub.protocol.InterHubMessage
 import spock.lang.Specification
 
@@ -35,7 +35,7 @@ class MessageHubBackedClientTest extends Specification {
     def "creates connection and cleans up on stop"() {
         Address address = Stub()
         ConnectCompletion connectCompletion = Mock()
-        Connection<InterHubMessage> backingConnection = Mock()
+        RemoteConnection<InterHubMessage> backingConnection = Mock()
         StoppableExecutor executor = Mock()
 
         when:
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedServerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedServerTest.groovy
index c94fc67..927f03c 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedServerTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MessageHubBackedServerTest.groovy
@@ -24,8 +24,8 @@ import org.gradle.internal.concurrent.StoppableExecutor
 import org.gradle.messaging.remote.ConnectionAcceptor
 import org.gradle.messaging.remote.ObjectConnection
 import org.gradle.messaging.remote.internal.ConnectCompletion
-import org.gradle.messaging.remote.internal.Connection
 import org.gradle.messaging.remote.internal.IncomingConnector
+import org.gradle.messaging.remote.internal.RemoteConnection
 import org.gradle.messaging.remote.internal.hub.protocol.InterHubMessage
 import spock.lang.Specification
 
@@ -37,7 +37,7 @@ class MessageHubBackedServerTest extends Specification {
     def "creates connection and cleans up on stop"() {
         ConnectionAcceptor acceptor = Mock()
         Action<ObjectConnection> connectAction = Mock()
-        Connection<InterHubMessage> backingConnection = Mock()
+        RemoteConnection<InterHubMessage> backingConnection = Mock()
         StoppableExecutor executor = Mock()
         ConnectCompletion completion = Mock()
         Action<ConnectCompletion> acceptAction
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializerTest.groovy
index ade55e1..feb48a7 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializerTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/hub/MethodInvocationSerializerTest.groovy
@@ -17,9 +17,9 @@
 package org.gradle.messaging.remote.internal.hub
 
 import org.gradle.messaging.dispatch.MethodInvocation
-import org.gradle.messaging.serialize.kryo.JavaSerializer
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder
+import org.gradle.internal.serialize.kryo.JavaSerializer
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder
 import spock.lang.Specification
 
 class MethodInvocationSerializerTest extends Specification {
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/InetAddressFactoryTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/InetAddressFactoryTest.groovy
index 8b985f7..84bc47a 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/InetAddressFactoryTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/InetAddressFactoryTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.messaging.remote.internal.inet
 
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 
 class InetAddressFactoryTest extends Specification {
@@ -31,6 +33,11 @@ class InetAddressFactoryTest extends Specification {
         !factory.findRemoteAddresses().empty
     }
 
+    def "always contains at least multicast interface"() {
+        expect:
+        !factory.findMulticastInterfaces().empty
+    }
+
     def "all local address is considered local"() {
         expect:
         factory.findLocalAddresses().every {
@@ -38,6 +45,7 @@ class InetAddressFactoryTest extends Specification {
         }
     }
 
+    @Requires(TestPrecondition.ONLINE)
     def "no remote address is considered local"() {
         expect:
         factory.findRemoteAddresses().every {
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/MulticastConnectionTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/MulticastConnectionTest.groovy
new file mode 100644
index 0000000..a867d87
--- /dev/null
+++ b/subprojects/messaging/src/test/groovy/org/gradle/messaging/remote/internal/inet/MulticastConnectionTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.messaging.remote.internal.inet
+
+import org.gradle.messaging.remote.internal.DefaultMessageSerializer
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+import org.gradle.util.AvailablePortFinder
+import org.junit.internal.AssumptionViolatedException
+import spock.lang.Timeout
+
+ at Timeout(30)
+class MulticastConnectionTest extends ConcurrentSpec {
+    def addressFactory = new InetAddressFactory()
+
+    def "can send multicast messages between peers"() {
+        def address = new SocketInetAddress(InetAddress.getByName("233.253.17.122"), AvailablePortFinder.createPrivate().nextAvailable)
+        def serializer = new DefaultMessageSerializer<String>(getClass().classLoader)
+
+        given:
+        multicastAvailable(address)
+
+        when:
+        def connection1 = new MulticastConnection<String>(address, serializer, addressFactory)
+        def connection2 = new MulticastConnection<String>(address, serializer, addressFactory)
+        connection1.dispatch("hi!")
+
+        then:
+        connection2.receive() == "hi!"
+
+        cleanup:
+        connection1?.stop()
+        connection2?.stop()
+    }
+
+    void multicastAvailable(SocketInetAddress address) {
+        def socket1 = new MulticastSocket(address.port);
+        def inetAddress = new InetSocketAddress(address.address, address.port)
+        addressFactory.findMulticastInterfaces().each { networkInterface ->
+            try {
+                socket1.joinGroup(inetAddress, networkInterface);
+            } catch (SocketException e) {
+                // Ignore
+            }
+        }
+        def socket2 = new MulticastSocket(address.port);
+        addressFactory.findMulticastInterfaces().each { networkInterface ->
+            try {
+                socket2.joinGroup(inetAddress, networkInterface);
+            } catch (SocketException e) {
+                // Ignore
+            }
+        }
+
+        def message = "hi".getBytes()
+        socket1.send(new DatagramPacket(message, message.length, address.address, address.port))
+
+        socket2.setSoTimeout(10000);
+
+        def packet = new DatagramPacket(new byte[1024], 1024)
+        try {
+            socket2.receive(packet)
+        } catch (SocketTimeoutException e) {
+            throw new AssumptionViolatedException("""Timeout waiting to receive message.
+network interfaces: ${NetworkInterface.networkInterfaces.collect { it.displayName }.join(', ')}
+selected: ${addressFactory.findMulticastInterfaces().collect { it.displayName }.join(', ')}
+""", e)
+        }
+
+        socket1.close()
+        socket2.close()
+        assert new String(packet.data, packet.offset, packet.length) == "hi"
+    }
+}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/AbstractCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/AbstractCodecTest.groovy
deleted file mode 100644
index 6962351..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/AbstractCodecTest.groovy
+++ /dev/null
@@ -1,524 +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.messaging.serialize
-
-import spock.lang.Specification
-import spock.lang.Unroll
-
-import java.nio.CharBuffer
-
-abstract class AbstractCodecTest extends Specification {
-    def "can encode and decode raw bytes using Stream view"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.outputStream.write(Byte.MIN_VALUE)
-            encoder.outputStream.write(Byte.MAX_VALUE)
-            encoder.outputStream.write(-1)
-            encoder.outputStream.write([1, 2, 3, 4] as byte[])
-            encoder.outputStream.write([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 4)
-        }
-        decode(bytes) { Decoder decoder ->
-            def buffer = new byte[3]
-            assert decoder.inputStream.read(buffer) == 3
-            assert buffer == [Byte.MIN_VALUE, Byte.MAX_VALUE, -1] as byte[]
-            assert decoder.inputStream.read(buffer, 1, 2) == 2
-            assert buffer[1] == 1
-            assert buffer[2] == 2
-            assert decoder.inputStream.read() == 3
-            assert decoder.inputStream.read() == 4
-            assert decoder.inputStream.read() == 0xc3
-            def content = decoder.inputStream.bytes
-            assert content == [0xc4, 0xc5, 0xc6] as byte[]
-
-            assert decoder.inputStream.read() == -1
-            assert decoder.inputStream.read(buffer) == -1
-            assert decoder.inputStream.read(buffer, 0, 1) == -1
-        }
-    }
-
-    def "ignores close on InputStream"() {
-        def inputStream = Mock(InputStream)
-
-        when:
-        decodeFrom(inputStream) { Decoder decoder ->
-            decoder.inputStream.close()
-        }
-
-        then:
-        0 * inputStream.close()
-    }
-
-    def "ignores close or flush on OutputStream"() {
-        def outputStream = Mock(OutputStream)
-
-        when:
-        encodeTo(outputStream) { Encoder encoder ->
-            encoder.outputStream.flush()
-            encoder.outputStream.close()
-        }
-
-        then:
-        0 * outputStream.close()
-        0 * outputStream.flush()
-    }
-
-    def "can encode and decode raw bytes"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeByte(Byte.MIN_VALUE)
-            encoder.writeByte(Byte.MAX_VALUE)
-            encoder.writeByte(-1 as byte)
-            encoder.writeBytes([1, 2, 3, 4] as byte[])
-            encoder.writeBytes([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 1)
-        }
-        decode(bytes) { Decoder decoder ->
-            def buffer = new byte[2]
-            decoder.readBytes(buffer)
-            assert buffer == [Byte.MIN_VALUE, Byte.MAX_VALUE] as byte[]
-            assert decoder.readByte() == -1 as byte
-            decoder.readBytes(buffer, 0, 2)
-            assert buffer[0] == 1
-            assert buffer[1] == 2
-            assert decoder.readByte() == 3
-            assert decoder.readByte() == 4
-            assert decoder.readByte() == 0xc3 as byte
-        }
-    }
-
-    def "can encode and decode many bytes"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            10000.times {
-                encoder.writeByte(it as byte)
-            }
-        }
-        decode(bytes) { Decoder decoder ->
-            10000.times {
-                assert decoder.readByte() == it as byte
-            }
-        }
-    }
-
-    def "decode fails when requested number of raw bytes are not available"() {
-        given:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBytes([0xc1, 0xc2, 0xc3] as byte[])
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readBytes(new byte[4])
-        }
-
-        then:
-        thrown(EOFException)
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readBytes(new byte[10], 0, 5)
-        }
-
-        then:
-        thrown(EOFException)
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readBytes(new byte[3], 0, 3)
-            decoder.readByte()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can skip raw bytes"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBytes([1, 2, 3, 4, 5, 6] as byte[])
-            encoder.writeBytes(new byte[4096])
-            encoder.writeBytes([7, 8] as byte[])
-        }
-        decode(bytes) { Decoder decoder ->
-            def buffer = new byte[2]
-            decoder.readBytes(buffer)
-            assert buffer == [1, 2] as byte[]
-            decoder.skipBytes(2)
-            assert decoder.readByte() == 5 as byte
-            assert decoder.readByte() == 6 as byte
-            decoder.skipBytes(2000)
-            decoder.skipBytes(2096)
-            assert decoder.readByte() == 7 as byte
-            assert decoder.readByte() == 8 as byte
-        }
-    }
-
-    def "can skip raw bytes using InputStream"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBytes([1, 2, 3, 4, 5, 6] as byte[])
-        }
-        decode(bytes) { Decoder decoder ->
-            def buffer = new byte[2]
-            decoder.readBytes(buffer)
-            assert buffer == [1, 2] as byte[]
-            assert decoder.inputStream.skip(2) == 2
-            assert decoder.readByte() == 5 as byte
-            assert decoder.readByte() == 6 as byte
-            assert decoder.inputStream.skip(2) == 0
-        }
-    }
-
-    def "decode fails when too few bytes are available to skip"() {
-        when:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBytes([1, 2] as byte[])
-        }
-        decode(bytes) { Decoder decoder ->
-            decoder.skipBytes(4)
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can encode and decode byte array"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBinary([] as byte[])
-            encoder.writeBinary([1, 2, 3, 4] as byte[])
-            encoder.writeBinary([0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] as byte[], 2, 3)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readBinary() == [] as byte[]
-            assert decoder.readBinary() == [1, 2, 3, 4] as byte[]
-            assert decoder.readBinary() == [0xc3, 0xc4, 0xc5] as byte[]
-        }
-    }
-
-    def "decode fails when byte array cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeBinary([1, 2, 3, 4] as byte[])
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readBinary()
-        }
-
-        then:
-        thrown(EOFException)
-
-        when:
-        decode([] as byte[]) { Decoder decoder ->
-            decoder.readBinary()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    @Unroll
-    def "can encode and decode long #value"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeLong(value)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readLong() == value
-        }
-
-        where:
-        value               | _
-        0                   | _
-        12                  | _
-        -1                  | _
-        0xff                | _
-        0xffdd              | _
-        0xffddcc            | _
-        0xffddccbb          | _
-        0xffddccbbaa        | _
-        0xffddccbbaa99      | _
-        0xffddccbbaa9988    | _
-        0x7fddccbbaa998877  | _
-        -0xff               | _
-        -0xffdd             | _
-        -0xffddcc           | _
-        -0xffddccbb         | _
-        -0xffddccbbaa       | _
-        -0xffddccbbaa99     | _
-        -0xffddccbbaa9988   | _
-        -0x7fddccbbaa998877 | _
-        Long.MAX_VALUE      | _
-        Long.MIN_VALUE      | _
-    }
-
-    def "decode fails when long cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeLong(0xa40745f3L)
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readLong()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can encode and decode a small long"() {
-        expect:
-        def bytesA = encode { Encoder encoder ->
-            encoder.writeSmallLong(a as long)
-        }
-        def bytesB = encode { Encoder encoder ->
-            encoder.writeSmallLong(b as long)
-        }
-        decode(bytesA) { Decoder decoder ->
-            assert decoder.readSmallLong() == a
-        }
-        decode(bytesB) { Decoder decoder ->
-            assert decoder.readSmallLong() == b
-        }
-        bytesA.length <= bytesB.length
-
-        where:
-        a                 | b
-        0                 | 0x1ff
-        0x2ff             | 0x1000
-        0x1000            | -1
-        Integer.MAX_VALUE | -1
-        Long.MAX_VALUE    | -1
-        Long.MAX_VALUE    | -0xc3412
-        Integer.MAX_VALUE | Integer.MIN_VALUE
-        Long.MAX_VALUE    | Long.MIN_VALUE
-    }
-
-    def "decode fails when small long cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeSmallLong(0xa40745f)
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readSmallLong()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    @Unroll
-    def "can encode and decode int #value"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeInt(value as int)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readInt() == value
-        }
-
-        where:
-        value             | _
-        0                 | _
-        12                | _
-        -1                | _
-        0xF               | _
-        0xFD              | _
-        0xFDD             | _
-        0xFFDD            | _
-        0xFFDDCC          | _
-        0x7FDDCCBB        | _
-        -0xFF             | _
-        Integer.MAX_VALUE | _
-        Integer.MIN_VALUE | _
-    }
-
-    def "decode fails when int cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeInt(0xa40745f)
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readInt()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can encode and decode a small int"() {
-        expect:
-        def bytesA = encode { Encoder encoder ->
-            encoder.writeSmallInt(a as int)
-        }
-        def bytesB = encode { Encoder encoder ->
-            encoder.writeSmallInt(b as int)
-        }
-        decode(bytesA) { Decoder decoder ->
-            assert decoder.readSmallInt() == a
-        }
-        decode(bytesB) { Decoder decoder ->
-            assert decoder.readSmallInt() == b
-        }
-        bytesA.length <= bytesB.length
-
-        where:
-        a                 | b
-        0                 | 0x1ff
-        0x2ff             | 0x1000
-        0x1000            | -1
-        Integer.MAX_VALUE | -1
-        Integer.MAX_VALUE | -0xc3412
-        Integer.MAX_VALUE | Integer.MIN_VALUE
-    }
-
-    def "decode fails when small int cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeSmallInt(0xa40745f)
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readSmallInt()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can encode and decode a boolean"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeBoolean(true)
-            encoder.writeBoolean(false)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readBoolean()
-            assert !decoder.readBoolean()
-        }
-    }
-
-    def "decode fails when boolean cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeBoolean(true)
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readBoolean()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "can encode and decode a string"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeString(value)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readString() == value.toString()
-        }
-
-        where:
-        value                            | _
-//        ""                               | _
-//        "all ascii"                      | _
-        "\u0000\u0101\u3100"             | _
-//        "${1 + 2}"                       | _
-//        new StringBuilder("some string") | _
-//        CharBuffer.wrap("a string")      | _
-//        (0..1000).join("-")              | _
-    }
-
-    def "decode fails when string cannot be fully read"() {
-        given:
-        def bytes = truncate { Encoder encoder ->
-            encoder.writeString("hi")
-        }
-
-        when:
-        decode(bytes) { Decoder decoder ->
-            decoder.readString()
-        }
-
-        then:
-        thrown(EOFException)
-    }
-
-    def "cannot encode a null string"() {
-        when:
-        encode { Encoder encoder ->
-            encoder.writeString(null)
-        }
-
-        then:
-        thrown(IllegalArgumentException)
-    }
-
-    def "can encode and decode a nullable string"() {
-        expect:
-        def bytes = encode { Encoder encoder ->
-            encoder.writeNullableString(value)
-        }
-        decode(bytes) { Decoder decoder ->
-            assert decoder.readNullableString() == value?.toString()
-        }
-
-        where:
-        value                            | _
-        null                             | _
-        ""                               | _
-        "all ascii"                      | _
-        "\u0000\u0101\u3100"             | _
-        "${1 + 2}"                       | _
-        new StringBuilder("some string") | _
-    }
-
-    abstract void encodeTo(OutputStream outputStream, Closure<Encoder> closure)
-
-    byte[] encode(Closure<Encoder> closure) {
-        def bytes = new ByteArrayOutputStream()
-        encodeTo(bytes, closure)
-        return bytes.toByteArray()
-    }
-
-    byte[] truncate(Closure<Encoder> closure) {
-        def bytes = new ByteArrayOutputStream()
-        encodeTo(bytes, closure)
-        def result = bytes.toByteArray()
-        if (result.length < 2) {
-            return [] as byte[]
-        }
-        return result[0..result.length - 2] as byte[]
-    }
-
-    abstract void decodeFrom(InputStream inputStream, Closure<Decoder> closure)
-
-    void decode(byte[] bytes, Closure<Decoder> closure) {
-        decodeFrom(new ByteArrayInputStream(bytes), closure)
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/BaseSerializerFactoryTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/BaseSerializerFactoryTest.groovy
deleted file mode 100644
index 7660f0a..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/BaseSerializerFactoryTest.groovy
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.messaging.serialize
-
-import spock.lang.Specification
-
-class BaseSerializerFactoryTest extends Specification {
-    def factory = new BaseSerializerFactory()
-
-    def "uses efficient serialization for Strings"() {
-        def encoder = Mock(Encoder)
-        def decoder = Mock(Decoder)
-
-        when:
-        def serializer = factory.getSerializerFor(String)
-        serializer.write(encoder, "hi")
-        serializer.read(decoder)
-
-        then:
-        1 * encoder.writeString("hi")
-        1 * decoder.readString() >> "bye"
-    }
-
-    def "uses efficient serialization for Files"() {
-        def encoder = Mock(Encoder)
-        def decoder = Mock(Decoder)
-
-        when:
-        def serializer = factory.getSerializerFor(File)
-        serializer.write(encoder, new File("some-file"))
-        def result = serializer.read(decoder)
-
-        then:
-        result == new File("some-file")
-        1 * encoder.writeString("some-file")
-        1 * decoder.readString() >> "some-file"
-    }
-
-    def "uses efficient serialization for Long"() {
-        def encoder = Mock(Encoder)
-        def decoder = Mock(Decoder)
-
-        when:
-        def serializer = factory.getSerializerFor(Long)
-        serializer.write(encoder, 123L)
-        serializer.read(decoder)
-
-        then:
-        1 * encoder.writeLong(123L)
-        1 * decoder.readLong() >> 456L
-    }
-
-    def "uses Java serialization for unknown type"() {
-        expect:
-        factory.getSerializerFor(Thing) instanceof DefaultSerializer
-    }
-
-    class Thing { }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerRegistryTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerRegistryTest.groovy
deleted file mode 100644
index 8d1e0b7..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerRegistryTest.groovy
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.messaging.serialize
-
-class DefaultSerializerRegistryTest extends SerializerSpec {
-    def longSerializer = Stub(Serializer) {
-        read(_) >> { Decoder decoder ->
-            return decoder.readSmallLong()
-        }
-        write(_, _) >> { Encoder encoder, Long value ->
-            encoder.writeSmallLong(value)
-        }
-    }
-    def intSerializer = Stub(Serializer) {
-        read(_) >> { Decoder decoder ->
-            return decoder.readSmallInt()
-        }
-        write(_, _) >> { Encoder encoder, Integer value ->
-            encoder.writeSmallInt(value)
-        }
-    }
-
-    def "serializes type information with a value"() {
-        given:
-        def registry = new DefaultSerializerRegistry()
-        registry.register(Long, longSerializer)
-        registry.register(Integer, intSerializer)
-        def serializer = registry.build()
-
-        expect:
-        serialize(123L, serializer) == 123L
-        serialize(123, serializer) == 123
-    }
-
-    def "does not write type tag when there is only one registered type"() {
-        given:
-        def registry = new DefaultSerializerRegistry()
-        registry.register(Long, longSerializer)
-        def serializer1 = registry.build()
-        registry.register(Integer, intSerializer)
-        def serializer2 = registry.build()
-
-        expect:
-        toBytes(123L, serializer1).length + 1 == toBytes(123L, serializer2).length
-    }
-
-    def "type information is independent of the order that types are registered"() {
-        given:
-        def registry = new DefaultSerializerRegistry()
-        registry.register(Long, longSerializer)
-        registry.register(Integer, intSerializer)
-        def serializer1 = registry.build()
-
-        and:
-        registry = new DefaultSerializerRegistry()
-        registry.register(Integer, intSerializer)
-        registry.register(Long, longSerializer)
-        def serializer2 = registry.build()
-
-        expect:
-        fromBytes(toBytes(123L, serializer1), serializer2) == 123L
-        fromBytes(toBytes(123, serializer1), serializer2) == 123
-    }
-
-    def "cannot write value with type that has not been registered"() {
-        given:
-        def registry = new DefaultSerializerRegistry()
-        registry.register(Long, longSerializer)
-        registry.register(Integer, intSerializer)
-        def serializer = registry.build()
-
-        when:
-        toBytes(123.4, serializer)
-
-        then:
-        thrown(IllegalArgumentException)
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerTest.groovy
deleted file mode 100644
index 69eee88..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/DefaultSerializerTest.groovy
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.messaging.serialize
-
-class DefaultSerializerTest extends SerializerSpec {
-    def canSerializeAndDeserializeObject() {
-        GroovyClassLoader classLoader = new GroovyClassLoader(getClass().classLoader)
-        DefaultSerializer serializer = new DefaultSerializer(classLoader)
-
-        Class cl = classLoader.parseClass('package org.gradle.cache; class TestObj implements Serializable { }')
-        Object o = cl.newInstance()
-
-        when:
-        def r = serialize(o, serializer)
-
-        then:
-        cl.isInstance(r)
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/ListSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/ListSerializerTest.groovy
deleted file mode 100644
index 81de1bf..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/ListSerializerTest.groovy
+++ /dev/null
@@ -1,45 +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.messaging.serialize
-
-class ListSerializerTest extends SerializerSpec {
-
-    def stringSerializer = new NullSafeStringSerializer()
-
-    def "serialize list of strings"() {
-        when:
-        def serializer = new ListSerializer(stringSerializer)
-
-        then:
-        serialize(["one", "two", "three"], serializer) == ["one", "two", "three"]
-    }
-
-    def "serialize list of longs"() {
-        when:
-        def serializer = new ListSerializer(BaseSerializerFactory.LONG_SERIALIZER)
-
-        then:
-        serialize([10L, 5L, 99L], serializer) == [10L, 5L, 99L]
-    }
-
-    def "serialize null entry"() {
-        when:
-        def serializer = new ListSerializer(stringSerializer)
-
-        then:
-        serialize(["one", null, "three"], serializer) == ["one", null, "three"]
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/LongSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/LongSerializerTest.groovy
deleted file mode 100644
index 6701422..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/LongSerializerTest.groovy
+++ /dev/null
@@ -1,34 +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.messaging.serialize
-
-class LongSerializerTest extends SerializerSpec {
-
-    def serializer = new LongSerializer()
-
-    def "writes and reads Longs"() {
-        expect:
-        serialize(144L, serializer) == 144L
-    }
-
-    def "does not permit null"() {
-        when:
-        serializer.write(new OutputStreamBackedEncoder(new ByteArrayOutputStream()), null)
-
-        then:
-        thrown(IllegalArgumentException)
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/MapSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/MapSerializerTest.groovy
deleted file mode 100644
index 6f61569..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/MapSerializerTest.groovy
+++ /dev/null
@@ -1,47 +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.messaging.serialize
-
-class MapSerializerTest extends SerializerSpec {
-
-    def stringSerializer = new NullSafeStringSerializer()
-
-    def "retains order of serialized entries"() {
-        when:
-        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
-        Map values = serialize([10L: "one", 2L: "two", 30L: "three"], serializer) as Map
-
-        then:
-        values.keySet() as List == [10L, 2L, 30L]
-    }
-
-    def "serialize map"() {
-        when:
-        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
-
-        then:
-        serialize([1L: "one", 2L: "two", 3L: "three"], serializer) == [1L: "one", 2L: "two", 3L: "three"]
-    }
-
-    def "serialize null value"() {
-        when:
-        def serializer = new MapSerializer(BaseSerializerFactory.LONG_SERIALIZER, stringSerializer)
-
-        then:
-        serialize([1L: "one", 2L: null], serializer) == [1L: "one", 2L: null]
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/SetSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/SetSerializerTest.groovy
deleted file mode 100644
index dc77bf4..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/SetSerializerTest.groovy
+++ /dev/null
@@ -1,47 +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.messaging.serialize
-
-class SetSerializerTest extends SerializerSpec {
-
-    def stringSerializer = new NullSafeStringSerializer()
-
-    def "serialize set of strings"() {
-        when:
-        def serializer = new SetSerializer(stringSerializer)
-
-        then:
-        serialize(["one", "two", "three"] as Set, serializer) as List == ["one", "two", "three"]
-    }
-
-    def "serialize set of longs"() {
-        when:
-        def serializer = new SetSerializer(BaseSerializerFactory.LONG_SERIALIZER)
-
-        then:
-        serialize([1L, 5L, 99L] as Set, serializer) as List == [1L, 5L, 99L]
-    }
-
-    def "serialize null entry"() {
-        when:
-        def serializer = new SetSerializer(stringSerializer)
-
-        then:
-        serialize(["one", null, "three"] as Set, serializer) as List == ["one", null, "three"]
-    }
-
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/StreamBackedCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/StreamBackedCodecTest.groovy
deleted file mode 100644
index c86b54d..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/StreamBackedCodecTest.groovy
+++ /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.messaging.serialize
-
-class StreamBackedCodecTest extends AbstractCodecTest {
-    @Override
-    void encodeTo(OutputStream outputStream, Closure<Encoder> closure) {
-        def encoder = new OutputStreamBackedEncoder(outputStream)
-        closure.call(encoder)
-    }
-
-    @Override
-    void decodeFrom(InputStream inputStream, Closure<Decoder> closure) {
-        def decoder = new InputStreamBackedDecoder(inputStream)
-        closure.call(decoder)
-    }
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/kryo/KryoBackedCodecTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/kryo/KryoBackedCodecTest.groovy
deleted file mode 100644
index bc9c2b1..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/messaging/serialize/kryo/KryoBackedCodecTest.groovy
+++ /dev/null
@@ -1,98 +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.messaging.serialize.kryo
-
-import org.gradle.messaging.serialize.AbstractCodecTest
-import org.gradle.messaging.serialize.Decoder
-import org.gradle.messaging.serialize.Encoder
-
-class KryoBackedCodecTest extends AbstractCodecTest {
-    @Override
-    void encodeTo(OutputStream outputStream, Closure<Encoder> closure) {
-        def encoder = new KryoBackedEncoder(outputStream, 10)
-        closure.call(encoder)
-        encoder.flush()
-    }
-
-    @Override
-    void decodeFrom(InputStream inputStream, Closure<Decoder> closure) {
-        def decoder = new KryoBackedDecoder(inputStream, 10)
-        closure.call(decoder)
-    }
-
-    def "can query write and read positions"() {
-        def outstr = new ByteArrayOutputStream()
-        def encoder = new KryoBackedEncoder(outstr)
-
-        expect:
-        encoder.writePosition == 0
-
-        when:
-        encoder.writeBoolean(true)
-        encoder.writeByte(12 as byte)
-        encoder.writeLong(1234)
-
-        then:
-        encoder.writePosition == 10
-        outstr.size() == 0
-
-        when:
-        encoder.flush()
-
-        then:
-        encoder.writePosition == 10
-        outstr.size() == 10
-
-        when:
-        encoder.writeBytes(new byte[4098])
-
-        then:
-        encoder.writePosition == 4108
-        outstr.size() == 4106
-
-        when:
-        encoder.close()
-
-        then:
-        encoder.writePosition == 4108
-        outstr.size() == 4108
-
-        when:
-        def instr = new ByteArrayInputStream(outstr.toByteArray())
-        def decoder = new KryoBackedDecoder(instr)
-
-        then:
-        instr.available() == 4108
-        decoder.readPosition == 0
-
-        when:
-        decoder.readBoolean()
-        decoder.readByte()
-        decoder.readLong()
-
-        then:
-        instr.available() == 12 // decoder has buffered from instr
-        decoder.readPosition == 10
-
-        when:
-        decoder.skipBytes(4098)
-
-        then:
-        instr.available() == 0
-        decoder.readPosition == 4108
-    }
-}
diff --git a/subprojects/messaging/src/testFixtures/groovy/org/gradle/internal/serialize/SerializerSpec.groovy b/subprojects/messaging/src/testFixtures/groovy/org/gradle/internal/serialize/SerializerSpec.groovy
new file mode 100644
index 0000000..9dcfbc6
--- /dev/null
+++ b/subprojects/messaging/src/testFixtures/groovy/org/gradle/internal/serialize/SerializerSpec.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.internal.serialize
+
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder
+import spock.lang.Specification
+
+class SerializerSpec extends Specification {
+    public <T> T serialize(T value, Serializer<T> serializer) {
+        def bytes = toBytes(value, serializer)
+        return fromBytes(bytes, serializer)
+    }
+
+    public <T> T fromBytes(byte[] bytes, Serializer<T> serializer) {
+        return serializer.read(new KryoBackedDecoder(new ByteArrayInputStream(bytes)))
+    }
+
+    public <T> byte[] toBytes(T value, Serializer<T> serializer) {
+        def bytes = new ByteArrayOutputStream()
+        def encoder = new KryoBackedEncoder(bytes)
+        serializer.write(encoder, value)
+        encoder.flush()
+
+        return bytes.toByteArray()
+    }
+}
diff --git a/subprojects/messaging/src/testFixtures/groovy/org/gradle/messaging/serialize/SerializerSpec.groovy b/subprojects/messaging/src/testFixtures/groovy/org/gradle/messaging/serialize/SerializerSpec.groovy
deleted file mode 100644
index 4d7fd6e..0000000
--- a/subprojects/messaging/src/testFixtures/groovy/org/gradle/messaging/serialize/SerializerSpec.groovy
+++ /dev/null
@@ -1,39 +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.messaging.serialize
-
-import spock.lang.Specification
-
-class SerializerSpec extends Specification {
-    def serialize(def value, Serializer serializer) {
-        def bytes = toBytes(value, serializer)
-        return serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes)))
-    }
-
-    def fromBytes(def bytes, Serializer serializer) {
-        return serializer.read(new InputStreamBackedDecoder(new ByteArrayInputStream(bytes)))
-    }
-
-    def toBytes(def value, Serializer serializer) {
-        def bytes = new ByteArrayOutputStream()
-        def encoder = new OutputStreamBackedEncoder(bytes)
-        serializer.write(encoder, value)
-        encoder.flush()
-
-        return bytes.toByteArray()
-    }
-}
diff --git a/subprojects/model-core/model-core.gradle b/subprojects/model-core/model-core.gradle
new file mode 100644
index 0000000..42ab655
--- /dev/null
+++ b/subprojects/model-core/model-core.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The model management core.
+ */
+apply plugin: "groovy"
+
+dependencies {
+    compile libraries.slf4j_api
+    compile project(':baseServices')
+    compile project(':baseServicesGroovy')
+    compile libraries.groovy
+    compile libraries.guava
+    compile libraries.commons_lang
+    compile libraries.jcip
+    compile libraries.asm
+
+    testCompile libraries.groovy
+
+    integTestCompile project(":core")
+    integTestRuntime project(':plugins')
+
+    testFixturesCompile project(":internalTesting")
+    testFixturesCompile project(":core")
+}
+
+useTestFixtures()
+useClassycle()
+strictCompile()
\ No newline at end of file
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ConfigurationCycleIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ConfigurationCycleIntegrationTest.groovy
new file mode 100644
index 0000000..b191555
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ConfigurationCycleIntegrationTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class ConfigurationCycleIntegrationTest extends AbstractIntegrationSpec {
+
+    def "configuration cycle error contains information useful for troubleshooting"() {
+        given:
+        EnableModelDsl.enable(executer)
+
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class Rules extends RuleSource {
+                @Model
+                String first(@Path("second") String second) {
+                    "foo"
+                }
+
+                @Model
+                String second() {
+                    "bar"
+                }
+
+                @Model
+                String third(@Path("first") String first) {
+                    "fizz"
+                }
+
+                @Mutate
+                void connectTasksToFirst(CollectionBuilder<Task> tasks, @Path("first") String first) {
+                }
+            }
+
+            apply type: Rules
+
+            model {
+                second {
+                    $("third")
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("""A cycle has been detected in model rule dependencies. References forming the cycle:
+Rules#first(java.lang.String) parameter 1 (path: second)
+  \\--- model.second @ build file '${buildFile}' line 29, column 17 @ line 30 (path: third)
+    \\--- Rules#third(java.lang.String) parameter 1 (path: first)
+      \\--- Rules#first(java.lang.String)""")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelReuseIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelReuseIntegrationTest.groovy
new file mode 100644
index 0000000..c43881e
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelReuseIntegrationTest.groovy
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import org.gradle.integtests.fixtures.executer.DaemonGradleExecuter
+import org.gradle.model.internal.persist.ReusingModelRegistryStore
+import spock.lang.Ignore
+
+//@IgnoreIf({ GradleContextualExecuter.isDaemon() })
+ at Ignore("failing builds randomly on windows: http://builds.gradle.org/viewLog.html?buildId=276330&buildTypeId=Gradle_Master_Coverage_WindowsJava18_2")
+class ModelReuseIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer = new DaemonGradleExecuter(distribution, testDirectoryProvider)
+        executer.beforeExecute {
+            requireIsolatedDaemons()
+            withArgument("-D$ReusingModelRegistryStore.TOGGLE=true")
+            withDaemonIdleTimeoutSecs(5)
+        }
+        EnableModelDsl.enable(executer)
+    }
+
+    def cleanup() {
+        executer.withArgument("--stop").run()
+    }
+
+    String hashFor(String prefix) {
+        (output =~ /$prefix: (\d+)/)[0][1]
+    }
+
+    def "model elements are reused when toggle is enabled and when using daemon"() {
+        when:
+        buildScript """
+            class Rules extends $RuleSource.name {
+                @$Model.name
+                List<String> vals() {
+                  []
+                }
+            }
+
+            pluginManager.apply Rules
+
+            model {
+                tasks {
+                    create("show") {
+                        doLast {
+                            println "vals: " + System.identityHashCode(\$("vals"))
+                            println "task: " + System.identityHashCode(it)
+                        }
+                    }
+                }
+            }
+        """
+
+
+        then:
+        succeeds "show"
+        ":show" in executedTasks
+        output.contains ReusingModelRegistryStore.BANNER
+
+        and:
+        def valHash = hashFor("vals")
+        def taskHash = hashFor("task")
+
+        when:
+        succeeds "show"
+
+        then:
+        valHash == hashFor("vals")
+        taskHash != hashFor("task")
+    }
+
+    def "can enable reuse with the component model"() {
+        when:
+        buildScript """
+            plugins {
+              id "org.gradle.jvm-component"
+              id "org.gradle.java-lang"
+            }
+
+            model {
+                components {
+                    create("main", JvmLibrarySpec)
+                }
+            }
+        """
+
+        then:
+        succeeds "build"
+        succeeds "build"
+    }
+
+    def "can enable reuse with the variants benchmark"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Flavour {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface Type {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            abstract class Variant {
+                abstract Flavour getFlavour()
+                abstract void setFlavour(Flavour flavour)
+
+                abstract Type getType()
+                abstract void setType(Type type)
+
+                String getName() {
+                    flavour.name + type.name
+                }
+            }
+
+            class VariantsRuleSource extends RuleSource {
+                @Model
+                void flavours(ManagedSet<Flavour> flavours) {
+                }
+
+                @Model
+                void types(ManagedSet<Type> types) {
+                }
+
+                @Model
+                void variants(ManagedSet<Variant> variants, ManagedSet<Flavour> flavours, ManagedSet<Type> types) {
+                    flavours.each { flavour ->
+                        types.each { type ->
+                            variants.create {
+                                it.flavour = flavour
+                                it.type = type
+                            }
+                        }
+                    }
+                }
+
+                @Mutate
+                void addVariantTasks(CollectionBuilder<Task> tasks, ManagedSet<Variant> variants) {
+                    variants.each {
+                        tasks.create(it.name)
+                    }
+                }
+
+                @Mutate
+                void addAllVariantsTasks(CollectionBuilder<Task> tasks, ManagedSet<Variant> variants) {
+                    tasks.create("allVariants") { allVariants ->
+                        variants.each {
+                            allVariants.dependsOn it.name
+                        }
+                    }
+                }
+            }
+
+            apply type: VariantsRuleSource
+
+            model {
+                flavours {
+                    create {
+                        name = "flavour1"
+                    }
+                    create {
+                        name = "flavour2"
+                    }
+                }
+                types {
+                    create {
+                        name = "type1"
+                    }
+                    create {
+                        name = "type2"
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "allVariants"
+        output.contains ReusingModelRegistryStore.BANNER
+        succeeds "allVariants"
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingFailureIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingFailureIntegrationTest.groovy
new file mode 100644
index 0000000..b307997
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingFailureIntegrationTest.groovy
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+/**
+ * Tests the information provided when a model rule fails to bind.
+ *
+ * @see ModelRuleBindingValidationIntegrationTest
+ */
+class ModelRuleBindingFailureIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "unbound rules are reported"() {
+        given:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class MyThing1 {}
+                static class MyThing2 {}
+                static class MyThing3 {}
+
+                static class Rules extends RuleSource {
+                    @Model
+                    MyThing1 thing1(MyThing2 thing2) {
+                        new MyThing1()
+                    }
+
+
+                    @Mutate
+                    void mutateThing2(MyThing2 thing2, MyThing3 thing3) {
+
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("""The following model rules are unbound:
+  MyPlugin\$Rules#mutateThing2(MyPlugin\$MyThing2, MyPlugin\$MyThing3)
+    Mutable:
+      - <unspecified> (MyPlugin\$MyThing2) parameter 1
+    Immutable:
+      - <unspecified> (MyPlugin\$MyThing3) parameter 2
+  MyPlugin\$Rules#thing1(MyPlugin\$MyThing2)
+    Immutable:
+      - <unspecified> (MyPlugin\$MyThing2) parameter 1""")
+    }
+
+    def "unbound dsl rules are reported"() {
+        given:
+        buildScript """
+
+            model {
+                foo.bar {
+
+                }
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("""The following model rules are unbound:
+  model.foo.bar @ build file '${buildFile}' line 4, column 17
+    Mutable:
+      - foo.bar (java.lang.Object)""")
+    }
+
+    def "suggestions are provided for unbound rules"() {
+        given:
+        buildScript """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks) {
+                        tasks.create("foobar")
+                        tasks.create("raboof")
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks.foonar {
+                }
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("""The following model rules are unbound:
+  model.tasks.foonar @ build file '${buildFile}' line 18, column 17
+    Mutable:
+      - tasks.foonar (java.lang.Object) - suggestions: tasks.foobar""")
+    }
+
+    def "ambiguous binding integration test"() {
+        given:
+        buildScript """
+            import org.gradle.model.*
+
+            class Plugin1 {
+                static class Rules extends RuleSource {
+                    @Model
+                    String s1() {
+                        "foo"
+                    }
+                }
+            }
+
+            class Plugin2 {
+                static class Rules extends RuleSource {
+                    @Model
+                    String s2() {
+                        "bar"
+                    }
+                }
+            }
+
+            class Plugin3 {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void m(String s) {
+                        "foo"
+                    }
+                }
+            }
+
+            apply type: Plugin1
+            apply type: Plugin2
+            apply type: Plugin3
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription("A problem occurred configuring root project")
+        failure.assertHasCause("There is a problem with model rule Plugin3\$Rules#m(java.lang.String).")
+        failure.assertHasCause("""Type-only model reference of type java.lang.String (parameter 1) is ambiguous as multiple model elements are available for this type:
+  - s1 (created by: Plugin1\$Rules#s1())
+  - s2 (created by: Plugin2\$Rules#s2())""")
+    }
+
+    def "incompatible type binding"() {
+        given:
+        buildScript """
+            import org.gradle.model.*
+
+            class Plugin1 {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void addTasks(@Path("tasks") Integer s1) {
+
+                    }
+                }
+            }
+
+            apply type: Plugin1
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("There is a problem with model rule Plugin1\$Rules#addTasks(java.lang.Integer).")
+        failure.assertHasCause("""Model reference to element 'tasks' with type java.lang.Integer (parameter 1) is invalid due to incompatible types.
+This element was created by Project.<init>.tasks() and can be mutated as the following types:
+  - org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>
+  - org.gradle.api.tasks.TaskContainer (or assignment compatible type thereof)""")
+    }
+
+    def "unbound inputs for creator are reported"() {
+        given:
+        buildScript """
+            import org.gradle.model.*
+
+            class Rules extends RuleSource {
+                @Model
+                Integer foo(@Path("bar") Integer bar) {
+                    22
+                }
+            }
+
+            apply type: Rules
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("""The following model rules are unbound:
+  Rules#foo(java.lang.Integer)
+    Immutable:
+      - bar (java.lang.Integer) parameter 1""")
+    }
+
+    def "unbound rule for project that has no needed tasks does not cause error"() {
+        when:
+        settingsFile << "include 'a', 'b'"
+        file("a/build.gradle") << "model { foo {} }"
+
+        then:
+        succeeds ":b:dependencies"
+        fails ":a:dependencies"
+        failure.assertHasDescription("A problem occurred configuring project ':a'")
+        failure.assertHasCause("The following model rules are unbound:")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingValidationIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingValidationIntegrationTest.groovy
new file mode 100644
index 0000000..3112c5c
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleBindingValidationIntegrationTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+/**
+ * Tests aspects of model rule binding validation such as when/why validation is run.
+ *
+ * @see ModelRuleBindingFailureIntegrationTest
+ */
+class ModelRuleBindingValidationIntegrationTest extends AbstractIntegrationSpec {
+
+    def "model rule that does not bind specified for project not used in the build does not fail the build"() {
+        when:
+        settingsFile << """
+            include ":used", ":unused"
+        """
+
+        file("unused/build.gradle") << """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class Rules extends RuleSource {
+                @Mutate
+                void unbound(CollectionBuilder<Task> tasks, String unbound) {
+                }
+            }
+
+            apply type: Rules
+        """
+
+        then:
+        succeeds ":used:tasks"
+    }
+
+    def "entire model is validated, not just what is 'needed'"() {
+        when:
+        buildScript """
+            class Rules extends RuleSource {
+              @Model
+              String s1(Integer iDontExist) {
+                "foo"
+              }
+            }
+
+            pluginManager.apply Rules
+        """
+
+        then:
+        fails "help"
+        failure.assertHasCause("""The following model rules are unbound:
+  Rules#s1(java.lang.Integer)
+    Immutable:
+      - <unspecified> (java.lang.Integer) parameter 1""")
+    }
+
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleCachingIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleCachingIntegrationTest.groovy
new file mode 100644
index 0000000..3712be0
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleCachingIntegrationTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.model.internal.inspect.ModelRuleExtractor
+import spock.lang.IgnoreIf
+
+ at IgnoreIf({ !GradleContextualExecuter.longLivingProcess })
+class ModelRuleCachingIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer.requireIsolatedDaemons()
+        buildFile << """
+            def ruleCache = project.services.get($ModelRuleExtractor.name).cache
+            def initialSize = ruleCache.size()
+            gradle.buildFinished { println "### extracted new rules: \${ruleCache.size() > initialSize}" }
+        """
+    }
+
+    boolean getNewRulesExtracted() {
+        def match = output =~ /.*### extracted new rules: (true|false).*/
+        match[0][1] == "true"
+    }
+
+    def "rules extracted from core plugins are reused across builds when using the daemon"() {
+        given:
+        buildFile << '''
+            apply plugin: 'java-lang'
+        '''
+
+        when:
+        run()
+
+        then:
+        newRulesExtracted
+
+        when:
+        run()
+
+        then:
+        !newRulesExtracted
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleSamplesIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..20b7328
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleSamplesIntegrationTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.junit.Rule
+
+class ModelRuleSamplesIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule Sample sample = new Sample(testDirectoryProvider)
+
+    @UsesSample("modelRules/modelDsl")
+    def "dsl creation example works"() {
+        when:
+        sample sample
+
+        then:
+        succeeds "hello"
+        output.contains("Hello John Smith!")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleValidationIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleValidationIntegrationTest.groovy
new file mode 100644
index 0000000..888938d
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ModelRuleValidationIntegrationTest.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.model
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class ModelRuleValidationIntegrationTest extends AbstractIntegrationSpec {
+
+    def "invalid model name produces error message"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model(" ")
+                    List<String> strings() {
+                      []
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'MyPlugin']")
+        failure.assertHasCause("Path of declared model element created by rule MyPlugin\$Rules#strings() is invalid.")
+        failure.assertHasCause("Model element name ' ' has illegal first character ' ' (names must start with an ASCII letter or underscore)")
+    }
+
+    def "model name can be at nested path"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model("foo. bar")
+                    List<String> strings() {
+                      []
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'MyPlugin']")
+        failure.assertHasCause("Path of declared model element created by rule MyPlugin\$Rules#strings() is invalid.")
+        failure.assertHasCause("Model path 'foo. bar' is invalid due to invalid name component")
+        failure.assertHasCause("Model element name ' bar' has illegal first character ' ' (names must start with an ASCII letter or underscore)")
+    }
+
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/MutationRuleApplicationOrderIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/MutationRuleApplicationOrderIntegrationTest.groovy
new file mode 100644
index 0000000..eaa6b14
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/MutationRuleApplicationOrderIntegrationTest.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class MutationRuleApplicationOrderIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildScript '''
+            class MutationRecorder {
+                def mutations = []
+            }
+
+            class EchoTask extends DefaultTask {
+                MutationRecorder recorder
+
+                @TaskAction
+                void printMessages() {
+                    println "mutations: ${recorder.mutations.join(", ")}"
+                }
+            }
+        '''
+    }
+
+    def "mutation rules from inner source classes applied via their common parent are executed in the order specified by class names of these rule sources"() {
+        when:
+        buildFile << '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MultipleRuleSources implements Plugin<Project> {
+                static class B extends RuleSource {
+                    @Mutate
+                    void b(MutationRecorder recorder) {
+                        recorder.mutations << "b"
+                    }
+                }
+
+                static class A extends RuleSource {
+                    @Model
+                    MutationRecorder recorder() {
+                        new MutationRecorder()
+                    }
+
+                    @Mutate
+                    void a(MutationRecorder recorder) {
+                        recorder.mutations << "a"
+                    }
+
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks, MutationRecorder recorderInput) {
+                        tasks.create("echo", EchoTask) {
+                            recorder = recorderInput
+                        }
+                    }
+                }
+
+                void apply(Project project) {}
+            }
+            apply type: MultipleRuleSources
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "mutations: a, b"
+    }
+
+    def "mutation rules are executed in the order of application for rule sources and order of declaration for dsl defined rules"() {
+        when:
+        EnableModelDsl.enable(executer)
+        buildFile << '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class FirstSource extends RuleSource {
+                @Mutate
+                void first(MutationRecorder recorder, @Path("firstInput") String input) {
+                    recorder.mutations << "first source"
+                }
+            }
+
+
+            class SecondSource extends RuleSource {
+                @Model
+                MutationRecorder recorder() {
+                    new MutationRecorder()
+                }
+
+                @Model
+                String secondInput() {
+                    ""
+                }
+
+                @Mutate
+                void second(MutationRecorder recorder, @Path("secondInput") String input) {
+                    recorder.mutations << "second source"
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks, MutationRecorder recorderInput) {
+                    tasks.create("echo", EchoTask) {
+                        recorder = recorderInput
+                    }
+                }
+            }
+
+            class FirstInputProvider extends RuleSource {
+                @Model
+                String firstInput() {
+                    ""
+                }
+            }
+
+            apply type: FirstSource
+            model {
+                recorder {
+                    $("firstInput")
+                    mutations << "first dsl"
+                }
+                recorder {
+                    $("secondInput")
+                    mutations << "second dsl"
+                }
+            }
+            apply type: SecondSource
+            apply type: FirstInputProvider
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "mutations: first source, first dsl, second dsl, second source"
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/PluginRuleSourceIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/PluginRuleSourceIntegrationTest.groovy
new file mode 100644
index 0000000..e483c54
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/PluginRuleSourceIntegrationTest.groovy
@@ -0,0 +1,434 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class PluginRuleSourceIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "plugin class can expose model rules"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    List<String> strings() {
+                      []
+                    }
+
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks, List<String> strings) {
+                        tasks.create("value") {
+                            it.doLast {
+                                println "value: $strings"
+                            }
+                        }
+                    }
+
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                strings {
+                    add "foo"
+                }
+            }
+        '''
+
+        then:
+        succeeds "value"
+
+        and:
+        output.contains "value: [foo]"
+    }
+
+    def "configuration in script is not executed if not needed"() {
+        given:
+        buildScript '''
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    List<String> strings() {
+                      []
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                strings {
+                    throw new RuntimeException();
+                }
+            }
+
+            task value
+        '''
+
+        expect:
+        succeeds "value"
+    }
+
+    def "informative error message when rules are invalid"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+                class Rules extends RuleSource {
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'MyPlugin']")
+        failure.assertHasCause("Type MyPlugin\$Rules is not a valid model rule source: enclosed classes must be static and non private")
+    }
+
+    def "informative error message when two plugins declare model at the same path"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { "foo" }
+                }
+            }
+
+            class MyOtherPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { "foo" }
+                }
+            }
+
+            apply type: MyPlugin
+            apply type: MyOtherPlugin
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'MyOtherPlugin']")
+        failure.assertHasCause("Cannot create 'string' using creation rule 'MyOtherPlugin\$Rules#string()' as the rule 'MyPlugin\$Rules#string()' is already registered to create this model element.")
+    }
+
+    def "informative error message when two plugins declare model at the same path and model is already created"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { "foo" }
+                }
+            }
+
+            class MyOtherPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { "bar" }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks {
+                    $("string")
+                }
+            }
+
+            task loadPlugin {
+                doLast {
+                    apply type: MyOtherPlugin
+                }
+            }
+        '''
+
+        then:
+        fails "loadPlugin"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'MyOtherPlugin']")
+        failure.assertHasCause("Cannot create 'string' using creation rule 'MyOtherPlugin\$Rules#string()' as the rule 'MyPlugin\$Rules#string()' has already been used to create this model element.")
+    }
+
+    def "informative error message when creation rule throws"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { throw new RuntimeException("oh no!") }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks {
+                    $("string")
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: MyPlugin\$Rules#string()")
+        failure.assertHasCause("oh no!")
+    }
+
+    def "informative error message when dsl mutation rule throws"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() { "foo" }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                string {
+                    throw new RuntimeException("oh no!")
+                }
+                tasks {
+                    $("string")
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: model.string")
+        failure.assertHasCause("oh no!")
+    }
+
+    def "model creator must provide instance"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Model
+                    String string() {
+                      null
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks {
+                    $("string")
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("error executing model rule: MyPlugin\$Rules#string() - rule returned null")
+    }
+
+    def "plugin applied by plugin can contribute rules"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyBasePlugin {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void strings(List<String> strings) {
+                      strings << "foo"
+                    }
+                }
+            }
+
+            class MyPlugin implements Plugin<Project> {
+                void apply(Project project) {
+                    project.pluginManager.apply(MyBasePlugin)
+                }
+
+                static class Rules extends RuleSource {
+                    @Model
+                    List<String> strings() {
+                      []
+                    }
+
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks, List<String> strings) {
+                        tasks.create("value") {
+                            it.doLast {
+                                println "value: $strings"
+                            }
+                        }
+                    }
+                }
+            }
+
+            apply plugin: MyPlugin
+        '''
+
+        then:
+        succeeds "value"
+
+        and:
+        output.contains "value: [foo]"
+    }
+
+    def "configuration made to a project extension during afterEvaluate() is visible to rule sources"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyExtension {
+                String value = "original"
+            }
+
+            class MyPlugin implements Plugin<Project> {
+                void apply(Project project) {
+                    project.extensions.create("myExtension", MyExtension)
+                }
+
+                static class Rules extends RuleSource {
+                    @Model
+                    MyExtension myExtension(ExtensionContainer extensions) {
+                        extensions.getByType(MyExtension)
+                    }
+
+                    @Model
+                    String value(MyExtension myExtension) {
+                        myExtension.value
+                    }
+
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks, String value) {
+                        tasks.create("value") {
+                            it.doLast {
+                                println "value: $value"
+                            }
+                        }
+                    }
+                }
+            }
+
+            apply plugin: MyPlugin
+
+            project.afterEvaluate {
+                project.myExtension.value = "configured"
+            }
+        '''
+
+        then:
+        succeeds "value"
+
+        and:
+        output.contains "value: configured"
+    }
+
+    def "rule can depend on a concrete task type"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks, @Path("tasks.injected") Exec execTask) {
+                        tasks.create("name") {
+                            it.doLast {
+                                println "name: ${execTask.name}"
+                            }
+                        }
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            task injected(type: Exec)
+        '''
+
+        then:
+        succeeds "name"
+
+        and:
+        output.contains "name: injected"
+    }
+
+    def "plugin application fails if rule source constructor throws exception"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class Rules extends RuleSource {
+                Rules() {
+                    throw new RuntimeException("failing constructor")
+                }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Failed to apply plugin [class 'Rules']")
+        failure.assertHasCause("Type Rules is not a valid model rule source: instance creation failed")
+        failure.assertHasCause("failing constructor")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/ScopedRuleSourceIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ScopedRuleSourceIntegrationTest.groovy
new file mode 100644
index 0000000..92f9c6f
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/ScopedRuleSourceIntegrationTest.groovy
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class ScopedRuleSourceIntegrationTest extends AbstractIntegrationSpec {
+
+    def "rule source can be applied in scope of a collection builder element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MessageTask extends DefaultTask {
+                String message = "default"
+
+                @TaskAction
+                void printMessages() {
+                    println "message: $message"
+                }
+            }
+
+            class EchoRules extends RuleSource {
+                @Mutate
+                void mutateEcho(Task echo, String message) {
+                    echo.message = message
+                }
+            }
+
+            class Rules extends RuleSource {
+                @Model
+                String message() {
+                    "foo"
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("echo", MessageTask)
+                    tasks.named("echo", EchoRules)
+                }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "message: foo"
+    }
+
+    def "scoped rule execution failure yields useful error message"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class ThrowingRule extends RuleSource {
+                @Mutate
+                void badRule(Task echo) {
+                    throw new RuntimeException("I'm broken")
+                }
+            }
+
+            class Rules extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.named("taskWithThrowingRuleApplied", ThrowingRule)
+                    tasks.create("taskWithThrowingRuleApplied")
+                }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: ThrowingRule#badRule(org.gradle.api.Task)")
+        failure.assertHasCause("I'm broken")
+    }
+
+    def "invalid rule definitions of scoped rules are reported with a message helping to identify the faulty rule"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class InvalidRuleSource extends RuleSource {
+                @Mutate
+                String invalidRule(Task echo) {
+                }
+            }
+
+            class Rules extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.named("taskWithInvalidRuleSourceApplied", InvalidRuleSource)
+                    tasks.create("taskWithInvalidRuleSourceApplied")
+                }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: Rules#addTasks(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>)")
+        failure.assertHasCause("InvalidRuleSource#invalidRule(org.gradle.api.Task) is not a valid model rule method")
+    }
+
+    def "unbound inputs of scoped rules are reported and their scope is shown"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class UnboundRuleSource extends RuleSource {
+                @Mutate
+                void unboundRule(String string, Integer integer, @Path("some.inner.path") String withInnerPath) {
+                }
+            }
+
+            class Rules extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.named("taskWithUnboundRuleSourceApplied", UnboundRuleSource)
+                    tasks.create("taskWithUnboundRuleSourceApplied")
+                }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("""The following model rules are unbound:
+  UnboundRuleSource#unboundRule(java.lang.String, java.lang.Integer, java.lang.String)
+    Mutable:
+      - <unspecified> (java.lang.String) parameter 1 in scope of 'tasks.taskWithUnboundRuleSourceApplied'
+    Immutable:
+      - <unspecified> (java.lang.Integer) parameter 2
+      - tasks.taskWithUnboundRuleSourceApplied.some.inner.path (java.lang.String) parameter 3""")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/TaskCreationIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/TaskCreationIntegrationTest.groovy
new file mode 100644
index 0000000..f4d9254
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/TaskCreationIntegrationTest.groovy
@@ -0,0 +1,675 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+
+class TaskCreationIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        buildFile << """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MessageTask extends DefaultTask {
+                String message = "default"
+
+                @TaskAction
+                void printMessages() {
+                    println "\$name message: \$message"
+                }
+            }
+"""
+    }
+
+    def "can use rule method to create tasks from model"() {
+        given:
+        buildFile << """
+            class MyModel {
+                List<String> tasks = []
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                MyModel myModel() {
+                    new MyModel()
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks, MyModel myModel) {
+                    myModel.tasks.each { n ->
+                        tasks.create(n) {
+                          description = "task \$n"
+                        }
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                myModel {
+                    tasks << "a" << "b"
+                }
+            }
+        """
+
+        when:
+        succeeds "tasks"
+
+        then:
+        output.contains "a - task a"
+        output.contains "b - task b"
+    }
+
+    def "can use rule DSL to create tasks"() {
+        given:
+        buildFile << """
+            model {
+                tasks {
+                    a {
+                        description = 'task a'
+                    }
+                    a(MessageTask)
+                    b(MessageTask) {
+                        description = 'task b'
+                    }
+                    c(Task) {
+                        description = 'task c'
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "tasks"
+
+        then:
+        output.contains "a - task a"
+        output.contains "b - task b"
+        output.contains "c - task c"
+    }
+
+    def "can configure tasks using rule DSL"() {
+        given:
+        buildFile << """
+            class MyMessage {
+                String message
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                MyMessage myMessage() {
+                    new MyMessage()
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks, MyMessage myMessage) {
+                    ['foo', 'bar'].each { n ->
+                        tasks.create(n, MessageTask) {
+                            message = "\${myMessage.message} \${name}: "
+                        }
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks.bar {
+                    message += "bar message!"
+                }
+                tasks {
+                    foo {
+                        message += 'foo message!'
+                    }
+                }
+                myMessage {
+                    message = "task"
+                }
+            }
+        """
+
+        when:
+        succeeds "foo", "bar"
+
+        then:
+        output.contains "foo message: task foo: foo message!"
+        output.contains "bar message: task bar: bar message!"
+    }
+
+    def "can configure tasks using rule methods taking some input"() {
+        given:
+        buildFile << """
+            class MyMessage {
+                String message
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                MyMessage myMessage() {
+                    new MyMessage()
+                }
+
+                @Mutate
+                void customMessage(@Path('tasks.bar') MessageTask task) {
+                    task.message += ' from'
+                }
+
+                @Defaults
+                void prepareMessage(@Path('tasks.bar') MessageTask task) {
+                    task.message = "task bar: "
+                }
+
+                @Finalize
+                void tweakCustomMessage(@Path('tasks.bar') MessageTask task) {
+                    task.message += " \$task.name"
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<MessageTask> tasks, MyMessage myMessage) {
+                    tasks.create('bar') {
+                        message += myMessage.message
+                    }
+                    tasks.create('foo') {
+                        message = 'foo'
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                myMessage {
+                    message = "hi"
+                }
+            }
+        """
+
+        when:
+        succeeds "foo", "bar"
+
+        then:
+        output.contains "foo message: foo"
+        output.contains "bar message: task bar: hi from bar"
+    }
+
+    def "can validate tasks using rule methods"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Validate
+                void checkTask(@Path('tasks.bar') MessageTask task) {
+                    throw new RuntimeException("task is invalid!")
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    ['foo', 'bar'].each { n ->
+                        tasks.create(n, MessageTask)
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        fails "bar"
+
+        then:
+        failure.assertHasCause('Exception thrown while executing model rule: MyPlugin#checkTask(MessageTask)')
+        failure.assertHasCause('task is invalid!')
+    }
+
+    def "can use CollectionBuilder API from a method rule to apply rules to tasks"() {
+        given:
+        buildFile << """
+            class MyMessage {
+                String message
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                MyMessage myMessage() {
+                    new MyMessage()
+                }
+
+                @Mutate
+                void addTasks(CollectionBuilder<MessageTask> tasks) {
+                    ['foo', 'bar'].each { n ->
+                        tasks.create(n, MessageTask) {
+                            message = "\$message \$name"
+                        }
+                    }
+                }
+
+                @Defaults
+                void applyMessages(CollectionBuilder<MessageTask> tasks, MyMessage myMessage) {
+                    tasks.beforeEach {
+                        message = myMessage.message
+                    }
+                    tasks.all {
+                        message += " with"
+                    }
+                    tasks.afterEach {
+                        message += " message!"
+                    }
+                }
+
+                @Mutate
+                void cleanupMessages(CollectionBuilder<MessageTask> tasks) {
+                    tasks.named('bar') {
+                        message = "[\$message]"
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                myMessage {
+                    message = "task"
+                }
+            }
+        """
+
+        when:
+        succeeds "foo", "bar"
+
+        then:
+        output.contains "foo message: task foo with message!"
+        output.contains "bar message: [task bar with] message!"
+    }
+
+    def "can use rule DSL to apply rules to all tasks"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<MessageTask> tasks) {
+                    ['foo', 'bar'].each { n ->
+                        tasks.create(n, MessageTask) {
+                            message = "\$message \$name"
+                        }
+                    }
+
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks {
+                    def messageTasks = withType(MessageTask)
+                    messageTasks.beforeEach {
+                        message = "task"
+                    }
+                    messageTasks.all {
+                        message += " with"
+                    }
+                    messageTasks.afterEach {
+                        message += " message"
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "foo", "bar"
+
+        then:
+        output.contains "foo message: task foo with message"
+        output.contains "bar message: task bar with message"
+    }
+
+    def "tasks created using legacy DSL are visible to rules"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void applyMessages(CollectionBuilder<MessageTask> tasks) {
+                    tasks.afterEach {
+                        message += " message!"
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            task foo(type: MessageTask) { message = 'custom' }
+            task bar(type: MessageTask)
+        """
+
+        when:
+        succeeds "foo", "bar"
+
+        then:
+        output.contains "foo message: custom message!"
+        output.contains "bar message: default message!"
+    }
+
+    def "task initializer defined by rule is invoked before actions defined through legacy task container DSL"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<MessageTask> tasks) {
+                    tasks.create("foo") {
+                        message = "foo message"
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            tasks.withType(MessageTask).all {
+                message = "task \$message"
+            }
+        """
+
+        when:
+        succeeds "foo"
+
+        then:
+        output.contains "foo message: task foo message"
+    }
+
+    def "can configure dependencies between tasks using task name"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo")
+                    tasks.create("bar")
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks.bar {
+                    dependsOn "foo"
+                }
+            }
+        """
+
+        when:
+        succeeds "bar"
+
+        then:
+        executedTasks == [":foo", ":bar"]
+    }
+
+    def "task instantiation and configuration is deferred until required"() {
+        given:
+        buildFile << """
+            class SomeTask extends DefaultTask {
+                SomeTask() { println "\$name created" }
+            }
+
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<SomeTask> tasks) {
+                    tasks.create("foo") {
+                        println "\$name configured"
+                    }
+                    tasks.create("bar") {
+                        println "\$name configured"
+                    }
+                    tasks.beforeEach {
+                        println "\$name initialized"
+                    }
+                    println "tasks defined"
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        succeeds "bar", "foo"
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators("""tasks defined
+bar created
+bar initialized
+bar configured
+foo created
+foo initialized
+foo configured
+"""))
+    }
+
+    def "two rules attempt to create task"() {
+        given:
+        buildFile << """
+            class MyModel {
+                List<String> tasks = []
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                MyModel myModel() {
+                    new MyModel()
+                }
+
+                @Mutate
+                void addTasks1(CollectionBuilder<Task> tasks, MyModel myModel) {
+                    myModel.tasks.each { n ->
+                        tasks.create(n) {
+                          description = "task \$n"
+                        }
+                    }
+                }
+
+                @Mutate
+                void addTasks2(CollectionBuilder<Task> tasks, MyModel myModel) {
+                    myModel.tasks.each { n ->
+                        tasks.create(n) {
+                          description = "task \$n"
+                        }
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                myModel {
+                    tasks << "a" << "b"
+                }
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks2(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>, MyModel)")
+        failure.assertHasCause("Cannot create 'tasks.a' using creation rule 'MyPlugin#addTasks2(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>, MyModel) > create(a)' as the rule 'MyPlugin#addTasks1(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>, MyModel) > create(a)' is already registered to create this model element.")
+    }
+
+    def "cannot create tasks during config of task"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo") {
+                      tasks.create("bar")
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>) > create(foo)")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>' given to rule 'MyPlugin#addTasks(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>)'")
+    }
+
+    def "failure during task instantiation is reasonably reported"() {
+        given:
+        buildFile << """
+            class Faulty extends DefaultTask {
+                Faulty() {
+                    throw new RuntimeException("!")
+                }
+            }
+
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo", Faulty)
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>)")
+        failure.assertHasCause("Could not create task of type 'Faulty'")
+    }
+
+    def "failure during task initial configuration is reasonably reported"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo") {
+                        throw new RuntimeException("config failure")
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: MyPlugin#addTasks(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>)")
+        failure.assertHasCause("config failure")
+    }
+
+    def "failure during task configuration is reasonably reported"() {
+        given:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTasks(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo")
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                tasks.foo {
+                    throw new RuntimeException("config failure")
+                }
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: model.tasks.foo")
+        failure.assertHasCause("config failure")
+        failure.assertHasLineNumber(25)
+    }
+
+    def "task created in afterEvaluate() is visible to rules"() {
+        when:
+        buildFile << '''
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void fromAfterEvaluateTaskAvailable(TaskContainer tasks) {
+                    tasks.fromAfterEvaluate.value += " and from container rule"
+                }
+                @Mutate
+                void fromAfterEvaluateTaskAvailable(@Path("tasks.fromAfterEvaluate") Task task) {
+                    task.value += " and from rule"
+                }
+            }
+
+            apply type: MyPlugin
+
+            project.afterEvaluate {
+                project.tasks.create("fromAfterEvaluate") {
+                    ext.value = "from after evaluate"
+                    doLast {
+                        println "value: $value"
+                    }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromAfterEvaluate"
+
+        and:
+        output.contains "value: from after evaluate and from container rule and from rule"
+    }
+
+    def "registering a creation rule for a task that is already defined using legacy DSL"() {
+        when:
+        buildFile << """
+            class MyPlugin extends RuleSource {
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks) {
+                    tasks.create("foo")
+                }
+            }
+
+            apply type: MyPlugin
+
+            task foo {}
+        """
+
+        then:
+        fails "foo"
+
+        and:
+        failure.assertHasCause("Cannot create 'tasks.foo' using creation rule 'MyPlugin#addTask(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>) > create(foo)' as the rule 'Project.<init>.tasks.foo()' is already registered to create this model element.")
+    }
+
+    def "can create task with invalid model space name"() {
+        when:
+        buildFile << """
+            tasks.create(".").doFirst {}
+        """
+
+        run "."
+
+        then:
+        ":." in executedTasks
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/AbstractClassBackedManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/AbstractClassBackedManagedTypeIntegrationTest.groovy
new file mode 100644
index 0000000..da414c9
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/AbstractClassBackedManagedTypeIntegrationTest.groovy
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class AbstractClassBackedManagedTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def "rule can provide a managed model element backed by an abstract class"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class Person {
+                abstract String getName()
+                abstract void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.name = "foo"
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: foo")
+    }
+
+    def "managed type implemented as abstract class can have generative getters"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class Person {
+                abstract String getFirstName()
+                abstract void setFirstName(String firstName)
+                abstract String getLastName()
+                abstract void setLastName(String lastName)
+
+                String getName() {
+                    "$firstName $lastName"
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.firstName = "Alan"
+                    person.lastName = "Turing"
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: Alan Turing")
+    }
+
+    def "managed type implemented as abstract class can have a custom toString() implementation"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class CustomToString {
+                abstract String getStringRepresentation()
+                abstract void setStringRepresentation(String representation)
+
+                String toString() {
+                    stringRepresentation
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createElement(CustomToString element) {
+                    element.stringRepresentation = "custom string representation"
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, CustomToString element) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "element: ${element.toString()}"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("element: custom string representation")
+    }
+
+    def "calling setters from custom toString() implementation is not allowed"() {
+        when:
+        buildFile << '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class CustomToStringCallingSetter {
+                abstract String getStringRepresentation()
+                abstract void setStringRepresentation(String representation)
+
+                String toString() {
+                    stringRepresentation = "foo"
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createModelElementCallingSetterInCustomToString(CustomToStringCallingSetter element) {
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, CustomToStringCallingSetter element) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "element: ${element.toString()}"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails 'echo'
+
+        and:
+        failure.assertHasCause("Calling setters of a managed type on itself is not allowed")
+    }
+
+    private void defineCallsSetterInNonAbstractGetterClass() {
+        buildFile << '''
+            @Managed
+            abstract class CallsSetterInNonAbstractGetter {
+                abstract String getName()
+                abstract void setName(String name)
+
+                String getInvalidGenerativeProperty() {
+                    name = "foo"
+                }
+            }
+        '''
+    }
+
+    def "calling setters from non-abstract getters is not allowed"() {
+        when:
+        buildFile << '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+        '''
+        defineCallsSetterInNonAbstractGetterClass()
+        buildFile << '''
+            class RulePlugin extends RuleSource {
+                @Model
+                void createModelElementCallingSetterInNonAbstractGetter(CallsSetterInNonAbstractGetter element) {
+                }
+
+                @Mutate
+                void accessInvalidGenerativeProperty(CollectionBuilder<Task> tasks, CallsSetterInNonAbstractGetter element) {
+                    element.invalidGenerativeProperty
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails 'tasks'
+
+        and:
+        failure.assertHasCause("Calling setters of a managed type on itself is not allowed")
+    }
+
+    def "calling setters of super class from non-abstract getters is not allowed"() {
+        when:
+        buildFile << '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+        '''
+        defineCallsSetterInNonAbstractGetterClass()
+        buildFile << '''
+            @Managed
+            abstract class CallsSuperGetterInNonAbstractGetter extends CallsSetterInNonAbstractGetter {
+
+                String getInvalidGenerativeProperty() {
+                    super.getInvalidGenerativeProperty()
+                }
+
+                String getGenerativeProperty() {
+                    super.getGenerativeProperty()
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createModelElementCallingSuperGetterInNonAbstractGetter(CallsSuperGetterInNonAbstractGetter element) {
+                }
+
+                @Mutate
+                void accessInvalidGenerativeProperty(CollectionBuilder<Task> tasks, CallsSuperGetterInNonAbstractGetter element) {
+                    element.invalidGenerativeProperty
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails 'tasks'
+
+        and:
+        failure.assertHasCause("Calling setters of a managed type on itself is not allowed")
+    }
+
+    def "reports managed abstract type in missing property error message"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class Person {
+                abstract String getName()
+                abstract void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void someone(Person person) {
+                }
+
+                @Mutate
+                void tasks(CollectionBuilder<Task> tasks, Person person) {
+                    println person.unknown
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "help"
+
+        and:
+        failure.assertHasFileName("Build file '$buildFile'")
+        failure.assertHasLineNumber(18)
+        failure.assertHasCause("No such property: unknown for class: Person")
+    }
+
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ComplexManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ComplexManagedTypeIntegrationTest.groovy
new file mode 100644
index 0000000..6040b59
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ComplexManagedTypeIntegrationTest.groovy
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class ComplexManagedTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def "rule can provide a composite managed model element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                String getDisplayName()
+                void setDisplayName(String name)
+
+                OperatingSystem getOperatingSystem()
+            }
+
+            @Managed
+            interface OperatingSystem {
+                Family getFamily()
+
+                String getVersion()
+                void setVersion(String name)
+            }
+
+            @Managed
+            interface Family {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void somePlatform(Platform platform) {
+                    assert platform.displayName == null
+                    assert platform.operatingSystem != null
+                    assert platform.operatingSystem.version == null
+                    assert platform.operatingSystem.family != null
+                    assert platform.operatingSystem.family.name == null
+
+                    platform.displayName = "Microsoft Windows 8.1"
+                    platform.operatingSystem.version = "8.1"
+                    platform.operatingSystem.family.name = "windows"
+
+                    assert platform.displayName == "Microsoft Windows 8.1"
+                    assert platform.operatingSystem.version == "8.1"
+                    assert platform.operatingSystem.family.name == "windows"
+
+                    assert platform.operatingSystem.is(platform.operatingSystem)
+                    assert platform.operatingSystem.family.is(platform.operatingSystem.family)
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Platform platform) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "platform: $platform"
+                            println "os: $platform.operatingSystem"
+                            println "family: $platform.operatingSystem.family"
+                            println "platform name: $platform.operatingSystem.family.name $platform.operatingSystem.version"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("platform: Platform 'somePlatform'")
+        output.contains("os: OperatingSystem 'somePlatform.operatingSystem'")
+        output.contains("family: Family 'somePlatform.operatingSystem.family'")
+        output.contains("platform name: windows 8.1")
+    }
+
+    def "rule can apply defaults to a nested managed model element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                String getDisplayName()
+                void setDisplayName(String name)
+
+                OperatingSystem getOperatingSystem()
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {
+                    platform.displayName = "Microsoft Windows"
+                    platform.operatingSystem.name += " OS"
+                }
+
+                @Defaults
+                void defaultOs(@Path('platform.operatingSystem') OperatingSystem os) {
+                    os.name = "default"
+                }
+
+                @Finalize
+                void cleanUpOs(@Path('platform.operatingSystem') OperatingSystem os) {
+                    os.name += " x86"
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Platform platform) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "platform: $platform.operatingSystem.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("platform: default OS x86")
+    }
+
+    def "rule can provide a managed model element that references another managed model element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                String getDisplayName()
+                void setDisplayName(String name)
+
+                OperatingSystem getOperatingSystem()
+                void setOperatingSystem(OperatingSystem operatingSystem)
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface OperatingSystems {
+                OperatingSystem getWindows()
+                OperatingSystem getLinux()
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void os(OperatingSystems os) {
+                  os.windows.name = "windows"
+                  os.linux.name = "linux"
+                }
+
+                @Model
+                void windowsPlatform(Platform platform, OperatingSystems os) {
+                  platform.displayName = "Microsoft Windows"
+
+                  assert platform.operatingSystem == null
+
+                  platform.operatingSystem = os.linux
+                  assert platform.operatingSystem.is(os.linux)
+
+                  platform.operatingSystem = null
+                  assert platform.operatingSystem == null
+
+                  platform.operatingSystem = os.windows
+                  assert platform.operatingSystem.is(os.windows)
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Platform platform) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "platform: $platform"
+                            println "os: $platform.operatingSystem"
+                            println "platform name: $platform.operatingSystem.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("platform: Platform 'windowsPlatform'")
+        output.contains("os: OperatingSystem 'os.windows'")
+        output.contains("platform name: windows")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/CyclicalManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/CyclicalManagedTypeIntegrationTest.groovy
new file mode 100644
index 0000000..44e8b88
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/CyclicalManagedTypeIntegrationTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class CyclicalManagedTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def "managed types can have cyclical managed type references"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Parent {
+                String getName()
+                void setName(String name)
+
+                Child getChild()
+            }
+
+            @Managed
+            interface Child {
+                Parent getParent()
+                void setParent(Parent parent)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createParent(Parent parent) {
+                    parent.name = "parent"
+                    parent.child.parent = parent
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, Parent parent) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $parent.child.parent.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: parent")
+    }
+
+    def "managed types can have cyclical managed type references where more than two types constitute the cycle"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface A {
+                String getName()
+                void setName(String name)
+
+                B getB()
+            }
+
+            @Managed
+            interface B {
+                C getC()
+            }
+
+            @Managed
+            interface C {
+                A getA()
+                void setA(A a)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createA(A a) {
+                    a.name = "a"
+                    a.b.c.a = a
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, A a) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $a.b.c.a.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: a")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/EnumsInManagedModelIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/EnumsInManagedModelIntegrationTest.groovy
new file mode 100644
index 0000000..7978b4f
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/EnumsInManagedModelIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class EnumsInManagedModelIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "can use enums in managed model elements"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            enum Gender {
+                FEMALE, MALE, OTHER
+            }
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+
+              Gender getGender()
+              void setGender(Gender gender)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void p1(Person p1) {}
+            }
+
+            apply type: Rules
+
+            model {
+              p1 {
+                gender = "MALE" // relying on Groovy enum coercion here
+              }
+
+              tasks {
+                create("printGender") {
+                  it.doLast {
+                    println "gender: " + $("p1").gender
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printGender"
+
+        and:
+        output.contains 'gender: MALE'
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy
new file mode 100644
index 0000000..d110741
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class InterfaceBackedManagedTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def "rule method can define a managed model element backed by an interface"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface Names {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void name(Names names) {
+                    assert names == names
+                    assert names.name == null
+
+                    names.name = "foo"
+
+                    assert names.name == "foo"
+                }
+
+                @Model
+                void someone(Person person, Names names) {
+                    person.name = names.name
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "person: $person"
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("person: Person 'someone'")
+        output.contains("name: foo")
+    }
+
+    def "rule method can apply defaults to a managed model element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class Names {
+                String name
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                Names name() {
+                    return new Names(name: "before")
+                }
+
+                @Model
+                void person(Person person) {
+                    person.name += " init"
+                }
+
+                @Defaults
+                void beforePerson(Person person, Names names) {
+                    person.name = names.name
+                }
+
+                @Finalize
+                void afterPerson(Person person) {
+                    person.name += " after"
+                }
+
+                @Mutate
+                void configurePerson(Person person) {
+                    person.name += " configure"
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: before init configure after")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "managed type implemented as interface can have generative getter default methods"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+
+            @Managed
+            interface Person {
+                String getFirstName();
+                void setFirstName(String firstName);
+                String getLastName();
+                void setLastName(String lastName);
+
+                default String getName() {
+                    return getFirstName() + " " + getLastName();
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.setFirstName("Alan");
+                    person.setLastName("Turing");
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo", task -> {
+                        task.doLast(unused -> {
+                            System.out.println(String.format("name: %s", person.getName()));
+                        });
+                    });
+                }
+            }
+        '''
+
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: Alan Turing")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "generative getters implemented as default methods cannot call setters"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+
+            @Managed
+            interface Person {
+                String getFirstName();
+                void setFirstName(String firstName);
+
+                default String getName() {
+                    setFirstName("foo");
+                    return getFirstName();
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("accessGenerativeName", task -> {
+                        task.doLast(unused -> {
+                            person.getName();
+                        });
+                    });
+                }
+            }
+        '''
+
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "accessGenerativeName"
+
+        and:
+        failure.assertHasCause("Calling setters of a managed type on itself is not allowed")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "non-abstract setters implemented as default interface methods are not allowed"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+
+            @Managed
+            interface Person {
+                String getName();
+                default void setName(String firstName) {
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                }
+
+                @Mutate
+                void linkPersonToTasks(CollectionBuilder<Task> tasks, Person person) {
+                }
+            }
+        '''
+
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Invalid managed model type Person: non-abstract setters are not allowed (invalid method: void Person#setName(java.lang.String))")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "non-mutative non-abstract methods implemented as default interface methods are not allowed"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+
+            @Managed
+            interface Person {
+                default void foo() {
+                }
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                }
+
+                @Mutate
+                void linkPersonToTasks(CollectionBuilder<Task> tasks, Person person) {
+                }
+            }
+        '''
+
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Invalid managed model type Person: only paired getter/setter methods are supported (invalid methods: void Person#foo())")
+    }
+
+    def "reports managed interface type in missing property error message"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void someone(Person person) {
+                }
+
+                @Mutate
+                void tasks(CollectionBuilder<Task> tasks, Person person) {
+                    println person.unknown
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "help"
+
+        and:
+        failure.assertHasFileName("Build file '$buildFile'")
+        failure.assertHasLineNumber(18)
+        failure.assertHasCause("No such property: unknown for class: Person")
+    }
+
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelMutationIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelMutationIntegrationTest.groovy
new file mode 100644
index 0000000..9838e8a
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelMutationIntegrationTest.groovy
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class InvalidManagedModelMutationIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "mutating managed inputs of a rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void person(Person person) {
+                }
+
+                @Model
+                String name(Person person) {
+                    person.name = "bar"
+                }
+
+                @Mutate
+                void addDependencyOnName(CollectionBuilder<Task> tasks, String name) {
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#name(Person)")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Person' given to rule 'RulePlugin#name(Person)")
+    }
+
+    def "mutating composite managed inputs of a rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Pet {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface Person {
+                Pet getPet()
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void person(Person person) {
+                }
+
+                @Mutate
+                void tryToModifyCompositeSubjectOfAnotherRule(CollectionBuilder<Task> tasks, Person person) {
+                    person.pet.name = "foo"
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToModifyCompositeSubjectOfAnotherRule")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Pet' given to rule 'RulePlugin#tryToModifyCompositeSubjectOfAnotherRule(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>, Person)'")
+    }
+
+    def "mutating managed inputs of a dsl rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void person(Person person) {
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                    $("person").name = "foo"
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: model.tasks")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Person' given to rule 'model.tasks @ build file")
+    }
+
+    def "mutating managed objects outside of a creation rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class Holder {
+                static Person person
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void person(Person person) {
+                    Holder.person = person
+                }
+
+                @Mutate
+                void tryToModifyManagedObject(CollectionBuilder<Task> tasks, Person person) {
+                    Holder.person.name = "foo"
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToModifyManagedObject")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Person' given to rule 'RulePlugin#person(Person)'")
+    }
+
+    def "mutating composite managed objects outside of a creation rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Pet {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface Person {
+                Pet getPet()
+            }
+
+            class Holder {
+                static Person person
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void person(Person person) {
+                    Holder.person = person
+                }
+
+                @Mutate
+                void tryToModifyManagedObject(CollectionBuilder<Task> tasks, Person person) {
+                    Holder.person.pet.name = "foo"
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToModifyManagedObject")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Pet' given to rule 'RulePlugin#person(Person)'")
+    }
+
+    def "mutating managed objects referenced by another managed object outside of a creation rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Pet {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface Person {
+                Pet getPet()
+                void setPet(Pet pet)
+            }
+
+            class Holder {
+                static Person person
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void pet(Pet pet) {
+                }
+
+                @Model
+                void person(Person person, Pet pet) {
+                    person.pet = pet
+                    Holder.person = person
+                }
+
+                @Mutate
+                void tryToModifyManagedObject(CollectionBuilder<Task> tasks, Person person) {
+                    Holder.person.pet.name = "foo"
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToModifyManagedObject")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'Pet' given to rule 'RulePlugin#person(Person, Pet)'")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelRuleIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelRuleIntegrationTest.groovy
new file mode 100644
index 0000000..13f5f20
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InvalidManagedModelRuleIntegrationTest.groovy
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Matchers
+
+class InvalidManagedModelRuleIntegrationTest extends AbstractIntegrationSpec{
+
+    def "provides a useful error message when setting an incompatible type on a managed instance in Groovy"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.setName(123)
+                }
+
+                @Mutate
+                void addDependencyOnPerson(CollectionBuilder<Task> tasks, Person person) {
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#createPerson(Person)")
+        failure.assertThatCause(Matchers.containsLine(Matchers.matchesRegexp(/No signature of method: .*\.setName\(\) is applicable for argument types: \(java.lang.Integer\) values: \[123\]/)))
+    }
+
+    def "provides a useful error message when setting an incompatible type on a managed instance in Java"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+            import java.lang.reflect.*;
+
+            @Managed
+            interface Person {
+                String getName();
+                void setName(String name);
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+                    Method setter = person.getClass().getMethod("setName", String.class);
+                    setter.invoke(person, 123);
+                }
+
+                @Mutate
+                void addDependencyOnPerson(CollectionBuilder<Task> tasks, Person person) {
+                }
+            }
+        '''
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#createPerson(Person)")
+        failure.assertHasCause("argument type mismatch")
+    }
+
+    def "cannot assign a non-managed instance to a property of a managed type"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                OperatingSystem getOperatingSystem()
+                void setOperatingSystem(OperatingSystem operatingSystem)
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {
+                }
+
+                @Mutate
+                void addDependencyOnPlatform(CollectionBuilder<Task> tasks, Platform platform) {
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                platform {
+                    operatingSystem = new OperatingSystem() {
+                        String name
+                    }
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: model.platform")
+        failure.assertHasCause("Only managed model instances can be set as property 'operatingSystem' of class 'Platform'")
+    }
+
+    def "cannot use value type as subject of void model rule"() {
+        given:
+        when:
+        buildScript '''
+            import org.gradle.model.*
+
+            class Rules extends RuleSource {
+              @Model
+              void s(String s) {}
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Rules#s(java.lang.String) is not a valid model rule method: a void returning model element creation rule cannot take a value type as the first parameter, which is the element being created. Return the value from the method.")
+    }
+
+    def "provides a useful error message when an invalid managed type is used in a rule"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+                String getName()
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Declaration of model rule RulePlugin#createPerson(Person) is invalid")
+        failure.assertHasCause("Invalid managed model type Person: read only property 'name' has non managed type java.lang.String, only managed types can be used")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedModelPropertyTargetingRuleIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedModelPropertyTargetingRuleIntegrationTest.groovy
new file mode 100644
index 0000000..c8fc597
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedModelPropertyTargetingRuleIntegrationTest.groovy
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class ManagedModelPropertyTargetingRuleIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "rule can target structured property of managed element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                OperatingSystem getOperatingSystem()
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {
+                  platform.operatingSystem.name = "foo"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("platform.operatingSystem") OperatingSystem os) {
+                  tasks.create("fromPlugin") {
+                    doLast { println "fromPlugin: $os.name" }
+                  }
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                  create("fromScript") {
+                    it.doLast { println "fromScript: " + $("platform.operatingSystem").name }
+                  }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromPlugin", "fromScript"
+
+        and:
+        output.contains("fromPlugin: foo")
+        output.contains("fromScript: foo")
+    }
+
+    def "rule can target structured property of managed element as subject"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                OperatingSystem getOperatingSystem()
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {}
+
+                @Mutate
+                void setOsName(@Path("platform.operatingSystem") OperatingSystem os) {
+                  os.name = "foo"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("platform.operatingSystem") OperatingSystem os) {
+                  tasks.create("fromPlugin") {
+                    doLast { println "fromPlugin: $os.name" }
+                  }
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                  create("fromScript") {
+                    it.doLast { println "fromScript: " + $("platform.operatingSystem.name") }
+                  }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromPlugin", "fromScript"
+
+        and:
+        output.contains("fromPlugin: foo")
+        output.contains("fromScript: foo")
+    }
+
+    def "rule can target simple property of managed element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {
+                  platform.name = "foo"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("platform.name") String name) {
+                  tasks.create("fromPlugin") {
+                    doLast { println "fromPlugin: $name" }
+                  }
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                  create("fromScript") {
+                    it.doLast { println "fromScript: " + $("platform.name") }
+                  }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromPlugin", "fromScript"
+
+        and:
+        output.contains("fromPlugin: foo")
+        output.contains("fromScript: foo")
+    }
+
+    def "mutation rule can target property of managed element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                OperatingSystem getOperatingSystem()
+            }
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {
+                  platform.operatingSystem.name = "foo"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("platform.operatingSystem.name") String name) {
+                  tasks.create("fromPlugin") {
+                    doLast { println "fromPlugin: $name" }
+                  }
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                  create("fromScript") {
+                    it.doLast { println "fromScript: " + $("platform.operatingSystem.name") }
+                  }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromPlugin", "fromScript"
+
+        and:
+        output.contains("fromPlugin: foo")
+        output.contains("fromScript: foo")
+    }
+
+    def "creation rule can target property of managed element"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface OperatingSystem {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void operatingSystem(OperatingSystem operatingSystem) {
+                  operatingSystem.name = "foo"
+                }
+
+                @Model
+                String name(@Path("operatingSystem.name") String name) {
+                  name
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("name") String name) {
+                  tasks.create("echo") {
+                    doLast { println "name: $name" }
+                  }
+                }
+
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: foo")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedSetIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedSetIntegrationTest.groovy
new file mode 100644
index 0000000..5e3addc
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedSetIntegrationTest.groovy
@@ -0,0 +1,582 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import org.gradle.util.TextUtil
+
+class ManagedSetIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "rule can create a managed collection of interface backed managed model elements"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+            }
+
+            class Names {
+                List<String> names = []
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              Names names() {
+                return new Names(names: ["p1", "p2"])
+              }
+
+              @Model
+              void people(ManagedSet<Person> people, Names names) {
+                names.names.each { n ->
+                    people.create { name = n }
+                }
+              }
+
+              @Mutate void addPeople(ManagedSet<Person> people) {
+                people.create { name = "p3" }
+                people.create { name = "p4" }
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              people {
+                create { name = "p0" }
+              }
+
+              tasks {
+                create("printPeople") {
+                  doLast {
+                    def people = $("people")
+                    def names = people*.name.sort().join(", ")
+                    println "people: ${people.toString()}"
+                    println "names: $names"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printPeople"
+
+        and:
+        output.contains "people: org.gradle.model.collection.ManagedSet<Person> 'people'"
+        output.contains 'names: p0, p1, p2, p3, p4'
+    }
+
+    def "rule can create a managed collection of abstract class backed managed model elements"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class Person {
+              abstract String getName()
+              abstract void setName(String string)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void people(ManagedSet<Person> people) {
+                people.create { name = "p1" }
+                people.create { name = "p2" }
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              tasks {
+                create("printPeople") {
+                  doLast {
+                    def names = $("people")*.name.sort().join(", ")
+                    println "people: $names"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printPeople"
+
+        and:
+        output.contains 'people: p1, p2'
+    }
+
+    def "managed model type has property of collection of managed types"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+            }
+
+            @Managed
+            interface Group {
+              String getName()
+              void setName(String string)
+              ManagedSet<Person> getMembers()
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void group(Group group) {
+                group.name = "Women in computing"
+
+                group.members.create { name = "Ada Lovelace" }
+                group.members.create { name = "Grace Hooper" }
+
+                assert group.members.is(group.members)
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              tasks {
+                create("printGroup") {
+                  doLast {
+                    def members = $("group").members*.name.sort().join(", ")
+                    def name = $("group").name
+                    println "$name: $members"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printGroup"
+
+        and:
+        output.contains 'Women in computing: Ada Lovelace, Grace Hooper'
+    }
+
+    def "managed model type can reference a collection of managed types"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+            }
+
+            @Managed
+            interface Group {
+              String getName()
+              void setName(String string)
+              ManagedSet<Person> getMembers()
+              void setMembers(ManagedSet<Person> members)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void people(ManagedSet<Person> people) {
+                people.create { name = "Ada Lovelace" }
+                people.create { name = "Grace Hooper" }
+              }
+
+              @Model
+              void group(Group group, @Path("people") ManagedSet<Person> people) {
+                group.name = "Women in computing"
+
+                assert group.members == null
+
+                group.members = people
+
+                assert group.members.is(people)
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              tasks {
+                create("printGroup") {
+                  doLast {
+                    def members = $("group").members*.name.sort().join(", ")
+                    def name = $("group").name
+                    println "$name: $members"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printGroup"
+
+        and:
+        output.contains 'Women in computing: Ada Lovelace, Grace Hooper'
+    }
+
+    def "rule method can apply defaults to a managed set"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void people(ManagedSet<Person> people) {
+                println "initialize"
+              }
+
+              @Defaults void initialPeople(ManagedSet<Person> people) {
+                println "apply defaults"
+              }
+
+              @Mutate void customPeople(ManagedSet<Person> people) {
+                println "configure"
+              }
+
+              @Finalize void finalPeople(ManagedSet<Person> people) {
+                println "finalize"
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              tasks {
+                create("printPeople") {
+                  doLast {
+                    def people = $("people")
+                    println "people: $people"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printPeople"
+
+        and:
+        output.contains TextUtil.toPlatformLineSeparators('''apply defaults
+initialize
+configure
+finalize
+''')
+    }
+
+    def "creation and configuration of managed set elements is deferred until required"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            abstract class Person {
+              Person() {
+                println "construct Person"
+              }
+              abstract String getName()
+              abstract void setName(String string)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void people(ManagedSet<Person> people) {
+                people.create {
+                    println "configure p1"
+                    name = "p1"
+                }
+                println "p1 defined"
+              }
+
+              @Mutate void addPeople(ManagedSet<Person> people) {
+                people.create {
+                  println "configure p2"
+                  name = "p2"
+                }
+                println "p2 defined"
+              }
+            }
+
+            apply type: Rules
+
+            model {
+              people {
+                create {
+                  println "configure p3"
+                  name = "p3"
+                }
+                println "p3 defined"
+              }
+
+              tasks {
+                create("printPeople") {
+                  doLast {
+                    def names = $("people")*.name.sort().join(", ")
+                    println "people: $names"
+                  }
+                }
+              }
+            }
+        '''
+
+        then:
+        succeeds "printPeople"
+
+        and:
+        output.contains TextUtil.toPlatformLineSeparators('''
+p1 defined
+p2 defined
+p3 defined
+construct Person
+configure p1
+construct Person
+configure p2
+construct Person
+configure p3
+''')
+
+        output.contains "people: p1, p2, p3"
+    }
+
+    def "reports failure that occurs in collection item initializer"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+              String getName()
+              void setName(String string)
+            }
+
+            class Rules extends RuleSource {
+              @Model
+              void people(ManagedSet<Person> people) {
+                people.create {
+                    throw new RuntimeException("broken")
+                }
+              }
+
+              @Mutate
+              void tasks(CollectionBuilder<Task> tasks, ManagedSet<Person> people) { }
+            }
+
+            apply type: Rules
+        '''
+
+        then:
+        fails "printPeople"
+
+        and:
+        failure.assertHasDescription('A problem occurred configuring root project')
+        failure.assertHasCause('Exception thrown while executing model rule: Rules#people(org.gradle.model.collection.ManagedSet<Person>)')
+        failure.assertHasCause('broken')
+    }
+
+    def "read methods of ManagedSet throw exceptions when used in a creation rule"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void people(ManagedSet<Person> people) {
+                    people.size()
+                }
+
+                @Mutate
+                void addDependencyOnPeople(CollectionBuilder<Task> tasks, ManagedSet<Person> people) {
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#people")
+        failure.assertHasCause("Attempt to read a write only view of model of type 'org.gradle.model.collection.ManagedSet<Person>' given to rule 'RulePlugin#people(org.gradle.model.collection.ManagedSet<Person>)'")
+    }
+
+    def "read methods of ManagedSet throw exceptions when used in a mutation rule"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void people(ManagedSet<Person> people) {
+                }
+
+                @Mutate
+                void readPeople(ManagedSet<Person> people) {
+                    people.toList()
+                }
+
+                @Mutate
+                void addDependencyOnPeople(CollectionBuilder<Task> tasks, ManagedSet<Person> people) {
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#readPeople")
+        failure.assertHasCause("Attempt to read a write only view of model of type 'org.gradle.model.collection.ManagedSet<Person>' given to rule 'RulePlugin#readPeople(org.gradle.model.collection.ManagedSet<Person>)'")
+    }
+
+    def "mutating a managed set that is an input of a rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void people(ManagedSet<Person> people) {}
+
+                @Mutate
+                void tryToMutateInputManagedSet(CollectionBuilder<Task> tasks, ManagedSet<Person> people) {
+                    people.create {}
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToMutateInputManagedSet")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'org.gradle.model.collection.ManagedSet<Person>' given to rule 'RulePlugin#tryToMutateInputManagedSet(org.gradle.model.collection.CollectionBuilder<org.gradle.api.Task>, org.gradle.model.collection.ManagedSet<Person>)'")
+    }
+
+    def "mutating a managed set outside of a creation rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+            }
+
+            class Holder {
+                static ManagedSet<Person> people
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void people(ManagedSet<Person> people) {
+                    Holder.people = people
+                }
+
+                @Mutate
+                void tryToMutateManagedSetOutsideOfCreationRule(CollectionBuilder<Task> tasks, ManagedSet<Person> people) {
+                    Holder.people.create {}
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: RulePlugin#tryToMutateManagedSetOutsideOfCreationRule")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'org.gradle.model.collection.ManagedSet<Person>' given to rule 'RulePlugin#people(org.gradle.model.collection.ManagedSet<Person>)'")
+    }
+
+    def "mutating managed set which is an input of a DSL rule is not allowed"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Person {
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void people(ManagedSet<Person> people) {
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                    $("people").create {}
+                }
+            }
+        '''
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause("Exception thrown while executing model rule: model.tasks")
+        failure.assertHasCause("Attempt to mutate closed view of model of type 'org.gradle.model.collection.ManagedSet<Person>' given to rule 'model.tasks @ build file")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeImplementationClassCachingSpec.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeImplementationClassCachingSpec.groovy
new file mode 100644
index 0000000..0bf68f1
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeImplementationClassCachingSpec.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class ManagedTypeImplementationClassCachingSpec extends AbstractIntegrationSpec {
+
+    def "managed type implementation class is generated once for each type and reused"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Named {
+                String getName()
+                void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void first(Named first) {
+                }
+
+                @Model
+                void second(Named second) {
+                }
+
+                @Mutate
+                void addCompareImplementationClassesTask(CollectionBuilder<Task> tasks, @Path("first") Named first, @Path("second") Named second) {
+                    tasks.create("compareImplementationClasses") {
+                        it.doLast {
+                            println "implementation class is reused: ${first.getClass().is(second.getClass())}"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "compareImplementationClasses"
+
+        and:
+        output.contains("implementation class is reused: true")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeWithUnmanagedPropertiesIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeWithUnmanagedPropertiesIntegrationTest.groovy
new file mode 100644
index 0000000..e3a909d
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/ManagedTypeWithUnmanagedPropertiesIntegrationTest.groovy
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+class ManagedTypeWithUnmanagedPropertiesIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "can have unmanaged property"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class UnmanagedThing {
+              String value
+            }
+
+            @Managed
+            interface ManagedThing {
+                @Unmanaged
+                UnmanagedThing getUnmanaged()
+                void setUnmanaged(UnmanagedThing unmanaged)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void m(ManagedThing thing) {
+                    thing.unmanaged = new UnmanagedThing(value: "foo")
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, ManagedThing thing) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "value: $thing.unmanaged.value"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains('value: foo')
+    }
+
+    def "unmanaged property of managed type can be targeted by rules"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Platform {
+                @Unmanaged
+                OperatingSystem getOperatingSystem()
+                void setOperatingSystem(OperatingSystem os)
+            }
+
+            class OperatingSystem {
+                String name
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void platform(Platform platform) {}
+
+                @Mutate
+                void setOs(Platform platform) {
+                    platform.operatingSystem = new OperatingSystem()
+                }
+
+                @Mutate
+                void setOsName(@Path("platform.operatingSystem") OperatingSystem os) {
+                  os.name = "foo"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("platform.operatingSystem") OperatingSystem os) {
+                  tasks.create("fromPlugin") {
+                    doLast { println "fromPlugin: $os.name" }
+                  }
+                }
+            }
+
+            apply type: RulePlugin
+
+            model {
+                tasks {
+                  create("fromScript") {
+                    it.doLast { println "fromScript: " + $("platform.operatingSystem").name }
+                  }
+                }
+            }
+        '''
+
+        then:
+        succeeds "fromPlugin", "fromScript"
+
+        and:
+        output.contains("fromPlugin: foo")
+        output.contains("fromScript: foo")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PolymorphicManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PolymorphicManagedTypeIntegrationTest.groovy
new file mode 100644
index 0000000..e9dbb26
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PolymorphicManagedTypeIntegrationTest.groovy
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class PolymorphicManagedTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def "rule can provide a managed model element backed by an abstract class that implements interfaces"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            interface Named {
+                String getName()
+            }
+
+            @Managed
+            abstract class Person implements Named {
+                abstract void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.name = "foo"
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: foo")
+    }
+
+    def "rule can provide a managed model element backed by an abstract class that extends other classes"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            abstract class Named {
+                abstract String getName()
+            }
+
+            @Managed
+            abstract class Person extends Named {
+                abstract void setName(String name)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPerson(Person person) {
+                    person.name = "foo"
+                }
+
+                @Mutate
+                void addPersonTask(CollectionBuilder<Task> tasks, Person person) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $person.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: foo")
+    }
+
+    def "managed model interface can extend other interface"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            interface Named {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface NamedThing extends Named {
+                String getValue()
+                void setValue(String value)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void namedThing(NamedThing namedThing) {
+                    namedThing.name = "name"
+                    namedThing.value = "value"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, NamedThing namedThing) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $namedThing.name, value: $namedThing.value"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: name, value: value")
+    }
+
+    def "can depend on managed super type as input and subject"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            interface Named {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface ManagedNamed extends Named {
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void managedNamed(ManagedNamed namedThing) {
+                }
+
+                @Mutate
+                void setName(Named named) {
+                    named.name = "superclass"
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, Named named) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $named.name"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: superclass")
+    }
+
+    def "two managed types can extend the same parent"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            interface Named {
+                String getName()
+                void setName(String name)
+            }
+
+            @Managed
+            interface NamedString extends Named {
+                String getValue()
+                void setValue(String value)
+            }
+
+            @Managed
+            interface NamedInteger extends Named {
+                Integer getValue()
+                void setValue(Integer value)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void namedString(NamedString namedString) {
+                    namedString.name = "string"
+                    namedString.value = "some value"
+                }
+
+                @Model
+                void namedInteger(NamedInteger namedInteger) {
+                    namedInteger.name = "integer"
+                    namedInteger.value = 1234
+                }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, NamedString string, NamedInteger integer) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "name: $string.name, value: $string.value"
+                            println "name: $integer.name, value: $integer.value"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains("name: string, value: some value")
+        output.contains("name: integer, value: 1234")
+    }
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PrimitivesInManagedModelIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PrimitivesInManagedModelIntegrationTest.groovy
new file mode 100644
index 0000000..cf21279
--- /dev/null
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/PrimitivesInManagedModelIntegrationTest.groovy
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.managed
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class PrimitivesInManagedModelIntegrationTest extends AbstractIntegrationSpec {
+
+    def "values of primitive types and boxed primitive types are widened as usual when using groovy"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface PrimitiveTypes {
+                Long getLongPropertyFromInt()
+                void setLongPropertyFromInt(Long value)
+
+                Long getLongPropertyFromInteger()
+                void setLongPropertyFromInteger(Long value)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPrimitiveTypes(PrimitiveTypes primitiveTypes) {
+                    primitiveTypes.longPropertyFromInt = 123
+                    primitiveTypes.longPropertyFromInteger = new Integer(321)
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, final PrimitiveTypes primitiveTypes) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "from int: $primitiveTypes.longPropertyFromInt"
+                            println "from Integer: $primitiveTypes.longPropertyFromInteger"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "from int: 123"
+        output.contains "from Integer: 321"
+    }
+
+    def "values of primitive types are boxed as usual when using java"() {
+        when:
+        file('buildSrc/src/main/java/Rules.java') << '''
+            import org.gradle.api.*;
+            import org.gradle.model.*;
+            import org.gradle.model.collection.*;
+
+            @Managed
+            interface PrimitiveProperty {
+                Long getLongProperty();
+                void setLongProperty(Long value);
+                Integer getIntegerProperty();
+                void setIntegerProperty(Integer value);
+                Boolean getBooleanProperty();
+                void setBooleanProperty(Boolean value);
+                Character getCharacterProperty();
+                void setCharacterProperty(Character value);
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void createPrimitiveProperty(PrimitiveProperty primitiveProperty) {
+                    primitiveProperty.setLongProperty(123l);
+                    primitiveProperty.setIntegerProperty(456);
+                    primitiveProperty.setBooleanProperty(true);
+                    primitiveProperty.setCharacterProperty('a');
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, final PrimitiveProperty primitiveProperty) {
+                    tasks.create("echo", new Action<Task>() {
+                        public void execute(Task task) {
+                            task.doLast(new Action<Task>() {
+                                public void execute(Task unused) {
+                                    System.out.println(String.format("long: %d", primitiveProperty.getLongProperty()));
+                                    System.out.println(String.format("integer: %d", primitiveProperty.getIntegerProperty()));
+                                    System.out.println(String.format("boolean: %s", primitiveProperty.getBooleanProperty()));
+                                    System.out.println(String.format("character: %s", primitiveProperty.getCharacterProperty()));
+                                }
+                            });
+                        }
+                    });
+                }
+            }
+        '''
+
+        buildScript '''
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "long: 123"
+        output.contains "integer: 456"
+        output.contains "boolean: true"
+        output.contains "character: a"
+    }
+
+    def "can set/get properties of all supported unmanaged types"() {
+        when:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface AllSupportedUnmanagedTypes {
+                Boolean getBooleanProperty()
+
+                void setBooleanProperty(Boolean value)
+
+                Integer getIntegerProperty()
+
+                void setIntegerProperty(Integer value)
+
+                Long getLongProperty()
+
+                void setLongProperty(Long value)
+
+                Double getDoubleProperty()
+
+                void setDoubleProperty(Double value)
+
+                BigInteger getBigIntegerProperty()
+
+                void setBigIntegerProperty(BigInteger value)
+
+                BigDecimal getBigDecimalProperty()
+
+                void setBigDecimalProperty(BigDecimal value)
+
+                String getStringProperty()
+
+                void setStringProperty(String value)
+            }
+
+            class RulePlugin extends RuleSource {
+                @Model
+                void supportedUnmanagedTypes(AllSupportedUnmanagedTypes element) {
+                    element.booleanProperty = Boolean.TRUE
+                    element.integerProperty = Integer.valueOf(1)
+                    element.longProperty = Long.valueOf(2L)
+                    element.doubleProperty = Double.valueOf(3.3)
+                    element.bigIntegerProperty = new BigInteger("4")
+                    element.bigDecimalProperty = new BigDecimal("5.5")
+                    element.stringProperty = "test"
+                }
+
+                @Mutate
+                void addEchoTask(CollectionBuilder<Task> tasks, AllSupportedUnmanagedTypes element) {
+                    tasks.create("echo") {
+                        it.doLast {
+                            println "boolean: ${element.booleanProperty}"
+                            println "integer: ${element.integerProperty}"
+                            println "long: ${element.longProperty}"
+                            println "double: ${element.doubleProperty}"
+                            println "big integer: ${element.bigIntegerProperty}"
+                            println "big decimal: ${element.bigDecimalProperty}"
+                            println "string: ${element.stringProperty}"
+                        }
+                    }
+                }
+            }
+
+            apply type: RulePlugin
+        '''
+
+        then:
+        succeeds "echo"
+
+        and:
+        output.contains "boolean: true"
+        output.contains "integer: 1"
+        output.contains "long: 2"
+        output.contains "double: 3.3"
+        output.contains "big integer: 4"
+        output.contains "big decimal: 5.5"
+        output.contains "string: test"
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/ConfigurationCycleException.java b/subprojects/model-core/src/main/java/org/gradle/model/ConfigurationCycleException.java
new file mode 100644
index 0000000..bed1196
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/ConfigurationCycleException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+
+/**
+ * Thrown when a cycle is encountered while configuring a model element.
+ */
+ at Incubating
+public class ConfigurationCycleException extends GradleException {
+    public ConfigurationCycleException(String message) {
+        super(message);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Defaults.java b/subprojects/model-core/src/main/java/org/gradle/model/Defaults.java
new file mode 100644
index 0000000..985482b
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Defaults.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the {@link RuleSource} method rule carrying this annotation initializes the rule subject with default values.
+ * <p>
+ * Default rules execute after {@link Model} rules (i.e. after the element is created), but before {@link Mutate} rules.
+ * The first parameter of the rule is the rule subject, which is mutable for the duration of the rule.
+ * <p>
+ * Please see {@link RuleSource} for more information on method rules.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Defaults {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Finalize.java b/subprojects/model-core/src/main/java/org/gradle/model/Finalize.java
new file mode 100644
index 0000000..a40ce71
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Finalize.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.model;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the {@link RuleSource} method rule carrying this annotation finalizes the rule subject.
+ * <p>
+ * Finalize rules execute after {@link Mutate} rules, but before {@link Validate} rules.
+ * The first parameter of the rule is the rule subject, which is mutable for the duration of the rule.
+ * <p>
+ * Please see {@link RuleSource} for more information on method rules.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Finalize {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleDeclarationException.java b/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleDeclarationException.java
new file mode 100644
index 0000000..56bf320
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleDeclarationException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+/**
+ * Thrown when a model rule, or source of model rules, is declared in an invalid way.
+ */
+ at Incubating
+ at Contextual
+public class InvalidModelRuleDeclarationException extends GradleException {
+
+    public InvalidModelRuleDeclarationException(String message) {
+        super(message);
+    }
+
+    public InvalidModelRuleDeclarationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidModelRuleDeclarationException(ModelRuleDescriptor descriptor, Throwable cause) {
+        super("Declaration of model rule " + descriptor.toString() + " is invalid.", cause);
+        if (cause == null) {
+            throw new IllegalArgumentException("'cause' cannot be null");
+        }
+    }
+
+    public InvalidModelRuleDeclarationException(ModelRuleDescriptor descriptor, String message) {
+        super(String.format("%s is not a valid model rule method: %s", descriptor, message));
+    }
+
+    public InvalidModelRuleDeclarationException(ModelRuleDescriptor descriptor, String message, Throwable cause) {
+        super(String.format("%s is not a valid model rule method: %s", descriptor, message), cause);
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleException.java b/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleException.java
new file mode 100644
index 0000000..a716e59
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/InvalidModelRuleException.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.model;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+/**
+ * Thrown when there is a problem with the usage of a model rule.
+ * <p>
+ * This exception is different to {@link InvalidModelRuleDeclarationException} in that it signifies a problem
+ * with using a model rule in a particular context, whereas {@code InvalidModelRuleDeclarationException} signifies
+ * a problem with the declaration of the model rule itself (which therefore means that the rule could not be used in any context).
+ * <p>
+ * This exception should always have cause, that provides information about the actual problem.
+ */
+ at Incubating
+ at Contextual
+public class InvalidModelRuleException extends GradleException {
+
+    // The usage pattern of this exception providing the rule identity and the cause providing the detail is the
+    // way it is due to how we render chained exceptions on build failures.
+    // That is, because the information is usually dense, splitting things up this way provides better output.
+
+    private final String descriptor;
+
+    public InvalidModelRuleException(ModelRuleDescriptor descriptor, Throwable cause) {
+        super("There is a problem with model rule " + descriptor.toString() + ".", cause);
+        if (cause == null) {
+            throw new IllegalArgumentException("'cause' cannot be null");
+        }
+        this.descriptor = descriptor.toString();
+    }
+
+    public String getDescriptor() {
+        return descriptor;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Managed.java b/subprojects/model-core/src/main/java/org/gradle/model/Managed.java
new file mode 100644
index 0000000..9504e22
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Managed.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A managed type is transparent to the model space, and enforces immutability at the appropriate times in the object's lifecycle.
+ * <p>
+ * Gradle generates implementations for managed types.
+ * As such, managed types are declared either as interfaces or abstract classes.
+ * The generated implementation integrates with the model space mechanisms, and manages mutability.
+ * <p>
+ * Managed types are mostly behaviour-less, as they are data.
+ * Instances of managed types should effectively be considered value objects.
+ *
+ * <h3>Properties</h3>
+ * <p>
+ * Managed types declare their structure as properties, via getter and setter methods.
+ * Getter and setter methods are expected to conform to the well-known Java Bean naming conventions.
+ * A read/write “name” property would be expressed via the following methods:
+ * <pre>
+ * void setName(String name);
+ * String getName();
+ * </pre>
+ * <p>
+ * A getter and setter must be declared for each property that is not of a managed type or of {@link org.gradle.model.collection.ManagedSet}.
+ * For properties of managed types or of {@link org.gradle.model.collection.ManagedSet} the getter is mandatory and the setter is optional.
+ * If no setter is provided the property is considered inherent and defaults to an "empty" instance of the type.
+ *
+ * <h4>Supported property types</h4>
+ * <p>
+ * The following JDK types are allowed:
+ * <ul>
+ * <li>{@link String}</li>
+ * <li>{@link Boolean}</li>
+ * <li>{@link Character}</li>
+ * <li>{@link Integer}</li>
+ * <li>{@link Long}</li>
+ * <li>{@link Double}</li>
+ * <li>{@link java.math.BigInteger}</li>
+ * <li>{@link java.math.BigDecimal}</li>
+ * </ul>
+ * <p>
+ * All {@link Enum} types are also allowed.
+ * <p>
+ * Properties that are themselves of a managed type are also supported.
+ * <p>
+ * Currently, the only collection type that is supported is {@link org.gradle.model.collection.ManagedSet}.
+ * <p>
+ * Properties of any other type must have their getter annotated with {@link Unmanaged}.
+ * An unmanaged property is not transparent to the model infrastructure and is guaranteed to be immutable when realized.
+ *
+ * <h3>Inheritance</h3>
+ * <p>
+ * Managed types can be arranged into an inheritance hierarchy.
+ * Every type in the hierarchy must conform to the constraints of managed types.
+ *
+ * <h3>Calculated read-only properties</h3>
+ * <p>
+ * Managed types can contain getter methods that return calculated values, based on other properties.
+ * For example, a “name” property may return the concatenation of a “firstName” and “lastName” property.
+ * When using Java 8 or later, such properties can be implemented as interface default methods.
+ * Alternatively, the managed type can be implemented as an abstract class with the calculated property implemented as a non-abstract getter method.
+ * In both cases, the implementation of the calculated property getter may not call any setter method.
+ *
+ * <h3>Abstract classes</h3>
+ * <p>
+ * A managed type can be implemented as an abstract class.
+ * All property getters and setters must be declared {@code abstract} (with the exception of calculated read-only properties).
+ * The class cannot contain instance variables, constructors, or any methods that are not a getter or setter.
+ *
+ * <h3>Creating managed model elements</h3>
+ * <p>
+ * Please see {@link Model} for information on creating model elements of managed types.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+ at Incubating
+public @interface Managed {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Model.java b/subprojects/model-core/src/main/java/org/gradle/model/Model.java
new file mode 100644
index 0000000..5ffbd61
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Model.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the {@link RuleSource} method rule carrying this annotation creates a new top level element in the model space.
+ * <p>
+ * The method must advertise a name and type for the model element.
+ * The name is defined either by the name of the method, or the {@link #value} of this annotation.
+ * The type is defined differently depending on whether the new element is {@link Managed} or not.
+
+ * <h3>Creating managed model elements</h3>
+ * <p>
+ * If the element is to be of a managed type, the method must return {@code void} and receive the newly created instance as the <b>first</b> parameter.
+ * All other parameters are considered <i>inputs</i>.
+ * <p>
+ * It is an error for a {@code @Model} rule to return {@code void} and specify a non-managed type as the first parameter.
+ * It is an error for a {@code @Model} rule to return {@code void} and for the first parameter to be annotated with {@link Path}.
+ * It is an error for a {@code @Model} rule to specify a managed type as the return type.
+
+ * <h3>Creating non-managed model elements</h3>
+ * <p>
+ * If the element is to be of a non-managed type, the method must return the newly created instance.
+ * All parameters are considered <i>inputs</i>.
+ *
+ * Please see {@link RuleSource} for more information on method rules.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Model {
+
+    /**
+     * Denotes the name by which the model element will be available.
+     * <p>
+     * If the value is the empty string, the exact name of the annotated method will be used.
+     * <p>
+     * The value must:
+     * <p>
+     * <ul>
+     * <li>Start with a lower case letter</li>
+     * <li>Contain only ASCII letters, numbers and the '_' character</li>
+     * </ul>
+     * <p>
+     * This restriction also applies when the name is being derived from the method name.
+     * </p>
+     *
+     * @return the name by which the model element will be available
+     */
+    String value() default "";
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/ModelRuleBindingException.java b/subprojects/model-core/src/main/java/org/gradle/model/ModelRuleBindingException.java
new file mode 100644
index 0000000..483c31b
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/ModelRuleBindingException.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.model;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+
+/**
+ * Thrown when there is a problem binding the model element references of a model rule.
+ * <p>
+ * Should always be thrown as the cause of a {@link org.gradle.model.InvalidModelRuleException}.
+ */
+ at Incubating
+public class ModelRuleBindingException extends GradleException {
+
+    public ModelRuleBindingException(String message) {
+        super(message);
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/ModelViewClosedException.java b/subprojects/model-core/src/main/java/org/gradle/model/ModelViewClosedException.java
new file mode 100644
index 0000000..29b8db2
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/ModelViewClosedException.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.model;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+/**
+ * Thrown when at attempt is made to mutate a subject of a rule after the rule has completed.
+ * <p>
+ * This can potentially happen when a reference to the subject is retained during a rule and then used afterwards,
+ * Such as when an anonymous inner class or closure “closes over” the subject.
+ */
+ at Incubating
+public class ModelViewClosedException extends GradleException {
+
+    public ModelViewClosedException(ModelType<?> type, ModelRuleDescriptor ruleDescriptor) {
+        super(String.format(
+                "Attempt to mutate closed view of model of type '%s' given to rule '%s'", type, ruleDescriptor
+        ));
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Mutate.java b/subprojects/model-core/src/main/java/org/gradle/model/Mutate.java
new file mode 100644
index 0000000..f239e14
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Mutate.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.model;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the {@link RuleSource} method rule carrying this annotation mutates the rule subject.
+ * <p>
+ * Mutate rules execute after {@link Defaults} rules, but before {@link Finalize} rules.
+ * The first parameter of the rule is the rule subject, which is mutable for the duration of the rule.
+ * <p>
+ * Please see {@link RuleSource} for more information on method rules.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Mutate {
+}
diff --git a/subprojects/core/src/main/groovy/org/gradle/model/Path.java b/subprojects/model-core/src/main/java/org/gradle/model/Path.java
similarity index 100%
rename from subprojects/core/src/main/groovy/org/gradle/model/Path.java
rename to subprojects/model-core/src/main/java/org/gradle/model/Path.java
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/RuleSource.java b/subprojects/model-core/src/main/java/org/gradle/model/RuleSource.java
new file mode 100644
index 0000000..3dd3aee
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/RuleSource.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+/**
+ * A marker type for a class that is a collection of rules.
+ * <p>
+ * A rule source is not used like a regular Java object.
+ * It is a stateless container of methods and possibly constants.
+ * <p>
+ * Please consult the “Rule based model configuration” chapter of the Gradle User Guide for general information about “rules”.
+ *
+ * <h3>Rule methods</h3>
+ * <p>
+ * Each method that is annotated with one of the following is considered a rule:
+ * <ul>
+ * <li>{@link Model}</li>
+ * <li>{@link Defaults}</li>
+ * <li>{@link Mutate}</li>
+ * <li>{@link Finalize}</li>
+ * <li>{@link Validate}</li>
+ * </ul>
+ * <p>
+ * Each annotation specifies the type of the rule, which affects when it will be executed.
+ * <p>
+ * The following constraints apply to all rule methods:
+ * <ul>
+ * <li>A method may only be annotated by at most one of the above annotations.</li>
+ * <li>A rule method may be {@code static} or not; it makes no difference.</li>
+ * <li>A rule method cannot be generic (i.e. cannot have type parameters).</li>
+ * <li>With the exception of {@link Model} methods, all methods must have at least one parameter.</li>
+ * <li>With the exception of {@link Model} methods, all methods must have a {@code void} return type.</li>
+ * </ul>
+ * <p>
+ * See {@link Model} for information on the significance of the return type of a {@link Model} method.
+ *
+ * <h4>Subjects and inputs</h4>
+ * <p>
+ * Method rules declare the subject and any inputs as parameters to the method.
+ * With the exception of {@link Model} methods, the subject of the rule is the, required, first parameter and all subsequent parameters are inputs.
+ * For a non-void {@link Model} method, the subject (i.e. model element being created) is the return object.
+ * For a void {@link Model} method, the subject is the first method parameter.
+ * <p>
+ * The {@link Path} annotation can be placed on any parameter (except the subject of {@link Model} rules) to indicate the model element to bind to.
+ * If there is no {@link Path} annotation, a “by-type” binding will be attempted.
+ * The binding scope is determined by how the rule source is applied.
+ *
+ * <h3>General class constraints</h3>
+ * <p>
+ * Along with the constraints on individual rule methods by their associated annotation, the following are general constraints of rule source implementations:
+ * <ul>
+ * <li>Constructors are not allowed.</li>
+ * <li>Inheritance hierarchies are not allowed (i.e. all rules sources must directly extend {@link RuleSource}).</li>
+ * <li>Instance variables are now allowed.</li>
+ * <li>Non-final static variables are now allowed (i.e. constants are allowed).</li>
+ * <li>Methods cannot be overloaded.</li>
+ * <li>Implementations cannot be generic (i.e. cannot use type parameters).</li>
+ * </ul>
+ */
+ at Incubating
+public class RuleSource {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Unmanaged.java b/subprojects/model-core/src/main/java/org/gradle/model/Unmanaged.java
new file mode 100644
index 0000000..6c0a013
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Unmanaged.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a property of a managed model element is explicitly of an unmanaged type.
+ * <p>
+ * This annotation must be present on the <b>getter</b> of the property for the unmanaged type.
+ * If the annotation is not present for a property that is not a managed type, a fatal error will occur.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Unmanaged {
+    // Note: this may be a temporary measure while existing infrastructure is being ported to managed model elements
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/Validate.java b/subprojects/model-core/src/main/java/org/gradle/model/Validate.java
new file mode 100644
index 0000000..2a31bfe
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/Validate.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the {@link RuleSource} method rule carrying this annotation validates the rule subject.
+ * <p>
+ * Validate rules execute after {@link Finalize} rules, but before rule subject is used as an input.
+ * The first parameter of the rule is the rule subject, which is <b>immutable</b>.
+ * <p>
+ * Please see {@link RuleSource} for more information on method rules.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface Validate {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/WriteOnlyModelViewException.java b/subprojects/model-core/src/main/java/org/gradle/model/WriteOnlyModelViewException.java
new file mode 100644
index 0000000..465d5da
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/WriteOnlyModelViewException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+/**
+ * Thrown when an attempt is made to read the value of a model element that is not readable at the time.
+ */
+ at Incubating
+public class WriteOnlyModelViewException extends GradleException {
+
+    public WriteOnlyModelViewException(ModelType<?> type, ModelRuleDescriptor ruleDescriptor) {
+        super(String.format(
+                "Attempt to read a write only view of model of type '%s' given to rule '%s'", type, ruleDescriptor
+        ));
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/collection/CollectionBuilder.java b/subprojects/model-core/src/main/java/org/gradle/model/collection/CollectionBuilder.java
new file mode 100644
index 0000000..ce23f83
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/collection/CollectionBuilder.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+import org.gradle.model.RuleSource;
+
+import java.util.Set;
+
+/**
+ * Allows the adding of items to a named collection where instantiation is managed.
+ *
+ * @param <T> the contract type for all items
+ */
+ at Incubating
+public interface CollectionBuilder<T> {
+    /**
+     * Returns a collection containing the items from this collection which are of the specified type.
+     *
+     * @param type The type.
+     * @param <S> The type.
+     * @return The collection.
+     */
+    <S> CollectionBuilder<S> withType(Class<S> type);
+
+    /**
+     * Returns the number of items in this collection.
+     *
+     * @return the size of this collection.
+     */
+    int size();
+
+    /**
+     * Returns true if this collection contains no items.
+     *
+     * @return true if this collection is empty.
+     */
+    boolean isEmpty();
+
+    /**
+     * Returns the item with the given name, if any.
+     *
+     * @param name The name of the item.
+     * @return The item, or null if no such item.
+     */
+    @Nullable
+    T get(Object name);
+
+    /**
+     * Returns the item with the given name, if any.
+     *
+     * @param name The name of the item.
+     * @return The item, or null if no such item.
+     */
+    @Nullable
+    T get(String name);
+
+    /**
+     * Returns true if this collection contains an item with the given name.
+     *
+     * @param name The name of the item.
+     * @return true if this collection contains an item with the given name.
+     */
+    boolean containsKey(Object name);
+
+    /**
+     * Returns true if this collection contains the given item.
+     *
+     * @param item The item.
+     * @return true if this collection contains the given item.
+     */
+    boolean containsValue(Object item);
+
+    /**
+     * Returns the names of the items in this collection.
+     *
+     * @return The names
+     */
+    Set<String> keySet();
+
+    /**
+     * Defines an item with the given name and type T. The item is not created immediately, but is instead created as it is required.
+     *
+     * @param name The name.
+     */
+    // TODO - exception when no default type
+    void create(String name);
+
+    /**
+     * Defines an item with the given name and type T. The item is not created immediately, but is instead created as it is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required.
+     *
+     * @param name The name.
+     * @param configAction An action that initialises the item. The action is executed when the item is required.
+     */
+    // TODO - exception when no default type
+    void create(String name, Action<? super T> configAction);
+
+    /**
+     * Defines an item with the given name and type. The item is not created immediately, but is instead created as it is required.
+     *
+     * @param name The name.
+     */
+    // TODO - exception when type cannot be created
+    <S extends T> void create(String name, Class<S> type);
+
+    /**
+     * Defines an item with the given name and type. The item is not created immediately, but is instead created as it is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required.
+     *
+     * @param name The name.
+     * @param configAction An action that initialises the item. The action is executed when the item is required.
+     */
+    // TODO - exception when type cannot be created
+    <S extends T> void create(String name, Class<S> type, Action<? super S> configAction);
+
+    /**
+     * Applies the given action to the given item, when the item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called after any actions provided to {@link #beforeEach(org.gradle.api.Action)} and {@link #create(String,
+     * org.gradle.api.Action)}.
+     *
+     * @param name The name.
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    void named(String name, Action<? super T> configAction);
+
+    /**
+     * Applies the given rule source class to the given item, when the item is required.
+     *
+     * <p>Rules are applied in the scope of the item therefore:
+     * <ul>
+     * <li>subject by-type and by-path bindings are of inner scope</li>
+     * <li>subject can be bound by type to a child of the scope in which the rule is applied</li>
+     * <li>input by-path bindings are of inner scope</li>
+     * <li>input by-type bindings are of outer scope</li>
+     * </ul>
+     * @param name The name.
+     * @param ruleSource A rule source class.
+     */
+    void named(String name, Class<? extends RuleSource> ruleSource);
+
+    /**
+     * Applies the given action to each item in this collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called before any actions provided to {@link #create(String, org.gradle.api.Action)}.
+     *
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    void beforeEach(Action<? super T> configAction);
+
+    /**
+     * Applies the given action to each item of the given type in this collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called before any actions provided to {@link #create(String, org.gradle.api.Action)}.
+     *
+     * @param type The type of elements to apply the action to.
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    <S> void beforeEach(Class<S> type, Action<? super S> configAction);
+
+    /**
+     * Applies the given action to each item in the collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called after any actions provided to {@link #beforeEach(org.gradle.api.Action)} and {@link #create(String,
+     * org.gradle.api.Action)}.
+     *
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    void all(Action<? super T> configAction);
+
+    /**
+     * Applies the given action to each item of the given type in the collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called after any actions provided to {@link #beforeEach(org.gradle.api.Action)} and {@link #create(String,
+     * org.gradle.api.Action)}.
+     *
+     * @param type The type of elements to apply the action to.
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    <S> void withType(Class<S> type, Action<? super S> configAction);
+
+    /**
+     * Applies the given rules to all items of the collection of the given type.
+     *
+     * @param type the type that the item must be/implement to have the rules applied
+     * @param rules rules to apply
+     */
+    <S> void withType(Class<S> type, Class<? extends RuleSource> rules);
+
+    /**
+     * Applies the given action to each item in the collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called after any actions provided to {@link #beforeEach(org.gradle.api.Action)}, {@link #create(String,
+     * org.gradle.api.Action)}, and other mutation methods.
+     *
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    void afterEach(Action<? super T> configAction);
+
+    /**
+     * Applies the given action to each item of the given type in the collection, as each item is required.
+     *
+     * <p>The given action is invoked to configure the item when the item is required. It is called after any actions provided to {@link #beforeEach(org.gradle.api.Action)}, {@link #create(String,
+     * org.gradle.api.Action)}, and other mutation methods.
+     *
+     * @param type The type of elements to apply the action to.
+     * @param configAction An action that configures the item. The action is executed when the item is required.
+     */
+    <S> void afterEach(Class<S> type, Action<? super S> configAction);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/collection/ManagedSet.java b/subprojects/model-core/src/main/java/org/gradle/model/collection/ManagedSet.java
new file mode 100644
index 0000000..c4d724e
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/collection/ManagedSet.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+
+import java.util.Set;
+
+/**
+ * A set of managed model objects.
+ * <p>
+ * {@link org.gradle.model.Managed} types may declare managed set properties.
+ * Managed sets can only contain managed types.
+ * <p>
+ * Managed set objects cannot be mutated via the mutative methods of the {@link Set} interface (e.g. {@link Set#add(Object)}, {@link Set#clear()}).
+ * To add elements to the set, the {@link #create(Action)} method can be used.
+ *
+ * @param <T> the type of model object
+ */
+ at Incubating
+public interface ManagedSet<T> extends Set<T> {
+
+    /**
+     * Declares a new set element, configured by the given action.
+     *
+     * @param action the object configuration
+     */
+    void create(Action<? super T> action);
+
+    /**
+     * Apply the given action to each set element just after it is created.
+     * <p>
+     * The configuration action is equivalent in terms of lifecycle to {@link org.gradle.model.Defaults} rule methods.
+     *
+     * @param configAction the object configuration
+     */
+    void beforeEach(Action<? super T> configAction);
+
+    /**
+     * Apply the given action to each set element just before it is considered to be realised.
+     * <p>
+     * The configuration action is equivalent in terms of lifecycle to {@link org.gradle.model.Finalize} rule methods.
+     *
+     * @param configAction the object configuration
+     */
+    void afterEach(Action<? super T> configAction);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/collection/package-info.java b/subprojects/model-core/src/main/java/org/gradle/model/collection/package-info.java
new file mode 100644
index 0000000..795239d
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/collection/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Collection oriented types and utilities.
+ */
+package org.gradle.model.collection;
\ No newline at end of file
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ActionBackedModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ActionBackedModelAction.java
new file mode 100644
index 0000000..a37c1af
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ActionBackedModelAction.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Action;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ActionBackedModelAction<T> implements ModelAction<T> {
+    private final ModelReference<T> subject;
+    private final Action<? super T> configAction;
+    private final ModelRuleDescriptor descriptor;
+
+    public ActionBackedModelAction(ModelReference<T> subject, ModelRuleDescriptor descriptor, Action<? super T> configAction) {
+        this.subject = subject;
+        this.configAction = configAction;
+        this.descriptor = descriptor;
+    }
+
+    public static <T> ModelAction<T> of(ModelReference<T> reference, ModelRuleDescriptor descriptor, Action<? super T> configAction) {
+        return new ActionBackedModelAction<T>(reference, descriptor, configAction);
+    }
+
+    @Override
+    public ModelReference<T> getSubject() {
+        return subject;
+    }
+
+    @Override
+    public void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs) {
+        configAction.execute(object);
+    }
+
+    @Override
+    public List<ModelReference<?>> getInputs() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/BiActionBackedModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/BiActionBackedModelAction.java
new file mode 100644
index 0000000..a0816d4
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/BiActionBackedModelAction.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BiActionBackedModelAction<T> implements ModelAction<T> {
+    private final ModelReference<T> modelReference;
+    private final ModelRuleDescriptor descriptor;
+    private final List<ModelReference<?>> inputs;
+    private final BiAction<? super T, ? super List<ModelView<?>>> initializer;
+
+    public BiActionBackedModelAction(ModelReference<T> modelReference, ModelRuleDescriptor descriptor, List<ModelReference<?>> inputs, BiAction<? super T, ? super List<ModelView<?>>> initializer) {
+        this.modelReference = modelReference;
+        this.descriptor = descriptor;
+        this.inputs = inputs;
+        this.initializer = initializer;
+    }
+
+    public static <T> BiActionBackedModelAction<T> of(ModelReference<T> modelReference, ModelRuleDescriptor descriptor, List<ModelReference<?>> inputs, BiAction<? super T, ? super List<ModelView<?>>> initializer) {
+        return new BiActionBackedModelAction<T>(modelReference, descriptor, inputs, initializer);
+    }
+
+    public static <T, I> BiActionBackedModelAction<T> single(ModelReference<T> modelReference, ModelRuleDescriptor descriptor, final ModelReference<I> input, final BiAction<? super T, ? super I> initializer) {
+        return new BiActionBackedModelAction<T>(modelReference, descriptor, Collections.<ModelReference<?>>singletonList(input), new BiAction<T, List<ModelView<?>>>() {
+            @Override
+            public void execute(T t, List<ModelView<?>> modelViews) {
+                initializer.execute(t, ModelViews.assertType(modelViews.get(0), input.getType()).getInstance());
+            }
+        });
+    }
+
+    @Override
+    public ModelReference<T> getSubject() {
+        return modelReference;
+    }
+
+    @Override
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    @Override
+    public List<ModelReference<?>> getInputs() {
+        return inputs;
+    }
+
+    @Override
+    public void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs) {
+        initializer.execute(object, inputs);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ChainingModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ChainingModelProjection.java
new file mode 100644
index 0000000..297d817
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ChainingModelProjection.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.api.specs.Spec;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+public class ChainingModelProjection implements ModelProjection {
+    private final Iterable<? extends ModelProjection> projections;
+
+    public ChainingModelProjection(Iterable<? extends ModelProjection> projections) {
+        this.projections = projections;
+    }
+
+    public <B> boolean canBeViewedAsWritable(final ModelType<B> type) {
+        return CollectionUtils.any(projections, new Spec<ModelProjection>() {
+            public boolean isSatisfiedBy(ModelProjection projection) {
+                return projection.canBeViewedAsWritable(type);
+            }
+        });
+    }
+
+    public <B> boolean canBeViewedAsReadOnly(final ModelType<B> type) {
+        return CollectionUtils.any(projections, new Spec<ModelProjection>() {
+            public boolean isSatisfiedBy(ModelProjection projection) {
+                return projection.canBeViewedAsReadOnly(type);
+            }
+        });
+    }
+
+    private Iterable<String> collectDescriptions(Transformer<Iterable<String>, ModelProjection> transformer) {
+        return CollectionUtils.flattenCollections(String.class, CollectionUtils.collect(projections, transformer));
+    }
+
+    public Iterable<String> getWritableTypeDescriptions() {
+        return collectDescriptions(new Transformer<Iterable<String>, ModelProjection>() {
+            public Iterable<String> transform(ModelProjection projection) {
+                return projection.getWritableTypeDescriptions();
+            }
+        });
+    }
+
+    public Iterable<String> getReadableTypeDescriptions() {
+        return collectDescriptions(new Transformer<Iterable<String>, ModelProjection>() {
+            public Iterable<String> transform(ModelProjection projection) {
+                return projection.getReadableTypeDescriptions();
+            }
+        });
+    }
+
+    @Nullable
+    public <T> ModelView<? extends T> asReadOnly(ModelType<T> type, MutableModelNode node, ModelRuleDescriptor ruleDescriptor) {
+        for (ModelProjection projection : projections) {
+            ModelView<? extends T> view = projection.asReadOnly(type, node, ruleDescriptor);
+            if (view != null) {
+                return view;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public <T> ModelView<? extends T> asWritable(ModelType<T> type, MutableModelNode node, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> inputs) {
+        for (ModelProjection projection : projections) {
+            ModelView<? extends T> view = projection.asWritable(type, node, ruleDescriptor, inputs);
+            if (view != null) {
+                return view;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ChainingModelProjection that = (ChainingModelProjection) o;
+
+        return projections.equals(that.projections);
+    }
+
+    @Override
+    public int hashCode() {
+        return projections.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "ChainingModelProjection{projections=" + projections + '}';
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/CollectionBuilderModelView.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/CollectionBuilderModelView.java
new file mode 100644
index 0000000..7eb762f
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/CollectionBuilderModelView.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.MissingMethodException;
+import groovy.lang.MissingPropertyException;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.model.ModelViewClosedException;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Set;
+
+import static org.gradle.internal.Cast.uncheckedCast;
+
+ at NotThreadSafe
+public class CollectionBuilderModelView<T> implements ModelView<CollectionBuilder<T>> {
+
+    private final ModelType<CollectionBuilder<T>> type;
+    private final CollectionBuilder<T> instance;
+    private final ModelRuleDescriptor ruleDescriptor;
+    private final ModelPath path;
+
+    private boolean closed;
+
+    public CollectionBuilderModelView(ModelPath path, ModelType<CollectionBuilder<T>> type, CollectionBuilder<T> rawInstance, ModelRuleDescriptor ruleDescriptor) {
+        this.path = path;
+        this.type = type;
+        this.ruleDescriptor = ruleDescriptor;
+        this.instance = new Decorator<T>(rawInstance);
+    }
+
+    @Override
+    public ModelPath getPath() {
+        return path;
+    }
+
+    public ModelType<CollectionBuilder<T>> getType() {
+        return type;
+    }
+
+    public CollectionBuilder<T> getInstance() {
+        return instance;
+    }
+
+    public void close() {
+        closed = true;
+    }
+
+    // TODO - mix in Groovy support and share with managed set
+    public class Decorator<I> extends GroovyObjectSupport implements CollectionBuilder<I> {
+        private final CollectionBuilder<I> rawInstance;
+
+        public Decorator(CollectionBuilder<I> rawInstance) {
+            this.rawInstance = rawInstance;
+        }
+
+        @Override
+        public String toString() {
+            return rawInstance.toString();
+        }
+
+        @Override
+        public <S> CollectionBuilder<S> withType(Class<S> type) {
+            return new Decorator<S>(rawInstance.withType(type));
+        }
+
+        @Override
+        public int size() {
+            return rawInstance.size();
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return rawInstance.isEmpty();
+        }
+
+        @Nullable
+        @Override
+        public I get(String name) {
+            return rawInstance.get(name);
+        }
+
+        @Nullable
+        @Override
+        public I get(Object name) {
+            return rawInstance.get(name);
+        }
+
+        @Override
+        public boolean containsKey(Object name) {
+            return rawInstance.containsKey(name);
+        }
+
+        @Override
+        public boolean containsValue(Object item) {
+            return rawInstance.containsValue(item);
+        }
+
+        @Override
+        public Set<String> keySet() {
+            return rawInstance.keySet();
+        }
+
+        @Override
+        public void create(String name) {
+            assertNotClosed();
+            rawInstance.create(name);
+        }
+
+        @Override
+        public void create(String name, Action<? super I> configAction) {
+            assertNotClosed();
+            rawInstance.create(name, configAction);
+        }
+
+        @Override
+        public <S extends I> void create(String name, Class<S> type) {
+            assertNotClosed();
+            rawInstance.create(name, type);
+        }
+
+        @Override
+        public <S extends I> void create(String name, Class<S> type, Action<? super S> configAction) {
+            assertNotClosed();
+            rawInstance.create(name, type, configAction);
+        }
+
+        @Override
+        public void named(String name, Action<? super I> configAction) {
+            assertNotClosed();
+            rawInstance.named(name, configAction);
+        }
+
+        @Override
+        public void named(String name, Class<? extends RuleSource> ruleSource) {
+            assertNotClosed();
+            rawInstance.named(name, ruleSource);
+        }
+
+        @Override
+        public void beforeEach(Action<? super I> configAction) {
+            assertNotClosed();
+            rawInstance.beforeEach(configAction);
+        }
+
+        @Override
+        public <S> void beforeEach(Class<S> type, Action<? super S> configAction) {
+            assertNotClosed();
+            rawInstance.beforeEach(type, configAction);
+        }
+
+        @Override
+        public void all(Action<? super I> configAction) {
+            assertNotClosed();
+            rawInstance.all(configAction);
+        }
+
+        @Override
+        public <S> void withType(Class<S> type, Action<? super S> configAction) {
+            assertNotClosed();
+            rawInstance.withType(type, configAction);
+        }
+
+        @Override
+        public <S> void withType(Class<S> type, Class<? extends RuleSource> rules) {
+            rawInstance.withType(type, rules);
+        }
+
+        @Override
+        public void afterEach(Action<? super I> configAction) {
+            assertNotClosed();
+            rawInstance.afterEach(configAction);
+        }
+
+        @Override
+        public <S> void afterEach(Class<S> type, Action<? super S> configAction) {
+            assertNotClosed();
+            rawInstance.afterEach(type, configAction);
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public void create(String name, Closure<? super I> configAction) {
+            create(name, new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public <S extends I> void create(String name, Class<S> type, Closure<? super S> configAction) {
+            create(name, type, new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public void named(String name, Closure<? super I> configAction) {
+            named(name, new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public void all(Closure<? super I> configAction) {
+            all(new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public <S> void withType(Class<S> type, Closure<? super S> configAction) {
+            withType(type, new ClosureBackedAction<S>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public void beforeEach(Closure<? super I> configAction) {
+            beforeEach(new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public <S> void beforeEach(Class<S> type, Closure<? super S> configAction) {
+            beforeEach(type, new ClosureBackedAction<S>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public void afterEach(Closure<? super I> configAction) {
+            afterEach(new ClosureBackedAction<I>(configAction));
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public <S> void afterEach(Class<S> type, Closure<? super S> configAction) {
+            afterEach(type, new ClosureBackedAction<S>(configAction));
+        }
+
+        // TODO - mix this in
+        @Override
+        public Object getProperty(String property) {
+            I element = rawInstance.get(property);
+            if (element == null) {
+                throw new MissingPropertyException(property, CollectionBuilder.class);
+            }
+            return element;
+        }
+
+        // TODO - mix this in and validate closure parameters
+        public Void methodMissing(String name, Object argsObj) {
+            Object[] args = (Object[]) argsObj;
+            if (args.length == 1 && args[0] instanceof Class<?>) {
+                Class<? extends I> itemType = uncheckedCast(args[0]);
+                create(name, itemType);
+            } else if (args.length == 2 && args[0] instanceof Class<?> && args[1] instanceof Closure<?>) {
+                Class<? extends I> itemType = uncheckedCast(args[0]);
+                Closure<? super I> closure = uncheckedCast(args[1]);
+                create(name, itemType, closure);
+            } else if (args.length == 1 && args[0] instanceof Closure<?>) {
+                Closure<? super I> closure = uncheckedCast(args[0]);
+                named(name, closure);
+            } else {
+                throw new MissingMethodException(name, CollectionBuilder.class, args);
+            }
+            return null;
+        }
+
+        private void assertNotClosed() {
+            if (closed) {
+                throw new ModelViewClosedException(type, ruleDescriptor);
+            }
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DefaultCollectionBuilder.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DefaultCollectionBuilder.java
new file mode 100644
index 0000000..d9acfc6
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DefaultCollectionBuilder.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Actions;
+import org.gradle.internal.BiAction;
+import org.gradle.internal.util.BiFunction;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.NestedModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static org.gradle.internal.Cast.uncheckedCast;
+
+ at NotThreadSafe
+public class DefaultCollectionBuilder<T> implements CollectionBuilder<T> {
+
+    private final ModelType<T> elementType;
+    private final ModelRuleDescriptor sourceDescriptor;
+    private final MutableModelNode modelNode;
+    private final BiFunction<? extends ModelCreators.Builder, ? super ModelPath, ? super ModelType<? extends T>> creatorFunction;
+
+    public DefaultCollectionBuilder(ModelType<T> elementType, ModelRuleDescriptor sourceDescriptor, MutableModelNode modelNode, BiFunction<? extends ModelCreators.Builder, ? super ModelPath, ? super ModelType<? extends T>> creatorFunction) {
+        this.elementType = elementType;
+        this.sourceDescriptor = sourceDescriptor;
+        this.modelNode = modelNode;
+        this.creatorFunction = creatorFunction;
+    }
+
+    @Override
+    public String toString() {
+        return modelNode.getPrivateData().toString();
+    }
+
+    @Override
+    public int size() {
+        return modelNode.getLinkCount(elementType);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    @Override
+    public <S> CollectionBuilder<S> withType(Class<S> type) {
+        if (type.equals(elementType.getConcreteClass())) {
+            return uncheckedCast(this);
+        }
+
+        if (elementType.getConcreteClass().isAssignableFrom(type)) {
+            Class<? extends T> castType = uncheckedCast(type);
+            CollectionBuilder<? extends T> subType = toSubType(castType);
+            return uncheckedCast(subType);
+        }
+
+        return new DefaultCollectionBuilder<S>(ModelType.of(type), sourceDescriptor, modelNode, new BiFunction<ModelCreators.Builder, ModelPath, ModelType<? extends S>>() {
+            @Override
+            public ModelCreators.Builder apply(ModelPath s, ModelType<? extends S> modelType) {
+                throw new IllegalArgumentException(String.format("Cannot create an item of type %s as this is not a subtype of %s.", modelType, elementType.toString()));
+            }
+        });
+    }
+
+    public <S extends T> CollectionBuilder<S> toSubType(Class<S> type) {
+        return new DefaultCollectionBuilder<S>(ModelType.of(type), sourceDescriptor, modelNode, creatorFunction);
+    }
+
+    @Nullable
+    @Override
+    public T get(Object name) {
+        return get((String) name);
+    }
+
+    @Nullable
+    @Override
+    public T get(String name) {
+        // TODO - lock this down
+        MutableModelNode link = modelNode.getLink(name);
+        if (link == null) {
+            return null;
+        }
+        link.ensureUsable();
+        return link.asWritable(elementType, sourceDescriptor, null).getInstance();
+    }
+
+    @Override
+    public boolean containsKey(Object name) {
+        return name instanceof String && modelNode.hasLink((String) name, elementType);
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return modelNode.getLinkNames(elementType);
+    }
+
+    @Override
+    public boolean containsValue(Object item) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    @Override
+    public void create(final String name) {
+        doCreate(name, elementType);
+    }
+
+    @Override
+    public void create(String name, Action<? super T> configAction) {
+        doCreate(name, elementType, configAction);
+    }
+
+    @Override
+    public <S extends T> void create(final String name, final Class<S> type) {
+        doCreate(name, ModelType.of(type));
+    }
+
+    @Override
+    public <S extends T> void create(final String name, final Class<S> type, final Action<? super S> configAction) {
+        doCreate(name, ModelType.of(type), configAction);
+    }
+
+    private <S extends T> void doCreate(final String name, final ModelType<S> type) {
+        doCreate(name, type, Actions.doNothing());
+    }
+
+    private <S extends T> void doCreate(final String name, final ModelType<S> type, final Action<? super S> initAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "create(%s)", name);
+
+        ModelCreators.Builder creatorBuilder = creatorFunction.apply(modelNode.getPath().child(name), type);
+
+        ModelCreator creator = creatorBuilder
+                .withProjection(new UnmanagedModelProjection<S>(type, true, true))
+                .descriptor(descriptor)
+                .build();
+
+        modelNode.addLink(creator);
+
+        modelNode.applyToLink(ModelActionRole.Initialize, new ActionBackedModelAction<S>(ModelReference.of(creator.getPath(), type), descriptor, new Action<S>() {
+            @Override
+            public void execute(S s) {
+                initAction.execute(s);
+            }
+        }));
+
+        onCreate(name, type);
+    }
+
+    protected <S extends T> void onCreate(String name, ModelType<S> type) {
+
+    }
+
+    @Override
+    public void named(final String name, Action<? super T> configAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "named(%s)", name);
+        ModelReference<T> subject = ModelReference.of(modelNode.getPath().child(name), elementType);
+        modelNode.applyToLink(ModelActionRole.Mutate, new ActionBackedModelAction<T>(subject, descriptor, configAction));
+    }
+
+    @Override
+    public void named(String name, Class<? extends RuleSource> ruleSource) {
+        modelNode.applyToLink(name, ruleSource);
+    }
+
+    @Override
+    public void all(final Action<? super T> configAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "all()");
+        ModelReference<T> subject = ModelReference.of(elementType);
+        modelNode.applyToAllLinks(ModelActionRole.Mutate, new ActionBackedModelAction<T>(subject, descriptor, configAction));
+    }
+
+    @Override
+    public <S> void withType(Class<S> type, Action<? super S> configAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "withType()");
+        ModelReference<S> subject = ModelReference.of(type);
+        modelNode.applyToAllLinks(ModelActionRole.Mutate, new ActionBackedModelAction<S>(subject, descriptor, configAction));
+    }
+
+    @Override
+    public <S> void withType(Class<S> type, Class<? extends RuleSource> rules) {
+        modelNode.applyToLinks(type, rules);
+    }
+
+    @Override
+    public void beforeEach(Action<? super T> configAction) {
+        doBeforeEach(elementType, configAction);
+    }
+
+    @Override
+    public <S> void beforeEach(Class<S> type, Action<? super S> configAction) {
+        doBeforeEach(ModelType.of(type), configAction);
+    }
+
+    private <S> void doBeforeEach(ModelType<S> type, Action<? super S> configAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "beforeEach()");
+        ModelReference<S> subject = ModelReference.of(type);
+        modelNode.applyToAllLinks(ModelActionRole.Defaults, new ActionBackedModelAction<S>(subject, descriptor, configAction));
+    }
+
+    @Override
+    public void afterEach(Action<? super T> configAction) {
+        doFinalizeAll(elementType, configAction);
+    }
+
+    @Override
+    public <S> void afterEach(Class<S> type, Action<? super S> configAction) {
+        doFinalizeAll(ModelType.of(type), configAction);
+    }
+
+    private <S> void doFinalizeAll(ModelType<S> type, Action<? super S> configAction) {
+        ModelRuleDescriptor descriptor = NestedModelRuleDescriptor.append(sourceDescriptor, "afterEach()");
+        ModelReference<S> subject = ModelReference.of(type);
+        modelNode.applyToAllLinks(ModelActionRole.Finalize, new ActionBackedModelAction<S>(subject, descriptor, configAction));
+    }
+
+    public static <I> ModelType<CollectionBuilder<I>> typeOf(ModelType<I> type) {
+        return new ModelType.Builder<CollectionBuilder<I>>() {
+        }.where(
+                new ModelType.Parameter<I>() {
+                }, type
+        ).build();
+    }
+
+    public static <I> ModelType<CollectionBuilder<I>> typeOf(Class<I> type) {
+        return typeOf(ModelType.of(type));
+    }
+
+    public static <I> ModelType<NamedEntityInstantiator<I>> instantiatorTypeOf(Class<I> type) {
+        return instantiatorTypeOf(ModelType.of(type));
+    }
+
+    public static <I> ModelType<NamedEntityInstantiator<I>> instantiatorTypeOf(ModelType<I> type) {
+        return new ModelType.Builder<NamedEntityInstantiator<I>>() {
+        }.where(
+                new ModelType.Parameter<I>() {
+                }, type
+        ).build();
+    }
+
+    public static <T> BiFunction<ModelCreators.Builder, ModelPath, ModelType<? extends T>> createAndStoreVia(final ModelReference<? extends NamedEntityInstantiator<? super T>> instantiatorReference, final ModelReference<? extends Collection<? super T>> storeReference) {
+        return new BiFunction<ModelCreators.Builder, ModelPath, ModelType<? extends T>>() {
+            @Override
+            public ModelCreators.Builder apply(final ModelPath path, final ModelType<? extends T> modelType) {
+                return ModelCreators
+                        .of(ModelReference.of(path, modelType), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                            @Override
+                            public void execute(MutableModelNode modelNode, List<ModelView<?>> modelViews) {
+                                doExecute(modelNode, modelType, modelViews);
+                            }
+
+                            public <S extends T> void doExecute(MutableModelNode modelNode, ModelType<S> subType, List<ModelView<?>> modelViews) {
+                                NamedEntityInstantiator<? super T> instantiator = ModelViews.getInstance(modelViews.get(0), instantiatorReference);
+                                S item = instantiator.create(path.getName(), subType.getConcreteClass());
+                                modelNode.setPrivateData(subType, item);
+                                modelNode.applyToSelf(ModelActionRole.Initialize, BiActionBackedModelAction.single(ModelReference.of(path, subType), new SimpleModelRuleDescriptor("DefaultCollectionBuilder.createAndStoreVia() - " + path), storeReference, new BiAction<S, Collection<? super T>>() {
+                                    @Override
+                                    public void execute(S s, Collection<? super T> objects) {
+                                        objects.add(s);
+                                    }
+                                }));
+                            }
+                        })
+                        .inputs(instantiatorReference);
+
+            }
+        };
+    }
+
+    public static <T> BiFunction<ModelCreators.Builder, ModelPath, ModelType<? extends T>> createVia(final ModelReference<? extends NamedEntityInstantiator<? super T>> instantiatorReference) {
+        return new BiFunction<ModelCreators.Builder, ModelPath, ModelType<? extends T>>() {
+            @Override
+            public ModelCreators.Builder apply(final ModelPath path, final ModelType<? extends T> modelType) {
+                return ModelCreators
+                        .of(ModelReference.of(path, modelType), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                            @Override
+                            public void execute(MutableModelNode modelNode, List<ModelView<?>> modelViews) {
+                                doExecute(modelNode, modelType, modelViews);
+                            }
+
+                            public <S extends T> void doExecute(MutableModelNode modelNode, ModelType<S> subType, List<ModelView<?>> modelViews) {
+                                NamedEntityInstantiator<? super T> instantiator = ModelViews.getInstance(modelViews.get(0), instantiatorReference);
+                                S item = instantiator.create(path.getName(), subType.getConcreteClass());
+                                modelNode.setPrivateData(subType, item);
+                            }
+                        })
+                        .inputs(instantiatorReference);
+
+            }
+        };
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DelegatingCollectionBuilder.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DelegatingCollectionBuilder.java
new file mode 100644
index 0000000..906313e
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DelegatingCollectionBuilder.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.BiAction;
+import org.gradle.internal.Cast;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Set;
+
+public class DelegatingCollectionBuilder<T> implements CollectionBuilder<T> {
+
+    private final CollectionBuilder<T> delegate;
+    private final ModelType<T> baseType;
+    private final BiAction<? super String, ? super ModelType<? extends T>> onCreate;
+
+    public DelegatingCollectionBuilder(CollectionBuilder<T> delegate, ModelType<T> baseType, BiAction<? super String, ? super ModelType<? extends T>> onCreate) {
+        this.delegate = delegate;
+        this.baseType = baseType;
+        this.onCreate = onCreate;
+    }
+
+    @Override
+    public <S> CollectionBuilder<S> withType(Class<S> type) {
+        // This cast is safe, because we know that .create() will fail on the real collection if S doesn't extend T
+        BiAction<? super String, ? super ModelType<? extends S>> castOnCreate = Cast.uncheckedCast(onCreate);
+        return new DelegatingCollectionBuilder<S>(delegate.withType(type), ModelType.of(type), castOnCreate);
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    @Nullable
+    public T get(Object name) {
+        return delegate.get(name);
+    }
+
+    @Override
+    @Nullable
+    public T get(String name) {
+        return delegate.get(name);
+    }
+
+    @Override
+    public boolean containsKey(Object name) {
+        return delegate.containsKey(name);
+    }
+
+    @Override
+    public boolean containsValue(Object item) {
+        return delegate.containsValue(item);
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return delegate.keySet();
+    }
+
+    @Override
+    public void create(String name) {
+        delegate.create(name);
+        onCreate(name, baseType);
+    }
+
+    @Override
+    public void create(String name, Action<? super T> configAction) {
+        delegate.create(name, configAction);
+        onCreate(name, baseType);
+    }
+
+    @Override
+    public <S extends T> void create(String name, Class<S> type) {
+        delegate.create(name, type);
+        onCreate(name, ModelType.of(type));
+    }
+
+    @Override
+    public <S extends T> void create(String name, Class<S> type, Action<? super S> configAction) {
+        onCreate(name, ModelType.of(type));
+        delegate.create(name, type, configAction);
+    }
+
+    @Override
+    public void named(String name, Action<? super T> configAction) {
+        delegate.named(name, configAction);
+    }
+
+    @Override
+    public void named(String name, Class<? extends RuleSource> ruleSource) {
+        delegate.named(name, ruleSource);
+    }
+
+    @Override
+    public void beforeEach(Action<? super T> configAction) {
+        delegate.beforeEach(configAction);
+    }
+
+    @Override
+    public <S> void beforeEach(Class<S> type, Action<? super S> configAction) {
+        delegate.beforeEach(type, configAction);
+    }
+
+    @Override
+    public void all(Action<? super T> configAction) {
+        delegate.all(configAction);
+    }
+
+    @Override
+    public <S> void withType(Class<S> type, Action<? super S> configAction) {
+        delegate.withType(type, configAction);
+    }
+
+    @Override
+    public <S> void withType(Class<S> type, Class<? extends RuleSource> rules) {
+        delegate.withType(type, rules);
+    }
+
+    @Override
+    public void afterEach(Action<? super T> configAction) {
+        delegate.afterEach(configAction);
+    }
+
+    @Override
+    public <S> void afterEach(Class<S> type, Action<? super S> configAction) {
+        delegate.afterEach(type, configAction);
+    }
+
+    private <S extends T> void onCreate(String name, ModelType<S> type) {
+        onCreate.execute(name, type);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DependencyOnlyExtractedModelRule.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DependencyOnlyExtractedModelRule.java
new file mode 100644
index 0000000..0f07a37
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DependencyOnlyExtractedModelRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import java.util.List;
+
+public class DependencyOnlyExtractedModelRule implements ExtractedModelRule {
+
+    private final List<Class<?>> dependencies;
+
+    public DependencyOnlyExtractedModelRule(List<Class<?>> dependencyList) {
+        this.dependencies = dependencyList;
+    }
+
+    @Override
+    public Type getType() {
+        return Type.DEPENDENCIES;
+    }
+
+    @Override
+    public ModelCreator getCreator() {
+        return null;
+    }
+
+    @Override
+    public ModelActionRole getActionRole() {
+        return null;
+    }
+
+    @Override
+    public ModelAction<?> getAction() {
+        return null;
+    }
+
+    public List<Class<?>> getRuleDependencies() {
+        return dependencies;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DirectNodeModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DirectNodeModelAction.java
new file mode 100644
index 0000000..d590cde
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DirectNodeModelAction.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Action;
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DirectNodeModelAction<T> implements ModelAction<T> {
+
+    private final ModelReference<T> subjectReference;
+    private final BiAction<? super MutableModelNode, ? super T> action;
+    private final ModelRuleDescriptor descriptor;
+
+    private DirectNodeModelAction(ModelReference<T> subjectReference, ModelRuleDescriptor descriptor, BiAction<? super MutableModelNode, ? super T> action) {
+        this.subjectReference = subjectReference;
+        this.action = action;
+        this.descriptor = descriptor;
+    }
+
+    public static <T> ModelAction<T> of(ModelReference<T> reference, ModelRuleDescriptor descriptor, final Action<? super MutableModelNode> action) {
+        return new DirectNodeModelAction<T>(reference, descriptor, new BiAction<MutableModelNode, T>() {
+            @Override
+            public void execute(MutableModelNode modelNode, T t) {
+                action.execute(modelNode);
+            }
+        });
+    }
+
+    public static <T> ModelAction<T> of(ModelReference<T> reference, ModelRuleDescriptor descriptor, BiAction<? super MutableModelNode, ? super T> action) {
+        return new DirectNodeModelAction<T>(reference, descriptor, action);
+    }
+
+    @Override
+    public ModelReference<T> getSubject() {
+        return subjectReference;
+    }
+
+    @Override
+    public void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs) {
+        action.execute(modelNode, object);
+    }
+
+    @Override
+    public List<ModelReference<?>> getInputs() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DuplicateModelException.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DuplicateModelException.java
new file mode 100644
index 0000000..1002cc9
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/DuplicateModelException.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.internal.core;
+
+import org.gradle.api.GradleException;
+
+// TODO generic model related super exception?
+public class DuplicateModelException extends GradleException {
+
+    public DuplicateModelException(String message) {
+        super(message);
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/EmptyModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/EmptyModelProjection.java
new file mode 100644
index 0000000..ed7b7a5
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/EmptyModelProjection.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collections;
+import java.util.List;
+
+public class EmptyModelProjection implements ModelProjection {
+
+    public static final ModelProjection INSTANCE = new EmptyModelProjection();
+
+    private EmptyModelProjection() {
+    }
+
+    @Nullable
+    @Override
+    public <T> ModelView<? extends T> asReadOnly(ModelType<T> type, MutableModelNode node, @Nullable ModelRuleDescriptor ruleDescriptor) {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public <T> ModelView<? extends T> asWritable(ModelType<T> type, MutableModelNode node, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> inputs) {
+        return null;
+    }
+
+    @Override
+    public <T> boolean canBeViewedAsWritable(ModelType<T> type) {
+        return false;
+    }
+
+    @Override
+    public <T> boolean canBeViewedAsReadOnly(ModelType<T> type) {
+        return false;
+    }
+
+    @Override
+    public Iterable<String> getWritableTypeDescriptions() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Iterable<String> getReadableTypeDescriptions() {
+        return Collections.emptyList();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelAction.java
new file mode 100644
index 0000000..8b22d77
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelAction.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+public class ExtractedModelAction implements ExtractedModelRule {
+
+    private final ModelActionRole role;
+    private final ModelAction<?> action;
+    private final List<? extends Class<?>> dependencies;
+
+    public ExtractedModelAction(ModelActionRole role, ModelAction<?> action) {
+        this(role, ImmutableList.<Class<?>>of(), action);
+    }
+
+    public ExtractedModelAction(ModelActionRole role, List<? extends Class<?>> dependencies, ModelAction<?> action) {
+        this.role = role;
+        this.action = action;
+        this.dependencies = dependencies;
+    }
+
+    @Override
+    public Type getType() {
+        return Type.ACTION;
+    }
+
+    @Override
+    public ModelCreator getCreator() {
+        return null;
+    }
+
+    @Override
+    public ModelActionRole getActionRole() {
+        return role;
+    }
+
+    @Override
+    public ModelAction<?> getAction() {
+        return action;
+    }
+
+    @Override
+    public List<? extends Class<?>> getRuleDependencies() {
+        return dependencies;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelCreator.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelCreator.java
new file mode 100644
index 0000000..2216e10
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelCreator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+public class ExtractedModelCreator implements ExtractedModelRule {
+
+    private final ModelCreator creator;
+
+    public ExtractedModelCreator(ModelCreator creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public Type getType() {
+        return Type.CREATOR;
+    }
+
+    @Override
+    public ModelCreator getCreator() {
+        return creator;
+    }
+
+    @Override
+    public ModelActionRole getActionRole() {
+        return null;
+    }
+
+    @Override
+    public ModelAction<?> getAction() {
+        return null;
+    }
+
+    @Override
+    public List<Class<?>> getRuleDependencies() {
+        return ImmutableList.of();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelRule.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelRule.java
new file mode 100644
index 0000000..0d5b791
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ExtractedModelRule.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import java.util.List;
+
+public interface ExtractedModelRule {
+
+    enum Type {
+        CREATOR,
+        ACTION,
+        DEPENDENCIES // this is pretty weird, but a 'dependencies' only type rule is a temporary thing
+    }
+
+    Type getType();
+
+    // null if not a creator
+    ModelCreator getCreator();
+
+    // null if not an action
+    ModelActionRole getActionRole();
+
+    // null if not an action
+    ModelAction<?> getAction();
+
+    // never null
+    List<? extends Class<?>> getRuleDependencies();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/InstanceModelView.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/InstanceModelView.java
new file mode 100644
index 0000000..73f199c
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/InstanceModelView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.type.ModelType;
+
+ at ThreadSafe
+public class InstanceModelView<T> implements ModelView<T> {
+
+    private final ModelPath path;
+    private final ModelType<T> type;
+    private final T instance;
+
+    public InstanceModelView(ModelPath path, ModelType<T> type, T instance) {
+        this.path = path;
+        this.type = type;
+        this.instance = instance;
+    }
+
+    public static <T> ModelView<T> of(ModelPath path, ModelType<T> type, T instance) {
+        return new InstanceModelView<T>(path, type, instance);
+    }
+
+    @Override
+    public ModelPath getPath() {
+        return path;
+    }
+
+    public ModelType<T> getType() {
+        return type;
+    }
+
+    public T getInstance() {
+        return instance;
+    }
+
+    public void close() {
+
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAction.java
new file mode 100644
index 0000000..1d44b66
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAction.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.model.internal.core;
+
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.List;
+
+public interface ModelAction<T> extends ModelRule {
+
+    ModelReference<T> getSubject();
+
+    void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs);
+
+    List<ModelReference<?>> getInputs();
+
+    ModelRuleDescriptor getDescriptor();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelActionRole.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelActionRole.java
new file mode 100644
index 0000000..4c5984f
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelActionRole.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+/**
+ * A hard-coded sequence of model actions that can be applied to a model element.
+ *
+ * <p>This is pretty much a placeholder for something more descriptive.
+ */
+public enum ModelActionRole {
+    Defaults, // Allows a mutation to setup default values for an element
+    Initialize, // Mutation provided when an element is defined
+    Mutate, // Customisations
+    Finalize, // Post customisation default values
+    Validate // Post mutation validations
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAdapter.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAdapter.java
new file mode 100644
index 0000000..9568207
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+public interface ModelAdapter {
+
+    @Nullable
+    <T> ModelView<? extends T> asReadOnly(ModelType<T> type, MutableModelNode node, @Nullable ModelRuleDescriptor ruleDescriptor);
+
+    @Nullable
+    <T> ModelView<? extends T> asWritable(ModelType<T> type, MutableModelNode node, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> implicitDependencies);
+
+    @Override
+        // must implement logical equality
+    boolean equals(Object other);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreator.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreator.java
new file mode 100644
index 0000000..20187cb
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreator.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.model.internal.core;
+
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.List;
+
+public interface ModelCreator extends ModelRule {
+    ModelRuleDescriptor getDescriptor();
+
+    ModelPath getPath();
+
+    ModelPromise getPromise();
+
+    ModelAdapter getAdapter();
+
+    void create(MutableModelNode node, List<ModelView<?>> inputs);
+
+    boolean isEphemeral();
+
+    List<? extends ModelReference<?>> getInputs();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreatorFactory.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreatorFactory.java
new file mode 100644
index 0000000..017c811
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreatorFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Action;
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+
+import java.util.List;
+
+public interface ModelCreatorFactory {
+    <T> ModelCreator creator(ModelRuleDescriptor descriptor,
+                             ModelPath path,
+                             ModelSchema<T> schema,
+                             Action<? super T> initializer);
+
+    <T> ModelCreator creator(ModelRuleDescriptor descriptor,
+                             ModelPath path,
+                             ModelSchema<T> schema,
+                             List<ModelReference<?>> initializerInputs,
+                             BiAction<? super T, ? super List<ModelView<?>>> initializer);
+
+    <T> ModelCreator creator(ModelRuleDescriptor descriptor,
+                             ModelPath path,
+                             ModelSchema<T> schema);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreators.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreators.java
new file mode 100644
index 0000000..69d859e
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelCreators.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.BiAction;
+import org.gradle.internal.Factories;
+import org.gradle.internal.Factory;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+ at ThreadSafe
+abstract public class ModelCreators {
+
+    public static <T> Builder bridgedInstance(ModelReference<T> modelReference, T instance) {
+        return unmanagedInstance(modelReference, Factories.constant(instance));
+    }
+
+    public static <T> Builder unmanagedInstance(final ModelReference<T> modelReference, final Factory<? extends T> factory) {
+        BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer = new BiAction<MutableModelNode, List<ModelView<?>>>() {
+            public void execute(MutableModelNode modelNode, List<ModelView<?>> inputs) {
+                modelNode.setPrivateData(modelReference.getType(), factory.create());
+            }
+        };
+
+        return of(modelReference, initializer)
+                .withProjection(new UnmanagedModelProjection<T>(modelReference.getType(), true, true));
+    }
+
+    public static Builder of(ModelReference<?> modelReference, BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer) {
+        return new Builder(modelReference, initializer);
+    }
+
+    @NotThreadSafe
+    public static class Builder {
+        private final BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer;
+        private final ModelReference<?> modelReference;
+        private final List<ModelProjection> projections = new ArrayList<ModelProjection>();
+        private boolean ephemeral;
+        private boolean hidden;
+
+        private ModelRuleDescriptor modelRuleDescriptor;
+        private List<? extends ModelReference<?>> inputs = Collections.emptyList();
+
+        private Builder(ModelReference<?> modelReference, BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer) {
+            this.modelReference = modelReference;
+            this.initializer = initializer;
+        }
+
+        public Builder descriptor(String descriptor) {
+            this.modelRuleDescriptor = new SimpleModelRuleDescriptor(descriptor);
+            return this;
+        }
+
+        public Builder descriptor(ModelRuleDescriptor descriptor) {
+            this.modelRuleDescriptor = descriptor;
+            return this;
+        }
+
+        public Builder inputs(List<? extends ModelReference<?>> inputs) {
+            this.inputs = inputs;
+            return this;
+        }
+
+        public Builder inputs(ModelReference<?>... inputs) {
+            this.inputs = Arrays.asList(inputs);
+            return this;
+        }
+
+        // Callers must take care
+        public Builder withProjection(ModelProjection projection) {
+            projections.add(projection);
+            return this;
+        }
+
+        public Builder hidden(boolean flag) {
+            this.hidden = flag;
+            return this;
+        }
+
+        public Builder ephemeral(boolean flag) {
+            this.ephemeral = flag;
+            return this;
+        }
+
+        public ModelCreator build() {
+            ModelProjection projection = projections.size() == 1 ? projections.get(0) : new ChainingModelProjection(projections);
+            return new ProjectionBackedModelCreator(modelReference.getPath(), modelRuleDescriptor, ephemeral, hidden, inputs, projection, initializer);
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelNode.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelNode.java
new file mode 100644
index 0000000..f187c1f
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelNode.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Set;
+
+public interface ModelNode {
+
+    boolean hasLink(String name);
+
+    boolean hasLink(String name, ModelType<?> type);
+
+    // Note: order is crucial here
+    public enum State {
+        Known(true),
+        Created(true),
+        DefaultsApplied(true),
+        Initialized(true),
+        Mutated(true),
+        Finalized(false),
+        SelfClosed(false),
+        GraphClosed(false);
+
+        public final boolean mutable;
+
+        State(boolean mutable) {
+            this.mutable = mutable;
+        }
+    }
+
+    boolean isEphemeral();
+
+    ModelPath getPath();
+
+    ModelRuleDescriptor getDescriptor();
+
+    State getState();
+
+    /**
+     * Creates a read-only view over this node's value.
+     *
+     * Callers should try to {@link ModelView#close()} the returned view when it is done with, allowing any internal cleanup to occur.
+     *
+     * Throws if this node can't be expressed as a read-only view of the requested type.
+     */
+    <T> ModelView<? extends T> asReadOnly(ModelType<T> type, @Nullable ModelRuleDescriptor ruleDescriptor);
+
+    Set<String> getLinkNames(ModelType<?> type);
+
+    Iterable<? extends ModelNode> getLinks(ModelType<?> type);
+
+    /**
+     * Should this node be hidden from the model report.
+     */
+    boolean isHidden();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPath.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPath.java
new file mode 100644
index 0000000..3360a8e
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPath.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.internal.exceptions.Contextual;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+ at ThreadSafe
+public class ModelPath implements Iterable<String>, Comparable<ModelPath> {
+    public static final ModelPath ROOT = new ModelPath("", Collections.<String>emptyList()) {
+        @Override
+        public String toString() {
+            return "<root>";
+        }
+
+        @Override
+        public ModelPath descendant(ModelPath path) {
+            return path;
+        }
+    };
+
+    public static final String SEPARATOR = ".";
+    public static final Splitter PATH_SPLITTER = Splitter.on('.');
+    public static final Joiner PATH_JOINER = Joiner.on('.');
+
+    private final String path;
+    private final List<String> components;
+
+    public ModelPath(String path) {
+        this.path = path;
+        this.components = PATH_SPLITTER.splitToList(path);
+    }
+
+    public ModelPath(Iterable<String> parts) {
+        this.path = PATH_JOINER.join(parts);
+        this.components = ImmutableList.copyOf(parts);
+    }
+
+    private ModelPath(String path, List<String> parts) {
+        this.path = path;
+        this.components = parts;
+    }
+
+    public int compareTo(ModelPath other) {
+        return path.compareTo(other.path);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ModelPath modelPath = (ModelPath) o;
+
+        return path.equals(modelPath.path);
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    public int getDepth() {
+        return components.size();
+    }
+
+    public List<String> getComponents() {
+        return components;
+    }
+
+    public Iterator<String> iterator() {
+        return components.iterator();
+    }
+
+    @Override
+    public String toString() {
+        return path;
+    }
+
+    public static ModelPath path(String path) {
+        return new ModelPath(path);
+    }
+
+    public static ModelPath path(Iterable<String> names) {
+        return new ModelPath(names);
+    }
+
+    public static String pathString(Iterable<String> names) {
+        return PATH_JOINER.join(names);
+    }
+
+    public ModelPath child(String child) {
+        List<String> childComponents = new ArrayList<String>(components);
+        childComponents.add(child);
+        return path(childComponents);
+    }
+
+    public ModelPath sibling(String name) {
+        if (this == ROOT) {
+            throw new IllegalStateException("Cannot create sibling path of root path");
+        }
+        List<String> newComponents = new ArrayList<String>(components);
+        newComponents.set(newComponents.size() - 1, name);
+        return path(newComponents);
+    }
+
+    public boolean isTopLevel() {
+        return getDepth() == 1;
+    }
+
+    public ModelPath getRootParent() {
+        return components.size() <= 1 ? null : ModelPath.path(components.get(0));
+    }
+
+    public ModelPath getParent() {
+        if (components.isEmpty()) {
+            return null;
+        }
+        if (components.size() == 1) {
+            return ROOT;
+        }
+        return path(components.subList(0, components.size() - 1));
+    }
+
+    public String getName() {
+        if (components.isEmpty()) {
+            return "";
+        }
+        return components.get(components.size() - 1);
+    }
+
+    public boolean isDirectChild(@Nullable ModelPath other) {
+        if (other == null) {
+            return false;
+        }
+        if (other.getDepth() != getDepth() + 1) {
+            return false;
+        }
+        ModelPath otherParent = other.getParent();
+        return otherParent != null && otherParent.equals(this);
+    }
+
+    public ModelPath descendant(ModelPath path) {
+        return path(Iterables.concat(components, path.components));
+    }
+
+    public static class InvalidNameException extends GradleException {
+        public InvalidNameException(String message) {
+            super(message);
+        }
+    }
+
+    @Contextual
+    public static class InvalidPathException extends GradleException {
+        public InvalidPathException(String message, InvalidNameException e) {
+            super(message, e);
+        }
+    }
+
+    private static final CharMatcher VALID_FIRST_CHAR_MATCHER = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.is('_'));
+    private final static CharMatcher INVALID_FIRST_CHAR_MATCHER = VALID_FIRST_CHAR_MATCHER.negate().precomputed();
+    private final static CharMatcher INVALID_CHAR_MATCHER = CharMatcher.inRange('0', '9').or(VALID_FIRST_CHAR_MATCHER).or(CharMatcher.is('-')).negate().precomputed();
+
+    public static void validateName(String name) {
+        if (name.isEmpty()) {
+            throw new InvalidNameException("Cannot use an empty string as a model element name.");
+        }
+
+        char firstChar = name.charAt(0);
+
+        if (INVALID_FIRST_CHAR_MATCHER.matches(firstChar)) {
+            throw new InvalidNameException(String.format("Model element name '%s' has illegal first character '%s' (names must start with an ASCII letter or underscore).", name, firstChar));
+        }
+
+        for (int i = 1; i < name.length(); ++i) {
+            char character = name.charAt(i);
+            if (INVALID_CHAR_MATCHER.matches(character)) {
+                throw new InvalidNameException(String.format("Model element name '%s' contains illegal character '%s' (only ASCII letters, numbers and the underscore are allowed).", name, character));
+            }
+        }
+    }
+
+    @Nullable
+    public static ModelPath validatedPath(@Nullable String path) {
+        if (path == null) {
+            return null;
+        } else {
+            validatePath(path);
+            return path(path);
+        }
+    }
+
+    public static ModelPath nonNullValidatedPath(String path) {
+        if (path == null) {
+            throw new IllegalArgumentException("path cannot be null");
+        } else {
+            return validatedPath(path);
+        }
+    }
+
+    public static void validatePath(String path) throws InvalidPathException {
+        if (path.isEmpty()) {
+            throw new InvalidPathException("Cannot use an empty string as a model path.", null);
+        }
+
+        if (path.startsWith(SEPARATOR)) {
+            throw new InvalidPathException(String.format("Model path '%s' cannot start with name separator '%s'.", path, SEPARATOR), null);
+        }
+
+        if (path.endsWith(SEPARATOR)) {
+            throw new InvalidPathException(String.format("Model path '%s' cannot end with name separator '%s'.", path, SEPARATOR), null);
+        }
+
+        List<String> names = PATH_SPLITTER.splitToList(path);
+        if (names.size() == 1) {
+            validateName(names.get(0));
+        } else {
+            for (String name : names) {
+                try {
+                    validateName(name);
+                } catch (InvalidNameException e) {
+                    throw new InvalidPathException(String.format("Model path '%s' is invalid due to invalid name component.", path), e);
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelProjection.java
new file mode 100644
index 0000000..3eecc39
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelProjection.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+public interface ModelProjection extends ModelPromise, ModelAdapter {
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPromise.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPromise.java
new file mode 100644
index 0000000..3483838
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelPromise.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.model.internal.core;
+
+import org.gradle.model.internal.type.ModelType;
+
+public interface ModelPromise {
+
+    <T> boolean canBeViewedAsWritable(ModelType<T> type);
+
+    <T> boolean canBeViewedAsReadOnly(ModelType<T> type);
+
+    // These methods return strings rather than types because it may be more complicated than what is able to be expressed via a ModelType.
+    // Also, we don't want to encourage compatibility checking occurring by looping through such types as we have more options for optimising the compatibility check internally.
+    // Also also, these methods are only called for reporting so values should typically not be precomputed.
+    Iterable<String> getWritableTypeDescriptions();
+
+    Iterable<String> getReadableTypeDescriptions();
+
+    @Override
+        // must implement logical equality
+    boolean equals(Object other);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelReference.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelReference.java
new file mode 100644
index 0000000..d636c20
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelReference.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.type.ModelType;
+
+/**
+ * A model reference is a speculative reference to a potential model element.
+ * <p>
+ * Rule subjects/inputs are defined in terms of references, as opposed to concrete identity.
+ * The reference may be by type only, or by path only.
+ * <p>
+ * A reference doesn't include the notion of readonly vs. writable as the context of the reference implies this.
+ * Having this be part of the reference would open opportunities for mismatch of that flag in the context.
+ *
+ * @param <T> the type of the reference.
+ */
+ at ThreadSafe
+public class ModelReference<T> {
+
+    private final ModelPath path;
+    private final ModelType<T> type;
+    private final String description;
+
+    private ModelReference(@Nullable ModelPath path, ModelType<T> type, String description) {
+        this.path = path;
+        this.type = type;
+        this.description = description;
+    }
+
+    public static ModelReference<Object> any() {
+        return of(ModelType.untyped());
+    }
+
+    public static <T> ModelReference<T> of(ModelPath path, ModelType<T> type, String description) {
+        return new ModelReference<T>(path, type, description);
+    }
+
+    public static <T> ModelReference<T> of(String path, ModelType<T> type, String description) {
+        return of(ModelPath.path(path), type, description);
+    }
+
+    public static <T> ModelReference<T> of(ModelPath path, ModelType<T> type) {
+        return new ModelReference<T>(path, type, null);
+    }
+
+    public static <T> ModelReference<T> of(ModelPath path, Class<T> type) {
+        return of(path, ModelType.of(type));
+    }
+
+    public static <T> ModelReference<T> of(String path, Class<T> type) {
+        return of(ModelPath.path(path), ModelType.of(type));
+    }
+
+    public static <T> ModelReference<T> of(String path, ModelType<T> type) {
+        return of(path == null ? null : ModelPath.path(path), type);
+    }
+
+    public static <T> ModelReference<T> of(Class<T> type) {
+        return of((ModelPath) null, ModelType.of(type));
+    }
+
+    public static <T> ModelReference<T> of(ModelType<T> type) {
+        return of((ModelPath) null, type);
+    }
+
+    public static ModelReference<Object> of(String path) {
+        return of(ModelPath.path(path), ModelType.UNTYPED);
+    }
+
+    public static ModelReference<Object> of(ModelPath path) {
+        return of(path, ModelType.UNTYPED);
+    }
+
+    public static ModelReference<Object> untyped(ModelPath path) {
+        return untyped(path, null);
+    }
+
+    public static ModelReference<Object> untyped(ModelPath path, String description) {
+        return of(path, ModelType.UNTYPED, description);
+    }
+
+    @Nullable
+    public ModelPath getPath() {
+        return path;
+    }
+
+    @Nullable
+    public String getDescription() {
+        return description;
+    }
+
+    public ModelType<T> getType() {
+        return type;
+    }
+
+    public boolean isUntyped() {
+        return type.equals(ModelType.UNTYPED);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ModelReference<?> that = (ModelReference<?>) o;
+
+        if (path == null) {
+            if (that.path == null) {
+                return type.equals(that.type);
+            }
+            return false;
+        }
+        return path.equals(that.path) && type.equals(that.type);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = path.hashCode();
+        result = 31 * result + type.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ModelReference{path=" + path + ", type=" + type + '}';
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRegistrar.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRegistrar.java
new file mode 100644
index 0000000..2031386
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRegistrar.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.model.internal.core;
+
+public interface ModelRegistrar {
+
+    public ModelRegistrar replace(ModelCreator newCreator);
+
+    public ModelRegistrar create(ModelCreator creator);
+
+    public ModelRegistrar createOrReplace(ModelCreator newCreator);
+
+    public <T> ModelRegistrar configure(ModelActionRole role, ModelAction<T> action);
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRule.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRule.java
new file mode 100644
index 0000000..8c53e98
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRule.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+public interface ModelRule {
+    ModelRuleDescriptor getDescriptor();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRuleExecutionException.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRuleExecutionException.java
new file mode 100644
index 0000000..713757c
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelRuleExecutionException.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.model.internal.core;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+ at Contextual
+// TODO should include some context on what the rule was trying to do (create vs. mutate)
+public class ModelRuleExecutionException extends GradleException {
+
+    public ModelRuleExecutionException(ModelRuleDescriptor descriptor, Throwable cause) {
+        super(toMessage(descriptor), cause);
+    }
+
+    public ModelRuleExecutionException(ModelRuleDescriptor descriptor, String error) {
+        super(toMessage(descriptor, error));
+    }
+
+    private static String toMessage(ModelRuleDescriptor descriptor) {
+        StringBuilder builder = new StringBuilder("Exception thrown while executing model rule: ");
+        descriptor.describeTo(builder);
+        return builder.toString();
+    }
+
+    private static String toMessage(ModelRuleDescriptor descriptor, String error) {
+        StringBuilder builder = new StringBuilder("error executing model rule: ");
+        descriptor.describeTo(builder);
+        builder.append(" - ");
+        builder.append(error);
+        return builder.toString();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelView.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelView.java
new file mode 100644
index 0000000..96ca4ff
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelView.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.model.internal.core;
+
+import org.gradle.model.internal.type.ModelType;
+
+public interface ModelView<T> {
+
+    ModelPath getPath();
+
+    ModelType<T> getType();
+
+    T getInstance();
+
+    void close();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelViews.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelViews.java
new file mode 100644
index 0000000..ad4dbfc
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ModelViews.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.model.internal.type.ModelType;
+
+public abstract class ModelViews {
+
+    public static <T> ModelView<T> assertType(ModelView<?> untypedView, ModelType<T> type) {
+        if (type.isAssignableFrom(untypedView.getType())) {
+            @SuppressWarnings("unchecked") ModelView<T> view = (ModelView<T>) untypedView;
+            return view;
+        } else {
+            // TODO better exception type
+            throw new IllegalArgumentException("Model view of type " + untypedView.getType() + " requested as " + type);
+        }
+    }
+
+    public static <T> ModelView<T> assertType(ModelView<?> untypedView, ModelReference<T> reference) {
+        return assertType(untypedView, reference.getType());
+    }
+
+    public static <T> T getInstance(ModelView<?> untypedView, ModelReference<T> reference) {
+        return assertType(untypedView, reference.getType()).getInstance();
+    }
+
+    public static <T> T getInstance(ModelView<?> untypedView, ModelType<T> type) {
+        return assertType(untypedView, type).getInstance();
+    }
+
+    public static <T> T getInstance(ModelView<?> untypedView, Class<T> type) {
+        return assertType(untypedView, ModelType.of(type)).getInstance();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/MutableModelNode.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/MutableModelNode.java
new file mode 100644
index 0000000..6135b71
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/MutableModelNode.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+import java.util.Set;
+
+public interface MutableModelNode extends ModelNode {
+
+    /**
+     * Creates a mutable view over this node's value.
+     *
+     * Callers should try to {@link ModelView#close()} the returned view when it is done with, allowing any internal cleanup to occur.
+     *
+     * Throws if this node can't be expressed as a mutable view of the requested type.
+     */
+    <T> ModelView<? extends T> asWritable(ModelType<T> type, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> implicitDependencies);
+
+    /**
+     * Adds a reference node to the graph. A reference node is a node that refers to some other node elsewhere in the graph, similar to a symbolic link.
+     */
+    void addReference(ModelCreator creator);
+
+    /**
+     * Adds a node to the graph, linked from this node. The given creator is used to initialize the node when required.
+     */
+    void addLink(ModelCreator creator);
+
+    /**
+     * Removes a node linked from this node from the graph.
+     */
+    void removeLink(String name);
+
+    /**
+     * Applies an action to this node.
+     */
+    <T> void applyToSelf(ModelActionRole type, ModelAction<T> action);
+
+    /**
+     * Applies an action to all nodes linked from this node.
+     */
+    <T> void applyToAllLinks(ModelActionRole type, ModelAction<T> action);
+
+    /**
+     * Applies an action to a linked node.
+     */
+    <T> void applyToLink(ModelActionRole type, ModelAction<T> action);
+
+    void applyToLink(String name, Class<? extends RuleSource> rules);
+
+    void applyToSelf(Class<? extends RuleSource> rules);
+
+    <T> void applyToLinks(Class<T> type, Class<? extends RuleSource> rules);
+
+    @Nullable
+    MutableModelNode getLink(String name);
+
+    int getLinkCount(ModelType<?> type);
+
+    Set<String> getLinkNames(ModelType<?> type);
+
+    Iterable<? extends MutableModelNode> getLinks(ModelType<?> type);
+
+    <T> void setPrivateData(ModelType<? super T> type, T object);
+
+    <T> T getPrivateData(ModelType<T> type);
+
+    Object getPrivateData();
+
+    @Nullable
+    MutableModelNode getTarget();
+
+    void setTarget(ModelNode target);
+
+    /**
+     * Ensure that the views are available, with default values applied.
+     */
+    void ensureUsable();
+
+    void setHidden(boolean hidden);
+
+    boolean isMutable();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/NamedEntityInstantiator.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/NamedEntityInstantiator.java
new file mode 100644
index 0000000..cf49bbc
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/NamedEntityInstantiator.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.model.internal.core;
+
+public interface NamedEntityInstantiator<T> {
+    <S extends T> S create(String name, Class<S> type);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ProjectionBackedModelCreator.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ProjectionBackedModelCreator.java
new file mode 100644
index 0000000..03ca04a
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/ProjectionBackedModelCreator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.List;
+
+ at ThreadSafe
+public class ProjectionBackedModelCreator implements ModelCreator {
+    private final ModelPath path;
+    private final ModelRuleDescriptor descriptor;
+    private final boolean ephemeral;
+    private final boolean hidden;
+    private final ModelProjection projection;
+    private final List<? extends ModelReference<?>> inputs;
+    private final BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer;
+
+    public ProjectionBackedModelCreator(
+            ModelPath path,
+            ModelRuleDescriptor descriptor,
+            boolean ephemeral,
+            boolean hidden,
+            List<? extends ModelReference<?>> inputs,
+            ModelProjection projection,
+            BiAction<? super MutableModelNode, ? super List<ModelView<?>>> initializer
+    ) {
+        this.path = path;
+        this.descriptor = descriptor;
+        this.ephemeral = ephemeral;
+        this.hidden = hidden;
+        this.projection = projection;
+        this.inputs = inputs;
+        this.initializer = initializer;
+    }
+
+    public ModelPath getPath() {
+        return path;
+    }
+
+    public ModelPromise getPromise() {
+        return projection;
+    }
+
+    public ModelAdapter getAdapter() {
+        return projection;
+    }
+
+    public void create(MutableModelNode node, List<ModelView<?>> inputs) {
+        node.setHidden(hidden);
+        initializer.execute(node, inputs);
+    }
+
+    @Override
+    public boolean isEphemeral() {
+        return ephemeral;
+    }
+
+    public List<? extends ModelReference<?>> getInputs() {
+        return inputs;
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/TypeCompatibilityModelProjectionSupport.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/TypeCompatibilityModelProjectionSupport.java
new file mode 100644
index 0000000..0b5ff21
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/TypeCompatibilityModelProjectionSupport.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.Cast;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collections;
+import java.util.List;
+
+ at ThreadSafe
+public abstract class TypeCompatibilityModelProjectionSupport<M> implements ModelProjection {
+
+    private final ModelType<M> type;
+    private final boolean canBeViewedAsReadOnly;
+    private final boolean canBeViewedAsWritable;
+
+    public TypeCompatibilityModelProjectionSupport(ModelType<M> type, boolean canBeViewedAsReadOnly, boolean canBeViewedAsWritable) {
+        this.type = type;
+        this.canBeViewedAsReadOnly = canBeViewedAsReadOnly;
+        this.canBeViewedAsWritable = canBeViewedAsWritable;
+    }
+
+    protected ModelType<M> getType() {
+        return type;
+    }
+
+    public <T> boolean canBeViewedAsWritable(ModelType<T> targetType) {
+        return canBeViewedAsWritable && targetType.isAssignableFrom(type);
+    }
+
+    public <T> boolean canBeViewedAsReadOnly(ModelType<T> targetType) {
+        return canBeViewedAsReadOnly && targetType.isAssignableFrom(type);
+    }
+
+    public <T> ModelView<? extends T> asWritable(ModelType<T> type, MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> inputs) {
+        if (canBeViewedAsWritable(type)) {
+            return Cast.uncheckedCast(toView(modelNode, ruleDescriptor, true));
+        } else {
+            return null;
+        }
+    }
+
+    public <T> ModelView<? extends T> asReadOnly(ModelType<T> type, MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor) {
+        if (canBeViewedAsReadOnly(type)) {
+            return Cast.uncheckedCast(toView(modelNode, ruleDescriptor, false));
+        } else {
+            return null;
+        }
+    }
+
+    protected abstract ModelView<M> toView(MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor, boolean writable);
+
+    public Iterable<String> getWritableTypeDescriptions() {
+        if (canBeViewedAsWritable) {
+            return Collections.singleton(description(type));
+        } else {
+            return Collections.emptySet();
+        }
+    }
+
+    public Iterable<String> getReadableTypeDescriptions() {
+        if (canBeViewedAsReadOnly) {
+            return Collections.singleton(description(type));
+        } else {
+            return Collections.emptySet();
+        }
+    }
+
+    public static String description(ModelType<?> type) {
+        return type.toString() + " (or assignment compatible type thereof)";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        TypeCompatibilityModelProjectionSupport<?> that = (TypeCompatibilityModelProjectionSupport<?>) o;
+        return canBeViewedAsReadOnly == that.canBeViewedAsReadOnly && canBeViewedAsWritable == that.canBeViewedAsWritable && type.equals(that.type);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type.hashCode();
+        result = 31 * result + (canBeViewedAsReadOnly ? 1 : 0);
+        result = 31 * result + (canBeViewedAsWritable ? 1 : 0);
+        return result;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/UnmanagedModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/UnmanagedModelProjection.java
new file mode 100644
index 0000000..b5ffc27
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/UnmanagedModelProjection.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+ at ThreadSafe
+public class UnmanagedModelProjection<M> extends TypeCompatibilityModelProjectionSupport<M> {
+
+    public UnmanagedModelProjection(ModelType<M> type) {
+        super(type, true, true);
+    }
+
+    public UnmanagedModelProjection(ModelType<M> type, boolean canBeViewedAsReadOnly, boolean canBeViewedAsWritable) {
+        super(type, canBeViewedAsReadOnly, canBeViewedAsWritable);
+    }
+
+    @Override
+    protected ModelView<M> toView(MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor, boolean writable) {
+        M instance = modelNode.getPrivateData(getType());
+        return InstanceModelView.of(modelNode.getPath(), getType(), instance);
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/AbstractModelRuleDescriptor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/AbstractModelRuleDescriptor.java
new file mode 100644
index 0000000..02d3691
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/AbstractModelRuleDescriptor.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.model.internal.core.rule.describe;
+
+import net.jcip.annotations.ThreadSafe;
+
+ at ThreadSafe
+public abstract class AbstractModelRuleDescriptor implements ModelRuleDescriptor {
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        describeTo(sb);
+        return sb.toString();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptor.java
new file mode 100644
index 0000000..a697875
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core.rule.describe;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.reflect.MethodDescription;
+import org.gradle.model.internal.method.WeaklyTypeReferencingMethod;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+// TODO some kind of context of why the method was attached (e.g. which plugin declared the rule)
+// TODO some kind of instance state for the method (might be the same as context above)
+ at ThreadSafe
+public class MethodModelRuleDescriptor extends AbstractModelRuleDescriptor {
+
+    private final WeaklyTypeReferencingMethod<?, ?> method;
+    private String description;
+
+    public MethodModelRuleDescriptor(ModelType<?> target, ModelType<?> returnType, Method method) {
+        this(WeaklyTypeReferencingMethod.of(target, returnType, method));
+    }
+
+    public MethodModelRuleDescriptor(WeaklyTypeReferencingMethod<?, ?> method) {
+        this.method = method;
+    }
+
+    public void describeTo(Appendable appendable) {
+        try {
+            appendable.append(getDescription());
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private String getDescription() {
+        if (description == null) {
+            description = MethodDescription.name(method.getName())
+                    .owner(method.getDeclaringClass())
+                    .takes(method.getGenericParameterTypes())
+                    .toString();
+        }
+
+        return description;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        MethodModelRuleDescriptor that = (MethodModelRuleDescriptor) o;
+
+        return method.equals(that.method);
+    }
+
+    @Override
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    public static ModelRuleDescriptor of(Class<?> clazz, final String methodName) {
+        List<Method> methodsOfName = CollectionUtils.filter(clazz.getDeclaredMethods(), new Spec<Method>() {
+            public boolean isSatisfiedBy(Method element) {
+                return element.getName().equals(methodName);
+            }
+        });
+
+        if (methodsOfName.isEmpty()) {
+            throw new IllegalStateException("Class " + clazz.getName() + " has no method named '" + methodName + "'");
+        }
+
+        if (methodsOfName.size() > 1) {
+            throw new IllegalStateException("Class " + clazz.getName() + " has more than one method named '" + methodName + "'");
+        }
+
+        Method method = methodsOfName.get(0);
+        return of(clazz, method);
+    }
+
+    public static ModelRuleDescriptor of(Class<?> clazz, Method method) {
+        return new MethodModelRuleDescriptor(ModelType.of(clazz), ModelType.returnType(method), method);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/ModelRuleDescriptor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/ModelRuleDescriptor.java
new file mode 100644
index 0000000..cfaa66e
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/ModelRuleDescriptor.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.model.internal.core.rule.describe;
+
+public interface ModelRuleDescriptor {
+
+    // TODO - expand to include the concept of identity and description
+
+    /**
+     * This method is expected to be idempotent.
+     *
+     * @param appendable where to write the description to.
+     */
+    void describeTo(Appendable appendable);
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/NestedModelRuleDescriptor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/NestedModelRuleDescriptor.java
new file mode 100644
index 0000000..e44f686
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/NestedModelRuleDescriptor.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.core.rule.describe;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.IOException;
+
+ at ThreadSafe
+public class NestedModelRuleDescriptor extends AbstractModelRuleDescriptor {
+
+    private final ModelRuleDescriptor parent;
+    private final ModelRuleDescriptor child;
+
+    public NestedModelRuleDescriptor(ModelRuleDescriptor parent, ModelRuleDescriptor child) {
+        this.parent = parent;
+        this.child = child;
+    }
+
+    public void describeTo(Appendable appendable) {
+        parent.describeTo(appendable);
+        try {
+            appendable.append(" > ");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        child.describeTo(appendable);
+    }
+
+    public static ModelRuleDescriptor append(ModelRuleDescriptor parent, String str) {
+        return new NestedModelRuleDescriptor(parent, new SimpleModelRuleDescriptor(str));
+    }
+
+    public static ModelRuleDescriptor append(ModelRuleDescriptor parent, String str, Object... args) {
+        return append(parent, String.format(str, args));
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/SimpleModelRuleDescriptor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/SimpleModelRuleDescriptor.java
new file mode 100644
index 0000000..9161720
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/core/rule/describe/SimpleModelRuleDescriptor.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.model.internal.core.rule.describe;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.IOException;
+
+ at ThreadSafe
+public class SimpleModelRuleDescriptor extends AbstractModelRuleDescriptor {
+
+    private final String descriptor;
+
+    public SimpleModelRuleDescriptor(String descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    public void describeTo(Appendable appendable) {
+        try {
+            appendable.append(descriptor);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractAnnotationDrivenModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractAnnotationDrivenModelRuleExtractor.java
new file mode 100644
index 0000000..fa1d4af
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractAnnotationDrivenModelRuleExtractor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import com.google.common.reflect.TypeToken;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.specs.Spec;
+
+import java.lang.annotation.Annotation;
+
+ at ThreadSafe
+public abstract class AbstractAnnotationDrivenModelRuleExtractor<T extends Annotation> implements MethodModelRuleExtractor {
+    private final Class<T> annotationType;
+
+    protected AbstractAnnotationDrivenModelRuleExtractor() {
+        @SuppressWarnings("unchecked") Class<T> annotationType = (Class<T>) new TypeToken<T>(getClass()) {}.getRawType();
+        this.annotationType = annotationType;
+    }
+
+    public Spec<MethodRuleDefinition<?, ?>> getSpec() {
+        return new AnnotationMatchingSpec();
+    }
+
+    public String getDescription() {
+        return String.format("annotated with @%s", annotationType.getSimpleName());
+   }
+
+    private class AnnotationMatchingSpec implements Spec<MethodRuleDefinition<?, ?>> {
+        public boolean isSatisfiedBy(MethodRuleDefinition<?, ?> ruleDefinition) {
+           return ruleDefinition.getAnnotation(annotationType) != null;
+       }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractModelCreationRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractModelCreationRuleExtractor.java
new file mode 100644
index 0000000..56913cb
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractModelCreationRuleExtractor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.Model;
+import org.gradle.model.internal.core.ModelPath;
+
+ at ThreadSafe
+public abstract class AbstractModelCreationRuleExtractor extends AbstractAnnotationDrivenModelRuleExtractor<Model> {
+
+    protected String determineModelName(MethodRuleDefinition<?, ?> ruleDefinition) {
+        String annotationValue = ruleDefinition.getAnnotation(Model.class).value();
+        String modelName = (annotationValue == null || annotationValue.isEmpty()) ? ruleDefinition.getMethodName() : annotationValue;
+
+        try {
+            ModelPath.validatePath(modelName);
+        } catch (Exception e) {
+            throw new InvalidModelRuleDeclarationException(String.format("Path of declared model element created by rule %s is invalid.", ruleDefinition.getDescriptor()), e);
+        }
+
+        return modelName;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractMutationModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractMutationModelRuleExtractor.java
new file mode 100644
index 0000000..5ccf3f1
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/AbstractMutationModelRuleExtractor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.internal.core.ModelActionRole;
+import org.gradle.model.internal.core.ExtractedModelAction;
+import org.gradle.model.internal.core.ExtractedModelRule;
+
+import java.lang.annotation.Annotation;
+
+ at ThreadSafe
+public abstract class AbstractMutationModelRuleExtractor<T extends Annotation> extends AbstractAnnotationDrivenModelRuleExtractor<T> {
+
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        validate(ruleDefinition);
+        return new ExtractedModelAction(getMutationType(), new MethodBackedModelAction<S>(ruleDefinition));
+    }
+
+    protected abstract ModelActionRole getMutationType();
+
+    private void validate(MethodRuleDefinition<?, ?> ruleDefinition) {
+        if (!ruleDefinition.getReturnType().getRawClass().equals(Void.TYPE)) {
+            throw new InvalidModelRuleDeclarationException(ruleDefinition.getDescriptor(), "only void can be used as return type for mutation rules");
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultMethodRuleDefinition.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultMethodRuleDefinition.java
new file mode 100644
index 0000000..c096845
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultMethodRuleDefinition.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.Cast;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.Path;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.method.WeaklyTypeReferencingMethod;
+import org.gradle.model.internal.type.ModelType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import static org.gradle.util.CollectionUtils.findFirst;
+
+ at ThreadSafe
+public class DefaultMethodRuleDefinition<T, R, S> implements MethodRuleDefinition<R, S> {
+    private ImmutableList<ModelReference<?>> references;
+
+    private final WeaklyTypeReferencingMethod<T, R> method;
+
+    private DefaultMethodRuleDefinition(Method method, ModelType<T> instanceType, ModelType<R> returnType) {
+        this.method = new WeaklyTypeReferencingMethod<T, R>(instanceType, returnType, method);
+        
+        ImmutableList.Builder<ModelReference<?>> referencesBuilder = ImmutableList.builder();
+        for (int i = 0; i < method.getGenericParameterTypes().length; i++) {
+            Annotation[] paramAnnotations = method.getParameterAnnotations()[i];
+            referencesBuilder.add(reference(paramAnnotations, i));
+        }
+        this.references = referencesBuilder.build();
+    }
+
+    public static <T> MethodRuleDefinition<?, ?> create(Class<T> source, Method method) {
+        return innerCreate(source, method);
+    }
+
+    private static <T, R, S> MethodRuleDefinition<R, S> innerCreate(Class<T> source, Method method) {
+        ModelType<R> returnType = ModelType.returnType(method);
+        return new DefaultMethodRuleDefinition<T, R, S>(method, ModelType.of(source), returnType);
+    }
+
+
+    public String getMethodName() {
+        return method.getName();
+    }
+
+    public ModelType<R> getReturnType() {
+        return method.getReturnType();
+    }
+
+    @Nullable
+    @Override
+    public ModelReference<S> getSubjectReference() {
+        return Cast.uncheckedCast(references.isEmpty() ? null : references.get(0));
+    }
+
+    @Override
+    public List<ModelReference<?>> getTailReferences() {
+        return references.size() > 1 ? references.subList(1, references.size()) : Collections.<ModelReference<?>>emptyList();
+    }
+
+    public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+        for (Annotation annotation : method.getAnnotations()) {
+            if (annotationType.isAssignableFrom(annotation.getClass())) {
+                return Cast.uncheckedCast(annotation);
+            }
+        }
+        return null;
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return new MethodModelRuleDescriptor(method);
+    }
+
+    public ModelRuleInvoker<R> getRuleInvoker() {
+        return new DefaultModelRuleInvoker<T, R>(method);
+    }
+
+    public List<ModelReference<?>> getReferences() {
+        return references;
+    }
+
+    private ModelReference<?> reference(Annotation[] annotations, int i) {
+        Path pathAnnotation = (Path) findFirst(annotations, new Spec<Annotation>() {
+            public boolean isSatisfiedBy(Annotation element) {
+                return element.annotationType().equals(Path.class);
+            }
+        });
+        String path = pathAnnotation == null ? null : pathAnnotation.value();
+        ModelType<?> cast = ModelType.of(method.getGenericParameterTypes()[i]);
+        return ModelReference.of(path == null ? null : validPath(path), cast, String.format("parameter %s", i + 1));
+    }
+
+    private ModelPath validPath(String path) {
+        try {
+            return ModelPath.validatedPath(path);
+        } catch (ModelPath.InvalidPathException e) {
+            throw new InvalidModelRuleDeclarationException(getDescriptor(), e);
+        } catch (ModelPath.InvalidNameException e) {
+            throw new InvalidModelRuleDeclarationException(getDescriptor(), e);
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelCreatorFactory.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelCreatorFactory.java
new file mode 100644
index 0000000..43c6fe8
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelCreatorFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.instance.ManagedProxyFactory;
+import org.gradle.model.internal.manage.projection.ManagedModelProjection;
+import org.gradle.model.internal.manage.projection.ManagedSetModelProjection;
+import org.gradle.model.internal.manage.schema.ModelCollectionSchema;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.ModelStructSchema;
+
+import java.util.List;
+
+public class DefaultModelCreatorFactory implements ModelCreatorFactory {
+    private final ModelSchemaStore schemaStore;
+    private final ManagedProxyFactory proxyFactory;
+
+    public DefaultModelCreatorFactory(ModelSchemaStore schemaStore) {
+        this.schemaStore = schemaStore;
+        this.proxyFactory = new ManagedProxyFactory();
+    }
+
+    @Override
+    public <T> ModelCreator creator(ModelRuleDescriptor descriptor, ModelPath path, ModelSchema<T> schema) {
+        ModelReference<T> modelReference = ModelReference.of(path, schema.getType());
+        return creator(descriptor, modelReference, schema, null);
+    }
+
+    @Override
+    public <T> ModelCreator creator(ModelRuleDescriptor descriptor, ModelPath path, ModelSchema<T> schema, Action<? super T> initializer) {
+        ModelReference<T> modelReference = ModelReference.of(path, schema.getType());
+        ModelAction<T> modelAction = new ActionBackedModelAction<T>(modelReference, descriptor, initializer);
+        return creator(descriptor, modelReference, schema, modelAction);
+    }
+
+    @Override
+    public <T> ModelCreator creator(ModelRuleDescriptor descriptor, ModelPath path, ModelSchema<T> schema, List<ModelReference<?>> initializerInputs, BiAction<? super T, ? super List<ModelView<?>>> initializer) {
+        ModelReference<T> modelReference = ModelReference.of(path, schema.getType());
+        ModelAction<T> modelAction = new BiActionBackedModelAction<T>(modelReference, descriptor, initializerInputs, initializer);
+        return creator(descriptor, modelReference, schema, modelAction);
+    }
+
+    private <T> ModelCreator creator(ModelRuleDescriptor descriptor, ModelReference<T> modelReference, ModelSchema<T> schema, @Nullable ModelAction<T> initializer) {
+        // TODO reuse pooled projections
+        if (schema instanceof ModelCollectionSchema) {
+            ModelCollectionSchema<T> collectionSchema = (ModelCollectionSchema<T>) schema;
+            ModelSchema<?> elementSchema = schemaStore.getSchema(collectionSchema.getElementType());
+            return ModelCreators.of(modelReference, new ManagedSetInitializer<T>(initializer))
+                    .withProjection(ManagedSetModelProjection.of(elementSchema, this))
+                    .descriptor(descriptor)
+                    .build();
+        }
+        if (schema instanceof ModelStructSchema) {
+            ModelStructSchema<T> structSchema = (ModelStructSchema<T>) schema;
+            return ModelCreators.of(modelReference, new ManagedModelInitializer<T>(descriptor, structSchema, schemaStore, this, initializer))
+                    .withProjection(new ManagedModelProjection<T>(structSchema, schemaStore, proxyFactory))
+                    .descriptor(descriptor)
+                    .build();
+        }
+        throw new IllegalArgumentException("Don't know how to create model element from schema for " + schema.getType());
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelRuleInvoker.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelRuleInvoker.java
new file mode 100644
index 0000000..9e1b651
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultModelRuleInvoker.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.UncheckedException;
+import org.gradle.model.internal.method.WeaklyTypeReferencingMethod;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+
+ at ThreadSafe
+class DefaultModelRuleInvoker<I, R> implements ModelRuleInvoker<R> {
+    private final WeaklyTypeReferencingMethod<I, R> method;
+
+    DefaultModelRuleInvoker(WeaklyTypeReferencingMethod<I, R> method) {
+        this.method = method;
+    }
+
+    public R invoke(Object... args) {
+        I instance = Modifier.isStatic(method.getModifiers()) ? null : toInstance();
+        return method.invoke(instance, args);
+    }
+
+    private I toInstance() {
+        try {
+            Class<I> concreteClass = method.getTarget().getConcreteClass();
+            Constructor<I> declaredConstructor = concreteClass.getDeclaredConstructor();
+            declaredConstructor.setAccessible(true);
+            return declaredConstructor.newInstance();
+        } catch (InstantiationException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } catch (IllegalAccessException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } catch (NoSuchMethodException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getTargetException());
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultsModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultsModelRuleExtractor.java
new file mode 100644
index 0000000..6310922
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/DefaultsModelRuleExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.Defaults;
+import org.gradle.model.internal.core.ModelActionRole;
+
+ at ThreadSafe
+public class DefaultsModelRuleExtractor extends AbstractMutationModelRuleExtractor<Defaults> {
+    @Override
+    protected ModelActionRole getMutationType() {
+        return ModelActionRole.Defaults;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/FinalizeModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/FinalizeModelRuleExtractor.java
new file mode 100644
index 0000000..4657021
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/FinalizeModelRuleExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.Finalize;
+import org.gradle.model.internal.core.ModelActionRole;
+
+ at ThreadSafe
+public class FinalizeModelRuleExtractor extends AbstractMutationModelRuleExtractor<Finalize> {
+    @Override
+    protected ModelActionRole getMutationType() {
+        return ModelActionRole.Finalize;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelCreationRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelCreationRuleExtractor.java
new file mode 100644
index 0000000..97258ec
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelCreationRuleExtractor.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.specs.Spec;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.extract.InvalidManagedModelElementTypeException;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+ at NotThreadSafe
+public class ManagedModelCreationRuleExtractor extends AbstractModelCreationRuleExtractor {
+    private final ModelSchemaStore schemaStore;
+    private final ModelCreatorFactory modelCreatorFactory;
+
+    public ManagedModelCreationRuleExtractor(ModelSchemaStore schemaStore, ModelCreatorFactory modelCreatorFactory) {
+        this.schemaStore = schemaStore;
+        this.modelCreatorFactory = modelCreatorFactory;
+    }
+
+    public String getDescription() {
+        return String.format("@%s and taking a managed model element", super.getDescription());
+    }
+
+    @Override
+    public Spec<MethodRuleDefinition<?, ?>> getSpec() {
+        final Spec<MethodRuleDefinition<?, ?>> superSpec = super.getSpec();
+        return new Spec<MethodRuleDefinition<?, ?>>() {
+            public boolean isSatisfiedBy(MethodRuleDefinition<?, ?> element) {
+                return superSpec.isSatisfiedBy(element) && element.getReturnType().equals(ModelType.of(Void.TYPE));
+            }
+        };
+    }
+
+    @Override
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        String modelName = determineModelName(ruleDefinition);
+
+        List<ModelReference<?>> references = ruleDefinition.getReferences();
+        if (references.isEmpty()) {
+            throw new InvalidModelRuleDeclarationException(ruleDefinition.getDescriptor(), "a void returning model element creation rule has to take a managed model element instance as the first argument");
+        }
+
+        ModelType<?> managedType = references.get(0).getType();
+        return new ExtractedModelCreator(buildModelCreatorForManagedType(managedType, ruleDefinition, ModelPath.path(modelName)));
+    }
+
+    private <T> ModelCreator buildModelCreatorForManagedType(ModelType<T> managedType, final MethodRuleDefinition<?, ?> ruleDefinition, ModelPath modelPath) {
+        ModelSchema<T> modelSchema = getModelSchema(managedType, ruleDefinition);
+
+        if (modelSchema.getKind().equals(ModelSchema.Kind.VALUE)) {
+            throw new InvalidModelRuleDeclarationException(ruleDefinition.getDescriptor(), "a void returning model element creation rule cannot take a value type as the first parameter, which is the element being created. Return the value from the method.");
+        }
+
+        if (!modelSchema.getKind().isManaged()) {
+            String description = String.format("a void returning model element creation rule has to take an instance of a managed type as the first argument");
+            throw new InvalidModelRuleDeclarationException(ruleDefinition.getDescriptor(), description);
+        }
+
+        List<ModelReference<?>> bindings = ruleDefinition.getReferences();
+        List<ModelReference<?>> inputs = bindings.subList(1, bindings.size());
+        ModelRuleDescriptor descriptor = ruleDefinition.getDescriptor();
+
+        return modelCreatorFactory.creator(descriptor, modelPath, modelSchema, inputs, new RuleMethodBackedMutationAction<T>(ruleDefinition.getRuleInvoker()));
+    }
+
+    private <T> ModelSchema<T> getModelSchema(ModelType<T> managedType, MethodRuleDefinition<?, ?> ruleDefinition) {
+        try {
+            return schemaStore.getSchema(managedType);
+        } catch (InvalidManagedModelElementTypeException e) {
+            throw new InvalidModelRuleDeclarationException(ruleDefinition.getDescriptor(), e);
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelInitializer.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelInitializer.java
new file mode 100644
index 0000000..61d7a5b
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedModelInitializer.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.internal.BiAction;
+import org.gradle.internal.BiActions;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.schema.ModelProperty;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.ModelStructSchema;
+import org.gradle.model.internal.type.ModelType;
+
+public class ManagedModelInitializer<T> implements BiAction<MutableModelNode, Object> {
+
+    private final ModelStructSchema<T> modelSchema;
+    private final ModelAction<T> initializer;
+    private final ModelRuleDescriptor descriptor;
+    private final ModelSchemaStore schemaStore;
+    private final ModelCreatorFactory modelCreatorFactory;
+
+    public ManagedModelInitializer(ModelRuleDescriptor descriptor, ModelStructSchema<T> modelSchema, ModelSchemaStore schemaStore, ModelCreatorFactory modelCreatorFactory, ModelAction<T> initializer) {
+        this.descriptor = descriptor;
+        this.schemaStore = schemaStore;
+        this.modelCreatorFactory = modelCreatorFactory;
+        this.modelSchema = modelSchema;
+        this.initializer = initializer;
+    }
+
+    public void execute(MutableModelNode modelNode, Object object) {
+        for (ModelProperty<?> property : modelSchema.getProperties().values()) {
+            addPropertyLink(modelNode, property);
+        }
+        if (initializer != null) {
+            modelNode.applyToSelf(ModelActionRole.Initialize, initializer);
+        }
+    }
+
+    private <P> void addPropertyLink(MutableModelNode modelNode, ModelProperty<P> property) {
+        ModelType<P> propertyType = property.getType();
+        ModelSchema<P> propertySchema = schemaStore.getSchema(propertyType);
+
+        if (propertySchema.getKind().isManaged()) {
+            if (!property.isWritable()) {
+                ModelCreator creator = modelCreatorFactory.creator(descriptor, modelNode.getPath().child(property.getName()), propertySchema);
+                modelNode.addLink(creator);
+            } else {
+                ModelProjection projection = new UnmanagedModelProjection<P>(propertyType, true, true);
+                ModelCreator creator = ModelCreators.of(ModelReference.of(modelNode.getPath().child(property.getName()), propertyType), BiActions.doNothing())
+                        .withProjection(projection)
+                        .descriptor(descriptor).build();
+                modelNode.addReference(creator);
+            }
+        } else {
+            ModelProjection projection = new UnmanagedModelProjection<P>(propertyType, true, true);
+            ModelCreator creator = ModelCreators.of(ModelReference.of(modelNode.getPath().child(property.getName()), propertyType), BiActions.doNothing())
+                    .withProjection(projection)
+                    .descriptor(descriptor).build();
+            modelNode.addLink(creator);
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedSetInitializer.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedSetInitializer.java
new file mode 100644
index 0000000..5cea45c
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ManagedSetInitializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.ModelAction;
+import org.gradle.model.internal.core.ModelActionRole;
+import org.gradle.model.internal.core.ModelView;
+import org.gradle.model.internal.core.MutableModelNode;
+
+import java.util.List;
+
+class ManagedSetInitializer<T> implements BiAction<MutableModelNode, List<ModelView<?>>> {
+    private final ModelAction<T> modelAction;
+
+    public ManagedSetInitializer(ModelAction<T> modelAction) {
+        this.modelAction = modelAction;
+    }
+
+    @Override
+    public void execute(MutableModelNode modelNode, List<ModelView<?>> inputs) {
+        if (modelAction != null) {
+            modelNode.applyToSelf(ModelActionRole.Initialize, modelAction);
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodBackedModelAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodBackedModelAction.java
new file mode 100644
index 0000000..d4e7c56
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodBackedModelAction.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.internal.core.ModelAction;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.ModelView;
+import org.gradle.model.internal.core.MutableModelNode;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.List;
+
+class MethodBackedModelAction<T> implements ModelAction<T> {
+    private final ModelRuleDescriptor descriptor;
+    private final ModelReference<T> subject;
+    private final List<ModelReference<?>> inputs;
+    private final ModelRuleInvoker<?> ruleInvoker;
+
+    public MethodBackedModelAction(MethodRuleDefinition<?, T> ruleDefinition) {
+        this(ruleDefinition.getRuleInvoker(), ruleDefinition.getDescriptor(), ruleDefinition.getSubjectReference(), ruleDefinition.getTailReferences());
+    }
+
+    public MethodBackedModelAction(ModelRuleInvoker<?> ruleInvoker, ModelRuleDescriptor descriptor, ModelReference<T> subject, List<ModelReference<?>> inputs) {
+        this.ruleInvoker = ruleInvoker;
+        this.subject = subject;
+        this.inputs = inputs;
+        this.descriptor = descriptor;
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    public ModelReference<T> getSubject() {
+        return subject;
+    }
+
+    public List<ModelReference<?>> getInputs() {
+        return inputs;
+    }
+
+    @Override
+    public void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs) {
+        Object[] args = new Object[1 + this.inputs.size()];
+        args[0] = object;
+        for (int i = 0; i < this.inputs.size(); ++i) {
+            args[i + 1] = inputs.get(i).getInstance();
+        }
+        ruleInvoker.invoke(args);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractor.java
new file mode 100644
index 0000000..cb0a31c
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.api.Nullable;
+import org.gradle.api.specs.Spec;
+import org.gradle.model.internal.core.ExtractedModelRule;
+
+public interface MethodModelRuleExtractor {
+    Spec<MethodRuleDefinition<?, ?>> getSpec();
+
+    String getDescription();
+
+    @Nullable
+    <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractors.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractors.java
new file mode 100644
index 0000000..0fa9130
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodModelRuleExtractors.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.ModelCreatorFactory;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+
+import java.util.List;
+
+ at ThreadSafe
+abstract public class MethodModelRuleExtractors {
+
+    public static List<MethodModelRuleExtractor> coreExtractors(ModelSchemaStore modelSchemaStore, ModelCreatorFactory modelCreatorFactory) {
+        return ImmutableList.<MethodModelRuleExtractor>of(
+                new UnmanagedModelCreationRuleExtractor(),
+                new ManagedModelCreationRuleExtractor(modelSchemaStore, modelCreatorFactory),
+                new DefaultsModelRuleExtractor(),
+                new MutateModelRuleExtractor(),
+                new FinalizeModelRuleExtractor(),
+                new ValidateModelRuleExtractor()
+        );
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodRuleDefinition.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodRuleDefinition.java
new file mode 100644
index 0000000..acea4b6
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MethodRuleDefinition.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+public interface MethodRuleDefinition<R, S> {
+
+    String getMethodName();
+
+    <A extends Annotation> A getAnnotation(Class<A> annotationType);
+
+    ModelType<R> getReturnType();
+
+    List<ModelReference<?>> getReferences();
+
+    @Nullable
+    ModelReference<S> getSubjectReference();
+
+    List<ModelReference<?>> getTailReferences();
+
+    ModelRuleDescriptor getDescriptor();
+
+    ModelRuleInvoker<R> getRuleInvoker();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleExtractor.java
new file mode 100644
index 0000000..ef690a9
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleExtractor.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import com.google.common.base.Joiner;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Transformer;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.reflect.MethodDescription;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.ExtractedModelRule;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.lang.reflect.*;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+ at ThreadSafe
+public class ModelRuleExtractor {
+
+    final LoadingCache<Class<?>, List<ExtractedModelRule>> cache = CacheBuilder.newBuilder()
+            .weakKeys()
+            .build(new CacheLoader<Class<?>, List<ExtractedModelRule>>() {
+                public List<ExtractedModelRule> load(Class<?> source) throws Exception {
+                    return doExtract(source);
+                }
+            });
+
+    private final Iterable<MethodModelRuleExtractor> handlers;
+
+    public ModelRuleExtractor(Iterable<MethodModelRuleExtractor> handlers) {
+        this.handlers = handlers;
+    }
+
+    private String describeHandlers() {
+        String desc = Joiner.on(", ").join(CollectionUtils.collect(handlers, new Transformer<String, MethodModelRuleExtractor>() {
+            public String transform(MethodModelRuleExtractor original) {
+                return original.getDescription();
+            }
+        }));
+
+        return "[" + desc + "]";
+    }
+
+    private static RuntimeException invalid(Class<?> source, String reason) {
+        return invalid(source, reason, null);
+    }
+
+    private static RuntimeException invalid(Class<?> source, String reason, Throwable throwable) {
+        return new InvalidModelRuleDeclarationException("Type " + source.getName() + " is not a valid model rule source: " + reason, throwable);
+    }
+
+    private static RuntimeException invalidMethod(Method method, String reason) {
+        String description = MethodDescription.name(method.getName())
+                .owner(method.getDeclaringClass())
+                .takes(method.getGenericParameterTypes())
+                .toString();
+        return invalid(description, reason);
+    }
+
+    private static RuntimeException invalid(ModelRuleDescriptor rule, String reason) {
+        StringBuilder sb = new StringBuilder();
+        rule.describeTo(sb);
+        return invalid(sb.toString(), reason);
+    }
+
+    private static RuntimeException invalid(String ruleDescription, String reason) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ruleDescription).append(" is not a valid model rule method").append(": ").append(reason);
+        return new InvalidModelRuleDeclarationException(sb.toString());
+    }
+
+    public Iterable<ExtractedModelRule> extract(Class<?> source) {
+        try {
+            return cache.get(source);
+        } catch (ExecutionException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } catch (UncheckedExecutionException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getCause());
+        }
+    }
+
+    private List<ExtractedModelRule> doExtract(Class<?> source) {
+        validate(source);
+        final Method[] methods = source.getDeclaredMethods();
+
+        // sort for determinism
+        Arrays.sort(methods, new Comparator<Method>() {
+            public int compare(Method o1, Method o2) {
+                return o1.toString().compareTo(o2.toString());
+            }
+        });
+
+        ImmutableList.Builder<ExtractedModelRule> registrations = ImmutableList.builder();
+
+        for (Method method : methods) {
+            if (method.getTypeParameters().length > 0) {
+                throw invalidMethod(method, "cannot have type variables (i.e. cannot be a generic method)");
+            }
+
+            MethodRuleDefinition<?, ?> ruleDefinition = DefaultMethodRuleDefinition.create(source, method);
+            MethodModelRuleExtractor handler = getMethodHandler(ruleDefinition);
+            if (handler != null) {
+                validateMethod(method);
+                ExtractedModelRule registration = handler.registration(ruleDefinition);
+                if (registration != null) {
+                    registrations.add(registration);
+                }
+            }
+        }
+        return registrations.build();
+    }
+
+    private MethodModelRuleExtractor getMethodHandler(MethodRuleDefinition<?, ?> ruleDefinition) {
+        MethodModelRuleExtractor handler = null;
+        for (MethodModelRuleExtractor candidateHandler : handlers) {
+            if (candidateHandler.getSpec().isSatisfiedBy(ruleDefinition)) {
+                if (handler == null) {
+                    handler = candidateHandler;
+                } else {
+                    throw invalid(ruleDefinition.getDescriptor(), "can only be one of " + describeHandlers());
+                }
+            }
+        }
+        return handler;
+    }
+
+    /**
+     * Validates that the given class is effectively static and has no instance state.
+     *
+     * @param source the class the validate
+     */
+    public void validate(Class<?> source) throws InvalidModelRuleDeclarationException {
+        // TODO - exceptions thrown here should point to some extensive documentation on the concept of class rule sources
+
+        int modifiers = source.getModifiers();
+
+        if (Modifier.isInterface(modifiers)) {
+            throw invalid(source, "must be a class, not an interface");
+        }
+
+        if (!RuleSource.class.isAssignableFrom(source) || !source.getSuperclass().equals(RuleSource.class)) {
+            throw invalid(source, "rule source classes must directly extend " + RuleSource.class.getName());
+        }
+
+        if (Modifier.isAbstract(modifiers)) {
+            throw invalid(source, "class cannot be abstract");
+        }
+
+        if (source.getEnclosingClass() != null) {
+            if (Modifier.isStatic(modifiers)) {
+                if (Modifier.isPrivate(modifiers)) {
+                    throw invalid(source, "class cannot be private");
+                }
+            } else {
+                throw invalid(source, "enclosed classes must be static and non private");
+            }
+        }
+
+        Constructor<?>[] constructors = source.getDeclaredConstructors();
+        for (Constructor<?> constructor : constructors) {
+            if (constructor.getParameterTypes().length > 0) {
+                throw invalid(source, "cannot declare a constructor that takes arguments");
+            }
+        }
+
+        try {
+            Constructor<?> constructor = constructors[0];
+            constructor.setAccessible(true);
+            constructor.newInstance();
+        } catch (InvocationTargetException e) {
+            throw invalid(source, "instance creation failed", e.getCause());
+        } catch (InstantiationException e) {
+            throw invalid(source, "instance creation failed", e);
+        } catch (IllegalAccessException e) {
+            throw invalid(source, "must have an accessible constructor", e);
+        }
+
+        Field[] fields = source.getDeclaredFields();
+        for (Field field : fields) {
+            int fieldModifiers = field.getModifiers();
+            if (!field.isSynthetic() && !(Modifier.isStatic(fieldModifiers) && Modifier.isFinal(fieldModifiers))) {
+                throw invalid(source, "field " + field.getName() + " is not static final");
+            }
+        }
+    }
+
+    private void validateMethod(Method ruleMethod) {
+        // TODO validations on method: synthetic, bridge methods, varargs, abstract, native
+        ModelType<?> returnType = ModelType.returnType(ruleMethod);
+        if (returnType.isRawClassOfParameterizedType()) {
+            throw invalidMethod(ruleMethod, "raw type " + returnType + " used for return type (all type parameters must be specified of parameterized type)");
+        }
+
+        int i = 0;
+        for (Type type : ruleMethod.getGenericParameterTypes()) {
+            ++i;
+            ModelType<?> modelType = ModelType.of(type);
+            if (modelType.isRawClassOfParameterizedType()) {
+                throw invalidMethod(ruleMethod, "raw type " + modelType + " used for parameter " + i + " (all type parameters must be specified of parameterized type)");
+            }
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleInvoker.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleInvoker.java
new file mode 100644
index 0000000..bda2621
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleInvoker.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+public interface ModelRuleInvoker<R> {
+    R invoke(Object... args);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleSourceDetector.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleSourceDetector.java
new file mode 100644
index 0000000..12eb516
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ModelRuleSourceDetector.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.Cast;
+import org.gradle.internal.UncheckedException;
+import org.gradle.model.RuleSource;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.ExecutionException;
+
+ at ThreadSafe
+public class ModelRuleSourceDetector {
+
+    private static final Comparator<Class<?>> COMPARE_BY_CLASS_NAME = new Comparator<Class<?>>() {
+        public int compare(Class<?> left, Class<?> right) {
+            return left.getName().compareTo(right.getName());
+        }
+    };
+
+    final LoadingCache<Class<?>, Collection<Reference<Class<? extends RuleSource>>>> cache = CacheBuilder.newBuilder()
+            .weakKeys()
+            .build(new CacheLoader<Class<?>, Collection<Reference<Class<? extends RuleSource>>>>() {
+                @Override
+                public Collection<Reference<Class<? extends RuleSource>>> load(@SuppressWarnings("NullableProblems") Class<?> container) throws Exception {
+                    if (isRuleSource(container)) {
+                        Class<? extends RuleSource> castClass = Cast.uncheckedCast(container);
+                        return ImmutableSet.<Reference<Class<? extends RuleSource>>>of(new WeakReference<Class<? extends RuleSource>>(castClass));
+                    }
+
+                    Class<?>[] declaredClasses = container.getDeclaredClasses();
+
+                    if (declaredClasses.length == 0) {
+                        return Collections.emptySet();
+                    } else {
+                        Class<?>[] sortedDeclaredClasses = new Class<?>[declaredClasses.length];
+                        System.arraycopy(declaredClasses, 0, sortedDeclaredClasses, 0, declaredClasses.length);
+                        Arrays.sort(sortedDeclaredClasses, COMPARE_BY_CLASS_NAME);
+
+                        ImmutableList.Builder<Reference<Class<? extends RuleSource>>> found = ImmutableList.builder();
+                        for (Class<?> declaredClass : sortedDeclaredClasses) {
+                            if (isRuleSource(declaredClass)) {
+                                Class<? extends RuleSource> castClass = Cast.uncheckedCast(declaredClass);
+                                found.add(new WeakReference<Class<? extends RuleSource>>(castClass));
+                            }
+                        }
+
+                        return found.build();
+                    }
+                }
+            });
+
+    // TODO return a richer data structure that provides meta data about how the source was found, for use is diagnostics
+    public Iterable<Class<? extends RuleSource>> getDeclaredSources(Class<?> container) {
+        try {
+            return FluentIterable.from(cache.get(container))
+                    .transform(new Function<Reference<Class<? extends RuleSource>>, Class<? extends RuleSource>>() {
+                        @Override
+                        public Class<? extends RuleSource> apply(Reference<Class<? extends RuleSource>> input) {
+                            return input.get();
+                        }
+                    })
+                    .filter(Predicates.notNull());
+        } catch (ExecutionException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public boolean hasRules(Class<?> container) {
+        return !Iterables.isEmpty(getDeclaredSources(container));
+    }
+
+    private boolean isRuleSource(Class<?> clazz) {
+        return RuleSource.class.isAssignableFrom(clazz);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MutateModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MutateModelRuleExtractor.java
new file mode 100644
index 0000000..a6bd6dc
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/MutateModelRuleExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.Mutate;
+import org.gradle.model.internal.core.ModelActionRole;
+
+ at ThreadSafe
+public class MutateModelRuleExtractor extends AbstractMutationModelRuleExtractor<Mutate> {
+    @Override
+    protected ModelActionRole getMutationType() {
+        return ModelActionRole.Mutate;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/RuleMethodBackedMutationAction.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/RuleMethodBackedMutationAction.java
new file mode 100644
index 0000000..d067907
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/RuleMethodBackedMutationAction.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.ModelView;
+
+import java.util.List;
+
+public class RuleMethodBackedMutationAction<T> implements BiAction<T, List<ModelView<?>>> {
+    private final ModelRuleInvoker<?> ruleInvoker;
+
+    public RuleMethodBackedMutationAction(ModelRuleInvoker<?> ruleInvoker) {
+        this.ruleInvoker = ruleInvoker;
+    }
+
+    public void execute(T subject, List<ModelView<?>> inputs) {
+        Object[] args = new Object[inputs.size() + 1];
+        args[0] = subject;
+        for (int i = 0; i < inputs.size(); i++) {
+            args[i + 1] = inputs.get(i).getInstance();
+        }
+        ruleInvoker.invoke(args);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/UnmanagedModelCreationRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/UnmanagedModelCreationRuleExtractor.java
new file mode 100644
index 0000000..e9bcfc9
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/UnmanagedModelCreationRuleExtractor.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.BiAction;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+ at ThreadSafe
+public class UnmanagedModelCreationRuleExtractor extends AbstractModelCreationRuleExtractor {
+
+    @Override
+    public Spec<MethodRuleDefinition<?, ?>> getSpec() {
+        final Spec<MethodRuleDefinition<?, ?>> superSpec = super.getSpec();
+        return new Spec<MethodRuleDefinition<?, ?>>() {
+            public boolean isSatisfiedBy(MethodRuleDefinition<?, ?> element) {
+                return superSpec.isSatisfiedBy(element) && !element.getReturnType().equals(ModelType.of(Void.TYPE));
+            }
+        };
+    }
+
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        String modelName = determineModelName(ruleDefinition);
+
+        ModelType<R> returnType = ruleDefinition.getReturnType();
+        List<ModelReference<?>> references = ruleDefinition.getReferences();
+        ModelRuleDescriptor descriptor = ruleDefinition.getDescriptor();
+
+        BiAction<MutableModelNode, List<ModelView<?>>> transformer = new ModelRuleInvokerBackedTransformer<R>(returnType, ruleDefinition.getRuleInvoker(), descriptor);
+        ModelCreator modelCreator = ModelCreators.of(ModelReference.of(ModelPath.path(modelName), returnType), transformer)
+                .withProjection(new UnmanagedModelProjection<R>(returnType, true, true))
+                .descriptor(descriptor)
+                .inputs(references)
+                .build();
+
+        return new ExtractedModelCreator(modelCreator);
+    }
+
+    public String getDescription() {
+        return String.format("%s and returning a model element", super.getDescription());
+    }
+
+    private static class ModelRuleInvokerBackedTransformer<T> implements BiAction<MutableModelNode, List<ModelView<?>>> {
+
+        private final ModelType<T> type;
+        private final ModelRuleDescriptor descriptor;
+        private final ModelRuleInvoker<T> ruleInvoker;
+
+        private ModelRuleInvokerBackedTransformer(ModelType<T> type, ModelRuleInvoker<T> ruleInvoker, ModelRuleDescriptor descriptor) {
+            this.type = type;
+            this.descriptor = descriptor;
+            this.ruleInvoker = ruleInvoker;
+        }
+
+        public void execute(MutableModelNode modelNode, List<ModelView<?>> inputs) {
+            T instance;
+            if (inputs.size() == 0) {
+                instance = ruleInvoker.invoke();
+            } else {
+                Object[] args = new Object[inputs.size()];
+                for (int i = 0; i < inputs.size(); i++) {
+                    args[i] = inputs.get(i).getInstance();
+                }
+
+                instance = ruleInvoker.invoke(args);
+            }
+            if (instance == null) {
+                throw new ModelRuleExecutionException(descriptor, "rule returned null");
+            }
+            modelNode.setPrivateData(type, instance);
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ValidateModelRuleExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ValidateModelRuleExtractor.java
new file mode 100644
index 0000000..c91b8c9
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/inspect/ValidateModelRuleExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.Validate;
+import org.gradle.model.internal.core.ModelActionRole;
+
+ at ThreadSafe
+public class ValidateModelRuleExtractor extends AbstractMutationModelRuleExtractor<Validate> {
+    @Override
+    protected ModelActionRole getMutationType() {
+        return ModelActionRole.Validate;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedInstance.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedInstance.java
new file mode 100644
index 0000000..6293ed9
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedInstance.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.instance;
+
+import org.gradle.model.internal.core.MutableModelNode;
+
+/**
+ * A marker interface that is implemented by instances of managed model types
+ */
+public interface ManagedInstance {
+    /**
+     * Returns the node that this managed instance is backed by.
+     */
+    MutableModelNode getBackingNode();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedProxyFactory.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedProxyFactory.java
new file mode 100644
index 0000000..76d7091
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ManagedProxyFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.instance;
+
+import org.gradle.internal.UncheckedException;
+import org.gradle.model.internal.manage.schema.ModelStructSchema;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+public class ManagedProxyFactory {
+
+    public <T> T createProxy(ModelElementState state, ModelStructSchema<T> schema) {
+        try {
+            Class<? extends T> generatedClass = schema.getManagedImpl();
+            if (generatedClass == null) {
+                throw new IllegalStateException("No managed implementation class available for: " + schema.getType());
+            }
+            Constructor<? extends T> constructor = generatedClass.getConstructor(ModelElementState.class);
+            return constructor.newInstance(state);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getTargetException());
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ModelElementState.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ModelElementState.java
new file mode 100644
index 0000000..bf49330
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/instance/ModelElementState.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.instance;
+
+import org.gradle.model.internal.core.MutableModelNode;
+
+public interface ModelElementState {
+    MutableModelNode getBackingNode();
+
+    String getDisplayName();
+
+    Object get(String name);
+
+    void set(String name, Object value);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedModelProjection.java
new file mode 100644
index 0000000..b485302
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedModelProjection.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.projection;
+
+import org.gradle.internal.Cast;
+import org.gradle.model.ModelViewClosedException;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelView;
+import org.gradle.model.internal.core.MutableModelNode;
+import org.gradle.model.internal.core.TypeCompatibilityModelProjectionSupport;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.instance.ManagedInstance;
+import org.gradle.model.internal.manage.instance.ManagedProxyFactory;
+import org.gradle.model.internal.manage.instance.ModelElementState;
+import org.gradle.model.internal.manage.schema.ModelProperty;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.ModelStructSchema;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ManagedModelProjection<M> extends TypeCompatibilityModelProjectionSupport<M> {
+
+    private final ModelSchemaStore schemaStore;
+    private final ManagedProxyFactory proxyFactory;
+    private final ModelStructSchema<M> schema;
+
+    public ManagedModelProjection(ModelStructSchema<M> schema, ModelSchemaStore schemaStore, ManagedProxyFactory proxyFactory) {
+        super(schema.getType(), true, true);
+        this.schema = schema;
+        this.schemaStore = schemaStore;
+        this.proxyFactory = proxyFactory;
+    }
+
+    @Override
+    protected ModelView<M> toView(final MutableModelNode modelNode, final ModelRuleDescriptor ruleDescriptor, final boolean writable) {
+        return new ModelView<M>() {
+
+            private boolean closed;
+            private final Map<String, Object> propertyViews = new HashMap<String, Object>();
+
+            @Override
+            public ModelPath getPath() {
+                return modelNode.getPath();
+            }
+
+            public ModelType<M> getType() {
+                return ManagedModelProjection.this.getType();
+            }
+
+            public M getInstance() {
+                return proxyFactory.createProxy(new State(), schema);
+            }
+
+            public void close() {
+                closed = true;
+            }
+
+            class State implements ModelElementState {
+                @Override
+                public MutableModelNode getBackingNode() {
+                    return modelNode;
+                }
+
+                @Override
+                public String getDisplayName() {
+                    return String.format("%s '%s'", getType(), modelNode.getPath().toString());
+                }
+
+                public Object get(String name) {
+                    if (propertyViews.containsKey(name)) {
+                        return propertyViews.get(name);
+                    }
+
+                    ModelProperty<?> property = schema.getProperties().get(name);
+
+                    Object value = doGet(property, name);
+                    propertyViews.put(name, value);
+                    return value;
+                }
+
+                private <T> T doGet(ModelProperty<T> property, String propertyName) {
+                    ModelType<T> propertyType = property.getType();
+                    ModelSchema<T> schema = schemaStore.getSchema(propertyType);
+
+                    // TODO we are relying on the creator having established these links, we should be checking
+                    MutableModelNode propertyNode = modelNode.getLink(propertyName);
+                    propertyNode.ensureUsable();
+
+                    MutableModelNode targetNode = propertyNode;
+                    if (property.isWritable() && schema.getKind().isManaged()) {
+                        targetNode = propertyNode.getTarget();
+                        if (targetNode == null) {
+                            return null;
+                        }
+                    }
+
+                    if (writable) {
+                        ModelView<? extends T> modelView = targetNode.asWritable(propertyType, ruleDescriptor, null);
+                        if (closed) {
+                            modelView.close();
+                        }
+                        return modelView.getInstance();
+                    } else {
+                        return targetNode.asReadOnly(propertyType, ruleDescriptor).getInstance();
+                    }
+                }
+
+                public void set(String name, Object value) {
+                    if (!writable || closed) {
+                        throw new ModelViewClosedException(getType(), ruleDescriptor);
+                    }
+
+                    ModelProperty<?> property = schema.getProperties().get(name);
+                    ModelType<?> propertyType = property.getType();
+
+                    doSet(name, value, propertyType);
+                    propertyViews.put(name, value);
+                }
+
+                private <T> void doSet(String name, Object value, ModelType<T> propertyType) {
+                    ModelSchema<T> schema = schemaStore.getSchema(propertyType);
+
+                    // TODO we are relying on the creator having established these links, we should be checking
+                    MutableModelNode propertyNode = modelNode.getLink(name);
+                    propertyNode.ensureUsable();
+
+                    if (schema.getKind().isManaged()) {
+                        if (value == null) {
+                            propertyNode.setTarget(null);
+                        } else if (ManagedInstance.class.isInstance(value)) {
+                            ManagedInstance managedInstance = (ManagedInstance) value;
+                            MutableModelNode targetNode = managedInstance.getBackingNode();
+                            propertyNode.setTarget(targetNode);
+                        } else {
+                            throw new IllegalArgumentException(String.format("Only managed model instances can be set as property '%s' of class '%s'", name, getType()));
+                        }
+                    } else {
+                        T castValue = Cast.uncheckedCast(value);
+                        propertyNode.setPrivateData(propertyType, castValue);
+                    }
+                }
+            }
+        };
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return this == o || !(o == null || getClass() != o.getClass()) && super.equals(o);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedSetModelProjection.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedSetModelProjection.java
new file mode 100644
index 0000000..438d616
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/projection/ManagedSetModelProjection.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.projection;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.model.ModelViewClosedException;
+import org.gradle.model.WriteOnlyModelViewException;
+import org.gradle.model.collection.ManagedSet;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.NestedModelRuleDescriptor;
+import org.gradle.model.internal.manage.instance.ManagedInstance;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class ManagedSetModelProjection<I> extends TypeCompatibilityModelProjectionSupport<ManagedSet<I>> {
+    private final ModelCreatorFactory modelCreatorFactory;
+    private final ModelSchema<I> elementSchema;
+
+    private ManagedSetModelProjection(ModelSchema<I> elementSchema, ModelCreatorFactory modelCreatorFactory) {
+        super(typeOf(elementSchema.getType()), true, true);
+        this.elementSchema = elementSchema;
+        this.modelCreatorFactory = modelCreatorFactory;
+    }
+
+    public static <I> ManagedSetModelProjection<I> of(ModelSchema<I> elementSchema, ModelCreatorFactory modelCreatorFactory) {
+        return new ManagedSetModelProjection<I>(elementSchema, modelCreatorFactory);
+    }
+
+    public static <I> ModelType<ManagedSet<I>> typeOf(ModelType<I> elementType) {
+        return new ModelType.Builder<ManagedSet<I>>() {
+        }.where(new ModelType.Parameter<I>() {
+        }, elementType).build();
+    }
+
+    @Override
+    protected ModelView<ManagedSet<I>> toView(final MutableModelNode modelNode, final ModelRuleDescriptor ruleDescriptor, final boolean writable) {
+        return new ManagedSetModelView<I>(getType(), elementSchema, modelNode, writable, ruleDescriptor, modelCreatorFactory);
+    }
+
+    private static class ManagedSetModelView<I> implements ModelView<ManagedSet<I>> {
+        private final ModelType<ManagedSet<I>> type;
+        private final ModelSchema<I> elementSchema;
+        private final MutableModelNode modelNode;
+        private final boolean writable;
+        private final ModelRuleDescriptor ruleDescriptor;
+        private final ModelCreatorFactory modelCreatorFactory;
+        private boolean closed;
+        private Set<I> elementViews;
+        private final ModelReference<I> elementReference;
+
+        public ManagedSetModelView(ModelType<ManagedSet<I>> type, ModelSchema<I> elementSchema, MutableModelNode modelNode, boolean writable, ModelRuleDescriptor ruleDescriptor, ModelCreatorFactory modelCreatorFactory) {
+            this.type = type;
+            this.elementSchema = elementSchema;
+            this.modelNode = modelNode;
+            this.writable = writable;
+            this.ruleDescriptor = ruleDescriptor;
+            this.modelCreatorFactory = modelCreatorFactory;
+            this.elementReference = ModelReference.of(elementSchema.getType());
+        }
+
+        @Override
+        public ModelPath getPath() {
+            return modelNode.getPath();
+        }
+
+        @Override
+        public ModelType<ManagedSet<I>> getType() {
+            return type;
+        }
+
+        @Override
+        public ManagedSet<I> getInstance() {
+            return new ModelNodeBackedManagedSet();
+        }
+
+        @Override
+        public void close() {
+            closed = true;
+        }
+
+        private void ensureReadable() {
+            if (writable && !closed) {
+                throw new WriteOnlyModelViewException(getType(), ruleDescriptor);
+            }
+            if (elementViews == null) {
+                elementViews = new LinkedHashSet<I>();
+                for (MutableModelNode node : modelNode.getLinks(elementSchema.getType())) {
+                    elementViews.add(node.asReadOnly(elementSchema.getType(), ruleDescriptor).getInstance());
+                }
+            }
+        }
+
+
+        public class ModelNodeBackedManagedSet implements ManagedSet<I>, ManagedInstance {
+            @Override
+            public MutableModelNode getBackingNode() {
+                return modelNode;
+            }
+
+            @Override
+            public String toString() {
+                return String.format("%s '%s'", getType(), modelNode.getPath().toString());
+            }
+
+            @Override
+            public void create(final Action<? super I> action) {
+                assertMutable();
+
+                // Generate a synthetic path for the element
+                String name = String.valueOf(modelNode.getLinkCount(elementSchema.getType()));
+                ModelPath path = modelNode.getPath().child(name);
+
+                modelNode.addLink(modelCreatorFactory.creator(ruleDescriptor, path, elementSchema, action));
+            }
+
+            public void assertMutable() {
+                if (!writable || closed) {
+                    throw new ModelViewClosedException(getType(), ruleDescriptor);
+                }
+            }
+
+            @Override
+            public void afterEach(Action<? super I> configAction) {
+                assertMutable();
+                modelNode.applyToAllLinks(ModelActionRole.Finalize, new ActionBackedModelAction<I>(elementReference, NestedModelRuleDescriptor.append(ruleDescriptor, "afterEach()"), configAction));
+            }
+
+            @Override
+            public void beforeEach(Action<? super I> configAction) {
+                assertMutable();
+                modelNode.applyToAllLinks(ModelActionRole.Defaults, new ActionBackedModelAction<I>(elementReference, NestedModelRuleDescriptor.append(ruleDescriptor, "afterEach()"), configAction));
+            }
+
+            @Override
+            public int size() {
+                ensureReadable();
+                return elementViews.size();
+            }
+
+            @Override
+            public boolean isEmpty() {
+                ensureReadable();
+                return elementViews.isEmpty();
+            }
+
+            @Override
+            public boolean contains(Object o) {
+                ensureReadable();
+                return elementViews.contains(o);
+            }
+
+            @Override
+            public Iterator<I> iterator() {
+                ensureReadable();
+                return elementViews.iterator();
+            }
+
+            @Override
+            public Object[] toArray() {
+                ensureReadable();
+                return elementViews.toArray();
+            }
+
+            @Override
+            public <T> T[] toArray(T[] a) {
+                ensureReadable();
+                return elementViews.toArray(a);
+            }
+
+            @Override
+            public boolean add(I e) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean remove(Object o) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean containsAll(Collection<?> c) {
+                ensureReadable();
+                return elementViews.containsAll(c);
+            }
+
+            @Override
+            public boolean addAll(Collection<? extends I> c) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean retainAll(Collection<?> c) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean removeAll(Collection<?> c) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void clear() {
+                throw new UnsupportedOperationException();
+            }
+
+            // TODO - mix this in using decoration. Also validate closure parameter types, if declared
+            public void create(Closure<?> closure) {
+                create(ClosureBackedAction.of(closure));
+            }
+
+            public void afterEach(Closure<?> closure) {
+                afterEach(ClosureBackedAction.of(closure));
+            }
+
+            public void beforeEach(Closure<?> closure) {
+                beforeEach(ClosureBackedAction.of(closure));
+            }
+
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelCollectionSchema.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelCollectionSchema.java
new file mode 100644
index 0000000..a79a3a4
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelCollectionSchema.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema;
+
+import org.gradle.model.internal.type.ModelType;
+
+public class ModelCollectionSchema<T> extends ModelSchema<T> {
+    private final ModelType<?> elementType;
+
+    public ModelCollectionSchema(ModelType<T> type, ModelType<?> elementType) {
+        super(type, Kind.COLLECTION);
+        this.elementType = elementType;
+    }
+
+    public ModelType<?> getElementType() {
+        return elementType;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelProperty.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelProperty.java
new file mode 100644
index 0000000..78e20e1
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelProperty.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema;
+
+import com.google.common.collect.ImmutableSet;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Set;
+
+ at ThreadSafe
+public class ModelProperty<T> {
+
+    private final String name;
+    private final ModelType<T> type;
+    private final boolean writable;
+    private final Set<ModelType<?>> declaredBy;
+    private final boolean unmanaged;
+
+    private ModelProperty(ModelType<T> type, String name, boolean writable, Set<ModelType<?>> declaredBy, boolean unmanaged) {
+        this.name = name;
+        this.type = type;
+        this.writable = writable;
+        this.declaredBy = ImmutableSet.copyOf(declaredBy);
+        this.unmanaged = unmanaged;
+    }
+
+    public static <T> ModelProperty<T> of(ModelType<T> type, String name, boolean writable, Set<ModelType<?>> declaredBy, boolean unmanaged) {
+        return new ModelProperty<T>(type, name, writable, declaredBy, unmanaged);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isUnmanaged() {
+        return unmanaged;
+    }
+
+    public ModelType<T> getType() {
+        return type;
+    }
+
+    public boolean isWritable() {
+        return writable;
+    }
+
+    public Set<ModelType<?>> getDeclaredBy() {
+        return declaredBy;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ModelProperty<?> that = (ModelProperty<?>) o;
+
+
+        return name.equals(that.name) && type.equals(that.type) && writable == that.writable;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + type.hashCode();
+        result = 31 * result + Boolean.valueOf(writable).hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchema.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchema.java
new file mode 100644
index 0000000..d2c1997
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchema.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.type.ModelType;
+
+ at ThreadSafe
+public class ModelSchema<T> {
+
+    public static enum Kind {
+        VALUE(false, true), // at the moment we are conflating this with unstructured primitives
+        COLLECTION,
+        STRUCT, // type is guaranteed to be an interface
+        UNMANAGED(false, false); // some type we know nothing about
+
+        private final boolean isManaged;
+        private final boolean isAllowedPropertyTypeOfManagedType;
+
+        private Kind() {
+            this(true, true);
+        }
+
+        private Kind(boolean isManaged, boolean isAllowedPropertyTypeOfManagedType) {
+            this.isManaged = isManaged;
+            this.isAllowedPropertyTypeOfManagedType = isAllowedPropertyTypeOfManagedType;
+        }
+
+        public boolean isManaged() {
+            return isManaged;
+        }
+
+        public boolean isAllowedPropertyTypeOfManagedType() {
+            return isAllowedPropertyTypeOfManagedType;
+        }
+    }
+
+    private final ModelType<T> type;
+    private final Kind kind;
+
+    public static <T> ModelSchema<T> value(ModelType<T> type) {
+        return new ModelSchema<T>(type, Kind.VALUE);
+    }
+
+    public static <T> ModelStructSchema<T> struct(ModelType<T> type, Iterable<ModelProperty<?>> properties, Class<? extends T> managedImpl) {
+        return new ModelStructSchema<T>(type, properties, managedImpl);
+    }
+
+    public static <T> ModelCollectionSchema<T> collection(ModelType<T> type, ModelType<?> elementType) {
+        return new ModelCollectionSchema<T>(type, elementType);
+    }
+
+    public static <T> ModelSchema<T> unmanaged(ModelType<T> type) {
+        return new ModelSchema<T>(type, Kind.UNMANAGED);
+    }
+
+    protected ModelSchema(ModelType<T> type, Kind kind) {
+        this.type = type;
+        this.kind = kind;
+    }
+
+    public ModelType<T> getType() {
+        return type;
+    }
+
+    public Kind getKind() {
+        return kind;
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchemaStore.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchemaStore.java
new file mode 100644
index 0000000..7dead48
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelSchemaStore.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema;
+
+import org.gradle.model.internal.type.ModelType;
+
+public interface ModelSchemaStore {
+
+    <T> ModelSchema<T> getSchema(ModelType<T> type);
+
+    /**
+     * Remove any cached information for types that have been GC'd.
+     */
+    void cleanUp(); // TODO hook this in to the (daemon) build lifecycle
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelStructSchema.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelStructSchema.java
new file mode 100644
index 0000000..414d462
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/ModelStructSchema.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema;
+
+import com.google.common.collect.ImmutableSortedMap;
+import org.gradle.model.internal.type.ModelType;
+
+import java.lang.ref.WeakReference;
+
+public class ModelStructSchema<T> extends ModelSchema<T> {
+    private final WeakReference<Class<? extends T>> managedImpl;
+    private final ImmutableSortedMap<String, ModelProperty<?>> properties;
+
+    public ModelStructSchema(ModelType<T> type, Iterable<ModelProperty<?>> properties, Class<? extends T> managedImpl) {
+        super(type, Kind.STRUCT);
+        ImmutableSortedMap.Builder<String, ModelProperty<?>> builder = ImmutableSortedMap.naturalOrder();
+        for (ModelProperty<?> property : properties) {
+            builder.put(property.getName(), property);
+        }
+        this.properties = builder.build();
+        this.managedImpl = new WeakReference<Class<? extends T>>(managedImpl);
+    }
+
+    public ImmutableSortedMap<String, ModelProperty<?>> getProperties() {
+        return properties;
+    }
+
+    public Class<? extends T> getManagedImpl() {
+        return managedImpl.get();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/ModelSchemaCache.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/ModelSchemaCache.java
new file mode 100644
index 0000000..3265f37
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/ModelSchemaCache.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.cache;
+
+import com.google.common.collect.Maps;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Cast;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A global thread and multi, volatile, classloader safe cache for model schemas.
+ * <p>
+ * Significant complexity is introduced by facilitating a volatile classloader environment.
+ * That is, classes being loaded and unloaded for the life of this cache.
+ * This requires ModelSchema objects to not retain strong class references (which they don't due to use of ModelType)
+ * and for strong class references not to be held for the cache keys.
+ * <p>
+ * The use of {@link WeakClassSet} as the cache key, opposed to just {@link Class}, is because a type may be composed of types from different classloaders.
+ * An example of this would be something like {@code ManagedSet<SomeCustomUserType>}.
+ * The class set abstraction effectively creates a key for all classes involved in a type.
+ * The {@link WeakClassSet#isCollected()} method returns true when any of the classes involved have been collected.
+ * All keys (and associated entries) are removed from the map by the {@link #cleanUp()} method, which should be invoked periodically to trim the cache of no longer needed data.
+ */
+ at ThreadSafe
+public class ModelSchemaCache {
+
+    private final HashMap<WeakClassSet, Map<ModelType<?>, ModelSchema<?>>> cache = Maps.newHashMap();
+    private final Lock lock = new ReentrantLock();
+
+    @Nullable
+    public <T> ModelSchema<T> get(ModelType<T> type) {
+        lock.lock();
+        try {
+            Map<ModelType<?>, ModelSchema<?>> typeCache = cache.get(WeakClassSet.of(type));
+            if (typeCache == null) {
+                return null;
+            } else {
+                return Cast.uncheckedCast(typeCache.get(type));
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public <T> void set(ModelType<T> type, ModelSchema<T> schema) {
+        lock.lock();
+        try {
+            Map<ModelType<?>, ModelSchema<?>> typeCache = cache.get(WeakClassSet.of(type));
+            if (typeCache == null) {
+                typeCache = Maps.newHashMap();
+                cache.put(WeakClassSet.of(type), typeCache);
+            }
+            typeCache.put(type, schema);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public long size() {
+        lock.lock();
+        cleanUp();
+        try {
+            long size = 0;
+            for (Map<ModelType<?>, ModelSchema<?>> values : cache.values()) {
+                size += values.size();
+            }
+            return size;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void cleanUp() {
+        lock.lock();
+        try {
+            Iterator<Map.Entry<WeakClassSet, Map<ModelType<?>, ModelSchema<?>>>> iterator = cache.entrySet().iterator();
+            while (iterator.hasNext()) {
+                if (iterator.next().getKey().isCollected()) {
+                    iterator.remove();
+                }
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/MultiWeakClassSet.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/MultiWeakClassSet.java
new file mode 100644
index 0000000..dda3279
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/MultiWeakClassSet.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.cache;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.gradle.internal.Cast;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+class MultiWeakClassSet extends WeakClassSet {
+
+    private static final Function<Class<?>, WeakReference<Class<?>>> TO_WEAK_REF = new Function<Class<?>, WeakReference<Class<?>>>() {
+        @Override
+        public WeakReference<Class<?>> apply(Class<?> input) {
+            return new WeakReference<Class<?>>(input);
+        }
+    };
+    private static final Function<WeakReference<Class<?>>, Object> UNPACK_REF = new Function<WeakReference<Class<?>>, Object>() {
+        @Override
+        public Object apply(WeakReference<Class<?>> input) {
+            return input.get();
+        }
+    };
+
+    private final List<WeakReference<Class<?>>> references;
+    private final int hash;
+
+    MultiWeakClassSet(List<Class<?>> classes) {
+        this.references = Lists.newArrayList(Iterables.transform(classes, TO_WEAK_REF));
+        this.hash = classes.hashCode();
+    }
+
+    @Override
+    public int hashCode() {
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MultiWeakClassSet) {
+            MultiWeakClassSet other = Cast.uncheckedCast(obj);
+            if (other.references.size() == references.size()) {
+                return Iterables.elementsEqual(
+                        Iterables.transform(other.references, UNPACK_REF),
+                        Iterables.transform(references, UNPACK_REF)
+                );
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    boolean isCollected() {
+        for (WeakReference<Class<?>> reference : references) {
+            if (reference.get() == null) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/SingleWeakClassSet.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/SingleWeakClassSet.java
new file mode 100644
index 0000000..90c7627
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/SingleWeakClassSet.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.cache;
+
+import java.lang.ref.WeakReference;
+
+class SingleWeakClassSet extends WeakClassSet {
+
+    private final WeakReference<Class<?>> reference;
+    private final int hash;
+
+    SingleWeakClassSet(Class<?> clazz) {
+        this.reference = new WeakReference<Class<?>>(clazz);
+        this.hash = clazz.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        Class<?> clazz = reference.get();
+        if (clazz == null) {
+            return false; // can't be equal otherwise wouldn't have been collected
+        }
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        SingleWeakClassSet that = (SingleWeakClassSet) o;
+        return clazz.equals(that.reference.get());
+    }
+
+    @Override
+    public int hashCode() {
+        return hash;
+    }
+
+    @Override
+    boolean isCollected() {
+        Class<?> referent = reference.get();
+        return referent == null;
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/WeakClassSet.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/WeakClassSet.java
new file mode 100644
index 0000000..0740567
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/cache/WeakClassSet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.cache;
+
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+abstract class WeakClassSet {
+
+    static WeakClassSet of(ModelType<?> type) {
+        List<Class<?>> allClasses = type.getAllClasses();
+        if (allClasses.size() == 1) {
+            return new SingleWeakClassSet(allClasses.iterator().next());
+        } else {
+            return new MultiWeakClassSet(allClasses);
+        }
+    }
+
+    abstract boolean isCollected();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStore.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStore.java
new file mode 100644
index 0000000..bd64709
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStore.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+ at NotThreadSafe
+public class DefaultModelSchemaStore implements ModelSchemaStore {
+
+    private static final DefaultModelSchemaStore INSTANCE = new DefaultModelSchemaStore();
+
+    final ModelSchemaCache cache = new ModelSchemaCache();
+    final ModelSchemaExtractor extractor = new ModelSchemaExtractor();
+
+    public static DefaultModelSchemaStore getInstance() {
+        return INSTANCE;
+    }
+
+    DefaultModelSchemaStore() {
+    }
+
+    public <T> ModelSchema<T> getSchema(ModelType<T> type) {
+        return extractor.extract(type, cache);
+    }
+
+    @Override
+    public void cleanUp() {
+        cache.cleanUp();
+    }
+
+    public long size() {
+        return cache.size();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/EnumStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/EnumStrategy.java
new file mode 100644
index 0000000..0102c54
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/EnumStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collections;
+
+public class EnumStrategy implements ModelSchemaExtractionStrategy {
+
+    public <T> ModelSchemaExtractionResult<T> extract(ModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
+        ModelType<T> type = extractionContext.getType();
+        if (type.getRawClass().isEnum()) {
+            return new ModelSchemaExtractionResult<T>(ModelSchema.value(type));
+        } else {
+            return null;
+        }
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.singleton("enum types");
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/InvalidManagedModelElementTypeException.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/InvalidManagedModelElementTypeException.java
new file mode 100644
index 0000000..c753cdb
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/InvalidManagedModelElementTypeException.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.collect.Lists;
+import org.gradle.model.internal.type.ModelType;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Deque;
+
+public class InvalidManagedModelElementTypeException extends RuntimeException {
+
+    private static String createPathString(ModelSchemaExtractionContext<?> extractionContext) {
+        StringBuilder prefix = new StringBuilder("  ");
+        StringWriter out = new StringWriter();
+        PrintWriter writer = new PrintWriter(out);
+
+        Deque<String> descriptions = Lists.newLinkedList();
+        ModelSchemaExtractionContext<?> current = extractionContext;
+        while (current != null) {
+            descriptions.push(current.getDescription());
+            current = current.getParent();
+        }
+
+        writer.println(descriptions.pop());
+
+        while (!descriptions.isEmpty()) {
+            writer.print(prefix);
+            writer.print("\\--- ");
+            writer.print(descriptions.pop());
+
+            if (!descriptions.isEmpty()) {
+                writer.println();
+                prefix.append("  ");
+            }
+        }
+
+        return out.toString();
+    }
+
+    private static String getMessage(ModelSchemaExtractionContext<?> extractionContext, String message) {
+        ModelType<?> type = extractionContext.getType();
+        StringWriter out = new StringWriter();
+        PrintWriter writer = new PrintWriter(out);
+        writer.print("Invalid managed model type " + type + ": " + message);
+
+        if (extractionContext.getParent() != null) {
+            writer.println();
+            writer.println("The type was analyzed due to the following dependencies:");
+            writer.print(createPathString(extractionContext));
+        }
+
+        return out.toString();
+    }
+
+    public InvalidManagedModelElementTypeException(ModelSchemaExtractionContext<?> extractionContext, String message) {
+        this(extractionContext, message, null);
+    }
+
+    public InvalidManagedModelElementTypeException(ModelSchemaExtractionContext<?> extractionContext, String message, Throwable throwable) {
+        super(getMessage(extractionContext, message), throwable);
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/JdkValueTypeStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/JdkValueTypeStrategy.java
new file mode 100644
index 0000000..a7c9c30
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/JdkValueTypeStrategy.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.List;
+
+public class JdkValueTypeStrategy implements ModelSchemaExtractionStrategy {
+
+    private final static List<ModelType<?>> TYPES = ImmutableList.<ModelType<?>>of(
+            ModelType.of(String.class),
+            ModelType.of(Boolean.class),
+            ModelType.of(Character.class),
+            ModelType.of(Integer.class),
+            ModelType.of(Long.class),
+            ModelType.of(Double.class),
+            ModelType.of(BigInteger.class),
+            ModelType.of(BigDecimal.class)
+    );
+
+    // Expected to be a subset of above
+    private final static List<ModelType<?>> NON_FINAL_TYPES = ImmutableList.<ModelType<?>>of(
+            ModelType.of(BigInteger.class),
+            ModelType.of(BigDecimal.class)
+    );
+
+    public <R> ModelSchemaExtractionResult<R> extract(ModelSchemaExtractionContext<R> extractionContext, ModelSchemaCache cache) {
+        ModelType<R> type = extractionContext.getType();
+        if (TYPES.contains(type)) {
+            return new ModelSchemaExtractionResult<R>(ModelSchema.value(type));
+        } else {
+            for (ModelType<?> nonFinalType : NON_FINAL_TYPES) {
+                if (nonFinalType.isAssignableFrom(type)) {
+                    throw new InvalidManagedModelElementTypeException(extractionContext, "subclasses of " + nonFinalType + " are not supported");
+                }
+            }
+
+            return null;
+        }
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.singleton("JDK value types: " + Joiner.on(", ").join(Iterables.transform(TYPES, new Function<ModelType<?>, Object>() {
+            public Object apply(ModelType<?> input) {
+                return input.getRawClass().getSimpleName();
+            }
+        })));
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGenerator.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGenerator.java
new file mode 100644
index 0000000..f503e86
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGenerator.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import groovy.lang.MissingMethodException;
+import groovy.lang.MissingPropertyException;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.internal.Cast;
+import org.gradle.internal.reflect.*;
+import org.gradle.model.internal.core.MutableModelNode;
+import org.gradle.model.internal.manage.instance.ManagedInstance;
+import org.gradle.model.internal.manage.instance.ModelElementState;
+import org.objectweb.asm.*;
+
+//CHECKSTYLE:OFF This import is needed to override wildcard import of of org.gradle.internal.reflect.NoSuchMethodException
+import java.lang.NoSuchMethodException;
+//CHECKSTYLE:ON
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+public class ManagedProxyClassGenerator {
+    /*
+        Note: there is deliberately no internal synchronizing or caching at this level.
+
+        Class generation should always be performed behind a ModelSchemaCache, by way of DefaultModelSchemaStore.
+        The generated class is then attached to the schema object.
+        This allows us to avoid yet another weak class based cache, and importantly having to acquire a lock to instantiate an implementation.
+     */
+
+    private static final JavaMethod<ClassLoader, ?> DEFINE_CLASS_METHOD = JavaReflectionUtil.method(ClassLoader.class, Class.class, "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
+
+    private static final String STATE_FIELD_NAME = "$state";
+    private static final String CAN_CALL_SETTERS_FIELD_NAME = "$canCallSetters";
+    private static final String CONSTRUCTOR_NAME = "<init>";
+    private static final String CONCRETE_SIGNATURE = null;
+    private static final String[] NO_EXCEPTIONS = new String[0];
+    private static final String STATE_SET_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class), Type.getType(Object.class));
+    private static final String MANAGED_INSTANCE_TYPE = Type.getInternalName(ManagedInstance.class);
+    private static final Type MODEL_ELEMENT_STATE_TYPE = Type.getType(ModelElementState.class);
+    private static final String CONSTRUCTOR_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, MODEL_ELEMENT_STATE_TYPE);
+    private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(String.class));
+    private static final String GET_BACKING_NODE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(MutableModelNode.class));
+    private static final String GET_PROPERTY_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(String.class));
+    private static final String MISSING_PROPERTY_EXCEPTION_TYPE = Type.getInternalName(MissingPropertyException.class);
+    private static final String CLASS_TYPE = Type.getInternalName(Class.class);
+    private static final String FOR_NAME_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class));
+    private static final String OBJECT_ARRAY_TYPE = Type.getInternalName(Object[].class);
+    private static final String MISSING_METHOD_EXCEPTION_TYPE = Type.getInternalName(MissingMethodException.class);
+    private static final String MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class), Type.getType(Class.class));
+    private static final String METHOD_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(String.class), Type.getType(Object.class));
+    private static final String SET_PROPERTY_MISSING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(String.class), Type.getType(Object.class));
+    private static final String MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class), Type.getType(Class.class), Type.getType(Object[].class));
+
+    /**
+     * Generates an implementation of the given managed type.
+     * <p>
+     * The generated class will implement/extend the managed type and will:
+     * <ul>
+     *     <li>provide implementations for abstract getters and setters</li>
+     *     <li>provide a `toString()` implementation</li>
+     *     <li>mix-in implementation of {@link ManagedInstance}</li>
+     *     <li>provide a constructor that accepts a {@link ModelElementState}, which will be used to implement the above.</li>
+     * </ul>
+     */
+    public <T> Class<? extends T> generate(Class<T> managedTypeClass) {
+        ClassWriter visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+
+        String generatedTypeName = managedTypeClass.getName() + "_Impl";
+        Type generatedType = Type.getType("L" + generatedTypeName.replaceAll("\\.", "/") + ";");
+
+        Class<?> superclass;
+        List<String> interfaceInternalNames;
+        if (managedTypeClass.isInterface()) {
+            superclass = Object.class;
+            interfaceInternalNames = ImmutableList.of(Type.getInternalName(managedTypeClass), MANAGED_INSTANCE_TYPE);
+        } else {
+            superclass = managedTypeClass;
+            interfaceInternalNames = ImmutableList.of(MANAGED_INSTANCE_TYPE);
+        }
+
+        generateProxyClass(visitor, managedTypeClass, interfaceInternalNames, generatedType, Type.getType(superclass));
+
+        return defineClass(visitor, managedTypeClass.getClassLoader(), generatedTypeName);
+    }
+
+    private <T> Class<? extends T> defineClass(ClassWriter visitor, ClassLoader classLoader, String generatedTypeName) {
+        byte[] bytecode = visitor.toByteArray();
+        return Cast.uncheckedCast(DEFINE_CLASS_METHOD.invoke(classLoader, generatedTypeName, bytecode, 0, bytecode.length));
+    }
+
+    private void generateProxyClass(ClassWriter visitor, Class<?> managedTypeClass, List<String> interfaceInternalNames, Type generatedType, Type superclassType) {
+        declareClass(visitor, interfaceInternalNames, generatedType, superclassType);
+        declareStateField(visitor);
+        declareCanCallSettersField(visitor);
+        writeConstructor(visitor, generatedType, superclassType);
+        writeToString(visitor, generatedType, managedTypeClass);
+        writeManagedInstanceMethods(visitor, generatedType);
+        writeGroovyMethods(visitor, managedTypeClass);
+        writeMutationMethods(visitor, generatedType, managedTypeClass);
+        visitor.visitEnd();
+    }
+
+    private void declareClass(ClassVisitor visitor, List<String> interfaceInternalNames, Type generatedType, Type superclassType) {
+        visitor.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, generatedType.getInternalName(), null,
+                superclassType.getInternalName(), Iterables.toArray(interfaceInternalNames, String.class));
+    }
+
+    private void declareStateField(ClassVisitor visitor) {
+        declareField(visitor, STATE_FIELD_NAME, ModelElementState.class);
+    }
+
+    private void declareCanCallSettersField(ClassVisitor visitor) {
+        declareField(visitor, CAN_CALL_SETTERS_FIELD_NAME, Boolean.TYPE);
+    }
+
+    private void declareField(ClassVisitor visitor, String name, Class<?> fieldClass) {
+        visitor.visitField(Opcodes.ACC_PRIVATE, name, Type.getDescriptor(fieldClass), null, null);
+    }
+
+    private void writeConstructor(ClassVisitor visitor, Type generatedType, Type superclassType) {
+        MethodVisitor constructorVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", CONSTRUCTOR_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        constructorVisitor.visitCode();
+
+        invokeSuperConstructor(constructorVisitor, superclassType);
+        assignStateField(constructorVisitor, generatedType);
+        setCanCallSettersField(constructorVisitor, generatedType, true);
+        finishVisitingMethod(constructorVisitor);
+    }
+
+    private void invokeSuperConstructor(MethodVisitor constructorVisitor, Type superclassType) {
+        putThisOnStack(constructorVisitor);
+        constructorVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superclassType.getInternalName(), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), false);
+    }
+
+    private void writeToString(ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass) {
+        Method toStringMethod = getToStringMethod(managedTypeClass);
+
+        if (toStringMethod == null || toStringMethod.getDeclaringClass().equals(Object.class)) {
+            writeDefaultToString(visitor, generatedType);
+        } else {
+            writeNonAbstractMethodWrapper(visitor, generatedType, managedTypeClass, toStringMethod);
+        }
+    }
+
+    private void writeDefaultToString(ClassVisitor visitor, Type generatedType) {
+        MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+        putStateFieldValueOnStack(methodVisitor, generatedType);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, MODEL_ELEMENT_STATE_TYPE.getInternalName(), "getDisplayName", TO_STRING_METHOD_DESCRIPTOR, true);
+        finishVisitingMethod(methodVisitor, Opcodes.ARETURN);
+    }
+
+    private Method getToStringMethod(Class<?> managedTypeClass) {
+        try {
+            return managedTypeClass.getMethod("toString");
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    private void writeGroovyMethods(ClassVisitor visitor, Class<?> managedTypeClass) {
+        // Object propertyMissing(String name)
+        MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "propertyMissing", GET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+
+        // throw new MissingPropertyException(name, <managed-type>.class)
+        methodVisitor.visitTypeInsn(Opcodes.NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
+        methodVisitor.visitInsn(Opcodes.DUP);
+        putFirstMethodArgumentOnStack(methodVisitor);
+        putClassOnStack(methodVisitor, managedTypeClass);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, MISSING_PROPERTY_EXCEPTION_TYPE, "<init>", MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR, false);
+        finishVisitingMethod(methodVisitor, Opcodes.ATHROW);
+
+        // Object propertyMissing(String name, Object value)
+
+        methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "propertyMissing", SET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+
+        // throw new MissingPropertyException(name, <managed-type>.class)
+        methodVisitor.visitTypeInsn(Opcodes.NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
+        methodVisitor.visitInsn(Opcodes.DUP);
+        putFirstMethodArgumentOnStack(methodVisitor);
+        putClassOnStack(methodVisitor, managedTypeClass);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, MISSING_PROPERTY_EXCEPTION_TYPE, "<init>", MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR, false);
+        finishVisitingMethod(methodVisitor, Opcodes.ATHROW);
+
+        // Object methodMissing(String name, Object args)
+        methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "methodMissing", METHOD_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+
+        // throw new MissingMethodException(name, <managed-type>.class, args)
+        methodVisitor.visitTypeInsn(Opcodes.NEW, MISSING_METHOD_EXCEPTION_TYPE);
+        methodVisitor.visitInsn(Opcodes.DUP);
+        putMethodArgumentOnStack(methodVisitor, 1);
+        putClassOnStack(methodVisitor, managedTypeClass);
+        putMethodArgumentOnStack(methodVisitor, 2);
+        methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, OBJECT_ARRAY_TYPE);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, MISSING_METHOD_EXCEPTION_TYPE, "<init>", MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR, false);
+        finishVisitingMethod(methodVisitor, Opcodes.ATHROW);
+    }
+
+    private void putClassOnStack(MethodVisitor methodVisitor, Class<?> managedTypeClass) {
+        putConstantOnStack(methodVisitor, managedTypeClass.getName());
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, CLASS_TYPE, "forName", FOR_NAME_METHOD_DESCRIPTOR, false);
+    }
+
+    private void writeManagedInstanceMethods(ClassVisitor visitor, Type generatedType) {
+        MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, "getBackingNode", GET_BACKING_NODE_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+        putStateFieldValueOnStack(methodVisitor, generatedType);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, MODEL_ELEMENT_STATE_TYPE.getInternalName(), "getBackingNode", GET_BACKING_NODE_METHOD_DESCRIPTOR, true);
+        finishVisitingMethod(methodVisitor, Opcodes.ARETURN);
+    }
+
+    private void assignStateField(MethodVisitor constructorVisitor, Type generatedType) {
+        putThisOnStack(constructorVisitor);
+        putFirstMethodArgumentOnStack(constructorVisitor);
+        constructorVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), STATE_FIELD_NAME, MODEL_ELEMENT_STATE_TYPE.getDescriptor());
+    }
+
+    private void setCanCallSettersField(MethodVisitor methodVisitor, Type generatedType, boolean canCallSetters) {
+        putThisOnStack(methodVisitor);
+        methodVisitor.visitLdcInsn(canCallSetters);
+        methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedType.getInternalName(), CAN_CALL_SETTERS_FIELD_NAME, Type.BOOLEAN_TYPE.getDescriptor());
+    }
+
+    private void putThisOnStack(MethodVisitor constructorVisitor) {
+        constructorVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+    }
+
+    private void finishVisitingMethod(MethodVisitor methodVisitor) {
+        finishVisitingMethod(methodVisitor, Opcodes.RETURN);
+    }
+
+    private void finishVisitingMethod(MethodVisitor methodVisitor, int returnOpcode) {
+        methodVisitor.visitInsn(returnOpcode);
+        methodVisitor.visitMaxs(0, 0);
+        methodVisitor.visitEnd();
+    }
+
+    private void writeMutationMethods(ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass) {
+        ClassDetails classDetails = ClassInspector.inspect(managedTypeClass);
+        for (PropertyDetails property : classDetails.getProperties()) {
+            for (Method method : property.getGetters()) {
+                if (Modifier.isAbstract(method.getModifiers())) {
+                    writeGetter(visitor, generatedType, method);
+                } else if (!Modifier.isFinal(method.getModifiers()) && !property.getName().equals("metaClass")) {
+                    writeNonAbstractMethodWrapper(visitor, generatedType, managedTypeClass, method);
+                }
+            }
+            for (Method method : property.getSetters()) {
+                writeSetter(visitor, generatedType, method);
+            }
+        }
+    }
+
+    private void writeSetter(ClassVisitor visitor, Type generatedType, Method method) {
+        String propertyName = getPropertyName(method);
+        Label calledOutsideOfConstructor = new Label();
+
+        MethodVisitor methodVisitor = declareMethod(visitor, method);
+
+        putCanCallSettersFieldValueOnStack(methodVisitor, generatedType);
+        jumpToLabelIfStackEvaluatesToTrue(methodVisitor, calledOutsideOfConstructor);
+        throwExceptionBecauseCalledOnItself(methodVisitor);
+
+        writeLabel(methodVisitor, calledOutsideOfConstructor);
+        putStateFieldValueOnStack(methodVisitor, generatedType);
+        putConstantOnStack(methodVisitor, propertyName);
+        putFirstMethodArgumentOnStack(methodVisitor);
+        invokeStateSetMethod(methodVisitor);
+
+        finishVisitingMethod(methodVisitor);
+    }
+
+    private void writeLabel(MethodVisitor methodVisitor, Label label) {
+        methodVisitor.visitLabel(label);
+        methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+    }
+
+    private void throwExceptionBecauseCalledOnItself(MethodVisitor methodVisitor) {
+        String exceptionInternalName = Type.getInternalName(UnsupportedOperationException.class);
+        methodVisitor.visitTypeInsn(Opcodes.NEW, exceptionInternalName);
+        methodVisitor.visitInsn(Opcodes.DUP);
+        putConstantOnStack(methodVisitor, "Calling setters of a managed type on itself is not allowed");
+
+        String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, exceptionInternalName, CONSTRUCTOR_NAME, constructorDescriptor, false);
+        methodVisitor.visitInsn(Opcodes.ATHROW);
+    }
+
+    private void jumpToLabelIfStackEvaluatesToTrue(MethodVisitor methodVisitor, Label label) {
+        methodVisitor.visitJumpInsn(Opcodes.IFNE, label);
+    }
+
+    private void invokeStateSetMethod(MethodVisitor methodVisitor) {
+        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, MODEL_ELEMENT_STATE_TYPE.getInternalName(), "set", STATE_SET_METHOD_DESCRIPTOR, true);
+    }
+
+    private void putConstantOnStack(MethodVisitor methodVisitor, Object value) {
+        methodVisitor.visitLdcInsn(value);
+    }
+
+    private MethodVisitor declareMethod(ClassVisitor visitor, Method method) {
+        MethodVisitor methodVisitor = visitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), CONCRETE_SIGNATURE, NO_EXCEPTIONS);
+        methodVisitor.visitCode();
+        return methodVisitor;
+    }
+
+    private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor) {
+        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
+    }
+
+    private void putMethodArgumentOnStack(MethodVisitor methodVisitor, int index) {
+        methodVisitor.visitVarInsn(Opcodes.ALOAD, index);
+    }
+
+    private void putStateFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
+        putFieldValueOnStack(methodVisitor, generatedType, STATE_FIELD_NAME, ModelElementState.class);
+    }
+
+    private void putCanCallSettersFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
+        putFieldValueOnStack(methodVisitor, generatedType, CAN_CALL_SETTERS_FIELD_NAME, Boolean.TYPE);
+    }
+
+    private void putFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType, String name, Class<?> fieldClass) {
+        putThisOnStack(methodVisitor);
+        methodVisitor.visitFieldInsn(Opcodes.GETFIELD, generatedType.getInternalName(), name, Type.getDescriptor(fieldClass));
+    }
+
+    private void writeGetter(ClassVisitor visitor, Type generatedType, Method method) {
+        String propertyName = getPropertyName(method);
+
+        MethodVisitor methodVisitor = declareMethod(visitor, method);
+
+        putStateFieldValueOnStack(methodVisitor, generatedType);
+        putConstantOnStack(methodVisitor, propertyName);
+        invokeStateGetMethod(methodVisitor);
+        castFirstStackElement(methodVisitor, method.getReturnType());
+        finishVisitingMethod(methodVisitor, Opcodes.ARETURN);
+    }
+
+    private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) {
+        methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(returnType));
+    }
+
+    private String getPropertyName(Method method) {
+        return StringUtils.uncapitalize(method.getName().substring(3));
+    }
+
+    private void invokeStateGetMethod(MethodVisitor methodVisitor) {
+        String methodDescriptor = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(String.class));
+        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, MODEL_ELEMENT_STATE_TYPE.getInternalName(), "get", methodDescriptor, true);
+    }
+
+    private void writeNonAbstractMethodWrapper(ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass, Method method) {
+        Label start = new Label();
+        Label end = new Label();
+        Label handler = new Label();
+
+        MethodVisitor methodVisitor = declareMethod(visitor, method);
+
+        methodVisitor.visitTryCatchBlock(start, end, handler, null);
+
+        setCanCallSettersField(methodVisitor, generatedType, false);
+
+        writeLabel(methodVisitor, start);
+        invokeSuperMethod(methodVisitor, managedTypeClass, method);
+        writeLabel(methodVisitor, end);
+
+        setCanCallSettersField(methodVisitor, generatedType, true);
+        methodVisitor.visitInsn(Opcodes.ARETURN);
+
+        writeLabel(methodVisitor, handler);
+        setCanCallSettersField(methodVisitor, generatedType, true);
+        methodVisitor.visitInsn(Opcodes.ATHROW);
+
+        methodVisitor.visitMaxs(0, 0);
+        methodVisitor.visitEnd();
+    }
+
+    private void invokeSuperMethod(MethodVisitor methodVisitor, Class<?> superClass, Method method) {
+        putThisOnStack(methodVisitor);
+        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superClass), method.getName(), Type.getMethodDescriptor(method), false);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedSetStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedSetStrategy.java
new file mode 100644
index 0000000..9c6f313
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ManagedSetStrategy.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.internal.Factory;
+import org.gradle.model.collection.ManagedSet;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collections;
+import java.util.List;
+
+ at ThreadSafe
+public class ManagedSetStrategy implements ModelSchemaExtractionStrategy {
+
+    private static final ModelType<ManagedSet<?>> MANAGED_SET_MODEL_TYPE = new ModelType<ManagedSet<?>>() {
+    };
+    private final Factory<String> supportedTypeDescriptions;
+
+    public ManagedSetStrategy(Factory<String> supportedTypeDescriptions) {
+        this.supportedTypeDescriptions = supportedTypeDescriptions;
+    }
+
+    public <T> ModelSchemaExtractionResult<T> extract(ModelSchemaExtractionContext<T> extractionContext, final ModelSchemaCache cache) {
+        ModelType<T> type = extractionContext.getType();
+        if (MANAGED_SET_MODEL_TYPE.isAssignableFrom(type)) {
+            if (!type.getRawClass().equals(ManagedSet.class)) {
+                throw new InvalidManagedModelElementTypeException(extractionContext, String.format("subtyping %s is not supported", ManagedSet.class.getName()));
+            }
+            if (type.isHasWildcardTypeVariables()) {
+                throw new InvalidManagedModelElementTypeException(extractionContext, String.format("type parameter of %s cannot be a wildcard", ManagedSet.class.getName()));
+            }
+
+            List<ModelType<?>> typeVariables = type.getTypeVariables();
+            if (typeVariables.isEmpty()) {
+                throw new InvalidManagedModelElementTypeException(extractionContext, String.format("type parameter of %s has to be specified", ManagedSet.class.getName()));
+            }
+
+            ModelType<?> elementType = typeVariables.get(0);
+
+            if (MANAGED_SET_MODEL_TYPE.isAssignableFrom(elementType)) {
+                throw new InvalidManagedModelElementTypeException(extractionContext, String.format("%1$s cannot be used as type parameter of %1$s", ManagedSet.class.getName()));
+            }
+
+            ModelSchema<T> schema = ModelSchema.collection(extractionContext.getType(), elementType);
+            ModelSchemaExtractionContext<?> typeParamExtractionContext = extractionContext.child(elementType, "element type", new Action<ModelSchemaExtractionContext<?>>() {
+                public void execute(ModelSchemaExtractionContext<?> context) {
+                    ModelSchema<?> typeParamSchema = cache.get(context.getType());
+
+                    if (!typeParamSchema.getKind().isManaged()) {
+                        throw new InvalidManagedModelElementTypeException(context.getParent(), String.format(
+                                "cannot create a managed set of type %s as it is an unmanaged type.%nSupported types:%n%s",
+                                context.getType(), supportedTypeDescriptions.create()
+                        ));
+                    }
+                }
+            });
+            return new ModelSchemaExtractionResult<T>(schema, ImmutableList.of(typeParamExtractionContext));
+        } else {
+            return null;
+        }
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.singleton(MANAGED_SET_MODEL_TYPE + " of a managed type");
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionContext.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionContext.java
new file mode 100644
index 0000000..a34f878
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionContext.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.collect.Lists;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Actions;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+ at NotThreadSafe
+public class ModelSchemaExtractionContext<T> {
+
+    private final ModelSchemaExtractionContext<?> parent;
+    private final ModelType<T> type;
+    private final String description;
+    private final List<Action<? super ModelSchemaExtractionContext<T>>> validators;
+
+    private ModelSchemaExtractionContext(ModelSchemaExtractionContext<?> parent, ModelType<T> type, String description, Action<? super ModelSchemaExtractionContext<T>> validator) {
+        this.parent = parent;
+        this.type = type;
+        this.description = description;
+        this.validators = Lists.newArrayListWithCapacity(2);
+
+        validators.add(validator);
+    }
+
+    public static <T> ModelSchemaExtractionContext<T> root(ModelType<T> type) {
+        return new ModelSchemaExtractionContext<T>(null, type, null, Actions.doNothing());
+    }
+
+    public static <T> ModelSchemaExtractionContext<T> root(ModelType<T> type, Action<? super ModelSchemaExtractionContext<T>> validator) {
+        return new ModelSchemaExtractionContext<T>(null, type, null, validator);
+    }
+
+    /**
+     * null if this is the root of the extraction
+     */
+    @Nullable
+    public ModelSchemaExtractionContext<?> getParent() {
+        return parent;
+    }
+
+    public ModelType<T> getType() {
+        return type;
+    }
+
+    public String getDescription() {
+        return description == null ? type.toString() : String.format("%s (%s)", description, type);
+    }
+
+    public <C> ModelSchemaExtractionContext<C> child(ModelType<C> type, String description) {
+        return child(type, description, Actions.doNothing());
+    }
+
+    public <C> ModelSchemaExtractionContext<C> child(ModelType<C> type, String description, Action<? super ModelSchemaExtractionContext<C>> validator) {
+        return new ModelSchemaExtractionContext<C>(this, type, description, validator);
+    }
+
+    public void validate() {
+        for (Action<? super ModelSchemaExtractionContext<T>> validator : validators) {
+            validator.execute(this);
+        }
+    }
+
+    public void addValidator(Action<? super ModelSchemaExtractionContext<T>> validator) {
+        validators.add(validator);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionResult.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionResult.java
new file mode 100644
index 0000000..e1d2d21
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionResult.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+
+import java.util.Collections;
+
+ at ThreadSafe
+public class ModelSchemaExtractionResult<T> {
+
+    private ModelSchema<T> schema;
+
+    private Iterable<? extends ModelSchemaExtractionContext<?>> dependencies;
+
+    public ModelSchemaExtractionResult(ModelSchema<T> schema) {
+        this(schema, Collections.<ModelSchemaExtractionContext<?>>emptyList());
+    }
+
+    public ModelSchemaExtractionResult(ModelSchema<T> schema, Iterable<? extends ModelSchemaExtractionContext<?>> dependencies) {
+        this.schema = schema;
+        this.dependencies = dependencies;
+    }
+
+    public ModelSchema<T> getSchema() {
+        return schema;
+    }
+
+    public Iterable<? extends ModelSchemaExtractionContext<?>> getDependencies() {
+        return dependencies;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionStrategy.java
new file mode 100644
index 0000000..09013bc
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractionStrategy.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+
+public interface ModelSchemaExtractionStrategy {
+
+    @Nullable
+    public <T> ModelSchemaExtractionResult<T> extract(ModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache);
+
+    Iterable<String> getSupportedManagedTypes();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractor.java
new file mode 100644
index 0000000..3dc7e31
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractor.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.Factory;
+import org.gradle.internal.SystemProperties;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+import java.util.Queue;
+
+ at ThreadSafe
+class ModelSchemaExtractor {
+
+    private final Factory<String> supportedTypeDescriptions = new Factory<String>() {
+        public String create() {
+            return getSupportedTypesDescription();
+        }
+    };
+    private final List<ModelSchemaExtractionStrategy> strategies = ImmutableList.of(
+            new PrimitiveStrategy(),
+            new EnumStrategy(),
+            new JdkValueTypeStrategy(),
+            new ManagedSetStrategy(supportedTypeDescriptions),
+            new StructStrategy(supportedTypeDescriptions),
+            new UnmanagedStrategy()
+    );
+
+    public <T> ModelSchema<T> extract(ModelType<T> type, ModelSchemaCache cache) {
+        ModelSchemaExtractionContext<T> context = ModelSchemaExtractionContext.root(type);
+        List<ModelSchemaExtractionContext<?>> validations = Lists.newLinkedList();
+        Queue<ModelSchemaExtractionContext<?>> unsatisfiedDependencies = Lists.newLinkedList();
+        ModelSchemaExtractionContext<?> extractionContext = context;
+        validations.add(extractionContext);
+
+        while (extractionContext != null) {
+            ModelSchemaExtractionResult<?> nextSchema = extractSchema(extractionContext, cache);
+            Iterable<? extends ModelSchemaExtractionContext<?>> dependencies = nextSchema.getDependencies();
+            Iterables.addAll(validations, dependencies);
+            pushUnsatisfiedDependencies(dependencies, unsatisfiedDependencies, cache);
+            extractionContext = unsatisfiedDependencies.poll();
+        }
+
+        for (ModelSchemaExtractionContext<?> validationContext : Lists.reverse(validations)) {
+            validationContext.validate();
+        }
+
+        return cache.get(context.getType());
+    }
+
+    private void pushUnsatisfiedDependencies(Iterable<? extends ModelSchemaExtractionContext<?>> allDependencies, Queue<ModelSchemaExtractionContext<?>> dependencyQueue, final ModelSchemaCache cache) {
+        Iterables.addAll(dependencyQueue, Iterables.filter(allDependencies, new Predicate<ModelSchemaExtractionContext<?>>() {
+            public boolean apply(ModelSchemaExtractionContext<?> dependency) {
+                return cache.get(dependency.getType()) == null;
+            }
+        }));
+    }
+
+    private <T> ModelSchemaExtractionResult<T> extractSchema(ModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
+        final ModelType<T> type = extractionContext.getType();
+        ModelSchema<T> cached = cache.get(type);
+        if (cached != null) {
+            return new ModelSchemaExtractionResult<T>(cached);
+        }
+
+        for (ModelSchemaExtractionStrategy strategy : strategies) {
+            ModelSchemaExtractionResult<T> result = strategy.extract(extractionContext, cache);
+            if (result != null) {
+                cache.set(type, result.getSchema());
+                return result;
+            }
+        }
+
+        // Should never get here, the last strategy should be a catch all
+        throw new IllegalStateException("No extraction strategy found for type: " + type);
+    }
+
+    private String getSupportedTypesDescription() {
+        return Joiner.on(SystemProperties.getInstance().getLineSeparator()).join(Iterables.transform(getSupportedTypes(), new Function<String, String>() {
+            public String apply(String input) {
+                return " - " + input;
+            }
+        }));
+    }
+
+    private Iterable<String> getSupportedTypes() {
+        return Iterables.concat(Iterables.transform(strategies, new Function<ModelSchemaExtractionStrategy, Iterable<String>>() {
+            public Iterable<String> apply(ModelSchemaExtractionStrategy input) {
+                return input.getSupportedManagedTypes();
+            }
+        }));
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/PrimitiveStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/PrimitiveStrategy.java
new file mode 100644
index 0000000..9df8882
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/PrimitiveStrategy.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.collect.ImmutableMap;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class PrimitiveStrategy implements ModelSchemaExtractionStrategy {
+
+    private final static Map<ModelType<?>, Class<?>> BOXED_REPLACEMENTS = ImmutableMap.<ModelType<?>, Class<?>>builder()
+            .put(ModelType.of(Boolean.TYPE), Boolean.class)
+            .put(ModelType.of(Character.TYPE), Character.class)
+            .put(ModelType.of(Float.TYPE), Double.class)
+            .put(ModelType.of(Integer.TYPE), Integer.class)
+            .put(ModelType.of(Long.TYPE), Long.class)
+            .put(ModelType.of(Short.TYPE), Integer.class)
+            .put(ModelType.of(Double.TYPE), Double.class)
+            .build();
+
+
+    public <T> ModelSchemaExtractionResult<T> extract(ModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
+        ModelType<T> type = extractionContext.getType();
+        if (type.getRawClass().isPrimitive()) {
+            Class<?> replacementType = BOXED_REPLACEMENTS.get(type);
+            if (replacementType != null) {
+                throw new InvalidManagedModelElementTypeException(extractionContext, String.format("type is not supported, please use %s instead", replacementType.getName()));
+            }
+        }
+
+        return null;
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.emptySet();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/StructStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/StructStrategy.java
new file mode 100644
index 0000000..415bee2
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/StructStrategy.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import com.google.common.base.*;
+import com.google.common.collect.*;
+import groovy.lang.GroovyObject;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Factory;
+import org.gradle.internal.reflect.MethodDescription;
+import org.gradle.internal.reflect.MethodSignatureEquivalence;
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+import org.gradle.model.internal.core.MutableModelNode;
+import org.gradle.model.internal.manage.instance.ManagedProxyFactory;
+import org.gradle.model.internal.manage.instance.ModelElementState;
+import org.gradle.model.internal.manage.schema.ModelProperty;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelStructSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.lang.reflect.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class StructStrategy implements ModelSchemaExtractionStrategy {
+
+    private static final NoOpModelElementState NO_OP_MODEL_ELEMENT_STATE = new NoOpModelElementState();
+
+    private final Set<Equivalence.Wrapper<Method>> ignoredMethods;
+
+    private final Factory<String> supportedTypeDescriptions;
+    private final MethodSignatureEquivalence equivalence = new MethodSignatureEquivalence();
+
+    private final ManagedProxyClassGenerator classGenerator = new ManagedProxyClassGenerator();
+    private final ManagedProxyFactory proxyFactory = new ManagedProxyFactory();
+
+    public StructStrategy(Factory<String> supportedTypeDescriptions) {
+        this.supportedTypeDescriptions = supportedTypeDescriptions;
+
+        Iterable<Method> ignoredMethods = Iterables.concat(Arrays.asList(Object.class.getMethods()), Arrays.asList(GroovyObject.class.getMethods()));
+        this.ignoredMethods = ImmutableSet.copyOf(Iterables.transform(ignoredMethods, new Function<Method, Equivalence.Wrapper<Method>>() {
+            public Equivalence.Wrapper<Method> apply(@Nullable Method input) {
+                return equivalence.wrap(input);
+            }
+        }));
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.singleton("interfaces and abstract classes annotated with " + Managed.class.getName());
+    }
+
+    public <R> ModelSchemaExtractionResult<R> extract(final ModelSchemaExtractionContext<R> extractionContext, final ModelSchemaCache cache) {
+        ModelType<R> type = extractionContext.getType();
+        Class<? super R> clazz = type.getRawClass();
+        if (clazz.isAnnotationPresent(Managed.class)) {
+            validateType(type, extractionContext);
+
+            Iterable<Method> methods = Arrays.asList(clazz.getMethods());
+            if (!clazz.isInterface()) {
+                methods = filterIgnoredMethods(methods);
+            }
+            ImmutableListMultimap<String, Method> methodsByName = Multimaps.index(methods, new Function<Method, String>() {
+                public String apply(Method method) {
+                    return method.getName();
+                }
+            });
+
+            ensureNoOverloadedMethods(extractionContext, methodsByName);
+
+            List<ModelProperty<?>> properties = Lists.newLinkedList();
+            List<Method> handled = Lists.newArrayListWithCapacity(clazz.getMethods().length);
+            ReturnTypeSpecializationOrdering returnTypeSpecializationOrdering = new ReturnTypeSpecializationOrdering();
+
+            for (String methodName : methodsByName.keySet()) {
+                if (methodName.startsWith("get") && !methodName.equals("get")) {
+                    ImmutableList<Method> getterMethods = methodsByName.get(methodName);
+
+                    // The overload check earlier verified that all methods for are equivalent for our purposes
+                    // So, taking the first one with the most specialized return type is fine.
+                    Method sampleMethod = returnTypeSpecializationOrdering.max(getterMethods);
+
+                    boolean abstractGetter = Modifier.isAbstract(sampleMethod.getModifiers());
+
+                    if (sampleMethod.getParameterTypes().length != 0) {
+                        throw invalidMethod(extractionContext, "getter methods cannot take parameters", sampleMethod);
+                    }
+
+                    Character getterPropertyNameFirstChar = methodName.charAt(3);
+                    if (!Character.isUpperCase(getterPropertyNameFirstChar)) {
+                        throw invalidMethod(extractionContext, "the 4th character of the getter method name must be an uppercase character", sampleMethod);
+                    }
+
+                    ModelType<?> returnType = ModelType.returnType(sampleMethod);
+
+                    String propertyNameCapitalized = methodName.substring(3);
+                    String propertyName = StringUtils.uncapitalize(propertyNameCapitalized);
+                    String setterName = "set" + propertyNameCapitalized;
+                    ImmutableList<Method> setterMethods = methodsByName.get(setterName);
+
+                    boolean isWritable = !setterMethods.isEmpty();
+                    if (isWritable) {
+                        Method setter = setterMethods.get(0);
+
+                        if (!abstractGetter) {
+                            throw invalidMethod(extractionContext, "setters are not allowed for non-abstract getters", setter);
+                        }
+                        validateSetter(extractionContext, returnType, setter);
+                        handled.addAll(setterMethods);
+                    }
+
+                    if (abstractGetter) {
+                        ImmutableSet<ModelType<?>> declaringClasses = ImmutableSet.copyOf(Iterables.transform(getterMethods, new Function<Method, ModelType<?>>() {
+                            public ModelType<?> apply(Method input) {
+                                return ModelType.of(input.getDeclaringClass());
+                            }
+                        }));
+
+                        boolean unmanaged = Iterables.any(getterMethods, new Predicate<Method>() {
+                            public boolean apply(Method input) {
+                                return input.getAnnotation(Unmanaged.class) != null;
+                            }
+                        });
+
+                        properties.add(ModelProperty.of(returnType, propertyName, isWritable, declaringClasses, unmanaged));
+                    }
+                    handled.addAll(getterMethods);
+                }
+            }
+
+            Iterable<Method> notHandled = Iterables.filter(methodsByName.values(), Predicates.not(Predicates.in(handled)));
+
+            // TODO - should call out valid getters without setters
+            if (!Iterables.isEmpty(notHandled)) {
+                throw invalidMethods(extractionContext, "only paired getter/setter methods are supported", notHandled);
+            }
+
+            Class<R> concreteClass = type.getConcreteClass();
+            Class<? extends R> implClass = classGenerator.generate(concreteClass);
+            final ModelStructSchema<R> schema = ModelSchema.struct(type, properties, implClass);
+            extractionContext.addValidator(new Action<ModelSchemaExtractionContext<R>>() {
+                @Override
+                public void execute(ModelSchemaExtractionContext<R> validatorModelSchemaExtractionContext) {
+                    ensureCanBeInstantiated(extractionContext, schema);
+                }
+            });
+            Iterable<ModelSchemaExtractionContext<?>> propertyDependencies = Iterables.transform(properties, new Function<ModelProperty<?>, ModelSchemaExtractionContext<?>>() {
+                public ModelSchemaExtractionContext<?> apply(final ModelProperty<?> property) {
+                    return toPropertyExtractionContext(extractionContext, property, cache);
+                }
+            });
+
+            return new ModelSchemaExtractionResult<R>(schema, propertyDependencies);
+        } else {
+            return null;
+        }
+    }
+
+    private <R> void ensureCanBeInstantiated(ModelSchemaExtractionContext<R> extractionContext, ModelStructSchema<R> schema) {
+        try {
+            proxyFactory.createProxy(NO_OP_MODEL_ELEMENT_STATE, schema);
+        } catch (Throwable e) {
+            throw new InvalidManagedModelElementTypeException(extractionContext, "instance creation failed", e);
+        }
+    }
+
+    private Iterable<Method> filterIgnoredMethods(Iterable<Method> methods) {
+        return Iterables.filter(methods, new Predicate<Method>() {
+            @Override
+            public boolean apply(Method method) {
+                return !method.isSynthetic() && !ignoredMethods.contains(equivalence.wrap(method));
+            }
+        });
+    }
+
+    private <R> void ensureNoOverloadedMethods(ModelSchemaExtractionContext<R> extractionContext, final ImmutableListMultimap<String, Method> methodsByName) {
+        ImmutableSet<String> methodNames = methodsByName.keySet();
+        for (String methodName : methodNames) {
+            ImmutableList<Method> methods = methodsByName.get(methodName);
+            if (methods.size() > 1) {
+                List<Method> deduped = CollectionUtils.dedup(methods, equivalence);
+                if (deduped.size() > 1) {
+                    throw invalidMethods(extractionContext, "overloaded methods are not supported", deduped);
+                }
+            }
+        }
+    }
+
+    private <R, P> ModelSchemaExtractionContext<P> toPropertyExtractionContext(final ModelSchemaExtractionContext<R> parentContext, final ModelProperty<P> property, final ModelSchemaCache modelSchemaCache) {
+        return parentContext.child(property.getType(), propertyDescription(parentContext, property), new Action<ModelSchemaExtractionContext<P>>() {
+            public void execute(ModelSchemaExtractionContext<P> propertyExtractionContext) {
+                ModelSchema<P> propertySchema = modelSchemaCache.get(property.getType());
+
+                if (propertySchema.getKind().isAllowedPropertyTypeOfManagedType() && property.isUnmanaged()) {
+                    throw new InvalidManagedModelElementTypeException(parentContext, String.format(
+                            "property '%s' is marked as @Unmanaged, but is of @Managed type '%s'. Please remove the @Managed annotation.%n%s",
+                            property.getName(), property.getType(), supportedTypeDescriptions.create()
+                    ));
+                }
+
+                if (!propertySchema.getKind().isAllowedPropertyTypeOfManagedType() && !property.isUnmanaged()) {
+                    throw new InvalidManagedModelElementTypeException(parentContext, String.format(
+                            "type %s cannot be used for property '%s' as it is an unmanaged type (please annotate the getter with @org.gradle.model.Unmanaged if you want this property to be unmanaged).%n%s",
+                            property.getType(), property.getName(), supportedTypeDescriptions.create()
+                    ));
+                }
+
+                if (!property.isWritable()) {
+                    if (property.isUnmanaged()) {
+                        throw new InvalidManagedModelElementTypeException(parentContext, String.format(
+                                "unmanaged property '%s' cannot be read only, unmanaged properties must have setters",
+                                property.getName())
+                        );
+                    }
+
+                    if (!propertySchema.getKind().isManaged()) {
+                        throw new InvalidManagedModelElementTypeException(parentContext, String.format(
+                                "read only property '%s' has non managed type %s, only managed types can be used",
+                                property.getName(), property.getType()));
+                    }
+                }
+            }
+        });
+    }
+
+    private String propertyDescription(ModelSchemaExtractionContext<?> parentContext, ModelProperty<?> property) {
+        if (property.getDeclaredBy().size() == 1 && property.getDeclaredBy().contains(parentContext.getType())) {
+            return String.format("property '%s'", property.getName());
+        } else {
+            ImmutableSortedSet<String> declaredBy = ImmutableSortedSet.copyOf(Iterables.transform(property.getDeclaredBy(), Functions.toStringFunction()));
+            return String.format("property '%s' declared by %s", property.getName(), Joiner.on(", ").join(declaredBy));
+        }
+    }
+
+    private void validateSetter(ModelSchemaExtractionContext<?> extractionContext, ModelType<?> propertyType, Method setter) {
+        if (!Modifier.isAbstract(setter.getModifiers())) {
+            throw invalidMethod(extractionContext, "non-abstract setters are not allowed", setter);
+        }
+
+        if (!setter.getReturnType().equals(void.class)) {
+            throw invalidMethod(extractionContext, "setter method must have void return type", setter);
+        }
+
+        Type[] setterParameterTypes = setter.getGenericParameterTypes();
+        if (setterParameterTypes.length != 1) {
+            throw invalidMethod(extractionContext, "setter method must have exactly one parameter", setter);
+        }
+
+        ModelType<?> setterType = ModelType.paramType(setter, 0);
+        if (!setterType.equals(propertyType)) {
+            String message = "setter method param must be of exactly the same type as the getter returns (expected: " + propertyType + ", found: " + setterType + ")";
+            throw invalidMethod(extractionContext, message, setter);
+        }
+    }
+
+    private void validateType(ModelType<?> type, ModelSchemaExtractionContext<?> extractionContext) {
+        Class<?> typeClass = type.getConcreteClass();
+
+        if (!typeClass.isInterface() && !Modifier.isAbstract(typeClass.getModifiers())) {
+            throw new InvalidManagedModelElementTypeException(extractionContext, "must be defined as an interface or an abstract class.");
+        }
+
+        if (typeClass.getTypeParameters().length > 0) {
+            throw new InvalidManagedModelElementTypeException(extractionContext, "cannot be a parameterized type.");
+        }
+
+        Constructor<?> customConstructor = findCustomConstructor(typeClass);
+        if (customConstructor != null) {
+            throw invalidMethod(extractionContext, "custom constructors are not allowed", customConstructor);
+        }
+
+        ensureNoInstanceScopedFields(extractionContext, typeClass);
+        ensureNoProtectedOrPrivateMethods(extractionContext, typeClass);
+    }
+
+    private void ensureNoProtectedOrPrivateMethods(ModelSchemaExtractionContext<?> extractionContext, Class<?> typeClass) {
+        Class<?> superClass = typeClass.getSuperclass();
+        if (superClass != null && !superClass.equals(Object.class)) {
+            ensureNoProtectedOrPrivateMethods(extractionContext, superClass);
+        }
+
+        Iterable<Method> protectedAndPrivateMethods = Iterables.filter(Arrays.asList(typeClass.getDeclaredMethods()), new Predicate<Method>() {
+            @Override
+            public boolean apply(Method method) {
+                int modifiers = method.getModifiers();
+                return !method.isSynthetic() && (Modifier.isProtected(modifiers) || Modifier.isPrivate(modifiers));
+            }
+        });
+
+        if (!Iterables.isEmpty(protectedAndPrivateMethods)) {
+            throw invalidMethods(extractionContext, "protected and private methods are not allowed", protectedAndPrivateMethods);
+        }
+    }
+
+    private void ensureNoInstanceScopedFields(ModelSchemaExtractionContext<?> extractionContext, Class<?> typeClass) {
+        Class<?> superClass = typeClass.getSuperclass();
+        if (superClass != null && !superClass.equals(Object.class)) {
+            ensureNoInstanceScopedFields(extractionContext, superClass);
+        }
+
+        List<Field> declaredFields = Arrays.asList(typeClass.getDeclaredFields());
+        Iterable<Field> instanceScopedFields = Iterables.filter(declaredFields, new Predicate<Field>() {
+            public boolean apply(Field field) {
+                return !Modifier.isStatic(field.getModifiers()) && !field.getName().equals("metaClass");
+            }
+        });
+        ImmutableSortedSet<String> sortedDescriptions = ImmutableSortedSet.copyOf(Iterables.transform(instanceScopedFields, new Function<Field, String>() {
+            public String apply(Field field) {
+                return field.toString();
+            }
+        }));
+        if (!sortedDescriptions.isEmpty()) {
+            throw new InvalidManagedModelElementTypeException(extractionContext, "instance scoped fields are not allowed (found fields: " + Joiner.on(", ").join(sortedDescriptions) + ").");
+        }
+    }
+
+    private Constructor<?> findCustomConstructor(Class<?> typeClass) {
+        Class<?> superClass = typeClass.getSuperclass();
+        if (superClass != null && !superClass.equals(Object.class)) {
+            Constructor<?> customSuperConstructor = findCustomConstructor(typeClass.getSuperclass());
+            if (customSuperConstructor != null) {
+                return customSuperConstructor;
+            }
+        }
+        Constructor<?>[] constructors = typeClass.getConstructors();
+        if (constructors.length == 0 || (constructors.length == 1 && constructors[0].getParameterTypes().length == 0)) {
+            return null;
+        } else {
+            for (Constructor<?> constructor : constructors) {
+                if (constructor.getParameterTypes().length > 0) {
+                    return constructor;
+                }
+            }
+            //this should never happen
+            throw new RuntimeException(String.format("Expected a constructor taking at least one argument in %s but no such constructors were found", typeClass.getName()));
+        }
+    }
+
+    private InvalidManagedModelElementTypeException invalidMethod(ModelSchemaExtractionContext<?> extractionContext, String message, Method method) {
+        return invalidMethod(extractionContext, message, MethodDescription.of(method));
+    }
+
+    private InvalidManagedModelElementTypeException invalidMethod(ModelSchemaExtractionContext<?> extractionContext, String message, Constructor<?> constructor) {
+        return invalidMethod(extractionContext, message, MethodDescription.of(constructor));
+    }
+
+    private InvalidManagedModelElementTypeException invalidMethod(ModelSchemaExtractionContext<?> extractionContext, String message, MethodDescription methodDescription) {
+        return new InvalidManagedModelElementTypeException(extractionContext, message + " (invalid method: " + methodDescription.toString() + ").");
+    }
+
+    private InvalidManagedModelElementTypeException invalidMethods(ModelSchemaExtractionContext<?> extractionContext, String message, final Iterable<Method> methods) {
+        final ImmutableSortedSet<String> descriptions = ImmutableSortedSet.copyOf(Iterables.transform(methods, new Function<Method, String>() {
+            public String apply(Method method) {
+                return MethodDescription.of(method).toString();
+            }
+        }));
+        return new InvalidManagedModelElementTypeException(extractionContext, message + " (invalid methods: " + Joiner.on(", ").join(descriptions) + ").");
+    }
+
+    static private class ReturnTypeSpecializationOrdering extends Ordering<Method> {
+
+        @Override
+        public int compare(Method left, Method right) {
+            Class<?> leftType = left.getReturnType();
+            Class<?> rightType = right.getReturnType();
+            if (leftType.equals(rightType)) {
+                return 0;
+            }
+            if (leftType.isAssignableFrom(rightType)) {
+                return -1;
+            }
+            if (rightType.isAssignableFrom(leftType)) {
+                return 1;
+            }
+            throw new UnsupportedOperationException(String.format("Cannot compare two types that aren't part of an inheritance hierarchy: %s, %s", leftType, rightType));
+        }
+    }
+
+    private static class NoOpModelElementState implements ModelElementState {
+        @Override
+        public MutableModelNode getBackingNode() {
+            return null;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return null;
+        }
+
+        public Object get(String name) {
+            return null;
+        }
+
+        public void set(String name, Object value) {
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedModelElementTypeException.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedModelElementTypeException.java
new file mode 100644
index 0000000..70c58c1
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedModelElementTypeException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+public class UnmanagedModelElementTypeException extends InvalidManagedModelElementTypeException {
+    public UnmanagedModelElementTypeException(ModelSchemaExtractionContext<?> extractionContext, String message) {
+        super(extractionContext, message);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedStrategy.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedStrategy.java
new file mode 100644
index 0000000..261b0ac
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/manage/schema/extract/UnmanagedStrategy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
+
+import java.util.Collections;
+
+public class UnmanagedStrategy implements ModelSchemaExtractionStrategy {
+    public <T> ModelSchemaExtractionResult<T> extract(ModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
+        return new ModelSchemaExtractionResult<T>(ModelSchema.unmanaged(extractionContext.getType()));
+    }
+
+    public Iterable<String> getSupportedManagedTypes() {
+        return Collections.emptyList();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/method/WeaklyTypeReferencingMethod.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/method/WeaklyTypeReferencingMethod.java
new file mode 100644
index 0000000..84f103d
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/method/WeaklyTypeReferencingMethod.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.method;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.gradle.api.GradleException;
+import org.gradle.internal.Cast;
+import org.gradle.internal.UncheckedException;
+import org.gradle.model.internal.type.ModelType;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+public class WeaklyTypeReferencingMethod<T, R> {
+
+    private final ModelType<T> target;
+    private final ModelType<R> returnType;
+    private final ModelType<?> declaringType;
+    private final String name;
+    private final ImmutableList<ModelType<?>> paramTypes;
+    private final int modifiers;
+
+
+    public WeaklyTypeReferencingMethod(ModelType<T> target, ModelType<R> returnType, Method method) {
+        this.target = target;
+        this.returnType = returnType;
+        this.declaringType = ModelType.of(method.getDeclaringClass());
+        this.name = method.getName();
+        paramTypes = ImmutableList.copyOf(Iterables.transform(Arrays.asList(method.getGenericParameterTypes()), new Function<Type, ModelType<?>>() {
+            public ModelType<?> apply(Type type) {
+                return ModelType.of(type);
+            }
+        }));
+        modifiers = method.getModifiers();
+    }
+
+    public static <T, R> WeaklyTypeReferencingMethod<T, R> of(ModelType<T> target, ModelType<R> returnType, Method method) {
+        return new WeaklyTypeReferencingMethod<T, R>(target, returnType, method);
+    }
+
+    public ModelType<T> getTarget() {
+        return target;
+    }
+
+    public ModelType<R> getReturnType() {
+        return returnType;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getModifiers() {
+        return modifiers;
+    }
+
+    public Class<?> getDeclaringClass() {
+        return declaringType.getRawClass();
+    }
+
+    public Annotation[] getAnnotations() {
+        //we could retrieve annotations at construction time and hold references to them but unfortunately
+        //in IBM JDK strong references are held from annotation instance to class in which it is used so we have to reflect
+        return findMethod().getAnnotations();
+    }
+
+    public Type[] getGenericParameterTypes() {
+        return Iterables.toArray(Iterables.transform(paramTypes, new Function<ModelType<?>, Type>() {
+            public Type apply(ModelType<?> modelType) {
+                return modelType.getType();
+            }
+        }), Type.class);
+    }
+
+    public R invoke(T target, Object... args) {
+        Method method = findMethod();
+        method.setAccessible(true);
+        try {
+            Object result = method.invoke(target, args);
+            return returnType.getConcreteClass().cast(result);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getCause());
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not call %s.%s() on %s", method.getDeclaringClass().getSimpleName(), method.getName(), target), e);
+        }
+    }
+
+    private Method findMethod() {
+        ModelType<Class<?>> classType = new ModelType<Class<?>>() {
+        };
+        Class<?>[] paramTypesArray = Iterables.toArray(Iterables.transform(paramTypes, new Function<ModelType<?>, Class<?>>() {
+            public Class<?> apply(ModelType<?> modelType) {
+                return modelType.getRawClass();
+            }
+        }), classType.getConcreteClass());
+
+        return findMethod(target.getRawClass(), paramTypesArray);
+    }
+
+    private Method findMethod(Class<?> currentTarget, Class<?>[] paramTypes) {
+        for (Method method : currentTarget.getDeclaredMethods()) {
+            if (method.getName().equals(name) && Arrays.equals(paramTypes, method.getParameterTypes())) {
+                return method;
+            }
+        }
+
+        Class<?> parent = currentTarget.getSuperclass();
+        if (parent == null) {
+            throw new org.gradle.internal.reflect.NoSuchMethodException(String.format("Could not find method %s(%s) on %s.", name, Joiner.on(", ").join(paramTypes), target.getRawClass().getSimpleName()));
+        } else {
+            return findMethod(parent, paramTypes);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder()
+                .append(target)
+                .append(returnType)
+                .append(name)
+                .append(paramTypes)
+                .toHashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WeaklyTypeReferencingMethod)) {
+            return false;
+        }
+
+        WeaklyTypeReferencingMethod<?, ?> other = Cast.uncheckedCast(obj);
+
+        return new EqualsBuilder()
+                .append(target, other.target)
+                .append(returnType, other.returnType)
+                .append(name, other.name)
+                .append(paramTypes, other.paramTypes)
+                .isEquals();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/BinderCreationListener.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/BinderCreationListener.java
new file mode 100644
index 0000000..84351f5
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/BinderCreationListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.model.internal.core.ModelPromise;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+abstract class BinderCreationListener extends ModelCreationListener {
+    final ModelRuleDescriptor descriptor;
+    final ModelReference<?> reference;
+    final boolean writable;
+
+    public BinderCreationListener(ModelRuleDescriptor descriptor, ModelReference<?> reference, boolean writable) {
+        this.descriptor = descriptor;
+        this.reference = reference;
+        this.writable = writable;
+    }
+
+    boolean isTypeCompatible(ModelPromise promise) {
+        return writable ? promise.canBeViewedAsWritable(reference.getType()) : promise.canBeViewedAsReadOnly(reference.getType());
+    }
+
+    @Override
+    public String toString() {
+        return "ModelCreationListener{parent=" + matchParent() + ", path=" + matchPath() + ", scope=" + matchScope() + ", type=" + matchType() + "class=" + getClass().getSimpleName() + '}';
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/CreatorRuleBinder.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/CreatorRuleBinder.java
new file mode 100644
index 0000000..4645968
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/CreatorRuleBinder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.model.internal.core.ModelCreator;
+import org.gradle.model.internal.core.ModelPath;
+
+import java.util.Collection;
+
+public class CreatorRuleBinder extends RuleBinder {
+    private final ModelCreator creator;
+
+    public CreatorRuleBinder(ModelCreator creator, ModelPath scope, Collection<RuleBinder> binders) {
+        super(creator.getInputs(), creator.getDescriptor(), scope, binders);
+        this.creator = creator;
+    }
+
+    public ModelCreator getCreator() {
+        return creator;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/DefaultModelRegistry.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/DefaultModelRegistry.java
new file mode 100644
index 0000000..849a1b3
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/DefaultModelRegistry.java
@@ -0,0 +1,1017 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.internal.BiActions;
+import org.gradle.internal.Cast;
+import org.gradle.model.ConfigurationCycleException;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.report.unbound.UnboundRule;
+import org.gradle.model.internal.type.ModelType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.*;
+
+import static org.gradle.model.internal.core.ModelActionRole.*;
+import static org.gradle.model.internal.core.ModelNode.State.*;
+
+ at NotThreadSafe
+public class DefaultModelRegistry implements ModelRegistry {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultModelRegistry.class);
+
+    private final ModelGraph modelGraph;
+    private final ModelRuleExtractor ruleExtractor;
+
+    private final Set<RuleBinder> binders = Sets.newIdentityHashSet();
+    private final List<MutatorRuleBinder<?>> pendingMutatorBinders = Lists.newLinkedList();
+    private final List<MutatorRuleBinder<?>> unboundSubjectMutatorBinders = Lists.newLinkedList();
+    private final LinkedHashMap<ModelRule, ModelBinding<?>> rulesWithInputsBeingClosed = Maps.newLinkedHashMap();
+
+    boolean reset;
+
+    public DefaultModelRegistry(ModelRuleExtractor ruleExtractor) {
+        this.ruleExtractor = ruleExtractor;
+        ModelCreator rootCreator = ModelCreators.of(ModelReference.of(ModelPath.ROOT), BiActions.doNothing()).descriptor("<root>").withProjection(EmptyModelProjection.INSTANCE).build();
+        modelGraph = new ModelGraph(new ModelElementNode(toCreatorBinder(rootCreator)));
+        modelGraph.getRoot().setState(Created);
+    }
+
+    private static String toString(ModelRuleDescriptor descriptor) {
+        StringBuilder stringBuilder = new StringBuilder();
+        descriptor.describeTo(stringBuilder);
+        return stringBuilder.toString();
+    }
+
+    public DefaultModelRegistry create(ModelCreator creator) {
+        ModelPath path = creator.getPath();
+        if (!ModelPath.ROOT.isDirectChild(path)) {
+            throw new InvalidModelRuleDeclarationException(creator.getDescriptor(), "Cannot create element at '" + path + "', only top level is allowed (e.g. '" + path.getRootParent() + "')");
+        }
+
+        registerNode(modelGraph.getRoot(), new ModelElementNode(toCreatorBinder(creator)));
+        return this;
+    }
+
+    public CreatorRuleBinder toCreatorBinder(ModelCreator creator) {
+        return new CreatorRuleBinder(creator, ModelPath.ROOT, binders);
+    }
+
+    private ModelNodeInternal registerNode(ModelNodeInternal parent, ModelNodeInternal child) {
+        if (reset) {
+            return child;
+        }
+
+        ModelCreator creator = child.getCreatorBinder().getCreator();
+        ModelPath path = child.getPath();
+
+        // Disabled before 2.3 release due to not wanting to validate task names (which may contain invalid chars), at least not yet
+        // ModelPath.validateName(name);
+
+        ModelNodeInternal node = modelGraph.find(path);
+        if (node != null) {
+            if (node.getState() == Known) {
+                throw new DuplicateModelException(
+                        String.format(
+                                "Cannot create '%s' using creation rule '%s' as the rule '%s' is already registered to create this model element.",
+                                path,
+                                toString(creator.getDescriptor()),
+                                toString(node.getDescriptor())
+                        )
+                );
+            }
+            throw new DuplicateModelException(
+                    String.format(
+                            "Cannot create '%s' using creation rule '%s' as the rule '%s' has already been used to create this model element.",
+                            path,
+                            toString(creator.getDescriptor()),
+                            toString(node.getDescriptor())
+                    )
+            );
+        }
+        if (!parent.isMutable()) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Cannot create '%s' using creation rule '%s' as model element '%s' is no longer mutable.",
+                            path,
+                            toString(creator.getDescriptor()),
+                            parent.getPath()
+                    )
+            );
+        }
+
+        node = parent.addLink(child);
+        modelGraph.add(node);
+        return node;
+    }
+
+    @Override
+    public <T> DefaultModelRegistry configure(ModelActionRole role, ModelAction<T> action) {
+        bind(action.getSubject(), role, action, ModelPath.ROOT);
+        return this;
+    }
+
+    @Override
+    public ModelRegistry apply(Class<? extends RuleSource> rules) {
+        modelGraph.getRoot().applyToSelf(rules);
+        return this;
+    }
+
+    private <T> void bind(ModelActionRole role, ModelAction<T> mutator, ModelPath scope) {
+        bind(mutator.getSubject(), role, mutator, scope);
+    }
+
+    private <T> void bind(ModelReference<T> subject, ModelActionRole role, ModelAction<T> mutator, ModelPath scope) {
+        if (reset) {
+            return;
+        }
+
+        MutatorRuleBinder<T> binder = new MutatorRuleBinder<T>(subject, role, mutator, scope, binders);
+
+        pendingMutatorBinders.add(binder);
+    }
+
+    private void flushPendingMutatorBinders() {
+        Iterator<MutatorRuleBinder<?>> iterator = pendingMutatorBinders.iterator();
+        while (iterator.hasNext()) {
+            MutatorRuleBinder<?> binder = iterator.next();
+            iterator.remove();
+            bindMutatorSubject(binder);
+        }
+    }
+
+    private <T> void bindMutatorSubject(final MutatorRuleBinder<T> binder) {
+        ModelCreationListener listener = listener(binder.getDescriptor(), binder.getSubjectReference(), binder.getScope(), true, new Action<ModelNodeInternal>() {
+            public void execute(ModelNodeInternal subject) {
+                if (!subject.canApply(binder.getRole())) {
+                    throw new IllegalStateException(String.format(
+                            "Cannot add %s rule '%s' for model element '%s' when element is in state %s.",
+                            binder.getRole(),
+                            binder.getAction().getDescriptor(),
+                            subject.getPath(),
+                            subject.getState()
+                    ));
+                }
+                unboundSubjectMutatorBinders.remove(binder);
+                binder.bindSubject(subject);
+                subject.addMutatorBinder(binder.getRole(), binder);
+            }
+        });
+        registerListener(listener);
+    }
+
+    private void bindInputs(final RuleBinder binder) {
+        if (!binder.isBindingInputs()) {
+            binder.setBindingInputs(true);
+            List<? extends ModelReference<?>> inputReferences = binder.getInputReferences();
+            for (int i = 0; i < inputReferences.size(); i++) {
+                final int finalI = i;
+                ModelReference<?> input = inputReferences.get(i);
+                ModelPath effectiveScope = input.getPath() == null ? ModelPath.ROOT : binder.getScope();
+                registerListener(listener(binder.getDescriptor(), input, effectiveScope, false, new Action<ModelNodeInternal>() {
+                    public void execute(ModelNodeInternal modelNode) {
+                        binder.bindInput(finalI, modelNode);
+                    }
+                }));
+                tryForceBind(binder);
+            }
+        }
+    }
+
+    private ModelCreationListener listener(ModelRuleDescriptor descriptor, ModelReference<?> reference, ModelPath scope, boolean writable, Action<? super ModelNodeInternal> bindAction) {
+        if (reference.getPath() != null) {
+            return new PathBinderCreationListener(descriptor, reference, scope, writable, bindAction);
+        }
+        return new OneOfTypeBinderCreationListener(descriptor, reference, scope, writable, bindAction);
+    }
+
+    public <T> T realize(ModelPath path, ModelType<T> type) {
+        return toType(type, require(path), "get(ModelPath, ModelType)");
+    }
+
+    @Override
+    public ModelNode atState(ModelPath path, ModelNode.State state) {
+        return atStateOrMaybeLater(path, state, false);
+    }
+
+    @Override
+    public ModelNode atStateOrLater(ModelPath path, ModelNode.State state) {
+        return atStateOrMaybeLater(path, state, true);
+    }
+
+    private ModelNode atStateOrMaybeLater(ModelPath path, ModelNode.State state, boolean laterOk) {
+        ModelNodeInternal node = modelGraph.find(path);
+        if (node == null) {
+            return null;
+        }
+        transition(node, state, laterOk);
+        return node;
+    }
+
+    public <T> T find(ModelPath path, ModelType<T> type) {
+        return toType(type, get(path), "find(ModelPath, ModelType)");
+    }
+
+    private <T> T toType(ModelType<T> type, ModelNodeInternal node, String msg) {
+        if (node == null) {
+            return null;
+        } else {
+            return assertView(node, type, null, msg).getInstance();
+        }
+    }
+
+    @Override
+    public ModelNode realizeNode(ModelPath path) {
+        return require(path);
+    }
+
+    private void registerListener(ModelCreationListener listener) {
+        modelGraph.addListener(listener);
+    }
+
+    public void remove(ModelPath path) {
+        ModelNodeInternal node = modelGraph.find(path);
+        if (node == null) {
+            return;
+        }
+
+        Iterable<? extends ModelNode> dependents = node.getDependents();
+        if (Iterables.isEmpty(dependents)) {
+            modelGraph.remove(node);
+        } else {
+            throw new RuntimeException("Tried to remove model " + path + " but it is depended on by: " + Joiner.on(", ").join(dependents));
+        }
+    }
+
+    @Override
+    public ModelRegistry createOrReplace(ModelCreator newCreator) {
+        ModelPath path = newCreator.getPath();
+        ModelNodeInternal node = modelGraph.find(path);
+        if (node == null) {
+            ModelNodeInternal parent = modelGraph.find(path.getParent());
+            if (parent == null) {
+                throw new IllegalStateException("Cannot create '" + path + "' as its parent node does not exist");
+            }
+
+            parent.addLink(newCreator);
+        } else {
+            replace(newCreator);
+        }
+
+        return this;
+    }
+
+    @Override
+    public ModelRegistry replace(ModelCreator newCreator) {
+        ModelNodeInternal node = modelGraph.find(newCreator.getPath());
+        if (node == null) {
+            throw new IllegalStateException("can not replace node " + newCreator.getPath() + " as it does not exist");
+        }
+
+        // Will internally verify that this is valid
+        node.replaceCreatorRuleBinder(toCreatorBinder(newCreator));
+        return this;
+    }
+
+    private ModelNode selfCloseAncestryAndSelf(ModelPath path) {
+        ModelPath parent = path.getParent();
+        if (parent != null) {
+            if (selfCloseAncestryAndSelf(parent) == null) {
+                return null;
+            }
+        }
+        return atStateOrLater(path, SelfClosed);
+    }
+
+    public void bindAllReferences() throws UnboundModelRulesException {
+        flushPendingMutatorBinders();
+        if (binders.isEmpty()) {
+            return;
+        }
+
+        boolean newInputsBound = true;
+        while (!binders.isEmpty() && newInputsBound) {
+            newInputsBound = false;
+            RuleBinder[] unboundBinders = binders.toArray(new RuleBinder[binders.size()]);
+            for (RuleBinder binder : unboundBinders) {
+                tryForceBind(binder);
+                newInputsBound = newInputsBound || binder.isBound();
+            }
+        }
+
+        if (!binders.isEmpty()) {
+            SortedSet<RuleBinder> sortedBinders = new TreeSet<RuleBinder>(new Comparator<RuleBinder>() {
+                @Override
+                public int compare(RuleBinder o1, RuleBinder o2) {
+                    return o1.getDescriptor().toString().compareTo(o2.getDescriptor().toString());
+                }
+            });
+            sortedBinders.addAll(binders);
+            throw unbound(sortedBinders);
+        }
+    }
+
+    private UnboundModelRulesException unbound(Iterable<RuleBinder> binders) {
+        ModelPathSuggestionProvider suggestionsProvider = new ModelPathSuggestionProvider(modelGraph.getFlattened().keySet());
+        List<? extends UnboundRule> unboundRules = new UnboundRulesProcessor(binders, suggestionsProvider).process();
+        return new UnboundModelRulesException(unboundRules);
+    }
+
+    private ModelNodeInternal require(ModelPath path) {
+        ModelNodeInternal node = get(path);
+        if (node == null) {
+            throw new IllegalStateException("No model node at '" + path + "'");
+        }
+        return node;
+    }
+
+    @Override
+    public ModelNode.State state(ModelPath path) {
+        ModelNodeInternal modelNode = modelGraph.find(path);
+        return modelNode == null ? null : modelNode.getState();
+    }
+
+    private ModelNodeInternal get(ModelPath path) {
+        ModelNodeInternal node = modelGraph.find(path);
+        if (node == null) {
+            return null;
+        }
+        close(node);
+        return node;
+    }
+
+    private void close(ModelNodeInternal node) {
+        transition(node, GraphClosed, false);
+    }
+
+    private void transition(ModelNodeInternal node, ModelNode.State desired, boolean laterOk) {
+        ModelPath path = node.getPath();
+        ModelNode.State state = node.getState();
+
+        LOGGER.debug("Transitioning model element '{}' from state {} to {}", path, state.name(), desired.name());
+
+        if (desired.ordinal() < state.ordinal()) {
+            if (laterOk) {
+                return;
+            } else {
+                throw new IllegalStateException("Cannot lifecycle model node '" + path + "' to state " + desired.name() + " as it is already at " + state.name());
+            }
+        }
+
+        if (state == desired) {
+            return;
+        }
+
+        if (state == Known && desired.ordinal() >= Created.ordinal()) {
+            CreatorRuleBinder creatorBinder = node.getCreatorBinder();
+            if (creatorBinder != null) {
+                forceBind(creatorBinder);
+            }
+            doCreate(node, creatorBinder);
+            node.notifyFired(creatorBinder);
+            node.setState(Created);
+
+            if (desired == Created) {
+                return;
+            }
+        }
+
+        if (!fireMutations(node, path, state, Defaults, DefaultsApplied, desired)) {
+            return;
+        }
+        if (!fireMutations(node, path, state, Initialize, Initialized, desired)) {
+            return;
+        }
+        if (!fireMutations(node, path, state, Mutate, Mutated, desired)) {
+            return;
+        }
+        if (!fireMutations(node, path, state, Finalize, Finalized, desired)) {
+            return;
+        }
+        if (!fireMutations(node, path, state, Validate, SelfClosed, desired)) {
+            return;
+        }
+
+        if (desired.ordinal() >= GraphClosed.ordinal()) {
+            for (ModelNodeInternal child : node.getLinks()) {
+                close(child);
+            }
+            node.setState(GraphClosed);
+        }
+
+        LOGGER.debug("Finished transitioning model element {} from state {} to {}", path, state.name(), desired.name());
+    }
+
+    private boolean forceBindReference(ModelReference<?> reference, ModelBinding<?> binding, ModelPath scope) {
+        if (binding == null) {
+            if (reference.getPath() == null) {
+                selfCloseAncestryAndSelf(scope);
+            } else {
+                selfCloseAncestryAndSelf(reference.getPath().getParent());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private boolean tryForceBind(RuleBinder binder) {
+        if (binder.isBound()) {
+            return false;
+        }
+        bindInputs(binder);
+
+        boolean boundSomething = false;
+        ModelPath scope = binder.getScope();
+
+        if (binder.getSubjectReference() != null && binder.getSubjectBinding() == null) {
+            if (forceBindReference(binder.getSubjectReference(), binder.getSubjectBinding(), scope)) {
+                boundSomething = binder.getSubjectBinding() != null;
+            }
+        }
+
+        for (int i = 0; i < binder.getInputReferences().size(); i++) {
+            if (forceBindReference(binder.getInputReferences().get(i), binder.getInputBindings().get(i), scope)) {
+                boundSomething = boundSomething || binder.getInputBindings().get(i) != null;
+            }
+        }
+
+        return boundSomething;
+    }
+
+    private void forceBind(RuleBinder binder) {
+        tryForceBind(binder);
+        if (!binder.isBound()) {
+            throw unbound(Collections.singleton(binder));
+        }
+    }
+
+    // NOTE: this should only be called from transition() as implicit logic is shared
+    private boolean fireMutations(ModelNodeInternal node, ModelPath path, ModelNode.State originalState, ModelActionRole type, ModelNode.State to, ModelNode.State desired) {
+        ModelNode.State nodeState = node.getState();
+        if (nodeState.ordinal() >= to.ordinal()) {
+            return nodeState.ordinal() < desired.ordinal();
+        }
+
+        flushPendingMutatorBinders();
+        for (MutatorRuleBinder<?> binder : node.getMutatorBinders(type)) {
+            forceBind(binder);
+            fireMutation(binder);
+            flushPendingMutatorBinders();
+            node.notifyFired(binder);
+        }
+
+        node.setState(to);
+
+        if (to == desired) {
+            LOGGER.debug("Finished transitioning model element {} from state {} to {}", path, originalState.name(), desired.name());
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private <T> ModelView<? extends T> assertView(ModelNodeInternal node, ModelType<T> targetType, @Nullable ModelRuleDescriptor descriptor, String msg, Object... msgArgs) {
+        ModelAdapter adapter = node.getAdapter();
+        ModelView<? extends T> view = adapter.asReadOnly(targetType, node, descriptor);
+        if (view == null) {
+            // TODO better error reporting here
+            throw new IllegalArgumentException("Model node '" + node.getPath().toString() + "' is not compatible with requested " + targetType + " (operation: " + String.format(msg, msgArgs) + ")");
+        } else {
+            return view;
+        }
+    }
+
+    private <T> ModelView<? extends T> assertView(ModelNodeInternal node, ModelReference<T> reference, ModelRuleDescriptor sourceDescriptor, List<ModelView<?>> inputs) {
+        ModelAdapter adapter = node.getAdapter();
+        ModelView<? extends T> view = adapter.asWritable(reference.getType(), node, sourceDescriptor, inputs);
+        if (view == null) {
+            // TODO better error reporting here
+            throw new IllegalArgumentException("Cannot project model element " + node.getPath() + " to writable type '" + reference.getType() + "' for rule " + sourceDescriptor);
+        } else {
+            return view;
+        }
+    }
+
+    private ModelNodeInternal doCreate(ModelNodeInternal node, CreatorRuleBinder boundCreator) {
+        ModelCreator creator = boundCreator.getCreator();
+        List<ModelView<?>> views = toViews(boundCreator.getInputBindings(), boundCreator.getCreator());
+
+        LOGGER.debug("Creating {} using {}", node.getPath(), creator.getDescriptor());
+
+        try {
+            creator.create(node, views);
+        } catch (Exception e) {
+            // TODO some representation of state of the inputs
+            throw new ModelRuleExecutionException(creator.getDescriptor(), e);
+        }
+
+        return node;
+    }
+
+    private <T> void fireMutation(MutatorRuleBinder<T> boundMutator) {
+        List<ModelView<?>> inputs = toViews(boundMutator.getInputBindings(), boundMutator.getAction());
+
+        ModelNodeInternal node = boundMutator.getSubjectBinding().getNode();
+        ModelAction<T> mutator = boundMutator.getAction();
+        ModelRuleDescriptor descriptor = mutator.getDescriptor();
+
+        LOGGER.debug("Mutating {} using {}", node.getPath(), mutator.getDescriptor());
+
+        ModelView<? extends T> view = assertView(node, boundMutator.getSubjectReference(), descriptor, inputs);
+        try {
+            mutator.execute(node, view.getInstance(), inputs);
+        } catch (Exception e) {
+            // TODO some representation of state of the inputs
+            throw new ModelRuleExecutionException(descriptor, e);
+        } finally {
+            view.close();
+        }
+    }
+
+    private List<ModelView<?>> toViews(List<ModelBinding<?>> bindings, ModelRule modelRule) {
+        // hot path; create as little as possible…
+        @SuppressWarnings("unchecked") ModelView<?>[] array = new ModelView<?>[bindings.size()];
+        int i = 0;
+        for (ModelBinding<?> binding : bindings) {
+            closeRuleBinding(modelRule, binding);
+            ModelPath path = binding.getNode().getPath();
+            ModelNodeInternal element = require(path);
+            ModelView<?> view = assertView(element, binding.getReference().getType(), modelRule.getDescriptor(), "toViews");
+            array[i++] = view;
+        }
+        @SuppressWarnings("unchecked") List<ModelView<?>> views = Arrays.asList(array);
+        return views;
+    }
+
+    private void closeRuleBinding(ModelRule modelRule, ModelBinding<?> binding) {
+        if (rulesWithInputsBeingClosed.containsKey(modelRule)) {
+            throw ruleCycle(modelRule);
+        }
+        rulesWithInputsBeingClosed.put(modelRule, binding);
+        try {
+            close(binding.getNode());
+        } finally {
+            rulesWithInputsBeingClosed.remove(modelRule);
+        }
+    }
+
+    private ConfigurationCycleException ruleCycle(ModelRule cycleStartRule) {
+        boolean cycleStartFound = false;
+        String indent = "  ";
+        StringBuilder prefix = new StringBuilder(indent);
+        StringWriter out = new StringWriter();
+        PrintWriter writer = new PrintWriter(out);
+
+        writer.println("A cycle has been detected in model rule dependencies. References forming the cycle:");
+
+        for (Map.Entry<ModelRule, ModelBinding<?>> ruleInputInClosing : rulesWithInputsBeingClosed.entrySet()) {
+            ModelRule rule = ruleInputInClosing.getKey();
+            ModelRuleDescriptor ruleDescriptor = rule.getDescriptor();
+            ModelBinding<?> binding = ruleInputInClosing.getValue();
+            if (cycleStartFound) {
+                reportRuleInputBeingClosed(indent, prefix, writer, ruleDescriptor, binding);
+            } else {
+                if (rule.equals(cycleStartRule)) {
+                    cycleStartFound = true;
+                    reportRuleInputBeingClosed(indent, prefix, writer, ruleDescriptor, binding);
+                }
+            }
+        }
+        writer.print(cycleStartRule.getDescriptor().toString());
+
+        return new ConfigurationCycleException(out.toString());
+    }
+
+    private void reportRuleInputBeingClosed(String indent, StringBuilder prefix, PrintWriter writer, ModelRuleDescriptor ruleDescriptor, ModelBinding<?> binding) {
+        writer.print(ruleDescriptor.toString());
+        String referenceDescription = binding.getReference().getDescription();
+        if (referenceDescription != null) {
+            writer.print(" ");
+            writer.print(referenceDescription);
+        }
+        writer.print(" (path: ");
+        writer.print(binding.getNode().getPath().toString());
+        writer.print(")");
+        writer.println();
+        writer.print(prefix);
+        writer.print("\\--- ");
+        prefix.append(indent);
+    }
+
+    @Override
+    public ModelNode node(ModelPath path) {
+        return modelGraph.find(path);
+    }
+
+    @Override
+    public void prepareForReuse() {
+        reset = true;
+
+        List<ModelNodeInternal> ephemerals = Lists.newLinkedList();
+        collectEphemeralChildren(modelGraph.getRoot(), ephemerals);
+        if (ephemerals.isEmpty()) {
+            LOGGER.info("No ephemeral model nodes found to reset");
+        } else {
+            if (LOGGER.isInfoEnabled()) {
+                LOGGER.info("Resetting ephemeral model nodes: " + Joiner.on(", ").join(ephemerals));
+            }
+
+            for (ModelNodeInternal ephemeral : ephemerals) {
+                ephemeral.reset();
+            }
+        }
+    }
+
+    private void collectEphemeralChildren(ModelNodeInternal node, Collection<ModelNodeInternal> ephemerals) {
+        for (ModelNodeInternal child : node.getLinks()) {
+            if (child.isEphemeral()) {
+                ephemerals.add(child);
+            } else {
+                collectEphemeralChildren(child, ephemerals);
+            }
+        }
+    }
+
+    private class ModelReferenceNode extends ModelNodeInternal {
+        private ModelNodeInternal target;
+
+        public ModelReferenceNode(CreatorRuleBinder creatorBinder) {
+            super(creatorBinder);
+        }
+
+        @Override
+        public ModelNodeInternal getTarget() {
+            return target;
+        }
+
+        @Override
+        public void setTarget(ModelNode target) {
+            this.target = (ModelNodeInternal) target;
+        }
+
+        @Override
+        public ModelNodeInternal addLink(ModelNodeInternal node) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addLink(ModelCreator creator) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addReference(ModelCreator creator) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeLink(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> void applyToSelf(ModelActionRole type, ModelAction<T> action) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> void applyToAllLinks(ModelActionRole type, ModelAction<T> action) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> void applyToLink(ModelActionRole type, ModelAction<T> action) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void applyToLink(String name, Class<? extends RuleSource> rules) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> void applyToLinks(Class<T> type, Class<? extends RuleSource> rules) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void applyToSelf(Class<? extends RuleSource> rules) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getLinkCount(ModelType<?> type) {
+            return 0;
+        }
+
+        @Override
+        public Set<String> getLinkNames(ModelType<?> type) {
+            return Collections.emptySet();
+        }
+
+        @Nullable
+        @Override
+        public MutableModelNode getLink(String name) {
+            return null;
+        }
+
+        @Override
+        public Iterable<? extends ModelNodeInternal> getLinks() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Iterable<? extends MutableModelNode> getLinks(ModelType<?> type) {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public boolean hasLink(String name, ModelType<?> type) {
+            return false;
+        }
+
+        @Override
+        public boolean hasLink(String name) {
+            return false;
+        }
+
+        @Override
+        public <T> T getPrivateData(ModelType<T> type) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> void setPrivateData(ModelType<? super T> type, T object) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object getPrivateData() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void ensureUsable() {
+        }
+    }
+
+    private class ModelElementNode extends ModelNodeInternal {
+        private final Map<String, ModelNodeInternal> links = Maps.newTreeMap();
+        private Object privateData;
+        private ModelType<?> privateDataType;
+
+        public ModelElementNode(CreatorRuleBinder creatorBinder) {
+            super(creatorBinder);
+        }
+
+        @Override
+        public ModelNodeInternal addLink(ModelNodeInternal node) {
+            links.put(node.getPath().getName(), node);
+            return node;
+        }
+
+        public <T> T getPrivateData(ModelType<T> type) {
+            if (privateData == null) {
+                return null;
+            }
+
+            if (!type.isAssignableFrom(privateDataType)) {
+                throw new ClassCastException("Cannot get private data '" + privateData + "' of type '" + privateDataType + "' as type '" + type);
+            }
+            return Cast.uncheckedCast(privateData);
+        }
+
+        @Override
+        public Object getPrivateData() {
+            return privateData;
+        }
+
+        public <T> void setPrivateData(ModelType<? super T> type, T object) {
+            if (!isMutable()) {
+                throw new IllegalStateException(String.format("Cannot set value for model element '%s' as this element is not mutable.", getPath()));
+            }
+            this.privateDataType = type;
+            this.privateData = object;
+        }
+
+        public boolean hasLink(String name) {
+            return links.containsKey(name);
+        }
+
+        @Nullable
+        public ModelNodeInternal getLink(String name) {
+            return links.get(name);
+        }
+
+        public Iterable<? extends ModelNodeInternal> getLinks() {
+            return links.values();
+        }
+
+        @Override
+        public int getLinkCount(ModelType<?> type) {
+            int count = 0;
+            for (ModelNodeInternal linked : links.values()) {
+                if (linked.getPromise().canBeViewedAsWritable(type)) {
+                    count++;
+                }
+            }
+            return count;
+        }
+
+        @Override
+        public Set<String> getLinkNames(ModelType<?> type) {
+            Set<String> names = Sets.newLinkedHashSet();
+            for (Map.Entry<String, ModelNodeInternal> entry : links.entrySet()) {
+                if (entry.getValue().getPromise().canBeViewedAsWritable(type)) {
+                    names.add(entry.getKey());
+                }
+            }
+            return names;
+        }
+
+        @Override
+        public Iterable<? extends MutableModelNode> getLinks(final ModelType<?> type) {
+            return Iterables.filter(links.values(), new Predicate<ModelNodeInternal>() {
+                @Override
+                public boolean apply(ModelNodeInternal input) {
+                    return input.getPromise().canBeViewedAsWritable(type);
+                }
+            });
+        }
+
+        @Override
+        public boolean hasLink(String name, ModelType<?> type) {
+            ModelNodeInternal linked = getLink(name);
+            return linked != null && linked.getPromise().canBeViewedAsWritable(type);
+        }
+
+        @Override
+        public <T> void applyToSelf(ModelActionRole type, ModelAction<T> action) {
+            if (!getPath().equals(action.getSubject().getPath())) {
+                throw new IllegalArgumentException(String.format("Element action reference has path (%s) which does not reference this node (%s).", action.getSubject().getPath(), getPath()));
+            }
+            bind(action.getSubject(), type, action, ModelPath.ROOT);
+        }
+
+        @Override
+        public <T> void applyToLink(ModelActionRole type, ModelAction<T> action) {
+            if (!getPath().isDirectChild(action.getSubject().getPath())) {
+                throw new IllegalArgumentException(String.format("Linked element action reference has a path (%s) which is not a child of this node (%s).", action.getSubject().getPath(), getPath()));
+            }
+            bind(action.getSubject(), type, action, ModelPath.ROOT);
+        }
+
+        @Override
+        public void applyToLink(String name, Class<? extends RuleSource> rules) {
+            apply(rules, getPath().child(name));
+        }
+
+        @Override
+        public void applyToSelf(Class<? extends RuleSource> rules) {
+            apply(rules, getPath());
+        }
+
+        @Override
+        public <T> void applyToLinks(Class<T> type, final Class<? extends RuleSource> rules) {
+            final ModelType<T> modelType = ModelType.of(type);
+            registerListener(new ModelCreationListener() {
+                @Nullable
+                @Override
+                public ModelPath matchParent() {
+                    return getPath();
+                }
+
+                @Nullable
+                @Override
+                public ModelType<?> matchType() {
+                    return modelType;
+                }
+
+                @Override
+                public boolean onCreate(ModelNodeInternal node) {
+                    node.applyToSelf(rules);
+                    return false;
+                }
+            });
+        }
+
+        public void apply(Class<? extends RuleSource> rules, ModelPath scope) {
+            Iterable<ExtractedModelRule> extractedRules = ruleExtractor.extract(rules);
+            for (ExtractedModelRule extractedRule : extractedRules) {
+                // TODO - remove this when we remove the 'rule dependencies' mechanism
+                if (!extractedRule.getRuleDependencies().isEmpty()) {
+                    throw new IllegalStateException("Rule source " + rules + " cannot have plugin dependencies (introduced by rule " + extractedRule + ")");
+                }
+
+                if (extractedRule.getType().equals(ExtractedModelRule.Type.CREATOR)) {
+                    if (scope.equals(ModelPath.ROOT)) {
+                        DefaultModelRegistry.this.create(extractedRule.getCreator());
+                    } else {
+                        throw new InvalidModelRuleDeclarationException("Rule " + extractedRule.getCreator().getDescriptor() + " cannot be applied at the scope of model element " + scope + " as creation rules cannot be used when applying rule sources to particular elements");
+                    }
+                } else if (extractedRule.getType().equals(ExtractedModelRule.Type.ACTION)) {
+                    // TODO this is a roundabout path, something like the registrar interface should be implementable by the regsitry and nodes
+                    bind(extractedRule.getActionRole(), extractedRule.getAction(), scope);
+                } else {
+                    throw new IllegalStateException("unexpected extracted rule type: " + extractedRule.getType());
+                }
+            }
+        }
+
+        @Override
+        public <T> void applyToAllLinks(final ModelActionRole type, final ModelAction<T> action) {
+            if (action.getSubject().getPath() != null) {
+                throw new IllegalArgumentException("Linked element action reference must have null path.");
+            }
+
+            registerListener(new ModelCreationListener() {
+                @Nullable
+                @Override
+                public ModelPath matchParent() {
+                    return getPath();
+                }
+
+                @Nullable
+                @Override
+                public ModelType<?> matchType() {
+                    return action.getSubject().getType();
+                }
+
+                @Override
+                public boolean onCreate(ModelNodeInternal node) {
+                    bind(ModelReference.of(node.getPath(), action.getSubject().getType()), type, action, ModelPath.ROOT);
+                    return false;
+                }
+            });
+        }
+
+        @Override
+        public void addReference(ModelCreator creator) {
+            if (!getPath().isDirectChild(creator.getPath())) {
+                throw new IllegalArgumentException(String.format("Reference element creator has a path (%s) which is not a child of this node (%s).", creator.getPath(), getPath()));
+            }
+            registerNode(this, new ModelReferenceNode(toCreatorBinder(creator)));
+        }
+
+        @Override
+        public void addLink(ModelCreator creator) {
+            if (!getPath().isDirectChild(creator.getPath())) {
+                throw new IllegalArgumentException(String.format("Linked element creator has a path (%s) which is not a child of this node (%s).", creator.getPath(), getPath()));
+            }
+            registerNode(this, new ModelElementNode(toCreatorBinder(creator)));
+        }
+
+        @Override
+        public void removeLink(String name) {
+            if (links.remove(name) != null) {
+                remove(getPath().child(name));
+            }
+        }
+
+        @Override
+        public ModelNodeInternal getTarget() {
+            return this;
+        }
+
+        @Override
+        public void setTarget(ModelNode target) {
+            throw new UnsupportedOperationException(String.format("This node (%s) is not a reference to another node.", getPath()));
+        }
+
+        @Override
+        public void ensureUsable() {
+            transition(this, Initialized, true);
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelBinding.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelBinding.java
new file mode 100644
index 0000000..6674db6
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelBinding.java
@@ -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.model.internal.registry;
+
+import com.google.common.base.Function;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+
+/**
+ * A binding of a reference to an actual model element.
+ * <p>
+ * A binding represents the knowledge that the model element referenced by the reference is known and can project a view of the reference type.
+ * Like the reference, whether the view is read or write is not inherent in the binding and is contextual.
+ */
+ at ThreadSafe
+class ModelBinding<T> {
+
+    private final ModelNodeInternal node;
+    private final ModelReference<T> reference;
+
+    private ModelBinding(ModelReference<T> reference, ModelNodeInternal node) {
+        this.node = node;
+        this.reference = reference;
+    }
+
+    public static <T> ModelBinding<T> of(ModelReference<T> reference, ModelNodeInternal modelNode) {
+        return new ModelBinding<T>(reference, modelNode);
+    }
+
+    public ModelReference<T> getReference() {
+        return reference;
+    }
+
+    public ModelNodeInternal getNode() {
+        return node;
+    }
+
+    @Override
+    public String toString() {
+        return "ModelBinding{reference=" + reference + ", node=" + node + '}';
+    }
+
+    public static class GetPath implements Function<ModelBinding<?>, ModelPath> {
+
+        public static final Function<ModelBinding<?>, ModelPath> INSTANCE = new GetPath();
+
+        private GetPath() {
+        }
+
+        @Nullable
+        public ModelPath apply(ModelBinding<?> input) {
+            return input.getNode().getPath();
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelCreationListener.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelCreationListener.java
new file mode 100644
index 0000000..b50c207
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelCreationListener.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.model.internal.registry;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
+
+public abstract class ModelCreationListener {
+    /**
+     * Returns the path of the node which this listener is interested in, or null if path is not relevant.
+     */
+    @Nullable
+    public ModelPath matchPath() {
+        return null;
+    }
+
+    /**
+     * Returns the parent path of the node which this listener is interested in, or null if path is not relevant.
+     */
+    @Nullable
+    public ModelPath matchParent() {
+        return null;
+    }
+
+    /**
+     * Return the path of the scope this listener is interested in, or null if the scope is not relevant.
+     *
+     * If the returned value is not null then the listener will be informed about element created at the returned path and about its immediate children if other criteria specified by this listener
+     * match as well.
+     */
+    @Nullable
+    public ModelPath matchScope() {
+        return null;
+    }
+
+    /**
+     * Returns the type of node which this listener is interested in, or null if type is not relevant.
+     */
+    @Nullable
+    public ModelType<?> matchType() {
+        return null;
+    }
+
+    /**
+     * Invoked for each node that matches the criteria specified by {@link #matchPath()}, {@link #matchParent()}, {@link #matchScope()} or {@link #matchType()}, or every node if
+     * no criteria specified. Stops notifying listener with further nodes when this method returns true.
+     */
+    public abstract boolean onCreate(ModelNodeInternal node);
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelGraph.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelGraph.java
new file mode 100644
index 0000000..afe8cb8
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelGraph.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.ModelNode;
+import org.gradle.model.internal.core.ModelPath;
+
+import java.util.*;
+
+public class ModelGraph {
+    private final ModelNodeInternal root;
+    private final Map<ModelPath, ModelNodeInternal> flattened = Maps.newTreeMap();
+    private final SetMultimap<ModelPath, ModelCreationListener> pathListeners = LinkedHashMultimap.create();
+    private final SetMultimap<ModelPath, ModelCreationListener> parentListeners = LinkedHashMultimap.create();
+    private final SetMultimap<ModelPath, ModelCreationListener> scopeListeners = LinkedHashMultimap.create();
+    private final Set<ModelCreationListener> listeners = new LinkedHashSet<ModelCreationListener>();
+    private boolean notifying;
+    private final List<ModelCreationListener> pendingListeners = new ArrayList<ModelCreationListener>();
+    private final List<ModelNodeInternal> pendingNodes = new ArrayList<ModelNodeInternal>();
+
+    public ModelGraph(ModelNodeInternal rootNode) {
+        this.root = rootNode;
+        flattened.put(root.getPath(), root);
+    }
+
+    public ModelNodeInternal getRoot() {
+        return root;
+    }
+
+    public Map<ModelPath, ModelNodeInternal> getFlattened() {
+        return Collections.unmodifiableMap(flattened);
+    }
+
+    public void add(ModelNodeInternal node) {
+        if (notifying) {
+            pendingNodes.add(node);
+            return;
+        }
+
+        doAdd(node);
+        flush();
+    }
+
+    private void doAdd(ModelNodeInternal node) {
+        flattened.put(node.getPath(), node);
+        notifying = true;
+        try {
+            notifyListeners(node, pathListeners.get(node.getPath()));
+            notifyListeners(node, parentListeners.get(node.getPath().getParent()));
+            notifyListeners(node, scopeListeners.get(node.getPath()));
+            notifyListeners(node, scopeListeners.get(node.getPath().getParent()));
+            notifyListeners(node, listeners);
+        } finally {
+            notifying = false;
+        }
+    }
+
+    private void notifyListeners(ModelNodeInternal node, Iterable<ModelCreationListener> listeners) {
+        Iterator<ModelCreationListener> iterator = listeners.iterator();
+        while (iterator.hasNext()) {
+            ModelCreationListener listener = iterator.next();
+            if (maybeNotify(node, listener)) {
+                iterator.remove();
+            }
+        }
+    }
+
+    public void addListener(ModelCreationListener listener) {
+        if (notifying) {
+            pendingListeners.add(listener);
+            return;
+        }
+
+        doAddListener(listener);
+        flush();
+    }
+
+    private void doAddListener(ModelCreationListener listener) {
+        notifying = true;
+        try {
+            if (listener.matchPath() != null) {
+                ModelNodeInternal node = flattened.get(listener.matchPath());
+                if (node != null) {
+                    if (maybeNotify(node, listener)) {
+                        return;
+                    }
+                }
+                pathListeners.put(listener.matchPath(), listener);
+                return;
+            }
+            if (listener.matchParent() != null) {
+                ModelNodeInternal parent = flattened.get(listener.matchParent());
+                if (parent != null) {
+                    for (ModelNodeInternal node : parent.getLinks()) {
+                        if (maybeNotify(node, listener)) {
+                            return;
+                        }
+                    }
+                }
+                parentListeners.put(listener.matchParent(), listener);
+                return;
+            }
+            if (listener.matchScope() != null) {
+                ModelNodeInternal scope = flattened.get(listener.matchScope());
+                if (scope != null) {
+                    if (maybeNotify(scope, listener)) {
+                        return;
+                    }
+                    for (ModelNodeInternal node : scope.getLinks()) {
+                        if (maybeNotify(node, listener)) {
+                            return;
+                        }
+                    }
+                }
+                scopeListeners.put(listener.matchScope(), listener);
+                return;
+            }
+            for (ModelNodeInternal node : flattened.values()) {
+                if (maybeNotify(node, listener)) {
+                    return;
+                }
+            }
+            listeners.add(listener);
+        } finally {
+            notifying = false;
+        }
+    }
+
+    private void flush() {
+        while (!pendingListeners.isEmpty()) {
+            doAddListener(pendingListeners.remove(0));
+        }
+        while (!pendingNodes.isEmpty()) {
+            doAdd(pendingNodes.remove(0));
+        }
+    }
+
+    private boolean maybeNotify(ModelNodeInternal node, ModelCreationListener listener) {
+        if (listener.matchType() != null && !node.getPromise().canBeViewedAsWritable(listener.matchType()) && !node.getPromise().canBeViewedAsReadOnly(listener.matchType())) {
+            return false;
+        }
+        return listener.onCreate(node);
+    }
+
+    @Nullable
+    public ModelNodeInternal find(ModelPath path) {
+        return flattened.get(path);
+    }
+
+    public ModelNodeInternal get(ModelPath path) {
+        ModelNodeInternal found = find(path);
+        if (found == null) {
+            throw new IllegalStateException("Expected model node @ '" + path + "' but none was found");
+        }
+
+        return found;
+    }
+
+    @Nullable
+    public ModelNodeInternal remove(ModelNode node) {
+        ModelNodeInternal parentNode = find(node.getPath().getParent());
+        if (parentNode != null) {
+            parentNode.removeLink(node.getPath().getName());
+        }
+
+        return flattened.remove(node.getPath());
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelNodeInternal.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelNodeInternal.java
new file mode 100644
index 0000000..b16052d
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelNodeInternal.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.gradle.api.Nullable;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.type.ModelType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+abstract class ModelNodeInternal implements MutableModelNode {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ModelNodeInternal.class);
+
+    private CreatorRuleBinder creatorBinder;
+    private Map<ModelActionRole, List<MutatorRuleBinder<?>>> mutators;
+    private final Set<ModelNodeInternal> dependencies = Sets.newHashSet();
+    private final Set<ModelNodeInternal> dependents = Sets.newHashSet();
+    private ModelNode.State state = ModelNode.State.Known;
+    private boolean hidden;
+
+    public ModelNodeInternal(CreatorRuleBinder creatorBinder) {
+        this.creatorBinder = creatorBinder;
+    }
+
+    public CreatorRuleBinder getCreatorBinder() {
+        return creatorBinder;
+    }
+
+    public void replaceCreatorRuleBinder(CreatorRuleBinder newCreatorBinder) {
+        if (getState() != State.Known) {
+            throw new IllegalStateException("Cannot replace creator rule binder when not in known state (node: " + this + ", state: " + getState() + ")");
+        }
+
+        ModelCreator newCreator = newCreatorBinder.getCreator();
+        ModelCreator oldCreator = creatorBinder.getCreator();
+
+        // Can't change type
+        if (!oldCreator.getPromise().equals(newCreator.getPromise())) {
+            throw new IllegalStateException("can not replace node " + getPath() + " with different promise (old: " + oldCreator.getPromise() + ", new: " + newCreator.getPromise() + ")");
+        }
+
+        // Can't have different inputs
+        if (!newCreator.getInputs().equals(oldCreator.getInputs())) {
+            Joiner joiner = Joiner.on(", ");
+            throw new IllegalStateException("can not replace node " + getPath() + " with creator with different input bindings (old: [" + joiner.join(oldCreator.getInputs()) + "], new: [" + joiner.join(newCreator.getInputs()) + "])");
+        }
+
+        this.creatorBinder = newCreatorBinder;
+    }
+
+    @Override
+    public boolean isHidden() {
+        return hidden;
+    }
+
+    @Override
+    public void setHidden(boolean hidden) {
+        this.hidden = hidden;
+    }
+
+    @Override
+    public boolean isEphemeral() {
+        return creatorBinder.getCreator().isEphemeral();
+    }
+
+    public void addMutatorBinder(ModelActionRole role, MutatorRuleBinder<?> mutator) {
+        if (mutators == null) {
+            mutators = Maps.newEnumMap(ModelActionRole.class);
+        }
+
+        List<MutatorRuleBinder<?>> mutatorsForRole = mutators.get(role);
+        if (mutatorsForRole == null) {
+            mutatorsForRole = Lists.newLinkedList();
+            mutators.put(role, mutatorsForRole);
+        }
+
+        mutatorsForRole.add(mutator);
+    }
+
+    public Iterable<MutatorRuleBinder<?>> getMutatorBinders(ModelActionRole role) {
+        if (mutators == null) {
+            return Collections.emptyList();
+        }
+        final List<MutatorRuleBinder<?>> ruleBinders = mutators.get(role);
+        if (ruleBinders == null) {
+            return Collections.emptyList();
+        } else {
+            return new Iterable<MutatorRuleBinder<?>>() {
+                @Override
+                public Iterator<MutatorRuleBinder<?>> iterator() {
+                    return new Iterator<MutatorRuleBinder<?>>() {
+                        int i;
+
+                        @Override
+                        public void remove() {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        @Override
+                        public boolean hasNext() {
+                            return i < ruleBinders.size();
+                        }
+
+                        @Override
+                        public MutatorRuleBinder<?> next() {
+                            if (hasNext()) {
+                                return ruleBinders.get(i++);
+                            } else {
+                                throw new NoSuchElementException();
+                            }
+                        }
+                    };
+                }
+            };
+        }
+    }
+
+    public void notifyFired(RuleBinder binder) {
+        assert binder.isBound();
+        for (ModelBinding<?> inputBinding : binder.getInputBindings()) {
+            ModelNodeInternal node = inputBinding.getNode();
+            dependencies.add(node);
+            node.dependents.add(this);
+        }
+    }
+
+    public Iterable<? extends ModelNode> getDependencies() {
+        return dependencies;
+    }
+
+    public Iterable<? extends ModelNode> getDependents() {
+        return dependents;
+    }
+
+    public ModelPath getPath() {
+        return creatorBinder.getCreator().getPath();
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return creatorBinder.getDescriptor();
+    }
+
+    public ModelNode.State getState() {
+        return state;
+    }
+
+    public void setState(ModelNode.State state) {
+        this.state = state;
+    }
+
+    public boolean isMutable() {
+        return state.mutable;
+    }
+
+    public boolean canApply(ModelActionRole type) {
+        return type.ordinal() >= state.ordinal() - ModelNode.State.Created.ordinal();
+    }
+
+    public ModelPromise getPromise() {
+        return creatorBinder.getCreator().getPromise();
+    }
+
+    public ModelAdapter getAdapter() {
+        return creatorBinder.getCreator().getAdapter();
+    }
+
+    @Override
+    public String toString() {
+        return getPath().toString();
+    }
+
+    public abstract ModelNodeInternal getTarget();
+
+    public abstract Iterable<? extends ModelNodeInternal> getLinks();
+
+    public abstract ModelNodeInternal addLink(ModelNodeInternal node);
+
+    @Override
+    public <T> ModelView<? extends T> asReadOnly(ModelType<T> type, @Nullable ModelRuleDescriptor ruleDescriptor) {
+        ModelView<? extends T> modelView = getAdapter().asReadOnly(type, this, ruleDescriptor);
+        if (modelView == null) {
+            throw new IllegalStateException("Model node " + getPath() + " cannot be expressed as a read-only view of type " + type);
+        }
+        return modelView;
+    }
+
+    @Override
+    public <T> ModelView<? extends T> asWritable(ModelType<T> type, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> inputs) {
+        ModelView<? extends T> modelView = getAdapter().asWritable(type, this, ruleDescriptor, inputs);
+        if (modelView == null) {
+            throw new IllegalStateException("Model node " + getPath() + " cannot be expressed as a mutable view of type " + type);
+        }
+        return modelView;
+    }
+
+    public void reset() {
+        if (getState() != State.Known) {
+            setState(State.Known);
+            setPrivateData(ModelType.untyped(), null);
+
+            for (ModelNodeInternal dependent : dependents) {
+                if (LOGGER.isInfoEnabled()) {
+                    LOGGER.info("resetting dependent node of {}: {}", this, dependent);
+                }
+                dependent.reset();
+            }
+
+            for (ModelNodeInternal child : getLinks()) {
+                if (LOGGER.isInfoEnabled()) {
+                    LOGGER.info("resetting child node of {}: {}", this, child);
+                }
+
+                child.reset();
+            }
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelPathSuggestionProvider.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelPathSuggestionProvider.java
new file mode 100644
index 0000000..3c696eb
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelPathSuggestionProvider.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Transformer;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+ at ThreadSafe
+class ModelPathSuggestionProvider implements Transformer<List<ModelPath>, ModelPath> {
+
+    private static final Predicate<Suggestion> REMOVE_NULLS = new Predicate<Suggestion>() {
+        public boolean apply(Suggestion input) {
+            return input != null;
+        }
+    };
+
+    private final Iterable<ModelPath> availablePaths;
+
+    public ModelPathSuggestionProvider(Iterable<ModelPath> availablePaths) {
+        this.availablePaths = availablePaths;
+    }
+
+    @ThreadSafe
+    private static class Suggestion implements Comparable<Suggestion> {
+
+        private static final Transformer<ModelPath, Suggestion> EXTRACT_PATH = new Transformer<ModelPath, Suggestion>() {
+            public ModelPath transform(Suggestion original) {
+                return original.path;
+            }
+        };
+
+        private final int distance;
+        private final ModelPath path;
+
+        private Suggestion(int distance, ModelPath path) {
+            this.distance = distance;
+            this.path = path;
+        }
+
+        public int compareTo(Suggestion o) {
+            int distanceDifference = distance - o.distance;
+            if (distanceDifference == 0) {
+                return path.toString().compareTo(o.path.toString());
+            } else {
+                return distanceDifference;
+            }
+        }
+    }
+
+    public List<ModelPath> transform(final ModelPath unavailable) {
+        Iterable<Suggestion> suggestions = Iterables.transform(availablePaths, new Function<ModelPath, Suggestion>() {
+            public Suggestion apply(ModelPath available) {
+                int distance = StringUtils.getLevenshteinDistance(unavailable.toString(), available.toString());
+                boolean suggest = distance <= Math.min(3, unavailable.toString().length() / 2);
+                if (suggest) {
+                    return new Suggestion(distance, available);
+                } else {
+                    // avoid excess creation of Suggestion objects
+                    return null;
+                }
+            }
+        });
+
+        suggestions = Iterables.filter(suggestions, REMOVE_NULLS);
+        List<Suggestion> sortedSuggestions = CollectionUtils.sort(suggestions);
+        return CollectionUtils.collect(sortedSuggestions, Suggestion.EXTRACT_PATH);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistry.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistry.java
new file mode 100644
index 0000000..a9ac62a
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistry.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.api.Nullable;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.type.ModelType;
+
+public interface ModelRegistry extends ModelRegistrar {
+
+    /**
+     * Get the fully defined model element at the given path as the given type.
+     * <p>
+     * No attempt to mutate the returned object should be made.
+     *
+     * @param path the path for the node
+     * @param type the type to project the node as
+     * @param <T> the type to project the node as
+     * @return the node as the given type
+     */
+    public <T> T realize(ModelPath path, ModelType<T> type);
+
+    /**
+     * Get the fully defined model element at the given path.
+     * <p>
+     * No attempt to mutate the returned object should be made.
+     *
+     * @param path the path for the node
+     * @return the node, or null if no such element.
+     */
+    @Nullable
+    ModelNode realizeNode(ModelPath path);
+
+    /**
+     * Get the fully defined model element at the given path as the given type, if present.
+     * <p>
+     * No attempt to mutate the returned object should be made.
+     *
+     * @param path the path for the node
+     * @param type the type to project the node as
+     * @param <T> the type to project the node as
+     * @return the node as the given type or null if no such element.
+     */
+    @Nullable
+    <T> T find(ModelPath path, ModelType<T> type);
+
+    /**
+     * Returns the node at the given path at the desired state, if it exists.
+     * <p>
+     * If there is no known node at that path, {@code null} is returned.
+     * <p>
+     * If the node exists but is at a later state than the requested state an exception will be thrown.
+     * If the node is at an earlier state it will be irrevocably transitioned to the desired state and returned.
+     * If it is at the desired state it is returned.
+     *
+     * @param path the path for the node
+     * @param state the desired node state
+     * @return the node at the desired state, or null if node is unknown
+     */
+    @Nullable
+    public ModelNode atState(ModelPath path, ModelNode.State state);
+
+    /**
+     * Returns the node at the given path at the desired state or later, if it exists.
+     * <p>
+     * If there is no known node at that path, {@code null} is returned.
+     * <p>
+     * If the node is at an earlier state than desired it will be irrevocably transitioned to the desired state and returned.
+     * If it is at the desired state or later it is returned.
+     *
+     * @param path the path for the node
+     * @param state the desired node state
+     * @return the node at the desired state, or null if node is unknown
+     */
+    @Nullable
+    public ModelNode atStateOrLater(ModelPath path, ModelNode.State state);
+
+    public ModelNode.State state(ModelPath path);
+
+    void remove(ModelPath path);
+
+    @Override
+    ModelRegistry replace(ModelCreator newCreator);
+
+    @Override
+    ModelRegistry createOrReplace(ModelCreator newCreator);
+
+    /**
+     * Attempts to bind the references of all model rules known at this point in time.
+     * <p>
+     * This method effectively validates that all references bind (i.e. all rules are executable).
+     * It should be called when the model registry is at some kind of logical checkpoint, in that it is reasonable
+     * to expect that all rules have been discovered.
+     * <p>
+     * However, it does not prevent rules from being added after being called.
+     * This is necessary as mutation rules can add rules etc.
+     * As such, this method can be called multiple times.
+     * Subsequent invocations will bind the references of rules added since the previous invocation.
+     * <p>
+     * If any reference cannot successfully bind, an exception will be thrown.
+     *
+     * @throws UnboundModelRulesException if there are unbindable references
+     */
+    void bindAllReferences() throws UnboundModelRulesException;
+
+    @Override
+    ModelRegistry create(ModelCreator creator);
+
+    @Override
+    <T> ModelRegistry configure(ModelActionRole role, ModelAction<T> action);
+
+    ModelRegistry apply(Class<? extends RuleSource> rules);
+
+    @Nullable
+    ModelNode node(ModelPath path);
+
+    /**
+     * Resets the state of the model registry, discarding all ephemeral state.
+     *
+     * This method also allows rules that were already added to be added again.
+     * All nodes that are known at the time this method is called are effectively frozen WRT rules.
+     */
+    // TODO Better name for this method?
+    void prepareForReuse();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistryScope.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistryScope.java
new file mode 100644
index 0000000..e06dcd7
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/ModelRegistryScope.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.model.internal.registry;
+
+public interface ModelRegistryScope {
+
+    ModelRegistry getModelRegistry();
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/MutatorRuleBinder.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/MutatorRuleBinder.java
new file mode 100644
index 0000000..f09b0ff
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/MutatorRuleBinder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.model.internal.core.ModelAction;
+import org.gradle.model.internal.core.ModelActionRole;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+
+import java.util.Collection;
+
+public class MutatorRuleBinder<T> extends RuleBinder {
+
+    private ModelBinding<T> subjectBinding;
+    private final ModelReference<T> subjectReference;
+    private final ModelActionRole role;
+    private final ModelAction<T> action;
+
+    public MutatorRuleBinder(ModelReference<T> subjectReference, ModelActionRole role, ModelAction<T> action, ModelPath scope, Collection<RuleBinder> binders) {
+        super(action.getInputs(), action.getDescriptor(), scope, binders);
+        this.subjectReference = subjectReference;
+        this.role = role;
+        this.action = action;
+    }
+
+    public ModelActionRole getRole() {
+        return role;
+    }
+
+    public ModelAction<T> getAction() {
+        return action;
+    }
+
+    public ModelReference<T> getSubjectReference() {
+        return subjectReference;
+    }
+
+    public ModelBinding<T> getSubjectBinding() {
+        return subjectBinding;
+    }
+
+    public void bindSubject(ModelNodeInternal modelNode) {
+        assert this.subjectBinding == null;
+        this.subjectBinding = RuleBinder.bind(subjectReference, modelNode);
+        maybeFire();
+    }
+
+    @Override
+    public boolean isBound() {
+        return subjectBinding != null && super.isBound();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/OneOfTypeBinderCreationListener.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/OneOfTypeBinderCreationListener.java
new file mode 100644
index 0000000..0f8e31c
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/OneOfTypeBinderCreationListener.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.model.InvalidModelRuleException;
+import org.gradle.model.ModelRuleBindingException;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.report.AmbiguousBindingReporter;
+import org.gradle.model.internal.type.ModelType;
+
+class OneOfTypeBinderCreationListener extends BinderCreationListener {
+    private final Action<? super ModelNodeInternal> bindAction;
+    private ModelPath boundTo;
+    private ModelRuleDescriptor boundToCreator;
+    private final ModelPath scope;
+
+    public OneOfTypeBinderCreationListener(ModelRuleDescriptor descriptor, ModelReference<?> reference, ModelPath scope, boolean writable, Action<? super ModelNodeInternal> bindAction) {
+        super(descriptor, reference, writable);
+        this.bindAction = bindAction;
+        this.scope = scope;
+    }
+
+    @Nullable
+    @Override
+    public ModelPath matchParent() {
+        return null;
+    }
+
+    @Override
+    public ModelPath matchScope() {
+        return scope;
+    }
+
+    @Nullable
+    @Override
+    public ModelType<?> matchType() {
+        return reference.getType();
+    }
+
+    public boolean onCreate(ModelNodeInternal node) {
+        ModelRuleDescriptor creatorDescriptor = node.getDescriptor();
+        ModelPath path = node.getPath();
+        if (boundTo != null) {
+            throw new InvalidModelRuleException(descriptor, new ModelRuleBindingException(
+                    new AmbiguousBindingReporter(reference, boundTo, boundToCreator, path, creatorDescriptor).asString()
+            ));
+        } else {
+            bindAction.execute(node);
+            boundTo = path;
+            boundToCreator = creatorDescriptor;
+            return false; // don't unregister listener, need to keep listening for other potential bindings
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/PathBinderCreationListener.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/PathBinderCreationListener.java
new file mode 100644
index 0000000..5b57ffd
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/PathBinderCreationListener.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.model.InvalidModelRuleException;
+import org.gradle.model.ModelRuleBindingException;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelPromise;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.report.IncompatibleTypeReferenceReporter;
+
+class PathBinderCreationListener extends BinderCreationListener {
+    private final Action<? super ModelNodeInternal> bindAction;
+    private final ModelPath path;
+
+    public PathBinderCreationListener(ModelRuleDescriptor descriptor, ModelReference<?> reference, ModelPath scope, boolean writable, Action<? super ModelNodeInternal> bindAction) {
+        super(descriptor, reference, writable);
+        this.bindAction = bindAction;
+        this.path = scope.descendant(reference.getPath());
+    }
+
+    @Nullable
+    @Override
+    public ModelPath matchPath() {
+        return path;
+    }
+
+    public boolean onCreate(ModelNodeInternal node) {
+        ModelRuleDescriptor creatorDescriptor = node.getDescriptor();
+        ModelPromise promise = node.getPromise();
+        if (isTypeCompatible(promise)) {
+            bindAction.execute(node);
+            return true; // bound by type and path, stop listening
+        } else {
+            throw new InvalidModelRuleException(descriptor, new ModelRuleBindingException(
+                    IncompatibleTypeReferenceReporter.of(creatorDescriptor, promise, reference, writable).asString()
+            ));
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/RuleBinder.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/RuleBinder.java
new file mode 100644
index 0000000..5cceb47
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/RuleBinder.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+ at NotThreadSafe
+public abstract class RuleBinder {
+
+    private final ModelRuleDescriptor descriptor;
+    private final List<? extends ModelReference<?>> inputReferences;
+    private final ModelPath scope;
+    private final Collection<RuleBinder> binders;
+
+    private boolean bindingInputs;
+    private int inputsBound;
+    private List<ModelBinding<?>> inputBindings;
+
+    public RuleBinder(List<? extends ModelReference<?>> inputReferences, ModelRuleDescriptor descriptor, ModelPath scope, Collection<RuleBinder> binders) {
+        this.inputReferences = inputReferences;
+        this.descriptor = descriptor;
+        this.scope = scope;
+        this.binders = binders;
+        this.inputBindings = inputReferences.isEmpty() ? Collections.<ModelBinding<?>>emptyList() : Arrays.asList(new ModelBinding<?>[inputReferences.size()]); // fix size
+        if (!isBound()) {
+            binders.add(this);
+        }
+    }
+
+    // is binding then inputs for this binder in progress?
+    public boolean isBindingInputs() {
+        return bindingInputs;
+    }
+
+    public void setBindingInputs(boolean bindingInputs) {
+        this.bindingInputs = bindingInputs;
+    }
+
+    public List<? extends ModelReference<?>> getInputReferences() {
+        return inputReferences;
+    }
+
+    public ModelBinding<?> getSubjectBinding() {
+        return null;
+    }
+
+    public ModelReference<?> getSubjectReference() {
+        return null;
+    }
+
+    public List<ModelBinding<?>> getInputBindings() {
+        return inputBindings;
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    public ModelPath getScope() {
+        return scope;
+    }
+
+    public void bindInput(int i, ModelNodeInternal modelNode) {
+        assert this.inputBindings.get(i) == null;
+        assert inputsBound < inputBindings.size();
+        this.inputBindings.set(i, bind(inputReferences.get(i), modelNode));
+        ++inputsBound;
+        maybeFire();
+    }
+
+    protected void maybeFire() {
+        if (isBound()) {
+            binders.remove(this);
+        }
+    }
+
+    public boolean isBound() {
+        return inputsBound == inputReferences.size();
+    }
+
+    static <I> ModelBinding<I> bind(ModelReference<I> reference, ModelNodeInternal modelNode) {
+        return ModelBinding.of(reference, modelNode);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundModelRulesException.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundModelRulesException.java
new file mode 100644
index 0000000..13231c2
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundModelRulesException.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.model.internal.registry;
+
+import org.gradle.api.GradleException;
+import org.gradle.model.internal.report.unbound.UnboundRule;
+import org.gradle.model.internal.report.unbound.UnboundRulesReporter;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+public class UnboundModelRulesException extends GradleException {
+
+    private final List<? extends UnboundRule> rules;
+
+    public UnboundModelRulesException(List<? extends UnboundRule> rules) {
+        super(toMessage(rules));
+        this.rules = rules;
+    }
+
+    private static String toMessage(Iterable<? extends UnboundRule> rules) {
+        StringWriter string = new StringWriter();
+        PrintWriter writer = new PrintWriter(string);
+        writer.println("The following model rules are unbound:");
+        new UnboundRulesReporter(writer, "  ").reportOn(rules);
+        return string.toString();
+    }
+
+    public List<? extends UnboundRule> getRules() {
+        return rules;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundRulesProcessor.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundRulesProcessor.java
new file mode 100644
index 0000000..f4911ea
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/registry/UnboundRulesProcessor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Transformer;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.report.unbound.UnboundRule;
+import org.gradle.model.internal.report.unbound.UnboundRuleInput;
+import org.gradle.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+ at ThreadSafe
+public class UnboundRulesProcessor {
+
+    private final Iterable<? extends RuleBinder> binders;
+    private final Transformer<? extends Collection<? extends ModelPath>, ? super ModelPath> suggestionsProvider;
+
+    public UnboundRulesProcessor(Iterable<? extends RuleBinder> binders, Transformer<? extends Collection<? extends ModelPath>, ? super ModelPath> suggestionsProvider) {
+        this.binders = binders;
+        this.suggestionsProvider = suggestionsProvider;
+    }
+
+    public List<? extends UnboundRule> process() {
+        List<UnboundRule> unboundRules = new ArrayList<UnboundRule>();
+        for (RuleBinder binder : binders) {
+            UnboundRule.Builder builder = UnboundRule.descriptor(binder.getDescriptor().toString());
+
+            ModelPath scope = binder.getScope();
+
+            if (binder.getSubjectReference() != null) {
+                ModelBinding<?> binding = binder.getSubjectBinding();
+                ModelReference<?> reference = binder.getSubjectReference();
+                UnboundRuleInput.Builder inputBuilder = toInputBuilder(binding, reference, scope);
+                if (scope != ModelPath.ROOT) {
+                    inputBuilder.scope(scope.toString());
+                }
+                builder.mutableInput(inputBuilder);
+            }
+
+            for (int i = 0; i < binder.getInputReferences().size(); ++i) {
+                ModelBinding<?> binding = binder.getInputBindings().get(i);
+                ModelReference<?> reference = binder.getInputReferences().get(i);
+                builder.immutableInput(toInputBuilder(binding, reference, binder.getScope()));
+            }
+
+            unboundRules.add(builder.build());
+        }
+        return unboundRules;
+    }
+
+    private UnboundRuleInput.Builder toInputBuilder(ModelBinding<?> binding, ModelReference<?> reference, ModelPath scope) {
+        UnboundRuleInput.Builder builder = UnboundRuleInput.type(reference.getType());
+        ModelPath path;
+        if (binding != null) {
+            builder.bound();
+            path = binding.getNode().getPath();
+        } else {
+            path = reference.getPath();
+            if (path != null) {
+                path = scope.descendant(path);
+                builder.suggestions(CollectionUtils.stringize(suggestionsProvider.transform(path)));
+            }
+        }
+        if (path != null) {
+            builder.path(path);
+        }
+        builder.description(reference.getDescription());
+        return builder;
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/report/AmbiguousBindingReporter.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/AmbiguousBindingReporter.java
new file mode 100644
index 0000000..fe57283
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/AmbiguousBindingReporter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.util.CollectionUtils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Comparator;
+import java.util.List;
+
+ at ThreadSafe
+public class AmbiguousBindingReporter {
+
+    private final static String INDENT = "  ";
+    private static final Comparator<Provider> PROVIDER_COMPARATOR = new Comparator<Provider>() {
+        public int compare(Provider o1, Provider o2) {
+            return o1.getPath().compareTo(o2.getPath());
+        }
+    };
+
+    private final String referenceType;
+    private final String referenceDescription;
+    private final List<Provider> providers;
+
+    public static class Provider {
+        private final String description;
+        private final String path;
+
+        public Provider(String path, String description) {
+            this.description = description;
+            this.path = path;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getPath() {
+            return path;
+        }
+    }
+
+    public AmbiguousBindingReporter(ModelReference<?> reference, ModelPath path1, ModelRuleDescriptor creator1, ModelPath path2, ModelRuleDescriptor creator2) {
+        this(reference.getType().toString(), reference.getDescription(), ImmutableList.of(
+                new Provider(path1.toString(), creator1.toString()),
+                new Provider(path2.toString(), creator2.toString())
+        ));
+    }
+
+    public AmbiguousBindingReporter(String referenceType, String referenceDescription, List<Provider> providers) {
+        this.referenceType = referenceType;
+        this.referenceDescription = referenceDescription;
+        this.providers = CollectionUtils.sort(providers, PROVIDER_COMPARATOR);
+    }
+
+    public String asString() {
+        StringWriter string = new StringWriter();
+        writeTo(new PrintWriter(string));
+        return string.toString();
+    }
+
+    public void writeTo(PrintWriter writer) {
+        //"type-only model reference of type '%s'%s is ambiguous as multiple model elements are available for this type:%n  %s (created by %s)%n  %s (created by %s)",
+        writer.print("Type-only model reference of type ");
+        writer.print(referenceType);
+        if (referenceDescription != null) {
+            writer.print(" (");
+            writer.print(referenceDescription);
+            writer.print(")");
+        }
+        writer.println(" is ambiguous as multiple model elements are available for this type:");
+
+        boolean first = true;
+        for (Provider provider : providers) {
+            if (!first) {
+                writer.println();
+            }
+            writer.print(INDENT);
+            writer.print("- ");
+            writer.print(provider.getPath());
+            writer.print(" (created by: ");
+            writer.print(provider.getDescription());
+            writer.print(")");
+            first = false;
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/report/IncompatibleTypeReferenceReporter.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/IncompatibleTypeReferenceReporter.java
new file mode 100644
index 0000000..3704abe
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/IncompatibleTypeReferenceReporter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.core.ModelPromise;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+ at ThreadSafe
+public class IncompatibleTypeReferenceReporter {
+
+    private final static String INDENT = "  ";
+
+    private final String creator;
+    private final String path;
+    private final String type;
+    private final String description;
+    private final boolean writable;
+    private final Iterable<String> candidateTypes;
+
+    public IncompatibleTypeReferenceReporter(String creator, String path, String type, String description, boolean writable, Iterable<String> candidateTypes) {
+        this.creator = creator;
+        this.path = path;
+        this.type = type;
+        this.description = description;
+        this.writable = writable;
+        this.candidateTypes = candidateTypes;
+    }
+
+    public static IncompatibleTypeReferenceReporter of(ModelRuleDescriptor creator, ModelPromise promise, ModelReference<?> reference, boolean writable) {
+        ModelPath path = reference.getPath();
+        String pathString = path == null ? "«none»" : path.toString();
+        return new IncompatibleTypeReferenceReporter(
+                creator.toString(), pathString, reference.getType().toString(), reference.getDescription(), writable,
+                writable ? promise.getWritableTypeDescriptions() : promise.getReadableTypeDescriptions()
+        );
+    }
+
+    public String asString() {
+        StringWriter string = new StringWriter();
+        writeTo(new PrintWriter(string));
+        return string.toString();
+    }
+
+    public void writeTo(PrintWriter writer) {
+        //"type-only model reference of type '%s'%s is ambiguous as multiple model elements are available for this type:%n  %s (created by %s)%n  %s (created by %s)",
+        writer.print("Model reference to element '");
+        writer.print(path);
+        writer.print("' with type ");
+        writer.print(type);
+        if (description != null) {
+            writer.print(" (");
+            writer.print(description);
+            writer.print(")");
+        }
+        writer.println(" is invalid due to incompatible types.");
+        writer.print("This element was created by ");
+        writer.print(creator);
+        writer.print(" and can be ");
+        writer.print(writable ? "mutated" : "read");
+        writer.println(" as the following types:");
+        boolean first = true;
+        for (String candidateType : candidateTypes) {
+            if (!first) {
+                writer.println();
+            }
+            writer.print(INDENT);
+            writer.print("- ");
+            writer.print(candidateType);
+            first = false;
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRule.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRule.java
new file mode 100644
index 0000000..d76fe5f
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRule.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report.unbound;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.ThreadSafe;
+
+import java.io.File;
+import java.util.List;
+
+ at ThreadSafe
+public class UnboundRule {
+
+    private final String descriptor;
+
+    private final ImmutableList<UnboundRuleInput> immutableInputs;
+    private final ImmutableList<UnboundRuleInput> mutableInputs;
+
+    private UnboundRule(String descriptor, ImmutableList<UnboundRuleInput> immutableInputs, ImmutableList<UnboundRuleInput> mutableInputs) {
+        this.descriptor = descriptor;
+        this.immutableInputs = immutableInputs;
+        this.mutableInputs = mutableInputs;
+    }
+
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    public List<? extends UnboundRuleInput> getImmutableInputs() {
+        return immutableInputs;
+    }
+
+    public List<? extends UnboundRuleInput> getMutableInputs() {
+        return mutableInputs;
+    }
+
+    public static Builder descriptor(String descriptor) {
+        return new Builder(descriptor);
+    }
+
+    public static Builder descriptor(String descriptor, File location, int line, int column) {
+        return new Builder(String.format("%s @ build file '%s' line %d, column %d", descriptor, location.getAbsolutePath(), line, column));
+    }
+
+    @NotThreadSafe
+    public static class Builder {
+
+        private String descriptor;
+        private final ImmutableList.Builder<UnboundRuleInput> immutableInputs = ImmutableList.builder();
+        private final ImmutableList.Builder<UnboundRuleInput> mutableInputs = ImmutableList.builder();
+
+        private Builder(String descriptor) {
+            this.descriptor = descriptor;
+        }
+
+        public Builder immutableInput(UnboundRuleInput.Builder inputBuilder) {
+            immutableInputs.add(inputBuilder.build());
+            return this;
+        }
+
+        public Builder mutableInput(UnboundRuleInput.Builder inputBuilder) {
+            mutableInputs.add(inputBuilder.build());
+            return this;
+        }
+
+        public UnboundRule build() {
+            return new UnboundRule(descriptor, immutableInputs.build(), mutableInputs.build());
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRuleInput.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRuleInput.java
new file mode 100644
index 0000000..32252b5
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRuleInput.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report.unbound;
+
+import com.google.common.collect.ImmutableList;
+import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+ at ThreadSafe
+public class UnboundRuleInput {
+
+    private final String path;
+    private final String type;
+    private final boolean bound;
+    private final String description;
+    private final ImmutableList<String> suggestedPaths;
+    private final String scope;
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public boolean isBound() {
+        return bound;
+    }
+
+    public ImmutableList<? extends String> getSuggestedPaths() {
+        return suggestedPaths;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    private UnboundRuleInput(String path, String type, boolean bound, ImmutableList<String> suggestedPaths, String description, String scope) {
+        this.path = path;
+        this.type = type;
+        this.bound = bound;
+        this.suggestedPaths = suggestedPaths;
+        this.description = description;
+        this.scope = scope;
+    }
+
+    public static Builder type(String type) {
+        return new Builder(type);
+    }
+
+    public static Builder type(Class<?> type) {
+        return type(type.getName());
+    }
+
+    public static Builder type(ModelType<?> type) {
+        return type(type.toString());
+    }
+
+    @NotThreadSafe
+    public static class Builder {
+
+        private String path;
+        private String type;
+        private boolean bound;
+        private ImmutableList.Builder<String> suggestedPaths = ImmutableList.builder();
+        private String description;
+        private String scope;
+
+        private Builder(String type) {
+            this.type = type;
+        }
+
+        public Builder path(String path) {
+            this.path = path;
+            return this;
+        }
+
+        public Builder path(ModelPath path) {
+            return path(path.toString());
+        }
+
+        public Builder bound() {
+            this.bound = true;
+            return this;
+        }
+
+        public Builder suggestions(Collection<? extends String> suggestedPaths) {
+            this.suggestedPaths.addAll(suggestedPaths);
+            return this;
+        }
+
+        public Builder suggestions(String... suggestedPath) {
+            return suggestions(Arrays.asList(suggestedPath));
+        }
+
+        public Builder description(String description) {
+            this.description = description;
+            return this;
+        }
+
+        public Builder scope(String scope) {
+            this.scope = scope;
+            return this;
+        }
+
+        public UnboundRuleInput build() {
+            return new UnboundRuleInput(path, type, bound, suggestedPaths.build(), description, scope);
+        }
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRulesReporter.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRulesReporter.java
new file mode 100644
index 0000000..30b428a
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/report/unbound/UnboundRulesReporter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report.unbound;
+
+import com.google.common.base.Joiner;
+import net.jcip.annotations.NotThreadSafe;
+
+import java.io.PrintWriter;
+
+ at NotThreadSafe
+public class UnboundRulesReporter {
+
+    private final PrintWriter writer;
+    private final String prefix;
+    private final static String INDENT = "  ";
+
+    public UnboundRulesReporter(PrintWriter writer, String prefix) {
+        this.writer = writer;
+        this.prefix = prefix;
+    }
+
+    public void reportOn(Iterable<? extends UnboundRule> rules) {
+        boolean first = true;
+        for (UnboundRule rule : rules) {
+            if (!first) {
+                writer.println();
+            }
+            first = false;
+
+            writer.print(prefix);
+
+            writer.print(rule.getDescriptor());
+            if (rule.getMutableInputs().size() > 0) {
+                heading("Mutable:");
+                reportInputs(rule.getMutableInputs());
+            }
+            if (rule.getImmutableInputs().size() > 0) {
+                heading("Immutable:");
+                reportInputs(rule.getImmutableInputs());
+            }
+        }
+    }
+
+    private void reportInputs(Iterable<? extends UnboundRuleInput> inputs) {
+        for (UnboundRuleInput input : inputs) {
+            item();
+            writer.print(input.isBound() ? "+ " : "- ");
+            String path = input.getPath() == null ? "<unspecified>" : input.getPath();
+            writer.print(String.format("%s (%s)", path, input.getType()));
+            if (input.getDescription() != null) {
+                writer.print(String.format(" %s", input.getDescription()));
+            }
+            if (input.getPath() == null && input.getScope() != null) {
+                writer.print(String.format(" in scope of '%s'", input.getScope()));
+            }
+            if (input.getSuggestedPaths().size() > 0) {
+                writer.print(" - suggestions: ");
+                writer.print(Joiner.on(", ").join(input.getSuggestedPaths()));
+            }
+        }
+    }
+
+    private void item() {
+        writer.println();
+        writer.print(prefix);
+        writer.print(INDENT);
+        writer.print(INDENT);
+    }
+
+    private void heading(String heading) {
+        writer.println();
+        writer.print(prefix);
+        writer.print(INDENT);
+        writer.print(heading);
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ClassTypeWrapper.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ClassTypeWrapper.java
new file mode 100644
index 0000000..1742249
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ClassTypeWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.lang.ref.WeakReference;
+
+class ClassTypeWrapper implements TypeWrapper {
+    private final WeakReference<Class<?>> reference;
+
+    public ClassTypeWrapper(Class<?> clazz) {
+        this.reference = new WeakReference<Class<?>>(clazz);
+    }
+
+    @Override
+    public Class<?> unwrap() {
+        return reference.get();
+    }
+
+    @Override
+    public String getRepresentation() {
+        return unwrap().getName();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelType.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelType.java
new file mode 100644
index 0000000..f87081d
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelType.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeResolver;
+import com.google.common.reflect.TypeToken;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.Cast;
+import org.gradle.util.CollectionUtils;
+
+import java.lang.reflect.*;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A type token, representing a resolved type.
+ * <p>
+ * Importantly, instances do not hold strong references to class objects.
+ * <p>
+ * Construct a type via one of the public static methods, or by creating an AIC…
+ * <pre>{@code
+ * ModelType<List<String>> type = new ModelType<List<String>>() {};
+ * }</pre>
+ */
+ at ThreadSafe
+public abstract class ModelType<T> {
+
+    public static final ModelType<Object> UNTYPED = ModelType.of(Object.class);
+
+    private final TypeWrapper wrapper;
+
+    private ModelType(TypeWrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+    protected ModelType() {
+        this.wrapper = wrap(new TypeToken<T>(getClass()) {
+        }.getType());
+    }
+
+    private TypeToken<T> getTypeToken() {
+        return Cast.uncheckedCast(TypeToken.of(getType()));
+    }
+
+    public static <T> ModelType<T> of(Class<T> clazz) {
+        return new Simple<T>(clazz);
+    }
+
+    public static <T> ModelType<T> returnType(Method method) {
+        return new Simple<T>(method.getGenericReturnType());
+    }
+
+    @Nullable
+    public static <T> ModelType<T> paramType(Method method, int i) {
+        Type[] parameterTypes = method.getGenericParameterTypes();
+        if (i < parameterTypes.length) {
+            return new Simple<T>(parameterTypes[i]);
+        } else {
+            return null;
+        }
+    }
+
+    public static <T> ModelType<T> typeOf(T instance) {
+        // TODO: should validate that clazz is of a non parameterized type
+        @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) instance.getClass();
+        return of(clazz);
+    }
+
+    public static ModelType<?> of(Type type) {
+        return Simple.typed(type);
+    }
+
+    public Class<? super T> getRawClass() {
+        return getTypeToken().getRawType();
+    }
+
+    public Class<T> getConcreteClass() {
+        return Cast.uncheckedCast(getRawClass());
+    }
+
+    public boolean isRawClassOfParameterizedType() {
+        Type type = getType();
+        return type instanceof Class && ((Class) type).getTypeParameters().length > 0;
+    }
+
+    public Type getType() {
+        return wrapper.unwrap();
+    }
+
+    public static ModelType<Object> untyped() {
+        return UNTYPED;
+    }
+
+    public boolean isParameterized() {
+        return getType() instanceof ParameterizedType;
+    }
+
+    public List<ModelType<?>> getTypeVariables() {
+        if (isParameterized()) {
+            Type[] typeArguments = ((ParameterizedType) getType()).getActualTypeArguments();
+            ImmutableList.Builder<ModelType<?>> builder = ImmutableList.builder();
+            for (Type typeArgument : typeArguments) {
+                builder.add(of(typeArgument));
+            }
+            return builder.build();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    @Nullable
+    public ModelType<? extends T> asSubclass(ModelType<?> modelType) {
+        if (isWildcard() || modelType.isWildcard()) {
+            return null;
+        }
+
+        Class<? super T> thisClass = getRawClass();
+        Class<?> otherClass = modelType.getRawClass();
+        boolean isSubclass = thisClass.isAssignableFrom(otherClass) && !thisClass.equals(otherClass);
+
+        if (isSubclass) {
+            @SuppressWarnings("unchecked") ModelType<? extends T> cast = (ModelType<? extends T>) modelType;
+            return cast;
+        } else {
+            return null;
+        }
+    }
+
+    public boolean isAssignableFrom(ModelType<?> modelType) {
+        return getTypeToken().isAssignableFrom(modelType.getTypeToken());
+    }
+
+    public boolean isWildcard() {
+        return getWildcardType() != null;
+    }
+
+    public ModelType<?> getUpperBound() {
+        WildcardType wildcardType = getWildcardType();
+        if (wildcardType == null) {
+            return null;
+        } else {
+            ModelType<?> upperBoundType = ModelType.of(wildcardType.getUpperBounds()[0]);
+            if (upperBoundType.equals(UNTYPED)) {
+                return null;
+            } else {
+                return upperBoundType;
+            }
+        }
+    }
+
+    public ModelType<?> getLowerBound() {
+        WildcardType wildcardType = getWildcardType();
+        if (wildcardType == null) {
+            return null;
+        } else {
+            Type[] lowerBounds = wildcardType.getLowerBounds();
+            if (lowerBounds.length == 0) {
+                return null;
+            } else {
+                return ModelType.of(lowerBounds[0]);
+            }
+        }
+    }
+
+    private WildcardType getWildcardType() {
+        Type type = getType();
+        if (type instanceof WildcardType) {
+            return (WildcardType) type;
+        } else {
+            return null;
+        }
+    }
+
+    public boolean isHasWildcardTypeVariables() {
+        if (isWildcard()) {
+            return true;
+        } else if (isParameterized()) {
+            for (ModelType<?> typeVariable : getTypeVariables()) {
+                if (typeVariable.isHasWildcardTypeVariables()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public List<Class<?>> getAllClasses() {
+        ImmutableList.Builder<Class<?>> builder = ImmutableList.builder();
+        addAllClasses(builder);
+        return builder.build();
+    }
+
+    private void addAllClasses(ImmutableCollection.Builder<Class<?>> builder) {
+        Type runtimeType = getType();
+        if (runtimeType instanceof Class) {
+            builder.add((Class<?>) runtimeType);
+        } else if (runtimeType instanceof ParameterizedType) {
+            builder.add((Class<?>) ((ParameterizedType) runtimeType).getRawType());
+            for (Type type : ((ParameterizedType) runtimeType).getActualTypeArguments()) {
+                ModelType.of(type).addAllClasses(builder);
+            }
+        } else if (runtimeType instanceof WildcardType) {
+            for (Type type : ((WildcardType) runtimeType).getLowerBounds()) {
+                ModelType.of(type).addAllClasses(builder);
+            }
+            for (Type type : ((WildcardType) runtimeType).getUpperBounds()) {
+                ModelType.of(type).addAllClasses(builder);
+            }
+        } else {
+            throw new IllegalArgumentException("Unable to deal with type " + runtimeType + " (" + runtimeType.getClass() + ")");
+        }
+    }
+
+    public String toString() {
+        return wrapper.getRepresentation();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ModelType)) {
+            return false;
+        }
+
+        ModelType<?> modelType = (ModelType<?>) o;
+
+        return getType().equals(modelType.getType());
+    }
+
+    @Override
+    public int hashCode() {
+        return getTypeToken().hashCode();
+    }
+
+    abstract public static class Builder<T> {
+        private TypeToken<T> typeToken;
+
+        public Builder() {
+            typeToken = new TypeToken<T>(getClass()) {
+            };
+        }
+
+        @SuppressWarnings("unchecked")
+        public <I> Builder<T> where(Parameter<I> parameter, ModelType<I> type) {
+            TypeResolver resolver = new TypeResolver().where(parameter.typeVariable, type.getTypeToken().getType());
+            typeToken = (TypeToken<T>) TypeToken.of(resolver.resolveType(typeToken.getType()));
+            return this;
+        }
+
+        public ModelType<T> build() {
+            return Simple.typed(typeToken.getType());
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    abstract public static class Parameter<T> {
+        private final TypeVariable<?> typeVariable;
+
+        public Parameter() {
+            Type type = new TypeToken<T>(getClass()) {
+            }.getType();
+            if (type instanceof TypeVariable<?>) {
+                this.typeVariable = (TypeVariable<?>) type;
+            } else {
+                throw new IllegalStateException("T for Parameter<T> MUST be a type variable");
+            }
+        }
+    }
+
+    public static abstract class Specs {
+        public static Spec<ModelType<?>> isAssignableTo(final ModelType<?> type) {
+            return new Spec<ModelType<?>>() {
+                public boolean isSatisfiedBy(ModelType<?> element) {
+                    return type.isAssignableFrom(element);
+                }
+            };
+        }
+
+        public static Spec<ModelType<?>> isAssignableFrom(final ModelType<?> type) {
+            return new Spec<ModelType<?>>() {
+                public boolean isSatisfiedBy(ModelType<?> element) {
+                    return element.isAssignableFrom(type);
+                }
+            };
+        }
+
+        public static Spec<ModelType<?>> isAssignableToAny(final Iterable<? extends ModelType<?>> types) {
+            return new Spec<ModelType<?>>() {
+                public boolean isSatisfiedBy(ModelType<?> element) {
+                    return CollectionUtils.any(types, isAssignableFrom(element));
+                }
+            };
+        }
+    }
+
+    private static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
+    private static final TypeWrapper[] EMPTY_TYPE_WRAPPER_ARRAY = new TypeWrapper[0];
+
+    private static TypeWrapper wrap(Type type) {
+        if (type == null) {
+            return NullTypeWrapper.INSTANCE;
+        } else if (type instanceof Class) {
+            return new ClassTypeWrapper((Class<?>) type);
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+            return new ParameterizedTypeWrapper(
+                    toWrappers(parameterizedType.getActualTypeArguments()),
+                    wrap(parameterizedType.getRawType()),
+                    wrap(parameterizedType.getOwnerType()),
+                    type.hashCode()
+            );
+        } else if (type instanceof WildcardType) {
+            WildcardType wildcardType = (WildcardType) type;
+            return new WildcardTypeWrapper(
+                    toWrappers(wildcardType.getUpperBounds()),
+                    toWrappers(wildcardType.getLowerBounds()),
+                    type.hashCode()
+            );
+        } else {
+            throw new IllegalArgumentException("cannot wrap type of type " + type.getClass());
+        }
+    }
+
+    static TypeWrapper[] toWrappers(Type[] types) {
+        if (types.length == 0) {
+            return EMPTY_TYPE_WRAPPER_ARRAY;
+        } else {
+            TypeWrapper[] wrappers = new TypeWrapper[types.length];
+            int i = 0;
+            for (Type type : types) {
+                wrappers[i++] = wrap(type);
+            }
+            return wrappers;
+        }
+    }
+
+    static Type[] unwrap(TypeWrapper[] wrappers) {
+        if (wrappers.length == 0) {
+            return EMPTY_TYPE_ARRAY;
+        } else {
+            Type[] types = new Type[wrappers.length];
+            int i = 0;
+            for (TypeWrapper wrapper : wrappers) {
+                types[i++] = wrapper.unwrap();
+            }
+            return types;
+        }
+    }
+
+    private static class Simple<T> extends ModelType<T> {
+        public static <T> ModelType<T> typed(Type type) {
+            return new Simple<T>(type);
+        }
+
+        public Simple(Type type) {
+            super(wrap(type));
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelTypes.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelTypes.java
new file mode 100644
index 0000000..2b57c30
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ModelTypes.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.util.Collection;
+
+public abstract class ModelTypes {
+
+    public static <T> ModelType<Collection<T>> collectionOf(Class<T> type) {
+        return new ModelType.Builder<Collection<T>>() {
+        }.where(new ModelType.Parameter<T>() {
+        }, ModelType.of(type)).build();
+    }
+
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/NullTypeWrapper.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/NullTypeWrapper.java
new file mode 100644
index 0000000..6f41a56
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/NullTypeWrapper.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.lang.reflect.Type;
+
+class NullTypeWrapper implements TypeWrapper {
+    final static TypeWrapper INSTANCE = new NullTypeWrapper();
+
+    @Override
+    public Type unwrap() {
+        return null;
+    }
+
+    @Override
+    public String getRepresentation() {
+        return "null";
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ParameterizedTypeWrapper.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ParameterizedTypeWrapper.java
new file mode 100644
index 0000000..c70ec2a
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/ParameterizedTypeWrapper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+class ParameterizedTypeWrapper implements ParameterizedType, TypeWrapper {
+
+    private final TypeWrapper[] actualTypeArguments;
+    private final TypeWrapper rawType;
+    private final TypeWrapper ownerType;
+    private final int hashCode;
+
+    public ParameterizedTypeWrapper(TypeWrapper[] actualTypeArguments, TypeWrapper rawType, TypeWrapper ownerType, int hashCode) {
+        this.actualTypeArguments = actualTypeArguments;
+        this.rawType = rawType;
+        this.ownerType = ownerType;
+        this.hashCode = hashCode;
+    }
+
+    @Override
+    public Type[] getActualTypeArguments() {
+        return ModelType.unwrap(actualTypeArguments);
+    }
+
+    @Override
+    public Type getRawType() {
+        return rawType.unwrap();
+    }
+
+    @Override
+    public Type getOwnerType() {
+        return ownerType.unwrap();
+    }
+
+    @Override
+    public Type unwrap() {
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof ParameterizedType) {
+            ParameterizedType that = (ParameterizedType) o;
+            if (this == that) {
+                return true;
+            } else {
+                Type ownerType = getOwnerType();
+                Type rawType = getRawType();
+                Type thatOwner = that.getOwnerType();
+                Type thatRawType = that.getRawType();
+                return (ownerType == null ? thatOwner == null : ownerType.equals(thatOwner))
+                        && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+                        && Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments());
+            }
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        Type ownerType = getOwnerType();
+        Class<?> rawType = (Class<?>) getRawType();
+        if (ownerType != null) {
+            if (ownerType instanceof Class) {
+                sb.append(((Class) ownerType).getName());
+            } else {
+                sb.append(ownerType.toString());
+            }
+
+            sb.append(".");
+
+            if (ownerType instanceof ParameterizedTypeWrapper) {
+                // Find simple name of nested type by removing the
+                // shared prefix with owner.
+                Class<?> ownerRaw = (Class<?>) ((ParameterizedTypeWrapper) ownerType).rawType.unwrap();
+                sb.append(rawType.getName().replace(ownerRaw.getName() + "$",
+                        ""));
+            } else {
+                sb.append(rawType.getName());
+            }
+        } else {
+            sb.append(rawType.getName());
+        }
+
+        Type[] actualTypeArguments = getActualTypeArguments();
+        if (actualTypeArguments != null && actualTypeArguments.length > 0) {
+            sb.append("<");
+            boolean first = true;
+            for (Type t : actualTypeArguments) {
+                if (!first) {
+                    sb.append(", ");
+                }
+                if (t instanceof Class) {
+                    sb.append(((Class) t).getName());
+                } else {
+                    sb.append(t.toString());
+                }
+                first = false;
+            }
+            sb.append(">");
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public String getRepresentation() {
+        return toString();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/TypeWrapper.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/TypeWrapper.java
new file mode 100644
index 0000000..64a4847
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/TypeWrapper.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.lang.reflect.Type;
+
+interface TypeWrapper {
+    Type unwrap();
+
+    String getRepresentation();
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/type/WildcardTypeWrapper.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/WildcardTypeWrapper.java
new file mode 100644
index 0000000..4cebd25
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/type/WildcardTypeWrapper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.type;
+
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+
+class WildcardTypeWrapper implements WildcardType, TypeWrapper {
+
+    private final TypeWrapper[] upperBounds;
+    private final TypeWrapper[] lowerBounds;
+    private final int hashCode;
+
+    public WildcardTypeWrapper(TypeWrapper[] upperBounds, TypeWrapper[] lowerBounds, int hashCode) {
+        this.upperBounds = upperBounds;
+        this.lowerBounds = lowerBounds;
+        this.hashCode = hashCode;
+    }
+
+    @Override
+    public Type[] getUpperBounds() {
+        return ModelType.unwrap(upperBounds);
+    }
+
+    @Override
+    public Type[] getLowerBounds() {
+        return ModelType.unwrap(lowerBounds);
+    }
+
+    @Override
+    public Type unwrap() {
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof WildcardType)) {
+            return false;
+        } else {
+            WildcardType var2 = (WildcardType) o;
+            return Arrays.equals(this.getLowerBounds(), var2.getLowerBounds()) && Arrays.equals(this.getUpperBounds(), var2.getUpperBounds());
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        Type[] lowerBounds = getLowerBounds();
+        Type[] bounds = lowerBounds;
+        StringBuilder sb = new StringBuilder();
+
+        if (lowerBounds.length > 0) {
+            sb.append("? super ");
+        } else {
+            Type[] upperBounds = getUpperBounds();
+            if (upperBounds.length > 0 && !upperBounds[0].equals(Object.class)) {
+                bounds = upperBounds;
+                sb.append("? extends ");
+            } else {
+                return "?";
+            }
+        }
+
+        assert bounds.length > 0;
+
+        boolean first = true;
+        for (Type bound : bounds) {
+            if (!first) {
+                sb.append(" & ");
+            }
+
+            first = false;
+            if (bound instanceof Class) {
+                sb.append(((Class) bound).getName());
+            } else {
+                sb.append(bound.toString());
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String getRepresentation() {
+        return toString();
+    }
+}
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/package-info.java b/subprojects/model-core/src/main/java/org/gradle/model/package-info.java
new file mode 100644
index 0000000..00a802f
--- /dev/null
+++ b/subprojects/model-core/src/main/java/org/gradle/model/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 operate upon the Gradle model.
+ */
+package org.gradle.model;
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/CollectionBuilderModelViewTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/CollectionBuilderModelViewTest.groovy
new file mode 100644
index 0000000..16cb147
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/CollectionBuilderModelViewTest.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.model.collection.internal
+
+import org.gradle.model.ModelViewClosedException
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.CollectionBuilderModelView
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class CollectionBuilderModelViewTest extends Specification {
+
+    def "cannot create items after view is closed"() {
+        def builder = Mock(CollectionBuilder)
+        def view = new CollectionBuilderModelView(ModelPath.path("things"), ModelType.of(CollectionBuilder), builder, new SimpleModelRuleDescriptor("foo"))
+        def instance = view.instance
+
+        when:
+        instance.create("foo")
+
+        then:
+        1 * builder.create("foo")
+
+        when:
+        view.close()
+
+        and:
+        instance.create("foo")
+
+        then:
+        thrown ModelViewClosedException
+
+        // assume other methods are implemented in the same way
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/DefaultCollectionBuilderTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/DefaultCollectionBuilderTest.groovy
new file mode 100644
index 0000000..70267e7
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/DefaultCollectionBuilderTest.groovy
@@ -0,0 +1,842 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal
+
+import org.gradle.api.Named
+import org.gradle.api.PolymorphicDomainObjectContainer
+import org.gradle.api.internal.ClosureBackedAction
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.model.*
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.*
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.registry.UnboundModelRulesException
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+
+class DefaultCollectionBuilderTest extends Specification {
+
+    def type = new ModelType<NamedThing>() {}
+
+    class NamedThing implements Named {
+        String name
+        String other
+    }
+
+    class SpecialNamedThing extends NamedThing implements Special {
+    }
+
+    def containerPath = ModelPath.path("container")
+    def containerType = new ModelType<PolymorphicDomainObjectContainer<NamedThing>>() {}
+    def collectionBuilderType = new ModelType<CollectionBuilder<NamedThing>>() {}
+    def registry = new ModelRegistryHelper()
+    def container = new DefaultPolymorphicDomainObjectContainer<NamedThing>(NamedThing, DirectInstantiator.INSTANCE, { it.getName() })
+
+    def setup() {
+        BridgedCollections.dynamicTypes(registry, containerPath, "container", containerType, containerType, ModelType.of(NamedThing), container, Named.Namer.forType(NamedThing), BridgedCollections.itemDescriptor("container"))
+        container.registerFactory(NamedThing) {
+            NamedThing.newInstance(name: it)
+        }
+        container.registerFactory(SpecialNamedThing) { SpecialNamedThing.newInstance(name: it) }
+    }
+
+    void mutate(@DelegatesTo(CollectionBuilder) Closure<? super CollectionBuilder<NamedThing>> action) {
+        def mutator = Stub(ModelAction)
+        mutator.subject >> ModelReference.of(containerPath, new ModelType<CollectionBuilder<NamedThing>>() {})
+        mutator.descriptor >> new SimpleModelRuleDescriptor("foo")
+        mutator.execute(*_) >> { new ClosureBackedAction<NamedThing>(action).execute(it[1]) }
+
+        registry.configure(ModelActionRole.Mutate, mutator)
+    }
+
+    void realize() {
+        registry.realizeNode(containerPath)
+    }
+
+    def "can define an item with name"() {
+        when:
+        mutate { create("foo") }
+        realize()
+
+        then:
+        container.getByName("foo") != null
+        registry.realize(containerPath.child("foo"), ModelType.of(NamedThing)) == container.getByName("foo")
+    }
+
+    def "does not eagerly create item"() {
+        when:
+        mutate {
+            create("foo")
+            create("bar")
+        }
+
+        then:
+        container.isEmpty()
+
+        when:
+        realize()
+
+        then:
+        container.getByName("bar")
+    }
+
+    def "can define item with custom type"() {
+        when:
+        mutate { create("foo", SpecialNamedThing) }
+        realize()
+
+        then:
+        container.getByName("foo") instanceof SpecialNamedThing
+    }
+
+    def "can define item using filtered collection"() {
+        when:
+        mutate {
+            withType(SpecialNamedThing).create("foo")
+            withType(NamedThing).create("bar")
+        }
+        realize()
+
+        then:
+        container.getByName("foo") instanceof SpecialNamedThing
+        container.getByName("bar").class == NamedThing
+    }
+
+    def "fails when using filtered collection to define item of type that is not assignable to collection item type"() {
+        when:
+        mutate {
+            withType(String).create("foo")
+        }
+        realize()
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause instanceof IllegalArgumentException
+        e.cause.message == "Cannot create an item of type java.lang.String as this is not a subtype of $NamedThing.name."
+    }
+
+    def "can register config rules for item"() {
+        when:
+        mutate {
+            create("foo") {
+                other = "changed"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "changed"
+    }
+
+    def "can register config rule and type for item"() {
+        when:
+        mutate {
+            create("foo", SpecialNamedThing) {
+                other = "changed"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "changed"
+    }
+
+    def "can query collection size"() {
+        when:
+        mutate {
+            assert size() == 0
+            assert it.isEmpty()
+
+            create("a")
+            create("b")
+
+            assert size() == 2
+            assert !isEmpty()
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).size() == 2
+    }
+
+    def "can query filtered collection size"() {
+        when:
+        mutate {
+            create("a")
+            create("b", SpecialNamedThing)
+
+            assert withType(SpecialNamedThing).size() == 1
+            assert withType(Special).size() == 1
+            assert withType(NamedThing).size() == 2
+            assert withType(String).size() == 0
+
+            assert !withType(SpecialNamedThing).isEmpty()
+            assert withType(String).isEmpty()
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).withType(SpecialNamedThing).size() == 1
+    }
+
+    def "can query collection membership"() {
+        when:
+        mutate {
+            assert !containsKey("a")
+            assert !containsKey(12)
+
+            create("a")
+            create("b")
+
+            assert it.containsKey("a")
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).containsKey("a")
+    }
+
+    def "can query filtered collection membership"() {
+        when:
+        mutate {
+            assert !withType(NamedThing).containsKey("a")
+            assert !withType(Integer).containsKey(12)
+
+            create("a")
+            create("b", SpecialNamedThing)
+
+            assert withType(Object).containsKey("a")
+            assert withType(NamedThing).containsKey("a")
+            assert !withType(SpecialNamedThing).containsKey("a")
+            assert !withType(Special).containsKey("a")
+            assert !withType(String).containsKey("a")
+
+            assert withType(Object).containsKey("b")
+            assert withType(NamedThing).containsKey("b")
+            assert withType(SpecialNamedThing).containsKey("b")
+            assert withType(Special).containsKey("b")
+            assert !withType(String).containsKey("b")
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).withType(SpecialNamedThing).containsKey("b")
+    }
+
+    def "can query collection keys"() {
+        when:
+        mutate {
+            assert keySet().isEmpty()
+
+            create("a")
+            create("b")
+
+            assert keySet() as List == ["a", "b"]
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).keySet() as List == ["a", "b"]
+    }
+
+    def "can query filtered collection keys"() {
+        when:
+        mutate {
+            assert withType(NamedThing).keySet().isEmpty()
+            assert withType(String).keySet().isEmpty()
+
+            create("b", SpecialNamedThing)
+            create("a")
+
+            assert withType(NamedThing).keySet() as List == ["a", "b"]
+            assert withType(SpecialNamedThing).keySet() as List == ["b"]
+            assert withType(Special).keySet() as List == ["b"]
+            assert withType(String).keySet().isEmpty()
+        }
+
+        then:
+        registry.realize(containerPath, collectionBuilderType).withType(Special).keySet() as List == ["b"]
+    }
+
+    def "can register mutate rule for item with name"() {
+        when:
+        mutate {
+            named("foo") {
+                assert other == "original"
+                other = "changed"
+            }
+            create("foo") {
+                other = "original"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "changed"
+    }
+
+    def "can register mutate rule for item with name using filtered container"() {
+        when:
+        mutate {
+            withType(Object).named("foo") {
+                other += " Object"
+            }
+            withType(Special).named("foo") {
+                other += " Special"
+            }
+            withType(SpecialNamedThing).named("foo") {
+                other += " SpecialNamedThing"
+            }
+            create("foo", SpecialNamedThing) {
+                other = "types:"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "types: Object Special SpecialNamedThing"
+    }
+
+    def "fails when named item does not have view with appropriate type"() {
+        when:
+        mutate {
+            withType(String).named("foo") {
+            }
+            create("foo")
+        }
+        realize()
+
+        then:
+        InvalidModelRuleException e = thrown()
+        e.cause instanceof ModelRuleBindingException
+        e.cause.message.startsWith("Model reference to element 'container.foo' with type java.lang.String is invalid due to incompatible types.")
+    }
+
+    static class SetOtherToName extends RuleSource {
+        @Mutate
+        void set(NamedThing thing) {
+            thing.other = thing.name
+        }
+    }
+
+    /**
+     * This test documents the current behaviour, not necessarily the desired.
+     *
+     * Ideally, we'd get a failure here indicating that container item 'foo' is not String & NamedThing
+     */
+    def "rules targeting item of mismatched type are allowed"() {
+        when:
+        mutate {
+            withType(String).named("foo", SetOtherToName)
+            create("foo")
+        }
+        realize()
+
+        then:
+        registry.get(containerPath.child("foo")).other == "foo"
+    }
+
+    def "can register mutate rule for all items using filtered container"() {
+        when:
+        mutate {
+            withType(Named).all {
+                other += " Named"
+            }
+            withType(String).all {
+                other += " String"
+            }
+            withType(NamedThing).all {
+                other += " NamedThing"
+            }
+            withType(Special).all {
+                other += " Special"
+            }
+            withType(SpecialNamedThing).all {
+                other += " SpecialNamedThing"
+            }
+            create("foo") {
+                other = "types:"
+            }
+            create("bar", SpecialNamedThing) {
+                other = "types:"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "types: Named NamedThing"
+        container.getByName("bar").other == "types: Named NamedThing Special SpecialNamedThing"
+    }
+
+    def "can register mutate rule for all items"() {
+        when:
+        mutate {
+            all {
+                assert other == "original"
+                other = "changed"
+            }
+            create("foo") {
+                other = "original"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "changed"
+    }
+
+    def "can register mutate rule for all items with specific type"() {
+        when:
+        mutate {
+            withType(Named) {
+                other += " Named"
+            }
+            withType(String) {
+                other += " String"
+            }
+            withType(Special) {
+                other += " Special"
+            }
+            withType(SpecialNamedThing) {
+                other += " SpecialNamedThing"
+            }
+            create("foo") {
+                other = "foo:"
+            }
+            create("bar", SpecialNamedThing) {
+                other = "bar:"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "foo: Named"
+        container.getByName("bar").other == "bar: Named Special SpecialNamedThing"
+    }
+
+    def "can register defaults rule for all items"() {
+        when:
+        mutate {
+            all {
+                other += " all{}"
+            }
+            create("foo") {
+                other += " create()"
+            }
+            beforeEach {
+                other = "beforeEach{}"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "beforeEach{} create() all{}"
+    }
+
+    def "can register defaults rule for all items with type"() {
+        when:
+        mutate {
+            beforeEach(Named) {
+                other = "Named"
+            }
+            beforeEach(String) {
+                other += " String"
+            }
+            beforeEach(Special) {
+                other += " Special"
+            }
+            beforeEach(SpecialNamedThing) {
+                other += " SpecialNamedThing"
+            }
+            create("foo") {
+                other += " create(foo)"
+            }
+            create("bar", SpecialNamedThing) {
+                other += " create(bar)"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "Named create(foo)"
+        container.getByName("bar").other == "Named Special SpecialNamedThing create(bar)"
+    }
+
+    def "can register finalize rule for all items"() {
+        when:
+        mutate {
+            all {
+                other += " all{}"
+            }
+            afterEach {
+                other += " afterEach{}"
+            }
+            create("foo") {
+                other = "create()"
+            }
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "create() all{} afterEach{}"
+    }
+
+    def "provides groovy DSL"() {
+        when:
+        mutate {
+            foo {
+                assert other == "original"
+                other = "changed"
+            }
+            foo(NamedThing) {
+                other = "original"
+            }
+            bar(SpecialNamedThing)
+        }
+        realize()
+
+        then:
+        container.getByName("foo").other == "changed"
+        container.getByName("bar") instanceof SpecialNamedThing
+    }
+
+    class MutableValue {
+        String value
+    }
+
+    class Bean {
+        String name
+        String value
+    }
+
+    class SpecialBean extends Bean {
+        String other
+    }
+
+    def "sensible error is thrown when trying to apply a class that does not extend RuleSource as a scoped rule"() {
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(MutableValue))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(ModelType.of(MutableValue))
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new MutableValue() }).build())
+                .collection("values", MutableValue, iRef)
+                .mutate {
+            it.descriptor("mutating elements").path "values" type cbType action { c ->
+                c.create("element")
+                c.named("element", Object)
+            }
+        }
+
+        when:
+        registry.realize(ModelPath.path("values"), ModelType.UNTYPED)
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause.class == InvalidModelRuleDeclarationException
+        e.cause.message == "Type java.lang.Object is not a valid model rule source: rule source classes must directly extend org.gradle.model.RuleSource"
+    }
+
+    static class ElementRules extends RuleSource {
+        @Mutate
+        void connectElementToInput(Bean element, String input) {
+            element.value = input
+        }
+    }
+
+    def "inputs of a rule from an inner source are not realised if the rule is not required"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+        def events = []
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) } as NamedEntityInstantiator).build())
+                .create("input", "input") { events << "input created" }
+                .collection("beans", Bean, iRef)
+                .mutate {
+            it.path "beans" type cbType action { c ->
+                events << "collection mutated"
+                c.create("element") { events << "$it.name created" }
+                c.named("element", ElementRules)
+            }
+
+        }
+
+        when:
+        registry.atState(ModelPath.path("beans"), ModelNode.State.SelfClosed)
+
+        then:
+        events == ["collection mutated"]
+
+        when:
+        registry.atState(ModelPath.path("beans"), ModelNode.State.GraphClosed)
+
+        then:
+        events == ["collection mutated", "element created", "input created"]
+    }
+
+    def "model rule with by-path dependency on non task related collection element's child that does exist passes validation"() {
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) } as NamedEntityInstantiator).build())
+                .createInstance("foo", new Bean())
+                .mutate {
+            it.path("foo").type(Bean).action("beans.element.mutable", ModelType.of(MutableValue)) { Bean subject, MutableValue input ->
+                subject.value = input.value
+            }
+        }
+        .collection("beans", Bean, iRef)
+                .mutate {
+            it.path "beans" type cbType action { c ->
+                c.create("element")
+            }
+        }
+        .mutate {
+            it.path "beans.element" node {
+                it.addLink(registry.instanceCreator("beans.element.mutable", new MutableValue(value: "bar")))
+            }
+        }
+
+        when:
+        registry.bindAllReferences()
+
+        then:
+        noExceptionThrown()
+    }
+
+    static class ByTypeSubjectBoundToScopeChildRule extends RuleSource {
+        @Mutate
+        void mutateScopeChild(MutableValue value) {
+            value.value = "foo"
+        }
+    }
+
+    def "model rule with by-type dependency on non task related collection element's child that does exist passes validation"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) } as NamedEntityInstantiator).build())
+                .collection("beans", Bean, iRef)
+                .mutate {
+            it.path "beans" type cbType action { c ->
+                c.create("element")
+                c.named("element", ByTypeSubjectBoundToScopeChildRule)
+            }
+        }
+        .mutate {
+            it.path "beans.element" node {
+                it.addLink(registry.instanceCreator("beans.element.mutable", new MutableValue()))
+            }
+        }
+
+        when:
+        registry.bindAllReferences()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "adding an unbound scoped rule for an element that is never created results in an error upon validation if the scope parent has been self closed"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) }).build())
+                .collection("beans", Bean, iRef)
+                .mutate {
+            it.path "beans" type cbType action { c ->
+                c.named("element", ElementRules)
+            }
+        }
+
+        when:
+        registry.atState(ModelPath.path("beans"), ModelNode.State.SelfClosed)
+        registry.bindAllReferences()
+
+        then:
+        UnboundModelRulesException e = thrown()
+        normaliseLineSeparators(e.message) == """The following model rules are unbound:
+  $ElementRules.name#connectElementToInput($Bean.name, $String.name)
+    Mutable:
+      - <unspecified> ($Bean.name) parameter 1 in scope of 'beans.element\'
+    Immutable:
+      - <unspecified> ($String.name) parameter 2"""
+    }
+
+    static class SetOther extends RuleSource {
+        @Mutate
+        void set(SpecialBean bean, String other) {
+            bean.other = other
+            bean.value = "changed"
+        }
+    }
+
+    def "can add rule source to all items of type"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+        NamedEntityInstantiator instantiator = { name, type ->
+            (type ?: Bean).newInstance(name: name)
+        }
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, instantiator).build())
+                .collection("beans", Bean, iRef)
+                .createInstance("s", "other")
+                .mutate {
+            it.path("beans").type(cbType).action { c ->
+                c.create("b1", Bean)
+                c.create("b2", Bean)
+                c.create("sb1", SpecialBean)
+                c.create("sb2", SpecialBean)
+                c.withType(SpecialBean, SetOther)
+            }
+        }
+
+        expect:
+        registry.node("s").state == ModelNode.State.Known
+
+        when:
+        registry.atState("beans", ModelNode.State.SelfClosed)
+
+        then:
+        registry.node("s").state == ModelNode.State.Known
+        registry.get("beans.b1", Bean).value != "changed"
+        registry.node("s").state == ModelNode.State.Known
+
+        when:
+        def sb2 = registry.get("beans.sb2", SpecialBean)
+
+        then:
+        sb2.other == "other"
+        registry.node("s").state == ModelNode.State.GraphClosed
+
+        when:
+        def sb1 = registry.get("beans.sb1", SpecialBean)
+
+        then:
+        sb1.other == "other"
+    }
+
+    static class SetProp extends RuleSource {
+        @Mutate
+        void m(@Path("foo") Bean bean) {}
+    }
+
+    def "when targeting by type, paths are interpreted relative to item"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+        NamedEntityInstantiator instantiator = { name, type ->
+            (type ?: Bean).newInstance(name: name)
+        }
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, instantiator).build())
+                .collection("beans", Bean, iRef)
+                .createInstance("s", "other")
+                .mutate {
+            it.path("beans").type(cbType).action { c ->
+                c.create("b1", Bean)
+                c.create("sb1", SpecialBean)
+                c.withType(SpecialBean, SetProp)
+            }
+        }
+
+        when:
+        registry.atState("beans", ModelNode.State.SelfClosed)
+        registry.get("beans.sb1", SpecialBean)
+        registry.bindAllReferences()
+
+        then:
+        UnboundModelRulesException e = thrown()
+        e.rules.size() == 1
+        e.rules.first().mutableInputs.first().path == "beans.sb1.foo"
+    }
+
+    static class SetValue extends RuleSource {
+        @Mutate
+        void set(Bean bean) {
+            bean.value = "changed"
+        }
+    }
+
+    def "when targeting by type, can have rule use more general type than target"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+        NamedEntityInstantiator instantiator = { name, type ->
+            (type ?: Bean).newInstance(name: name)
+        }
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, instantiator).build())
+                .collection("beans", Bean, iRef)
+                .createInstance("s", "other")
+                .mutate {
+            it.path("beans").type(cbType).action { c ->
+                c.create("sb1", SpecialBean)
+                c.withType(SpecialBean, SetValue)
+            }
+        }
+
+        when:
+        registry.atState("beans", ModelNode.State.SelfClosed)
+
+        then:
+        registry.get("beans.sb1", SpecialBean).value == "changed"
+    }
+
+    def "when targeting by type, can have rule use more specific type than target"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+        NamedEntityInstantiator instantiator = { name, type ->
+            (type ?: Bean).newInstance(name: name)
+        }
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, instantiator).build())
+                .collection("beans", Bean, iRef)
+                .createInstance("s", "other")
+                .mutate {
+            it.path("beans").type(cbType).action { c ->
+                c.create("sb1", SpecialBean)
+                c.withType(Bean, SetOther)
+            }
+        }
+
+        when:
+        registry.atState("beans", ModelNode.State.SelfClosed)
+
+        then:
+        registry.get("beans.sb1", SpecialBean).other == "other"
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/HasDependencies.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/HasDependencies.groovy
new file mode 100644
index 0000000..25f05c7
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/HasDependencies.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal
+
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at interface HasDependencies {
+}
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/Special.java b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/Special.java
new file mode 100644
index 0000000..34f23ca
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/collection/internal/Special.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.collection.internal;
+
+/**
+ * Out on its own due to http://jira.codehaus.org/browse/GROOVY-7010
+ */
+interface Special {
+}
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/InstanceBackedModelCreatorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/InstanceBackedModelCreatorTest.groovy
new file mode 100644
index 0000000..c311d1c
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/InstanceBackedModelCreatorTest.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.model.internal.core
+
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.registry.DefaultModelRegistry
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class InstanceBackedModelCreatorTest extends Specification {
+
+    def registry = new DefaultModelRegistry(null)
+
+    def "action is called"() {
+        when:
+        def foo = ModelReference.of("foo", List)
+        def bar = ModelReference.of("bar", List)
+
+        def descriptor = new SimpleModelRuleDescriptor("foo")
+
+        def fooList = []
+        def fooCreator = ModelCreators.bridgedInstance(foo, fooList).descriptor(descriptor).build()
+        registry.create(fooCreator)
+
+        def barList = []
+        def factory = Mock(org.gradle.internal.Factory) {
+            1 * create() >> barList
+        }
+        def barCreator = ModelCreators.unmanagedInstance(bar, factory).descriptor(descriptor).build()
+        registry.create(barCreator)
+
+        then:
+        !fooCreator.promise.canBeViewedAsReadOnly(ModelType.of(String))
+        !fooCreator.promise.canBeViewedAsWritable(ModelType.of(String))
+        fooCreator.promise.canBeViewedAsReadOnly(ModelType.of(List))
+        fooCreator.promise.canBeViewedAsWritable(ModelType.of(List))
+
+        registry.realize(foo.path, foo.type).is(fooList)
+        registry.realize(bar.path, bar.type).is(barList)
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathTest.groovy
new file mode 100644
index 0000000..4530e2b
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core
+
+import spock.lang.Specification
+
+class ModelPathTest extends Specification {
+    def "root path has no parent"() {
+        def path = ModelPath.ROOT
+
+        expect:
+        path.toString() == "<root>"
+        path.components == []
+        path.name == ""
+        path.depth == 0
+        !path.topLevel
+        path.parent == null
+        path.rootParent == null
+    }
+
+    def "path with single component is top level"() {
+        def path = ModelPath.path("p")
+
+        expect:
+        path.toString() == "p"
+        path.components == ["p"]
+        path.name == "p"
+        path.depth == 1
+        path.topLevel
+        path.parent == ModelPath.ROOT
+        path.rootParent == null
+        path == ModelPath.ROOT.child("p")
+    }
+
+    def "can create path with separator in one component"() {
+        def parent = ModelPath.path([name])
+        def path = ModelPath.path([name, name])
+        def child = ModelPath.path([name, name, name])
+
+        expect:
+        parent.components == [name]
+        parent.name == name
+        parent.toString() == name
+        parent.topLevel
+        parent.depth == 1
+        parent.parent == ModelPath.ROOT
+        parent.rootParent == null
+
+        path.components == [name, name]
+        path.name == name
+        path.toString() == "${name}.${name}"
+        path.parent == parent
+        path.rootParent == parent
+        path.name == name
+        path.depth == 2
+
+        child.components == [name, name, name]
+        child.name == name
+        child.parent == path
+        child.rootParent == parent
+        child.depth == 3
+
+        where:
+        name       | _
+        "."        | _
+        "..."      | _
+        "file.txt" | _
+    }
+
+    def "can create child with separator in name"() {
+        def path = ModelPath.path("parent")
+        def child = path.child(name)
+
+        expect:
+        child.parent == path
+        child.name == name
+        child.depth == 2
+        child.toString() == "parent.${name}"
+
+        where:
+        name       | _
+        "."        | _
+        "..."      | _
+        "file.txt" | _
+    }
+
+    def "direct child"() {
+        expect:
+        ModelPath.ROOT.isDirectChild(ModelPath.path("p"))
+        !ModelPath.ROOT.isDirectChild(ModelPath.ROOT)
+        !ModelPath.ROOT.isDirectChild(ModelPath.path("a.b"))
+
+        ModelPath.path("a.b").isDirectChild(ModelPath.path("a.b.c"))
+        !ModelPath.path("a.b").isDirectChild(ModelPath.path("a.a.b"))
+        !ModelPath.path("a.b").isDirectChild(ModelPath.path("a.b"))
+        !ModelPath.path("a.b").isDirectChild(ModelPath.path("a"))
+        !ModelPath.path("a.b").isDirectChild(null)
+    }
+
+    def "can create paths for indirect descendants"() {
+        expect:
+        ModelPath.ROOT.descendant(ModelPath.path("c.d")) == ModelPath.path("c.d")
+        ModelPath.path("a.b").descendant(ModelPath.path("c.d")) == ModelPath.path("a.b.c.d")
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathValidationTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathValidationTest.groovy
new file mode 100644
index 0000000..2ba18ca
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelPathValidationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.core
+
+import spock.lang.Specification
+
+class ModelPathValidationTest extends Specification {
+
+    def "validate name"() {
+        when:
+        ModelPath.validateName("")
+
+        then:
+        def e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "empty string"
+
+        when:
+        ModelPath.validateName("ü")
+
+        then:
+        e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "first character"
+
+        when:
+        ModelPath.validateName(" ")
+
+        then:
+        e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "first character"
+
+        when:
+        ModelPath.validateName("a ")
+
+        then:
+        e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "contains illegal"
+
+        when:
+        ModelPath.validateName("aü")
+
+        then:
+        e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "contains illegal"
+
+        when:
+        ModelPath.validateName("abü")
+
+        then:
+        e = thrown(ModelPath.InvalidNameException)
+        e.message =~ "contains illegal"
+
+        when:
+        ModelPath.validateName("Z9z")
+        ModelPath.validateName("abc")
+        ModelPath.validateName("aBC")
+        ModelPath.validateName("a10")
+        ModelPath.validateName("_")
+        ModelPath.validateName("_a")
+        ModelPath.validateName("__")
+        ModelPath.validateName("a_Z")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "validate model path"() {
+        when:
+        ModelPath.validatePath("foo. bar")
+
+        then:
+        def e = thrown ModelPath.InvalidPathException
+        e.cause instanceof ModelPath.InvalidNameException
+
+        when:
+        ModelPath.validatePath("foo.bar")
+
+        then:
+        noExceptionThrown()
+
+        when:
+        ModelPath.validatePath(".foo.bar")
+
+        then:
+        e = thrown ModelPath.InvalidPathException
+        e.message =~ "start with"
+
+        when:
+        ModelPath.validatePath("foo.bar.")
+
+        then:
+        e = thrown ModelPath.InvalidPathException
+        e.message =~ "end with"
+
+        when:
+        ModelPath.validatePath("")
+
+        then:
+        e = thrown ModelPath.InvalidPathException
+        e.message =~ "empty string"
+
+        when:
+        ModelPath.validatePath("-")
+
+        then:
+        e = thrown ModelPath.InvalidNameException
+        e.message =~ "illegal first character '-'"
+
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeJavaTest.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeJavaTest.java
new file mode 100644
index 0000000..2cc8255
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeJavaTest.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.model.internal.core;
+
+import org.gradle.model.internal.type.ModelType;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that need full static typing.
+ */
+public class ModelTypeJavaTest {
+
+    @Test
+    public void testBuildType() throws Exception {
+        assertEquals(new ModelType<Map<String, Integer>>() {}, buildMap(ModelType.of(String.class), ModelType.of(Integer.class)));
+    }
+
+    static <K, V> ModelType<Map<K, V>> buildMap(ModelType<K> k, ModelType<V> v) {
+        return new ModelType.Builder<Map<K, V>>() {}
+                .where(new ModelType.Parameter<K>() {}, k)
+                .where(new ModelType.Parameter<V>() {}, v)
+                .build();
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeTest.groovy
new file mode 100644
index 0000000..7cbe397
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/ModelTypeTest.groovy
@@ -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.model.internal.core
+
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class ModelTypeTest extends Specification {
+
+    def "represents type variables"() {
+        when:
+        def type = new ModelType<Map<String, Map<Integer, Float>>>() {}
+
+        then:
+        type.typeVariables[0] == ModelType.of(String)
+        type.typeVariables[1] == new ModelType<Map<Integer, Float>>() {}
+        type.typeVariables[1].typeVariables[0] == ModelType.of(Integer)
+        type.typeVariables[1].typeVariables[1] == ModelType.of(Float)
+    }
+
+    def "generic type compatibility"() {
+        def chars = new ModelType<List<CharSequence>>() {}
+        def strings = new ModelType<List<String>>() {}
+        def extendsChars = new ModelType<List<? extends CharSequence>>() {}
+        def superStrings = new ModelType<List<? super String>>() {}
+
+        expect:
+        !chars.isAssignableFrom(strings)
+
+        strings.isAssignableFrom(strings)
+        !strings.isAssignableFrom(extendsChars)
+        !strings.isAssignableFrom(superStrings)
+
+        chars.isAssignableFrom(chars)
+        !chars.isAssignableFrom(extendsChars)
+        !chars.isAssignableFrom(superStrings)
+
+        extendsChars.isAssignableFrom(chars)
+        extendsChars.isAssignableFrom(strings)
+        extendsChars.isAssignableFrom(extendsChars)
+        !extendsChars.isAssignableFrom(superStrings)
+
+        superStrings.isAssignableFrom(chars)
+        superStrings.isAssignableFrom(strings)
+        superStrings.isAssignableFrom(superStrings)
+        !superStrings.isAssignableFrom(extendsChars)
+    }
+
+    def m1(List<? extends String> strings) {}
+
+    def m2(List<? super String> strings) {}
+
+    def m3(List<?> anything) {}
+
+    def "wildcards"() {
+        def extendsString = ModelType.paramType(getClass().getDeclaredMethod("m1", List.class), 0).typeVariables[0]
+        def superString = ModelType.paramType(getClass().getDeclaredMethod("m2", List.class), 0).typeVariables[0]
+        def anything = ModelType.paramType(getClass().getDeclaredMethod("m3", List.class), 0).typeVariables[0]
+
+        expect:
+        extendsString.wildcard
+        superString.wildcard
+        anything.wildcard
+
+        extendsString.upperBound == ModelType.of(String)
+        extendsString.lowerBound == null
+
+        superString.upperBound == null
+        superString.lowerBound == ModelType.of(String)
+
+        anything.upperBound == null
+        anything.lowerBound == null
+    }
+
+    def "isSubclass"() {
+        def extendsString = ModelType.paramType(getClass().getDeclaredMethod("m1", List.class), 0).typeVariables[0]
+        def superString = ModelType.paramType(getClass().getDeclaredMethod("m2", List.class), 0).typeVariables[0]
+        def anything = ModelType.paramType(getClass().getDeclaredMethod("m3", List.class), 0).typeVariables[0]
+
+        expect:
+        !ModelType.of(String).asSubclass(ModelType.of(String))
+        ModelType.of(CharSequence).asSubclass(ModelType.of(String))
+        !ModelType.of(String).asSubclass(ModelType.of(CharSequence))
+        !anything.asSubclass(superString)
+        !superString.asSubclass(anything)
+        !superString.asSubclass(extendsString)
+        !extendsString.asSubclass(superString)
+    }
+
+    def "has wildcards"() {
+        expect:
+        !ModelType.of(String).hasWildcardTypeVariables
+        new ModelType<List<?>>() {}.hasWildcardTypeVariables
+        new ModelType<List<? extends CharSequence>>() {}.hasWildcardTypeVariables
+        new ModelType<List<? super CharSequence>>() {}.hasWildcardTypeVariables
+        !new ModelType<List<List<String>>>() {}.hasWildcardTypeVariables
+        new ModelType<List<List<?>>>() {}.hasWildcardTypeVariables
+        new ModelType<List<List<List<?>>>>() {}.hasWildcardTypeVariables
+        new ModelType<List<List<? super List<String>>>>() {}.hasWildcardTypeVariables
+    }
+
+    def "is raw of param type"() {
+        expect:
+        !new ModelType<List<?>>() {}.rawClassOfParameterizedType
+        !new ModelType<List<String>>() {}.rawClassOfParameterizedType
+        new ModelType<List>() {}.rawClassOfParameterizedType
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptorTest.groovy
new file mode 100644
index 0000000..e4140f0
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/core/rule/describe/MethodModelRuleDescriptorTest.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.model.internal.core.rule.describe
+
+import spock.lang.Specification
+
+class MethodModelRuleDescriptorTest extends Specification {
+
+    def "check description"() {
+        when:
+        def sb = new StringBuilder()
+        MethodModelRuleDescriptor.of(getClass(), method).describeTo(sb)
+
+        then:
+        sb.toString() == getClass().name + "#" + method + description
+
+        where:
+        method        | description
+        "noArgs"      | "()"
+        "oneArg"      | "(java.lang.String)"
+        "twoArgs"     | "(java.lang.String, java.lang.String)"
+        "genericArgs" | "(java.util.List<java.lang.String>, java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>)"
+    }
+
+    def noArgs() {}
+
+    def oneArg(String s1) {}
+
+    def twoArgs(String s1, String s2) {}
+
+    def genericArgs(List<String> list, Map<Integer, List<String>> map) {}
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/AnotherManagedWithPropertyOfInvalidManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/AnotherManagedWithPropertyOfInvalidManagedType.java
new file mode 100644
index 0000000..ba955de
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/AnotherManagedWithPropertyOfInvalidManagedType.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface AnotherManagedWithPropertyOfInvalidManagedType {
+    ParametrizedManaged<String> getInvalidManaged();
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ClassModelRuleSourceValidationTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ClassModelRuleSourceValidationTest.groovy
new file mode 100644
index 0000000..3c64646
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ClassModelRuleSourceValidationTest.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.model.internal.inspect
+
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.RuleSource
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ClassModelRuleSourceValidationTest extends Specification {
+
+    @Unroll
+    def "invalid #type - #reason"() {
+        when:
+        new ModelRuleExtractor([]).validate(type)
+
+        then:
+        def e = thrown(InvalidModelRuleDeclarationException)
+        def message = e.message
+        def actualReason = message.split(":", 2)[1].trim()
+        actualReason == reason
+
+        where:
+        type                               | reason
+        OuterClass.AbstractClass           | "class cannot be abstract"
+        OuterClass.AnInterface             | "must be a class, not an interface"
+        OuterClass.InnerInstanceClass      | "enclosed classes must be static and non private"
+        new RuleSource() {}.getClass()     | "enclosed classes must be static and non private"
+        OuterClass.HasTwoConstructors      | "cannot declare a constructor that takes arguments"
+        OuterClass.HasInstanceVar          | "field foo is not static final"
+        OuterClass.HasFinalInstanceVar     | "field foo is not static final"
+        OuterClass.HasNonFinalStaticVar    | "field foo is not static final"
+        OuterClass.DoesNotExtendRuleSource | "rule source classes must directly extend org.gradle.model.RuleSource"
+        OuterClass.HasSuperclass           | "rule source classes must directly extend org.gradle.model.RuleSource"
+    }
+
+    @Unroll
+    def "valid #type"() {
+        when:
+        new ModelRuleExtractor([]).validate(type)
+
+        then:
+        noExceptionThrown()
+
+        where:
+        type << [
+                OuterClass.InnerPublicStaticClass,
+                OuterClass.HasExplicitDefaultConstructor,
+                OuterClass.HasStaticFinalField
+        ]
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java
new file mode 100644
index 0000000..3819d65
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import java.util.List;
+
+interface HasStrings<T> {
+    List<T> strings();
+}
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedAnnotatedClass.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedAnnotatedClass.java
new file mode 100644
index 0000000..8f23c0d
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedAnnotatedClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public class ManagedAnnotatedClass {
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedPropertyOfInvalidManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedPropertyOfInvalidManagedType.java
new file mode 100644
index 0000000..4268879
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedPropertyOfInvalidManagedType.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ManagedWithNestedPropertyOfInvalidManagedType {
+    ManagedWithPropertyOfInvalidManagedType getManagedWithNestedInvalidManagedType();
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedReferenceOfInvalidManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedReferenceOfInvalidManagedType.java
new file mode 100644
index 0000000..a3813ed
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNestedReferenceOfInvalidManagedType.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ManagedWithNestedReferenceOfInvalidManagedType {
+    ManagedWithReferenceOfInvalidManagedType getManagedWithNestedInvalidManagedType();
+    void setManagedWithNestedInvalidManagedType(ManagedWithReferenceOfInvalidManagedType managedWithNestedManagedType);
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNonManageableParents.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNonManageableParents.java
new file mode 100644
index 0000000..1e932f3
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithNonManageableParents.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ManagedWithNonManageableParents extends ManagedWithPropertyOfInvalidManagedType, AnotherManagedWithPropertyOfInvalidManagedType {
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithPropertyOfInvalidManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithPropertyOfInvalidManagedType.java
new file mode 100644
index 0000000..1b82a73
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithPropertyOfInvalidManagedType.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ManagedWithPropertyOfInvalidManagedType {
+    ParametrizedManaged<String> getInvalidManaged();
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithReferenceOfInvalidManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithReferenceOfInvalidManagedType.java
new file mode 100644
index 0000000..62a8be3
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ManagedWithReferenceOfInvalidManagedType.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ManagedWithReferenceOfInvalidManagedType {
+    ParametrizedManaged<String> getInvalidManaged();
+    void setInvalidManaged(ParametrizedManaged<String> invalidManaged);
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleBindingTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleBindingTest.groovy
new file mode 100644
index 0000000..b621d68
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleBindingTest.groovy
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect
+
+import org.gradle.model.*
+import org.gradle.model.internal.core.UnmanagedModelProjection
+import org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.registry.DefaultModelRegistry
+import org.gradle.model.internal.report.AmbiguousBindingReporter
+import org.gradle.model.internal.report.IncompatibleTypeReferenceReporter
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+import spock.lang.Unroll
+
+/**
+ * Test the binding of rules by the registry.
+ */
+class ModelRuleBindingTest extends Specification {
+    def extractor = new ModelRuleExtractor(MethodModelRuleExtractors.coreExtractors(DefaultModelSchemaStore.instance, new DefaultModelCreatorFactory(DefaultModelSchemaStore.instance)))
+    def modelRegistry = new DefaultModelRegistry(extractor)
+
+    static class AmbiguousBindingsInOneSource extends RuleSource {
+        @Mutate
+        void m(String s) {
+
+        }
+
+        @Model
+        String s1() {
+            "foo"
+        }
+
+        @Model
+        String s2() {
+            "bar"
+        }
+    }
+
+
+    def "error message produced when unpathed reference matches more than one item"() {
+        when:
+        modelRegistry.apply(AmbiguousBindingsInOneSource).bindAllReferences()
+
+        then:
+        def e = thrown(InvalidModelRuleException)
+        e.descriptor == MethodModelRuleDescriptor.of(AmbiguousBindingsInOneSource, "m").toString()
+        def cause = e.cause as ModelRuleBindingException
+        def message = new AmbiguousBindingReporter(String.name, "parameter 1", [
+                new AmbiguousBindingReporter.Provider("s2", MethodModelRuleDescriptor.of(AmbiguousBindingsInOneSource, "s2").toString()),
+                new AmbiguousBindingReporter.Provider("s1", MethodModelRuleDescriptor.of(AmbiguousBindingsInOneSource, "s1").toString()),
+        ]).asString()
+
+        cause.message == message
+    }
+
+    static class ProvidesStringOne extends RuleSource {
+        @Model
+        String s1() {
+            "foo"
+        }
+    }
+
+    static class ProvidesStringTwo extends RuleSource {
+        @Model
+        String s2() {
+            "bar"
+        }
+    }
+
+    static class MutatesString extends RuleSource {
+        @Mutate
+        void m(String s) {
+
+        }
+    }
+
+    @Unroll
+    def "ambiguous binding is detected irrespective of discovery order - #order.simpleName"() {
+        when:
+        order.each {
+            modelRegistry.apply(it)
+        }
+        modelRegistry.bindAllReferences()
+
+        then:
+        def e = thrown(InvalidModelRuleException)
+        e.descriptor == MethodModelRuleDescriptor.of(MutatesString, "m").toString()
+
+        def cause = e.cause as ModelRuleBindingException
+        def message = new AmbiguousBindingReporter(String.name, "parameter 1", [
+                new AmbiguousBindingReporter.Provider("s2", MethodModelRuleDescriptor.of(ProvidesStringTwo, "s2").toString()),
+                new AmbiguousBindingReporter.Provider("s1", MethodModelRuleDescriptor.of(ProvidesStringOne, "s1").toString()),
+        ]).asString()
+
+        cause.message == message
+
+        where:
+        order << [ProvidesStringOne, ProvidesStringTwo, MutatesString].permutations()
+    }
+
+    static class MutatesS1AsInteger extends RuleSource {
+        @Mutate
+        void m(@Path("s1") Integer s1) {
+
+        }
+    }
+
+    @Unroll
+    def "incompatible writable type binding of mutate rule is detected irrespective of discovery order - #order.simpleName"() {
+        when:
+        order.each {
+            modelRegistry.apply(it)
+        }
+        modelRegistry.bindAllReferences()
+
+        then:
+        def e = thrown(InvalidModelRuleException)
+        e.descriptor == MethodModelRuleDescriptor.of(MutatesS1AsInteger, "m").toString()
+
+        def cause = e.cause as ModelRuleBindingException
+        def message = new IncompatibleTypeReferenceReporter(
+                MethodModelRuleDescriptor.of(ProvidesStringOne, "s1").toString(),
+                "s1",
+                Integer.name,
+                "parameter 1",
+                true,
+                [UnmanagedModelProjection.description(ModelType.of(String))]
+        ).asString()
+
+        cause.message == message
+
+        where:
+        order << [ProvidesStringOne, MutatesS1AsInteger].permutations()
+    }
+
+    static class ReadS1AsInteger extends RuleSource {
+        @Mutate
+        void m(Integer unbound, @Path("s1") Integer s1) {
+
+        }
+    }
+
+    @Unroll
+    def "incompatible readable type binding of mutate rule is detected irrespective of discovery order - #order.simpleName"() {
+        when:
+        order.each {
+            modelRegistry.apply(it)
+        }
+        modelRegistry.bindAllReferences()
+
+        then:
+        def e = thrown(InvalidModelRuleException)
+        e.descriptor == MethodModelRuleDescriptor.of(ReadS1AsInteger, "m").toString()
+
+        def cause = e.cause as ModelRuleBindingException
+        def message = new IncompatibleTypeReferenceReporter(
+                MethodModelRuleDescriptor.of(ProvidesStringOne, "s1").toString(),
+                "s1",
+                Integer.name,
+                "parameter 2",
+                false,
+                [UnmanagedModelProjection.description(ModelType.of(String))]
+        ).asString()
+
+        cause.message == message
+
+        where:
+        order << [ProvidesStringOne, ReadS1AsInteger].permutations()
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleExtractorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..7659c4c
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleExtractorTest.groovy
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect
+
+import org.codehaus.groovy.reflection.ClassInfo
+import org.gradle.model.*
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelCreators
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.manage.schema.extract.InvalidManagedModelElementTypeException
+import org.gradle.model.internal.registry.DefaultModelRegistry
+import org.gradle.model.internal.registry.ModelRegistry
+import org.gradle.model.internal.type.ModelType
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+import spock.lang.Unroll
+import spock.util.concurrent.PollingConditions
+
+import java.beans.Introspector
+
+class ModelRuleExtractorTest extends Specification {
+    ModelRegistry registry = new DefaultModelRegistry(null)
+    def extractor = new ModelRuleExtractor(MethodModelRuleExtractors.coreExtractors(DefaultModelSchemaStore.instance, new DefaultModelCreatorFactory(DefaultModelSchemaStore.instance)))
+
+    static class ModelThing {
+        final String name
+
+        ModelThing(String name) {
+            this.name = name
+        }
+    }
+
+    static class EmptyClass extends RuleSource {}
+
+    def "can inspect class with no rules"() {
+        expect:
+        extractor.extract(EmptyClass).empty
+    }
+
+    static class SimpleModelCreationRuleInferredName extends RuleSource {
+        @Model
+        static ModelThing modelPath() {
+            new ModelThing("foo")
+        }
+    }
+
+    List<ExtractedModelRule> extract(Class<?> source) {
+        extractor.extract(source)
+    }
+
+    void registerRules(Class<?> clazz) {
+        def rules = extract(clazz)
+        rules.each {
+            if (it.type == ExtractedModelRule.Type.CREATOR) {
+                registry.create(it.creator)
+            } else if (it.type == ExtractedModelRule.Type.ACTION) {
+                registry.configure(it.actionRole, it.action)
+            }
+        }
+    }
+
+    def "can inspect class with simple model creation rule"() {
+        when:
+        def rule = extract(SimpleModelCreationRuleInferredName).first()
+
+        then:
+        rule.type == ExtractedModelRule.Type.CREATOR
+        rule.creator.path.toString() == "modelPath"
+    }
+
+    static class ParameterizedModel extends RuleSource {
+        @Model
+        List<String> strings() {
+            Arrays.asList("foo")
+        }
+
+        @Model
+        List<? super String> superStrings() {
+            Arrays.asList("foo")
+        }
+
+        @Model
+        List<? extends String> extendsStrings() {
+            Arrays.asList("foo")
+        }
+
+        @Model
+        List<?> wildcard() {
+            Arrays.asList("foo")
+        }
+    }
+
+    def "can inspect class with model creation rule for paramaterized type"() {
+        when:
+        registerRules(ParameterizedModel)
+
+        then:
+        registry.realizeNode(ModelPath.path("strings")).promise.canBeViewedAsReadOnly(new ModelType<List<String>>() {})
+        registry.realizeNode(ModelPath.path("superStrings")).promise.canBeViewedAsReadOnly(new ModelType<List<? super String>>() {})
+        registry.realizeNode(ModelPath.path("extendsStrings")).promise.canBeViewedAsReadOnly(new ModelType<List<? extends String>>() {})
+        registry.realizeNode(ModelPath.path("wildcard")).promise.canBeViewedAsReadOnly(new ModelType<List<?>>() {})
+    }
+
+    static class HasGenericModelRule extends RuleSource {
+        @Model
+        static <T> List<T> thing() {
+            []
+        }
+    }
+
+    def "model creation rule cannot be generic"() {
+        when:
+        registerRules(HasGenericModelRule)
+
+        then:
+        def e = thrown(InvalidModelRuleDeclarationException)
+        e.message == "$HasGenericModelRule.name#thing() is not a valid model rule method: cannot have type variables (i.e. cannot be a generic method)"
+    }
+
+    static class HasMultipleRuleAnnotations extends RuleSource {
+        @Model
+        @Mutate
+        static String thing() {
+            ""
+        }
+    }
+
+    def "model rule method cannot be annotated with multiple rule annotations"() {
+        when:
+        registerRules(HasMultipleRuleAnnotations)
+
+        then:
+        def e = thrown(InvalidModelRuleDeclarationException)
+        e.message == "$HasMultipleRuleAnnotations.name#thing() is not a valid model rule method: can only be one of [annotated with @Model and returning a model element, @annotated with @Model and taking a managed model element, annotated with @Defaults, annotated with @Mutate, annotated with @Finalize, annotated with @Validate]"
+    }
+
+    static class ConcreteGenericModelType extends RuleSource {
+        @Model
+        static List<String> strings() {
+            []
+        }
+    }
+
+    def "type variables of model type are captured"() {
+        when:
+        registerRules(ConcreteGenericModelType)
+        def node = registry.realizeNode(new ModelPath("strings"))
+        def type = node.adapter.asReadOnly(new ModelType<List<String>>() {}, node, null).type
+
+        then:
+        type.parameterized
+        type.typeVariables[0] == ModelType.of(String)
+    }
+
+    static class ConcreteGenericModelTypeImplementingGenericInterface extends RuleSource implements HasStrings<String> {
+        @Model
+        List<String> strings() {
+            []
+        }
+    }
+
+    def "type variables of model type are captured when method is generic in interface"() {
+        when:
+        registerRules(ConcreteGenericModelTypeImplementingGenericInterface)
+        def node = registry.realizeNode(new ModelPath("strings"))
+        def type = node.adapter.asReadOnly(new ModelType<List<String>>() {}, node, null).type
+
+        then:
+        type.parameterized
+        type.typeVariables[0] == ModelType.of(String)
+    }
+
+    static class HasRuleWithIdentityCrisis extends RuleSource {
+        @Mutate
+        @Model
+        void foo() {}
+    }
+
+    def "rule cannot be of more than one type"() {
+        when:
+        registerRules(HasRuleWithIdentityCrisis)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class GenericMutationRule extends RuleSource {
+        @Mutate
+        <T> void mutate(T thing) {}
+    }
+
+    def "mutation rule cannot be generic"() {
+        when:
+        registerRules(GenericMutationRule)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class NonVoidMutationRule extends RuleSource {
+        @Mutate
+        String mutate(String thing) {}
+    }
+
+    def "only void is allowed as return type of a mutation rule"() {
+        when:
+        registerRules(NonVoidMutationRule)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class RuleWithEmptyInputPath extends RuleSource {
+        @Model
+        String create(@Path("") String thing) {}
+    }
+
+    def "path of rule input cannot be empty"() {
+        when:
+        registerRules(RuleWithEmptyInputPath)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class RuleWithInvalidInputPath extends RuleSource {
+        @Model
+        String create(@Path("!!!!") String thing) {}
+    }
+
+    def "path of rule input has to be valid"() {
+        when:
+        registerRules(RuleWithInvalidInputPath)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class MutationRules extends RuleSource {
+        @Mutate
+        static void mutate1(List<String> strings) {
+            strings << "1"
+        }
+
+        @Mutate
+        static void mutate2(List<String> strings) {
+            strings << "2"
+        }
+
+        @Mutate
+        static void mutate3(List<Integer> strings) {
+            strings << 3
+        }
+    }
+
+    // Not an exhaustive test of the mechanics of mutation rules, just testing the extraction and registration
+    def "mutation rules are registered"() {
+        given:
+        def path = new ModelPath("strings")
+        def type = new ModelType<List<String>>() {}
+
+        // Have to make the inputs exist so the binding can be inferred by type
+        // or, the inputs could be annotated with @Path
+        registry.create(ModelCreators.bridgedInstance(ModelReference.of(path, type), []).descriptor("strings").build())
+
+        when:
+        registerRules(MutationRules)
+
+
+        then:
+        def node = registry.realizeNode(path)
+        node.adapter.asReadOnly(type, node, null).instance.sort() == ["1", "2"]
+    }
+
+    static class MutationAndFinalizeRules extends RuleSource {
+        @Mutate
+        static void mutate3(List<Integer> strings) {
+            strings << 3
+        }
+
+        @Finalize
+        static void finalize1(List<String> strings) {
+            strings << "2"
+        }
+
+        @Mutate
+        static void mutate1(List<String> strings) {
+            strings << "1"
+        }
+    }
+
+    // Not an exhaustive test of the mechanics of finalize rules, just testing the extraction and registration
+    def "finalize rules are registered"() {
+        given:
+        def path = new ModelPath("strings")
+        def type = new ModelType<List<String>>() {}
+
+        // Have to make the inputs exist so the binding can be inferred by type
+        // or, the inputs could be annotated with @Path
+        registry.create(ModelCreators.bridgedInstance(ModelReference.of(path, type), []).descriptor("strings").build())
+
+        when:
+        registerRules(MutationAndFinalizeRules)
+
+        then:
+        def node = registry.realizeNode(path)
+        node.adapter.asReadOnly(type, node, null).instance == ["1", "2"]
+    }
+
+    def "methods are processed ordered by their to string representation"() {
+        when:
+        def stringListType = new ModelType<List<String>>() {}
+        def integerListType = new ModelType<List<Integer>>() {}
+
+        registry.create(ModelCreators.bridgedInstance(ModelReference.of(ModelPath.path("strings"), stringListType), []).descriptor("strings").build())
+        registry.create(ModelCreators.bridgedInstance(ModelReference.of(ModelPath.path("integers"), integerListType), []).descriptor("integers").build())
+
+        then:
+        extractor.extract(MutationAndFinalizeRules)*.action*.descriptor == [
+                MethodModelRuleDescriptor.of(MutationAndFinalizeRules, "finalize1"),
+                MethodModelRuleDescriptor.of(MutationAndFinalizeRules, "mutate1"),
+                MethodModelRuleDescriptor.of(MutationAndFinalizeRules, "mutate3")
+        ]
+
+    }
+
+    static class InvalidModelNameViaAnnotation extends RuleSource {
+        @Model(" ")
+        String foo() {
+            "foo"
+        }
+    }
+
+    def "invalid model name is not allowed"() {
+        when:
+        registerRules(InvalidModelNameViaAnnotation)
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    static class RuleSetCreatingAnInterfaceThatIsNotAnnotatedWithManaged extends RuleSource {
+        @Model
+        void bar(NonManaged foo) {
+        }
+    }
+
+    def "type of the first argument of void returning model definition has to be @Managed annotated"() {
+        when:
+        registerRules(RuleSetCreatingAnInterfaceThatIsNotAnnotatedWithManaged)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "$RuleSetCreatingAnInterfaceThatIsNotAnnotatedWithManaged.name#bar($NonManaged.name) is not a valid model rule method: a void returning model element creation rule has to take an instance of a managed type as the first argument"
+    }
+
+    static class RuleSourceCreatingAClassAnnotatedWithManaged extends RuleSource {
+        @Model
+        void bar(ManagedAnnotatedClass foo) {
+        }
+    }
+
+    def "type of the first argument of void returning model definition has to be a valid managed type"() {
+        when:
+        registerRules(RuleSourceCreatingAClassAnnotatedWithManaged)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "Declaration of model rule $RuleSourceCreatingAClassAnnotatedWithManaged.name#bar($ManagedAnnotatedClass.name) is invalid."
+        e.cause instanceof InvalidManagedModelElementTypeException
+        e.cause.message == "Invalid managed model type $ManagedAnnotatedClass.name: must be defined as an interface or an abstract class."
+    }
+
+    static class RuleSourceWithAVoidReturningNoArgumentMethod extends RuleSource {
+        @Model
+        void bar() {
+        }
+    }
+
+    def "void returning model definition has to take at least one argument"() {
+        when:
+        registerRules(RuleSourceWithAVoidReturningNoArgumentMethod)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "$RuleSourceWithAVoidReturningNoArgumentMethod.name#bar() is not a valid model rule method: a void returning model element creation rule has to take a managed model element instance as the first argument"
+    }
+
+    static class RuleSourceCreatingManagedWithNestedPropertyOfInvalidManagedType extends RuleSource {
+        @Model
+        void bar(ManagedWithNestedPropertyOfInvalidManagedType foo) {
+        }
+    }
+
+    static class RuleSourceCreatingManagedWithNestedReferenceOfInvalidManagedType extends RuleSource {
+        @Model
+        void bar(ManagedWithNestedReferenceOfInvalidManagedType foo) {
+        }
+    }
+
+    @Unroll
+    def "void returning model definition with for a type with a nested property of invalid managed type - #inspected.simpleName"() {
+        when:
+        registerRules(inspected)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "Declaration of model rule $inspected.name#bar($managedType.name) is invalid."
+        e.cause instanceof InvalidManagedModelElementTypeException
+        e.cause.message == TextUtil.toPlatformLineSeparators("""Invalid managed model type $invalidTypeName: cannot be a parameterized type.
+The type was analyzed due to the following dependencies:
+${managedType.name}
+  \\--- property 'managedWithNestedInvalidManagedType' (${nestedManagedType.name})
+    \\--- property 'invalidManaged' ($invalidTypeName)""")
+
+        where:
+        inspected                                                        | managedType                                    | nestedManagedType
+        RuleSourceCreatingManagedWithNestedPropertyOfInvalidManagedType  | ManagedWithNestedPropertyOfInvalidManagedType  | ManagedWithPropertyOfInvalidManagedType
+        RuleSourceCreatingManagedWithNestedReferenceOfInvalidManagedType | ManagedWithNestedReferenceOfInvalidManagedType | ManagedWithReferenceOfInvalidManagedType
+
+        invalidTypeName = "$ParametrizedManaged.name<$String.name>"
+    }
+
+    static class RuleSourceCreatingManagedWithNonManageableParent extends RuleSource {
+        @Model
+        void bar(ManagedWithNonManageableParents foo) {
+        }
+    }
+
+    def "error message produced when super type is not a manageable type indicates the original (sub) type"() {
+        when:
+        registerRules(RuleSourceCreatingManagedWithNonManageableParent)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "Declaration of model rule $RuleSourceCreatingManagedWithNonManageableParent.name#bar($ManagedWithNonManageableParents.name) is invalid."
+        e.cause instanceof InvalidManagedModelElementTypeException
+        e.cause.message == TextUtil.toPlatformLineSeparators("""Invalid managed model type $invalidTypeName: cannot be a parameterized type.
+The type was analyzed due to the following dependencies:
+${ManagedWithNonManageableParents.name}
+  \\--- property 'invalidManaged' declared by ${AnotherManagedWithPropertyOfInvalidManagedType.name}, ${ManagedWithPropertyOfInvalidManagedType.name} ($invalidTypeName)""")
+
+        where:
+        invalidTypeName = "$ParametrizedManaged.name<$String.name>"
+    }
+
+    static class HasRuleWithUncheckedCollectionBuilder extends RuleSource {
+        @Model
+        static ModelThing modelPath(CollectionBuilder foo) {
+            new ModelThing("foo")
+        }
+    }
+
+    def "error when trying to use collection builder without specifying type param"() {
+        when:
+        registerRules(HasRuleWithUncheckedCollectionBuilder)
+
+        then:
+        InvalidModelRuleDeclarationException e = thrown()
+        e.message == "$HasRuleWithUncheckedCollectionBuilder.name#modelPath(org.gradle.model.collection.CollectionBuilder) is not a valid model rule method: raw type org.gradle.model.collection.CollectionBuilder used for parameter 1 (all type parameters must be specified of parameterized type)"
+    }
+
+    def "extracted rules are cached"() {
+        when:
+        def fromFirstExtraction = extractor.extract(MutationRules)
+        def fromSecondExtraction = extractor.extract(MutationRules)
+
+        then:
+        fromFirstExtraction.is(fromSecondExtraction)
+    }
+
+    def "cache does not hold strong references"() {
+        given:
+        def cl = new GroovyClassLoader(getClass().classLoader)
+        def source = cl.parseClass('''
+            import org.gradle.model.*
+
+            class Rules extends RuleSource {
+                @Mutate
+                void mutate(String value) {
+                }
+            }
+        ''')
+
+        when:
+        extractor.extract(source)
+
+        then:
+        extractor.cache.size() == 1
+
+        when:
+        cl.clearCache()
+        forcefullyClearReferences(source)
+        source = null
+
+        then:
+        new PollingConditions(timeout: 10).eventually {
+            System.gc()
+            extractor.cache.cleanUp()
+            extractor.cache.size() == 0
+        }
+    }
+
+    private void forcefullyClearReferences(Class<?> clazz) {
+        // Remove soft references (dependent on Groovy internals)
+        def f = ClassInfo.getDeclaredField("globalClassSet")
+        f.setAccessible(true)
+        ClassInfo.ClassInfoSet globalClassSet = f.get(null) as ClassInfo.ClassInfoSet
+        globalClassSet.remove(clazz)
+
+        // Remove soft references
+        Introspector.flushFromCaches(clazz)
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleSourceDetectorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleSourceDetectorTest.groovy
new file mode 100644
index 0000000..0b29f28
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ModelRuleSourceDetectorTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect
+
+import org.gradle.model.RuleSource
+import spock.lang.Specification
+import spock.lang.Unroll
+import spock.util.concurrent.PollingConditions
+
+class ModelRuleSourceDetectorTest extends Specification {
+
+    private ModelRuleSourceDetector detector = new ModelRuleSourceDetector()
+
+    static class HasOneSource {
+        static class Source extends RuleSource {}
+
+        static class NotSource {}
+    }
+
+    static class HasTwoSources {
+        static class SourceOne extends RuleSource {}
+
+        static class SourceTwo extends RuleSource {}
+
+        static class NotSource {}
+    }
+
+    static class IsASource extends RuleSource {
+    }
+
+    static class SourcesNotDeclaredAlphabetically {
+        static class B extends RuleSource {}
+
+        static class A extends RuleSource {}
+    }
+
+    @Unroll
+    def "find model rule sources - #clazz"() {
+        expect:
+        detector.getDeclaredSources(clazz).toList() == expected
+
+        where:
+        clazz         | expected
+        String        | []
+        HasOneSource  | [HasOneSource.Source]
+        HasTwoSources | [HasTwoSources.SourceOne, HasTwoSources.SourceTwo]
+        IsASource     | [IsASource]
+    }
+
+    @Unroll
+    def "has model sources - #clazz"() {
+        expect:
+        detector.hasRules(clazz) == expected
+
+        where:
+        clazz        | expected
+        String       | false
+        HasOneSource | true
+        IsASource    | true
+    }
+
+    @Unroll
+    def "does not hold strong reference"() {
+        given:
+        def cl = new GroovyClassLoader(getClass().classLoader)
+        addClass(cl, impl)
+
+        expect:
+        detector.cache.size() == 1
+
+        when:
+        cl.clearCache()
+
+        then:
+        new PollingConditions(timeout: 10).eventually {
+            System.gc()
+            detector.cache.cleanUp()
+            detector.cache.size() == 0
+        }
+
+        where:
+        impl << [
+                "class SomeThing {}",
+                "class SomeThing extends ${RuleSource.name} {}",
+                "class SomeThing { static class Inner extends ${RuleSource.name} { } }",
+        ]
+    }
+
+    def "detected sources are returned ordered by class name"() {
+        expect:
+        detector.getDeclaredSources(SourcesNotDeclaredAlphabetically).toList() == [SourcesNotDeclaredAlphabetically.A, SourcesNotDeclaredAlphabetically.B]
+    }
+
+    private void addClass(GroovyClassLoader cl, String impl) {
+        def type = cl.parseClass(impl)
+        detector.getDeclaredSources(type)
+        type = null
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/MutationRuleExecutionOrderTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/MutationRuleExecutionOrderTest.groovy
new file mode 100644
index 0000000..e97b832
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/MutationRuleExecutionOrderTest.groovy
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect
+
+import org.gradle.model.Model
+import org.gradle.model.Mutate
+import org.gradle.model.Path
+import org.gradle.model.RuleSource
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.registry.DefaultModelRegistry
+import spock.lang.Specification
+
+class MutationRuleExecutionOrderTest extends Specification {
+    def extractor = new ModelRuleExtractor(MethodModelRuleExtractors.coreExtractors(DefaultModelSchemaStore.instance, new DefaultModelCreatorFactory(DefaultModelSchemaStore.instance)))
+    def modelRegistry = new ModelRegistryHelper(new DefaultModelRegistry(extractor))
+
+    static class MutationRecorder {
+        def mutations = []
+    }
+
+    static class ByPathRules extends RuleSource {
+        @Model
+        MutationRecorder recorder() {
+            new MutationRecorder()
+        }
+
+        @Mutate
+        void b(@Path("recorder") MutationRecorder recorder) {
+            recorder.mutations << "b"
+        }
+
+        @Mutate
+        void a(@Path("recorder") MutationRecorder recorder) {
+            recorder.mutations << "a"
+        }
+    }
+
+    def "mutation rules from the same plugin are applied in the order specified by their signatures"() {
+        when:
+        modelRegistry.apply(ByPathRules)
+
+        then:
+        modelRegistry.get("recorder", MutationRecorder).mutations == ["a", "b"]
+    }
+
+    static class MixedRules extends RuleSource {
+        @Model
+        MutationRecorder recorder() {
+            new MutationRecorder()
+        }
+
+        @Mutate
+        void b(@Path("recorder") MutationRecorder recorder) {
+            recorder.mutations << "b"
+        }
+
+        @Mutate
+        void a(MutationRecorder recorder) {
+            recorder.mutations << "a"
+        }
+    }
+
+    def "mutation rule application order is consistent if by type subject bound rules are used"() {
+        when:
+        modelRegistry.apply(MixedRules)
+
+        then:
+        modelRegistry.get("recorder", MutationRecorder).mutations == ["a", "b"]
+    }
+
+    static class MutationRulesWithInputs extends RuleSource {
+        @Model
+        MutationRecorder recorder() {
+            new MutationRecorder()
+        }
+
+        @Mutate
+        void b(MutationRecorder recorder, @Path("secondInput") String input) {
+            recorder.mutations << input
+        }
+
+        @Mutate
+        void a(MutationRecorder recorder, @Path("firstInput") String input) {
+            recorder.mutations << input
+        }
+    }
+
+    static class FirstInputCreationRule extends RuleSource {
+        @Model
+        String firstInput() {
+            "first"
+        }
+    }
+
+    static class SecondInputCreationRule extends RuleSource {
+        @Model
+        String secondInput() {
+            "second"
+        }
+    }
+
+    def "binding order does not affect mutation rule execution order"() {
+        when:
+        modelRegistry.apply(MutationRulesWithInputs)
+        modelRegistry.apply(SecondInputCreationRule)
+        modelRegistry.apply(FirstInputCreationRule)
+
+        then:
+        modelRegistry.get("recorder", MutationRecorder).mutations == ["first", "second"]
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/NonManaged.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/NonManaged.java
new file mode 100644
index 0000000..f92cac8
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/NonManaged.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+public interface NonManaged {
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/OuterClass.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/OuterClass.java
new file mode 100644
index 0000000..de706de
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/OuterClass.java
@@ -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.model.internal.inspect;
+
+import org.gradle.model.RuleSource;
+
+ at SuppressWarnings("UnusedDeclaration")
+public class OuterClass {
+    public static abstract class AbstractClass extends RuleSource {
+    }
+
+    public static interface AnInterface {
+    }
+
+    public class InnerInstanceClass extends RuleSource {
+    }
+
+    private class PrivateInnerStaticClass {
+    }
+
+    public static class HasSuperclass extends InnerPublicStaticClass {
+    }
+
+    public static class DoesNotExtendRuleSource {
+    }
+
+    public static class HasTwoConstructors extends RuleSource {
+        public HasTwoConstructors() {
+        }
+
+        public HasTwoConstructors(String arg) {
+        }
+    }
+
+    public static class HasInstanceVar extends RuleSource {
+        private String foo;
+    }
+
+    public static class HasFinalInstanceVar extends RuleSource {
+        private final String foo = null;
+    }
+
+    public static class HasNonFinalStaticVar extends RuleSource {
+        private static String foo;
+    }
+
+    public static class InnerPublicStaticClass extends RuleSource {
+    }
+
+    public static class HasExplicitDefaultConstructor extends RuleSource {
+        public HasExplicitDefaultConstructor() {
+        }
+    }
+
+    public static class HasStaticFinalField extends RuleSource {
+        private static final Object VALUE = null;
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ParametrizedManaged.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ParametrizedManaged.java
new file mode 100644
index 0000000..28bad0b
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/ParametrizedManaged.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.inspect;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface ParametrizedManaged<T> {
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/instance/ManagedProxyTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/instance/ManagedProxyTest.groovy
new file mode 100644
index 0000000..5bc7ba6
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/instance/ManagedProxyTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.instance
+
+import org.gradle.model.Managed
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class ManagedProxyTest extends Specification {
+
+    def factory = new ManagedProxyFactory()
+
+    @Managed
+    static private interface ManagedType {
+        ManagedType getSelf()
+    }
+
+    def "a useful type name is used in stacktrace for a generated managed model type"() {
+        given:
+        def proxy = factory.createProxy([get: { throw new RuntimeException("from state") }] as ModelElementState, DefaultModelSchemaStore.instance.getSchema(ModelType.of(ManagedType)))
+
+        when:
+        proxy.self
+
+        then:
+        RuntimeException e = thrown()
+        e.message == "from state"
+        e.stackTrace.any { it.className == ManagedType.name + "_Impl" && it.methodName == "getSelf" }
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/projection/ManagedSetModelProjectionTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/projection/ManagedSetModelProjectionTest.groovy
new file mode 100644
index 0000000..49df419
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/projection/ManagedSetModelProjectionTest.groovy
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.projection
+
+import org.gradle.api.internal.ClosureBackedAction
+import org.gradle.internal.BiAction
+import org.gradle.model.Managed
+import org.gradle.model.ModelViewClosedException
+import org.gradle.model.collection.ManagedSet
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.model.internal.core.ModelRuleExecutionException
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.inspect.DefaultModelCreatorFactory
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ManagedSetModelProjectionTest extends Specification {
+    @Managed
+    interface NamedThing {
+        String getName()
+
+        void setName(String name);
+
+        String getValue()
+
+        void setValue(String value)
+    }
+
+    def collectionPath = ModelPath.path("collection")
+    def collectionType = new ModelType<ManagedSet<NamedThing>>() {}
+    def schemaStore = DefaultModelSchemaStore.instance
+    def factory = new DefaultModelCreatorFactory(schemaStore)
+    def registry = new ModelRegistryHelper()
+    private ModelReference<ManagedSet<NamedThing>> reference = ModelReference.of(collectionPath, new ModelType<ManagedSet<NamedThing>>() {})
+
+    def setup() {
+        registry.create(
+                factory.creator(
+                        new SimpleModelRuleDescriptor("define collection"),
+                        collectionPath,
+                        schemaStore.getSchema(collectionType),
+                        [],
+                        { value, inputs -> } as BiAction)
+        )
+    }
+
+    void mutate(@DelegatesTo(ManagedSet) Closure<?> action) {
+        registry.mutate(reference, new ClosureBackedAction<>(action))
+        registry.realizeNode(collectionPath)
+    }
+
+    def "can define and query elements"() {
+        when:
+        mutate {
+            create { name = '1' }
+            create { name = '2' }
+        }
+
+        then:
+        def set = registry.realize(collectionPath, collectionType)
+        set*.name == ['1', '2']
+        set.toArray().collect { it.name } == ['1', '2']
+        set.toArray(new NamedThing[2]).collect { it.name } == ['1', '2']
+    }
+
+    def "reuses element views"() {
+        when:
+        mutate {
+            create { name = '1' }
+            create { name = '2' }
+        }
+
+        then:
+        def set = registry.realize(collectionPath, collectionType)
+        def e1 = set.find { it.name == '1' }
+        def e2 = set.find { it.name == '1' }
+        e1.is(e2)
+    }
+
+    def "can query set size"() {
+        when:
+        mutate {
+            create { name = '1' }
+            create { name = '2' }
+        }
+
+        then:
+        !registry.realize(collectionPath, collectionType).isEmpty()
+        registry.realize(collectionPath, collectionType).size() == 2
+    }
+
+    def "can query set membership"() {
+        when:
+        mutate {
+            create { name = '1' }
+            create { name = '2' }
+        }
+
+        then:
+        def set = registry.realize(collectionPath, collectionType)
+        set.contains(set.find { it.name == '1' })
+        !set.contains("green")
+        !set.contains({} as NamedThing)
+
+        set.containsAll(set)
+        set.containsAll(set as List)
+        set.containsAll(set.findAll { it.name == '1' })
+        !set.containsAll(["green"])
+        !set.containsAll([{} as NamedThing])
+    }
+
+    def "can configure children"() {
+        when:
+        mutate {
+            afterEach {
+                value += " after"
+            }
+            create { name = '1' }
+            beforeEach {
+                value = "before"
+            }
+            create { name = '2' }
+        }
+
+        then:
+        def set = registry.realize(collectionPath, collectionType).toList()
+        set[0].value == "before after"
+        set[1].value == "before after"
+    }
+
+    @Unroll
+    def "cannot configure children when used as an input - #method"() {
+        when:
+        registry.createInstance("things", []).mutate {
+            it.path("things").action(reference.path, reference.type, { things, set ->
+                set."$method" {
+
+                }
+            })
+        }
+
+        registry.get("things")
+
+        then:
+        def e = thrown ModelRuleExecutionException
+        e.cause instanceof ModelViewClosedException
+
+        where:
+        method << ["afterEach", "beforeEach"]
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStoreTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStoreTest.groovy
new file mode 100644
index 0000000..3c99f11
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/DefaultModelSchemaStoreTest.groovy
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.reflection.ClassInfo
+import org.gradle.model.Managed
+import org.gradle.model.collection.ManagedSet
+import org.gradle.model.internal.manage.schema.ModelStructSchema
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+import spock.lang.Unroll
+import spock.util.concurrent.PollingConditions
+
+import java.beans.Introspector
+
+class DefaultModelSchemaStoreTest extends Specification {
+
+    def store = new DefaultModelSchemaStore()
+
+    def "can get schema"() {
+        // intentionally use two different “instances” of the same type
+        def type1 = ModelType.of(SimpleManagedType)
+        def type2 = ModelType.of(SimpleManagedType)
+
+        expect:
+        store.getSchema(type1).is(store.getSchema(type2))
+    }
+
+    @Unroll
+    def "does not hold strong reference"() {
+        given:
+        def cl = new GroovyClassLoader(getClass().classLoader)
+        addClass(cl, impl)
+
+        expect:
+        store.cache.size() > 0
+
+        when:
+        cl.clearCache()
+
+        then:
+        new PollingConditions(timeout: 10).eventually {
+            System.gc()
+            store.cleanUp()
+            store.size() == 0
+        }
+
+        where:
+        impl << [
+                "class SomeThing {}",
+                "@${Managed.name} interface SomeThing { SomeThing getThing() }",
+                "@${Managed.name} interface SomeThing { ${ManagedSet.name}<SomeThing> getThings() }",
+                "@${Managed.name} interface SomeThing { @${Managed.name} static interface Child {}; ${ManagedSet.name}<Child> getThings() }",
+        ]
+    }
+
+    @Unroll
+    def "does not hold strong reference to a managed #type type"() {
+        given:
+        def modelType = ModelType.of(new GroovyClassLoader(getClass().classLoader).parseClass("@${Managed.name} $type ManagedType {}"))
+
+        when:
+        def schema = store.getSchema(modelType)
+
+        then:
+        store.cache.size() > 0
+
+        when:
+        forcefullyClearReferences(schema)
+
+        then:
+        new PollingConditions(timeout: 10).eventually {
+            System.gc()
+            store.cleanUp()
+            store.size() == 0
+            schema.managedImpl == null // collected too
+        }
+
+        where:
+        type << ["abstract class", "interface"]
+    }
+
+    @CompileStatic
+    // must be compile static to avoid call sites being created with soft class refs
+    private static void forcefullyClearReferences(ModelStructSchema schema) {
+        // Remove strong internal circular ref
+        (schema.type.rawClass.classLoader as GroovyClassLoader).clearCache()
+
+        // Remove soft references (dependent on Groovy internals)
+        def f = ClassInfo.getDeclaredField("globalClassSet")
+        f.setAccessible(true)
+        ClassInfo.ClassInfoSet globalClassSet = f.get(null) as ClassInfo.ClassInfoSet
+        globalClassSet.remove(schema.type.rawClass)
+        globalClassSet.remove(schema.managedImpl)
+
+        // Remove soft references
+        Introspector.flushFromCaches(schema.type.rawClass)
+        Introspector.flushFromCaches(schema.managedImpl)
+    }
+
+    def "canonicalizes introspection for different sites of generic type"() {
+        when:
+        def cl = new GroovyClassLoader()
+        addClass(cl, "@${Managed.name} interface Thing {}")
+        addClass(cl, "@${Managed.name} interface Container1 { ${ManagedSet.name}<Thing> getThings() }")
+        addClass(cl, "@${Managed.name} interface Container2 { ${ManagedSet.name}<Thing> getThings() }")
+
+        then:
+        store.cache.size() == 4
+    }
+
+    def "caches schema for different instances of same base type"() {
+        when:
+        def cl = new GroovyClassLoader()
+        addClass(cl, "@${Managed.name} interface Thing1 {}")
+        addClass(cl, "@${Managed.name} interface Thing2 {}")
+        addClass(cl, "@${Managed.name} interface Container1 { ${ManagedSet.name}<Thing1> getThings() }")
+        addClass(cl, "@${Managed.name} interface Container2 { ${ManagedSet.name}<Thing2> getThings() }")
+
+        then:
+        store.cache.size() == 6
+    }
+
+    private void addClass(GroovyClassLoader cl, String impl) {
+        def type = cl.parseClass(impl)
+        store.getSchema(ModelType.of(type))
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGeneratorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGeneratorTest.groovy
new file mode 100644
index 0000000..2d9f057
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ManagedProxyClassGeneratorTest.groovy
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract
+
+import org.gradle.model.internal.core.MutableModelNode
+import org.gradle.model.internal.manage.instance.ManagedInstance
+import org.gradle.model.internal.manage.instance.ModelElementState
+import spock.lang.Ignore
+import spock.lang.Specification
+
+class ManagedProxyClassGeneratorTest extends Specification {
+    static def generator = new ManagedProxyClassGenerator()
+    static classes = [:]
+
+    def "generates a proxy class for an interface"() {
+        expect:
+        def impl = newInstance(SomeType)
+        impl instanceof SomeType
+    }
+
+    def "mixes in ManagedInstance"() {
+        def node = Stub(MutableModelNode)
+        def state = Stub(ModelElementState) {
+            getBackingNode() >> node
+        }
+
+        expect:
+        def proxyClass = generate(SomeType)
+        def impl = proxyClass.newInstance(state)
+        impl instanceof ManagedInstance
+        impl.backingNode == node
+    }
+
+    def "mixes in toString() implementation that delegates to element state"() {
+        def state = Stub(ModelElementState) {
+            getDisplayName() >> "<display-name>"
+        }
+
+        expect:
+        def proxyClass = generate(SomeType)
+        def impl = proxyClass.newInstance(state)
+        impl.toString() == "<display-name>"
+    }
+
+    def "reports contract type rather than implementation class in groovy missing property error message"() {
+        given:
+        def impl = newInstance(SomeType)
+
+        when:
+        impl.unknown
+
+        then:
+        MissingPropertyException e = thrown()
+        e.message == "No such property: unknown for class: ${SomeType.name}"
+
+        when:
+        impl.unknown = '12'
+
+        then:
+        e = thrown()
+        e.message == "No such property: unknown for class: ${SomeType.name}"
+    }
+
+    @Ignore
+    def "reports contract type rather than implementation class when attempting to set read-only property"() {
+        given:
+        def impl = newInstance(SomeType)
+
+        when:
+        impl.readOnly = '12'
+
+        then:
+        ReadOnlyPropertyException e = thrown()
+        e.message == "Cannot set readonly property: readOnly for class: ${SomeType.name}"
+    }
+
+    def "reports contract type rather than implementation class when attempting to invoke unknown method"() {
+        given:
+        def impl = newInstance(SomeType)
+
+        when:
+        impl.unknown('12')
+
+        then:
+        MissingMethodException e = thrown()
+        e.message.startsWith("No signature of method: ${SomeType.name}.unknown() is applicable")
+    }
+
+    def "reports contract type rather than implementation class when attempting to invoke method with unsupported parameters"() {
+        given:
+        def impl = newInstance(SomeType)
+
+        when:
+        impl.setValue('12')
+
+        then:
+        MissingMethodException e = thrown()
+        e.message.startsWith("No signature of method: ${SomeType.name}.setValue() is applicable")
+    }
+
+    def newInstance(Class<?> type) {
+        def generated = generate(type)
+        return generated.newInstance(Stub(ModelElementState))
+    }
+
+    def generate(Class<?> type) {
+        def generated = classes[type]
+        if (generated == null) {
+            generated = generator.generate(type)
+            classes[type] = generated
+        }
+        return generated
+    }
+
+    interface SomeType {
+        Integer getValue()
+
+        void setValue(Integer value)
+
+        String getReadOnly()
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractorTest.groovy
new file mode 100644
index 0000000..7a05f56
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/ModelSchemaExtractorTest.groovy
@@ -0,0 +1,814 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract
+
+import org.gradle.internal.reflect.MethodDescription
+import org.gradle.model.Managed
+import org.gradle.model.Unmanaged
+import org.gradle.model.collection.ManagedSet
+import org.gradle.model.internal.manage.schema.ModelSchema
+import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache
+import org.gradle.model.internal.type.ModelType
+import org.gradle.util.TextUtil
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.util.regex.Pattern
+
+class ModelSchemaExtractorTest extends Specification {
+
+    def store = new ModelSchemaExtractor()
+    @Shared def cache = new ModelSchemaCache()
+
+    static interface NotAnnotatedInterface {}
+
+    def "unmanaged type"() {
+        expect:
+        extract(NotAnnotatedInterface).kind == ModelSchema.Kind.UNMANAGED
+    }
+
+    @Managed
+    static class EmptyStaticClass {}
+
+    def "must be interface"() {
+        expect:
+        fail EmptyStaticClass, "must be defined as an interface or an abstract class"
+    }
+
+    @Managed
+    static interface ParameterizedEmptyInterface<T> {}
+
+    def "cannot parameterize"() {
+        expect:
+        fail ParameterizedEmptyInterface, "cannot be a parameterized type"
+    }
+
+    @Managed
+    static interface NoGettersOrSetters {
+        void foo(String bar)
+    }
+
+    @Managed
+    static interface HasExtraNonPropertyMethods {
+        String getName()
+
+        void setName(String name)
+
+        void foo(String bar)
+    }
+
+    def "can only have getters and setters"() {
+        expect:
+        fail NoGettersOrSetters, Pattern.quote("only paired getter/setter methods are supported (invalid methods: ${MethodDescription.name("foo").returns(void.class).owner(NoGettersOrSetters).takes(String)})")
+        fail HasExtraNonPropertyMethods, Pattern.quote("nly paired getter/setter methods are supported (invalid methods: ${MethodDescription.name("foo").returns(void.class).owner(HasExtraNonPropertyMethods).takes(String)})")
+    }
+
+    @Managed
+    static interface OnlyGetter {
+        String getName()
+    }
+
+    def "must be symmetrical"() {
+        expect:
+        fail OnlyGetter, "read only property 'name' has non managed type java.lang.String, only managed types can be used"
+    }
+
+    @Managed
+    static interface SingleStringNameProperty {
+        String getName()
+
+        void setName(String name)
+    }
+
+    def "extract single property"() {
+        when:
+        def properties = extract(SingleStringNameProperty).properties
+
+        then:
+        properties.size() == 1
+        properties.name.type == ModelType.of(String)
+    }
+
+    @Managed
+    static interface GetterWithParams {
+        String getName(String name)
+
+        void setName(String name)
+
+    }
+
+    def "malformed getter"() {
+        expect:
+        fail GetterWithParams, "getter methods cannot take parameters"
+    }
+
+    @Managed
+    static interface NonVoidSetter {
+        String getName()
+
+        String setName(String name)
+    }
+
+    def "non void setter"() {
+        expect:
+        fail NonVoidSetter, "setter method must have void return type"
+    }
+
+    @Managed
+    static interface SetterWithExtraParams {
+        String getName()
+
+        void setName(String name, String otherName)
+    }
+
+    def "setter with extra params"() {
+        expect:
+        fail SetterWithExtraParams, "setter method must have exactly one parameter"
+    }
+
+    @Managed
+    static interface MisalignedSetterType {
+        String getName()
+
+        void setName(Object name)
+    }
+
+    def "misaligned setter type"() {
+        expect:
+        fail MisalignedSetterType, "setter method param must be of exactly the same type"
+    }
+
+    @Managed
+    static interface SetterOnly {
+        void setName(String name);
+    }
+
+    def "setter only"() {
+        expect:
+        fail SetterOnly, "only paired getter/setter methods are supported"
+    }
+
+    @Managed
+    static interface NonStringProperty {
+        Object getName()
+
+        void setName(Object name)
+    }
+
+    def "only selected unmanaged property types are allowed"() {
+        expect:
+        fail NonStringProperty, Pattern.quote("an unmanaged type")
+    }
+
+    @Managed
+    static interface BytePrimitiveProperty {
+        byte getByteProperty()
+
+        void setByteProperty(byte value)
+    }
+
+    def "byte property types are not allowed and there is no suggested replacement"() {
+        expect:
+        fail BytePrimitiveProperty, Pattern.quote("an unmanaged type")
+    }
+
+    @Unroll
+    def "boxed types are suggested when primitive types are being used - #primitiveType"() {
+        when:
+        def interfaceWithPrimitiveProperty = new GroovyClassLoader(getClass().classLoader).parseClass """
+            import org.gradle.model.Managed
+
+            @Managed
+            interface PrimitiveProperty {
+                $primitiveType.name getPrimitiveProperty()
+
+                void setPrimitiveProperty($primitiveType.name value)
+            }
+        """
+
+        then:
+        fail interfaceWithPrimitiveProperty, primitiveType, Pattern.quote("type is not supported, please use $boxedType.name instead")
+
+        where:
+        primitiveType | boxedType
+        boolean.class | Boolean
+        char.class    | Character
+        float.class   | Double
+        long.class    | Long
+        short.class   | Integer
+        int.class     | Integer
+        double.class  | Double
+    }
+
+    @Managed
+    static interface MultipleProps {
+        String getProp1();
+
+        void setProp1(String string);
+
+        String getProp2();
+
+        void setProp2(String string);
+
+        String getProp3();
+
+        void setProp3(String string);
+    }
+
+    def "multiple properties"() {
+        when:
+        def properties = extract(MultipleProps).properties.values()
+
+        then:
+        properties*.name == ["prop1", "prop2", "prop3"]
+        properties*.type == [ModelType.of(String)] * 3
+    }
+
+    @Managed
+    interface SelfReferencing {
+        SelfReferencing getSelf()
+    }
+
+    def "can extract self referencing type"() {
+        expect:
+        extract(SelfReferencing).properties.self.type == ModelType.of(SelfReferencing)
+    }
+
+    @Managed
+    interface A1 {
+        A1 getA();
+
+        B1 getB();
+
+        C1 getC();
+
+        D1 getD();
+    }
+
+    @Managed
+    interface B1 {
+        A1 getA();
+
+        B1 getB();
+
+        C1 getC();
+
+        D1 getD();
+    }
+
+    @Managed
+    interface C1 {
+        A1 getA();
+
+        B1 getB();
+
+        C1 getC();
+
+        D1 getD();
+    }
+
+    @Managed
+    interface D1 {
+        A1 getA();
+
+        B1 getB();
+
+        C1 getC();
+
+        D1 getD();
+    }
+
+    def "can extract incestuous nest"() {
+        expect:
+        extract(type).properties.a.type == extract(A1).type
+        extract(type).properties.b.type == extract(B1).type
+        extract(type).properties.c.type == extract(C1).type
+        extract(type).properties.d.type == extract(D1).type
+
+        where:
+        type << [A1, B1, C1, D1]
+    }
+
+    @Managed
+    static interface WithInheritedProperties extends SingleStringNameProperty {
+        Integer getCount()
+
+        void setCount(Integer count)
+    }
+
+    def "extracts inherited properties"() {
+        when:
+        def properties = extract(WithInheritedProperties).properties.values()
+
+        then:
+        properties*.name == ["count", "name"]
+    }
+
+    static interface SingleIntegerValueProperty {
+        Integer getValue()
+
+        void setValue(Integer count)
+    }
+
+    @Managed
+    static interface WithMultipleParents extends SingleStringNameProperty, SingleIntegerValueProperty {
+    }
+
+    def "extracts properties from multiple parents"() {
+        when:
+        def properties = extract(WithMultipleParents).properties.values()
+
+        then:
+        properties*.name == ["name", "value"]
+    }
+
+    static interface SinglePropertyNotAnnotated {
+        String getName()
+
+        void setName(String name)
+    }
+
+    @Managed
+    static interface WithInheritedPropertiesFromGrandparent extends WithInheritedProperties {
+        Boolean getFlag()
+
+        void setFlag(Boolean flag)
+    }
+
+    def "extracts properties from multiple levels of inheritance"() {
+        when:
+        def properties = extract(WithInheritedPropertiesFromGrandparent).properties.values()
+
+        then:
+        properties*.name == ["count", "flag", "name"]
+    }
+
+    @Managed
+    static interface WithInheritedPropertiesFromNotAnnotated extends SinglePropertyNotAnnotated {
+        Integer getCount()
+
+        void setCount(Integer count)
+    }
+
+    def "can extract inherited properties from an interface not annotated with @Managed"() {
+        when:
+        def properties = extract(WithInheritedPropertiesFromNotAnnotated).properties.values()
+
+        then:
+        properties*.name == ["count", "name"]
+    }
+
+    @Managed
+    static interface SingleStringValueProperty {
+        String getValue()
+
+        void setValue(String value)
+    }
+
+    static interface SingleFloatValueProperty {
+        Float getValue()
+
+        void setValue(Float value)
+    }
+
+    @Managed
+    static interface ConflictingPropertiesInParents extends SingleIntegerValueProperty, SingleStringValueProperty, SingleFloatValueProperty {
+    }
+
+    def "conflicting properties of super types are detected"() {
+        given:
+        def invalidMethods = [
+                MethodDescription.name("getValue").owner(SingleFloatValueProperty).returns(Float).takes(),
+                MethodDescription.name("getValue").owner(SingleIntegerValueProperty).returns(Integer).takes(),
+                MethodDescription.name("getValue").owner(SingleStringValueProperty).returns(String).takes(),
+        ]
+        def message = Pattern.quote("overloaded methods are not supported (invalid methods: ${invalidMethods.join(", ")})")
+
+        expect:
+        fail ConflictingPropertiesInParents, message
+    }
+
+    static interface AnotherSingleStringValueProperty {
+        String getValue()
+
+        void setValue(String value)
+    }
+
+    @Managed
+    static interface SamePropertyInMultipleTypes extends SingleStringValueProperty, AnotherSingleStringValueProperty {
+    }
+
+    def "exact same properties defined in multiple types of the hierarchy are allowed"() {
+        when:
+        def properties = extract(SamePropertyInMultipleTypes).properties.values()
+
+        then:
+        properties*.name == ["value"]
+    }
+
+    static interface ReadOnlyProperty {
+        SingleStringValueProperty getSingleStringValueProperty()
+    }
+
+    @Managed
+    static interface WritableProperty extends ReadOnlyProperty {
+        void setSingleStringValueProperty(SingleStringValueProperty value)
+    }
+
+    def "read only property of a super type can be made writable"() {
+        when:
+        def properties = extract(WritableProperty).properties.values()
+
+        then:
+        properties*.writable == [true]
+    }
+
+    @Managed
+    static interface ChildWithNoGettersOrSetters extends NoGettersOrSetters {
+    }
+
+    def "invalid methods of super types are reported"() {
+        expect:
+        fail ChildWithNoGettersOrSetters, Pattern.quote("only paired getter/setter methods are supported (invalid methods: ${MethodDescription.name("foo").returns(void.class).owner(NoGettersOrSetters).takes(String)})")
+    }
+
+    def "type argument of a managed set has to be specified"() {
+        given:
+        def type = ModelType.returnType(TypeHolder.getDeclaredMethod("noParam"))
+
+        expect:
+        fail type, "type parameter of $ManagedSet.name has to be specified"
+    }
+
+    static interface TypeHolder {
+        ManagedSet noParam();
+    }
+
+    @Managed
+    interface Thing {}
+
+    @Managed
+    interface SpecialThing extends Thing {}
+
+    interface SimpleModel {
+        Thing getThing()
+    }
+
+    @Managed
+    interface SpecialModel extends SimpleModel {
+        SpecialThing getThing()
+
+        void setThing(SpecialThing thing)
+    }
+
+    def "a subclass may specialize a property type"() {
+        when:
+        def properties = extract(SpecialModel).properties.values()
+
+        then:
+        properties*.type == [ModelType.of(SpecialThing)]
+    }
+
+    @Unroll
+    def "type argument of a managed set cannot be a wildcard - #type"() {
+        expect:
+        fail type, "type parameter of $ManagedSet.name cannot be a wildcard"
+
+        where:
+        type << [
+                new ModelType<ManagedSet<?>>() {},
+                new ModelType<ManagedSet<? extends A1>>() {},
+                new ModelType<ManagedSet<? super A1>>() {}
+        ]
+    }
+
+    def "type argument of a managed set has to be managed"() {
+        given:
+        def type = new ModelType<ManagedSet<Object>>() {}
+
+        when:
+        extract(type)
+
+        then:
+        InvalidManagedModelElementTypeException e = thrown()
+        e.message == TextUtil.toPlatformLineSeparators("""Invalid managed model type ${new ModelType<ManagedSet<Object>>() {}}: cannot create a managed set of type $Object.name as it is an unmanaged type.
+Supported types:
+ - enum types
+ - JDK value types: String, Boolean, Character, Integer, Long, Double, BigInteger, BigDecimal
+ - org.gradle.model.collection.ManagedSet<?> of a managed type
+ - interfaces and abstract classes annotated with org.gradle.model.Managed""")
+    }
+
+    def "type argument of a managed set has to be a valid managed type"() {
+        given:
+        def type = new ModelType<ManagedSet<SetterOnly>>() {}
+
+        when:
+        extract(type)
+
+        then:
+        InvalidManagedModelElementTypeException e = thrown()
+        def invalidMethodDescription = MethodDescription.name("setName").returns(void.class).owner(SetterOnly).takes(String)
+        e.message == TextUtil.toPlatformLineSeparators("""Invalid managed model type $SetterOnly.name: only paired getter/setter methods are supported (invalid methods: ${invalidMethodDescription}).
+The type was analyzed due to the following dependencies:
+$type
+  \\--- element type ($SetterOnly.name)""")
+    }
+
+    def "specializations of managed set are not supported"() {
+        given:
+        def type = new ModelType<SpecialManagedSet<A1>>() {}
+
+        expect:
+        fail type, "subtyping $ManagedSet.name is not supported"
+    }
+
+    def "managed sets of managed set are not supported"() {
+        given:
+        def type = new ModelType<ManagedSet<ManagedSet<A1>>>() {}
+
+        expect:
+        fail type, "$ManagedSet.name cannot be used as type parameter of $ManagedSet.name"
+    }
+
+    static class MyBigInteger extends BigInteger {
+        MyBigInteger(String s) {
+            super(s)
+        }
+    }
+
+    static class MyBigDecimal extends BigDecimal {
+        MyBigDecimal(String s) {
+            super(s)
+        }
+    }
+
+    def "cannot subclass non final value type"() {
+        expect:
+        fail MyBigInteger, Pattern.quote("subclasses of java.math.BigInteger are not supported")
+        fail MyBigDecimal, Pattern.quote("subclasses of java.math.BigDecimal are not supported")
+    }
+
+    static enum MyEnum {
+        A, B, C
+    }
+
+    def "can extract enum"() {
+        expect:
+        extract(MyEnum).kind == ModelSchema.Kind.VALUE
+    }
+
+    @Managed
+    static interface HasUnmanagedOnManaged {
+        @Unmanaged
+        MyEnum getMyEnum();
+
+        void setMyEnum(MyEnum myEnum)
+    }
+
+    def "cannot annotate managed type property with unmanaged"() {
+        expect:
+        fail HasUnmanagedOnManaged, Pattern.quote("property 'myEnum' is marked as @Unmanaged, but is of @Managed type")
+    }
+
+    @Managed
+    static interface MissingUnmanaged {
+        InputStream getThing();
+
+        void setThing(InputStream inputStream);
+    }
+
+    def "unamanaged types must be annotated with unmanaged"() {
+        expect:
+        fail MissingUnmanaged, Pattern.quote("it is an unmanaged type (please annotate the getter with @org.gradle.model.Unmanaged if you want this property to be unmanaged)")
+    }
+
+    @Managed
+    static interface ExtendsMissingUnmanaged {
+        @Unmanaged
+        InputStream getThing();
+
+        void setThing(InputStream inputStream);
+    }
+
+    private void fail(extractType, String msgPattern) {
+        fail(extractType, extractType, msgPattern)
+    }
+
+    def "subtype can declare property unmanaged"() {
+        expect:
+        extract(ExtendsMissingUnmanaged).properties.get("thing").type.rawClass == InputStream
+    }
+
+    @Managed
+    static interface NoSetterForUnmanaged {
+        @Unmanaged
+        InputStream getThing();
+    }
+
+    def "must have setter for unmanaged"() {
+        expect:
+        fail NoSetterForUnmanaged, Pattern.quote("unmanaged property 'thing' cannot be read only, unmanaged properties must have setters")
+    }
+
+    @Managed
+    static interface AddsSetterToNoSetterForUnmanaged extends NoSetterForUnmanaged {
+        void setThing(InputStream inputStream);
+    }
+
+    def "subtype can add unmanaged setter"() {
+        expect:
+        extract(AddsSetterToNoSetterForUnmanaged).properties.get("thing").type.rawClass == InputStream
+    }
+
+    @Managed
+    static abstract class NonAbstractGetterWithSetter {
+        String getName() {}
+        abstract void setName(String name)
+    }
+
+    @Managed
+    static abstract class NonAbstractSetter {
+        abstract String getName()
+
+        void setName(String name) {}
+    }
+
+    def "non-abstract mutator methods are not allowed"() {
+        expect:
+        fail NonAbstractGetterWithSetter, Pattern.quote("setters are not allowed for non-abstract getters (invalid method: ${MethodDescription.name("setName").owner(NonAbstractGetterWithSetter).returns(void.class).takes(String)})")
+        fail NonAbstractSetter, Pattern.quote("non-abstract setters are not allowed (invalid method: ${MethodDescription.name("setName").owner(NonAbstractSetter).takes(String).returns(void.class)})")
+    }
+
+    @Managed
+    static abstract class ConstructorWithArguments {
+        ConstructorWithArguments(String arg) {}
+    }
+
+    @Managed
+    static abstract class AdditionalConstructorWithArguments {
+        AdditionalConstructorWithArguments() {}
+
+        AdditionalConstructorWithArguments(String arg) {}
+    }
+
+    static class SuperConstructorWithArguments {
+        SuperConstructorWithArguments(String arg) {}
+    }
+
+    @Managed
+    static abstract class ConstructorCallingSuperConstructorWithArgs extends SuperConstructorWithArguments {
+        ConstructorCallingSuperConstructorWithArgs() {
+            super("foo")
+        }
+    }
+
+    @Managed
+    static abstract class CustomConstructorInSuperClass extends ConstructorCallingSuperConstructorWithArgs {
+    }
+
+    def "custom constructors are not allowed"() {
+        expect:
+        fail ConstructorWithArguments, Pattern.quote("custom constructors are not allowed (invalid method: ${MethodDescription.name("<init>").owner(ConstructorWithArguments).takes(String)})")
+        fail AdditionalConstructorWithArguments, Pattern.quote("custom constructors are not allowed (invalid method: ${MethodDescription.name("<init>").owner(AdditionalConstructorWithArguments).takes(String)})")
+        fail CustomConstructorInSuperClass, Pattern.quote("custom constructors are not allowed (invalid method: ${MethodDescription.name("<init>").owner(SuperConstructorWithArguments).takes(String)})")
+    }
+
+    @Managed
+    static abstract class WithInstanceScopedField {
+        private String name
+        private int age
+    }
+
+    @Managed
+    static abstract class WithInstanceScopedFieldInSuperclass extends WithInstanceScopedField {
+    }
+
+    def "instance scoped fields are not allowed"() {
+        expect:
+        fail WithInstanceScopedField, Pattern.quote("instance scoped fields are not allowed (found fields: private int ${WithInstanceScopedField.name}.age, private java.lang.String ${WithInstanceScopedField.name}.name)")
+        fail WithInstanceScopedFieldInSuperclass, Pattern.quote("instance scoped fields are not allowed (found fields: private int ${WithInstanceScopedField.name}.age, private java.lang.String ${WithInstanceScopedField.name}.name)")
+    }
+
+    @Managed
+    static abstract class ThrowsInConstructor {
+        ThrowsInConstructor() {
+            throw new RuntimeException("from constructor")
+        }
+    }
+
+    def "classes that cannot be instantiated are detected as soon as they are extracted"() {
+        when:
+        extract(ThrowsInConstructor)
+
+        then:
+        InvalidManagedModelElementTypeException e = thrown()
+        e.message == "Invalid managed model type ${ThrowsInConstructor.name}: instance creation failed"
+        e.cause.message == "from constructor"
+    }
+
+    @Managed
+    static abstract class CallsSetterInConstructor {
+        abstract String getName()
+
+        abstract void setName(String name)
+
+        CallsSetterInConstructor() {
+            name = "foo"
+        }
+    }
+
+    def "calling setters from constructor is not allowed"() {
+        when:
+        extract(CallsSetterInConstructor)
+
+        then:
+        InvalidManagedModelElementTypeException e = thrown()
+        e.message == "Invalid managed model type ${CallsSetterInConstructor.name}: instance creation failed"
+        e.cause.class == UnsupportedOperationException
+        e.cause.message == "Calling setters of a managed type on itself is not allowed"
+    }
+
+    @Managed
+    static abstract class ProtectedAbstractMethods {
+        protected abstract String getName()
+        protected abstract void setName(String name)
+    }
+
+    @Managed
+    static abstract class ProtectedAbstractMethodsInSuper extends ProtectedAbstractMethods {
+    }
+
+    def "protected abstract methods are not allowed"() {
+        given:
+        def getterDescription = MethodDescription.name("getName").owner(ProtectedAbstractMethods).takes().returns(String)
+        def setterDescription = MethodDescription.name("setName").owner(ProtectedAbstractMethods).returns(void.class).takes(String)
+
+        expect:
+        fail ProtectedAbstractMethods, Pattern.quote("protected and private methods are not allowed (invalid methods: $getterDescription, $setterDescription)")
+        fail ProtectedAbstractMethodsInSuper, Pattern.quote("protected and private methods are not allowed (invalid methods: $getterDescription, $setterDescription)")
+    }
+
+    @Managed
+    static abstract class ProtectedAndPrivateNonAbstractMethods {
+        protected String getName() {
+            return null;
+        }
+        private void setName(String name) {}
+    }
+
+    @Managed
+    static abstract class ProtectedAndPrivateNonAbstractMethodsInSuper extends ProtectedAndPrivateNonAbstractMethods {
+    }
+
+    def "protected and private non-abstract methods are not allowed"() {
+        given:
+        def getterDescription = MethodDescription.name("getName").owner(ProtectedAndPrivateNonAbstractMethods).takes().returns(String)
+        def setterDescription = MethodDescription.name("setName").owner(ProtectedAndPrivateNonAbstractMethods).returns(void.class).takes(String)
+
+        expect:
+        fail ProtectedAndPrivateNonAbstractMethods, Pattern.quote("protected and private methods are not allowed (invalid methods: $getterDescription, $setterDescription)")
+        fail ProtectedAndPrivateNonAbstractMethodsInSuper, Pattern.quote("protected and private methods are not allowed (invalid methods: $getterDescription, $setterDescription)")
+    }
+
+    private void fail(extractType, errorType, String msgPattern) {
+        try {
+            extract(extractType)
+            throw new AssertionError("schema extraction from ${getName(extractType)} should failed with message: $msgPattern")
+        } catch (InvalidManagedModelElementTypeException e) {
+            assert e.message.startsWith("Invalid managed model type ${getName(errorType)}: ")
+            assert e.message =~ msgPattern
+        }
+    }
+
+    private ModelSchema<?> extract(ModelType<?> modelType) {
+        store.extract(modelType, cache)
+    }
+
+    private ModelSchema<?> extract(Class<?> clazz) {
+        extract(ModelType.of(clazz))
+    }
+
+    private String getName(ModelType<?> modelType) {
+        modelType
+    }
+
+    private String getName(Class<?> clazz) {
+        clazz.name
+    }
+}
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SimpleManagedType.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SimpleManagedType.java
new file mode 100644
index 0000000..fcce73a
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SimpleManagedType.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import org.gradle.model.Managed;
+
+ at Managed
+public interface SimpleManagedType {
+
+    String getName();
+
+    void setName(String name);
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SpecialManagedSet.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SpecialManagedSet.java
new file mode 100644
index 0000000..e65e2cf
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/manage/schema/extract/SpecialManagedSet.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.manage.schema.extract;
+
+import org.gradle.model.collection.ManagedSet;
+
+interface SpecialManagedSet<T> extends ManagedSet<T> {}
\ No newline at end of file
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/DefaultModelRegistryTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/DefaultModelRegistryTest.groovy
new file mode 100644
index 0000000..dcb7c78
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/DefaultModelRegistryTest.groovy
@@ -0,0 +1,826 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.api.Action
+import org.gradle.api.Transformer
+import org.gradle.internal.BiAction
+import org.gradle.model.ConfigurationCycleException
+import org.gradle.model.internal.core.*
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.type.ModelType
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+
+class DefaultModelRegistryTest extends Specification {
+
+    def registry = new ModelRegistryHelper()
+
+    def "can maybe get non existing"() {
+        when:
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+
+        then:
+        thrown IllegalStateException
+
+        when:
+        def modelElement = registry.find(ModelPath.path("foo"), ModelType.untyped())
+
+        then:
+        noExceptionThrown()
+
+        and:
+        modelElement == null
+    }
+
+    def "can get element for which a creator has been registered"() {
+        given:
+        registry.createInstance("foo", "value")
+
+        expect:
+        registry.realize(ModelPath.path("foo"), ModelType.untyped()) == "value"
+    }
+
+    def "can get root node"() {
+        expect:
+        registry.realizeNode(ModelPath.ROOT) != null
+    }
+
+    def "cannot get element for which creator inputs are not bound"() {
+        given:
+        registry.create("foo") { it.descriptor("foo creator").unmanaged(String, "other", null, Stub(Transformer)) }
+
+        when:
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+
+        then:
+        UnboundModelRulesException e = thrown()
+        normaliseLineSeparators(e.message) == """The following model rules are unbound:
+  foo creator
+    Immutable:
+      - other (java.lang.Object)"""
+    }
+
+    def "cannot register creator when element already known"() {
+        given:
+        registry.create("foo") { it.descriptor("create foo as String").unmanaged("value") }
+
+        when:
+        registry.create("foo") { it.descriptor("create foo as Integer").unmanaged(12.toInteger()) }
+
+        then:
+        DuplicateModelException e = thrown()
+        e.message == /Cannot create 'foo' using creation rule 'create foo as Integer' as the rule 'create foo as String' is already registered to create this model element./
+    }
+
+    def "cannot register creator when element already closed"() {
+        given:
+        registry.create("foo") { it.descriptor("create foo as String").unmanaged("value") }
+
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+
+        when:
+        registry.create("foo") { it.descriptor("create foo as Integer").unmanaged(12.toInteger()) }
+
+        then:
+        DuplicateModelException e = thrown()
+        e.message == /Cannot create 'foo' using creation rule 'create foo as Integer' as the rule 'create foo as String' has already been used to create this model element./
+    }
+
+    def "rule cannot add link when element already known"() {
+        def mutatorAction = Mock(Action)
+
+        given:
+        registry.create("foo") { it.descriptor("create foo as Integer").unmanaged(12.toInteger()) }
+        registry.mutate { it.path "foo" type Integer descriptor "mutate foo as Integer" node mutatorAction }
+        mutatorAction.execute(_) >> { MutableModelNode node ->
+            node.addLink(registry.creator("foo.bar") { it.descriptor("create foo.bar as String").unmanaged("12") })
+            node.addLink(registry.creator("foo.bar") { it.descriptor("create foo.bar as Integer").unmanaged(12) })
+        }
+
+        when:
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.message == /Exception thrown while executing model rule: mutate foo as Integer/
+        e.cause instanceof DuplicateModelException
+        e.cause.message == /Cannot create 'foo.bar' using creation rule 'create foo.bar as Integer' as the rule 'create foo.bar as String' is already registered to create this model element./
+    }
+
+    def "inputs for creator are bound when inputs already closed"() {
+        def action = Mock(Transformer)
+
+        given:
+        registry.createInstance("foo", 12.toInteger())
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+        registry.create("bar") { it.unmanaged String, Integer, action }
+        action.transform(12) >> "[12]"
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.untyped()) == "[12]"
+    }
+
+    def "inputs for creator are bound when inputs already known"() {
+        def action = Mock(Transformer)
+
+        given:
+        registry.createInstance("foo", 12.toInteger())
+        registry.create("bar") { it.unmanaged String, Integer, action }
+        action.transform(12) >> "[12]"
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.untyped()) == "[12]"
+    }
+
+    def "inputs for creator are bound as inputs become known"() {
+        def action = Mock(Transformer)
+
+        given:
+        registry.create("bar") { it.unmanaged String, Integer, action }
+        registry.createInstance("foo", 12.toInteger())
+        action.transform(12) >> "[12]"
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.untyped()) == "[12]"
+    }
+
+    def "inputs for creator are bound when inputs later defined by some rule"() {
+        def creatorAction = Mock(Transformer)
+        def mutatorAction = Mock(Action)
+
+        given:
+        registry.create("bar") { it.unmanaged(String, "foo.child", creatorAction) }
+        registry.createInstance("foo", 12.toInteger())
+        registry.mutate { it.path "foo" type Integer node mutatorAction }
+        mutatorAction.execute(_) >> { MutableModelNode node -> node.addLink(registry.instanceCreator("foo.child", 12.toInteger())) }
+        creatorAction.transform(12) >> "[12]"
+
+        expect:
+        registry.realize(ModelPath.path("foo"), ModelType.untyped()) // TODO - should not need this - the input can be inferred from the input path
+        registry.realize(ModelPath.path("bar"), ModelType.untyped()) == "[12]"
+    }
+
+    def "creator and mutators are invoked in order before element is closed"() {
+        def action = Mock(Action)
+
+        given:
+        def actionImpl = registry.action().path("foo").type(Bean).action(action)
+        registry
+                .create("foo", new Bean(), action)
+                .configure(ModelActionRole.Defaults, actionImpl)
+                .configure(ModelActionRole.Initialize, actionImpl)
+                .configure(ModelActionRole.Mutate, actionImpl)
+                .configure(ModelActionRole.Finalize, actionImpl)
+                .configure(ModelActionRole.Validate, actionImpl)
+
+        when:
+        def value = registry.realize(ModelPath.path("foo"), ModelType.of(Bean)).value
+
+        then:
+        value == "create > defaults > initialize > mutate > finalize"
+
+        and:
+        1 * action.execute(_) >> { Bean bean ->
+            assert bean.value == null
+            bean.value = "create"
+        }
+        1 * action.execute(_) >> { Bean bean ->
+            bean.value += " > defaults"
+        }
+        1 * action.execute(_) >> { Bean bean ->
+            bean.value += " > initialize"
+        }
+        1 * action.execute(_) >> { Bean bean ->
+            bean.value += " > mutate"
+        }
+        1 * action.execute(_) >> { Bean bean ->
+            bean.value += " > finalize"
+        }
+        1 * action.execute(_) >> { Bean bean ->
+            assert bean.value == "create > defaults > initialize > mutate > finalize"
+        }
+        0 * action._
+
+        when:
+        registry.realize(ModelPath.path("foo"), ModelType.of(Bean))
+
+        then:
+        0 * action._
+    }
+
+    def "creator for linked element invoked before element is closed"() {
+        def action = Mock(Action)
+
+        given:
+        registry.createInstance("foo", new Bean())
+        registry.mutate { it.path "foo" type Bean node action }
+
+        when:
+        registry.realize(ModelPath.path("foo"), ModelType.of(Bean))
+
+        then:
+        1 * action.execute(_) >> { MutableModelNode node -> node.addLink(registry.creator("foo.bar", "value", action)) }
+        1 * action.execute(_)
+        0 * action._
+    }
+
+    def "inputs for mutator are bound when inputs already closed"() {
+        def action = Mock(BiAction)
+
+        given:
+        registry.createInstance("foo", 12.toInteger())
+        registry.realize(ModelPath.path("foo"), ModelType.untyped())
+        registry.createInstance("bar", new Bean())
+        registry.mutate { it.path("bar").type(Bean).action(Integer, action) }
+        action.execute(_, 12) >> { bean, value -> bean.value = "[12]" }
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.of(Bean)).value == "[12]"
+    }
+
+    def "inputs for mutator are bound when inputs already known"() {
+        def action = Mock(BiAction)
+
+        given:
+        registry.createInstance("foo", 12.toInteger())
+        registry.createInstance("bar", new Bean())
+        registry.mutate { it.path("bar").type(Bean).action(Integer, action) }
+        action.execute(_, 12) >> { bean, value -> bean.value = "[12]" }
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.of(Bean)).value == "[12]"
+    }
+
+    def "inputs for mutator are bound as inputs become known"() {
+        def action = Mock(BiAction)
+
+        given:
+        registry.createInstance("bar", new Bean())
+        registry.mutate { it.path("bar").type(Bean).action(Integer, action) }
+        registry.createInstance("foo", 12.toInteger())
+        action.execute(_, 12) >> { bean, value -> bean.value = "[12]" }
+
+        expect:
+        registry.realize(ModelPath.path("bar"), ModelType.of(Bean)).value == "[12]"
+    }
+
+    def "can attach a mutator with inputs to all elements linked from an element"() {
+        def creatorAction = Mock(Action)
+        def mutatorAction = Mock(BiAction)
+
+        given:
+        registry.create("parent") { it.unmanagedNode Integer, creatorAction }
+        creatorAction.execute(_) >> { MutableModelNode node ->
+            node.applyToAllLinks(ModelActionRole.Mutate, registry.action().type(Bean).action(String, mutatorAction))
+            node.addLink(registry.instanceCreator("parent.foo", new Bean(value: "foo")))
+            node.addLink(registry.instanceCreator("parent.bar", new Bean(value: "bar")))
+        }
+        mutatorAction.execute(_, _) >> { Bean bean, String prefix -> bean.value = "$prefix: $bean.value" }
+        registry.createInstance("prefix", "prefix")
+
+        registry.realize(ModelPath.path("parent"), ModelType.untyped()) // TODO - should not need this
+
+        expect:
+        registry.realize(ModelPath.path("parent.foo"), ModelType.of(Bean)).value == "prefix: foo"
+        registry.realize(ModelPath.path("parent.bar"), ModelType.of(Bean)).value == "prefix: bar"
+    }
+
+    def "can attach a mutator to all elements with specific type linked from an element"() {
+        def creatorAction = Mock(Action)
+        def mutatorAction = Mock(Action)
+
+        given:
+        registry.create("parent") { it.unmanagedNode Integer, creatorAction }
+        creatorAction.execute(_) >> { MutableModelNode node ->
+            node.applyToAllLinks(ModelActionRole.Mutate, registry.action().type(Bean).action(mutatorAction))
+            node.addLink(registry.instanceCreator("parent.foo", "ignore me"))
+            node.addLink(registry.instanceCreator("parent.bar", new Bean(value: "bar")))
+        }
+        mutatorAction.execute(_) >> { Bean bean -> bean.value = "prefix: $bean.value" }
+
+        registry.realize(ModelPath.path("parent"), ModelType.untyped()) // TODO - should not need this
+
+        expect:
+        registry.realize(ModelPath.path("parent.bar"), ModelType.of(Bean)).value == "prefix: bar"
+        registry.realize(ModelPath.path("parent.foo"), ModelType.of(String)) == "ignore me"
+    }
+
+    def "can attach a mutator with inputs to element linked from another element"() {
+        def creatorAction = Mock(Action)
+        def mutatorAction = Mock(BiAction)
+
+        given:
+        registry.create("parent") { it.unmanagedNode Integer, creatorAction }
+        creatorAction.execute(_) >> { MutableModelNode node ->
+            node.applyToLink(ModelActionRole.Mutate, registry.action().path("parent.foo").type(Bean).action(String, mutatorAction))
+            node.addLink(registry.instanceCreator("parent.foo", new Bean(value: "foo")))
+            node.addLink(registry.instanceCreator("parent.bar", new Bean(value: "bar")))
+        }
+        mutatorAction.execute(_, _) >> { Bean bean, String prefix -> bean.value = "$prefix: $bean.value" }
+        registry.create(registry.instanceCreator("prefix", "prefix"))
+
+        registry.realize(ModelPath.path("parent"), ModelType.untyped()) // TODO - should not need this
+
+        expect:
+        registry.realize(ModelPath.path("parent.foo"), ModelType.of(Bean)).value == "prefix: foo"
+        registry.realize(ModelPath.path("parent.bar"), ModelType.of(Bean)).value == "bar"
+    }
+
+    def "cannot attach link when element is not mutable"() {
+        def action = Stub(Action)
+
+        given:
+        registry.createInstance("thing", "value")
+        registry.configure(ModelActionRole.Validate) { it.path "thing" type Object node action }
+        action.execute(_) >> { MutableModelNode node -> node.addLink(registry.creator("thing.child") { it.descriptor("create thing.child as String").unmanaged("value") }) }
+
+        when:
+        registry.realize(ModelPath.path("thing"), ModelType.untyped())
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause instanceof IllegalStateException
+        e.cause.message == "Cannot create 'thing.child' using creation rule 'create thing.child as String' as model element 'thing' is no longer mutable."
+    }
+
+    def "cannot set value when element is not mutable"() {
+        def action = Stub(Action)
+
+        given:
+        registry.createInstance("thing", "value")
+        registry.configure(ModelActionRole.Validate) { it.path("thing").type(Object).node(action) }
+        action.execute(_) >> { MutableModelNode node -> node.setPrivateData(ModelType.of(String), "value 2") }
+
+        when:
+        registry.realize(ModelPath.path("thing"), ModelType.untyped())
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause instanceof IllegalStateException
+        e.cause.message == "Cannot set value for model element 'thing' as this element is not mutable."
+    }
+
+    @Unroll
+    def "cannot add action for #targetRole mutation when in #fromRole mutation"() {
+        def action = Stub(Action)
+
+        given:
+        registry.createInstance("thing", "value")
+                .configure(fromRole) { it.path("thing").node(action) }
+        action.execute(_) >> { MutableModelNode node -> registry.configure(targetRole) { it.path("thing").type(String).descriptor("X").action {} } }
+
+        when:
+        registry.realize(ModelPath.path("thing"), ModelType.untyped())
+
+        then:
+        IllegalStateException e = thrown()
+        e.message.startsWith "Cannot add $targetRole rule 'X' for model element 'thing'"
+
+        where:
+        fromRole                   | targetRole
+        ModelActionRole.Initialize | ModelActionRole.Defaults
+        ModelActionRole.Mutate     | ModelActionRole.Defaults
+        ModelActionRole.Mutate     | ModelActionRole.Initialize
+        ModelActionRole.Finalize   | ModelActionRole.Defaults
+        ModelActionRole.Finalize   | ModelActionRole.Initialize
+        ModelActionRole.Finalize   | ModelActionRole.Mutate
+        ModelActionRole.Validate   | ModelActionRole.Defaults
+        ModelActionRole.Validate   | ModelActionRole.Initialize
+        ModelActionRole.Validate   | ModelActionRole.Mutate
+        ModelActionRole.Validate   | ModelActionRole.Finalize
+    }
+
+    @Unroll
+    def "cannot add action for #targetRole mutation when in #fromState state"() {
+        def action = Stub(Action)
+
+        given:
+        registry.createInstance("thing", "value")
+                .createInstance("another", "value")
+                .configure(ModelActionRole.Mutate) {
+            it.path("another").node(action)
+        }
+        action.execute(_) >> {
+            MutableModelNode node -> registry.configure(targetRole) { it.path("thing").type(String).descriptor("X").action {} }
+        }
+
+        when:
+        registry.atState(ModelPath.path("thing"), fromState)
+        registry.realize(ModelPath.path("another"), ModelType.untyped())
+
+        then:
+        IllegalStateException e = thrown()
+        e.message.startsWith "Cannot add $targetRole rule 'X' for model element 'thing'"
+
+        where:
+        fromState                       | targetRole
+        ModelNode.State.DefaultsApplied | ModelActionRole.Defaults
+        ModelNode.State.Initialized     | ModelActionRole.Initialize
+        ModelNode.State.Initialized     | ModelActionRole.Defaults
+        ModelNode.State.Mutated         | ModelActionRole.Mutate
+        ModelNode.State.Mutated         | ModelActionRole.Defaults
+        ModelNode.State.Mutated         | ModelActionRole.Initialize
+        ModelNode.State.Finalized       | ModelActionRole.Finalize
+        ModelNode.State.Finalized       | ModelActionRole.Defaults
+        ModelNode.State.Finalized       | ModelActionRole.Initialize
+        ModelNode.State.Finalized       | ModelActionRole.Mutate
+        ModelNode.State.SelfClosed      | ModelActionRole.Validate
+        ModelNode.State.SelfClosed      | ModelActionRole.Defaults
+        ModelNode.State.SelfClosed      | ModelActionRole.Initialize
+        ModelNode.State.SelfClosed      | ModelActionRole.Mutate
+        ModelNode.State.SelfClosed      | ModelActionRole.Finalize
+    }
+
+    @Unroll
+    def "can add action for #targetRole mutation when in #fromRole mutation"() {
+        given:
+        registry.createInstance("thing", new MutableValue(value: "initial")).configure(fromRole) {
+            it.path("thing").node { MutableModelNode node ->
+                registry.configure(targetRole) {
+                    it.path("thing").type(MutableValue).action {
+                        it.value = "mutated"
+                    }
+                }
+            }
+        }
+
+        when:
+        def thing = registry.realize(ModelPath.path("thing"), ModelType.of(MutableValue))
+
+        then:
+        thing.value == "mutated"
+
+        where:
+        fromRole                   | targetRole
+        ModelActionRole.Defaults   | ModelActionRole.Defaults
+        ModelActionRole.Defaults   | ModelActionRole.Initialize
+        ModelActionRole.Defaults   | ModelActionRole.Mutate
+        ModelActionRole.Defaults   | ModelActionRole.Finalize
+        ModelActionRole.Defaults   | ModelActionRole.Validate
+        ModelActionRole.Initialize | ModelActionRole.Initialize
+        ModelActionRole.Initialize | ModelActionRole.Mutate
+        ModelActionRole.Initialize | ModelActionRole.Finalize
+        ModelActionRole.Initialize | ModelActionRole.Validate
+        ModelActionRole.Mutate     | ModelActionRole.Mutate
+        ModelActionRole.Mutate     | ModelActionRole.Finalize
+        ModelActionRole.Mutate     | ModelActionRole.Validate
+        ModelActionRole.Finalize   | ModelActionRole.Finalize
+        ModelActionRole.Finalize   | ModelActionRole.Validate
+        ModelActionRole.Validate   | ModelActionRole.Validate
+    }
+
+    @Unroll
+    def "can add action for #targetRole mutation when in #fromState state"() {
+        def action = Stub(Action)
+
+        given:
+        registry.createInstance("thing", "value")
+                .createInstance("another", "value")
+                .configure(ModelActionRole.Mutate) {
+            it.path("another").node(action)
+        }
+        action.execute(_) >> {
+            MutableModelNode node -> registry.configure(targetRole) { it.path("thing").type(String).descriptor("X").action {} }
+        }
+
+        when:
+        registry.atState(ModelPath.path("thing"), fromState)
+        registry.realize(ModelPath.path("another"), ModelType.untyped())
+
+        then:
+        noExceptionThrown()
+
+        where:
+        fromState                       | targetRole
+        ModelNode.State.DefaultsApplied | ModelActionRole.Initialize
+        ModelNode.State.DefaultsApplied | ModelActionRole.Mutate
+        ModelNode.State.DefaultsApplied | ModelActionRole.Finalize
+        ModelNode.State.DefaultsApplied | ModelActionRole.Validate
+        ModelNode.State.Initialized     | ModelActionRole.Mutate
+        ModelNode.State.Initialized     | ModelActionRole.Finalize
+        ModelNode.State.Initialized     | ModelActionRole.Validate
+        ModelNode.State.Mutated         | ModelActionRole.Finalize
+        ModelNode.State.Mutated         | ModelActionRole.Validate
+        ModelNode.State.Finalized       | ModelActionRole.Validate
+    }
+
+    @Unroll
+    def "can get node at state"() {
+        given:
+        registry.createInstance("thing", new Bean(value: "created"))
+        ModelActionRole.values().each { role ->
+            registry.configure(role, {
+                it.path "thing" type Bean action {
+                    if (it) {
+                        it.value = role.name()
+                    }
+                }
+            })
+        }
+
+        expect:
+        registry.atState(ModelPath.path("thing"), state).getPrivateData(ModelType.of(Bean))?.value == expected
+
+        where:
+        state                           | expected
+        ModelNode.State.Known           | null
+        ModelNode.State.Created         | "created"
+        ModelNode.State.DefaultsApplied | ModelActionRole.Defaults.name()
+        ModelNode.State.Initialized     | ModelActionRole.Initialize.name()
+        ModelNode.State.Mutated         | ModelActionRole.Mutate.name()
+        ModelNode.State.Finalized       | ModelActionRole.Finalize.name()
+        ModelNode.State.SelfClosed      | ModelActionRole.Validate.name()
+        ModelNode.State.GraphClosed     | ModelActionRole.Validate.name()
+    }
+
+    def "asking for element at known state does not invoke creator"() {
+        given:
+        def events = []
+        registry.create("thing", new Bean(), { events << "created" })
+
+        when:
+        registry.atState(ModelPath.path("thing"), ModelNode.State.Known)
+
+        then:
+        events == []
+
+        when:
+        registry.atState(ModelPath.path("thing"), ModelNode.State.Created)
+
+        then:
+        events == ["created"]
+    }
+
+    @Unroll
+    def "asking for unknown element at any state returns null"() {
+        expect:
+        registry.atState(ModelPath.path("thing"), state) == null
+
+        where:
+        state << ModelNode.State.values().toList()
+    }
+
+    def "getting self closed collection defines all links but does not realise them until graph closed"() {
+        given:
+        def events = []
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) } as NamedEntityInstantiator).build())
+                .collection("things", Bean, iRef)
+                .mutate {
+            it.path "things" type cbType action { c ->
+                events << "collection mutated"
+                c.create("c1") { events << "$it.name created" }
+            }
+        }
+
+        when:
+        def cbNode = registry.atState(ModelPath.path("things"), ModelNode.State.SelfClosed)
+
+        then:
+        events == ["collection mutated"]
+        cbNode.getLinkNames(ModelType.of(Bean)).toList() == ["c1"]
+
+        when:
+        registry.atState(ModelPath.path("things"), ModelNode.State.GraphClosed)
+
+        then:
+        events == ["collection mutated", "c1 created"]
+    }
+
+    @Unroll
+    def "cannot request model node at earlier state when at #state"() {
+        given:
+        registry.createInstance("thing", new Bean())
+
+        expect:
+        registry.atState(ModelPath.path("thing"), state)
+
+        when:
+        // This has to be in a when block to stop Spock rewriting it
+        ModelNode.State.values().findAll { it.ordinal() < state.ordinal() }.each { earlier ->
+            try {
+                registry.atState(ModelPath.path("thing"), earlier)
+                throw new AssertionError("Expected error")
+            } catch (IllegalStateException e) {
+                assert e.message == "Cannot lifecycle model node 'thing' to state ${earlier.name()} as it is already at ${state.name()}"
+            }
+        }
+
+        then:
+        true
+
+        where:
+        state << ModelNode.State.values().toList()
+    }
+
+    @Unroll
+    def "is benign to request element at current state"() {
+        given:
+        registry.createInstance("thing", new Bean())
+
+        when:
+        // not in loop to get different stacktrace line numbers
+        registry.atState(ModelPath.path("thing"), state)
+        registry.atState(ModelPath.path("thing"), state)
+        registry.atState(ModelPath.path("thing"), state)
+
+        then:
+        noExceptionThrown()
+
+        where:
+        state << ModelNode.State.values().toList()
+    }
+
+    @Unroll
+    def "is benign to request element at prior state"() {
+        given:
+        registry.createInstance("thing", new Bean())
+
+        when:
+        registry.atState(ModelPath.path("thing"), state)
+        ModelNode.State.values().findAll { it.ordinal() <= state.ordinal() }.each {
+            registry.atStateOrLater(ModelPath.path("thing"), state)
+        }
+
+        then:
+        noExceptionThrown()
+
+        where:
+        state << ModelNode.State.values().toList()
+    }
+
+    @Unroll
+    def "requesting at current state does not reinvoke actions"() {
+        given:
+        def events = []
+        registry.createInstance("thing", new Bean())
+        def uptoRole = ModelActionRole.values().findAll { it.ordinal() <= role.ordinal() }
+        uptoRole.each { r ->
+            registry.configure(r) { it.path "thing" type Bean action { events << r.name() } }
+        }
+
+        when:
+        registry.atState(ModelPath.path("thing"), state)
+
+        then:
+        events == uptoRole*.name()
+
+        when:
+        registry.atState(ModelPath.path("thing"), state)
+
+        then:
+        events == uptoRole*.name()
+
+        where:
+        state                           | role
+        ModelNode.State.DefaultsApplied | ModelActionRole.Defaults
+        ModelNode.State.Initialized     | ModelActionRole.Initialize
+        ModelNode.State.Mutated         | ModelActionRole.Mutate
+        ModelNode.State.Finalized       | ModelActionRole.Finalize
+        ModelNode.State.SelfClosed      | ModelActionRole.Validate
+        ModelNode.State.GraphClosed     | ModelActionRole.Validate
+    }
+
+    def "only rules that actually have unbound inputs are reported as unbound"() {
+        given:
+        def cbType = DefaultCollectionBuilder.typeOf(ModelType.of(Bean))
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Bean)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .createInstance("foo", new Bean())
+                .mutate {
+            it.descriptor("non-bindable").path("foo").type(Bean).action("emptyBeans.element", ModelType.of(Bean), null, {})
+        }
+        .mutate {
+            it.descriptor("bindable").path("foo").type(Bean).action("beans.element", ModelType.of(Bean)) {
+            }
+        }
+        .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Bean(name: name) } as NamedEntityInstantiator).build())
+                .collection("beans", Bean, iRef)
+                .mutate {
+            it.path "beans" type cbType action { c ->
+                c.create("element")
+            }
+        }
+        .collection("emptyBeans", Bean, iRef)
+
+        when:
+        registry.bindAllReferences()
+
+        then:
+        UnboundModelRulesException e = thrown()
+        normaliseLineSeparators(e.message) == '''The following model rules are unbound:
+  non-bindable
+    Mutable:
+      + foo (org.gradle.model.internal.registry.DefaultModelRegistryTest$Bean)
+    Immutable:
+      - emptyBeans.element (org.gradle.model.internal.registry.DefaultModelRegistryTest$Bean)'''
+    }
+
+    def "two element mutation rule based configuration cycles are detected"() {
+        given:
+        registry.createInstance("foo", "foo")
+                .createInstance("bar", "bar")
+                .mutate { it.path("foo").descriptor("foo mutator").type(String).action("bar", ModelType.of(String), "parameter 1", {}) }
+                .mutate { it.path("bar").descriptor("bar mutator").type(String).action("foo", ModelType.of(String), null, {}) }
+
+        when:
+        registry.get("foo")
+
+        then:
+        ConfigurationCycleException e = thrown()
+        e.message == TextUtil.toPlatformLineSeparators("""A cycle has been detected in model rule dependencies. References forming the cycle:
+foo mutator parameter 1 (path: bar)
+  \\--- bar mutator (path: foo)
+    \\--- foo mutator""")
+    }
+
+    def "multiple element configuration cycles are detected"() {
+        registry.create("foo") { it.unmanaged(String, "bar") { "foo" } }
+                .create("bar") { it.unmanaged(String, "fizz") { "bar" } }
+                .createInstance("fizz", "fizz")
+                .mutate { it.path("fizz").descriptor("fizz mutator").type(String).action("buzz", ModelType.of(String), {}) }
+                .createInstance("buzz", "buzz")
+                .mutate { it.path("buzz").descriptor("buzz mutator").type(String).action("foo", ModelType.of(String), {}) }
+
+        when:
+        registry.get("foo")
+
+        then:
+        ConfigurationCycleException e = thrown()
+        e.message == TextUtil.toPlatformLineSeparators("""A cycle has been detected in model rule dependencies. References forming the cycle:
+foo creator bar (path: bar)
+  \\--- bar creator fizz (path: fizz)
+    \\--- fizz mutator buzz (path: buzz)
+      \\--- buzz mutator foo (path: foo)
+        \\--- foo creator""")
+    }
+
+    def "one element configuration cycles are detected"() {
+        given:
+        registry.createInstance("foo", "foo")
+                .mutate { it.path("foo").descriptor("foo mutator").type(String).action(String) {} }
+
+        when:
+        registry.get("foo")
+
+        then:
+        ConfigurationCycleException e = thrown()
+        e.message == TextUtil.toPlatformLineSeparators("""A cycle has been detected in model rule dependencies. References forming the cycle:
+foo mutator java.lang.String (path: foo)
+  \\--- foo mutator""")
+    }
+
+    def "only the elements actually forming the cycle are reported when configuration cycles are detected"() {
+        given:
+        registry.create("foo") { it.unmanaged(String, "bar") { "foo" } }
+                .create("bar") { it.unmanaged(String, "fizz") { "bar" } }
+                .mutate { it.path("foo").type(String).action(String) {} }
+                .create("fizz") { it.unmanaged(String, "buzz") { "buzz" } }
+                .mutate { it.path("fizz").descriptor("fizz mutator").type(String).action("bar", ModelType.of(String), {}) }
+                .createInstance("buzz", "buzz")
+
+        when:
+        registry.get("foo")
+
+        then:
+        ConfigurationCycleException e = thrown()
+        e.message == TextUtil.toPlatformLineSeparators("""A cycle has been detected in model rule dependencies. References forming the cycle:
+bar creator fizz (path: fizz)
+  \\--- fizz mutator bar (path: bar)
+    \\--- bar creator""")
+    }
+
+    class Bean {
+        String name
+        String value
+    }
+
+    class MutableValue {
+        String value
+    }
+
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelGraphTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelGraphTest.groovy
new file mode 100644
index 0000000..8128d02
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelGraphTest.groovy
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.ModelPromise
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class ModelGraphTest extends Specification {
+    def graph = new ModelGraph(root())
+    def nodes = [:]
+
+    def setup() {
+        nodes[graph.root.path] = graph.root
+    }
+
+    def "notifies listener when node added"() {
+        def listener = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+
+        given:
+        graph.addListener(listener)
+
+        when:
+        graph.add(a)
+        graph.add(b)
+
+        then:
+        1 * listener.onCreate(a)
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of existing nodes"() {
+        def listener = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+
+        given:
+        graph.add(a)
+        graph.add(b)
+
+        when:
+        graph.addListener(listener)
+
+        then:
+        1 * listener.onCreate(graph.root)
+        1 * listener.onCreate(a)
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+    }
+
+    def "stops notifying listener of new nodes after listener signals it is done"() {
+        def listener = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+
+        given:
+        graph.addListener(listener)
+
+        when:
+        graph.add(a)
+        graph.add(b)
+
+        then:
+        1 * listener.onCreate(a) >> true
+        0 * listener.onCreate(_)
+    }
+
+    def "stops notifying listener of existing nodes after listener signals it is done"() {
+        def listener = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+        def c = node("c")
+
+        given:
+        graph.add(a)
+        graph.add(b)
+
+        when:
+        graph.addListener(listener)
+        graph.add(c)
+
+        then:
+        1 * listener.onCreate(graph.root)
+        1 * listener.onCreate(a) >> true
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of new node with matching path"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a")
+        def b = node("b")
+        def c = node("c")
+
+        given:
+        listener.matchPath() >> b.path
+        graph.addListener(listener)
+
+        when:
+        graph.add(a)
+        graph.add(b)
+        graph.add(c)
+
+        then:
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of existing node with matching path"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a")
+        def b = node("b")
+        def c = node("c")
+
+        given:
+        listener.matchPath() >> b.path
+        graph.add(a)
+        graph.add(b)
+        graph.add(c)
+
+        when:
+        graph.addListener(listener)
+
+        then:
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of node with matching parent"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a")
+        def b = node("a.b")
+        def c = node("a.c")
+        def d = node("d")
+
+        given:
+        listener.matchParent() >> a.path
+        a.links >> [b]
+
+        when:
+        graph.add(a)
+        graph.add(b)
+        graph.addListener(listener)
+
+        then:
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+
+        when:
+        graph.add(c)
+        graph.add(d)
+
+        then:
+        1 * listener.onCreate(c)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of node with matching type"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a", Integer)
+        def b = node("b", String)
+        def c = node("c", String)
+        def d = node("d", Long)
+
+        given:
+        listener.matchType() >> ModelType.of(String)
+
+        when:
+        graph.add(a)
+        graph.add(b)
+        graph.addListener(listener)
+        graph.add(c)
+        graph.add(d)
+
+        then:
+        1 * listener.onCreate(b)
+        1 * listener.onCreate(c)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener of node with matching parent and type"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a", String)
+        def b = node("a.b", String)
+        def c = node("a.c", String)
+        def d = node("a.d", Long)
+
+        given:
+        listener.matchType() >> ModelType.of(String)
+        listener.matchParent() >> a.path
+        a.links >> [b]
+
+        when:
+        graph.add(a)
+        graph.add(b)
+        graph.addListener(listener)
+
+        then:
+        1 * listener.onCreate(b)
+        0 * listener.onCreate(_)
+
+        when:
+        graph.add(c)
+        graph.add(d)
+
+        then:
+        1 * listener.onCreate(c)
+        0 * listener.onCreate(_)
+    }
+
+    def "notifies listener about a node with matching scope and its children"() {
+        def listener = Mock(ModelCreationListener)
+
+        def a = node("a", String)
+        def b = node("a.b", String)
+        def c = node("a.b.c", String)
+        def d = node("a.b.d", String)
+        def e = node("a.b.e", Integer)
+        def f = node("a.b.c.f", String)
+
+        given:
+        listener.matchType() >> ModelType.of(String)
+        listener.matchScope() >> b.path
+        b.links >> [c]
+
+        when:
+        graph.add(a)
+        graph.add(b)
+        graph.add(c)
+        graph.addListener(listener)
+
+        then:
+        1 * listener.onCreate(b)
+        1 * listener.onCreate(c)
+        0 * listener.onCreate(_)
+
+        when:
+        graph.add(d)
+        graph.add(e)
+        graph.add(f)
+
+        then:
+        1 * listener.onCreate(d)
+        0 * listener.onCreate(_)
+        0 * listener.onCreate(_)
+    }
+
+    def "listener can add listeners when node added"() {
+        def listener1 = Mock(ModelCreationListener)
+        def listener2 = Mock(ModelCreationListener)
+        def listener3 = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+
+        given:
+        listener2.matchPath() >> b.path
+        listener3.matchPath() >> b.path
+
+        when:
+        graph.add(a)
+        graph.addListener(listener1)
+        graph.add(b)
+
+        then:
+        1 * listener1.onCreate(graph.root)
+        1 * listener1.onCreate(a) >> { graph.addListener(listener2); false }
+        1 * listener1.onCreate(b) >> { graph.addListener(listener3); false }
+        1 * listener2.onCreate(b)
+        1 * listener3.onCreate(b)
+        0 * listener1.onCreate(_)
+        0 * listener2.onCreate(_)
+        0 * listener3.onCreate(_)
+    }
+
+    def "listener can add nodes that are consumed by other listeners"() {
+        def listener1 = Mock(ModelCreationListener)
+        def listener2 = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+        def c = node("c")
+        def d = node("d")
+
+        given:
+        graph.addListener(listener1)
+        listener2.matchPath() >> b.path
+        graph.addListener(listener2)
+
+        when:
+        graph.add(a)
+
+        then:
+        1 * listener1.onCreate(a) >> { graph.add(b); false }
+        1 * listener1.onCreate(b) >> { graph.add(c); false }
+        1 * listener1.onCreate(c) >> { graph.add(d); false }
+        1 * listener1.onCreate(d)
+        1 * listener2.onCreate(b) >> true
+        0 * listener1.onCreate(_)
+        0 * listener2.onCreate(_)
+    }
+
+    def "listener is not notified of nodes it creates after it signals it is done"() {
+        def listener1 = Mock(ModelCreationListener)
+        def listener2 = Mock(ModelCreationListener)
+        def a = node("a")
+        def b = node("b")
+        def c = node("c")
+        def d = node("d")
+
+        given:
+        graph.addListener(listener1)
+        graph.addListener(listener2)
+        listener2.onCreate(b) >> { graph.add(c); graph.add(d) }
+
+        when:
+        graph.add(a)
+
+        then:
+        1 * listener1.onCreate(a) >> {
+            graph.add(b);
+            true
+        }
+        0 * listener1.onCreate(_)
+    }
+
+    def node(String path, Class<?> type = String) {
+        return Stub(ModelNodeInternal) {
+            getPath() >> ModelPath.path(path)
+            getPromise() >> Stub(ModelPromise) {
+                canBeViewedAsWritable(_) >> { ModelType t -> return t.concreteClass == type }
+            }
+            toString() >> "node $path"
+        }
+    }
+
+    def root() {
+        return Stub(ModelNodeInternal) {
+            getPath() >> ModelPath.ROOT
+        }
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelNodeReplacementTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelNodeReplacementTest.groovy
new file mode 100644
index 0000000..cb0a617
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelNodeReplacementTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import spock.lang.Specification
+
+class ModelNodeReplacementTest extends Specification {
+
+    def registry = new ModelRegistryHelper()
+
+    def "can replace known node"() {
+        when:
+        registry.createInstance("foo", "foo")
+        registry.replace(registry.creator("foo").unmanaged("bar"))
+
+        then:
+        registry.get("foo") == "bar"
+    }
+
+    def "cannot replace realized node"() {
+        when:
+        registry.createInstance("foo", "foo")
+
+        then:
+        registry.get("foo") == "foo"
+
+        when:
+        registry.replace(registry.creator("foo").unmanaged("bar"))
+
+        then:
+        thrown IllegalStateException
+    }
+
+    def "cannot replace node with different type"() {
+        when:
+        registry.createInstance("foo", "foo")
+        registry.replace(registry.creator("foo").unmanaged(2))
+
+        then:
+        thrown IllegalStateException
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelPathSuggestionProviderTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelPathSuggestionProviderTest.groovy
new file mode 100644
index 0000000..8c86556
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelPathSuggestionProviderTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.model.internal.core.ModelPath
+import spock.lang.Specification
+
+class ModelPathSuggestionProviderTest extends Specification {
+
+    List<String> availablePaths
+
+    List<String> suggestionsFor(String path) {
+        new ModelPathSuggestionProvider(availablePaths.collect { new ModelPath(it) }).transform(new ModelPath(path))*.toString()
+    }
+
+    def "suggests model paths with Levenshtein distance lower than 4"() {
+        when:
+        availablePaths = ["task.afoobar", "tasks.boofar", "tasks.foobar", "tasks.f", "fooba"]
+
+        then:
+        suggestionsFor("tasks.fooba") == ["tasks.foobar", "task.afoobar", "tasks.boofar"]
+    }
+
+    def "suggests model paths with Levenshtein distance lower than half it's length for strings shorter than 6 characters"() {
+        when:
+        availablePaths = ["fo", "bor", "for", "of", "foob"]
+
+        then:
+        suggestionsFor("foo") == ["fo", "foob", "for"]
+        suggestionsFor("foor") == ["foob", "for", "bor", "fo"]
+        suggestionsFor("foora") == ["foob", "for"]
+    }
+
+    def "suggestions are ordered by distance then alphabetical"() {
+        when:
+        availablePaths = ["tasks.foobar", "task.afoobar", "tasks.f", "tasks.boofar", "fooba", "tasks.foo"]
+
+        then:
+        suggestionsFor("tasks.fooba") == ["tasks.foobar", "tasks.foo", "task.afoobar", "tasks.boofar"]
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelRegistryEphemeralNodeTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelRegistryEphemeralNodeTest.groovy
new file mode 100644
index 0000000..a7bb34b
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ModelRegistryEphemeralNodeTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.api.Transformer
+import org.gradle.internal.Factory
+import org.gradle.model.internal.core.*
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import spock.lang.Specification
+
+class ModelRegistryEphemeralNodeTest extends Specification {
+
+    def registry = new ModelRegistryHelper()
+
+    def "ephemeral model nodes are discarded when registry is reset"() {
+        when:
+        def events = []
+        registry.create("foo") { it.ephemeral(true).unmanaged(List, { [] } as Factory) }
+        registry.mutate(List) {
+            it.add "1"
+            events.add "mutate"
+        }
+
+        then:
+        registry.get("foo") == ["1"]
+        registry.node("foo").state == ModelNode.State.GraphClosed
+        events.size() == 1
+
+        when:
+        registry.prepareForReuse()
+
+        then:
+        registry.node("foo").state == ModelNode.State.Known
+        registry.get("foo") == ["1"]
+        events.size() == 2
+    }
+
+    def "dependents of ephemeral nodes are reset"() {
+        when:
+        def events = []
+        registry.create("foo") { it.ephemeral(true).unmanaged(List, { [] } as Factory) }
+        registry.create("bar") { it.ephemeral(false).unmanaged(Queue, { [] } as Factory) }
+        registry.mutate(List) {
+            it.add "1"
+            events.add "mutate foo"
+        }
+        registry.mutate {
+            it.path("bar").type(Queue).action(List) { bar, foo ->
+                bar.add(foo.first())
+                events.add "mutate bar"
+            }
+        }
+
+        then:
+        registry.get("bar") == ["1"]
+        registry.node("foo").state == ModelNode.State.GraphClosed
+        registry.node("bar").state == ModelNode.State.GraphClosed
+        events == ["mutate foo", "mutate bar"]
+
+        when:
+        registry.prepareForReuse()
+
+        then:
+        registry.node("foo").state == ModelNode.State.Known
+        registry.node("bar").state == ModelNode.State.Known
+        registry.get("foo") == ["1"]
+        events.size() == 3
+        registry.node("bar").state == ModelNode.State.Known
+        registry.get("bar") == ["1"]
+        events.size() == 4
+    }
+
+    static class Thing {
+        String name
+        String value
+    }
+
+    def "children of ephemeral collection nodes are implicitly ephemeral"() {
+        when:
+        def iType = DefaultCollectionBuilder.instantiatorTypeOf(Thing)
+        def iRef = ModelReference.of("instantiator", iType)
+
+        registry
+                .create(ModelCreators.bridgedInstance(iRef, { name, type -> new Thing(name: name) } as NamedEntityInstantiator).build())
+                .create("things") {
+            it.ephemeral(true).collection(Thing, iRef)
+        }
+        registry.mutateCollection("things", Thing) {
+            it.create("foo") {
+                it.value = "1"
+            }
+            it.create("bar")
+        }
+        registry.mutate(ModelReference.of("things.bar", Thing)) {
+            it.value = "2"
+        }
+
+        then:
+        registry.get("things")
+        registry.node("things.foo").state == ModelNode.State.GraphClosed
+        registry.node("things.bar").state == ModelNode.State.GraphClosed
+
+        when:
+        registry.prepareForReuse()
+
+        then:
+        registry.node("things").state == ModelNode.State.Known
+        registry.node("things.foo").state == ModelNode.State.Known
+        registry.node("things.bar").state == ModelNode.State.Known
+    }
+
+    def "nodes with creators dependent on ephemeral nodes are reset"() {
+            when:
+            def val = "1"
+            registry.create("foo") { it.ephemeral(true).unmanaged(List, { [] } as Factory) }
+            registry.create("bar") { it.ephemeral(false).unmanaged(Queue, List, { it } as Transformer) }
+            registry.mutate(List) {
+                it.add val
+            }
+
+            then:
+            registry.get("foo") == ["1"]
+            registry.get("bar") == ["1"]
+
+            when:
+            val = "2"
+            registry.prepareForReuse()
+
+            then:
+            registry.get("foo") == ["2"]
+            registry.get("bar") == ["2"]
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ScopedRuleTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ScopedRuleTest.groovy
new file mode 100644
index 0000000..5973e14
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/ScopedRuleTest.groovy
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.Model
+import org.gradle.model.Mutate
+import org.gradle.model.Path
+import org.gradle.model.RuleSource
+import org.gradle.model.collection.internal.HasDependencies
+import org.gradle.model.internal.core.DependencyOnlyExtractedModelRule
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelRuleExecutionException
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.inspect.AbstractAnnotationDrivenModelRuleExtractor
+import org.gradle.model.internal.inspect.DefaultModelCreatorFactory
+import org.gradle.model.internal.inspect.MethodModelRuleExtractors
+import org.gradle.model.internal.inspect.MethodRuleDefinition
+import org.gradle.model.internal.inspect.ModelRuleExtractor
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import spock.lang.Specification
+
+class ScopedRuleTest extends Specification {
+
+    def extractors = [new DependencyAddingModelRuleExtractor()] + MethodModelRuleExtractors.coreExtractors(DefaultModelSchemaStore.getInstance(), new DefaultModelCreatorFactory(DefaultModelSchemaStore.getInstance()))
+    def registry = new ModelRegistryHelper(new DefaultModelRegistry(new ModelRuleExtractor(extractors)))
+
+    static class RuleSourceUsingRuleWithDependencies extends RuleSource {
+        @HasDependencies
+        void rule() {}
+    }
+
+    class ImperativePlugin implements Plugin<Project> {
+        void apply(Project target) {
+        }
+    }
+
+    class DependencyAddingModelRuleExtractor extends AbstractAnnotationDrivenModelRuleExtractor<HasDependencies> {
+        @Override
+        def <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+            new DependencyOnlyExtractedModelRule([ImperativePlugin])
+        }
+    }
+
+    def "cannot apply a scoped rule that has dependencies"() {
+        registry.createInstance("values", "foo")
+                .apply("values", RuleSourceUsingRuleWithDependencies)
+
+        when:
+        registry.get("values")
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause.class == IllegalStateException
+        e.cause.message.startsWith "Rule source $RuleSourceUsingRuleWithDependencies cannot have plugin dependencies"
+    }
+
+    static class CreatorRule extends RuleSource {
+        @Model
+        String string() {
+            "foo"
+        }
+    }
+
+    def "cannot apply creator rules in scope other than root"() {
+        given:
+        registry.createInstance("values", "foo")
+                .apply("values", CreatorRule)
+
+        when:
+        registry.get("values")
+
+        then:
+        ModelRuleExecutionException e = thrown()
+        e.cause.class == InvalidModelRuleDeclarationException
+        e.cause.message == "Rule org.gradle.model.internal.registry.ScopedRuleTest\$CreatorRule#string() cannot be applied at the scope of model element values as creation rules cannot be used when applying rule sources to particular elements"
+    }
+
+    static class ByPathBoundInputsChildRule extends RuleSource {
+        @Mutate
+        void mutateFirst(@Path("first") MutableValue first) {
+            first.value = "first"
+        }
+
+        @Mutate
+        void mutateSecond(@Path("second") MutableValue second, @Path("first") MutableValue first) {
+            second.value = "from first: $first.value"
+        }
+    }
+
+    class MutableValue {
+        String value
+    }
+
+    def "by-path bindings of scoped rules are bound to inner scope"() {
+        given:
+        registry.createInstance("first", new MutableValue())
+                .createInstance("second", new MutableValue())
+                .createInstance("values", "foo")
+                .apply("values", ByPathBoundInputsChildRule)
+                .mutate {
+            it.path "values" node {
+                it.addLink(registry.instanceCreator("values.first", new MutableValue()))
+                it.addLink(registry.instanceCreator("values.second", new MutableValue()))
+            }
+        }
+
+        when:
+        registry.get("values")
+
+        then:
+        registry.get("first", MutableValue).value == null
+        registry.get("second", MutableValue).value == null
+        registry.get("values.first", MutableValue).value == "first"
+        registry.get("values.second", MutableValue).value == "from first: first"
+    }
+
+    static class ByTypeSubjectBoundToScopeChildRule extends RuleSource {
+        @Mutate
+        void mutateScopeChild(MutableValue value) {
+            value.value = "foo"
+        }
+    }
+
+    def "can bind subject by type to a child of rule scope"() {
+        given:
+        registry.createInstance("values", "foo")
+                .apply("values", ByTypeSubjectBoundToScopeChildRule)
+                .mutate {
+            it.path "values" node {
+                it.addLink(registry.instanceCreator("values.mutable", new MutableValue()))
+            }
+        }
+
+        when:
+        registry.get("values")
+
+        then:
+        registry.get("values.mutable", MutableValue).value == "foo"
+    }
+
+    static class ByTypeBindingSubjectRule extends RuleSource {
+        @Mutate
+        void connectElementToInput(MutableValue element, Integer input) {
+            element.value = input
+        }
+    }
+
+    def "by-type subject bindings are scoped to the scope of an inner rule"() {
+        given:
+        registry.createInstance("element", new MutableValue())
+                .createInstance("input", 10)
+                .createInstance("values", "foo")
+                .apply("values", ByTypeBindingSubjectRule)
+                .mutate {
+            it.path "values" node {
+                it.addLink(registry.instanceCreator("values.element", new MutableValue()))
+            }
+        }
+
+        when:
+        registry.get("values")
+
+        then:
+        registry.get("values.element", MutableValue).value == "10"
+        registry.get("element", MutableValue).value == null
+    }
+
+    static class ByTypeBindingInputRule extends RuleSource {
+        @Mutate
+        void byTypeInputBindingRule(MutableValue inner, MutableValue outer) {
+            inner.value = "from outer: $outer.value"
+        }
+    }
+
+    def "by-type input bindings are scoped to the outer scope"() {
+        given:
+        registry.createInstance("values", "foo")
+                .apply("values", ByTypeBindingInputRule)
+                .createInstance("element", new MutableValue(value: "outer"))
+                .mutate {
+            it.path "values" node {
+                it.addLink(registry.instanceCreator("values.element", new MutableValue()))
+            }
+        }
+
+        when:
+        registry.get("values")
+
+        then:
+        registry.get("values.element", MutableValue).value == "from outer: outer"
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/UnboundRulesProcessorTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/UnboundRulesProcessorTest.groovy
new file mode 100644
index 0000000..2f07b16
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/registry/UnboundRulesProcessorTest.groovy
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.registry
+
+import org.gradle.api.Nullable
+import org.gradle.api.Transformer
+import org.gradle.internal.BiActions
+import org.gradle.internal.Transformers
+import org.gradle.model.RuleSource
+import org.gradle.model.internal.core.*
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.report.unbound.UnboundRule
+import org.gradle.model.internal.report.unbound.UnboundRuleInput
+import org.gradle.model.internal.report.unbound.UnboundRulesReporter
+import org.gradle.model.internal.type.ModelType
+import org.gradle.util.ConfigureUtil
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+
+class UnboundRulesProcessorTest extends Specification {
+
+    List<RuleBinder> binders = []
+
+    Transformer<List<ModelPath>, ModelPath> suggestionProvider = Transformers.constant([])
+
+    String getReportForProcessedBinders() {
+        reportFor(new UnboundRulesProcessor(binders, suggestionProvider).process())
+    }
+
+    void binder(@DelegatesTo(RuleBinderTestBuilder) Closure config) {
+        binders << ConfigureUtil.configure(config, new RuleBinderTestBuilder()).build()
+    }
+
+    String reportFor(UnboundRule.Builder... rules) {
+        reportFor(rules.toList()*.build())
+    }
+
+    String reportFor(List<UnboundRule> rules) {
+        def writer = new StringWriter()
+        new UnboundRulesReporter(new PrintWriter(writer), "").reportOn(rules)
+        normaliseLineSeparators(writer.toString())
+    }
+
+    def "creates unbound rules for unfulfilled binders with unbound subject reference"() {
+        binder {
+            descriptor("ruleWithUnboundSubjectReference")
+            subjectReference("path.subject", String)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithUnboundSubjectReference")
+                        .mutableInput(UnboundRuleInput.type(String).path("path.subject"))
+        )
+    }
+
+    def "creates unbound rules for multiple binders"() {
+        binder {
+            descriptor("firstRule")
+            subjectReference("path.subject.first", String)
+        }
+        binder {
+            descriptor("secondRule")
+            subjectReference("path.subject.second", Number)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("firstRule")
+                        .mutableInput(UnboundRuleInput.type(String).path("path.subject.first")),
+                UnboundRule.descriptor("secondRule")
+                        .mutableInput(UnboundRuleInput.type(Number).path("path.subject.second"))
+        )
+    }
+
+    def "creates unbound rules for unfulfilled binders with unbound input references"() {
+        binder {
+            descriptor("ruleWithUnboundInputReferences")
+            inputReference("reference.first", String)
+            inputReference("reference.second", Number)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithUnboundInputReferences")
+                        .immutableInput(UnboundRuleInput.type(String).path("reference.first"))
+                        .immutableInput(UnboundRuleInput.type(Number).path("reference.second"))
+        )
+    }
+
+    def "creates unbound rules for unfulfilled binders with pathless references"() {
+        binder {
+            descriptor("ruleWithUnboundPathlessReferences")
+            subjectReference(Number)
+            inputReference(String)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithUnboundPathlessReferences")
+                        .mutableInput(UnboundRuleInput.type(Number))
+                        .immutableInput(UnboundRuleInput.type(String))
+        )
+    }
+
+    def "creates unbound rules for unfulfilled binders with bound subject reference and partially bound input references"() {
+        binder {
+            descriptor("partiallyBoundRule")
+            subjectReference("subject", Number)
+            inputReference("reference.first", Number)
+            inputReference(String)
+            inputReference("reference.third", Boolean)
+            bindSubjectReference("subject")
+            bindInputReference(0, "reference.first")
+            bindInputReference(1, "reference.second")
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("partiallyBoundRule")
+                        .mutableInput(UnboundRuleInput.type(Number).path("subject").bound())
+                        .immutableInput(UnboundRuleInput.type(Number).path("reference.first").bound())
+                        .immutableInput(UnboundRuleInput.type(String).path("reference.second").bound())
+                        .immutableInput(UnboundRuleInput.type(Boolean).path("reference.third"))
+        )
+    }
+
+    def "creates unbound rules with suggestions"() {
+        given:
+        binder {
+            descriptor("ruleWithSuggestions")
+            subjectReference("subject", Number)
+            inputReference(String)
+            inputReference("input.second", Boolean)
+            inputReference("input.third", Long)
+            bindInputReference(2, "input.third")
+        }
+
+        setSuggestionProvider { [it] }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithSuggestions")
+                        .mutableInput(UnboundRuleInput.type(Number).path("subject").suggestions("subject"))
+                        .immutableInput(UnboundRuleInput.type(String))
+                        .immutableInput(UnboundRuleInput.type(Boolean).path("input.second").suggestions("input.second"))
+                        .immutableInput(UnboundRuleInput.type(Long).path("input.third").bound())
+        )
+    }
+
+    def "creates scoped unbound rules with by-type bound subject"() {
+        binder {
+            descriptor("ruleWithUnboundSubjectReference")
+            scope("some.scope")
+            subjectReference(String)
+            inputReference(String)
+            inputReference("input", Boolean)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithUnboundSubjectReference")
+                        .mutableInput(UnboundRuleInput.type(String).scope("some.scope"))
+                        .immutableInput(UnboundRuleInput.type(String))
+                        .immutableInput(UnboundRuleInput.type(Boolean).path("some.scope.input"))
+        )
+    }
+
+    def "creates scoped unbound rules with by-path bound subject"() {
+        binder {
+            descriptor("ruleWithUnboundSubjectReference")
+            scope("some.scope")
+            subjectReference("subject", String)
+        }
+
+        expect:
+        reportForProcessedBinders == reportFor(
+                UnboundRule.descriptor("ruleWithUnboundSubjectReference")
+                        .mutableInput(UnboundRuleInput.type(String).path("some.scope.subject"))
+        )
+    }
+
+    private class RuleBinderTestBuilder {
+
+        private ModelRuleDescriptor descriptor
+        private ModelReference<?> subjectReference
+        private String subjectReferenceBindingPath
+        private List<ModelReference<?>> inputReferences = []
+        private Map<Integer, String> boundInputReferencePaths = [:]
+        private ModelPath scope = ModelPath.ROOT
+
+        void subjectReference(Class type) {
+            subjectReference = ModelReference.of(ModelType.of(type))
+        }
+
+        void subjectReference(String path, Class type) {
+            subjectReference = ModelReference.of(new ModelPath(path), ModelType.of(type))
+        }
+
+        void bindSubjectReference(String path) {
+            subjectReferenceBindingPath = path
+        }
+
+        void inputReference(Class type) {
+            inputReferences.add(ModelReference.of(ModelType.of(type)))
+        }
+
+        void inputReference(String path, Class type) {
+            inputReferences.add(ModelReference.of(new ModelPath(path), ModelType.of(type)))
+        }
+
+        void bindInputReference(int index, String path) {
+            boundInputReferencePaths[index] = path
+        }
+
+        void descriptor(String descriptor) {
+            this.descriptor = new SimpleModelRuleDescriptor(descriptor)
+        }
+
+        void scope(String path) {
+            scope = ModelPath.path(path)
+        }
+
+        RuleBinder build() {
+            def binder = new RuleBinder(inputReferences, descriptor, scope, []) {
+                @Override
+                ModelBinding<?> getSubjectBinding() {
+                    subjectReferenceBindingPath ? bind(getSubjectReference(), new TestNode(subjectReferenceBindingPath)) : null
+                }
+
+                @Override
+                ModelReference<?> getSubjectReference() {
+                    RuleBinderTestBuilder.this.subjectReference
+                }
+            }
+            boundInputReferencePaths.each { index, path ->
+                binder.bindInput(index, new TestNode(path))
+            }
+            return binder
+        }
+    }
+
+    private static class TestNode extends ModelNodeInternal {
+        TestNode(String creationPath) {
+            super(toBinder(creationPath))
+        }
+
+        private static CreatorRuleBinder toBinder(String creationPath) {
+            def creator = ModelCreators.of(ModelReference.of(creationPath), BiActions.doNothing()).descriptor("test").withProjection(EmptyModelProjection.INSTANCE).build()
+            def binder = new CreatorRuleBinder(creator, ModelPath.ROOT, [])
+            binder
+        }
+
+        @Override
+        ModelNodeInternal getTarget() {
+            return this
+        }
+
+        @Override
+        Iterable<? extends ModelNodeInternal> getLinks() {
+            return null
+        }
+
+        @Override
+        ModelNodeInternal addLink(ModelNodeInternal node) {
+            return null
+        }
+
+        @Override
+        def <T> ModelView<? extends T> asWritable(ModelType<T> type, ModelRuleDescriptor ruleDescriptor, List<ModelView<?>> implicitDependencies) {
+            return null
+        }
+
+        @Override
+        void addReference(ModelCreator creator) {
+
+        }
+
+        @Override
+        void addLink(ModelCreator creator) {
+
+        }
+
+        @Override
+        void removeLink(String name) {
+
+        }
+
+        @Override
+        def <T> void applyToSelf(ModelActionRole type, ModelAction<T> action) {
+
+        }
+
+        @Override
+        def <T> void applyToAllLinks(ModelActionRole type, ModelAction<T> action) {
+
+        }
+
+        @Override
+        def <T> void applyToLink(ModelActionRole type, ModelAction<T> action) {
+
+        }
+
+
+        @Override
+        def <T> void applyToLinks(Class<T> type, Class<? extends RuleSource> rules) {
+
+        }
+
+        @Override
+        void applyToLink(String name, Class<? extends RuleSource> rules) {
+
+        }
+
+        @Override
+        void applyToSelf(Class<? extends RuleSource> rules) {
+
+        }
+
+        @Override
+        MutableModelNode getLink(String name) {
+            return null
+        }
+
+        @Override
+        int getLinkCount(ModelType<?> type) {
+            return 0
+        }
+
+        @Override
+        Set<String> getLinkNames(ModelType<?> type) {
+            return null
+        }
+
+        @Override
+        Iterable<? extends MutableModelNode> getLinks(ModelType<?> type) {
+            return null
+        }
+
+        @Override
+        boolean hasLink(String name) {
+            return false
+        }
+
+        @Override
+        boolean hasLink(String name, ModelType<?> type) {
+            return false
+        }
+
+        @Override
+        def <T> void setPrivateData(ModelType<? super T> type, T object) {
+
+        }
+
+        @Override
+        def <T> T getPrivateData(ModelType<T> type) {
+            return null
+        }
+
+        @Override
+        Object getPrivateData() {
+            return null
+        }
+
+        @Override
+        void setTarget(ModelNode target) {
+
+        }
+
+        @Override
+        void ensureUsable() {
+
+        }
+
+        @Override
+        def <T> ModelView<? extends T> asReadOnly(ModelType<T> type, @Nullable ModelRuleDescriptor ruleDescriptor) {
+            return null
+        }
+    }
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/report/unbound/UnboundRulesReporterTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/report/unbound/UnboundRulesReporterTest.groovy
new file mode 100644
index 0000000..b026f75
--- /dev/null
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/report/unbound/UnboundRulesReporterTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report.unbound
+
+import org.gradle.util.TextUtil
+import spock.lang.Specification
+
+class UnboundRulesReporterTest extends Specification {
+
+    def output = new StringWriter()
+    def reporter = new UnboundRulesReporter(new PrintWriter(output), "> ")
+
+    def "reports on unbound rules"() {
+        when:
+        reporter.reportOn([
+                UnboundRule.descriptor("r1")
+                        .mutableInput(UnboundRuleInput.type(String).path("parent.p1"))
+                        .mutableInput(UnboundRuleInput.type(String).scope("some.scope"))
+                        .mutableInput(UnboundRuleInput.type(Integer).bound().path("parent.p3"))
+                        .immutableInput(UnboundRuleInput.type(Number).path("parent.p4").suggestions("parent.p31", "parent.p32"))
+                        .immutableInput(UnboundRuleInput.type(Number))
+                        .immutableInput(UnboundRuleInput.type(Number).bound().path("parent.p6")).build()
+        ])
+
+        then:
+        output.toString() == TextUtil.toPlatformLineSeparators("""> r1
+>   Mutable:
+>     - parent.p1 (java.lang.String)
+>     - <unspecified> (java.lang.String) in scope of 'some.scope'
+>     + parent.p3 (java.lang.Integer)
+>   Immutable:
+>     - parent.p4 (java.lang.Number) - suggestions: parent.p31, parent.p32
+>     - <unspecified> (java.lang.Number)
+>     + parent.p6 (java.lang.Number)""")
+    }
+}
diff --git a/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/internal/fixture/ModelRegistryHelper.java b/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/internal/fixture/ModelRegistryHelper.java
new file mode 100644
index 0000000..81baf15
--- /dev/null
+++ b/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/internal/fixture/ModelRegistryHelper.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.fixture;
+
+import org.gradle.api.Action;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.internal.*;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.inspect.DefaultModelCreatorFactory;
+import org.gradle.model.internal.inspect.MethodModelRuleExtractors;
+import org.gradle.model.internal.inspect.ModelRuleExtractor;
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore;
+import org.gradle.model.internal.registry.DefaultModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.registry.ModelRegistryScope;
+import org.gradle.model.internal.registry.UnboundModelRulesException;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.gradle.model.internal.core.ModelActionRole.Mutate;
+import static org.gradle.model.internal.core.ModelPath.nonNullValidatedPath;
+
+/**
+ * A helper for adding rules to a model registry.
+ *
+ * Allows unsafe use of the model registry by allow registering of rules that can close over external, unmanaged, state.
+ */
+public class ModelRegistryHelper implements ModelRegistry {
+
+    private final ModelRegistry modelRegistry;
+
+    public ModelRegistryHelper() {
+        this(new DefaultModelRegistry(new ModelRuleExtractor(MethodModelRuleExtractors.coreExtractors(DefaultModelSchemaStore.getInstance(), new DefaultModelCreatorFactory(DefaultModelSchemaStore.getInstance())))));
+    }
+
+    public ModelRegistryHelper(ModelRegistryScope modelRegistryScope) {
+        this(modelRegistryScope.getModelRegistry());
+    }
+
+    public ModelRegistryHelper(ModelRegistry modelRegistry) {
+        this.modelRegistry = modelRegistry;
+    }
+
+    @Override
+    public <T> T realize(ModelPath path, ModelType<T> type) {
+        return modelRegistry.realize(path, type);
+    }
+
+    @Override
+    @Nullable
+    public ModelNode atState(ModelPath path, ModelNode.State state) {
+        return modelRegistry.atState(path, state);
+    }
+
+    public ModelNode atState(String path, ModelNode.State state) {
+        return atState(ModelPath.path(path), state);
+    }
+
+    @Override
+    @Nullable
+    public ModelNode atStateOrLater(ModelPath path, ModelNode.State state) {
+        return modelRegistry.atStateOrLater(path, state);
+    }
+
+    @Override
+    public ModelNode.State state(ModelPath path) {
+        return modelRegistry.state(path);
+    }
+
+    @Override
+    @Nullable
+    public <T> T find(ModelPath path, ModelType<T> type) {
+        return modelRegistry.find(path, type);
+    }
+
+    @Override
+    public ModelNode realizeNode(ModelPath path) {
+        return modelRegistry.realizeNode(path);
+    }
+
+    public ModelNode realizeNode(String path) {
+        return realizeNode(ModelPath.path(path));
+    }
+
+    @Override
+    public void remove(ModelPath path) {
+        modelRegistry.remove(path);
+    }
+
+    @Override
+    public ModelRegistryHelper replace(ModelCreator newCreator) {
+        modelRegistry.replace(newCreator);
+        return this;
+    }
+
+    @Override
+    public void bindAllReferences() throws UnboundModelRulesException {
+        modelRegistry.bindAllReferences();
+    }
+
+    public ModelRegistry getModelRegistry() {
+        return modelRegistry;
+    }
+
+    public ModelActionBuilder<Object> action() {
+        return ModelActionBuilder.of();
+    }
+
+    public <C> ModelRegistryHelper createInstance(String path, final C c) {
+        return create(instanceCreator(path, c));
+    }
+
+    public <C> ModelRegistryHelper create(String path, final C c, Action<? super C> action) {
+        return create(creator(path, c, action));
+    }
+
+    private <C> ModelCreator creator(String path, C c, Action<? super C> action) {
+        return creator(path).unmanaged(c, action);
+    }
+
+    public ModelRegistryHelper create(ModelCreator creator) {
+        modelRegistry.create(creator);
+        return this;
+    }
+
+
+    @Override
+    public ModelRegistryHelper createOrReplace(ModelCreator newCreator) {
+        modelRegistry.createOrReplace(newCreator);
+        return this;
+    }
+
+    @Override
+    public <T> ModelRegistryHelper configure(ModelActionRole role, ModelAction<T> action) {
+        modelRegistry.configure(role, action);
+        return this;
+    }
+
+    @Override
+    public ModelRegistry apply(Class<? extends RuleSource> rules) {
+        return modelRegistry.apply(rules);
+    }
+
+    @Override
+    public ModelNode node(ModelPath path) {
+        return modelRegistry.node(path);
+    }
+
+    @Nullable
+    public ModelNode node(String path) {
+        return node(ModelPath.path(path));
+    }
+
+    @Override
+    public void prepareForReuse() {
+        modelRegistry.prepareForReuse();
+    }
+
+    public ModelRegistryHelper create(String path, Transformer<? extends ModelCreator, ? super ModelCreatorBuilder> def) {
+        return create(ModelPath.path(path), def);
+    }
+
+    public ModelRegistryHelper create(ModelPath path, Transformer<? extends ModelCreator, ? super ModelCreatorBuilder> def) {
+        modelRegistry.create(def.transform(creator(path)));
+        return this;
+    }
+
+    public ModelCreatorBuilder creator(String path) {
+        return creator(ModelPath.path(path));
+    }
+
+    public <I> ModelRegistryHelper collection(String path, final Class<I> itemType, final ModelReference<? extends NamedEntityInstantiator<I>> instantiator) {
+        return create(path, new Transformer<ModelCreator, ModelCreatorBuilder>() {
+            @Override
+            public ModelCreator transform(ModelCreatorBuilder modelCreatorBuilder) {
+                return modelCreatorBuilder.collection(itemType, instantiator);
+            }
+        });
+    }
+
+    public <I> ModelRegistryHelper mutateCollection(final String path, final Class<I> itemType, final Action<? super CollectionBuilder<I>> action) {
+        return mutate(new Transformer<ModelAction<?>, ModelActionBuilder<Object>>() {
+            @Override
+            public ModelAction<?> transform(ModelActionBuilder<Object> builder) {
+                return builder.path(path).type(DefaultCollectionBuilder.typeOf(ModelType.of(itemType))).action(action);
+            }
+        });
+    }
+
+    public ModelCreator creator(String path, Transformer<? extends ModelCreator, ? super ModelCreatorBuilder> action) {
+        return action.transform(creator(ModelPath.path(path)));
+    }
+
+    public <C> ModelCreator instanceCreator(String path, final C c) {
+        return creator(path).unmanaged(c);
+    }
+
+    public ModelCreatorBuilder creator(ModelPath path) {
+        return new ModelCreatorBuilder(path);
+    }
+
+    public ModelRegistryHelper configure(ModelActionRole role, Transformer<? extends ModelAction<?>, ? super ModelActionBuilder<Object>> def) {
+        return configure(role, def.transform(ModelActionBuilder.of()));
+    }
+
+    public ModelRegistryHelper mutate(Transformer<? extends ModelAction<?>, ? super ModelActionBuilder<Object>> def) {
+        return configure(Mutate, def);
+    }
+
+    public <T> ModelRegistryHelper mutate(Class<T> type, Action<? super T> action) {
+        return apply(Mutate, type, action);
+    }
+
+    public <T> ModelRegistryHelper mutate(ModelReference<T> reference, Action<? super T> action) {
+        return configure(Mutate, reference, action);
+    }
+
+    public ModelRegistryHelper mutate(final String path, final Action<? super MutableModelNode> action) {
+        return configure(Mutate, new Transformer<ModelAction<?>, ModelActionBuilder<Object>>() {
+            @Override
+            public ModelAction<?> transform(ModelActionBuilder<Object> objectModelActionBuilder) {
+                return objectModelActionBuilder.path(path).node(action);
+            }
+        });
+    }
+
+    public ModelRegistryHelper apply(String path, final Class<? extends RuleSource> rules) {
+        return mutate(path, new Action<MutableModelNode>() {
+            @Override
+            public void execute(MutableModelNode mutableModelNode) {
+                mutableModelNode.applyToSelf(rules);
+            }
+        });
+    }
+
+    private <T> ModelRegistryHelper apply(ModelActionRole role, final Class<T> type, final Action<? super T> action) {
+        return configure(role, ModelReference.of(type), action);
+    }
+
+    private <T> ModelRegistryHelper configure(ModelActionRole role, final ModelReference<T> reference, final Action<? super T> action) {
+        return configure(role, new Transformer<ModelAction<?>, ModelActionBuilder<Object>>() {
+            @Override
+            public ModelAction<?> transform(ModelActionBuilder<Object> objectModelActionBuilder) {
+                return objectModelActionBuilder.path(reference.getPath()).type(reference.getType()).action(action);
+            }
+        });
+    }
+
+    public <T> T get(String path, Class<T> type) {
+        return modelRegistry.realize(nonNullValidatedPath(path), ModelType.of(type));
+    }
+
+    public Object get(String path) {
+        return get(path, Object.class);
+    }
+
+    public Object get(ModelPath path) {
+        return get(path.toString());
+    }
+
+    public void realize(String path) {
+        modelRegistry.realize(nonNullValidatedPath(path), ModelType.UNTYPED);
+    }
+
+    public static <C> ModelCreator creator(String path, Class<C> type, String inputPath, final Transformer<? extends C, Object> action) {
+        return creator(path, ModelType.of(type), inputPath, action);
+    }
+
+    public static <C> ModelCreator creator(String path, final ModelType<C> modelType, String inputPath, final Transformer<? extends C, Object> action) {
+        return ModelCreators.of(ModelReference.of(path, modelType), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+            @Override
+            public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                mutableModelNode.setPrivateData(modelType, action.transform(inputs.get(0).getInstance()));
+            }
+        }).withProjection(new UnmanagedModelProjection<C>(modelType, true, true))
+                .inputs(refs(ModelReference.of(inputPath)))
+                .descriptor("create " + path)
+                .build();
+    }
+
+
+    private static List<ModelReference<?>> refs(ModelReference<?>... refType) {
+        return Arrays.asList(refType);
+    }
+
+    public static class ModelActionBuilder<T> {
+
+        private static final List<ModelReference<?>> NO_REFS = Collections.emptyList();
+
+        private ModelPath path;
+        private ModelType<T> type;
+        private ModelRuleDescriptor descriptor;
+
+        private ModelActionBuilder(ModelPath path, ModelType<T> type, ModelRuleDescriptor descriptor) {
+            this.path = path;
+            this.type = type;
+            this.descriptor = descriptor;
+        }
+
+        public static ModelActionBuilder<Object> of() {
+            return new ModelActionBuilder<Object>(null, ModelType.UNTYPED, new SimpleModelRuleDescriptor("testrule"));
+        }
+
+        private <N> ModelActionBuilder<N> copy(ModelType<N> type) {
+            return new ModelActionBuilder<N>(path, type, descriptor);
+        }
+
+        public ModelActionBuilder<T> path(String path) {
+            return this.path(ModelPath.path(path));
+        }
+
+        public ModelActionBuilder<T> path(ModelPath path) {
+            this.path = path;
+            return this;
+        }
+
+        public ModelActionBuilder<T> descriptor(String descriptor) {
+            return descriptor(new SimpleModelRuleDescriptor(descriptor));
+        }
+
+        public ModelActionBuilder<T> descriptor(ModelRuleDescriptor descriptor) {
+            this.descriptor = descriptor;
+            return this;
+        }
+
+        public <N> ModelActionBuilder<N> type(Class<N> type) {
+            return type(ModelType.of(type));
+        }
+
+        public <N> ModelActionBuilder<N> type(ModelType<N> type) {
+            return copy(type);
+        }
+
+        public ModelAction<T> action(final Action<? super T> action) {
+            return build(NO_REFS, new TriAction<MutableModelNode, T, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, T t, List<ModelView<?>> inputs) {
+                    action.execute(t);
+                }
+            });
+        }
+
+        public ModelAction<T> node(final Action<? super MutableModelNode> action) {
+            return build(NO_REFS, new TriAction<MutableModelNode, T, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, T t, List<ModelView<?>> inputs) {
+                    action.execute(mutableModelNode);
+                }
+            });
+        }
+
+        public <I> ModelAction<T> action(ModelPath modelPath, ModelType<I> inputType, BiAction<? super T, ? super I> action) {
+            return action(modelPath, inputType, inputType.toString(), action);
+        }
+
+        public <I> ModelAction<T> action(String modelPath, ModelType<I> inputType, BiAction<? super T, ? super I> action) {
+            return action(modelPath, inputType, modelPath, action);
+        }
+
+        public <I> ModelAction<T> action(final ModelPath modelPath, final ModelType<I> inputType, String referenceDescription, final BiAction<? super T, ? super I> action) {
+            return build(refs(ModelReference.of(modelPath, inputType, referenceDescription)), new TriAction<MutableModelNode, T, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, T t, List<ModelView<?>> inputs) {
+                    action.execute(t, ModelViews.assertType(inputs.get(0), inputType).getInstance());
+                }
+            });
+        }
+
+        public <I> ModelAction<T> action(final String modelPath, final ModelType<I> inputType, String referenceDescription, final BiAction<? super T, ? super I> action) {
+            return action(ModelPath.path(modelPath), inputType, referenceDescription, action);
+        }
+
+        public <I> ModelAction<T> action(final ModelType<I> inputType, final BiAction<? super T, ? super I> action) {
+            return action((ModelPath) null, inputType, action);
+        }
+
+        public <I> ModelAction<T> action(final Class<I> inputType, final BiAction<? super T, ? super I> action) {
+            return action(ModelType.of(inputType), action);
+        }
+
+        private ModelAction<T> build(List<ModelReference<?>> references, TriAction<? super MutableModelNode, ? super T, ? super List<ModelView<?>>> action) {
+            return toAction(references, action, path, type, descriptor);
+        }
+
+        private static <T> ModelAction<T> toAction(final List<ModelReference<?>> references, final TriAction<? super MutableModelNode, ? super T, ? super List<ModelView<?>>> action, final ModelPath path, final ModelType<T> type, final ModelRuleDescriptor descriptor) {
+            return new ModelAction<T>() {
+                @Override
+                public ModelReference<T> getSubject() {
+                    return ModelReference.of(path, type);
+                }
+
+                @Override
+                public void execute(MutableModelNode modelNode, T object, List<ModelView<?>> inputs) {
+                    action.execute(modelNode, object, inputs);
+                }
+
+                @Override
+                public List<ModelReference<?>> getInputs() {
+                    return references;
+                }
+
+                @Override
+                public ModelRuleDescriptor getDescriptor() {
+                    return descriptor;
+                }
+            };
+        }
+    }
+
+    public static class ModelCreatorBuilder {
+        private final ModelPath path;
+        private boolean ephemeral;
+        private ModelRuleDescriptor descriptor = new SimpleModelRuleDescriptor("tester");
+
+        public ModelCreatorBuilder(ModelPath path) {
+            this.path = path;
+            descriptor = new SimpleModelRuleDescriptor(path + " creator");
+        }
+
+        public ModelCreatorBuilder descriptor(String descriptor) {
+            return descriptor(new SimpleModelRuleDescriptor(descriptor));
+        }
+
+        public ModelCreatorBuilder ephemeral(boolean flag) {
+            this.ephemeral = flag;
+            return this;
+        }
+
+        public ModelCreatorBuilder descriptor(ModelRuleDescriptor descriptor) {
+            this.descriptor = descriptor;
+            return this;
+        }
+
+        public <C> ModelCreator unmanaged(final Class<C> modelType, String inputPath, final Transformer<? extends C, Object> action) {
+            return unmanaged(modelType, inputPath, inputPath, action);
+        }
+
+        public <C> ModelCreator unmanaged(final Class<C> modelType, String inputPath, String referenceDescription, final Transformer<? extends C, Object> action) {
+            return unmanaged(ModelType.of(modelType), inputPath, referenceDescription, action);
+        }
+
+        public <C> ModelCreator unmanaged(final ModelType<C> modelType, String inputPath, final Transformer<? extends C, Object> action) {
+            return unmanaged(modelType, inputPath, inputPath, action);
+        }
+
+        public <C> ModelCreator unmanaged(final ModelType<C> modelType, String inputPath, String inputDescriptor, final Transformer<? extends C, Object> action) {
+            return ModelCreators.of(ModelReference.of(path), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                    mutableModelNode.setPrivateData(modelType, action.transform(inputs.get(0).getInstance()));
+                }
+            }).withProjection(new UnmanagedModelProjection<C>(modelType, true, true))
+                    .inputs(ModelReference.of(inputPath, ModelType.UNTYPED, inputDescriptor))
+                    .descriptor(descriptor)
+                    .ephemeral(ephemeral)
+                    .build();
+        }
+
+        public <C, I> ModelCreator unmanaged(Class<C> type, final Class<I> inputType, final Transformer<? extends C, ? super I> action) {
+            return unmanaged(ModelType.of(type), ModelType.of(inputType), action);
+        }
+
+        public <C, I> ModelCreator unmanaged(final ModelType<C> modelType, final ModelType<I> inputModelType, final Transformer<? extends C, ? super I> action) {
+            return ModelCreators.of(ModelReference.of(path, modelType), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                    mutableModelNode.setPrivateData(modelType, action.transform(ModelViews.assertType(inputs.get(0), inputModelType).getInstance()));
+                }
+            }).withProjection(new UnmanagedModelProjection<C>(modelType, true, true))
+                    .inputs(ModelReference.of(inputModelType))
+                    .ephemeral(ephemeral)
+                    .descriptor(descriptor)
+                    .build();
+        }
+
+        public <C> ModelCreator unmanaged(Class<C> type, final Factory<? extends C> initializer) {
+            return unmanaged(ModelType.of(type), initializer);
+        }
+
+        public <C> ModelCreator unmanaged(Class<C> type, final C c) {
+            return unmanaged(ModelType.of(type), Factories.constant(c));
+        }
+
+        private <C> ModelCreator unmanaged(final ModelType<C> modelType, final Factory<? extends C> initializer) {
+            return ModelCreators.of(ModelReference.of(path), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                    mutableModelNode.setPrivateData(modelType, initializer.create());
+                }
+            }).withProjection(new UnmanagedModelProjection<C>(modelType, true, true))
+                    .descriptor(descriptor)
+                    .ephemeral(ephemeral)
+                    .build();
+        }
+
+        public <C> ModelCreator unmanagedNode(Class<C> modelType, final Action<? super MutableModelNode> action) {
+            return unmanagedNode(ModelType.of(modelType), action);
+        }
+
+        public <C> ModelCreator unmanagedNode(ModelType<C> modelType, final Action<? super MutableModelNode> action) {
+            return ModelCreators.of(ModelReference.of(path), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode mutableModelNode, List<ModelView<?>> inputs) {
+                    action.execute(mutableModelNode);
+                }
+            }).withProjection(new UnmanagedModelProjection<C>(modelType, true, true))
+                    .descriptor(descriptor)
+                    .ephemeral(ephemeral)
+                    .build();
+        }
+
+        public <C> ModelCreator unmanaged(C c) {
+            return unmanaged(c, Actions.doNothing());
+        }
+
+        public <C> ModelCreator unmanaged(final C c, final Action<? super C> action) {
+            return unmanaged(ModelType.typeOf(c).getConcreteClass(), new Factory<C>() {
+                @Override
+                public C create() {
+                    action.execute(c);
+                    return c;
+                }
+            });
+        }
+
+        public <I> ModelCreator collection(Class<I> itemType, final ModelReference<? extends NamedEntityInstantiator<? super I>> instantiator) {
+            final ModelType<I> itemModelType = ModelType.of(itemType);
+            final ModelType<CollectionBuilder<I>> collectionBuilderType = DefaultCollectionBuilder.typeOf(itemModelType);
+
+            return ModelCreators.of(ModelReference.of(path, collectionBuilderType), new BiAction<MutableModelNode, List<ModelView<?>>>() {
+                @Override
+                public void execute(MutableModelNode node, List<ModelView<?>> inputs) {
+                    node.setPrivateData(
+                            collectionBuilderType,
+                            new DefaultCollectionBuilder<I>(itemModelType, descriptor, node, DefaultCollectionBuilder.createVia(instantiator))
+                    );
+                }
+            })
+                    .withProjection(new UnmanagedModelProjection<CollectionBuilder<I>>(collectionBuilderType, true, true))
+                    .descriptor(descriptor)
+                    .ephemeral(ephemeral)
+                    .build();
+        }
+    }
+
+}
diff --git a/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/report/unbound/UnboundRulesReportMatchers.groovy b/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/report/unbound/UnboundRulesReportMatchers.groovy
new file mode 100644
index 0000000..9a10091
--- /dev/null
+++ b/subprojects/model-core/src/testFixtures/groovy/org/gradle/model/report/unbound/UnboundRulesReportMatchers.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.report.unbound
+
+import org.gradle.model.internal.report.unbound.UnboundRule
+import org.gradle.model.internal.report.unbound.UnboundRulesReporter
+import org.hamcrest.Matcher
+
+import static org.gradle.util.Matchers.normalizedLineSeparators
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+import static org.hamcrest.Matchers.equalTo
+
+class UnboundRulesReportMatchers {
+
+    static Matcher<String> unbound(UnboundRule.Builder... rules) {
+        def string = new StringWriter()
+        def writer = new PrintWriter(string)
+        writer.println("The following model rules are unbound:")
+        def reporter = new UnboundRulesReporter(writer, "  ")
+        reporter.reportOn(rules.toList()*.build())
+
+        normalizedLineSeparators(equalTo(normaliseLineSeparators(string.toString())))
+    }
+}
diff --git a/subprojects/model-groovy/model-groovy.gradle b/subprojects/model-groovy/model-groovy.gradle
new file mode 100644
index 0000000..1c0290c
--- /dev/null
+++ b/subprojects/model-groovy/model-groovy.gradle
@@ -0,0 +1,33 @@
+
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Groovy specific adaptions to the model management.
+ */
+apply plugin: "groovy"
+
+dependencies {
+    compile libraries.slf4j_api
+    compile project(':baseServicesGroovy')
+    compile project(':modelCore')
+    compile libraries.groovy
+}
+
+useTestFixtures()
+useTestFixtures(project: ':modelCore', sourceSet: 'test')
+useClassycle()
+strictCompile()
diff --git a/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslCreationIntegrationTest.groovy b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslCreationIntegrationTest.groovy
new file mode 100644
index 0000000..44744bb
--- /dev/null
+++ b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslCreationIntegrationTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+import static org.gradle.util.Matchers.containsText
+
+class ModelDslCreationIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "can create elements"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            @Managed
+            interface Thing {
+                String getName()
+                void setName(String name)
+            }
+
+            model {
+                thing1(Thing) {
+                    name = "foo"
+                }
+                thing2(Thing) {
+                    name = \$("thing1.name") + " bar"
+                }
+                tasks {
+                    create("echo") {
+                        doLast {
+                            println "thing2.name: " + \$("thing2.name")
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "echo"
+        output.contains "thing2.name: foo bar"
+    }
+
+    def "can create elements without mutating"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            @Managed
+            interface Thing {
+                String getName()
+                void setName(String name)
+            }
+
+            model {
+                thing1(Thing)
+                tasks {
+                    create("echo") {
+                        doLast {
+                            println "thing1.name: " + \$("thing1.name")
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "echo"
+        output.contains "thing1.name: null"
+    }
+
+    def "cannot create non managed types"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            interface Thing {
+                String getName()
+                void setName(String name)
+            }
+
+            model {
+                thing1(Thing) {
+                    name = "foo"
+                }
+                tasks {
+                    create("echo") {
+                        doLast {
+                            println "thing1.name: " + \$("thing1.name")
+                        }
+                    }
+                }
+            }
+        """
+
+        then:
+        fails "dependencies" // something that doesn't actually require thing1 to be built
+        failure.assertThatCause(containsText("model.thing1 @ build file"))
+        failure.assertThatCause(containsText("Cannot create an element of type Thing as it is not a managed type"))
+    }
+
+}
diff --git a/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslIntegrationTest.groovy b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslIntegrationTest.groovy
new file mode 100644
index 0000000..e23105d
--- /dev/null
+++ b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/ModelDslIntegrationTest.groovy
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import org.gradle.model.dsl.internal.transform.RulesVisitor
+
+import static org.hamcrest.Matchers.containsString
+
+/**
+ * Tests the fundamental usages of the model dsl.
+ *
+ * Boundary tests for the transform and specialised cases should go in other dedicated test classes.
+ */
+class ModelDslIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    def "rule inputs can be referenced in closures that are not executed during rule execution"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                String foo() {
+                  "foo"
+                }
+
+                @Model
+                List<String> strings() {
+                  []
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            model {
+              tasks {
+                create("printStrings") {
+                  doLast {
+                    // Being in doLast is significant here.
+                    // This is not going to execute until much later, so we are testing that we can still access the input
+                    println "strings: " + \$("strings")
+                  }
+                }
+              }
+              strings {
+                add \$("foo")
+              }
+            }
+        """
+
+        then:
+        succeeds "printStrings"
+        output.contains "strings: " + ["foo"]
+    }
+
+    def "inputs are fully configured when used in rules"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                List<String> strings() {
+                  []
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            model {
+              tasks {
+                create("printStrings") {
+                  doLast {
+                    println "strings: " + \$("strings")
+                  }
+                }
+              }
+              strings {
+                add "foo"
+              }
+              strings {
+                add "bar"
+              }
+            }
+        """
+
+        then:
+        succeeds "printStrings"
+        output.contains "strings: " + ["foo", "bar"]
+    }
+
+    def "the same input can be referenced more than once, and refers to the same object"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                List<String> strings() {
+                  []
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            model {
+              tasks {
+                create("assertDuplicateInputIsSameObject") {
+                  doLast {
+                    assert \$("strings").is(\$("strings"))
+                  }
+                }
+              }
+            }
+        """
+
+        then:
+        succeeds "assertDuplicateInputIsSameObject"
+    }
+
+    def "can use model block in script plugin"() {
+        given:
+        settingsFile << "include 'a'; include 'b'"
+        when:
+
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                String foo() {
+                  "foo"
+                }
+
+                @Model
+                List<String> strings() {
+                  []
+                }
+              }
+            }
+
+            subprojects {
+                apply type: MyPlugin
+                apply from: "\$rootDir/script.gradle"
+            }
+        """
+        file("a/build.gradle") << """
+            model {
+              strings { add "a" }
+            }
+        """
+        file("b/build.gradle") << """
+            model {
+              strings { add "b" }
+            }
+        """
+        file("script.gradle") << """
+            model {
+              tasks {
+                create("printStrings") {
+                  doLast {
+                    println project.name + ": " + \$("strings")
+                  }
+                }
+              }
+              strings {
+                add \$("foo")
+              }
+            }
+        """
+
+        then:
+        succeeds "printStrings"
+        output.contains "a: " + ["foo", "a"]
+        output.contains "b: " + ["foo", "b"]
+    }
+
+    def "only closure literals can be used as rules"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                String foo() {
+                  "foo"
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            def c = {};
+            model {
+                foo(c)
+            }
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber 17
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(RulesVisitor.INVALID_RULE_SIGNATURE))
+    }
+
+}
diff --git a/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleDetectionIntegrationSpec.groovy b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleDetectionIntegrationSpec.groovy
new file mode 100644
index 0000000..9d47f54
--- /dev/null
+++ b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleDetectionIntegrationSpec.groovy
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import spock.lang.Unroll
+
+import static org.hamcrest.Matchers.containsString
+
+class ModelDslRuleDetectionIntegrationSpec extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    @Unroll
+    def "rules are detected when model path is a straight property reference chain - #path"() {
+        given:
+        def normalisedPath = path.replace('"', '').replaceAll("'", "")
+
+        when:
+        buildScript """
+            import org.gradle.model.collection.*
+
+            @Managed
+            interface Item {
+                String getValue()
+                void setValue(String value)
+            }
+
+            @Managed
+            interface A extends Item {
+                B getB()
+            }
+
+            @Managed
+            interface B extends Item {
+                C getC()
+            }
+
+            @Managed
+            interface C extends Item {
+                D getD()
+            }
+
+            @Managed
+            interface D extends Item {
+            }
+
+            class MyPlugin extends RuleSource {
+                @Model
+                void a(A a) { }
+
+                @Mutate
+                void addTask(CollectionBuilder<Task> tasks, @Path("$normalisedPath") Item item) {
+                    tasks.create("printValue") {
+                        it.doLast {
+                            println "value: " + item.value
+                        }
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                $path {
+                  value = "foo"
+                }
+            }
+        """
+
+        then:
+        succeeds "printValue"
+        output.contains("value: foo")
+
+        where:
+        path << [
+                "a",
+                "a.b",
+                "a.b.c",
+                "a.b.c.d",
+                'a."b".c."d"'
+        ]
+    }
+
+    @Unroll
+    def "only literal property paths are allowed - #pathCode"() {
+        when:
+        buildScript """
+            model {
+                $pathCode {
+
+                }
+            }
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber 3
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(RulesVisitor.INVALID_STATEMENT))
+
+        where:
+        pathCode << [
+                '"a" + "a"',
+                'foo.bar().baz',
+                'foo["bar"]',
+                'foo["bar"].baz',
+                'def a = b; b',
+        ]
+    }
+
+    @Unroll
+    def "only rules are allowed in the model block - #code"() {
+        when:
+        buildScript """
+            model {
+                $code
+            }
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber 3
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(RulesVisitor.INVALID_STATEMENT))
+
+        where:
+        code << [
+                'def a = "foo"',
+                'if (true) {}',
+                'try {} catch(e) {}',
+        ]
+    }
+}
diff --git a/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleInputDetectionIntegrationSpec.groovy b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleInputDetectionIntegrationSpec.groovy
new file mode 100644
index 0000000..179c7ab
--- /dev/null
+++ b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/ModelDslRuleInputDetectionIntegrationSpec.groovy
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import org.gradle.model.internal.report.unbound.UnboundRule
+import org.gradle.model.internal.report.unbound.UnboundRuleInput
+import spock.lang.Unroll
+
+import static org.gradle.model.report.unbound.UnboundRulesReportMatchers.unbound
+import static org.hamcrest.Matchers.containsString
+
+class ModelDslRuleInputDetectionIntegrationSpec extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        EnableModelDsl.enable(executer)
+    }
+
+    @Unroll
+    def "only literal strings can be given to dollar - #code"() {
+        when:
+        buildScript """
+        model {
+          foo {
+            $code
+          }
+        }
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber 4
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(RuleVisitor.INVALID_ARGUMENT_LIST))
+
+        where:
+        code << [
+                '$(1)',
+                '$("$name")',
+                '$("a" + "b")',
+                'def a = "foo"; $(a)',
+                '$("foo", "bar")',
+                '$()',
+                '$(null)',
+                '$("")'
+        ]
+    }
+
+    @Unroll
+    def "dollar method is only detected with no explicit receiver - #code"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                String foo() {
+                  "foo"
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            model {
+              foo {
+                $code
+              }
+            }
+        """
+
+        then:
+        succeeds "tasks" // succeeds because we don't fail on invalid usage, and don't fail due to unbound inputs
+
+        where:
+        code << [
+                'something.$(1)',
+//                'this.$("$name")',
+//                'foo.bar().$("a" + "b")',
+        ]
+    }
+
+    def "input references are found in nested code - #code"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Mutate void addPrintTask(CollectionBuilder<Task> tasks, List<String> strings) {
+                        tasks.create("printMessage", PrintTask) {
+                            it.message = strings
+                        }
+                    }
+
+                    @Model String foo() {
+                        "foo"
+                    }
+
+                    @Model List<String> strings() {
+                        []
+                    }
+                }
+            }
+
+            class PrintTask extends DefaultTask {
+                String message
+
+                @TaskAction
+                void doPrint() {
+                    println "message: " + message
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                strings {
+                    $code
+                }
+            }
+        """
+
+        then:
+        succeeds "printMessage"
+        output.contains("message: [foo]")
+
+        where:
+        code << [
+                'if (true) { add $("foo") }',
+                'if (false) {} else if (true) { add $("foo") }',
+                'if (false) {} else { add $("foo") }',
+                'def i = true; while(i) { add $("foo"); i = false }',
+                '[1].each { add $("foo") }',
+                'add "${$("foo")}"',
+                'def v = $("foo"); add(v)',
+                'add($("foo"))',
+                'add($("foo").toString())',
+        ]
+    }
+
+    def "input model path must be valid"() {
+        when:
+        buildScript """
+            import org.gradle.model.*
+
+            class MyPlugin {
+              static class Rules extends RuleSource {
+                @Model
+                List<String> strings() {
+                  []
+                }
+              }
+            }
+
+            apply type: MyPlugin
+
+            model {
+              tasks {
+                \$("foo. bar") // line 21
+              }
+            }
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber(17)
+        failure.assertThatCause(containsString("Invalid model path given as rule input."))
+        failure.assertThatCause(containsString("Model path 'foo. bar' is invalid due to invalid name component."))
+        failure.assertThatCause(containsString("Model element name ' bar' has illegal first character ' ' (names must start with an ASCII letter or underscore)."))
+    }
+
+    def "location and suggestions are provided for unbound rule inputs specified using a name"() {
+        given:
+        buildScript '''
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+
+            class MyPlugin {
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void addTasks(CollectionBuilder<Task> tasks) {
+                        tasks.create("foobar")
+                        tasks.create("raboof")
+                    }
+                }
+            }
+
+            apply type: MyPlugin
+
+            model {
+                foo {
+                    $('tasks.foonar')
+                    $('tasks.fooar')
+                    $('tasks.foonar')
+                }
+            }
+        '''
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertThatCause(unbound(
+                UnboundRule.descriptor("model.foo", buildFile, 18, 17)
+                        .mutableInput(UnboundRuleInput.type(Object).path("foo"))
+                        .immutableInput(UnboundRuleInput.type(Object).path("tasks.foonar").suggestions("tasks.foobar").description("@ line 19"))
+                        .immutableInput(UnboundRuleInput.type(Object).path("tasks.fooar").suggestions("tasks.foobar").description("@ line 20"))
+        ))
+    }
+
+    def "can not access project or script from rule"() {
+        when:
+        buildScript """
+            model {
+                tasks {
+                    assert owner == null
+                    assert this == null
+
+                    try {
+                        project.tasks
+                        assert false : "should not reach here"
+                    } catch (MissingPropertyException ignore) {
+
+                    }
+                }
+            }
+        """
+
+        then:
+        succeeds "tasks"
+    }
+}
diff --git a/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/NestedModelDslUsageIntegrationSpec.groovy b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/NestedModelDslUsageIntegrationSpec.groovy
new file mode 100644
index 0000000..704f824
--- /dev/null
+++ b/subprojects/model-groovy/src/integTest/groovy/org/gradle/model/dsl/internal/transform/NestedModelDslUsageIntegrationSpec.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+import org.gradle.model.dsl.internal.NonTransformedModelDslBacking
+import spock.lang.Unroll
+
+import static org.hamcrest.Matchers.containsString
+
+class NestedModelDslUsageIntegrationSpec extends AbstractIntegrationSpec {
+
+    def setup() {
+        EnableModelDsl.enable(executer)
+    }
+
+    @Unroll
+    def "model block can be used in nested context in build script - #code"() {
+        given:
+        settingsFile << "include 'a', 'b'"
+
+        when:
+        buildScript """
+            ${testPluginImpl()}
+
+            allprojects { apply type: TestPlugin }
+
+            $code {
+                model {
+                    strings {
+                        add "foo"
+                    }
+                }
+            }
+
+        """
+
+        then:
+        succeeds "printStrings"
+        output.contains "strings: [foo]"
+
+        where:
+        code << [
+                "subprojects",
+                "project(':a')",
+                "if (true)"
+        ]
+    }
+
+    def "model block can be used from init script"() {
+        when:
+        file("init.gradle") << """
+            ${testPluginImpl()}
+
+            allprojects {
+                apply type: TestPlugin
+
+                model {
+                    strings {
+                        add "foo"
+                    }
+                }
+            }
+        """
+
+        then:
+        args("-I", file("init.gradle").absolutePath)
+        succeeds "printStrings"
+        output.contains "strings: [foo]"
+    }
+
+    @Unroll
+    def "model block rules in nested context cannot use inputs - #code"() {
+        given:
+        settingsFile << "include 'a', 'b'"
+
+        when:
+        buildScript """
+            ${testPluginImpl()}
+
+            allprojects { apply type: TestPlugin }
+
+            $code {
+                model {
+                    strings {
+                        add \$("foo")
+                    }
+                }
+            }
+
+        """
+
+        then:
+        fails "printStrings"
+        failure.assertHasCause(NonTransformedModelDslBacking.ATTEMPTED_INPUT_SYNTAX_USED_MESSAGE)
+
+        where:
+        code << [
+                "subprojects",
+                "project(':a')",
+                "if (true)"
+        ]
+    }
+
+    def "model block used in init script cannot use inputs"() {
+        when:
+        file("init.gradle") << """
+            ${testPluginImpl()}
+
+            allprojects {
+                apply type: TestPlugin
+
+                model {
+                     strings {
+                        add \$("foo")
+                    }
+                }
+            }
+        """
+
+        then:
+        args("-I", file("init.gradle").absolutePath)
+        fails "printStrings"
+        failure.assertHasCause(NonTransformedModelDslBacking.ATTEMPTED_INPUT_SYNTAX_USED_MESSAGE)
+    }
+
+    def "model block must received transformed closure"() {
+        when:
+        buildScript """
+            ${testPluginImpl()}
+            apply type: TestPlugin
+
+
+            def c = {
+                strings {
+                    add \$("foo")
+                }
+            }
+
+            model(c)
+        """
+
+        then:
+        fails "tasks"
+        failure.assertHasLineNumber 22
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(ModelBlockTransformer.NON_LITERAL_CLOSURE_TO_TOP_LEVEL_MODEL_MESSAGE))
+    }
+
+    String testPluginImpl() {
+        return """
+            class TestPlugin {
+                static class Rules extends org.gradle.model.RuleSource {
+                    @org.gradle.model.Model String foo() { "foo" }
+                    @org.gradle.model.Model List<String> strings() { [] }
+                    @org.gradle.model.Mutate void addTask(org.gradle.model.collection.CollectionBuilder<Task> tasks, List<String> strings) {
+                        tasks.create("printStrings") { it.doLast { println "strings: " + strings } }
+                    }
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/NonTransformedModelDslBacking.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/NonTransformedModelDslBacking.java
new file mode 100644
index 0000000..72bb2bf
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/NonTransformedModelDslBacking.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.MissingMethodException;
+import groovy.lang.MissingPropertyException;
+import net.jcip.annotations.NotThreadSafe;
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+ at NotThreadSafe
+public class NonTransformedModelDslBacking extends GroovyObjectSupport {
+
+    // TODO include link to documentation giving more explanation of what's going on here.
+    public static final String ATTEMPTED_INPUT_SYNTAX_USED_MESSAGE = "$() syntax cannot be used when model {} block is not a top level statement in the script";
+
+    private final ModelPath modelPath;
+    private final ModelRegistry modelRegistry;
+    private final ModelSchemaStore modelSchemaStore;
+    private final ModelCreatorFactory modelCreatorFactory;
+    private AtomicBoolean executingDsl;
+
+    public NonTransformedModelDslBacking(ModelRegistry modelRegistry, ModelSchemaStore modelSchemaStore, ModelCreatorFactory modelCreatorFactory) {
+        this(new AtomicBoolean(), null, modelRegistry, modelSchemaStore, modelCreatorFactory);
+    }
+
+    private NonTransformedModelDslBacking(AtomicBoolean executingDsl, ModelPath modelPath, ModelRegistry modelRegistry, ModelSchemaStore modelSchemaStore, ModelCreatorFactory modelCreatorFactory) {
+        this.executingDsl = executingDsl;
+        this.modelPath = modelPath;
+        this.modelRegistry = modelRegistry;
+        this.modelSchemaStore = modelSchemaStore;
+        this.modelCreatorFactory = modelCreatorFactory;
+    }
+
+    private NonTransformedModelDslBacking getChildPath(String name) {
+        ModelPath path = modelPath == null ? ModelPath.path(name) : modelPath.child(name);
+        return new NonTransformedModelDslBacking(executingDsl, path, modelRegistry, modelSchemaStore, modelCreatorFactory);
+    }
+
+    private void registerConfigurationAction(final Closure<?> action) {
+        modelRegistry.configure(ModelActionRole.Mutate,
+                new ActionBackedModelAction<Object>(
+                        ModelReference.untyped(modelPath),
+                        new SimpleModelRuleDescriptor("model." + modelPath), new ClosureBackedAction<Object>(action)
+                ));
+    }
+
+    private <T> void registerCreator(Class<T> type, Closure<?> closure) {
+        ModelRuleDescriptor descriptor = new SimpleModelRuleDescriptor("model." + modelPath);
+        ModelSchema<T> schema = modelSchemaStore.getSchema(ModelType.of(type));
+        if (!schema.getKind().isManaged()) {
+            throw new InvalidModelRuleDeclarationException(descriptor, "Cannot create an element of type " + type.getName() + " as it is not a managed type");
+        }
+
+        modelRegistry.create(modelCreatorFactory.creator(descriptor, modelPath, schema, new ClosureBackedAction<T>(closure)));
+    }
+
+    public void configure(Closure<?> action) {
+        executingDsl.set(true);
+        try {
+            new ClosureBackedAction<Object>(action).execute(this);
+        } finally {
+            executingDsl.set(false);
+        }
+    }
+
+    public NonTransformedModelDslBacking propertyMissing(String name) {
+        if (!executingDsl.get()) {
+            throw new MissingPropertyException(name, getClass());
+        }
+        return getChildPath(name);
+    }
+
+    public Void methodMissing(String name, Object argsObj) {
+        Object[] args = (Object[]) argsObj;
+
+        if (!executingDsl.get()) {
+            if (name.equals("$")) {
+                throw new GradleException(ATTEMPTED_INPUT_SYNTAX_USED_MESSAGE);
+            } else {
+                throw new MissingMethodException(name, getClass(), args);
+            }
+        } else {
+            if (args.length == 1 && args[0] instanceof Closure) {
+                Closure<?> closure = (Closure) args[0];
+                getChildPath(name).registerConfigurationAction(closure);
+                return null;
+            } else if (args.length == 2 && args[0] instanceof Class && args[1] instanceof Closure) {
+                Class<?> clazz = (Class<?>) args[0];
+                Closure<?> closure = (Closure<?>) args[1];
+                getChildPath(name).registerCreator(clazz, closure);
+                return null;
+            } else if (args.length == 1 && args[0] instanceof Class) {
+                Class<?> clazz = (Class<?>) args[0];
+                Closure<?> closure = Closure.IDENTITY;
+                getChildPath(name).registerCreator(clazz, closure);
+                return null;
+            } else {
+                throw new MissingMethodException(name, getClass(), args);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/TransformedModelDslBacking.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/TransformedModelDslBacking.java
new file mode 100644
index 0000000..e1d7f4d
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/TransformedModelDslBacking.java
@@ -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.model.dsl.internal;
+
+import com.google.common.collect.Lists;
+import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.internal.BiAction;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.dsl.internal.inputs.RuleInputAccessBacking;
+import org.gradle.model.dsl.internal.transform.RuleMetadata;
+import org.gradle.model.dsl.internal.transform.RulesBlock;
+import org.gradle.model.dsl.internal.transform.SourceLocation;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.manage.schema.ModelSchema;
+import org.gradle.model.internal.manage.schema.ModelSchemaStore;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+
+import java.util.List;
+
+ at ThreadSafe
+public class TransformedModelDslBacking {
+
+    private static final Transformer<List<ModelReference<?>>, Closure<?>> INPUT_PATHS_EXTRACTOR = new Transformer<List<ModelReference<?>>, Closure<?>>() {
+        public List<ModelReference<?>> transform(Closure<?> closure) {
+            RuleMetadata ruleMetadata = getRuleMetadata(closure);
+            String[] paths = ruleMetadata.inputPaths();
+            List<ModelReference<?>> references = Lists.newArrayListWithCapacity(paths.length);
+            for (int i = 0; i < paths.length; i++) {
+                String description = String.format("@ line %d", ruleMetadata.inputLineNumbers()[i]);
+                references.add(ModelReference.untyped(ModelPath.path(paths[i]), description));
+            }
+            return references;
+        }
+    };
+
+    private static final Transformer<SourceLocation, Closure<?>> RULE_LOCATION_EXTRACTOR = new Transformer<SourceLocation, Closure<?>>() {
+        public SourceLocation transform(Closure<?> closure) {
+            RuleMetadata ruleMetadata = getRuleMetadata(closure);
+            return new SourceLocation(ruleMetadata.scriptSourceDescription(), ruleMetadata.lineNumber(), ruleMetadata.columnNumber());
+        }
+    };
+
+    private final ModelRegistry modelRegistry;
+    private final Transformer<? extends List<ModelReference<?>>, ? super Closure<?>> inputPathsExtractor;
+    private final Transformer<SourceLocation, ? super Closure<?>> ruleLocationExtractor;
+    private final ModelSchemaStore schemaStore;
+    private final ModelCreatorFactory modelCreatorFactory;
+
+    public TransformedModelDslBacking(ModelRegistry modelRegistry, ModelSchemaStore schemaStore, ModelCreatorFactory modelCreatorFactory) {
+        this(modelRegistry, schemaStore, modelCreatorFactory, INPUT_PATHS_EXTRACTOR, RULE_LOCATION_EXTRACTOR);
+    }
+
+    TransformedModelDslBacking(ModelRegistry modelRegistry, ModelSchemaStore schemaStore, ModelCreatorFactory modelCreatorFactory, Transformer<? extends List<ModelReference<?>>, ? super Closure<?>> inputPathsExtractor,
+                               Transformer<SourceLocation, ? super Closure<?>> ruleLocationExtractor) {
+        this.modelRegistry = modelRegistry;
+        this.schemaStore = schemaStore;
+        this.modelCreatorFactory = modelCreatorFactory;
+        this.inputPathsExtractor = inputPathsExtractor;
+        this.ruleLocationExtractor = ruleLocationExtractor;
+    }
+
+    public void configure(String modelPathString, Closure<?> closure) {
+        List<ModelReference<?>> inputs = inputPathsExtractor.transform(closure);
+        SourceLocation sourceLocation = ruleLocationExtractor.transform(closure);
+        ModelPath modelPath = ModelPath.path(modelPathString);
+        ModelAction<Object> action = BiActionBackedModelAction.of(ModelReference.of(modelPath), toDescriptor(sourceLocation, modelPath), inputs, new ExecuteClosure<Object>(closure));
+        modelRegistry.configure(ModelActionRole.Mutate, action);
+    }
+
+    public <T> void create(String modelPathString, @DelegatesTo.Target Class<T> type, @DelegatesTo(genericTypeIndex = 0) Closure<?> closure) {
+        List<ModelReference<?>> inputs = inputPathsExtractor.transform(closure);
+        SourceLocation sourceLocation = ruleLocationExtractor.transform(closure);
+        ModelPath modelPath = ModelPath.path(modelPathString);
+        ModelSchema<T> schema = schemaStore.getSchema(ModelType.of(type));
+        ModelRuleDescriptor descriptor = toDescriptor(sourceLocation, modelPath);
+        if (!schema.getKind().isManaged()) {
+            throw new InvalidModelRuleDeclarationException(descriptor, "Cannot create an element of type " + type.getName() + " as it is not a managed type");
+        }
+        ModelCreator creator = modelCreatorFactory.creator(descriptor, modelPath, schema, inputs, new ExecuteClosure<T>(closure));
+        modelRegistry.create(creator);
+    }
+
+    public ModelRuleDescriptor toDescriptor(SourceLocation sourceLocation, ModelPath modelPath) {
+        return sourceLocation.asDescriptor("model." + modelPath);
+    }
+
+    private static RuleMetadata getRuleMetadata(Closure<?> closure) {
+        RuleMetadata ruleMetadata = closure.getClass().getAnnotation(RuleMetadata.class);
+        if (ruleMetadata == null) {
+            throw new IllegalStateException(String.format("Expected %s annotation to be used on the argument closure.", RuleMetadata.class.getName()));
+        }
+        return ruleMetadata;
+    }
+
+    public static boolean isTransformedBlock(Closure<?> closure) {
+        Class<?> closureClass = closure.getClass();
+        RulesBlock annotation = closureClass.getAnnotation(RulesBlock.class);
+        return annotation != null;
+    }
+
+    private static class ExecuteClosure<T> implements BiAction<T, List<ModelView<?>>> {
+        private final Closure<?> closure;
+
+        public ExecuteClosure(Closure<?> closure) {
+            this.closure = closure.rehydrate(null, null, null);
+        }
+
+        @Override
+        public void execute(final T object, List<ModelView<?>> inputs) {
+            RuleInputAccessBacking.runWithContext(inputs, new Runnable() {
+                public void run() {
+                    new ClosureBackedAction<Object>(closure).execute(object);
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccess.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccess.java
new file mode 100644
index 0000000..45ecaf4
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccess.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.inputs;
+
+public interface RuleInputAccess {
+
+    public Object input(String modelPath);
+
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccessBacking.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccessBacking.java
new file mode 100644
index 0000000..9cbd23d
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/inputs/RuleInputAccessBacking.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.inputs;
+
+import com.google.common.collect.Maps;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.ModelView;
+
+import java.util.List;
+import java.util.Map;
+
+ at ThreadSafe
+public abstract class RuleInputAccessBacking {
+
+    private RuleInputAccessBacking() {
+    }
+
+    private static final ThreadLocal<Map<String, Object>> INPUT = new ThreadLocal<Map<String, Object>>();
+
+    public static void runWithContext(List<ModelView<?>> views, Runnable runnable) {
+        Map<String, Object> map = Maps.newHashMap();
+        int i = 0;
+        for (ModelView<?> view : views) {
+            map.put(view.getPath().toString(), views.get(i++).getInstance());
+        }
+
+        INPUT.set(map);
+        try {
+            runnable.run();
+        } finally {
+            INPUT.remove();
+        }
+    }
+
+    public static RuleInputAccess getAccess() {
+        final Map<String, Object> inputs = INPUT.get();
+        return new RuleInputAccess() {
+            public Object input(String modelPath) {
+                return inputs.get(modelPath);
+            }
+        };
+    }
+
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ClosureCreationInterceptingVerifier.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ClosureCreationInterceptingVerifier.java
new file mode 100644
index 0000000..ec6cd9f
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ClosureCreationInterceptingVerifier.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import net.jcip.annotations.ThreadSafe;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.gradle.api.Action;
+
+/**
+ * The verifier is the only thing in the Groovy compiler chain that gets to visit the classes generated from closure expressions. If we want to transform these classes, we have to do it with this
+ * *hack*.
+ */
+ at ThreadSafe
+public class ClosureCreationInterceptingVerifier implements Action<ClassNode> {
+
+    public static final Action<ClassNode> INSTANCE = new ClosureCreationInterceptingVerifier();
+
+    public void execute(ClassNode node) {
+        if (node.implementsInterface(ClassHelper.GENERATED_CLOSURE_Type)) {
+            RulesVisitor.visitGeneratedClosure(node);
+            RuleVisitor.visitGeneratedClosure(node);
+        }
+    }
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ModelBlockTransformer.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ModelBlockTransformer.java
new file mode 100644
index 0000000..90502d1
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/ModelBlockTransformer.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import net.jcip.annotations.NotThreadSafe;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.gradle.groovy.scripts.internal.AbstractScriptTransformer;
+import org.gradle.groovy.scripts.internal.AstUtils;
+import org.gradle.groovy.scripts.internal.ScriptBlock;
+
+import java.util.Collections;
+import java.util.List;
+
+ at NotThreadSafe
+public class ModelBlockTransformer extends AbstractScriptTransformer {
+
+    public static boolean isEnabled() {
+        return Boolean.getBoolean("org.gradle.model.dsl");
+    }
+
+    @Override
+    protected int getPhase() {
+        return Phases.CANONICALIZATION;
+    }
+
+    public static final String MODEL = "model";
+    private static final List<String> SCRIPT_BLOCK_NAMES = Collections.singletonList(MODEL);
+
+    public static final String NON_LITERAL_CLOSURE_TO_TOP_LEVEL_MODEL_MESSAGE = "The top level model() method can only be called with a literal closure argument";
+
+    /*
+        TODO change this so that we extract all the information at compile time.
+
+        At the moment we use the transform to:
+
+        1. validate/restrict the syntax
+        2. transform rules into something more robust (e.g. foo.bar.baz {} into configure("foo.bar.baz", {})) - no dynamic propertyMissing() nonsense
+        3. hoist out input references (i.e. $()) into an annotation on rule closure classes to make available
+
+        This means we actually have to execute the code block in order to find the rule information within.
+        This is also problematic because it means we have to serialize this information into some form that fits into annotations.
+
+        Later, we will extract all the “up-front” information we need to know during compile time.
+        This will mean that we only need to execute the rules themselves, and not any code to actually register the rules.
+     */
+
+    @Override
+    public void call(SourceUnit source) throws CompilationFailedException {
+        if (!isEnabled()) {
+            return;
+        }
+
+        List<Statement> statements = source.getAST().getStatementBlock().getStatements();
+        for (Statement statement : statements) {
+            ScriptBlock scriptBlock = AstUtils.detectScriptBlock(statement, SCRIPT_BLOCK_NAMES);
+            if (scriptBlock == null) {
+                // Look for model(«») (i.e. call to model with anything other than non literal closure)
+                MethodCallExpression methodCall = AstUtils.extractBareMethodCall(statement);
+                if (methodCall == null) {
+                    continue;
+                }
+
+                String methodName = AstUtils.extractConstantMethodName(methodCall);
+                if (methodName == null) {
+                    continue;
+                }
+
+                if (methodName.equals(MODEL)) {
+                    source.getErrorCollector().addError(
+                            new SyntaxException(NON_LITERAL_CLOSURE_TO_TOP_LEVEL_MODEL_MESSAGE, statement.getLineNumber(), statement.getColumnNumber()),
+                            source
+                    );
+                }
+            } else {
+                RuleVisitor ruleVisitor = new RuleVisitor(source);
+                RulesVisitor rulesVisitor = new RulesVisitor(source, ruleVisitor);
+                scriptBlock.getClosureExpression().getCode().visit(rulesVisitor);
+            }
+        }
+    }
+
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleMetadata.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleMetadata.java
new file mode 100644
index 0000000..32e92eb
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleMetadata.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface RuleMetadata {
+    String[] inputPaths() default {};
+    int[] inputLineNumbers() default {};
+    String scriptSourceDescription();
+    int lineNumber();
+    int columnNumber();
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleVisitor.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleVisitor.java
new file mode 100644
index 0000000..2a4dd32
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RuleVisitor.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimaps;
+import net.jcip.annotations.NotThreadSafe;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.gradle.groovy.scripts.internal.AstUtils;
+import org.gradle.internal.SystemProperties;
+import org.gradle.model.dsl.internal.inputs.RuleInputAccess;
+import org.gradle.model.dsl.internal.inputs.RuleInputAccessBacking;
+import org.gradle.model.internal.core.ModelPath;
+
+import java.util.List;
+import java.util.Map;
+
+ at NotThreadSafe
+public class RuleVisitor extends CodeVisitorSupport {
+
+    public static final String INVALID_ARGUMENT_LIST = "argument list must be exactly 1 literal non empty string";
+
+    private static final String AST_NODE_METADATA_INPUTS_KEY = RuleVisitor.class.getName() + ".inputs";
+    public static final String AST_NODE_METADATA_LOCATION_KEY = RuleVisitor.class.getName() + ".location";
+
+    private static final String DOLLAR = "$";
+    private static final String INPUT = "input";
+    private static final ClassNode ANNOTATION_CLASS_NODE = new ClassNode(RuleMetadata.class);
+    private static final ClassNode CONTEXTUAL_INPUT_TYPE = new ClassNode(RuleInputAccessBacking.class);
+    private static final ClassNode ACCESS_API_TYPE = new ClassNode(RuleInputAccess.class);
+    private static final String GET_ACCESS = "getAccess";
+
+    private static final String ACCESS_HOLDER_FIELD = "_" + RuleInputAccess.class.getName().replace(".", "_");
+
+    private final SourceUnit sourceUnit;
+    private ImmutableListMultimap.Builder<String, Integer> inputs;
+    private VariableExpression accessVariable;
+
+    public RuleVisitor(SourceUnit sourceUnit) {
+        this.sourceUnit = sourceUnit;
+    }
+
+    // Not part of a normal visitor, see ClosureCreationInterceptingVerifier
+    public static void visitGeneratedClosure(ClassNode node) {
+        MethodNode method = AstUtils.getGeneratedClosureImplMethod(node);
+        Statement closureCode = method.getCode();
+        SourceLocation sourceLocation = closureCode.getNodeMetaData(AST_NODE_METADATA_LOCATION_KEY);
+        if (sourceLocation != null) {
+            AnnotationNode metadataAnnotation = new AnnotationNode(ANNOTATION_CLASS_NODE);
+
+            metadataAnnotation.addMember("scriptSourceDescription", new ConstantExpression(sourceLocation.getScriptSourceDescription()));
+            metadataAnnotation.addMember("lineNumber", new ConstantExpression(sourceLocation.getLineNumber()));
+            metadataAnnotation.addMember("columnNumber", new ConstantExpression(sourceLocation.getColumnNumber()));
+
+            ListMultimap<String, Integer> inputs = closureCode.getNodeMetaData(AST_NODE_METADATA_INPUTS_KEY);
+            if (!inputs.isEmpty()) {
+                List<Expression> pathValues = Lists.newArrayListWithCapacity(inputs.size());
+                List<Expression> lineNumberValues = Lists.newArrayListWithCapacity(inputs.size());
+                for (Map.Entry<String, List<Integer>> input : Multimaps.asMap(inputs).entrySet()) {
+                    pathValues.add(new ConstantExpression(input.getKey()));
+                    lineNumberValues.add(new ConstantExpression(input.getValue().get(0)));
+                }
+
+                metadataAnnotation.addMember("inputPaths", new ListExpression(pathValues));
+                metadataAnnotation.addMember("inputLineNumbers", new ListExpression(lineNumberValues));
+            }
+
+            node.addAnnotation(metadataAnnotation);
+        }
+    }
+
+    @Override
+    public void visitClosureExpression(ClosureExpression expression) {
+        if (inputs == null) {
+            inputs = ImmutableListMultimap.builder();
+            try {
+                accessVariable = new VariableExpression(ACCESS_HOLDER_FIELD, ACCESS_API_TYPE);
+
+                super.visitClosureExpression(expression);
+
+                BlockStatement code = (BlockStatement) expression.getCode();
+                code.setNodeMetaData(AST_NODE_METADATA_INPUTS_KEY, inputs.build());
+                accessVariable.setClosureSharedVariable(true);
+                StaticMethodCallExpression getAccessCall = new StaticMethodCallExpression(CONTEXTUAL_INPUT_TYPE, GET_ACCESS, ArgumentListExpression.EMPTY_ARGUMENTS);
+                DeclarationExpression variableDeclaration = new DeclarationExpression(accessVariable, new Token(Types.ASSIGN, "=", -1, -1), getAccessCall);
+                code.getStatements().add(0, new ExpressionStatement(variableDeclaration));
+                code.getVariableScope().putDeclaredVariable(accessVariable);
+            } finally {
+                inputs = null;
+            }
+        } else {
+            expression.getVariableScope().putReferencedLocalVariable(accessVariable);
+            super.visitClosureExpression(expression);
+        }
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        String methodName = call.getMethodAsString();
+        if (call.isImplicitThis() && methodName != null && methodName.equals(DOLLAR)) {
+            visitInputMethod(call);
+        } else {
+            // visit the method call, because one of the args may be an input method call
+            super.visitMethodCallExpression(call);
+        }
+    }
+
+    private void visitInputMethod(MethodCallExpression call) {
+        ConstantExpression argExpression = AstUtils.hasSingleConstantStringArg(call);
+        if (argExpression == null) { // not a valid signature
+            error(call, INVALID_ARGUMENT_LIST);
+        } else {
+            String modelPath = argExpression.getText();
+            if (modelPath.isEmpty()) {
+                error(argExpression, INVALID_ARGUMENT_LIST);
+                return;
+            }
+
+            try {
+                ModelPath.validatePath(modelPath);
+            } catch (ModelPath.InvalidPathException e) {
+                // TODO find a better way to present this information in the error message
+                // Attempt to mimic Gradle nested exception output
+                String message = "Invalid model path given as rule input." + SystemProperties.getInstance().getLineSeparator()
+                        + "  > " + e.getMessage();
+                if (e.getCause() != null) {
+                    // if there is a cause, it's an invalid name exception
+                    message += SystemProperties.getInstance().getLineSeparator() + "    > " + e.getCause().getMessage();
+                }
+                error(argExpression, message);
+                return;
+            }
+
+            inputs.put(modelPath, call.getLineNumber());
+            call.setObjectExpression(new VariableExpression(accessVariable));
+            call.setMethod(new ConstantExpression(INPUT));
+        }
+    }
+
+    private void error(ASTNode call, String message) {
+        SyntaxException syntaxException = new SyntaxException(message, call.getLineNumber(), call.getColumnNumber());
+        sourceUnit.getErrorCollector().addError(syntaxException, sourceUnit);
+    }
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesBlock.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesBlock.java
new file mode 100644
index 0000000..92ea27c
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesBlock.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation attached to rule block closures at compile time to indicate they have been transformed.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface RulesBlock {
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesVisitor.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesVisitor.java
new file mode 100644
index 0000000..cf404e6
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/RulesVisitor.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import com.google.common.collect.Lists;
+import net.jcip.annotations.ThreadSafe;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.gradle.api.Nullable;
+import org.gradle.groovy.scripts.internal.AstUtils;
+import org.gradle.groovy.scripts.internal.RestrictiveCodeVisitor;
+import org.gradle.groovy.scripts.internal.ScriptSourceDescriptionTransformer;
+import org.gradle.internal.Pair;
+import org.gradle.model.internal.core.ModelPath;
+
+import java.util.List;
+
+ at ThreadSafe
+public class RulesVisitor extends RestrictiveCodeVisitor {
+
+    private static final String AST_NODE_METADATA_KEY = RulesVisitor.class.getName();
+    private static final ClassNode ANNOTATION_CLASS_NODE = new ClassNode(RulesBlock.class);
+
+
+    // TODO - have to do much better here
+    public static final String INVALID_STATEMENT = "illegal rule";
+    public static final String INVALID_RULE_SIGNATURE = "Rule must follow the pattern '«name»(«type») {}' for a creator, and '«name» {}' for an action";
+
+    private final SourceUnit sourceUnit;
+    private final RuleVisitor ruleVisitor;
+
+    public RulesVisitor(SourceUnit sourceUnit, RuleVisitor ruleVisitor) {
+        super(sourceUnit, INVALID_STATEMENT);
+        this.sourceUnit = sourceUnit;
+        this.ruleVisitor = ruleVisitor;
+    }
+
+    public static void visitGeneratedClosure(ClassNode node) {
+        MethodNode method = AstUtils.getGeneratedClosureImplMethod(node);
+        Boolean isRulesBlock = method.getCode().getNodeMetaData(AST_NODE_METADATA_KEY);
+        if (isRulesBlock != null) {
+            AnnotationNode markerAnnotation = new AnnotationNode(ANNOTATION_CLASS_NODE);
+            node.addAnnotation(markerAnnotation);
+        }
+    }
+
+    @Override
+    public void visitBlockStatement(BlockStatement block) {
+        block.setNodeMetaData(AST_NODE_METADATA_KEY, true);
+
+        for (Statement statement : block.getStatements()) {
+            statement.visit(this);
+        }
+    }
+
+    @Override
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        ClosureExpression closureExpression = AstUtils.getSingleClosureArg(call);
+        if (closureExpression != null) {
+            rewriteAction(call, extractModelPathFromMethodTarget(call), closureExpression);
+            return;
+        }
+
+        Pair<ClassExpression, ClosureExpression> args = AstUtils.getClassAndClosureArgs(call);
+        if (args != null) {
+            rewriteCreator(call, extractModelPathFromMethodTarget(call), args.getRight(), args.getLeft());
+            return;
+        }
+
+        ClassExpression classArg = AstUtils.getClassArg(call);
+        if (classArg != null) {
+            List<Statement> statements = Lists.newLinkedList();
+            statements.add(new EmptyStatement());
+            BlockStatement block = new BlockStatement(statements, new VariableScope());
+            closureExpression = new ClosureExpression(Parameter.EMPTY_ARRAY, block);
+            closureExpression.setVariableScope(block.getVariableScope());
+            rewriteCreator(call, extractModelPathFromMethodTarget(call), closureExpression, classArg);
+            return;
+        }
+
+        restrict(call, INVALID_RULE_SIGNATURE);
+    }
+
+    public void rewriteCreator(MethodCallExpression call, String modelPath, ClosureExpression closureExpression, ClassExpression typeExpression) {
+        ConstantExpression modelPathArgument = new ConstantExpression(modelPath);
+        ArgumentListExpression replacedArgumentList = new ArgumentListExpression(modelPathArgument, typeExpression, closureExpression);
+        call.setMethod(new ConstantExpression("create"));
+        call.setArguments(replacedArgumentList);
+
+        // Call directly on the delegate to avoid some dynamic dispatch
+        call.setImplicitThis(true);
+        call.setObjectExpression(new MethodCallExpression(VariableExpression.THIS_EXPRESSION, "getDelegate", ArgumentListExpression.EMPTY_ARGUMENTS));
+
+        SourceLocation sourceLocation = new SourceLocation(getScriptSourceDescription(), call.getLineNumber(), call.getColumnNumber());
+        closureExpression.getCode().setNodeMetaData(RuleVisitor.AST_NODE_METADATA_LOCATION_KEY, sourceLocation);
+
+        closureExpression.visit(ruleVisitor);
+    }
+
+    public void rewriteAction(MethodCallExpression call, String modelPath, ClosureExpression closureExpression) {
+        // Rewrite the method call to match ModelDsl#configure(String, Closure), which is what the delegate will be
+        ConstantExpression modelPathArgument = new ConstantExpression(modelPath);
+        ArgumentListExpression replacedArgumentList = new ArgumentListExpression(modelPathArgument, closureExpression);
+        call.setMethod(new ConstantExpression("configure"));
+        call.setArguments(replacedArgumentList);
+
+        // Call directly on the delegate to avoid some dynamic dispatch
+        call.setImplicitThis(true);
+        call.setObjectExpression(new MethodCallExpression(VariableExpression.THIS_EXPRESSION, "getDelegate", ArgumentListExpression.EMPTY_ARGUMENTS));
+
+        SourceLocation sourceLocation = new SourceLocation(getScriptSourceDescription(), call.getLineNumber(), call.getColumnNumber());
+        closureExpression.getCode().setNodeMetaData(RuleVisitor.AST_NODE_METADATA_LOCATION_KEY, sourceLocation);
+
+        closureExpression.visit(ruleVisitor);
+    }
+
+    private String getScriptSourceDescription() {
+        return sourceUnit.getAST().getNodeMetaData(ScriptSourceDescriptionTransformer.AST_NODE_METADATA_KEY);
+    }
+
+    @Nullable // if the target was invalid
+    private String extractModelPathFromMethodTarget(MethodCallExpression call) {
+        Expression target = call.getMethod();
+        List<String> names = Lists.newLinkedList();
+        while (true) {
+            if (target instanceof ConstantExpression) {
+                if (target.getType().equals(ClassHelper.STRING_TYPE)) {
+                    String name = target.getText();
+                    names.add(0, name);
+                    if (call.isImplicitThis()) {
+                        break;
+                    } else {
+                        target = call.getObjectExpression();
+                        continue;
+                    }
+                }
+            } else if (target instanceof PropertyExpression) {
+                PropertyExpression propertyExpression = (PropertyExpression) target;
+                Expression property = propertyExpression.getProperty();
+                if (property instanceof ConstantExpression) {
+                    ConstantExpression constantProperty = (ConstantExpression) property;
+                    if (constantProperty.getType().equals(ClassHelper.STRING_TYPE)) {
+                        String name = constantProperty.getText();
+                        names.add(0, name);
+                        target = propertyExpression.getObjectExpression();
+                        continue;
+                    }
+                }
+            } else if (target instanceof VariableExpression) {
+                // This will be the left most property
+                names.add(0, ((VariableExpression) target).getName());
+                break;
+            }
+
+            // Invalid paths fall through to here
+
+            restrict(call);
+            return null;
+        }
+
+        // TODO - validate that it's a valid model path
+        return ModelPath.pathString(names);
+    }
+}
diff --git a/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/SourceLocation.java b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/SourceLocation.java
new file mode 100644
index 0000000..e90955d
--- /dev/null
+++ b/subprojects/model-groovy/src/main/java/org/gradle/model/dsl/internal/transform/SourceLocation.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal.transform;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+
+ at ThreadSafe
+public class SourceLocation {
+    private final String scriptSourceDescription;
+    private final int lineNumber;
+    private final int columnNumber;
+
+    public SourceLocation(String scriptSourceDescription, int lineNumber, int columnNumber) {
+        this.scriptSourceDescription = scriptSourceDescription;
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public int getColumnNumber() {
+        return columnNumber;
+    }
+
+    public String getScriptSourceDescription() {
+        return scriptSourceDescription;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s line %d, column %d", scriptSourceDescription, lineNumber, columnNumber);
+    }
+
+    public ModelRuleDescriptor asDescriptor(String val) {
+        return new SimpleModelRuleDescriptor(String.format("%s @ %s", val, toString()));
+    }
+}
diff --git a/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/NonTransformedModelDslBackingTest.groovy b/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/NonTransformedModelDslBackingTest.groovy
new file mode 100644
index 0000000..9714199
--- /dev/null
+++ b/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/NonTransformedModelDslBackingTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dsl.internal
+
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.Managed
+import org.gradle.model.collection.ManagedSet
+import org.gradle.model.internal.core.ModelCreators
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.model.internal.core.ModelRuleExecutionException
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.inspect.DefaultModelCreatorFactory
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class NonTransformedModelDslBackingTest extends Specification {
+
+    def modelRegistry = new ModelRegistryHelper()
+    def schemaStore = DefaultModelSchemaStore.instance
+    def creatorFactory = new DefaultModelCreatorFactory(schemaStore)
+    def modelDsl = new NonTransformedModelDslBacking(getModelRegistry(), schemaStore, creatorFactory)
+
+    void register(String pathString, Object element) {
+        modelRegistry.create(ModelCreators.bridgedInstance(ModelReference.of(pathString, element.class), element).descriptor("register").build())
+    }
+
+    def "can add rules via dsl"() {
+        given:
+        register("foo", [])
+
+        when:
+        modelDsl.configure {
+            foo {
+                add 1
+            }
+        }
+
+        then:
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.of(List)) == [1]
+    }
+
+    @Managed
+    interface Thing {
+        void setName(String name)
+
+        String getName()
+    }
+
+    @Managed
+    interface Foo {
+        ManagedSet<Thing> getBar()
+    }
+
+    interface Unmanaged {}
+
+    def "can create via DSL"() {
+        when:
+        modelDsl.configure {
+            foo(Foo)
+        }
+
+        then:
+        modelRegistry.get("foo", Foo).bar.empty
+    }
+
+    def "can only create top level"() {
+        when:
+        modelDsl.configure {
+            foo.bar(Foo)
+        }
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    def "can create and configure via DSL"() {
+        when:
+        modelDsl.configure {
+            foo(Foo) {
+                bar.create {
+                    name = "one"
+                }
+            }
+        }
+
+        then:
+        modelRegistry.get("foo", Foo).bar.first().name == "one"
+    }
+
+    def "cannot create unmanaged"() {
+        when:
+        modelDsl.configure {
+            unmanaged(Unmanaged)
+        }
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    def "can use property accessors in DSL to build model object path"() {
+        when:
+        modelDsl.configure {
+            foo(Foo)
+            foo.bar {
+                create {
+                    it.name = "foo"
+                }
+            }
+        }
+
+        then:
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.of(Foo)).bar*.name == ["foo"]
+    }
+
+    def "does not add rules when not configuring"() {
+        given:
+        register("foo", new TestObject())
+        register("bah", new TestObject())
+
+        when:
+        modelDsl.configure {
+            foo {
+                defineSomeThing {
+                    unknown
+                }
+            }
+        }
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.UNTYPED)
+
+        then:
+        def e = thrown(ModelRuleExecutionException)
+        def missingProp = e.cause
+        missingProp instanceof MissingPropertyException
+        missingProp.property == 'unknown'
+
+        when:
+        modelDsl.configure {
+            bah {
+                defineSomeThing {
+                    unknown {}
+                }
+            }
+        }
+        modelRegistry.realize(ModelPath.path("bah"), ModelType.UNTYPED)
+
+        then:
+        e = thrown(ModelRuleExecutionException)
+        def missingMethod = e.cause
+        assert missingMethod instanceof MissingMethodException
+        missingMethod.method == 'unknown'
+    }
+
+    static class TestObject {
+        String prop
+
+        def defineSomeThing(Closure cl) {
+            cl.delegate = this
+            cl.call()
+        }
+    }
+}
+
diff --git a/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/TransformedModelDslBackingTest.groovy b/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/TransformedModelDslBackingTest.groovy
new file mode 100644
index 0000000..3eb4f86
--- /dev/null
+++ b/subprojects/model-groovy/src/test/groovy/org/gradle/model/dsl/internal/TransformedModelDslBackingTest.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.model.dsl.internal
+
+import org.gradle.api.Transformer
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.Managed
+import org.gradle.model.dsl.internal.inputs.RuleInputAccessBacking
+import org.gradle.model.dsl.internal.transform.SourceLocation
+import org.gradle.model.internal.core.ModelCreators
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import org.gradle.model.internal.inspect.DefaultModelCreatorFactory
+import org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore
+import org.gradle.model.internal.registry.DefaultModelRegistry
+import org.gradle.model.internal.type.ModelType
+import spock.lang.Specification
+
+class TransformedModelDslBackingTest extends Specification {
+
+    def modelRegistry = new DefaultModelRegistry(null)
+    Transformer<List<ModelReference<?>>, Closure<?>> referenceExtractor = Mock()
+    Transformer<SourceLocation, Closure<?>> locationExtractor = Mock()
+    def schemaStore = DefaultModelSchemaStore.instance
+    def creator = new DefaultModelCreatorFactory(schemaStore)
+    def modelDsl = new TransformedModelDslBacking(getModelRegistry(), schemaStore, creator, referenceExtractor, locationExtractor)
+
+    void register(String pathString, Object element) {
+        modelRegistry.create(ModelCreators.bridgedInstance(ModelReference.of(pathString, element.class), element).descriptor("register").build())
+    }
+
+    def "can add rules via dsl"() {
+        given:
+        register("foo", [])
+        referenceExtractor.transform(_) >> []
+        locationExtractor.transform(_) >> Mock(SourceLocation) {
+            asDescriptor(_) >> new SimpleModelRuleDescriptor("foo")
+        }
+
+        when:
+        modelDsl.configure("foo") {
+            add 1
+        }
+
+        then:
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.of(List)) == [1]
+    }
+
+    @Managed
+    static abstract class Thing {
+        abstract String getValue()
+
+        abstract void setValue(String value)
+    }
+
+    def "can add creator via dsl"() {
+        given:
+        referenceExtractor.transform(_) >> []
+        locationExtractor.transform(_) >> Mock(SourceLocation) {
+            asDescriptor(_) >> new SimpleModelRuleDescriptor("foo")
+        }
+
+        when:
+        modelDsl.create("foo", Thing) {
+            value = "set"
+        }
+
+        then:
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.of(Thing)).value == "set"
+    }
+
+    def "can only create top level"() {
+        given:
+        referenceExtractor.transform(_) >> []
+        locationExtractor.transform(_) >> Mock(SourceLocation) {
+            asDescriptor(_) >> new SimpleModelRuleDescriptor("foo")
+        }
+
+        when:
+        modelDsl.create("foo.bar", Thing) {
+            value = "set"
+        }
+
+        then:
+        thrown InvalidModelRuleDeclarationException
+    }
+
+    def "can registers extracted references"() {
+        given:
+        register("foo", [])
+        register("value", "123")
+        referenceExtractor.transform(_) >> [ModelReference.of("value", Object)]
+        locationExtractor.transform(_) >> Mock(SourceLocation) {
+            asDescriptor(_) >> new SimpleModelRuleDescriptor("foo")
+        }
+
+        when:
+        modelDsl.with {
+            configure("foo") {
+                // this is effectively what it gets transformed to
+                add RuleInputAccessBacking.access.input("value")
+            }
+        }
+
+        then:
+        modelRegistry.realize(ModelPath.path("foo"), ModelType.of(List)) == ["123"]
+    }
+
+}
+
diff --git a/subprojects/native/native.gradle b/subprojects/native/native.gradle
index 4677efe..1639a64 100755
--- a/subprojects/native/native.gradle
+++ b/subprojects/native/native.gradle
@@ -7,9 +7,6 @@ dependencies {
     compile libraries.slf4j_api
     compile libraries.jna
     compile libraries.nativePlatform
-    compile module('org.jruby.ext.posix:jna-posix:1.0.3') {
-        dependency libraries.jna
-    }
     compile module('org.fusesource.jansi:jansi:1.2.1') {
         dependency libraries.jna
     }
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationException.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationException.java
new file mode 100644
index 0000000..ac18836
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationException.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.internal.nativeintegration;
+
+public class NativeIntegrationException extends RuntimeException {
+    public NativeIntegrationException(String message) {
+        super(message);
+    }
+
+    public NativeIntegrationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationUnavailableException.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationUnavailableException.java
new file mode 100644
index 0000000..61302ae
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/NativeIntegrationUnavailableException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration;
+
+/**
+ * Thrown when the native integration for the current platform is not available for some reason (eg unsupported operating system, cannot load native library, etc).
+ */
+public class NativeIntegrationUnavailableException extends NativeIntegrationException {
+    public NativeIntegrationUnavailableException(String message) {
+        super(message);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ProcessEnvironment.java
new file mode 100644
index 0000000..27f219a
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ProcessEnvironment.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * Provides access to information about the current process.
+ *
+ * <p>Implementations are not thread-safe.</p>
+ */
+public interface ProcessEnvironment {
+    /**
+     * Sets the environment of this process, if possible.
+     *
+     * @param source The environment
+     * @return true if environment changed, false if not possible.
+     */
+    public boolean maybeSetEnvironment(Map<String, String> source);
+
+    /**
+     * Removes the given environment variable.
+     *
+     * @param name The name of the environment variable.
+     * @throws NativeIntegrationException If the environment variable cannot be removed.
+     */
+    void removeEnvironmentVariable(String name) throws NativeIntegrationException;
+
+    /**
+     * Removes the given environment variable, if possible.
+     *
+     * @param name The name of the environment variable.
+     * @return true if removed, false if not possible.
+     */
+    boolean maybeRemoveEnvironmentVariable(String name);
+
+    /**
+     * Sets the given environment variable.
+     *
+     * @param name The name
+     * @param value The value. Can be null, which removes the environment variable.
+     * @throws NativeIntegrationException If the environment variable cannot be set.
+     */
+    void setEnvironmentVariable(String name, String value) throws NativeIntegrationException;
+
+    /**
+     * Sets the given environment variable, if possible.
+     *
+     * @param name The name
+     * @param value The value
+     * @return true if set, false if not possible.
+     */
+    boolean maybeSetEnvironmentVariable(String name, String value);
+
+    /**
+     * Returns the working directory of the current process.
+     *
+     * @throws NativeIntegrationException If the process directory is not available.
+     */
+    File getProcessDir() throws NativeIntegrationException;
+
+    /**
+     * Sets the process working directory.
+     *
+     * @param processDir The directory.
+     * @throws NativeIntegrationException If process directory cannot be set.
+     */
+    void setProcessDir(File processDir) throws NativeIntegrationException;
+
+    /**
+     * Sets the process working directory, if possible
+     *
+     * @param processDir The directory.
+     * @return true if the directory can be set, false if not possible.
+     */
+    boolean maybeSetProcessDir(File processDir);
+
+    /**
+     * Returns the OS level PID for the current process.
+     *
+     * @throws NativeIntegrationException If the pid is not available.
+     */
+    Long getPid() throws NativeIntegrationException;
+
+    /**
+     * Returns the OS level PID for the current process, or null if not available.
+     */
+    Long maybeGetPid();
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ReflectiveEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ReflectiveEnvironment.java
new file mode 100644
index 0000000..661357b
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/ReflectiveEnvironment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.nativeintegration;
+
+import org.gradle.internal.os.OperatingSystem;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+/**
+ * Uses reflection to update private environment state
+ */
+public class ReflectiveEnvironment {
+
+    public void unsetenv(String name) {
+        Map<String, String> map = getEnv();
+        map.remove(name);
+        if (OperatingSystem.current().isWindows()) {
+            Map<String, String> env2 = getWindowsEnv();
+            env2.remove(name);
+        }
+    }
+
+    public void setenv(String name, String value) {
+        Map<String, String> map = getEnv();
+        map.put(name, value);
+        if (OperatingSystem.current().isWindows()) {
+            Map<String, String> env2 = getWindowsEnv();
+            env2.put(name, value);
+        }
+    }
+
+    /**
+     * Windows keeps an extra map with case insensitive keys. The map is used when the user calls {@link System#getenv(String)}
+     */
+    private Map<String, String> getWindowsEnv() {
+        try {
+            Class<?> sc = Class.forName("java.lang.ProcessEnvironment");
+            Field caseinsensitive = sc.getDeclaredField("theCaseInsensitiveEnvironment");
+            caseinsensitive.setAccessible(true);
+            @SuppressWarnings("unchecked")
+            Map<String, String> result = (Map<String, String>)caseinsensitive.get(null);
+            return result;
+        } catch (Exception e) {
+            throw new NativeIntegrationException("Unable to get mutable windows case insensitive environment map", e);
+        }
+    }
+
+    private Map<String, String> getEnv() {
+        try {
+            Map<String, String> theUnmodifiableEnvironment = System.getenv();
+            Class<?> cu = theUnmodifiableEnvironment.getClass();
+            Field m = cu.getDeclaredField("m");
+            m.setAccessible(true);
+            @SuppressWarnings("unchecked")
+            Map<String, String> result = (Map<String, String>)m.get(theUnmodifiableEnvironment);
+            return result;
+        } catch (Exception e) {
+            throw new NativeIntegrationException("Unable to get mutable environment map", e);
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleDetector.java
new file mode 100644
index 0000000..7214ed5
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleDetector.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.internal.nativeintegration.console;
+
+import org.gradle.api.Nullable;
+
+public interface ConsoleDetector {
+    /**
+     * Locates the console for this process, if any.
+     *
+     * @return Information about the console, or null if this process is not attached to the console.
+     */
+    @Nullable
+    ConsoleMetaData getConsole();
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleMetaData.java
new file mode 100644
index 0000000..cdfa2ce
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/ConsoleMetaData.java
@@ -0,0 +1,36 @@
+/*
+ * 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.nativeintegration.console;
+
+public interface ConsoleMetaData {
+    /**
+     * Returns true if the current process' stdout is attached to the console.
+     */
+    boolean isStdOut();
+
+    /**
+     * Returns true if the current process' stderr is attached to the console.
+     */
+    boolean isStdErr();
+
+    /**
+     * <p>Returns the number of columns available in the console.</p>
+     *
+     * @return The number of columns available in the console. If no information is available return 0.
+     */
+    public int getCols();
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/FallbackConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/FallbackConsoleMetaData.java
new file mode 100644
index 0000000..228b4c2
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/FallbackConsoleMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * 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.nativeintegration.console;
+
+public class FallbackConsoleMetaData implements ConsoleMetaData {
+    public boolean isStdOut() {
+        return true;
+    }
+
+    public boolean isStdErr() {
+        return true;
+    }
+
+    public int getCols() {
+        return 0;
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetector.java
new file mode 100644
index 0000000..cde3d48
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetector.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.internal.nativeintegration.console;
+
+import net.rubygrapefruit.platform.Terminals;
+
+import static net.rubygrapefruit.platform.Terminals.Output.Stderr;
+import static net.rubygrapefruit.platform.Terminals.Output.Stdout;
+
+public class NativePlatformConsoleDetector implements ConsoleDetector {
+    private final Terminals terminals;
+
+    public NativePlatformConsoleDetector(Terminals terminals) {
+        this.terminals = terminals;
+    }
+
+    public ConsoleMetaData getConsole() {
+        // Dumb terminal doesn't support ANSI control codes.
+        // TODO - remove this when we use Terminal rather than JAnsi to render to console
+        String term = System.getenv("TERM");
+        if (term != null && term.equals("dumb")) {
+            return null;
+        }
+
+        boolean stdout = terminals.isTerminal(Stdout);
+        boolean stderr = terminals.isTerminal(Stderr);
+        if (stdout) {
+            return new NativePlatformConsoleMetaData(stdout, stderr, terminals.getTerminal(Stdout));
+        } else if (stderr) {
+            return new NativePlatformConsoleMetaData(stdout, stderr, terminals.getTerminal(Stderr));
+        }
+        return null;
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleMetaData.java
new file mode 100644
index 0000000..246ffb8
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NativePlatformConsoleMetaData.java
@@ -0,0 +1,43 @@
+/*
+ * 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.nativeintegration.console;
+
+import net.rubygrapefruit.platform.Terminal;
+
+public class NativePlatformConsoleMetaData implements ConsoleMetaData {
+    private final boolean stdout;
+    private final boolean stderr;
+    private final Terminal terminal;
+
+    public NativePlatformConsoleMetaData(boolean stdout, boolean stderr, Terminal terminal) {
+        this.stdout = stdout;
+        this.stderr = stderr;
+        this.terminal = terminal;
+    }
+
+    public boolean isStdOut() {
+        return stdout;
+    }
+
+    public boolean isStdErr() {
+        return stderr;
+    }
+
+    public int getCols() {
+        return terminal.getTerminalSize().getCols();
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NoOpConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NoOpConsoleDetector.java
new file mode 100644
index 0000000..03fee07
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/NoOpConsoleDetector.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.internal.nativeintegration.console;
+
+public class NoOpConsoleDetector implements ConsoleDetector {
+    public ConsoleMetaData getConsole() {
+        return null;
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/UnixConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/UnixConsoleMetaData.java
new file mode 100644
index 0000000..c8fcde0
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/UnixConsoleMetaData.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.internal.nativeintegration.console;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UnixConsoleMetaData implements ConsoleMetaData {
+    public static final Logger LOGGER = LoggerFactory.getLogger(UnixConsoleMetaData.class);
+    private final boolean stdout;
+    private final boolean stderr;
+
+    public UnixConsoleMetaData(boolean stdout, boolean stderr) {
+        this.stdout = stdout;
+        this.stderr = stderr;
+    }
+
+    public boolean isStdOut() {
+        return stdout;
+    }
+
+    public boolean isStdErr() {
+        return stderr;
+    }
+
+    public int getCols() {
+        final String columns = System.getenv("COLUMNS");
+        if (columns != null) {
+            try {
+                return Integer.parseInt(columns);
+            } catch (NumberFormatException ex) {
+                LOGGER.debug("Cannot parse COLUMNS environment variable to get console width. Value: '{}'", columns);
+            }
+        }
+        return 0;
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/WindowsConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/WindowsConsoleDetector.java
new file mode 100755
index 0000000..4587246
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/console/WindowsConsoleDetector.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.internal.nativeintegration.console;
+
+import org.fusesource.jansi.WindowsAnsiOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class WindowsConsoleDetector implements ConsoleDetector {
+    public ConsoleMetaData getConsole() {
+        // Use Jansi's detection mechanism
+        try {
+            new WindowsAnsiOutputStream(new ByteArrayOutputStream());
+            return new FallbackConsoleMetaData();
+        } catch (IOException ignore) {
+            // Not attached to a console
+            return null;
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Chmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Chmod.java
new file mode 100644
index 0000000..21e3308
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Chmod.java
@@ -0,0 +1,31 @@
+/*
+ * 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.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface Chmod {
+    /**
+     * Changes the Unix permissions of a provided file. Implementations that don't
+     * support Unix permissions may choose to ignore this request.
+     *
+     * @param file the file to change permissions on
+     * @param mode the permissions, e.g. 0755
+     * @throws FileException if the permissions can't be changed for some reason.
+     */
+    public void chmod(File file, int mode) throws FileException;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileCanonicalizer.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileCanonicalizer.java
new file mode 100644
index 0000000..377979b
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileCanonicalizer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface FileCanonicalizer {
+    File canonicalize(File file) throws FileException;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileException.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileException.java
new file mode 100644
index 0000000..0c3cdd8
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem;
+
+public class FileException extends RuntimeException {
+    public FileException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeAccessor.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeAccessor.java
new file mode 100644
index 0000000..f972bcf
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeAccessor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface FileModeAccessor {
+    public int getUnixMode(File f) throws Exception;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeMutator.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeMutator.java
new file mode 100644
index 0000000..1fa51e3
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileModeMutator.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface FileModeMutator {
+    public void chmod(File file, int mode) throws Exception;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileSystem.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileSystem.java
new file mode 100755
index 0000000..be5233a
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/FileSystem.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem;
+
+import java.io.File;
+
+/**
+ * A file system accessible to Gradle.
+ */
+public interface FileSystem extends Chmod, Stat {
+    /**
+     * Default Unix permissions for directories, {@code 755}.
+     */
+    public static final int DEFAULT_DIR_MODE = 0755;
+
+    /**
+     * Default Unix permissions for files, {@code 644}.
+     */
+    public static final int DEFAULT_FILE_MODE = 0644;
+
+    /**
+     * Tells whether the file system is case sensitive.
+     *
+     * @return <tt>true</tt> if the file system is case sensitive, <tt>false</tt> otherwise
+     */
+    boolean isCaseSensitive();
+
+    /**
+     * Tells if the file system can create symbolic links. If the answer cannot be determined accurately,
+     * <tt>false</tt> is returned.
+     *
+     * @return <tt>true</tt> if the file system can create symbolic links, <tt>false</tt> otherwise
+     */
+    boolean canCreateSymbolicLink();
+
+    /**
+     * Creates a symbolic link to a target file.
+     *
+     * @param link the link to be created
+     * @param target the file to link to
+     * @exception FileException if the operation fails
+     */
+    void createSymbolicLink(File link, File target) throws FileException;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Stat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Stat.java
new file mode 100644
index 0000000..1554368
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Stat.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.internal.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface Stat {
+    public int getUnixMode(File f) throws FileException;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Symlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Symlink.java
new file mode 100644
index 0000000..6c677dc
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/Symlink.java
@@ -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.internal.nativeintegration.filesystem;
+
+import java.io.File;
+
+public interface Symlink {
+    boolean isSymlinkSupported();
+
+    void symlink(File link, File target) throws Exception;
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7FileCanonicalizer.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7FileCanonicalizer.java
new file mode 100644
index 0000000..32fccc4
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7FileCanonicalizer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.jdk7;
+
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
+import org.gradle.internal.nativeintegration.filesystem.FileException;
+
+import java.io.File;
+import java.io.IOException;
+
+public class Jdk7FileCanonicalizer implements FileCanonicalizer {
+    public File canonicalize(File file) throws FileException {
+        try {
+            return file.toPath().toRealPath().toFile();
+        } catch (IOException e) {
+            throw new FileException(String.format("Could not canonicalize file %s.", file), e);
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverter.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverter.java
new file mode 100644
index 0000000..4b08bb3
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.nativeintegration.filesystem.jdk7;
+
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static java.nio.file.attribute.PosixFilePermission.*;
+
+public class PosixFilePermissionConverter {
+
+    static Set<PosixFilePermission> convertToPermissionsSet(int mode) {
+        Set<PosixFilePermission> result = EnumSet.noneOf(PosixFilePermission.class);
+
+        if (isSet(mode, 0400)) {
+            result.add(OWNER_READ);
+        }
+        if (isSet(mode, 0200)) {
+            result.add(OWNER_WRITE);
+        }
+        if (isSet(mode, 0100)) {
+            result.add(OWNER_EXECUTE);
+        }
+
+        if (isSet(mode, 040)) {
+            result.add(GROUP_READ);
+        }
+        if (isSet(mode, 020)) {
+            result.add(GROUP_WRITE);
+        }
+        if (isSet(mode, 010)) {
+            result.add(GROUP_EXECUTE);
+        }
+        if (isSet(mode, 04)) {
+            result.add(OTHERS_READ);
+        }
+        if (isSet(mode, 02)) {
+            result.add(OTHERS_WRITE);
+        }
+        if (isSet(mode, 01)) {
+            result.add(OTHERS_EXECUTE);
+        }
+        return result;
+    }
+
+    private static boolean isSet(int mode, int testbit) {
+        return (mode & testbit) == testbit;
+    }
+
+    public static int convertToInt(Set<PosixFilePermission> permissions) {
+        int result = 0;
+        if (permissions.contains(OWNER_READ)) {
+            result = result | 0400;
+        }
+        if (permissions.contains(OWNER_WRITE)) {
+            result = result | 0200;
+        }
+        if (permissions.contains(OWNER_EXECUTE)) {
+            result = result | 0100;
+        }
+        if (permissions.contains(GROUP_READ)) {
+            result = result | 040;
+        }
+        if (permissions.contains(GROUP_WRITE)) {
+            result = result | 020;
+        }
+        if (permissions.contains(GROUP_EXECUTE)) {
+            result = result | 010;
+        }
+        if (permissions.contains(OTHERS_READ)) {
+            result = result | 04;
+        }
+        if (permissions.contains(OTHERS_WRITE)) {
+            result = result | 02;
+        }
+        if (permissions.contains(OTHERS_EXECUTE)) {
+            result = result | 01;
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandler.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandler.java
new file mode 100644
index 0000000..de37e87
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandler.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.internal.nativeintegration.filesystem.jdk7;
+
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+
+import static org.gradle.internal.nativeintegration.filesystem.jdk7.PosixFilePermissionConverter.convertToInt;
+import static org.gradle.internal.nativeintegration.filesystem.jdk7.PosixFilePermissionConverter.convertToPermissionsSet;
+
+public class PosixJdk7FilePermissionHandler implements FileModeAccessor, FileModeMutator {
+
+    public int getUnixMode(File file) throws IOException {
+        final PosixFileAttributes posixFileAttributes = Files.readAttributes(file.toPath(), PosixFileAttributes.class);
+        return convertToInt(posixFileAttributes.permissions());
+    }
+
+    public void chmod(File f, int mode) throws IOException {
+        PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(f.toPath(), PosixFileAttributeView.class);
+        fileAttributeView.setPermissions(convertToPermissionsSet(mode));
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/EmptyChmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/EmptyChmod.java
new file mode 100644
index 0000000..2ef49d7
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/EmptyChmod.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.internal.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+class EmptyChmod implements FileModeMutator {
+    public void chmod(File f, int mode) throws FileNotFoundException {
+        if (!f.exists()) {
+            throw new FileNotFoundException(String.format("File '%s' does not exist.", f));
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackFileCanonicalizer.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackFileCanonicalizer.java
new file mode 100644
index 0000000..1e57083
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackFileCanonicalizer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
+import org.gradle.internal.nativeintegration.filesystem.FileException;
+
+import java.io.File;
+import java.io.IOException;
+
+class FallbackFileCanonicalizer implements FileCanonicalizer {
+    public File canonicalize(File file) throws FileException {
+        try {
+            return file.getCanonicalFile();
+        } catch (IOException e) {
+            throw new FileException(String.format("Could not canonicalize file %s.", file), e);
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackStat.java
new file mode 100644
index 0000000..d189634
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FallbackStat.java
@@ -0,0 +1,36 @@
+/*
+ * 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.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+import org.gradle.internal.nativeintegration.filesystem.FileSystem;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+class FallbackStat implements FileModeAccessor {
+    public int getUnixMode(File f) throws IOException {
+        if (f.isDirectory()) {
+            return FileSystem.DEFAULT_DIR_MODE;
+        } else if (f.exists()) {
+            return FileSystem.DEFAULT_FILE_MODE;
+        } else {
+            throw new FileNotFoundException(String.format("File '%s' not found.", f));
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FileSystemServices.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FileSystemServices.java
new file mode 100644
index 0000000..bc689d4
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/FileSystemServices.java
@@ -0,0 +1,84 @@
+/*
+ * 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.nativeintegration.filesystem.services;
+
+import net.rubygrapefruit.platform.PosixFiles;
+import org.gradle.api.JavaVersion;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer;
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+import org.gradle.internal.nativeintegration.filesystem.Symlink;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.gradle.internal.os.OperatingSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FileSystemServices {
+    private static final Logger LOGGER = LoggerFactory.getLogger(FileSystemServices.class);
+
+    @SuppressWarnings("UnusedDeclaration")
+    public FileCanonicalizer createFileCanonicalizer() {
+        return (FileCanonicalizer) newInstance("org.gradle.internal.nativeintegration.filesystem.jdk7.Jdk7FileCanonicalizer", FallbackFileCanonicalizer.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    public FileSystem createFileSystem(OperatingSystem operatingSystem, PosixFiles posixFiles) throws Exception {
+        // Use no-op implementations for windows
+        if (operatingSystem.isWindows()) {
+            return new GenericFileSystem(new EmptyChmod(), new FallbackStat(), new WindowsSymlink());
+        }
+
+        if (posixFiles instanceof UnavailablePosixFiles) {
+            LOGGER.debug("Native-platform file system integration is not available. Continuing with fallback.");
+        } else {
+            Symlink symlink = new NativePlatformBackedSymlink(posixFiles);
+            FileModeMutator chmod = new NativePlatformBackedChmod(posixFiles);
+            FileModeAccessor stat = new NativePlatformBackedStat(posixFiles);
+            return new GenericFileSystem(chmod, stat, symlink);
+        }
+
+        LOGGER.debug("Using UnsupportedSymlink implementation.");
+        Symlink symlink = new UnsupportedSymlink();
+
+        // Use java 7 APIs, if available, otherwise fallback to no-op
+        Object handler = newInstance("org.gradle.internal.nativeintegration.filesystem.jdk7.PosixJdk7FilePermissionHandler", UnsupportedFilePermissions.class);
+        return new GenericFileSystem((FileModeMutator) handler, (FileModeAccessor) handler, symlink);
+    }
+
+    private Object newInstance(String jdk7Type, Class<?> fallbackType) {
+        // Use java 7 APIs, if available
+        Class<?> handlerClass = null;
+        if (JavaVersion.current().isJava7()) {
+            try {
+                handlerClass = FileSystemServices.class.getClassLoader().loadClass(jdk7Type);
+                LOGGER.debug("Using JDK 7 file service {}", jdk7Type);
+            } catch (ClassNotFoundException e) {
+                // Ignore
+            }
+        }
+        if (handlerClass == null) {
+            LOGGER.debug("Unable to load {}. Continuing with fallback {}.", jdk7Type, fallbackType.getName());
+            handlerClass = fallbackType;
+        }
+        try {
+            return handlerClass.newInstance();
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystem.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystem.java
new file mode 100644
index 0000000..ceae842
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystem.java
@@ -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.internal.nativeintegration.filesystem.services;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
+import org.gradle.internal.nativeintegration.filesystem.FileException;
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+import org.gradle.internal.nativeintegration.filesystem.Symlink;
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+
+class GenericFileSystem implements FileSystem {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GenericFileSystem.class);
+
+    final boolean caseSensitive;
+    final boolean canCreateSymbolicLink;
+
+    private final FileModeMutator chmod;
+    private final FileModeAccessor stat;
+    private final Symlink symlink;
+
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+    public boolean canCreateSymbolicLink() {
+        return canCreateSymbolicLink;
+    }
+
+    public void createSymbolicLink(File link, File target) {
+        try {
+            symlink.symlink(link, target);
+        } catch (Exception e) {
+            throw new FileException(String.format("Could not create symlink from '%s' to '%s'.", link.getPath(), target.getPath()), e);
+        }
+    }
+
+    public int getUnixMode(File f) {
+        try {
+            return stat.getUnixMode(f);
+        } catch (Exception e) {
+            throw new FileException(String.format("Could not get file mode for '%s'.", f), e);
+        }
+    }
+
+    public void chmod(File f, int mode) {
+        try {
+            chmod.chmod(f, mode);
+        } catch (Exception e) {
+            throw new FileException(String.format("Could not set file mode %o on '%s'.", mode, f), e);
+        }
+    }
+
+    public GenericFileSystem(FileModeMutator chmod, FileModeAccessor stat, Symlink symlink) {
+        this.stat = stat;
+        this.symlink = symlink;
+        this.chmod = chmod;
+        canCreateSymbolicLink = symlink.isSymlinkSupported();
+        String content = generateUniqueContent();
+        File file = null;
+        try {
+            checkJavaIoTmpDirExists();
+            file = createFile(content);
+            caseSensitive = probeCaseSensitive(file, content);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            FileUtils.deleteQuietly(file);
+        }
+    }
+
+    private String generateUniqueContent() {
+        return UUID.randomUUID().toString();
+    }
+
+    private File createFile(String content) throws IOException {
+        File file = File.createTempFile("gradle_fs_probing", null, null);
+        Files.write(content, file, Charsets.UTF_8);
+        return file;
+    }
+
+    private boolean probeCaseSensitive(File file, String content) {
+        try {
+            File upperCased = new File(file.getPath().toUpperCase());
+            return !hasContent(upperCased, content);
+        } catch (IOException e) {
+            // not fully accurate but a sensible fallback
+            // see http://stackoverflow.com/questions/1288102/how-do-i-detect-whether-the-file-system-is-case-sensitive
+            boolean result = !new File("foo").equals(new File("FOO"));
+            LOGGER.info("Failed to determine if file system is case sensitive. Best guess is '{}'.", result);
+            return result;
+        }
+    }
+
+    private boolean hasContent(File file, String content) throws IOException {
+        return file.exists() && Files.readFirstLine(file, Charsets.UTF_8).equals(content);
+    }
+
+    private void checkJavaIoTmpDirExists() throws IOException {
+        File dir = new File(System.getProperty("java.io.tmpdir"));
+        if (!dir.exists()) {
+            throw new IOException("java.io.tmpdir is set to a directory that doesn't exist: " + dir);
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedChmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedChmod.java
new file mode 100644
index 0000000..d09a708
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedChmod.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import net.rubygrapefruit.platform.PosixFiles;
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+
+import java.io.File;
+
+class NativePlatformBackedChmod implements FileModeMutator {
+    private final PosixFiles posixFiles;
+
+    public NativePlatformBackedChmod(PosixFiles posixFiles) {
+        this.posixFiles = posixFiles;
+    }
+
+    public void chmod(File file, int mode) {
+        posixFiles.setMode(file, mode);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedStat.java
new file mode 100644
index 0000000..2faf78a
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedStat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import net.rubygrapefruit.platform.PosixFiles;
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+
+import java.io.File;
+import java.io.IOException;
+
+class NativePlatformBackedStat implements FileModeAccessor {
+    private final PosixFiles posixFiles;
+
+    public NativePlatformBackedStat(PosixFiles posixFiles) {
+        this.posixFiles = posixFiles;
+    }
+
+    public int getUnixMode(File f) throws IOException {
+        return posixFiles.getMode(f);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedSymlink.java
new file mode 100644
index 0000000..1a4b13d
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/NativePlatformBackedSymlink.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import net.rubygrapefruit.platform.PosixFiles;
+import org.gradle.internal.nativeintegration.filesystem.Symlink;
+
+import java.io.File;
+import java.io.IOException;
+
+class NativePlatformBackedSymlink implements Symlink {
+    private final PosixFiles posixFiles;
+
+    public NativePlatformBackedSymlink(PosixFiles posixFiles) {
+        this.posixFiles = posixFiles;
+    }
+
+    public boolean isSymlinkSupported() {
+        return true;
+    }
+
+    public void symlink(File link, File target) throws IOException {
+        link.getParentFile().mkdirs();
+        posixFiles.symlink(link, target.getPath());
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnavailablePosixFiles.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnavailablePosixFiles.java
new file mode 100644
index 0000000..6573022
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnavailablePosixFiles.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import net.rubygrapefruit.platform.PosixFiles;
+
+public interface UnavailablePosixFiles extends PosixFiles {
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissions.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissions.java
new file mode 100644
index 0000000..6348449
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissions.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor;
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class UnsupportedFilePermissions implements FileModeAccessor, FileModeMutator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(UnsupportedFilePermissions.class);
+    private final AtomicBoolean warned = new AtomicBoolean();
+    private final FallbackStat stat = new FallbackStat();
+    private final EmptyChmod chmod = new EmptyChmod();
+
+    public int getUnixMode(File f) throws IOException {
+        maybeWarn();
+        return stat.getUnixMode(f);
+    }
+
+    public void chmod(File file, int mode) throws Exception {
+        maybeWarn();
+        chmod.chmod(file, mode);
+    }
+
+    private void maybeWarn() {
+        if (warned.compareAndSet(false, true)) {
+            LOGGER.warn("Support for reading or changing file permissions is only available on this platform using Java 7 or later.");
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedSymlink.java
new file mode 100644
index 0000000..3f11efb
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedSymlink.java
@@ -0,0 +1,32 @@
+/*
+ * 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.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.Symlink;
+
+import java.io.File;
+import java.io.IOException;
+
+class UnsupportedSymlink implements Symlink {
+    public boolean isSymlinkSupported() {
+        return false;
+    }
+
+    public void symlink(File link, File target) throws IOException {
+        throw new IOException("Support for the creation of symlinks is only available on this platform using Java 7 or later.");
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/WindowsSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/WindowsSymlink.java
new file mode 100644
index 0000000..8140262
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/filesystem/services/WindowsSymlink.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services;
+
+import org.gradle.internal.nativeintegration.filesystem.Symlink;
+
+import java.io.File;
+import java.io.IOException;
+
+class WindowsSymlink implements Symlink {
+    public boolean isSymlinkSupported() {
+        return false;
+    }
+
+    public void symlink(File link, File target) throws IOException {
+        throw new IOException("Creation of symlinks is not supported on this platform.");
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/JnaBootPathConfigurer.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/JnaBootPathConfigurer.java
new file mode 100644
index 0000000..a1ac86d
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/JnaBootPathConfigurer.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.internal.nativeintegration.jna;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.internal.nativeintegration.NativeIntegrationException;
+import org.gradle.internal.os.OperatingSystem;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class JnaBootPathConfigurer {
+    /**
+     * Attempts to find the jna library and copies it to a specified folder.
+     * The copy operation happens only once. Sets the jna-related system property.
+     *
+     * This hackery is to prevent JNA from creating a shared lib in the tmp dir, as it does not clean things up.
+     *
+     * @param storageDir - where to store the jna library
+     */
+    public void configure(File storageDir) {
+        String nativePrefix = OperatingSystem.current().getNativePrefix();
+        File tmpDir = new File(storageDir, String.format("jna/%s", nativePrefix));
+        tmpDir.mkdirs();
+        String jnaLibName = OperatingSystem.current().isMacOsX() ? "libjnidispatch.jnilib" : System.mapLibraryName("jnidispatch");
+        File libFile = new File(tmpDir, jnaLibName);
+        if (!libFile.exists()) {
+            String resourceName = "/com/sun/jna/" + nativePrefix + "/" + jnaLibName;
+            try {
+                InputStream lib = getClass().getResourceAsStream(resourceName);
+                if (lib == null) {
+                    return;
+                }
+                try {
+                    FileOutputStream outputStream = new FileOutputStream(libFile);
+                    try {
+                        IOUtils.copy(lib, outputStream);
+                    } finally {
+                        outputStream.close();
+                    }
+                } finally {
+                    lib.close();
+                }
+            } catch (IOException e) {
+                throw new NativeIntegrationException(String.format("Could not create JNA native library '%s'.", libFile), e);
+            }
+        }
+        System.setProperty("jna.boot.library.path", tmpDir.getAbsolutePath());
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/UnsupportedEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/UnsupportedEnvironment.java
new file mode 100644
index 0000000..051254c
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/jna/UnsupportedEnvironment.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.internal.nativeintegration.jna;
+
+import org.gradle.internal.nativeintegration.NativeIntegrationException;
+import org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.os.OperatingSystem;
+
+import java.io.File;
+import java.util.Map;
+
+public class UnsupportedEnvironment implements ProcessEnvironment {
+    public boolean maybeSetEnvironment(Map<String, String> source) {
+        return false;
+    }
+
+    public void removeEnvironmentVariable(String name) throws NativeIntegrationException {
+        throw notSupported();
+    }
+
+    public boolean maybeRemoveEnvironmentVariable(String name) {
+        return false;
+    }
+
+    public void setEnvironmentVariable(String name, String value) throws NativeIntegrationException {
+        throw notSupported();
+    }
+
+    public boolean maybeSetEnvironmentVariable(String name, String value) {
+        return false;
+    }
+
+    public File getProcessDir() throws NativeIntegrationException {
+        throw notSupported();
+    }
+
+    public void setProcessDir(File processDir) throws NativeIntegrationException {
+        throw notSupported();
+    }
+
+    public boolean maybeSetProcessDir(File processDir) {
+        return false;
+    }
+
+    public Long getPid() throws NativeIntegrationException {
+        throw notSupported();
+    }
+
+    public Long maybeGetPid() {
+        return null;
+    }
+
+    private NativeIntegrationException notSupported() {
+        return new NativeIntegrationUnavailableException("We don't support this operating system: " + OperatingSystem.current());
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/AbstractProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/AbstractProcessEnvironment.java
new file mode 100755
index 0000000..c6919da
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/AbstractProcessEnvironment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration.processenvironment;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.gradle.internal.nativeintegration.NativeIntegrationException;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.ReflectiveEnvironment;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public abstract class AbstractProcessEnvironment implements ProcessEnvironment {
+    //for updates to private JDK caches of the environment state
+    private final ReflectiveEnvironment reflectiveEnvironment = new ReflectiveEnvironment();
+
+    public boolean maybeSetEnvironment(Map<String, String> source) {
+        // need to take copy to prevent ConcurrentModificationException
+        List<String> keysToRemove = Lists.newArrayList(Sets.difference(System.getenv().keySet(), source.keySet()));
+        for (String key : keysToRemove) {
+            removeEnvironmentVariable(key);
+        }
+        for (Map.Entry<String, String> entry : source.entrySet()) {
+            setEnvironmentVariable(entry.getKey(), entry.getValue());
+        }
+        return true;
+    }
+
+    public void removeEnvironmentVariable(String name) throws NativeIntegrationException {
+        removeNativeEnvironmentVariable(name);
+        reflectiveEnvironment.unsetenv(name);
+    }
+
+    protected abstract void removeNativeEnvironmentVariable(String name);
+
+    public boolean maybeRemoveEnvironmentVariable(String name) {
+        removeEnvironmentVariable(name);
+        return true;
+    }
+
+    public void setEnvironmentVariable(String name, String value) throws NativeIntegrationException {
+        if (value == null) {
+            removeEnvironmentVariable(name);
+            return;
+        }
+
+        setNativeEnvironmentVariable(name, value);
+        reflectiveEnvironment.setenv(name, value);
+    }
+
+    protected abstract void setNativeEnvironmentVariable(String name, String value);
+
+    public boolean maybeSetEnvironmentVariable(String name, String value) {
+        setEnvironmentVariable(name, value);
+        return true;
+    }
+
+    public void setProcessDir(File processDir) throws NativeIntegrationException {
+        if (!processDir.exists()) {
+            return;
+        }
+
+        setNativeProcessDir(processDir);
+        System.setProperty("user.dir", processDir.getAbsolutePath());
+    }
+
+    protected abstract void setNativeProcessDir(File processDir);
+
+    public boolean maybeSetProcessDir(File processDir) {
+        setProcessDir(processDir);
+        return true;
+    }
+
+    public Long maybeGetPid() {
+        return getPid();
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/NativePlatformBackedProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/NativePlatformBackedProcessEnvironment.java
new file mode 100755
index 0000000..a53e234
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/processenvironment/NativePlatformBackedProcessEnvironment.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.internal.nativeintegration.processenvironment;
+
+import net.rubygrapefruit.platform.Process;
+
+import java.io.File;
+
+public class NativePlatformBackedProcessEnvironment extends AbstractProcessEnvironment {
+    private final Process process;
+
+    public NativePlatformBackedProcessEnvironment(Process process) {
+        this.process = process;
+    }
+
+    @Override
+    protected void removeNativeEnvironmentVariable(String name) {
+        process.setEnvironmentVariable(name, null);
+    }
+
+    @Override
+    protected void setNativeEnvironmentVariable(String name, String value) {
+        process.setEnvironmentVariable(name, value);
+    }
+
+    @Override
+    protected void setNativeProcessDir(File processDir) {
+        process.setWorkingDirectory(processDir);
+    }
+
+    public File getProcessDir() {
+        return process.getWorkingDirectory();
+    }
+
+    public Long getPid() {
+        return Long.valueOf(process.getProcessId());
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/FileSystems.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/FileSystems.java
new file mode 100644
index 0000000..6550782
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/FileSystems.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.internal.nativeintegration.services;
+
+import org.gradle.internal.nativeplatform.filesystem.FileSystem;
+
+public abstract class FileSystems {
+    public static FileSystem getDefault() {
+        return NativeServices.getInstance().get(FileSystem.class);
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/NativeServices.java b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/NativeServices.java
new file mode 100755
index 0000000..90cda3d
--- /dev/null
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeintegration/services/NativeServices.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration.services;
+
+import net.rubygrapefruit.platform.*;
+import net.rubygrapefruit.platform.Process;
+import net.rubygrapefruit.platform.internal.DefaultProcessLauncher;
+import org.gradle.internal.SystemProperties;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.nativeintegration.console.ConsoleDetector;
+import org.gradle.internal.nativeintegration.console.NativePlatformConsoleDetector;
+import org.gradle.internal.nativeintegration.console.NoOpConsoleDetector;
+import org.gradle.internal.nativeintegration.console.WindowsConsoleDetector;
+import org.gradle.internal.nativeintegration.filesystem.services.FileSystemServices;
+import org.gradle.internal.nativeintegration.filesystem.services.UnavailablePosixFiles;
+import org.gradle.internal.nativeintegration.jna.JnaBootPathConfigurer;
+import org.gradle.internal.nativeintegration.jna.UnsupportedEnvironment;
+import org.gradle.internal.nativeintegration.processenvironment.NativePlatformBackedProcessEnvironment;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.service.DefaultServiceRegistry;
+import org.gradle.internal.service.ServiceRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Provides various native platform integration services.
+ */
+public class NativeServices extends DefaultServiceRegistry implements ServiceRegistry {
+    private static final Logger LOGGER = LoggerFactory.getLogger(NativeServices.class);
+    private static boolean useNativePlatform = "true".equalsIgnoreCase(System.getProperty("org.gradle.native", "true"));
+    private static final NativeServices INSTANCE = new NativeServices();
+    private static boolean initialized;
+
+    /**
+     * Initializes the native services to use the given user home directory to store native libs and other resources. Does nothing if already initialized. Will be implicitly initialized on first usage
+     * of a native service. Also initializes the Native-Platform library using the given user home directory.
+     */
+    public static void initialize(File userHomeDir) {
+        initialize(userHomeDir, true);
+    }
+
+    public static synchronized void initialize(File userHomeDir, boolean initializeJNA) {
+        File nativeDir = new File(userHomeDir, "native");
+        if (useNativePlatform) {
+            try {
+                net.rubygrapefruit.platform.Native.init(nativeDir);
+            } catch (NativeIntegrationUnavailableException ex) {
+                LOGGER.debug("Native-platform is not available.");
+                useNativePlatform = false;
+            } catch (NativeException ex) {
+                LOGGER.debug("Unable to initialize native-platform. Failure: {}", format(ex));
+                useNativePlatform = false;
+            }
+        }
+        if (OperatingSystem.current().isWindows() && initializeJNA) {
+            // JNA is still being used by jansi
+            new JnaBootPathConfigurer().configure(nativeDir);
+        }
+        initialized = true;
+    }
+
+    public static synchronized NativeServices getInstance() {
+        if (!initialized) {
+            // If this occurs while running gradle or running integration tests, it is indicative of a problem.
+            // If this occurs while running unit tests, then either use the NativeServicesTestFixture or the '@UsesNativeServices' annotation.
+            throw new IllegalStateException("Cannot get an instance of NativeServices without first calling initialize().");
+        }
+        return INSTANCE;
+    }
+
+    private NativeServices() {
+        addProvider(new FileSystemServices());
+    }
+
+    @Override
+    public void close() {
+        // Don't close
+    }
+
+    protected OperatingSystem createOperatingSystem() {
+        return OperatingSystem.current();
+    }
+
+    protected Jvm createJvm() {
+        return Jvm.current();
+    }
+
+    protected ProcessEnvironment createProcessEnvironment(OperatingSystem operatingSystem) {
+        if (useNativePlatform) {
+            try {
+                net.rubygrapefruit.platform.Process process = net.rubygrapefruit.platform.Native.get(Process.class);
+                return new NativePlatformBackedProcessEnvironment(process);
+            } catch (NativeIntegrationUnavailableException ex) {
+                LOGGER.debug("Native-platform process integration is not available. Continuing with fallback.");
+            }
+        }
+
+        return new UnsupportedEnvironment();
+    }
+
+    protected ConsoleDetector createConsoleDetector(OperatingSystem operatingSystem) {
+        if (useNativePlatform) {
+            try {
+                Terminals terminals = net.rubygrapefruit.platform.Native.get(Terminals.class);
+                return new NativePlatformConsoleDetector(terminals);
+            } catch (NativeIntegrationUnavailableException ex) {
+                LOGGER.debug("Native-platform terminal integration is not available. Continuing with fallback.");
+            } catch (NativeException ex) {
+                LOGGER.debug("Unable to load from native-platform backed ConsoleDetector. Continuing with fallback. Failure: {}", format(ex));
+            }
+        }
+
+        try {
+            if (operatingSystem.isWindows()) {
+                return new WindowsConsoleDetector();
+            }
+        } catch (LinkageError e) {
+            // Thrown when jna cannot initialize the native stuff
+            LOGGER.debug("Unable to load native library. Continuing with fallback. Failure: {}", format(e));
+        }
+        return new NoOpConsoleDetector();
+    }
+
+    protected WindowsRegistry createWindowsRegistry(OperatingSystem operatingSystem) {
+        if (useNativePlatform && operatingSystem.isWindows()) {
+            return net.rubygrapefruit.platform.Native.get(WindowsRegistry.class);
+        }
+        return notAvailable(WindowsRegistry.class);
+    }
+
+    protected SystemInfo createSystemInfo() {
+        if (useNativePlatform) {
+            try {
+                return net.rubygrapefruit.platform.Native.get(SystemInfo.class);
+            } catch (NativeIntegrationUnavailableException e) {
+                LOGGER.debug("Native-platform system info is not available. Continuing with fallback.");
+            }
+        }
+        return notAvailable(SystemInfo.class);
+    }
+
+    protected ProcessLauncher createProcessLauncher() {
+        if (useNativePlatform) {
+            try {
+                return net.rubygrapefruit.platform.Native.get(ProcessLauncher.class);
+            } catch (NativeIntegrationUnavailableException e) {
+                LOGGER.debug("Native-platform process launcher is not available. Continuing with fallback.");
+            }
+        }
+        return new DefaultProcessLauncher();
+    }
+
+    protected PosixFiles createPosixFiles() {
+        if (useNativePlatform) {
+            try {
+                return net.rubygrapefruit.platform.Native.get(PosixFiles.class);
+            } catch (NativeIntegrationUnavailableException e) {
+                LOGGER.debug("Native-platform posix files is not available.  Continuing with fallback.");
+            }
+        }
+        return notAvailable(UnavailablePosixFiles.class);
+    }
+
+    private <T> T notAvailable(Class<T> type) {
+        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new BrokenService(type.getSimpleName()));
+    }
+
+    private static String format(Throwable throwable) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(throwable.toString());
+        for (Throwable current = throwable.getCause(); current != null; current = current.getCause()) {
+            builder.append(SystemProperties.getInstance().getLineSeparator());
+            builder.append("caused by: ");
+            builder.append(current.toString());
+        }
+        return builder.toString();
+    }
+
+    private static class BrokenService implements InvocationHandler {
+        private final String type;
+
+        private BrokenService(String type) {
+            this.type = type;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            throw new org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException(String.format("%s is not supported on this operating system.", type));
+        }
+    }
+}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationException.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationException.java
deleted file mode 100644
index b5296ef..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationException.java
+++ /dev/null
@@ -1,26 +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.internal.nativeplatform;
-
-public class NativeIntegrationException extends RuntimeException {
-    public NativeIntegrationException(String message) {
-        super(message);
-    }
-
-    public NativeIntegrationException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationUnavailableException.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationUnavailableException.java
deleted file mode 100644
index 1990a2d..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/NativeIntegrationUnavailableException.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.internal.nativeplatform;
-
-/**
- * Thrown when the native integration for the current platform is not available for some reason (eg unsupported operating system, cannot load native library, etc).
- */
-public class NativeIntegrationUnavailableException extends NativeIntegrationException {
-    public NativeIntegrationUnavailableException(String message) {
-        super(message);
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ProcessEnvironment.java
deleted file mode 100644
index 650e93c..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ProcessEnvironment.java
+++ /dev/null
@@ -1,103 +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.internal.nativeplatform;
-
-import java.io.File;
-import java.util.Map;
-
-/**
- * Provides access to information about the current process.
- *
- * <p>Implementations are not thread-safe.</p>
- */
-public interface ProcessEnvironment {
-    /**
-     * Sets the environment of this process, if possible.
-     *
-     * @param source The environment
-     * @return true if environment changed, false if not possible.
-     */
-    public boolean maybeSetEnvironment(Map<String, String> source);
-
-    /**
-     * Removes the given environment variable.
-     *
-     * @param name The name of the environment variable.
-     * @throws NativeIntegrationException If the environment variable cannot be removed.
-     */
-    void removeEnvironmentVariable(String name) throws NativeIntegrationException;
-
-    /**
-     * Removes the given environment variable, if possible.
-     *
-     * @param name The name of the environment variable.
-     * @return true if removed, false if not possible.
-     */
-    boolean maybeRemoveEnvironmentVariable(String name);
-
-    /**
-     * Sets the given environment variable.
-     *
-     * @param name The name
-     * @param value The value. Can be null, which removes the environment variable.
-     * @throws NativeIntegrationException If the environment variable cannot be set.
-     */
-    void setEnvironmentVariable(String name, String value) throws NativeIntegrationException;
-
-    /**
-     * Sets the given environment variable, if possible.
-     *
-     * @param name The name
-     * @param value The value
-     * @return true if set, false if not possible.
-     */
-    boolean maybeSetEnvironmentVariable(String name, String value);
-
-    /**
-     * Returns the working directory of the current process.
-     *
-     * @throws NativeIntegrationException If the process directory is not available.
-     */
-    File getProcessDir() throws NativeIntegrationException;
-
-    /**
-     * Sets the process working directory.
-     *
-     * @param processDir The directory.
-     * @throws NativeIntegrationException If process directory cannot be set.
-     */
-    void setProcessDir(File processDir) throws NativeIntegrationException;
-
-    /**
-     * Sets the process working directory, if possible
-     *
-     * @param processDir The directory.
-     * @return true if the directory can be set, false if not possible.
-     */
-    boolean maybeSetProcessDir(File processDir);
-
-    /**
-     * Returns the OS level PID for the current process.
-     *
-     * @throws NativeIntegrationException If the pid is not available.
-     */
-    Long getPid() throws NativeIntegrationException;
-
-    /**
-     * Returns the OS level PID for the current process, or null if not available.
-     */
-    Long maybeGetPid();
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ReflectiveEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ReflectiveEnvironment.java
deleted file mode 100644
index 242da0b..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/ReflectiveEnvironment.java
+++ /dev/null
@@ -1,76 +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.internal.nativeplatform;
-
-import org.gradle.internal.os.OperatingSystem;
-
-import java.lang.reflect.Field;
-import java.util.Map;
-
-/**
- * Uses reflection to update private environment state
- */
-public class ReflectiveEnvironment {
-
-    public void unsetenv(String name) {
-        Map<String, String> map = getEnv();
-        map.remove(name);
-        if (OperatingSystem.current().isWindows()) {
-            Map<String, String> env2 = getWindowsEnv();
-            env2.remove(name);
-        }
-    }
-
-    public void setenv(String name, String value) {
-        Map<String, String> map = getEnv();
-        map.put(name, value);
-        if (OperatingSystem.current().isWindows()) {
-            Map<String, String> env2 = getWindowsEnv();
-            env2.put(name, value);
-        }
-    }
-
-    /**
-     * Windows keeps an extra map with case insensitive keys. The map is used when the user calls {@link System#getenv(String)}
-     */
-    private Map<String, String> getWindowsEnv() {
-        try {
-            Class<?> sc = Class.forName("java.lang.ProcessEnvironment");
-            Field caseinsensitive = sc.getDeclaredField("theCaseInsensitiveEnvironment");
-            caseinsensitive.setAccessible(true);
-            @SuppressWarnings("unchecked")
-            Map<String, String> result = (Map<String, String>)caseinsensitive.get(null);
-            return result;
-        } catch (Exception e) {
-            throw new NativeIntegrationException("Unable to get mutable windows case insensitive environment map", e);
-        }
-    }
-
-    private Map<String, String> getEnv() {
-        try {
-            Map<String, String> theUnmodifiableEnvironment = System.getenv();
-            Class<?> cu = theUnmodifiableEnvironment.getClass();
-            Field m = cu.getDeclaredField("m");
-            m.setAccessible(true);
-            @SuppressWarnings("unchecked")
-            Map<String, String> result = (Map<String, String>)m.get(theUnmodifiableEnvironment);
-            return result;
-        } catch (Exception e) {
-            throw new NativeIntegrationException("Unable to get mutable environment map", e);
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleDetector.java
deleted file mode 100644
index 5de70ab..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleDetector.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.internal.nativeplatform.console;
-
-import org.gradle.api.Nullable;
-
-public interface ConsoleDetector {
-    /**
-     * Locates the console for this process, if any.
-     *
-     * @return Information about the console, or null if this process is not attached to the console.
-     */
-    @Nullable
-    ConsoleMetaData getConsole();
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleMetaData.java
deleted file mode 100644
index b6c54f7..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/ConsoleMetaData.java
+++ /dev/null
@@ -1,36 +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.nativeplatform.console;
-
-public interface ConsoleMetaData {
-    /**
-     * Returns true if the current process' stdout is attached to the console.
-     */
-    boolean isStdOut();
-
-    /**
-     * Returns true if the current process' stderr is attached to the console.
-     */
-    boolean isStdErr();
-
-    /**
-     * <p>Returns the number of columns available in the console.</p>
-     *
-     * @return The number of columns available in the console. If no information is available return 0.
-     */
-    public int getCols();
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/FallbackConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/FallbackConsoleMetaData.java
deleted file mode 100644
index d922297..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/FallbackConsoleMetaData.java
+++ /dev/null
@@ -1,31 +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.nativeplatform.console;
-
-public class FallbackConsoleMetaData implements ConsoleMetaData{
-    public boolean isStdOut() {
-        return true;
-    }
-
-    public boolean isStdErr() {
-        return true;
-    }
-
-    public int getCols() {
-        return 0;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetector.java
deleted file mode 100644
index b69d06a..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetector.java
+++ /dev/null
@@ -1,48 +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.nativeplatform.console;
-
-import net.rubygrapefruit.platform.Terminals;
-
-import static net.rubygrapefruit.platform.Terminals.Output.Stderr;
-import static net.rubygrapefruit.platform.Terminals.Output.Stdout;
-
-public class NativePlatformConsoleDetector implements ConsoleDetector {
-    private final Terminals terminals;
-
-    public NativePlatformConsoleDetector(Terminals terminals) {
-        this.terminals = terminals;
-    }
-
-    public ConsoleMetaData getConsole() {
-        // Dumb terminal doesn't support ANSI control codes.
-        // TODO - remove this when we use Terminal rather than JAnsi to render to console
-        String term = System.getenv("TERM");
-        if (term != null && term.equals("dumb")) {
-            return null;
-        }
-
-        boolean stdout = terminals.isTerminal(Stdout);
-        boolean stderr = terminals.isTerminal(Stderr);
-        if (stdout) {
-            return new NativePlatformConsoleMetaData(stdout, stderr, terminals.getTerminal(Stdout));
-        } else if (stderr) {
-            return new NativePlatformConsoleMetaData(stdout, stderr, terminals.getTerminal(Stderr));
-        }
-        return null;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleMetaData.java
deleted file mode 100644
index 75ecf45..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NativePlatformConsoleMetaData.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.internal.nativeplatform.console;
-
-import net.rubygrapefruit.platform.Terminal;
-
-public class NativePlatformConsoleMetaData implements ConsoleMetaData {
-    private final boolean stdout;
-    private final boolean stderr;
-    private final Terminal terminal;
-
-    public NativePlatformConsoleMetaData(boolean stdout, boolean stderr, Terminal terminal) {
-        this.stdout = stdout;
-        this.stderr = stderr;
-        this.terminal = terminal;
-    }
-
-    public boolean isStdOut() {
-        return stdout;
-    }
-
-    public boolean isStdErr() {
-        return stderr;
-    }
-
-    public int getCols() {
-        return terminal.getTerminalSize().getCols();
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NoOpConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NoOpConsoleDetector.java
deleted file mode 100644
index 119300d..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/NoOpConsoleDetector.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.internal.nativeplatform.console;
-
-public class NoOpConsoleDetector implements ConsoleDetector {
-    public ConsoleMetaData getConsole() {
-        return null;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/UnixConsoleMetaData.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/UnixConsoleMetaData.java
deleted file mode 100644
index 9bfdcb1..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/UnixConsoleMetaData.java
+++ /dev/null
@@ -1,51 +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.nativeplatform.console;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class UnixConsoleMetaData implements ConsoleMetaData {
-    public static final Logger LOGGER = LoggerFactory.getLogger(UnixConsoleMetaData.class);
-    private final boolean stdout;
-    private final boolean stderr;
-
-    public UnixConsoleMetaData(boolean stdout, boolean stderr) {
-        this.stdout = stdout;
-        this.stderr = stderr;
-    }
-
-    public boolean isStdOut() {
-        return stdout;
-    }
-
-    public boolean isStdErr() {
-        return stderr;
-    }
-
-    public int getCols() {
-        final String columns = System.getenv("COLUMNS");
-        if (columns != null) {
-            try {
-                return Integer.parseInt(columns);
-            } catch (NumberFormatException ex) {
-                LOGGER.debug("Cannot parse COLUMNS environment variable to get console width. Value: '{}'", columns);
-            }
-        }
-        return 0;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/WindowsConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/WindowsConsoleDetector.java
deleted file mode 100755
index 7ef3716..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/console/WindowsConsoleDetector.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.internal.nativeplatform.console;
-
-import org.fusesource.jansi.WindowsAnsiOutputStream;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-public class WindowsConsoleDetector implements ConsoleDetector {
-    public ConsoleMetaData getConsole() {
-        // Use Jansi's detection mechanism
-        try {
-            new WindowsAnsiOutputStream(new ByteArrayOutputStream());
-            return new FallbackConsoleMetaData();
-        } catch (IOException ignore) {
-            // Not attached to a console
-            return null;
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Chmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Chmod.java
deleted file mode 100644
index 66a7c68..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Chmod.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.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-public interface Chmod {
-    /**
-     * Changes the Unix permissions of a provided file. Implementations that don't
-     * support Unix permissions may choose to ignore this request.
-     *
-     * @param file the file to change permissions on
-     * @param mode the permissions, e.g. 0755
-     * @throws java.io.FileNotFoundException if {@code file} doesn't exist
-     * @throws IOException if the permissions can't be changed
-     */
-    public void chmod(File file, int mode) throws IOException;
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/DefaultFilePathEncoder.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/DefaultFilePathEncoder.java
deleted file mode 100644
index 35be6a4..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/DefaultFilePathEncoder.java
+++ /dev/null
@@ -1,42 +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.nativeplatform.filesystem;
-
-import com.sun.jna.WString;
-import org.gradle.internal.nativeplatform.jna.LibC;
-
-import java.io.File;
-
-class DefaultFilePathEncoder implements FilePathEncoder {
-    private final LibC libC;
-
-    DefaultFilePathEncoder(LibC libC) {
-        this.libC = libC;
-    }
-
-    public byte[] encode(File file) {
-        byte[] path = new byte[file.getAbsolutePath().length() * 3 + 1];
-        int pathLength = libC.wcstombs(path, new WString(file.getAbsolutePath()), path.length);
-        if (pathLength < 0) {
-            throw new RuntimeException(String.format("Could not encode file path '%s'.", file.getAbsolutePath()));
-        }
-        byte[] zeroTerminatedByteArray = new byte[pathLength + 1];
-        System.arraycopy(path, 0, zeroTerminatedByteArray, 0, pathLength);
-        zeroTerminatedByteArray[pathLength] = 0;
-        return zeroTerminatedByteArray;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/EmptyChmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/EmptyChmod.java
deleted file mode 100644
index 65bc53c..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/EmptyChmod.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.internal.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-class EmptyChmod implements Chmod {
-    public void chmod(File f, int mode) throws IOException {
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackStat.java
deleted file mode 100644
index 1108039..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackStat.java
+++ /dev/null
@@ -1,30 +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.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-class FallbackStat implements Stat {
-    public int getUnixMode(File f) throws IOException {
-        if (f.isDirectory()) {
-            return FileSystem.DEFAULT_DIR_MODE;
-        } else {
-            return FileSystem.DEFAULT_FILE_MODE;
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackSymlink.java
deleted file mode 100644
index 053ba76..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FallbackSymlink.java
+++ /dev/null
@@ -1,26 +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.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-public class FallbackSymlink implements Symlink {
-    public void symlink(File link, File target) throws IOException {
-        throw new IOException("Creation of symlinks is not supported on the platform.");
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FilePathEncoder.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FilePathEncoder.java
deleted file mode 100644
index b671c20..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FilePathEncoder.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.internal.nativeplatform.filesystem;
-
-import java.io.File;
-
-interface FilePathEncoder {
-    byte[] encode(File file);
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystem.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystem.java
index 20ab029..36a9303 100755
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystem.java
+++ b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystem.java
@@ -15,68 +15,8 @@
  */
 package org.gradle.internal.nativeplatform.filesystem;
 
-import java.io.File;
-import java.io.IOException;
-
 /**
  * A file system accessible to Gradle.
  */
-public interface FileSystem extends Chmod, Stat {
-    /**
-     * Default Unix permissions for directories, {@code 755}.
-     */
-    public static final int DEFAULT_DIR_MODE = 0755;
-
-    /**
-     * Default Unix permissions for files, {@code 644}.
-     */
-    public static final int DEFAULT_FILE_MODE = 0644;
-
-    /**
-     * Tells whether the file system is case sensitive.
-     *
-     * @return <tt>true</tt> if the file system is case sensitive, <tt>false</tt> otherwise
-     */
-    boolean isCaseSensitive();
-
-    /**
-     * Tells if the file system can create symbolic links. If the answer cannot be determined accurately,
-     * <tt>false</tt> is returned.
-     *
-     * @return <tt>true</tt> if the file system can create symbolic links, <tt>false</tt> otherwise
-     */
-    boolean canCreateSymbolicLink();
-
-    /**
-     * Creates a symbolic link to a target file.
-     *
-     *
-     * @param link the link to be created
-     * @param target the file to link to
-     * @exception java.io.IOException if the operation fails
-     */
-    void createSymbolicLink(File link, File target) throws IOException;
-
-    /**
-     * Tries to create a symbolic link to a target file.
-     *
-     * @param link the link to be created
-     * @param target the file to link to
-     * @return <tt>true</tt> if the operation was successful, <tt>false</tt> otherwise
-     */
-    boolean tryCreateSymbolicLink(File link, File target);
-
-    /**
-     * Returns the Unix permissions for a provided file. Some file systems may not
-     * support Unix permissions, in which case sensible default values are returned
-     * instead.
-     *
-     * @param file the file to read permissions from
-     * @throws java.io.FileNotFoundException if {@code file} doesn't exist
-     * @throws IOException if the permissions can't be read
-     * @return the file's Unix permissions, e.g. 0755
-     * @see #DEFAULT_DIR_MODE
-     * @see #DEFAULT_FILE_MODE
-     */
-    int getUnixMode(File file) throws IOException;
+public interface FileSystem extends org.gradle.internal.nativeintegration.filesystem.FileSystem {
 }
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystemServices.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystemServices.java
deleted file mode 100644
index 7e23061..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/FileSystemServices.java
+++ /dev/null
@@ -1,116 +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.nativeplatform.filesystem;
-
-import com.sun.jna.Native;
-import net.rubygrapefruit.platform.NativeIntegrationUnavailableException;
-import net.rubygrapefruit.platform.PosixFiles;
-import org.gradle.api.JavaVersion;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.nativeplatform.jna.LibC;
-import org.gradle.internal.os.OperatingSystem;
-import org.jruby.ext.posix.BaseNativePOSIX;
-import org.jruby.ext.posix.JavaPOSIX;
-import org.jruby.ext.posix.POSIX;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FileSystemServices {
-    private static final Logger LOGGER = LoggerFactory.getLogger(FileSystemServices.class);
-
-    @SuppressWarnings("UnusedDeclaration")
-    public FileSystem createFileSystem(OperatingSystem operatingSystem) {
-        // Use no-op implementations for windows
-        if (operatingSystem.isWindows()) {
-            return new GenericFileSystem(new EmptyChmod(), new FallbackStat(), new FallbackSymlink());
-        }
-
-        try {
-            PosixFiles posixFiles = net.rubygrapefruit.platform.Native.get(PosixFiles.class);
-            Symlink symlink = new NativePlatformBackedSymlink(posixFiles);
-            Chmod chmod = new NativePlatformBackedChmod(posixFiles);
-            Stat stat = new NativePlatformBackedStat(posixFiles);
-            return new GenericFileSystem(chmod, stat, symlink);
-        } catch (NativeIntegrationUnavailableException ex) {
-            LOGGER.debug("Native-platform file system integration is not available. Continuing with fallback.");
-        }
-
-        LibC libC = loadLibC();
-        Symlink symlink = symlink(libC);
-
-        // Use libc backed implementations on Linux, if libc available
-        POSIX posix = PosixUtil.current();
-        if ((libC != null && (operatingSystem.isLinux())) && posix instanceof BaseNativePOSIX) {
-            FilePathEncoder filePathEncoder = new DefaultFilePathEncoder(libC);
-            Chmod chmod = new LibcChmod(libC, filePathEncoder);
-            Stat stat = new LibCStat(libC, operatingSystem, (BaseNativePOSIX) posix, filePathEncoder);
-            return new GenericFileSystem(chmod, stat, symlink);
-        }
-
-        // Use java 7 APIs, if available
-        if (JavaVersion.current().isJava7()) {
-            String jdkFilePermissionclass = "org.gradle.internal.nativeplatform.filesystem.jdk7.PosixJdk7FilePermissionHandler";
-            try {
-                Object handler = FileSystemServices.class.getClassLoader().loadClass(jdkFilePermissionclass).newInstance();
-                return new GenericFileSystem((Chmod) handler, (Stat) handler, symlink);
-            } catch (ClassNotFoundException e) {
-                LOGGER.warn(String.format("Unable to load %s. Continuing with fallback.", jdkFilePermissionclass));
-            } catch (Exception e) {
-                throw UncheckedException.throwAsUncheckedException(e);
-            }
-        }
-
-        // Attempt to use libc for chmod and posix for stat, and fallback to no-op implementations if not available
-        return new GenericFileSystem(chmod(libC), stat(), symlink);
-    }
-
-    private Symlink symlink(LibC libC) {
-        if (libC != null) {
-            return new LibcSymlink(libC);
-        }
-        LOGGER.debug("Using FallbackSymlink implementation.");
-        return new FallbackSymlink();
-    }
-
-    private Stat stat() {
-        POSIX posix = PosixUtil.current();
-        if (posix instanceof JavaPOSIX) {
-            return new FallbackStat();
-        } else {
-            return new PosixStat(posix);
-        }
-    }
-
-    private Chmod chmod(LibC libC) {
-        if (libC != null) {
-            return new LibcChmod(libC, new DefaultFilePathEncoder(libC));
-        }
-        LOGGER.debug("Using EmptyChmod implementation.");
-        return new EmptyChmod();
-    }
-
-    private static LibC loadLibC() {
-        try {
-            return (LibC) Native.loadLibrary("c", LibC.class);
-        } catch (LinkageError e) {
-            LOGGER.debug("Unable to load LibC library. Continuing with fallback filesystem implementations.");
-            return null;
-        }
-    }
-
-}
-
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/GenericFileSystem.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/GenericFileSystem.java
deleted file mode 100644
index 6327e41..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/GenericFileSystem.java
+++ /dev/null
@@ -1,144 +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.internal.nativeplatform.filesystem;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.io.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.UUID;
-
-public class GenericFileSystem implements FileSystem {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GenericFileSystem.class);
-
-    final boolean caseSensitive;
-    final boolean canCreateSymbolicLink;
-
-    private final Chmod chmod;
-    private final Stat stat;
-    private final Symlink symlink;
-
-    public boolean isCaseSensitive() {
-        return caseSensitive;
-    }
-
-    public boolean canCreateSymbolicLink() {
-        return canCreateSymbolicLink;
-    }
-
-    public void createSymbolicLink(File link, File target) throws IOException {
-        symlink.symlink(link, target);
-    }
-
-    public boolean tryCreateSymbolicLink(File link, File target) {
-        try {
-            symlink.symlink(link, target);
-            return true;
-        } catch (IOException e) {
-            return false;
-        }
-    }
-
-    public int getUnixMode(File f) throws IOException {
-        assertFileExists(f);
-        return stat.getUnixMode(f);
-    }
-
-    public void chmod(File f, int mode) throws IOException {
-        assertFileExists(f);
-        chmod.chmod(f, mode);
-    }
-
-    protected final void assertFileExists(File f) throws FileNotFoundException {
-        if (!f.exists()) {
-            throw new FileNotFoundException(f + " does not exist");
-        }
-    }
-
-    public GenericFileSystem(Chmod chmod, Stat stat, Symlink symlink) {
-        this.stat = stat;
-        this.symlink = symlink;
-        this.chmod = chmod;
-        String content = generateUniqueContent();
-        File file = null;
-        try {
-            checkJavaIoTmpDirExists();
-            file = createFile(content);
-            caseSensitive = probeCaseSensitive(file, content);
-            canCreateSymbolicLink = probeCanCreateSymbolicLink(file, content);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            FileUtils.deleteQuietly(file);
-        }
-    }
-
-    private String generateUniqueContent() {
-        return UUID.randomUUID().toString();
-    }
-
-    private File createFile(String content) throws IOException {
-        File file = File.createTempFile("gradle_fs_probing", null, null);
-        Files.write(content, file, Charsets.UTF_8);
-        return file;
-    }
-
-    private boolean probeCaseSensitive(File file, String content) {
-        try {
-            File upperCased = new File(file.getPath().toUpperCase());
-            return !hasContent(upperCased, content);
-        } catch (IOException e) {
-            // not fully accurate but a sensible fallback
-            // see http://stackoverflow.com/questions/1288102/how-do-i-detect-whether-the-file-system-is-case-sensitive
-            boolean result = !new File("foo").equals(new File("FOO"));
-            LOGGER.info("Failed to determine if file system is case sensitive. Best guess is '{}'.", result);
-            return result;
-        }
-    }
-
-    private boolean probeCanCreateSymbolicLink(File file, String content) {
-        File link = null;
-        try {
-            link = generateUniqueTempFileName();
-            return tryCreateSymbolicLink(link, file) && hasContent(link, content);
-        } catch (IOException e) {
-            LOGGER.info("Failed to determine if file system can create symbolic links. Assuming it can't.");
-            return false;
-        } finally {
-            FileUtils.deleteQuietly(link);
-        }
-    }
-
-    private boolean hasContent(File file, String content) throws IOException {
-        return file.exists() && Files.readFirstLine(file, Charsets.UTF_8).equals(content);
-    }
-
-    private File generateUniqueTempFileName() throws IOException {
-        return new File(System.getProperty("java.io.tmpdir"), "gradle_unique_file_name" + UUID.randomUUID().toString());
-    }
-
-    private void checkJavaIoTmpDirExists() throws IOException {
-        File dir = new File(System.getProperty("java.io.tmpdir"));
-        if (!dir.exists()) {
-            throw new IOException("java.io.tmpdir is set to a directory that doesn't exist: " + dir);
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibCStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibCStat.java
deleted file mode 100644
index a9ad76c..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibCStat.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.internal.nativeplatform.filesystem;
-
-import org.gradle.internal.nativeplatform.jna.LibC;
-import org.gradle.internal.os.OperatingSystem;
-import org.jruby.ext.posix.BaseNativePOSIX;
-import org.jruby.ext.posix.FileStat;
-import org.jruby.ext.posix.Linux64FileStat;
-
-import java.io.File;
-import java.io.IOException;
-
-class LibCStat implements Stat {
-    private final LibC libc;
-    private final FilePathEncoder encoder;
-    private final OperatingSystem operatingSystem;
-    private final BaseNativePOSIX nativePOSIX;
-
-    public LibCStat(LibC libc, OperatingSystem operatingSystem, BaseNativePOSIX nativePOSIX, FilePathEncoder encoder) {
-        this.libc = libc;
-        this.operatingSystem = operatingSystem;
-        this.nativePOSIX = nativePOSIX;
-        this.encoder = encoder;
-    }
-
-    public int getUnixMode(File f) throws IOException {
-        FileStat stat = nativePOSIX.allocateStat();
-        initPlatformSpecificStat(stat, encoder.encode(f));
-        return stat.mode() & 0777;
-    }
-
-    private void initPlatformSpecificStat(FileStat stat, byte[] encodedFilePath) {
-        if (operatingSystem.isMacOsX()) {
-            libc.stat(encodedFilePath, stat);
-        } else {
-            final int statVersion = stat instanceof Linux64FileStat ? 3 : 0;
-            libc.__xstat64(statVersion, encodedFilePath, stat);
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcChmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcChmod.java
deleted file mode 100644
index aee2921..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcChmod.java
+++ /dev/null
@@ -1,42 +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.nativeplatform.filesystem;
-
-import com.sun.jna.LastErrorException;
-import org.gradle.internal.nativeplatform.jna.LibC;
-
-import java.io.File;
-import java.io.IOException;
-
-class LibcChmod implements Chmod {
-    private final LibC libc;
-    private final FilePathEncoder encoder;
-
-    public LibcChmod(LibC libc, FilePathEncoder encoder) {
-        this.libc = libc;
-        this.encoder = encoder;
-    }
-
-    public void chmod(File f, int mode) throws IOException {
-        try {
-            byte[] encodedFilePath = encoder.encode(f);
-            libc.chmod(encodedFilePath, mode);
-        } catch (LastErrorException exception) {
-            throw new IOException(String.format("Failed to set file permissions %s on file %s. errno: %d", mode, f.getName(), exception.getErrorCode()));
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcSymlink.java
deleted file mode 100644
index a54432f..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/LibcSymlink.java
+++ /dev/null
@@ -1,40 +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.nativeplatform.filesystem;
-
-import com.sun.jna.LastErrorException;
-import org.gradle.internal.nativeplatform.jna.LibC;
-
-import java.io.File;
-import java.io.IOException;
-
-public class LibcSymlink implements Symlink {
-    private final LibC libC;
-
-    public LibcSymlink(LibC libC) {
-        this.libC = libC;
-    }
-
-    public void symlink(File link, File target) throws IOException {
-        link.getParentFile().mkdirs();
-        try {
-            libC.symlink(target.getPath(), link.getPath());
-        } catch (LastErrorException e) {
-            throw new IOException(String.format("Could not create symlink from '%s' to '%s'. Errno is %s.", link.getPath(), target.getPath(), e.getErrorCode()));
-        }
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedChmod.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedChmod.java
deleted file mode 100644
index 6c1cc31..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedChmod.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.nativeplatform.filesystem;
-
-import net.rubygrapefruit.platform.PosixFiles;
-
-import java.io.File;
-import java.io.IOException;
-
-class NativePlatformBackedChmod implements Chmod {
-    private final PosixFiles posixFiles;
-
-    public NativePlatformBackedChmod(PosixFiles posixFiles) {
-        this.posixFiles = posixFiles;
-    }
-
-    public void chmod(File file, int mode) throws IOException {
-        posixFiles.setMode(file, mode);
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedStat.java
deleted file mode 100644
index 984ff4f..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedStat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.nativeplatform.filesystem;
-
-import net.rubygrapefruit.platform.PosixFiles;
-
-import java.io.File;
-import java.io.IOException;
-
-class NativePlatformBackedStat implements Stat {
-    private final PosixFiles posixFiles;
-
-    public NativePlatformBackedStat(PosixFiles posixFiles) {
-        this.posixFiles = posixFiles;
-    }
-
-    public int getUnixMode(File f) throws IOException {
-        return posixFiles.getMode(f);
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedSymlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedSymlink.java
deleted file mode 100644
index 5bae05e..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/NativePlatformBackedSymlink.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.nativeplatform.filesystem;
-
-import net.rubygrapefruit.platform.PosixFiles;
-
-import java.io.File;
-import java.io.IOException;
-
-class NativePlatformBackedSymlink implements Symlink {
-    private final PosixFiles posixFiles;
-
-    public NativePlatformBackedSymlink(PosixFiles posixFiles) {
-        this.posixFiles = posixFiles;
-    }
-
-    public void symlink(File link, File target) throws IOException {
-        link.getParentFile().mkdirs();
-        posixFiles.symlink(link, target.getPath());
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixStat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixStat.java
deleted file mode 100644
index a43221c..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixStat.java
+++ /dev/null
@@ -1,34 +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.nativeplatform.filesystem;
-
-import org.jruby.ext.posix.POSIX;
-
-import java.io.File;
-import java.io.IOException;
-
-class PosixStat implements Stat {
-    private final POSIX posix;
-
-    public PosixStat(POSIX posix) {
-        this.posix = posix;
-    }
-
-    public int getUnixMode(File f) throws IOException {
-        return this.posix.stat(f.getAbsolutePath()).mode() & 0777;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixUtil.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixUtil.java
deleted file mode 100644
index 7d236b3..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/PosixUtil.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.internal.nativeplatform.filesystem;
-
-import org.jruby.ext.posix.POSIX;
-import org.jruby.ext.posix.POSIXFactory;
-import org.jruby.ext.posix.POSIXHandler;
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.PrintStream;
-
-public class PosixUtil {
-    private static final POSIX POSIX = POSIXFactory.getPOSIX(new POSIXHandlerImpl(), true);
-
-    public static POSIX current() {
-        return POSIX;
-    }
-
-    private static class POSIXHandlerImpl implements POSIXHandler {
-        public void error(POSIX.ERRORS error, String message) {
-            throw new UnsupportedOperationException(error + " - " + message);
-        }
-
-        public void unimplementedError(String message) {
-            throw new UnsupportedOperationException(message);
-        }
-
-        public void warn(WARNING_ID warningId, String message, Object... objects) {
-        }
-
-        public boolean isVerbose() {
-            return false;
-        }
-
-        public File getCurrentWorkingDirectory() {
-            throw new UnsupportedOperationException();
-        }
-
-        public String[] getEnv() {
-            throw new UnsupportedOperationException();
-        }
-
-        public InputStream getInputStream() {
-            return System.in;
-        }
-
-        public PrintStream getOutputStream() {
-            return System.out;
-        }
-
-        public int getPID() {
-            throw new UnsupportedOperationException();
-        }
-
-        public PrintStream getErrorStream() {
-            return System.err;
-        }
-    }
-
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Stat.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Stat.java
deleted file mode 100644
index f201a2b..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Stat.java
+++ /dev/null
@@ -1,24 +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.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-public interface Stat {
-    public int getUnixMode(File f) throws IOException;
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Symlink.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Symlink.java
deleted file mode 100644
index f2925b2..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/Symlink.java
+++ /dev/null
@@ -1,24 +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.nativeplatform.filesystem;
-
-import java.io.File;
-import java.io.IOException;
-
-public interface Symlink {
-    void symlink(File link, File target) throws IOException;
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverter.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverter.java
deleted file mode 100644
index ba8c510..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverter.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.internal.nativeplatform.filesystem.jdk7;
-
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.EnumSet;
-import java.util.Set;
-
-import static java.nio.file.attribute.PosixFilePermission.*;
-
-public class PosixFilePermissionConverter {
-
-    static Set<PosixFilePermission> convertToPermissionsSet(int mode) {
-        Set<PosixFilePermission> result = EnumSet.noneOf(PosixFilePermission.class);
-
-        if (isSet(mode, 0400)) {
-            result.add(OWNER_READ);
-        }
-        if (isSet(mode, 0200)) {
-            result.add(OWNER_WRITE);
-        }
-        if (isSet(mode, 0100)) {
-            result.add(OWNER_EXECUTE);
-        }
-
-        if (isSet(mode, 040)) {
-            result.add(GROUP_READ);
-        }
-        if (isSet(mode, 020)) {
-            result.add(GROUP_WRITE);
-        }
-        if (isSet(mode, 010)) {
-            result.add(GROUP_EXECUTE);
-        }
-        if (isSet(mode, 04)) {
-            result.add(OTHERS_READ);
-        }
-        if (isSet(mode, 02)) {
-            result.add(OTHERS_WRITE);
-        }
-        if (isSet(mode, 01)) {
-            result.add(OTHERS_EXECUTE);
-        }
-        return result;
-    }
-
-    private static boolean isSet(int mode, int testbit) {
-        return (mode & testbit) == testbit;
-    }
-
-    public static int convertToInt(Set<PosixFilePermission> permissions) {
-        int result = 0;
-        if (permissions.contains(OWNER_READ)) {
-            result = result | 0400;
-        }
-        if (permissions.contains(OWNER_WRITE)) {
-            result = result | 0200;
-        }
-        if (permissions.contains(OWNER_EXECUTE)) {
-            result = result | 0100;
-        }
-        if (permissions.contains(GROUP_READ)) {
-            result = result | 040;
-        }
-        if (permissions.contains(GROUP_WRITE)) {
-            result = result | 020;
-        }
-        if (permissions.contains(GROUP_EXECUTE)) {
-            result = result | 010;
-        }
-        if (permissions.contains(OTHERS_READ)) {
-            result = result | 04;
-        }
-        if (permissions.contains(OTHERS_WRITE)) {
-            result = result | 02;
-        }
-        if (permissions.contains(OTHERS_EXECUTE)) {
-            result = result | 01;
-        }
-        return result;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandler.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandler.java
deleted file mode 100644
index c96de84..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandler.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.internal.nativeplatform.filesystem.jdk7;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.attribute.PosixFileAttributeView;
-import java.nio.file.attribute.PosixFileAttributes;
-
-import org.gradle.internal.nativeplatform.NativeIntegrationException;
-import org.gradle.internal.nativeplatform.filesystem.Chmod;
-import org.gradle.internal.nativeplatform.filesystem.Stat;
-
-import static org.gradle.internal.nativeplatform.filesystem.jdk7.PosixFilePermissionConverter.convertToInt;
-import static org.gradle.internal.nativeplatform.filesystem.jdk7.PosixFilePermissionConverter.convertToPermissionsSet;
-
-public class PosixJdk7FilePermissionHandler implements Stat, Chmod {
-
-    public int getUnixMode(File file) {
-        try {
-            final PosixFileAttributes posixFileAttributes = Files.readAttributes(file.toPath(), PosixFileAttributes.class);
-            return convertToInt(posixFileAttributes.permissions());
-        }catch (Exception e) {
-            throw new NativeIntegrationException(String.format("Failed to read File permissions for %s", file.getAbsolutePath()), e);
-        }
-    }
-
-    public void chmod(File f, int mode) throws IOException {
-        PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(f.toPath(), PosixFileAttributeView.class);
-        fileAttributeView.setPermissions(convertToPermissionsSet(mode));
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/JnaBootPathConfigurer.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/JnaBootPathConfigurer.java
deleted file mode 100644
index 75035b2..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/JnaBootPathConfigurer.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.internal.nativeplatform.jna;
-
-import org.apache.commons.io.IOUtils;
-import org.gradle.internal.nativeplatform.NativeIntegrationException;
-import org.gradle.internal.os.OperatingSystem;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class JnaBootPathConfigurer {
-    /**
-     * Attempts to find the jna library and copies it to a specified folder.
-     * The copy operation happens only once. Sets the jna-related system property.
-     *
-     * This hackery is to prevent JNA from creating a shared lib in the tmp dir, as it does not clean things up.
-     *
-     * @param storageDir - where to store the jna library
-     */
-    public void configure(File storageDir) {
-        String nativePrefix = OperatingSystem.current().getNativePrefix();
-        File tmpDir = new File(storageDir, String.format("jna/%s", nativePrefix));
-        tmpDir.mkdirs();
-        String jnaLibName = OperatingSystem.current().isMacOsX() ? "libjnidispatch.jnilib" : System.mapLibraryName("jnidispatch");
-        File libFile = new File(tmpDir, jnaLibName);
-        if (!libFile.exists()) {
-            String resourceName = "/com/sun/jna/" + nativePrefix + "/" + jnaLibName;
-            try {
-                InputStream lib = getClass().getResourceAsStream(resourceName);
-                if (lib == null) {
-                    return;
-                }
-                try {
-                    FileOutputStream outputStream = new FileOutputStream(libFile);
-                    try {
-                        IOUtils.copy(lib, outputStream);
-                    } finally {
-                        outputStream.close();
-                    }
-                } finally {
-                    lib.close();
-                }
-            } catch (IOException e) {
-                throw new NativeIntegrationException(String.format("Could not create JNA native library '%s'.", libFile), e);
-            }
-        }
-        System.setProperty("jna.boot.library.path", tmpDir.getAbsolutePath());
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibC.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibC.java
deleted file mode 100644
index bf3cd41..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibC.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.internal.nativeplatform.jna;
-
-import com.sun.jna.LastErrorException;
-import com.sun.jna.Library;
-import com.sun.jna.WString;
-import org.jruby.ext.posix.FileStat;
-
-public interface LibC extends Library {
-    //CHECKSTYLE:OFF
-    public int setenv(String name, String value, int overwrite) throws LastErrorException;
-    public String getcwd(byte[] out, int size) throws LastErrorException;
-    public int chdir(String dirAbsolutePath) throws LastErrorException;
-    public int getpid();
-    public int isatty(int fdes);
-    public int stat(byte[] filePath, FileStat fileStat) throws LastErrorException;
-    public int __xstat64(int version, byte[] filePath, FileStat fileStat) throws LastErrorException;
-    public int chmod(byte[] filePath, int mode) throws LastErrorException;
-    public int wcstombs(byte[] dest, WString source, int size) throws LastErrorException;
-    public int symlink(String target, String link) throws LastErrorException;
-    //CHECKSTYLE:ON
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedConsoleDetector.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedConsoleDetector.java
deleted file mode 100644
index 6f8499c..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedConsoleDetector.java
+++ /dev/null
@@ -1,64 +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.internal.nativeplatform.jna;
-
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.nativeplatform.console.ConsoleMetaData;
-import org.gradle.internal.nativeplatform.console.UnixConsoleMetaData;
-import org.gradle.internal.nativeplatform.console.ConsoleDetector;
-
-import java.io.FileDescriptor;
-import java.lang.reflect.Field;
-
-public class LibCBackedConsoleDetector implements ConsoleDetector {
-    private final LibC libC;
-
-    public LibCBackedConsoleDetector(LibC libC) {
-        this.libC = libC;
-    }
-
-    public ConsoleMetaData getConsole() {
-        boolean stdout = checkIsConsole(FileDescriptor.out);
-        boolean stderr = checkIsConsole(FileDescriptor.err);
-        if (!stdout && !stderr) {
-            return null;
-        }
-
-        // Dumb terminal doesn't support ANSI control codes. Should really be using termcap database.
-        String term = System.getenv("TERM");
-        if (term != null && term.equals("dumb")) {
-            return null;
-        }
-
-        // Assume a terminal
-        return new UnixConsoleMetaData(stdout, stderr);
-    }
-
-    private boolean checkIsConsole(FileDescriptor fileDescriptor) {
-        int osFileDesc;
-        try {
-            Field fdField = FileDescriptor.class.getDeclaredField("fd");
-            fdField.setAccessible(true);
-            osFileDesc = fdField.getInt(fileDescriptor);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-
-        // Determine if we're connected to a terminal
-        return libC.isatty(osFileDesc) != 0;
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironment.java
deleted file mode 100755
index 95e75a4..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironment.java
+++ /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.internal.nativeplatform.jna;
-
-import com.sun.jna.LastErrorException;
-import com.sun.jna.Native;
-import org.gradle.internal.nativeplatform.NativeIntegrationException;
-import org.gradle.internal.nativeplatform.processenvironment.AbstractProcessEnvironment;
-
-import java.io.File;
-
-/**
- * Uses JNA to drive the POSIX API provided by libc
- */
-public class LibCBackedProcessEnvironment extends AbstractProcessEnvironment {
-    private static final int LOTS_OF_CHARS = 2048;
-    private final LibC libc;
-
-    public LibCBackedProcessEnvironment(LibC libc) {
-        this.libc = libc;
-    }
-
-    public void setNativeEnvironmentVariable(String name, String value) {
-        try {
-            libc.setenv(name, value, 1);
-        } catch (LastErrorException lastErrorException) {
-            throw new NativeIntegrationException(String.format("Could not set environment variable '%s'. errno: %d", name, lastErrorException.getErrorCode()));
-        }
-    }
-
-    public void removeNativeEnvironmentVariable(String name) {
-        setNativeEnvironmentVariable(name, "");
-    }
-
-    public void setNativeProcessDir(File dir) {
-        try {
-            libc.chdir(dir.getAbsolutePath());
-        } catch (LastErrorException lastErrorException) {
-            throw new NativeIntegrationException(String.format("Could not set process working directory to '%s'. errno: %d", dir, lastErrorException.getErrorCode()));
-        }
-    }
-
-    public File getProcessDir() {
-        byte[] out = new byte[LOTS_OF_CHARS];
-        try {
-            libc.getcwd(out, LOTS_OF_CHARS);
-        } catch (LastErrorException lastErrorException) {
-            throw new NativeIntegrationException(String.format("Could not get process working directory. errno: %d", lastErrorException.getErrorCode()));
-        }
-        return new File(Native.toString(out));
-    }
-
-    public Long getPid() {
-        return Long.valueOf(libc.getpid());
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/UnsupportedEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/UnsupportedEnvironment.java
deleted file mode 100644
index f758f38..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/jna/UnsupportedEnvironment.java
+++ /dev/null
@@ -1,70 +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.internal.nativeplatform.jna;
-
-import org.gradle.internal.nativeplatform.NativeIntegrationException;
-import org.gradle.internal.nativeplatform.NativeIntegrationUnavailableException;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.os.OperatingSystem;
-
-import java.io.File;
-import java.util.Map;
-
-public class UnsupportedEnvironment implements ProcessEnvironment {
-    public boolean maybeSetEnvironment(Map<String, String> source) {
-        return false;
-    }
-
-    public void removeEnvironmentVariable(String name) throws NativeIntegrationException {
-        throw notSupported();
-    }
-
-    public boolean maybeRemoveEnvironmentVariable(String name) {
-        return false;
-    }
-
-    public void setEnvironmentVariable(String name, String value) throws NativeIntegrationException {
-        throw notSupported();
-    }
-
-    public boolean maybeSetEnvironmentVariable(String name, String value) {
-        return false;
-    }
-
-    public File getProcessDir() throws NativeIntegrationException {
-        throw notSupported();
-    }
-
-    public void setProcessDir(File processDir) throws NativeIntegrationException {
-        throw notSupported();
-    }
-
-    public boolean maybeSetProcessDir(File processDir) {
-        return false;
-    }
-
-    public Long getPid() throws NativeIntegrationException {
-        throw notSupported();
-    }
-
-    public Long maybeGetPid() {
-        return null;
-    }
-
-    private NativeIntegrationException notSupported() {
-        return new NativeIntegrationUnavailableException("We don't support this operating system: " + OperatingSystem.current());
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/AbstractProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/AbstractProcessEnvironment.java
deleted file mode 100755
index 078f1a6..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/AbstractProcessEnvironment.java
+++ /dev/null
@@ -1,92 +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.internal.nativeplatform.processenvironment;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.gradle.internal.nativeplatform.NativeIntegrationException;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.ReflectiveEnvironment;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-public abstract class AbstractProcessEnvironment implements ProcessEnvironment {
-    //for updates to private JDK caches of the environment state
-    private final ReflectiveEnvironment reflectiveEnvironment = new ReflectiveEnvironment();
-
-    public boolean maybeSetEnvironment(Map<String, String> source) {
-        // need to take copy to prevent ConcurrentModificationException
-        List<String> keysToRemove = Lists.newArrayList(Sets.difference(System.getenv().keySet(), source.keySet()));
-        for (String key : keysToRemove) {
-            removeEnvironmentVariable(key);
-        }
-        for (Map.Entry<String, String> entry : source.entrySet()) {
-            setEnvironmentVariable(entry.getKey(), entry.getValue());
-        }
-        return true;
-    }
-
-    public void removeEnvironmentVariable(String name) throws NativeIntegrationException {
-        removeNativeEnvironmentVariable(name);
-        reflectiveEnvironment.unsetenv(name);
-    }
-
-    protected abstract void removeNativeEnvironmentVariable(String name);
-
-    public boolean maybeRemoveEnvironmentVariable(String name) {
-        removeEnvironmentVariable(name);
-        return true;
-    }
-
-    public void setEnvironmentVariable(String name, String value) throws NativeIntegrationException {
-        if (value == null) {
-            removeEnvironmentVariable(name);
-            return;
-        }
-
-        setNativeEnvironmentVariable(name, value);
-        reflectiveEnvironment.setenv(name, value);
-    }
-
-    protected abstract void setNativeEnvironmentVariable(String name, String value);
-
-    public boolean maybeSetEnvironmentVariable(String name, String value) {
-        setEnvironmentVariable(name, value);
-        return true;
-    }
-
-    public void setProcessDir(File processDir) throws NativeIntegrationException {
-        if (!processDir.exists()) {
-            return;
-        }
-
-        setNativeProcessDir(processDir);
-        System.setProperty("user.dir", processDir.getAbsolutePath());
-    }
-
-    protected abstract void setNativeProcessDir(File processDir);
-
-    public boolean maybeSetProcessDir(File processDir) {
-        setProcessDir(processDir);
-        return true;
-    }
-
-    public Long maybeGetPid() {
-        return getPid();
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/NativePlatformBackedProcessEnvironment.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/NativePlatformBackedProcessEnvironment.java
deleted file mode 100755
index 4215dc5..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/processenvironment/NativePlatformBackedProcessEnvironment.java
+++ /dev/null
@@ -1,51 +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.internal.nativeplatform.processenvironment;
-
-import net.rubygrapefruit.platform.Process;
-
-import java.io.File;
-
-public class NativePlatformBackedProcessEnvironment extends AbstractProcessEnvironment {
-    private final Process process;
-
-    public NativePlatformBackedProcessEnvironment(Process process) {
-        this.process = process;
-    }
-
-    @Override
-    protected void removeNativeEnvironmentVariable(String name) {
-        process.setEnvironmentVariable(name, null);
-    }
-
-    @Override
-    protected void setNativeEnvironmentVariable(String name, String value) {
-        process.setEnvironmentVariable(name, value);
-    }
-
-    @Override
-    protected void setNativeProcessDir(File processDir) {
-        process.setWorkingDirectory(processDir);
-    }
-
-    public File getProcessDir() {
-        return process.getWorkingDirectory();
-    }
-
-    public Long getPid() {
-        return Long.valueOf(process.getProcessId());
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/FileSystems.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/FileSystems.java
deleted file mode 100644
index 984e46a..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/FileSystems.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.internal.nativeplatform.services;
-
-import org.gradle.internal.nativeplatform.filesystem.FileSystem;
-
-public abstract class FileSystems {
-    public static FileSystem getDefault() {
-        return NativeServices.getInstance().get(FileSystem.class);
-    }
-}
diff --git a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/NativeServices.java b/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/NativeServices.java
deleted file mode 100755
index cefc2cd..0000000
--- a/subprojects/native/src/main/java/org/gradle/internal/nativeplatform/services/NativeServices.java
+++ /dev/null
@@ -1,197 +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.internal.nativeplatform.services;
-
-import com.sun.jna.Native;
-import net.rubygrapefruit.platform.*;
-import net.rubygrapefruit.platform.Process;
-import net.rubygrapefruit.platform.internal.DefaultProcessLauncher;
-import org.gradle.internal.SystemProperties;
-import org.gradle.internal.jvm.Jvm;
-import org.gradle.internal.nativeplatform.ProcessEnvironment;
-import org.gradle.internal.nativeplatform.console.ConsoleDetector;
-import org.gradle.internal.nativeplatform.console.NativePlatformConsoleDetector;
-import org.gradle.internal.nativeplatform.console.NoOpConsoleDetector;
-import org.gradle.internal.nativeplatform.console.WindowsConsoleDetector;
-import org.gradle.internal.nativeplatform.filesystem.FileSystemServices;
-import org.gradle.internal.nativeplatform.jna.*;
-import org.gradle.internal.nativeplatform.processenvironment.NativePlatformBackedProcessEnvironment;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.internal.service.DefaultServiceRegistry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-/**
- * Provides various native platform integration services.
- */
-public class NativeServices extends DefaultServiceRegistry {
-    private static final Logger LOGGER = LoggerFactory.getLogger(NativeServices.class);
-    private static boolean useNativePlatform = "true".equalsIgnoreCase(System.getProperty("org.gradle.native", "true"));
-    private static final NativeServices INSTANCE = new NativeServices();
-
-    /**
-     * Initializes the native services to use the given user home directory to store native libs and other resources. Does nothing if already initialized. Will be implicitly initialized on first usage
-     * of a native service. Also initializes the Native-Platform library using the given user home directory.
-     */
-    public static void initialize(File userHomeDir) {
-        File nativeDir = new File(userHomeDir, "native");
-        if (useNativePlatform) {
-            try {
-                net.rubygrapefruit.platform.Native.init(nativeDir);
-            } catch (NativeIntegrationUnavailableException ex) {
-                LOGGER.debug("Native-platform is not available.");
-                useNativePlatform = false;
-            } catch (NativeException ex) {
-                LOGGER.debug("Unable to initialize native-platform. Failure: {}", format(ex));
-                useNativePlatform = false;
-            }
-        }
-        new JnaBootPathConfigurer().configure(nativeDir);
-    }
-
-    public static NativeServices getInstance() {
-        return INSTANCE;
-    }
-
-    private NativeServices() {
-        addProvider(new FileSystemServices());
-    }
-
-    @Override
-    public void close() {
-        // Don't close
-    }
-
-    protected OperatingSystem createOperatingSystem() {
-        return OperatingSystem.current();
-    }
-
-    protected Jvm createJvm() {
-        return Jvm.current();
-    }
-
-    protected ProcessEnvironment createProcessEnvironment(OperatingSystem operatingSystem) {
-        if (useNativePlatform) {
-            try {
-                net.rubygrapefruit.platform.Process process = net.rubygrapefruit.platform.Native.get(Process.class);
-                return new NativePlatformBackedProcessEnvironment(process);
-            } catch (NativeIntegrationUnavailableException ex) {
-                LOGGER.debug("Native-platform process integration is not available. Continuing with fallback.");
-            }
-        }
-
-        try {
-            if (operatingSystem.isUnix()) {
-                return new LibCBackedProcessEnvironment(get(LibC.class));
-            } else {
-                return new UnsupportedEnvironment();
-            }
-        } catch (LinkageError e) {
-            // Thrown when jna cannot initialize the native stuff
-            LOGGER.debug("Unable to load native library. Continuing with fallback. Failure: {}", format(e));
-            return new UnsupportedEnvironment();
-        }
-    }
-
-    protected ConsoleDetector createConsoleDetector(OperatingSystem operatingSystem) {
-        if (useNativePlatform) {
-            try {
-                Terminals terminals = net.rubygrapefruit.platform.Native.get(Terminals.class);
-                return new NativePlatformConsoleDetector(terminals);
-            } catch (NativeIntegrationUnavailableException ex) {
-                LOGGER.debug("Native-platform terminal integration is not available. Continuing with fallback.");
-            } catch (NativeException ex) {
-                LOGGER.debug("Unable to load from native-platform backed ConsoleDetector. Continuing with fallback. Failure: {}", format(ex));
-            }
-        }
-
-        try {
-            if (operatingSystem.isWindows()) {
-                return new WindowsConsoleDetector();
-            }
-            return new LibCBackedConsoleDetector(get(LibC.class));
-        } catch (LinkageError e) {
-            // Thrown when jna cannot initialize the native stuff
-            LOGGER.debug("Unable to load native library. Continuing with fallback. Failure: {}", format(e));
-            return new NoOpConsoleDetector();
-        }
-    }
-
-    protected WindowsRegistry createWindowsRegistry(OperatingSystem operatingSystem) {
-        if (useNativePlatform && operatingSystem.isWindows()) {
-            return net.rubygrapefruit.platform.Native.get(WindowsRegistry.class);
-        }
-        return notAvailable(WindowsRegistry.class);
-    }
-
-    protected SystemInfo createSystemInfo() {
-        if (useNativePlatform) {
-            try {
-                return net.rubygrapefruit.platform.Native.get(SystemInfo.class);
-            } catch (NativeIntegrationUnavailableException e) {
-                LOGGER.debug("Native-platform system info is not available. Continuing with fallback.");
-            }
-        }
-        return notAvailable(SystemInfo.class);
-    }
-
-    protected ProcessLauncher createProcessLauncher() {
-        if (useNativePlatform) {
-            try {
-                return net.rubygrapefruit.platform.Native.get(ProcessLauncher.class);
-            } catch (NativeIntegrationUnavailableException e) {
-                LOGGER.debug("Native-platform process launcher is not available. Continuing with fallback.");
-            }
-        }
-        return new DefaultProcessLauncher();
-    }
-
-    private <T> T notAvailable(Class<T> type) {
-        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new BrokenService(type.getSimpleName()));
-    }
-
-    protected LibC createLibC() {
-        return (LibC) Native.loadLibrary("c", LibC.class);
-    }
-
-    private static String format(Throwable throwable) {
-        StringBuilder builder = new StringBuilder();
-        builder.append(throwable.toString());
-        for (Throwable current = throwable.getCause(); current != null; current = current.getCause()) {
-            builder.append(SystemProperties.getLineSeparator());
-            builder.append("caused by: ");
-            builder.append(current.toString());
-        }
-        return builder.toString();
-    }
-
-    private static class BrokenService implements InvocationHandler {
-        private final String type;
-
-        private BrokenService(String type) {
-            this.type = type;
-        }
-
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-            return new org.gradle.internal.nativeplatform.NativeIntegrationUnavailableException(String.format("%s is not supported on this operating system.", type));
-        }
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy
new file mode 100644
index 0000000..fea96d6
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy
@@ -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.internal.nativeintegration.console
+
+import spock.lang.Specification
+import net.rubygrapefruit.platform.Terminals
+
+class NativePlatformConsoleDetectorTest extends Specification {
+    private Terminals terminals = Mock()
+    private NativePlatformConsoleDetector detector = new NativePlatformConsoleDetector(terminals)
+
+    def "returns null when neither stdout or stderr is attached to console"() {
+        given:
+        terminals.isTerminal(Terminals.Output.Stdout) >> false
+        terminals.isTerminal(Terminals.Output.Stderr) >> false
+
+        expect:
+        detector.console == null
+    }
+
+    def "returns metadata when stdout and stderr are attached to console"() {
+        given:
+        terminals.isTerminal(Terminals.Output.Stdout) >> true
+        terminals.isTerminal(Terminals.Output.Stderr) >> true
+
+        expect:
+        detector.console != null
+        detector.console.stdOut
+        detector.console.stdErr
+    }
+
+    def "returns metadata when only stdout is attached to console"() {
+        given:
+        terminals.isTerminal(Terminals.Output.Stdout) >> true
+        terminals.isTerminal(Terminals.Output.Stderr) >> false
+
+        expect:
+        detector.console != null
+        detector.console.stdOut
+        !detector.console.stdErr
+    }
+
+    def "returns metadata when only stderr is attached to console"() {
+        given:
+        terminals.isTerminal(Terminals.Output.Stdout) >> false
+        terminals.isTerminal(Terminals.Output.Stderr) >> true
+
+        expect:
+        detector.console != null
+        !detector.console.stdOut
+        detector.console.stdErr
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy
new file mode 100644
index 0000000..1d38ce7
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration.filesystem
+
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.Specification
+
+class CommonFileSystemTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir
+
+    def fs = NativeServicesTestFixture.instance.get(FileSystem)
+
+    def "unix permissions cannot be read on non existing file"() {
+        def file = tmpDir.file("someFile")
+
+        when:
+        fs.getUnixMode(file)
+
+        then:
+        FileException e = thrown()
+        e.message == "Could not get file mode for '$file'."
+    }
+
+    def "unix permissions cannot be set on non existing file"() {
+        def file = tmpDir.file("someFile")
+
+        when:
+        fs.chmod(file, 0644)
+
+        then:
+        FileException e = thrown()
+        e.message == "Could not set file mode 644 on '$file'."
+    }
+
+    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    def "unix permissions on files can be changed and read"() {
+        def f = tmpDir.createFile("someFile\u03B1.txt")
+
+        when:
+        fs.chmod(f, mode)
+
+        then:
+        fs.getUnixMode(f) == mode
+        f.mode == mode
+
+        where:
+        mode << [0644, 0600, 0751]
+    }
+
+    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    def "unix permissions on directories can be changed and read"() {
+        def d = tmpDir.createDir("someDir\u03B1")
+
+        when:
+        fs.chmod(d, mode)
+
+        then:
+        fs.getUnixMode(d) == mode
+        d.mode == mode
+
+        where:
+        mode << [0755, 0700, 0722]
+    }
+
+    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
+    def "unix permissions have default values on unsupported platforms"() {
+        expect:
+        fs.getUnixMode(tmpDir.createFile("someFile")) == FileSystem.DEFAULT_FILE_MODE
+        fs.getUnixMode(tmpDir.createDir("someDir")) == FileSystem.DEFAULT_DIR_MODE
+    }
+
+    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
+    def "setting unix permissions does nothing on unsupported platforms"() {
+        expect:
+        fs.chmod(tmpDir.createFile("someFile"), 0644)
+    }
+
+    @Requires(TestPrecondition.SYMLINKS)
+    def "can create symlink on platforms that support symlinks"() {
+        def target = tmpDir.createFile("target.txt")
+        def link = tmpDir.file("link.txt")
+
+        when:
+        fs.createSymbolicLink(link, target)
+
+        then:
+        link.exists()
+        link.readLink() == target.absolutePath
+    }
+
+    @Requires(TestPrecondition.NO_SYMLINKS)
+    def "cannot create symlinks on platforms that do not support symlinks"() {
+        def target = tmpDir.createFile("target.txt")
+        def link = tmpDir.file("link.txt")
+
+        when:
+        fs.createSymbolicLink(link, target)
+
+        then:
+        thrown(FileException)
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy
new file mode 100644
index 0000000..19200dd
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.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.internal.nativeintegration.filesystem
+
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Specification
+
+ at Requires(TestPrecondition.LINUX)
+class LinuxFileSystemTest extends Specification {
+    def fs = NativeServicesTestFixture.instance.get(FileSystem)
+
+    def "is case sensitive"() {
+        expect:
+        fs.caseSensitive
+    }
+
+    def "can create symbolic link"() {
+        expect:
+        fs.canCreateSymbolicLink()
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy
new file mode 100644
index 0000000..7ab7f0e
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.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.internal.nativeintegration.filesystem
+
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Specification
+
+ at Requires(TestPrecondition.MAC_OS_X)
+class MacOsFileSystemTest extends Specification {
+    def fs = NativeServicesTestFixture.instance.get(FileSystem)
+
+    def "is not case sensitive"() {
+        expect:
+        !fs.caseSensitive
+    }
+
+    def "can create symbolic link"() {
+        expect:
+        fs.canCreateSymbolicLink()
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy
new file mode 100644
index 0000000..85ab7aa
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.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.internal.nativeintegration.filesystem
+
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Specification
+
+ at Requires(TestPrecondition.WINDOWS)
+class WindowsFileSystemTest extends Specification {
+    def fs = NativeServicesTestFixture.instance.get(FileSystem)
+
+    def "windows file system is case insensitive"() {
+        expect:
+        !fs.caseSensitive
+    }
+
+    def "windows file system cannot create symbolic link"() {
+        expect:
+        !fs.canCreateSymbolicLink()
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverterTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverterTest.groovy
new file mode 100644
index 0000000..c577cb4
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixFilePermissionConverterTest.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.internal.nativeintegration.filesystem.jdk7
+
+import java.nio.file.attribute.PosixFilePermission
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Specification
+import static java.nio.file.attribute.PosixFilePermission.*
+
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+class PosixFilePermissionConverterTest extends Specification {
+    def "converts Set<PosixFilePermission to int representation"() {
+
+        expect:
+        PosixFilePermissionConverter.convertToInt(perms) == intValue
+
+        where:
+        perms                                                                |       intValue
+        EnumSet.noneOf(PosixFilePermission)                                  |       0
+        EnumSet.allOf(PosixFilePermission)                                   |       0777
+        EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)                   |       0700
+        EnumSet.of(OWNER_READ, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE)       |       0470
+        EnumSet.of(OWNER_READ, GROUP_READ, OTHERS_READ)                      |       0444
+        EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ)         |       0644
+        EnumSet.of(OWNER_READ, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE)       |       0560
+    }
+
+
+    def "converts int representation to Set<PosixFilePermission)String representation"() {
+        expect:
+        perms == PosixFilePermissionConverter.convertToPermissionsSet(intValue)
+
+        where:
+        perms                                                                |       intValue
+        EnumSet.noneOf(PosixFilePermission)                                  |       0
+        EnumSet.allOf(PosixFilePermission)                                   |       0777
+        EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)                   |       0700
+        EnumSet.of(OWNER_READ, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE)       |       0470
+        EnumSet.of(OWNER_READ, GROUP_READ, OTHERS_READ)                      |       0444
+        EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ)         |       0644
+        EnumSet.of(OWNER_READ, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE)       |       0560
+
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
new file mode 100644
index 0000000..030f12d
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
@@ -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.internal.nativeintegration.filesystem.jdk7
+
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.Specification
+
+ at Requires(TestPrecondition.NOT_WINDOWS)
+class PosixJdk7FilePermissionHandlerTest extends Specification {
+    @Rule TestNameTestDirectoryProvider temporaryFolder
+
+    def "test chmod on non windows platforms with JDK7"() {
+        setup:
+        def file = temporaryFolder.createFile("testFile")
+        def handler = new PosixJdk7FilePermissionHandler()
+        when:
+        handler.chmod(file, mode);
+        then:
+        mode == handler.getUnixMode(file);
+        where:
+        mode << [0722, 0644, 0744, 0755]
+    }
+
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystemTest.groovy
new file mode 100644
index 0000000..ff0174f
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/GenericFileSystemTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services
+import org.gradle.internal.nativeintegration.filesystem.FileException
+import org.gradle.internal.nativeintegration.filesystem.FileModeAccessor
+import org.gradle.internal.nativeintegration.filesystem.FileModeMutator
+import org.gradle.internal.nativeintegration.filesystem.Symlink
+import spock.lang.Specification
+
+class GenericFileSystemTest extends Specification {
+    def fileModeMutator = Stub(FileModeMutator)
+    def fileModeAccessor = Stub(FileModeAccessor)
+    def symlink = Stub(Symlink)
+    def fileSystem = new GenericFileSystem(fileModeMutator, fileModeAccessor, symlink)
+
+    def "wraps failure to set file mode"() {
+        def failure = new RuntimeException()
+        def file = new File("does-not-exist")
+
+        given:
+        fileModeMutator.chmod(_, _) >> { throw failure }
+
+        when:
+        fileSystem.chmod(file, 0640)
+
+        then:
+        FileException e = thrown()
+        e.message == "Could not set file mode 640 on '$file'."
+    }
+
+    def "wraps failure to get file mode"() {
+        def failure = new RuntimeException()
+        def file = new File("does-not-exist")
+
+        given:
+        fileModeAccessor.getUnixMode(_) >> { throw failure }
+
+        when:
+        fileSystem.getUnixMode(file)
+
+        then:
+        FileException e = thrown()
+        e.message == "Could not get file mode for '$file'."
+    }
+
+    def "wraps failure to get create symlink"() {
+        def failure = new RuntimeException()
+        def file = new File("does-not-exist")
+        def target = new File("target")
+
+        given:
+        symlink.symlink(_, _) >> { throw failure }
+
+        when:
+        fileSystem.createSymbolicLink(file, target)
+
+        then:
+        FileException e = thrown()
+        e.message == "Could not create symlink from '$file' to '$target'."
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissionsTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissionsTest.groovy
new file mode 100644
index 0000000..5b90804
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/UnsupportedFilePermissionsTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.filesystem.services
+
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestOutputEventListener
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class UnsupportedFilePermissionsTest extends Specification {
+    def outputEventListener = new TestOutputEventListener()
+    @Rule ConfigureLogging logging = new ConfigureLogging(outputEventListener)
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def permissions = new UnsupportedFilePermissions()
+
+    def "warns on first attempt to stat a file"() {
+        when:
+        permissions.getUnixMode(tmpDir.createFile("file"))
+        permissions.getUnixMode(tmpDir.createDir("dir"))
+
+        then:
+        outputEventListener.toString() == '[WARN Support for reading or changing file permissions is only available on this platform using Java 7 or later.]'
+    }
+
+    def "warns on first attempt to chmod a file"() {
+        when:
+        permissions.chmod(tmpDir.createFile("file"), 0644)
+        permissions.chmod(tmpDir.createDir("dir"), 0644)
+
+        then:
+        outputEventListener.toString() == '[WARN Support for reading or changing file permissions is only available on this platform using Java 7 or later.]'
+    }
+
+    def "warns at most once"() {
+        when:
+        permissions.chmod(tmpDir.createFile("file"), 0644)
+        permissions.getUnixMode(tmpDir.createDir("dir"))
+        permissions.getUnixMode(tmpDir.createFile("file"))
+
+        then:
+        outputEventListener.toString() == '[WARN Support for reading or changing file permissions is only available on this platform using Java 7 or later.]'
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy
new file mode 100755
index 0000000..39db35a
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.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.internal.nativeintegration.processenvironment
+
+import org.gradle.internal.nativeintegration.ProcessEnvironment
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.SetSystemProperties
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+import spock.lang.Specification
+
+class ProcessEnvironmentTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule final SetSystemProperties systemProperties = new SetSystemProperties()
+    final ProcessEnvironment env = NativeServicesTestFixture.getInstance().get(ProcessEnvironment)
+
+    @Requires(TestPrecondition.SET_ENV_VARIABLE)
+    def "can set and remove environment variable"() {
+        when:
+        env.setEnvironmentVariable("TEST_ENV_1", "value")
+        env.maybeSetEnvironmentVariable("TEST_ENV_2", "value")
+
+        then:
+        System.getenv("TEST_ENV_1") == "value"
+        System.getenv("TEST_ENV_2") == "value"
+
+        when:
+        env.removeEnvironmentVariable("TEST_ENV_1")
+        env.maybeRemoveEnvironmentVariable("TEST_ENV_2")
+
+        then:
+        System.getenv("TEST_ENV_1") == null
+        System.getenv("TEST_ENV_2") == null
+    }
+
+    @Requires(TestPrecondition.WORKING_DIR)
+    def "can get working directory of current process"() {
+        expect:
+        env.processDir.canonicalFile == new File('.').canonicalFile
+    }
+
+    @Requires(TestPrecondition.WORKING_DIR)
+    def "can get set working directory of current process"() {
+        File originalDir = new File(System.getProperty("user.dir"))
+
+        when:
+        env.setProcessDir(tmpDir.testDirectory)
+
+        then:
+        env.processDir.canonicalFile == tmpDir.testDirectory
+        new File(".").canonicalFile == tmpDir.testDirectory
+
+        cleanup:
+        System.setProperty("user.dir", originalDir.absolutePath)
+        env.setProcessDir(originalDir)
+    }
+
+    @Requires(TestPrecondition.PROCESS_ID)
+    def "can get pid of current process"() {
+        expect:
+        env.pid != null
+        env.maybeGetPid() == env.pid
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesInitializationTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesInitializationTest.groovy
new file mode 100644
index 0000000..6f16bd0
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesInitializationTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.nativeintegration.services
+
+import org.gradle.internal.reflect.JavaMethod
+import org.gradle.internal.reflect.JavaReflectionUtil
+import spock.lang.Specification
+
+import java.lang.reflect.Constructor
+
+class NativeServicesInitializationTest extends Specification {
+    def "cannot get an instance of NativeServices without initializing first" () {
+        // Construct an isolated classloader so we can load a pristine NativeServices class
+        // that's guaranteed not to have been initialized before
+        ClassLoader classLoader = new URLClassLoader(((URLClassLoader) getClass().getClassLoader()).getURLs(), (ClassLoader)null)
+        Class nativeServicesClass = classLoader.loadClass("org.gradle.internal.nativeintegration.services.NativeServices")
+        JavaMethod nativeServicesGetInstance = JavaReflectionUtil.staticMethod(nativeServicesClass, nativeServicesClass, "getInstance")
+
+        when:
+        nativeServicesGetInstance.invokeStatic()
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == "Cannot get an instance of NativeServices without first calling initialize()."
+    }
+
+    def "no public constructors on NativeServices" () {
+        Constructor[] constructors = NativeServices.getConstructors()
+
+        expect:
+        constructors.size() == 0
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesTest.groovy
new file mode 100755
index 0000000..6f86233
--- /dev/null
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/services/NativeServicesTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.nativeintegration.services
+
+import net.rubygrapefruit.platform.ProcessLauncher
+import net.rubygrapefruit.platform.SystemInfo
+import net.rubygrapefruit.platform.WindowsRegistry
+import org.gradle.internal.nativeintegration.ProcessEnvironment
+import org.gradle.internal.nativeintegration.console.ConsoleDetector
+import org.gradle.internal.nativeintegration.filesystem.Chmod
+import org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
+import org.gradle.internal.nativeintegration.filesystem.Stat
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.util.UsesNativeServices
+import spock.lang.Specification
+
+ at UsesNativeServices
+class NativeServicesTest extends Specification {
+    NativeServices services
+
+    def setup() {
+        services = NativeServices.getInstance()
+    }
+
+    def "makes a ProcessEnvironment available"() {
+        expect:
+        services.get(ProcessEnvironment) != null
+    }
+
+    def "makes an OperatingSystem available"() {
+        expect:
+        services.get(OperatingSystem) != null
+    }
+
+    def "makes a FileSystem available"() {
+        expect:
+        services.get(FileSystem) != null
+        services.get(Chmod) != null
+        services.get(Stat) != null
+    }
+
+    def "makes a FileCanonicalizer available"() {
+        expect:
+        services.get(FileCanonicalizer) != null
+    }
+
+    def "makes a ConsoleDetector available"() {
+        expect:
+        services.get(ConsoleDetector) != null
+    }
+
+    def "makes a WindowsRegistry available"() {
+        expect:
+        services.get(WindowsRegistry) != null
+    }
+
+    def "makes a SystemInfo available"() {
+        expect:
+        services.get(SystemInfo) != null
+    }
+
+    def "makes a ProcessLauncher available"() {
+        expect:
+        services.get(ProcessLauncher) != null
+    }
+}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetectorTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetectorTest.groovy
deleted file mode 100644
index d21b6a6..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/console/NativePlatformConsoleDetectorTest.groovy
+++ /dev/null
@@ -1,67 +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.nativeplatform.console
-
-import spock.lang.Specification
-import net.rubygrapefruit.platform.Terminals
-
-class NativePlatformConsoleDetectorTest extends Specification {
-    private Terminals terminals = Mock()
-    private NativePlatformConsoleDetector detector = new NativePlatformConsoleDetector(terminals)
-
-    def "returns null when neither stdout or stderr is attached to console"() {
-        given:
-        terminals.isTerminal(Terminals.Output.Stdout) >> false
-        terminals.isTerminal(Terminals.Output.Stderr) >> false
-
-        expect:
-        detector.console == null
-    }
-
-    def "returns metadata when stdout and stderr are attached to console"() {
-        given:
-        terminals.isTerminal(Terminals.Output.Stdout) >> true
-        terminals.isTerminal(Terminals.Output.Stderr) >> true
-
-        expect:
-        detector.console != null
-        detector.console.stdOut
-        detector.console.stdErr
-    }
-
-    def "returns metadata when only stdout is attached to console"() {
-        given:
-        terminals.isTerminal(Terminals.Output.Stdout) >> true
-        terminals.isTerminal(Terminals.Output.Stderr) >> false
-
-        expect:
-        detector.console != null
-        detector.console.stdOut
-        !detector.console.stdErr
-    }
-
-    def "returns metadata when only stderr is attached to console"() {
-        given:
-        terminals.isTerminal(Terminals.Output.Stdout) >> false
-        terminals.isTerminal(Terminals.Output.Stderr) >> true
-
-        expect:
-        detector.console != null
-        !detector.console.stdOut
-        detector.console.stdErr
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/CommonFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/CommonFileSystemTest.groovy
deleted file mode 100644
index 1b755b8..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/CommonFileSystemTest.groovy
+++ /dev/null
@@ -1,113 +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.internal.nativeplatform.filesystem
-
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-import spock.lang.Specification
-
-class CommonFileSystemTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir
-
-    def fs = NativeServices.instance.get(FileSystem)
-
-    def "unix permissions cannot be read on non existing file"() {
-        when:
-        fs.getUnixMode(tmpDir.file("someFile"))
-
-        then:
-        thrown(FileNotFoundException)
-    }
-
-    def "unix permissions cannot be set on non existing file"() {
-        when:
-        fs.chmod(tmpDir.file("someFile"), 0644)
-
-        then:
-        thrown(FileNotFoundException)
-    }
-
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
-    def "unix permissions on files can be changed and read"() {
-        def f = tmpDir.createFile("someFile\u03B1.txt")
-
-        when:
-        fs.chmod(f, mode)
-
-        then:
-        fs.getUnixMode(f) == mode
-        f.mode == mode
-
-        where:
-        mode << [0644, 0600, 0751]
-    }
-
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
-    def "unix permissions on directories can be changed and read"() {
-        def d = tmpDir.createDir("someDir\u03B1")
-
-        when:
-        fs.chmod(d, mode)
-
-        then:
-        fs.getUnixMode(d) == mode
-        d.mode == mode
-
-        where:
-        mode << [0755, 0700, 0722]
-    }
-
-    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
-    def "unix permissions have default values on unsupported platforms"() {
-        expect:
-        fs.getUnixMode(tmpDir.createFile("someFile")) == FileSystem.DEFAULT_FILE_MODE
-        fs.getUnixMode(tmpDir.createDir("someDir")) == FileSystem.DEFAULT_DIR_MODE
-    }
-
-    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
-    def "setting unix permissions does nothing on unsupported platforms"() {
-        expect:
-        fs.chmod(tmpDir.createFile("someFile"), 0644)
-    }
-
-    @Requires(TestPrecondition.SYMLINKS)
-    def "can create symlink on platforms that support symlinks"() {
-        def target = tmpDir.createFile("target.txt")
-        def link = tmpDir.file("link.txt")
-
-        when:
-        fs.createSymbolicLink(link, target)
-
-        then:
-        link.exists()
-        link.readLink() == target.absolutePath
-    }
-
-    @Requires(TestPrecondition.NO_SYMLINKS)
-    def "cannot create symlinks on platforms that do not support symlinks"() {
-        def target = tmpDir.createFile("target.txt")
-        def link = tmpDir.file("link.txt")
-
-        when:
-        fs.createSymbolicLink(link, target)
-
-        then:
-        thrown(IOException)
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LibcStatTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LibcStatTest.groovy
deleted file mode 100644
index 6c1437c..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LibcStatTest.groovy
+++ /dev/null
@@ -1,51 +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.nativeplatform.filesystem;
-
-import spock.lang.Specification
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.internal.nativeplatform.jna.LibC
-import org.jruby.ext.posix.BaseNativePOSIX
-import spock.lang.Unroll
-import org.jruby.ext.posix.FileStat;
-
-class LibcStatTest extends Specification {
-
-    OperatingSystem os = Mock();
-    LibC libc = Mock()
-    BaseNativePOSIX posix = Mock()
-    FilePathEncoder encoder = Mock()
-
-    @Unroll
-    def "LibCStat on #osname maps to libc #libcMethodName"(){
-        given:
-        1 * os.isMacOsX() >> (osname == "macosx");
-        1 * posix.allocateStat() >> Mock(FileStat)
-        File testFile = new File("a/file/path")
-        LibCStat libCStat = new LibCStat(libc, os, posix, encoder);
-        
-        when:
-        libCStat.getUnixMode(testFile)
-
-        then:
-        1 * libc."${libcMethodName}"(*_);
-        where:
-        osname   | libcMethodName
-        "macosx" | "stat"
-        "linux"  | "__xstat64"
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LinuxFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LinuxFileSystemTest.groovy
deleted file mode 100644
index f11bebe..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/LinuxFileSystemTest.groovy
+++ /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.internal.nativeplatform.filesystem
-
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Specification
-
- at Requires(TestPrecondition.LINUX)
-class LinuxFileSystemTest extends Specification {
-    def fs = NativeServices.instance.get(FileSystem)
-
-    def "is case sensitive"() {
-        expect:
-        fs.caseSensitive
-    }
-
-    def "can create symbolic link"() {
-        expect:
-        fs.canCreateSymbolicLink()
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/MacOsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/MacOsFileSystemTest.groovy
deleted file mode 100644
index 9c3c73a..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/MacOsFileSystemTest.groovy
+++ /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.internal.nativeplatform.filesystem
-
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Specification
-
- at Requires(TestPrecondition.MAC_OS_X)
-class MacOsFileSystemTest extends Specification {
-    def fs = NativeServices.instance.get(FileSystem)
-
-    def "is not case sensitive"() {
-        expect:
-        !fs.caseSensitive
-    }
-
-    def "can create symbolic link"() {
-        expect:
-        fs.canCreateSymbolicLink()
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/WindowsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/WindowsFileSystemTest.groovy
deleted file mode 100644
index df16c86..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/WindowsFileSystemTest.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.nativeplatform.filesystem
-
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Specification
-
- at Requires(TestPrecondition.WINDOWS)
-class WindowsFileSystemTest extends Specification {
-    def fs = NativeServices.instance.get(FileSystem)
-
-    def "windows file system is case insensitive"() {
-        expect:
-        !fs.caseSensitive
-    }
-
-    def "windows file system cannot create symbolic link"() {
-        expect:
-        !fs.canCreateSymbolicLink()
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverterTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverterTest.groovy
deleted file mode 100644
index 5cc771e..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixFilePermissionConverterTest.groovy
+++ /dev/null
@@ -1,59 +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.nativeplatform.filesystem.jdk7
-
-import java.nio.file.attribute.PosixFilePermission
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Specification
-import static java.nio.file.attribute.PosixFilePermission.*
-
- at Requires(TestPrecondition.JDK7_OR_LATER)
-class PosixFilePermissionConverterTest extends Specification {
-    def "converts Set<PosixFilePermission to int representation"() {
-
-        expect:
-        PosixFilePermissionConverter.convertToInt(perms) == intValue
-
-        where:
-        perms                                                                |       intValue
-        EnumSet.noneOf(PosixFilePermission)                                  |       0
-        EnumSet.allOf(PosixFilePermission)                                   |       0777
-        EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)                   |       0700
-        EnumSet.of(OWNER_READ, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE)       |       0470
-        EnumSet.of(OWNER_READ, GROUP_READ, OTHERS_READ)                      |       0444
-        EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ)         |       0644
-        EnumSet.of(OWNER_READ, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE)       |       0560
-    }
-
-
-    def "converts int representation to Set<PosixFilePermission)String representation"() {
-        expect:
-        perms == PosixFilePermissionConverter.convertToPermissionsSet(intValue)
-
-        where:
-        perms                                                                |       intValue
-        EnumSet.noneOf(PosixFilePermission)                                  |       0
-        EnumSet.allOf(PosixFilePermission)                                   |       0777
-        EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)                   |       0700
-        EnumSet.of(OWNER_READ, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE)       |       0470
-        EnumSet.of(OWNER_READ, GROUP_READ, OTHERS_READ)                      |       0444
-        EnumSet.of(OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ)         |       0644
-        EnumSet.of(OWNER_READ, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE)       |       0560
-
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
deleted file mode 100644
index c8697fc..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
+++ /dev/null
@@ -1,41 +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.nativeplatform.filesystem.jdk7
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-import spock.lang.Specification
-
- at Requires(TestPrecondition.NOT_WINDOWS)
-class PosixJdk7FilePermissionHandlerTest extends Specification {
-    @Rule TestNameTestDirectoryProvider temporaryFolder
-
-    def "test chmod on non windows platforms with JDK7"() {
-        setup:
-        def file = temporaryFolder.createFile("testFile")
-        def handler = new PosixJdk7FilePermissionHandler()
-        when:
-        handler.chmod(file, mode);
-        then:
-        mode == handler.getUnixMode(file);
-        where:
-        mode << [0722, 0644, 0744, 0755]
-    }
-
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironmentTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironmentTest.groovy
deleted file mode 100644
index b643fcb..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/jna/LibCBackedProcessEnvironmentTest.groovy
+++ /dev/null
@@ -1,39 +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.nativeplatform.jna
-
-import org.gradle.internal.nativeplatform.NativeIntegrationException
-import org.gradle.internal.nativeplatform.services.NativeServices
-import spock.lang.Specification
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-
-class LibCBackedProcessEnvironmentTest extends Specification {
-
-    NativeServices registry = NativeServices.getInstance();
-
-    @Requires(TestPrecondition.FILE_PERMISSIONS) //MACOSX & UNIX
-    def "setNativeEnvironmentVariable throws NativeEnvironmentException for NULL value on env name with errno code"() {
-        setup:
-        LibCBackedProcessEnvironment processEnvironment = new LibCBackedProcessEnvironment(registry.get(LibC.class))
-        when:
-        processEnvironment.setNativeEnvironmentVariable(null, "TEST_ENV_VAR");
-        then:
-        def e = thrown(NativeIntegrationException);
-        e.message == "Could not set environment variable 'null'. errno: 22"
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/processenvironment/ProcessEnvironmentTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/processenvironment/ProcessEnvironmentTest.groovy
deleted file mode 100755
index dd816ee..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/processenvironment/ProcessEnvironmentTest.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.internal.nativeplatform.processenvironment
-
-import org.gradle.internal.nativeplatform.ProcessEnvironment
-import org.gradle.internal.nativeplatform.services.NativeServices
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-import spock.lang.Specification
-
-class ProcessEnvironmentTest extends Specification {
-    @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    @Rule final SetSystemProperties systemProperties = new SetSystemProperties()
-    final ProcessEnvironment env = NativeServices.getInstance().get(ProcessEnvironment)
-
-    @Requires(TestPrecondition.SET_ENV_VARIABLE)
-    def "can set and remove environment variable"() {
-        when:
-        env.setEnvironmentVariable("TEST_ENV_1", "value")
-        env.maybeSetEnvironmentVariable("TEST_ENV_2", "value")
-
-        then:
-        System.getenv("TEST_ENV_1") == "value"
-        System.getenv("TEST_ENV_2") == "value"
-
-        when:
-        env.removeEnvironmentVariable("TEST_ENV_1")
-        env.maybeRemoveEnvironmentVariable("TEST_ENV_2")
-
-        then:
-        System.getenv("TEST_ENV_1") == null
-        System.getenv("TEST_ENV_2") == null
-    }
-
-    @Requires(TestPrecondition.WORKING_DIR)
-    def "can get working directory of current process"() {
-        expect:
-        env.processDir.canonicalFile == new File('.').canonicalFile
-    }
-
-    @Requires(TestPrecondition.WORKING_DIR)
-    def "can get set working directory of current process"() {
-        File originalDir = new File(System.getProperty("user.dir"))
-
-        when:
-        env.setProcessDir(tmpDir.testDirectory)
-
-        then:
-        env.processDir.canonicalFile == tmpDir.testDirectory
-        new File(".").canonicalFile == tmpDir.testDirectory
-
-        cleanup:
-        System.setProperty("user.dir", originalDir.absolutePath)
-        env.setProcessDir(originalDir)
-    }
-
-    @Requires(TestPrecondition.PROCESS_ID)
-    def "can get pid of current process"() {
-        expect:
-        env.pid != null
-        env.maybeGetPid() == env.pid
-    }
-}
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/services/NativeServicesTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/services/NativeServicesTest.groovy
deleted file mode 100755
index 3645ee4..0000000
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeplatform/services/NativeServicesTest.groovy
+++ /dev/null
@@ -1,68 +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.internal.nativeplatform.services
-
-import net.rubygrapefruit.platform.ProcessLauncher
-import net.rubygrapefruit.platform.SystemInfo
-import net.rubygrapefruit.platform.WindowsRegistry
-import org.gradle.internal.nativeplatform.console.ConsoleDetector
-import org.gradle.internal.nativeplatform.ProcessEnvironment
-import org.gradle.internal.nativeplatform.filesystem.Chmod
-import org.gradle.internal.nativeplatform.filesystem.FileSystem
-import org.gradle.internal.nativeplatform.filesystem.Stat
-import org.gradle.internal.os.OperatingSystem
-import spock.lang.Specification
-
-class NativeServicesTest extends Specification {
-    final NativeServices services = NativeServices.getInstance()
-
-    def "makes a ProcessEnvironment available"() {
-        expect:
-        services.get(ProcessEnvironment) != null
-    }
-
-    def "makes an OperatingSystem available"() {
-        expect:
-        services.get(OperatingSystem) != null
-    }
-
-    def "makes a FileSystem available"() {
-        expect:
-        services.get(FileSystem) != null
-        services.get(Chmod) != null
-        services.get(Stat) != null
-    }
-
-    def "makes a ConsoleDetector available"() {
-        expect:
-        services.get(ConsoleDetector) != null
-    }
-
-    def "makes a WindowsRegistry available"() {
-        expect:
-        services.get(WindowsRegistry) != null
-    }
-
-    def "makes a SystemInfo available"() {
-        expect:
-        services.get(SystemInfo) != null
-    }
-
-    def "makes a ProcessLauncher available"() {
-        expect:
-        services.get(ProcessLauncher) != null
-    }
-}
diff --git a/subprojects/open-api/open-api.gradle b/subprojects/open-api/open-api.gradle
index 99965bb..859524c 100644
--- a/subprojects/open-api/open-api.gradle
+++ b/subprojects/open-api/open-api.gradle
@@ -1,6 +1,5 @@
 dependencies {
-    testCompile libraries.groovy
-
+    integTestCompile libraries.groovy
     integTestCompile libraries.slf4j_api
     integTestCompile libraries.commons_lang
 }
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java
deleted file mode 100644
index 547674a..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi;
-
-import org.gradle.internal.UncheckedException;
-import org.gradle.openapi.external.foundation.RequestObserverVersion1;
-import org.gradle.openapi.external.foundation.RequestVersion1;
-
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-public class BlockingRequestObserver implements RequestObserverVersion1 {
-    private final String typeOfInterest;  //either RequestVersion1.EXECUTION_TYPE or RequestVersion1.REFRESH_TYPE
-    private RequestVersion1 request;
-    private Integer result;
-    private String output;
-    private final Lock lock = new ReentrantLock();
-    private final Condition condition = lock.newCondition();
-    private Throwable failure;
-
-    public BlockingRequestObserver() {
-        this(RequestVersion1.EXECUTION_TYPE);
-    }
-
-    public BlockingRequestObserver(String typeOfInterest) {
-        this.typeOfInterest = typeOfInterest;
-    }
-
-    public RequestVersion1 getRequest() {
-        lock.lock();
-        try {
-            assertComplete();
-            return request;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public int getResult() {
-        lock.lock();
-        try {
-            assertComplete();
-            return result;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public String getOutput() {
-        lock.lock();
-        try {
-            assertComplete();
-            return output;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    private void assertComplete() {
-        if (request == null) {
-            throw new AssertionError("Request has not completed.");
-        }
-    }
-
-    void reset() {
-        lock.lock();
-        try {
-            request = null;
-            result = null;
-            output = null;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public void executionRequestAdded(RequestVersion1 request) {
-    }
-
-    public void refreshRequestAdded(RequestVersion1 request) {
-    }
-
-    public void aboutToExecuteRequest(RequestVersion1 request) {
-    }
-
-    public void requestExecutionComplete(RequestVersion1 request, int result, String output) {
-        if (this.typeOfInterest.equals(request.getType())) {
-            lock.lock();
-            try {
-                if (this.request != null) {
-                    failure = new AssertionError("Multiple results for request.");
-                }
-                this.request = request;
-                this.result = result;
-                this.output = output;
-                condition.signalAll();
-            } finally {
-                lock.unlock();
-            }
-        }
-    }
-
-    void waitForRequestExecutionComplete(int timeOutValue, TimeUnit timeOutUnits) {
-        lock.lock();
-        try {
-            Date expiry = new Date(System.currentTimeMillis() + timeOutUnits.toMillis(timeOutValue));
-            while (failure == null && request == null) {
-                if (!condition.awaitUntil(expiry)) {
-                    throw new AssertionError("Timeout waiting for request to complete.");
-                }
-            }
-            if (failure != null) {
-                throw failure;
-            }
-        } catch(Throwable t) {
-            throw UncheckedException.throwAsUncheckedException(t);
-        } finally {
-            lock.unlock();
-        }
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.java
deleted file mode 100644
index 14a6ebe..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.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.integtests.openapi;
-
-import org.gradle.openapi.external.foundation.GradleInterfaceVersion2;
-import org.gradle.openapi.external.foundation.ProjectVersion1;
-import org.gradle.openapi.external.foundation.RequestVersion1;
-import org.gradle.openapi.external.ui.CommandLineArgumentAlteringListenerVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
-import org.gradle.openapi.external.ui.UIFactory;
-
-import java.io.File;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class CrossVersionBuilder {
-    private File targetGradleHomeDir;
-    private File currentDir;
-    private String version;
-
-    public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        this.version = version;
-    }
-
-    public File getCurrentDir() {
-        return currentDir;
-    }
-
-    public void setCurrentDir(File currentDir) {
-        this.currentDir = currentDir;
-    }
-
-    public File getTargetGradleHomeDir() {
-        return targetGradleHomeDir;
-    }
-
-    public void setTargetGradleHomeDir(File targetGradleHomeDir) {
-        this.targetGradleHomeDir = targetGradleHomeDir;
-    }
-
-    public void build() throws Exception {
-        assert targetGradleHomeDir != null;
-        assert currentDir != null;
-
-        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
-        SinglePaneUIVersion1 singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), targetGradleHomeDir, testSingleDualPaneUIInteractionVersion1, false);
-        singlePane.addCommandLineArgumentAlteringListener(new CommandLineArgumentAlteringListenerVersion1() {
-            public String getAdditionalCommandLineArguments(String commandLineArguments) {
-                return "'-g=" + new File(currentDir, "gradle-user-home").getAbsolutePath() + "'";
-            }
-        });
-
-        String actualVersion = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion();
-        assertEquals(version, actualVersion);
-
-        singlePane.setCurrentDirectory(currentDir);
-
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1();
-
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE);
-        gradleInterface.addRequestObserver(testRequestObserver);
-
-        singlePane.aboutToShow();
-
-        gradleInterface.refreshTaskTree();
-
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS);
-
-        assertEquals(String.format("Execution failed%n%s", testRequestObserver.getOutput()), 0, (long) testRequestObserver.getResult());
-
-        List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
-        assertTrue(!rootProjects.isEmpty());
-
-        ProjectVersion1 rootProject = rootProjects.get(0);
-        assertEquals(3, rootProject.getSubProjects().size());
-
-        assertTrue(rootProject.getTasks().size() > 4);
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
index 72e4707..afc57de 100644
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
+++ b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest.groovy
@@ -16,50 +16,85 @@
 package org.gradle.integtests.openapi
 
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
+import org.gradle.integtests.fixtures.IgnoreVersions
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.GradleDistribution
-import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.internal.classloader.DefaultClassLoaderFactory
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Assert
 import org.junit.Rule
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
- at Requires(TestPrecondition.SWING)
+import java.lang.reflect.InvocationTargetException
+
+ at IgnoreVersions({!it.openApiSupported})
 class CrossVersionCompatibilityIntegrationTest extends CrossVersionIntegrationSpec {
     private final Logger logger = LoggerFactory.getLogger(CrossVersionCompatibilityIntegrationTest)
     @Rule public final TestResources resources = new TestResources(temporaryFolder)
 
-    public void canUseOpenApiFromCurrentVersionToBuildUsingAnOlderVersion() {
-        expect:
-        checkCanBuildUsing(current, previous)
+    public void cannotUseOpenApiFromOlderVersionToBuildUsingCurrentVersion() {
+        def classloader = loadOpenApi(previous)
+
+        when:
+        attemptToCreateSinglePaneUI(classloader)
+
+        then:
+        InvocationTargetException e = thrown()
+        e.cause.message == 'Support for the Gradle Open API was removed in the Gradle 2.0 release. You should use the Gradle tooling API instead.'
+
+        when:
+        attemptToCreateDualPaneUI(classloader)
+
+        then:
+        e = thrown()
+        e.cause.message == 'Support for the Gradle Open API was removed in the Gradle 2.0 release. You should use the Gradle tooling API instead.'
+
+        when:
+        attemptToCreateRunner(classloader)
+
+        then:
+        e = thrown()
+        e.cause.message == 'Support for the Gradle Open API was removed in the Gradle 2.0 release. You should use the Gradle tooling API instead.'
     }
 
-    public void canUseOpenApiFromOlderVersionToBuildUsingCurrentVersion() {
-        expect:
-        checkCanBuildUsing(previous, current)
+    void attemptToCreateSinglePaneUI(ClassLoader classloader) {
+        def interactionType = classloader.loadClass("org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1")
+        def interaction = [:].asType(interactionType)
+        def factory = classloader.loadClass("org.gradle.openapi.external.ui.UIFactory")
+        def method = factory.getMethods().find { it.name == 'createSinglePaneUI' }
+        try {
+            method.invoke(null, classloader, current.gradleHomeDir, interaction, true)
+        } catch (InvocationTargetException e) {
+            throw e.cause
+        }
     }
 
-    void checkCanBuildUsing(GradleDistribution openApiVersion, GradleDistribution buildVersion) {
-        if (!buildVersion.openApiSupported) {
-            System.out.println("skipping $buildVersion as it does not support the open API.")
-            return
+    void attemptToCreateDualPaneUI(ClassLoader classloader) {
+        def interactionType = classloader.loadClass("org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1")
+        def interaction = [:].asType(interactionType)
+        def factory = classloader.loadClass("org.gradle.openapi.external.ui.UIFactory")
+        def method = factory.getMethods().find { it.name == 'createDualPaneUI' }
+        try {
+            method.invoke(null, classloader, current.gradleHomeDir, interaction, true)
+        } catch (InvocationTargetException e) {
+            throw e.cause
         }
-        if (!openApiVersion.openApiSupported) {
-            System.out.println("skipping $openApiVersion as it does not support the open API.")
-            return
+    }
+
+    void attemptToCreateRunner(ClassLoader classloader) {
+        def interactionType = classloader.loadClass("org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1")
+        def interaction = [:].asType(interactionType)
+        def factory = classloader.loadClass("org.gradle.openapi.external.runner.GradleRunnerFactory")
+        def method = factory.getMethods().find { it.name == 'createGradleRunner' }
+        try {
+            method.invoke(null, classloader, current.gradleHomeDir, interaction, true)
+        } catch (InvocationTargetException e) {
+            throw e.cause
         }
-        def testClasses = ClasspathUtil.getClasspathForClass(CrossVersionBuilder.class)
-        def junitJar = ClasspathUtil.getClasspathForClass(Assert.class)
-        def classpath = [testClasses, junitJar] + openApiVersion.gradleHomeDir.file('lib').listFiles().findAll { it.name =~ /gradle-open-api.*\.jar/ }
+    }
+
+    private ClassLoader loadOpenApi(GradleDistribution openApiVersion) {
+        def classpath = openApiVersion.gradleHomeDir.file('lib').listFiles().findAll { it.name =~ /gradle-open-api.*\.jar/ }
         logger.info('Using Open API classpath {}', classpath)
-        def classloader = new DefaultClassLoaderFactory().createIsolatedClassLoader(classpath.collect { it.toURI() })
-        def builder = classloader.loadClass(CrossVersionBuilder.class.name).newInstance()
-        builder.targetGradleHomeDir = buildVersion.gradleHomeDir
-        builder.currentDir = testDirectory
-        builder.version = buildVersion.version.version
-        builder.build()
+        new DefaultClassLoaderFactory().createIsolatedClassLoader(classpath.collect { it.toURI() })
     }
 }
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/ExtraTestCommandLineOptionsListener.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/ExtraTestCommandLineOptionsListener.java
deleted file mode 100644
index 1f086df..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/ExtraTestCommandLineOptionsListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi;
-
-import org.gradle.openapi.external.ui.CommandLineArgumentAlteringListenerVersion1;
-
-import java.io.File;
-
-public class ExtraTestCommandLineOptionsListener implements CommandLineArgumentAlteringListenerVersion1 {
-    private final File gradleUserHomeDir;
-
-    public ExtraTestCommandLineOptionsListener(File gradleUserHomeDir) {
-        this.gradleUserHomeDir = gradleUserHomeDir;
-    }
-
-    public String getAdditionalCommandLineArguments(String commandLineArguments) {
-        return String.format("--no-search-upward --gradle-user-home \'%s\'", gradleUserHomeDir);
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy
deleted file mode 100644
index d2c94c6..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi
-
-import org.apache.commons.lang.builder.ReflectionToStringBuilder
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.GradleDistribution
-import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
-import org.gradle.openapi.external.runner.GradleRunnerFactory
-import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1
-import org.gradle.openapi.external.runner.GradleRunnerVersion1
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-
-class GradleRunnerTest {
-
-  static final String JAVA_PROJECT_NAME = 'javaproject'
-  static final String SHARED_NAME = 'shared'
-  static final String API_NAME = 'api'
-  static final String WEBAPP_NAME = 'webservice'
-  static final String SERVICES_NAME = 'services'
-  static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-  private File javaprojectDir
-    File gradleUserHomeDir
-
-  @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-  final GradleDistribution dist = new UnderDevelopmentGradleDistribution()
-  @Rule public final TestResources resources = new TestResources(temporaryFolder, 'testproject')
-
-  @Before
-  void setUp() {
-      javaprojectDir = temporaryFolder.testDirectory
-      gradleUserHomeDir = temporaryFolder.file("gradle-user-home")
-
-  }
-
-    String toCommand(String command) {
-        "'-g=$gradleUserHomeDir.absolutePath' $command"
-    }
-
-  /**
-   * We just want to make sure we can instantiate a GradleRunner here. That's all
-  */
-  @Test
-  public void testInstantiation()
-  {
-    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1(javaprojectDir)
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-  }
-
-  /**
-   * This does a basic execution. It also checks to make sure that the notifications were fired
-   * correctly.
-  */
-  @Test
-  public void testExecution()
-  {
-      def gradleUserHome = temporaryFolder.file("gradle-user-home")
-    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1( javaprojectDir )
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-
-    runner.executeCommand(toCommand("clean build"))
-
-        //wait for it to complete
-    int totalWaitTime = 0;
-    int maximumWaitTime = 80
-    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
-        try {
-            Thread.sleep(1000);
-        }
-        catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        totalWaitTime += 1;
-    }
-
-    if( totalWaitTime > maximumWaitTime ) {
-      throw new AssertionError( "Waited $totalWaitTime seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\n. Interaction: $interaction" )
-    }
-
-    //now make sure we were notified of things correctly:
-
-    //it should have fired a message that execution has started
-    Assert.assertTrue( "Execution did not report started. Interaction: $interaction", interaction.executionStarted )
-    
-    //it should have finished
-    Assert.assertTrue( "Execution did not report finished. Interaction: $interaction", interaction.executionFinished )
-
-    //it should have been successful
-    Assert.assertTrue( "Did not execute command successfully. Interaction: $interaction", interaction.wasSuccessful )
-
-    //we should have output
-    Assert.assertTrue( "Missing output. Interaction: $interaction", interaction.output.length() > 0 )
-
-    //we should have a message when we finished (basically the full output)
-    Assert.assertTrue( "Missing finish message. Interaction: $interaction", interaction.finishMessage != null )
-
-    //there should have been multiple tasks to execute
-    Assert.assertTrue( "Not enough tasks executed. Expected multiple. Found $interaction.numberOfTasksToExecute. Interaction: $interaction", interaction.numberOfTasksToExecute > 1 )
-
-    //we should have been notified that tasks started and completed (we're not interested in tracking how many times or specific tasks as that might change too often with releases of gradle.
-    Assert.assertTrue( "No tasks reported started. Interaction: $interaction", interaction.taskStarted )
-    Assert.assertTrue( "No tasks reported completed. Interaction: $interaction", interaction.taskCompleted )
-  }
-
-  /**
-   * This tests killing a task. We'll start a build task then kill it after it starts executing.
-   * Note: the kill interaction actually kills execution. It waits for a certain number of tasks
-   * to be executed.
-  */
-  @Test
-  public void testKill()
-  {
-    KillTestInteraction interaction = new KillTestInteraction(javaprojectDir)
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    interaction.runner = runner
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-
-    runner.executeCommand(toCommand("build"))
-
-        //wait for it to complete
-    int totalWaitTime = 0;
-    int maximumWaitTime = 80
-    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
-        try {
-            Thread.sleep(1000);
-        }
-        catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        totalWaitTime += 1;
-    }
-
-    if( totalWaitTime > maximumWaitTime ) {
-      throw new AssertionError( "Waited $totalWaitTime seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nInteraction: $interaction")
-    }
-
-    //make sure we tried to kill the task
-    Assert.assertTrue( "Did not attempt to kill execution. Interaction: $interaction", interaction.killedTask )
-
-    //now make sure we were notified of things correctly:
-
-    //it should NOT have been successful
-    Assert.assertFalse( "Erroneously executed successfully (was not killed). Interaction: $interaction", interaction.wasSuccessful )
-
-    //it should have fired a message that execution has started
-    Assert.assertTrue( "Execution did not report started. Interaction: $interaction", interaction.executionStarted )
-
-    //it should have finished
-    Assert.assertTrue( "Execution did not report finished. Interaction: $interaction", interaction.executionFinished )
-  }
-}
-
-  //Inner class used to track what has been called
-  public class TestGradleRunnerInteractionVersion1 implements GradleRunnerInteractionVersion1
-  {
-    private File workingDirectory
-    private StringBuilder output = new StringBuilder()
-    private String finishMessage
-    boolean wasSuccessful
-    boolean executionStarted
-    int numberOfTasksToExecute
-    boolean executionFinished
-    boolean taskCompleted
-    boolean taskStarted
-
-
-    public TestGradleRunnerInteractionVersion1(File workingDirectory) {
-      this.workingDirectory = workingDirectory;
-    }
-
-    def TestGradleRunnerInteractionVersion1() {
-    }
-
-    File getWorkingDirectory() { return workingDirectory }
-
-    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
-
-    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.AlwaysFull }
-
-    void reportExecutionStarted() { executionStarted = true }
-    void reportNumberOfTasksToExecute(int size) { numberOfTasksToExecute = size }
-
-    //both of these will be fired often. We're not going to try to track that.
-    void reportTaskStarted(String currentTaskName, float percentComplete) { taskStarted = true; }
-    void reportTaskComplete(String currentTaskName, float percentComplete) { taskCompleted = true }
-
-    void reportLiveOutput(String output) { this.output.append( output ) }
-
-    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-      this.executionFinished = true;
-      this.wasSuccessful = wasSuccessful
-      this.finishMessage = message;
-    }
-
-    File getCustomGradleExecutable() { return null; }
-
-    String toString() {
-      return ReflectionToStringBuilder.toString(this);
-    }
-  }
-
-
-  //class to track that has class was started and then kills it. 
-  private class KillTestInteraction implements GradleRunnerInteractionVersion1
-  {
-    private GradleRunnerVersion1 runner
-    int tasks = 0
-    boolean killedTask
-
-    //after at least 2 tasks start, try to kill the process. This simulates someone killing it while
-    //its in the middle of running
-    def void reportTaskStarted(String currentTaskName, float percentComplete) {
-      tasks++
-      if( tasks == 2 ) {
-        killedTask = true
-        runner.killProcess();
-      }
-    }
-    private File workingDirectory
-    boolean wasSuccessful
-    boolean executionStarted
-    boolean executionFinished
-
-    public KillTestInteraction(File workingDirectory) {
-      this.workingDirectory = workingDirectory;
-    }
-
-    File getWorkingDirectory() { return workingDirectory }
-
-    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
-
-    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.InternalExceptions }
-
-    void reportExecutionStarted() { executionStarted = true }
-    void reportNumberOfTasksToExecute(int size) { }
-
-    void reportTaskComplete(String currentTaskName, float percentComplete) {}
-    void reportLiveOutput(String output) {}
-    File getCustomGradleExecutable() { return null; }
-
-    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-      this.executionFinished = true;
-      this.wasSuccessful = wasSuccessful
-    }
-
-    String toString() {
-      return ReflectionToStringBuilder.toString(this);
-    }
-  }
\ No newline at end of file
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java
deleted file mode 100644
index 8dd1280..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi;
-
-import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext;
-import org.gradle.internal.UncheckedException;
-import org.gradle.openapi.external.ui.DualPaneUIVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
-import org.gradle.openapi.external.ui.UIFactory;
-import org.gradle.test.fixtures.file.TestDirectoryProvider;
-import org.junit.Assert;
-import org.junit.rules.MethodRule;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.Statement;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.List;
-
-public class OpenApiFixture implements MethodRule {
-    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext();
-    private final TestDirectoryProvider testDirectoryProvider;
-    private final List<JFrame> frames = new ArrayList<JFrame>();
-
-    public OpenApiFixture(TestDirectoryProvider testDirectoryProvider) {
-        this.testDirectoryProvider = testDirectoryProvider;
-    }
-
-    public Statement apply(final Statement base, FrameworkMethod method, final Object target) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                try {
-                    base.evaluate();
-                } finally {
-                    SwingUtilities.invokeAndWait(new Runnable() {
-                        public void run() {
-                            for (JFrame frame : frames) {
-                                frame.dispose();
-                            }
-                        }
-                    });
-                }
-            }
-        };
-    }
-
-    public SinglePaneUIVersion1 createSinglePaneUI() {
-        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
-        SinglePaneUIVersion1 singlePane;
-        try {
-            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), buildContext.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-
-        //make sure we got something
-        Assert.assertNotNull(singlePane);
-
-        singlePane.setCurrentDirectory(testDirectoryProvider.getTestDirectory());
-        singlePane.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(buildContext.getGradleUserHomeDir()));
-
-        return singlePane;
-    }
-
-    public DualPaneUIVersion1 createDualPaneUI() {
-        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
-        DualPaneUIVersion1 dualPane;
-        try {
-            dualPane = UIFactory.createDualPaneUI(getClass().getClassLoader(), buildContext.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-
-        //make sure we got something
-        Assert.assertNotNull(dualPane);
-
-        dualPane.setCurrentDirectory(testDirectoryProvider.getTestDirectory());
-        dualPane.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(buildContext.getGradleUserHomeDir()));
-
-        return dualPane;
-    }
-
-    public JFrame open(SinglePaneUIVersion1 ui) {
-        return createTestFrame(ui.getComponent(), null);
-    }
-
-    public JFrame open(DualPaneUIVersion1 ui) {
-        return createTestFrame(ui.getMainComponent(), ui.getOutputPanel());
-    }
-
-    /*
-    * This shows the specified frame for a moment so the event queue can be emptied. This
-    * ensures Swing events a processed
-    * Don't place anything between the following three lines (especially something that might
-    * throw an exception). This shows and hides the UI, giving it time to actually show itself
-    * and empty the event dispatch queue.
-    */
-    public void flushEventQueue(final JFrame frame) {
-        try {
-            SwingUtilities.invokeAndWait(new Runnable() {
-                public void run() {
-                    frame.setVisible(true);
-                }
-            });
-            Thread.sleep(500);
-            SwingUtilities.invokeAndWait(new Runnable() {
-                public void run() {
-                    frame.setVisible(false);
-                }
-            });
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    private JFrame createTestFrame(Component mainComponent, Component outputComponent) {
-        JFrame frame = new JFrame();
-        frames.add(frame);
-        frame.setSize(650, 500);
-        JPanel panel = new JPanel(new BorderLayout());
-        frame.getContentPane().add(panel);
-
-        //add a large red label explaining this
-        JLabel label2 = new JLabel("Performing Open API Integration Test!");
-        label2.setForeground(Color.red);
-        label2.setFont(label2.getFont().deriveFont(26f));
-        panel.add(label2, BorderLayout.NORTH);
-
-        //add the main UI to the frame
-        panel.add(mainComponent, BorderLayout.CENTER);
-
-        if (outputComponent != null) {
-            panel.add(outputComponent, BorderLayout.SOUTH);
-        }
-
-        return frame;
-    }
-
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy
deleted file mode 100644
index a1c6a54..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy
+++ /dev/null
@@ -1,971 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi
-
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.openapi.external.ExternalUtility
-import org.gradle.openapi.external.foundation.GradleInterfaceVersion2
-import org.gradle.openapi.external.foundation.ProjectVersion1
-import org.gradle.openapi.external.foundation.RequestVersion1
-import org.gradle.openapi.external.foundation.TaskVersion1
-import org.gradle.openapi.external.foundation.favorites.FavoriteTaskVersion1
-import org.gradle.openapi.external.foundation.favorites.FavoritesEditorVersion1
-import org.gradle.openapi.external.ui.*
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.PreconditionVerifier
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Assert
-import org.junit.ClassRule
-import org.junit.Rule
-import org.junit.Test
-
-import javax.swing.*
-import java.awt.*
-import java.awt.event.HierarchyEvent
-import java.awt.event.HierarchyListener
-import java.util.concurrent.TimeUnit
-
-import static org.hamcrest.Matchers.*
-
-/**
- * This tests numerous aspects of the Open API UI. This is how the Idea plugin extracts the UI from
- * Gradle.
- */
- at Requires(TestPrecondition.SWING)
-class OpenApiUiTest {
-
-    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext();
-    @Rule public TestResources resources = new TestResources(temporaryFolder, 'testproject')
-    @Rule public OpenApiFixture openApi = new OpenApiFixture(temporaryFolder)
-    @ClassRule public static PreconditionVerifier verifier = new PreconditionVerifier()
-
-    /**
-     This tests to see if we can call the UIFactory to create a single pane UI.
-     This is only testing that extracting the UI returns something without giving
-     errors and that it has a component. This is just a good general-case test
-     to make sure the basics are working.
-     */
-    @Test
-    void testSinglePaneBasic() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-
-        //make sure we got something
-        Assert.assertNotNull(singlePane)
-
-        //tell it we're about to show it, so it'll create a component
-        singlePane.aboutToShow();
-
-        //make sure we now have that component
-        Assert.assertNotNull(singlePane.getComponent())
-    }
-
-    /**
-     * This tests to see if we can call the UIFactory to create a dual pane UI.
-     This is only testing that extracting the UI returns something without giving
-     errors and that it has its dual components. This is just a good general-case test
-     to make sure the basics are working.
-     */
-    @Test
-    void testDualPaneBasic() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-
-        //make sure we got something
-        Assert.assertNotNull(dualPane)
-
-        //tell it we're about to show it, so it'll create a component
-        dualPane.aboutToShow();
-
-        //make sure we now have the main component
-        Assert.assertNotNull(dualPane.getMainComponent())
-
-        //and the output component
-        Assert.assertNotNull(dualPane.getOutputPanel())
-    }
-
-    /**
-     * This verifies that favorites are working for some basics. We're going to test this with both
-     * the single and dual pane UIs (they actually use the same editor then for other tests we'll
-     * assume they're same).
-     */
-    @Test
-    void testFavoritesBasic() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        checkFavoritesBasic(singlePane)
-
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        checkFavoritesBasic(dualPane)
-    }
-
-    /**
-     * This verifies that we favorites are basically working based on the given UI. We're going to add one, then
-     * do some 'gets' to find the just-added favorite.
-     */
-    private void checkFavoritesBasic(BasicGradleUIVersion1 basicGradleUI) {
-        FavoritesEditorVersion1 editor = basicGradleUI.getFavoritesEditor()
-
-        //there should be no favorites as of yet
-        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
-
-        //add one (doesn't really matter what it is)
-        def fullCommandLine = "-t -S"
-        def displayName = "Task List With Stack trace"
-        FavoriteTaskVersion1 favorite = editor.addFavorite(fullCommandLine, displayName, true)
-
-        //make sure something was added
-        Assert.assertEquals(1, editor.getFavoriteTasks().size())
-
-        //get the newly-added favorite by command line.
-        FavoriteTaskVersion1 matchingFavorite1 = editor.getFavorite(fullCommandLine)
-        Assert.assertEquals(favorite, matchingFavorite1)
-
-        //get the newly-added favorite by displayName.
-        FavoriteTaskVersion1 matchingFavorite2 = editor.getFavoriteByDisplayName(displayName)
-        Assert.assertEquals(favorite, matchingFavorite2)
-
-        Assert.assertTrue(matchingFavorite2.alwaysShowOutput())
-    }
-
-    /**
-     * This verifies that we can edit favorites. We're going to add a favorite then edit its
-     * command line, display name, and 'show output' setting.
-     */
-    @Test
-    void testEditingFavorites() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-        def originalFullCommandLine = "-t -S"
-        def originalDisplayName = "Task List With Stack trace"
-        FavoriteTaskVersion1 addedFavorite = editor.addFavorite(originalFullCommandLine, originalDisplayName, true)
-
-        //make sure we can find the just-added favorite
-        Assert.assertNotNull(editor.getFavorite(originalFullCommandLine))
-        Assert.assertNotNull(editor.getFavoriteByDisplayName(originalDisplayName))
-
-        String newFullCommandLine = "-t -S -d"
-        String newDisplayName = "new task list"
-        String error = editor.editFavorite(addedFavorite, newFullCommandLine, newDisplayName, false)
-        Assert.assertNull(error)  //we should get no error
-
-        //now we shouldn't be able to find the favorite using the original values. This is part of verifying the values were in fact changed.
-        Assert.assertNull(editor.getFavorite(originalFullCommandLine))
-        Assert.assertNull(editor.getFavoriteByDisplayName(originalDisplayName))
-
-        //make sure we can find it using the new values. This is part of verifying the values were in fact changed.
-        Assert.assertNotNull(editor.getFavorite(newFullCommandLine))
-        Assert.assertNotNull(editor.getFavoriteByDisplayName(newDisplayName))
-
-        //there should just be 1 favorite
-        Assert.assertEquals(1, editor.getFavoriteTasks().size())
-    }
-
-    /**
-     * This verifies that we can remove favorites. We're going to add some favorites then remove them
-     * verifying that they've gone.
-     */
-    @Test
-    void testRemovingFavorites() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-        //there should be no favorites as of yet
-        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
-
-        //add one (doesn't really matter what it is)
-        String command1 = "build"
-        FavoriteTaskVersion1 favorite1 = editor.addFavorite(command1, command1, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command1))
-        Assert.assertEquals(1, editor.getFavoriteTasks().size())
-
-        //add another one
-        String command2 = "build -xtest"
-        FavoriteTaskVersion1 favorite2 = editor.addFavorite(command2, command2, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command2))
-        Assert.assertEquals(2, editor.getFavoriteTasks().size())
-
-        String command3 = "clean"
-        FavoriteTaskVersion1 favorite3 = editor.addFavorite(command3, command3, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command3))
-        Assert.assertEquals(3, editor.getFavoriteTasks().size())
-
-        String command4 = "docs"
-        FavoriteTaskVersion1 favorite4 = editor.addFavorite(command4, command4, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command4))
-        Assert.assertEquals(4, editor.getFavoriteTasks().size())
-
-        //now remove one of them
-        java.util.List removed1 = [favorite2]
-        editor.removeFavorites(removed1)
-
-        //make sure it was removed
-        Assert.assertNull(editor.getFavorite(command2))
-        Assert.assertEquals(3, editor.getFavoriteTasks().size())
-
-        //now remove multiples
-        java.util.List removed2 = [favorite1, favorite4]
-        editor.removeFavorites(removed2)
-
-        //make sure they were both removed
-        Assert.assertNull(editor.getFavorite(command1))
-        Assert.assertNull(editor.getFavorite(command4))
-        Assert.assertEquals(1, editor.getFavoriteTasks().size())
-    }
-
-    /**
-     * This tests executing multiple favorites at once. We'll add two favorites, then execute both of them
-     * via GradleInterfaceVersion1.executeFavorites(). This should execute both as a single command
-     * concatenating them together.
-     */
-    @Test
-    void testExecutingFavorites() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-        //this starts the execution queue
-        singlePane.aboutToShow()
-
-        //there should be no favorites as of yet
-        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
-
-        //add one (doesn't really matter what it is)
-        String command1 = "build"
-        FavoriteTaskVersion1 favorite1 = editor.addFavorite(command1, command1, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command1))
-        Assert.assertEquals(1, editor.getFavoriteTasks().size())
-
-        //add another one
-        String command2 = "clean"
-        FavoriteTaskVersion1 favorite2 = editor.addFavorite(command2, command2, true)
-
-        //make sure it was added
-        Assert.assertNotNull(editor.getFavorite(command2))
-        Assert.assertEquals(2, editor.getFavoriteTasks().size())
-
-        //add a request observer so we can observe when the command is finished. This allows us to
-        //see what was actually executed.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
-        ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).addRequestObserver(testRequestObserver)
-
-        //now execute both favorites
-        java.util.List<FavoriteTaskVersion1> favorites = [favorite1, favorite2]
-        RequestVersion1 request = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).executeFavorites(favorites)
-
-        Assert.assertNotNull(request)
-
-        //verify that the actual command that was executed is a concatenation of both favorites
-        Assert.assertThat(request.getFullCommandLine(), startsWith("build clean"))
-    }
-
-    /**
-     * This tests getting projects and tasks from gradle. It then goes through a series of tests
-     * related to projects and tasks (such as making sure their description is returned, their
-     * parent is returned, etc).
-     */
-    @Test
-    void testProjectsAndTasks() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-        //make sure our samples directory exists
-        Assert.assertTrue(gradleInterface.getCurrentDirectory().isDirectory())
-
-        //add a request observer so we can observe when the command is finished. This allows us to
-        //see what was actually executed.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
-        gradleInterface.addRequestObserver(testRequestObserver)
-
-        //this starts the execution queue
-        dualPane.aboutToShow()
-
-        gradleInterface.refreshTaskTree()
-
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-
-        Assert.assertEquals("Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
-
-        java.util.List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
-        Assert.assertFalse(rootProjects.isEmpty());   //do we have any root projects?
-
-        ProjectVersion1 rootProject = rootProjects.get(0);
-        Assert.assertNotNull(rootProject);
-        Assert.assertThat(rootProject.getSubProjects().size(), equalTo(3))
-
-        //Quick check to make sure there are tasks on each of the sub projects.
-        //The exact task names will change over time, so I don't want to try
-        //to test for those. I'll just make sure there are several.
-        Iterator<ProjectVersion1> iterator = rootProjects.get(0).getSubProjects().iterator();
-        while (iterator.hasNext()) {
-            ProjectVersion1 projectVersion1 = iterator.next();
-            Assert.assertTrue(projectVersion1.getTasks().size() > 4);
-        }
-
-        //there should be a 'services' project
-        ProjectVersion1 servicesProject = rootProjects.get(0).getSubProject("services");
-        Assert.assertNotNull(servicesProject);
-
-        //and it contains a 'webservice' sub project
-        ProjectVersion1 webserviceProject = servicesProject.getSubProject("webservice");
-        Assert.assertNotNull(webserviceProject);
-
-        ProjectVersion1 apiProject = rootProjects.get(0).getSubProject("api");
-        Assert.assertNotNull(apiProject);
-
-        //verify the parent project is set correctly
-        Assert.assertEquals(servicesProject, webserviceProject.getParentProject())
-
-        //verify its full name is correct (this might should be prefixed with a colon)
-        Assert.assertEquals("services:webservice", webserviceProject.getFullProjectName())
-
-        //verify getSubProjectFromFullPath works
-        ProjectVersion1 foundProject = rootProject.getSubProjectFromFullPath("services:webservice")
-        Assert.assertNotNull("Failed to find services:webservice", foundProject)
-        Assert.assertEquals(webserviceProject, foundProject)
-
-        //verify that are multiple tasks here (we know their should be)
-        Assert.assertTrue(webserviceProject.getTasks().size() > 4);
-
-        //verify getTaskFromFullPath works
-        TaskVersion1 apiBuildTask = rootProject.getTaskFromFullPath(":api:build")
-        Assert.assertNotNull("Failed to find :api:build", apiBuildTask)
-
-        Assert.assertEquals(apiProject, apiBuildTask.getProject())
-
-        //then make sure it has a description
-        Assert.assertNotNull(apiBuildTask.getDescription())
-
-        //and that its not marked as the default (we need a task to be the default so we can verify it returns true)
-        Assert.assertFalse(apiBuildTask.isDefault())
-
-        //there are no default tasks here
-        Assert.assertTrue(apiProject.getDefaultTasks().isEmpty())
-
-        //this build task is a child of the api project. Should be the same task we got earlier
-        TaskVersion1 buildTask = apiProject.getTask("build")
-        Assert.assertNotNull("Failed to find build task", buildTask)
-        Assert.assertEquals(apiBuildTask, buildTask)
-    }
-
-    /**
-     * This verifies that the GradleInterfaceVersion1.refreshTaskTree that takes
-     * additional arguments works. We're not really interested in what those additional
-     * arguments are, just that it passes them along.
-     */
-    @Test
-    void testRefreshWithArguments() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-        //make sure our samples directory exists
-        if (!gradleInterface.getCurrentDirectory().exists()) {
-            throw new AssertionError('sample project missing. Expected it at: ' + gradleInterface.getCurrentDirectory())
-        }
-
-        BlockingRequestObserver setupListener = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
-        gradleInterface.addRequestObserver(setupListener)
-
-        //this starts the execution queue
-        dualPane.aboutToShow()
-
-        // wait for the implicit refresh to complete
-        setupListener.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-        gradleInterface.removeRequestObserver(setupListener)
-
-        //add a request observer so we can observe when the command is finished. This allows us to
-        //see what was actually executed.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
-        gradleInterface.addRequestObserver(testRequestObserver)
-
-        RequestVersion1 request = gradleInterface.refreshTaskTree2("-xtest")
-
-        //make sure that the actual request is the normal refresh request with our
-        //(this line is really what we're trying to test)
-        Assert.assertThat(request.getFullCommandLine(), startsWith("tasks -xtest"))
-
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-
-        Assert.assertEquals("Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
-
-        Assert.assertEquals("Not our request", request, testRequestObserver.request);
-    }
-
-    /**
-     * This verifies that you can add custom stuff to the setup tab. This is a UI test and is kinda tricky. We're going
-     * to use a HierarchyListener to see if our component is made visible. This will confirm if it was added or not
-     * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
-     * really doing here, is adding a 'custom' component to the UI, then adding the UI to a frame, then showing the frame,
-     * so we can verify that our component was shown.
-     */
-    @Test
-    void testAddingComponentToSetupTab() {
-        if (java.awt.GraphicsEnvironment.isHeadless()) {
-            return;  // Can't run this test in headless mode!
-        }
-
-        JLabel label = new JLabel("Testing Testing 123")
-        TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
-        label.addHierarchyListener(hierarchyAdapter)
-
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-
-        //make sure we haven't been told the component was shown or hidden yet
-        Assert.assertFalse(hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
-
-        singlePane.aboutToShow();
-
-        singlePane.setCustomPanelToSetupTab(label)
-
-        //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
-        Assert.assertFalse(hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
-
-        //now create a frame, place the UI in it, then show it briefly
-        JFrame frame = openApi.open(singlePane)
-
-        //set the Setup tab as the current tab. This is required to actually show the component.
-        int setupTabIndex = singlePane.getGradleTabIndex("Setup");
-        Assert.assertTrue("Failed to get index of setup tab", setupTabIndex != -1)
-        singlePane.setCurrentGradleTab(setupTabIndex);
-
-        //still should not show the component (its not yet visible, but is about to be)
-        Assert.assertFalse(hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
-
-        //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
-        //queue. This is required for the setup tab to become current as well as show the custom component we added.
-        openApi.flushEventQueue(frame)
-
-        Assert.assertEquals("The setup tab was not selected", setupTabIndex, singlePane.getCurrentGradleTab())
-
-        //now the label should have been made visible then invisible
-        Assert.assertTrue(hierarchyAdapter.componentWasShown)
-        Assert.assertTrue(hierarchyAdapter.componentWasHidden)
-    }
-
-    /**
-     * This verifies that you can add a custom tab to the UI. This is a UI test and is kinda tricky. We're going
-     * to use a HierarchyListener to see if our tab component is made visible. This will confirm if it was added or not
-     * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
-     * really doing here, is adding a tab to the UI, then adding the UI to a frame, then showing the frame so we can
-     * then verify that our tab was shown (actually using our tab's component).
-     */
-    @Test
-    void testAddingCustomTab() {
-        if (java.awt.GraphicsEnvironment.isHeadless()) {
-            return;  // Can't run this test in headless mode!
-        }
-
-        TestTab testTab = new TestTab()
-
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-
-        //make sure we haven't been told the component was shown or hidden yet
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
-
-        //make sure things are initialized properly. These should all be false
-        Assert.assertFalse(testTab.nameRetrieved);
-        Assert.assertFalse(testTab.informedAboutToShow);
-        Assert.assertFalse(testTab.componentCreated);
-
-        int originalCount = singlePane.getGradleTabCount();
-
-        singlePane.addTab(99, testTab) //I don't really care about the index. It should accept a number that is too large and handle it appropriately.
-
-        singlePane.aboutToShow()
-
-        //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
-
-        //now create a frame, place the UI in it, then show it briefly
-        JFrame frame = openApi.open(singlePane)
-
-        String testTabName = "Test Tab"
-
-        //set the test tab as the current tab. This is required to actually show the component.
-        int testTabIndex = singlePane.getGradleTabIndex(testTabName)
-        Assert.assertTrue("Failed to get index of test tab", testTabIndex != -1)
-        singlePane.setCurrentGradleTab(testTabIndex)
-
-        //just to test getGradleTabName, make sure it returns our tab name
-        Assert.assertEquals(testTabName, singlePane.getGradleTabName(testTabIndex))
-
-        //to test getGradleTabCount, make sure the tab count went up by 1
-        Assert.assertEquals(originalCount + 1, singlePane.getGradleTabCount())
-
-        //still should not show the component (its not yet visible, but is about to be)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
-
-        //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
-        //queue. This is required for the test tab to become current.
-        openApi.flushEventQueue(frame)
-
-        Assert.assertEquals("The test tab was not selected", testTabIndex, singlePane.getCurrentGradleTab())
-
-        //now the label should have been made visible then invisible
-        Assert.assertTrue(testTab.hierarchyAdapter.componentWasShown)
-        Assert.assertTrue(testTab.hierarchyAdapter.componentWasHidden)
-
-        //at the end, the name should have been queried, we should have been told we were about to shown, and the component should be created
-        Assert.assertTrue(testTab.nameRetrieved);
-        Assert.assertTrue(testTab.informedAboutToShow);
-        Assert.assertTrue(testTab.componentCreated);
-
-        //reset the test tab (resets the listener so we can remove the tab and verify that it no longer shows up, as well as some of our test variables)
-        testTab.reset()
-        singlePane.removeTab(testTab)
-
-        //I'm going to set the current tab, but this shouldn't do anything because the tab was removed
-        singlePane.setCurrentGradleTab(testTabIndex);
-
-        //part of showing the UI is telling it its about to be shown. In this case, nothing should happen
-        //related to the test tab. It has been removed
-        singlePane.aboutToShow()
-
-        //This shows and hides the UI, giving it time to actually show itself and empty the event
-        //dispatch queue. This is required for the test tab to become current (were it still present).
-        openApi.flushEventQueue(frame)
-
-        //try to get the test tab
-        testTabIndex = singlePane.getGradleTabIndex("Test Tab");
-        Assert.assertTrue("Erroneously got index of test tab. It was removed", testTabIndex == -1)
-
-        //we've removed it, so it shouldn't have been polled about or informed of anything
-        Assert.assertFalse(testTab.nameRetrieved);
-        Assert.assertFalse(testTab.informedAboutToShow);
-        Assert.assertFalse(testTab.componentCreated);
-
-        //It was not shown after the reset, these should both be false
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
-        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
-    }
-
-    /**
-     * We want to make sure the settings are working correctly here. This is the mechanism that
-     * handles saving/restoring the values within the UI and can be stored in different ways
-     * depending on how the UI integrated with its parent (its up to whoever implements
-     * SettingsNodeVersion1). Here, to spot check that the basics are working, we'll create a
-     * UI, set a value, close it, then recreate it using the same settings object. The values
-     * should be saved upon close and then restored.
-     */
-    @Test
-    void testSettings() {
-        TestSettingsNodeVersion1 settingsNode = new TestSettingsNodeVersion1();
-
-        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), settingsNode);
-        SinglePaneUIVersion1 singlePane = null;
-        try {
-            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), buildContext.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
-        } catch (Exception e) {
-            throw new AssertionError("Failed to extract single pane: Caused by " + e.getMessage())
-        }
-
-        File illegalDirectory = temporaryFolder.testDirectory.file("non-existant").createDir();
-        if (illegalDirectory.equals(singlePane.getCurrentDirectory())) {
-            throw new AssertionError("Directory already set to 'test' directory. The test is not setup correctly.");
-        }
-
-        //this is required to get the ball rolling
-        singlePane.aboutToShow();
-
-        //set the current directory after calling aboutToShow (otherwise, it'll stomp over us when it restores its default settings)
-        singlePane.setCurrentDirectory(illegalDirectory);
-
-        //close the UI. This saves the current settings.
-        singlePane.close();
-
-        //now instantiate it again
-        testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), settingsNode);
-        try {
-            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), buildContext.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
-        } catch (Exception e) {
-            throw new AssertionError("Failed to extract single pane (second time): Caused by " + e.getMessage())
-        }
-
-        //this should restore the previous settings
-        singlePane.aboutToShow();
-
-        Assert.assertEquals(illegalDirectory, singlePane.getCurrentDirectory());
-    }
-
-    /**
-     * This tests that the command line altering mechanism works. This adds additional
-     * things to the command line being executed. This is used by gradle build systems
-     * that need custom, system-specific arguments passed to it that aren't known by gradle
-     * proper. For example: you're working with a large project made of MANY subprojects, a
-     * custom IDE plugin can track which projects you're focusing on and pass that information
-     * to the build system via this mechanism, so it only builds appropriate projects.
-     * This is awkward to test because its real use requires a customized build system,
-     * so I'm going to pass an argument that is illegal by itself -- meaning no tasks
-     * are specified. Then I'll alter the command line by adding an actual task. Then
-     * wait for it to complete and verify what was executed
-     */
-    @Test
-    void testCommandLineAlteringListener() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-        dualPane.aboutToShow()
-
-        //add a request observer so we can observe when the command is finished. This allows us to
-        //see what was actually executed.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
-        gradleInterface.addRequestObserver(testRequestObserver)
-
-        //now that we know that command is illegal by itself, try it again but the listener will append 'build'
-        //to the command line which makes it legal (again, we don't really care what we execute.
-        TestCommandLineArgumentAlteringListenerVersion1 commandLineArgumentAlteringListener = new TestCommandLineArgumentAlteringListenerVersion1("classes")
-        gradleInterface.addCommandLineArgumentAlteringListener(commandLineArgumentAlteringListener)
-
-        //execute this before we do our test. This is not legal by itself. It should fail. That means our
-        //test is setup correctly. For example: if someone adds a default task to this project, this will
-        //generate NO error and thus, our test will prove nothing. If you get a test failure here, you
-        //can try changing the command line to something that's illegal by itself (we don't care what).
-        RequestVersion1 request = gradleInterface.executeCommand2("-s", "test command")
-
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-
-        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith("-s "))
-        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), endsWith(" classes"))
-
-        //make sure it completed execution correctly
-        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
-
-        //the request that was executed should be equal to our original command with our 'altered' command added to it
-        Assert.assertNotNull("Missing 'execution completed' request", testRequestObserver.request)
-
-        //just to be paranoid, let's make sure it was actually our request. If this fails, it probably represents something
-        //fundamentally flawed with the request or request wrapper mechanism.
-        Assert.assertEquals(request, testRequestObserver.request)
-
-        gradleInterface.removeRequestObserver(testRequestObserver)
-        gradleInterface.removeCommandLineArgumentAlteringListener(commandLineArgumentAlteringListener)
-    }
-
-    /**
-     * This tests that getVersion returns the same thing as the jar's suffix.
-     * We'll get the gradle jar, then strip off its extension and verify that
-     * the jar's name ends with the version number.
-     */
-    @Test
-    void testVersion() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        String version = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion()
-
-        Assert.assertNotNull("null version number", version)
-
-        Assert.assertFalse("Empty version number", version.trim().equals(""))       //shouldn't be empty
-
-        File gradleJar = ExternalUtility.getGradleJar(buildContext.gradleHomeDir)
-
-        Assert.assertNotNull("Missing gradle jar", gradleJar)                         //we should have a gradle jar
-
-        int indexOfExtension = gradleJar.getName().toLowerCase().lastIndexOf(".jar")  //get the index of its extension
-
-        Assert.assertTrue("Has no '.jar' extension", indexOfExtension != -1)          //it had better have an extension
-
-        String jarName = gradleJar.getName().substring(0, indexOfExtension)              //get its name minus the extension
-
-        assert jarName.endsWith(version)  //the name (minus extension) should end with the version
-    }
-
-    /**
-     * This is just a spot check that getGradleHomeDirectory works. Its based off
-     * of the gradle you're running.
-     */
-    @Test
-    void testGradleHomeDirectory() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-
-        Assert.assertEquals(buildContext.gradleHomeDir, singlePane.getGradleHomeDirectory())
-    }
-
-    /**
-     * This is just a spot check that we can get an instance of the OutputUILord.
-     * Other tests cover its functionality more thoroughly. This is just to make sure
-     * its working when accessed via the Open API.
-     */
-    @Test
-    void testOutputUILord() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-        Assert.assertNotNull(outputUILord)
-    }
-
-    /**
-     * This tests that you can correctly obtain the number of output tabs from a
-     * dual pane UI. This
-     */
-    @Test
-    void testDualPaneOutputPaneNumber() {
-        if (java.awt.GraphicsEnvironment.isHeadless()) {
-            return;  // Can't run this test in headless mode!
-        }
-
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-
-        //now create a frame, place the UI in it, then show it briefly
-        JFrame frame = openApi.open(dualPane)
-
-        //make sure we got something
-        Assert.assertNotNull(dualPane)
-
-        //tell it we're about to show it, so it'll create a component
-        dualPane.aboutToShow()
-
-        dualPane.refreshTaskTree()
-
-        openApi.flushEventQueue(frame)
-
-        //there should be one opened output tab for the refresh
-        Assert.assertEquals(1, dualPane.getNumberOfOpenedOutputTabs())
-
-        dualPane.executeCommand("build", "build")
-
-        openApi.flushEventQueue(frame)
-
-        //there should be 2 opened output tabs. One for refresh, one for build
-        Assert.assertEquals(2, dualPane.getNumberOfOpenedOutputTabs())
-    }
-
-    /**
-     * This tests whether or not a the UI is considered busy. Its busy if its
-     * executing a command. To test this, we'll execute a command and verify
-     * we're busy. When it finishes, we'll verify we're not longer busy.
-     * We'll also check that canClose works properly. If we're busy, calling
-     * canClose should prompt the user to confirm closing and return their
-     * answer. If we're not busy, it should not prompt the user and return
-     * true.
-     */
-    @Test
-    void testBusy() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-        dualPane.aboutToShow()
-
-        //add a request observer so we can observe when the command is finished.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
-        gradleInterface.addRequestObserver(testRequestObserver)
-
-        gradleInterface.executeCommand("build", "test command")
-
-        //now that there's a real command in the queue, we should be considered busy
-        Assert.assertTrue(dualPane.isBusy())
-        Assert.assertTrue(gradleInterface.isBusy())
-
-        //we're busy, we shouldn't be able to close
-        TestCloseInteraction testCloseInteraction = new TestCloseInteraction(false)
-        Assert.assertFalse(dualPane.canClose(testCloseInteraction))
-
-        //since we just asked to close and we're busy, make sure we prompted the user
-        Assert.assertTrue(testCloseInteraction.wasPromptedToConfirmClose)
-
-        testRequestObserver.waitForRequestExecutionComplete(120, TimeUnit.SECONDS)
-
-        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith("build"))
-
-        //make sure it completed execution correctly
-        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
-
-        //make sure we're not longer considered busy
-        Assert.assertFalse(dualPane.isBusy())
-        Assert.assertFalse(gradleInterface.isBusy())
-
-        //make sure we can close now
-        testCloseInteraction = new TestCloseInteraction(false)
-        Assert.assertTrue(dualPane.canClose(testCloseInteraction))
-
-        //since we just asked to close and we're NOT busy, make sure we did NOT prompt the user
-        Assert.assertFalse(testCloseInteraction.wasPromptedToConfirmClose)
-
-        gradleInterface.removeRequestObserver(testRequestObserver)
-    }
-
-    /**
-     * This tests that we can set a custom gradle executor.
-     */
-    @Test
-    void testSettingCustomGradleExecutor() {
-        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
-        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-        //it should be null by default
-        Assert.assertNull(gradleInterface.getCustomGradleExecutable())
-
-        //now let's set it to a custom gradle executable. Actually, we're not going to really get
-        //a custom one; we'll use the normal one. Why? Because a real custom one would probably
-        //become a pain for maintaining this test. Here, we're interested that the basics are working
-        //from an open-api standpoint.
-        File gradleExecutor = getCustomGradleExecutable()
-
-        gradleInterface.setCustomGradleExecutable(gradleExecutor)
-
-        //make sure it was set
-        Assert.assertEquals(gradleExecutor, gradleInterface.getCustomGradleExecutable())
-        Assert.assertEquals(gradleExecutor, dualPane.getCustomGradleExecutable()) //just another way to get it
-
-        //add a request observer so we can observe when the command is finished.
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
-        gradleInterface.addRequestObserver(testRequestObserver)
-
-        //this starts the execution queue
-        dualPane.aboutToShow()
-
-        dualPane.refreshTaskTree()
-
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-
-        //make sure it completed execution correctly
-        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
-
-        gradleInterface.removeRequestObserver(testRequestObserver)
-    }
-
-    /**
-     * This gets a gradle executable. That is, a way to launch gradle (shell script or batch file).
-     */
-    private File getCustomGradleExecutable() {
-        //now let's set it to a custom gradle executable. We'll just point it to the regular
-        //gradle file (but it'll be the custom one.
-        String name = OperatingSystem.current().getScriptName("bin/gradle");
-
-        File gradleExecutor = new File(buildContext.getGradleHomeDir(), name)
-
-        //make sure the executable exists
-        Assert.assertTrue("Missing gradle executable at: " + gradleExecutor, gradleExecutor.exists())
-
-        return gradleExecutor
-    }
-}
-
-/**
- * Inner class for tracking a component's visiblity has changed.
- * A HierarchyListener is how Swing notifies you that a component's visibility has changed.
- * We'll use it to track if the component was shown and then hidden.
- */
-private class TestVisibilityHierarchyListener implements HierarchyListener {
-    private boolean componentWasShown = false;
-    private boolean componentWasHidden = false;
-
-    void hierarchyChanged(HierarchyEvent e) {
-        if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
-            if (e.getComponent().isShowing()) {
-                componentWasShown = true;
-            }
-            else {
-                componentWasHidden = true;
-            }
-        }
-    }
-}
-
-/**
- * A class that manages a dummy gradle tab. It just consists of a label,
- *  but tracks that certain fields were called.
- */
-class TestTab implements GradleTabVersion1 {
-    private JLabel label = new JLabel("Testing Testing 123")
-    private TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
-    private boolean nameRetrieved
-    private boolean informedAboutToShow
-    private boolean componentCreated
-
-    def TestTab() {
-        label.addHierarchyListener(hierarchyAdapter)
-    }
-
-    private void reset() {
-        label.removeHierarchyListener(hierarchyAdapter)         //remove the existing listener
-        hierarchyAdapter = new TestVisibilityHierarchyListener()  //create a new one
-        label.addHierarchyListener(hierarchyAdapter)            //add it
-
-        nameRetrieved = false
-        informedAboutToShow = false
-        componentCreated = false
-    }
-
-    String getName() {
-        nameRetrieved = true;
-        return "Test Tab";
-    }
-
-    Component createComponent() {
-        componentCreated = true;
-        return label;
-    }
-
-    void aboutToShow() {
-        informedAboutToShow = true;
-    }
-}
-
-/**
- * Class that tracks whether we were prompted to confirm close. It also returns a specific
- * value to that prompt.
- */
-private class TestCloseInteraction implements BasicGradleUIVersion1.CloseInteraction {
-    boolean wasPromptedToConfirmClose
-    boolean promptResult
-
-
-
-    def TestCloseInteraction(promptResult) {
-        this.promptResult = promptResult;
-    }
-
-    boolean promptUserToConfirmClosingWhileBusy() {
-        wasPromptedToConfirmClose = true
-        return promptResult
-    }
-}
-
-/**
- * This appends a specified string to the command line when executing a command.
- */
-private class TestCommandLineArgumentAlteringListenerVersion1 implements CommandLineArgumentAlteringListenerVersion1 {
-    private final String additionalArguments;
-
-    def TestCommandLineArgumentAlteringListenerVersion1(additionalArguments) {
-        this.additionalArguments = additionalArguments;
-    }
-
-    String getAdditionalCommandLineArguments(String commandLineArguments) {
-        if (commandLineArguments.startsWith("-s ")) {  //we're only interested in altering this one command
-            return additionalArguments;
-        }
-
-        return null;
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy
deleted file mode 100644
index a6d9cf0..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi
-
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.openapi.external.ui.OutputUILordVersion1
-import org.gradle.openapi.external.ui.SinglePaneUIVersion1
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.PreconditionVerifier
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Assert
-import org.junit.ClassRule
-import org.junit.Rule
-import org.junit.Test
-
-import javax.swing.*
-import java.awt.*
-import java.util.concurrent.TimeUnit
-
-import static org.hamcrest.Matchers.startsWith
-
-/**
- * Tests aspects of the OutputUILord in OpenAPI
- */
- at Requires(TestPrecondition.SWING)
-class OutputUILordTest {
-
-    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
-
-    @Rule public OpenApiFixture openApi = new OpenApiFixture(temporaryFolder)
-    @Rule public TestResources resources = new TestResources(temporaryFolder, 'testProject')
-    @ClassRule public static PreconditionVerifier verifier = new PreconditionVerifier()
-
-    /**
-     * This verifies that you can add file extension to the output lord. This is for
-     * highlighting file links in the output. Here, we're just interested in whether
-     * or not the functions work via/exists in the Open API. The actual functionality
-     * is tested elsewhere.
-     */
-    @Test
-    void testAddingFileExtension() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-        outputUILord.addFileExtension('.txt', ':')
-        java.util.List extensions = outputUILord.getFileExtensions()
-        Assert.assertTrue(extensions.contains('.txt'))
-    }
-
-    /**
-     * This verifies that you can add prefixed file extensions to the output lord. This
-     * is for highlighting file links in the output. Here, we're just interested in whether
-     * or not the functions work via/exists in the Open API. The actual functionality is tested elsewhere.
-     */
-    @Test
-    void testAddingPrefixedFileLink() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-        outputUILord.addPrefixedFileLink("Error Text", "The error is:", ".txt", ":")
-    }
-
-    /**
-     * This tests setting the font. There's not much here to do other than set it and then
-     * get it, making sure its the same. This isn't worried so much about the font itself as
-     * much as the open API doesn't have a problem with setting the font.
-     */
-    @Test
-    void testFont() {
-        if (java.awt.GraphicsEnvironment.isHeadless()) {
-            return;  // Can't run this test in headless mode!
-        }
-
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-        Font font = UIManager.getFont("Button.font")  //this specific font is not important
-
-        //make sure that the above font doesn't happen to be the default font for the output lord. If it
-        //is, this test will silently succeed even if it should fail.
-        Assert.assertNotSame("Fonts are the same. This test is not setup correctly.", font, outputUILord.getOutputTextFont())
-
-        //now set the new font and then make sure it worked
-        outputUILord.setOutputTextFont(font)
-    }
-
-    @Test
-    void testReExecute() {
-        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
-        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-        singlePane.aboutToShow()
-
-        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver()
-        singlePane.getGradleInterfaceVersion1().addRequestObserver(testRequestObserver)
-
-        //now execute a command
-        singlePane.executeCommand("build", "test build")
-
-        //wait for it to complete
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-        testRequestObserver.reset()
-
-        //now the single command we're trying to test
-        outputUILord.reExecuteLastCommand();
-
-        //wait again for it exit
-        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
-
-        //make sure it executed the correct request
-        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith('build'))
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java
deleted file mode 100644
index e6ea876..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi;
-
-import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
-
-import java.io.File;
-
-/**
- * Implementation of AlternateUIInteractionVersion1 for testing purposes.
- * This would lend itself well for mocking. 
-  */
-public class TestAlternateUIInteractionVersion1 implements AlternateUIInteractionVersion1 {
-
-    private boolean supportsEditingOpeningFiles;
-
-    public TestAlternateUIInteractionVersion1() {
-    }
-
-    public TestAlternateUIInteractionVersion1(boolean supportsEditingOpeningFiles) {
-        this.supportsEditingOpeningFiles = supportsEditingOpeningFiles;
-    }
-
-    public void openFile(File file, int line) {
-
-    }
-
-    public void editFile(File file, int line) {
-
-    }
-
-    public boolean doesSupportEditingOpeningFiles() {
-        return supportsEditingOpeningFiles;
-    }
-
-    public void aboutToExecuteCommand(String fullCommandLine) {
-
-    }
-}
\ No newline at end of file
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.java
deleted file mode 100644
index 9ba3cca..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.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.integtests.openapi;
-
-import org.gradle.openapi.external.ui.SettingsNodeVersion1;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Implementation of settings node. It basically mirrors a DOM.
- */
-public class TestSettingsNodeVersion1 implements SettingsNodeVersion1 {
-
-    private String name;
-    private String value;
-    private HashMap<String, String> attributes = new HashMap<String, String>();
-    private SettingsNodeVersion1 parent;
-    private List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
-
-    public TestSettingsNodeVersion1() {
-        //this creates a root settings node.
-    }
-
-    public TestSettingsNodeVersion1(SettingsNodeVersion1 parent) {
-        this.parent = parent;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setValue(String value) {
-        this.value = value;
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-    public void setValueOfChild(String name, String value) {
-        SettingsNodeVersion1 settingsNode = addChildIfNotPresent(name);
-        settingsNode.setValue(value);
-    }
-
-    public String getValueOfChild(String name, String defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-            if (value != null) {
-                return value;
-            }
-        }
-        return defaultValue;
-    }
-
-    public SettingsNodeVersion1 getChildNode(String name) {
-        Iterator<SettingsNodeVersion1> iterator = children.iterator();
-        while (iterator.hasNext()) {
-            SettingsNodeVersion1 childNode = iterator.next();
-            if (name.equals(childNode.getName())) {
-                return childNode;
-            }
-        }
-        return null;
-    }
-
-    public List<SettingsNodeVersion1> getChildNodes() {
-        return children;
-    }
-
-    public List<SettingsNodeVersion1> getChildNodes(String name) {
-        List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
-
-        Iterator<SettingsNodeVersion1> iterator = children.iterator();
-        while (iterator.hasNext()) {
-            SettingsNodeVersion1 childNode = iterator.next();
-            if (name.equals(childNode.getName())) {
-                children.add(childNode);
-            }
-        }
-
-        return children;
-    }
-
-    public int getValueOfChildAsInt(String name, int defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            try {
-                if (value != null) {
-                    return Integer.parseInt(value);
-                }
-            } catch (NumberFormatException e) {
-                //we couldn't parse it. Just return the default.
-            }
-        }
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsInt(String name, int value) {
-        setValueOfChild(name, Integer.toString(value));
-    }
-
-    public long getValueOfChildAsLong(String name, long defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            try {
-                if (value != null) {
-                    return Long.parseLong(value);
-                }
-            } catch (NumberFormatException e) {
-                //we couldn't parse it. Just return the default.
-            }
-        }
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsLong(String name, long value) {
-        setValueOfChild(name, Long.toString(value));
-    }
-
-    public boolean getValueOfChildAsBoolean(String name, boolean defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            //I'm not calling 'Boolean.parseBoolean( value )' because it will return false if the value isn't true/false
-            //and we want it to return whatever the default is if its not a boolean.
-            if (value != null) {
-                if ("true".equalsIgnoreCase(value)) {
-                    return true;
-                }
-
-                if ("false".equalsIgnoreCase(value)) {
-                    return false;
-                }
-            }
-        }
-
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsBoolean(String name, boolean value) {
-        setValueOfChild(name, Boolean.toString(value));
-    }
-
-    public SettingsNodeVersion1 addChild(String name) {
-        SettingsNodeVersion1 childNode = new TestSettingsNodeVersion1(this);
-        childNode.setName(name);
-
-        children.add(childNode);
-        return childNode;
-    }
-
-    public SettingsNodeVersion1 addChildIfNotPresent(String name) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode == null) {
-            settingsNode = addChild(name);
-        }
-
-        return settingsNode;
-    }
-
-    public SettingsNodeVersion1 getNodeAtPath(String... pathPortions) {
-        if (pathPortions == null || pathPortions.length == 0) {
-            return null;
-        }
-
-        String firstPathPortion = pathPortions[0];
-
-        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
-
-        int index = 1; //Skip the first one. we've already used that one.
-        while (index < pathPortions.length && currentNode != null) {
-            String pathPortion = pathPortions[index];
-            currentNode = currentNode.getChildNode(pathPortion);
-            index++;
-        }
-
-        return currentNode;
-    }
-
-    private SettingsNodeVersion1 getNodeAtPathCreateIfNotFound(String... pathPortions) {
-        if (pathPortions == null || pathPortions.length == 0) {
-            return null;
-        }
-
-        String firstPathPortion = pathPortions[0];
-
-        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
-        if (currentNode == null) {
-            currentNode = addChild(firstPathPortion);
-        }
-
-        int index = 1;
-        while (index < pathPortions.length) {
-            String pathPortion = pathPortions[index];
-            currentNode = currentNode.getChildNode(pathPortion);
-            if (currentNode == null) {
-                currentNode = addChild(firstPathPortion);
-            }
-
-            index++;
-        }
-
-        return currentNode;
-    }
-
-    public void removeFromParent() {
-        ((TestSettingsNodeVersion1) this.parent).children.remove(this);
-        this.parent = null;
-    }
-
-    public void removeAllChildren() {
-        children.clear();
-    }
-
-    @Override
-    public String toString() {
-        return getName() + "='" + getValue() + "' " + children.size() + " children";
-    }
-}
diff --git a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java b/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java
deleted file mode 100644
index 96e8f82..0000000
--- a/subprojects/open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests.openapi;
-
-import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
-import org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1;
-import org.gradle.openapi.external.ui.SettingsNodeVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
-
-/**
- * This is a test implementation of both SinglePaneUIInteractionVersion1 and DualPaneUIInteractionVersion1.
- * This is really just a container to hand off more complex interactions to the UI when asked for.
- */
-public class TestSingleDualPaneUIInteractionVersion1 implements SinglePaneUIInteractionVersion1, DualPaneUIInteractionVersion1 {
-
-    private AlternateUIInteractionVersion1 alternateUIInteractionVersion1;
-    private SettingsNodeVersion1 settingsNodeVersion1;
-
-    public TestSingleDualPaneUIInteractionVersion1(AlternateUIInteractionVersion1 alternateUIInteractionVersion1, SettingsNodeVersion1 settingsNodeVersion1) {
-        this.alternateUIInteractionVersion1 = alternateUIInteractionVersion1;
-        this.settingsNodeVersion1 = settingsNodeVersion1;
-    }
-
-    public AlternateUIInteractionVersion1 instantiateAlternateUIInteraction() {
-        return alternateUIInteractionVersion1;
-    }
-
-    public SettingsNodeVersion1 instantiateSettings() {
-        return settingsNodeVersion1;
-    }
-}
diff --git a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/build.gradle b/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/build.gradle
deleted file mode 100644
index 357ceb0..0000000
--- a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-allprojects {
-    apply plugin: 'java'
-}
diff --git a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/settings.gradle b/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/settings.gradle
deleted file mode 100644
index 955919e..0000000
--- a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/CrossVersionCompatibilityIntegrationTest/shared/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include 'a', 'b', 'c'
\ No newline at end of file
diff --git a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle b/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle
deleted file mode 100644
index 09c8011..0000000
--- a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle
+++ /dev/null
@@ -1 +0,0 @@
-allprojects { apply plugin: 'java' }
\ No newline at end of file
diff --git a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle b/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle
deleted file mode 100644
index 56b61cd..0000000
--- a/subprojects/open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include 'services:webservice', 'api', 'shared'
\ No newline at end of file
diff --git a/subprojects/open-api/src/main/java/org/gradle/foundation/BootstrapLoader.java b/subprojects/open-api/src/main/java/org/gradle/foundation/BootstrapLoader.java
deleted file mode 100644
index 7f02c5e..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/foundation/BootstrapLoader.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.foundation;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- This handles the work of loading gradle dynamically. Due to jar version issues,
- you can't just load all jar files.
-
- This does NOT require any system or environment variables to be set.
-
- To use this, instantiate this, then call one of the initialize functions.
- Now you can get the class loader to load whatever classes you like.
-
- @deprecated No replacement
-  */
- at Deprecated
-public class BootstrapLoader {
-    private URLClassLoader libClassLoader;
-
-    public void initialize(File gradleHome, boolean bootStrapDebug) throws Exception {
-        initialize(ClassLoader.getSystemClassLoader().getParent(), gradleHome, false, true, bootStrapDebug);
-    }
-
-    /*
-      Call this to initialize gradle.
-      @param  parentClassloader    a parent class loader. Probably whatever class loader
-                                   is used by the caller.
-      @param  gradleHome           the root directory where gradle is installed. This
-                                   directory should have a 'bin' child directory.
-      @param  useParentLastClassLoader true to use a class loader that will delegate
-                                   to the parent only if it can't find it locally. This
-                                   should only be true if you're trying to load gradle
-                                   dynamically from another application.
-      @param  loadOpenAPI          True to load the gradle open API, false not to.
-                                   If you're calling this from a tool using the OpenAPI,
-                                   then you've probably already loaded it, so pass in false
-                                   here, otherwise, pass in true.
-      @param  bootStrapDebug       true to output debug information about the loading
-                                   process.
-      @throws Exception            if something goes wrong.
-   */
-    public void initialize(ClassLoader parentClassloader, File gradleHome, boolean useParentLastClassLoader, boolean loadOpenAPI, boolean bootStrapDebug) throws Exception {
-        if (gradleHome == null || !gradleHome.exists()) {
-            throw new RuntimeException("Gradle home not defined!");
-        }
-
-        if (bootStrapDebug) {
-            System.out.println("Gradle Home is declared by system property gradle.home to: " + gradleHome.getAbsolutePath());
-        }
-
-        System.setProperty("gradle.home", gradleHome.getAbsolutePath());
-
-        List<URL> loggingJars = toUrl(getLoggingJars());
-
-        List<File> nonLoggingJarFiles = getNonLoggingJars();
-        removeUnwantedJarFiles(nonLoggingJarFiles, loadOpenAPI);
-        List<URL> nonLoggingJars = toUrl(nonLoggingJarFiles);
-
-        if (bootStrapDebug) {
-            System.out.println("Parent Classloader of new context classloader is: " + parentClassloader);
-            System.out.println("Adding the following files to new logging classloader: " + loggingJars);
-            System.out.println("Adding the following files to new lib classloader: " + nonLoggingJars);
-        }
-
-        URLClassLoader loggingClassLoader = new URLClassLoader(loggingJars.toArray(new URL[loggingJars.size()]), parentClassloader);
-
-        if (useParentLastClassLoader) {
-            libClassLoader = new ParentLastClassLoader(nonLoggingJars.toArray(new URL[nonLoggingJars.size()]), loggingClassLoader);
-        } else {
-            libClassLoader = new URLClassLoader(nonLoggingJars.toArray(new URL[nonLoggingJars.size()]), loggingClassLoader);
-        }
-
-        if (bootStrapDebug) {
-            System.out.println("Logging class loader: " + loggingClassLoader);
-            System.out.println("Lib class loader: " + libClassLoader);
-        }
-    }
-
-    public static File[] getGradleHomeLibClasspath() {
-        File gradleHomeLib = new File(System.getProperty("gradle.home") + "/lib");
-        if (gradleHomeLib.isDirectory()) {
-            return gradleHomeLib.listFiles();
-        }
-        return new File[0];
-    }
-
-    public static List<File> getNonLoggingJars() {
-        List<File> pathElements = new ArrayList<File>();
-        for (File file : getGradleClasspath()) {
-            if (!isLogLib(file)) {
-                pathElements.add(file);
-            }
-        }
-        return pathElements;
-    }
-
-    public static List<File> getLoggingJars() {
-        List<File> pathElements = new ArrayList<File>();
-        for (File file : getGradleClasspath()) {
-            if (isLogLib(file)) {
-                pathElements.add(file);
-            }
-        }
-        return pathElements;
-    }
-
-    private static boolean isLogLib(File file) {
-        return file.getName().startsWith("logback") || file.getName().startsWith("slf4j");
-    }
-
-    public static List<File> getGradleClasspath() {
-        File customGradleBin = null;
-        List<File> pathElements = new ArrayList<File>();
-        if (System.getProperty("gradle.bootstrap.gradleBin") != null) {
-            customGradleBin = new File(System.getProperty("gradle.bootstrap.gradleBin"));
-            pathElements.add(customGradleBin);
-        }
-        for (File homeLibFile : getGradleHomeLibClasspath()) {
-            if (homeLibFile.isFile() && !(customGradleBin != null && homeLibFile.getName().startsWith("gradle-"))) {
-                pathElements.add(homeLibFile);
-            }
-        }
-        return pathElements;
-    }
-
-    /*
-      This removes unwanted jar files. At the time of this writing, we're only
-      interested in the open api jar.
-
-      @param  nonLoggingJarFiles a list of jar files
-      @param  loadOpenAPI        true to keep the open api jar, false to remove it.
-   */
-    private void removeUnwantedJarFiles(List<File> nonLoggingJarFiles, boolean loadOpenAPI) {
-        if (loadOpenAPI) {
-            return;
-        }
-
-        Iterator<File> iterator = nonLoggingJarFiles.iterator();
-        while (iterator.hasNext()) {
-            File file = iterator.next();
-            if (file.getName().startsWith("gradle-open-api-")) {
-                iterator.remove();
-            }
-        }
-    }
-
-    /*
-      Call this to get the class loader you can use to load gradle classes.
-      @return a URLClassLoader
-   */
-    public URLClassLoader getClassLoader() {
-        return libClassLoader;
-    }
-
-    public Class load(String classPath) throws Exception {
-        return libClassLoader.loadClass(classPath);
-    }
-
-    private static List<URL> toUrl(List<File> files) throws MalformedURLException {
-        List<URL> result = new ArrayList<URL>();
-        for (File file : files) {
-            result.add(file.toURI().toURL());
-        }
-        return result;
-    }
-}
diff --git a/subprojects/open-api/src/main/java/org/gradle/foundation/ParentLastClassLoader.java b/subprojects/open-api/src/main/java/org/gradle/foundation/ParentLastClassLoader.java
deleted file mode 100644
index 0d95db5..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/foundation/ParentLastClassLoader.java
+++ /dev/null
@@ -1,72 +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.foundation;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.net.URLStreamHandlerFactory;
-
-/**
- * <p>This class loader delegates to the parent class loader ONLY if it cannot find it itself. This is meant to solve classloading issues when running something as, say, a plugin inside an application
- * that may have already loaded a different version of some required jars. This makes sure it looks locally first. This is the opposite of a ClassLoader's typical behavior, but it necessary when you
- * can't control the environment in which you're running.
- *
- * <p>Using this class can be very dangerous. You must carefully make sure you understand the ramifications of using this. You should also probably make this the first class loader between your plugin
- * and the plugin's owner.
- * @deprecated No replacement
- */
- at Deprecated
-public class ParentLastClassLoader extends URLClassLoader {
-    public ParentLastClassLoader(URL[] urls, ClassLoader parent) {
-        super(urls, parent);
-    }
-
-    public ParentLastClassLoader(URL[] urls) {
-        super(urls);
-    }
-
-    public ParentLastClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
-        super(urls, parent, factory);
-    }
-
-    /*
-    This has been overridden to look at the parent class loader last.
-    */
-    @Override
-    public Class<?> loadClass(String name) throws ClassNotFoundException {
-        // First check whether it's already been loaded, if so use it
-        Class loadedClass = findLoadedClass(name);
-
-        // Not loaded, try to load it
-        if (loadedClass == null) {
-            try {
-                // Ignore parent delegation and just try to load locally
-                loadedClass = findClass(name);
-            } catch (ClassNotFoundException e) {
-                // Swallow exception - does not exist locally
-            }
-
-            // If not found locally, use normal parent delegation in URLClassloader
-            if (loadedClass == null) {
-                // throws ClassNotFoundException if not found in delegation hierarchy at all
-                loadedClass = super.loadClass(name);
-            }
-        }
-        // will never return null (ClassNotFoundException will be thrown)
-        return loadedClass;
-    }
-}
-
diff --git a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ExternalUtility.java b/subprojects/open-api/src/main/java/org/gradle/openapi/external/ExternalUtility.java
deleted file mode 100644
index b4743ba..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ExternalUtility.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.openapi.external;
-
-import org.gradle.foundation.BootstrapLoader;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.regex.Pattern;
-
-/**
- * Utility functions required by the OpenAPI
- * @deprecated No replacement
- */
- at Deprecated
-public class ExternalUtility {
-    private static final Pattern GRADLE_CORE_PATTERN = Pattern.compile("^gradle-core-\\d.*\\.jar$");
-
-    /**
-     * Call this to get a classloader that has loaded gradle.
-     *
-     * @param parentClassLoader Your classloader. Probably the classloader of whatever class is calling this.
-     * @param gradleHomeDirectory the root directory of a gradle installation
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return a classloader that has loaded gradle and all of its dependencies.
-     */
-
-    public static ClassLoader getGradleClassloader(ClassLoader parentClassLoader, File gradleHomeDirectory, boolean showDebugInfo) throws Exception {
-        File gradleJarFile = getGradleJar(gradleHomeDirectory);
-        if (gradleJarFile == null) {
-            throw new RuntimeException("Not a valid gradle home directory '" + gradleHomeDirectory.getAbsolutePath() + "'");
-        }
-
-        System.setProperty("gradle.home", gradleHomeDirectory.getAbsolutePath());
-
-        BootstrapLoader bootstrapLoader = new BootstrapLoader();
-        bootstrapLoader.initialize(parentClassLoader, gradleHomeDirectory, true, false, showDebugInfo);
-        return bootstrapLoader.getClassLoader();
-    }
-
-    /**
-     * This locates the gradle jar. We do NOT want the gradle-wrapper jar.
-     *
-     * @param gradleHomeDirectory the root directory of a gradle installation. We're expecting this to have a child directory named 'lib'.
-     * @return the gradle jar file. Null if we didn't find it.
-     */
-    public static File getGradleJar(File gradleHomeDirectory) {
-        File libDirectory = new File(gradleHomeDirectory, "lib");
-        if (!libDirectory.exists()) {
-            return null;
-        }
-
-        //try to get the gradle.jar. It'll be "gradle-[version].jar"
-        File[] files = libDirectory.listFiles(new FileFilter() {
-            public boolean accept(File file) {
-                return GRADLE_CORE_PATTERN.matcher(file.getName()).matches();
-            }
-        });
-
-        if (files == null || files.length == 0) {
-            return null;
-        }
-
-        //if they've given us a directory with multiple gradle jars, tell them. We won't know which one to use.
-        if (files.length > 1) {
-            throw new RuntimeException("Installation has multiple gradle jars. Cannot determine which one to use. Found files: " + createFileNamesString(files));
-        }
-
-        return files[0];
-    }
-
-    private static StringBuilder createFileNamesString(File[] files) {
-        StringBuilder fileNames = new StringBuilder();
-        for (File f : files) {
-            fileNames.append(f.getAbsolutePath() + ", ");
-        }
-        fileNames.delete(fileNames.length() - 2, fileNames.length()); // Remove the trailing ', '
-        return fileNames;
-    }
-
-    //just a function to help debugging. If we can't find the constructor we want, this dumps out what is available.
-
-    public static String dumpConstructors(Class classInQuestion) {
-        StringBuilder builder = new StringBuilder();
-        Constructor[] constructors = classInQuestion.getConstructors();
-        for (int index = 0; index < constructors.length; index++) {
-            Constructor constructor = constructors[index];
-            builder.append(constructor).append('\n');
-        }
-
-        return builder.toString();
-    }
-
-    public static String dumpMethods(Class classInQuestion) {
-        StringBuilder builder = new StringBuilder();
-
-        Method[] methods = classInQuestion.getMethods();
-        for (int index = 0; index < methods.length; index++) {
-            Method method = methods[index];
-            builder.append(method).append('\n');
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * This attempts to load the a class from the specified gradle home directory.
-     *
-     * @param classToLoad the full path to the class to load
-     * @param parentClassLoader Your classloader. Probably the classloader of whatever class is calling this.
-     * @param gradleHomeDirectory the root directory of a gradle installation
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     */
-    public static Class loadGradleClass(String classToLoad, ClassLoader parentClassLoader, File gradleHomeDirectory, boolean showDebugInfo) throws Exception {
-        ClassLoader bootStrapClassLoader = getGradleClassloader(parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        Thread.currentThread().setContextClassLoader(bootStrapClassLoader);
-
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        try {
-            return bootStrapClassLoader.loadClass(classToLoad);
-        } catch (NoClassDefFoundError e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        } catch (Throwable e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        }
-    }
-
-    /**
-     * This wraps up invoking a static method into a single call.
-     *
-     * @param classToInvoke the class that has the method
-     * @param methodName the name of the method to invoke
-     * @param argumentsClasses the classes of the arguments (we can't determine this from the argumentValues because they can be of class A, but implement class B and B is be the argument type of the
-     * method in question
-     * @param argumentValues the values of the arguments.
-     * @return the return value of invoking the method.
-     */
-    public static Object invokeStaticMethod(Class classToInvoke, String methodName, Class[] argumentsClasses, Object... argumentValues) throws Exception {
-        Method method = null;
-        try {
-            method = classToInvoke.getDeclaredMethod(methodName, argumentsClasses);
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-            System.out.println("Dumping available methods on " + classToInvoke.getName() + "\n" + ExternalUtility.dumpMethods(classToInvoke));
-            throw e;
-        }
-        return method.invoke(null, argumentValues);
-    }
-}
diff --git a/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerFactory.java b/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerFactory.java
deleted file mode 100644
index 02bad4e..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerFactory.java
+++ /dev/null
@@ -1,133 +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.openapi.external.runner;
-
-import org.gradle.openapi.external.ExternalUtility;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-
-/**
- * This provides a simple way to execute gradle commands from an external process. call createGradleRunner to instantiate a gradle runner. You can then use that to execute commands.
- * @deprecated Use the tooling API instead.
- */
- at Deprecated
-public class GradleRunnerFactory {
-    /**
-     * Call this to instantiate an object that you can use to execute gradle commands directly.
-     *
-     * Note: this function is meant to be backward and forward compatible. So this signature should not change at all, however, it may take and return objects that implement ADDITIONAL interfaces. That
-     * is, it will always return a GradleRunnerVersion1, but it may also be an object that implements GradleRunnerVersion2 (notice the 2). The caller will need to dynamically determine that. The
-     * GradleRunnerInteractionVersion1 may take an object that also implements GradleRunnerInteractionVersion2. If so, we'll dynamically determine that and handle it. Of course, this all depends on
-     * what happens in the future.
-     *
-     * @param parentClassLoader Your classloader. Probably the classloader of whatever class is calling this.
-     * @param gradleHomeDirectory the root directory of a gradle installation
-     * @param interaction this is how we interact with the caller.
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return a gradle runner
-     * @deprecated Use the tooling API instead.
-     */
-    @Deprecated
-    public static GradleRunnerVersion1 createGradleRunner(ClassLoader parentClassLoader, File gradleHomeDirectory, GradleRunnerInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //much of this function is exception handling so if we can't obtain it via the newer factory method, then
-        //we'll try the old way, but we want to report the original exception if we can't do it either way.
-        Exception viaFactoryException = null;
-        GradleRunnerVersion1 gradleRunner = null;
-
-        //first, try it the new way
-        try {
-            gradleRunner = createGradleRunnerViaFactory(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        } catch (Exception e) {
-            //we might ignore this. It means we're probably using an older version of gradle. That case is handled below.
-            //If not, this exception will be thrown at the end.
-            viaFactoryException = e;
-        }
-
-        //try it the old way
-        if (gradleRunner == null) {
-            gradleRunner = createGradleRunnerOldWay(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        }
-
-        //if we still don't have a gradle runner and we have an exception from using the factory, throw it. If we
-        //got an exception using the 'old way', it would have been thrown already and we wouldn't be here.
-        if (gradleRunner == null && viaFactoryException != null) {
-            throw viaFactoryException;
-        }
-
-        return gradleRunner;
-    }
-
-    /**
-     * This function uses a factory to instantiate a GradleRunner. The factory is located with the version of gradle pointed to by gradleHomeDirectory and thus allows the version of gradle being loaded
-     * to make decisions about how to instantiate the runner. This is needed as multiple versions of the runner are being used.
-     */
-    private static GradleRunnerVersion1 createGradleRunnerViaFactory(ClassLoader parentClassLoader, File gradleHomeDirectory, GradleRunnerInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = ExternalUtility.loadGradleClass("org.gradle.openapi.wrappers.RunnerWrapperFactory", parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        if (soughtClass == null) {
-            return null;
-        }
-
-        Class[] argumentClasses = new Class[]{File.class, GradleRunnerInteractionVersion1.class, boolean.class};
-
-        Object gradleRunner = ExternalUtility.invokeStaticMethod(soughtClass, "createGradleRunner", argumentClasses, gradleHomeDirectory, interaction, showDebugInfo);
-        return (GradleRunnerVersion1) gradleRunner;
-    }
-
-    /**
-     * This function uses an early way (early 0.9 pre-release and sooner) of instantiating the GradleRunner and should no longer be used. It unfortunately is tied to a single wrapper class instance
-     * (which it tries to directly instantiate). This doesn't allow the GradleRunner to adaptively determine what to instantiate.
-     */
-    private static GradleRunnerVersion1 createGradleRunnerOldWay(ClassLoader parentClassLoader, File gradleHomeDirectory, GradleRunnerInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        ClassLoader bootStrapClassLoader = ExternalUtility.getGradleClassloader(parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        Thread.currentThread().setContextClassLoader(bootStrapClassLoader);
-
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = null;
-        try {
-            soughtClass = bootStrapClassLoader.loadClass("org.gradle.openapi.wrappers.runner.GradleRunnerWrapper");
-        } catch (NoClassDefFoundError e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        } catch (ClassNotFoundException e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        }
-
-        if (soughtClass == null) {
-            return null;
-        }
-
-        //instantiate it.
-        Constructor constructor = null;
-        try {
-            constructor = soughtClass.getDeclaredConstructor(File.class, GradleRunnerInteractionVersion1.class);
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-            System.out.println("Dumping available constructors on " + soughtClass.getName() + "\n" + ExternalUtility.dumpConstructors(soughtClass));
-
-            throw e;
-        }
-
-        Object gradleRunner = constructor.newInstance(gradleHomeDirectory, interaction);
-
-        return (GradleRunnerVersion1) gradleRunner;
-    }
-}
diff --git a/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerVersion1.java b/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerVersion1.java
deleted file mode 100644
index 3397af8..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/openapi/external/runner/GradleRunnerVersion1.java
+++ /dev/null
@@ -1,35 +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.openapi.external.runner;
-
-/**
- * This executes gradle commands in an external process.
- * @deprecated Use the tooling API instead
- */
- at Deprecated
-public interface GradleRunnerVersion1 {
-    /**
-     * Call this to execute the specified command line.
-     *
-     * @param commandLine the command to execute
-     */
-    public void executeCommand(String commandLine);
-
-    /**
-     * Call this to stop the gradle command. This is killing the process, not gracefully exiting.
-     */
-    public void killProcess();
-}
diff --git a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/DualPaneUIVersion1.java b/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/DualPaneUIVersion1.java
deleted file mode 100644
index dec3f66..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/DualPaneUIVersion1.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.openapi.external.ui;
-
-import javax.swing.JComponent;
-import java.awt.Component;
-
-/**
-This is a gradle UI that is broken into two panels: one contains a tabbed pane
- of tasks, favorites, command line, etc. The other pane contains the output.
- This is meant to simplify how an IDE plugin can interact with gradle. Specifically,
- this allows the 'main' pane to be vertical and the output pane to be horizontal.
-
- To use this, you'll want to get an instance of this from Gradle. Then setup
- your UI and add this to it via getComponent. Then call aboutToShow before
- you display your UI. Call close before you hide your UI. You'll need to set
- the current directory (at any time) so gradle knows where your project is
- located.
-
- @deprecated No replacement
- */
- at Deprecated
-public interface DualPaneUIVersion1 extends BasicGradleUIVersion1 {
-   /**
-      Returns a component that shows the task tree tab, favorites tab, etc.
-      suitable for inserting in your UI.
-      @return the main component
-    */
-   public JComponent getMainComponent();
-
-   /**
-      Returns a component that shows the output of the tasks being executed.
-      This is suitable for inserting in your UI.
-      @return the output component
-   */
-   public Component getOutputPanel();
-
-    /**
-     * This gets the number of opened output tabs. This is used by the Idea plugin
-     * to determine if it should close the entire output pane when a tab is closed
-     * This doesn't determine whether or not the tabs are busy. See
-     * GradleInterfaceVersion1.isBusy for that 
-     * @return the number of opened output tabs.
-     */
-   public int getNumberOfOpenedOutputTabs();
-}
diff --git a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/UIFactory.java b/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/UIFactory.java
deleted file mode 100644
index 8b80bf4..0000000
--- a/subprojects/open-api/src/main/java/org/gradle/openapi/external/ui/UIFactory.java
+++ /dev/null
@@ -1,238 +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.openapi.external.ui;
-
-import org.gradle.openapi.external.ExternalUtility;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-
-/**
- * This loads up the main gradle UI. This is intended to be used as a plugin inside another application (like an IDE) in a dynamic fashion. If you're always going to ship the entire plugin with the
- * entire Gradle dist, you don't need to use this. This is meant to dynamically load Gradle from its dist. The idea is that you point your plugin to a Gradle dist and then can always load the latest
- * version.
- * @deprecated No replacement
- */
- at Deprecated
-public class UIFactory {
-    private static final String UIWRAPPER_FACTORY_CLASS_NAME = "org.gradle.openapi.wrappers.UIWrapperFactory";
-
-    /**
-     * Call this to instantiate a self-contained gradle UI. That is, everything in the UI is in a single panel (versus 2 panels one for the tasks and one for the output). This will load gradle via
-     * reflection, instantiate the UI and all required gradle-related classes.
-     *
-     * <p>Note: this function is meant to be backward and forward compatible. So this signature should not change at all, however, it may take and return objects that implement ADDITIONAL interfaces.
-     * That is, it will always return SinglePaneUIVersion1, but it may also be an object that implements SinglePaneUIVersion2 (notice the 2). The caller will need to dynamically determine that. The
-     * SinglePaneUIInteractionVersion1 may take an object that also implements SinglePaneUIInteractionVersion2. If so, we'll dynamically determine that and handle it. Of course, this all depends on
-     * what happens in the future.
-     *
-     * @param parentClassLoader Your classloader. Probably the classloader of whatever class is calling this.
-     * @param gradleHomeDirectory the root directory of a gradle installation
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return the UI object.
-     * @deprecated Use the tooling API instead.
-     */
-    @Deprecated
-    public static SinglePaneUIVersion1 createSinglePaneUI(ClassLoader parentClassLoader, File gradleHomeDirectory, final SinglePaneUIInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //much of this function is exception handling so if we can't obtain it via the newer factory method, then
-        //we'll try the old way, but we want to report the original exception if we can't do it either way.
-        Exception viaFactoryException = null;
-        SinglePaneUIVersion1 gradleUI = null;
-
-        //first, try it the new way
-        try {
-            gradleUI = createSinglePaneUIViaFactory(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        } catch (Exception e) {
-            //we might ignore this. It means we're probably using an older version of gradle. That case is handled below.
-            //If not, this exception will be thrown at the end.
-            viaFactoryException = e;
-        }
-
-        //try it the old way
-        if (gradleUI == null) {
-            gradleUI = createSinglePaneUIOldWay(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        }
-
-        //if we still don't have a gradle ui and we have an exception from using the factory, throw it. If we
-        //got an exception using the 'old way', it would have been thrown already and we wouldn't be here.
-        if (gradleUI == null && viaFactoryException != null) {
-            throw viaFactoryException;
-        }
-
-        return gradleUI;
-    }
-
-    /**
-     * This function uses a factory to instantiate the UI. The factory is located with the version of gradle pointed to by gradleHomeDirectory and thus allows the version of gradle being loaded to make
-     * decisions about how to instantiate the UI. This is needed as multiple versions of the UI are being used.
-     */
-    private static SinglePaneUIVersion1 createSinglePaneUIViaFactory(ClassLoader parentClassLoader, File gradleHomeDirectory, final SinglePaneUIInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = ExternalUtility.loadGradleClass(UIWRAPPER_FACTORY_CLASS_NAME, parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        if (soughtClass == null) {
-            return null;
-        }
-
-        Class[] argumentClasses = new Class[]{SinglePaneUIInteractionVersion1.class, boolean.class};
-
-        Object gradleUI = ExternalUtility.invokeStaticMethod(soughtClass, "createSinglePaneUI", argumentClasses, interaction, showDebugInfo);
-        return (SinglePaneUIVersion1) gradleUI;
-    }
-
-    /**
-     * This function uses an early way (early 0.9 pre-release and sooner) of instantiating the UI and should no longer be used. It unfortunately is tied to a single wrapper class instance (which it
-     * tries to directly instantiate). This doesn't allow the Gradle UI to adaptively determine what to instantiate.
-     */
-    private static SinglePaneUIVersion1 createSinglePaneUIOldWay(ClassLoader parentClassLoader, File gradleHomeDirectory, final SinglePaneUIInteractionVersion1 singlePaneUIArguments,
-                                                                 boolean showDebugInfo) throws Exception {
-        ClassLoader bootStrapClassLoader = ExternalUtility.getGradleClassloader(parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        Thread.currentThread().setContextClassLoader(bootStrapClassLoader);
-
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = null;
-        try {
-            soughtClass = bootStrapClassLoader.loadClass("org.gradle.openapi.wrappers.ui.SinglePaneUIWrapper");
-        } catch (NoClassDefFoundError e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        } catch (ClassNotFoundException e) {  //might be a version mismatch
-            e.printStackTrace();
-        }
-        if (soughtClass == null) {
-            return null;
-        }
-
-        //instantiate it.
-        Constructor constructor = null;
-        try {
-            constructor = soughtClass.getDeclaredConstructor(SinglePaneUIInteractionVersion1.class);
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-            System.out.println("Dumping available constructors on " + soughtClass.getName() + "\n" + ExternalUtility.dumpConstructors(soughtClass));
-
-            throw e;
-        }
-        Object singlePaneUI = constructor.newInstance(singlePaneUIArguments);
-        return (SinglePaneUIVersion1) singlePaneUI;
-    }
-
-    /**
-     * Call this to instantiate a gradle UI that contains the main tab control separate from the output panel. This allows you to position the output however you like. For example: you can place the
-     * main pane along the side going vertically and you can place the output pane along the bottom going horizontally. This will load gradle via reflection, instantiate the UI and all required
-     * gradle-related classes.
-     *
-     * <p>Note: this function is meant to be backward and forward compatible. So this signature should not change at all, however, it may take and return objects that implement ADDITIONAL interfaces.
-     * That is, it will always return SinglePaneUIVersion1, but it may also be an object that implements SinglePaneUIVersion2 (notice the 2). The caller will need to dynamically determine that. The
-     * SinglePaneUIInteractionVersion1 may take an object that also implements SinglePaneUIInteractionVersion2. If so, we'll dynamically determine that and handle it. Of course, this all depends on
-     * what happens in the future.
-     *
-     * @param parentClassLoader Your classloader. Probably the classloader of whatever class is calling this.
-     * @param gradleHomeDirectory the root directory of a gradle installation
-     * @param interaction this is how we interact with the caller.
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return the UI object.
-     * @deprecated Use the tooling API instead.
-     */
-    @Deprecated
-    public static DualPaneUIVersion1 createDualPaneUI(ClassLoader parentClassLoader, File gradleHomeDirectory, final DualPaneUIInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //much of this function is exception handling so if we can't obtain it via the newer factory method, then
-        //we'll try the old way, but we want to report the original exception if we can't do it either way.
-        Exception viaFactoryException = null;
-        DualPaneUIVersion1 gradleUI = null;
-
-        //first, try it the new way
-        try {
-            gradleUI = createDualPaneUIViaFactory(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        } catch (Exception e) {
-            //we might ignore this. It means we're probably using an older version of gradle. That case is handled below.
-            //If not, this exception will be thrown at the end.
-            viaFactoryException = e;
-        }
-
-        //try it the old way
-        if (gradleUI == null) {
-            gradleUI = createDualPaneUIOldWay(parentClassLoader, gradleHomeDirectory, interaction, showDebugInfo);
-        }
-
-        //if we still don't have a gradle ui and we have an exception from using the factory, throw it. If we
-        //got an exception using the 'old way', it would have been thrown already and we wouldn't be here.
-        if (gradleUI == null && viaFactoryException != null) {
-            throw viaFactoryException;
-        }
-
-        return gradleUI;
-    }
-
-    /**
-     * This function uses a factory to instantiate the UI. The factory is located with the version of gradle pointed to by gradleHomeDirectory and thus allows the version of gradle being loaded to make
-     * decisions about how to instantiate the UI. This is needed as multiple versions of the UI are being used.
-     * @deprecated Use the tooling API instead.
-     */
-    @Deprecated
-    public static DualPaneUIVersion1 createDualPaneUIViaFactory(ClassLoader parentClassLoader, File gradleHomeDirectory, final DualPaneUIInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = ExternalUtility.loadGradleClass(UIWRAPPER_FACTORY_CLASS_NAME, parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        if (soughtClass == null) {
-            return null;
-        }
-
-        Class[] argumentClasses = new Class[]{DualPaneUIInteractionVersion1.class, boolean.class};
-
-        Object gradleUI = ExternalUtility.invokeStaticMethod(soughtClass, "createDualPaneUI", argumentClasses, interaction, showDebugInfo);
-        return (DualPaneUIVersion1) gradleUI;
-    }
-
-    /**
-     * This function uses an early way (early 0.9 pre-release and sooner) of instantiating the UI and should no longer be used. It unfortunately is tied to a single wrapper class instance (which it
-     * tries to directly instantiate). This doesn't allow the Gradle UI to adaptively determine what to instantiate.
-     */
-    private static DualPaneUIVersion1 createDualPaneUIOldWay(ClassLoader parentClassLoader, File gradleHomeDirectory, final DualPaneUIInteractionVersion1 interaction, boolean showDebugInfo)
-            throws Exception {
-        ClassLoader bootStrapClassLoader = ExternalUtility.getGradleClassloader(parentClassLoader, gradleHomeDirectory, showDebugInfo);
-        Thread.currentThread().setContextClassLoader(bootStrapClassLoader);
-
-        //load the class in gradle that wraps our return interface and handles versioning issues.
-        Class soughtClass = null;
-        try {
-            soughtClass = bootStrapClassLoader.loadClass("org.gradle.openapi.wrappers.ui.DualPaneUIWrapper");
-        } catch (NoClassDefFoundError e) {  //might be a version mismatch
-            e.printStackTrace();
-            return null;
-        } catch (ClassNotFoundException e) {  //might be a version mismatch
-            e.printStackTrace();
-        }
-        if (soughtClass == null) {
-            return null;
-        }
-
-        //instantiate it.
-        Constructor constructor = null;
-        try {
-            constructor = soughtClass.getDeclaredConstructor(DualPaneUIInteractionVersion1.class);
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-            System.out.println("Dumping available constructors on " + soughtClass.getName() + "\n" + ExternalUtility.dumpConstructors(soughtClass));
-
-            throw e;
-        }
-        Object gradleUI = constructor.newInstance(interaction);
-        return (DualPaneUIVersion1) gradleUI;
-    }
-}
diff --git a/subprojects/open-api/src/test/groovy/org/gradle/openapi/external/ExternalUtilityTest.groovy b/subprojects/open-api/src/test/groovy/org/gradle/openapi/external/ExternalUtilityTest.groovy
deleted file mode 100644
index b47d9a9..0000000
--- a/subprojects/open-api/src/test/groovy/org/gradle/openapi/external/ExternalUtilityTest.groovy
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.openapi.external
-
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-public class ExternalUtilityTest extends Specification {
-    @Rule
-    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
-
-    def getTheRightGradleCore() {
-        TestFile libDir = tmpDir.testDirectory.file('lib')
-
-        when:
-        libDir.deleteDir()
-        libDir.createDir().files('gradle-core-0.9.jar', 'gradle-core-worker-0.9.jar', 'gradle-open-api-0.9.jar')*.touch()
-
-        then:
-        ExternalUtility.getGradleJar(tmpDir.testDirectory).absolutePath == new File(libDir, 'gradle-core-0.9.jar').absolutePath
-
-        when:
-        libDir.deleteDir()
-        libDir.createDir().files('gradle-core-0.9-20100315080959+0100.jar', 'gradle-core-worker-0.9-20100315080959+0100.jar', 'gradle-open-api-0.9-20100315080959+0100.jar')*.touch()
-
-        then:
-        ExternalUtility.getGradleJar(tmpDir.testDirectory).absolutePath == new File(libDir, 'gradle-core-0.9-20100315080959+0100.jar').absolutePath
-    }
-
-    def failWithMultipleGradleCore() {
-        tmpDir.testDirectory.file('lib').createDir().files('gradle-core-0.9.jar', 'gradle-core-0.10.jar', 'gradle-open-api-0.9.jar')*.touch()
-
-        when:
-        ExternalUtility.getGradleJar(tmpDir.testDirectory)
-
-        then:
-        RuntimeException e = thrown()
-        println e.message
-        e.message.contains('gradle-core-0.9.jar')
-        e.message.contains('gradle-core-0.10.jar')
-    }
-
-    def returnNullWitNonExistingGradleCore() {
-        tmpDir.testDirectory.file('lib').createDir().files('gradle-open-api-0.9.jar')*.touch()
-
-        expect:
-        ExternalUtility.getGradleJar(tmpDir.testDirectory) == null
-    }
-
-    def failWitNonExistingGradleHome() {
-        expect:
-        ExternalUtility.getGradleJar(tmpDir.testDirectory) == null
-    }
-
-}
diff --git a/subprojects/osgi/src/integTest/groovy/org/gradle/api/plugins/osgi/OsgiPluginIntegrationSpec.groovy b/subprojects/osgi/src/integTest/groovy/org/gradle/api/plugins/osgi/OsgiPluginIntegrationSpec.groovy
index 9ec248f..2aa2613 100644
--- a/subprojects/osgi/src/integTest/groovy/org/gradle/api/plugins/osgi/OsgiPluginIntegrationSpec.groovy
+++ b/subprojects/osgi/src/integTest/groovy/org/gradle/api/plugins/osgi/OsgiPluginIntegrationSpec.groovy
@@ -17,11 +17,13 @@
 package org.gradle.api.plugins.osgi
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class OsgiPluginIntegrationSpec extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2237")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2237")
     def "can set modelled manifest properties with instruction"() {
         given:
         buildFile << """
@@ -53,7 +55,8 @@ class OsgiPluginIntegrationSpec extends AbstractIntegrationSpec {
         manifestText.contains("Bundle-SymbolicName: bar")
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2237")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2237")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "jar task remains incremental"() {
         given:
         // Unsure why, but this problem doesn't show if we don't wait a little bit
diff --git a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
index 53bb103..c2511f2 100644
--- a/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
+++ b/subprojects/osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiPlugin.groovy
@@ -26,14 +26,13 @@ import org.gradle.api.plugins.JavaPlugin
  */
 public class OsgiPlugin implements Plugin<Project> {
     public void apply(Project project) {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
 
         def osgiConvention = new OsgiPluginConvention(project)
         project.convention.plugins.osgi = osgiConvention
 
         project.plugins.withType(JavaPlugin) {
             def osgiManifest = osgiConvention.osgiManifest {
-                from project.manifest
                 classesDir = project.sourceSets.main.output.classesDir
                 classpath = project.configurations.runtime
             }
diff --git a/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties b/subprojects/osgi/src/main/resources/META-INF/gradle-plugins/org.gradle.osgi.properties
similarity index 100%
rename from subprojects/osgi/src/main/resources/META-INF/gradle-plugins/osgi.properties
rename to subprojects/osgi/src/main/resources/META-INF/gradle-plugins/org.gradle.osgi.properties
diff --git a/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginConventionTest.groovy b/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginConventionTest.groovy
index 727f7f8..fb897de 100644
--- a/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginConventionTest.groovy
+++ b/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginConventionTest.groovy
@@ -28,7 +28,7 @@ class OsgiPluginConventionTest extends Specification {
     OsgiPluginConvention osgiPluginConvention = new OsgiPluginConvention(project)
 
     def setup() {
-        project.plugins.apply(JavaBasePlugin)
+        project.pluginManager.apply(JavaBasePlugin)
     }
 
     def osgiManifestWithNoClosure() {
diff --git a/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginTest.groovy b/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginTest.groovy
index 4cd64a5..d06a940 100644
--- a/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginTest.groovy
+++ b/subprojects/osgi/src/test/groovy/org/gradle/api/plugins/osgi/OsgiPluginTest.groovy
@@ -40,7 +40,6 @@ public class OsgiPluginTest extends Specification {
         
         expect:
         OsgiManifest osgiManifest = project.jar.manifest
-        osgiManifest.mergeSpecs[0].mergePaths[0] == project.manifest
         osgiManifest.classpath == project.configurations."$JavaPlugin.RUNTIME_CONFIGURATION_NAME"
         osgiManifest.classesDir == project.sourceSets."$SourceSet.MAIN_SOURCE_SET_NAME".output.classesDir
     }
diff --git a/subprojects/performance/performance.gradle b/subprojects/performance/performance.gradle
index 1945006..53f0f93 100644
--- a/subprojects/performance/performance.gradle
+++ b/subprojects/performance/performance.gradle
@@ -11,7 +11,7 @@ repositories {
 }
 
 dependencies {
-    junit 'junit:junit:4.11'
+    junit 'junit:junit:4.12'
     reports "jquery:jquery.min:1.11.0 at js"
     reports "flot:flot:0.8.1:min at js"
 
@@ -111,6 +111,17 @@ task lotDependencies(type: ProjectGeneratorTask, description: 'Generates a small
     }
 }
 
+task lotProjectDependencies(type: ProjectGeneratorTask, description: 'Generates a large multi-project build with a large Dependency Graph and plenty of project dependencies') {
+    projects = 100
+    sourceFiles = 1
+    templateArgs = [resolveDependenciesTask: true, projectDependencies: true]
+
+    dependencyGraph {
+        size = 400
+        depth = 6
+    }
+}
+
 task manyProjects(type: ProjectGeneratorTask) {
     projects = 100
     sourceFiles = 0
@@ -121,9 +132,228 @@ def generators = tasks.withType(ProjectGeneratorTask) {
     testDependencies = configurations.junit
 }
 
+task smallOldJava(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['java-source', 'old-java-plugin']
+    testDependencies = files()
+}
+
+task mediumOldJava(type: ProjectGeneratorTask) {
+    projects = 25
+    subProjectTemplates = ['java-source', 'old-java-plugin']
+    testDependencies = files()
+}
+
+task bigOldJava(type: ProjectGeneratorTask) {
+    projects = 500
+    subProjectTemplates = ['java-source', 'old-java-plugin']
+    testDependencies = files()
+}
+
+task smallNewJava(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['java-source', 'new-java-plugin']
+    testDependencies = files()
+}
+
+task mediumNewJava(type: ProjectGeneratorTask) {
+    projects = 25
+    subProjectTemplates = ['java-source', 'new-java-plugin']
+    testDependencies = files()
+}
+
+task bigNewJava(type: ProjectGeneratorTask) {
+    projects = 500
+    subProjectTemplates = ['java-source', 'new-java-plugin']
+    testDependencies = files()
+}
+
+task smallVariantsNewModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-new-model']
+    templateArgs = [
+            flavourCount: 1,
+            typeCount: 1
+    ]
+    testDependencies = files()
+}
+
+task mediumVariantsNewModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-new-model']
+    templateArgs = [
+            flavourCount: 5,
+            typeCount: 5
+    ]
+    testDependencies = files()
+}
+
+task bigVariantsNewModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-new-model']
+    templateArgs = [
+            flavourCount: 23,
+            typeCount: 23
+    ]
+    testDependencies = files()
+}
+
+task smallVariantsOldModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-old-model']
+    templateArgs = [
+            flavourCount: 1,
+            typeCount: 1
+    ]
+    testDependencies = files()
+}
+
+task mediumVariantsOldModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-old-model']
+    templateArgs = [
+            flavourCount: 5,
+            typeCount: 5
+    ]
+    testDependencies = files()
+}
+
+task bigVariantsOldModel(type: ProjectGeneratorTask) {
+    projects = 1
+    subProjectTemplates = ['variants-old-model']
+    templateArgs = [
+            flavourCount: 23,
+            typeCount: 23
+    ]
+    testDependencies = files()
+}
+
+task variantsNewModelMultiproject(type: ProjectGeneratorTask) {
+    projects = 10
+    subProjectTemplates = ['variants-new-model']
+    templateArgs = [
+            flavourCount: 23,
+            typeCount: 23
+    ]
+    testDependencies = files()
+}
+
+task variantsOldModelMultiproject(type: ProjectGeneratorTask) {
+    projects = 10
+    subProjectTemplates = ['variants-old-model']
+    templateArgs = [
+            flavourCount: 23,
+            typeCount: 23
+    ]
+    testDependencies = files()
+}
+
+task bigEmpty(type: ProjectGeneratorTask) {
+    projects = 10000
+    subProjectTemplates = ['empty']
+}
+
+task smallNative(type: ProjectGeneratorTask) {
+    projects = 1
+    sourceFiles = 20
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 1
+    ]
+    subProjectTemplates = ['native-source', 'native-component']
+}
+
+task mediumNative(type: ProjectGeneratorTask) {
+    projects = 1
+    sourceFiles = 100
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 20
+    ]
+    subProjectTemplates = ['native-source', 'native-component']
+}
+
+task bigNative(type: ProjectGeneratorTask) {
+    projects = 1
+    sourceFiles = 500
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 50
+    ]
+    subProjectTemplates = ['native-source', 'native-component']
+}
+
+task multiNative(type: ProjectGeneratorTask) {
+    projects = 10
+    sourceFiles = 20
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 10,
+            functionCount: 20
+    ]
+    subProjectTemplates = ['native-source', 'native-component']
+}
+
+task manyProjectsNative(type: ProjectGeneratorTask) {
+    projects = 500
+    sourceFiles = 1
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 3,
+            functionCount: 1
+    ]
+    subProjectTemplates = ['native-source', 'native-component']
+}
+
+task smallScenarioNative(type: ProjectGeneratorTask) {
+    projects = 1
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 1
+    ]
+    subProjectTemplates = ['native-source', 'native-scenario']
+}
+
+task mediumScenarioNative(type: ProjectGeneratorTask) {
+    projects = 25
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 20
+    ]
+    subProjectTemplates = ['native-source', 'native-scenario']
+}
+
+task bigScenarioNative(type: ProjectGeneratorTask) {
+    projects = 500
+    nativeProject = true
+    templateArgs = [
+            moduleCount: 1,
+            functionCount: 50
+    ]
+    subProjectTemplates = ['native-source', 'native-scenario']
+}
+
+task bigOldJavaMoreSource(type: ProjectGeneratorTask) {
+    projects = 500
+    sourceFiles = 10
+    linesOfCodePerSourceFile = 20
+    subProjectTemplates = ['java-source', 'old-java-plugin']
+    testDependencies = files()
+}
+
 task all(dependsOn: generators)
 
-task prepareSamples(dependsOn: [small, multi, lotDependencies, withJUnit, withTestNG, withVerboseTestNG, withVerboseJUnit, manyProjects])
+task prepareSamples(dependsOn: [bigEmpty, small, multi, lotDependencies, withJUnit, withTestNG, withVerboseTestNG, withVerboseJUnit, manyProjects,
+                                smallOldJava, mediumOldJava, bigOldJava, smallNewJava, mediumNewJava, bigNewJava,
+                                smallVariantsNewModel, mediumVariantsNewModel, bigVariantsNewModel, smallVariantsOldModel, mediumVariantsOldModel, bigVariantsOldModel,
+                                variantsNewModelMultiproject, variantsOldModelMultiproject, smallNative, mediumNative, bigNative, multiNative,
+                                smallScenarioNative, mediumScenarioNative, bigScenarioNative, manyProjectsNative,
+                                bigOldJavaMoreSource, lotProjectDependencies])
 
 task report {
     def reportDir = new File(buildDir, "performance-tests/report")
@@ -132,7 +362,7 @@ task report {
 
     doLast {
         def cl = new URLClassLoader(sourceSets.testFixtures.runtimeClasspath.collect { it.toURI().toURL()} as URL[], ClassLoader.systemClassLoader.parent)
-        def store = cl.loadClass("org.gradle.performance.results.ResultsStore").newInstance()
+        def store = cl.loadClass("org.gradle.performance.results.AllResultsStore").newInstance()
         try {
             def generator = cl.loadClass("org.gradle.performance.results.ReportGenerator").newInstance()
             generator.generate(store, reportDir)
@@ -142,8 +372,26 @@ task report {
     }
 }
 
-integTest {
+task performanceTest(type: Test) {
     dependsOn prepareSamples
     finalizedBy report
     maxParallelForks = 1
+    options {
+        includeCategories 'org.gradle.performance.PerformanceTest'
+    }
+}
+
+task performanceExperiments(type: Test) {
+    dependsOn prepareSamples
+    finalizedBy report
+    maxParallelForks = 1
+    options {
+        includeCategories 'org.gradle.performance.Experiment'
+    }
+}
+
+integTest {
+    options {
+        excludeCategories 'org.gradle.performance.PerformanceTest'
+    }
 }
diff --git a/subprojects/performance/src/configPlugin/ConfigPlugin.java b/subprojects/performance/src/configPlugin/ConfigPlugin.java
index c04c745..5b7e037 100644
--- a/subprojects/performance/src/configPlugin/ConfigPlugin.java
+++ b/subprojects/performance/src/configPlugin/ConfigPlugin.java
@@ -18,9 +18,9 @@ public class ConfigPlugin implements Plugin<Project> {
             child.getDependencies().add("compile", "commons-httpclient:commons-httpclient:3.0");
             child.getDependencies().add("compile", "commons-codec:commons-codec:1.2");
             child.getDependencies().add("compile", "org.slf4j:jcl-over-slf4j:1.6.6");
-            child.getDependencies().add("compile", "org.codehaus:groovy:groovy-all:2.0.5");
+            child.getDependencies().add("compile", "org.codehaus:groovy:groovy-all:2.3.10");
             child.getDependencies().add("compile", "commons-codec:commons-codec:1.2");
-            child.getDependencies().add("testCompile", "junit:junit:4.11");
+            child.getDependencies().add("testCompile", "junit:junit:4.12");
             child.getDependencies().add("testCompile", "org.testng:testng:6.4");
             child.getDependencies().add("runtime", "com.googlecode:reflectasm:1.01");
             Test test = (Test)(child.getTasks().getByName("test"));
diff --git a/subprojects/performance/src/generator.groovy b/subprojects/performance/src/generator.groovy
index a1cfa21..a827158 100644
--- a/subprojects/performance/src/generator.groovy
+++ b/subprojects/performance/src/generator.groovy
@@ -16,10 +16,12 @@ class TestProject {
     Integer linesOfCodePerSourceFile
     List<MavenModule> dependencies
     MavenRepository repository;
+    Integer subprojectNumber
 
-    TestProject(String name, Object defaults) {
+    TestProject(String name, Object defaults, Integer subprojectNumber = null) {
         this.name = name
         this.defaults = defaults
+        this.subprojectNumber = subprojectNumber
     }
 
     int getSourceFiles() {
@@ -38,12 +40,16 @@ class TestProject {
 class ProjectGeneratorTask extends DefaultTask {
     @OutputDirectory
     File destDir
+
     boolean groovyProject
     boolean scalaProject
+    boolean nativeProject
     int sourceFiles = 1
     Integer testSourceFiles
     int linesOfCodePerSourceFile = 5
-    @InputFiles FileCollection testDependencies
+
+    @InputFiles
+    FileCollection testDependencies
 
     final List<TestProject> projects = []
     List<String> rootProjectTemplates = ['root-project']
@@ -51,6 +57,8 @@ class ProjectGeneratorTask extends DefaultTask {
     final SimpleTemplateEngine engine = new SimpleTemplateEngine()
     final Map<File, Template> templates = [:]
 
+    Map<String, Object> templateArgs = [:]
+
     final DependencyGraph dependencyGraph = new DependencyGraph()
 
     def ProjectGeneratorTask() {
@@ -68,7 +76,7 @@ class ProjectGeneratorTask extends DefaultTask {
             projects.subList(projectCount, projects.size()).clear()
         } else {
             while (projects.size() < projectCount) {
-                def project = projects.empty ? new TestProject("root", this) : new TestProject("project${projects.size()}", this)
+                def project = projects.empty ? new TestProject("root", this) : new TestProject("project${projects.size()}", this, projects.size())
                 projects << project
             }
         }
@@ -88,7 +96,7 @@ class ProjectGeneratorTask extends DefaultTask {
         MavenRepository repo = generateDependencyRepository()
         generateRootProject()
         subprojects.each {
-            if(repo){
+            if (repo) {
                 it.setRepository(repo)
                 it.setDependencies(repo.getDependenciesOfTransitiveLevel(1))
             }
@@ -104,7 +112,7 @@ class ProjectGeneratorTask extends DefaultTask {
         return projects[0]
     }
 
-    MavenRepository generateDependencyRepository(){
+    MavenRepository generateDependencyRepository() {
         MavenRepository repo = new RepositoryBuilder(getDestDir())
                 .withArtifacts(dependencyGraph.size)
                 .withDepth(dependencyGraph.depth)
@@ -120,7 +128,7 @@ class ProjectGeneratorTask extends DefaultTask {
     def generateRootProject() {
         def templates = subprojectNames.empty ? subProjectTemplates : rootProjectTemplates
         if (!templates.empty) {
-            templates << 'heap-capture'
+            templates.addAll(['build-event-timestamps', 'heap-capture'])
         }
         generateProject rootProject, subprojects: subprojectNames, projectDir: destDir,
                 files: subprojectNames.empty ? [] : ['settings.gradle'],
@@ -128,8 +136,11 @@ class ProjectGeneratorTask extends DefaultTask {
                 includeSource: subprojectNames.empty
 
         project.copy {
-            from testDependencies
-            into new File(getDestDir(), 'lib/test')
+            from "src/templates/init.gradle"
+            into(getDestDir())
+            into('lib/test') {
+                from testDependencies
+            }
         }
     }
 
@@ -171,12 +182,14 @@ class ProjectGeneratorTask extends DefaultTask {
             }
         }
 
-        args += [projectName: testProject.name, groovyProject: groovyProject, scalaProject: scalaProject,
-                propertyCount: (testProject.linesOfCodePerSourceFile.intdiv(7)), repository: testProject.repository, dependencies:testProject.dependencies,
-                testProject: testProject
-                ]
+        args += [projectName  : testProject.name, subprojectNumber: testProject.subprojectNumber, groovyProject: groovyProject, scalaProject: scalaProject,
+                 propertyCount: (testProject.linesOfCodePerSourceFile.intdiv(7)), repository: testProject.repository, dependencies: testProject.dependencies,
+                 testProject  : testProject
+        ]
+
+        args += templateArgs
 
-        files.each {String name ->
+        files.each { String name ->
             generate(name, name, args)
         }
 
@@ -215,6 +228,14 @@ class ProjectGeneratorTask extends DefaultTask {
                     generate("src/test/scala/${packageName.replace('.', '/')}/${classArgs.testClassName}.scala", 'Test.scala', classArgs)
                 }
             }
+            if (nativeProject) {
+                testProject.sourceFiles.times { s ->
+                    args.moduleCount.times { m ->
+                        Map classArgs = args + [componentName: "lib${m + 1}", functionName: "lib${s + 1}"]
+                        generate("src/${classArgs.componentName}/c/${classArgs.functionName}.c", 'lib.c', classArgs)
+                    }
+                }
+            }
         }
     }
 
@@ -239,8 +260,9 @@ class DependencyGraph {
     int size = 0
     int depth = 1
     boolean useSnapshotVersions = false
-    boolean isEmpty(){
-        size==0
+
+    boolean isEmpty() {
+        size == 0
     }
 }
 
@@ -265,14 +287,14 @@ class MavenRepository {
         return module
     }
 
-    void publish(){
-        modules.each{
+    void publish() {
+        modules.each {
             it.publish()
         }
     }
 
-    List<MavenModule> getDependenciesOfTransitiveLevel(int level){
-        return modules.findAll{((int)(it.artifactId - "artifact").toInteger() % depth) == level - 1 }
+    List<MavenModule> getDependenciesOfTransitiveLevel(int level) {
+        return modules.findAll { ((int) (it.artifactId - "artifact").toInteger() % depth) == level - 1 }
     }
 }
 
@@ -307,7 +329,7 @@ class MavenModule {
         return this
     }
 
-    String shortNotation(){
+    String shortNotation() {
         return "$groupId:$artifactId:$version"
     }
 
@@ -488,15 +510,15 @@ class RepositoryBuilder {
     }
 
     MavenRepository create() {
-        if(numberOfArtifacts==0){
+        if (numberOfArtifacts == 0) {
             return null;
         }
         targetDir.mkdirs();
         MavenRepository repo = new MavenRepository(new File(targetDir, "mavenRepo"))
         numberOfArtifacts.times {
-            if(withSnapshotVersions){
+            if (withSnapshotVersions) {
                 repo.addModule('group', "artifact$it", "1.0-SNAPSHOT")
-            }else{
+            } else {
                 repo.addModule('group', "artifact$it")
             }
         }
@@ -509,7 +531,7 @@ class RepositoryBuilder {
 
     void transformGraphToDepth(List<MavenModule> modules, int depth) {
         def depGroups = modules.groupBy { (int) (it.artifactId - "artifact").toInteger() / depth }
-        depGroups.each {idx, groupModules ->
+        depGroups.each { idx, groupModules ->
             for (int i = 0; i < groupModules.size() - 1; i++) {
                 def next = groupModules[i + 1]
                 groupModules[i].dependsOn(next.groupId, next.artifactId, next.version)
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/CleanBuildPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/CleanBuildPerformanceTest.groovy
index d0f3794..fb2f36e 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/CleanBuildPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/CleanBuildPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class CleanBuildPerformanceTest extends AbstractPerformanceTest {
+class CleanBuildPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' clean build")
     def "clean build"() {
         given:
@@ -29,7 +28,7 @@ class CleanBuildPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['clean', 'build']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.4', '1.8', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', '2.4', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/ConfigurationPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ConfigurationPerformanceTest.groovy
index 05e5bff..daf8b7b 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/ConfigurationPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ConfigurationPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class ConfigurationPerformanceTest extends AbstractPerformanceTest {
+class ConfigurationPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' configuration")
     def "configuration"() {
         given:
@@ -29,7 +28,7 @@ class ConfigurationPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['help']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.1', 'last']
+        runner.targetVersions = ['1.0', '1.1', '2.0', '2.2.1', '2.4', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/DaemonPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DaemonPerformanceTest.groovy
new file mode 100644
index 0000000..58e799f
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DaemonPerformanceTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.gradle.performance.measure.DataAmount
+import spock.lang.Unroll
+
+import static org.gradle.performance.measure.Duration.millis
+
+class DaemonPerformanceTest extends AbstractCrossVersionPerformanceTest {
+
+    @Unroll("Project '#testProject' build")
+    def "build"() {
+        given:
+        runner.testId = "daemon clean build $testProject"
+        runner.testProject = testProject
+        runner.useDaemon = true
+        runner.tasksToRun = ['clean', 'build']
+        runner.maxExecutionTimeRegression = maxTimeReg
+        runner.maxMemoryRegression = maxMemReg
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', '2.4', 'last']
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+
+        where:
+        testProject | maxTimeReg   | maxMemReg
+        "small"     | millis(500)  | DataAmount.kbytes(150)
+        "multi"     | millis(1000) | DataAmount.mbytes(10)
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyReportPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyReportPerformanceTest.groovy
index 9413fc6..4424283 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyReportPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyReportPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class DependencyReportPerformanceTest extends AbstractPerformanceTest {
+class DependencyReportPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' dependency report")
     def "dependency report"() {
         given:
@@ -29,7 +28,7 @@ class DependencyReportPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['dependencyReport']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.8', 'last']
+        runner.targetVersions = ['1.0', '1.8', '2.0', '2.2.1', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyResolutionStressTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyResolutionStressTest.groovy
index 98fec2a..233cf44 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyResolutionStressTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/DependencyResolutionStressTest.groovy
@@ -33,7 +33,9 @@ import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
 import java.util.zip.ZipEntry
 import java.util.zip.ZipOutputStream
+import org.junit.experimental.categories.Category
 
+ at Category(PerformanceTest)
 class DependencyResolutionStressTest extends Specification {
     @Rule TestNameTestDirectoryProvider workspace = new TestNameTestDirectoryProvider()
     GradleDistribution distribution = new UnderDevelopmentGradleDistribution()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/FirstBuildPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/FirstBuildPerformanceTest.groovy
index c8fb6df..a20deba 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/FirstBuildPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/FirstBuildPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class FirstBuildPerformanceTest extends AbstractPerformanceTest {
+class FirstBuildPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' first use")
     def "build"() {
         // This is just an approximation of first use. We simply recompile the scripts
@@ -31,7 +30,7 @@ class FirstBuildPerformanceTest extends AbstractPerformanceTest {
         runner.tasksToRun = ['help']
         runner.args = ['--recompile-scripts']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.10', '1.12', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', '2.4', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/IdeIntegrationPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/IdeIntegrationPerformanceTest.groovy
index 9ef2bda..aaf5009 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/IdeIntegrationPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/IdeIntegrationPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class IdeIntegrationPerformanceTest extends AbstractPerformanceTest {
+class IdeIntegrationPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' eclipse")
     def "eclipse"() {
         given:
@@ -29,7 +28,7 @@ class IdeIntegrationPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['eclipse']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.4', '1.8', '1.12', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', 'last']
 
         when:
         def result = runner.run()
@@ -39,9 +38,9 @@ class IdeIntegrationPerformanceTest extends AbstractPerformanceTest {
 
         where:
         testProject       | maxExecutionTimeRegression
-        "small"           | millis(750)
-        "multi"           | millis(1500)
-        "lotDependencies" | millis(3000)
+        "small"           | millis(800)
+        "multi"           | millis(500)
+        "lotDependencies" | millis(500)
     }
 
     @Unroll("Project '#testProject' idea")
@@ -51,7 +50,7 @@ class IdeIntegrationPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['idea']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.8', '1.10', '1.12', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', 'last']
 
         when:
         def result = runner.run()
@@ -61,8 +60,8 @@ class IdeIntegrationPerformanceTest extends AbstractPerformanceTest {
 
         where:
         testProject       | maxExecutionTimeRegression
-        "small"           | millis(750)
-        "multi"           | millis(1500)
-        "lotDependencies" | millis(3000)
+        "small"           | millis(800)
+        "multi"           | millis(500)
+        "lotDependencies" | millis(500)
     }
 }
\ No newline at end of file
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/ManyEmptyProjectsHelpPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ManyEmptyProjectsHelpPerformanceTest.groovy
new file mode 100644
index 0000000..c68e266
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ManyEmptyProjectsHelpPerformanceTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.gradle.performance.measure.DataAmount
+
+import static org.gradle.performance.measure.Duration.millis
+
+class ManyEmptyProjectsHelpPerformanceTest extends AbstractCrossVersionPerformanceTest {
+
+    def run() {
+        given:
+        runner.testId = "many empty projects help"
+        runner.testProject = "bigEmpty"
+        runner.tasksToRun = ['help']
+        runner.maxExecutionTimeRegression = millis(2000)
+        runner.maxMemoryRegression = DataAmount.mbytes(200)
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', '2.4', 'last']
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeParallelPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeParallelPerformanceTest.groovy
new file mode 100644
index 0000000..a1c4359
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeParallelPerformanceTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import spock.lang.Unroll
+
+class NativeParallelPerformanceTest extends AbstractCrossBuildPerformanceTest {
+    @Unroll
+    def "#size parallel performance test" () {
+        when:
+        runner.testId = "native parallel build ${size}"
+        runner.testGroup = 'parallel builds'
+        runner.buildSpec {
+            projectName("${size}Native").displayName("parallel").invocation {
+                tasksToRun("clean", "assemble")
+            }
+        }
+        runner.baseline {
+            projectName("${size}Native").displayName("serial").invocation {
+                tasksToRun("clean", "assemble").disableParallelWorkers()
+            }
+        }
+
+        then:
+        runner.run()
+
+        where:
+        size << [ "small", "medium", "big", "multi" ]
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativePerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativePerformanceTest.groovy
new file mode 100644
index 0000000..97d4e57
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativePerformanceTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import spock.lang.Unroll
+
+import static org.gradle.performance.measure.Duration.millis
+
+class NativePerformanceTest extends AbstractCrossVersionPerformanceTest {
+    @Unroll('Project #type native build')
+    def "build" () {
+        given:
+        runner.testId = "native build ${type}"
+        runner.testProject = "${type}Native"
+        runner.tasksToRun = [ "clean", "assemble" ]
+        runner.maxExecutionTimeRegression = maxExecutionTimeRegression
+        runner.targetVersions = [ '2.3', '2.4', 'last' ]
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+
+        where:
+        type           | maxExecutionTimeRegression
+        "small"        | millis(500)
+        "medium"       | millis(500)
+        "big"          | millis(1000)
+        "multi"        | millis(1000)
+    }
+
+    def "Many projects native build" () {
+        given:
+        runner.testId = "native build many projects"
+        runner.testProject = "manyProjectsNative"
+        runner.tasksToRun = [ "clean", "assemble" ]
+        runner.maxExecutionTimeRegression = millis(500)
+        runner.targetVersions = [ '2.3', '2.4', 'last' ]
+        runner.useDaemon = true
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeScenarioPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeScenarioPerformanceTest.groovy
new file mode 100644
index 0000000..a754c25
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/NativeScenarioPerformanceTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.gradle.performance.fixture.BuildExperimentSpec
+import spock.lang.Unroll
+
+class NativeScenarioPerformanceTest extends AbstractCrossBuildPerformanceTest {
+    @Override
+    protected void defaultSpec(BuildExperimentSpec.Builder builder) {
+        builder.invocation.gradleOpts("-Xmx1024m", "-XX:MaxPermSize=256m")
+        super.defaultSpec(builder)
+    }
+
+    @Unroll
+    def "native #size project comparison for #scenario" () {
+        when:
+        runner.testGroup = "Native build comparison"
+        runner.testId = "$size native comparison $scenario build"
+        runner.baseline {
+            projectName("${size}ScenarioNative").displayName("baseline").invocation {
+                tasksToRun(*tasks)
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}ScenarioNative").displayName("with daemon").invocation {
+                tasksToRun(*tasks).useDaemon()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}ScenarioNative").displayName("with tooling api").invocation {
+                tasksToRun(*tasks).useToolingApi()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}ScenarioNative").displayName("with daemon (no client logging)").invocation {
+                tasksToRun(*tasks).useDaemon().disableDaemonLogging()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}ScenarioNative").displayName("with daemon (reuse)").invocation {
+                tasksToRun(*tasks).useDaemon().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}ScenarioNative").displayName("with tooling api (reuse)").invocation {
+                tasksToRun(*tasks).useToolingApi().enableModelReuse()
+            }
+        }
+
+        then:
+        runner.run()
+
+        where:
+        scenario | size     | tasks
+        "empty"  | "small"  | ["help"]
+        "empty"  | "medium" | ["help"]
+        "empty"  | "big"    | ["help"]
+        "full"   | "small"  | ["clean", "assemble"]
+        "full"   | "medium" | ["clean", "assemble"]
+        "full"   | "big"    | ["clean", "assemble"]
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldJavaPluginBigProjectPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldJavaPluginBigProjectPerformanceTest.groovy
new file mode 100644
index 0000000..22d5fe2
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldJavaPluginBigProjectPerformanceTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import spock.lang.Unroll
+
+import static org.gradle.performance.measure.DataAmount.mbytes
+import static org.gradle.performance.measure.Duration.millis
+
+class OldJavaPluginBigProjectPerformanceTest extends AbstractCrossVersionPerformanceTest {
+
+    @Unroll("#scenario build")
+    def "build"() {
+        given:
+        runner.testId = "big project old java plugin $scenario build"
+        runner.testProject = "bigOldJavaMoreSource"
+        runner.useDaemon = true
+        runner.tasksToRun = tasks
+        runner.maxExecutionTimeRegression = millis(200)
+        runner.maxMemoryRegression = mbytes(100)
+        runner.targetVersions = ['2.0', '2.2.1', '2.4', 'last']
+        runner.gradleOpts = ["-Xmx1024m", "-XX:MaxPermSize=256m"]
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+
+        where:
+        scenario  | tasks
+        "empty"   | ["help"]
+        "full"    | ["clean", "assemble"]
+        "partial" | [":project1:clean", ":project1:assemble"]
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldVsNewJavaPluginPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldVsNewJavaPluginPerformanceTest.groovy
new file mode 100644
index 0000000..656b67d
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/OldVsNewJavaPluginPerformanceTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.gradle.performance.fixture.BuildExperimentSpec
+import spock.lang.Unroll
+
+class OldVsNewJavaPluginPerformanceTest extends AbstractCrossBuildPerformanceTest {
+
+    @Override
+    protected void defaultSpec(BuildExperimentSpec.Builder builder) {
+        builder.invocation.gradleOpts("-Xmx1024m", "-XX:MaxPermSize=256m")
+        super.defaultSpec(builder)
+    }
+
+    @Unroll
+    def "#size project old vs new java plugin #scenario build"() {
+        when:
+        runner.testGroup = "old vs new java plugin"
+        runner.testId = "$size project old vs new java plugin $scenario build"
+        runner.buildSpec {
+            projectName("${size}NewJava").displayName("new plugin").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}NewJava").displayName("new plugin (reuse)").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}NewJava").displayName("new plugin (reuse + tooling api)").invocation {
+                tasksToRun(*tasks).useToolingApi().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}NewJava").displayName("new plugin (no client logging)").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl().disableDaemonLogging()
+            }
+        }
+        runner.baseline {
+            projectName("${size}OldJava").displayName("old plugin").invocation {
+                tasksToRun(*tasks).useDaemon()
+            }
+        }
+        runner.baseline {
+            projectName("${size}OldJava").displayName("old plugin (tooling api)").invocation {
+                tasksToRun(*tasks).useToolingApi()
+            }
+        }
+        runner.baseline {
+            projectName("${size}OldJava").displayName("old plugin (no client logging)").invocation {
+                tasksToRun(*tasks).useDaemon().disableDaemonLogging()
+            }
+        }
+
+        then:
+        runner.run()
+
+        where:
+        scenario  | size     | tasks
+        "empty"   | "small"  | ["help"]
+        "empty"   | "medium" | ["help"]
+        "empty"   | "big"    | ["help"]
+        "full"    | "small"  | ["clean", "assemble"]
+        "full"    | "medium" | ["clean", "assemble"]
+        "full"    | "big"    | ["clean", "assemble"]
+        "partial" | "medium" | [":project1:clean", ":project1:assemble"]
+        "partial" | "big"    | [":project1:clean", ":project1:assemble"]
+    }
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/ParallelBuildPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ParallelBuildPerformanceTest.groovy
new file mode 100644
index 0000000..208da91
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ParallelBuildPerformanceTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+class ParallelBuildPerformanceTest extends AbstractCrossBuildPerformanceTest {
+
+    def "test"() {
+        when:
+        runner.testId = "parallel builds"
+        runner.testGroup = "parallel builds"
+        runner.buildSpec {
+            projectName("multi").displayName("parallel").invocation {
+                tasksToRun("clean", "build").args("--parallel", "--max-workers=2")
+            }
+        }
+        runner.baseline {
+            projectName("multi").displayName("serial").invocation {
+                tasksToRun("clean", "build")
+            }
+        }
+
+        then:
+        runner.run()
+    }
+
+}
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/ProjectDependenciesPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ProjectDependenciesPerformanceTest.groovy
new file mode 100644
index 0000000..cc457df
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/ProjectDependenciesPerformanceTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.junit.experimental.categories.Category
+
+ at Category(Experiment)
+class ProjectDependenciesPerformanceTest extends AbstractCrossVersionPerformanceTest {
+
+    def "resolving dependencies"() {
+        given:
+        runner.testId = "resolving dependencies lotProjectDependencies"
+        runner.testProject = "lotProjectDependencies"
+        runner.tasksToRun = ['resolveDependencies']
+        runner.useDaemon = true
+        runner.targetVersions = ['2.2.1', '2.3', '2.4', 'last']
+
+        when:
+        def result = runner.run()
+
+        then:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/TestExecutionPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/TestExecutionPerformanceTest.groovy
index 6883bea..8da1d96 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/TestExecutionPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/TestExecutionPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class TestExecutionPerformanceTest extends AbstractPerformanceTest {
+class TestExecutionPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' test execution")
     def "test execution"() {
         given:
@@ -30,7 +29,7 @@ class TestExecutionPerformanceTest extends AbstractPerformanceTest {
         runner.tasksToRun = ['cleanTest', 'test']
         runner.args = ['-q']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.8', '1.11', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/UpToDateBuildPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/UpToDateBuildPerformanceTest.groovy
index bd22cb0..b28cd5a 100644
--- a/subprojects/performance/src/integTest/groovy/org/gradle/performance/UpToDateBuildPerformanceTest.groovy
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/UpToDateBuildPerformanceTest.groovy
@@ -16,12 +16,11 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.AbstractPerformanceTest
 import spock.lang.Unroll
 
 import static org.gradle.performance.measure.Duration.millis
 
-class UpToDateBuildPerformanceTest extends AbstractPerformanceTest {
+class UpToDateBuildPerformanceTest extends AbstractCrossVersionPerformanceTest {
     @Unroll("Project '#testProject' up-to-date build")
     def "build"() {
         given:
@@ -29,7 +28,7 @@ class UpToDateBuildPerformanceTest extends AbstractPerformanceTest {
         runner.testProject = testProject
         runner.tasksToRun = ['build']
         runner.maxExecutionTimeRegression = maxExecutionTimeRegression
-        runner.targetVersions = ['1.0', '1.4', '1.8', 'last']
+        runner.targetVersions = ['1.0', '2.0', '2.2.1', '2.4', 'last']
 
         when:
         def result = runner.run()
diff --git a/subprojects/performance/src/integTest/groovy/org/gradle/performance/VariantsPerformanceTest.groovy b/subprojects/performance/src/integTest/groovy/org/gradle/performance/VariantsPerformanceTest.groovy
new file mode 100644
index 0000000..69718fe
--- /dev/null
+++ b/subprojects/performance/src/integTest/groovy/org/gradle/performance/VariantsPerformanceTest.groovy
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.performance
+
+import spock.lang.Unroll
+
+class VariantsPerformanceTest extends AbstractCrossBuildPerformanceTest {
+
+    @Unroll
+    def "#size project using variants #scenario build"() {
+        when:
+        runner.testGroup = "project using variants"
+        runner.testId = "$size project using variants $scenario build"
+        runner.buildSpec {
+            projectName("${size}VariantsNewModel").displayName("new model").invocation {
+                tasksToRun(task).useDaemon().enableTransformedModelDsl()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}VariantsNewModel").displayName("new model (reuse)").invocation {
+                tasksToRun(task).useDaemon().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}VariantsNewModel").displayName("new model (reuse + tooling api)").invocation {
+                tasksToRun(task).useToolingApi().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("${size}VariantsNewModel").displayName("new model (no client logging)").invocation {
+                tasksToRun(task).useDaemon().enableTransformedModelDsl().disableDaemonLogging()
+            }
+        }
+        runner.baseline {
+            projectName("${size}VariantsOldModel").displayName("old model").invocation {
+                tasksToRun(task).useDaemon()
+            }
+        }
+        runner.baseline {
+            projectName("${size}VariantsOldModel").displayName("old model (tooling api)").invocation {
+                tasksToRun(task).useToolingApi()
+            }
+        }
+        runner.baseline {
+            projectName("${size}VariantsOldModel").displayName("old model (no client logging)").invocation {
+                tasksToRun(task).useDaemon().disableDaemonLogging()
+            }
+        }
+
+        then:
+        runner.run()
+
+        where:
+        scenario  | size     | task
+        "empty"   | "small"  | "help"
+        "empty"   | "medium" | "help"
+        "empty"   | "big"    | "help"
+        "full"    | "small"  | "allVariants"
+        "full"    | "medium" | "allVariants"
+        "full"    | "big"    | "allVariants"
+        "partial" | "medium" | "flavour1type1_t1"
+        "partial" | "big"    | "flavour1type1_t1"
+    }
+
+    @Unroll
+    def "multiproject using variants #scenario build"() {
+        when:
+        runner.testGroup = "project using variants"
+        runner.testId = "multiproject using variants $scenario build"
+        runner.buildSpec {
+            projectName("variantsNewModelMultiproject").displayName("new model").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl()
+            }
+        }
+        runner.buildSpec {
+            projectName("variantsNewModelMultiproject").displayName("new model (reuse)").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("variantsNewModelMultiproject").displayName("new model (reuse + tooling api)").invocation {
+                tasksToRun(*tasks).useToolingApi().enableTransformedModelDsl().enableModelReuse()
+            }
+        }
+        runner.buildSpec {
+            projectName("variantsNewModelMultiproject").displayName("new model (no client logging)").invocation {
+                tasksToRun(*tasks).useDaemon().enableTransformedModelDsl().disableDaemonLogging()
+            }
+        }
+        runner.baseline {
+            projectName("variantsOldModelMultiproject").displayName("old model").invocation {
+                tasksToRun(*tasks).useDaemon()
+            }
+        }
+        runner.baseline {
+            projectName("variantsOldModelMultiproject").displayName("old model (tooling api)").invocation {
+                tasksToRun(*tasks).useToolingApi()
+            }
+        }
+        runner.baseline {
+            projectName("variantsOldModelMultiproject").displayName("old model (no client logging)").invocation {
+                tasksToRun(*tasks).useDaemon().disableDaemonLogging()
+            }
+        }
+
+        then:
+        runner.run()
+
+        where:
+        scenario                      | tasks
+        "single variant"              | [":project1:flavour1type1_t1"]
+        "all variants single project" | [":project1:allVariants"]
+        "all variants all projects"   | ["allVariants"]
+    }
+}
diff --git a/subprojects/performance/src/templates/build-event-timestamps/build.gradle b/subprojects/performance/src/templates/build-event-timestamps/build.gradle
new file mode 100644
index 0000000..31209ac
--- /dev/null
+++ b/subprojects/performance/src/templates/build-event-timestamps/build.gradle
@@ -0,0 +1,20 @@
+${original}
+
+def configurationEndTimestamp
+
+gradle.taskGraph.whenReady {
+    configurationEndTimestamp = System.currentTimeMillis()
+}
+
+gradle.buildFinished {
+    def buildEndTimestamp = System.currentTimeMillis()
+    buildDir.mkdirs()
+    def writer = new File(buildDir, "buildEventTimestamps.txt").newPrintWriter()
+    try {
+        writer.println(settingsEvaluatedTimestamp)
+        writer.println(configurationEndTimestamp)
+        writer.print(buildEndTimestamp)
+    } finally {
+        writer.close()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/config-inject/build.gradle b/subprojects/performance/src/templates/config-inject/build.gradle
index dc4fb1c..0494c1a 100644
--- a/subprojects/performance/src/templates/config-inject/build.gradle
+++ b/subprojects/performance/src/templates/config-inject/build.gradle
@@ -17,10 +17,10 @@ dependencies {
     compile 'commons-lang:commons-lang:2.5'
     compile "commons-httpclient:commons-httpclient:3.0"
     compile "commons-codec:commons-codec:1.2"
-    compile "org.slf4j:jcl-over-slf4j:1.7.5"
-    compile "org.codehaus:groovy:groovy-all:2.0.5"
+    compile "org.slf4j:jcl-over-slf4j:1.7.10"
+    compile "org.codehaus:groovy:groovy-all:2.3.10"
     compile "commons-codec:commons-codec:1.2"
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
     testCompile 'org.testng:testng:6.4'
     runtime 'com.googlecode:reflectasm:1.01'
 
@@ -36,7 +36,7 @@ test {
 <% if (groovyProject) { %>
 apply plugin: 'groovy'
 dependencies {
-    compile 'org.codehaus:groovy:groovy-all:2.0.5'
+    compile 'org.codehaus:groovy:groovy-all:2.3.10'
 }
 <% } %>
 
diff --git a/subprojects/performance/src/templates/heap-capture/build.gradle b/subprojects/performance/src/templates/heap-capture/build.gradle
index 787c2d5..48a0128 100644
--- a/subprojects/performance/src/templates/heap-capture/build.gradle
+++ b/subprojects/performance/src/templates/heap-capture/build.gradle
@@ -7,15 +7,15 @@ gradle.buildFinished {
     def heap = ManagementFactory.memoryMXBean.heapMemoryUsage
     def nonHeap = ManagementFactory.memoryMXBean.nonHeapMemoryUsage
     logger.lifecycle "BEFORE GC"
-    logger.lifecycle "heap: \${format(heap.used)} (initial \${format(heap.init)}, commited \${format(heap.committed)}, max \${format(heap.max)}"
-    logger.lifecycle "nonHeap: \${format(nonHeap.used)} (initial \${format(nonHeap.init)}, commited \${format(nonHeap.committed)}, max \${format(nonHeap.max)}"
+    logger.lifecycle "heap: \${format(heap.used)} (initial \${format(heap.init)}, committed \${format(heap.committed)}, max \${format(heap.max)}"
+    logger.lifecycle "nonHeap: \${format(nonHeap.used)} (initial \${format(nonHeap.init)}, committed \${format(nonHeap.committed)}, max \${format(nonHeap.max)}"
 
     ManagementFactory.memoryMXBean.gc()
     heap = ManagementFactory.memoryMXBean.heapMemoryUsage
     nonHeap = ManagementFactory.memoryMXBean.nonHeapMemoryUsage
     logger.lifecycle "AFTER GC"
-    logger.lifecycle "heap: \${format(heap.used)} (initial \${format(heap.init)}, commited \${format(heap.committed)}, max \${format(heap.max)}"
-    logger.lifecycle "nonHeap: \${format(nonHeap.used)} (initial \${format(nonHeap.init)}, commited \${format(nonHeap.committed)}, max \${format(nonHeap.max)}"
+    logger.lifecycle "heap: \${format(heap.used)} (initial \${format(heap.init)}, committed \${format(heap.committed)}, max \${format(heap.max)}"
+    logger.lifecycle "nonHeap: \${format(nonHeap.used)} (initial \${format(nonHeap.init)}, committed \${format(nonHeap.committed)}, max \${format(nonHeap.max)}"
     buildDir.mkdirs()
     new File(buildDir, "totalMemoryUsed.txt").text = heap.used
 }
diff --git a/subprojects/performance/src/templates/init.gradle b/subprojects/performance/src/templates/init.gradle
new file mode 100644
index 0000000..724c62c
--- /dev/null
+++ b/subprojects/performance/src/templates/init.gradle
@@ -0,0 +1,5 @@
+gradle.settingsEvaluated {
+    rootProject {
+        it.ext.settingsEvaluatedTimestamp = System.currentTimeMillis()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/java-source/Production.java b/subprojects/performance/src/templates/java-source/Production.java
new file mode 100644
index 0000000..ba24a43
--- /dev/null
+++ b/subprojects/performance/src/templates/java-source/Production.java
@@ -0,0 +1,24 @@
+package ${packageName};
+
+public class ${productionClassName} {
+    private final String property;
+
+    public ${productionClassName}(String param) {
+        this.property = param;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+<% propertyCount.times { %>
+    private String prop${it};
+
+    public String getProp${it}() {
+        return prop${it};
+    }
+
+    public void setProp${it}(String value) {
+        prop${it} = value;
+    }
+<% } %>
+}
diff --git a/subprojects/performance/src/templates/native-component/build.gradle b/subprojects/performance/src/templates/native-component/build.gradle
new file mode 100644
index 0000000..3030328
--- /dev/null
+++ b/subprojects/performance/src/templates/native-component/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'c'
+
+model {
+    components {
+        <% moduleCount.times { %>
+        lib${it+1}(NativeLibrarySpec)
+        <% } %>
+    }
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/native-scenario/build.gradle b/subprojects/performance/src/templates/native-scenario/build.gradle
new file mode 100644
index 0000000..f926921
--- /dev/null
+++ b/subprojects/performance/src/templates/native-scenario/build.gradle
@@ -0,0 +1,70 @@
+import org.gradle.model.*
+
+apply plugin: 'c'
+
+model {
+    components {
+        <% moduleCount.times { %>
+            lib${it+1}(NativeLibrarySpec)
+        <% } %>
+    }
+
+    custom(CustomExtension) {
+        action = { println "" }
+        value = "."
+    }
+
+    tasks {
+        create("extraTask1", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask2", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask3", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask4", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+    }
+    tasks {
+        withType(MyTaskClass, TaskWiring)
+    }
+}
+
+ at Managed
+interface CustomExtension {
+    @Unmanaged
+    Closure<?> getAction()
+    void setAction(Closure<?> action)
+    String getValue()
+    void setValue(String value)
+}
+
+class TaskWiring extends RuleSource {
+    @Mutate
+    void wire(MyTaskClass task, CustomExtension custom) {
+        task.doLast(custom.action)
+        10.times {
+            task.description += custom.value
+        }
+    }
+}
+
+class MyTaskClass extends DefaultTask {
+
+    @InputFiles
+    FileCollection input
+
+    @OutputFiles
+    FileCollection output
+
+    @Input
+    boolean flag
+
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/native-source/lib.c b/subprojects/performance/src/templates/native-source/lib.c
new file mode 100644
index 0000000..8937a4a
--- /dev/null
+++ b/subprojects/performance/src/templates/native-source/lib.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+<% functionCount.times { %>
+int ${functionName}_${it+1} () {
+  printf("Hello world!");
+  return 0;
+}
+<% } %>
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/new-java-plugin/build.gradle b/subprojects/performance/src/templates/new-java-plugin/build.gradle
new file mode 100644
index 0000000..3775a51
--- /dev/null
+++ b/subprojects/performance/src/templates/new-java-plugin/build.gradle
@@ -0,0 +1,112 @@
+import org.gradle.model.*
+
+plugins {
+    id 'jvm-component'
+    id 'java-lang'
+}
+
+model {
+    components {
+        main(JvmLibrarySpec)
+    }
+    tasks {
+        create("test", Test) {
+            dependsOn "jar", "compileTestJava", "processTestResources"
+        }
+        create("compileTestJava", JavaCompile) {
+            dependsOn "jar"
+        }
+        create("processTestResources", Copy) {
+
+        }
+        create("extraTask1", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask2", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask3", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+        create("extraTask4", MyTaskClass) {
+            description = name
+            doLast {}
+        }
+    }
+    tasks.check {
+        dependsOn "test"
+    }
+    tasks.build {
+        // not quite parity with the old build, we have no way of expressing
+        // tasks.build.dependsOn tasks.withType(Checkstyle)
+        dependsOn \$("tasks.checkstyleMain"), \$("tasks.checkstyleTest")
+    }
+
+    checkstyle(CheckstyleExtension) {
+        ignoreFailures = true
+    }
+
+    tasks {
+        create("checkstyleMain", Checkstyle)
+        create("checkstyleTest", Checkstyle)
+        withType(Checkstyle, CheckstyleTaskDefaults)
+    }
+
+    custom(CustomExtension) {
+        action = { println "" }
+        value = "."
+    }
+
+    tasks {
+        withType(MyTaskClass, TaskWiring)
+    }
+
+}
+
+class CheckstyleTaskDefaults extends RuleSource {
+    @Defaults
+    void add(Checkstyle task, CheckstyleExtension extension) {
+        task.ignoreFailures = extension.ignoreFailures
+    }
+}
+
+ at Managed
+interface CheckstyleExtension {
+    Boolean getIgnoreFailures()
+    void setIgnoreFailures(Boolean flag)
+}
+
+ at Managed
+interface CustomExtension {
+    @Unmanaged
+    Closure<?> getAction()
+    void setAction(Closure<?> action)
+    String getValue()
+    void setValue(String value)
+}
+
+class TaskWiring extends RuleSource {
+    @Mutate
+    void wire(MyTaskClass task, CustomExtension custom) {
+        task.doLast(custom.action)
+        10.times {
+            task.description += custom.value
+        }
+    }
+}
+
+class MyTaskClass extends DefaultTask {
+
+    @InputFiles
+    FileCollection input
+
+    @OutputFiles
+    FileCollection output
+
+    @Input
+    boolean flag
+
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/old-java-plugin/build.gradle b/subprojects/performance/src/templates/old-java-plugin/build.gradle
new file mode 100644
index 0000000..343875a
--- /dev/null
+++ b/subprojects/performance/src/templates/old-java-plugin/build.gradle
@@ -0,0 +1,76 @@
+apply plugin: 'java'
+
+class CheckstyleExtension {
+    boolean ignoreFailures
+}
+
+class CustomExtension {
+    Closure<?> action = { println "" }
+    String value = "."
+}
+
+class FakeCheckstylePlugin implements Plugin {
+
+    void apply(project) {
+        project.extensions.create("checkstyle", CheckstyleExtension)
+
+        project.checkstyle {
+            ignoreFailures = true
+        }
+
+        project.tasks.create("checkstyleMain", Checkstyle)
+        project.tasks.create("checkstyleTest", Checkstyle)
+
+        project.tasks.withType(Checkstyle) {
+            ignoreFailures = project.checkstyle.ignoreFailures
+        }
+
+        project.build.dependsOn project.tasks.withType(Checkstyle)
+    }
+}
+
+apply plugin: FakeCheckstylePlugin
+
+extensions.create("custom", CustomExtension)
+
+
+task extraTask1(type: MyTaskClass) {
+    description = name
+    doLast {}
+}
+
+task extraTask2(type: MyTaskClass) {
+    description = name
+    doLast {}
+}
+
+task extraTask3(type: MyTaskClass) {
+    description = name
+    doLast {}
+}
+
+task extraTask4(type: MyTaskClass) {
+    description = name
+    doLast {}
+}
+
+tasks.withType(MyTaskClass).all {
+    doLast(custom.action)
+    10.times {
+        description += custom.value
+    }
+}
+
+
+class MyTaskClass extends DefaultTask {
+
+    @InputFiles
+    FileCollection input
+
+    @OutputFiles
+    FileCollection output
+
+    @Input
+    boolean flag
+
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/project-with-source/build.gradle b/subprojects/performance/src/templates/project-with-source/build.gradle
index e47923b..f842723 100644
--- a/subprojects/performance/src/templates/project-with-source/build.gradle
+++ b/subprojects/performance/src/templates/project-with-source/build.gradle
@@ -11,20 +11,27 @@ repositories {
     mavenCentral()
 }
 
+<% if (binding.hasVariable("projectDependencies")) { %>
+    configurations {
+        compile.extendsFrom projectsConfiguration
+    }
+<% } %>
+
 dependencies {
     compile 'commons-lang:commons-lang:2.5'
     compile "commons-httpclient:commons-httpclient:3.0"
     compile "commons-codec:commons-codec:1.2"
-    compile "org.slf4j:jcl-over-slf4j:1.7.5"
-    compile "org.codehaus.groovy:groovy:2.0.5"
-    compile "commons-codec:commons-codec:1.2"
-    testCompile 'junit:junit:4.11'
-    testCompile 'org.testng:testng:6.4'
+    compile "org.slf4j:jcl-over-slf4j:1.7.10"
+    compile "org.codehaus.groovy:groovy:2.3.10"
+    testCompile 'junit:junit:4.12'
     runtime 'com.googlecode:reflectasm:1.01'
 
     <% if (dependencies) { dependencies.each { %>
     compile "${it.shortNotation()}" <% } %>
     <% } %>
+    <% if (binding.hasVariable("projectDependencies") && subprojectNumber > 1) { (1..<subprojectNumber).each { %>
+    projectsConfiguration project(":project${it}") <% } %>
+    <% } %>
 }
 
 test {
@@ -34,7 +41,7 @@ test {
 <% if (groovyProject) { %>
 apply plugin: 'groovy'
 dependencies {
-    compile 'org.codehaus:groovy:groovy-all:2.0.5'
+    compile 'org.codehaus:groovy:groovy-all:2.3.10'
 }
 <% } %>
 
@@ -52,6 +59,12 @@ tasks.withType(ScalaCompile) {
 }
 <% } %>
 
+<% if (binding.hasVariable("resolveDependenciesTask")) { %>
+task resolveDependencies {
+    dependsOn configurations.testRuntime
+}
+<% } %>
+
 task dependencyReport(type: DependencyReportTask) {
     outputs.upToDateWhen { false }
     outputFile = new File(buildDir, "dependencies.txt")
diff --git a/subprojects/performance/src/templates/project-with-source/pom.xml b/subprojects/performance/src/templates/project-with-source/pom.xml
index cb71573..6978411 100644
--- a/subprojects/performance/src/templates/project-with-source/pom.xml
+++ b/subprojects/performance/src/templates/project-with-source/pom.xml
@@ -32,12 +32,12 @@
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
-            <version>1.6.6</version>
+            <version>1.7.7</version>
         </dependency>
         <dependency>
             <groupId>org.codehaus.groovy</groupId>
             <artifactId>groovy</artifactId>
-            <version>2.0.5</version>
+            <version>2.3.9</version>
         </dependency>
         <dependency>
             <groupId>com.googlecode</groupId>
@@ -48,13 +48,7 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.11</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-            <version>6.4</version>
+            <version>4.12</version>
             <scope>test</scope>
         </dependency>
     <% if (dependencies) { dependencies.each { dep -> %>
diff --git a/subprojects/performance/src/templates/root-project/pom.xml b/subprojects/performance/src/templates/root-project/pom.xml
index 3dc02df..709a0b0 100644
--- a/subprojects/performance/src/templates/root-project/pom.xml
+++ b/subprojects/performance/src/templates/root-project/pom.xml
@@ -4,6 +4,7 @@
     <groupId>org.gradle</groupId>
     <artifactId>${projectName}</artifactId>
     <packaging>pom</packaging>
+    <version>1.0</version>
     <modules>
         <% subprojects.each { out.println "<module>$it</module>" } %>
     </modules>
diff --git a/subprojects/performance/src/templates/settings.gradle b/subprojects/performance/src/templates/settings.gradle
index 529201d..e51d235 100644
--- a/subprojects/performance/src/templates/settings.gradle
+++ b/subprojects/performance/src/templates/settings.gradle
@@ -1 +1,7 @@
-<% subprojects.each { out.println "include '$it'"} %>
\ No newline at end of file
+<%
+    def i = 0
+    def groups = subprojects.collect { "'$it'" }.groupBy { i++ % 100 }
+    groups.each {
+        println "include " + it.value.join(", ")
+    }
+%>
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/variants-new-model/build.gradle b/subprojects/performance/src/templates/variants-new-model/build.gradle
new file mode 100644
index 0000000..ef44546
--- /dev/null
+++ b/subprojects/performance/src/templates/variants-new-model/build.gradle
@@ -0,0 +1,194 @@
+import org.gradle.model.*
+import org.gradle.model.collection.*
+
+ at Managed
+interface Domain {
+    ManagedSet<Flavour> getFlavours()
+    ManagedSet<Type> getTypes()
+    ManagedSet<Variant> getVariants()
+}
+
+ at Managed
+interface Flavour {
+    String getName()
+    void setName(String name)
+    String getP1()
+    void setP1(String p1)
+    String getP2()
+    void setP2(String p2)
+    String getP3()
+    void setP3(String p3)
+    String getP4()
+    void setP4(String p4)
+}
+
+ at Managed
+interface Type {
+    String getName()
+    void setName(String name)
+    String getP1()
+    void setP1(String p1)
+    String getP2()
+    void setP2(String p2)
+    String getP3()
+    void setP3(String p3)
+    String getP4()
+    void setP4(String p4)
+}
+
+ at Managed
+abstract class Variant {
+    abstract Flavour getFlavour()
+    abstract void setFlavour(Flavour flavour)
+
+    abstract Type getType()
+    abstract void setType(Type type)
+
+    abstract String getP1()
+    abstract void setP1(String p1)
+    abstract String getP2()
+    abstract void setP2(String p2)
+    abstract String getP3()
+    abstract void setP3(String p3)
+    abstract String getP4()
+    abstract void setP4(String p4)
+
+    String getName() {
+        flavour.name + type.name
+    }
+}
+
+ at Managed
+interface Extension1 {
+    String getP1()
+    void setP1(String p1)
+    String getP2()
+    void setP2(String p2)
+}
+
+ at Managed
+interface Extension2 {
+    String getP3()
+    void setP3(String p3)
+    String getP4()
+    void setP4(String p4)
+}
+
+model {
+    domain(Domain)
+    e1(Extension1) {
+        p1 = "p1"
+        p2 = "p2"
+    }
+    e2(Extension2) {
+        p3 = "p3"
+        p4 = "p4"
+    }
+    domain.flavours {
+        <% flavourCount.times { %>
+            create {
+                name = "flavour${it + 1}"
+                p3 = \$("e2.p3")
+                p4 = \$("e2.p4")
+            }
+        <% } %>
+    }
+    domain.types {
+        <% typeCount.times { %>
+            create {
+                name = "type${it + 1}"
+                p3 = \$("e2.p3")
+                p4 = \$("e2.p4")
+            }
+        <% } %>
+    }
+    domain.variants { v ->
+        \$("domain.flavours").each { flavour ->
+            \$("domain.types").each { type ->
+                v.create {
+                    p3 = \$("e2.p3")
+                    p4 = \$("e2.p4")
+                    it.flavour = flavour
+                    it.type = type
+                }
+            }
+        }
+    }
+    tasks {
+        20.times {
+            create("extraTask\$it", MyTaskClass) {
+                description = name
+                doLast {}
+            }
+        }
+    }
+    tasks { t ->
+        \$("domain.variants").each { variant ->
+            def t1 = t.create(variant.name + "_t1", CustomTask) {
+                p3 = \$("e2.p3")
+                p4 = \$("e2.p4")
+            }
+            def t2 = t.create(variant.name + "_t2", CustomTask) {
+                p3 = \$("e2.p3")
+                p4 = \$("e2.p4")
+            }
+            def t3 = t.create(variant.name + "_t3", CustomTask) {
+                p3 = \$("e2.p3")
+                p4 = \$("e2.p4")
+            }
+        }
+    }
+    tasks {
+        create("allVariants") {
+            dependsOn \$("domain.variants")*.name.collect { ["\${it}_t1", "\${it}_t2", "\${it}_t3"] }
+        }
+    }
+    domain.flavours {
+        beforeEach {
+            it.p1 = \$("e1.p1")
+            it.p2 = \$("e1.p2")
+        }
+    }
+    domain.types {
+        beforeEach {
+            it.p1 = \$("e1.p1")
+            it.p2 = \$("e1.p2")
+        }
+    }
+    domain.variants {
+        beforeEach {
+            it.p1 = \$("e1.p1")
+            it.p2 = \$("e1.p2")
+        }
+    }
+    tasks {
+        withType(CustomTask) {
+            it.p1 = \$("e1.p1")
+            it.p2 = \$("e1.p2")
+        }
+    }
+}
+
+class CustomTask extends DefaultTask {
+    @Input
+    String p1
+    @Input
+    String p2
+    @Input
+    String p3
+    @Input
+    String p4
+}
+
+class MyTaskClass extends DefaultTask {
+
+    @InputFiles
+    FileCollection input
+
+    @OutputFiles
+    FileCollection output
+
+    @Input
+    boolean flag
+
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/variants-old-model/build.gradle b/subprojects/performance/src/templates/variants-old-model/build.gradle
new file mode 100644
index 0000000..17c4bdb
--- /dev/null
+++ b/subprojects/performance/src/templates/variants-old-model/build.gradle
@@ -0,0 +1,167 @@
+class Domain {
+    NamedDomainObjectContainer<Flavour> flavours
+    NamedDomainObjectContainer<Type> types
+    NamedDomainObjectContainer<Variant> variants
+
+    Domain(Project project) {
+        flavours = project.container(Flavour) { new Flavour(name: it) }
+        types = project.container(Type) { new Type(name: it) }
+        variants = project.container(Variant) { new Variant(name: it) }
+    }
+
+    void flavours(Closure closure) {
+        ConfigureUtil.configure(closure, flavours)
+    }
+
+    void types(Closure closure) {
+        ConfigureUtil.configure(closure, types)
+    }
+}
+
+class Extension1 {
+    String p1
+    String p2
+}
+
+class Extension2 {
+    String p3
+    String p4
+}
+
+class Flavour {
+    String name
+    String p1
+    String p2
+    String p3
+    String p4
+}
+
+class Type {
+    String name
+    String p1
+    String p2
+    String p3
+    String p4
+}
+
+class Variant {
+    String name
+    Flavour flavour
+    Type type
+    String p1
+    String p2
+    String p3
+    String p4
+}
+
+extensions.create("domain", Domain, project)
+extensions.create("e1", Extension1)
+extensions.create("e2", Extension2)
+
+domain.flavours.all {
+    it.p1 = e1.p1
+    it.p2 = e1.p2
+}
+
+domain.types.all {
+    it.p1 = e1.p1
+    it.p2 = e1.p2
+}
+
+domain.variants.all {
+    it.p1 = e1.p1
+    it.p2 = e1.p2
+}
+
+tasks.withType(CustomTask) {
+    it.p1 = e1.p1
+    it.p2 = e1.p2
+}
+
+task allVariants
+
+domain {
+    e1 {
+        p1 = "p1"
+        p2 = "p2"
+    }
+    e2 {
+        p3 = "p3"
+        p4 = "p4"
+    }
+    variants.all { variant ->
+        def t1 = tasks.create(variant.name + "_t1", CustomTask) {
+            p3 = e2.p3
+            p4 = e2.p4
+        }
+        def t2 = tasks.create(variant.name + "_t2", CustomTask) {
+            p3 = e2.p3
+            p4 = e2.p4
+        }
+        def t3 = tasks.create(variant.name + "_t3", CustomTask) {
+            p3 = e2.p3
+            p4 = e2.p4
+        }
+        allVariants.dependsOn t1, t2, t3
+    }
+
+    flavours.all { flavour ->
+        types.all { type ->
+            variants.create("\${flavour.name + type.name}") {
+                p3 = e2.p3
+                p4 = e2.p4
+                it.flavour = flavour
+                it.type = type
+            }
+        }
+    }
+
+    flavours {
+        <% flavourCount.times { %>
+        flavour<%= it + 1 %> {
+            p3 = e2.p3
+            p4 = e2.p4
+        }
+        <% } %>
+    }
+
+    types {
+        <% typeCount.times { %>
+        type<%= it + 1 %> {
+            p3 = e2.p3
+            p4 = e2.p4
+        }
+        <% } %>
+    }
+}
+
+class CustomTask extends DefaultTask {
+    @Input
+    String p1
+    @Input
+    String p2
+    @Input
+    String p3
+    @Input
+    String p4
+}
+
+20.times {
+    tasks.create("extraTask\$it", MyTaskClass) {
+        description = name
+        doLast {}
+    }
+}
+
+class MyTaskClass extends DefaultTask {
+
+    @InputFiles
+    FileCollection input
+
+    @OutputFiles
+    FileCollection output
+
+    @Input
+    boolean flag
+
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/templates/with-testng/build.gradle b/subprojects/performance/src/templates/with-testng/build.gradle
index 05a9cd8..b0691b8 100644
--- a/subprojects/performance/src/templates/with-testng/build.gradle
+++ b/subprojects/performance/src/templates/with-testng/build.gradle
@@ -1,7 +1,10 @@
 ${original}
 
+dependencies {
+    testCompile 'org.testng:testng:6.4'
+}
+
 test {
     useTestNG()
     maxParallelForks = 2
-    testReport = true
 }
diff --git a/subprojects/performance/src/templates/with-verbose-testng/build.gradle b/subprojects/performance/src/templates/with-verbose-testng/build.gradle
index 05a9cd8..b0691b8 100644
--- a/subprojects/performance/src/templates/with-verbose-testng/build.gradle
+++ b/subprojects/performance/src/templates/with-verbose-testng/build.gradle
@@ -1,7 +1,10 @@
 ${original}
 
+dependencies {
+    testCompile 'org.testng:testng:6.4'
+}
+
 test {
     useTestNG()
     maxParallelForks = 2
-    testReport = true
 }
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/ResultSpecification.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/ResultSpecification.groovy
index ece5b64..0db9ac2 100644
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/ResultSpecification.groovy
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/ResultSpecification.groovy
@@ -16,16 +16,17 @@
 
 package org.gradle.performance
 
-import org.gradle.performance.fixture.PerformanceResults
+import org.gradle.performance.fixture.CrossBuildPerformanceResults
+import org.gradle.performance.fixture.CrossVersionPerformanceResults
 import org.gradle.performance.measure.Amount
 import org.gradle.performance.measure.DataAmount
 import org.gradle.performance.measure.Duration
 import org.gradle.performance.measure.MeasuredOperation
 import spock.lang.Specification
 
-class ResultSpecification extends Specification {
-    PerformanceResults results(Map<String, ?> options = [:]) {
-        def results = new PerformanceResults()
+abstract class ResultSpecification extends Specification {
+    CrossVersionPerformanceResults crossVersionResults(Map<String, ?> options = [:]) {
+        def results = new CrossVersionPerformanceResults()
         results.testId = "test-id"
         results.testProject = "test-project"
         results.tasks = ["clean", "build"]
@@ -38,9 +39,26 @@ class ResultSpecification extends Specification {
         return results
     }
 
+    CrossBuildPerformanceResults crossBuildResults(Map<String, ?> options = [:]) {
+        def results = new CrossBuildPerformanceResults(
+                testId: "test-id",
+                testGroup: "test-group",
+                jvm: "java 7",
+                versionUnderTest: "Gradle 1.0",
+                operatingSystem: "windows",
+                testTime: 100,
+                vcsBranch: "master",
+                vcsCommit: "abcdef"
+        )
+        options.each { key, value -> results."$key" = value }
+        return results
+    }
+
     MeasuredOperation operation(Map<String, Object> args = [:]) {
         def operation = new MeasuredOperation()
-        operation.executionTime = args.executionTime instanceof Amount ? args.executionTime : Duration.millis(args?.executionTime ?: 120)
+        operation.totalTime = args.totalTime instanceof Amount ? args.totalTime : Duration.millis(args?.totalTime ?: 120)
+        operation.configurationTime = args.configurationTime instanceof Amount ? args.configurationTime : Duration.millis(args.configurationTime ?: 30);
+        operation.executionTime = args.executionTime instanceof Amount ? args.executionTime : Duration.millis(args.configurationTime ?: 80);
         operation.totalMemoryUsed = args.totalMemoryUsed instanceof Amount ? args.totalMemoryUsed : DataAmount.bytes(args?.totalMemoryUsed ?: 1024)
         operation.totalHeapUsage = args.totalHeapUsage instanceof Amount ? args.totalHeapUsage : DataAmount.bytes(args?.totalHeapUsage ?: 4096)
         operation.maxHeapUsage = args.maxHeapUsage instanceof Amount ? args.maxHeapUsage : DataAmount.bytes(args?.maxHeapUsage ?: 2000)
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/BuildEventTimestampCollectorTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/BuildEventTimestampCollectorTest.groovy
new file mode 100644
index 0000000..46fcab5
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/BuildEventTimestampCollectorTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.performance.measure.Duration
+import org.gradle.performance.measure.MeasuredOperation
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class BuildEventTimestampCollectorTest extends Specification {
+
+    private static final String FILENAME = "timestamps.txt"
+
+    @Rule TestNameTestDirectoryProvider directoryProvider = new TestNameTestDirectoryProvider()
+
+    MeasuredOperation measuredOperation = new MeasuredOperation()
+    BuildEventTimestampCollector collector = new BuildEventTimestampCollector(FILENAME)
+
+    private void collect() {
+        collector.collect(directoryProvider.testDirectory, measuredOperation)
+    }
+
+    private void timestampFileContents(String contents) {
+        directoryProvider.file(FILENAME) << TextUtil.toPlatformLineSeparators(contents)
+    }
+
+    private String getLogFilePath() {
+        directoryProvider.testDirectory.file(FILENAME).absolutePath
+    }
+
+    def "throws when output file is not found"() {
+        when:
+        collect()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == "Could not find $logFilePath. Cannot collect build event timestamps."
+    }
+
+    def "throws when file does not contain 3 lines"() {
+        given:
+        timestampFileContents """1425907240
+1425907245
+"""
+        when:
+        collect()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == "Build event timestamp log at $logFilePath should contain exactly 3 lines."
+    }
+
+    def "throws when file contains anything that can't be parsed to a long"() {
+        given:
+        timestampFileContents """null
+1425907240
+1425907245"""
+
+        when:
+        collect()
+
+        then:
+        IllegalStateException e = thrown()
+        e.message == "One of the timestamps in build event timestamp log at $logFilePath is not valid."
+        e.cause instanceof NumberFormatException
+    }
+
+    def "configuration time and execution time are collected"() {
+        given:
+        timestampFileContents """1425907240
+1425907245
+1425907255"""
+
+        when:
+        collect()
+
+        then:
+        measuredOperation.configurationTime == Duration.millis(5)
+        measuredOperation.executionTime == Duration.millis(10)
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResultsTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResultsTest.groovy
new file mode 100644
index 0000000..7e4232e
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResultsTest.groovy
@@ -0,0 +1,287 @@
+/*
+ * 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.performance.fixture
+
+import org.gradle.performance.ResultSpecification
+import org.gradle.performance.measure.DataAmount
+import org.gradle.performance.measure.Duration
+
+class CrossVersionPerformanceResultsTest extends ResultSpecification {
+    def CrossVersionPerformanceResults result = new CrossVersionPerformanceResults(testProject: "some-project", tasks: [])
+
+    def "passes when average execution time for current release is smaller than average execution time for previous releases"() {
+        given:
+        result.baseline("1.0").results.add(operation(totalTime: 110))
+        result.baseline("1.0").results.add(operation(totalTime: 100))
+        result.baseline("1.0").results.add(operation(totalTime: 90))
+
+        and:
+        result.current.add(operation(totalTime: 90))
+        result.current.add(operation(totalTime: 110))
+        result.current.add(operation(totalTime: 90))
+
+        expect:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+
+    def "passes when average execution time for current release is within specified range of average execution time for previous releases"() {
+        given:
+        result.baseline("1.0").maxExecutionTimeRegression = Duration.millis(10)
+        result.baseline("1.0").results << operation(totalTime: 100)
+        result.baseline("1.0").results << operation(totalTime: 100)
+        result.baseline("1.0").results << operation(totalTime: 100)
+
+        result.baseline("1.3").results << operation(totalTime: 115)
+        result.baseline("1.3").results << operation(totalTime: 105)
+        result.baseline("1.3").results << operation(totalTime: 110)
+
+        and:
+        result.current << operation(totalTime: 110)
+        result.current << operation(totalTime: 110)
+        result.current << operation(totalTime: 110)
+
+        expect:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+
+    def "fails when average execution time for current release is larger than average execution time for previous releases"() {
+        given:
+        result.baseline("1.0").maxExecutionTimeRegression = Duration.millis(10)
+        result.baseline("1.0").results << operation(totalTime: 100)
+        result.baseline("1.0").results << operation(totalTime: 100)
+        result.baseline("1.0").results << operation(totalTime: 100)
+
+        result.baseline("1.3").maxExecutionTimeRegression = Duration.millis(10)
+        result.baseline("1.3").results << operation(totalTime: 101)
+        result.baseline("1.3").results << operation(totalTime: 100)
+        result.baseline("1.3").results << operation(totalTime: 100)
+
+        and:
+        result.current << operation(totalTime: 110)
+        result.current << operation(totalTime: 110)
+        result.current << operation(totalTime: 111)
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.startsWith("Speed ${result.displayName}: we're slower than 1.0.")
+        e.message.contains('Difference: 10.333 ms slower (10.333 ms), 10.33%')
+        !e.message.contains('1.3')
+    }
+
+    def "passes when average heap usage for current release is smaller than average heap usage for previous releases"() {
+        given:
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+
+        result.baseline("1.3").results << operation(totalMemoryUsed: 800)
+        result.baseline("1.3").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.3").results << operation(totalMemoryUsed: 1200)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1000)
+        result.current << operation(totalMemoryUsed: 1005)
+        result.current << operation(totalMemoryUsed: 994)
+
+        expect:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+
+    def "passes when average heap usage for current release is slightly larger than average heap usage for previous releases"() {
+        given:
+        result.baseline("1.0").maxMemoryRegression = DataAmount.bytes(100)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
+
+        result.baseline("1.3").maxMemoryRegression = DataAmount.bytes(100)
+        result.baseline("1.3").results << operation(totalMemoryUsed: 900)
+        result.baseline("1.3").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.3").results << operation(totalMemoryUsed: 1100)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1100)
+        result.current << operation(totalMemoryUsed: 1100)
+        result.current << operation(totalMemoryUsed: 1100)
+
+        expect:
+        result.assertCurrentVersionHasNotRegressed()
+    }
+
+    def "fails when average heap usage for current release is larger than average heap usage for previous releases"() {
+        given:
+        result.baseline("1.0").maxMemoryRegression = DataAmount.bytes(100)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
+
+        result.baseline("1.2").maxMemoryRegression = DataAmount.bytes(100)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1100)
+        result.current << operation(totalMemoryUsed: 1100)
+        result.current << operation(totalMemoryUsed: 1101)
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.startsWith("Memory ${result.displayName}: we need more memory than 1.2.")
+        e.message.contains('Difference: 100.333 B more (100.333 B), 10.03%')
+        !e.message.contains('than 1.0')
+    }
+
+    def "fails when both heap usage and execution time have regressed"() {
+        given:
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 150)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1000, totalTime: 100)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 150)
+
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, totalTime: 100)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, totalTime: 100)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, totalTime: 100)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1100, totalTime: 110)
+        result.current << operation(totalMemoryUsed: 1100, totalTime: 110)
+        result.current << operation(totalMemoryUsed: 1101, totalTime: 111)
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.contains("Speed ${result.displayName}: we're slower than 1.2.")
+        e.message.contains('Difference: 10.333 ms slower (10.333 ms)')
+        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
+        e.message.contains('Difference: 100.333 B more (100.333 B)')
+        !e.message.contains('than 1.0')
+    }
+
+    def "fails when a previous operation fails"() {
+        given:
+        result.baseline("1.0").results << operation(failure: new RuntimeException())
+        result.current.add(operation())
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.startsWith("Some builds have failed.")
+    }
+
+    def "fails when a current operation fails"() {
+        given:
+        result.baseline("1.0").results << operation()
+        result.current.add(operation(failure: new RuntimeException()))
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.startsWith("Some builds have failed.")
+    }
+
+    def "fails when an operation fails"() {
+        given:
+        result.current.add(operation())
+        result.baseline("1.0").results << operation()
+        result.baseline("oldVersion").results << operation(failure: new RuntimeException())
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.startsWith("Some builds have failed.")
+    }
+
+    def "fails if one of the baseline version is faster and the other needs less memory"() {
+        given:
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 100)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 100)
+
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, totalTime: 150)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, totalTime: 150)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1100, totalTime: 125)
+        result.current << operation(totalMemoryUsed: 1100, totalTime: 125)
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.contains("Speed ${result.displayName}: we're slower than 1.0.")
+        e.message.contains('Difference: 25 ms slower')
+        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
+        e.message.contains('Difference: 100 B more')
+        e.message.count(result.displayName) == 2
+    }
+
+    def "fails if all of the baseline versions are better in every respect"() {
+        given:
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 120)
+        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, totalTime: 120)
+
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1100, totalTime: 150)
+        result.baseline("1.2").results << operation(totalMemoryUsed: 1100, totalTime: 150)
+
+        and:
+        result.current << operation(totalMemoryUsed: 1300, totalTime: 200)
+        result.current << operation(totalMemoryUsed: 1300, totalTime: 200)
+
+        when:
+        result.assertCurrentVersionHasNotRegressed()
+
+        then:
+        AssertionError e = thrown()
+        e.message.contains("Speed ${result.displayName}: we're slower than 1.0.")
+        e.message.contains("Speed ${result.displayName}: we're slower than 1.2.")
+        e.message.contains("Memory ${result.displayName}: we need more memory than 1.0.")
+        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
+    }
+
+    def "can lookup the results for a baseline version"() {
+        expect:
+        def baseline = result.baseline("1.0")
+        baseline.version == "1.0"
+
+        and:
+        result.baseline("1.0") == baseline
+        result.version("1.0") == baseline
+    }
+
+    def "can lookup the current results using the branch as the version name"() {
+        given:
+        result.vcsBranch = 'master'
+
+        expect:
+        def version = result.version('master')
+        version.results.is(result.current)
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunnerTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunnerTest.groovy
new file mode 100644
index 0000000..b97e0fc
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunnerTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
+import org.gradle.performance.ResultSpecification
+import org.gradle.performance.measure.DataAmount
+import org.gradle.performance.measure.Duration
+import org.gradle.util.GradleVersion
+
+class CrossVersionPerformanceTestRunnerTest extends ResultSpecification {
+    final experimentRunner = Mock(BuildExperimentRunner)
+    final reporter = Mock(DataReporter)
+    final testProjectLocator = Stub(TestProjectLocator)
+    final currentGradle = Stub(GradleDistribution)
+    final mostRecentRelease = new ReleasedVersionDistributions().mostRecentFinalRelease.version.version
+    final currentVersionBase = GradleVersion.current().baseVersion.version
+
+    def "runs test and builds results"() {
+        given:
+        def runner = runner()
+        runner.testId = 'some-test'
+        runner.testProject = 'test1'
+        runner.targetVersions = ['1.0', '1.1']
+        runner.tasksToRun = ['clean', 'build']
+        runner.args = ['--arg1', '--arg2']
+        runner.warmUpRuns = 1
+        runner.runs = 4
+        runner.maxExecutionTimeRegression = Duration.millis(100)
+        runner.maxMemoryRegression = DataAmount.bytes(10)
+
+        when:
+        def results = runner.run()
+
+        then:
+        results.testId == 'some-test'
+        results.testProject == 'test1'
+        results.tasks == ['clean', 'build']
+        results.args == ['--arg1', '--arg2']
+        results.versionUnderTest
+        results.jvm
+        results.operatingSystem
+        results.current.size() == 4
+        results.current.totalTime.average == Duration.seconds(10)
+        results.current.totalMemoryUsed.average == DataAmount.kbytes(10)
+        results.baselineVersions*.version == ['1.0', '1.1']
+        results.baseline('1.0').results.size() == 4
+        results.baseline('1.1').results.size() == 4
+        results.baselineVersions.every { it.maxExecutionTimeRegression == runner.maxExecutionTimeRegression }
+        results.baselineVersions.every { it.maxMemoryRegression == runner.maxMemoryRegression }
+
+        and:
+        3 * experimentRunner.run(_, _) >> { BuildExperimentSpec spec, MeasuredOperationList result ->
+            result.add(operation(totalTime: Duration.seconds(10), totalMemoryUsed: DataAmount.kbytes(10)))
+            result.add(operation(totalTime: Duration.seconds(10), totalMemoryUsed: DataAmount.kbytes(10)))
+            result.add(operation(totalTime: Duration.seconds(10), totalMemoryUsed: DataAmount.kbytes(10)))
+            result.add(operation(totalTime: Duration.seconds(10), totalMemoryUsed: DataAmount.kbytes(10)))
+        }
+        1 * reporter.report(_)
+        0 * reporter._
+    }
+
+    def "can use 'last' baseline version to refer to most recently released version"() {
+        given:
+        def runner = runner()
+        runner.targetVersions = ['1.0', 'last']
+
+        when:
+        def results = runner.run()
+
+        then:
+        results.baselineVersions*.version == ['1.0', mostRecentRelease]
+    }
+
+    def "ignores baseline version if it has the same base as the version under test"() {
+        given:
+        def runner = runner()
+        runner.targetVersions = ['1.0', currentVersionBase, mostRecentRelease, 'last']
+
+        when:
+        def results = runner.run()
+
+        then:
+        results.baselineVersions*.version == ['1.0', mostRecentRelease]
+    }
+
+    def runner() {
+        def runner = new CrossVersionPerformanceTestRunner(experimentRunner, reporter)
+        runner.testId = 'some-test'
+        runner.testProjectLocator = testProjectLocator
+        runner.current = currentGradle
+        runner.runs = 1
+        return runner
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCEventParserTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCEventParserTest.groovy
new file mode 100644
index 0000000..fa73151
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCEventParserTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.joda.time.DateTime
+import org.joda.time.DateTimeZone
+import spock.lang.Specification
+import spock.lang.Subject
+
+class GCEventParserTest extends Specification {
+
+    @Subject parser = new GCEventParser((char) '.')
+
+    def "parses event"() {
+        when:
+        def e = parser.parseLine "2015-01-22T16:04:50.319+0000: [Full GC (System) [PSYoungGen: 2048K->0K(114688K)] [PSOldGen: 24097K->26017K(262080K)] 26145K->26017K(376768K) [PSPermGen: 41509K->41509K(77696K)], 0.1944213 secs] [Times: user=0.20 sys=0.00, real=0.19 secs] "
+
+        then:
+        e.timestamp == new DateTime(2015, 1, 22, 16, 4, 50, 319, DateTimeZone.default) // timezone information is discarded
+        e.start == 26145
+        e.committed == 376768
+        e.end == 26017
+
+        when:
+        e = parser.parseLine "2015-02-12T07:14:50.459-1000: [GC [DefNew: 2560K->319K(2880K), 0.0034420 secs] 2560K->588K(9408K), 0.0034820 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] "
+
+        then:
+        e.timestamp == new DateTime(2015, 2, 12, 7, 14, 50, 459, DateTimeZone.default)
+        e.start == 2560
+        e.committed == 9408
+        e.end == 588
+    }
+
+    def "reports unrecognized events"() {
+        when:
+        parser.parseLine "foo bar"
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message.contains "foo bar"
+    }
+
+    def "ignores events that seem to happen on windows"() {
+        when:
+        def e = parser.parseLine " [Times: user=0.20 sys=0.00, real=0.19 secs] "
+
+        then:
+        e == GCEventParser.GCEvent.IGNORED
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCLoggingCollectorTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCLoggingCollectorTest.groovy
index 051f32c..181c731 100644
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCLoggingCollectorTest.groovy
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/GCLoggingCollectorTest.groovy
@@ -16,29 +16,31 @@
 
 package org.gradle.performance.fixture
 
-import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.performance.measure.DataAmount
 import org.gradle.performance.measure.MeasuredOperation
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.Resources
+import org.joda.time.DateTime
 import org.junit.Rule
 import spock.lang.Specification
 import spock.lang.Unroll
 
 class GCLoggingCollectorTest extends Specification {
-    @Rule Resources resources = new Resources()
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule
+    Resources resources = new Resources()
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     def collector = new GCLoggingCollector()
 
     @Unroll
-    def "parses GC Log #logName"() {
-        def operation = new MeasuredOperation()
+    def "parses GC Log #logName with locale #locale"() {
+        def operation = new MeasuredOperation(start: DateTime.parse("2015-01-22T$startTime"), end: DateTime.parse("2015-01-22T$endTime"))
         def projectDir = tmpDir.createDir("project")
         resources.getResource(logName).copyTo(projectDir.file("gc.txt"))
 
         when:
-        collector.beforeExecute(projectDir, Stub(GradleExecuter))
-        collector.collect(projectDir, operation)
+        collector.getAdditionalGradleOpts(projectDir)
+        collector.collect(operation, locale)
 
         then:
         operation.totalHeapUsage == DataAmount.kbytes(totalHeapUsage)
@@ -47,9 +49,14 @@ class GCLoggingCollectorTest extends Specification {
         operation.maxCommittedHeap == DataAmount.kbytes(maxCommittedHeap)
 
         where:
-        logName    | totalHeapUsage | maxHeapUsage | maxUncollectedHeap | maxCommittedHeap
-        "gc-1.txt" | 76639          | 33334        | 20002              | 44092
-        "gc-2.txt" | 140210         | 40427        | 34145              | 223360
-        "gc-3.txt" | 183544         | 119384       | 37982              | 295488
+        logName             | totalHeapUsage | maxHeapUsage | maxUncollectedHeap | maxCommittedHeap | locale         | startTime      | endTime
+        "gc-1.txt"          | 76639          | 33334        | 20002              | 44092            | Locale.US      | "16:04:50.000" | "16:05:00.000"
+        "gc-2.txt"          | 140210         | 40427        | 34145              | 223360           | Locale.US      | "16:04:50.000" | "16:05:00.000"
+        "gc-3.txt"          | 183544         | 119384       | 37982              | 295488           | Locale.US      | "16:04:50.000" | "16:05:00.000"
+        "gc-4.txt"          | 709909         | 292868       | 86455              | 474176           | Locale.GERMANY | "16:04:50.000" | "16:05:00.000"
+        "win-1.txt"         | 76639          | 33334        | 20002              | 44092            | Locale.US      | "16:04:50.000" | "16:05:00.000"
+        "gc-3.txt"          | 40704          | 18661        | 6827               | 65472            | Locale.US      | "16:04:50.519" | "16:04:51.319"
+        "gc-3.txt"          | 142840         | 119384       | 37982              | 295488           | Locale.US      | "16:04:51.319" | "16:04:52.519"
+        "mac-jdk8.0.25.txt" | 48177          | 49967        | 13008              | 308224           | Locale.US      | "16:04:51.319" | "16:04:52.519"
     }
 }
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceResultsTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceResultsTest.groovy
deleted file mode 100644
index 396b7e5..0000000
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceResultsTest.groovy
+++ /dev/null
@@ -1,287 +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.performance.fixture
-
-import org.gradle.performance.ResultSpecification
-import org.gradle.performance.measure.DataAmount
-import org.gradle.performance.measure.Duration
-
-class PerformanceResultsTest extends ResultSpecification {
-    def PerformanceResults result = new PerformanceResults(testProject: "some-project", tasks: [])
-
-    def "passes when average execution time for current release is smaller than average execution time for previous releases"() {
-        given:
-        result.baseline("1.0").results.add(operation(executionTime: 110))
-        result.baseline("1.0").results.add(operation(executionTime: 100))
-        result.baseline("1.0").results.add(operation(executionTime: 90))
-
-        and:
-        result.current.add(operation(executionTime: 90))
-        result.current.add(operation(executionTime: 110))
-        result.current.add(operation(executionTime: 90))
-
-        expect:
-        result.assertCurrentVersionHasNotRegressed()
-    }
-
-    def "passes when average execution time for current release is within specified range of average execution time for previous releases"() {
-        given:
-        result.baseline("1.0").maxExecutionTimeRegression = Duration.millis(10)
-        result.baseline("1.0").results << operation(executionTime: 100)
-        result.baseline("1.0").results << operation(executionTime: 100)
-        result.baseline("1.0").results << operation(executionTime: 100)
-
-        result.baseline("1.3").results << operation(executionTime: 115)
-        result.baseline("1.3").results << operation(executionTime: 105)
-        result.baseline("1.3").results << operation(executionTime: 110)
-
-        and:
-        result.current << operation(executionTime: 110)
-        result.current << operation(executionTime: 110)
-        result.current << operation(executionTime: 110)
-
-        expect:
-        result.assertCurrentVersionHasNotRegressed()
-    }
-
-    def "fails when average execution time for current release is larger than average execution time for previous releases"() {
-        given:
-        result.baseline("1.0").maxExecutionTimeRegression = Duration.millis(10)
-        result.baseline("1.0").results << operation(executionTime: 100)
-        result.baseline("1.0").results << operation(executionTime: 100)
-        result.baseline("1.0").results << operation(executionTime: 100)
-
-        result.baseline("1.3").maxExecutionTimeRegression = Duration.millis(10)
-        result.baseline("1.3").results << operation(executionTime: 101)
-        result.baseline("1.3").results << operation(executionTime: 100)
-        result.baseline("1.3").results << operation(executionTime: 100)
-
-        and:
-        result.current << operation(executionTime: 110)
-        result.current << operation(executionTime: 110)
-        result.current << operation(executionTime: 111)
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.startsWith("Speed ${result.displayName}: we're slower than 1.0.")
-        e.message.contains('Difference: 10.333 ms slower (10.333 ms), 10.33%')
-        !e.message.contains('1.3')
-    }
-
-    def "passes when average heap usage for current release is smaller than average heap usage for previous releases"() {
-        given:
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-
-        result.baseline("1.3").results << operation(totalMemoryUsed: 800)
-        result.baseline("1.3").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.3").results << operation(totalMemoryUsed: 1200)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1000)
-        result.current << operation(totalMemoryUsed: 1005)
-        result.current << operation(totalMemoryUsed: 994)
-
-        expect:
-        result.assertCurrentVersionHasNotRegressed()
-    }
-
-    def "passes when average heap usage for current release is slightly larger than average heap usage for previous releases"() {
-        given:
-        result.baseline("1.0").maxMemoryRegression = DataAmount.bytes(100)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000)
-
-        result.baseline("1.3").maxMemoryRegression = DataAmount.bytes(100)
-        result.baseline("1.3").results << operation(totalMemoryUsed: 900)
-        result.baseline("1.3").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.3").results << operation(totalMemoryUsed: 1100)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1100)
-        result.current << operation(totalMemoryUsed: 1100)
-        result.current << operation(totalMemoryUsed: 1100)
-
-        expect:
-        result.assertCurrentVersionHasNotRegressed()
-    }
-
-    def "fails when average heap usage for current release is larger than average heap usage for previous releases"() {
-        given:
-        result.baseline("1.0").maxMemoryRegression = DataAmount.bytes(100)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1001)
-
-        result.baseline("1.2").maxMemoryRegression = DataAmount.bytes(100)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1100)
-        result.current << operation(totalMemoryUsed: 1100)
-        result.current << operation(totalMemoryUsed: 1101)
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.startsWith("Memory ${result.displayName}: we need more memory than 1.2.")
-        e.message.contains('Difference: 100.333 B more (100.333 B), 10.03%')
-        !e.message.contains('than 1.0')
-    }
-
-    def "fails when both heap usage and execution time have regressed"() {
-        given:
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 150)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1000, executionTime: 100)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 150)
-
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, executionTime: 100)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, executionTime: 100)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, executionTime: 100)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1100, executionTime: 110)
-        result.current << operation(totalMemoryUsed: 1100, executionTime: 110)
-        result.current << operation(totalMemoryUsed: 1101, executionTime: 111)
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.contains("Speed ${result.displayName}: we're slower than 1.2.")
-        e.message.contains('Difference: 10.333 ms slower (10.333 ms)')
-        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
-        e.message.contains('Difference: 100.333 B more (100.333 B)')
-        !e.message.contains('than 1.0')
-    }
-
-    def "fails when a previous operation fails"() {
-        given:
-        result.baseline("1.0").results << operation(failure: new RuntimeException())
-        result.current.add(operation())
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.startsWith("Some builds have failed.")
-    }
-
-    def "fails when a current operation fails"() {
-        given:
-        result.baseline("1.0").results << operation()
-        result.current.add(operation(failure: new RuntimeException()))
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.startsWith("Some builds have failed.")
-    }
-
-    def "fails when an operation fails"() {
-        given:
-        result.current.add(operation())
-        result.baseline("1.0").results << operation()
-        result.baseline("oldVersion").results << operation(failure: new RuntimeException())
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.startsWith("Some builds have failed.")
-    }
-
-    def "fails if one of the baseline version is faster and the other needs less memory"() {
-        given:
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 100)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 100)
-
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, executionTime: 150)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1000, executionTime: 150)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1100, executionTime: 125)
-        result.current << operation(totalMemoryUsed: 1100, executionTime: 125)
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.contains("Speed ${result.displayName}: we're slower than 1.0.")
-        e.message.contains('Difference: 25 ms slower')
-        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
-        e.message.contains('Difference: 100 B more')
-        e.message.count(result.displayName) == 2
-    }
-
-    def "fails if all of the baseline versions are better in every respect"() {
-        given:
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 120)
-        result.baseline("1.0").results << operation(totalMemoryUsed: 1200, executionTime: 120)
-
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1100, executionTime: 150)
-        result.baseline("1.2").results << operation(totalMemoryUsed: 1100, executionTime: 150)
-
-        and:
-        result.current << operation(totalMemoryUsed: 1300, executionTime: 200)
-        result.current << operation(totalMemoryUsed: 1300, executionTime: 200)
-
-        when:
-        result.assertCurrentVersionHasNotRegressed()
-
-        then:
-        AssertionError e = thrown()
-        e.message.contains("Speed ${result.displayName}: we're slower than 1.0.")
-        e.message.contains("Speed ${result.displayName}: we're slower than 1.2.")
-        e.message.contains("Memory ${result.displayName}: we need more memory than 1.0.")
-        e.message.contains("Memory ${result.displayName}: we need more memory than 1.2.")
-    }
-
-    def "can lookup the results for a baseline version"() {
-        expect:
-        def baseline = result.baseline("1.0")
-        baseline.version == "1.0"
-
-        and:
-        result.baseline("1.0") == baseline
-        result.version("1.0") == baseline
-    }
-
-    def "can lookup the current results using the branch as the version name"() {
-        given:
-        result.vcsBranch = 'master'
-
-        expect:
-        def version = result.version('master')
-        version.results.is(result.current)
-    }
-}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceTestRunnerTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceTestRunnerTest.groovy
deleted file mode 100644
index 49791fa..0000000
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/PerformanceTestRunnerTest.groovy
+++ /dev/null
@@ -1,105 +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.performance.fixture
-
-import org.gradle.integtests.fixtures.executer.GradleDistribution
-import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
-import org.gradle.performance.ResultSpecification
-import org.gradle.performance.measure.DataAmount
-import org.gradle.performance.measure.Duration
-import org.gradle.util.GradleVersion
-
-class PerformanceTestRunnerTest extends ResultSpecification {
-    final timer = Mock(OperationTimer)
-    final reporter = Mock(DataReporter)
-    final testProjectLocator = Stub(TestProjectLocator)
-    final dataCollector = Stub(DataCollector)
-    final currentGradle = Stub(GradleDistribution)
-    final mostRecentRelease = new ReleasedVersionDistributions().mostRecentFinalRelease.version.version
-    final currentVersionBase = GradleVersion.current().baseVersion.version
-
-    def "runs test and builds results"() {
-        given:
-        def runner = runner()
-        runner.testId = 'some-test'
-        runner.testProject = 'test1'
-        runner.targetVersions = ['1.0', '1.1']
-        runner.tasksToRun = ['clean', 'build']
-        runner.args = ['--arg1', '--arg2']
-        runner.warmUpRuns = 1
-        runner.runs = 4
-        runner.maxExecutionTimeRegression = Duration.millis(100)
-        runner.maxMemoryRegression = DataAmount.bytes(10)
-
-        when:
-        def results = runner.run()
-
-        then:
-        results.testId == 'some-test'
-        results.testProject == 'test1'
-        results.tasks == ['clean', 'build']
-        results.args == ['--arg1', '--arg2']
-        results.versionUnderTest
-        results.jvm
-        results.operatingSystem
-        results.current.size() == 4
-        results.current.executionTime.average == Duration.seconds(10)
-        results.current.totalMemoryUsed.average == DataAmount.kbytes(10)
-        results.baselineVersions*.version == ['1.0', '1.1']
-        results.baseline('1.0').results.size() == 4
-        results.baseline('1.1').results.size() == 4
-        results.baselineVersions.every { it.maxExecutionTimeRegression == runner.maxExecutionTimeRegression }
-        results.baselineVersions.every { it.maxMemoryRegression == runner.maxMemoryRegression }
-
-        and:
-        // warmup runs are discarded
-        3 * timer.measure(_) >> operation(executionTime: Duration.seconds(100), totalMemoryUsed: DataAmount.kbytes(100))
-        12 * timer.measure(_) >> operation(executionTime: Duration.seconds(10), totalMemoryUsed: DataAmount.kbytes(10))
-        1 * reporter.report(_)
-        0 * timer._
-        0 * reporter._
-    }
-
-    def "can use 'last' baseline version to refer to most recently released version"() {
-        given:
-        def runner = runner()
-        runner.targetVersions = ['1.0', 'last']
-
-        when:
-        def results = runner.run()
-
-        then:
-        results.baselineVersions*.version == ['1.0', mostRecentRelease]
-    }
-
-    def "ignores baseline version if it has the same base as the version under test"() {
-        given:
-        def runner = runner()
-        runner.targetVersions = ['1.0', currentVersionBase, mostRecentRelease, 'last']
-
-        when:
-        def results = runner.run()
-
-        then:
-        results.baselineVersions*.version == ['1.0', mostRecentRelease]
-    }
-
-    def runner() {
-        return new PerformanceTestRunner(testId: 'some-test',
-                timer: timer, testProjectLocator: testProjectLocator, dataCollector: dataCollector, current: currentGradle, reporter: reporter)
-    }
-}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/WaitingReaderTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/WaitingReaderTest.groovy
new file mode 100644
index 0000000..14d4092
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/fixture/WaitingReaderTest.groovy
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+
+class WaitingReaderTest extends ConcurrentSpec {
+
+    def input = new ExpandingReader()
+    def source = new BufferedReader(input)
+
+    def "can read lines"() {
+        def reader = new WaitingReader(source, 100, 1)
+        input.append("1\n2\n")
+
+        expect:
+        reader.readLine() == "1"
+        reader.retriedCount == 0
+
+        reader.readLine() == "2"
+        reader.retriedCount == 0
+
+        reader.readLine() == null
+        reader.retriedCount > 0
+    }
+
+    def "can receive content after end of stream reached"() {
+        def reader = new WaitingReader(source, 100, 1)
+        input.append("1\n2\n")
+
+        expect:
+        reader.readLine() == "1"
+        reader.readLine() == "2"
+        reader.readLine() == null
+        input.append("3\n4")
+        reader.readLine() == "3"
+        reader.readLine() == "4"
+        reader.readLine() == null
+    }
+
+    def "test"() {
+        def reader = new WaitingReader(source, 1000, 10)
+        input.append("first part of the line")
+
+        start {
+            sleep(200)
+            input.append(", second part of the line\nnext line\n")
+        }
+
+        expect:
+        reader.readLine() == "first part of the line, second part of the line"
+        reader.readLine() == "next line"
+    }
+
+    static class ExpandingReader extends Reader {
+        final buffer = new StringBuilder()
+
+        void append(String content) {
+            buffer.append(content)
+        }
+
+        @Override
+        void close() throws IOException {
+        }
+
+        @Override
+        synchronized int read(char[] dest, int offset, int len) throws IOException {
+            if (buffer.length() == 0) {
+                return -1
+            }
+            int count = Math.min(len, buffer.length())
+            for (int i = 0; i < count; i++) {
+                dest[i + offset] = buffer.charAt(i)
+            }
+            buffer.delete(0, count)
+            return count
+        }
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/measure/DataSeriesTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/measure/DataSeriesTest.groovy
index 2547730..5f88dc8 100644
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/measure/DataSeriesTest.groovy
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/measure/DataSeriesTest.groovy
@@ -19,7 +19,7 @@ package org.gradle.performance.measure
 import spock.lang.Specification
 
 class DataSeriesTest extends Specification {
-    def "can calculate average and min and max"() {
+    def "can calculate statistics for samples"() {
         def v1 = DataAmount.kbytes(10)
         def v2 = DataAmount.kbytes(20)
         def v3 = DataAmount.kbytes(30)
@@ -29,6 +29,7 @@ class DataSeriesTest extends Specification {
         series.average == v2
         series.min == v1
         series.max == v3
+        series.stddev == DataAmount.bytes(8360.92)
     }
 
     def "ignores null values"() {
@@ -52,5 +53,6 @@ class DataSeriesTest extends Specification {
         series.average == null
         series.min == null
         series.max == null
+        series.stddev == null
     }
 }
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/results/CompositeResultsStoreTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CompositeResultsStoreTest.groovy
new file mode 100644
index 0000000..6bebe94
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CompositeResultsStoreTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results
+
+import spock.lang.Specification
+
+class CompositeResultsStoreTest extends Specification {
+    def store1 = Mock(ResultsStore)
+    def store2 = Mock(ResultsStore)
+    def store = new CompositeResultsStore(store1, store2)
+
+    def "returns union of test names"() {
+        given:
+        store1.testNames >> ['a', 'b']
+        store2.testNames >> ['c', 'd']
+
+        expect:
+        store.testNames == ['a', 'b', 'c', 'd']
+    }
+
+    def "delegates to appropriate store for details of given test"() {
+        given:
+        store1.testNames >> ['a', 'b']
+        store2.testNames >> ['c', 'd']
+
+        when:
+        store.getTestResults('c')
+
+        then:
+        1 * store2.getTestResults('c')
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossBuildResultsStoreTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossBuildResultsStoreTest.groovy
new file mode 100644
index 0000000..714e4e2
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossBuildResultsStoreTest.groovy
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results
+
+import org.gradle.performance.ResultSpecification
+import org.gradle.performance.fixture.BuildDisplayInfo
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+
+import static org.gradle.performance.measure.DataAmount.kbytes
+import static org.gradle.performance.measure.Duration.minutes
+
+class CrossBuildResultsStoreTest extends ResultSpecification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final dbFile = tmpDir.file("results")
+
+    def "persists reports"() {
+        given:
+        def results1 = crossBuildResults(testId: "test1", testGroup: "group1")
+        def buildResults1 = results1.buildResult(
+                new BuildDisplayInfo(
+                        "simple",
+                        "simple display",
+                        ["build"],
+                        ["-i"]
+                )
+        )
+        buildResults1 << operation(totalTime: minutes(12),
+                configurationTime: minutes(1),
+                executionTime: minutes(10),
+                totalMemoryUsed: kbytes(12.33),
+                totalHeapUsage: kbytes(5612.45),
+                maxHeapUsage: kbytes(124.01),
+                maxUncollectedHeap: kbytes(45.22),
+                maxCommittedHeap: kbytes(200)
+        )
+        def buildResults2 = results1.buildResult(new BuildDisplayInfo("complex", "complex display", [], []))
+        buildResults2 << operation()
+        buildResults2 << operation()
+
+        and:
+        def results2 = crossBuildResults(testId: "test2", testGroup: "group2")
+        results2.buildResult(new BuildDisplayInfo("simple", "simple display", ["build"], ["-i"]))
+
+        when:
+        def writeStore = new CrossBuildResultsStore(dbFile)
+        writeStore.report(results1)
+        writeStore.report(results2)
+        writeStore.close()
+
+        then:
+        tmpDir.file("results.h2.db").exists()
+
+        when:
+        def readStore = new CrossBuildResultsStore(dbFile)
+        def tests = readStore.testNames
+
+        then:
+        tests == ["test1", "test2"]
+
+        when:
+        def history = readStore.getTestResults("test1")
+
+        then:
+        history.id == "test1"
+        history.name == "test1"
+        history.experimentCount == 2
+        history.experimentLabels == ["complex display", "simple display"]
+
+        and:
+        def firstSpecification = history.builds[0]
+        firstSpecification == new BuildDisplayInfo("complex", "complex display", [], [])
+        history.results.first().buildResult(firstSpecification).size() == 2
+
+        and:
+        def secondSpecification = history.builds[1]
+        secondSpecification == new BuildDisplayInfo("simple", "simple display", ["build"], ["-i"])
+        def crossBuildPerformanceResults = history.results.first()
+        crossBuildPerformanceResults.testId == "test1"
+        crossBuildPerformanceResults.jvm == "java 7"
+        crossBuildPerformanceResults.versionUnderTest == "Gradle 1.0"
+        crossBuildPerformanceResults.operatingSystem == "windows"
+        crossBuildPerformanceResults.testTime == 100
+        crossBuildPerformanceResults.vcsBranch == "master"
+        crossBuildPerformanceResults.vcsCommit == "abcdef"
+
+        and:
+        def operation = crossBuildPerformanceResults.buildResult(secondSpecification).first
+        operation.totalTime == minutes(12)
+        operation.configurationTime == minutes(1)
+        operation.executionTime == minutes(10)
+        operation.totalMemoryUsed == kbytes(12.33)
+        operation.totalHeapUsage == kbytes(5612.45)
+        operation.maxHeapUsage == kbytes(124.01)
+        operation.maxUncollectedHeap == kbytes(45.22)
+        operation.maxCommittedHeap == kbytes(200)
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+
+    def "returns top n results in descending date order"() {
+        given:
+        def results1 = crossBuildResults(testId: "test1", testTime: 1000)
+        results1.buildResult(new BuildDisplayInfo("simple1", "simple 1", ["build"], ["-i"]))
+
+        and:
+        def results2 = crossBuildResults(testId: "test1", testTime: 2000)
+        results2.buildResult(new BuildDisplayInfo("simple2", "simple 2", ["build"], ["-i"]))
+
+        and:
+        def results3 = crossBuildResults(testId: "test1", testTime: 3000)
+        results3.buildResult(new BuildDisplayInfo("simple3", "simple 3", ["build"], ["-i"]))
+
+        and:
+        def writeStore = new CrossBuildResultsStore(dbFile)
+        writeStore.report(results2)
+        writeStore.report(results3)
+        writeStore.report(results1)
+        writeStore.close()
+
+        when:
+        def readStore = new CrossBuildResultsStore(dbFile)
+        def history = readStore.getTestResults("test1")
+
+        then:
+        history.results*.testTime == [3000, 2000, 1000]
+
+        when:
+        history = readStore.getTestResults("test1", 2)
+
+        then:
+        history.results*.testTime == [3000, 2000]
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossVersionResultsStoreTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossVersionResultsStoreTest.groovy
new file mode 100644
index 0000000..a8e7e01
--- /dev/null
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/results/CrossVersionResultsStoreTest.groovy
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results
+
+import org.gradle.performance.ResultSpecification
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+
+import static org.gradle.performance.measure.DataAmount.kbytes
+import static org.gradle.performance.measure.Duration.minutes
+
+class CrossVersionResultsStoreTest extends ResultSpecification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final dbFile = tmpDir.file("results")
+
+    def "persists results"() {
+        def result1 = crossVersionResults(testId: "test1",
+                testProject: "test-project",
+                tasks: ["clean", "build"],
+                args: ["--arg1"],
+                operatingSystem: "some-os",
+                jvm: "java 6",
+                testTime: 10000,
+                versionUnderTest: "1.7-rc-1",
+                vcsBranch: "master",
+                vcsCommit: "1234")
+        def baseline1 = result1.baseline("1.0")
+        def baseline2 = result1.baseline("1.5")
+        result1.current << operation(totalTime: minutes(12),
+                configurationTime: minutes(1),
+                executionTime: minutes(10),
+                totalMemoryUsed: kbytes(12.33),
+                totalHeapUsage: kbytes(5612.45),
+                maxHeapUsage: kbytes(124.01),
+                maxUncollectedHeap: kbytes(45.22),
+                maxCommittedHeap: kbytes(200))
+        baseline1.results << operation()
+        baseline2.results << operation()
+        baseline2.results << operation()
+        baseline2.results << operation()
+
+        def result2 = crossVersionResults(testId: "test2", testTime: 20000, versionUnderTest: "1.7-rc-2")
+        result2.current << operation()
+        result2.current << operation()
+        def baseline3 = result2.baseline("1.0")
+        baseline3.results << operation()
+
+        when:
+        def writeStore = new CrossVersionResultsStore(dbFile)
+        writeStore.report(result1)
+        writeStore.report(result2)
+        writeStore.close()
+
+        then:
+        tmpDir.file("results.h2.db").exists()
+
+        when:
+        def readStore = new CrossVersionResultsStore(dbFile)
+        def tests = readStore.testNames
+
+        then:
+        tests == ["test1", "test2"]
+
+        when:
+        def history = readStore.getTestResults("test1")
+
+        then:
+        history.id == "test1"
+        history.name == "test1"
+        history.baselineVersions == ["1.0", "1.5"]
+        history.experimentCount == 3
+        history.experimentLabels == ["1.0", "1.5", "master"]
+
+        and:
+        def results = history.results
+        results.size() == 1
+        results[0].testId == "test1"
+        results[0].displayName == "Results for test project 'test-project' with tasks clean, build"
+        results[0].testProject == "test-project"
+        results[0].tasks == ["clean", "build"]
+        results[0].args == ["--arg1"]
+        results[0].operatingSystem == "some-os"
+        results[0].jvm == "java 6"
+        results[0].testTime == 10000
+        results[0].versionUnderTest == '1.7-rc-1'
+        results[0].vcsBranch == 'master'
+        results[0].vcsCommit == '1234'
+        results[0].current.size() == 1
+        results[0].current[0].totalTime == minutes(12)
+        results[0].current[0].configurationTime == minutes(1)
+        results[0].current[0].executionTime == minutes(10)
+        results[0].current[0].totalMemoryUsed == kbytes(12.33)
+        results[0].current[0].totalHeapUsage == kbytes(5612.45)
+        results[0].current[0].maxHeapUsage == kbytes(124.01)
+        results[0].current[0].maxUncollectedHeap == kbytes(45.22)
+        results[0].current[0].maxCommittedHeap == kbytes(200)
+        results[0].baselineVersions*.version == ["1.0", "1.5"]
+        results[0].baseline("1.0").results.size() == 1
+        results[0].baseline("1.5").results.size() == 3
+
+        when:
+        history = readStore.getTestResults("test2")
+        results = history.results
+        readStore.close()
+
+        then:
+        history.baselineVersions == ["1.0"]
+
+        and:
+        results.size() == 1
+        results[0].testId == "test2"
+        results[0].testTime == 20000
+        results[0].versionUnderTest == '1.7-rc-2'
+        results[0].current.size() == 2
+        results[0].baselineVersions*.version == ["1.0"]
+        results[0].baseline("1.0").results.size() == 1
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+
+    def "returns test names in ascending order"() {
+        given:
+        def writeStore = new CrossVersionResultsStore(dbFile)
+        writeStore.report(crossVersionResults(testId: "test3"))
+        writeStore.report(crossVersionResults(testId: "test1"))
+        writeStore.report(crossVersionResults(testId: "test2"))
+        writeStore.close()
+
+        when:
+        def readStore = new CrossVersionResultsStore(dbFile)
+        def tests = readStore.testNames
+
+        then:
+        tests == ["test1", "test2", "test3"]
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+
+    def "returns top n test executions in descending date order"() {
+        given:
+        def writeStore = new CrossVersionResultsStore(dbFile)
+        writeStore.report(crossVersionResults(testId: "some test", testTime: 30000, versionUnderTest: "1.7-rc-3"))
+        writeStore.report(crossVersionResults(testId: "some test", testTime: 10000, versionUnderTest: "1.7-rc-1"))
+        writeStore.report(crossVersionResults(testId: "some test", testTime: 20000, versionUnderTest: "1.7-rc-2"))
+        writeStore.close()
+
+        when:
+        def readStore = new CrossVersionResultsStore(dbFile)
+        def results = readStore.getTestResults("some test")
+
+        then:
+        results.results.size() == 3
+        results.results*.versionUnderTest == ["1.7-rc-3", "1.7-rc-2", "1.7-rc-1"]
+        results.resultsOldestFirst*.versionUnderTest == ["1.7-rc-1", "1.7-rc-2", "1.7-rc-3"]
+
+        when:
+        results = readStore.getTestResults("some test", 2)
+
+        then:
+        results.results.size() == 2
+        results.results*.versionUnderTest == ["1.7-rc-3", "1.7-rc-2"]
+        results.resultsOldestFirst*.versionUnderTest == ["1.7-rc-2", "1.7-rc-3"]
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+
+    def "the known versions for a test is the union of all baseline versions in ascending order and the union of test branches"() {
+        given:
+        def writeStore = new CrossVersionResultsStore(dbFile)
+
+        def results1 = crossVersionResults(vcsBranch: "master")
+        results1.baseline("1.8-rc-2").results << operation()
+        results1.baseline("1.0").results << operation()
+        def results2 = crossVersionResults(vcsBranch: "release")
+        results2.baseline("1.8-rc-1").results << operation()
+        results2.baseline("1.0").results << operation()
+        results2.baseline("1.10").results << operation()
+        def results3 = crossVersionResults(vcsBranch: "master")
+        results3.baseline("1.8").results << operation()
+        results3.baseline("1.10").results << operation()
+
+        writeStore.report(results1)
+        writeStore.report(results2)
+        writeStore.report(results3)
+        writeStore.close()
+
+        when:
+        def readStore = new CrossVersionResultsStore(dbFile)
+        def results = readStore.getTestResults("test-id")
+
+        then:
+        results.baselineVersions == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10"]
+        results.branches == ["master", "release"]
+        results.knownVersions == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10", "master", "release"]
+        results.experimentCount == 7
+        results.experimentLabels == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10", "master", "release"]
+
+        cleanup:
+        writeStore?.close()
+        readStore?.close()
+    }
+
+    def "returns empty results for unknown id"() {
+        given:
+        def store = new CrossVersionResultsStore(dbFile)
+
+        expect:
+        store.getTestResults("unknown").baselineVersions.empty
+        store.getTestResults("unknown").results.empty
+
+        cleanup:
+        store?.close()
+    }
+}
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/results/ReportGeneratorTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/results/ReportGeneratorTest.groovy
index 8b8953c..b0a0f4d 100644
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/results/ReportGeneratorTest.groovy
+++ b/subprojects/performance/src/test/groovy/org/gradle/performance/results/ReportGeneratorTest.groovy
@@ -28,8 +28,8 @@ class ReportGeneratorTest extends ResultSpecification {
 
     def "generates report"() {
         setup:
-        def store = new ResultsStore(dbFile)
-        def result2 = results()
+        def store = new CrossVersionResultsStore(dbFile)
+        def result2 = crossVersionResults()
         result2.current << operation()
         result2.current << operation()
         store.report(result2)
diff --git a/subprojects/performance/src/test/groovy/org/gradle/performance/results/ResultsStoreTest.groovy b/subprojects/performance/src/test/groovy/org/gradle/performance/results/ResultsStoreTest.groovy
deleted file mode 100644
index da4a98d..0000000
--- a/subprojects/performance/src/test/groovy/org/gradle/performance/results/ResultsStoreTest.groovy
+++ /dev/null
@@ -1,248 +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.performance.results
-
-import org.gradle.performance.ResultSpecification
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-
-import static org.gradle.performance.measure.DataAmount.kbytes
-import static org.gradle.performance.measure.Duration.minutes
-
-class ResultsStoreTest extends ResultSpecification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    final dbFile = tmpDir.file("results")
-
-    def "persists results"() {
-        def result1 = results(testId: "test1",
-                testProject: "test-project",
-                tasks: ["clean", "build"],
-                args: ["--arg1"],
-                operatingSystem: "some-os",
-                jvm: "java 6",
-                testTime: 10000,
-                versionUnderTest: "1.7-rc-1",
-                vcsBranch: "master",
-                vcsCommit: "1234")
-        def baseline1 = result1.baseline("1.0")
-        def baseline2 = result1.baseline("1.5")
-        result1.current << operation(executionTime: minutes(12),
-                totalMemoryUsed: kbytes(12.33),
-                totalHeapUsage: kbytes(5612.45),
-                maxHeapUsage: kbytes(124.01),
-                maxUncollectedHeap: kbytes(45.22),
-                maxCommittedHeap: kbytes(200))
-        baseline1.results << operation()
-        baseline2.results << operation()
-        baseline2.results << operation()
-        baseline2.results << operation()
-
-        def result2 = results(testId: "test2", testTime: 20000, versionUnderTest: "1.7-rc-2")
-        result2.current << operation()
-        result2.current << operation()
-        def baseline3 = result2.baseline("1.0")
-        baseline3.results << operation()
-
-        when:
-        def writeStore = new ResultsStore(dbFile)
-        writeStore.report(result1)
-        writeStore.report(result2)
-        writeStore.close()
-
-        then:
-        tmpDir.file("results.h2.db").exists()
-
-        when:
-        def readStore = new ResultsStore(dbFile)
-        def tests = readStore.testNames
-
-        then:
-        tests == ["test1", "test2"]
-
-        when:
-        def history = readStore.getTestResults("test1")
-
-        then:
-        history.baselineVersions == ["1.0", "1.5"]
-
-        and:
-        def results = history.results
-        results.size() == 1
-        results[0].testId == "test1"
-        results[0].displayName == "Results for test project 'test-project' with tasks clean, build"
-        results[0].testProject == "test-project"
-        results[0].tasks == ["clean", "build"]
-        results[0].args == ["--arg1"]
-        results[0].operatingSystem == "some-os"
-        results[0].jvm == "java 6"
-        results[0].testTime == 10000
-        results[0].versionUnderTest == '1.7-rc-1'
-        results[0].vcsBranch == 'master'
-        results[0].vcsCommit == '1234'
-        results[0].current.size() == 1
-        results[0].current[0].executionTime == minutes(12)
-        results[0].current[0].totalMemoryUsed == kbytes(12.33)
-        results[0].current[0].totalHeapUsage == kbytes(5612.45)
-        results[0].current[0].maxHeapUsage == kbytes(124.01)
-        results[0].current[0].maxUncollectedHeap == kbytes(45.22)
-        results[0].current[0].maxCommittedHeap == kbytes(200)
-        results[0].baselineVersions*.version == ["1.0", "1.5"]
-        results[0].baseline("1.0").results.size() == 1
-        results[0].baseline("1.5").results.size() == 3
-
-        when:
-        history = readStore.getTestResults("test2")
-        results = history.results
-        readStore.close()
-
-        then:
-        history.baselineVersions == ["1.0"]
-
-        and:
-        results.size() == 1
-        results[0].testId == "test2"
-        results[0].testTime == 20000
-        results[0].versionUnderTest == '1.7-rc-2'
-        results[0].current.size() == 2
-        results[0].baselineVersions*.version == ["1.0"]
-        results[0].baseline("1.0").results.size() == 1
-
-        cleanup:
-        writeStore?.close()
-        readStore?.close()
-    }
-
-    def "returns test names in ascending order"() {
-        given:
-        def writeStore = new ResultsStore(dbFile)
-        writeStore.report(results(testId: "test3"))
-        writeStore.report(results(testId: "test1"))
-        writeStore.report(results(testId: "test2"))
-        writeStore.close()
-
-        when:
-        def readStore = new ResultsStore(dbFile)
-        def tests = readStore.testNames
-
-        then:
-        tests == ["test1", "test2", "test3"]
-
-        cleanup:
-        writeStore?.close()
-        readStore?.close()
-    }
-
-    def "returns test executions in descending date order"() {
-        given:
-        def writeStore = new ResultsStore(dbFile)
-        writeStore.report(results(testId: "some test", testTime: 30000, versionUnderTest: "1.7-rc-3"))
-        writeStore.report(results(testId: "some test", testTime: 10000, versionUnderTest: "1.7-rc-1"))
-        writeStore.report(results(testId: "some test", testTime: 20000, versionUnderTest: "1.7-rc-2"))
-        writeStore.close()
-
-        when:
-        def readStore = new ResultsStore(dbFile)
-        def results = readStore.getTestResults("some test")
-
-        then:
-        results.results.size() == 3
-        results.results*.versionUnderTest == ["1.7-rc-3", "1.7-rc-2", "1.7-rc-1"]
-        results.resultsOldestFirst*.versionUnderTest == ["1.7-rc-1", "1.7-rc-2", "1.7-rc-3"]
-
-        cleanup:
-        writeStore?.close()
-        readStore?.close()
-    }
-
-    def "the known versions for a test is the union of all baseline versions in ascending order and the union of test branches"() {
-        given:
-        def writeStore = new ResultsStore(dbFile)
-
-        def results1 = results(vcsBranch: "master")
-        results1.baseline("1.8-rc-2").results << operation()
-        results1.baseline("1.0").results << operation()
-        def results2 = results(vcsBranch: "release")
-        results2.baseline("1.8-rc-1").results << operation()
-        results2.baseline("1.0").results << operation()
-        results2.baseline("1.10").results << operation()
-        def results3 = results(vcsBranch: "master")
-        results3.baseline("1.8").results << operation()
-        results3.baseline("1.10").results << operation()
-
-        writeStore.report(results1)
-        writeStore.report(results2)
-        writeStore.report(results3)
-        writeStore.close()
-
-        when:
-        def readStore = new ResultsStore(dbFile)
-        def results = readStore.getTestResults("test-id")
-
-        then:
-        results.baselineVersions == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10"]
-        results.branches == ["master", "release"]
-        results.knownVersions == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10", "master", "release"]
-
-        cleanup:
-        writeStore?.close()
-        readStore?.close()
-    }
-
-    def "the set of known versions is the union of all baseline versions and branches"() {
-        given:
-        def writeStore = new ResultsStore(dbFile)
-
-        def results1 = results(testId: "test-1", vcsBranch: "master")
-        results1.baseline("1.8-rc-2").results << operation()
-        results1.baseline("1.0").results << operation()
-        def results2 = results(testId: "test-2", vcsBranch: "release")
-        results2.baseline("1.8-rc-1").results << operation()
-        results2.baseline("1.0").results << operation()
-        results2.baseline("1.10").results << operation()
-        def results3 = results(testId: "test-3", vcsBranch: "release")
-        results3.baseline("1.8").results << operation()
-        results3.baseline("2.0").results << operation()
-
-        writeStore.report(results1)
-        writeStore.report(results2)
-        writeStore.report(results3)
-        writeStore.close()
-
-        when:
-        def readStore = new ResultsStore(dbFile)
-        def results = readStore.getVersions()
-
-        then:
-        results == ["1.0", "1.8-rc-1", "1.8-rc-2", "1.8", "1.10", "2.0", "master", "release"]
-
-        cleanup:
-        writeStore?.close()
-        readStore?.close()
-    }
-
-    def "returns empty results for unknown id"() {
-        given:
-        def store = new ResultsStore(dbFile)
-
-        expect:
-        store.getTestResults("unknown").baselineVersions.empty
-        store.getTestResults("unknown").results.empty
-
-        cleanup:
-        store?.close()
-    }
-}
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-1.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-1.txt
index bf07a6d..2c7f956 100644
--- a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-1.txt
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-1.txt
@@ -1,12 +1,12 @@
-0.122: [GC 0.122: [DefNew: 2176K->256K(2432K), 0.0026630 secs] 2176K->581K(7936K), 0.0026880 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-0.207: [GC 0.207: [DefNew: 2432K->256K(2432K), 0.0024440 secs] 2757K->910K(7936K), 0.0024650 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-0.273: [GC 0.273: [DefNew: 2432K->255K(2432K), 0.0022840 secs] 3086K->1147K(7936K), 0.0023110 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-1.344: [GC 1.344: [DefNew: 2424K->256K(2432K), 0.0023470 secs]1.346: [Tenured: 5860K->4659K(5888K), 0.0269100 secs] 7884K->4659K(8320K), [Perm : 12541K->12541K(21248K)], 0.0294080 secs] [Times: user=0.03 sys=0.01, real=0.03 secs]
-1.428: [GC 1.428: [DefNew: 3136K->384K(3520K), 0.0019360 secs] 7795K->5077K(11288K), 0.0019590 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
-2.219: [GC 2.219: [DefNew: 5119K->512K(5120K), 0.0036670 secs] 11901K->7829K(16364K), 0.0037020 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
-2.885: [GC 2.885: [DefNew: 5031K->273K(5120K), 0.0032980 secs]2.888: [Tenured: 11552K->11084K(11628K), 0.0628400 secs] 16221K->11084K(16748K), [Perm : 19743K->19743K(21248K)], 0.0662580 secs] [Times: user=0.11 sys=0.00, real=0.07 secs]
-4.565: [GC 4.565: [DefNew: 12288K->1045K(13760K), 0.0044820 secs] 30486K->19244K(44092K), 0.0045210 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
-4.717: [GC 4.717: [DefNew: 13333K->942K(13760K), 0.0078990 secs] 31532K->20002K(44092K), 0.0079420 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:50.319+0000: [GC 0.122: [DefNew: 2176K->256K(2432K), 0.0026630 secs] 2176K->581K(7936K), 0.0026880 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:50.519+0000: [GC 0.207: [DefNew: 2432K->256K(2432K), 0.0024440 secs] 2757K->910K(7936K), 0.0024650 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:50.619+0000: [GC 0.273: [DefNew: 2432K->255K(2432K), 0.0022840 secs] 3086K->1147K(7936K), 0.0023110 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:51.319+0000: [GC 1.344: [DefNew: 2424K->256K(2432K), 0.0023470 secs]1.346: [Tenured: 5860K->4659K(5888K), 0.0269100 secs] 7884K->4659K(8320K), [Perm : 12541K->12541K(21248K)], 0.0294080 secs] [Times: user=0.03 sys=0.01, real=0.03 secs]
+2015-01-22T16:04:51.519+0000: [GC 1.428: [DefNew: 3136K->384K(3520K), 0.0019360 secs] 7795K->5077K(11288K), 0.0019590 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:52.319+0000: [GC 2.219: [DefNew: 5119K->512K(5120K), 0.0036670 secs] 11901K->7829K(16364K), 0.0037020 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:52.519+0000: [GC 2.885: [DefNew: 5031K->273K(5120K), 0.0032980 secs]2.888: [Tenured: 11552K->11084K(11628K), 0.0628400 secs] 16221K->11084K(16748K), [Perm : 19743K->19743K(21248K)], 0.0662580 secs] [Times: user=0.11 sys=0.00, real=0.07 secs]
+2015-01-22T16:04:56.319+0000: [GC 4.565: [DefNew: 12288K->1045K(13760K), 0.0044820 secs] 30486K->19244K(44092K), 0.0045210 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:56.819+0000: [GC 4.717: [DefNew: 13333K->942K(13760K), 0.0078990 secs] 31532K->20002K(44092K), 0.0079420 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
 Heap
  def new generation   total 13760K, used 12425K [0x00000007f2e00000, 0x00000007f3ce0000, 0x00000007f58a0000)
   eden space 12288K,  94% used [0x00000007f2e00000, 0x00000007f394bf40, 0x00000007f3a00000)
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-2.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-2.txt
index 4aed31d..50f53d0 100644
--- a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-2.txt
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-2.txt
@@ -1,10 +1,10 @@
-0.415: [GC [PSYoungGen: 13568K->2221K(15808K)] 13568K->2511K(51968K), 0.0039800 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
-0.712: [GC [PSYoungGen: 15789K->2208K(15808K)] 16079K->3676K(51968K), 0.0048130 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
-0.956: [GC [PSYoungGen: 15776K->2240K(15808K)] 17244K->5196K(51968K), 0.0048400 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
-1.212: [GC [PSYoungGen: 15808K->2240K(29376K)] 18764K->7273K(65536K), 0.0089280 secs] [Times: user=0.05 sys=0.01, real=0.01 secs]
-1.772: [GC [PSYoungGen: 29376K->2240K(29376K)] 34409K->11683K(65536K), 0.0187800 secs] [Times: user=0.11 sys=0.02, real=0.02 secs]
-2.237: [GC [PSYoungGen: 29376K->4849K(60992K)] 38819K->14293K(97152K), 0.0090450 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
-7.397: [Full GC (System) [PSYoungGen: 9542K->0K(187200K)] [ParOldGen: 30885K->34145K(36160K)] 40427K->34145K(223360K) [PSPermGen: 32579K->32561K(65152K)], 0.2854620 secs] [Times: user=1.27 sys=0.02, real=0.29 secs]
+2015-01-22T16:04:50.319+0000: [GC [PSYoungGen: 13568K->2221K(15808K)] 13568K->2511K(51968K), 0.0039800 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
+2015-01-22T16:04:50.519+0000: [GC [PSYoungGen: 15789K->2208K(15808K)] 16079K->3676K(51968K), 0.0048130 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:50.619+0000: [GC [PSYoungGen: 15776K->2240K(15808K)] 17244K->5196K(51968K), 0.0048400 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:51.319+0000: [GC [PSYoungGen: 15808K->2240K(29376K)] 18764K->7273K(65536K), 0.0089280 secs] [Times: user=0.05 sys=0.01, real=0.01 secs]
+2015-01-22T16:04:51.519+0000: [GC [PSYoungGen: 29376K->2240K(29376K)] 34409K->11683K(65536K), 0.0187800 secs] [Times: user=0.11 sys=0.02, real=0.02 secs]
+2015-01-22T16:04:52.319+0000: [GC [PSYoungGen: 29376K->4849K(60992K)] 38819K->14293K(97152K), 0.0090450 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:52.519+0000: [Full GC (System) [PSYoungGen: 9542K->0K(187200K)] [ParOldGen: 30885K->34145K(36160K)] 40427K->34145K(223360K) [PSPermGen: 32579K->32561K(65152K)], 0.2854620 secs] [Times: user=1.27 sys=0.02, real=0.29 secs]
 Heap
  PSYoungGen      total 187200K, used 5532K [0x00000007ee560000, 0x00000007fad10000, 0x0000000800000000)
   eden space 170304K, 3% used [0x00000007ee560000,0x00000007eeac7280,0x00000007f8bb0000)
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-3.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-3.txt
index 6b5a76a..40a98e4 100644
--- a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-3.txt
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-3.txt
@@ -1,10 +1,10 @@
-0.705: [GC [PSYoungGen: 13568K->2226K(15808K)] 13568K->2623K(51904K), 0.0069900 secs] [Times: user=0.04 sys=0.01, real=0.00 secs] 
-0.975: [GC [PSYoungGen: 15794K->2224K(15808K)] 16191K->3532K(51904K), 0.0099290 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
-1.381: [GC [PSYoungGen: 15792K->2208K(15808K)] 17100K->5093K(51904K), 0.0070110 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
-1.625: [GC [PSYoungGen: 15776K->2240K(29376K)] 18661K->6827K(65472K), 0.0053900 secs] [Times: user=0.03 sys=0.01, real=0.00 secs] 
-35.550: [Full GC [PSYoungGen: 9383K->0K(172352K)] [ParOldGen: 30417K->31827K(58048K)] 39800K->31827K(230400K) [PSPermGen: 34674K->34638K(69632K)], 0.1364430 secs] [Times: user=0.65 sys=0.01, real=0.14 secs]
-50.758: [GC [PSYoungGen: 87556K->6147K(237440K)] 119384K->37982K(295488K), 0.0126410 secs] [Times: user=0.04 sys=0.01, real=0.01 secs] 
-50.770: [Full GC (System) [PSYoungGen: 6147K->0K(237440K)] [ParOldGen: 31835K->31328K(58048K)] 37982K->31328K(295488K) [PSPermGen: 39021K->39019K(81536K)], 0.1088040 secs] [Times: user=0.39 sys=0.02, real=0.11 secs] 
+2015-01-22T16:04:50.319+0000: [GC [PSYoungGen: 13568K->2226K(15808K)] 13568K->2623K(51904K), 0.0069900 secs] [Times: user=0.04 sys=0.01, real=0.00 secs]
+2015-01-22T16:04:50.519+0000: [GC [PSYoungGen: 15794K->2224K(15808K)] 16191K->3532K(51904K), 0.0099290 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:50.619+0000: [GC [PSYoungGen: 15792K->2208K(15808K)] 17100K->5093K(51904K), 0.0070110 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:51.319+0000: [GC [PSYoungGen: 15776K->2240K(29376K)] 18661K->6827K(65472K), 0.0053900 secs] [Times: user=0.03 sys=0.01, real=0.00 secs]
+2015-01-22T16:04:51.519+0000: [Full GC [PSYoungGen: 9383K->0K(172352K)] [ParOldGen: 30417K->31827K(58048K)] 39800K->31827K(230400K) [PSPermGen: 34674K->34638K(69632K)], 0.1364430 secs] [Times: user=0.65 sys=0.01, real=0.14 secs]
+2015-01-22T16:04:52.319+0000: [GC [PSYoungGen: 87556K->6147K(237440K)] 119384K->37982K(295488K), 0.0126410 secs] [Times: user=0.04 sys=0.01, real=0.01 secs]
+2015-01-22T16:04:52.519+0000: [Full GC (System) [PSYoungGen: 6147K->0K(237440K)] [ParOldGen: 31835K->31328K(58048K)] 37982K->31328K(295488K) [PSPermGen: 39021K->39019K(81536K)], 0.1088040 secs] [Times: user=0.39 sys=0.02, real=0.11 secs]
 Heap
  PSYoungGen      total 237440K, used 8742K [0x00000007ee600000, 0x00000007ff6e0000, 0x0000000800000000)
   eden space 231232K, 3% used [0x00000007ee600000,0x00000007eee898f0,0x00000007fc7d0000)
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-4.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-4.txt
new file mode 100644
index 0000000..c5f4c3a
--- /dev/null
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/gc-4.txt
@@ -0,0 +1,19 @@
+2015-01-22T16:04:50.319+0000: [GC [PSYoungGen: 30784K->5029K(35840K)] 30784K->5223K(117696K), 0,0066100 secs] [Times: user=0,01 sys=0,00, real=0,01 secs]
+2015-01-22T16:04:50.519+0000: [GC [PSYoungGen: 35813K->5025K(66624K)] 36007K->6893K(148480K), 0,0077370 secs] [Times: user=0,03 sys=0,00, real=0,01 secs]
+2015-01-22T16:04:50.619+0000: [GC [PSYoungGen: 66593K->5040K(66624K)] 68461K->12857K(148480K), 0,0123070 secs] [Times: user=0,03 sys=0,00, real=0,01 secs]
+2015-01-22T16:04:51.319+0000: [GC [PSYoungGen: 66608K->5024K(128192K)] 74425K->20722K(210048K), 0,0189750 secs] [Times: user=0,06 sys=0,01, real=0,02 secs]
+2015-01-22T16:04:51.519+0000: [GC [PSYoungGen: 128160K->5024K(128192K)] 143858K->34052K(210048K), 0,0262190 secs] [Times: user=0,07 sys=0,01, real=0,02 secs]
+2015-01-22T16:04:52.319+0000: [GC [PSYoungGen: 128160K->20224K(261312K)] 157188K->51780K(343168K), 0,0373200 secs] [Times: user=0,10 sys=0,03, real=0,04 secs]
+2015-01-22T16:04:52.519+0000: [GC [PSYoungGen: 261312K->25408K(266496K)] 292868K->86455K(348352K), 0,0672450 secs] [Times: user=0,25 sys=0,01, real=0,07 secs]
+2015-01-22T16:04:56.319+0000: [Full GC [PSYoungGen: 25408K->0K(266496K)] [ParOldGen: 61047K->78755K(156352K)] 86455K->78755K(422848K) [PSPermGen: 29867K->29851K(60288K)], 0,4369730 secs] [Times: user=1,54 sys=0,02, real=0,44 secs]
+2015-01-22T16:04:56.819+0000: [GC [PSYoungGen: 28517K->3868K(317824K)] 107272K->82623K(474176K), 0,0073580 secs] [Times: user=0,01 sys=0,01, real=0,01 secs]
+2015-01-22T16:04:57.819+0000: [Full GC (System) [PSYoungGen: 3868K->0K(317824K)] [ParOldGen: 78755K->79466K(156352K)] 82623K->79466K(474176K) [PSPermGen: 30788K->30788K(68160K)], 0,1601310 secs] [Times: user=0,55 sys=0,00, real=0,16 secs]
+Heap
+ PSYoungGen      total 317824K, used 9328K [0x00000000d80b0000, 0x00000000f8720000, 0x0000000100000000)
+  eden space 313920K, 2% used [0x00000000d80b0000,0x00000000d89cc068,0x00000000eb340000)
+  from space 3904K, 0% used [0x00000000f8350000,0x00000000f8350000,0x00000000f8720000)
+  to   space 44544K, 0% used [0x00000000f3020000,0x00000000f3020000,0x00000000f5ba0000)
+ ParOldGen       total 156352K, used 79466K [0x0000000088200000, 0x0000000091ab0000, 0x00000000d80b0000)
+  object space 156352K, 50% used [0x0000000088200000,0x000000008cf9aa80,0x0000000091ab0000)
+ PSPermGen       total 68160K, used 30844K [0x0000000083000000, 0x0000000087290000, 0x0000000088200000)
+  object space 68160K, 45% used [0x0000000083000000,0x0000000084e1f310,0x0000000087290000)
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/mac-jdk8.0.25.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/mac-jdk8.0.25.txt
new file mode 100644
index 0000000..39dd113
--- /dev/null
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/mac-jdk8.0.25.txt
@@ -0,0 +1,24 @@
+Java HotSpot(TM) 64-Bit Server VM (25.25-b02) for bsd-amd64 JRE (1.8.0_25-b17), built on Sep 17 2014 16:56:50 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
+Memory: 4k page, physical 16777216k(220132k free)
+
+/proc/meminfo:
+
+CommandLine flags: -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:-PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
+2015-01-22T16:04:50.319+0000: [GC (Allocation Failure) [PSYoungGen: 65536K->7647K(76288K)] 65536K->7663K(251392K), 0.0081231 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:50.519+0000: [GC (Allocation Failure) [PSYoungGen: 73183K->10732K(141824K)] 73199K->11334K(316928K), 0.0083230 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:50.619+0000: [GC (Metadata GC Threshold) [PSYoungGen: 37665K->8146K(141824K)] 38266K->8756K(316928K), 0.0057478 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:51.319+0000: [Full GC (Metadata GC Threshold) [PSYoungGen: 8146K->0K(141824K)] [ParOldGen: 609K->7853K(108544K)] 8756K->7853K(250368K), [Metaspace: 20722K->20722K(1067008K)], 0.0278495 secs] [Times: user=0.09 sys=0.01, real=0.02 secs]
+2015-01-22T16:04:51.519+0000: [GC (System.gc()) [PSYoungGen: 42114K->5147K(199680K)] 49967K->13008K(308224K), 0.0054855 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]
+2015-01-22T16:04:52.319+0000: [Full GC (System.gc()) [PSYoungGen: 5147K->0K(199680K)] [ParOldGen: 7861K->11164K(108544K)] 13008K->11164K(308224K), [Metaspace: 23444K->23444K(1071104K)], 0.0846303 secs] [Times: user=0.54 sys=0.01, real=0.08 secs]
+2015-01-22T16:04:52.519+0000: [GC (System.gc()) [PSYoungGen: 6062K->256K(194560K)] 17227K->11428K(303104K), 0.0010566 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.319+0000: [Full GC (System.gc()) [PSYoungGen: 256K->0K(194560K)] [ParOldGen: 11172K->9753K(108544K)] 11428K->9753K(303104K), [Metaspace: 23523K->23523K(1071104K)], 0.0921023 secs] [Times: user=0.57 sys=0.01, real=0.09 secs]
+Heap
+ PSYoungGen      total 194560K, used 17324K [0x000000076ab00000, 0x000000077b600000, 0x00000007c0000000)
+  eden space 194048K, 8% used [0x000000076ab00000,0x000000076bbeb380,0x0000000776880000)
+  from space 512K, 0% used [0x000000077a600000,0x000000077a600000,0x000000077a680000)
+  to   space 10752K, 0% used [0x000000077ab80000,0x000000077ab80000,0x000000077b600000)
+ ParOldGen       total 108544K, used 9753K [0x00000006c0000000, 0x00000006c6a00000, 0x000000076ab00000)
+  object space 108544K, 8% used [0x00000006c0000000,0x00000006c09866f8,0x00000006c6a00000)
+ Metaspace       used 23599K, capacity 24096K, committed 24192K, reserved 1071104K
+  class space    used 3283K, capacity 3430K, committed 3456K, reserved 1048576K
+76K
diff --git a/subprojects/performance/src/test/resources/org/gradle/performance/fixture/win-1.txt b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/win-1.txt
new file mode 100644
index 0000000..86a7192
--- /dev/null
+++ b/subprojects/performance/src/test/resources/org/gradle/performance/fixture/win-1.txt
@@ -0,0 +1,20 @@
+2015-01-22T16:04:56.819+0000: [GC 0.122: [DefNew: 2176K->256K(2432K), 0.0026630 secs] 2176K->581K(7936K), 0.0026880 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.819+0000: [GC 0.207: [DefNew: 2432K->256K(2432K), 0.0024440 secs] 2757K->910K(7936K), 0.0024650 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.819+0000: [GC 0.273: [DefNew: 2432K->255K(2432K), 0.0022840 secs] 3086K->1147K(7936K), 0.0023110 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.819+0000: [GC 1.344: [DefNew: 2424K->256K(2432K), 0.0023470 secs]1.346: [Tenured: 5860K->4659K(5888K), 0.0269100 secs] 7884K->4659K(8320K), [Perm : 12541K->12541K(21248K)], 0.0294080 secs] [Times: user=0.03 sys=0.01, real=0.03 secs]
+2015-01-22T16:04:56.819+0000: [GC 1.428: [DefNew: 3136K->384K(3520K), 0.0019360 secs] 7795K->5077K(11288K), 0.0019590 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.819+0000: [GC 2.219: [DefNew: 5119K->512K(5120K), 0.0036670 secs] 11901K->7829K(16364K), 0.0037020 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
+2015-01-22T16:04:56.819+0000: [GC 2.885: [DefNew: 5031K->273K(5120K), 0.0032980 secs]2.888: [Tenured: 11552K->11084K(11628K), 0.0628400 secs] 16221K->11084K(16748K), [Perm : 19743K->19743K(21248K)], 0.0662580 secs] [Times: user=0.11 sys=0.00, real=0.07 secs]
+2015-01-22T16:04:56.819+0000: [GC 4.565: [DefNew: 12288K->1045K(13760K), 0.0044820 secs] 30486K->19244K(44092K), 0.0045210 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
+2015-01-22T16:04:56.819+0000: [GC 4.717: [DefNew: 13333K->942K(13760K), 0.0078990 secs] 31532K->20002K(44092K), 0.0079420 secs]
+ [Times: user=0.01 sys=0.00, real=0.00 secs]
+Heap
+ def new generation   total 13760K, used 12425K [0x00000007f2e00000, 0x00000007f3ce0000, 0x00000007f58a0000)
+  eden space 12288K,  94% used [0x00000007f2e00000, 0x00000007f394bf40, 0x00000007f3a00000)
+  from space 1472K,  58% used [0x00000007f3a00000, 0x00000007f3ad6718, 0x00000007f3b70000)
+  to   space 1472K,   0% used [0x00000007f3b70000, 0x00000007f3b70000, 0x00000007f3ce0000)
+ tenured generation   total 30332K, used 20909K [0x00000007f58a0000, 0x00000007f763f000, 0x00000007fae00000)
+   the space 30332K,  68% used [0x00000007f58a0000, 0x00000007f6d0b4f0, 0x00000007f6d0b600, 0x00000007f763f000)
+ compacting perm gen  total 24448K, used 24194K [0x00000007fae00000, 0x00000007fc5e0000, 0x0000000800000000)
+   the space 24448K,  98% used [0x00000007fae00000, 0x00000007fc5a0bc0, 0x00000007fc5a0c00, 0x00000007fc5e0000)
+No shared spaces configured.
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossBuildPerformanceTest.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossBuildPerformanceTest.groovy
new file mode 100644
index 0000000..66ef73e
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossBuildPerformanceTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance
+
+import org.gradle.performance.fixture.BuildExperimentRunner
+import org.gradle.performance.fixture.BuildExperimentSpec
+import org.gradle.performance.fixture.CrossBuildPerformanceTestRunner
+import org.gradle.performance.fixture.GradleSessionProvider
+import org.gradle.performance.results.CrossBuildResultsStore
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import org.junit.experimental.categories.Category
+import spock.lang.Specification
+
+ at Category(PerformanceTest)
+class AbstractCrossBuildPerformanceTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    static def resultStore = new CrossBuildResultsStore()
+
+    final def runner = new CrossBuildPerformanceTestRunner(new BuildExperimentRunner(new GradleSessionProvider(tmpDir)), resultStore) {
+        @Override
+        protected void defaultSpec(BuildExperimentSpec.Builder builder) {
+            builder.invocationCount(5).warmUpCount(1)
+            super.defaultSpec(builder)
+            AbstractCrossBuildPerformanceTest.this.defaultSpec(builder)
+        }
+
+        @Override
+        protected void finalizeSpec(BuildExperimentSpec.Builder builder) {
+            super.finalizeSpec(builder)
+            AbstractCrossBuildPerformanceTest.this.finalizeSpec(builder)
+        }
+    }
+
+    protected void defaultSpec(BuildExperimentSpec.Builder builder) {
+
+    }
+
+    protected void finalizeSpec(BuildExperimentSpec.Builder builder) {
+
+    }
+
+    static {
+        // TODO - find a better way to cleanup
+        System.addShutdownHook {
+            resultStore.close()
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossVersionPerformanceTest.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossVersionPerformanceTest.groovy
new file mode 100644
index 0000000..80e63fd
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/AbstractCrossVersionPerformanceTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * 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.performance
+
+import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
+import org.gradle.performance.fixture.BuildExperimentRunner
+import org.gradle.performance.fixture.CompositeDataReporter
+import org.gradle.performance.fixture.CrossVersionPerformanceTestRunner
+import org.gradle.performance.fixture.GradleSessionProvider
+import org.gradle.performance.fixture.TextFileDataReporter
+import org.gradle.performance.measure.DataAmount
+import org.gradle.performance.measure.Duration
+import org.gradle.performance.results.CrossVersionResultsStore
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import org.junit.experimental.categories.Category
+
+ at Category(PerformanceTest)
+class AbstractCrossVersionPerformanceTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    static def resultStore = new CrossVersionResultsStore()
+    static def textReporter = new TextFileDataReporter(new File("build/performance-tests/results.txt"))
+
+    final def runner = new CrossVersionPerformanceTestRunner(new BuildExperimentRunner(new GradleSessionProvider(tmpDir)), new CompositeDataReporter([textReporter, resultStore]))
+
+    def setup() {
+        runner.current = new UnderDevelopmentGradleDistribution()
+        runner.runs = 5
+        runner.warmUpRuns = 1
+        runner.targetVersions = ['1.0', '1.4', '1.8', 'last']
+        runner.maxExecutionTimeRegression = Duration.millis(500)
+        runner.maxMemoryRegression = DataAmount.mbytes(25)
+    }
+
+    static {
+        // TODO - find a better way to cleanup
+        System.addShutdownHook {
+            resultStore.close()
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/Experiment.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/Experiment.java
new file mode 100644
index 0000000..099af79
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/Experiment.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance;
+
+public interface Experiment extends PerformanceTest {
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/PerformanceTest.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/PerformanceTest.java
new file mode 100644
index 0000000..94347b8
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/PerformanceTest.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance;
+
+public interface PerformanceTest {
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/AbstractPerformanceTest.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/AbstractPerformanceTest.groovy
deleted file mode 100644
index fdb61d1..0000000
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/AbstractPerformanceTest.groovy
+++ /dev/null
@@ -1,52 +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.performance.fixture
-
-import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
-import org.gradle.performance.measure.DataAmount
-import org.gradle.performance.measure.Duration
-import org.gradle.performance.results.ResultsStore
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class AbstractPerformanceTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    static def resultStore = new ResultsStore()
-    static def textReporter = new TextFileDataReporter(new File("build/performance-tests/results.txt"))
-
-    final def runner = new PerformanceTestRunner(
-            testDirectoryProvider: tmpDir,
-            current: new UnderDevelopmentGradleDistribution(),
-            runs: 5,
-            warmUpRuns: 1,
-            targetVersions: ['1.0', '1.4', '1.8', 'last'],
-            maxExecutionTimeRegression: Duration.millis(500),
-            maxMemoryRegression: DataAmount.mbytes(25)
-    )
-
-    def setup() {
-        runner.reporter = new CompositeDataReporter([textReporter, resultStore])
-    }
-
-    static {
-        // TODO - find a better way to cleanup
-        System.addShutdownHook {
-            resultStore.close()
-        }
-    }
-}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BaselineVersion.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BaselineVersion.groovy
index 319cfda..dceb6bc 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BaselineVersion.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BaselineVersion.groovy
@@ -20,7 +20,8 @@ import org.gradle.performance.measure.Amount
 import org.gradle.performance.measure.DataAmount
 import org.gradle.performance.measure.Duration
 
-import static org.gradle.performance.fixture.PrettyCalculator.*
+import static org.gradle.performance.fixture.PrettyCalculator.toBytes
+import static org.gradle.performance.fixture.PrettyCalculator.toMillis
 
 class BaselineVersion implements VersionResults {
     final String version
@@ -39,8 +40,8 @@ class BaselineVersion implements VersionResults {
 
     String getSpeedStatsAgainst(String displayName, MeasuredOperationList current) {
         def sb = new StringBuilder()
-        def thisVersionAverage = results.executionTime.average
-        def currentVersionAverage = current.executionTime.average
+        def thisVersionAverage = results.totalTime.average
+        def currentVersionAverage = current.totalTime.average
         if (currentVersionAverage > thisVersionAverage) {
             sb.append "Speed $displayName: we're slower than $version.\n"
         } else {
@@ -58,6 +59,7 @@ class BaselineVersion implements VersionResults {
     String getMemoryStatsAgainst(String displayName, MeasuredOperationList current) {
         def sb = new StringBuilder()
         def currentVersionAverage = current.totalMemoryUsed.average
+        assert currentVersionAverage != null
         def thisVersionAverage = results.totalMemoryUsed.average
         if (currentVersionAverage > thisVersionAverage) {
             sb.append("Memory $displayName: we need more memory than $version.\n")
@@ -78,6 +80,6 @@ class BaselineVersion implements VersionResults {
     }
 
     boolean fasterThan(MeasuredOperationList current) {
-        current.executionTime.average - results.executionTime.average > maxExecutionTimeRegression
+        current.totalTime.average - results.totalTime.average > maxExecutionTimeRegression
     }
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildDisplayInfo.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildDisplayInfo.groovy
new file mode 100644
index 0000000..e7db9fe
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildDisplayInfo.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import groovy.transform.CompileStatic
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+
+ at CompileStatic
+ at EqualsAndHashCode
+ at ToString
+class BuildDisplayInfo {
+
+    final String projectName
+    final String displayName
+    final List<String> tasksToRun
+    final List<String> args
+
+    BuildDisplayInfo(String projectName, String displayName, List<String> tasksToRun, List<String> args) {
+        this.projectName = projectName
+        this.displayName = displayName
+        this.tasksToRun = tasksToRun
+        this.args = args
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildEventTimestampCollector.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildEventTimestampCollector.java
new file mode 100644
index 0000000..de1316c
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildEventTimestampCollector.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.performance.measure.Duration;
+import org.gradle.performance.measure.MeasuredOperation;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.List;
+
+public class BuildEventTimestampCollector implements DataCollector {
+
+    private final String outputFile;
+
+    public BuildEventTimestampCollector(String outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    @Override
+    public List<String> getAdditionalGradleOpts(File workingDir) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<String> getAdditionalArgs(File workingDir) {
+        return ImmutableList.of("--init-script=init.gradle");
+    }
+
+    @Override
+    public void collect(File testProjectDir, MeasuredOperation operation) {
+        final File timestampFile = new File(testProjectDir, outputFile);
+        final String absolutePath = timestampFile.getAbsolutePath();
+        if (!timestampFile.exists()) {
+            throw new IllegalStateException(String.format("Could not find %s. Cannot collect build event timestamps.", absolutePath));
+        }
+        List<String> lines = readLines(timestampFile);
+        if (lines.size() != 3) {
+            throw new IllegalStateException(String.format("Build event timestamp log at %s should contain exactly 3 lines.", absolutePath));
+        }
+        List<Long> timestamps = parseTimestamps(absolutePath, lines);
+        operation.setConfigurationTime(Duration.millis(timestamps.get(1) - timestamps.get(0)));
+        operation.setExecutionTime(Duration.millis(timestamps.get(2) - timestamps.get(1)));
+    }
+
+    private List<Long> parseTimestamps(final String absolutePath, List<String> lines) {
+        try {
+            return ImmutableList.of(Long.valueOf(lines.get(0)), Long.valueOf(lines.get(1)), Long.valueOf(lines.get(2)));
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(String.format("One of the timestamps in build event timestamp log at %s is not valid.", absolutePath), e);
+        }
+    }
+
+    private List<String> readLines(File timestampFile) {
+        ImmutableList.Builder<String> lines = ImmutableList.builder();
+        FileReader fileReader = null;
+        try {
+            fileReader = new FileReader(timestampFile);
+            BufferedReader reader = new BufferedReader(fileReader);
+            String line;
+            while ((line = reader.readLine()) != null) {
+                lines.add(line);
+            }
+            return lines.build();
+        } catch (FileNotFoundException e) {
+            throw new UncheckedIOException(e);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        } finally {
+            if (fileReader != null) {
+                try {
+                    fileReader.close();
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentRunner.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentRunner.java
new file mode 100644
index 0000000..8b9c722
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentRunner.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+import org.gradle.api.Action;
+import org.gradle.performance.measure.MeasuredOperation;
+
+import java.io.File;
+import java.util.List;
+
+public class BuildExperimentRunner {
+    private final GCLoggingCollector gcCollector = new GCLoggingCollector();
+    private final DataCollector dataCollector;
+    private final GradleSessionProvider executerProvider;
+    private final OperationTimer timer = new OperationTimer();
+
+    public BuildExperimentRunner(GradleSessionProvider executerProvider) {
+        this.executerProvider = executerProvider;
+        MemoryInfoCollector memoryInfoCollector = new MemoryInfoCollector();
+        memoryInfoCollector.setOutputFileName("build/totalMemoryUsed.txt");
+        BuildEventTimestampCollector buildEventTimestampCollector = new BuildEventTimestampCollector("build/buildEventTimestamps.txt");
+        dataCollector = new CompositeDataCollector(memoryInfoCollector, gcCollector, buildEventTimestampCollector);
+    }
+
+    public void run(BuildExperimentSpec experiment, MeasuredOperationList results) {
+        System.out.println();
+        System.out.println(String.format("%s ...", experiment.getDisplayName()));
+        System.out.println();
+
+        File workingDirectory = experiment.getInvocation().getWorkingDirectory();
+        final List<String> additionalGradleOpts = dataCollector.getAdditionalGradleOpts(workingDirectory);
+        final List<String> additionalArgs = dataCollector.getAdditionalArgs(workingDirectory);
+
+        GradleInvocationSpec buildSpec = experiment.getInvocation().withAdditionalGradleOpts(additionalGradleOpts).withAdditionalArgs(additionalArgs);
+        GradleSession session = executerProvider.session(buildSpec);
+
+        session.prepare();
+        try {
+            for (int i = 0; i < experiment.getWarmUpCount(); i++) {
+                System.out.println();
+                System.out.println(String.format("Warm-up #%s", i + 1));
+                runOnce(session, new MeasuredOperationList());
+            }
+            for (int i = 0; i < experiment.getInvocationCount(); i++) {
+                System.out.println();
+                System.out.println(String.format("Test run #%s", i + 1));
+                runOnce(session, results);
+            }
+        } finally {
+            session.cleanup();
+        }
+    }
+
+    private void runOnce(final GradleSession session, MeasuredOperationList results) {
+        final Runnable runner = session.runner();
+
+        MeasuredOperation operation = timer.measure(new Action<MeasuredOperation>() {
+            @Override
+            public void execute(MeasuredOperation measuredOperation) {
+                runner.run();
+            }
+        });
+
+        if (operation.getException() == null) {
+            dataCollector.collect(session.getInvocation().getWorkingDirectory(), operation);
+        }
+
+        results.add(operation);
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentSpec.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentSpec.groovy
new file mode 100644
index 0000000..ee3172b
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/BuildExperimentSpec.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import groovy.transform.CompileStatic
+import groovy.transform.EqualsAndHashCode
+
+ at CompileStatic
+ at EqualsAndHashCode
+class BuildExperimentSpec {
+
+    String displayName
+    String projectName
+    GradleInvocationSpec invocation
+    Integer warmUpCount
+    Integer invocationCount
+
+    BuildExperimentSpec(String displayName, String projectName, GradleInvocationSpec invocation, Integer warmUpCount, Integer invocationCount) {
+        this.displayName = displayName
+        this.projectName = projectName
+        this.invocation = invocation
+        this.warmUpCount = warmUpCount
+        this.invocationCount = invocationCount
+    }
+
+    static Builder builder() {
+        new Builder()
+    }
+
+    BuildDisplayInfo getDisplayInfo() {
+        new BuildDisplayInfo(projectName, displayName, invocation.tasksToRun, invocation.args)
+    }
+
+    static class Builder {
+        String displayName
+        String projectName
+        GradleInvocationSpec.Builder invocation = GradleInvocationSpec.builder()
+        Integer warmUpCount
+        Integer invocationCount
+
+        Builder displayName(String displayName) {
+            this.displayName = displayName
+            this
+        }
+
+        Builder projectName(String projectName) {
+            this.projectName = projectName
+            this
+        }
+
+        Builder warmUpCount(Integer warmUpCount) {
+            this.warmUpCount = warmUpCount
+            this
+        }
+
+        Builder invocationCount(Integer invocationCount) {
+            this.invocationCount = invocationCount
+            this
+        }
+
+        Builder invocation(@DelegatesTo(GradleInvocationSpec.Builder) Closure<?> conf) {
+            invocation.with(conf)
+            this
+        }
+
+        BuildExperimentSpec build() {
+            assert projectName != null
+            assert displayName != null
+            assert invocation != null
+            assert warmUpCount >= 0
+            assert invocationCount > 0
+
+            new BuildExperimentSpec(displayName, projectName, invocation.build(), warmUpCount, invocationCount)
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataCollector.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataCollector.java
index 8774404..f3dc713 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataCollector.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataCollector.java
@@ -16,7 +16,7 @@
 
 package org.gradle.performance.fixture;
 
-import org.gradle.integtests.fixtures.executer.GradleExecuter;
+import com.google.common.collect.Lists;
 import org.gradle.performance.measure.MeasuredOperation;
 
 import java.io.File;
@@ -30,10 +30,22 @@ public class CompositeDataCollector implements DataCollector {
         this.collectors = Arrays.asList(collectors);
     }
 
-    public void beforeExecute(File testProjectDir, GradleExecuter executer) {
+    @Override
+    public List<String> getAdditionalGradleOpts(File workingDir) {
+        List<String> additional = Lists.newLinkedList();
         for (DataCollector collector : collectors) {
-            collector.beforeExecute(testProjectDir, executer);
+            additional.addAll(collector.getAdditionalGradleOpts(workingDir));
         }
+        return additional;
+    }
+
+    @Override
+    public List<String> getAdditionalArgs(File workingDir) {
+        List<String> additional = Lists.newLinkedList();
+        for (DataCollector collector : collectors) {
+            additional.addAll(collector.getAdditionalArgs(workingDir));
+        }
+        return additional;
     }
 
     public void collect(File testProjectDir, MeasuredOperation operation) {
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataReporter.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataReporter.java
index 053ef1a..7ac1921 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataReporter.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CompositeDataReporter.java
@@ -20,19 +20,19 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-public class CompositeDataReporter implements DataReporter {
-    private final List<DataReporter> reporters;
+public class CompositeDataReporter implements DataReporter<CrossVersionPerformanceResults> {
+    private final List<DataReporter<CrossVersionPerformanceResults>> reporters;
     private final Set<String> testIds = new HashSet<String>();
 
-    public CompositeDataReporter(List<DataReporter> reporters) {
+    public CompositeDataReporter(List<DataReporter<CrossVersionPerformanceResults>> reporters) {
         this.reporters = reporters;
     }
 
-    public void report(PerformanceResults results) {
+    public void report(CrossVersionPerformanceResults results) {
         if (!testIds.add(results.getTestId())) {
             throw new IllegalArgumentException(String.format("Multiple performance test executions with id '%s' found.", results.getTestId()));
         }
-        for (DataReporter reporter : reporters) {
+        for (DataReporter<CrossVersionPerformanceResults> reporter : reporters) {
             reporter.report(results);
         }
     }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceResults.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceResults.groovy
new file mode 100644
index 0000000..8be7238
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceResults.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.internal.exceptions.DefaultMultiCauseException
+
+class CrossBuildPerformanceResults extends PerformanceTestResult {
+    String testGroup
+    String versionUnderTest
+
+    private final Map<BuildDisplayInfo, MeasuredOperationList> buildResults = new LinkedHashMap<>()
+
+    def clear() {
+        buildResults.clear()
+    }
+
+    @Override
+    String toString() {
+        return testId
+    }
+
+    MeasuredOperationList buildResult(BuildDisplayInfo buildInfo) {
+        def buildResult = buildResults[buildInfo]
+        if (buildResult == null) {
+            buildResult = new MeasuredOperationList(name: buildInfo.displayName)
+            buildResults[buildInfo] = buildResult
+        }
+        return buildResult
+    }
+
+    public Set<BuildDisplayInfo> getBuilds() {
+        buildResults.keySet()
+    }
+
+    List<Exception> getFailures() {
+        buildResults.values().collect() {
+            it.exception
+        }.flatten().findAll()
+    }
+
+    void assertEveryBuildSucceeds() {
+        if (failures) {
+            throw new DefaultMultiCauseException("Performance test '$testId' failed", failures)
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceTestRunner.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceTestRunner.groovy
new file mode 100644
index 0000000..80f1dbe
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossBuildPerformanceTestRunner.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
+import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.util.GradleVersion
+
+class CrossBuildPerformanceTestRunner {
+    final GradleDistribution gradleDistribution = new UnderDevelopmentGradleDistribution()
+    final BuildExperimentRunner experimentRunner
+    final TestProjectLocator testProjectLocator = new TestProjectLocator()
+
+    String testId
+    String testGroup
+    List<BuildExperimentSpec> specs = []
+
+    final DataReporter<CrossBuildPerformanceResults> reporter
+
+    public CrossBuildPerformanceTestRunner(BuildExperimentRunner experimentRunner, DataReporter<CrossBuildPerformanceResults> dataReporter) {
+        this.reporter = dataReporter
+        this.experimentRunner = experimentRunner
+    }
+
+    public void baseline(@DelegatesTo(BuildExperimentSpec.Builder) Closure<?> configureAction) {
+        buildSpec(configureAction)
+    }
+
+    public void buildSpec(@DelegatesTo(BuildExperimentSpec.Builder) Closure<?> configureAction) {
+        def builder = BuildExperimentSpec.builder()
+        defaultSpec(builder)
+        builder.with(configureAction)
+        finalizeSpec(builder)
+        def specification = builder.build()
+
+        if (specs.any { it.displayName == specification.displayName }) {
+            throw new IllegalStateException("Multiple specifications with display name '${specification.displayName}.")
+        }
+        specs << specification
+    }
+
+    protected void defaultSpec(BuildExperimentSpec.Builder builder) {
+        builder.invocation.distribution(gradleDistribution)
+    }
+
+    protected void finalizeSpec(BuildExperimentSpec.Builder builder) {
+        assert builder.projectName
+        builder.invocation.workingDirectory = testProjectLocator.findProjectDir(builder.projectName)
+    }
+
+    public CrossBuildPerformanceResults run() {
+        assert !specs.empty
+        assert testId
+
+        def results = new CrossBuildPerformanceResults(
+                testId: testId,
+                testGroup: testGroup,
+                jvm: Jvm.current().toString(),
+                operatingSystem: OperatingSystem.current().toString(),
+                versionUnderTest: GradleVersion.current().getVersion(),
+                vcsBranch: Git.current().branchName,
+                vcsCommit: Git.current().commitId,
+                testTime: System.currentTimeMillis()
+        )
+
+        runAllSpecifications(results)
+
+        results.assertEveryBuildSucceeds()
+        reporter.report(results)
+
+        return results
+    }
+
+    void runAllSpecifications(CrossBuildPerformanceResults results) {
+        specs.each {
+            def operations = results.buildResult(it.displayInfo)
+            experimentRunner.run(it, operations)
+        }
+    }
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResults.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResults.groovy
new file mode 100644
index 0000000..f5c8936
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceResults.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.performance.fixture
+
+import org.gradle.api.logging.Logging
+
+public class CrossVersionPerformanceResults extends PerformanceTestResult {
+    private final static LOGGER = Logging.getLogger(CrossVersionPerformanceResults.class)
+
+    String testProject
+    String[] args
+    String[] tasks
+    String versionUnderTest
+
+    private final Map<String, BaselineVersion> baselineVersions = new LinkedHashMap<>()
+    final MeasuredOperationList current = new MeasuredOperationList(name: "Current Gradle")
+    private final results = new CurrentVersionResults(current)
+
+    def clear() {
+        baselineVersions.values().each { it.clearResults() }
+        current.clear()
+    }
+
+    @Override
+    String toString() {
+        return displayName
+    }
+
+    String getDisplayName() {
+        return "Results for test project '$testProject' with tasks ${tasks.join(', ')}"
+    }
+
+    Collection<BaselineVersion> getBaselineVersions() {
+        return baselineVersions.values()
+    }
+
+    /**
+     * Locates the given baseline version, adding it if not present.
+     */
+    BaselineVersion baseline(String version) {
+        def baselineVersion = baselineVersions[version]
+        if (baselineVersion == null) {
+            baselineVersion = new BaselineVersion(version)
+            baselineVersions[version] = baselineVersion
+        }
+        return baselineVersion
+    }
+
+    /**
+     * Locates the given version. Can use either a baseline version or the current branch name.
+     */
+    VersionResults version(String version) {
+        if (version.equals(vcsBranch)) {
+            return results
+        }
+        return baseline(version)
+    }
+
+    List<MeasuredOperationList> getFailures() {
+        def failures = []
+        baselineVersions.values().each {
+            failures.addAll it.results.findAll { it.exception }
+        }
+        failures.addAll current.findAll { it.exception }
+        return failures
+    }
+
+    void assertEveryBuildSucceeds() {
+        LOGGER.info("Asserting all builds have succeeded...");
+        assert failures.collect { it.exception }.empty: "Some builds have failed."
+    }
+
+    void assertCurrentVersionHasNotRegressed() {
+        def slower = checkBaselineVersion({ it.fasterThan(current) }, { it.getSpeedStatsAgainst(displayName, current) })
+        def larger = checkBaselineVersion({ it.usesLessMemoryThan(current) }, { it.getMemoryStatsAgainst(displayName, current) })
+        assertEveryBuildSucceeds()
+        if (slower && larger) {
+            throw new AssertionError("$slower\n$larger")
+        }
+        if (slower) {
+            throw new AssertionError(slower)
+        }
+        if (larger) {
+            throw new AssertionError(larger)
+        }
+    }
+
+    private String checkBaselineVersion(Closure fails, Closure provideMessage) {
+        def failed = false
+        def failure = new StringBuilder()
+        baselineVersions.values().each {
+            String message = provideMessage(it)
+            if (fails(it)) {
+                failed = true
+                failure.append message
+            }
+            println message
+        }
+        return failed ? failure.toString() : null
+    }
+
+    private static class CurrentVersionResults implements VersionResults {
+        final MeasuredOperationList results
+
+        CurrentVersionResults(MeasuredOperationList results) {
+            this.results = results
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunner.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunner.groovy
new file mode 100644
index 0000000..a84af2c
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/CrossVersionPerformanceTestRunner.groovy
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
+import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.performance.measure.Amount
+import org.gradle.performance.measure.DataAmount
+import org.gradle.performance.measure.Duration
+import org.gradle.util.GradleVersion
+
+public class CrossVersionPerformanceTestRunner extends PerformanceTestSpec {
+    GradleDistribution current
+    final IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext()
+    final DataReporter<CrossVersionPerformanceResults> reporter
+    TestProjectLocator testProjectLocator = new TestProjectLocator()
+    final BuildExperimentRunner experimentRunner
+
+    String testProject
+    boolean useDaemon
+
+    List<String> tasksToRun = []
+    List<String> args = []
+    List<String> gradleOpts = []
+
+    List<String> targetVersions = []
+    Amount<Duration> maxExecutionTimeRegression = Duration.millis(0)
+    Amount<DataAmount> maxMemoryRegression = DataAmount.bytes(0)
+
+    CrossVersionPerformanceTestRunner(BuildExperimentRunner experimentRunner, DataReporter<CrossVersionPerformanceResults> reporter) {
+        this.reporter = reporter
+        this.experimentRunner = experimentRunner
+    }
+
+    CrossVersionPerformanceResults run() {
+        assert !targetVersions.empty
+        assert testId
+
+        def results = new CrossVersionPerformanceResults(
+                testId: testId,
+                testProject: testProject,
+                tasks: tasksToRun,
+                args: args,
+                jvm: Jvm.current().toString(),
+                operatingSystem: OperatingSystem.current().toString(),
+                versionUnderTest: GradleVersion.current().getVersion(),
+                vcsBranch: Git.current().branchName,
+                vcsCommit: Git.current().commitId,
+                testTime: System.currentTimeMillis())
+
+        def releasedDistributions = new ReleasedVersionDistributions()
+        def releasedVersions = releasedDistributions.all*.version.version
+        def mostRecentFinalRelease = releasedDistributions.mostRecentFinalRelease.version.version
+        def currentBaseVersion = GradleVersion.current().getBaseVersion().version
+        def allVersions = targetVersions.collect { (it == 'last') ? mostRecentFinalRelease : it }.unique()
+        allVersions.remove(currentBaseVersion)
+
+        // A target version may be something that is yet unreleased, so filter that out
+        allVersions.removeAll { !releasedVersions.contains(it) }
+
+        assert !allVersions.isEmpty()
+
+        File projectDir = testProjectLocator.findProjectDir(testProject)
+
+        println "Running performance tests for test project '$testProject', no. of runs: $runs"
+
+        allVersions.each { it ->
+            def baselineVersion = results.baseline(it)
+            baselineVersion.maxExecutionTimeRegression = maxExecutionTimeRegression
+            baselineVersion.maxMemoryRegression = maxMemoryRegression
+
+            runVersion(buildContext.distribution(baselineVersion.version), projectDir, baselineVersion.results)
+        }
+
+        runVersion(current, projectDir, results.current)
+
+        reporter.report(results)
+        results.assertEveryBuildSucceeds()
+
+        return results
+    }
+
+    private void runVersion(GradleDistribution dist, File projectDir, MeasuredOperationList results) {
+        def spec = BuildExperimentSpec.builder()
+                .projectName(testId)
+                .displayName(dist.version.version)
+                .warmUpCount(warmUpRuns)
+                .invocationCount(runs)
+                .invocation {
+            workingDirectory(projectDir)
+            distribution(dist)
+            tasksToRun(this.tasksToRun as String[])
+            args(this.args as String[])
+            gradleOpts(this.gradleOpts as String[])
+            useDaemon(this.useDaemon)
+        }.build()
+
+        experimentRunner.run(spec, results)
+    }
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataCollector.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataCollector.java
index d01527d..423d99d 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataCollector.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataCollector.java
@@ -16,13 +16,13 @@
 
 package org.gradle.performance.fixture;
 
-import org.gradle.integtests.fixtures.executer.GradleExecuter;
 import org.gradle.performance.measure.MeasuredOperation;
 
 import java.io.File;
+import java.util.List;
 
 public interface DataCollector {
-    void beforeExecute(File testProjectDir, GradleExecuter executer);
-
+    List<String> getAdditionalGradleOpts(File workingDir);
+    List<String> getAdditionalArgs(File workingDir);
     void collect(File testProjectDir, MeasuredOperation operation);
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataReporter.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataReporter.groovy
index 7611b4b..628da36 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataReporter.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/DataReporter.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 the original author or authors.
+ * Copyright 2014 the original author 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,6 @@
 
 package org.gradle.performance.fixture
 
-interface DataReporter {
-    void report(PerformanceResults results)
-}
+public interface DataReporter<T> {
+    void report(T results)
+}
\ No newline at end of file
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCEventParser.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCEventParser.java
new file mode 100644
index 0000000..6232310
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCEventParser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class GCEventParser {
+
+    private final Pattern pattern;
+    private final Pattern ignorePattern;
+
+    GCEventParser(char decimalSeparator) {
+        pattern = Pattern.compile(String.format("(.+): \\[(?:(?:Full )?GC(?: ?(?:[^\\s]+|\\(.+?\\)))?) (?:\\d+\\%s\\d+: )?\\[.*\\] (\\d+)K->(\\d+)K\\((\\d+)K\\)", decimalSeparator));
+        ignorePattern = Pattern.compile(String.format("Java HotSpot.+|Memory:.+|/proc.+|CommandLine flags:.+|\\s*\\[Times: .+\\]\\s*"));
+    }
+
+    GCEvent parseLine(String line) {
+        if (line.trim().isEmpty()) {
+            return GCEvent.IGNORED;
+        }
+
+        Matcher matcher = pattern.matcher(line);
+        if (!matcher.lookingAt()) {
+            if (ignorePattern.matcher(line).matches()) {
+                //I see this kind of events on windows. Let's see if this approach helps resolving them.
+                return GCEvent.IGNORED;
+            } else {
+                throw new IllegalArgumentException("Unrecognized garbage collection event found in garbage collection log: " + line);
+            }
+        }
+
+        DateTime timestamp = DateTime.parse(matcher.group(1));
+        // Some JVMs generate an incorrect timezone offset in the timestamps. Discard timezone and use the local timezone instead
+        timestamp = timestamp.toLocalDateTime().toDateTime(DateTimeZone.getDefault());
+        long start = Long.parseLong(matcher.group(2));
+        long end = Long.parseLong(matcher.group(3));
+        long committed = Long.parseLong(matcher.group(4));
+
+        return new GCEvent(start, end, committed, timestamp);
+    }
+
+    static class GCEvent {
+        final long start;
+        final long end;
+        final long committed;
+        final DateTime timestamp;
+        final static GCEvent IGNORED = new GCEvent(-1, -1, -1, null);
+
+        GCEvent(long start, long end, long committed, DateTime timestamp) {
+            this.start = start;
+            this.end = end;
+            this.committed = committed;
+            this.timestamp = timestamp;
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCLoggingCollector.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCLoggingCollector.java
index 86e9106..128b3c3 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCLoggingCollector.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GCLoggingCollector.java
@@ -16,41 +16,63 @@
 
 package org.gradle.performance.fixture;
 
-import org.gradle.integtests.fixtures.executer.GradleExecuter;
 import org.gradle.performance.measure.DataAmount;
 import org.gradle.performance.measure.MeasuredOperation;
+import org.gradle.util.GFileUtils;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.text.DecimalFormatSymbols;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class GCLoggingCollector implements DataCollector {
     private File logFile;
 
-    public void beforeExecute(File testProjectDir, GradleExecuter executer) {
-        logFile = new File(testProjectDir, "gc.txt");
-        executer.withGradleOpts("-verbosegc", "-XX:+PrintGCDetails", "-Xloggc:" + logFile.getAbsolutePath());
+    @Override
+    public List<String> getAdditionalGradleOpts(File workingDir) {
+        logFile = new File(workingDir, "gc.txt");
+        return Arrays.asList(
+                "-verbosegc",
+                "-XX:+PrintGCDetails",
+                "-XX:+PrintGCDateStamps",
+                "-Xloggc:" + logFile.getAbsolutePath(),
+                "-XX:-PrintGCTimeStamps"
+        );
+    }
+
+    @Override
+    public List<String> getAdditionalArgs(File workingDir) {
+        return Collections.emptyList();
     }
 
     public void collect(File testProjectDir, MeasuredOperation operation) {
+        collect(operation, Locale.getDefault());
+    }
+
+    public void collect(MeasuredOperation operation, Locale locale) {
         try {
             BufferedReader reader = new BufferedReader(new FileReader(logFile));
             try {
-                collect(reader, operation);
+                collect(new WaitingReader(reader), operation, locale);
             } finally {
                 reader.close();
             }
         } catch (Exception e) {
-            throw new RuntimeException(String.format("Could not process garbage collector log %s.", logFile), e);
+            throw new RuntimeException(String.format("Could not process garbage collector log %s. File contents:\n%s", logFile, GFileUtils.readFileQuietly(logFile)), e);
         }
     }
 
-    private void collect(BufferedReader reader, MeasuredOperation operation) throws IOException {
-        Pattern collectionEventPattern = Pattern.compile("\\d+\\.\\d+: \\[(?:(?:Full GC(?: [^\\s]+)?)|GC) (\\d+\\.\\d+: )?\\[.*\\] (\\d+)K->(\\d+)K\\((\\d+)K\\)");
+    private void collect(WaitingReader reader, MeasuredOperation operation, Locale locale) throws IOException {
+        char decimalSeparator = (new DecimalFormatSymbols(locale)).getDecimalSeparator();
+        GCEventParser eventParser = new GCEventParser(decimalSeparator);
         Pattern memoryPoolPattern = Pattern.compile("([\\w\\s]+) total (\\d+)K, used (\\d+)K \\[.+");
 
         long totalHeapUsage = 0;
@@ -62,32 +84,40 @@ public class GCLoggingCollector implements DataCollector {
 
         long usageAtPreviousCollection = 0;
         int events = 0;
+        boolean processHeapUsageSummary = false;
 
         while (true) {
             String line = reader.readLine();
-            if (line == null || line.equals("Heap")) {
+            if (line == null) {
+                break;
+            }
+            if (line.equals("Heap")) {
+                processHeapUsageSummary = true;
                 break;
             }
 
-            Matcher matcher = collectionEventPattern.matcher(line);
-            if (!matcher.lookingAt()) {
-                throw new IllegalArgumentException("Unrecognized garbage collection event found in garbage collection log: " + line);
+            GCEventParser.GCEvent event = eventParser.parseLine(line);
+            if (event == GCEventParser.GCEvent.IGNORED) {
+                continue;
             }
-            events++;
 
-            long start = Long.parseLong(matcher.group(2));
-            long end = Long.parseLong(matcher.group(3));
-            long committed = Long.parseLong(matcher.group(4));
+            events++;
 
-            if (start < usageAtPreviousCollection) {
+            if (event.start < usageAtPreviousCollection) {
                 throw new IllegalArgumentException("Unexpected max heap size found in garbage collection event: " + line);
             }
 
-            totalHeapUsage += start - usageAtPreviousCollection;
-            maxUsage = Math.max(maxUsage, start);
-            maxUncollectedUsage = Math.max(maxUncollectedUsage, end);
-            maxCommittedUsage = Math.max(maxCommittedUsage, committed);
-            usageAtPreviousCollection = end;
+            if (event.timestamp.isAfter(operation.getEnd())) {
+                break;
+            }
+            if (!event.timestamp.isBefore(operation.getStart())) {
+                totalHeapUsage += event.start - usageAtPreviousCollection;
+                maxUsage = Math.max(maxUsage, event.start);
+                maxUncollectedUsage = Math.max(maxUncollectedUsage, event.end);
+                maxCommittedUsage = Math.max(maxCommittedUsage, event.committed);
+            }
+
+            usageAtPreviousCollection = event.end;
         }
 
         if (events == 0) {
@@ -99,40 +129,45 @@ public class GCLoggingCollector implements DataCollector {
         long finalHeapUsage = 0;
         long finalCommittedHeap = 0;
 
-        while (true) {
-            String line = reader.readLine();
-            if (line == null) {
-                break;
-            }
-            Matcher matcher = memoryPoolPattern.matcher(line);
-            if (!matcher.lookingAt()) {
-                continue;
-            }
-
-            String pool = matcher.group(1).trim();
-            if (pool.toLowerCase().contains("perm gen") || pool.toLowerCase().contains("permgen")) {
-                continue;
+        if (processHeapUsageSummary) {
+            while (true) {
+                String line = reader.readLine();
+                if (line == null) {
+                    break;
+                }
+                Matcher matcher = memoryPoolPattern.matcher(line);
+                if (!matcher.lookingAt()) {
+                    continue;
+                }
+
+                String pool = matcher.group(1).trim();
+                if (pool.toLowerCase().contains("perm gen") || pool.toLowerCase().contains("permgen")) {
+                    //perm gen usage is always the last one to be listed
+                    //by breaking we don't loose the time at the end of the file because of using WaitingReader
+                    break;
+                }
+
+                long committed = Long.parseLong(matcher.group(2));
+                long usage = Long.parseLong(matcher.group(3));
+
+                finalHeapUsage += usage;
+                finalCommittedHeap += committed;
             }
 
-            long committed = Long.parseLong(matcher.group(2));
-            long usage = Long.parseLong(matcher.group(3));
 
-            finalHeapUsage += usage;
-            finalCommittedHeap += committed;
-        }
+            if (finalHeapUsage == 0) {
+                throw new IllegalArgumentException("Did not find any memory pool usage details in garbage collection log.");
+            }
 
-        if (finalHeapUsage == 0) {
-            throw new IllegalArgumentException("Did not find any memory pool usage details in garbage collection log.");
-        }
+            if (finalHeapUsage < usageAtPreviousCollection) {
+                throw new IllegalArgumentException("Unexpected max heap size found in memory pool usage.");
+            }
 
-        if (finalHeapUsage < usageAtPreviousCollection) {
-            throw new IllegalArgumentException("Unexpected max heap size found in memory pool usage.");
+            totalHeapUsage += finalHeapUsage - usageAtPreviousCollection;
+            maxUsage = Math.max(maxUsage, finalHeapUsage);
+            maxCommittedUsage = Math.max(maxCommittedUsage, finalCommittedHeap);
         }
 
-        totalHeapUsage += finalHeapUsage - usageAtPreviousCollection;
-        maxUsage = Math.max(maxUsage, finalHeapUsage);
-        maxCommittedUsage = Math.max(maxCommittedUsage, finalCommittedHeap);
-
         operation.setTotalHeapUsage(DataAmount.kbytes(BigDecimal.valueOf(totalHeapUsage)));
         operation.setMaxHeapUsage(DataAmount.kbytes(BigDecimal.valueOf(maxUsage)));
         operation.setMaxUncollectedHeap(DataAmount.kbytes(BigDecimal.valueOf(maxUncollectedUsage)));
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleExecuterBackedSession.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleExecuterBackedSession.groovy
new file mode 100644
index 0000000..8e585d3
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleExecuterBackedSession.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import groovy.transform.CompileStatic
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+
+ at CompileStatic
+class GradleExecuterBackedSession implements GradleSession {
+
+    final GradleInvocationSpec invocation
+
+    private final TestDirectoryProvider testDirectoryProvider
+
+    GradleExecuterBackedSession(GradleInvocationSpec invocation, TestDirectoryProvider testDirectoryProvider) {
+        this.testDirectoryProvider = testDirectoryProvider
+        this.invocation = invocation
+
+    }
+
+    @Override
+    void prepare() {
+        cleanup()
+    }
+
+    @Override
+    Runnable runner() {
+        def runner = createExecuter(true)
+        return { runner.run() }
+    }
+
+    @Override
+    void cleanup() {
+        createExecuter(false).withTasks().withArgument("--stop").run()
+    }
+
+    private GradleExecuter createExecuter(boolean withGradleOpts) {
+        def executer = invocation.gradleDistribution.executer(testDirectoryProvider).
+                requireGradleHome().
+                requireIsolatedDaemons().
+                withDeprecationChecksDisabled().
+                withStackTraceChecksDisabled().
+                withArgument('-u').
+                inDirectory(invocation.workingDirectory).
+                withTasks(invocation.tasksToRun)
+
+        if (withGradleOpts) {
+            if (invocation.useDaemon) {
+                executer.withGradleOpts("-Dorg.gradle.jvmargs=" + invocation.gradleOpts.join(" "))
+            } else {
+                executer.withGradleOpts(invocation.gradleOpts as String[])
+            }
+        }
+
+        invocation.args.each { executer.withArgument(it) }
+
+        if (invocation.useDaemon) {
+            executer.withArgument('--daemon')
+        }
+
+        executer
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleInvocationSpec.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleInvocationSpec.groovy
new file mode 100644
index 0000000..4f1284e
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleInvocationSpec.groovy
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import com.google.common.collect.ImmutableList
+import groovy.transform.CompileStatic
+import groovy.transform.EqualsAndHashCode
+import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.launcher.daemon.configuration.GradleProperties
+import org.gradle.model.internal.persist.ReusingModelRegistryStore
+
+ at CompileStatic
+ at EqualsAndHashCode
+class GradleInvocationSpec {
+
+    final GradleDistribution gradleDistribution
+    final File workingDirectory
+    final List<String> tasksToRun
+    final List<String> args
+    final List<String> gradleOpts
+    final boolean useDaemon
+    final boolean useToolingApi
+
+    GradleInvocationSpec(GradleDistribution gradleDistribution, File workingDirectory, List<String> tasksToRun, List<String> args, List<String> gradleOpts, boolean useDaemon, boolean useToolingApi) {
+        this.gradleDistribution = gradleDistribution
+        this.workingDirectory = workingDirectory
+        this.tasksToRun = tasksToRun
+        this.args = args
+        this.gradleOpts = gradleOpts
+        this.useDaemon = useDaemon
+        this.useToolingApi = useToolingApi
+    }
+
+    static Builder builder() {
+        return new Builder()
+    }
+
+    GradleInvocationSpec withAdditionalGradleOpts(List<String> additionalGradleOpts) {
+        return new GradleInvocationSpec(gradleDistribution, workingDirectory, tasksToRun, args, ImmutableList.builder().addAll(gradleOpts).addAll(additionalGradleOpts).build(), useDaemon, useToolingApi)
+    }
+
+    GradleInvocationSpec withAdditionalArgs(List<String> additionalArgs) {
+        return new GradleInvocationSpec(gradleDistribution, workingDirectory, tasksToRun, ImmutableList.builder().addAll(args).addAll(additionalArgs).build(), gradleOpts, useDaemon, useToolingApi)
+    }
+
+    static class Builder {
+        GradleDistribution gradleDistribution
+        File workingDirectory
+        List<String> tasksToRun = []
+        List<String> args = []
+        List<String> gradleOptions = []
+        boolean useDaemon
+        boolean useToolingApi
+
+        Builder distribution(GradleDistribution gradleDistribution) {
+            this.gradleDistribution = gradleDistribution
+            this
+        }
+
+        Builder workingDirectory(File workingDirectory) {
+            this.workingDirectory = workingDirectory
+            this
+        }
+
+        Builder tasksToRun(String... taskToRun) {
+            this.tasksToRun.addAll(Arrays.asList(taskToRun))
+            this
+        }
+
+        Builder args(String... args) {
+            this.args.addAll(Arrays.asList(args))
+            this
+        }
+
+        Builder gradleOpts(String... gradleOpts) {
+            this.gradleOptions.addAll(Arrays.asList(gradleOpts))
+            this
+        }
+
+        Builder useDaemon() {
+            useDaemon(true)
+        }
+
+        Builder useDaemon(boolean flag) {
+            this.useDaemon = flag
+            this
+        }
+
+        Builder useToolingApi() {
+            useToolingApi(true)
+        }
+
+        Builder useToolingApi(boolean flag) {
+            this.useToolingApi = flag
+            this
+        }
+
+        Builder enableModelReuse() {
+            gradleOpts("-D$ReusingModelRegistryStore.TOGGLE=true")
+        }
+
+        Builder disableDaemonLogging() {
+            gradleOpts("-Dorg.gradle.daemon.disable-output=true")
+        }
+
+        Builder enableTransformedModelDsl() {
+            gradleOpts("-Dorg.gradle.model.dsl=true")
+        }
+
+        Builder disableParallelWorkers() {
+            gradleOpts("-D${GradleProperties.WORKERS_PROPERTY}=1")
+        }
+
+        GradleInvocationSpec build() {
+            assert gradleDistribution != null
+            assert workingDirectory != null
+
+            return new GradleInvocationSpec(gradleDistribution, workingDirectory, tasksToRun.asImmutable(), args.asImmutable(), gradleOptions.asImmutable(), useDaemon, useToolingApi)
+        }
+
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSession.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSession.java
new file mode 100644
index 0000000..fc4c4a7
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSession.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+public interface GradleSession {
+
+    public abstract GradleInvocationSpec getInvocation();
+
+    public abstract void prepare();
+
+    public abstract Runnable runner();
+
+    public abstract void cleanup();
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSessionProvider.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSessionProvider.java
new file mode 100644
index 0000000..91791b1
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/GradleSessionProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+import org.gradle.test.fixtures.file.TestDirectoryProvider;
+
+public class GradleSessionProvider {
+
+    private final TestDirectoryProvider testDirectoryProvider;
+
+    public GradleSessionProvider(TestDirectoryProvider testDirectoryProvider) {
+        this.testDirectoryProvider = testDirectoryProvider;
+    }
+
+    public GradleSession session(GradleInvocationSpec buildSpec) {
+        if (buildSpec.isUseToolingApi()) {
+            return new ToolingApiBackedGradleSession(buildSpec, testDirectoryProvider);
+        } else {
+            return new GradleExecuterBackedSession(buildSpec, testDirectoryProvider);
+        }
+
+    }
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MeasuredOperationList.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MeasuredOperationList.groovy
index e764f4b..84330a5 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MeasuredOperationList.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MeasuredOperationList.groovy
@@ -41,12 +41,20 @@ public class MeasuredOperationList extends LinkedList<MeasuredOperation> {
         return new DataSeries<DataAmount>(this.collect { it.maxCommittedHeap })
     }
 
+    DataSeries<Duration> getTotalTime() {
+        return new DataSeries<Duration>(this.collect { it.totalTime })
+    }
+
+    DataSeries<Duration> getConfigurationTime() {
+        return new DataSeries<Duration>(this.collect { it.configurationTime })
+    }
+
     DataSeries<Duration> getExecutionTime() {
         return new DataSeries<Duration>(this.collect { it.executionTime })
     }
 
     String getSpeedStats() {
-        format(executionTime)
+        format(totalTime)
     }
 
     String getMemoryStats() {
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MemoryInfoCollector.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MemoryInfoCollector.groovy
index 64eceab..2bbd825 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MemoryInfoCollector.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/MemoryInfoCollector.groovy
@@ -16,14 +16,20 @@
 
 package org.gradle.performance.fixture
 
-import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.performance.measure.DataAmount
 import org.gradle.performance.measure.MeasuredOperation
 
 public class MemoryInfoCollector implements DataCollector {
     String outputFileName
 
-    public void beforeExecute(File testProjectDir, GradleExecuter executer) {
+    @Override
+    List<String> getAdditionalGradleOpts(File workingDir) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    List<String> getAdditionalArgs(File workingDir) {
+        return Collections.emptyList();
     }
 
     public void collect(File testProjectDir, MeasuredOperation operation) {
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/OperationTimer.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/OperationTimer.java
index 52e466f..99dd0b2 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/OperationTimer.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/OperationTimer.java
@@ -16,21 +16,24 @@
 
 package org.gradle.performance.fixture;
 
-import groovy.lang.Closure;
+import org.gradle.api.Action;
 import org.gradle.performance.measure.Duration;
 import org.gradle.performance.measure.MeasuredOperation;
+import org.joda.time.DateTime;
 
 public class OperationTimer {
-    public MeasuredOperation measure(Closure operation) {
+    public MeasuredOperation measure(Action<? super MeasuredOperation> action) {
         MeasuredOperation result = new MeasuredOperation();
-        long start = System.currentTimeMillis();
+        DateTime start = DateTime.now();
         try {
-            operation.call(result);
+            action.execute(result);
         } catch (Exception e) {
             result.setException(e);
         }
-        long end = System.currentTimeMillis();
-        result.setExecutionTime(Duration.millis(end - start));
+        DateTime end = DateTime.now();
+        result.setStart(start);
+        result.setEnd(end);
+        result.setTotalTime(Duration.millis(end.getMillis() - start.getMillis()));
         return result;
     }
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceResults.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceResults.groovy
deleted file mode 100644
index 281cc52..0000000
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceResults.groovy
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.performance.fixture
-
-import org.gradle.api.logging.Logging
-
-public class PerformanceResults {
-    private final static LOGGER = Logging.getLogger(PerformanceResults.class)
-
-    String testId
-    String testProject
-    String[] args
-    String[] tasks
-    String jvm
-    String operatingSystem
-    long testTime
-    String versionUnderTest
-    String vcsBranch
-    String vcsCommit
-
-    private final Map<String, BaselineVersion> baselineVersions = new LinkedHashMap<>()
-    final MeasuredOperationList current = new MeasuredOperationList(name: "Current Gradle")
-    private final results = new CurrentVersionResults(current)
-
-    def clear() {
-        baselineVersions.values().each { it.clearResults() }
-        current.clear()
-    }
-
-    @Override
-    String toString() {
-        return displayName
-    }
-
-    String getDisplayName() {
-        return "Results for test project '$testProject' with tasks ${tasks.join(', ')}"
-    }
-
-    Collection<BaselineVersion> getBaselineVersions() {
-        return baselineVersions.values()
-    }
-
-    /**
-     * Locates the given baseline version, adding it if not present.
-     */
-    BaselineVersion baseline(String version) {
-        def baselineVersion = baselineVersions[version]
-        if (baselineVersion == null) {
-            baselineVersion = new BaselineVersion(version)
-            baselineVersions[version] = baselineVersion
-        }
-        return baselineVersion
-    }
-
-    /**
-     * Locates the given version. Can use either a baseline version or the current branch name.
-     */
-    VersionResults version(String version) {
-        if (version.equals(vcsBranch)) {
-            return results
-        }
-        return baseline(version)
-    }
-
-    List<MeasuredOperationList> getFailures() {
-        def failures = []
-        baselineVersions.values().each {
-            failures.addAll it.results.findAll { it.exception }
-        }
-        failures.addAll current.findAll { it.exception }
-        return failures
-    }
-
-    void assertEveryBuildSucceeds() {
-        LOGGER.info("Asserting all builds have succeeded...");
-        assert failures.collect { it.exception }.empty: "Some builds have failed."
-    }
-
-    void assertCurrentVersionHasNotRegressed() {
-        def slower = checkBaselineVersion({ it.fasterThan(current) }, { it.getSpeedStatsAgainst(displayName, current) })
-        def larger = checkBaselineVersion({ it.usesLessMemoryThan(current) }, { it.getMemoryStatsAgainst(displayName, current) })
-        assertEveryBuildSucceeds()
-        if (slower && larger) {
-            throw new AssertionError("$slower\n$larger")
-        }
-        if (slower) {
-            throw new AssertionError(slower)
-        }
-        if (larger) {
-            throw new AssertionError(larger)
-        }
-    }
-
-    private String checkBaselineVersion(Closure fails, Closure provideMessage) {
-        def failed = false
-        def failure = new StringBuilder()
-        baselineVersions.values().each {
-            String message = provideMessage(it)
-            if (fails(it)) {
-                failed = true
-                failure.append message
-            }
-            println message
-        }
-        return failed ? failure.toString() : null
-    }
-
-    private static class CurrentVersionResults implements VersionResults {
-        final MeasuredOperationList results
-
-        CurrentVersionResults(MeasuredOperationList results) {
-            this.results = results
-        }
-    }
-}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestResult.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestResult.java
new file mode 100644
index 0000000..122884e
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+public class PerformanceTestResult {
+    String testId;
+    String jvm;
+    String operatingSystem;
+    long testTime;
+    String vcsBranch;
+    String vcsCommit;
+
+    public String getTestId() {
+        return testId;
+    }
+
+    public void setTestId(String testId) {
+        this.testId = testId;
+    }
+
+    public long getTestTime() {
+        return testTime;
+    }
+
+    public void setTestTime(long testTime) {
+        this.testTime = testTime;
+    }
+
+    public String getVcsBranch() {
+        return vcsBranch;
+    }
+
+    public void setVcsBranch(String vcsBranch) {
+        this.vcsBranch = vcsBranch;
+    }
+
+    public String getVcsCommit() {
+        return vcsCommit;
+    }
+
+    public void setVcsCommit(String vcsCommit) {
+        this.vcsCommit = vcsCommit;
+    }
+
+    public String getOperatingSystem() {
+        return operatingSystem;
+    }
+
+    public void setOperatingSystem(String operatingSystem) {
+        this.operatingSystem = operatingSystem;
+    }
+
+    public String getJvm() {
+        return jvm;
+    }
+
+    public void setJvm(String jvm) {
+        this.jvm = jvm;
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestRunner.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestRunner.groovy
deleted file mode 100644
index 7c43e3c..0000000
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestRunner.groovy
+++ /dev/null
@@ -1,138 +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.performance.fixture
-
-import org.gradle.integtests.fixtures.executer.GradleDistribution
-import org.gradle.integtests.fixtures.executer.GradleExecuter
-import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
-import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
-import org.gradle.internal.jvm.Jvm
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.performance.measure.Amount
-import org.gradle.performance.measure.DataAmount
-import org.gradle.performance.measure.Duration
-import org.gradle.performance.measure.MeasuredOperation
-import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.util.GradleVersion
-
-public class PerformanceTestRunner {
-    TestDirectoryProvider testDirectoryProvider
-    GradleDistribution current
-    IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext()
-    DataReporter reporter
-    OperationTimer timer = new OperationTimer()
-    TestProjectLocator testProjectLocator = new TestProjectLocator()
-
-    String testId
-    String testProject
-    int runs
-    int warmUpRuns
-
-    List<String> tasksToRun = []
-    DataCollector dataCollector = new CompositeDataCollector(
-            new MemoryInfoCollector(outputFileName: "build/totalMemoryUsed.txt"),
-            new GCLoggingCollector())
-    List<String> args = []
-
-    List<String> targetVersions = []
-    Amount<Duration> maxExecutionTimeRegression = Duration.millis(0)
-    Amount<DataAmount> maxMemoryRegression = DataAmount.bytes(0)
-
-    PerformanceResults results
-
-    PerformanceResults run() {
-        assert !targetVersions.empty
-        assert testId
-
-        results = new PerformanceResults(
-                testId: testId,
-                testProject: testProject,
-                tasks: tasksToRun,
-                args: args,
-                jvm: Jvm.current().toString(),
-                operatingSystem: OperatingSystem.current().toString(),
-                versionUnderTest: GradleVersion.current().getVersion(),
-                vcsBranch: Git.current().branchName,
-                vcsCommit: Git.current().commitId,
-                testTime: System.currentTimeMillis())
-
-        def releasedDistributions = new ReleasedVersionDistributions()
-        def releasedVersions = releasedDistributions.all*.version.version
-        def mostRecentFinalRelease = releasedDistributions.mostRecentFinalRelease.version.version
-        def currentBaseVersion = GradleVersion.current().getBaseVersion().version
-        def allVersions = targetVersions.collect { (it == 'last') ? mostRecentFinalRelease : it }.unique()
-        allVersions.remove(currentBaseVersion)
-
-        // A target version may be something that is yet unreleased, so filter that out
-        allVersions.removeAll { !releasedVersions.contains(it) }
-
-        assert !allVersions.isEmpty()
-
-        allVersions.each { it ->
-            def baselineVersion = results.baseline(it)
-            baselineVersion.maxExecutionTimeRegression = maxExecutionTimeRegression
-            baselineVersion.maxMemoryRegression = maxMemoryRegression
-        }
-
-        println "Running performance tests for test project '$testProject', no. of runs: $runs"
-        warmUpRuns.times {
-            println "Executing warm-up run #${it + 1}"
-            runOnce()
-        }
-        results.clear()
-        runs.times {
-            println "Executing test run #${it + 1}"
-            runOnce()
-        }
-        reporter.report(results)
-        results
-    }
-
-    void runOnce() {
-        File projectDir = testProjectLocator.findProjectDir(testProject)
-        results.baselineVersions.each {
-            println "Gradle ${it.version}..."
-            runOnce(buildContext.distribution(it.version), projectDir, it.results)
-        }
-
-        println "Current Gradle..."
-        runOnce(current, projectDir, results.current)
-    }
-
-    void runOnce(GradleDistribution dist, File projectDir, MeasuredOperationList results) {
-        def executer = this.executer(dist, projectDir)
-        dataCollector.beforeExecute(projectDir, executer)
-        def operation = timer.measure { MeasuredOperation operation ->
-            executer.run()
-        }
-        if (operation.exception == null) {
-            dataCollector.collect(projectDir, operation)
-        }
-        results.add(operation)
-    }
-
-    GradleExecuter executer(GradleDistribution dist, File projectDir) {
-        dist.executer(testDirectoryProvider).
-                requireGradleHome().
-                withDeprecationChecksDisabled().
-                withStackTraceChecksDisabled().
-                withArguments('-u').
-                inDirectory(projectDir).
-                withTasks(tasksToRun).
-                withArguments(args)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestSpec.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestSpec.java
new file mode 100644
index 0000000..58f099c
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/PerformanceTestSpec.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+public class PerformanceTestSpec {
+    String testId;
+    int runs;
+    int warmUpRuns;
+
+    public String getTestId() {
+        return testId;
+    }
+
+    public void setTestId(String testId) {
+        this.testId = testId;
+    }
+
+    public int getRuns() {
+        return runs;
+    }
+
+    public void setRuns(int runs) {
+        this.runs = runs;
+    }
+
+    public int getWarmUpRuns() {
+        return warmUpRuns;
+    }
+
+    public void setWarmUpRuns(int warmUpRuns) {
+        this.warmUpRuns = warmUpRuns;
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/TextFileDataReporter.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/TextFileDataReporter.groovy
index 2b7141d..a10c154 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/TextFileDataReporter.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/TextFileDataReporter.groovy
@@ -16,14 +16,14 @@
 
 package org.gradle.performance.fixture
 
-class TextFileDataReporter implements DataReporter {
+class TextFileDataReporter implements DataReporter<CrossVersionPerformanceResults> {
     private final File outputFile
 
     TextFileDataReporter(File outputFile) {
         this.outputFile = outputFile
     }
 
-    void report(PerformanceResults results) {
+    void report(CrossVersionPerformanceResults results) {
         outputFile.parentFile.mkdirs()
         results.baselineVersions.each {
             outputFile << it.getSpeedStatsAgainst(results.displayName, results.current)
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/ToolingApiBackedGradleSession.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/ToolingApiBackedGradleSession.groovy
new file mode 100644
index 0000000..d2b74ed
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/ToolingApiBackedGradleSession.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture
+
+import groovy.transform.CompileStatic
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.tooling.BuildLauncher
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.internal.consumer.DefaultGradleConnector
+
+ at CompileStatic
+class ToolingApiBackedGradleSession implements GradleSession {
+
+    final GradleInvocationSpec invocation
+
+    private final TestDirectoryProvider testDirectoryProvider
+    private final GradleExecuterBackedSession executerBackedSession
+    private ProjectConnection projectConnection
+    private BuildLauncher buildLauncher
+
+    ToolingApiBackedGradleSession(GradleInvocationSpec invocation, TestDirectoryProvider testDirectoryProvider) {
+        this.testDirectoryProvider = testDirectoryProvider
+        this.invocation = invocation
+        this.executerBackedSession = new GradleExecuterBackedSession(invocation, testDirectoryProvider)
+    }
+
+    @Override
+    void prepare() {
+        executerBackedSession.prepare()
+
+        DefaultGradleConnector connector = GradleConnector.newConnector() as DefaultGradleConnector
+        projectConnection = connector
+                .daemonBaseDir(testDirectoryProvider.testDirectory.file("daemon"))
+                .forProjectDirectory(invocation.workingDirectory)
+                .useInstallation(invocation.gradleDistribution.gradleHomeDir)
+                .connect()
+
+        buildLauncher = projectConnection.newBuild()
+                .withArguments(invocation.args + ["-u"] as String[])
+                .forTasks(invocation.tasksToRun as String[])
+                .setJvmArguments(invocation.gradleOpts as String[])
+                .setStandardOutput(System.out)
+                .setStandardError(System.err)
+    }
+
+    @Override
+    Runnable runner() {
+        return { buildLauncher.run() }
+    }
+
+    @Override
+    void cleanup() {
+        projectConnection?.close()
+        executerBackedSession.cleanup()
+    }
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/WaitingReader.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/WaitingReader.java
new file mode 100644
index 0000000..90d625f
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/fixture/WaitingReader.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.fixture;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ * When there are no more lines to read from the source reader, this implementation waits until a full new line of content is available.
+ * We need this kind of thing because when gc log is used by a forked process (e.g. the daemon) there is a delay
+ * between a) forked process has finished and b) the gc log information has the final heap information.
+ * The gc log can be written out in chunks (especially if a big GC is happening) which means that we have to wait
+ * for a full line to be available instead of just any content before returning from readLine().
+ */
+public class WaitingReader {
+
+    private static final int FIFTY_KB = 51200;
+    private static final int EOF = -1;
+    private static final char NEW_LINE = '\n';
+    private final BufferedReader reader;
+    private final int timeoutMs;
+    private final int clockTick;
+
+    //for testing
+    int retriedCount;
+
+    public WaitingReader(BufferedReader reader) {
+        this(reader, 5000, 200);
+    }
+
+    public WaitingReader(BufferedReader reader, int timeoutMs, int clockTick) {
+        this.reader = reader;
+        this.timeoutMs = timeoutMs;
+        this.clockTick = clockTick;
+    }
+
+    String readLine() throws IOException {
+        long upTo = System.currentTimeMillis() + timeoutMs;
+        reader.mark(FIFTY_KB);
+        int character = EOF;
+        while(character != NEW_LINE) {
+            character = reader.read();
+            if (character == EOF) {
+                if (System.currentTimeMillis() >= upTo) {
+                    break;
+                }
+                try {
+                    Thread.sleep(clockTick);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+                retriedCount++;
+            }
+        }
+        reader.reset();
+        String line = reader.readLine();
+        return line;
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/DataSeries.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/DataSeries.java
index 3e7cd71..7582bd5 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/DataSeries.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/DataSeries.java
@@ -16,6 +16,7 @@
 
 package org.gradle.performance.measure;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 
 /**
@@ -25,6 +26,7 @@ public class DataSeries<Q> extends ArrayList<Amount<Q>> {
     private final Amount<Q> average;
     private final Amount<Q> max;
     private final Amount<Q> min;
+    private final Amount<Q> stddev;
 
     public DataSeries(Iterable<? extends Amount<Q>> values) {
         for (Amount<Q> value : values) {
@@ -37,6 +39,7 @@ public class DataSeries<Q> extends ArrayList<Amount<Q>> {
             average = null;
             max = null;
             min = null;
+            stddev = null;
             return;
         }
 
@@ -52,6 +55,21 @@ public class DataSeries<Q> extends ArrayList<Amount<Q>> {
         average = total.div(size());
         this.min = min;
         this.max = max;
+
+        BigDecimal sumSquares = BigDecimal.ZERO;
+        Units<Q> baseUnits = average.getUnits().getBaseUnits();
+        BigDecimal averageValue = average.toUnits(baseUnits).getValue();
+        for (int i = 0; i < size(); i++) {
+            Amount<Q> amount = get(i);
+            BigDecimal diff = amount.toUnits(baseUnits).getValue();
+            diff = diff.subtract(averageValue);
+            diff = diff.multiply(diff);
+            sumSquares = sumSquares.add(diff);
+        }
+        // This isn't quite right, as we may lose precision when converting to a double
+        BigDecimal result = BigDecimal.valueOf(Math.sqrt(sumSquares.divide(BigDecimal.valueOf(size()), BigDecimal.ROUND_HALF_UP).doubleValue())).setScale(2, BigDecimal.ROUND_HALF_UP);
+
+        stddev = Amount.valueOf(result, baseUnits);
     }
 
     public Amount<Q> getAverage() {
@@ -65,4 +83,8 @@ public class DataSeries<Q> extends ArrayList<Amount<Q>> {
     public Amount<Q> getMax() {
         return max;
     }
+
+    public Amount<Q> getStddev() {
+        return stddev;
+    }
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/MeasuredOperation.groovy b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/MeasuredOperation.groovy
index 0294b04..f763aa9 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/MeasuredOperation.groovy
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/measure/MeasuredOperation.groovy
@@ -16,7 +16,13 @@
 
 package org.gradle.performance.measure
 
+import org.joda.time.DateTime
+
 public class MeasuredOperation {
+    DateTime start
+    DateTime end
+    Amount<Duration> totalTime
+    Amount<Duration> configurationTime
     Amount<Duration> executionTime
     Exception exception
     /** The non-collectable heap usage at the end of the build. This was the original metric used */
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/AllResultsStore.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/AllResultsStore.java
new file mode 100644
index 0000000..013be97
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/AllResultsStore.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import org.gradle.internal.concurrent.CompositeStoppable;
+
+import java.io.Closeable;
+import java.util.List;
+
+public class AllResultsStore implements ResultsStore, Closeable {
+    private final CrossVersionResultsStore crossVersion = new CrossVersionResultsStore();
+    private final CrossBuildResultsStore crossBuild = new CrossBuildResultsStore();
+    private final CompositeResultsStore store = new CompositeResultsStore(crossVersion, crossBuild);
+
+    @Override
+    public List<String> getTestNames() {
+        return store.getTestNames();
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(String testName) {
+        return store.getTestResults(testName);
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(String testName, int mostRecentN) {
+        return store.getTestResults(testName, mostRecentN);
+    }
+
+    @Override
+    public void close() {
+        CompositeStoppable.stoppable(crossVersion, crossBuild).stop();
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CompositeResultsStore.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CompositeResultsStore.java
new file mode 100644
index 0000000..d90d747
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CompositeResultsStore.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import java.util.*;
+
+public class CompositeResultsStore implements ResultsStore {
+    private final List<ResultsStore> stores;
+    private Map<String, ResultsStore> tests;
+
+    public CompositeResultsStore(ResultsStore... stores) {
+        this.stores = Arrays.asList(stores);
+    }
+
+    @Override
+    public List<String> getTestNames() {
+        buildTests();
+        return new ArrayList<String>(tests.keySet());
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(String testName) {
+        return getStoreForTest(testName).getTestResults(testName);
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(String testName, int mostRecentN) {
+        return getStoreForTest(testName).getTestResults(testName, mostRecentN);
+    }
+
+    private ResultsStore getStoreForTest(String testName) {
+        buildTests();
+        if (!tests.containsKey(testName)) {
+            throw new IllegalArgumentException(String.format("Unknown test '%s'.", testName));
+        }
+        return tests.get(testName);
+    }
+
+    private void buildTests() {
+        if (tests == null) {
+            Map<String, ResultsStore> tests = new LinkedHashMap<String, ResultsStore>();
+            for (ResultsStore store : stores) {
+                for (String testName : store.getTestNames()) {
+                    if (tests.containsKey(testName)) {
+                        throw new IllegalArgumentException(String.format("Duplicate test '%s'", testName));
+                    }
+                    tests.put(testName, store);
+                }
+            }
+            this.tests = tests;
+        }
+    }
+
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ConnectionAction.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ConnectionAction.java
new file mode 100644
index 0000000..a10b08f
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ConnectionAction.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import java.sql.Connection;
+
+public interface ConnectionAction<T> {
+    T execute(Connection connection) throws Exception;
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildResultsStore.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildResultsStore.java
new file mode 100644
index 0000000..d7541ad
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildResultsStore.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.gradle.performance.fixture.BuildDisplayInfo;
+import org.gradle.performance.fixture.CrossBuildPerformanceResults;
+import org.gradle.performance.fixture.DataReporter;
+import org.gradle.performance.fixture.MeasuredOperationList;
+import org.gradle.performance.measure.DataAmount;
+import org.gradle.performance.measure.Duration;
+import org.gradle.performance.measure.MeasuredOperation;
+
+import java.io.Closeable;
+import java.io.File;
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+public class CrossBuildResultsStore implements ResultsStore, DataReporter<CrossBuildPerformanceResults>, Closeable {
+
+    private final File dbFile;
+    private final H2FileDb db;
+
+    public CrossBuildResultsStore() {
+        this(new File(System.getProperty("user.home"), ".gradle-performance-test-data/cross-build-results"));
+    }
+
+    public CrossBuildResultsStore(File dbFile) {
+        this.dbFile = dbFile;
+        this.db = new H2FileDb(dbFile, new CrossBuildResultsSchemaInitializer());
+    }
+
+    public void report(final CrossBuildPerformanceResults results) {
+        try {
+            db.withConnection(new ConnectionAction<Void>() {
+                public Void execute(Connection connection) throws Exception {
+                    long executionId;
+                    PreparedStatement statement = connection.prepareStatement("insert into testExecution(testId, executionTime, versionUnderTest, operatingSystem, jvm, vcsBranch, vcsCommit, testGroup) values (?, ?, ?, ?, ?, ?, ?, ?)");
+                    try {
+                        statement.setString(1, results.getTestId());
+                        statement.setTimestamp(2, new Timestamp(results.getTestTime()));
+                        statement.setString(3, results.getVersionUnderTest());
+                        statement.setString(4, results.getOperatingSystem());
+                        statement.setString(5, results.getJvm());
+                        statement.setString(6, results.getVcsBranch());
+                        statement.setString(7, results.getVcsCommit());
+                        statement.setString(8, results.getTestGroup());
+                        statement.execute();
+                        ResultSet keys = statement.getGeneratedKeys();
+                        keys.next();
+                        executionId = keys.getLong(1);
+                    } finally {
+                        statement.close();
+                    }
+                    statement = connection.prepareStatement("insert into testOperation(testExecution, testProject, displayName, tasks, args, totalTime, configurationTime, executionTime, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+                    try {
+                        for (BuildDisplayInfo displayInfo : results.getBuilds()) {
+                            addOperations(statement, executionId, displayInfo, results.buildResult(displayInfo));
+                        }
+                    } finally {
+                        statement.close();
+                    }
+                    return null;
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not open results datastore '%s'.", dbFile), e);
+        }
+    }
+
+    private void addOperations(PreparedStatement statement, long executionId, BuildDisplayInfo displayInfo, MeasuredOperationList operations) throws SQLException {
+        for (MeasuredOperation operation : operations) {
+            statement.setLong(1, executionId);
+            statement.setString(2, displayInfo.getProjectName());
+            statement.setString(3, displayInfo.getDisplayName());
+            statement.setObject(4, toArray(displayInfo.getTasksToRun()));
+            statement.setObject(5, toArray(displayInfo.getArgs()));
+            statement.setBigDecimal(6, operation.getTotalTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(7, operation.getConfigurationTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(8, operation.getExecutionTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(9, operation.getTotalMemoryUsed().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(10, operation.getTotalHeapUsage().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(11, operation.getMaxHeapUsage().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(12, operation.getMaxUncollectedHeap().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(13, operation.getMaxCommittedHeap().toUnits(DataAmount.BYTES).getValue());
+            statement.execute();
+        }
+    }
+
+    private String[] toArray(List<String> list) {
+        return list.toArray(new String[list.size()]);
+    }
+
+    public void close() {
+        db.close();
+    }
+
+    public List<String> getTestNames() {
+        try {
+            return db.withConnection(new ConnectionAction<List<String>>() {
+                public List<String> execute(Connection connection) throws Exception {
+                    List<String> testNames = new ArrayList<String>();
+                    ResultSet testGroups = connection.createStatement().executeQuery("select distinct testGroup from testExecution order by testGroup");
+                    PreparedStatement testIdsStatement = connection.prepareStatement("select distinct testId from testExecution where testGroup = ? order by testId");
+                    while (testGroups.next()) {
+                        testIdsStatement.setString(1, testGroups.getString(1));
+                        ResultSet testExecutions = testIdsStatement.executeQuery();
+                        while (testExecutions.next()) {
+                            testNames.add(testExecutions.getString(1));
+                        }
+                        testExecutions.close();
+                    }
+                    testIdsStatement.close();
+                    testGroups.close();
+                    return testNames;
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not load test history from datastore '%s'.", dbFile), e);
+        }
+    }
+
+    @Override
+    public CrossBuildTestExecutionHistory getTestResults(String testName) {
+        return getTestResults(testName, Integer.MAX_VALUE);
+    }
+
+    public CrossBuildTestExecutionHistory getTestResults(final String testName, final int mostRecentN) {
+        try {
+            return db.withConnection(new ConnectionAction<CrossBuildTestExecutionHistory>() {
+                public CrossBuildTestExecutionHistory execute(Connection connection) throws Exception {
+                    List<CrossBuildPerformanceResults> results = Lists.newArrayList();
+                    Set<BuildDisplayInfo> builds = Sets.newTreeSet(new Comparator<BuildDisplayInfo>() {
+                        @Override
+                        public int compare(BuildDisplayInfo o1, BuildDisplayInfo o2) {
+                            return o1.getDisplayName().compareTo(o2.getDisplayName());
+                        }
+                    });
+                    PreparedStatement executionsForName = connection.prepareStatement("select top ? id, executionTime, versionUnderTest, operatingSystem, jvm, vcsBranch, vcsCommit, testGroup from testExecution where testId = ? order by executionTime desc");
+                    PreparedStatement operationsForExecution = connection.prepareStatement("select testProject, displayName, tasks, args, totalTime, configurationTime, executionTime, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes from testOperation where testExecution = ?");
+                    executionsForName.setInt(1, mostRecentN);
+                    executionsForName.setString(2, testName);
+                    ResultSet testExecutions = executionsForName.executeQuery();
+                    while (testExecutions.next()) {
+                        long id = testExecutions.getLong(1);
+                        CrossBuildPerformanceResults performanceResults = new CrossBuildPerformanceResults();
+                        performanceResults.setTestId(testName);
+                        performanceResults.setTestTime(testExecutions.getTimestamp(2).getTime());
+                        performanceResults.setVersionUnderTest(testExecutions.getString(3));
+                        performanceResults.setOperatingSystem(testExecutions.getString(4));
+                        performanceResults.setJvm(testExecutions.getString(5));
+                        performanceResults.setVcsBranch(testExecutions.getString(6).trim());
+                        performanceResults.setVcsCommit(testExecutions.getString(7));
+                        performanceResults.setTestGroup(testExecutions.getString(8));
+
+                        if (ignore(performanceResults)) {
+                            continue;
+                        }
+
+                        results.add(performanceResults);
+
+                        operationsForExecution.setLong(1, id);
+                        ResultSet resultSet = operationsForExecution.executeQuery();
+                        while (resultSet.next()) {
+                            BuildDisplayInfo displayInfo = new BuildDisplayInfo(
+                                    resultSet.getString(1),
+                                    resultSet.getString(2),
+                                    toList(resultSet.getObject(3)),
+                                    toList(resultSet.getObject(4))
+                            );
+
+                            MeasuredOperation operation = new MeasuredOperation();
+                            operation.setTotalTime(Duration.millis(resultSet.getBigDecimal(5)));
+                            operation.setConfigurationTime(Duration.millis(resultSet.getBigDecimal(6)));
+                            operation.setExecutionTime(Duration.millis(resultSet.getBigDecimal(7)));
+                            operation.setTotalMemoryUsed(DataAmount.bytes(resultSet.getBigDecimal(8)));
+                            operation.setTotalHeapUsage(DataAmount.bytes(resultSet.getBigDecimal(9)));
+                            operation.setMaxHeapUsage(DataAmount.bytes(resultSet.getBigDecimal(10)));
+                            operation.setMaxUncollectedHeap(DataAmount.bytes(resultSet.getBigDecimal(11)));
+                            operation.setMaxCommittedHeap(DataAmount.bytes(resultSet.getBigDecimal(12)));
+
+                            performanceResults.buildResult(displayInfo).add(operation);
+                            builds.add(displayInfo);
+                        }
+                        resultSet.close();
+                    }
+                    testExecutions.close();
+                    operationsForExecution.close();
+                    executionsForName.close();
+
+                    return new CrossBuildTestExecutionHistory(testName, ImmutableList.copyOf(builds), results);
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not load results from datastore '%s'.", dbFile), e);
+        }
+    }
+
+    private boolean ignore(CrossBuildPerformanceResults performanceResults) {
+        return performanceResults.getVcsCommit().equals("be4e537ebdaab43fd1dae5c4b1d52a56987f5be2")
+                || performanceResults.getVcsCommit().equals("508ccbeb7633413609bd3be205c40f30a8c5f2bb")
+                || performanceResults.getVcsCommit().equals("fdd431387993e1d7e4d6d3aec31a43ec4b533567");
+    }
+
+    private List<String> toList(Object object) {
+        Object[] value = (Object[]) object;
+        List<String> list = Lists.newLinkedList();
+        for (Object aValue : value) {
+            list.add(aValue.toString());
+        }
+        return list;
+    }
+
+    private class CrossBuildResultsSchemaInitializer implements ConnectionAction<Void> {
+        @Override
+        public Void execute(Connection connection) throws Exception {
+            Statement statement = connection.createStatement();
+            statement.execute("create table if not exists testExecution (id bigint identity not null, testId varchar not null, executionTime timestamp not null, versionUnderTest varchar not null, operatingSystem varchar not null, jvm varchar not null, vcsBranch varchar not null, vcsCommit varchar)");
+            statement.execute("create table if not exists testOperation (testExecution bigint not null, testProject varchar not null, displayName varchar not null, tasks array not null, args array not null, executionTimeMs decimal not null, heapUsageBytes decimal not null, totalHeapUsageBytes decimal, maxHeapUsageBytes decimal, maxUncollectedHeapBytes decimal, maxCommittedHeapBytes decimal, foreign key(testExecution) references testExecution(id))");
+            statement.execute("alter table testExecution add column if not exists testGroup varchar");
+            statement.execute("update testExecution set testGroup = 'old vs new java plugin' where testGroup is null and testId like '%old vs new java plugin%'");
+            statement.execute("update testExecution set testGroup = 'project using variants' where testGroup is null and testId like '%project using variants%'");
+            statement.execute("update testExecution set testGroup = testId where testGroup is null");
+            statement.execute("alter table testExecution alter column testGroup set not null");
+            if (columnExists(connection, "TESTOPERATION", "EXECUTIONTIMEMS")) {
+                statement.execute("alter table testOperation alter column executionTimeMs rename to totalTime");
+                statement.execute("alter table testOperation add column executionTime decimal");
+                statement.execute("update testOperation set executionTime = 0");
+                statement.execute("alter table testOperation alter column executionTime set not null");
+                statement.execute("alter table testOperation add column configurationTime decimal");
+                statement.execute("update testOperation set configurationTime = 0");
+                statement.execute("alter table testOperation alter column configurationTime set not null");
+            }
+            statement.close();
+            return null;
+        }
+
+        private boolean columnExists(Connection connection, String table, String column) throws SQLException {
+            ResultSet columns = connection.getMetaData().getColumns(null, null, table, column);
+            boolean exists = columns.next();
+            columns.close();
+            return exists;
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildTestExecutionHistory.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildTestExecutionHistory.java
new file mode 100644
index 0000000..544b5c5
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossBuildTestExecutionHistory.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import org.gradle.performance.fixture.BuildDisplayInfo;
+import org.gradle.performance.fixture.CrossBuildPerformanceResults;
+import org.gradle.performance.fixture.MeasuredOperationList;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public class CrossBuildTestExecutionHistory implements TestExecutionHistory {
+    private final String name;
+
+    private final List<BuildDisplayInfo> builds;
+
+    private final List<CrossBuildPerformanceResults> newestFirst;
+
+    public CrossBuildTestExecutionHistory(String name, List<BuildDisplayInfo> builds, List<CrossBuildPerformanceResults> newestFirst) {
+        this.name = name;
+        this.builds = builds;
+        this.newestFirst = newestFirst;
+    }
+
+    public List<BuildDisplayInfo> getBuilds() {
+        return builds;
+    }
+
+    public List<CrossBuildPerformanceResults> getResults() {
+        return newestFirst;
+    }
+
+    @Override
+    public String getId() {
+        return name.replaceAll("\\s+", "-");
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public List<PerformanceResults> getPerformanceResults() {
+        return Lists.transform(newestFirst, new Function<CrossBuildPerformanceResults, PerformanceResults>() {
+            public PerformanceResults apply(@Nullable final CrossBuildPerformanceResults results) {
+                return new KnownBuildSpecificationsPerformanceResults(results);
+            }
+        });
+    }
+
+    @Override
+    public int getExperimentCount() {
+        return builds.size();
+    }
+
+    @Override
+    public List<String> getExperimentLabels() {
+        return Lists.transform(builds, new Function<BuildDisplayInfo, String>() {
+            public String apply(@Nullable BuildDisplayInfo specification) {
+                return specification.getDisplayName();
+            }
+        });
+    }
+
+    private class KnownBuildSpecificationsPerformanceResults implements PerformanceResults {
+        private final CrossBuildPerformanceResults results;
+
+        public KnownBuildSpecificationsPerformanceResults(CrossBuildPerformanceResults results) {
+            this.results = results;
+        }
+
+        @Override
+        public String getVersionUnderTest() {
+            return results.getVersionUnderTest();
+        }
+
+        @Override
+        public String getVcsBranch() {
+            return results.getVcsBranch();
+        }
+
+        @Override
+        public long getTestTime() {
+            return results.getTestTime();
+        }
+
+        @Override
+        public String getVcsCommit() {
+            return results.getVcsCommit();
+        }
+
+        @Override
+        public List<MeasuredOperationList> getExperiments() {
+            return Lists.transform(builds, new Function<BuildDisplayInfo, MeasuredOperationList>() {
+                @Override
+                public MeasuredOperationList apply(@Nullable BuildDisplayInfo specification) {
+                    return results.buildResult(specification);
+                }
+            });
+        }
+
+        @Override
+        public String getOperatingSystem() {
+            return results.getOperatingSystem();
+        }
+
+        @Override
+        public String getJvm() {
+            return results.getJvm();
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionResultsStore.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionResultsStore.java
new file mode 100644
index 0000000..60c0e72
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionResultsStore.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import org.gradle.internal.UncheckedException;
+import org.gradle.performance.fixture.BaselineVersion;
+import org.gradle.performance.fixture.CrossVersionPerformanceResults;
+import org.gradle.performance.fixture.DataReporter;
+import org.gradle.performance.fixture.MeasuredOperationList;
+import org.gradle.performance.measure.DataAmount;
+import org.gradle.performance.measure.Duration;
+import org.gradle.performance.measure.MeasuredOperation;
+import org.gradle.util.GradleVersion;
+
+import java.io.Closeable;
+import java.io.File;
+import java.sql.*;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * A {@link org.gradle.performance.fixture.DataReporter} implementation that stores results in an H2 relational database.
+ */
+public class CrossVersionResultsStore implements DataReporter<CrossVersionPerformanceResults>, ResultsStore, Closeable {
+    private final File dbFile;
+    private final long ignoreV17Before;
+    private final H2FileDb db;
+
+    public CrossVersionResultsStore() {
+        this(new File(System.getProperty("user.home"), ".gradle-performance-test-data/results"));
+    }
+
+    public CrossVersionResultsStore(File dbFile) {
+        this.dbFile = dbFile;
+        db = new H2FileDb(dbFile, new CrossVersionResultsSchemaInitializer());
+
+        // Ignore some broken samples before the given date
+        DateFormat timeStampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        timeStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+        try {
+            ignoreV17Before = timeStampFormat.parse("2013-07-03 00:00:00").getTime();
+        } catch (ParseException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public void report(final CrossVersionPerformanceResults results) {
+        try {
+            db.withConnection(new ConnectionAction<Void>() {
+                public Void execute(Connection connection) throws Exception {
+                    long testId;
+                    PreparedStatement statement = connection.prepareStatement("insert into testExecution(testId, executionTime, targetVersion, testProject, tasks, args, operatingSystem, jvm, vcsBranch, vcsCommit) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+                    try {
+                        statement.setString(1, results.getTestId());
+                        statement.setTimestamp(2, new Timestamp(results.getTestTime()));
+                        statement.setString(3, results.getVersionUnderTest());
+                        statement.setString(4, results.getTestProject());
+                        statement.setObject(5, results.getTasks());
+                        statement.setObject(6, results.getArgs());
+                        statement.setString(7, results.getOperatingSystem());
+                        statement.setString(8, results.getJvm());
+                        statement.setString(9, results.getVcsBranch());
+                        statement.setString(10, results.getVcsCommit());
+                        statement.execute();
+                        ResultSet keys = statement.getGeneratedKeys();
+                        keys.next();
+                        testId = keys.getLong(1);
+                    } finally {
+                        statement.close();
+                    }
+                    statement = connection.prepareStatement("insert into testOperation(testExecution, version, totalTime, configurationTime, executionTime, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+                    try {
+                        addOperations(statement, testId, null, results.getCurrent());
+                        for (BaselineVersion baselineVersion : results.getBaselineVersions()) {
+                            addOperations(statement, testId, baselineVersion.getVersion(), baselineVersion.getResults());
+                        }
+                    } finally {
+                        statement.close();
+                    }
+                    return null;
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not open results datastore '%s'.", dbFile), e);
+        }
+    }
+
+    private void addOperations(PreparedStatement statement, long testId, String version, MeasuredOperationList operations) throws SQLException {
+        for (MeasuredOperation operation : operations) {
+            statement.setLong(1, testId);
+            statement.setString(2, version);
+            statement.setBigDecimal(3, operation.getTotalTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(4, operation.getConfigurationTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(5, operation.getExecutionTime().toUnits(Duration.MILLI_SECONDS).getValue());
+            statement.setBigDecimal(6, operation.getTotalMemoryUsed().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(7, operation.getTotalHeapUsage().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(8, operation.getMaxHeapUsage().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(9, operation.getMaxUncollectedHeap().toUnits(DataAmount.BYTES).getValue());
+            statement.setBigDecimal(10, operation.getMaxCommittedHeap().toUnits(DataAmount.BYTES).getValue());
+            statement.execute();
+        }
+    }
+
+    @Override
+    public List<String> getTestNames() {
+        try {
+            return db.withConnection(new ConnectionAction<List<String>>() {
+                public List<String> execute(Connection connection) throws Exception {
+                    List<String> testNames = new ArrayList<String>();
+                    ResultSet testExecutions = connection.createStatement().executeQuery("select distinct testId from testExecution order by testId");
+                    while (testExecutions.next()) {
+                        testNames.add(testExecutions.getString(1));
+                    }
+                    return testNames;
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not load test history from datastore '%s'.", dbFile), e);
+        }
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(String testName) {
+        return getTestResults(testName, Integer.MAX_VALUE);
+    }
+
+    @Override
+    public TestExecutionHistory getTestResults(final String testName, final int mostRecentN) {
+        try {
+            return db.withConnection(new ConnectionAction<CrossVersionTestExecutionHistory>() {
+                public CrossVersionTestExecutionHistory execute(Connection connection) throws Exception {
+                    List<CrossVersionPerformanceResults> results = new ArrayList<CrossVersionPerformanceResults>();
+                    Set<String> allVersions = new TreeSet<String>(new Comparator<String>() {
+                        public int compare(String o1, String o2) {
+                            return GradleVersion.version(o1).compareTo(GradleVersion.version(o2));
+                        }
+                    });
+                    Set<String> allBranches = new TreeSet<String>();
+                    PreparedStatement executionsForName = connection.prepareStatement("select top ? id, executionTime, targetVersion, testProject, tasks, args, operatingSystem, jvm, vcsBranch, vcsCommit from testExecution where testId = ? order by executionTime desc");
+                    PreparedStatement operationsForExecution = connection.prepareStatement("select version, totalTime, configurationTime, executionTime, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes from testOperation where testExecution = ?");
+                    executionsForName.setInt(1, mostRecentN);
+                    executionsForName.setString(2, testName);
+                    ResultSet testExecutions = executionsForName.executeQuery();
+                    while (testExecutions.next()) {
+                        long id = testExecutions.getLong(1);
+                        CrossVersionPerformanceResults performanceResults = new CrossVersionPerformanceResults();
+                        performanceResults.setTestId(testName);
+                        performanceResults.setTestTime(testExecutions.getTimestamp(2).getTime());
+                        performanceResults.setVersionUnderTest(testExecutions.getString(3));
+                        performanceResults.setTestProject(testExecutions.getString(4));
+                        performanceResults.setTasks(toArray(testExecutions.getObject(5)));
+                        performanceResults.setArgs(toArray(testExecutions.getObject(6)));
+                        performanceResults.setOperatingSystem(testExecutions.getString(7));
+                        performanceResults.setJvm(testExecutions.getString(8));
+                        performanceResults.setVcsBranch(testExecutions.getString(9).trim());
+                        performanceResults.setVcsCommit(testExecutions.getString(10));
+
+                        results.add(performanceResults);
+                        allBranches.add(performanceResults.getVcsBranch());
+
+                        operationsForExecution.setLong(1, id);
+                        ResultSet builds = operationsForExecution.executeQuery();
+                        while (builds.next()) {
+                            String version = builds.getString(1);
+                            if ("1.7".equals(version) && performanceResults.getTestTime() <= ignoreV17Before) {
+                                // Ignore some broken samples
+                                continue;
+                            }
+                            MeasuredOperation operation = new MeasuredOperation();
+                            operation.setTotalTime(Duration.millis(builds.getBigDecimal(2)));
+                            operation.setConfigurationTime(Duration.millis(builds.getBigDecimal(3)));
+                            operation.setExecutionTime(Duration.millis(builds.getBigDecimal(4)));
+                            operation.setTotalMemoryUsed(DataAmount.bytes(builds.getBigDecimal(5)));
+                            operation.setTotalHeapUsage(DataAmount.bytes(builds.getBigDecimal(6)));
+                            operation.setMaxHeapUsage(DataAmount.bytes(builds.getBigDecimal(7)));
+                            operation.setMaxUncollectedHeap(DataAmount.bytes(builds.getBigDecimal(8)));
+                            operation.setMaxCommittedHeap(DataAmount.bytes(builds.getBigDecimal(9)));
+
+                            if (version == null) {
+                                performanceResults.getCurrent().add(operation);
+                            } else {
+                                BaselineVersion baselineVersion = performanceResults.baseline(version);
+                                baselineVersion.getResults().add(operation);
+                                allVersions.add(version);
+                            }
+                        }
+                    }
+                    testExecutions.close();
+                    operationsForExecution.close();
+                    executionsForName.close();
+
+                    return new CrossVersionTestExecutionHistory(testName, new ArrayList<String>(allVersions), new ArrayList<String>(allBranches), results);
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(String.format("Could not load results from datastore '%s'.", dbFile), e);
+        }
+    }
+
+    private String[] toArray(Object object) {
+        Object[] value = (Object[]) object;
+        String[] result = new String[value.length];
+        for (int i = 0; i < value.length; i++) {
+            result[i] = value[i].toString();
+        }
+        return result;
+    }
+
+    public void close() {
+        db.close();
+    }
+
+    private class CrossVersionResultsSchemaInitializer implements ConnectionAction<Void> {
+        @Override
+        public Void execute(Connection connection) throws Exception {
+            Statement statement = connection.createStatement();
+            statement.execute("create table if not exists testExecution (id bigint identity not null, testId varchar not null, executionTime timestamp not null, targetVersion varchar not null, testProject varchar not null, tasks array not null, args array not null, operatingSystem varchar not null, jvm varchar not null)");
+            statement.execute("create table if not exists testOperation (testExecution bigint not null, version varchar, executionTimeMs decimal not null, heapUsageBytes decimal not null, foreign key(testExecution) references testExecution(id))");
+            statement.execute("alter table testExecution add column if not exists vcsBranch varchar not null default 'master'");
+            statement.execute("alter table testExecution add column if not exists vcsCommit varchar");
+            statement.execute("alter table testOperation add column if not exists totalHeapUsageBytes decimal");
+            statement.execute("alter table testOperation add column if not exists maxHeapUsageBytes decimal");
+            statement.execute("alter table testOperation add column if not exists maxUncollectedHeapBytes decimal");
+            statement.execute("alter table testOperation add column if not exists maxCommittedHeapBytes decimal");
+            if (columnExists(connection, "TESTOPERATION", "EXECUTIONTIMEMS")) {
+                statement.execute("alter table testOperation alter column executionTimeMs rename to totalTime");
+                statement.execute("alter table testOperation add column executionTime decimal");
+                statement.execute("update testOperation set executionTime = 0");
+                statement.execute("alter table testOperation alter column executionTime set not null");
+                statement.execute("alter table testOperation add column configurationTime decimal");
+                statement.execute("update testOperation set configurationTime = 0");
+                statement.execute("alter table testOperation alter column configurationTime set not null");
+            }
+            statement.close();
+            return null;
+        }
+
+        private boolean columnExists(Connection connection, String table, String column) throws SQLException {
+            ResultSet columns = connection.getMetaData().getColumns(null, null, table, column);
+            boolean exists = columns.next();
+            columns.close();
+            return exists;
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionTestExecutionHistory.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionTestExecutionHistory.java
new file mode 100644
index 0000000..527e5f0
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/CrossVersionTestExecutionHistory.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.performance.results;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import org.gradle.performance.fixture.CrossVersionPerformanceResults;
+import org.gradle.performance.fixture.MeasuredOperationList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CrossVersionTestExecutionHistory implements TestExecutionHistory {
+    private final String name;
+    private final List<String> versions;
+    private final List<String> branches;
+    private final List<CrossVersionPerformanceResults> newestFirst;
+    private List<CrossVersionPerformanceResults> oldestFirst;
+    private List<String> knownVersions;
+
+    public CrossVersionTestExecutionHistory(String name, List<String> versions, List<String> branches, List<CrossVersionPerformanceResults> newestFirst) {
+        this.name = name;
+        this.versions = versions;
+        this.branches = branches;
+        this.newestFirst = newestFirst;
+    }
+
+    @Override
+    public String getId() {
+        return name.replaceAll("\\s+", "-");
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public List<String> getBaselineVersions() {
+        return versions;
+    }
+
+    public List<String> getBranches() {
+        return branches;
+    }
+
+    public List<String> getKnownVersions() {
+        if (knownVersions == null) {
+            ArrayList<String> result = new ArrayList<String>();
+            result.addAll(versions);
+            result.addAll(branches);
+            knownVersions = result;
+        }
+        return knownVersions;
+    }
+
+    /**
+     * Returns results from most recent to least recent.
+     */
+    public List<CrossVersionPerformanceResults> getResults() {
+        return newestFirst;
+    }
+
+    /**
+     * Returns results from least recent to most recent.
+     */
+    public List<CrossVersionPerformanceResults> getResultsOldestFirst() {
+        if (oldestFirst == null) {
+            oldestFirst = new ArrayList<CrossVersionPerformanceResults>(newestFirst);
+            Collections.reverse(oldestFirst);
+        }
+        return oldestFirst;
+    }
+
+    @Override
+    public List<PerformanceResults> getPerformanceResults() {
+        return Lists.transform(getResults(), new Function<CrossVersionPerformanceResults, PerformanceResults>() {
+            public PerformanceResults apply(final CrossVersionPerformanceResults result) {
+                return new KnownVersionsPerformanceResults(result);
+            }
+        });
+    }
+
+    @Override
+    public int getExperimentCount() {
+        return getKnownVersions().size();
+    }
+
+    @Override
+    public List<String> getExperimentLabels() {
+        return getKnownVersions();
+    }
+
+    private class KnownVersionsPerformanceResults implements PerformanceResults {
+        private final CrossVersionPerformanceResults result;
+
+        public KnownVersionsPerformanceResults(CrossVersionPerformanceResults result) {
+            this.result = result;
+        }
+
+        public String getVersionUnderTest() {
+            return result.getVersionUnderTest();
+        }
+
+        public String getVcsBranch() {
+            return result.getVcsBranch();
+        }
+
+        public long getTestTime() {
+            return result.getTestTime();
+        }
+
+        @Override
+        public String getVcsCommit() {
+            return result.getVcsCommit();
+        }
+
+        public List<MeasuredOperationList> getExperiments() {
+            return Lists.transform(getKnownVersions(), new Function<String, MeasuredOperationList>() {
+                public MeasuredOperationList apply(String version) {
+                    return result.version(version).getResults();
+                }
+            });
+        }
+
+        @Override
+        public String getOperatingSystem() {
+            return result.getOperatingSystem();
+        }
+
+        @Override
+        public String getJvm() {
+            return result.getJvm();
+        }
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/H2FileDb.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/H2FileDb.java
new file mode 100644
index 0000000..7aa13ab
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/H2FileDb.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+public class H2FileDb {
+    private final File dbFile;
+    private final ConnectionAction<Void> schemaInitializer;
+    private Connection connection;
+
+    public H2FileDb(File dbFile, ConnectionAction<Void> schemaInitializer) {
+        this.dbFile = dbFile;
+        this.schemaInitializer = schemaInitializer;
+    }
+
+    public void close() {
+        if (connection != null) {
+            try {
+                connection.close();
+            } catch (SQLException e) {
+                throw new RuntimeException(String.format("Could not close datastore '%s'.", dbFile), e);
+            } finally {
+                connection = null;
+            }
+        }
+    }
+
+    public <T> T withConnection(ConnectionAction<T> action) throws Exception {
+        if (connection == null) {
+            dbFile.getParentFile().mkdirs();
+            Class.forName("org.h2.Driver");
+            connection = DriverManager.getConnection(String.format("jdbc:h2:%s", dbFile.getAbsolutePath()), "sa", "");
+            try {
+                schemaInitializer.execute(connection);
+            } catch (Exception e) {
+                connection.close();
+                connection = null;
+                throw e;
+            }
+        }
+        return action.execute(connection);
+    }
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/HtmlPageGenerator.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/HtmlPageGenerator.java
index 2b19386..ff6e241 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/HtmlPageGenerator.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/HtmlPageGenerator.java
@@ -17,31 +17,42 @@
 package org.gradle.performance.results;
 
 import com.googlecode.jatl.Html;
+import org.gradle.api.Transformer;
+import org.gradle.performance.fixture.MeasuredOperationList;
+import org.gradle.performance.measure.Amount;
+import org.gradle.performance.measure.DataSeries;
 import org.gradle.reporting.ReportRenderer;
 import org.gradle.util.GradleVersion;
 
 import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
 
 public abstract class HtmlPageGenerator<T> extends ReportRenderer<T, Writer> {
     protected final FormatSupport format = new FormatSupport();
 
+    protected int getDepth() {
+        return 0;
+    }
+
     protected void headSection(Html html) {
+        String rootDir = getDepth() == 0 ? "" : "../";
         html.meta()
                 .httpEquiv("Content-Type")
                 .content("text/html; charset=utf-8");
         html.link()
                 .rel("stylesheet")
                 .type("text/css")
-                .href("css/style.css")
+                .href(rootDir + "css/style.css")
                 .end();
         html.script()
-                .src("js/jquery.min-1.11.0.js")
+                .src(rootDir + "js/jquery.min-1.11.0.js")
                 .end();
         html.script()
-                .src("js/flot-0.8.1-min.js")
+                .src(rootDir + "js/flot-0.8.1-min.js")
                 .end();
         html.script()
-                .src("js/report.js")
+                .src(rootDir + "js/report.js")
                 .end();
     }
 
@@ -51,4 +62,55 @@ public abstract class HtmlPageGenerator<T> extends ReportRenderer<T, Writer> {
                 .text(String.format("Generated at %s by %s", format.executionTimestamp(), GradleVersion.current()))
                 .end();
     }
+
+    protected static class MetricsHtml extends Html {
+        public MetricsHtml(Writer writer) {
+            super(writer);
+        }
+
+        protected <T> void renderSamplesForExperiment(Iterable<MeasuredOperationList> experiments, Transformer<DataSeries<T>, MeasuredOperationList> transformer) {
+            List<DataSeries<T>> values = new ArrayList<DataSeries<T>>();
+            Amount<T> min = null;
+            Amount<T> max = null;
+            for (MeasuredOperationList testExecution : experiments) {
+                DataSeries<T> data = transformer.transform(testExecution);
+                if (data.isEmpty()) {
+                    values.add(null);
+                } else {
+                    Amount<T> value = data.getAverage();
+                    values.add(data);
+                    if (min == null || value.compareTo(min) < 0) {
+                        min = value;
+                    }
+                    if (max == null || value.compareTo(max) > 0) {
+                        max = value;
+                    }
+                }
+            }
+            if (min != null && min.equals(max)) {
+                min = null;
+                max = null;
+            }
+
+            for (DataSeries<T> data : values) {
+                if (data == null) {
+                    td().text("").end();
+                } else {
+                    Amount<T> value = data.getAverage();
+                    String classAttr = "numeric";
+                    if (value.equals(min)) {
+                        classAttr += " min-value";
+                    }
+                    if (value.equals(max)) {
+                        classAttr += " max-value";
+                    }
+                    td()
+                        .classAttr(classAttr)
+                        .title("avg: " + value + ", min: " + data.getMin() + ", max: " + data.getMax() + ", stddev: " + data.getStddev() + ", values: " + data)
+                        .text(value.format())
+                    .end();
+                }
+            }
+        }
+    }
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/IndexPageGenerator.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/IndexPageGenerator.java
index cfb31c9..3d26153 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/IndexPageGenerator.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/IndexPageGenerator.java
@@ -16,9 +16,11 @@
 
 package org.gradle.performance.results;
 
-import com.googlecode.jatl.Html;
-import org.gradle.performance.fixture.PerformanceResults;
-import org.gradle.performance.fixture.VersionResults;
+import org.gradle.api.Transformer;
+import org.gradle.performance.fixture.MeasuredOperationList;
+import org.gradle.performance.measure.DataAmount;
+import org.gradle.performance.measure.DataSeries;
+import org.gradle.performance.measure.Duration;
 
 import java.io.IOException;
 import java.io.Writer;
@@ -28,8 +30,7 @@ import java.util.List;
 public class IndexPageGenerator extends HtmlPageGenerator<ResultsStore> {
     @Override
     public void render(final ResultsStore store, Writer writer) throws IOException {
-        new Html(writer) {{
-            List<String> versions = store.getVersions();
+        new MetricsHtml(writer) {{
             html();
                 head();
                     headSection(this);
@@ -42,7 +43,7 @@ public class IndexPageGenerator extends HtmlPageGenerator<ResultsStore> {
                     div().id("controls").end();
                     table().classAttr("history");
                     for (String testName : testNames) {
-                        TestExecutionHistory testHistory = store.getTestResults(testName);
+                        TestExecutionHistory testHistory = store.getTestResults(testName, 5);
                         tr();
                             th().colspan("6").classAttr("test-execution");
                                 text(testName);
@@ -50,51 +51,43 @@ public class IndexPageGenerator extends HtmlPageGenerator<ResultsStore> {
                         end();
                         tr().classAttr("control-groups");
                             th().colspan("3").end();
-                            th().colspan(String.valueOf(versions.size())).text("Average execution time").end();
-                            th().colspan(String.valueOf(versions.size())).text("Average heap usage").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average execution time").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average heap usage").end();
                         end();
                         tr();
                             th().text("Date").end();
                             th().text("Test version").end();
                             th().text("Branch").end();
-                            for (String version : versions) {
-                                th().classAttr("numeric").text(version).end();
+                            for (String label : testHistory.getExperimentLabels()) {
+                                th().classAttr("numeric").text(label).end();
                             }
-                            for (String version : versions) {
-                                th().classAttr("numeric").text(version).end();
+                            for (String label : testHistory.getExperimentLabels()) {
+                                th().classAttr("numeric").text(label).end();
                             }
                         end();
-                        for (int i = 0; i < testHistory.getResults().size() && i < 5; i++) {
-                            PerformanceResults performanceResults = testHistory.getResults().get(i);
+                        List<PerformanceResults> results = testHistory.getPerformanceResults();
+                        for (PerformanceResults performanceResults : results) {
                             tr();
                                 td().text(format.timestamp(new Date(performanceResults.getTestTime()))).end();
                                 td().text(performanceResults.getVersionUnderTest()).end();
                                 td().text(performanceResults.getVcsBranch()).end();
-                                for (String version : versions) {
-                                    VersionResults versionResults = performanceResults.version(version);
-                                    td().classAttr("numeric");
-                                    if (versionResults.getResults().isEmpty()) {
-                                        text("");
-                                    } else {
-                                        text(versionResults.getResults().getExecutionTime().getAverage().format());
+                                renderSamplesForExperiment(performanceResults.getExperiments(), new Transformer<DataSeries<Duration>, MeasuredOperationList>() {
+                                    @Override
+                                    public DataSeries<Duration> transform(MeasuredOperationList measuredOperations) {
+                                        return measuredOperations.getTotalTime();
                                     }
-                                    end();
-                                }
-                                for (String version : versions) {
-                                    VersionResults versionResults = performanceResults.version(version);
-                                    td().classAttr("numeric");
-                                    if (versionResults.getResults().isEmpty()) {
-                                        text("");
-                                    } else {
-                                        text(versionResults.getResults().getTotalMemoryUsed().getAverage().format());
+                                });
+                                renderSamplesForExperiment(performanceResults.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                    @Override
+                                    public DataSeries<DataAmount> transform(MeasuredOperationList measuredOperations) {
+                                        return measuredOperations.getTotalMemoryUsed();
                                     }
-                                    end();
-                                }
+                                });
                             end();
                         }
                         tr();
                             td().colspan("6");
-                                String url = testHistory.getId() + ".html";
+                                String url = "tests/" + testHistory.getId() + ".html";
                                 a().href(url).text("details...").end();
                             end();
                         end();
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/PerformanceResults.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/PerformanceResults.java
new file mode 100644
index 0000000..307add7
--- /dev/null
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/PerformanceResults.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.performance.results;
+
+import org.gradle.performance.fixture.MeasuredOperationList;
+
+import java.util.List;
+
+public interface PerformanceResults {
+
+    String getVersionUnderTest();
+    String getVcsBranch();
+    long getTestTime();
+
+    String getVcsCommit();
+
+    /**
+     * Returns the results of the experiments executed as part of this performance test.
+     */
+    List<MeasuredOperationList> getExperiments();
+
+    String getOperatingSystem();
+
+    String getJvm();
+}
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ReportGenerator.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ReportGenerator.java
index 380bb90..ec81032 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ReportGenerator.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ReportGenerator.java
@@ -31,10 +31,11 @@ public class ReportGenerator {
 
             fileRenderer.render(store, new IndexPageGenerator(), new File(outputDirectory, "index.html"));
 
+            File testsDir = new File(outputDirectory, "tests");
             for (String testName : store.getTestNames()) {
                 TestExecutionHistory testResults = store.getTestResults(testName);
-                fileRenderer.render(testResults, testHtmlRenderer, new File(outputDirectory, testResults.getId() + ".html"));
-                fileRenderer.render(testResults, testDataRenderer, new File(outputDirectory, testResults.getId() + ".json"));
+                fileRenderer.render(testResults, testHtmlRenderer, new File(testsDir, testResults.getId() + ".html"));
+                fileRenderer.render(testResults, testDataRenderer, new File(testsDir, testResults.getId() + ".json"));
             }
 
             copyResource("jquery.min-1.11.0.js", outputDirectory);
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ResultsStore.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ResultsStore.java
index 1d3c9d5..c742801 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ResultsStore.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/ResultsStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 the original author or authors.
+ * Copyright 2014 the original author 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,282 +16,21 @@
 
 package org.gradle.performance.results;
 
-import org.gradle.internal.UncheckedException;
-import org.gradle.performance.fixture.BaselineVersion;
-import org.gradle.performance.fixture.DataReporter;
-import org.gradle.performance.fixture.MeasuredOperationList;
-import org.gradle.performance.fixture.PerformanceResults;
-import org.gradle.performance.measure.DataAmount;
-import org.gradle.performance.measure.Duration;
-import org.gradle.performance.measure.MeasuredOperation;
-import org.gradle.util.GradleVersion;
+import java.util.List;
 
-import java.io.File;
-import java.sql.*;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.*;
+public interface ResultsStore {
+    /**
+     * Returns the names of the test cases known to this store, in display order.
+     */
+    List<String> getTestNames();
 
-/**
- * A {@link DataReporter} implementation that stores results in an H2 relational database.
- */
-public class ResultsStore implements DataReporter {
-    private final File dbFile;
-    private Connection connection;
-    private final long ignoreV17Before;
-
-    public ResultsStore() {
-        this(new File(System.getProperty("user.home"), ".gradle-performance-test-data/results"));
-    }
-
-    public ResultsStore(File dbFile) {
-        this.dbFile = dbFile;
-
-        // Ignore some broken samples before the given date
-        DateFormat timeStampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        timeStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-        try {
-            ignoreV17Before = timeStampFormat.parse("2013-07-03 00:00:00").getTime();
-        } catch (ParseException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public void report(final PerformanceResults results) {
-        try {
-            withConnection(new ConnectionAction<Void>() {
-                public Void execute(Connection connection) throws Exception {
-                    long testId;
-                    PreparedStatement statement = connection.prepareStatement("insert into testExecution(testId, executionTime, targetVersion, testProject, tasks, args, operatingSystem, jvm, vcsBranch, vcsCommit) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
-                    try {
-                        statement.setString(1, results.getTestId());
-                        statement.setTimestamp(2, new Timestamp(results.getTestTime()));
-                        statement.setString(3, results.getVersionUnderTest());
-                        statement.setString(4, results.getTestProject());
-                        statement.setObject(5, results.getTasks());
-                        statement.setObject(6, results.getArgs());
-                        statement.setString(7, results.getOperatingSystem());
-                        statement.setString(8, results.getJvm());
-                        statement.setString(9, results.getVcsBranch());
-                        statement.setString(10, results.getVcsCommit());
-                        statement.execute();
-                        ResultSet keys = statement.getGeneratedKeys();
-                        keys.next();
-                        testId = keys.getLong(1);
-                    } finally {
-                        statement.close();
-                    }
-                    statement = connection.prepareStatement("insert into testOperation(testExecution, version, executionTimeMs, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes) values (?, ?, ?, ?, ?, ?, ?, ?)");
-                    try {
-                        addOperations(statement, testId, null, results.getCurrent());
-                        for (BaselineVersion baselineVersion : results.getBaselineVersions()) {
-                            addOperations(statement, testId, baselineVersion.getVersion(), baselineVersion.getResults());
-                        }
-                    } finally {
-                        statement.close();
-                    }
-                    return null;
-                }
-            });
-        } catch (Exception e) {
-            throw new RuntimeException(String.format("Could not open results datastore '%s'.", dbFile), e);
-        }
-    }
-
-    private void addOperations(PreparedStatement statement, long testId, String version, MeasuredOperationList operations) throws SQLException {
-        for (MeasuredOperation operation : operations) {
-            statement.setLong(1, testId);
-            statement.setString(2, version);
-            statement.setBigDecimal(3, operation.getExecutionTime().toUnits(Duration.MILLI_SECONDS).getValue());
-            statement.setBigDecimal(4, operation.getTotalMemoryUsed().toUnits(DataAmount.BYTES).getValue());
-            statement.setBigDecimal(5, operation.getTotalHeapUsage().toUnits(DataAmount.BYTES).getValue());
-            statement.setBigDecimal(6, operation.getMaxHeapUsage().toUnits(DataAmount.BYTES).getValue());
-            statement.setBigDecimal(7, operation.getMaxUncollectedHeap().toUnits(DataAmount.BYTES).getValue());
-            statement.setBigDecimal(8, operation.getMaxCommittedHeap().toUnits(DataAmount.BYTES).getValue());
-            statement.execute();
-        }
-    }
-
-    public List<String> getTestNames() {
-        try {
-            return withConnection(new ConnectionAction<List<String>>() {
-                public List<String> execute(Connection connection) throws Exception {
-                    List<String> testNames = new ArrayList<String>();
-                    ResultSet testExecutions = connection.createStatement().executeQuery("select distinct testId from testExecution order by testId");
-                    while (testExecutions.next()) {
-                        testNames.add(testExecutions.getString(1));
-                    }
-                    return testNames;
-                }
-            });
-        } catch (Exception e) {
-            throw new RuntimeException(String.format("Could not load test history from datastore '%s'.", dbFile), e);
-        }
-    }
-
-    public List<String> getVersions() {
-        try {
-            return withConnection(new ConnectionAction<List<String>>() {
-                public List<String> execute(Connection connection) throws Exception {
-                    Set<String> allVersions = new TreeSet<String>(new Comparator<String>() {
-                        public int compare(String o1, String o2) {
-                            return GradleVersion.version(o1).compareTo(GradleVersion.version(o2));
-                        }
-                    });
-                    PreparedStatement uniqueVersions = connection.prepareStatement("select distinct version from testOperation");
-                    ResultSet versions = uniqueVersions.executeQuery();
-                    while (versions.next()) {
-                        String version = versions.getString(1);
-                        if (version != null) {
-                            allVersions.add(version);
-                        }
-                    }
-                    versions.close();
-                    uniqueVersions.close();
-
-                    ArrayList<String> result = new ArrayList<String>();
-                    result.addAll(allVersions);
-
-                    PreparedStatement uniqueBranches = connection.prepareStatement("select distinct vcsBranch from testExecution");
-                    ResultSet branches = uniqueBranches.executeQuery();
-                    Set<String> allBranches = new TreeSet<String>();
-                    while (branches.next()) {
-                        allBranches.add(branches.getString(1).trim());
-                    }
-                    branches.close();
-                    uniqueBranches.close();
-
-                    result.addAll(allBranches);
-
-                    return result;
-                }
-            });
-        } catch (Exception e) {
-            throw new RuntimeException(String.format("Could not load version list from datastore '%s'.", dbFile), e);
-        }
-    }
-
-    public TestExecutionHistory getTestResults(final String testName) {
-        try {
-            return withConnection(new ConnectionAction<TestExecutionHistory>() {
-                public TestExecutionHistory execute(Connection connection) throws Exception {
-                    List<PerformanceResults> results = new ArrayList<PerformanceResults>();
-                    Set<String> allVersions = new TreeSet<String>(new Comparator<String>() {
-                        public int compare(String o1, String o2) {
-                            return GradleVersion.version(o1).compareTo(GradleVersion.version(o2));
-                        }
-                    });
-                    Set<String> allBranches = new TreeSet<String>();
-                    PreparedStatement executionsForName = connection.prepareStatement("select id, executionTime, targetVersion, testProject, tasks, args, operatingSystem, jvm, vcsBranch, vcsCommit from testExecution where testId = ? order by executionTime desc");
-                    PreparedStatement operationsForExecution = connection.prepareStatement("select version, executionTimeMs, heapUsageBytes, totalHeapUsageBytes, maxHeapUsageBytes, maxUncollectedHeapBytes, maxCommittedHeapBytes from testOperation where testExecution = ?");
-                    executionsForName.setString(1, testName);
-                    ResultSet testExecutions = executionsForName.executeQuery();
-                    while (testExecutions.next()) {
-                        long id = testExecutions.getLong(1);
-                        PerformanceResults performanceResults = new PerformanceResults();
-                        performanceResults.setTestId(testName);
-                        performanceResults.setTestTime(testExecutions.getTimestamp(2).getTime());
-                        performanceResults.setVersionUnderTest(testExecutions.getString(3));
-                        performanceResults.setTestProject(testExecutions.getString(4));
-                        performanceResults.setTasks(toArray(testExecutions.getObject(5)));
-                        performanceResults.setArgs(toArray(testExecutions.getObject(6)));
-                        performanceResults.setOperatingSystem(testExecutions.getString(7));
-                        performanceResults.setJvm(testExecutions.getString(8));
-                        performanceResults.setVcsBranch(testExecutions.getString(9).trim());
-                        performanceResults.setVcsCommit(testExecutions.getString(10));
-
-                        results.add(performanceResults);
-                        allBranches.add(performanceResults.getVcsBranch());
-
-                        operationsForExecution.setLong(1, id);
-                        ResultSet builds = operationsForExecution.executeQuery();
-                        while (builds.next()) {
-                            String version = builds.getString(1);
-                            if ("1.7".equals(version) && performanceResults.getTestTime() <= ignoreV17Before) {
-                                // Ignore some broken samples
-                                continue;
-                            }
-                            MeasuredOperation operation = new MeasuredOperation();
-                            operation.setExecutionTime(Duration.millis(builds.getBigDecimal(2)));
-                            operation.setTotalMemoryUsed(DataAmount.bytes(builds.getBigDecimal(3)));
-                            operation.setTotalHeapUsage(DataAmount.bytes(builds.getBigDecimal(4)));
-                            operation.setMaxHeapUsage(DataAmount.bytes(builds.getBigDecimal(5)));
-                            operation.setMaxUncollectedHeap(DataAmount.bytes(builds.getBigDecimal(6)));
-                            operation.setMaxCommittedHeap(DataAmount.bytes(builds.getBigDecimal(7)));
-
-                            if (version == null) {
-                                performanceResults.getCurrent().add(operation);
-                            } else {
-                                BaselineVersion baselineVersion = performanceResults.baseline(version);
-                                baselineVersion.getResults().add(operation);
-                                allVersions.add(version);
-                            }
-                        }
-                    }
-                    testExecutions.close();
-                    operationsForExecution.close();
-                    executionsForName.close();
-
-                    return new TestExecutionHistory(testName, new ArrayList<String>(allVersions), new ArrayList<String>(allBranches), results);
-                }
-            });
-        } catch (Exception e) {
-            throw new RuntimeException(String.format("Could not load results from datastore '%s'.", dbFile), e);
-        }
-    }
-
-    private String[] toArray(Object object) {
-        Object[] value = (Object[]) object;
-        String[] result = new String[value.length];
-        for (int i = 0; i < value.length; i++) {
-            result[i] = value[i].toString();
-        }
-        return result;
-    }
-
-    public void close() {
-        if (connection != null) {
-            try {
-                connection.close();
-            } catch (SQLException e) {
-                throw new RuntimeException(String.format("Could not close datastore '%s'.", dbFile), e);
-            } finally {
-                connection = null;
-            }
-        }
-    }
-
-    private <T> T withConnection(ConnectionAction<T> action) throws Exception {
-        if (connection == null) {
-            dbFile.getParentFile().mkdirs();
-            Class.forName("org.h2.Driver");
-            connection = DriverManager.getConnection(String.format("jdbc:h2:%s", dbFile.getAbsolutePath()), "sa", "");
-            try {
-                initSchema(connection);
-            } catch (Exception e) {
-                connection.close();
-                connection = null;
-                throw e;
-            }
-        }
-        return action.execute(connection);
-    }
-
-    private void initSchema(Connection connection) throws Exception {
-        Statement statement = connection.createStatement();
-        statement.execute("create table if not exists testExecution (id bigint identity not null, testId varchar not null, executionTime timestamp not null, targetVersion varchar not null, testProject varchar not null, tasks array not null, args array not null, operatingSystem varchar not null, jvm varchar not null)");
-        statement.execute("create table if not exists testOperation (testExecution bigint not null, version varchar, executionTimeMs decimal not null, heapUsageBytes decimal not null, foreign key(testExecution) references testExecution(id))");
-        statement.execute("alter table testExecution add column if not exists vcsBranch varchar not null default 'master'");
-        statement.execute("alter table testExecution add column if not exists vcsCommit varchar");
-        statement.execute("alter table testOperation add column if not exists totalHeapUsageBytes decimal");
-        statement.execute("alter table testOperation add column if not exists maxHeapUsageBytes decimal");
-        statement.execute("alter table testOperation add column if not exists maxUncollectedHeapBytes decimal");
-        statement.execute("alter table testOperation add column if not exists maxCommittedHeapBytes decimal");
-        statement.close();
-    }
+    /**
+     * Returns the full history of the given test.
+     */
+    TestExecutionHistory getTestResults(String testName);
 
-    private interface ConnectionAction<T> {
-        T execute(Connection connection) throws Exception;
-    }
+    /**
+     * Returns the n most recent instances of the given test.
+     */
+    TestExecutionHistory getTestResults(String testName, int mostRecentN);
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestDataGenerator.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestDataGenerator.java
index b77a26e..c9d938d 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestDataGenerator.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestDataGenerator.java
@@ -16,9 +16,9 @@
 
 package org.gradle.performance.results;
 
+import com.google.common.collect.Lists;
 import org.gradle.api.Transformer;
 import org.gradle.performance.fixture.MeasuredOperationList;
-import org.gradle.performance.fixture.PerformanceResults;
 import org.gradle.reporting.ReportRenderer;
 
 import java.io.IOException;
@@ -33,7 +33,7 @@ public class TestDataGenerator extends ReportRenderer<TestExecutionHistory, Writ
     @Override
     public void render(TestExecutionHistory testHistory, Writer output) throws IOException {
         PrintWriter out = new PrintWriter(output);
-        List<PerformanceResults> sortedResults = testHistory.getResultsOldestFirst();
+        List<PerformanceResults> sortedResults = Lists.reverse(testHistory.getPerformanceResults());
         out.println("{");
         out.println("\"labels\": [");
         for (int i = 0; i < sortedResults.size(); i++) {
@@ -44,10 +44,10 @@ public class TestDataGenerator extends ReportRenderer<TestExecutionHistory, Writ
             out.print("\"" + format.date(new Date(results.getTestTime())) + "\"");
         }
         out.println("],");
-        out.print("\"executionTime\":");
+        out.print("\"totalTime\":");
         render(testHistory, new Transformer<String, MeasuredOperationList>() {
             public String transform(MeasuredOperationList original) {
-                return format.seconds(original.getExecutionTime().getAverage());
+                return format.seconds(original.getTotalTime().getAverage());
             }
         }, out);
         out.println(",");
@@ -62,20 +62,20 @@ public class TestDataGenerator extends ReportRenderer<TestExecutionHistory, Writ
     }
 
     void render(TestExecutionHistory testHistory, Transformer<String, MeasuredOperationList> valueRenderer, PrintWriter out) {
-        List<PerformanceResults> sortedResults = testHistory.getResultsOldestFirst();
+        List<PerformanceResults> sortedResults = Lists.reverse(testHistory.getPerformanceResults());
         out.println("  [");
-        for (int i = 0; i < testHistory.getKnownVersions().size(); i++) {
-            String version = testHistory.getKnownVersions().get(i);
+        List<String> labels = testHistory.getExperimentLabels();
+        for (int i = 0; i < labels.size(); i++) {
             if (i > 0) {
                 out.println(",");
             }
             out.println("  {");
-            out.println("    \"label\": \"" + version + "\",");
+            out.println("    \"label\": \"" + labels.get(i) + "\",");
             out.print("\"data\": [");
             boolean empty = true;
             for (int j = 0; j < sortedResults.size(); j++) {
                 PerformanceResults results = sortedResults.get(j);
-                MeasuredOperationList measuredOperations = results.version(version).getResults();
+                MeasuredOperationList measuredOperations = results.getExperiments().get(i);
                 if (!measuredOperations.isEmpty()) {
                     if (!empty) {
                         out.print(", ");
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestExecutionHistory.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestExecutionHistory.java
index 8be02da..f100ff7 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestExecutionHistory.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestExecutionHistory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 the original author or authors.
+ * Copyright 2014 the original author 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,68 +16,22 @@
 
 package org.gradle.performance.results;
 
-import org.gradle.performance.fixture.PerformanceResults;
-
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
-public class TestExecutionHistory {
-    private final String name;
-    private final List<String> versions;
-    private final List<String> branches;
-    private final List<PerformanceResults> newestFirst;
-    private List<PerformanceResults> oldestFirst;
-    private List<String> knownVersions;
-
-    public TestExecutionHistory(String name, List<String> versions, List<String> branches, List<PerformanceResults> newestFirst) {
-        this.name = name;
-        this.versions = versions;
-        this.branches = branches;
-        this.newestFirst = newestFirst;
-    }
-
-    public String getId() {
-        return name.replaceAll("\\s+", "-");
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public List<String> getBaselineVersions() {
-        return versions;
-    }
+public interface TestExecutionHistory {
+    String getId();
 
-    public List<String> getBranches() {
-        return branches;
-    }
+    String getName();
 
-    public List<String> getKnownVersions() {
-        if (knownVersions == null) {
-            ArrayList<String> result = new ArrayList<String>();
-            result.addAll(versions);
-            result.addAll(branches);
-            knownVersions = result;
-        }
-        return knownVersions;
-    }
+    List<PerformanceResults> getPerformanceResults();
 
     /**
-     * Returns results from most recent to least recent.
+     * Returns the number of experiments per performance test.
      */
-    public List<PerformanceResults> getResults() {
-        return newestFirst;
-    }
+    int getExperimentCount();
 
     /**
-     * Returns results from least recent to most recent.
+     * Returns the labels of the experiments.
      */
-    public List<PerformanceResults> getResultsOldestFirst() {
-        if (oldestFirst == null) {
-            oldestFirst = new ArrayList<PerformanceResults>(newestFirst);
-            Collections.reverse(oldestFirst);
-        }
-        return oldestFirst;
-    }
+    List<String> getExperimentLabels();
 }
diff --git a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestPageGenerator.java b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestPageGenerator.java
index 5a7caf9..d5ef8c8 100644
--- a/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestPageGenerator.java
+++ b/subprojects/performance/src/testFixtures/groovy/org/gradle/performance/results/TestPageGenerator.java
@@ -16,27 +16,25 @@
 
 package org.gradle.performance.results;
 
-import com.google.common.base.Joiner;
-import com.googlecode.jatl.Html;
 import org.gradle.api.Transformer;
 import org.gradle.performance.fixture.MeasuredOperationList;
-import org.gradle.performance.fixture.PerformanceResults;
-import org.gradle.performance.fixture.VersionResults;
-import org.gradle.performance.measure.Amount;
 import org.gradle.performance.measure.DataAmount;
 import org.gradle.performance.measure.DataSeries;
 import org.gradle.performance.measure.Duration;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 
 public class TestPageGenerator extends HtmlPageGenerator<TestExecutionHistory> {
     @Override
+    protected int getDepth() {
+        return 1;
+    }
+
+    @Override
     public void render(final TestExecutionHistory testHistory, Writer writer) throws IOException {
-        new Html(writer) {{
+        new MetricsHtml(writer) {{
             html();
                 head();
                     headSection(this);
@@ -47,7 +45,7 @@ public class TestPageGenerator extends HtmlPageGenerator<TestExecutionHistory> {
                         text("  success: function(data) {\n");
                         text("    var labels = data.labels;\n");
                         text("    var options = { series: { points: { show: true }, lines: { show: true } }, legend: { noColumns: 0, margin: 1 }, grid: { hoverable: true, clickable: true }, xaxis: { tickFormatter: function(index, value) { return labels[index]; } } };\n");
-                        text("    $.plot('#executionTimeChart', data.executionTime, options);\n");
+                        text("    $.plot('#executionTimeChart', data.totalTime, options);\n");
                         text("    $.plot('#heapUsageChart', data.heapUsage, options);\n");
                         text("    $('#executionTimeChart').bind('plothover', function (event, pos, item) {\n");
                         text("      if (!item) {\n");
@@ -87,12 +85,14 @@ public class TestPageGenerator extends HtmlPageGenerator<TestExecutionHistory> {
                     table().classAttr("history");
                         tr().classAttr("control-groups");
                             th().colspan("4").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average execution time").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average heap usage (old measurement)").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average total heap usage").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average max heap usage").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average max uncollected heap").end();
-                            th().colspan(String.valueOf(testHistory.getKnownVersions().size())).text("Average max committed heap").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average build time").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average configuration time").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average execution time").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average heap usage (old measurement)").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average total heap usage").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average max heap usage").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average max uncollected heap").end();
+                            th().colspan(String.valueOf(testHistory.getExperimentCount())).text("Average max committed heap").end();
                             th().colspan("4").text("Details").end();
                         end();
                         tr();
@@ -100,122 +100,69 @@ public class TestPageGenerator extends HtmlPageGenerator<TestExecutionHistory> {
                             th().text("Test version").end();
                             th().text("Branch").end();
                             th().text("Git commit").end();
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
-                            }
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
-                            }
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
+                            for (int i = 0; i < 8; i++) {
+                                for (String label : testHistory.getExperimentLabels()) {
+                                    th().classAttr("numeric").text(label).end();
+                                }
                             }
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
-                            }
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
-                            }
-                            for (String version : testHistory.getKnownVersions()) {
-                                th().classAttr("numeric").text(version).end();
-                            }
-                            th().text("Test project").end();
-                            th().text("Tasks").end();
                             th().text("Operating System").end();
                             th().text("JVM").end();
                         end();
-                        for (PerformanceResults performanceResults : testHistory.getResults()) {
+                        for (PerformanceResults results : testHistory.getPerformanceResults()) {
                             tr();
-                                td().text(format.timestamp(new Date(performanceResults.getTestTime()))).end();
-                                td().text(performanceResults.getVersionUnderTest()).end();
-                                td().text(performanceResults.getVcsBranch()).end();
-                                td().text(performanceResults.getVcsCommit()).end();
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<Duration>, MeasuredOperationList>() {
+                                td().text(format.timestamp(new Date(results.getTestTime()))).end();
+                                td().text(results.getVersionUnderTest()).end();
+                                td().text(results.getVcsBranch()).end();
+                                td().text(results.getVcsCommit()).end();
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<Duration>, MeasuredOperationList>() {
+                                    public DataSeries<Duration> transform(MeasuredOperationList original) {
+                                        return original.getTotalTime();
+                                    }
+                                });
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<Duration>, MeasuredOperationList>() {
+                                    public DataSeries<Duration> transform(MeasuredOperationList original) {
+                                        return original.getConfigurationTime();
+                                    }
+                                });
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<Duration>, MeasuredOperationList>() {
                                     public DataSeries<Duration> transform(MeasuredOperationList original) {
                                         return original.getExecutionTime();
                                     }
                                 });
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
                                     public DataSeries<DataAmount> transform(MeasuredOperationList original) {
                                         return original.getTotalMemoryUsed();
                                     }
                                 });
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
                                     public DataSeries<DataAmount> transform(MeasuredOperationList original) {
                                         return original.getTotalHeapUsage();
                                     }
                                 });
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
                                     public DataSeries<DataAmount> transform(MeasuredOperationList original) {
                                         return original.getMaxHeapUsage();
                                     }
                                 });
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
                                     public DataSeries<DataAmount> transform(MeasuredOperationList original) {
                                         return original.getMaxUncollectedHeap();
                                     }
                                 });
-                                renderMetricForVersions(testHistory, performanceResults, new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
+                                renderSamplesForExperiment(results.getExperiments(), new Transformer<DataSeries<DataAmount>, MeasuredOperationList>() {
                                     public DataSeries<DataAmount> transform(MeasuredOperationList original) {
                                         return original.getMaxCommittedHeap();
                                     }
                                 });
-                                td().text(performanceResults.getTestProject()).end();
-                                td();
-                                    text(Joiner.on(", ").join(performanceResults.getArgs()));
-                                    text(" ");
-                                    text(Joiner.on(", ").join(performanceResults.getTasks()));
-                                end();
-                                td().text(performanceResults.getOperatingSystem()).end();
-                                td().text(performanceResults.getJvm()).end();
+                                td().text(results.getOperatingSystem()).end();
+                                td().text(results.getJvm()).end();
                             end();
                         }
                     end();
                 end();
                 footer(this);
             endAll();
-        }
-
-            private <T> void renderMetricForVersions(TestExecutionHistory testHistory, PerformanceResults testExecution, Transformer<DataSeries<T>, MeasuredOperationList> transformer) {
-                List<Amount<T>> values = new ArrayList<Amount<T>>();
-                Amount<T> min = null;
-                Amount<T> max = null;
-                for (String version : testHistory.getKnownVersions()) {
-                    VersionResults versionResults = testExecution.version(version);
-                    DataSeries<T> data = transformer.transform(versionResults.getResults());
-                    if (data.isEmpty()) {
-                        values.add(null);
-                    } else {
-                        Amount<T> value = data.getAverage();
-                        values.add(value);
-                        if (min == null || value.compareTo(min) < 0) {
-                            min = value;
-                        }
-                        if (max == null || value.compareTo(max) > 0) {
-                            max = value;
-                        }
-                    }
-                }
-                if (min != null && min.equals(max)) {
-                    min = null;
-                    max = null;
-                }
-
-                for (Amount<?> value : values) {
-                    if (value == null) {
-                        td().text("").end();
-                    } else {
-                        String classAttr = "numeric";
-                        if (value.equals(min)) {
-                            classAttr += " min-value";
-                        }
-                        if (value.equals(max)) {
-                            classAttr += " max-value";
-                        }
-                        td().classAttr(classAttr).text(value.format()).end();
-                    }
-                }
-            }
-        };
+        }};
     }
 
 }
diff --git a/subprojects/platform-base/platform-base.gradle b/subprojects/platform-base/platform-base.gradle
new file mode 100644
index 0000000..0aa76f2
--- /dev/null
+++ b/subprojects/platform-base/platform-base.gradle
@@ -0,0 +1,10 @@
+dependencies {
+    compile libraries.groovy
+    compile project(":core")
+}
+
+useClassycle()
+strictCompile()
+useTestFixtures()
+useTestFixtures(sourceSet: 'testFixtures')
+
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AssembleTaskIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AssembleTaskIntegrationTest.groovy
new file mode 100644
index 0000000..af2487f
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AssembleTaskIntegrationTest.groovy
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.hamcrest.Matchers
+
+class AssembleTaskIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        settingsFile << """rootProject.name = 'assemble-binary'"""
+        buildFile << """
+            plugins {
+                id 'language-base'
+            }
+        """
+    }
+
+    def "is up to date when there are no not buildable binaries"() {
+        withBinaries("sampleBinary")
+
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped ":assemble"
+    }
+
+    def "is up to date when there are both buildable and not buildable binaries"() {
+        withBinaries("sampleBinary", "notBuildableBinary")
+
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped ":assemble"
+    }
+
+    def "produces sensible error when no binaries are buildable" () {
+        withBinaries("notBuildableBinary1", "notBuildableBinary2")
+
+        when:
+        fails "assemble"
+
+        then:
+        failureDescriptionContains("Execution failed for task ':assemble'.")
+        failure.assertThatCause(Matchers.<String>allOf(
+            Matchers.startsWith("No buildable binaries found:"),
+            Matchers.containsString("notBuildableBinary1: Binary notBuildableBinary1 has 'notBuildable' in the name"),
+            Matchers.containsString("notBuildableBinary2: Binary notBuildableBinary2 has 'notBuildable' in the name")
+        ))
+    }
+
+    def "does not produce error when binaries are buildable" () {
+        withBinaries("buildableBinary1", "notBuildableBinary", "buildableBinary2")
+
+        expect:
+        succeeds "assemble"
+        skipped ":buildableBinary1", ":buildableBinary2"
+        notExecuted ":notBuildableBinary"
+    }
+
+    def "does not produce error when assemble task has other dependencies" () {
+        withBinaries("notBuildableBinary")
+        buildFile << """
+            task someOtherTask
+            assemble.dependsOn someOtherTask
+        """
+
+        expect:
+        succeeds "assemble"
+    }
+
+    def "does not produce error when no binaries are configured" () {
+        expect:
+        succeeds "assemble"
+    }
+
+    def withSampleBinary() {
+        buildFile << """
+            import org.gradle.model.*
+            import org.gradle.model.collection.*
+            import org.gradle.platform.base.internal.BinaryBuildAbility
+
+            interface SampleBinary extends BinarySpec {
+            }
+        """
+    }
+
+    def withSampleBinaryPlugin(String... binaries) {
+        buildFile << """
+            class MySamplePlugin implements Plugin<Project> {
+                void apply(final Project project) {}
+
+                static class Rules extends RuleSource {
+                    @BinaryType
+                    void register(BinaryTypeBuilder<SampleBinary> builder) {
+                        builder.defaultImplementation(DefaultSampleBinary)
+                    }
+
+                    @Mutate
+                    void createSampleBinary(CollectionBuilder<SampleBinary> binarySpecs) {
+                        ${generateBinaries(binaries)}
+                    }
+                }
+            }
+
+            apply plugin:MySamplePlugin
+        """
+    }
+
+    def generateBinaries(binaries) {
+        return binaries.collect { "binarySpecs.create(\"${it}\")" }.join("\n")
+    }
+
+    def withBinaries(String... binaries) {
+        withSampleBinary()
+        buildFile << """
+            class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {
+                @Override
+                protected BinaryBuildAbility getBinaryBuildAbility() {
+                    return new BinaryBuildAbility() {
+                        @Override
+                        public boolean isBuildable() {
+                            return ! getName().contains("notBuildable");
+                        }
+
+                        @Override
+                        public void explain(TreeVisitor<? super String> visitor) {
+                            visitor.node("Binary \${getName()} has 'notBuildable' in the name")
+                        }
+                    };
+                }
+            }
+        """
+        withSampleBinaryPlugin(binaries)
+    }
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AutoTestedSamplePlatformBaseIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AutoTestedSamplePlatformBaseIntegrationTest.groovy
new file mode 100644
index 0000000..c4cf57a
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/AutoTestedSamplePlatformBaseIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+class AutoTestedSamplePlatformBaseIntegrationTest extends AbstractAutoTestedSamplesTest {
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/platform-base/src/main")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/ComponentTypeSampleIntegTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/ComponentTypeSampleIntegTest.groovy
new file mode 100644
index 0000000..4d2e9c8
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/ComponentTypeSampleIntegTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Rule
+
+class ComponentTypeSampleIntegTest extends AbstractIntegrationSpec {
+    @Rule Sample componentTypeSample = new Sample(temporaryFolder, "customModel/componentType")
+
+    def "can create custom component with binaries"() {
+        given:
+        sample componentTypeSample
+        componentTypeSample.dir.file("build.gradle") << """
+
+task checkModel << {
+    assert project.componentSpecs.size() == 2
+    def titleAImage = project.componentSpecs.imageA
+    assert titleAImage instanceof ImageComponent
+    assert titleAImage.projectPath == project.path
+    assert titleAImage.displayName == "DefaultImageComponent 'imageA'"
+    assert titleAImage.title == 'TitleA'
+    assert titleAImage.binaries.collect{it.name}.sort() == ['TitleA14pxBinary', 'TitleA28pxBinary', 'TitleA40pxBinary']
+}
+
+"""
+        expect:
+        succeeds "checkModel"
+    }
+
+    def "can create all binaries"() {
+        given:
+        sample componentTypeSample
+        when:
+        succeeds "assemble"
+        then:
+        executedAndNotSkipped ":renderTitleA14pxSvg", ":TitleA14pxBinary", ":renderTitleA28pxSvg", ":TitleA28pxBinary", ":renderTitleA40pxSvg",
+                              ":TitleA40pxBinary", ":renderTitleB14pxSvg", ":TitleB14pxBinary", ":renderTitleB28pxSvg", ":TitleB28pxBinary",
+                              ":renderTitleB40pxSvg", ":TitleB40pxBinary", ":assemble"
+
+        and:
+        componentTypeSample.dir.file("build/renderedSvg").assertHasDescendants("TitleA_14px.svg", "TitleA_28px.svg", "TitleA_40px.svg", "TitleB_14px.svg",
+                                                                         "TitleB_28px.svg", "TitleB_40px.svg")
+    }
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryIntegrationTest.groovy
new file mode 100644
index 0000000..f62835c
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryIntegrationTest.groovy
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+
+class CustomBinaryIntegrationTest extends AbstractIntegrationSpec {
+    def "setup"() {
+        buildFile << """
+import org.gradle.model.*
+import org.gradle.model.collection.*
+
+interface SampleBinary extends BinarySpec {
+    String getVersion()
+    void setVersion(String version)
+}
+class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {
+    String version
+}
+"""
+    }
+
+    def "custom binary type can be registered and created"() {
+        when:
+        buildWithCustomBinaryPlugin()
+        and:
+        buildFile << """
+task checkModel << {
+    assert project.binaries.size() == 1
+    def sampleBinary = project.binaries.sampleBinary
+    assert sampleBinary instanceof SampleBinary
+    assert sampleBinary.displayName == "DefaultSampleBinary 'sampleBinary'"
+}
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can configure binary defined by rule method using rule DSL"() {
+        when:
+        buildWithCustomBinaryPlugin()
+
+        and:
+        buildFile << """
+task checkModel << {
+    assert project.binaries.size() == 1
+    def sampleBinary = project.binaries.sampleBinary
+    assert sampleBinary instanceof SampleBinary
+    assert sampleBinary.version == '1.2'
+    assert sampleBinary.displayName == "DefaultSampleBinary 'sampleBinary'"
+}
+
+model {
+    binaries {
+        sampleBinary {
+            version = '1.2'
+        }
+    }
+}
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "creates lifecycle task per binary"() {
+        when:
+        buildWithCustomBinaryPlugin()
+        then:
+        succeeds "sampleBinary"
+    }
+
+    def "can register custom binary model without creating"() {
+        when:
+        buildFile << """
+        class MySamplePlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+            }
+        }
+
+        apply plugin:MySamplePlugin
+
+        task checkModel << {
+            assert project.binaries.size() == 0
+        }
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can have binary declaration and creation in separate plugins"() {
+        when:
+        buildFile << """
+        class MyBinaryDeclarationModel implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+            }
+        }
+
+        class MyBinaryCreationPlugin implements Plugin<Project> {
+            void apply(final Project project) {
+                project.apply(plugin:MyBinaryDeclarationModel)
+            }
+
+            static class Rules extends RuleSource {
+                @Mutate
+                void createSampleBinaries(CollectionBuilder<SampleBinary> binaries) {
+                    binaries.create("sampleBinary")
+                }
+
+            }
+        }
+
+        apply plugin:MyBinaryCreationPlugin
+
+        task checkModel << {
+            assert project.binaries.size() == 1
+            def sampleBinary = project.binaries.sampleBinary
+            assert sampleBinary instanceof SampleBinary
+            assert sampleBinary.displayName == "DefaultSampleBinary 'sampleBinary'"
+        }
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can define and create multiple binary types in the same plugin"() {
+        when:
+        buildFile << """
+        interface AnotherSampleBinary extends BinarySpec {}
+        class DefaultAnotherSampleBinary extends BaseBinarySpec implements AnotherSampleBinary {}
+
+        class MySamplePlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+
+                @Mutate
+                void createSampleBinaryInstances(CollectionBuilder<SampleBinary> binaries) {
+                    binaries.create("sampleBinary")
+                }
+
+                @BinaryType
+                void registerAnother(BinaryTypeBuilder<AnotherSampleBinary> builder) {
+                    builder.defaultImplementation(DefaultAnotherSampleBinary)
+                }
+
+                @Mutate
+                void createAnotherSampleBinaryInstances(CollectionBuilder<AnotherSampleBinary> anotherBinaries) {
+                    anotherBinaries.create("anotherSampleBinary")
+                }
+            }
+        }
+
+        apply plugin:MySamplePlugin
+
+        task checkModel << {
+            assert project.binaries.size() == 2
+            def sampleBinary = project.binaries.sampleBinary
+            assert sampleBinary instanceof SampleBinary
+            assert sampleBinary.displayName == "DefaultSampleBinary 'sampleBinary'"
+
+            def anotherSampleBinary = project.binaries.anotherSampleBinary
+            assert anotherSampleBinary instanceof AnotherSampleBinary
+            assert anotherSampleBinary.displayName == "DefaultAnotherSampleBinary 'anotherSampleBinary'"
+        }
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "reports failure for invalid binary type method"() {
+        given:
+        settingsFile << """rootProject.name = 'custom-binary'"""
+        buildFile << """
+        class MySamplePlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder, String illegalOtherParameter) {
+                }
+            }
+        }
+
+        apply plugin:MySamplePlugin
+"""
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription "A problem occurred evaluating root project 'custom-binary'."
+        failure.assertHasCause "Failed to apply plugin [class 'MySamplePlugin']"
+        failure.assertHasCause "MySamplePlugin\$Rules#register(org.gradle.platform.base.BinaryTypeBuilder<SampleBinary>, java.lang.String) is not a valid binary model rule method."
+        failure.assertHasCause "Method annotated with @BinaryType must have a single parameter of type 'org.gradle.platform.base.BinaryTypeBuilder'."
+    }
+
+    def "cannot register same binary type multiple times"() {
+        given:
+        buildWithCustomBinaryPlugin()
+        and:
+        buildFile << """
+        class MyOtherPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules1 extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+            }
+        }
+
+        apply plugin:MyOtherPlugin
+"""
+        when:
+        fails "tasks"
+        then:
+        failure.assertHasDescription "A problem occurred configuring root project 'custom-binary'."
+        failure.assertHasCause "Exception thrown while executing model rule: MyOtherPlugin\$Rules1#register(org.gradle.platform.base.BinaryTypeBuilder<SampleBinary>)"
+        failure.assertHasCause "Cannot register a factory for type SampleBinary because a factory for this type was already registered by MySamplePlugin\$Rules#register(org.gradle.platform.base.BinaryTypeBuilder<SampleBinary>)."
+    }
+
+    def "additional binaries listed in components report"() {
+        given:
+        buildWithCustomBinaryPlugin()
+        when:
+        succeeds "components"
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators(""":components
+
+------------------------------------------------------------
+Root project
+------------------------------------------------------------
+
+No components defined for this project.
+
+Additional binaries
+-------------------
+DefaultSampleBinary 'sampleBinary'
+    build using task: :sampleBinary
+
+Note: currently not all plugins register their components, so some components may not be visible here.
+
+BUILD SUCCESSFUL"""))
+    }
+
+    def buildWithCustomBinaryPlugin() {
+        settingsFile << """rootProject.name = 'custom-binary'"""
+        buildFile << """
+        class MySamplePlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+
+                @Mutate
+                void createSampleBinary(CollectionBuilder<SampleBinary> binarySpecs) {
+                    println "creating binary"
+                    binarySpecs.create("sampleBinary")
+                }
+            }
+        }
+
+        apply plugin:MySamplePlugin
+        """
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryTasksIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryTasksIntegrationTest.groovy
new file mode 100644
index 0000000..a25f27f
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomBinaryTasksIntegrationTest.groovy
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Unroll
+
+public class CustomBinaryTasksIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        buildFile << """
+        import org.gradle.model.*
+        import org.gradle.model.collection.*
+
+        interface SampleBinary extends BinarySpec {}
+        class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {}
+        interface SampleLibrary extends ComponentSpec {}
+        class DefaultSampleLibrary extends BaseComponentSpec implements SampleLibrary {}
+
+        class MyComponentBasePlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleLibrary> builder) {
+                    builder.defaultImplementation(DefaultSampleLibrary)
+                }
+
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+
+                @Mutate
+                void createSampleComponentComponents(CollectionBuilder<SampleLibrary> componentSpecs) {
+                    componentSpecs.create("sampleLib")
+                }
+
+                @ComponentBinaries
+                void createBinariesForSampleLibrary(CollectionBuilder<SampleBinary> binaries, SampleLibrary library) {
+                    binaries.create("\${library.name}BinaryOne")
+                    binaries.create("\${library.name}BinaryTwo")
+                }
+            }
+        }
+        apply plugin:MyComponentBasePlugin
+
+        """
+    }
+
+
+    @Unroll
+    def "executing #taskdescr triggers custom task"() {
+        given:
+        buildFile << """
+        class BinaryTasksPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryTasks
+                void createSampleComponentComponents(CollectionBuilder<Task> tasks, SampleBinary binary) {
+                    tasks.create("\${binary.name}Task")
+                }
+            }
+        }
+        apply plugin:BinaryTasksPlugin
+"""
+
+        when:
+        succeeds taskName
+        then:
+        executed ":sampleLibBinaryOneTask", ":sampleLibBinaryOne"
+
+        where:
+        taskName             | taskdescr
+        "assemble"           | "assemble task"
+        "sampleLibBinaryOne" | "binary lifecycle task"
+    }
+
+    def "can reference rule-added tasks in model"() {
+        given:
+        buildFile << """
+        class BinaryTasksPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryTasks
+                void createSampleComponentComponents(CollectionBuilder<Task> tasks, SampleBinary binary) {
+                    tasks.create("\${binary.name}Task")
+                }
+            }
+        }
+        apply plugin:BinaryTasksPlugin
+
+        task checkModel << {
+            assert project.binaries.size() == 2
+            assert project.binaries.sampleLibBinaryOne != null
+            assert project.binaries.sampleLibBinaryOne.tasks*.name == ['sampleLibBinaryOneTask']
+        }
+"""
+        expect:
+        succeeds "checkModel"
+    }
+
+    def "rule can declare task with type"() {
+        given:
+        buildFile << """
+        class BinaryCreationTask extends DefaultTask {
+            BinarySpec binary
+            @TaskAction void create(){
+                println "Building \${binary.getName()} via \${getName()} of type BinaryCreationTask"
+
+            }
+        }
+        class BinaryTasksPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryTasks
+                void createSampleComponentComponents(CollectionBuilder<Task> tasks, SampleBinary binary) {
+                    tasks.create("\${binary.name}Task", BinaryCreationTask) {
+                        println "configuring \${binary.getName()}"
+                        it.binary = binary
+                    }
+                }
+            }
+        }
+        apply plugin:BinaryTasksPlugin
+"""
+        when:
+        succeeds "sampleLibBinaryOne"
+
+        then:
+        executedAndNotSkipped ":sampleLibBinaryOneTask"
+        output.contains("Building sampleLibBinaryOne via sampleLibBinaryOneTask of type BinaryCreationTask")
+    }
+
+    def "rule applies only to specified binary type"() {
+        given:
+        buildFile << """
+        interface OtherBinary extends SampleBinary {}
+        class DefaultOtherBinary extends DefaultSampleBinary implements OtherBinary {}
+
+        class MyOtherBinariesPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<OtherBinary> builder) {
+                    builder.defaultImplementation(DefaultOtherBinary)
+                }
+
+                @ComponentBinaries
+                void createBinariesForSampleLibrary(CollectionBuilder<OtherBinary> binaries, SampleLibrary library) {
+                    binaries.create("\${library.name}OtherBinary")
+                }
+
+                @BinaryTasks
+                void createTasks(CollectionBuilder<Task> tasks, OtherBinary binary) {
+                    tasks.create("\${binary.name}OtherTask")
+                }
+            }
+        }
+        apply plugin:MyOtherBinariesPlugin
+"""
+        when:
+        succeeds "sampleLibBinaryOne"
+
+        then:
+        notExecuted ":sampleLibOtherBinaryOtherTask"
+
+        when:
+        succeeds "sampleLibOtherBinary"
+
+        then:
+        executed ":sampleLibOtherBinaryOtherTask"
+    }
+
+    def "can use additional parameters as rule inputs"() {
+        given:
+        buildFile << """
+        class CustomModel {
+            List<String> values = []
+        }
+
+        class BinaryTasksPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+
+                @Model
+                CustomModel customModel() {
+                    new CustomModel()
+                }
+
+                @BinaryTasks
+                void createTasks(CollectionBuilder<Task> tasks, $ruleInputs) {
+                    model.values.each { postFix ->
+                        tasks.create("\${binary.name}\${postFix}");
+                    }
+                }
+            }
+        }
+
+        apply plugin: BinaryTasksPlugin
+
+        model {
+            customModel {
+                values << "1st" << "2nd"
+            }
+        }
+
+"""
+        when:
+        succeeds "assemble"
+
+        then:
+        executed ":sampleLibBinaryOne1st", ":sampleLibBinaryOne2nd", ":sampleLibBinaryTwo1st", ":sampleLibBinaryTwo2nd"
+
+        where:
+        ruleInputs << ["SampleBinary binary, CustomModel model", "CustomModel model, SampleBinary binary"]
+    }
+
+    def "can create multiple tasks for each of multiple binaries for same component"() {
+        given:
+        buildFile << """
+        class BinaryTasksPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @BinaryTasks
+                void createTasks(CollectionBuilder<Task> tasks, SampleBinary binary) {
+                    tasks.create("\${binary.name}TaskOne"){
+                        it.doLast{
+                            println "running \${it.name}"
+                        }
+                    }
+                    tasks.create("\${binary.name}TaskTwo"){
+                        it.doLast{
+                            println "running \${it.name}"
+                        }
+                        it.dependsOn "\${binary.name}TaskOne"
+                    }
+                }
+            }
+        }
+        apply plugin:BinaryTasksPlugin
+"""
+
+        when:
+        succeeds "assemble"
+        then:
+        executedAndNotSkipped ":sampleLibBinaryOneTaskOne", ":sampleLibBinaryOneTaskTwo", ":sampleLibBinaryOne",
+                ":sampleLibBinaryTwoTaskOne", ":sampleLibBinaryTwoTaskTwo", ":sampleLibBinaryTwo",
+                ":assemble"
+
+        output.contains "running sampleLibBinaryOneTaskOne"
+        output.contains "running sampleLibBinaryOneTaskTwo"
+        output.contains "running sampleLibBinaryTwoTaskOne"
+        output.contains "running sampleLibBinaryTwoTaskTwo"
+    }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentBinariesIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentBinariesIntegrationTest.groovy
new file mode 100644
index 0000000..76a3bca
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentBinariesIntegrationTest.groovy
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Unroll
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class CustomComponentBinariesIntegrationTest extends AbstractIntegrationSpec {
+
+    def "setup"() {
+        buildFile << """
+import org.gradle.model.*
+import org.gradle.model.collection.*
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.Instantiator
+import javax.inject.Inject
+import org.gradle.internal.service.ServiceRegistry
+
+interface SampleBinary extends BinarySpec {}
+interface OtherSampleBinary extends SampleBinary {}
+
+interface LibrarySourceSet extends LanguageSourceSet {}
+
+class DefaultLibrarySourceSet extends BaseLanguageSourceSet implements LibrarySourceSet { }
+
+class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {}
+
+class OtherSampleBinaryImpl extends BaseBinarySpec implements OtherSampleBinary {}
+
+interface SampleLibrary extends ComponentSpec {}
+
+class DefaultSampleLibrary extends BaseComponentSpec implements SampleLibrary {}
+
+    class MyBinaryDeclarationModel implements Plugin<Project> {
+        void apply(final Project project) {}
+
+        static class ComponentModel extends RuleSource {
+            @ComponentType
+            void register(ComponentTypeBuilder<SampleLibrary> builder) {
+                builder.defaultImplementation(DefaultSampleLibrary)
+            }
+
+            @Mutate
+            void createSampleComponentComponents(CollectionBuilder<SampleLibrary> componentSpecs, ServiceRegistry serviceRegistry) {
+                componentSpecs.create("sampleLib", new Action<SampleLibrary>() {
+                    public void execute(SampleLibrary library) {
+                        library.sources {
+                            it.add(BaseLanguageSourceSet.create(DefaultLibrarySourceSet, "librarySource", "librarySource", serviceRegistry.get(FileResolver), serviceRegistry.get(Instantiator)))
+                        }
+                    }
+                });
+            }
+
+            @BinaryType
+            void register(BinaryTypeBuilder<SampleBinary> builder) {
+                builder.defaultImplementation(DefaultSampleBinary)
+            }
+
+            @BinaryType
+            void registerOther(BinaryTypeBuilder<OtherSampleBinary> builder) {
+                builder.defaultImplementation(OtherSampleBinaryImpl)
+            }
+        }
+    }
+
+    apply plugin:MyBinaryDeclarationModel
+"""
+    }
+
+    def "can register binaries using @ComponentBinaries"() {
+        when:
+        buildFile << withSimpleComponentBinaries()
+        buildFile << """
+
+
+        task checkModel << {
+            assert project.binaries.size() == 2
+            def sampleBinary = project.binaries.sampleLibBinary
+            def othersSampleBinary = project.binaries.sampleLibOtherBinary
+            assert sampleBinary instanceof SampleBinary
+            assert sampleBinary.displayName == "DefaultSampleBinary 'sampleLibBinary'"
+            assert othersSampleBinary instanceof OtherSampleBinary
+            assert othersSampleBinary.displayName == "OtherSampleBinaryImpl 'sampleLibOtherBinary'"
+        }
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "links binaries to component"() {
+        given:
+        buildFile << withSimpleComponentBinaries()
+        when:
+        succeeds "components"
+        then:
+        output.contains(toPlatformLineSeparators(
+"""DefaultSampleLibrary 'sampleLib'
+--------------------------------
+
+Source sets
+    DefaultLibrarySourceSet 'librarySource:librarySource'
+        src${File.separator}sampleLib${File.separator}librarySource
+
+Binaries
+    DefaultSampleBinary 'sampleLibBinary'
+        build using task: :sampleLibBinary
+    OtherSampleBinaryImpl 'sampleLibOtherBinary'
+        build using task: :sampleLibOtherBinary
+"""))
+    }
+
+    def "links components sourceSets to binaries"() {
+        when:
+        buildFile << withSimpleComponentBinaries()
+        buildFile << """
+        task checkSourceSets << {
+            def sampleBinary = project.binaries.sampleLibBinary
+            def othersSampleBinary = project.binaries.sampleLibOtherBinary
+            assert sampleBinary.source[0] instanceof DefaultLibrarySourceSet
+            assert sampleBinary.source[0].displayName == "DefaultLibrarySourceSet 'librarySource:librarySource'"
+            assert othersSampleBinary.source[0] instanceof DefaultLibrarySourceSet
+            assert othersSampleBinary.source[0].displayName == "DefaultLibrarySourceSet 'librarySource:librarySource'"
+        }
+"""
+        then:
+        succeeds "checkSourceSets"
+    }
+
+    @Unroll
+    def "can execute #taskdescr to build binary"() {
+        given:
+        buildFile << withSimpleComponentBinaries()
+        when:
+        succeeds taskName
+        then:
+        output.contains(":sampleLibBinary UP-TO-DATE")
+        where:
+        taskName          | taskdescr
+        "sampleLibBinary" | "lifecycle task"
+        "assemble"        | "assemble task"
+    }
+
+    def "Can access lifecycle task of binary via BinarySpec.buildTask"(){
+        when:
+        buildFile << withSimpleComponentBinaries()
+        buildFile << """
+
+        task tellTaskName << {
+            assert project.binaries.sampleLibBinary.buildTask instanceof Task
+            assert project.binaries.sampleLibBinary.buildTask.name ==  "sampleLibBinary"
+        }
+"""
+        then:
+        succeeds "tellTaskName"
+    }
+
+    def "ComponentBinaries rule supports additional parameters as rule inputs"() {
+        given:
+        buildFile << """
+        class CustomModel {
+            List<String> values = []
+        }
+
+        class MyComponentBinariesPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+               @Model
+               CustomModel customModel() {
+                   new CustomModel()
+               }
+
+               @ComponentBinaries
+               void createBinariesForSampleLibrary(CollectionBuilder<SampleBinary> binaries, $ruleInputs) {
+                   myModel.values.each{ value ->
+                        binaries.create("\${library.name}\${value}Binary")
+
+                   }
+               }
+           }
+        }
+
+
+        apply plugin: MyComponentBinariesPlugin
+
+        model {
+            customModel {
+                values << "1st" << "2nd"
+            }
+        }"""
+
+        when:
+        succeeds "components"
+        then:
+        output.contains(toPlatformLineSeparators("""
+DefaultSampleLibrary 'sampleLib'
+--------------------------------
+
+Source sets
+    DefaultLibrarySourceSet 'librarySource:librarySource'
+        src${File.separator}sampleLib${File.separator}librarySource
+
+Binaries
+    DefaultSampleBinary 'sampleLib1stBinary'
+        build using task: :sampleLib1stBinary
+    DefaultSampleBinary 'sampleLib2ndBinary'
+        build using task: :sampleLib2ndBinary
+"""))
+        where:
+        ruleInputs << ["SampleLibrary library, CustomModel myModel"]//,  "CustomModel myModel, SampleLibrary library"]
+    }
+
+    String withSimpleComponentBinaries() {
+        """
+         class MyComponentBinariesPlugin implements Plugin<Project> {
+            void apply(final Project project) {}
+
+            static class Rules extends RuleSource {
+                @ComponentBinaries
+                void createBinariesForSampleLibrary(CollectionBuilder<SampleBinary> binaries, SampleLibrary library) {
+                    binaries.create("\${library.name}Binary")
+                    binaries.create("\${library.name}OtherBinary", OtherSampleBinary)
+                }
+            }
+         }
+        apply plugin: MyComponentBinariesPlugin
+"""
+    }
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentPluginIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentPluginIntegrationTest.groovy
new file mode 100644
index 0000000..14c66a7
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/CustomComponentPluginIntegrationTest.groovy
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+
+class CustomComponentPluginIntegrationTest extends AbstractIntegrationSpec {
+    def "setup"() {
+        buildFile << """
+import org.gradle.model.*
+import org.gradle.model.collection.*
+
+interface SampleComponent extends ComponentSpec {
+    String getVersion()
+    void setVersion(String version)
+}
+class DefaultSampleComponent extends BaseComponentSpec implements SampleComponent {
+    String version
+}
+"""
+    }
+
+    def "plugin declares custom component"() {
+        when:
+        buildWithCustomComponentPlugin()
+
+        and:
+        buildFile << """
+task checkModel << {
+    assert project.componentSpecs.size() == 1
+    def sampleLib = project.componentSpecs.sampleLib
+    assert sampleLib instanceof SampleComponent
+    assert sampleLib.projectPath == project.path
+    assert sampleLib.displayName == "DefaultSampleComponent 'sampleLib'"
+    assert sampleLib.version == null
+}
+"""
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can configure component declared by model rule method using model rules DSL"() {
+        when:
+        buildWithCustomComponentPlugin()
+
+        and:
+        buildFile << """
+model {
+    components {
+        sampleLib {
+            version = '12'
+        }
+    }
+}
+task checkModel << {
+    def sampleLib = project.componentSpecs.sampleLib
+    assert sampleLib.version == '12'
+}
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can configure component declared by model rule DSL using model rule method"() {
+        when:
+        buildFile << """
+            class MySamplePlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+
+                @Mutate
+                void createSampleComponentComponents(CollectionBuilder<SampleComponent> componentSpecs) {
+                    componentSpecs.afterEach {
+                        version += ".1"
+                    }
+                }
+            }
+
+            apply plugin:MySamplePlugin
+
+            model {
+                components {
+                    sampleLib(SampleComponent) {
+                        version = '12'
+                    }
+                }
+            }
+
+            task checkModel << {
+                def sampleLib = project.componentSpecs.sampleLib
+                assert sampleLib.version == '12.1'
+            }
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "can register custom component model without creating"() {
+        when:
+        buildFile << """
+            class MySamplePlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+            }
+
+            apply plugin:MySamplePlugin
+
+            task checkModel << {
+                assert project.componentSpecs.size() == 0
+            }
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "custom component listed in components report"() {
+        given:
+        buildWithCustomComponentPlugin()
+
+        when:
+        succeeds "components"
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators(""":components
+
+------------------------------------------------------------
+Root project
+------------------------------------------------------------
+
+DefaultSampleComponent 'sampleLib'
+----------------------------------
+
+Source sets
+    No source sets.
+
+Binaries
+    No binaries.
+
+Note: currently not all plugins register their components, so some components may not be visible here.
+
+BUILD SUCCESSFUL"""))
+    }
+
+    def "can have component declaration and creation in separate plugins"() {
+        when:
+        buildFile << """
+            class MyComponentDeclarationModel extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+            }
+
+            class MyComponentCreationPlugin implements Plugin<Project> {
+                void apply(final Project project) {
+                    project.apply(plugin:MyComponentDeclarationModel)
+                }
+
+                static class Rules extends RuleSource {
+                    @Mutate
+                    void createSampleComponentComponents(CollectionBuilder<SampleComponent> componentSpecs) {
+                        componentSpecs.create("sampleLib")
+                    }
+                }
+            }
+
+            apply plugin:MyComponentCreationPlugin
+
+            task checkModel << {
+                 assert project.componentSpecs.size() == 1
+                 def sampleLib = project.componentSpecs.sampleLib
+                 assert sampleLib instanceof SampleComponent
+                 assert sampleLib.projectPath == project.path
+                 assert sampleLib.displayName == "DefaultSampleComponent 'sampleLib'"
+            }
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "Can define and create multiple component types in the same plugin"(){
+        when:
+        buildFile << """
+            interface SampleLibrary extends LibrarySpec {}
+            class DefaultSampleLibrary extends BaseComponentSpec implements SampleLibrary {}
+
+            class MySamplePlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+
+                @ComponentType
+                void registerAnother(ComponentTypeBuilder<SampleLibrary> builder) {
+                    builder.defaultImplementation(DefaultSampleLibrary)
+                }
+
+                @Mutate
+                void createSampleComponentInstances(CollectionBuilder<SampleComponent> componentSpecs) {
+                    componentSpecs.create("sampleComponent")
+                }
+
+                @Mutate
+                void createSampleLibraryInstances(CollectionBuilder<SampleLibrary> componentSpecs) {
+                    componentSpecs.create("sampleLib")
+                }
+            }
+
+            apply plugin:MySamplePlugin
+
+            task checkModel << {
+                 assert project.componentSpecs.size() == 2
+
+                 def sampleComponent = project.componentSpecs.sampleComponent
+                 assert sampleComponent instanceof SampleComponent
+                 assert sampleComponent.projectPath == project.path
+                 assert sampleComponent.displayName == "DefaultSampleComponent 'sampleComponent'"
+
+                 def sampleLib = project.componentSpecs.sampleLib
+                 assert sampleLib instanceof SampleLibrary
+                 assert sampleLib.projectPath == project.path
+                 assert sampleLib.displayName == "DefaultSampleLibrary 'sampleLib'"
+            }
+"""
+
+        then:
+        succeeds "checkModel"
+    }
+
+    def "reports failure for invalid component type method"() {
+        given:
+        settingsFile << """rootProject.name = 'custom-component'"""
+        buildFile << """
+            class MySamplePlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder, String illegalOtherParameter) {
+                }
+            }
+
+            apply plugin:MySamplePlugin
+"""
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription "A problem occurred evaluating root project 'custom-component'."
+        failure.assertHasCause "Failed to apply plugin [class 'MySamplePlugin']"
+        failure.assertHasCause "MySamplePlugin#register(org.gradle.platform.base.ComponentTypeBuilder<SampleComponent>, java.lang.String) is not a valid component model rule method."
+        failure.assertHasCause "Method annotated with @ComponentType must have a single parameter of type 'org.gradle.platform.base.ComponentTypeBuilder'."
+    }
+
+    def "cannot register same component type multiple times"(){
+        given:
+        buildWithCustomComponentPlugin()
+
+        and:
+        buildFile << """
+            class MyOtherPlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+            }
+
+            apply plugin:MyOtherPlugin
+"""
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription "A problem occurred configuring root project 'custom-component'."
+        failure.assertHasCause "Exception thrown while executing model rule: MyOtherPlugin#register(org.gradle.platform.base.ComponentTypeBuilder<SampleComponent>)"
+        failure.assertHasCause "Cannot register a factory for type SampleComponent because a factory for this type was already registered by MySamplePlugin#register(org.gradle.platform.base.ComponentTypeBuilder<SampleComponent>)."
+    }
+
+    def buildWithCustomComponentPlugin() {
+        settingsFile << """rootProject.name = 'custom-component'"""
+        buildFile << """
+            class MySamplePlugin extends RuleSource {
+                @ComponentType
+                void register(ComponentTypeBuilder<SampleComponent> builder) {
+                    builder.defaultImplementation(DefaultSampleComponent)
+                }
+                @Mutate
+                void createSampleComponentComponents(CollectionBuilder<SampleComponent> componentSpecs) {
+                    componentSpecs.create("sampleLib")
+                }
+            }
+
+            apply plugin:MySamplePlugin
+        """
+    }
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeIntegrationTest.groovy
new file mode 100644
index 0000000..695d500
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeIntegrationTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.EnableModelDsl
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class LanguageTypeIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+        interface CustomLanguageSourceSet extends LanguageSourceSet {}
+        class DefaultCustomLanguageSourceSet extends BaseLanguageSourceSet implements CustomLanguageSourceSet {}
+
+        class CustomLanguagePlugin extends RuleSource {
+            @LanguageType
+            void declareCustomLanguage(LanguageTypeBuilder<CustomLanguageSourceSet> builder) {
+                builder.setLanguageName("custom")
+                builder.defaultImplementation(DefaultCustomLanguageSourceSet)
+            }
+        }
+
+        apply plugin:CustomLanguagePlugin
+"""
+    }
+
+    def "registers language in languageRegistry"(){
+        given:
+        EnableModelDsl.enable(executer)
+        buildFile << """
+model {
+    tasks {
+        create("printLanguages") {
+            it.doLast {
+                 def languages = \$("languages")*.name.sort().join(", ")
+                 println "registered languages: \$languages"
+            }
+        }
+    }
+}
+        """
+        when:
+        succeeds "printLanguages"
+        then:
+        output.contains("registered languages: custom")
+    }
+
+    def "can add custom language sourceSet to component"() {
+        when:
+        buildFile << """
+        import org.gradle.model.*
+        import org.gradle.model.collection.*
+
+        interface SampleComponent extends ComponentSpec {}
+        class DefaultSampleComponent extends BaseComponentSpec implements SampleComponent {}
+
+
+        class CustomComponentPlugin extends RuleSource {
+            @ComponentType
+            void register(ComponentTypeBuilder<SampleComponent> builder) {
+                builder.defaultImplementation(DefaultSampleComponent)
+            }
+
+            @Mutate
+            void createSampleComponentComponents(CollectionBuilder<SampleComponent> componentSpecs) {
+                componentSpecs.create("main")
+            }
+        }
+
+        apply plugin:CustomComponentPlugin
+
+        model {
+            components {
+                main {
+                    sources {
+                        custom(CustomLanguageSourceSet)
+                    }
+                }
+            }
+        }
+
+"""
+        then:
+        succeeds "components"
+        and:
+        output.contains(toPlatformLineSeparators("""
+DefaultSampleComponent 'main'
+-----------------------------
+
+Source sets
+    DefaultCustomLanguageSourceSet 'main:custom'
+        src${File.separator}main${File.separator}custom
+"""))
+    }
+
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy
new file mode 100644
index 0000000..e0bece0
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.test.fixtures.archive.ZipTestFixture
+import org.junit.Rule
+import spock.lang.Ignore
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+ at Ignore("Temp - LD - 5/2/15")
+class LanguageTypeSampleIntegrationTest extends AbstractIntegrationSpec {
+    @Rule
+    Sample languageTypeSample = new Sample(temporaryFolder, "customModel/languageType")
+
+    def "shows custom language sourcesets in component"() {
+        given:
+        sample languageTypeSample
+        when:
+        succeeds "components"
+        then:
+        output.contains(toPlatformLineSeparators("""
+DefaultDocumentationComponent 'docs'
+------------------------------------
+
+Source sets
+    DefaultMarkdownSourceSet 'docs:userguide'
+        src${File.separator}docs${File.separator}userguide
+
+Binaries
+    DefaultDocumentationBinary 'docsBinary'
+        build using task: :docsBinary
+"""))
+
+    }
+
+    def "can build binary"() {
+        given:
+        sample languageTypeSample
+        when:
+        succeeds "assemble"
+        then:
+        executedTasks == [":docsBinaryUserguideHtmlCompile", ":zipDocsBinary", ":docsBinary", ":assemble"]
+        and:
+        new ZipTestFixture(languageTypeSample.dir.file("build/docsBinary/docsBinary.zip")).containsDescendants(
+                "userguide/chapter1.html",
+                "userguide/chapter2.html",
+                "userguide/index.html")
+
+    }
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LanguageBasePluginIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LanguageBasePluginIntegrationTest.groovy
new file mode 100644
index 0000000..958e082
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LanguageBasePluginIntegrationTest.groovy
@@ -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.language.base.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class LanguageBasePluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LifecycleBasePluginIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LifecycleBasePluginIntegrationTest.groovy
new file mode 100644
index 0000000..d18e10b
--- /dev/null
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/plugins/LifecycleBasePluginIntegrationTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.plugins
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Unroll
+
+class LifecycleBasePluginIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+        apply plugin:org.gradle.language.base.plugins.LifecycleBasePlugin
+        """
+    }
+
+    @Unroll
+    def "throws deprecation warning when applied in build with #taskName"() {
+        when:
+        buildFile << """
+
+        task $taskName << {
+            println "custom $taskName task"
+        }
+        """
+        executer.withDeprecationChecksDisabled()
+        succeeds(taskName)
+        then:
+        output.contains("Defining custom '$taskName' task when using the standard Gradle lifecycle plugins has been deprecated and is scheduled to be removed")
+        where:
+        taskName << ["check", "clean", "build", "assemble"]
+    }
+
+    def "can attach custom task as dependency to lifecycle task - #task"() {
+        when:
+        buildFile << """
+            task myTask {}
+            ${taskName}.dependsOn myTask
+        """
+
+        then:
+        succeeds(taskName)
+        ":myTask" in executedTasks
+
+        where:
+        taskName << ["check", "build"]
+    }
+
+    def "binaries are built when build task execution is requested"() {
+        buildFile << """
+            import org.gradle.model.collection.CollectionBuilder
+
+            interface SampleBinary extends BinarySpec {
+            }
+
+            class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {
+            }
+
+            class SampleBinaryPlugin extends RuleSource {
+                @BinaryType
+                void register(BinaryTypeBuilder<SampleBinary> builder) {
+                    builder.defaultImplementation(DefaultSampleBinary)
+                }
+
+                @Mutate
+                void createSampleBinary(CollectionBuilder<SampleBinary> binarySpecs) {
+                    binarySpecs.create("sampleBinary")
+                }
+            }
+
+            apply plugin: SampleBinaryPlugin
+        """
+
+        when:
+        succeeds "build"
+
+        then:
+        ":sampleBinary" in executedTasks
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/internal/tasks/compile/Compiler.java b/subprojects/platform-base/src/main/java/org/gradle/api/internal/tasks/compile/Compiler.java
new file mode 100644
index 0000000..2aa556b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/api/internal/tasks/compile/Compiler.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.compile;
+
+/**
+ * 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.
+ */
+ at Deprecated
+public interface Compiler {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/FunctionalSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/FunctionalSourceSet.java
new file mode 100644
index 0000000..ac58bc4
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/FunctionalSourceSet.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.language.base;
+
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * A container holding {@link LanguageSourceSet}s with a similar function
+ * (production code, test code, etc.).
+ */
+ at Incubating
+// TODO:DAZ Make this internal
+public interface FunctionalSourceSet extends ExtensiblePolymorphicDomainObjectContainer<LanguageSourceSet>, Named {
+    FunctionalSourceSet copy(String name);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/LanguageSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/LanguageSourceSet.java
new file mode 100644
index 0000000..6dd3f18
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/LanguageSourceSet.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.language.base;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.api.Task;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.BuildableModelElement;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * A set of sources for a programming language.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface LanguageSourceSet extends Named, BuildableModelElement {
+    String getDisplayName();
+
+    // TODO: do we want to keep using SourceDirectorySet in the new API?
+    // would feel more natural if dirs could be added directly to LanguageSourceSet
+    // could also think about extending SourceDirectorySet
+
+    /**
+     * The source files.
+     */
+    SourceDirectorySet getSource();
+
+    /**
+     * Configure the sources
+     */
+    void source(Action<? super SourceDirectorySet> config);
+
+    void generatedBy(Task generatorTask);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/ProjectSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/ProjectSourceSet.java
new file mode 100644
index 0000000..ab7c503
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/ProjectSourceSet.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.language.base;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+
+/**
+ * A container of {@link org.gradle.language.base.LanguageSourceSet}s. Added to a project by the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
+ */
+ at Incubating
+public interface ProjectSourceSet extends DomainObjectSet<LanguageSourceSet> {}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/SourcesArtifact.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/SourcesArtifact.java
new file mode 100644
index 0000000..8038ab0
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/SourcesArtifact.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.base.artifact;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Artifact;
+
+/**
+ * An artifact containing sources for a software component.
+ *
+ * @since 2.0
+ */
+ at Incubating
+public interface SourcesArtifact extends Artifact {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java
new file mode 100644
index 0000000..2b3361a
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 representing artifacts relevant to languages in general.
+ */
+package org.gradle.language.base.artifact;
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultFunctionalSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultFunctionalSourceSet.java
new file mode 100644
index 0000000..eb33b44
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultFunctionalSourceSet.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.language.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+
+public class DefaultFunctionalSourceSet extends DefaultPolymorphicDomainObjectContainer<LanguageSourceSet> implements FunctionalSourceSet {
+    private final String name;
+    private final ProjectSourceSet projectSourceSet;
+
+    public DefaultFunctionalSourceSet(String name, Instantiator instantiator, final ProjectSourceSet projectSourceSet) {
+        super(LanguageSourceSet.class, instantiator);
+        this.name = name;
+        this.projectSourceSet = projectSourceSet;
+        whenObjectAdded(new Action<LanguageSourceSet>() {
+            public void execute(LanguageSourceSet languageSourceSet) {
+                projectSourceSet.add(languageSourceSet);
+            }
+        });
+    }
+
+    @Override
+    public String toString() {
+        return String.format("source set '%s'", name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    // TODO:DAZ This needs unit testing
+    // TODO:DAZ Perhaps we should pull out a LanguageSourceSet 'factory-for-type' so we only register the languages once
+    public FunctionalSourceSet copy(String name) {
+        DefaultFunctionalSourceSet copy = getInstantiator().newInstance(DefaultFunctionalSourceSet.class, name, getInstantiator(), projectSourceSet);
+        for (Class<? extends LanguageSourceSet> languageType : factories.keySet()) {
+            copyFactory(copy, languageType);
+        }
+        copy.addAll(this);
+        return copy;
+    }
+
+    <T extends LanguageSourceSet, U extends T> void copyFactory(DefaultFunctionalSourceSet target, Class<T> type) {
+        @SuppressWarnings("unchecked")
+        NamedDomainObjectFactory<U> factory = (NamedDomainObjectFactory<U>) factories.get(type);
+        target.registerFactory(type, factory);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultProjectSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultProjectSourceSet.java
new file mode 100644
index 0000000..9557375
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/DefaultProjectSourceSet.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.language.base.internal;
+
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+
+public class DefaultProjectSourceSet extends DefaultDomainObjectSet<LanguageSourceSet> implements ProjectSourceSet {
+    public DefaultProjectSourceSet() {
+        super(LanguageSourceSet.class);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetContainer.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetContainer.java
new file mode 100644
index 0000000..0a42f35
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetContainer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.util.Set;
+
+public class LanguageSourceSetContainer {
+    private final NotationParser<Object, Set<LanguageSourceSet>> sourcesNotationParser = SourceSetNotationParser.parser();
+    private FunctionalSourceSet mainSources;
+    private Set<LanguageSourceSet> additionalSources = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
+
+    public void setMainSources(FunctionalSourceSet mainSources) {
+        this.mainSources = mainSources;
+    }
+
+    public void source(Object sources) {
+        additionalSources.addAll(sourcesNotationParser.parseNotation(sources));
+    }
+
+    public FunctionalSourceSet getMainSources() {
+        return mainSources;
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSources() {
+        Set<LanguageSourceSet> all = Sets.newLinkedHashSet();
+        all.addAll(mainSources);
+        all.addAll(additionalSources);
+        return new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class, all);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetInternal.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetInternal.java
new file mode 100644
index 0000000..6a2cd81
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/LanguageSourceSetInternal.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.language.base.internal;
+
+import org.gradle.api.Task;
+import org.gradle.language.base.LanguageSourceSet;
+
+public interface LanguageSourceSetInternal extends LanguageSourceSet {
+
+    /**
+     * A unique name for this source set across all functional source sets.
+     */
+    String getFullName();
+
+    /**
+     * Return true if the source set contains sources, or if the source set is generated.
+     */
+    boolean getMayHaveSources();
+
+    Task getGeneratorTask();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceSetNotationParser.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceSetNotationParser.java
new file mode 100644
index 0000000..f96d928
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceSetNotationParser.java
@@ -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.language.base.internal;
+
+import org.gradle.internal.typeconversion.*;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class SourceSetNotationParser {
+    public static NotationParser<Object, Set<LanguageSourceSet>> parser() {
+        return NotationParserBuilder
+                .toType(new TypeInfo<Set<LanguageSourceSet>>(Set.class))
+                .converter(new FunctionalSourceSetConverter())
+                .converter(new SingleLanguageSourceSetConverter())
+                .converter(new LanguageSourceSetCollectionConverter())
+                .toComposite();
+    }
+
+    private static class FunctionalSourceSetConverter extends TypedNotationConverter<FunctionalSourceSet, Set<LanguageSourceSet>> {
+        private FunctionalSourceSetConverter() {
+            super(FunctionalSourceSet.class);
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(FunctionalSourceSet notation) {
+            return notation;
+        }
+    }
+
+    private static class SingleLanguageSourceSetConverter extends TypedNotationConverter<LanguageSourceSet, Set<LanguageSourceSet>> {
+        private SingleLanguageSourceSetConverter() {
+            super(LanguageSourceSet.class);
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(LanguageSourceSet notation) {
+            return Collections.singleton(notation);
+        }
+    }
+
+    private static class LanguageSourceSetCollectionConverter extends TypedNotationConverter<Collection<LanguageSourceSet>, Set<LanguageSourceSet>> {
+        private LanguageSourceSetCollectionConverter() {
+            super(new TypeInfo<Collection<LanguageSourceSet>>(Collection.class));
+        }
+
+        @Override
+        protected Set<LanguageSourceSet> parseType(Collection<LanguageSourceSet> notation) {
+            return new LinkedHashSet<LanguageSourceSet>(notation);
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceTransformTaskConfig.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceTransformTaskConfig.java
new file mode 100644
index 0000000..b7999b3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/SourceTransformTaskConfig.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Task;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.BinarySpec;
+
+public interface SourceTransformTaskConfig {
+    String getTaskPrefix();
+    Class<? extends DefaultTask> getTaskType();
+    void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompileSpec.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompileSpec.java
new file mode 100644
index 0000000..09b2b6b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompileSpec.java
@@ -0,0 +1,20 @@
+/*
+ * 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.language.base.internal.compile;
+
+public interface CompileSpec {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/Compiler.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/Compiler.java
new file mode 100644
index 0000000..ea8312b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/Compiler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.base.internal.compile;
+
+import org.gradle.api.tasks.WorkResult;
+
+public interface Compiler<T extends CompileSpec> {
+    WorkResult execute(T spec);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerFactory.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerFactory.java
new file mode 100644
index 0000000..8003058
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerFactory.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.compile;
+
+public interface CompilerFactory<T extends CompileSpec> {
+    Compiler<T> newCompiler(T spec);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerUtil.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerUtil.java
new file mode 100644
index 0000000..84009ce
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/compile/CompilerUtil.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.compile;
+
+public class CompilerUtil {
+    @SuppressWarnings("unchecked")
+    public static <T extends CompileSpec> Compiler<T> castCompiler(Compiler<?> compiler) {
+        return (Compiler<T>) compiler;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/plugins/CleanRule.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/plugins/CleanRule.java
new file mode 100644
index 0000000..a768f0a
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/plugins/CleanRule.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.plugins;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Rule;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Delete;
+import org.gradle.api.tasks.TaskContainer;
+
+public class CleanRule implements Rule {
+
+    public static final String CLEAN = "clean";
+
+    private final TaskContainer tasks;
+
+    public CleanRule(TaskContainer tasks) {
+        this.tasks = tasks;
+    }
+
+    public String getDescription() {
+        return String.format("Pattern: %s<TaskName>: Cleans the output files of a task.", CLEAN);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Rule: %s", getDescription());
+    }
+
+    public void apply(String taskName) {
+        if (!taskName.startsWith(CLEAN) || taskName.equals(CLEAN)) {
+            return;
+        }
+        String targetTaskName = taskName.substring(CLEAN.length());
+        if (Character.isLowerCase(targetTaskName.charAt(0))) {
+            return;
+        }
+
+        Task task = tasks.findByName(StringUtils.uncapitalize(targetTaskName));
+        if (task == null) {
+            return;
+        }
+
+        Delete clean = tasks.create(taskName, Delete.class);
+        clean.delete(task.getOutputs().getFiles());
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageRegistry.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageRegistry.java
new file mode 100644
index 0000000..85d41c3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageRegistry.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+import com.google.common.reflect.TypeToken;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+
+public class DefaultLanguageRegistry extends DefaultDomainObjectSet<LanguageRegistration<?>> implements LanguageRegistry {
+    public DefaultLanguageRegistry() {
+        super(getLanguageRegistrationType());
+    }
+
+    private static Class<LanguageRegistration<?>> getLanguageRegistrationType() {
+        @SuppressWarnings("unchecked")
+        Class<LanguageRegistration<?>> rawType = (Class<LanguageRegistration<?>>) new TypeToken<LanguageRegistration<?>>() {}.getRawType();
+        return rawType;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageTransformContainer.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageTransformContainer.java
new file mode 100644
index 0000000..f64b7b9
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/DefaultLanguageTransformContainer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+import com.google.common.reflect.TypeToken;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+
+public class DefaultLanguageTransformContainer extends DefaultDomainObjectSet<LanguageTransform<?, ?>> implements LanguageTransformContainer {
+    public DefaultLanguageTransformContainer() {
+        super(getLanguageTransformType());
+    }
+
+    private static Class<LanguageTransform<?, ?>> getLanguageTransformType() {
+        @SuppressWarnings("unchecked")
+        Class<LanguageTransform<?, ?>> rawType = (Class<LanguageTransform<?, ?>>) new TypeToken<LanguageTransform<?, ?>>() {}.getRawType();
+        return rawType;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistration.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistration.java
new file mode 100644
index 0000000..0cfc26b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistration.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A registered language.
+ */
+public interface LanguageRegistration<U extends LanguageSourceSet> {
+    /**
+     * The name.
+     */
+    String getName();
+
+    /**
+     * The interface type of the language source set.
+     */
+    Class<U> getSourceSetType();
+
+    NamedDomainObjectFactory<? extends U> getSourceSetFactory(String parentName);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistry.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistry.java
new file mode 100644
index 0000000..dcb32ca
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+import org.gradle.api.DomainObjectSet;
+
+/**
+ * A registry of languages.
+ */
+public interface LanguageRegistry extends DomainObjectSet<LanguageRegistration<?>> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransform.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransform.java
new file mode 100644
index 0000000..cd9f184
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransform.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.TransformationFileType;
+
+import java.util.Map;
+
+/**
+ * A registered language transformation.
+ */
+public interface LanguageTransform<U extends LanguageSourceSet, V extends TransformationFileType> {
+    /**
+     * The interface type of the language source set.
+     */
+    Class<U> getSourceSetType();
+
+    /**
+     * The output type generated from these language sources.
+     */
+    Class<V> getOutputType();
+
+    /**
+     * The tool extensions that should be added to any binary with these language sources.
+     */
+    Map<String, Class<?>> getBinaryTools();
+
+    /**
+     * The task used to transform sources into code for the target runtime.
+     */
+    SourceTransformTaskConfig getTransformTask();
+
+    // TODO:DAZ This should be declarative, not imperative
+    boolean applyToBinary(BinarySpec binary);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransformContainer.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransformContainer.java
new file mode 100644
index 0000000..12d1cbd
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/LanguageTransformContainer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry;
+
+import org.gradle.api.DomainObjectSet;
+
+/**
+ * A registry of language transforms.
+ */
+public interface LanguageTransformContainer extends DomainObjectSet<LanguageTransform<?, ?>> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/RuleBasedLanguageRegistration.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/RuleBasedLanguageRegistration.java
new file mode 100644
index 0000000..771ab43
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/registry/RuleBasedLanguageRegistration.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.language.base.internal.registry;
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+
+public class RuleBasedLanguageRegistration<T extends BaseLanguageSourceSet> implements LanguageRegistration<T> {
+
+    private final String name;
+    private final Class<T> sourceSetType;
+    private final Class<? extends T> sourceSetImplementation;
+    private Instantiator instantiator;
+    private FileResolver fileResolver;
+
+    public RuleBasedLanguageRegistration(String name, Class<T> sourceSetType, Class<? extends T> sourceSetImplementation, Instantiator instantiator, FileResolver fileResolver) {
+        this.name = name;
+        this.sourceSetType = sourceSetType;
+        this.sourceSetImplementation = sourceSetImplementation;
+        this.instantiator = instantiator;
+        this.fileResolver = fileResolver;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Class<T> getSourceSetType() {
+        return sourceSetType;
+    }
+
+    @Override
+    public NamedDomainObjectFactory<? extends T> getSourceSetFactory(final String parentName) {
+        return new NamedDomainObjectFactory<T>() {
+            @Override
+            public T create(String name) {
+                return BaseLanguageSourceSet.create(sourceSetImplementation, name, parentName, fileResolver, instantiator);
+            }
+        };
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/SimpleStaleClassCleaner.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/SimpleStaleClassCleaner.java
new file mode 100644
index 0000000..54af863
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/SimpleStaleClassCleaner.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.language.base.internal.tasks;
+
+import org.gradle.api.internal.TaskOutputsInternal;
+
+import java.io.File;
+
+public class SimpleStaleClassCleaner extends StaleClassCleaner {
+    private final TaskOutputsInternal taskOutputs;
+    private boolean didWork;
+
+    public SimpleStaleClassCleaner(TaskOutputsInternal taskOutputs) {
+        this.taskOutputs = taskOutputs;
+    }
+
+    @Override
+    public void execute() {
+        String prefix = getDestinationDir().getAbsolutePath() + File.separator;
+        for (File f : taskOutputs.getPreviousFiles()) {
+            if (f.getAbsolutePath().startsWith(prefix)) {
+                didWork |= f.delete();
+            }
+        }
+    }
+
+    public boolean getDidWork() {
+        return didWork;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleClassCleaner.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleClassCleaner.java
new file mode 100644
index 0000000..b0305a1
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/internal/tasks/StaleClassCleaner.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.language.base.internal.tasks;
+
+import org.gradle.api.file.FileCollection;
+
+import java.io.File;
+
+public abstract class StaleClassCleaner {
+    private File destinationDir;
+    FileCollection source;
+
+    public abstract void execute();
+
+    public FileCollection getSource() {
+        return source;
+    }
+
+    public void setSource(FileCollection source) {
+        this.source = source;
+    }
+
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/package-info.java
similarity index 100%
rename from subprojects/language-base/src/main/groovy/org/gradle/language/base/package-info.java
rename to subprojects/platform-base/src/main/java/org/gradle/language/base/package-info.java
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/ComponentModelBasePlugin.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/ComponentModelBasePlugin.java
new file mode 100644
index 0000000..2ed6ad8
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/ComponentModelBasePlugin.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.base.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.plugins.ExtensionContainer;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.*;
+import org.gradle.model.*;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.collection.internal.BridgedCollections;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.platform.base.PlatformContainer;
+import org.gradle.platform.base.internal.*;
+
+import javax.inject.Inject;
+
+import static org.apache.commons.lang.StringUtils.capitalize;
+
+/**
+ * Base plugin for language support.
+ *
+ * Adds a {@link org.gradle.platform.base.ComponentSpecContainer} named {@code componentSpecs} to the project.
+ *
+ * For each binary instance added to the binaries container, registers a lifecycle task to create that binary.
+ */
+ at Incubating
+public class ComponentModelBasePlugin implements Plugin<ProjectInternal> {
+
+    private final Instantiator instantiator;
+    private final ModelRegistry modelRegistry;
+
+    @Inject
+    public ComponentModelBasePlugin(Instantiator instantiator, ModelRegistry modelRegistry) {
+        this.instantiator = instantiator;
+        this.modelRegistry = modelRegistry;
+    }
+
+    public void apply(final ProjectInternal project) {
+        project.getPluginManager().apply(LanguageBasePlugin.class);
+
+        // TODO:DAZ Remove this extension: will first need to change ComponentTypeRuleDefinitionHandler not to access ComponentSpecContainer via extension
+        DefaultComponentSpecContainer components = project.getExtensions().create("componentSpecs", DefaultComponentSpecContainer.class, instantiator);
+        String descriptor = ComponentModelBasePlugin.class.getName() + ".apply()";
+        BridgedCollections.dynamicTypes(
+                modelRegistry,
+                ModelPath.path("components"),
+                descriptor,
+                ModelType.of(DefaultComponentSpecContainer.class),
+                ModelType.of(DefaultComponentSpecContainer.class),
+                ModelType.of(ComponentSpec.class),
+                components,
+                Named.Namer.INSTANCE,
+                BridgedCollections.itemDescriptor(descriptor)
+        );
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @Model
+        LanguageRegistry languages(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(Instantiator.class).newInstance(DefaultLanguageRegistry.class);
+        }
+
+        @Model
+        LanguageTransformContainer languageTransforms(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(Instantiator.class).newInstance(DefaultLanguageTransformContainer.class);
+        }
+
+        @Defaults
+        void initializeSourceSetsForComponents(final CollectionBuilder<ComponentSpec> components, LanguageRegistry languageRegistry, LanguageTransformContainer languageTransforms) {
+            for (LanguageRegistration<?> languageRegistration : languageRegistry) {
+                // TODO - allow beforeEach() to be applied to internal types
+                components.beforeEach(ComponentSourcesRegistrationAction.create(languageRegistration, languageTransforms));
+            }
+        }
+
+        // Required because creation of Binaries from Components is not yet wired into the infrastructure
+        @Mutate
+        void closeComponentsForBinaries(CollectionBuilder<Task> tasks, ComponentSpecContainer components) {
+        }
+
+        // Finalizing here, as we need this to run after any 'assembling' task (jar, link, etc) is created.
+        @Finalize
+        void createSourceTransformTasks(final TaskContainer tasks, final BinaryContainer binaries, LanguageTransformContainer languageTransforms) {
+            for (LanguageTransform<?, ?> language : languageTransforms) {
+                for (final BinarySpecInternal binary : binaries.withType(BinarySpecInternal.class)) {
+                    if (binary.isLegacyBinary() || !language.applyToBinary(binary)) {
+                        continue;
+                    }
+
+                    final SourceTransformTaskConfig taskConfig = language.getTransformTask();
+                    binary.getSource().withType(language.getSourceSetType(), new Action<LanguageSourceSet>() {
+                        public void execute(LanguageSourceSet languageSourceSet) {
+                            LanguageSourceSetInternal sourceSet = (LanguageSourceSetInternal) languageSourceSet;
+                            if (sourceSet.getMayHaveSources()) {
+                                String taskName = taskConfig.getTaskPrefix() + capitalize(binary.getName()) + capitalize(sourceSet.getFullName());
+                                Task task = tasks.create(taskName, taskConfig.getTaskType());
+
+                                taskConfig.configureTask(task, binary, sourceSet);
+
+                                task.dependsOn(sourceSet);
+                                binary.getTasks().add(task);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Mutate
+        void applyDefaultSourceConventions(CollectionBuilder<ComponentSpec> componentSpecs) {
+            componentSpecs.afterEach(new Action<ComponentSpec>() {
+                @Override
+                public void execute(ComponentSpec componentSpec) {
+                    for (LanguageSourceSet languageSourceSet : componentSpec.getSource()) {
+                        // Only apply default locations when none explicitly configured
+                        if (languageSourceSet.getSource().getSrcDirs().isEmpty()) {
+                            languageSourceSet.getSource().srcDir(String.format("src/%s/%s", componentSpec.getName(), languageSourceSet.getName()));
+                        }
+                    }
+                }
+            });
+        }
+
+        // TODO:DAZ Work out why this is required
+        @Mutate
+        void closeSourcesForBinaries(BinaryContainer binaries, ProjectSourceSet sources) {
+            // Only required because sources aren't fully integrated into model
+        }
+
+        @Model
+        PlatformContainer platforms(ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return instantiator.newInstance(DefaultPlatformContainer.class, instantiator);
+        }
+
+        @Model
+        PlatformResolvers platformResolver(PlatformContainer platforms, ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return instantiator.newInstance(DefaultPlatformResolvers.class, platforms);
+        }
+
+        @Mutate
+        void registerPlatformExtension(ExtensionContainer extensions, PlatformContainer platforms) {
+            extensions.add("platforms", platforms);
+        }
+
+    }
+
+    // TODO:DAZ Needs to be a separate action since can't have parameterized utility methods in a RuleSource
+    private static class ComponentSourcesRegistrationAction<U extends LanguageSourceSet> implements Action<ComponentSpec> {
+        private final LanguageRegistration<U> languageRegistration;
+        private final LanguageTransformContainer languageTransforms;
+
+        private ComponentSourcesRegistrationAction(LanguageRegistration<U> registration, LanguageTransformContainer languageTransforms) {
+            this.languageRegistration = registration;
+            this.languageTransforms = languageTransforms;
+        }
+
+        public static <U extends LanguageSourceSet> ComponentSourcesRegistrationAction<U> create(LanguageRegistration<U> registration, LanguageTransformContainer languageTransforms) {
+            return new ComponentSourcesRegistrationAction<U>(registration, languageTransforms);
+        }
+
+        public void execute(ComponentSpec componentSpec) {
+            ComponentSpecInternal componentSpecInternal = (ComponentSpecInternal) componentSpec;
+            registerLanguageSourceSetFactory(componentSpecInternal);
+            createDefaultSourceSetForComponents(componentSpecInternal);
+        }
+
+        void registerLanguageSourceSetFactory(final ComponentSpecInternal component) {
+            final FunctionalSourceSet functionalSourceSet = component.getSources();
+            NamedDomainObjectFactory<? extends U> sourceSetFactory = languageRegistration.getSourceSetFactory(functionalSourceSet.getName());
+            functionalSourceSet.registerFactory(languageRegistration.getSourceSetType(), sourceSetFactory);
+        }
+
+        // If there is a transform for the language into one of the component inputs, add a default source set
+        void createDefaultSourceSetForComponents(final ComponentSpecInternal component) {
+            final FunctionalSourceSet functionalSourceSet = component.getSources();
+            for (LanguageTransform<?, ?> languageTransform : languageTransforms) {
+                if (languageTransform.getSourceSetType().equals(languageRegistration.getSourceSetType())
+                        && component.getInputTypes().contains(languageTransform.getOutputType())) {
+                    functionalSourceSet.maybeCreate(languageRegistration.getName(), languageRegistration.getSourceSetType());
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LanguageBasePlugin.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LanguageBasePlugin.java
new file mode 100644
index 0000000..ac5924d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LanguageBasePlugin.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.base.plugins;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.*;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.plugins.ExtensionContainer;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.internal.BiAction;
+import org.gradle.internal.BiActions;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.DefaultProjectSourceSet;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.internal.BridgedCollections;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.model.internal.type.ModelTypes;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.platform.base.internal.DefaultBinaryContainer;
+
+import javax.inject.Inject;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base plugin for language support.
+ *
+ * Adds a {@link org.gradle.platform.base.BinaryContainer} named {@code binaries} to the project. Adds a {@link org.gradle.language.base.ProjectSourceSet} named {@code sources} to the project.
+ *
+ * For each binary instance added to the binaries container, registers a lifecycle task to create that binary.
+ */
+ at Incubating
+public class LanguageBasePlugin implements Plugin<Project> {
+
+    private final Instantiator instantiator;
+    private final ModelRegistry modelRegistry;
+
+    @Inject
+    public LanguageBasePlugin(Instantiator instantiator, ModelRegistry modelRegistry) {
+        this.instantiator = instantiator;
+        this.modelRegistry = modelRegistry;
+    }
+
+    public void apply(final Project target) {
+        target.getPluginManager().apply(LifecycleBasePlugin.class);
+        target.getExtensions().create("sources", DefaultProjectSourceSet.class);
+
+        DefaultBinaryContainer binaries = target.getExtensions().create("binaries", DefaultBinaryContainer.class, instantiator);
+        applyRules(modelRegistry, binaries);
+    }
+
+    private static void applyRules(ModelRegistry modelRegistry, DefaultBinaryContainer binaries) {
+        final String descriptor = LanguageBasePlugin.class.getName() + ".apply()";
+        final ModelRuleDescriptor ruleDescriptor = new SimpleModelRuleDescriptor(descriptor);
+        ModelPath binariesPath = ModelPath.path("binaries");
+        BridgedCollections.dynamicTypes(
+                modelRegistry,
+                binariesPath,
+                descriptor,
+                ModelType.of(DefaultBinaryContainer.class),
+                ModelType.of(DefaultBinaryContainer.class),
+                ModelType.of(BinarySpec.class),
+                binaries,
+                Named.Namer.INSTANCE,
+                BridgedCollections.itemDescriptor(descriptor)
+        );
+
+        modelRegistry.configure(ModelActionRole.Defaults, DirectNodeModelAction.of(ModelReference.of(binariesPath), ruleDescriptor, new Action<MutableModelNode>() {
+            @Override
+            public void execute(MutableModelNode binariesNode) {
+                binariesNode.applyToAllLinks(ModelActionRole.Finalize, BiActionBackedModelAction.single(ModelReference.of(BinarySpec.class), ruleDescriptor, ModelReference.of(ITaskFactory.class), new BiAction<BinarySpec, ITaskFactory>() {
+                    @Override
+                    public void execute(BinarySpec binary, ITaskFactory taskFactory) {
+                        if (!((BinarySpecInternal) binary).isLegacyBinary()) {
+                            TaskInternal binaryLifecycleTask = taskFactory.create(binary.getName(), DefaultTask.class);
+                            binaryLifecycleTask.setGroup(LifecycleBasePlugin.BUILD_GROUP);
+                            binaryLifecycleTask.setDescription(String.format("Assembles %s.", binary));
+                            binary.setBuildTask(binaryLifecycleTask);
+                        }
+                    }
+                }));
+
+                binariesNode.applyToAllLinks(ModelActionRole.Initialize, DirectNodeModelAction.of(ModelReference.of(BinarySpec.class), new SimpleModelRuleDescriptor(descriptor + ".tasks"), new Action<MutableModelNode>() {
+                    @Override
+                    public void execute(MutableModelNode modelNode) {
+                        ModelPath binaryPath = modelNode.getPath();
+                        ModelPath taskNodePath = binaryPath.child("__tasks");
+                        ModelType<Collection<Task>> taskCollectionType = ModelTypes.collectionOf(Task.class);
+                        ModelReference<Collection<Task>> tasksNodeReference = ModelReference.of(taskNodePath, taskCollectionType);
+                        modelNode.addLink(ModelCreators.of(tasksNodeReference, BiActions.doNothing())
+                                        .withProjection(new UnmanagedModelProjection<Collection<Task>>(taskCollectionType))
+                                        .descriptor(descriptor + ".createTasksNode")
+                                        .build()
+                        );
+                        MutableModelNode link = modelNode.getLink(taskNodePath.getName());
+                        assert link != null;
+                        link.setPrivateData(taskCollectionType, modelNode.getPrivateData(ModelType.of(BinarySpec.class)).getTasks());
+                    }
+                }));
+            }
+        }));
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+
+        @Model
+        ProjectSourceSet sources(ExtensionContainer extensions) {
+            return extensions.getByType(ProjectSourceSet.class);
+        }
+
+        @Mutate
+        void copyBinaryTasksToTaskContainer(TaskContainer tasks, BinaryContainer binaries) {
+            for (BinarySpec binary : binaries) {
+                tasks.addAll(binary.getTasks());
+                Task buildTask = binary.getBuildTask();
+                if (buildTask != null) {
+                    tasks.add(buildTask);
+                }
+            }
+        }
+
+        @Mutate
+        void attachBinariesToAssembleLifecycle(@Path("tasks.assemble") Task assemble, BinaryContainer binaries) {
+            List<BinarySpecInternal> notBuildable = Lists.newArrayList();
+            boolean hasBuildableBinaries = false;
+            for (BinarySpecInternal binary : binaries.withType(BinarySpecInternal.class)) {
+                if (!binary.isLegacyBinary()) {
+                    if (binary.isBuildable()) {
+                        assemble.dependsOn(binary);
+                        hasBuildableBinaries = true;
+                    } else {
+                        notBuildable.add(binary);
+                    }
+                }
+            }
+            if (!hasBuildableBinaries && !notBuildable.isEmpty()) {
+                assemble.doFirst(new CheckForNotBuildableBinariesAction(notBuildable));
+            }
+        }
+
+        private static class CheckForNotBuildableBinariesAction implements Action<Task> {
+            private final List<BinarySpecInternal> notBuildable;
+
+            public CheckForNotBuildableBinariesAction(List<BinarySpecInternal> notBuildable) {
+                this.notBuildable = notBuildable;
+            }
+
+            @Override
+            public void execute(Task task) {
+                Set<? extends Task> taskDependencies = task.getTaskDependencies().getDependencies(task);
+
+                if (taskDependencies.isEmpty()) {
+                    TreeFormatter formatter = new TreeFormatter();
+                    formatter.node("No buildable binaries found");
+                    formatter.startChildren();
+                    for (BinarySpecInternal binary : notBuildable) {
+                        formatter.node(binary.getName());
+                        formatter.startChildren();
+                        binary.getBuildAbility().explain(formatter);
+                        formatter.endChildren();
+                    }
+                    formatter.endChildren();
+                    throw new GradleException(formatter.toString());
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LifecycleBasePlugin.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LifecycleBasePlugin.java
new file mode 100644
index 0000000..0aa0580
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LifecycleBasePlugin.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.tasks.Delete;
+import org.gradle.language.base.internal.plugins.CleanRule;
+import org.gradle.util.DeprecationLogger;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * <p>A {@link org.gradle.api.Plugin} which defines a basic project lifecycle.</p>
+ */
+ at Incubating
+public class LifecycleBasePlugin implements Plugin<ProjectInternal> {
+    public static final String CLEAN_TASK_NAME = "clean";
+    public static final String ASSEMBLE_TASK_NAME = "assemble";
+    public static final String CHECK_TASK_NAME = "check";
+    public static final String BUILD_TASK_NAME = "build";
+    public static final String BUILD_GROUP = "build";
+    public static final String VERIFICATION_GROUP = "verification";
+
+    private static final String CUSTOM_LIFECYCLE_TASK_DEPRECATION_MSG = "Defining custom '%s' task when using the standard Gradle lifecycle plugins";
+    private static final List<String> PLACEHOLDER_TASKS = Arrays.asList(BUILD_TASK_NAME, CHECK_TASK_NAME, CLEAN_TASK_NAME, ASSEMBLE_TASK_NAME);
+
+    public void apply(ProjectInternal project) {
+        addClean(project);
+        addCleanRule(project);
+        addAssemble(project);
+        addCheck(project);
+        addBuild(project);
+        addDeprecationWarningsAboutCustomLifecycleTasks(project);
+    }
+
+    private void addClean(final ProjectInternal project) {
+        project.getTasks().addPlaceholderAction(CLEAN_TASK_NAME, Delete.class, new Action<Delete>() {
+            @Override
+            public void execute(Delete clean) {
+                clean.setDescription("Deletes the build directory.");
+                clean.setGroup(VERIFICATION_GROUP);
+                clean.delete(new Callable<File>() {
+                    public File call() throws Exception {
+                        return project.getBuildDir();
+                    }
+                });
+            }
+        });
+    }
+
+    private void addCleanRule(Project project) {
+        project.getTasks().addRule(new CleanRule(project.getTasks()));
+    }
+
+    private void addAssemble(ProjectInternal project) {
+        project.getTasks().addPlaceholderAction(ASSEMBLE_TASK_NAME, DefaultTask.class, new Action<TaskInternal>() {
+            @Override
+            public void execute(TaskInternal assembleTask) {
+                assembleTask.setDescription("Assembles the outputs of this project.");
+                assembleTask.setGroup(BUILD_GROUP);
+            }
+        });
+    }
+
+    private void addCheck(final ProjectInternal project) {
+        project.getTasks().addPlaceholderAction(CHECK_TASK_NAME, DefaultTask.class, new Action<TaskInternal>() {
+            @Override
+            public void execute(TaskInternal checkTask) {
+                checkTask.setDescription("Runs all checks.");
+                checkTask.setGroup(VERIFICATION_GROUP);
+            }
+        });
+    }
+
+    private void addBuild(final ProjectInternal project) {
+        project.getTasks().addPlaceholderAction(BUILD_TASK_NAME, DefaultTask.class, new Action<DefaultTask>() {
+            @Override
+            public void execute(DefaultTask buildTask) {
+                buildTask.setDescription("Assembles and tests this project.");
+                buildTask.setGroup(BUILD_GROUP);
+                buildTask.dependsOn(ASSEMBLE_TASK_NAME);
+                buildTask.dependsOn(CHECK_TASK_NAME);
+            }
+        });
+    }
+
+    private void addDeprecationWarningsAboutCustomLifecycleTasks(ProjectInternal project) {
+        project.getTasks().all(new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                if (PLACEHOLDER_TASKS.contains(task.getName())) {
+                    DeprecationLogger.nagUserOfDeprecated(String.format(CUSTOM_LIFECYCLE_TASK_DEPRECATION_MSG, task.getName()));
+                }
+            }
+        });
+    }
+}
diff --git a/subprojects/language-base/src/main/groovy/org/gradle/language/base/plugins/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/package-info.java
similarity index 100%
rename from subprojects/language-base/src/main/groovy/org/gradle/language/base/plugins/package-info.java
rename to subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/package-info.java
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/BaseLanguageSourceSet.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/BaseLanguageSourceSet.java
new file mode 100644
index 0000000..dc0dfbf
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/BaseLanguageSourceSet.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.sources;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.ObjectInstantiationException;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.platform.base.ModelInstantiationException;
+
+/**
+ * Base class for custom language sourceset implementations. A custom implementation of {@link org.gradle.language.base.LanguageSourceSet} must extend this type.
+ */
+public abstract class BaseLanguageSourceSet extends AbstractBuildableModelElement implements LanguageSourceSetInternal {
+    private String name;
+    private String fullName;
+    private String parentName;
+    private String typeName;
+    private SourceDirectorySet source;
+    private boolean generated;
+    private Task generatorTask;
+
+    // TODO:DAZ This is only here as a convenience for subclasses to create additional SourceDirectorySets
+    protected FileResolver fileResolver;
+
+    public String getName() {
+        return name;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    @Override
+    public void builtBy(Object... tasks) {
+        generated = true;
+        super.builtBy(tasks);
+    }
+
+    public void generatedBy(Task generatorTask) {
+        this.generatorTask = generatorTask;
+    }
+
+    public Task getGeneratorTask() {
+        return generatorTask;
+    }
+
+    public boolean getMayHaveSources() {
+        // TODO:DAZ This doesn't take into account build dependencies of the SourceDirectorySet.
+        // Should just ditch SourceDirectorySet from here since it's not really a great model, and drags in too much baggage.
+        return generated || !source.isEmpty();
+    }
+
+    protected String getTypeName() {
+        return typeName;
+    }
+
+    public String getDisplayName() {
+        return String.format("%s '%s:%s'", getTypeName(), parentName, getName());
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public void source(Action<? super SourceDirectorySet> config) {
+        config.execute(getSource());
+    }
+
+    public SourceDirectorySet getSource() {
+        return source;
+    }
+
+    private static ThreadLocal<SourceSetInfo> nextSourceSetInfo = new ThreadLocal<SourceSetInfo>();
+
+    public static <T extends BaseLanguageSourceSet> T create(Class<T> type, String name, String parentName, FileResolver fileResolver, Instantiator instantiator) {
+        if (type.equals(BaseLanguageSourceSet.class)) {
+            throw new ModelInstantiationException("Cannot create instance of abstract class BaseLanguageSourceSet.");
+        }
+        nextSourceSetInfo.set(new SourceSetInfo(name, parentName, type.getSimpleName(), fileResolver));
+        try {
+            try {
+                return instantiator.newInstance(type);
+            } catch (ObjectInstantiationException e) {
+                throw new ModelInstantiationException(String.format("Could not create LanguageSourceSet of type %s", type.getSimpleName()), e.getCause());
+            }
+        } finally {
+            nextSourceSetInfo.set(null);
+        }
+    }
+
+    protected BaseLanguageSourceSet() {
+        this(nextSourceSetInfo.get());
+    }
+
+    private BaseLanguageSourceSet(SourceSetInfo info) {
+        if (info == null) {
+            throw new ModelInstantiationException("Direct instantiation of a BaseLanguageSourceSet is not permitted. Use a LanguageTypeBuilder instead.");
+        }
+        this.name = info.name;
+        this.parentName = info.parentName;
+        this.typeName = info.typeName;
+        this.fullName = info.parentName + StringUtils.capitalize(name);
+        this.source = new DefaultSourceDirectorySet("source", info.fileResolver);
+        this.fileResolver = info.fileResolver;
+        super.builtBy(source.getBuildDependencies());
+    }
+
+    private static class SourceSetInfo {
+        final String name;
+        final String parentName;
+        final String typeName;
+        final FileResolver fileResolver;
+
+        private SourceSetInfo(String name, String parentName, String typeName, FileResolver fileResolver) {
+            this.name = name;
+            this.parentName = parentName;
+            this.typeName = typeName;
+            this.fileResolver = fileResolver;
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/package-info.java
new file mode 100644
index 0000000..9ae441f
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/sources/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * General purpose types for language sources support.
+ */
+ at Incubating
+package org.gradle.language.base.sources;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/Application.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Application.java
new file mode 100644
index 0000000..c8b4ec0
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Application.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Component;
+
+/**
+ * A software application.
+ */
+ at Incubating
+public interface Application extends Component {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationBinarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationBinarySpec.java
new file mode 100644
index 0000000..5a64ccf
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationBinarySpec.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A binary produced from a `{@link org.gradle.platform.base.ApplicationSpec}`.
+ * */
+ at Incubating
+public interface ApplicationBinarySpec extends BinarySpec {
+    /**
+     * The application that this binary belongs to.
+     */
+    ApplicationSpec getApplication();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationSpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationSpec.java
new file mode 100644
index 0000000..bb7810b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ApplicationSpec.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A specification of a {@link org.gradle.platform.base.Application}.
+ */
+ at Incubating
+public interface ApplicationSpec extends ComponentSpec {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/Binary.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Binary.java
new file mode 100644
index 0000000..c8f05b7
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Binary.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.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A physical binary artifact, which can run on a particular platform or runtime.
+ */
+ at Incubating
+public interface Binary {
+    /**
+     * Returns a human-consumable display name for this binary.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryContainer.java
new file mode 100644
index 0000000..092d516
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryContainer.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.platform.base;
+
+import org.gradle.api.*;
+
+/**
+ * A container for project binaries, which represent physical artifacts that can run on a particular platform or runtime.
+ * Added to a project by the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
+ */
+ at Incubating
+public interface BinaryContainer extends ExtensiblePolymorphicDomainObjectContainer<BinarySpec> {}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinarySpec.java
new file mode 100644
index 0000000..e441bd8
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinarySpec.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.*;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a binary artifact that is the result of building a project component.
+ */
+ at Incubating @HasInternalProtocol
+public interface BinarySpec extends BuildableModelElement, Named {
+    /**
+     * Returns a human-consumable display name for this binary.
+     */
+    String getDisplayName();
+
+    /**
+     * Can this binary be built in the current environment?
+     */
+    boolean isBuildable();
+
+    /**
+     * The source sets used to compile this binary.
+     */
+    DomainObjectSet<LanguageSourceSet> getSource();
+
+    /**
+     * Adds one or more {@link org.gradle.language.base.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 org.gradle.language.base.LanguageSourceSet}</li>
+     *     <li>A Collection of {@link org.gradle.language.base.LanguageSourceSet}s</li>
+     * </ul>
+     */
+    // TODO:DAZ Remove this
+    void source(Object source);
+
+    /**
+     * Configures the source sets used to build this binary.
+     */
+    void sources(Action<? super PolymorphicDomainObjectContainer<LanguageSourceSet>> action);
+
+    /**
+     * The set of tasks associated with this binary.
+     */
+    BinaryTasksCollection getTasks();
+
+    /**
+     * Configures the tasks that build this binary.
+     */
+    void tasks(Action<? super BinaryTasksCollection> action);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasks.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasks.java
new file mode 100644
index 0000000..638b88e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasks.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares the tasks to build a custom {@link org.gradle.platform.base.BinarySpec} binary.
+ *
+ * The following example demonstrates how to register multiple tasks for custom binary using a plugin with a
+ * {@link org.gradle.platform.base.BinaryTasks} annotation.
+ *
+ * <pre autoTested='true'>
+ * import org.gradle.model.*
+ * import org.gradle.model.collection.*
+ *
+ * interface SampleComponent extends ComponentSpec {}
+ * interface SampleBinary extends BinarySpec {}
+ * class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {}
+ *
+ * apply plugin: MyCustomBinariesPlugin
+ *
+ * class MyCustomBinaryCreationTask extends DefaultTask {
+ *      {@literal @}TaskAction void build() {
+ *          //building the binary
+ *      }
+ * }
+ *
+ * class MyCustomBinariesPlugin extends RuleSource {
+ *     {@literal @}BinaryType
+ *     void register(BinaryTypeBuilder<SampleBinary> builder) {
+ *         builder.defaultImplementation(DefaultSampleBinary)
+ *     }
+ *
+ *     {@literal @}BinaryTasks
+ *     void createBinaryTasks(CollectionBuilder<Task> tasks, SampleBinary binary) {
+ *         tasks.create("${binary.name}Task1", MyCustomBinaryCreationTask)
+ *         tasks.create("${binary.name}Task2") {
+ *             dependsOn "${binary.name}Task1"
+ *         }
+ *     }
+ * }
+ * </pre>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface BinaryTasks {
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasksCollection.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasksCollection.java
new file mode 100644
index 0000000..1c57c44
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTasksCollection.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Task;
+
+/**
+ * A collection of tasks associated to a binary
+ * */
+public interface BinaryTasksCollection extends DomainObjectSet<Task> {
+    /**
+     * The 'lifecycle' task, that can be used to construct this binary.
+     */
+    Task getBuild();
+
+    <T extends Task> void create(String name, Class<T> type, Action<? super T> config);
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryType.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryType.java
new file mode 100644
index 0000000..8c03de5
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryType.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Declares that a custom {@link org.gradle.platform.base.BinarySpec} type.
+ *
+ * The following example demonstrates how to register a custom component type using a plugin with a
+ * {@link org.gradle.platform.base.BinaryType} annotation.
+ *
+ * <pre autoTested=''>
+ * import org.gradle.model.*
+ * import org.gradle.model.collection.*
+ *
+ * interface SampleBinary extends BinarySpec {}
+ * class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {}
+ *
+ * apply plugin: MySamplePlugin
+ *
+ * class MySamplePlugin extends RuleSource {
+ *     {@literal @}BinaryType
+ *     void defineBinaryType(BinaryTypeBuilder<SampleBinary> builder) {
+ *         builder.defaultImplementation(DefaultSampleBinary)
+ *     }
+ * }
+ * </pre>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface BinaryType {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTypeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTypeBuilder.java
new file mode 100644
index 0000000..294f2e3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/BinaryTypeBuilder.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Allows a plugin to register a custom binary type.
+ * @param <C> The binary type.
+ */
+ at Incubating
+public interface BinaryTypeBuilder<C extends BinarySpec> extends TypeBuilder<C> {}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentBinaries.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentBinaries.java
new file mode 100644
index 0000000..6c92b80
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentBinaries.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares the binaries that should be built for a custom {@link org.gradle.platform.base.ComponentSpec} type.
+ *
+ * The following example demonstrates how to register a binary for a custom component type using a plugin with a
+ * {@link org.gradle.platform.base.ComponentBinaries} annotation.
+ * Furthermore the plugin registers 'DefaultSampleBinary' as implementation for {@link org.gradle.platform.base.BinarySpec}.
+ *
+ * <pre autoTested='true'>
+ * import org.gradle.model.*
+ * import org.gradle.model.collection.*
+ *
+ * interface SampleComponent extends ComponentSpec {}
+ * interface SampleBinary extends BinarySpec {}
+ * class DefaultSampleBinary extends BaseBinarySpec implements SampleBinary {}
+ *
+ * apply plugin: MyCustomBinariesPlugin
+ *
+ * class MyCustomBinariesPlugin extends RuleSource {
+ *     {@literal @}BinaryType
+ *     void register(BinaryTypeBuilder<SampleBinary> builder) {
+ *         builder.defaultImplementation(DefaultSampleBinary)
+ *     }
+ *
+ *     {@literal @}ComponentBinaries
+ *     void createBinariesForSampleLibrary(CollectionBuilder<SampleBinary> binaries, SampleComponent component) {
+ *         binaries.create("${component.name}Binary", SampleBinary)
+ *     }
+ * }
+ * </pre>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface ComponentBinaries {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpec.java
new file mode 100644
index 0000000..2dac062
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpec.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.*;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A software component that is built by a Gradle project.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface ComponentSpec extends Named {
+    /**
+     * The path the the project containing this component.
+     */
+    String getProjectPath();
+
+    /**
+     * Returns a human-consumable display name for this component.
+     */
+    String getDisplayName();
+
+    /**
+     * The source sets that are used to build this component.
+     */
+    DomainObjectSet<LanguageSourceSet> getSource();
+
+    /**
+     * Configures the source sets used to build this component.
+     */
+    void sources(Action<? super PolymorphicDomainObjectContainer<LanguageSourceSet>> action);
+
+    /**
+     * The binaries that are built for this component. You can use this to configure the binaries for this component.
+     */
+    DomainObjectSet<BinarySpec> getBinaries();
+
+    /**
+     * Configures the binaries that are produced for this component.
+     */
+    void binaries(Action<? super DomainObjectSet<BinarySpec>> action);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecContainer.java
new file mode 100644
index 0000000..93c8869
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecContainer.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.platform.base;
+
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Incubating;
+
+/**
+ * A container of software components.
+ * TODO:DAZ Merge with org.gradle.api.component.SoftwareComponentContainer
+ */
+ at Incubating
+public interface ComponentSpecContainer extends ExtensiblePolymorphicDomainObjectContainer<ComponentSpec> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecIdentifier.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecIdentifier.java
new file mode 100644
index 0000000..24ef66e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentSpecIdentifier.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * An identifier for a {@link ComponentSpec}, which has a name.
+ */
+ at Incubating
+public interface ComponentSpecIdentifier extends Named {
+    String getProjectPath();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentType.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentType.java
new file mode 100644
index 0000000..942c92e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentType.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares that a custom {@link org.gradle.platform.base.ComponentSpec} type.
+ *
+ * The following example demonstrates how to register a custom component type using a plugin with a
+ * {@link ComponentType} annotation.
+ * Furthermore the plugin creates an instance of SampleComponent named 'sampleComponent'.
+ *
+ * <pre autoTested='true'>
+ * import org.gradle.model.*
+ * import org.gradle.model.collection.*
+ *
+ * interface SampleComponent extends ComponentSpec {}
+ * class DefaultSampleComponent extends BaseComponentSpec implements SampleComponent {}
+ *
+ * apply plugin: MySamplePlugin
+ *
+ * class MySamplePlugin extends RuleSource {
+ *     {@literal @}ComponentType
+ *     void register(ComponentTypeBuilder<SampleComponent> builder) {
+ *         builder.defaultImplementation(DefaultSampleComponent)
+ *     }
+ *
+ *     {@literal @}Mutate
+ *     void createSampleLibraryComponents(CollectionBuilder<SampleComponent> componentSpecs) {
+ *         componentSpecs.create("sampleComponent")
+ *     }
+ * }
+ * </pre>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface ComponentType {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentTypeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentTypeBuilder.java
new file mode 100644
index 0000000..0f135b5
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ComponentTypeBuilder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+/**
+ * Allows a plugin to register a custom component type.
+ * @param <C> The component type.
+ */
+public interface ComponentTypeBuilder<C extends ComponentSpec> extends TypeBuilder<C> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/InvalidModelException.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/InvalidModelException.java
new file mode 100644
index 0000000..ee51a6c
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/InvalidModelException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.GradleException;
+
+/**
+ * Thrown when a component model is declared in an invalid way.
+ */
+public class InvalidModelException extends GradleException {
+    public InvalidModelException(String message) {
+        super(message);
+    }
+
+    public InvalidModelException(String message, Exception cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageType.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageType.java
new file mode 100644
index 0000000..fa96c2c
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The following example demonstrates how to register a custom language type using a plugin with a
+ * {@link LanguageType} annotation.
+ *
+ * <pre autoTested=''>
+ * import org.gradle.model.*
+ * import org.gradle.model.collection.*
+ *
+ * interface CustomLanguageSourceSet extends LanguageSourceSet {}
+ * class DefaultCustomLanguageSourceSet extends BaseLanguageSourceSet implements CustomLanguageSourceSet {}
+ *
+ * apply plugin: MySamplePlugin
+ *
+ * class MySamplePlugin extends RuleSource {
+ *     {@literal @}LanguageType
+ *     void declareCustomLanguage(LanguageTypeBuilder<CustomLanguageSourceSet> builder) {
+ *         builder.setLanguageName("custom")
+ *         builder.defaultImplementation(DefaultCustomLanguageSourceSet)
+ *     }
+ * }
+ * </pre>
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Incubating
+public @interface LanguageType {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageTypeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageTypeBuilder.java
new file mode 100644
index 0000000..8550f91
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LanguageTypeBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Allows a plugin to register a custom language.
+ * @param <T> The custom language sourceset type.
+ */
+ at Incubating
+public interface LanguageTypeBuilder<T extends LanguageSourceSet> extends TypeBuilder<T> {
+    void setLanguageName(String languageName);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/Library.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Library.java
new file mode 100644
index 0000000..fe92a9b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Library.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.component.Component;
+
+/**
+ * A library that can be linked into or depended on by another software element.
+ */
+ at Incubating
+public interface Library extends Component {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibraryBinarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibraryBinarySpec.java
new file mode 100644
index 0000000..1116157
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibraryBinarySpec.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A binary produced from a `{@link org.gradle.platform.base.LibrarySpec}`.
+ * */
+ at Incubating
+ public interface LibraryBinarySpec extends BinarySpec {
+    /**
+     * The library that this binary belongs to.
+     */
+    LibrarySpec getLibrary();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibrarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibrarySpec.java
new file mode 100644
index 0000000..4bdbcc5
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/LibrarySpec.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A specification of a {@link org.gradle.platform.base.Library}.
+ */
+ at Incubating
+public interface LibrarySpec extends ComponentSpec {
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ModelInstantiationException.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ModelInstantiationException.java
new file mode 100644
index 0000000..773d85c
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ModelInstantiationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.GradleException;
+
+/**
+ * Thrown when a model element cannot be instantiated for some reason.
+ */
+public class ModelInstantiationException extends GradleException {
+    public ModelInstantiationException(String message) {
+        this(message, null);
+    }
+
+    public ModelInstantiationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/Platform.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Platform.java
new file mode 100644
index 0000000..b631557
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/Platform.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * The platform or runtime that a binary is designed to run on.
+ *
+ * Examples: the JvmPlatform defines a java runtime, while the NativePlatform defines the Operating System and Architecture for a native app.
+ */
+ at Incubating
+public interface Platform extends Named {
+
+    /**
+     * Returns a human consumable name for this platform.
+     *
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformAwareComponentSpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformAwareComponentSpec.java
new file mode 100644
index 0000000..fb29d29
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformAwareComponentSpec.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * Defines Platform specific operations for ComponentSpecs
+ */
+ at Incubating @HasInternalProtocol
+public interface PlatformAwareComponentSpec extends ComponentSpec {
+    /**
+     * Specifies a platform that this component should be built be for.
+     */
+    public void targetPlatform(String targetPlatform);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformContainer.java
new file mode 100644
index 0000000..525fcc9
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/PlatformContainer.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.platform.base;
+
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Incubating;
+
+/**
+ * A container of {@link Platform}s.
+ */
+ at Incubating
+public interface PlatformContainer extends ExtensiblePolymorphicDomainObjectContainer<Platform> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChain.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChain.java
new file mode 100644
index 0000000..87a12d9
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChain.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * A set of compilers that are used together to construct binaries.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface ToolChain extends Named {
+    /**
+     * Returns a human consumable name for this tool chain.
+     *
+     * @since 1.11
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChainRegistry.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChainRegistry.java
new file mode 100644
index 0000000..ac3a2ef
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/ToolChainRegistry.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+/**
+ * A Provider of {@link ToolChain}s.
+ * @param <P>
+ * @param <T>
+ */
+public interface ToolChainRegistry<P extends Platform, T extends ToolChain> {
+    /**
+     * Returns the best tool chain to build for the target platform.
+     */
+    T getForPlatform(P targetPlatform);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/TransformationFileType.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/TransformationFileType.java
new file mode 100644
index 0000000..3488f72
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/TransformationFileType.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The type of a file of a transformation
+ * */
+ at Incubating
+public interface TransformationFileType {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/TypeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/TypeBuilder.java
new file mode 100644
index 0000000..b99195f
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/TypeBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * A TypeBuilder to configure read the implementation class of a type.
+ * @param <C> The component type.
+ * */
+ at Incubating
+ at HasInternalProtocol
+public interface TypeBuilder<C> {
+
+    /**
+     * Allows the plugin to register the implementation type.
+     * @param implementation the implementation class.
+     */
+    TypeBuilder<C> defaultImplementation(Class<? extends C> implementation);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/BaseBinarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/BaseBinarySpec.java
new file mode 100644
index 0000000..e36ed46
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/BaseBinarySpec.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.binary;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.ObjectInstantiationException;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetContainer;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.ModelInstantiationException;
+import org.gradle.platform.base.internal.BinaryBuildAbility;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.platform.base.internal.FixedBuildAbility;
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection;
+
+/**
+ * Base class for custom binary implementations.
+ * A custom implementation of {@link org.gradle.platform.base.BinarySpec} must extend this type.
+ *
+ * TODO at the moment leaking BinarySpecInternal here to generate lifecycleTask in
+ * LanguageBasePlugin$createLifecycleTaskForBinary#createLifecycleTaskForBinary rule
+ *
+ */
+ at Incubating
+public abstract class BaseBinarySpec extends AbstractBuildableModelElement implements BinarySpecInternal {
+    private final LanguageSourceSetContainer sourceSets = new LanguageSourceSetContainer();
+
+    private static ThreadLocal<BinaryInfo> nextBinaryInfo = new ThreadLocal<BinaryInfo>();
+    private final BinaryTasksCollection tasks;
+
+    private final String name;
+    private final String typeName;
+
+    private boolean disabled;
+
+    public static <T extends BaseBinarySpec> T create(Class<T> type, String name, Instantiator instantiator, ITaskFactory taskFactory) {
+        if (type.equals(BaseBinarySpec.class)) {
+            throw new ModelInstantiationException("Cannot create instance of abstract class BaseBinarySpec.");
+        }
+        nextBinaryInfo.set(new BinaryInfo(name, type.getSimpleName(), taskFactory, instantiator));
+        try {
+            try {
+                return instantiator.newInstance(type);
+            } catch (ObjectInstantiationException e) {
+                throw new ModelInstantiationException(String.format("Could not create binary of type %s", type.getSimpleName()), e.getCause());
+            }
+        } finally {
+            nextBinaryInfo.set(null);
+        }
+    }
+
+    protected BaseBinarySpec() {
+        this(nextBinaryInfo.get());
+    }
+
+    private BaseBinarySpec(BinaryInfo info) {
+        if (info == null) {
+            throw new ModelInstantiationException("Direct instantiation of a BaseBinarySpec is not permitted. Use a BinaryTypeBuilder instead.");
+        }
+        this.name = info.name;
+        this.typeName = info.typeName;
+        this.tasks = info.instantiator.newInstance(DefaultBinaryTasksCollection.class, this, info.taskFactory);
+    }
+
+    protected String getTypeName() {
+        return typeName;
+    }
+
+    public String getDisplayName() {
+        return String.format("%s '%s'", getTypeName(), getName());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setBuildable(boolean buildable) {
+        this.disabled = !buildable;
+    }
+
+    public final boolean isBuildable() {
+        return getBuildAbility().isBuildable();
+    }
+
+    public FunctionalSourceSet getBinarySources() {
+        return sourceSets.getMainSources();
+    }
+
+    public void setBinarySources(FunctionalSourceSet sources) {
+        sourceSets.setMainSources(sources);
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSource() {
+        return sourceSets.getSources();
+    }
+
+    public void sources(Action<? super PolymorphicDomainObjectContainer<LanguageSourceSet>> action) {
+        action.execute(sourceSets.getMainSources());
+    }
+
+    // TODO:DAZ Remove this
+    public void source(Object source) {
+        sourceSets.source(source);
+    }
+
+    public BinaryTasksCollection getTasks() {
+        return tasks;
+    }
+
+    @Override
+    public void tasks(Action<? super BinaryTasksCollection> action) {
+        action.execute(tasks);
+    }
+
+    public boolean isLegacyBinary() {
+        return false;
+    }
+
+    private static class BinaryInfo {
+        private final String name;
+        private final String typeName;
+        private final ITaskFactory taskFactory;
+        private final Instantiator instantiator;
+
+        private BinaryInfo(String name, String typeName, ITaskFactory taskFactory, Instantiator instantiator) {
+            this.name = name;
+            this.typeName = typeName;
+            this.taskFactory = taskFactory;
+            this.instantiator = instantiator;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    @Override
+    public final BinaryBuildAbility getBuildAbility() {
+        if (disabled) {
+            return new FixedBuildAbility(false);
+        }
+        return getBinaryBuildAbility();
+    }
+
+    protected BinaryBuildAbility getBinaryBuildAbility() {
+        // Default behavior is to always be buildable.  Binary implementations should define what
+        // criteria make them buildable or not.
+        return new FixedBuildAbility(true);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/package-info.java
new file mode 100644
index 0000000..832260d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/binary/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * General purpose types for binary support.
+ */
+ at Incubating
+package org.gradle.platform.base.binary;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/BaseComponentSpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/BaseComponentSpec.java
new file mode 100644
index 0000000..aa488b3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/BaseComponentSpec.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.component;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.ObjectInstantiationException;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.*;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Base class for custom component implementations.
+ * A custom implementation of {@link ComponentSpec} must extend this type.
+ */
+ at Incubating
+public abstract class BaseComponentSpec implements ComponentSpecInternal {
+    private static ThreadLocal<ComponentInfo> nextComponentInfo = new ThreadLocal<ComponentInfo>();
+    private final FunctionalSourceSet mainSourceSet;
+
+    private final ComponentSpecIdentifier identifier;
+    private final String typeName;
+    private final DomainObjectSet<BinarySpec> binaries = new DefaultDomainObjectSet<BinarySpec>(BinarySpec.class);
+
+    public static <T extends BaseComponentSpec> T create(Class<T> type, ComponentSpecIdentifier identifier, FunctionalSourceSet mainSourceSet, Instantiator instantiator) {
+        if (type.equals(BaseComponentSpec.class)) {
+            throw new ModelInstantiationException("Cannot create instance of abstract class BaseComponentSpec.");
+        }
+        nextComponentInfo.set(new ComponentInfo(identifier, type.getSimpleName(), mainSourceSet));
+        try {
+            try {
+                return instantiator.newInstance(type);
+            } catch (ObjectInstantiationException e) {
+                throw new ModelInstantiationException(String.format("Could not create component of type %s", type.getSimpleName()), e.getCause());
+            }
+        } finally {
+            nextComponentInfo.set(null);
+        }
+    }
+
+    protected BaseComponentSpec() {
+        this(nextComponentInfo.get());
+    }
+
+    private BaseComponentSpec(ComponentInfo info) {
+        if (info == null) {
+            throw new ModelInstantiationException("Direct instantiation of a BaseComponentSpec is not permitted. Use a ComponentTypeBuilder instead.");
+        }
+
+        this.identifier = info.componentIdentifier;
+        this.typeName = info.typeName;
+        this.mainSourceSet = info.sourceSets;
+    }
+
+    public String getName() {
+        return identifier.getName();
+    }
+
+    public String getProjectPath() {
+        return identifier.getProjectPath();
+    }
+
+    protected String getTypeName() {
+        return typeName;
+    }
+
+    public String getDisplayName() {
+        return String.format("%s '%s'", getTypeName(), getName());
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSource() {
+        return new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class, mainSourceSet);
+    }
+
+    public DomainObjectSet<BinarySpec> getBinaries() {
+        return binaries;
+    }
+
+    @Override
+    public void binaries(Action<? super DomainObjectSet<BinarySpec>> action) {
+        action.execute(binaries);
+    }
+
+    public FunctionalSourceSet getSources() {
+        return mainSourceSet;
+    }
+
+    public void sources(Action<? super PolymorphicDomainObjectContainer<LanguageSourceSet>> action) {
+        action.execute(mainSourceSet);
+    }
+
+    public Set<Class<? extends TransformationFileType>> getInputTypes() {
+        return Collections.emptySet();
+    }
+
+    private static class ComponentInfo {
+        final ComponentSpecIdentifier componentIdentifier;
+        final String typeName;
+        final FunctionalSourceSet sourceSets;
+
+        private ComponentInfo(ComponentSpecIdentifier componentIdentifier,
+                              String typeName,
+                              FunctionalSourceSet sourceSets) {
+            this.componentIdentifier = componentIdentifier;
+            this.typeName = typeName;
+            this.sourceSets = sourceSets;
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/package-info.java
new file mode 100644
index 0000000..dd4fd82
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/component/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * General purpose types for library support.
+ */
+ at Incubating
+package org.gradle.platform.base.component;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryBuildAbility.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryBuildAbility.java
new file mode 100644
index 0000000..a8e1b7b
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryBuildAbility.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.util.TreeVisitor;
+
+public interface BinaryBuildAbility {
+    boolean isBuildable();
+    void explain(TreeVisitor<? super String> visitor);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingScheme.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingScheme.java
new file mode 100644
index 0000000..78ad0d1
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingScheme.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.platform.base.internal;
+
+import org.gradle.api.Nullable;
+
+import java.util.List;
+
+// TODO:DAZ Split data from behaviour
+// data: fullName + component parts [typeName, dimensions, baseName?]
+// Can determine baseName
+
+// behaviour: composing these
+public interface BinaryNamingScheme {
+    String getBaseName();
+
+    String getLifecycleTaskName();
+
+    String getTaskName(@Nullable String verb);
+
+    String getTaskName(@Nullable String verb, @Nullable String target);
+
+    String getOutputDirectoryBase();
+
+    String getDescription();
+
+    List<String> getVariantDimensions();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingSchemeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingSchemeBuilder.java
new file mode 100644
index 0000000..df916e8
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryNamingSchemeBuilder.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.platform.base.internal;
+
+public interface BinaryNamingSchemeBuilder {
+    BinaryNamingSchemeBuilder withComponentName(String name);
+
+    BinaryNamingSchemeBuilder withTypeString(String newTypeString);
+
+    BinaryNamingSchemeBuilder withVariantDimension(String dimension);
+
+    BinaryNamingScheme build();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinarySpecInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinarySpecInternal.java
new file mode 100644
index 0000000..908ff49
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinarySpecInternal.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.platform.base.BinarySpec;
+
+public interface BinarySpecInternal extends BinarySpec {
+    FunctionalSourceSet getBinarySources();
+
+    void setBinarySources(FunctionalSourceSet sources);
+
+    void setBuildable(boolean buildable);
+
+    BinaryBuildAbility getBuildAbility();
+
+    boolean isLegacyBinary();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java
new file mode 100644
index 0000000..ed3e797
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import groovy.lang.Closure;
+import org.gradle.api.*;
+import org.gradle.api.specs.Spec;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+ at SuppressWarnings("rawtypes")
+public class BinaryTasksCollectionWrapper implements BinaryTasksCollection {
+
+    private final BinaryTasksCollection delegate;
+
+    public BinaryTasksCollectionWrapper(BinaryTasksCollection delegate) {
+        this.delegate = delegate;
+    }
+
+    public <T extends Task> T findSingleTaskWithType(Class<T> type) {
+        DomainObjectSet<T> tasks = withType(type);
+        if (tasks.size() == 0) {
+            return null;
+        }
+        if (tasks.size() > 1) {
+            throw new UnknownDomainObjectException(String.format("Multiple tasks with type '%s' found.", type.getSimpleName()));
+        }
+        return tasks.iterator().next();
+    }
+
+    @Override
+    public Task getBuild() {
+        return delegate.getBuild();
+    }
+
+    @Override
+    public <T extends Task> void create(String name, Class<T> type, Action<? super T> config) {
+        delegate.create(name, type, config);
+    }
+
+    @Override
+    public <S extends Task> DomainObjectSet<S> withType(Class<S> type) {
+        return delegate.withType(type);
+    }
+
+    @Override
+    public DomainObjectSet<Task> matching(Spec<? super Task> spec) {
+        return delegate.matching(spec);
+    }
+
+    @Override
+    public DomainObjectSet<Task> matching(Closure spec) {
+        return delegate.matching(spec);
+    }
+
+    @Override
+    public Set<Task> findAll(Closure spec) {
+        return delegate.findAll(spec);
+    }
+
+    @Override
+    public <S extends Task> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
+        return delegate.withType(type, configureAction);
+    }
+
+    @Override
+    public <S extends Task> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
+        return delegate.withType(type, configureClosure);
+    }
+
+    @Override
+    public Action<? super Task> whenObjectAdded(Action<? super Task> action) {
+        return delegate.whenObjectAdded(action);
+    }
+
+    @Override
+    public void whenObjectAdded(Closure action) {
+        delegate.whenObjectAdded(action);
+    }
+
+    @Override
+    public Action<? super Task> whenObjectRemoved(Action<? super Task> action) {
+        return delegate.whenObjectRemoved(action);
+    }
+
+    @Override
+    public void whenObjectRemoved(Closure action) {
+        delegate.whenObjectRemoved(action);
+    }
+
+    @Override
+    public void all(Action<? super Task> action) {
+        delegate.all(action);
+    }
+
+    @Override
+    public void all(Closure action) {
+        delegate.all(action);
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return delegate.contains(o);
+    }
+
+    @Override
+    public Iterator<Task> iterator() {
+        return delegate.iterator();
+    }
+
+    @Override
+    public Object[] toArray() {
+        return delegate.toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return delegate.toArray(a);
+    }
+
+    @Override
+    public boolean add(Task task) {
+        return delegate.add(task);
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        return delegate.remove(o);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        return delegate.containsAll(c);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends Task> c) {
+        return delegate.addAll(c);
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        return delegate.removeAll(c);
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        return delegate.retainAll(c);
+    }
+
+    @Override
+    public void clear() {
+        delegate.clear();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return delegate.equals(o);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ComponentSpecInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ComponentSpecInternal.java
new file mode 100644
index 0000000..ea282ee
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ComponentSpecInternal.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.platform.base.TransformationFileType;
+
+import java.util.Set;
+
+public interface ComponentSpecInternal extends ComponentSpec {
+
+    FunctionalSourceSet getSources();
+
+    Set<Class<? extends TransformationFileType>> getInputTypes();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryContainer.java
new file mode 100644
index 0000000..ffe65c8
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryContainer.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.platform.base.internal;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.internal.rules.RuleAwarePolymorphicDomainObjectContainer;
+
+public class DefaultBinaryContainer extends RuleAwarePolymorphicDomainObjectContainer<BinarySpec> implements BinaryContainer {
+    public DefaultBinaryContainer(Instantiator instantiator) {
+        super(BinarySpec.class, instantiator);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingScheme.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingScheme.java
new file mode 100644
index 0000000..263e26e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingScheme.java
@@ -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.platform.base.internal;
+
+import org.gradle.api.Nullable;
+import org.gradle.util.GUtil;
+
+import java.util.List;
+
+public class DefaultBinaryNamingScheme implements BinaryNamingScheme {
+    final String parentName;
+    final String typeString;
+    final String dimensionPrefix;
+    final List<String> dimensions;
+
+    public DefaultBinaryNamingScheme(String parentName, String typeString, List<String> dimensions) {
+        this.parentName = parentName;
+        this.typeString = typeString;
+        this.dimensions = dimensions;
+        this.dimensionPrefix = createPrefix(dimensions);
+    }
+
+    private String createPrefix(List<String> dimensions) {
+        if (dimensions.isEmpty()) {
+            return "";
+        }
+        return makeName(dimensions.toArray(new String[dimensions.size()]));
+    }
+
+    public String getBaseName() {
+        return parentName;
+    }
+
+    public String getLifecycleTaskName() {
+        return getTaskName(null, null);
+    }
+
+    public String getOutputDirectoryBase() {
+        StringBuilder builder = new StringBuilder(makeName(parentName, typeString));
+        if (dimensionPrefix.length() > 0) {
+            builder.append('/');
+            builder.append(dimensionPrefix);
+        }
+        return builder.toString();
+    }
+
+    public String getDescription() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(GUtil.toWords(typeString));
+        builder.append(" '");
+        builder.append(parentName);
+        for (String dimension : dimensions) {
+            builder.append(':');
+            builder.append(dimension);
+        }
+        builder.append(':');
+        appendUncapitalized(builder, typeString);
+        builder.append("'");
+        return builder.toString();
+    }
+
+    public List<String> getVariantDimensions() {
+        return dimensions;
+    }
+
+    public String getTaskName(@Nullable String verb) {
+        return getTaskName(verb, null);
+    }
+
+    public String getTaskName(@Nullable String verb, @Nullable String target) {
+        return makeName(verb, dimensionPrefix, parentName, typeString, target);
+    }
+
+    public String makeName(String... words) {
+        StringBuilder builder = new StringBuilder();
+        for (String word : words) {
+            if (word == null || word.length() == 0) {
+                continue;
+            }
+            if (builder.length() == 0) {
+                appendUncapitalized(builder, word);
+            } else {
+                appendCapitalized(builder, word);
+            }
+        }
+        return builder.toString();
+    }
+
+    private void appendCapitalized(StringBuilder builder, String word) {
+        if (word.length() == 0) {
+            return;
+        }
+        builder.append(Character.toTitleCase(word.charAt(0))).append(word.substring(1));
+    }
+
+    private void appendUncapitalized(StringBuilder builder, String word) {
+        if (word.length() == 0) {
+            return;
+        }
+        builder.append(Character.toLowerCase(word.charAt(0))).append(word.substring(1));
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeBuilder.java
new file mode 100644
index 0000000..d782072
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeBuilder.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.platform.base.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultBinaryNamingSchemeBuilder implements BinaryNamingSchemeBuilder {
+    private final String parentName;
+    private final String typeString;
+    private final List<String> dimensions;
+
+    public DefaultBinaryNamingSchemeBuilder() {
+        this.parentName = null;
+        this.typeString = "";
+        this.dimensions = new ArrayList<String>();
+    }
+
+    public DefaultBinaryNamingSchemeBuilder(BinaryNamingScheme basis) {
+        assert basis instanceof DefaultBinaryNamingScheme;
+        DefaultBinaryNamingScheme clone = (DefaultBinaryNamingScheme) basis;
+        this.parentName = clone.parentName;
+        this.typeString = clone.typeString;
+        this.dimensions = clone.dimensions;
+    }
+
+    private DefaultBinaryNamingSchemeBuilder(String parentName, String typeString, List<String> dimensions) {
+        this.parentName = parentName;
+        this.typeString = typeString;
+        this.dimensions = dimensions;
+    }
+
+    public BinaryNamingSchemeBuilder withComponentName(String name) {
+        return new DefaultBinaryNamingSchemeBuilder(name, typeString, dimensions);
+    }
+
+    public BinaryNamingSchemeBuilder withTypeString(String newTypeString) {
+        return new DefaultBinaryNamingSchemeBuilder(parentName, newTypeString, dimensions);
+    }
+
+    public BinaryNamingSchemeBuilder withVariantDimension(String dimension) {
+        List<String> newDimensions = new ArrayList<String>(dimensions);
+        newDimensions.add(dimension);
+        return new DefaultBinaryNamingSchemeBuilder(parentName, typeString, newDimensions);
+    }
+
+    public BinaryNamingScheme build() {
+        return new DefaultBinaryNamingScheme(parentName, typeString, dimensions);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryTasksCollection.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryTasksCollection.java
new file mode 100644
index 0000000..006ae53
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultBinaryTasksCollection.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Task;
+import org.gradle.api.UnknownDomainObjectException;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+public class DefaultBinaryTasksCollection extends DefaultDomainObjectSet<Task> implements BinaryTasksCollection {
+
+    private final BinarySpec binary;
+    private final ITaskFactory taskFactory;
+
+    public DefaultBinaryTasksCollection(BinarySpec binarySpecInternal, ITaskFactory taskFactory) {
+        super(Task.class);
+        this.binary = binarySpecInternal;
+        this.taskFactory = taskFactory;
+    }
+
+    public Task getBuild() {
+        return binary.getBuildTask();
+    }
+
+    public <T extends Task> T findSingleTaskWithType(Class<T> type) {
+        DomainObjectSet<T> tasks = withType(type);
+        if (tasks.size() == 0) {
+            return null;
+        }
+        if (tasks.size() > 1) {
+            throw new UnknownDomainObjectException(String.format("Multiple tasks with type '%s' found.", type.getSimpleName()));
+        }
+        return tasks.iterator().next();
+    }
+
+    @Override
+    public <T extends Task> void create(String name, Class<T> type, Action<? super T> config) {
+        @SuppressWarnings("unchecked") T task = (T) taskFactory.create(name, (Class<TaskInternal>) type);
+        add(task);
+        config.execute(task);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecContainer.java
new file mode 100644
index 0000000..bd31962
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecContainer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.platform.base.internal.rules.RuleAwarePolymorphicDomainObjectContainer;
+
+public class DefaultComponentSpecContainer extends RuleAwarePolymorphicDomainObjectContainer<ComponentSpec> implements ComponentSpecContainer {
+
+    public DefaultComponentSpecContainer(Instantiator instantiator) {
+        super(ComponentSpec.class, instantiator);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecIdentifier.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecIdentifier.java
new file mode 100644
index 0000000..a86bb4d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultComponentSpecIdentifier.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.platform.base.internal;
+
+import org.gradle.platform.base.ComponentSpecIdentifier;
+
+/**
+ * An identifier for a component that is built as part of the current build.
+ */
+public class DefaultComponentSpecIdentifier implements ComponentSpecIdentifier {
+    private final String projectPath;
+    private final String name;
+
+    public DefaultComponentSpecIdentifier(String projectPath, String name) {
+        this.projectPath = projectPath;
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getProjectPath() {
+        return projectPath;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DefaultComponentSpecIdentifier)) {
+            return false;
+        }
+
+        DefaultComponentSpecIdentifier that = (DefaultComponentSpecIdentifier) o;
+        return name.equals(that.name) && projectPath.equals(that.projectPath);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = projectPath.hashCode();
+        result = 31 * result + name.hashCode();
+        return result;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformContainer.java
new file mode 100644
index 0000000..c3e3579
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformContainer.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.platform.base.internal;
+
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.platform.base.Platform;
+import org.gradle.platform.base.PlatformContainer;
+
+public class DefaultPlatformContainer extends DefaultPolymorphicDomainObjectContainer<Platform> implements PlatformContainer {
+
+    public DefaultPlatformContainer(Instantiator instantiator) {
+        super(Platform.class, instantiator);
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformRequirement.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformRequirement.java
new file mode 100644
index 0000000..2f498ae
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformRequirement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+public class DefaultPlatformRequirement implements PlatformRequirement {
+    private final String platformName;
+
+    public static PlatformRequirement create(String name) {
+        return new DefaultPlatformRequirement(name);
+    }
+
+    public DefaultPlatformRequirement(String platformName) {
+        this.platformName = platformName;
+    }
+
+    @Override
+    public String getPlatformName() {
+        return platformName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultPlatformRequirement that = (DefaultPlatformRequirement) o;
+        return platformName.equals(that.platformName);
+
+    }
+
+    @Override
+    public int hashCode() {
+        return platformName.hashCode();
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformResolvers.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformResolvers.java
new file mode 100644
index 0000000..25fdb4e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/DefaultPlatformResolvers.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.platform.base.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.NamedDomainObjectSet;
+import org.gradle.api.specs.Spec;
+import org.gradle.platform.base.Platform;
+import org.gradle.platform.base.PlatformContainer;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+public class DefaultPlatformResolvers implements PlatformResolvers {
+    private final List<PlatformResolver<?>> platformResolvers = Lists.newArrayList();
+    private final PlatformContainer platforms;
+
+    public DefaultPlatformResolvers(PlatformContainer platforms) {
+        this.platforms = platforms;
+    }
+
+    @Override
+    public void register(PlatformResolver<?> platformResolver) {
+        platformResolvers.add(platformResolver);
+    }
+
+    @Override
+    public <T extends Platform> T resolve(Class<T> type, PlatformRequirement platformRequirement) {
+        for (PlatformResolver<?> platformResolver : platformResolvers) {
+            if (platformResolver.getType().equals(type)) {
+                @SuppressWarnings("unchecked") PlatformResolver<T> pr = (PlatformResolver<T>) platformResolver;
+                T resolved = pr.resolve(platformRequirement);
+                if (resolved != null) {
+                    return resolved;
+                }
+            }
+        }
+        return resolveFromContainer(type, platformRequirement);
+    }
+
+    private <T extends Platform> T resolveFromContainer(Class<T> type, PlatformRequirement platformRequirement) {
+        final String target = platformRequirement.getPlatformName();
+
+        NamedDomainObjectSet<T> allWithType = platforms.withType(type);
+        T matching = CollectionUtils.findFirst(allWithType, new Spec<T>() {
+            public boolean isSatisfiedBy(T element) {
+                return element.getName().equals(target);
+            }
+        });
+
+        if (matching == null) {
+            throw new InvalidUserDataException(String.format("Invalid %s: %s", type.getSimpleName(), target));
+        }
+        return matching;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java
new file mode 100644
index 0000000..f9326ab
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.util.TreeVisitor;
+
+public class FixedBuildAbility implements BinaryBuildAbility {
+    private final boolean buildable;
+
+    public FixedBuildAbility(boolean buildable) {
+        this.buildable = buildable;
+    }
+
+    @Override
+    public boolean isBuildable() {
+        return buildable;
+    }
+
+    @Override
+    public void explain(TreeVisitor<? super String> visitor) {
+        if (!buildable) {
+            visitor.node("Disabled by user");
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformAwareComponentSpecInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformAwareComponentSpecInternal.java
new file mode 100644
index 0000000..1dcc164
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformAwareComponentSpecInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.PlatformAwareComponentSpec;
+
+import java.util.List;
+
+/**
+ * Defines Platform specific operations for ComponentSpecs
+ */
+ at Incubating
+public interface PlatformAwareComponentSpecInternal extends PlatformAwareComponentSpec {
+    /**
+     * Get the names of the targeted platforms that this component should be built for.
+     *
+     * @return the list of targeted platforms, may be empty but never null.
+     */
+    List<PlatformRequirement> getTargetPlatforms();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformRequirement.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformRequirement.java
new file mode 100644
index 0000000..b6f6541
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformRequirement.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+public interface PlatformRequirement {
+    String getPlatformName();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolver.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolver.java
new file mode 100644
index 0000000..12d02e6
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolver.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.platform.base.Platform;
+
+public interface PlatformResolver<T extends Platform> {
+    Class<T> getType();
+    T resolve(PlatformRequirement platformRequirement);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolvers.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolvers.java
new file mode 100644
index 0000000..cb66de0
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/PlatformResolvers.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.platform.base.Platform;
+
+public interface PlatformResolvers {
+    void register(PlatformResolver<?> platformResolver);
+    <T extends Platform> T resolve(Class<T> type, PlatformRequirement platformRequirement);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ToolSearchBuildAbility.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ToolSearchBuildAbility.java
new file mode 100644
index 0000000..4d2056e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/ToolSearchBuildAbility.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal;
+
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+import org.gradle.util.TreeVisitor;
+
+public class ToolSearchBuildAbility implements BinaryBuildAbility {
+    final ToolSearchResult result;
+
+    public ToolSearchBuildAbility(ToolSearchResult result) {
+        this.result = result;
+    }
+
+    @Override
+    public boolean isBuildable() {
+        return result.isAvailable();
+    }
+
+    @Override
+    public void explain(TreeVisitor<? super String> visitor) {
+        result.explain(visitor);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/LanguageTypeBuilderInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/LanguageTypeBuilderInternal.java
new file mode 100644
index 0000000..4f70ad9
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/LanguageTypeBuilderInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.builder;
+
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.LanguageTypeBuilder;
+
+public interface LanguageTypeBuilderInternal<T extends LanguageSourceSet> extends LanguageTypeBuilder<T>, TypeBuilderInternal<T> {
+    String getLanguageName();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/TypeBuilderInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/TypeBuilderInternal.java
new file mode 100644
index 0000000..30e6850
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/builder/TypeBuilderInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.builder;
+
+import org.gradle.platform.base.TypeBuilder;
+
+public interface TypeBuilderInternal<T> extends TypeBuilder<T> {
+    Class<? extends T> getDefaultImplementation();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractAnnotationDrivenComponentModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractAnnotationDrivenComponentModelRuleExtractor.java
new file mode 100644
index 0000000..3545476
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractAnnotationDrivenComponentModelRuleExtractor.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.inspect.AbstractAnnotationDrivenModelRuleExtractor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.InvalidModelException;
+
+import java.lang.annotation.Annotation;
+import java.security.InvalidParameterException;
+import java.util.HashMap;
+import java.util.List;
+
+public abstract class AbstractAnnotationDrivenComponentModelRuleExtractor<T extends Annotation> extends AbstractAnnotationDrivenModelRuleExtractor<T> {
+    protected void assertIsVoidMethod(MethodRuleDefinition<?, ?> ruleDefinition) {
+        if (!ModelType.of(Void.TYPE).equals(ruleDefinition.getReturnType())) {
+            throw new InvalidModelException(String.format("Method %s must not have a return value.", getDescription()));
+        }
+    }
+
+    protected <V> void visitCollectionBuilderSubject(RuleMethodDataCollector dataCollector, MethodRuleDefinition<?, ?> ruleDefinition, Class<V> typeParameter) {
+        if (ruleDefinition.getReferences().size() == 0) {
+            throw new InvalidModelException(String.format("Method %s must have a parameter of type '%s'.", getDescription(), CollectionBuilder.class.getName()));
+        }
+
+        @SuppressWarnings("ConstantConditions") ModelType<?> builder = ruleDefinition.getSubjectReference().getType();
+
+        if (!ModelType.of(CollectionBuilder.class).isAssignableFrom(builder)) {
+            throw new InvalidModelException(String.format("Method %s first parameter must be of type '%s'.", getDescription(), CollectionBuilder.class.getName()));
+        }
+        if (builder.getTypeVariables().size() != 1) {
+            throw new InvalidModelException(String.format("Parameter of type '%s' must declare a type parameter extending '%s'.", CollectionBuilder.class.getSimpleName(), typeParameter.getSimpleName()));
+        }
+        ModelType<?> subType = builder.getTypeVariables().get(0);
+
+        if (subType.isWildcard()) {
+            throw new InvalidModelException(String.format("%s type '%s' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.).", typeParameter.getName(), subType.toString()));
+        }
+        dataCollector.parameterTypes.put(typeParameter, subType.getConcreteClass());
+    }
+
+    protected class RuleMethodDataCollector {
+        private HashMap<Class<?>, Class<?>> parameterTypes = new HashMap<Class<?>, Class<?>>();
+
+        @SuppressWarnings("unchecked")
+        public <S, R extends S> Class<R> getParameterType(Class<S> baseClass) {
+            return (Class<R>) parameterTypes.get(baseClass);
+        }
+
+        public <S> void put(Class<S> baseClass, Class<? extends S> concreteClass) {
+            if (!baseClass.isAssignableFrom(concreteClass)) {
+                throw new InvalidParameterException(String.format("Class %s must be assignable from Class %s", baseClass.getName(), concreteClass.getName()));
+            }
+            parameterTypes.put(baseClass, concreteClass);
+        }
+    }
+
+    protected <S> void visitDependency(RuleMethodDataCollector dataCollector, MethodRuleDefinition<?, ?> ruleDefinition, ModelType<S> expectedDependency) {
+        // TODO:DAZ Use ModelType.toString instead of getSimpleName()
+        List<ModelReference<?>> references = ruleDefinition.getReferences();
+        ModelType<? extends S> dependency = null;
+        for (ModelReference<?> reference : references) {
+            ModelType<? extends S> newDependency = expectedDependency.asSubclass(reference.getType());
+            if (newDependency != null) {
+                if (dependency != null) {
+                    throw new InvalidModelException(String.format("Method %s must have one parameter extending %s. Found multiple parameter extending %s.", getDescription(),
+                            expectedDependency.getConcreteClass().getSimpleName(),
+                            expectedDependency.getConcreteClass().getSimpleName()));
+
+                }
+                dependency = newDependency;
+            }
+        }
+
+        if (dependency == null) {
+            throw new InvalidModelException(String.format("Method %s must have one parameter extending %s. Found no parameter extending %s.", getDescription(),
+                    expectedDependency.getConcreteClass().getSimpleName(),
+                    expectedDependency.getConcreteClass().getSimpleName()));
+        }
+        dataCollector.put(expectedDependency.getConcreteClass(), dependency.getConcreteClass());
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractTypeBuilder.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractTypeBuilder.java
new file mode 100644
index 0000000..bceaa7d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/AbstractTypeBuilder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import org.gradle.platform.base.InvalidModelException;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+
+public abstract class AbstractTypeBuilder<T> implements TypeBuilderInternal<T> {
+    private final Class<?> markerAnnotation;
+    Class<? extends T> implementation;
+
+    public AbstractTypeBuilder(Class<?> markerAnnotation){
+        this.markerAnnotation = markerAnnotation;
+    }
+
+    public TypeBuilderInternal<T> defaultImplementation(Class<? extends T> implementation) {
+        if (this.implementation != null) {
+            throw new InvalidModelException(String.format("Method annotated with @%s cannot set default implementation multiple times.", markerAnnotation.getSimpleName()));
+        }
+        this.implementation = implementation;
+        return this;
+    }
+
+    public Class<? extends T> getDefaultImplementation() {
+        return this.implementation;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractor.java
new file mode 100644
index 0000000..d4413ec
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractor.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.Cast;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.model.internal.type.ModelTypes;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.BinaryTasks;
+import org.gradle.platform.base.InvalidModelException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BinaryTasksModelRuleExtractor extends AbstractAnnotationDrivenComponentModelRuleExtractor<BinaryTasks> {
+
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        return createRegistration(ruleDefinition);
+    }
+
+    private <R, S extends BinarySpec> ExtractedModelRule createRegistration(MethodRuleDefinition<R, ?> ruleDefinition) {
+        try {
+            RuleMethodDataCollector dataCollector = new RuleMethodDataCollector();
+            verifyMethodSignature(dataCollector, ruleDefinition);
+
+            final Class<S> binaryType = dataCollector.getParameterType(BinarySpec.class);
+
+            final BinaryTaskRule<R, S> binaryTaskRule = new BinaryTaskRule<R, S>(binaryType, ruleDefinition);
+            return new ExtractedModelAction(ModelActionRole.Defaults, ImmutableList.of(ComponentModelBasePlugin.class), DirectNodeModelAction.of(ModelReference.of("binaries"), new SimpleModelRuleDescriptor("binaries*.create()"), new Action<MutableModelNode>() {
+                @Override
+                public void execute(MutableModelNode modelNode) {
+                    modelNode.applyToAllLinks(ModelActionRole.Finalize, binaryTaskRule);
+                }
+            }));
+        } catch (InvalidModelException e) {
+            throw invalidModelRule(ruleDefinition, e);
+        }
+    }
+
+    private void verifyMethodSignature(RuleMethodDataCollector taskDataCollector, MethodRuleDefinition<?, ?> ruleDefinition) {
+        assertIsVoidMethod(ruleDefinition);
+        visitCollectionBuilderSubject(taskDataCollector, ruleDefinition, Task.class);
+        visitDependency(taskDataCollector, ruleDefinition, ModelType.of(BinarySpec.class));
+    }
+
+    //TODO extract common general method reusable by all AnnotationRuleDefinitionHandler
+    protected InvalidModelRuleDeclarationException invalidModelRule(MethodRuleDefinition<?, ?> ruleDefinition, InvalidModelException e) {
+        StringBuilder sb = new StringBuilder();
+        ruleDefinition.getDescriptor().describeTo(sb);
+        sb.append(" is not a valid BinaryTask model rule method.");
+        return new InvalidModelRuleDeclarationException(sb.toString(), e);
+    }
+
+    private class BinaryTaskRule<R, T extends BinarySpec> extends CollectionBuilderBasedRule<R, Task, T, T> {
+
+        public BinaryTaskRule(Class<T> binaryType, MethodRuleDefinition<R, ?> ruleDefinition) {
+            super(ModelReference.of(binaryType), binaryType, ruleDefinition);
+        }
+
+        public void execute(MutableModelNode modelNode, final T binary, List<ModelView<?>> inputs) {
+            DefaultCollectionBuilder<TaskInternal> collectionBuilder = new DefaultCollectionBuilder<TaskInternal>(
+                    ModelType.of(TaskInternal.class),
+                    getDescriptor(),
+                    modelNode,
+                    DefaultCollectionBuilder.createAndStoreVia(
+                            ModelReference.of(ITaskFactory.class),
+                            ModelReference.of(modelNode.getPath().child("__tasks"), ModelTypes.collectionOf(Task .class))
+                    )
+
+            ) {
+                @Override
+                protected <S extends TaskInternal> void onCreate(final String name, ModelType<S> type) {
+                    Task task = get(name);
+                    binary.builtBy(task);
+                }
+            };
+
+            CollectionBuilder<Task> cast = Cast.uncheckedCast(collectionBuilder);
+
+            List<ModelView<?>> inputsWithBinary = new ArrayList<ModelView<?>>(inputs.size() + 1);
+            inputsWithBinary.addAll(inputs);
+            inputsWithBinary.add(new InstanceModelView<T>(getSubject().getPath(), getSubject().getType(), binary));
+
+            invoke(inputsWithBinary, cast, binary, binary);
+        }
+    }
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractor.java
new file mode 100644
index 0000000..bc2f300
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.BinaryType;
+import org.gradle.platform.base.BinaryTypeBuilder;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.DefaultBinaryContainer;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BinaryTypeModelRuleExtractor extends TypeModelRuleExtractor<BinaryType, BinarySpec, BaseBinarySpec> {
+    private final Instantiator instantiator;
+
+    public BinaryTypeModelRuleExtractor(final Instantiator instantiator) {
+        super("binary", BinarySpec.class, BaseBinarySpec.class, BinaryTypeBuilder.class, JavaReflectionUtil.factory(DirectInstantiator.INSTANCE, DefaultBinaryTypeBuilder.class));
+        this.instantiator = instantiator;
+    }
+
+    @Override
+    protected <R, S> ExtractedModelRule createRegistration(MethodRuleDefinition<R, S> ruleDefinition, ModelType<? extends BinarySpec> type, TypeBuilderInternal<BinarySpec> builder) {
+        ImmutableList<Class<?>> dependencies = ImmutableList.<Class<?>>of(ComponentModelBasePlugin.class);
+        ModelType<? extends BaseBinarySpec> implementation = determineImplementationType(type, builder);
+        if (implementation != null) {
+            ModelAction<?> mutator = new RegistrationAction(type, implementation, ruleDefinition.getDescriptor(), instantiator);
+            return new ExtractedModelAction(ModelActionRole.Defaults, dependencies, mutator);
+        }
+        return new DependencyOnlyExtractedModelRule(dependencies);
+    }
+
+    public static class DefaultBinaryTypeBuilder extends AbstractTypeBuilder<BinarySpec> implements BinaryTypeBuilder<BinarySpec> {
+        public DefaultBinaryTypeBuilder() {
+            super(BinaryType.class);
+        }
+    }
+
+    private static class RegistrationAction implements ModelAction<DefaultBinaryContainer> {
+        private final ModelType<? extends BinarySpec> publicType;
+        private final ModelType<? extends BaseBinarySpec> implementationType;
+        private final ModelRuleDescriptor descriptor;
+        private final Instantiator instantiator;
+        private final ModelReference<DefaultBinaryContainer> subject;
+        private final List<ModelReference<?>> inputs;
+
+        public RegistrationAction(ModelType<? extends BinarySpec> publicType, ModelType<? extends BaseBinarySpec> implementationType, ModelRuleDescriptor descriptor, Instantiator instantiator) {
+            this.publicType = publicType;
+            this.implementationType = implementationType;
+            this.descriptor = descriptor;
+            this.instantiator = instantiator;
+            this.subject = ModelReference.of(DefaultBinaryContainer.class);
+            this.inputs = Collections.<ModelReference<?>>singletonList(ModelReference.of(ITaskFactory.class));
+        }
+
+        @Override
+        public ModelReference<DefaultBinaryContainer> getSubject() {
+            return subject;
+        }
+
+        @Override
+        public ModelRuleDescriptor getDescriptor() {
+            return descriptor;
+        }
+
+        @Override
+        public List<ModelReference<?>> getInputs() {
+            return inputs;
+        }
+
+        @Override
+        public void execute(MutableModelNode modelNode, DefaultBinaryContainer binaries, final List<ModelView<?>> inputs) {
+            @SuppressWarnings("unchecked")
+            Class<BinarySpec> publicClass = (Class<BinarySpec>) publicType.getConcreteClass();
+            binaries.registerFactory(publicClass, new NamedDomainObjectFactory<BaseBinarySpec>() {
+                public BaseBinarySpec create(String name) {
+                    return BaseBinarySpec.create(implementationType.getConcreteClass(), name, instantiator, ModelViews.assertType(inputs.get(0), ModelType.of(ITaskFactory.class)).getInstance());
+                }
+            }, descriptor);
+        }
+    }
+}
+
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/CollectionBuilderBasedRule.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/CollectionBuilderBasedRule.java
new file mode 100644
index 0000000..4cd2058
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/CollectionBuilderBasedRule.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.specs.Spec;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.ModelAction;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.ModelView;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class CollectionBuilderBasedRule<R, S, T, C> implements ModelAction<C> {
+    private final ModelReference<C> subject;
+    private final Class<? extends T> baseType;
+    private final MethodRuleDefinition<R, ?> ruleDefinition;
+
+    private ImmutableList<ModelReference<?>> inputs;
+    protected int baseTypeParameterIndex;
+
+    public CollectionBuilderBasedRule(ModelReference<C> subject, Class<? extends T> baseType, MethodRuleDefinition<R, ?> ruleDefinition, ModelReference<?>... additionalInput) {
+        this.subject = subject;
+        this.baseType = baseType;
+        this.ruleDefinition = ruleDefinition;
+        this.inputs = calculateInputs(Arrays.asList(additionalInput));
+    }
+
+    public List<ModelReference<?>> getInputs() {
+        return this.inputs;
+    }
+
+    public ModelReference<C> getSubject() {
+        return subject;
+    }
+
+    public ModelRuleDescriptor getDescriptor() {
+        return ruleDefinition.getDescriptor();
+    }
+
+    private ImmutableList<ModelReference<?>> calculateInputs(List<ModelReference<?>> modelReferences) {
+        final List<ModelReference<?>> references = this.ruleDefinition.getReferences().subList(1, this.ruleDefinition.getReferences().size());
+        final List<ModelReference<?>> filteredReferences = CollectionUtils.filter(references, new Spec<ModelReference<?>>() {
+            public boolean isSatisfiedBy(ModelReference<?> element) {
+                if (element.getType().equals(ModelType.of(baseType))) {
+                    baseTypeParameterIndex = references.indexOf(element) + 1;
+                    return false;
+                }
+                return true;
+            }
+        });
+
+        ImmutableList.Builder<ModelReference<?>> allInputs = ImmutableList.builder();
+        allInputs.addAll(modelReferences);
+        allInputs.addAll(filteredReferences);
+        return allInputs.build();
+    }
+
+    protected void invoke(List<ModelView<?>> inputs, CollectionBuilder<S> collectionBuilder, T baseTypeParameter, Object ignoredInput) {
+        Object[] args = new Object[inputs.size() + 1];
+        args[0] = collectionBuilder;
+        args[baseTypeParameterIndex] = baseTypeParameter;
+
+        for (ModelView<?> view : inputs) {
+            Object instance = view.getInstance();
+            if (instance == ignoredInput) {
+                continue;
+            }
+            for (int i = 0; i < args.length; i++) {
+                if (args[i] == null) {
+                    args[i] = instance;
+                    break;
+                }
+            }
+        }
+        ruleDefinition.getRuleInvoker().invoke(args);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractor.java
new file mode 100644
index 0000000..6822adf
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractor.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.internal.BiAction;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.DelegatingCollectionBuilder;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.*;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+
+import java.util.List;
+
+public class ComponentBinariesModelRuleExtractor extends AbstractAnnotationDrivenComponentModelRuleExtractor<ComponentBinaries> {
+
+    @Override
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        return createRegistration(ruleDefinition);
+    }
+
+    private <R, S extends BinarySpec> ExtractedModelRule createRegistration(MethodRuleDefinition<R, ?> ruleDefinition) {
+        try {
+            RuleMethodDataCollector dataCollector = new RuleMethodDataCollector();
+            visitAndVerifyMethodSignature(dataCollector, ruleDefinition);
+
+            Class<S> binaryType = dataCollector.getParameterType(BinarySpec.class);
+            Class<? extends ComponentSpec> componentType = dataCollector.getParameterType(ComponentSpec.class);
+            ModelReference<CollectionBuilder<BinarySpec>> subject = ModelReference.of(ModelPath.path("binaries"), DefaultCollectionBuilder.typeOf(ModelType.of(BinarySpec.class)));
+            ComponentBinariesRule<R, S> componentBinariesRule = new ComponentBinariesRule<R, S>(subject, componentType, binaryType, ruleDefinition);
+
+            return new ExtractedModelAction(ModelActionRole.Mutate, ImmutableList.of(ComponentModelBasePlugin.class), componentBinariesRule);
+        } catch (InvalidModelException e) {
+            throw invalidModelRule(ruleDefinition, e);
+        }
+    }
+
+    private void visitAndVerifyMethodSignature(RuleMethodDataCollector dataCollector, MethodRuleDefinition<?, ?> ruleDefinition) {
+        assertIsVoidMethod(ruleDefinition);
+        visitCollectionBuilderSubject(dataCollector, ruleDefinition, BinarySpec.class);
+        visitDependency(dataCollector, ruleDefinition, ModelType.of(ComponentSpec.class));
+    }
+
+    private class ComponentBinariesRule<R, S extends BinarySpec> extends CollectionBuilderBasedRule<R, S, ComponentSpec, CollectionBuilder<BinarySpec>> {
+
+        private final Class<? extends ComponentSpec> componentType;
+        private final Class<S> binaryType;
+
+        public ComponentBinariesRule(ModelReference<CollectionBuilder<BinarySpec>> subject, final Class<? extends ComponentSpec> componentType, final Class<S> binaryType, MethodRuleDefinition<R, ?> ruleDefinition) {
+            super(subject, componentType, ruleDefinition, ModelReference.of(ComponentSpecContainer.class));
+            this.componentType = componentType;
+            this.binaryType = binaryType;
+        }
+
+        public void execute(MutableModelNode modelNode, final CollectionBuilder<BinarySpec> binaries, List<ModelView<?>> inputs) {
+            ComponentSpecContainer componentSpecs = ModelViews.assertType(inputs.get(0), ModelType.of(ComponentSpecContainer.class)).getInstance();
+
+            for (final ComponentSpec componentSpec : componentSpecs.withType(componentType)) {
+                CollectionBuilder<S> typed = binaries.withType(binaryType);
+                CollectionBuilder<S> wrapped = new DelegatingCollectionBuilder<S>(typed, ModelType.of(binaryType), new BiAction<String, ModelType<? extends S>>() {
+                    @Override
+                    public void execute(String s, ModelType<? extends S> modelType) {
+                        BinarySpec binary = binaries.get(s);
+                        assert binary != null : "binary should not be null";
+                        componentSpec.getBinaries().add(binary);
+                        BinarySpecInternal binaryInternal = (BinarySpecInternal) binary;
+                        FunctionalSourceSet binarySourceSet = ((ComponentSpecInternal) componentSpec).getSources().copy(s);
+                        binaryInternal.setBinarySources(binarySourceSet);
+                    }
+                });
+
+                invoke(inputs, wrapped, componentSpec, componentSpecs);
+            }
+        }
+    }
+
+
+    protected InvalidModelRuleDeclarationException invalidModelRule(MethodRuleDefinition<?, ?> ruleDefinition, InvalidModelException e) {
+        StringBuilder sb = new StringBuilder();
+        ruleDefinition.getDescriptor().describeTo(sb);
+        sb.append(" is not a valid ComponentBinaries model rule method.");
+        return new InvalidModelRuleDeclarationException(sb.toString(), e);
+    }
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentModelBaseServiceRegistry.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentModelBaseServiceRegistry.java
new file mode 100644
index 0000000..0946085
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentModelBaseServiceRegistry.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.model.internal.inspect.MethodModelRuleExtractor;
+import org.gradle.platform.base.Platform;
+import org.gradle.platform.base.internal.toolchain.DefaultToolResolver;
+import org.gradle.platform.base.internal.toolchain.ToolChainInternal;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+public class ComponentModelBaseServiceRegistry implements PluginServiceRegistry {
+
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new GlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration){
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.addProvider(new ProjectScopeServices());
+    }
+
+    private static class ProjectScopeServices {
+        ToolResolver createToolResolver(ServiceRegistry services) {
+            DefaultToolResolver toolResolver = new DefaultToolResolver();
+            for (ToolChainInternal<?> toolChain : services.getAll(ToolChainInternal.class)) {
+                @SuppressWarnings("unchecked") ToolChainInternal<? extends Platform> converted = toolChain;
+                toolResolver.registerToolChain(converted);
+            }
+            return toolResolver;
+        }
+    }
+
+    private static class GlobalScopeServices {
+        MethodModelRuleExtractor createLanguageTypePluginInspector() {
+            return new LanguageTypeModelRuleExtractor();
+        }
+
+        MethodModelRuleExtractor createComponentModelPluginInspector(Instantiator instantiator) {
+            return new ComponentTypeModelRuleExtractor(instantiator);
+        }
+
+        MethodModelRuleExtractor createBinaryTypeModelPluginInspector(Instantiator instantiator) {
+            return new BinaryTypeModelRuleExtractor(instantiator);
+        }
+
+        MethodModelRuleExtractor createComponentBinariesPluginInspector() {
+            return new ComponentBinariesModelRuleExtractor();
+        }
+        MethodModelRuleExtractor createBinaryTaskPluginInspector() {
+            return new BinaryTasksModelRuleExtractor();
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentTypeModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentTypeModelRuleExtractor.java
new file mode 100644
index 0000000..b166c65
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/ComponentTypeModelRuleExtractor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.ComponentSpec;
+import org.gradle.platform.base.ComponentSpecIdentifier;
+import org.gradle.platform.base.ComponentType;
+import org.gradle.platform.base.ComponentTypeBuilder;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.DefaultComponentSpecContainer;
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ComponentTypeModelRuleExtractor extends TypeModelRuleExtractor<ComponentType, ComponentSpec, BaseComponentSpec> {
+
+    private Instantiator instantiator;
+
+    public ComponentTypeModelRuleExtractor(final Instantiator instantiator) {
+        super("component", ComponentSpec.class, BaseComponentSpec.class, ComponentTypeBuilder.class, JavaReflectionUtil.factory(DirectInstantiator.INSTANCE, DefaultComponentTypeBuilder.class));
+        this.instantiator = instantiator;
+    }
+
+    @Override
+    protected <R, S> ExtractedModelRule createRegistration(MethodRuleDefinition<R, S> ruleDefinition, ModelType<? extends ComponentSpec> type, TypeBuilderInternal<ComponentSpec> builder) {
+        ImmutableList<Class<?>> dependencies = ImmutableList.<Class<?>>of(ComponentModelBasePlugin.class);
+        ModelType<? extends BaseComponentSpec> implementation = determineImplementationType(type, builder);
+        if (implementation != null) {
+            ModelAction<?> mutator = new RegistrationAction(type, implementation, ruleDefinition.getDescriptor(), instantiator);
+            return new ExtractedModelAction(ModelActionRole.Defaults, dependencies, mutator);
+        }
+        return new DependencyOnlyExtractedModelRule(dependencies);
+    }
+
+    public static class DefaultComponentTypeBuilder extends AbstractTypeBuilder<ComponentSpec> implements ComponentTypeBuilder<ComponentSpec> {
+        public DefaultComponentTypeBuilder() {
+            super(ComponentType.class);
+        }
+    }
+
+    private static class RegistrationAction implements ModelAction<DefaultComponentSpecContainer> {
+        private final ModelType<? extends ComponentSpec> publicType;
+        private final ModelType<? extends BaseComponentSpec> implementationType;
+        private final ModelRuleDescriptor descriptor;
+        private final Instantiator instantiator;
+        private final ModelReference<DefaultComponentSpecContainer> subject;
+        private final List<ModelReference<?>> inputs;
+
+        public RegistrationAction(ModelType<? extends ComponentSpec> publicType, ModelType<? extends BaseComponentSpec> implementationType, ModelRuleDescriptor descriptor, Instantiator instantiator) {
+            this.publicType = publicType;
+            this.implementationType = implementationType;
+            this.descriptor = descriptor;
+            this.instantiator = instantiator;
+            this.subject = ModelReference.of(DefaultComponentSpecContainer.class);
+            this.inputs = Arrays.<ModelReference<?>>asList(ModelReference.of(ProjectIdentifier.class), ModelReference.of(ProjectSourceSet.class));
+        }
+
+        @Override
+        public ModelReference<DefaultComponentSpecContainer> getSubject() {
+            return subject;
+        }
+
+        @Override
+        public ModelRuleDescriptor getDescriptor() {
+            return descriptor;
+        }
+
+        @Override
+        public List<ModelReference<?>> getInputs() {
+            return inputs;
+        }
+
+        @Override
+        public void execute(MutableModelNode modelNode, DefaultComponentSpecContainer components, List<ModelView<?>> inputs) {
+            final ProjectIdentifier projectIdentifier = ModelViews.assertType(inputs.get(0), ModelType.of(ProjectIdentifier.class)).getInstance();
+            final ProjectSourceSet projectSourceSet = ModelViews.assertType(inputs.get(1), ModelType.of(ProjectSourceSet.class)).getInstance();
+            @SuppressWarnings("unchecked")
+            Class<ComponentSpec> publicClass = (Class<ComponentSpec>) publicType.getConcreteClass();
+            components.registerFactory(publicClass, new NamedDomainObjectFactory<BaseComponentSpec>() {
+                public BaseComponentSpec create(String name) {
+                    FunctionalSourceSet componentSourceSet = instantiator.newInstance(DefaultFunctionalSourceSet.class, name, instantiator, projectSourceSet);
+                    ComponentSpecIdentifier id = new DefaultComponentSpecIdentifier(projectIdentifier.getPath(), name);
+                    return BaseComponentSpec.create(implementationType.getConcreteClass(), id, componentSourceSet, instantiator);
+                }
+            }, descriptor);
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/LanguageTypeModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/LanguageTypeModelRuleExtractor.java
new file mode 100644
index 0000000..4787006
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/LanguageTypeModelRuleExtractor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.registry.LanguageRegistry;
+import org.gradle.language.base.internal.registry.RuleBasedLanguageRegistration;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.model.internal.core.*;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+import org.gradle.platform.base.internal.builder.LanguageTypeBuilderInternal;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+import org.gradle.platform.base.internal.util.ImplementationTypeDetermer;
+
+import java.util.List;
+
+public class LanguageTypeModelRuleExtractor extends TypeModelRuleExtractor<LanguageType, LanguageSourceSet, BaseLanguageSourceSet> {
+    public ImplementationTypeDetermer<LanguageSourceSet, BaseLanguageSourceSet> implementationTypeDetermer = new ImplementationTypeDetermer<LanguageSourceSet, BaseLanguageSourceSet>("language", BaseLanguageSourceSet.class);
+
+    public LanguageTypeModelRuleExtractor() {
+        super("language", LanguageSourceSet.class, BaseLanguageSourceSet.class, LanguageTypeBuilder.class, JavaReflectionUtil.factory(DirectInstantiator.INSTANCE, DefaultLanguageTypeBuilder.class));
+    }
+
+    @Override
+    protected <R, S> ExtractedModelRule createRegistration(MethodRuleDefinition<R, S> ruleDefinition, ModelType<? extends LanguageSourceSet> type, TypeBuilderInternal<LanguageSourceSet> builder) {
+        ImmutableList<Class<?>> dependencies = ImmutableList.<Class<?>>of(ComponentModelBasePlugin.class);
+        ModelType<? extends BaseLanguageSourceSet> implementation = implementationTypeDetermer.determineImplementationType(type, builder);
+        if (implementation != null) {
+            ModelAction<?> mutator = new RegisterTypeRule(type, implementation, ((LanguageTypeBuilderInternal) builder).getLanguageName(), ruleDefinition.getDescriptor());
+            return new ExtractedModelAction(ModelActionRole.Defaults, dependencies, mutator);
+        }
+        return new DependencyOnlyExtractedModelRule(dependencies);
+    }
+
+    public static class DefaultLanguageTypeBuilder extends AbstractTypeBuilder<LanguageSourceSet> implements LanguageTypeBuilderInternal<LanguageSourceSet> {
+        private String languageName;
+
+        public DefaultLanguageTypeBuilder() {
+            super(LanguageType.class);
+        }
+
+        @Override
+        public void setLanguageName(String languageName) {
+            this.languageName = languageName;
+        }
+
+        @Override
+        public String getLanguageName() {
+            return languageName;
+        }
+    }
+
+    protected static class RegisterTypeRule implements ModelAction<LanguageRegistry> {
+        private final ModelType<? extends LanguageSourceSet> type;
+        private final ModelType<? extends BaseLanguageSourceSet> implementation;
+        private String languageName;
+        private final ModelRuleDescriptor descriptor;
+        private final ModelReference<LanguageRegistry> subject;
+        private final List<ModelReference<?>> inputs;
+
+        protected RegisterTypeRule(ModelType<? extends LanguageSourceSet> type, ModelType<? extends BaseLanguageSourceSet> implementation, String languageName, ModelRuleDescriptor descriptor) {
+            this.type = type;
+            this.implementation = implementation;
+            this.languageName = languageName;
+            this.descriptor = descriptor;
+
+            subject = ModelReference.of(LanguageRegistry.class);
+            inputs = ImmutableList.<ModelReference<?>>of(ModelReference.of(ServiceRegistry.class));
+        }
+
+        public ModelReference<LanguageRegistry> getSubject() {
+            return subject;
+        }
+
+        public List<ModelReference<?>> getInputs() {
+            return inputs;
+        }
+
+        public ModelRuleDescriptor getDescriptor() {
+            return descriptor;
+        }
+
+        public void execute(MutableModelNode modelNode, LanguageRegistry languageRegistry, List<ModelView<?>> inputs) {
+            ServiceRegistry serviceRegistry = ModelViews.assertType(inputs.get(0), ModelType.of(ServiceRegistry.class)).getInstance();
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            @SuppressWarnings("unchecked")
+            Class<BaseLanguageSourceSet> publicClass = (Class<BaseLanguageSourceSet>) type.getConcreteClass();
+            Class<? extends BaseLanguageSourceSet> implementationClass = implementation.getConcreteClass();
+            languageRegistry.add(new RuleBasedLanguageRegistration<BaseLanguageSourceSet>(languageName, publicClass, implementationClass, instantiator, fileResolver));
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/TypeModelRuleExtractor.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/TypeModelRuleExtractor.java
new file mode 100644
index 0000000..714b37a
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/registry/TypeModelRuleExtractor.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Factory;
+import org.gradle.model.InvalidModelRuleDeclarationException;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.ExtractedModelRule;
+import org.gradle.model.internal.inspect.MethodRuleDefinition;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.InvalidModelException;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+
+import java.lang.annotation.Annotation;
+
+public abstract class TypeModelRuleExtractor<A extends Annotation, T, U extends T> extends AbstractAnnotationDrivenComponentModelRuleExtractor<A> {
+
+    private final String modelName;
+    private final ModelType<T> baseInterface;
+    private final ModelType<U> baseImplementation;
+    private final ModelType<?> builderInterface;
+    private final Factory<? extends TypeBuilderInternal<T>> typeBuilderFactory;
+
+    public TypeModelRuleExtractor(String modelName, Class<T> baseInterface, Class<U> baseImplementation, Class<?> builderInterface, Factory<? extends TypeBuilderInternal<T>> typeBuilderFactory) {
+        this.modelName = modelName;
+        this.typeBuilderFactory = typeBuilderFactory;
+        this.baseInterface = ModelType.of(baseInterface);
+        this.baseImplementation = ModelType.of(baseImplementation);
+        this.builderInterface = ModelType.of(builderInterface);
+    }
+
+    public <R, S> ExtractedModelRule registration(MethodRuleDefinition<R, S> ruleDefinition) {
+        try {
+            ModelType<? extends T> type = readType(ruleDefinition);
+            TypeBuilderInternal<T> builder = typeBuilderFactory.create();
+            ruleDefinition.getRuleInvoker().invoke(builder);
+            return createRegistration(ruleDefinition, type, builder);
+        } catch (InvalidModelException e) {
+            throw invalidModelRule(ruleDefinition, e);
+        }
+    }
+
+    @Nullable
+    protected abstract <R, S> ExtractedModelRule createRegistration(MethodRuleDefinition<R, S> ruleDefinition, ModelType<? extends T> type, TypeBuilderInternal<T> builder);
+
+    protected ModelType<? extends T> readType(MethodRuleDefinition<?, ?> ruleDefinition) {
+        assertIsVoidMethod(ruleDefinition);
+        if (ruleDefinition.getReferences().size() != 1) {
+            throw new InvalidModelException(String.format("Method %s must have a single parameter of type '%s'.", getDescription(), builderInterface.toString()));
+        }
+        ModelReference<?> subjectReference = ruleDefinition.getSubjectReference();
+        @SuppressWarnings("ConstantConditions") ModelType<?> builder = subjectReference.getType();
+        if (!builderInterface.isAssignableFrom(builder)) {
+            throw new InvalidModelException(String.format("Method %s must have a single parameter of type '%s'.", getDescription(), builderInterface.toString()));
+        }
+        if (builder.getTypeVariables().size() != 1) {
+            throw new InvalidModelException(String.format("Parameter of type '%s' must declare a type parameter.", builderInterface.toString()));
+        }
+        ModelType<?> subType = builder.getTypeVariables().get(0);
+
+        if (subType.isWildcard()) {
+            throw new InvalidModelException(String.format("%s type '%s' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.).", StringUtils.capitalize(modelName), subType.toString()));
+        }
+
+        ModelType<? extends T> asSubclass = baseInterface.asSubclass(subType);
+        if (asSubclass == null) {
+            throw new InvalidModelException(String.format("%s type '%s' is not a subtype of '%s'.", StringUtils.capitalize(modelName), subType.toString(), baseInterface.toString()));
+        }
+
+        return asSubclass;
+    }
+
+    protected InvalidModelRuleDeclarationException invalidModelRule(MethodRuleDefinition<?, ?> ruleDefinition, InvalidModelException e) {
+        StringBuilder sb = new StringBuilder();
+        ruleDefinition.getDescriptor().describeTo(sb);
+        sb.append(String.format(" is not a valid %s model rule method.", modelName));
+        return new InvalidModelRuleDeclarationException(sb.toString(), e);
+    }
+
+    protected ModelType<? extends U> determineImplementationType(ModelType<? extends T> type, TypeBuilderInternal<T> builder) {
+        Class<? extends T> implementation = builder.getDefaultImplementation();
+        if (implementation == null) {
+            return null;
+        }
+
+        ModelType<? extends T> implementationType = ModelType.of(implementation);
+        ModelType<? extends U> asSubclass = baseImplementation.asSubclass(implementationType);
+
+        if (asSubclass == null) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must extend '%s'.", StringUtils.capitalize(modelName), implementationType, baseImplementation));
+        }
+
+        if (!type.isAssignableFrom(asSubclass)) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must implement '%s'.", StringUtils.capitalize(modelName), asSubclass, type));
+        }
+
+        try {
+            asSubclass.getRawClass().getConstructor();
+        } catch (NoSuchMethodException nsmException) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must have public default constructor.", StringUtils.capitalize(modelName), asSubclass));
+        }
+
+        return asSubclass;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainer.java
new file mode 100644
index 0000000..33f2f27
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.rules;
+
+import com.google.common.collect.Maps;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
+
+import java.util.Map;
+
+public abstract class RuleAwarePolymorphicDomainObjectContainer<T> extends DefaultPolymorphicDomainObjectContainer<T> {
+    private final Map<Class<? extends T>, ModelRuleDescriptor> creators = Maps.newHashMap();
+
+    public RuleAwarePolymorphicDomainObjectContainer(Class<T> type, Instantiator instantiator) {
+        super(type, instantiator);
+    }
+
+    public <U extends T> void registerFactory(Class<U> type, NamedDomainObjectFactory<? extends U> factory, ModelRuleDescriptor descriptor) {
+        checkCanRegister(type, descriptor);
+        super.registerFactory(type, factory);
+    }
+
+    private void checkCanRegister(Class<? extends T> type, ModelRuleDescriptor descriptor) {
+        ModelRuleDescriptor creator = creators.get(type);
+        if (creator != null) {
+            StringBuilder builder = new StringBuilder("Cannot register a factory for type ")
+                    .append(type.getSimpleName())
+                    .append(" because a factory for this type was already registered by ");
+            creator.describeTo(builder);
+            builder.append(".");
+            throw new GradleException(builder.toString());
+        }
+        creators.put(type, descriptor);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/test/DefaultTestSuiteContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/test/DefaultTestSuiteContainer.java
new file mode 100644
index 0000000..a25d778
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/test/DefaultTestSuiteContainer.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.platform.base.internal.test;
+
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.platform.base.test.TestSuiteContainer;
+import org.gradle.platform.base.test.TestSuiteSpec;
+
+public class DefaultTestSuiteContainer extends DefaultPolymorphicDomainObjectContainer<TestSuiteSpec> implements TestSuiteContainer {
+    public DefaultTestSuiteContainer(Instantiator instantiator) {
+        super(TestSuiteSpec.class, instantiator);
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgCollector.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgCollector.java
new file mode 100644
index 0000000..c03d223
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgCollector.java
@@ -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.platform.base.internal.toolchain;
+
+public interface ArgCollector {
+    
+    ArgCollector args(Object... args);
+
+    ArgCollector args(Iterable<?> args);
+
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgWriter.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgWriter.java
new file mode 100755
index 0000000..617048d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ArgWriter.java
@@ -0,0 +1,89 @@
+/*
+ * 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.platform.base.internal.toolchain;
+
+import org.gradle.api.Transformer;
+
+import java.io.PrintWriter;
+import java.util.regex.Pattern;
+
+public class ArgWriter implements ArgCollector {
+    private static final Pattern WHITESPACE = Pattern.compile("\\s");
+    private final PrintWriter writer;
+    private final boolean backslashEscape;
+
+    private ArgWriter(PrintWriter writer, boolean backslashEscape) {
+        this.writer = writer;
+        this.backslashEscape = backslashEscape;
+    }
+
+    public static ArgWriter unixStyle(PrintWriter writer) {
+        return new ArgWriter(writer, true);
+    }
+
+    public static Transformer<ArgWriter, PrintWriter> unixStyleFactory() {
+        return new Transformer<ArgWriter, PrintWriter>() {
+            public ArgWriter transform(PrintWriter original) {
+                return unixStyle(original);
+            }
+        };
+    }
+
+    public static ArgWriter windowsStyle(PrintWriter writer) {
+        return new ArgWriter(writer, false);
+    }
+
+    public static Transformer<ArgWriter, PrintWriter> windowsStyleFactory() {
+        return new Transformer<ArgWriter, PrintWriter>() {
+            public ArgWriter transform(PrintWriter original) {
+                return windowsStyle(original);
+            }
+        };
+    }
+
+    /**
+     * Writes a set of args on a single line, escaping and quoting as required.
+     */
+    public ArgWriter args(Object... args) {
+        for (int i = 0; i < args.length; i++) {
+            Object arg = args[i];
+            if (i > 0) {
+                writer.print(' ');
+            }
+            String str = arg.toString();
+            if (backslashEscape) {
+                str = str.replace("\\", "\\\\").replace("\"", "\\\"");
+            }
+            if (WHITESPACE.matcher(str).find()) {
+                writer.print('\"');
+                writer.print(str);
+                writer.print('\"');
+            } else {
+                writer.print(str);
+            }
+        }
+        writer.println();
+        return this;
+    }
+
+    public ArgCollector args(Iterable<?> args) {
+        for (Object arg : args) {
+            args(arg);
+        }
+        return this;
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedCompiler.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedCompiler.java
new file mode 100644
index 0000000..9dda1be
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedCompiler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import org.gradle.language.base.internal.compile.*;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.util.TreeVisitor;
+
+public class DefaultResolvedCompiler<C extends CompileSpec> implements ResolvedTool<org.gradle.language.base.internal.compile.Compiler<C>> {
+    private final ToolProvider provider;
+    private final Class<C> specType;
+
+    public DefaultResolvedCompiler(ToolProvider provider, Class<C> specType) {
+        this.provider = provider;
+        this.specType = specType;
+    }
+
+    @Override
+    public Compiler<C> get() {
+        return provider.newCompiler(specType);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void explain(TreeVisitor<? super String> visitor) {
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedTool.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedTool.java
new file mode 100644
index 0000000..6cce4fb
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultResolvedTool.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import org.gradle.util.TreeVisitor;
+
+public class DefaultResolvedTool<T> implements ResolvedTool<T> {
+    private final ToolProvider provider;
+    private final Class<T> toolType;
+
+    public DefaultResolvedTool(ToolProvider provider, Class<T> toolType) {
+        this.provider = provider;
+        this.toolType = toolType;
+    }
+
+    @Override
+    public T get() {
+        return provider.get(toolType);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void explain(TreeVisitor<? super String> visitor) {
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultToolResolver.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultToolResolver.java
new file mode 100644
index 0000000..e4822a2
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/DefaultToolResolver.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import com.google.common.collect.Sets;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.Platform;
+import org.gradle.util.TreeVisitor;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+public class DefaultToolResolver implements ToolResolver {
+    private Set<ToolChainInternal<? extends Platform>> toolChains;
+
+    public DefaultToolResolver() {
+        this.toolChains = Sets.newHashSet();
+    }
+
+    public void registerToolChain(ToolChainInternal<? extends Platform> toolChain) {
+        toolChains.add(toolChain);
+    }
+
+    /**
+     * Finds the most inherited Platform parameter type of the select method on a toolchain.  It assumes that
+     * a ToolChainInternal has only one public declared select method.
+     */
+    private Class<? extends Platform> getPlatformType(ToolChainInternal<? extends Platform> toolChain) {
+        //TODO Do we want to support ToolChains with select methods for multiple platform types?
+        Class<?> toolChainClass = toolChain.getClass();
+        Class<? extends Platform> platformType = null;
+        for (Method method : toolChainClass.getMethods()) {
+            if (method.getName().equals("select") && Modifier.isPublic(method.getModifiers())) {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (parameterTypes.length == 1 && Platform.class.isAssignableFrom(parameterTypes[0])) {
+                    @SuppressWarnings("unchecked") Class<? extends Platform> converted = (Class<? extends Platform>) parameterTypes[0];
+                    // Check to see if this type is more inherited than what has already been found
+                    // This filters out any methods from parent classes/interfaces
+                    if (platformType == null || platformType.isAssignableFrom(converted)) {
+                        platformType = converted;
+                    }
+                }
+            }
+        }
+        return platformType;
+    }
+
+    /**
+     * Filters the list of toolchains for only those that support the given platform
+     */
+    private <P extends Platform> Set<ToolChainInternal<P>> filterToolChains(P platform) {
+        Set<ToolChainInternal<P>> platformToolChains = Sets.newHashSet();
+        for (ToolChainInternal<? extends Platform> raw : toolChains) {
+            if (getPlatformType(raw).isAssignableFrom(platform.getClass())) {
+                @SuppressWarnings("unchecked") ToolChainInternal<P> converted = (ToolChainInternal<P>) raw;
+                platformToolChains.add(converted);
+            }
+        }
+        return platformToolChains;
+    }
+
+    protected <P extends Platform> ToolSearchResult findToolProvider(P requirement) {
+        ToolSearchFailure notAvailableResult = new ToolSearchFailure("No tool chains can satisfy the requirement");
+        for (ToolChainInternal<P> toolChain : filterToolChains(requirement)) {
+            ToolSearchResult result = toolChain.select(requirement);
+            if (result.isAvailable()) {
+                return result;
+            } else {
+                notAvailableResult.addResult(result);
+            }
+        }
+        return notAvailableResult;
+    }
+
+    @Override
+    public <P extends Platform> ToolSearchResult checkToolAvailability(P requirement) {
+        return findToolProvider(requirement);
+    }
+
+    @Override
+    public <T, P extends Platform> ResolvedTool<T> resolve(Class<T> toolType, P requirement) {
+        ToolSearchResult toolProvider = findToolProvider(requirement);
+        if (toolProvider.isAvailable()) {
+            return new DefaultResolvedTool<T>((ToolProvider)toolProvider, toolType);
+        } else {
+            ResolvedToolSearchFailure<T> notAvailableResult = new ResolvedToolSearchFailure<T>(String.format("No tool chains can provide a tool of type %s", toolType.getSimpleName()));
+            notAvailableResult.addResult(toolProvider);
+            return notAvailableResult;
+        }
+    }
+
+    @Override
+    public <C extends CompileSpec, P extends Platform> ResolvedTool<Compiler<C>> resolveCompiler(Class<C> specType, P requirement) {
+        ToolSearchResult toolProvider = findToolProvider(requirement);
+        if (toolProvider.isAvailable()) {
+            return new DefaultResolvedCompiler<C>((ToolProvider)toolProvider, specType);
+        } else {
+            CompilerSearchFailure<C> notAvailableResult = new CompilerSearchFailure<C>(String.format("No tool chains can provide a compiler for type %s", specType.getSimpleName()));
+            notAvailableResult.addResult(toolProvider);
+            return notAvailableResult;
+        }
+    }
+
+    private static class ResolvedToolSearchFailure<T> extends ToolSearchFailure implements ResolvedTool<T> {
+        public ResolvedToolSearchFailure(String message) {
+            super(message);
+        }
+
+        @Override
+        public T get() {
+            throw failure();
+        }
+    }
+
+    private static class CompilerSearchFailure<T extends CompileSpec> extends ToolSearchFailure implements ResolvedTool<Compiler<T>> {
+        public CompilerSearchFailure(String message) {
+            super(message);
+        }
+
+        @Override
+        public Compiler<T> get() {
+            throw failure();
+        }
+    }
+
+    private static class ToolSearchFailure implements ToolSearchResult {
+        private final String message;
+        Set<ToolSearchResult> results = Sets.newHashSet();
+
+        public ToolSearchFailure(String message) {
+            this.message = message;
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(message);
+            visitor.startChildren();
+            for (ToolSearchResult result : results) {
+                result.explain(visitor);
+            }
+            visitor.endChildren();
+        }
+
+        RuntimeException failure() {
+            TreeFormatter formatter = new TreeFormatter();
+            explain(formatter);
+            return new GradleException(formatter.toString());
+        }
+
+        void addResult(ToolSearchResult result) {
+            results.add(result);
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ResolvedTool.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ResolvedTool.java
new file mode 100644
index 0000000..0eeb10d
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ResolvedTool.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+public interface ResolvedTool<T> extends ToolSearchResult {
+    public T get();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainAvailability.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainAvailability.java
new file mode 100644
index 0000000..5bc009e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainAvailability.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.platform.base.internal.toolchain;
+
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.util.TreeVisitor;
+
+public class ToolChainAvailability implements ToolSearchResult {
+    private ToolSearchResult reason;
+
+    public boolean isAvailable() {
+        return reason == null;
+    }
+
+    public String getUnavailableMessage() {
+        TreeFormatter formatter = new TreeFormatter();
+        this.explain(formatter);
+        return formatter.toString();
+    }
+
+    public void explain(TreeVisitor<? super String> visitor) {
+        reason.explain(visitor);
+    }
+
+    public ToolChainAvailability unavailable(String unavailableMessage) {
+        if (reason == null) {
+            reason = new FixedMessageToolSearchResult(unavailableMessage);
+        }
+        return this;
+    }
+
+    public ToolChainAvailability mustBeAvailable(ToolSearchResult tool) {
+        if (!tool.isAvailable() && reason == null) {
+            reason = tool;
+        }
+        return this;
+    }
+
+    private static class FixedMessageToolSearchResult implements ToolSearchResult {
+        private final String message;
+
+        private FixedMessageToolSearchResult(String message) {
+            this.message = message;
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(message);
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainInternal.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainInternal.java
new file mode 100644
index 0000000..a249b74
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolChainInternal.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import org.gradle.platform.base.Platform;
+import org.gradle.platform.base.ToolChain;
+
+public interface ToolChainInternal<T extends Platform> extends ToolChain {
+    /**
+     * Locates the tools that can target the given platform.
+     */
+    ToolProvider select(T targetPlatform);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolProvider.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolProvider.java
new file mode 100644
index 0000000..5c8d2cf
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolProvider.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+
+public interface ToolProvider extends ToolSearchResult {
+    <T extends CompileSpec> Compiler<T> newCompiler(Class<T> spec);
+
+    <T> T get(Class<T> toolType);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolResolver.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolResolver.java
new file mode 100644
index 0000000..c982b7e
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain;
+
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.platform.base.Platform;
+import org.gradle.language.base.internal.compile.Compiler;
+
+public interface ToolResolver {
+    <P extends Platform> ToolSearchResult checkToolAvailability(P requirement);
+
+    <T, P extends Platform> ResolvedTool<T> resolve(Class<T> toolType, P requirement);
+
+    <C extends CompileSpec, P extends Platform> ResolvedTool<Compiler<C>> resolveCompiler(Class<C> specType, P requirement);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolSearchResult.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolSearchResult.java
new file mode 100644
index 0000000..d7543a5
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/toolchain/ToolSearchResult.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.platform.base.internal.toolchain;
+
+import org.gradle.util.TreeVisitor;
+
+public interface ToolSearchResult {
+    boolean isAvailable();
+
+    /**
+     * Writes some diagnostics about why the tool is not available.
+     */
+    void explain(TreeVisitor<? super String> visitor);
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/util/ImplementationTypeDetermer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/util/ImplementationTypeDetermer.java
new file mode 100644
index 0000000..fdc2375
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/util/ImplementationTypeDetermer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.InvalidModelException;
+import org.gradle.platform.base.internal.builder.TypeBuilderInternal;
+
+public class ImplementationTypeDetermer<T, U extends T> {
+
+
+    private final ModelType<U> baseImplementation;
+    private String modelName;
+
+    public ImplementationTypeDetermer(String modelName, Class<U> baseImplementation) {
+        this.modelName = modelName;
+        this.baseImplementation = ModelType.of(baseImplementation);
+    }
+
+    public ModelType<? extends U> determineImplementationType(ModelType<? extends T> type, TypeBuilderInternal<? extends T> builder) {
+        Class<? extends T> implementation = builder.getDefaultImplementation();
+        if (implementation == null) {
+            return null;
+        }
+
+        ModelType<? extends T> implementationType = ModelType.of(implementation);
+        ModelType<? extends U> asSubclass = baseImplementation.asSubclass(implementationType);
+
+        if (asSubclass == null) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must extend '%s'.", StringUtils.capitalize(modelName), implementationType, baseImplementation));
+        }
+
+        if (!type.isAssignableFrom(asSubclass)) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must implement '%s'.", StringUtils.capitalize(modelName), asSubclass, type));
+        }
+
+        try {
+            asSubclass.getRawClass().getConstructor();
+        } catch (NoSuchMethodException nsmException) {
+            throw new InvalidModelException(String.format("%s implementation '%s' must have public default constructor.", StringUtils.capitalize(modelName), asSubclass));
+        }
+
+        return asSubclass;
+    }
+}
+
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/package-info.java
new file mode 100644
index 0000000..b495fd7
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * General purpose types for runtime support.
+ */
+ at Incubating
+package org.gradle.platform.base;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteBinarySpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteBinarySpec.java
new file mode 100644
index 0000000..79ba597
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteBinarySpec.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.BinarySpec;
+
+/**
+ * A binary which runs a suite of tests.
+ */
+ at Incubating
+public interface TestSuiteBinarySpec extends BinarySpec {
+    /**
+     * Returns the test suite that this binary belongs to.
+     */
+    TestSuiteSpec getTestSuite();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteContainer.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteContainer.java
new file mode 100644
index 0000000..b0cd1eb
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteContainer.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.platform.base.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectContainer;
+
+/**
+ * A container of {@link TestSuiteSpec} instances.
+ */
+ at Incubating
+public interface TestSuiteContainer extends NamedDomainObjectContainer<TestSuiteSpec> {
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteSpec.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteSpec.java
new file mode 100644
index 0000000..7dda659
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/TestSuiteSpec.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.ComponentSpec;
+
+/**
+ * A component representing a suite of tests that will be executed together.
+ */
+ at Incubating
+public interface TestSuiteSpec extends ComponentSpec {
+    /**
+     * The tested component.
+     */
+    ComponentSpec getTestedComponent();
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/package-info.java
new file mode 100644
index 0000000..1a059f3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/test/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * General purpose types for test suite support.
+ */
+ at Incubating
+package org.gradle.platform.base.test;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/language-base/src/main/resources/META-INF/gradle-plugins/language-base.properties b/subprojects/platform-base/src/main/resources/META-INF/gradle-plugins/org.gradle.language-base.properties
similarity index 100%
rename from subprojects/language-base/src/main/resources/META-INF/gradle-plugins/language-base.properties
rename to subprojects/platform-base/src/main/resources/META-INF/gradle-plugins/org.gradle.language-base.properties
diff --git a/subprojects/platform-base/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/platform-base/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..2f33673
--- /dev/null
+++ b/subprojects/platform-base/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.platform.base.internal.registry.ComponentModelBaseServiceRegistry
\ No newline at end of file
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/ComponentTypeModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/ComponentTypeModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..3b91dc5
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/ComponentTypeModelRuleExtractorTest.groovy
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal
+
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.ComponentModelBasePlugin
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelActionRole
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.platform.base.*
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultComponentSpecContainer
+import org.gradle.platform.base.internal.registry.AbstractAnnotationModelRuleExtractorTest
+import org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+
+class ComponentTypeModelRuleExtractorTest extends AbstractAnnotationModelRuleExtractorTest {
+    Instantiator instantiator = DirectInstantiator.INSTANCE
+
+    ComponentTypeModelRuleExtractor ruleHandler = new ComponentTypeModelRuleExtractor(instantiator)
+
+    @Override
+    Class<? extends Annotation> getAnnotation() { return ComponentType }
+
+    Class<?> ruleClass = Rules
+
+    def "applies ComponentModelBasePlugin and creates component type rule"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("validTypeRule"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.ACTION
+        registration.actionRole == ModelActionRole.Defaults
+        registration.action.subject == ModelReference.of(DefaultComponentSpecContainer)
+    }
+
+    def "applies ComponentModelBasePlugin only when implementation not set"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("noImplementationSet"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.DEPENDENCIES
+    }
+
+    @Unroll
+    def "decent error message for #descr"() {
+        def ruleMethod = ruleDefinitionForMethod(methodName)
+        def ruleDescription = getStringDescription(ruleMethod)
+
+        when:
+        ruleHandler.registration(ruleMethod)
+
+        then:
+        def ex = thrown(InvalidModelRuleDeclarationException)
+        ex.message == "${ruleDescription} is not a valid component model rule method."
+        ex.cause instanceof InvalidModelException
+        ex.cause.message == expectedMessage
+
+        where:
+        methodName                         | expectedMessage                                                                                                         | descr
+        "extraParameter"                   | "Method annotated with @ComponentType must have a single parameter of type '${ComponentTypeBuilder.name}'."             | "additional rule parameter"
+        "binaryTypeBuilder"                | "Method annotated with @ComponentType must have a single parameter of type '${ComponentTypeBuilder.name}'."             | "wrong builder type"
+        "returnValue"                      | "Method annotated with @ComponentType must not have a return value."                                                    | "method with return type"
+        "implementationSetMultipleTimes"   | "Method annotated with @ComponentType cannot set default implementation multiple times."                                | "implementation set multiple times"
+        "noTypeParam"                      | "Parameter of type '${ComponentTypeBuilder.name}' must declare a type parameter."                                       | "missing type parameter"
+        "wildcardType"                     | "Component type '?' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)."                               | "wildcard type parameter"
+        "extendsType"                      | "Component type '? extends ${ComponentSpec.name}' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)." | "extends type parameter"
+        "superType"                        | "Component type '? super ${ComponentSpec.name}' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)."   | "super type parameter"
+        "notComponentSpec"                 | "Component type '${NotComponentSpec.name}' is not a subtype of '${ComponentSpec.name}'."                                | "type not extending ComponentSpec"
+        "notCustomComponent"               | "Component type '${ComponentSpec.name}' is not a subtype of '${ComponentSpec.name}'."                                   | "type is ComponentSpec"
+        "notImplementingLibraryType"       | "Component implementation '${NotImplementingCustomComponent.name}' must implement '${SomeComponentSpec.name}'."         | "implementation not implementing type class"
+        "notExtendingDefaultSampleLibrary" | "Component implementation '${NotExtendingBaseComponentSpec.name}' must extend '${BaseComponentSpec.name}'."             | "implementation not extending BaseComponentSpec"
+        "noDefaultConstructor"             | "Component implementation '${NoDefaultConstructor.name}' must have public default constructor."                         | "implementation with no public default constructor"
+    }
+
+    interface SomeComponentSpec extends ComponentSpec {}
+
+    static class SomeComponentSpecImpl extends BaseComponentSpec implements SomeComponentSpec {}
+
+    static class SomeComponentSpecOtherImpl extends SomeComponentSpecImpl {}
+
+    interface NotComponentSpec {}
+
+    static class NotImplementingCustomComponent extends BaseComponentSpec implements ComponentSpec {}
+
+    abstract static class NotExtendingBaseComponentSpec implements SomeComponentSpec {}
+
+    static class NoDefaultConstructor extends BaseComponentSpec implements SomeComponentSpec {
+        NoDefaultConstructor(String arg) {
+        }
+    }
+
+    static class Rules {
+        @ComponentType
+        static void validTypeRule(ComponentTypeBuilder<SomeComponentSpec> builder) {
+            builder.defaultImplementation(SomeComponentSpecImpl)
+        }
+
+        @ComponentType
+        static void wildcardType(ComponentTypeBuilder<?> builder) {
+        }
+
+        @ComponentType
+        static void extendsType(ComponentTypeBuilder<? extends ComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void superType(ComponentTypeBuilder<? super ComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void extraParameter(ComponentTypeBuilder<SomeComponentSpec> builder, String otherParam) {
+        }
+
+        @ComponentType
+        static String returnValue(ComponentTypeBuilder<SomeComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void noImplementationSet(ComponentTypeBuilder<SomeComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void implementationSetMultipleTimes(ComponentTypeBuilder<SomeComponentSpec> builder) {
+            builder.defaultImplementation(SomeComponentSpecImpl)
+            builder.defaultImplementation(SomeComponentSpecOtherImpl)
+        }
+
+        @ComponentType
+        static void binaryTypeBuilder(BinaryTypeBuilder<BinarySpec> builder) {
+        }
+
+        @ComponentType
+        static void noTypeParam(ComponentTypeBuilder builder) {
+        }
+
+        @ComponentType
+        static void notComponentSpec(ComponentTypeBuilder<NotComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void notCustomComponent(ComponentTypeBuilder<ComponentSpec> builder) {
+        }
+
+        @ComponentType
+        static void notImplementingLibraryType(ComponentTypeBuilder<SomeComponentSpec> builder) {
+            builder.defaultImplementation(NotImplementingCustomComponent)
+        }
+
+        @ComponentType
+        static void notExtendingDefaultSampleLibrary(ComponentTypeBuilder<SomeComponentSpec> builder) {
+            builder.defaultImplementation(NotExtendingBaseComponentSpec)
+        }
+
+        @ComponentType
+        static void noDefaultConstructor(ComponentTypeBuilder<SomeComponentSpec> builder) {
+            builder.defaultImplementation(NoDefaultConstructor)
+        }
+    }
+}
+
+
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy
new file mode 100644
index 0000000..2759506
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/DefaultFunctionalSourceSetTest.groovy
@@ -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.language.base.internal
+
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.ProjectSourceSet
+import spock.lang.Specification
+
+class DefaultFunctionalSourceSetTest extends Specification {
+    def "has reasonable string representation"() {
+        def sourceSet = new DefaultFunctionalSourceSet("main", Stub(Instantiator), Stub(ProjectSourceSet))
+
+        expect:
+        sourceSet.toString() == /source set 'main'/
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/SourceSetNotationParserTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/SourceSetNotationParserTest.groovy
new file mode 100644
index 0000000..8cd7b45
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/SourceSetNotationParserTest.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.language.base.internal
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import spock.lang.Specification
+
+class SourceSetNotationParserTest extends Specification {
+    def projectSources = Mock(ProjectSourceSet)
+    def parser = SourceSetNotationParser.parser()
+    def languageSourceSet1 = languageSourceSet("lss1")
+    def languageSourceSet2 = languageSourceSet("lss2")
+
+    def "translates single LanguageSourceSet"() {
+        expect:
+        parser.parseNotation(languageSourceSet1) as List == [languageSourceSet1]
+    }
+
+    def "collects all LanguageSourceSets for a FunctionalSourceSet"() {
+        when:
+        def functionalSourceSet = new DefaultFunctionalSourceSet("func", DirectInstantiator.INSTANCE, projectSources)
+        functionalSourceSet.add(languageSourceSet1)
+        functionalSourceSet.add(languageSourceSet2)
+
+        then:
+        parser.parseNotation(functionalSourceSet) as List == [languageSourceSet1, languageSourceSet2]
+    }
+
+    def "collects all LanguageSourceSets in a collection"() {
+        expect:
+        parser.parseNotation([languageSourceSet1, languageSourceSet2]) as List == [languageSourceSet1, languageSourceSet2]
+        parser.parseNotation([languageSourceSet2, languageSourceSet1]) as List == [languageSourceSet2, languageSourceSet1]
+    }
+
+    private LanguageSourceSet languageSourceSet(def name) {
+        Stub(LanguageSourceSet) {
+            getName() >> name
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/registry/LanguageTypeModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/registry/LanguageTypeModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..efd34f1
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/registry/LanguageTypeModelRuleExtractorTest.groovy
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.internal.registry
+
+import org.gradle.api.Action
+import org.gradle.api.Task
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.AbstractBuildableModelElement
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.plugins.ComponentModelBasePlugin
+import org.gradle.language.base.sources.BaseLanguageSourceSet
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelActionRole
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.platform.base.InvalidModelException
+import org.gradle.platform.base.LanguageType
+import org.gradle.platform.base.LanguageTypeBuilder
+import org.gradle.platform.base.internal.registry.AbstractAnnotationModelRuleExtractorTest
+import org.gradle.platform.base.internal.registry.LanguageTypeModelRuleExtractor
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+
+class LanguageTypeModelRuleExtractorTest extends AbstractAnnotationModelRuleExtractorTest {
+
+    Class<?> ruleClass = Rules
+
+    LanguageTypeModelRuleExtractor ruleHandler = new LanguageTypeModelRuleExtractor()
+
+    @Override
+    Class<? extends Annotation> getAnnotation() {
+        return LanguageType
+    }
+
+    @Unroll
+    def "decent error message for #descr"() {
+        def ruleMethod = ruleDefinitionForMethod(methodName)
+        def ruleDescription = getStringDescription(ruleMethod)
+
+        when:
+        ruleHandler.registration(ruleMethod)
+
+        then:
+        def ex = thrown(InvalidModelRuleDeclarationException)
+        ex.message == "${ruleDescription} is not a valid language model rule method."
+        ex.cause instanceof InvalidModelException
+        ex.cause.message == expectedMessage
+
+        where:
+        methodName                          | expectedMessage                                                                                                              | descr
+        "returnValue"                       | "Method annotated with @LanguageType must not have a return value."                                                          | "non void method"
+        "noParams"                          | "Method annotated with @LanguageType must have a single parameter of type '${LanguageTypeBuilder.name}'."                    | "no LanguageTypeBuilder subject"
+        "wrongSubject"                      | "Method annotated with @LanguageType must have a single parameter of type '${LanguageTypeBuilder.name}'."                    | "wrong rule subject type"
+        "rawLanguageTypeBuilder"            | "Parameter of type 'org.gradle.platform.base.LanguageTypeBuilder' must declare a type parameter."                            | "non typed CollectionBuilder parameter"
+        "wildcardLanguageTypeBuilder"       | "Language type '?' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)."                                     | "wild card CollectionBuilder parameter"
+        "notImplementingLibraryType"        | "Language implementation '${NotImplementingCustomLanguageSourceSet.name}' must implement '${CustomLanguageSourceSet.name}'." | "implementation not implementing type class"
+        "wrongSubType"                      | "Language type 'java.lang.String' is not a subtype of 'org.gradle.language.base.LanguageSourceSet'."                         | "implementation not extending BaseComponentSpec"
+        "notExtendingBaseLanguageSourceSet" | "Language implementation '${NotExtendingBaseLanguageSourceSet.name}' must extend '${BaseLanguageSourceSet.name}'."           | "implementation not extending ${BaseLanguageSourceSet.name}"
+        "noPublicCtorImplementation"        | "Language implementation '${ImplementationWithNoPublicConstructor.name}' must have public default constructor."              | "implementation with not public default constructor"
+    }
+
+    def "applies ComponentModelBasePlugin and creates language type rule"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("validTypeRule"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.ACTION
+        registration.actionRole == ModelActionRole.Defaults
+        registration.action.subject == ModelReference.of(LanguageRegistry)
+    }
+
+    def "only applies ComponentModelBasePlugin when implementation not set"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("noImplementationTypeRule"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.DEPENDENCIES
+    }
+
+    interface CustomLanguageSourceSet extends LanguageSourceSet {}
+
+    static class ImplementingCustomLanguageSourceSet extends BaseLanguageSourceSet implements CustomLanguageSourceSet {
+    }
+
+    static class ImplementationWithNoPublicConstructor extends BaseLanguageSourceSet implements CustomLanguageSourceSet {
+        public ImplementationWithNoPublicConstructor(String someString) {}
+    }
+
+    class NotImplementingCustomLanguageSourceSet extends BaseLanguageSourceSet {}
+
+    class NotExtendingBaseLanguageSourceSet extends AbstractBuildableModelElement implements CustomLanguageSourceSet {
+        @Override
+        String getDisplayName() {
+            return null
+        }
+
+        @Override
+        SourceDirectorySet getSource() {
+            return null
+        }
+
+        @Override
+        void source(Action<? super SourceDirectorySet> config) {
+        }
+
+        @Override
+        void generatedBy(Task generatorTask) {
+            }
+
+        @Override
+        String getName() {
+            return null
+        }
+    }
+
+    static class Rules {
+
+        @LanguageType
+        String returnValue(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+            return null
+        }
+
+        @LanguageType
+        void noParams() {
+        }
+
+        @LanguageType
+        void wrongSubject(LanguageSourceSet sourcet) {
+        }
+
+        @LanguageType
+        void rawLanguageTypeBuilder(LanguageTypeBuilder builder) {
+        }
+
+        @LanguageType
+        void wildcardLanguageTypeBuilder(LanguageTypeBuilder<?> builder) {
+        }
+
+        @LanguageType
+        void wrongSubType(LanguageTypeBuilder<String> languageBuilder) {
+        }
+
+        @LanguageType
+        void notExtendingBaseLanguageSourceSet(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+            languageBuilder.defaultImplementation(NotExtendingBaseLanguageSourceSet)
+        }
+
+        @LanguageType
+        void notImplementingLibraryType(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+            languageBuilder.defaultImplementation(NotImplementingCustomLanguageSourceSet)
+        }
+
+        @LanguageType
+        void noImplementationTypeRule(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+        }
+
+        @LanguageType
+        void noPublicCtorImplementation(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+            languageBuilder.defaultImplementation(ImplementationWithNoPublicConstructor)
+        }
+
+        @LanguageType
+        void validTypeRule(LanguageTypeBuilder<CustomLanguageSourceSet> languageBuilder) {
+            languageBuilder.defaultImplementation(ImplementingCustomLanguageSourceSet)
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/tasks/SimpleStaleClassCleanerTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/tasks/SimpleStaleClassCleanerTest.groovy
new file mode 100644
index 0000000..6e86868
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/internal/tasks/SimpleStaleClassCleanerTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.base.internal.tasks
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.TaskOutputsInternal
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class SimpleStaleClassCleanerTest extends Specification {
+    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    private final outputs = Mock(TaskOutputsInternal)
+    private final SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(outputs)
+    
+    def deletesAllPreviousOutputFiles() {
+        def file1 = tmpDir.file('file1').createFile()
+        def file2 = tmpDir.file('file2').createFile()
+        cleaner.destinationDir = tmpDir.testDirectory
+
+        when:
+        cleaner.execute()
+
+        then:
+        !file1.exists()
+        !file2.exists()
+        1 * outputs.previousFiles >> { [iterator: { [file1, file2].iterator() }] as FileCollection }
+
+        and:
+        cleaner.didWork
+    }
+
+    def doesNotDeleteFilesWhichAreNotUnderTheDestinationDir() {
+        def destDir = tmpDir.file('dir')
+        def file1 = destDir.file('file1').createFile()
+        def file2 = tmpDir.file('file2').createFile()
+        cleaner.destinationDir = destDir
+
+        when:
+        cleaner.execute()
+
+        then:
+        !file1.exists()
+        file2.exists()
+        1 * outputs.previousFiles >> { [iterator: { [file1, file2].iterator() }] as FileCollection }
+
+        and:
+        cleaner.didWork
+    }
+
+    def reportsWhenNoWorkDone() {
+        cleaner.destinationDir = tmpDir.file('dir')
+
+        when:
+        cleaner.execute()
+
+        then:
+        1 * outputs.previousFiles >> { [iterator: { [].iterator() }] as FileCollection }
+
+        and:
+        !cleaner.didWork
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/ComponentModelBasePluginTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/ComponentModelBasePluginTest.groovy
new file mode 100644
index 0000000..037b4a7
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/ComponentModelBasePluginTest.groovy
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.plugins
+
+import org.gradle.api.Action
+import org.gradle.api.NamedDomainObjectFactory
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.internal.SourceTransformTaskConfig
+import org.gradle.language.base.internal.registry.LanguageRegistration
+import org.gradle.language.base.internal.registry.LanguageTransform
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.platform.base.BinarySpec
+import org.gradle.platform.base.TransformationFileType
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.ComponentSpecInternal
+import org.gradle.util.TestUtil
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+
+class ComponentModelBasePluginTest extends Specification {
+    def project = TestUtil.createRootProject()
+
+    def "adds componentSpecs extension"() {
+        when:
+        project.pluginManager.apply(ComponentModelBasePlugin)
+        then:
+        project.componentSpecs != null
+    }
+
+    def "adds componentSpecs model"() {
+        when:
+        project.pluginManager.apply(ComponentModelBasePlugin)
+        then:
+        project.modelRegistry.get(ModelPath.path("components")) != null
+    }
+
+    def "registers language sourceset factory and created default source set for component"() {
+        setup:
+
+        def componentSpecInternal = Mock(ComponentSpecInternal)
+        _ * componentSpecInternal.name >> "testComponent"
+        _ * componentSpecInternal.inputTypes >> [TestTransformFile.class]
+
+        def componentFunctionalSourceSet = Mock(FunctionalSourceSet)
+        _ * componentFunctionalSourceSet.name >> "testComponentSources"
+        _ * componentSpecInternal.sources >> componentFunctionalSourceSet
+        _ * componentSpecInternal.source >> WrapUtil.toDomainObjectSet(LanguageSourceSet)
+
+        when:
+        project.pluginManager.apply(ComponentModelBasePlugin)
+        project.model {
+            languages {
+                add(new TestLanguageRegistration())
+            }
+            languageTransforms {
+                add(new TestLanguageRegistration())
+            }
+        }
+        project.componentSpecs.add(componentSpecInternal)
+        project.tasks.realize()
+
+        then:
+        1 * componentFunctionalSourceSet.registerFactory(TestSourceSet, _ as NamedDomainObjectFactory)
+        1 * componentFunctionalSourceSet.maybeCreate("test", TestSourceSet)
+        1 * componentFunctionalSourceSet.getName() >> "testFunctionalSourceSet"
+        0 * componentFunctionalSourceSet._
+    }
+
+    public static class TestLanguageRegistration implements LanguageRegistration, LanguageTransform {
+        @Override
+        String getName() {
+            return "test"
+        }
+
+        @Override
+        Class getSourceSetType() {
+            return TestSourceSet.class
+        }
+
+        @Override
+        Map<String, Class<?>> getBinaryTools() {
+            return null
+        }
+
+        @Override
+        Class<? extends TransformationFileType> getOutputType() {
+            return TestTransformFile.class
+        }
+
+        @Override
+        SourceTransformTaskConfig getTransformTask() {
+            return null
+        }
+
+        @Override
+        boolean applyToBinary(BinarySpec binary) {
+            return false
+        }
+
+        @Override
+        public NamedDomainObjectFactory getSourceSetFactory(String parentName) {
+            return new NamedDomainObjectFactory() {
+                @Override
+                Object create(String name) {
+                    new TestSourceImplementation(name, parentName, fileResolver)
+                }
+            }
+        }
+    }
+
+    public static class TestSourceImplementation implements TestSourceSet {
+        String name
+
+        public TestSourceImplementation(String name, String parent, FileResolver fileResolver) {
+            this.name = name;
+        }
+
+        @Override
+        String getName() {
+            return name;
+        }
+
+        FileCollection getCompileClasspath() {
+            return null
+        }
+
+        void setCompileClasspath(FileCollection classpath) {
+
+        }
+
+        FileCollection getRuntimeClasspath() {
+            return null
+        }
+
+        void setRuntimeClasspath(FileCollection classpath) {
+
+        }
+
+        def getOutput() {
+            return null
+        }
+
+        TestSourceSet compiledBy(Object... taskPaths) {
+            return null
+        }
+
+        SourceDirectorySet getResources() {
+            return null
+        }
+
+        TestSourceSet resources(Closure configureClosure) {
+            return null
+        }
+
+        SourceDirectorySet getJava() {
+            return null
+        }
+
+        TestSourceSet java(Closure configureClosure) {
+            return null
+        }
+
+        SourceDirectorySet getAllJava() {
+            return null
+        }
+
+        SourceDirectorySet getAllSource() {
+            return null
+        }
+
+        String getClassesTaskName() {
+            return null
+        }
+
+        String getProcessResourcesTaskName() {
+            return null
+        }
+
+        String getCompileJavaTaskName() {
+            return null
+        }
+
+        String getCompileTaskName(String language) {
+            return null
+        }
+
+        String getJarTaskName() {
+            return null
+        }
+
+        String getTaskName(String verb, String target) {
+            return null
+        }
+
+        String getCompileConfigurationName() {
+            return null
+        }
+
+        String getRuntimeConfigurationName() {
+            return null
+        }
+
+        String getDisplayName() {
+            return null
+        }
+
+        SourceDirectorySet getSource() {
+            return null
+        }
+
+        @Override
+        void source(Action<? super SourceDirectorySet> config) {
+
+        }
+
+        @Override
+        void generatedBy(Task generatorTask) {
+
+        }
+
+        @Override
+        Task getBuildTask() {
+            return null
+        }
+
+        @Override
+        void setBuildTask(Task lifecycleTask) {
+
+        }
+
+        @Override
+        void builtBy(Object... tasks) {
+
+        }
+
+        @Override
+        boolean hasBuildDependencies() {
+            return false
+        }
+
+        @Override
+        TaskDependency getBuildDependencies() {
+            return null
+        }
+    }
+
+    public static class TestTransformFile implements TransformationFileType {
+
+    }
+
+    public static interface TestSourceSet extends LanguageSourceSet {
+    }
+
+    public static class TestComponentSpecInternal extends BaseComponentSpec implements ComponentSpecInternal {
+        @Override
+        Set<Class<? extends TransformationFileType>> getInputTypes() {
+            return new HashSet<Class<? extends TransformationFileType>>(0)
+        }
+    }
+
+    public static class TestComponentSpec extends BaseComponentSpec {
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LanguageBasePluginTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LanguageBasePluginTest.groovy
new file mode 100644
index 0000000..4df570f
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LanguageBasePluginTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.plugins
+
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.platform.base.BinaryContainer
+import org.gradle.platform.base.internal.BinarySpecInternal
+import org.gradle.platform.base.internal.DefaultBinaryContainer
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class LanguageBasePluginTest extends Specification {
+    DefaultProject project = TestUtil.createRootProject()
+
+    def setup() {
+        project.pluginManager.apply(LanguageBasePlugin)
+    }
+
+    def "adds a 'binaries' container to the project"() {
+        expect:
+        project.extensions.findByName("binaries") instanceof BinaryContainer
+    }
+
+    def "adds a 'sources' container to the project"() {
+        expect:
+        project.extensions.findByName("sources") instanceof ProjectSourceSet
+    }
+
+    def "copies binary tasks into task container"() {
+        def tasks = Mock(TaskContainer)
+        def binaries = new DefaultBinaryContainer(DirectInstantiator.INSTANCE)
+        def binary = Mock(BinarySpecInternal)
+        def binaryTasks = new DefaultBinaryTasksCollection(binary, Mock(ITaskFactory))
+        def someTask = Mock(Task) { getName() >> "someTask" }
+        def buildTask = Mock(Task) { getName() >> "lifecycleTask" }
+        binaryTasks.add(someTask)
+
+        when:
+        binaries.add(binary)
+        def rules = new LanguageBasePlugin.Rules()
+        rules.copyBinaryTasksToTaskContainer(tasks, binaries)
+
+        then:
+        binary.name >> "binaryName"
+        binary.toString() >> "binary foo"
+        binary.getTasks() >> binaryTasks
+        binary.getBuildTask() >> buildTask
+
+        and:
+        1 * tasks.addAll(binaryTasks)
+        1 * tasks.add(buildTask)
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LifecycleBasePluginTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LifecycleBasePluginTest.groovy
new file mode 100644
index 0000000..247cdf0
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/language/base/plugins/LifecycleBasePluginTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.base.plugins
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Task
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.tasks.Delete
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
+import static org.gradle.language.base.plugins.LifecycleBasePlugin.*
+import static org.hamcrest.Matchers.instanceOf
+
+class LifecycleBasePluginTest extends Specification {
+    private final DefaultProject project = TestUtil.createRootProject()
+
+    public void createsTasksAndAppliesMappings() {
+        when:
+        project.pluginManager.apply(LifecycleBasePlugin)
+
+        then:
+        def clean = project.tasks[CLEAN_TASK_NAME]
+        clean instanceOf(Delete)
+        clean dependsOn()
+        clean.targetFiles.files == [project.buildDir] as Set
+
+        and:
+        def assemble = project.tasks[ASSEMBLE_TASK_NAME]
+        assemble.group == BUILD_GROUP
+        assemble instanceOf(DefaultTask)
+
+        and:
+        def check = project.tasks[CHECK_TASK_NAME]
+        check.group == VERIFICATION_GROUP
+        check instanceOf(DefaultTask)
+
+        and:
+        def build = project.tasks[BUILD_TASK_NAME]
+        build.group == LifecycleBasePlugin.BUILD_GROUP
+        build dependsOn(ASSEMBLE_TASK_NAME, CHECK_TASK_NAME)
+        check instanceOf(DefaultTask)
+    }
+
+    public void addsACleanRule() {
+        given:
+        Task test = project.task('test')
+        test.outputs.files(project.buildDir)
+
+        when:
+        project.pluginManager.apply(LifecycleBasePlugin)
+
+        then:
+        Task cleanTest = project.tasks['cleanTest']
+        cleanTest instanceOf(Delete)
+        cleanTest.delete == [test.outputs.files] as Set
+    }
+
+    public void cleanRuleIsCaseSensitive() {
+        given:
+        project.task('testTask')
+        project.task('12')
+
+        when:
+        project.pluginManager.apply(LifecycleBasePlugin)
+
+        then:
+        project.tasks.findByName('cleantestTask') == null
+        project.tasks.findByName('cleanTesttask') == null
+        project.tasks.findByName('cleanTestTask') instanceof Delete
+        project.tasks.findByName('clean12') instanceof Delete
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/binary/BaseBinarySpecTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/binary/BaseBinarySpecTest.groovy
new file mode 100644
index 0000000..ef4753b
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/binary/BaseBinarySpecTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.binary
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.platform.base.ModelInstantiationException
+import spock.lang.Specification
+
+class BaseBinarySpecTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+
+    def "cannot instantiate directly"() {
+        when:
+        new BaseBinarySpec() {}
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Direct instantiation of a BaseBinarySpec is not permitted. Use a BinaryTypeBuilder instead."
+    }
+
+    def "cannot create instance of base class"() {
+        when:
+        BaseBinarySpec.create(BaseBinarySpec, "sampleBinary", instantiator, Mock(ITaskFactory))
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Cannot create instance of abstract class BaseBinarySpec."
+    }
+
+    def "binary has name and sensible display name"() {
+        def binary = BaseBinarySpec.create(MySampleBinary, "sampleBinary", instantiator, Mock(ITaskFactory))
+
+        expect:
+        binary.class == MySampleBinary
+        binary.name == "sampleBinary"
+        binary.displayName == "MySampleBinary 'sampleBinary'"
+    }
+
+    def "create fails if subtype does not have a public no-args constructor"() {
+        when:
+        BaseBinarySpec.create(MyConstructedBinary, "sampleBinary", instantiator, Mock(ITaskFactory))
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Could not create binary of type MyConstructedBinary"
+        e.cause instanceof IllegalArgumentException
+        e.cause.message.startsWith "Could not find any public constructor for class"
+    }
+
+    static class MySampleBinary extends BaseBinarySpec {
+    }
+    static class MyConstructedBinary extends BaseBinarySpec {
+        MyConstructedBinary(String arg) {}
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/component/BaseComponentSpecTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/component/BaseComponentSpecTest.groovy
new file mode 100644
index 0000000..f69494b
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/component/BaseComponentSpecTest.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.component
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.platform.base.ComponentSpecIdentifier
+import org.gradle.platform.base.ModelInstantiationException
+import spock.lang.Specification
+
+class BaseComponentSpecTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+    def componentId = Mock(ComponentSpecIdentifier)
+    FunctionalSourceSet functionalSourceSet;
+
+    def setup() {
+        functionalSourceSet = new DefaultFunctionalSourceSet("testFSS", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet));
+    }
+
+    def "cannot instantiate directly"() {
+        when:
+        new BaseComponentSpec() {}
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Direct instantiation of a BaseComponentSpec is not permitted. Use a ComponentTypeBuilder instead."
+    }
+
+    def "cannot create instance of base class"() {
+        when:
+        BaseComponentSpec.create(BaseComponentSpec, componentId, functionalSourceSet, instantiator)
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Cannot create instance of abstract class BaseComponentSpec."
+    }
+
+    def "library has name, path and sensible display name"() {
+        def component = BaseComponentSpec.create(MySampleComponent, componentId, functionalSourceSet, instantiator)
+
+        when:
+        _ * componentId.name >> "jvm-lib"
+        _ * componentId.projectPath >> ":project-path"
+
+        then:
+        component.class == MySampleComponent
+        component.name == "jvm-lib"
+        component.projectPath == ":project-path"
+        component.displayName == "MySampleComponent 'jvm-lib'"
+    }
+
+    def "create fails if subtype does not have a public no-args constructor"() {
+
+        when:
+        BaseComponentSpec.create(MyConstructedComponent, componentId, functionalSourceSet, instantiator)
+
+        then:
+        def e = thrown ModelInstantiationException
+        e.message == "Could not create component of type MyConstructedComponent"
+        e.cause instanceof IllegalArgumentException
+        e.cause.message.startsWith "Could not find any public constructor for class"
+    }
+
+    def "contains sources of associated main sourceSet"() {
+        when:
+        def lss1 = languageSourceSet("lss1")
+        functionalSourceSet.add(lss1)
+
+        def component = BaseComponentSpec.create(MySampleComponent, componentId, functionalSourceSet, instantiator)
+
+        and:
+        def lss2 = languageSourceSet("lss2")
+        functionalSourceSet.add(lss2)
+
+        then:
+        component.getSource() as List == [lss1, lss2]
+    }
+
+    def languageSourceSet(String name) {
+        Stub(LanguageSourceSet) {
+            getName() >> name
+        }
+    }
+
+    static class MySampleComponent extends BaseComponentSpec {}
+    static class MyConstructedComponent extends BaseComponentSpec {
+        MyConstructedComponent(String arg) {}
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/BuildableModelElementTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/BuildableModelElementTest.groovy
new file mode 100644
index 0000000..ef7f1d7
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/BuildableModelElementTest.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.platform.base.internal
+import org.gradle.api.DefaultTask
+import org.gradle.api.Task
+import org.gradle.api.internal.AbstractBuildableModelElement
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+public class BuildableModelElementTest extends Specification {
+
+    def element = new TestBuildableModelElement()
+    def dependedOn1 = Stub(Task)
+    def dependedOn2 = Stub(Task)
+    def lifecycleTask = TestUtil.createTask(DefaultTask)
+
+    def "has direct dependencies with no lifecycle task set"() {
+        when:
+        element.builtBy(dependedOn1, dependedOn2)
+
+        then:
+        element.getBuildDependencies().getDependencies(Stub(Task)) == [dependedOn1, dependedOn2] as Set
+    }
+
+    def "has intervening lifecycle task as dependency when set"() {
+        when:
+        element.builtBy(dependedOn1)
+        element.setBuildTask(lifecycleTask)
+        element.builtBy(dependedOn2)
+
+        then:
+        element.getBuildDependencies().getDependencies(Stub(Task)) == [lifecycleTask] as Set
+
+        and:
+        lifecycleTask.getTaskDependencies().getDependencies(Stub(Task)) == [dependedOn1, dependedOn2] as Set
+    }
+
+    class TestBuildableModelElement extends AbstractBuildableModelElement {
+
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeTest.groovy
new file mode 100644
index 0000000..5b3d3d3
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryNamingSchemeTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal
+
+import spock.lang.Specification
+
+class DefaultBinaryNamingSchemeTest extends Specification {
+    def "generates task names for native binaries"() {
+        expect:
+        def namingScheme = createNamingScheme(parentName, type, dimensions)
+        namingScheme.getTaskName(verb, target) == taskName
+
+        where:
+        parentName | type   | dimensions     | verb       | target    | taskName
+        "test"     | ""     | []             | null       | null      | "test"
+        "test"     | "type" | []             | null       | null      | "testType"
+        "test"     | "type" | []             | null       | "classes" | "testTypeClasses"
+        "test"     | ""     | []             | null       | "classes" | "testClasses"
+        "test"     | "type" | []             | "assemble" | null      | "assembleTestType"
+        "test"     | "type" | []             | "compile"  | "java"    | "compileTestTypeJava"
+        "test"     | "type" | ["one", "two"] | null       | null      | "oneTwoTestType"
+        "test"     | "type" | ["one", "two"] | null       | "classes" | "oneTwoTestTypeClasses"
+        "test"     | "type" | ["one", "two"] | "assemble" | null      | "assembleOneTwoTestType"
+        "test"     | "type" | ["one", "two"] | "compile"  | "java"    | "compileOneTwoTestTypeJava"
+    }
+
+    def "generates task name with extended inputs"() {
+        expect:
+        def namingScheme = createNamingScheme("theBinary", "theType", ['firstDimension', 'secondDimension'])
+        namingScheme.getTaskName("theVerb", "theTarget") == "theVerbFirstDimensionSecondDimensionTheBinaryTheTypeTheTarget"
+    }
+
+    def "generates base name and output directory"() {
+        def namingScheme = createNamingScheme(parentName, "", dimensions)
+
+        expect:
+        namingScheme.getLifecycleTaskName() == lifecycleName
+        namingScheme.getOutputDirectoryBase() == outputDir
+
+        where:
+        parentName    | dimensions                                 | lifecycleName                               | outputDir
+        "test"        | []                                         | "test"                                      | "test"
+        "test"        | ["one", "two"]                             | "oneTwoTest"                                | "test/oneTwo"
+        "mainLibrary" | ["enterpriseEdition", "osx_x64", "static"] | "enterpriseEditionOsx_x64StaticMainLibrary" | "mainLibrary/enterpriseEditionOsx_x64Static"
+        "mainLibrary" | ["EnterpriseEdition", "Osx_x64", "Static"] | "enterpriseEditionOsx_x64StaticMainLibrary" | "mainLibrary/enterpriseEditionOsx_x64Static"
+    }
+
+    def "generates description"() {
+        def namingScheme = createNamingScheme(parentName, typeName, dimensions)
+
+        expect:
+        namingScheme.getDescription() == lifecycleName
+
+        where:
+        parentName | typeName        | dimensions     | lifecycleName
+        "parent"   | "Executable"    | []             | "executable 'parent:executable'"
+        "parent"   | "SharedLibrary" | []             | "shared library 'parent:sharedLibrary'"
+        "parent"   | "SharedLibrary" | ["one"]        | "shared library 'parent:one:sharedLibrary'"
+        "parent"   | "SharedLibrary" | ["one", "two"] | "shared library 'parent:one:two:sharedLibrary'"
+    }
+
+    private BinaryNamingScheme createNamingScheme(def parentName, def type, def dimensions) {
+        return new DefaultBinaryNamingScheme(parentName, type, dimensions)
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryTasksCollectionTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryTasksCollectionTest.groovy
new file mode 100644
index 0000000..199f8eb
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/DefaultBinaryTasksCollectionTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal
+
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.Task
+import org.gradle.api.UnknownDomainObjectException
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.api.tasks.Copy
+import spock.lang.Specification
+
+class DefaultBinaryTasksCollectionTest extends Specification {
+    def binary = Mock(BinarySpecInternal)
+    def ITaskFactory taskFactory = Mock(ITaskFactory)
+    def tasks = new DefaultBinaryTasksCollection(binary, taskFactory)
+    def task = Mock(Task)
+
+    def "can create task"() {
+        def action = Mock(Action)
+
+        when:
+        tasks.create("foo", DefaultTask, action)
+
+        then:
+        1 * taskFactory.create("foo", DefaultTask)
+        1 * action.execute(_)
+    }
+
+    def "provides lifecycle task for binary"() {
+        when:
+        1 * binary.buildTask >> task
+
+        then:
+        tasks.build == task
+    }
+
+    def "returns null for missing single task with type"() {
+        expect:
+        tasks.findSingleTaskWithType(Copy) == null
+    }
+
+    def "returns single task with type"() {
+        def copyTask = Mock(Copy)
+        when:
+        tasks.add(copyTask)
+
+        then:
+        tasks.findSingleTaskWithType(Copy) == copyTask
+    }
+
+    def "fails finding single task with type where multiple exist"() {
+        def copyTask1 = Mock(Copy)
+        def copyTask2 = Mock(Copy)
+        when:
+        tasks.add(copyTask1)
+        tasks.add(copyTask2)
+
+        and:
+        tasks.findSingleTaskWithType(Copy)
+
+        then:
+        def t = thrown UnknownDomainObjectException
+        t.message == "Multiple tasks with type 'Copy' found."
+    }
+    
+    
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/FixedBuildAbilityTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/FixedBuildAbilityTest.groovy
new file mode 100644
index 0000000..93119e9
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/FixedBuildAbilityTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal
+
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class FixedBuildAbilityTest extends Specification {
+    def "is buildable" () {
+        when:
+        def ability = new FixedBuildAbility(true)
+
+        then:
+        ability.buildable
+    }
+
+    def "is not buildable" () {
+        when:
+        def ability = new FixedBuildAbility(false)
+
+        then:
+        !ability.buildable
+    }
+
+    def "explains not buildable reason" () {
+        TreeVisitor visitor = Mock(TreeVisitor)
+
+        when:
+        def ability = new FixedBuildAbility(false)
+        ability.explain(visitor)
+
+        then:
+        visitor.node("Disabled by user")
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/ToolSearchBuildAbilityTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/ToolSearchBuildAbilityTest.groovy
new file mode 100644
index 0000000..050f77e
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/ToolSearchBuildAbilityTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal
+
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class ToolSearchBuildAbilityTest extends Specification {
+    ToolSearchResult result = Mock(ToolSearchResult)
+    ToolSearchBuildAbility ability = new ToolSearchBuildAbility(result)
+
+    def "is buildable when tool search is successful" () {
+        when:
+        result.isAvailable() >> true
+
+        then:
+        ability.isBuildable()
+    }
+
+    def "is not builadble when tool search is not successful" () {
+        when:
+        result.isAvailable() >> false
+
+        then:
+        !ability.isBuildable()
+    }
+
+    def "explains reason when tool search is not successful" () {
+        TreeVisitor visitor = Mock(TreeVisitor)
+
+        when:
+        result.isAvailable() >> false
+        result.explain(_) >> { TreeVisitor v ->
+            v.node("Tool search failed")
+        }
+        ability.explain(visitor)
+
+        then:
+        visitor.node("Tool search failed")
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/AbstractAnnotationModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/AbstractAnnotationModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..dcb7f3a
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/AbstractAnnotationModelRuleExtractorTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry
+
+import org.gradle.model.internal.inspect.DefaultMethodRuleDefinition
+import org.gradle.model.internal.inspect.MethodRuleDefinition
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+import java.lang.reflect.Method
+
+public abstract class AbstractAnnotationModelRuleExtractorTest extends Specification{
+    def ruleDefinition = Mock(MethodRuleDefinition)
+
+    protected abstract AbstractAnnotationDrivenComponentModelRuleExtractor getRuleHandler();
+
+    abstract Class<? extends Annotation> getAnnotation();
+    abstract Class<?> getRuleClass();
+
+    @Unroll
+    def "handles methods annotated with @#annotationName"() {
+        when:
+        1 * ruleDefinition.getAnnotation(annotation) >> null
+
+        then:
+        !ruleHandler.spec.isSatisfiedBy(ruleDefinition)
+
+
+        when:
+        1 * ruleDefinition.getAnnotation(annotation) >> Mock(annotation)
+
+        then:
+        ruleHandler.spec.isSatisfiedBy(ruleDefinition)
+        where:
+        annotationName << [annotation.getSimpleName()]
+    }
+
+    def ruleDefinitionForMethod(String methodName) {
+        for (Method candidate : ruleClass.getDeclaredMethods()) {
+            if (candidate.getName().equals(methodName)) {
+                return DefaultMethodRuleDefinition.create(ruleClass, candidate)
+            }
+        }
+        throw new IllegalArgumentException("Not a test method name")
+    }
+
+    def getStringDescription(MethodRuleDefinition ruleDefinition) {
+        def builder = new StringBuilder()
+        ruleDefinition.descriptor.describeTo(builder)
+        builder.toString()
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..57e0d23
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTasksModelRuleExtractorTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry
+
+import org.gradle.api.Task
+import org.gradle.language.base.plugins.ComponentModelBasePlugin
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelActionRole
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.platform.base.BinarySpec
+import org.gradle.platform.base.BinaryTasks
+import org.gradle.platform.base.InvalidModelException
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+
+class BinaryTasksModelRuleExtractorTest extends AbstractAnnotationModelRuleExtractorTest {
+
+    BinaryTasksModelRuleExtractor ruleHandler = new BinaryTasksModelRuleExtractor()
+
+    @Override
+    Class<? extends Annotation> getAnnotation() {
+        return BinaryTasks
+    }
+
+    Class<?> ruleClass = Rules.class
+
+    @Unroll
+    def "decent error message for #descr"() {
+        def ruleMethod = ruleDefinitionForMethod(methodName)
+        def ruleDescription = getStringDescription(ruleMethod)
+
+        when:
+        ruleHandler.registration(ruleMethod)
+
+        then:
+        def ex = thrown(InvalidModelRuleDeclarationException)
+        ex.message == "${ruleDescription} is not a valid BinaryTask model rule method."
+        ex.cause instanceof InvalidModelException
+        ex.cause.message == expectedMessage
+
+        where:
+        methodName             | expectedMessage                                                                                                             | descr
+        "returnValue"          | "Method annotated with @BinaryTasks must not have a return value."                                                          | "non void method"
+        "noParams"             | "Method annotated with @BinaryTasks must have a parameter of type '${CollectionBuilder.name}'."                             | "no CollectionBuilder subject"
+        "wrongSubject"         | "Method annotated with @BinaryTasks first parameter must be of type '${CollectionBuilder.name}'."                           | "wrong rule subject type"
+        "noBinaryParameter"    | "Method annotated with @BinaryTasks must have one parameter extending BinarySpec. Found no parameter extending BinarySpec." | "no component spec parameter"
+        "rawCollectionBuilder" | "Parameter of type 'CollectionBuilder' must declare a type parameter extending 'Task'."                                     | "non typed CollectionBuilder parameter"
+    }
+
+    @Unroll
+    def "applies ComponentModelBasePlugin and adds binary task creation rule for plain sample binary"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("validTypeRule"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.ACTION
+        registration.actionRole == ModelActionRole.Defaults
+        registration.action.subject == ModelReference.of("binaries")
+    }
+
+    interface SomeBinary extends BinarySpec {}
+
+    static class Rules {
+
+        @BinaryTasks
+        static String returnValue(CollectionBuilder<Task> builder, SomeBinary binary) {
+        }
+
+        @BinaryTasks
+        static void noParams() {
+        }
+
+        @BinaryTasks
+        static void wrongSubject(binary) {
+        }
+
+        @BinaryTasks
+        static void rawCollectionBuilder(CollectionBuilder tasks, SomeBinary binary) {
+        }
+
+        @BinaryTasks
+        static void noBinaryParameter(CollectionBuilder<Task> builder) {
+        }
+
+        @BinaryTasks
+        static void validTypeRule(CollectionBuilder<Task> tasks, SomeBinary binary) {
+            tasks.create("create${binary.getName()}")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..a20e4fc
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/BinaryTypeModelRuleExtractorTest.groovy
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry
+
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.language.base.plugins.ComponentModelBasePlugin
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelActionRole
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.platform.base.BinarySpec
+import org.gradle.platform.base.BinaryType
+import org.gradle.platform.base.BinaryTypeBuilder
+import org.gradle.platform.base.InvalidModelException
+import org.gradle.platform.base.binary.BaseBinarySpec
+import org.gradle.platform.base.internal.DefaultBinaryContainer
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+
+class BinaryTypeModelRuleExtractorTest extends AbstractAnnotationModelRuleExtractorTest {
+
+    Instantiator instantiator = DirectInstantiator.INSTANCE
+
+    BinaryTypeModelRuleExtractor ruleHandler = new BinaryTypeModelRuleExtractor(instantiator)
+
+    @Override
+    Class<? extends Annotation> getAnnotation() {
+        return BinaryType
+    }
+
+    Class<?> ruleClass = Rules
+
+    def "applies ComponentModelBasePlugin and creates binary type rule"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("validTypeRule"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.ACTION
+        registration.actionRole == ModelActionRole.Defaults
+        registration.action.subject == ModelReference.of(DefaultBinaryContainer)
+    }
+
+    def "applies ComponentModelBasePlugin only when implementation not set"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod("noImplementationSet"))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.DEPENDENCIES
+    }
+
+    @Unroll
+    def "decent error message for #descr"() {
+        def ruleMethod = ruleDefinitionForMethod(methodName)
+        def ruleDescription = getStringDescription(ruleMethod)
+
+        when:
+        ruleHandler.registration(ruleMethod)
+
+        then:
+        def ex = thrown(InvalidModelRuleDeclarationException)
+        ex.message == "${ruleDescription} is not a valid binary model rule method."
+        ex.cause instanceof InvalidModelException
+        ex.cause.message == expectedMessage
+
+        where:
+        methodName                         | expectedMessage                                                                                                        | descr
+        "extraParameter"                   | "Method annotated with @BinaryType must have a single parameter of type '${BinaryTypeBuilder.name}'."                  | "additional rule parameter"
+        "returnValue"                      | "Method annotated with @BinaryType must not have a return value."                                                      | "method with return type"
+        "implementationSetMultipleTimes"   | "Method annotated with @BinaryType cannot set default implementation multiple times."                                  | "implementation set multiple times"
+        "noTypeParam"                      | "Parameter of type '${BinaryTypeBuilder.name}' must declare a type parameter."                                         | "missing type parameter"
+        "notBinarySpec"                    | "Binary type '${NotBinarySpec.name}' is not a subtype of '${BinarySpec.name}'."                                        | "type not extending BinarySpec"
+        "notCustomBinary"                  | "Binary type '${BinarySpec.name}' is not a subtype of '${BinarySpec.name}'."                                           | "type is BinarySpec"
+        "wildcardType"                     | "Binary type '?' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)."                                 | "wildcard type parameter"
+        "extendsType"                      | "Binary type '? extends ${BinarySpec.getName()}' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)." | "extends type parameter"
+        "superType"                        | "Binary type '? super ${BinarySpec.getName()}' cannot be a wildcard type (i.e. cannot use ? super, ? extends etc.)."   | "super type parameter"
+        "notImplementingBinaryType"        | "Binary implementation '${NotImplementingCustomBinary.name}' must implement '${SomeBinarySpec.name}'."                 | "implementation not implementing type class"
+        "notExtendingDefaultSampleLibrary" | "Binary implementation '${NotExtendingBaseBinarySpec.name}' must extend '${BaseBinarySpec.name}'."                     | "implementation not extending BaseBinarySpec"
+        "noDefaultConstructor"             | "Binary implementation '${NoDefaultConstructor.name}' must have public default constructor."                           | "implementation with no public default constructor"
+    }
+
+    interface SomeBinarySpec extends BinarySpec {}
+
+    static class SomeBinarySpecImpl extends BaseBinarySpec implements SomeBinarySpec {}
+
+    static class SomeBinarySpecOtherImpl extends SomeBinarySpecImpl {}
+
+    interface NotBinarySpec {}
+
+    static class NotImplementingCustomBinary extends BaseBinarySpec implements BinarySpec {}
+
+    abstract static class NotExtendingBaseBinarySpec implements BinaryTypeModelRuleExtractorTest.SomeBinarySpec {}
+
+    static class NoDefaultConstructor extends BaseBinarySpec implements SomeBinarySpec {
+        NoDefaultConstructor(String arg) {
+        }
+    }
+
+    static class Rules {
+        @BinaryType
+        static void validTypeRule(BinaryTypeBuilder<SomeBinarySpec> builder) {
+            builder.defaultImplementation(SomeBinarySpecImpl)
+        }
+
+        @BinaryType
+        static void extraParameter(BinaryTypeBuilder<SomeBinarySpec> builder, String otherParam) {
+        }
+
+        @BinaryType
+        static String returnValue(BinaryTypeBuilder<SomeBinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void noImplementationSet(BinaryTypeBuilder<SomeBinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void implementationSetMultipleTimes(BinaryTypeBuilder<SomeBinarySpec> builder) {
+            builder.defaultImplementation(SomeBinarySpecImpl)
+            builder.defaultImplementation(SomeBinarySpecOtherImpl)
+        }
+
+        @BinaryType
+        static void noTypeParam(BinaryTypeBuilder builder) {
+        }
+
+        @BinaryType
+        static void wildcardType(BinaryTypeBuilder<?> builder) {
+        }
+
+        @BinaryType
+        static void extendsType(BinaryTypeBuilder<? extends BinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void superType(BinaryTypeBuilder<? super BinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void notBinarySpec(BinaryTypeBuilder<NotBinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void notCustomBinary(BinaryTypeBuilder<BinarySpec> builder) {
+        }
+
+        @BinaryType
+        static void notImplementingBinaryType(BinaryTypeBuilder<SomeBinarySpec> builder) {
+            builder.defaultImplementation(NotImplementingCustomBinary)
+        }
+
+        @BinaryType
+        static void notExtendingDefaultSampleLibrary(BinaryTypeBuilder<SomeBinarySpec> builder) {
+            builder.defaultImplementation(NotExtendingBaseBinarySpec)
+        }
+
+        @BinaryType
+        static void noDefaultConstructor(BinaryTypeBuilder<SomeBinarySpec> builder) {
+            builder.defaultImplementation(NoDefaultConstructor)
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractorTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractorTest.groovy
new file mode 100644
index 0000000..0087370
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/registry/ComponentBinariesModelRuleExtractorTest.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.registry
+
+import org.gradle.language.base.plugins.ComponentModelBasePlugin
+import org.gradle.model.InvalidModelRuleDeclarationException
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.model.internal.core.DefaultCollectionBuilder
+import org.gradle.model.internal.core.ExtractedModelRule
+import org.gradle.model.internal.core.ModelActionRole
+import org.gradle.model.internal.core.ModelReference
+import org.gradle.platform.base.*
+import spock.lang.Unroll
+
+import java.lang.annotation.Annotation
+
+class ComponentBinariesModelRuleExtractorTest extends AbstractAnnotationModelRuleExtractorTest {
+
+    ComponentBinariesModelRuleExtractor ruleHandler = new ComponentBinariesModelRuleExtractor()
+
+    @Override
+    Class<? extends Annotation> getAnnotation() {
+        return ComponentBinaries
+    }
+
+    Class<?> ruleClass = Rules
+
+    @Unroll
+    def "applies ComponentModelBasePlugin and creates componentBinary rule #descr"() {
+        when:
+        def registration = ruleHandler.registration(ruleDefinitionForMethod(ruleName))
+
+        then:
+        registration.ruleDependencies == [ComponentModelBasePlugin]
+        registration.type == ExtractedModelRule.Type.ACTION
+        registration.actionRole == ModelActionRole.Mutate
+        registration.action.subject == ModelReference.of("binaries", DefaultCollectionBuilder.typeOf(BinarySpec))
+
+        where:
+        ruleName         | descr
+        "rawBinarySpec"  | "for plain BinarySpec"
+        "validTypeRule"  | "for plain sample binary"
+        "librarySubType" | "for library sub types"
+    }
+
+    @Unroll
+    def "decent error message for #descr"() {
+        def ruleMethod = ruleDefinitionForMethod(methodName)
+        def ruleDescription = getStringDescription(ruleMethod)
+
+        when:
+        ruleHandler.registration(ruleMethod)
+
+        then:
+        def ex = thrown(InvalidModelRuleDeclarationException)
+        ex.message == "${ruleDescription} is not a valid ComponentBinaries model rule method."
+        ex.cause instanceof InvalidModelException
+        ex.cause.message == expectedMessage
+
+        where:
+        methodName                | expectedMessage                                                                                                                               | descr
+        "noParams"                | "Method annotated with @ComponentBinaries must have a parameter of type '${CollectionBuilder.name}'."                                         | "no CollectionBuilder parameter"
+        "wrongSubject"            | "Method annotated with @ComponentBinaries first parameter must be of type '${CollectionBuilder.name}'."                                       | "wrong rule subject type"
+        "multipileComponentSpecs" | "Method annotated with @ComponentBinaries must have one parameter extending ComponentSpec. Found multiple parameter extending ComponentSpec." | "additional component spec parameter"
+        "noComponentSpec"         | "Method annotated with @ComponentBinaries must have one parameter extending ComponentSpec. Found no parameter extending ComponentSpec."       | "no component spec parameter"
+        "returnValue"             | "Method annotated with @ComponentBinaries must not have a return value."                                                                      | "non void method"
+        "rawCollectionBuilder"    | "Parameter of type 'CollectionBuilder' must declare a type parameter extending 'BinarySpec'."                                                 | "non typed CollectionBuilder parameter"
+    }
+
+    interface SomeBinarySpec extends BinarySpec {}
+
+    interface SomeLibrary extends ComponentSpec {}
+
+    interface RawLibrary extends ComponentSpec {}
+
+    interface SomeBinarySubType extends SomeBinarySpec {}
+
+    static class Rules {
+        @ComponentBinaries
+        static void noParams() {
+        }
+
+        @ComponentBinaries
+        static void validTypeRule(CollectionBuilder<SomeBinarySpec> binaries, SomeLibrary library) {
+            binaries.create("${library.name}Binary", library)
+        }
+
+        @ComponentBinaries
+        static void rawBinarySpec(CollectionBuilder<BinarySpec> binaries, RawLibrary library) {
+            binaries.create("${library.name}Binary", library)
+        }
+
+        @ComponentBinaries
+        static void rawCollectionBuilder(CollectionBuilder binaries, RawLibrary library) {
+            binaries.create("${library.name}Binary", library)
+        }
+
+        @ComponentBinaries
+        static void librarySubType(CollectionBuilder<SomeBinarySubType> binaries, SomeLibrary library) {
+            binaries.create("${library.name}Binary", library)
+        }
+
+        @ComponentBinaries
+        static void wrongSubject(SomeLibrary library) {
+        }
+
+        @ComponentBinaries
+        static void multipileComponentSpecs(CollectionBuilder<SomeBinarySpec> binaries, SomeLibrary library, SomeLibrary otherLibrary) {
+            binaries.create("${library.name}Binary", library)
+        }
+
+        @ComponentBinaries
+        static void noComponentSpec(CollectionBuilder<SomeBinarySpec> binaries) {
+        }
+
+        @ComponentBinaries
+        static String returnValue(BinaryTypeBuilder<SomeBinarySpec> builder) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainerTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainerTest.groovy
new file mode 100644
index 0000000..e21b688
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/rules/RuleAwarePolymorphicDomainObjectContainerTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.rules
+
+import org.gradle.api.GradleException
+import org.gradle.api.Named
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor
+import spock.lang.Specification
+
+class RuleAwarePolymorphicDomainObjectContainerTest extends Specification {
+
+    def container = new DummyContainer()
+
+    def "reports duplicate type registration that was created without rule context"() {
+        given:
+        container.registerFactory(Dummy, { new Dummy("foo") })
+
+        when:
+        container.registerFactory(Dummy, { new Dummy("other") })
+
+        then:
+        def t = thrown GradleException
+        t.message == "Cannot register a factory for type Dummy because a factory for this type is already registered."
+    }
+
+    def "reports duplicate type registration that was created with rule context"() {
+        given:
+        container.registerFactory(Dummy, { new Dummy(it) }, new SimpleModelRuleDescriptor("<model-rule>"))
+
+        when:
+        container.registerFactory(Dummy, { new Dummy(it) }, new SimpleModelRuleDescriptor("<other-rule>"))
+
+        then:
+        def t = thrown GradleException
+        t.message == "Cannot register a factory for type Dummy because a factory for this type was already registered by <model-rule>."
+    }
+
+    class DummyContainer extends RuleAwarePolymorphicDomainObjectContainer<Object> {
+        DummyContainer() {
+            super(Dummy.class, DirectInstantiator.INSTANCE)
+        }
+    }
+
+    class Dummy implements Named {
+        String name
+
+        Dummy(String name) {
+            this.name = name
+        }
+    }
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ArgWriterTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ArgWriterTest.groovy
new file mode 100755
index 0000000..037fa4f
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ArgWriterTest.groovy
@@ -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.platform.base.internal.toolchain
+
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class ArgWriterTest extends Specification {
+    final StringWriter writer = new StringWriter()
+    final PrintWriter printWriter = new PrintWriter(writer, true)
+    final ArgWriter argWriter = ArgWriter.unixStyle(printWriter)
+
+    def "writes single argument to line"() {
+        when:
+        argWriter.args("-nologo")
+
+        then:
+        writer.toString() == toPlatformLineSeparators("-nologo\n")
+    }
+
+    def "writes multiple arguments to line"() {
+        when:
+        argWriter.args("-I", "some/dir")
+
+        then:
+        writer.toString() == toPlatformLineSeparators("-I some/dir\n")
+    }
+
+    def "quotes argument with whitespace"() {
+        when:
+        argWriter.args("ab c", "d e f")
+
+        then:
+        writer.toString() == toPlatformLineSeparators('"ab c" "d e f"\n')
+    }
+
+    def "escapes double quotes in argument"() {
+        when:
+        argWriter.args('"abc"', 'a" bc')
+
+        then:
+        writer.toString() == toPlatformLineSeparators('\\"abc\\" "a\\" bc"\n')
+    }
+
+    def "escapes backslash in argument"() {
+        when:
+        argWriter.args('a\\b', 'a \\ bc')
+
+        then:
+        writer.toString() == toPlatformLineSeparators('a\\\\b "a \\\\ bc"\n')
+    }
+
+    def "does not escape characters in windows style"() {
+        def argWriter = ArgWriter.windowsStyle(printWriter)
+
+        when:
+        argWriter.args('a\\b', 'a "\\" bc')
+
+        then:
+        writer.toString() == toPlatformLineSeparators('a\\b "a "\\" bc"\n')
+    }
+
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/DefaultToolResolverTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/DefaultToolResolverTest.groovy
new file mode 100644
index 0000000..25d4f27
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/DefaultToolResolverTest.groovy
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.platform.base.internal.toolchain
+
+import org.gradle.language.base.internal.compile.CompileSpec
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.platform.base.Platform
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class DefaultToolResolverTest extends Specification {
+    def "can find an available toolchain" () {
+        def toolChains = [
+                Mock(ToolChainInternal) {
+                    select(_) >> Mock(ToolProvider) {
+                        isAvailable() >> response1
+                    }
+                },
+                Mock(ToolChainInternal) {
+                    select(_) >> Mock(ToolProvider) {
+                        isAvailable() >> response2
+                    }
+                }
+        ]
+        DefaultToolResolver resolver = new DefaultToolResolver()
+        toolChains.each { resolver.registerToolChain(it) }
+
+        expect:
+        resolver.checkToolAvailability(Stub(Platform)).available == availability
+
+        where:
+        response1 | response2 | availability
+        false     | true      | true
+        true      | false     | true
+        false     | false     | false
+    }
+
+    def "no available toolchains explains all ToolSearchResults" () {
+        def provider1 = Mock(ToolProvider) {
+            isAvailable() >> false
+        }
+        def toolChain1 = Mock(ToolChainInternal) {
+            select(_) >> provider1
+        }
+        def provider2 = Mock(ToolProvider) {
+            isAvailable() >> false
+        }
+        def toolChain2 = Mock(ToolChainInternal) {
+            select(_) >> provider2
+        }
+
+        DefaultToolResolver resolver = new DefaultToolResolver()
+        resolver.registerToolChain(toolChain1)
+        resolver.registerToolChain(toolChain2)
+        ToolSearchResult result = resolver.checkToolAvailability(Stub(Platform))
+        TreeVisitor visitor = Stub(TreeVisitor)
+
+        when:
+        result.explain(visitor)
+
+        then:
+        provider1.explain(visitor)
+        provider2.explain(visitor)
+    }
+
+    def "can resolve an available compiler" () {
+        def compileSpec = new TestCompileSpec() {}
+        def provider = Mock(ToolProvider) {
+            isAvailable() >> true
+        }
+        def toolChain = Mock(ToolChainInternal) {
+            select(_) >> provider
+        }
+        DefaultToolResolver resolver = new DefaultToolResolver()
+        resolver.registerToolChain(toolChain)
+
+        when:
+        ResolvedTool<Compiler<TestCompileSpec>> tool = resolver.resolveCompiler(TestCompileSpec.class, Stub(Platform))
+
+        then:
+        tool.available
+
+        when:
+        tool.get()
+
+        then:
+        1 * provider.newCompiler(TestCompileSpec.class)
+    }
+
+    def "can resolve an available tool" () {
+        def provider = Mock(ToolProvider) {
+            isAvailable() >> true
+        }
+        def toolChain = Mock(ToolChainInternal) {
+            select(_) >> provider
+        }
+        DefaultToolResolver resolver = new DefaultToolResolver()
+        resolver.registerToolChain(toolChain)
+
+        when:
+        ResolvedTool<String> tool = resolver.resolve(String.class, Stub(Platform))
+
+        then:
+        tool.available
+
+        when:
+        tool.get()
+
+        then:
+        1 * provider.get(String.class)
+    }
+
+    private static interface TestCompileSpec extends CompileSpec {}
+}
diff --git a/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ToolChainAvailabilityTest.groovy b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ToolChainAvailabilityTest.groovy
new file mode 100644
index 0000000..410da56
--- /dev/null
+++ b/subprojects/platform-base/src/test/groovy/org/gradle/platform/base/internal/toolchain/ToolChainAvailabilityTest.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.platform.base.internal.toolchain
+
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class ToolChainAvailabilityTest extends Specification {
+    def "visits message"() {
+        def visitor = Mock(TreeVisitor)
+
+        given:
+        def availability = new ToolChainAvailability()
+        availability.unavailable("some reason")
+
+        when:
+        availability.explain(visitor)
+
+        then:
+        visitor.node("some reason")
+    }
+
+    def "ignores available tool"() {
+        def searchResult = Mock(ToolSearchResult)
+
+        given:
+        searchResult.available >> true
+
+        when:
+        def availability = new ToolChainAvailability()
+        availability.mustBeAvailable(searchResult)
+
+        then:
+        availability.available
+    }
+
+    def "visits missing tool"() {
+        def visitor = Mock(TreeVisitor)
+        def searchResult = Mock(ToolSearchResult)
+
+        given:
+        searchResult.available >> false
+
+        and:
+        def availability = new ToolChainAvailability()
+        availability.mustBeAvailable(searchResult)
+
+        when:
+        availability.explain(visitor)
+
+        then:
+        1 * searchResult.explain(visitor)
+    }
+}
diff --git a/subprojects/platform-base/src/testFixtures/groovy/org/gradle/test/fixtures/plugin/AbstractLanguagePluginSpec.groovy b/subprojects/platform-base/src/testFixtures/groovy/org/gradle/test/fixtures/plugin/AbstractLanguagePluginSpec.groovy
new file mode 100644
index 0000000..e8eab26
--- /dev/null
+++ b/subprojects/platform-base/src/testFixtures/groovy/org/gradle/test/fixtures/plugin/AbstractLanguagePluginSpec.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.test.fixtures.plugin
+
+import org.gradle.language.base.internal.registry.LanguageRegistry
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.type.ModelType
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+import spock.lang.Unroll
+
+abstract class AbstractLanguagePluginSpec extends Specification {
+    final def project = TestUtil.createRootProject()
+
+    abstract def getPluginClass()
+
+    abstract def getLanguageSourceSet()
+
+    abstract String getLanguageId()
+
+    @Unroll
+    def "registers #language in language registration"() {
+        when:
+        project.pluginManager.apply(pluginClass)
+        project.evaluate()
+
+
+        then:
+        def languageRegistry = project.modelRegistry.realize(new ModelPath("languages"), ModelType.of(LanguageRegistry))
+        def languageRegistration = languageRegistry.find { it.name == language }
+
+        languageRegistration != null
+        languageRegistration.sourceSetType == languageSourceSet
+
+        where:
+        language = languageId
+    }
+
+}
diff --git a/subprojects/platform-jvm/platform-jvm.gradle b/subprojects/platform-jvm/platform-jvm.gradle
new file mode 100644
index 0000000..c79454b
--- /dev/null
+++ b/subprojects/platform-jvm/platform-jvm.gradle
@@ -0,0 +1,17 @@
+dependencies {
+    compile project(":platformBase")
+    compile project(":core")
+    compile project(":diagnostics")
+
+    testCompile libraries.groovy
+
+    // To pick up JavaToolChainInternal implementation
+    // TODO - get rid of cycle
+    integTestRuntime project(':languageJava')
+}
+
+useTestFixtures()
+useTestFixtures(project: ':diagnostics')
+
+useClassycle()
+strictCompile()
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/ComponentReportIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..f89d1c7
--- /dev/null
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.jvm
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.reporting.components.AbstractComponentReportIntegrationTest
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private JavaVersion currentJvm = JavaVersion.current()
+    private String currentJava = "java" + currentJvm.majorVersion
+    private String currentJdk = String.format("JDK %s (%s)", currentJvm.majorVersion, currentJvm);
+
+    def "shows details of Java library"() {
+        given:
+        buildFile << """
+plugins {
+    id 'jvm-component'
+    id 'java-lang'
+}
+
+model {
+    components {
+        someLib(JvmLibrarySpec)
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+JVM library 'someLib'
+---------------------
+
+Source sets
+    Java source 'someLib:java'
+        src/someLib/java
+    JVM resources 'someLib:resources'
+        src/someLib/resources
+
+Binaries
+    Jar 'someLibJar'
+        build using task: :someLibJar
+        platform: $currentJava
+        tool chain: $currentJdk
+        Jar file: build/jars/someLibJar/someLib.jar
+"""
+    }
+
+    @Requires(TestPrecondition.JDK7_OR_LATER)
+    def "shows details of jvm library with multiple targets"() {
+        given:
+        buildFile << """
+    apply plugin: 'jvm-component'
+    apply plugin: 'java-lang'
+
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "java5"
+                targetPlatform "java6"
+                targetPlatform "java7"
+            }
+        }
+    }
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+JVM library 'myLib'
+-------------------
+
+Source sets
+    Java source 'myLib:java'
+        src/myLib/java
+    JVM resources 'myLib:resources'
+        src/myLib/resources
+
+Binaries
+    Jar 'java5MyLibJar'
+        build using task: :java5MyLibJar
+        platform: java5
+        tool chain: $currentJdk
+        Jar file: build/jars/java5MyLibJar/myLib.jar
+    Jar 'java6MyLibJar'
+        build using task: :java6MyLibJar
+        platform: java6
+        tool chain: $currentJdk
+        Jar file: build/jars/java6MyLibJar/myLib.jar
+    Jar 'java7MyLibJar'
+        build using task: :java7MyLibJar
+        platform: java7
+        tool chain: $currentJdk
+        Jar file: build/jars/java7MyLibJar/myLib.jar
+"""
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    def "shows which jvm libraries are buildable"() {
+        given:
+        buildFile << """
+    apply plugin: 'jvm-component'
+    apply plugin: 'java-lang'
+
+    model {
+        components {
+            myLib(JvmLibrarySpec) {
+                targetPlatform "java5"
+                targetPlatform "java6"
+                targetPlatform "java9"
+            }
+            myLib2(JvmLibrarySpec) {
+                targetPlatform "java6"
+                binaries.all { buildable = false }
+            }
+        }
+    }
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+JVM library 'myLib'
+-------------------
+
+Source sets
+    Java source 'myLib:java'
+        src/myLib/java
+    JVM resources 'myLib:resources'
+        src/myLib/resources
+
+Binaries
+    Jar 'java5MyLibJar'
+        build using task: :java5MyLibJar
+        platform: java5
+        tool chain: $currentJdk
+        Jar file: build/jars/java5MyLibJar/myLib.jar
+    Jar 'java6MyLibJar'
+        build using task: :java6MyLibJar
+        platform: java6
+        tool chain: $currentJdk
+        Jar file: build/jars/java6MyLibJar/myLib.jar
+    Jar 'java9MyLibJar' (not buildable)
+        build using task: :java9MyLibJar
+        platform: java9
+        tool chain: $currentJdk
+        Jar file: build/jars/java9MyLibJar/myLib.jar
+        No tool chains can satisfy the requirement: Could not target platform: 'Java SE 9' using tool chain: '${currentJdk}'.
+
+JVM library 'myLib2'
+--------------------
+
+Source sets
+    Java source 'myLib2:java'
+        src/myLib2/java
+    JVM resources 'myLib2:resources'
+        src/myLib2/resources
+
+Binaries
+    Jar 'myLib2Jar' (not buildable)
+        build using task: :myLib2Jar
+        platform: java6
+        tool chain: $currentJdk
+        Jar file: build/jars/myLib2Jar/myLib2.jar
+        Disabled by user
+"""
+    }
+}
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/JarBinariesIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/JarBinariesIntegrationTest.groovy
new file mode 100644
index 0000000..a41c2c4
--- /dev/null
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/JarBinariesIntegrationTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm
+
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.hamcrest.Matchers
+
+class JarBinariesIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'jvm-component'
+            }
+        """
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    def "assemble task constructs all buildable binaries" () {
+        buildFile << """
+            model {
+                components {
+                    myJvmLib1(JvmLibrarySpec) {
+                        targetPlatform "java9"
+                    }
+                    myJvmLib2(JvmLibrarySpec)
+                }
+            }
+        """
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(":myJvmLib2Jar")
+        notExecuted(":myJvmLib1Jar")
+
+        and:
+        file("build/jars/myJvmLib2Jar/myJvmLib2.jar").assertExists()
+        file("build/jars/myJvmLib1Jar/myJvmLib1.jar").assertDoesNotExist()
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    def "assemble task produces sensible error when there are no buildable binaries" () {
+        buildFile << """
+            model {
+                components {
+                    myJvmLib1(JvmLibrarySpec) {
+                        targetPlatform "java9"
+                    }
+                    myJvmLib2(JvmLibrarySpec) {
+                        targetPlatform "java9"
+                    }
+                    myJvmLib3(JvmLibrarySpec) {
+                        binaries.all { buildable = false }
+                    }
+                }
+            }
+        """
+
+        when:
+        fails "assemble"
+
+        then:
+        failureDescriptionContains("Execution failed for task ':assemble'.")
+        failure.assertThatCause(Matchers.<String>allOf(
+                Matchers.startsWith("No buildable binaries found:"),
+                Matchers.containsString("No tool chains can satisfy the requirement: Could not target platform: 'Java SE 9' using tool chain: 'JDK ${JavaVersion.current().majorVersion} (${JavaVersion.current()})"),
+                Matchers.containsString("Disabled by user")
+        ))
+    }
+}
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/AutoTestedSamplePlatformJvmIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/AutoTestedSamplePlatformJvmIntegrationTest.groovy
new file mode 100644
index 0000000..3cf5f28
--- /dev/null
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/AutoTestedSamplePlatformJvmIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.plugins
+
+import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+class AutoTestedSamplePlatformJvmIntegrationTest extends AbstractAutoTestedSamplesTest {
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/platform-jvm/src/main")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginGoodBehaviourTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginGoodBehaviourTest.groovy
new file mode 100644
index 0000000..80ecb60
--- /dev/null
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginGoodBehaviourTest.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.plugins
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class JvmComponentPluginGoodBehaviourTest extends WellBehavedPluginTest {
+}
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginIntegrationTest.groovy
new file mode 100644
index 0000000..876202c
--- /dev/null
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/plugins/JvmComponentPluginIntegrationTest.groovy
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.plugins
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.JarTestFixture
+import spock.lang.Ignore
+
+class JvmComponentPluginIntegrationTest extends AbstractIntegrationSpec {
+    def "does not create library or binaries when not configured"() {
+        when:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+    task validate << {
+        assert componentSpecs.empty
+        assert binaries.empty
+    }
+"""
+        then:
+        succeeds "validate"
+
+        and:
+        !file("build").exists()
+    }
+
+    def "defines jvm library and binary model objects and lifecycle task"() {
+        when:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myLib(JvmLibrarySpec)
+        }
+    }
+
+    task validate << {
+        assert componentSpecs.size() == 1
+        def myLib = componentSpecs.myLib
+        assert myLib.name == 'myLib'
+        assert myLib == componentSpecs['myLib']
+        assert myLib instanceof JvmLibrarySpec
+
+        assert myLib.sources.size() == 0
+
+        assert binaries.size() == 1
+        assert myLib.binaries as Set == binaries as Set
+
+        def myLibJar = (binaries as List)[0]
+        assert myLibJar instanceof JarBinarySpec
+        assert myLibJar.name == 'myLibJar'
+        assert myLibJar.displayName == "Jar 'myLibJar'"
+
+        def binaryTask = tasks['myLibJar']
+        assert binaryTask.group == 'build'
+        assert binaryTask.description == "Assembles Jar 'myLibJar'."
+        assert myLibJar.buildTask == binaryTask
+
+        def jarTask = tasks['createMyLibJar']
+        assert jarTask instanceof org.gradle.jvm.tasks.Jar
+        assert jarTask.group == null
+        assert jarTask.description == "Creates the binary file for Jar 'myLibJar'."
+    }
+"""
+        then:
+        succeeds "validate"
+    }
+
+    def "creates empty jar when no language sources available"() {
+        given:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myJvmLib(JvmLibrarySpec)
+        }
+    }
+"""
+        when:
+        succeeds "myJvmLibJar"
+
+        then:
+        executed ":createMyJvmLibJar", ":myJvmLibJar"
+
+        and:
+        def jar = new JarTestFixture(file("build/jars/myJvmLibJar/myJvmLib.jar"))
+        jar.hasDescendants()
+    }
+
+    def "can configure jvm binary"() {
+        given:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myJvmLib(JvmLibrarySpec)
+        }
+        jvm {
+            allBinaries { jar ->
+                jar.jarFile = file("\${project.buildDir}/bin/\${jar.name}.bin")
+            }
+        }
+    }
+"""
+        when:
+        succeeds "myJvmLibJar"
+
+        then:
+        file("build/bin/myJvmLibJar.bin").assertExists()
+    }
+
+    @Ignore("Not yet implemented")
+    def "can configure jvm binary for component"() {
+        given:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myJvmLib(JvmLibrarySpec) {
+                binaries.withType(JarBinarySpec) { jar ->
+                    jar.jarFile = file("\${project.buildDir}/bin/\${jar.name}.bin")
+                }
+            }
+        }
+    }
+"""
+        when:
+        succeeds "myJvmLibJar"
+
+        then:
+        file("build/bin/myJvmLibJar.bin").assertExists()
+    }
+
+    def "can specify additional builder tasks for binary"() {
+        given:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myJvmLib(JvmLibrarySpec)
+        }
+    }
+    binaries.all { binary ->
+        def logTask = project.tasks.create("log_\${binary.name}") {
+            doLast {
+                println "Constructing \${binary.displayName}"
+            }
+        }
+        binary.builtBy(logTask)
+    }
+"""
+        when:
+        succeeds "myJvmLibJar"
+
+        then:
+        executed ":createMyJvmLibJar", ":log_myJvmLibJar", ":myJvmLibJar"
+
+        and:
+        output.contains("Constructing Jar 'myJvmLibJar'")
+    }
+
+    def "can define multiple jvm libraries in single project"() {
+        when:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myLibOne(JvmLibrarySpec)
+            myLibTwo(JvmLibrarySpec)
+        }
+    }
+
+    task validate << {
+        assert componentSpecs.size() == 2
+        assert componentSpecs.myLibOne instanceof JvmLibrarySpec
+        assert componentSpecs.myLibTwo instanceof JvmLibrarySpec
+
+        assert binaries.size() == 2
+        assert binaries.myLibOneJar == componentSpecs.myLibOne.binaries[0]
+        assert binaries.myLibTwoJar == componentSpecs.myLibTwo.binaries[0]
+    }
+"""
+        then:
+        succeeds "validate"
+    }
+
+    def "can build multiple jvm libraries in single project"() {
+        given:
+        buildFile << """
+    plugins {
+        id 'jvm-component'
+    }
+
+    model {
+        components {
+            myLibOne(JvmLibrarySpec)
+            myLibTwo(JvmLibrarySpec)
+        }
+    }
+"""
+        when:
+        succeeds "myLibOneJar"
+
+        then:
+        executed ":createMyLibOneJar", ":myLibOneJar"
+        notExecuted ":myLibTwoJar"
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executed ":createMyLibOneJar", ":myLibOneJar", ":createMyLibTwoJar", ":myLibTwoJar"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/Attributes.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/Attributes.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/Attributes.java
rename to subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/Attributes.java
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/Manifest.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/Manifest.java
new file mode 100644
index 0000000..0963f5b
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/Manifest.java
@@ -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.java.archives;
+
+import groovy.lang.Closure;
+
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * Represents the manifest file of a JAR file.
+ */
+public interface Manifest {
+    /**
+     * Returns the main attributes of the manifest.
+     */
+    Attributes getAttributes();
+
+    /**
+     * Returns the sections of the manifest (excluding the main section).
+     *
+     * @return A map with the sections, where the key represents the section name and value the section attributes.
+     */
+    Map<String, Attributes> getSections();
+
+    /**
+     * Adds content to the main attributes of the manifest.
+     *
+     * @param attributes The values to add to the main attributes. The values can be any object. For evaluating the value objects
+     * their {@link Object#toString()} method is used. This is done lazily either before writing or when {@link #getEffectiveManifest()}
+     * is called. 
+     *
+     * @return this
+     * @throws ManifestException If a key is invalid according to the manifest spec or if a key or value is null.
+     */
+    Manifest attributes(Map<String, ?> attributes) throws ManifestException;
+
+    /**
+     * Adds content to the given section of the manifest.
+     * 
+     * @param attributes The values to add to the section. The values can be any object. For evaluating the value objects
+     * their {@link Object#toString()} method is used. This is done lazily either before writing or when {@link #getEffectiveManifest()}
+     * is called.
+     * @param sectionName The name of the section
+     *
+     * @return this
+     * @throws ManifestException If a key is invalid according to the manifest spec or if a key or value is null.
+     */
+    Manifest attributes(Map<String, ?> attributes, String sectionName) throws ManifestException;
+
+    /**
+     * Returns a new manifest instance where all the attribute values are expanded (e.g. their toString method is called).
+     * The returned manifest also contains all the attributes of the to be merged manifests specified in {@link #from(Object...)}.
+     */
+    Manifest getEffectiveManifest();
+
+    /**
+     * Writes the manifest into a writer.
+     *
+     * @param writer The writer to write the manifest to
+     * @return this
+     */
+    Manifest writeTo(Writer writer);
+
+    /**
+     * Writes the manifest into a file. The path's are resolved as defined by {@link org.gradle.api.Project#files(Object...)}
+     *
+     * @param path The path of the file to write the manifest into.
+     * @return this
+     */
+    Manifest writeTo(Object path);
+
+    /**
+     * Specifies other manifests to be merged into this manifest. A merge path can either be another instance of
+     * {@link org.gradle.api.java.archives.Manifest} or a file path as interpreted by {@link org.gradle.api.Project#files(Object...)}.
+     *
+     * The merge is not happening instantaneously. It happens either before writing or when {@link #getEffectiveManifest()}
+     * is called.
+     *
+     * @param mergePath
+     * @return this
+     */
+    Manifest from(Object... mergePath);
+
+    /**
+     * Specifies other manifests to be merged into this manifest. A merge path is interpreted as described in
+     * {@link #from(Object...)}.
+     *
+     * The merge is not happening instantaneously. It happens either before writing or when {@link #getEffectiveManifest()}
+     * is called.
+     *
+     * The closure configures the underlying {@link org.gradle.api.java.archives.ManifestMergeSpec}.
+     *
+     * @param mergePath
+     * @param closure
+     * @return this
+     */
+    Manifest from(Object mergePath, Closure<?> closure);
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestException.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestException.java
rename to subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestMergeDetails.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java
rename to subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestMergeDetails.java
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestMergeSpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestMergeSpec.java
new file mode 100644
index 0000000..121d6ea
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestMergeSpec.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.java.archives;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+
+/**
+ * Specifies how the entries of multiple manifests should be merged together.
+ */
+public interface ManifestMergeSpec {
+    /**
+     * Adds a merge path to a manifest that should be merged into the base manifest. A merge path can be either another
+     * {@link org.gradle.api.java.archives.Manifest} or a path that is evaluated as per
+     * {@link org.gradle.api.Project#files(Object...)} . If multiple merge paths are specified, the manifest are merged
+     * in the order in which they are added.
+     * 
+     * @param mergePaths The paths of manifests to be merged
+     * @return this
+     */
+    ManifestMergeSpec from(Object... mergePaths);
+
+    /**
+     * Adds an action to be applied to each key-value tuple in a merge operation. If multiple merge paths are specified,
+     * the action is called for each key-value tuple of each merge operation. The given action is called with a
+     * {@link org.gradle.api.java.archives.ManifestMergeDetails} as its parameter. Actions are executed
+     * in the order added.
+     *
+     * @param mergeAction A merge action to be executed.
+     * @return this
+     */
+    ManifestMergeSpec eachEntry(Action<? super ManifestMergeDetails> mergeAction);
+
+    /**
+     * Adds an action to be applied to each key-value tuple in a merge operation. If multiple merge paths are specified,
+     * the action is called for each key-value tuple of each merge operation. The given closure is called with a
+     * {@link org.gradle.api.java.archives.ManifestMergeDetails} as its parameter. Actions are executed
+     * in the order added.
+     *
+     * @param mergeAction The action to execute.
+     * @return this
+     */
+    ManifestMergeSpec eachEntry(Closure<?> mergeAction);
+}
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultAttributes.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultAttributes.java
new file mode 100644
index 0000000..d01aaa2
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultAttributes.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.java.archives.internal;
+
+import org.gradle.api.java.archives.Attributes;
+import org.gradle.api.java.archives.ManifestException;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class DefaultAttributes implements Attributes {
+    protected Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+
+    public int size() {
+        return attributes.size();
+    }
+
+    public boolean isEmpty() {
+        return attributes.isEmpty();
+    }
+
+    public boolean containsKey(Object key) {
+        return attributes.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return attributes.containsValue(value);
+    }
+
+    public Object get(Object key) {
+        return attributes.get(key);
+    }
+
+    public Object put(String key, Object value) {
+        if (key == null) {
+            throw new ManifestException("The key of a manifest attribute must not be null.");
+        }
+        if (value == null) {
+            throw new ManifestException(String.format("The value of a manifest attribute must not be null (Key=%s).", key));
+        }
+        try {
+            new java.util.jar.Attributes.Name(key);
+        } catch (IllegalArgumentException e) {
+            throw new ManifestException(String.format("The Key=%s violates the Manifest spec!", key));
+        }
+        return attributes.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return attributes.remove(key);
+    }
+
+    public void putAll(Map<? extends String, ? extends Object> m) {
+        for (Entry<? extends String, ? extends Object> entry : m.entrySet()) {
+            put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    public void clear() {
+        attributes.clear();
+    }
+
+    public Set<String> keySet() {
+        return attributes.keySet();
+    }
+
+    public Collection<Object> values() {
+        return attributes.values();
+    }
+
+    public Set<Entry<String, Object>> entrySet() {
+        return attributes.entrySet();
+    }
+
+    public boolean equals(Object o) {
+        return attributes.equals(o);
+    }
+
+    public int hashCode() {
+        return attributes.hashCode();
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifest.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifest.java
new file mode 100644
index 0000000..f03f992
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.java.archives.internal;
+
+import groovy.lang.Closure;
+import org.apache.tools.ant.taskdefs.Manifest;
+import org.apache.tools.ant.taskdefs.Manifest.Attribute;
+import org.apache.tools.ant.taskdefs.Manifest.Section;
+import org.apache.tools.ant.taskdefs.ManifestException;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.ErroringAction;
+import org.gradle.internal.IoActions;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.java.archives.Attributes;
+import org.gradle.api.java.archives.ManifestMergeSpec;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.*;
+import java.util.*;
+
+public class DefaultManifest implements org.gradle.api.java.archives.Manifest {
+    private List<ManifestMergeSpec> manifestMergeSpecs = new ArrayList<ManifestMergeSpec>();
+
+    private DefaultAttributes attributes = new DefaultAttributes();
+
+    private Map<String, Attributes> sections = new LinkedHashMap<String, Attributes>();
+
+    private FileResolver fileResolver;
+
+    public DefaultManifest(FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+        init();
+    }
+
+    public DefaultManifest(Object manifestPath, FileResolver fileResolver) {
+        this.fileResolver = fileResolver;
+        read(manifestPath);
+    }
+
+    private void init() {
+        getAttributes().put("Manifest-Version", "1.0");
+    }
+
+    public DefaultManifest mainAttributes(Map<String, ?> attributes) {
+        return attributes(attributes);
+    }
+
+    public DefaultManifest attributes(Map<String, ?> attributes) {
+        getAttributes().putAll(attributes);
+        return this;
+    }
+
+    public DefaultManifest attributes(Map<String, ?> attributes, String sectionName) {
+        if (!sections.containsKey(sectionName)) {
+            sections.put(sectionName, new DefaultAttributes());
+        }
+        sections.get(sectionName).putAll(attributes);
+        return this;
+    }
+
+    public Attributes getAttributes() {
+        return attributes;
+    }
+
+    public Map<String, Attributes> getSections() {
+        return sections;
+    }
+
+    public DefaultManifest clear() {
+        attributes.clear();
+        sections.clear();
+        manifestMergeSpecs.clear();
+        init();
+        return this;
+    }
+
+    private Manifest generateAntManifest() {
+        Manifest antManifest = new Manifest();
+        addAttributesToAnt(antManifest);
+        addSectionAttributesToAnt(antManifest);
+        return antManifest;
+    }
+
+    private void addAttributesToAnt(Manifest antManifest) {
+        for (Map.Entry<String, Object> entry : attributes.entrySet()) {
+            try {
+                antManifest.addConfiguredAttribute(new Attribute(entry.getKey(), entry.getValue().toString()));
+            } catch (ManifestException e) {
+                throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
+            }
+        }
+    }
+
+    private void addSectionAttributesToAnt(Manifest antManifest) {
+        for (Map.Entry<String, Attributes> entry : sections.entrySet()) {
+            Section section = new Section();
+            section.setName(entry.getKey());
+            try {
+                antManifest.addConfiguredSection(section);
+                for (Map.Entry<String, Object> attributeEntry : entry.getValue().entrySet()) {
+                    section.addConfiguredAttribute(new Attribute(attributeEntry.getKey(), attributeEntry.getValue().toString()));
+                }
+            } catch (ManifestException e) {
+                throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
+            }
+        }
+    }
+
+    public DefaultManifest from(Object... mergePaths) {
+        from(mergePaths, null);
+        return this;
+    }
+
+    public DefaultManifest from(Object mergePaths, Closure<?> closure) {
+        DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec();
+        mergeSpec.from(mergePaths);
+        manifestMergeSpecs.add(mergeSpec);
+        ConfigureUtil.configure(closure, mergeSpec);
+        return this;
+    }
+
+    public DefaultManifest getEffectiveManifest() {
+        return getEffectiveManifestInternal(this);
+    }
+
+    protected DefaultManifest getEffectiveManifestInternal(DefaultManifest baseManifest) {
+        DefaultManifest resultManifest = baseManifest;
+        for (ManifestMergeSpec manifestMergeSpec : manifestMergeSpecs) {
+            resultManifest = ((DefaultManifestMergeSpec) manifestMergeSpec).merge(resultManifest, fileResolver);
+        }
+        return resultManifest;
+    }
+
+    public DefaultManifest writeTo(Writer writer) {
+        PrintWriter printWriter = new PrintWriter(writer);
+        try {
+            getEffectiveManifest().generateAntManifest().write(printWriter);
+            printWriter.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return this;
+    }
+
+    public org.gradle.api.java.archives.Manifest writeTo(Object path) {
+        IoActions.writeTextFile(fileResolver.resolve(path), new ErroringAction<Writer>() {
+            @Override
+            protected void doExecute(Writer writer) throws Exception {
+                writeTo(writer);
+            }
+        });
+        return this;
+    }
+
+    public List<ManifestMergeSpec> getMergeSpecs() {
+        return manifestMergeSpecs;
+    }
+
+    public boolean isEqualsTo(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || !(o instanceof DefaultManifest)) {
+            return false;
+        }
+
+        DefaultManifest effectiveThis = getEffectiveManifest();
+        DefaultManifest effectiveThat = ((DefaultManifest) o).getEffectiveManifest();
+
+        if (!effectiveThis.attributes.equals(effectiveThat.attributes)) {
+            return false;
+        }
+        if (!effectiveThis.sections.equals(effectiveThat.sections)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private void read(Object manifestPath) {
+        File manifestFile = fileResolver.resolve(manifestPath);
+        try {
+            FileReader reader = new FileReader(manifestFile);
+            Manifest antManifest;
+            try {
+                antManifest = new Manifest(reader);
+            } finally {
+                reader.close();
+            }
+            addAntManifestToAttributes(antManifest);
+            addAntManifestToSections(antManifest);
+        } catch (ManifestException e) {
+            throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void addAntManifestToAttributes(Manifest antManifest) {
+        Enumeration<String> attributeKeys = antManifest.getMainSection().getAttributeKeys();
+        while (attributeKeys.hasMoreElements()) {
+            String key = attributeKeys.nextElement();
+            String attributeKey = antManifest.getMainSection().getAttribute(key).getName();
+            attributes.put(attributeKey, antManifest.getMainSection().getAttributeValue(key));
+        }
+        attributes.put("Manifest-Version", antManifest.getManifestVersion());
+    }
+
+    private void addAntManifestToSections(Manifest antManifest) {
+        Enumeration<String> sectionNames = antManifest.getSectionNames();
+        while (sectionNames.hasMoreElements()) {
+            String sectionName = sectionNames.nextElement();
+            addAntManifestToSection(antManifest, sectionName);
+        }
+    }
+
+    private void addAntManifestToSection(Manifest antManifest, String sectionName) {
+        DefaultAttributes attributes = new DefaultAttributes();
+        sections.put(sectionName, attributes);
+        Enumeration<String> attributeKeys = antManifest.getSection(sectionName).getAttributeKeys();
+        while (attributeKeys.hasMoreElements()) {
+            String key = attributeKeys.nextElement();
+            String attributeKey = antManifest.getSection(sectionName).getAttribute(key).getName();
+            attributes.put(attributeKey, antManifest.getSection(sectionName).getAttributeValue(key));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeDetails.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifestMergeDetails.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeDetails.java
rename to subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifestMergeDetails.java
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java
new file mode 100644
index 0000000..807f324
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.java.archives.internal;
+
+import com.google.common.collect.Sets;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.internal.ClosureBackedAction;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.java.archives.Attributes;
+import org.gradle.api.java.archives.Manifest;
+import org.gradle.api.java.archives.ManifestMergeDetails;
+import org.gradle.api.java.archives.ManifestMergeSpec;
+import org.gradle.util.GUtil;
+import org.gradle.util.WrapUtil;
+
+import java.util.*;
+
+public class DefaultManifestMergeSpec implements ManifestMergeSpec {
+    List<Object> mergePaths = new ArrayList<Object>();
+    private final List<Action<? super ManifestMergeDetails>> actions = new ArrayList<Action<? super ManifestMergeDetails>>();
+
+    public ManifestMergeSpec from(Object... mergePaths) {
+        GUtil.flatten(mergePaths, this.mergePaths);
+        return this;
+    }
+
+    public ManifestMergeSpec eachEntry(Action<? super ManifestMergeDetails> mergeAction) {
+        actions.add(mergeAction);
+        return this;
+    }
+
+    public ManifestMergeSpec eachEntry(Closure<?> mergeAction) {
+        return eachEntry(new ClosureBackedAction<ManifestMergeDetails>(mergeAction));
+    }
+
+    public DefaultManifest merge(Manifest baseManifest, FileResolver fileResolver) {
+        DefaultManifest mergedManifest = new DefaultManifest(fileResolver);
+        mergedManifest.getAttributes().putAll(baseManifest.getAttributes());
+        mergedManifest.getSections().putAll(baseManifest.getSections());
+        for (Object mergePath : mergePaths) {
+            DefaultManifest manifestToMerge = createManifest(mergePath, fileResolver);
+            mergedManifest = mergeManifest(mergedManifest, manifestToMerge, fileResolver);
+        }
+        return mergedManifest;
+    }
+
+    private DefaultManifest mergeManifest(DefaultManifest baseManifest, DefaultManifest toMergeManifest, FileResolver fileResolver) {
+        DefaultManifest mergedManifest = new DefaultManifest(fileResolver);
+        mergeSection(null, mergedManifest, baseManifest.getAttributes(), toMergeManifest.getAttributes());
+        Set<String> allSections = Sets.union(baseManifest.getSections().keySet(), toMergeManifest.getSections().keySet());
+        for (String section : allSections) {
+            mergeSection(section, mergedManifest,
+                    GUtil.elvis(baseManifest.getSections().get(section), new DefaultAttributes()),
+                    GUtil.elvis(toMergeManifest.getSections().get(section), new DefaultAttributes()));
+        }
+        return mergedManifest;
+    }
+
+    private void mergeSection(String section, DefaultManifest mergedManifest, Attributes baseAttributes, Attributes mergeAttributes) {
+        Map<String, Object> mergeOnlyAttributes = new LinkedHashMap<String, Object>(mergeAttributes);
+        Set<DefaultManifestMergeDetails> mergeDetailsSet = new LinkedHashSet<DefaultManifestMergeDetails>();
+
+        for (Map.Entry<String, Object> baseEntry : baseAttributes.entrySet()) {
+            Object mergeValue = mergeAttributes.get(baseEntry.getKey());
+            mergeDetailsSet.add(getMergeDetails(section, baseEntry.getKey(), baseEntry.getValue(), mergeValue));
+            mergeOnlyAttributes.remove(baseEntry.getKey());
+        }
+        for (Map.Entry<String, Object> mergeEntry : mergeOnlyAttributes.entrySet()) {
+            mergeDetailsSet.add(getMergeDetails(section, mergeEntry.getKey(), null, mergeEntry.getValue()));
+        }
+        
+        for (DefaultManifestMergeDetails mergeDetails : mergeDetailsSet) {
+            for (Action<? super ManifestMergeDetails> action : actions) {
+                action.execute(mergeDetails);
+            }
+            addMergeDetailToManifest(section, mergedManifest, mergeDetails);
+        }
+    }
+
+    private DefaultManifestMergeDetails getMergeDetails(String section, String key, Object baseValue, Object mergeValue) {
+        String value = null;
+        String baseValueString = baseValue != null ? baseValue.toString() : null;
+        String mergeValueString = mergeValue != null ? mergeValue.toString() : null;
+        value = mergeValueString == null ? baseValueString : mergeValueString; 
+        return new DefaultManifestMergeDetails(section, key, baseValueString, mergeValueString, value);
+    }
+
+    private void addMergeDetailToManifest(String section, DefaultManifest mergedManifest, DefaultManifestMergeDetails mergeDetails) {
+        if (!mergeDetails.isExcluded()) {
+            if (section == null) {
+                mergedManifest.attributes(WrapUtil.toMap(mergeDetails.getKey(), mergeDetails.getValue()));
+            } else {
+                mergedManifest.attributes(WrapUtil.toMap(mergeDetails.getKey(), mergeDetails.getValue()), section);
+            }
+        }
+    }
+
+    private DefaultManifest createManifest(Object mergePath, FileResolver fileResolver) {
+        if (mergePath instanceof DefaultManifest) {
+            return ((DefaultManifest) mergePath).getEffectiveManifest();
+        }
+        return new DefaultManifest(mergePath, fileResolver);
+    }
+
+    public List<Object> getMergePaths() {
+        return mergePaths;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/package-info.java
similarity index 100%
rename from subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/package-info.java
rename to subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/package-info.java
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/Classpath.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/Classpath.java
new file mode 100644
index 0000000..0b32495
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/Classpath.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.jvm;
+
+import org.gradle.api.Buildable;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+
+/**
+ * A collection of files to be used as a class path.
+ */
+ at Incubating
+public interface Classpath extends Buildable {
+    FileCollection getFiles();
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JarBinarySpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JarBinarySpec.java
new file mode 100644
index 0000000..a851d2b
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JarBinarySpec.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+import java.io.File;
+
+/**
+ * Definition of a Jar file binary that is to be built by Gradle.
+ */
+ at Incubating @HasInternalProtocol
+public interface JarBinarySpec extends JvmBinarySpec {
+    /**
+     * The jar file output for this binary.
+     */
+    File getJarFile();
+
+    /**
+     * Sets the jar file output for this binary.
+     */
+    void setJarFile(File jarFile);
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinarySpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinarySpec.java
new file mode 100644
index 0000000..e09a09a
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinarySpec.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+import java.io.File;
+
+/**
+ * Represents a binary artifact that is the result of building a jvm component.
+ */
+ at Incubating
+public interface JvmBinarySpec extends BinarySpec {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    JvmBinaryTasks getTasks();
+
+    /**
+     * The target platform for this binary.
+     */
+    JavaPlatform getTargetPlatform();
+
+    /**
+     * Sets the target platform for this binary.
+     */
+    void setTargetPlatform(JavaPlatform platform);
+
+    /**
+     * Returns the {@link org.gradle.jvm.toolchain.JavaToolChain} that will be used to build this binary.
+     */
+    JavaToolChain getToolChain();
+
+    /**
+     * Sets the {@link org.gradle.jvm.toolchain.JavaToolChain} that will be used to build this binary.
+     */
+    void setToolChain(JavaToolChain toolChain);
+
+    /**
+     * Returns the {@link org.gradle.jvm.toolchain.JavaToolChain} that will be used to build this binary.
+     */
+    ToolResolver getToolResolver();
+
+    void setToolResolver(ToolResolver toolResolver);
+
+    /**
+     * The classes directory for this binary.
+     */
+    File getClassesDir();
+
+    /**
+     * Sets the classes directory for this binary.
+     */
+    void setClassesDir(File classesDir);
+
+    /**
+     * The resources directory for this binary.
+     */
+    File getResourcesDir();
+
+    /**
+     * Sets the resources directory for this binary.
+     */
+    void setResourcesDir(File dir);
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinaryTasks.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinaryTasks.java
new file mode 100644
index 0000000..f995807
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmBinaryTasks.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+/**
+ * Provides access to key tasks used for building the binary.
+ */
+ at Incubating
+public interface JvmBinaryTasks extends BinaryTasksCollection {
+    /**
+     * The jar task used to create an archive for this binary.
+     */
+    Jar getJar();
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmByteCode.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmByteCode.java
new file mode 100644
index 0000000..e8530da
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmByteCode.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.TransformationFileType;
+
+/**
+ * LanguageOutputType marking Jvm byte code.
+ * */
+ at Incubating
+ public class JvmByteCode implements TransformationFileType {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentExtension.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentExtension.java
new file mode 100644
index 0000000..a118001
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentExtension.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+
+/**
+ * The configuration for jvm components created by this build.
+ */
+// TODO:DAZ Remove this. It's just a hack to allow Jar binaries to be configured in the DSL.
+// Can't use binaries.all since the action needs to execute _after_ the plugin-supplied actions
+// There's a story in the design doc to do this properly
+ at Incubating
+public interface JvmComponentExtension {
+    void allBinaries(Action<? super JvmBinarySpec> action);
+
+    Action<JvmBinarySpec> getAllBinariesAction();
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentSpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentSpec.java
new file mode 100644
index 0000000..9de5612
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmComponentSpec.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.platform.base.ComponentSpec;
+
+/**
+ * Definition of a software component that is to be built by Gradle to run a on JVM platform.
+ */
+public interface JvmComponentSpec extends ComponentSpec {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrary.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrary.java
new file mode 100644
index 0000000..33bc978
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrary.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.Library;
+
+/**
+ * A Library component that runs on the Java Virtual Machine.
+ */
+ at Incubating
+public interface JvmLibrary extends Library {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrarySpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrarySpec.java
new file mode 100644
index 0000000..b118545
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmLibrarySpec.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.LibrarySpec;
+import org.gradle.platform.base.PlatformAwareComponentSpec;
+
+/**
+ * Definition of a JVM library component that is to be built by Gradle.
+ */
+ at Incubating @HasInternalProtocol
+public interface JvmLibrarySpec extends LibrarySpec, JvmComponentSpec, PlatformAwareComponentSpec {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmResources.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmResources.java
new file mode 100644
index 0000000..eb773f4
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/JvmResources.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.TransformationFileType;
+
+/**
+ * LanguageOutputType marking Jvm Resource Output
+ */
+
+ at Incubating
+public class JvmResources implements TransformationFileType {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/AbstractJvmBinaryRenderer.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/AbstractJvmBinaryRenderer.java
new file mode 100644
index 0000000..72e707a
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/AbstractJvmBinaryRenderer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.api.reporting.components.internal.AbstractBinaryRenderer;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.jvm.JvmBinarySpec;
+
+public abstract class AbstractJvmBinaryRenderer<T extends JvmBinarySpec> extends AbstractBinaryRenderer<T> {
+    @Override
+    protected void renderDetails(T binary, TextReportBuilder builder) {
+        builder.item("platform", binary.getTargetPlatform().getName());
+        builder.item("tool chain", binary.getToolChain().getDisplayName());
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultClasspath.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultClasspath.java
new file mode 100644
index 0000000..3727933
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultClasspath.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.jvm.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.api.internal.tasks.TaskResolver;
+import org.gradle.jvm.Classpath;
+import org.gradle.api.tasks.TaskDependency;
+
+public class DefaultClasspath implements Classpath {
+    private final FileCollection files;
+
+    public DefaultClasspath(FileResolver fileResolver, TaskResolver taskResolver) {
+        files = new DefaultConfigurableFileCollection(fileResolver, taskResolver);
+    }
+
+    public FileCollection getFiles() {
+        return files;
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return files.getBuildDependencies();
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJarBinarySpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJarBinarySpec.java
new file mode 100644
index 0000000..a355988
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJarBinarySpec.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.jvm.JvmBinaryTasks;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryBuildAbility;
+import org.gradle.platform.base.internal.ToolSearchBuildAbility;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+import java.io.File;
+
+public class DefaultJarBinarySpec extends BaseBinarySpec implements JarBinarySpecInternal {
+    private final JvmBinaryTasks tasks = new DefaultJvmBinaryTasks(super.getTasks());
+    private JavaToolChain toolChain;
+    private JavaPlatform platform;
+    private File classesDir;
+    private File resourcesDir;
+    private File jarFile;
+    private String baseName;
+    private ToolResolver toolResolver;
+
+    @Override
+    protected String getTypeName() {
+        return "Jar";
+    }
+
+    public String getBaseName() {
+        return baseName == null ? getName() : baseName;
+    }
+
+    public void setBaseName(String baseName) {
+        this.baseName = baseName;
+    }
+
+    public JvmBinaryTasks getTasks() {
+        return tasks;
+    }
+
+    public JavaToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public void setToolChain(JavaToolChain toolChain) {
+        this.toolChain = toolChain;
+    }
+
+    public ToolResolver getToolResolver() {
+        return toolResolver;
+    }
+
+    public void setToolResolver(ToolResolver toolResolver) {
+        this.toolResolver = toolResolver;
+    }
+
+    public JavaPlatform getTargetPlatform() {
+        return platform;
+    }
+
+    public void setTargetPlatform(JavaPlatform platform) {
+        this.platform = platform;
+    }
+
+    public File getJarFile() {
+        return jarFile;
+    }
+
+    public void setJarFile(File jarFile) {
+        this.jarFile = jarFile;
+    }
+
+    public File getClassesDir() {
+        return classesDir;
+    }
+
+    public void setClassesDir(File classesDir) {
+        this.classesDir = classesDir;
+    }
+
+    public File getResourcesDir() {
+        return resourcesDir;
+    }
+
+    public void setResourcesDir(File resourcesDir) {
+        this.resourcesDir = resourcesDir;
+    }
+
+    @Override
+    protected BinaryBuildAbility getBinaryBuildAbility() {
+        return new ToolSearchBuildAbility(getToolResolver().checkToolAvailability(getTargetPlatform()));
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmBinaryTasks.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmBinaryTasks.java
new file mode 100644
index 0000000..dd2165f
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmBinaryTasks.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.jvm.JvmBinaryTasks;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.BinaryTasksCollectionWrapper;
+
+public class DefaultJvmBinaryTasks extends BinaryTasksCollectionWrapper implements JvmBinaryTasks {
+
+    public DefaultJvmBinaryTasks(BinaryTasksCollection delegate) {
+        super(delegate);
+    }
+
+    public Jar getJar() {
+        return findSingleTaskWithType(Jar.class);
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmLibrarySpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmLibrarySpec.java
new file mode 100644
index 0000000..e02a2ab
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/DefaultJvmLibrarySpec.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.jvm.JvmByteCode;
+import org.gradle.jvm.JvmResources;
+import org.gradle.platform.base.TransformationFileType;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+
+import java.util.*;
+
+public class DefaultJvmLibrarySpec extends BaseComponentSpec implements JvmLibrarySpecInternal {
+    private final Set<Class<? extends TransformationFileType>> languageOutputs = new HashSet<Class<? extends TransformationFileType>>();
+    private final List<PlatformRequirement> targetPlatforms = Lists.newArrayList();
+
+    public DefaultJvmLibrarySpec() {
+        this.languageOutputs.add(JvmResources.class);
+        this.languageOutputs.add(JvmByteCode.class);
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "JVM library";
+    }
+
+    public Set<Class<? extends TransformationFileType>> getInputTypes() {
+        return languageOutputs;
+    }
+
+    public List<PlatformRequirement> getTargetPlatforms() {
+        return Collections.unmodifiableList(targetPlatforms);
+    }
+
+    public void targetPlatform(String targetPlatform) {
+        this.targetPlatforms.add(DefaultPlatformRequirement.create(targetPlatform));
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinaryRenderer.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinaryRenderer.java
new file mode 100644
index 0000000..62b70e3
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinaryRenderer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.jvm.JarBinarySpec;
+
+public class JarBinaryRenderer extends AbstractJvmBinaryRenderer<JarBinarySpec> {
+    @Override
+    public Class<JarBinarySpec> getTargetType() {
+        return JarBinarySpec.class;
+    }
+
+    @Override
+    protected void renderOutputs(JarBinarySpec binary, TextReportBuilder builder) {
+        builder.item("Jar file", binary.getJarFile());
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinarySpecInternal.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinarySpecInternal.java
new file mode 100644
index 0000000..83b18ea
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JarBinarySpecInternal.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.jvm.JarBinarySpec;
+
+public interface JarBinarySpecInternal extends JarBinarySpec, BinarySpecInternal {
+    String getBaseName();
+
+    void setBaseName(String baseName);
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JavaPlatformResolver.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JavaPlatformResolver.java
new file mode 100644
index 0000000..cf7c910
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JavaPlatformResolver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.jvm.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.JavaVersion;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.platform.base.internal.PlatformRequirement;
+import org.gradle.platform.base.internal.PlatformResolver;
+
+import java.util.List;
+
+public class JavaPlatformResolver implements PlatformResolver<JavaPlatform> {
+    private final List<JavaPlatform> platforms = Lists.newArrayList();
+
+    public JavaPlatformResolver() {
+        for (JavaVersion javaVersion : JavaVersion.values()) {
+            DefaultJavaPlatform javaPlatform = new DefaultJavaPlatform(javaVersion);
+            platforms.add(javaPlatform);
+        }
+    }
+
+    @Override
+    public Class<JavaPlatform> getType() {
+        return JavaPlatform.class;
+    }
+
+    @Override
+    public JavaPlatform resolve(PlatformRequirement platformRequirement) {
+        for (JavaPlatform platform : platforms) {
+            if (platform.getName().equals(platformRequirement.getPlatformName())) {
+                return platform;
+            }
+        }
+        return null;
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JvmLibrarySpecInternal.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JvmLibrarySpecInternal.java
new file mode 100644
index 0000000..e14ea77
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/JvmLibrarySpecInternal.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.jvm.JvmLibrarySpec;
+import org.gradle.platform.base.internal.PlatformAwareComponentSpecInternal;
+
+/**
+ * Definition of a JVM library component that is to be built by Gradle.
+ */
+public interface JvmLibrarySpecInternal extends JvmLibrarySpec, PlatformAwareComponentSpecInternal {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/PlatformJvmServices.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/PlatformJvmServices.java
new file mode 100644
index 0000000..a01cd89
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/PlatformJvmServices.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class PlatformJvmServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.add(JarBinaryRenderer.class);
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/plugins/DefaultJvmComponentExtension.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/plugins/DefaultJvmComponentExtension.java
new file mode 100644
index 0000000..26a9df1
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/plugins/DefaultJvmComponentExtension.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal.plugins;
+
+import org.gradle.api.Action;
+import org.gradle.jvm.JvmBinarySpec;
+import org.gradle.jvm.JvmComponentExtension;
+import org.gradle.listener.ActionBroadcast;
+
+public class DefaultJvmComponentExtension implements JvmComponentExtension {
+    private final ActionBroadcast<JvmBinarySpec> binariesAction = new ActionBroadcast<JvmBinarySpec>();
+
+    public void allBinaries(Action<? super JvmBinarySpec> action) {
+        binariesAction.add(action);
+    }
+
+    public Action<JvmBinarySpec> getAllBinariesAction() {
+        return binariesAction;
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/toolchain/JavaToolChainInternal.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/toolchain/JavaToolChainInternal.java
new file mode 100644
index 0000000..81cdfab
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/toolchain/JavaToolChainInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal.toolchain;
+
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.platform.base.internal.toolchain.ToolChainInternal;
+
+public interface JavaToolChainInternal extends JavaToolChain, ToolChainInternal<JavaPlatform> {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/package-info.java
new file mode 100644
index 0000000..8c21369
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Types for support of JVM runtime.
+ */
+ at Incubating
+package org.gradle.jvm;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/JavaPlatform.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/JavaPlatform.java
new file mode 100644
index 0000000..49f3b7e
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/JavaPlatform.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.platform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.JavaVersion;
+import org.gradle.platform.base.Platform;
+
+/**
+ * Defines and configures a Java SE runtime environment, consisting of a JVM runtime and a set of class libraries.
+ *
+ * <pre autoTested="true">
+ * plugins {
+ *   id "jvm-component"
+ *   id "java-lang"
+ * }
+ *
+ * model {
+ *   components {
+ *     myLib(JvmLibrarySpec) {
+ *       targetPlatform "java6"
+ *     }
+ *   }
+ * }
+ * </pre>
+ */
+ at Incubating
+public interface JavaPlatform extends Platform {
+    JavaVersion getTargetCompatibility();
+    void setTargetCompatibility(JavaVersion targetCompatibility);
+}
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/internal/DefaultJavaPlatform.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/internal/DefaultJavaPlatform.java
new file mode 100644
index 0000000..e55329d
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/internal/DefaultJavaPlatform.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.platform.internal;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.JavaVersion;
+import org.gradle.jvm.platform.JavaPlatform;
+
+/**
+ * Default implementation of JvmPlatform
+ */
+ at Incubating
+public class DefaultJavaPlatform implements JavaPlatform {
+    private final String name;
+    private JavaVersion targetCompatibility;
+
+    public DefaultJavaPlatform(String name) {
+        this.name = name;
+        this.targetCompatibility = JavaVersion.current();
+    }
+
+    public DefaultJavaPlatform(JavaVersion javaVersion) {
+        this.name = generateName(javaVersion);
+        this.targetCompatibility = javaVersion;
+    }
+
+    public JavaVersion getTargetCompatibility() {
+        return targetCompatibility;
+    }
+
+    public String getDisplayName() {
+        return String.format("Java SE %s", targetCompatibility.getMajorVersion());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public void setTargetCompatibility(JavaVersion targetCompatibility) {
+        this.targetCompatibility = targetCompatibility;
+    }
+
+    private static String generateName(JavaVersion javaVersion) {
+        return "java" + javaVersion.getMajorVersion();
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/package-info.java
new file mode 100644
index 0000000..ca6816a
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/platform/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for managing platform variance
+ */
+package org.gradle.jvm.platform;
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/JvmComponentPlugin.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/JvmComponentPlugin.java
new file mode 100644
index 0000000..ae66265
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/JvmComponentPlugin.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.jvm.plugins;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.*;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.JarBinarySpec;
+import org.gradle.jvm.JvmComponentExtension;
+import org.gradle.jvm.JvmLibrarySpec;
+import org.gradle.jvm.internal.*;
+import org.gradle.jvm.internal.plugins.DefaultJvmComponentExtension;
+import org.gradle.jvm.internal.toolchain.JavaToolChainInternal;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.jvm.toolchain.JavaToolChainRegistry;
+import org.gradle.jvm.toolchain.internal.DefaultJavaToolChainRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.*;
+import org.gradle.platform.base.internal.*;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Base plugin for JVM component support. Applies the {@link org.gradle.language.base.plugins.ComponentModelBasePlugin}. Registers the {@link org.gradle.jvm.JvmLibrarySpec} library type for
+ * the {@link org.gradle.platform.base.ComponentSpecContainer}.
+ */
+ at Incubating
+ at SuppressWarnings("UnusedDeclaration")
+public class JvmComponentPlugin extends RuleSource {
+    @ComponentType
+    void register(ComponentTypeBuilder<JvmLibrarySpec> builder) {
+        builder.defaultImplementation(DefaultJvmLibrarySpec.class);
+    }
+
+    @BinaryType
+    void registerJar(BinaryTypeBuilder<JarBinarySpec> builder) {
+        builder.defaultImplementation(DefaultJarBinarySpec.class);
+    }
+
+    @Model
+    JvmComponentExtension jvm(ServiceRegistry serviceRegistry) {
+        final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+        return instantiator.newInstance(DefaultJvmComponentExtension.class);
+    }
+
+    @Model
+    BinaryNamingSchemeBuilder binaryNamingSchemeBuilder() {
+        return new DefaultBinaryNamingSchemeBuilder();
+    }
+
+    @Model
+    JavaToolChainRegistry javaToolChain(ServiceRegistry serviceRegistry) {
+        JavaToolChainInternal toolChain = serviceRegistry.get(JavaToolChainInternal.class);
+        return new DefaultJavaToolChainRegistry(toolChain);
+    }
+
+    @Mutate
+    public void registerPlatformResolver(PlatformResolvers platformResolvers) {
+        platformResolvers.register(new JavaPlatformResolver());
+    }
+
+    @ComponentBinaries
+    public void createBinaries(CollectionBuilder<JarBinarySpec> binaries, final JvmLibrarySpec jvmLibrary,
+                               PlatformResolvers platforms, BinaryNamingSchemeBuilder namingSchemeBuilder, final JvmComponentExtension jvmComponentExtension,
+                               @Path("buildDir") File buildDir, ServiceRegistry serviceRegistry, JavaToolChainRegistry toolChains) {
+
+        final File binariesDir = new File(buildDir, "jars");
+        final File classesDir = new File(buildDir, "classes");
+        ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+
+        List<JavaPlatform> selectedPlatforms = resolvePlatforms(jvmLibrary, platforms);
+        for (final JavaPlatform platform : selectedPlatforms) {
+            final JavaToolChainInternal toolChain = (JavaToolChainInternal) toolChains.getForPlatform(platform);
+            final String binaryName = createBinaryName(jvmLibrary, namingSchemeBuilder, selectedPlatforms, platform);
+            binaries.create(binaryName, new ConfigureJarBinary(jvmLibrary, toolChain, platform, classesDir, binariesDir, jvmComponentExtension, toolResolver));
+        }
+    }
+
+    private List<JavaPlatform> resolvePlatforms(JvmLibrarySpec jvmLibrary, final PlatformResolvers platforms) {
+        List<PlatformRequirement> targetPlatforms = ((JvmLibrarySpecInternal) jvmLibrary).getTargetPlatforms();
+        if (targetPlatforms.isEmpty()) {
+            // TODO:DAZ Make it simpler to get the default java platform name, or use a spec here
+            String defaultJavaPlatformName = new DefaultJavaPlatform(JavaVersion.current()).getName();
+            targetPlatforms = Collections.singletonList(DefaultPlatformRequirement.create(defaultJavaPlatformName));
+        }
+        return CollectionUtils.collect(targetPlatforms, new Transformer<JavaPlatform, PlatformRequirement>() {
+            @Override
+            public JavaPlatform transform(PlatformRequirement platformRequirement) {
+                return platforms.resolve(JavaPlatform.class, platformRequirement);
+            }
+        });
+    }
+
+    @BinaryTasks
+    public void createTasks(CollectionBuilder<Task> tasks, final JarBinarySpec binary) {
+        String taskName = "create" + StringUtils.capitalize(binary.getName());
+        tasks.create(taskName, Jar.class, new Action<Jar>() {
+            @Override
+            public void execute(Jar jar) {
+                jar.setDescription(String.format("Creates the binary file for %s.", binary));
+                jar.from(binary.getClassesDir());
+                jar.from(binary.getResourcesDir());
+
+                jar.setDestinationDir(binary.getJarFile().getParentFile());
+                jar.setArchiveName(binary.getJarFile().getName());
+            }
+        });
+
+        // bad, bad, bad
+        binary.builtBy(tasks.get(taskName));
+    }
+
+    private String createBinaryName(JvmLibrarySpec jvmLibrary, BinaryNamingSchemeBuilder namingSchemeBuilder, List<JavaPlatform> selectedPlatforms, JavaPlatform platform) {
+        BinaryNamingSchemeBuilder componentBuilder = namingSchemeBuilder
+                .withComponentName(jvmLibrary.getName())
+                .withTypeString("jar");
+        if (selectedPlatforms.size() > 1) {
+            componentBuilder = componentBuilder.withVariantDimension(platform.getName());
+        }
+        return componentBuilder.build().getLifecycleTaskName();
+    }
+
+    private static class ConfigureJarBinary implements Action<JarBinarySpec> {
+        private final JvmLibrarySpec jvmLibrary;
+        private final JavaToolChainInternal toolChain;
+        private final JavaPlatform platform;
+        private final File classesDir;
+        private final File binariesDir;
+        private final JvmComponentExtension jvmComponentExtension;
+        private final ToolResolver toolResolver;
+
+        public ConfigureJarBinary(JvmLibrarySpec jvmLibrary, JavaToolChainInternal toolChain, JavaPlatform platform, File classesDir, File binariesDir, JvmComponentExtension jvmComponentExtension, ToolResolver toolResolver) {
+            this.jvmLibrary = jvmLibrary;
+            this.toolChain = toolChain;
+            this.platform = platform;
+            this.classesDir = classesDir;
+            this.binariesDir = binariesDir;
+            this.jvmComponentExtension = jvmComponentExtension;
+            this.toolResolver = toolResolver;
+        }
+
+        public void execute(JarBinarySpec jarBinary) {
+            JarBinarySpecInternal jarBinaryInternal = (JarBinarySpecInternal) jarBinary;
+            jarBinaryInternal.setBaseName(jvmLibrary.getName());
+            jarBinary.setToolChain(toolChain);
+            jarBinary.setTargetPlatform(platform);
+            jarBinary.setToolResolver(toolResolver);
+
+            File outputDir = new File(classesDir, jarBinary.getName());
+            jarBinary.setClassesDir(outputDir);
+            jarBinary.setResourcesDir(outputDir);
+            jarBinary.setJarFile(new File(binariesDir, String.format("%s/%s.jar", jarBinary.getName(), jarBinaryInternal.getBaseName())));
+
+            jvmComponentExtension.getAllBinariesAction().execute(jarBinary);
+        }
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/package-info.java
new file mode 100644
index 0000000..386113c
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/plugins/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Base plugins that add support for JVM runtime.
+ */
+ at Incubating
+package org.gradle.jvm.plugins;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/Jar.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/Jar.java
new file mode 100644
index 0000000..ea4e6d2
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/Jar.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.tasks;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.internal.file.collections.FileTreeAdapter;
+import org.gradle.api.internal.file.collections.MapFileTree;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.java.archives.Manifest;
+import org.gradle.api.java.archives.internal.DefaultManifest;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.bundling.Zip;
+import org.gradle.util.ConfigureUtil;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.concurrent.Callable;
+
+/**
+ * Assembles a JAR archive.
+ */
+ at ParallelizableTask
+ at Incubating
+public class Jar extends Zip {
+
+    public static final String DEFAULT_EXTENSION = "jar";
+    private Manifest manifest;
+    private final CopySpecInternal metaInf;
+
+    public Jar() {
+        setExtension(DEFAULT_EXTENSION);
+
+        manifest = new DefaultManifest(getFileResolver());
+        // Add these as separate specs, so they are not affected by the changes to the main spec
+        metaInf = (CopySpecInternal) getRootSpec().addFirst().into("META-INF");
+        metaInf.addChild().from(new Callable<FileTreeAdapter>() {
+            public FileTreeAdapter call() throws Exception {
+                MapFileTree manifestSource = new MapFileTree(getTemporaryDirFactory(), getFileSystem());
+                manifestSource.add("MANIFEST.MF", new Action<OutputStream>() {
+                    public void execute(OutputStream outputStream) {
+                        Manifest manifest = getManifest();
+                        if (manifest == null) {
+                            manifest = new DefaultManifest(null);
+                        }
+                        manifest.writeTo(new OutputStreamWriter(outputStream));
+                    }
+
+                });
+                return new FileTreeAdapter(manifestSource);
+            }
+        });
+        getMainSpec().eachFile(new Action<FileCopyDetails>() {
+            public void execute(FileCopyDetails details) {
+                if (details.getPath().equalsIgnoreCase("META-INF/MANIFEST.MF")) {
+                    details.exclude();
+                }
+            }
+        });
+    }
+
+    /**
+     * Returns the manifest for this JAR archive.
+     *
+     * @return The manifest
+     */
+    public Manifest getManifest() {
+        return manifest;
+    }
+
+    /**
+     * Sets the manifest for this JAR archive.
+     *
+     * @param manifest The manifest. May be null.
+     */
+    public void setManifest(Manifest manifest) {
+        this.manifest = manifest;
+    }
+
+    /**
+     * Configures the manifest for this JAR archive.
+     *
+     * <p>The given closure is executed to configure the manifest. The {@link org.gradle.api.java.archives.Manifest} is passed to the closure as its delegate.</p>
+     *
+     * @param configureClosure The closure.
+     * @return This.
+     */
+    public Jar manifest(Closure<?> configureClosure) {
+        if (getManifest() == null) {
+            manifest = new DefaultManifest(((ProjectInternal) getProject()).getFileResolver());
+        }
+
+        ConfigureUtil.configure(configureClosure, getManifest());
+        return this;
+    }
+
+    public CopySpec getMetaInf() {
+        return metaInf.addChild();
+    }
+
+    /**
+     * Adds content to this JAR archive's META-INF directory.
+     *
+     * <p>The given closure is executed to configure a {@code CopySpec}. The {@link org.gradle.api.file.CopySpec} is passed to the closure as its delegate.</p>
+     *
+     * @param configureClosure The closure.
+     * @return The created {@code CopySpec}
+     */
+    public CopySpec metaInf(Closure<?> configureClosure) {
+        return ConfigureUtil.configure(configureClosure, getMetaInf());
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/package-info.java
new file mode 100644
index 0000000..3a4fef0
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/tasks/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that add support for JVM runtime.
+ */
+ at Incubating
+package org.gradle.jvm.tasks;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChain.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChain.java
new file mode 100644
index 0000000..14c8411
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChain.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.ToolChain;
+
+/**
+ * A set of tools for building from Java source.
+ *
+ * <p>A {@code JavaToolChain} is able to:
+ *
+ * <ul>
+ *
+ * <li>Compile Java source to bytecode.</li>
+ *
+ * <li>Generate Javadoc from Java source.</li>
+ *
+ * </ul>
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface JavaToolChain extends ToolChain {
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChainRegistry.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChainRegistry.java
new file mode 100644
index 0000000..6cc1b2a
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/JavaToolChainRegistry.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.jvm.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.platform.base.ToolChainRegistry;
+
+/**
+ * A container for {@link JavaToolChain}s.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface JavaToolChainRegistry extends ToolChainRegistry<JavaPlatform, JavaToolChain> {
+}
\ No newline at end of file
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolChainRegistry.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolChainRegistry.java
new file mode 100644
index 0000000..60ae537
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolChainRegistry.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.toolchain.internal;
+
+import org.gradle.jvm.internal.toolchain.JavaToolChainInternal;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.jvm.toolchain.JavaToolChainRegistry;
+
+public class DefaultJavaToolChainRegistry implements JavaToolChainRegistry {
+    private final JavaToolChainInternal toolChain;
+
+    public DefaultJavaToolChainRegistry(JavaToolChainInternal toolChain) {
+        this.toolChain = toolChain;
+    }
+
+    public JavaToolChain getForPlatform(JavaPlatform targetPlatform) {
+        return toolChain;
+    }
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/package-info.java
new file mode 100644
index 0000000..53cc7c6
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Defines tools that can build things that run on the JVM.
+ */
+package org.gradle.jvm.toolchain;
diff --git a/subprojects/platform-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-component.properties b/subprojects/platform-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-component.properties
new file mode 100644
index 0000000..b1a33b8
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/resources/META-INF/gradle-plugins/org.gradle.jvm-component.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.jvm.plugins.JvmComponentPlugin
diff --git a/subprojects/platform-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/platform-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..72095b3
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.jvm.internal.PlatformJvmServices
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultAttributesTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultAttributesTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultAttributesTest.groovy
rename to subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultAttributesTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpecTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpecTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpecTest.groovy
rename to subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpecTest.groovy
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestTest.groovy
similarity index 100%
rename from subprojects/plugins/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestTest.groovy
rename to subprojects/platform-jvm/src/test/groovy/org/gradle/api/java/archives/internal/DefaultManifestTest.groovy
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJarBinarySpecTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJarBinarySpecTest.groovy
new file mode 100644
index 0000000..d241682
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJarBinarySpecTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.jvm.JvmLibrarySpec
+import org.gradle.jvm.platform.JavaPlatform
+import org.gradle.jvm.toolchain.JavaToolChain
+import org.gradle.platform.base.binary.BaseBinarySpec
+import spock.lang.Specification
+
+class DefaultJarBinarySpecTest extends Specification {
+    def library = Mock(JvmLibrarySpec)
+    def toolChain = Mock(JavaToolChain)
+    def platform = Mock(JavaPlatform)
+
+    def "binary takes name and displayName from naming scheme"() {
+        when:
+        def binary = binary()
+
+        then:
+        binary.name == "jvm-lib-jar"
+        binary.displayName == "Jar 'jvm-lib-jar'"
+    }
+
+    def "binary has properties for classesDir and jar file"() {
+        when:
+        def binary = binary()
+
+        then:
+        binary.jarFile == null
+        binary.classesDir == null
+
+        when:
+        def jarFile = Mock(File)
+        def classesDir = Mock(File)
+
+        and:
+        binary.jarFile = jarFile
+        binary.classesDir = classesDir
+
+        then:
+        binary.jarFile == jarFile
+        binary.classesDir == classesDir
+    }
+
+    private DefaultJarBinarySpec binary() {
+        BaseBinarySpec.create(DefaultJarBinarySpec, "jvm-lib-jar", DirectInstantiator.INSTANCE, Mock(ITaskFactory))
+    }
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmBinaryTasksTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmBinaryTasksTest.groovy
new file mode 100644
index 0000000..a76a4d7
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmBinaryTasksTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal
+
+import org.gradle.api.GradleException
+import org.gradle.api.Task
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.jvm.JvmBinarySpec
+import org.gradle.jvm.tasks.Jar
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import spock.lang.Specification
+
+class DefaultJvmBinaryTasksTest extends Specification {
+    def binary = Mock(JvmBinarySpec)
+    def taskFactory = Mock(ITaskFactory)
+    def tasks = new DefaultJvmBinaryTasks(new DefaultBinaryTasksCollection(binary, taskFactory))
+
+    def "provides access to build task"() {
+        def buildTask = Mock(Task)
+        when:
+        binary.buildTask >> buildTask
+
+        then:
+        tasks.build == buildTask
+    }
+
+    def "provides access to jar task"() {
+        def jar = Mock(Jar)
+        when:
+        tasks.add(jar)
+
+        then:
+        tasks.jar == jar
+    }
+
+    def "fails when asked for jar task with multiple present"() {
+        when:
+        tasks.add(Mock(Jar))
+        tasks.add(Mock(Jar))
+        tasks.jar
+
+        then:
+        def e = thrown GradleException
+        e.message == "Multiple tasks with type 'Jar' found."
+    }
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmLibrarySpecTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmLibrarySpecTest.groovy
new file mode 100644
index 0000000..347c2cb
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/DefaultJvmLibrarySpecTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.platform.base.ComponentSpecIdentifier
+import org.gradle.platform.base.component.BaseComponentSpec
+import spock.lang.Specification
+
+class DefaultJvmLibrarySpecTest extends Specification {
+    def libraryId = Mock(ComponentSpecIdentifier)
+    FunctionalSourceSet mainSourceSet
+
+    def setup(){
+        mainSourceSet = new DefaultFunctionalSourceSet("testFss", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet));
+    }
+
+    def "library has name and path"() {
+        def library = createJvmLibrarySpec()
+
+        when:
+        _ * libraryId.name >> "jvm-lib"
+        _ * libraryId.projectPath >> ":project-path"
+
+        then:
+        library.name == "jvm-lib"
+        library.projectPath == ":project-path"
+        library.displayName == "JVM library 'jvm-lib'"
+    }
+
+    def "contains sources of associated main sourceSet"() {
+        when:
+        def lss1 = languageSourceSet("lss1")
+        mainSourceSet.add(lss1)
+
+        and:
+        def library = createJvmLibrarySpec()
+        def lss2 = languageSourceSet("lss2")
+        mainSourceSet.add(lss2)
+
+        then:
+        library.getSource() as List == [lss1, lss2]
+    }
+
+    private DefaultJvmLibrarySpec createJvmLibrarySpec() {
+        BaseComponentSpec.create(DefaultJvmLibrarySpec, libraryId, mainSourceSet, DirectInstantiator.INSTANCE)
+    }
+
+    def languageSourceSet(String name) {
+        Stub(LanguageSourceSet) {
+            getName() >> name
+        }
+    }
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/plugins/CreateJvmBinariesTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/plugins/CreateJvmBinariesTest.groovy
new file mode 100644
index 0000000..9a91c15
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/internal/plugins/CreateJvmBinariesTest.groovy
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.internal.plugins
+import org.gradle.api.Action
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistryBuilder
+import org.gradle.jvm.JarBinarySpec
+import org.gradle.jvm.JvmComponentExtension
+import org.gradle.jvm.internal.DefaultJvmLibrarySpec
+import org.gradle.jvm.internal.toolchain.JavaToolChainInternal
+import org.gradle.jvm.platform.JavaPlatform
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform
+import org.gradle.jvm.plugins.JvmComponentPlugin
+import org.gradle.jvm.toolchain.JavaToolChainRegistry
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.ComponentSpecIdentifier
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.BinaryNamingScheme
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder
+import org.gradle.platform.base.internal.PlatformResolvers
+import org.gradle.platform.base.internal.toolchain.ToolResolver
+import spock.lang.Specification
+
+class CreateJvmBinariesTest extends Specification {
+    def buildDir = new File("buildDir")
+    def namingSchemeBuilder = Mock(BinaryNamingSchemeBuilder)
+    def toolChain = Mock(JavaToolChainInternal)
+    def rule = new JvmComponentPlugin()
+    def platforms = Mock(PlatformResolvers)
+    CollectionBuilder<JarBinarySpec> binaries = Mock(CollectionBuilder)
+    def instantiator = Mock(Instantiator)
+    def mainSourceSet = new DefaultFunctionalSourceSet("ss", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet))
+    def toolChainRegistry = Mock(JavaToolChainRegistry)
+    def toolResolver = Mock(ToolResolver)
+
+    def serviceRegistry = ServiceRegistryBuilder.builder().provider(new Object() {
+        Instantiator createInstantiator() {
+            instantiator
+        }
+        ToolResolver createToolResolver() {
+            toolResolver
+        }
+    }).build()
+
+    def "adds a binary for each jvm library"() {
+        def library = BaseComponentSpec.create(DefaultJvmLibrarySpec, componentId("jvmLibOne", ":project-path"), mainSourceSet, DirectInstantiator.INSTANCE)
+        def namingScheme = Mock(BinaryNamingScheme)
+        def jvmExtension = Mock(JvmComponentExtension)
+        def platform = new DefaultJavaPlatform("test")
+        def source1 = sourceSet("ss1")
+        def source2 = sourceSet("ss2")
+
+        when:
+        library.sources.addAll([source1, source2])
+        rule.createBinaries(binaries, library, platforms, namingSchemeBuilder, jvmExtension, buildDir, serviceRegistry, toolChainRegistry)
+
+        then:
+        1 * platforms.resolve(JavaPlatform, _) >> platform
+        1 * toolChainRegistry.getForPlatform(platform) >> toolChain
+        1 * namingSchemeBuilder.withComponentName("jvmLibOne") >> namingSchemeBuilder
+        1 * namingSchemeBuilder.withTypeString("jar") >> namingSchemeBuilder
+        1 * namingSchemeBuilder.build() >> namingScheme
+        _ * namingScheme.lifecycleTaskName >> "jvmLibJar"
+        1 * binaries.create("jvmLibJar", _ as Action)
+        0 * _
+    }
+
+    def componentId(def name, def path) {
+        Stub(ComponentSpecIdentifier) {
+            getName() >> name
+            getProjectPath() >> path
+        }
+    }
+
+    def sourceSet(def name) {
+        Stub(LanguageSourceSet) {
+            getName() >> name
+        }
+    }
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/platform/internal/DefaultJavaPlatformTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/platform/internal/DefaultJavaPlatformTest.groovy
new file mode 100644
index 0000000..63816ca
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/platform/internal/DefaultJavaPlatformTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.platform.internal
+
+import org.gradle.api.JavaVersion
+import spock.lang.Specification
+
+class DefaultJavaPlatformTest extends Specification {
+
+    def "is for current java version if not set"() {
+        when:
+        def platform = new DefaultJavaPlatform("test")
+
+        then:
+        platform.targetCompatibility == JavaVersion.current()
+    }
+
+    def "can set java version"() {
+        when:
+        def platform = new DefaultJavaPlatform(JavaVersion.VERSION_1_5)
+
+        then:
+        platform.name == "java5"
+        platform.displayName == "Java SE 5"
+        platform.targetCompatibility == JavaVersion.VERSION_1_5
+    }
+
+    def "has reasonable string representation"() {
+        when:
+        def platform = new DefaultJavaPlatform(JavaVersion.current())
+
+        then:
+        platform.name == "java${JavaVersion.current().majorVersion}"
+        platform.displayName == "Java SE ${JavaVersion.current().majorVersion}"
+        platform.toString() == platform.displayName
+    }
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/tasks/JarTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/tasks/JarTest.groovy
new file mode 100644
index 0000000..8844e2d
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/tasks/JarTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * 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.jvm.tasks
+
+import org.gradle.api.java.archives.internal.DefaultManifest
+import org.gradle.api.tasks.bundling.AbstractArchiveTask
+import org.gradle.api.tasks.bundling.AbstractArchiveTaskTest
+import org.junit.Before
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotNull
+
+class JarTest extends AbstractArchiveTaskTest {
+    Jar jar
+
+    @Before public void setUp()  {
+        jar = createTask(Jar)
+        configure(jar)
+    }
+
+    AbstractArchiveTask getArchiveTask() {
+        jar
+    }
+
+    @Test public void testJar() {
+        assertEquals(Jar.DEFAULT_EXTENSION, jar.extension)
+        assertNotNull(jar.manifest)
+        assertNotNull(jar.metaInf)
+    }
+
+    @Test public void testManifest() {
+        jar.manifest = new DefaultManifest(null);
+        jar.manifest {
+            attributes(key: 'value')
+        }
+        assertEquals(jar.manifest.attributes.key, 'value')
+    }
+
+    @Test public void testManifestWithNullManifest() {
+        jar.manifest = null
+        jar.manifest {
+            attributes(key: 'value')
+        }
+        assertEquals(jar.manifest.attributes.key, 'value')
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/platform-native.gradle b/subprojects/platform-native/platform-native.gradle
new file mode 100644
index 0000000..76c1a7a
--- /dev/null
+++ b/subprojects/platform-native/platform-native.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+dependencies {
+    compile libraries.groovy
+    compile libraries.commons_io
+    compile project(':core')
+    compile project(":platformBase")
+    compile project(":diagnostics")
+
+    integTestRuntime project(":maven")
+    // Required to test visual studio project file generation for generated sources
+    integTestRuntime project(":ideNative")
+
+    testFixturesCompile project(":internalIntegTesting")
+}
+
+useTestFixtures()
+useTestFixtures(project: ":messaging")
+useTestFixtures(sourceSet: "testFixtures")
+useTestFixtures(project: ":platformBase")
+useTestFixtures(project: ":modelCore")
+useTestFixtures(project: ":diagnostics")
+
+strictCompile()
+useClassycle(exclude: ["org/gradle/nativeplatform/plugins/**",
+                       "org/gradle/nativeplatform/tasks/**",
+                       "org/gradle/nativeplatform/internal/resolve/**",
+                       "org/gradle/nativeplatform/toolchain/internal/**"])
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/AutoTestedSamplesRuntimeNativeIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/AutoTestedSamplesRuntimeNativeIntegrationTest.groovy
new file mode 100644
index 0000000..779c31b
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/AutoTestedSamplesRuntimeNativeIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.nativeplatform
+
+import org.gradle.integtests.fixtures.AbstractAutoTestedSamplesTest
+import org.junit.Test
+
+class AutoTestedSamplesRuntimeNativeIntegrationTest extends AbstractAutoTestedSamplesTest{
+
+    @Test
+    void runSamples() {
+        runSamplesFrom("subprojects/platform-native/src/main")
+    }
+
+}
+
+
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy
new file mode 100755
index 0000000..a5a77cd
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class BinaryBuildTypesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CppHelloWorldApp()
+
+    def "creates debug and release variants"() {
+        when:
+        helloWorldApp.writeSources(file("src/main"))
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    buildTypes {
+        debug {
+            ext.debug = true
+        }
+        integration {
+            ext.debug = true
+        }
+        release {
+            ext.debug = false
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all { binary ->
+                if (toolChain in Gcc && buildType.debug) {
+                    cppCompiler.args "-g"
+                }
+                if (toolChain in VisualCpp) {
+                    // Apply to all debug build types: 'debug' and 'integration'
+                    if (buildType.debug) {
+                        cppCompiler.args '/Zi'
+                        cppCompiler.define 'DEBUG'
+                        linker.args '/DEBUG'
+                    }
+                }
+                // Apply to 'integration' type binaries only
+                if (buildType == buildTypes['integration']) {
+                    cppCompiler.define "FRENCH"
+                }
+            }
+        }
+    }
+}
+        """
+        and:
+        succeeds "debugMainExecutable", "integrationMainExecutable", "releaseMainExecutable"
+
+        then:
+        with(executable("build/binaries/mainExecutable/debug/main")) {
+            it.assertExists()
+            it.assertDebugFileExists()
+            it.exec().out == helloWorldApp.englishOutput
+        }
+        with (executable("build/binaries/mainExecutable/integration/main")) {
+            it.assertExists()
+            it.assertDebugFileExists()
+            it.exec().out == helloWorldApp.frenchOutput
+        }
+        with (executable("build/binaries/mainExecutable/release/main")) {
+            it.assertExists()
+            it.assertDebugFileDoesNotExist()
+            it.exec().out == helloWorldApp.englishOutput
+        }
+    }
+
+    def "configure component for a single build type"() {
+        when:
+        helloWorldApp.writeSources(file("src/main"))
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    buildTypes {
+        debug
+        release
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetBuildTypes "release"
+            binaries.all { binary ->
+                if (buildType == buildTypes.release) {
+                    cppCompiler.define "FRENCH"
+                }
+            }
+        }
+    }
+}
+"""
+
+        and:
+        succeeds "mainExecutable"
+
+        then:
+        // Build type dimension is flattened since there is only one possible value
+        executedAndNotSkipped(":mainExecutable")
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "executable with build type depends on library with matching build type"() {
+        when:
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    buildTypes {
+        debug
+        release
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello", linkage: "static"
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+binaries.all {
+    if (buildType == buildTypes.debug) {
+        cppCompiler.define "FRENCH" // Equate 'debug' to 'french' for this test
+    }
+}
+        """
+        and:
+        succeeds "installDebugMainExecutable", "installReleaseMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/debug").exec().out == helloWorldApp.frenchOutput
+        installation("build/install/mainExecutable/release").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "fails with reasonable error message when trying to target an unknown build type"() {
+        when:
+        settingsFile << "rootProject.name = 'bad-build-type'"
+        buildFile << """
+model {
+    buildTypes {
+        debug
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetBuildTypes "unknown"
+        }
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.nativeplatform.plugins.NativeComponentModelPlugin\$Rules#createNativeBinaries(")
+        failure.assertHasCause("Invalid BuildType: 'unknown'")
+    }
+
+    def "fails with reasonable error message when depended on library has no variant with matching build type"() {
+        when:
+        settingsFile << "rootProject.name = 'no-matching-build-type'"
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    buildTypes {
+        debug
+        release
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello", linkage: "static"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            targetBuildTypes "debug"
+        }
+    }
+}
+"""
+
+        and:
+        fails "releaseMainExecutable"
+
+        then:
+        failure.assertHasDescription("No static library binary available for library 'hello' with [flavor: 'default', platform: '${NativePlatforms.defaultPlatformName}', buildType: 'release']")
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy
new file mode 100755
index 0000000..a954462
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy
@@ -0,0 +1,384 @@
+/*
+ * 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.nativeplatform
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.hamcrest.Matchers
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+import spock.lang.Unroll
+
+class BinaryConfigurationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "can configure the binaries of a C++ application"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                cppCompiler.define 'ENABLE_GREETING'
+            }
+        }
+    }
+}
+"""
+
+        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/binaries/mainExecutable/main")
+        executable.exec().out == "Hello!"
+    }
+
+    def "can build debug binaries for a C++ executable"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                if (toolChain in VisualCpp) {
+                    cppCompiler.args '/Zi'
+                    linker.args '/DEBUG'
+                } else {
+                    cppCompiler.args '-g'
+                }
+            }
+        }
+    }
+}
+"""
+
+        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/main")
+        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"
+
+model {
+    components { comp ->
+        hello(NativeLibrarySpec) {
+            binaries.all {
+                cppCompiler.define 'ENABLE_GREETING'
+            }
+        }
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib comp.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/binaries/helloStaticLibrary/hello").assertExists()
+        installation("build/install/mainExecutable").exec().out == "Hello!"
+    }
+
+    def "can configure a binary to use additional source sets"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+
+model {
+    components { comp ->
+        util(NativeLibrarySpec) {
+            sources {
+                cpp {
+                    exportedHeaders.srcDir "src/shared/headers"
+                }
+            }
+        }
+        main(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    exportedHeaders.srcDir "src/shared/headers"
+                }
+            }
+            binaries.all {
+                source comp.util.sources.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!"
+    }
+
+    def "can customize binaries before and after linking"() {
+        def helloWorldApp = new CppHelloWorldApp()
+        given:
+        buildFile << """
+apply plugin: 'cpp'
+
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+
+binaries.withType(NativeExecutableBinary) { binary ->
+    def preLink = task("\${binary.name}PreLink") {
+        dependsOn binary.tasks.withType(CppCompile)
+
+        doLast {
+            println "Pre Link"
+        }
+    }
+    binary.tasks.link.dependsOn preLink
+
+    def postLink = task("\${binary.name}PostLink") {
+        dependsOn binary.tasks.link
+
+        doLast {
+            println "Post Link"
+        }
+    }
+
+    binary.builtBy postLink
+}
+"""
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executedTasks == [":compileMainExecutableMainCpp", ":mainExecutablePreLink", ":linkMainExecutable", ":mainExecutablePostLink", ":mainExecutable"]
+    }
+
+    @Issue("GRADLE-2973")
+    @IgnoreIf({ !GradleContextualExecuter.isParallel() })
+    def "releases cache lock when compilation fails with --parallel"() {
+        def helloWorldApp = new CppHelloWorldApp()
+        given:
+        settingsFile << "include ':a', ':b'"
+        buildFile << """
+subprojects {
+    apply plugin: 'cpp'
+    model {
+        components {
+            main(NativeExecutableSpec)
+        }
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("a/src/main"))
+        helloWorldApp.writeSources(file("b/src/main"))
+
+        file("b/src/main/cpp/broken.cpp") << """
+    A broken C++ file
+"""
+
+        expect:
+        fails "mainExecutable"
+        failure.assertThatCause(Matchers.not(Matchers.containsString("Could not stop")))
+    }
+
+    def "can configure output file for binaries"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                executableFile = modPath(executableFile)
+            }
+        }
+        hello(NativeLibrarySpec) {
+            binaries.withType(SharedLibraryBinarySpec) {
+                sharedLibraryFile = modPath(sharedLibraryFile)
+                sharedLibraryLinkFile = modPath(sharedLibraryLinkFile)
+            }
+            binaries.withType(StaticLibraryBinarySpec) {
+                staticLibraryFile = modPath(staticLibraryFile)
+            }
+        }
+    }
+}
+
+def modPath(File file) {
+    new File("\${file.parentFile}/new_output/_\${file.name}")
+}
+"""
+
+        when:
+        succeeds "mainExecutable", "helloSharedLibrary", "helloStaticLibrary"
+
+        then:
+        def modPath = {TestFile file -> new TestFile("${file.parentFile}/new_output/_${file.name}")}
+        modPath(executable("build/binaries/mainExecutable/main").file).assertExists()
+        modPath(sharedLibrary("build/binaries/helloSharedLibrary/hello").file).assertExists()
+        modPath(staticLibrary("build/binaries/helloStaticLibrary/hello").file).assertExists()
+    }
+
+    @Unroll
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "can link to #linkage library binary with custom output file"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello", linkage: "${linkage}"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            binaries.withType(SharedLibraryBinarySpec) {
+                sharedLibraryFile = modPath(sharedLibraryFile)
+                sharedLibraryLinkFile = modPath(sharedLibraryLinkFile)
+            }
+            binaries.withType(StaticLibraryBinarySpec) {
+                staticLibraryFile = modPath(staticLibraryFile)
+            }
+        }
+    }
+}
+
+def modPath(File file) {
+    new File("\${file.parentFile}/new_output/_\${file.name}")
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        where:
+        linkage << ["static", "shared"]
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy
new file mode 100755
index 0000000..0f32a9c
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class BinaryFlavorsIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    static final DEFAULT = HelloWorldApp.HELLO_WORLD
+    static final FRENCH = HelloWorldApp.HELLO_WORLD_FRENCH
+
+    def helloWorldApp = new ExeWithLibraryUsingLibraryHelloWorldApp()
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+
+        buildFile << """
+apply plugin: "cpp"
+model {
+    flavors {
+        english
+        french
+        german
+    }
+    components {
+        greetings(NativeLibrarySpec) {
+            binaries.all {
+                if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) {
+                    cppCompiler.args("-fPIC");
+                }
+            }
+        }
+        hello(NativeLibrarySpec) {
+            binaries.all {
+                lib library: 'greetings', linkage: 'static'
+            }
+        }
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+
+        helloWorldApp.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+    }
+
+    def "can configure components for a single flavor"() {
+        given:
+        buildFile << """
+binaries.all {
+    if (flavor == flavors.french) {
+        cppCompiler.define "FRENCH"
+    }
+}
+model {
+    components {
+        main.targetFlavors "french"
+        hello.targetFlavors "french"
+        greetings.targetFlavors "french"
+    }
+}
+"""
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == FRENCH + " " + FRENCH
+    }
+
+    def "builds executable for each defined flavor when not configured for component"() {
+        when:
+        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable", "installGermanMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/english").assertInstalled()
+        installation("build/install/mainExecutable/french").assertInstalled()
+        installation("build/install/mainExecutable/german").assertInstalled()
+    }
+
+    def "executable with flavors depends on library with matching flavors"() {
+        when:
+        buildFile << """
+model {
+    components {
+        main {
+            targetFlavors "english", "french"
+            binaries.all {
+                if (flavor == flavors.french) {
+                    cppCompiler.define "FRENCH"
+                }
+            }
+        }
+        withType(NativeLibrarySpec) {
+            targetFlavors "english", "french"
+            binaries.all {
+                if (flavor == flavors.french) {
+                    cppCompiler.define "FRENCH"
+                }
+            }
+        }
+    }
+}
+"""
+
+        and:
+        succeeds "installEnglishMainExecutable", "installFrenchMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/english").exec().out == DEFAULT + " " + DEFAULT
+        installation("build/install/mainExecutable/french").exec().out == FRENCH + " " + FRENCH
+    }
+
+    def "build fails when library has no matching flavour"() {
+        when:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        hello {
+            targetFlavors "english", "french"
+        }
+        main {
+            targetFlavors "english", "german"
+            binaries.all {
+                lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+
+        then:
+        fails "germanMainExecutable"
+        failure.assertHasDescription("No shared library binary available for library 'hello' with [flavor: 'german', platform: '${NativePlatforms.defaultPlatformName}', buildType: 'debug']")
+    }
+
+    def "fails with reasonable error message when trying to target an unknown flavor"() {
+        when:
+        buildFile << """
+model {
+    components {
+        main.targetFlavors "unknown"
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.nativeplatform.plugins.NativeComponentModelPlugin\$Rules#createNativeBinaries")
+        failure.assertHasCause("Invalid Flavor: 'unknown'")
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/ComponentReportIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..43d7dd0
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform
+
+import org.gradle.api.reporting.components.AbstractComponentReportIntegrationTest
+import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private String currentNative = NativePlatformsTestFixture.defaultPlatformName
+
+    def "shows details of native C++ library"() {
+        given:
+        buildFile << """
+plugins {
+    id 'cpp'
+}
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        someLib(NativeLibrarySpec)
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+Native library 'someLib'
+------------------------
+
+Source sets
+    C++ source 'someLib:cpp'
+        src/someLib/cpp
+
+Binaries
+    Shared library 'someLib:sharedLibrary'
+        build using task: :someLibSharedLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/someLibSharedLibrary/libsomeLib.dylib
+    Static library 'someLib:staticLibrary'
+        build using task: :someLibStaticLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/someLibStaticLibrary/libsomeLib.a
+"""
+    }
+
+    def "shows details of native C++ library that is not buildable"() {
+        given:
+        buildFile << """
+plugins {
+    id 'cpp'
+}
+
+model {
+    platforms {
+        windows { operatingSystem 'windows'; architecture 'sparc' }
+    }
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        someLib(NativeLibrarySpec) {
+            targetPlatform "windows"
+        }
+        anotherLib(NativeLibrarySpec) {
+            binaries.withType(StaticLibraryBinarySpec) { buildable = false }
+        }
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+Native library 'anotherLib'
+---------------------------
+
+Source sets
+    C++ source 'anotherLib:cpp'
+        src/anotherLib/cpp
+
+Binaries
+    Shared library 'anotherLib:sharedLibrary'
+        build using task: :anotherLibSharedLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/anotherLibSharedLibrary/libanotherLib.dylib
+    Static library 'anotherLib:staticLibrary' (not buildable)
+        build using task: :anotherLibStaticLibrary
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/anotherLibStaticLibrary/libanotherLib.a
+        Disabled by user
+
+Native library 'someLib'
+------------------------
+
+Source sets
+    C++ source 'someLib:cpp'
+        src/someLib/cpp
+
+Binaries
+    Shared library 'someLib:sharedLibrary' (not buildable)
+        build using task: :someLibSharedLibrary
+        platform: windows
+        build type: debug
+        flavor: default
+        tool chain: unavailable
+        shared library file: build/binaries/someLibSharedLibrary/someLib.dll
+        No tool chain is available to build for platform 'windows':
+          - ${toolChain.instanceDisplayName}: Don't know how to build for platform 'windows'.
+    Static library 'someLib:staticLibrary' (not buildable)
+        build using task: :someLibStaticLibrary
+        platform: windows
+        build type: debug
+        flavor: default
+        tool chain: unavailable
+        static library file: build/binaries/someLibStaticLibrary/someLib.lib
+        No tool chain is available to build for platform 'windows':
+          - ${toolChain.instanceDisplayName}: Don't know how to build for platform 'windows'.
+"""
+    }
+
+    def "shows details of polyglot native library with multiple variants"() {
+        given:
+        buildFile << """
+plugins {
+    id 'c'
+    id 'cpp'
+    id 'assembler'
+}
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    platforms {
+        i386 { architecture 'i386' }
+        amd64 { architecture 'amd64' }
+    }
+    flavors {
+        free
+        paid
+    }
+    components {
+        someLib(NativeLibrarySpec) {
+            targetPlatform "i386"
+            targetPlatform "amd64"
+        }
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+Native library 'someLib'
+------------------------
+
+Source sets
+    Assembler source 'someLib:asm'
+        src/someLib/asm
+    C source 'someLib:c'
+        src/someLib/c
+    C++ source 'someLib:cpp'
+        src/someLib/cpp
+
+Binaries
+    Shared library 'someLib:amd64:free:sharedLibrary'
+        build using task: :amd64FreeSomeLibSharedLibrary
+        platform: amd64
+        build type: debug
+        flavor: free
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/someLibSharedLibrary/amd64Free/libsomeLib.dylib
+    Static library 'someLib:amd64:free:staticLibrary'
+        build using task: :amd64FreeSomeLibStaticLibrary
+        platform: amd64
+        build type: debug
+        flavor: free
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/someLibStaticLibrary/amd64Free/libsomeLib.a
+    Shared library 'someLib:amd64:paid:sharedLibrary'
+        build using task: :amd64PaidSomeLibSharedLibrary
+        platform: amd64
+        build type: debug
+        flavor: paid
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/someLibSharedLibrary/amd64Paid/libsomeLib.dylib
+    Static library 'someLib:amd64:paid:staticLibrary'
+        build using task: :amd64PaidSomeLibStaticLibrary
+        platform: amd64
+        build type: debug
+        flavor: paid
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/someLibStaticLibrary/amd64Paid/libsomeLib.a
+    Shared library 'someLib:i386:free:sharedLibrary'
+        build using task: :i386FreeSomeLibSharedLibrary
+        platform: i386
+        build type: debug
+        flavor: free
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/someLibSharedLibrary/i386Free/libsomeLib.dylib
+    Static library 'someLib:i386:free:staticLibrary'
+        build using task: :i386FreeSomeLibStaticLibrary
+        platform: i386
+        build type: debug
+        flavor: free
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/someLibStaticLibrary/i386Free/libsomeLib.a
+    Shared library 'someLib:i386:paid:sharedLibrary'
+        build using task: :i386PaidSomeLibSharedLibrary
+        platform: i386
+        build type: debug
+        flavor: paid
+        tool chain: Tool chain 'clang' (Clang)
+        shared library file: build/binaries/someLibSharedLibrary/i386Paid/libsomeLib.dylib
+    Static library 'someLib:i386:paid:staticLibrary'
+        build using task: :i386PaidSomeLibStaticLibrary
+        platform: i386
+        build type: debug
+        flavor: paid
+        tool chain: Tool chain 'clang' (Clang)
+        static library file: build/binaries/someLibStaticLibrary/i386Paid/libsomeLib.a
+"""
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy
new file mode 100755
index 0000000..0625a27
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Unroll
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class LibraryApiDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+apply plugin: "cpp"
+
+// Allow static libraries to be linked into shared
+binaries.withType(StaticLibraryBinarySpec) {
+    if (toolChain in Gcc || toolChain in Clang) {
+        cppCompiler.args '-fPIC'
+    }
+}
+"""
+    }
+
+    @Unroll
+    def "can use api linkage via #notationName notation"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+
+        app.library.headerFiles*.writeToDir(file("src/helloApi"))
+        app.library.sourceFiles*.writeToDir(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components { comp ->
+        helloApi(NativeLibrarySpec)
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib ${notation}
+            }
+        }
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib ${notation}
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        where:
+        notationName | notation
+        "direct"     | "comp.helloApi.api"
+        "map"        | "library: 'helloApi', linkage: 'api'"
+    }
+
+    def "executable compiles using functions defined in header-only utility library"() {
+        given:
+        file("src/util/headers/util.h") << """
+            const char *message = "Hello from the utility library";
+"""
+        file("src/main/cpp/main.cpp") << """
+            #include "util.h"
+            #include <iostream>
+
+            int main () {
+                std::cout << message;
+                return 0;
+            }
+"""
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'util'
+            }
+        }
+        util(NativeLibrarySpec)
+    }
+}
+"""
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == "Hello from the utility library"
+    }
+
+    def "executable compiles using functions defined in utility library with build type variants"() {
+        given:
+        file("src/util/debug/util.h") << """
+            const char *message = "Hello from the debug library";
+"""
+        file("src/util/release/util.h") << """
+            const char *message = "Hello from the release library";
+"""
+        file("src/main/cpp/main.cpp") << """
+            #include "util.h"
+            #include <iostream>
+
+            int main () {
+                std::cout << message;
+                return 0;
+            }
+"""
+        buildFile << """
+model {
+    buildTypes {
+        debug
+        release
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'util'
+            }
+        }
+        util(NativeLibrarySpec) {
+            binaries.all { binary ->
+                sources {
+                    buildTypeSources(CppSourceSet) {
+                        sources {
+                            exportedHeaders.srcDir "src/util/\${binary.buildType.name}"
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+        when:
+        succeeds "installDebugMainExecutable", "installReleaseMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable/debug").exec().out == "Hello from the debug library"
+        installation("build/install/mainExecutable/release").exec().out == "Hello from the release library"
+    }
+
+    def "can choose alternative library implementation of api"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        app.alternateLibrarySources*.writeToDir(file("src/hello2"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+                cpp.lib library: 'hello2'
+            }
+        }
+        hello(NativeLibrarySpec)
+        hello2(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+            }
+        }
+    }
+}
+        """
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.alternateLibraryOutput
+    }
+
+    def "can use api linkage for component graph with library dependency cycle"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+        app.greetingsHeader.writeToDir(file("src/hello"))
+        app.greetingsSources*.writeToDir(file("src/greetings"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'greetings', linkage: 'static'
+            }
+        }
+        greetings(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+            }
+        }
+    }
+}
+        """
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+    }
+
+    def "can compile but not link when executable depends on api of library required for linking"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+        """
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.")
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy
new file mode 100755
index 0000000..c356b2a
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy
@@ -0,0 +1,317 @@
+/*
+ * 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.nativeplatform
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Issue
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class LibraryBinariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+    }
+
+    def "executable can use a mix of static and shared libraries"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        helloStatic(NativeLibrarySpec)
+        helloShared(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'helloStatic', linkage: 'static'
+                cpp.lib library: 'helloShared', linkage: 'shared'
+            }
+        }
+    }
+}
+"""
+
+        and:
+        file("src/helloStatic/cpp/hellostatic.cpp") << """
+            #include <iostream>
+
+            void helloStatic() {
+                std::cout << "Hello static";
+            }
+        """
+
+        and:
+        file("src/helloStatic/headers/hellostatic.h") << """
+            void helloStatic();
+        """
+
+        and:
+        file("src/helloShared/cpp/helloshared.cpp") << """
+            #include <iostream>
+            #include "helloshared.h"
+
+            void DLL_FUNC helloShared() {
+                std::cout << "Hello shared";
+            }
+        """
+
+        and:
+        file("src/helloShared/headers/helloshared.h") << """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC helloShared();
+        """
+
+        and:
+        file("src/main/cpp/main.cpp") << """
+            #include "hellostatic.h"
+            #include "helloshared.h"
+
+            int main () {
+                helloStatic();
+                helloShared();
+                return 0;
+            }
+        """
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        staticLibrary("build/binaries/helloStaticStaticLibrary/helloStatic").assertExistsAndDelete()
+        sharedLibrary("build/binaries/helloSharedSharedLibrary/helloShared").assertExistsAndDelete()
+        installation("build/install/mainExecutable")
+                .assertIncludesLibraries("helloShared")
+                .exec().out == "Hello staticHello shared"
+    }
+
+    def "executable can use a combination of libraries from the same and other projects"() {
+        given:
+        settingsFile << """
+include 'exe', 'lib'
+"""
+        buildFile << """
+project('lib') {
+    apply plugin: "cpp"
+    model {
+        components {
+            helloLib(NativeLibrarySpec)
+        }
+    }
+}
+project('exe') {
+// TODO:DAZ Remove this
+    evaluationDependsOn(":lib")
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp {
+                        lib library: "helloMain"
+                        lib project: ":lib", library: "helloLib"
+                    }
+                }
+            }
+            helloMain(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        and:
+        file("lib/src/helloLib/cpp/hellolib.cpp") << """
+            #include <iostream>
+            #include "hellolib.h"
+
+            void DLL_FUNC helloLib() {
+                std::cout << "Hello lib";
+            }
+        """
+
+        and:
+        file("lib/src/helloLib/headers/hellolib.h") << """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC helloLib();
+        """
+
+        and:
+        file("exe/src/helloMain/cpp/hellomain.cpp") << """
+            #include <iostream>
+            #include "hellomain.h"
+
+            void DLL_FUNC helloMain() {
+                std::cout << "Hello main" << std::endl;
+            }
+        """
+
+        and:
+        file("exe/src/helloMain/headers/hellomain.h") << """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC helloMain();
+        """
+
+        and:
+        file("exe/src/main/cpp/main.cpp") << """
+            #include "hellolib.h"
+            #include "hellomain.h"
+
+            int main () {
+                helloMain();
+                helloLib();
+                return 0;
+            }
+        """
+
+        when:
+        succeeds "exe:installMainExecutable"
+
+        then:
+        sharedLibrary("lib/build/binaries/helloLibSharedLibrary/helloLib").assertExistsAndDelete()
+        sharedLibrary("exe/build/binaries/helloMainSharedLibrary/helloMain").assertExistsAndDelete()
+        installation("exe/build/install/mainExecutable")
+                .assertIncludesLibraries("helloLib", "helloMain")
+                .exec().out == "Hello main\nHello lib"
+    }
+
+    def "source set library dependencies are not shared with other source sets"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+apply plugin: "c"
+
+model {
+    components {
+        libCpp(NativeLibrarySpec)
+        libC(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'libCpp', linkage: 'static'
+                c.lib library: 'libC', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+
+        and:
+        file("src/main/headers/head.h") << """
+            void cppOut();
+
+            extern "C" {
+                void cOut();
+            }
+"""
+
+        file("src/main/cpp/main.cpp") << """
+            #include "head.h"
+
+            int main () {
+                cppOut();
+                cOut();
+                return 0;
+            }
+"""
+        and: "C and CPP sources sets depend on header file with same name"
+
+        file("src/main/cpp/test.cpp") << """
+            #include <iostream>
+            #include "output.h"
+
+            void cppOut() {
+                std::cout << OUTPUT << "_";
+            }
+"""
+
+        file("src/main/c/test.c") << """
+            #include <stdio.h>
+            #include "output.h"
+
+            void cOut() {
+                printf(OUTPUT);
+            }
+"""
+
+        and: "Library header files define different OUTPUT values"
+
+        file("src/libCpp/headers/output.h") << """
+            #define OUTPUT "CPP"
+"""
+
+        file("src/libC/headers/output.h") << """
+            #define OUTPUT "C"
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == "CPP_C"
+    }
+
+    @Issue("GRADLE-2925")
+    def "headers for source set added to library binary are available to consuming binary"() {
+        def app = new CppHelloWorldApp()
+        given:
+        buildFile << """
+apply plugin: "cpp"
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            binaries.all {
+                sources {
+                    helloLib(CppSourceSet) {
+                        source.srcDir "src/helloLib/cpp"
+                        exportedHeaders.srcDir "src/helloLib/headers"
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        and:
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/helloLib"))
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy
new file mode 100755
index 0000000..a2d15ce
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy
@@ -0,0 +1,399 @@
+/*
+ * 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.nativeplatform
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ExeWithDiamondDependencyHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Unroll
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class LibraryDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+            allprojects {
+                apply plugin: "cpp"
+                // Allow static libraries to be linked into shared
+                binaries.withType(StaticLibraryBinarySpec) {
+                    if (toolChain in Gcc || toolChain in Clang) {
+                        cppCompiler.args '-fPIC'
+                    }
+                }
+            }
+"""
+    }
+
+    @Unroll
+    def "produces reasonable error message when referenced library #label"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("exe/src/main"))
+        app.library.writeSources(file("lib/src/hello"))
+
+        and:
+        settingsFile.text = "include ':exe', ':other'"
+        buildFile << """
+project(":exe") {
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib ${dependencyNotation}
+                }
+            }
+            hello(NativeLibrarySpec)
+        }
+    }
+}
+project(":other") {
+    model {
+        components {
+            hello(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        when:
+        fails ":exe:mainExecutable"
+
+        then:
+        failure.assertHasDescription(description)
+        failure.assertHasCause(cause)
+
+        where:
+        label                                  | dependencyNotation                      | description                                                | cause
+        "does not exist"                       | "library: 'unknown'"                    | "Could not locate library 'unknown'."                      | "NativeLibrarySpec with name 'unknown' not found."
+        "project that does not exist"          | "project: ':unknown', library: 'hello'" | "Could not locate library 'hello' for project ':unknown'." | "Project with path ':unknown' could not be found in project ':exe'."
+        "does not exist in referenced project" | "project: ':other', library: 'unknown'" | "Could not locate library 'unknown' for project ':other'." | "NativeLibrarySpec with name 'unknown' not found."
+    }
+
+    @Unroll
+    def "can use #notationName notation to reference library in same project"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components { comp ->
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib ${notation}
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        where:
+        notationName | notation
+        "direct"     | "comp.hello"
+        "map"        | "library: 'hello'"
+    }
+
+    @Unroll
+    def "can use map #notationName notation to reference library dependency of binary"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components { comp ->
+        hello(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            binaries.all { binary ->
+                binary.lib ${notation}
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        where:
+        notationName | notation
+        "direct"     | "comp.hello"
+        "map"        | "library: 'hello'"
+    }
+
+    def "can use map notation to reference static library in same project"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/hello"))
+
+        and:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
+    }
+
+    @Unroll
+    def "can use map notation to reference library in different project#label"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.executable.writeSources(file("exe/src/main"))
+        app.library.writeSources(file("lib/src/hello"))
+
+        and:
+        settingsFile.text = "include ':lib', ':exe'"
+        buildFile << """
+project(":exe") {
+    ${explicitEvaluation}
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'hello'
+                }
+            }
+        }
+    }
+}
+project(":lib") {
+    model {
+        components {
+            hello(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        when:
+        if (configureOnDemand) {
+            executer.withArgument('--configure-on-demand')
+        }
+        succeeds ":exe:installMainExecutable"
+
+        then:
+        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
+
+        where:
+        label                       | configureOnDemand | explicitEvaluation
+        ""                          | false             | ""
+        " with configure-on-demand" | true              | ""
+//        " with evaluationDependsOn" | false             | "evaluationDependsOn(':lib')"
+        " with afterEvaluate"       | false             | """
+project.afterEvaluate {
+    binaries*.libs*.linkFiles.files.each { println it }
+}
+"""
+    }
+
+    def "can use map notation to transitively reference libraries in different projects"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("greet/src/greetings"))
+
+        and:
+        settingsFile.text = "include ':exe', ':lib', ':greet'"
+        buildFile << """
+project(":exe") {
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'hello'
+                }
+            }
+        }
+    }
+}
+project(":lib") {
+    model {
+        components {
+            hello(NativeLibrarySpec) {
+                sources {
+                    cpp.lib project: ':greet', library: 'greetings', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+project(":greet") {
+    model {
+        components {
+            greetings(NativeLibrarySpec)
+        }
+    }
+}
+"""
+
+        when:
+        succeeds ":exe:installMainExecutable"
+
+        then:
+        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
+    }
+
+    def "can have component graph with project dependency cycle"() {
+        given:
+        def app = new ExeWithLibraryUsingLibraryHelloWorldApp()
+        app.writeSources(file("exe/src/main"), file("lib/src/hello"), file("exe/src/greetings"))
+
+        and:
+        settingsFile.text = "include ':exe', ':lib'"
+        buildFile << """
+project(":exe") {
+    apply plugin: "cpp"
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':lib', library: 'hello'
+                }
+            }
+            greetings(NativeLibrarySpec)
+        }
+    }
+}
+project(":lib") {
+    apply plugin: "cpp"
+    model {
+        components {
+            hello(NativeLibrarySpec) {
+                sources {
+                    cpp.lib project: ':exe', library: 'greetings', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds ":exe:installMainExecutable"
+
+        then:
+        installation("exe/build/install/mainExecutable").exec().out == app.englishOutput
+    }
+
+    def "can have component graph with diamond dependency"() {
+        given:
+        def app = new ExeWithDiamondDependencyHelloWorldApp()
+        app.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+
+        and:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello"
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        greetings(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        and:
+        notExecuted ":greetingsSharedLibrary"
+        sharedLibrary("build/binaries/greetingsSharedLibrary/greetings").assertDoesNotExist()
+    }
+
+    def "can have component graph with both static and shared variants of same library"() {
+        given:
+        def app = new ExeWithDiamondDependencyHelloWorldApp()
+        app.writeSources(file("src/main"), file("src/hello"), file("src/greetings"))
+
+        and:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: "hello", linkage: "shared"
+                cpp.lib library: "greetings", linkage: "shared"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                cpp.lib library: "greetings", linkage: "static"
+            }
+        }
+        greetings(NativeLibrarySpec)
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.englishOutput
+
+        and:
+        executedAndNotSkipped ":greetingsSharedLibrary", ":greetingsStaticLibrary"
+        sharedLibrary("build/binaries/greetingsSharedLibrary/greetings").assertExists()
+        staticLibrary("build/binaries/greetingsStaticLibrary/greetings").assertExists()
+
+        and:
+        println executable("build/binaries/mainExecutable/main").binaryInfo.listLinkedLibraries()
+        println sharedLibrary("build/binaries/helloSharedLibrary/hello").binaryInfo.listLinkedLibraries()
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy
new file mode 100755
index 0000000..a5ac95c
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy
@@ -0,0 +1,360 @@
+/*
+ * 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.nativeplatform
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.CppCallingCHelloWorldApp
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.hamcrest.Matchers
+
+import static org.gradle.util.Matchers.containsText
+
+class NativeBinariesIntegrationTest 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"
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").assertDoesNotExist()
+    }
+
+    def "assemble task constructs all buildable binaries"() {
+        given:
+        new CHelloWorldApp().writeSources(file("src/main"))
+
+        and:
+        buildFile << """
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+
+apply plugin: 'c'
+
+model {
+    platforms {
+        unknown {
+            architecture "unknown"
+        }
+    }
+}
+model {
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "unknown"
+            targetPlatform NativePlatforms.defaultPlatformName
+        }
+    }
+}
+"""
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":${NativePlatforms.defaultPlatformName}MainExecutable"
+        notExecuted ":unknownMainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/${NativePlatforms.defaultPlatformName}/main").assertExists()
+        executable("build/binaries/mainExecutable/unknown/main").assertDoesNotExist()
+    }
+
+    def "assemble task produces sensible error when there are no buildable binaries" () {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    platforms {
+        unknown {
+            architecture "unknown"
+        }
+    }
+}
+model {
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "unknown"
+        }
+        hello(NativeLibrarySpec) {
+            targetPlatform "unknown"
+        }
+        another(NativeLibrarySpec) {
+            binaries.all { buildable = false }
+        }
+    }
+}
+"""
+        when:
+        fails "assemble"
+
+        then:
+        failureDescriptionContains("Execution failed for task ':assemble'.")
+        failure.assertThatCause(Matchers.<String>allOf(
+                Matchers.startsWith("No buildable binaries found:"),
+                Matchers.containsString("helloSharedLibrary: No tool chain is available to build for platform 'unknown'"),
+                Matchers.containsString("helloStaticLibrary: No tool chain is available to build for platform 'unknown'"),
+                Matchers.containsString("mainExecutable: No tool chain is available to build for platform 'unknown'"),
+                Matchers.containsString("anotherStaticLibrary: Disabled by user"),
+                Matchers.containsString("anotherSharedLibrary: Disabled by user")
+        ))
+    }
+
+    def "assemble executable from component with multiple language source sets"() {
+        given:
+        useMixedSources()
+
+        when:
+        buildFile << """
+apply plugin: "c"
+apply plugin: "cpp"
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c {
+                    source.srcDir "src/test/c"
+                    exportedHeaders.srcDir "src/test/headers"
+                }
+                cpp {
+                    source.srcDir "src/test/cpp"
+                    exportedHeaders.srcDir "src/test/headers"
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    // TODO:DAZ Should not need a component here
+    def "assemble executable binary directly from language source sets"() {
+        given:
+        useMixedSources()
+
+        when:
+        buildFile << """
+apply plugin: "c"
+apply plugin: "cpp"
+
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+
+binaries.all {
+    sources {
+        testCpp(CppSourceSet) {
+            source.srcDir "src/test/cpp"
+            exportedHeaders.srcDir "src/test/headers"
+        }
+        testC(CSourceSet) {
+            source.srcDir "src/test/c"
+            exportedHeaders.srcDir "src/test/headers"
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "mainExecutable"
+
+        and:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "cannot add java sources to native binary"() {
+        given:
+        useMixedSources()
+        file("src/test/java/HelloWorld.java") << """
+    This would not compile
+"""
+
+        when:
+        buildFile << """
+apply plugin: "c"
+apply plugin: "cpp"
+apply plugin: "java"
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                java(JavaSourceSet) {
+                    source.srcDir "src/test/java"
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        fails "mainExecutable"
+        failure.assertHasCause("Exception thrown while executing model rule: model.components > create(main)");
+        failure.assertHasCause("Cannot create a JavaSourceSet because this type is not known to this container. Known types are: CSourceSet, CppSourceSet")
+    }
+
+    private def useMixedSources() {
+        helloWorldApp.writeSources(file("src/test"))
+    }
+
+    def "build fails when link executable fails"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        and:
+        file("src", "main", "cpp", "helloworld.cpp") << """
+            int thing() { return 0; }
+        """
+
+        expect:
+        fails "mainExecutable"
+        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.");
+        failure.assertHasCause("A build operation failed.")
+        def exeName = executable("build/binaries/mainExecutable/main").file.name
+        failure.assertThatCause(containsText("Linker failed while linking ${exeName}"))
+    }
+
+    def "build fails when link library fails"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        main(NativeLibrarySpec)
+    }
+}
+"""
+
+        and:
+        file("src/main/cpp/hello1.cpp") << """
+            void hello() {
+            }
+"""
+
+        and:
+        file("src/main/cpp/hello2.cpp") << """
+            void hello() {
+            }
+"""
+
+        when:
+        fails "mainSharedLibrary"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':linkMainSharedLibrary'.");
+        failure.assertHasCause("A build operation failed.")
+        def libName = sharedLibrary("build/binaries/mainSharedLibrary/main").file.name
+        failure.assertThatCause(containsText("Linker failed while linking ${libName}"))
+    }
+
+    def "build fails when create static library fails"() {
+        given:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        main(NativeLibrarySpec)
+    }
+}
+
+binaries.withType(StaticLibraryBinarySpec) {
+    staticLibArchiver.args "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("A build operation failed.")
+        def libName = staticLibrary("build/binaries/mainSharedLibrary/main").file.name
+        failure.assertThatCause(containsText("Static library archiver failed while archiving ${libName}"))
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    def "installed executable receives command-line parameters"() {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        echo(NativeExecutableSpec)
+    }
+}
+"""
+        file("src/echo/c/main.c") << """
+// Simple hello world app
+#include <stdio.h>
+
+// Print the command line args
+int main (int argc, char *argv[]) {
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        printf("[%s] ", argv[i]);
+    }
+    printf("\\n");
+    return 0;
+}
+"""
+
+        when:
+        succeeds "installEchoExecutable"
+
+        then:
+        def installation = installation("build/install/echoExecutable")
+        installation.exec().out == "\n"
+        installation.exec("foo", "bar").out == "[foo] [bar] \n"
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..4ceb6a5
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativePlatformSamplesIntegrationTest.groovy
@@ -0,0 +1,240 @@
+/*
+ * 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.nativeplatform
+
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GccCompatible
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class NativePlatformSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule final TestNameTestDirectoryProvider testDirProvider = new TestNameTestDirectoryProvider()
+    @Rule public final Sample cppLib = sample(testDirProvider, 'cpp-lib')
+    @Rule public final Sample cppExe = sample(testDirProvider, 'cpp-exe')
+    @Rule public final Sample multiProject = sample(testDirProvider, 'multi-project')
+    @Rule public final Sample flavors = sample(testDirProvider, 'flavors')
+    @Rule public final Sample variants = sample(testDirProvider, 'variants')
+    @Rule public final Sample toolChains = sample(testDirProvider, 'tool-chains')
+    @Rule public final Sample prebuilt = sample(testDirProvider, 'prebuilt')
+    @Rule public final Sample targetPlatforms = sample(testDirProvider, 'target-platforms')
+
+    private static Sample sample(TestDirectoryProvider testDirectoryProvider, String name) {
+        return new Sample(testDirectoryProvider, "native-binaries/${name}", name)
+    }
+
+    def "exe"() {
+        given:
+        // Need to PATH to be set to find the 'strip' executable
+        toolChain.initialiseEnvironment()
+
+        and:
+        sample cppExe
+
+        when:
+        run "installMain"
+
+        then:
+        executedAndNotSkipped ":compileMainExecutableMainCpp", ":linkMainExecutable", ":stripMainExecutable", ":mainExecutable"
+
+        and:
+        executable(cppExe.dir.file("build/binaries/mainExecutable/main")).exec().out == "Hello, World!\n"
+        installation(cppExe.dir.file("build/install/mainExecutable")).exec().out == "Hello, World!\n"
+
+        cleanup:
+        toolChain.resetEnvironment()
+    }
+
+    def "lib"() {
+        given:
+        sample cppLib
+
+        when:
+        run "mainSharedLibrary"
+
+        then:
+        executedAndNotSkipped ":compileMainSharedLibraryMainCpp", ":linkMainSharedLibrary", ":mainSharedLibrary"
+
+        and:
+        sharedLibrary(cppLib.dir.file("build/binaries/mainSharedLibrary/main")).assertExists()
+
+        when:
+        sample cppLib
+        run "mainStaticLibrary"
+
+        then:
+        executedAndNotSkipped ":compileMainStaticLibraryMainCpp", ":createMainStaticLibrary", ":mainStaticLibrary"
+
+        and:
+        staticLibrary(cppLib.dir.file("build/binaries/mainStaticLibrary/main")).assertExists()
+    }
+
+    def flavors() {
+        when:
+        sample flavors
+        run "installEnglishMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileEnglishHelloSharedLibraryHelloCpp", ":linkEnglishHelloSharedLibrary", ":englishHelloSharedLibrary"
+        executedAndNotSkipped ":compileEnglishMainExecutableMainCpp", ":linkEnglishMainExecutable", ":englishMainExecutable"
+
+        and:
+        executable(flavors.dir.file("build/binaries/mainExecutable/english/main")).assertExists()
+        sharedLibrary(flavors.dir.file("build/binaries/helloSharedLibrary/english/hello")).assertExists()
+
+        and:
+        installation(flavors.dir.file("build/install/mainExecutable/english")).exec().out == "Hello world!\n"
+
+        when:
+        sample flavors
+        run "installFrenchMainExecutable"
+
+        then:
+        executedAndNotSkipped ":compileFrenchHelloSharedLibraryHelloCpp", ":linkFrenchHelloSharedLibrary", ":frenchHelloSharedLibrary"
+        executedAndNotSkipped ":compileFrenchMainExecutableMainCpp", ":linkFrenchMainExecutable", ":frenchMainExecutable"
+
+        and:
+        executable(flavors.dir.file("build/binaries/mainExecutable/french/main")).assertExists()
+        sharedLibrary(flavors.dir.file("build/binaries/helloSharedLibrary/french/hello")).assertExists()
+
+        and:
+        installation(flavors.dir.file("build/install/mainExecutable/french")).exec().out == "Bonjour monde!\n"
+    }
+
+    def variants() {
+        when:
+        sample variants
+        run "assemble"
+
+        then:
+        final debugX86 = executable(variants.dir.file("build/binaries/mainExecutable/x86Debug/main"))
+        final releaseX86 = executable(variants.dir.file("build/binaries/mainExecutable/x86Release/main"))
+        final debugX64 = executable(variants.dir.file("build/binaries/mainExecutable/x64Debug/main"))
+        final releaseX64 = executable(variants.dir.file("build/binaries/mainExecutable/x64Release/main"))
+        final debugIA64 = executable(variants.dir.file("build/binaries/mainExecutable/itaniumDebug/main"))
+        final releaseIA64 = executable(variants.dir.file("build/binaries/mainExecutable/itaniumRelease/main"))
+
+        debugX86.binaryInfo.arch.name == "x86"
+        debugX86.assertDebugFileExists()
+        debugX86.exec().out == "Hello world!\n"
+
+        releaseX86.binaryInfo.arch.name == "x86"
+        releaseX86.assertDebugFileDoesNotExist()
+        releaseX86.exec().out == "Hello world!\n"
+
+        // x86_64 binaries not supported on MinGW or cygwin
+        if (toolChain.id == "mingw" || toolChain.id == "gcccygwin") {
+            debugX64.assertDoesNotExist()
+            releaseX64.assertDoesNotExist()
+        } else {
+            debugX64.binaryInfo.arch.name == "x86_64"
+            releaseX64.binaryInfo.arch.name == "x86_64"
+        }
+
+        // Itanium not built
+        debugIA64.assertDoesNotExist()
+        releaseIA64.assertDoesNotExist()
+    }
+
+    def "tool chains"() {
+        given:
+        sample toolChains
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        executable(toolChains.dir.file("build/binaries/mainExecutable/main")).exec().out == "Hello from ${toolChain.typeDisplayName}!\n"
+    }
+
+    def multiProject() {
+        given:
+        sample multiProject
+
+        when:
+        run "installMainExecutable"
+
+        then:
+        ":exe:mainExecutable" in executedTasks
+
+        and:
+        sharedLibrary(multiProject.dir.file("lib/build/binaries/mainSharedLibrary/main")).assertExists()
+        executable(multiProject.dir.file("exe/build/binaries/mainExecutable/main")).assertExists()
+        installation(multiProject.dir.file("exe/build/install/mainExecutable")).exec().out == "Hello, World!\n"
+    }
+
+    @RequiresInstalledToolChain(GccCompatible)
+    def "target platforms"() {
+        given:
+        sample targetPlatforms
+        and:
+        targetPlatforms.dir.file("build.gradle") << """
+model {
+    toolChains {
+        all{
+            target("arm"){
+                cppCompiler.withArguments { args ->
+                    args << "-m32"
+                }
+                linker.withArguments { args ->
+                    args << "-m32"
+                }
+            }
+            target("sparc")
+        }
+    }
+}
+"""
+
+        when:
+        run "installArmMainExecutable", "installSparcMainExecutable"
+
+        then:
+        executable(targetPlatforms.dir.file("build/binaries/mainExecutable/arm/main")).exec().out == "Hello from ${toolChain.typeDisplayName}!\n"
+        executable(targetPlatforms.dir.file("build/binaries/mainExecutable/arm/main")).binaryInfo.arch.isI386()
+
+        executable(targetPlatforms.dir.file("build/binaries/mainExecutable/sparc/main")).exec().out == "Hello from ${toolChain.typeDisplayName}!\n"
+    }
+
+    def prebuilt() {
+        given:
+        inDirectory(prebuilt.dir.file("3rd-party-lib/util"))
+        run "assemble"
+
+        and:
+        sample prebuilt
+
+        when:
+        succeeds "assemble"
+
+        then:
+
+        executable(prebuilt.dir.file("build/binaries/mainExecutable/debug/main")).exec().out ==
+"""Built with Boost version: 1_55
+Util build type: DEBUG
+"""
+        executable(prebuilt.dir.file("build/binaries/mainExecutable/release/main")).exec().out ==
+"""Built with Boost version: 1_55
+Util build type: RELEASE
+"""
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy
new file mode 100755
index 0000000..560e3c7
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class PrebuiltLibrariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    final app = new CppHelloWorldApp()
+
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("libs/src/hello"))
+
+        file("libs/build.gradle") << """
+apply plugin: 'cpp'
+model {
+    flavors {
+        english
+        french
+    }
+    components {
+        hello(NativeLibrarySpec) {
+            binaries.all {
+                if (flavor == flavors.french) {
+                    cppCompiler.define "FRENCH"
+                }
+            }
+        }
+    }
+}
+"""
+    }
+
+    private void preBuildLibrary() {
+        executer.inDirectory(file("libs"))
+        run "assemble"
+    }
+
+    def "can link to a prebuilt header-only library with api linkage"() {
+        given:
+        app.alternateLibrarySources*.writeToDir(file("src/main"))
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'api'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.alternateLibraryOutput
+    }
+
+    def "can link to a prebuilt library with static and shared linkage"() {
+        given:
+        preBuildLibrary()
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+                binaries.withType(StaticLibraryBinary) {
+                    def libName = targetPlatform.operatingSystem.windows ? 'hello.lib' : 'libhello.a'
+                    staticLibraryFile = file("libs/build/binaries/helloStaticLibrary/english/\${libName}")
+                }
+                binaries.withType(SharedLibraryBinary) {
+                    def os = targetPlatform.operatingSystem
+                    def baseDir = "libs/build/binaries/helloSharedLibrary/french"
+                    if (os.windows) {
+                        // Windows uses a .dll file, and a different link file if it exists (not Cygwin or MinGW)
+                        sharedLibraryFile = file("\${baseDir}/hello.dll")
+                        if (file("\${baseDir}/hello.lib").exists()) {
+                            sharedLibraryLinkFile = file("\${baseDir}/hello.lib")
+                        }
+                    } else if (os.macOsX) {
+                        sharedLibraryFile = file("\${baseDir}/libhello.dylib")
+                    } else {
+                        sharedLibraryFile = file("\${baseDir}/libhello.so")
+                    }
+                }
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+        mainStatic(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    source.srcDir "src/main/cpp"
+                    lib library: 'hello', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable", "installMainStaticExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.frenchOutput
+        installation("build/install/mainStaticExecutable").exec().out == app.englishOutput
+    }
+
+    def "searches all prebuilt library repositories"() {
+        given:
+        preBuildLibrary()
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs1(PrebuiltLibraries) {
+            nope {
+                headers.srcDir "not/here"
+            }
+        }
+        libs2(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+                binaries.withType(StaticLibraryBinary) {
+                    def libName = targetPlatform.operatingSystem.windows ? 'hello.lib' : 'libhello.a'
+                    staticLibraryFile = file("libs/build/binaries/helloStaticLibrary/french/\${libName}")
+                }
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+        """
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == app.frenchOutput
+    }
+
+    def "locates prebuilt library in another project"() {
+        given:
+        app.executable.writeSources(file("projectA/src/main"))
+        app.librarySources*.writeToDir(file("projectA/src/main"))
+        app.libraryHeader.writeToDir(file("projectB/libs/src/hello"))
+
+        and:
+        settingsFile.text = "include ':projectA', ':projectB'"
+        buildFile << """
+project(':projectA') {
+    apply plugin: 'cpp'
+    model {
+        components {
+            main(NativeExecutableSpec) {
+                sources {
+                    cpp.lib project: ':projectB', library: 'hello', linkage: 'api'
+                }
+            }
+        }
+    }
+}
+        """
+
+        file("projectB/build.gradle") << """
+apply plugin: 'cpp'
+
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "../libs/src/hello/headers"
+            }
+        }
+    }
+}
+        """
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("projectA/build/install/mainExecutable").exec().out == app.englishOutput
+    }
+
+    def "produces reasonable error message when no output file is defined for binary"() {
+        given:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Static library file not set for prebuilt library 'hello'.")
+    }
+
+    def "produces reasonable error message when prebuilt library output file does not exist"() {
+        given:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+                binaries.withType(StaticLibraryBinary) { binary ->
+                    staticLibraryFile = file("does_not_exist")
+                }
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "tasks"
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Static library file ${file("does_not_exist").absolutePath} does not exist for prebuilt library 'hello'.")
+    }
+
+    def "produces reasonable error message when prebuilt library does not exist"() {
+        given:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello
+        }
+        libs2(PrebuiltLibraries) {
+            hello2
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'other'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Could not locate library 'other'.")
+        failure.assertHasCause("NativeLibrarySpec with name 'other' not found.")
+        failure.assertHasCause("Prebuilt library with name 'other' not found in repositories '[libs, libs2]'.")
+    }
+
+    def "produces reasonable error message when prebuilt library does not exist in a different project"() {
+        given:
+        settingsFile.text = "include ':projectA', ':projectB'"
+        file("projectA/build.gradle") << """
+apply plugin: 'cpp'
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            hello {
+                headers.srcDir "libs/src/hello/headers"
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib project: ':projectB', library: 'hello', linkage: 'api'
+            }
+        }
+    }
+}
+        """
+
+        file("projectB/build.gradle") << """
+            apply plugin: 'cpp'
+            model {
+                repositories {
+                    libs(PrebuiltLibraries) {
+                        hello1
+                    }
+                    libs2(PrebuiltLibraries) {
+                        hello2
+                    }
+                }
+            }
+        """
+
+        when:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Could not locate library 'hello' for project ':projectB'.")
+        failure.assertHasCause("NativeLibrarySpec with name 'hello' not found.")
+        failure.assertHasCause("Prebuilt library with name 'hello' not found in repositories '[libs, libs2]'.")
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy
new file mode 100755
index 0000000..bbaf0c3
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.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.nativeplatform
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.NOT_WINDOWS)
+class SharedLibrarySoNameIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+
+        def app = new CppHelloWorldApp()
+        app.library.writeSources(file("src/hello"))
+
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    components {
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+    }
+
+    def "library soname is file name when installName is not set"() {
+        when:
+        succeeds "helloSharedLibrary"
+
+        then:
+        final sharedLibrary = sharedLibrary("build/binaries/helloSharedLibrary/hello")
+        sharedLibrary.soName == sharedLibrary.file.name
+    }
+
+    def "library soname uses specified installName"() {
+        given:
+        buildFile << """
+tasks.withType(LinkSharedLibrary) {
+    it.installName = 'hello-install-name'
+}
+"""
+
+        when:
+        succeeds "helloSharedLibrary"
+
+        then:
+        sharedLibrary("build/binaries/helloSharedLibrary/hello").soName == "hello-install-name"
+    }
+
+    def "library soname defaults when installName is null"() {
+        given:
+        buildFile << """
+tasks.withType(LinkSharedLibrary) {
+    it.installName = null
+}
+"""
+
+        when:
+        succeeds "helloSharedLibrary"
+
+        then:
+        final library = sharedLibrary("build/binaries/helloSharedLibrary/hello")
+        def expectedSoName = OperatingSystem.current().macOsX ? library.file.absolutePath : null
+        library.soName == expectedSoName
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/BinaryNativePlatformIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/BinaryNativePlatformIntegrationTest.groovy
new file mode 100755
index 0000000..1c28950
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/BinaryNativePlatformIntegrationTest.groovy
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform
+
+import net.rubygrapefruit.platform.Native
+import net.rubygrapefruit.platform.SystemInfo
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.PlatformDetectingTestApp
+import org.gradle.nativeplatform.fixtures.binaryinfo.DumpbinBinaryInfo
+import org.gradle.nativeplatform.fixtures.binaryinfo.OtoolBinaryInfo
+import org.gradle.nativeplatform.fixtures.binaryinfo.ReadelfBinaryInfo
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Unroll
+
+ at Requires(TestPrecondition.NOT_UNKNOWN_OS)
+class BinaryNativePlatformIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def testApp = new PlatformDetectingTestApp()
+    def os = OperatingSystem.current()
+
+    def setup() {
+        buildFile << """
+plugins {
+    id 'cpp'
+}
+model {
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        testApp.writeSources(file("src/main"))
+    }
+
+    // Tests will only work on x86 and x86-64 architectures
+    def currentArch() {
+        // On windows we currently target i386 by default, even on amd64
+        if (OperatingSystem.current().windows || Native.get(SystemInfo).architecture == SystemInfo.Architecture.i386) {
+            return [name: "x86", altName: "i386"]
+        }
+        return [name: "x86-64", altName: "amd64"]
+    }
+
+    def "build binary for a default target platform"() {
+        given:
+        def arch = currentArch();
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executedAndNotSkipped(":mainExecutable")
+        executable("build/binaries/mainExecutable/main").binaryInfo.arch.name == arch.name
+        executable("build/binaries/mainExecutable/main").exec().out == "${arch.altName} ${os.familyName}" * 2
+        binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objs/mainExecutable/mainCpp")).arch.name == arch.name
+    }
+
+    def "configure component for a single target platform"() {
+        when:
+        buildFile << """
+model {
+    platforms {
+        sparc {
+            architecture "sparc"
+        }
+        x86 {
+            architecture "x86"
+        }
+        x86_64 {
+            architecture "x86_64"
+        }
+    }
+    components {
+        main.targetPlatform "x86"
+    }
+}
+"""
+
+        and:
+        succeeds "assemble"
+
+        then:
+        // Platform dimension is flattened since there is only one possible value
+        executedAndNotSkipped(":mainExecutable")
+        executable("build/binaries/mainExecutable/main").binaryInfo.arch.name == "x86"
+        executable("build/binaries/mainExecutable/main").exec().out == "i386 ${os.familyName}" * 2
+    }
+
+    def "defaults to current platform when platforms are defined but not targeted"() {
+        def arch = currentArch()
+        when:
+        buildFile << """
+model {
+    platforms {
+        sparc {
+            architecture "sparc"
+        }
+        x86 {
+            architecture "x86"
+        }
+    }
+}
+"""
+
+        and:
+        succeeds "assemble"
+
+        then:
+        // Platform dimension is flattened since there is only one possible value
+        executedAndNotSkipped(":mainExecutable")
+        executable("build/binaries/mainExecutable/main").binaryInfo.arch.name == arch.name
+        executable("build/binaries/mainExecutable/main").exec().out == "${arch.altName} ${os.familyName}" * 2
+    }
+
+    def "library with matching platform is enforced by dependency resolution"() {
+        given:
+        testApp.executable.writeSources(file("src/exe"))
+        testApp.library.writeSources(file("src/hello"))
+        when:
+        buildFile << """
+model {
+    platforms {
+        sparc {
+            architecture "sparc"
+        }
+        x86 {
+            architecture "x86"
+        }
+        x86_64 {
+            architecture "x86_64"
+        }
+    }
+    components {
+        exe(NativeExecutableSpec) {
+            targetPlatform "x86"
+            sources {
+                cpp.lib library: "hello", linkage: "static"
+            }
+        }
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+}
+"""
+
+        and:
+        succeeds "exeExecutable"
+
+        then:
+        // Platform dimension is flattened since there is only one possible value
+        executedAndNotSkipped(":exeExecutable")
+        executable("build/binaries/exeExecutable/exe").binaryInfo.arch.name == "x86"
+        executable("build/binaries/exeExecutable/exe").exec().out == "i386 ${os.familyName}" * 2
+    }
+
+    def "library with no platform defined is correctly chosen by dependency resolution"() {
+        def arch = currentArch();
+
+        given:
+        testApp.executable.writeSources(file("src/exe"))
+        testApp.library.writeSources(file("src/hello"))
+        when:
+        buildFile << """
+model {
+    components {
+        exe(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+
+        and:
+        succeeds "exeExecutable"
+
+        then:
+        executedAndNotSkipped(":exeExecutable")
+        executable("build/binaries/exeExecutable/exe").binaryInfo.arch.name == arch.name
+        executable("build/binaries/exeExecutable/exe").exec().out == "${arch.altName} ${os.familyName}" * 2
+    }
+
+    def "build binary for multiple target architectures"() {
+        when:
+        buildFile << """
+model {
+    platforms {
+        x86 {
+            architecture "x86"
+        }
+        x86_64 {
+            architecture "x86_64"
+        }
+        itanium {
+            architecture "ia-64"
+        }
+        arm {
+            architecture "arm"
+        }
+    }
+    components {
+        main {
+            targetPlatform "x86"
+            targetPlatform "x86_64"
+            targetPlatform "itanium"
+            targetPlatform "arm"
+        }
+    }
+}
+
+"""
+
+        and:
+        succeeds "assemble"
+
+        then:
+        executable("build/binaries/mainExecutable/x86/main").binaryInfo.arch.name == "x86"
+        executable("build/binaries/mainExecutable/x86/main").exec().out == "i386 ${os.familyName}" * 2
+        binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objs/mainExecutable/x86/mainCpp")).arch.name == "x86"
+
+        // x86_64 binaries not supported on MinGW or cygwin
+        if (toolChain.id == "mingw" || toolChain.id == "gcccygwin") {
+            executable("build/binaries/mainExecutable/x86_64/main").assertDoesNotExist()
+        } else {
+            executable("build/binaries/mainExecutable/x86_64/main").binaryInfo.arch.name == "x86_64"
+            executable("build/binaries/mainExecutable/x86_64/main").exec().out == "amd64 ${os.familyName}" * 2
+            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objs/mainExecutable/x86_64/mainCpp")).arch.name == "x86_64"
+        }
+
+        // Itanium only supported on visualCpp
+        if (toolChain.visualCpp) {
+            executable("build/binaries/mainExecutable/itanium/main").binaryInfo.arch.name == "ia-64"
+            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objs/mainExecutable/itanium/mainCpp")).arch.name == "ia-64"
+        } else {
+            executable("build/binaries/mainExecutable/itanium/main").assertDoesNotExist()
+        }
+
+        // ARM only supported on visualCpp 2013
+        if (toolChain.meets(ToolChainRequirement.VisualCpp2013)) {
+            executable("build/binaries/mainExecutable/arm/main").binaryInfo.arch.name == "arm"
+            binaryInfo(objectFileFor(file("src/main/cpp/main.cpp"), "build/objs/mainExecutable/arm/mainCpp")).arch.name == "arm"
+        } else {
+            executable("build/binaries/mainExecutable/arm/main").assertDoesNotExist()
+        }
+    }
+
+    def "can configure binary for multiple target operating systems"() {
+        String currentOs
+        if (os.windows) {
+            currentOs = "windows"
+        } else if (os.linux) {
+            currentOs = "linux"
+        } else if (os.macOsX) {
+            currentOs = "osx"
+        } else {
+            throw new AssertionError("Unexpected operating system")
+        }
+
+        when:
+        buildFile << """
+model {
+    platforms {
+        osx {
+            operatingSystem "osx"
+            architecture "x86"
+        }
+        windows {
+            operatingSystem "windows"
+            architecture "x86"
+        }
+        linux {
+            operatingSystem "linux"
+            architecture "x86"
+        }
+    }
+    components {
+        main.targetPlatform "$currentOs"
+    }
+}
+
+binaries.matching({ it.targetPlatform.operatingSystem.windows }).all {
+    cppCompiler.define "FRENCH"
+}
+        """
+        and:
+        succeeds "assemble"
+
+        then:
+        if (os.windows) {
+            executable("build/binaries/mainExecutable/main").exec().out == "i386 windows" * 2
+        } else if (os.linux) {
+            executable("build/binaries/mainExecutable/main").exec().out == "i386 linux" * 2
+        } else if (os.macOsX) {
+            executable("build/binaries/mainExecutable/main").exec().out == "i386 os x" * 2
+        } else {
+            throw new AssertionError("Unexpected operating system")
+        }
+    }
+
+    @Unroll
+    def "fails with reasonable error message when trying to build for an #type"() {
+        when:
+        buildFile << """
+model {
+    platforms {
+        unavailable {
+            ${config}
+        }
+    }
+    components {
+        main.targetPlatform 'unavailable'
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainCpp'.")
+        failure.assertHasCause("""No tool chain is available to build for platform 'unavailable':
+  - ${toolChain.instanceDisplayName}: Don't know how to build for platform 'unavailable'.""")
+
+        where:
+        type                           | config
+        "unavailable architecture"     | "architecture 'sparc'"
+        "unavailable operating system" | "operatingSystem 'solaris'"
+        "unknown architecture"         | "architecture 'unknown'"
+        "unknown operating system"     | "operatingSystem 'unknown'"
+    }
+
+    def "fails with reasonable error message when trying to target an unknown platform"() {
+        when:
+        settingsFile << "rootProject.name = 'bad-platform'"
+        buildFile << """
+model {
+    platforms {
+        main
+    }
+    components {
+        main.targetPlatform "unknown"
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasCause("Exception thrown while executing model rule: org.gradle.nativeplatform.plugins.NativeComponentModelPlugin\$Rules#createNativeBinaries")
+        failure.assertHasCause("Invalid NativePlatform: unknown")
+    }
+
+    def "fails with reasonable error message when depended on library has no variant with matching platform"() {
+        when:
+        settingsFile << "rootProject.name = 'no-matching-platform'"
+        buildFile << """
+model {
+    platforms {
+        one
+        two
+    }
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "two"
+        }
+        main {
+            targetPlatform "one"
+            sources {
+                cpp.lib library: 'hello'
+            }
+        }
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("No shared library binary available for library 'hello' with [flavor: 'default', platform: 'one', buildType: 'debug']")
+    }
+
+    def binaryInfo(TestFile file) {
+        file.assertIsFile()
+        if (os.macOsX) {
+            return new OtoolBinaryInfo(file)
+        }
+        if (os.windows) {
+            return new DumpbinBinaryInfo(file)
+        }
+        return new ReadelfBinaryInfo(file)
+    }
+
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginIntegrationTest.groovy
new file mode 100644
index 0000000..bc00536
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginIntegrationTest.groovy
@@ -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.nativeplatform.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class NativeComponentPluginIntegrationTest extends WellBehavedPluginTest {
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/GeneratedSourcesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/GeneratedSourcesIntegrationTest.groovy
new file mode 100755
index 0000000..d72314c
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/GeneratedSourcesIntegrationTest.groovy
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.sourceset
+import org.apache.commons.io.FileUtils
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.WindowsResourceHelloWorldApp
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VisualCpp
+// TODO:DAZ Test incremental
+class GeneratedSourcesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def setup() {
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+class GenerateSources extends DefaultTask {
+    @InputDirectory File inputDir
+    @OutputDirectory File sourceDir
+    @OutputDirectory @Optional File headerDir
+
+    @TaskAction
+    void processInputFiles() {
+        project.copy {
+            from inputDir
+            into sourceDir.parentFile
+            filter { String line ->
+                line.replaceAll('REMOVE_ME', '')
+            }
+        }
+    }
+}
+task generateCSources(type: GenerateSources) {
+    inputDir project.file("src/input")
+    headerDir project.file("build/src/generated/headers")
+    sourceDir project.file("build/src/generated/c")
+}
+"""
+    }
+
+    private void degenerateInputSources() {
+        FileUtils.listFiles(file("src/input"), null, true).each { File file ->
+            file.text = "REMOVE_ME\n" + file.text
+        }
+    }
+
+    def "generator task produces c sources and headers"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.generatedBy tasks.generateCSources
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "generator task produces sources for dependent source set with headers only"() {
+        given:
+        // Write sources to src/main, headers to src/input
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.sourceFiles*.writeToDir(file("src/main"))
+        app.library.headerFiles*.writeToDir(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+    apply plugin: 'c'
+
+model {
+    components { comp ->
+        headersOnly(NativeLibrarySpec) {
+            sources {
+                c.generatedBy tasks.generateCSources
+            }
+        }
+        main(NativeExecutableSpec) {
+            sources {
+                c.lib comp.headersOnly.sources.c
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "generator task produces sources for dependent source set"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                generatedC(CSourceSet) {
+                    generatedBy tasks.generateCSources
+                }
+                c.lib sources.generatedC
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "can have library composed of generated sources"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                c {
+                    generatedBy tasks.generateCSources
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "can depend on header-only library composed of generated sources"() {
+        given:
+        // Write sources to src/main, headers to src/hello
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.sourceFiles*.writeToDir(file("src/main"))
+        app.library.headerFiles*.writeToDir(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.lib library: 'hello', linkage: 'api'
+            }
+        }
+        hello(NativeLibrarySpec) {
+            sources {
+                c {
+                    generatedBy tasks.generateCSources
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "generator task produces cpp sources"() {
+        given:
+        def app = new CppHelloWorldApp()
+        app.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'cpp'
+
+task generateCppSources(type: GenerateSources) {
+    inputDir project.file("src/input")
+    headerDir project.file("build/src/generated/headers")
+    sourceDir project.file("build/src/generated/cpp")
+}
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.generatedBy tasks.generateCppSources
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "generator task produces assembler sources"() {
+        given:
+        def app = new MixedLanguageHelloWorldApp(AbstractInstalledToolChainIntegrationSpec.toolChain)
+        def asmSources = app.sourceFiles.findAll({it.path == 'asm'})
+        def mainSources = app.headerFiles + app.sourceFiles.findAll({it.path != 'asm'})
+        mainSources.removeAll {it.path == 'asm'}
+        mainSources*.writeToDir(file("src/main"))
+        asmSources*.writeToDir(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << app.pluginScript
+        buildFile << app.extraConfiguration
+        buildFile << """
+task generateAsmSources(type: GenerateSources) {
+    inputDir project.file("src/input")
+    sourceDir project.file("build/src/generated/asm")
+}
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                asm.generatedBy tasks.generateAsmSources
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    @RequiresInstalledToolChain(VisualCpp)
+    def "generator task produces windows resources"() {
+        given:
+        def app = new WindowsResourceHelloWorldApp()
+        def rcSources = app.sourceFiles.findAll {it.path == 'rc'}
+        def mainSources = app.headerFiles + app.sourceFiles - rcSources
+        mainSources*.writeToDir(file("src/main"))
+        rcSources*.writeToDir(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << app.pluginScript
+        buildFile << app.extraConfiguration
+        buildFile << """
+task generateRcSources(type: GenerateSources) {
+    inputDir project.file("src/input")
+    sourceDir project.file("build/src/generated/rc")
+}
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                rc.generatedBy tasks.generateRcSources
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "produces reasonable error message when generator task does not have sourceDir property"() {
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+task generateSources {
+}
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.generatedBy tasks.generateSources
+            }
+        }
+    }
+}
+"""
+
+        and:
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasCause "Exception thrown while executing model rule: org.gradle.nativeplatform.plugins.NativeComponentModelPlugin\$Rules#configureGeneratedSourceSets("
+        failure.assertHasCause "Could not find property 'sourceDir' on task ':generateSources'."
+    }
+
+    def "can explicitly configure source and header directories from generator task"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c {
+                    builtBy tasks.generateCSources
+                    source {
+                        srcDirs tasks.generateCSources.sourceDir
+                    }
+                    exportedHeaders {
+                        srcDirs tasks.generateCSources.headerDir
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "can configure generator task properties after wiring"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        when:
+        buildFile << """
+apply plugin: 'c'
+
+task lateConfiguredGenerator(type: GenerateSources)
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.generatedBy tasks.lateConfiguredGenerator
+            }
+        }
+    }
+}
+
+lateConfiguredGenerator {
+    inputDir project.file("src/input")
+    headerDir project.file("build/src/generated/headers")
+    sourceDir project.file("build/src/generated/c")
+}
+"""
+
+        then:
+        executableBuilt(app)
+    }
+
+    def "creates visual studio project including generated sources"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/input"))
+        degenerateInputSources()
+
+        and:
+        buildFile << """
+apply plugin: 'visual-studio'
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                c.generatedBy tasks.generateCSources
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainVisualStudio"
+
+        then:
+        final mainSolution = new SolutionFile(file("mainExe.sln"))
+        mainSolution.assertHasProjects("mainExe")
+
+        and:
+        final projectFile = new ProjectFile(file("mainExe.vcxproj"))
+        projectFile.sourceFiles as Set == [
+                "build.gradle",
+                "build/src/generated/c/hello.c",
+                "build/src/generated/c/main.c",
+                "build/src/generated/c/sum.c"
+        ] as Set
+        projectFile.headerFiles == [ "build/src/generated/headers/hello.h" ]
+        projectFile.projectConfigurations.keySet() == ['debug'] as Set
+        with (projectFile.projectConfigurations['debug']) {
+            // TODO - should not include the default location
+            includePath == "src/main/headers;build/src/generated/headers"
+        }
+    }
+
+    def executableBuilt(def app) {
+        succeeds "mainExecutable"
+        assert executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
+        true
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetCompileDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetCompileDependenciesIntegrationTest.groovy
new file mode 100755
index 0000000..3474070
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetCompileDependenciesIntegrationTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.sourceset
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+
+class SourceSetCompileDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+
+        file("src/main/headers/funcs.h") << """
+            int func1();
+            int func2();
+"""
+        file("src/main/cpp/main.cpp") << """
+            #include <iostream>
+            #include "funcs.h"
+            int main () {
+                std::cout << func1() << func2() << std::endl;
+                return 0;
+            }
+"""
+        file("src/main/cpp/func1.cpp") << """
+            #include "lib.h"
+
+            int func1() {
+                return LIB_ID;
+            }
+"""
+        file("src/main/otherCpp/func2.cpp") << """
+            #include "lib.h"
+
+            int func2() {
+                return LIB_ID;
+            }
+"""
+        file("src/lib1/headers/lib.h") << """
+            #define LIB_ID 1
+"""
+        file("src/lib2/headers/lib.h") << """
+            #define LIB_ID 2
+"""
+
+        and:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        lib1(NativeLibrarySpec)
+        lib2(NativeLibrarySpec)
+    }
+}
+"""
+    }
+
+    def "dependencies of 2 language source sets are not shared when compiling"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp {
+                    lib library: 'lib1', linkage: 'api'
+                }
+                otherCpp(CppSourceSet) {
+                    lib library: 'lib2', linkage: 'api'
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == "12\n"
+    }
+
+    def "dependencies of language source set added to binary are not shared when compiling"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib library: 'lib1', linkage: 'api'
+            }
+            binaries.all {
+                sources {
+                    other(CppSourceSet) {
+                        source.srcDir "src/main/otherCpp"
+                        lib library: 'lib2', linkage: 'api'
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == "12\n"
+    }
+
+    def "dependencies of binary are shared with all source sets when compiling"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                otherCpp(CppSourceSet)
+            }
+            binaries.all {
+                lib library: 'lib1', linkage: 'api'
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == "11\n"
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetDependenciesIntegrationTest.groovy
new file mode 100755
index 0000000..94ed005
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetDependenciesIntegrationTest.groovy
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativeplatform.sourceset
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.fixtures.app.CppCallingCHelloWorldApp
+
+// TODO:DAZ Test incremental
+// TODO:DAZ Test dependency on functional source set
+// TODO:DAZ Test dependency on source set that is not HeaderExportingSourceSet
+// TODO:DAZ Sad day tests
+class SourceSetDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def "source dependency on source set of same type"() {
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/library"))
+
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                library(CSourceSet) {
+                    source.srcDir "src/library/c"
+                    exportedHeaders.srcDir "src/library/headers"
+                }
+                c.lib sources.library
+            }
+        }
+    }
+}
+"""
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
+    }
+
+    def "source dependency on source set of headers"() {
+        def app = new CHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.sourceFiles*.writeToDir(file("src/main"))
+        app.library.headerFiles*.writeToDir(file("src/library"))
+
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    components { comp ->
+        library(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                c.lib comp.library.sources.c
+            }
+        }
+    }
+}
+"""
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
+    }
+
+    def "source dependency on source set of different type"() {
+        def app = new CppCallingCHelloWorldApp()
+        app.executable.writeSources(file("src/main"))
+        app.library.writeSources(file("src/library"))
+
+        buildFile << """
+apply plugin: 'cpp'
+apply plugin: 'c'
+
+model {
+    components {
+        main(NativeExecutableSpec) {
+            sources {
+                library(CSourceSet) {
+                    exportedHeaders.srcDir "src/library/headers"
+                    source.srcDir "src/library/c"
+                }
+                cpp.lib sources.library
+            }
+        }
+    }
+}
+"""
+        when:
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == app.englishOutput
+    }
+
+    def "source files in depended-on source set are not included"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/main"))
+
+        file("src/extra/cpp/bad.cpp") << """
+    FILE WILL BE IGNORED: source set dependency set only considers headers
+"""
+
+        buildFile << """
+apply plugin: 'cpp'
+
+model {
+    components { comp ->
+        extra(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp.lib comp.extra.sources.cpp
+            }
+        }
+    }
+}
+"""
+        expect:
+        succeeds "mainExecutable"
+    }
+
+    def "binary depending on source set has no effect"() {
+        given:
+        def app = new CHelloWorldApp()
+        app.writeSources(file("src/main"))
+
+        file("src/extra/headers/hello.h") << """
+    FILE WILL BE IGNORED: source set dependency added to binary
+"""
+
+        buildFile << """
+apply plugin: 'cpp'
+
+model {
+    components { comp ->
+        extra(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib comp.extra.sources.cpp
+            }
+        }
+    }
+}
+"""
+        expect:
+        succeeds "mainExecutable"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy
new file mode 100755
index 0000000..5492505
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativeplatform.sourceset
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class SourceSetLinkDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def "setup"() {
+        settingsFile << "rootProject.name = 'test'"
+
+        file("src/main/headers/funcs.h") << """
+            int func1();
+            int func2();
+"""
+        file("src/main/cpp/main.cpp") << """
+            #include <iostream>
+            #include "funcs.h"
+            int main () {
+                std::cout << func1() << func2() << std::endl;
+                return 0;
+            }
+"""
+        file("src/main/cpp1/func1.cpp") << """
+            int getOne();
+
+            int func1() {
+                return getOne();
+            }
+"""
+        file("src/other/cpp/func2.cpp") << """
+            int getTwo();
+
+            int func2() {
+                return getTwo();
+            }
+"""
+        file("src/lib1/cpp/getters.cpp") << """
+            int getOne() {
+                return 1;
+            }
+            int getTwo() {
+                return 2;
+            }
+"""
+
+        and:
+        buildFile << """
+apply plugin: "cpp"
+model {
+    components {
+        lib1(NativeLibrarySpec)
+        main(NativeExecutableSpec) {
+            sources {
+                cpp(CppSourceSet)
+                cpp1(CppSourceSet)
+            }
+        }
+    }
+}
+"""
+    }
+
+    def "library dependency of binary is available when linking all source sets"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main {
+            sources {
+                other(CppSourceSet) {
+                    source.srcDir "src/other/cpp"
+                }
+            }
+            binaries.all {
+                lib library: 'lib1', linkage: 'static'
+            }
+        }
+    }
+}
+"""
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == "12\n"
+    }
+
+    def "library dependency of 1 language source set is available to another when linking"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main {
+            sources {
+                other(CppSourceSet) {
+                    source.srcDir "src/other/cpp"
+                    lib library: 'lib1', linkage: 'static'
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == "12\n"
+    }
+
+    def "dependencies of language source set added to binary are available when linking"() {
+        given:
+        buildFile << """
+model {
+    components {
+        main {
+            binaries.all {
+                sources {
+                    other(CppSourceSet) {
+                        source.srcDir "src/other/cpp"
+                        lib library: 'lib1', linkage: 'static'
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        when:
+        succeeds "installMainExecutable"
+
+        then:
+        installation("build/install/mainExecutable").exec().out == "12\n"
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/CommonToolchainCustomizationIntegTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/CommonToolchainCustomizationIntegTest.groovy
new file mode 100644
index 0000000..660eca8
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/CommonToolchainCustomizationIntegTest.groovy
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain;
+
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+
+public class CommonToolchainCustomizationIntegTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def helloWorldApp = new CppHelloWorldApp()
+
+    def "can add action to tool chain that modifies tool arguments prior to execution"() {
+        when:
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.writeSources(file("src/main"))
+        buildFile << """
+apply plugin: 'cpp'
+
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                cppCompiler.withArguments { args ->
+                    Collections.replaceAll(args, "CUSTOM", "-DFRENCH")
+                }
+                linker.withArguments { args ->
+                    args.remove "CUSTOM"
+                }
+            }
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                cppCompiler.args "CUSTOM"
+                linker.args "CUSTOM"
+            }
+        }
+    }
+}
+        """
+        and:
+        succeeds "mainExecutable"
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCrossCompilationIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCrossCompilationIntegrationTest.groovy
new file mode 100755
index 0000000..d50476b
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCrossCompilationIntegrationTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GccCompatible
+
+ at RequiresInstalledToolChain(GccCompatible)
+class GccToolChainCrossCompilationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(file("src/hello"))
+    }
+
+    def "uses naming scheme of target platform when cross-compiling"() {
+        // TODO - use linux as the target when running on windows
+        buildFile << """
+model {
+    platforms {
+        custom {
+            operatingSystem 'windows'
+        }
+    }
+    toolChains {
+        ${toolChain.id} {
+            target('custom') {
+                if (${!OperatingSystem.current().windows}) {
+                    cCompiler.withArguments { it << '-fPIC' }
+                }
+            }
+        }
+    }
+    components {
+        all {
+            targetPlatform "custom"
+        }
+    }
+}
+"""
+
+        when:
+        run 'mainExe'
+
+        then:
+        file(OperatingSystem.WINDOWS.getStaticLibraryName("build/binaries/helloStaticLibrary/hello")).file
+        file(OperatingSystem.WINDOWS.getExecutableName("build/binaries/mainExecutable/main")).file
+
+        when:
+        run 'helloSharedLib'
+
+        then:
+        file(OperatingSystem.WINDOWS.getSharedLibraryName("build/binaries/helloSharedLibrary/hello")).file
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy
new file mode 100755
index 0000000..31fc530
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GccCompatible
+
+ at RequiresInstalledToolChain(GccCompatible)
+class GccToolChainCustomisationIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(file("src/hello"))
+    }
+
+    def "can configure platform specific args"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            target("arm"){
+                cCompiler.withArguments { args ->
+                    args << "-m32"
+                    args << "-DFRENCH"
+                }
+                linker.withArguments { args ->
+                    args << "-m32"
+                }
+            }
+            target("sparc")
+        }
+    }
+    platforms {
+        arm {
+            architecture "arm"
+        }
+        i386 {
+            architecture "i386"
+        }
+        sparc {
+            architecture "sparc"
+        }
+    }
+    components {
+        all {
+            targetPlatform "arm"
+            targetPlatform "i386"
+            targetPlatform "sparc"
+        }
+    }
+}
+"""
+
+        and:
+        succeeds "armMainExecutable", "i386MainExecutable", "sparcMainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/arm/main").binaryInfo.arch.name == "x86"
+        executable("build/binaries/mainExecutable/arm/main").exec().out == helloWorldApp.frenchOutput
+
+        executable("build/binaries/mainExecutable/i386/main").binaryInfo.arch.name == "x86"
+        executable("build/binaries/mainExecutable/i386/main").exec().out == helloWorldApp.englishOutput
+
+        executable("build/binaries/mainExecutable/sparc/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "can configure tool executables"() {
+        def binDir = testDirectory.createDir("bin")
+        wrapperTool(binDir, "c-compiler", toolChain.CCompiler, "-DFRENCH")
+        wrapperTool(binDir, "static-lib", toolChain.staticLibArchiver)
+        wrapperTool(binDir, "linker", toolChain.linker)
+
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            path file('${binDir.toURI()}')
+            eachPlatform {
+                cCompiler.executable = 'c-compiler'
+                staticLibArchiver.executable = 'static-lib'
+                linker.executable = 'linker'
+            }
+        }
+    }
+}
+"""
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.frenchOutput
+    }
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    def "can configure platform specific executables"() {
+        def binDir = testDirectory.createDir("bin")
+        wrapperTool(binDir, "french-c-compiler", toolChain.CCompiler, "-DFRENCH")
+        wrapperTool(binDir, "static-lib", toolChain.staticLibArchiver)
+        wrapperTool(binDir, "linker", toolChain.linker)
+
+        when:
+        file("src/execTest/c/execTest.c") <<"""
+            #include <stdio.h>
+
+            int main () {
+                #if defined(__cplusplus)
+                printf("C++ compiler used");
+                #else
+                printf("C compiler used");
+                #endif
+                return 0;
+            }
+        """
+        and:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            target("alwaysFrench"){
+                cCompiler.executable = '${binDir.absolutePath}/french-c-compiler'
+                staticLibArchiver.executable = '${binDir.absolutePath}/static-lib'
+                linker.executable = '${binDir.absolutePath}/linker'
+            }
+
+            target("alwaysCPlusPlus") {
+                def compilerMap = [gcc: 'g++', clang: 'clang++']
+                cCompiler.executable = compilerMap[cCompiler.executable]
+                cCompiler.withArguments { args ->
+                    Collections.replaceAll(args, "c", "c++")
+                }
+            }
+        }
+    }
+
+    platforms {
+        alwaysFrench
+        alwaysCPlusPlus
+    }
+    components {
+        execTest(NativeExecutableSpec) {
+            targetPlatform "alwaysFrench"
+            targetPlatform "alwaysCPlusPlus"
+        }
+        main {
+            targetPlatform "alwaysFrench"
+            targetPlatform "alwaysCPlusPlus"
+        }
+        hello {
+            targetPlatform "alwaysFrench"
+            targetPlatform "alwaysCPlusPlus"
+        }
+    }
+}
+"""
+        succeeds "assemble"
+        then:
+        executable("build/binaries/mainExecutable/alwaysFrench/main").exec().out == helloWorldApp.frenchOutput
+        executable("build/binaries/mainExecutable/alwaysCPlusPlus/main").exec().out == helloWorldApp.englishOutput
+        executable("build/binaries/execTestExecutable/alwaysCPlusPlus/execTest").exec().out == "C++ compiler used"
+        executable("build/binaries/execTestExecutable/alwaysFrench/execTest").exec().out == "C compiler used"
+    }
+
+    def wrapperTool(TestFile binDir, String wrapperName, String executable, String... additionalArgs) {
+        def script = binDir.file(OperatingSystem.current().getExecutableName(wrapperName))
+        if (OperatingSystem.current().windows) {
+            script.text = "${executable} ${additionalArgs.join(' ')} %*"
+        } else {
+            script.text = "${executable} ${additionalArgs.join(' ')} \"\$@\""
+            script.permissions = "rwxr--r--"
+        }
+        return script
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainDiscoveryIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainDiscoveryIntegrationTest.groovy
new file mode 100755
index 0000000..d7c39c6
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainDiscoveryIntegrationTest.groovy
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+import org.hamcrest.Matchers
+import spock.lang.IgnoreIf
+
+import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GccCompatible
+
+ at RequiresInstalledToolChain(GccCompatible)
+class GccToolChainDiscoveryIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        main(NativeExecutableSpec) {
+            binaries.all {
+                lib library: 'hello', linkage: 'static'
+            }
+        }
+        hello(NativeLibrarySpec)
+    }
+}
+"""
+
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(file("src/hello"))
+    }
+
+    def "can build when language tools that are not required are not available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                cppCompiler.executable = 'does-not-exist'
+            }
+        }
+    }
+}
+"""
+        succeeds "mainExecutable"
+
+        then:
+        executable("build/binaries/mainExecutable/main").exec().out == helloWorldApp.englishOutput
+    }
+
+    def "does not break when compiler not available and not building"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                cCompiler.executable = 'does-not-exist'
+                cppCompiler.executable = 'does-not-exist'
+                linker.executable = 'does-not-exist'
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "help"
+    }
+
+    def "tool chain is not available when no tools are available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                assembler.executable = 'does-not-exist'
+                cCompiler.executable = 'does-not-exist'
+                cppCompiler.executable = 'does-not-exist'
+                linker.executable = 'does-not-exist'
+                staticLibArchiver.executable = 'does-not-exist'
+                objcCompiler.executable = 'does-not-exist'
+                objcppCompiler.executable = 'does-not-exist'
+            }
+        }
+    }
+}
+"""
+        fails "compileMainExecutableMainC"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.")
+        failure.assertThatCause(Matchers.startsWith("No tool chain is available to build for platform '${NativePlatforms.defaultPlatformName}'"))
+        failure.assertThatCause(Matchers.containsString("- ${toolChain.instanceDisplayName}: Could not find C compiler 'does-not-exist'"))
+    }
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def "fails when required language tool is not available but other language tools are available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                cCompiler.executable = 'does-not-exist'
+            }
+        }
+    }
+}
+"""
+        fails "compileMainExecutableMainC"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.")
+        failure.assertThatCause(Matchers.startsWith("Could not find C compiler 'does-not-exist'"))
+    }
+
+    def "fails when required linker tool is not available but language tool is available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            eachPlatform {
+                linker.executable = 'does-not-exist'
+            }
+        }
+    }
+}
+"""
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':linkMainExecutable'.")
+        failure.assertThatCause(Matchers.startsWith("Could not find Linker 'does-not-exist'"))
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy
new file mode 100755
index 0000000..fc52a9c
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy
@@ -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.nativeplatform.toolchain
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.CppCompilerDetectingTestApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at RequiresInstalledToolChain
+class MultipleNativeToolChainIntegrationTest extends AbstractIntegrationSpec {
+    def helloWorld = new CppCompilerDetectingTestApp()
+
+    def setup() {
+        buildFile << """
+plugins { id 'cpp' }
+"""
+
+        helloWorld.writeSources(file("src/main"))
+    }
+
+    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @RequiresInstalledToolChain(ToolChainRequirement.Gcc)
+    def "can build with multiple tool chains"() {
+        AvailableToolChains.InstalledToolChain x86ToolChain = OperatingSystem.current().isWindows() ?
+                AvailableToolChains.getToolChain(ToolChainRequirement.VisualCpp) :
+                AvailableToolChains.getToolChain(ToolChainRequirement.Clang)
+        AvailableToolChains.InstalledToolChain sparcToolChain = AvailableToolChains.getToolChain(ToolChainRequirement.Gcc)
+
+        when:
+        buildFile << """
+model {
+    platforms {
+        i386 {
+            architecture "i386"
+        }
+        sparc {
+            architecture "sparc"
+        }
+    }
+    toolChains {
+        ${x86ToolChain.buildScriptConfig}
+        ${sparcToolChain.buildScriptConfig}
+        ${sparcToolChain.id} {
+            target("sparc")
+        }
+    }
+    components {
+        main(NativeExecutableSpec) {
+            targetPlatform "i386"
+            targetPlatform "sparc"
+        }
+    }
+}
+"""
+
+        then:
+        succeeds 'i386MainExecutable', 'sparcMainExecutable'
+
+        and:
+        def i386Exe = x86ToolChain.executable(file("build/binaries/mainExecutable/i386/main"))
+        assert i386Exe.exec().out == helloWorld.expectedOutput(x86ToolChain)
+        def sparcExe = sparcToolChain.executable(file("build/binaries/mainExecutable/sparc/main"))
+        assert sparcExe.exec().out == helloWorld.expectedOutput(sparcToolChain)
+    }
+
+    def checkInstall(String path, AvailableToolChains.InstalledToolChain toolChain) {
+        def executable = file(OperatingSystem.current().getScriptName(path))
+        executable.assertExists()
+        assert executable.execute([], toolChain.runtimeEnv).out == helloWorld.expectedOutput(toolChain)
+        return true
+    }
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainDiscoveryIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainDiscoveryIntegrationTest.groovy
new file mode 100755
index 0000000..6f9dd61
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainDiscoveryIntegrationTest.groovy
@@ -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.nativeplatform.toolchain
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.app.CppCompilerDetectingTestApp
+
+/**
+ * Test that each available tool chain can be discovered and used without configuration, assuming it is in the path.
+ */
+class NativeToolChainDiscoveryIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def helloWorldApp = new CppCompilerDetectingTestApp()
+
+    def setup() {
+        // Discard init script content generated by superclass
+        initScript.text = ""
+    }
+
+    def "can discover tool chain in environment"() {
+        given:
+        toolChain.initialiseEnvironment();
+
+        and:
+        buildFile << """
+apply plugin: 'cpp'
+model {
+    toolChains {
+        tc(${toolChain.implementationClass})
+    }
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.expectedOutput(toolChain)
+
+        cleanup:
+        toolChain.resetEnvironment();
+    }
+
+    def "uses correct tool chain when explicitly configured"() {
+        given:
+        buildFile << """
+apply plugin: 'cpp'
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+        """
+
+        and:
+        helloWorldApp.writeSources(file("src/main"))
+
+        when:
+        run "mainExecutable"
+
+        then:
+        def mainExecutable = executable("build/binaries/mainExecutable/main")
+        mainExecutable.assertExists()
+        mainExecutable.exec().out == helloWorldApp.expectedOutput(toolChain)
+    }
+
+}
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/VisualCppToolChainDiscoveryIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/VisualCppToolChainDiscoveryIntegrationTest.groovy
new file mode 100755
index 0000000..49f46be
--- /dev/null
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/VisualCppToolChainDiscoveryIntegrationTest.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
+import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.nativeplatform.platform.internal.NativePlatforms
+import org.hamcrest.Matchers
+
+ at RequiresInstalledToolChain(ToolChainRequirement.VisualCpp)
+class VisualCppToolChainDiscoveryIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    def helloWorldApp = new CHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: 'c'
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        main(NativeExecutableSpec)
+    }
+}
+"""
+
+        helloWorldApp.executable.writeSources(file("src/main"))
+        helloWorldApp.library.writeSources(file("src/main"))
+    }
+
+    def "tool chain is not available when visual studio install is not available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            installDir "does-not-exist"
+        }
+    }
+}
+"""
+
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.")
+        failure.assertThatCause(Matchers.startsWith("No tool chain is available to build for platform '${NativePlatforms.defaultPlatformName}'"))
+        failure.assertThatCause(Matchers.containsString("- ${toolChain.instanceDisplayName}: The specified installation directory '${file('does-not-exist')}' does not appear to contain a Visual Studio installation."))
+    }
+
+    def "tool chain is not available when SDK install is not available"() {
+        when:
+        buildFile << """
+model {
+    toolChains {
+        ${toolChain.id} {
+            windowsSdkDir "does-not-exist"
+        }
+    }
+}
+"""
+        fails "mainExecutable"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':compileMainExecutableMainC'.")
+        failure.assertThatCause(Matchers.startsWith("No tool chain is available to build for platform '${NativePlatforms.defaultPlatformName}'"))
+        failure.assertThatCause(Matchers.containsString("- ${toolChain.instanceDisplayName}: The specified installation directory '${file('does-not-exist')}' does not appear to contain a Windows SDK installation."))
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/DependentSourceSet.java b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/DependentSourceSet.java
new file mode 100644
index 0000000..cd9d290
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/DependentSourceSet.java
@@ -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.language.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * A source set that depends on one or more {@link org.gradle.nativeplatform.NativeDependencySet}s to be built.
+ */
+ at Incubating
+public interface DependentSourceSet extends LanguageSourceSet {
+    /**
+     * 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 org.gradle.nativeplatform.NativeLibrarySpec}</li>
+     *     <li>A {@link org.gradle.nativeplatform.NativeDependencySet}</li>
+     *     <li>A {@link java.util.Map} containing the library selector.</li>
+     * </ul>
+     *
+     * The Map notation supports the following String attributes:
+     *
+     * <ul>
+     *     <li>project: the path to the project containing the library (optional, defaults to current project)</li>
+     *     <li>library: the name of the library (required)</li>
+     *     <li>linkage: the library linkage required ['shared'/'static'] (optional, defaults to 'shared')</li>
+     * </ul>
+     */
+    void lib(Object library);
+
+    /**
+     * Adds a pre-compiled header to be used when compiling sources in this source set.
+     *
+     * @param header the header to precompile
+     */
+    void preCompiledHeader(String header);
+
+    /**
+     * Returns any pre-compiled headers configured for this source set.
+     *
+     * @return the pre-compiled headers
+     */
+    Set<String> getPreCompiledHeaders();
+
+    File getPrefixHeaderFile();
+
+    void setPrefixHeaderFile(File prefixHeaderFile);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java
new file mode 100644
index 0000000..b640c65
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/HeaderExportingSourceSet.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.language.nativeplatform;
+
+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<? super SourceDirectorySet> config);
+
+    /**
+     * The headers as a directory set.
+     */
+    SourceDirectorySet getExportedHeaders();
+
+    /**
+     * The headers that are private to this source set and implicitly available. These are not explicitly made available for compilation.
+     */
+    SourceDirectorySet getImplicitHeaders();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/NativeResourceSet.java b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/NativeResourceSet.java
new file mode 100644
index 0000000..5d993b7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/NativeResourceSet.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.language.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * A source set that provides resources.
+ */
+ at Incubating
+public interface NativeResourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/internal/SourceIncludes.java b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/internal/SourceIncludes.java
new file mode 100644
index 0000000..7babeca
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/internal/SourceIncludes.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.language.nativeplatform.internal;
+
+import java.util.List;
+
+public interface SourceIncludes {
+    List<String> getQuotedIncludes();
+    List<String> getSystemIncludes();
+    List<String> getMacroIncludes();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/package-info.java
new file mode 100644
index 0000000..f956413
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/language/nativeplatform/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Model classes for managing language sources.
+ */
+package org.gradle.language.nativeplatform;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildType.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildType.java
new file mode 100644
index 0000000..a151a10
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildType.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * Specifies a build-type for a native binary. Common build types are 'debug' and 'release', but others may be defined.
+ */
+ at Incubating
+public interface BuildType extends Named {
+    /**
+     * Returns a human-consumable name for this build type.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildTypeContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildTypeContainer.java
new file mode 100644
index 0000000..9d1336e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/BuildTypeContainer.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectContainer;
+
+/**
+ * A container of {@link BuildType}s.
+ */
+ at Incubating
+public interface BuildTypeContainer extends NamedDomainObjectContainer<BuildType> {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Flavor.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Flavor.java
new file mode 100644
index 0000000..00fccbf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Flavor.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * Defines a custom variant that differentiate a {@link NativeBinary}.
+ */
+ at Incubating
+public interface Flavor extends Named {
+    /**
+     * Returns a human-consumable display name for this flavor.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/FlavorContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/FlavorContainer.java
new file mode 100644
index 0000000..8cbb658
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/FlavorContainer.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectContainer;
+
+/**
+ * A container of {@link Flavor}s.
+ * <p/>
+ * If no flavor is explicitly configured, will contain a single {@link Flavor} named 'default'.
+ * Any flavors explicitly configured will overwrite the default flavor.
+ */
+ at Incubating
+public interface FlavorContainer extends NamedDomainObjectContainer<Flavor> {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinary.java
new file mode 100644
index 0000000..ef8fba6
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinary.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.Binary;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * Represents a particular binary artifact.
+ */
+ at Incubating
+public interface NativeBinary extends Binary {
+    /**
+     * The flavor that this binary was built with.
+     */
+    Flavor getFlavor();
+
+    /**
+     * Returns the {@link org.gradle.nativeplatform.platform.NativePlatform} that this binary is targeted to run on.
+     */
+    NativePlatform getTargetPlatform();
+
+    /**
+     * Returns the {@link BuildType} used to construct this binary.
+     */
+    BuildType getBuildType();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinarySpec.java
new file mode 100644
index 0000000..18db659
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeBinarySpec.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.platform.base.BinarySpec;
+
+import java.util.Collection;
+
+/**
+ * Represents a binary artifact that is the result of building a native component.
+ */
+ at Incubating @HasInternalProtocol
+public interface NativeBinarySpec extends BinarySpec {
+    /**
+     * The component that this binary was built from.
+     */
+    NativeComponentSpec getComponent();
+
+    /**
+     * The flavor that this binary was built with.
+     */
+    Flavor getFlavor();
+
+    /**
+     * Returns the {@link org.gradle.nativeplatform.platform.NativePlatform} that this binary is targeted to run on.
+     */
+    NativePlatform getTargetPlatform();
+
+    /**
+     * Returns the {@link BuildType} used to construct this binary.
+     */
+    BuildType getBuildType();
+
+    /**
+     * 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 NativeLibrarySpec}</li>
+     *     <li>A {@link NativeDependencySet}</li>
+     *     <li>A {@link java.util.Map} containing the library selector.</li>
+     * </ul>
+     *
+     * The Map notation supports the following String attributes:
+     *
+     * <ul>
+     *     <li>project: the path to the project containing the library (optional, defaults to current project)</li>
+     *     <li>library: the name of the library (required)</li>
+     *     <li>linkage: the library linkage required ['shared'/'static'] (optional, defaults to 'shared')</li>
+     * </ul>
+     */
+    void lib(Object library);
+
+    /**
+     * Returns the {@link org.gradle.nativeplatform.toolchain.NativeToolChain} that will be used to build this binary.
+     */
+    NativeToolChain getToolChain();
+
+    /**
+     * The settings used for linking this binary.
+     */
+    Tool getLinker();
+
+    /**
+     * The static archiver settings used for creating this binary.
+     */
+    Tool getStaticLibArchiver();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentExtension.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentExtension.java
new file mode 100644
index 0000000..052f843
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentExtension.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectContainer;
+
+/**
+ * The configuration for native components generated by this build.
+ */
+ at Incubating
+public interface NativeComponentExtension {
+    /**
+     * The {@link NativeExecutableSpec} components produced by the build.
+     */
+    NamedDomainObjectContainer<NativeExecutableSpec> getExecutables();
+
+    /**
+     * Configure the {@link NativeExecutableSpec} components produced by the build.
+     */
+    void executables(Action<? super NamedDomainObjectContainer<? super NativeExecutableSpec>> action);
+
+    /**
+     * The {@link NativeLibrarySpec} components produced by the build.
+     */
+    NamedDomainObjectContainer<NativeLibrarySpec> getLibraries();
+
+    /**
+     * Configure the {@link NativeLibrarySpec} components produced by the build.
+     */
+    void libraries(Action<? super NamedDomainObjectContainer<? super NativeLibrarySpec>> action);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentSpec.java
new file mode 100644
index 0000000..3072466
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeComponentSpec.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.ComponentSpec;
+
+/**
+ * Definition of a software component that is to be built by Gradle to run a on JVM platform.
+ */
+ at Incubating
+public interface NativeComponentSpec extends ComponentSpec {
+    /**
+     * 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);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeDependencySet.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeDependencySet.java
new file mode 100644
index 0000000..0017c7a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform;
+
+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/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutable.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutable.java
new file mode 100644
index 0000000..8772e9f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutable.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.Application;
+
+/**
+ * An executable native component that is built by Gradle.
+ */
+ at Incubating
+public interface NativeExecutable extends Application {
+    
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinary.java
new file mode 100644
index 0000000..7597070
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinary.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * A binary artifact for a {@link NativeExecutable}, targeted at a particular platform with specific configuration.
+ */
+ at Incubating
+public interface NativeExecutableBinary extends NativeBinary {
+    /**
+     * The executable file.
+     */
+    File getExecutableFile();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinarySpec.java
new file mode 100644
index 0000000..82ecfb6
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableBinarySpec.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.platform.base.ApplicationBinarySpec;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+import java.io.File;
+
+/**
+ * An binary built by Gradle for a native application.
+ */
+ at Incubating
+public interface NativeExecutableBinarySpec extends NativeBinarySpec, ApplicationBinarySpec {
+    /**
+     * Provides access to key tasks used for building the binary.
+     */
+    public interface TasksCollection extends BinaryTasksCollection {
+        /**
+         * The link task.
+         */
+        Task getLink();
+
+        /**
+         * The install task.
+         */
+        Task getInstall();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeExecutableSpec getApplication();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeExecutableSpec getComponent();
+
+    /**
+     * The executable file.
+     */
+    File getExecutableFile();
+
+    /**
+     * The executable file.
+     */
+    void setExecutableFile(File executableFile);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    TasksCollection getTasks();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableSpec.java
new file mode 100644
index 0000000..d3011a9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeExecutableSpec.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.ApplicationSpec;
+
+/**
+ * Definition of a native executable component that is to be built by Gradle.
+ */
+ at Incubating
+public interface NativeExecutableSpec extends ApplicationSpec, NativeComponentSpec, TargetedNativeComponent {
+    
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrary.java
new file mode 100644
index 0000000..3781302
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrary.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.Library;
+
+/**
+ * A library component that is built by a gradle project.
+ */
+ at Incubating
+public interface NativeLibrary extends Library {
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinary.java
new file mode 100644
index 0000000..1e11d2c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinary.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+
+/**
+ * A physical representation of a {@link NativeLibrary} component.
+ */
+ at Incubating
+public interface NativeLibraryBinary extends NativeBinary {
+
+    FileCollection getHeaderDirs();
+
+    FileCollection getLinkFiles();
+
+    FileCollection getRuntimeFiles();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinarySpec.java
new file mode 100644
index 0000000..ef9f530
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryBinarySpec.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.platform.base.LibraryBinarySpec;
+
+/**
+ * Represents a binary artifact that is the result of building a native library component.
+ */
+public interface NativeLibraryBinarySpec extends NativeBinarySpec, LibraryBinarySpec {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeLibrarySpec getLibrary();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeLibrarySpec getComponent();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryRequirement.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryRequirement.java
new file mode 100644
index 0000000..ea31c28
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibraryRequirement.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A dependency on a native library within the build.
+ */
+ at Incubating
+public interface NativeLibraryRequirement {
+    /**
+     * The path to the project containing the library.
+     */
+    String getProjectPath();
+
+    /**
+     * The name of the required library.
+     */
+    String getLibraryName();
+
+    /**
+     * The required linkage.
+     */
+    String getLinkage();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrarySpec.java
new file mode 100644
index 0000000..de0c034
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/NativeLibrarySpec.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.LibrarySpec;
+
+/**
+ * Definition of a native library component that is to be built by Gradle.
+ */
+ at Incubating
+public interface NativeLibrarySpec extends LibrarySpec, NativeComponentSpec, TargetedNativeComponent {
+    /**
+     * Converts this library to a native library requirement that uses the shared library variant. This is the default.
+     */
+    NativeLibraryRequirement getShared();
+
+    /**
+     * Converts this library to a native library requirement that uses the static library variant.
+     */
+    NativeLibraryRequirement getStatic();
+
+    /**
+     * Converts this library to a native library requirement that uses the api library linkage.
+     */
+    NativeLibraryRequirement getApi();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/ObjectFile.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/ObjectFile.java
new file mode 100644
index 0000000..d82ff72
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/ObjectFile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.platform.base.TransformationFileType;
+
+/**
+ * LanguageOutputType marking object file output type.
+ * */
+ at Incubating
+public class ObjectFile implements TransformationFileType {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibraries.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibraries.java
new file mode 100644
index 0000000..d5a962b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibraries.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectSet;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+
+/**
+ * A container of {@link PrebuiltLibrary} instances.
+ */
+ at Incubating
+public interface PrebuiltLibraries extends ArtifactRepository, NamedDomainObjectSet<PrebuiltLibrary> {
+    PrebuiltLibrary resolveLibrary(String name);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibrary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibrary.java
new file mode 100644
index 0000000..8e107db
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltLibrary.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.nativeplatform;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.api.file.SourceDirectorySet;
+
+/**
+ * A library component that is not built by gradle.
+ */
+ at Incubating
+public interface PrebuiltLibrary extends Named, NativeLibrary {
+    /**
+     * The binaries that are built for this component. You can use this to configure the binaries for this component.
+     */
+    DomainObjectSet<NativeLibraryBinary> getBinaries();
+
+    /**
+     * The headers exported by this library. These headers will be added to all binaries for this library.
+     */
+    SourceDirectorySet getHeaders();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltSharedLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltSharedLibraryBinary.java
new file mode 100644
index 0000000..35b334c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltSharedLibraryBinary.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * A shared library that exists at a known location on the filesystem.
+ */
+ at Incubating
+public interface PrebuiltSharedLibraryBinary extends SharedLibraryBinary {
+    /**
+     * The shared library file.
+     */
+    void setSharedLibraryFile(File sharedLibraryFile);
+
+    /**
+     * The shared library link file.
+     */
+    void setSharedLibraryLinkFile(File sharedLibraryLinkFile);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltStaticLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltStaticLibraryBinary.java
new file mode 100644
index 0000000..16add38
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/PrebuiltStaticLibraryBinary.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * A static library that exists at a known location on the filesystem.
+ */
+ at Incubating
+public interface PrebuiltStaticLibraryBinary extends StaticLibraryBinary {
+    /**
+     * The static library binary file.
+     */
+    void setStaticLibraryFile(File staticLibraryFile);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Repositories.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Repositories.java
new file mode 100644
index 0000000..48bc200
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Repositories.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+
+/**
+ * The repositories that Gradle will search for prebuilt libraries.
+ */
+ at Incubating
+public interface Repositories extends PolymorphicDomainObjectContainer<ArtifactRepository> {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinary.java
new file mode 100644
index 0000000..788f57d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinary.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * A {@link NativeLibrary} that has been compiled and linked as a shared library.
+ */
+ at Incubating
+public interface SharedLibraryBinary extends NativeLibraryBinary {
+
+    /**
+     * The shared library file.
+     */
+    File getSharedLibraryFile();
+
+    /**
+     * The shared library link file.
+     */
+    File getSharedLibraryLinkFile();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinarySpec.java
new file mode 100644
index 0000000..0988c8a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/SharedLibraryBinarySpec.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+import java.io.File;
+
+/**
+ * A shared library binary built by Gradle for a native library.
+ */
+ at Incubating
+public interface SharedLibraryBinarySpec extends NativeLibraryBinarySpec {
+    /**
+     * Provides access to key tasks used for building the binary.
+     */
+    public interface TasksCollection extends BinaryTasksCollection {
+        /**
+         * Returns the link task for this binary.
+         */
+        Task getLink();
+    }
+
+    /**
+     * The shared library file.
+     */
+    File getSharedLibraryFile();
+
+    /**
+     * The shared library link file.
+     */
+    File getSharedLibraryLinkFile();
+
+    /**
+     * The shared library file.
+     */
+    void setSharedLibraryFile(File sharedLibraryFile);
+
+    /**
+     * The shared library link file.
+     */
+    void setSharedLibraryLinkFile(File sharedLibraryLinkFile);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    TasksCollection getTasks();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinary.java
new file mode 100644
index 0000000..3b926cf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinary.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+import java.io.File;
+
+/**
+ * A {@link NativeLibrary} that has been compiled and archived into a static library.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface StaticLibraryBinary extends NativeLibraryBinary {
+    /**
+     * The static library file.
+     */
+    File getStaticLibraryFile();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinarySpec.java
new file mode 100644
index 0000000..e3d4a68
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/StaticLibraryBinarySpec.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.platform.base.BinaryTasksCollection;
+
+import java.io.File;
+
+/**
+ * A static library binary built by Gradle for a native library.
+ */
+ at Incubating
+public interface StaticLibraryBinarySpec extends NativeLibraryBinarySpec {
+    /**
+     * Provides access to key tasks used for building the binary.
+     */
+    public interface TasksCollection extends BinaryTasksCollection {
+        /**
+         * The create static library task.
+         */
+        Task getCreateStaticLib();
+    }
+
+    /**
+     * The static library file.
+     */
+    File getStaticLibraryFile();
+
+    /**
+     * The static library binary file.
+     */
+    void setStaticLibraryFile(File staticLibraryFile);
+
+    /**
+     * Add some additional files required by consumers of this library at link time.
+     */
+    void additionalLinkFiles(FileCollection files);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    TasksCollection getTasks();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/TargetedNativeComponent.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/TargetedNativeComponent.java
new file mode 100644
index 0000000..1a76ed2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/TargetedNativeComponent.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.nativeplatform;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.PlatformAwareComponentSpec;
+
+/**
+ * A native component that can be configured to target certain variant dimensions.
+ * This functionality is a temporary workaround to eliminate configuration of unnecessary domain objects and tasks.
+ */
+ at Incubating @HasInternalProtocol
+public interface TargetedNativeComponent extends PlatformAwareComponentSpec {
+
+    /**
+     * Specifies the names of one or more {@link Flavor}s that this component should be built for.
+     */
+    void targetFlavors(String... flavorSelectors);
+
+    /**
+     * Specifies the names of one or more {@link BuildType}s that this component should be built for.
+     */
+    void targetBuildTypes(String... platformSelectors);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Tool.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/Tool.java
new file mode 100644
index 0000000..30fef1d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform;
+
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * Configuration of the arguments of a ToolChain executable.
+ */
+ at Incubating
+public interface Tool {
+    /**
+     * The arguments passed when executing this tool.
+     */
+    List<String> getArgs();
+
+    /**
+     * Adds a number of arguments to be passed to the tool.
+     */
+    void args(String... args);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractBinaryToolSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractBinaryToolSpec.java
new file mode 100644
index 0000000..41c7224
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractBinaryToolSpec.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.nativeplatform.internal;
+
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AbstractBinaryToolSpec implements BinaryToolSpec {
+    private List<String> args = new ArrayList<String>();
+    private List<String> systemArgs = new ArrayList<String>();
+    private File tempDir;
+    private NativePlatform platform;
+    private BuildOperationLogger oplogger;
+
+    public NativePlatform getTargetPlatform() {
+        return platform;
+    }
+
+    public void setTargetPlatform(NativePlatform platform) {
+        this.platform = platform;
+    }
+
+    public File getTempDir() {
+        return tempDir;
+    }
+
+    public void setTempDir(File tempDir) {
+        this.tempDir = tempDir;
+    }
+
+    public List<String> getArgs() {
+        return args;
+    }
+
+    public void args(List<String> args) {
+        this.args.addAll(args);
+    }
+
+    public List<String> getSystemArgs() {
+        return systemArgs;
+    }
+
+    public void systemArgs(List<String> args) {
+       if(!systemArgs.containsAll(args)){
+           systemArgs.addAll(args);
+       }
+    }
+
+    public List<String> getAllArgs() {
+        List<String> allArgs = new ArrayList<String>(systemArgs.size() + args.size());
+        allArgs.addAll(systemArgs);
+        allArgs.addAll(args);
+        return allArgs;
+    }
+
+    public BuildOperationLogger getOperationLogger() {
+        return oplogger;
+    }
+
+    public void setOperationLogger(BuildOperationLogger oplogger) {
+        this.oplogger = oplogger;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinaryRenderer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinaryRenderer.java
new file mode 100644
index 0000000..dc53c9b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinaryRenderer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.reporting.components.internal.AbstractBinaryRenderer;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+public abstract class AbstractNativeBinaryRenderer<T extends NativeBinarySpec> extends AbstractBinaryRenderer<T> {
+    @Override
+    protected void renderDetails(T binary, TextReportBuilder builder) {
+        builder.item("platform", binary.getTargetPlatform().getName());
+        builder.item("build type", binary.getBuildType().getName());
+        builder.item("flavor", binary.getFlavor().getName());
+        builder.item("tool chain", binary.getToolChain().getDisplayName());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinarySpec.java
new file mode 100644
index 0000000..6cda4e5
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeBinarySpec.java
@@ -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.nativeplatform.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.internal.resolve.NativeBinaryResolveResult;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryBuildAbility;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.platform.base.internal.ToolSearchBuildAbility;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public abstract class AbstractNativeBinarySpec extends BaseBinarySpec implements NativeBinarySpecInternal {
+    private final Set<? super Object> libs = new LinkedHashSet<Object>();
+    private final DefaultTool linker = new DefaultTool();
+    private final DefaultTool staticLibArchiver = new DefaultTool();
+    private NativeComponentSpec component;
+    private PlatformToolProvider toolProvider;
+    private BinaryNamingScheme namingScheme;
+    private Flavor flavor;
+    private NativeToolChain toolChain;
+    private NativePlatform targetPlatform;
+    private BuildType buildType;
+    private NativeDependencyResolver resolver;
+
+    public String getDisplayName() {
+        return namingScheme.getDescription();
+    }
+
+    public NativeComponentSpec getComponent() {
+        return component;
+    }
+
+    public void setComponent(NativeComponentSpec component) {
+        this.component = component;
+        setBinarySources(((ComponentSpecInternal) component).getSources().copy(getName()));
+    }
+
+    public Flavor getFlavor() {
+        return flavor;
+    }
+
+    public void setFlavor(Flavor flavor) {
+        this.flavor = flavor;
+    }
+
+    public NativeToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public void setToolChain(NativeToolChain toolChain) {
+        this.toolChain = toolChain;
+    }
+
+    public NativePlatform getTargetPlatform() {
+        return targetPlatform;
+    }
+
+    public void setTargetPlatform(NativePlatform targetPlatform) {
+        this.targetPlatform = targetPlatform;
+    }
+
+    public BuildType getBuildType() {
+        return buildType;
+    }
+
+    public void setBuildType(BuildType buildType) {
+        this.buildType = buildType;
+    }
+
+    public Tool getLinker() {
+        return linker;
+    }
+
+    public Tool getStaticLibArchiver() {
+        return staticLibArchiver;
+    }
+
+    public BinaryNamingScheme getNamingScheme() {
+        return namingScheme;
+    }
+
+    public void setNamingScheme(BinaryNamingScheme namingScheme) {
+        this.namingScheme = namingScheme;
+    }
+
+    public Collection<NativeDependencySet> getLibs() {
+        return resolve(getSource().withType(DependentSourceSet.class)).getAllResults();
+    }
+
+    public Collection<NativeDependencySet> getLibs(DependentSourceSet sourceSet) {
+        return resolve(Collections.singleton(sourceSet)).getAllResults();
+    }
+
+    public void lib(Object notation) {
+        libs.add(notation);
+    }
+
+    public Collection<NativeLibraryBinary> getDependentBinaries() {
+        return resolve(getSource().withType(DependentSourceSet.class)).getAllLibraryBinaries();
+    }
+
+    private NativeBinaryResolveResult resolve(Collection<? extends DependentSourceSet> sourceSets) {
+        Set<? super Object> allLibs = new LinkedHashSet<Object>(libs);
+        for (DependentSourceSet dependentSourceSet : sourceSets) {
+            allLibs.addAll(dependentSourceSet.getLibs());
+        }
+        NativeBinaryResolveResult resolution = new NativeBinaryResolveResult(this, allLibs);
+        resolver.resolve(resolution);
+        return resolution;
+    }
+
+    public PlatformToolProvider getPlatformToolProvider() {
+        return toolProvider;
+    }
+
+    public void setPlatformToolProvider(PlatformToolProvider toolProvider) {
+        this.toolProvider = toolProvider;
+    }
+
+    public void setResolver(NativeDependencyResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    @Override
+    protected BinaryBuildAbility getBinaryBuildAbility() {
+        NativeToolChainInternal toolChainInternal = (NativeToolChainInternal) getToolChain();
+        NativePlatformInternal platformInternal = (NativePlatformInternal) getTargetPlatform();
+        return new ToolSearchBuildAbility(toolChainInternal.select(platformInternal));
+    }
+
+    public void binaryInputs(FileCollection files) {
+        // TODO - should split this up, so that the inputs are attached to an object that represents the binary, which is then later used to configure the link/assemble tasks
+        getCreateOrLink().source(files);
+    }
+
+    protected abstract ObjectFilesToBinary getCreateOrLink();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeComponentSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeComponentSpec.java
new file mode 100644
index 0000000..1718a97
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeComponentSpec.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.nativeplatform.internal;
+
+import com.google.common.collect.Sets;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.ObjectFile;
+import org.gradle.platform.base.TransformationFileType;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.util.GUtil;
+
+import java.util.Set;
+
+public abstract class AbstractNativeComponentSpec extends BaseComponentSpec implements NativeComponentSpec, ComponentSpecInternal {
+    private String baseName;
+
+    public String getBaseName() {
+        return GUtil.elvis(baseName, getName());
+    }
+
+    public void setBaseName(String baseName) {
+        this.baseName = baseName;
+    }
+
+    public Set<Class<? extends TransformationFileType>> getInputTypes() {
+        Set<Class<? extends TransformationFileType>> inputTypes = Sets.newHashSet();
+        inputTypes.add(ObjectFile.class);
+        return inputTypes;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeLibraryBinarySpec.java
new file mode 100644
index 0000000..de353d2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractNativeLibraryBinarySpec.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.nativeplatform.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.AbstractFileCollection;
+import org.gradle.api.internal.tasks.DefaultTaskDependency;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.platform.base.LibraryBinarySpec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public abstract class AbstractNativeLibraryBinarySpec extends AbstractNativeBinarySpec implements LibraryBinarySpec {
+    @Override
+    public NativeLibrarySpec getComponent() {
+        return (NativeLibrarySpec) super.getComponent();
+    }
+
+    @Override
+    public NativeLibrarySpec getLibrary() {
+        return getComponent();
+    }
+
+    protected boolean hasSources() {
+        for (LanguageSourceSet sourceSet : getSource()) {
+            if (!sourceSet.getSource().isEmpty()) {
+                return true;
+            }
+            if (sourceSet.hasBuildDependencies()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public FileCollection getHeaderDirs() {
+        return new AbstractFileCollection() {
+            public String getDisplayName() {
+                return String.format("Headers for %s", getName());
+            }
+
+            public Set<File> getFiles() {
+                Set<File> headerDirs = new LinkedHashSet<File>();
+                for (HeaderExportingSourceSet sourceSet : getSource().withType(HeaderExportingSourceSet.class)) {
+                    headerDirs.addAll(sourceSet.getExportedHeaders().getSrcDirs());
+                }
+                return headerDirs;
+            }
+
+            @Override
+            public TaskDependency getBuildDependencies() {
+                DefaultTaskDependency dependency = new DefaultTaskDependency();
+                for (HeaderExportingSourceSet sourceSet : getSource().withType(HeaderExportingSourceSet.class)) {
+                    dependency.add(sourceSet.getBuildDependencies());
+                }
+                return dependency;
+            }
+        };
+    }
+
+    protected abstract class LibraryOutputs extends AbstractFileCollection {
+        public final Set<File> getFiles() {
+            if (hasOutputs()) {
+                return getOutputs();
+            }
+            return Collections.emptySet();
+        }
+
+        public final String getDisplayName() {
+            return AbstractNativeLibraryBinarySpec.this.toString();
+        }
+
+        public final TaskDependency getBuildDependencies() {
+            if (hasOutputs()) {
+                return AbstractNativeLibraryBinarySpec.this.getBuildDependencies();
+            }
+            return new DefaultTaskDependency();
+        }
+
+        protected abstract boolean hasOutputs();
+
+        protected abstract Set<File> getOutputs();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractTargetedNativeComponentSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractTargetedNativeComponentSpec.java
new file mode 100644
index 0000000..d7e2b2e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/AbstractTargetedNativeComponentSpec.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.nativeplatform.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Named;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+
+import java.util.*;
+
+public abstract class AbstractTargetedNativeComponentSpec extends AbstractNativeComponentSpec implements TargetedNativeComponentInternal {
+
+    private final List<PlatformRequirement> targetPlatforms = Lists.newArrayList();
+    private final Set<String> buildTypes = new HashSet<String>();
+    private final Set<String> flavors = new HashSet<String>();
+
+    public List<PlatformRequirement> getTargetPlatforms() {
+        return Collections.unmodifiableList(targetPlatforms);
+    }
+
+    public void targetPlatform(String targetPlatform) {
+        this.targetPlatforms.add(DefaultPlatformRequirement.create(targetPlatform));
+    }
+
+    public void targetFlavors(String... flavorSelectors) {
+        Collections.addAll(flavors, flavorSelectors);
+    }
+
+    public void targetBuildTypes(String... buildTypeSelectors) {
+        Collections.addAll(buildTypes, buildTypeSelectors);
+    }
+
+    public Set<Flavor> chooseFlavors(Set<? extends Flavor> candidates) {
+        return chooseElements(Flavor.class, candidates, flavors);
+    }
+
+    public Set<BuildType> chooseBuildTypes(Set<? extends BuildType> candidates) {
+        return chooseElements(BuildType.class, candidates, buildTypes);
+    }
+
+    protected <T extends Named> Set<T> chooseElements(Class<T> type, Set<? extends T> candidates, Set<String> names) {
+        if (names.isEmpty()) {
+            return new LinkedHashSet<T>(candidates);
+        }
+
+        Set<String> unusedNames = new HashSet<String>(names);
+        Set<T> chosen = new LinkedHashSet<T>();
+        for (T candidate : candidates) {
+            if (unusedNames.remove(candidate.getName())) {
+                chosen.add(candidate);
+            }
+        }
+
+        if (!unusedNames.isEmpty()) {
+            throw new InvalidUserDataException(String.format("Invalid %s: '%s'", type.getSimpleName(), unusedNames.iterator().next()));
+        }
+
+        return chosen;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BinaryToolSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BinaryToolSpec.java
new file mode 100644
index 0000000..95c38c8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BinaryToolSpec.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.nativeplatform.internal;
+
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.io.File;
+import java.util.List;
+
+public interface BinaryToolSpec extends CompileSpec {
+    NativePlatform getTargetPlatform();
+
+    void setTargetPlatform(NativePlatform platform);
+
+    File getTempDir();
+
+    void setTempDir(File tempDir);
+
+    List<String> getArgs();
+
+    void args(List<String> args);
+
+    List<String> getSystemArgs();
+
+    void systemArgs(List<String> args);
+
+    List<String> getAllArgs();
+
+    BuildOperationLogger getOperationLogger();
+
+    void setOperationLogger(BuildOperationLogger oplogger);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BuildOperationLoggingCompilerDecorator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BuildOperationLoggingCompilerDecorator.java
new file mode 100644
index 0000000..9084610
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/BuildOperationLoggingCompilerDecorator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+
+public class BuildOperationLoggingCompilerDecorator<T extends BinaryToolSpec> implements Compiler<T> {
+
+    private final Compiler<? super T> delegate;
+
+    private BuildOperationLoggingCompilerDecorator(Compiler<? super T> delegate) {
+        this.delegate = delegate;
+    }
+
+    public static <T extends BinaryToolSpec> Compiler<T> wrap(Compiler<T> delegate) {
+        return new BuildOperationLoggingCompilerDecorator<T>(delegate);
+    }
+
+    @Override
+    public WorkResult execute(T spec) {
+        spec.getOperationLogger().start();
+        try {
+            return delegate.execute(spec);
+        } finally {
+            spec.getOperationLogger().done();
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/CompilerOutputFileNamingScheme.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/CompilerOutputFileNamingScheme.java
new file mode 100644
index 0000000..327f4b3
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/CompilerOutputFileNamingScheme.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.apache.commons.io.FilenameUtils;
+import org.gradle.internal.hash.HashUtil;
+
+import java.io.File;
+
+public class CompilerOutputFileNamingScheme {
+    private String objectFileNameSuffix;
+    private File outputBaseFolder;
+
+    public CompilerOutputFileNamingScheme withOutputBaseFolder(File outputBaseFolder) {
+        this.outputBaseFolder = outputBaseFolder;
+        return this;
+    }
+
+    public CompilerOutputFileNamingScheme withObjectFileNameSuffix(String suffix){
+        this.objectFileNameSuffix = suffix;
+        return this;
+    }
+
+    public File map(File sourceFile) {
+        final String baseName = FilenameUtils.removeExtension(sourceFile.getName());
+        String compactMD5 = HashUtil.createCompactMD5(sourceFile.getAbsolutePath());
+        File hashDirectory = new File(outputBaseFolder, compactMD5);
+        return new File(hashDirectory, String.format("%s%s", baseName, objectFileNameSuffix));
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildType.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildType.java
new file mode 100644
index 0000000..a020aa7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildType.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.BuildType;
+
+public class DefaultBuildType implements BuildType {
+    private final String name;
+
+    public DefaultBuildType(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getDisplayName() {
+        return String.format("build type '%s'", name);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeContainer.java
new file mode 100644
index 0000000..c0f1343
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeContainer.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.nativeplatform.internal;
+
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.BuildTypeContainer;
+
+public class DefaultBuildTypeContainer extends AbstractNamedDomainObjectContainer<BuildType> implements BuildTypeContainer {
+
+    public DefaultBuildTypeContainer(Instantiator instantiator) {
+        super(BuildType.class, instantiator);
+    }
+
+    @Override
+    protected BuildType doCreate(String name) {
+        return getInstantiator().newInstance(DefaultBuildType.class, name);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavor.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavor.java
new file mode 100644
index 0000000..f5e900c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavor.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.Flavor;
+
+public class DefaultFlavor implements Flavor {
+    public static final String DEFAULT = "default";
+    private final String name;
+
+    public DefaultFlavor(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getDisplayName() {
+        return String.format("flavor '%s'", name);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavorContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavorContainer.java
new file mode 100644
index 0000000..83d6bc8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultFlavorContainer.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.nativeplatform.internal;
+
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.FlavorContainer;
+
+public class DefaultFlavorContainer extends AbstractNamedDomainObjectContainer<Flavor> implements FlavorContainer {
+
+    public DefaultFlavorContainer(Instantiator instantiator) {
+        super(Flavor.class, instantiator);
+    }
+
+    @Override
+    protected Flavor doCreate(String name) {
+        return getInstantiator().newInstance(DefaultFlavor.class, name);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultLinkerSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultLinkerSpec.java
new file mode 100644
index 0000000..7e85315
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultLinkerSpec.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.nativeplatform.internal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultLinkerSpec extends AbstractBinaryToolSpec implements LinkerSpec {
+
+    private final List<File> objectFiles = new ArrayList<File>();
+    private final List<File> libraries = new ArrayList<File>();
+    private final List<File> libraryPath = new ArrayList<File>();
+    private File outputFile;
+
+    public List<File> getObjectFiles() {
+        return objectFiles;
+    }
+
+    public void objectFiles(Iterable<File> objectFiles) {
+        addAll(this.objectFiles, objectFiles);
+    }
+
+    public List<File> getLibraries() {
+        return libraries;
+    }
+
+    public void libraries(Iterable<File> libraries) {
+        addAll(this.libraries, libraries);
+    }
+
+    public List<File> getLibraryPath() {
+        return libraryPath;
+    }
+
+    public void libraryPath(File... libraryPath) {
+        Collections.addAll(this.libraryPath, libraryPath);
+    }
+
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    private void addAll(List<File> list, Iterable<File> iterable) {
+        for (File file : iterable) {
+            list.add(file);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpec.java
new file mode 100644
index 0000000..4ea74f7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpec.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.nativeplatform.NativeExecutableBinary;
+import org.gradle.nativeplatform.NativeExecutableBinarySpec;
+import org.gradle.nativeplatform.NativeExecutableSpec;
+import org.gradle.nativeplatform.tasks.InstallExecutable;
+import org.gradle.nativeplatform.tasks.LinkExecutable;
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.BinaryTasksCollectionWrapper;
+
+import java.io.File;
+
+public class DefaultNativeExecutableBinarySpec extends AbstractNativeBinarySpec implements NativeExecutableBinary, NativeExecutableBinarySpecInternal {
+    private final DefaultTasksCollection tasks = new DefaultTasksCollection(super.getTasks());
+    private File executableFile;
+
+    @Override
+    public NativeExecutableSpec getComponent() {
+        return (NativeExecutableSpec) super.getComponent();
+    }
+
+    @Override
+    public NativeExecutableSpec getApplication() {
+        return getComponent();
+    }
+
+    public File getExecutableFile() {
+        return executableFile;
+    }
+
+    public void setExecutableFile(File executableFile) {
+        this.executableFile = executableFile;
+    }
+
+    public File getPrimaryOutput() {
+        return getExecutableFile();
+    }
+
+    @Override
+    protected ObjectFilesToBinary getCreateOrLink() {
+        return tasks.getLink();
+    }
+
+    public NativeExecutableBinarySpec.TasksCollection getTasks() {
+        return tasks;
+    }
+
+    private static class DefaultTasksCollection extends BinaryTasksCollectionWrapper implements NativeExecutableBinarySpec.TasksCollection {
+
+        public DefaultTasksCollection(BinaryTasksCollection delegate) {
+            super(delegate);
+        }
+
+        public LinkExecutable getLink() {
+            return findSingleTaskWithType(LinkExecutable.class);
+        }
+
+        public InstallExecutable getInstall() {
+            return findSingleTaskWithType(InstallExecutable.class);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java
new file mode 100755
index 0000000..6100222
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.nativeplatform.NativeExecutableSpec;
+
+public class DefaultNativeExecutableSpec extends AbstractTargetedNativeComponentSpec implements NativeExecutableSpec {
+
+    public String getDisplayName() {
+        return String.format("native executable '%s'", getName());
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpec.java
new file mode 100755
index 0000000..365f314
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpec.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+
+public class DefaultNativeLibrarySpec extends AbstractTargetedNativeComponentSpec implements NativeLibrarySpec {
+    public String getDisplayName() {
+        return String.format("native library '%s'", getName());
+    }
+
+    public NativeLibraryRequirement getShared() {
+        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "shared");
+    }
+
+    public NativeLibraryRequirement getStatic() {
+        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "static");
+    }
+
+    public NativeLibraryRequirement getApi() {
+        return new ProjectNativeLibraryRequirement(getProjectPath(), this.getName(), "api");
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpec.java
new file mode 100644
index 0000000..6339f26
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpec.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.nativeplatform.NativeResourceSet;
+import org.gradle.nativeplatform.SharedLibraryBinary;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.tasks.LinkSharedLibrary;
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.BinaryTasksCollectionWrapper;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+public class DefaultSharedLibraryBinarySpec extends AbstractNativeLibraryBinarySpec implements SharedLibraryBinary, SharedLibraryBinarySpecInternal {
+    private final DefaultTasksCollection tasks = new DefaultTasksCollection(super.getTasks());
+    private File sharedLibraryFile;
+    private File sharedLibraryLinkFile;
+
+    public File getSharedLibraryFile() {
+        return sharedLibraryFile;
+    }
+
+    public void setSharedLibraryFile(File sharedLibraryFile) {
+        this.sharedLibraryFile = sharedLibraryFile;
+    }
+
+    public File getSharedLibraryLinkFile() {
+        return sharedLibraryLinkFile;
+    }
+
+    public void setSharedLibraryLinkFile(File sharedLibraryLinkFile) {
+        this.sharedLibraryLinkFile = sharedLibraryLinkFile;
+    }
+
+    public File getPrimaryOutput() {
+        return getSharedLibraryFile();
+    }
+
+    public FileCollection getLinkFiles() {
+        return new SharedLibraryLinkOutputs();
+    }
+
+    public FileCollection getRuntimeFiles() {
+        return new SharedLibraryRuntimeOutputs();
+    }
+
+    @Override
+    protected ObjectFilesToBinary getCreateOrLink() {
+        return tasks.getLink();
+    }
+
+    public SharedLibraryBinarySpec.TasksCollection getTasks() {
+        return tasks;
+    }
+
+    private static class DefaultTasksCollection extends BinaryTasksCollectionWrapper implements SharedLibraryBinarySpec.TasksCollection {
+        public DefaultTasksCollection(BinaryTasksCollection delegate) {
+            super(delegate);
+        }
+
+        public LinkSharedLibrary getLink() {
+            return findSingleTaskWithType(LinkSharedLibrary.class);
+        }
+    }
+
+    private class SharedLibraryLinkOutputs extends LibraryOutputs {
+        @Override
+        protected boolean hasOutputs() {
+            return hasSources() && !isResourceOnly();
+        }
+
+        @Override
+        protected Set<File> getOutputs() {
+            return Collections.singleton(getSharedLibraryLinkFile());
+        }
+
+        private boolean isResourceOnly() {
+            return hasResources() && !hasExportedSymbols();
+        }
+
+        private boolean hasResources() {
+            for (NativeResourceSet windowsResourceSet : getSource().withType(NativeResourceSet.class)) {
+                if (!windowsResourceSet.getSource().isEmpty()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean hasExportedSymbols() {
+            for (LanguageSourceSet languageSourceSet : getSource()) {
+                if (!(languageSourceSet instanceof NativeResourceSet)) {
+                    if (!languageSourceSet.getSource().isEmpty()) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private class SharedLibraryRuntimeOutputs extends LibraryOutputs {
+        @Override
+        protected boolean hasOutputs() {
+            return hasSources();
+        }
+
+        @Override
+        protected Set<File> getOutputs() {
+            return Collections.singleton(getSharedLibraryFile());
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryArchiverSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryArchiverSpec.java
new file mode 100644
index 0000000..e6c3838
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryArchiverSpec.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.nativeplatform.internal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultStaticLibraryArchiverSpec extends AbstractBinaryToolSpec implements StaticLibraryArchiverSpec {
+
+    private final List<File> objectFiles = new ArrayList<File>();
+    private File outputFile;
+
+    public List<File> getObjectFiles() {
+        return objectFiles;
+    }
+
+    public void objectFiles(Iterable<File> objectFiles) {
+        for (File objectFile : objectFiles) {
+            this.objectFiles.add(objectFile);
+        }
+    }
+
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpec.java
new file mode 100644
index 0000000..60832f6
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpec.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.nativeplatform.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.nativeplatform.StaticLibraryBinary;
+import org.gradle.nativeplatform.StaticLibraryBinarySpec;
+import org.gradle.nativeplatform.tasks.CreateStaticLibrary;
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.BinaryTasksCollectionWrapper;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultStaticLibraryBinarySpec extends AbstractNativeLibraryBinarySpec implements StaticLibraryBinary, StaticLibraryBinarySpecInternal {
+    private final List<FileCollection> additionalLinkFiles = new ArrayList<FileCollection>();
+    private final DefaultTasksCollection tasks = new DefaultTasksCollection(super.getTasks());
+    private File staticLibraryFile;
+
+    public File getStaticLibraryFile() {
+        return staticLibraryFile;
+    }
+
+    public void setStaticLibraryFile(File staticLibraryFile) {
+        this.staticLibraryFile = staticLibraryFile;
+    }
+
+    public File getPrimaryOutput() {
+        return getStaticLibraryFile();
+    }
+
+    public void additionalLinkFiles(FileCollection files) {
+        this.additionalLinkFiles.add(files);
+    }
+
+    public FileCollection getLinkFiles() {
+        return new StaticLibraryLinkOutputs();
+    }
+
+    public FileCollection getRuntimeFiles() {
+        return new SimpleFileCollection();
+    }
+
+    @Override
+    protected ObjectFilesToBinary getCreateOrLink() {
+        return tasks.getCreateStaticLib();
+    }
+
+    public StaticLibraryBinarySpec.TasksCollection getTasks() {
+        return tasks;
+    }
+
+    private static class DefaultTasksCollection extends BinaryTasksCollectionWrapper implements StaticLibraryBinarySpec.TasksCollection {
+        public DefaultTasksCollection(BinaryTasksCollection delegate) {
+            super(delegate);
+        }
+
+        public CreateStaticLibrary getCreateStaticLib() {
+            return findSingleTaskWithType(CreateStaticLibrary.class);
+        }
+    }
+
+    private class StaticLibraryLinkOutputs extends LibraryOutputs {
+        @Override
+        protected boolean hasOutputs() {
+            return hasSources() || !additionalLinkFiles.isEmpty();
+        }
+
+        @Override
+        protected Set<File> getOutputs() {
+            Set<File> allFiles = new LinkedHashSet<File>();
+            if (hasSources()) {
+                allFiles.add(getStaticLibraryFile());
+            }
+            for (FileCollection resourceSet : additionalLinkFiles) {
+                allFiles.addAll(resourceSet.getFiles());
+            }
+            return allFiles;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultTool.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultTool.java
new file mode 100644
index 0000000..0ab362d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/DefaultTool.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.Tool;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A tool that is part of a tool chain (compiler, linker, assembler, etc).
+ */
+public class DefaultTool implements Tool {
+    private final ArrayList<String> args = new ArrayList<String>();
+
+    public List<String> getArgs() {
+        return args;
+    }
+
+    public void args(String... args) {
+        Collections.addAll(this.args, args);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/LinkerSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/LinkerSpec.java
new file mode 100644
index 0000000..764e50f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/LinkerSpec.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.nativeplatform.internal;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A high level interface to the linker, specifying what is to be linked and how.
+ */
+public interface LinkerSpec extends BinaryToolSpec {
+
+    List<File> getObjectFiles();
+
+    void objectFiles(Iterable<File> objectFiles);
+
+    List<File> getLibraries();
+
+    void libraries(Iterable<File> libraries);
+
+    List<File> getLibraryPath();
+
+    void libraryPath(File... libraryPath);
+
+    File getOutputFile();
+
+    void setOutputFile(File outputFile);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecInternal.java
new file mode 100644
index 0000000..21f559a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecInternal.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.nativeplatform.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+
+import java.io.File;
+import java.util.Collection;
+
+public interface NativeBinarySpecInternal extends NativeBinarySpec, BinarySpecInternal {
+    void setComponent(NativeComponentSpec component);
+
+    void setFlavor(Flavor flavor);
+
+    void setToolChain(NativeToolChain toolChain);
+
+    void setTargetPlatform(NativePlatform targetPlatform);
+
+    void setBuildType(BuildType buildType);
+
+    BinaryNamingScheme getNamingScheme();
+
+    void setNamingScheme(BinaryNamingScheme namingScheme);
+
+    PlatformToolProvider getPlatformToolProvider();
+
+    void setPlatformToolProvider(PlatformToolProvider toolProvider);
+
+    void setResolver(NativeDependencyResolver resolver);
+
+    File getPrimaryOutput();
+
+    Collection<NativeDependencySet> getLibs(DependentSourceSet sourceSet);
+
+    Collection<NativeLibraryBinary> getDependentBinaries();
+
+    /**
+     * Adds some files to include as input to the link/assemble step of this binary.
+     */
+    void binaryInputs(FileCollection files);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinaryRenderer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinaryRenderer.java
new file mode 100644
index 0000000..c002431
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinaryRenderer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.nativeplatform.NativeExecutableBinarySpec;
+
+public class NativeExecutableBinaryRenderer extends AbstractNativeBinaryRenderer<NativeExecutableBinarySpec> {
+    @Override
+    public Class<NativeExecutableBinarySpec> getTargetType() {
+        return NativeExecutableBinarySpec.class;
+    }
+
+    @Override
+    protected void renderTasks(NativeExecutableBinarySpec binary, TextReportBuilder builder) {
+        builder.item("install using task", binary.getTasks().getInstall().getPath());
+    }
+
+    @Override
+    protected void renderOutputs(NativeExecutableBinarySpec binary, TextReportBuilder builder) {
+        builder.item("executable file", binary.getExecutableFile());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinarySpecInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinarySpecInternal.java
new file mode 100644
index 0000000..148f529
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativeExecutableBinarySpecInternal.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.nativeplatform.NativeExecutableBinarySpec;
+
+public interface NativeExecutableBinarySpecInternal extends NativeExecutableBinarySpec, NativeBinarySpecInternal {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativePlatformResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativePlatformResolver.java
new file mode 100644
index 0000000..bf71b87
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/NativePlatformResolver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.specs.Spec;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatforms;
+import org.gradle.platform.base.internal.PlatformRequirement;
+import org.gradle.platform.base.internal.PlatformResolver;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Set;
+
+public class NativePlatformResolver implements PlatformResolver<NativePlatform> {
+    private final Set<NativePlatform> nativePlatforms = Sets.newHashSet();
+
+    public NativePlatformResolver() {
+        nativePlatforms.addAll(NativePlatforms.defaultPlatformDefinitions());
+    }
+
+    public Class<NativePlatform> getType() {
+        return NativePlatform.class;
+    }
+
+    @Override
+    public NativePlatform resolve(final PlatformRequirement platformRequirement) {
+        NativePlatforms.defaultPlatformDefinitions();
+        return CollectionUtils.findFirst(nativePlatforms, new Spec<NativePlatform>() {
+            @Override
+            public boolean isSatisfiedBy(NativePlatform element) {
+                return element.getName().equals(platformRequirement.getPlatformName());
+            }
+        });
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/ProjectNativeLibraryRequirement.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/ProjectNativeLibraryRequirement.java
new file mode 100644
index 0000000..3daca75
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/ProjectNativeLibraryRequirement.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+public class ProjectNativeLibraryRequirement implements NativeLibraryRequirement {
+    private final String projectPath;
+    private final String libraryName;
+    private final String linkage;
+
+    public ProjectNativeLibraryRequirement(String libraryName, String linkage) {
+        this.projectPath = null;
+        this.libraryName = libraryName;
+        this.linkage = linkage;
+    }
+
+    public ProjectNativeLibraryRequirement(String projectPath, String libraryName, String linkage) {
+        this.projectPath = projectPath;
+        this.libraryName = libraryName;
+        this.linkage = linkage;
+    }
+
+    public String getProjectPath() {
+        return projectPath;
+    }
+
+    public String getLibraryName() {
+        return libraryName;
+    }
+
+    public String getLinkage() {
+        return linkage;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinaryRenderer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinaryRenderer.java
new file mode 100644
index 0000000..296bb69
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinaryRenderer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+
+public class SharedLibraryBinaryRenderer extends AbstractNativeBinaryRenderer<SharedLibraryBinarySpec> {
+    @Override
+    public Class<SharedLibraryBinarySpec> getTargetType() {
+        return SharedLibraryBinarySpec.class;
+    }
+
+    @Override
+    protected void renderOutputs(SharedLibraryBinarySpec binary, TextReportBuilder builder) {
+        builder.item("shared library file", binary.getSharedLibraryFile());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinarySpecInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinarySpecInternal.java
new file mode 100644
index 0000000..ba8edee
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryBinarySpecInternal.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+
+public interface SharedLibraryBinarySpecInternal extends SharedLibraryBinarySpec, NativeBinarySpecInternal {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryLinkerSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/SharedLibraryLinkerSpec.java
new file mode 100644
index 0000000..1ac3e66
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform.internal;
+
+public interface SharedLibraryLinkerSpec extends LinkerSpec {
+    String getInstallName();
+
+    void setInstallName(String path);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryArchiverSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryArchiverSpec.java
new file mode 100644
index 0000000..5663bd0
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryArchiverSpec.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.nativeplatform.internal;
+
+import java.io.File;
+import java.util.List;
+
+public interface StaticLibraryArchiverSpec extends BinaryToolSpec {
+
+    File getOutputFile();
+
+    void setOutputFile(File outputFile);
+
+    List<File> getObjectFiles();
+
+    void objectFiles(Iterable<File> source);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinaryRenderer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinaryRenderer.java
new file mode 100644
index 0000000..79cea39
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinaryRenderer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.nativeplatform.StaticLibraryBinarySpec;
+
+public class StaticLibraryBinaryRenderer extends AbstractNativeBinaryRenderer<StaticLibraryBinarySpec> {
+    @Override
+    public Class<StaticLibraryBinarySpec> getTargetType() {
+        return StaticLibraryBinarySpec.class;
+    }
+
+    @Override
+    protected void renderOutputs(StaticLibraryBinarySpec binary, TextReportBuilder builder) {
+        builder.item("static library file", binary.getStaticLibraryFile());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinarySpecInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinarySpecInternal.java
new file mode 100644
index 0000000..56eaa56
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/StaticLibraryBinarySpecInternal.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal;
+
+import org.gradle.nativeplatform.StaticLibraryBinarySpec;
+
+public interface StaticLibraryBinarySpecInternal extends StaticLibraryBinarySpec, NativeBinarySpecInternal {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/TargetedNativeComponentInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/TargetedNativeComponentInternal.java
new file mode 100644
index 0000000..8c126e6
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/TargetedNativeComponentInternal.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.nativeplatform.internal;
+
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.TargetedNativeComponent;
+import org.gradle.platform.base.internal.PlatformAwareComponentSpecInternal;
+
+import java.util.Set;
+
+public interface TargetedNativeComponentInternal extends TargetedNativeComponent, PlatformAwareComponentSpecInternal {
+    Set<Flavor> chooseFlavors(Set<? extends Flavor> candidates);
+    Set<BuildType> chooseBuildTypes(Set<? extends BuildType> candidates);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactory.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactory.java
new file mode 100644
index 0000000..d41a147
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal.configure;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.internal.*;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder;
+
+public class DefaultNativeBinariesFactory implements NativeBinariesFactory {
+    private final Instantiator instantiator;
+    private final Action<NativeBinarySpec> configureAction;
+    private final NativeDependencyResolver resolver;
+    private final ITaskFactory taskFactory;
+
+    public DefaultNativeBinariesFactory(Instantiator instantiator, Action<NativeBinarySpec> configureAction, NativeDependencyResolver resolver, ITaskFactory taskFactory) {
+        this.configureAction = configureAction;
+        this.instantiator = instantiator;
+        this.resolver = resolver;
+        this.taskFactory = taskFactory;
+    }
+
+    public void createNativeBinaries(NativeComponentSpec component, BinaryNamingSchemeBuilder namingScheme, NativeToolChain toolChain, PlatformToolProvider toolProvider, NativePlatform platform, BuildType buildType, Flavor flavor) {
+        if (component instanceof NativeLibrarySpec) {
+            createNativeBinary(DefaultSharedLibraryBinarySpec.class, component, namingScheme.withTypeString("SharedLibrary").build(), toolChain, toolProvider, platform, buildType, flavor);
+            createNativeBinary(DefaultStaticLibraryBinarySpec.class, component, namingScheme.withTypeString("StaticLibrary").build(), toolChain, toolProvider, platform, buildType, flavor);
+        } else {
+            createNativeBinary(DefaultNativeExecutableBinarySpec.class, component, namingScheme.withTypeString("Executable").build(), toolChain, toolProvider, platform, buildType, flavor);
+        }
+    }
+    private <T extends AbstractNativeBinarySpec> void createNativeBinary(Class<T> type, NativeComponentSpec component, BinaryNamingScheme namingScheme,
+                                                                         NativeToolChain toolChain, PlatformToolProvider toolProvider, NativePlatform platform, BuildType buildType, Flavor flavor) {
+        T nativeBinary = create(type, instantiator, component, namingScheme, resolver, toolChain, toolProvider, platform, buildType, flavor, taskFactory);
+        setupDefaults(nativeBinary);
+        component.getBinaries().add(nativeBinary);
+    }
+
+    public static <T extends AbstractNativeBinarySpec> T create(Class<T> type, Instantiator instantiator,
+                                                                NativeComponentSpec component, BinaryNamingScheme namingScheme, NativeDependencyResolver resolver,
+                                                                NativeToolChain toolChain, PlatformToolProvider toolProvider, NativePlatform platform, BuildType buildType, Flavor flavor, ITaskFactory taskFactory) {
+        T nativeBinary = BaseBinarySpec.create(type, namingScheme.getLifecycleTaskName(), instantiator, taskFactory);
+        nativeBinary.setNamingScheme(namingScheme);
+        nativeBinary.setComponent(component);
+        nativeBinary.setTargetPlatform(platform);
+        nativeBinary.setToolChain(toolChain);
+        nativeBinary.setPlatformToolProvider(toolProvider);
+        nativeBinary.setBuildType(buildType);
+        nativeBinary.setFlavor(flavor);
+        nativeBinary.setResolver(resolver);
+        return nativeBinary;
+    }
+
+    private void setupDefaults(NativeBinarySpec nativeBinary) {
+        configureAction.execute(nativeBinary);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeComponentExtension.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeComponentExtension.java
new file mode 100644
index 0000000..0e87674
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeComponentExtension.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.configure;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.nativeplatform.NativeExecutableSpec;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.NativeComponentExtension;
+
+public class DefaultNativeComponentExtension implements NativeComponentExtension {
+    private final NamedDomainObjectContainer<NativeExecutableSpec> executables;
+    private final NamedDomainObjectContainer<NativeLibrarySpec> libraries;
+
+    public DefaultNativeComponentExtension(NamedDomainObjectContainer<NativeExecutableSpec> executables, NamedDomainObjectContainer<NativeLibrarySpec> libraries) {
+        this.executables = executables;
+        this.libraries = libraries;
+    }
+
+    public NamedDomainObjectContainer<NativeExecutableSpec> getExecutables() {
+        return executables;
+    }
+
+    public void executables(Action<? super NamedDomainObjectContainer<? super NativeExecutableSpec>> action) {
+        action.execute(executables);
+    }
+
+    public NamedDomainObjectContainer<NativeLibrarySpec> getLibraries() {
+        return libraries;
+    }
+
+    public void libraries(Action<? super NamedDomainObjectContainer<? super NativeLibrarySpec>> action) {
+        action.execute(libraries);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinariesFactory.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinariesFactory.java
new file mode 100644
index 0000000..f9fca11
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinariesFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal.configure;
+
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+
+public interface NativeBinariesFactory {
+    void createNativeBinaries(NativeComponentSpec component, BinaryNamingSchemeBuilder namingScheme, NativeToolChain toolChain, PlatformToolProvider toolProvider, NativePlatform platform, BuildType buildType, Flavor flavor);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializer.java
new file mode 100644
index 0000000..7a50824
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal.configure;
+
+import org.gradle.api.Action;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeExecutableBinarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.StaticLibraryBinarySpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+
+import java.io.File;
+
+public class NativeBinarySpecInitializer implements Action<NativeBinarySpec> {
+    private final File binariesOutputDir;
+
+    public NativeBinarySpecInitializer(File buildDir) {
+        binariesOutputDir = new File(buildDir, "binaries");
+    }
+
+    public void execute(NativeBinarySpec nativeBinary) {
+        BinaryNamingScheme namingScheme = ((NativeBinarySpecInternal) nativeBinary).getNamingScheme();
+        PlatformToolProvider toolProvider = ((NativeBinarySpecInternal) nativeBinary).getPlatformToolProvider();
+        File binaryOutputDir = new File(binariesOutputDir, namingScheme.getOutputDirectoryBase());
+        String baseName = nativeBinary.getComponent().getBaseName();
+
+        if (nativeBinary instanceof NativeExecutableBinarySpec) {
+            ((NativeExecutableBinarySpec) nativeBinary).setExecutableFile(new File(binaryOutputDir, toolProvider.getExecutableName(baseName)));
+        } else if (nativeBinary instanceof SharedLibraryBinarySpec) {
+            ((SharedLibraryBinarySpec) nativeBinary).setSharedLibraryFile(new File(binaryOutputDir, toolProvider.getSharedLibraryName(baseName)));
+            ((SharedLibraryBinarySpec) nativeBinary).setSharedLibraryLinkFile(new File(binaryOutputDir, toolProvider.getSharedLibraryLinkFileName(baseName)));
+        } else if (nativeBinary instanceof StaticLibraryBinarySpec) {
+            ((StaticLibraryBinarySpec) nativeBinary).setStaticLibraryFile(new File(binaryOutputDir, toolProvider.getStaticLibraryName(baseName)));
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializer.java
new file mode 100644
index 0000000..e20fa0e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializer.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.nativeplatform.internal.configure;
+
+import org.gradle.api.Action;
+import org.gradle.api.Named;
+import org.gradle.api.Transformer;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.TargetedNativeComponentInternal;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.platform.internal.NativePlatforms;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.internal.PlatformResolvers;
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+import org.gradle.util.CollectionUtils;
+
+import java.util.*;
+
+public class NativeComponentSpecInitializer implements Action<NativeComponentSpec> {
+    private final NativeBinariesFactory factory;
+    private final NativeToolChainRegistryInternal toolChainRegistry;
+    private final PlatformResolvers platforms;
+    private final Set<BuildType> allBuildTypes = new LinkedHashSet<BuildType>();
+    private final Set<Flavor> allFlavors = new LinkedHashSet<Flavor>();
+
+    private final BinaryNamingSchemeBuilder namingSchemeBuilder;
+
+    public NativeComponentSpecInitializer(NativeBinariesFactory factory, BinaryNamingSchemeBuilder namingSchemeBuilder, NativeToolChainRegistryInternal toolChainRegistry,
+                                          PlatformResolvers platforms, Collection<? extends BuildType> allBuildTypes, Collection<? extends Flavor> allFlavors) {
+        this.factory = factory;
+        this.namingSchemeBuilder = namingSchemeBuilder;
+        this.toolChainRegistry = toolChainRegistry;
+        this.allBuildTypes.addAll(allBuildTypes);
+        this.allFlavors.addAll(allFlavors);
+        this.platforms = platforms;
+    }
+
+    public void execute(NativeComponentSpec projectNativeComponent) {
+        TargetedNativeComponentInternal targetedComponent = (TargetedNativeComponentInternal) projectNativeComponent;
+        List<NativePlatform> resolvedPlatforms = resolvePlatforms(targetedComponent);
+
+        for (NativePlatform platform: resolvedPlatforms) {
+            NativeToolChainInternal toolChain = (NativeToolChainInternal) toolChainRegistry.getForPlatform(platform);
+            PlatformToolProvider toolProvider = toolChain.select((NativePlatformInternal) platform);
+
+            BinaryNamingSchemeBuilder builder = namingSchemeBuilder.withComponentName(projectNativeComponent.getName());
+            builder = maybeAddDimension(builder, platform, resolvedPlatforms);
+            executeForEachBuildType(projectNativeComponent, (NativePlatformInternal) platform, builder, toolChain, toolProvider);
+        }
+    }
+
+    private List<NativePlatform> resolvePlatforms(TargetedNativeComponentInternal targetedComponent) {
+        List<PlatformRequirement> targetPlatforms = targetedComponent.getTargetPlatforms();
+        if (targetPlatforms.isEmpty()) {
+            PlatformRequirement requirement = DefaultPlatformRequirement.create(NativePlatforms.getDefaultPlatformName());
+            targetPlatforms = Collections.singletonList(requirement);
+        }
+        return CollectionUtils.collect(targetPlatforms, new Transformer<NativePlatform, PlatformRequirement>() {
+            @Override
+            public NativePlatform transform(PlatformRequirement platformRequirement) {
+                return platforms.resolve(NativePlatform.class, platformRequirement);
+            }
+        });
+    }
+
+    private void executeForEachBuildType(NativeComponentSpec projectNativeComponent, NativePlatformInternal platform, BinaryNamingSchemeBuilder builder, NativeToolChainInternal toolChain, PlatformToolProvider toolProvider) {
+        Set<BuildType> targetBuildTypes = ((TargetedNativeComponentInternal) projectNativeComponent).chooseBuildTypes(allBuildTypes);
+        for (BuildType buildType : targetBuildTypes) {
+            BinaryNamingSchemeBuilder nameBuilder = maybeAddDimension(builder, buildType, targetBuildTypes);
+            executeForEachFlavor(projectNativeComponent, platform, buildType, nameBuilder, toolChain, toolProvider);
+        }
+    }
+
+    private void executeForEachFlavor(NativeComponentSpec projectNativeComponent, NativePlatform platform, BuildType buildType, BinaryNamingSchemeBuilder buildTypedNameBuilder, NativeToolChainInternal toolChain, PlatformToolProvider toolProvider) {
+        Set<Flavor> targetFlavors = ((TargetedNativeComponentInternal) projectNativeComponent).chooseFlavors(allFlavors);
+        for (Flavor flavor : targetFlavors) {
+            BinaryNamingSchemeBuilder flavoredNameBuilder = maybeAddDimension(buildTypedNameBuilder, flavor, targetFlavors);
+            factory.createNativeBinaries(projectNativeComponent, flavoredNameBuilder, toolChain, toolProvider, platform, buildType, flavor);
+        }
+    }
+
+    private <T extends Named> BinaryNamingSchemeBuilder maybeAddDimension(BinaryNamingSchemeBuilder builder, T variation, Collection<T> variations) {
+        if (variations.size() > 1) {
+            builder = builder.withVariantDimension(variation.getName());
+        }
+        return builder;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializer.java
new file mode 100644
index 0000000..f62e224
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.configure;
+
+import org.gradle.api.Action;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.util.Map;
+
+public class ToolSettingNativeBinaryInitializer implements Action<NativeBinarySpec> {
+    private final LanguageTransformContainer languageTransforms;
+
+    public ToolSettingNativeBinaryInitializer(LanguageTransformContainer languageTransforms) {
+        this.languageTransforms = languageTransforms;
+    }
+
+    // TODO:DAZ This should only add tools for transforms that apply.
+    public void execute(NativeBinarySpec nativeBinary) {
+        for (LanguageTransform<?, ?> language : languageTransforms) {
+            Map<String, Class<?>> binaryTools = language.getBinaryTools();
+            for (String toolName : binaryTools.keySet()) {
+                ((ExtensionAware) nativeBinary).getExtensions().create(toolName, binaryTools.get(toolName));
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/DefaultPreCompiledHeaderTransformContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/DefaultPreCompiledHeaderTransformContainer.java
new file mode 100644
index 0000000..e4170f3
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/DefaultPreCompiledHeaderTransformContainer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.pch;
+
+import com.google.common.reflect.TypeToken;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+
+public class DefaultPreCompiledHeaderTransformContainer extends DefaultDomainObjectSet<LanguageTransform<?, ?>> implements PreCompiledHeaderTransformContainer {
+    public DefaultPreCompiledHeaderTransformContainer() {
+        super(getLanguageTransformType());
+    }
+
+    private static Class<LanguageTransform<?, ?>> getLanguageTransformType() {
+        @SuppressWarnings("unchecked")
+        Class<LanguageTransform<?, ?>> rawType = (Class<LanguageTransform<?, ?>>) new TypeToken<LanguageTransform<?, ?>>() {}.getRawType();
+        return rawType;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/PreCompiledHeaderTransformContainer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/PreCompiledHeaderTransformContainer.java
new file mode 100644
index 0000000..a2bb5e0
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/pch/PreCompiledHeaderTransformContainer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.pch;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+
+public interface PreCompiledHeaderTransformContainer extends DomainObjectSet<LanguageTransform<?, ?>> {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/AbstractPrebuiltLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/AbstractPrebuiltLibraryBinary.java
new file mode 100644
index 0000000..71de041
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/AbstractPrebuiltLibraryBinary.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+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.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class AbstractPrebuiltLibraryBinary extends AbstractBuildableModelElement implements NativeLibraryBinary {
+    private final String name;
+    private final PrebuiltLibrary library;
+    private final BuildType buildType;
+    private final NativePlatform targetPlatform;
+    private final Flavor flavor;
+
+    public AbstractPrebuiltLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, NativePlatform targetPlatform, Flavor flavor) {
+        this.name = name;
+        this.library = library;
+        this.buildType = buildType;
+        this.targetPlatform = targetPlatform;
+        this.flavor = flavor;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public PrebuiltLibrary getComponent() {
+        return library;
+    }
+
+    public BuildType getBuildType() {
+        return buildType;
+    }
+
+    public Flavor getFlavor() {
+        return flavor;
+    }
+
+    public NativePlatform getTargetPlatform() {
+        return targetPlatform;
+    }
+
+    public FileCollection getHeaderDirs() {
+        return new SimpleFileCollection(library.getHeaders().getSrcDirs());
+    }
+
+    protected FileCollection createFileCollection(File file, String fileDescription) {
+        return new FileCollectionAdapter(new ValidatingFileSet(file, getComponent().getName(), fileDescription));
+    }
+
+    private static class ValidatingFileSet implements MinimalFileSet {
+        private final File file;
+        private final String libraryName;
+        private final String fileDescription;
+
+        private ValidatingFileSet(File file, String libraryName, String fileDescription) {
+            this.file = file;
+            this.libraryName = libraryName;
+            this.fileDescription = fileDescription;
+        }
+
+        public String getDisplayName() {
+            return String.format("%s for prebuilt library '%s'", fileDescription, libraryName);
+        }
+
+        public Set<File> getFiles() {
+            if (file == null) {
+                throw new PrebuiltLibraryResolveException(String.format("%s not set for prebuilt library '%s'.", fileDescription, libraryName));
+            }
+            if (!file.exists() || !file.isFile()) {
+                throw new PrebuiltLibraryResolveException(String.format("%s %s does not exist for prebuilt library '%s'.", fileDescription, file.getAbsolutePath(), libraryName));
+            }
+            return Collections.singleton(file);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibraries.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibraries.java
new file mode 100644
index 0000000..935e346
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibraries.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.PrebuiltLibraries;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+
+public class DefaultPrebuiltLibraries extends AbstractNamedDomainObjectContainer<PrebuiltLibrary> implements PrebuiltLibraries {
+    private final FileResolver fileResolver;
+    private final Action<PrebuiltLibrary> libraryInitializer;
+    private String name;
+
+    public DefaultPrebuiltLibraries(String name, Instantiator instantiator, FileResolver fileResolver, Action<PrebuiltLibrary> libraryInitializer) {
+        super(PrebuiltLibrary.class, instantiator);
+        this.name = name;
+        this.fileResolver = fileResolver;
+        this.libraryInitializer = libraryInitializer;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    protected PrebuiltLibrary doCreate(String name) {
+        return getInstantiator().newInstance(DefaultPrebuiltLibrary.class, name, fileResolver);
+    }
+
+    public PrebuiltLibrary resolveLibrary(String name) {
+        PrebuiltLibrary library = findByName(name);
+        if (library != null && library.getBinaries().isEmpty()) {
+            libraryInitializer.execute(library);
+        }
+        return library;
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibrary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibrary.java
new file mode 100644
index 0000000..b7e75fe
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltLibrary.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+
+public class DefaultPrebuiltLibrary implements PrebuiltLibrary {
+
+    private final String name;
+    private final SourceDirectorySet headers;
+    private final DomainObjectSet<NativeLibraryBinary> binaries;
+
+    public DefaultPrebuiltLibrary(String name, FileResolver fileResolver) {
+        this.name = name;
+        headers = new DefaultSourceDirectorySet("headers", fileResolver);
+        binaries = new DefaultDomainObjectSet<NativeLibraryBinary>(NativeLibraryBinary.class);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public SourceDirectorySet getHeaders() {
+        return headers;
+    }
+
+    public DomainObjectSet<NativeLibraryBinary> getBinaries() {
+        return binaries;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.java
new file mode 100644
index 0000000..70fdac9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinary.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+import org.gradle.nativeplatform.PrebuiltSharedLibraryBinary;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.io.File;
+
+public class DefaultPrebuiltSharedLibraryBinary extends AbstractPrebuiltLibraryBinary implements PrebuiltSharedLibraryBinary {
+    private File sharedLibraryFile;
+    private File sharedLibraryLinkFile;
+
+    public DefaultPrebuiltSharedLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, NativePlatform targetPlatform, Flavor flavor) {
+        super(name, library, buildType, targetPlatform, flavor);
+    }
+
+    public String getDisplayName() {
+        return String.format("shared library '%s'", getName());
+    }
+
+    public void setSharedLibraryFile(File sharedLibraryFile) {
+        this.sharedLibraryFile = sharedLibraryFile;
+    }
+
+    public File getSharedLibraryFile() {
+        return sharedLibraryFile;
+    }
+
+    public void setSharedLibraryLinkFile(File sharedLibraryLinkFile) {
+        this.sharedLibraryLinkFile = sharedLibraryLinkFile;
+    }
+
+    public File getSharedLibraryLinkFile() {
+        if (sharedLibraryLinkFile != null) {
+            return sharedLibraryLinkFile;
+        }
+        return sharedLibraryFile;
+    }
+
+    public FileCollection getLinkFiles() {
+        return createFileCollection(getSharedLibraryLinkFile(), "Shared library link file");
+    }
+
+    public FileCollection getRuntimeFiles() {
+        return createFileCollection(getSharedLibraryFile(), "Shared library runtime file");
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java
new file mode 100644
index 0000000..1ff742d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinary.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+import org.gradle.nativeplatform.PrebuiltStaticLibraryBinary;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.io.File;
+
+public class DefaultPrebuiltStaticLibraryBinary extends AbstractPrebuiltLibraryBinary implements PrebuiltStaticLibraryBinary {
+    private File staticLibraryFile;
+
+    public DefaultPrebuiltStaticLibraryBinary(String name, PrebuiltLibrary library, BuildType buildType, NativePlatform targetPlatform, Flavor flavor) {
+        super(name, library, buildType, targetPlatform, flavor);
+    }
+
+    public String getDisplayName() {
+        return String.format("static library '%s'", getName());
+    }
+
+    public void setStaticLibraryFile(File staticLibraryFile) {
+        this.staticLibraryFile = staticLibraryFile;
+    }
+
+    public File getStaticLibraryFile() {
+        return staticLibraryFile;
+    }
+
+    public FileCollection getLinkFiles() {
+        return createFileCollection(getStaticLibraryFile(), "Static library file");
+    }
+
+    public FileCollection getRuntimeFiles() {
+        return new SimpleFileCollection();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryBinaryLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryBinaryLocator.java
new file mode 100644
index 0000000..51ff73b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryBinaryLocator.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.NamedDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.internal.resolve.LibraryBinaryLocator;
+import org.gradle.nativeplatform.internal.resolve.ProjectLocator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PrebuiltLibraryBinaryLocator implements LibraryBinaryLocator {
+    private final ProjectLocator projectLocator;
+
+    public PrebuiltLibraryBinaryLocator(ProjectLocator projectLocator) {
+        this.projectLocator = projectLocator;
+    }
+
+    public DomainObjectSet<NativeLibraryBinary> getBinaries(NativeLibraryRequirement requirement) {
+        ProjectInternal project = projectLocator.locateProject(requirement.getProjectPath());
+        NamedDomainObjectSet<PrebuiltLibraries> repositories = project.getModelRegistry().realize(ModelPath.path("repositories"), ModelType.of(Repositories.class)).withType(PrebuiltLibraries.class);
+        if (repositories.isEmpty()) {
+            throw new PrebuiltLibraryResolveException("Project does not have any prebuilt library repositories.");
+        }
+        PrebuiltLibrary prebuiltLibrary = getPrebuiltLibrary(repositories, requirement.getLibraryName());
+        return prebuiltLibrary.getBinaries();
+    }
+
+    private PrebuiltLibrary getPrebuiltLibrary(NamedDomainObjectSet<PrebuiltLibraries> repositories, String libraryName) {
+        List<String> repositoryNames = new ArrayList<String>();
+        for (PrebuiltLibraries prebuiltLibraries : repositories) {
+            repositoryNames.add(prebuiltLibraries.getName());
+            PrebuiltLibrary prebuiltLibrary = prebuiltLibraries.resolveLibrary(libraryName);
+            if (prebuiltLibrary != null) {
+                return prebuiltLibrary;
+            }
+        }
+        throw new PrebuiltLibraryResolveException(
+                String.format("Prebuilt library with name '%s' not found in repositories '%s'.", libraryName, repositoryNames));
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryInitializer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryInitializer.java
new file mode 100644
index 0000000..301796f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryInitializer.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.Action;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.Flavor;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.PrebuiltLibrary;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatforms;
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.DefaultBinaryNamingSchemeBuilder;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+// TODO:DAZ We shouldn't be instantiating all binary instances: instead should be instantiating required binary instance when resolving
+public class PrebuiltLibraryInitializer implements Action<PrebuiltLibrary> {
+    private final Instantiator instantiator;
+    private final Set<NativePlatform> allPlatforms = new LinkedHashSet<NativePlatform>();
+    private final Set<BuildType> allBuildTypes = new LinkedHashSet<BuildType>();
+    private final Set<Flavor> allFlavors = new LinkedHashSet<Flavor>();
+
+    public PrebuiltLibraryInitializer(Instantiator instantiator,
+                                      Collection<? extends NativePlatform> allPlatforms, Collection<? extends BuildType> allBuildTypes, Collection<? extends Flavor> allFlavors) {
+        this.instantiator = instantiator;
+        this.allPlatforms.addAll(allPlatforms);
+        this.allPlatforms.addAll(NativePlatforms.defaultPlatformDefinitions());
+        this.allBuildTypes.addAll(allBuildTypes);
+        this.allFlavors.addAll(allFlavors);
+    }
+
+    public void execute(PrebuiltLibrary prebuiltLibrary) {
+        for (NativePlatform platform : allPlatforms) {
+            for (BuildType buildType : allBuildTypes) {
+                for (Flavor flavor : allFlavors) {
+                    createNativeBinaries(prebuiltLibrary, platform, buildType, flavor);
+                }
+            }
+        }
+    }
+
+    public void createNativeBinaries(PrebuiltLibrary library, NativePlatform platform, BuildType buildType, Flavor flavor) {
+        createNativeBinary(DefaultPrebuiltSharedLibraryBinary.class, library, platform, buildType, flavor);
+        createNativeBinary(DefaultPrebuiltStaticLibraryBinary.class, library, platform, buildType, flavor);
+    }
+
+    public <T extends NativeLibraryBinary> void createNativeBinary(Class<T> type, PrebuiltLibrary library, NativePlatform platform, BuildType buildType, Flavor flavor) {
+        String name = getName(type, library, platform, buildType, flavor);
+        T nativeBinary = instantiator.newInstance(type, name, library, buildType, platform, flavor);
+        library.getBinaries().add(nativeBinary);
+    }
+
+    private <T extends NativeLibraryBinary> String getName(Class<T> type, PrebuiltLibrary library, NativePlatform platform, BuildType buildType, Flavor flavor) {
+        BinaryNamingSchemeBuilder namingScheme = new DefaultBinaryNamingSchemeBuilder()
+                .withComponentName(library.getName())
+                .withTypeString(type.getSimpleName())
+                .withVariantDimension(platform.getName())
+                .withVariantDimension(buildType.getName())
+                .withVariantDimension(flavor.getName());
+        return namingScheme.build().getLifecycleTaskName();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryResolveException.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryResolveException.java
new file mode 100644
index 0000000..39dd91f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/prebuilt/PrebuiltLibraryResolveException.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.nativeplatform.internal.prebuilt;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+
+ at Contextual
+public class PrebuiltLibraryResolveException extends GradleException {
+    public PrebuiltLibraryResolveException(String message) {
+        super(message);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ApiRequirementNativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ApiRequirementNativeDependencyResolver.java
new file mode 100644
index 0000000..2144358
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ApiRequirementNativeDependencyResolver.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.nativeplatform.NativeDependencySet;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+/**
+ * Adapts an 'api' library requirement to a default linkage, and then wraps the result so that only headers are provided.
+ */
+public class ApiRequirementNativeDependencyResolver implements NativeDependencyResolver {
+    private final NativeDependencyResolver delegate;
+
+    public ApiRequirementNativeDependencyResolver(NativeDependencyResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
+        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getAllResolutions()) {
+            String linkage = getLinkage(resolution);
+            if ("api".equals(linkage)) {
+                resolution.setRequirement(new ApiAdaptedNativeLibraryRequirement(resolution.getRequirement()));
+            }
+        }
+
+        delegate.resolve(nativeBinaryResolveResult);
+
+        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getAllResolutions()) {
+            if (resolution.getRequirement() instanceof ApiAdaptedNativeLibraryRequirement) {
+                ApiAdaptedNativeLibraryRequirement adaptedRequirement = (ApiAdaptedNativeLibraryRequirement) resolution.getRequirement();
+                resolution.setRequirement(adaptedRequirement.getOriginal());
+//                resolution.setLibraryBinary(null);
+                resolution.setNativeDependencySet(new ApiNativeDependencySet(resolution.getNativeDependencySet()));
+            }
+        }
+    }
+
+    private String getLinkage(NativeBinaryRequirementResolveResult resolution) {
+        if (resolution.getRequirement() == null) {
+            return null;
+        }
+        return resolution.getRequirement().getLinkage();
+    }
+
+    private static class ApiAdaptedNativeLibraryRequirement implements NativeLibraryRequirement {
+        private final NativeLibraryRequirement original;
+        public ApiAdaptedNativeLibraryRequirement(NativeLibraryRequirement original) {
+            this.original = original;
+        }
+
+        public NativeLibraryRequirement getOriginal() {
+            return original;
+        }
+
+        public String getProjectPath() {
+            return original.getProjectPath();
+        }
+
+        public String getLibraryName() {
+            return original.getLibraryName();
+        }
+
+        public String getLinkage() {
+            // Rely on the default linkage for providing the headers
+            return null;
+        }
+    }
+
+    private static class ApiNativeDependencySet implements NativeDependencySet {
+        private final NativeDependencySet delegate;
+
+        public ApiNativeDependencySet(NativeDependencySet delegate) {
+            this.delegate = delegate;
+        }
+
+        public FileCollection getIncludeRoots() {
+            return delegate.getIncludeRoots();
+        }
+
+        public FileCollection getLinkFiles() {
+            return new SimpleFileCollection();
+        }
+
+        public FileCollection getRuntimeFiles() {
+            return new SimpleFileCollection();
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ChainedLibraryBinaryLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ChainedLibraryBinaryLocator.java
new file mode 100644
index 0000000..f215c2e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ChainedLibraryBinaryLocator.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChainedLibraryBinaryLocator implements LibraryBinaryLocator {
+    private final List<LibraryBinaryLocator> locators = new ArrayList<LibraryBinaryLocator>();
+
+    public ChainedLibraryBinaryLocator(List<? extends LibraryBinaryLocator> locators) {
+        this.locators.addAll(locators);
+    }
+
+    public DomainObjectSet<NativeLibraryBinary> getBinaries(NativeLibraryRequirement requirement) {
+        List<Exception> failures = new ArrayList<Exception>();
+        for (LibraryBinaryLocator locator : locators) {
+            try {
+                return locator.getBinaries(requirement);
+            } catch (Exception e) {
+                failures.add(e);
+            }
+        }
+        throw new LibraryResolveException(getFailureMessage(requirement), failures);
+    }
+
+    private String getFailureMessage(NativeLibraryRequirement requirement) {
+        return requirement.getProjectPath() == null
+                ? String.format("Could not locate library '%s'.", requirement.getLibraryName())
+                : String.format("Could not locate library '%s' for project '%s'.", requirement.getLibraryName(), requirement.getProjectPath());
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultLibraryResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultLibraryResolver.java
new file mode 100644
index 0000000..24506f4
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultLibraryResolver.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.util.GUtil;
+
+import java.util.Set;
+
+class DefaultLibraryResolver {
+    private final Instantiator instantiator;
+    private final NativeLibraryRequirement requirement;
+    private final NativeBinarySpec context;
+    private final LibraryBinaryLocator libraryBinaryLocator;
+
+    public DefaultLibraryResolver(LibraryBinaryLocator libraryBinaryLocator, Instantiator instantiator, NativeLibraryRequirement requirement, NativeBinarySpec context) {
+        this.instantiator = instantiator;
+        this.requirement = requirement;
+        this.context = context;
+        this.libraryBinaryLocator = libraryBinaryLocator;
+    }
+
+    public NativeLibraryBinary resolveLibraryBinary() {
+        return new LibraryResolution()
+                .withFlavor(context.getFlavor())
+                .withPlatform(context.getTargetPlatform())
+                .withBuildType(context.getBuildType())
+                .resolveLibrary(libraryBinaryLocator.getBinaries(requirement));
+    }
+
+    private class LibraryResolution {
+        private Flavor flavor;
+        private NativePlatform platform;
+        private BuildType buildType;
+
+        public LibraryResolution withFlavor(Flavor flavor) {
+            this.flavor = flavor;
+            return this;
+        }
+
+        public LibraryResolution withPlatform(NativePlatform platform) {
+            this.platform = platform;
+            return this;
+        }
+
+        public LibraryResolution withBuildType(BuildType buildType) {
+            this.buildType = buildType;
+            return this;
+        }
+
+        public NativeDependencySet resolve(DomainObjectSet<NativeLibraryBinary> allBinaries) {
+            NativeLibraryBinary resolve = resolveLibrary(allBinaries);
+            return new DefaultNativeDependencySet(resolve);
+        }
+
+        public NativeLibraryBinary resolveLibrary(DomainObjectSet<NativeLibraryBinary> allBinaries) {
+            Class<? extends NativeLibraryBinary> type = getTypeForLinkage(requirement.getLinkage());
+            DomainObjectSet<? extends NativeLibraryBinary> candidateBinaries = allBinaries.withType(type);
+            return resolve(candidateBinaries);
+        }
+
+        private Class<? extends NativeLibraryBinary> getTypeForLinkage(String linkage) {
+            if ("static".equals(linkage)) {
+                return StaticLibraryBinary.class;
+            }
+            if ("shared".equals(linkage) || linkage == null) {
+                return SharedLibraryBinary.class;
+            }
+            throw new InvalidUserDataException("Not a valid linkage: " + linkage);
+        }
+
+        private NativeLibraryBinary resolve(Set<? extends NativeLibraryBinary> candidates) {
+            for (NativeLibraryBinary candidate : candidates) {
+                if (flavor != null && !flavor.getName().equals(candidate.getFlavor().getName())) {
+                    continue;
+                }
+                if (platform != null && !platform.getName().equals(candidate.getTargetPlatform().getName())) {
+                    continue;
+                }
+                if (buildType != null && !buildType.getName().equals(candidate.getBuildType().getName())) {
+                    continue;
+                }
+
+                return candidate;
+            }
+
+            String typeName = GUtil.elvis(requirement.getLinkage(), "shared");
+            throw new LibraryResolveException(String.format("No %s library binary available for library '%s' with [flavor: '%s', platform: '%s', buildType: '%s']",
+                    typeName, requirement.getLibraryName(), flavor.getName(), platform.getName(), buildType.getName()));
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultNativeDependencySet.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultNativeDependencySet.java
new file mode 100644
index 0000000..b99e8e7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultNativeDependencySet.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.nativeplatform.NativeDependencySet;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+
+public class DefaultNativeDependencySet implements NativeDependencySet {
+    private final NativeLibraryBinary binary;
+
+    public DefaultNativeDependencySet(NativeLibraryBinary binary) {
+        this.binary = binary;
+    }
+
+    public FileCollection getIncludeRoots() {
+        return binary.getHeaderDirs();
+    }
+
+    public FileCollection getLinkFiles() {
+        return binary.getLinkFiles();
+    }
+
+    public FileCollection getRuntimeFiles() {
+        return binary.getRuntimeFiles();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultProjectLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultProjectLocator.java
new file mode 100644
index 0000000..e39e7af
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/DefaultProjectLocator.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.project.ProjectInternal;
+
+public class DefaultProjectLocator implements ProjectLocator {
+    private final String projectPath;
+    private final ProjectFinder delegate;
+
+    public DefaultProjectLocator(String projectPath, ProjectFinder delegate) {
+        this.projectPath = projectPath;
+        this.delegate = delegate;
+    }
+
+    public ProjectInternal locateProject(String path) {
+        if (path == null || path.length() == 0) {
+            return delegate.getProject(projectPath);
+        }
+
+        ProjectInternal referencedProject = delegate.getProject(path);
+        // TODO This is a brain-dead way to ensure that the reference project's model is ready to access
+        referencedProject.evaluate();
+        referencedProject.getTasks().discoverTasks();
+        return referencedProject;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/InputHandlingNativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/InputHandlingNativeDependencyResolver.java
new file mode 100644
index 0000000..d32a431
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/InputHandlingNativeDependencyResolver.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.nativeplatform.internal.resolve;
+
+import org.gradle.nativeplatform.NativeDependencySet;
+
+public class InputHandlingNativeDependencyResolver implements NativeDependencyResolver {
+    private final NativeDependencyResolver delegate;
+
+    public InputHandlingNativeDependencyResolver(NativeDependencyResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
+        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
+            if (resolution.getInput() instanceof NativeDependencySet) {
+                resolution.setNativeDependencySet((NativeDependencySet) resolution.getInput());
+            }
+        }
+        delegate.resolve(nativeBinaryResolveResult);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryBinaryLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryBinaryLocator.java
new file mode 100644
index 0000000..188b22b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryBinaryLocator.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+public interface LibraryBinaryLocator {
+    DomainObjectSet<NativeLibraryBinary> getBinaries(NativeLibraryRequirement requirement);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryNativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryNativeDependencyResolver.java
new file mode 100644
index 0000000..a7cfaa7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryNativeDependencyResolver.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.nativeplatform.internal.resolve;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+
+public class LibraryNativeDependencyResolver implements NativeDependencyResolver {
+    private final LibraryBinaryLocator libraryBinaryLocator;
+    private final Instantiator instantiator;
+
+    public LibraryNativeDependencyResolver(final LibraryBinaryLocator locator, Instantiator instantiator) {
+        libraryBinaryLocator = locator;
+        this.instantiator = instantiator;
+    }
+
+    public void resolve(NativeBinaryResolveResult resolution) {
+        for (NativeBinaryRequirementResolveResult requirementResolution : resolution.getPendingResolutions()) {
+            DefaultLibraryResolver libraryResolver = new DefaultLibraryResolver(libraryBinaryLocator, instantiator, requirementResolution.getRequirement(), resolution.getTarget());
+            NativeLibraryBinary libraryBinary = libraryResolver.resolveLibraryBinary();
+            requirementResolution.setLibraryBinary(libraryBinary);
+            requirementResolution.setNativeDependencySet(new DefaultNativeDependencySet(libraryBinary));
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryResolveException.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryResolveException.java
new file mode 100644
index 0000000..b92f26a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/LibraryResolveException.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.nativeplatform.internal.resolve;
+
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
+import org.gradle.internal.exceptions.Contextual;
+
+ at Contextual
+class LibraryResolveException extends DefaultMultiCauseException {
+
+    public LibraryResolveException(String message) {
+        super(message);
+    }
+
+    LibraryResolveException(String message, Iterable<? extends Throwable> causes) {
+        super(message, causes);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryRequirementResolveResult.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryRequirementResolveResult.java
new file mode 100644
index 0000000..a62d193
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryRequirementResolveResult.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.nativeplatform.internal.resolve;
+
+import org.gradle.nativeplatform.NativeDependencySet;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+public class NativeBinaryRequirementResolveResult {
+    private Object input;
+    private NativeLibraryRequirement requirement;
+    private NativeLibraryBinary libraryBinary;
+    private NativeDependencySet nativeDependencySet;
+
+    public NativeBinaryRequirementResolveResult(Object input) {
+        this.input = input;
+    }
+
+    public Object getInput() {
+        return input;
+    }
+
+    public void setRequirement(NativeLibraryRequirement requirement) {
+        this.requirement = requirement;
+    }
+
+    public NativeLibraryRequirement getRequirement() {
+        return requirement;
+    }
+
+    public NativeLibraryBinary getLibraryBinary() {
+        return libraryBinary;
+    }
+
+    public void setLibraryBinary(NativeLibraryBinary libraryBinary) {
+        this.libraryBinary = libraryBinary;
+    }
+
+    public NativeDependencySet getNativeDependencySet() {
+        return nativeDependencySet;
+    }
+
+    public void setNativeDependencySet(NativeDependencySet nativeDependencySet) {
+        this.nativeDependencySet = nativeDependencySet;
+    }
+
+    public boolean isComplete() {
+        return nativeDependencySet != null;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryResolveResult.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryResolveResult.java
new file mode 100644
index 0000000..c60e333
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeBinaryResolveResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.resolve;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.specs.Spec;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.NativeDependencySet;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class NativeBinaryResolveResult {
+    private final NativeBinarySpec target;
+    private final List<NativeBinaryRequirementResolveResult> resolutions = new ArrayList<NativeBinaryRequirementResolveResult>();
+
+    public NativeBinaryResolveResult(NativeBinarySpec target, Collection<?> libs) {
+        this.target = target;
+        for (Object lib : libs) {
+            resolutions.add(new NativeBinaryRequirementResolveResult(lib));
+        }
+    }
+
+    public NativeBinarySpec getTarget() {
+        return target;
+    }
+
+    public List<NativeBinaryRequirementResolveResult> getAllResolutions() {
+        return resolutions;
+    }
+
+    public List<NativeDependencySet> getAllResults() {
+        return CollectionUtils.collect(getAllResolutions(), new Transformer<NativeDependencySet, NativeBinaryRequirementResolveResult>() {
+            public NativeDependencySet transform(NativeBinaryRequirementResolveResult original) {
+                return original.getNativeDependencySet();
+            }
+        });
+    }
+
+    public List<NativeLibraryBinary> getAllLibraryBinaries() {
+        List<NativeLibraryBinary> result = new ArrayList<NativeLibraryBinary>();
+        for (NativeBinaryRequirementResolveResult resolution : getAllResolutions()) {
+            if (resolution.getLibraryBinary() != null) {
+                result.add(resolution.getLibraryBinary());
+            }
+        }
+        return result;
+    }
+
+    public List<NativeBinaryRequirementResolveResult> getPendingResolutions() {
+        return CollectionUtils.filter(resolutions, new Spec<NativeBinaryRequirementResolveResult>() {
+            public boolean isSatisfiedBy(NativeBinaryRequirementResolveResult element) {
+                return !element.isComplete();
+            }
+        });
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParser.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParser.java
new file mode 100644
index 0000000..d352550
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParser.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.internal.ProjectNativeLibraryRequirement;
+
+class NativeDependencyNotationParser {
+    public static NotationParser<Object, NativeLibraryRequirement> parser() {
+        return NotationParserBuilder
+                .toType(NativeLibraryRequirement.class)
+                .converter(new LibraryConverter())
+                .converter(new NativeLibraryRequirementMapNotationConverter())
+                .toComposite();
+    }
+
+    private static class LibraryConverter extends TypedNotationConverter<NativeLibrarySpec, NativeLibraryRequirement> {
+        private LibraryConverter() {
+            super(NativeLibrarySpec.class);
+        }
+
+        @Override
+        protected NativeLibraryRequirement parseType(NativeLibrarySpec notation) {
+            return notation.getShared();
+        }
+    }
+
+    private static class NativeLibraryRequirementMapNotationConverter extends MapNotationConverter<NativeLibraryRequirement> {
+
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Map with mandatory 'library' and optional 'project' and 'linkage' keys").example("[project: ':someProj', library: 'mylib', linkage: 'static']");
+        }
+
+        @SuppressWarnings("unused")
+        protected NativeLibraryRequirement parseMap(@MapKey("library") String libraryName, @Optional @MapKey("project") String projectPath, @Optional @MapKey("linkage") String linkage) {
+            return new ProjectNativeLibraryRequirement(projectPath, libraryName, linkage);
+        }
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolver.java
new file mode 100644
index 0000000..c01757c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolver.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.nativeplatform.internal.resolve;
+
+public interface NativeDependencyResolver {
+    void resolve(NativeBinaryResolveResult resolution);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolverServices.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolverServices.java
new file mode 100644
index 0000000..c02714c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyResolverServices.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal.resolve;
+
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.internal.prebuilt.PrebuiltLibraryBinaryLocator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NativeDependencyResolverServices {
+
+    public ProjectLocator createProjectLocator(ProjectFinder projectFinder, DependencyMetaDataProvider metaDataProvider) {
+        String currentProjectPath = metaDataProvider.getModule().getProjectPath();
+        return new DefaultProjectLocator(currentProjectPath, projectFinder);
+    }
+
+    public LibraryBinaryLocator createLibraryBinaryLocator(ProjectLocator projectLocator) {
+        List<LibraryBinaryLocator> locators = new ArrayList<LibraryBinaryLocator>();
+        locators.add(new ProjectLibraryBinaryLocator(projectLocator));
+        locators.add(new PrebuiltLibraryBinaryLocator(projectLocator));
+        return new ChainedLibraryBinaryLocator(locators);
+    }
+
+    public NativeDependencyResolver createResolver(LibraryBinaryLocator locator, Instantiator instantiator) {
+        NativeDependencyResolver resolver = new LibraryNativeDependencyResolver(locator, instantiator);
+        resolver = new ApiRequirementNativeDependencyResolver(resolver);
+        resolver = new RequirementParsingNativeDependencyResolver(resolver);
+        resolver = new SourceSetNativeDependencyResolver(resolver);
+        return new InputHandlingNativeDependencyResolver(resolver);
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocator.java
new file mode 100644
index 0000000..5910bab
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocator.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.nativeplatform.internal.resolve;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.Project;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.NativeLibraryBinary;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+
+public class ProjectLibraryBinaryLocator implements LibraryBinaryLocator {
+    private final ProjectLocator projectLocator;
+
+    public ProjectLibraryBinaryLocator(ProjectLocator projectLocator) {
+        this.projectLocator = projectLocator;
+    }
+
+    // Converts the binaries of a project library into regular binary instances
+    public DomainObjectSet<NativeLibraryBinary> getBinaries(NativeLibraryRequirement requirement) {
+        Project project = findProject(requirement);
+        ComponentSpecContainer componentSpecContainer = project.getExtensions().findByType(ComponentSpecContainer.class);
+        if (componentSpecContainer == null) {
+            throw new LibraryResolveException(String.format("Project does not have a libraries container: '%s'", project.getPath()));
+        }
+        DomainObjectSet<NativeBinarySpec> projectBinaries = componentSpecContainer.withType(NativeLibrarySpec.class).getByName(requirement.getLibraryName()).getBinaries().withType(NativeBinarySpec.class);
+        DomainObjectSet<NativeLibraryBinary> binaries = new DefaultDomainObjectSet<NativeLibraryBinary>(NativeLibraryBinary.class);
+        // TODO:DAZ Convert, don't cast
+        for (NativeBinarySpec nativeBinarySpec : projectBinaries) {
+            binaries.add((NativeLibraryBinary) nativeBinarySpec);
+        }
+        return binaries;
+    }
+
+    private Project findProject(NativeLibraryRequirement requirement) {
+        return projectLocator.locateProject(requirement.getProjectPath());
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLocator.java
new file mode 100644
index 0000000..7d316af
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLocator.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.internal.resolve;
+
+import org.gradle.api.internal.project.ProjectInternal;
+
+public interface ProjectLocator {
+    ProjectInternal locateProject(String path);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/RequirementParsingNativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/RequirementParsingNativeDependencyResolver.java
new file mode 100644
index 0000000..21c4b69
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/RequirementParsingNativeDependencyResolver.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.nativeplatform.internal.resolve;
+
+import org.gradle.internal.typeconversion.NotationParser;
+import org.gradle.nativeplatform.NativeLibraryRequirement;
+
+public class RequirementParsingNativeDependencyResolver implements NativeDependencyResolver {
+    private final NotationParser<Object, NativeLibraryRequirement> parser = NativeDependencyNotationParser.parser();
+
+    private final NativeDependencyResolver delegate;
+
+    public RequirementParsingNativeDependencyResolver(NativeDependencyResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
+        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
+            NativeLibraryRequirement requirement = parser.parseNotation(resolution.getInput());
+            resolution.setRequirement(requirement);
+        }
+        delegate.resolve(nativeBinaryResolveResult);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/SourceSetNativeDependencyResolver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/SourceSetNativeDependencyResolver.java
new file mode 100644
index 0000000..5d3a6b7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/resolve/SourceSetNativeDependencyResolver.java
@@ -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.nativeplatform.internal.resolve;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.AbstractFileCollection;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.nativeplatform.NativeDependencySet;
+
+import java.io.File;
+import java.util.Set;
+
+public class SourceSetNativeDependencyResolver implements NativeDependencyResolver {
+    private final NativeDependencyResolver delegate;
+
+    public SourceSetNativeDependencyResolver(NativeDependencyResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    public void resolve(NativeBinaryResolveResult nativeBinaryResolveResult) {
+        for (NativeBinaryRequirementResolveResult resolution : nativeBinaryResolveResult.getPendingResolutions()) {
+            if (resolution.getInput() instanceof LanguageSourceSet) {
+                LanguageSourceSet input = (LanguageSourceSet) resolution.getInput();
+                resolution.setNativeDependencySet(createNativeDependencySet(input));
+            }
+        }
+        delegate.resolve(nativeBinaryResolveResult);
+    }
+
+    private NativeDependencySet createNativeDependencySet(LanguageSourceSet sourceSet) {
+        if (sourceSet instanceof HeaderExportingSourceSet) {
+            return new LanguageSourceSetNativeDependencySet((HeaderExportingSourceSet) sourceSet);
+        }
+        return new EmptyNativeDependencySet();
+    }
+
+    private static class EmptyNativeDependencySet implements NativeDependencySet {
+        public FileCollection getIncludeRoots() {
+            return empty();
+        }
+
+        public FileCollection getLinkFiles() {
+            return empty();
+        }
+
+        public FileCollection getRuntimeFiles() {
+            return empty();
+        }
+
+        private FileCollection empty() {
+            return new SimpleFileCollection();
+        }
+    }
+
+    private static class LanguageSourceSetNativeDependencySet extends EmptyNativeDependencySet {
+        private final HeaderExportingSourceSet sourceSet;
+
+        private LanguageSourceSetNativeDependencySet(HeaderExportingSourceSet sourceSet) {
+            this.sourceSet = sourceSet;
+        }
+
+        public FileCollection getIncludeRoots() {
+            return new AbstractFileCollection() {
+                @Override
+                public String getDisplayName() {
+                    return "Include roots of " + sourceSet.getName();
+                }
+
+                public Set<File> getFiles() {
+                    return sourceSet.getExportedHeaders().getSrcDirs();
+                }
+
+                @Override
+                public TaskDependency getBuildDependencies() {
+                    return sourceSet.getBuildDependencies();
+                }
+            };
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/services/NativeBinaryServices.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/services/NativeBinaryServices.java
new file mode 100644
index 0000000..27bcc00
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/internal/services/NativeBinaryServices.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.nativeplatform.internal.services;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.nativeplatform.internal.NativeExecutableBinaryRenderer;
+import org.gradle.nativeplatform.internal.SharedLibraryBinaryRenderer;
+import org.gradle.nativeplatform.internal.StaticLibraryBinaryRenderer;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolverServices;
+import org.gradle.nativeplatform.test.internal.NativeTestSuiteBinaryRenderer;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultWindowsSdkLocator;
+
+public class NativeBinaryServices implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.add(SharedLibraryBinaryRenderer.class);
+        registration.add(StaticLibraryBinaryRenderer.class);
+        registration.add(NativeExecutableBinaryRenderer.class);
+        registration.add(NativeTestSuiteBinaryRenderer.class);
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.add(DefaultVisualStudioLocator.class);
+        registration.add(DefaultWindowsSdkLocator.class);
+        registration.add(CompilerMetaDataProviderFactory.class);
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.addProvider(new NativeDependencyResolverServices());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/package-info.java
new file mode 100644
index 0000000..8349800
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/Architecture.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/Architecture.java
new file mode 100644
index 0000000..7113204
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/Architecture.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.nativeplatform.platform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * A cpu architecture.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface Architecture extends Named {
+    /**
+     * Returns a human-consumable display name for this architecture.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/NativePlatform.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/NativePlatform.java
new file mode 100644
index 0000000..4349064
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/NativePlatform.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.Platform;
+
+/**
+ * A target platform for building native binaries. Each target platform is given a name, and may optionally be given
+ * a specific {@link Architecture} and/or {@link OperatingSystem} to target.
+ *
+ * <pre>
+ *     model {
+ *         platforms {
+ *             windows_x86 {
+ *                 architecture "i386"
+ *                 operatingSystem "windows"
+ *             }
+ *         }
+ *     }
+ * </pre>
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface NativePlatform extends Platform {
+    /**
+     * The cpu architecture being targeted. Defaults to the default architecture produced by the tool chain.
+     */
+    Architecture getArchitecture();
+
+    /**
+     * Sets the cpu architecture being targeted.
+     * The architecture is provided as a string name, which is translated into one of the supported architecture types.
+     *
+     * <table>
+     *     <tr>
+     *         <th>Instruction Set</th>
+     *         <th>32-bit names</th>
+     *         <th>64-bit names</th>
+     *     </tr>
+     *     <tr>
+     *         <td>Intel x86</td>
+     *         <td>"x86", "i386", "ia-32"</td>
+     *         <td>"x86_64", "amd64", "x64", "x86-64"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Intel Itanium</td>
+     *         <td></td>
+     *         <td>"ia-64"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Power PC</td>
+     *         <td>"ppc"</td>
+     *         <td>"ppc64"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Sparc</td>
+     *         <td>"sparc", "sparc32", "sparc-v7", "sparc-v8"</td>
+     *         <td>"sparc64", "ultrasparc", "sparc-v9"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>ARM</td>
+     *         <td>"arm"</td>
+     *         <td></td>
+     *     </tr>
+     * </table>
+     */
+    void architecture(String name);
+
+    /**
+     * The operating system being targeted.
+     * Defaults to the default operating system targeted by the tool chain (normally the current operating system).
+     */
+    OperatingSystem getOperatingSystem();
+
+    /**
+     * Sets the operating system being targeted.
+     * The operating system is provided as a string name, which is translated into one of the supported operating system types.
+     *
+     * <table>
+     *     <tr>
+     *         <th>Operating System</th>
+     *         <th>Aliases</th>
+     *     </tr>
+     *     <tr>
+     *         <td>Windows</td>
+     *         <td>"windows"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>GNU/Linux</td>
+     *         <td>"linux"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Mac OS X</td>
+     *         <td>"osx", "mac os x", "darwin"</td>
+     *     </tr>
+     *     <tr>
+     *         <td>Solaris</td>
+     *         <td>"solaris", "sunos"</td>
+     *     </tr>
+     * </table>
+     */
+    void operatingSystem(String name);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/OperatingSystem.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/OperatingSystem.java
new file mode 100644
index 0000000..2626743
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/OperatingSystem.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.nativeplatform.platform;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Named;
+
+/**
+ * A machine operating system.
+ */
+ at Incubating
+public interface OperatingSystem extends Named {
+    /**
+     * Returns a human-consumable display name for this operating system.
+     */
+    String getDisplayName();
+
+    /**
+     * Is this the current OS?
+     */
+    boolean isCurrent();
+
+    /**
+     * Is it Windows?
+     */
+    boolean isWindows();
+
+    /**
+     * Is it Mac OS X?
+     */
+    boolean isMacOsX();
+
+    /**
+     * Is it Linux?
+     */
+    boolean isLinux();
+
+    /**
+     * Is it Solaris?
+     */
+    boolean isSolaris();
+
+    /**
+     * Is it FreeBSD?
+     */
+    boolean isFreeBSD();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/ArchitectureInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/ArchitectureInternal.java
new file mode 100644
index 0000000..89ccbe3
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/ArchitectureInternal.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.nativeplatform.platform.internal;
+
+import org.gradle.nativeplatform.platform.Architecture;
+
+public interface ArchitectureInternal extends Architecture {
+    enum InstructionSet { X86, ITANIUM, PPC, SPARC, ARM }
+
+    boolean isI386();
+
+    boolean isAmd64();
+
+    boolean isIa64();
+
+    boolean isArm();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/Architectures.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/Architectures.java
new file mode 100644
index 0000000..555d4d7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/Architectures.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.nativeplatform.platform.internal;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+public class Architectures {
+
+    public static final KnownArchitecture X86 = new KnownArchitecture("x86", "i386", "ia-32", "i686");
+    public static final KnownArchitecture X86_64 = new KnownArchitecture("x86-64", "x86_64", "amd64", "x64");
+    public static final KnownArchitecture IA_64 = new KnownArchitecture("ia-64", "ia64");
+    public static final KnownArchitecture ARM_V7 = new KnownArchitecture("arm-v7", "armv7", "arm", "arm32");
+
+    private static final List<KnownArchitecture> KNOWN_ARCHITECTURES = asList(
+            X86,
+            X86_64,
+            IA_64,
+            ARM_V7,
+            new KnownArchitecture("arm-v8", "arm64"),
+            new KnownArchitecture("ppc"),
+            new KnownArchitecture("ppc64"),
+            new KnownArchitecture("sparc-v8", "sparc", "sparc32"),
+            new KnownArchitecture("sparc-v9", "sparc64", "ultrasparc")
+    );
+
+    public static ArchitectureInternal forInput(String input) {
+        for (KnownArchitecture knownArchitecture : KNOWN_ARCHITECTURES) {
+            if (knownArchitecture.isAlias(input.toLowerCase())) {
+                return new DefaultArchitecture(knownArchitecture.getCanonicalName());
+            }
+        }
+        return new DefaultArchitecture(input);
+    }
+
+    public static class KnownArchitecture {
+        private final String canonicalName;
+        private final List<String> aliases;
+
+        public KnownArchitecture(String canonicalName, String... aliases) {
+            this.canonicalName = canonicalName;
+            this.aliases = Arrays.asList(aliases);
+        }
+
+        public String getCanonicalName() {
+            return canonicalName;
+        }
+
+        public boolean isAlias(String input) {
+            return canonicalName.equals(input) || aliases.contains(input);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitecture.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitecture.java
new file mode 100644
index 0000000..db6c187
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitecture.java
@@ -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.nativeplatform.platform.internal;
+
+public class DefaultArchitecture implements ArchitectureInternal {
+    private final String name;
+
+    public DefaultArchitecture(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getDisplayName() {
+        return String.format("architecture '%s'", name);
+    }
+
+    public boolean isI386() {
+        return Architectures.X86.isAlias(name);
+    }
+
+    public boolean isAmd64() {
+        return Architectures.X86_64.isAlias(name);
+    }
+
+    public boolean isIa64() {
+        return Architectures.IA_64.isAlias(name);
+    }
+
+    public boolean isArm() {
+        return Architectures.ARM_V7.isAlias(name);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultArchitecture other = (DefaultArchitecture) o;
+        return name.equals(other.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatform.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatform.java
new file mode 100644
index 0000000..903103b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatform.java
@@ -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.nativeplatform.platform.internal;
+
+import net.rubygrapefruit.platform.SystemInfo;
+import org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException;
+import org.gradle.internal.nativeintegration.services.NativeServices;
+import org.gradle.internal.os.OperatingSystem;
+
+public class DefaultNativePlatform implements NativePlatformInternal {
+    private final String name;
+    private ArchitectureInternal architecture;
+    private OperatingSystemInternal operatingSystem;
+
+    public DefaultNativePlatform(String name) {
+        this(name, getCurrentOperatingSystem(), getCurrentArchitecture());
+    }
+
+    protected DefaultNativePlatform(String name, OperatingSystemInternal operatingSystem, ArchitectureInternal architecture) {
+        this.name = name;
+        this.architecture = architecture;
+        this.operatingSystem = operatingSystem;
+    }
+
+    private static DefaultOperatingSystem getCurrentOperatingSystem() {
+        return new DefaultOperatingSystem(System.getProperty("os.name"), OperatingSystem.current());
+    }
+
+    public static ArchitectureInternal getCurrentArchitecture() {
+        String architectureName;
+        try {
+            architectureName = NativeServices.getInstance().get(SystemInfo.class).getArchitectureName();
+        } catch (NativeIntegrationUnavailableException e) {
+            architectureName = System.getProperty("os.arch");
+        }
+        return Architectures.forInput(architectureName);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getDisplayName() {
+        return String.format("platform '%s'", name);
+    }
+
+    public ArchitectureInternal getArchitecture() {
+        return architecture;
+    }
+
+    public void architecture(String name) {
+        architecture = Architectures.forInput(name);
+    }
+
+    public OperatingSystemInternal getOperatingSystem() {
+        return operatingSystem;
+    }
+
+    public void operatingSystem(String name) {
+        operatingSystem = new DefaultOperatingSystem(name);
+    }
+
+    public String getCompatibilityString() {
+        return String.format("%s:%s", getArchitecture().getName(), getOperatingSystem().getName());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystem.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystem.java
new file mode 100644
index 0000000..9aebe48
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystem.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.platform.internal;
+
+import org.gradle.internal.os.OperatingSystem;
+
+public class DefaultOperatingSystem implements OperatingSystemInternal {
+    private static final OperatingSystem CURRENT_OS = OperatingSystem.current();
+
+    private final String name;
+    private final OperatingSystem internalOs;
+
+    public DefaultOperatingSystem(String name) {
+        this(name, OperatingSystem.forName(name));
+    }
+
+    public DefaultOperatingSystem(String name, OperatingSystem internalOs) {
+        this.name = name;
+        this.internalOs = internalOs;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDisplayName() {
+        return String.format("operating system '%s'", name);
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public OperatingSystem getInternalOs() {
+        return internalOs;
+    }
+
+    public boolean isCurrent() {
+        return internalOs == CURRENT_OS;
+    }
+
+    public boolean isWindows() {
+        return internalOs.isWindows();
+    }
+
+    public boolean isLinux() {
+        return internalOs.isLinux();
+    }
+
+    public boolean isMacOsX() {
+        return internalOs.isMacOsX();
+    }
+
+    public boolean isSolaris() {
+        return internalOs == OperatingSystem.SOLARIS;
+    }
+
+    public boolean isFreeBSD() {
+        return internalOs == OperatingSystem.FREE_BSD;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultOperatingSystem that = (DefaultOperatingSystem) o;
+        return name.equals(that.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatformInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatformInternal.java
new file mode 100644
index 0000000..3c93ce9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatformInternal.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.nativeplatform.platform.internal;
+
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+public interface NativePlatformInternal extends NativePlatform {
+    ArchitectureInternal getArchitecture();
+
+    OperatingSystemInternal getOperatingSystem();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatforms.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatforms.java
new file mode 100644
index 0000000..7ec3e19
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/NativePlatforms.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform.internal;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class NativePlatforms {
+
+    private static final String OS_WINDOWS = "windows";
+    private static final String OS_LINUX = "linux";
+    private static final String OS_OSX = "osx";
+    private static final String OS_UNIX = "unix";
+    private static final String ARCH_X86 = "x86";
+
+    // TODO:DAZ This config should be used via platforms.create {}
+    public static Set<DefaultNativePlatform> defaultPlatformDefinitions() {
+        Set<DefaultNativePlatform> platforms = new LinkedHashSet<DefaultNativePlatform>();
+
+        OperatingSystemInternal windows = new DefaultOperatingSystem(OS_WINDOWS);
+        OperatingSystemInternal linux = new DefaultOperatingSystem(OS_LINUX);
+        OperatingSystemInternal osx = new DefaultOperatingSystem(OS_OSX);
+        OperatingSystemInternal unix = new DefaultOperatingSystem(OS_UNIX);
+        OperatingSystemInternal freebsd = new DefaultOperatingSystem("freebsd");
+        OperatingSystemInternal solaris = new DefaultOperatingSystem("solaris");
+
+        ArchitectureInternal x86 = Architectures.forInput(ARCH_X86);
+        ArchitectureInternal x64 = Architectures.forInput("x86_64");
+        ArchitectureInternal ia64 = Architectures.forInput("ia64");
+        ArchitectureInternal armv7 = Architectures.forInput("armv7");
+        ArchitectureInternal armv8 = Architectures.forInput("armv8");
+        ArchitectureInternal sparc = Architectures.forInput("sparc");
+        ArchitectureInternal ultrasparc = Architectures.forInput("ultrasparc");
+        ArchitectureInternal ppc = Architectures.forInput("ppc");
+        ArchitectureInternal ppc64 = Architectures.forInput("ppc64");
+
+        platforms.add(createPlatform(windows, x86));
+        platforms.add(createPlatform(windows, x64));
+        platforms.add(createPlatform(windows, armv7));
+        platforms.add(createPlatform(windows, ia64));
+
+        platforms.add(createPlatform(freebsd, x86));
+        platforms.add(createPlatform(freebsd, x64));
+        platforms.add(createPlatform(freebsd, armv7));
+        platforms.add(createPlatform(freebsd, armv8));
+        platforms.add(createPlatform(freebsd, ppc));
+        platforms.add(createPlatform(freebsd, ppc64));
+
+        platforms.add(createPlatform(unix, x86));
+        platforms.add(createPlatform(unix, x64));
+        platforms.add(createPlatform(unix, armv7));
+        platforms.add(createPlatform(unix, armv8));
+        platforms.add(createPlatform(unix, ppc));
+        platforms.add(createPlatform(unix, ppc64));
+
+        platforms.add(createPlatform(linux, x64));
+        platforms.add(createPlatform(linux, x86));
+        platforms.add(createPlatform(linux, armv7));
+        platforms.add(createPlatform(linux, armv8));
+
+        platforms.add(createPlatform(osx, x86));
+        platforms.add(createPlatform(osx, x64));
+
+        platforms.add(createPlatform(solaris, x64));
+        platforms.add(createPlatform(solaris, x86));
+        platforms.add(createPlatform(solaris, sparc));
+        platforms.add(createPlatform(solaris, ultrasparc));
+
+        return platforms;
+    }
+
+    private static DefaultNativePlatform createPlatform(OperatingSystemInternal os, ArchitectureInternal arch) {
+        return new DefaultNativePlatform(platformName(os.getName(), arch.getName()), os, arch);
+    }
+
+    private static String platformName(String os, String arch) {
+        return String.format("%s_%s", os, arch);
+    }
+
+    public static String getDefaultPlatformName() {
+        NativePlatformInternal defaultPlatform = new DefaultNativePlatform("default");
+        OperatingSystemInternal os = defaultPlatform.getOperatingSystem();
+        ArchitectureInternal architecture = defaultPlatform.getArchitecture();
+
+        // TODO:DAZ This is a very limited implementation of defaults, just to get the build passing
+        if (os.isWindows()) {
+            // Always use x86 as default on windows
+            return platformName(OS_WINDOWS, ARCH_X86);
+        }
+        if (os.isLinux()) {
+            return platformName(OS_LINUX, architecture.getName());
+        }
+        if (os.isMacOsX()) {
+            return platformName(OS_OSX, architecture.getName());
+        }
+        return platformName(OS_UNIX, ARCH_X86);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/OperatingSystemInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/OperatingSystemInternal.java
new file mode 100644
index 0000000..5b4ad11
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/internal/OperatingSystemInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform.internal;
+
+import org.gradle.nativeplatform.platform.OperatingSystem;
+
+public interface OperatingSystemInternal extends OperatingSystem {
+    org.gradle.internal.os.OperatingSystem getInternalOs();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/package-info.java
new file mode 100644
index 0000000..5f44a5c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/platform/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 defining a native binary platform.
+ */
+package org.gradle.nativeplatform.platform;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPlugin.java
new file mode 100644
index 0000000..855f2fb
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPlugin.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.plugins;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.*;
+import org.gradle.api.artifacts.repositories.ArtifactRepository;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.plugins.ExtensionContainer;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.internal.Actions;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.internal.SourceTransformTaskConfig;
+import org.gradle.language.base.internal.registry.LanguageTransform;
+import org.gradle.language.base.internal.registry.LanguageTransformContainer;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet;
+import org.gradle.model.*;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.nativeplatform.*;
+import org.gradle.nativeplatform.internal.*;
+import org.gradle.nativeplatform.internal.configure.*;
+import org.gradle.nativeplatform.internal.pch.DefaultPreCompiledHeaderTransformContainer;
+import org.gradle.nativeplatform.internal.pch.PreCompiledHeaderTransformContainer;
+import org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltLibraries;
+import org.gradle.nativeplatform.internal.prebuilt.PrebuiltLibraryInitializer;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform;
+import org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask;
+import org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal;
+import org.gradle.platform.base.*;
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.DefaultBinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.PlatformResolvers;
+
+import javax.inject.Inject;
+import java.io.File;
+
+/**
+ * A plugin that sets up the infrastructure for defining native binaries.
+ */
+ at Incubating
+public class NativeComponentModelPlugin implements Plugin<ProjectInternal> {
+    private final Instantiator instantiator;
+
+    @Inject
+    public NativeComponentModelPlugin(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    public void apply(final ProjectInternal project) {
+        project.getPluginManager().apply(ComponentModelBasePlugin.class);
+
+        project.getExtensions().create("buildTypes", DefaultBuildTypeContainer.class, instantiator);
+        project.getExtensions().create("flavors", DefaultFlavorContainer.class, instantiator);
+        project.getExtensions().create("toolChains", DefaultNativeToolChainRegistry.class, instantiator);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+
+        @ComponentType
+        void nativeExecutable(ComponentTypeBuilder<NativeExecutableSpec> builder) {
+            builder.defaultImplementation(DefaultNativeExecutableSpec.class);
+        }
+
+        @ComponentType
+        void nativeLibrary(ComponentTypeBuilder<NativeLibrarySpec> builder) {
+            builder.defaultImplementation(DefaultNativeLibrarySpec.class);
+        }
+
+        @Model
+        Repositories repositories(ServiceRegistry serviceRegistry, FlavorContainer flavors, PlatformContainer platforms, BuildTypeContainer buildTypes) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            Action<PrebuiltLibrary> initializer = new PrebuiltLibraryInitializer(instantiator, platforms.withType(NativePlatform.class), buildTypes, flavors);
+            return new DefaultRepositories(instantiator, fileResolver, initializer);
+        }
+
+        @Model
+        NativeToolChainRegistryInternal toolChains(ExtensionContainer extensionContainer) {
+            return extensionContainer.getByType(NativeToolChainRegistryInternal.class);
+        }
+
+        @Model
+        BuildTypeContainer buildTypes(ExtensionContainer extensionContainer) {
+            return extensionContainer.getByType(BuildTypeContainer.class);
+        }
+
+        @Model
+        FlavorContainer flavors(ExtensionContainer extensionContainer) {
+            return extensionContainer.getByType(FlavorContainer.class);
+        }
+
+        @Model
+        NamedDomainObjectSet<NativeComponentSpec> nativeComponents(ComponentSpecContainer components) {
+            return components.withType(NativeComponentSpec.class);
+        }
+
+        @Model
+        PreCompiledHeaderTransformContainer preCompiledHeaderTransformContainer(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(Instantiator.class).newInstance(DefaultPreCompiledHeaderTransformContainer.class);
+        }
+
+        @Mutate
+        public void registerNativePlatformResolver(PlatformResolvers resolvers) {
+            resolvers.register(new NativePlatformResolver());
+        }
+
+        @Defaults
+        public void registerFactoryForCustomNativePlatforms(PlatformContainer platforms, ServiceRegistry serviceRegistry) {
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            NamedDomainObjectFactory<NativePlatform> nativePlatformFactory = new NamedDomainObjectFactory<NativePlatform>() {
+                public NativePlatform create(String name) {
+                    return instantiator.newInstance(DefaultNativePlatform.class, name);
+                }
+            };
+
+            platforms.registerFactory(NativePlatform.class, nativePlatformFactory);
+
+            // TODO:DAZ This is only here for backward compatibility: platforms should be typed on creation, I think.
+            platforms.registerFactory(Platform.class, nativePlatformFactory);
+        }
+
+        // TODO:DAZ Migrate to @BinaryType and @ComponentBinaries
+        @Mutate
+        public void createNativeBinaries(BinaryContainer binaries, NamedDomainObjectSet<NativeComponentSpec> nativeComponents,
+                                         LanguageTransformContainer languageTransforms, NativeToolChainRegistryInternal toolChains,
+                                         PlatformResolvers platforms, BuildTypeContainer buildTypes, FlavorContainer flavors,
+                                         ServiceRegistry serviceRegistry, @Path("buildDir") File buildDir, ITaskFactory taskFactory) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            NativeDependencyResolver resolver = serviceRegistry.get(NativeDependencyResolver.class);
+            Action<NativeBinarySpec> configureBinaryAction = new NativeBinarySpecInitializer(buildDir);
+            Action<NativeBinarySpec> setToolsAction = new ToolSettingNativeBinaryInitializer(languageTransforms);
+            @SuppressWarnings("unchecked") Action<NativeBinarySpec> initAction = Actions.composite(configureBinaryAction, setToolsAction);
+            NativeBinariesFactory factory = new DefaultNativeBinariesFactory(instantiator, initAction, resolver, taskFactory);
+            BinaryNamingSchemeBuilder namingSchemeBuilder = new DefaultBinaryNamingSchemeBuilder();
+            Action<NativeComponentSpec> createBinariesAction =
+                    new NativeComponentSpecInitializer(factory, namingSchemeBuilder, toolChains, platforms, buildTypes, flavors);
+
+            for (NativeComponentSpec component : nativeComponents) {
+                createBinariesAction.execute(component);
+                binaries.addAll(component.getBinaries());
+            }
+        }
+
+        @Finalize
+        public void createDefaultToolChain(NativeToolChainRegistryInternal toolChains) {
+            if (toolChains.isEmpty()) {
+                toolChains.addDefaultToolChains();
+            }
+        }
+
+        @Finalize
+        public void createDefaultBuildTypes(BuildTypeContainer buildTypes) {
+            if (buildTypes.isEmpty()) {
+                buildTypes.create("debug");
+            }
+        }
+
+        @Finalize
+        public void createDefaultFlavor(FlavorContainer flavors) {
+            if (flavors.isEmpty()) {
+                flavors.create(DefaultFlavor.DEFAULT);
+            }
+        }
+
+        @Mutate
+        void configureGeneratedSourceSets(CollectionBuilder<ComponentSpec> componentSpecs) {
+            componentSpecs.afterEach(new Action<ComponentSpec>() {
+                @Override
+                public void execute(ComponentSpec componentSpec) {
+                    for (LanguageSourceSetInternal languageSourceSet : componentSpec.getSource().withType(LanguageSourceSetInternal.class)) {
+                        Task generatorTask = languageSourceSet.getGeneratorTask();
+                        if (generatorTask != null) {
+                            languageSourceSet.builtBy(generatorTask);
+                            maybeSetSourceDir(languageSourceSet.getSource(), generatorTask, "sourceDir");
+                            if (languageSourceSet instanceof HeaderExportingSourceSet) {
+                                maybeSetSourceDir(((HeaderExportingSourceSet) languageSourceSet).getExportedHeaders(), generatorTask, "headerDir");
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        @Mutate
+        void configurePrefixHeaderFiles(CollectionBuilder<ComponentSpec> componentSpecs, final @Path("buildDir") File buildDir) {
+            componentSpecs.afterEach(new Action<ComponentSpec>() {
+                @Override
+                public void execute(ComponentSpec componentSpec) {
+                    for (DependentSourceSet dependentSourceSet : componentSpec.getSource().withType(DependentSourceSet.class)) {
+                        if (CollectionUtils.isNotEmpty(dependentSourceSet.getPreCompiledHeaders())) {
+                            String prefixHeaderDirName = String.format("tmp/%s/prefixHeaders", dependentSourceSet.getName());
+                            File prefixHeaderDir = new File(buildDir, prefixHeaderDirName);
+                            final File prefixHeaderFile = new File(prefixHeaderDir, "prefix-headers.h");
+                            dependentSourceSet.setPrefixHeaderFile(prefixHeaderFile);
+                        }
+                    }
+                }
+            });
+        }
+
+        @Mutate
+        void configurePrefixHeaderGenerationTasks(final TaskContainer tasks, NamedDomainObjectSet<NativeComponentSpec> nativeComponents) {
+            for (NativeComponentSpec nativeComponentSpec : nativeComponents) {
+                nativeComponentSpec.getSource().withType(DependentSourceSet.class, new Action<DependentSourceSet>() {
+                    @Override
+                    public void execute(final DependentSourceSet dependentSourceSet) {
+                        if (dependentSourceSet.getPrefixHeaderFile() !=  null) {
+                            String taskName = String.format("generate%sPrefixHeaderFile", StringUtils.capitalize(dependentSourceSet.getName()));
+                            tasks.create(taskName, PrefixHeaderFileGenerateTask.class, new Action<PrefixHeaderFileGenerateTask>() {
+                                @Override
+                                public void execute(PrefixHeaderFileGenerateTask prefixHeaderFileGenerateTask) {
+                                    prefixHeaderFileGenerateTask.setPrefixHeaderFile(dependentSourceSet.getPrefixHeaderFile());
+                                    prefixHeaderFileGenerateTask.setHeaders(dependentSourceSet.getPreCompiledHeaders());
+                                }
+                            });
+                        }
+                    }
+                });
+            }
+        }
+
+        @Mutate
+        void configurePreCompiledHeaderCompileTasks(CollectionBuilder<NativeBinarySpecInternal> binaries, final ServiceRegistry serviceRegistry, final PreCompiledHeaderTransformContainer pchTransformContainer, final @Path("buildDir") File buildDir) {
+            binaries.all(new Action<NativeBinarySpecInternal>() {
+                @Override
+                public void execute(final NativeBinarySpecInternal nativeBinarySpec) {
+                    for (final LanguageTransform<?, ?> transform : pchTransformContainer) {
+                        nativeBinarySpec.getSource().withType(transform.getSourceSetType(), new Action<LanguageSourceSet>() {
+                            @Override
+                            public void execute(final LanguageSourceSet languageSourceSet) {
+                                final DependentSourceSet dependentSourceSet = (DependentSourceSet) languageSourceSet;
+                                if (CollectionUtils.isNotEmpty(dependentSourceSet.getPreCompiledHeaders())) {
+                                    final SourceTransformTaskConfig taskConfig = transform.getTransformTask();
+                                    String pchTaskName = String.format("%s%s%sPreCompiledHeader", taskConfig.getTaskPrefix(), StringUtils.capitalize(nativeBinarySpec.getName()), StringUtils.capitalize(dependentSourceSet.getName()));
+                                    nativeBinarySpec.getTasks().create(pchTaskName, taskConfig.getTaskType(), new Action<DefaultTask>() {
+                                        @Override
+                                        public void execute(DefaultTask task) {
+                                            taskConfig.configureTask(task, nativeBinarySpec, dependentSourceSet);
+                                        }
+                                    });
+                                }
+                            }
+                        });
+                    }
+                }
+            });
+        }
+
+        @Mutate
+        public void applyHeaderSourceSetConventions(CollectionBuilder<ComponentSpec> componentSpecs) {
+            componentSpecs.afterEach(new Action<ComponentSpec>() {
+                @Override
+                public void execute(ComponentSpec componentSpec) {
+                    DomainObjectSet<LanguageSourceSet> functionalSourceSet = componentSpec.getSource();
+                    for (HeaderExportingSourceSet headerSourceSet : functionalSourceSet.withType(HeaderExportingSourceSet.class)) {
+                        // Only apply default locations when none explicitly configured
+                        if (headerSourceSet.getExportedHeaders().getSrcDirs().isEmpty()) {
+                            headerSourceSet.getExportedHeaders().srcDir(String.format("src/%s/headers", componentSpec.getName()));
+                        }
+
+                        headerSourceSet.getImplicitHeaders().setSrcDirs(headerSourceSet.getSource().getSrcDirs());
+                        headerSourceSet.getImplicitHeaders().include("**/*.h");
+                    }
+                }
+            });
+        }
+
+        private void maybeSetSourceDir(SourceDirectorySet sourceSet, Task task, String propertyName) {
+            Object value = task.property(propertyName);
+            if (value != null) {
+                sourceSet.srcDir(value);
+            }
+        }
+    }
+
+    private static class DefaultRepositories extends DefaultPolymorphicDomainObjectContainer<ArtifactRepository> implements Repositories {
+        private DefaultRepositories(final Instantiator instantiator, final FileResolver fileResolver, final Action<PrebuiltLibrary> binaryFactory) {
+            super(ArtifactRepository.class, instantiator, new ArtifactRepositoryNamer());
+            registerFactory(PrebuiltLibraries.class, new NamedDomainObjectFactory<PrebuiltLibraries>() {
+                public PrebuiltLibraries create(String name) {
+                    return instantiator.newInstance(DefaultPrebuiltLibraries.class, name, instantiator, fileResolver, binaryFactory);
+                }
+            });
+        }
+    }
+
+    private static class ArtifactRepositoryNamer implements Namer<ArtifactRepository> {
+        public String determineName(ArtifactRepository object) {
+            return object.getName();
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentPlugin.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentPlugin.groovy
new file mode 100644
index 0000000..1d2d572
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/NativeComponentPlugin.groovy
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.plugins
+
+import org.gradle.api.Incubating
+import org.gradle.api.Plugin
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.NativeExecutableBinarySpec
+import org.gradle.nativeplatform.SharedLibraryBinarySpec
+import org.gradle.nativeplatform.StaticLibraryBinarySpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.nativeplatform.tasks.CreateStaticLibrary
+import org.gradle.nativeplatform.tasks.InstallExecutable
+import org.gradle.nativeplatform.tasks.LinkExecutable
+import org.gradle.nativeplatform.tasks.LinkSharedLibrary
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec
+import org.gradle.nativeplatform.toolchain.internal.plugins.StandardToolChainsPlugin
+import org.gradle.platform.base.BinaryContainer
+
+/**
+ * A plugin that creates tasks used for constructing native binaries.
+ */
+ at Incubating
+public class NativeComponentPlugin implements Plugin<ProjectInternal> {
+
+    public void apply(final ProjectInternal project) {
+        project.pluginManager.apply(NativeComponentModelPlugin.class);
+        project.pluginManager.apply(StandardToolChainsPlugin)
+
+        createTasks(project.tasks, project.binaries)
+    }
+
+    // TODO:DAZ Convert to a model rule and use simple iteration - this breaks non-rule code that uses binary.tasks.link
+    static void createTasks(TaskContainer tasks, BinaryContainer binaries) {
+        binaries.withType(NativeBinarySpec) { NativeBinarySpecInternal binary ->
+            createTasksForBinary(tasks, binary)
+        }
+    }
+
+    private static void createTasksForBinary(TaskContainer tasks, NativeBinarySpecInternal binary) {
+        def builderTask
+        if (binary instanceof NativeExecutableBinarySpec || binary instanceof NativeTestSuiteBinarySpec) {
+            builderTask = createLinkExecutableTask(tasks, binary)
+            binary.tasks.add createInstallTask(tasks, binary);
+        } else if (binary instanceof SharedLibraryBinarySpec) {
+            builderTask = createLinkSharedLibraryTask(tasks, binary)
+        } else if (binary instanceof StaticLibraryBinarySpec) {
+            builderTask = createStaticLibraryTask(tasks, binary)
+        } else {
+            throw new RuntimeException("Not a valid binary type for building: " + binary)
+        }
+        binary.tasks.add builderTask
+        binary.builtBy builderTask
+    }
+
+    private static LinkExecutable createLinkExecutableTask(TaskContainer tasks, def executable) {
+        def binary = executable as NativeBinarySpecInternal
+        LinkExecutable linkTask = tasks.create(binary.namingScheme.getTaskName("link"), LinkExecutable)
+        linkTask.description = "Links ${executable}"
+
+        linkTask.toolChain = binary.toolChain
+        linkTask.targetPlatform = executable.targetPlatform
+
+        linkTask.lib { binary.libs*.linkFiles }
+
+        linkTask.conventionMapping.outputFile = { executable.executableFile }
+        linkTask.linkerArgs = binary.linker.args
+        return linkTask
+    }
+
+    private static LinkSharedLibrary createLinkSharedLibraryTask(TaskContainer tasks, SharedLibraryBinarySpec sharedLibrary) {
+        def binary = sharedLibrary as NativeBinarySpecInternal
+        LinkSharedLibrary linkTask = tasks.create(binary.namingScheme.getTaskName("link"), LinkSharedLibrary)
+        linkTask.description = "Links ${sharedLibrary}"
+
+        linkTask.toolChain = binary.toolChain
+        linkTask.targetPlatform = binary.targetPlatform
+
+        linkTask.lib { binary.libs*.linkFiles }
+
+        linkTask.conventionMapping.outputFile = { sharedLibrary.sharedLibraryFile }
+        linkTask.conventionMapping.installName = { sharedLibrary.sharedLibraryFile.name }
+        linkTask.linkerArgs = binary.linker.args
+        return linkTask
+    }
+
+    private static CreateStaticLibrary createStaticLibraryTask(TaskContainer tasks, StaticLibraryBinarySpec staticLibrary) {
+        def binary = staticLibrary as NativeBinarySpecInternal
+        CreateStaticLibrary task = tasks.create(binary.namingScheme.getTaskName("create"), CreateStaticLibrary)
+        task.description = "Creates ${staticLibrary}"
+
+        task.toolChain = binary.toolChain
+        task.targetPlatform = staticLibrary.targetPlatform
+        task.conventionMapping.outputFile = { staticLibrary.staticLibraryFile }
+        task.staticLibArgs = binary.staticLibArchiver.args
+        return task
+    }
+
+    private static createInstallTask(TaskContainer tasks, def executable) {
+        def binary = executable as NativeBinarySpecInternal
+        InstallExecutable installTask = tasks.create(binary.namingScheme.getTaskName("install"), InstallExecutable)
+        installTask.description = "Installs a development image of $executable"
+        installTask.group = LifecycleBasePlugin.BUILD_GROUP
+
+        installTask.toolChain = binary.toolChain
+
+        def project = installTask.project
+        installTask.conventionMapping.destinationDir = { project.file("${project.buildDir}/install/${binary.namingScheme.outputDirectoryBase}") }
+
+        installTask.conventionMapping.executable = { executable.executableFile }
+        installTask.lib { binary.libs*.runtimeFiles }
+
+        installTask.dependsOn(executable)
+        return installTask
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/plugins/package-info.java
new file mode 100644
index 0000000..8b03e66
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform.plugins;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/AbstractLinkTask.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/AbstractLinkTask.groovy
new file mode 100644
index 0000000..ee1b39c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/AbstractLinkTask.groovy
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.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.internal.operations.logging.BuildOperationLoggerFactory
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner
+import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator
+import org.gradle.nativeplatform.internal.LinkerSpec
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+
+import javax.inject.Inject
+
+ at Incubating
+abstract class AbstractLinkTask extends DefaultTask implements ObjectFilesToBinary {
+    @Inject
+    AbstractLinkTask() {
+        libs = project.files()
+        source = project.files()
+        inputs.property("outputType") {
+            NativeToolChainInternal.Identifier.identify((NativeToolChainInternal) toolChain, (NativePlatformInternal) targetPlatform)
+        }
+    }
+
+    /**
+     * The tool chain used for linking.
+     */
+    NativeToolChain toolChain
+    NativePlatform targetPlatform
+
+    // 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
+    FileCollection 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
+    }
+
+    @Inject
+    public BuildOperationLoggerFactory getOperationLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    void link() {
+        def cleaner = new SimpleStaleClassCleaner(getOutputs())
+        cleaner.setDestinationDir(getDestinationDir())
+        cleaner.execute()
+
+        if (source.empty) {
+            didWork = false
+            return
+        }
+
+        def spec = createLinkerSpec()
+        spec.targetPlatform = getTargetPlatform()
+        spec.tempDir = getTemporaryDir()
+        spec.outputFile = getOutputFile()
+
+        spec.objectFiles getSource()
+        spec.libraries getLibs()
+        spec.args getLinkerArgs()
+
+        def operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir())
+        spec.operationLogger = operationLogger
+
+        def result = BuildOperationLoggingCompilerDecorator.wrap(toolChain.select(targetPlatform).newCompiler(spec.getClass())).execute(spec)
+        didWork = result.didWork
+    }
+
+    protected abstract LinkerSpec createLinkerSpec();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/CreateStaticLibrary.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/CreateStaticLibrary.groovy
new file mode 100644
index 0000000..9247428
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/CreateStaticLibrary.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.nativeplatform.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.internal.operations.logging.BuildOperationLoggerFactory
+import org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator
+import org.gradle.nativeplatform.internal.DefaultStaticLibraryArchiverSpec
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+
+import javax.inject.Inject
+
+/**
+ * Assembles a static library from object files.
+ */
+ at Incubating
+ at ParallelizableTask
+class CreateStaticLibrary extends DefaultTask implements ObjectFilesToBinary {
+    private FileCollection source
+
+    @Inject
+    CreateStaticLibrary() {
+        source = project.files()
+        inputs.property("outputType") {
+            NativeToolChainInternal.Identifier.identify((NativeToolChainInternal) toolChain, (NativePlatformInternal) targetPlatform)
+        }
+    }
+
+    /**
+     * The tool chain used for creating the static library.
+     */
+    NativeToolChain toolChain
+
+    /**
+     * The platform being targeted.
+     */
+    NativePlatform targetPlatform
+
+    /**
+     * 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
+
+    @Inject
+    public BuildOperationLoggerFactory getOperationLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @TaskAction
+    void link() {
+
+        def spec = new DefaultStaticLibraryArchiverSpec()
+        spec.tempDir = getTemporaryDir()
+        spec.outputFile = getOutputFile()
+        spec.objectFiles getSource()
+        spec.args getStaticLibArgs()
+
+        def operationLogger = getOperationLoggerFactory().newOperationLogger(getName(), getTemporaryDir())
+        spec.operationLogger = operationLogger
+
+        def result = BuildOperationLoggingCompilerDecorator.wrap(toolChain.select(targetPlatform).newCompiler(spec.getClass())).execute(spec)
+        didWork = result.didWork
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/InstallExecutable.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/InstallExecutable.groovy
new file mode 100644
index 0000000..3de9600
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/InstallExecutable.groovy
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.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.ParallelizableTask
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.Gcc
+
+import javax.inject.Inject
+/**
+ * Installs an executable with it's dependent libraries so it can be easily executed.
+ */
+ at Incubating
+ at ParallelizableTask
+public class InstallExecutable extends DefaultTask {
+
+    @Inject
+    InstallExecutable() {
+        this.libs = project.files()
+    }
+
+    @Inject
+    Instantiator getInstantiator() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Inject
+    FileOperations getFileOperations() {
+        throw new UnsupportedOperationException()
+    }
+
+    /**
+     * The tool chain used for linking.
+     */
+    NativeToolChain 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
+    }
+
+    /**
+     * Returns the script file that can be used to run the install image.
+     */
+    File getRunScript() {
+        new File(getDestinationDir(), os.getScriptName(getExecutable().name))
+    }
+
+    @Inject
+    OperatingSystem getOs() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Inject
+    FileSystem getFileSystem() {
+        throw new UnsupportedOperationException()
+    }
+
+    @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 pathEntry : ((Gcc) toolChain).path) {
+                toolChainPath.append(pathEntry.absolutePath).append(";")
+            }
+            toolChainPath.append("%PATH%")
+        }
+
+        runScript.text = """
+ at echo off
+SETLOCAL
+$toolChainPath
+CALL "%~dp0lib\\${executable.name}" %*
+EXIT /B %ERRORLEVEL%
+
+ENDLOCAL
+"""
+    }
+
+    private void installUnix() {
+        final destination = getDestinationDir()
+        final executable = getExecutable()
+
+        installToDir(new File(destination, "lib"))
+
+        runScript.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.chmod(runScript, 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/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkExecutable.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkExecutable.groovy
new file mode 100644
index 0000000..88c670d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkExecutable.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.nativeplatform.tasks
+import org.gradle.api.Incubating
+import org.gradle.api.tasks.ParallelizableTask
+import org.gradle.nativeplatform.internal.DefaultLinkerSpec
+import org.gradle.nativeplatform.internal.LinkerSpec
+/**
+ * Links a binary executable from object files and libraries.
+ */
+ at Incubating
+ at ParallelizableTask
+class LinkExecutable extends AbstractLinkTask {
+
+    @Override
+    protected LinkerSpec createLinkerSpec() {
+        return new DefaultLinkerSpec()
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkSharedLibrary.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkSharedLibrary.groovy
new file mode 100644
index 0000000..2518cdc
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/LinkSharedLibrary.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.nativeplatform.tasks
+
+import org.gradle.api.Incubating
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.ParallelizableTask
+import org.gradle.nativeplatform.internal.DefaultLinkerSpec
+import org.gradle.nativeplatform.internal.LinkerSpec
+import org.gradle.nativeplatform.internal.SharedLibraryLinkerSpec
+
+/**
+ * Links a binary shared library from object files and imported libraries.
+ */
+ at Incubating
+ at ParallelizableTask
+class LinkSharedLibrary extends AbstractLinkTask {
+    @Input @Optional
+    String installName;
+
+    @Override
+    protected LinkerSpec createLinkerSpec() {
+        final spec = new Spec()
+        spec.installName = getInstallName()
+        return spec
+    }
+
+    private static class Spec extends DefaultLinkerSpec implements SharedLibraryLinkerSpec {
+        String installName;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/ObjectFilesToBinary.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/ObjectFilesToBinary.java
new file mode 100644
index 0000000..ebf3170
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/ObjectFilesToBinary.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.nativeplatform.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+
+/**
+ * A task that combines a set of object files into a single binary.
+ */
+ at Incubating
+public interface ObjectFilesToBinary extends Task {
+    /**
+     * 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/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/PrefixHeaderFileGenerateTask.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/PrefixHeaderFileGenerateTask.java
new file mode 100644
index 0000000..905a73b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/PrefixHeaderFileGenerateTask.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.tasks;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.nativeplatform.toolchain.internal.PrefixHeaderFileGeneratorUtil;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * Generates a prefix header file from a list of headers to be precompiled.
+ */
+public class PrefixHeaderFileGenerateTask extends DefaultTask {
+    @Input
+    Set<String> headers;
+
+    @OutputFile
+    File prefixHeaderFile;
+
+    @TaskAction
+    void generatePrefixHeaderFile() {
+        PrefixHeaderFileGeneratorUtil.generatePCHFile(headers, prefixHeaderFile);
+    }
+
+    public Set<String> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Set<String> headers) {
+        this.headers = headers;
+    }
+
+    public File getPrefixHeaderFile() {
+        return prefixHeaderFile;
+    }
+
+    public void setPrefixHeaderFile(File prefixHeaderFile) {
+        this.prefixHeaderFile = prefixHeaderFile;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/tasks/package-info.java
new file mode 100644
index 0000000..c34cb7e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform.tasks;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteBinarySpec.java
new file mode 100644
index 0000000..0f66a80
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteBinarySpec.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.nativeplatform.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.test.TestSuiteBinarySpec;
+
+import java.io.File;
+
+/**
+ * An executable which runs a suite of tests.
+ */
+ at Incubating @HasInternalProtocol
+public interface NativeTestSuiteBinarySpec extends TestSuiteBinarySpec, NativeBinarySpec {
+    /**
+     * Provides access to key tasks used for building the binary.
+     */
+    public interface TasksCollection extends BinaryTasksCollection {
+        /**
+         * The link task.
+         */
+        Task getLink();
+
+        /**
+         * The install task.
+         */
+        Task getInstall();
+
+        /**
+         * The run task.
+         */
+        Task getRun();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeTestSuiteSpec getTestSuite();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    NativeTestSuiteSpec getComponent();
+
+    /**
+     * The tested binary.
+     */
+    NativeBinarySpec getTestedBinary();
+
+    /**
+     * The executable file.
+     */
+    File getExecutableFile();
+
+    /**
+     * The executable file.
+     */
+    void setExecutableFile(File executableFile);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    TasksCollection getTasks();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteSpec.java
new file mode 100644
index 0000000..43b0284
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/NativeTestSuiteSpec.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.nativeplatform.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.platform.base.test.TestSuiteSpec;
+
+/**
+ * A component representing a suite of tests that will be executed together.
+ */
+ at Incubating
+public interface NativeTestSuiteSpec extends TestSuiteSpec, NativeComponentSpec {
+    /**
+     * {@inheritDoc}
+     */
+    NativeComponentSpec getTestedComponent();
+
+    void setTestedComponent(NativeComponentSpec testedComponent);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpec.java
new file mode 100644
index 0000000..650448f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpec.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.internal;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.internal.AbstractNativeBinarySpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.tasks.InstallExecutable;
+import org.gradle.nativeplatform.tasks.LinkExecutable;
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+import org.gradle.nativeplatform.test.NativeTestSuiteSpec;
+import org.gradle.nativeplatform.test.tasks.RunTestExecutable;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.BinaryTasksCollectionWrapper;
+
+import java.io.File;
+
+public abstract class DefaultNativeTestSuiteBinarySpec extends AbstractNativeBinarySpec implements NativeTestSuiteBinarySpecInternal {
+    private final DefaultTasksCollection tasks = new DefaultTasksCollection(super.getTasks());
+    private NativeBinarySpec testedBinary;
+    private File executableFile;
+
+    @Override
+    public NativeTestSuiteSpec getComponent() {
+        return (NativeTestSuiteSpec) super.getComponent();
+    }
+
+    public NativeBinarySpec getTestedBinary() {
+        return testedBinary;
+    }
+
+    public void setTestedBinary(NativeBinarySpecInternal testedBinary) {
+        this.testedBinary = testedBinary;
+        setTargetPlatform(testedBinary.getTargetPlatform());
+        setToolChain(testedBinary.getToolChain());
+        setPlatformToolProvider(testedBinary.getPlatformToolProvider());
+        setBuildType(testedBinary.getBuildType());
+        setFlavor(testedBinary.getFlavor());
+    }
+
+    public File getExecutableFile() {
+        return executableFile;
+    }
+
+    public void setExecutableFile(File executableFile) {
+        this.executableFile = executableFile;
+    }
+
+    public File getPrimaryOutput() {
+        return getExecutableFile();
+    }
+
+    @Override
+    protected ObjectFilesToBinary getCreateOrLink() {
+        return tasks.getLink();
+    }
+
+    public NativeTestSuiteBinarySpec.TasksCollection getTasks() {
+        return tasks;
+    }
+
+    private static class DefaultTasksCollection extends BinaryTasksCollectionWrapper implements NativeTestSuiteBinarySpec.TasksCollection {
+        public DefaultTasksCollection(BinaryTasksCollection delegate) {
+            super(delegate);
+        }
+
+        public LinkExecutable getLink() {
+            return findSingleTaskWithType(LinkExecutable.class);
+        }
+
+        public InstallExecutable getInstall() {
+            return findSingleTaskWithType(InstallExecutable.class);
+        }
+
+        public RunTestExecutable getRun() {
+            return findSingleTaskWithType(RunTestExecutable.class);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinaryRenderer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinaryRenderer.java
new file mode 100644
index 0000000..ed1096f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinaryRenderer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.internal;
+
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.nativeplatform.internal.AbstractNativeBinaryRenderer;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+
+public class NativeTestSuiteBinaryRenderer extends AbstractNativeBinaryRenderer<NativeTestSuiteBinarySpec> {
+    @Override
+    public Class<NativeTestSuiteBinarySpec> getTargetType() {
+        return NativeTestSuiteBinarySpec.class;
+    }
+
+    @Override
+    protected void renderTasks(NativeTestSuiteBinarySpec binary, TextReportBuilder builder) {
+        builder.item("run using task", binary.getTasks().getRun().getPath());
+    }
+
+    @Override
+    protected void renderOutputs(NativeTestSuiteBinarySpec binary, TextReportBuilder builder) {
+        builder.item("executable file", binary.getExecutableFile());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinarySpecInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinarySpecInternal.java
new file mode 100644
index 0000000..e378473
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/internal/NativeTestSuiteBinarySpecInternal.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.internal;
+
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+
+public interface NativeTestSuiteBinarySpecInternal extends NativeTestSuiteBinarySpec, NativeBinarySpecInternal {
+    void setTestedBinary(NativeBinarySpecInternal testedBinary);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/package-info.java
new file mode 100644
index 0000000..2c66b66
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/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 testing native binaries.
+ */
+package org.gradle.nativeplatform.test;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPlugin.java
new file mode 100644
index 0000000..3ed9075
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPlugin.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.language.nativeplatform.DependentSourceSet;
+import org.gradle.model.*;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+import org.gradle.nativeplatform.tasks.InstallExecutable;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+import org.gradle.nativeplatform.test.tasks.RunTestExecutable;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.test.DefaultTestSuiteContainer;
+import org.gradle.platform.base.test.TestSuiteContainer;
+
+import java.io.File;
+
+/**
+ * A plugin that sets up the infrastructure for testing native binaries with CUnit.
+ */
+ at Incubating
+public class NativeBinariesTestPlugin implements Plugin<Project> {
+    public void apply(final Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @Model
+        TestSuiteContainer testSuites(ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return instantiator.newInstance(DefaultTestSuiteContainer.class, instantiator);
+        }
+
+        @Finalize
+            // Must run after test binaries have been created (currently in CUnit plugin)
+        void attachTestedBinarySourcesToTestBinaries(BinaryContainer binaries) {
+            for (NativeTestSuiteBinarySpec testSuiteBinary : binaries.withType(NativeTestSuiteBinarySpec.class)) {
+                NativeBinarySpec testedBinary = testSuiteBinary.getTestedBinary();
+                testSuiteBinary.source(testedBinary.getSource());
+
+                for (DependentSourceSet testSource : testSuiteBinary.getSource().withType(DependentSourceSet.class)) {
+                    testSource.lib(testedBinary.getSource());
+                }
+            }
+        }
+
+        @Finalize
+        public void createTestTasks(final TaskContainer tasks, BinaryContainer binaries) {
+            for (NativeTestSuiteBinarySpec testBinary : binaries.withType(NativeTestSuiteBinarySpec.class)) {
+                NativeBinarySpecInternal binary = (NativeBinarySpecInternal) testBinary;
+                final BinaryNamingScheme namingScheme = binary.getNamingScheme();
+
+                RunTestExecutable runTask = tasks.create(namingScheme.getTaskName("run"), RunTestExecutable.class);
+                final Project project = runTask.getProject();
+                runTask.setDescription(String.format("Runs the %s", binary));
+
+                final InstallExecutable installTask = binary.getTasks().withType(InstallExecutable.class).iterator().next();
+                runTask.getInputs().files(installTask.getOutputs().getFiles());
+                runTask.setExecutable(installTask.getRunScript().getPath());
+                runTask.setOutputDir(new File(project.getBuildDir(), "/test-results/" + namingScheme.getOutputDirectoryBase()));
+
+                testBinary.getTasks().add(runTask);
+            }
+        }
+
+        @Mutate
+        void attachBinariesToCheckLifecycle(CollectionBuilder<Task> tasks, final BinaryContainer binaries) {
+            // TODO - binaries aren't an input to this rule, they're an input to the action
+            tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
+                @Override
+                public void execute(Task checkTask) {
+                    for (NativeTestSuiteBinarySpec testBinary : binaries.withType(NativeTestSuiteBinarySpec.class)) {
+                        checkTask.dependsOn(testBinary.getTasks().getRun());
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/plugins/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/plugins/package-info.java
new file mode 100644
index 0000000..79137f1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/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.
+ */
+
+/**
+ * Plugin classes for generic support for testing native binaries.
+ */
+package org.gradle.nativeplatform.test.plugins;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/tasks/RunTestExecutable.groovy b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/tasks/RunTestExecutable.groovy
new file mode 100644
index 0000000..3d4f06f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/tasks/RunTestExecutable.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.tasks
+import org.gradle.api.GradleException
+import org.gradle.api.Incubating
+import org.gradle.api.tasks.AbstractExecTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.ParallelizableTask
+import org.gradle.api.tasks.TaskAction
+import org.gradle.logging.ConsoleRenderer
+/**
+ * Runs a compiled and installed test executable.
+ */
+ at Incubating
+ at SuppressWarnings("unchecked")
+ at ParallelizableTask
+public class RunTestExecutable extends AbstractExecTask<RunTestExecutable> {
+    public RunTestExecutable() {
+        super(RunTestExecutable.class);
+    }
+
+    /**
+     * The directory where the results should be generated.
+     */
+    @OutputDirectory File outputDir
+
+    /**
+     * Should the build continue if a test fails, or should the build break?
+     */
+    @Input boolean ignoreFailures
+
+    @TaskAction
+    @Override
+    protected void exec() {
+        // Make convention mapping work
+        setExecutable(getExecutable());
+        setWorkingDir(getOutputDir());
+
+        try {
+            super.exec();
+        } catch (Exception e) {
+            handleTestFailures(e);
+        }
+    }
+
+    private void handleTestFailures(Exception e) {
+        String message = "There were failing tests";
+        String resultsUrl = new ConsoleRenderer().asClickableFileUrl(getOutputDir());
+        message = message.concat(". See the results at: " + resultsUrl);
+
+        if (isIgnoreFailures()) {
+            getLogger().warn(message);
+        } else {
+            throw new GradleException(message, e);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/tasks/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/tasks/package-info.java
new file mode 100644
index 0000000..d0d8ba3
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/test/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 test execution.
+ */
+package org.gradle.nativeplatform.test.tasks;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/Clang.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/Clang.java
new file mode 100644
index 0000000..c11b9cc
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/Clang.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.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The <a href="http://clang.llvm.org">Clang</a> tool chain.
+ */
+ at Incubating
+public interface Clang extends GccCompatibleToolChain {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/CommandLineToolConfiguration.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/CommandLineToolConfiguration.java
new file mode 100644
index 0000000..b66c326
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/CommandLineToolConfiguration.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.nativeplatform.toolchain;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * An executable tool that forms part of a tool chain.
+ */
+ at Incubating
+public interface CommandLineToolConfiguration {
+    /**
+     * Adds an action that will be applied to the command-line arguments prior to execution.
+     */
+    void withArguments(Action<? super List<String>> arguments);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/Gcc.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/Gcc.java
new file mode 100644
index 0000000..5f75582
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * The <a href="http://gcc.gnu.org/">GNU GCC</a> tool chain.
+ */
+ at Incubating
+public interface Gcc extends GccCompatibleToolChain {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCommandLineToolConfiguration.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCommandLineToolConfiguration.java
new file mode 100644
index 0000000..b841ac0
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCommandLineToolConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain;
+
+
+import org.gradle.api.Incubating;
+
+/**
+ * An executable tool used for GCC that allows customizing the executable.
+ */
+ at Incubating
+public interface GccCommandLineToolConfiguration extends CommandLineToolConfiguration {
+    /**
+     * The name of the executable file for this tool.
+     */
+    String getExecutable();
+
+    /**
+     * Set the name of the executable file for this tool.
+     * The executable will be located in the tool chain path.
+     */
+    void setExecutable(String file);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCompatibleToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCompatibleToolChain.java
new file mode 100644
index 0000000..9d22ddf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccCompatibleToolChain.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.nativeplatform.toolchain;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A ToolChain that can handle additional platforms simply by configuring the NativeBinary.
+ */
+ at Incubating
+public interface GccCompatibleToolChain extends NativeToolChain {
+    /**
+     * The paths setting required for executing the tool chain.
+     * These are used to locate tools for this tool chain, and are prepended to the system PATH when executing these tools.
+     */
+    List<File> getPath();
+
+    /**
+     * Append an entry or entries to the tool chain path.
+     *
+     * @param pathEntries The path values to append. These are evaluated as per {@link org.gradle.api.Project#files(Object...)}
+     */
+    void path(Object... pathEntries);
+
+    /**
+     * Add support for target platform specified by name.
+     */
+    public void target(String platformName);
+
+    /**
+     * Add configuration for a target platform specified by name with additional configuration action.
+     */
+    public void target(String platformName, Action<? super GccPlatformToolChain> action);
+
+    /**
+     * Adds an action that can fine-tune the tool configuration for each platform supported by this tool chain.
+     */
+    public void eachPlatform(Action<? super GccPlatformToolChain> action);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccPlatformToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccPlatformToolChain.java
new file mode 100644
index 0000000..c66b7bf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/GccPlatformToolChain.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * GCC specific settings for the tools used to build for a particular platform.
+ */
+ at Incubating
+public interface GccPlatformToolChain extends NativePlatformToolChain {
+    /**
+     * Returns the settings to use for the C compiler.
+     */
+    GccCommandLineToolConfiguration getcCompiler();
+
+    /**
+     * Returns the settings to use for the C++ compiler.
+     */
+    GccCommandLineToolConfiguration getCppCompiler();
+
+    /**
+     * Returns the settings to use for the Objective-C compiler.
+     */
+    GccCommandLineToolConfiguration getObjcCompiler();
+
+    /**
+     * Returns the settings to use for the Objective-C++ compiler.
+     */
+    GccCommandLineToolConfiguration getObjcppCompiler();
+
+    /**
+     * Returns the settings to use for the assembler.
+     */
+    GccCommandLineToolConfiguration getAssembler();
+
+    /**
+     * Returns the settings to use for the linker.
+     */
+    GccCommandLineToolConfiguration getLinker();
+
+    /**
+     * Returns the settings to use for the archiver.
+     */
+    GccCommandLineToolConfiguration getStaticLibArchiver();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativePlatformToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativePlatformToolChain.java
new file mode 100644
index 0000000..27b867e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativePlatformToolChain.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * A platform specific configurable tool chain.
+ */
+ at Incubating
+public interface NativePlatformToolChain {
+    /**
+     * Returns the platform which this tool chain builds for.
+     */
+    NativePlatform getPlatform();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChain.java
new file mode 100644
index 0000000..ec7c709
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChain.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.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.ToolChain;
+
+/**
+ * A set of compilers and linkers that are used together to construct a native binary.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface NativeToolChain extends ToolChain {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java
new file mode 100644
index 0000000..068c012
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.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.nativeplatform.toolchain;
+
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.platform.base.ToolChainRegistry;
+
+/**
+ * A container for {@link NativeToolChain}s.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface NativeToolChainRegistry extends ExtensiblePolymorphicDomainObjectContainer<NativeToolChain>, ToolChainRegistry<NativePlatform, NativeToolChain> {
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCpp.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCpp.java
new file mode 100644
index 0000000..407cd1b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCpp.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.nativeplatform.toolchain;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
+/**
+ * The Visual C++ tool chain.
+ */
+ at Incubating
+public interface VisualCpp extends NativeToolChain {
+    /**
+     * The directory where Visual Studio or Visual C++ is installed.
+     */
+    File getInstallDir();
+
+    /**
+     * The directory where Visual Studio or Visual C++ is installed.
+     */
+    void setInstallDir(Object installDir);
+
+    /**
+     * The directory where Windows SDK is installed.
+     */
+    File getWindowsSdkDir();
+
+    /**
+     * The directory where Windows SDK is installed.
+     */
+    void setWindowsSdkDir(Object installDir);
+
+    /**
+     * Adds an action that can fine-tune the tool configuration for each platform supported by this tool chain.
+     */
+    public void eachPlatform(Action<? super VisualCppPlatformToolChain> action);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCppPlatformToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCppPlatformToolChain.java
new file mode 100644
index 0000000..469aa8a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/VisualCppPlatformToolChain.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Visual C++ specific settings for the tools used to build for a particular platform.
+ */
+ at Incubating
+public interface VisualCppPlatformToolChain extends NativePlatformToolChain {
+    /**
+     * Returns the settings to use for the C compiler.
+     */
+    CommandLineToolConfiguration getcCompiler();
+
+    /**
+     * Returns the settings to use for the C++ compiler.
+     */
+    CommandLineToolConfiguration getCppCompiler();
+
+    /**
+     * Returns the settings to use for the Windows resources compiler.
+     */
+    CommandLineToolConfiguration getRcCompiler();
+
+    /**
+     * Returns the settings to use for the assembler.
+     */
+    CommandLineToolConfiguration getAssembler();
+
+    /**
+     * Returns the settings to use for the linker.
+     */
+    CommandLineToolConfiguration getLinker();
+
+    /**
+     * Returns the settings to use for the archiver.
+     */
+    CommandLineToolConfiguration getStaticLibArchiver();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/AbstractPlatformToolProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/AbstractPlatformToolProvider.java
new file mode 100644
index 0000000..89a9ce8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/AbstractPlatformToolProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerUtil;
+import org.gradle.nativeplatform.internal.LinkerSpec;
+import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.*;
+import org.gradle.util.TreeVisitor;
+
+public abstract class AbstractPlatformToolProvider implements PlatformToolProvider {
+    protected final OperatingSystemInternal targetOperatingSystem;
+    protected final BuildOperationProcessor buildOperationProcessor;
+
+    public AbstractPlatformToolProvider(BuildOperationProcessor buildOperationProcessor, OperatingSystemInternal targetOperatingSystem) {
+        this.targetOperatingSystem = targetOperatingSystem;
+        this.buildOperationProcessor = buildOperationProcessor;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public void explain(TreeVisitor<? super String> visitor) {
+    }
+
+    public String getExecutableName(String executablePath) {
+        return targetOperatingSystem.getInternalOs().getExecutableName(executablePath);
+    }
+
+    public String getSharedLibraryName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getSharedLibraryName(libraryPath);
+    }
+
+    public String getSharedLibraryLinkFileName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getSharedLibraryName(libraryPath);
+    }
+
+    public String getStaticLibraryName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getStaticLibraryName(libraryPath);
+    }
+
+    @Override
+    public <T> T get(Class<T> toolType) {
+        throw new IllegalArgumentException(String.format("Don't know how to provide tool of type %s.", toolType.getSimpleName()));
+    }
+
+    public <T extends CompileSpec> org.gradle.language.base.internal.compile.Compiler<T> newCompiler(Class<T> spec) {
+        if (CppCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createCppCompiler());
+        }
+        if (CppPCHCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createCppPCHCompiler());
+        }
+        if (CCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createCCompiler());
+        }
+        if (CPCHCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createCPCHCompiler());
+        }
+        if (ObjectiveCppCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createObjectiveCppCompiler());
+        }
+        if (ObjectiveCppPCHCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createObjectiveCppPCHCompiler());
+        }
+        if (ObjectiveCCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createObjectiveCCompiler());
+        }
+        if (ObjectiveCPCHCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createObjectiveCPCHCompiler());
+        }
+        if (WindowsResourceCompileSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createWindowsResourceCompiler());
+        }
+        if (AssembleSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createAssembler());
+        }
+        if (LinkerSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createLinker());
+        }
+        if (StaticLibraryArchiverSpec.class.isAssignableFrom(spec)) {
+            return CompilerUtil.castCompiler(createStaticLibraryArchiver());
+        }
+        throw new IllegalArgumentException(String.format("Don't know how to compile from a spec of type %s.", spec.getClass().getSimpleName()));
+    }
+
+    protected final RuntimeException unavailableTool(String message) {
+        return new RuntimeException(message);
+    }
+
+    protected Compiler<?> createCppCompiler() {
+        throw unavailableTool("C++ compiler is not available");
+    }
+
+    protected Compiler<?> createCppPCHCompiler() {
+        throw unavailableTool("C++ pre-compiled header compiler is not available");
+    }
+
+    protected Compiler<?> createCCompiler() {
+        throw unavailableTool("C compiler is not available");
+    }
+
+    protected Compiler<?> createCPCHCompiler() {
+        throw unavailableTool("C pre-compiled header compiler is not available");
+    }
+
+    protected Compiler<?> createObjectiveCppCompiler() {
+        throw unavailableTool("Obj-C++ compiler is not available");
+    }
+
+    protected Compiler<?> createObjectiveCppPCHCompiler() {
+        throw unavailableTool("Obj-C++ pre-compiled header compiler is not available");
+    }
+
+    protected Compiler<?> createObjectiveCCompiler() {
+        throw unavailableTool("Obj-C compiler is not available");
+    }
+
+    protected Compiler<?> createObjectiveCPCHCompiler() {
+        throw unavailableTool("Obj-C compiler is not available");
+    }
+
+    protected Compiler<?> createWindowsResourceCompiler() {
+        throw unavailableTool("Windows resource compiler is not available");
+    }
+
+    protected Compiler<?> createAssembler() {
+        throw unavailableTool("Assembler is not available");
+    }
+
+    protected Compiler<?> createLinker() {
+        throw unavailableTool("Linker is not available");
+    }
+
+    protected Compiler<?> createStaticLibraryArchiver() {
+        throw unavailableTool("Static library archiver is not available");
+    }
+
+    public String getObjectFileExtension() {
+        return targetOperatingSystem.isWindows() ? ".obj" : ".o";
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java
new file mode 100644
index 0000000..f59d2c1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.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.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Transformer;
+import org.gradle.nativeplatform.internal.BinaryToolSpec;
+
+import java.util.List;
+
+public interface ArgsTransformer<T extends BinaryToolSpec> extends Transformer<List<String>, T> {
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolContext.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolContext.java
new file mode 100644
index 0000000..c6cf182
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolContext.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Action;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public interface CommandLineToolContext {
+    List<File> getPath();
+
+    Map<String, String> getEnvironment();
+
+    Action<List<String>> getArgAction();
+
+    CommandLineToolInvocation createInvocation(String description, File workingDirectory, Iterable<String> args, BuildOperationLogger oplogger);
+    CommandLineToolInvocation createInvocation(String description, Iterable<String> args, BuildOperationLogger oplogger);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocation.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocation.java
new file mode 100644
index 0000000..63970bf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocation.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.nativeplatform.toolchain.internal;
+
+import org.gradle.internal.operations.BuildOperation;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public interface CommandLineToolInvocation extends BuildOperation  {
+
+    List<File> getPath();
+
+    Map<String, String> getEnvironment();
+
+    File getWorkDirectory();
+
+    Iterable<String> getArgs();
+
+    BuildOperationLogger getLogger();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationFailure.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationFailure.java
new file mode 100644
index 0000000..d138a96
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationFailure.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.internal.operations.BuildOperation;
+import org.gradle.internal.operations.BuildOperationFailure;
+
+class CommandLineToolInvocationFailure extends BuildOperationFailure {
+    CommandLineToolInvocationFailure(BuildOperation operation, String message) {
+        super(operation, message);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationWorker.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationWorker.java
new file mode 100644
index 0000000..32861b9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/CommandLineToolInvocationWorker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.internal.operations.BuildOperationWorker;
+
+public interface CommandLineToolInvocationWorker extends BuildOperationWorker<CommandLineToolInvocation> {
+    /**
+     * Returns a human consumable name for this tool.
+     */
+    String getDisplayName();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocation.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocation.java
new file mode 100644
index 0000000..db506e4
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocation.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.nativeplatform.toolchain.internal;
+
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+class DefaultCommandLineToolInvocation implements CommandLineToolInvocation {
+    private String description;
+    private final File workDirectory;
+    private final Iterable<String> args;
+    private final CommandLineToolContext context;
+    private final BuildOperationLogger oplogger;
+
+    DefaultCommandLineToolInvocation(String description, File workDirectory, Iterable<String> args, CommandLineToolContext context, BuildOperationLogger oplogger) {
+        this.description = description;
+        this.workDirectory = workDirectory;
+        this.args = args;
+        this.context = context;
+        this.oplogger = oplogger;
+    }
+
+    public Iterable<String> getArgs() {
+        return args;
+    }
+
+    @Override
+    public BuildOperationLogger getLogger() {
+        return oplogger;
+    }
+
+    public File getWorkDirectory() {
+        return workDirectory;
+    }
+
+    public List<File> getPath() {
+        return context.getPath();
+    }
+
+    public Map<String, String> getEnvironment() {
+        return context.getEnvironment();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorker.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorker.java
new file mode 100644
index 0000000..4f51b92
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorker.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import com.google.common.base.Joiner;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.process.internal.ExecException;
+import org.gradle.util.GFileUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+
+public class DefaultCommandLineToolInvocationWorker implements CommandLineToolInvocationWorker {
+    private final String name;
+    private final File executable;
+    private final ExecActionFactory execActionFactory;
+
+    public DefaultCommandLineToolInvocationWorker(String name, File executable, ExecActionFactory execActionFactory) {
+        this.name = name;
+        this.executable = executable;
+        this.execActionFactory = execActionFactory;
+    }
+
+    public String getDisplayName() {
+        return String.format("command line tool '%s'", name);
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public void execute(CommandLineToolInvocation invocation) {
+        ExecAction toolExec = execActionFactory.newExecAction();
+
+        toolExec.executable(executable);
+        if (invocation.getWorkDirectory() != null) {
+            GFileUtils.mkdirs(invocation.getWorkDirectory());
+            toolExec.workingDir(invocation.getWorkDirectory());
+        }
+
+        toolExec.args(invocation.getArgs());
+
+        if (!invocation.getPath().isEmpty()) {
+            String pathVar = OperatingSystem.current().getPathVar();
+            String toolPath = Joiner.on(File.pathSeparator).join(invocation.getPath());
+            toolPath = toolPath + File.pathSeparator + System.getenv(pathVar);
+            toolExec.environment(pathVar, toolPath);
+            if (OperatingSystem.current().isWindows() && toolExec.getEnvironment().containsKey(pathVar.toUpperCase())) {
+                toolExec.getEnvironment().remove(pathVar.toUpperCase());
+            }
+        }
+
+        toolExec.environment(invocation.getEnvironment());
+
+        ByteArrayOutputStream errOutput = new ByteArrayOutputStream();
+        ByteArrayOutputStream stdOutput = new ByteArrayOutputStream();
+        toolExec.setErrorOutput(errOutput);
+        toolExec.setStandardOutput(stdOutput);
+
+        try {
+            toolExec.execute();
+            invocation.getLogger().operationSuccess(invocation.getDescription(), combineOutput(stdOutput, errOutput));
+        } catch (ExecException e) {
+            invocation.getLogger().operationFailed(invocation.getDescription(), combineOutput(stdOutput, errOutput));
+            throw new CommandLineToolInvocationFailure(invocation, String.format("%s failed while %s.", name, invocation.getDescription()));
+        }
+    }
+
+    private String combineOutput(OutputStream stdOutput, OutputStream errOutput) {
+        return stdOutput.toString() + errOutput.toString();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultMutableCommandLineToolContext.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultMutableCommandLineToolContext.java
new file mode 100644
index 0000000..90277af
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultMutableCommandLineToolContext.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Action;
+import org.gradle.internal.Actions;
+import org.gradle.internal.operations.logging.BuildOperationLogger;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultMutableCommandLineToolContext implements MutableCommandLineToolContext {
+    private Action<List<String>> postArgsAction = Actions.doNothing();
+    private final Map<String, String> environment = new HashMap<String, String>();
+    private final List<File> path = new ArrayList<File>();
+
+    public void setArgAction(Action<List<String>> argAction) {
+        postArgsAction = argAction;
+    }
+
+    public void addPath(File pathEntry) {
+        this.path.add(pathEntry);
+    }
+
+    public void addPath(List<File> path) {
+        this.path.addAll(path);
+    }
+
+    public List<File> getPath() {
+        return path;
+    }
+
+    public Map<String, String> getEnvironment() {
+        return environment;
+    }
+
+    public Action<List<String>> getArgAction() {
+        return postArgsAction;
+    }
+
+    public void addEnvironmentVar(String key, String value) {
+        this.environment.put(key, value);
+    }
+
+    public CommandLineToolInvocation createInvocation(String description, File workDirectory, Iterable<String> args, BuildOperationLogger oplogger) {
+        return new DefaultCommandLineToolInvocation(description, workDirectory, args, this, oplogger);
+    }
+
+    public CommandLineToolInvocation createInvocation(String description, Iterable<String> args, BuildOperationLogger oplogger) {
+        File currentWorkingDirectory = null;
+        return createInvocation(description, currentWorkingDirectory, args, oplogger);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistry.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistry.java
new file mode 100755
index 0000000..8bb65df
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistry.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.internal.DefaultPolymorphicDomainObjectContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+import org.gradle.util.TreeVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultNativeToolChainRegistry extends DefaultPolymorphicDomainObjectContainer<NativeToolChain> implements NativeToolChainRegistryInternal {
+    private final Map<String, Class<? extends NativeToolChain>> registeredDefaults = new LinkedHashMap<String, Class<? extends NativeToolChain>>();
+    private final List<NativeToolChainInternal> searchOrder = new ArrayList<NativeToolChainInternal>();
+
+    public DefaultNativeToolChainRegistry(Instantiator instantiator) {
+        super(NativeToolChain.class, instantiator);
+        whenObjectAdded(new Action<NativeToolChain>() {
+            public void execute(NativeToolChain toolChain) {
+                searchOrder.add((NativeToolChainInternal) toolChain);
+            }
+        });
+        whenObjectRemoved(new Action<NativeToolChain>() {
+            public void execute(NativeToolChain toolChain) {
+                searchOrder.remove(toolChain);
+            }
+        });
+    }
+
+    @Override
+    protected void handleAttemptToAddItemWithNonUniqueName(NativeToolChain toolChain) {
+        throw new InvalidUserDataException(String.format("ToolChain with name '%s' added multiple times", toolChain.getName()));
+    }
+
+    public void registerDefaultToolChain(String name, Class<? extends NativeToolChain> type) {
+        registeredDefaults.put(name, type);
+    }
+
+    public void addDefaultToolChains() {
+        for (String name : registeredDefaults.keySet()) {
+            create(name, registeredDefaults.get(name));
+        }
+    }
+
+    public NativeToolChain getForPlatform(NativePlatform targetPlatform) {
+        for (NativeToolChainInternal toolChain : searchOrder) {
+            if (toolChain.select((NativePlatformInternal) targetPlatform).isAvailable()) {
+                return toolChain;
+            }
+        }
+
+        // No tool chains can build for this platform. Assemble a description of why
+        Map<String, PlatformToolProvider> candidates = new LinkedHashMap<String, PlatformToolProvider>();
+        for (NativeToolChainInternal toolChain : searchOrder) {
+            candidates.put(toolChain.getDisplayName(), toolChain.select((NativePlatformInternal) targetPlatform));
+        }
+
+        return new UnavailableNativeToolChain(new UnavailableToolChainDescription(targetPlatform, candidates));
+    }
+
+    private static class UnavailableToolChainDescription implements ToolSearchResult {
+        private final NativePlatform targetPlatform;
+        private final Map<String, PlatformToolProvider> candidates;
+
+        private UnavailableToolChainDescription(NativePlatform targetPlatform, Map<String, PlatformToolProvider> candidates) {
+            this.targetPlatform = targetPlatform;
+            this.candidates = candidates;
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(String.format("No tool chain is available to build for platform '%s'", targetPlatform.getName()));
+            visitor.startChildren();
+            for (Map.Entry<String, PlatformToolProvider> entry : candidates.entrySet()) {
+                visitor.node(entry.getKey());
+                visitor.startChildren();
+                entry.getValue().explain(visitor);
+                visitor.endChildren();
+            }
+            if (candidates.isEmpty()) {
+                visitor.node("No tool chain plugin applied.");
+            }
+            visitor.endChildren();
+        }
+    }
+
+    private static class UnavailableNativeToolChain implements NativeToolChainInternal {
+        private final ToolSearchResult failure;
+
+        UnavailableNativeToolChain(ToolSearchResult failure) {
+            this.failure = failure;
+        }
+
+        public String getDisplayName() {
+            return getName();
+        }
+
+        public String getName() {
+            return "unavailable";
+        }
+
+        public PlatformToolProvider select(NativePlatformInternal targetPlatform) {
+            return new UnavailablePlatformToolProvider(targetPlatform.getOperatingSystem(), failure);
+        }
+
+        public String getOutputType() {
+            return "unavailable";
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ExtendableToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ExtendableToolChain.java
new file mode 100644
index 0000000..6dcb4e1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ExtendableToolChain.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.nativeplatform.toolchain.NativePlatformToolChain;
+
+import java.io.File;
+
+public abstract class ExtendableToolChain<T extends NativePlatformToolChain> implements NativeToolChainInternal {
+    private final String name;
+    protected final OperatingSystem operatingSystem;
+    private final FileResolver fileResolver;
+    protected final ActionBroadcast<T> configureActions = new ActionBroadcast<T>();
+    protected final BuildOperationProcessor buildOperationProcessor;
+
+    protected ExtendableToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver) {
+        this.name = name;
+        this.operatingSystem = operatingSystem;
+        this.fileResolver = fileResolver;
+        this.buildOperationProcessor = buildOperationProcessor;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    protected abstract String getTypeName();
+
+    public String getDisplayName() {
+        return String.format("Tool chain '%s' (%s)", getName(), getTypeName());
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public String getOutputType() {
+        return String.format("%s-%s", getName(), operatingSystem.getName());
+    }
+
+    public void eachPlatform(Action<? super T> action) {
+        configureActions.add(action);
+    }
+
+    protected File resolve(Object path) {
+        return fileResolver.resolve(path);
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MacroArgsConverter.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MacroArgsConverter.java
new file mode 100644
index 0000000..3bd3613
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MacroArgsConverter.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.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Transformer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class MacroArgsConverter implements Transformer<List<String>, Map<String, String>> {
+    public List<String> transform(Map<String, String> original) {
+        List<String> macroList = new ArrayList<String>(original.size());
+        for (String macroName : original.keySet()) {
+            String macroDef = original.get(macroName);
+            String arg = macroDef == null ? macroName : String.format("%s=%s", macroName, macroDef);
+            macroList.add(arg);
+        }
+        return macroList;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MutableCommandLineToolContext.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MutableCommandLineToolContext.java
new file mode 100644
index 0000000..2ddd01c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/MutableCommandLineToolContext.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.Action;
+
+import java.io.File;
+import java.util.List;
+
+public interface MutableCommandLineToolContext extends CommandLineToolContext {
+
+    void setArgAction(Action<List<String>> argAction);
+
+    void addPath(File pathEntry);
+
+    void addPath(List<File> path);
+
+    void addEnvironmentVar(String key, String value);
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompileSpec.java
new file mode 100644
index 0000000..423f13c
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompileSpec.java
@@ -0,0 +1,85 @@
+/*
+ * 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.nativeplatform.toolchain.internal;
+
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+import org.gradle.nativeplatform.internal.BinaryToolSpec;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 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);
+
+    List<File> getIncludeRoots();
+
+    void include(Iterable<File> includeRoots);
+
+    void include(File... includeRoots);
+
+    List<File> getSourceFiles();
+
+    void setSourceFiles(Collection<File> sources);
+
+    void source(Iterable<File> sources);
+
+    List<File> getRemovedSourceFiles();
+
+    void setRemovedSourceFiles(Collection<File> sources);
+
+    void removedSource(Iterable<File> sources);
+
+    Map<String, String> getMacros();
+
+    void setMacros(Map<String, String> macros);
+
+    void define(String name);
+
+    void define(String name, String value);
+
+    boolean isPositionIndependentCode();
+
+    void setPositionIndependentCode(boolean flag);
+
+    boolean isIncrementalCompile();
+
+    void setIncrementalCompile(boolean flag);
+
+    File getPrefixHeaderFile();
+
+    void setPrefixHeaderFile(File prefixHeaderFile);
+
+    File getPreCompiledHeaderObjectFile();
+
+    void setPreCompiledHeaderObjectFile(File preCompiledHeaderObjectFile);
+
+    Set<String> getPreCompiledHeaders();
+
+    void setPreCompiledHeaders(Set<String> headers);
+
+    Map<File, SourceIncludes> getSourceFileIncludes();
+
+    void setSourceFileIncludes(Map<File, SourceIncludes> map);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompiler.java
new file mode 100644
index 0000000..00a302f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompiler.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.FileUtils;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.BuildOperationQueue;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.nativeplatform.internal.SourceIncludes;
+import org.gradle.nativeplatform.internal.CompilerOutputFileNamingScheme;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+abstract public class NativeCompiler<T extends NativeCompileSpec> implements Compiler<T> {
+
+    private final CommandLineToolInvocationWorker commandLineToolInvocationWorker;
+    private final ArgsTransformer<T> argsTransformer;
+    private final Transformer<T, T> specTransformer;
+    private final CommandLineToolContext invocationContext;
+    private final String objectFileExtension;
+    private final boolean useCommandFile;
+
+    private final BuildOperationProcessor buildOperationProcessor;
+
+    public NativeCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, ArgsTransformer<T> argsTransformer, Transformer<T, T> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        this.invocationContext = invocationContext;
+        this.objectFileExtension = objectFileExtension;
+        this.useCommandFile = useCommandFile;
+        this.argsTransformer = argsTransformer;
+        this.specTransformer = specTransformer;
+        this.commandLineToolInvocationWorker = commandLineToolInvocationWorker;
+        this.buildOperationProcessor = buildOperationProcessor;
+    }
+
+    public WorkResult execute(T spec) {
+        final T transformedSpec = specTransformer.transform(spec);
+        final List<String> genericArgs = getArguments(transformedSpec);
+        final BuildOperationQueue<CommandLineToolInvocation> buildQueue = buildOperationProcessor.newQueue(commandLineToolInvocationWorker, spec.getOperationLogger().getLogLocation());
+
+        File objectDir = transformedSpec.getObjectFileDir();
+        for (File sourceFile : transformedSpec.getSourceFiles()) {
+            CommandLineToolInvocation perFileInvocation =
+                    createPerFileInvocation(genericArgs, sourceFile, objectDir, spec);
+            buildQueue.add(perFileInvocation);
+        }
+
+        // Wait on all executions to complete or fail
+        buildQueue.waitForCompletion();
+
+        return new SimpleWorkResult(!transformedSpec.getSourceFiles().isEmpty());
+    }
+
+    protected List<String> getArguments(T spec) {
+        List<String> args = argsTransformer.transform(spec);
+
+        Action<List<String>> userArgTransformer = invocationContext.getArgAction();
+        // modifies in place
+        userArgTransformer.execute(args);
+
+        if (useCommandFile) {
+            // Shorten args and write out an options.txt file
+            // This must be called only once per execute()
+            addOptionsFileArgs(args, spec.getTempDir());
+        }
+        return args;
+    }
+
+    protected List<String> getSourceArgs(File sourceFile) {
+        return Collections.singletonList(sourceFile.getAbsolutePath());
+    }
+
+    protected abstract List<String> getOutputArgs(T spec, File outputFile);
+
+    protected abstract void addOptionsFileArgs(List<String> args, File tempDir);
+
+    protected abstract List<String> getPCHArgs(T spec);
+
+    protected File getOutputFileDir(File sourceFile, File objectFileDir, String fileSuffix) {
+        boolean windowsPathLimitation = OperatingSystem.current().isWindows();
+
+        File outputFile = new CompilerOutputFileNamingScheme()
+                .withObjectFileNameSuffix(fileSuffix)
+                .withOutputBaseFolder(objectFileDir)
+                .map(sourceFile);
+        File outputDirectory = outputFile.getParentFile();
+        if (!outputDirectory.exists()) {
+            outputDirectory.mkdirs();
+        }
+        return windowsPathLimitation ? FileUtils.assertInWindowsPathLengthLimitation(outputFile) : outputFile;
+    }
+
+    protected List<String> maybeGetPCHArgs(final T spec, File sourceFile) {
+        if (spec.getPreCompiledHeaders() == null) {
+            return Lists.newArrayList();
+        }
+
+        final SourceIncludes includes = spec.getSourceFileIncludes().get(sourceFile);
+        boolean usePCH = CollectionUtils.every(spec.getPreCompiledHeaders(), new Spec<String>() {
+            @Override
+            public boolean isSatisfiedBy(String header) {
+                List<String> headerIncludes;
+                if (header.startsWith("<")) {
+                    header = header.substring(1, header.length()-1);
+                    headerIncludes = includes.getSystemIncludes();
+                } else {
+                    headerIncludes = includes.getQuotedIncludes();
+                }
+                return headerIncludes.contains(header);
+            }
+        });
+        if (usePCH) {
+            return getPCHArgs(spec);
+        } else {
+            return Lists.newArrayList();
+        }
+    }
+
+    protected CommandLineToolInvocation createPerFileInvocation(List<String> genericArgs, File sourceFile, File objectDir, T spec) {
+        String objectFileSuffix = objectFileExtension;
+        List<String> sourceArgs = getSourceArgs(sourceFile);
+        List<String> outputArgs = getOutputArgs(spec, getOutputFileDir(sourceFile, objectDir, objectFileSuffix));
+        List<String> pchArgs = maybeGetPCHArgs(spec, sourceFile);
+
+        return invocationContext.createInvocation("compiling ".concat(sourceFile.getName()), objectDir, buildPerFileArgs(genericArgs, sourceArgs, outputArgs, pchArgs), spec.getOperationLogger());
+    }
+
+    protected Iterable<String> buildPerFileArgs(List<String> genericArgs, List<String> sourceArgs, List<String> outputArgs, List<String> pchArgs) {
+        return Iterables.concat(genericArgs, pchArgs, sourceArgs, outputArgs);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainInternal.java
new file mode 100644
index 0000000..d77ead8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainInternal.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.nativeplatform.toolchain.internal;
+
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.platform.base.internal.toolchain.ToolChainInternal;
+
+public interface NativeToolChainInternal extends NativeToolChain, ToolChainInternal<NativePlatformInternal> {
+    /**
+     * Locates the tools that can target the given platform.
+     */
+    PlatformToolProvider select(NativePlatformInternal targetPlatform);
+
+    /**
+     * Returns a unique, opaque, getOutputType for the output produced by this toolchain on the current operating system.
+     */
+    String getOutputType();
+
+    public static class Identifier {
+        public static String identify(NativeToolChainInternal toolChain, NativePlatformInternal platform) {
+            return String.format("%s:%s:%s", toolChain.getOutputType(), platform.getArchitecture().getName(), platform.getOperatingSystem().getName());
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainRegistryInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainRegistryInternal.java
new file mode 100644
index 0000000..2dbd453
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/NativeToolChainRegistryInternal.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.nativeplatform.toolchain.internal;
+
+import org.gradle.nativeplatform.toolchain.NativeToolChain;
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
+
+public interface NativeToolChainRegistryInternal extends NativeToolChainRegistry {
+    /**
+     * Registers a default ToolChain, which may later be added to the registry via {@link #addDefaultToolChains()}.
+     */
+    void registerDefaultToolChain(String name, Class<? extends NativeToolChain> type);
+
+    /**
+     * Adds default tool chains to the registry.
+     */
+    void addDefaultToolChains();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriter.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriter.java
new file mode 100644
index 0000000..96f0872
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriter.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.nativeplatform.toolchain.internal;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.platform.base.internal.toolchain.ArgWriter;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+
+public class OptionsFileArgsWriter implements Action<List<String>> {
+    private final Transformer<ArgWriter, PrintWriter> argWriterFactory;
+    private final File tempDir;
+
+    public OptionsFileArgsWriter(Transformer<ArgWriter, PrintWriter> argWriterFactory, File tempDir) {
+        this.argWriterFactory = argWriterFactory;
+        this.tempDir = tempDir;
+    }
+
+    @Override
+    public void execute(List<String> args) {
+        List<String> originalArgs = Lists.newArrayList(args);
+        args.clear();
+        args.addAll(transformArgs(originalArgs, tempDir));
+    }
+
+    protected List<String> transformArgs(List<String> originalArgs, File tempDir) {
+        GFileUtils.mkdirs(tempDir);
+        File optionsFile = new File(tempDir, "options.txt");
+        try {
+            PrintWriter writer = new PrintWriter(optionsFile);
+            try {
+                ArgWriter argWriter = argWriterFactory.transform(writer);
+                argWriter.args(originalArgs);
+            } finally {
+                IOUtils.closeQuietly(writer);
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(String.format("Could not write compiler options file '%s'.", optionsFile.getAbsolutePath()), e);
+        }
+
+        return Arrays.asList(String.format("@%s", optionsFile.getAbsolutePath()));
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompiler.java
new file mode 100755
index 0000000..088b1d8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompiler.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.nativeplatform.toolchain.internal;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativeplatform.internal.CompilerOutputFileNamingScheme;
+
+import java.io.File;
+
+public class OutputCleaningCompiler<T extends NativeCompileSpec> implements Compiler<T> {
+
+    private final Compiler<T> compiler;
+    private final String outputFileSuffix;
+
+    public OutputCleaningCompiler(Compiler<T> compiler, String outputFileSuffix) {
+        this.compiler = compiler;
+        this.outputFileSuffix = outputFileSuffix;
+    }
+
+    public WorkResult execute(T spec) {
+        boolean didRemove = deleteOutputsForRemovedSources(spec);
+        boolean didCompile = compileSources(spec);
+        return new SimpleWorkResult(didRemove || didCompile);
+    }
+
+    private boolean compileSources(T spec) {
+        if (spec.getSourceFiles().isEmpty()) {
+            return false;
+        }
+        return compiler.execute(spec).getDidWork();
+    }
+
+    private boolean deleteOutputsForRemovedSources(NativeCompileSpec spec) {
+        boolean didRemove = false;
+        for (File removedSource : spec.getRemovedSourceFiles()) {
+            File objectFile = getObjectFile(spec.getObjectFileDir(), removedSource);
+            if (objectFile.delete()) {
+                didRemove = true;
+                objectFile.getParentFile().delete();
+            }
+        }
+        return didRemove;
+    }
+
+    private File getObjectFile(File objectFileRoot, File sourceFile) {
+        return new CompilerOutputFileNamingScheme()
+                        .withObjectFileNameSuffix(outputFileSuffix)
+                        .withOutputBaseFolder(objectFileRoot)
+                        .map(sourceFile);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PCHObjectDirectoryGeneratorUtil.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PCHObjectDirectoryGeneratorUtil.java
new file mode 100644
index 0000000..0ef195a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PCHObjectDirectoryGeneratorUtil.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.apache.commons.io.FileUtils;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.File;
+import java.io.IOException;
+
+public class PCHObjectDirectoryGeneratorUtil {
+    public static File generatePCHObjectDirectory(File tempDir, File prefixHeaderFile, File preCompiledHeaderObjectFile) {
+        File generatedDir = new File(tempDir, "preCompiledHeaders");
+        generatedDir.mkdirs();
+        File generatedHeader = new File(generatedDir, prefixHeaderFile.getName());
+        File generatedPCH = new File(generatedDir, preCompiledHeaderObjectFile.getName());
+        try {
+            FileUtils.copyFile(prefixHeaderFile, generatedHeader);
+            FileUtils.copyFile(preCompiledHeaderObjectFile, generatedPCH);
+            return generatedDir;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PlatformToolProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PlatformToolProvider.java
new file mode 100644
index 0000000..53f8342
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PlatformToolProvider.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.nativeplatform.toolchain.internal;
+
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+
+public interface PlatformToolProvider extends ToolProvider {
+    String getObjectFileExtension();
+
+    String getPCHFileExtension();
+
+    String getExecutableName(String executablePath);
+
+    String getSharedLibraryName(String libraryPath);
+
+    String getSharedLibraryLinkFileName(String libraryPath);
+
+    String getStaticLibraryName(String libraryPath);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtil.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtil.java
new file mode 100644
index 0000000..8591e7b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.apache.commons.io.FileUtils;
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+public class PrefixHeaderFileGeneratorUtil {
+    public static void generatePCHFile(Set<String> headers, File headerFile) {
+        if (!headerFile.getParentFile().exists()) {
+            headerFile.getParentFile().mkdirs();
+        }
+
+        try {
+            FileUtils.writeLines(headerFile, CollectionUtils.collect(CollectionUtils.toList(headers), new Transformer<String, String>() {
+                @Override
+                public String transform(String header) {
+                    if (header.startsWith("<")) {
+                        return "#include ".concat(header);
+                    } else {
+                        return "#include \"".concat(header).concat("\"");
+                    }
+                }
+            }));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ToolType.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ToolType.java
new file mode 100644
index 0000000..944513f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/ToolType.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.nativeplatform.toolchain.internal;
+
+import org.gradle.util.GUtil;
+
+public enum ToolType {
+    CPP_COMPILER("C++ compiler"),
+    C_COMPILER("C compiler"),
+    OBJECTIVECPP_COMPILER("Objective-C++ compiler"),
+    OBJECTIVEC_COMPILER("Objective-C compiler"),
+    WINDOW_RESOURCES_COMPILER("Windows resources 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/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailablePlatformToolProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailablePlatformToolProvider.java
new file mode 100644
index 0000000..c489330
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailablePlatformToolProvider.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal;
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+import org.gradle.util.TreeVisitor;
+
+public class UnavailablePlatformToolProvider implements PlatformToolProvider {
+    private final ToolSearchResult failure;
+    private final OperatingSystemInternal targetOperatingSystem;
+
+    public UnavailablePlatformToolProvider(OperatingSystemInternal targetOperatingSystem, ToolSearchResult failure) {
+        this.targetOperatingSystem = targetOperatingSystem;
+        this.failure = failure;
+    }
+
+    public boolean isAvailable() {
+        return false;
+    }
+
+    public void explain(TreeVisitor<? super String> visitor) {
+        failure.explain(visitor);
+    }
+
+    private RuntimeException failure() {
+        TreeFormatter formatter = new TreeFormatter();
+        this.explain(formatter);
+        return new GradleException(formatter.toString());
+    }
+
+    @Override
+    public String getObjectFileExtension() {
+        return null;
+    }
+
+    @Override
+    public String getPCHFileExtension() {
+        return null;
+    }
+
+    public String getExecutableName(String executablePath) {
+        return targetOperatingSystem.getInternalOs().getExecutableName(executablePath);
+    }
+
+    public String getSharedLibraryName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getSharedLibraryName(libraryPath);
+    }
+
+    public String getSharedLibraryLinkFileName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getSharedLibraryName(libraryPath);
+    }
+
+    public String getStaticLibraryName(String libraryPath) {
+        return targetOperatingSystem.getInternalOs().getStaticLibraryName(libraryPath);
+    }
+
+    @Override
+    public <T> T get(Class<T> toolType) {
+        throw new IllegalArgumentException(String.format("Don't know how to provide tool of type %s.", toolType.getSimpleName()));
+    }
+
+    @Override
+    public <T extends CompileSpec> Compiler<T> newCompiler(Class<T> specType) {
+        throw failure();
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/clang/ClangToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/clang/ClangToolChain.java
new file mode 100644
index 0000000..b987475
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/clang/ClangToolChain.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.nativeplatform.toolchain.internal.clang;
+
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
+import org.gradle.nativeplatform.toolchain.internal.gcc.DefaultGccPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory;
+import org.gradle.process.internal.ExecActionFactory;
+
+public class ClangToolChain extends AbstractGccCompatibleToolChain implements Clang {
+    public static final String DEFAULT_NAME = "clang";
+
+    public ClangToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, CompilerMetaDataProviderFactory metaDataProviderFactory, Instantiator instantiator) {
+        super(name, buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, metaDataProviderFactory.clang(), instantiator);
+    }
+
+    @Override
+    protected void configureDefaultTools(DefaultGccPlatformToolChain toolChain) {
+        toolChain.getLinker().setExecutable("clang++");
+        toolChain.getcCompiler().setExecutable("clang");
+        toolChain.getCppCompiler().setExecutable("clang++");
+        toolChain.getObjcCompiler().setExecutable("clang");
+        toolChain.getObjcppCompiler().setExecutable("clang++");
+        toolChain.getAssembler().setExecutable("clang");
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "Clang";
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/AssembleSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/AssembleSpec.java
new file mode 100644
index 0000000..0376ac5
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/AssembleSpec.java
@@ -0,0 +1,26 @@
+/*
+ * 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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+/**
+ * A compile spec that will be used to generate object files for combining into a native binary.
+ */
+public interface AssembleSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CCompileSpec.java
new file mode 100644
index 0000000..efa0605
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface CCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CPCHCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CPCHCompileSpec.java
new file mode 100644
index 0000000..007a8c9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CPCHCompileSpec.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface CPCHCompileSpec extends NativeCompileSpec {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CppCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CppCompileSpec.java
new file mode 100644
index 0000000..cb985d1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface CppCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CppPCHCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CppPCHCompileSpec.java
new file mode 100644
index 0000000..49f4ac7
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/CppPCHCompileSpec.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface CppPCHCompileSpec extends NativeCompileSpec {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCCompileSpec.java
new file mode 100644
index 0000000..245e932
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCCompileSpec.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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface ObjectiveCCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCPCHCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCPCHCompileSpec.java
new file mode 100644
index 0000000..fa85aae
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCPCHCompileSpec.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface ObjectiveCPCHCompileSpec extends NativeCompileSpec {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppCompileSpec.java
new file mode 100644
index 0000000..e9b7963
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppCompileSpec.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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface ObjectiveCppCompileSpec extends NativeCompileSpec {
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppPCHCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppPCHCompileSpec.java
new file mode 100644
index 0000000..cf26c59
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/ObjectiveCppPCHCompileSpec.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface ObjectiveCppPCHCompileSpec extends NativeCompileSpec {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/WindowsResourceCompileSpec.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/WindowsResourceCompileSpec.java
new file mode 100644
index 0000000..9cfeb91
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/compilespec/WindowsResourceCompileSpec.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.nativeplatform.toolchain.internal.compilespec;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public interface WindowsResourceCompileSpec extends NativeCompileSpec {
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java
new file mode 100644
index 0000000..abf76fe
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.Actions;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.GccCompatibleToolChain;
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.NativePlatformToolChain;
+import org.gradle.nativeplatform.toolchain.internal.ExtendableToolChain;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+import org.gradle.nativeplatform.toolchain.internal.UnavailablePlatformToolProvider;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProvider;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.GccVersionResult;
+import org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolSearchResult;
+import org.gradle.nativeplatform.toolchain.internal.tools.DefaultGccCommandLineToolConfiguration;
+import org.gradle.nativeplatform.toolchain.internal.tools.GccCommandLineToolConfigurationInternal;
+import org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath;
+import org.gradle.platform.base.internal.toolchain.ToolChainAvailability;
+import org.gradle.process.internal.ExecActionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+/**
+ * A tool chain that has GCC semantics.
+ */
+public abstract class AbstractGccCompatibleToolChain extends ExtendableToolChain<GccPlatformToolChain> implements GccCompatibleToolChain {
+    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGccCompatibleToolChain.class);
+    private final ExecActionFactory execActionFactory;
+    private final ToolSearchPath toolSearchPath;
+    private final List<TargetPlatformConfiguration> platformConfigs = new ArrayList<TargetPlatformConfiguration>();
+    private final CompilerMetaDataProvider metaDataProvider;
+    private final Instantiator instantiator;
+    private int configInsertLocation;
+
+    public AbstractGccCompatibleToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, CompilerMetaDataProvider metaDataProvider, Instantiator instantiator) {
+        this(name, buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, new ToolSearchPath(operatingSystem), metaDataProvider, instantiator);
+    }
+
+    AbstractGccCompatibleToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, ToolSearchPath tools, CompilerMetaDataProvider metaDataProvider, Instantiator instantiator) {
+        super(name, buildOperationProcessor, operatingSystem, fileResolver);
+        this.execActionFactory = execActionFactory;
+        this.toolSearchPath = tools;
+        this.metaDataProvider = metaDataProvider;
+        this.instantiator = instantiator;
+
+        target(new Intel32Architecture());
+        target(new Intel64Architecture());
+        configInsertLocation = 0;
+    }
+
+    protected CommandLineToolSearchResult locate(GccCommandLineToolConfigurationInternal gccTool) {
+        return toolSearchPath.locate(gccTool.getToolType(), gccTool.getExecutable());
+    }
+
+    public List<File> getPath() {
+        return toolSearchPath.getPath();
+    }
+
+    public void path(Object... pathEntries) {
+        for (Object path : pathEntries) {
+            toolSearchPath.path(resolve(path));
+        }
+    }
+
+    protected CompilerMetaDataProvider getMetaDataProvider() {
+        return metaDataProvider;
+    }
+
+    public void target(String platformName) {
+        target(platformName, Actions.<NativePlatformToolChain>doNothing());
+    }
+
+    public void target(String platformName, Action<? super GccPlatformToolChain> action) {
+        target(new DefaultTargetPlatformConfiguration(asList(platformName), action));
+    }
+
+    public void target(List<String> platformNames, Action<? super GccPlatformToolChain> action) {
+        target(new DefaultTargetPlatformConfiguration(platformNames, action));
+    }
+
+    private void target(TargetPlatformConfiguration targetPlatformConfiguration) {
+        platformConfigs.add(configInsertLocation, targetPlatformConfiguration);
+        configInsertLocation++;
+    }
+
+    public PlatformToolProvider select(NativePlatformInternal targetPlatform) {
+        TargetPlatformConfiguration targetPlatformConfigurationConfiguration = getPlatformConfiguration(targetPlatform);
+        ToolChainAvailability result = new ToolChainAvailability();
+        if (targetPlatformConfigurationConfiguration == null) {
+            result.unavailable(String.format("Don't know how to build for platform '%s'.", targetPlatform.getName()));
+            return new UnavailablePlatformToolProvider(targetPlatform.getOperatingSystem(), result);
+        }
+
+        DefaultGccPlatformToolChain configurableToolChain = instantiator.newInstance(DefaultGccPlatformToolChain.class, targetPlatform);
+        addDefaultTools(configurableToolChain);
+        configureDefaultTools(configurableToolChain);
+        targetPlatformConfigurationConfiguration.apply(configurableToolChain);
+        configureActions.execute(configurableToolChain);
+
+        initTools(configurableToolChain, result);
+        if (!result.isAvailable()) {
+            return new UnavailablePlatformToolProvider(targetPlatform.getOperatingSystem(), result);
+        }
+
+        return new GccPlatformToolProvider(buildOperationProcessor, targetPlatform.getOperatingSystem(), toolSearchPath, configurableToolChain, execActionFactory, configurableToolChain.isCanUseCommandFile());
+    }
+
+    protected void initTools(DefaultGccPlatformToolChain platformToolChain, ToolChainAvailability availability) {
+        // Attempt to determine whether the compiler is the correct implementation
+        boolean found = false;
+        for (GccCommandLineToolConfigurationInternal tool : platformToolChain.getCompilers()) {
+            CommandLineToolSearchResult compiler = locate(tool);
+            if (compiler.isAvailable()) {
+                GccVersionResult versionResult = getMetaDataProvider().getGccMetaData(compiler.getTool(), platformToolChain.getCompilerProbeArgs());
+                availability.mustBeAvailable(versionResult);
+                if (!versionResult.isAvailable()) {
+                    return;
+                }
+                // Assume all the other compilers are ok, if they happen to be installed
+                LOGGER.debug("Found {} with version {}", ToolType.C_COMPILER.getToolName(), versionResult);
+                found = true;
+                initForImplementation(platformToolChain, versionResult);
+                break;
+            }
+        }
+
+        // Attempt to locate each tool
+        for (GccCommandLineToolConfigurationInternal tool : platformToolChain.getTools()) {
+            found |= toolSearchPath.locate(tool.getToolType(), tool.getExecutable()).isAvailable();
+        }
+        if (!found) {
+            // No tools found - report just the C compiler as missing
+            // TODO - report whichever tool is actually required, eg if there's only assembler source, complain about the assembler
+            GccCommandLineToolConfigurationInternal cCompiler = platformToolChain.getcCompiler();
+            availability.mustBeAvailable(locate(cCompiler));
+        }
+    }
+
+    protected void initForImplementation(DefaultGccPlatformToolChain platformToolChain, GccVersionResult versionResult) {
+    }
+
+    private void addDefaultTools(DefaultGccPlatformToolChain toolChain) {
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.C_COMPILER, "gcc"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.CPP_COMPILER, "g++"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.LINKER, "g++"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.STATIC_LIB_ARCHIVER, "ar"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.OBJECTIVECPP_COMPILER, "g++"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.OBJECTIVEC_COMPILER, "gcc"));
+        toolChain.add(instantiator.newInstance(DefaultGccCommandLineToolConfiguration.class, ToolType.ASSEMBLER, "gcc"));
+    }
+
+    protected void configureDefaultTools(DefaultGccPlatformToolChain toolChain) {
+    }
+
+    protected TargetPlatformConfiguration getPlatformConfiguration(NativePlatformInternal targetPlatform) {
+        for (TargetPlatformConfiguration platformConfig : platformConfigs) {
+            if (platformConfig.supportsPlatform(targetPlatform)) {
+                return platformConfig;
+            }
+        }
+        return null;
+    }
+
+    private class Intel32Architecture implements TargetPlatformConfiguration {
+
+        public boolean supportsPlatform(NativePlatformInternal targetPlatform) {
+            return targetPlatform.getOperatingSystem().isCurrent() && targetPlatform.getArchitecture().isI386();
+        }
+
+        public void apply(DefaultGccPlatformToolChain gccToolChain) {
+            gccToolChain.compilerProbeArgs("-m32");
+            Action<List<String>> m32args = new Action<List<String>>() {
+                public void execute(List<String> args) {
+                    args.add("-m32");
+                }
+            };
+            gccToolChain.getCppCompiler().withArguments(m32args);
+            gccToolChain.getcCompiler().withArguments(m32args);
+            gccToolChain.getObjcCompiler().withArguments(m32args);
+            gccToolChain.getObjcppCompiler().withArguments(m32args);
+            gccToolChain.getLinker().withArguments(m32args);
+            gccToolChain.getAssembler().withArguments(m32args);
+
+        }
+    }
+
+    private class Intel64Architecture implements TargetPlatformConfiguration {
+        public boolean supportsPlatform(NativePlatformInternal targetPlatform) {
+            return targetPlatform.getOperatingSystem().isCurrent()
+                    && targetPlatform.getArchitecture().isAmd64();
+        }
+
+        public void apply(DefaultGccPlatformToolChain gccToolChain) {
+            gccToolChain.compilerProbeArgs("-m64");
+            Action<List<String>> m64args = new Action<List<String>>() {
+                public void execute(List<String> args) {
+                    args.add("-m64");
+                }
+            };
+            gccToolChain.getCppCompiler().withArguments(m64args);
+            gccToolChain.getcCompiler().withArguments(m64args);
+            gccToolChain.getObjcCompiler().withArguments(m64args);
+            gccToolChain.getObjcppCompiler().withArguments(m64args);
+            gccToolChain.getLinker().withArguments(m64args);
+            gccToolChain.getAssembler().withArguments(m64args);
+        }
+    }
+
+    private static class DefaultTargetPlatformConfiguration implements TargetPlatformConfiguration {
+        //TODO this should be a container of platforms
+        private final Collection<String> platformNames;
+        private Action<? super GccPlatformToolChain> configurationAction;
+
+        public DefaultTargetPlatformConfiguration(Collection<String> targetPlatformNames, Action<? super GccPlatformToolChain> configurationAction) {
+            this.platformNames = targetPlatformNames;
+            this.configurationAction = configurationAction;
+        }
+
+        public boolean supportsPlatform(NativePlatformInternal targetPlatform) {
+            return platformNames.contains(targetPlatform.getName());
+        }
+
+        public void apply(DefaultGccPlatformToolChain platformToolChain) {
+            configurationAction.execute(platformToolChain);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ArStaticLibraryArchiver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ArStaticLibraryArchiver.java
new file mode 100755
index 0000000..55bc2e2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ArStaticLibraryArchiver.java
@@ -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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.BuildOperationQueue;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A static library archiver based on the GNU 'ar' utility
+ */
+class ArStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
+    private final CommandLineToolInvocationWorker commandLineToolInvocationWorker;
+    private final ArgsTransformer<StaticLibraryArchiverSpec> argsTransformer = new ArchiverSpecToArguments();
+    private final CommandLineToolContext invocationContext;
+    private final BuildOperationProcessor buildOperationProcessor;
+
+    ArStaticLibraryArchiver(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext) {
+        this.buildOperationProcessor = buildOperationProcessor;
+        this.commandLineToolInvocationWorker = commandLineToolInvocationWorker;
+        this.invocationContext = invocationContext;
+    }
+
+    public WorkResult execute(StaticLibraryArchiverSpec spec) {
+        deletePreviousOutput(spec);
+
+        BuildOperationQueue<CommandLineToolInvocation> queue = buildOperationProcessor.newQueue(commandLineToolInvocationWorker, spec.getOperationLogger().getLogLocation());
+        List<String> args = argsTransformer.transform(spec);
+        invocationContext.getArgAction().execute(args);
+        CommandLineToolInvocation invocation = invocationContext.createInvocation(
+                String.format("archiving %s", spec.getOutputFile().getName()), args, spec.getOperationLogger());
+        queue.add(invocation);
+        queue.waitForCompletion();
+        return new SimpleWorkResult(true);
+    }
+
+    private void deletePreviousOutput(StaticLibraryArchiverSpec spec) {
+        // Need to delete the previous archive, otherwise stale object files will remain
+        if (!spec.getOutputFile().isFile()) {
+            return;
+        }
+        if (!(spec.getOutputFile().delete())) {
+            throw new GradleException("Create static archive failed: could not delete previous archive");
+        }
+    }
+
+    private static class ArchiverSpecToArguments implements ArgsTransformer<StaticLibraryArchiverSpec> {
+        public List<String> transform(StaticLibraryArchiverSpec spec) {
+            List<String> args = new ArrayList<String>();
+            // -r : Add files to static archive, creating if required
+            // -c : Don't write message to standard error when creating archive
+            // -s : Create an object file index (equivalent to running 'ranlib')
+            args.add("-rcs");
+            args.addAll(spec.getAllArgs());
+            args.add(spec.getOutputFile().getAbsolutePath());
+            for (File file : spec.getObjectFiles()) {
+                args.add(file.getAbsolutePath());
+            }
+            return args;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/Assembler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/Assembler.java
new file mode 100755
index 0000000..a49300f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/Assembler.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
+
+class Assembler extends GccCompatibleNativeCompiler<AssembleSpec> {
+
+    Assembler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new AssemblerArgsTransformer(), Transformers.<AssembleSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class AssemblerArgsTransformer  extends GccCompilerArgsTransformer<AssembleSpec> {
+        protected String getLanguage() {
+            return "assembler";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompiler.java
new file mode 100755
index 0000000..3e4af29
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompiler.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
+
+class CCompiler extends GccCompatibleNativeCompiler<CCompileSpec> {
+
+    CCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new CCompileArgsTransformer(), Transformers.<CCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class CCompileArgsTransformer extends GccCompilerArgsTransformer<CCompileSpec> {
+        protected String getLanguage() {
+            return "c";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CPCHCompiler.java
new file mode 100644
index 0000000..90c9297
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CPCHCompiler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec;
+
+public class CPCHCompiler extends GccCompatibleNativeCompiler<CPCHCompileSpec> {
+    public CPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new CPCHCompileArgsTransformer(), Transformers.<CPCHCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class CPCHCompileArgsTransformer extends GccCompilerArgsTransformer<CPCHCompileSpec> {
+        protected String getLanguage() {
+            return "c-header";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompiler.java
new file mode 100755
index 0000000..0441cb0
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec;
+
+class CppCompiler extends GccCompatibleNativeCompiler<CppCompileSpec>  {
+
+    CppCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new CppCompileArgsTransformer(), Transformers.<CppCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class CppCompileArgsTransformer extends GccCompilerArgsTransformer<CppCompileSpec> {
+        protected String getLanguage() {
+            return "c++";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppPCHCompiler.java
new file mode 100644
index 0000000..56a1c94
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppPCHCompiler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec;
+
+public class CppPCHCompiler extends GccCompatibleNativeCompiler<CppPCHCompileSpec> {
+    public CppPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new CppPCHCompileArgsTransformer(), Transformers.<CppPCHCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class CppPCHCompileArgsTransformer extends GccCompilerArgsTransformer<CppPCHCompileSpec> {
+        protected String getLanguage() {
+            return "c++-header";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/DefaultGccPlatformToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/DefaultGccPlatformToolChain.java
new file mode 100644
index 0000000..4af2041
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/DefaultGccPlatformToolChain.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+import org.gradle.nativeplatform.toolchain.internal.tools.DefaultGccCommandLineToolConfiguration;
+import org.gradle.nativeplatform.toolchain.internal.tools.GccCommandLineToolConfigurationInternal;
+import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
+
+import java.util.*;
+
+public class DefaultGccPlatformToolChain implements GccPlatformToolChain, ToolRegistry {
+    private final NativePlatform platform;
+    private boolean canUseCommandFile = true;
+    private List<String> compilerProbeArgs = new ArrayList<String>();
+    private final Map<ToolType, GccCommandLineToolConfigurationInternal> tools = new HashMap<ToolType, GccCommandLineToolConfigurationInternal>();
+
+    public DefaultGccPlatformToolChain(NativePlatform platform) {
+        this.platform = platform;
+    }
+
+    public boolean isCanUseCommandFile() {
+        return canUseCommandFile;
+    }
+
+    public void setCanUseCommandFile(boolean canUseCommandFile) {
+        this.canUseCommandFile = canUseCommandFile;
+    }
+
+    public List<String> getCompilerProbeArgs() {
+        return compilerProbeArgs;
+    }
+
+    public void compilerProbeArgs(String... args) {
+        this.compilerProbeArgs.addAll(Arrays.asList(args));
+    }
+
+    public GccCommandLineToolConfigurationInternal getTool(ToolType toolType) {
+        return tools.get(toolType);
+    }
+
+    public Collection<GccCommandLineToolConfigurationInternal> getTools() {
+        return tools.values();
+    }
+
+    public Collection<GccCommandLineToolConfigurationInternal> getCompilers() {
+        return Arrays.asList(tools.get(ToolType.C_COMPILER), tools.get(ToolType.CPP_COMPILER), tools.get(ToolType.OBJECTIVEC_COMPILER), tools.get(ToolType.OBJECTIVECPP_COMPILER));
+    }
+
+    public void add(DefaultGccCommandLineToolConfiguration tool) {
+        tools.put(tool.getToolType(), tool);
+    }
+
+    public NativePlatform getPlatform() {
+        return platform;
+    }
+
+    public GccCommandLineToolConfigurationInternal getcCompiler() {
+        return tools.get(ToolType.C_COMPILER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getCppCompiler() {
+        return tools.get(ToolType.CPP_COMPILER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getObjcCompiler() {
+        return tools.get(ToolType.OBJECTIVEC_COMPILER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getObjcppCompiler() {
+        return tools.get(ToolType.OBJECTIVECPP_COMPILER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getAssembler() {
+        return tools.get(ToolType.ASSEMBLER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getLinker() {
+        return tools.get(ToolType.LINKER);
+    }
+
+    public GccCommandLineToolConfigurationInternal getStaticLibArchiver() {
+        return tools.get(ToolType.STATIC_LIB_ARCHIVER);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompiler.java
new file mode 100644
index 0000000..9e43e4a
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompiler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class GccCompatibleNativeCompiler<T extends NativeCompileSpec> extends NativeCompiler<T> {
+
+    GccCompatibleNativeCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, final ArgsTransformer<T> argsTransformer, Transformer<T, T> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, argsTransformer, specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected List<String> getOutputArgs(T spec, File outputFile) {
+        return Arrays.asList("-o", outputFile.getAbsolutePath());
+    }
+
+    @Override
+    protected void addOptionsFileArgs(List<String> args, File tempDir) {
+        OptionsFileArgsWriter writer = new GccOptionsFileArgsWriter(tempDir);
+        // modifies args in place
+        writer.execute(args);
+    }
+
+    @Override
+    protected List<String> getPCHArgs(T spec) {
+        List<String> pchArgs = new ArrayList<String>();
+        if (spec.getPrefixHeaderFile() != null) {
+            pchArgs.add("-include");
+            pchArgs.add(spec.getPrefixHeaderFile().getAbsolutePath());
+        }
+        return pchArgs;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompilerArgsTransformer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompilerArgsTransformer.java
new file mode 100644
index 0000000..7ae6e7d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompilerArgsTransformer.java
@@ -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.nativeplatform.toolchain.internal.gcc;
+
+import com.google.common.collect.Lists;
+import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
+import org.gradle.nativeplatform.toolchain.internal.MacroArgsConverter;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Maps common options for C/C++ compiling with GCC
+ */
+abstract class GccCompilerArgsTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T> {
+    public List<String> transform(T spec) {
+        List<String> args = Lists.newArrayList();
+        addToolSpecificArgs(spec, args);
+        addMacroArgs(spec, args);
+        addUserArgs(spec, args);
+        addIncludeArgs(spec, args);
+        return args;
+    }
+
+    protected void addToolSpecificArgs(T spec, List<String> args) {
+        Collections.addAll(args, "-x", getLanguage());
+        args.add("-c");
+        if (spec.isPositionIndependentCode()) {
+            if (!spec.getTargetPlatform().getOperatingSystem().isWindows()) {
+                args.add("-fPIC");
+            }
+        }
+    }
+
+    protected void addIncludeArgs(T spec, List<String> args) {
+        for (File file : spec.getIncludeRoots()) {
+            args.add("-I");
+            args.add(file.getAbsolutePath());
+        }
+    }
+
+    protected void addMacroArgs(T spec, List<String> args) {
+        for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
+            args.add("-D" + macroArg);
+        }
+    }
+
+    protected void addUserArgs(T spec, List<String> args) {
+        args.addAll(spec.getAllArgs());
+    }
+
+    protected abstract String getLanguage();
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinker.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinker.java
new file mode 100755
index 0000000..d540092
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinker.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.BuildOperationQueue;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.internal.LinkerSpec;
+import org.gradle.nativeplatform.internal.SharedLibraryLinkerSpec;
+import org.gradle.nativeplatform.platform.OperatingSystem;
+import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+class GccLinker implements Compiler<LinkerSpec> {
+
+    private final CommandLineToolInvocationWorker commandLineToolInvocationWorker;
+    private final ArgsTransformer<LinkerSpec> argsTransformer;
+    private final CommandLineToolContext invocationContext;
+    private final boolean useCommandFile;
+    private final BuildOperationProcessor buildOperationProcessor;
+
+
+    GccLinker(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, boolean useCommandFile) {
+        this.buildOperationProcessor = buildOperationProcessor;
+        this.argsTransformer = new GccLinkerArgsTransformer();
+        this.invocationContext = invocationContext;
+        this.useCommandFile = useCommandFile;
+        this.commandLineToolInvocationWorker = commandLineToolInvocationWorker;
+    }
+
+    public WorkResult execute(LinkerSpec spec) {
+        BuildOperationQueue<CommandLineToolInvocation> queue = buildOperationProcessor.newQueue(commandLineToolInvocationWorker, spec.getOperationLogger().getLogLocation());
+
+        List<String> args = argsTransformer.transform(spec);
+        invocationContext.getArgAction().execute(args);
+        if (useCommandFile) {
+            new GccOptionsFileArgsWriter(spec.getTempDir()).execute(args);
+        }
+        CommandLineToolInvocation invocation = invocationContext.createInvocation(
+                String.format("linking %s", spec.getOutputFile().getName()), args, spec.getOperationLogger());
+        queue.add(invocation);
+        queue.waitForCompletion();
+        return new SimpleWorkResult(true);
+    }
+
+    private static class GccLinkerArgsTransformer implements ArgsTransformer<LinkerSpec> {
+        public List<String> transform(LinkerSpec spec) {
+            List<String> args = new ArrayList<String>();
+            
+            args.addAll(spec.getSystemArgs());
+
+            if (spec instanceof SharedLibraryLinkerSpec) {
+                args.add("-shared");
+                maybeSetInstallName((SharedLibraryLinkerSpec) spec, args);
+            }
+            args.add("-o");
+            args.add(spec.getOutputFile().getAbsolutePath());
+            for (File file : spec.getObjectFiles()) {
+                args.add(file.getAbsolutePath());
+            }
+            for (File file : spec.getLibraries()) {
+                args.add(file.getAbsolutePath());
+            }
+            if (!spec.getLibraryPath().isEmpty()) {
+                throw new UnsupportedOperationException("Library Path not yet supported on GCC");
+            }
+
+            for (String userArg : spec.getArgs()) {
+                args.add(userArg);
+            }
+
+            return args;
+        }
+
+        private void maybeSetInstallName(SharedLibraryLinkerSpec spec, List<String> args) {
+            String installName = spec.getInstallName();
+            OperatingSystem targetOs = spec.getTargetPlatform().getOperatingSystem();
+
+            if (installName == null || targetOs.isWindows()) {
+                return;
+            }
+            if (targetOs.isMacOsX()) {
+                args.add("-Wl,-install_name," + installName);
+            } else {
+                args.add("-Wl,-soname," + installName);
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriter.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriter.java
new file mode 100644
index 0000000..33c10f5
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriter.java
@@ -0,0 +1,53 @@
+/*
+ * 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.nativeplatform.toolchain.internal.gcc;
+
+import com.google.common.collect.Lists;
+import org.gradle.platform.base.internal.toolchain.ArgWriter;
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Uses an option file for arguments passed to GCC if possible.
+ * Certain GCC options do not function correctly when included in an option file, so include these directly on the command line as well.
+ */
+class GccOptionsFileArgsWriter extends OptionsFileArgsWriter {
+    private static final List<String> CLI_ONLY_ARGS = Arrays.asList("-m32", "-m64");
+
+    public GccOptionsFileArgsWriter(File tempDir) {
+        super(ArgWriter.unixStyleFactory(), tempDir);
+    }
+
+    @Override
+    public List<String> transformArgs(List<String> originalArgs, File tempDir) {
+        List<String> commandLineOnlyArgs = getCommandLineOnlyArgs(originalArgs);
+        List<String> finalArgs = Lists.newArrayList();
+        finalArgs.addAll(super.transformArgs(originalArgs, tempDir));
+        finalArgs.addAll(commandLineOnlyArgs);
+        return finalArgs;
+    }
+
+    private List<String> getCommandLineOnlyArgs(List<String> allArgs) {
+        List<String> commandLineOnlyArgs = new ArrayList<String>(allArgs);
+        commandLineOnlyArgs.retainAll(CLI_ONLY_ARGS);
+        return commandLineOnlyArgs;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccPlatformToolProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccPlatformToolProvider.java
new file mode 100644
index 0000000..b06b164
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccPlatformToolProvider.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.internal.LinkerSpec;
+import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal;
+import org.gradle.nativeplatform.toolchain.internal.*;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.*;
+import org.gradle.nativeplatform.toolchain.internal.tools.GccCommandLineToolConfigurationInternal;
+import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
+import org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath;
+import org.gradle.process.internal.ExecActionFactory;
+
+class GccPlatformToolProvider extends AbstractPlatformToolProvider {
+    private final ToolSearchPath toolSearchPath;
+    private final ToolRegistry toolRegistry;
+    private final ExecActionFactory execActionFactory;
+    private final boolean useCommandFile;
+
+    GccPlatformToolProvider(BuildOperationProcessor buildOperationProcessor, OperatingSystemInternal targetOperatingSystem, ToolSearchPath toolSearchPath, ToolRegistry toolRegistry, ExecActionFactory execActionFactory, boolean useCommandFile) {
+        super(buildOperationProcessor, targetOperatingSystem);
+        this.toolRegistry = toolRegistry;
+        this.toolSearchPath = toolSearchPath;
+        this.useCommandFile = useCommandFile;
+        this.execActionFactory = execActionFactory;
+    }
+
+    @Override
+    protected Compiler<CppCompileSpec> createCppCompiler() {
+        GccCommandLineToolConfigurationInternal cppCompilerTool = toolRegistry.getTool(ToolType.CPP_COMPILER);
+        CppCompiler cppCompiler = new CppCompiler(buildOperationProcessor, commandLineTool(cppCompilerTool), context(cppCompilerTool), getObjectFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<CppCompileSpec>(cppCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createCppPCHCompiler() {
+        GccCommandLineToolConfigurationInternal cppCompilerTool = toolRegistry.getTool(ToolType.CPP_COMPILER);
+        CppPCHCompiler cppPCHCompiler = new CppPCHCompiler(buildOperationProcessor, commandLineTool(cppCompilerTool), context(cppCompilerTool), getPCHFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<CppPCHCompileSpec>(cppPCHCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<CCompileSpec> createCCompiler() {
+        GccCommandLineToolConfigurationInternal cCompilerTool = toolRegistry.getTool(ToolType.C_COMPILER);
+        CCompiler cCompiler = new CCompiler(buildOperationProcessor, commandLineTool(cCompilerTool), context(cCompilerTool), getObjectFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<CCompileSpec>(cCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createCPCHCompiler() {
+        GccCommandLineToolConfigurationInternal cCompilerTool = toolRegistry.getTool(ToolType.C_COMPILER);
+        CPCHCompiler cpchCompiler = new CPCHCompiler(buildOperationProcessor, commandLineTool(cCompilerTool), context(cCompilerTool), getPCHFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<CPCHCompileSpec>(cpchCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<ObjectiveCppCompileSpec> createObjectiveCppCompiler() {
+        GccCommandLineToolConfigurationInternal objectiveCppCompilerTool = toolRegistry.getTool(ToolType.OBJECTIVECPP_COMPILER);
+        ObjectiveCppCompiler objectiveCppCompiler = new ObjectiveCppCompiler(buildOperationProcessor, commandLineTool(objectiveCppCompilerTool), context(objectiveCppCompilerTool), getObjectFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<ObjectiveCppCompileSpec>(objectiveCppCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createObjectiveCppPCHCompiler() {
+        GccCommandLineToolConfigurationInternal objectiveCppCompilerTool = toolRegistry.getTool(ToolType.OBJECTIVECPP_COMPILER);
+        ObjectiveCppPCHCompiler objectiveCppPCHCompiler = new ObjectiveCppPCHCompiler(buildOperationProcessor, commandLineTool(objectiveCppCompilerTool), context(objectiveCppCompilerTool), getPCHFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<ObjectiveCppPCHCompileSpec>(objectiveCppPCHCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<ObjectiveCCompileSpec> createObjectiveCCompiler() {
+        GccCommandLineToolConfigurationInternal objectiveCCompilerTool = toolRegistry.getTool(ToolType.OBJECTIVEC_COMPILER);
+        ObjectiveCCompiler objectiveCCompiler = new ObjectiveCCompiler(buildOperationProcessor, commandLineTool(objectiveCCompilerTool), context(objectiveCCompilerTool), getObjectFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<ObjectiveCCompileSpec>(objectiveCCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createObjectiveCPCHCompiler() {
+        GccCommandLineToolConfigurationInternal objectiveCCompilerTool = toolRegistry.getTool(ToolType.OBJECTIVEC_COMPILER);
+        ObjectiveCPCHCompiler objectiveCPCHCompiler = new ObjectiveCPCHCompiler(buildOperationProcessor, commandLineTool(objectiveCCompilerTool), context(objectiveCCompilerTool), getPCHFileExtension(), useCommandFile);
+        return new OutputCleaningCompiler<ObjectiveCPCHCompileSpec>(objectiveCPCHCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<AssembleSpec> createAssembler() {
+        GccCommandLineToolConfigurationInternal assemblerTool = toolRegistry.getTool(ToolType.ASSEMBLER);
+        // Disable command line file for now because some custom assemblers
+        // don't understand the same arguments as GCC.
+        return new Assembler(buildOperationProcessor, commandLineTool(assemblerTool), context(assemblerTool), getObjectFileExtension(), false);
+    }
+
+    @Override
+    protected Compiler<LinkerSpec> createLinker() {
+        GccCommandLineToolConfigurationInternal linkerTool = toolRegistry.getTool(ToolType.LINKER);
+        return new GccLinker(buildOperationProcessor, commandLineTool(linkerTool), context(linkerTool), useCommandFile);
+    }
+
+    @Override
+    protected Compiler<StaticLibraryArchiverSpec> createStaticLibraryArchiver() {
+        GccCommandLineToolConfigurationInternal staticLibArchiverTool = toolRegistry.getTool(ToolType.STATIC_LIB_ARCHIVER);
+        return new ArStaticLibraryArchiver(buildOperationProcessor, commandLineTool(staticLibArchiverTool), context(staticLibArchiverTool));
+    }
+
+    private CommandLineToolInvocationWorker commandLineTool(GccCommandLineToolConfigurationInternal tool) {
+        ToolType key = tool.getToolType();
+        String exeName = tool.getExecutable();
+        return new DefaultCommandLineToolInvocationWorker(key.getToolName(), toolSearchPath.locate(key, exeName).getTool(), execActionFactory);
+    }
+
+    private CommandLineToolContext context(GccCommandLineToolConfigurationInternal toolConfiguration) {
+        MutableCommandLineToolContext baseInvocation = new DefaultMutableCommandLineToolContext();
+        // MinGW requires the path to be set
+        baseInvocation.addPath(toolSearchPath.getPath());
+        baseInvocation.addEnvironmentVar("CYGWIN", "nodosfilewarning");
+        baseInvocation.setArgAction(toolConfiguration.getArgAction());
+        return baseInvocation;
+    }
+
+    @Override
+    public String getPCHFileExtension() {
+        return ".h.gch";
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChain.java
new file mode 100755
index 0000000..41c7223
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChain.java
@@ -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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.GccVersionResult;
+import org.gradle.process.internal.ExecActionFactory;
+
+
+/**
+ * Compiler adapter for GCC.
+ */
+public class GccToolChain extends AbstractGccCompatibleToolChain implements Gcc {
+    public static final String DEFAULT_NAME = "gcc";
+
+    public GccToolChain(Instantiator instantiator, String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, CompilerMetaDataProviderFactory metaDataProviderFactory) {
+        super(name, buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, metaDataProviderFactory.gcc(), instantiator);
+    }
+
+    @Override
+    protected String getTypeName() {
+        return "GNU GCC";
+    }
+
+    @Override
+    protected void initForImplementation(DefaultGccPlatformToolChain platformToolChain, GccVersionResult versionResult) {
+        platformToolChain.setCanUseCommandFile(versionResult.getVersion().getMajor() >= 4);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCCompiler.java
new file mode 100644
index 0000000..681a4f2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCCompiler.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCCompileSpec;
+
+class ObjectiveCCompiler extends GccCompatibleNativeCompiler<ObjectiveCCompileSpec> {
+
+    ObjectiveCCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new ObjectiveCCompileArgsTransformer(), Transformers.<ObjectiveCCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class ObjectiveCCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCCompileSpec> {
+        protected String getLanguage() {
+            return "objective-c";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCPCHCompiler.java
new file mode 100644
index 0000000..2339b62
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCPCHCompiler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCPCHCompileSpec;
+
+public class ObjectiveCPCHCompiler extends GccCompatibleNativeCompiler<ObjectiveCPCHCompileSpec> {
+    public ObjectiveCPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new ObjectiveCPCHCompileArgsTransformer(), Transformers.<ObjectiveCPCHCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class ObjectiveCPCHCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCPCHCompileSpec> {
+        protected String getLanguage() {
+            return "objective-c-header";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppCompiler.java
new file mode 100644
index 0000000..3ce5df9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppCompiler.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.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppCompileSpec;
+
+class ObjectiveCppCompiler extends GccCompatibleNativeCompiler<ObjectiveCppCompileSpec> {
+
+    ObjectiveCppCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new ObjectiveCppCompileArgsTransformer(), Transformers.<ObjectiveCppCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class ObjectiveCppCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCppCompileSpec> {
+        protected String getLanguage() {
+            return "objective-c++";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppPCHCompiler.java
new file mode 100644
index 0000000..776eab1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ObjectiveCppPCHCompiler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.internal.Transformers;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppPCHCompileSpec;
+
+public class ObjectiveCppPCHCompiler extends GccCompatibleNativeCompiler<ObjectiveCppPCHCompileSpec> {
+    public ObjectiveCppPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new ObjectiveCppPCHCompileArgsTransformer(), Transformers.<ObjectiveCppPCHCompileSpec>noOpTransformer(), objectFileExtension, useCommandFile);
+    }
+
+    private static class ObjectiveCppPCHCompileArgsTransformer extends GccCompilerArgsTransformer<ObjectiveCppPCHCompileSpec> {
+        protected String getLanguage() {
+            return "objective-c++-header";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/TargetPlatformConfiguration.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/TargetPlatformConfiguration.java
new file mode 100644
index 0000000..59a0839
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/TargetPlatformConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc;
+
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+
+public interface TargetPlatformConfiguration {
+    /**
+     * Returns whether a platform is supported or not.
+     */
+    boolean supportsPlatform(NativePlatformInternal targetPlatform);
+
+    /**
+     *  applies a platform specific toolchain configuration
+     */
+    void apply(DefaultGccPlatformToolChain platformToolChain);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProvider.java
new file mode 100644
index 0000000..d207cfb
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc.version;
+
+import java.io.File;
+import java.util.List;
+
+public interface CompilerMetaDataProvider {
+    public GccVersionResult getGccMetaData(File gccBinary, List<String> additionalArgs);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProviderFactory.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProviderFactory.java
new file mode 100644
index 0000000..6247fc1
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/CompilerMetaDataProviderFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc.version;
+
+import org.gradle.api.internal.file.FileLookup;
+import org.gradle.process.internal.DefaultExecAction;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CompilerMetaDataProviderFactory {
+    private final CachingCompilerMetaDataProvider gcc;
+    private final CachingCompilerMetaDataProvider clang;
+
+    public CompilerMetaDataProviderFactory(final FileLookup fileLookup) {
+        ExecActionFactory factory = new ExecActionFactory() {
+            public ExecAction newExecAction() {
+                return new DefaultExecAction(fileLookup.getFileResolver());
+            }
+        };
+        gcc = new CachingCompilerMetaDataProvider(GccVersionDeterminer.forGcc(factory));
+        clang = new CachingCompilerMetaDataProvider(GccVersionDeterminer.forClang(factory));
+    }
+
+    public CompilerMetaDataProvider gcc() {
+        return gcc;
+    }
+
+    public CompilerMetaDataProvider clang() {
+        return clang;
+    }
+
+    private static class CachingCompilerMetaDataProvider implements CompilerMetaDataProvider {
+        private final CompilerMetaDataProvider delegate;
+        private final Map<Key, GccVersionResult> resultMap = new HashMap<Key, GccVersionResult>();
+
+        private CachingCompilerMetaDataProvider(CompilerMetaDataProvider delegate) {
+            this.delegate = delegate;
+        }
+
+        public GccVersionResult getGccMetaData(File gccBinary, List<String> additionalArgs) {
+            Key key = new Key(gccBinary, additionalArgs);
+            GccVersionResult result = resultMap.get(key);
+            if (result == null) {
+                result = delegate.getGccMetaData(gccBinary, additionalArgs);
+                resultMap.put(key, result);
+            }
+            return result;
+        }
+    }
+
+    private static class Key {
+        final File gccBinary;
+        final List<String> args;
+
+        private Key(File gccBinary, List<String> args) {
+            this.gccBinary = gccBinary;
+            this.args = args;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            Key other = (Key) obj;
+            return other.gccBinary.equals(gccBinary) && other.args.equals(args);
+        }
+
+        @Override
+        public int hashCode() {
+            return gccBinary.hashCode() ^ args.hashCode();
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminer.java
new file mode 100644
index 0000000..60a9ff6
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminer.java
@@ -0,0 +1,218 @@
+/*
+ * 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.nativeplatform.toolchain.internal.gcc.version;
+
+import com.google.common.base.Joiner;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal;
+import org.gradle.nativeplatform.platform.internal.Architectures;
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform;
+import org.gradle.process.ExecResult;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.util.TreeVisitor;
+import org.gradle.util.VersionNumber;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Given a File pointing to an (existing) gcc/g++/clang/clang++ binary, extracts the version number and default architecture by running with -dM -E and scraping the output.
+ */
+public class GccVersionDeterminer implements CompilerMetaDataProvider {
+    private static final Pattern DEFINE_PATTERN = Pattern.compile("\\s*#define\\s+(\\S+)\\s+(.*)");
+    private final ExecActionFactory execActionFactory;
+    private final boolean clang;
+
+    public static GccVersionDeterminer forGcc(ExecActionFactory execActionFactory) {
+        return new GccVersionDeterminer(execActionFactory, false);
+    }
+
+    public static GccVersionDeterminer forClang(ExecActionFactory execActionFactory) {
+        return new GccVersionDeterminer(execActionFactory, true);
+    }
+
+    GccVersionDeterminer(ExecActionFactory execActionFactory, boolean expectClang) {
+        this.execActionFactory = execActionFactory;
+        this.clang = expectClang;
+    }
+
+    public GccVersionResult getGccMetaData(File gccBinary, List<String> args) {
+        List<String> allArgs = new ArrayList<String>(args);
+        allArgs.add("-dM");
+        allArgs.add("-E");
+        allArgs.add("-");
+        String output = transform(gccBinary, allArgs);
+        if (output == null) {
+            return new BrokenResult(String.format("Could not determine %s version: failed to execute %s %s.", getDescription(), gccBinary.getName(), Joiner.on(' ').join(allArgs)));
+        }
+        return transform(output, gccBinary);
+    }
+
+    private String getDescription() {
+        return clang ? "Clang" : "GCC";
+    }
+
+    private String transform(File gccBinary, List<String> args) {
+        ExecAction exec = execActionFactory.newExecAction();
+        exec.executable(gccBinary.getAbsolutePath());
+        exec.setWorkingDir(gccBinary.getParentFile());
+        exec.args(args);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        exec.setStandardOutput(baos);
+        exec.setErrorOutput(new ByteArrayOutputStream());
+        exec.setIgnoreExitValue(true);
+        ExecResult result = exec.execute();
+
+        int exitValue = result.getExitValue();
+        if (exitValue == 0) {
+            return new String(baos.toByteArray());
+        } else {
+            return null;
+        }
+    }
+
+    private GccVersionResult transform(String output, File gccBinary) {
+        BufferedReader reader = new BufferedReader(new StringReader(output));
+        String line;
+        Map<String, String> defines = new HashMap<String, String>();
+        try {
+            while ((line = reader.readLine()) != null) {
+                Matcher matcher = DEFINE_PATTERN.matcher(line);
+                if (!matcher.matches()) {
+                    return new BrokenResult(String.format("Could not determine %s version: %s produced unexpected output.", getDescription(), gccBinary.getName()));
+                }
+                defines.put(matcher.group(1), matcher.group(2));
+            }
+        } catch (IOException e) {
+            // Should not happen reading from a StringReader
+            throw new UncheckedIOException(e);
+        }
+        if (!defines.containsKey("__GNUC__")) {
+            return new BrokenResult(String.format("Could not determine %s version: %s produced unexpected output.", getDescription(), gccBinary.getName()));
+        }
+        int major;
+        int minor;
+        int patch;
+        if (clang) {
+            if (!defines.containsKey("__clang__")) {
+                return new BrokenResult(String.format("%s appears to be GCC rather than Clang. Treating it as GCC.", gccBinary.getName()));
+            }
+            major = toInt(defines.get("__clang_major__"));
+            minor = toInt(defines.get("__clang_minor__"));
+            patch = toInt(defines.get("__clang_patchlevel__"));
+        } else {
+            if (defines.containsKey("__clang__")) {
+                return new BrokenResult(String.format("XCode %s is a wrapper around Clang. Treating it as Clang and not GCC.", gccBinary.getName()));
+            }
+            major = toInt(defines.get("__GNUC__"));
+            minor = toInt(defines.get("__GNUC_MINOR__"));
+            patch = toInt(defines.get("__GNUC_PATCHLEVEL__"));
+        }
+        final ArchitectureInternal architecture = determineArchitecture(defines);
+        return new DefaultGccVersionResult(new VersionNumber(major, minor, patch, null), architecture, clang);
+    }
+
+    private ArchitectureInternal determineArchitecture(Map<String, String> defines) {
+        boolean i386 = defines.containsKey("__i386__");
+        boolean amd64 = defines.containsKey("__amd64__");
+        final ArchitectureInternal architecture;
+        if (i386) {
+            architecture = Architectures.forInput("i386");
+        } else if (amd64) {
+            architecture = Architectures.forInput("amd64");
+        } else {
+            architecture = DefaultNativePlatform.getCurrentArchitecture();
+        }
+        return architecture;
+    }
+
+    private int toInt(String value) {
+        if (value == null) {
+            return 0;
+        }
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    private static class DefaultGccVersionResult implements GccVersionResult {
+        private final VersionNumber scrapedVersion;
+        private final ArchitectureInternal architecture;
+        private final boolean clang;
+
+        public DefaultGccVersionResult(VersionNumber scrapedVersion, ArchitectureInternal architecture, boolean clang) {
+            this.scrapedVersion = scrapedVersion;
+            this.architecture = architecture;
+            this.clang = clang;
+        }
+
+        public VersionNumber getVersion() {
+            return scrapedVersion;
+        }
+
+        public boolean isClang() {
+            return clang;
+        }
+
+        public ArchitectureInternal getDefaultArchitecture() {
+            return architecture;
+        }
+
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+        }
+    }
+
+    private static class BrokenResult implements GccVersionResult {
+        private final String message;
+
+        private BrokenResult(String message) {
+            this.message = message;
+        }
+
+        public VersionNumber getVersion() {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean isClang() {
+            throw new UnsupportedOperationException();
+        }
+
+        public ArchitectureInternal getDefaultArchitecture() {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(message);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionResult.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionResult.java
new file mode 100644
index 0000000..8b2dca5
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionResult.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.nativeplatform.toolchain.internal.gcc.version;
+
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal;
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+import org.gradle.util.VersionNumber;
+
+public interface GccVersionResult extends ToolSearchResult {
+    /**
+     * Returns true if the implementation is Clang, false if GCC.
+     */
+    boolean isClang();
+
+    ArchitectureInternal getDefaultArchitecture();
+
+    VersionNumber getVersion();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/Assembler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/Assembler.java
new file mode 100755
index 0000000..77656ea
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/Assembler.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.nativeplatform.toolchain.internal.msvcpp;
+
+import com.google.common.collect.Iterables;
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
+
+import java.util.List;
+
+class Assembler extends VisualCppNativeCompiler<AssembleSpec> {
+
+    Assembler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, Transformer<AssembleSpec, AssembleSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new AssemblerArgsTransformer(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected Iterable<String> buildPerFileArgs(List<String> genericArgs, List<String> sourceArgs, List<String> outputArgs, List<String> pchArgss) {
+        // ml/ml64 have position sensitive arguments,
+        // e.g., /Fo must appear before /c and /c must appear before the source file.
+
+        return Iterables.concat(outputArgs, genericArgs, sourceArgs);
+    }
+
+    private static class AssemblerArgsTransformer extends VisualCppCompilerArgsTransformer<AssembleSpec> {
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompiler.java
new file mode 100755
index 0000000..97c1173
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompiler.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
+
+class CCompiler extends VisualCppNativeCompiler<CCompileSpec> {
+
+    CCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<CCompileSpec, CCompileSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new CCompilerArgsTransformer(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    private static class CCompilerArgsTransformer extends VisualCppCompilerArgsTransformer<CCompileSpec> {
+        protected String getLanguageOption() {
+            return "/TC";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CPCHCompiler.java
new file mode 100644
index 0000000..c9b9ff2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CPCHCompiler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+public class CPCHCompiler extends VisualCppNativeCompiler<CPCHCompileSpec> {
+    public CPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<CPCHCompileSpec, CPCHCompileSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new VisualCppPCHCompilerArgsTransformer<CPCHCompileSpec>(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected List<String> getOutputArgs(CPCHCompileSpec spec, File outputFile) {
+        return Collections.singletonList("/Fp" + outputFile.getAbsolutePath());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompiler.java
new file mode 100755
index 0000000..7eee1a9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompiler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec;
+
+class CppCompiler extends VisualCppNativeCompiler<CppCompileSpec> {
+
+    CppCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<CppCompileSpec, CppCompileSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new CppCompilerArgsTransformer(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    private static class CppCompilerArgsTransformer extends VisualCppCompilerArgsTransformer<CppCompileSpec> {
+        protected String getLanguageOption() {
+            return "/TP";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppPCHCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppPCHCompiler.java
new file mode 100644
index 0000000..9537efa
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppPCHCompiler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+public class CppPCHCompiler extends VisualCppNativeCompiler<CppPCHCompileSpec> {
+    public CppPCHCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<CppPCHCompileSpec, CppPCHCompileSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, new VisualCppPCHCompilerArgsTransformer<CppPCHCompileSpec>(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected List<String> getOutputArgs(CppPCHCompileSpec spec, File outputFile) {
+        return Collections.singletonList("/Fp" + outputFile.getAbsolutePath());
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualCppPlatformToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualCppPlatformToolChain.java
new file mode 100644
index 0000000..342310b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualCppPlatformToolChain.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.CommandLineToolConfiguration;
+import org.gradle.nativeplatform.toolchain.VisualCppPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+import org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolConfigurationInternal;
+import org.gradle.nativeplatform.toolchain.internal.tools.DefaultCommandLineToolConfiguration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultVisualCppPlatformToolChain implements VisualCppPlatformToolChain {
+    private final NativePlatform platform;
+    protected final Map<ToolType, CommandLineToolConfigurationInternal> tools;
+
+    public DefaultVisualCppPlatformToolChain(NativePlatform platform, Instantiator instantiator) {
+        this.platform = platform;
+        tools = new HashMap<ToolType, CommandLineToolConfigurationInternal>();
+        tools.put(ToolType.C_COMPILER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.C_COMPILER));
+        tools.put(ToolType.CPP_COMPILER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.CPP_COMPILER));
+        tools.put(ToolType.LINKER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.LINKER));
+        tools.put(ToolType.STATIC_LIB_ARCHIVER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.STATIC_LIB_ARCHIVER));
+        tools.put(ToolType.ASSEMBLER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.ASSEMBLER));
+        tools.put(ToolType.WINDOW_RESOURCES_COMPILER, instantiator.newInstance(DefaultCommandLineToolConfiguration.class, ToolType.WINDOW_RESOURCES_COMPILER));
+    }
+
+    public CommandLineToolConfiguration getcCompiler() {
+        return tools.get(ToolType.C_COMPILER);
+    }
+
+    public CommandLineToolConfiguration getCppCompiler() {
+        return tools.get(ToolType.CPP_COMPILER);
+    }
+
+    public CommandLineToolConfiguration getRcCompiler() {
+        return tools.get(ToolType.WINDOW_RESOURCES_COMPILER);
+    }
+
+    public CommandLineToolConfiguration getAssembler() {
+        return tools.get(ToolType.ASSEMBLER);
+    }
+
+    public CommandLineToolConfiguration getLinker() {
+        return tools.get(ToolType.LINKER);
+    }
+
+    public CommandLineToolConfiguration getStaticLibArchiver() {
+        return tools.get(ToolType.STATIC_LIB_ARCHIVER);
+    }
+
+    public NativePlatform getPlatform() {
+        return platform;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java
new file mode 100644
index 0000000..c01bd4f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocator.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException;
+import net.rubygrapefruit.platform.SystemInfo;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativeplatform.platform.Architecture;
+import org.gradle.nativeplatform.platform.internal.Architectures;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.TreeVisitor;
+import org.gradle.util.VersionNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+
+public class DefaultVisualStudioLocator implements VisualStudioLocator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultVisualStudioLocator.class);
+    private static final String[] REGISTRY_BASEPATHS = {
+        "SOFTWARE\\",
+        "SOFTWARE\\Wow6432Node\\"
+    };
+    private static final String REGISTRY_ROOTPATH_VC = "Microsoft\\VisualStudio\\SxS\\VC7";
+    private static final String PATH_COMMON = "Common7/";
+    private static final String PATH_COMMONTOOLS = PATH_COMMON + "Tools/";
+    private static final String PATH_COMMONIDE = PATH_COMMON + "IDE/";
+    private static final String PATH_BIN = "bin/";
+    private static final String PATH_INCLUDE = "include/";
+    private static final String COMPILER_FILENAME = "cl.exe";
+
+    private static final String ARCHITECTURE_AMD64 = "amd64";
+    private static final String ARCHITECTURE_X86 = "x86";
+    private static final String ARCHITECTURE_ARM = "arm";
+    private static final String ARCHITECTURE_IA64 = "ia-64";
+    private static final String BINPATH_AMD64_AMD64 = "bin/amd64";
+    private static final String BINPATH_AMD64_ARM = "bin/amd64_arm";
+    private static final String BINPATH_AMD64_X86 = "bin/amd64_x86";
+    private static final String BINPATH_X86_AMD64 = "bin/x86_amd64";
+    private static final String BINPATH_X86_ARM = "bin/x86_arm";
+    private static final String BINPATH_X86_IA64 = "bin/x86_ia64";
+    private static final String BINPATH_X86_X86 = "bin";
+    private static final String LIBPATH_AMD64 = "lib/amd64";
+    private static final String LIBPATH_ARM = "lib/arm";
+    private static final String LIBPATH_IA64 = "lib/ia64";
+    private static final String LIBPATH_X86 = "lib";
+    private static final String ASSEMBLER_FILENAME_AMD64 = "ml64.exe";
+    private static final String ASSEMBLER_FILENAME_ARM = "armasm.exe";
+    private static final String ASSEMBLER_FILENAME_IA64 = "ias.exe";
+    private static final String ASSEMBLER_FILENAME_X86 = "ml.exe";
+    private static final String DEFINE_ARMPARTITIONAVAILABLE = "_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE";
+
+    private final Map<File, VisualStudioInstall> foundInstalls = new HashMap<File, VisualStudioInstall>();
+    private final OperatingSystem os;
+    private final WindowsRegistry windowsRegistry;
+    private final SystemInfo systemInfo;
+    private VisualStudioInstall pathInstall;
+    private boolean initialised;
+
+    public DefaultVisualStudioLocator(OperatingSystem os, WindowsRegistry windowsRegistry, SystemInfo systemInfo) {
+        this.os = os;
+        this.windowsRegistry = windowsRegistry;
+        this.systemInfo = systemInfo;
+    }
+
+    public SearchResult locateVisualStudioInstalls(File candidate) {
+        if (!initialised) {
+            locateInstallsInRegistry();
+            locateInstallInPath();
+            initialised = true;
+        }
+
+        if (candidate != null) {
+            return locateUserSpecifiedInstall(candidate);
+        }
+
+        return determineDefaultInstall();
+    }
+
+    private void locateInstallsInRegistry() {
+        for (String baseKey : REGISTRY_BASEPATHS) {
+            locateInstallsInRegistry(baseKey);
+        }
+    }
+
+    private void locateInstallsInRegistry(String baseKey) {
+        List<String> visualCppVersions;
+        try {
+            visualCppVersions = windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_VC);
+        } catch (MissingRegistryEntryException e) {
+            // No Visual Studio information available in the registry
+            return;
+        }
+
+        for (String valueName : visualCppVersions) {
+            if (!valueName.matches("\\d+\\.\\d+")) {
+                // Ignore the other values
+                continue;
+            }
+            File visualCppDir = new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_VC, valueName));
+            visualCppDir = GFileUtils.canonicalise(visualCppDir);
+            File visualStudioDir = visualCppDir.getParentFile();
+
+            if (isVisualCpp(visualCppDir) && isVisualStudio(visualStudioDir)) {
+                LOGGER.debug("Found Visual C++ {} at {}", valueName, visualCppDir);
+                VersionNumber version = VersionNumber.parse(valueName);
+                VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ " + valueName, visualStudioDir, visualCppDir, version);
+                VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
+                foundInstalls.put(visualStudioDir, visualStudio);
+            } else {
+                LOGGER.debug("Ignoring candidate Visual C++ directory {} as it does not look like a Visual C++ installation.", visualCppDir);
+            }
+        }
+    }
+
+    private void locateInstallInPath() {
+        File compilerInPath = os.findInPath(COMPILER_FILENAME);
+        if (compilerInPath == null) {
+            LOGGER.debug("No visual c++ compiler found in system path.");
+            return;
+        }
+
+        File visualCppDir = GFileUtils.canonicalise(compilerInPath.getParentFile().getParentFile());
+        if (!isVisualCpp(visualCppDir)) {
+            visualCppDir = visualCppDir.getParentFile();
+            if (!isVisualCpp(visualCppDir)) {
+                LOGGER.debug("Ignoring candidate Visual C++ install for {} as it does not look like a Visual C++ installation.", compilerInPath);
+                return;
+            }
+        }
+        LOGGER.debug("Found Visual C++ install {} using system path", visualCppDir);
+
+        File visualStudioDir = visualCppDir.getParentFile();
+        if (!foundInstalls.containsKey(visualStudioDir)) {
+            VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ from system path", visualStudioDir, visualCppDir, VersionNumber.UNKNOWN);
+            VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
+            foundInstalls.put(visualStudioDir, visualStudio);
+        }
+        pathInstall = foundInstalls.get(visualStudioDir);
+    }
+
+    private SearchResult locateUserSpecifiedInstall(File candidate) {
+        File visualStudioDir = GFileUtils.canonicalise(candidate);
+        File visualCppDir = new File(visualStudioDir, "VC");
+        if (!isVisualStudio(visualStudioDir) || !isVisualCpp(visualCppDir)) {
+            LOGGER.debug("Ignoring candidate Visual C++ install for {} as it does not look like a Visual C++ installation.", candidate);
+            return new InstallNotFound(String.format("The specified installation directory '%s' does not appear to contain a Visual Studio installation.", candidate));
+        }
+
+        if (!foundInstalls.containsKey(visualStudioDir)) {
+            VisualCppInstall visualCpp = buildVisualCppInstall("Visual C++ from user provided path", visualStudioDir, visualCppDir, VersionNumber.UNKNOWN);
+            VisualStudioInstall visualStudio = new VisualStudioInstall(visualStudioDir, visualCpp);
+            foundInstalls.put(visualStudioDir, visualStudio);
+        }
+        return new InstallFound(foundInstalls.get(visualStudioDir));
+    }
+
+    private VisualCppInstall buildVisualCppInstall(String name, File vsPath, File basePath, VersionNumber version) {
+        boolean isNativeAmd64 = systemInfo.getArchitecture() == SystemInfo.Architecture.amd64;
+        Map<Architecture, List<File>> paths = new HashMap<Architecture, List<File>>();
+        Map<Architecture, File> binaryPaths = new HashMap<Architecture, File>();
+        Map<Architecture, File> libraryPaths = new HashMap<Architecture, File>();
+        Map<Architecture, File> includePaths = new HashMap<Architecture, File>();
+        Map<Architecture, String> assemblerFilenames = new HashMap<Architecture, String>();
+        Map<Architecture, Map<String, String>> definitions = new HashMap<Architecture, Map<String, String>>();
+
+        Architecture amd64 = Architectures.forInput(ARCHITECTURE_AMD64);
+        Architecture x86 = Architectures.forInput(ARCHITECTURE_X86);
+        Architecture arm = Architectures.forInput(ARCHITECTURE_ARM);
+        Architecture ia64 = Architectures.forInput(ARCHITECTURE_IA64);
+
+        File includePath = new File(basePath, PATH_INCLUDE);
+        File commonTools = new File(vsPath, PATH_COMMONTOOLS);
+        File commonIde = new File(vsPath, PATH_COMMONIDE);
+
+        if (isNativeAmd64) {
+            Architecture[] architectures = {
+                amd64,
+                x86,
+                arm
+            };
+            String[] binPaths = {
+                BINPATH_AMD64_AMD64,
+                BINPATH_AMD64_X86,
+                BINPATH_AMD64_ARM
+            };
+            String[] libPaths = {
+                LIBPATH_AMD64,
+                LIBPATH_X86,
+                LIBPATH_ARM
+            };
+            String[] asmFilenames = {
+                ASSEMBLER_FILENAME_AMD64,
+                ASSEMBLER_FILENAME_X86,
+                ASSEMBLER_FILENAME_ARM
+            };
+
+            for (int i = 0; i != architectures.length; ++i) {
+                Architecture architecture = architectures[i];
+                File binPath = new File(basePath, binPaths[i]);
+                File libPath = new File(basePath, libPaths[i]);
+
+                if (binPath.isDirectory() && libPath.isDirectory()) {
+                    Map<String, String> definitionsList = new LinkedHashMap<String, String>();
+                    List<File> pathsList = new ArrayList<File>();
+
+                    pathsList.add(commonTools);
+                    pathsList.add(commonIde);
+
+                    // For cross-compilers, add the native compiler to the path as well
+                    if (architecture != amd64) {
+                        pathsList.add(new File(basePath, binPaths[0]));
+                    }
+
+                    if (architecture == arm) {
+                        definitionsList.put(DEFINE_ARMPARTITIONAVAILABLE, "1");
+                    }
+
+                    binaryPaths.put(architecture, binPath);
+                    libraryPaths.put(architecture, libPath);
+                    includePaths.put(architecture, includePath);
+                    assemblerFilenames.put(architecture, asmFilenames[i]);
+                    paths.put(architecture, pathsList);
+                    definitions.put(architecture, definitionsList);
+                }
+            }
+        }
+
+        Architecture[] architectures = {
+            x86,
+            amd64,
+            ia64,
+            arm
+        };
+        String[] binPaths = {
+            BINPATH_X86_X86,
+            BINPATH_X86_AMD64,
+            BINPATH_X86_IA64,
+            BINPATH_X86_ARM
+        };
+        String[] libPaths = {
+            LIBPATH_X86,
+            LIBPATH_AMD64,
+            LIBPATH_IA64,
+            LIBPATH_ARM
+        };
+        String[] asmFilenames = {
+            ASSEMBLER_FILENAME_X86,
+            ASSEMBLER_FILENAME_AMD64,
+            ASSEMBLER_FILENAME_IA64,
+            ASSEMBLER_FILENAME_ARM
+        };
+
+        for (int i = 0; i != architectures.length; ++i) {
+            Architecture architecture = architectures[i];
+
+            if (!binaryPaths.containsKey(architecture)) {
+                File binPath = new File(basePath, binPaths[i]);
+                File libPath = new File(basePath, libPaths[i]);
+    
+                if (binPath.isDirectory() && libPath.isDirectory()) {
+                    Map<String, String> definitionsList = new LinkedHashMap<String, String>();
+                    List<File> pathsList = new ArrayList<File>();
+
+                    pathsList.add(commonTools);
+                    pathsList.add(commonIde);
+
+                    // For cross-compilers, add the native compiler to the path as well
+                    if (architecture != x86) {
+                        pathsList.add(new File(basePath, binPaths[0]));
+                    }
+
+                    if (architecture == arm) {
+                        definitionsList.put(DEFINE_ARMPARTITIONAVAILABLE, "1");
+                    }
+
+                    binaryPaths.put(architecture, binPath);
+                    libraryPaths.put(architecture, libPath);
+                    includePaths.put(architecture, includePath);
+                    assemblerFilenames.put(architecture, asmFilenames[i]);
+                    paths.put(architecture, pathsList);
+                    definitions.put(architecture, definitionsList);
+                }
+            }
+        }
+
+        return new VisualCppInstall(name, version, paths, binaryPaths, libraryPaths, includePaths, assemblerFilenames, definitions);
+    }
+
+    private SearchResult determineDefaultInstall() {
+        if (pathInstall != null) {
+            return new InstallFound(pathInstall);
+        }
+
+        VisualStudioInstall candidate = null;
+
+        for (VisualStudioInstall visualStudio : foundInstalls.values()) {
+            if (candidate == null || visualStudio.getVersion().compareTo(candidate.getVersion()) > 0) {
+                candidate = visualStudio;
+            }
+        }
+
+        return candidate == null ? new InstallNotFound("Could not locate a Visual Studio installation, using the Windows registry and system path.") : new InstallFound(candidate);
+    }
+
+    private static boolean isVisualStudio(File candidate) {
+        return new File(candidate, PATH_COMMON).isDirectory() && isVisualCpp(new File(candidate, "VC"));
+    }
+
+    private static boolean isVisualCpp(File candidate) {
+        return new File(candidate, PATH_BIN + COMPILER_FILENAME).isFile();
+    }
+
+    private static class InstallFound implements SearchResult {
+        private final VisualStudioInstall install;
+
+        public InstallFound(VisualStudioInstall install) {
+            this.install = install;
+        }
+
+        public VisualStudioInstall getVisualStudio() {
+            return install;
+        }
+
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+        }
+    }
+
+    private static class InstallNotFound implements SearchResult {
+        private final String message;
+
+        private InstallNotFound(String message) {
+            this.message = message;
+        }
+
+        public VisualStudioInstall getVisualStudio() {
+            return null;
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(message);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java
new file mode 100644
index 0000000..cb995d4
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocator.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.util.GFileUtils;
+import org.gradle.util.TreeVisitor;
+import org.gradle.util.VersionNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultWindowsSdkLocator implements WindowsSdkLocator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultWindowsSdkLocator.class);
+    private static final String REGISTRY_BASEPATHS[] = {
+        "SOFTWARE\\",
+        "SOFTWARE\\Wow6432Node\\"
+    };
+    private static final String REGISTRY_ROOTPATH_SDK = "Microsoft\\Microsoft SDKs\\Windows";
+    private static final String REGISTRY_ROOTPATH_KIT = "Microsoft\\Windows Kits\\Installed Roots";
+    private static final String REGISTRY_FOLDER = "InstallationFolder";
+    private static final String REGISTRY_VERSION = "ProductVersion";
+    private static final String REGISTRY_NAME = "ProductName";
+    private static final String REGISTRY_KIT_8 = "KitsRoot";
+    private static final String REGISTRY_KIT_81 = "KitsRoot81";
+    private static final String VERSION_KIT_8 = "8.0";
+    private static final String VERSION_KIT_81 = "8.1";
+    private static final String VERSION_USER = "user";
+
+    private static final String NAME_USER = "User-provided Windows SDK";
+    private static final String NAME_KIT = "Windows Kit";
+
+    private static final String RESOURCE_PATHS[] = {
+        "bin/x86/",
+        "bin/"
+    };
+
+    private static final String KERNEL32_PATHS[] = {
+        "lib/winv6.3/um/x86/",
+        "lib/win8/um/x86/",
+        "lib/"
+    };
+
+    private static final String RESOURCE_FILENAME = "rc.exe";
+    private static final String KERNEL32_FILENAME = "kernel32.lib";
+
+    private final Map<File, WindowsSdk> foundSdks = new HashMap<File, WindowsSdk>();
+    private final OperatingSystem os;
+    private final WindowsRegistry windowsRegistry;
+    private WindowsSdk pathSdk;
+    private boolean initialised;
+
+    public DefaultWindowsSdkLocator(OperatingSystem os, WindowsRegistry windowsRegistry) {
+        this.os = os;
+        this.windowsRegistry = windowsRegistry;
+    }
+
+    public SearchResult locateWindowsSdks(File candidate) {
+        if (!initialised) {
+            locateSdksInRegistry();
+            locateKitsInRegistry();
+            locateSdkInPath();
+            initialised = true;
+        }
+
+        if (candidate != null) {
+            return locateUserSpecifiedSdk(candidate);
+        }
+
+        return locateDefaultSdk();
+    }
+
+    private void locateSdksInRegistry() {
+        for (String baseKey : REGISTRY_BASEPATHS) {
+            locateSdksInRegistry(baseKey);
+        }
+    }
+
+    private void locateSdksInRegistry(String baseKey) {
+        try {
+            List<String> subkeys = windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_SDK);
+            for (String subkey : subkeys) {
+                try {
+                    String basePath = baseKey + REGISTRY_ROOTPATH_SDK + "\\" + subkey;
+                    File sdkDir = GFileUtils.canonicalise(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_FOLDER)));
+                    String version = formatVersion(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_VERSION));
+                    String name = windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, basePath, REGISTRY_NAME);
+
+                    if (isWindowsSdk(sdkDir)) {
+                        LOGGER.debug("Found Windows SDK {} at {}", version, sdkDir);
+                        addSdk(sdkDir, version, name);
+                    } else {
+                        LOGGER.debug("Ignoring candidate Windows SDK directory {} as it does not look like a Windows SDK installation.", sdkDir);
+                    }
+                } catch (MissingRegistryEntryException e) {
+                    // Ignore the subkey if it doesn't have a folder and version
+                }
+            }
+        } catch (MissingRegistryEntryException e) {
+            // No SDK information available in the registry
+        }
+    }
+
+    private void locateKitsInRegistry() {
+        for (String baseKey : REGISTRY_BASEPATHS) {
+            locateKitsInRegistry(baseKey);
+        }
+    }
+
+    private void locateKitsInRegistry(String baseKey) {
+        String[] versions = {
+                VERSION_KIT_8,
+                VERSION_KIT_81
+        };
+        String[] keys = {
+                REGISTRY_KIT_8,
+                REGISTRY_KIT_81
+        };
+
+        for (int i = 0; i != keys.length; ++i) {
+            try {
+                File kitDir = GFileUtils.canonicalise(new File(windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, baseKey + REGISTRY_ROOTPATH_KIT, keys[i])));
+                if (isWindowsSdk(kitDir)) {
+                    LOGGER.debug("Found Windows Kit {} at {}", versions[i], kitDir);
+                    addSdk(kitDir, versions[i], NAME_KIT + " " + versions[i]);
+                } else {
+                    LOGGER.debug("Ignoring candidate Windows Kit directory {} as it does not look like a Windows Kit installation.", kitDir);
+                }
+            } catch (MissingRegistryEntryException e) {
+                // Ignore the version if the string cannot be read
+            }
+        }
+    }
+
+    private void locateSdkInPath() {
+        File resourceCompiler = os.findInPath(RESOURCE_FILENAME);
+        if (resourceCompiler == null) {
+            LOGGER.debug("Could not find Windows resource compiler in system path.");
+            return;
+        }
+        File sdkDir = GFileUtils.canonicalise(resourceCompiler.getParentFile().getParentFile());
+        if (!isWindowsSdk(sdkDir)) {
+            sdkDir = sdkDir.getParentFile();
+            if (!isWindowsSdk(sdkDir)) {
+                LOGGER.debug("Ignoring candidate Windows SDK for {} as it does not look like a Windows SDK installation.", resourceCompiler);
+            }
+        }
+        LOGGER.debug("Found Windows SDK {} using system path", sdkDir);
+
+        if (!foundSdks.containsKey(sdkDir)) {
+            addSdk(sdkDir, "path", "Path-resolved Windows SDK");
+        }
+        pathSdk = foundSdks.get(sdkDir);
+    }
+
+    private SearchResult locateUserSpecifiedSdk(File candidate) {
+        File sdkDir = GFileUtils.canonicalise(candidate);
+        if (!isWindowsSdk(sdkDir)) {
+            return new SdkNotFound(String.format("The specified installation directory '%s' does not appear to contain a Windows SDK installation.", candidate));
+        }
+
+        if (!foundSdks.containsKey(sdkDir)) {
+            addSdk(sdkDir, VERSION_USER, NAME_USER);
+        }
+        return new SdkFound(foundSdks.get(sdkDir));
+    }
+
+    private SearchResult locateDefaultSdk() {
+        if (pathSdk != null) {
+            return new SdkFound(pathSdk);
+        }
+
+        WindowsSdk candidate = null;
+        for (WindowsSdk windowsSdk : foundSdks.values()) {
+            if (candidate == null || windowsSdk.getVersion().compareTo(candidate.getVersion()) > 0) {
+                candidate = windowsSdk;
+            }
+        }
+        return candidate == null ? new SdkNotFound("Could not locate a Windows SDK installation, using the Windows registry and system path.") : new SdkFound(candidate);
+    }
+
+    private void addSdk(File path, String version, String name) {
+        foundSdks.put(path, new WindowsSdk(path, VersionNumber.parse(version), name));
+    }
+
+    private static boolean isWindowsSdk(File candidate) {
+        boolean hasResourceCompiler = false;
+        boolean hasKernel32Lib = false;
+
+        for (String path : RESOURCE_PATHS) {
+            if (new File(candidate, path + RESOURCE_FILENAME).isFile()) {
+                hasResourceCompiler = true;
+                break;
+            }
+        }
+
+        for (String path : KERNEL32_PATHS) {
+            if (new File(candidate, path + KERNEL32_FILENAME).isFile()) {
+                hasKernel32Lib = true;
+                break;
+            }
+        }
+
+        return hasResourceCompiler && hasKernel32Lib;
+    }
+
+    private static String formatVersion(String version) {
+        int index = StringUtils.ordinalIndexOf(version, ".", 2);
+
+        if (index != -1) {
+            version = version.substring(0, index);
+        }
+
+        return version;
+    }
+
+    private static class SdkFound implements SearchResult {
+        private final WindowsSdk sdk;
+
+        public SdkFound(WindowsSdk sdk) {
+            this.sdk = sdk;
+        }
+
+        public WindowsSdk getSdk() {
+            return sdk;
+        }
+
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+        }
+    }
+
+    private static class SdkNotFound implements SearchResult {
+        private final String message;
+
+        private SdkNotFound(String message) {
+            this.message = message;
+        }
+
+        public WindowsSdk getSdk() {
+            return null;
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node(message);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/EscapeUserArgs.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/EscapeUserArgs.java
new file mode 100644
index 0000000..d22ffba
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/EscapeUserArgs.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.util.CollectionUtils;
+
+import java.util.List;
+
+class EscapeUserArgs implements Transformer<String, String> {
+    public static String escapeUserArg(String original) {
+        return new EscapeUserArgs().transform(original);
+    }
+
+    public static List<String> escapeUserArgs(List<String> original) {
+        return new EscapeUserArgs().transform(original);
+    }
+
+    public String transform(String original) {
+        return original.replace("\\", "\\\\").replace("\"", "\\\"");
+    }
+
+    public List<String> transform(List<String> args) {
+        return CollectionUtils.collect(args, this);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.java
new file mode 100755
index 0000000..1333ab9
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LibExeStaticLibraryArchiver.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.BuildOperationQueue;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.nativeplatform.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
+
+class LibExeStaticLibraryArchiver implements Compiler<StaticLibraryArchiverSpec> {
+    private final CommandLineToolInvocationWorker commandLineToolInvocationWorker;
+    private final Transformer<StaticLibraryArchiverSpec, StaticLibraryArchiverSpec> specTransformer;
+
+    private final ArgsTransformer<StaticLibraryArchiverSpec> argsTransformer;
+    private final CommandLineToolContext invocationContext;
+    private final BuildOperationProcessor buildOperationProcessor;
+
+    LibExeStaticLibraryArchiver(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<StaticLibraryArchiverSpec, StaticLibraryArchiverSpec> specTransformer) {
+        this.buildOperationProcessor = buildOperationProcessor;
+        this.specTransformer = specTransformer;
+        this.argsTransformer = new LibExeSpecToArguments();
+        this.commandLineToolInvocationWorker = commandLineToolInvocationWorker;
+        this.invocationContext = invocationContext;
+    }
+
+    public WorkResult execute(StaticLibraryArchiverSpec spec) {
+        BuildOperationQueue<CommandLineToolInvocation> queue = buildOperationProcessor.newQueue(commandLineToolInvocationWorker, spec.getOperationLogger().getLogLocation());
+        StaticLibraryArchiverSpec transformedSpec = specTransformer.transform(spec);
+        List<String> args = argsTransformer.transform(transformedSpec);
+        invocationContext.getArgAction().execute(args);
+        new VisualCppOptionsFileArgsWriter(spec.getTempDir()).execute(args);
+        CommandLineToolInvocation invocation = invocationContext.createInvocation(
+                String.format("archiving %s", spec.getOutputFile().getName()), args, spec.getOperationLogger());
+        queue.add(invocation);
+        queue.waitForCompletion();
+        return new SimpleWorkResult(true);
+    }
+
+    private static class LibExeSpecToArguments implements ArgsTransformer<StaticLibraryArchiverSpec> {
+        public List<String> transform(StaticLibraryArchiverSpec spec) {
+            List<String> args = new ArrayList<String>();
+            args.add("/OUT:" + spec.getOutputFile().getAbsolutePath());
+            args.add("/NOLOGO");
+            args.addAll(escapeUserArgs(spec.getAllArgs()));
+            for (File file : spec.getObjectFiles()) {
+                args.add(file.getAbsolutePath());
+            }
+            return args;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LinkExeLinker.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LinkExeLinker.java
new file mode 100755
index 0000000..b7499c8
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/LinkExeLinker.java
@@ -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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.operations.BuildOperationQueue;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.nativeplatform.internal.LinkerSpec;
+import org.gradle.nativeplatform.internal.SharedLibraryLinkerSpec;
+import org.gradle.nativeplatform.toolchain.internal.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.nativeplatform.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
+
+class LinkExeLinker implements Compiler<LinkerSpec> {
+
+    private final CommandLineToolInvocationWorker commandLineToolInvocationWorker;
+    private final Transformer<LinkerSpec, LinkerSpec> specTransformer;
+    private final ArgsTransformer<LinkerSpec> argsTransformer;
+    private final CommandLineToolContext invocationContext;
+    private final BuildOperationProcessor buildOperationProcessor;
+
+    LinkExeLinker(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, Transformer<LinkerSpec, LinkerSpec> specTransformer) {
+        this.buildOperationProcessor = buildOperationProcessor;
+        this.argsTransformer = new LinkerArgsTransformer();
+        this.commandLineToolInvocationWorker = commandLineToolInvocationWorker;
+        this.invocationContext = invocationContext;
+        this.specTransformer = specTransformer;
+    }
+
+    public WorkResult execute(LinkerSpec spec) {
+        BuildOperationQueue<CommandLineToolInvocation> queue = buildOperationProcessor.newQueue(commandLineToolInvocationWorker, spec.getOperationLogger().getLogLocation());
+        LinkerSpec transformedSpec = specTransformer.transform(spec);
+        List<String> args = argsTransformer.transform(transformedSpec);
+        invocationContext.getArgAction().execute(args);
+        new VisualCppOptionsFileArgsWriter(spec.getTempDir()).execute(args);
+        CommandLineToolInvocation invocation = invocationContext.createInvocation(
+                String.format("linking %s", spec.getOutputFile().getName()), args, spec.getOperationLogger());
+        queue.add(invocation);
+        queue.waitForCompletion();
+        return new SimpleWorkResult(true);
+    }
+
+    private static class LinkerArgsTransformer implements ArgsTransformer<LinkerSpec> {
+        public List<String> transform(LinkerSpec spec) {
+            List<String> args = new ArrayList<String>();
+            args.addAll(escapeUserArgs(spec.getAllArgs()));
+            args.add("/OUT:" + spec.getOutputFile().getAbsolutePath());
+            args.add("/NOLOGO");
+            if (spec instanceof SharedLibraryLinkerSpec) {
+                args.add("/DLL");
+            }
+            for (File pathEntry : spec.getLibraryPath()) {
+                args.add("/LIBPATH:" + pathEntry.getAbsolutePath());
+            }
+            for (File file : spec.getObjectFiles()) {
+                args.add(file.getAbsolutePath());
+            }
+            for (File file : spec.getLibraries()) {
+                args.add(file.getAbsolutePath());
+            }
+            return args;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java
new file mode 100644
index 0000000..10f526d
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppCompilerArgsTransformer.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import com.google.common.collect.Lists;
+import org.gradle.nativeplatform.toolchain.internal.ArgsTransformer;
+import org.gradle.nativeplatform.toolchain.internal.MacroArgsConverter;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+import java.io.File;
+import java.util.List;
+
+import static org.gradle.nativeplatform.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArg;
+import static org.gradle.nativeplatform.toolchain.internal.msvcpp.EscapeUserArgs.escapeUserArgs;
+
+abstract class VisualCppCompilerArgsTransformer<T extends NativeCompileSpec> implements ArgsTransformer<T> {
+    public List<String> transform(T spec) {
+        List<String> args = Lists.newArrayList();
+        addToolSpecificArgs(spec, args);
+        addMacroArgs(spec, args);
+        addUserArgs(spec, args);
+        addIncludeArgs(spec, args);
+        return args;
+    }
+
+    private void addUserArgs(T spec, List<String> args) {
+        args.addAll(escapeUserArgs(spec.getAllArgs()));
+    }
+
+    protected void addToolSpecificArgs(T spec, List<String> args) {
+        args.add(getLanguageOption());
+        args.add("/nologo");
+        args.add("/c");
+    }
+
+    protected void addIncludeArgs(T spec, List<String> args) {
+        for (File file : spec.getIncludeRoots()) {
+            args.add("/I" + file.getAbsolutePath());
+        }
+    }
+
+    protected void addMacroArgs(T spec, List<String> args) {
+        for (String macroArg : new MacroArgsConverter().transform(spec.getMacros())) {
+            args.add(escapeUserArg("/D" + macroArg));
+        }
+    }
+
+    /**
+     * Returns compiler specific language option
+     * @return compiler language option or empty string if the language does not require it
+     */
+    protected String getLanguageOption() {
+        return "";
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppInstall.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppInstall.java
new file mode 100644
index 0000000..ec7a51e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppInstall.java
@@ -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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Named;
+import org.gradle.nativeplatform.platform.Architecture;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public class VisualCppInstall implements Named {
+    private static final String COMPILER_FILENAME = "cl.exe";
+    private static final String LINKER_FILENAME = "link.exe";
+    private static final String ARCHIVER_FILENAME = "lib.exe";
+
+    private final Map<Architecture, List<File>> paths;
+    private final Map<Architecture, File> binaryPaths;
+    private final Map<Architecture, File> libraryPaths;
+    private final Map<Architecture, File> includePaths;
+    private final Map<Architecture, String> assemblerFilenames;
+    private final Map<Architecture, Map<String, String>> definitions;
+    private final String name;
+    private final VersionNumber version;
+
+    public VisualCppInstall(String name, VersionNumber version,
+            Map<Architecture, List<File>> paths, Map<Architecture, File> binaryPaths, Map<Architecture, File> libraryPaths,
+            Map<Architecture, File> includePaths, Map<Architecture, String> assemblerFilenames,
+            Map<Architecture, Map<String, String>> definitions) {
+        this.paths = paths;
+        this.name = name;
+        this.version = version;
+        this.binaryPaths = binaryPaths;
+        this.libraryPaths = libraryPaths;
+        this.includePaths = includePaths;
+        this.assemblerFilenames = assemblerFilenames;
+        this.definitions = definitions;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public VersionNumber getVersion() {
+        return version;
+    }
+
+    public boolean isSupportedPlatform(NativePlatformInternal targetPlatform) {
+        // TODO:ADAM - ARM only if the target OS is Windows 8 or later
+        // TODO:MPUT - ARM also if the target OS is Windows RT or Windows Phone/Mobile/CE
+        // TODO:ADAM - IA64 only if the target OS is Windows 2008 or earlier
+        return targetPlatform.getOperatingSystem().isWindows()
+                && (binaryPaths.containsKey(getPlatformArchitecture(targetPlatform)));
+    }
+
+    public List<File> getPath(NativePlatformInternal targetPlatform) {
+        return paths.get(getPlatformArchitecture(targetPlatform));
+    }
+
+    public File getCompiler(NativePlatformInternal targetPlatform) {
+        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), COMPILER_FILENAME);
+    }
+
+    public File getLinker(NativePlatformInternal targetPlatform) {
+        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), LINKER_FILENAME);
+    }
+
+    public File getArchiver(NativePlatformInternal targetPlatform) {
+        return new File(binaryPaths.get(getPlatformArchitecture(targetPlatform)), ARCHIVER_FILENAME);
+    }
+
+    public File getAssembler(NativePlatformInternal targetPlatform) {
+        Architecture architecture = getPlatformArchitecture(targetPlatform);
+        return new File(binaryPaths.get(architecture), assemblerFilenames.get(architecture));
+    }
+
+    public File getBinaryPath(NativePlatformInternal targetPlatform) {
+        return binaryPaths.get(getPlatformArchitecture(targetPlatform));
+    }
+
+    public File getLibraryPath(NativePlatformInternal targetPlatform) {
+        return libraryPaths.get(getPlatformArchitecture(targetPlatform));
+    }
+
+    public Map<String, String> getDefinitions(NativePlatformInternal targetPlatform) {
+        return definitions.get(getPlatformArchitecture(targetPlatform));
+    }
+
+    public File getIncludePath(NativePlatformInternal targetPlatform) {
+        return includePaths.get(getPlatformArchitecture(targetPlatform));
+    }
+
+    private Architecture getPlatformArchitecture(NativePlatformInternal targetPlatform) {
+        return targetPlatform.getArchitecture();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompiler.java
new file mode 100644
index 0000000..5f3a017
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompiler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class VisualCppNativeCompiler<T extends NativeCompileSpec> extends NativeCompiler<T> {
+
+    VisualCppNativeCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineToolInvocationWorker, CommandLineToolContext invocationContext, ArgsTransformer<T> argsTransformer, Transformer<T, T> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineToolInvocationWorker, invocationContext, argsTransformer, specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected List<String> getOutputArgs(T spec, File outputFile) {
+        // MSVC doesn't allow a space between Fo and the file name
+        return Collections.singletonList("/Fo" + outputFile.getAbsolutePath());
+    }
+
+    @Override
+    protected void addOptionsFileArgs(List<String> args, File tempDir) {
+        OptionsFileArgsWriter writer = new VisualCppOptionsFileArgsWriter(tempDir);
+        // modifies args in place
+        writer.execute(args);
+    }
+
+    @Override
+    protected List<String> getPCHArgs(T spec) {
+        List<String> pchArgs = new ArrayList<String>();
+        if (CollectionUtils.isNotEmpty(spec.getPreCompiledHeaders()) && spec.getPreCompiledHeaderObjectFile() != null) {
+            String lastHeader = (String) CollectionUtils.get(spec.getPreCompiledHeaders(), spec.getPreCompiledHeaders().size() - 1);
+            if (lastHeader.startsWith("<")) {
+                lastHeader = lastHeader.substring(1, lastHeader.length()-1);
+            }
+            pchArgs.add("/Yu".concat(lastHeader));
+            pchArgs.add("/Fp".concat(spec.getPreCompiledHeaderObjectFile().getAbsolutePath()));
+        }
+        return pchArgs;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriter.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriter.java
new file mode 100644
index 0000000..3617d15
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriter.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.platform.base.internal.toolchain.ArgWriter;
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriter;
+
+import java.io.File;
+
+/**
+ * Uses an option file for arguments passed to Visual C++.
+ */
+class VisualCppOptionsFileArgsWriter extends OptionsFileArgsWriter {
+    public VisualCppOptionsFileArgsWriter(File tempDir) {
+        super(ArgWriter.windowsStyleFactory(), tempDir);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHCompilerArgsTransformer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHCompilerArgsTransformer.java
new file mode 100644
index 0000000..00faa60
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHCompilerArgsTransformer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+public class VisualCppPCHCompilerArgsTransformer<T extends NativeCompileSpec> extends VisualCppCompilerArgsTransformer<T> {
+    @Override
+    protected String getLanguageOption() {
+        return "/Yc";
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtil.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtil.java
new file mode 100644
index 0000000..3eb897b
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtil.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.gradle.api.GradleException;
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec;
+
+import java.io.File;
+import java.io.IOException;
+
+public class VisualCppPCHSourceFileGeneratorUtil {
+    private static SourceFileExtensionCalculator calculator = new SourceFileExtensionCalculator();
+
+    public static <T extends NativeCompileSpec> File generatePCHSourceFile(T original, File sourceFile) {
+        File generatedSourceDir = new File(original.getTempDir(), "pchGeneratedSource");
+        generatedSourceDir.mkdirs();
+        File generatedSource = new File(generatedSourceDir, FilenameUtils.removeExtension(sourceFile.getName()).concat(calculator.transform(original.getClass())));
+        File headerFileCopy = new File(generatedSourceDir, sourceFile.getName());
+        try {
+            FileUtils.copyFile(sourceFile, headerFileCopy);
+            FileUtils.writeStringToFile(generatedSource, "#include \"".concat(headerFileCopy.getName()).concat("\""));
+            return generatedSource;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    static class SourceFileExtensionCalculator implements Transformer<String, Class<? extends NativeCompileSpec>> {
+        @Override
+        public String transform(Class<? extends NativeCompileSpec> specClass) {
+            if (CPCHCompileSpec.class.isAssignableFrom(specClass)) {
+                return ".c";
+            }
+
+            if (CppPCHCompileSpec.class.isAssignableFrom(specClass)) {
+                return ".cpp";
+            }
+
+            throw new GradleException("Cannot determine source file extension for spec with type ".concat(specClass.getSimpleName()));
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformer.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformer.java
new file mode 100644
index 0000000..77a9c79
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.Transformer;
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec;
+
+import java.io.File;
+import java.util.List;
+
+public class VisualCppPCHSourceFileTransformer<T extends NativeCompileSpec> implements Transformer<T, T> {
+    @Override
+    public T transform(T original) {
+        List<File> newSourceFiles = Lists.newArrayList();
+        for (File sourceFile : original.getSourceFiles()) {
+            newSourceFiles.add(VisualCppPCHSourceFileGeneratorUtil.generatePCHSourceFile(original, sourceFile));
+        }
+        original.setSourceFiles(newSourceFiles);
+        return original;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProvider.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProvider.java
new file mode 100644
index 0000000..2bc2f35
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProvider.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.Transformer;
+import org.gradle.internal.Transformers;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.nativeplatform.internal.LinkerSpec;
+import org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal;
+import org.gradle.nativeplatform.toolchain.internal.*;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.WindowsResourceCompileSpec;
+import org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolConfigurationInternal;
+import org.gradle.process.internal.ExecActionFactory;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+class VisualCppPlatformToolProvider extends AbstractPlatformToolProvider {
+    private final Map<ToolType, CommandLineToolConfigurationInternal> commandLineToolConfigurations;
+    private final VisualCppInstall visualCpp;
+    private final WindowsSdk sdk;
+    private final NativePlatformInternal targetPlatform;
+    private final ExecActionFactory execActionFactory;
+
+    VisualCppPlatformToolProvider(BuildOperationProcessor buildOperationProcessor, OperatingSystemInternal operatingSystem, Map<ToolType, CommandLineToolConfigurationInternal> commandLineToolConfigurations, VisualCppInstall visualCpp, WindowsSdk sdk, NativePlatformInternal targetPlatform, ExecActionFactory execActionFactory) {
+        super(buildOperationProcessor, operatingSystem);
+        this.commandLineToolConfigurations = commandLineToolConfigurations;
+        this.visualCpp = visualCpp;
+        this.sdk = sdk;
+        this.targetPlatform = targetPlatform;
+        this.execActionFactory = execActionFactory;
+    }
+
+    @Override
+    public String getSharedLibraryLinkFileName(String libraryName) {
+        return getSharedLibraryName(libraryName).replaceFirst("\\.dll$", ".lib");
+    }
+
+    @Override
+    protected Compiler<CppCompileSpec> createCppCompiler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("C++ compiler", visualCpp.getCompiler(targetPlatform));
+        CppCompiler cppCompiler = new CppCompiler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.CPP_COMPILER)), addIncludePathAndDefinitions(CppCompileSpec.class), getObjectFileExtension(), true);
+        return new OutputCleaningCompiler<CppCompileSpec>(cppCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createCppPCHCompiler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("C++ compiler", visualCpp.getCompiler(targetPlatform));
+        CppPCHCompiler cppPCHCompiler = new CppPCHCompiler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.CPP_COMPILER)), allSpecTransforms(CppPCHCompileSpec.class), getPCHFileExtension(), true);
+        return new OutputCleaningCompiler<CppPCHCompileSpec>(cppPCHCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<CCompileSpec> createCCompiler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("C compiler", visualCpp.getCompiler(targetPlatform));
+        CCompiler cCompiler = new CCompiler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.C_COMPILER)), addIncludePathAndDefinitions(CCompileSpec.class), getObjectFileExtension(), true);
+        return new OutputCleaningCompiler<CCompileSpec>(cCompiler, getObjectFileExtension());
+    }
+
+    @Override
+    protected Compiler<?> createCPCHCompiler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("C compiler", visualCpp.getCompiler(targetPlatform));
+        CPCHCompiler cpchCompiler = new CPCHCompiler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.C_COMPILER)), allSpecTransforms(CPCHCompileSpec.class), getPCHFileExtension(), true);
+        return new OutputCleaningCompiler<CPCHCompileSpec>(cpchCompiler, getPCHFileExtension());
+    }
+
+    @Override
+    protected Compiler<AssembleSpec> createAssembler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("Assembler", visualCpp.getAssembler(targetPlatform));
+        return new Assembler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.ASSEMBLER)), addIncludePathAndDefinitions(AssembleSpec.class), getObjectFileExtension(), false);
+    }
+
+    @Override
+    protected Compiler<?> createObjectiveCppCompiler() {
+        throw unavailableTool("Objective-C++ is not available on the Visual C++ toolchain");
+    }
+
+    @Override
+    protected Compiler<?> createObjectiveCCompiler() {
+        throw unavailableTool("Objective-C is not available on the Visual C++ toolchain");
+    }
+
+    @Override
+    protected Compiler<WindowsResourceCompileSpec> createWindowsResourceCompiler() {
+        CommandLineToolInvocationWorker commandLineTool = tool("Windows resource compiler", sdk.getResourceCompiler(targetPlatform));
+        String objectFileExtension = ".res";
+        WindowsResourceCompiler windowsResourceCompiler = new WindowsResourceCompiler(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.WINDOW_RESOURCES_COMPILER)), addIncludePathAndDefinitions(WindowsResourceCompileSpec.class), objectFileExtension, false);
+        return new OutputCleaningCompiler<WindowsResourceCompileSpec>(windowsResourceCompiler, objectFileExtension);
+    }
+
+    @Override
+    protected Compiler<LinkerSpec> createLinker() {
+        CommandLineToolInvocationWorker commandLineTool = tool("Linker", visualCpp.getLinker(targetPlatform));
+        return new LinkExeLinker(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.LINKER)), addLibraryPath());
+    }
+
+    @Override
+    protected Compiler<StaticLibraryArchiverSpec> createStaticLibraryArchiver() {
+        CommandLineToolInvocationWorker commandLineTool = tool("Static library archiver", visualCpp.getArchiver(targetPlatform));
+        return new LibExeStaticLibraryArchiver(buildOperationProcessor, commandLineTool, context(commandLineToolConfigurations.get(ToolType.STATIC_LIB_ARCHIVER)), Transformers.<StaticLibraryArchiverSpec>noOpTransformer());
+    }
+
+    private CommandLineToolInvocationWorker tool(String toolName, File exe) {
+        return new DefaultCommandLineToolInvocationWorker(toolName, exe, execActionFactory);
+    }
+
+    private CommandLineToolContext context(CommandLineToolConfigurationInternal commandLineToolConfiguration) {
+        MutableCommandLineToolContext invocationContext = new DefaultMutableCommandLineToolContext();
+        // The visual C++ tools use the path to find other executables
+        // TODO:ADAM - restrict this to the specific path for the target tool
+        invocationContext.addPath(visualCpp.getPath(targetPlatform));
+        invocationContext.addPath(sdk.getBinDir(targetPlatform));
+        // Clear environment variables that might effect cl.exe & link.exe
+        clearEnvironmentVars(invocationContext, "INCLUDE", "CL", "LIBPATH", "LINK", "LIB");
+
+        invocationContext.setArgAction(commandLineToolConfiguration.getArgAction());
+        return invocationContext;
+    }
+
+    private void clearEnvironmentVars(MutableCommandLineToolContext invocation, String... names) {
+        // TODO:DAZ This check should really be done in the compiler process
+        Map<String, ?> environmentVariables = Jvm.current().getInheritableEnvironmentVariables(System.getenv());
+        for (String name : names) {
+            Object value = environmentVariables.get(name);
+            if (value != null) {
+                VisualCppToolChain.LOGGER.warn("Ignoring value '{}' set for environment variable '{}'.", value, name);
+                invocation.addEnvironmentVar(name, "");
+            }
+        }
+    }
+
+    private <T extends NativeCompileSpec> Transformer<T, T> allSpecTransforms(final Class<T> type) {
+        return new Transformer<T, T>() {
+            @Override
+            public T transform(T original) {
+                List<Transformer<T, T>> transformers = Lists.newArrayList();
+                transformers.add(new VisualCppPCHSourceFileTransformer<T>());
+                transformers.add(addIncludePathAndDefinitions(type));
+
+                T next = original;
+                for (Transformer<T, T> transformer :  transformers) {
+                    next = transformer.transform(next);
+                }
+                return next;
+            }
+        };
+    }
+
+    // TODO:DAZ These should be modelled properly, not hidden in a compile spec transformation
+    private <T extends NativeCompileSpec> Transformer<T, T> addIncludePathAndDefinitions(Class<T> type) {
+        return new Transformer<T, T>() {
+            public T transform(T original) {
+                original.include(visualCpp.getIncludePath(targetPlatform));
+                original.include(sdk.getIncludeDirs());
+                for (Map.Entry<String, String> definition : visualCpp.getDefinitions(targetPlatform).entrySet()) {
+                    original.define(definition.getKey(), definition.getValue());
+                }
+                return original;
+            }
+        };
+    }
+
+    private Transformer<LinkerSpec, LinkerSpec> addLibraryPath() {
+        return new Transformer<LinkerSpec, LinkerSpec>() {
+            public LinkerSpec transform(LinkerSpec original) {
+                original.libraryPath(visualCpp.getLibraryPath(targetPlatform), sdk.getLibDir(targetPlatform));
+                return original;
+            }
+        };
+    }
+
+    @Override
+    public String getPCHFileExtension() {
+        return ".pch";
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChain.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChain.java
new file mode 100755
index 0000000..8253f78
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChain.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.nativeplatform.toolchain.VisualCpp;
+import org.gradle.nativeplatform.toolchain.VisualCppPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.internal.ExtendableToolChain;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.nativeplatform.toolchain.internal.UnavailablePlatformToolProvider;
+import org.gradle.platform.base.internal.toolchain.ToolChainAvailability;
+import org.gradle.process.internal.ExecActionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class VisualCppToolChain extends ExtendableToolChain<VisualCppPlatformToolChain> implements VisualCpp, NativeToolChainInternal {
+
+    private final String name;
+    private final OperatingSystem operatingSystem;
+    private final FileResolver fileResolver;
+
+    protected static final Logger LOGGER = LoggerFactory.getLogger(VisualCppToolChain.class);
+
+    public static final String DEFAULT_NAME = "visualCpp";
+
+    private final ExecActionFactory execActionFactory;
+    private final VisualStudioLocator visualStudioLocator;
+    private final WindowsSdkLocator windowsSdkLocator;
+    private final Instantiator instantiator;
+    private File installDir;
+    private File windowsSdkDir;
+    private VisualCppInstall visualCpp;
+    private WindowsSdk windowsSdk;
+    private ToolChainAvailability availability;
+
+    public VisualCppToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory,
+                              VisualStudioLocator visualStudioLocator, WindowsSdkLocator windowsSdkLocator, Instantiator instantiator) {
+        super(name, buildOperationProcessor, operatingSystem, fileResolver);
+
+        this.name = name;
+        this.operatingSystem = operatingSystem;
+        this.fileResolver = fileResolver;
+        this.execActionFactory = execActionFactory;
+        this.visualStudioLocator = visualStudioLocator;
+        this.windowsSdkLocator = windowsSdkLocator;
+        this.instantiator = instantiator;
+    }
+
+    protected String getTypeName() {
+        return "Visual Studio";
+    }
+
+    public File getInstallDir() {
+        return installDir;
+    }
+
+    public void setInstallDir(Object installDirPath) {
+        this.installDir = resolve(installDirPath);
+    }
+
+    public File getWindowsSdkDir() {
+        return windowsSdkDir;
+    }
+
+    public void setWindowsSdkDir(Object windowsSdkDirPath) {
+        this.windowsSdkDir = resolve(windowsSdkDirPath);
+    }
+
+    public PlatformToolProvider select(NativePlatformInternal targetPlatform) {
+        ToolChainAvailability result = new ToolChainAvailability();
+        result.mustBeAvailable(getAvailability());
+        if (visualCpp != null && !visualCpp.isSupportedPlatform(targetPlatform)) {
+            result.unavailable(String.format("Don't know how to build for platform '%s'.", targetPlatform.getName()));
+        }
+        if (!result.isAvailable()) {
+            return new UnavailablePlatformToolProvider(targetPlatform.getOperatingSystem(), result);
+        }
+
+        DefaultVisualCppPlatformToolChain configurableToolChain = instantiator.newInstance(DefaultVisualCppPlatformToolChain.class, targetPlatform, instantiator);
+        configureActions.execute(configurableToolChain);
+
+        return new VisualCppPlatformToolProvider(buildOperationProcessor, targetPlatform.getOperatingSystem(), configurableToolChain.tools, visualCpp, windowsSdk, targetPlatform, execActionFactory);
+    }
+
+    private ToolChainAvailability getAvailability() {
+        if (availability == null) {
+            availability = new ToolChainAvailability();
+            checkAvailable(availability);
+        }
+        return availability;
+    }
+
+    private void checkAvailable(ToolChainAvailability availability) {
+        if (!operatingSystem.isWindows()) {
+            availability.unavailable("Visual Studio is not available on this operating system.");
+            return;
+        }
+        VisualStudioLocator.SearchResult visualStudioSearchResult = visualStudioLocator.locateVisualStudioInstalls(installDir);
+        availability.mustBeAvailable(visualStudioSearchResult);
+        if (visualStudioSearchResult.isAvailable()) {
+            visualCpp = visualStudioSearchResult.getVisualStudio().getVisualCpp();
+        }
+        WindowsSdkLocator.SearchResult windowsSdkSearchResult = windowsSdkLocator.locateWindowsSdks(windowsSdkDir);
+        availability.mustBeAvailable(windowsSdkSearchResult);
+        if (windowsSdkSearchResult.isAvailable()) {
+            windowsSdk = windowsSdkSearchResult.getSdk();
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDisplayName() {
+        return String.format("Tool chain '%s' (%s)", getName(), getTypeName());
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioInstall.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioInstall.java
new file mode 100644
index 0000000..aff3517
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioInstall.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Named;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+
+public class VisualStudioInstall implements Named {
+    private final VisualCppInstall visualCppInstall;
+    private final File baseDir;
+
+    public VisualStudioInstall(File baseDir,  VisualCppInstall visualCppInstall) {
+        this.baseDir = baseDir;
+        this.visualCppInstall = visualCppInstall;
+    }
+
+    public String getName() {
+        return visualCppInstall.getName();
+    }
+
+    public VersionNumber getVersion() {
+        return visualCppInstall.getVersion();
+    }
+
+    public File getVisualStudioDir() {
+        return baseDir;
+    }
+
+    public VisualCppInstall getVisualCpp() {
+        return visualCppInstall;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioLocator.java
new file mode 100644
index 0000000..5a8feda
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualStudioLocator.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+
+import java.io.File;
+
+public interface VisualStudioLocator {
+
+    SearchResult locateVisualStudioInstalls(File candidate);
+
+    interface SearchResult extends ToolSearchResult {
+        VisualStudioInstall getVisualStudio();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompiler.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompiler.java
new file mode 100644
index 0000000..f9b8bfc
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompiler.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.nativeplatform.toolchain.internal.msvcpp;
+
+import com.google.common.collect.Iterables;
+import org.gradle.api.Transformer;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext;
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker;
+import org.gradle.nativeplatform.toolchain.internal.compilespec.WindowsResourceCompileSpec;
+
+import java.util.List;
+
+class WindowsResourceCompiler extends VisualCppNativeCompiler<WindowsResourceCompileSpec> {
+
+    WindowsResourceCompiler(BuildOperationProcessor buildOperationProcessor, CommandLineToolInvocationWorker commandLineTool, CommandLineToolContext invocationContext, Transformer<WindowsResourceCompileSpec, WindowsResourceCompileSpec> specTransformer, String objectFileExtension, boolean useCommandFile) {
+        super(buildOperationProcessor, commandLineTool, invocationContext, new RcCompilerArgsTransformer(), specTransformer, objectFileExtension, useCommandFile);
+    }
+
+    @Override
+    protected Iterable<String> buildPerFileArgs(List<String> genericArgs, List<String> sourceArgs, List<String> outputArgs, List<String> pchArgs) {
+        // RC has position sensitive arguments, the output args need to appear before the source file
+        return Iterables.concat(genericArgs, outputArgs, sourceArgs);
+    }
+
+    private static class RcCompilerArgsTransformer extends VisualCppCompilerArgsTransformer<WindowsResourceCompileSpec> {
+        protected void addToolSpecificArgs(WindowsResourceCompileSpec spec, List<String> args) {
+            args.add(getLanguageOption());
+            args.add("/nologo");
+        }
+        protected String getLanguageOption() {
+            return "/r";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdk.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdk.java
new file mode 100644
index 0000000..f06af48
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdk.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.api.Named;
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal;
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+
+public class WindowsSdk implements Named {
+    private static final String[] BINPATHS_X86 = {
+        "bin/x86",
+        "Bin"
+    };
+    private static final String[] BINPATHS_AMD64 = {
+        "bin/x64"
+    };
+    private static final String[] BINPATHS_IA64 = {
+        "bin/IA64"
+    };
+    private static final String[] BINPATHS_ARM = {
+        "bin/arm"
+    };
+    private static final String LIBPATH_SDK8 = "Lib/win8/um/";
+    private static final String LIBPATH_SDK81 = "Lib/winv6.3/um/";
+    private static final String[] LIBPATHS_X86 = {
+        LIBPATH_SDK81 + "x86",
+        LIBPATH_SDK8 + "x86",
+        "lib"
+    };
+    private static final String[] LIBPATHS_AMD64 = {
+        LIBPATH_SDK81 + "x64",
+        LIBPATH_SDK8 + "x64",
+        "lib/x64"
+    };
+    private static final String[] LIBPATHS_IA64 = {
+        "lib/IA64"
+    };
+    private static final String[] LIBPATHS_ARM = {
+        LIBPATH_SDK81 + "arm",
+        LIBPATH_SDK8 + "arm"
+    };
+
+    private final File baseDir;
+    private final VersionNumber version;
+    private final String name;
+
+    public WindowsSdk(File baseDir, VersionNumber version, String name) {
+        this.baseDir = baseDir;
+        this.version = version;
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public VersionNumber getVersion() {
+        return version;
+    }
+
+    public File getResourceCompiler(NativePlatformInternal platform) {
+        return new File(getBinDir(platform), "rc.exe");
+    }
+
+    public File getBinDir(NativePlatformInternal platform) {
+        if (architecture(platform).isAmd64()) {
+            return getAvailableFile(BINPATHS_AMD64);
+        }
+        if (architecture(platform).isIa64()) {
+            return getAvailableFile(BINPATHS_IA64);
+        }
+        if (architecture(platform).isArm()) {
+            return getAvailableFile(BINPATHS_ARM);
+        }
+        return getAvailableFile(BINPATHS_X86);
+    }
+
+    public File[] getIncludeDirs() {
+        File[] includesSdk8 = new File[] {
+            new File(baseDir, "Include/shared"),
+            new File(baseDir, "Include/um")
+        };
+        for (File file : includesSdk8) {
+            if (!file.isDirectory()) {
+                return new File[] {
+                    new File(baseDir, "Include")
+                };
+            }
+        }
+        return includesSdk8;
+    }
+
+    public File getLibDir(NativePlatformInternal platform) {
+        if (architecture(platform).isAmd64()) {
+            return getAvailableFile(LIBPATHS_AMD64);
+        }
+        if (architecture(platform).isIa64()) {
+            return getAvailableFile(LIBPATHS_IA64);
+        }
+        if (architecture(platform).isArm()) {
+            return getAvailableFile(LIBPATHS_ARM);
+        }
+        return getAvailableFile(LIBPATHS_X86);
+    }
+
+    private ArchitectureInternal architecture(NativePlatformInternal platform) {
+        return platform.getArchitecture();
+    }
+
+    private File getAvailableFile(String... candidates) {
+        for (String candidate : candidates) {
+            File file = new File(baseDir, candidate);
+            if (file.isDirectory()) {
+                return file;
+            }
+        }
+
+        return new File(baseDir, candidates[0]);
+    }
+
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdkLocator.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdkLocator.java
new file mode 100644
index 0000000..b8d20b2
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsSdkLocator.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.nativeplatform.toolchain.internal.msvcpp;
+
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+
+import java.io.File;
+
+public interface WindowsSdkLocator {
+
+    SearchResult locateWindowsSdks(File candidate);
+
+    interface SearchResult extends ToolSearchResult {
+        WindowsSdk getSdk();
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/plugins/StandardToolChainsPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/plugins/StandardToolChainsPlugin.java
new file mode 100644
index 0000000..70fdfcf
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/plugins/StandardToolChainsPlugin.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.nativeplatform.toolchain.internal.plugins;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.nativeplatform.toolchain.plugins.ClangCompilerPlugin;
+import org.gradle.nativeplatform.toolchain.plugins.GccCompilerPlugin;
+import org.gradle.nativeplatform.toolchain.plugins.MicrosoftVisualCppPlugin;
+
+/**
+ * Registers the standard tool chains.
+ */
+public class StandardToolChainsPlugin implements Plugin<Project> {
+    public void apply(Project project) {
+        project.getPluginManager().apply(MicrosoftVisualCppPlugin.class);
+        project.getPluginManager().apply(GccCompilerPlugin.class);
+        project.getPluginManager().apply(ClangCompilerPlugin.class);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolConfigurationInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolConfigurationInternal.java
new file mode 100644
index 0000000..9faa367
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolConfigurationInternal.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.api.Action;
+import org.gradle.nativeplatform.toolchain.CommandLineToolConfiguration;
+
+import java.util.List;
+
+public interface CommandLineToolConfigurationInternal extends CommandLineToolConfiguration{
+
+    public Action<List<String>> getArgAction();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java
new file mode 100644
index 0000000..bf1081f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.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.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult;
+
+import java.io.File;
+
+public interface CommandLineToolSearchResult extends ToolSearchResult {
+    File getTool();
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultCommandLineToolConfiguration.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultCommandLineToolConfiguration.java
new file mode 100644
index 0000000..cb4d985
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultCommandLineToolConfiguration.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.api.Action;
+import org.gradle.internal.Actions;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultCommandLineToolConfiguration implements CommandLineToolConfigurationInternal {
+    private final ToolType toolType;
+    private List<Action<? super List<String>>> argActions = new ArrayList<Action<? super List<String>>>();
+
+    public DefaultCommandLineToolConfiguration(ToolType toolType) {
+        this.toolType = toolType;
+    }
+
+    public ToolType getToolType() {
+        return toolType;
+    }
+
+    public void withArguments(Action<? super List<String>>  action) {
+        argActions.add(action);
+    }
+
+    public Action<List<String>> getArgAction() {
+        return Actions.composite(argActions);
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultGccCommandLineToolConfiguration.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultGccCommandLineToolConfiguration.java
new file mode 100644
index 0000000..be42440
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/DefaultGccCommandLineToolConfiguration.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.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+
+public class DefaultGccCommandLineToolConfiguration extends DefaultCommandLineToolConfiguration implements GccCommandLineToolConfigurationInternal {
+    private String executable;
+
+    public DefaultGccCommandLineToolConfiguration(ToolType toolType, String defaultExecutable) {
+        super(toolType);
+        this.executable = defaultExecutable;
+    }
+
+    public String getExecutable() {
+        return executable;
+    }
+
+    public void setExecutable(String file) {
+        executable = file;
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/GccCommandLineToolConfigurationInternal.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/GccCommandLineToolConfigurationInternal.java
new file mode 100644
index 0000000..802478e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/GccCommandLineToolConfigurationInternal.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.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.api.Action;
+import org.gradle.nativeplatform.toolchain.GccCommandLineToolConfiguration;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+
+import java.util.List;
+
+public interface GccCommandLineToolConfigurationInternal extends GccCommandLineToolConfiguration {
+
+    ToolType getToolType();
+
+    Action<List<String>> getArgAction();
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolRegistry.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolRegistry.java
new file mode 100644
index 0000000..b29e457
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolRegistry.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.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+
+public interface ToolRegistry {
+    GccCommandLineToolConfigurationInternal getTool(ToolType toolType);
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPath.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPath.java
new file mode 100644
index 0000000..f23e5d4
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPath.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.tools;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.nativeplatform.toolchain.internal.ToolType;
+import org.gradle.util.TreeVisitor;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ToolSearchPath {
+    private final Map<String, File> executables = new HashMap<String, File>();
+    private final List<File> pathEntries = new ArrayList<File>();
+
+    private final OperatingSystem operatingSystem;
+
+    public ToolSearchPath(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 CommandLineToolSearchResult locate(ToolType key, String exeName) {
+        File executable = executables.get(exeName);
+        if (executable == null) {
+            executable = findExecutable(operatingSystem, exeName);
+            if (executable != null) {
+                executables.put(exeName, executable);
+            }
+        }
+        return executable == null || !executable.isFile() ? new MissingTool(key, exeName, pathEntries) : new FoundTool(executable);
+    }
+
+    private File findExecutable(OperatingSystem operatingSystem, String name) {
+        List<File> path = pathEntries.isEmpty() ? operatingSystem.getPath() : pathEntries;
+        String exeName = operatingSystem.getExecutableName(name);
+        try {
+            if (name.contains(File.separator)) {
+                return maybeResolveFile(operatingSystem, new File(name), new File(exeName));
+            }
+            for (File pathEntry : path) {
+                File resolved = maybeResolveFile(operatingSystem, new File(pathEntry, name), new File(pathEntry, exeName));
+                if (resolved != null) {
+                    return resolved;
+                }
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+
+        return null;
+    }
+
+    private File maybeResolveFile(OperatingSystem operatingSystem, File symlinkCandidate, File exeCandidate) throws IOException {
+        if (exeCandidate.isFile()) {
+            return exeCandidate;
+        }
+        if (operatingSystem.isWindows()) {
+            File symlink = maybeResolveCygwinSymlink(symlinkCandidate);
+            if (symlink != null) {
+                return symlink;
+            }
+        }
+        return null;
+    }
+
+    private File maybeResolveCygwinSymlink(File symlink) throws IOException {
+        if (!symlink.isFile()) {
+            return null;
+        }
+        if (symlink.length() <= 11) {
+            return null;
+        }
+
+        String pathStr;
+        DataInputStream instr = new DataInputStream(new BufferedInputStream(new FileInputStream(symlink)));
+        try {
+            byte[] header = new byte[10];
+            instr.readFully(header);
+            if (!new String(header, "utf-8").equals("!<symlink>")) {
+                return null;
+            }
+            byte[] pathContent = new byte[(int) symlink.length() - 11];
+            instr.readFully(pathContent);
+            pathStr = new String(pathContent, "utf-8");
+        } finally {
+            instr.close();
+        }
+
+        symlink = new File(symlink.getParentFile(), pathStr);
+        if (symlink.isFile()) {
+            return symlink.getCanonicalFile();
+        }
+        return null;
+    }
+
+    private static class FoundTool implements CommandLineToolSearchResult {
+        private final File tool;
+
+        private FoundTool(File tool) {
+            this.tool = tool;
+        }
+
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public File getTool() {
+            return tool;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+        }
+    }
+
+    private static class MissingTool implements CommandLineToolSearchResult {
+        private final ToolType type;
+        private final String exeName;
+        private final List<File> path;
+
+        private MissingTool(ToolType type, String exeName, List<File> path) {
+            this.type = type;
+            this.exeName = exeName;
+            this.path = path;
+        }
+
+        public void explain(TreeVisitor<? super String> visitor) {
+            if (path.isEmpty()) {
+                visitor.node(String.format("Could not find %s '%s' in system path.", type.getToolName(), exeName));
+            } else {
+                visitor.node(String.format("Could not find %s '%s'. Searched in", type.getToolName(), exeName));
+                visitor.startChildren();
+                for (File location : path) {
+                    visitor.node(location.toString());
+                }
+                visitor.endChildren();
+            }
+        }
+
+        public File getTool() {
+            TreeFormatter formatter = new TreeFormatter();
+            explain(formatter);
+            throw new GradleException(formatter.toString());
+        }
+
+        public boolean isAvailable() {
+            return false;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/package-info.java
new file mode 100644
index 0000000..5ab6a5f
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.nativeplatform.toolchain;
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPlugin.java
new file mode 100644
index 0000000..d9e598e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPlugin.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Defaults;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal;
+import org.gradle.nativeplatform.toolchain.internal.clang.ClangToolChain;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory;
+import org.gradle.process.internal.ExecActionFactory;
+
+/**
+ * A {@link Plugin} which makes the <a href="http://clang.llvm.org">Clang</a> compiler available for compiling C/C++ code.
+ */
+ at Incubating
+public class ClangCompilerPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+    }
+
+    static class Rules extends RuleSource {
+        @Defaults
+        public static void addToolChain(NativeToolChainRegistryInternal toolChainRegistry, ServiceRegistry serviceRegistry) {
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final ExecActionFactory execActionFactory = serviceRegistry.get(ExecActionFactory.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            final BuildOperationProcessor buildOperationProcessor = serviceRegistry.get(BuildOperationProcessor.class);
+            final CompilerMetaDataProviderFactory metaDataProviderFactory = serviceRegistry.get(CompilerMetaDataProviderFactory.class);
+
+            toolChainRegistry.registerFactory(Clang.class, new NamedDomainObjectFactory<Clang>() {
+                public Clang create(String name) {
+                    return instantiator.newInstance(ClangToolChain.class, name, buildOperationProcessor, OperatingSystem.current(), fileResolver, execActionFactory, metaDataProviderFactory, instantiator);
+                }
+            });
+            toolChainRegistry.registerDefaultToolChain(ClangToolChain.DEFAULT_NAME, Clang.class);
+        }
+
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPlugin.java
new file mode 100644
index 0000000..a039a94
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPlugin.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Defaults;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal;
+import org.gradle.nativeplatform.toolchain.internal.gcc.GccToolChain;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory;
+import org.gradle.process.internal.ExecActionFactory;
+
+/**
+ * A {@link Plugin} which makes the <a href="http://gcc.gnu.org/">GNU GCC/G++ compiler</a> available for compiling C/C++ code.
+ */
+ at Incubating
+public class GccCompilerPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+    }
+
+    static class Rules extends RuleSource {
+        @Defaults
+        public static void addToolChain(NativeToolChainRegistryInternal toolChainRegistry, ServiceRegistry serviceRegistry) {
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final ExecActionFactory execActionFactory = serviceRegistry.get(ExecActionFactory.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+
+            final BuildOperationProcessor buildOperationProcessor = serviceRegistry.get(BuildOperationProcessor.class);
+
+            final CompilerMetaDataProviderFactory metaDataProviderFactory = serviceRegistry.get(CompilerMetaDataProviderFactory.class);
+
+            toolChainRegistry.registerFactory(Gcc.class, new NamedDomainObjectFactory<Gcc>() {
+                public Gcc create(String name) {
+                    return instantiator.newInstance(GccToolChain.class, instantiator, name, buildOperationProcessor, OperatingSystem.current(), fileResolver, execActionFactory, metaDataProviderFactory);
+                }
+            });
+            toolChainRegistry.registerDefaultToolChain(GccToolChain.DEFAULT_NAME, Gcc.class);
+        }
+
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPlugin.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPlugin.java
new file mode 100755
index 0000000..f04300e
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPlugin.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.operations.BuildOperationProcessor;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Defaults;
+import org.gradle.model.RuleSource;
+import org.gradle.nativeplatform.plugins.NativeComponentPlugin;
+import org.gradle.nativeplatform.toolchain.VisualCpp;
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioLocator;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsSdkLocator;
+import org.gradle.process.internal.ExecActionFactory;
+
+/**
+ * A {@link Plugin} which makes the Microsoft Visual C++ compiler available to compile C/C++ code.
+ */
+ at Incubating
+public class MicrosoftVisualCppPlugin implements Plugin<Project> {
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(NativeComponentPlugin.class);
+    }
+
+    static class Rules extends RuleSource {
+        @Defaults
+        public static void addToolChain(NativeToolChainRegistryInternal toolChainRegistry, ServiceRegistry serviceRegistry) {
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final ExecActionFactory execActionFactory = serviceRegistry.get(ExecActionFactory.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            final OperatingSystem operatingSystem = serviceRegistry.get(OperatingSystem.class);
+            final BuildOperationProcessor buildOperationProcessor = serviceRegistry.get(BuildOperationProcessor.class);
+            final VisualStudioLocator visualStudioLocator = serviceRegistry.get(VisualStudioLocator.class);
+            final WindowsSdkLocator windowsSdkLocator = serviceRegistry.get(WindowsSdkLocator.class);
+
+            toolChainRegistry.registerFactory(VisualCpp.class, new NamedDomainObjectFactory<VisualCpp>() {
+                public VisualCpp create(String name) {
+                    return instantiator.newInstance(VisualCppToolChain.class, name, buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator, instantiator);
+                }
+            });
+            toolChainRegistry.registerDefaultToolChain(VisualCppToolChain.DEFAULT_NAME, VisualCpp.class);
+        }
+
+    }
+}
diff --git a/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/package-info.java b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/toolchain/plugins/package-info.java
new file mode 100644
index 0000000..f3a4278
--- /dev/null
+++ b/subprojects/platform-native/src/main/groovy/org/gradle/nativeplatform/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.
+ */
+
+/**
+ * Built-in tool chain support.
+ */
+package org.gradle.nativeplatform.toolchain.plugins;
diff --git a/subprojects/platform-native/src/main/resources/META-INF/gradle-plugins/org.gradle.native-component.properties b/subprojects/platform-native/src/main/resources/META-INF/gradle-plugins/org.gradle.native-component.properties
new file mode 100644
index 0000000..732f40e
--- /dev/null
+++ b/subprojects/platform-native/src/main/resources/META-INF/gradle-plugins/org.gradle.native-component.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.nativeplatform.plugins.NativeComponentPlugin
\ No newline at end of file
diff --git a/subprojects/platform-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/platform-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..7366a44
--- /dev/null
+++ b/subprojects/platform-native/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.nativeplatform.internal.services.NativeBinaryServices
\ No newline at end of file
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeTest.groovy
new file mode 100644
index 0000000..6c968ca
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultBuildTypeTest.groovy
@@ -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.nativeplatform.internal
+
+import spock.lang.Specification
+
+class DefaultBuildTypeTest extends Specification {
+    def "has useful string representation"() {
+        def buildType = new DefaultBuildType("release")
+
+        expect:
+        buildType.toString() == "build type 'release'"
+        buildType.displayName == "build type 'release'"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultFlavorTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultFlavorTest.groovy
new file mode 100644
index 0000000..bae60ab
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultFlavorTest.groovy
@@ -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.nativeplatform.internal
+
+import spock.lang.Specification
+
+class DefaultFlavorTest extends Specification {
+    def "has useful string representation"() {
+        def flavor = new DefaultFlavor("someFlavor")
+
+        expect:
+        flavor.toString() == "flavor 'someFlavor'"
+        flavor.displayName == "flavor 'someFlavor'"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeComponentTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeComponentTest.groovy
new file mode 100644
index 0000000..c83fc43
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeComponentTest.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.nativeplatform.internal
+import org.gradle.api.internal.AsmBackedClassGenerator
+import org.gradle.api.internal.ClassGeneratorBackedInstantiator
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import spock.lang.Specification
+
+class DefaultNativeComponentTest extends Specification {
+    def instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
+    def id = new DefaultComponentSpecIdentifier("project", "name")
+    FunctionalSourceSet mainSourceSet
+    def component
+
+    def setup(){
+        mainSourceSet = new DefaultFunctionalSourceSet("testFunctionalSourceSet", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet))
+        component = BaseComponentSpec.create(TestNativeComponentSpec, id, mainSourceSet, instantiator)
+    }
+
+    def "flavors can be chosen and will replace default flavor"() {
+        when:
+        component.targetFlavors "flavor1", "flavor2"
+
+        and:
+        component.targetFlavors("flavor3")
+
+        then:
+        component.chooseFlavors([flavor("flavor1"), flavor("flavor2"), flavor("flavor3"), flavor("flavor4")] as Set)*.name == ["flavor1", "flavor2", "flavor3"]
+    }
+
+    static class TestNativeComponentSpec extends AbstractTargetedNativeComponentSpec {
+        String getDisplayName() {
+            return "test component"
+        }
+    }
+
+    def flavor(String name) {
+        new DefaultFlavor(name)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpecTest.groovy
new file mode 100644
index 0000000..d00f112
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableBinarySpecTest.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.nativeplatform.internal
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.internal.configure.DefaultNativeBinariesFactory
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.tasks.InstallExecutable
+import org.gradle.nativeplatform.tasks.LinkExecutable
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultBinaryNamingScheme
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class DefaultNativeExecutableBinarySpecTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+    def namingScheme = new DefaultBinaryNamingScheme("bigOne", "executable", [])
+    def tasks = new DefaultNativeExecutableBinarySpec.DefaultTasksCollection(new DefaultBinaryTasksCollection(null, Mock(ITaskFactory)))
+
+    def "has useful string representation"() {
+        given:
+        def executable = BaseComponentSpec.create(DefaultNativeExecutableSpec, new DefaultComponentSpecIdentifier("path", "name"), new DefaultFunctionalSourceSet("name", instantiator, Stub(ProjectSourceSet)), instantiator)
+
+        when:
+        def binary = DefaultNativeBinariesFactory.create(DefaultNativeExecutableBinarySpec, instantiator, executable, namingScheme, Mock(NativeDependencyResolver), Stub(NativeToolChainInternal), Stub(PlatformToolProvider), Stub(NativePlatform), Stub(BuildType), new DefaultFlavor("flavorOne"), Mock(ITaskFactory))
+
+        then:
+        binary.toString() == "executable 'bigOne:executable'"
+    }
+
+    def "returns null for link and install when none defined"() {
+        expect:
+        tasks.link == null
+        tasks.install == null
+    }
+
+    def "returns link task when defined"() {
+        when:
+        final linkTask = TestUtil.createTask(LinkExecutable)
+        tasks.add(linkTask)
+
+        then:
+        tasks.link == linkTask
+    }
+
+    def "returns install task when defined"() {
+        when:
+        final install = TestUtil.createTask(InstallExecutable)
+        tasks.add(install)
+
+        then:
+        tasks.install == install
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpecTest.groovy
new file mode 100644
index 0000000..162e1f2
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpecTest.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.nativeplatform.internal
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import spock.lang.Specification
+
+class DefaultNativeExecutableSpecTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+    def mainSourceSet = new DefaultFunctionalSourceSet("testFS", instantiator, Stub(ProjectSourceSet))
+    def executable = BaseComponentSpec.create(DefaultNativeExecutableSpec, new DefaultComponentSpecIdentifier("project-path", "someExe"), mainSourceSet, instantiator)
+
+    def "has useful string representation"() {
+        expect:
+        executable.toString() == "native executable 'someExe'"
+        executable.displayName == "native executable 'someExe'"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpecTest.groovy
new file mode 100644
index 0000000..d696e61
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultNativeLibrarySpecTest.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.nativeplatform.internal
+
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import spock.lang.Specification
+
+class DefaultNativeLibrarySpecTest extends Specification {
+    FunctionalSourceSet mainSourceSet = new DefaultFunctionalSourceSet("testFS", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet))
+    final library = BaseComponentSpec.create(DefaultNativeLibrarySpec, new DefaultComponentSpecIdentifier("project-path", "someLib"), mainSourceSet, DirectInstantiator.INSTANCE)
+
+    def "has useful string representation"() {
+        expect:
+        library.toString() == "native library 'someLib'"
+        library.displayName == "native library 'someLib'"
+    }
+
+    def "can use shared variant as requirement"() {
+        when:
+        def requirement = library.shared
+
+        then:
+        requirement.projectPath == 'project-path'
+        requirement.libraryName == 'someLib'
+        requirement.linkage == 'shared'
+    }
+
+    def "can use static variant as requirement"() {
+        when:
+        def requirement = library.static
+
+        then:
+        requirement.projectPath == 'project-path'
+        requirement.libraryName == 'someLib'
+        requirement.linkage == 'static'
+    }
+
+    def "can use api linkage as requirement"() {
+        when:
+        def requirement = library.api
+
+        then:
+        requirement.projectPath == 'project-path'
+        requirement.libraryName == 'someLib'
+        requirement.linkage == 'api'
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpecTest.groovy
new file mode 100644
index 0000000..26c90ba
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultSharedLibraryBinarySpecTest.groovy
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal
+
+import org.gradle.api.Task
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.language.nativeplatform.NativeResourceSet
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.tasks.LinkSharedLibrary
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultBinaryNamingScheme
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.nativeplatform.internal.configure.DefaultNativeBinariesFactory.create
+
+class DefaultSharedLibraryBinarySpecTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir
+    def instantiator = DirectInstantiator.INSTANCE
+    def namingScheme = new DefaultBinaryNamingScheme("main", "sharedLibrary", [])
+    final toolChain = Stub(NativeToolChainInternal)
+    final platform = Stub(NativePlatform)
+    final buildType = Stub(BuildType)
+    final resolver = Stub(NativeDependencyResolver)
+    def sharedLibraryFile = Mock(File)
+    def sharedLibraryLinkFile = Mock(File)
+
+    def "has useful string representation"() {
+        expect:
+        sharedLibrary.toString() == "shared library 'main:sharedLibrary'"
+    }
+
+    def "can set output files"() {
+        given:
+        def binary = sharedLibrary
+
+        when:
+        binary.sharedLibraryFile = sharedLibraryFile
+        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
+
+        then:
+        binary.sharedLibraryFile == sharedLibraryFile
+        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
+    }
+
+    def "can convert binary to a native dependency"() {
+        given:
+        def binary = sharedLibrary
+        binary.sharedLibraryFile = sharedLibraryFile
+        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
+        def lifecycleTask = Stub(Task)
+        binary.setBuildTask(lifecycleTask)
+        binary.builtBy(Stub(Task))
+
+        and: "has at least one header exporting source set"
+        final headerDir = tmpDir.createDir("headerDir")
+        def headerDirSet = Stub(SourceDirectorySet) {
+            getSrcDirs() >> [headerDir]
+        }
+        def sourceDirSet = Stub(SourceDirectorySet) {
+            getFiles() >> [tmpDir.createFile("input.src")]
+        }
+        def sourceSet = Stub(HeaderExportingSourceSet) {
+            getSource() >> sourceDirSet
+            getExportedHeaders() >> headerDirSet
+        }
+        binary.source sourceSet
+
+        expect:
+        binary.sharedLibraryFile == sharedLibraryFile
+        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
+
+        binary.headerDirs.files == [headerDir] as Set
+
+        and:
+        binary.linkFiles.files == [binary.sharedLibraryLinkFile] as Set
+        binary.linkFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
+        binary.linkFiles.toString() == "shared library 'main:sharedLibrary'"
+
+        and:
+        binary.runtimeFiles.files == [binary.sharedLibraryFile] as Set
+        binary.runtimeFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
+        binary.runtimeFiles.toString() == "shared library 'main:sharedLibrary'"
+    }
+
+    def "has empty link files when has resources and no symbols are exported from library"() {
+        when:
+        def binary = sharedLibrary
+        def sourceDirSet = Stub(SourceDirectorySet) {
+            getFiles() >> [tmpDir.createFile("input.rc")]
+        }
+        def resourceSet = Stub(NativeResourceSet) {
+            getSource() >> sourceDirSet
+        }
+        binary.source resourceSet
+
+        def binaryFile = tmpDir.createFile("binary.run")
+        def linkFile = tmpDir.createFile("binary.link")
+        toolChain.getSharedLibraryLinkFileName(binaryFile.path) >> linkFile.path
+
+        then:
+        binary.linkFiles.files == [] as Set
+    }
+
+    def "returns null for link and builder when none defined"() {
+        given:
+        def binary = sharedLibrary
+
+        expect:
+        binary.tasks.build == null
+        binary.tasks.link == null
+    }
+
+    def "returns link task when defined"() {
+        given:
+        def binary = sharedLibrary
+
+        when:
+        final linkTask = TestUtil.createTask(LinkSharedLibrary)
+        binary.tasks.add(linkTask)
+
+        then:
+        binary.tasks.link == linkTask
+    }
+
+    private DefaultSharedLibraryBinarySpec getSharedLibrary() {
+        final library = BaseComponentSpec.create(DefaultNativeLibrarySpec, new DefaultComponentSpecIdentifier("path", "libName"), new DefaultFunctionalSourceSet("name", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet)), instantiator);
+        return create(DefaultSharedLibraryBinarySpec, instantiator, library, namingScheme, resolver, toolChain, Stub(PlatformToolProvider), platform, buildType, new DefaultFlavor("flavorOne"), Mock(ITaskFactory))
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpecTest.groovy
new file mode 100644
index 0000000..143e8dc
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/DefaultStaticLibraryBinarySpecTest.groovy
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal
+
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.language.nativeplatform.HeaderExportingSourceSet
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.tasks.CreateStaticLibrary
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultBinaryNamingScheme
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.gradle.nativeplatform.internal.configure.DefaultNativeBinariesFactory.create
+
+class DefaultStaticLibraryBinarySpecTest extends Specification {
+    @Rule
+    TestNameTestDirectoryProvider tmpDir
+    def instantiator = DirectInstantiator.INSTANCE
+    final library = BaseComponentSpec.create(DefaultNativeLibrarySpec, new DefaultComponentSpecIdentifier("path", "libName"), new DefaultFunctionalSourceSet("name", instantiator, Stub(ProjectSourceSet)), instantiator)
+    def namingScheme = new DefaultBinaryNamingScheme("main", "staticLibrary", [])
+    def toolChain = Stub(NativeToolChainInternal)
+    def platform = Stub(NativePlatform)
+    def buildType = Stub(BuildType)
+    final resolver = Stub(NativeDependencyResolver)
+    final outputFile = Mock(File)
+    def tasks = new DefaultStaticLibraryBinarySpec.DefaultTasksCollection(new DefaultBinaryTasksCollection(null, Mock(ITaskFactory)))
+
+    def "has useful string representation"() {
+        expect:
+        staticLibrary.toString() == "static library 'main:staticLibrary'"
+    }
+
+    def getStaticLibrary() {
+        create(DefaultStaticLibraryBinarySpec, instantiator, library, namingScheme, resolver, toolChain, Stub(PlatformToolProvider), platform, buildType, new DefaultFlavor("flavorOne"), Mock(ITaskFactory))
+    }
+
+    def "can set output file"() {
+        given:
+        final binary = staticLibrary
+        def outputFile = Mock(File)
+
+        when:
+        binary.staticLibraryFile = outputFile
+
+        then:
+        binary.staticLibraryFile == outputFile
+    }
+
+    def "can convert binary to a native dependency"() {
+        final binary = staticLibrary
+        given:
+        def lifecycleTask = Stub(Task)
+        binary.buildTask = lifecycleTask
+        binary.builtBy(Stub(Task))
+
+        and:
+        binary.staticLibraryFile = outputFile
+
+        and:
+        final headerDir = tmpDir.createDir("headerDir")
+        addSources(binary, headerDir)
+
+        expect:
+        binary.headerDirs.files == [headerDir] as Set
+        binary.staticLibraryFile == outputFile
+
+        and:
+        binary.linkFiles.files == [binary.staticLibraryFile] as Set
+        binary.linkFiles.buildDependencies.getDependencies(Stub(Task)) == [lifecycleTask] as Set
+        binary.linkFiles.toString() == "static library 'main:staticLibrary'"
+
+        and:
+        binary.runtimeFiles.files.isEmpty()
+        binary.runtimeFiles.buildDependencies.getDependencies(Stub(Task)) == [] as Set
+    }
+
+    def "includes additional link files in native dependency"() {
+        final binary = staticLibrary
+        given:
+        binary.staticLibraryFile = outputFile
+        def linkFile1 = Mock(File)
+        def linkFile2 = Mock(File)
+        def additionalLinkFiles = Stub(FileCollection) {
+            getFiles() >> [linkFile1, linkFile2]
+        }
+        binary.additionalLinkFiles(additionalLinkFiles)
+
+        and:
+        addSources(binary, tmpDir.createDir("headerDir"))
+
+        expect:
+        binary.staticLibraryFile == outputFile
+        binary.linkFiles.files == [binary.staticLibraryFile, linkFile1, linkFile2] as Set
+    }
+
+    def "returns null for createStaticLib and builder when none defined"() {
+        expect:
+        tasks.createStaticLib == null
+    }
+
+    def "returns create task when defined"() {
+        when:
+        final createTask = TestUtil.createTask(CreateStaticLibrary)
+        tasks.add(createTask)
+
+        then:
+        tasks.createStaticLib == createTask
+    }
+
+    private TestFile addSources(DefaultStaticLibraryBinarySpec binary, def headerDir) {
+        def headerDirSet = Stub(SourceDirectorySet) {
+            getSrcDirs() >> [headerDir]
+        }
+        def sourceDirSet = Stub(SourceDirectorySet) {
+            getFiles() >> [tmpDir.createFile("input.src")]
+        }
+        def sourceSet = Stub(HeaderExportingSourceSet) {
+            getSource() >> sourceDirSet
+            getExportedHeaders() >> headerDirSet
+        }
+        binary.source sourceSet
+        headerDir
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecTest.groovy
new file mode 100644
index 0000000..7f7d376
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/NativeBinarySpecTest.groovy
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.LanguageSourceSet
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.language.nativeplatform.DependentSourceSet
+import org.gradle.nativeplatform.*
+import org.gradle.nativeplatform.internal.resolve.NativeBinaryResolveResult
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.Architectures
+import org.gradle.nativeplatform.tasks.ObjectFilesToBinary
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultBinaryNamingScheme
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import spock.lang.Specification
+
+import static org.gradle.nativeplatform.internal.configure.DefaultNativeBinariesFactory.create
+
+class NativeBinarySpecTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+    def flavor1 = new DefaultFlavor("flavor1")
+    def id = new DefaultComponentSpecIdentifier("project", "name")
+    def sourceSet = new DefaultFunctionalSourceSet("testFunctionalSourceSet", instantiator, Stub(ProjectSourceSet))
+    def component = BaseComponentSpec.create(TestNativeComponentSpec, id, sourceSet, instantiator)
+
+    def toolChain1 = Stub(NativeToolChainInternal) {
+        getName() >> "ToolChain1"
+    }
+    def platform1 = Stub(NativePlatform) {
+        getArchitecture() >> Architectures.forInput("i386")
+    }
+    def buildType1 = Stub(BuildType) {
+        getName() >> "BuildType1"
+    }
+    def resolver = Mock(NativeDependencyResolver)
+
+    def "binary uses source from its owner component"() {
+        given:
+        def sourceSet = Stub(LanguageSourceSet)
+
+        when:
+        component.sources.add(sourceSet)
+        def binary = testBinary(component)
+
+        then:
+        binary.source.contains(sourceSet)
+    }
+
+    def "binary uses all source sets from a functional source set"() {
+        given:
+        def binary = testBinary(component)
+        def functionalSourceSet = new DefaultFunctionalSourceSet("func", instantiator, Stub(ProjectSourceSet))
+        def sourceSet1 = Stub(LanguageSourceSet) {
+            getName() >> "ss1"
+        }
+        def sourceSet2 = Stub(LanguageSourceSet) {
+            getName() >> "ss2"
+        }
+
+        when:
+        functionalSourceSet.add(sourceSet1)
+        functionalSourceSet.add(sourceSet2)
+
+        and:
+        binary.source functionalSourceSet
+
+        then:
+        binary.source.contains(sourceSet1)
+        binary.source.contains(sourceSet2)
+    }
+
+    def "uses resolver to resolve lib to dependency"() {
+        def binary = testBinary(component, flavor1)
+        def lib = new Object()
+        def dependency = Stub(NativeDependencySet)
+
+        when:
+        binary.lib(lib)
+
+        and:
+        1 * resolver.resolve({ NativeBinaryResolveResult result ->
+            result.allResolutions*.input == [lib]
+        }) >> { NativeBinaryResolveResult result ->
+            result.allResolutions[0].nativeDependencySet = dependency
+        }
+
+        then:
+        binary.libs// == [dependency]
+    }
+
+    def "binary libs include source set dependencies"() {
+        def binary = testBinary(component)
+        def lib = new Object()
+        def dependency = Stub(NativeDependencySet)
+
+        when:
+        def sourceSet = Stub(DependentSourceSet) {
+            getLibs() >> [lib]
+        }
+        binary.source sourceSet
+
+        1 * resolver.resolve({ NativeBinaryResolveResult result ->
+            result.allResolutions*.input == [lib]
+        }) >> { NativeBinaryResolveResult result ->
+            result.allResolutions[0].nativeDependencySet = dependency
+        }
+
+        then:
+        binary.getLibs(sourceSet) == [dependency]
+    }
+
+    def "order of libraries is maintained"() {
+        def binary = testBinary(component)
+        def libraryBinary = Mock(NativeLibraryBinary)
+        def dependency1 = Stub(NativeDependencySet)
+        def dependency2 = Stub(NativeDependencySet)
+        def dependency3 = Stub(NativeDependencySet)
+
+        when:
+        binary.lib(dependency1)
+        binary.lib(libraryBinary)
+        binary.lib(dependency3)
+
+        and:
+        1 * resolver.resolve({ NativeBinaryResolveResult result ->
+            result.allResolutions*.input == [dependency1, libraryBinary, dependency3]
+        }) >> { NativeBinaryResolveResult result ->
+            result.allResolutions[0].nativeDependencySet = dependency1
+            result.allResolutions[1].nativeDependencySet = dependency2
+            result.allResolutions[2].nativeDependencySet = dependency3
+        }
+
+        then:
+        binary.libs as List == [dependency1, dependency2, dependency3]
+    }
+
+    def "library added to binary is ordered before library for source set"() {
+        def binary = testBinary(component)
+        def lib1 = new Object()
+        def dep1 = Stub(NativeDependencySet)
+        def lib2 = new Object()
+        def dep2 = Stub(NativeDependencySet)
+        def sourceLib = new Object()
+        def sourceDep = Stub(NativeDependencySet)
+
+        when:
+        binary.lib(lib1)
+        def sourceSet = Stub(DependentSourceSet) {
+            getLibs() >> [sourceLib]
+        }
+        binary.source sourceSet
+        binary.lib(lib2)
+
+        and:
+        1 * resolver.resolve({ NativeBinaryResolveResult result ->
+            result.allResolutions*.input == [lib1, lib2, sourceLib]
+        }) >> { NativeBinaryResolveResult result ->
+            result.allResolutions[0].nativeDependencySet = dep1
+            result.allResolutions[1].nativeDependencySet = dep2
+            result.allResolutions[2].nativeDependencySet = sourceDep
+        }
+
+        then:
+        binary.libs as List == [dep1, dep2, sourceDep]
+    }
+
+    def testBinary(NativeComponentSpec owner, Flavor flavor = new DefaultFlavor(DefaultFlavor.DEFAULT)) {
+        return create(TestNativeBinarySpec, instantiator, owner, new DefaultBinaryNamingScheme("baseName", "", []), resolver, toolChain1, Stub(PlatformToolProvider), platform1, buildType1, flavor, Mock(ITaskFactory))
+    }
+
+    static class TestNativeComponentSpec extends AbstractNativeComponentSpec {
+        String getDisplayName() {
+            return "test component"
+        }
+    }
+
+    static class TestNativeBinarySpec extends AbstractNativeBinarySpec {
+        def owner
+        def tasks = new DefaultBinaryTasksCollection(this, null)
+
+        String getOutputFileName() {
+            return null
+        }
+
+        File getPrimaryOutput() {
+            File binaryOutputDir = getBinaryOutputDir();
+            return new File(binaryOutputDir, getOutputFileName());
+        }
+
+        @Override
+        protected ObjectFilesToBinary getCreateOrLink() {
+            return null;
+        }
+
+        DefaultBinaryTasksCollection getTasks() {
+            return tasks;
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultBuildTypesTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultBuildTypesTest.groovy
new file mode 100644
index 0000000..e67a137
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultBuildTypesTest.groovy
@@ -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.nativeplatform.internal.configure
+
+import org.gradle.nativeplatform.BuildTypeContainer
+import org.gradle.nativeplatform.plugins.NativeComponentModelPlugin
+import spock.lang.Specification
+
+class CreateDefaultBuildTypesTest extends Specification {
+    def buildTypes = Mock(BuildTypeContainer)
+    def rule = new NativeComponentModelPlugin.Rules()
+
+    def "adds a default build type when none configured"() {
+        when:
+        rule.createDefaultBuildTypes(buildTypes)
+
+        then:
+        1 * buildTypes.empty >> true
+        1 * buildTypes.create("debug")
+        0 * buildTypes._
+    }
+
+    def "does not add default build type when some configured"() {
+        when:
+        rule.createDefaultBuildTypes(buildTypes)
+
+        then:
+        1 * buildTypes.empty >> false
+        0 * buildTypes._
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultFlavorsTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultFlavorsTest.groovy
new file mode 100644
index 0000000..0c7275f
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/CreateDefaultFlavorsTest.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.nativeplatform.internal.configure
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.nativeplatform.internal.DefaultFlavor
+import org.gradle.nativeplatform.internal.DefaultFlavorContainer
+import org.gradle.nativeplatform.plugins.NativeComponentModelPlugin
+import spock.lang.Specification
+
+class CreateDefaultFlavorsTest extends Specification {
+    def flavorContainer = new DefaultFlavorContainer(DirectInstantiator.INSTANCE)
+    def rule = new NativeComponentModelPlugin.Rules()
+
+    def "has a single default flavor when not configured"() {
+        when:
+        rule.createDefaultFlavor(flavorContainer)
+
+        then:
+        flavorContainer.size() == 1
+        flavorNames == [DefaultFlavor.DEFAULT] as Set
+    }
+
+    def "configured flavors overwrite default flavor"() {
+        when:
+        flavorContainer.configure {
+            flavor1 {}
+            flavor2 {}
+        }
+        and:
+        rule.createDefaultFlavor(flavorContainer)
+
+        then:
+        flavorNames == ["flavor1", "flavor2"] as Set
+    }
+
+    def "can explicitly add flavor named 'default'"() {
+        when:
+        flavorContainer.configure {
+            flavor1 {}
+            it.'default' {}
+            flavor2 {}
+        }
+        and:
+        rule.createDefaultFlavor(flavorContainer)
+
+        then:
+        flavorNames == [DefaultFlavor.DEFAULT, "flavor1", "flavor2"] as Set
+
+    }
+
+    def getFlavorNames() {
+        return flavorContainer.collect { it.name } as Set
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactoryTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactoryTest.groovy
new file mode 100644
index 0000000..7ca9981
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/DefaultNativeBinariesFactoryTest.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.nativeplatform.internal.configure
+
+import org.gradle.api.Action
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.nativeplatform.*
+import org.gradle.nativeplatform.internal.DefaultNativeExecutableSpec
+import org.gradle.nativeplatform.internal.DefaultNativeLibrarySpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.platform.base.internal.DefaultBinaryNamingSchemeBuilder
+import spock.lang.Specification
+
+class DefaultNativeBinariesFactoryTest extends Specification {
+    def resolver = Mock(NativeDependencyResolver)
+    Action<NativeBinarySpec> configAction = Mock(Action)
+
+    def toolChain = Mock(NativeToolChainInternal)
+    def toolProvider = Mock(PlatformToolProvider)
+    def platform = Mock(NativePlatform)
+    def buildType = Mock(BuildType)
+    def flavor = Mock(Flavor)
+
+    def id = new DefaultComponentSpecIdentifier("project", "name")
+
+    def namingSchemeBuilder = new DefaultBinaryNamingSchemeBuilder().withComponentName("test")
+    def instantiator = DirectInstantiator.INSTANCE;
+    def factory = new DefaultNativeBinariesFactory(instantiator, configAction, resolver, Mock(ITaskFactory))
+    def mainSourceSet = new DefaultFunctionalSourceSet("testFunctionalSourceSet", instantiator, Stub(ProjectSourceSet));
+
+    def "creates binaries for executable"() {
+        given:
+        def executable = BaseComponentSpec.create(DefaultNativeExecutableSpec, id, mainSourceSet, instantiator)
+
+        when:
+        1 * configAction.execute(_)
+
+        and:
+        factory.createNativeBinaries(executable, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+
+        then:
+        executable.binaries.size() == 1
+        def binary = (executable.binaries as List)[0] as NativeBinarySpecInternal
+        binary.name == "testExecutable"
+        binary.toolChain == toolChain
+        binary.platformToolProvider == toolProvider
+        binary.targetPlatform == platform
+        binary.buildType == buildType
+        binary.flavor == flavor
+    }
+
+    def "creates binaries for library"() {
+        given:
+        def library = BaseComponentSpec.create(DefaultNativeLibrarySpec.class, id, mainSourceSet, DirectInstantiator.INSTANCE)
+
+        when:
+        2 * configAction.execute(_)
+
+        and:
+        factory.createNativeBinaries(library, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+
+        then:
+        library.binaries.size() == 2
+        def sharedLibrary = (library.binaries.withType(SharedLibraryBinarySpec) as List)[0] as NativeBinarySpecInternal
+        sharedLibrary.name == "testSharedLibrary"
+        sharedLibrary.toolChain == toolChain
+        sharedLibrary.platformToolProvider == toolProvider
+        sharedLibrary.targetPlatform == platform
+        sharedLibrary.buildType == buildType
+        sharedLibrary.flavor == flavor
+
+        def staticLibrary = (library.binaries.withType(SharedLibraryBinarySpec) as List)[0] as NativeBinarySpecInternal
+        staticLibrary.name == "testSharedLibrary"
+        staticLibrary.toolChain == toolChain
+        staticLibrary.platformToolProvider == toolProvider
+        staticLibrary.targetPlatform == platform
+        staticLibrary.buildType == buildType
+        staticLibrary.flavor == flavor
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializerTest.groovy
new file mode 100644
index 0000000..fb2ba38
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeBinarySpecInitializerTest.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.nativeplatform.internal.configure
+
+import org.gradle.api.Project
+import org.gradle.nativeplatform.NativeComponentSpec
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal
+import org.gradle.nativeplatform.internal.NativeExecutableBinarySpecInternal
+import org.gradle.nativeplatform.internal.SharedLibraryBinarySpecInternal
+import org.gradle.nativeplatform.internal.StaticLibraryBinarySpecInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.internal.BinaryNamingScheme
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class NativeBinarySpecInitializerTest extends Specification {
+    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+
+    def project = Mock(Project)
+    def configAction
+
+    def namingScheme = Mock(BinaryNamingScheme)
+    def toolProvider = Mock(PlatformToolProvider)
+
+    def setup() {
+        project.buildDir >> tmpDir.testDirectory
+        configAction = new NativeBinarySpecInitializer(project.buildDir)
+    }
+
+    def "test executable"() {
+        def binary = initBinary(NativeExecutableBinarySpecInternal, NativeExecutableSpec)
+
+        when:
+        toolProvider.getExecutableName("base_name") >> "exe_name"
+
+        and:
+        configAction.execute(binary)
+
+        then:
+        1 * binary.setExecutableFile(tmpDir.testDirectory.file("binaries", "output_dir", "exe_name"))
+    }
+
+    def "test shared library"() {
+        def binary = initBinary(SharedLibraryBinarySpecInternal, NativeLibrarySpec)
+
+        when:
+        toolProvider.getSharedLibraryName("base_name") >> "shared_library_name"
+        toolProvider.getSharedLibraryLinkFileName("base_name") >> "shared_library_link_name"
+
+        and:
+        configAction.execute(binary)
+
+        then:
+        1 * binary.setSharedLibraryFile(tmpDir.testDirectory.file("binaries", "output_dir", "shared_library_name"))
+        1 * binary.setSharedLibraryLinkFile(tmpDir.testDirectory.file("binaries", "output_dir", "shared_library_link_name"))
+    }
+
+    def "test static library"() {
+        def binary = initBinary(StaticLibraryBinarySpecInternal, NativeLibrarySpec)
+
+        when:
+        toolProvider.getStaticLibraryName("base_name") >> "static_library_name"
+
+        and:
+        configAction.execute(binary)
+
+        then:
+        1 * binary.setStaticLibraryFile(tmpDir.testDirectory.file("binaries", "output_dir", "static_library_name"))
+    }
+
+    private <T extends NativeBinarySpecInternal> T initBinary(Class<T> type, Class<? extends NativeComponentSpec> componentType) {
+        def binary = Mock(type)
+        def component = Stub(componentType)
+        binary.component >> component
+        binary.platformToolProvider >> toolProvider
+        binary.namingScheme >> namingScheme
+
+        namingScheme.outputDirectoryBase >> "output_dir"
+        component.baseName >> "base_name"
+        return binary
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializerTest.groovy
new file mode 100644
index 0000000..61939a9
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/NativeComponentSpecInitializerTest.groovy
@@ -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.nativeplatform.internal.configure
+import org.gradle.api.Named
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.ProjectSourceSet
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.Flavor
+import org.gradle.nativeplatform.internal.DefaultNativeExecutableSpec
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.component.BaseComponentSpec
+import org.gradle.platform.base.internal.BinaryNamingSchemeBuilder
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier
+import org.gradle.platform.base.internal.DefaultPlatformRequirement
+import org.gradle.platform.base.internal.PlatformResolvers
+import spock.lang.Specification
+
+class NativeComponentSpecInitializerTest extends Specification {
+    def instantiator = DirectInstantiator.INSTANCE
+    def toolChains = Mock(NativeToolChainRegistryInternal)
+    def toolChain = Mock(NativeToolChainInternal)
+    def toolProvider = Mock(PlatformToolProvider)
+    def nativeBinariesFactory = Mock(NativeBinariesFactory)
+    def namingSchemeBuilder = Mock(BinaryNamingSchemeBuilder)
+    def platforms = Mock(PlatformResolvers)
+    def platform = createStub(NativePlatformInternal, "platform1")
+
+    def buildType = createStub(BuildType, "buildType1")
+    def flavor = createStub(Flavor, "flavor1")
+
+    def id = new DefaultComponentSpecIdentifier("project", "name")
+    def mainSourceSet = new DefaultFunctionalSourceSet("testFSS", DirectInstantiator.INSTANCE, Stub(ProjectSourceSet));
+    def component = BaseComponentSpec.create(DefaultNativeExecutableSpec, id, mainSourceSet, instantiator)
+
+    def "does not use variant dimension names for single valued dimensions"() {
+        component.targetPlatform("platform1")
+
+        when:
+        def factory = new NativeComponentSpecInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains, platforms, [buildType], [flavor])
+        factory.execute(component)
+
+        then:
+        1 * platforms.resolve(NativePlatform, requirement("platform1")) >> platform
+        1 * toolChains.getForPlatform(platform) >> toolChain
+        1 * toolChain.select(platform) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+        0 * namingSchemeBuilder._
+    }
+
+    def "does not use variant dimension names when component targets a single point on dimension"() {
+        when:
+        def factory = new NativeComponentSpecInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
+                platforms, [buildType, Mock(BuildType)], [flavor, Mock(Flavor)])
+        component.targetPlatform("platform1")
+        component.targetBuildTypes("buildType1")
+        component.targetFlavors("flavor1")
+        factory.execute(component)
+
+        then:
+        1 * platforms.resolve(NativePlatform, requirement("platform1")) >> platform
+        1 * toolChains.getForPlatform(platform) >> toolChain
+        1 * toolChain.select(platform) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+        0 * namingSchemeBuilder._
+    }
+
+    def "includes platform in name for when multiple platforms"() {
+        def platform2 = createStub(NativePlatformInternal, "platform2")
+        component.targetPlatform("platform1")
+        component.targetPlatform("platform2")
+
+        when:
+        def factory = new NativeComponentSpecInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
+                platforms, [buildType], [flavor])
+        factory.execute(component)
+
+
+        then:
+        1 * platforms.resolve(NativePlatform, requirement("platform1")) >> platform
+        1 * platforms.resolve(NativePlatform, requirement("platform2")) >> platform2
+
+        1 * toolChains.getForPlatform(platform) >> toolChain
+        1 * toolChain.select(platform) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+        1 * namingSchemeBuilder.withVariantDimension("platform1") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+        0 * _
+
+        then:
+        1 * toolChains.getForPlatform(platform2) >> toolChain
+        1 * toolChain.select(platform2) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+        1 * namingSchemeBuilder.withVariantDimension("platform2") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform2, buildType, flavor)
+        0 * _
+    }
+
+    def "includes buildType in name for when multiple buildTypes"() {
+        final BuildType buildType2 = createStub(BuildType, "buildType2")
+        component.targetPlatform("platform1")
+
+        when:
+        def factory = new NativeComponentSpecInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
+                platforms, [buildType, buildType2], [flavor])
+        factory.execute(component)
+
+        then:
+        1 * platforms.resolve(NativePlatform, requirement("platform1")) >> platform
+        1 * toolChains.getForPlatform(platform) >> toolChain
+        1 * toolChain.select(platform) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+
+        then:
+        1 * namingSchemeBuilder.withVariantDimension("buildType1") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+        0 * _
+
+        then:
+        1 * namingSchemeBuilder.withVariantDimension("buildType2") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType2, flavor)
+        0 * _
+    }
+
+    def "includes flavor in name for when multiple flavors"() {
+        component.targetPlatform("platform1")
+        final Flavor flavor2 = createStub(Flavor, "flavor2")
+        when:
+        def factory = new NativeComponentSpecInitializer(nativeBinariesFactory, namingSchemeBuilder, toolChains,
+                platforms, [buildType], [flavor, flavor2])
+        factory.execute(component)
+
+        then:
+        1 * platforms.resolve(NativePlatform, requirement("platform1")) >> platform
+        1 * toolChains.getForPlatform(platform) >> toolChain
+        1 * toolChain.select(platform) >> toolProvider
+        1 * namingSchemeBuilder.withComponentName("name") >> namingSchemeBuilder
+
+        then:
+        1 * namingSchemeBuilder.withVariantDimension("flavor1") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor)
+        0 * _
+
+        then:
+        1 * namingSchemeBuilder.withVariantDimension("flavor2") >> namingSchemeBuilder
+        1 * nativeBinariesFactory.createNativeBinaries(component, namingSchemeBuilder, toolChain, toolProvider, platform, buildType, flavor2)
+        0 * _
+    }
+
+    def requirement(String name) {
+        DefaultPlatformRequirement.create(name)
+    }
+
+    private <T extends Named> T createStub(Class<T> type, def name) {
+        def stub = Stub(type) {
+            getName() >> name
+        }
+        return stub
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializerTest.groovy
new file mode 100644
index 0000000..14bb723
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/configure/ToolSettingNativeBinaryInitializerTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.configure
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.plugins.ExtensionContainer
+import org.gradle.language.base.internal.registry.DefaultLanguageTransformContainer
+import org.gradle.language.base.internal.registry.LanguageTransform
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.internal.DefaultTool
+import spock.lang.Specification
+
+class ToolSettingNativeBinaryInitializerTest extends Specification {
+    def binary = Mock(ExtensionAwareNativeBinary)
+    def languageTransforms = new DefaultLanguageTransformContainer()
+    def language = Mock(LanguageTransform)
+    def initializer = new ToolSettingNativeBinaryInitializer(languageTransforms)
+
+    def "does nothing with no languages"() {
+
+        when:
+        initializer.execute(binary)
+
+        then:
+        0 * _
+    }
+
+    def "does nothing when language has not tools registered"() {
+        when:
+        languageTransforms.add(language)
+
+        language.binaryTools >> [:]
+
+        and:
+        initializer.execute(binary)
+
+        then:
+        0 * _
+    }
+
+    def "adds extension for each tool"() {
+        def extensions = Mock(ExtensionContainer)
+        when:
+        languageTransforms.add(language)
+        language.binaryTools >> [tool: DefaultTool, other: String]
+
+        and:
+        initializer.execute(binary)
+
+        then:
+        _ * binary.extensions >> extensions
+        1 * extensions.create("tool", DefaultTool)
+        1 * extensions.create("other", String)
+        0 * _
+    }
+
+    private interface ExtensionAwareNativeBinary extends NativeBinarySpec, ExtensionAware {}
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy
new file mode 100644
index 0000000..55f4b06
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltSharedLibraryBinaryTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.internal.prebuilt
+
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.Flavor
+import org.gradle.nativeplatform.PrebuiltLibrary
+import org.gradle.nativeplatform.platform.NativePlatform
+import spock.lang.Specification
+
+class DefaultPrebuiltSharedLibraryBinaryTest extends Specification {
+    def binary = new DefaultPrebuiltSharedLibraryBinary("name", Stub(PrebuiltLibrary), Stub(BuildType), Stub(NativePlatform), Stub(Flavor))
+
+    def "has useful string representation"() {
+        expect:
+        binary.toString() == "shared library 'name'"
+        binary.displayName == "shared library 'name'"
+    }
+
+    def "uses library file when link file not set"() {
+        given:
+        def sharedLibraryFile = Mock(File)
+        def sharedLibraryLinkFile = Mock(File)
+
+        when:
+        binary.sharedLibraryFile = sharedLibraryFile
+
+        then:
+        binary.sharedLibraryFile == sharedLibraryFile
+        binary.sharedLibraryLinkFile == sharedLibraryFile
+
+        when:
+        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
+
+        then:
+        binary.sharedLibraryFile == sharedLibraryFile
+        binary.sharedLibraryLinkFile == sharedLibraryLinkFile
+    }
+
+    def "uses specified linke file and library file"() {
+        given:
+        def sharedLibraryFile = createFile()
+        def sharedLibraryLinkFile = createFile()
+
+        when:
+        binary.sharedLibraryFile = sharedLibraryFile
+        binary.sharedLibraryLinkFile = sharedLibraryLinkFile
+
+        then:
+        binary.linkFiles.files == [sharedLibraryLinkFile] as Set
+        binary.runtimeFiles.files == [sharedLibraryFile] as Set
+    }
+
+    def createFile() {
+        def file = Stub(File) {
+            exists() >> true
+            isFile() >> true
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.groovy
new file mode 100644
index 0000000..395b1ee
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/prebuilt/DefaultPrebuiltStaticLibraryBinaryTest.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.nativeplatform.internal.prebuilt
+
+import org.gradle.nativeplatform.BuildType
+import org.gradle.nativeplatform.Flavor
+import org.gradle.nativeplatform.PrebuiltLibrary
+import org.gradle.nativeplatform.platform.NativePlatform
+import spock.lang.Specification
+
+class DefaultPrebuiltStaticLibraryBinaryTest extends Specification {
+    def binary = new DefaultPrebuiltStaticLibraryBinary("name", Stub(PrebuiltLibrary), Stub(BuildType), Stub(NativePlatform), Stub(Flavor))
+
+    def "has useful string representation"() {
+        expect:
+        binary.toString() == "static library 'name'"
+        binary.displayName == "static library 'name'"
+    }
+
+    def "can set static library file"() {
+        given:
+        def file = createFile()
+
+        when:
+        binary.staticLibraryFile = file
+
+        then:
+        binary.staticLibraryFile == file
+        binary.linkFiles.files == [file] as Set
+
+        and:
+        binary.runtimeFiles.empty
+    }
+
+    def createFile() {
+        def file = Stub(File) {
+            exists() >> true
+            isFile() >> true
+        }
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParserTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParserTest.groovy
new file mode 100644
index 0000000..99dd0da
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/NativeDependencyNotationParserTest.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.nativeplatform.internal.resolve
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.NativeLibraryRequirement
+import spock.lang.Specification
+
+class NativeDependencyNotationParserTest extends Specification {
+    def parser = NativeDependencyNotationParser.parser()
+    def requirement = Mock(NativeLibraryRequirement)
+    def library = Mock(NativeLibrarySpec)
+    def project = Mock(ProjectInternal)
+
+    def "uses shared variant of library"() {
+        when:
+        def input = library
+
+        and:
+        library.shared >> requirement
+
+        then:
+        parser.parseNotation(input) == requirement
+    }
+
+    def "parses map notation for library in same project"() {
+        when:
+        def input = [library: 'libName']
+        def dependency = parser.parseNotation(input)
+
+        then:
+        dependency.projectPath == null
+        dependency.libraryName == "libName"
+        dependency.linkage == null
+    }
+
+    def "parses map notation for library in other project"() {
+        when:
+        def input = [project: 'other', library: 'libName']
+        def dependency = parser.parseNotation(input)
+
+
+        then:
+        dependency.projectPath == "other"
+        dependency.libraryName == "libName"
+        dependency.linkage == null
+    }
+
+    def "parses map notation for library with defined linkage"() {
+        when:
+        def input = [project: 'other', library: 'libName', linkage: 'static']
+        def dependency = parser.parseNotation(input)
+
+        then:
+        dependency.projectPath == "other"
+        dependency.libraryName == "libName"
+        dependency.linkage == "static"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy
new file mode 100644
index 0000000..a41024e
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/internal/resolve/ProjectLibraryBinaryLocatorTest.groovy
@@ -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.nativeplatform.internal.resolve
+
+import org.gradle.api.NamedDomainObjectSet
+import org.gradle.api.UnknownDomainObjectException
+import org.gradle.api.UnknownProjectException
+import org.gradle.api.internal.DefaultDomainObjectSet
+import org.gradle.api.internal.plugins.ExtensionContainerInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.nativeplatform.NativeBinarySpec
+import org.gradle.nativeplatform.NativeLibraryBinary
+import org.gradle.nativeplatform.NativeLibraryRequirement
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.internal.ProjectNativeLibraryRequirement
+import org.gradle.platform.base.ComponentSpecContainer
+import spock.lang.Specification
+
+class ProjectLibraryBinaryLocatorTest extends Specification {
+    def project = Mock(ProjectInternal)
+    def projectLocator = Mock(ProjectLocator)
+    def requirement = Mock(NativeLibraryRequirement)
+    def library = Mock(NativeLibrarySpec)
+    def binary = Mock(MockNativeLibraryBinary)
+    def binaries = new DefaultDomainObjectSet(NativeBinarySpec, [binary])
+    def convertedBinaries = new DefaultDomainObjectSet(NativeLibraryBinary, [binary])
+    def locator = new ProjectLibraryBinaryLocator(projectLocator)
+
+    def setup() {
+        library.binaries >> binaries
+    }
+
+    def "locates binaries for library in same project"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("libName", null)
+
+        and:
+        projectLocator.locateProject(null) >> project
+        findLibraryInProject()
+
+        then:
+        locator.getBinaries(requirement) == convertedBinaries
+    }
+
+    def "locates binaries for library in other project"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("other", "libName", null)
+
+        and:
+        projectLocator.locateProject("other") >> project
+        findLibraryInProject()
+
+        then:
+        locator.getBinaries(requirement) == convertedBinaries
+    }
+
+    def "parses map notation for library with static linkage"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("other", "libName", "static")
+
+        and:
+        projectLocator.locateProject("other") >> project
+        findLibraryInProject()
+
+        then:
+        locator.getBinaries(requirement) == convertedBinaries
+    }
+
+    def "fails for unknown project"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("unknown", "libName", "static")
+
+        and:
+        projectLocator.locateProject("unknown") >> { throw new UnknownProjectException("unknown")}
+
+        and:
+        locator.getBinaries(requirement)
+
+        then:
+        thrown(UnknownProjectException)
+    }
+
+    def "fails for unknown library"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("other", "unknown", "static")
+
+        and:
+        projectLocator.locateProject("other") >> project
+        def libraries = findLibraryContainer(project)
+        libraries.getByName("unknown") >> { throw new UnknownDomainObjectException("unknown") }
+
+        and:
+        locator.getBinaries(requirement)
+
+        then:
+        thrown(UnknownDomainObjectException)
+    }
+
+    def "fails when project does not have libraries"() {
+        when:
+        requirement = new ProjectNativeLibraryRequirement("other", "libName", "static")
+
+        and:
+        projectLocator.locateProject("other") >> project
+        def extensions = Mock(ExtensionContainerInternal)
+        project.getExtensions() >> extensions
+        extensions.findByName("libraries") >> null
+        project.path >> "project-path"
+
+        and:
+        locator.getBinaries(requirement)
+
+        then:
+        def e = thrown(LibraryResolveException)
+        e.message == "Project does not have a libraries container: 'project-path'"
+    }
+
+    private void findLibraryInProject() {
+        def libraries = findLibraryContainer(project)
+        libraries.getByName("libName") >> library
+    }
+
+    private findLibraryContainer(ProjectInternal project) {
+        def extensions = Mock(ExtensionContainerInternal)
+        def components = Mock(ComponentSpecContainer)
+        def libraryContainer = Mock(NamedDomainObjectSet)
+        project.getExtensions() >> extensions
+        extensions.findByType(ComponentSpecContainer) >> components
+        components.withType(NativeLibrarySpec) >> libraryContainer
+        return libraryContainer
+    }
+
+    interface MockNativeLibraryBinary extends NativeBinarySpec, NativeLibraryBinary {}
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ArchitecturesTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ArchitecturesTest.groovy
new file mode 100644
index 0000000..da09033
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ArchitecturesTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform.internal
+
+import spock.lang.Specification
+
+
+class ArchitecturesTest extends Specification {
+    def "test 32-bit aliases"() {
+        expect:
+        Architectures.forInput(architecture).isI386()
+        where:
+        architecture << [ "x86", "i386", "ia-32", "i686" ]
+    }
+
+    def "test 64-bit aliases"() {
+        expect:
+        Architectures.forInput(architecture).isAmd64()
+        where:
+        architecture << [ "x86-64", "x86_64", "amd64", "x64" ]
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitectureTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitectureTest.groovy
new file mode 100644
index 0000000..af7882b
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultArchitectureTest.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.nativeplatform.platform.internal
+
+import spock.lang.Specification
+
+class DefaultArchitectureTest extends Specification {
+    def "has useful string representation"() {
+        def architecture = new DefaultArchitecture("arch")
+
+        expect:
+        architecture.toString() == "architecture 'arch'"
+        architecture.displayName == "architecture 'arch'"
+    }
+
+    def "recognises key architectures"() {
+        def arch = new DefaultArchitecture(name)
+
+        expect:
+        arch.name == name
+        arch.i386 == i386
+        arch.amd64 == amd64
+        arch.ia64 == ia64
+        arch.arm == arm
+
+        where:
+        name        | i386  | amd64 | ia64  | arm
+        "x86"       | true  | false | false | false
+        "x86-64"    | false | true  | false | false
+        "ia-64"     | false | false | true  | false
+        "arm-v7"    | false | false | false | true
+        "arbitrary" | false | false | false | false
+    }
+
+    def "can create arbitrary operating system"() {
+        def arch = new DefaultArchitecture("arbitrary")
+
+        expect:
+        arch.name == "arbitrary"
+        arch.toString() == "architecture 'arbitrary'"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatformTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatformTest.groovy
new file mode 100644
index 0000000..a180b1f
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultNativePlatformTest.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.nativeplatform.platform.internal
+
+import spock.lang.Specification
+
+class DefaultNativePlatformTest extends Specification {
+    def os = Mock(OperatingSystemInternal)
+    def arch = Mock(ArchitectureInternal)
+    def platform = new DefaultNativePlatform("platform", os, arch)
+
+    def "has useful string representation"() {
+        expect:
+        platform.displayName == "platform 'platform'"
+        platform.toString() == "platform 'platform'"
+    }
+
+    def "can configure architecture"() {
+        when:
+        platform.architecture "x86"
+
+        then:
+        platform.architecture.name == "x86"
+        platform.architecture.i386
+
+        when:
+        platform.architecture "i386"
+
+        then:
+        platform.architecture.name == "x86"
+        platform.architecture.i386
+    }
+
+    def "can configure operating system"() {
+        when:
+        platform.operatingSystem "the-os"
+
+        then:
+        platform.operatingSystem.name == "the-os"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystemTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystemTest.groovy
new file mode 100644
index 0000000..733ddfe
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/DefaultOperatingSystemTest.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.nativeplatform.platform.internal
+
+import org.gradle.internal.os.OperatingSystem
+import spock.lang.Specification
+
+class DefaultOperatingSystemTest extends Specification {
+    def "has useful string representation"() {
+        def os = new DefaultOperatingSystem("sunos")
+
+        expect:
+        os.toString() == "operating system 'sunos'"
+        os.displayName == "operating system 'sunos'"
+        os.internalOs == OperatingSystem.SOLARIS
+    }
+
+    def "recognises key operating systems"() {
+        def os = new DefaultOperatingSystem(name)
+
+        expect:
+        os.name == name
+        os.internalOs == internalOs
+
+        where:
+        name      | internalOs
+        "windows" | OperatingSystem.WINDOWS
+        "osx"     | OperatingSystem.MAC_OS
+        "linux"   | OperatingSystem.LINUX
+        "sunos"   | OperatingSystem.SOLARIS
+        "solaris" | OperatingSystem.SOLARIS
+        "freebsd" | OperatingSystem.FREE_BSD
+    }
+
+    def "can create arbitrary operating system"() {
+        def os = new DefaultOperatingSystem("arbitrary")
+
+        expect:
+        os.name == "arbitrary"
+        os.toString() == "operating system 'arbitrary'"
+
+        os.internalOs == OperatingSystem.UNIX
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ReadelfBinaryInfoTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ReadelfBinaryInfoTest.groovy
new file mode 100644
index 0000000..c3b5f3e
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/platform/internal/ReadelfBinaryInfoTest.groovy
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.platform.internal
+
+import org.gradle.nativeplatform.fixtures.binaryinfo.ReadelfBinaryInfo
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ReadelfBinaryInfoTest extends Specification {
+    @Unroll("reads soname value with #language readelf output")
+    def "reads soname value"() {
+        when:
+        def inputLines = input.readLines()
+
+        then:
+        ReadelfBinaryInfo.readSoName(inputLines) == 'heythere'
+
+        where:
+        [language, input] << [
+            ["English", """
+Dynamic section at offset 0xdf8 contains 24 entries:
+  Tag        Type                         Name/Value
+ 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
+ 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
+ 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
+ 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
+ 0x000000000000000e (SONAME)             Library soname: [heythere]
+ 0x000000000000000c (INIT)               0x668
+"""],
+            ["German", """
+Dynamische Sektion an Offset 0x1a6da8 enthält 26 Einträge:
+  Tag       Typ                          Name/Wert
+ 0x00000001 (NEEDED)                     Gemeinsame Bibliothek [ld-linux.so.2]
+ 0x0000000e (SONAME)                     soname der Bibliothek: [heythere]
+ 0x0000000c (INIT)                       0x198c0
+ 0x00000019 (INIT_ARRAY)                 0x1a61e8
+ 0x0000001b (INIT_ARRAYSZ)               12 (Bytes)
+ 0x00000004 (HASH)                       0x1a1b34
+ 0x6ffffef5 (GNU_HASH)                   0x1b8
+ 0x00000005 (STRTAB)                     0xd438
+ 0x00000006 (SYMTAB)                     0x3ec8
+ 0x0000000a (STRSZ)                      23846 (Bytes)
+ 0x0000000b (SYMENT)                     16 (Bytes)
+ 0x00000003 (PLTGOT)                     0x1a8000
+ 0x00000002 (PLTRELSZ)                   96 (Bytes)
+ 0x00000014 (PLTREL)                     REL
+ 0x00000017 (JMPREL)                     0x172e8
+ 0x00000011 (REL)                        0x148d8
+ 0x00000012 (RELSZ)                      10768 (Bytes)
+ 0x00000013 (RELENT)                     8 (Bytes)
+ 0x6ffffffc (VERDEF)                     0x1440c
+ 0x6ffffffd (VERDEFNUM)                  33
+ 0x0000001e (FLAGS)                      STATIC_TLS
+ 0x6ffffffe (VERNEED)                    0x14898
+ 0x6fffffff (VERNEEDNUM)                 1
+ 0x6ffffff0 (VERSYM)                     0x1315e
+ 0x6ffffffa (RELCOUNT)                   1253
+ 0x00000000 (NULL)                       0x0
+"""
+            ]]
+    }
+
+    @Unroll("returns null for no soname value with #language readelf output")
+    def "returns null for no soname value"() {
+        when:
+        def inputLines = input.readLines()
+
+        then:
+        ReadelfBinaryInfo.readSoName(inputLines) == null
+
+        where:
+        [language, input] << [
+            ["English", """
+Dynamic section at offset 0xdf8 contains 24 entries:
+  Tag        Type                         Name/Value
+ 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
+ 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
+ 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
+ 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
+ 0x000000000000000c (INIT)               0x668
+"""],
+            ["German", """
+Dynamische Sektion an Offset 0x1a6da8 enthält 26 Einträge:
+  Tag       Typ                          Name/Wert
+ 0x00000001 (NEEDED)                     Gemeinsame Bibliothek [ld-linux.so.2]
+ 0x0000000c (INIT)                       0x198c0
+ 0x00000019 (INIT_ARRAY)                 0x1a61e8
+ 0x0000001b (INIT_ARRAYSZ)               12 (Bytes)
+ 0x00000004 (HASH)                       0x1a1b34
+ 0x6ffffef5 (GNU_HASH)                   0x1b8
+ 0x00000005 (STRTAB)                     0xd438
+ 0x00000006 (SYMTAB)                     0x3ec8
+ 0x0000000a (STRSZ)                      23846 (Bytes)
+ 0x0000000b (SYMENT)                     16 (Bytes)
+ 0x00000003 (PLTGOT)                     0x1a8000
+ 0x00000002 (PLTRELSZ)                   96 (Bytes)
+ 0x00000014 (PLTREL)                     REL
+ 0x00000017 (JMPREL)                     0x172e8
+ 0x00000011 (REL)                        0x148d8
+ 0x00000012 (RELSZ)                      10768 (Bytes)
+ 0x00000013 (RELENT)                     8 (Bytes)
+ 0x6ffffffc (VERDEF)                     0x1440c
+ 0x6ffffffd (VERDEFNUM)                  33
+ 0x0000001e (FLAGS)                      STATIC_TLS
+ 0x6ffffffe (VERNEED)                    0x14898
+ 0x6fffffff (VERNEEDNUM)                 1
+ 0x6ffffff0 (VERSYM)                     0x1315e
+ 0x6ffffffa (RELCOUNT)                   1253
+ 0x00000000 (NULL)                       0x0
+"""
+            ]]
+    }
+
+    @Unroll("reads architecture value with #language readelf output")
+    def "reads architecture value"() {
+        when:
+        def inputLines = input.readLines()
+
+        then:
+        ReadelfBinaryInfo.readArch(inputLines).isI386() == true
+
+        where:
+        [language, input] << [
+            ["English", """
+ELF Header:
+ Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
+ Class:                             ELF32
+ Data:                              2's complement, little endian
+ Version:                           1 (current)
+ OS/ABI:                            UNIX - System V
+ ABI Version:                       0
+ Type:                              EXEC (Executable file)
+ Machine:                           Intel 80386
+ Version:                           0x1
+ Entry point address:               0x8048310
+ Start of program headers:          52 (bytes into file)
+ Start of section headers:          4400 (bytes into file)
+ Flags:                             0x0
+ Size of this header:               52 (bytes)
+ Size of program headers:           32 (bytes)
+ Number of program headers:         8
+ Size of section headers:           40 (bytes)
+ Number of section headers:         29
+ Section header string table index: 26
+"""],
+            ["German", """
+ELF-Header:
+  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
+  Klasse:                            ELF32
+  Daten:                             2er-Komplement, Little-Endian
+  Version:                           1 (current)
+  OS/ABI:                            UNIX - System V
+  ABI-Version:                       0
+  Typ:                               DYN (geteilte Objektadatei)
+  Maschine:                          Intel 80386
+  Version:                           0x1
+  Einstiegspunktadresse:               0x19bc0
+  Beginn der Programm-Header:          52 (Bytes in Datei)
+  Beginn der Sektions-header:          1739908 (Bytes in Datei)
+  Flags:                             0x0
+  Größe dieses Headers:              52 (Byte)
+  Größe der Programm-Header:         32 (Byte)
+  Number of program headers:         10
+  Größe der Sektions-Header:         40 (bytes)
+  Anzahl der Sektions-Header:        67
+  Sektions-Header Stringtabellen-Index: 66
+"""
+            ]]
+    }
+
+    @Unroll("reads architecture value and throws RuntimeException with #language readelf output")
+    def "reads architecture value and throws RuntimeException"() {
+        when:
+        def inputLines = input.readLines()
+        ReadelfBinaryInfo.readArch(inputLines)
+
+        then:
+        thrown(RuntimeException)
+
+        where:
+        [language, input] << [
+            ["English", """
+ELF Header:
+ Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
+ Class:                             ELF32
+ Data:                              2's complement, little endian
+ Version:                           1 (current)
+ OS/ABI:                            UNIX - System V
+ ABI Version:                       0
+ Type:                              EXEC (Executable file)
+ Version:                           0x1
+ Entry point address:               0x8048310
+ Start of program headers:          52 (bytes into file)
+ Start of section headers:          4400 (bytes into file)
+ Flags:                             0x0
+ Size of this header:               52 (bytes)
+ Size of program headers:           32 (bytes)
+ Number of program headers:         8
+ Size of section headers:           40 (bytes)
+ Number of section headers:         29
+ Section header string table index: 26
+"""],
+            ["German", """
+ELF-Header:
+  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
+  Klasse:                            ELF32
+  Daten:                             2er-Komplement, Little-Endian
+  Version:                           1 (current)
+  OS/ABI:                            UNIX - System V
+  ABI-Version:                       0
+  Typ:                               DYN (geteilte Objektadatei)
+  Version:                           0x1
+  Einstiegspunktadresse:               0x19bc0
+  Beginn der Programm-Header:          52 (Bytes in Datei)
+  Beginn der Sektions-header:          1739908 (Bytes in Datei)
+  Flags:                             0x0
+  Größe dieses Headers:              52 (Byte)
+  Größe der Programm-Header:         32 (Byte)
+  Number of program headers:         10
+  Größe der Sektions-Header:         40 (bytes)
+  Anzahl der Sektions-Header:        67
+  Sektions-Header Stringtabellen-Index: 66
+"""
+            ]]
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPluginTest.groovy
new file mode 100644
index 0000000..f29d8ba
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentModelPluginTest.groovy
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.plugins
+
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.model.internal.type.ModelType
+import org.gradle.nativeplatform.*
+import org.gradle.nativeplatform.internal.DefaultFlavor
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.platform.base.PlatformContainer
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class NativeComponentModelPluginTest extends Specification {
+    final def project = TestUtil.createRootProject()
+    def modelRegistryHelper = new ModelRegistryHelper(project)
+
+    def setup() {
+        project.pluginManager.apply(NativeComponentModelPlugin)
+    }
+
+    def "adds model extensions"() {
+        expect:
+        project.modelRegistry.realize(ModelPath.path("toolChains"), ModelType.of(NativeToolChainRegistry)) != null
+        project.modelRegistry.realize(ModelPath.path("platforms"), ModelType.of(PlatformContainer)) != null
+        project.modelRegistry.realize(ModelPath.path("buildTypes"), ModelType.of(BuildTypeContainer)) != null
+        project.modelRegistry.realize(ModelPath.path("flavors"), ModelType.of(FlavorContainer)) != null
+    }
+
+    def "does not provide a default tool chain"() {
+        expect:
+        project.modelRegistry.realize(ModelPath.path("toolChains"), ModelType.of(NativeToolChainRegistry)).isEmpty()
+    }
+
+    def "adds default flavor to every binary"() {
+        when:
+        project.model {
+            components {
+                exe(NativeExecutableSpec)
+                lib(NativeLibrarySpec)
+            }
+        }
+        realize()
+
+        then:
+        one(project.binaries.withType(NativeExecutableBinarySpec)).flavor.name == DefaultFlavor.DEFAULT
+        one(project.binaries.withType(SharedLibraryBinarySpec)).flavor.name == DefaultFlavor.DEFAULT
+    }
+
+    def "behaves correctly for defaults when domain is explicitly configured"() {
+        when:
+        modelRegistryHelper
+                .mutate(NativeToolChainRegistry) { it.add toolChain("tc") }
+                .mutate(PlatformContainer) { it.add named(NativePlatformInternal, "platform") }
+                .mutate(BuildTypeContainer) { it.add named(BuildType, "bt") }
+                .mutate(FlavorContainer) { it.add named(Flavor, "flavor1") }
+
+        and:
+        realize()
+
+        then:
+        one(project.modelRegistry.realize(ModelPath.path("toolChains"), ModelType.of(NativeToolChainRegistry))).name == 'tc'
+        project.modelRegistry.realize(ModelPath.path("platforms"), ModelType.of(PlatformContainer)).size() == 1
+        one(project.modelRegistry.realize(ModelPath.path("buildTypes"), ModelType.of(BuildTypeContainer))).name == 'bt'
+        one(project.modelRegistry.realize(ModelPath.path("flavors"), ModelType.of(FlavorContainer))).name == 'flavor1'
+    }
+
+    def "creates binaries for executable"() {
+        when:
+        project.pluginManager.apply(NativeComponentModelPlugin)
+        modelRegistryHelper
+                .mutate(NativeToolChainRegistry) { it.add toolChain("tc") }
+                .mutate(PlatformContainer) { it.add named(NativePlatformInternal, "platform") }
+                .mutate(BuildTypeContainer) { it.add named(BuildType, "bt") }
+                .mutate(FlavorContainer) { it.add named(Flavor, "flavor1") }
+
+        project.model {
+            components {
+                test(NativeExecutableSpec) {
+                    targetPlatform "platform"
+                }
+            }
+        }
+        realize()
+
+        then:
+        NativeExecutableSpec executable = one(project.componentSpecs) as NativeExecutableSpec
+        NativeExecutableBinarySpec executableBinary = one(project.binaries) as NativeExecutableBinarySpec
+        with(executableBinary) {
+            name == 'testExecutable'
+            component == executable
+            toolChain.name == "tc"
+            targetPlatform.name == "platform"
+            buildType.name == "bt"
+            flavor.name == "flavor1"
+        }
+
+        and:
+        executable.binaries == [executableBinary] as Set
+    }
+
+    def "creates binaries for library"() {
+        when:
+        project.pluginManager.apply(NativeComponentModelPlugin)
+        modelRegistryHelper
+                .mutate(NativeToolChainRegistry) { it.add toolChain("tc") }
+                .mutate(PlatformContainer) { it.add named(NativePlatformInternal, "platform") }
+                .mutate(BuildTypeContainer) { it.add named(BuildType, "bt") }
+                .mutate(FlavorContainer) { it.add named(Flavor, "flavor1") }
+
+        project.model {
+            components {
+                test(NativeLibrarySpec) {
+                    targetPlatform "platform"
+                }
+            }
+        }
+        realize()
+
+        then:
+        NativeLibrarySpec library = one(project.componentSpecs) as NativeLibrarySpec
+        SharedLibraryBinarySpec sharedLibraryBinary = project.binaries.testSharedLibrary as SharedLibraryBinarySpec
+        with(sharedLibraryBinary) {
+            name == 'testSharedLibrary'
+            component == library
+
+            toolChain.name == "tc"
+            targetPlatform.name == "platform"
+            buildType.name == "bt"
+            flavor.name == "flavor1"
+        }
+
+        and:
+        StaticLibraryBinarySpec staticLibraryBinary = project.binaries.testStaticLibrary as StaticLibraryBinarySpec
+        with(staticLibraryBinary) {
+            name == 'testStaticLibrary'
+            component == library
+
+            toolChain.name == "tc"
+            targetPlatform.name == "platform"
+            buildType.name == "bt"
+            flavor.name == "flavor1"
+        }
+
+        and:
+        library.binaries.contains(sharedLibraryBinary)
+        library.binaries.contains(staticLibraryBinary)
+    }
+
+    def "creates lifecycle task for each binary"() {
+        when:
+        project.pluginManager.apply(NativeComponentModelPlugin)
+        project.model {
+            components {
+                exe(NativeExecutableSpec)
+                lib(NativeLibrarySpec)
+            }
+        }
+        realize()
+
+        then:
+        NativeExecutableBinarySpec executableBinary = project.binaries.exeExecutable as NativeExecutableBinarySpec
+        with(oneTask(executableBinary.buildDependencies)) {
+            name == executableBinary.name
+            group == LifecycleBasePlugin.BUILD_GROUP
+        }
+        SharedLibraryBinarySpec sharedLibraryBinary = project.binaries.libSharedLibrary as SharedLibraryBinarySpec
+        with(oneTask(sharedLibraryBinary.buildDependencies)) {
+            name == sharedLibraryBinary.name
+            group == LifecycleBasePlugin.BUILD_GROUP
+        }
+        StaticLibraryBinarySpec staticLibraryBinary = project.binaries.libStaticLibrary as StaticLibraryBinarySpec
+        with(oneTask(staticLibraryBinary.buildDependencies)) {
+            name == staticLibraryBinary.name
+            group == LifecycleBasePlugin.BUILD_GROUP
+        }
+    }
+
+    private void realize() {
+        project.tasks.realize();
+        project.bindAllModelRules()
+    }
+
+    static <T> T one(Collection<T> collection) {
+        assert collection.size() == 1
+        return collection.iterator().next()
+    }
+
+    public <T> T named(Class<T> type, def name) {
+        Stub(type) {
+            getName() >> name
+        }
+    }
+
+    def toolChain(def name) {
+        Stub(NativeToolChainInternal) {
+            getName() >> name
+            select(_) >> Stub(PlatformToolProvider) {
+                isAvailable() >> true
+            }
+        }
+    }
+
+    Task oneTask(TaskDependency dependencies) {
+        def tasks = dependencies.getDependencies(Stub(Task))
+        assert tasks.size() == 1
+        return tasks.asList()[0]
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginTest.groovy
new file mode 100644
index 0000000..bae9269
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/plugins/NativeComponentPluginTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.plugins
+
+import org.gradle.api.tasks.TaskDependencyMatchers
+import org.gradle.nativeplatform.NativeExecutableSpec
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.tasks.CreateStaticLibrary
+import org.gradle.nativeplatform.tasks.InstallExecutable
+import org.gradle.nativeplatform.tasks.LinkExecutable
+import org.gradle.nativeplatform.tasks.LinkSharedLibrary
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class NativeComponentPluginTest extends Specification {
+    final def project = TestUtil.createRootProject()
+
+    def setup() {
+        project.pluginManager.apply(NativeComponentPlugin)
+    }
+
+    def "creates link and install task for executable"() {
+        when:
+        project.model {
+            components {
+                test(NativeExecutableSpec)
+            }
+        }
+        project.tasks.realize()
+        project.bindAllModelRules()
+
+        then:
+        def testExecutable = project.binaries.testExecutable
+        with(project.tasks.linkTestExecutable) {
+            it instanceof LinkExecutable
+            it == testExecutable.tasks.link
+            it.toolChain == testExecutable.toolChain
+            it.targetPlatform == testExecutable.targetPlatform
+            it.linkerArgs == testExecutable.linker.args
+        }
+
+        and:
+        def lifecycleTask = project.tasks.testExecutable
+        lifecycleTask TaskDependencyMatchers.dependsOn("linkTestExecutable")
+
+        and:
+        project.tasks.installTestExecutable instanceof InstallExecutable
+    }
+
+    def "creates link task and static archive task for library"() {
+        when:
+        project.model {
+            components {
+                test(NativeLibrarySpec)
+            }
+        }
+        project.tasks.realize()
+        project.bindAllModelRules()
+
+        then:
+        def sharedLibraryBinary = project.binaries.testSharedLibrary
+        with(project.tasks.linkTestSharedLibrary) {
+            it instanceof LinkSharedLibrary
+            it == sharedLibraryBinary.tasks.link
+            it.toolChain == sharedLibraryBinary.toolChain
+            it.targetPlatform == sharedLibraryBinary.targetPlatform
+            it.linkerArgs == sharedLibraryBinary.linker.args
+        }
+
+        and:
+        def sharedLibTask = project.tasks.testSharedLibrary
+        sharedLibTask TaskDependencyMatchers.dependsOn("linkTestSharedLibrary")
+
+        and:
+        def staticLibraryBinary = project.binaries.testStaticLibrary
+        with(project.tasks.createTestStaticLibrary) {
+            it instanceof CreateStaticLibrary
+            it == staticLibraryBinary.tasks.createStaticLib
+            it.toolChain == staticLibraryBinary.toolChain
+            it.targetPlatform == staticLibraryBinary.targetPlatform
+            it.staticLibArgs == staticLibraryBinary.staticLibArchiver.args
+        }
+
+        and:
+        def staticLibTask = project.tasks.testStaticLibrary
+        staticLibTask TaskDependencyMatchers.dependsOn("createTestStaticLibrary")
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpecTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpecTest.groovy
new file mode 100644
index 0000000..86b1417
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/internal/DefaultNativeTestSuiteBinarySpecTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.internal
+
+import org.gradle.nativeplatform.tasks.InstallExecutable
+import org.gradle.nativeplatform.tasks.LinkExecutable
+import org.gradle.nativeplatform.test.tasks.RunTestExecutable
+import org.gradle.platform.base.internal.DefaultBinaryTasksCollection
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class DefaultNativeTestSuiteBinarySpecTest extends Specification {
+    def tasks = new DefaultNativeTestSuiteBinarySpec.DefaultTasksCollection(new DefaultBinaryTasksCollection(null, null))
+
+    def "returns null for link, install and run when none defined"() {
+        expect:
+        tasks.link == null
+        tasks.install == null
+        tasks.run == null
+    }
+
+    def "returns link task when defined"() {
+        when:
+        final linkTask = TestUtil.createTask(LinkExecutable)
+        tasks.add(linkTask)
+
+        then:
+        tasks.link == linkTask
+        tasks.install == null
+        tasks.run == null
+    }
+
+    def "returns install task when defined"() {
+        when:
+        final installTask = TestUtil.createTask(InstallExecutable)
+        tasks.add(installTask)
+
+        then:
+        tasks.link == null
+        tasks.install == installTask
+        tasks.run == null
+    }
+
+    def "returns run task when defined"() {
+        when:
+        final runTask = TestUtil.createTask(RunTestExecutable)
+        tasks.add(runTask)
+
+        then:
+        tasks.link == null
+        tasks.install == null
+        tasks.run == runTask
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPluginTest.groovy
new file mode 100644
index 0000000..bbc6764
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/test/plugins/NativeBinariesTestPluginTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.plugins
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativeplatform.plugins.NativeComponentModelPlugin
+import org.gradle.nativeplatform.tasks.InstallExecutable
+import org.gradle.nativeplatform.test.NativeTestSuiteSpec
+import org.gradle.nativeplatform.test.internal.DefaultNativeTestSuiteBinarySpec
+import org.gradle.nativeplatform.test.tasks.RunTestExecutable
+import org.gradle.platform.base.binary.BaseBinarySpec
+import org.gradle.platform.base.internal.BinaryNamingScheme
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class NativeBinariesTestPluginTest extends Specification {
+    final def project = TestUtil.createRootProject()
+
+    def setup() {
+        project.pluginManager.apply(NativeComponentModelPlugin)
+    }
+
+    def "run tasks are added to the binary task list"() {
+        given:
+        def namingScheme = Mock(BinaryNamingScheme)
+        namingScheme.getTaskName("run") >> "runTestBinary"
+
+        def task = project.tasks.create("installTestBinary", InstallExecutable.class)
+        task.destinationDir = project.projectDir
+        task.executable = project.file("executable")
+
+        def binary = BaseBinarySpec.create(TestSpec, "testBinary", project.services.get(Instantiator), Mock(ITaskFactory))
+        binary.setNamingScheme(namingScheme)
+        binary.tasks.add(task)
+
+        project.binaries.add(binary)
+
+        when:
+        new NativeBinariesTestPlugin.Rules().createTestTasks(project.tasks, project.binaries)
+
+        then:
+        binary.tasks.withType(RunTestExecutable).size() == 1
+        binary.tasks.run != null
+
+        and:
+        project.tasks.getByName("check") dependsOn("runTestBinary")
+    }
+
+    static class TestSpec extends DefaultNativeTestSuiteBinarySpec {
+        @Override
+        NativeTestSuiteSpec getTestSuite() {
+            return null
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorkerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorkerTest.groovy
new file mode 100644
index 0000000..7179e9c
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultCommandLineToolInvocationWorkerTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+
+import org.gradle.internal.operations.BuildOperationFailure
+import org.gradle.internal.operations.logging.BuildOperationLogger
+import org.gradle.process.internal.ExecAction
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.process.internal.ExecException
+import spock.lang.Specification
+
+class DefaultCommandLineToolInvocationWorkerTest extends Specification {
+    def "throws exception when exec fails"() {
+        given:
+        def execAction = Mock(ExecAction)
+        def execActionFactory = Stub(ExecActionFactory) {
+            newExecAction() >> execAction
+        }
+
+        def context = new DefaultMutableCommandLineToolContext()
+        def executable = Mock(File)
+        def commandLineTool = new DefaultCommandLineToolInvocationWorker("Tool", executable, execActionFactory)
+        def invocation = new DefaultCommandLineToolInvocation("doing something", null, [], context, Mock(BuildOperationLogger))
+
+        when:
+        commandLineTool.execute(invocation)
+
+        then:
+        1 * execAction.executable(executable)
+        1 * execAction.execute() >> { throw new ExecException("fail") }
+        BuildOperationFailure e = thrown()
+        e.getMessage().contains('Tool failed while doing something')
+
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistryTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistryTest.groovy
new file mode 100644
index 0000000..7d2c3bf
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/DefaultNativeToolChainRegistryTest.groovy
@@ -0,0 +1,183 @@
+/*
+ * 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.nativeplatform.toolchain.internal
+import org.gradle.api.GradleException
+import org.gradle.api.NamedDomainObjectFactory
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+import static org.gradle.util.TextUtil.toPlatformLineSeparators
+
+class DefaultNativeToolChainRegistryTest extends Specification {
+    def project = TestUtil.createRootProject()
+    def instantiator = project.services.get(Instantiator)
+    def registry = instantiator.newInstance(DefaultNativeToolChainRegistry, instantiator)
+    def NamedDomainObjectFactory<TestNativeToolChain> factory = Mock(NamedDomainObjectFactory)
+    def platform = new DefaultNativePlatform("platform")
+
+    def "setup"() {
+        project.extensions.add("toolChains", registry)
+        registry.registerFactory(TestNativeToolChain, factory)
+    }
+
+    def "adds default tool chains"() {
+        def defaultToolChain1 = unavailableToolChain("test1")
+        def defaultToolChain2 = availableToolChain("test2")
+        def defaultToolChain3 = availableToolChain("test3")
+
+        when:
+        registry.registerDefaultToolChain("test1", TestNativeToolChain)
+        registry.registerDefaultToolChain("test2", TestNativeToolChain)
+        registry.registerDefaultToolChain("test3", TestNativeToolChain)
+        registry.addDefaultToolChains()
+
+        then:
+        registry.asList() == [defaultToolChain1, defaultToolChain2, defaultToolChain3]
+    }
+
+    def "can add default tool chain when some configured"() {
+        def defaultToolChain = availableToolChain("default")
+        def configuredToolChain = unavailableToolChain("configured")
+
+        when:
+        registry.registerDefaultToolChain("default", TestNativeToolChain)
+
+        and:
+        registry.create("configured", TestNativeToolChain)
+
+        and:
+        registry.addDefaultToolChains()
+
+        then:
+        registry.asList() == [configuredToolChain, defaultToolChain]
+    }
+
+    def "provides unavailable tool chain when no tool chain available for requested platform"() {
+        unavailableToolChain("test", "nope")
+        unavailableToolChain("test2", "not me")
+        unavailableToolChain("test3", "not me either")
+
+        given:
+        registry.registerDefaultToolChain("test", TestNativeToolChain)
+        registry.registerDefaultToolChain("test2", TestNativeToolChain)
+        registry.registerDefaultToolChain("test3", TestNativeToolChain)
+        registry.addDefaultToolChains()
+
+        and:
+        def tc = registry.getForPlatform(platform)
+        def result = tc.select(platform)
+
+        when:
+        result.newCompiler(CCompileSpec.class)
+
+        then:
+        GradleException e = thrown()
+        e.message == toPlatformLineSeparators("""No tool chain is available to build for platform 'platform':
+  - Tool chain 'test': nope
+  - Tool chain 'test2': not me
+  - Tool chain 'test3': not me either""")
+    }
+
+    def "can use DSL to configure toolchains"() {
+        def defaultToolChain = availableToolChain("test")
+        def anotherToolChain = unavailableToolChain("another")
+
+        when:
+        registry.registerDefaultToolChain("test", TestNativeToolChain)
+        registry.addDefaultToolChains()
+
+        and:
+        project.toolChains {
+            test {
+                baseDir = "foo"
+            }
+            another(TestNativeToolChain) {
+                baseDir = "bar"
+            }
+        }
+
+        then:
+        1 * defaultToolChain.setBaseDir("foo")
+        1 * anotherToolChain.setBaseDir("bar")
+
+        and:
+        registry.asList() == [anotherToolChain, defaultToolChain]
+    }
+
+    def "tool chains are returned in name order"() {
+        def test = unavailableToolChain("test")
+        def tc2 = availableToolChain("test2")
+        def tcFirst = availableToolChain("first")
+
+        when:
+        registry.create("test", TestNativeToolChain)
+        registry.create("test2", TestNativeToolChain)
+        registry.create("first", TestNativeToolChain)
+
+        then:
+        registry.toList() == [tcFirst, test, tc2]
+    }
+
+    def "uses first available tool chain that can target platform"() {
+        def defaultToolChain1 = unavailableToolChain("test1")
+        def defaultToolChain2 = availableToolChain("test2")
+        def defaultToolChain3 = availableToolChain("test3")
+
+        given:
+        registry.add(defaultToolChain1)
+        registry.add(defaultToolChain2)
+        registry.add(defaultToolChain3)
+
+        expect:
+        registry.getForPlatform(platform) == defaultToolChain2
+    }
+
+    def availableToolChain(String name) {
+        PlatformToolProvider platformToolChain = Stub(PlatformToolProvider) {
+            _ * isAvailable() >> true
+        }
+        TestNativeToolChain testToolChain = Mock(TestNativeToolChain) {
+            _ * getName() >> name
+            _ * select(platform) >> platformToolChain
+        }
+        factory.create(name) >> testToolChain
+        return testToolChain
+    }
+
+    def unavailableToolChain(String name, String message = "Not available") {
+        PlatformToolProvider platformToolChain = Stub(PlatformToolProvider) {
+            _ * isAvailable() >> false
+            _ * explain(_) >> { it[0].node(message) }
+        }
+        TestNativeToolChain testToolChain = Mock(TestNativeToolChain) {
+            _ * getName() >> name
+            _ * getDisplayName() >> "Tool chain '$name'"
+            _ * select(platform) >> platformToolChain
+        }
+        factory.create(name) >> testToolChain
+        return testToolChain
+    }
+
+    interface TestNativeToolChain extends NativeToolChainInternal
+    {
+        void setBaseDir(String value);
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompilerTest.groovy
new file mode 100644
index 0000000..d131a42
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/NativeCompilerTest.groovy
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+
+import org.gradle.api.Action
+import org.gradle.internal.concurrent.DefaultExecutorFactory
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.operations.DefaultBuildOperationProcessor
+import org.gradle.internal.operations.logging.BuildOperationLogger
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+public abstract class NativeCompilerTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    private static final String O_EXT = ".o"
+
+    protected abstract NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile)
+    protected NativeCompiler getCompiler() {
+        getCompiler(new DefaultMutableCommandLineToolContext(), O_EXT, false)
+    }
+
+    protected abstract Class<? extends NativeCompileSpec> getCompileSpecType()
+    protected abstract List<String> getCompilerSpecificArguments(File includeDir)
+
+    protected CommandLineToolInvocationWorker commandLineTool = Mock(CommandLineToolInvocationWorker)
+    protected BuildOperationProcessor buildOperationProcessor = new DefaultBuildOperationProcessor(new DefaultExecutorFactory(), 1)
+
+    def "arguments include source file"() {
+        given:
+        def compiler = getCompiler()
+        def testDir = tmpDirProvider.testDirectory
+        def sourceFile = testDir.file("source.ext")
+
+        when:
+        def args = compiler.getSourceArgs(sourceFile)
+
+        then:
+        args == [ sourceFile.absoluteFile.toString() ]
+    }
+
+    @Unroll
+    def "output file directory honors output extension '#extension' and directory"() {
+        given:
+        def compiler = getCompiler()
+        def testDir = tmpDirProvider.testDirectory
+        def sourceFile = testDir.file("source.ext")
+
+        when:
+        def outputFile = compiler.getOutputFileDir(sourceFile, testDir, extension)
+
+        then:
+        // Creates directory
+        outputFile.parentFile.exists()
+        // Rooted under test directory
+        outputFile.parentFile.parentFile == testDir
+        // TODO: Test for MD5 directory name?
+        outputFile.name == "source$extension"
+
+        where:
+        extension | _
+        ".o"      | _
+        ".obj"    | _
+    }
+
+    def "arguments contains parameters from spec"() {
+        given:
+        def compiler = getCompiler()
+        def testDir = tmpDirProvider.testDirectory
+        def includeDir = testDir.file("includes")
+        def expectedArgs = getCompilerSpecificArguments(includeDir)
+
+        when:
+        NativeCompileSpec compileSpec = Stub(getCompileSpecType()) {
+            getMacros() >> [foo: "bar", empty: null]
+            getAllArgs() >> ["-firstArg", "-secondArg"]
+            getIncludeRoots() >> [ includeDir ]
+            getOperationLogger() >> Mock(BuildOperationLogger)
+            getPrefixHeaderFile() >> null
+            getPreCompiledHeaderObjectFile() >> null
+        }
+
+        and:
+        def actualArgs = compiler.getArguments(compileSpec)
+
+        then:
+        actualArgs == expectedArgs
+    }
+
+    @Unroll("Compiles source files (options.txt=#withOptionsFile) with #description")
+    def "compiles all source files in separate executions"() {
+        given:
+        def invocationContext = new DefaultMutableCommandLineToolContext()
+        def compiler = getCompiler(invocationContext, O_EXT, withOptionsFile)
+        def testDir = tmpDirProvider.testDirectory
+        def objectFileDir = testDir.file("output/objects")
+        def sourceFiles = [ testDir.file("source1.ext"), testDir.file("source2.ext") ]
+
+        when:
+        def compileSpec = Stub(getCompileSpecType()) {
+            getTempDir() >> testDir
+            getObjectFileDir() >> objectFileDir
+            getSourceFiles() >> sourceFiles
+            getOperationLogger() >> Mock(BuildOperationLogger) {
+                getLogLocation() >> "<log location>"
+            }
+            getPrefixHeaderFile() >> null
+            getPreCompiledHeaderObjectFile() >> null
+        }
+
+        and:
+        compiler.execute(compileSpec)
+
+        then:
+
+        sourceFiles.each{ sourceFile ->
+            1 * commandLineTool.execute(_)
+        }
+        0 * _
+
+        where:
+        withOptionsFile | description
+        true            | "options written to options.txt"
+        false           | "options passed on the command line only"
+    }
+
+    def "user-supplied arg actions run once per execute"() {
+        given:
+        def invocationContext = new DefaultMutableCommandLineToolContext()
+        def action = Mock(Action)
+        invocationContext.setArgAction(action)
+        def compiler = getCompiler(invocationContext, O_EXT, false)
+        def testDir = tmpDirProvider.testDirectory
+        def objectFileDir = testDir.file("output/objects")
+        def sourceFiles = [ testDir.file("source1.ext"), testDir.file("source2.ext") ]
+        when:
+        NativeCompileSpec compileSpec = Stub(getCompileSpecType()) {
+            getObjectFileDir() >> objectFileDir
+            getSourceFiles() >> sourceFiles
+            getOperationLogger() >> Mock(BuildOperationLogger)
+            getPrefixHeaderFile() >> null
+            getPreCompiledHeaderObjectFile() >> null
+        }
+        and:
+        invocationContext.getArgAction() >> action
+
+        and:
+        compiler.execute(compileSpec)
+
+        then:
+        1 * action.execute(_)
+        2 * commandLineTool.execute(_)
+    }
+
+    def "options file is written"() {
+        given:
+        def invocationContext = new DefaultMutableCommandLineToolContext()
+        def compiler = getCompiler(invocationContext, O_EXT, true)
+        def testDir = tmpDirProvider.testDirectory
+        def includeDir = testDir.file("includes")
+        def commandLineArgs = getCompilerSpecificArguments(includeDir)
+
+        when:
+        NativeCompileSpec compileSpec = Stub(getCompileSpecType()) {
+            getMacros() >> [foo: "bar", empty: null]
+            getAllArgs() >> ["-firstArg", "-secondArg"]
+            getIncludeRoots() >> [ includeDir ]
+            getTempDir() >> testDir
+            getOperationLogger() >> Mock(BuildOperationLogger)
+            getPrefixHeaderFile() >> null
+            getPreCompiledHeaderObjectFile() >> null
+        }
+
+        and:
+        def actualArgs = compiler.getArguments(compileSpec)
+
+        then:
+        // Almost all options are stripped when using the options file
+        actualArgs != commandLineArgs
+        // options file should exist
+        testDir.file("options.txt").exists()
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriterTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriterTest.groovy
new file mode 100644
index 0000000..05b4372
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OptionsFileArgsWriterTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+
+import spock.lang.Specification
+
+abstract class OptionsFileArgsWriterTest extends Specification {
+
+    abstract OptionsFileArgsWriter getArgsWriter()
+
+    def "final args only contains @path/to/options.txt"() {
+        given:
+        def argsWriter = getArgsWriter()
+        List<String> args = ["firstArg", "secondArg"]
+
+        when:
+        argsWriter.execute(args)
+
+        then:
+        args.size() == 1
+        hasOptionsFile(args)
+    }
+
+    boolean hasOptionsFile(args) {
+        args.any({ it.startsWith("@") && it.endsWith("options.txt") })
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompilerTest.groovy
new file mode 100644
index 0000000..338976a
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/OutputCleaningCompilerTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+
+import org.gradle.api.tasks.WorkResult
+import org.gradle.internal.hash.HashUtil
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+import org.gradle.language.base.internal.compile.Compiler;
+
+class OutputCleaningCompilerTest extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    private static final String O_EXT = ".o"
+
+    NativeCompileSpec spec = Mock(NativeCompileSpec);
+    Compiler delegateCompiler = Mock(Compiler)
+
+    OutputCleaningCompiler cleanCompiler = new OutputCleaningCompiler<NativeCompileSpec>(delegateCompiler, O_EXT);
+
+    TestFile outputDir = tmpDirProvider.createDir("objs")
+
+    WorkResult workResult = Mock(WorkResult)
+    List<TestFile> sourceFiles
+
+    def setup() {
+        _ * spec.objectFileDir >> outputDir
+        _ * delegateCompiler.execute(_) >> { NativeCompileSpec spec ->
+            List<File> sourceFiles = spec.getSourceFiles()
+            sourceFiles.each{ inputFile ->
+                createObjDummy(inputFile)
+            }
+            _ * workResult.getDidWork() >> !sourceFiles.isEmpty();
+            return workResult
+        }
+    }
+
+    def "deletes output files and according hash directory"() {
+        setup:
+        sourceFiles = Arrays.asList(tmpDirProvider.file("src/main/c/main.c"), tmpDirProvider.file("src/main/c/foo/main2.c"))
+        when:
+        compile(sourceFiles[0], sourceFiles[1])
+        then:
+        outputDir.listFiles().size() == 2
+
+        when:
+        compile(sourceFiles[0])
+        then:
+        objectFile(sourceFiles[0])
+        !objectFile(sourceFiles[1])
+
+
+        when:
+        compile(sourceFiles[1])
+        then:
+        !objectFile(sourceFiles[0])
+        objectFile(sourceFiles[1])
+    }
+
+    def "removes stale output when source file is moved"() {
+        setup:
+        def orgFile = tmpDirProvider.file("src/main/c/org/main.c")
+        def movedFile = tmpDirProvider.file("src/main/c/moved/main.c")
+        sourceFiles = [orgFile, movedFile]
+
+        when:
+        compile(orgFile)
+
+        then:
+        objectFile(orgFile)
+        !objectFile(movedFile)
+
+        when:
+        compile(movedFile)
+        then:
+        !objectFile(orgFile)
+        objectFile(movedFile)
+    }
+
+    def objectFile(TestFile testFile) {
+        assert outputDir.listFiles().size() == 1
+        assert outputDir.listFiles()[0].listFiles().size() == 1
+        outputDir.listFiles()[0].listFiles()[0].text == testFile.absolutePath
+    }
+
+    def compile(TestFile... sourceToCompile) {
+        List<TestFile> toCompile = Arrays.asList(sourceToCompile)
+        List<TestFile> toRemove = sourceFiles - toCompile
+        2 * spec.getSourceFiles() >> toCompile
+        1 * spec.getRemovedSourceFiles() >> toRemove
+        cleanCompiler.execute(spec)
+    }
+
+    def createObjDummy(File sourceFile) {
+        TestFile objectFile = outputDir.file("${HashUtil.createCompactMD5(sourceFile.absolutePath)}/${sourceFile.name - ".c" + ".o" }")
+        objectFile.touch()
+        objectFile.text = sourceFile.absolutePath
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtilTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtilTest.groovy
new file mode 100644
index 0000000..f733d49
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/PrefixHeaderFileGeneratorUtilTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+
+import com.google.common.collect.Sets
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class PrefixHeaderFileGeneratorUtilTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    def "generates a prefix header file" () {
+        def headers = Sets.newLinkedHashSet()
+        headers.add "header.h"
+        headers.add "<stdio.h>"
+        headers.add "some/path/to/another.h"
+        def tempDir = tmpDirProvider.createDir("temp")
+        def prefixHeaderFile = new File(tempDir, "prefix-headers.h")
+
+        when:
+        PrefixHeaderFileGeneratorUtil.generatePCHFile(headers, prefixHeaderFile)
+
+        then:
+        prefixHeaderFile.text == TextUtil.toPlatformLineSeparators(
+"""#include "header.h"
+#include <stdio.h>
+#include "some/path/to/another.h"
+""")
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailableNativePlatformToolProviderTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailableNativePlatformToolProviderTest.groovy
new file mode 100644
index 0000000..0d077fd
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/UnavailableNativePlatformToolProviderTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal
+import org.gradle.api.GradleException
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal
+import org.gradle.platform.base.internal.toolchain.ToolChainAvailability
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class UnavailableNativePlatformToolProviderTest extends Specification {
+    def reason = new ToolChainAvailability().unavailable("broken")
+    def toolChain = new UnavailablePlatformToolProvider(Stub(OperatingSystemInternal), reason)
+
+    def "is not available"() {
+        expect:
+        !toolChain.available
+
+        def visitor = Mock(TreeVisitor)
+
+        when:
+        toolChain.explain(visitor)
+
+        then:
+        1 * visitor.node("broken")
+    }
+
+    def "throws failure when attempting to create a compiler"() {
+        when:
+        toolChain.newCompiler(NativeCompileSpec.class)
+
+        then:
+        GradleException e = thrown()
+        e.message == "broken"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
new file mode 100644
index 0000000..ddaece8
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.text.TreeFormatter
+import org.gradle.nativeplatform.platform.internal.*
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain
+import org.gradle.nativeplatform.toolchain.NativePlatformToolChain
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider
+import org.gradle.nativeplatform.toolchain.internal.ToolType
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProvider
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.GccVersionResult
+import org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolSearchResult
+import org.gradle.nativeplatform.toolchain.internal.tools.GccCommandLineToolConfigurationInternal
+import org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.util.TreeVisitor
+import org.gradle.util.UsesNativeServices
+import spock.lang.Specification
+
+import static org.gradle.nativeplatform.platform.internal.ArchitectureInternal.InstructionSet.X86
+
+ at UsesNativeServices
+class AbstractGccCompatibleToolChainTest extends Specification {
+    def fileResolver = Mock(FileResolver)
+    def execActionFactory = Mock(ExecActionFactory)
+    def toolSearchPath = Stub(ToolSearchPath)
+    def tool = Stub(CommandLineToolSearchResult) {
+        isAvailable() >> true
+    }
+    def missing = Stub(CommandLineToolSearchResult) {
+        isAvailable() >> false
+    }
+    def correctCompiler = Stub(GccVersionResult) {
+        isAvailable() >> true
+    }
+    def metaDataProvider = Stub(CompilerMetaDataProvider)
+    def operatingSystem = Stub(OperatingSystem)
+    def buildOperationProcessor = Stub(BuildOperationProcessor)
+
+    def instantiator = DirectInstantiator.INSTANCE
+    def toolChain = new TestNativeToolChain("test", buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, toolSearchPath, metaDataProvider, instantiator)
+    def platform = Stub(NativePlatformInternal)
+
+    def dummyOs = new DefaultOperatingSystem("currentOS", OperatingSystem.current())
+    def dummyArch = Architectures.forInput("x86_64")
+
+    def "is unavailable when platform is not known and is not the default platform"() {
+        given:
+        platform.name >> 'unknown'
+
+        expect:
+        def platformToolChain = toolChain.select(platform)
+        !platformToolChain.available
+        getMessage(platformToolChain) == "Don't know how to build for platform 'unknown'."
+    }
+
+    def "is unavailable when no language tools can be found"() {
+        def compilerMissing = Stub(CommandLineToolSearchResult) {
+            isAvailable() >> false
+            explain(_) >> { TreeVisitor<String> visitor -> visitor.node("c compiler not found") }
+        }
+
+        given:
+        platform.operatingSystem >> dummyOs
+        platform.architecture >> dummyArch
+
+        and:
+        toolSearchPath.locate(ToolType.C_COMPILER, "gcc") >> compilerMissing
+        toolSearchPath.locate(ToolType.CPP_COMPILER, "g++") >> missing
+        toolSearchPath.locate(ToolType.OBJECTIVEC_COMPILER, "gcc") >> missing
+        toolSearchPath.locate(ToolType.OBJECTIVECPP_COMPILER, "g++") >> missing
+
+        expect:
+        def platformToolChain = toolChain.select(platform)
+        !platformToolChain.available
+        getMessage(platformToolChain) == "c compiler not found"
+    }
+
+    def "is unavailable when a compiler is found with incorrect implementation"() {
+        def wrongCompiler = Stub(GccVersionResult) {
+            isAvailable() >> false
+            explain(_) >> { TreeVisitor<String> visitor -> visitor.node("c compiler is not gcc") }
+        }
+
+        given:
+        platform.operatingSystem >> dummyOs
+        platform.architecture >> dummyArch
+
+        and:
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> wrongCompiler
+
+        expect:
+        def platformToolChain = toolChain.select(platform)
+        !platformToolChain.available
+        getMessage(platformToolChain) == "c compiler is not gcc"
+    }
+
+    def "is available when any language tool can be found and compiler has correct implementation"() {
+        given:
+        platform.operatingSystem >> dummyOs
+        platform.architecture >> dummyArch
+
+        and:
+        toolSearchPath.locate(ToolType.C_COMPILER, "gcc") >> missing
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> correctCompiler
+
+        and:
+
+        expect:
+        toolChain.select(platform).available
+    }
+
+    def "is available when platform configuration registered for platform and tools are available"() {
+        given:
+        platform.name >> "SomePlatform"
+        toolChain.target("SomePlatform", Mock(Action))
+
+        and:
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> correctCompiler
+
+        expect:
+        toolChain.select(platform).available
+    }
+
+    def "selected toolChain applies platform configuration action"() {
+        def platform1 = Mock(NativePlatformInternal)
+        def platform2 = Mock(NativePlatformInternal)
+        platform1.name >> "platform1"
+        platform2.name >> "platform2"
+
+        platform1.operatingSystem >> dummyOs
+        platform2.operatingSystem >> dummyOs
+
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> correctCompiler
+
+        given:
+        int platformActionApplied = 0
+        toolChain.target([platform1.getName(), platform2.getName()], new Action<NativePlatformToolChain>() {
+            void execute(NativePlatformToolChain configurableToolChain) {
+                platformActionApplied++;
+            }
+        });
+
+        when:
+        PlatformToolProvider selected = toolChain.select(platform1)
+
+        then:
+        selected.isAvailable();
+        assert platformActionApplied == 1
+
+        when:
+        selected = toolChain.select(platform2)
+
+        then:
+        selected.isAvailable()
+        assert platformActionApplied == 2
+    }
+
+    def "supplies no additional arguments to target native binary for tool chain default"() {
+        def action = Mock(Action)
+
+        given:
+        toolSearchPath.locate(_, _) >> tool
+        platform.getOperatingSystem() >> dummyOs
+        platform.getArchitecture() >> dummyArch
+        toolChain.eachPlatform(action)
+
+        when:
+        toolChain.select(platform)
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            argsFor(platformToolChain.linker) == []
+            argsFor(platformToolChain.cCompiler) == []
+            argsFor(platformToolChain.cppCompiler) == []
+            argsFor(platformToolChain.assembler) == []
+            argsFor(platformToolChain.staticLibArchiver) == []
+            argsFor(platformToolChain.objcCompiler) == []
+            argsFor(platformToolChain.objcppCompiler) == []
+        }
+    }
+
+    def "supplies args for supported architecture for non-os x platforms"() {
+        def action = Mock(Action)
+
+        given:
+        toolSearchPath.locate(_, _) >> tool
+        platform.operatingSystem >> dummyOs
+        platform.architecture >> Architectures.forInput(arch)
+        toolChain.eachPlatform(action)
+
+        when:
+        toolChain.select(platform)
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            argsFor(platformToolChain.linker) == [linkerArg]
+            argsFor(platformToolChain.cppCompiler) == [compilerArg]
+            argsFor(platformToolChain.cCompiler) == [compilerArg]
+            argsFor(platformToolChain.objcCompiler) == [compilerArg]
+            argsFor(platformToolChain.objcppCompiler) == [compilerArg]
+            argsFor(platformToolChain.assembler) == [compilerArg]
+            argsFor(platformToolChain.staticLibArchiver) == []
+        }
+
+        where:
+        arch     | linkerArg | compilerArg
+        "i386"   | "-m32"    | "-m32"
+        "x86_64" | "-m64"    | "-m64"
+    }
+
+    def "supplies args for supported architecture for os x platforms"() {
+        def action = Mock(Action)
+
+        given:
+        toolSearchPath.locate(_, _) >> tool
+        platform.operatingSystem >> new DefaultOperatingSystem("osx", OperatingSystem.MAC_OS)
+        platform.architecture >> new DefaultArchitecture(arch)
+
+        toolChain.target(platform.name)
+        toolChain.eachPlatform(action)
+
+        when:
+        toolChain.select(platform)
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            argsFor(platformToolChain.linker) == [linkerArg]
+            argsFor(platformToolChain.cppCompiler) == [compilerArg]
+            argsFor(platformToolChain.cCompiler) == [compilerArg]
+            argsFor(platformToolChain.objcCompiler) == [compilerArg]
+            argsFor(platformToolChain.objcppCompiler) == [compilerArg]
+            argsFor(platformToolChain.assembler) == assemblerArgs
+            argsFor(platformToolChain.staticLibArchiver) == []
+        }
+
+        where:
+        arch     | instructionSet | registerSize | linkerArg | compilerArg | assemblerArgs
+        "i386"   | X86            | 32           | "-m32"    | "-m32"      | ["-arch", "i386"]
+        "x86_64" | X86            | 64           | "-m64"    | "-m64"      | ["-arch", "x86_64"]
+    }
+
+    def "uses supplied platform configurations in order to target binary"() {
+        setup:
+        _ * platform.getName() >> "platform2"
+        def platformConfig1 = Mock(Action)
+        def platformConfig2 = Mock(Action)
+
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> correctCompiler
+
+        toolChain.target("platform1", platformConfig1)
+        toolChain.target("platform2", platformConfig2)
+
+        when:
+        PlatformToolProvider platformToolChain = toolChain.select(platform)
+
+        then:
+        platformToolChain.available
+
+        and:
+        1 * platformConfig2.execute(_)
+    }
+
+    def "uses platform specific toolchain configuration"() {
+        given:
+        boolean configurationApplied = false
+        _ * platform.getName() >> "testPlatform"
+
+        when:
+        toolSearchPath.locate(_, _) >> tool
+        metaDataProvider.getGccMetaData(_, _) >> correctCompiler
+
+        and:
+        toolChain.target(platform.getName(), new Action<NativePlatformToolChain>() {
+            void execute(NativePlatformToolChain configurableToolChain) {
+                configurationApplied = true;
+            }
+        })
+
+        then:
+        toolChain.select(platform).available
+        configurationApplied
+    }
+
+    def "provided action can configure platform tool chain"() {
+        given:
+        platform.operatingSystem >> dummyOs
+        platform.architecture >> dummyArch
+
+        def action = Mock(Action)
+        toolChain.eachPlatform(action)
+
+        when:
+        toolChain.select(platform)
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            assert platformToolChain.platform == platform
+            assert platformToolChain.cCompiler
+            assert platformToolChain.cppCompiler
+            assert platformToolChain.objcCompiler
+            assert platformToolChain.objcppCompiler
+            assert platformToolChain.linker
+            assert platformToolChain.staticLibArchiver
+        }
+    }
+
+    def getMessage(ToolSearchResult result) {
+        def formatter = new TreeFormatter()
+        result.explain(formatter)
+        return formatter.toString()
+    }
+
+    static class TestNativeToolChain extends AbstractGccCompatibleToolChain {
+        TestNativeToolChain(String name, BuildOperationProcessor buildOperationProcessor, OperatingSystem operatingSystem, FileResolver fileResolver, ExecActionFactory execActionFactory, ToolSearchPath tools, CompilerMetaDataProvider metaDataProvider, Instantiator instantiator) {
+            super(name, buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, tools, metaDataProvider, instantiator)
+        }
+
+        @Override
+        protected String getTypeName() {
+            return "Test"
+        }
+    }
+
+    def argsFor(GccCommandLineToolConfigurationInternal tool) {
+        def args = []
+        tool.getArgAction().execute(args)
+        args
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AssemblerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AssemblerTest.groovy
new file mode 100644
index 0000000..1e51963
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AssemblerTest.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.nativeplatform.toolchain.internal.gcc
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec
+
+class AssemblerTest extends GccCompatibleNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new Assembler(buildOperationProcessor, commandLineTool, invocationContext, objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<AssembleSpec> getCompileSpecType() {
+        AssembleSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '-x', 'assembler' ] + super.getCompilerSpecificArguments(includeDir)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompilerTest.groovy
new file mode 100644
index 0000000..27509d4
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CCompilerTest.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.nativeplatform.toolchain.internal.gcc
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec
+
+class CCompilerTest extends GccCompatibleNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new CCompiler(buildOperationProcessor, commandLineTool, invocationContext, objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<CCompileSpec> getCompileSpecType() {
+        CCompileSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '-x', 'c' ] + super.getCompilerSpecificArguments(includeDir)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ClangToolChainTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ClangToolChainTest.groovy
new file mode 100644
index 0000000..88b4621
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/ClangToolChainTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain
+import org.gradle.nativeplatform.toolchain.internal.clang.ClangToolChain
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class ClangToolChainTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+    final FileResolver fileResolver = Mock(FileResolver)
+    final Instantiator instantiator = DirectInstantiator.INSTANCE
+    final toolChain = new ClangToolChain("clang", Stub(BuildOperationProcessor), Stub(OperatingSystem), fileResolver, Stub(ExecActionFactory), Stub(CompilerMetaDataProviderFactory), instantiator)
+
+    def "provides default tools"() {
+        def action = Mock(Action)
+
+        when:
+        toolChain.target("platform", action)
+        toolChain.select(Stub(NativePlatformInternal) { getName() >> "platform" })
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            assert platformToolChain.assembler.executable == 'clang'
+            assert platformToolChain.cCompiler.executable == 'clang'
+            assert platformToolChain.cppCompiler.executable == 'clang++'
+            assert platformToolChain.objcCompiler.executable == 'clang'
+            assert platformToolChain.objcppCompiler.executable == 'clang++'
+            assert platformToolChain.linker.executable == 'clang++'
+            assert platformToolChain.staticLibArchiver.executable == 'ar'
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompilerTest.groovy
new file mode 100644
index 0000000..f867482
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/CppCompilerTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec
+
+class CppCompilerTest extends GccCompatibleNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new CppCompiler(buildOperationProcessor, commandLineTool, invocationContext, objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<CppCompileSpec> getCompileSpecType() {
+        CppCompileSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '-x', 'c++' ] + super.getCompilerSpecificArguments(includeDir)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompilerTest.groovy
new file mode 100644
index 0000000..d3740b2
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccCompatibleNativeCompilerTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec
+import org.gradle.nativeplatform.toolchain.internal.NativeCompilerTest
+
+abstract class GccCompatibleNativeCompilerTest extends NativeCompilerTest {
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '-c', '-Dfoo=bar', '-Dempty', '-firstArg', '-secondArg', '-I', includeDir.absoluteFile.toString() ]
+    }
+
+    def "arguments include GCC output flag and output file name"() {
+        given:
+        def compiler = getCompiler()
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output.ext")
+
+        when:
+        def args = compiler.getOutputArgs(Stub(NativeCompileSpec), outputFile)
+
+        then:
+        args == [ '-o', outputFile.absoluteFile.toString() ]
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinkerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinkerTest.groovy
new file mode 100644
index 0000000..f273d5b
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccLinkerTest.groovy
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+import org.gradle.internal.Actions
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.operations.BuildOperationQueue
+import org.gradle.internal.operations.logging.BuildOperationLogger
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.internal.LinkerSpec
+import org.gradle.nativeplatform.internal.SharedLibraryLinkerSpec
+import org.gradle.nativeplatform.platform.NativePlatform
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
+import org.gradle.nativeplatform.platform.internal.DefaultOperatingSystem
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.UsesNativeServices
+import org.junit.Rule
+import spock.lang.Specification
+
+ at UsesNativeServices
+class GccLinkerTest extends Specification {
+    public static final String LOG_LOCATION = "<log location>"
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    def operationLogger =  Mock(BuildOperationLogger)
+    def executable = new File("executable")
+    def invocationContext = Mock(CommandLineToolContext)
+    def invocation = Mock(CommandLineToolInvocation)
+    CommandLineToolInvocationWorker commandLineTool = Mock(CommandLineToolInvocationWorker)
+    BuildOperationProcessor buildOperationProcessor = Mock(BuildOperationProcessor)
+    BuildOperationQueue queue = Mock(BuildOperationQueue)
+
+    GccLinker linker = new GccLinker(buildOperationProcessor, commandLineTool, invocationContext, false)
+
+    def "links all object files in a single execution"() {
+        given:
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output/lib")
+
+        final expectedArgs = [
+                "-sys1", "-sys2",
+                "-shared",
+                getSoNameProp("installName"),
+                "-o", outputFile.absolutePath,
+                testDir.file("one.o").absolutePath,
+                testDir.file("two.o").absolutePath,
+                "-arg1", "-arg2"].flatten()
+
+        when:
+        LinkerSpec spec = Mock(SharedLibraryLinkerSpec)
+        spec.getSystemArgs() >> ['-sys1', '-sys2']
+        spec.getArgs() >> ['-arg1', '-arg2']
+        spec.getOutputFile() >> outputFile
+        spec.getLibraries() >> []
+        spec.getLibraryPath() >> []
+        spec.getInstallName() >> "installName"
+        spec.getTargetPlatform() >> new DefaultNativePlatform("default")
+        spec.getObjectFiles() >> [testDir.file("one.o"), testDir.file("two.o")]
+        spec.getOperationLogger() >> operationLogger
+
+        and:
+        linker.execute(spec)
+
+        then:
+        1 * operationLogger.getLogLocation() >> LOG_LOCATION
+        1 * buildOperationProcessor.newQueue(commandLineTool, LOG_LOCATION) >> queue
+        1 * invocationContext.getArgAction() >> Actions.doNothing()
+        1 * invocationContext.createInvocation("linking lib", expectedArgs, operationLogger) >> invocation
+        1 * queue.add(invocation)
+        1 * queue.waitForCompletion()
+        0 * _
+    }
+
+    List<String> getSoNameProp(def value) {
+        if (OperatingSystem.current().isWindows()) {
+            return []
+        }
+        if (OperatingSystem.current().isMacOsX()) {
+            return ["-Wl,-install_name,${value}"]
+        }
+        return ["-Wl,-soname,${value}"]
+    }
+
+    def "sets -install_name for osx"() {
+        given:
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output/lib")
+
+        final expectedArgs = [
+                "-shared",
+                "-Wl,-install_name,installName",
+                "-o", outputFile.absolutePath,
+                testDir.file("one.o").absolutePath].flatten()
+
+        when:
+        NativePlatform platform = Mock(NativePlatform)
+        platform.getOperatingSystem() >> new DefaultOperatingSystem("osx", OperatingSystem.MAC_OS)
+
+        LinkerSpec spec = Mock(SharedLibraryLinkerSpec)
+        spec.getSystemArgs() >> []
+        spec.getArgs() >> []
+        spec.getOutputFile() >> outputFile
+        spec.getLibraries() >> []
+        spec.getLibraryPath() >> []
+        spec.getInstallName() >> "installName"
+        spec.getTargetPlatform() >> platform
+        spec.getObjectFiles() >> [testDir.file("one.o")]
+        spec.getOperationLogger() >> operationLogger
+
+        and:
+        linker.execute(spec)
+
+        then:
+        1 * operationLogger.getLogLocation() >> LOG_LOCATION
+        1 * buildOperationProcessor.newQueue(commandLineTool, LOG_LOCATION) >> queue
+        1 * invocationContext.getArgAction() >> Actions.doNothing()
+        1 * invocationContext.createInvocation("linking lib", expectedArgs, operationLogger) >> invocation
+        1 * queue.add(invocation)
+        1 * queue.waitForCompletion()
+        0 * _
+    }
+
+    def "ignores install name for windows"() {
+        given:
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output/lib")
+
+        final expectedArgs = [
+                "-shared",
+                "-o", outputFile.absolutePath,
+                testDir.file("one.o").absolutePath].flatten()
+
+        when:
+        NativePlatform platform = Mock(NativePlatform)
+        platform.getOperatingSystem() >> new DefaultOperatingSystem("windows", OperatingSystem.WINDOWS)
+
+        LinkerSpec spec = Mock(SharedLibraryLinkerSpec)
+        spec.getSystemArgs() >> []
+        spec.getArgs() >> []
+        spec.getOutputFile() >> outputFile
+        spec.getLibraries() >> []
+        spec.getLibraryPath() >> []
+        spec.getInstallName() >> "installName"
+        spec.getTargetPlatform() >> platform
+        spec.getObjectFiles() >> [testDir.file("one.o")]
+        spec.getOperationLogger() >> operationLogger
+
+        and:
+        linker.execute(spec)
+
+        then:
+        1 * operationLogger.getLogLocation() >> LOG_LOCATION
+        1 * buildOperationProcessor.newQueue(commandLineTool, LOG_LOCATION) >> queue
+        1 * invocationContext.getArgAction() >> Actions.doNothing()
+        1 * invocationContext.createInvocation("linking lib", expectedArgs, operationLogger) >> invocation
+        1 * queue.add(invocation)
+        1 * queue.waitForCompletion()
+        0 * _
+    }
+
+    def "sets -soname for linux"() {
+        given:
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output/lib")
+
+        final expectedArgs = [
+                "-shared",
+                "-Wl,-soname,installName",
+                "-o", outputFile.absolutePath,
+                testDir.file("one.o").absolutePath].flatten()
+
+        when:
+        NativePlatform platform = Mock(NativePlatform)
+        platform.getOperatingSystem() >> new DefaultOperatingSystem("osx", OperatingSystem.LINUX)
+
+        LinkerSpec spec = Mock(SharedLibraryLinkerSpec)
+        spec.getSystemArgs() >> []
+        spec.getArgs() >> []
+        spec.getOutputFile() >> outputFile
+        spec.getLibraries() >> []
+        spec.getLibraryPath() >> []
+        spec.getInstallName() >> "installName"
+        spec.getTargetPlatform() >> platform
+        spec.getObjectFiles() >> [testDir.file("one.o")]
+        spec.getOperationLogger() >> operationLogger
+
+        and:
+        linker.execute(spec)
+
+        then:
+        1 * operationLogger.getLogLocation() >> LOG_LOCATION
+        1 * buildOperationProcessor.newQueue(commandLineTool, LOG_LOCATION) >> queue
+        1 * invocationContext.getArgAction() >> Actions.doNothing()
+        1 * invocationContext.createInvocation("linking lib", expectedArgs, operationLogger) >> invocation
+        1 * queue.add(invocation)
+        1 * queue.waitForCompletion()
+        0 * _
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriterTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriterTest.groovy
new file mode 100644
index 0000000..b4a5064
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccOptionsFileArgsWriterTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriter
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriterTest
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Unroll
+
+class GccOptionsFileArgsWriterTest extends OptionsFileArgsWriterTest {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    OptionsFileArgsWriter getArgsWriter() {
+        new GccOptionsFileArgsWriter(tmpDirProvider.getTestDirectory())
+    }
+
+    @Unroll
+    def "args keep #arg in command line options"() {
+        given:
+        def argsWriter = getArgsWriter()
+        List<String> args = [arg, "strippedArg"]
+
+        when:
+        argsWriter.execute(args)
+
+        then:
+        args.size() == 2
+        args.contains(arg)
+        hasOptionsFile(args)
+
+        where:
+        arg    | _
+        '-m32' | _
+        '-m64' | _
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChainTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChainTest.groovy
new file mode 100644
index 0000000..253b564
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/GccToolChainTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.gcc
+
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.CompilerMetaDataProviderFactory
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class GccToolChainTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+    final FileResolver fileResolver = Mock(FileResolver)
+    Instantiator instantiator = DirectInstantiator.INSTANCE
+
+    final toolChain = new GccToolChain(instantiator , "gcc", Stub(BuildOperationProcessor), OperatingSystem.current(), fileResolver, Stub(ExecActionFactory), Stub(CompilerMetaDataProviderFactory))
+
+    def "provides default tools"() {
+        def action = Mock(Action)
+
+        when:
+        toolChain.target("platform", action)
+        toolChain.select(Stub(NativePlatformInternal) { getName() >> "platform" })
+
+        then:
+        1 * action.execute(_) >> { GccPlatformToolChain platformToolChain ->
+            assert platformToolChain.assembler.executable == 'gcc'
+            assert platformToolChain.cCompiler.executable == 'gcc'
+            assert platformToolChain.cppCompiler.executable == 'g++'
+            assert platformToolChain.objcCompiler.executable == 'gcc'
+            assert platformToolChain.objcppCompiler.executable == 'g++'
+            assert platformToolChain.linker.executable == 'g++'
+            assert platformToolChain.staticLibArchiver.executable == 'ar'
+        }
+    }
+
+    def "resolves path entries"() {
+        def testDir = tmpDirProvider.testDirectory
+
+        when:
+        toolChain.path "The Path"
+        toolChain.path "Path1", "Path2"
+
+        then:
+        fileResolver.resolve("The Path") >> testDir.file("one")
+        fileResolver.resolve("Path1") >> testDir.file("two")
+        fileResolver.resolve("Path2") >> testDir.file("three")
+
+        and:
+        toolChain.path == [testDir.file("one"), testDir.file("two"), testDir.file("three")]
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy
new file mode 100644
index 0000000..5e25d10
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/version/GccVersionDeterminerTest.groovy
@@ -0,0 +1,197 @@
+/*
+ * 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.nativeplatform.toolchain.internal.gcc.version
+
+import org.gradle.api.Transformer
+import org.gradle.process.ExecResult
+import org.gradle.process.internal.ExecAction
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.util.TreeVisitor
+import org.gradle.util.UsesNativeServices
+import org.gradle.util.VersionNumber
+import spock.lang.Specification
+import spock.lang.Unroll
+
+ at UsesNativeServices
+class GccVersionDeterminerTest extends Specification {
+    def execActionFactory = Mock(ExecActionFactory)
+    static def gcc4 = """#define __GNUC_MINOR__ 2
+#define __GNUC_PATCHLEVEL__ 1
+#define __GNUC__ 4
+#define __INTMAX_C(c) c ## LL
+#define __REGISTER_PREFIX__ """
+    static def gcc3 = """#define __gnu_linux__ 1
+#define __GNUC_PATCHLEVEL__ 4
+#define __GNUC__ 3
+#define __GNUC_MINOR__ 3
+"""
+    static def gccMajorOnly = """#define __gnu_linux__ 1
+#define __GNUC__ 3
+"""
+    static def gccNoMinor = """#define __gnu_linux__ 1
+#define __GNUC__ 3
+#define __GNUC_PATCHLEVEL__ 4
+"""
+    static def gccX86 = """#define __GNUC_MINOR__ 2
+#define __GNUC_PATCHLEVEL__ 1
+#define __GNUC__ 4
+#define __i386__ 1
+"""
+    static def gccAmd64 = """#define __GNUC_MINOR__ 2
+#define __GNUC_PATCHLEVEL__ 1
+#define __GNUC__ 4
+#define __amd64__ 1
+"""
+    static def clang = """#define __GNUC_MINOR__ 2
+#define __GNUC_PATCHLEVEL__ 1
+#define __GNUC__ 4
+#define __VERSION__ "4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)"
+#define __clang__ 1
+#define __clang_major__ 5
+#define __clang_minor__ 0
+#define __clang_patchlevel__ 0
+#define __clang_version__ "5.0 (clang-500.2.79)"
+"""
+
+    @Unroll
+    "can scrape version from output of GCC #version"() {
+        expect:
+        def result = output(output)
+        result.available
+        result.version == VersionNumber.parse(version)
+        !result.clang
+
+        where:
+        output       | version
+        gccMajorOnly | "3.0.0"
+        gccNoMinor   | "3.0.4"
+        gcc3         | "3.3.4"
+        gcc4         | "4.2.1"
+    }
+
+    @Unroll
+    "can scrape architecture from GCC output"() {
+        expect:
+        def x86 = output(gccX86)
+        x86.defaultArchitecture.isI386()
+
+        def amd64 = output(gccAmd64)
+        amd64.defaultArchitecture.isAmd64()
+    }
+
+    def "handles output that cannot be parsed"() {
+        def visitor = Mock(TreeVisitor)
+
+        expect:
+        def result = output(output)
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not determine GCC version: g++ produced unexpected output.")
+
+        where:
+        output << ["not sure about this", ""]
+    }
+
+    def "handles failure to execute g++"() {
+        given:
+        def visitor = Mock(TreeVisitor)
+        def action = Mock(ExecAction)
+        def execResult = Mock(ExecResult)
+
+        and:
+        def determiner = GccVersionDeterminer.forGcc(execActionFactory)
+        def binary = new File("g++")
+
+        when:
+        def result = determiner.getGccMetaData(binary, [])
+
+        then:
+        1 * execActionFactory.newExecAction() >> action
+        1 * action.execute() >> execResult
+        1 * execResult.getExitValue() >> 1
+
+        and:
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not determine GCC version: failed to execute g++ -dM -E -.")
+    }
+
+    def "can scrape ok output for clang"() {
+        expect:
+        def result = output clang, true
+        result.available
+        result.version == VersionNumber.parse("5.0.0")
+        result.clang
+    }
+
+    def "detects clang pretending to be gcc"() {
+        def visitor = Mock(TreeVisitor)
+
+        expect:
+        def result = output clang
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("XCode g++ is a wrapper around Clang. Treating it as Clang and not GCC.")
+    }
+
+    def "detects gcc pretending to be clang"() {
+        def visitor = Mock(TreeVisitor)
+
+        expect:
+        def result = output gcc4, true
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("g++ appears to be GCC rather than Clang. Treating it as GCC.")
+    }
+
+    GccVersionResult output(String output, boolean clang = false) {
+        def action = Mock(ExecAction)
+        def result = Mock(ExecResult)
+        1 * execActionFactory.newExecAction() >> action
+        1 * action.setStandardOutput(_) >> { OutputStream outstr -> outstr << output; action }
+        1 * action.execute() >> result
+        new GccVersionDeterminer(execActionFactory, clang).getGccMetaData(new File("g++"), [])
+    }
+
+    Transformer transformer(constant) {
+        transformer { constant }
+    }
+
+    Transformer transformer(Closure closure) {
+        new Transformer() {
+            String transform(original) {
+                closure.call(original)
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/AssemblerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/AssemblerTest.groovy
new file mode 100644
index 0000000..d936da8
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/AssemblerTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.internal.Transformers
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec
+
+class AssemblerTest extends VisualCppNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new Assembler(buildOperationProcessor, commandLineTool, invocationContext, Transformers.noOpTransformer(), objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<AssembleSpec> getCompileSpecType() {
+        AssembleSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [''] + super.getCompilerSpecificArguments(includeDir)
+    }
+
+    def "check that position sensitive arguments are in the right order"() {
+        given:
+        def genericArgs = [ '/c' ]
+        def sourceArgs = [ 'path/to/source.s' ]
+        def outputArgs = [ 'path/to/output.o' ]
+        def compiler = getCompiler()
+        expect:
+        compiler.buildPerFileArgs(genericArgs, sourceArgs, outputArgs, []) as List == [ 'path/to/output.o', '/c', 'path/to/source.s' ]
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompilerTest.groovy
new file mode 100644
index 0000000..d69759c
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CCompilerTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+import org.gradle.internal.Transformers
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec
+
+class CCompilerTest extends VisualCppNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new CCompiler(buildOperationProcessor, commandLineTool, invocationContext, Transformers.noOpTransformer(), objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<CCompileSpec> getCompileSpecType() {
+        CCompileSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '/TC' ] + super.getCompilerSpecificArguments(includeDir)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompilerTest.groovy
new file mode 100644
index 0000000..a1ff0f8
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/CppCompilerTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.internal.Transformers
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec
+
+class CppCompilerTest  extends VisualCppNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new CppCompiler(buildOperationProcessor, commandLineTool, invocationContext, Transformers.noOpTransformer(), objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<CppCompileSpec> getCompileSpecType() {
+        CppCompileSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        [ '/TP' ] + super.getCompilerSpecificArguments(includeDir)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy
new file mode 100644
index 0000000..aa601b4
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultVisualStudioLocatorTest.groovy
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException
+import net.rubygrapefruit.platform.SystemInfo
+import net.rubygrapefruit.platform.WindowsRegistry
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TreeVisitor
+import org.gradle.util.VersionNumber
+import org.junit.Rule
+
+import spock.lang.Specification
+
+class DefaultVisualStudioLocatorTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final WindowsRegistry windowsRegistry =  Stub(WindowsRegistry)
+    final SystemInfo systemInfo =  Stub(SystemInfo)
+    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
+        isWindows() >> true
+        getExecutableName(_ as String) >> { String exeName -> exeName }
+    }
+    final VisualStudioLocator visualStudioLocator = new DefaultVisualStudioLocator(operatingSystem, windowsRegistry, systemInfo)
+
+    def "use highest visual studio version found in the registry"() {
+        def dir1 = vsDir("vs1");
+        def dir2 = vsDir("vs2");
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["", "11.0", "12.0", "ignore-me"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "11.0") >> dir1.absolutePath + "/VC"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> dir2.absolutePath + "/VC"
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(null)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ 12.0"
+        result.visualStudio.version == VersionNumber.parse("12.0")
+        result.visualStudio.baseDir == dir2
+        result.visualStudio.visualCpp
+    }
+
+    def "visual studio not available when nothing in registry and executable not found in path"() {
+        def visitor = Mock(TreeVisitor)
+
+        given:
+        windowsRegistry.getValueNames(_, _) >> { throw new MissingRegistryEntryException("not found") }
+        operatingSystem.findInPath(_) >> null
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(null)
+
+        then:
+        !result.available
+        result.visualStudio == null
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not locate a Visual Studio installation, using the Windows registry and system path.")
+    }
+
+    def "locates visual studio installation based on executables in path"() {
+        def vsDir = vsDir("vs")
+
+        given:
+        windowsRegistry.getValueNames(_, _) >> { throw new MissingRegistryEntryException("not found") }
+        operatingSystem.findInPath("cl.exe") >> vsDir.file("VC/bin/cl.exe")
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(null)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ from system path"
+        result.visualStudio.version == VersionNumber.UNKNOWN
+        result.visualStudio.baseDir == vsDir
+    }
+
+    def "uses visual studio using specified install dir"() {
+        def vsDir1 = vsDir("vs")
+        def vsDir2 = vsDir("vs-2")
+        def ignored = vsDir("vs-3")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> ignored.absolutePath + "/VC"
+        assert visualStudioLocator.locateVisualStudioInstalls(null).available
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(vsDir1)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ from user provided path"
+        result.visualStudio.version == VersionNumber.UNKNOWN
+        result.visualStudio.baseDir == vsDir1
+
+        when:
+        result = visualStudioLocator.locateVisualStudioInstalls(vsDir2)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ from user provided path"
+        result.visualStudio.version == VersionNumber.UNKNOWN
+        result.visualStudio.baseDir == vsDir2
+    }
+
+    def "visual studio not found when specified directory does not look like an install"() {
+        def visitor = Mock(TreeVisitor)
+        def providedDir = tmpDir.createDir("vs")
+        def ignoredDir = vsDir("vs-2")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> ignoredDir.absolutePath + "/VC"
+        assert visualStudioLocator.locateVisualStudioInstalls(null).available
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(providedDir)
+
+        then:
+        !result.available
+        result.visualStudio == null
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("The specified installation directory '$providedDir' does not appear to contain a Visual Studio installation.")
+    }
+
+    def "fills in meta-data from registry for install discovered using the system path"() {
+        def vsDir = vsDir("vs")
+
+        given:
+        operatingSystem.findInPath("cl.exe") >> vsDir.file("VC/bin/cl.exe")
+
+        and:
+        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> vsDir.absolutePath + "/VC"
+        
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(null)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ 12.0"
+        result.visualStudio.version == VersionNumber.parse("12.0")
+        result.visualStudio.baseDir == vsDir
+    }
+
+    def "fills in meta-data from registry for user specified install"() {
+        def vsDir = vsDir("vs")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+
+        and:
+        windowsRegistry.getValueNames(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/) >> ["12.0"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\VisualStudio\SxS\VC7/, "12.0") >> vsDir.absolutePath + "/VC"
+
+        when:
+        def result = visualStudioLocator.locateVisualStudioInstalls(vsDir)
+
+        then:
+        result.available
+        result.visualStudio.name == "Visual C++ 12.0"
+        result.visualStudio.version == VersionNumber.parse("12.0")
+        result.visualStudio.baseDir == vsDir
+    }
+
+    def vsDir(String name) {
+        def dir = tmpDir.createDir(name)
+        dir.createDir("Common7")
+        dir.createFile("VC/bin/cl.exe")
+        dir.createDir("VC/lib")
+        return dir
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy
new file mode 100644
index 0000000..38bb93f
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/DefaultWindowsSdkLocatorTest.groovy
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import net.rubygrapefruit.platform.MissingRegistryEntryException
+import net.rubygrapefruit.platform.WindowsRegistry
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TreeVisitor
+import org.gradle.util.VersionNumber
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultWindowsSdkLocatorTest extends Specification {
+    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    final WindowsRegistry windowsRegistry = Stub(WindowsRegistry)
+    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
+        isWindows() >> true
+        getExecutableName(_ as String) >> { String exeName -> exeName }
+    }
+    final WindowsSdkLocator windowsSdkLocator = new DefaultWindowsSdkLocator(operatingSystem, windowsRegistry)
+
+    def "uses highest version SDK found in registry"() {
+        def dir1 = sdkDir("sdk1")
+        def dir2 = sdkDir("sdk2")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1", "v2"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> dir1.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "sdk 1"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "InstallationFolder") >> dir2.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "ProductVersion") >> "7.1"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v2/, "ProductName") >> "sdk 2"
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        result.available
+        result.sdk.name == "sdk 2"
+        result.sdk.version == VersionNumber.parse("7.1")
+        result.sdk.baseDir == dir2
+    }
+
+    def "uses windows kit if version is higher than windows SDK"() {
+        def dir1 = sdkDir("sdk1")
+        def dir2 = kitDir("sdk2")
+        def dir3 = kitDir("sdk3")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\SDKs\Windows/) >> ["v1"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> dir1.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.1"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "sdk 1"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot") >> dir2.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot81") >> dir3.absolutePath
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        result.available
+        result.sdk.name == "Windows Kit 8.1"
+        result.sdk.version == VersionNumber.parse("8.1")
+        result.sdk.baseDir == dir3
+    }
+
+    def "handles missing SDKs and Kits"() {
+        def dir = sdkDir("sdk1")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\SDKs\Windows/) >> { throw new MissingRegistryEntryException("missing") }
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot") >> { throw new MissingRegistryEntryException("missing") }
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Windows Kits\Installed Roots/, "KitsRoot81") >> dir.absolutePath
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        result.available
+        result.sdk.name == "Windows Kit 8.1"
+        result.sdk.version == VersionNumber.parse("8.1")
+        result.sdk.baseDir == dir
+    }
+
+    def "locates windows SDK based on executables in path"() {
+        def sdkDir = sdkDir("sdk")
+
+        given:
+        operatingSystem.findInPath("rc.exe") >> sdkDir.file("bin/rc.exe")
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        result.available
+        result.sdk.name == "Path-resolved Windows SDK"
+        result.sdk.version == VersionNumber.UNKNOWN
+        result.sdk.baseDir == sdkDir
+    }
+
+    def "SDK not available when not found in registry or system path"() {
+        def visitor = Mock(TreeVisitor)
+
+        given:
+        operatingSystem.findInPath(_) >> null
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        !result.available
+        result.sdk == null
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not locate a Windows SDK installation, using the Windows registry and system path.")
+    }
+
+    def "uses windows SDK using specified install dir"() {
+        def sdkDir1 = this.sdkDir("sdk-1")
+        def sdkDir2 = this.sdkDir("sdk-2")
+        def ignoredDir = sdkDir("ignored")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> ignoredDir.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
+        assert windowsSdkLocator.locateWindowsSdks(null).available
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(sdkDir1)
+
+        then:
+        result.available
+        result.sdk.name == "User-provided Windows SDK"
+        result.sdk.version == VersionNumber.UNKNOWN
+        result.sdk.baseDir == sdkDir1
+
+        when:
+        result = windowsSdkLocator.locateWindowsSdks(sdkDir2)
+
+        then:
+        result.available
+        result.sdk.name == "User-provided Windows SDK"
+        result.sdk.version == VersionNumber.UNKNOWN
+        result.sdk.baseDir == sdkDir2
+    }
+
+    def "SDK not available when specified install dir does not look like an SDK"() {
+        def sdkDir1 = tmpDir.createDir("dir")
+        def ignoredDir = sdkDir("ignored")
+        def visitor = Mock(TreeVisitor)
+
+        given:
+        operatingSystem.findInPath(_) >> null
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> ignoredDir.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
+        assert windowsSdkLocator.locateWindowsSdks(null).available
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(sdkDir1)
+
+        then:
+        !result.available
+        result.sdk == null
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("The specified installation directory '$sdkDir1' does not appear to contain a Windows SDK installation.")
+    }
+
+    def "fills in meta-data from registry for SDK discovered using the path"() {
+        def sdkDir = sdkDir("sdk1")
+
+        given:
+        operatingSystem.findInPath("rc.exe") >> sdkDir.file("bin/rc.exe")
+
+        and:
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> sdkDir.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(null)
+
+        then:
+        result.available
+        result.sdk.name == "installed sdk"
+        result.sdk.version == VersionNumber.parse("7.0")
+        result.sdk.baseDir == sdkDir
+    }
+
+    def "fills in meta-data from registry for SDK specified by user"() {
+        def sdkDir = sdkDir("sdk1")
+
+        given:
+        operatingSystem.findInPath(_) >> null
+
+        and:
+        windowsRegistry.getSubkeys(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows/) >> ["v1"]
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "InstallationFolder") >> sdkDir.absolutePath
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductVersion") >> "7.0"
+        windowsRegistry.getStringValue(WindowsRegistry.Key.HKEY_LOCAL_MACHINE, /SOFTWARE\Microsoft\Microsoft SDKs\Windows\v1/, "ProductName") >> "installed sdk"
+
+        when:
+        def result = windowsSdkLocator.locateWindowsSdks(sdkDir)
+
+        then:
+        result.available
+        result.sdk.name == "installed sdk"
+        result.sdk.version == VersionNumber.parse("7.0")
+        result.sdk.baseDir == sdkDir
+    }
+
+    def sdkDir(String name) {
+        def dir = tmpDir.createDir(name)
+        dir.createFile("bin/rc.exe")
+        dir.createFile("lib/kernel32.lib")
+        return dir
+    }
+
+    def kitDir(String name) {
+        def dir = tmpDir.createDir(name)
+        dir.createFile("bin/x86/rc.exe")
+        dir.createFile("lib/win8/um/x86/kernel32.lib")
+        return dir
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompilerTest.groovy
new file mode 100644
index 0000000..206f280
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppNativeCompilerTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec
+import org.gradle.nativeplatform.toolchain.internal.NativeCompilerTest
+
+abstract class VisualCppNativeCompilerTest extends NativeCompilerTest {
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        ['/nologo', '/c', '/Dfoo=bar', '/Dempty', '-firstArg', '-secondArg',
+         '/I' + includeDir.absoluteFile.toString()]
+    }
+
+    def "arguments include MSVC output flag and output file name"() {
+        given:
+        def compiler = getCompiler()
+        def testDir = tmpDirProvider.testDirectory
+        def outputFile = testDir.file("output.ext")
+        def spec = Stub(NativeCompileSpec)
+
+        when:
+        def args = compiler.getOutputArgs(spec, outputFile)
+
+        then:
+        args == ['/Fo' + outputFile.absoluteFile.toString()]
+    }
+
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriterTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriterTest.groovy
new file mode 100644
index 0000000..80a734b
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppOptionsFileArgsWriterTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriter
+import org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriterTest
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+
+class VisualCppOptionsFileArgsWriterTest extends OptionsFileArgsWriterTest {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    OptionsFileArgsWriter getArgsWriter() {
+        new VisualCppOptionsFileArgsWriter(tmpDirProvider.getTestDirectory())
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtilTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtilTest.groovy
new file mode 100644
index 0000000..b8aaa0b
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileGeneratorUtilTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class VisualCppPCHSourceFileGeneratorUtilTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+
+    def "can generate a source file for a pre-compiled header" () {
+        given:
+        def tempDir = tmpDirProvider.createDir("temp")
+        def pchSourceDir = tempDir.createDir("pchGeneratedSource")
+        def headerDir = tmpDirProvider.createDir("headers")
+        def sourceFile = headerDir.createFile("test.h")
+        def spec = Mock(type) {
+            getTempDir() >> tempDir
+        }
+
+        when:
+        def generated = VisualCppPCHSourceFileGeneratorUtil.generatePCHSourceFile(spec, sourceFile)
+
+        then:
+        generated.name == "test.${extension}"
+        generated.parentFile == pchSourceDir
+        generated.text == "#include \"test.h\""
+        pchSourceDir.assertContainsDescendants("test.h", "test.${extension}")
+
+        where:
+        type           | extension
+        CPCHCompileSpec   | "c"
+        CppPCHCompileSpec | "cpp"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformerTest.groovy
new file mode 100644
index 0000000..fe8de86
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPCHSourceFileTransformerTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class VisualCppPCHSourceFileTransformerTest extends Specification {
+    @Rule final TestNameTestDirectoryProvider tmpDirProvider = new TestNameTestDirectoryProvider()
+    def tempDir = tmpDirProvider.createDir("temp")
+    def pchSourceDir = tempDir.createDir("pchGeneratedSource")
+    def headerDir = tmpDirProvider.createDir("headers")
+    def sourceFile = headerDir.createFile("test.h")
+    VisualCppPCHSourceFileTransformer<CPCHCompileSpec> transformer = new VisualCppPCHSourceFileTransformer<CPCHCompileSpec>()
+
+    def "transforms pre-compiled header spec to contain generated source files" () {
+        def spec = Mock(CPCHCompileSpec) {
+            getTempDir() >> tempDir
+            getSourceFiles() >> [ sourceFile ]
+        }
+
+        when:
+        transformer.transform(spec)
+
+        then:
+        spec.setSourceFiles(_) >> { args ->
+            def sourceFiles = args[0]
+            assert sourceFiles.size() == 1
+            assert sourceFiles[0].name == "test.c"
+            assert sourceFiles[0].parentFile == pchSourceDir
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProviderTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProviderTest.groovy
new file mode 100644
index 0000000..7d9207a
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppPlatformToolProviderTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.platform.internal.OperatingSystemInternal
+import org.gradle.process.internal.ExecActionFactory
+import spock.lang.Specification
+
+class VisualCppPlatformToolProviderTest extends Specification {
+    def "windows shared link file names end with lib"() {
+        given:
+        def operatingSystem = Mock(OperatingSystemInternal)
+        VisualCppPlatformToolProvider toolProvider = new VisualCppPlatformToolProvider(Mock(BuildOperationProcessor), operatingSystem, [:], Mock(VisualCppInstall), Mock(WindowsSdk), Mock(NativePlatformInternal), Mock(ExecActionFactory) )
+
+        when:
+        operatingSystem.getInternalOs() >> OperatingSystem.WINDOWS
+        and:
+        def actual = toolProvider.getSharedLibraryLinkFileName("sharedLibrary")
+
+        then:
+        actual == "sharedLibrary.lib"
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy
new file mode 100644
index 0000000..13d45d4
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/VisualCppToolChainTest.groovy
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.api.Action
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.operations.BuildOperationProcessor
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.text.TreeFormatter
+import org.gradle.nativeplatform.platform.internal.NativePlatformInternal
+import org.gradle.nativeplatform.toolchain.VisualCppPlatformToolChain
+import org.gradle.platform.base.internal.toolchain.ToolChainAvailability
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult
+import org.gradle.process.internal.ExecActionFactory
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class VisualCppToolChainTest extends Specification {
+    TestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+    final FileResolver fileResolver = Mock(FileResolver)
+    final ExecActionFactory execActionFactory = Mock(ExecActionFactory)
+    final BuildOperationProcessor buildOperationProcessor = Stub(BuildOperationProcessor)
+    final VisualStudioLocator.SearchResult visualStudioLookup = Stub(VisualStudioLocator.SearchResult)
+    final WindowsSdkLocator.SearchResult windowsSdkLookup = Stub(WindowsSdkLocator.SearchResult)
+    final Instantiator instantiator = DirectInstantiator.INSTANCE
+    VisualCppToolChain toolChain
+
+    final VisualStudioLocator visualStudioLocator = Stub(VisualStudioLocator) {
+        locateVisualStudioInstalls(_) >> visualStudioLookup
+    }
+    final WindowsSdkLocator windowsSdkLocator = Stub(WindowsSdkLocator) {
+        locateWindowsSdks(_) >> windowsSdkLookup
+    }
+    final OperatingSystem operatingSystem = Stub(OperatingSystem) {
+        isWindows() >> true
+    }
+
+    def setup() {
+        toolChain = new VisualCppToolChain("visualCpp", buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator, instantiator)
+    }
+
+    def "installs an unavailable tool chain when not windows"() {
+        given:
+        def operatingSystem = Stub(OperatingSystem)
+        operatingSystem.isWindows() >> false
+        def toolChain = new VisualCppToolChain("visualCpp", buildOperationProcessor, operatingSystem, fileResolver, execActionFactory, visualStudioLocator, windowsSdkLocator, instantiator)
+
+        when:
+        def availability = new ToolChainAvailability()
+        toolChain.checkAvailable(availability)
+
+        then:
+        !availability.available
+        availability.unavailableMessage == 'Visual Studio is not available on this operating system.'
+    }
+
+    def "is not available when visual studio installation cannot be located"() {
+        when:
+        visualStudioLookup.available >> false
+        visualStudioLookup.explain(_) >> { TreeVisitor<String> visitor -> visitor.node("vs install not found anywhere") }
+        windowsSdkLookup.available >> false
+
+        and:
+        def result = toolChain.select(Stub(NativePlatformInternal))
+
+        then:
+        !result.available
+        getMessage(result) == "vs install not found anywhere"
+    }
+
+    def "is not available when windows SDK cannot be located"() {
+        when:
+        visualStudioLookup.available >> true
+
+        windowsSdkLookup.available >> false
+        windowsSdkLookup.explain(_) >> { TreeVisitor<String> visitor -> visitor.node("sdk not found anywhere") }
+
+        and:
+        def result = toolChain.select(Stub(NativePlatformInternal))
+
+        then:
+        !result.available
+        getMessage(result) == "sdk not found anywhere"
+    }
+
+    def "is not available when visual studio installation and windows SDK can be located and visual studio install does not support target platform"() {
+        when:
+        def visualStudio = Stub(VisualStudioInstall)
+        def visualCpp = Stub(VisualCppInstall)
+        def platform = Stub(NativePlatformInternal) { getName() >> 'platform' }
+        visualStudioLookup.available >> true
+        windowsSdkLookup.available >> true
+        visualStudioLookup.visualStudio >> visualStudio
+        visualStudioLookup.visualStudio >> Stub(VisualStudioInstall)
+        visualStudio.visualCpp >> visualCpp
+        visualCpp.isSupportedPlatform(platform) >> false
+
+        and:
+        def result = toolChain.select(platform)
+
+        then:
+        !result.available
+        getMessage(result) == "Don't know how to build for platform 'platform'."
+    }
+
+    def "is available when visual studio installation and windows SDK can be located and visual studio install supports target platform"() {
+        when:
+        def visualStudio = Stub(VisualStudioInstall)
+        def visualCpp = Stub(VisualCppInstall)
+        def platform = Stub(NativePlatformInternal)
+        visualStudioLookup.available >> true
+        windowsSdkLookup.available >> true
+        visualStudioLookup.visualStudio >> visualStudio
+        visualStudioLookup.visualStudio >> Stub(VisualStudioInstall)
+        visualStudio.visualCpp >> visualCpp
+        visualCpp.isSupportedPlatform(platform) >> true
+
+        and:
+        def platformToolChain = toolChain.select(platform)
+
+        then:
+        platformToolChain.available
+    }
+
+    def "uses provided installDir and windowsSdkDir for location"() {
+        when:
+        toolChain.installDir = "install-dir"
+        toolChain.windowsSdkDir = "windows-sdk-dir"
+
+        and:
+        fileResolver.resolve("install-dir") >> file("vs")
+        visualStudioLocator.locateVisualStudioInstalls(file("vs")) >> visualStudioLookup
+        visualStudioLookup.available >> true
+
+        and:
+        fileResolver.resolve("windows-sdk-dir") >> file("win-sdk")
+        windowsSdkLocator.locateWindowsSdks(file("win-sdk")) >> windowsSdkLookup
+        windowsSdkLookup.available >> true
+
+        and:
+        0 * _._
+
+        then:
+        def availability = new ToolChainAvailability()
+        toolChain.checkAvailable(availability);
+        availability.available
+    }
+
+    def "resolves install directory"() {
+        when:
+        toolChain.installDir = "The Path"
+
+        then:
+        fileResolver.resolve("The Path") >> file("one")
+
+        and:
+        toolChain.installDir == file("one")
+    }
+
+    def "resolves windows sdk directory"() {
+        when:
+        toolChain.windowsSdkDir = "The Path"
+
+        then:
+        fileResolver.resolve("The Path") >> file("one")
+
+        and:
+        toolChain.windowsSdkDir == file("one")
+    }
+
+    def "provided action can configure platform tool chain"() {
+        given:
+        def platform = Stub(NativePlatformInternal)
+        def visualStudio = Stub(VisualStudioInstall)
+        def visualCpp = Stub(VisualCppInstall)
+        visualStudioLookup.available >> true
+        windowsSdkLookup.available >> true
+        visualStudioLookup.visualStudio >> visualStudio
+        visualStudioLookup.visualStudio >> Stub(VisualStudioInstall)
+        visualStudio.visualCpp >> visualCpp
+        visualCpp.isSupportedPlatform(platform) >> true
+
+        def action = Mock(Action)
+        toolChain.eachPlatform(action)
+
+        when:
+        toolChain.select(platform)
+
+        then:
+        1 * action.execute(_) >> { VisualCppPlatformToolChain platformToolChain ->
+            assert platformToolChain.platform == platform
+            assert platformToolChain.assembler
+            assert platformToolChain.cCompiler
+            assert platformToolChain.cppCompiler
+            assert platformToolChain.rcCompiler
+            assert platformToolChain.linker
+            assert platformToolChain.staticLibArchiver
+        }
+    }
+
+    def file(String name) {
+        testDirectoryProvider.testDirectory.file(name)
+    }
+
+    def createFile(String name) {
+        file(name).createFile()
+    }
+
+    def getMessage(ToolSearchResult result) {
+        def formatter = new TreeFormatter()
+        result.explain(formatter)
+        return formatter.toString()
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompilerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompilerTest.groovy
new file mode 100644
index 0000000..ef0a2c9
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/msvcpp/WindowsResourceCompilerTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.msvcpp
+
+import org.gradle.internal.Transformers
+import org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext
+import org.gradle.nativeplatform.toolchain.internal.NativeCompiler
+import org.gradle.nativeplatform.toolchain.internal.compilespec.WindowsResourceCompileSpec
+
+class WindowsResourceCompilerTest extends VisualCppNativeCompilerTest {
+
+    @Override
+    protected NativeCompiler getCompiler(CommandLineToolContext invocationContext, String objectFileExtension, boolean useCommandFile) {
+        new WindowsResourceCompiler(buildOperationProcessor, commandLineTool, invocationContext, Transformers.noOpTransformer(), objectFileExtension, useCommandFile)
+    }
+
+    @Override
+    protected Class<WindowsResourceCompileSpec> getCompileSpecType() {
+        WindowsResourceCompileSpec
+    }
+
+    @Override
+    protected List<String> getCompilerSpecificArguments(File includeDir) {
+        ['/r', '/nologo', '/Dfoo=bar', '/Dempty', '-firstArg', '-secondArg',
+         '/I' + includeDir.absoluteFile.toString()]
+    }
+
+    def "check that position sensitive arguments are in the right order"() {
+        given:
+        def genericArgs = [ '/nologo' ]
+        def sourceArgs = [ 'path/to/source.rc' ]
+        def outputArgs = [ 'path/to/output.res' ]
+        def compiler = getCompiler()
+        expect:
+        compiler.buildPerFileArgs(genericArgs, sourceArgs, outputArgs, []) as List == [ '/nologo', 'path/to/output.res', 'path/to/source.rc' ]
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPathTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPathTest.groovy
new file mode 100644
index 0000000..2f1c711
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/tools/ToolSearchPathTest.groovy
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.toolchain.internal.tools
+
+import org.gradle.api.GradleException
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.toolchain.internal.ToolType
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TreeVisitor
+import org.junit.Rule
+import spock.lang.Specification
+
+class ToolSearchPathTest extends Specification {
+    @Rule def TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    def os = Stub(OperatingSystem)
+    def registry = new ToolSearchPath(os)
+
+    def "finds executable in system path"() {
+        def file = tmpDir.createFile("cc.bin")
+
+        given:
+        os.getExecutableName("cc") >> "cc.bin"
+        os.path >> [file.parentFile]
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "finds executable in provided path"() {
+        def file = tmpDir.createFile("cc.bin")
+
+        given:
+        os.getExecutableName("cc") >> "cc.bin"
+        registry.setPath([file.parentFile])
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "does not use path when executable name contains a file separator"() {
+        def file = tmpDir.createFile("cc.bin")
+        def base = tmpDir.createFile("cc")
+
+        given:
+        os.getExecutableName(base.absolutePath) >> file.absolutePath
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, base.absolutePath)
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "resolves cygwin symlinks on Windows"() {
+        def file = tmpDir.createFile("cc.bin")
+        def symlink = tmpDir.file("cc")
+
+        given:
+        symlink.setText("!<symlink>cc.bin\u0000", "utf-8")
+        os.path >> [symlink.parentFile]
+        os.windows >> true
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "ignores files that do not look like cygwin symlinks"() {
+        def candidate1 = tmpDir.createFile("dir1/cc")
+        def candidate2 = tmpDir.createFile("dir2/cc")
+        def candidate3 = tmpDir.createFile("dir3/cc")
+        def file = tmpDir.createFile("dir4/cc.bin")
+
+        given:
+        candidate1.setText("!<symlink>", "utf-8")
+        candidate2.setText("!<symlink:abcd.bin", "utf-8")
+        candidate3.setText("")
+        os.getExecutableName("cc") >> "cc.bin"
+        os.path >> [candidate1.parentFile, candidate2.parentFile, candidate3.parentFile, file.parentFile]
+        os.windows >> true
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "returns first match found in path"() {
+        def symlink = tmpDir.createFile("dir1/cc")
+        def file = tmpDir.createFile("dir2/cc.bin")
+        def ignored = tmpDir.createFile("dir3/cc.bin")
+
+        given:
+        symlink.setText("!<symlink>../dir2/cc.bin\u0000", "utf-8")
+        os.getExecutableName("cc") >> "cc.bin"
+        os.path >> [symlink.parentFile, ignored.parentFile]
+        os.windows >> true
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        result.available
+        result.tool == file
+    }
+
+    def "executable is unavailable when not found in path"() {
+        def visitor = Mock(TreeVisitor)
+        def dir1 = tmpDir.createDir("some-dir")
+        def dir2 = tmpDir.createDir("some-dir-2")
+
+        given:
+        os.getExecutableName("cc") >> "cc.bin"
+        registry.setPath([dir1, dir2])
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not find C compiler 'cc'. Searched in")
+        1 * visitor.startChildren()
+        1 * visitor.node(dir1.toString())
+        1 * visitor.node(dir2.toString())
+        1 * visitor.endChildren()
+        0 * visitor._
+    }
+
+    def "executable is unavailable when not found in system path"() {
+        def visitor = Mock(TreeVisitor)
+
+        given:
+        os.getExecutableName("cc") >> "cc.bin"
+        os.path >> []
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        !result.available
+
+        when:
+        result.explain(visitor)
+
+        then:
+        1 * visitor.node("Could not find C compiler 'cc' in system path.")
+        0 * visitor._
+    }
+
+    def "cannot use an unavailable tool"() {
+        given:
+        os.findInPath("cc") >> null
+
+        when:
+        def result = registry.locate(ToolType.C_COMPILER, "cc")
+
+        then:
+        !result.available
+
+        when:
+        result.getTool()
+
+        then:
+        GradleException e = thrown()
+        e.message == "Could not find C compiler 'cc' in system path."
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPluginTest.groovy
new file mode 100644
index 0000000..27dacf0
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/ClangCompilerPluginTest.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.nativeplatform.toolchain.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.Clang
+import org.gradle.nativeplatform.toolchain.internal.clang.ClangToolChain
+
+class ClangCompilerPluginTest extends NativeToolChainPluginTest {
+
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        ClangCompilerPlugin
+    }
+
+    @Override
+    Class<? extends NativeToolChain> getToolchainClass() {
+        Clang
+    }
+
+    @Override
+    String getToolchainName() {
+        "clang"
+    }
+
+    def "makes a Clang tool chain available"() {
+        when:
+        register()
+
+        then:
+        toolchain instanceof ClangToolChain
+        toolchain.displayName == "Tool chain 'clang' (Clang)"
+    }
+
+    def "registers default Clang tool chain"() {
+        when:
+        addDefaultToolchain()
+
+        then:
+        toolchain instanceof ClangToolChain
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPluginTest.groovy
new file mode 100644
index 0000000..f812e4e
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/GccCompilerPluginTest.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.nativeplatform.toolchain.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.Gcc
+import org.gradle.nativeplatform.toolchain.internal.gcc.GccToolChain
+import org.gradle.util.TestUtil
+
+class GccCompilerPluginTest extends NativeToolChainPluginTest {
+    def project = TestUtil.createRootProject()
+
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        GccCompilerPlugin
+    }
+
+    @Override
+    Class<? extends NativeToolChain> getToolchainClass() {
+        Gcc
+    }
+
+    @Override
+    String getToolchainName() {
+        "gcc"
+    }
+
+    def "makes a Gcc tool chain available"() {
+        when:
+        register()
+
+        then:
+        toolchain instanceof GccToolChain
+        toolchain.displayName == "Tool chain 'gcc' (GNU GCC)"
+    }
+
+    def "registers default Gcc tool chain"() {
+        when:
+        addDefaultToolchain()
+
+        then:
+        toolchain instanceof GccToolChain
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy
new file mode 100644
index 0000000..b068eac
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/MicrosoftVisualCppPluginTest.groovy
@@ -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.nativeplatform.toolchain.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.VisualCpp
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+
+class MicrosoftVisualCppPluginTest extends NativeToolChainPluginTest {
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider()
+
+    @Override
+    Class<? extends Plugin> getPluginClass() {
+        MicrosoftVisualCppPlugin
+    }
+
+    @Override
+    Class<? extends NativeToolChain> getToolchainClass() {
+        VisualCpp
+    }
+
+    @Override
+    String getToolchainName() {
+        VisualCppToolChain.DEFAULT_NAME
+    }
+
+    def "makes a VisualCpp tool chain available"() {
+        when:
+        register()
+
+        then:
+        toolchain instanceof VisualCppToolChain
+    }
+
+    def "registers default VisualCpp tool chain"() {
+        when:
+        addDefaultToolchain()
+
+        then:
+        toolchain instanceof VisualCppToolChain
+    }
+
+    def file(String name) {
+        testDirectoryProvider.testDirectory.file(name)
+    }
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/NativeToolChainPluginTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/NativeToolChainPluginTest.groovy
new file mode 100644
index 0000000..5754d77
--- /dev/null
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/plugins/NativeToolChainPluginTest.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.nativeplatform.toolchain.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.plugins.ExtraPropertiesExtension
+import org.gradle.model.internal.fixture.ModelRegistryHelper
+import org.gradle.nativeplatform.toolchain.NativeToolChain
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal
+import org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+abstract class NativeToolChainPluginTest extends Specification {
+
+    def project = TestUtil.createRootProject()
+    def modelRegistryHelper = new ModelRegistryHelper(project)
+
+    def setup() {
+        project.pluginManager.apply(getPluginClass())
+    }
+
+    abstract Class<? extends Plugin> getPluginClass()
+
+    abstract Class<? extends NativeToolChain> getToolchainClass()
+
+    String getToolchainName() {
+        "toolchain"
+    }
+
+    NativeToolChainInternal getToolchain() {
+        modelRegistryHelper.get("toolChains", NativeToolChainRegistryInternal).getByName(getToolchainName()) as NativeToolChainInternal
+    }
+
+    void register() {
+        modelRegistryHelper.mutate(NativeToolChainRegistry) {
+            it.create(getToolchainName(), getToolchainClass())
+        }
+    }
+
+    void addDefaultToolchain() {
+        modelRegistryHelper.mutate(NativeToolChainRegistryInternal) {
+            it.addDefaultToolChains()
+        }
+    }
+
+    def "tool chain is extended"() {
+        when:
+        register()
+
+        then:
+        with(toolchain) {
+            it instanceof ExtensionAware
+            it.ext instanceof ExtraPropertiesExtension
+        }
+    }
+}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/FiltersFile.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/FiltersFile.groovy
similarity index 100%
rename from subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/FiltersFile.groovy
rename to subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/FiltersFile.groovy
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy
new file mode 100644
index 0000000..65d8167
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/ProjectFile.groovy
@@ -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.ide.visualstudio.fixtures
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.TextUtil
+
+class ProjectFile {
+    String name
+    TestFile projectFile
+    Node projectXml
+
+    ProjectFile(TestFile projectFile) {
+        assert projectFile.exists()
+        this.projectFile = projectFile
+        this.name = projectFile.name.replace(".vcxproj", "")
+        this.projectXml = new XmlParser().parse(projectFile)
+    }
+
+    public Map<String, Configuration> getProjectConfigurations() {
+        def configs = itemGroup("ProjectConfigurations").collect {
+            new Configuration(it.Configuration[0].text(), it.Platform[0].text())
+        }
+        return configs.collectEntries {
+            [it.name, it]
+        }
+    }
+
+    public String getProjectGuid() {
+        return globals.ProjectGUID[0].text()
+    }
+
+    public Node getGlobals() {
+        return projectXml.PropertyGroup.find({it.'@Label' == 'Globals'}) as Node
+    }
+
+    public List<String> getSourceFiles() {
+        def sources = itemGroup('Sources').ClCompile
+        return normalise(sources*.'@Include')
+    }
+
+    public List<String> getResourceFiles() {
+        def sources = itemGroup('References').ResourceCompile
+        return normalise(sources*.'@Include')
+    }
+
+    public List<String> getHeaderFiles() {
+        def sources = itemGroup('Headers').ClInclude
+        return normalise(sources*.'@Include')
+    }
+
+    private static List<String> normalise(List<String> files) {
+        return files.collect({ TextUtil.normaliseFileSeparators(it)}).sort()
+    }
+
+    private Node itemGroup(String label) {
+        return projectXml.ItemGroup.find({it.'@Label' == label}) as Node
+    }
+
+    class Configuration {
+        String name
+        String platformName
+
+        Configuration(String name, String platformName) {
+            this.name = name
+            this.platformName = platformName
+        }
+
+        ProjectFile getProject() {
+            return ProjectFile.this
+        }
+
+        String getMacros() {
+            buildConfiguration.NMakePreprocessorDefinitions[0].text()
+        }
+
+        String getIncludePath() {
+            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeIncludeSearchPath[0].text())
+        }
+
+        String getBuildCommand() {
+            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeBuildCommandLine[0].text())
+        }
+
+        String getOutputFile() {
+            TextUtil.normaliseFileSeparators(buildConfiguration.NMakeOutput[0].text())
+        }
+
+        private Node getBuildConfiguration() {
+            projectXml.PropertyGroup.find({ it.'@Label' == 'NMakeConfiguration' && it.'@Condition' == condition}) as Node
+        }
+
+        private String getCondition() {
+            "'\$(Configuration)|\$(Platform)'=='${name}|${platformName}'"
+        }
+    }
+
+    void assertHasComponentSources(TestNativeComponent component, String basePath) {
+        assert sourceFiles == ['build.gradle'] + sourceFiles(component.sourceFiles, basePath)
+        assert headerFiles == sourceFiles(component.headerFiles, basePath)
+    }
+
+    void assertHasComponentSources(TestNativeComponent component, String basePath, TestNativeComponent component2, String basePath2) {
+        assert sourceFiles == ['build.gradle'] + sourceFiles(component.sourceFiles, basePath) + sourceFiles(component2.sourceFiles, basePath2)
+        assert headerFiles == sourceFiles(component.headerFiles, basePath) + sourceFiles(component2.headerFiles, basePath2)
+    }
+
+    private static List<String> sourceFiles(List<SourceFile> files, String path) {
+        return files*.withPath(path).sort()
+    }
+
+}
diff --git a/subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/SolutionFile.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/SolutionFile.groovy
similarity index 100%
rename from subprojects/cpp/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/SolutionFile.groovy
rename to subprojects/platform-native/src/testFixtures/groovy/org/gradle/ide/visualstudio/fixtures/SolutionFile.groovy
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
new file mode 100755
index 0000000..6cb71a0
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
@@ -0,0 +1,85 @@
+/*
+ * 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.nativeplatform.fixtures
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.hash.HashUtil
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.internal.CompilerOutputFileNamingScheme
+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
+    File initScript
+
+    def setup() {
+        initScript = file("init.gradle") << """
+allprojects { p ->
+    apply plugin: ${toolChain.pluginClass}
+
+    model {
+          toolChains {
+            ${toolChain.buildScriptConfig}
+          }
+    }
+}
+"""
+        executer.beforeExecute({
+            usingInitScript(initScript)
+        })
+    }
+
+    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) {
+        return toolChain.objectFile(file(path))
+    }
+
+    def SharedLibraryFixture sharedLibrary(Object path) {
+        return toolChain.sharedLibrary(file(path))
+    }
+
+    def StaticLibraryFixture staticLibrary(Object path) {
+        return toolChain.staticLibrary(file(path))
+    }
+
+    def NativeBinaryFixture resourceOnlyLibrary(Object path) {
+        return toolChain.resourceOnlyLibrary(file(path))
+    }
+
+    def objectFileFor(TestFile sourceFile, String rootObjectFilesDir = "build/objs/mainExecutable/main${sourceType}") {
+        File objectFile = new CompilerOutputFileNamingScheme()
+                        .withObjectFileNameSuffix(OperatingSystem.current().isWindows() ? ".obj" : ".o")
+                        .withOutputBaseFolder(file(rootObjectFilesDir))
+                        .map(sourceFile)
+        return file(getTestDirectory().toURI().relativize(objectFile.toURI()));
+    }
+
+    String hashFor(File inputFile){
+        HashUtil.createCompactMD5(inputFile.getAbsolutePath());
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AvailableToolChains.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AvailableToolChains.java
new file mode 100755
index 0000000..6f4a064
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AvailableToolChains.java
@@ -0,0 +1,503 @@
+/*
+ * 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.nativeplatform.fixtures;
+
+import com.google.common.base.Joiner;
+import net.rubygrapefruit.platform.SystemInfo;
+import net.rubygrapefruit.platform.WindowsRegistry;
+import org.gradle.api.Nullable;
+import org.gradle.api.internal.file.TestFiles;
+import org.gradle.internal.nativeintegration.ProcessEnvironment;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.VisualCpp;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.GccVersionDeterminer;
+import org.gradle.nativeplatform.toolchain.internal.gcc.version.GccVersionResult;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioInstall;
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioLocator;
+import org.gradle.nativeplatform.toolchain.plugins.ClangCompilerPlugin;
+import org.gradle.nativeplatform.toolchain.plugins.GccCompilerPlugin;
+import org.gradle.nativeplatform.toolchain.plugins.MicrosoftVisualCppPlugin;
+import org.gradle.process.internal.DefaultExecAction;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.process.internal.ExecActionFactory;
+import org.gradle.test.fixtures.file.TestFile;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
+import org.gradle.util.VersionNumber;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class AvailableToolChains {
+    private static List<ToolChainCandidate> toolChains;
+
+    /**
+     * Locates the tool chain that would be used as the default for the current machine, if any.
+     * @return null if there is no such tool chain.
+     */
+    @Nullable
+    public static InstalledToolChain getDefaultToolChain() {
+        for (ToolChainCandidate toolChain : getToolChains()) {
+            if (toolChain.isAvailable()) {
+                return (InstalledToolChain) toolChain;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Locates a tool chain that meets the given criteria, if any.
+     *
+     * @return null if there is no such tool chain.
+     */
+    @Nullable
+    public static ToolChainCandidate getToolChain(ToolChainRequirement requirement) {
+        for (ToolChainCandidate toolChainCandidate : getToolChains()) {
+            if (toolChainCandidate.meets(requirement)) {
+                return toolChainCandidate;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return A list of all known tool chains for this platform. Includes those tool chains that are not available on the current machine.
+     */
+    public static List<ToolChainCandidate> getToolChains() {
+        if (toolChains == null) {
+            List<ToolChainCandidate> compilers = new ArrayList<ToolChainCandidate>();
+            if (OperatingSystem.current().isWindows()) {
+                compilers.add(findVisualCpp());
+                compilers.add(findMinGW());
+                compilers.add(findCygwin());
+            } else {
+                compilers.add(findGcc());
+                compilers.add(findClang());
+            }
+            toolChains = compilers;
+        }
+        return toolChains;
+    }
+
+    static private ToolChainCandidate findClang() {
+        File compilerExe = OperatingSystem.current().findInPath("clang");
+        if (compilerExe != null) {
+            return new InstalledClang();
+        }
+        return new UnavailableToolChain("clang");
+    }
+
+    static private ToolChainCandidate findVisualCpp() {
+        // Search in the standard installation locations
+        VisualStudioLocator vsLocator = new DefaultVisualStudioLocator(OperatingSystem.current(), NativeServicesTestFixture.getInstance().get(WindowsRegistry.class), NativeServicesTestFixture.getInstance().get(SystemInfo.class));
+        VisualStudioLocator.SearchResult searchResult = vsLocator.locateVisualStudioInstalls(null);
+        if (searchResult.isAvailable()) {
+            VisualStudioInstall install = searchResult.getVisualStudio();
+            return new InstalledVisualCpp().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 InstalledWindowsGcc("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 InstalledWindowsGcc("gcc cygwin").inPath(compilerExe.getParentFile());
+        }
+
+        return new UnavailableToolChain("gcc cygwin");
+    }
+
+    static private ToolChainCandidate findGcc() {
+        GccVersionDeterminer versionDeterminer = GccVersionDeterminer.forGcc(new ExecActionFactory() {
+            public ExecAction newExecAction() {
+                return new DefaultExecAction(TestFiles.resolver());
+            }
+        });
+
+        List<File> gppCandidates = OperatingSystem.current().findAllInPath("g++");
+        for (int i = 0; i < gppCandidates.size(); i++) {
+            File candidate = gppCandidates.get(i);
+            GccVersionResult version = versionDeterminer.getGccMetaData(candidate, Collections.<String>emptyList());
+            if (version.isAvailable()) {
+                InstalledGcc gcc = new InstalledGcc("gcc");
+                if (i > 0) {
+                    // Not the first g++ in the path, needs the path variable updated
+                    gcc.inPath(candidate.getParentFile());
+                }
+                return gcc;
+            }
+        }
+
+        return new UnavailableToolChain("gcc");
+    }
+
+    public static abstract class ToolChainCandidate {
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+
+        public abstract String getDisplayName();
+
+        public abstract boolean isAvailable();
+
+        public abstract boolean meets(ToolChainRequirement requirement);
+
+        public abstract void initialiseEnvironment();
+
+        public abstract void resetEnvironment();
+
+   }
+    
+    public abstract static class InstalledToolChain extends ToolChainCandidate {
+        private static final ProcessEnvironment PROCESS_ENVIRONMENT = NativeServicesTestFixture.getInstance().get(ProcessEnvironment.class);
+        protected final List<File> pathEntries = new ArrayList<File>();
+        private final String displayName;
+        protected final String pathVarName;
+        private final String objectFileNameSuffix;
+
+        private String originalPath;
+
+        public InstalledToolChain(String displayName) {
+            this.displayName = displayName;
+            this.pathVarName = OperatingSystem.current().getPathVar();
+            this.objectFileNameSuffix = OperatingSystem.current().isWindows() ? ".obj" : ".o";
+        }
+
+        InstalledToolChain inPath(File... pathEntries) {
+            Collections.addAll(this.pathEntries, pathEntries);
+            return this;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return true;
+        }
+
+        public String getTypeDisplayName() {
+            return getDisplayName().replaceAll("\\s+\\d+(\\.\\d+)*$", "");
+        }
+
+        public abstract String getInstanceDisplayName();
+
+        public ExecutableFixture executable(Object path) {
+            return new ExecutableFixture(new TestFile(OperatingSystem.current().getExecutableName(path.toString())), this);
+        }
+
+        public TestFile objectFile(Object path) {
+            return new TestFile(path.toString() + objectFileNameSuffix);
+        }
+
+        public SharedLibraryFixture sharedLibrary(Object path) {
+            return new SharedLibraryFixture(new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
+        }
+
+        public StaticLibraryFixture staticLibrary(Object path) {
+            return new StaticLibraryFixture(new TestFile(OperatingSystem.current().getStaticLibraryName(path.toString())), this);
+        }
+
+        public NativeBinaryFixture resourceOnlyLibrary(Object path) {
+            return new NativeBinaryFixture(new TestFile(OperatingSystem.current().getSharedLibraryName(path.toString())), this);
+        }
+
+        /**
+         * Initialise the process environment so that this tool chain is visible to the default discovery mechanism that the
+         * plugin uses (eg add the compiler to the PATH).
+         */
+        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);
+            }
+        }
+
+        public void resetEnvironment() {
+            if (originalPath != null) {
+                PROCESS_ENVIRONMENT.setEnvironmentVariable(pathVarName, originalPath);
+            }
+        }
+
+        public abstract String getBuildScriptConfig();
+
+        public abstract String getImplementationClass();
+
+        public abstract String getPluginClass();
+
+        public boolean isVisualCpp() {
+            return false;
+        }
+
+        public List<File> getPathEntries() {
+            return pathEntries;
+        }
+
+        /**
+         * The environment required to execute a binary created by this toolchain.
+         */
+        public List<String> getRuntimeEnv() {
+            // Toolchains should be linking against stuff in the standard locations
+            return Collections.emptyList();
+        }
+
+        public String getId() {
+            return displayName.replaceAll("\\W", "");
+        }
+    }
+
+    public static abstract class GccCompatibleToolChain extends InstalledToolChain {
+        protected GccCompatibleToolChain(String displayName) {
+            super(displayName);
+        }
+
+        protected String find(String tool) {
+            if (getPathEntries().isEmpty()) {
+                return tool;
+            }
+            return new File(getPathEntries().get(0), tool).getAbsolutePath();
+        }
+
+        public String getLinker() {
+            return getCCompiler();
+        }
+
+        public String getStaticLibArchiver() {
+            return find("ar");
+        }
+
+        public abstract String getCCompiler();
+    }
+
+    public static class InstalledGcc extends GccCompatibleToolChain {
+        public InstalledGcc(String name) {
+            super(name);
+        }
+
+        @Override
+        public boolean meets(ToolChainRequirement requirement) {
+            return requirement == ToolChainRequirement.Gcc || requirement == ToolChainRequirement.GccCompatible || requirement == ToolChainRequirement.Available;
+        }
+
+        @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(), pathEntry.toURI());
+            }
+            return config;
+        }
+
+        @Override
+        public String getCCompiler() {
+            return find("gcc");
+        }
+
+        public String getInstanceDisplayName() {
+            return String.format("Tool chain '%s' (GNU GCC)", getId());
+        }
+
+        public String getImplementationClass() {
+            return Gcc.class.getSimpleName();
+        }
+
+        @Override
+        public String getPluginClass() {
+            return GccCompilerPlugin.class.getSimpleName();
+        }
+    }
+
+    public static class InstalledWindowsGcc extends InstalledGcc {
+        public InstalledWindowsGcc(String name) {
+            super(name);
+        }
+
+        /**
+         * The environment required to execute a binary created by this toolchain.
+         */
+        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 static class InstalledVisualCpp extends InstalledToolChain {
+        private VersionNumber version;
+        private File installDir;
+
+        public InstalledVisualCpp() {
+            super("visual c++");
+        }
+
+        @Override
+        public String getId() {
+            return "visualCpp";
+        }
+
+        public InstalledVisualCpp withInstall(VisualStudioInstall install) {
+            DefaultNativePlatform targetPlatform = new DefaultNativePlatform("default");
+            installDir = install.getVisualStudioDir();
+            version = install.getVersion();
+            pathEntries.addAll(install.getVisualCpp().getPath(targetPlatform));
+            return this;
+        }
+
+        @Override
+        public boolean meets(ToolChainRequirement requirement) {
+            switch (requirement) {
+                case Available:
+                case VisualCpp:
+                    return true;
+                case VisualCpp2013:
+                    return version.compareTo(VersionNumber.parse("12.0")) >= 0;
+                default:
+                    return false;
+            }
+        }
+
+        @Override
+        public String getBuildScriptConfig() {
+            String config = String.format("%s(%s)\n", getId(), getImplementationClass());
+            if (installDir != null) {
+                config += String.format("%s.installDir = file('%s')", getId(), installDir.toURI());
+            }
+            return config;
+        }
+
+        public String getImplementationClass() {
+            return VisualCpp.class.getSimpleName();
+        }
+
+        public String getInstanceDisplayName() {
+            return String.format("Tool chain '%s' (Visual Studio)", getId());
+        }
+
+        @Override
+        public String getPluginClass() {
+            return MicrosoftVisualCppPlugin.class.getSimpleName();
+        }
+
+        public boolean isVisualCpp() {
+            return true;
+        }
+
+        public VersionNumber getVersion() {
+            return version;
+        }
+
+        @Override
+        public TestFile objectFile(Object path) {
+            return new TestFile(path.toString() + ".obj");
+        }
+    }
+
+    public static class InstalledClang extends GccCompatibleToolChain {
+        public InstalledClang() {
+            super("clang");
+        }
+
+        @Override
+        public boolean meets(ToolChainRequirement requirement) {
+            return requirement == ToolChainRequirement.Clang || requirement == ToolChainRequirement.GccCompatible || requirement == ToolChainRequirement.Available;
+        }
+
+        @Override
+        public String getBuildScriptConfig() {
+            return "clang(Clang)";
+        }
+
+        @Override
+        public String getCCompiler() {
+            return find("clang");
+        }
+
+        public String getInstanceDisplayName() {
+            return String.format("Tool chain '%s' (Clang)", getId());
+        }
+
+        @Override
+        public String getImplementationClass() {
+            return Clang.class.getSimpleName();
+        }
+
+        @Override
+        public String getPluginClass() {
+            return ClangCompilerPlugin.class.getSimpleName();
+        }
+    }
+
+    public static class UnavailableToolChain extends ToolChainCandidate {
+        private final String name;
+
+        public UnavailableToolChain(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public boolean meets(ToolChainRequirement requirement) {
+            return false;
+        }
+
+        @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/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ExecutableFixture.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ExecutableFixture.groovy
new file mode 100644
index 0000000..9c06311
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ExecutableFixture.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.nativeplatform.fixtures
+
+import org.gradle.test.fixtures.file.ExecOutput
+import org.gradle.test.fixtures.file.TestFile
+
+class ExecutableFixture extends NativeBinaryFixture {
+    ExecutableFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
+        super(file, toolChain)
+    }
+
+    public ExecOutput exec(Object... args) {
+        assertExists()
+        return file.execute(args as List, toolChain.runtimeEnv)
+    }
+
+    public List<String> listLinkedLibraries() {
+        return binaryInfo.listLinkedLibraries()
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativeBinaryFixture.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativeBinaryFixture.groovy
new file mode 100644
index 0000000..a7a408d
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativeBinaryFixture.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.nativeplatform.fixtures
+
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.binaryinfo.BinaryInfo
+import org.gradle.nativeplatform.fixtures.binaryinfo.DumpbinBinaryInfo
+import org.gradle.nativeplatform.fixtures.binaryinfo.OtoolBinaryInfo
+import org.gradle.nativeplatform.fixtures.binaryinfo.ReadelfBinaryInfo
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestFile.Snapshot
+
+class NativeBinaryFixture {
+    final TestFile file
+    protected final AvailableToolChains.InstalledToolChain toolChain
+
+    NativeBinaryFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
+        this.file = file
+        this.toolChain = toolChain
+    }
+
+    URI toURI() {
+        file.toURI()
+    }
+
+    Snapshot snapshot() {
+        file.snapshot()
+    }
+
+    void assertHasChangedSince(Snapshot snapshot) {
+        file.assertContentsHaveChangedSince(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")
+    }
+
+    boolean assertExistsAndDelete() {
+        assertExists()
+        file.delete()
+    }
+
+    BinaryInfo getBinaryInfo() {
+        file.assertExists()
+        if (OperatingSystem.current().isMacOsX()) {
+            return new OtoolBinaryInfo(file);
+        }
+        if (OperatingSystem.current().isWindows()) {
+            return new DumpbinBinaryInfo(file);
+        }
+        return new ReadelfBinaryInfo(file);
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativeInstallationFixture.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativeInstallationFixture.groovy
new file mode 100644
index 0000000..2f275a3
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/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.nativeplatform.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/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativePlatformsTestFixture.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativePlatformsTestFixture.java
new file mode 100644
index 0000000..6714386
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/NativePlatformsTestFixture.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures;
+
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform;
+import org.gradle.nativeplatform.platform.internal.NativePlatforms;
+import org.gradle.testfixtures.internal.NativeServicesTestFixture;
+
+import java.util.Set;
+
+public class NativePlatformsTestFixture {
+    static {
+        NativeServicesTestFixture.initialize();
+    }
+
+    public static Set<DefaultNativePlatform> defaultPlatformDefinitions() {
+        return NativePlatforms.defaultPlatformDefinitions();
+    }
+
+    public static String getDefaultPlatformName() {
+        return NativePlatforms.getDefaultPlatformName();
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChain.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChain.groovy
new file mode 100644
index 0000000..e48175a
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChain.groovy
@@ -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.nativeplatform.fixtures
+
+import org.spockframework.runtime.extension.ExtensionAnnotation
+
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target([ElementType.METHOD, ElementType.TYPE])
+ at ExtensionAnnotation(RequiresInstalledToolChainExtension.class)
+public @interface RequiresInstalledToolChain {
+    ToolChainRequirement value() default ToolChainRequirement.Available;
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChainExtension.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChainExtension.groovy
new file mode 100644
index 0000000..4d56afa
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/RequiresInstalledToolChainExtension.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.fixtures
+
+import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
+import org.spockframework.runtime.model.FeatureInfo
+import org.spockframework.runtime.model.SpecInfo
+
+class RequiresInstalledToolChainExtension extends AbstractAnnotationDrivenExtension<RequiresInstalledToolChain> {
+    @Override
+    void visitSpecAnnotation(RequiresInstalledToolChain annotation, SpecInfo spec) {
+        final available = isToolChainAvailable(annotation)
+        spec.skipped |= !available
+    }
+
+    @Override
+    void visitFeatureAnnotation(RequiresInstalledToolChain annotation, FeatureInfo feature) {
+        final available = isToolChainAvailable(annotation)
+        feature.skipped |= !available
+    }
+
+    private static boolean isToolChainAvailable(RequiresInstalledToolChain annotation) {
+        def requiredToolChain = AvailableToolChains.getToolChain(annotation.value())
+        return requiredToolChain != null
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SharedLibraryFixture.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SharedLibraryFixture.groovy
new file mode 100644
index 0000000..80ec654
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SharedLibraryFixture.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.nativeplatform.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+
+class SharedLibraryFixture extends NativeBinaryFixture {
+    SharedLibraryFixture(TestFile file, AvailableToolChains.InstalledToolChain 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()
+        }
+    }
+
+    String getSoName() {
+        return binaryInfo.soName
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SingleToolChainTestRunner.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SingleToolChainTestRunner.java
new file mode 100755
index 0000000..fc36890
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/SingleToolChainTestRunner.java
@@ -0,0 +1,89 @@
+/*
+ * 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.nativeplatform.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();
+        if (enableAllToolChains) {
+            for (AvailableToolChains.ToolChainCandidate toolChain : toolChains) {
+                if (!toolChain.isAvailable()) {
+                    throw new RuntimeException(String.format("Tool chain %s is not available.", toolChain.getDisplayName()));
+                }
+                add(new ToolChainExecution(toolChain, true));
+            }
+        } else {
+            boolean hasEnabled = false;
+            for (AvailableToolChains.ToolChainCandidate toolChain : toolChains) {
+                if (!hasEnabled && toolChain.isAvailable()) {
+                    add(new ToolChainExecution(toolChain, true));
+                    hasEnabled = true;
+                } else {
+                    add(new ToolChainExecution(toolChain, false));
+                }
+            }
+        }
+    }
+
+    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) {
+            if (enabled) {
+                RequiresInstalledToolChain toolChainRestriction = testDetails.getAnnotation(RequiresInstalledToolChain.class);
+                return toolChainRestriction == null
+                        || toolChain.meets(toolChainRestriction.value());
+            }
+            return false;
+        }
+
+        @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()));
+            AbstractInstalledToolChainIntegrationSpec.setToolChain((AvailableToolChains.InstalledToolChain) toolChain);
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/StaticLibraryFixture.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/StaticLibraryFixture.groovy
new file mode 100644
index 0000000..2f55e58
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/StaticLibraryFixture.groovy
@@ -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.nativeplatform.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+
+class StaticLibraryFixture extends NativeBinaryFixture {
+    StaticLibraryFixture(TestFile file, AvailableToolChains.InstalledToolChain toolChain) {
+        super(file, toolChain)
+    }
+
+    List<String> listObjectFiles() {
+        getBinaryInfo().listObjectFiles()
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ToolChainRequirement.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ToolChainRequirement.java
new file mode 100644
index 0000000..62a4937
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/ToolChainRequirement.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.nativeplatform.fixtures;
+
+public enum ToolChainRequirement {
+    // Any available tool chain
+    Available,
+    // Any available Visual Studio implementation
+    VisualCpp,
+    // Any available Visual Studio >= 2013
+    VisualCpp2013,
+    // Any available GCC implementation (including mingw, cygwin, but not clang)
+    Gcc,
+    // Any available GCC compatible implementation (including mingw, cygwin, and clang)
+    GccCompatible,
+    // Any available Clang
+    Clang
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCallingMixedCAndCppHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCallingMixedCAndCppHelloWorldApp.groovy
new file mode 100644
index 0000000..63fb951
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCallingMixedCAndCppHelloWorldApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile;
+
+public class CCallingMixedCAndCppHelloWorldApp extends HelloWorldApp {
+    @Override
+    List<String> getPluginList() {
+        return ['c', 'cpp']
+    }
+
+    @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
+            }
+"""),
+        sourceFile("c", "sum.c", """
+            #include "hello.h"
+
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+""")
+    ]
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCompilerDetectingTestApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCompilerDetectingTestApp.groovy
new file mode 100644
index 0000000..de6405f
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CCompilerDetectingTestApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.AvailableToolChains.InstalledToolChain
+
+class CCompilerDetectingTestApp extends TestApp {
+    String expectedOutput(InstalledToolChain toolChain) {
+        "C ${toolChain.typeDisplayName}"
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "c-detector.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC detectCCompiler();
+        """);
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return [
+            sourceFile("c", "c-detector.c", """
+                #include <stdio.h>
+                #include "c-detector.h"
+
+                void detectCCompiler() {
+                #if !defined(__cplusplus)
+                    printf("C ");
+                #endif
+                #if defined(__clang__)
+                    printf("clang");
+                #elif defined(__GNUC__) && defined(__MINGW32__)
+                    printf("mingw");
+                #elif defined(__GNUC__) && defined(__CYGWIN__)
+                    printf("gcc cygwin");
+                #elif defined(__GNUC__)
+                    printf("gcc");
+                #elif defined(_MSC_VER)
+                    printf("visual c++");
+                #else
+                    printf("unknown");
+                #endif
+                }
+        """)
+        ]
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        return new SourceFile("c", "main.c", """
+#include <stdio.h>
+#include "c-detector.h"
+
+int main () {
+    detectCCompiler();
+    return 0;
+}
+""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CHelloWorldApp.groovy
new file mode 100644
index 0000000..4796cda
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CHelloWorldApp.groovy
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+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
+            #ifdef CUSTOM
+            char* greeting() {
+                return CUSTOM;
+            }
+            #endif
+            void DLL_FUNC sayHello() {
+                #if defined(FRENCH) || defined(CUSTOM)
+                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);
+                }
+
+                // Extra function to ensure library has different size
+                int anotherFunction() {
+                    return 1000;
+                }
+            """),
+            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"
+
+    TestNativeComponent getCunitTests() {
+        return new TestNativeComponent() {
+            List<SourceFile> sourceFiles = [
+                    sourceFile("c", "test.c", """
+#include <CUnit/Basic.h>
+#include "hello.h"
+#include "gradle_cunit_register.h"
+
+int init_test(void) {
+    return 0;
+}
+
+int clean_test(void) {
+    return 0;
+}
+
+void test_sum(void) {
+  CU_ASSERT(sum(0, 2) == 2);
+#ifndef ONE_TEST
+  CU_ASSERT(sum(0, -2) == -2);
+  CU_ASSERT(sum(2, 2) == 4);
+#endif
+}
+
+void gradle_cunit_register() {
+    CU_pSuite pSuiteMath = CU_add_suite("hello test", init_test, clean_test);
+    CU_add_test(pSuiteMath, "test_sum", test_sum);
+}
+                    """),
+            ]
+            List<SourceFile> headerFiles = [
+            ]
+
+            String testOutput = """
+Suite: hello test
+  Test: test of sum ...passed
+
+Run Summary:    Type  Total    Ran Passed Failed Inactive
+              suites      1      1    n/a      0        0
+               tests      1      1      1      0        0
+             asserts      3      3      3      0      n/a
+"""
+        };
+    }
+
+    public SourceFile getBrokenFile() {
+        return sourceFile("c", "broken.c", """'broken""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CPCHHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CPCHHelloWorldApp.groovy
new file mode 100644
index 0000000..6ef9d72
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CPCHHelloWorldApp.groovy
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class CPCHHelloWorldApp extends PCHHelloWorldApp {
+
+    @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() {
+        getLibraryHeader("")
+    }
+
+    @Override
+    SourceFile getLibraryHeader(String path) {
+        sourceFile("headers/${path}", "hello.h", """
+            #ifndef HELLO_H
+            #define 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);
+
+            #ifdef FRENCH
+            #pragma message("<==== compiling bonjour.h ====>")
+            #else
+            #pragma message("<==== compiling hello.h ====>")
+            #endif
+            #endif
+        """);
+    }
+
+    @Override
+    public TestApp getAlternate() {
+        return new TestApp() {
+            @Override
+            SourceFile getMainSource() {
+                return getAlternateMainSource()
+            }
+
+            @Override
+            SourceFile getLibraryHeader() {
+                return sourceFile("headers", "hello.h", """
+                    #ifndef HELLO_H
+                    #define 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);
+
+                    #pragma message("<==== compiling althello.h ====>")
+                    #endif
+                """);
+            }
+
+            @Override
+            List<SourceFile> getLibrarySources() {
+                return getAlternateLibrarySources()
+            }
+        }
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return getLibrarySources("")
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources(String headerPath) {
+        return [
+                sourceFile("c", "hello.c", """
+                #include "${headerPath}hello.h"
+                #include <stdio.h>
+
+                #ifdef FRENCH
+                char* greeting() {
+                    return "${HELLO_WORLD_FRENCH}";
+                }
+                #endif
+                #ifdef CUSTOM
+                char* greeting() {
+                    return CUSTOM;
+                }
+                #endif
+                void DLL_FUNC sayHello() {
+                    #if defined(FRENCH) || defined(CUSTOM)
+                    printf("%s\\n", greeting());
+                    #else
+                    printf("${HELLO_WORLD}\\n");
+                    #endif
+                    fflush(stdout);
+                }
+            """),
+                sourceFile("c", "sum.c", """
+                #include "${headerPath}hello.h"
+
+                int DLL_FUNC sum(int a, int b) {
+                    return a + b;
+                }
+            """)
+        ]
+    }
+
+    @Override
+    SourceFile getSystemHeader() {
+        return getSystemHeader("")
+    }
+
+    @Override
+    SourceFile getSystemHeader(String path) {
+        sourceFile("headers/${path}", "systemHeader.h", """
+            #ifndef SYSTEMHEADER_H
+            #define SYSTEMHEADER_H
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+            void DLL_FUNC systemCall();
+            #pragma message("<==== compiling systemHeader.h ====>")
+            #endif
+        """)
+    }
+
+    @Override
+    String getIOHeader() {
+        return "stdio.h"
+    }
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return getMainSource()
+    }
+
+    @Override
+    String getAlternateOutput() {
+        return null
+    }
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return getLibrarySources()
+    }
+
+    @Override
+    String getAlternateLibraryOutput() {
+        return null
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCallingCHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCallingCHelloWorldApp.groovy
new file mode 100644
index 0000000..b5b917a
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCallingCHelloWorldApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile;
+
+public class CppCallingCHelloWorldApp extends HelloWorldApp {
+    @Override
+    List<String> getPluginList() {
+        return ['c', 'cpp']
+    }
+
+    @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/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCompilerDetectingTestApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCompilerDetectingTestApp.groovy
new file mode 100644
index 0000000..075a7a0
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppCompilerDetectingTestApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.AvailableToolChains.InstalledToolChain
+
+class CppCompilerDetectingTestApp extends TestApp {
+    String expectedOutput(InstalledToolChain toolChain) {
+        "C++ ${toolChain.typeDisplayName}"
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "cpp-detector.h", """
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            void DLL_FUNC detectCppCompiler();
+        """);
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return [
+            sourceFile("cpp", "cpp-detector.cpp", """
+                #include <stdio.h>
+                #include "cpp-detector.h"
+
+                void detectCppCompiler() {
+                #if defined(__cplusplus)
+                    printf("C++ ");
+                #endif
+                #if defined(__clang__)
+                    printf("clang");
+                #elif defined(__GNUC__) && defined(__MINGW32__)
+                    printf("mingw");
+                #elif defined(__GNUC__) && defined(__CYGWIN__)
+                    printf("gcc cygwin");
+                #elif defined(__GNUC__)
+                    printf("gcc");
+                #elif defined(_MSC_VER)
+                    printf("visual c++");
+                #else
+                    printf("unknown");
+                #endif
+                }
+        """)
+        ]
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        return new SourceFile("cpp", "main.cpp", """
+#include <stdio.h>
+#include "cpp-detector.h"
+
+int main () {
+    detectCppCompiler();
+    return 0;
+}
+""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppHelloWorldApp.groovy
new file mode 100644
index 0000000..daff0ce
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppHelloWorldApp.groovy
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class CppHelloWorldApp extends IncrementalHelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("cpp", "main.cpp", """
+            // Simple hello world app
+            #include <iostream>
+            #include "hello.h"
+
+            int main () {
+              Greeter greeter;
+              greeter.sayHello();
+              std::cout << sum(5, 7);
+              return 0;
+            }
+        """);
+    }
+
+    SourceFile getAlternateMainSource() {
+        sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            int main () {
+              Greeter greeter;
+              greeter.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
+
+            class Greeter {
+                public:
+                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 Greeter::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 Greeter::sayHello() {
+                std::cout << "[${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}]" << std::endl;
+            }
+
+            // Extra function to ensure library has different size
+            int anotherFunction() {
+                return 1000;
+            }
+        """),
+        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"
+
+    TestNativeComponent getGoogleTestTests() {
+        return new TestNativeComponent() {
+            List<SourceFile> sourceFiles = [
+                    sourceFile("cpp", "test.cpp", """
+#include "gtest/gtest.h"
+#include "hello.h"
+
+using namespace testing;
+
+TEST(HelloTest, test_sum) {
+  ASSERT_TRUE(sum(0, 2) == 2);
+#ifndef ONE_TEST
+  ASSERT_TRUE(sum(0, -2) == -2);
+  ASSERT_TRUE(sum(2, 2) == 4);
+#endif
+}
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+                    """),
+            ]
+            List<SourceFile> headerFiles = [
+            ]
+
+            String testOutput = """
+Running main() from gtest_main.cc
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from MathSuite
+[ RUN      ] MathSuite.sum
+[       OK ] MathSuite.sum (0 ms)
+[----------] 1 test from MathSuite (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 1 test.
+"""
+        };
+    }
+
+    public SourceFile getBrokenFile() {
+        return sourceFile("cpp", "broken.cpp", """'broken""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppPCHHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppPCHHelloWorldApp.groovy
new file mode 100644
index 0000000..9789109
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/CppPCHHelloWorldApp.groovy
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class CppPCHHelloWorldApp extends PCHHelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("cpp", "main.cpp", """
+            // Simple hello world app
+            #include <iostream>
+            #include "hello.h"
+
+            int main () {
+              Greeter greeter;
+              greeter.sayHello();
+              std::cout << sum(5, 7);
+              return 0;
+            }
+        """);
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        getLibraryHeader("")
+    }
+
+    @Override
+    SourceFile getLibraryHeader(String path) {
+        return sourceFile("headers/${path}", "hello.h", """
+            #ifndef HELLO_H
+            #define HELLO_H
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            class Greeter {
+                public:
+                void DLL_FUNC sayHello();
+            };
+
+            int DLL_FUNC sum(int a, int b);
+            #ifdef FRENCH
+            #pragma message("<==== compiling bonjour.h ====>")
+            #else
+            #pragma message("<==== compiling hello.h ====>")
+            #endif
+            #endif
+        """);
+    }
+
+    @Override
+    TestApp getAlternate() {
+        return new TestApp() {
+            @Override
+            SourceFile getMainSource() {
+                return getAlternateMainSource()
+            }
+
+            @Override
+            SourceFile getLibraryHeader() {
+                return sourceFile("headers", "hello.h", """
+                    #ifndef HELLO_H
+                    #define HELLO_H
+                    #ifdef _WIN32
+                    #define DLL_FUNC __declspec(dllexport)
+                    #else
+                    #define DLL_FUNC
+                    #endif
+
+                    class Greeter {
+                        public:
+                        void DLL_FUNC sayHello();
+                    };
+
+                    int DLL_FUNC sum(int a, int b);
+                    #pragma message("<==== compiling althello.h ====>")
+                    #endif
+                """);
+            }
+
+            @Override
+            List<SourceFile> getLibrarySources() {
+                return getAlternateLibrarySources()
+            }
+        }
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return getLibrarySources("")
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources(String path) {
+        return [
+                sourceFile("cpp", "hello.cpp", """
+                    #include "${path}hello.h"
+                    #include <iostream>
+
+                    #ifdef FRENCH
+                    const char* greeting() {
+                        return "${HELLO_WORLD_FRENCH}";
+                    }
+                    #endif
+
+                    void DLL_FUNC Greeter::sayHello() {
+                        #ifdef FRENCH
+                        std::cout << greeting() << std::endl;
+                        #else
+                        std::cout << "${HELLO_WORLD}" << std::endl;
+                        #endif
+                    }
+                """),
+                sourceFile("cpp", "sum.cpp", """
+                    #include "${path}hello.h"
+
+                    int DLL_FUNC sum(int a, int b) {
+                        return a + b;
+                    }
+                """)
+        ]
+    }
+
+    @Override
+    SourceFile getSystemHeader() {
+        return getSystemHeader("")
+    }
+
+    @Override
+    SourceFile getSystemHeader(String path) {
+        sourceFile("headers/${path}", "systemHeader.h", """
+            #ifndef SYSTEMHEADER_H
+            #define SYSTEMHEADER_H
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+            void DLL_FUNC systemCall();
+            #pragma message("<==== compiling systemHeader.h ====>")
+            #endif
+        """)
+    }
+
+    @Override
+    String getIOHeader() {
+        return "iostream"
+    }
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return getMainSource()
+    }
+
+    @Override
+    String getAlternateOutput() {
+        return null
+    }
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return getLibrarySources()
+    }
+
+    @Override
+    String getAlternateLibraryOutput() {
+        return null
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.groovy
new file mode 100644
index 0000000..4132108
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithDiamondDependencyHelloWorldApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+public class ExeWithDiamondDependencyHelloWorldApp extends ExeWithLibraryUsingLibraryHelloWorldApp {
+
+    @Override
+    String getEnglishOutput() {
+        return HELLO_WORLD + " " + HELLO_WORLD + " " + HELLO_WORLD
+    }
+
+    @Override
+    String getFrenchOutput() {
+        return HELLO_WORLD_FRENCH + "\n"
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            #include "hello.h"
+            #include "greetings.h"
+
+            const char* getExeHello() {
+                #ifdef FRENCH
+                return "${HELLO_WORLD_FRENCH}";
+                #else
+                return "${HELLO_WORLD}";
+                #endif
+            }
+
+            int main () {
+                std::cout << getExeHello() << " ";
+                std::cout << getHello() << " ";
+                sayHello();
+                return 0;
+            }
+        """)
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy
new file mode 100644
index 0000000..f4757d9
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ExeWithLibraryUsingLibraryHelloWorldApp.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.test.fixtures.file.TestFile;
+
+public class ExeWithLibraryUsingLibraryHelloWorldApp extends HelloWorldApp {
+
+    void writeSources(TestFile mainSourceDir, TestFile librarySourceDir, TestFile greetingsLibrarySourceDir) {
+        getExecutable().writeSources(mainSourceDir)
+        getLibrary().writeSources(librarySourceDir)
+        getGreetingsHeader().writeToDir(greetingsLibrarySourceDir);
+        for (SourceFile sourceFile : greetingsSources) {
+            sourceFile.writeToDir(greetingsLibrarySourceDir);
+        }
+    }
+
+
+    public TestNativeComponent getGreetingsLibrary() {
+        return new TestNativeComponent() {
+            @Override
+            public List<SourceFile> getHeaderFiles() {
+                return Arrays.asList(getGreetingsHeader())
+            }
+
+            @Override
+            public List<SourceFile> getSourceFiles() {
+                return greetingsSources
+            }
+        };
+    }
+
+    @Override
+    String getEnglishOutput() {
+        return HELLO_WORLD + " " + HELLO_WORLD
+    }
+
+    @Override
+    String getFrenchOutput() {
+        return HELLO_WORLD_FRENCH + "\n"
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("cpp", "main.cpp", """
+            #include <iostream>
+            #include "hello.h"
+
+            const char* getExeHello() {
+                #ifdef FRENCH
+                return "${HELLO_WORLD_FRENCH}";
+                #else
+                return "${HELLO_WORLD}";
+                #endif
+            }
+
+            int main () {
+                std::cout << getExeHello() << " ";
+                sayHello();
+                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();
+        """);
+    }
+
+    List<SourceFile> librarySources = [
+        sourceFile("cpp", "hello.cpp", """
+            #include <iostream>
+            #include "hello.h"
+            #include "greetings.h"
+
+            void DLL_FUNC sayHello() {
+                std::cout << getHello();
+            }
+        """)
+    ]
+
+    SourceFile getGreetingsHeader() {
+        sourceFile("headers", "greetings.h", """
+            #include <string>
+
+            #ifdef _WIN32
+            #define DLL_FUNC __declspec(dllexport)
+            #else
+            #define DLL_FUNC
+            #endif
+
+            std::string DLL_FUNC getHello();
+        """);
+    }
+
+    List<SourceFile> greetingsSources = [
+        sourceFile("cpp", "greetings.cpp", """
+            #include "greetings.h"
+
+            std::string DLL_FUNC getHello() {
+                #ifdef FRENCH
+                return "${HELLO_WORLD_FRENCH}";
+                #else
+                return "${HELLO_WORLD}";
+                #endif
+            }
+        """)
+    ]
+
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/HelloWorldApp.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/HelloWorldApp.java
new file mode 100644
index 0000000..7bed58f
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/HelloWorldApp.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app;
+
+import org.apache.commons.io.FilenameUtils;
+import org.gradle.api.Transformer;
+import org.gradle.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+public abstract class HelloWorldApp extends TestApp {
+    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 getCustomOutput(String value) {
+        return value + "\n12";
+    }
+
+    public String getExtraConfiguration() {
+        return "";
+    }
+
+    public String getExtraConfiguration(String binaryName) {
+        return "";
+    }
+
+    public String getSourceType() {
+        return getMainSource().getPath();
+    }
+
+    public String getSourceExtension() {
+        return FilenameUtils.getExtension(getMainSource().getName());
+    }
+
+    public List<String> getPluginList() {
+        return Collections.singletonList(getNormalizedPluginName());
+    }
+
+    private String getNormalizedPluginName() {
+        return getSourceType().replaceAll("([a-z])([A-Z])", "$1-$2").toLowerCase();
+    }
+
+    public String getPluginScript() {
+        StringBuilder builder = new StringBuilder();
+        for (String plugin : getPluginList()) {
+            builder.append("apply plugin: '").append(plugin).append("'\n");
+        }
+        return builder.toString();
+    }
+
+    public String compilerArgs(String arg) {
+        return compilerConfig("args", arg);
+    }
+
+    public String compilerDefine(String define) {
+        return compilerConfig("define", define);
+    }
+
+    public String compilerDefine(String define, String value) {
+        return compilerConfig("define", define, value);
+    }
+
+    private String compilerConfig(String action, String... args) {
+        String quotedArgs = CollectionUtils.join(",", CollectionUtils.collect(args, new SingleQuotingTransformer()));
+        StringBuilder builder = new StringBuilder();
+        for (String plugin : getPluginList()) {
+            String compilerPrefix = getCompilerPrefix(plugin);
+            if (compilerPrefix == null) {
+                continue;
+            }
+            builder.append(compilerPrefix).append("Compiler.").append(action).append(" ").append(quotedArgs).append("\n");
+        }
+
+        return builder.toString();
+    }
+
+    private String getCompilerPrefix(String plugin) {
+        if (plugin.equals("c")) {
+            return "c";
+        }
+        if (plugin.equals("cpp")) {
+            return "cpp";
+        }
+        if (plugin.equals("objective-c")) {
+            return "objc";
+        }
+        if (plugin.equals("objective-cpp")) {
+            return "objcpp";
+        }
+        return null;
+    }
+
+    private static class SingleQuotingTransformer implements Transformer<Object, String> {
+        public Object transform(String original) {
+            return "'" + original + "'";
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/IncrementalHelloWorldApp.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/IncrementalHelloWorldApp.java
new file mode 100644
index 0000000..b9e0c42
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/IncrementalHelloWorldApp.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.nativeplatform.fixtures.app;
+
+import org.gradle.integtests.fixtures.SourceFile;
+
+import java.util.List;
+
+public abstract class IncrementalHelloWorldApp extends HelloWorldApp {
+    public TestApp getAlternate() {
+        return new TestApp() {
+            @Override
+            public SourceFile getMainSource() {
+                return getAlternateMainSource();
+            }
+
+            @Override
+            public SourceFile getLibraryHeader() {
+                return getAlternateLibraryHeader();
+            }
+
+            @Override
+            public List<SourceFile> getLibrarySources() {
+                return getAlternateLibrarySources();
+            }
+        };
+    }
+    private SourceFile getAlternateLibraryHeader() {
+        return getLibraryHeader();
+    }
+
+    public abstract SourceFile getAlternateMainSource();
+    public abstract String getAlternateOutput();
+
+    public abstract List<SourceFile> getAlternateLibrarySources();
+    public abstract String getAlternateLibraryOutput();
+
+    protected abstract SourceFile getBrokenFile();
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedLanguageHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedLanguageHelloWorldApp.groovy
new file mode 100644
index 0000000..13f69a2
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedLanguageHelloWorldApp.groovy
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+
+public class MixedLanguageHelloWorldApp extends HelloWorldApp {
+    private final AvailableToolChains.InstalledToolChain toolChain
+
+    MixedLanguageHelloWorldApp(AvailableToolChains.InstalledToolChain toolChain) {
+        this.toolChain = toolChain
+    }
+
+    @Override
+    List<String> getPluginList() {
+        return ['c', 'cpp', 'assembler']
+    }
+
+    String getExtraConfiguration() {
+        return """
+            model {
+                platforms {
+                    x86 {
+                        architecture "i386"
+                    }
+                }
+            }
+            componentSpecs.all {
+                it.targetPlatform "x86"
+            }
+"""
+    }
+
+    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);
+            }
+
+            // Ensure consistent asm name mapping on all platforms
+            #if !defined(_MSC_VER)
+            extern int sumx(int a, int b) asm("_sumx");
+            #endif
+
+            int DLL_FUNC sum(int a, int b) {
+                return sumx(a, b);
+            }
+"""),
+            new SourceFile("asm", "sum.s", getAsmSource())
+        ]
+    }
+
+    protected def getAsmSource() {
+        if (toolChain.isVisualCpp()) {
+            return windowsMasmSource
+        }
+        return i386GnuAsmSource
+    }
+
+    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/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedObjectiveCHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedObjectiveCHelloWorldApp.groovy
new file mode 100644
index 0000000..9d3504d
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/MixedObjectiveCHelloWorldApp.groovy
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class MixedObjectiveCHelloWorldApp extends HelloWorldApp {
+
+    List pluginList = ["objective-c", "objective-cpp", "c", "cpp"]
+
+    public String getExtraConfiguration(String binaryName = null) {
+        return """
+            binaries.matching { ${binaryName ? "it.name == '$binaryName'" : "true"} }.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("objc", "main.m", """
+            // Simple hello world app
+            #import "hello.h"
+
+            int main (int argc, const char * argv[]) {
+                doGreeting();
+                printf("%d", sum(7, 5));
+                return 0;
+            }
+
+        """);
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        return sourceFile("headers", "hello.h", """
+            #ifdef __cplusplus
+            extern "C" {
+            #endif
+            int sum(int a, int b);
+            void world();
+            #ifdef __cplusplus
+            }
+            #endif
+            void doGreeting();
+            void hello();
+            void bonjour();
+           """)
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return [
+                sourceFile("objc", "objclib.c", """
+            #import <Foundation/Foundation.h>
+            #include <stdio.h>
+            #include "hello.h"
+
+            void doGreeting()
+            {
+                #ifdef FRENCH
+                bonjour();
+                #else
+                hello();
+                world();
+                #endif
+            }
+
+            void hello()
+            {
+                NSString *hello = @"Hello";
+                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+                NSData *strData = [hello dataUsingEncoding: NSASCIIStringEncoding];
+                [stdout writeData: strData];
+            }"""),
+
+                sourceFile("c", "clib.c", """
+            #include <stdio.h>
+            #include "hello.h"
+
+            void bonjour() {
+                printf("Bonjour, Monde!\\n");
+            }
+        """),
+                sourceFile("cpp", "cpplib.cpp", """
+            #include "hello.h"
+            #include <iostream>
+            using namespace std;
+
+            void world() {
+                cout << ", World!" << std::endl;
+            }
+        """),
+                sourceFile("objcpp", "sum.mm", """
+            #import "hello.h"
+            int sum(int a, int b) {
+                return a + b;
+            }
+            """)
+        ]
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCHelloWorldApp.groovy
new file mode 100644
index 0000000..98e4cdd
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCHelloWorldApp.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class ObjectiveCHelloWorldApp extends IncrementalHelloWorldApp {
+
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("objc", "main.m", """
+            // Simple hello world app
+            #include "hello.h"
+
+            int main(int argc, const char * argv[])
+            {
+                Greeter* greeter = [Greeter new];
+                [greeter sayHello];
+                [greeter release];
+                printf("%d", sum(7, 5));
+                return 0;
+            }
+        """);
+    }
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return sourceFile("objc", "main.m", """
+            #import "hello.h"
+
+            int main (int argc, const char * argv[])
+            {
+                Greeter* greeter = [Greeter new];
+                [greeter sayHello];
+                [greeter release];
+                return 0;
+            }
+        """);
+    }
+
+    String alternateOutput = "$HELLO_WORLD\n"
+
+    @Override
+    SourceFile getLibraryHeader() {
+        return sourceFile("headers", "hello.h", """
+            #import <Foundation/Foundation.h>
+
+            @interface Greeter : NSObject
+                - (void)sayHello;
+            @end
+
+            int sum(int a, int b);
+        """);
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return [
+                sourceFile("objc", "hello.m", """
+            #import "hello.h"
+
+            @implementation Greeter
+            - (void) sayHello {
+                NSString *helloWorld = @"${HELLO_WORLD}";
+                #ifdef FRENCH
+                helloWorld = @"${HELLO_WORLD_FRENCH}";
+                #endif
+                fprintf(stdout, "%s\\n", [helloWorld UTF8String]);
+            }
+            @end
+        """),
+                sourceFile("objc", "sum.m", """
+            #import "hello.h"
+
+            int sum (int a, int b)
+            {
+                return a + b;
+            }
+        """)]
+    }
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return [
+                sourceFile("objc", "hello.m", """
+            #import "hello.h"
+
+            @implementation Greeter
+            - (void) sayHello {
+                NSString *helloWorld = @"${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}";
+                fprintf(stdout, "%s\\n", [helloWorld UTF8String]);
+            }
+            @end
+
+            // Extra function to ensure library has different size
+            int anotherFunction() {
+                return 1000;
+            }
+        """),
+                sourceFile("objc", "sum.m", """
+            #import "hello.h"
+
+            int sum (int a, int b)
+            {
+                return a + b;
+            }
+        """)]
+    }
+
+    String alternateLibraryOutput = "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}\n12"
+
+    public String getExtraConfiguration(String binaryName = null) {
+        return """
+            binaries.matching { ${binaryName ? "it.name == '$binaryName'" : "true"} }.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    List<String> getPluginList() {
+        ['objective-c']
+    }
+
+    public SourceFile getBrokenFile() {
+        return sourceFile("objc", "broken.m", """'broken""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCPCHHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCPCHHelloWorldApp.groovy
new file mode 100644
index 0000000..f65850d
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCPCHHelloWorldApp.groovy
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class ObjectiveCPCHHelloWorldApp extends PCHHelloWorldApp {
+
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("objc", "main.m", """
+            // Simple hello world app
+            #include "hello.h"
+
+            int main(int argc, const char * argv[])
+            {
+                Greeter* greeter = [Greeter new];
+                [greeter sayHello];
+                [greeter release];
+                printf("%d", sum(7, 5));
+                return 0;
+            }
+        """);
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        return getLibraryHeader("")
+    }
+
+    @Override
+    SourceFile getLibraryHeader(String path) {
+        return sourceFile("headers/${path}", "hello.h", """
+            #ifndef HELLO_H
+            #define HELLO_H
+            #import <Foundation/Foundation.h>
+
+            @interface Greeter : NSObject
+                - (void)sayHello;
+            @end
+
+            int sum(int a, int b);
+
+            #ifdef FRENCH
+            #pragma message("<==== compiling bonjour.h ====>")
+            #else
+            #pragma message("<==== compiling hello.h ====>")
+            #endif
+            #endif
+        """);
+    }
+
+    @Override
+    TestApp getAlternate() {
+        return new TestApp() {
+            @Override
+            SourceFile getMainSource() {
+                return getAlternateMainSource()
+            }
+
+            @Override
+            SourceFile getLibraryHeader() {
+                return sourceFile("headers", "hello.h", """
+                #ifndef HELLO_H
+                #define HELLO_H
+                #import <Foundation/Foundation.h>
+
+                @interface Greeter : NSObject
+                    - (void)sayHello;
+                @end
+
+                int sum(int a, int b);
+
+                #pragma message("<==== compiling althello.h ====>")
+                #endif
+            """);
+            }
+
+            @Override
+            List<SourceFile> getLibrarySources() {
+                return getAlternateLibrarySources()
+            }
+        }
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return getLibrarySources("")
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources(String path) {
+        return [
+                sourceFile("objc", "hello.m", """
+            #import "${path}hello.h"
+            #import <stdio.h>
+
+            @implementation Greeter
+            - (void) sayHello {
+                NSString *helloWorld = @"${HELLO_WORLD}";
+                #ifdef FRENCH
+                helloWorld = @"${HELLO_WORLD_FRENCH}";
+                #endif
+                fprintf(stdout, "%s\\n", [helloWorld UTF8String]);
+            }
+            @end
+        """),
+                sourceFile("objc", "sum.m", """
+            #import "${path}hello.h"
+
+            int sum (int a, int b)
+            {
+                return a + b;
+            }
+        """)]
+    }
+
+    @Override
+    SourceFile getSystemHeader() {
+        return getSystemHeader("")
+    }
+
+    @Override
+    SourceFile getSystemHeader(String path) {
+        sourceFile("headers/${path}", "systemHeader.h", """
+            #ifndef SYSTEMHEADER_H
+            #define SYSTEMHEADER_H
+            void systemCall();
+            #pragma message("<==== compiling systemHeader.h ====>")
+            #endif
+        """)
+    }
+
+    @Override
+    String getIOHeader() {
+        return "stdio.h"
+    }
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return getMainSource()
+    }
+
+    @Override
+    String getAlternateOutput() {
+        return null
+    }
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return getAlternateLibrarySources()
+    }
+
+    @Override
+    String getAlternateLibraryOutput() {
+        return null
+    }
+
+    public String getExtraConfiguration(String binaryName = null) {
+        return """
+            binaries.matching { ${binaryName ? "it.name == '$binaryName'" : "true"} }.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    List<String> getPluginList() {
+        ['objective-c']
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppHelloWorldApp.groovy
new file mode 100644
index 0000000..dc55678
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppHelloWorldApp.groovy
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class ObjectiveCppHelloWorldApp extends IncrementalHelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("objcpp", "main.mm", """
+            // Simple hello world app
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #import <Foundation/Foundation.h>
+            #include "hello.h"
+
+            int main (int argc, const char * argv[])
+            {
+                sayHello();
+                printf("%d", sum(7, 5));
+                return 0;
+            }
+        """);
+    }
+
+    @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("objcpp", "hello.mm", """
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #include "hello.h"
+            #import <Foundation/Foundation.h>
+
+            #include <iostream>
+
+            #ifdef FRENCH
+            const char* greeting() {
+                return "${HELLO_WORLD_FRENCH}";
+            }
+            #endif
+
+            void DLL_FUNC sayHello() {
+                #ifdef FRENCH
+                std::cout << greeting() << std::endl;
+                #else
+                NSString *helloWorld = @"${HELLO_WORLD}\\n";
+                NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+                NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
+                [stdout writeData: strData];
+                #endif
+            }
+        """),
+            sourceFile("objcpp", "sum.mm", """
+            #include "hello.h"
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)
+    ]
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return sourceFile("objcpp", "main.mm", """
+            // Simple hello world app
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #import <Foundation/Foundation.h>
+            #import <iostream>
+            #import "hello.h"
+
+            int main (int argc, const char * argv[])
+            {
+                std::cout << "${HELLO_WORLD} ${HELLO_WORLD}" << std::endl;
+                return 0;
+            }
+        """);
+    }
+
+    String alternateOutput = "${HELLO_WORLD} ${HELLO_WORLD}\n"
+
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return [
+            sourceFile("objcpp", "hello.mm", """
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #include <iostream>
+            #include "hello.h"
+
+            void DLL_FUNC sayHello() {
+                std::cout << "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}" << std::endl;
+            }
+        """),
+        sourceFile("objcpp", "sum.mm", """
+            #include "hello.h"
+            int DLL_FUNC sum(int a, int b) {
+                return a + b;
+            }
+        """)]
+    }
+
+    String alternateLibraryOutput = "${HELLO_WORLD} - ${HELLO_WORLD_FRENCH}\n12"
+
+    public String getExtraConfiguration(String binaryName = null) {
+        return """
+            binaries.matching { ${binaryName ? "it.name == '$binaryName'" : "true"} }.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    List<String> getPluginList() {
+        ['objective-cpp']
+    }
+
+    public SourceFile getBrokenFile() {
+        return sourceFile("objcpp", "broken.mm", """'broken""")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppPCHHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppPCHHelloWorldApp.groovy
new file mode 100644
index 0000000..b46c7ab
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/ObjectiveCppPCHHelloWorldApp.groovy
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class ObjectiveCppPCHHelloWorldApp extends PCHHelloWorldApp {
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("objcpp", "main.mm", """
+            // Simple hello world app
+            #define __STDC_LIMIT_MACROS
+            #include <stdint.h>
+            #import <Foundation/Foundation.h>
+            #include "hello.h"
+
+            int main (int argc, const char * argv[])
+            {
+                sayHello();
+                printf("%d", sum(7, 5));
+                return 0;
+            }
+        """);
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        getLibraryHeader("")
+    }
+    @Override
+    SourceFile getLibraryHeader(String path) {
+        return sourceFile("headers/${path}", "hello.h", """
+            #ifndef HELLO_H
+            #define HELLO_H
+            void sayHello();
+            int sum(int a, int b);
+            #ifdef FRENCH
+            #pragma message("<==== compiling bonjour.h ====>")
+            #else
+            #pragma message("<==== compiling hello.h ====>")
+            #endif
+            #endif
+        """);
+    }
+
+    @Override
+    TestApp getAlternate() {
+        return new TestApp() {
+            @Override
+            SourceFile getMainSource() {
+                return getAlternateMainSource()
+            }
+
+            @Override
+            SourceFile getLibraryHeader() {
+                return sourceFile("headers", "hello.h", """
+                    #ifndef HELLO_H
+                    #define HELLO_H
+                    void sayHello();
+                    int sum(int a, int b);
+
+                    #pragma message("<==== compiling althello.h ====>")
+                    #endif
+                """);
+            }
+
+            @Override
+            List<SourceFile> getLibrarySources() {
+                return getAlternateLibrarySources()
+            }
+        }
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources() {
+        return getLibrarySources("")
+    }
+
+    @Override
+    List<SourceFile> getLibrarySources(String path) {
+        return [
+                sourceFile("objcpp", "hello.mm", """
+                    #define __STDC_LIMIT_MACROS
+                    #include "${path}hello.h"
+                    #include <iostream>
+                    #include <stdint.h>
+                    #import <Foundation/Foundation.h>
+
+                    #ifdef FRENCH
+                    const char* greeting() {
+                        return "${HELLO_WORLD_FRENCH}";
+                    }
+                    #endif
+
+                    void sayHello() {
+                        #ifdef FRENCH
+                        std::cout << greeting() << std::endl;
+                        #else
+                        NSString *helloWorld = @"${HELLO_WORLD}\\n";
+                        NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+                        NSData *strData = [helloWorld dataUsingEncoding: NSASCIIStringEncoding];
+                        [stdout writeData: strData];
+                        #endif
+                    }
+                """),
+                sourceFile("objcpp", "sum.mm", """
+                    #include "${path}hello.h"
+                    int sum(int a, int b) {
+                        return a + b;
+                    }
+                """)
+        ]
+    }
+
+    @Override
+    SourceFile getSystemHeader() {
+        return getSystemHeader("")
+    }
+
+    @Override
+    SourceFile getSystemHeader(String path) {
+        sourceFile("headers/${path}", "systemHeader.h", """
+            #ifndef SYSTEMHEADER_H
+            #define SYSTEMHEADER_H
+            void systemCall();
+            #pragma message("<==== compiling systemHeader.h ====>")
+            #endif
+        """)
+    }
+
+    @Override
+    String getIOHeader() {
+        return "iostream"
+    }
+
+    @Override
+    SourceFile getAlternateMainSource() {
+        return getMainSource()
+    }
+
+    @Override
+    String getAlternateOutput() {
+        return null
+    }
+
+    @Override
+    List<SourceFile> getAlternateLibrarySources() {
+        return getLibrarySources()
+    }
+
+    @Override
+    String getAlternateLibraryOutput() {
+        return null
+    }
+
+    public String getExtraConfiguration(String binaryName = null) {
+        return """
+            binaries.matching { ${binaryName ? "it.name == '$binaryName'" : "true"} }.all {
+                if (targetPlatform.operatingSystem.macOsX) {
+                    linker.args "-framework", "Foundation"
+                } else {
+                    objcppCompiler.args "-I/usr/include/GNUstep", "-I/usr/local/include/objc", "-fconstant-string-class=NSConstantString", "-D_NATIVE_OBJC_EXCEPTIONS"
+                    linker.args "-lgnustep-base", "-lobjc"
+                }
+            }
+        """
+    }
+
+    @Override
+    List<String> getPluginList() {
+        ['objective-cpp']
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PCHHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PCHHelloWorldApp.groovy
new file mode 100644
index 0000000..c454bc9
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PCHHelloWorldApp.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+abstract class PCHHelloWorldApp extends IncrementalHelloWorldApp {
+    abstract SourceFile getLibraryHeader(String path)
+    abstract List<SourceFile> getLibrarySources(String path)
+    abstract SourceFile getSystemHeader()
+    abstract SourceFile getSystemHeader(String path)
+    abstract String getIOHeader()
+
+    public SourceFile getBrokenFile() {
+        return null
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PlatformDetectingTestApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PlatformDetectingTestApp.groovy
new file mode 100644
index 0000000..13ac06f
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/PlatformDetectingTestApp.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class PlatformDetectingTestApp extends TestApp {
+    @Override
+    SourceFile getMainSource() {
+        sourceFile("cpp", "main.cpp", """
+#include <iostream>
+using namespace std;
+#include "hello.h"
+
+int main () {
+    ${outputPlatform()}
+
+    outputLibraryPlatform();
+}
+""")
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        sourceFile("headers", "hello.h", """
+#ifdef _WIN32
+#define DLL_FUNC __declspec(dllexport)
+#else
+#define DLL_FUNC
+#endif
+
+void DLL_FUNC outputLibraryPlatform();
+        """);
+    }
+
+
+    List<SourceFile> librarySources = [
+        sourceFile("cpp", "hello.cpp", """
+#include <iostream>
+using namespace std;
+#include "hello.h"
+
+void DLL_FUNC outputLibraryPlatform() {
+    ${outputPlatform()}
+}
+        """)
+    ]
+
+    def outputPlatform() {
+        return """
+    #if defined(__x86_64__) || defined(_M_X64)
+    cout << "amd64";
+    #elif defined(__i386) || defined(_M_IX86)
+    cout << "i386";
+    #elif defined(_M_IA64)
+    cout << "itanium";
+    #else
+    cout << "unknown";
+    #endif
+    cout << " ";
+
+    #if defined(__linux__)
+    cout << "linux";
+    #elif defined(__APPLE__) && defined(__MACH__)
+    cout << "os x";
+    #elif defined(_WIN32) || defined (_WIN64) || defined (__CYGWIN__)
+    cout << "windows";
+    #else
+    cout << "unknown";
+    #endif
+"""
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestApp.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestApp.java
new file mode 100644
index 0000000..3f2d86d
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestApp.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.fixtures.app;
+
+import org.gradle.integtests.fixtures.SourceFile;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class TestApp extends TestNativeComponent {
+    public abstract SourceFile getMainSource();
+    public abstract SourceFile getLibraryHeader();
+    public abstract List<SourceFile> getLibrarySources();
+
+    public TestNativeComponent getLibrary() {
+        return new TestNativeComponent() {
+            @Override
+            public List<SourceFile> getSourceFiles() {
+                return getLibrarySources();
+            }
+
+            @Override
+            public List<SourceFile> getHeaderFiles() {
+                return Arrays.asList(getLibraryHeader());
+            }
+        };
+    }
+
+    public TestNativeComponent getExecutable() {
+        return new TestNativeComponent() {
+            @Override
+            public List<SourceFile> getHeaderFiles() {
+                return Collections.emptyList();
+            }
+
+            @Override
+            public List<SourceFile> getSourceFiles() {
+                return Arrays.asList(getMainSource());
+            }
+        };
+    }
+
+    @Override
+    public List<SourceFile> getHeaderFiles() {
+        ArrayList<SourceFile> headerFiles = new ArrayList<SourceFile>();
+        headerFiles.addAll(getExecutable().getHeaderFiles());
+        headerFiles.addAll(getLibrary().getHeaderFiles());
+        return headerFiles;
+    }
+
+    @Override
+    public List<SourceFile> getSourceFiles() {
+        ArrayList<SourceFile> sourceFiles = new ArrayList<SourceFile>();
+        sourceFiles.addAll(getExecutable().getSourceFiles());
+        sourceFiles.addAll(getLibrary().getSourceFiles());
+        return sourceFiles;
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestNativeComponent.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestNativeComponent.groovy
new file mode 100644
index 0000000..4768df2
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/TestNativeComponent.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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.test.fixtures.file.TestFile
+
+abstract class TestNativeComponent {
+    List<SourceFile> getAllFiles() {
+        return sourceFiles + headerFiles
+    }
+
+    abstract List<SourceFile> getSourceFiles()
+
+    abstract List<SourceFile> getHeaderFiles()
+
+    protected SourceFile sourceFile(String path, String name, String content) {
+        return new SourceFile(path, name, content);
+    }
+
+    public void writeSources(TestFile sourceDir) {
+        for (SourceFile srcFile : allFiles) {
+            srcFile.writeToDir(sourceDir)
+        }
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/WindowsResourceHelloWorldApp.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/WindowsResourceHelloWorldApp.groovy
new file mode 100644
index 0000000..98a0538
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/WindowsResourceHelloWorldApp.groovy
@@ -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.nativeplatform.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class WindowsResourceHelloWorldApp extends HelloWorldApp {
+    @Override
+    String getEnglishOutput() {
+        return HELLO_WORLD
+    }
+
+    @Override
+    String getFrenchOutput() {
+        return HELLO_WORLD_FRENCH
+    }
+
+    @Override
+    List<String> getPluginList() {
+        ['cpp', 'windows-resources']
+    }
+
+    @Override
+    String getExtraConfiguration() {
+        return """
+            binaries.all {
+                linker.args "user32.lib"
+            }
+            binaries.withType(SharedLibraryBinarySpec) {
+                cppCompiler.define "DLL_EXPORT"
+            }
+"""
+    }
+
+    @Override
+    String compilerArgs(String arg) {
+        "rcCompiler.args '${arg}'"
+    }
+
+    @Override
+    String compilerDefine(String define) {
+        "rcCompiler.define '${define}'"
+    }
+
+    @Override
+    String compilerDefine(String define, String value) {
+        "rcCompiler.define '${define}', '${value}'"
+    }
+
+    @Override
+    SourceFile getMainSource() {
+        return sourceFile("cpp", "main.cpp", """
+#include "hello.h"
+
+int main () {
+    hello();
+    return 0;
+}
+""");
+    }
+
+    @Override
+    SourceFile getLibraryHeader() {
+        return sourceFile("headers", "hello.h", """
+#define IDS_HELLO    111
+
+#ifdef DLL_EXPORT
+#define DLL_FUNC __declspec(dllexport)
+#define MODULE_HANDLE GetModuleHandle("hello")
+#else
+#define DLL_FUNC
+#define MODULE_HANDLE null
+#endif
+
+void DLL_FUNC hello();
+""");
+    }
+
+    List<SourceFile> librarySources = [
+        sourceFile("cpp", "hello.cpp", """
+#include <iostream>
+#include <windows.h>
+#include <string>
+#include "hello.h"
+
+std::string LoadStringFromResource(UINT stringID)
+{
+    HINSTANCE instance = GetModuleHandle("hello");
+    WCHAR * pBuf = NULL;
+    int len = LoadStringW(instance, stringID, reinterpret_cast<LPWSTR>(&pBuf), 0);
+    std::wstring wide = std::wstring(pBuf, len);
+    return std::string(wide.begin(), wide.end());
+}
+
+void DLL_FUNC hello() {
+    std::string hello = LoadStringFromResource(IDS_HELLO);
+    std::cout << hello;
+}
+"""),
+        sourceFile("rc", "resources.rc", """
+#include "hello.h"
+
+STRINGTABLE
+{
+    #ifdef FRENCH
+    IDS_HELLO, "${HELLO_WORLD_FRENCH}"
+    #else
+    IDS_HELLO, "${HELLO_WORLD}"
+    #endif
+}
+""")
+    ]
+
+    List<SourceFile> getResourceSources() {
+        getLibrarySources().subList(1, 2)
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/BinaryInfo.java b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/BinaryInfo.java
new file mode 100644
index 0000000..2dc98a5
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/BinaryInfo.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.nativeplatform.fixtures.binaryinfo;
+
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal;
+
+import java.util.List;
+
+public interface BinaryInfo {
+    ArchitectureInternal getArch();
+    List<String> listObjectFiles();
+    List<String> listLinkedLibraries();
+    String getSoName();
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/DumpbinBinaryInfo.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/DumpbinBinaryInfo.groovy
new file mode 100644
index 0000000..d7db284
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/DumpbinBinaryInfo.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.nativeplatform.fixtures.binaryinfo
+import net.rubygrapefruit.platform.SystemInfo
+import net.rubygrapefruit.platform.WindowsRegistry
+import org.gradle.internal.nativeintegration.services.NativeServices
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.Architectures
+import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator
+import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioInstall
+
+class DumpbinBinaryInfo implements BinaryInfo {
+    final File binaryFile
+    final File vcBin
+    final String vcPath
+
+    DumpbinBinaryInfo(File binaryFile) {
+        this.binaryFile = binaryFile
+
+        VisualStudioInstall vsInstall = findVisualStudio()
+        DefaultNativePlatform targetPlatform = new DefaultNativePlatform("default");
+        vcBin = vsInstall.getVisualCpp().getBinaryPath(targetPlatform)
+        vcPath = vsInstall.getVisualCpp().getPath(targetPlatform).join(';')
+    }
+
+    static VisualStudioInstall findVisualStudio() {
+        def vsLocator = new DefaultVisualStudioLocator(OperatingSystem.current(), NativeServices.instance.get(WindowsRegistry), NativeServices.instance.get(SystemInfo))
+        return vsLocator.locateVisualStudioInstalls(null).visualStudio
+    }
+
+    private findExe(String exe) {
+        final candidate = new File(vcBin, exe)
+        if (candidate.exists()) {
+            return candidate
+        }
+        throw new RuntimeException("dumpbin.exe not found")
+    }
+
+    private String getDumpbinHeaders() {
+        def dumpbin = findExe("dumpbin.exe")
+        def process = [dumpbin.absolutePath, '/HEADERS', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
+        return process.inputStream.text
+    }
+
+    def static readArch(def input) {
+        def pattern = /(?m)^.* machine \((.*)\).*$/
+        return (input =~ pattern).findResult { line, group ->
+            group.trim()
+        }
+    }
+
+    ArchitectureInternal getArch() {
+        def archString = readArch(dumpbinHeaders)
+        switch (archString) {
+            case "x86":
+                return Architectures.forInput("x86")
+            case "x64":
+                return Architectures.forInput("x86_64")
+            case "IA64":
+                return Architectures.forInput("ia-64")
+            default:
+                throw new RuntimeException("Cannot determine architecture for ${archString}")
+        }
+    }
+
+    List<String> listObjectFiles() {
+        def dumpbin = findExe("lib.exe")
+        def process = [dumpbin.absolutePath, '/LIST', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
+        return process.inputStream.readLines().drop(3).collect { new File(it).name }
+    }
+
+    List<String> listLinkedLibraries() {
+        def dumpbin = findExe("dumpbin.exe")
+        def process = [dumpbin.absolutePath, '/IMPORTS', binaryFile.absolutePath].execute(["PATH=$vcPath"], null)
+        return process.inputStream.readLines()
+    }
+
+    String getSoName() {
+        throw new UnsupportedOperationException("soname is not relevant on windows")
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/OtoolBinaryInfo.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/OtoolBinaryInfo.groovy
new file mode 100644
index 0000000..9815f02
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/OtoolBinaryInfo.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.nativeplatform.fixtures.binaryinfo
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.Architectures
+
+class OtoolBinaryInfo implements BinaryInfo {
+    def binaryFile
+
+    OtoolBinaryInfo(File binaryFile) {
+        this.binaryFile = binaryFile    }
+
+    ArchitectureInternal getArch() {
+        def process = ['otool', '-hv', binaryFile.absolutePath].execute()
+        def lines = process.inputStream.readLines()
+        def archString = lines[3].split()[1]
+
+        switch (archString) {
+            case "I386":
+                return Architectures.forInput("x86")
+            case "X86_64":
+                return Architectures.forInput("x86_64")
+            default:
+                throw new RuntimeException("Cannot determine architecture for ${archString}")
+        }
+    }
+
+    List<String> listObjectFiles() {
+        def process = ['ar', '-t', binaryFile.getAbsolutePath()].execute()
+        return process.inputStream.readLines().drop(1)
+    }
+
+    List<String> listLinkedLibraries() {
+        def process = ['otool', '-L', binaryFile.absolutePath].execute()
+        def lines = process.inputStream.readLines()
+        return lines
+    }
+
+    String getSoName() {
+        def process = ['otool', '-D', binaryFile.absolutePath].execute()
+        def lines = process.inputStream.readLines()
+        return lines[1]
+    }
+}
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/ReadelfBinaryInfo.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/ReadelfBinaryInfo.groovy
new file mode 100644
index 0000000..21ab362
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/binaryinfo/ReadelfBinaryInfo.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.nativeplatform.fixtures.binaryinfo
+import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
+import org.gradle.nativeplatform.platform.internal.Architectures
+
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+class ReadelfBinaryInfo implements BinaryInfo {
+
+    private final File binaryFile
+
+    ReadelfBinaryInfo(File binaryFile) {
+        this.binaryFile = binaryFile
+    }
+
+    ArchitectureInternal getArch() {
+        def process = ['readelf', '-h', binaryFile.absolutePath].execute()
+        List<String> lines = process.inputStream.readLines()
+        return readArch(lines)
+    }
+
+    List<String> listObjectFiles() {
+        def process = ['ar', '-t', binaryFile.getAbsolutePath()].execute()
+        return process.inputStream.readLines()
+    }
+
+    List<String> listLinkedLibraries() {
+        def process = ['readelf', '-d', binaryFile.absolutePath].execute()
+        def lines = process.inputStream.readLines()
+        return lines
+    }
+
+    String getSoName() {
+        def process = ['readelf', '-d', binaryFile.absolutePath].execute()
+        List<String> lines = process.inputStream.readLines()
+        return readSoName(lines)
+    }
+
+    static String readSoName(List<String> lines) {
+        final Pattern pattern = ~/^.*\(SONAME\)\s+.*soname.*\: \[(.*)\]$/
+        String matchingLine = lines.find {
+            pattern.matcher(it).matches()
+        }
+        if (matchingLine == null) {
+            return null;
+        }
+        final Matcher matcher = pattern.matcher(matchingLine)
+        assert matcher.matches()
+        return matcher.group(1)
+    }
+
+    static ArchitectureInternal readArch(List<String> lines) {
+        def archString = readFirstHeaderValue(lines, "Machine:", "Maschine:")
+        switch (archString) {
+            case "Intel 80386":
+                return Architectures.forInput("x86")
+            case "Advanced Micro Devices X86-64":
+                return Architectures.forInput("x86_64")
+            default:
+                throw new RuntimeException("Cannot determine architecture for ${archString}\nreadelf output:\n${lines}")
+        }
+    }
+
+    private static String readFirstHeaderValue(List<String> lines, String... headers) {
+        def matchingLines = headers.collect { header ->
+            String matchingLine = lines.find {
+                it.trim().startsWith(header)
+            }
+            matchingLine?.replaceFirst(header, "")?.trim()
+        }
+        return matchingLines.find { it != null }
+    }
+}
diff --git a/subprojects/platform-play/platform-play.gradle b/subprojects/platform-play/platform-play.gradle
new file mode 100644
index 0000000..aecee73
--- /dev/null
+++ b/subprojects/platform-play/platform-play.gradle
@@ -0,0 +1,18 @@
+apply plugin: "groovy"
+
+dependencies {
+    compile project(":core")
+    compile project(":platformJvm")
+    compile project(":languageJvm")
+    compile project(":languageScala")
+    compile project(":javascript")
+    compile project(":diagnostics")
+}
+
+useTestFixtures()
+useTestFixtures(project: ":platformBase", sourceSet: 'integTest')
+useTestFixtures(project: ":languageScala", sourceSet: 'integTest')
+useTestFixtures(project: ":languageJava", sourceSet: 'integTest')
+
+useClassycle()
+strictCompile()
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndJavaLangProjectIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndJavaLangProjectIntegrationTest.groovy
new file mode 100644
index 0000000..ecf7a91
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndJavaLangProjectIntegrationTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.fixtures.TestJavaComponent
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+class MixedPlayAndJavaLangProjectIntegrationTest extends AbstractIntegrationSpec {
+    TestJvmComponent javaApp = new TestJavaComponent()
+    PlayApp playApp = new BasicPlayApp()
+
+    def setup() {
+        playApp.writeSources(file("."))
+        javaApp.writeSources(file("src/javaLib"))
+        javaApp.writeResources(file("src/javaLib/resources"))
+        settingsFile.text = "rootProject.name = 'mixedJavaAndPlay'"
+        buildFile.text = """
+        plugins {
+            id 'jvm-component'
+            id '${javaApp.languageName}-lang'
+            id 'play'
+        }
+        repositories{
+            mavenCentral()
+            jcenter()
+            maven{
+                name = "typesafe-maven-release"
+                url = "https://repo.typesafe.com/typesafe/maven-releases"
+            }
+        }
+
+        model {
+            components {
+                javaLib(JvmLibrarySpec)
+            }
+        }
+"""
+    }
+
+    def "assemble builds jvm component and play component binaries"() {
+        when:
+        succeeds("assemble")
+        then:
+        executedAndNotSkipped(":compileJavaLibJarJavaLibJava", ":processJavaLibJarJavaLibResources", ":createJavaLibJar", ":javaLibJar", ":createPlayBinaryAssetsJar",
+                ":routesCompileRoutesSourcesPlayBinary", ":twirlCompileTwirlTemplatesPlayBinary", ":scalaCompilePlayBinary", ":createPlayBinaryJar", ":playBinary", ":assemble")
+        and:
+        file("build/classes/javaLibJar").assertHasDescendants(javaApp.expectedOutputs*.fullPath as String[])
+        new JarTestFixture(file("build/jars/javaLibJar/javaLib.jar")).hasDescendants(javaApp.expectedOutputs*.fullPath as String[])
+        file("build/playBinary/lib/mixedJavaAndPlay.jar").exists()
+        file("build/playBinary/lib/mixedJavaAndPlay-assets.jar").exists()
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndScalaLangProjectIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndScalaLangProjectIntegrationTest.groovy
new file mode 100644
index 0000000..a942e49
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/MixedPlayAndScalaLangProjectIntegrationTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.jvm.TestJvmComponent
+import org.gradle.language.scala.fixtures.TestScalaComponent
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+class MixedPlayAndScalaLangProjectIntegrationTest extends AbstractIntegrationSpec {
+    TestJvmComponent scalaApp = new TestScalaComponent()
+    PlayApp playApp = new BasicPlayApp()
+
+    def setup() {
+        playApp.writeSources(file("."))
+        scalaApp.writeSources(file("src/scalaLib"))
+        scalaApp.writeResources(file("src/scalaLib/resources"))
+        settingsFile.text = "rootProject.name = 'mixedScalaAndPlay'"
+        buildFile.text = """
+        plugins {
+            id 'jvm-component'
+            id '${scalaApp.languageName}-lang'
+            id 'play'
+        }
+        repositories{
+            mavenCentral()
+            jcenter()
+            maven{
+                name = "typesafe-maven-release"
+                url = "https://repo.typesafe.com/typesafe/maven-releases"
+            }
+        }
+
+        model {
+            components {
+                scalaLib(JvmLibrarySpec)
+            }
+        }
+"""
+    }
+
+    def "assemble builds jvm component and play component binaries"() {
+        when:
+        succeeds("assemble")
+        then:
+        executedAndNotSkipped(":createPlayBinaryAssetsJar", ":routesCompileRoutesSourcesPlayBinary", ":twirlCompileTwirlTemplatesPlayBinary", ":scalaCompilePlayBinary", ":createPlayBinaryJar", ":playBinary",
+                ":compileScalaLibJarScalaLibScala", ":processScalaLibJarScalaLibResources", ":createScalaLibJar", ":scalaLibJar", ":assemble")
+        and:
+        file("build/classes/scalaLibJar").assertHasDescendants(scalaApp.expectedOutputs*.fullPath as String[])
+        new JarTestFixture(file("build/jars/scalaLibJar/scalaLib.jar")).hasDescendants(scalaApp.expectedOutputs*.fullPath as String[])
+        file("build/playBinary/lib/mixedScalaAndPlay.jar").exists()
+        file("build/playBinary/lib/mixedScalaAndPlay-assets.jar").exists()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayAppWithFailingTestsIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayAppWithFailingTestsIntegrationTest.groovy
new file mode 100644
index 0000000..0aa429e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayAppWithFailingTestsIntegrationTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.play.integtest.fixtures.PlayMultiVersionIntegrationTest
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.play.integtest.fixtures.app.WithFailingTestsApp
+import org.gradle.util.TextUtil
+
+class PlayAppWithFailingTestsIntegrationTest extends PlayMultiVersionIntegrationTest {
+
+    PlayApp playApp = new WithFailingTestsApp();
+
+    def setup() {
+        playApp.writeSources(file("."))
+        buildFile << """
+model {
+    components {
+        play {
+            targetPlatform "play-${version}"
+        }
+    }
+}
+"""
+    }
+
+    def "reports failing run play app tests"() {
+        when:
+        fails("testPlayBinary")
+        then:
+
+        output.contains(TextUtil.toPlatformLineSeparators("""
+FailingApplicationSpec > Application should::render the index page FAILED
+    org.specs2.reporter.SpecFailureAssertionFailedError
+"""))
+
+        output.contains(TextUtil.toPlatformLineSeparators("""
+FailingIntegrationSpec > Application should::work from within a browser FAILED
+    org.specs2.reporter.SpecFailureAssertionFailedError
+"""))
+        errorOutput.contains("6 tests completed, 2 failed")
+        errorOutput.contains("> There were failing tests.")
+
+        def result = new JUnitXmlTestExecutionResult(testDirectory, "build/playBinary/reports/test/xml")
+        result.assertTestClassesExecuted("ApplicationSpec", "IntegrationSpec", "FailingApplicationSpec", "FailingIntegrationSpec")
+        result.testClass("ApplicationSpec").assertTestCount(2, 0, 0)
+        result.testClass("IntegrationSpec").assertTestCount(1, 0, 0)
+        result.testClass("FailingIntegrationSpec").assertTestCount(1, 1, 0)
+        result.testClass("FailingApplicationSpec").assertTestCount(2, 1, 0)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayApplicationBinariesIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayApplicationBinariesIntegrationTest.groovy
new file mode 100644
index 0000000..ecfa394
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayApplicationBinariesIntegrationTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.hamcrest.Matchers
+
+class PlayApplicationBinariesIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'play-application'
+            }
+        """
+    }
+
+    def "produces sensible error when play binary is not buildable" () {
+        buildFile << """
+            model {
+                components {
+                    play {
+                        binaries.all { buildable = false }
+                    }
+                }
+            }
+        """
+
+        when:
+        fails "assemble"
+
+        then:
+        failureDescriptionContains("Execution failed for task ':assemble'.")
+        failure.assertThatCause(Matchers.<String>allOf(
+                Matchers.startsWith("No buildable binaries found:"),
+                Matchers.containsString("playBinary: Disabled by user")
+        ))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayBinaryApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayBinaryApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..a1e9285
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayBinaryApplicationIntegrationTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.play.integtest.fixtures.PlayMultiVersionRunApplicationIntegrationTest
+import org.gradle.util.TextUtil
+
+abstract class PlayBinaryApplicationIntegrationTest extends PlayMultiVersionRunApplicationIntegrationTest {
+
+    def "can build play app binary"() {
+        when:
+        succeeds("assemble")
+
+        then:
+        executedAndNotSkipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary",
+                ":assemble")
+
+        and:
+        verifyJars()
+
+        when:
+        succeeds("createPlayBinaryJar")
+
+        then:
+        skipped(":createPlayBinaryJar", ":twirlCompileTwirlTemplatesPlayBinary")
+    }
+
+    def "can run play app"() {
+        setup:
+        httpPort = portFinder.nextAvailable
+        buildFile << """
+            model {
+                tasks.runPlayBinary {
+                    httpPort = $httpPort
+                }
+            }
+        """
+        run "assemble"
+
+        when:
+        def userInput = new PipedOutputStream();
+        executer.withStdIn(new PipedInputStream(userInput))
+        GradleHandle gradleHandle = executer.withTasks("runPlayBinary").start()
+
+        then:
+        verifyStarted()
+
+        and:
+        verifyRunningApp()
+
+        when: "stopping gradle"
+        userInput.write(4) // ctrl+d
+        userInput.write(TextUtil.toPlatformLineSeparators("\n").bytes) // For some reason flush() doesn't get the keystroke to the DaemonExecuter
+
+        gradleHandle.waitForFinish()
+
+        then: "play server is stopped too"
+        verifyStopped()
+    }
+
+    void verifyJars() {
+        jar("build/playBinary/lib/${playApp.name}.jar").containsDescendants(
+                "Routes.class",
+                "views/html/index.class",
+                "views/html/main.class",
+                "controllers/Application.class",
+                "application.conf")
+        jar("build/playBinary/lib/${playApp.name}-assets.jar").containsDescendants(
+                "public/images/favicon.svg",
+                "public/stylesheets/main.css",
+                "public/javascripts/hello.js")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayDistributionApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayDistributionApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..34c6667
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayDistributionApplicationIntegrationTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.play.integtest.fixtures.DistributionTestExecHandleBuilder
+import org.gradle.play.integtest.fixtures.PlayMultiVersionRunApplicationIntegrationTest
+import org.gradle.process.internal.ExecHandle
+import org.gradle.process.internal.ExecHandleBuilder
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+abstract class PlayDistributionApplicationIntegrationTest extends PlayMultiVersionRunApplicationIntegrationTest {
+    def "can build play app distribution"() {
+        when:
+        succeeds("stage")
+
+        then:
+        executedAndNotSkipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryDistributionJar",
+                ":createPlayBinaryAssetsJar",
+                ":createPlayBinaryStartScripts",
+                ":stagePlayBinaryDist")
+
+        and:
+        verifyJars()
+        verifyStagedFiles()
+
+        when:
+        succeeds("dist")
+
+        then:
+        executedAndNotSkipped(":createPlayBinaryDist")
+        skipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryDistributionJar",
+                ":createPlayBinaryAssetsJar",
+                ":createPlayBinaryStartScripts",
+                ":stagePlayBinaryDist")
+
+        and:
+        verifyZips()
+    }
+
+    @Requires(TestPrecondition.NOT_UNKNOWN_OS)
+    def "can run play distribution" () {
+        ExecHandleBuilder builder
+        ExecHandle handle
+        String distDirPath = new File(testDirectory, "build/stage").path
+
+        setup:
+        httpPort = portFinder.nextAvailable
+        run "stage"
+
+        when:
+        builder = new DistributionTestExecHandleBuilder(httpPort.toString(), distDirPath)
+        handle = builder.build()
+        handle.start()
+
+        then:
+        verifyStarted()
+
+        and:
+        verifyRunningApp()
+
+        cleanup:
+        ((DistributionTestExecHandleBuilder.DistributionTestExecHandle) handle).shutdown()
+        verifyStopped()
+    }
+
+    void verifyZips() {
+        zip("build/distributions/playBinary.zip").containsDescendants(
+                "playBinary/lib/${playApp.name}.jar",
+                "playBinary/lib/${playApp.name}-assets.jar",
+                "playBinary/bin/playBinary",
+                "playBinary/bin/playBinary.bat",
+                "playBinary/conf/application.conf",
+                "playBinary/README"
+        )
+    }
+
+    void verifyStagedFiles() {
+        file("build/stage/playBinary").assertContainsDescendants(
+                "lib/${playApp.name}.jar",
+                "lib/${playApp.name}-assets.jar",
+                "bin/playBinary",
+                "bin/playBinary.bat",
+                "conf/application.conf",
+                "README"
+        )
+    }
+
+    void verifyJars() {
+        jar("build/distributionJars/playBinary/${playApp.name}.jar").containsDescendants(
+                "Routes.class",
+                "views/html/index.class",
+                "views/html/main.class",
+                "controllers/Application.class",
+                "application.conf")
+
+        jar("build/distributionJars/playBinary/${playApp.name}.jar").isManifestPresentAndFirstEntry()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayMultiProjectApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayMultiProjectApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..bb3188b
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayMultiProjectApplicationIntegrationTest.groovy
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.play.integtest.fixtures.DistributionTestExecHandleBuilder
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.play.integtest.fixtures.app.PlayMultiProject
+import org.gradle.process.internal.ExecHandle
+import org.gradle.process.internal.ExecHandleBuilder
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.archive.ZipTestFixture
+import org.gradle.util.AvailablePortFinder
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.TextUtil
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+class PlayMultiProjectApplicationIntegrationTest extends AbstractIntegrationSpec {
+    PlayApp playApp = new PlayMultiProject()
+
+    int httpPort
+    def portFinder = AvailablePortFinder.createPrivate()
+
+    def setup() {
+        playApp.writeSources(file("."))
+    }
+
+    def "can build play app binary"() {
+        when:
+        succeeds(":primary:assemble")
+
+        then:
+        executedAndNotSkipped(
+                ":javalibrary:jar",
+                ":submodule:playBinary",
+                ":primary:playBinary",
+                ":primary:assemble")
+
+        and:
+        jar("primary/build/playBinary/lib/primary.jar").containsDescendants(
+                "Routes.class",
+                "controllers/Application.class")
+        jar("primary/build/playBinary/lib/primary-assets.jar").hasDescendants(
+                "public/primary.txt")
+        jar("submodule/build/playBinary/lib/submodule.jar").containsDescendants(
+                "controllers/submodule/Application.class")
+        jar("submodule/build/playBinary/lib/submodule-assets.jar").hasDescendants(
+                "public/submodule.txt")
+
+        when:
+        succeeds(":primary:dist")
+
+        then:
+        zip("primary/build/distributions/playBinary.zip").containsDescendants(
+                "playBinary/lib/primary.jar",
+                "playBinary/lib/primary-assets.jar",
+                "playBinary/lib/submodule.jar",
+                "playBinary/lib/submodule-assets.jar",
+                "playBinary/lib/javalibrary.jar",
+                "playBinary/bin/playBinary",
+                "playBinary/bin/playBinary.bat",
+                "playBinary/conf/application.conf"
+        )
+
+        when:
+        succeeds(":primary:stage")
+
+        then:
+        file("primary/build/stage/playBinary").assertIsDir().assertContainsDescendants(
+                "lib/primary.jar",
+                "lib/primary-assets.jar",
+                "lib/submodule.jar",
+                "lib/submodule-assets.jar",
+                "bin/playBinary",
+                "bin/playBinary.bat",
+                "conf/application.conf"
+        )
+    }
+
+    
+    def "can run play app"(){
+        setup:
+        httpPort = portFinder.nextAvailable
+
+        file("primary/build.gradle") << """
+    model {
+        tasks.runPlayBinary {
+            httpPort = $httpPort
+        }
+    }
+"""
+        run ":primary:assemble"
+
+        when:
+        def userInput = new PipedOutputStream();
+        executer.withStdIn(new PipedInputStream(userInput))
+        GradleHandle gradleHandle = executer.withTasks(":primary:runPlayBinary").start()
+
+        then:
+        def url = playUrl().toString()
+        available(url, "Play app", 60000)
+
+        and:
+        validateRunningApp();
+
+        when: "stopping gradle"
+        userInput.write(4) // ctrl+d
+        userInput.write(TextUtil.toPlatformLineSeparators("\n").bytes) // For some reason flush() doesn't get the keystroke to the DaemonExecuter
+
+        gradleHandle.waitForFinish()
+
+        then: "play server is stopped too"
+        notAvailable(url)
+    }
+
+    @Requires(TestPrecondition.NOT_UNKNOWN_OS)
+    def "can run play distribution" () {
+        println file(".")
+
+        ExecHandle handle
+        String distDirPath = file("primary/build/stage").path
+
+        setup:
+        httpPort = portFinder.nextAvailable
+        run ":primary:stage"
+
+        when:
+        ExecHandleBuilder builder = new DistributionTestExecHandleBuilder(httpPort.toString(), distDirPath)
+        handle = builder.build()
+        handle.start()
+
+        then:
+        available(playUrl().toString(), "Play app", 60000)
+
+        and:
+        validateRunningApp()
+
+        cleanup:
+        ((DistributionTestExecHandleBuilder.DistributionTestExecHandle) handle).shutdown()
+        notAvailable(playUrl().toString())
+    }
+
+    def validateRunningApp() {
+        assertUrlContent playUrl(), "Your new application is ready."
+        assertUrlContent playUrl("assets/primary.txt"), "Primary asset"
+        assertUrlContent playUrl("submodule"), "Submodule page"
+        assertUrlContent playUrl("assets/submodule.txt"), "Submodule asset"
+        true
+    }
+
+    URL playUrl(String path='') {
+        return new URL("http://localhost:$httpPort/${path}")
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+
+    ZipTestFixture zip(String fileName) {
+        new ZipTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayPlatformIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayPlatformIntegrationTest.groovy
new file mode 100644
index 0000000..5f1d226
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayPlatformIntegrationTest.groovy
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.archive.ZipTestFixture
+import spock.lang.Unroll
+
+public class PlayPlatformIntegrationTest extends AbstractIntegrationSpec {
+    PlayApp playApp = new BasicPlayApp()
+
+    def setup() {
+        playApp.writeSources(file("."))
+    }
+
+    def "can build play app binary for default platform"() {
+        when:
+        succeeds("stage")
+
+        then:
+        file("build/stage/playBinary/lib").assertContainsDescendants(
+                "play_2.11-2.3.7.jar"
+        )
+    }
+
+    @Unroll
+    def "can build play app binary for specified platform [#platform]"() {
+        when:
+        buildFile << """
+model {
+    components {
+        play {
+            platform ${platform}
+        }
+    }
+}
+"""
+
+        succeeds("stage")
+
+        then:
+        file("build/stage/playBinary/lib").assertContainsDescendants(
+                "play_${scalaPlatform}-${playVersion}.jar"
+        )
+
+        where:
+        platform                       | playVersion | scalaPlatform
+        "play: '2.2.4'"                | '2.2.4'     | '2.10'
+        "play: '2.2.4', scala: '2.10'" | '2.2.4'     | '2.10'
+
+        "play: '2.3.6'"                | '2.3.6'     | '2.11'
+        "play: '2.3.6', scala: '2.10'" | '2.3.6'     | '2.10'
+        "play: '2.3.6', scala: '2.11'" | '2.3.6'     | '2.11'
+    }
+
+    def "fails when trying to build a Play 2.2.x application with Scala 2.11.x"() {
+        when:
+        buildFile << """
+model {
+    components {
+        play {
+            platform play: '2.2.4', scala: '2.11'
+        }
+    }
+}
+"""
+
+        then:
+        fails "assemble"
+
+        and:
+        failure.assertHasCause "Play versions 2.2.x are not compatible with Scala platform 2.11. Compatible Scala platforms are [2.10]."
+    }
+
+    def "fails when trying to build for an unsupported play platform"() {
+        when:
+        buildFile << """
+model {
+    components {
+        play {
+            platform play: '2.1.0'
+        }
+    }
+}
+"""
+
+        then:
+        fails "assemble"
+
+        and:
+        failure.assertHasCause "Not a supported Play version: 2.1.0. This plugin is compatible with: [2.3.x, 2.2.x]."
+    }
+
+    def "fails when trying to build for an invalid scala platform"() {
+        when:
+        buildFile << """
+model {
+    components {
+        play {
+            platform play: '2.3.6', scala: 'X'
+        }
+    }
+}
+"""
+
+        then:
+        fails "assemble"
+
+        and:
+        failure.assertHasCause "Not a supported Scala platform identifier X. Supported values are: ['2.10', '2.11']."
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+
+    ZipTestFixture zip(String fileName) {
+        new ZipTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayTestApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayTestApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..4f16207
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/PlayTestApplicationIntegrationTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest
+
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.play.integtest.fixtures.PlayMultiVersionApplicationIntegrationTest
+import org.gradle.util.AvailablePortFinder
+
+abstract class PlayTestApplicationIntegrationTest extends PlayMultiVersionApplicationIntegrationTest {
+    def portFinder = AvailablePortFinder.createPrivate()
+
+    def "can run play app tests"() {
+        setup:
+        int testPort = portFinder.nextAvailable
+        buildFile << """
+        model {
+            tasks.testPlayBinary {
+                systemProperty 'testserver.port', $testPort
+            }
+        }
+        """
+
+        when:
+        succeeds("check")
+        then:
+        executed(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary",
+                ":compilePlayBinaryTests",
+                ":testPlayBinary")
+
+        then:
+        verifyTestOutput(new JUnitXmlTestExecutionResult(testDirectory, "build/playBinary/reports/test/xml"))
+
+        when:
+        succeeds("check")
+        then:
+        skipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary",
+                ":compilePlayBinaryTests",
+                ":testPlayBinary")
+    }
+
+    void verifyTestOutput(TestExecutionResult result) { }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/AdvancedAppContentVerifier.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/AdvancedAppContentVerifier.groovy
new file mode 100644
index 0000000..76eb2c3
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/AdvancedAppContentVerifier.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.advanced
+
+import org.gradle.play.integtest.fixtures.PlayMultiVersionRunApplicationIntegrationTest
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+class AdvancedAppContentVerifier {
+    static void verifyRunningApp(PlayMultiVersionRunApplicationIntegrationTest test) {
+        // Custom Routes
+        assert test.playUrl().text.contains("<li>foo:1</li>")
+        assert test.playUrl("root").text.contains("<li>bar:2</li>")
+        assert test.playUrl("java/one").text.contains("Your new application is ready.")
+        assert test.playUrl("scala/one").text.contains("<li>foo:1</li>")
+
+        // Custom Assets
+        assertUrlContent test.playUrl("assets/javascripts/test.js"), test.file("app/assets/javascripts/sample.js")
+        assertUrlContent test.playUrl("assets/javascripts/sample.js"), test.file("app/assets/javascripts/sample.js")
+        assertUrlContent test.playUrl("assets/javascripts/test.min.js"), minifiedSample
+        assertUrlContent test.playUrl("assets/javascripts/sample.min.js"), minifiedSample
+    }
+
+    static String getMinifiedSample() {
+        return "(function(){var c,e,f,b;b=function(a){return a*a};c=[1,2,3,4,5];e={root:Math.sqrt,square:b,cube:function(a){return a*b(a)}};\"undefined\"!==typeof elvis&&null!==elvis&&alert(\"I knew it!\");(function(){var a,b,d;d=[];a=0;for(b=c.length;a<b;a++)f=c[a],d.push(e.cube(f));return d})()}).call(this);"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayBinaryAdvancedAppIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayBinaryAdvancedAppIntegrationTest.groovy
new file mode 100644
index 0000000..cd942d9
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayBinaryAdvancedAppIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.advanced
+
+import org.gradle.play.integtest.PlayBinaryApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.AdvancedPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+
+class PlayBinaryAdvancedAppIntegrationTest extends PlayBinaryApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new AdvancedPlayApp()
+    }
+
+    @Override
+    void verifyJars() {
+        super.verifyJars()
+
+        jar("build/playBinary/lib/${playApp.name}.jar").containsDescendants(
+                "views/html/awesome/index.class",
+                "special/strangename/Application.class",
+                "models/DataType.class",
+                "models/ScalaClass.class",
+                "controllers/scala/MixedJava.class",
+                "controllers/jva/PureJava.class"
+        )
+
+        jar("build/playBinary/lib/${playApp.name}-assets.jar").containsDescendants(
+                "public/javascripts/sample.js",
+                "public/javascripts/sample.min.js",
+                "public/javascripts/test.js",
+                "public/javascripts/test.min.js"
+        )
+    }
+
+    @Override
+    void verifyRunningApp() {
+        super.verifyRunningApp()
+
+        AdvancedAppContentVerifier.verifyRunningApp(this)
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayDistributionAdvancedAppIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayDistributionAdvancedAppIntegrationTest.groovy
new file mode 100644
index 0000000..1c82acb
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/advanced/PlayDistributionAdvancedAppIntegrationTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.advanced
+
+import org.gradle.play.integtest.PlayDistributionApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.AdvancedPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+class PlayDistributionAdvancedAppIntegrationTest extends PlayDistributionApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new AdvancedPlayApp()
+    }
+
+    @Override
+    void verifyZips() {
+        super.verifyZips()
+
+        zip("build/distributions/playBinary.zip").containsDescendants(
+                "playBinary/conf/jva.routes",
+                "playBinary/conf/scala.routes"
+        )
+    }
+
+    @Override
+    void verifyStagedFiles() {
+        super.verifyStagedFiles()
+
+        file("build/stage/playBinary").assertContainsDescendants(
+                "conf/jva.routes",
+                "conf/scala.routes"
+        )
+    }
+
+    @Override
+    void verifyJars() {
+        super.verifyJars()
+
+        jar("build/distributionJars/playBinary/${playApp.name}.jar").containsDescendants(
+                "views/html/awesome/index.class",
+                "special/strangename/Application.class",
+                "models/DataType.class",
+                "models/ScalaClass.class",
+                "controllers/scala/MixedJava.class",
+                "controllers/jva/PureJava.class"
+        )
+    }
+
+    @Override
+    void verifyRunningApp() {
+        super.verifyRunningApp()
+
+        AdvancedAppContentVerifier.verifyRunningApp(this)
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayBinaryBasicAppIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayBinaryBasicAppIntegrationTest.groovy
new file mode 100644
index 0000000..cc63717
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayBinaryBasicAppIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.basic
+
+import org.gradle.play.integtest.PlayBinaryApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+
+class PlayBinaryBasicAppIntegrationTest extends PlayBinaryApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new BasicPlayApp()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayDistributionBasicAppIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayDistributionBasicAppIntegrationTest.groovy
new file mode 100644
index 0000000..3b51697
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayDistributionBasicAppIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.basic
+
+import org.gradle.play.integtest.PlayDistributionApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+
+class PlayDistributionBasicAppIntegrationTest extends PlayDistributionApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new BasicPlayApp()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayTestBasicAppIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayTestBasicAppIntegrationTest.groovy
new file mode 100644
index 0000000..c764005
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/basic/PlayTestBasicAppIntegrationTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.basic
+
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.play.integtest.PlayTestApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.play.integtest.fixtures.app.PlayApp
+
+class PlayTestBasicAppIntegrationTest extends PlayTestApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new BasicPlayApp()
+    }
+
+    @Override
+    void verifyTestOutput(TestExecutionResult result) {
+        result.assertTestClassesExecuted("ApplicationSpec", "IntegrationSpec")
+        result.testClass("ApplicationSpec").assertTestCount(2, 0, 0)
+        result.testClass("IntegrationSpec").assertTestCount(1, 0, 0)
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayBinaryAppWithDependenciesIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayBinaryAppWithDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..f6fe3c6
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayBinaryAppWithDependenciesIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.dependencies
+
+import org.gradle.play.integtest.PlayBinaryApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.play.integtest.fixtures.app.PlayAppWithDependencies
+
+class PlayBinaryAppWithDependenciesIntegrationTest extends PlayBinaryApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new PlayAppWithDependencies()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayDistributionAppWithDependenciesIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayDistributionAppWithDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..9706570
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayDistributionAppWithDependenciesIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.dependencies
+
+import org.gradle.play.integtest.PlayDistributionApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.play.integtest.fixtures.app.PlayAppWithDependencies
+
+class PlayDistributionAppWithDependenciesIntegrationTest extends PlayDistributionApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new PlayAppWithDependencies()
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayTestAppWithDependenciesIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayTestAppWithDependenciesIntegrationTest.groovy
new file mode 100644
index 0000000..a533a97
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/dependencies/PlayTestAppWithDependenciesIntegrationTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.dependencies
+
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.play.integtest.PlayTestApplicationIntegrationTest
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.play.integtest.fixtures.app.PlayAppWithDependencies
+
+class PlayTestAppWithDependenciesIntegrationTest extends PlayTestApplicationIntegrationTest {
+    @Override
+    PlayApp getPlayApp() {
+        return new PlayAppWithDependencies()
+    }
+
+    @Override
+    void verifyTestOutput(TestExecutionResult result) {
+        result.assertTestClassesExecuted("ApplicationSpec", "IntegrationSpec")
+        result.testClass("ApplicationSpec").assertTestCount(2, 0, 0)
+        result.testClass("IntegrationSpec").assertTestCount(1, 0, 0)
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/DistributionTestExecHandleBuilder.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/DistributionTestExecHandleBuilder.groovy
new file mode 100644
index 0000000..62b062c
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/DistributionTestExecHandleBuilder.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures
+
+import com.google.common.collect.Lists
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.process.internal.ExecHandle
+import org.gradle.process.internal.ExecHandleBuilder
+
+class DistributionTestExecHandleBuilder extends ExecHandleBuilder {
+    final String port
+
+    DistributionTestExecHandleBuilder(String port, String baseDirName) {
+        super()
+        this.port = port
+
+        def extension = ""
+        if (OperatingSystem.current().windows) {
+            extension = ".bat"
+        }
+
+        this.setExecutable("${baseDirName}/playBinary/bin/playBinary${extension}")
+        this.environment("PLAY_BINARY_OPTS": "-Dhttp.port=${port}")
+        this.setWorkingDir(baseDirName)
+    }
+
+    @Override
+    List<String> getAllArguments() {
+        return Lists.newArrayList()
+    }
+
+    @Override
+    ExecHandle build() {
+        return new DistributionTestExecHandle(super.build(), port)
+    }
+
+    public static class DistributionTestExecHandle implements ExecHandle {
+        @Delegate
+        final ExecHandle delegate
+        final String port
+
+        public DistributionTestExecHandle(ExecHandle delegate, String port) {
+            this.delegate = delegate
+            this.port = port
+        }
+
+        void shutdown() {
+            try {
+                new URL("http://localhost:${port}/shutdown").bytes
+            } catch (SocketException e) {
+                // Expected
+            }
+
+            try {
+                abort()
+            } catch (IllegalStateException e) {
+                // Ignore if process is already not running
+                println "Did not abort play process since current state is: ${state.toString()}"
+            }
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayCoverage.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayCoverage.groovy
new file mode 100644
index 0000000..8e82b34
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayCoverage.groovy
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures
+
+class PlayCoverage {
+    static final String[] DEFAULT = ["2.2.1", "2.2.6", "2.3.1", "2.3.7"]
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..de7420c
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionApplicationIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures
+
+import org.gradle.play.integtest.fixtures.app.PlayApp
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.archive.ZipTestFixture
+
+abstract class PlayMultiVersionApplicationIntegrationTest extends PlayMultiVersionIntegrationTest {
+    abstract PlayApp getPlayApp()
+
+    def setup() {
+        buildFile << """
+            model {
+                components {
+                    play {
+                        targetPlatform "play-${version}"
+                    }
+                }
+            }
+        """
+
+        playApp.writeSources(file("."))
+        settingsFile << """
+            rootProject.name = '${playApp.name}'
+        """
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+
+    ZipTestFixture zip(String fileName) {
+        new ZipTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionIntegrationTest.groovy
new file mode 100644
index 0000000..7de8af7
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at TargetCoverage({PlayCoverage.DEFAULT})
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+abstract class PlayMultiVersionIntegrationTest extends MultiVersionIntegrationSpec {
+
+
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionRunApplicationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionRunApplicationIntegrationTest.groovy
new file mode 100644
index 0000000..ec8d06b
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/PlayMultiVersionRunApplicationIntegrationTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures
+
+import org.gradle.util.AvailablePortFinder
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+abstract class PlayMultiVersionRunApplicationIntegrationTest extends PlayMultiVersionApplicationIntegrationTest {
+    int httpPort
+    def portFinder = AvailablePortFinder.createPrivate()
+
+    URL playUrl(String path='') {
+        return new URL("http://localhost:$httpPort/${path}")
+    }
+
+    void verifyStarted() {
+        def url = playUrl().toString()
+        available(url, "Play app", 60000)
+        assert playUrl().text.contains("Your new application is ready.")
+    }
+
+    void verifyStopped() {
+        notAvailable(playUrl().toString())
+    }
+
+    void verifyRunningApp() {
+        // Check all static assets from the shared content
+        assertUrlContent playUrl("assets/stylesheets/main.css"), file("public/stylesheets/main.css")
+        assertUrlContent playUrl("assets/javascripts/hello.js"), file("public/javascripts/hello.js")
+        assertBinaryUrlContent playUrl("assets/images/favicon.svg"), file("public/images/favicon.svg")
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/AdvancedPlayApp.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/AdvancedPlayApp.groovy
new file mode 100644
index 0000000..a3b5ca1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/AdvancedPlayApp.groovy
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+class AdvancedPlayApp extends PlayApp {
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/BasicPlayApp.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/BasicPlayApp.groovy
new file mode 100644
index 0000000..5b57b90
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/BasicPlayApp.groovy
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+class BasicPlayApp extends PlayApp {
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayApp.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayApp.groovy
new file mode 100644
index 0000000..f5a6b95
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayApp.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.GFileUtils
+
+abstract class PlayApp {
+
+    String getName() {
+        getClass().getSimpleName().toLowerCase()
+    }
+
+    List<SourceFile> getAllFiles() {
+        return appSources + testSources + viewSources + assetSources + confSources + otherSources
+    }
+
+    SourceFile getGradleBuild() {
+        sourceFile("", "build.gradle")
+    }
+
+    List<SourceFile> getAssetSources() {
+        sourceFiles("public", "shared")
+    }
+
+    List<SourceFile> getAppSources() {
+        return sourceFiles("app").findAll {
+            it.path != "app/views"
+        }
+    }
+
+    List<SourceFile> getViewSources() {
+        return sourceFiles("app/views");
+    }
+
+    List<SourceFile> getConfSources() {
+        return sourceFiles("conf", "shared") + sourceFiles("conf")
+    }
+
+    List<SourceFile> getTestSources() {
+        return sourceFiles("test")
+    }
+
+    List<SourceFile> getOtherSources() {
+        return [ sourceFile("", "README", "shared") ]
+    }
+
+
+    protected SourceFile sourceFile(String path, String name, String baseDir = getName()) {
+        URL resource = getClass().getResource("$baseDir/$path/$name");
+        File file = new File(resource.toURI())
+        return new SourceFile(path, name, file.text);
+    }
+
+    void writeSources(TestFile sourceDir) {
+        gradleBuild.writeToDir(sourceDir)
+        for (SourceFile srcFile : allFiles) {
+            srcFile.writeToDir(sourceDir)
+        }
+    }
+
+    List<SourceFile> sourceFiles(String baseDir, String rootDir = getName()) {
+        List sourceFiles = new ArrayList()
+
+        URL resource = getClass().getResource("$rootDir/$baseDir")
+        if(resource != null){
+            File baseDirFile = new File(resource.toURI())
+            baseDirFile.eachFileRecurse { File source ->
+                if(!source.isDirectory()){
+                    String fileName = source.getName()
+                    def subpath = GFileUtils.relativePath(baseDirFile, source.parentFile);
+                    sourceFiles.add(sourceFile("$baseDir/$subpath", fileName, rootDir))
+                }
+            }
+        }
+
+        return sourceFiles
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayAppWithDependencies.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayAppWithDependencies.groovy
new file mode 100644
index 0000000..8426e2a
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayAppWithDependencies.groovy
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+class PlayAppWithDependencies extends PlayApp {
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayMultiProject.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayMultiProject.groovy
new file mode 100644
index 0000000..ac0057b
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/PlayMultiProject.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class PlayMultiProject extends PlayApp {
+    @Override
+    List<SourceFile> getAllFiles() {
+        return sourceFiles("primary") + sourceFiles("submodule") + sourceFiles("javalibrary") + sourceFile("", "settings.gradle")
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/WithFailingTestsApp.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/WithFailingTestsApp.groovy
new file mode 100644
index 0000000..c2fbab5
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/fixtures/app/WithFailingTestsApp.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.fixtures.app
+
+import org.gradle.integtests.fixtures.SourceFile
+
+class WithFailingTestsApp extends PlayApp {
+    List<SourceFile> appSources
+    List<SourceFile> viewSources
+    List<SourceFile> confSources
+    List<SourceFile> testSources
+
+    @Override
+    SourceFile getGradleBuild() {
+        return sourceFile("", "build.gradle", "basicplayapp")
+    }
+
+    public WithFailingTestsApp(){
+        appSources = sourceFiles("app", "basicplayapp");
+        viewSources = sourceFiles("app/views", "basicplayapp");
+        confSources = sourceFiles("conf", "shared") + sourceFiles("conf", "basicplayapp")
+        testSources = sourceFiles("test") + sourceFiles("test", "basicplayapp")
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AbstractPlaySampleIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AbstractPlaySampleIntegrationTest.groovy
new file mode 100644
index 0000000..928667d
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AbstractPlaySampleIntegrationTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.samples
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.util.AvailablePortFinder
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.TextUtil
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+abstract class AbstractPlaySampleIntegrationTest extends AbstractIntegrationSpec {
+    def portFinder = AvailablePortFinder.createPrivate()
+    def initScript
+    int httpPort
+
+    abstract Sample getPlaySample();
+
+    void checkContent() {
+        assertUrlContentContains playUrl(), "Your new application is ready."
+        assertUrlContent playUrl("assets/stylesheets/main.css"), publicAsset("stylesheets/main.css")
+        assertUrlContent playUrl("assets/javascripts/hello.js"), publicAsset("javascripts/hello.js")
+        assertBinaryUrlContent playUrl("assets/images/favicon.png"), publicAsset("images/favicon.png")
+    }
+
+    def setup() {
+        httpPort = portFinder.nextAvailable
+        initScript = file("initFile") << """
+            gradle.allprojects {
+                tasks.withType(PlayRun) {
+                    httpPort = $httpPort
+                }
+            }
+        """
+    }
+
+    def "produces usable application" () {
+        when:
+        executer.usingInitScript(initScript)
+        sample playSample
+
+        // Assemble first so that build time doesn't play into the startup timeout
+        then:
+        succeeds "assemble"
+
+        when:
+        sample playSample
+        def userInput = new PipedOutputStream();
+        executer.withStdIn(new PipedInputStream(userInput))
+        executer.usingInitScript(initScript)
+        GradleHandle gradleHandle = executer.withTasks(":runPlayBinary").start()
+
+        then:
+        available("http://localhost:$httpPort", "Play app", 60000)
+
+        and:
+        checkContent()
+
+        when:
+        stopWithCtrlD(userInput, gradleHandle)
+
+        then: "play server is stopped too"
+        notAvailable("http://localhost:$httpPort")
+    }
+
+    static stopWithCtrlD(PipedOutputStream userInput, GradleHandle gradleHandle) {
+        userInput.write(4) // ctrl+d
+        userInput.write(TextUtil.toPlatformLineSeparators("\n").bytes) // For some reason flush() doesn't get the keystroke to the DaemonExecuter
+        gradleHandle.waitForFinish()
+    }
+
+    URL playUrl(String path='') {
+        return new URL("http://localhost:$httpPort/${path}")
+    }
+
+    File publicAsset(String asset) {
+        return new File(playSample.dir, "public/${asset}")
+    }
+
+    File appAsset(String asset) {
+        return new File(playSample.dir, "app/assets/${asset}")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AdvancedPlaySampleIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AdvancedPlaySampleIntegrationTest.groovy
new file mode 100644
index 0000000..755c8a2
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/AdvancedPlaySampleIntegrationTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.samples
+
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Rule
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+class AdvancedPlaySampleIntegrationTest extends AbstractPlaySampleIntegrationTest {
+    @Rule
+    Sample advancedPlaySample = new Sample(temporaryFolder, "play/advanced")
+
+    Sample getPlaySample() {
+        return advancedPlaySample
+    }
+
+    @Override
+    void checkContent() {
+        super.checkContent()
+        assertUrlContent playUrl("assets/javascripts/sample.js"), appAsset("javascripts/sample.js")
+        assertUrlContent playUrl("assets/coffeescript/console.js"), coffeeScriptGeneratedJavaScript
+        assertUrlContent playUrl("hello/gradle"), "Hello Gradle!"
+        assert playUrl("square").text.contains("Square it!")
+        assert playUrl("questions").text.contains("What is your quest?")
+    }
+
+    String getCoffeeScriptGeneratedJavaScript() {
+        return """(function() {
+  console.log("This is coffeescript!");
+
+}).call(this);
+"""
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/BasicPlaySampleIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/BasicPlaySampleIntegrationTest.groovy
new file mode 100644
index 0000000..0494285
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/BasicPlaySampleIntegrationTest.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.samples
+
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Rule
+
+class BasicPlaySampleIntegrationTest extends AbstractPlaySampleIntegrationTest {
+    @Rule
+    Sample basicPlaySample = new Sample(temporaryFolder, "play/basic")
+
+    Sample getPlaySample() {
+        return basicPlaySample
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/MultiprojectPlaySampleIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/MultiprojectPlaySampleIntegrationTest.groovy
new file mode 100644
index 0000000..1c9e6d9
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/integtest/samples/MultiprojectPlaySampleIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.integtest.samples
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.junit.Rule
+
+import static org.gradle.integtests.fixtures.UrlValidator.*
+
+class MultiprojectPlaySampleIntegrationTest extends AbstractPlaySampleIntegrationTest {
+    @Rule
+    Sample multiprojectSample = new Sample(temporaryFolder, "play/multiproject")
+
+    Sample getPlaySample() {
+        return multiprojectSample
+    }
+
+    @Override
+    void checkContent() {
+        assertUrlContentContains playUrl(), "Here is a multiproject app! (built by Gradle)"
+        assertUrlContent playUrl("assets/javascript/timestamp.js"), publicAsset("javascript/timestamp.js")
+        assertBinaryUrlContent playUrl("assets/images/gradle.ico"), publicAsset("images/gradle.ico")
+
+        checkAdminModuleContent()
+   }
+
+    private void checkAdminModuleContent() {
+        assertUrlContentContains playUrl("admin"), "Here is the ADMIN module. (built by Gradle)"
+        assertUrlContent playUrl("admin/assets/javascript/admin.js"), moduleAsset("admin", "javascript/admin.js")
+    }
+
+    File moduleAsset(String module, String asset) {
+        return new File(playSample.dir, "modules/${module}/public/${asset}")
+    }
+
+    def "can run module subproject independently" () {
+        when:
+        executer.usingInitScript(initScript)
+        sample playSample
+
+        // Assemble first so that build time doesn't play into the startup timeout
+        then:
+        succeeds ":admin:assemble"
+
+        when:
+        sample playSample
+        def userInput = new PipedOutputStream();
+        executer.withStdIn(new PipedInputStream(userInput))
+        executer.usingInitScript(initScript)
+        GradleHandle gradleHandle = executer.withTasks(":admin:runPlayBinary").start()
+
+        then:
+        available("http://localhost:$httpPort/admin", "Play app", 60000)
+
+        and:
+        checkAdminModuleContent()
+
+        when:
+        stopWithCtrlD(userInput, gradleHandle)
+
+        then: "play server is stopped too"
+        notAvailable("http://localhost:$httpPort/admin")
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginGoodBehaviourIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginGoodBehaviourIntegrationTest.groovy
new file mode 100644
index 0000000..853c86e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginGoodBehaviourIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class PlayApplicationPluginGoodBehaviourIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getMainTask() {
+        // assemble task will fail because the binary is not 100% buildable without a repository
+        return "playBinary"
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginIntegrationTest.groovy
new file mode 100644
index 0000000..e78e6a2
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayApplicationPluginIntegrationTest.groovy
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import com.sun.xml.internal.ws.util.StringUtils
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import spock.lang.Unroll
+
+class PlayApplicationPluginIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule
+    public final TestResources resources = new TestResources(temporaryFolder)
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'play-app' """
+        buildFile << """
+        plugins {
+            id 'play-application'
+        }
+
+        repositories{
+            jcenter()
+            maven{
+                name = "typesafe-maven-release"
+                url = "https://repo.typesafe.com/typesafe/maven-releases"
+            }
+        }
+"""
+    }
+
+    def "can register PlayApplicationSpec component"() {
+        when:
+        succeeds "components"
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators("""
+Play Application 'play'
+-----------------------
+
+Source sets
+    Java source 'play:java'
+        app
+        includes: **/*.java
+    JVM resources 'play:resources'
+        conf
+    Routes source 'play:routesSources'
+        conf
+        includes: routes, *.routes
+    Scala source 'play:scala'
+        app
+        includes: **/*.scala
+    Twirl template source 'play:twirlTemplates'
+        app
+        includes: **/*.html
+
+Binaries
+    Play Application Jar 'playBinary'
+        build using task: :playBinary
+        platform: Play Platform (Play 2.3.7, Scala: 2.11, Java: Java SE ${JavaVersion.current().majorVersion})"""))
+    }
+
+    def "cannot register multiple PlayApplicationSpec components"() {
+        given:
+        buildFile << """
+        model {
+             components {
+                 myOtherApp(PlayApplicationSpec)
+             }
+        }
+"""
+        when:
+        fails "components"
+
+        then:
+        failure.assertHasDescription("A problem occurred configuring root project 'play-app'.")
+        failure.assertHasCause("Multiple components of type 'PlayApplicationSpec' are not supported.")
+    }
+
+    def "builds empty play binary when no sources present"() {
+        when:
+        succeeds("assemble")
+
+        then:
+        executedAndNotSkipped(
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary",
+                ":assemble")
+        skipped(":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary")
+
+        and:
+        jar("build/playBinary/lib/play-app.jar").hasDescendants()
+        jar("build/playBinary/lib/play-app-assets.jar").hasDescendants()
+    }
+
+    @Unroll
+    def "can declare additional #languageName sourceSets"() {
+        given:
+        buildFile << """
+        model {
+            components {
+                play {
+                    sources {
+                        extra($sourceSetType) {
+                            source.srcDir "src/extra"
+                        }
+                    }
+                }
+            }
+        }
+"""
+        and:
+        file("src/extra/org/acme/model/Person.${languageName}") << """
+            package org.acme.model;
+            class Person {
+            }
+"""
+
+        when:
+        succeeds("components")
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators("""
+Play Application 'play'
+-----------------------
+
+Source sets
+    ${StringUtils.capitalize(languageName)} source 'play:extra'
+        src${File.separator}extra
+    Java source 'play:java'
+        app
+        includes: **/*.java
+    JVM resources 'play:resources'
+        conf
+    Routes source 'play:routesSources'
+        conf
+        includes: routes, *.routes
+    Scala source 'play:scala'
+        app
+        includes: **/*.scala
+    Twirl template source 'play:twirlTemplates'
+        app
+"""))
+
+        when:
+        succeeds("assemble")
+
+        then:
+        executedAndNotSkipped(
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":scalaCompilePlayBinary",
+                ":playBinary",
+                ":assemble")
+        skipped(":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary")
+
+        and:
+        jar("build/playBinary/lib/play-app.jar").hasDescendants("org/acme/model/Person.class")
+        jar("build/playBinary/lib/play-app-assets.jar").hasDescendants()
+
+        where:
+
+        languageName | sourceSetType
+        "scala"      | "ScalaLanguageSourceSet"
+        "java"       | "JavaSourceSet"
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginGoodBehaviourIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginGoodBehaviourIntegrationTest.groovy
new file mode 100644
index 0000000..ca36690
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginGoodBehaviourIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.play.plugins
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class PlayCoffeeScriptPluginGoodBehaviourIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        return 'play-coffeescript'
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginIntegrationTest.groovy
new file mode 100644
index 0000000..a220ce0
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginIntegrationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.TextUtil
+import org.junit.Rule
+
+class PlayCoffeeScriptPluginIntegrationTest extends AbstractIntegrationSpec {
+    @Rule
+    public final TestResources resources = new TestResources(temporaryFolder)
+
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'play-application'
+                id 'play-coffeescript'
+            }
+
+            model {
+                components {
+                    play {
+                        sources {
+                            otherCoffeeScript(CoffeeScriptSourceSet)
+                        }
+                    }
+                }
+            }
+        """
+    }
+
+    def "coffeescript source set appears in component listing"() {
+        when:
+        succeeds "components"
+
+        then:
+        normalizedOutput.contains("""
+    CoffeeScript source 'play:coffeeScriptAssets'
+        app/assets
+        includes: **/*.coffee""")
+        normalizedOutput.contains("""
+    CoffeeScript source 'play:otherCoffeeScript'
+        src/play/otherCoffeeScript
+""")
+    }
+
+    def "creates and configures compile task when source exists"() {
+        buildFile << """
+            task checkTasks {
+                doLast {
+                    assert tasks.withType(CoffeeScriptCompile).size() == 2
+                    tasks.withType(CoffeeScriptCompile)*.name as Set == ["compilePlayBinaryCoffeeScriptAssets", "compilePlayBinaryOtherCoffeeScript"] as Set
+                }
+            }
+        """
+
+        when:
+        file("app/assets/test.coffee") << "test"
+        file("src/play/otherCoffeeScript/other.coffee") << "test"
+
+        then:
+        succeeds "checkTasks"
+    }
+
+    def "does not create compile task when source does not exist"() {
+        buildFile << """
+            task checkTasks {
+                doLast {
+                    assert tasks.withType(CoffeeScriptCompile).size() == 0
+                }
+            }
+        """
+
+        expect:
+        succeeds "checkTasks"
+    }
+
+    private String getNormalizedOutput() {
+        return TextUtil.normaliseLineSeparators(TextUtil.normaliseFileSeparators(output))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayDistributionPluginIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayDistributionPluginIntegrationTest.groovy
new file mode 100644
index 0000000..c43cda5
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayDistributionPluginIntegrationTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.fixtures.archive.ZipTestFixture
+import org.junit.Rule
+
+class PlayDistributionPluginIntegrationTest extends AbstractIntegrationSpec {
+    @Rule
+    public final TestResources resources = new TestResources(temporaryFolder)
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'dist-play-app' """
+        buildFile << """
+            plugins {
+                id 'play'
+            }
+
+            repositories {
+                jcenter()
+                maven{
+                    name = "typesafe-maven-release"
+                    url = "https://repo.typesafe.com/typesafe/maven-releases"
+                }
+            }
+        """
+    }
+
+    def "builds empty distribution when no sources present" () {
+        buildFile << """
+            model {
+                tasks.createPlayBinaryStartScripts {
+                    doLast {
+                        assert classpath.contains(file(createPlayBinaryDistributionJar.archivePath))
+                    }
+                }
+                tasks.createPlayBinaryDist {
+                    doLast {
+                        def zipFileNames = zipTree(archivePath).collect { it.name }
+                        configurations.playRun.collect { it.name }.each { assert zipFileNames.contains(it) }
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "stage"
+
+        then:
+        executedAndNotSkipped(
+                ":createPlayBinaryJar",
+                ":createPlayBinaryDistributionJar",
+                ":createPlayBinaryAssetsJar",
+                ":createPlayBinaryStartScripts",
+                ":stagePlayBinaryDist")
+        skipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary")
+
+        and:
+        file("build/stage/playBinary").assertContainsDescendants(
+                "lib/dist-play-app.jar",
+                "lib/dist-play-app-assets.jar",
+                "bin/playBinary",
+                "bin/playBinary.bat"
+        )
+        if (OperatingSystem.current().linux || OperatingSystem.current().macOsX) {
+            assert file("build/stage/playBinary/bin/playBinary").mode == 0755
+        }
+
+        when:
+        succeeds "dist"
+
+        then:
+        executedAndNotSkipped(":createPlayBinaryDist")
+        skipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":scalaCompilePlayBinary",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryDistributionJar",
+                ":createPlayBinaryAssetsJar",
+                ":createPlayBinaryStartScripts")
+
+        and:
+        zip("build/distributions/playBinary.zip").containsDescendants(
+                "playBinary/lib/dist-play-app.jar",
+                "playBinary/lib/dist-play-app-assets.jar",
+                "playBinary/bin/playBinary",
+                "playBinary/bin/playBinary.bat"
+        )
+    }
+
+    ZipTestFixture zip(String path) {
+        return new ZipTestFixture(file(path))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginGoodBehaviourIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginGoodBehaviourIntegrationTest.groovy
new file mode 100644
index 0000000..d36abb8
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginGoodBehaviourIntegrationTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.play.plugins
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class PlayJavaScriptPluginGoodBehaviourIntegrationTest extends WellBehavedPluginTest {
+
+    @Override
+    String getPluginName() {
+        return 'play-javascript'
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginIntegrationTest.groovy
new file mode 100644
index 0000000..06b9750
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/plugins/PlayJavaScriptPluginIntegrationTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+
+class PlayJavaScriptPluginIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'play-application'
+                id 'play-javascript'
+            }
+
+            model {
+                components {
+                    play {
+                        sources {
+                            otherJavaScript(JavaScriptSourceSet)
+                        }
+                    }
+                }
+            }
+        """
+    }
+
+    def "javascript source sets appear in component listing"() {
+        when:
+        succeeds "components"
+
+        then:
+        normalizedOutput.contains("""
+    JavaScript source 'play:javaScriptAssets'
+        app/assets
+        includes: **/*.js
+    JavaScript source 'play:otherJavaScript'
+        src/play/otherJavaScript
+""")
+    }
+
+    def "creates and configures minify task when source exists"() {
+        buildFile << """
+            task checkTasks {
+                doLast {
+                    tasks.withType(JavaScriptMinify)*.name as Set == ["minifyPlayBinaryJavaScriptAssets", "minifyPlayBinaryOtherJavaScript"] as Set
+                }
+            }
+        """
+
+        when:
+        file("app/assets/test.js") << "test"
+        file("src/play/otherJavaScript/other.js") << "test"
+
+        then:
+        succeeds "checkTasks"
+    }
+
+    def "does not create minify task when source does not exist"() {
+        buildFile << """
+            task checkTasks {
+                doLast {
+                    assert tasks.withType(JavaScriptMinify).size() == 0
+                }
+            }
+        """
+
+        expect:
+        succeeds "checkTasks"
+    }
+
+    private String getNormalizedOutput() {
+        return TextUtil.normaliseLineSeparators(TextUtil.normaliseFileSeparators(output))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractCoffeeScriptCompileIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractCoffeeScriptCompileIntegrationTest.groovy
new file mode 100644
index 0000000..54eab77
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractCoffeeScriptCompileIntegrationTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.gradle.test.fixtures.file.TestFile
+
+abstract class AbstractCoffeeScriptCompileIntegrationTest extends AbstractJavaScriptMinifyIntegrationTest {
+
+    TestFile compiled(String fileName) {
+        return compiled(getDefaultSourceSet(), fileName)
+    }
+
+    TestFile compiled(String sourceSet, String fileName) {
+        file("build/playBinary/src/compilePlayBinary${sourceSet}/${fileName}")
+    }
+
+    void matchesExpectedRaw(String fileName) {
+        matchesExpectedRaw(getDefaultSourceSet(), fileName)
+    }
+
+    void matchesExpectedRaw(String sourceSet, String fileName) {
+        assert compiled(sourceSet, fileName).exists()
+        matchesExpectedRaw(compiled(sourceSet, fileName))
+    }
+
+    @Override
+    TestFile copied(String fileName) {
+        return super.copied("${defaultSourceSet}JavaScript", fileName)
+    }
+
+    @Override
+    void matchesExpected(String fileName) {
+        super.matchesExpected("${defaultSourceSet}JavaScript", fileName)
+    }
+
+    @Override
+    TestFile minified(String fileName) {
+        return super.minified("${defaultSourceSet}JavaScript", fileName)
+    }
+
+    def withCoffeeScriptSource(String path) {
+        withCoffeeScriptSource(file(path))
+    }
+
+    def withCoffeeScriptSource(File file) {
+        file << coffeeScriptSource()
+    }
+
+    def coffeeScriptSource() {
+        return """
+# Assignment:
+number   = 42
+opposite = true
+
+# Conditions:
+number = -42 if opposite
+
+# Functions:
+square = (x) -> x * x
+
+# Arrays:
+list = [1, 2, 3, 4, 5]
+
+# Objects:
+math =
+  root:   Math.sqrt
+  square: square
+  cube:   (x) -> x * square x
+
+# Splats:
+race = (winner, runners...) ->
+  print winner, runners
+
+# Existence:
+alert "I knew it!" if elvis?
+
+# Array comprehensions:
+cubes = (math.cube num for num in list)"""
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractJavaScriptMinifyIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractJavaScriptMinifyIntegrationTest.groovy
new file mode 100644
index 0000000..9ca5ec1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/AbstractJavaScriptMinifyIntegrationTest.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.TextUtil
+
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+abstract class AbstractJavaScriptMinifyIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'js-play-app' """
+    }
+
+    abstract String getDefaultSourceSet();
+
+    JarTestFixture getAssetsJar() {
+        jar("build/playBinary/lib/js-play-app-assets.jar")
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+
+    TestFile assets(String fileName) {
+        file("app/assets/${fileName}")
+    }
+
+    TestFile copied(String fileName) {
+        return minified(fileName)
+    }
+
+    TestFile copied(String sourceSet, String fileName) {
+        return minified(sourceSet, fileName)
+    }
+
+    TestFile minified(String fileName) {
+        return minified(getDefaultSourceSet(), fileName)
+    }
+
+    TestFile minified(String sourceSet, String fileName) {
+        file("build/playBinary/src/minifyPlayBinary${sourceSet}/${fileName}")
+    }
+
+    void matchesExpected(File file) {
+        assert TextUtil.normaliseLineSeparators(file.text) == TextUtil.normaliseLineSeparators(expectedMinifiedJavaScript())
+    }
+
+    void matchesExpected(String fileName) {
+        matchesExpected(getDefaultSourceSet(), fileName)
+    }
+
+    void matchesExpected(String sourceSet, String fileName) {
+        assert minified(sourceSet, fileName).exists()
+        matchesExpected(minified(sourceSet, fileName))
+    }
+
+    void matchesExpectedRaw(File file) {
+        assert compareWithoutWhiteSpace(file.text, expectedJavaScript())
+    }
+
+    boolean compareWithoutWhiteSpace(String string1, String string2) {
+        return withoutWhiteSpace(string1) == withoutWhiteSpace(string2)
+    }
+
+    def withoutWhiteSpace(String string) {
+        return string.replaceAll("\\s+", " ");
+    }
+
+    def withJavaScriptSource(String path) {
+        withJavaScriptSource(file(path))
+    }
+
+    def withJavaScriptSource(File file) {
+        file << expectedJavaScript()
+    }
+
+    def expectedJavaScript() {
+        return """(function() {
+var cubes, list, math, num, number, opposite, race, square,
+  __slice = [].slice;
+
+number = 42;
+
+opposite = true;
+
+if (opposite) {
+  number = -42;
+}
+
+square = function(x) {
+  return x * x;
+};
+
+list = [1, 2, 3, 4, 5];
+
+math = {
+  root: Math.sqrt,
+  square: square,
+  cube: function(x) {
+    return x * square(x);
+  }
+};
+
+race = function() {
+  var runners, winner;
+  winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+  return print(winner, runners);
+};
+
+if (typeof elvis !== "undefined" && elvis !== null) {
+  alert("I knew it!");
+}
+
+cubes = (function() {
+  var _i, _len, _results;
+  _results = [];
+  for (_i = 0, _len = list.length; _i < _len; _i++) {
+    num = list[_i];
+    _results.push(math.cube(num));
+  }
+  return _results;
+})();
+}).call(this);
+"""
+    }
+
+    def expectedMinifiedJavaScript() {
+        return "(function(){var c,e,f,b;b=function(a){return a*a};c=[1,2,3,4,5];e={root:Math.sqrt,square:b,cube:function(a){return a*b(a)}};\"undefined\"!==typeof elvis&&null!==elvis&&alert(\"I knew it!\");(function(){var a,b,d;d=[];a=0;for(b=c.length;a<b;a++)f=c[a],d.push(e.cube(f));return d})()}).call(this);"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CoffeeScriptCompileIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CoffeeScriptCompileIntegrationTest.groovy
new file mode 100644
index 0000000..14be7d1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CoffeeScriptCompileIntegrationTest.groovy
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+class CoffeeScriptCompileIntegrationTest extends AbstractCoffeeScriptCompileIntegrationTest {
+    @Override
+    String getDefaultSourceSet() {
+        return "CoffeeScriptAssets"
+    }
+
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'play'
+                id 'play-coffeescript'
+            }
+
+            repositories{
+                jcenter()
+                maven{
+                    name = "typesafe-maven-release"
+                    url = "https://repo.typesafe.com/typesafe/maven-releases"
+                }
+                maven {
+                    name = "gradle-js"
+                    url = "https://repo.gradle.org/gradle/javascript-public"
+                }
+            }
+        """
+    }
+
+    def "compiles default coffeescript source set as part of play application build" () {
+        when:
+        withCoffeeScriptSource(assets("test.coffee"))
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":compilePlayBinaryCoffeeScriptAssets",
+                ":minifyPlayBinaryCoffeeScriptAssetsJavaScript",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        matchesExpectedRaw("test.js")
+        matchesExpectedRaw(copied("test.js"))
+        matchesExpected("test.min.js")
+        assetsJar.containsDescendants(
+                "public/test.js",
+                "public/test.min.js"
+        )
+    }
+
+    def "minify task depends on compile task" () {
+        when:
+        withCoffeeScriptSource(assets("test.coffee"))
+        succeeds "minifyPlayBinaryCoffeeScriptAssetsJavaScript"
+
+        then:
+        executedAndNotSkipped ":compilePlayBinaryCoffeeScriptAssets"
+    }
+
+    def "compiles multiple coffeescript source sets as part of play application build" () {
+        given:
+        withCoffeeScriptSource(assets("test1.coffee"))
+        withCoffeeScriptSource("src/play/extraCoffeeScript/xxx/test2.coffee")
+        withCoffeeScriptSource("extra/a/b/c/test3.coffee")
+        withJavaScriptSource('src/play/extraJavaScript/test/test4.js')
+        withJavaScriptSource(assets("test5.js"))
+
+        when:
+        buildFile << """
+            model {
+                components {
+                    play {
+                        sources {
+                            extraCoffeeScript(CoffeeScriptSourceSet)
+                            anotherCoffeeScript(CoffeeScriptSourceSet) {
+                                source.srcDir "extra"
+                            }
+                            extraJavaScript(JavaScriptSourceSet)
+                        }
+                    }
+                }
+            }
+        """
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":compilePlayBinaryCoffeeScriptAssets",
+                ":minifyPlayBinaryCoffeeScriptAssetsJavaScript",
+                ":compilePlayBinaryExtraCoffeeScript",
+                ":minifyPlayBinaryExtraCoffeeScriptJavaScript",
+                ":compilePlayBinaryAnotherCoffeeScript",
+                ":minifyPlayBinaryAnotherCoffeeScriptJavaScript",
+                ":minifyPlayBinaryJavaScriptAssets",
+                ":minifyPlayBinaryExtraJavaScript",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        matchesExpectedRaw("test1.js")
+        matchesExpectedRaw("ExtraCoffeeScript", "xxx/test2.js")
+        matchesExpectedRaw("AnotherCoffeeScript", "a/b/c/test3.js")
+        matchesExpectedRaw(copied("test1.js"))
+        matchesExpectedRaw(copied("ExtraCoffeeScriptJavaScript", "xxx/test2.js"))
+        matchesExpectedRaw(copied("AnotherCoffeeScriptJavaScript", "a/b/c/test3.js"))
+        matchesExpected("test1.min.js")
+        matchesExpected("ExtraCoffeeScriptJavaScript", "xxx/test2.min.js")
+        matchesExpected("AnotherCoffeeScriptJavaScript", "a/b/c/test3.min.js")
+        assetsJar.containsDescendants(
+                "public/test1.js",
+                "public/xxx/test2.js",
+                "public/a/b/c/test3.js",
+                "public/test/test4.js",
+                "public/test5.js",
+                "public/test1.min.js",
+                "public/xxx/test2.min.js",
+                "public/a/b/c/test3.min.js",
+                "public/test/test4.min.js",
+                "public/test5.min.js"
+        )
+    }
+
+    def "does not recompile when inputs and outputs are unchanged" () {
+        given:
+        withCoffeeScriptSource(assets("test.coffee"))
+        succeeds "assemble"
+
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped(":compilePlayBinaryCoffeeScriptAssets",
+                ":minifyPlayBinaryCoffeeScriptAssetsJavaScript",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+    }
+
+    def "recompiles when inputs are changed" () {
+        given:
+        withCoffeeScriptSource(assets("test.coffee"))
+        succeeds "assemble"
+
+        when:
+        assets("test.coffee") << '\nalert "this is a change!"'
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":compilePlayBinaryCoffeeScriptAssets",
+                ":minifyPlayBinaryCoffeeScriptAssetsJavaScript",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+    }
+
+    def "recompiles when outputs are removed" () {
+        given:
+        withCoffeeScriptSource(assets("test.coffee"))
+        succeeds "assemble"
+
+        when:
+        compiled("test.js").delete()
+        copied("test.js").delete()
+        minified("test.min.js").delete()
+        assetsJar.file.delete()
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":compilePlayBinaryCoffeeScriptAssets",
+                ":minifyPlayBinaryCoffeeScriptAssetsJavaScript",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        compiled("test.js").exists()
+        copied("test.js").exists()
+        minified("test.min.js").exists()
+    }
+
+    def "cleans removed source file on compile" () {
+        given:
+        withCoffeeScriptSource(assets("test1.coffee"))
+        def source2 = withCoffeeScriptSource(assets("test2.coffee"))
+
+        when:
+        succeeds "assemble"
+
+        then:
+        assetsJar.containsDescendants(
+                "public/test1.js",
+                "public/test2.js",
+                "public/test1.min.js",
+                "public/test2.min.js"
+        )
+
+        when:
+        source2.delete()
+        succeeds "assemble"
+
+        then:
+        ! compiled("test2.js").exists()
+        ! copied("test2.js").exists()
+        ! minified("test2.min.js").exists()
+        assetsJar.countFiles("public/test2.js") == 0
+        assetsJar.countFiles("public/test2.min.js") == 0
+    }
+
+    def "produces sensible error on compile failure" () {
+        given:
+        assets("test1.coffee") << "if"
+
+        when:
+        fails "assemble"
+
+        then:
+        failure.assertHasDescription "Execution failed for task ':compilePlayBinaryCoffeeScriptAssets'."
+        failure.assertHasCause "Failed to compile coffeescript file: test1.coffee"
+        failure.assertHasCause "SyntaxError: unexpected if (coffee-script-js-1.8.0.js#10)"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CustomCoffeeScriptImplementationIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CustomCoffeeScriptImplementationIntegrationTest.groovy
new file mode 100644
index 0000000..856a478
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/CustomCoffeeScriptImplementationIntegrationTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+class CustomCoffeeScriptImplementationIntegrationTest extends AbstractCoffeeScriptCompileIntegrationTest {
+    def customCoffeeScriptImplFileName
+
+    @Override
+    String getDefaultSourceSet() {
+        return "CoffeeScriptAssets"
+    }
+
+    def setup() {
+        customCoffeeScriptImplFileName = 'coffeescript/coffee-script.min.js'
+        file(customCoffeeScriptImplFileName) << getClass().getResource("/coffee-script.min.js").text
+
+        withCoffeeScriptSource('app/assets/test.coffee')
+        withCoffeeScriptSource('src/play/extraCoffeeScriptAssets/test2.coffee')
+        buildFile << """
+            plugins {
+                id 'play'
+                id 'play-coffeescript'
+            }
+
+            repositories{
+                jcenter()
+            }
+        """
+    }
+
+    def "can compile coffeescript with a custom implementation from file"() {
+        buildFile << """
+            model {
+                components {
+                    play {
+                        sources {
+                            extraCoffeeScriptAssets(CoffeeScriptSourceSet)
+                        }
+                        binaries.all {
+                            tasks.withType(PlayCoffeeScriptCompile) {
+                                coffeeScriptJs = files("${customCoffeeScriptImplFileName}")
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "compilePlayBinaryCoffeeScriptAssets", "compilePlayBinaryExtraCoffeeScriptAssets"
+
+        then:
+        matchesExpectedRaw('test.js')
+        matchesExpectedRaw('ExtraCoffeeScriptAssets', 'test2.js')
+    }
+
+    def "can compile coffeescript with a custom implementation from configuration"() {
+        buildFile << """
+            configurations {
+                coffeeScript
+            }
+
+            dependencies {
+                coffeeScript files("${customCoffeeScriptImplFileName}")
+            }
+
+            model {
+                components {
+                    play {
+                        sources {
+                            extraCoffeeScriptAssets(CoffeeScriptSourceSet)
+                        }
+                        binaries.all {
+                            tasks.withType(PlayCoffeeScriptCompile) {
+                                coffeeScriptJs = configurations.coffeeScript
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "compilePlayBinaryCoffeeScriptAssets", "compilePlayBinaryExtraCoffeeScriptAssets"
+
+        then:
+        matchesExpectedRaw('test.js')
+        matchesExpectedRaw('ExtraCoffeeScriptAssets', 'test2.js')
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/DistributionZipIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/DistributionZipIntegrationTest.groovy
new file mode 100644
index 0000000..813547e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/DistributionZipIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.archive.ZipTestFixture
+
+class DistributionZipIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        settingsFile << """ rootProject.name = 'dist-play-app' """
+        buildFile << """
+            plugins {
+                id 'play'
+            }
+
+            repositories {
+                jcenter()
+                maven{
+                    name = "typesafe-maven-release"
+                    url = "https://repo.typesafe.com/typesafe/maven-releases"
+                }
+            }
+        """
+    }
+
+    def "can add to default distribution" () {
+        buildFile << """
+            model {
+                distributions {
+                    playBinary {
+                        contents {
+                            from "additionalFile.txt"
+                        }
+                    }
+                }
+            }
+        """
+        file("additionalFile.txt").createFile()
+
+        when:
+        succeeds "dist"
+
+        then:
+        zip("build/distributions/playBinary.zip").containsDescendants("playBinary/additionalFile.txt")
+    }
+
+    def "cannot add arbitrary distribution" () {
+        buildFile << """
+            model {
+                distributions {
+                    myDist { }
+                }
+            }
+        """
+
+        when:
+        fails "dist"
+
+        then:
+        failureDescriptionContains("A problem occurred configuring root project 'dist-play-app'")
+        failureHasCause("Cannot create a Distribution named 'myDist' because this container does not support creating elements by name alone.")
+    }
+
+    ZipTestFixture zip(String path) {
+        return new ZipTestFixture(file(path))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/JavaScriptMinifyIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/JavaScriptMinifyIntegrationTest.groovy
new file mode 100644
index 0000000..ff0d382
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/JavaScriptMinifyIntegrationTest.groovy
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.hamcrest.Matchers
+
+class JavaScriptMinifyIntegrationTest extends AbstractJavaScriptMinifyIntegrationTest {
+    @Override
+    String getDefaultSourceSet() {
+        return "JavaScriptAssets"
+    }
+
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'play-application'
+                id 'play-javascript'
+            }
+
+            repositories{
+                jcenter()
+                maven{
+                    name = "typesafe-maven-release"
+                    url = "https://repo.typesafe.com/typesafe/maven-releases"
+                }
+            }
+        """
+    }
+
+    def "minifies default javascript source set as part of play application build"() {
+        given:
+        withJavaScriptSource("app/assets/test.js")
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":minifyPlayBinaryJavaScriptAssets",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        minified("test.min.js").exists()
+        copied("test.js").exists()
+        assetsJar.containsDescendants(
+                "public/test.min.js",
+                "public/test.js"
+        )
+
+        and:
+        matchesExpected("test.min.js")
+    }
+
+    def "does not re-minify when inputs and outputs are unchanged"() {
+        given:
+        withJavaScriptSource("app/assets/test.js")
+        succeeds "assemble"
+
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped(":minifyPlayBinaryJavaScriptAssets",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+    }
+
+    def "re-minifies when an output is removed" () {
+        given:
+        withJavaScriptSource("app/assets/test.js")
+        succeeds "assemble"
+
+        // Detects missing output
+        when:
+        minified("test.min.js").delete()
+        assetsJar.file.delete()
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":minifyPlayBinaryJavaScriptAssets",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        minified("test.min.js").exists()
+    }
+
+    def "re-minifies when an input is changed" () {
+        given:
+        withJavaScriptSource("app/assets/test.js")
+        succeeds "assemble"
+
+        // Detects changed input
+        when:
+        file("app/assets/test.js") << "alert('this is a change!');"
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":minifyPlayBinaryJavaScriptAssets",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+    }
+
+    def "cleans removed source file on minify" () {
+        given:
+        withJavaScriptSource("app/assets/test1.js")
+        def source2 = withJavaScriptSource("app/assets/test2.js")
+
+        when:
+        succeeds "assemble"
+
+        then:
+        minified("test1.min.js").exists()
+        minified("test2.min.js").exists()
+        copied("test1.js").exists()
+        copied("test2.js").exists()
+        assetsJar.containsDescendants(
+                "public/test1.min.js",
+                "public/test2.min.js",
+                "public/test1.js",
+                "public/test2.js"
+        )
+
+        when:
+        source2.delete()
+        succeeds "assemble"
+
+        then:
+        ! minified("test2.min.js").exists()
+        ! copied("test2.js").exists()
+        assetsJar.countFiles("public/test2.min.js") == 0
+        assetsJar.countFiles("public/test2.js") == 0
+    }
+
+    def "minifies multiple javascript source sets as part of play application build" () {
+        given:
+        withJavaScriptSource("app/assets/test1.js")
+        withJavaScriptSource("extra/javascripts/test2.js")
+        withJavaScriptSource("src/play/anotherJavaScript/a/b/c/test3.js")
+
+        buildFile << """
+            model {
+                components {
+                    play {
+                        sources {
+                            extraJavaScript(JavaScriptSourceSet) {
+                                source.srcDir "extra"
+                            }
+                            anotherJavaScript(JavaScriptSourceSet)
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":minifyPlayBinaryJavaScriptAssets",
+                ":minifyPlayBinaryExtraJavaScript",
+                ":minifyPlayBinaryAnotherJavaScript",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+        matchesExpected("test1.min.js")
+        matchesExpected("ExtraJavaScript", "javascripts/test2.min.js")
+        matchesExpected("AnotherJavaScript", "a/b/c/test3.min.js")
+        matchesExpectedRaw(minified("test1.js"))
+        matchesExpectedRaw(minified("ExtraJavaScript", "javascripts/test2.js"))
+        matchesExpectedRaw(minified("AnotherJavaScript", "a/b/c/test3.js"))
+        assetsJar.containsDescendants(
+                "public/test1.min.js",
+                "public/javascripts/test2.min.js",
+                "public/a/b/c/test3.min.js",
+                "public/test1.js",
+                "public/javascripts/test2.js",
+                "public/a/b/c/test3.js"
+        )
+
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped(":minifyPlayBinaryJavaScriptAssets",
+                ":minifyPlayBinaryExtraJavaScript",
+                ":minifyPlayBinaryAnotherJavaScript",
+                ":createPlayBinaryJar",
+                ":createPlayBinaryAssetsJar",
+                ":playBinary")
+    }
+
+    def "produces sensible error on minify failure"() {
+        given:
+        file("app/assets/javascripts/test1.js") << "BAD SOURCE"
+        file("app/assets/javascripts/test2.js") << "BAD SOURCE"
+        withJavaScriptSource("app/assets/javascripts/hello.js")
+
+        when:
+        fails "assemble"
+
+        then:
+        minified("javascripts/hello.min.js").exists()
+        copied("javascripts/hello.js").exists()
+        failure.assertHasDescription("Execution failed for task ':minifyPlayBinaryJavaScriptAssets'.")
+
+        String slash = File.separator
+        failure.assertThatCause(Matchers.allOf([
+                Matchers.startsWith("Minification failed with the following errors:"),
+                Matchers.containsString("app${slash}assets${slash}javascripts${slash}test1.js line 1 : 4"),
+                Matchers.containsString("app${slash}assets${slash}javascripts${slash}test2.js line 1 : 4")
+        ]))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/PlayAssetsJarIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/PlayAssetsJarIntegrationTest.groovy
new file mode 100644
index 0000000..18e5730
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/PlayAssetsJarIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.play.integtest.fixtures.app.BasicPlayApp
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at Requires(TestPrecondition.JDK7_OR_LATER)
+class PlayAssetsJarIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        new BasicPlayApp().writeSources(file("."))
+        settingsFile << """ rootProject.name = 'play-app' """
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":createPlayBinaryJar", ":createPlayBinaryAssetsJar"
+        jar("build/playBinary/lib/play-app-assets.jar").containsDescendants(
+                "public/images/favicon.svg",
+                "public/stylesheets/main.css",
+                "public/javascripts/hello.js")
+    }
+
+    def "does not rebuild when public assets remain unchanged" () {
+        when:
+        succeeds "assemble"
+
+        then:
+        skipped ":createPlayBinaryJar", ":createPlayBinaryAssetsJar"
+    }
+
+    def "rebuilds when public assets change" () {
+        when:
+        file("public/stylesheets/main.css") << "\n"
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":createPlayBinaryAssetsJar"
+        skipped ":createPlayBinaryJar"
+
+        and:
+        jar("build/playBinary/lib/play-app-assets.jar").assertFileContent("public/stylesheets/main.css", file("public/stylesheets/main.css").text)
+    }
+
+    def "rebuilds when public assets are removed" () {
+        when:
+        file("public/stylesheets/main.css").delete()
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped ":createPlayBinaryAssetsJar"
+        skipped ":createPlayBinaryJar"
+
+        and:
+        jar("build/playBinary/lib/play-app-assets.jar").countFiles("public/stylesheets/main.css") == 0
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/RoutesCompileIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/RoutesCompileIntegrationTest.groovy
new file mode 100644
index 0000000..b0bb07c
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/RoutesCompileIntegrationTest.groovy
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.play.tasks
+
+import org.apache.commons.lang.StringUtils
+import org.gradle.play.integtest.fixtures.PlayMultiVersionIntegrationTest
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.TextUtil
+
+class RoutesCompileIntegrationTest extends PlayMultiVersionIntegrationTest {
+    def destinationDirPath = "build/playBinary/src/routesCompileRoutesSourcesPlayBinary"
+    def destinationDir = file(destinationDirPath)
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'routes-play-app' """
+        buildFile <<"""
+plugins {
+    id 'play-application'
+}
+
+model {
+    components {
+        play {
+            targetPlatform "play-${version}"
+        }
+    }
+}
+
+repositories{
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+}
+"""
+    }
+
+    def "can run RoutesCompile"() {
+        given:
+        withRoutesTemplate()
+        expect:
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala")
+    }
+
+    def "runs compiler multiple times"(){
+        when:
+        withRoutesTemplate()
+        then:
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala")
+
+        withRoutesTemplate("foo")
+        and:
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+        then:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala",
+                "controllers/foo/routes.java", "foo/routes_reverseRouting.scala", "foo/routes_routing.scala")
+
+        when:
+        file("conf/foo.routes").delete()
+        then:
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala")
+    }
+
+    def "removes stale output files in incremental compile"(){
+        given:
+        TestFile templateFile = withRoutesTemplate()
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+
+        and:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala")
+        def routesFirstCompileSnapshot = file(destinationDirPath, "controllers/routes.java").snapshot();
+        def revRoutingFirstCompileSnapshot = file(destinationDirPath, "routes_reverseRouting.scala").snapshot();
+        def routingFirstCompileSnapshot = file(destinationDirPath, "routes_routing.scala").snapshot();
+
+        when:
+        templateFile.delete()
+
+        then:
+        succeeds("routesCompileRoutesSourcesPlayBinary")
+        and:
+        file(destinationDirPath, "controllers/routes.java").assertHasNotChangedSince(routesFirstCompileSnapshot);
+        file(destinationDirPath, "routes_reverseRouting.scala").assertHasNotChangedSince(revRoutingFirstCompileSnapshot);
+        file(destinationDirPath, "routes_routing.scala").assertHasNotChangedSince(routingFirstCompileSnapshot);
+    }
+
+    def "compiles multiple Routes source sets as part of play application build" () {
+        withExtraSourceSets()
+        withRoutesTemplate()
+        withRoutesSource(file("extraRoutes", "some", "pkg", "some.pkg.routes"), ".some.pkg")
+        withRoutesSource(file("otherRoutes", "other", "other.routes"), ".other")
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":routesCompileRoutesSourcesPlayBinary",
+                ":routesCompileExtraRoutesPlayBinary",
+                ":routesCompileOtherRoutesPlayBinary"
+        )
+
+        and:
+        destinationDir.assertHasDescendants("controllers/routes.java", "routes_reverseRouting.scala", "routes_routing.scala")
+        destinationDir("extraRoutes").assertHasDescendants("controllers/some/pkg/routes.java", "some/pkg/routes_reverseRouting.scala", "some/pkg/routes_routing.scala")
+        destinationDir("otherRoutes").assertHasDescendants("controllers/other/routes.java", "other/routes_reverseRouting.scala", "other/routes_routing.scala")
+
+        and:
+        jar("build/playBinary/lib/routes-play-app.jar").containsDescendants("controllers/routes.class")
+        jar("build/playBinary/lib/routes-play-app.jar").containsDescendants("controllers/some/pkg/routes.class")
+        jar("build/playBinary/lib/routes-play-app.jar").containsDescendants("controllers/other/routes.class")
+    }
+
+    def "extra route sources appear in the components report" () {
+        withExtraSourceSets()
+
+        when:
+        succeeds "components"
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators("""
+Play Application 'play'
+-----------------------
+
+Source sets
+    Routes source 'play:extraRoutes'
+        extraRoutes
+    Java source 'play:java'
+        app
+        includes: **/*.java
+    Routes source 'play:otherRoutes'
+        otherRoutes
+    JVM resources 'play:resources'
+        conf
+    Routes source 'play:routesSources'
+        conf
+        includes: routes, *.routes
+    Scala source 'play:scala'
+        app
+        includes: **/*.scala
+    Twirl template source 'play:twirlTemplates'
+        app
+        includes: **/*.html
+
+Binaries
+"""))
+    }
+
+    def destinationDir(String sourceSetName) {
+        return file("build/playBinary/src/routesCompile${StringUtils.capitalize(sourceSetName)}PlayBinary")
+    }
+
+    def withRoutesSource(TestFile routesFile, String packageId) {
+        routesFile.createFile()
+        routesFile << """
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# Home page
+GET     /                          controllers${packageId}.Application.index()
+"""
+        withControllerSource(file("app/controllers/${packageId}/Application.scala"), packageId)
+        return routesFile
+    }
+
+    def withControllerSource(TestFile file, String packageId) {
+        file.createFile()
+        file << """
+package controllers${packageId}
+
+
+import play.api._
+import play.api.mvc._
+import models._
+
+object Application extends Controller {
+  def index = Action {
+    Ok("Your new application is ready.")
+  }
+}
+"""
+    }
+
+    def withRoutesTemplate(String packageName = "") {
+        def routesFile = packageName.isEmpty() ? file("conf", "routes") : file("conf", packageName + ".routes")
+        def packageId = packageName.isEmpty() ? "" : ".$packageName"
+        withRoutesSource(routesFile, packageId)
+    }
+
+    def withExtraSourceSets() {
+        buildFile << """
+            model {
+                components {
+                    play {
+                        sources {
+                            extraRoutes(RoutesSourceSet) {
+                                source.srcDir "extraRoutes"
+                            }
+                            otherRoutes(RoutesSourceSet) {
+                                source.srcDir "otherRoutes"
+                            }
+                        }
+                    }
+                }
+            }
+        """
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlCompileIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlCompileIntegrationTest.groovy
new file mode 100644
index 0000000..0ff0c21
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlCompileIntegrationTest.groovy
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.gradle.play.integtest.fixtures.PlayMultiVersionIntegrationTest
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.util.TextUtil
+
+class TwirlCompileIntegrationTest extends PlayMultiVersionIntegrationTest {
+    def destinationDirPath = "build/playBinary/src/twirlCompileTwirlTemplatesPlayBinary/views/html"
+    def destinationDir = file(destinationDirPath)
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'twirl-play-app' """
+        buildFile << """
+            plugins {
+                id 'play-application'
+            }
+
+            repositories{
+                jcenter()
+                maven{
+                    name = "typesafe-maven-release"
+                    url = "https://repo.typesafe.com/typesafe/maven-releases"
+                }
+            }
+
+            model {
+                components {
+                    play {
+                        targetPlatform "play-${version}"
+                    }
+                }
+            }
+        """
+    }
+
+    def "can run TwirlCompile"() {
+        given:
+        withTwirlTemplate()
+        when:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        then:
+        destinationDir.assertHasDescendants("index.template.scala")
+
+        when:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        then:
+        skipped(":twirlCompileTwirlTemplatesPlayBinary");
+    }
+
+    def "runs compiler incrementally"() {
+        when:
+        withTwirlTemplate("input1.scala.html")
+        then:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("input1.template.scala")
+        def input1FirstCompileSnapshot = file("${destinationDirPath}/input1.template.scala").snapshot();
+
+        when:
+        withTwirlTemplate("input2.scala.html")
+        and:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        then:
+        destinationDir.assertHasDescendants("input1.template.scala", "input2.template.scala")
+        and:
+        file("${destinationDirPath}/input1.template.scala").assertHasNotChangedSince(input1FirstCompileSnapshot)
+
+        when:
+        file("app/views/input2.scala.html").delete()
+        then:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("input1.template.scala")
+    }
+
+    def "removes stale output files in incremental compile"(){
+        given:
+        withTwirlTemplate("input1.scala.html")
+        withTwirlTemplate("input2.scala.html")
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+
+        and:
+        destinationDir.assertHasDescendants("input1.template.scala", "input2.template.scala")
+        def input1FirstCompileSnapshot = file("${destinationDirPath}/input1.template.scala").snapshot();
+
+        when:
+        file("app/views/input2.scala.html").delete()
+
+        then:
+        succeeds("twirlCompileTwirlTemplatesPlayBinary")
+        and:
+        destinationDir.assertHasDescendants("input1.template.scala")
+        file("${destinationDirPath}/input1.template.scala").assertHasNotChangedSince(input1FirstCompileSnapshot);
+        file("${destinationDirPath}/input2.template.scala").assertDoesNotExist()
+    }
+
+    def "builds multiple twirl source sets as part of play build" () {
+        withExtraSourceSets()
+        withTemplateSource(file("app", "views", "index.scala.html"))
+        withTemplateSource(file("otherSources", "templates", "other.scala.html"))
+        withTemplateSource(file("extraSources", "extra.scala.html"))
+
+        when:
+        succeeds "assemble"
+
+        then:
+        executedAndNotSkipped(
+                ":twirlCompileTwirlTemplatesPlayBinary",
+                ":twirlCompileExtraTwirlPlayBinary",
+                ":twirlCompileOtherTwirlPlayBinary"
+        )
+
+        and:
+        destinationDir.assertHasDescendants("index.template.scala")
+        file("build/playBinary/src/twirlCompileOtherTwirlPlayBinary/templates/html").assertHasDescendants("other.template.scala")
+        file("build/playBinary/src/twirlCompileExtraTwirlPlayBinary/html").assertHasDescendants("extra.template.scala")
+
+        and:
+        jar("build/playBinary/lib/twirl-play-app.jar").assertContainsFile("views/html/index.class")
+        jar("build/playBinary/lib/twirl-play-app.jar").assertContainsFile("templates/html/other.class")
+        jar("build/playBinary/lib/twirl-play-app.jar").assertContainsFile("html/extra.class")
+    }
+
+    def "extra sources appear in the component report"() {
+        withExtraSourceSets()
+
+        when:
+        succeeds "components"
+
+        then:
+        output.contains(TextUtil.toPlatformLineSeparators("""
+Play Application 'play'
+-----------------------
+
+Source sets
+    Twirl template source 'play:extraTwirl'
+        extraSources
+    Java source 'play:java'
+        app
+        includes: **/*.java
+    Twirl template source 'play:otherTwirl'
+        otherSources
+    JVM resources 'play:resources'
+        conf
+    Routes source 'play:routesSources'
+        conf
+        includes: routes, *.routes
+    Scala source 'play:scala'
+        app
+        includes: **/*.scala
+    Twirl template source 'play:twirlTemplates'
+        app
+        includes: **/*.html
+
+Binaries
+"""))
+
+    }
+
+
+    def withTemplateSource(File templateFile) {
+        templateFile << """@(message: String)
+
+    @play20.welcome(message)
+
+"""
+    }
+
+    def withTwirlTemplate(String fileName = "index.scala.html") {
+        def templateFile = file("app", "views", fileName)
+        templateFile.createFile()
+        withTemplateSource(templateFile)
+    }
+
+    def withExtraSourceSets() {
+        buildFile << """
+            model {
+                components {
+                    play {
+                        sources {
+                            extraTwirl(TwirlSourceSet) {
+                                source.srcDir "extraSources"
+                            }
+                            otherTwirl(TwirlSourceSet) {
+                                source.srcDir "otherSources"
+                            }
+                        }
+                    }
+                }
+            }
+        """
+    }
+
+    JarTestFixture jar(String fileName) {
+        new JarTestFixture(file(fileName))
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlVersionIntegrationTest.groovy b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlVersionIntegrationTest.groovy
new file mode 100644
index 0000000..a11cf26
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlVersionIntegrationTest.groovy
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class TwirlVersionIntegrationTest extends AbstractIntegrationSpec {
+    def baseBuildFile = """
+        plugins {
+            id 'play-application'
+        }
+
+        repositories{
+            jcenter()
+            maven{
+                name = "typesafe-maven-release"
+                url = "https://repo.typesafe.com/typesafe/maven-releases"
+            }
+        }
+    """
+
+    def setup() {
+        settingsFile << """ rootProject.name = 'twirl-play-app' """
+    }
+
+    def "changing between twirl-incompatible versions of play causes Twirl to recompile" () {
+        withPlayVersion("2.2.1")
+        withTemplateSource(file("app", "views", "index.scala.html"))
+
+        when:
+        succeeds "playBinary"
+
+        then:
+        executedAndNotSkipped(":twirlCompileTwirlTemplatesPlayBinary", ":scalaCompilePlayBinary")
+
+        and:
+        file("build/playBinary/src/twirlCompileTwirlTemplatesPlayBinary/views/html/index.template.scala").exists()
+
+        when:
+        withPlayVersion("2.3.7")
+        succeeds "playBinary"
+
+        then:
+        executedAndNotSkipped(":twirlCompileTwirlTemplatesPlayBinary", ":scalaCompilePlayBinary")
+
+        and:
+        file("build/playBinary/src/twirlCompileTwirlTemplatesPlayBinary/views/html/index.template.scala").exists()
+    }
+
+    def "changing between twirl-compatible versions of play does NOT cause Twirl to recompile" () {
+        withPlayVersion("2.3.1")
+        withTemplateSource(file("app", "views", "index.scala.html"))
+
+        when:
+        succeeds "playBinary"
+
+        then:
+        executedAndNotSkipped(":twirlCompileTwirlTemplatesPlayBinary", ":scalaCompilePlayBinary")
+
+        and:
+        file("build/playBinary/src/twirlCompileTwirlTemplatesPlayBinary/views/html/index.template.scala").exists()
+
+        when:
+        withPlayVersion("2.3.7")
+        succeeds "playBinary"
+
+        then:
+        skipped(":twirlCompileTwirlTemplatesPlayBinary")
+        executedAndNotSkipped(":scalaCompilePlayBinary")
+    }
+
+    def withPlayVersion(String playVersion) {
+        buildFile.delete()
+        buildFile << """
+            $baseBuildFile
+
+            model {
+                components {
+                    play {
+                        targetPlatform "play-${playVersion}"
+                    }
+                }
+            }
+        """
+    }
+
+    def withTemplateSource(File templateFile) {
+        templateFile << """@(message: String)
+
+            @play20.welcome(message)
+
+        """
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/coffee-script.min.js b/subprojects/platform-play/src/integTest/resources/coffee-script.min.js
new file mode 100644
index 0000000..0e63a65
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/coffee-script.min.js
@@ -0,0 +1,12 @@
+/**
+ * CoffeeScript Compiler v1.8.0
+ * http://coffeescript.org
+ *
+ * Copyright 2011, Jeremy Ashkenas
+ * Released under the MIT License
+ */
+(function(root){var CoffeeScript=function(){function require(e){return require[e]}return require["./helpers"]=function(){var e={},t={exports:e};return function(){var t,n,i,r,s,o,a;e.starts=function(e,t,n){return t===e.substr(n,t.length)},e.ends=function(e,t,n){var i;return i=t.length,t===e.substr(e.length-i-(n||0),i)},e.repeat=s=function(e,t){var n;for(n="";t>0;)1&t&&(n+=e),t>>>=1,e+=e;return n},e.compact=function(e){var t,n,i,r;for(r=[],n=0,i=e.length;i>n;n++)t=e[n],t&&r.push(t);return  [...]
+break;case 31:this.$=i.addLocationDataFn(o[a],o[a])(new i.Literal(s[a]));break;case 32:this.$=i.addLocationDataFn(o[a],o[a])(new i.Undefined);break;case 33:this.$=i.addLocationDataFn(o[a],o[a])(new i.Null);break;case 34:this.$=i.addLocationDataFn(o[a],o[a])(new i.Bool(s[a]));break;case 35:this.$=i.addLocationDataFn(o[a-2],o[a])(new i.Assign(s[a-2],s[a]));break;case 36:this.$=i.addLocationDataFn(o[a-3],o[a])(new i.Assign(s[a-3],s[a]));break;case 37:this.$=i.addLocationDataFn(o[a-4],o[a])( [...]
+this.trace(e)},parse:function(e){function t(){var e;return e=n.lexer.lex()||p,"number"!=typeof e&&(e=n.symbols_[e]||e),e}var n=this,i=[0],r=[null],s=[],o=this.table,a="",c=0,h=0,l=0,u=2,p=1,d=s.slice.call(arguments,1);this.lexer.setInput(e),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,this.lexer.yylloc===void 0&&(this.lexer.yylloc={});var f=this.lexer.yylloc;s.push(f);var m=this.lexer.options&&this.lexer.options.ranges;this.parseError="function"==typeof this.yy.pars [...]
+},n.prototype.compilePatternMatch=function(e){var i,r,s,o,a,c,h,l,u,d,f,m,g,k,y,v,w,F,L,E,S,R,A,I,_,$,O,B;if(v=e.level===x,F=this.value,m=this.variable.base.objects,!(g=m.length))return s=F.compileToFragments(e),e.level>=N?this.wrapInBraces(s):s;if(l=this.variable.isObject(),v&&1===g&&!((f=m[0])instanceof G))return f instanceof n?(A=f,I=A.variable,h=I.base,f=A.value):h=l?f["this"]?f.properties[0].name:f:new D(0),i=b.test(h.unwrap().value||0),F=new Z(F),F.properties.push(new(i?t:T)(h)),_= [...]
+null==t&&(t=!1),s=o.readFileSync(e,"utf8"),a=65279===s.charCodeAt(0)?s.substring(1):s;try{n=i(a,{filename:e,sourceMap:t,literate:c.isLiterate(e)})}catch(h){throw r=h,c.updateSyntaxError(r,a,e)}return n},h=new t,l.lexer={lex:function(){var e,t;return t=this.tokens[this.pos++],t?(e=t[0],this.yytext=t[1],this.yylloc=t[2],this.errorToken=t.origin||t,this.yylineno=this.yylloc.first_line):e="",e},setInput:function(e){return this.tokens=e,this.pos=0},upcomingInput:function(){return""}},l.yy=req [...]
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/sample.js b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/sample.js
new file mode 100644
index 0000000..295d573
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/sample.js
@@ -0,0 +1,47 @@
+(function() {
+  var cubes, list, math, num, number, opposite, race, square,
+    __slice = [].slice;
+
+  number = 42;
+
+  opposite = true;
+
+  if (opposite) {
+    number = -42;
+  }
+
+  square = function(x) {
+    return x * x;
+  };
+
+  list = [1, 2, 3, 4, 5];
+
+  math = {
+    root: Math.sqrt,
+    square: square,
+    cube: function(x) {
+      return x * square(x);
+    }
+  };
+
+  race = function() {
+    var runners, winner;
+    winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+    return print(winner, runners);
+  };
+
+  if (typeof elvis !== "undefined" && elvis !== null) {
+    alert("I knew it!");
+  }
+
+  cubes = (function() {
+    var _i, _len, _results;
+    _results = [];
+    for (_i = 0, _len = list.length; _i < _len; _i++) {
+      num = list[_i];
+      _results.push(math.cube(num));
+    }
+    return _results;
+  })();
+
+}).call(this);
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/test.coffee b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/test.coffee
new file mode 100644
index 0000000..52b30e1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/assets/javascripts/test.coffee
@@ -0,0 +1,28 @@
+# Assignment:
+number   = 42
+opposite = true
+
+# Conditions:
+number = -42 if opposite
+
+# Functions:
+square = (x) -> x * x
+
+# Arrays:
+list = [1, 2, 3, 4, 5]
+
+# Objects:
+math =
+    root:   Math.sqrt
+    square: square
+    cube:   (x) -> x * square x
+
+# Splats:
+race = (winner, runners...) ->
+    print winner, runners
+
+# Existence:
+alert "I knew it!" if elvis?
+
+# Array comprehensions:
+cubes = (math.cube num for num in list)
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/Application.scala
new file mode 100644
index 0000000..152794e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/Application.scala
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+
+import play.api._
+import play.api.mvc._
+import models._
+
+object Application extends Controller {
+  def index = Action {
+    Ok(views.html.index("Your new application is ready."))
+  }
+  def root = Action {
+    Ok(views.html.awesome.index(List(new DataType("bar", 2))))
+  }
+  def shutdown = Action {
+    System.exit(0)
+    Ok("shutdown")
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/jva/PureJava.java b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/jva/PureJava.java
new file mode 100644
index 0000000..df3d0a0
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/jva/PureJava.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package controllers.jva;
+
+import play.*;
+import play.mvc.*;
+import views.html.*;
+
+public class PureJava extends Controller {
+
+    public static Result index() {
+        return ok(index.render("Your new application is ready."));
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/scala/MixedJava.java b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/scala/MixedJava.java
new file mode 100644
index 0000000..b6fe43f
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/controllers/scala/MixedJava.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers.scala;
+
+import play.*;
+import play.mvc.*;
+import views.html.*;
+
+public class MixedJava extends Controller {
+
+    public static Result index() {
+        System.out.println(new models.ScalaClass("Java can also reference Scala files"));
+        return ok(index.render("Your new mixed application is ready."));
+    }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/DataType.java b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/DataType.java
new file mode 100644
index 0000000..ccef193
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/DataType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package models;
+
+public class DataType {
+    private final String foo;
+    private final int bar;
+
+    public DataType(String foo, int bar) {
+        this.foo = foo;
+        this.bar = bar;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s:%s", foo, bar);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/ScalaClass.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/ScalaClass.scala
new file mode 100644
index 0000000..155c670
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/models/ScalaClass.scala
@@ -0,0 +1,3 @@
+package models
+
+class ScalaClass(val name: String)
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/special/strangename/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/special/strangename/Application.scala
new file mode 100644
index 0000000..0607725
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/special/strangename/Application.scala
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package special.strangename
+import play.api._
+import play.api.mvc._
+object Application extends Controller {
+  def index = Action {
+    Ok(views.html.index("Your new application is ready."))
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/awesome/index.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/awesome/index.scala.html
new file mode 100644
index 0000000..7cf2ba9
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/awesome/index.scala.html
@@ -0,0 +1,7 @@
+@(stuff: List[DataType])
+
+<ul>
+    @for(s <- stuff) {
+    <li>@s</li>
+    }
+</ul>
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/index.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/index.scala.html
new file mode 100644
index 0000000..f11549c
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/index.scala.html
@@ -0,0 +1,9 @@
+@(message: String)
+
+ at main("Welcome to Play") {
+
+ at play20.welcome(message)
+
+ at awesome.index(List(new DataType("foo", 1)))
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/main.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/main.scala.html
new file mode 100644
index 0000000..96aabbe
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/app/views/main.scala.html
@@ -0,0 +1,13 @@
+@(title: String)(content: Html)
+<!DOCTYPE html>
+<html>
+<head>
+    <title>@title</title>
+    @**<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
+    <link rel="shortcut icon" type="image/svg" href="@routes.Assets.at("images/favicon.svg")">
+    <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>**@
+</head>
+<body>
+ at content
+</body>
+</html>
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/build.gradle
new file mode 100644
index 0000000..1b1f91e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+    id 'play'
+    id 'play-coffeescript'
+}
+
+repositories {
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+    maven {
+        name = "gradle-js"
+        url = "https://repo.gradle.org/gradle/javascript-public"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/jva.routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/jva.routes
new file mode 100644
index 0000000..5adbc17
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/jva.routes
@@ -0,0 +1 @@
+GET        /one         controllers.jva.PureJava.index
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/routes
new file mode 100644
index 0000000..d63c4f3
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/routes
@@ -0,0 +1,10 @@
+# Routes
+GET /          controllers.Application.index
+GET /root      controllers.Application.root
+GET /shutdown  controllers.Application.shutdown
+
+->  /scala     scala.Routes
+->  /java      jva.Routes
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/scala.routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/scala.routes
new file mode 100644
index 0000000..e29b95f
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/advancedplayapp/conf/scala.routes
@@ -0,0 +1,2 @@
+GET        /one         controllers.scala.MixedJava.index
+POST       /two         special.strangename.Application.index
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/controllers/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/controllers/Application.scala
new file mode 100644
index 0000000..0d6bc74
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/controllers/Application.scala
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import play.api._
+import play.api.mvc._
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok(views.html.index("Your new application is ready."))
+  }
+
+  def shutdown = Action {
+    System.exit(0)
+    Ok("shutdown")
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/index.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/index.scala.html
new file mode 100644
index 0000000..596bf24
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/index.scala.html
@@ -0,0 +1,7 @@
+@(message: String)
+
+ at main("Welcome to Play") {
+
+    @play20.welcome(message)
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/main.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/main.scala.html
new file mode 100644
index 0000000..d245c51
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/app/views/main.scala.html
@@ -0,0 +1,13 @@
+@(title: String)(content: Html)
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
+    </head>
+    <body>
+ at content
+    </body>
+</html>
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/build.gradle
new file mode 100644
index 0000000..b373f49
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/build.gradle
@@ -0,0 +1,11 @@
+plugins {
+    id 'play'
+}
+
+repositories {
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/conf/routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/conf/routes
new file mode 100644
index 0000000..784cdbe
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/conf/routes
@@ -0,0 +1,9 @@
+# Routes
+# Home page
+GET     /                           controllers.Application.index
+
+GET     /shutdown                   controllers.Application.shutdown
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
+
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/ApplicationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/ApplicationSpec.scala
new file mode 100644
index 0000000..12e93c1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/ApplicationSpec.scala
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+import play.api.test._
+import play.api.test.Helpers._
+ at RunWith(classOf[JUnitRunner])
+class ApplicationSpec extends Specification {
+  "Application" should {
+    "send 404 on a bad request" in new WithApplication {
+      route(FakeRequest(GET, "/boum")) must beNone
+    }
+    "render the index page" in new WithApplication {
+      val home = route(FakeRequest(GET, "/")).get
+      status(home) must equalTo(OK)
+      contentType(home) must beSome.which(_ == "text/html")
+      contentAsString(home) must contain("Your new application is ready.")
+    }
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/IntegrationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/IntegrationSpec.scala
new file mode 100644
index 0000000..ea6c8d9
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/basicplayapp/test/IntegrationSpec.scala
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+import play.api.test._
+import play.api.test.Helpers._
+/**
+ * add your integration spec here.
+ * An integration test will fire up a whole play appl
+ */
+ at RunWith(classOf[JUnitRunner])
+class IntegrationSpec extends Specification {
+    "Application" should {
+        "work from within a browser" in new WithBrowser {
+            browser.goTo("http://localhost:" + port)
+            browser.pageSource must contain("Your new application is ready.")
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/controllers/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/controllers/Application.scala
new file mode 100644
index 0000000..b984a17
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/controllers/Application.scala
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import com.google.common.base.Strings
+import play.api._
+import play.api.mvc._
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok(views.html.index(Strings.nullToEmpty("Your new application is ready.")))
+  }
+
+  def shutdown = Action {
+    System.exit(0)
+    Ok("shutdown")
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/index.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/index.scala.html
new file mode 100644
index 0000000..596bf24
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/index.scala.html
@@ -0,0 +1,7 @@
+@(message: String)
+
+ at main("Welcome to Play") {
+
+    @play20.welcome(message)
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/main.scala.html b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/main.scala.html
new file mode 100644
index 0000000..d245c51
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/app/views/main.scala.html
@@ -0,0 +1,13 @@
+@(title: String)(content: Html)
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>@title</title>
+        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
+        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
+        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
+    </head>
+    <body>
+ at content
+    </body>
+</html>
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/build.gradle
new file mode 100644
index 0000000..b36eff1
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+    id 'play'
+}
+
+dependencies {
+    play "com.google.guava:guava:17.0"
+    playTest "commons-lang:commons-lang:2.6"
+}
+
+repositories {
+    jcenter()
+    maven{
+        name = "typesafe-maven-release"
+        url = "https://repo.typesafe.com/typesafe/maven-releases"
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/conf/routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/conf/routes
new file mode 100644
index 0000000..784cdbe
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/conf/routes
@@ -0,0 +1,9 @@
+# Routes
+# Home page
+GET     /                           controllers.Application.index
+
+GET     /shutdown                   controllers.Application.shutdown
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file               controllers.Assets.at(path="/public", file)
+
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/ApplicationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/ApplicationSpec.scala
new file mode 100644
index 0000000..fb589fc
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/ApplicationSpec.scala
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+import play.api.test._
+import play.api.test.Helpers._
+
+ at RunWith(classOf[JUnitRunner])
+class ApplicationSpec extends Specification {
+  "Application" should {
+    "send 404 on a bad request" in new WithApplication {
+      route(FakeRequest(GET, "/boum")) must beNone
+    }
+    "render the index page" in new WithApplication {
+      val home = route(FakeRequest(GET, "/")).get
+      status(home) must equalTo(OK)
+      contentType(home) must beSome.which(_ == "text/html")
+      contentAsString(home) must contain("Your new application is ready.")
+    }
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/IntegrationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/IntegrationSpec.scala
new file mode 100644
index 0000000..ff9620b
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playappwithdependencies/test/IntegrationSpec.scala
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+import play.api.test._
+import play.api.test.Helpers._
+
+import com.google.common.base.Strings
+import org.apache.commons.lang.StringUtils
+
+/**
+ * add your integration spec here.
+ * An integration test will fire up a whole play appl
+ */
+ at RunWith(classOf[JUnitRunner])
+class IntegrationSpec extends Specification {
+    "Application" should {
+        "work from within a browser" in new WithBrowser {
+            browser.goTo("http://localhost:" + port)
+            browser.pageSource must contain(StringUtils.strip(Strings.nullToEmpty("  Your new application is ready.   ")))
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/build.gradle
new file mode 100644
index 0000000..d54c456
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/build.gradle
@@ -0,0 +1,9 @@
+allprojects {
+    repositories{
+        jcenter()
+        maven{
+            name = "typesafe-maven-release"
+            url = "https://repo.typesafe.com/typesafe/maven-releases"
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/build.gradle
new file mode 100644
index 0000000..c5fc4ba
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+    id 'java'
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/src/main/java/org/test/Util.java b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/src/main/java/org/test/Util.java
new file mode 100644
index 0000000..6f6e183
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/javalibrary/src/main/java/org/test/Util.java
@@ -0,0 +1,7 @@
+package org.test;
+
+public class Util {
+    public static String fullStop(String input) {
+        return input + ".";
+    }
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/app/controllers/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/app/controllers/Application.scala
new file mode 100644
index 0000000..f3124eb
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/app/controllers/Application.scala
@@ -0,0 +1,18 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+
+import org.test.Util
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok(Util.fullStop("Your new application is ready"))
+  }
+
+  def shutdown = Action {
+    System.exit(0)
+    Ok("shutdown")
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/build.gradle
new file mode 100644
index 0000000..b9cd567
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/build.gradle
@@ -0,0 +1,8 @@
+plugins {
+    id 'play'
+}
+
+dependencies {
+    play project(":submodule")
+    play project(":javalibrary")
+}
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/application.conf b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/application.conf
new file mode 100644
index 0000000..f16d74c
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/application.conf
@@ -0,0 +1,11 @@
+application.secret="changeme"
+application.langs="en"
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/routes b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/routes
new file mode 100644
index 0000000..286027e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/conf/routes
@@ -0,0 +1,5 @@
+GET     /                          controllers.Application.index
+GET     /shutdown                  controllers.Application.shutdown
+GET     /submodule                 controllers.submodule.Application.index
+GET     /assets/*file              controllers.Assets.at(path="/public", file)
+
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/public/primary.txt b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/public/primary.txt
new file mode 100644
index 0000000..f1cdb7f
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/primary/public/primary.txt
@@ -0,0 +1 @@
+Primary asset
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/settings.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/settings.gradle
new file mode 100644
index 0000000..e7f2e66
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/settings.gradle
@@ -0,0 +1 @@
+include 'primary', 'submodule', 'javalibrary'
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/app/controllers/submodule/Application.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/app/controllers/submodule/Application.scala
new file mode 100644
index 0000000..5a0a6a4
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/app/controllers/submodule/Application.scala
@@ -0,0 +1,12 @@
+package controllers.submodule
+
+import play.api._
+import play.api.mvc._
+
+object Application extends Controller {
+
+  def index = Action {
+    Ok("Submodule page")
+  }
+
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/build.gradle b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/build.gradle
new file mode 100644
index 0000000..d6c795d
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/build.gradle
@@ -0,0 +1,3 @@
+plugins {
+    id 'play'
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/public/submodule.txt b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/public/submodule.txt
new file mode 100644
index 0000000..84947ac
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/playmultiproject/submodule/public/submodule.txt
@@ -0,0 +1 @@
+Submodule asset
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/README b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/README
new file mode 100644
index 0000000..6c0ab1e
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/README
@@ -0,0 +1 @@
+A test README file
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/conf/application.conf b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/conf/application.conf
new file mode 100644
index 0000000..76576e8
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/conf/application.conf
@@ -0,0 +1,11 @@
+application.secret="TY9[b`xw2MeXUt;M<i_B0kUKm8/?PD1cS1WhFYyZ[1^6`Apew34q6DyNL=UqG/1l"
+application.langs="en"
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/images/favicon.svg b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/images/favicon.svg
new file mode 100644
index 0000000..3d5ed64
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/images/favicon.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
+ preserveAspectRatio="xMidYMid meet">
+<g transform="translate(0.000000,16.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+</g>
+</svg>
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/javascripts/hello.js b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/javascripts/hello.js
new file mode 100644
index 0000000..74413f5
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/javascripts/hello.js
@@ -0,0 +1,3 @@
+if (window.console) {
+    console.log("Welcome to your Play application's JavaScript!");
+}
\ No newline at end of file
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/stylesheets/main.css
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/shared/public/stylesheets/main.css
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingApplicationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingApplicationSpec.scala
new file mode 100644
index 0000000..2ec43ec
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingApplicationSpec.scala
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.Ignore
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+import play.api.test._
+import play.api.test.Helpers._
+ at RunWith(classOf[JUnitRunner])
+class FailingApplicationSpec extends Specification {
+  "Application" should {
+    "send 404 on a bad request" in new WithApplication{
+      route(FakeRequest(GET, "/boum")) must beNone
+    }
+    "render the index page" in new WithApplication{
+      val home = route(FakeRequest(GET, "/")).get
+      status(home) must equalTo(OK)
+      contentType(home) must beSome.which(_ == "text/html")
+      contentAsString(home) must contain ("This application content is wrong")
+    }
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingIntegrationSpec.scala b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingIntegrationSpec.scala
new file mode 100644
index 0000000..ee5c864
--- /dev/null
+++ b/subprojects/platform-play/src/integTest/resources/org/gradle/play/integtest/fixtures/app/withfailingtestsapp/test/FailingIntegrationSpec.scala
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.junit.runner._
+
+import play.api.test._
+import play.api.test.Helpers._
+
+ at RunWith(classOf[JUnitRunner])
+class FailingIntegrationSpec extends Specification {
+
+  "Application" should {
+
+    "work from within a browser" in new WithBrowser {
+
+      browser.goTo("http://localhost:" + port)
+
+      browser.pageSource must contain("This application content is wrong.")
+    }
+  }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/CoffeeScriptSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/CoffeeScriptSourceSet.java
new file mode 100644
index 0000000..ae4f982
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/CoffeeScriptSourceSet.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.coffeescript;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a source set for CoffeeScript sources
+ */
+ at Incubating
+public interface CoffeeScriptSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSet.java
new file mode 100644
index 0000000..7871884
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.coffeescript.internal;
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.coffeescript.CoffeeScriptSourceSet;
+
+/**
+ * Default implementation of CoffeeScriptSourceSet
+ */
+public class DefaultCoffeeScriptSourceSet extends BaseLanguageSourceSet implements CoffeeScriptSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "CoffeeScript source";
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/package-info.java
new file mode 100644
index 0000000..ae68865
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/coffeescript/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Language support classes for CoffeeScript
+ */
+package org.gradle.language.coffeescript;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/javascript/JavaScriptSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/JavaScriptSourceSet.java
new file mode 100644
index 0000000..1f4f127
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/JavaScriptSourceSet.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.javascript;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a source set containing javascript sources.
+ */
+ at Incubating
+public interface JavaScriptSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSet.java
new file mode 100644
index 0000000..b008ce9
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.javascript.internal;
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.javascript.JavaScriptSourceSet;
+
+/**
+ * Default implementation of JavaScriptSourceSet
+ */
+public class DefaultJavaScriptSourceSet extends BaseLanguageSourceSet implements JavaScriptSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "JavaScript source";
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/javascript/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/package-info.java
new file mode 100644
index 0000000..7ad1bc4
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/javascript/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Language support classes for javascript
+ */
+package org.gradle.language.javascript;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/routes/RoutesSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/routes/RoutesSourceSet.java
new file mode 100644
index 0000000..505ef9a
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/routes/RoutesSourceSet.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.routes;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a source set containing routes files
+ */
+ at Incubating
+public interface RoutesSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/routes/internal/DefaultRoutesSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/routes/internal/DefaultRoutesSourceSet.java
new file mode 100644
index 0000000..e57851e
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/routes/internal/DefaultRoutesSourceSet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.routes.internal;
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.routes.RoutesSourceSet;
+
+/**
+ * Default implementation of a RoutesSourceSet
+ */
+public class DefaultRoutesSourceSet extends BaseLanguageSourceSet implements RoutesSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "Routes source";
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/routes/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/language/routes/package-info.java
new file mode 100644
index 0000000..a4a7ff0
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/routes/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Language support classes for routes
+ */
+package org.gradle.language.routes;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/twirl/TwirlSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/TwirlSourceSet.java
new file mode 100644
index 0000000..f5a0be9
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/TwirlSourceSet.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.twirl;
+
+import org.gradle.api.Incubating;
+import org.gradle.language.base.LanguageSourceSet;
+
+/**
+ * Represents a source set containing twirl templates
+ */
+ at Incubating
+public interface TwirlSourceSet extends LanguageSourceSet {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/twirl/internal/DefaultTwirlSourceSet.java b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/internal/DefaultTwirlSourceSet.java
new file mode 100644
index 0000000..1becfae
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/internal/DefaultTwirlSourceSet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.twirl.internal;
+
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.twirl.TwirlSourceSet;
+
+/**
+ * Default implementation of a TwirlSourceSet
+ */
+public class DefaultTwirlSourceSet extends BaseLanguageSourceSet implements TwirlSourceSet {
+    @Override
+    protected String getTypeName() {
+        return "Twirl template source";
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/language/twirl/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/package-info.java
new file mode 100644
index 0000000..d03487d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/language/twirl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Language support classes for twirl
+ */
+package org.gradle.language.twirl;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/JvmClasses.java b/subprojects/platform-play/src/main/java/org/gradle/play/JvmClasses.java
new file mode 100644
index 0000000..014d840
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/JvmClasses.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play;
+
+import org.gradle.api.BuildableModelElement;
+import org.gradle.api.Incubating;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * A set of classes and resources that operate together.
+ */
+ at Incubating
+// TODO:DAZ Move this to platform-jvm
+public interface JvmClasses extends BuildableModelElement {
+    /**
+     * The classes directory for this binary.
+     */
+    File getClassesDir();
+
+    /**
+     * Sets the classes directory for this binary.
+     */
+    void setClassesDir(File classesDir);
+
+    /**
+     * A set of resource directories for this binary.
+     */
+    Set<File> getResourceDirs();
+
+    /**
+     * Add a resource directory to this binary.
+     */
+    void addResourceDir(File resourceDir);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationBinarySpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationBinarySpec.java
new file mode 100644
index 0000000..904f82b
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationBinarySpec.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.javascript.JavaScriptSourceSet;
+import org.gradle.language.scala.ScalaLanguageSourceSet;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.play.platform.PlayPlatform;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * Represents a binary artifact that is the result of building a play component.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface PlayApplicationBinarySpec extends BinarySpec {
+    PlayApplicationSpec getApplication();
+
+    PlayPlatform getTargetPlatform();
+
+    File getJarFile();
+
+    File getAssetsJarFile();
+
+    JvmClasses getClasses();
+
+    PublicAssets getAssets();
+
+    Map<LanguageSourceSet, ScalaLanguageSourceSet> getGeneratedScala();
+
+    Map<LanguageSourceSet, JavaScriptSourceSet> getGeneratedJavaScript();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationSpec.java
new file mode 100644
index 0000000..a7a101e
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/PlayApplicationSpec.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.PlatformAwareComponentSpec;
+
+/**
+ * Definition of a play framework software component that is built by Gradle.
+ */
+ at Incubating @HasInternalProtocol
+public interface PlayApplicationSpec extends PlatformAwareComponentSpec {
+
+    void platform(Object platformRequirements);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/PublicAssets.java b/subprojects/platform-play/src/main/java/org/gradle/play/PublicAssets.java
new file mode 100644
index 0000000..eb78f6f
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/PublicAssets.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play;
+
+import org.gradle.api.BuildableModelElement;
+import org.gradle.api.Incubating;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * A set of public assets added to a binary.
+ */
+ at Incubating
+public interface PublicAssets extends BuildableModelElement {
+    /**
+     * A set of asset directories for this binary.
+     */
+    Set<File> getAssetDirs();
+
+    /**
+     * Add an asset directory to this binary.
+     */
+    void addAssetDir(File resourceDir);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistribution.java b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistribution.java
new file mode 100644
index 0000000..13383d3
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistribution.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.distribution;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.distribution.Distribution;
+import org.gradle.play.PlayApplicationBinarySpec;
+
+/**
+ * Represents a Play distribution package
+ */
+ at Incubating
+public interface PlayDistribution extends Distribution {
+    PlayApplicationBinarySpec getBinary();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistributionContainer.java b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistributionContainer.java
new file mode 100644
index 0000000..dc51319
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/PlayDistributionContainer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.distribution;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.distribution.Distribution;
+
+/**
+ * Manages a set of {@link org.gradle.api.distribution.Distribution} objects.
+ */
+ at Incubating
+public interface PlayDistributionContainer extends PolymorphicDomainObjectContainer<Distribution> {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/distribution/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/package-info.java
new file mode 100644
index 0000000..f9a619a
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/distribution/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 related to creating a Play distribution.
+ */
+package org.gradle.play.distribution;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/CleaningPlayToolCompiler.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/CleaningPlayToolCompiler.java
new file mode 100644
index 0000000..b4db410
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/CleaningPlayToolCompiler.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.play.internal.spec.PlayCompileSpec;
+
+public class CleaningPlayToolCompiler<T extends PlayCompileSpec> implements Compiler<T> {
+    private final Compiler<T> delegate;
+    private TaskOutputsInternal taskOutputs;
+
+    public CleaningPlayToolCompiler(Compiler<T> delegate, TaskOutputsInternal taskOutputs) {
+        this.delegate = delegate;
+        this.taskOutputs = taskOutputs;
+    }
+
+    public WorkResult execute(T spec) {
+        SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(taskOutputs);
+        cleaner.setDestinationDir(spec.getDestinationDir());
+        cleaner.execute();
+        return delegate.execute(spec);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationBinarySpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationBinarySpec.java
new file mode 100644
index 0000000..48c4f69
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationBinarySpec.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.javascript.JavaScriptSourceSet;
+import org.gradle.language.scala.ScalaLanguageSourceSet;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryBuildAbility;
+import org.gradle.platform.base.internal.ToolSearchBuildAbility;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.play.JvmClasses;
+import org.gradle.play.PlayApplicationSpec;
+import org.gradle.play.PublicAssets;
+import org.gradle.play.platform.PlayPlatform;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+public class DefaultPlayApplicationBinarySpec extends BaseBinarySpec implements PlayApplicationBinarySpecInternal {
+    private final JvmClasses classesDir = new DefaultJvmClasses();
+    private final PublicAssets assets = new DefaultPublicAssets();
+    private Map<LanguageSourceSet, ScalaLanguageSourceSet> generatedScala = Maps.newHashMap();
+    private Map<LanguageSourceSet, JavaScriptSourceSet> generatedJavaScript = Maps.newHashMap();
+    private PlayPlatform platform;
+    private File jarFile;
+    private File assetsJarFile;
+    private FileCollection classpath;
+    private ToolResolver toolResolver;
+    private PlayApplicationSpec application;
+
+    @Override
+    protected String getTypeName() {
+        return "Play Application Jar";
+    }
+
+    @Override
+    public PlayApplicationSpec getApplication() {
+        return application;
+    }
+
+    @Override
+    public void setApplication(PlayApplicationSpec application) {
+        this.application = application;
+    }
+
+    public PlayPlatform getTargetPlatform() {
+        return platform;
+    }
+
+    public File getJarFile() {
+        return jarFile;
+    }
+
+    public void setTargetPlatform(PlayPlatform platform) {
+        this.platform = platform;
+    }
+
+    public void setJarFile(File file) {
+        this.jarFile = file;
+    }
+
+    public File getAssetsJarFile() {
+        return assetsJarFile;
+    }
+
+    public void setAssetsJarFile(File assetsJarFile) {
+        this.assetsJarFile = assetsJarFile;
+    }
+
+    public JvmClasses getClasses() {
+        return classesDir;
+    }
+
+    public PublicAssets getAssets() {
+        return assets;
+    }
+
+    @Override
+    public Map<LanguageSourceSet, ScalaLanguageSourceSet> getGeneratedScala() {
+        return generatedScala;
+    }
+
+    @Override
+    public Map<LanguageSourceSet, JavaScriptSourceSet> getGeneratedJavaScript() {
+        return generatedJavaScript;
+    }
+
+    @Override
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    @Override
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+    @Override
+    public BinaryBuildAbility getBinaryBuildAbility() {
+        return new ToolSearchBuildAbility(toolResolver.checkToolAvailability(getTargetPlatform()));
+    }
+
+    @Override
+    public void setToolResolver(ToolResolver toolResolver) {
+        this.toolResolver = toolResolver;
+    }
+
+    @Override
+    public ToolResolver getToolResolver() {
+        return toolResolver;
+    }
+
+    private static class DefaultJvmClasses extends AbstractBuildableModelElement implements JvmClasses {
+        private Set<File> resourceDirs = Sets.newLinkedHashSet();
+        private File classesDir;
+
+        public File getClassesDir() {
+            return classesDir;
+        }
+
+        public void setClassesDir(File classesDir) {
+            this.classesDir = classesDir;
+        }
+
+        public Set<File> getResourceDirs() {
+            return resourceDirs;
+        }
+
+        public void addResourceDir(File resourceDir) {
+            resourceDirs.add(resourceDir);
+        }
+    }
+
+    private static class DefaultPublicAssets extends AbstractBuildableModelElement implements PublicAssets {
+        private Set<File> resourceDirs = Sets.newLinkedHashSet();
+
+        public Set<File> getAssetDirs() {
+            return resourceDirs;
+        }
+
+        public void addAssetDir(File assetDir) {
+            resourceDirs.add(assetDir);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationSpec.java
new file mode 100644
index 0000000..7dc5f37
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayApplicationSpec.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import com.google.common.collect.Lists;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultPlayApplicationSpec extends BaseComponentSpec implements PlayApplicationSpecInternal {
+    private final List<PlatformRequirement> targetPlatforms = Lists.newArrayList();
+
+    protected String getTypeName() {
+        return "Play Application";
+    }
+
+    public List<PlatformRequirement> getTargetPlatforms() {
+        return Collections.unmodifiableList(targetPlatforms);
+    }
+
+    public void targetPlatform(String targetPlatform) {
+        this.targetPlatforms.add(DefaultPlatformRequirement.create(targetPlatform));
+    }
+
+    @Override
+    public void platform(Object platformRequirements) {
+        PlatformRequirement requirement = PlayPlatformNotationParser.parser().parseNotation(platformRequirements);
+        this.targetPlatforms.add(requirement);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayPlatform.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayPlatform.java
new file mode 100644
index 0000000..aef5080
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/DefaultPlayPlatform.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.play.internal;
+
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.play.internal.platform.PlayPlatformInternal;
+
+public class DefaultPlayPlatform implements PlayPlatformInternal {
+    private final String playVersion;
+    private final ScalaPlatform scalaPlatform;
+    private final JavaPlatform javaPlatform;
+    private final String name;
+
+    public DefaultPlayPlatform(String name, String playVersion, ScalaPlatform scalaPlatform, JavaPlatform javaPlatform) {
+        this.name = name;
+        this.playVersion = playVersion;
+        this.scalaPlatform = scalaPlatform;
+        this.javaPlatform = javaPlatform;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return String.format("Play Platform (Play %s, Scala: %s, Java: %s)", playVersion, scalaPlatform.getScalaCompatibilityVersion(), javaPlatform.getDisplayName());
+    }
+
+    @Override
+    public String getPlayVersion() {
+        return playVersion;
+    }
+
+    @Override
+    public ScalaPlatform getScalaPlatform() {
+        return scalaPlatform;
+    }
+
+    @Override
+    public JavaPlatform getJavaPlatform() {
+        return javaPlatform;
+    }
+
+    @Override
+    public String getDependencyNotation(String playModule) {
+        return String.format("com.typesafe.play:%s_%s:%s", playModule, scalaPlatform.getScalaCompatibilityVersion(), playVersion);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationBinarySpecInternal.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationBinarySpecInternal.java
new file mode 100644
index 0000000..6e81d19
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationBinarySpecInternal.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.PlayApplicationSpec;
+import org.gradle.play.platform.PlayPlatform;
+
+import java.io.File;
+
+public interface PlayApplicationBinarySpecInternal extends PlayApplicationBinarySpec, BinarySpecInternal {
+    void setApplication(PlayApplicationSpec application);
+
+    void setTargetPlatform(PlayPlatform platform);
+
+    void setToolResolver(ToolResolver toolResolver);
+
+    ToolResolver getToolResolver();
+
+    void setJarFile(File file);
+
+    void setAssetsJarFile(File file);
+
+    // TODO:DAZ Should be taken from the LanguageSourceSet instances?
+    // TODO:DAZ Should be a Classpath instance
+    FileCollection getClasspath();
+
+    void setClasspath(FileCollection applicationClasspath);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationSpecInternal.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationSpecInternal.java
new file mode 100644
index 0000000..0d54534
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayApplicationSpecInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import org.gradle.platform.base.internal.PlatformAwareComponentSpecInternal;
+import org.gradle.play.PlayApplicationSpec;
+
+public interface PlayApplicationSpecInternal extends PlayApplicationSpec, PlatformAwareComponentSpecInternal {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformNotationParser.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformNotationParser.java
new file mode 100644
index 0000000..e3e1b8a
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformNotationParser.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal;
+
+import org.gradle.api.tasks.Optional;
+import org.gradle.internal.exceptions.DiagnosticsVisitor;
+import org.gradle.internal.typeconversion.*;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+
+// TODO:DAZ Unit test
+public class PlayPlatformNotationParser {
+
+    private static final NotationParserBuilder<PlatformRequirement> BUILDER = NotationParserBuilder
+            .toType(PlatformRequirement.class)
+            .fromCharSequence(new StringConverter())
+            .converter(new MapConverter());
+
+
+    public static NotationParser<Object, PlatformRequirement> parser() {
+        return builder().toComposite();
+    }
+
+    private static NotationParserBuilder<PlatformRequirement> builder() {
+        return BUILDER;
+    }
+
+    static class MapConverter extends MapNotationConverter<PlatformRequirement> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("Map defining the platform versions").example("[play: '2.3.7', scala:'2.11.4', java: '1.6']");
+        }
+
+        protected PlatformRequirement parseMap(@MapKey("play") String playVersion,
+                                               @MapKey("scala") @Optional String scalaVersion,
+                                               @MapKey("java") @Optional String javaVersion) {
+            return new PlayPlatformRequirement(playVersion, scalaVersion, javaVersion);
+        }
+    }
+
+    static class StringConverter implements NotationConverter<String, PlatformRequirement> {
+        @Override
+        public void describe(DiagnosticsVisitor visitor) {
+            visitor.candidate("The name of a Play platform").example("'play-2.3.7'.");
+        }
+
+        public void convert(String notation, NotationConvertResult<? super PlatformRequirement> result) throws TypeConversionException {
+            result.converted(new DefaultPlatformRequirement(notation));
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformRequirement.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformRequirement.java
new file mode 100644
index 0000000..13ea116
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformRequirement.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.play.internal;
+
+import org.gradle.platform.base.internal.PlatformRequirement;
+
+public class PlayPlatformRequirement implements PlatformRequirement {
+    private final String platformName;
+    private final String playVersion;
+    private final String scalaVersion;
+    private final String javaVersion;
+
+    public PlayPlatformRequirement(String playVersion, String scalaVersion, String javaVersion) {
+        this.playVersion = playVersion;
+        this.scalaVersion = scalaVersion;
+        this.javaVersion = javaVersion;
+        platformName = createName(playVersion, scalaVersion, javaVersion);
+    }
+
+    @Override
+    public String getPlatformName() {
+        return platformName;
+    }
+
+    String getPlayVersion() {
+        return playVersion;
+    }
+
+    String getScalaVersion() {
+        return scalaVersion;
+    }
+
+    String getJavaVersion() {
+        return javaVersion;
+    }
+
+    private String createName(String playVersion, String scalaVersion, String javaVersion) {
+        StringBuilder builder = new StringBuilder("play-");
+        builder.append(playVersion);
+        if (scalaVersion != null) {
+            builder.append("-");
+            builder.append(scalaVersion);
+        }
+        if (javaVersion != null) {
+            builder.append("_");
+            builder.append(javaVersion);
+        }
+        return builder.toString();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformResolver.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformResolver.java
new file mode 100644
index 0000000..c4bc719
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/PlayPlatformResolver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.play.internal;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.JavaVersion;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.language.scala.internal.DefaultScalaPlatform;
+import org.gradle.platform.base.internal.PlatformRequirement;
+import org.gradle.platform.base.internal.PlatformResolver;
+import org.gradle.play.internal.platform.PlayMajorVersion;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.util.GUtil;
+
+// TODO:DAZ Resolve the JavaPlatform and ScalaPlatform, rather than instantiating directly
+public class PlayPlatformResolver implements PlatformResolver<PlayPlatform> {
+    @Override
+    public Class<PlayPlatform> getType() {
+        return PlayPlatform.class;
+    }
+
+    @Override
+    public PlayPlatform resolve(PlatformRequirement platformRequirement) {
+        if (platformRequirement instanceof PlayPlatformRequirement) {
+            PlayPlatformRequirement requirement = (PlayPlatformRequirement) platformRequirement;
+            return resolve(requirement.getPlatformName(), requirement.getPlayVersion(), requirement.getScalaVersion(), requirement.getJavaVersion());
+        }
+        String playVersion = parsePlayVersionFromPlatformName(platformRequirement.getPlatformName());
+        return resolve(platformRequirement.getPlatformName(), playVersion, null, null);
+    }
+
+    private PlayPlatform resolve(String name, String playVersion, String scalaVersion, String javaVersion) {
+        PlayMajorVersion playMajorVersion = PlayMajorVersion.forPlayVersion(playVersion);
+        JavaPlatform javaPlatform = getJavaPlatform(javaVersion);
+        ScalaPlatform scalaPlatform = getScalaPlatform(playMajorVersion, scalaVersion);
+        return new DefaultPlayPlatform(name, playVersion, scalaPlatform, javaPlatform);
+    }
+
+    private String parsePlayVersionFromPlatformName(String playPlatformName) {
+        if (playPlatformName.startsWith("play-")) {
+            return playPlatformName.substring(5);
+        }
+        throw new InvalidUserDataException(String.format("Not a valid Play platform: %s.", playPlatformName));
+    }
+
+    private JavaPlatform getJavaPlatform(String preferredJavaVersion) {
+        if (preferredJavaVersion != null) {
+            return new DefaultJavaPlatform(JavaVersion.toVersion(preferredJavaVersion));
+        }
+        return new DefaultJavaPlatform(JavaVersion.current());
+    }
+
+    private ScalaPlatform getScalaPlatform(PlayMajorVersion playMajorVersion, String preferredScalaVersion) {
+        String scalaVersion = GUtil.elvis(preferredScalaVersion, playMajorVersion.getDefaultScalaPlatform());
+        ScalaPlatform scalaPlatform = createScalaPlatform(scalaVersion);
+
+        playMajorVersion.validateCompatible(scalaPlatform);
+        return scalaPlatform;
+    }
+
+    private ScalaPlatform createScalaPlatform(String compatibilityVersion) {
+        if ("2.10".equals(compatibilityVersion)) {
+            return new DefaultScalaPlatform("2.10.4");
+        }
+        if ("2.11".equals(compatibilityVersion)) {
+            return new DefaultScalaPlatform("2.11.4");
+        }
+        throw new InvalidUserDataException(String.format("Not a supported Scala platform identifier %s. Supported values are: ['2.10', '2.11'].", compatibilityVersion));
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistribution.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistribution.java
new file mode 100644
index 0000000..5748ee7
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistribution.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.distribution;
+
+import org.gradle.api.distribution.internal.DefaultDistribution;
+import org.gradle.api.file.CopySpec;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.distribution.PlayDistribution;
+
+public class DefaultPlayDistribution extends DefaultDistribution implements PlayDistribution {
+    final private PlayApplicationBinarySpec binary;
+
+    public DefaultPlayDistribution(String name, CopySpec contents, PlayApplicationBinarySpec binary) {
+        super(name, contents);
+        this.binary = binary;
+    }
+
+    public PlayApplicationBinarySpec getBinary() {
+        return binary;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistributionContainer.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistributionContainer.java
new file mode 100644
index 0000000..0254261
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/distribution/DefaultPlayDistributionContainer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.distribution;
+
+import org.gradle.api.distribution.Distribution;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.platform.base.internal.rules.RuleAwarePolymorphicDomainObjectContainer;
+import org.gradle.play.distribution.PlayDistributionContainer;
+
+public class DefaultPlayDistributionContainer extends RuleAwarePolymorphicDomainObjectContainer<Distribution> implements PlayDistributionContainer {
+    public DefaultPlayDistributionContainer(Instantiator instantiator) {
+        super(Distribution.class, instantiator);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/DefaultJavaScriptCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/DefaultJavaScriptCompileSpec.java
new file mode 100644
index 0000000..44042bc
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/DefaultJavaScriptCompileSpec.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.javascript;
+
+
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultJavaScriptCompileSpec implements JavaScriptCompileSpec, Serializable {
+
+    private final Iterable<RelativeFile> sources;
+    private final File destinationDir;
+    private final BaseForkOptions forkOptions;
+
+    public DefaultJavaScriptCompileSpec(Iterable<RelativeFile> sources, File destinationDir, BaseForkOptions forkOptions) {
+        this.sources = sources;
+        this.destinationDir = destinationDir;
+        this.forkOptions = forkOptions;
+    }
+
+    @Override
+    public Iterable<RelativeFile> getSources() {
+        return sources;
+    }
+
+    @Override
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+
+    @Override
+    public BaseForkOptions getForkOptions() {
+        return forkOptions;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/GoogleClosureCompiler.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/GoogleClosureCompiler.java
new file mode 100644
index 0000000..5945f9f
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/GoogleClosureCompiler.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.javascript;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.internal.Factory;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.reflect.DirectInstantiator;
+import org.gradle.internal.reflect.JavaMethod;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.reflect.PropertyAccessor;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.plugins.javascript.base.SourceTransformationException;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.List;
+
+public class GoogleClosureCompiler implements Compiler<JavaScriptCompileSpec>, Serializable {
+    private static final String DEFAULT_GOOGLE_CLOSURE_VERSION = "v20141215";
+    private Class<?> sourceFileClass;
+    private Class<?> compilerOptionsClass;
+    private Class<Enum> compilationLevelClass;
+    private Class<Object> compilerClass;
+
+    public List<String> getClassLoaderPackages() {
+        return Lists.newArrayList("com.google.javascript");
+    }
+
+    public static Object getDependencyNotation() {
+        return String.format("com.google.javascript:closure-compiler:%s", DEFAULT_GOOGLE_CLOSURE_VERSION);
+    }
+
+    @Override
+    public WorkResult execute(JavaScriptCompileSpec spec) {
+        JavaScriptCompileDestinationCalculator destinationCalculator = new JavaScriptCompileDestinationCalculator(spec.getDestinationDir());
+        List<String> allErrors = Lists.newArrayList();
+
+        for (RelativeFile sourceFile : spec.getSources()) {
+            allErrors.addAll(compile(sourceFile, spec, destinationCalculator));
+        }
+
+        if (allErrors.isEmpty()) {
+            return new SimpleWorkResult(true);
+        } else {
+            throw new SourceTransformationException(String.format("Minification failed with the following errors:\n\t%s", StringUtils.join(allErrors, "\n\t")), null);
+        }
+    }
+
+    List<String> compile(RelativeFile javascriptFile, JavaScriptCompileSpec spec, JavaScriptCompileDestinationCalculator destinationCalculator) {
+        List<String> errors = Lists.newArrayList();
+
+        loadCompilerClasses(getClass().getClassLoader());
+
+        // Create a SourceFile object to represent an "empty" extern
+        JavaMethod<?, Object> fromCodeJavaMethod = JavaReflectionUtil.staticMethod(sourceFileClass, Object.class, "fromCode", String.class, String.class);
+        Object extern = fromCodeJavaMethod.invokeStatic("/dev/null", "");
+
+        // Create a SourceFile object to represent the javascript file to compile
+        JavaMethod<?, Object> fromFileJavaMethod = JavaReflectionUtil.staticMethod(sourceFileClass, Object.class, "fromFile", File.class);
+        Object sourceFile = fromFileJavaMethod.invokeStatic(javascriptFile.getFile());
+
+        // Construct a new CompilerOptions class
+        Factory<?> compilerOptionsFactory = JavaReflectionUtil.factory(DirectInstantiator.INSTANCE, compilerOptionsClass);
+        Object compilerOptions = compilerOptionsFactory.create();
+
+        // Get the CompilationLevel.SIMPLE_OPTIMIZATIONS class and set it on the CompilerOptions class
+        @SuppressWarnings({ "rawtypes", "unchecked" }) Enum simpleLevel = Enum.valueOf(compilationLevelClass, "SIMPLE_OPTIMIZATIONS");
+        @SuppressWarnings("rawtypes") JavaMethod<Enum, Void> setOptionsForCompilationLevelMethod = JavaReflectionUtil.method(compilationLevelClass, Void.class, "setOptionsForCompilationLevel", compilerOptionsClass);
+        setOptionsForCompilationLevelMethod.invoke(simpleLevel, compilerOptions);
+
+        // Construct a new Compiler class
+        Factory<?> compilerFactory = JavaReflectionUtil.factory(DirectInstantiator.INSTANCE, compilerClass, getDummyPrintStream());
+        Object compiler = compilerFactory.create();
+
+        // Compile the javascript file with the options we've created
+        JavaMethod<Object, Object> compileMethod = JavaReflectionUtil.method(compilerClass, Object.class, "compile", sourceFileClass, sourceFileClass, compilerOptionsClass);
+        Object result = compileMethod.invoke(compiler, extern, sourceFile, compilerOptions);
+
+        // Get any errors from the compiler result
+        PropertyAccessor<Object, Object[]> jsErrorsField = JavaReflectionUtil.readableField(result, Object[].class, "errors");
+        Object[] jsErrors = jsErrorsField.getValue(result);
+
+        if (jsErrors.length == 0) {
+            // If no errors, get the compiled source and write it to the destination file
+            JavaMethod<Object, String> toSourceMethod = JavaReflectionUtil.method(compilerClass, String.class, "toSource");
+            String compiledSource = toSourceMethod.invoke(compiler);
+            GFileUtils.writeFile(compiledSource, destinationCalculator.transform(javascriptFile));
+        } else {
+            for (Object error : jsErrors) {
+                errors.add(error.toString());
+            }
+        }
+
+        return errors;
+    }
+
+    private void loadCompilerClasses(ClassLoader cl) {
+        try {
+            if (sourceFileClass == null) {
+                sourceFileClass = cl.loadClass("com.google.javascript.jscomp.SourceFile");
+            }
+            if (compilerOptionsClass == null) {
+                compilerOptionsClass = cl.loadClass("com.google.javascript.jscomp.CompilerOptions");
+            }
+            if (compilationLevelClass == null) {
+                @SuppressWarnings("unchecked") Class<Enum> clazz = (Class<Enum>) cl.loadClass("com.google.javascript.jscomp.CompilationLevel");
+                compilationLevelClass = clazz;
+            }
+            if (compilerClass == null) {
+                @SuppressWarnings("unchecked") Class<Object> clazz = (Class<Object>) cl.loadClass("com.google.javascript.jscomp.Compiler");
+                compilerClass = clazz;
+            }
+        } catch (ClassNotFoundException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private PrintStream getDummyPrintStream() {
+        OutputStream os = new OutputStream() {
+            @Override
+            public void write(int b) throws IOException {
+                // do nothing
+            }
+        };
+        return new PrintStream(os);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculator.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculator.java
new file mode 100644
index 0000000..e656040
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.javascript;
+
+import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.RelativeFile;
+
+import java.io.File;
+
+public class JavaScriptCompileDestinationCalculator implements Transformer<File, RelativeFile> {
+    private final File destinationDir;
+
+    public JavaScriptCompileDestinationCalculator(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    @Override
+    public File transform(RelativeFile file) {
+        final File outputFileDir = new File(destinationDir, file.getRelativePath().getParent().getPathString());
+        return new File(outputFileDir, getMinifiedFileName(file.getFile().getName()));
+    }
+
+    private static String getMinifiedFileName(String fileName) {
+        int extIndex = fileName.lastIndexOf('.');
+        if (extIndex == -1) {
+            return fileName + ".min";
+        }
+        String prefix = fileName.substring(0, extIndex);
+        String extension = fileName.substring(extIndex);
+        return String.format("%s.min%s", prefix, extension);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileSpec.java
new file mode 100644
index 0000000..61ff25d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/javascript/JavaScriptCompileSpec.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.javascript;
+
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.play.internal.spec.PlayCompileSpec;
+
+public interface JavaScriptCompileSpec extends PlayCompileSpec {
+    Iterable<RelativeFile> getSources();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayMajorVersion.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayMajorVersion.java
new file mode 100644
index 0000000..72d2450
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayMajorVersion.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.platform;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.util.VersionNumber;
+
+import java.util.List;
+
+public enum PlayMajorVersion {
+    PLAY_2_2_X("2.2.x", "2.10"),
+    PLAY_2_3_X("2.3.x", "2.11", "2.10");
+
+    private final String name;
+    private final List<String> compatibleScalaVersions;
+
+    PlayMajorVersion(String name, String... compatibleScalaVersions) {
+        this.name = name;
+        this.compatibleScalaVersions = Lists.newArrayList(compatibleScalaVersions);
+    }
+
+    public void validateCompatible(ScalaPlatform scalaPlatform) {
+        if (!compatibleScalaVersions.contains(scalaPlatform.getScalaCompatibilityVersion())) {
+            throw new InvalidUserDataException(
+                    String.format("Play versions %s are not compatible with Scala platform %s. Compatible Scala platforms are %s.",
+                            name, scalaPlatform.getScalaCompatibilityVersion(), compatibleScalaVersions));
+        }
+    }
+
+    public String getDefaultScalaPlatform() {
+        return compatibleScalaVersions.get(0);
+    }
+
+    public static PlayMajorVersion forPlatform(PlayPlatform targetPlatform) {
+        String playVersion = targetPlatform.getPlayVersion();
+        return forPlayVersion(playVersion);
+    }
+
+    public static PlayMajorVersion forPlayVersion(String playVersion) {
+        VersionNumber versionNumber = VersionNumber.parse(playVersion);
+        if (versionNumber.getMajor() == 2 && versionNumber.getMinor() == 2) {
+            return PlayMajorVersion.PLAY_2_2_X;
+        }
+        if (versionNumber.getMajor() == 2 && versionNumber.getMinor() == 3) {
+            return PlayMajorVersion.PLAY_2_3_X;
+        }
+        throw new InvalidUserDataException(String.format("Not a supported Play version: %s. This plugin is compatible with: [2.3.x, 2.2.x].", playVersion));
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayPlatformInternal.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayPlatformInternal.java
new file mode 100644
index 0000000..d6701b3
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/platform/PlayPlatformInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.platform;
+
+import org.gradle.play.platform.PlayPlatform;
+
+public interface PlayPlatformInternal extends PlayPlatform {
+    String getDependencyNotation(String playModule);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultRoutesCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultRoutesCompileSpec.java
new file mode 100644
index 0000000..6739237
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultRoutesCompileSpec.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import org.gradle.api.tasks.compile.BaseForkOptions;
+
+import java.io.File;
+
+public class DefaultRoutesCompileSpec implements RoutesCompileSpec {
+    private final Iterable<File> sourceFiles;
+    private final File outputDirectory;
+    private final BaseForkOptions forkOptions;
+    private final boolean javaProject;
+
+    public DefaultRoutesCompileSpec(Iterable<File> sourceFiles, File outputDirectory, BaseForkOptions forkOptions, boolean javaProject) {
+        this.sourceFiles = sourceFiles;
+        this.outputDirectory = outputDirectory;
+        this.forkOptions = forkOptions;
+        this.javaProject = javaProject;
+    }
+
+    public Iterable<File> getSources() {
+        return sourceFiles;
+    }
+
+    public File getDestinationDir() {
+        return outputDirectory;
+    }
+
+    public BaseForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    public boolean isJavaProject() {
+        return javaProject;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultVersionedRoutesCompilerAdapter.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultVersionedRoutesCompilerAdapter.java
new file mode 100644
index 0000000..9ed498e
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/DefaultVersionedRoutesCompilerAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import java.util.Arrays;
+import java.util.List;
+
+abstract class DefaultVersionedRoutesCompilerAdapter implements VersionedRoutesCompilerAdapter {
+    private final String playVersion;
+    private final String scalaVersion;
+
+    public DefaultVersionedRoutesCompilerAdapter(String playVersion, String scalaVersion) {
+        this.playVersion = playVersion;
+        this.scalaVersion = scalaVersion;
+    }
+
+    protected boolean isGenerateReverseRoute() {
+        return true;
+    }
+
+    protected boolean isNamespaceReverseRouter() {
+        return false;
+    }
+
+    protected boolean isGenerateRefReverseRouter() {
+        return false;
+    }
+
+    public Object getDependencyNotation() {
+        return String.format("com.typesafe.play:routes-compiler_%s:%s", scalaVersion, playVersion);
+    }
+
+    public List<String> getClassLoaderPackages() {
+        return Arrays.asList("play.router", "scala.collection", "scala.collection.mutable", "scala.util.matching");
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompileSpec.java
new file mode 100644
index 0000000..bbef556
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompileSpec.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import org.gradle.play.internal.spec.PlayCompileSpec;
+
+import java.io.File;
+import java.io.Serializable;
+
+public interface RoutesCompileSpec extends PlayCompileSpec, Serializable {
+    Iterable<File> getSources();
+
+    boolean isJavaProject();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompiler.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompiler.java
new file mode 100644
index 0000000..c44d676
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompiler.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RoutesCompiler implements Compiler<RoutesCompileSpec>, Serializable {
+    private final VersionedRoutesCompilerAdapter adapter;
+
+    public RoutesCompiler(VersionedRoutesCompilerAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    public WorkResult execute(RoutesCompileSpec spec) {
+        boolean didWork = false;
+        // Need to compile all secondary routes ("Foo.routes") before primary ("routes")
+        ArrayList<File> primaryRoutes = Lists.newArrayList();
+        ArrayList<File> secondaryRoutes = Lists.newArrayList();
+        for (File source : spec.getSources()) {
+            if (source.getName().equals("routes")) {
+                primaryRoutes.add(source);
+            } else {
+                secondaryRoutes.add(source);
+            }
+        }
+
+        // Compile all secondary routes files first
+        for (File sourceFile : secondaryRoutes) {
+            Boolean ret = compile(sourceFile, spec);
+            didWork = ret || didWork;
+        }
+
+        // Compile all main routes files last
+        for (File sourceFile : primaryRoutes) {
+            Boolean ret = compile(sourceFile, spec);
+            didWork = ret || didWork;
+        }
+
+        return new SimpleWorkResult(didWork);
+    }
+
+    private Boolean compile(File sourceFile, RoutesCompileSpec spec) {
+
+        try {
+            ClassLoader cl = getClass().getClassLoader();
+            ScalaMethod compile = adapter.getCompileMethod(cl);
+            Object ret = compile.invoke(adapter.createCompileParameters(cl, sourceFile, spec.getDestinationDir(), spec.isJavaProject()));
+            if (ret != null && ret instanceof Boolean) {
+                return (Boolean) ret;
+            } else {
+                return true; //assume we did some work
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Error invoking the Play routes compiler.", e);
+        }
+    }
+
+    public Object getDependencyNotation() {
+        return adapter.getDependencyNotation();
+    }
+
+    public List<String> getClassLoaderPackages() {
+        return adapter.getClassLoaderPackages();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV22X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV22X.java
new file mode 100644
index 0000000..56eea89
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV22X.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import com.google.common.collect.Lists;
+import org.gradle.scala.internal.reflect.ScalaListBuffer;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaReflectionUtil;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+class RoutesCompilerAdapterV22X extends DefaultVersionedRoutesCompilerAdapter {
+    private final List<String> defaultScalaImports = Lists.newArrayList();
+    private final List<String> defaultJavaImports = Lists.newArrayList("play.libs.F");
+
+    public RoutesCompilerAdapterV22X(String playVersion) {
+        // No 2.11 version of routes compiler published
+        super(playVersion, "2.10");
+    }
+
+    public ScalaMethod getCompileMethod(ClassLoader cl) throws ClassNotFoundException {
+        return ScalaReflectionUtil.scalaMethod(
+                cl,
+                "play.router.RoutesCompiler",
+                "compile",
+                File.class,
+                File.class,
+                cl.loadClass("scala.collection.Seq"),
+                boolean.class,
+                boolean.class
+        );
+    }
+
+    public Object[] createCompileParameters(ClassLoader cl, File file, File destinationDir, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        return new Object[] {
+                file,
+                destinationDir,
+                ScalaListBuffer.fromList(cl, javaProject ? defaultJavaImports : defaultScalaImports),
+                isGenerateReverseRoute(),
+                isNamespaceReverseRouter()
+        };
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV23X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV23X.java
new file mode 100644
index 0000000..2ceef47
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerAdapterV23X.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import com.google.common.collect.Lists;
+import org.gradle.scala.internal.reflect.ScalaListBuffer;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaReflectionUtil;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+class RoutesCompilerAdapterV23X extends DefaultVersionedRoutesCompilerAdapter {
+    private final List<String> defaultScalaImports = Lists.newArrayList("controllers.Assets.Asset");
+    private final List<String> defaultJavaImports = Lists.newArrayList("controllers.Assets.Asset", "play.libs.F");
+
+    public RoutesCompilerAdapterV23X(String playVersion) {
+        // No 2.11 version of routes compiler published
+        super(playVersion, "2.10");
+    }
+
+    public ScalaMethod getCompileMethod(ClassLoader cl) throws ClassNotFoundException {
+        return ScalaReflectionUtil.scalaMethod(
+                cl,
+                "play.router.RoutesCompiler",
+                "compile",
+                File.class,
+                File.class,
+                cl.loadClass("scala.collection.Seq"),
+                boolean.class,
+                boolean.class,
+                boolean.class
+        );
+    }
+
+    @Override
+    public Object[] createCompileParameters(ClassLoader cl, File file, File destinationDir, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        return new Object[] {
+                file,
+                destinationDir,
+                ScalaListBuffer.fromList(cl, javaProject ? defaultJavaImports : defaultScalaImports),
+                isGenerateReverseRoute(),
+                isGenerateRefReverseRouter(),
+                isNamespaceReverseRouter()
+        };
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerFactory.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerFactory.java
new file mode 100644
index 0000000..f9aff39
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/RoutesCompilerFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import org.gradle.play.internal.platform.PlayMajorVersion;
+import org.gradle.play.platform.PlayPlatform;
+
+public class RoutesCompilerFactory {
+    public static RoutesCompiler create(PlayPlatform playPlatform) {
+        return new RoutesCompiler(createAdapter(playPlatform));
+    }
+
+    public static VersionedRoutesCompilerAdapter createAdapter(PlayPlatform playPlatform) {
+        String playVersion = playPlatform.getPlayVersion();
+        switch (PlayMajorVersion.forPlatform(playPlatform)) {
+            case PLAY_2_2_X:
+                return new RoutesCompilerAdapterV22X(playVersion);
+            case PLAY_2_3_X:
+                return new RoutesCompilerAdapterV23X(playVersion);
+            default:
+                throw new RuntimeException("Could not create routes compile spec for Play version: " + playVersion);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/VersionedRoutesCompilerAdapter.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/VersionedRoutesCompilerAdapter.java
new file mode 100644
index 0000000..b3b1d1e
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/routes/VersionedRoutesCompilerAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.routes;
+
+import org.gradle.scala.internal.reflect.ScalaMethod;
+
+import java.io.File;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+public interface VersionedRoutesCompilerAdapter extends Serializable {
+    Object getDependencyNotation();
+
+    ScalaMethod getCompileMethod(ClassLoader cl) throws ClassNotFoundException;
+
+    Object[] createCompileParameters(ClassLoader cl, File file, File destinationDir, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException;
+
+    List<String> getClassLoaderPackages();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultPlayRunSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultPlayRunSpec.java
new file mode 100644
index 0000000..414c0e3
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultPlayRunSpec.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import com.google.common.collect.Sets;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+
+import java.io.File;
+import java.io.Serializable;
+
+public class DefaultPlayRunSpec implements PlayRunSpec, Serializable {
+    private final Iterable<File> classpath;
+    private final File projectPath;
+    private BaseForkOptions forkOptions;
+    private int httpPort;
+
+    public DefaultPlayRunSpec(FileCollection classpath, File projectPath, BaseForkOptions forkOptions, int httpPort) {
+        this.classpath = Sets.newHashSet(classpath);
+        this.projectPath = projectPath;
+        this.forkOptions = forkOptions;
+        this.httpPort = httpPort;
+    }
+
+    public BaseForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    public Iterable<File> getClasspath() {
+        return classpath;
+    }
+
+    public File getProjectPath() {
+        return projectPath;
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultVersionedPlayRunAdapter.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultVersionedPlayRunAdapter.java
new file mode 100644
index 0000000..ddf25fd
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/DefaultVersionedPlayRunAdapter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaReflectionUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+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.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.jar.JarFile;
+
+public abstract class DefaultVersionedPlayRunAdapter implements VersionedPlayRunAdapter, Serializable {
+
+    protected abstract Class<?> getBuildLinkClass(ClassLoader classLoader) throws ClassNotFoundException;
+
+    protected abstract Class<?> getDocHandlerFactoryClass(ClassLoader classLoader) throws ClassNotFoundException;
+
+    protected abstract Class<?> getBuildDocHandlerClass(ClassLoader docsClassLoader) throws ClassNotFoundException;
+
+    public Object getBuildLink(ClassLoader classLoader, final File projectPath, final Iterable<File> classpath) throws ClassNotFoundException {
+        return Proxy.newProxyInstance(classLoader, new Class<?>[]{getBuildLinkClass(classLoader)}, new InvocationHandler() {
+            private volatile boolean shouldReloadNextTime = true;
+
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                if (method.getName().equals("projectPath")) {
+                    return projectPath;
+                } else if (method.getName().equals("reload")) {
+                    if (shouldReloadNextTime) {
+                        shouldReloadNextTime = false; //reload only once for now
+                        DefaultClassPath projectClasspath = new DefaultClassPath(classpath);
+                        return new URLClassLoader(projectClasspath.getAsURLs().toArray(new URL[]{}), Thread.currentThread().getContextClassLoader());
+                    } else {
+                        return null;
+                    }
+                } else if (method.getName().equals("settings")) {
+                    return new HashMap<String, String>();
+                }
+                //TODO: all methods
+                return null;
+            }
+        });
+    }
+
+    public Object getBuildDocHandler(ClassLoader docsClassLoader, Iterable<File> classpath) throws NoSuchMethodException, ClassNotFoundException, IOException, IllegalAccessException {
+        Class<?> docHandlerFactoryClass = getDocHandlerFactoryClass(docsClassLoader);
+        Method docHandlerFactoryMethod = docHandlerFactoryClass.getMethod("fromJar", JarFile.class, String.class);
+        JarFile documentationJar = findDocumentationJar(classpath);
+        try {
+            return docHandlerFactoryMethod.invoke(null, documentationJar, "play/docs/content");
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.unwrapAndRethrow(e);
+        }
+    }
+
+    private JarFile findDocumentationJar(Iterable<File> classpath) throws IOException {
+        // TODO:DAZ Use the location of the DocHandlerFactoryClass instead.
+        File docJarFile = null;
+        for (File file : classpath) {
+            if (file.getName().startsWith("play-docs")) {
+                docJarFile = file;
+                break;
+            }
+        }
+        return new JarFile(docJarFile);
+    }
+
+
+    public ScalaMethod getNettyServerDevHttpMethod(ClassLoader classLoader, ClassLoader docsClassLoader) throws ClassNotFoundException {
+        return ScalaReflectionUtil.scalaMethod(classLoader, "play.core.server.NettyServer", "mainDevHttpMode", getBuildLinkClass(classLoader), getBuildDocHandlerClass(docsClassLoader), int.class);
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayAppLifecycleUpdate.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayAppLifecycleUpdate.java
new file mode 100644
index 0000000..0f50dbe
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayAppLifecycleUpdate.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import java.io.Serializable;
+
+public class PlayAppLifecycleUpdate implements Serializable {
+    private final boolean running;
+    private final Exception exception;
+
+    public static PlayAppLifecycleUpdate stopped() {
+        return new PlayAppLifecycleUpdate(false);
+    }
+
+    public static PlayAppLifecycleUpdate running() {
+        return new PlayAppLifecycleUpdate(true);
+    }
+
+    public static PlayAppLifecycleUpdate failed(Exception exception) {
+        return new PlayAppLifecycleUpdate(exception);
+    }
+
+    private PlayAppLifecycleUpdate(boolean isRunning) {
+        this.running = isRunning;
+        this.exception = null;
+    }
+
+    private PlayAppLifecycleUpdate(Exception exception) {
+        this.running = false;
+        this.exception = exception;
+    }
+
+    public Exception getException() {
+        return exception;
+    }
+
+    public boolean isRunning() {
+        return running && exception == null;
+    }
+
+    public boolean isStopped() {
+        return !running && exception == null;
+    }
+
+    public boolean isFailed() {
+        return exception != null;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunner.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunner.java
new file mode 100644
index 0000000..84588ba
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunner.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.Factory;
+import org.gradle.process.internal.JavaExecHandleBuilder;
+import org.gradle.process.internal.WorkerProcess;
+import org.gradle.process.internal.WorkerProcessBuilder;
+
+import java.io.File;
+
+public class PlayApplicationRunner {
+    private final File workingDir;
+    private final Factory<WorkerProcessBuilder> workerFactory;
+    private final VersionedPlayRunAdapter adapter;
+
+    public PlayApplicationRunner(File workingDir, Factory<WorkerProcessBuilder> workerFactory, VersionedPlayRunAdapter adapter) {
+        this.workingDir = workingDir;
+        this.workerFactory = workerFactory;
+        this.adapter = adapter;
+    }
+
+    public PlayApplicationRunnerToken start(PlayRunSpec spec) {
+        WorkerProcess process = createWorkerProcess(workingDir, workerFactory, spec, adapter);
+        process.start();
+
+        PlayWorkerClient clientCallBack = new PlayWorkerClient();
+        process.getConnection().addIncoming(PlayRunWorkerClientProtocol.class, clientCallBack);
+        PlayRunWorkerServerProtocol workerServer = process.getConnection().addOutgoing(PlayRunWorkerServerProtocol.class);
+        process.getConnection().connect();
+        PlayAppLifecycleUpdate result = clientCallBack.waitForRunning();
+        if (result.isRunning()) {
+            return new PlayApplicationRunnerToken(workerServer, clientCallBack);
+        } else {
+            throw new GradleException("Unable to start Play application.", result.getException());
+        }
+    }
+
+    private static WorkerProcess createWorkerProcess(File workingDir, Factory<WorkerProcessBuilder> workerFactory, PlayRunSpec spec, VersionedPlayRunAdapter adapter) {
+        WorkerProcessBuilder builder = workerFactory.create();
+        builder.setBaseName("Gradle Play Worker");
+        builder.applicationClasspath(spec.getClasspath());
+        builder.sharedPackages("org.gradle.play.internal.run", "play.core", "play.core.server", "play.docs", "scala");
+        JavaExecHandleBuilder javaCommand = builder.getJavaCommand();
+        javaCommand.setWorkingDir(workingDir);
+        javaCommand.setMinHeapSize(spec.getForkOptions().getMemoryInitialSize());
+        javaCommand.setMaxHeapSize(spec.getForkOptions().getMemoryMaximumSize());
+        return builder.worker(new PlayWorkerServer(spec, adapter)).build();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunnerToken.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunnerToken.java
new file mode 100644
index 0000000..af78bef
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayApplicationRunnerToken.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+public class PlayApplicationRunnerToken {
+
+    private final PlayWorkerClient clientCallBack;
+    private final PlayRunWorkerServerProtocol workerServer;
+
+    public PlayApplicationRunnerToken(PlayRunWorkerServerProtocol workerServer, PlayWorkerClient clientCallBack) {
+        this.workerServer = workerServer;
+        this.clientCallBack = clientCallBack;
+    }
+
+    public PlayAppLifecycleUpdate stop() {
+        workerServer.stop();
+        return clientCallBack.waitForStop();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV22X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV22X.java
new file mode 100644
index 0000000..afce834
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV22X.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+public class PlayRunAdapterV22X extends DefaultVersionedPlayRunAdapter {
+    @Override
+    protected Class<?> getBuildLinkClass(ClassLoader classLoader) throws ClassNotFoundException {
+        return classLoader.loadClass("play.core.SBTLink");
+    }
+
+    @Override
+    protected Class<?> getBuildDocHandlerClass(ClassLoader classLoader) throws ClassNotFoundException {
+        return classLoader.loadClass("play.core.SBTDocHandler");
+    }
+
+    @Override
+    protected Class<?> getDocHandlerFactoryClass(ClassLoader docsClassLoader) throws ClassNotFoundException {
+        return docsClassLoader.loadClass("play.docs.SBTDocHandlerFactory");
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV23X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV23X.java
new file mode 100644
index 0000000..811e535
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunAdapterV23X.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+public class PlayRunAdapterV23X extends DefaultVersionedPlayRunAdapter {
+    @Override
+    protected Class<?> getBuildLinkClass(ClassLoader classLoader) throws ClassNotFoundException {
+        return classLoader.loadClass("play.core.BuildLink");
+    }
+
+    @Override
+    protected Class<?> getBuildDocHandlerClass(ClassLoader classLoader) throws ClassNotFoundException {
+        return classLoader.loadClass("play.core.BuildDocHandler");
+    }
+
+    @Override
+    protected Class<?> getDocHandlerFactoryClass(ClassLoader docsClassLoader) throws ClassNotFoundException {
+        return docsClassLoader.loadClass("play.docs.BuildDocHandlerFactory");
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunSpec.java
new file mode 100644
index 0000000..d14566c
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunSpec.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.api.tasks.compile.BaseForkOptions;
+
+import java.io.File;
+
+public interface PlayRunSpec {
+
+    BaseForkOptions getForkOptions();
+
+    Iterable<File> getClasspath();
+
+    File getProjectPath();
+
+    int getHttpPort();
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerClientProtocol.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerClientProtocol.java
new file mode 100644
index 0000000..510d42b
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerClientProtocol.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+public interface PlayRunWorkerClientProtocol {
+    void update(PlayAppLifecycleUpdate result);
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerServerProtocol.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerServerProtocol.java
new file mode 100644
index 0000000..0497448
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayRunWorkerServerProtocol.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.internal.concurrent.Stoppable;
+
+public interface PlayRunWorkerServerProtocol extends Stoppable {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerClient.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerClient.java
new file mode 100644
index 0000000..838144a
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerClient.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.internal.UncheckedException;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+
+public class PlayWorkerClient implements PlayRunWorkerClientProtocol {
+
+    private final BlockingQueue<PlayAppLifecycleUpdate> startEvent = new SynchronousQueue<PlayAppLifecycleUpdate>();
+    private final BlockingQueue<PlayAppLifecycleUpdate> stopEvent = new SynchronousQueue<PlayAppLifecycleUpdate>();
+
+    public void update(PlayAppLifecycleUpdate update) {
+        try {
+            if (update.isStopped()) {
+                stopEvent.put(update);
+            } else {
+                startEvent.put(update);
+            }
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public PlayAppLifecycleUpdate waitForRunning() {
+        try {
+            return startEvent.take();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public PlayAppLifecycleUpdate waitForStop() {
+        try {
+            return stopEvent.take();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerServer.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerServer.java
new file mode 100644
index 0000000..1522c06
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/PlayWorkerServer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.api.Action;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.UncheckedException;
+import org.gradle.process.internal.WorkerProcessContext;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+
+import java.io.Serializable;
+import java.util.concurrent.CountDownLatch;
+
+public class PlayWorkerServer implements Action<WorkerProcessContext>, PlayRunWorkerServerProtocol, Serializable {
+
+    private PlayRunSpec runSpec;
+    private VersionedPlayRunAdapter spec;
+
+    private volatile CountDownLatch stop;
+
+    public PlayWorkerServer(PlayRunSpec runSpec, VersionedPlayRunAdapter spec) {
+        this.runSpec = runSpec;
+        this.spec = spec;
+    }
+
+    public void execute(WorkerProcessContext context) {
+        stop = new CountDownLatch(1);
+        final PlayRunWorkerClientProtocol clientProtocol = context.getServerConnection().addOutgoing(PlayRunWorkerClientProtocol.class);
+        context.getServerConnection().addIncoming(PlayRunWorkerServerProtocol.class, this);
+        context.getServerConnection().connect();
+        final PlayAppLifecycleUpdate result = startServer();
+        try {
+            clientProtocol.update(result);
+            stop.await();
+        } catch (InterruptedException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        } finally {
+            clientProtocol.update(PlayAppLifecycleUpdate.stopped());
+        }
+    }
+
+    private PlayAppLifecycleUpdate startServer() {
+        try {
+            run();
+            return PlayAppLifecycleUpdate.running();
+        } catch (Exception e) {
+            Logging.getLogger(this.getClass()).error("Failed to run Play", e);
+            return PlayAppLifecycleUpdate.failed(e);
+        }
+    }
+
+    private void run() {
+        try {
+            ClassLoader classLoader = getClass().getClassLoader();
+            ClassLoader docsClassLoader = getClass().getClassLoader();
+
+            Object buildDocHandler = spec.getBuildDocHandler(docsClassLoader, runSpec.getClasspath());
+            ScalaMethod runMethod = spec.getNettyServerDevHttpMethod(classLoader, docsClassLoader);
+            Object buildLink = spec.getBuildLink(classLoader, runSpec.getProjectPath(), runSpec.getClasspath());
+            runMethod.invoke(buildLink, buildDocHandler, runSpec.getHttpPort());
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public void stop() {
+        stop.countDown();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/VersionedPlayRunAdapter.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/VersionedPlayRunAdapter.java
new file mode 100644
index 0000000..c2ceb54
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/run/VersionedPlayRunAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.run;
+
+import org.gradle.scala.internal.reflect.ScalaMethod;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface VersionedPlayRunAdapter {
+    Object getBuildLink(ClassLoader classLoader, final File projectPath, Iterable<File> classpath) throws ClassNotFoundException;
+
+    Object getBuildDocHandler(ClassLoader docsClassLoader, Iterable<File> classpath) throws NoSuchMethodException, ClassNotFoundException, IOException, IllegalAccessException;
+
+    ScalaMethod getNettyServerDevHttpMethod(ClassLoader classLoader, ClassLoader docsClassLoader) throws ClassNotFoundException;
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayApplicationBinaryRenderer.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayApplicationBinaryRenderer.java
new file mode 100644
index 0000000..d13ff65
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayApplicationBinaryRenderer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.spec;
+
+import org.gradle.api.reporting.components.internal.AbstractBinaryRenderer;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.play.PlayApplicationBinarySpec;
+
+public class PlayApplicationBinaryRenderer extends AbstractBinaryRenderer<PlayApplicationBinarySpec> {
+    @Override
+    public Class<PlayApplicationBinarySpec> getTargetType() {
+        return PlayApplicationBinarySpec.class;
+    }
+
+    @Override
+    protected void renderDetails(PlayApplicationBinarySpec binary, TextReportBuilder builder) {
+        builder.item("platform", binary.getTargetPlatform().getDisplayName());
+        //builder.item("tool chain", binary.getToolChain().getDisplayName());
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayCompileSpec.java
new file mode 100644
index 0000000..caf47c6
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/spec/PlayCompileSpec.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.spec;
+
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.language.base.internal.compile.CompileSpec;
+
+import java.io.File;
+
+public interface PlayCompileSpec extends CompileSpec {
+    File getDestinationDir();
+
+    BaseForkOptions getForkOptions();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DaemonPlayCompiler.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DaemonPlayCompiler.java
new file mode 100644
index 0000000..fcd806b
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DaemonPlayCompiler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
+import org.gradle.api.internal.tasks.compile.daemon.DaemonForkOptions;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.play.internal.spec.PlayCompileSpec;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+public class DaemonPlayCompiler<T extends PlayCompileSpec> extends AbstractDaemonCompiler<T> {
+    private final Iterable<File> compilerClasspath;
+    private final List<String> classLoaderPackages;
+
+    public DaemonPlayCompiler(File projectDir, Compiler<T> compiler, CompilerDaemonFactory compilerDaemonFactory, Iterable<File> compilerClasspath, List<String> classLoaderPackages) {
+        super(projectDir, compiler, compilerDaemonFactory);
+        this.compilerClasspath = compilerClasspath;
+        this.classLoaderPackages = classLoaderPackages;
+    }
+
+    @Override
+    protected DaemonForkOptions toDaemonOptions(PlayCompileSpec spec) {
+        BaseForkOptions forkOptions = spec.getForkOptions();
+        return new DaemonForkOptions(forkOptions.getMemoryInitialSize(), forkOptions.getMemoryMaximumSize(), Collections.<String>emptyList(), compilerClasspath, classLoaderPackages);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolChain.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolChain.java
new file mode 100644
index 0000000..d1317df
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolChain.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.internal.Factory;
+import org.gradle.internal.text.TreeFormatter;
+import org.gradle.language.base.internal.compile.*;
+import org.gradle.play.internal.javascript.GoogleClosureCompiler;
+import org.gradle.play.internal.routes.RoutesCompilerFactory;
+import org.gradle.play.internal.twirl.TwirlCompilerFactory;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.process.internal.WorkerProcessBuilder;
+import org.gradle.util.CollectionUtils;
+import org.gradle.util.TreeVisitor;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+public class DefaultPlayToolChain implements PlayToolChainInternal {
+    private FileResolver fileResolver;
+    private CompilerDaemonManager compilerDaemonManager;
+    private final ConfigurationContainer configurationContainer;
+    private final DependencyHandler dependencyHandler;
+    private final Factory<WorkerProcessBuilder> workerProcessBuilderFactory;
+
+    public DefaultPlayToolChain(FileResolver fileResolver, CompilerDaemonManager compilerDaemonManager, ConfigurationContainer configurationContainer, DependencyHandler dependencyHandler, Factory<WorkerProcessBuilder> workerProcessBuilderFactory) {
+        this.fileResolver = fileResolver;
+        this.compilerDaemonManager = compilerDaemonManager;
+        this.configurationContainer = configurationContainer;
+        this.dependencyHandler = dependencyHandler;
+        this.workerProcessBuilderFactory = workerProcessBuilderFactory;
+    }
+
+    public String getName() {
+        return String.format("PlayToolchain");
+    }
+
+    public String getDisplayName() {
+        return String.format("Default Play Toolchain");
+    }
+
+    public PlayToolProvider select(PlayPlatform targetPlatform) {
+        try {
+            Set<File> twirlClasspath = resolveToolClasspath(TwirlCompilerFactory.createAdapter(targetPlatform).getDependencyNotation()).resolve();
+            Set<File> routesClasspath = resolveToolClasspath(RoutesCompilerFactory.createAdapter(targetPlatform).getDependencyNotation()).resolve();
+            Set<File> javascriptClasspath = resolveToolClasspath(GoogleClosureCompiler.getDependencyNotation()).resolve();
+            return new DefaultPlayToolProvider(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory, targetPlatform, twirlClasspath, routesClasspath, javascriptClasspath);
+        } catch (ResolveException e) {
+            return new UnavailablePlayToolProvider(e);
+        }
+    }
+
+    private Configuration resolveToolClasspath(Object... dependencyNotations) {
+        List<Dependency> dependencies = CollectionUtils.collect(dependencyNotations, new Transformer<Dependency, Object>() {
+            public Dependency transform(Object dependencyNotation) {
+                return dependencyHandler.create(dependencyNotation);
+            }
+        });
+        Dependency[] dependenciesArray = dependencies.toArray(new Dependency[dependencies.size()]);
+        return configurationContainer.detachedConfiguration(dependenciesArray);
+    }
+
+    private static class UnavailablePlayToolProvider implements PlayToolProvider {
+        private final Exception exception;
+
+        public UnavailablePlayToolProvider(Exception exception) {
+            this.exception = exception;
+        }
+
+        @Override
+        public <T extends CompileSpec> org.gradle.language.base.internal.compile.Compiler<T> newCompiler(Class<T> spec) {
+            throw failure();
+        }
+
+        @Override
+        public <T> T get(Class<T> toolType) {
+            throw failure();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        private RuntimeException failure() {
+            TreeFormatter formatter = new TreeFormatter();
+            this.explain(formatter);
+            return new GradleException(formatter.toString());
+        }
+
+        @Override
+        public void explain(TreeVisitor<? super String> visitor) {
+            visitor.node("Cannot provide Play tool provider");
+            visitor.startChildren();
+            visitor.node(exception.getCause().getMessage());
+            visitor.endChildren();
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolProvider.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolProvider.java
new file mode 100644
index 0000000..10f7424
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/DefaultPlayToolProvider.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.CompileSpec;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.play.internal.javascript.JavaScriptCompileSpec;
+import org.gradle.play.internal.javascript.GoogleClosureCompiler;
+import org.gradle.play.internal.platform.PlayMajorVersion;
+import org.gradle.play.internal.routes.RoutesCompileSpec;
+import org.gradle.play.internal.routes.RoutesCompiler;
+import org.gradle.play.internal.routes.RoutesCompilerFactory;
+import org.gradle.play.internal.run.PlayApplicationRunner;
+import org.gradle.play.internal.run.PlayRunAdapterV22X;
+import org.gradle.play.internal.run.PlayRunAdapterV23X;
+import org.gradle.play.internal.run.VersionedPlayRunAdapter;
+import org.gradle.play.internal.spec.PlayCompileSpec;
+import org.gradle.play.internal.twirl.TwirlCompileSpec;
+import org.gradle.play.internal.twirl.TwirlCompiler;
+import org.gradle.play.internal.twirl.TwirlCompilerFactory;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.process.internal.WorkerProcessBuilder;
+import org.gradle.util.TreeVisitor;
+
+import java.io.File;
+import java.util.Set;
+
+class DefaultPlayToolProvider implements PlayToolProvider {
+
+    private final FileResolver fileResolver;
+    private final CompilerDaemonManager compilerDaemonManager;
+    private final ConfigurationContainer configurationContainer;
+    private final DependencyHandler dependencyHandler;
+    private final PlayPlatform targetPlatform;
+    private final PlayMajorVersion playMajorVersion;
+    private Factory<WorkerProcessBuilder> workerProcessBuilderFactory;
+    private final Set<File> twirlClasspath;
+    private final Set<File> routesClasspath;
+    private final Set<File> javaScriptClasspath;
+
+    public DefaultPlayToolProvider(FileResolver fileResolver, CompilerDaemonManager compilerDaemonManager, ConfigurationContainer configurationContainer,
+                                   DependencyHandler dependencyHandler, Factory<WorkerProcessBuilder> workerProcessBuilderFactory, PlayPlatform targetPlatform,
+                                   Set<File> twirlClasspath, Set<File> routesClasspath, Set<File> javaScriptClasspath) {
+        this.fileResolver = fileResolver;
+        this.compilerDaemonManager = compilerDaemonManager;
+        this.configurationContainer = configurationContainer;
+        this.dependencyHandler = dependencyHandler;
+        this.workerProcessBuilderFactory = workerProcessBuilderFactory;
+        this.targetPlatform = targetPlatform;
+        this.playMajorVersion = PlayMajorVersion.forPlatform(targetPlatform);
+        this.twirlClasspath = twirlClasspath;
+        this.routesClasspath = routesClasspath;
+        this.javaScriptClasspath = javaScriptClasspath;
+    }
+
+    // TODO:DAZ Detangle Routes adapter from compile specs
+    public <T extends CompileSpec> Compiler<T> newCompiler(Class<T> spec) {
+        if (TwirlCompileSpec.class.isAssignableFrom(spec)) {
+            TwirlCompiler twirlCompiler = TwirlCompilerFactory.create(targetPlatform);
+            return cast(new DaemonPlayCompiler<TwirlCompileSpec>(fileResolver.resolve("."), twirlCompiler, compilerDaemonManager, twirlClasspath, twirlCompiler.getClassLoaderPackages()));
+        } else if (RoutesCompileSpec.class.isAssignableFrom(spec)) {
+            RoutesCompiler routesCompiler = RoutesCompilerFactory.create(targetPlatform);
+            return cast(new DaemonPlayCompiler<RoutesCompileSpec>(fileResolver.resolve("."), routesCompiler, compilerDaemonManager, routesClasspath, routesCompiler.getClassLoaderPackages()));
+        } else if (JavaScriptCompileSpec.class.isAssignableFrom(spec)) {
+            GoogleClosureCompiler javaScriptCompiler = new GoogleClosureCompiler();
+            return cast(new DaemonPlayCompiler<JavaScriptCompileSpec>(fileResolver.resolve("."), javaScriptCompiler, compilerDaemonManager, javaScriptClasspath, javaScriptCompiler.getClassLoaderPackages()));
+        }
+        throw new IllegalArgumentException(String.format("Cannot create Compiler for unsupported CompileSpec type '%s'", spec.getSimpleName()));
+    }
+
+    @Override
+    public <T> T get(Class<T> toolType) {
+        if (PlayApplicationRunner.class.isAssignableFrom(toolType)) {
+            return toolType.cast(newApplicationRunner());
+        }
+        throw new IllegalArgumentException(String.format("Don't know how to provide tool of type %s.", toolType.getSimpleName()));
+    }
+
+    private <T extends CompileSpec> Compiler<T> cast(Compiler<? extends PlayCompileSpec> raw) {
+        @SuppressWarnings("unchecked")
+        Compiler<T> converted = (Compiler<T>) raw;
+        return converted;
+    }
+
+    public PlayApplicationRunner newApplicationRunner() {
+        VersionedPlayRunAdapter playRunAdapter = createPlayRunAdapter();
+        return new PlayApplicationRunner(fileResolver.resolve("."), workerProcessBuilderFactory, playRunAdapter);
+    }
+
+    private VersionedPlayRunAdapter createPlayRunAdapter() {
+        switch (playMajorVersion) {
+            case PLAY_2_2_X:
+                return new PlayRunAdapterV22X();
+            case PLAY_2_3_X:
+            default:
+                return new PlayRunAdapterV23X();
+        }
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public void explain(TreeVisitor<? super String> visitor) {
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainInternal.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainInternal.java
new file mode 100644
index 0000000..277724f
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainInternal.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.platform.base.internal.toolchain.ToolChainInternal;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.play.toolchain.PlayToolChain;
+
+public interface PlayToolChainInternal extends PlayToolChain, ToolChainInternal<PlayPlatform> {
+    public PlayToolProvider select(PlayPlatform targetPlatform);
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainServiceRegistry.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainServiceRegistry.java
new file mode 100644
index 0000000..173f6c2
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolChainServiceRegistry.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
+import org.gradle.internal.Factory;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.play.internal.spec.PlayApplicationBinaryRenderer;
+import org.gradle.process.internal.WorkerProcessBuilder;
+
+public class PlayToolChainServiceRegistry implements PluginServiceRegistry {
+
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.add(PlayApplicationBinaryRenderer.class);
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+        registration.addProvider(new ProjectScopeCompileServices());
+    }
+
+    private static class ProjectScopeCompileServices {
+        PlayToolChainInternal createPlayToolChain(FileResolver fileResolver, CompilerDaemonManager compilerDaemonManager, ConfigurationContainer configurationContainer, DependencyHandler dependencyHandler, Factory<WorkerProcessBuilder> workerProcessBuilderFactory) {
+            return new DefaultPlayToolChain(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolProvider.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolProvider.java
new file mode 100644
index 0000000..5979dc7
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/toolchain/PlayToolProvider.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain;
+
+import org.gradle.platform.base.internal.toolchain.ToolProvider;
+
+public interface PlayToolProvider extends ToolProvider {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/DefaultTwirlCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/DefaultTwirlCompileSpec.java
new file mode 100644
index 0000000..5a7c678
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/DefaultTwirlCompileSpec.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+
+import java.io.File;
+
+public class DefaultTwirlCompileSpec implements TwirlCompileSpec {
+    private final Iterable<RelativeFile> sources;
+    private final File destinationDir;
+    private BaseForkOptions forkOptions;
+    private final boolean javaProject;
+
+    public DefaultTwirlCompileSpec(Iterable<RelativeFile> sources, File destinationDir, BaseForkOptions forkOptions, boolean javaProject) {
+        this.sources = sources;
+        this.destinationDir = destinationDir;
+        this.forkOptions = forkOptions;
+        this.javaProject = javaProject;
+    }
+
+    public boolean isJavaProject() {
+        return javaProject;
+    }
+
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+
+    public Iterable<RelativeFile> getSources() {
+        return sources;
+    }
+
+    public BaseForkOptions getForkOptions() {
+        return forkOptions;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompileSpec.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompileSpec.java
new file mode 100644
index 0000000..39adeac
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompileSpec.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.play.internal.spec.PlayCompileSpec;
+
+import java.io.Serializable;
+
+public interface TwirlCompileSpec extends PlayCompileSpec, Serializable {
+    Iterable<RelativeFile> getSources();
+
+    boolean isJavaProject();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompiler.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompiler.java
new file mode 100644
index 0000000..1472892
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompiler.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.internal.tasks.SimpleWorkResult;
+import org.gradle.api.tasks.WorkResult;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaOptionInvocationWrapper;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Twirl compiler uses reflection to load and invoke the actual compiler classes/methods.
+ * See spec.versions for individual methods.
+ */
+public class TwirlCompiler implements Compiler<TwirlCompileSpec>, Serializable {
+
+    private final VersionedTwirlCompilerAdapter adapter;
+
+    public TwirlCompiler(VersionedTwirlCompilerAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    public WorkResult execute(TwirlCompileSpec spec) {
+        ArrayList<File> outputFiles = Lists.newArrayList();
+        try {
+            ClassLoader cl = getClass().getClassLoader();
+            ScalaMethod compile = adapter.getCompileMethod(cl);
+            Iterable<RelativeFile> sources = spec.getSources();
+            for (RelativeFile sourceFile : sources) {
+                Object result = compile.invoke(adapter.createCompileParameters(cl, sourceFile.getFile(), sourceFile.getBaseDir(), spec.getDestinationDir(), spec.isJavaProject()));
+                ScalaOptionInvocationWrapper<File> maybeFile = new ScalaOptionInvocationWrapper<File>(result);
+                if (maybeFile.isDefined()) {
+                    outputFiles.add(maybeFile.get());
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Error invoking Play Twirl template compiler.", e);
+        }
+
+        return new SimpleWorkResult(!outputFiles.isEmpty());
+    }
+
+    public Object getDependencyNotation() {
+        return adapter.getDependencyNotation();
+    }
+
+    public List<String> getClassLoaderPackages() {
+        return adapter.getClassLoaderPackages();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV10X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV10X.java
new file mode 100644
index 0000000..a570b98
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV10X.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.scala.internal.reflect.ScalaCodecMapper;
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaReflectionUtil;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.List;
+
+class TwirlCompilerAdapterV10X implements VersionedTwirlCompilerAdapter {
+
+    private static final String DEFAULT_JAVA_IMPORTS =
+              "import models._;"
+            + "import controllers._;"
+            + "import java.lang._;"
+            + "import java.util._;"
+            + "import scala.collection.JavaConversions._;"
+            + "import scala.collection.JavaConverters._;"
+            + "import play.api.i18n._;"
+            + "import play.core.j.PlayMagicForJava._;"
+            + "import play.mvc._;"
+            + "import play.data._;"
+            + "import play.api.data.Field;"
+            + "import play.mvc.Http.Context.Implicit._;"
+            + "import views.html._;";
+
+    private static final String DEFAULT_SCALA_IMPORTS =
+              "import models._;"
+            + "import controllers._;"
+            + "import play.api.i18n._;"
+            + "import play.api.mvc._;"
+            + "import play.api.data._;"
+            + "import views.html._;";
+
+    private final String scalaVersion;
+    private final String twirlVersion;
+
+    public TwirlCompilerAdapterV10X(String twirlVersion, String scalaVersion) {
+        this.scalaVersion = scalaVersion;
+        this.twirlVersion = twirlVersion;
+    }
+
+    public ScalaMethod getCompileMethod(ClassLoader cl) throws ClassNotFoundException {
+        return ScalaReflectionUtil.scalaMethod(
+                cl,
+                "play.twirl.compiler.TwirlCompiler",
+                "compile",
+                File.class,
+                File.class,
+                File.class,
+                String.class,
+                String.class,
+                cl.loadClass(ScalaCodecMapper.getClassName()),
+                boolean.class,
+                boolean.class
+        );
+    }
+
+    public Object[] createCompileParameters(ClassLoader cl, File file, File sourceDirectory, File destinationDirectory, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        return new Object[] {
+                file,
+                sourceDirectory,
+                destinationDirectory,
+                "play.twirl.api.HtmlFormat",
+                javaProject ? DEFAULT_JAVA_IMPORTS : DEFAULT_SCALA_IMPORTS,
+                ScalaCodecMapper.create(cl, "UTF-8"),
+                isInclusiveDots(),
+                isUseOldParser()
+        };
+    }
+
+    private boolean isInclusiveDots() {
+        return false;
+    }
+
+    private boolean isUseOldParser() {
+        return false;
+    }
+
+    public List<String> getClassLoaderPackages() {
+        return Arrays.asList("play.twirl.compiler", "scala.io"); //scala.io is for Codec which is a parameter to twirl
+    }
+
+    public Object getDependencyNotation() {
+        return String.format("com.typesafe.play:twirl-compiler_%s:%s", scalaVersion, twirlVersion);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV22X.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV22X.java
new file mode 100644
index 0000000..5bbb67d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerAdapterV22X.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.scala.internal.reflect.ScalaMethod;
+import org.gradle.scala.internal.reflect.ScalaReflectionUtil;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.List;
+
+class TwirlCompilerAdapterV22X implements VersionedTwirlCompilerAdapter {
+
+    // TODO:DAZ Validate these
+    private static final String DEFAULT_JAVA_IMPORTS =
+              "import play.api.templates._;"
+            + "import play.api.templates.PlayMagic._;"
+            + "import models._;"
+            + "import controllers._;"
+            + "import play.api.i18n._;"
+            + "import play.api.mvc._;"
+            + "import play.api.mvc._;"
+            + "import play.api.data._;"
+            + "import views.html._;";
+
+    private static final String DEFAULT_SCALA_IMPORTS =
+              "import play.api.templates._;"
+            + "import play.api.templates.PlayMagic._;"
+            + "import models._;"
+            + "import controllers._;"
+            + "import play.api.i18n._;"
+            + "import play.api.mvc._;"
+            + "import play.api.mvc._;"
+            + "import play.api.data._;"
+            + "import views.html._;";
+
+    private final String twirlVersion;
+    private final String scalaVersion;
+
+    public TwirlCompilerAdapterV22X(String twirlVersion, String scalaVersion) {
+        this.twirlVersion = twirlVersion;
+        this.scalaVersion = scalaVersion;
+    }
+
+    public ScalaMethod getCompileMethod(final ClassLoader cl) throws ClassNotFoundException {
+        return ScalaReflectionUtil.scalaMethod(
+                cl,
+                "play.templates.ScalaTemplateCompiler",
+                "compile",
+                File.class,
+                File.class,
+                File.class,
+                String.class,
+                String.class
+        );
+    }
+
+    @Override
+    public Object[] createCompileParameters(ClassLoader cl, File file, File sourceDirectory, File destinationDirectory, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        return new Object[] {
+                file,
+                sourceDirectory,
+                destinationDirectory,
+                "play.api.templates.HtmlFormat",
+                javaProject ? DEFAULT_JAVA_IMPORTS : DEFAULT_SCALA_IMPORTS
+        };
+    }
+
+    public List<String> getClassLoaderPackages() {
+        return Arrays.asList("play.templates");
+    }
+
+    public Object getDependencyNotation() {
+        return String.format("com.typesafe.play:templates-compiler_%s:%s", scalaVersion, twirlVersion);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerFactory.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerFactory.java
new file mode 100644
index 0000000..5d7b176
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/TwirlCompilerFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.play.internal.platform.PlayMajorVersion;
+import org.gradle.play.platform.PlayPlatform;
+
+public class TwirlCompilerFactory {
+    public static TwirlCompiler create(PlayPlatform playPlatform) {
+        return new TwirlCompiler(createAdapter(playPlatform));
+    }
+
+    public static VersionedTwirlCompilerAdapter createAdapter(PlayPlatform playPlatform) {
+        String playVersion = playPlatform.getPlayVersion();
+        String scalaCompatibilityVersion = playPlatform.getScalaPlatform().getScalaCompatibilityVersion();
+        switch (PlayMajorVersion.forPlatform(playPlatform)) {
+            case PLAY_2_2_X:
+                return new TwirlCompilerAdapterV22X("2.2.3", scalaCompatibilityVersion);
+            case PLAY_2_3_X:
+                return new TwirlCompilerAdapterV10X("1.0.2", scalaCompatibilityVersion);
+            default:
+                throw new RuntimeException("Could not create Twirl compile spec for Play version: " + playVersion);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/VersionedTwirlCompilerAdapter.java b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/VersionedTwirlCompilerAdapter.java
new file mode 100644
index 0000000..55098c8
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/internal/twirl/VersionedTwirlCompilerAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.twirl;
+
+import org.gradle.scala.internal.reflect.ScalaMethod;
+
+import java.io.File;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+public interface VersionedTwirlCompilerAdapter extends Serializable {
+    public Object getDependencyNotation();
+
+    ScalaMethod getCompileMethod(ClassLoader cl) throws ClassNotFoundException;
+
+    Object[] createCompileParameters(ClassLoader cl, File file, File sourceDirectory, File destinationDirectory, boolean javaProject) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException;
+
+    List<String> getClassLoaderPackages();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/package-info.java
new file mode 100644
index 0000000..a45e9b6
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 the Play Framework support in Gradle.
+ */
+package org.gradle.play;
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/platform/PlayPlatform.java b/subprojects/platform-play/src/main/java/org/gradle/play/platform/PlayPlatform.java
new file mode 100644
index 0000000..ff7635d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/platform/PlayPlatform.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.platform;
+
+import org.gradle.api.Incubating;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.language.scala.ScalaPlatform;
+import org.gradle.platform.base.Platform;
+
+/**
+ * Defines and configures a Play Framework environment including versions of Play, Scala and Java.
+ */
+ at Incubating
+public interface PlayPlatform extends Platform {
+
+    public String getPlayVersion();
+
+    public ScalaPlatform getScalaPlatform();
+
+    public JavaPlatform getJavaPlatform();
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/platform/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/platform/package-info.java
new file mode 100644
index 0000000..22d8ce9
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/platform/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for managing play platform
+ * */
+package org.gradle.play.platform;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayApplicationPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayApplicationPlugin.java
new file mode 100644
index 0000000..c2cfbca
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayApplicationPlugin.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.play.plugins;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.*;
+import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
+import org.gradle.api.internal.java.DefaultJvmResourceSet;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.plugins.ExtensionContainer;
+import org.gradle.api.tasks.scala.IncrementalCompileOptions;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.java.JavaSourceSet;
+import org.gradle.language.java.internal.DefaultJavaLanguageSourceSet;
+import org.gradle.language.java.plugins.JavaLanguagePlugin;
+import org.gradle.language.jvm.JvmResourceSet;
+import org.gradle.language.routes.RoutesSourceSet;
+import org.gradle.language.routes.internal.DefaultRoutesSourceSet;
+import org.gradle.language.scala.ScalaLanguageSourceSet;
+import org.gradle.language.scala.internal.DefaultScalaLanguageSourceSet;
+import org.gradle.language.scala.plugins.ScalaLanguagePlugin;
+import org.gradle.language.scala.tasks.PlatformScalaCompile;
+import org.gradle.language.twirl.TwirlSourceSet;
+import org.gradle.language.twirl.internal.DefaultTwirlSourceSet;
+import org.gradle.model.*;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.*;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.platform.base.internal.DefaultPlatformRequirement;
+import org.gradle.platform.base.internal.PlatformRequirement;
+import org.gradle.platform.base.internal.PlatformResolvers;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.play.JvmClasses;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.PlayApplicationSpec;
+import org.gradle.play.PublicAssets;
+import org.gradle.play.internal.*;
+import org.gradle.play.internal.platform.PlayPlatformInternal;
+import org.gradle.play.internal.routes.RoutesCompileSpec;
+import org.gradle.play.internal.run.PlayApplicationRunner;
+import org.gradle.play.internal.twirl.TwirlCompileSpec;
+import org.gradle.play.internal.twirl.TwirlCompilerFactory;
+import org.gradle.play.platform.PlayPlatform;
+import org.gradle.play.tasks.PlayRun;
+import org.gradle.play.tasks.RoutesCompile;
+import org.gradle.play.tasks.TwirlCompile;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * Plugin for Play Framework component support. Registers the {@link org.gradle.play.PlayApplicationSpec} component type for the {@link org.gradle.platform.base.ComponentSpecContainer}.
+ */
+
+ at Incubating
+public class PlayApplicationPlugin implements Plugin<Project> {
+    private final static String DEFAULT_PLAY_VERSION = "2.3.7";
+    public static final int DEFAULT_HTTP_PORT = 9000;
+
+    @Override
+    public void apply(Project project) {
+        project.getPluginManager().apply(JavaLanguagePlugin.class);
+        project.getPluginManager().apply(ScalaLanguagePlugin.class);
+        project.getExtensions().create("playConfigurations", PlayPluginConfigurations.class, project.getConfigurations(), project.getDependencies());
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+        @Model
+        PlayPluginConfigurations configurations(ExtensionContainer extensions) {
+            return extensions.getByType(PlayPluginConfigurations.class);
+        }
+
+        @Model
+        FileResolver fileResolver(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(FileResolver.class);
+        }
+
+        @ComponentType
+        void register(ComponentTypeBuilder<PlayApplicationSpec> builder) {
+            builder.defaultImplementation(DefaultPlayApplicationSpec.class);
+        }
+
+        @Mutate
+        public void registerPlatformResolver(PlatformResolvers platformResolvers) {
+            platformResolvers.register(new PlayPlatformResolver());
+        }
+
+        @Mutate
+        void createDefaultPlayApp(CollectionBuilder<PlayApplicationSpec> builder) {
+            builder.create("play");
+        }
+
+        @BinaryType
+        void registerApplication(BinaryTypeBuilder<PlayApplicationBinarySpec> builder) {
+            builder.defaultImplementation(DefaultPlayApplicationBinarySpec.class);
+        }
+
+        @LanguageType
+        void registerTwirlLanguageType(LanguageTypeBuilder<TwirlSourceSet> builder) {
+            builder.setLanguageName("twirl");
+            builder.defaultImplementation(DefaultTwirlSourceSet.class);
+        }
+
+        @LanguageType
+        void registerRoutesLanguageType(LanguageTypeBuilder<RoutesSourceSet> builder) {
+            builder.setLanguageName("routes");
+            builder.defaultImplementation(DefaultRoutesSourceSet.class);
+        }
+
+        @Mutate
+        void configureDefaultPlaySources(ComponentSpecContainer components, ServiceRegistry serviceRegistry) {
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            components.withType(PlayApplicationSpec.class).all(new Action<PlayApplicationSpec>() {
+                public void execute(PlayApplicationSpec playComponent) {
+                    // TODO:DAZ Scala source set type should be registered via scala-lang plugin
+                    ScalaLanguageSourceSet scalaSources = BaseLanguageSourceSet.create(DefaultScalaLanguageSourceSet.class, "scala", playComponent.getName(), fileResolver, instantiator);
+
+                    // Compile scala/java sources under /app\
+                    // TODO:DAZ Should be selecting 'controllers/**' and 'model/**' I think, allowing user to add more includes
+                    scalaSources.getSource().srcDir("app");
+                    scalaSources.getSource().include("**/*.scala");
+                    FunctionalSourceSet sources = ((ComponentSpecInternal) playComponent).getSources();
+                    sources.add(scalaSources);
+
+                    JavaSourceSet javaSources = BaseLanguageSourceSet.create(DefaultJavaLanguageSourceSet.class, "java", playComponent.getName(), fileResolver, instantiator);
+                    javaSources.getSource().srcDir("app");
+                    javaSources.getSource().include("**/*.java");
+                    sources.add(javaSources);
+
+                    DefaultSourceDirectorySet resourcesDirectorySet = new DefaultSourceDirectorySet("resources", fileResolver);
+                    JvmResourceSet appResources = instantiator.newInstance(DefaultJvmResourceSet.class, "resources", playComponent.getName(), resourcesDirectorySet);
+                    appResources.getSource().srcDirs("conf");
+                    sources.add(appResources);
+                }
+            });
+        }
+
+        @Validate
+        void failOnMultiplePlayComponents(CollectionBuilder<PlayApplicationSpec> container) {
+            if (container.size() >= 2) {
+                throw new GradleException("Multiple components of type 'PlayApplicationSpec' are not supported.");
+            }
+        }
+
+        @Finalize
+        void failOnMultipleTargetPlatforms(ComponentSpecContainer container) {
+            for (PlayApplicationSpecInternal playApplicationSpec : container.withType(PlayApplicationSpecInternal.class)) {
+                if (playApplicationSpec.getTargetPlatforms().size() > 1) {
+                    throw new GradleException("Multiple target platforms for 'PlayApplicationSpec' is not (yet) supported.");
+                }
+            }
+        }
+
+        @ComponentBinaries
+        void createBinaries(CollectionBuilder<PlayApplicationBinarySpec> binaries, final PlayApplicationSpec componentSpec,
+                            final PlatformResolvers platforms, final PlayPluginConfigurations configurations, final ServiceRegistry serviceRegistry,
+                            @Path("buildDir") final File buildDir, final ProjectIdentifier projectIdentifier) {
+
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            final ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+            final String binaryName = String.format("%sBinary", componentSpec.getName());
+
+            binaries.create(binaryName, new Action<PlayApplicationBinarySpec>() {
+                public void execute(PlayApplicationBinarySpec playBinary) {
+                    PlayApplicationBinarySpecInternal playBinaryInternal = (PlayApplicationBinarySpecInternal) playBinary;
+                    playBinaryInternal.setApplication(componentSpec);
+                    final File binaryBuildDir = new File(buildDir, binaryName);
+
+                    final PlayPlatform chosenPlatform = resolveTargetPlatform(componentSpec, platforms, configurations);
+                    initialiseConfigurations(configurations, chosenPlatform);
+
+                    playBinaryInternal.setTargetPlatform(chosenPlatform);
+                    playBinaryInternal.setToolResolver(toolResolver);
+
+                    File mainJar = new File(binaryBuildDir, String.format("lib/%s.jar", projectIdentifier.getName()));
+                    File assetsJar = new File(binaryBuildDir, String.format("lib/%s-assets.jar", projectIdentifier.getName()));
+                    playBinaryInternal.setJarFile(mainJar);
+                    playBinaryInternal.setAssetsJarFile(assetsJar);
+
+                    configurations.getPlay().addArtifact(new DefaultPublishArtifact(projectIdentifier.getName(), "jar", "jar", null, new Date(), mainJar, playBinaryInternal));
+                    configurations.getPlay().addArtifact(new DefaultPublishArtifact(projectIdentifier.getName(), "jar", "jar", "assets", new Date(), assetsJar, playBinaryInternal));
+
+                    JvmClasses classes = playBinary.getClasses();
+                    classes.setClassesDir(new File(binaryBuildDir, "classes"));
+
+                    DomainObjectSet<JvmResourceSet> jvmResourceSets = componentSpec.getSource().withType(JvmResourceSet.class);
+                    for (JvmResourceSet jvmResourceSet : jvmResourceSets) {
+                        for (File resourceDir : jvmResourceSet.getSource()) {
+                            classes.addResourceDir(resourceDir);
+                        }
+                    }
+
+                    // TODO:DAZ These should be configured on the component
+                    PublicAssets assets = playBinary.getAssets();
+                    assets.addAssetDir(new File(projectIdentifier.getProjectDir(), "public"));
+
+                    playBinaryInternal.setClasspath(configurations.getPlay().getFileCollection());
+                }
+            });
+        }
+
+        private PlayPlatform resolveTargetPlatform(PlayApplicationSpec componentSpec, final PlatformResolvers platforms, PlayPluginConfigurations configurations) {
+            PlatformRequirement targetPlatform = getTargetPlatform((PlayApplicationSpecInternal) componentSpec);
+            return platforms.resolve(PlayPlatform.class, targetPlatform);
+        }
+
+        private PlatformRequirement getTargetPlatform(PlayApplicationSpecInternal playApplicationSpec) {
+            if (playApplicationSpec.getTargetPlatforms().isEmpty()) {
+                String defaultPlayPlatform = String.format("play-%s", DEFAULT_PLAY_VERSION);
+                return DefaultPlatformRequirement.create(defaultPlayPlatform);
+            }
+            if (playApplicationSpec.getTargetPlatforms().size() == 1) {
+                return playApplicationSpec.getTargetPlatforms().get(0);
+            }
+            throw new InvalidUserDataException("Play application can only target a single platform");
+        }
+
+        private void initialiseConfigurations(PlayPluginConfigurations configurations, PlayPlatform playPlatform) {
+            configurations.getPlayPlatform().addDependency(((PlayPlatformInternal) playPlatform).getDependencyNotation("play"));
+            configurations.getPlayTest().addDependency(((PlayPlatformInternal) playPlatform).getDependencyNotation("play-test"));
+            configurations.getPlayRun().addDependency(((PlayPlatformInternal) playPlatform).getDependencyNotation("play-docs"));
+        }
+
+        @Mutate
+        void createTwirlSourceSets(CollectionBuilder<PlayApplicationSpec> components) {
+            components.beforeEach(new Action<PlayApplicationSpec>() {
+                @Override
+                public void execute(PlayApplicationSpec playComponent) {
+                    TwirlSourceSet twirlSourceSet = ((ComponentSpecInternal) playComponent).getSources().create("twirlTemplates", TwirlSourceSet.class);
+                    twirlSourceSet.getSource().srcDir("app");
+                    twirlSourceSet.getSource().include("**/*.html");
+                }
+            });
+        }
+
+        @Mutate
+        void createRoutesSourceSets(CollectionBuilder<PlayApplicationSpec> components) {
+            components.beforeEach(new Action<PlayApplicationSpec>() {
+                @Override
+                public void execute(PlayApplicationSpec playComponent) {
+                    RoutesSourceSet routesSourceSet = ((ComponentSpecInternal) playComponent).getSources().create("routesSources", RoutesSourceSet.class);
+                    routesSourceSet.getSource().srcDir("conf");
+                    routesSourceSet.getSource().include("routes");
+                    routesSourceSet.getSource().include("*.routes");
+                }
+            });
+        }
+
+        @Mutate
+        void createGeneratedScalaSourceSets(CollectionBuilder<PlayApplicationBinarySpec> binaries, final ServiceRegistry serviceRegistry) {
+            createGeneratedScalaSourceSetsForType(TwirlSourceSet.class, binaries, serviceRegistry);
+            createGeneratedScalaSourceSetsForType(RoutesSourceSet.class, binaries, serviceRegistry);
+        }
+
+        void createGeneratedScalaSourceSetsForType(final Class<? extends LanguageSourceSet> languageSourceSetType, CollectionBuilder<PlayApplicationBinarySpec> binaries, ServiceRegistry serviceRegistry) {
+            final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            binaries.all(new Action<PlayApplicationBinarySpec>() {
+                @Override
+                public void execute(PlayApplicationBinarySpec playApplicationBinarySpec) {
+                    for (LanguageSourceSet languageSourceSet : playApplicationBinarySpec.getSource().withType(languageSourceSetType)) {
+                        ScalaLanguageSourceSet twirlScalaSources = BaseLanguageSourceSet.create(DefaultScalaLanguageSourceSet.class, String.format("%sScalaSources", languageSourceSet.getName()), playApplicationBinarySpec.getName(), fileResolver, instantiator);
+                        playApplicationBinarySpec.getGeneratedScala().put(languageSourceSet, twirlScalaSources);
+                    }
+                }
+            });
+        }
+
+        @BinaryTasks
+        void createTwirlCompileTasks(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary, ServiceRegistry serviceRegistry, @Path("buildDir") final File buildDir) {
+            final ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+            final ResolvedTool<Compiler<TwirlCompileSpec>> compilerTool = toolResolver.resolveCompiler(TwirlCompileSpec.class, binary.getTargetPlatform());
+            for (final TwirlSourceSet twirlSourceSet : binary.getSource().withType(TwirlSourceSet.class)) {
+                final String twirlCompileTaskName = String.format("twirlCompile%s%s", StringUtils.capitalize(twirlSourceSet.getName()), StringUtils.capitalize(binary.getName()));
+                final File twirlCompileOutputDirectory = srcOutputDirectory(buildDir, binary, twirlCompileTaskName);
+
+                tasks.create(twirlCompileTaskName, TwirlCompile.class, new Action<TwirlCompile>() {
+                    public void execute(TwirlCompile twirlCompile) {
+                        twirlCompile.setDependencyNotation(TwirlCompilerFactory.createAdapter(binary.getTargetPlatform()).getDependencyNotation());
+                        twirlCompile.setSource(twirlSourceSet.getSource());
+                        twirlCompile.setOutputDirectory(twirlCompileOutputDirectory);
+                        twirlCompile.setCompilerTool(compilerTool);
+
+                        ScalaLanguageSourceSet twirlScalaSources = binary.getGeneratedScala().get(twirlSourceSet);
+                        twirlScalaSources.getSource().srcDir(twirlCompileOutputDirectory);
+                        twirlScalaSources.builtBy(twirlCompile);
+                    }
+                });
+            }
+        }
+
+        @BinaryTasks
+        void createRoutesCompileTasks(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary, ServiceRegistry serviceRegistry, @Path("buildDir") final File buildDir) {
+            final ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+            final ResolvedTool<Compiler<RoutesCompileSpec>> compilerTool = toolResolver.resolveCompiler(RoutesCompileSpec.class, binary.getTargetPlatform());
+            for (final RoutesSourceSet routesSourceSet : binary.getSource().withType(RoutesSourceSet.class)) {
+                final String routesCompileTaskName = String.format("routesCompile%s%s", StringUtils.capitalize(routesSourceSet.getName()), StringUtils.capitalize(binary.getName()));
+                final File routesCompilerOutputDirectory = srcOutputDirectory(buildDir, binary, routesCompileTaskName);
+
+                tasks.create(routesCompileTaskName, RoutesCompile.class, new Action<RoutesCompile>() {
+                    public void execute(RoutesCompile routesCompile) {
+                        routesCompile.setCompilerTool(compilerTool);
+                        routesCompile.setAdditionalImports(new ArrayList<String>());
+                        routesCompile.setSource(routesSourceSet.getSource());
+                        routesCompile.setOutputDirectory(routesCompilerOutputDirectory);
+
+                        ScalaLanguageSourceSet routesScalaSources = binary.getGeneratedScala().get(routesSourceSet);
+                        routesScalaSources.getSource().srcDir(routesCompilerOutputDirectory);
+                        routesScalaSources.builtBy(routesCompile);
+                    }
+                });
+            }
+        }
+
+        @BinaryTasks
+        void createScalaCompileTask(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary, @Path("buildDir") final File buildDir) {
+            final String scalaCompileTaskName = String.format("scalaCompile%s", StringUtils.capitalize(binary.getName()));
+            tasks.create(scalaCompileTaskName, PlatformScalaCompile.class, new Action<PlatformScalaCompile>() {
+                public void execute(PlatformScalaCompile scalaCompile) {
+
+                    scalaCompile.setDestinationDir(binary.getClasses().getClassesDir());
+                    scalaCompile.setPlatform(binary.getTargetPlatform().getScalaPlatform());
+                    //infer scala classpath
+                    String targetCompatibility = binary.getTargetPlatform().getJavaPlatform().getTargetCompatibility().getMajorVersion();
+                    scalaCompile.setSourceCompatibility(targetCompatibility);
+                    scalaCompile.setTargetCompatibility(targetCompatibility);
+
+                    IncrementalCompileOptions incrementalOptions = scalaCompile.getScalaCompileOptions().getIncrementalOptions();
+                    incrementalOptions.setAnalysisFile(new File(buildDir, String.format("tmp/scala/compilerAnalysis/%s.analysis", scalaCompileTaskName)));
+
+                    for (LanguageSourceSet appSources : binary.getSource().withType(ScalaLanguageSourceSet.class)) {
+                        scalaCompile.source(appSources.getSource());
+                        scalaCompile.dependsOn(appSources);
+                    }
+
+                    for (LanguageSourceSet appSources : binary.getSource().withType(JavaSourceSet.class)) {
+                        scalaCompile.source(appSources.getSource());
+                        scalaCompile.dependsOn(appSources);
+                    }
+
+                    for (LanguageSourceSet generatedSourceSet : binary.getGeneratedScala().values()) {
+                        scalaCompile.source(generatedSourceSet.getSource());
+                        scalaCompile.dependsOn(generatedSourceSet.getBuildDependencies());
+                    }
+
+                    scalaCompile.setClasspath(((PlayApplicationBinarySpecInternal)binary).getClasspath());
+
+                    binary.getClasses().builtBy(scalaCompile);
+                }
+            });
+        }
+
+        @BinaryTasks
+        void createJarTasks(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary) {
+            String jarTaskName = String.format("create%sJar", StringUtils.capitalize(binary.getName()));
+            tasks.create(jarTaskName, Jar.class, new Action<Jar>() {
+                public void execute(Jar jar) {
+                    jar.setDestinationDir(binary.getJarFile().getParentFile());
+                    jar.setArchiveName(binary.getJarFile().getName());
+                    jar.from(binary.getClasses().getClassesDir());
+                    jar.from(binary.getClasses().getResourceDirs());
+                    jar.dependsOn(binary.getClasses());
+                }
+            });
+
+            String assetsJarTaskName = String.format("create%sAssetsJar", StringUtils.capitalize(binary.getName()));
+            tasks.create(assetsJarTaskName, Jar.class, new Action<Jar>() {
+                public void execute(Jar jar) {
+                    jar.setDestinationDir(binary.getAssetsJarFile().getParentFile());
+                    jar.setArchiveName(binary.getAssetsJarFile().getName());
+                    jar.setClassifier("assets");
+                    CopySpecInternal newSpec = jar.getRootSpec().addChild();
+                    newSpec.from(binary.getAssets().getAssetDirs());
+                    newSpec.into("public");
+                    jar.dependsOn(binary.getAssets());
+                }
+            });
+        }
+
+        // TODO:DAZ Need a nice way to create tasks that are associated with a binary but not part of _building_ it.
+        @Mutate
+        void createPlayRunTask(CollectionBuilder<Task> tasks, BinaryContainer binaryContainer, ServiceRegistry serviceRegistry, final PlayPluginConfigurations configurations) {
+            ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+            for (final PlayApplicationBinarySpecInternal binary : binaryContainer.withType(PlayApplicationBinarySpecInternal.class)) {
+                final ResolvedTool<PlayApplicationRunner> playApplicationRunnerTool = toolResolver.resolve(PlayApplicationRunner.class, binary.getTargetPlatform());
+                String runTaskName = String.format("run%s", StringUtils.capitalize(binary.getName()));
+                tasks.create(runTaskName, PlayRun.class, new Action<PlayRun>() {
+                    public void execute(PlayRun playRun) {
+                        playRun.setHttpPort(DEFAULT_HTTP_PORT);
+                        playRun.setPlayApplicationRunnerTool(playApplicationRunnerTool);
+                        playRun.setApplicationJar(binary.getJarFile());
+                        playRun.setAssetsJar(binary.getAssetsJarFile());
+                        playRun.setRuntimeClasspath(configurations.getPlayRun().getFileCollection());
+                        playRun.dependsOn(binary.getBuildTask());
+                    }
+                });
+            }
+        }
+
+        private File srcOutputDirectory(File buildDir, PlayApplicationBinarySpec binary, String taskName) {
+            return new File(buildDir, String.format("%s/src/%s", binary.getName(), taskName));
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayCoffeeScriptPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayCoffeeScriptPlugin.java
new file mode 100644
index 0000000..b492159
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayCoffeeScriptPlugin.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.coffeescript.CoffeeScriptSourceSet;
+import org.gradle.language.coffeescript.internal.DefaultCoffeeScriptSourceSet;
+import org.gradle.language.javascript.JavaScriptSourceSet;
+import org.gradle.language.javascript.internal.DefaultJavaScriptSourceSet;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.BinaryTasks;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.PlayApplicationSpec;
+import org.gradle.play.tasks.PlayCoffeeScriptCompile;
+
+import java.io.File;
+
+import static org.apache.commons.lang.StringUtils.capitalize;
+
+/**
+ * Plugin for adding coffeescript compilation to a Play application.  Adds support for
+ * defining {@link org.gradle.language.coffeescript.CoffeeScriptSourceSet} source sets.  A
+ * "coffeeScriptAssets" source set is created by default.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Incubating
+public class PlayCoffeeScriptPlugin extends RuleSource {
+    private static final String DEFAULT_COFFEESCRIPT_VERSION = "1.8.0";
+    private static final String DEFAULT_RHINO_VERSION = "1.7R4";
+
+    static String getDefaultCoffeeScriptDependencyNotation() {
+        return String.format("org.coffeescript:coffee-script-js:%s at js", DEFAULT_COFFEESCRIPT_VERSION);
+    }
+
+    static String getDefaultRhinoDependencyNotation() {
+        return String.format("org.mozilla:rhino:%s", DEFAULT_RHINO_VERSION);
+    }
+
+    @LanguageType
+    void registerCoffeeScript(LanguageTypeBuilder<CoffeeScriptSourceSet> builder) {
+        builder.setLanguageName("coffeeScript");
+        builder.defaultImplementation(DefaultCoffeeScriptSourceSet.class);
+    }
+
+    @Mutate
+    void createCoffeeScriptSourceSets(CollectionBuilder<PlayApplicationSpec> components) {
+        components.beforeEach(new Action<PlayApplicationSpec>() {
+            @Override
+            public void execute(PlayApplicationSpec playComponent) {
+                // TODO - should have some way to lookup using internal type
+                CoffeeScriptSourceSet coffeeScriptSourceSet = ((ComponentSpecInternal) playComponent).getSources().create("coffeeScriptAssets", CoffeeScriptSourceSet.class);
+                coffeeScriptSourceSet.getSource().srcDir("app/assets");
+                coffeeScriptSourceSet.getSource().include("**/*.coffee");
+            }
+        });
+    }
+
+    @Mutate
+    void createGeneratedJavaScriptSourceSets(CollectionBuilder<PlayApplicationBinarySpec> binaries, final ServiceRegistry serviceRegistry) {
+        final FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+        final Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+        binaries.all(new Action<PlayApplicationBinarySpec>() {
+            @Override
+            public void execute(PlayApplicationBinarySpec playApplicationBinarySpec) {
+                for (CoffeeScriptSourceSet coffeeScriptSourceSet : playApplicationBinarySpec.getSource().withType(CoffeeScriptSourceSet.class)) {
+                    JavaScriptSourceSet javaScriptSourceSet = BaseLanguageSourceSet.create(DefaultJavaScriptSourceSet.class, String.format("%sJavaScript", coffeeScriptSourceSet.getName()), playApplicationBinarySpec.getName(), fileResolver, instantiator);
+                    playApplicationBinarySpec.getGeneratedJavaScript().put(coffeeScriptSourceSet, javaScriptSourceSet);
+                }
+            }
+        });
+    }
+
+    @BinaryTasks
+    void createCoffeeScriptTasks(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary, @Path("buildDir") final File buildDir) {
+        tasks.beforeEach(PlayCoffeeScriptCompile.class, new Action<PlayCoffeeScriptCompile>() {
+            @Override
+            public void execute(PlayCoffeeScriptCompile coffeeScriptCompile) {
+                coffeeScriptCompile.setRhinoClasspathNotation(getDefaultRhinoDependencyNotation());
+                coffeeScriptCompile.setCoffeeScriptJsNotation(getDefaultCoffeeScriptDependencyNotation());
+            }
+        });
+
+        for (final CoffeeScriptSourceSet coffeeScriptSourceSet : binary.getSource().withType(CoffeeScriptSourceSet.class)) {
+            if (((LanguageSourceSetInternal) coffeeScriptSourceSet).getMayHaveSources()) {
+                final String compileTaskName = "compile" + capitalize(binary.getName()) + capitalize(coffeeScriptSourceSet.getName());
+                tasks.create(compileTaskName, PlayCoffeeScriptCompile.class, new Action<PlayCoffeeScriptCompile>() {
+                    @Override
+                    public void execute(PlayCoffeeScriptCompile coffeeScriptCompile) {
+                        File outputDirectory = outputDirectory(buildDir, binary, compileTaskName);
+                        coffeeScriptCompile.setDestinationDir(outputDirectory);
+                        coffeeScriptCompile.setSource(coffeeScriptSourceSet.getSource());
+
+                        JavaScriptSourceSet javaScriptSourceSet = binary.getGeneratedJavaScript().get(coffeeScriptSourceSet);
+                        javaScriptSourceSet.getSource().srcDir(outputDirectory);
+                        javaScriptSourceSet.builtBy(coffeeScriptCompile);
+                    }
+                });
+            }
+        }
+    }
+
+    private File outputDirectory(File buildDir, PlayApplicationBinarySpec binary, String taskName) {
+        return new File(buildDir, String.format("%s/src/%s", binary.getName(), taskName));
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayDistributionPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayDistributionPlugin.java
new file mode 100644
index 0000000..1dd7187
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayDistributionPlugin.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.*;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.file.copy.CopySpecInternal;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.application.CreateStartScripts;
+import org.gradle.api.tasks.bundling.Zip;
+import org.gradle.internal.Actions;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.model.Defaults;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.distribution.PlayDistributionContainer;
+import org.gradle.play.distribution.PlayDistribution;
+import org.gradle.play.internal.distribution.DefaultPlayDistributionContainer;
+import org.gradle.play.internal.distribution.DefaultPlayDistribution;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A plugin that adds a distribution zip to a Play application build.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Incubating
+public class PlayDistributionPlugin extends RuleSource {
+    public static final String DISTRIBUTION_GROUP = "distribution";
+
+    @Model
+    PlayDistributionContainer distributions(ServiceRegistry serviceRegistry) {
+        Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+        return new DefaultPlayDistributionContainer(instantiator);
+    }
+
+    @Mutate
+    void createLifecycleTasks(CollectionBuilder<Task> tasks) {
+        tasks.create("dist", new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                task.setDescription("Assembles all play distributions.");
+                task.setGroup(DISTRIBUTION_GROUP);
+            }
+        });
+
+        tasks.create("stage", new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                task.setDescription("Stages all play distributions.");
+                task.setGroup(DISTRIBUTION_GROUP);
+            }
+        });
+    }
+
+    @Defaults
+    void createDistributions(@Path("distributions") PlayDistributionContainer distributions, BinaryContainer binaryContainer, PlayPluginConfigurations configurations, ServiceRegistry serviceRegistry) {
+        FileOperations fileOperations = serviceRegistry.get(FileOperations.class);
+        Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+        for (PlayApplicationBinarySpec binary : binaryContainer.withType(PlayApplicationBinarySpec.class)) {
+            PlayDistribution distribution = instantiator.newInstance(DefaultPlayDistribution.class, binary.getName(), fileOperations.copySpec(Actions.doNothing()), binary);
+            distribution.setBaseName(binary.getName());
+            distributions.add(distribution);
+        }
+    }
+
+    @Mutate
+    void createDistributionContentTasks(CollectionBuilder<Task> tasks, final @Path("buildDir") File buildDir,
+                                        final @Path("distributions") PlayDistributionContainer distributions,
+                                        final PlayPluginConfigurations configurations) {
+        for (PlayDistribution distribution : distributions.withType(PlayDistribution.class)) {
+            final PlayApplicationBinarySpec binary = distribution.getBinary();
+            if (binary == null) {
+                throw new InvalidUserCodeException(String.format("Play Distribution '%s' does not have a configured Play binary.", distribution.getName()));
+            }
+
+            final File distJarDir = new File(buildDir, String.format("distributionJars/%s", distribution.getName()));
+            final String jarTaskName = String.format("create%sDistributionJar", StringUtils.capitalize(distribution.getName()));
+            tasks.create(jarTaskName, Jar.class, new Action<Jar>() {
+                @Override
+                public void execute(Jar jar) {
+                    jar.dependsOn(binary.getTasks().withType(Jar.class));
+                    jar.from(jar.getProject().zipTree(binary.getJarFile()));
+                    jar.setDestinationDir(distJarDir);
+                    jar.setArchiveName(binary.getJarFile().getName());
+
+                    Map<String, Object> classpath = Maps.newHashMap();
+                    classpath.put("Class-Path", new PlayManifestClasspath(configurations.getPlayRun(), binary.getAssetsJarFile()));
+                    jar.getManifest().attributes(classpath);
+                }
+            });
+            final Task distributionJar = tasks.get(jarTaskName);
+
+            final File scriptsDir = new File(buildDir, String.format("scripts/%s", distribution.getName()));
+            String createStartScriptsTaskName = String.format("create%sStartScripts", StringUtils.capitalize(distribution.getName()));
+            tasks.create(createStartScriptsTaskName, CreateStartScripts.class, new Action<CreateStartScripts>() {
+                @Override
+                public void execute(CreateStartScripts createStartScripts) {
+                    createStartScripts.setDescription("Creates OS specific scripts to run the play application.");
+                    createStartScripts.setClasspath(distributionJar.getOutputs().getFiles());
+                    createStartScripts.setMainClassName("play.core.server.NettyServer");
+                    createStartScripts.setApplicationName(binary.getName());
+                    createStartScripts.setOutputDir(scriptsDir);
+                }
+            });
+            Task createStartScripts = tasks.get(createStartScriptsTaskName);
+
+            CopySpecInternal distSpec = (CopySpecInternal) distribution.getContents();
+            CopySpec libSpec = distSpec.addChild().into("lib");
+            libSpec.from(distributionJar);
+            libSpec.from(binary.getAssetsJarFile());
+            libSpec.from(configurations.getPlayRun().getFileCollection());
+
+            CopySpec binSpec = distSpec.addChild().into("bin");
+            binSpec.from(createStartScripts);
+            binSpec.setFileMode(0755);
+
+            CopySpec confSpec = distSpec.addChild().into("conf");
+            confSpec.from("conf").exclude("routes");
+            distSpec.from("README");
+        }
+    }
+
+    @Mutate
+    void createDistributionZipTasks(CollectionBuilder<Task> tasks, final @Path("buildDir") File buildDir, PlayDistributionContainer distributions) {
+        for (final PlayDistribution distribution : distributions.withType(PlayDistribution.class)) {
+            final String stageTaskName = String.format("stage%sDist", StringUtils.capitalize(distribution.getName()));
+            final File stageDir = new File(buildDir, "stage");
+            final String baseName = StringUtils.isNotEmpty(distribution.getBaseName()) ? distribution.getBaseName() : distribution.getName();
+            tasks.create(stageTaskName, Copy.class, new Action<Copy>() {
+                @Override
+                public void execute(Copy copy) {
+                    copy.setDescription("Copies the binary distribution to a staging directory.");
+                    copy.setGroup(DISTRIBUTION_GROUP);
+                    copy.setDestinationDir(stageDir);
+
+                    CopySpecInternal baseSpec = copy.getRootSpec().addChild();
+                    baseSpec.into(baseName);
+                    baseSpec.with(distribution.getContents());
+                }
+            });
+            tasks.named("stage", new Action<Task>() {
+                @Override
+                public void execute(Task task) {
+                    task.dependsOn(stageTaskName);
+                }
+            });
+
+            final Task stageTask = tasks.get(stageTaskName);
+            final String distributionTaskName = String.format("create%sDist", StringUtils.capitalize(distribution.getName()));
+            tasks.create(distributionTaskName, Zip.class, new Action<Zip>() {
+                @Override
+                public void execute(final Zip zip) {
+                    zip.setDescription("Bundles the play binary as a distribution.");
+                    zip.setGroup(DISTRIBUTION_GROUP);
+                    zip.setArchiveName(String.format("%s.zip", baseName));
+                    zip.setDestinationDir(new File(buildDir, "distributions"));
+                    zip.from(stageTask);
+                }
+            });
+
+            tasks.named("dist", new Action<Task>() {
+                @Override
+                public void execute(Task task) {
+                    task.dependsOn(distributionTaskName);
+                }
+            });
+        }
+    }
+
+    /**
+     * Represents a classpath to be defined in a jar manifest
+     */
+    static class PlayManifestClasspath {
+        final PlayPluginConfigurations.PlayConfiguration playConfiguration;
+        final File assetsJarFile;
+
+        public PlayManifestClasspath(PlayPluginConfigurations.PlayConfiguration playConfiguration, File assetsJarFile) {
+            this.playConfiguration = playConfiguration;
+            this.assetsJarFile = assetsJarFile;
+        }
+
+        @Override
+        public String toString() {
+            Set<File> classpathFiles = playConfiguration.getFileCollection().getFiles();
+            classpathFiles.add(assetsJarFile);
+            Set<String> classpathFileNames = CollectionUtils.collect(classpathFiles, new Transformer<String, File>() {
+                @Override
+                public String transform(File file) {
+                    return file.getName();
+                }
+            });
+
+            return StringUtils.join(classpathFileNames, " ");
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayJavaScriptPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayJavaScriptPlugin.java
new file mode 100644
index 0000000..59f0b01
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayJavaScriptPlugin.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+import org.gradle.language.javascript.JavaScriptSourceSet;
+import org.gradle.language.javascript.internal.DefaultJavaScriptSourceSet;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.BinaryTasks;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.PlayApplicationSpec;
+import org.gradle.play.internal.javascript.JavaScriptCompileSpec;
+import org.gradle.play.tasks.JavaScriptMinify;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import java.io.File;
+
+import static org.apache.commons.lang.StringUtils.capitalize;
+
+/**
+ * Plugin for adding javascript processing to a Play application.  Registers "javascript" language support with the {@link org.gradle.language.javascript.JavaScriptSourceSet}.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Incubating
+public class PlayJavaScriptPlugin extends RuleSource {
+    @LanguageType
+    void registerJavascript(LanguageTypeBuilder<JavaScriptSourceSet> builder) {
+        builder.setLanguageName("javaScript");
+        builder.defaultImplementation(DefaultJavaScriptSourceSet.class);
+    }
+
+    @Mutate
+    void createJavascriptSourceSets(CollectionBuilder<PlayApplicationSpec> components) {
+        components.beforeEach(new Action<PlayApplicationSpec>() {
+            @Override
+            public void execute(PlayApplicationSpec playComponent) {
+                // TODO - should have some way to lookup using internal type
+                JavaScriptSourceSet javaScriptSourceSet = ((ComponentSpecInternal) playComponent).getSources().create("javaScriptAssets", JavaScriptSourceSet.class);
+                javaScriptSourceSet.getSource().srcDir("app/assets");
+                javaScriptSourceSet.getSource().include("**/*.js");
+            }
+        });
+    }
+
+    @BinaryTasks
+    void createJavaScriptTasks(CollectionBuilder<Task> tasks, final PlayApplicationBinarySpec binary, ServiceRegistry serviceRegistry, @Path("buildDir") final File buildDir) {
+        ToolResolver toolResolver = serviceRegistry.get(ToolResolver.class);
+        ResolvedTool<Compiler<JavaScriptCompileSpec>> compilerTool = toolResolver.resolveCompiler(JavaScriptCompileSpec.class, binary.getTargetPlatform());
+        for (JavaScriptSourceSet javaScriptSourceSet : binary.getSource().withType(JavaScriptSourceSet.class)) {
+            if (((LanguageSourceSetInternal) javaScriptSourceSet).getMayHaveSources()) {
+                createJavaScriptMinifyTask(tasks, javaScriptSourceSet, binary, compilerTool, buildDir);
+            }
+        }
+
+        for (JavaScriptSourceSet javaScriptSourceSet : binary.getGeneratedJavaScript().values()) {
+            createJavaScriptMinifyTask(tasks, javaScriptSourceSet, binary, compilerTool, buildDir);
+        }
+    }
+
+    void createJavaScriptMinifyTask(CollectionBuilder<Task> tasks, final JavaScriptSourceSet javaScriptSourceSet, final PlayApplicationBinarySpec binary, final ResolvedTool<Compiler<JavaScriptCompileSpec>> compilerTool,  @Path("buildDir") final File buildDir) {
+        final String minifyTaskName = "minify" + capitalize(binary.getName()) + capitalize(javaScriptSourceSet.getName());
+        final File minifyOutputDirectory = new File(buildDir, String.format("%s/src/%s", binary.getName(), minifyTaskName));
+        tasks.create(minifyTaskName, JavaScriptMinify.class, new Action<JavaScriptMinify>() {
+            @Override
+            public void execute(JavaScriptMinify javaScriptMinify) {
+                javaScriptMinify.setSource(javaScriptSourceSet.getSource());
+                javaScriptMinify.setDestinationDir(minifyOutputDirectory);
+                javaScriptMinify.setCompilerTool(compilerTool);
+
+                binary.getAssets().builtBy(javaScriptMinify);
+                binary.getAssets().addAssetDir(minifyOutputDirectory);
+
+                javaScriptMinify.dependsOn(javaScriptSourceSet.getBuildDependencies());
+            }
+        });
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPlugin.java
new file mode 100644
index 0000000..de742da
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPlugin.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+
+/**
+ * Plugin for Play Framework component support. Registers the {@link org.gradle.play.PlayApplicationSpec} component type for the {@link org.gradle.platform.base.ComponentSpecContainer}.
+ */
+ at Incubating
+public class PlayPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPluginManager().apply(PlayApplicationPlugin.class);
+        project.getPluginManager().apply(PlayTestPlugin.class);
+        project.getPluginManager().apply(PlayJavaScriptPlugin.class);
+        project.getPluginManager().apply(PlayDistributionPlugin.class);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPluginConfigurations.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPluginConfigurations.java
new file mode 100644
index 0000000..a72fa04
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayPluginConfigurations.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.file.FileCollection;
+
+/**
+ * Conventional locations and names for play plugins.
+ */
+public class PlayPluginConfigurations {
+    public static final String PLATFORM_CONFIGURATION = "playPlatform";
+    public static final String COMPILE_CONFIGURATION = "play";
+    public static final String RUN_CONFIGURATION = "playRun";
+    public static final String TEST_COMPILE_CONFIGURATION = "playTest";
+
+    private final ConfigurationContainer configurations;
+    private final DependencyHandler dependencyHandler;
+
+    public PlayPluginConfigurations(ConfigurationContainer configurations, DependencyHandler dependencyHandler) {
+        this.configurations = configurations;
+        this.dependencyHandler = dependencyHandler;
+        Configuration playPlatform = configurations.create(PLATFORM_CONFIGURATION);
+
+        Configuration playCompile = configurations.create(COMPILE_CONFIGURATION);
+        playCompile.extendsFrom(playPlatform);
+
+        Configuration playRun = configurations.create(RUN_CONFIGURATION);
+        playRun.extendsFrom(playCompile);
+
+        Configuration playTestCompile = configurations.create(TEST_COMPILE_CONFIGURATION);
+        playTestCompile.extendsFrom(playCompile);
+
+        configurations.maybeCreate(Dependency.DEFAULT_CONFIGURATION).extendsFrom(playCompile);
+    }
+
+    public PlayConfiguration getPlayPlatform() {
+        return new PlayConfiguration(PLATFORM_CONFIGURATION);
+    }
+
+    public PlayConfiguration getPlay() {
+        return new PlayConfiguration(COMPILE_CONFIGURATION);
+    }
+
+    public PlayConfiguration getPlayRun() {
+        return new PlayConfiguration(RUN_CONFIGURATION);
+    }
+
+    public PlayConfiguration getPlayTest() {
+        return new PlayConfiguration(TEST_COMPILE_CONFIGURATION);
+    }
+
+    /**
+     * Wrapper around a Configuration instance used by the PlayApplicationPlugin.
+     */
+    class PlayConfiguration {
+        private final String name;
+
+        PlayConfiguration(String name) {
+            this.name = name;
+        }
+
+        FileCollection getFileCollection() {
+            return configurations.getByName(name);
+        }
+        
+        void addDependency(String notation) {
+            dependencyHandler.add(name, notation);
+        }
+
+        void addArtifact(PublishArtifact artifact) {
+            configurations.getByName(name).getArtifacts().add(artifact);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayTestPlugin.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayTestPlugin.java
new file mode 100644
index 0000000..c4897b4
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/PlayTestPlugin.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.tasks.scala.IncrementalCompileOptions;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.language.scala.tasks.PlatformScalaCompile;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.play.PlayApplicationBinarySpec;
+import org.gradle.play.internal.PlayApplicationBinarySpecInternal;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * Plugin for executing tests from a Play Framework application.
+ */
+ at SuppressWarnings("UnusedDeclaration")
+ at Incubating
+public class PlayTestPlugin extends RuleSource {
+    @Mutate
+    void createTestTasks(CollectionBuilder<Task> tasks, BinaryContainer binaryContainer, final PlayPluginConfigurations configurations,
+                         final FileResolver fileResolver, final ProjectIdentifier projectIdentifier, @Path("buildDir") final File buildDir) {
+        for (final PlayApplicationBinarySpecInternal binary : binaryContainer.withType(PlayApplicationBinarySpecInternal.class)) {
+            final FileCollection testCompileClasspath = getTestCompileClasspath(binary, configurations);
+
+            final String testCompileTaskName = String.format("compile%sTests", StringUtils.capitalize(binary.getName()));
+            // TODO:DAZ Model a test suite
+            final File testSourceDir = fileResolver.resolve("test");
+            final File testClassesDir = new File(buildDir, String.format("%s/testClasses", binary.getName()));
+            tasks.create(testCompileTaskName, PlatformScalaCompile.class, new Action<PlatformScalaCompile>() {
+                public void execute(PlatformScalaCompile scalaCompile) {
+                    scalaCompile.setClasspath(testCompileClasspath);
+
+                    scalaCompile.dependsOn(binary.getBuildTask());
+                    scalaCompile.setPlatform(binary.getTargetPlatform().getScalaPlatform());
+                    scalaCompile.setDestinationDir(testClassesDir);
+                    scalaCompile.setSource(testSourceDir);
+                    String targetCompatibility = binary.getTargetPlatform().getJavaPlatform().getTargetCompatibility().getMajorVersion();
+                    scalaCompile.setSourceCompatibility(targetCompatibility);
+                    scalaCompile.setTargetCompatibility(targetCompatibility);
+
+                    IncrementalCompileOptions incrementalOptions = scalaCompile.getScalaCompileOptions().getIncrementalOptions();
+                    incrementalOptions.setAnalysisFile(new File(buildDir, String.format("tmp/scala/compilerAnalysis/%s.analysis", testCompileTaskName)));
+
+                    binary.getTasks().add(scalaCompile);
+                }
+            });
+
+            final String testTaskName = String.format("test%s", StringUtils.capitalize(binary.getName()));
+            final File binaryBuildDir = new File(buildDir, binary.getName());
+            tasks.create(testTaskName, Test.class, new Action<Test>() {
+                public void execute(Test test) {
+                    test.setClasspath(getRuntimeClasspath(testClassesDir, testCompileClasspath));
+
+                    test.setTestClassesDir(testClassesDir);
+                    test.setBinResultsDir(new File(binaryBuildDir, String.format("results/%s/bin", testTaskName)));
+                    test.getReports().getJunitXml().setDestination(new File(binaryBuildDir, "reports/test/xml"));
+                    test.getReports().getHtml().setDestination(new File(binaryBuildDir, "reports/test"));
+                    test.dependsOn(testCompileTaskName);
+                    test.setTestSrcDirs(Arrays.asList(testSourceDir));
+                    test.setWorkingDir(projectIdentifier.getProjectDir());
+                }
+            });
+            binary.getTasks().add(tasks.get(testTaskName));
+        }
+    }
+
+    private FileCollection getTestCompileClasspath(PlayApplicationBinarySpec binary, PlayPluginConfigurations configurations) {
+        return new SimpleFileCollection(binary.getJarFile()).plus(configurations.getPlayTest().getFileCollection());
+    }
+
+    private FileCollection getRuntimeClasspath(File testClassesDir, FileCollection testCompileClasspath) {
+        return new SimpleFileCollection(testClassesDir).plus(testCompileClasspath);
+    }
+
+    @Mutate
+    void attachTestSuitesToCheckTask(CollectionBuilder<Task> tasks, final BinaryContainer binaries) {
+        // TODO - binaries aren't an input to this rule, they're an input to the action
+        tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
+            @Override
+            public void execute(Task checkTask) {
+                // TODO Need a better mechanism to wire tasks into lifecycle
+                for (PlayApplicationBinarySpec binary : binaries.withType(PlayApplicationBinarySpec.class)) {
+                    checkTask.dependsOn(binary.getTasks().withType(Test.class));
+                }
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/plugins/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/package-info.java
new file mode 100644
index 0000000..dc1f4a1
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/plugins/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 that add support for the Play framework.
+ */
+ at Incubating package org.gradle.play.plugins;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/JavaScriptMinify.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/JavaScriptMinify.java
new file mode 100644
index 0000000..658bb5f
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/JavaScriptMinify.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.file.FileOperations;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SourceTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.play.internal.javascript.DefaultJavaScriptCompileSpec;
+import org.gradle.play.internal.javascript.JavaScriptCompileSpec;
+import org.gradle.play.toolchain.PlayToolChain;
+import org.gradle.language.base.internal.compile.Compiler;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.List;
+
+/**
+ * Task to minify JavaScript assets.
+ */
+ at Incubating
+public class JavaScriptMinify extends SourceTask {
+    private File destinationDir;
+    private ResolvedTool<Compiler<JavaScriptCompileSpec>> compilerTool;
+    private BaseForkOptions forkOptions;
+
+    public JavaScriptMinify() {
+        this.include("**/*.js");
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the tool chain that will be used to compile the javascript source.
+     *
+     * @return The tool chain.
+     */
+    @Incubating
+    @Inject
+    public PlayToolChain getToolChain() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the output directory that processed javascript is written to.
+     *
+     * @return The output directory.
+     */
+    @OutputDirectory
+    public File getDestinationDir() {
+        return destinationDir;
+    }
+
+    /**
+     * Sets the output directory where processed javascript should be written.
+     *
+     * @param destinationDir The output directory.
+     */
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    public void setCompilerTool(ResolvedTool<Compiler<JavaScriptCompileSpec>> compilerTool) {
+        this.compilerTool = compilerTool;
+    }
+
+    /**
+     * The fork options to be applied to the javascript compiler.
+     *
+     * @return The fork options for the javascript compiler.
+     */
+    public BaseForkOptions getForkOptions() {
+        if (forkOptions == null) {
+            forkOptions = new BaseForkOptions();
+        }
+        return forkOptions;
+    }
+
+    @TaskAction
+    void compileJavaScriptSources() {
+        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
+        cleaner.setDestinationDir(getDestinationDir());
+        cleaner.execute();
+
+        MinifyFileVisitor visitor = new MinifyFileVisitor();
+        getSource().visit(visitor);
+
+        JavaScriptCompileSpec spec = new DefaultJavaScriptCompileSpec(visitor.relativeFiles, getDestinationDir(), getForkOptions());
+        compilerTool.get().execute(spec);
+    }
+
+    /**
+     * Copies each file in the source set to the output directory and gathers relative files for compilation
+     */
+    class MinifyFileVisitor implements FileVisitor {
+        List<RelativeFile> relativeFiles = Lists.newArrayList();
+
+        @Override
+        public void visitDir(FileVisitDetails dirDetails) {
+            new File(destinationDir, dirDetails.getRelativePath().getPathString()).mkdirs();
+        }
+
+        @Override
+        public void visitFile(final FileVisitDetails fileDetails) {
+            final File outputFileDir = new File(destinationDir, fileDetails.getRelativePath().getParent().getPathString());
+
+            // Copy the raw form
+            FileOperations fileOperations = (ProjectInternal) getProject();
+            fileOperations.copy(new Action<CopySpec>() {
+                @Override
+                public void execute(CopySpec copySpec) {
+                    copySpec.from(fileDetails.getFile()).into(outputFileDir);
+                }
+            });
+
+            // Capture the relative file
+            relativeFiles.add(new RelativeFile(fileDetails.getFile(), fileDetails.getRelativePath()));
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayCoffeeScriptCompile.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayCoffeeScriptCompile.java
new file mode 100644
index 0000000..3c4ab82
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayCoffeeScriptCompile.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
+import org.gradle.plugins.javascript.coffeescript.CoffeeScriptCompile;
+
+/**
+ * Task for compiling CoffeeScript sources
+ */
+ at Incubating
+public class PlayCoffeeScriptCompile extends CoffeeScriptCompile {
+    public void setCoffeeScriptJsNotation(String notation) {
+        super.setCoffeeScriptJs(getDetachedConfiguration(notation));
+    }
+
+    @Override
+    public void setCoffeeScriptJs(Object coffeeScriptJs) {
+        super.setCoffeeScriptJs(coffeeScriptJs);
+    }
+
+    public void setRhinoClasspathNotation(String notation) {
+        setRhinoClasspath(getDetachedConfiguration(notation));
+    }
+
+    private Configuration getDetachedConfiguration(String notation) {
+        Dependency dependency = getProject().getDependencies().create(notation);
+        return getProject().getConfigurations().detachedConfiguration(dependency);
+    }
+
+    @Override
+    public void doCompile() {
+        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
+        cleaner.setDestinationDir(getDestinationDir());
+        cleaner.execute();
+        super.doCompile();
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayRun.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayRun.java
new file mode 100644
index 0000000..8314eeb
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/PlayRun.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.internal.file.collections.SimpleFileCollection;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.logging.ProgressLogger;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.play.internal.run.DefaultPlayRunSpec;
+import org.gradle.play.internal.run.PlayApplicationRunner;
+import org.gradle.play.internal.run.PlayApplicationRunnerToken;
+import org.gradle.play.internal.run.PlayRunSpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A Task to run a play application.
+ */
+ at Incubating
+public class PlayRun extends ConventionTask {
+    private static Logger logger = LoggerFactory.getLogger(PlayRun.class);
+
+    private int httpPort;
+
+    @InputFile
+    private File applicationJar;
+
+    @InputFile
+    private File assetsJar;
+
+    @InputFiles
+    private FileCollection runtimeClasspath;
+
+    private BaseForkOptions forkOptions;
+
+    private PlayApplicationRunnerToken runnerToken;
+    private ResolvedTool<PlayApplicationRunner> playApplicationRunnerTool;
+
+    /**
+     * fork options for the running a play application.
+     */
+    public BaseForkOptions getForkOptions() {
+        if (forkOptions == null) {
+            forkOptions = new BaseForkOptions();
+        }
+        return forkOptions;
+    }
+
+    @TaskAction
+    public void run() {
+        ProgressLoggerFactory progressLoggerFactory = getServices().get(ProgressLoggerFactory.class);
+        ProgressLogger progressLogger = progressLoggerFactory.newOperation(PlayRun.class)
+                .start("Start Play server", "Starting Play");
+
+        int httpPort = getHttpPort();
+        FileCollection applicationJars = new SimpleFileCollection(applicationJar, assetsJar);
+        applicationJars = applicationJars.plus(runtimeClasspath);
+        PlayRunSpec spec = new DefaultPlayRunSpec(applicationJars, getProject().getProjectDir(), getForkOptions(), httpPort);
+
+        try {
+            runnerToken = playApplicationRunnerTool.get().start(spec);
+            progressLogger.completed();
+            progressLogger = progressLoggerFactory.newOperation(PlayRun.class)
+                    .start(String.format("Run Play App at http://localhost:%d/", httpPort),
+                            String.format("Running at http://localhost:%d/ (stop with ctrl+d)", httpPort));
+            waitForCtrlD();
+            runnerToken.stop();
+        } finally {
+            progressLogger.completed();
+        }
+    }
+
+    private void waitForCtrlD() {
+        while (true) {
+            try {
+                int c = System.in.read();
+                if (c == -1 || c == 4) {
+                    // STOP on Ctrl-D or EOF.
+                    logger.info("received end of stream (ctrl+d)");
+                    return;
+                }
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+
+    public void setHttpPort(int httpPort) {
+        this.httpPort = httpPort;
+    }
+
+    public void setApplicationJar(File applicationJar) {
+        this.applicationJar = applicationJar;
+    }
+
+    public void setAssetsJar(File assetsJar) {
+        this.assetsJar = assetsJar;
+    }
+
+    public void setRuntimeClasspath(FileCollection runtimeClasspath) {
+        this.runtimeClasspath = runtimeClasspath;
+    }
+
+    public void setPlayApplicationRunnerTool(ResolvedTool<PlayApplicationRunner> playApplicationRunnerTool) {
+        this.playApplicationRunnerTool = playApplicationRunnerTool;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/RoutesCompile.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/RoutesCompile.java
new file mode 100644
index 0000000..ba24632
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/RoutesCompile.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SourceTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.play.internal.CleaningPlayToolCompiler;
+import org.gradle.play.internal.routes.DefaultRoutesCompileSpec;
+import org.gradle.play.internal.routes.RoutesCompileSpec;
+import org.gradle.play.toolchain.PlayToolChain;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Task for compiling routes templates
+ */
+ at Incubating
+public class RoutesCompile extends SourceTask {
+
+    /**
+     * Target directory for the compiled route files.
+     */
+    private File outputDirectory;
+
+    /**
+     * Additional imports used for by generated files.
+     */
+    private List<String> additionalImports = new ArrayList<String>();
+
+    private boolean javaProject;
+    private boolean namespaceReverseRouter;
+    private BaseForkOptions forkOptions;
+    private ResolvedTool<Compiler<RoutesCompileSpec>> compilerTool;
+
+    /**
+     * Returns the directory to generate the parser source files into.
+     *
+     * @return The output directory.
+     */
+    @OutputDirectory
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    /**
+     * Specifies the directory to generate the parser source files into.
+     *
+     * @param outputDirectory The output directory. Must not be null.
+     */
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * Specifies the additional imports of the Play Routes compiler.
+     */
+    public List<String> getAdditionalImports() {
+        return additionalImports;
+    }
+
+    /**
+     * Returns the additional imports of the Play Routes compiler.
+     *
+     * @return The additional imports.
+     */
+    public void setAdditionalImports(List<String> additionalImports) {
+        this.additionalImports.addAll(additionalImports);
+    }
+
+
+    @TaskAction
+    void compile() {
+        RoutesCompileSpec spec = new DefaultRoutesCompileSpec(getSource().getFiles(), getOutputDirectory(), getForkOptions(), isJavaProject());
+        new CleaningPlayToolCompiler<RoutesCompileSpec>(compilerTool.get(), getOutputs()).execute(spec);
+    }
+
+    public void setCompilerTool(ResolvedTool<Compiler<RoutesCompileSpec>> compilerTool) {
+        this.compilerTool = compilerTool;
+    }
+
+    public boolean isJavaProject() {
+        return javaProject;
+    }
+
+    /**
+     * Returns the tool chain that will be used to compile the routes source.
+     *
+     * @return The tool chain.
+     */
+    @Incubating
+    @Inject
+    public PlayToolChain getToolChain() {
+        throw new UnsupportedOperationException();
+    }
+
+    public BaseForkOptions getForkOptions() {
+        if (forkOptions == null) {
+            forkOptions = new BaseForkOptions();
+        }
+        return forkOptions;
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/TwirlCompile.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/TwirlCompile.java
new file mode 100644
index 0000000..2250cf1
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/TwirlCompile.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks;
+
+import com.google.common.collect.Lists;
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.file.FileVisitor;
+import org.gradle.api.internal.file.RelativeFile;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SourceTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.BaseForkOptions;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.platform.base.internal.toolchain.ResolvedTool;
+import org.gradle.play.internal.CleaningPlayToolCompiler;
+import org.gradle.play.internal.twirl.DefaultTwirlCompileSpec;
+import org.gradle.play.internal.twirl.TwirlCompileSpec;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Task for compiling twirl templates
+ */
+ at Incubating
+public class TwirlCompile extends SourceTask {
+
+    /**
+     * Target directory for the compiled template files.
+     */
+    private File outputDirectory;
+    private BaseForkOptions forkOptions;
+    private ResolvedTool<Compiler<TwirlCompileSpec>> compilerTool;
+    private TwirlStaleOutputCleaner cleaner;
+    private Object dependencyNotation;
+
+    /**
+     * fork options for the twirl compiler.
+     */
+    public BaseForkOptions getForkOptions() {
+        if (forkOptions == null) {
+            forkOptions = new BaseForkOptions();
+        }
+        return forkOptions;
+    }
+
+    public void setCompilerTool(ResolvedTool<Compiler<TwirlCompileSpec>> compilerTool) {
+        this.compilerTool = compilerTool;
+    }
+
+    /**
+     * Returns the directory to generate the parser source files into.
+     *
+     * @return The output directory.
+     */
+    @OutputDirectory
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    @Input
+    public Object getDependencyNotation() {
+        return dependencyNotation;
+    }
+
+    public void setDependencyNotation(Object dependencyNotation) {
+        this.dependencyNotation = dependencyNotation;
+    }
+
+    /**
+     * Specifies the directory to generate the parser source files into.
+     *
+     * @param outputDirectory The output directory. Must not be null.
+     */
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    @TaskAction
+    void compile(IncrementalTaskInputs inputs) {
+        RelativeFileCollector relativeFileCollector = new RelativeFileCollector();
+        getSource().visit(relativeFileCollector);
+        TwirlCompileSpec spec = new DefaultTwirlCompileSpec(relativeFileCollector.relativeFiles, getOutputDirectory(), getForkOptions(), useJavaDefaults());
+        if (!inputs.isIncremental()) {
+            new CleaningPlayToolCompiler<TwirlCompileSpec>(compilerTool.get(), getOutputs()).execute(spec);
+        } else {
+            final Set<File> sourcesToCompile = new HashSet<File>();
+            inputs.outOfDate(new Action<InputFileDetails>() {
+                public void execute(InputFileDetails inputFileDetails) {
+                    sourcesToCompile.add(inputFileDetails.getFile());
+                }
+            });
+
+            final Set<File> staleOutputFiles = new HashSet<File>();
+            inputs.removed(new Action<InputFileDetails>() {
+                public void execute(InputFileDetails inputFileDetails) {
+                    staleOutputFiles.add(inputFileDetails.getFile());
+                }
+            });
+            if (cleaner == null) {
+                cleaner = new TwirlStaleOutputCleaner(getOutputDirectory());
+            }
+            cleaner.execute(staleOutputFiles);
+            compilerTool.get().execute(spec);
+        }
+    }
+
+    private boolean useJavaDefaults() {
+        return false; //TODO: add this as a configurable parameter
+    }
+
+    void setCleaner(TwirlStaleOutputCleaner cleaner) {
+        this.cleaner = cleaner;
+    }
+
+    private static class TwirlStaleOutputCleaner {
+        private final File destinationDir;
+
+        public TwirlStaleOutputCleaner(File destinationDir) {
+            this.destinationDir = destinationDir;
+        }
+
+        public void execute(Set<File> staleSources) {
+            for (File removedInputFile : staleSources) {
+                File staleOuputFile = calculateOutputFile(removedInputFile);
+                staleOuputFile.delete();
+            }
+        }
+
+        File calculateOutputFile(File inputFile) {
+            String inputFileName = inputFile.getName();
+            String[] splits = inputFileName.split("\\.");
+            String relativeOutputFilePath = String.format("views/%s/%s.template.scala", splits[2], splits[0]); //TODO: use Twirl library instead?
+            return new File(destinationDir, relativeOutputFilePath);
+        }
+    }
+
+    private static class RelativeFileCollector implements FileVisitor {
+        List<RelativeFile> relativeFiles = Lists.newArrayList();
+
+        @Override
+        public void visitDir(FileVisitDetails dirDetails) {
+        }
+
+        @Override
+        public void visitFile(FileVisitDetails fileDetails) {
+            relativeFiles.add(new RelativeFile(fileDetails.getFile(), fileDetails.getRelativePath()));
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/tasks/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/package-info.java
new file mode 100644
index 0000000..ee0e14d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Task classes used for the Play Framework support in Gradle.
+ */
+package org.gradle.play.tasks;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/PlayToolChain.java b/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/PlayToolChain.java
new file mode 100644
index 0000000..1a1abc1
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/PlayToolChain.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.toolchain;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+import org.gradle.platform.base.ToolChain;
+
+/**
+ * A set of tools for building applications using the Play Framework.
+ */
+ at Incubating
+ at HasInternalProtocol
+public interface PlayToolChain extends ToolChain {
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/package-info.java
new file mode 100644
index 0000000..01dc125
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/play/toolchain/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Defines tools that can build play applications.
+ */
+package org.gradle.play.toolchain;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaCodecMapper.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaCodecMapper.java
new file mode 100644
index 0000000..a5e0cdd
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaCodecMapper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.scala.internal.reflect;
+
+import java.io.Serializable;
+
+public class ScalaCodecMapper implements Serializable {
+    public static String getClassName() {
+        return "scala.io.Codec";
+    }
+
+    public static Object create(ClassLoader cl, String codec) {
+        ScalaMethod method = ScalaReflectionUtil.scalaMethod(cl, getClassName(), "apply", String.class);
+        return method.invoke(codec);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaListBuffer.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaListBuffer.java
new file mode 100644
index 0000000..2d2b5bb
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaListBuffer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.scala.internal.reflect;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class ScalaListBuffer {
+    public static <T> Object fromList(ClassLoader cl, List<T> list) {
+        try {
+            Class<?> bufferClass = cl.loadClass("scala.collection.mutable.ListBuffer");
+            Object buffer = bufferClass.newInstance();
+            Method bufferPlusEq = bufferClass.getMethod("$plus$eq", Object.class);
+
+            for (T elem : list) {
+                bufferPlusEq.invoke(buffer, elem);
+            }
+            return buffer;
+        } catch (Exception exception) {
+            throw new RuntimeException(exception);
+        }
+
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaMethod.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaMethod.java
new file mode 100644
index 0000000..46d8b2d
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaMethod.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.scala.internal.reflect;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.UncheckedException;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class ScalaMethod {
+    private final String description;
+    private final Method method;
+    private final Object instance;
+
+    public ScalaMethod(ClassLoader classLoader, String className, String methodName, Class<?>... typeParameters) {
+        description = String.format("%s.%s()", className, methodName);
+        Class<?> baseClass = getClass(classLoader, className);
+        final Field scalaObject = getModule(baseClass);
+        instance = getInstance(scalaObject);
+        method = getMethod(scalaObject.getType(), methodName, typeParameters);
+    }
+
+    private Method getMethod(Class<?> type, String methodName, Class<?>[] typeParameters) {
+        try {
+            return type.getMethod(methodName, typeParameters);
+        } catch (NoSuchMethodException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private Object getInstance(Field scalaObject) {
+        try {
+            return scalaObject.get(null);
+        } catch (IllegalAccessException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private Class<?> getClass(ClassLoader classLoader, String typeName) {
+        try {
+            return classLoader.loadClass(typeName + "$");
+        } catch (ClassNotFoundException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private Field getModule(Class<?> baseClass) {
+        try {
+            return baseClass.getField("MODULE$");
+        } catch (NoSuchFieldException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public Object invoke(Object... args) {
+        try {
+            return method.invoke(instance, args);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.throwAsUncheckedException(e.getCause());
+        } catch (Exception e) {
+            throw new GradleException(String.format("Could not invoke Scala method %s.", description), e);
+        }
+    }
+
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaOptionInvocationWrapper.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaOptionInvocationWrapper.java
new file mode 100644
index 0000000..fccd0b0
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaOptionInvocationWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.scala.internal.reflect;
+
+
+import org.gradle.internal.Cast;
+
+import java.lang.reflect.Method;
+
+public class ScalaOptionInvocationWrapper<T> {
+    private final Object obj;
+
+
+    public ScalaOptionInvocationWrapper(Object obj) {
+        this.obj = obj;
+    }
+
+    public boolean isDefined() {
+        try {
+            Method resultIsDefined = obj.getClass().getMethod("isDefined");
+            return (Boolean) resultIsDefined.invoke(obj);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public T get() {
+        try {
+            return Cast.uncheckedCast(obj.getClass().getMethod("get").invoke(obj));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaReflectionUtil.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaReflectionUtil.java
new file mode 100644
index 0000000..0755caf
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/ScalaReflectionUtil.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.scala.internal.reflect;
+
+public class ScalaReflectionUtil {
+    public static ScalaMethod scalaMethod(ClassLoader classLoader, String typeName, String methodName, Class<?>... typeParameters) {
+        return new ScalaMethod(classLoader, typeName, methodName, typeParameters);
+    }
+}
diff --git a/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/package-info.java b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/package-info.java
new file mode 100644
index 0000000..2b29575
--- /dev/null
+++ b/subprojects/platform-play/src/main/java/org/gradle/scala/internal/reflect/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Internal helper methods and classes to handle Scala through reflection
+ */
+ at Incubating
+package org.gradle.scala.internal.reflect;
+
+import org.gradle.api.Incubating;
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-application.properties b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-application.properties
new file mode 100644
index 0000000..cb32c4c
--- /dev/null
+++ b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-application.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.play.plugins.PlayApplicationPlugin
diff --git a/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-coffeescript.properties b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-coffeescript.properties
new file mode 100644
index 0000000..6c07c25
--- /dev/null
+++ b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-coffeescript.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.play.plugins.PlayCoffeeScriptPlugin
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-javascript.properties b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-javascript.properties
new file mode 100644
index 0000000..29733cc
--- /dev/null
+++ b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play-javascript.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.play.plugins.PlayJavaScriptPlugin
\ No newline at end of file
diff --git a/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play.properties b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play.properties
new file mode 100644
index 0000000..f426d50
--- /dev/null
+++ b/subprojects/platform-play/src/main/resources/META-INF/gradle-plugins/org.gradle.play.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.play.plugins.PlayPlugin
diff --git a/subprojects/platform-play/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/platform-play/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..0451ac7
--- /dev/null
+++ b/subprojects/platform-play/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.play.internal.toolchain.PlayToolChainServiceRegistry
\ No newline at end of file
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSetTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSetTest.groovy
new file mode 100644
index 0000000..27e5780
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/language/coffeescript/internal/DefaultCoffeeScriptSourceSetTest.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.language.coffeescript.internal
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.sources.BaseLanguageSourceSet
+import spock.lang.Specification
+
+class DefaultCoffeeScriptSourceSetTest extends Specification {
+    def "has useful String representation"() {
+        def sourceSet = BaseLanguageSourceSet.create(DefaultCoffeeScriptSourceSet, "coffeeX", "playX", Stub(FileResolver), DirectInstantiator.INSTANCE)
+
+        expect:
+        sourceSet.displayName == "CoffeeScript source 'playX:coffeeX'"
+        sourceSet.toString() == "CoffeeScript source 'playX:coffeeX'"
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSetTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSetTest.groovy
new file mode 100644
index 0000000..a162e39
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/org/gradle/language/javascript/internal/DefaultJavaScriptSourceSetTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.org.gradle.language.javascript.internal
+
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.language.base.sources.BaseLanguageSourceSet
+import org.gradle.language.javascript.internal.DefaultJavaScriptSourceSet
+import spock.lang.Specification
+
+class DefaultJavaScriptSourceSetTest extends Specification {
+    def "has useful String representation"() {
+        def sourceSet = BaseLanguageSourceSet.create(DefaultJavaScriptSourceSet, "javascriptX", "playX", Stub(FileResolver), DirectInstantiator.INSTANCE)
+
+        expect:
+        sourceSet.displayName == "JavaScript source 'playX:javascriptX'"
+        sourceSet.toString() == "JavaScript source 'playX:javascriptX'"
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayApplicationBinarySpecTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayApplicationBinarySpecTest.groovy
new file mode 100644
index 0000000..037f4c9
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayApplicationBinarySpecTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.platform.base.binary.BaseBinarySpec
+import org.gradle.platform.base.internal.BinaryBuildAbility
+import org.gradle.platform.base.internal.toolchain.ToolResolver
+import org.gradle.platform.base.internal.toolchain.ToolSearchResult
+import org.gradle.util.TreeVisitor
+import spock.lang.Specification
+
+class DefaultPlayApplicationBinarySpecTest extends Specification {
+    PlayApplicationBinarySpecInternal playBinary = BaseBinarySpec.create(DefaultPlayApplicationBinarySpec.class, "test", DirectInstantiator.INSTANCE, Stub(ITaskFactory))
+
+    def "sets binary build ability for unavailable toolchain" () {
+        ToolSearchResult result = Mock(ToolSearchResult) {
+            isAvailable() >> false
+        }
+        ToolResolver toolResolver = Mock(ToolResolver) {
+            checkToolAvailability(_) >> result
+        }
+        playBinary.setToolResolver(toolResolver)
+
+        when:
+        BinaryBuildAbility buildAbility = playBinary.getBuildAbility()
+
+        then:
+        ! buildAbility.buildable
+
+        when:
+        buildAbility.explain(Stub(TreeVisitor))
+
+        then:
+        1 * result.explain(_)
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayToolChainTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayToolChainTest.groovy
new file mode 100644
index 0000000..8f6df93
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/DefaultPlayToolChainTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal
+
+import org.gradle.api.GradleException
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.ResolveException
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager
+import org.gradle.internal.text.TreeFormatter
+import org.gradle.language.scala.ScalaPlatform
+import org.gradle.play.internal.toolchain.DefaultPlayToolChain
+import org.gradle.play.internal.twirl.TwirlCompileSpec
+import org.gradle.play.platform.PlayPlatform
+import org.gradle.process.internal.WorkerProcessBuilder
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultPlayToolChainTest extends Specification {
+    FileResolver fileResolver = Mock()
+    CompilerDaemonManager compilerDaemonManager = Mock()
+    ConfigurationContainer configurationContainer = Mock()
+    DependencyHandler dependencyHandler = Mock()
+    PlayPlatform playPlatform = Stub(PlayPlatform)
+    org.gradle.internal.Factory<WorkerProcessBuilder>  workerProcessBuilderFactory = Mock()
+    def toolChain = new DefaultPlayToolChain(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory)
+
+    def setup() {
+        playPlatform.playVersion >> "2.3.7"
+        playPlatform.scalaPlatform >> Stub(ScalaPlatform) {
+            getScalaCompatibilityVersion() >> "2.10"
+        }
+    }
+
+    def "provides meaningful name"() {
+        expect:
+        toolChain.getName() == "PlayToolchain"
+    }
+
+    def "provides meaningful displayname"() {
+        expect:
+        toolChain.getDisplayName() == "Default Play Toolchain"
+    }
+
+    def "can select toolprovider when dependencies are available"() {
+        given:
+        dependencyAvailable("twirl-compiler_2.10")
+        dependencyAvailable("routes-compiler_2.10")
+        dependencyAvailable("closure-compiler")
+
+        when:
+        def toolprovider = toolChain.select(playPlatform)
+
+        then:
+        toolprovider.isAvailable()
+    }
+
+    @Unroll
+    def "cannot select toolprovider when #failedDependency is not available" () {
+        given:
+        dependencyAvailableIfNotFailed("twirl-compiler_2.10", failedDependency)
+        dependencyAvailableIfNotFailed("routes-compiler_2.10", failedDependency)
+        dependencyAvailableIfNotFailed("closure-compiler", failedDependency)
+
+        when:
+        def toolprovider = toolChain.select(playPlatform)
+
+        then:
+        !toolprovider.isAvailable()
+
+        and:
+        TreeFormatter formatter = new TreeFormatter()
+        toolprovider.explain(formatter)
+        formatter.toString() == "Cannot provide Play tool provider: Cannot resolve '${failedDependency}'."
+
+        when:
+        toolprovider.get(String.class)
+
+        then:
+        def e1 = thrown(GradleException)
+        e1.message == "Cannot provide Play tool provider: Cannot resolve '${failedDependency}'."
+
+        when:
+        toolprovider.newCompiler(TwirlCompileSpec.class)
+
+        then:
+        def e2 = thrown(GradleException)
+        e2.message == "Cannot provide Play tool provider: Cannot resolve '${failedDependency}'."
+
+        where:
+        failedDependency       | _
+        "twirl-compiler_2.10"  | _
+        "routes-compiler_2.10" | _
+        "closure-compiler"     | _
+    }
+
+    private void dependencyAvailableIfNotFailed(String dependency, String failedDepenency) {
+        if (dependency == failedDepenency) {
+            dependencyNotAvailable(dependency)
+        } else {
+            dependencyAvailable(dependency)
+        }
+    }
+
+    private void dependencyAvailable(String dependency) {
+        Dependency someDependency = Mock()
+        Configuration someConfiguration = Mock()
+        (_..1) * dependencyHandler.create({ it =~ dependency }) >> someDependency
+        (_..1) * configurationContainer.detachedConfiguration(someDependency) >> someConfiguration
+        (_..1) * someConfiguration.resolve() >> new HashSet<File>()
+    }
+
+    private void dependencyNotAvailable(String dependency) {
+        Dependency someDependency = Mock()
+        ResolveException resolveException = Mock()
+        Exception resolveExceptionCause = Mock()
+        Configuration someConfiguration = Mock()
+
+        _ * resolveException.cause >> resolveExceptionCause
+        _ * resolveExceptionCause.getMessage() >> "Cannot resolve '$dependency'."
+        1 * dependencyHandler.create({ it =~ dependency }) >> someDependency
+        1 * configurationContainer.detachedConfiguration(someDependency) >> someConfiguration
+        1 * someConfiguration.resolve() >> { throw resolveException }
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/PlayPlatformResolverTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/PlayPlatformResolverTest.groovy
new file mode 100644
index 0000000..a2f5b38
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/PlayPlatformResolverTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.JavaVersion
+import org.gradle.platform.base.internal.DefaultPlatformRequirement
+import org.gradle.play.platform.PlayPlatform
+import spock.lang.Specification
+
+class PlayPlatformResolverTest extends Specification {
+    def resolver = new PlayPlatformResolver()
+
+    def "provides correct platform type"() {
+        expect:
+        resolver.getType() == PlayPlatform.class
+    }
+
+    def "fails to resolve invalid play platform"() {
+        when:
+        resolve "java-1.6.0"
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message == "Not a valid Play platform: java-1.6.0."
+    }
+
+    def "fails to resolve unsupported play version"() {
+        when:
+        resolve requirement
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message == "Not a supported Play version: 2.1.0. This plugin is compatible with: [2.3.x, 2.2.x]."
+
+        where:
+        requirement << ["play-2.1.0", [play: '2.1.0']]
+    }
+
+    def "resolves platform for Play 2.2.x"() {
+        when:
+        def playPlatform = resolve(requirement)
+
+        then:
+        playPlatform.name == "play-2.2.3"
+        playPlatform.playVersion == "2.2.3"
+        playPlatform.javaPlatform.targetCompatibility == JavaVersion.current()
+        playPlatform.scalaPlatform.scalaVersion == "2.10.4"
+
+        where:
+        requirement << ["play-2.2.3", [play: '2.2.3']]
+    }
+
+    def "resolves platform for Play 2.3.x"() {
+        when:
+        def playPlatform = resolve(requirement)
+
+        then:
+        playPlatform.name == "play-2.3.4"
+        playPlatform.playVersion == "2.3.4"
+        playPlatform.javaPlatform.targetCompatibility == JavaVersion.current()
+        playPlatform.scalaPlatform.scalaVersion == "2.11.4"
+
+        where:
+        requirement << ["play-2.3.4", [play: '2.3.4']]
+    }
+
+    def "resolves platform with specified scala version"() {
+        when:
+        def playPlatform = resolve play: "2.3.1", scala: "2.10"
+
+        then:
+        playPlatform.name == "play-2.3.1-2.10"
+        playPlatform.playVersion == "2.3.1"
+        playPlatform.javaPlatform.targetCompatibility == JavaVersion.current()
+        playPlatform.scalaPlatform.scalaVersion == "2.10.4"
+    }
+
+    def "fails to resolve Play platform with incompatible Scala version"() {
+        when:
+        resolve play: "2.2.6", scala: "2.11"
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message == "Play versions 2.2.x are not compatible with Scala platform 2.11. Compatible Scala platforms are [2.10]."
+    }
+
+    def "fails to resolve Play platform with unsupported Scala version"() {
+        when:
+        resolve play: "2.2.6", scala: "2.9"
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message == "Not a supported Scala platform identifier 2.9. Supported values are: ['2.10', '2.11']."
+    }
+
+    def "fails to resolve Play platform with full Scala version"() {
+        when:
+        resolve play: "2.2.6", scala: "2.10.4"
+
+        then:
+        def e = thrown(InvalidUserDataException)
+        e.message == "Not a supported Scala platform identifier 2.10.4. Supported values are: ['2.10', '2.11']."
+    }
+
+    def "resolves platform with specified java version"() {
+        when:
+        def playPlatform = resolve play: "2.3.1", java: "1.6"
+
+        then:
+        playPlatform.name == "play-2.3.1_1.6"
+        playPlatform.playVersion == "2.3.1"
+        playPlatform.javaPlatform.targetCompatibility == JavaVersion.toVersion("1.6")
+        playPlatform.scalaPlatform.scalaVersion == "2.11.4"
+    }
+
+    private PlayPlatform resolve(String playPlatform) {
+        resolver.resolve(DefaultPlatformRequirement.create(playPlatform))
+    }
+
+    private PlayPlatform resolve(Map<String, String> platform) {
+        resolver.resolve(new PlayPlatformRequirement(platform['play'], platform['scala'], platform['java']))
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculatorTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculatorTest.groovy
new file mode 100644
index 0000000..ab5607e
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/javascript/JavaScriptCompileDestinationCalculatorTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.javascript
+
+import org.gradle.api.file.RelativePath
+import org.gradle.api.internal.file.RelativeFile
+import spock.lang.Specification
+
+class JavaScriptCompileDestinationCalculatorTest extends Specification {
+    File outputDir = new File("/path/to/output")
+    JavaScriptCompileDestinationCalculator calculator = new JavaScriptCompileDestinationCalculator(outputDir)
+
+    def "calculates correct destination for javascript file"() {
+        when:
+        File inputFile = new File("/some/input/javascript/${fileName}")
+        RelativeFile relativeInputFile = new RelativeFile(inputFile, new RelativePath(true, "javascript", fileName))
+
+        then:
+        calculator.transform(relativeInputFile) == new File("/path/to/output/javascript/${minFileName}")
+
+        where:
+        fileName      | minFileName
+        "file.js"     | "file.min.js"
+        "file.max.js" | "file.max.min.js"
+        ".js"         | ".min.js"
+        "no-ext"      | "no-ext.min"
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DaemonPlayCompilerTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DaemonPlayCompilerTest.groovy
new file mode 100644
index 0000000..2be79b7
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DaemonPlayCompilerTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory
+import org.gradle.api.tasks.compile.BaseForkOptions
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.play.internal.spec.PlayCompileSpec
+import spock.lang.Specification
+
+class DaemonPlayCompilerTest extends Specification {
+
+    def workingDirectory = Mock(File)
+    def delegate = Mock(Compiler)
+    def compilerDaemonFactory = Mock(CompilerDaemonFactory)
+    def spec = Mock(PlayCompileSpec)
+    def forkOptions = Mock(BaseForkOptions)
+
+    def setup(){
+        _ * spec.getForkOptions() >> forkOptions
+    }
+
+    def "passes compile classpath and packages to daemon options"() {
+        given:
+        def classpath = someClasspath()
+        def packages = ["foo", "bar"]
+        def compiler = new DaemonPlayCompiler(workingDirectory, delegate, compilerDaemonFactory, classpath, packages)
+        when:
+        def options = compiler.toDaemonOptions(spec);
+        then:
+        options.getClasspath() == classpath
+        options.getSharedPackages() == packages
+    }
+
+    def "applies fork settings to daemon options"(){
+        given:
+        def compiler = new DaemonPlayCompiler(workingDirectory, delegate, compilerDaemonFactory, someClasspath(), [])
+        when:
+        1 * forkOptions.getMemoryInitialSize() >> "256m"
+        1 * forkOptions.getMemoryMaximumSize() >> "512m"
+        then:
+        def options = compiler.toDaemonOptions(spec);
+        options.getMinHeapSize() == "256m"
+        options.getMaxHeapSize() == "512m"
+    }
+
+    def someClasspath() {
+        [Mock(File), Mock(File)]
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DefaultPlayToolProviderTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DefaultPlayToolProviderTest.groovy
new file mode 100644
index 0000000..b49a582
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/internal/toolchain/DefaultPlayToolProviderTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.internal.toolchain
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager
+import org.gradle.language.base.internal.compile.CompileSpec
+import org.gradle.language.scala.ScalaPlatform
+import org.gradle.play.internal.run.PlayApplicationRunner
+import org.gradle.play.internal.run.PlayRunAdapterV22X
+import org.gradle.play.internal.run.PlayRunAdapterV23X
+import org.gradle.play.internal.run.PlayRunSpec
+import org.gradle.play.platform.PlayPlatform
+import org.gradle.process.internal.WorkerProcessBuilder
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class DefaultPlayToolProviderTest extends Specification {
+    FileResolver fileResolver = Mock()
+    CompilerDaemonManager compilerDaemonManager = Mock()
+    ConfigurationContainer configurationContainer = Mock()
+    DependencyHandler dependencyHandler = Mock()
+    PlayPlatform playPlatform = Mock()
+    ScalaPlatform scalaPlatform= Mock()
+    org.gradle.internal.Factory<WorkerProcessBuilder> workerProcessBuilderFactory = Mock()
+    Set<File> twirlClasspath = Stub(Set)
+    Set<File> routesClasspath = Stub(Set)
+    Set<File> javascriptClasspath = Stub(Set)
+
+    DefaultPlayToolProvider playToolProvider
+    PlayRunSpec playRunSpec = Mock()
+
+    @Unroll
+    def "provides playRunner for play #playVersion"(){
+        setup:
+        _ * playPlatform.getPlayVersion() >> playVersion
+
+        when:
+        playToolProvider = new DefaultPlayToolProvider(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory, playPlatform, twirlClasspath, routesClasspath, javascriptClasspath)
+        def runner = playToolProvider.get(PlayApplicationRunner.class)
+
+        then:
+        runner != null
+        runner.adapter.class == adapter
+
+        and:
+        1 * fileResolver.resolve('.') >> new File(".")
+
+        where:
+        playVersion | adapter
+        "2.2.x"     | PlayRunAdapterV22X
+        "2.3.x"     | PlayRunAdapterV23X
+    }
+
+    def "cannot create tool provider for unsupported play versions"() {
+        when:
+        _ * playPlatform.getPlayVersion() >> playVersion
+        playToolProvider = new DefaultPlayToolProvider(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory, playPlatform, twirlClasspath, routesClasspath, javascriptClasspath)
+
+        then: "fails with meaningful error message"
+        def exception = thrown(InvalidUserDataException)
+        exception.message == "Not a supported Play version: ${playVersion}. This plugin is compatible with: [2.3.x, 2.2.x]."
+
+        and: "no dependencies resolved"
+        0 * dependencyHandler.create(_)
+        0 * configurationContainer.detachedConfiguration(_)
+
+        where:
+        playVersion << ["2.1.x", "2.4.x", "3.0.0"]
+    }
+
+    def "newCompiler provides decent error for unsupported CompileSpec"() {
+        setup:
+        _ * playPlatform.getPlayVersion() >> "2.3.7"
+        playToolProvider = new DefaultPlayToolProvider(fileResolver, compilerDaemonManager, configurationContainer, dependencyHandler, workerProcessBuilderFactory, playPlatform, twirlClasspath, routesClasspath, javascriptClasspath)
+
+        when:
+        playToolProvider.newCompiler(UnknownCompileSpec.class)
+
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "Cannot create Compiler for unsupported CompileSpec type 'UnknownCompileSpec'"
+    }
+
+}
+
+class UnknownCompileSpec implements CompileSpec {}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginTest.groovy
new file mode 100644
index 0000000..cbf34d3
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayCoffeeScriptPluginTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.api.Action
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.coffeescript.CoffeeScriptSourceSet
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.internal.ComponentSpecInternal
+import org.gradle.play.PlayApplicationSpec
+import spock.lang.Specification
+
+class PlayCoffeeScriptPluginTest extends Specification {
+    def "adds coffeescript source sets to play components" () {
+        def plugin = new PlayCoffeeScriptPlugin()
+        def components = Mock(CollectionBuilder)
+        def sources = Mock(FunctionalSourceSet)
+        def sourceSet = Mock(CoffeeScriptSourceSet)
+        def sourceDirSet = Mock(SourceDirectorySet)
+
+        when:
+        def playApp = Stub(PlayAppInternal) {
+            getName() >> "play"
+            getSources() >> sources
+        }
+        _ * components.beforeEach(_) >> { Action a -> a.execute(playApp) }
+
+        and:
+        plugin.createCoffeeScriptSourceSets(components)
+
+        then:
+        1 * sources.create("coffeeScriptAssets", CoffeeScriptSourceSet) >> sourceSet
+        2 * sourceSet.getSource() >> sourceDirSet
+        1 * sourceDirSet.srcDir("app/assets")
+        1 * sourceDirSet.include("**/*.coffee")
+    }
+
+    interface PlayAppInternal extends PlayApplicationSpec, ComponentSpecInternal {}
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayDistributionPluginTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayDistributionPluginTest.groovy
new file mode 100644
index 0000000..7f1c3ff
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayDistributionPluginTest.groovy
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.api.Action
+import org.gradle.api.DomainObjectSet
+import org.gradle.api.NamedDomainObjectSet
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.file.CopySpec
+import org.gradle.api.file.FileCollection
+import org.gradle.api.file.FileTree
+import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.file.copy.CopySpecInternal
+import org.gradle.api.internal.file.copy.DestinationRootCopySpec
+import org.gradle.api.java.archives.Manifest
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.application.CreateStartScripts
+import org.gradle.api.tasks.bundling.Zip
+import org.gradle.internal.reflect.Instantiator
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.jvm.tasks.Jar
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.BinaryContainer
+import org.gradle.platform.base.BinaryTasksCollection
+import org.gradle.play.PlayApplicationBinarySpec
+import org.gradle.play.distribution.PlayDistribution
+import org.gradle.play.distribution.PlayDistributionContainer
+import org.gradle.play.internal.distribution.DefaultPlayDistribution
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+
+class PlayDistributionPluginTest extends Specification {
+    def plugin = new PlayDistributionPlugin()
+
+    def "adds default distribution for each binary" () {
+        DomainObjectSet jarTasks1 = Stub(DomainObjectSet)
+        DomainObjectSet jarTasks2 = Stub(DomainObjectSet)
+        PlayApplicationBinarySpec bin1 = binary("bin1", jarTasks1)
+        PlayApplicationBinarySpec bin2 = binary("bin2", jarTasks2)
+        BinaryContainer binaryContainer = binaryContainer([ bin1, bin2 ])
+
+        def distributions = [ bin1: distribution(bin1), bin2: distribution(bin2) ]
+        PlayDistributionContainer distributionContainer = Mock(PlayDistributionContainer) {
+            findByName(_) >> { String name ->
+                return distributions[name]
+            }
+        }
+        ServiceRegistry serviceRegistry = Mock(ServiceRegistry) {
+            get(Instantiator.class) >> Mock(Instantiator) {
+                newInstance(DefaultPlayDistribution.class, _, _, _) >> { Class c, otherArgs ->
+                    return distributions[otherArgs[0]]
+                }
+            }
+            get(FileOperations.class) >> Mock(FileOperations) {
+                copySpec(_) >> Stub(CopySpec)
+            }
+        }
+        ConfigurationContainer configurationContainer = Stub(ConfigurationContainer) {
+            create(_) >> Stub(Configuration)
+            maybeCreate(_) >> Stub(Configuration)
+        }
+        PlayPluginConfigurations configurations = new PlayPluginConfigurations(configurationContainer, Stub(DependencyHandler))
+
+        when:
+        plugin.createDistributions(distributionContainer, binaryContainer, configurations, serviceRegistry)
+
+        then:
+        1 * distributionContainer.add(distributions["bin1"])
+        1 * distributionContainer.add(distributions["bin2"])
+    }
+
+    def "adds scripts and distribution jar tasks for binary" () {
+        def distributions = Mock(PlayDistributionContainer)
+        File buildDir = new File("")
+        DomainObjectSet jarTasks = Stub(DomainObjectSet)
+        PlayApplicationBinarySpec binary = binary("playBinary", jarTasks)
+        binary.getJarFile() >> Stub(File) {
+            getName() >> "playBinary.zip"
+        }
+        CollectionBuilder tasks = Mock(CollectionBuilder) {
+            get("createPlayBinaryStartScripts") >> Stub(CreateStartScripts)
+            get("createPlayBinaryDistributionJar") >> Stub(Jar)
+        }
+        PlayDistribution distribution = Mock(PlayDistribution) {
+            getName() >> "playBinary"
+            getBinary() >> binary
+            getContents() >> Mock(CopySpecInternal) {
+                1 * from("README")
+                addChild() >> Mock(CopySpecInternal) {
+                    into("lib") >> Mock(CopySpec) {
+                        1 * from(_ as Jar)
+                        1 * from(_ as File)
+                        1 * from(_ as FileCollection)
+                    }
+                    into("bin") >> Mock(CopySpec) {
+                        1 * from(_ as CreateStartScripts)
+                        1 * setFileMode(0755)
+                    }
+                    into("conf") >> Mock(CopySpec) {
+                        1 * from("conf") >> Mock(CopySpec) {
+                            1 * exclude("routes")
+                        }
+                    }
+                }
+            }
+        }
+        ConfigurationContainer configurationContainer = Stub(ConfigurationContainer) {
+            create(_) >> Stub(Configuration)
+            maybeCreate(_) >> Stub(Configuration)
+        }
+        PlayPluginConfigurations configurations = new PlayPluginConfigurations(configurationContainer, Stub(DependencyHandler))
+
+        when:
+        plugin.createDistributionContentTasks(tasks, buildDir, distributions, configurations)
+
+        then:
+        1 * distributions.withType(PlayDistribution) >> WrapUtil.toNamedDomainObjectSet(PlayDistribution, distribution)
+        1 * tasks.create("createPlayBinaryStartScripts", CreateStartScripts, _) >> { String name, Class type, Action action ->
+            action.execute(Mock(CreateStartScripts) {
+                1 * setDescription(_)
+                1 * setClasspath(_)
+                1 * setMainClassName("play.core.server.NettyServer")
+                1 * setApplicationName("playBinary")
+                1 * setOutputDir(_)
+            })
+        }
+        1 * tasks.create("createPlayBinaryDistributionJar", Jar, _) >> { String name, Class type, Action action ->
+            action.execute(Mock(Jar) {
+                1 * setArchiveName("playBinary.zip")
+                1 * dependsOn(jarTasks)
+                1 * setDestinationDir(_)
+                1 * from(_ as FileTree)
+                1 * getProject() >> Stub(Project) {
+                    fileTree(_) >> Stub(FileTree)
+                }
+                1 * getManifest() >> Mock(Manifest) {
+                    1 * attributes(_) >> { Map attributes ->
+                        assert attributes.containsKey("Class-Path")
+                    }
+                }
+            })
+        }
+    }
+
+    def "adds dist and stage tasks for binary" () {
+        File buildDir = new File("")
+        DomainObjectSet jarTasks = Stub(DomainObjectSet)
+        PlayApplicationBinarySpec binary = binary("playBinary", jarTasks)
+        CollectionBuilder tasks = Mock(CollectionBuilder) {
+            get("stagePlayBinaryDist") >> Stub(Copy)
+        }
+        PlayDistribution distribution = Mock(PlayDistribution) {
+            getName() >> "playBinary"
+            getContents() >> Mock(CopySpecInternal)
+        }
+        def distributions = Mock(PlayDistributionContainer)
+
+        when:
+        plugin.createDistributionZipTasks(tasks, buildDir, distributions)
+
+        then:
+        1 * distributions.withType(PlayDistribution) >> WrapUtil.toNamedDomainObjectSet(PlayDistribution, distribution)
+        1 * tasks.create("createPlayBinaryDist", Zip, _) >> { String name, Class type, Action action ->
+            action.execute(Mock(Zip) {
+                1 * setDescription(_)
+                1 * setGroup(_)
+                1 * setDestinationDir(_)
+                1 * setArchiveName("playBinary.zip")
+                1 * from(_ as Copy)
+            })
+        }
+        1 * tasks.create("stagePlayBinaryDist", Copy, _) >> { String name, Class type, Action action ->
+            action.execute(Mock(Copy) {
+                1 * setDescription(_)
+                1 * setGroup(_)
+                1 * setDestinationDir(_)
+                1 * getRootSpec() >> Mock(DestinationRootCopySpec) {
+                    1 * addChild() >> Mock(CopySpecInternal) {
+                        1 * into("playBinary")
+                        1 * with(_ as CopySpecInternal)
+                    }
+                }
+            })
+        }
+    }
+
+    def binaryContainer(List binaries) {
+        return Stub(BinaryContainer) {
+            withType(PlayApplicationBinarySpec.class) >> Stub(NamedDomainObjectSet) {
+                iterator() >> binaries.iterator()
+            }
+        }
+    }
+
+    def distributions(List distributions) {
+        return Stub(PlayDistributionContainer) {
+            iterator() >> distributions.iterator()
+            matching(_) >> Stub(NamedDomainObjectSet) {
+                iterator() >> distributions.iterator()
+            }
+        }
+    }
+
+    def binary(String name, DomainObjectSet jarTasks) {
+        return Stub(PlayApplicationBinarySpec) {
+            getTasks() >> Stub(BinaryTasksCollection) {
+                withType(Jar.class) >> jarTasks
+            }
+            getName() >> name
+        }
+    }
+
+    def distribution(PlayApplicationBinarySpec binary) {
+        return Mock(PlayDistribution) {
+            getBinary() >> binary
+        }
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayJavaScriptPluginTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayJavaScriptPluginTest.groovy
new file mode 100644
index 0000000..1354ac4
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayJavaScriptPluginTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.api.Action
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.language.base.FunctionalSourceSet
+import org.gradle.language.javascript.JavaScriptSourceSet
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.internal.ComponentSpecInternal
+import org.gradle.play.PlayApplicationSpec
+import spock.lang.Specification
+
+class PlayJavaScriptPluginTest extends Specification {
+    def "adds javaScript source sets to play components" () {
+        def plugin = new PlayJavaScriptPlugin()
+        def components = Mock(CollectionBuilder)
+        def sources = Mock(FunctionalSourceSet)
+        def sourceSet = Mock(JavaScriptSourceSet)
+        def sourceDirSet = Mock(SourceDirectorySet)
+
+        when:
+        def playApp = Stub(PlayAppInternal) {
+            getName() >> "play"
+            getSources() >> sources
+        }
+        _ * components.beforeEach(_) >> { Action a -> a.execute(playApp) }
+
+        and:
+        plugin.createJavascriptSourceSets(components)
+
+        then:
+        1 * sources.create("javaScriptAssets", JavaScriptSourceSet) >> sourceSet
+        2 * sourceSet.getSource() >> sourceDirSet
+        1 * sourceDirSet.srcDir("app/assets")
+        1 * sourceDirSet.include("**/*.js")
+    }
+
+    interface PlayAppInternal extends PlayApplicationSpec, ComponentSpecInternal {}
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayTestPluginTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayTestPluginTest.groovy
new file mode 100644
index 0000000..284ebc1
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/plugins/PlayTestPluginTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.plugins
+
+import org.gradle.api.Task
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.file.FileResolver
+import org.gradle.api.internal.project.ProjectIdentifier
+import org.gradle.api.tasks.testing.Test
+import org.gradle.language.scala.tasks.PlatformScalaCompile
+import org.gradle.model.collection.CollectionBuilder
+import org.gradle.platform.base.BinaryContainer
+import org.gradle.platform.base.BinaryTasksCollection
+import org.gradle.play.internal.PlayApplicationBinarySpecInternal
+import org.gradle.play.internal.toolchain.PlayToolChainInternal
+import org.gradle.play.internal.toolchain.PlayToolProvider
+import org.gradle.play.platform.PlayPlatform
+import spock.lang.Specification
+
+class PlayTestPluginTest extends Specification {
+
+    CollectionBuilder<Task> taskCollectionBuilder = Mock(CollectionBuilder)
+    def binaryContainer = Mock(BinaryContainer)
+    def projectIdentifier = Mock(ProjectIdentifier)
+    def binary = Mock(PlayApplicationBinarySpecInternal)
+    def playPlatform = Mock(PlayPlatform)
+    def playToolChain = Mock(PlayToolChainInternal)
+    def playToolProvider = Mock(PlayToolProvider)
+
+    def configuration = Stub(Configuration)
+    def configurations = Mock(ConfigurationContainer)
+    def dependencyHandler = Mock(DependencyHandler)
+
+    File buildDir = new File("tmp")
+
+    PlayTestPlugin plugin = new PlayTestPlugin()
+
+    def setup(){
+        _ * binaryContainer.withType(PlayApplicationBinarySpecInternal.class) >> binaryContainer
+        _ * binaryContainer.iterator() >> [binary].iterator()
+        _ * binary.name >> "someBinary"
+        _ * binary.getTasks() >> Mock(BinaryTasksCollection)
+
+        _ * configurations.create(_) >> configuration
+        _ * configurations.maybeCreate(_) >> configuration
+    }
+
+    def "adds test related tasks per binary"() {
+        given:
+        def fileResolver = Mock(FileResolver)
+        1 * fileResolver.resolve('test') >> new File('test')
+
+        when:
+        plugin.createTestTasks(taskCollectionBuilder, binaryContainer, new PlayPluginConfigurations(configurations, dependencyHandler), fileResolver, projectIdentifier, buildDir)
+
+        then:
+        1 * taskCollectionBuilder.create("compileSomeBinaryTests", PlatformScalaCompile, _)
+        1 * taskCollectionBuilder.create("testSomeBinary", Test, _)
+        0 * taskCollectionBuilder.create(_)
+        0 * taskCollectionBuilder.create(_, _, _)
+    }
+}
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/PlayRunTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/PlayRunTest.groovy
new file mode 100644
index 0000000..9c7a7a5
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/PlayRunTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+import org.gradle.api.internal.file.collections.SimpleFileCollection
+import org.gradle.platform.base.internal.toolchain.ResolvedTool
+import org.gradle.play.internal.run.PlayApplicationRunner
+import org.gradle.play.internal.run.PlayApplicationRunnerToken
+import org.gradle.play.internal.run.PlayRunSpec
+import org.gradle.util.RedirectStdIn
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class PlayRunTest extends Specification {
+
+    PlayApplicationRunnerToken runnerToken = Mock(PlayApplicationRunnerToken)
+    PlayApplicationRunner playApplicationRunner = Mock(PlayApplicationRunner)
+    ResolvedTool<PlayApplicationRunner> playApplicationRunnerTool = Mock(ResolvedTool) {
+        get() >> playApplicationRunner
+    }
+    InputStream systemInputStream = Mock()
+
+    @Rule
+    RedirectStdIn redirectStdIn;
+
+    PlayRun playRun
+
+    def setup() {
+        playRun = TestUtil.createTask(PlayRun)
+        playRun.applicationJar = new File("application.jar")
+        playRun.runtimeClasspath = new SimpleFileCollection()
+        playRun.playApplicationRunnerTool = playApplicationRunnerTool
+        System.in = systemInputStream
+    }
+
+    def "can customize memory"() {
+        given:
+        1 * systemInputStream.read() >> 4
+        playRun.forkOptions.memoryInitialSize = "1G"
+        playRun.forkOptions.memoryMaximumSize = "5G"
+        when:
+        playRun.execute();
+        then:
+        1 * playApplicationRunner.start(_) >> { PlayRunSpec spec ->
+            assert spec.getForkOptions().memoryInitialSize == "1G"
+            assert spec.getForkOptions().memoryMaximumSize == "5G"
+            runnerToken
+        }
+    }
+
+    def "passes forkOptions never null"() {
+        1 * systemInputStream.read() >> 4
+        when:
+        playRun.execute();
+        then:
+        1 * playApplicationRunner.start(_) >> { PlayRunSpec spec ->
+            assert spec.getForkOptions() != null
+            runnerToken
+        }
+    }
+
+    def "stops application after receiving ctrl+d"() {
+        1 * systemInputStream.read() >> {
+            1 * runnerToken.stop()
+            return 4
+        }
+        when:
+        playRun.execute();
+        then:
+        1 * playApplicationRunner.start(_) >> runnerToken
+    }
+}
\ No newline at end of file
diff --git a/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/TwirlCompileTest.groovy b/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/TwirlCompileTest.groovy
new file mode 100644
index 0000000..977bea1
--- /dev/null
+++ b/subprojects/platform-play/src/test/groovy/org/gradle/play/tasks/TwirlCompileTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.play.tasks
+import org.gradle.api.Action
+import org.gradle.api.internal.TaskExecutionHistory
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs
+import org.gradle.api.tasks.incremental.InputFileDetails
+import org.gradle.platform.base.internal.toolchain.ResolvedTool
+import org.gradle.play.internal.twirl.TwirlCompileSpec
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+import org.gradle.language.base.internal.compile.Compiler
+
+class TwirlCompileTest extends Specification {
+    DefaultProject project = TestUtil.createRootProject()
+    TwirlCompile compile = project.tasks.create("twirlCompile", TwirlCompile)
+    Compiler<TwirlCompileSpec> twirlCompiler = Mock(Compiler)
+    ResolvedTool<Compiler<TwirlCompileSpec>> twirlCompilerTool = Mock(ResolvedTool) {
+        get() >> twirlCompiler
+    }
+    IncrementalTaskInputs taskInputs = Mock(IncrementalTaskInputs)
+
+    def "invokes twirl compiler"(){
+        given:
+        def outputDir = Mock(File);
+        compile.compilerTool = twirlCompilerTool
+        compile.outputDirectory = outputDir
+        compile.outputs.history = Stub(TaskExecutionHistory)
+        when:
+        compile.compile(withNonIncrementalInputs())
+        then:
+        1 * twirlCompiler.execute(_)
+    }
+
+    IncrementalTaskInputs withNonIncrementalInputs() {
+        _ * taskInputs.isIncremental() >> false
+        taskInputs;
+    }
+
+    def "deletes stale output files"(){
+        given:
+        def outputDir = new File("outputDir");
+        compile.compilerTool = twirlCompilerTool
+        compile.outputDirectory = outputDir
+        def outputCleaner = Spy(TwirlCompile.TwirlStaleOutputCleaner, constructorArgs: [outputDir])
+        compile.setCleaner(outputCleaner)
+        when:
+        compile.compile(withDeletedInputFile())
+        then:
+        1 * outputCleaner.execute(_)
+        1 * twirlCompiler.execute(_)
+    }
+
+    IncrementalTaskInputs withDeletedInputFile() {
+        def details = someInputFileDetails();
+        _ * taskInputs.isIncremental() >> true;
+        _ * taskInputs.outOfDate(_)
+        _ * taskInputs.removed({Action<InputFileDetails> action -> action.execute(details)})
+        taskInputs
+    }
+
+    private InputFileDetails someInputFileDetails() {
+        def inputFileDetails = Mock(InputFileDetails)
+        _  * inputFileDetails.getFile() >> new File("some/path/index.scala.html");
+        inputFileDetails
+    }
+}
diff --git a/subprojects/plugin-development/plugin-development.gradle b/subprojects/plugin-development/plugin-development.gradle
new file mode 100644
index 0000000..42be993
--- /dev/null
+++ b/subprojects/plugin-development/plugin-development.gradle
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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')
+    testRuntime project(':toolingApi')
+}
+
+useTestFixtures()
+useClassycle()
+strictCompile()
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginIntegrationTest.groovy
new file mode 100644
index 0000000..78e2327
--- /dev/null
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginIntegrationTest.groovy
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.devel.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.test.fixtures.archive.JarTestFixture
+
+class JavaGradlePluginPluginIntegrationTest extends WellBehavedPluginTest {
+    final static String NO_DESCRIPTOR_WARNING = JavaGradlePluginPlugin.NO_DESCRIPTOR_WARNING_MESSAGE
+    final static String BAD_IMPL_CLASS_WARNING_PREFIX = JavaGradlePluginPlugin.BAD_IMPL_CLASS_WARNING_MESSAGE.split('%')[0]
+    final static String INVALID_DESCRIPTOR_WARNING_PREFIX = JavaGradlePluginPlugin.INVALID_DESCRIPTOR_WARNING_MESSAGE.split('%')[0]
+
+    @Override
+    String getMainTask() {
+        return "jar"
+    }
+
+    def "applying java-gradle-plugin causes project to be a java project"() {
+        given:
+        applyPlugin()
+
+        expect:
+        succeeds "compileJava"
+    }
+
+    def "jar produces usable plugin jar"() {
+        given:
+        buildFile()
+        def descriptorFile = goodPluginDescriptor()
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        def jar = new JarTestFixture(file('build/libs/test.jar'))
+        jar.assertContainsFile('META-INF/gradle-plugins/test-plugin.properties') &&
+            jar.assertFileContent('META-INF/gradle-plugins/test-plugin.properties', descriptorFile.text)
+        jar.assertContainsFile('com/xxx/TestPlugin.class')
+        ! output.contains(NO_DESCRIPTOR_WARNING)
+        ! output.contains(BAD_IMPL_CLASS_WARNING_PREFIX)
+        ! output.contains(INVALID_DESCRIPTOR_WARNING_PREFIX)
+    }
+
+    def "jar issues warning if built jar does not contain any plugin descriptors" () {
+        given:
+        buildFile()
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        output.contains(NO_DESCRIPTOR_WARNING)
+    }
+
+
+    def "jar issues warning if built jar contains bad descriptor" (String descriptorContents, String warningMessage) {
+        given:
+        buildFile()
+        badPluginDescriptor(descriptorContents)
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        output.contains(warningMessage)
+
+        where:
+        descriptorContents                              | warningMessage
+        ''                                              | NO_DESCRIPTOR_WARNING
+        'implementation-class='                         | INVALID_DESCRIPTOR_WARNING_PREFIX
+        'implementation-class=com.xxx.WrongPluginClass' | BAD_IMPL_CLASS_WARNING_PREFIX
+    }
+
+    def "jar issues warning if built jar contains one bad descriptor out of multiple descriptors" (String descriptorContents, String warningMessage) {
+        given:
+        buildFile()
+        goodPluginDescriptor()
+        badPluginDescriptor('bad-plugin', descriptorContents)
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        output.count(warningMessage) == 1
+
+        where:
+        descriptorContents                              | warningMessage
+        'implementation-class='                         | INVALID_DESCRIPTOR_WARNING_PREFIX
+        'implementation-class=com.xxx.WrongPluginClass' | BAD_IMPL_CLASS_WARNING_PREFIX
+    }
+
+    def "jar issues correct warnings if built jar contains multiple bad descriptors" (String descriptorContents, String warningMessage, int messageCount) {
+        given:
+        buildFile()
+        badPluginDescriptor('bad-plugin1', descriptorContents)
+        badPluginDescriptor('bad-plugin2', descriptorContents)
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        output.count(warningMessage) == messageCount
+
+        where:
+        descriptorContents                              | warningMessage                    | messageCount
+        ''                                              | NO_DESCRIPTOR_WARNING             | 1
+        'implementation-class='                         | INVALID_DESCRIPTOR_WARNING_PREFIX | 2
+        'implementation-class=com.xxx.WrongPluginClass' | BAD_IMPL_CLASS_WARNING_PREFIX     | 2
+    }
+
+    def "jar issues correct warnings if built jar contains mixed descriptor problems" () {
+        given:
+        buildFile()
+        badPluginDescriptor('bad-plugin1', 'implementation-class=')
+        badPluginDescriptor('bad-plugin2', 'implementation-class=com.xxx.WrongPluginClass')
+        goodPlugin()
+
+        expect:
+        succeeds "jar"
+        output.count(BAD_IMPL_CLASS_WARNING_PREFIX) == 1
+        output.count(INVALID_DESCRIPTOR_WARNING_PREFIX) == 1
+    }
+
+    def buildFile() {
+        buildFile << """
+apply plugin: 'java-gradle-plugin'
+
+jar {
+    archiveName 'test.jar'
+}
+"""
+    }
+
+    def goodPluginDescriptor() {
+        file('src/main/resources/META-INF/gradle-plugins/test-plugin.properties') << """
+implementation-class=com.xxx.TestPlugin
+"""
+    }
+
+    def goodPlugin() {
+        file('src/main/java/com/xxx/TestPlugin.java') << """
+package com.xxx;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+public class TestPlugin implements Plugin<Project> {
+    public void apply(Project project) { }
+}
+"""
+    }
+
+    def badPluginDescriptor(String descriptorId, String descriptorContents) {
+        file("src/main/resources/META-INF/gradle-plugins/${descriptorId}.properties") << descriptorContents
+    }
+
+    def badPluginDescriptor(String descriptorContents) {
+        badPluginDescriptor('test-plugin', descriptorContents)
+    }
+}
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java
new file mode 100644
index 0000000..c259ecc
--- /dev/null
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.devel.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.file.FileCopyDetails;
+import org.gradle.api.internal.plugins.PluginDescriptor;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.bundling.Jar;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * A plugin for validating java gradle plugins during the jar task.  Emits warnings for common error conditions.
+ */
+ at Incubating
+public class JavaGradlePluginPlugin implements Plugin<Project> {
+    private static final Logger LOGGER = Logging.getLogger(JavaGradlePluginPlugin.class);
+    static final String COMPILE_CONFIGURATION = "compile";
+    static final String JAR_TASK = "jar";
+    static final String GRADLE_PLUGINS = "gradle-plugins";
+    static final String PLUGIN_DESCRIPTOR_PATTERN = "META-INF/" + GRADLE_PLUGINS + "/*.properties";
+    static final String CLASSES_PATTERN = "**/*.class";
+    static final String BAD_IMPL_CLASS_WARNING_MESSAGE = "A valid plugin descriptor was found for %s but the implementation class %s was not found in the jar.";
+    static final String INVALID_DESCRIPTOR_WARNING_MESSAGE = "A plugin descriptor was found for %s but it was invalid.";
+    static final String NO_DESCRIPTOR_WARNING_MESSAGE = "No valid plugin descriptors were found in META-INF/" + GRADLE_PLUGINS + "";
+
+    public void apply(Project project) {
+        project.getPluginManager().apply(JavaPlugin.class);
+        applyDependencies(project);
+        configureJarTask(project);
+    }
+
+    private void applyDependencies(Project project) {
+        DependencyHandler dependencies = project.getDependencies();
+        dependencies.add(COMPILE_CONFIGURATION, dependencies.gradleApi());
+    }
+
+    private void configureJarTask(Project project) {
+        Jar jarTask = (Jar) project.getTasks().findByName(JAR_TASK);
+        List<PluginDescriptor> descriptors = new ArrayList<PluginDescriptor>();
+        Set<String> classList = new HashSet<String>();
+        PluginDescriptorCollectorAction pluginDescriptorCollector = new PluginDescriptorCollectorAction(descriptors);
+        ClassManifestCollectorAction classManifestCollector = new ClassManifestCollectorAction(classList);
+        PluginValidationAction pluginValidationAction = new PluginValidationAction(descriptors, classList);
+
+        jarTask.filesMatching(PLUGIN_DESCRIPTOR_PATTERN, pluginDescriptorCollector);
+        jarTask.filesMatching(CLASSES_PATTERN, classManifestCollector);
+        jarTask.appendParallelSafeAction(pluginValidationAction);
+    }
+
+    /**
+     * Implements plugin validation tasks to validate that a proper plugin jar is produced.
+     */
+    static class PluginValidationAction implements Action<Task> {
+        Collection<PluginDescriptor> descriptors;
+        Set<String> classes;
+
+        PluginValidationAction(Collection<PluginDescriptor> descriptors, Set<String> classes) {
+            this.descriptors = descriptors;
+            this.classes = classes;
+        }
+
+        public void execute(Task task) {
+            if (descriptors == null || descriptors.isEmpty()) {
+                LOGGER.warn(NO_DESCRIPTOR_WARNING_MESSAGE);
+            } else {
+                for (PluginDescriptor descriptor : descriptors) {
+                    URI descriptorURI = null;
+                    try {
+                        descriptorURI = descriptor.getPropertiesFileUrl().toURI();
+                    } catch (URISyntaxException e) {
+                        // Do nothing since the only side effect is that we wouldn't
+                        // be able to log the plugin descriptor file name.  Shouldn't
+                        // be a reasonable scenario where this occurs since these
+                        // descriptors should be generated from real files.
+                    }
+                    String pluginFileName = descriptorURI != null ? new File(descriptorURI).getName() : "UNKNOWN";
+                    String pluginImplementation = descriptor.getImplementationClassName();
+                    if (pluginImplementation.length() == 0) {
+                        LOGGER.warn(String.format(INVALID_DESCRIPTOR_WARNING_MESSAGE, pluginFileName));
+                    } else if (!hasFullyQualifiedClass(pluginImplementation)) {
+                        LOGGER.warn(String.format(BAD_IMPL_CLASS_WARNING_MESSAGE, pluginFileName, pluginImplementation));
+                    }
+                }
+            }
+        }
+
+        boolean hasFullyQualifiedClass(String fqClass) {
+            return classes.contains(fqClass.replaceAll("\\.", "/") + ".class");
+        }
+    }
+
+    /**
+     * A file copy action that collects plugin descriptors as they are added to the jar.
+     */
+    static class PluginDescriptorCollectorAction implements Action<FileCopyDetails> {
+        List<PluginDescriptor> descriptors;
+
+        PluginDescriptorCollectorAction(List<PluginDescriptor> descriptors) {
+            this.descriptors = descriptors;
+        }
+
+        public void execute(FileCopyDetails fileCopyDetails) {
+            PluginDescriptor descriptor;
+            try {
+                descriptor = new PluginDescriptor(fileCopyDetails.getFile().toURI().toURL());
+            } catch (MalformedURLException e) {
+                // Not sure under what scenario (if any) this would occur,
+                // but there's no sense in collecting the descriptor if it does.
+                return;
+            }
+            if (descriptor.getImplementationClassName() != null) {
+                descriptors.add(descriptor);
+            }
+        }
+    }
+
+    /**
+     * A file copy action that collects class file paths as they are added to the jar.
+     */
+    static class ClassManifestCollectorAction implements Action<FileCopyDetails> {
+        Set<String> classList;
+
+        ClassManifestCollectorAction(Set<String> classList) {
+            this.classList = classList;
+        }
+
+        public void execute(FileCopyDetails fileCopyDetails) {
+            classList.add(fileCopyDetails.getRelativePath().toString());
+        }
+    }
+}
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/package-info.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/package-info.java
new file mode 100644
index 0000000..a80e0d0
--- /dev/null
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for assisting with plugin development.
+ */
+package org.gradle.plugin.devel.plugins;
\ No newline at end of file
diff --git a/subprojects/plugin-development/src/main/resources/META-INF/gradle-plugins/org.gradle.java-gradle-plugin.properties b/subprojects/plugin-development/src/main/resources/META-INF/gradle-plugins/org.gradle.java-gradle-plugin.properties
new file mode 100644
index 0000000..33f8021
--- /dev/null
+++ b/subprojects/plugin-development/src/main/resources/META-INF/gradle-plugins/org.gradle.java-gradle-plugin.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.plugin.devel.plugins.JavaGradlePluginPlugin
\ No newline at end of file
diff --git a/subprojects/plugin-development/src/test/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginTest.groovy b/subprojects/plugin-development/src/test/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginTest.groovy
new file mode 100644
index 0000000..ccc461b
--- /dev/null
+++ b/subprojects/plugin-development/src/test/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginTest.groovy
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.devel.plugins
+
+import org.gradle.api.Action
+import org.gradle.api.Task
+import org.gradle.api.file.FileCopyDetails
+import org.gradle.api.file.RelativePath
+import org.gradle.api.internal.ConventionMapping
+import org.gradle.api.internal.plugins.PluginDescriptor
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.tasks.bundling.Jar
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.internal.LogEvent
+import org.gradle.logging.internal.OutputEvent
+import org.gradle.logging.internal.OutputEventListener
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+class JavaGradlePluginPluginTest extends Specification {
+    final ResettableOutputEventListener outputEventListener = new ResettableOutputEventListener()
+
+    @Rule
+    final ConfigureLogging logging = new ConfigureLogging(outputEventListener)
+
+    final static String NO_DESCRIPTOR_WARNING = JavaGradlePluginPlugin.NO_DESCRIPTOR_WARNING_MESSAGE
+    final static String BAD_IMPL_CLASS_WARNING_PREFIX = JavaGradlePluginPlugin.BAD_IMPL_CLASS_WARNING_MESSAGE.split('%')[0]
+    final static String INVALID_DESCRIPTOR_WARNING_PREFIX = JavaGradlePluginPlugin.INVALID_DESCRIPTOR_WARNING_MESSAGE.split('%')[0]
+
+    def project = TestUtil.builder().withName("plugin").build()
+
+    def "PluginDescriptorCollectorAction correctly identifies plugin descriptor file"(String contents, String expectedPluginImpl, boolean expectedEmpty) {
+        setup:
+        List<PluginDescriptor> descriptors = new ArrayList<PluginDescriptor>()
+        Action<FileCopyDetails> findPluginDescriptor = new JavaGradlePluginPlugin.PluginDescriptorCollectorAction(descriptors)
+        File descriptorFile = project.file('test-plugin.properties')
+        descriptorFile << contents
+        FileCopyDetails stubDetails = Stub(FileCopyDetails) {
+            getFile() >> descriptorFile
+        }
+
+        expect:
+        findPluginDescriptor.execute(stubDetails)
+        descriptors.isEmpty() == expectedEmpty
+        descriptors.isEmpty() || descriptors.get(0).implementationClassName == expectedPluginImpl
+
+        where:
+        contents                             | expectedPluginImpl | expectedEmpty
+        'implementation-class=xxx.SomeClass' | 'xxx.SomeClass'    | false
+        'implementation-class='              | ''                 | false
+        ''                                   | null               | true
+    }
+
+    def "ClassManifestCollector captures class name"() {
+        setup:
+        Set<String> classList = new HashSet<String>()
+        Action<FileCopyDetails> classManifestCollector = new JavaGradlePluginPlugin.ClassManifestCollectorAction(classList)
+        FileCopyDetails stubDetails = Stub(FileCopyDetails) {
+            getRelativePath() >> { new RelativePath(true, 'com', 'xxx', 'TestPlugin.class') }
+        }
+
+        when:
+        classManifestCollector.execute(stubDetails)
+
+        then:
+        classList.contains('com/xxx/TestPlugin.class')
+    }
+
+    def "PluginValidationAction finds fully qualified class"(List classList, String fqClass, boolean expectedValue) {
+        setup:
+        Action<Task> pluginValidationAction = new JavaGradlePluginPlugin.PluginValidationAction([], classList as Set<String>)
+
+        expect:
+        pluginValidationAction.hasFullyQualifiedClass(fqClass) == expectedValue
+
+        where:
+        classList                        | fqClass              | expectedValue
+        ['com/xxx/TestPlugin.class']     | 'com.xxx.TestPlugin' | true
+        ['TestPlugin.class']             | 'TestPlugin'         | true
+        []                               | 'com.xxx.TestPlugin' | false
+        ['com/xxx/yyy/TestPlugin.class'] | 'com.xxx.TestPlugin' | false
+    }
+
+    def "PluginValidationAction logs correct warning messages"(String impl, String implFile, String expectedMessage) {
+        setup:
+        Task stubTask = Stub(Task)
+        List<PluginDescriptor> descriptors = []
+        if (impl != null) {
+            descriptors.add(Stub(PluginDescriptor) {
+                _ * getPropertiesFileUrl() >> { new URL("file:///test-plugin-${impl}.properties") }
+                _ * getImplementationClassName() >> { impl }
+            })
+        }
+        Set<String> classes = new HashSet<String>()
+        if (implFile) {
+            classes.add(implFile)
+        }
+        Action<Task> pluginValidationAction = new JavaGradlePluginPlugin.PluginValidationAction(descriptors, classes)
+        outputEventListener.reset()
+
+        expect:
+        pluginValidationAction.execute(stubTask)
+        expectedMessage == null || outputEventListener.toString().contains(expectedMessage)
+
+        where:
+        impl    | implFile      | expectedMessage
+        null    | null          | NO_DESCRIPTOR_WARNING
+        ''      | null          | INVALID_DESCRIPTOR_WARNING_PREFIX
+        'x.y.z' | null          | BAD_IMPL_CLASS_WARNING_PREFIX
+        'x.y.z' | 'z.class'     | BAD_IMPL_CLASS_WARNING_PREFIX
+        'x.y.z' | 'x/y/z.class' | null
+    }
+
+    def "apply adds java plugin"() {
+        when:
+        project.pluginManager.apply(JavaGradlePluginPlugin)
+
+        then:
+        project.plugins.findPlugin(JavaPlugin)
+    }
+
+    def "apply adds gradleApi dependency to compile"() {
+        when:
+        project.pluginManager.apply(JavaGradlePluginPlugin)
+
+        then:
+        project.configurations
+                .getByName(JavaGradlePluginPlugin.COMPILE_CONFIGURATION)
+                .dependencies.find {
+            it.source.files == project.dependencies.gradleApi().source.files
+        }
+    }
+
+    def "apply configures filesMatching actions on jar spec"() {
+        setup:
+        project.pluginManager.apply(JavaPlugin)
+        def Jar mockJarTask = mockJar(project)
+
+        when:
+        project.pluginManager.apply(JavaGradlePluginPlugin)
+
+        then:
+        1 * mockJarTask.filesMatching(JavaGradlePluginPlugin.PLUGIN_DESCRIPTOR_PATTERN, { it instanceof JavaGradlePluginPlugin.PluginDescriptorCollectorAction })
+        1 * mockJarTask.filesMatching(JavaGradlePluginPlugin.CLASSES_PATTERN, { it instanceof JavaGradlePluginPlugin.ClassManifestCollectorAction })
+    }
+
+    def "apply configures doLast action on jar"() {
+        setup:
+        project.pluginManager.apply(JavaPlugin)
+        def Jar mockJarTask = mockJar(project)
+
+        when:
+        project.pluginManager.apply(JavaGradlePluginPlugin)
+
+        then:
+        1 * mockJarTask.appendParallelSafeAction({ it instanceof JavaGradlePluginPlugin.PluginValidationAction })
+    }
+
+    def Jar mockJar(project) {
+        def Jar mockJar = Mock(Jar) {
+            _ * getName() >> { JavaGradlePluginPlugin.JAR_TASK }
+            _ * getConventionMapping() >> { Stub(ConventionMapping) }
+        }
+        project.tasks.remove(project.tasks.getByName(JavaGradlePluginPlugin.JAR_TASK))
+        project.tasks.add(mockJar)
+        return mockJar
+    }
+
+    static class ResettableOutputEventListener implements OutputEventListener {
+        final StringBuffer buffer = new StringBuffer()
+
+        void reset() {
+            buffer.delete(0, buffer.size())
+        }
+
+        @Override
+        String toString() {
+            return buffer.toString()
+        }
+
+        @Override
+        synchronized void onOutput(OutputEvent event) {
+            LogEvent logEvent = event as LogEvent
+            buffer.append(logEvent.message)
+        }
+    }
+}
diff --git a/subprojects/plugin-use/plugin-use.gradle b/subprojects/plugin-use/plugin-use.gradle
new file mode 100644
index 0000000..4a67de8
--- /dev/null
+++ b/subprojects/plugin-use/plugin-use.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 project(":core")
+    compile project(":dependencyManagement")
+    compile project(":resourcesHttp")
+    compile libraries.gson
+
+    integTestRuntime project(':plugins')
+
+    testFixturesCompile project(":internalIntegTesting")
+}
+
+useTestFixtures()
+useClassycle()
\ No newline at end of file
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/CorePluginUseIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/CorePluginUseIntegrationSpec.groovy
new file mode 100644
index 0000000..1ffe32b
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/CorePluginUseIntegrationSpec.groovy
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.hamcrest.Matchers.startsWith
+
+class CorePluginUseIntegrationSpec extends AbstractIntegrationSpec {
+
+    public static final String QUALIFIED_JAVA = "org.gradle.java"
+    public static final String UNQUALIFIED_JAVA = "java"
+
+    void "can resolve core plugins"() {
+        when:
+        buildScript """
+            plugins {
+              id 'java'
+            }
+        """
+
+        then:
+        succeeds "javadoc"
+    }
+
+    void "can resolve qualified core plugins"() {
+        when:
+        buildScript """
+            plugins {
+              id 'org.gradle.java'
+            }
+        """
+
+        then:
+        succeeds "javadoc"
+    }
+
+    void "core plugins cannot have a version number"() {
+        given:
+        buildScript """
+            plugins {
+                id "java" version "1.0"
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription("Error resolving plugin [id: 'java', version: '1.0']")
+        failure.assertHasCause("Plugin 'java' is a core Gradle plugin, which cannot be specified with a version number")
+        failure.assertHasFileName("Build file '$buildFile.absolutePath'")
+        failure.assertHasLineNumber(3)
+    }
+
+    void "qualified core plugins cannot have a version number"() {
+        given:
+        buildScript """
+            plugins {
+                id "org.gradle.java" version "1.0"
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription("Error resolving plugin [id: 'org.gradle.java', version: '1.0']")
+        failure.assertHasCause("Plugin 'org.gradle.java' is a core Gradle plugin, which cannot be specified with a version number")
+        failure.assertHasFileName("Build file '$buildFile.absolutePath'")
+        failure.assertHasLineNumber(3)
+    }
+
+    def "cant ask for same plugin twice"() {
+        given:
+        buildScript """
+            plugins {
+                id "java"
+                id "java"
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertThatDescription(startsWith("Plugin with id 'java' was already requested at line 3"))
+        failure.assertHasFileName("Build file '$buildFile.absolutePath'")
+        failure.assertHasLineNumber(4)
+    }
+
+    def "can reapply core plugin applied via plugins block"() {
+        when:
+        buildScript """
+            plugins {
+                id "java"
+            }
+
+            assert plugins.hasPlugin("java")
+
+            apply plugin: "java"
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    def "can reapply core plugin applied via qualified id in plugins block"() {
+        when:
+        buildScript """
+            plugins {
+                id "org.gradle.java"
+            }
+
+            assert plugins.hasPlugin("java")
+
+            apply plugin: "java"
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    def "can use qualified and unqualified ids to detect core plugins"() {
+        when:
+        buildScript """
+            plugins {
+                id "$pluginId"
+            }
+
+            def i = 0
+            plugins.withId("$QUALIFIED_JAVA") {
+                ++i
+            }
+            plugins.withId("$UNQUALIFIED_JAVA") {
+                ++i
+            }
+            assert i == 2
+
+            assert plugins.getPlugin("$QUALIFIED_JAVA")
+            assert plugins.getPlugin("$UNQUALIFIED_JAVA")
+        """
+
+        then:
+        succeeds "tasks"
+
+        where:
+        pluginId << [QUALIFIED_JAVA, UNQUALIFIED_JAVA]
+    }
+
+    def "can use apply method to load core plugins qualified or unqualified"() {
+        when:
+        buildScript """
+            apply plugin: "${pluginId}"
+        """
+
+        then:
+        succeeds "clean"
+
+        where:
+        pluginId << [QUALIFIED_JAVA, UNQUALIFIED_JAVA]
+    }
+
+    def "can use apply method with other form of core plugin without problem"() {
+        when:
+        buildScript """
+            plugins {
+                id "${plugins[0]}"
+            }
+
+            apply plugin: "${plugins[1]}"
+        """
+
+        then:
+        succeeds "clean"
+
+        where:
+        plugins << [QUALIFIED_JAVA, UNQUALIFIED_JAVA].permutations()
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy
new file mode 100644
index 0000000..83f9c8d
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+//These tests depend on https://plugins.gradle.org
+ at Requires(TestPrecondition.ONLINE)
+class DeployedPortalIntegrationSpec extends AbstractIntegrationSpec {
+
+    private final static String HELLO_WORLD_PLUGIN_ID = "org.gradle.hello-world"
+    private final static String HELLO_WORLD_PLUGIN_VERSION = "0.2"
+
+    def setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "can resolve a plugin from portal"() {
+        when:
+        buildScript """
+            plugins {
+                id "$HELLO_WORLD_PLUGIN_ID" version "$HELLO_WORLD_PLUGIN_VERSION"
+            }
+        """
+
+        then:
+        succeeds("helloWorld")
+
+        and:
+        output.contains("Hello World!")
+    }
+
+    def "resolving a non-existing plugin results in an informative error message"() {
+        when:
+        buildScript """
+            plugins {
+                id "org.gradle.non-existing" version "1.0"
+            }
+        """
+
+        then:
+        fails("dependencies")
+
+        and:
+        failureDescriptionStartsWith("Plugin [id: 'org.gradle.non-existing', version: '1.0'] was not found in any of the following sources:")
+        failureDescriptionContains("- Gradle Central Plugin Repository (no 'org.gradle.non-existing' plugin available - see https://plugins.gradle.org for available plugins)")
+    }
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginAlreadyOnClasspathDetectionIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginAlreadyOnClasspathDetectionIntegrationSpec.groovy
new file mode 100644
index 0000000..4bb8e88
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginAlreadyOnClasspathDetectionIntegrationSpec.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.plugin.PluginBuilder
+
+import static org.gradle.plugin.use.resolve.internal.NotNonCorePluginOnClasspathCheckPluginResolver.pluginOnClasspathErrorMessage
+
+class NonCorePluginAlreadyOnClasspathDetectionIntegrationSpec extends AbstractIntegrationSpec {
+
+    private testPluginBuildscriptBlock() {
+        return """
+            buildscript {
+                repositories {
+                    maven { url "$mavenRepo.uri" }
+                }
+                dependencies {
+                    classpath "plugin:plugin:1.0"
+                }
+            }
+        """
+    }
+
+    private testPluginPluginsBlock() {
+        return """
+            plugins {
+                id "plugin"
+            }
+        """
+    }
+
+    private publishTestPlugin() {
+        def pluginBuilder = new PluginBuilder(testDirectory.file("plugin"))
+
+        def module = mavenRepo.module("plugin", "plugin")
+        def artifactFile = module.artifact([:]).artifactFile
+        module.publish()
+
+        def message = "from plugin"
+        def taskName = "pluginTask"
+        pluginBuilder.addPluginWithPrintlnTask(taskName, message, "plugin")
+        pluginBuilder.publishTo(executer, artifactFile)
+    }
+
+    private publishBuildSrcTestPlugin() {
+        def pluginBuilder = new PluginBuilder(file("buildSrc"))
+        def message = "from plugin"
+        def taskName = "pluginTask"
+        pluginBuilder.addPluginWithPrintlnTask(taskName, message, "plugin")
+        pluginBuilder.generateForBuildSrc()
+    }
+
+    def "cannot apply plugins added to parent buildscript classpath in plugins block"() {
+        given:
+        publishTestPlugin()
+
+        when:
+        buildScript """
+            ${testPluginBuildscriptBlock()}
+        """
+
+        settingsFile << "include 'sub'"
+
+        file("sub/build.gradle") << """
+            ${testPluginPluginsBlock()}
+        """
+
+        then:
+        fails "sub:tasks"
+
+        and:
+        failure.assertHasCause(pluginOnClasspathErrorMessage('plugin'))
+    }
+
+    def "cannot apply buildSrc plugins in plugins block"() {
+        given:
+        publishBuildSrcTestPlugin()
+
+        when:
+        buildScript """
+            ${testPluginPluginsBlock()}
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasCause(pluginOnClasspathErrorMessage('plugin'))
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginUseIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginUseIntegrationSpec.groovy
new file mode 100644
index 0000000..b094c79
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonCorePluginUseIntegrationSpec.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class NonCorePluginUseIntegrationSpec extends AbstractIntegrationSpec {
+
+    def "non core plugin without version produces error message"() {
+        given:
+        buildScript """
+            plugins {
+                id "foo.bar"
+            }
+        """
+
+        when:
+        fails "tasks"
+
+        then:
+        failure.assertHasDescription("""Plugin [id: 'foo.bar'] was not found in any of the following sources:
+
+- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
+- Gradle Central Plugin Repository (plugin dependency must include a version number for this source)""")
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonDeclarativePluginUseIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonDeclarativePluginUseIntegrationSpec.groovy
new file mode 100644
index 0000000..48dc0cd
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/NonDeclarativePluginUseIntegrationSpec.groovy
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.plugin.use.resolve.service.PluginResolutionServiceTestServer
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.junit.Rule
+
+import static org.hamcrest.Matchers.startsWith
+
+class NonDeclarativePluginUseIntegrationSpec extends AbstractIntegrationSpec {
+
+    public static final String PLUGIN_ID = "org.myplugin"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT = "plugin"
+    public static final String USE = "plugins { id '$PLUGIN_ID' version '$VERSION' }"
+
+    @Rule
+    PluginResolutionServiceTestServer service = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        service.start()
+    }
+
+    def "non declarative plugin implementation can access core plugins and not core impl"() {
+        given:
+        publishPlugin """
+            project.apply plugin: 'java'
+
+            // Can see plugin classes
+            getClass().classLoader.loadClass('org.gradle.api.plugins.JavaPlugin')
+
+            // Can't see core impl classes
+            def implClassName = 'com.google.common.collect.Multimap'
+            project.getClass().getClassLoader().loadClass(implClassName)
+
+            try {
+                getClass().getClassLoader().loadClass(implClassName)
+                assert false : "should have failed to load gradle implementation class: \$implClassName"
+            } catch (ClassNotFoundException ignore) {
+
+            }
+
+            project.task('pluginTask')
+        """
+
+        when:
+        buildScript USE
+
+        then:
+        succeeds("pluginTask")
+    }
+
+    def "plugin implementation and dependencies are visible to plugin and build script"() {
+        given:
+        def pluginBuilder2 = new PluginBuilder(file("plugin2"))
+        pluginBuilder2.addPlugin("project.task('plugin2Task')", "test-plugin-2", "TestPlugin2")
+
+        def module2 = service.m2repo.module(GROUP, ARTIFACT + "2", VERSION)
+        pluginBuilder2.publishTo(executer, module2.artifactFile)
+        module2.allowAll()
+
+        def module = publishPlugin """
+            // can load plugin dependended on
+            project.apply plugin: 'test-plugin-2'
+
+            // Can see dependency classes
+            getClass().classLoader.loadClass('${pluginBuilder2.packageName}.TestPlugin2')
+
+            project.task('pluginTask')
+        """
+
+        module.dependsOn(GROUP, ARTIFACT + "2", VERSION)
+        module.publishPom()
+
+        when:
+        buildScript """
+            $USE
+
+            def ops = []
+
+            plugins.withId('$PLUGIN_ID') {
+              ops << "withId 1"
+            }
+
+            plugins.withId("test-plugin-2") {
+              ops << "withId 2"
+            }
+
+            def class1 = ${pluginBuilder.packageName}.TestPlugin
+            def class2 = ${pluginBuilder2.packageName}.TestPlugin2
+
+            plugins.withType(class1) {
+              ops << "withType 1"
+            }
+
+            plugins.withType(class2) {
+              ops << "withType 2"
+            }
+
+            apply plugin: '$PLUGIN_ID'
+            apply plugin: 'test-plugin-2'
+
+            println "ops = \$ops"
+        """
+
+        then:
+        succeeds("pluginTask", "plugin2Task")
+
+        and:
+        output.contains 'ops = [withId 1, withId 2, withType 1, withType 2]'
+    }
+
+    def "classes from builscript and plugin block are visible in same build"() {
+        given:
+        def pluginBuilder2 = new PluginBuilder(file("plugin2"))
+        pluginBuilder2.addPlugin("project.task('plugin2Task')", "test-plugin-2", "TestPlugin2")
+
+        def module2 = service.m2repo.module(GROUP, ARTIFACT + "2", VERSION)
+        pluginBuilder2.publishTo(executer, module2.artifactFile)
+        module2.allowAll()
+
+        def module = publishPlugin ""
+        module.dependsOn(GROUP, ARTIFACT + "2", VERSION)
+        module.publishPom()
+
+        when:
+        buildScript """
+            buildscript {
+              dependencies {
+                classpath "$GROUP:${ARTIFACT + 2}:$VERSION"
+              }
+              repositories {
+                maven { url "$service.m2repo.uri" }
+              }
+            }
+            $USE
+
+
+            def class1 = ${pluginBuilder.packageName}.TestPlugin
+            def class2 = ${pluginBuilder2.packageName}.TestPlugin2
+        """
+
+        then:
+        succeeds("tasks")
+    }
+
+    def "dependencies of non declarative plugins influence buildscript dependency resolution"() {
+        given:
+        [1, 2].each { n ->
+            def m = service.m2repo.module("test", "test", n)
+            m.publish().allowAll()
+
+            file("j$n").with {
+                file("d/v.txt") << n
+                m.artifactFile.delete()
+                zipTo(m.artifactFile)
+            }
+
+        }
+
+        when:
+        def pluginModule = publishPlugin """
+            project.task('pluginTask').doLast {
+                println "pluginTask - " + this.getClass().classLoader.getResource('d/v.txt').text
+            }
+        """
+
+        pluginModule.dependsOn("test", "test", "2").publishPom()
+
+        and:
+        buildScript """
+            buildscript {
+                repositories {
+                    maven { url "$service.m2repo.uri" }
+                }
+                dependencies {
+                    classpath "test:test:1"
+                }
+            }
+
+            $USE
+
+            task scriptTask << {
+                println "scriptTask - " + this.getClass().classLoader.getResource('d/v.txt').text
+            }
+
+            task buildscriptDependencies << {
+                println "buildscriptDependencies - " + buildscript.configurations.classpath.resolvedConfiguration.resolvedArtifacts.find { it.name == "test" }.moduleVersion.id.version
+            }
+        """
+
+        then:
+        succeeds "pluginTask", "scriptTask", "buildscriptDependencies"
+
+        and:
+        output.contains "pluginTask - 2"
+        output.contains "scriptTask - 2"
+        output.contains "buildscriptDependencies - 2"
+    }
+
+    def "failure due to no plugin with id in implementation"() {
+        when:
+        expectPluginQuery()
+        def module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+        pluginBuilder.addPlugin(PLUGIN_ID, "other")
+        pluginBuilder.publishTo(executer, module.artifactFile)
+
+        and:
+        buildScript """
+            $USE
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertThatDescription(startsWith("Could not apply requested plugin [id: 'org.myplugin', version: '1.0'] as it does not provide a plugin with id 'org.myplugin'"))
+        failure.assertHasLineNumber(2)
+    }
+
+    def "failure due to plugin class is unloadable"() {
+        when:
+        expectPluginQuery()
+        def module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+        pluginBuilder.addUnloadablePlugin("org.myplugin", "OtherPlugin")
+        pluginBuilder.publishTo(executer, module.artifactFile)
+
+        and:
+        buildScript """
+            $USE
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasDescription("An exception occurred applying plugin request [id: 'org.myplugin', version: '1.0']")
+        failure.assertHasCause("Could not create plugin of type 'OtherPlugin'.")
+        failure.assertHasLineNumber(2)
+    }
+
+    def "failure due to plugin apply throwing"() {
+        when:
+        expectPluginQuery()
+        def module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+        pluginBuilder.addPlugin("throw new Exception('throwing plugin')", PLUGIN_ID)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+
+        and:
+        buildScript """
+            $USE
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasDescription("An exception occurred applying plugin request [id: 'org.myplugin', version: '1.0']")
+        failure.assertHasCause("throwing plugin")
+        failure.assertHasLineNumber(2)
+    }
+
+    def expectPluginQuery() {
+        service.expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION) {
+            legacy = true
+        }
+    }
+
+    MavenHttpModule publishPlugin(String impl) {
+        expectPluginQuery()
+
+        def module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+
+        pluginBuilder.addPlugin(impl, PLUGIN_ID)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+
+        module
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseClassLoadingIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseClassLoadingIntegrationSpec.groovy
new file mode 100644
index 0000000..8cc6806
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseClassLoadingIntegrationSpec.groovy
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.api.Project
+import org.gradle.api.specs.AndSpec
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.plugin.use.resolve.service.PluginResolutionServiceTestServer
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.junit.Rule
+
+class PluginUseClassLoadingIntegrationSpec extends AbstractIntegrationSpec {
+
+    public static final String PLUGIN_ID = "org.myplugin"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT = "plugin"
+    public static final String USE = "plugins { id '$PLUGIN_ID' version '$VERSION' }"
+
+    def pluginBuilder = new PluginBuilder(file(ARTIFACT))
+
+    @Rule
+    PluginResolutionServiceTestServer resolutionService = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def setup() {
+        executer.requireGradleHome() // need accurate classloading
+        executer.requireOwnGradleUserHomeDir()
+        resolutionService.start()
+    }
+
+    def "plugin is available via plugins container"() {
+        publishPlugin()
+
+        buildScript """
+            $USE
+
+            task verify << {
+                def foundByClass = false
+                plugins.withType(pluginClass) { foundByClass = true }
+                def foundById = false
+                plugins.withId("$PLUGIN_ID") { foundById = true }
+
+                assert foundByClass
+                assert foundById
+            }
+        """
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "plugin class isn't visible to build script"() {
+        publishPlugin()
+
+        buildScript """
+            $USE
+
+            task verify << {
+                try {
+                    getClass().getClassLoader().loadClass("org.gradle.test.TestPlugin")
+                    throw new AssertionError("plugin class *is* visible to build script")
+                } catch (ClassNotFoundException expected) {}
+            }
+        """
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "plugin can access Gradle API classes"() {
+        publishPlugin """
+            assert project instanceof ${Project.name}; new ${AndSpec.name}()
+            project.task("verify")
+        """
+
+        buildScript USE
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "plugin cannot access core Gradle plugin classes"() {
+        publishPlugin("""
+            try {
+                getClass().getClassLoader().loadClass('org.gradle.api.plugins.JavaPlugin')
+                assert false : "should have failed to load java plugin"
+            } catch (ClassNotFoundException ignore) {
+
+            }
+
+            project.task("verify")
+        """)
+
+        buildScript USE
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "plugin cannot access Gradle implementation classes"() {
+        publishPlugin("""
+            def implClassName = 'com.google.common.collect.Multimap'
+            project.getClass().getClassLoader().loadClass(implClassName)
+
+            try {
+                getClass().getClassLoader().loadClass(implClassName)
+                assert false : "should have failed to load gradle implementation class: \$implClassName"
+            } catch (ClassNotFoundException ignore) {
+
+            }
+
+            project.task("verify")
+        """)
+
+        buildScript USE
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "plugin classes are reused if possible"() {
+        given:
+        publishPlugin()
+        settingsFile << """
+            include "p1"
+            include "p2"
+        """
+
+        when:
+        file("p1/build.gradle") << USE
+        file("p2/build.gradle") << USE
+
+        buildScript """
+            evaluationDependsOnChildren()
+            task verify <<  {
+                project(":p1").pluginClass.is(project(":p2").pluginClass)
+            }
+        """
+
+        then:
+        succeeds "verify"
+    }
+
+    void publishPlugin() {
+        publishPlugin("project.ext.pluginApplied = true; project.ext.pluginClass = getClass()")
+    }
+
+    void publishPlugin(String impl) {
+        resolutionService.expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION)
+        def module = resolutionService.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+
+        pluginBuilder.addPlugin(impl, PLUGIN_ID)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy
new file mode 100644
index 0000000..7ec36e5
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.GradleVersion
+import spock.lang.Unroll
+
+import static org.gradle.plugin.internal.PluginId.*
+import static org.gradle.plugin.use.internal.PluginUseScriptBlockMetadataExtractor.*
+import static org.hamcrest.Matchers.containsString
+
+class PluginUseDslIntegrationSpec extends AbstractIntegrationSpec {
+
+    def "can use plugins block in project build scripts"() {
+        when:
+        buildScript """
+          plugins {
+            id "java"
+            id "noop" version "1.0"
+          }
+        """
+
+        then:
+        succeeds "help"
+    }
+
+    def "buildscript blocks are allowed before plugin statements"() {
+        when:
+        buildScript """
+            buildscript {}
+            plugins {}
+        """
+
+        then:
+        succeeds "tasks"
+    }
+
+    def "buildscript blocks are not allowed after plugin blocks"() {
+        when:
+        buildScript """
+            plugins {}
+            buildscript {}
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasLineNumber 3
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString("all buildscript {} blocks must appear before any plugins {} blocks"))
+        includesLinkToUserguide()
+    }
+
+    void includesLinkToUserguide() {
+        assert failure.assertThatCause(containsString("http://gradle.org/docs/${GradleVersion.current().getVersion()}/userguide/plugins.html#sec:plugins_block"))
+    }
+
+    def "build logic cannot precede plugins block"() {
+        when:
+        buildScript """
+            someThing()
+            plugins {}
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasLineNumber 3
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString("only buildscript {} and other plugins {} script blocks are allowed before plugins {} blocks, no other statements are allowed"))
+        includesLinkToUserguide()
+    }
+
+    def "build logic cannot precede any plugins block"() {
+        when:
+        buildScript """
+            plugins {}
+            someThing()
+            plugins {}
+        """
+
+        then:
+        fails "tasks"
+
+        and:
+        failure.assertHasLineNumber 4
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString("only buildscript {} and other plugins {} script blocks are allowed before plugins {} blocks, no other statements are allowed"))
+        includesLinkToUserguide()
+    }
+
+    def "settings scripts cannot plugin blocks"() {
+        when:
+        settingsFile << "plugins {}"
+
+        then:
+        fails "help"
+
+        failure.assertHasLineNumber 1
+        failure.assertHasFileName("Settings file '$settingsFile.absolutePath'")
+        failure.assertThatCause(containsString("Only Project build scripts can contain plugins {} blocks"))
+        includesLinkToUserguide()
+    }
+
+    def "init scripts cannot have plugin blocks"() {
+        def initScript = file("init.gradle")
+
+        when:
+        initScript << "plugins {}"
+
+        then:
+        args "-I", initScript.absolutePath
+        fails "help"
+
+        failure.assertHasLineNumber 1
+        failure.assertHasFileName("Initialization script '$initScript.absolutePath'")
+        failure.assertThatCause(containsString("Only Project build scripts can contain plugins {} blocks"))
+        includesLinkToUserguide()
+    }
+
+    def "script plugins cannot have plugin blocks"() {
+        def scriptPlugin = file("plugin.gradle")
+
+        when:
+        scriptPlugin << "plugins {}"
+        buildScript "apply from: 'plugin.gradle'"
+
+        then:
+        fails "help"
+
+        failure.assertHasLineNumber 1
+        failure.assertHasFileName("Script '$scriptPlugin.absolutePath'")
+        failure.assertThatCause(containsString("Only Project build scripts can contain plugins {} blocks"))
+        includesLinkToUserguide()
+    }
+
+    def "script plugins applied to arbitrary objects cannot have plugin blocks"() {
+        def scriptPlugin = file("plugin.gradle")
+
+        when:
+        scriptPlugin << "plugins {}"
+        buildScript "task foo; apply from: 'plugin.gradle', to: foo"
+
+        then:
+        fails "help"
+
+        failure.assertHasLineNumber 1
+        failure.assertHasFileName("Script '$scriptPlugin.absolutePath'")
+        failure.assertThatCause(containsString("Only Project build scripts can contain plugins {} blocks"))
+        includesLinkToUserguide()
+    }
+
+    @Unroll
+    def "illegal syntax in plugins block - #code"() {
+        when:
+        buildScript("""plugins {\n$code\n}""")
+
+        then:
+        fails "help"
+        failure.assertHasLineNumber lineNumber
+        failure.assertHasFileName("Build file '${buildFile}'")
+        failure.assertThatCause(containsString(msg))
+        includesLinkToUserguide()
+
+        where:
+        lineNumber | code                                   | msg
+        2          | "a"                                    | BASE_MESSAGE
+        2          | "def a = null"                         | BASE_MESSAGE
+        2          | "def a = id('foo')"                    | BASE_MESSAGE
+        2          | "delegate.id('a')"                     | BASE_MESSAGE
+        2          | "id()"                                 | INVALID_ARGUMENT_LIST
+        2          | "id(1)"                                | INVALID_ARGUMENT_LIST
+        2          | "id(System.getProperty('foo'))"        | INVALID_ARGUMENT_LIST
+        2          | "id('a' + 'b')"                        | INVALID_ARGUMENT_LIST
+        2          | "id(\"\${'foo'}\")"                    | INVALID_ARGUMENT_LIST
+        2          | "version('foo')"                       | BASE_MESSAGE
+        2          | "id('foo').version(1)"                 | INVALID_ARGUMENT_LIST
+        2          | "id 'foo' version 1"                   | INVALID_ARGUMENT_LIST
+        2          | "id 'foo' bah '1'"                     | VERSION_MESSAGE
+        2          | "foo 'foo' version '1'"                | BASE_MESSAGE
+        3          | "id('foo')\nfoo 'bar'"                 | BASE_MESSAGE
+        2          | "if (true) id 'foo'"                   | BASE_MESSAGE
+        2          | "id 'foo';version 'bar'"               | BASE_MESSAGE
+        2          | "id('foo').\"\${'version'}\" 'bar'"    | BASE_MESSAGE
+        2          | "id ' '"                               | invalidPluginIdCharMessage(' ' as char)
+        2          | "id '\$'"                              | invalidPluginIdCharMessage('$' as char)
+        2          | "id ''"                                | INVALID_ARGUMENT_LIST
+        2          | "id 'foo' version ''"                  | INVALID_ARGUMENT_LIST
+        2          | "id null"                              | INVALID_ARGUMENT_LIST
+        2          | "id 'foo' version null"                | INVALID_ARGUMENT_LIST
+        2          | "id '.foo'"                            | ID_SEPARATOR_ON_START_OR_END
+        2          | "id 'foo.'"                            | ID_SEPARATOR_ON_START_OR_END
+        2          | "id '.'"                               | ID_SEPARATOR_ON_START_OR_END
+        2          | "id 'foo..bar'"                        | DOUBLE_SEPARATOR
+        2          | "file('foo')" /* script api */         | BASE_MESSAGE
+        2          | "getVersion()" /* script target api */ | BASE_MESSAGE
+    }
+
+    @Unroll
+    def "allowed syntax in plugins block - #code"() {
+        given:
+        when:
+        buildScript("""plugins {\n$code\n}""")
+
+        then:
+        succeeds "help"
+
+        where:
+        code << [
+                "id('noop')",
+                "id 'noop'",
+                "id('noop').version('bar')",
+                "id 'noop' version 'bar'",
+                "id('noop').\nversion 'bar'",
+                "id('java');id('noop')",
+                "id('java')\nid('noop')",
+                "id('noop').version('bar');id('java')",
+                "id('noop').version('bar')\nid('java')",
+        ]
+    }
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PostPluginResolutionFailuresIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PostPluginResolutionFailuresIntegrationSpec.groovy
new file mode 100644
index 0000000..7a51ab6
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PostPluginResolutionFailuresIntegrationSpec.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.plugin.use.resolve.service.PluginResolutionServiceTestServer
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.junit.Rule
+
+import static org.hamcrest.Matchers.startsWith
+
+class PostPluginResolutionFailuresIntegrationSpec extends AbstractIntegrationSpec {
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Rule
+    PluginResolutionServiceTestServer portal = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        portal.start()
+    }
+
+    def "error finding plugin by id"() {
+        portal.expectPluginQuery("org.my.myplugin", "1.0", "my", "plugin", "1.0")
+        publishPlugin("otherid", "my", "plugin", "1.0")
+
+        buildScript applyPlugin("org.my.myplugin", "1.0")
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(startsWith("Could not apply requested plugin [id: 'org.my.myplugin', version: '1.0'] as it does not provide a plugin with id 'org.my.myplugin'"))
+        failure.assertHasLineNumber(3)
+    }
+
+    def "error loading plugin"() {
+        portal.expectPluginQuery("org.my.myplugin", "1.0", "my", "plugin", "1.0")
+        publishUnloadablePlugin("org.my.myplugin", "my", "plugin", "1.0")
+
+        buildScript applyPlugin("org.my.myplugin", "1.0")
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(startsWith("An exception occurred applying plugin request [id: 'org.my.myplugin', version: '1.0']"))
+        failure.assertHasLineNumber(3)
+        failure.assertHasCause("Could not create plugin of type 'TestPlugin'.")
+    }
+
+    def "error applying plugin"() {
+        portal.expectPluginQuery("org.my.myplugin", "1.0", "my", "plugin", "1.0")
+        publishFailingPlugin("org.my.myplugin", "my", "plugin", "1.0")
+
+        buildScript applyPlugin("org.my.myplugin", "1.0")
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(startsWith("An exception occurred applying plugin request [id: 'org.my.myplugin', version: '1.0']"))
+        failure.assertHasLineNumber(3)
+        failure.assertHasCause("throwing plugin")
+    }
+
+    private void publishPlugin(String pluginId, String group, String artifact, String version) {
+        def module = portal.m2repo.module(group, artifact, version) // don't know why tests are failing if this module is publish()'ed
+        module.allowAll()
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", pluginId)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    private void publishUnloadablePlugin(String pluginId, String group, String artifact, String version) {
+        def module = portal.m2repo.module(group, artifact, version)
+        module.allowAll()
+        pluginBuilder.addUnloadablePlugin(pluginId)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    private void publishFailingPlugin(String pluginId, String group, String artifact, String version) {
+        def module = portal.m2repo.module(group, artifact, version)
+        module.allowAll()
+        pluginBuilder.addPlugin("throw new Exception('throwing plugin')", pluginId)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    private static String applyPlugin(String id, String version) {
+        """
+            plugins {
+                id "$id" version "$version"
+            }
+
+            task verify // no-op, but gives us a task to execute
+        """
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/RuleSourcePluginUseIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/RuleSourcePluginUseIntegrationSpec.groovy
new file mode 100644
index 0000000..155d927
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/RuleSourcePluginUseIntegrationSpec.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.plugin.use.resolve.service.PluginResolutionServiceTestServer
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.junit.Rule
+
+class RuleSourcePluginUseIntegrationSpec extends AbstractIntegrationSpec {
+
+    public static final String PLUGIN_ID = "org.myplugin"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT = "plugin"
+    def pluginBuilder = new PluginBuilder(file(ARTIFACT))
+
+    @Rule
+    PluginResolutionServiceTestServer resolutionService = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        resolutionService.start()
+    }
+
+    def "can apply a rule source only plugin via plugins container"() {
+        publishRuleSourcePlugin()
+
+        buildScript """
+            plugins { id '$PLUGIN_ID' version '$VERSION' }
+        """
+
+        expect:
+        succeeds("fromModelRule")
+        output.contains("Model rule provided task executed")
+    }
+
+    void publishRuleSourcePlugin() {
+        resolutionService.expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION)
+        def module = resolutionService.m2repo.module(GROUP, ARTIFACT, VERSION)
+        module.allowAll()
+
+        pluginBuilder.addRuleSource(PLUGIN_ID)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingCrossVersionIntegrationTest.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingCrossVersionIntegrationTest.groovy
new file mode 100644
index 0000000..45adaa2
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingCrossVersionIntegrationTest.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.plugin.use.resolve.service
+
+import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.junit.Rule
+
+ at TargetVersions(["2.1+"])
+class PluginResolutionCachingCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
+
+    public static final String PLUGIN_ID = "org.my.myplugin"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT = "plugin"
+
+    @Rule
+    PluginResolutionServiceTestServer service = new PluginResolutionServiceTestServer(version(current), mavenRepo)
+    private MavenHttpModule module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+
+    void setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "cached resolution by previous version is not used by this version"() {
+        when:
+        def gradleUserHome = file("gradle-home")
+        def currentExecuter = service.configure(version(current)).withGradleUserHomeDir(gradleUserHome)
+        def previousExecuter = service.configure(version(previous)).withGradleUserHomeDir(gradleUserHome)
+
+        def pluginBuilder = new PluginBuilder(file("plugin"))
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", PLUGIN_ID)
+        pluginBuilder.publishTo(currentExecuter, module.artifactFile)
+        service.start()
+        file("build.gradle") << """
+            plugins { id '$PLUGIN_ID' version '$VERSION' }
+            task pluginApplied << {
+                assert project.pluginApplied
+            }
+        """
+
+        service.expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION)
+        service.forVersion(previousExecuter.distribution.version) {
+            expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION)
+        }
+
+        module.allowAll()
+
+        then:
+        previousExecuter.withTasks("pluginApplied").run()
+        currentExecuter.withTasks("pluginApplied").run()
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingIntegrationTest.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingIntegrationTest.groovy
new file mode 100644
index 0000000..ac89d0a
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionCachingIntegrationTest.groovy
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.junit.Rule
+
+import static org.hamcrest.Matchers.startsWith
+
+class PluginResolutionCachingIntegrationTest extends AbstractIntegrationSpec {
+
+    public static final String PLUGIN_ID = "org.my.myplugin"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT = "plugin"
+
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Rule
+    PluginResolutionServiceTestServer service = new PluginResolutionServiceTestServer(executer, mavenRepo)
+    private MavenHttpModule module = service.m2repo.module(GROUP, ARTIFACT, VERSION)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        publishPlugin()
+        service.start()
+        buildScript """
+            plugins { id '$PLUGIN_ID' version '$VERSION' }
+            task pluginApplied << {
+                assert project.pluginApplied
+            }
+        """
+    }
+
+    def "successful plugin resolution is cached"() {
+        expect:
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        build()
+    }
+
+    def "--refresh-dependencies invalidates cache"() {
+        expect:
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        args "--refresh-dependencies"
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        args() // clear --refresh-dependencies
+        build()
+    }
+
+    def "can use --refresh-dependencies on first run"() {
+        expect:
+        args "--refresh-dependencies"
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        build()
+    }
+
+    def "not found plugin is not cached"() {
+        expect:
+        pluginQueryNotFound()
+        failPluginNotFound()
+
+        reset()
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        build()
+    }
+
+    def "error response is not cached"() {
+        expect:
+        pluginQueryError()
+        failError()
+
+        reset()
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        build()
+    }
+
+    def "unexpected response is not cached"() {
+        expect:
+        pluginQueryUnexpectedResponse()
+        failError()
+
+        reset()
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        build()
+    }
+
+    def "--offline can be used if response is cached"() {
+        expect:
+        pluginQuery()
+        moduleResolution()
+        build()
+
+        reset()
+        args "--offline"
+        build()
+    }
+
+    void reset() {
+        service.http.resetExpectations()
+    }
+
+    void build() {
+        run "pluginApplied"
+    }
+
+    void failPluginNotFound() {
+        fails "tasks"
+        failure.assertThatDescription(startsWith("Plugin [id: 'org.my.myplugin', version: '1.0'] was not found"))
+    }
+
+    void failError() {
+        fails "tasks"
+        failure.assertHasDescription("Error resolving plugin [id: 'org.my.myplugin', version: '1.0']")
+    }
+
+    void publishPlugin() {
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", PLUGIN_ID)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    void moduleResolution() {
+        module.allowAll()
+    }
+
+    void pluginQuery() {
+        service.expectPluginQuery(PLUGIN_ID, VERSION, GROUP, ARTIFACT, VERSION)
+    }
+
+    void pluginQueryNotFound() {
+        service.expectNotFound(PLUGIN_ID, VERSION)
+    }
+
+    void pluginQueryError() {
+        service.expectQueryAndReturnError(PLUGIN_ID, VERSION, 500) {
+            errorCode = "INTERNAL_SERVER_ERROR"
+            message = "Something went wrong"
+        }
+    }
+
+    void pluginQueryUnexpectedResponse() {
+        service.expectPluginQuery(PLUGIN_ID, VERSION) {
+            writer.withWriter {
+                it << "foo"
+            }
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionDeprecatedClientIntegrationTest.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionDeprecatedClientIntegrationTest.groovy
new file mode 100644
index 0000000..0417e15
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionDeprecatedClientIntegrationTest.groovy
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.plugin.use.resolve.service
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.hash.HashUtil
+import org.gradle.plugin.use.internal.PluginUsePluginServiceRegistry
+import org.gradle.plugin.use.resolve.service.internal.PersistentCachingPluginResolutionServiceClient
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.gradle.test.fixtures.server.http.MavenHttpModule
+import org.junit.Rule
+
+import static org.gradle.util.Matchers.containsText
+
+class PluginResolutionDeprecatedClientIntegrationTest extends AbstractIntegrationSpec {
+
+    public static final String PLUGIN_ID_1 = "org.my.myplugin_1"
+    public static final String PLUGIN_ID_2 = "org.my.myplugin_2"
+    public static final String VERSION = "1.0"
+    public static final String GROUP = "my"
+    public static final String ARTIFACT_1 = "plugin_1"
+    public static final String ARTIFACT_2 = "plugin_2"
+
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Rule
+    PluginResolutionServiceTestServer service = new PluginResolutionServiceTestServer(executer, mavenRepo)
+    private MavenHttpModule module1 = service.m2repo.module(GROUP, ARTIFACT_1, VERSION)
+    private MavenHttpModule module2 = service.m2repo.module(GROUP, ARTIFACT_2, VERSION)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        executer.beforeExecute {
+            it.withDeprecationChecksDisabled().withStackTraceChecksDisabled()
+            moduleResolution()
+        }
+
+        publishPlugin()
+        service.start()
+    }
+
+    def "deprecation message is displayed if protocol is deprecated"() {
+        given:
+        deprecateClient("FOO")
+
+        expect:
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        reset()
+        build()
+        containsDeprecationMessage("FOO")
+
+        // test that the right scoped cache was created.
+        // would be nice to have a less fragile way to do this.
+        new File(executer.gradleUserHomeDir, "caches/${executer.distribution.version.version}/${PluginUsePluginServiceRegistry.CACHE_NAME}/${PersistentCachingPluginResolutionServiceClient.CLIENT_STATUS_CACHE_NAME}.bin".toString()).exists()
+    }
+
+    def "deprecation message is output once per build"() {
+        expect:
+        deprecateClient("FOO")
+        useBothPlugins()
+        pluginQuery1()
+        pluginQuery2()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        reset()
+        build()
+        containsDeprecationMessage("FOO")
+    }
+
+    def "changed deprecation message is detected across builds"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        reset()
+        deprecateClient("BAR")
+        usePlugin2() // use a different plugin, because the response for the previous will be cached
+        pluginQuery2()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("BAR")
+    }
+
+    def "deprecation message disappears if revoked server side"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        reset()
+        deprecateClient(null)
+        usePlugin2() // use a different plugin, because the response for the previous will be cached
+        pluginQuery2()
+        build()
+        containsNoDeprecationMessage()
+    }
+
+    def "deprecation displayed on error"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQueryError1()
+        service.expectStatusQuery()
+        failError()
+        containsDeprecationMessage("FOO")
+
+        usePlugin1()
+        pluginQueryError1()
+        failError()
+        containsDeprecationMessage("FOO")
+    }
+
+    def "deprecation displayed on not found"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQueryNotFound1()
+        service.expectStatusQuery()
+        failPluginNotFound()
+        containsDeprecationMessage("FOO")
+
+        usePlugin1()
+        pluginQueryNotFound1()
+        failPluginNotFound()
+        containsDeprecationMessage("FOO")
+    }
+
+    def "deprecation message is checked when running with refresh dependencies"() {
+        expect:
+        deprecateClient("FOO")
+        service.statusChecksum("a")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        deprecateClient("BAR")
+        service.statusChecksum("a")
+        build()
+        containsDeprecationMessage("FOO")
+
+        args "--refresh-dependencies"
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("BAR")
+
+        build()
+        containsDeprecationMessage("BAR")
+    }
+
+    def "deprecation message is displayed when running with offline"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+
+        args "--offline"
+        build()
+        containsDeprecationMessage("FOO")
+    }
+
+    def "error response from status does not fail build"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQuery404()
+        build()
+        output.contains("Received error response fetching client status from")
+
+        service.expectStatusQuery404()
+        build()
+        output.contains("Received error response fetching client status from")
+    }
+
+    def "exception response from status does not fail build"() {
+        expect:
+        deprecateClient("FOO")
+        usePlugin1()
+        pluginQuery1()
+        service.expectStatusQueryOutOfProtocol()
+        build()
+        output.contains("Exception thrown fetching client status")
+
+        service.expectStatusQueryOutOfProtocol()
+        build()
+        output.contains("Exception thrown fetching client status")
+
+        // Test that if the issue gets resolved, everything works as it should
+        service.expectStatusQuery()
+        build()
+        containsDeprecationMessage("FOO")
+    }
+
+    void usePlugin1() {
+        buildScript """
+            plugins {
+                id '$PLUGIN_ID_1' version '$VERSION'
+            }
+        """
+    }
+
+    void usePlugin2() {
+        buildScript """
+            plugins {
+                id '$PLUGIN_ID_2' version '$VERSION'
+            }
+        """
+    }
+
+    void useBothPlugins() {
+        buildScript """
+            plugins {
+                id '$PLUGIN_ID_1' version '$VERSION'
+                id '$PLUGIN_ID_2' version '$VERSION'
+            }
+        """
+
+    }
+
+    void reset() {
+        service.http.resetExpectations()
+    }
+
+    void build() {
+        run "tasks"
+    }
+
+    void containsDeprecationMessage(String message) {
+        assert effectiveOutput.count("Plugin resolution service client status service ${service.apiAddress}/${executer.distribution.version.version} reported that this client has been deprecated: $message") == 1
+    }
+
+    void containsNoDeprecationMessage() {
+        effectiveOutput.count("this client has been deprecated") == 0
+    }
+
+    private String getEffectiveOutput() {
+        (failure == null ? result : failure).output
+    }
+
+    void failPluginNotFound() {
+        fails "tasks"
+        failure.assertThatDescription(containsText("Plugin \\[id: '.+', version: '1.0'\\] was not found"))
+    }
+
+    void fail() {
+        fails "tasks"
+    }
+
+    void failError() {
+        fails "tasks"
+        failure.assertThatDescription(containsText("Error resolving plugin"))
+    }
+
+    void publishPlugin() {
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", PLUGIN_ID_1, "TestPlugin1")
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", PLUGIN_ID_2, "TestPlugin2")
+        pluginBuilder.publishTo(executer, module1.artifactFile)
+        module1.artifactFile.copyTo(module2.artifactFile)
+    }
+
+    private void moduleResolution() {
+        module1.allowAll()
+        module2.allowAll()
+    }
+
+    void pluginQuery1() {
+        service.expectPluginQuery(PLUGIN_ID_1, VERSION, GROUP, ARTIFACT_1, VERSION)
+    }
+
+    void pluginQuery2() {
+        service.expectPluginQuery(PLUGIN_ID_2, VERSION, GROUP, ARTIFACT_2, VERSION)
+    }
+
+
+    void deprecateClient(String msg) {
+        service.deprecateClient(msg)
+        service.statusChecksum(msg == null ? null : HashUtil.createCompactMD5(msg))
+    }
+
+    void pluginQueryNotFound1() {
+        service.expectNotFound(PLUGIN_ID_1, VERSION)
+    }
+
+    void pluginQueryNotFound2() {
+        service.expectNotFound(PLUGIN_ID_1, VERSION)
+    }
+
+    void pluginQueryError1() {
+        service.expectQueryAndReturnError(PLUGIN_ID_1, VERSION, 500) {
+            errorCode = "INTERNAL_SERVER_ERROR"
+            message = "Something went wrong"
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceCommsIntegrationTest.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceCommsIntegrationTest.groovy
new file mode 100644
index 0000000..ccdfb4f
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceCommsIntegrationTest.groovy
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.ExecutionFailure
+import org.gradle.plugin.use.resolve.service.internal.ErrorResponse
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.util.GradleVersion
+import org.hamcrest.Matchers
+import org.junit.Rule
+import spock.lang.Unroll
+
+import static org.gradle.util.Matchers.containsText
+
+/**
+ * Tests the communication aspects of working with the plugin resolution service.
+ */
+public class PluginResolutionServiceCommsIntegrationTest extends AbstractIntegrationSpec {
+    public static final String PLUGIN_ID = "org.my.myplugin"
+    public static final String PLUGIN_VERSION = "1.0"
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Rule
+    PluginResolutionServiceTestServer portal = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        portal.start()
+    }
+
+    @Unroll
+    def "response that is not an expected service response is fatal to plugin resolution - status = #statusCode"() {
+        def responseBody = "<html><bogus/></html>"
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            status = statusCode
+            contentType = "text/html"
+            characterEncoding = "UTF-8"
+            writer.withWriter {
+                it << responseBody
+            }
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        outOfProtocolCause("content type is 'text/html; charset=utf-8', expected 'application/json'")
+
+        where:
+        statusCode << [200, 404, 500]
+    }
+
+    def "404 plugin resolution service response is not fatal to plugin resolution, but indicates plugin is not available in service"() {
+        portal.expectNotFound(PLUGIN_ID, PLUGIN_VERSION)
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(Matchers.startsWith("Plugin [id: 'org.my.myplugin', version: '1.0'] was not found in any of the following sources:"))
+    }
+
+    def "404 resolution that indicates plugin is known but not by that version produces indicative message"() {
+        portal.expectQueryAndReturnError(PLUGIN_ID, PLUGIN_VERSION, 404) {
+            errorCode = ErrorResponse.Code.UNKNOWN_PLUGIN_VERSION
+            message = "portal message: \u03b1"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(Matchers.startsWith("Plugin [id: 'org.my.myplugin', version: '1.0'] was not found in any of the following sources:"))
+        failure.assertThatDescription(Matchers.containsString("portal message: \u03b1"))
+    }
+
+    def "404 resolution that indicates plugin is unknown produces indicative message"() {
+        portal.expectQueryAndReturnError(PLUGIN_ID, PLUGIN_VERSION, 404) {
+            errorCode = ErrorResponse.Code.UNKNOWN_PLUGIN
+            message = "portal message: \u03b1"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        failure.assertThatDescription(Matchers.startsWith("Plugin [id: 'org.my.myplugin', version: '1.0'] was not found in any of the following sources:"))
+        failure.assertThatDescription(Matchers.containsString("portal message: \u03b1"))
+    }
+
+    def "failed module resolution fails plugin resolution"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION, "my", "plugin", PLUGIN_VERSION)
+
+        buildScript applyAndVerify()
+        portal.m2repo.module("my", "plugin", PLUGIN_VERSION).missing()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Failed to resolve all plugin dependencies from " + portal.m2repo.uri)
+        failure.assertHasCause("Could not find my:plugin:1.0.")
+    }
+
+    def "portal JSON response with unknown implementation type fails plugin resolution"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION, "foo", "foo", "foo") {
+            implementationType = "SUPER_GREAT"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        outOfProtocolCause("invalid plugin metadata - unsupported implementation type 'SUPER_GREAT'")
+    }
+
+    void outOfProtocolCause(String cause) {
+        failure.assertHasCause("The response from ${portal.pluginUrl(PLUGIN_ID, PLUGIN_VERSION)} was not a valid response from a Gradle Plugin Resolution Service: $cause")
+    }
+
+    def "portal JSON response with missing repo fails plugin resolution"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION, "foo", "bar", PLUGIN_VERSION) {
+            implementation.repo = null
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        failure.assertHasDescription("Error resolving plugin [id: 'org.my.myplugin', version: '1.0']")
+        outOfProtocolCause("invalid plugin metadata - no module repository specified")
+    }
+
+    @Unroll
+    def "portal JSON response with invalid JSON syntax fails plugin resolution - #statusCode"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            contentType = "application/json"
+            outputStream.withStream { it << "[}".getBytes("utf8") }
+            status = statusCode
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        outOfProtocolCause("could not parse response JSON")
+
+        where:
+        statusCode << [200, 500, 410]
+    }
+
+    def "extra information in portal JSON response is tolerated (and neglected)"() {
+        publishPlugin(PLUGIN_ID, "my", "plugin", PLUGIN_VERSION)
+
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            contentType = "application/json"
+            outputStream.withStream {
+                it << """
+                {
+                    "id": "org.my.myplugin",
+                    "version": "1.0",
+                    "implementation": {
+                        "gav": "my:plugin:1.0",
+                        "repo": "$portal.m2repo.uri",
+                        "extra1": "info1"
+                    },
+                    "implementationType": "M2_JAR",
+                    "extra2": "info2"
+                }
+                """.getBytes("utf8")
+            }
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "portal redirects are being followed"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            sendRedirect("/${portal.API_PATH}/${GradleVersion.current().version}/plugin/use/org.my.otherplugin/2.0")
+        }
+        portal.expectQueryAndReturnError("org.my.otherplugin", "2.0", 500) {
+            errorCode = "REDIRECTED"
+            message = "redirected"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        failure.assertThatCause(Matchers.startsWith("Plugin resolution service returned HTTP 500 with message 'redirected'"))
+    }
+
+    def "error message is embedded in user error message"() {
+        portal.expectQueryAndReturnError(PLUGIN_ID, PLUGIN_VERSION, 500) {
+            errorCode = "INTERNAL_SERVER_ERROR"
+            message = "Bintray communication failure"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Plugin resolution service returned HTTP 500 with message 'Bintray communication failure' (url: ${portal.pluginUrl(PLUGIN_ID, PLUGIN_VERSION)})")
+    }
+
+    @Unroll
+    def "incompatible error message schema - status #errorStatusCode"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            status = errorStatusCode
+            contentType = "application/json"
+            outputStream.withStream {
+                it << """
+                {
+                    "foo": "bar"
+                }
+                """.getBytes("utf8")
+            }
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        outOfProtocolCause("invalid error response - no error code specified")
+
+        where:
+        errorStatusCode << [500, 410]
+    }
+
+    @Unroll
+    def "error message schema is relaxed- status #errorStatusCode"() {
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION) {
+            status = errorStatusCode
+            contentType = "application/json"
+            outputStream.withStream {
+                it << """
+                {
+                    "errorCode": "foo",
+                    "message": "bar",
+                    "extra": "boom!"
+
+                }
+                """.getBytes("utf8")
+            }
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Plugin resolution service returned HTTP $errorStatusCode with message 'bar' (url: ${portal.pluginUrl(PLUGIN_ID, PLUGIN_VERSION)})")
+
+        where:
+        errorStatusCode << [500, 410]
+    }
+
+    def "error message for 4xx is embedded in user error message"() {
+        portal.expectQueryAndReturnError(PLUGIN_ID, PLUGIN_VERSION, 405) {
+            errorCode = "SOME_STRANGE_ERROR"
+            message = "Some strange error message"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Plugin resolution service returned HTTP 405 with message 'Some strange error message' (url: ${portal.pluginUrl(PLUGIN_ID, PLUGIN_VERSION)})")
+    }
+
+    def "response can contain utf8"() {
+        portal.expectQueryAndReturnError(PLUGIN_ID, PLUGIN_VERSION, 500) {
+            errorCode = "INTERNAL_SERVER_ERROR"
+            message = "\u00E9"
+        }
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Plugin resolution service returned HTTP 500 with message 'é' (url: ${portal.pluginUrl(PLUGIN_ID, PLUGIN_VERSION)})")
+    }
+
+    def ExecutionFailure errorResolvingPlugin() {
+        failure.assertHasDescription("Error resolving plugin [id: 'org.my.myplugin', version: '1.0']")
+    }
+
+    def "non contactable resolution service produces error"() {
+        portal.injectUrlOverride(executer) // have to do this, because only happens by default if test server is running
+        portal.stop()
+
+        buildScript applyAndVerify()
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertThatCause(containsText("Could not GET 'http://localhost:\\d+/.+?/plugin/use/org.my.myplugin/1\\.0'"))
+        failure.assertThatCause(containsText("Connection to http://localhost:\\d+ refused"))
+    }
+
+    def "non contactable dependency repository produces error"() {
+        given:
+        // Get an address that isn't used
+        def httpServer = new HttpServer()
+        httpServer.start()
+        def address = httpServer.address
+        httpServer.stop()
+
+        buildScript applyAndVerify()
+
+        portal.expectPluginQuery(PLUGIN_ID, PLUGIN_VERSION, "foo", "bar", PLUGIN_VERSION) {
+            implementation.repo = address
+        }
+
+        expect:
+        fails("verify")
+        errorResolvingPlugin()
+        failure.assertHasCause("Failed to resolve all plugin dependencies from $address")
+        failure.assertThatCause(containsText("Connection to $address refused"))
+    }
+
+    private void publishPlugin(String pluginId, String group, String artifact, String version) {
+        def module = portal.m2repo.module(group, artifact, version) // don't know why tests are failing if this module is publish()'ed
+        module.allowAll()
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", pluginId)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    private static String applyAndVerify(String id = PLUGIN_ID, String version = PLUGIN_VERSION) {
+        """
+            plugins {
+                id "$id" version "$version"
+            }
+
+            task verify << {
+                assert pluginApplied
+            }
+        """
+    }
+
+}
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceIntegrationSpec.groovy
new file mode 100644
index 0000000..cc9cbff
--- /dev/null
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceIntegrationSpec.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.plugin.PluginBuilder
+import org.hamcrest.Matchers
+import org.junit.Rule
+
+class PluginResolutionServiceIntegrationSpec extends AbstractIntegrationSpec {
+
+    def pluginBuilder = new PluginBuilder(file("plugin"))
+
+    @Rule
+    PluginResolutionServiceTestServer portal = new PluginResolutionServiceTestServer(executer, mavenRepo)
+
+    def setup() {
+        executer.requireOwnGradleUserHomeDir()
+        portal.start()
+    }
+
+    def "plugin declared in plugins {} block gets resolved and applied"() {
+        portal.expectPluginQuery("org.my.myplugin", "1.0", "my", "plugin", "1.0")
+        publishPlugin("org.my.myplugin", "my", "plugin", "1.0")
+
+        buildScript applyAndVerify("org.my.myplugin", "1.0")
+
+        expect:
+        succeeds("verify")
+    }
+
+    def "resolution fails if Gradle is in offline mode"() {
+        buildScript applyAndVerify("org.my.myplugin", "1.0")
+        args("--offline")
+
+        expect:
+        fails("verify")
+        failure.assertHasDescription("Error resolving plugin [id: 'org.my.myplugin', version: '1.0']")
+        failure.assertHasCause("Plugin cannot be resolved from $portal.apiAddress because Gradle is running in offline mode")
+    }
+
+    def "cannot resolve plugin with snapshot version"() {
+        buildScript applyAndVerify("org.my.myplugin", "1.0-SNAPSHOT")
+
+        expect:
+        fails("verify")
+        pluginNotFound("1.0-SNAPSHOT")
+        resolutionServiceDetail("snapshot plugin versions are not supported")
+    }
+
+    def "cannot resolve plugin with dynamic version"() {
+        buildScript applyAndVerify("org.my.myplugin", pluginVersion)
+
+        expect:
+        fails("verify")
+        pluginNotFound(pluginVersion)
+        resolutionServiceDetail("dynamic plugin versions are not supported")
+
+        where:
+        pluginVersion << ["[1.0,2.0)", "1.+", "latest.release"]
+    }
+
+    private void publishPlugin(String pluginId, String group, String artifact, String version) {
+        def module = portal.m2repo.module(group, artifact, version) // don't know why tests are failing if this module is publish()'ed
+        module.allowAll()
+        pluginBuilder.addPlugin("project.ext.pluginApplied = true", pluginId)
+        pluginBuilder.publishTo(executer, module.artifactFile)
+    }
+
+    private String applyAndVerify(String id, String version) {
+        """
+            plugins {
+                id "$id" version "$version"
+            }
+
+            task verify << {
+                assert pluginApplied
+            }
+        """
+    }
+
+    void pluginNotFound(String version = "1.0") {
+        failure.assertThatDescription(Matchers.startsWith("Plugin [id: 'org.my.myplugin', version: '$version'] was not found in any of the following sources:"))
+    }
+
+    void resolutionServiceDetail(String detail) {
+        failure.assertThatDescription(Matchers.containsString("Gradle Central Plugin Repository ($detail)"))
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java
new file mode 100644
index 0000000..4c4e456
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.initialization.dsl.ScriptHandler;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.internal.plugins.*;
+import org.gradle.api.plugins.InvalidPluginException;
+import org.gradle.api.plugins.UnknownPluginException;
+import org.gradle.api.specs.Spec;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.exceptions.LocationAwareException;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.resolve.internal.*;
+
+import java.io.File;
+import java.util.*;
+
+import static org.gradle.util.CollectionUtils.any;
+import static org.gradle.util.CollectionUtils.collect;
+
+public class DefaultPluginRequestApplicator implements PluginRequestApplicator {
+
+    private final PluginRegistry pluginRegistry;
+    private final PluginResolver pluginResolver;
+
+    public DefaultPluginRequestApplicator(PluginRegistry pluginRegistry, PluginResolver pluginResolver) {
+        this.pluginRegistry = pluginRegistry;
+        this.pluginResolver = pluginResolver;
+    }
+
+    public void applyPlugins(PluginRequests requests, final ScriptHandler scriptHandler, @Nullable final PluginManagerInternal target, ClassLoaderScope classLoaderScope) {
+        if (requests.isEmpty()) {
+            defineScriptHandlerClassScope(scriptHandler, classLoaderScope);
+            return;
+        }
+
+        if (target == null) {
+            throw new IllegalStateException("Plugin target is 'null' and there are plugin requests");
+        }
+
+        final PluginResolver effectivePluginResolver = wrapInNotInClasspathCheck(classLoaderScope);
+
+        List<Result> results = collect(requests, new Transformer<Result, PluginRequest>() {
+            public Result transform(PluginRequest request) {
+                return resolveToFoundResult(effectivePluginResolver, request);
+            }
+        });
+
+        // Could be different to ids in the requests as they may be unqualified
+        final Map<Result, PluginId> legacyActualPluginIds = Maps.newLinkedHashMap();
+        final Map<Result, PluginImplementation<?>> pluginImpls = Maps.newLinkedHashMap();
+
+        if (!results.isEmpty()) {
+            final RepositoryHandler repositories = scriptHandler.getRepositories();
+            final List<MavenArtifactRepository> mavenRepos = repositories.withType(MavenArtifactRepository.class);
+            final Set<String> repoUrls = Sets.newLinkedHashSet();
+
+            for (final Result result : results) {
+                applyPlugin(result.request, result.found.getPluginId(), new Runnable() {
+                    @Override
+                    public void run() {
+                        result.found.execute(new PluginResolveContext() {
+                            public void addLegacy(PluginId pluginId, final String m2RepoUrl, Object dependencyNotation) {
+                                legacyActualPluginIds.put(result, pluginId);
+                                repoUrls.add(m2RepoUrl);
+                                scriptHandler.getDependencies().add(ScriptHandler.CLASSPATH_CONFIGURATION, dependencyNotation);
+                            }
+
+                            @Override
+                            public void add(PluginImplementation<?> plugin) {
+                                pluginImpls.put(result, plugin);
+                            }
+                        });
+                    }
+                });
+            }
+
+            for (final String m2RepoUrl : repoUrls) {
+                boolean repoExists = any(mavenRepos, new Spec<MavenArtifactRepository>() {
+                    public boolean isSatisfiedBy(MavenArtifactRepository element) {
+                        return element.getUrl().toString().equals(m2RepoUrl);
+                    }
+                });
+                if (!repoExists) {
+                    repositories.maven(new Action<MavenArtifactRepository>() {
+                        public void execute(MavenArtifactRepository mavenArtifactRepository) {
+                            mavenArtifactRepository.setUrl(m2RepoUrl);
+                        }
+                    });
+                }
+            }
+        }
+
+        defineScriptHandlerClassScope(scriptHandler, classLoaderScope);
+
+        // We're making an assumption here that the target's plugin registry is backed classLoaderScope.
+        // Because we are only build.gradle files right now, this holds.
+        // It won't for arbitrary scripts though.
+        for (final Map.Entry<Result, PluginId> entry : legacyActualPluginIds.entrySet()) {
+            final PluginRequest request = entry.getKey().request;
+            final PluginId id = entry.getValue();
+            applyPlugin(request, id, new Runnable() {
+                public void run() {
+                    target.apply(id.toString());
+                }
+            });
+        }
+
+        for (final Map.Entry<Result, PluginImplementation<?>> entry : pluginImpls.entrySet()) {
+            final Result result = entry.getKey();
+            applyPlugin(result.request, result.found.getPluginId(), new Runnable() {
+                public void run() {
+                    target.apply(entry.getValue());
+                }
+            });
+        }
+    }
+
+    private void defineScriptHandlerClassScope(ScriptHandler scriptHandler, ClassLoaderScope classLoaderScope) {
+        Configuration classpathConfiguration = scriptHandler.getConfigurations().getByName(ScriptHandler.CLASSPATH_CONFIGURATION);
+        Set<File> files = classpathConfiguration.getFiles();
+        ClassPath classPath = new DefaultClassPath(files);
+        classLoaderScope.export(classPath);
+        classLoaderScope.lock();
+    }
+
+    private PluginResolver wrapInNotInClasspathCheck(ClassLoaderScope classLoaderScope) {
+        PluginDescriptorLocator scriptClasspathPluginDescriptorLocator = new ClassloaderBackedPluginDescriptorLocator(classLoaderScope.getParent().getExportClassLoader());
+        return new NotNonCorePluginOnClasspathCheckPluginResolver(pluginResolver, pluginRegistry, scriptClasspathPluginDescriptorLocator);
+    }
+
+    private void applyPlugin(PluginRequest request, PluginId id, Runnable applicator) {
+        try {
+            try {
+                applicator.run();
+            } catch (UnknownPluginException e) {
+                throw new InvalidPluginException(
+                        String.format(
+                                "Could not apply requested plugin %s as it does not provide a plugin with id '%s'."
+                                        + " This is caused by an incorrect plugin implementation."
+                                        + " Please contact the plugin author(s).",
+                                request, id
+                        ),
+                        e
+                );
+            } catch (Exception e) {
+                throw new InvalidPluginException(String.format("An exception occurred applying plugin request %s", request), e);
+            }
+        } catch (Exception e) {
+            throw new LocationAwareException(e, request.getScriptDisplayName(), request.getLineNumber());
+        }
+    }
+
+    private Result resolveToFoundResult(PluginResolver effectivePluginResolver, PluginRequest request) {
+        Result result = new Result(request);
+        try {
+            effectivePluginResolver.resolve(request, result);
+        } catch (Exception e) {
+            throw new LocationAwareException(
+                    new GradleException(String.format("Error resolving plugin %s", request.getDisplayName()), e),
+                    request.getScriptDisplayName(), request.getLineNumber());
+        }
+
+        if (!result.isFound()) {
+            String message = buildNotFoundMessage(request, result);
+            Exception exception = new UnknownPluginException(message);
+            throw new LocationAwareException(exception, request.getScriptDisplayName(), request.getLineNumber());
+        }
+
+        return result;
+    }
+
+    private String buildNotFoundMessage(PluginRequest pluginRequest, Result result) {
+        if (result.notFoundList.isEmpty()) {
+            // this shouldn't happen, resolvers should call notFound()
+            return String.format("Plugin %s was not found", pluginRequest.getDisplayName());
+        } else {
+            Formatter sb = new Formatter();
+            sb.format("Plugin %s was not found in any of the following sources:%n", pluginRequest.getDisplayName());
+
+            for (NotFound notFound : result.notFoundList) {
+                sb.format("%n- %s", notFound.source);
+                if (notFound.detail != null) {
+                    sb.format(" (%s)", notFound.detail);
+                }
+            }
+
+            return sb.toString();
+        }
+    }
+
+    private static class NotFound {
+        private final String source;
+        private final String detail;
+
+        private NotFound(String source, String detail) {
+            this.source = source;
+            this.detail = detail;
+        }
+    }
+
+    private static class Result implements PluginResolutionResult {
+        private final List<NotFound> notFoundList = new LinkedList<NotFound>();
+        private final PluginRequest request;
+        private PluginResolution found;
+
+        public Result(PluginRequest request) {
+            this.request = request;
+        }
+
+        public void notFound(String sourceDescription, String notFoundDetail) {
+            notFoundList.add(new NotFound(sourceDescription, notFoundDetail));
+        }
+
+        public void found(String sourceDescription, PluginResolution pluginResolution) {
+            found = pluginResolution;
+        }
+
+        public boolean isFound() {
+            return found != null;
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginResolverFactory.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginResolverFactory.java
new file mode 100644
index 0000000..89b2fd6
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginResolverFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.internal.Factory;
+import org.gradle.plugin.use.resolve.internal.CompositePluginResolver;
+import org.gradle.plugin.use.resolve.internal.CorePluginResolver;
+import org.gradle.plugin.use.resolve.internal.NoopPluginResolver;
+import org.gradle.plugin.use.resolve.internal.PluginResolver;
+import org.gradle.plugin.use.resolve.service.internal.PluginResolutionServiceResolver;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class PluginResolverFactory implements Factory<PluginResolver> {
+
+    private final PluginRegistry pluginRegistry;
+    private final DocumentationRegistry documentationRegistry;
+    private final PluginResolutionServiceResolver pluginResolutionServiceResolver;
+
+    public PluginResolverFactory(
+            PluginRegistry pluginRegistry,
+            DocumentationRegistry documentationRegistry,
+            PluginResolutionServiceResolver pluginResolutionServiceResolver
+    ) {
+        this.pluginRegistry = pluginRegistry;
+        this.documentationRegistry = documentationRegistry;
+        this.pluginResolutionServiceResolver = pluginResolutionServiceResolver;
+    }
+
+    public PluginResolver create() {
+        List<PluginResolver> resolvers = new LinkedList<PluginResolver>();
+        addDefaultResolvers(resolvers);
+        return new CompositePluginResolver(resolvers);
+    }
+
+    private void addDefaultResolvers(List<PluginResolver> resolvers) {
+        resolvers.add(new NoopPluginResolver(pluginRegistry));
+        resolvers.add(new CorePluginResolver(documentationRegistry, pluginRegistry));
+        resolvers.add(pluginResolutionServiceResolver);
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginUsePluginServiceRegistry.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginUsePluginServiceRegistry.java
new file mode 100644
index 0000000..59886da
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/PluginUsePluginServiceRegistry.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.internal;
+
+import org.gradle.StartParameter;
+import org.gradle.api.UnknownProjectException;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.artifacts.DependencyManagementServices;
+import org.gradle.api.internal.artifacts.DependencyResolutionServices;
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.initialization.BasicDomainObjectContext;
+import org.gradle.api.internal.plugins.PluginInspector;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.cache.CacheRepository;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.internal.FileLockManager;
+import org.gradle.initialization.ClassLoaderScopeRegistry;
+import org.gradle.internal.Factory;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.transport.http.DefaultHttpSettings;
+import org.gradle.internal.resource.transport.http.HttpClientHelper;
+import org.gradle.internal.resource.transport.http.HttpResourceAccessor;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+import org.gradle.plugin.use.resolve.service.internal.*;
+
+import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
+
+public class PluginUsePluginServiceRegistry implements PluginServiceRegistry {
+
+    public static final String CACHE_NAME = "plugin-resolution";
+
+    public void registerGlobalServices(ServiceRegistration registration) {
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+        registration.addProvider(new BuildScopeServices());
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class BuildScopeServices {
+        PluginResolutionServiceClient createPluginResolutionServiceClient(CacheRepository cacheRepository, StartParameter startParameter) {
+            HttpClientHelper http = new HttpClientHelper(new DefaultHttpSettings(new PasswordCredentials()));
+            HttpResourceAccessor accessor = new HttpResourceAccessor(http);
+            PluginResolutionServiceClient httpClient = startParameter.isOffline()
+                    ? new OfflinePluginResolutionServiceClient()
+                    : new HttpPluginResolutionServiceClient(accessor);
+
+            PersistentCache cache = cacheRepository
+                    .cache(CACHE_NAME)
+                    .withDisplayName("Plugin Resolution Cache")
+                    .withLockOptions(mode(FileLockManager.LockMode.None))
+                    .open();
+
+            PluginResolutionServiceClient persistentCachingClient = new PersistentCachingPluginResolutionServiceClient(httpClient, cache);
+            PluginResolutionServiceClient inMemoryCachingClient = new InMemoryCachingPluginResolutionServiceClient(persistentCachingClient);
+            return new DeprecationListeningPluginResolutionServiceClient(inMemoryCachingClient);
+        }
+
+        PluginResolutionServiceResolver createPluginResolutionServiceResolver(PluginResolutionServiceClient pluginResolutionServiceClient, VersionSelectorScheme versionSelectorScheme,
+                                                                              StartParameter startParameter, final DependencyManagementServices dependencyManagementServices,
+                                                                              final FileResolver fileResolver, final DependencyMetaDataProvider dependencyMetaDataProvider,
+                                                                              ClassLoaderScopeRegistry classLoaderScopeRegistry, PluginInspector pluginInspector) {
+            final ProjectFinder projectFinder = new ProjectFinder() {
+                public ProjectInternal getProject(String path) {
+                    throw new UnknownProjectException("Cannot use project dependencies in a plugin resolution definition.");
+                }
+            };
+
+            return new PluginResolutionServiceResolver(pluginResolutionServiceClient, versionSelectorScheme, startParameter, classLoaderScopeRegistry.getCoreScope(), new Factory<DependencyResolutionServices>() {
+                public DependencyResolutionServices create() {
+                    return dependencyManagementServices.create(fileResolver, dependencyMetaDataProvider, projectFinder, new BasicDomainObjectContext());
+                }
+            }, pluginInspector);
+        }
+
+        PluginResolverFactory createPluginResolverFactory(PluginRegistry pluginRegistry, DocumentationRegistry documentationRegistry, PluginResolutionServiceResolver pluginResolutionServiceResolver) {
+            return new PluginResolverFactory(pluginRegistry, documentationRegistry, pluginResolutionServiceResolver);
+        }
+
+        PluginRequestApplicator createPluginRequestApplicator(PluginRegistry pluginRegistry, PluginResolverFactory pluginResolverFactory) {
+            return new DefaultPluginRequestApplicator(pluginRegistry, pluginResolverFactory.create());
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/ClassPathPluginResolution.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/ClassPathPluginResolution.java
new file mode 100644
index 0000000..c4c9596
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/ClassPathPluginResolution.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.plugin.use.resolve.internal;
+
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.internal.plugins.DefaultPluginRegistry;
+import org.gradle.api.internal.plugins.PluginImplementation;
+import org.gradle.api.internal.plugins.PluginInspector;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.plugins.UnknownPluginException;
+import org.gradle.internal.Factory;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.plugin.internal.PluginId;
+
+public class ClassPathPluginResolution implements PluginResolution {
+
+    private final PluginId pluginId;
+    private final ClassLoaderScope parent;
+    private final Factory<? extends ClassPath> classPathFactory;
+    private final PluginInspector pluginInspector;
+
+    public ClassPathPluginResolution(PluginId pluginId, ClassLoaderScope parent, Factory<? extends ClassPath> classPathFactory, PluginInspector pluginInspector) {
+        this.pluginId = pluginId;
+        this.parent = parent;
+        this.classPathFactory = classPathFactory;
+        this.pluginInspector = pluginInspector;
+    }
+
+    public PluginId getPluginId() {
+        return pluginId;
+    }
+
+    @Override
+    public void execute(PluginResolveContext pluginResolveContext) {
+        ClassPath classPath = classPathFactory.create();
+        ClassLoaderScope loaderScope = parent.createChild("plugin-" + pluginId.asString());
+        loaderScope.local(classPath);
+        loaderScope.lock();
+        PluginRegistry pluginRegistry = new DefaultPluginRegistry(pluginInspector, loaderScope);
+        PluginImplementation<?> plugin = pluginRegistry.lookup(pluginId);
+        if (plugin == null) {
+            throw new UnknownPluginException("Plugin with id '" + pluginId + "' not found.");
+        }
+        pluginResolveContext.add(plugin);
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CompositePluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CompositePluginResolver.java
new file mode 100644
index 0000000..32975c0
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CompositePluginResolver.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.plugin.use.resolve.internal;
+
+import org.gradle.plugin.use.internal.PluginRequest;
+
+import java.util.List;
+
+public class CompositePluginResolver implements PluginResolver {
+
+    private final List<PluginResolver> repositories;
+
+    public CompositePluginResolver(List<PluginResolver> repositories) {
+        this.repositories = repositories;
+    }
+
+    public void resolve(PluginRequest pluginRequest, PluginResolutionResult result) {
+        for (PluginResolver repository : repositories) {
+            repository.resolve(pluginRequest, result);
+            if (result.isFound()) {
+                break;
+            }
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java
new file mode 100644
index 0000000..d6d445b
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.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.plugin.use.resolve.internal;
+
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.plugins.DefaultPluginManager;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.api.internal.plugins.PluginImplementation;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.internal.InvalidPluginRequestException;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+public class CorePluginResolver implements PluginResolver {
+
+    private final DocumentationRegistry documentationRegistry;
+    private final PluginRegistry pluginRegistry;
+
+    public CorePluginResolver(DocumentationRegistry documentationRegistry, PluginRegistry pluginRegistry) {
+        this.documentationRegistry = documentationRegistry;
+        this.pluginRegistry = pluginRegistry;
+    }
+
+    public void resolve(PluginRequest pluginRequest, PluginResolutionResult result) {
+        PluginId id = pluginRequest.getId();
+
+        if (!id.isQualified() || id.inNamespace(DefaultPluginManager.CORE_PLUGIN_NAMESPACE)) {
+            PluginImplementation<?> plugin = pluginRegistry.lookup(id);
+            if (plugin == null) {
+                result.notFound(getDescription(), String.format("not a core plugin, please see %s for available core plugins", documentationRegistry.getDocumentationFor("standard_plugins")));
+            } else {
+                if (pluginRequest.getVersion() != null) {
+                    throw new InvalidPluginRequestException(pluginRequest,
+                            "Plugin '" + id + "' is a core Gradle plugin, which cannot be specified with a version number. "
+                                    + "Such plugins are versioned as part of Gradle. Please remove the version number from the declaration."
+                    );
+                }
+                result.found(getDescription(), new SimplePluginResolution(plugin));
+            }
+        } else {
+            result.notFound(getDescription(), String.format("plugin is not in '%s' namespace", DefaultPluginManager.CORE_PLUGIN_NAMESPACE));
+        }
+    }
+
+    public static String getDescription() {
+        return "Gradle Core Plugins";
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NoopPluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NoopPluginResolver.java
new file mode 100644
index 0000000..a57e2c2
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NoopPluginResolver.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.internal;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.internal.plugins.DefaultPotentialPluginWithId;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.internal.InvalidPluginRequestException;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+// Used for testing the plugins DSL
+public class NoopPluginResolver implements PluginResolver {
+
+    public static final PluginId NOOP_PLUGIN_ID = PluginId.of("noop");
+    private final PluginRegistry pluginRegistry;
+
+    public NoopPluginResolver(PluginRegistry pluginRegistry) {
+        this.pluginRegistry = pluginRegistry;
+    }
+
+    public void resolve(PluginRequest pluginRequest, PluginResolutionResult result) throws InvalidPluginRequestException {
+        if (pluginRequest.getId().equals(NOOP_PLUGIN_ID)) {
+            result.found("noop resolver", new SimplePluginResolution(DefaultPotentialPluginWithId.of(NOOP_PLUGIN_ID, pluginRegistry.inspect(NoopPlugin.class))));
+        }
+    }
+
+    public static class NoopPlugin implements Plugin<Object> {
+        public void apply(Object target) {
+            // do nothing
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NotNonCorePluginOnClasspathCheckPluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NotNonCorePluginOnClasspathCheckPluginResolver.java
new file mode 100644
index 0000000..e5b6037
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/NotNonCorePluginOnClasspathCheckPluginResolver.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.plugin.use.resolve.internal;
+
+import org.gradle.api.internal.plugins.PluginDescriptor;
+import org.gradle.api.internal.plugins.PluginDescriptorLocator;
+import org.gradle.api.internal.plugins.PluginRegistry;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.internal.InvalidPluginRequestException;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+public class NotNonCorePluginOnClasspathCheckPluginResolver implements PluginResolver {
+
+    private final PluginResolver delegate;
+    private final PluginRegistry corePluginRegistry;
+    private final PluginDescriptorLocator pluginDescriptorLocator;
+
+    public NotNonCorePluginOnClasspathCheckPluginResolver(PluginResolver delegate, PluginRegistry corePluginRegistry, PluginDescriptorLocator pluginDescriptorLocator) {
+        this.delegate = delegate;
+        this.corePluginRegistry = corePluginRegistry;
+        this.pluginDescriptorLocator = pluginDescriptorLocator;
+    }
+
+    public void resolve(PluginRequest pluginRequest, PluginResolutionResult result) {
+        PluginId pluginId = pluginRequest.getId();
+        PluginDescriptor pluginDescriptor = pluginDescriptorLocator.findPluginDescriptor(pluginId.toString());
+        if (pluginDescriptor == null || isCorePlugin(pluginId)) {
+            delegate.resolve(pluginRequest, result);
+        } else {
+            throw new InvalidPluginRequestException(pluginRequest, pluginOnClasspathErrorMessage(pluginId.toString()));
+        }
+    }
+
+    public static String pluginOnClasspathErrorMessage(String pluginId) {
+        return String.format("Plugin '%s' is already on the script classpath. Plugins on the script classpath cannot be applied in the plugins {} block. Add  \"apply plugin: '%s'\" to the body of the script to use the plugin.", pluginId, pluginId);
+    }
+
+    private boolean isCorePlugin(PluginId pluginId) {
+        return corePluginRegistry.lookup(pluginId) != null;
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolution.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolution.java
new file mode 100644
index 0000000..be99ef7
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolution.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.plugin.use.resolve.internal;
+
+import org.gradle.api.Action;
+import org.gradle.plugin.internal.PluginId;
+
+/**
+ * The result of attempting to resolve a plugin to a classpath.
+ */
+public interface PluginResolution extends Action<PluginResolveContext> {
+    PluginId getPluginId();
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolutionResult.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolutionResult.java
new file mode 100644
index 0000000..a16cc86
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolutionResult.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.internal;
+
+import org.gradle.api.Nullable;
+
+/**
+ * A write only object that {@link PluginResolver} implementations receive and work with to communicate their results.
+ * <p>
+ * It has a slightly peculiar interface as it is designed to “collect up” what happens as each resolver tries to resolve the plugin.
+ * <p>
+ * A “source” is some logical source of plugins (e.g. core plugin set, plugin resolution service instance)
+ */
+public interface PluginResolutionResult {
+
+    /**
+     * Record that the plugin was not found in some source of plugins.
+     *
+     * @param sourceDescription a description of the source of plugins, where the plugin requested could not be found
+     * @param notFoundDetail detail on why the plugin couldn't be found (e.g. it might be available by a different version)
+     */
+    void notFound(String sourceDescription, @Nullable String notFoundDetail);
+
+    /**
+     * Record that the plugin was found in some source of plugins.
+     * <p>
+     * If a plugin is found, no further resolvers will be queried.
+     *
+     * @param sourceDescription a description of the source of plugins, where the plugin requested could not be found
+     * @param pluginResolution the plugin resolution
+     */
+    void found(String sourceDescription, PluginResolution pluginResolution);
+
+    /**
+     * Whether the plugin has been found (i.e. has {@link #found(String, PluginResolution)} has been called)
+     *
+     * @return whether the plugin has been found
+     */
+    boolean isFound();
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolveContext.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolveContext.java
new file mode 100644
index 0000000..402abdf
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolveContext.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.internal;
+
+import org.gradle.api.internal.plugins.PluginImplementation;
+import org.gradle.plugin.internal.PluginId;
+
+public interface PluginResolveContext {
+    void addLegacy(PluginId pluginId, String m2RepoUrl, Object dependencyNotation);
+
+    void add(PluginImplementation<?> plugin);
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolver.java
new file mode 100644
index 0000000..c3fcb55
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/PluginResolver.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.plugin.use.resolve.internal;
+
+import org.gradle.api.Incubating;
+import org.gradle.plugin.use.internal.InvalidPluginRequestException;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+/**
+ * A repository of plugins.
+ */
+ at Incubating
+public interface PluginResolver {
+
+    void resolve(PluginRequest pluginRequest, PluginResolutionResult result) throws InvalidPluginRequestException;
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/SimplePluginResolution.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/SimplePluginResolution.java
new file mode 100644
index 0000000..6e7aab2
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/SimplePluginResolution.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.plugin.use.resolve.internal;
+
+import org.gradle.api.internal.plugins.PluginImplementation;
+import org.gradle.plugin.internal.PluginId;
+
+public class SimplePluginResolution implements PluginResolution {
+    private final PluginImplementation<?> plugin;
+
+    public SimplePluginResolution(PluginImplementation<?> plugin) {
+        this.plugin = plugin;
+    }
+
+    public PluginId getPluginId() {
+        return plugin.getPluginId();
+    }
+
+    @Override
+    public void execute(PluginResolveContext pluginResolveContext) {
+        pluginResolveContext.add(plugin);
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ClientStatus.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ClientStatus.java
new file mode 100644
index 0000000..7be3b4a
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ClientStatus.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.plugin.use.resolve.service.internal;
+
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+public class ClientStatus {
+
+    private final String deprecationMessage;
+
+    public ClientStatus(String deprecationMessage) {
+        this.deprecationMessage = deprecationMessage;
+    }
+
+    public String getDeprecationMessage() {
+        return deprecationMessage;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ClientStatus that = (ClientStatus) o;
+
+        if (deprecationMessage != null ? !deprecationMessage.equals(that.deprecationMessage) : that.deprecationMessage != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return deprecationMessage != null ? deprecationMessage.hashCode() : 0;
+    }
+
+    public static class Serializer implements org.gradle.internal.serialize.Serializer<ClientStatus> {
+        public ClientStatus read(Decoder decoder) throws Exception {
+            return new ClientStatus(decoder.readNullableString());
+        }
+
+        public void write(Encoder encoder, ClientStatus value) throws Exception {
+            encoder.writeNullableString(value.deprecationMessage);
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClient.java
new file mode 100644
index 0000000..c78f7d9
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClient.java
@@ -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.plugin.use.resolve.service.internal;
+
+import org.gradle.api.Action;
+import org.gradle.plugin.use.internal.PluginRequest;
+import org.gradle.util.DeprecationLogger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class DeprecationListeningPluginResolutionServiceClient implements PluginResolutionServiceClient {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeprecationListeningPluginResolutionServiceClient.class);
+
+    private final PluginResolutionServiceClient delegate;
+    private final Action<? super String> action;
+
+    public DeprecationListeningPluginResolutionServiceClient(PluginResolutionServiceClient delegate) {
+        this(delegate, new Action<String>() {
+            public void execute(String s) {
+                DeprecationLogger.nagUserWith(s);
+            }
+        });
+    }
+
+    DeprecationListeningPluginResolutionServiceClient(PluginResolutionServiceClient delegate, Action<? super String> action) {
+        this.delegate = delegate;
+        this.action = action;
+    }
+
+    public Response<PluginUseMetaData> queryPluginMetadata(String portalUrl, boolean shouldValidate, PluginRequest pluginRequest) {
+        Response<PluginUseMetaData> response = delegate.queryPluginMetadata(portalUrl, shouldValidate, pluginRequest);
+        String statusChecksum = response.getClientStatusChecksum();
+        if (statusChecksum != null) {
+            checkForDeprecation(portalUrl, shouldValidate, statusChecksum);
+        }
+        return response;
+    }
+
+    public Response<ClientStatus> queryClientStatus(String portalUrl, boolean shouldValidate, String checksum) {
+        return delegate.queryClientStatus(portalUrl, shouldValidate, checksum);
+    }
+
+    private void checkForDeprecation(String portalUrl, boolean shouldValidate, String statusChecksum) {
+        Response<ClientStatus> response;
+        try {
+            response = delegate.queryClientStatus(portalUrl, shouldValidate, statusChecksum);
+        } catch (Exception e) {
+            LOGGER.warn("Exception thrown fetching client status", e);
+            return;
+        }
+
+        if (response.isError()) {
+            LOGGER.warn("Received error response fetching client status from {}: {}", response.getUrl(), response.getErrorResponse());
+        } else {
+            ClientStatus status = response.getResponse();
+            String deprecationMessage = status.getDeprecationMessage();
+            if (deprecationMessage != null) {
+                String message = toMessage(deprecationMessage, response.getUrl());
+                action.execute(message);
+            }
+        }
+    }
+
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    public static String toMessage(String deprecationMessage, String responseUrl) {
+        return String.format("Plugin resolution service client status service %s reported that this client has been deprecated: %s", responseUrl, deprecationMessage);
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ErrorResponse.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ErrorResponse.java
new file mode 100644
index 0000000..d21357e
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/ErrorResponse.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal;
+
+/**
+ * Defines the JSON protocol for plugin resolution service error responses.
+ */
+public class ErrorResponse {
+    public final String errorCode;
+    public final String message;
+
+    public ErrorResponse(String errorCode, String message) {
+        this.errorCode = errorCode;
+        this.message = message;
+    }
+
+    public boolean is(Code code) {
+        return code.name().equals(errorCode);
+    }
+
+    public enum Code {
+        UNKNOWN_PLUGIN,
+        UNKNOWN_PLUGIN_VERSION
+    }
+
+    @Override
+    public String toString() {
+        return "{errorCode='" + errorCode + '\'' + ", message='" + message + '\'' + '}';
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClient.java
new file mode 100644
index 0000000..0f6bab7
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClient.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal;
+
+import com.google.common.escape.Escaper;
+import com.google.common.net.UrlEscapers;
+import com.google.gson.Gson;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonSyntaxException;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.internal.Actions;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transport.http.HttpResourceAccessor;
+import org.gradle.internal.resource.transport.http.HttpResponseResource;
+import org.gradle.plugin.use.internal.PluginRequest;
+import org.gradle.util.GradleVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class HttpPluginResolutionServiceClient implements PluginResolutionServiceClient {
+    private static final Escaper PATH_SEGMENT_ESCAPER = UrlEscapers.urlPathSegmentEscaper();
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpPluginResolutionServiceClient.class);
+    private static final String CLIENT_REQUEST_BASE = String.format("%s", PATH_SEGMENT_ESCAPER.escape(GradleVersion.current().getVersion()));
+    private static final String PLUGIN_USE_REQUEST_URL = "/plugin/use/%s/%s";
+    private static final String JSON = "application/json";
+
+    public static final String CLIENT_STATUS_CHECKSUM_HEADER = "X-Gradle-Client-Status-Checksum";
+
+    private final HttpResourceAccessor resourceAccessor;
+
+    public HttpPluginResolutionServiceClient(HttpResourceAccessor resourceAccessor) {
+        this.resourceAccessor = resourceAccessor;
+    }
+
+    @Nullable
+    public Response<PluginUseMetaData> queryPluginMetadata(String portalUrl, boolean shouldValidate, final PluginRequest pluginRequest) {
+        String escapedId = PATH_SEGMENT_ESCAPER.escape(pluginRequest.getId().toString());
+        String escapedPluginVersion = PATH_SEGMENT_ESCAPER.escape(pluginRequest.getVersion());
+        final String requestUrl = toRequestUrl(portalUrl, String.format(PLUGIN_USE_REQUEST_URL, escapedId, escapedPluginVersion));
+        return request(requestUrl, PluginUseMetaData.class, new Action<PluginUseMetaData>() {
+            public void execute(PluginUseMetaData pluginUseMetaData) {
+                validate(requestUrl, pluginUseMetaData);
+            }
+        });
+    }
+
+    public Response<ClientStatus> queryClientStatus(String portalUrl, boolean shouldValidate, String checksum) {
+        final String requestUrl = toRequestUrl(portalUrl, "");
+        return request(requestUrl, ClientStatus.class, Actions.doNothing());
+    }
+
+    private String toRequestUrl(String portalUrl, String path) {
+        return String.format("%s/%s%s", portalUrl, CLIENT_REQUEST_BASE, path);
+    }
+
+    private <T> Response<T> request(final String requestUrl, final Class<T> type, final Action<? super T> validator) {
+        final URI requestUri = toUri(requestUrl, "plugin request");
+
+        try {
+            HttpResponseResource response = resourceAccessor.getRawResource(requestUri);
+            try {
+                final int statusCode = response.getStatusCode();
+                String contentType = response.getContentType();
+                if (contentType == null || !contentType.equalsIgnoreCase(JSON)) {
+                    final String message = String.format("content type is '%s', expected '%s'", contentType == null ? "" : contentType, JSON);
+                    throw new OutOfProtocolException(requestUrl, message);
+                }
+
+                final String clientStatusChecksum = response.getHeaderValue(CLIENT_STATUS_CHECKSUM_HEADER);
+                Reader reader = new InputStreamReader(response.openStream(), "utf-8");
+                try {
+                    if (statusCode == 200) {
+                        T payload = new Gson().fromJson(reader, type);
+                        validator.execute(payload);
+                        return new SuccessResponse<T>(payload, statusCode, requestUrl, clientStatusChecksum);
+                    } else if (statusCode >= 400 && statusCode < 600) {
+                        ErrorResponse errorResponse = validate(requestUrl, new Gson().fromJson(reader, ErrorResponse.class));
+                        return new ErrorResponseResponse<T>(errorResponse, statusCode, requestUrl, clientStatusChecksum);
+                    } else {
+                        throw new OutOfProtocolException(requestUrl, "unexpected HTTP response status " + statusCode);
+                    }
+                } catch (JsonSyntaxException e) {
+                    throw new OutOfProtocolException(requestUrl, "could not parse response JSON", e);
+                } catch (JsonIOException e) {
+                    throw new OutOfProtocolException(requestUrl, "could not parse response JSON", e);
+                }
+            } finally {
+                response.close();
+            }
+        } catch (IOException e) {
+            throw ResourceException.getFailed(requestUri, e);
+        }
+    }
+
+    public void close() {
+
+    }
+
+    private PluginUseMetaData validate(String url, PluginUseMetaData pluginUseMetaData) {
+        if (pluginUseMetaData.implementationType == null) {
+            throw new OutOfProtocolException(url, "invalid plugin metadata - no implementation type specified");
+        }
+        if (!pluginUseMetaData.implementationType.equals(PluginUseMetaData.M2_JAR)) {
+            throw new OutOfProtocolException(url, String.format("invalid plugin metadata - unsupported implementation type '%s'", pluginUseMetaData.implementationType));
+        }
+        if (pluginUseMetaData.implementation == null) {
+            throw new OutOfProtocolException(url, "invalid plugin metadata - no implementation specified");
+        }
+        if (pluginUseMetaData.implementation.get("gav") == null) {
+            throw new OutOfProtocolException(url, "invalid plugin metadata - no module coordinates specified");
+        }
+        if (pluginUseMetaData.implementation.get("repo") == null) {
+            throw new OutOfProtocolException(url, "invalid plugin metadata - no module repository specified");
+        }
+
+        return pluginUseMetaData;
+    }
+
+    private ErrorResponse validate(String url, ErrorResponse errorResponse) {
+        if (errorResponse.errorCode == null) {
+            throw new OutOfProtocolException(url, "invalid error response - no error code specified");
+        }
+
+        if (errorResponse.message == null) {
+            throw new OutOfProtocolException(url, "invalid error response - no message specified");
+        }
+
+        return errorResponse;
+    }
+
+    private URI toUri(String url, String kind) {
+        try {
+            return new URI(url);
+        } catch (URISyntaxException e) {
+            throw new GradleException(String.format("Invalid %s URL: %s", kind, url), e);
+        }
+    }
+
+    private static class OutOfProtocolException extends GradleException {
+        private OutOfProtocolException(String requestUrl, String message) {
+            super(toMessage(requestUrl, message));
+        }
+
+        private OutOfProtocolException(String requestUrl, String message, Throwable cause) {
+            super(toMessage(requestUrl, message), cause);
+        }
+
+        private static String toMessage(String requestUrl, String message) {
+            return String.format("The response from %s was not a valid response from a Gradle Plugin Resolution Service: %s", requestUrl, message);
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClient.java
new file mode 100644
index 0000000..f5d002e
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClient.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.gradle.api.Nullable;
+import org.gradle.api.Transformer;
+import org.gradle.internal.Factory;
+import org.gradle.internal.Transformers;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+import java.io.IOException;
+
+public class InMemoryCachingPluginResolutionServiceClient implements PluginResolutionServiceClient {
+
+    private final Cache<Key<PluginIdentity>, Response<PluginUseMetaData>> pluginMetadataCache = CacheBuilder.newBuilder().build();
+    private final Cache<Key<String>, Response<ClientStatus>> statusCache = CacheBuilder.newBuilder().build();
+    private final PluginResolutionServiceClient delegate;
+
+    public InMemoryCachingPluginResolutionServiceClient(PluginResolutionServiceClient delegate) {
+        this.delegate = delegate;
+    }
+
+    public Response<PluginUseMetaData> queryPluginMetadata(final String portalUrl, final boolean shouldValidate, final PluginRequest pluginRequest) {
+        Key<PluginIdentity> key = new Key<PluginIdentity>(portalUrl, new PluginIdentity(pluginRequest.getId(), pluginRequest.getVersion()));
+        return getResponse(
+                key,
+                pluginMetadataCache,
+                new Factory<Response<PluginUseMetaData>>() {
+                    public Response<PluginUseMetaData> create() {
+                        return delegate.queryPluginMetadata(portalUrl, shouldValidate, pluginRequest);
+                    }
+                },
+                Transformers.constant(key)
+        );
+    }
+
+    public Response<ClientStatus> queryClientStatus(final String portalUrl, final boolean shouldValidate, @Nullable final String checksum) {
+        return getResponse(
+                checksum == null ? null : new Key<String>(portalUrl, checksum),
+                statusCache,
+                new Factory<Response<ClientStatus>>() {
+                    public Response<ClientStatus> create() {
+                        return delegate.queryClientStatus(portalUrl, shouldValidate, checksum);
+                    }
+                },
+                new Transformer<Key<String>, Response<ClientStatus>>() {
+                    public Key<String> transform(Response<ClientStatus> original) {
+                        return new Key<String>(portalUrl, original.getClientStatusChecksum());
+                    }
+                }
+        );
+    }
+
+    private <K, V> Response<V> getResponse(Key<K> key, Cache<Key<K>, Response<V>> cache, Factory<Response<V>> responseFactory, Transformer<Key<K>, ? super Response<V>> keyGenerator) {
+        Response<V> response = key == null ? null : cache.getIfPresent(key);
+        if (response != null) {
+            return response;
+        } else {
+            response = responseFactory.create();
+            if (!response.isError()) {
+                Key<K> actualKey = keyGenerator.transform(response);
+                cache.put(actualKey, response);
+            }
+            return response;
+        }
+    }
+
+    public void close() throws IOException {
+        try {
+            delegate.close();
+        } finally {
+            pluginMetadataCache.invalidateAll();
+            statusCache.invalidateAll();
+        }
+    }
+
+    public static class PluginIdentity {
+        final PluginId pluginId;
+        final String pluginVersion;
+
+        public PluginIdentity(PluginId pluginId, String pluginVersion) {
+            this.pluginId = pluginId;
+            this.pluginVersion = pluginVersion;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            PluginIdentity that = (PluginIdentity) o;
+
+            if (!pluginId.equals(that.pluginId)) {
+                return false;
+            }
+            if (!pluginVersion.equals(that.pluginVersion)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = pluginId.hashCode();
+            result = 31 * result + pluginVersion.hashCode();
+            return result;
+        }
+    }
+
+    public static class Key<T> {
+        final String portalUrl;
+        final T value;
+
+        public Key(String portalUrl, T value) {
+            this.portalUrl = portalUrl;
+            this.value = value;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Key key = (Key) o;
+
+            if (!portalUrl.equals(key.portalUrl)) {
+                return false;
+            }
+            if (!value.equals(key.value)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = portalUrl.hashCode();
+            result = 31 * result + value.hashCode();
+            return result;
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/OfflinePluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/OfflinePluginResolutionServiceClient.java
new file mode 100644
index 0000000..acdb0c8
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/OfflinePluginResolutionServiceClient.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.plugin.use.resolve.service.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+import java.io.IOException;
+
+public class OfflinePluginResolutionServiceClient implements PluginResolutionServiceClient {
+    public Response<PluginUseMetaData> queryPluginMetadata(String portalUrl, boolean shouldValidate, PluginRequest pluginRequest) {
+        throw new GradleException(String.format("Plugin cannot be resolved from %s because Gradle is running in offline mode", portalUrl));
+    }
+
+    public Response<ClientStatus> queryClientStatus(String portalUrl, boolean shouldValidate, String checksum) {
+        throw new GradleException(String.format("Client status cannot be resolved from %s because Gradle is running in offline mode", portalUrl));
+    }
+
+    public void close() throws IOException {
+
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClient.java
new file mode 100644
index 0000000..bd2e082
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClient.java
@@ -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.plugin.use.resolve.service.internal;
+
+import org.gradle.api.specs.Spec;
+import org.gradle.api.specs.Specs;
+import org.gradle.cache.PersistentCache;
+import org.gradle.cache.PersistentIndexedCache;
+import org.gradle.cache.PersistentIndexedCacheParameters;
+import org.gradle.internal.Factory;
+import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.Serializer;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+import java.io.IOException;
+
+public class PersistentCachingPluginResolutionServiceClient implements PluginResolutionServiceClient {
+
+    public static final String PLUGIN_USE_METADATA_CACHE_NAME = "plugin-use-metadata";
+    public static final String PLUGIN_USE_METADATA_OP_NAME = "queryPluginMetadata";
+    public static final String CLIENT_STATUS_CACHE_NAME = "client-status";
+    public static final String CLIENT_STATUS_OP_NAME = "queryClientStatus";
+
+    private final PluginResolutionServiceClient delegate;
+    private final PersistentCache cacheAccess;
+    private final PersistentIndexedCache<PluginRequestKey, Response<PluginUseMetaData>> pluginUseMetadataCache;
+    private final PersistentIndexedCache<ClientStatusKey, Response<ClientStatus>> clientStatusCache;
+
+    public PersistentCachingPluginResolutionServiceClient(PluginResolutionServiceClient delegate, PersistentCache persistentCache) {
+        this.delegate = delegate;
+        this.cacheAccess = persistentCache;
+        this.pluginUseMetadataCache = persistentCache.createCache(PersistentIndexedCacheParameters.of(
+                        PLUGIN_USE_METADATA_CACHE_NAME, new PluginRequestKey.Serializer(), ResponseSerializer.of(new PluginUseMetaData.Serializer()))
+        );
+        this.clientStatusCache = persistentCache.createCache(PersistentIndexedCacheParameters.of(
+                        CLIENT_STATUS_CACHE_NAME, new ClientStatusKey.Serializer(), ResponseSerializer.of(new ClientStatus.Serializer()))
+        );
+    }
+
+    public Response<PluginUseMetaData> queryPluginMetadata(final String portalUrl, final boolean shouldValidate, final PluginRequest pluginRequest) {
+        PluginRequestKey key = PluginRequestKey.of(portalUrl, pluginRequest);
+        Factory<Response<PluginUseMetaData>> factory = new Factory<Response<PluginUseMetaData>>() {
+            public Response<PluginUseMetaData> create() {
+                return delegate.queryPluginMetadata(portalUrl, shouldValidate, pluginRequest);
+            }
+        };
+
+        if (shouldValidate) {
+            return fetch(PLUGIN_USE_METADATA_OP_NAME, pluginUseMetadataCache, key, factory);
+        } else {
+            return maybeFetch(PLUGIN_USE_METADATA_OP_NAME, pluginUseMetadataCache, key, factory);
+        }
+    }
+
+    public Response<ClientStatus> queryClientStatus(final String portalUrl, final boolean shouldValidate, final String checksum) {
+        ClientStatusKey key = new ClientStatusKey(portalUrl);
+        Factory<Response<ClientStatus>> factory = new Factory<Response<ClientStatus>>() {
+            public Response<ClientStatus> create() {
+                return delegate.queryClientStatus(portalUrl, shouldValidate, checksum);
+            }
+        };
+
+        if (shouldValidate) {
+            return fetch(CLIENT_STATUS_OP_NAME, clientStatusCache, key, factory);
+        } else {
+            return maybeFetch(CLIENT_STATUS_OP_NAME, clientStatusCache, key, factory, new Spec<Response<ClientStatus>>() {
+                public boolean isSatisfiedBy(Response<ClientStatus> element) {
+                    return !element.getClientStatusChecksum().equals(checksum);
+                }
+            });
+        }
+    }
+
+    private <K, V extends Response<?>> V maybeFetch(String operationName, final PersistentIndexedCache<K, V> cache, final K key, Factory<V> factory) {
+        return maybeFetch(operationName, cache, key, factory, Specs.SATISFIES_NONE);
+    }
+
+    private <K, V extends Response<?>> V maybeFetch(String operationName, final PersistentIndexedCache<K, V> cache, final K key, Factory<V> factory, Spec<? super V> shouldFetch) {
+        V cachedValue = cacheAccess.useCache(operationName + " - read", new Factory<V>() {
+            public V create() {
+                return cache.get(key);
+            }
+        });
+
+        boolean fetch = cachedValue == null || shouldFetch.isSatisfiedBy(cachedValue);
+        if (fetch) {
+            return fetch(operationName, cache, key, factory);
+        } else {
+            return cachedValue;
+        }
+    }
+
+    private <K, V extends Response<?>> V fetch(String operationName, final PersistentIndexedCache<K, V> cache, final K key, Factory<V> factory) {
+        final V value = factory.create();
+        if (value.isError()) {
+            return value;
+        }
+
+        cacheAccess.useCache(operationName + " - write", new Runnable() {
+            public void run() {
+                cache.put(key, value);
+            }
+        });
+        return value;
+    }
+
+    public void close() throws IOException {
+        CompositeStoppable.stoppable(delegate, cacheAccess).stop();
+    }
+
+    private static class ResponseSerializer<T> implements Serializer<Response<T>> {
+
+        private final Serializer<T> payloadSerializer;
+
+        private static <T> ResponseSerializer<T> of(Serializer<T> payloadSerializer) {
+            return new ResponseSerializer<T>(payloadSerializer);
+        }
+
+        private ResponseSerializer(Serializer<T> payloadSerializer) {
+            this.payloadSerializer = payloadSerializer;
+        }
+
+        public Response<T> read(Decoder decoder) throws Exception {
+            return new SuccessResponse<T>(
+                    payloadSerializer.read(decoder),
+                    decoder.readSmallInt(),
+                    decoder.readString(),
+                    decoder.readNullableString()
+            );
+        }
+
+        public void write(Encoder encoder, Response<T> value) throws Exception {
+            T response = value.getResponse();
+            payloadSerializer.write(encoder, response);
+            encoder.writeSmallInt(value.getStatusCode());
+            encoder.writeString(value.getUrl());
+            encoder.writeNullableString(value.getClientStatusChecksum());
+        }
+    }
+
+    static class PluginRequestKey {
+        private final String id;
+        private final String version;
+
+        private final String url;
+
+        private static PluginRequestKey of(String url, PluginRequest pluginRequest) {
+            return new PluginRequestKey(pluginRequest.getId().toString(), pluginRequest.getVersion(), url);
+        }
+
+        private PluginRequestKey(String id, String version, String url) {
+            this.id = id;
+            this.version = version;
+            this.url = url;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            PluginRequestKey key = (PluginRequestKey) o;
+
+            return id.equals(key.id) && url.equals(key.url) && version.equals(key.version);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = id.hashCode();
+            result = 31 * result + version.hashCode();
+            result = 31 * result + url.hashCode();
+            return result;
+        }
+
+        private static class Serializer implements org.gradle.internal.serialize.Serializer<PluginRequestKey> {
+
+            public PluginRequestKey read(Decoder decoder) throws Exception {
+                return new PluginRequestKey(decoder.readString(), decoder.readString(), decoder.readString());
+            }
+
+            public void write(Encoder encoder, PluginRequestKey value) throws Exception {
+                encoder.writeString(value.id);
+                encoder.writeString(value.version);
+                encoder.writeString(value.url);
+            }
+        }
+
+    }
+
+    public static class ClientStatusKey {
+
+        private final String portalUrl;
+
+        public ClientStatusKey(String portalUrl) {
+            this.portalUrl = portalUrl;
+        }
+
+        public static class Serializer implements org.gradle.internal.serialize.Serializer<ClientStatusKey> {
+            public ClientStatusKey read(Decoder decoder) throws Exception {
+                return new ClientStatusKey(decoder.readString());
+            }
+
+            public void write(Encoder encoder, ClientStatusKey value) throws Exception {
+                encoder.writeString(value.portalUrl);
+            }
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            ClientStatusKey that = (ClientStatusKey) o;
+
+            return portalUrl.equals(that.portalUrl);
+        }
+
+        @Override
+        public int hashCode() {
+            return portalUrl.hashCode();
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceClient.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceClient.java
new file mode 100644
index 0000000..aa2631b
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceClient.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.plugin.use.resolve.service.internal;
+
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.api.Nullable;
+import org.gradle.plugin.use.internal.PluginRequest;
+
+import java.io.Closeable;
+
+/**
+ * A client for a Gradle Plugin Resolution web service.
+ * <p>
+ * Methods of this interface take a {@code portalUrl} parameter.
+ * This should be the base of the web service address space, without the Gradle version number component.
+ * So, for the public instance this should be {@code "https://plugins.gradle.org/api"}.
+ * <p>
+ * Methods of this interface take a {@code shouldValidate} parameter.
+ * When this parameter is true, implementations SHOULD ensure that the returned data is up to date.
+ * That is, cached responses should not be used.
+ * However, implementations can choose to ignore this.
+ * As such, callers should not always expect this to be honoured.
+ */
+ at ThreadSafe
+public interface PluginResolutionServiceClient extends Closeable {
+
+    /**
+     * Fetch information about a particular plugin at a particular version.
+     * <p>
+     * This maps to the {@code /«gradle version»/plugin/use/«id»/«version»} service.
+     *
+     * @param portalUrl the base url of the web service
+     * @param shouldValidate whether cached information should be validated
+     * @param pluginRequest the plugin identity and version
+     * @return the plugin data
+     */
+    Response<PluginUseMetaData> queryPluginMetadata(String portalUrl, boolean shouldValidate, PluginRequest pluginRequest);
+
+    /**
+     * Fetch status information about the current client.
+     * <p>
+     * This maps to the {@code /«gradle version»} service.
+     * <p>
+     * The {@code checksum} parameter can be used as a hint for the suitability of a cached response.
+     * If {@code shouldValidate} is false and the client has cached response with the given checksum, the cached response may be returned.
+     * This checksum value is provided by {@link PluginResolutionServiceClient.Response#getClientStatusChecksum()} of all responses returned by this interface.
+     * If the checksum value is not known, pass {@code null} which will force a refresh of the status.
+     *
+     * @param portalUrl the base url of the web service
+     * @param shouldValidate whether cached information should be validated
+     * @param checksum the latest checksum value for the status if known, otherwise {@code null}
+     * @return the client status
+     */
+    Response<ClientStatus> queryClientStatus(String portalUrl, boolean shouldValidate, @Nullable String checksum);
+
+    public static interface Response<T> {
+        boolean isError();
+
+        int getStatusCode();
+
+        ErrorResponse getErrorResponse();
+
+        T getResponse();
+
+        String getUrl();
+
+        @Nullable
+        String getClientStatusChecksum();
+    }
+
+    public static class ErrorResponseResponse<T> implements Response<T> {
+        private final ErrorResponse errorResponse;
+        private final int statusCode;
+        private final String url;
+        private final String clientStatusChecksum;
+
+        public ErrorResponseResponse(ErrorResponse errorResponse, int statusCode, String url, String clientStatusChecksum) {
+            this.errorResponse = errorResponse;
+            this.statusCode = statusCode;
+            this.url = url;
+            this.clientStatusChecksum = clientStatusChecksum;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public String getClientStatusChecksum() {
+            return clientStatusChecksum;
+        }
+
+        public boolean isError() {
+            return true;
+        }
+
+        public int getStatusCode() {
+            return statusCode;
+        }
+
+        public ErrorResponse getErrorResponse() {
+            return errorResponse;
+        }
+
+        public T getResponse() {
+            return null;
+        }
+    }
+
+    public static class SuccessResponse<T> implements Response<T> {
+        private final T response;
+        private final int statusCode;
+        private final String url;
+        private final String clientStatusChecksum;
+
+        public SuccessResponse(T response, int statusCode, String url, String clientStatusChecksum) {
+            this.response = response;
+            this.statusCode = statusCode;
+            this.url = url;
+            this.clientStatusChecksum = clientStatusChecksum;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public String getClientStatusChecksum() {
+            return clientStatusChecksum;
+        }
+
+        public boolean isError() {
+            return false;
+        }
+
+        public int getStatusCode() {
+            return statusCode;
+        }
+
+        public ErrorResponse getErrorResponse() {
+            return null;
+        }
+
+        public T getResponse() {
+            return response;
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceResolver.java
new file mode 100644
index 0000000..6926fae
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginResolutionServiceResolver.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal;
+
+import org.gradle.StartParameter;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.internal.artifacts.DependencyResolutionServices;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.internal.plugins.PluginInspector;
+import org.gradle.api.specs.Specs;
+import org.gradle.internal.Factories;
+import org.gradle.internal.Factory;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.exceptions.Contextual;
+import org.gradle.plugin.internal.PluginId;
+import org.gradle.plugin.use.internal.InvalidPluginRequestException;
+import org.gradle.plugin.use.internal.PluginRequest;
+import org.gradle.plugin.use.resolve.internal.*;
+
+import java.io.File;
+import java.util.Set;
+
+public class PluginResolutionServiceResolver implements PluginResolver {
+
+    public static final String OVERRIDE_URL_PROPERTY = PluginResolutionServiceResolver.class.getName() + ".repo.override";
+    private static final String DEFAULT_API_URL = "https://plugins.gradle.org/api/gradle";
+
+    private final PluginResolutionServiceClient portalClient;
+    private final VersionSelectorScheme versionSelectorScheme;
+    private final StartParameter startParameter;
+    private final Factory<DependencyResolutionServices> dependencyResolutionServicesFactory;
+    private final ClassLoaderScope parentScope;
+    private final PluginInspector pluginInspector;
+
+    public PluginResolutionServiceResolver(
+            PluginResolutionServiceClient portalClient,
+            VersionSelectorScheme versionSelectorScheme, StartParameter startParameter,
+            ClassLoaderScope parentScope, Factory<DependencyResolutionServices> dependencyResolutionServicesFactory, PluginInspector pluginInspector
+    ) {
+        this.portalClient = portalClient;
+        this.versionSelectorScheme = versionSelectorScheme;
+        this.startParameter = startParameter;
+        this.parentScope = parentScope;
+        this.dependencyResolutionServicesFactory = dependencyResolutionServicesFactory;
+        this.pluginInspector = pluginInspector;
+    }
+
+    private static String getUrl() {
+        return System.getProperty(OVERRIDE_URL_PROPERTY, DEFAULT_API_URL);
+    }
+
+    public void resolve(PluginRequest pluginRequest, PluginResolutionResult result) throws InvalidPluginRequestException {
+        if (pluginRequest.getVersion() == null) {
+            result.notFound(getDescription(), "plugin dependency must include a version number for this source");
+        } else {
+            if (pluginRequest.getVersion().endsWith("-SNAPSHOT")) {
+                result.notFound(getDescription(), "snapshot plugin versions are not supported");
+            } else if (isDynamicVersion(pluginRequest.getVersion())) {
+                result.notFound(getDescription(), "dynamic plugin versions are not supported");
+            } else {
+                HttpPluginResolutionServiceClient.Response<PluginUseMetaData> response = portalClient.queryPluginMetadata(getUrl(), startParameter.isRefreshDependencies(), pluginRequest);
+                if (response.isError()) {
+                    ErrorResponse errorResponse = response.getErrorResponse();
+                    if (response.getStatusCode() == 404) {
+                        result.notFound(getDescription(), errorResponse.message);
+                    } else {
+                        throw new GradleException(String.format("Plugin resolution service returned HTTP %d with message '%s' (url: %s)", response.getStatusCode(), errorResponse.message, response.getUrl()));
+                    }
+                } else {
+                    PluginUseMetaData metaData = response.getResponse();
+                    if (metaData.legacy) {
+                        handleLegacy(metaData, result);
+                    } else {
+                        ClassPath classPath = resolvePluginDependencies(metaData);
+                        PluginResolution resolution = new ClassPathPluginResolution(pluginRequest.getId(), parentScope, Factories.constant(classPath), pluginInspector);
+                        result.found(getDescription(), resolution);
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleLegacy(final PluginUseMetaData metadata, PluginResolutionResult result) {
+        final PluginId pluginId = PluginId.of(metadata.id);
+        result.found(getDescription(), new PluginResolution() {
+            @Override
+            public PluginId getPluginId() {
+                return pluginId;
+            }
+
+            public void execute(PluginResolveContext context) {
+                context.addLegacy(pluginId, metadata.implementation.get("repo"), metadata.implementation.get("gav"));
+            }
+        });
+    }
+
+    private boolean isDynamicVersion(String version) {
+        return versionSelectorScheme.parseSelector(version).isDynamic();
+    }
+
+    private ClassPath resolvePluginDependencies(final PluginUseMetaData metadata) {
+        DependencyResolutionServices resolution = dependencyResolutionServicesFactory.create();
+
+        RepositoryHandler repositories = resolution.getResolveRepositoryHandler();
+        final String repoUrl = metadata.implementation.get("repo");
+        repositories.maven(new Action<MavenArtifactRepository>() {
+            public void execute(MavenArtifactRepository mavenArtifactRepository) {
+                mavenArtifactRepository.setUrl(repoUrl);
+            }
+        });
+
+        Dependency dependency = resolution.getDependencyHandler().create(metadata.implementation.get("gav"));
+
+        ConfigurationContainerInternal configurations = (ConfigurationContainerInternal) resolution.getConfigurationContainer();
+        ConfigurationInternal configuration = configurations.detachedConfiguration(dependency);
+
+        try {
+            Set<File> files = configuration.getResolvedConfiguration().getFiles(Specs.satisfyAll());
+            return new DefaultClassPath(files);
+        } catch (ResolveException e) {
+            throw new DependencyResolutionException("Failed to resolve all plugin dependencies from " + repoUrl, e.getCause());
+        }
+    }
+
+    public String getDescription() {
+        return "Gradle Central Plugin Repository";
+    }
+
+    @Contextual
+    public static class DependencyResolutionException extends GradleException {
+        public DependencyResolutionException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+}
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginUseMetaData.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginUseMetaData.java
new file mode 100644
index 0000000..96cc137
--- /dev/null
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/service/internal/PluginUseMetaData.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal;
+
+import org.gradle.internal.serialize.BaseSerializerFactory;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+import java.util.Map;
+
+/**
+ * Defines the JSON protocol for plugin resolution service responses to plugin metadata queries.
+ */
+public class PluginUseMetaData {
+
+    public static final String M2_JAR = "M2_JAR";
+
+    public final String id;
+    public final String version;
+    public final Map<String, String> implementation;
+    public final String implementationType;
+    public final boolean legacy;
+
+    public PluginUseMetaData(String id, String version, Map<String, String> implementation, String implementationType, boolean legacy) {
+        this.id = id;
+        this.version = version;
+        this.implementation = implementation;
+        this.implementationType = implementationType;
+        this.legacy = legacy;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PluginUseMetaData)) {
+            return false;
+        }
+
+        PluginUseMetaData that = (PluginUseMetaData) o;
+
+        if (legacy != that.legacy) {
+            return false;
+        }
+        if (!id.equals(that.id)) {
+            return false;
+        }
+        if (!implementation.equals(that.implementation)) {
+            return false;
+        }
+        if (!implementationType.equals(that.implementationType)) {
+            return false;
+        }
+        if (!version.equals(that.version)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id.hashCode();
+        result = 31 * result + version.hashCode();
+        result = 31 * result + implementation.hashCode();
+        result = 31 * result + implementationType.hashCode();
+        result = 31 * result + (legacy ? 1 : 0);
+        return result;
+    }
+
+    public static class Serializer implements org.gradle.internal.serialize.Serializer<PluginUseMetaData> {
+        public PluginUseMetaData read(Decoder decoder) throws Exception {
+            return new PluginUseMetaData(
+                    decoder.readString(),
+                    decoder.readString(),
+                    BaseSerializerFactory.NO_NULL_STRING_MAP_SERIALIZER.read(decoder),
+                    decoder.readString(),
+                    decoder.readBoolean()
+            );
+        }
+
+        public void write(Encoder encoder, PluginUseMetaData value) throws Exception {
+            encoder.writeString(value.id);
+            encoder.writeString(value.version);
+            BaseSerializerFactory.NO_NULL_STRING_MAP_SERIALIZER.write(encoder, value.implementation);
+            encoder.writeString(value.implementationType);
+            encoder.writeBoolean(value.legacy);
+        }
+    }
+}
diff --git a/subprojects/plugin-use/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/plugin-use/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..2d74c24
--- /dev/null
+++ b/subprojects/plugin-use/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.plugin.use.internal.PluginUsePluginServiceRegistry
\ No newline at end of file
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy
new file mode 100644
index 0000000..94c6d9a
--- /dev/null
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.internal
+
+import org.gradle.api.Plugin
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.plugins.PluginRegistry
+import org.gradle.api.internal.plugins.PluginImplementation
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.plugin.internal.PluginId
+import org.gradle.plugin.use.internal.DefaultPluginRequest
+import org.gradle.plugin.use.internal.InvalidPluginRequestException
+import org.gradle.plugin.use.internal.PluginRequest
+import spock.lang.Specification
+
+class CorePluginResolverTest extends Specification {
+
+    static class MyPlugin implements Plugin {
+        @Override
+        void apply(Object target) {
+        }
+    }
+
+    def docRegistry = Mock(DocumentationRegistry)
+    def pluginRegistry = Mock(PluginRegistry)
+    def result = Mock(PluginResolutionResult)
+
+    def resolver = new CorePluginResolver(docRegistry, pluginRegistry)
+
+    PluginRequest request(String id, String version = null) {
+        new DefaultPluginRequest(id, version, 1, new StringScriptSource("test", "test"))
+    }
+
+    def "non core plugins are ignored"() {
+        when:
+        resolver.resolve(request("foo.bar"), result)
+
+        then:
+        1 * result.notFound(resolver.getDescription(), "plugin is not in 'org.gradle' namespace")
+    }
+
+    def "can resolve unqualified"() {
+        when:
+        resolver.resolve(request("foo"), result)
+
+        then:
+        1 * pluginRegistry.lookup(PluginId.of("foo")) >> Mock(PluginImplementation) { asClass() >> MyPlugin }
+        1 * result.found(resolver.getDescription(), { it instanceof SimplePluginResolution && it.plugin.asClass() == MyPlugin })
+    }
+
+    def "can resolve qualified"() {
+        when:
+        resolver.resolve(request("org.gradle.foo"), result)
+
+        then:
+        1 * pluginRegistry.lookup(PluginId.of("org.gradle.foo")) >> Mock(PluginImplementation) { asClass() >> MyPlugin }
+        1 * result.found(resolver.getDescription(), { it instanceof SimplePluginResolution && it.plugin.asClass() == MyPlugin })
+    }
+
+    def "cannot have version number"() {
+        when:
+        resolver.resolve(request("foo", "1.0"), result)
+
+        then:
+        1 * pluginRegistry.lookup(PluginId.of("foo")) >> Mock(PluginImplementation) { asClass() >> MyPlugin }
+
+        and:
+        thrown InvalidPluginRequestException
+    }
+
+    def "ignores plugin with version that isn't found in registry"() {
+        when:
+        resolver.resolve(request("org.gradle.foo", "1.0"), result)
+
+        then:
+        1 * pluginRegistry.lookup(PluginId.of("org.gradle.foo")) >> null
+        1 * result.notFound(resolver.getDescription(), { it.contains("not a core plugin") })
+    }
+
+}
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClientTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClientTest.groovy
new file mode 100644
index 0000000..d3f810c
--- /dev/null
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/DeprecationListeningPluginResolutionServiceClientTest.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.plugin.use.resolve.service.internal
+
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.plugin.use.internal.DefaultPluginRequest
+import org.gradle.plugin.use.internal.PluginRequest
+import spock.lang.Specification
+
+import static org.gradle.plugin.use.resolve.service.internal.DeprecationListeningPluginResolutionServiceClient.toMessage
+
+class DeprecationListeningPluginResolutionServiceClientTest extends Specification {
+
+    public static final String PORTAL_URL_1 = "http://foo"
+    public static final PluginRequest REQUEST_1 = request("foo")
+    public static final String PLUGIN_URL_1 = "$PORTAL_URL_1/foo/1"
+    public static final PluginUseMetaData PLUGIN_METADATA_1 = new PluginUseMetaData("foo", "1", [foo: "bar"], "implType", false)
+    public static final ClientStatus CLIENT_STATUS_1 = new ClientStatus("One")
+    public static final ClientStatus CLIENT_STATUS_2 = new ClientStatus("Two")
+    public static final ErrorResponse ERROR_1 = new ErrorResponse("ERROR", "error")
+
+    def msgs = []
+
+    def delegate = Mock(PluginResolutionServiceClient)
+    def client = new DeprecationListeningPluginResolutionServiceClient(delegate, { msgs << it })
+
+    def "no client status"() {
+        given:
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, null)
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1)
+
+        then:
+        msgs.isEmpty()
+    }
+
+    def "fetches client status"() {
+        def checksum = "foo"
+        given:
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, checksum)
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, checksum) >> new PluginResolutionServiceClient.SuccessResponse<ClientStatus>(CLIENT_STATUS_1, 200, PLUGIN_URL_1, checksum)
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1)
+
+        then:
+        msgs.size() == 1
+        msgs[0] == toMessage(CLIENT_STATUS_1.deprecationMessage, PLUGIN_URL_1)
+    }
+
+    def "ignores error response"() {
+        given:
+        def checksum = "foo"
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, checksum)
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, checksum) >> new PluginResolutionServiceClient.ErrorResponseResponse<PluginUseMetaData>(ERROR_1, 200, PLUGIN_URL_1, checksum)
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1)
+
+        then:
+        noExceptionThrown()
+        msgs.isEmpty()
+    }
+
+    def "ignores exception"() {
+        given:
+        def checksum = "foo"
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, checksum)
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, checksum) >> { throw new Exception("!!!") }
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1)
+
+        then:
+        noExceptionThrown()
+        msgs.isEmpty()
+    }
+
+    static PluginRequest request(String id, String version = "1") {
+        new DefaultPluginRequest(id, version, 1, new StringScriptSource("test", "test"))
+    }
+}
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClientTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClientTest.groovy
new file mode 100644
index 0000000..65ebd77
--- /dev/null
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/HttpPluginResolutionServiceClientTest.groovy
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal
+
+import com.google.gson.Gson
+import org.gradle.api.GradleException
+import org.gradle.internal.resource.transport.http.HttpResourceAccessor
+import org.gradle.internal.resource.transport.http.HttpResponseResource
+import org.gradle.plugin.internal.PluginId
+import org.gradle.plugin.use.internal.PluginRequest
+import org.gradle.util.GradleVersion
+import spock.lang.Specification
+
+class HttpPluginResolutionServiceClientTest extends Specification {
+    public static final String PLUGIN_PORTAL_URL = "http://plugin.portal"
+    private resourceAccessor = Mock(HttpResourceAccessor)
+    private client = new HttpPluginResolutionServiceClient(resourceAccessor)
+    private request = Stub(PluginRequest) {
+        getId() >> PluginId.of("foo")
+    }
+
+    def "returns plugin metadata for successful query"() {
+        given:
+        def metaData = new PluginUseMetaData("foo", "bar", [gav: "foo:bar:baz", repo: "http://repo.com"], PluginUseMetaData.M2_JAR, false)
+
+        when:
+        stubResponse(200, toJson(metaData))
+
+        then:
+        client.queryPluginMetadata(PLUGIN_PORTAL_URL, true, request).response == metaData
+    }
+
+    def "returns client status successful query"() {
+        given:
+        def status = new ClientStatus("message")
+
+        when:
+        stubResponse(200, toJson(status))
+
+        then:
+        client.queryClientStatus(PLUGIN_PORTAL_URL, true, null).response == status
+    }
+
+    def "returns error response for unsuccessful plugin query"() {
+        def errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", "Not feeling well today")
+
+        when:
+        stubResponse(500, toJson(errorResponse))
+        def response = client.queryPluginMetadata(PLUGIN_PORTAL_URL, true, request)
+
+        then:
+        response.error
+        with(response.errorResponse) {
+            errorCode == errorResponse.errorCode
+            message == errorResponse.message
+        }
+        response.statusCode == 500
+    }
+
+    def "returns error response for unsuccessful status query"() {
+        def errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", "Not feeling well today")
+
+        when:
+        stubResponse(500, toJson(errorResponse))
+        def response = client.queryClientStatus(PLUGIN_PORTAL_URL, true, null)
+
+        then:
+        response.error
+        with(response.errorResponse) {
+            errorCode == errorResponse.errorCode
+            message == errorResponse.message
+        }
+        response.statusCode == 500
+    }
+
+    def "only exactly 200 means success"() {
+        when:
+        stubResponse(201, "{}")
+        client.queryPluginMetadata(PLUGIN_PORTAL_URL, true, request)
+
+        then:
+        def e = thrown(GradleException)
+        e.message.contains "unexpected HTTP response status 201"
+    }
+
+    def "outside of 4xx - 5xx is unhanlded"() {
+        when:
+        stubResponse(650, "{}")
+        client.queryPluginMetadata(PLUGIN_PORTAL_URL, true, request)
+
+        then:
+        def e = thrown(GradleException)
+        e.message.contains "unexpected HTTP response status 650"
+    }
+
+    def "id and version are properly encoded"() {
+        given:
+        def customRequest = Stub(PluginRequest) {
+            getId() >> new PluginId("foo/bar")
+            getVersion() >> "1/0"
+        }
+
+        when:
+        client.queryPluginMetadata(PLUGIN_PORTAL_URL, true, customRequest)
+
+        then:
+        1 * resourceAccessor.getRawResource(new URI("$PLUGIN_PORTAL_URL/${GradleVersion.current().getVersion()}/plugin/use/foo%2Fbar/1%2F0")) >> Stub(HttpResponseResource) {
+            getStatusCode() >> 500
+            getContentType() >> "application/json"
+            openStream() >> new ByteArrayInputStream("{errorCode: 'FOO', message: 'BAR'}".getBytes("utf8"))
+        }
+        0 * resourceAccessor.getRawResource(_)
+    }
+
+    private void stubResponse(int statusCode, String jsonResponse = null) {
+        interaction {
+            resourceAccessor.getRawResource(_) >> Stub(HttpResponseResource) {
+                getStatusCode() >> statusCode
+                if (jsonResponse != null) {
+                    getContentType() >> "application/json"
+                    openStream() >> new ByteArrayInputStream(jsonResponse.getBytes("utf8"))
+                }
+            }
+        }
+    }
+
+    private String toJson(Object object) {
+        new Gson().toJson(object)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClientTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClientTest.groovy
new file mode 100644
index 0000000..9057d41
--- /dev/null
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/InMemoryCachingPluginResolutionServiceClientTest.groovy
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal
+
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.plugin.use.internal.DefaultPluginRequest
+import org.gradle.plugin.use.internal.PluginRequest
+import spock.lang.Specification
+
+class InMemoryCachingPluginResolutionServiceClientTest extends Specification {
+
+    public static final String PORTAL_URL_1 = "http://foo"
+    public static final PluginRequest REQUEST_1 = request("foo")
+    public static final String PLUGIN_URL_1 = "$PORTAL_URL_1/foo/1"
+    public static final PluginUseMetaData PLUGIN_METADATA_1 = new PluginUseMetaData("foo", "1", [foo: "bar"], "implType", false)
+    public static final ClientStatus CLIENT_STATUS_1 = new ClientStatus("One")
+    public static final ClientStatus CLIENT_STATUS_2 = new ClientStatus("Two")
+    public static final ErrorResponse ERROR_1 = new ErrorResponse("ERROR", "error")
+
+    def delegate = Mock(PluginResolutionServiceClient)
+
+    def createClient() {
+        new InMemoryCachingPluginResolutionServiceClient(delegate)
+    }
+
+    def "caches delegate success response"() {
+        given:
+        def response = new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, null)
+        def client = createClient()
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+
+        then:
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> response
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+
+        then:
+        0 * delegate._
+    }
+
+    def "does not cache delegate error response"() {
+        given:
+        def response = new PluginResolutionServiceClient.ErrorResponseResponse(ERROR_1, 500, PLUGIN_URL_1, null)
+        def client = createClient()
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+
+        then:
+        2 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> response
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+
+        then:
+        2 * delegate.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1) >> response
+    }
+
+    def "caches client status response"() {
+        given:
+        def response = new PluginResolutionServiceClient.SuccessResponse<ClientStatus>(CLIENT_STATUS_1, 200, PORTAL_URL_1, "1")
+        def changedResponse = new PluginResolutionServiceClient.SuccessResponse<ClientStatus>(CLIENT_STATUS_2, 200, PORTAL_URL_1, "2")
+
+        def client = createClient()
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, null).response == response.response
+        client.queryClientStatus(PORTAL_URL_1, false, null).response == response.response
+
+        then:
+        2 * delegate.queryClientStatus(PORTAL_URL_1, false, null) >> response
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "1").response == response.response
+
+        then:
+        0 * delegate._
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "not 1").response == response.response
+
+        then:
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, "not 1") >> response
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "1").response == response.response
+
+        then:
+        0 * delegate._
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, true, "1").response == changedResponse.response
+
+        then:
+        0 * delegate._
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "2").response == changedResponse.response
+
+        then:
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, "2") >> changedResponse
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, null).response == response
+
+        then:
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, null) >> response
+    }
+
+    static PluginRequest request(String id, String version = "1") {
+        new DefaultPluginRequest(id, version, 1, new StringScriptSource("test", "test"))
+    }
+
+}
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClientTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClientTest.groovy
new file mode 100644
index 0000000..caa1500
--- /dev/null
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/service/internal/PersistentCachingPluginResolutionServiceClientTest.groovy
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service.internal
+
+import org.gradle.cache.PersistentIndexedCache
+import org.gradle.groovy.scripts.StringScriptSource
+import org.gradle.plugin.use.internal.DefaultPluginRequest
+import org.gradle.plugin.use.internal.PluginRequest
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.InMemoryCacheFactory
+import org.junit.Rule
+import spock.lang.Specification
+
+class PersistentCachingPluginResolutionServiceClientTest extends Specification {
+
+    public static final String PORTAL_URL_1 = "http://foo"
+    public static final PluginRequest REQUEST_1 = request("foo")
+    public static final String PLUGIN_URL_1 = "$PORTAL_URL_1/foo/1"
+    public static final PluginUseMetaData PLUGIN_METADATA_1 = new PluginUseMetaData("foo", "1", [foo: "bar"], "implType", false)
+    public static final ClientStatus CLIENT_STATUS_1 = new ClientStatus("One")
+    public static final ClientStatus CLIENT_STATUS_2 = new ClientStatus("Two")
+    public static final ErrorResponse ERROR_1 = new ErrorResponse("ERROR", "error")
+
+    def delegate = Mock(PluginResolutionServiceClient)
+
+    @Rule
+    TestNameTestDirectoryProvider testDirectoryProvider = new TestNameTestDirectoryProvider();
+
+    def caches = new InMemoryCacheFactory.InMemoryCache(testDirectoryProvider.testDirectory)
+
+    PersistentIndexedCache<PersistentCachingPluginResolutionServiceClient.PluginRequestKey, PluginResolutionServiceClient.Response<PluginUseMetaData>> getPluginCache() {
+        caches[PersistentCachingPluginResolutionServiceClient.PLUGIN_USE_METADATA_CACHE_NAME]
+    }
+
+    PersistentIndexedCache<PersistentCachingPluginResolutionServiceClient.ClientStatusKey, PluginResolutionServiceClient.Response<ClientStatus>> getClientStatusCache() {
+        caches[PersistentCachingPluginResolutionServiceClient.CLIENT_STATUS_CACHE_NAME]
+    }
+
+    def createClient() {
+        new PersistentCachingPluginResolutionServiceClient(delegate, caches)
+    }
+
+    def "caches delegate success response"() {
+        given:
+        def response = new PluginResolutionServiceClient.SuccessResponse<PluginUseMetaData>(PLUGIN_METADATA_1, 200, PLUGIN_URL_1, null)
+        def client = createClient()
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+
+        then:
+        1 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> response
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+
+        then:
+        2 * delegate.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1) >> response
+    }
+
+    def "does not cache delegate error response"() {
+        given:
+        def response = new PluginResolutionServiceClient.ErrorResponseResponse(ERROR_1, 500, PLUGIN_URL_1, null)
+        def client = createClient()
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1).response == response.response
+
+        then:
+        2 * delegate.queryPluginMetadata(PORTAL_URL_1, false, REQUEST_1) >> response
+
+        when:
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+        client.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1).response == response.response
+
+        then:
+        2 * delegate.queryPluginMetadata(PORTAL_URL_1, true, REQUEST_1) >> response
+    }
+
+    def "caches client status response"() {
+        given:
+        def response = new PluginResolutionServiceClient.SuccessResponse<ClientStatus>(CLIENT_STATUS_1, 200, PORTAL_URL_1, "1")
+        def changedResponse = new PluginResolutionServiceClient.SuccessResponse<ClientStatus>(CLIENT_STATUS_2, 200, PORTAL_URL_1, "2")
+
+        def client = createClient()
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, null).response == response.response
+        client.queryClientStatus(PORTAL_URL_1, false, null).response == response.response
+
+        then:
+        2 * delegate.queryClientStatus(PORTAL_URL_1, false, null) >> response
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "1").response == response.response
+
+        then:
+        0 * delegate._
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "not 1").response == response.response
+
+        then:
+        1 * delegate.queryClientStatus(PORTAL_URL_1, false, "not 1") >> response
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "1").response == response.response
+
+        then:
+        0 * delegate._
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, true, "1").response == changedResponse.response
+
+        then:
+        1 * delegate.queryClientStatus(PORTAL_URL_1, true, "1") >> changedResponse
+
+        when:
+        client.queryClientStatus(PORTAL_URL_1, false, "2").response == changedResponse.response
+
+        then:
+        0 * delegate._
+    }
+
+    def "closes cache and delegate"() {
+        when:
+        createClient().close()
+
+        then:
+        caches.closed
+        1 * delegate.close()
+    }
+
+    static PluginRequest request(String id, String version = "1") {
+        new DefaultPluginRequest(id, version, 1, new StringScriptSource("test", "test"))
+    }
+
+}
diff --git a/subprojects/plugin-use/src/testFixtures/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceTestServer.groovy b/subprojects/plugin-use/src/testFixtures/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceTestServer.groovy
new file mode 100644
index 0000000..edde6a4
--- /dev/null
+++ b/subprojects/plugin-use/src/testFixtures/groovy/org/gradle/plugin/use/resolve/service/PluginResolutionServiceTestServer.groovy
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.plugin.use.resolve.service
+
+import org.gradle.api.Action
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.plugin.use.resolve.service.internal.ClientStatus
+import org.gradle.plugin.use.resolve.service.internal.HttpPluginResolutionServiceClient
+import org.gradle.plugin.use.resolve.service.internal.PluginResolutionServiceResolver
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.util.ConfigureUtil
+import org.gradle.util.GradleVersion
+import org.junit.rules.ExternalResource
+
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+import static org.gradle.test.fixtures.server.http.HttpServer.Utils.json
+
+class PluginResolutionServiceTestServer extends ExternalResource {
+
+    public final static String API_PATH = "api"
+
+    final HttpServer http
+
+    final MavenHttpRepository m2repo
+    private GradleVersion gradleVersion = GradleVersion.current()
+    private String deprecationMessage
+    private String statusChecksum
+
+    PluginResolutionServiceTestServer(GradleExecuter executer, MavenFileRepository repo) {
+        this.http = new HttpServer()
+        this.m2repo = new MavenHttpRepository(http, repo)
+        configure(executer)
+    }
+
+    public <T extends GradleExecuter> T configure(T executer) {
+        executer.beforeExecute(new Action<GradleExecuter>() {
+            void execute(GradleExecuter e) {
+                if (http.running) {
+                    injectUrlOverride(e)
+                }
+            }
+        })
+        executer
+    }
+
+    public String getApiAddress() {
+        "$http.address/$API_PATH"
+    }
+
+    void injectUrlOverride(GradleExecuter e) {
+        e.withArgument("-D$PluginResolutionServiceResolver.OVERRIDE_URL_PROPERTY=$apiAddress")
+    }
+
+    public <T> T forVersion(GradleVersion gradleVersion, @DelegatesTo(PluginResolutionServiceTestServer) Closure<T> closure) {
+        def previousVersion = this.gradleVersion
+        this.gradleVersion = gradleVersion
+        try {
+            this.with(closure)
+        } finally {
+            this.gradleVersion = previousVersion
+        }
+    }
+
+    void expectNotFound(String pluginId, String version) {
+        expectQueryAndReturnError(pluginId, version, 404) {
+            errorCode = "UNKNOWN_PLUGIN"
+            message = "No plugin is available with id '$pluginId'"
+        }
+    }
+
+    void deprecateClient(String msg) {
+        this.deprecationMessage = msg
+    }
+
+    void statusChecksum(String checksum) {
+        this.statusChecksum = checksum
+    }
+/*
+
+    errorCode: «string», // meaningful known identifier of error type
+    message: «string», // Short description of problem
+    detail: «string», // Longer description of problem (optional)
+    source: «string», //  meaningful known identifier of component that produced error (optional)
+    data: «object», // proprietary dictionary of data, structure of which is known for 'errorCode'
+
+     */
+
+    static class PluginUseResponse {
+        String id
+        String version
+        Boolean legacy
+
+        static class Implementation {
+            String gav
+            String repo
+
+            Implementation(String gav, String repo) {
+                this.gav = gav
+                this.repo = repo
+            }
+        }
+
+        Implementation implementation
+        String implementationType
+
+        PluginUseResponse(String id, String version, Implementation implementation, String implementationType) {
+            this.id = id
+            this.version = version
+            this.implementation = implementation
+            this.implementationType = implementationType
+        }
+    }
+
+    static class MutableErrorResponse {
+        String errorCode = "NONE"
+        String message = "NONE"
+        String detail
+        String source
+        Map data
+    }
+
+    public void expectPluginQuery(String pluginId, String pluginVersion, String group, String artifact, String version,
+                                  @DelegatesTo(value = PluginUseResponse, strategy = Closure.DELEGATE_FIRST) Closure<?> configurer = null) {
+
+        if (!pluginId.contains(".")) {
+            throw new IllegalArgumentException("unqualified plugin id - must be qualified")
+        }
+
+        def useResponse = new PluginUseResponse(pluginId, pluginVersion, new PluginUseResponse.Implementation("$group:$artifact:$version", m2repo.uri.toString()), "M2_JAR")
+
+        if (configurer) {
+            ConfigureUtil.configure(configurer, useResponse)
+        }
+
+        http.expect("/$API_PATH/${gradleVersion.version}/plugin/use/$pluginId/$pluginVersion", ["GET"], new HttpServer.ActionSupport("search action") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                addDeprecationHeader(response)
+                json(response, useResponse)
+            }
+        })
+    }
+
+    public void expectStatusQuery() {
+        http.expect("/$API_PATH/${gradleVersion.version}", ["GET"], new HttpServer.ActionSupport("client status") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                addDeprecationHeader(response)
+                if (deprecationMessage == null) {
+                    json(response, [:])
+                } else {
+                    json(response, new ClientStatus(deprecationMessage))
+                }
+            }
+        })
+    }
+
+    public void expectStatusQuery404() {
+        http.expect("/$API_PATH/${gradleVersion.version}", ["GET"], new HttpServer.ActionSupport("client status") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                response.status = 404
+                addDeprecationHeader(response)
+                json(response, new MutableErrorResponse())
+            }
+        })
+    }
+
+    public void expectStatusQueryOutOfProtocol() {
+        http.expect("/$API_PATH/${gradleVersion.version}", ["GET"], new HttpServer.ActionSupport("client status") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                response.writer.withWriter {
+                    it << "foo"
+                }
+            }
+        })
+    }
+
+    public void expectPluginQuery(String pluginId, String pluginVersion, @DelegatesTo(value = HttpServletResponse, strategy = Closure.DELEGATE_FIRST) Closure<?> configurer) {
+        http.expect("/$API_PATH/${gradleVersion.version}/plugin/use/$pluginId/$pluginVersion", ["GET"], new HttpServer.ActionSupport("search action") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                addDeprecationHeader(response)
+                ConfigureUtil.configure(configurer, response)
+            }
+        })
+    }
+
+    public void expectQueryAndReturnError(String pluginId, String pluginVersion, int httpStatus, @DelegatesTo(value = MutableErrorResponse, strategy = Closure.DELEGATE_FIRST) Closure<?> configurer) {
+        def errorResponse = new MutableErrorResponse()
+        ConfigureUtil.configure(configurer, errorResponse)
+
+        http.expect("/$API_PATH/${gradleVersion.version}/plugin/use/$pluginId/$pluginVersion", ["GET"], new HttpServer.ActionSupport("search action") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                addDeprecationHeader(response)
+                response.status = httpStatus
+                json(response, errorResponse)
+            }
+
+        })
+    }
+
+    private void addDeprecationHeader(HttpServletResponse response) {
+        if (deprecationMessage != null) {
+            response.addHeader(HttpPluginResolutionServiceClient.CLIENT_STATUS_CHECKSUM_HEADER, statusChecksum)
+        }
+    }
+
+    String pluginUrl(String pluginId, String pluginVersion) {
+        "$apiAddress/${gradleVersion.version}/plugin/use/$pluginId/$pluginVersion"
+    }
+
+    void start() {
+        http.start()
+    }
+
+    void stop() {
+        http.stop()
+    }
+
+    @Override
+    protected void after() {
+        http.after()
+    }
+}
diff --git a/subprojects/plugins/plugins.gradle b/subprojects/plugins/plugins.gradle
index cbb72d8..5be1bb9 100644
--- a/subprojects/plugins/plugins.gradle
+++ b/subprojects/plugins/plugins.gradle
@@ -24,10 +24,13 @@ dependencies {
     compile libraries.groovy
 
     compile project(':core')
-    compile project(':coreImpl')
+    compile project(':dependencyManagement')
     compile project(':reporting')
-    compile project(':languageBase')
+    compile project(':platformJvm')
     compile project(':languageJvm')
+    compile project(':languageJava')
+    compile project(':languageGroovy')
+    compile project(':diagnostics')
 
     compile libraries.ant
     compile libraries.asm
@@ -37,11 +40,12 @@ dependencies {
     compile libraries.slf4j_api
     compile 'org.testng:testng:6.3.1'
 
-    provided files(jvm.toolsJar) // for SunJavaCompiler
-
     runtime libraries.commons_cli
 
+    testCompile "com.google.inject:guice:2.0 at jar"
     testCompile libraries.jsoup
+
+    integTestRuntime project(":maven")
 }
 
 evaluationDependsOn(":wrapper")
@@ -58,4 +62,5 @@ test {
 
 useTestFixtures()
 useTestFixtures(sourceSet: "testFixtures")
-useTestFixtures(project: ":coreImpl")
\ No newline at end of file
+useTestFixtures(project: ":dependencyManagement")
+useTestFixtures(project: ':diagnostics')
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy
new file mode 100644
index 0000000..68158c8
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+class ApplicationPluginIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        createSampleProjectSetup()
+    }
+
+    def "can generate start scripts with minimal user configuration"() {
+        when:
+        succeeds('startScripts')
+
+        then:
+        File unixStartScript = assertGeneratedUnixStartScript()
+        String unixStartScriptContent = unixStartScript.text
+        unixStartScriptContent.contains('##  sample start up script for UN*X')
+        unixStartScriptContent.contains('DEFAULT_JVM_OPTS=""')
+        unixStartScriptContent.contains('APP_NAME="sample"')
+        unixStartScriptContent.contains('CLASSPATH=\$APP_HOME/lib/sample.jar')
+        unixStartScriptContent.contains('exec "\$JAVACMD" "\${JVM_OPTS[@]}" -classpath "\$CLASSPATH" org.gradle.test.Main "\$@"')
+        File windowsStartScript = assertGeneratedWindowsStartScript()
+        String windowsStartScriptContentText = windowsStartScript.text
+        windowsStartScriptContentText.contains('@rem  sample startup script for Windows')
+        windowsStartScriptContentText.contains('set DEFAULT_JVM_OPTS=')
+        windowsStartScriptContentText.contains('set CLASSPATH=%APP_HOME%\\lib\\sample.jar')
+        windowsStartScriptContentText.contains('"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %SAMPLE_OPTS%  -classpath "%CLASSPATH%" org.gradle.test.Main %CMD_LINE_ARGS%')
+    }
+
+    def "can generate starts script generation with custom user configuration"() {
+        given:
+        buildFile << """
+applicationName = 'myApp'
+applicationDefaultJvmArgs = ["-Dgreeting.language=en", "-DappId=\${project.name - ':'}"]
+"""
+
+        when:
+        succeeds('startScripts')
+
+        then:
+        File unixStartScript = assertGeneratedUnixStartScript('myApp')
+        String unixStartScriptContent = unixStartScript.text
+        unixStartScriptContent.contains('##  myApp start up script for UN*X')
+        unixStartScriptContent.contains('APP_NAME="myApp"')
+        unixStartScriptContent.contains('DEFAULT_JVM_OPTS=\'"-Dgreeting.language=en" "-DappId=sample"\'')
+        unixStartScriptContent.contains('CLASSPATH=\$APP_HOME/lib/sample.jar')
+        unixStartScriptContent.contains('exec "\$JAVACMD" "\${JVM_OPTS[@]}" -classpath "\$CLASSPATH" org.gradle.test.Main "\$@"')
+        File windowsStartScript = assertGeneratedWindowsStartScript('myApp.bat')
+        String windowsStartScriptContentText = windowsStartScript.text
+        windowsStartScriptContentText.contains('@rem  myApp startup script for Windows')
+        windowsStartScriptContentText.contains('set DEFAULT_JVM_OPTS="-Dgreeting.language=en" "-DappId=sample"')
+        windowsStartScriptContentText.contains('set CLASSPATH=%APP_HOME%\\lib\\sample.jar')
+        windowsStartScriptContentText.contains('"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %MY_APP_OPTS%  -classpath "%CLASSPATH%" org.gradle.test.Main %CMD_LINE_ARGS%')
+    }
+
+    def "can change template file for default start script generators"() {
+        given:
+        file('customUnixStartScript.txt') << '${applicationName} start up script for UN*X'
+        file('customWindowsStartScript.txt') << '${applicationName} start up script for Windows'
+
+        buildFile << """
+startScripts {
+    applicationName = 'myApp'
+    unixStartScriptGenerator.template = resources.text.fromFile(file('customUnixStartScript.txt'))
+    windowsStartScriptGenerator.template = resources.text.fromFile(file('customWindowsStartScript.txt'))
+}
+"""
+        when:
+        succeeds('startScripts')
+
+        then:
+        File unixStartScript = assertGeneratedUnixStartScript('myApp')
+        unixStartScript.text == 'myApp start up script for UN*X'
+        File windowsStartScript = assertGeneratedWindowsStartScript('myApp.bat')
+        windowsStartScript.text == 'myApp start up script for Windows'
+    }
+
+    def "can use custom start script generators"() {
+        given:
+        buildFile << '''
+startScripts {
+    applicationName = 'myApp'
+    unixStartScriptGenerator = new CustomUnixStartScriptGenerator()
+    windowsStartScriptGenerator = new CustomWindowsStartScriptGenerator()
+}
+
+class CustomUnixStartScriptGenerator implements ScriptGenerator {
+    void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination) {
+        destination << "\${details.applicationName} start up script for UN*X"
+    }
+}
+
+class CustomWindowsStartScriptGenerator implements ScriptGenerator {
+    void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination) {
+        destination << "\${details.applicationName} start up script for Windows"
+    }
+}
+'''
+        when:
+        succeeds('startScripts')
+
+        then:
+        File unixStartScript = assertGeneratedUnixStartScript('myApp')
+        unixStartScript.text == 'myApp start up script for UN*X'
+        File windowsStartScript = assertGeneratedWindowsStartScript('myApp.bat')
+        windowsStartScript.text == 'myApp start up script for Windows'
+    }
+
+    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    def "can execute generated Unix start script"() {
+        when:
+        succeeds('installDist')
+
+        then:
+        file('build/install/sample').exists()
+
+        when:
+        TestFile startScriptDir = file('build/install/sample/bin')
+        buildFile << """
+task execStartScript(type: Exec) {
+    workingDir '$startScriptDir.canonicalPath'
+    commandLine './sample'
+}
+"""
+        ExecutionResult result = succeeds('execStartScript')
+
+        then:
+        result.output.contains('Hello World!')
+    }
+
+    @Requires(TestPrecondition.WINDOWS)
+    def "can execute generated Windows start script"() {
+        when:
+        succeeds('installDist')
+
+        then:
+        file('build/install/sample').exists()
+
+        when:
+        TestFile startScriptDir = file('build/install/sample/bin')
+        String escapedStartScriptDir = startScriptDir.canonicalPath.replaceAll('\\\\', '\\\\\\\\')
+        buildFile << """
+task execStartScript(type: Exec) {
+    workingDir '$escapedStartScriptDir'
+    commandLine 'cmd', '/c', 'sample.bat'
+}
+"""
+        ExecutionResult result = succeeds('execStartScript')
+
+        then:
+        result.output.contains('Hello World!')
+    }
+
+    private void createSampleProjectSetup() {
+        createMainClass()
+        populateBuildFile()
+        populateSettingsFile()
+    }
+
+    private void createMainClass() {
+        file('src/main/java/org/gradle/test/Main.java') << """
+package org.gradle.test;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("Hello World!");
+    }
+}
+"""
+    }
+
+    private void populateBuildFile() {
+        buildFile << """
+apply plugin: 'application'
+
+mainClassName = 'org.gradle.test.Main'
+"""
+    }
+
+    private void populateSettingsFile() {
+        settingsFile << """
+rootProject.name = 'sample'
+"""
+    }
+
+    private File assertGeneratedUnixStartScript(String filename = 'sample') {
+        File startScript = getGeneratedStartScript(filename)
+        assert startScript.exists()
+        assert startScript.canRead()
+        assert startScript.canExecute()
+        startScript
+    }
+
+    private File assertGeneratedWindowsStartScript(String filename = 'sample.bat') {
+        File startScript = getGeneratedStartScript(filename)
+        assert startScript.exists()
+        startScript
+    }
+
+    private File getGeneratedStartScript(String filename) {
+        File scriptOutputDir = file('build/scripts')
+        new File(scriptOutputDir, filename)
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
index 9f08e3f..cd363af 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 package org.gradle.api.plugins
-
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
@@ -42,4 +41,29 @@ class BasePluginIntegrationTest extends AbstractIntegrationSpec {
         lock?.release()
     }
 
+    def "can define 'build' and 'check' tasks when applying plugin"() {
+        buildFile << """
+            apply plugin: 'base'
+
+            task build {
+                dependsOn 'check'
+                doLast {
+                    println "CUSTOM BUILD"
+                }
+            }
+
+            task check << {
+                println "CUSTOM CHECK"
+            }
+"""
+        when:
+        executer.withDeprecationChecksDisabled()
+        succeeds "build"
+
+        then:
+        executedAndNotSkipped ":check", ":build"
+        output.contains "CUSTOM CHECK"
+        output.contains "CUSTOM BUILD"
+    }
+
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/DistributionPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/DistributionPluginIntegrationTest.groovy
index 9f00c88..4cb428d 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/DistributionPluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/DistributionPluginIntegrationTest.groovy
@@ -17,16 +17,12 @@
 package org.gradle.api.plugins
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.test.fixtures.maven.MavenPom
 
 import static org.hamcrest.Matchers.containsString
 
 class DistributionPluginIntegrationTest extends WellBehavedPluginTest {
-
     @Override
-    String getPluginId() {
-        "distribution"
-    }
-
     String getMainTask() {
         return "distZip"
     }
@@ -57,6 +53,42 @@ class DistributionPluginIntegrationTest extends WellBehavedPluginTest {
         file("unzip/TestProject-custom/someFile").assertIsFile()
     }
 
+    def "can publish distribution"() {
+        when:
+        buildFile << """
+            apply plugin:'distribution'
+            apply plugin:'maven'
+            group = "org.acme"
+            version = "1.0"
+
+            distributions {
+                main {
+                    contents {
+                        from { "someFile" }
+                    }
+                }
+            }
+
+            uploadArchives {
+                repositories {
+                    mavenDeployer {
+                        repository(url: "${file("repo").toURI()}")
+                    }
+                }
+            }
+            """
+        then:
+        succeeds("uploadArchives")
+        file("repo/org/acme/TestProject/1.0/TestProject-1.0.zip").assertIsFile()
+
+        and:
+        def pom = new MavenPom(file("repo/org/acme/TestProject/1.0/TestProject-1.0.pom"))
+        pom.groupId == "org.acme"
+        pom.artifactId == "TestProject"
+        pom.version == "1.0"
+        pom.packaging == "zip"
+    }
+
     def createTaskForCustomDistributionWithCustomName() {
         when:
         buildFile << """
@@ -99,8 +131,6 @@ class DistributionPluginIntegrationTest extends WellBehavedPluginTest {
         failure.assertThatDescription(containsString("Distribution baseName must not be null or empty! Check your configuration of the distribution plugin."))
     }
 
-
-
     def createDistributionWithoutVersion() {
         given:
         createDir('src/main/dist') {
@@ -126,6 +156,32 @@ class DistributionPluginIntegrationTest extends WellBehavedPluginTest {
         file('build/distributions/myDistribution.zip').exists()
     }
 
+    def assembleAllDistribution() {
+        given:
+        createDir('src/main/dist') {
+            file 'file1.txt'
+            dir2 {
+                file 'file2.txt'
+            }
+        }
+        and:
+        buildFile << """
+            apply plugin:'distribution'
+
+
+            distributions {
+                main{
+                    baseName='myDistribution'
+                }
+            }
+            """
+        when:
+        run('assemble')
+        then:
+        file('build/distributions/myDistribution.zip').exists()
+        file('build/distributions/myDistribution.tar').exists()
+    }
+
     def createDistributionWithVersion() {
         given:
         createDir('src/main/dist') {
@@ -328,6 +384,29 @@ class DistributionPluginIntegrationTest extends WellBehavedPluginTest {
                 'docs/dir2/file4.txt')
     }
 
+    def installDistCanBeRerun() {
+        when:
+        buildFile << """
+            apply plugin:'distribution'
+
+            distributions {
+                custom{
+                    contents {
+                        from { "someFile" }
+                    }
+                }
+            }
+
+            """
+        succeeds('installCustomDist')
+        // update the file so that when it re-runs it is not UP-TO-DATE
+        file("someFile") << "updated"
+        then:
+        succeeds('installCustomDist')
+        and:
+        file('build/install/TestProject-custom/someFile').assertIsCopyOf(file("someFile"))
+    }
+
     def createTarTaskForCustomDistribution() {
         when:
         buildFile << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaLibraryDistributionIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaLibraryDistributionIntegrationTest.groovy
index 967076a..09e03ec 100755
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaLibraryDistributionIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaLibraryDistributionIntegrationTest.groovy
@@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.containsString
 class JavaLibraryDistributionIntegrationTest extends WellBehavedPluginTest {
 
     @Override
-    String getPluginId() {
+    String getPluginName() {
         "java-library-distribution"
     }
 
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ParallelJavaPluginTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ParallelJavaPluginTest.groovy
new file mode 100644
index 0000000..0b3575b
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ParallelJavaPluginTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins
+
+import org.gradle.execution.taskgraph.DefaultTaskExecutionPlan
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.junit.Rule
+import spock.lang.IgnoreIf
+
+/**
+ * This specification runs a java plugin based build that defines additional source sets for building independent jars with
+ * the full lifecycle and running independent test tasks. Such build allows to exercise running multiple tasks from a single project in parallel.
+ */
+ at IgnoreIf({ GradleContextualExecuter.parallel })
+// no point, always runs in parallel
+class ParallelJavaPluginTest extends AbstractIntegrationSpec {
+    @Rule
+    TestResources resources = new TestResources(temporaryFolder)
+
+    def setup() {
+        executer.withArgument("--parallel")
+                .withArgument("--max-workers=4")
+                .withArgument("-D${DefaultTaskExecutionPlan.INTRA_PROJECT_TOGGLE}=true")
+    }
+
+    def "can execute a java build that runs tasks in parallel"() {
+        expect:
+        succeeds "build"
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
index 68fafb1..53bece7 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
@@ -17,6 +17,8 @@
 package org.gradle.api.tasks
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class JavaExecIntegrationTest extends AbstractIntegrationSpec {
@@ -53,7 +55,8 @@ class JavaExecIntegrationTest extends AbstractIntegrationSpec {
             }
         """
     }
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "java exec is not incremental by default"() {
         when:
         run "run"
@@ -69,6 +72,7 @@ class JavaExecIntegrationTest extends AbstractIntegrationSpec {
     }
 
     @Issue("GRADLE-1483")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "when the user declares outputs it becomes incremental"() {
         given:
         buildFile << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyBasePluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyBasePluginIntegrationTest.groovy
index 63fb7d6..081d772 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyBasePluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyBasePluginIntegrationTest.groovy
@@ -18,36 +18,7 @@ package org.gradle.groovy
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
 class GroovyBasePluginIntegrationTest extends AbstractIntegrationSpec {
-    def "defaults groovyClasspath to 'groovy' configuration if the latter is non-empty"() {
-        executer.withDeprecationChecksDisabled()
-        file("build.gradle") << """
-apply plugin: "groovy-base"
-
-sourceSets {
-    custom
-}
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    groovy "org.codehaus.groovy:groovy-all:2.1.2"
-}
-
-task groovydoc(type: Groovydoc)
-
-task verify << {
-    assert compileCustomGroovy.groovyClasspath.is(configurations.groovy)
-    assert groovydoc.groovyClasspath.is(configurations.groovy)
-}
-"""
-
-        expect:
-        succeeds("verify")
-    }
-
-    def "defaults Groovy class path to inferred Groovy dependency if Groovy configuration is empty"() {
+    def "defaults Groovy class path to inferred Groovy dependency"() {
         file("build.gradle") << """
 apply plugin: "groovy-base"
 
@@ -77,10 +48,10 @@ task verify << {
         succeeds("verify")
 
         where:
-        dependency                                  | jarFile
-        "org.codehaus.groovy:groovy-all:2.1.2"      | "groovy-all-2.1.2.jar"
-        "org.codehaus.groovy:groovy:2.1.2"          | "groovy-2.1.2.jar"
-        "org.codehaus.groovy:groovy-all:2.1.2:indy" | "groovy-all-2.1.2-indy.jar"
+        dependency                                   | jarFile
+        "org.codehaus.groovy:groovy-all:2.3.10"      | "groovy-all-2.3.10.jar"
+        "org.codehaus.groovy:groovy:2.3.10"          | "groovy-2.3.10.jar"
+        "org.codehaus.groovy:groovy-all:2.3.10:indy" | "groovy-all-2.3.10-indy.jar"
     }
 
     def "only resolves source class path feeding into inferred Groovy class path if/when the latter is actually used (but not during autowiring)"() {
@@ -96,7 +67,7 @@ repositories {
 }
 
 dependencies {
-    customCompile "org.codehaus.groovy:groovy-all:2.1.2"
+    customCompile "org.codehaus.groovy:groovy-all:2.3.10"
 }
 
 task groovydoc(type: Groovydoc) {
@@ -141,4 +112,5 @@ task verify << {
         then:
         failure.assertHasDescription "Cannot infer Groovy class path because no Groovy Jar was found on class path: configuration ':compile'"
     }
+
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyPluginIntegrationTest.groovy
deleted file mode 100644
index 118b8e2..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/GroovyPluginIntegrationTest.groovy
+++ /dev/null
@@ -1,52 +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.groovy
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class GroovyPluginIntegrationTest extends AbstractIntegrationSpec {
-
-    def groovyConfigurationCanStillBeUsedButIsDeprecated() {
-        given:
-        buildFile << """
-            apply plugin: "groovy"
-
-            repositories {
-                mavenCentral()
-            }
-
-            dependencies {
-                groovy "org.codehaus.groovy:groovy-all:2.0.5"
-                compile "com.google.guava:guava:11.0.2"
-            }
-        """
-
-        file("src/main/groovy/Thing.groovy") << """
-            import com.google.common.base.Strings
-
-            class Thing {}
-        """
-
-        and:
-        executer.withDeprecationChecksDisabled()
-
-        when:
-        succeeds("build")
-
-        then:
-        output.contains("The groovy configuration has been deprecated")
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntForkingGroovyCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntForkingGroovyCompilerIntegrationTest.groovy
deleted file mode 100644
index 4881ba8..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntForkingGroovyCompilerIntegrationTest.groovy
+++ /dev/null
@@ -1,46 +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.groovy.compile
-
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.util.VersionNumber
-
- at TargetVersions(['1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.1.0'])
-class AntForkingGroovyCompilerIntegrationTest extends GroovyCompilerIntegrationSpec {
-
-    @Override
-    String compilerConfiguration() {
-        '''
-    tasks.withType(GroovyCompile) {
-        groovyOptions.useAnt = true
-        groovyOptions.fork = true
-    }
-'''
-    }
-
-    @Override
-    String getCompilationFailureMessage() {
-        return "Forked groovyc returned error code: 1"
-    }
-
-    @Override
-    String getCompileErrorOutput() {
-        if (version == '1.7.11' || versionNumber >= VersionNumber.parse('1.8.0')) {
-            return output
-        }
-        return errorOutput
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntInProcessGroovyCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntInProcessGroovyCompilerIntegrationTest.groovy
deleted file mode 100644
index 20cc575..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/AntInProcessGroovyCompilerIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.groovy.compile
-
-import org.gradle.integtests.fixtures.TargetVersions
-
-// Note that canCompileAgainstGroovyClassThatDependsOnExternalClass() isn't executed
-// for all Groovy versions (but still shown as green). See the (inherited) test method for details.
- at TargetVersions(['1.6.9', '1.7.10', '1.7.11' , '1.8.6', '1.8.8', '2.0.5'])
-class AntInProcessGroovyCompilerIntegrationTest extends BasicGroovyCompilerIntegrationSpec {
-    def setup() {
-        executer.requireGradleHome()
-    }
-
-    @Override
-    def String compilerConfiguration() {
-    '''
-    tasks.withType(GroovyCompile) {
-        groovyOptions.useAnt = true
-        groovyOptions.fork = false
-    }
-'''
-    }
-
-    @Override
-    String getCompilationFailureMessage() {
-        return "Compilation Failed"
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
index a9cb48d..0754848 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
@@ -16,20 +16,27 @@
 package org.gradle.groovy.compile
 
 import com.google.common.collect.Ordering
+import org.gradle.api.Action
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetVersions
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.ExecutionFailure
 import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.util.VersionNumber
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import org.junit.Rule
 
- at TargetVersions(['1.5.8', '1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.1.0'])
+ at TargetVersions(['1.5.8', '1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.1.9', '2.2.2', '2.3.10', '2.4.3'])
 abstract class BasicGroovyCompilerIntegrationSpec extends MultiVersionIntegrationSpec {
-    @Rule TestResources resources = new TestResources(temporaryFolder)
+    @Rule
+    TestResources resources = new TestResources(temporaryFolder)
 
     String groovyDependency = "org.codehaus.groovy:groovy-all:$version"
 
+    String getGroovyVersionNumber() {
+        version.split(":", 2)[0]
+    }
+
     def setup() {
         // necessary for picking up some of the output/errorOutput when forked executer is used
         executer.withArgument("-i")
@@ -48,6 +55,231 @@ abstract class BasicGroovyCompilerIntegrationSpec extends MultiVersionIntegratio
         module << ["groovy-all", "groovy"]
     }
 
+    def "compileWithAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "", // no Java
+                "$annotationText class Groovy {}"
+        )
+
+        then:
+        succeeds("compileGroovy")
+        !errorOutput
+        file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Groovy$$Generated.java').exists()
+        file('build/classes/main/Groovy$$Generated.class').exists()
+    }
+
+    def "compileBadCodeWithAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "", // no Java
+                "$annotationText class Groovy { def m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        file('build/classes/stub/Groovy.java').exists()
+        file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Groovy$$Generated.java').exists()
+        file('build/classes/main/Groovy$$Generated.class').exists()
+    }
+
+    def "compileBadCodeWithoutAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                false,
+                "", // no Java
+                "$annotationText class Groovy { def m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        // No Groovy stubs will be created if there are no java files
+        // and an annotation processor is not on the classpath
+        !file('build/classes/stub/Groovy.java').exists()
+        !file('build/classes/main/Groovy.class').exists()
+        !file('build/classes/main/Groovy$$Generated.java').exists()
+        !file('build/classes/main/Groovy$$Generated.class').exists()
+    }
+
+    def "compileBadCodeWithAnnotationProcessorDisabled"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "", // no Java
+                "$annotationText class Groovy { void m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        buildFile << """
+            compileGroovy {
+                options.compilerArgs << '-proc:none'
+            }
+        """
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        // Because annotation processing is disabled
+        // No Groovy stubs will be created
+        !file('build/classes/stub/Groovy.java').exists()
+        !file('build/classes/main/Groovy.class').exists()
+        !file('build/classes/main/Groovy$$Generated.java').exists()
+        !file('build/classes/main/Groovy$$Generated.class').exists()
+    }
+
+    def "jointCompileBadCodeWithoutAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                false,
+                "public class Java {}",
+                "class Groovy { def m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        // If there is no annotation processor on the classpath,
+        // the Groovy stub class won't be compiled, because it is not
+        // referenced by any java code in the joint compile
+        file('build/classes/stub/Groovy.java').exists()
+        !file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Java.class').exists()
+    }
+
+    def "jointCompileWithAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "$annotationText public class Java {}",
+                "$annotationText class Groovy {}"
+        )
+
+        then:
+        succeeds("compileGroovy")
+        !errorOutput
+        file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Java.class').exists()
+        file('build/classes/main/Groovy$$Generated.java').exists()
+        file('build/classes/main/Java$$Generated.java').exists()
+        file('build/classes/main/Groovy$$Generated.class').exists()
+        file('build/classes/main/Java$$Generated.class').exists()
+    }
+
+    def "jointCompileBadCodeWithAnnotationProcessor"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "$annotationText public class Java {}",
+                "$annotationText class Groovy { void m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        // Because there is an annotation processor on the classpath,
+        // the Java stub of Groovy.groovy will be compiled even if
+        // it's not referenced by any other java code, even if the
+        // Groovy compiler fails to compile the same class.
+        file('build/classes/stub/Groovy.java').exists()
+        file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Java.class').exists()
+        file('build/classes/main/Groovy$$Generated.java').exists()
+        file('build/classes/main/Java$$Generated.java').exists()
+        file('build/classes/main/Groovy$$Generated.class').exists()
+        file('build/classes/main/Java$$Generated.class').exists()
+    }
+
+    def "jointCompileWithAnnotationProcessorDisabled"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "$annotationText public class Java {}",
+                "$annotationText class Groovy { }"
+        )
+
+        buildFile << """
+            compileGroovy {
+                options.compilerArgs << '-proc:none'
+            }
+        """
+
+        then:
+        succeeds("compileGroovy")
+        !errorOutput
+        file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Java.class').exists()
+        !file('build/classes/main/Groovy$$Generated.java').exists()
+        !file('build/classes/main/Java$$Generated.java').exists()
+        !file('build/classes/main/Groovy$$Generated.class').exists()
+        !file('build/classes/main/Java$$Generated.class').exists()
+    }
+
+    def "jointCompileBadCodeWithAnnotationProcessorDisabled"() {
+        when:
+        writeAnnotationProcessingBuild(
+                true,
+                "$annotationText public class Java {}",
+                "$annotationText class Groovy { void m() { $nonCompilableImperativeGroovy } }"
+        )
+
+        buildFile << """
+            compileGroovy {
+                options.compilerArgs << '-proc:none'
+            }
+        """
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains 'unable to resolve class'
+        failure.assertHasCause(compilationFailureMessage)
+
+        // Because annotation processing is disabled
+        // the Groovy class won't be compiled, because it is not
+        // referenced by any java code in the joint compile
+        file('build/classes/stub/Groovy.java').exists()
+        !file('build/classes/main/Groovy.class').exists()
+        file('build/classes/main/Java.class').exists()
+        !file('build/classes/main/Groovy$$Generated.java').exists()
+        !file('build/classes/main/Java$$Generated.java').exists()
+        !file('build/classes/main/Groovy$$Generated.class').exists()
+        !file('build/classes/main/Java$$Generated.class').exists()
+    }
+
+    def "groovyToolClassesAreNotVisible"() {
+        if (versionLowerThan("2.0")) {
+            return
+        }
+
+        groovyDependency = "org.codehaus.groovy:groovy:$version"
+
+        expect:
+        fails("compileGroovy")
+        errorOutput.contains('unable to resolve class AntBuilder')
+
+        when:
+        buildFile << "dependencies { compile 'org.codehaus.groovy:groovy-ant:${version}' }"
+
+        then:
+        succeeds("compileGroovy")
+        !errorOutput
+        file("build/classes/main/Thing.class").exists()
+    }
+
     def "compileBadCode"() {
         expect:
         fails("compileGroovy")
@@ -68,14 +300,6 @@ abstract class BasicGroovyCompilerIntegrationSpec extends MultiVersionIntegratio
     }
 
     def "canCompileAgainstGroovyClassThatDependsOnExternalClass"() {
-        if (getClass() == AntInProcessGroovyCompilerIntegrationTest &&
-                (version == '1.6.9' || version == '1.7.11' || versionNumber >= VersionNumber.parse('1.8.7'))) {
-            // known not to work in 1.7.11, 1.8.7 and beyond (see comment on GRADLE-2404)
-            // only works with 1.6.9 if JUnit makes it on Ant (!) class path, which is no longer the case
-            // note that these problems only apply to useAnt=true; fork=false
-            return
-        }
-
         expect:
         succeeds("test")
     }
@@ -88,6 +312,68 @@ abstract class BasicGroovyCompilerIntegrationSpec extends MultiVersionIntegratio
         !errorOutput
     }
 
+    def "configurationScriptNotSupported"() {
+        if (!versionLowerThan("2.1")) {
+            return
+        }
+
+        expect:
+        fails("compileGroovy")
+        failure.assertHasCause("Using a Groovy compiler configuration script requires Groovy 2.1+ but found Groovy $groovyVersionNumber")
+    }
+
+    def "useConfigurationScript"() {
+        if (versionLowerThan("2.1")) {
+            return
+        }
+
+        expect:
+        fails("compileGroovy")
+        compileErrorOutput.contains('Cannot find matching method java.lang.String#bar()')
+    }
+
+    def "failsBecauseOfMissingConfigFile"() {
+        if (versionLowerThan("2.1")) {
+            return
+        }
+        expect:
+        fails("compileGroovy")
+        failure.assertHasCause("File '${file('groovycompilerconfig.groovy')}' specified for property 'groovyOptions.configurationScript' does not exist.")
+    }
+
+    def "failsBecauseOfInvalidConfigFile"() {
+        if (versionLowerThan("2.1")) {
+            return
+        }
+        expect:
+        fails("compileGroovy")
+        failure.assertHasCause("Could not execute Groovy compiler configuration script: ${file('groovycompilerconfig.groovy')}")
+    }
+
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "compileJavaFx8Code"() {
+        expect:
+        succeeds("compileGroovy")
+    }
+
+    def "cant compile against gradle base services"() {
+        def gradleBaseServicesClass = Action
+        buildScript """
+            apply plugin: 'groovy'
+            repositories { mavenCentral() }
+        """
+
+        when:
+        file("src/main/groovy/Groovy.groovy") << """
+            import ${gradleBaseServicesClass.name}
+            class Groovy {}
+        """
+
+        then:
+        fails("compileGroovy")
+        compileErrorOutput.contains("unable to resolve class ${gradleBaseServicesClass.name}")
+    }
+
     protected ExecutionResult run(String... tasks) {
         configureGroovy()
         super.run(tasks)
@@ -114,9 +400,7 @@ dependencies {
     compile '${groovyDependency.toString()}'
 }
 
-DeprecationLogger.whileDisabled {
-    ${compilerConfiguration()}
-}
+${compilerConfiguration()}
         """
     }
 
@@ -135,9 +419,136 @@ DeprecationLogger.whileDisabled {
     }
 
     int compareToVersion(String other) {
-        def versionParts = version.split("\\.") as List
+        def versionParts = groovyVersionNumber.split("\\.") as List
         def otherParts = other.split("\\.") as List
-        def ordering = Ordering.<Integer>natural().lexicographical()
+        def ordering = Ordering.<Integer> natural().lexicographical()
         ordering.compare(versionParts, otherParts)
     }
+
+    String getAnnotationText() {
+        "@com.test.SimpleAnnotation"
+    }
+
+    String getNonCompilableImperativeGroovy() {
+        "Bad code = new thatDoesntAffectStubGeneration()"
+    }
+
+    def writeAnnotationProcessorProject() {
+        file("processor").create {
+            file("build.gradle") << "apply plugin: 'java'"
+            "src/main" {
+                file("resources/META-INF/services/javax.annotation.processing.Processor") << "com.test.SimpleAnnotationProcessor"
+                "java/com/test/" {
+                    file("SimpleAnnotation.java") << """
+                        package com.test;
+
+                        import java.lang.annotation.ElementType;
+                        import java.lang.annotation.Retention;
+                        import java.lang.annotation.RetentionPolicy;
+                        import java.lang.annotation.Target;
+
+                        @Retention(RetentionPolicy.SOURCE)
+                        @Target(ElementType.TYPE)
+                        public @interface SimpleAnnotation {}
+                    """
+
+                    file("SimpleAnnotationProcessor.java") << """
+                        package com.test;
+
+                        import java.io.BufferedWriter;
+                        import java.io.IOException;
+                        import java.io.Writer;
+                        import java.util.Set;
+
+                        import javax.annotation.processing.AbstractProcessor;
+                        import javax.annotation.processing.RoundEnvironment;
+                        import javax.annotation.processing.SupportedAnnotationTypes;
+                        import javax.lang.model.element.Element;
+                        import javax.lang.model.element.TypeElement;
+                        import javax.lang.model.SourceVersion;
+                        import javax.tools.JavaFileObject;
+
+                        @SupportedAnnotationTypes("com.test.SimpleAnnotation")
+                        public class SimpleAnnotationProcessor extends AbstractProcessor {
+                            @Override
+                            public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
+                                if (isClasspathContaminated()) {
+                                    throw new RuntimeException("Annotation Processor Classpath is contaminated by Gradle ClassLoader");
+                                }
+
+                                for (final Element classElement : roundEnv.getElementsAnnotatedWith(SimpleAnnotation.class)) {
+                                    final String className = String.format("%s\$\$Generated", classElement.getSimpleName().toString());
+
+                                    Writer writer = null;
+                                    try {
+                                        final JavaFileObject file = processingEnv.getFiler().createSourceFile(className);
+
+                                        writer = new BufferedWriter(file.openWriter());
+                                        writer.append(String.format("public class %s {\\n", className));
+                                        writer.append("}");
+                                    } catch (final IOException e) {
+                                        throw new RuntimeException(e);
+                                    } finally {
+                                        if (writer != null) {
+                                            try {
+                                                writer.close();
+                                            } catch (final IOException e) {
+                                                throw new RuntimeException(e);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                return true;
+                            }
+
+                            @Override
+                            public SourceVersion getSupportedSourceVersion() {
+                                return SourceVersion.latestSupported();
+                            }
+
+                            private boolean isClasspathContaminated() {
+                                try {
+                                    Class.forName("$Action.name");
+                                    return true;
+                                } catch (final ClassNotFoundException e) {
+                                    return false;
+                                }
+                            }
+                        }
+                    """
+                }
+            }
+        }
+    }
+
+    def writeAnnotationProcessingBuild(boolean useProcessor, String java, String groovy) {
+        buildFile << """
+            apply plugin: "groovy"
+            repositories { mavenCentral() }
+            compileGroovy {
+                groovyOptions.with {
+                    stubDir = file("\$buildDir/classes/stub")
+                    keepStubs = true
+                }
+            }
+        """
+
+        if (useProcessor) {
+            settingsFile << "include 'processor'"
+            writeAnnotationProcessorProject()
+            buildFile << """
+                dependencies {
+                    compile project(":processor")
+                }
+            """
+        }
+
+        if (java) {
+            file("src/main/groovy/Java.java") << java
+        }
+        if (groovy) {
+            file("src/main/groovy/Groovy.groovy") << groovy
+        }
+    }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec.groovy
index 3fef919..e6caa2e 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec.groovy
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 package org.gradle.groovy.compile
-
 import org.gradle.internal.jvm.Jvm
 import spock.lang.Issue
 
@@ -70,6 +69,22 @@ abstract class GroovyCompilerIntegrationSpec extends BasicGroovyCompilerIntegrat
         noExceptionThrown()
     }
 
+    // This is named funny to keep the path to the project
+    // under Windows's limits.  This checks that we can use
+    // ServletCategory as an extension class when compiling
+    // Groovy code.
+    @Issue("GRADLE-3235")
+    def gradle3235() {
+        if (versionLowerThan('2.0.5')) {
+            return
+        }
+
+        when:
+        run("test")
+
+        then:
+        noExceptionThrown()
+    }
 
     def canJointCompileWithJavaCompilerExecutable() {
         args("-PjdkHome=${Jvm.current().getExecutable('javac')}")
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InProcessGroovyCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InProcessGroovyCompilerIntegrationTest.groovy
index 1acfe67..03abe08 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InProcessGroovyCompilerIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InProcessGroovyCompilerIntegrationTest.groovy
@@ -20,7 +20,6 @@ class InProcessGroovyCompilerIntegrationTest extends ApiGroovyCompilerIntegratio
     String compilerConfiguration() {
 '''
     tasks.withType(GroovyCompile) {
-        groovyOptions.useAnt = false
         groovyOptions.fork = false
     }
 '''
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest.groovy
index ee7ce8e..1956ca2 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest.groovy
@@ -48,4 +48,22 @@ class IncrementalGroovyCompileIntegrationTest extends AbstractIntegrationTest {
         ExecutionFailure failure = executer.withTasks("classes").runWithFailure();
         failure.assertHasDescription("Execution failed for task ':compileGroovy'.");
     }
+
+    @Test
+    public void failsCompilationWhenConfigScriptIsUpdated() {
+        // compilation passes with a config script that does nothing
+        executer.withTasks('compileGroovy').run().assertTasksExecuted(":compileJava",":compileGroovy")
+
+        // make sure it fails if the config script applies type checking
+        file('groovycompilerconfig.groovy').assertIsFile().copyFrom(file('newgroovycompilerconfig.groovy'))
+
+        ExecutionFailure failure = executer.withTasks("compileGroovy").runWithFailure();
+        failure.assertHasCause('Compilation failed; see the compiler error output for details')
+
+        // and eventually make sure it passes again if no config script is applied whatsoever
+        file('build.gradle').assertIsFile().copyFrom(file('newbuild.gradle'))
+
+        executer.withTasks('compileGroovy').run().assertTasksExecuted(':compileJava',':compileGroovy').assertTaskSkipped(':compileJava')
+
+    }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InvokeDynamicGroovyCompilerSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InvokeDynamicGroovyCompilerSpec.groovy
index 3576eda..04402f3 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InvokeDynamicGroovyCompilerSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/InvokeDynamicGroovyCompilerSpec.groovy
@@ -34,7 +34,6 @@ class InvokeDynamicGroovyCompilerSpec extends ApiGroovyCompilerIntegrationSpec {
     String compilerConfiguration() {
         '''
 tasks.withType(GroovyCompile) {
-    groovyOptions.useAnt = false
     groovyOptions.fork = false
 }
         '''
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
index 78a0058..9809e33 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
@@ -27,7 +27,7 @@ class JreJavaHomeGroovyIntegrationTest extends AbstractIntegrationSpec {
 
     @IgnoreIf({ AvailableJavaHomes.bestJre == null})
     @Unroll
-    def "groovy java cross compilation works in forking mode = #forkMode and useAnt = #useAnt when JAVA_HOME is set to JRE"() {
+    def "groovy java cross compilation works in forking mode = #forkMode when JAVA_HOME is set to JRE"() {
         given:
         def jreJavaHome = AvailableJavaHomes.bestJre
         writeJavaTestSource("src/main/groovy")
@@ -40,7 +40,6 @@ class JreJavaHomeGroovyIntegrationTest extends AbstractIntegrationSpec {
                 }
                 compileGroovy{
                     options.fork = ${forkMode}
-                    DeprecationLogger.whileDisabled { options.useAnt = ${useAnt} }
                 }
                 """
         when:
@@ -50,15 +49,12 @@ class JreJavaHomeGroovyIntegrationTest extends AbstractIntegrationSpec {
         file("build/classes/main/org/test/GroovyClazz.class").exists()
 
         where:
-        forkMode | useAnt
-        false    | false
-        false    | true
-        true     | false
+        forkMode << [true, false]
     }
 
     @Requires(TestPrecondition.WINDOWS)
     @Unroll
-    def "groovy compiler works when gradle is started with no JAVA_HOME defined in forking mode = #forkMode and useAnt = #useAnt"() {
+    def "groovy compiler works when gradle is started with no JAVA_HOME defined in forking mode = #forkMode"() {
         given:
         writeJavaTestSource("src/main/groovy")
         writeGroovyTestSource("src/main/groovy")
@@ -69,24 +65,19 @@ class JreJavaHomeGroovyIntegrationTest extends AbstractIntegrationSpec {
             }
             compileGroovy {
                 options.fork = ${forkMode}
-                options.useAnt = ${useAnt}
-                groovyOptions.useAnt = ${useAnt}
             }
             """
         when:
         def envVars = System.getenv().findAll { !(it.key in ['GRADLE_OPTS', 'JAVA_HOME', 'Path']) }
         envVars.put("Path", "C:\\Windows\\System32")
-        executer.withEnvironmentVars(envVars).withDeprecationChecksDisabled().withTasks("compileGroovy").run()
+        executer.withEnvironmentVars(envVars).withTasks("compileGroovy").run()
 
         then:
         file("build/classes/main/org/test/JavaClazz.class").exists()
         file("build/classes/main/org/test/GroovyClazz.class").exists()
 
         where:
-        forkMode | useAnt
-        false    | false
-        false    | true
-        true     | false
+        forkMode << [true, false]
     }
 
     private writeJavaTestSource(String srcDir, String clazzName = "JavaClazz") {
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/ComponentReportIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..d2ba808
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.java
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.reporting.components.AbstractComponentReportIntegrationTest
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private JavaVersion currentJvm = JavaVersion.current()
+    private String currentJava = "java" + currentJvm.majorVersion
+    private String currentJdk = String.format("JDK %s (%s)", currentJvm.majorVersion, currentJvm);
+
+    def "shows details of legacy Java project"() {
+        given:
+        buildFile << """
+plugins {
+    id 'java'
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+No components defined for this project.
+
+Additional source sets
+----------------------
+Java source 'main:java'
+    src/main/java
+JVM resources 'main:resources'
+    src/main/resources
+Java source 'test:java'
+    src/test/java
+JVM resources 'test:resources'
+    src/test/resources
+
+Additional binaries
+-------------------
+Classes 'main'
+    build using task: :classes
+    platform: $currentJava
+    tool chain: $currentJdk
+    classes dir: build/classes/main
+    resources dir: build/resources/main
+Classes 'test'
+    build using task: :testClasses
+    platform: $currentJava
+    tool chain: $currentJdk
+    classes dir: build/classes/test
+    resources dir: build/resources/test
+"""
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy
new file mode 100644
index 0000000..9417d38
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.java
+
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.internal.jvm.JavaInfo
+import org.gradle.util.TextUtil
+import org.junit.Assume
+
+ at TargetVersions(["1.5", "1.6", "1.7", "1.8"])
+class JavaCrossCompilationIntegrationTest extends MultiVersionIntegrationSpec {
+    def JavaInfo getTarget() {
+        return AvailableJavaHomes.getJdk(JavaVersion.toVersion(version))
+    }
+
+    def setup() {
+        Assume.assumeTrue(target != null)
+        def java = TextUtil.escapeString(target.getJavaExecutable())
+        def javac = TextUtil.escapeString(target.getExecutable("javac"))
+        def javadoc = TextUtil.escapeString(target.getExecutable("javadoc"))
+
+        buildFile << """
+apply plugin: 'java'
+sourceCompatibility = ${version}
+targetCompatibility = ${version}
+repositories { mavenCentral() }
+tasks.withType(JavaCompile) {
+    options.with {
+        fork = true
+        forkOptions.executable = "$javac"
+    }
+}
+tasks.withType(Javadoc) {
+    executable = "$javadoc"
+}
+tasks.withType(Test) {
+    executable = "$java"
+}
+"""
+
+        file("src/main/java/Thing.java") << """
+/** Some thing. */
+public class Thing { }
+"""
+    }
+
+    def "can compile source and run JUnit tests using target Java version"() {
+        given:
+        buildFile << """
+dependencies { testCompile 'junit:junit:4.12' }
+"""
+
+        file("src/test/java/ThingTest.java") << """
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ThingTest {
+    @Test
+    public void verify() {
+        assertTrue(System.getProperty("java.version").startsWith("${version}."));
+    }
+}
+"""
+
+        expect:
+        succeeds 'test'
+    }
+
+    def "can compile source and run TestNG tests using target Java version"() {
+        given:
+        buildFile << """
+dependencies { testCompile 'org.testng:testng:6.8.8' }
+"""
+
+        file("src/test/java/ThingTest.java") << """
+import org.testng.annotations.Test;
+
+public class ThingTest {
+    @Test
+    public void verify() {
+        assert System.getProperty("java.version").startsWith("${version}.");
+    }
+}
+"""
+
+        expect:
+        succeeds 'test'
+    }
+
+    def "can generate Javadocs using target Java version"() {
+        expect:
+        succeeds 'javadoc'
+        file('build/docs/javadoc/Thing.html').text.matches("(?s).*Generated by javadoc \\(.*?\\Q${version}.\\E.*")
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaPluginGoodBehaviourTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaPluginGoodBehaviourTest.groovy
deleted file mode 100644
index cad2d31..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaPluginGoodBehaviourTest.groovy
+++ /dev/null
@@ -1,47 +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.java
-
-import org.gradle.integtests.fixtures.WellBehavedPluginTest
-import spock.lang.Issue
-
-class JavaPluginGoodBehaviourTest extends WellBehavedPluginTest {
-    @Override
-    String getMainTask() {
-        return "build"
-    }
-
-    @Issue("http://issues.gradle.org/browse/GRADLE-2851")
-    def "changing debug flag does not produce deprecation warning"() {
-        // We are testing here that Groovy prefers the is*() variant over the get one.
-        // If it prefers the get one we'll get a deprecation warning
-        given:
-        applyPlugin()
-
-        when:
-        buildFile << """
-            compileJava {
-                configure(options) {
-                    def value = debug
-                    value = failOnError
-                }
-            }
-        """
-
-        then:
-        succeeds "tasks"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntForkingJavaCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntForkingJavaCompilerIntegrationTest.groovy
deleted file mode 100644
index 6714cbb..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntForkingJavaCompilerIntegrationTest.groovy
+++ /dev/null
@@ -1,35 +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.java.compile
-
-class AntForkingJavaCompilerIntegrationTest extends BasicJavaCompilerIntegrationSpec {
-    def compilerConfiguration() {
-        '''
-compileJava.options.with {
-    useAnt = true
-    fork = true
-}
-'''
-    }
-
-    def logStatement() {
-        "Compiling with Ant javac task"
-    }
-
-    def getCompilerErrorOutput() {
-        return output
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntInProcessJavaCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntInProcessJavaCompilerIntegrationTest.groovy
deleted file mode 100644
index b4c384f..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AntInProcessJavaCompilerIntegrationTest.groovy
+++ /dev/null
@@ -1,35 +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.java.compile
-
-class AntInProcessJavaCompilerIntegrationTest extends BasicJavaCompilerIntegrationSpec {
-    def compilerConfiguration() {
-        '''
-compileJava.options.with {
-    useAnt = true
-    fork = false
-}
-'''
-    }
-
-    def logStatement() {
-        "Compiling with Ant javac task"
-    }
-
-    def getCompilerErrorOutput() {
-        return output
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
index 641be7e..0614a46 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
@@ -17,17 +17,18 @@
 
 package org.gradle.java.compile
 
+import org.gradle.api.Action
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ClassFile
+import org.gradle.test.fixtures.file.ClassFile
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 
 abstract class BasicJavaCompilerIntegrationSpec extends AbstractIntegrationSpec {
     def setup() {
         executer.withArguments("-i")
         buildFile << buildScript()
         buildFile << """
-DeprecationLogger.whileDisabled {
     ${compilerConfiguration()}
-}
 """
     }
 
@@ -123,6 +124,23 @@ compileJava.options.debug = false
         !noDebug.debugIncludesLocalVariables
     }
 
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "compileJavaFx8Code"() {
+        given:
+        file("src/main/java/compile/test/FxApp.java") << '''
+import javafx.application.Application;
+import javafx.stage.Stage;
+
+public class FxApp extends Application {
+    public void start(Stage stage) {
+    }
+}
+'''
+
+        expect:
+        succeeds("compileJava")
+    }
+
     def getCompilerErrorOutput() {
         return errorOutput
     }
@@ -131,8 +149,12 @@ compileJava.options.debug = false
         '''
 apply plugin: "java"
 
+repositories {
+    mavenCentral()
+}
+
 dependencies {
-    compile localGroovy()
+    compile "org.codehaus.groovy:groovy:2.3.10"
 }
 '''
     }
@@ -148,14 +170,13 @@ package compile.test;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 
 import java.util.Arrays;
-import java.util.List;
 
 public class Person {
     String name;
     int age;
 
     void hello() {
-        List<Integer> vars = Arrays.asList(3, 1, 2);
+        Iterable<Integer> vars = Arrays.asList(3, 1, 2);
         DefaultGroovyMethods.max(vars);
     }
 }'''
@@ -211,5 +232,127 @@ class Main {
     def classFile(String path) {
         return new ClassFile(file(path))
     }
+
+    def "can use annotation processor"() {
+        when:
+        buildFile << """
+            apply plugin: "java"
+            dependencies {
+                compile project(":processor")
+            }
+        """
+        settingsFile << "include 'processor'"
+        writeAnnotationProcessorProject()
+
+        file("src/main/java/Java.java") << "@com.test.SimpleAnnotation public class Java {}"
+
+        then:
+        succeeds("compileJava")
+        file('build/classes/main/Java$$Generated.java').exists()
+    }
+
+    def writeAnnotationProcessorProject() {
+        file("processor").create {
+            file("build.gradle") << "apply plugin: 'java'"
+            "src/main" {
+                file("resources/META-INF/services/javax.annotation.processing.Processor") << "com.test.SimpleAnnotationProcessor"
+                "java/com/test/" {
+                    file("SimpleAnnotation.java") << """
+                        package com.test;
+
+                        import java.lang.annotation.ElementType;
+                        import java.lang.annotation.Retention;
+                        import java.lang.annotation.RetentionPolicy;
+                        import java.lang.annotation.Target;
+
+                        @Retention(RetentionPolicy.SOURCE)
+                        @Target(ElementType.TYPE)
+                        public @interface SimpleAnnotation {}
+                    """
+
+                    file("SimpleAnnotationProcessor.java") << """
+                        package com.test;
+
+                        import java.io.BufferedWriter;
+                        import java.io.IOException;
+                        import java.io.Writer;
+                        import java.util.Set;
+
+                        import javax.annotation.processing.AbstractProcessor;
+                        import javax.annotation.processing.RoundEnvironment;
+                        import javax.annotation.processing.SupportedAnnotationTypes;
+                        import javax.lang.model.element.Element;
+                        import javax.lang.model.element.TypeElement;
+                        import javax.lang.model.SourceVersion;
+                        import javax.tools.JavaFileObject;
+
+                        @SupportedAnnotationTypes("com.test.SimpleAnnotation")
+                        public class SimpleAnnotationProcessor extends AbstractProcessor {
+                            @Override
+                            public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
+                                if (isClasspathContaminated()) {
+                                    throw new RuntimeException("Annotation Processor Classpath is contaminated by Gradle ClassLoader");
+                                }
+
+                                for (final Element classElement : roundEnv.getElementsAnnotatedWith(SimpleAnnotation.class)) {
+                                    final String className = String.format("%s\$\$Generated", classElement.getSimpleName().toString());
+
+                                    Writer writer = null;
+                                    try {
+                                        final JavaFileObject file = processingEnv.getFiler().createSourceFile(className);
+
+                                        writer = new BufferedWriter(file.openWriter());
+                                        writer.append(String.format("public class %s {\\n", className));
+                                        writer.append("}");
+                                    } catch (final IOException e) {
+                                        throw new RuntimeException(e);
+                                    } finally {
+                                        if (writer != null) {
+                                            try {
+                                                writer.close();
+                                            } catch (final IOException e) {
+                                                throw new RuntimeException(e);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                return true;
+                            }
+
+                            @Override
+                            public SourceVersion getSupportedSourceVersion() {
+                                return SourceVersion.latestSupported();
+                            }
+
+                            private boolean isClasspathContaminated() {
+                                try {
+                                    Class.forName("$Action.name");
+                                    return true;
+                                } catch (final ClassNotFoundException e) {
+                                    return false;
+                                }
+                            }
+                        }
+                    """
+                }
+            }
+        }
+    }
+
+    def "cant compile against gradle base services"() {
+        def gradleBaseServicesClass = Action
+
+        when:
+        file("src/main/java/Java.java") << """
+            import ${gradleBaseServicesClass.name};
+            public class Java {}
+        """
+
+        then:
+        fails("compileJava")
+        compilerErrorOutput.contains("package ${gradleBaseServicesClass.package.name} does not exist")
+    }
+
 }
 
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/CommandLineJavaCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/CommandLineJavaCompilerIntegrationTest.groovy
index 3826eeb..fa3b911 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/CommandLineJavaCompilerIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/CommandLineJavaCompilerIntegrationTest.groovy
@@ -29,7 +29,6 @@ class CommandLineJavaCompilerIntegrationTest extends JavaCompilerIntegrationSpec
 
         """
 compileJava.options.with {
-    useAnt = false
     fork = true
     forkOptions.executable = "$executable"
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/InProcessJavaCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/InProcessJavaCompilerIntegrationTest.groovy
index c5daf67..0a39b93 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/InProcessJavaCompilerIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/InProcessJavaCompilerIntegrationTest.groovy
@@ -19,7 +19,6 @@ class InProcessJavaCompilerIntegrationTest extends JavaCompilerIntegrationSpec {
     def compilerConfiguration() {
         '''
 compileJava.options.with {
-    useAnt = false
     fork = false
 }
 '''
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/IncrementalJavaCompilationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/IncrementalJavaCompilationIntegrationTest.groovy
deleted file mode 100644
index c5ba87e..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/IncrementalJavaCompilationIntegrationTest.groovy
+++ /dev/null
@@ -1,219 +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.java.compile
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class IncrementalJavaCompilationIntegrationTest extends AbstractIntegrationSpec {
-
-    def setup() {
-        buildFile << """
-            allprojects {
-                apply plugin: 'java'
-                compileJava.options.incremental = true
-            }
-
-            task cleanFiles(type: Delete) {
-                delete("changedFiles.txt", "unchangedFiles.txt")
-            }
-
-            compileJava {
-                dependsOn cleanFiles
-                def times = [:]
-                doFirst {
-                    fileTree("build/classes/main").each {
-                        if (it.file) {
-                            times[it] = it.lastModified()
-                        }
-                    }
-                }
-                doLast {
-                    sleep(1100) //lastModified granularity
-                    def changedFiles = ""
-                    def unchangedFiles = ""
-                    times.each { k,v ->
-                        if (k.lastModified() != v) {
-                            changedFiles += k.name + ","
-                        } else {
-                            unchangedFiles += k.name + ","
-                        }
-                    }
-                    file("changedFiles.txt").text = changedFiles
-                    file("unchangedFiles.txt").text = unchangedFiles
-                }
-            }
-        """
-
-        file("src/main/java/org/Person.java") << """package org;
-        public interface Person {
-            String getName();
-        }"""
-        file("src/main/java/org/PersonImpl.java") << """package org;
-        public class PersonImpl implements Person {
-            public String getName() { return "Szczepan"; }
-        }"""
-        file("src/main/java/org/AnotherPersonImpl.java") << """package org;
-        public class AnotherPersonImpl extends PersonImpl {
-            public String getName() { return "Szczepan Faber " + WithConst.X; }
-        }"""
-        file("src/main/java/org/WithConst.java") << """package org;
-        public class WithConst {
-            final static int X = 100;
-        }"""
-    }
-
-    Set getChangedFiles() {
-        file("changedFiles.txt").text.split(",").findAll { it.length() > 0 }.collect { it.replaceAll("\\.class", "")}
-    }
-
-    Set getUnchangedFiles() {
-        file("unchangedFiles.txt").text.split(",").findAll { it.length() > 0 }.collect { it.replaceAll("\\.class", "")}
-    }
-
-    def "compiles only a single class that was changed"() {
-        run "compileJava"
-
-        file("src/main/java/org/AnotherPersonImpl.java").text = """package org;
-        public class AnotherPersonImpl implements Person {
-            public String getName() { return "Hans"; }
-        }"""
-
-        when: run "compileJava"
-
-        then: changedFiles == ['AnotherPersonImpl'] as Set
-    }
-
-    def "refreshes the class dependencies with each run"() {
-        run "compileJava"
-
-        file("src/main/java/org/AnotherPersonImpl.java").text = """package org;
-        public class AnotherPersonImpl {}""" //remove the dependency to the interface
-
-        when: run "compileJava"
-
-        then: changedFiles == ['AnotherPersonImpl'] as Set
-
-        when:
-        file("src/main/java/org/Person.java").text = """package org;
-        public interface Person {
-            String getName();
-            String toString();
-        }"""
-        run "compileJava"
-
-        then: changedFiles == ['PersonImpl', 'Person'] as Set
-    }
-
-    def "detects class transitive dependents"() {
-        when: run "compileJava"
-
-        then:
-        changedFiles.empty
-        unchangedFiles.empty
-
-        when:
-        file("src/main/java/org/Person.java").text = """package org;
-        public interface Person {
-            String toString();
-        }"""
-
-        run "compileJava"
-
-        then:
-        changedFiles == ['AnotherPersonImpl', 'PersonImpl', 'Person'] as Set
-    }
-
-    def "is sensitive to deletion and change"() {
-        run "compileJava"
-
-        assert file("src/main/java/org/PersonImpl.java").delete()
-
-        file("src/main/java/org/AnotherPersonImpl.java").text = """package org;
-        public class AnotherPersonImpl implements Person {
-            public String getName() { return "Hans"; }
-        }"""
-
-        when: run "compileJava"
-
-        then:
-        !file("build/classes/main/org/PersonImpl.class").exists()
-        changedFiles == ['AnotherPersonImpl', 'PersonImpl'] as Set
-    }
-
-    def "is sensitive to inlined constants"() {
-        run "compileJava"
-
-        file("src/main/java/org/WithConst.java").text = """package org;
-        public class WithConst {
-            static final int X = 20;
-        }"""
-
-        when: run "compileJava"
-
-        then:
-        unchangedFiles.empty
-        changedFiles.containsAll(['WithConst', 'AnotherPersonImpl', 'PersonImpl', 'Person'])
-    }
-
-    def "is sensitive to source annotations"() {
-        file("src/main/java/org/ClassAnnotation.java").text = """package org; import java.lang.annotation.*;
-            @Retention(RetentionPolicy.RUNTIME) public @interface ClassAnnotation {}
-        """
-        file("src/main/java/org/SourceAnnotation.java").text = """package org; import java.lang.annotation.*;
-            @Retention(RetentionPolicy.SOURCE) public @interface SourceAnnotation {}
-        """
-        file("src/main/java/org/UsesClassAnnotation.java").text = """package org;
-            @ClassAnnotation public class UsesClassAnnotation {}
-        """
-        file("src/main/java/org/UsesSourceAnnotation.java").text = """package org;
-            @SourceAnnotation public class UsesSourceAnnotation {}
-        """
-        run "compileJava"
-
-        file("src/main/java/org/ClassAnnotation.java").text = """package org; import java.lang.annotation.*;
-            @Retention(RetentionPolicy.RUNTIME) public @interface ClassAnnotation {
-                String foo() default "foo";
-            }"""
-
-        when: run "compileJava"
-
-        then:
-        unchangedFiles.empty
-        changedFiles.containsAll(['WithConst', 'AnotherPersonImpl', 'PersonImpl', 'Person'])
-    }
-
-    def "removal of class causes deletion of inner classes"() {
-        file("src/main/java/org/B.java") << """package org;
-            public class B {
-                public static class InnerB {}
-            }
-        """
-
-        when: run "compileJava"
-
-        then:
-        def classes = [file('build/classes/main/org/B.class'), file('build/classes/main/org/B$InnerB.class')]
-        classes.each { assert it.exists() }
-
-        when:
-        assert file("src/main/java/org/B.java").delete()
-        run "compileJava"
-
-        then:
-        classes.each { assert !it.exists() }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/JavaCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/JavaCompilerIntegrationSpec.groovy
index c2e6e1d..715bb93 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/JavaCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/JavaCompilerIntegrationSpec.groovy
@@ -16,6 +16,14 @@
 package org.gradle.java.compile
 
 abstract class JavaCompilerIntegrationSpec extends BasicJavaCompilerIntegrationSpec {
+    def setup() {
+        buildFile << """
+        tasks.withType(JavaCompile) {
+            options.compilerArgs << '-Xlint:all' << '-Werror'
+        }
+"""
+    }
+
     def compileWithLongClasspath() {
         given:
         goodCode()
@@ -23,11 +31,21 @@ abstract class JavaCompilerIntegrationSpec extends BasicJavaCompilerIntegrationS
         and:
         buildFile << '''
             dependencies {
-                compile files((1..999).collect { "$projectDir/lib/library${it}.jar" })
+                file("$projectDir/lib/").mkdirs()
+                compile files((1..999).collect {
+                    createJarFile("$projectDir/lib/library${it}.jar")
+                })
+            }
+
+            def createJarFile(String libraryPath) {
+                new java.util.jar.JarOutputStream(new FileOutputStream(file(libraryPath)), new java.util.jar.Manifest()).withStream {
+                    libraryPath
+                }
             }
         '''
 
         expect:
+
         succeeds("compileJava")
         output.contains(logStatement())
         !errorOutput
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
index efe9017..9371c54 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
@@ -27,7 +27,7 @@ class JreJavaHomeJavaIntegrationTest extends AbstractIntegrationSpec {
 
     @IgnoreIf({ AvailableJavaHomes.bestJre == null })
     @Unroll
-    def "java compilation works in forking mode = #forkMode and useAnt = #useAnt when JAVA_HOME is set to JRE"() {
+    def "java compilation works in forking mode = #forkMode when JAVA_HOME is set to JRE"() {
         given:
         def jreJavaHome = AvailableJavaHomes.bestJre
         writeJavaTestSource("src/main/java");
@@ -36,44 +36,36 @@ class JreJavaHomeJavaIntegrationTest extends AbstractIntegrationSpec {
         apply plugin:'java'
         compileJava {
             options.fork = ${forkMode}
-            options.useAnt = ${useAnt}
         }
         """
         when:
-        executer.withEnvironmentVars("JAVA_HOME": jreJavaHome.absolutePath).withDeprecationChecksDisabled().withTasks("compileJava").run().output
+        executer.withEnvironmentVars("JAVA_HOME": jreJavaHome.absolutePath).withTasks("compileJava").run().output
         then:
         file("build/classes/main/org/test/JavaClazz.class").exists()
 
         where:
-        forkMode | useAnt
-        false    | false
-        false    | true
-        true     | false
+        forkMode << [true, false]
     }
 
     @Requires(TestPrecondition.WINDOWS)
     @Unroll
-    def "java compilation works in forking mode = #forkMode and useAnt = #useAnt when gradle is started with no JAVA_HOME defined"() {
+    def "java compilation works in forking mode = #forkMode when gradle is started with no JAVA_HOME defined"() {
         given:
         writeJavaTestSource("src/main/java");
         file('build.gradle') << """
         apply plugin:'java'
         compileJava {
             options.fork = ${forkMode}
-            options.useAnt = ${useAnt}
         }
         """
         def envVars = System.getenv().findAll { !(it.key in ['GRADLE_OPTS', 'JAVA_HOME', 'Path']) }
         envVars.put("Path", "C:\\Windows\\System32")
         when:
-        executer.withEnvironmentVars(envVars).withDeprecationChecksDisabled().withTasks("compileJava").run()
+        executer.withEnvironmentVars(envVars).withTasks("compileJava").run()
         then:
         file("build/classes/main/org/test/JavaClazz.class").exists()
         where:
-        forkMode | useAnt
-        false    | false
-        false    | true
-        true     | false
+        forkMode << [true, false]
     }
 
     private writeJavaTestSource(String srcDir) {
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy
deleted file mode 100644
index 53c3169..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/javadoc/JavadocIntegrationTest.groovy
+++ /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.javadoc
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.TestResources
-import org.junit.Rule
-import spock.lang.Issue
-
-class JavadocIntegrationTest extends AbstractIntegrationSpec {
-    @Rule TestResources testResources = new TestResources(temporaryFolder)
-
-    @Issue("GRADLE-1563")
-    def handlesTagsAndTaglets() {
-        when:
-        run("javadoc")
-
-        then:
-        def javadoc = testResources.dir.file("build/docs/javadoc/Person.html")
-        javadoc.text =~ /(?ms)This is the Person class.*Author.*author value.*Deprecated.*deprecated value.*Custom Tag.*custom tag value/
-        // we can't currently control the order between tags and taglets (limitation on our side)
-        javadoc.text =~ /(?ms)Custom Taglet.*custom taglet value/
-    }
-
-    @Issue("GRADLE-2520")
-    def canCombineLocalOptionWithOtherOptions() {
-        when:
-        run("javadoc")
-
-        then:
-        def javadoc = testResources.dir.file("build/docs/javadoc/Person.html")
-        javadoc.text =~ /(?ms)USED LOCALE=de_DE/
-        javadoc.text =~ /(?ms)Serial no. is valid javadoc!/
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
index eaf938b..5ee8b40 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
@@ -95,7 +95,7 @@ public class BarTest {
         file("build.gradle") << """
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.10' }
+            dependencies { testCompile 'junit:junit:4.12' }
             test.beforeTest { println "executed " + it }
         """
 
@@ -104,15 +104,15 @@ public class BarTest {
 
         then:
         //asserting on output because test results are kept in between invocations
-        !result.output.contains("executed test test(BarTest)")
-        result.output.contains("executed test test(FooTest)")
+        !result.output.contains("executed Test test(BarTest)")
+        result.output.contains("executed Test test(FooTest)")
 
         when:
         result = executer.withTasks("test", "-Dtest.single=Bar").run()
 
         then:
-        result.output.contains("executed test test(BarTest)")
-        !result.output.contains("executed test test(FooTest)")
+        result.output.contains("executed Test test(BarTest)")
+        !result.output.contains("executed Test test(FooTest)")
 
         when:
         result = executer.withTasks("test", "-Dtest.single=Bar").run()
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
index f554d88..0dd77b0 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
@@ -29,7 +29,7 @@ class SuiteTimestampIntegrationTest extends AbstractIntegrationSpec {
         file("build.gradle") << """
             apply plugin: 'java'
                 repositories { mavenCentral() }
-                dependencies { testCompile 'junit:junit:4.11' }
+                dependencies { testCompile 'junit:junit:4.12' }
         """
 
         file("src/test/java/SomeTest.java") << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy
index cca4a00..0d0e4a8 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy
@@ -58,6 +58,7 @@ class TestEnvironmentIntegrationTest extends AbstractIntegrationSpec {
         result.testClass('org.gradle.JUnitTest').assertTestPassed('mySecurityManagerIsUsed')
     }
 
+    @Requires(TestPrecondition.JDK7_OR_EARLIER)
     def canRunTestsWithJMockitLoadedWithJavaAgent() {
         when:
         run 'test'
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy
index 4d792aa..995d032 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy
@@ -56,7 +56,7 @@ public class SomeTest {
         buildFile << """
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile "junit:junit:4.11" }
+dependencies { testCompile "junit:junit:4.12" }
 
 test.addTestOutputListener(new VerboseOutputListener(logger: project.logger))
 
@@ -84,10 +84,10 @@ class RemoveMeListener implements TestOutputListener {
         def failure = executer.withTasks('test').runWithFailure()
 
         then:
-        failure.output.contains('test showsOutputWhenPassing(SomeTest) StdOut out passing')
-        failure.output.contains('test showsOutputWhenFailing(SomeTest) StdOut out failing')
-        failure.output.contains('test showsOutputWhenPassing(SomeTest) StdErr err passing')
-        failure.output.contains('test showsOutputWhenFailing(SomeTest) StdErr err failing')
+        failure.output.contains('Test showsOutputWhenPassing(SomeTest) StdOut out passing')
+        failure.output.contains('Test showsOutputWhenFailing(SomeTest) StdOut out failing')
+        failure.output.contains('Test showsOutputWhenPassing(SomeTest) StdErr err passing')
+        failure.output.contains('Test showsOutputWhenFailing(SomeTest) StdErr err failing')
 
         !failure.output.contains("remove me!")
     }
@@ -109,7 +109,7 @@ public class SomeTest {
         buildFile << """
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile "junit:junit:4.11" }
+dependencies { testCompile "junit:junit:4.12" }
 
 test.onOutput { descriptor, event ->
     logger.lifecycle("first: " + event.message)
@@ -152,7 +152,7 @@ public class SomeTest {
         buildFile << """
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile "junit:junit:4.11" }
+dependencies { testCompile "junit:junit:4.12" }
 
 test.testLogging {
     showStandardStreams = true
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy
index 3b2d3c6..5fb9833 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy
@@ -17,7 +17,9 @@
 package org.gradle.testing
 
 import org.gradle.integtests.fixtures.*
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.junit.Rule
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 import spock.lang.Unroll
 
@@ -39,11 +41,11 @@ public class LoggingTest {
     @org.junit.Test
     public void test() {
         if (System.getProperty("LogLessStuff", "false").equals("true")) {
-            System.out.print("stdout.");
-            System.err.print("stderr.");
+            System.out.println("stdout.");
+            System.err.println("stderr.");
         } else {
-            System.out.print("This is stdout.");
-            System.err.print("This is stderr.");
+            System.out.println("This is stdout.");
+            System.err.println("This is stderr.");
         }
     }
 }
@@ -54,16 +56,16 @@ public class LoggingTest {
 
         then:
         def result = new HtmlTestExecutionResult(testDirectory)
-        result.testClass("LoggingTest").assertStdout(equalTo("This is stdout."))
-        result.testClass("LoggingTest").assertStderr(equalTo("This is stderr."))
+        result.testClass("LoggingTest").assertStdout(equalTo("This is stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("This is stderr.\n"))
 
         when:
         executer.withArguments("-DLogLessStuff=true")
         run "test"
 
         then:
-        result.testClass("LoggingTest").assertStdout(equalTo("stdout."))
-        result.testClass("LoggingTest").assertStderr(equalTo("stderr."))
+        result.testClass("LoggingTest").assertStdout(equalTo("stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("stderr.\n"))
     }
 
     @UsesSample("testing/testReport")
@@ -80,6 +82,7 @@ public class LoggingTest {
         htmlReport.testClass("org.gradle.sample.UtilTest").assertTestCount(1, 0, 0).assertTestPassed("ok").assertStdout(equalTo("hello from UtilTest.\n"))
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "merges report with duplicated classes and methods"() {
         given:
         buildFile << """
@@ -140,11 +143,11 @@ public class SuperTest {
 $testFilePrelude
 public class SubTest {
     @Category(SubClassTests.class) @Test public void onlySub() {
-        System.out.println("org.gradle.testing.SubTest#onlySub");
+        System.out.println("org.gradle.testing.SubTest#onlySub " + System.getProperty("category"));
         assertEquals("sub", System.getProperty("category"));
     }
     @Category(SubClassTests.class) @Test public void passing() {
-        System.out.println("org.gradle.testing.SubTest#passing");
+        System.out.println("org.gradle.testing.SubTest#passing " + System.getProperty("category"));
     }
 }
 """
@@ -169,10 +172,14 @@ public class SubClassTests extends SuperClassTests {
                 .assertTestFailed("failing", equalTo('java.lang.AssertionError: failing test'))
                 .assertStdout(allOf(containsString('org.gradle.testing.SuperTest#failing\n'), containsString('org.gradle.testing.SuperTest#passing\n')))
         htmlReport.testClass("org.gradle.testing.SubTest").assertTestCount(4, 1, 0).assertTestPassed("passing") // onlySub is passing once and failing once
-                .assertStdout(allOf(containsString('org.gradle.testing.SubTest#passing\n'), containsString('org.gradle.testing.SubTest#onlySub\n')))
+                .assertStdout(allOf(containsString('org.gradle.testing.SubTest#passing sub\n'),
+                        containsString('org.gradle.testing.SubTest#passing super\n'),
+                        containsString('org.gradle.testing.SubTest#onlySub sub\n'),
+                        containsString('org.gradle.testing.SubTest#onlySub super\n')))
     }
 
-    @Issue("http://issues.gradle.org//browse/GRADLE-2821")
+    @Issue("https://issues.gradle.org//browse/GRADLE-2821")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "test report task can handle test tasks that did not run tests"() {
         given:
         buildScript """
@@ -204,7 +211,7 @@ public class SubClassTests extends SuperClassTests {
         new HtmlTestExecutionResult(testDirectory, "build/reports/tr").assertTestClassesExecuted("Thing")
     }
 
-    @Issue("http://issues.gradle.org//browse/GRADLE-2915")
+    @Issue("https://issues.gradle.org//browse/GRADLE-2915")
     def "test report task can handle tests tasks not having been executed"() {
         when:
         buildScript """
@@ -226,6 +233,7 @@ public class SubClassTests extends SuperClassTests {
         succeeds "testReport"
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "test report task is skipped when there are no results"() {
         given:
         buildScript """
@@ -246,6 +254,7 @@ public class SubClassTests extends SuperClassTests {
     }
 
     @Unroll
+    @IgnoreIf({GradleContextualExecuter.parallel})
     "#type report files are considered outputs"() {
         given:
         buildScript """
@@ -283,6 +292,7 @@ public class SubClassTests extends SuperClassTests {
         "html" | "build/reports/tests"
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "results or reports are linked to in error output"() {
         given:
         buildScript """
@@ -320,7 +330,7 @@ public class SubClassTests extends SuperClassTests {
         !errorOutput.contains("See the")
     }
 
-
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "output per test case flag invalidates outputs"() {
         when:
         buildScript """
@@ -411,7 +421,7 @@ public class SubClassTests extends SuperClassTests {
         """
         apply plugin: 'java'
         repositories { mavenCentral() }
-        dependencies { testCompile 'junit:junit:4.11' }
+        dependencies { testCompile 'junit:junit:4.12' }
         """
     }
 
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy
index 2acab66..fea948b 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy
@@ -18,7 +18,7 @@ package org.gradle.testing
 import org.apache.commons.lang.RandomStringUtils
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.internal.os.OperatingSystem
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
 import org.hamcrest.Matchers
@@ -31,13 +31,14 @@ import spock.lang.Unroll
  */
 class TestingIntegrationTest extends AbstractIntegrationSpec {
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-1948")
+    @Issue("https://issues.gradle.org/browse/GRADLE-1948")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "test interrupting its own thread does not kill test execution"() {
         given:
         buildFile << """
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile "junit:junit:4.11" }
+            dependencies { testCompile "junit:junit:4.12" }
         """
 
         and:
@@ -85,7 +86,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
         file('build.gradle') << """
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.10' }
+            dependencies { testCompile 'junit:junit:4.12' }
         """
 
         when:
@@ -128,7 +129,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
             apply plugin: 'java'
             repositories { mavenCentral() }
             dependencies {
-                testCompile 'junit:junit:4.10'
+                testCompile 'junit:junit:4.12'
             }
         """
 
@@ -145,7 +146,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
         results.testClass("ExceptionTest").assertTestFailed("testThrow", Matchers.equalTo('ExceptionTest$BadlyBehavedException: Broken readObject()'))
     }
 
-    @IgnoreIf({ OperatingSystem.current().isWindows() })
+    @Requires(TestPrecondition.NOT_WINDOWS)
     def "can use long paths for working directory"() {
         given:
         // windows can handle a path up to 260 characters
@@ -157,7 +158,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
         buildFile << """
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile "junit:junit:4.11" }
+            dependencies { testCompile "junit:junit:4.12" }
             test.workingDir = "${testWorkingDir.toURI()}"
         """
 
@@ -176,7 +177,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
         succeeds "test"
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2313")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2313")
     @Unroll
     "can clean test after extracting class file with #framework"() {
         when:
@@ -199,11 +200,11 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
 
         where:
         framework   | dependency                | superClass
-        "useJUnit"  | "junit:junit:4.11"        | "org.junit.runner.Result"
+        "useJUnit"  | "junit:junit:4.12"        | "org.junit.runner.Result"
         "useTestNG" | "org.testng:testng:6.3.1" | "org.testng.Converter"
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2527")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2527")
     def "test class detection works for custom test tasks"() {
         given:
         buildFile << """
@@ -218,7 +219,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
                 }
 
                 dependencies{
-	                othertestsCompile "junit:junit:4.11"
+	                othertestsCompile "junit:junit:4.12"
                 }
 
                 task othertestsTest(type:Test){
@@ -251,8 +252,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
         result.assertTestClassesExecuted("TestCaseExtendsAbstractClass")
     }
 
-    @Requires(TestPrecondition.JDK6_OR_LATER) // Guava 15 requires JDK 6
-    @Issue("http://issues.gradle.org/browse/GRADLE-2962")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2962")
     def "incompatible user versions of classes that we also use don't affect test execution"() {
 
         // These dependencies are quite particular.
@@ -282,7 +282,7 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
                 last 'com.google.collections:google-collections:1.0'
                 compile configurations.first + configurations.last
 
-                testCompile 'junit:junit:4.11'
+                testCompile 'junit:junit:4.12'
             }
         """
 
@@ -307,4 +307,55 @@ class TestingIntegrationTest extends AbstractIntegrationSpec {
             assertTestFailed("test", Matchers.containsString("\$EmptyImmutableCollection"))
         }
     }
-}
\ No newline at end of file
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3157")
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    def "test class detection works when '-parameters' compiler option is used (JEP 118)"() {
+        when:
+        buildScript """
+            apply plugin: 'java'
+            repositories {
+                mavenCentral()
+            }
+            dependencies {
+                testCompile 'junit:junit:4.12'
+            }
+            tasks.withType(JavaCompile) {
+                options.with {
+                    compilerArgs << '-parameters'
+                }
+            }
+        """
+
+        and:
+        file("src/test/java/TestHelper.java") << """
+            public class TestHelper {
+                public void helperMethod(String foo, int bar) {
+                    // this method shouldn't cause failure due to API version check
+                    // in org.objectweb.asm.MethodVisitor#visitParameter
+                }
+            }
+        """
+
+        and:
+        file("src/test/java/TestCase.java") << """
+            import org.junit.Test;
+            import static org.junit.Assert.assertTrue;
+            public class TestCase {
+                @Test
+                public void test() {
+                    assertTrue(Double.valueOf(System.getProperty("java.specification.version")) >= 1.8);
+                }
+            }
+        """
+
+        then:
+        run "test"
+
+        and:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("TestCase").with {
+            assertTestCount(1, 0, 0)
+        }
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest.groovy
index 17da4f0..b5bcf73 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest.groovy
@@ -19,9 +19,9 @@ package org.gradle.testing.cucumberjvm
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.junit.Rule
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class CucumberJVMReportIntegrationTest extends AbstractIntegrationSpec {
@@ -29,8 +29,8 @@ class CucumberJVMReportIntegrationTest extends AbstractIntegrationSpec {
     @Rule
     public final TestResources resources = new TestResources(temporaryFolder)
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2739")
-    @Requires(TestPrecondition.JDK6_OR_LATER)
+    @Issue("https://issues.gradle.org/browse/GRADLE-2739")
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def testReportingSupportsCucumberStepsWithSlashes() {
         when:
         run "test"
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy
index 8348ba4..0d2546d 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy
@@ -17,6 +17,8 @@ package org.gradle.testing.fixture
 
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 
 abstract class AbstractTestFilteringIntegrationTest extends MultiVersionIntegrationSpec {
 
@@ -158,6 +160,7 @@ abstract class AbstractTestFilteringIntegrationTest extends MultiVersionIntegrat
         then: failure.assertHasCause("No tests found for given includes: [FooTest.missingMethod]")
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "task is out of date when included methods change"() {
         buildFile << """
             test {
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
index c7d738b..584d0c8 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
@@ -17,9 +17,10 @@
 package org.gradle.testing.fixture;
 
 class JUnitCoverage {
-    final static String NEWEST = '4.11'
+    final static String NEWEST = '4.12'
     final static String[] LARGE_COVERAGE = ['4.0', '4.4', '4.8.2', NEWEST]
     final static String[] STANDARD_COVERAGE = ['4.4', NEWEST]
+    final static String[] LOGGING = [NEWEST]
     final static String[] ASSUMPTIONS = ['4.5', NEWEST]
     final static String[] CATEGORIES = ['4.8', NEWEST]
     final static String[] FILTERING = ['4.6', NEWEST]
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/TestNGCoverage.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/TestNGCoverage.groovy
index b1d756b..675ab21 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/TestNGCoverage.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/fixture/TestNGCoverage.groovy
@@ -18,5 +18,17 @@ package org.gradle.testing.fixture;
 
 class TestNGCoverage {
     final static String NEWEST = '6.8.7'
-    final static String[] STANDARD_COVERAGE = ['5.14.10', NEWEST]
+    final static String[] STANDARD_COVERAGE = ['5.14.10', '6.2', NEWEST]
+
+    /**
+     * Adds java plugin and configures TestNG support in given build script file.
+     */
+    static void enableTestNG(File buildFile) {
+        buildFile << """
+            apply plugin: 'java'
+            repositories { jcenter() }
+            dependencies { testCompile "org.testng:testng:${NEWEST}" }
+            test.useTestNG()
+        """
+    }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy
index 7984559..71fe645 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy
@@ -29,7 +29,7 @@ public class JUnitAssumptionsIntegrationTest extends MultiVersionIntegrationSpec
     @Rule TestResources resources = new TestResources(temporaryFolder)
 
     def supportsAssumptions() {
-        executer.noExtraLogging() //TODO SF check if this is still needed (and in other tests, too)
+        executer.noExtraLogging()
         buildFile << "dependencies { testCompile 'junit:junit:$version' }"
 
         when:
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy
index a12334f..1b4d4c8 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy
@@ -59,6 +59,6 @@ public class JUnitCategoriesIntegrationSpec extends AbstractIntegrationSpec {
 
     def testTaskFailsIfCategoriesNotSupported() {
         when: fails('test')
-        then: failure.error.contains("JUnit Categories defined but declared JUnit version does not support Categories.")
+        then: failure.assertHasCause("JUnit Categories defined but declared JUnit version does not support Categories.")
     }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..ffeacb7
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,121 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.util.TextUtil
+import org.junit.Rule
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+
+// cannot make assumptions about order in which test methods of JUnit4Test get executed
+class JUnitConsoleLoggingIntegrationTest extends AbstractIntegrationSpec {
+    @Rule TestResources resources = new TestResources(temporaryFolder)
+    ExecutionResult result
+
+    def setup() {
+        executer.noExtraLogging().withStackTraceChecksDisabled().withTasks("test")
+    }
+
+    def "defaultLifecycleLogging"() {
+        when:
+        result = executer.runWithFailure()
+
+        then:
+        outputContains("""
+org.gradle.JUnit4Test > badTest FAILED
+    java.lang.RuntimeException at JUnit4Test.groovy:44
+        """)
+    }
+
+    def "customQuietLogging"() {
+        when:
+        result = executer.withArguments("-q").runWithFailure()
+
+        then:
+        outputContains("""
+badTest FAILED
+    java.lang.RuntimeException: bad
+        at org.gradle.JUnit4Test.beBad(JUnit4Test.groovy:44)
+        at org.gradle.JUnit4Test.badTest(JUnit4Test.groovy:28)
+        """)
+
+        outputContains("ignoredTest SKIPPED")
+
+        outputContains("org.gradle.JUnit4Test FAILED")
+    }
+
+    def "standardOutputLogging"() {
+        when:
+        result = executer.withArguments("-q").runWithFailure()
+
+        then:
+        outputContains("""
+org.gradle.JUnit4StandardOutputTest > printTest STANDARD_OUT
+    line 1
+    line 2
+    line 3
+        """)
+    }
+
+    def "test logging is included in XML results"() {
+        file("build.gradle") << """
+            apply plugin: 'java'
+                repositories { mavenCentral() }
+                dependencies { testCompile 'junit:junit:4.12' }
+        """
+
+        file("src/test/java/EncodingTest.java") << """
+import org.junit.*;
+
+public class EncodingTest {
+    @Test public void encodesCdata() {
+        System.out.println("< html allowed, cdata closing token ]]> encoded!");
+        System.out.print("no EOL, ");
+        System.out.println("non-asci char: ż");
+        System.out.println("xml entity: &");
+        System.err.println("< html allowed, cdata closing token ]]> encoded!");
+    }
+    @Test public void encodesAttributeValues() {
+        throw new RuntimeException("html: <> cdata: ]]>");
+    }
+}
+"""
+        when:
+        executer.withTasks("test").runWithFailure()
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+                .testClass("EncodingTest")
+                .assertTestPassed("encodesCdata")
+                .assertTestFailed("encodesAttributeValues", equalTo('java.lang.RuntimeException: html: <> cdata: ]]>'))
+                .assertStdout(equalTo("""< html allowed, cdata closing token ]]> encoded!
+no EOL, non-asci char: ż
+xml entity: &
+"""))
+                .assertStderr(equalTo("< html allowed, cdata closing token ]]> encoded!\n"))
+    }
+
+
+    private void outputContains(String text) {
+        assert result.output.contains(TextUtil.toPlatformLineSeparators(text.trim()))
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringSupportIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringSupportIntegrationTest.groovy
index e949b6e..4ece704 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringSupportIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringSupportIntegrationTest.groovy
@@ -38,7 +38,7 @@ public class JUnitFilteringSupportIntegrationTest extends AbstractIntegrationSpe
         when: fails("test")
 
         then:
-        failure.error.contains("Test filtering is not supported for given version of JUnit. Please upgrade JUnit version to at least 4.6.")
+        failure.assertHasCause("Test filtering is not supported for given version of JUnit. Please upgrade JUnit version to at least 4.6.")
     }
 
     void "informs that we dont support filtering for JUnit 3.x"() {
@@ -57,6 +57,6 @@ public class JUnitFilteringSupportIntegrationTest extends AbstractIntegrationSpe
 
         when: fails("test")
 
-        then: failure.error.contains("Test filtering is not supported for given version of JUnit. Please upgrade JUnit version to at least 4.6.")
+        then: failure.assertHasCause("Test filtering is not supported for given version of JUnit. Please upgrade JUnit version to at least 4.6.")
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy
index 001b498..eb24de7 100755
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy
@@ -15,65 +15,45 @@
  */
 package org.gradle.testing.junit
 
-import org.gradle.integtests.fixtures.AbstractIntegrationTest
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
-import org.junit.Before
 import org.junit.Rule
-import org.junit.Test
+import spock.lang.IgnoreIf
+import spock.lang.Issue
 
 import static org.gradle.util.Matchers.containsLine
-import static org.gradle.util.Matchers.containsText
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 
-public class JUnitIntegrationTest extends AbstractIntegrationTest {
+public class JUnitIntegrationTest extends AbstractIntegrationSpec {
     @Rule
-    public final TestResources resources = new TestResources(testDirectoryProvider)
+    final TestResources resources = new TestResources(testDirectoryProvider)
 
-    @Before
-    public void before() {
+    def setup() {
         executer.noExtraLogging()
     }
 
-    @Test
-    public void executesTestsInCorrectEnvironment() {
-        executer.withTasks('build').run();
+    def executesTestsInCorrectEnvironment() {
+        when:
+        executer.withTasks('build').run()
 
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.OtherTest')
-
         result.testClass('org.gradle.OkTest').assertTestPassed('ok')
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('This is test stdout'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('non-asci char: ż'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('no EOL'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('class loaded'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('test constructed'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('stdout from another thread'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('This is test stderr'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('this is a warning'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('sys out from another test method'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('sys err from another test method'))
-
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('before class out'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('before class err'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('after class out'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('after class err'))
-
         result.testClass('org.gradle.OtherTest').assertTestPassed('ok')
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other class loaded'))
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('other test constructed'))
-        result.testClass('org.gradle.OtherTest').assertStderr(containsString('This is other stderr'))
-        result.testClass('org.gradle.OtherTest').assertStderr(containsString('this is another warning'))
     }
 
-    @Test
-    public void suitesOutputIsVisible() {
-        executer.withTasks('test').run();
+    def suitesOutputIsVisible() {
+        when:
+        executer.withTasks('test').run()
+
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.ASuite', 'org.gradle.OkTest', 'org.gradle.OtherTest')
         result.testClass('org.gradle.ASuite').assertStdout(containsString('suite class loaded'))
@@ -88,30 +68,34 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
         result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
     }
 
-    @Test
-    public void testClassesCanBeSharedByMultipleSuites() {
-        executer.withTasks('test').run();
+    def testClassesCanBeSharedByMultipleSuites() {
+        when:
+        executer.withTasks('test').run()
+
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.SomeTest')
         result.testClass("org.gradle.SomeTest").assertTestCount(2, 0, 0)
         result.testClass("org.gradle.SomeTest").assertTestsExecuted("ok", "ok")
     }
 
-    @Test
-    public void canRunTestsUsingJUnit3() {
+    def canRunTestsUsingJUnit3() {
+        when:
         resources.maybeCopy('JUnitIntegrationTest/junit3Tests')
         executer.withTasks('check').run()
 
+        then:
         def result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.Junit3Test')
         result.testClass('org.gradle.Junit3Test').assertTestsExecuted('testRenamesItself')
         result.testClass('org.gradle.Junit3Test').assertTestPassed('testRenamesItself')
     }
 
-    @Test
-    public void reportsAndBreaksBuildWhenTestFails() {
+    def reportsAndBreaksBuildWhenTestFails() {
+        when:
         executer.withTasks('build').runWithFailure().assertTestsFailed()
 
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted(
                 'org.gradle.ClassWithBrokenRunner',
@@ -139,33 +123,45 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
         result.testClass('org.gradle.BrokenConstructor').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
         result.testClass('org.gradle.BrokenException').assertTestFailed('broken', startsWith('Could not determine failure message for exception of type org.gradle.BrokenException$BrokenRuntimeException: java.lang.UnsupportedOperationException'))
         result.testClass('org.gradle.CustomException').assertTestFailed('custom', startsWith('Exception with a custom toString implementation'))
-        result.testClass('org.gradle.Unloadable').assertTestFailed('initializationError', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.Unloadable').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
+        result.testClass('org.gradle.Unloadable').assertTestFailed('ok2', startsWith('java.lang.NoClassDefFoundError'))
         result.testClass('org.gradle.UnserializableException').assertTestFailed('unserialized', equalTo('org.gradle.UnserializableException$UnserializableRuntimeException: whatever'))
     }
 
-    @Test
-    public void canRunSingleTests() {
+    def canRunSingleTests() {
+        when:
         executer.withTasks('test').withArguments('-Dtest.single=Ok2').run()
+
+        then:
         def result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('Ok2')
 
+        when:
         executer.withTasks('cleanTest', 'test').withArguments('-Dtest.single=Ok').run()
+
+        then:
         result.assertTestClassesExecuted('Ok', 'Ok2')
 
+        when:
         def failure = executer.withTasks('test').withArguments('-Dtest.single=DoesNotMatchAClass').runWithFailure()
+
+        then:
         failure.assertHasCause('Could not find matching test for pattern: DoesNotMatchAClass')
 
+        when:
         failure = executer.withTasks('test').withArguments('-Dtest.single=NotATest').runWithFailure()
+
+        then:
         failure.assertHasCause('Could not find matching test for pattern: NotATest')
     }
 
-    @Test
-    public void canUseTestSuperClassesFromAnotherProject() {
-        testDirectory.file('settings.gradle').write("include 'a', 'b'");
+    def canUseTestSuperClassesFromAnotherProject() {
+        given:
+        testDirectory.file('settings.gradle').write("include 'a', 'b'")
         testDirectory.file('b/build.gradle') << '''
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { compile 'junit:junit:4.7' }
+            dependencies { compile 'junit:junit:4.12' }
         '''
         testDirectory.file('b/src/main/java/org/gradle/AbstractTest.java') << '''
             package org.gradle;
@@ -173,7 +169,7 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
                 @org.junit.Test public void ok() { }
             }
         '''
-        TestFile buildFile = testDirectory.file('a/build.gradle');
+        TestFile buildFile = testDirectory.file('a/build.gradle')
         buildFile << '''
             apply plugin: 'java'
             repositories { mavenCentral() }
@@ -185,20 +181,22 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
             }
         '''
 
-        executer.withTasks('a:test').run();
+        when:
+        executer.withTasks('a:test').run()
 
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory.file('a'))
         result.assertTestClassesExecuted('org.gradle.SomeTest')
         result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
     }
 
-    @Test
-    public void canExcludeSuperClassesFromExecution() {
-        TestFile buildFile = testDirectory.file('build.gradle');
+    def canExcludeSuperClassesFromExecution() {
+        given:
+        TestFile buildFile = testDirectory.file('build.gradle')
         buildFile << '''
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.7' }
+            dependencies { testCompile 'junit:junit:4.12' }
             test { exclude '**/BaseTest.*' }
         '''
         testDirectory.file('src/test/java/org/gradle/BaseTest.java') << '''
@@ -213,17 +211,20 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
             }
         '''
 
-        executer.withTasks('test').run();
+        when:
+        executer.withTasks('test').run()
 
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.SomeTest')
         result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
     }
 
-    @Test
-    public void detectsTestClasses() {
+    def detectsTestClasses() {
+        when:
         executer.withTasks('test').run()
 
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner', 'org.gradle.TestsOnInner$SomeInner')
         result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
@@ -232,13 +233,24 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
         result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
     }
 
-    @Test
-    public void runsAllTestsInTheSameForkedJvm() {
+    @Issue("https://issues.gradle.org//browse/GRADLE-3114")
+    def createsRunnerBeforeTests() {
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.ExecutionOrderTest')
+        result.testClass('org.gradle.ExecutionOrderTest').assertTestPassed('classUnderTestIsLoadedOnlyByRunner')
+    }
+
+    def runsAllTestsInTheSameForkedJvm() {
+        given:
         testDirectory.file('build.gradle').writelns(
                 "apply plugin: 'java'",
                 "repositories { mavenCentral() }",
-                "dependencies { compile 'junit:junit:4.7' }"
-        );
+                "dependencies { compile 'junit:junit:4.12' }"
+        )
         testDirectory.file('src/test/java/org/gradle/AbstractTest.java').writelns(
                 "package org.gradle;",
                 "public abstract class AbstractTest {",
@@ -246,33 +258,35 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
                 "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
                 "        System.out.println(String.format(\"VM START TIME = %s\", time));",
                 "    }",
-                "}");
+                "}")
         testDirectory.file('src/test/java/org/gradle/SomeTest.java').writelns(
                 "package org.gradle;",
                 "public class SomeTest extends AbstractTest {",
-                "}");
+                "}")
         testDirectory.file('src/test/java/org/gradle/SomeTest2.java').writelns(
                 "package org.gradle;",
                 "public class SomeTest2 extends AbstractTest {",
-                "}");
+                "}")
 
-        executer.withTasks('test').run();
+        when:
+        executer.withTasks('test').run()
 
-        TestFile results1 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest.xml');
-        TestFile results2 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
-        results1.assertIsFile();
-        results2.assertIsFile();
-        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)));
+        then:
+        TestFile results1 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest.xml')
+        TestFile results2 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest2.xml')
+        results1.assertIsFile()
+        results2.assertIsFile()
+        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)))
     }
 
-    @Test
-    public void canSpecifyMaximumNumberOfTestClassesToExecuteInAForkedJvm() {
+    def canSpecifyMaximumNumberOfTestClassesToExecuteInAForkedJvm() {
+        given:
         testDirectory.file('build.gradle').writelns(
                 "apply plugin: 'java'",
                 "repositories { mavenCentral() }",
-                "dependencies { compile 'junit:junit:4.7' }",
+                "dependencies { compile 'junit:junit:4.12' }",
                 "test.forkEvery = 1"
-        );
+        )
         testDirectory.file('src/test/java/org/gradle/AbstractTest.java').writelns(
                 "package org.gradle;",
                 "public abstract class AbstractTest {",
@@ -280,31 +294,33 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
                 "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
                 "        System.out.println(String.format(\"VM START TIME = %s\", time));",
                 "    }",
-                "}");
+                "}")
         testDirectory.file('src/test/java/org/gradle/SomeTest.java').writelns(
                 "package org.gradle;",
                 "public class SomeTest extends AbstractTest {",
-                "}");
+                "}")
         testDirectory.file('src/test/java/org/gradle/SomeTest2.java').writelns(
                 "package org.gradle;",
                 "public class SomeTest2 extends AbstractTest {",
-                "}");
+                "}")
 
-        executer.withTasks('test').run();
+        when:
+        executer.withTasks('test').run()
 
-        TestFile results1 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest.xml');
-        TestFile results2 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest2.xml');
-        results1.assertIsFile();
-        results2.assertIsFile();
+        then:
+        TestFile results1 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest.xml')
+        TestFile results2 = testDirectory.file('build/test-results/TEST-org.gradle.SomeTest2.xml')
+        results1.assertIsFile()
+        results2.assertIsFile()
         assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), not(equalTo(results2.linesThat(
-                containsString('VM START TIME =')).get(0))));
+                containsString('VM START TIME =')).get(0))))
     }
 
-    @Test
-    public void canListenForTestResults() {
+    def canListenForTestResults() {
+        given:
         testDirectory.file('src/main/java/AppException.java').writelns(
                 "public class AppException extends Exception { }"
-        );
+        )
 
         testDirectory.file('src/test/java/SomeTest.java').writelns(
                 "public class SomeTest {",
@@ -312,17 +328,17 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
                 "@org.junit.Test public void knownError() { throw new RuntimeException(\"message\"); }",
                 "@org.junit.Test public void unknownError() throws AppException { throw new AppException(); }",
                 "}"
-        );
+        )
         testDirectory.file('src/test/java/SomeOtherTest.java').writelns(
                 "public class SomeOtherTest {",
                 "@org.junit.Test public void pass() { }",
                 "}"
-        );
+        )
 
         testDirectory.file('build.gradle') << '''
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.7' }
+            dependencies { testCompile 'junit:junit:4.12' }
             def listener = new TestListenerImpl()
             test.addTestListener(listener)
             test.ignoreFailures = true
@@ -334,37 +350,40 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
             }
         '''
 
-        ExecutionResult result = executer.withTasks("test").run();
-        assert containsLine(result.getOutput(), "START [tests] [Test Run]");
-        assert containsLine(result.getOutput(), "FINISH [tests] [Test Run] [FAILURE] [4]");
-
-        assert containsLine(result.getOutput(), "START [process 'Gradle Test Executor 1'] [Gradle Test Executor 1]");
-        assert containsLine(result.getOutput(), "FINISH [process 'Gradle Test Executor 1'] [Gradle Test Executor 1] [FAILURE] [4]");
-
-        assert containsLine(result.getOutput(), "START [test class SomeOtherTest] [SomeOtherTest]");
-        assert containsLine(result.getOutput(), "FINISH [test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]");
-        assert containsLine(result.getOutput(), "START [test pass(SomeOtherTest)] [pass]");
-        assert containsLine(result.getOutput(), "FINISH [test pass(SomeOtherTest)] [pass] [SUCCESS] [1] [null]");
-
-        assert containsLine(result.getOutput(), "START [test class SomeTest] [SomeTest]");
-        assert containsLine(result.getOutput(), "FINISH [test class SomeTest] [SomeTest] [FAILURE] [3]");
-        assert containsLine(result.getOutput(), "START [test fail(SomeTest)] [fail]");
-        assert containsLine(result.getOutput(), "FINISH [test fail(SomeTest)] [fail] [FAILURE] [1] [java.lang.AssertionError: message]");
-        assert containsLine(result.getOutput(), "START [test knownError(SomeTest)] [knownError]");
-        assert containsLine(result.getOutput(), "FINISH [test knownError(SomeTest)] [knownError] [FAILURE] [1] [java.lang.RuntimeException: message]");
-        assert containsLine(result.getOutput(), "START [test unknownError(SomeTest)] [unknownError]");
-        assert containsLine(result.getOutput(), "FINISH [test unknownError(SomeTest)] [unknownError] [FAILURE] [1] [AppException]");
+        when:
+        ExecutionResult result = executer.withTasks("test").run()
+
+        then:
+        assert containsLine(result.getOutput(), "START [Gradle Test Run :test] [Gradle Test Run :test]")
+        assert containsLine(result.getOutput(), "FINISH [Gradle Test Run :test] [Gradle Test Run :test] [FAILURE] [4]")
+
+        assert containsLine(result.getOutput(), "START [Gradle Test Executor 1] [Gradle Test Executor 1]")
+        assert containsLine(result.getOutput(), "FINISH [Gradle Test Executor 1] [Gradle Test Executor 1] [FAILURE] [4]")
+
+        assert containsLine(result.getOutput(), "START [Test class SomeOtherTest] [SomeOtherTest]")
+        assert containsLine(result.getOutput(), "FINISH [Test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]")
+        assert containsLine(result.getOutput(), "START [Test pass(SomeOtherTest)] [pass]")
+        assert containsLine(result.getOutput(), "FINISH [Test pass(SomeOtherTest)] [pass] [SUCCESS] [1] [null]")
+
+        assert containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
+        assert containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest] [FAILURE] [3]")
+        assert containsLine(result.getOutput(), "START [Test fail(SomeTest)] [fail]")
+        assert containsLine(result.getOutput(), "FINISH [Test fail(SomeTest)] [fail] [FAILURE] [1] [java.lang.AssertionError: message]")
+        assert containsLine(result.getOutput(), "START [Test knownError(SomeTest)] [knownError]")
+        assert containsLine(result.getOutput(), "FINISH [Test knownError(SomeTest)] [knownError] [FAILURE] [1] [java.lang.RuntimeException: message]")
+        assert containsLine(result.getOutput(), "START [Test unknownError(SomeTest)] [unknownError]")
+        assert containsLine(result.getOutput(), "FINISH [Test unknownError(SomeTest)] [unknownError] [FAILURE] [1] [AppException]")
     }
 
-    @Test
-    public void canListenForTestResultsWhenJUnit3IsUsed() {
+    def canListenForTestResultsWhenJUnit3IsUsed() {
+        given:
         testDirectory.file('src/test/java/SomeTest.java').writelns(
                 "public class SomeTest extends junit.framework.TestCase {",
                 "public void testPass() { }",
                 "public void testFail() { junit.framework.Assert.fail(\"message\"); }",
                 "public void testError() { throw new RuntimeException(\"message\"); }",
                 "}"
-        );
+        )
 
         testDirectory.file('build.gradle') << '''
             apply plugin: 'java'
@@ -381,60 +400,38 @@ public class JUnitIntegrationTest extends AbstractIntegrationTest {
             }
         '''
 
-        ExecutionResult result = executer.withTasks("test").run();
-        assert containsLine(result.getOutput(), "START [test class SomeTest] [SomeTest]");
-        assert containsLine(result.getOutput(), "FINISH [test class SomeTest] [SomeTest]");
-        assert containsLine(result.getOutput(), "START [test testPass(SomeTest)] [testPass]");
-        assert containsLine(result.getOutput(), "FINISH [test testPass(SomeTest)] [testPass] [null]");
-        assert containsLine(result.getOutput(), "START [test testFail(SomeTest)] [testFail]");
-        assert containsLine(result.getOutput(), "FINISH [test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]");
-        assert containsLine(result.getOutput(), "START [test testError(SomeTest)] [testError]");
-        assert containsLine(result.getOutput(), "FINISH [test testError(SomeTest)] [testError] [java.lang.RuntimeException: message]");
+        when:
+        ExecutionResult result = executer.withTasks("test").run()
+
+        then:
+        assert containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
+        assert containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest]")
+        assert containsLine(result.getOutput(), "START [Test testPass(SomeTest)] [testPass]")
+        assert containsLine(result.getOutput(), "FINISH [Test testPass(SomeTest)] [testPass] [null]")
+        assert containsLine(result.getOutput(), "START [Test testFail(SomeTest)] [testFail]")
+        assert containsLine(result.getOutput(), "FINISH [Test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]")
+        assert containsLine(result.getOutput(), "START [Test testError(SomeTest)] [testError]")
+        assert containsLine(result.getOutput(), "FINISH [Test testError(SomeTest)] [testError] [java.lang.RuntimeException: message]")
     }
 
-    @Test
-    public void canHaveMultipleTestTaskInstances() {
+    @IgnoreIf({GradleContextualExecuter.parallel})
+    def canHaveMultipleTestTaskInstances() {
+        when:
         executer.withTasks('check').run()
 
+        then:
         def result = new JUnitXmlTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted('org.gradle.Test1', 'org.gradle.Test2')
         result.testClass('org.gradle.Test1').assertTestPassed('ok')
         result.testClass('org.gradle.Test2').assertTestPassed('ok')
     }
 
-    @Test
-    void canHandleMultipleThreadsWritingToSystemOut() {
-        def result = executer.withTasks("test").run()
-        assert result.getOutput().contains("thread 0 out")
-        assert result.getOutput().contains("thread 1 out")
-        assert result.getOutput().contains("thread 2 out")
-
-        def junitResult = new DefaultTestExecutionResult(testDirectory)
-        def testClass = junitResult.testClass("org.gradle.SystemOutTest")
-        testClass.assertStdout(containsText("thread 0 out"))
-        testClass.assertStdout(containsText("thread 1 out"))
-        testClass.assertStdout(containsText("thread 2 out"))
-    }
-
-    @Test
-    void canHandleMultipleThreadsWritingToSystemErr() {
-        def result = executer.withTasks("test").run()
-        assert result.getOutput().contains("thread 0 err")
-        assert result.getOutput().contains("thread 1 err")
-        assert result.getOutput().contains("thread 2 err")
-
-        def junitResult = new DefaultTestExecutionResult(testDirectory)
-        def testClass = junitResult.testClass("org.gradle.SystemErrTest")
-        testClass.assertStderr(containsText("thread 0 err"))
-        testClass.assertStderr(containsText("thread 1 err"))
-        testClass.assertStderr(containsText("thread 2 err"))
-    }
+    def supportsJunit3Suites() {
+        when:
+        executer.withTasks('test').run()
 
-    @Test
-    public void supportsJunit3Suites() {
-        executer.withTasks('test').run();
+        then:
         DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-
         result.assertTestClassesExecuted('org.gradle.SomeTest1', 'org.gradle.SomeTest2', 'org.gradle.SomeSuite')
         result.testClass("org.gradle.SomeTest1").assertTestCount(1, 0, 0)
         result.testClass("org.gradle.SomeTest1").assertTestsExecuted("testOk1")
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy
new file mode 100644
index 0000000..43d607a
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.junit
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+
+class JUnitJnaIntegrationTest extends AbstractIntegrationSpec {
+    @Rule
+    final TestResources resources = new TestResources(testDirectoryProvider)
+
+    @Requires(TestPrecondition.WINDOWS)
+    def canRunTestsUsingJna() {
+        when:
+        executer.withTasks('build').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('OkTest')
+        result.testClass('OkTest').assertTestPassed('ok')
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingIntegrationTest.groovy
deleted file mode 100644
index 9055cf2..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,121 +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.testing.junit
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.util.TextUtil
-import org.junit.Rule
-import org.junit.Test
-
-import static org.hamcrest.Matchers.equalTo
-
-// cannot make assumptions about order in which test methods of JUnit4Test get executed
-class JUnitLoggingIntegrationTest extends AbstractIntegrationSpec {
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-    ExecutionResult result
-
-    def setup() {
-        executer.noExtraLogging().withStackTraceChecksDisabled().withTasks("test")
-    }
-
-    def "defaultLifecycleLogging"() {
-        when:
-        result = executer.runWithFailure()
-
-        then:
-        outputContains("""
-org.gradle.JUnit4Test > badTest FAILED
-    java.lang.RuntimeException at JUnit4Test.groovy:44
-        """)
-    }
-
-    def "customQuietLogging"() {
-        when:
-        result = executer.withArguments("-q").runWithFailure()
-
-        then:
-        outputContains("""
-badTest FAILED
-    java.lang.RuntimeException: bad
-        at org.gradle.JUnit4Test.beBad(JUnit4Test.groovy:44)
-        at org.gradle.JUnit4Test.badTest(JUnit4Test.groovy:28)
-        """)
-
-        outputContains("ignoredTest SKIPPED")
-
-        outputContains("org.gradle.JUnit4Test FAILED")
-    }
-
-    def "standardOutputLogging"() {
-        when:
-        result = executer.withArguments("-q").runWithFailure()
-
-        then:
-        outputContains("""
-org.gradle.JUnit4StandardOutputTest > printTest STANDARD_OUT
-    line 1
-    line 2
-    line 3
-        """)
-    }
-
-    def "test logging is included in XML results"() {
-        file("build.gradle") << """
-            apply plugin: 'java'
-                repositories { mavenCentral() }
-                dependencies { testCompile 'junit:junit:4.11' }
-        """
-
-        file("src/test/java/EncodingTest.java") << """
-import org.junit.*;
-
-public class EncodingTest {
-    @Test public void encodesCdata() {
-        System.out.println("< html allowed, cdata closing token ]]> encoded!");
-        System.out.print("no EOL, ");
-        System.out.println("non-asci char: ż");
-        System.out.println("xml entity: &");
-        System.err.println("< html allowed, cdata closing token ]]> encoded!");
-    }
-    @Test public void encodesAttributeValues() {
-        throw new RuntimeException("html: <> cdata: ]]>");
-    }
-}
-"""
-        when:
-        executer.withTasks("test").runWithFailure()
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-                .testClass("EncodingTest")
-                .assertTestPassed("encodesCdata")
-                .assertTestFailed("encodesAttributeValues", equalTo('java.lang.RuntimeException: html: <> cdata: ]]>'))
-                .assertStdout(equalTo("""< html allowed, cdata closing token ]]> encoded!
-no EOL, non-asci char: ż
-xml entity: &
-"""))
-                .assertStderr(equalTo("< html allowed, cdata closing token ]]> encoded!\n"))
-    }
-
-
-    private void outputContains(String text) {
-        assert result.output.contains(TextUtil.toPlatformLineSeparators(text.trim()))
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..662527c
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.junit
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.fixture.JUnitCoverage
+import org.gradle.util.TextUtil
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.is
+
+ at TargetCoverage({JUnitCoverage.LOGGING})
+class JUnitLoggingOutputCaptureIntegrationTest extends MultiVersionIntegrationSpec {
+    def setup() {
+        buildFile << """
+            apply plugin: "java"
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:$version' }
+            test {
+                reports.junitXml.outputPerTestCase = true
+                onOutput { test, event -> print "\$test -> \$event.message" }
+            }
+        """
+    }
+
+    def "captures logging output events"() {
+        file("src/test/java/OkTest.java") << """
+public class OkTest {
+    static {
+        System.out.println("class loaded");
+    }
+
+    public OkTest() {
+        System.out.println("test constructed");
+    }
+
+    @org.junit.BeforeClass public static void init() {
+        System.out.println("before class out");
+        System.err.println("before class err");
+    }
+
+    @org.junit.AfterClass public static void end() {
+        System.out.println("after class out");
+        System.err.println("after class err");
+    }
+
+    @org.junit.Before
+    public void before() {
+        System.out.println("before out");
+        System.err.println("before err");
+    }
+
+    @org.junit.After
+    public void after() {
+        System.out.println("after out");
+        System.err.println("after err");
+    }
+
+    @org.junit.Test
+    public void ok() {
+        System.out.print("test out: \u03b1</html>");
+        System.out.println();
+        System.err.println("test err");
+    }
+
+    @org.junit.Test
+    public void anotherOk() {
+        System.out.println("ok out");
+        System.err.println("ok err");
+    }
+}
+"""
+        when: run "test"
+
+        then:
+        result.output.contains(TextUtil.toPlatformLineSeparators("""Test class OkTest -> class loaded
+Test class OkTest -> before class out
+Test class OkTest -> before class err
+Test class OkTest -> test constructed
+Test anotherOk(OkTest) -> before out
+Test anotherOk(OkTest) -> before err
+Test anotherOk(OkTest) -> ok out
+Test anotherOk(OkTest) -> ok err
+Test anotherOk(OkTest) -> after out
+Test anotherOk(OkTest) -> after err
+Test class OkTest -> test constructed
+Test ok(OkTest) -> before out
+Test ok(OkTest) -> before err
+Test ok(OkTest) -> test out: \u03b1</html>
+Test ok(OkTest) -> test err
+Test ok(OkTest) -> after out
+Test ok(OkTest) -> after err
+Test class OkTest -> after class out
+Test class OkTest -> after class err
+"""))
+
+        // This test covers current behaviour, not necessarily desired behaviour
+
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = xmlReport.testClass("OkTest")
+        classResult.assertTestCaseStdout("ok", is("""before out
+test out: \u03b1</html>
+after out
+"""))
+        classResult.assertTestCaseStderr("ok", is("""before err
+test err
+after err
+"""))
+        classResult.assertStdout(is("""class loaded
+before class out
+test constructed
+test constructed
+after class out
+"""))
+        classResult.assertStderr(is("""before class err
+after class err
+"""))
+
+        def htmlReport = new HtmlTestExecutionResult(testDirectory)
+        def classReport = htmlReport.testClass("OkTest")
+        classReport.assertStdout(is("""class loaded
+before class out
+test constructed
+before out
+ok out
+after out
+test constructed
+before out
+test out: \u03b1</html>
+after out
+after class out
+"""))
+        classReport.assertStderr(is("""before class err
+before err
+ok err
+after err
+before err
+test err
+after err
+after class err
+"""))
+    }
+
+    def "captures output from logging frameworks"() {
+        buildFile << """
+dependencies { testCompile "org.slf4j:slf4j-simple:1.7.10", "org.slf4j:slf4j-api:1.7.10" }
+"""
+        file("src/test/java/FooTest.java") << """
+
+            public class FooTest {
+                private final static org.slf4j.Logger SLF4J = org.slf4j.LoggerFactory.getLogger(FooTest.class);
+                private final static java.util.logging.Logger JUL = java.util.logging.Logger.getLogger(FooTest.class.getName());
+
+                @org.junit.Test
+                public void foo() {
+                  SLF4J.info("slf4j info");
+                  JUL.info("jul info");
+                  JUL.warning("jul warning");
+                }
+            }
+        """
+
+        when: run("test")
+
+        then:
+        result.output.contains("Test foo(FooTest) -> [Test worker] INFO FooTest - slf4j info")
+        result.output.contains("Test foo(FooTest) -> INFO: jul info")
+        result.output.contains("Test foo(FooTest) -> WARNING: jul warning")
+
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("FooTest")
+        classResult.assertTestCaseStderr("foo", containsString("[Test worker] INFO FooTest - slf4j info"))
+        classResult.assertTestCaseStderr("foo", containsString("INFO: jul info"))
+        classResult.assertTestCaseStderr("foo", containsString("WARNING: jul warning"))
+    }
+
+    def "test can generate output from multiple threads"() {
+        file("src/test/java/OkTest.java") << """
+import java.util.logging.Logger;
+import java.util.List;
+import java.util.ArrayList;
+
+public class OkTest {
+    @org.junit.Test
+    public void ok() throws Exception {
+        // logging from multiple threads
+        List<Thread> threads  = new ArrayList<Thread>();
+        for (int i = 0; i < 5; i++) {
+            Thread thread = new Thread("thread " + i) {
+                @Override
+                public void run() {
+                    System.out.print("stdout from "); // print a partial line
+                    System.err.println("stderr from " + getName());
+                    System.out.println(getName());
+                    Logger.getLogger("test-logger").info("info from " + getName());
+                }
+            };
+            thread.start();
+            threads.add(thread);
+        }
+        for(Thread thread: threads) {
+            thread.join();
+        }
+    }
+}
+"""
+
+        when:
+        run("test")
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("OkTest")
+
+        5.times { n ->
+            assert result.output.contains("Test ok(OkTest) -> stdout from thread $n")
+            assert result.output.contains("Test ok(OkTest) -> stderr from thread $n")
+            assert result.output.contains("Test ok(OkTest) -> INFO: info from thread $n")
+
+            classResult.assertTestCaseStdout("ok", containsString("stdout from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("stderr from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("INFO: info from thread $n"))
+        }
+    }
+
+    def "output does not require trailing end-of-line separator"() {
+        file("src/test/java/OkTest.java") << """
+public class OkTest {
+    @org.junit.Before
+    public void before() {
+        System.out.print("[before out]");
+        System.err.print("[before err]");
+    }
+
+    @org.junit.After
+    public void after() {
+        System.out.print("[after out]");
+        System.err.print("[after err]");
+    }
+
+    @org.junit.BeforeClass public static void init() {
+        System.out.print("[before class out]");
+        System.err.print("[before class err]");
+    }
+
+    @org.junit.AfterClass public static void end() {
+        System.out.print("[after class out]");
+        System.err.print("[after class err]");
+    }
+
+    @org.junit.Test
+    public void ok() {
+        System.out.print("[test out]");
+        System.err.print("[test err]");
+    }
+
+    @org.junit.Test
+    public void anotherOk() {
+        System.out.println();
+        System.err.println();
+        System.out.print("[ok out]");
+        System.err.print("[ok err]");
+    }
+}
+"""
+
+        when: run "test"
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("OkTest")
+        classResult.assertTestCaseStdout("ok", is("[before out][test out][after out]"))
+        classResult.assertTestCaseStderr("ok", is("[before err][test err][after err]"))
+        classResult.assertTestCaseStdout("anotherOk", is("[before out]\n[ok out][after out]"))
+        classResult.assertTestCaseStderr("anotherOk", is("[before err]\n[ok err][after err]"))
+        classResult.assertStdout(is("[before class out][after class out]"))
+        classResult.assertStderr(is("[before class err][after class err]"))
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGConsoleLoggingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..a63743b
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * 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.testing.testng
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.TextUtil
+
+// can make assumptions about order in which test methods of TestNGTest get executed
+// because the methods are chained with 'methodDependsOn'
+class TestNGConsoleLoggingIntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        executer.noExtraLogging().withStackTraceChecksDisabled().withTasks("test")
+
+        buildFile << """
+            apply plugin: "groovy"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                compile "org.codehaus.groovy:groovy:2.3.10"
+                testCompile "org.testng:testng:6.3.1"
+            }
+
+            test {
+                useTestNG()
+                testLogging {
+                    quiet {
+                        events "skipped", "failed"
+                        minGranularity 2
+                        maxGranularity -1
+                        displayGranularity 3
+                        exceptionFormat "full"
+                        stackTraceFilters "truncate", "groovy"
+                    }
+                }
+            }
+        """
+
+        file("src/test/groovy/org/gradle/TestNGTest.groovy") << """
+            package org.gradle
+
+            import org.testng.annotations.Test
+
+            class TestNGTest {
+                @Test
+                void goodTest() {}
+
+                @Test(dependsOnMethods = ["goodTest"])
+                void badTest() {
+                    beBad()
+                }
+
+                @Test(dependsOnMethods = ["badTest"])
+                void ignoredTest() {}
+
+                @Test(dependsOnMethods = ["goodTest"])
+                void printTest() {
+                    println "line 1\\nline 2"
+                    println "line 3"
+                }
+
+                private beBad() {
+                    throw new RuntimeException("bad")
+                }
+            }
+        """
+    }
+
+    def "defaultLifecycleLogging"() {
+        when:
+        result = executer.runWithFailure()
+
+        then:
+        outputContains("""
+Gradle test > org.gradle.TestNGTest.badTest FAILED
+    java.lang.RuntimeException at TestNGTest.groovy:25
+        """)
+    }
+
+    def customQuietLogging() {
+        when:
+        result = executer.withArguments("-q").runWithFailure()
+
+        then:
+        outputContains("""
+Gradle test > org.gradle.TestNGTest.badTest FAILED
+    java.lang.RuntimeException: bad
+        at org.gradle.TestNGTest.beBad(TestNGTest.groovy:25)
+        at org.gradle.TestNGTest.badTest(TestNGTest.groovy:12)
+
+Gradle test > org.gradle.TestNGTest.ignoredTest SKIPPED
+
+Gradle test FAILED
+
+Gradle suite FAILED
+        """)
+    }
+
+    def "standardOutputLogging"() {
+        given:
+        buildFile.text = """
+            apply plugin: "groovy"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                compile "org.codehaus.groovy:groovy:2.3.10"
+                testCompile "org.testng:testng:6.3.1"
+            }
+
+            test {
+                useTestNG()
+                testLogging {
+                    quiet {
+                        events "standardOut", "standardError"
+                    }
+                }
+            }
+        """
+
+        and:
+        file("src/test/groovy/org/gradle/TestNGStandardOutputTest.groovy") << """
+            package org.gradle
+
+            import org.testng.annotations.Test
+
+            class TestNGStandardOutputTest {
+                @Test
+                void printTest() {
+                    println "line 1\\nline 2"
+                    println "line 3"
+                }
+            }
+        """
+
+        when:
+        result = executer.withArguments("-q").runWithFailure()
+
+        then:
+        outputContains("""
+Gradle test > org.gradle.TestNGStandardOutputTest.printTest STANDARD_OUT
+    line 1
+    line 2
+    line 3
+        """)
+    }
+
+    private void outputContains(String text) {
+        assert result.output.contains(TextUtil.toPlatformLineSeparators(text.trim()))
+    }
+
+
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest.groovy
new file mode 100644
index 0000000..dadefa5
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.testng
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestNGExecutionResult
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+
+import static org.gradle.testing.fixture.TestNGCoverage.NEWEST
+
+class TestNGFailurePolicyIntegrationTest extends AbstractIntegrationSpec {
+
+    @Rule public TestResources resources = new TestResources(testDirectoryProvider)
+
+    TestClassExecutionResult getTestResults() {
+        new TestNGExecutionResult(testDirectory).testClass("org.gradle.failurepolicy.TestWithFailureInConfigMethod")
+    }
+
+    void usingTestNG(String version) {
+        buildFile << """
+            dependencies { testCompile "org.testng:testng:${version}" }
+        """
+    }
+
+    def "skips tests after a config method failure by default"() {
+        when:
+        usingTestNG(NEWEST)
+
+        then:
+        fails "test"
+
+        and:
+        testResults.assertConfigMethodFailed("fail")
+        testResults.assertTestSkipped("someTest")
+    }
+
+    def "can be configured to continue executing tests after a config method failure"() {
+        when:
+        usingTestNG(NEWEST)
+        buildFile << """
+            test.options {
+                configFailurePolicy "continue"
+            }
+        """
+
+        then:
+        fails "test"
+
+        and:
+        testResults.assertConfigMethodFailed("fail")
+        testResults.assertTestPassed("someTest")
+    }
+
+    def "informative error is shown when trying to use config failure policy and a version that does not support it"() {
+        when:
+        usingTestNG("5.12.1")
+        buildFile << """
+            test.options {
+                configFailurePolicy "continue"
+            }
+        """
+
+        then:
+        fails "test"
+
+        and:
+        failure.assertHasCause("The version of TestNG used does not support setting config failure policy to 'continue'.")
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
index de12170..5ba13d4 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
@@ -27,7 +27,6 @@ import spock.lang.Issue
 
 import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.assertThat
 
 class TestNGIntegrationTest extends AbstractIntegrationTest {
 
@@ -40,11 +39,7 @@ class TestNGIntegrationTest extends AbstractIntegrationTest {
 
     @Test
     void executesTestsInCorrectEnvironment() {
-        ExecutionResult result = executer.withTasks('test').run();
-
-        assertThat(result.output, not(containsString('stdout')))
-        assertThat(result.error, not(containsString('stderr')))
-        assertThat(result.error, not(containsString('a warning')))
+        executer.withTasks('test').run();
 
         new DefaultTestExecutionResult(testDirectory).testClass('org.gradle.OkTest').assertTestPassed('ok')
     }
@@ -53,20 +48,22 @@ class TestNGIntegrationTest extends AbstractIntegrationTest {
     void canListenForTestResults() {
         ExecutionResult result = executer.withTasks("test").run();
 
-        assert containsLine(result.getOutput(), "START [tests] [Test Run]");
-        assert containsLine(result.getOutput(), "FINISH [tests] [Test Run]");
-        assert containsLine(result.getOutput(), "START [process 'Gradle Test Executor 1'] [Gradle Test Executor 1]");
-        assert containsLine(result.getOutput(), "FINISH [process 'Gradle Test Executor 1'] [Gradle Test Executor 1]");
-        assert containsLine(result.getOutput(), "START [test 'Gradle test'] [Gradle test]");
-        assert containsLine(result.getOutput(), "FINISH [test 'Gradle test'] [Gradle test]");
-        assert containsLine(result.getOutput(), "START [test method pass(SomeTest)] [pass]");
-        assert containsLine(result.getOutput(), "FINISH [test method pass(SomeTest)] [pass] [null]");
-        assert containsLine(result.getOutput(), "START [test method fail(SomeTest)] [fail]");
-        assert containsLine(result.getOutput(), "FINISH [test method fail(SomeTest)] [fail] [java.lang.AssertionError]");
-        assert containsLine(result.getOutput(), "START [test method knownError(SomeTest)] [knownError]");
-        assert containsLine(result.getOutput(), "FINISH [test method knownError(SomeTest)] [knownError] [java.lang.RuntimeException: message]");
-        assert containsLine(result.getOutput(), "START [test method unknownError(SomeTest)] [unknownError]");
-        assert containsLine(result.getOutput(), "FINISH [test method unknownError(SomeTest)] [unknownError] [AppException]");
+        assert containsLine(result.getOutput(), "START [Gradle Test Run :test] [Gradle Test Run :test]");
+        assert containsLine(result.getOutput(), "FINISH [Gradle Test Run :test] [Gradle Test Run :test]");
+        assert containsLine(result.getOutput(), "START [Gradle Test Executor 1] [Gradle Test Executor 1]");
+        assert containsLine(result.getOutput(), "FINISH [Gradle Test Executor 1] [Gradle Test Executor 1]");
+        assert containsLine(result.getOutput(), "START [Test suite 'Gradle suite'] [Gradle suite]");
+        assert containsLine(result.getOutput(), "FINISH [Test suite 'Gradle suite'] [Gradle suite]");
+        assert containsLine(result.getOutput(), "START [Test suite 'Gradle test'] [Gradle test]");
+        assert containsLine(result.getOutput(), "FINISH [Test suite 'Gradle test'] [Gradle test]");
+        assert containsLine(result.getOutput(), "START [Test method pass(SomeTest)] [pass]");
+        assert containsLine(result.getOutput(), "FINISH [Test method pass(SomeTest)] [pass] [null]");
+        assert containsLine(result.getOutput(), "START [Test method fail(SomeTest)] [fail]");
+        assert containsLine(result.getOutput(), "FINISH [Test method fail(SomeTest)] [fail] [java.lang.AssertionError]");
+        assert containsLine(result.getOutput(), "START [Test method knownError(SomeTest)] [knownError]");
+        assert containsLine(result.getOutput(), "FINISH [Test method knownError(SomeTest)] [knownError] [java.lang.RuntimeException: message]");
+        assert containsLine(result.getOutput(), "START [Test method unknownError(SomeTest)] [unknownError]");
+        assert containsLine(result.getOutput(), "FINISH [Test method unknownError(SomeTest)] [unknownError] [AppException]");
     }
 
     @Test
@@ -153,7 +150,7 @@ test {
 """
         executer.withTasks("test").run()
     }
-    
+
     @Test
     void supportsTestGroups() {
         executer.withTasks("test").run()
@@ -172,4 +169,4 @@ test {
         result.testClass('org.gradle.factory.FactoryTest').assertStdout(containsString('TestingSecond'))
         result.testClass('org.gradle.factory.FactoryTest').assertStdout(not(containsString('Default test name')))
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingIntegrationTest.groovy
deleted file mode 100644
index 4eee33c..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,170 +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.testing.testng
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.TextUtil
-
-// can make assumptions about order in which test methods of TestNGTest get executed
-// because the methods are chained with 'methodDependsOn'
-class TestNGLoggingIntegrationTest extends AbstractIntegrationSpec {
-
-    def setup() {
-        executer.noExtraLogging().withStackTraceChecksDisabled().withTasks("test")
-
-        buildFile << """
-            apply plugin: "groovy"
-
-            repositories {
-                mavenCentral()
-            }
-
-            dependencies {
-                compile "org.codehaus.groovy:groovy-all:2.0.5"
-                testCompile "org.testng:testng:6.3.1"
-            }
-
-            test {
-                useTestNG()
-                testLogging {
-                    quiet {
-                        events "skipped", "failed"
-                        minGranularity 2
-                        maxGranularity -1
-                        displayGranularity 3
-                        exceptionFormat "full"
-                        stackTraceFilters "truncate", "groovy"
-                    }
-                }
-            }
-        """
-
-        file("src/test/groovy/org/gradle/TestNGTest.groovy") << """
-            package org.gradle
-
-            import org.testng.annotations.Test
-
-            class TestNGTest {
-                @Test
-                void goodTest() {}
-
-                @Test(dependsOnMethods = ["goodTest"])
-                void badTest() {
-                    beBad()
-                }
-
-                @Test(dependsOnMethods = ["badTest"])
-                void ignoredTest() {}
-
-                @Test(dependsOnMethods = ["goodTest"])
-                void printTest() {
-                    println "line 1\\nline 2"
-                    println "line 3"
-                }
-
-                private beBad() {
-                    throw new RuntimeException("bad")
-                }
-            }
-        """
-    }
-
-    def "defaultLifecycleLogging"() {
-        when:
-        result = executer.runWithFailure()
-
-        then:
-        outputContains("""
-Gradle test > org.gradle.TestNGTest.badTest FAILED
-    java.lang.RuntimeException at TestNGTest.groovy:25
-        """)
-    }
-
-    def customQuietLogging() {
-        when:
-        result = executer.withArguments("-q").runWithFailure()
-
-        then:
-        outputContains("""
-org.gradle.TestNGTest.badTest FAILED
-    java.lang.RuntimeException: bad
-        at org.gradle.TestNGTest.beBad(TestNGTest.groovy:25)
-        at org.gradle.TestNGTest.badTest(TestNGTest.groovy:12)
-
-org.gradle.TestNGTest.ignoredTest SKIPPED
-
-Gradle test FAILED
-        """)
-    }
-
-    def "standardOutputLogging"() {
-        given:
-        buildFile.text = """
-            apply plugin: "groovy"
-
-            repositories {
-                mavenCentral()
-            }
-
-            dependencies {
-                compile "org.codehaus.groovy:groovy-all:2.0.5"
-                testCompile "org.testng:testng:6.3.1"
-            }
-
-            test {
-                useTestNG()
-                testLogging {
-                    quiet {
-                        events "standardOut", "standardError"
-                    }
-                }
-            }
-        """
-
-        and:
-        file("src/test/groovy/org/gradle/TestNGStandardOutputTest.groovy") << """
-            package org.gradle
-
-            import org.testng.annotations.Test
-
-            class TestNGStandardOutputTest {
-                @Test
-                void printTest() {
-                    println "line 1\\nline 2"
-                    println "line 3"
-                }
-            }
-        """
-
-        when:
-        result = executer.withArguments("-q").runWithFailure()
-
-        then:
-        outputContains("""
-Gradle test > org.gradle.TestNGStandardOutputTest.printTest STANDARD_OUT
-    line 1
-    line 2
-    line 3
-        """)
-    }
-
-    private void outputContains(String text) {
-        assert result.output.contains(TextUtil.toPlatformLineSeparators(text.trim()))
-    }
-
-
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingOutputCaptureIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..8d4ed47
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGLoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.testng
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.fixture.TestNGCoverage
+import org.gradle.util.TextUtil
+
+import static org.hamcrest.Matchers.is
+
+ at TargetCoverage({TestNGCoverage.STANDARD_COVERAGE})
+class TestNGLoggingOutputCaptureIntegrationTest extends MultiVersionIntegrationSpec {
+
+    def setup() {
+        buildFile << """
+            apply plugin: "java"
+            repositories { jcenter() }
+            dependencies { testCompile "org.testng:testng:$version" }
+            test {
+                useTestNG()
+                reports.junitXml.outputPerTestCase = true
+                onOutput { test, event -> print "\$test -> \$event.message" }
+            }
+        """
+
+        file("src/test/java/FooTest.java") << """import org.testng.annotations.*;
+            public class FooTest {
+                static { System.out.println("static out"); System.err.println("static err"); }
+
+                public FooTest() {
+                    System.out.println("constructor out"); System.err.println("constructor err");
+                }
+
+                @BeforeClass public static void beforeClass() {
+                    System.out.println("beforeClass out"); System.err.println("beforeClass err");
+                }
+
+                @BeforeTest public void beforeTest() {
+                    System.out.println("beforeTest out"); System.err.println("beforeTest err");
+                }
+
+                @Test public void m1() {
+                    System.out.print("m1: ");
+                    System.out.print("\u03b1</html>");
+                    System.out.println();
+                    System.err.println("m1 err");
+                }
+
+                @Test public void m2() {
+                    System.out.println("m2 out"); System.err.println("m2 err");
+                }
+
+                @AfterTest public void afterTest() {
+                    System.out.println("afterTest out"); System.err.println("afterTest err");
+                }
+
+                @AfterClass public static void afterClass() {
+                    System.out.println("afterClass out"); System.err.println("afterClass err");
+                }
+            }
+        """
+    }
+
+    def "attaches events to correct test descriptors of a suite"() {
+        buildFile << "test.useTestNG { suites 'suite.xml' }"
+
+        file("suite.xml") << """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="AwesomeSuite">
+  <test name='The Foo Test'><classes><class name='FooTest'/></classes></test>
+</suite>"""
+
+        when: run "test"
+
+        then:
+        result.output.contains(TextUtil.toPlatformLineSeparators("""Gradle Test Executor 1 -> static out
+Gradle Test Executor 1 -> static err
+Gradle Test Executor 1 -> constructor out
+Gradle Test Executor 1 -> constructor err
+Test suite 'The Foo Test' -> beforeTest out
+Test suite 'The Foo Test' -> beforeTest err
+Test suite 'The Foo Test' -> beforeClass out
+Test suite 'The Foo Test' -> beforeClass err
+Test method m1(FooTest) -> m1: \u03b1</html>
+Test method m1(FooTest) -> m1 err
+Test method m2(FooTest) -> m2 out
+Test method m2(FooTest) -> m2 err
+Test suite 'The Foo Test' -> afterClass out
+Test suite 'The Foo Test' -> afterClass err
+Test suite 'The Foo Test' -> afterTest out
+Test suite 'The Foo Test' -> afterTest err
+"""))
+
+        /**
+         * This test documents the current behavior. It's not right, we're missing a lot of output in the report.
+         */
+
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = xmlReport.testClass("FooTest")
+
+        classResult.assertTestCaseStderr("m1", is("m1 err\n"))
+        classResult.assertTestCaseStderr("m2", is("m2 err\n"))
+        classResult.assertTestCaseStdout("m1", is("m1: \u03b1</html>\n"))
+        classResult.assertTestCaseStdout("m2", is("m2 out\n"))
+        classResult.assertStderr(is(""))
+        classResult.assertStdout(is(""))
+
+        def htmlReport = new HtmlTestExecutionResult(testDirectory)
+        def classReport = htmlReport.testClass("FooTest")
+        classReport.assertStdout(is("m1: \u03b1</html>\nm2 out\n"))
+        classReport.assertStderr(is("m1 err\nm2 err\n"))
+    }
+
+    def "attaches output events to correct test descriptors"() {
+        when: run "test"
+
+        then:
+        result.output.contains(TextUtil.toPlatformLineSeparators("""Gradle Test Executor 1 -> static out
+Gradle Test Executor 1 -> static err
+Gradle Test Executor 1 -> constructor out
+Gradle Test Executor 1 -> constructor err
+Test suite 'Gradle test' -> beforeTest out
+Test suite 'Gradle test' -> beforeTest err
+Test suite 'Gradle test' -> beforeClass out
+Test suite 'Gradle test' -> beforeClass err
+Test method m1(FooTest) -> m1: \u03b1</html>
+Test method m1(FooTest) -> m1 err
+Test method m2(FooTest) -> m2 out
+Test method m2(FooTest) -> m2 err
+Test suite 'Gradle test' -> afterClass out
+Test suite 'Gradle test' -> afterClass err
+Test suite 'Gradle test' -> afterTest out
+Test suite 'Gradle test' -> afterTest err
+"""))
+
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = xmlReport.testClass("FooTest")
+
+
+        /**
+         * This test documents the current behavior. It's not right, we're missing a lot of output in the report.
+         */
+
+        classResult.assertTestCaseStderr("m1", is("m1 err\n"))
+        classResult.assertTestCaseStderr("m2", is("m2 err\n"))
+        classResult.assertTestCaseStdout("m1", is("m1: \u03b1</html>\n"))
+        classResult.assertTestCaseStdout("m2", is("m2 out\n"))
+        classResult.assertStderr(is(""))
+        classResult.assertStdout(is(""))
+
+        def htmlReport = new HtmlTestExecutionResult(testDirectory)
+        def classReport = htmlReport.testClass("FooTest")
+        classReport.assertStdout(is("m1: \u03b1</html>\nm2 out\n"))
+        classReport.assertStderr(is("m1 err\nm2 err\n"))
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGParallelSuiteIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGParallelSuiteIntegrationTest.groovy
new file mode 100644
index 0000000..6c1290f
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGParallelSuiteIntegrationTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.testing.testng
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.fixture.TestNGCoverage
+import spock.lang.Issue
+
+ at TargetCoverage({TestNGCoverage.STANDARD_COVERAGE})
+public class TestNGParallelSuiteIntegrationTest extends MultiVersionIntegrationSpec {
+
+    @Issue("GRADLE-3190")
+    def "runs with multiple parallel threads"() {
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies {
+                testCompile 'org.testng:testng:$version'
+            }
+            test {
+              useTestNG {
+                suites "suite.xml"
+              }
+            }
+        """
+
+        String suiteXml = ""
+        200.times { x ->
+            file("src/test/java/Foo${x}Test.java") << """
+                public class Foo${x}Test {
+                    @org.testng.annotations.Test public void test() {
+                        for (int i=0; i<20; i++) {
+                            System.out.println("" + i + " - foo ${x} - " + Thread.currentThread().getId());
+                        }
+                    }
+                }
+            """
+            suiteXml += "<test name='t${x}'><classes><class name='Foo${x}Test'/></classes></test>\n"
+        }
+
+        file("suite.xml") << """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="AwesomeSuite" parallel="tests" thread-count="20">
+  $suiteXml
+</suite>"""
+
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("Foo0Test").assertTestsExecuted("test")
+        result.testClass("Foo199Test").assertTestsExecuted("test")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGProducesOldReportsIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGProducesOldReportsIntegrationTest.groovy
index 8163c0a..5bc9a4c 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGProducesOldReportsIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGProducesOldReportsIntegrationTest.groovy
@@ -13,10 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
-
-
 package org.gradle.testing.testng
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
@@ -88,7 +84,7 @@ test {
 }
 """
         when:
-        executer.withDeprecationChecksDisabled().withTasks('test').run()
+        executer.withTasks('test').run()
 
         then:
         new JUnitXmlTestExecutionResult(file(".")).hasJUnitXmlResults()
@@ -101,8 +97,8 @@ test {
         where:
         preConfig                                | postConfig                                                                           | path
         ""                                       | ""                                                                                   | TestNGExecutionResult.DEFAULT_TESTNG_REPORT
-        "testReportDir = file('xyz')"            | "reports.html.destination = file('abc')"                                             | "abc"
-        ""                                       | "testReportDir = file('xyz');reports.html.destination = file('abc')"                 | "abc"
+        "reports.html.destination = file('xyz')" | "reports.html.destination = file('abc')"                                             | "abc"
+        ""                                       | "reports.html.destination = file('abc')"                                             | "abc"
         "reports.html.destination = file('abc')" | "options.outputDirectory = file('xyz')"                                              | "xyz"
         ""                                       | "options.outputDirectory = file('xyz');reports.html.destination = file('ignore me')" | "xyz"
     }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGStaticLoggingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGStaticLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..85c1c0e
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/testing/testng/TestNGStaticLoggingIntegrationTest.groovy
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.testing.testng
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.testing.fixture.TestNGCoverage
+import spock.lang.Issue
+
+import static org.hamcrest.Matchers.containsString
+import static org.hamcrest.Matchers.is
+
+class TestNGStaticLoggingIntegrationTest extends AbstractIntegrationSpec {
+    def setup() {
+        TestNGCoverage.enableTestNG(buildFile)
+        buildFile << """
+            test {
+                reports.junitXml.outputPerTestCase = true
+                onOutput { test, event -> print "\$test -> \$event.message" }
+            }
+        """
+    }
+
+    @Issue("GRADLE-2841")
+    def "captures output from logging frameworks"() {
+        buildFile << """
+            dependencies { compile "org.slf4j:slf4j-simple:1.7.10", "org.slf4j:slf4j-api:1.7.10" }
+"""
+        file("src/test/java/FooTest.java") << """
+            import org.testng.annotations.*;
+
+            public class FooTest {
+                private final static org.slf4j.Logger SLF4J = org.slf4j.LoggerFactory.getLogger(FooTest.class);
+                private final static java.util.logging.Logger JUL = java.util.logging.Logger.getLogger(FooTest.class.getName());
+
+                @Test public void foo() {
+                  SLF4J.info("slf4j info");
+                  JUL.info("jul info");
+                  JUL.warning("jul warning");
+                }
+            }
+        """
+
+        when: run("test")
+
+        then:
+        result.output.contains("Test method foo(FooTest) -> [Test worker] INFO FooTest - slf4j info")
+        result.output.contains("Test method foo(FooTest) -> INFO: jul info")
+        result.output.contains("Test method foo(FooTest) -> WARNING: jul warning")
+
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("FooTest")
+        classResult.assertTestCaseStderr("foo", containsString("[Test worker] INFO FooTest - slf4j info"))
+        classResult.assertTestCaseStderr("foo", containsString("INFO: jul info"))
+        classResult.assertTestCaseStderr("foo", containsString("WARNING: jul warning"))
+    }
+
+    @Issue("GRADLE-2841")
+    def "captures logging from System streams referenced from static initializer"() {
+        file("src/test/java/FooTest.java") << """
+            import org.testng.annotations.*;
+            import java.io.PrintStream;
+
+            public class FooTest {
+                static PrintStream out = System.out;
+                static PrintStream err = System.err;
+                static { out.println("cool output from initializer"); }
+                @Test public void foo() { out.println("cool output from test"); err.println("err output from test"); }
+            }
+        """
+
+        when: run("test")
+        then:
+        result.output.contains("Test method foo(FooTest) -> cool output from test")
+        result.output.contains("Test method foo(FooTest) -> err output from test")
+        result.output.contains("Gradle Test Executor 1 -> cool output from initializer")
+
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        testResult.testClass("FooTest").assertTestCaseStdout("foo", is("cool output from test\n"))
+        testResult.testClass("FooTest").assertTestCaseStderr("foo", is("err output from test\n"))
+    }
+
+    def "test can generate output from multiple threads"() {
+        file("src/test/java/OkTest.java") << """
+import java.util.logging.Logger;
+import java.util.List;
+import java.util.ArrayList;
+import org.testng.annotations.*;
+
+public class OkTest {
+    @Test
+    public void ok() throws Exception {
+        // logging from multiple threads
+        List<Thread> threads  = new ArrayList<Thread>();
+        for (int i = 0; i < 5; i++) {
+            Thread thread = new Thread("thread " + i) {
+                @Override
+                public void run() {
+                    System.out.print("stdout from "); // print a partial line
+                    System.err.println("stderr from " + getName());
+                    System.out.println(getName());
+                    Logger.getLogger("test-logger").info("info from " + getName());
+                }
+            };
+            thread.start();
+            threads.add(thread);
+        }
+        for(Thread thread: threads) {
+            thread.join();
+        }
+    }
+}
+"""
+
+        when:
+        run("test")
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("OkTest")
+
+        5.times { n ->
+            assert result.output.contains("Test method ok(OkTest) -> stdout from thread $n")
+            assert result.output.contains("Test method ok(OkTest) -> stderr from thread $n")
+            assert result.output.contains("Test method ok(OkTest) -> INFO: info from thread $n")
+
+            classResult.assertTestCaseStdout("ok", containsString("stdout from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("stderr from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("INFO: info from thread $n"))
+        }
+    }
+
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/build.gradle
new file mode 100644
index 0000000..b172e71
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/build.gradle
@@ -0,0 +1,60 @@
+apply plugin: "java"
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.12'
+}
+
+configurations {
+    customTestCompile.extendsFrom testCompile
+    customTestRuntime.extendsFrom testRuntime
+}
+
+sourceSets {
+    custom
+    customTest
+}
+
+task customTest(type: Test) {
+    testClassesDir = sourceSets.customTest.output.classesDir
+    classpath = sourceSets.customTest.runtimeClasspath
+}
+
+check.dependsOn customTest
+
+task javadocJar(type: Jar) {
+    from javadoc
+    classifier = 'javadoc'
+}
+
+task sourcesJar(type: Jar) {
+    from sourceSets.main.allSource
+    classifier = 'sources'
+}
+
+task customJar(type: Jar) {
+    from sourceSets.custom.output
+    classifier = 'custom'
+}
+
+task customJavadoc(type: Javadoc) {
+    classpath = sourceSets.custom.output
+    source = sourceSets.custom.allJava
+}
+
+task customJavadocJar(type: Jar) {
+    from customJavadoc
+    classifier = 'javadoc'
+}
+
+task customSourcesJar(type: Jar) {
+    from sourceSets.custom.allSource
+    classifier = 'custom-sources'
+}
+
+artifacts {
+    archives javadocJar, sourcesJar, customJar, customJavadocJar, customSourcesJar
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/custom/java/SomeClass.java b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/custom/java/SomeClass.java
new file mode 100644
index 0000000..8a4a439
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/custom/java/SomeClass.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SomeClass {
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/custom/resources/resource.txt
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/custom/resources/resource.txt
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/customTest/java/SomeTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/customTest/java/SomeTest.java
new file mode 100644
index 0000000..0f7004a
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/customTest/java/SomeTest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.Test;
+
+public class SomeTest {
+
+    @Test
+    public void someTest() {
+    }
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/customTest/resources/testResource.txt
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/customTest/resources/testResource.txt
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/main/java/SomeClass.java b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/main/java/SomeClass.java
new file mode 100644
index 0000000..8a4a439
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/main/java/SomeClass.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SomeClass {
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/main/resources/resource.txt
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/main/resources/resource.txt
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/test/java/SomeTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/test/java/SomeTest.java
new file mode 100644
index 0000000..0f7004a
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/test/java/SomeTest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.Test;
+
+public class SomeTest {
+
+    @Test
+    public void someTest() {
+    }
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink b/subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/test/resources/testResource.txt
similarity index 100%
copy from subprojects/core/src/test/resources/org/gradle/api/file/symlinks/symlink
copy to subprojects/plugins/src/integTest/resources/org/gradle/api/plugins/ParallelJavaPluginTest/shared/src/test/resources/testResource.txt
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/build.gradle
index 7520244..fe46ed2 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.10"
+    testCompile "junit:junit:4.12"
 }
 
 sourceSets.test.groovy.filter.includes = ["**/*.spec"]
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/canCompileAgainstGroovyClassThatDependsOnExternalClass/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/canCompileAgainstGroovyClassThatDependsOnExternalClass/build.gradle
index daae5fa..5bc2b7a 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/canCompileAgainstGroovyClassThatDependsOnExternalClass/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/canCompileAgainstGroovyClassThatDependsOnExternalClass/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.11"
+    testCompile "junit:junit:4.12"
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/build.gradle
new file mode 100644
index 0000000..c93ae49
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/build.gradle
@@ -0,0 +1,5 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/src/main/groovy/FxApp.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/src/main/groovy/FxApp.groovy
new file mode 100644
index 0000000..68a38d9
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileJavaFx8Code/src/main/groovy/FxApp.groovy
@@ -0,0 +1,9 @@
+import javafx.application.Application
+import javafx.stage.Stage
+
+class FxApp extends Application {
+
+    @Override
+    void start(Stage stage) {
+    }
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle
new file mode 100644
index 0000000..fc20e82
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy
new file mode 100644
index 0000000..2ffc5b6
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy
@@ -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.groovy.compile.BasicGroovyCompilerIntegrationSpec.configurationScriptNotSupported
+
+import groovy.transform.TypeChecked
+
+withConfig(configuration) {
+    ast(TypeChecked)
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy
new file mode 100644
index 0000000..1c41eda
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy
@@ -0,0 +1,5 @@
+class BrokenClass {
+    void doesNotTypeCheck() {
+        "foo".bar()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle
new file mode 100644
index 0000000..fc20e82
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy
new file mode 100644
index 0000000..5f9beaa
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy
@@ -0,0 +1,3 @@
+withConfig(configuration) {
+    ast(TypeChecked) // invalid config file because of a missing import
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy
new file mode 100644
index 0000000..1c41eda
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy
@@ -0,0 +1,5 @@
+class BrokenClass {
+    void doesNotTypeCheck() {
+        "foo".bar()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle
new file mode 100644
index 0000000..fc20e82
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy
new file mode 100644
index 0000000..1c41eda
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy
@@ -0,0 +1,5 @@
+class BrokenClass {
+    void doesNotTypeCheck() {
+        "foo".bar()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/build.gradle
new file mode 100644
index 0000000..c93ae49
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/build.gradle
@@ -0,0 +1,5 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/src/main/groovy/Thing.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/src/main/groovy/Thing.groovy
new file mode 100644
index 0000000..6975849
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/groovyToolClassesAreNotVisible/src/main/groovy/Thing.groovy
@@ -0,0 +1,3 @@
+class Thing {
+    def builder = new AntBuilder()
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/build.gradle
new file mode 100644
index 0000000..fc20e82
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy
new file mode 100644
index 0000000..0d91ba0
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy
@@ -0,0 +1,5 @@
+import groovy.transform.TypeChecked
+
+withConfig(configuration) {
+    ast(TypeChecked)
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy
new file mode 100644
index 0000000..1c41eda
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy
@@ -0,0 +1,5 @@
+class BrokenClass {
+    void doesNotTypeCheck() {
+        "foo".bar()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/build.gradle
index 4a687c9..866d9ee 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/build.gradle
@@ -5,6 +5,5 @@ repositories {
 }
 
 dependencies {
-    compile localGroovy()
-    testCompile "junit:junit:4.10"
+    compile "junit:junit:4.12"
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/src/test/groovy/TestCaseTransformTest.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/src/test/groovy/TestCaseTransformTest.groovy
index f9ced50..f5ed8e4 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/src/test/groovy/TestCaseTransformTest.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformThatReferencesGroovyTestCase/src/test/groovy/TestCaseTransformTest.groovy
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-import org.junit.Test
-
+ at TestCase
 class TestCaseTransformTest {
-    @Test
-    void addsGroovyTestCaseAsSuperclass() {
-        assert MyTestCase.superclass == GroovyTestCase
+    void testAddsGroovyTestCaseAsSuperclass() {
+        assert getClass().superclass == GroovyTestCase
+        shouldFail(IOException.class) {
+            throw new IOException()
+        }
     }
-
-    @TestCase
-    static abstract class MyTestCase {}
-}
-
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/build.gradle
index c10fb18..486718c 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.10"
+    testCompile "junit:junit:4.12"
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseBuiltInAstTransform/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseBuiltInAstTransform/build.gradle
index c10fb18..486718c 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseBuiltInAstTransform/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseBuiltInAstTransform/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.10"
+    testCompile "junit:junit:4.12"
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/build.gradle
index c10fb18..486718c 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.10"
+    testCompile "junit:junit:4.12"
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle
new file mode 100644
index 0000000..20d27b7
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle
@@ -0,0 +1,10 @@
+apply plugin: 'groovy'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'javax.servlet:servlet-api:2.4'
+    compile 'javax.servlet:jsp-api:2.0'
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/groovy/com/example/Country.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/groovy/com/example/Country.groovy
new file mode 100644
index 0000000..cbb7f10
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/groovy/com/example/Country.groovy
@@ -0,0 +1,11 @@
+package com.example
+
+import groovy.transform.Canonical
+import groovy.transform.CompileStatic
+
+ at Canonical
+ at CompileStatic
+class Country {
+
+    String name
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
new file mode 100644
index 0000000..a7f2c62
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
@@ -0,0 +1,3 @@
+moduleName=test-extension-module
+moduleVersion=1.0.0
+extensionClasses=groovy.servlet.ServletCategory
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy
new file mode 100644
index 0000000..6daa349
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import groovy.transform.Canonical
+import groovy.transform.CompileStatic
+
+ at Canonical
+ at CompileStatic
+class DummyFileForCompilation {}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/build.gradle
new file mode 100644
index 0000000..081681b
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: "groovy"
+
+dependencies {
+    compile localGroovy()
+}
+
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy
new file mode 100644
index 0000000..fa35f59
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy
@@ -0,0 +1,4 @@
+import groovy.transform.TypeChecked
+
+withConfig(configuration) {
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newbuild.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newbuild.gradle
new file mode 100644
index 0000000..5a42ecd
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newbuild.gradle
@@ -0,0 +1,5 @@
+apply plugin: "groovy"
+
+dependencies {
+    compile localGroovy()
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy
new file mode 100644
index 0000000..0d91ba0
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy
@@ -0,0 +1,5 @@
+import groovy.transform.TypeChecked
+
+withConfig(configuration) {
+    ast(TypeChecked)
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy
new file mode 100644
index 0000000..1c41eda
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy
@@ -0,0 +1,5 @@
+class BrokenClass {
+    void doesNotTypeCheck() {
+        "foo".bar()
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenTestFrameworkChanges/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenTestFrameworkChanges/build.gradle
index ae579c0..ee2c4e0 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenTestFrameworkChanges/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenTestFrameworkChanges/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
     testCompile 'org.testng:testng:6.3.1'
 }
 
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/build.gradle
index a3ad914..0604758 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle
index 46d17d3..8196f19 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.7"
+    testCompile "junit:junit:4.12"
 }
 
 test {
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle
index b4e8810..b0e7c96 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.7"
+    testCompile "junit:junit:4.12"
 }
 
 test {
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
index 49be87a..24b2a7f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
@@ -7,7 +7,7 @@ import static junit.framework.Assert.*;
 public class MySystemClassLoader extends URLClassLoader {
     public MySystemClassLoader(ClassLoader parent) {
         super(new URL[0], parent);
-        // Should be constructed with the default system ClassLoader as parent
+        // Should be constructed with the default system ClassLoader as root
         assertEquals(getClass().getClassLoader(), parent);
     }
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle
index 2d44108..b64addf 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    compile "junit:junit:4.7"
+    compile "junit:junit:4.12"
 }
 
 jar {
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java
index adcdd5c..82475b8 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java
@@ -13,7 +13,7 @@ import static junit.framework.Assert.assertEquals;
 public class MySystemClassLoader extends URLClassLoader {
     public MySystemClassLoader(ClassLoader parent) throws URISyntaxException, ClassNotFoundException {
         super(new URL[0], parent);
-        // Should be constructed with the default system ClassLoader as parent
+        // Should be constructed with the default system ClassLoader as root
         assertEquals(getClass().getClassLoader(), parent);
         addClasspathFor("org.gradle.MyAgent");
         addClasspathFor("org.gradle.JUnitTest");
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithJMockitLoadedWithJavaAgent/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithJMockitLoadedWithJavaAgent/build.gradle
index e844153..cfa1446 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithJMockitLoadedWithJavaAgent/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithJMockitLoadedWithJavaAgent/build.gradle
@@ -11,7 +11,7 @@ configurations {
 
 dependencies {
     jmockit "com.googlecode.jmockit:jmockit:0.999.13"
-    testCompile "junit:junit:4.7"
+    testCompile "junit:junit:4.12"
 }
 
 test {
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest/testReportingSupportsCucumberStepsWithSlashes/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest/testReportingSupportsCucumberStepsWithSlashes/build.gradle
index 0123629..eb5f08a 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest/testReportingSupportsCucumberStepsWithSlashes/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/cucumberjvm/CucumberJVMReportIntegrationTest/testReportingSupportsCucumberStepsWithSlashes/build.gradle
@@ -21,7 +21,7 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.11"
+    testCompile "junit:junit:4.12"
     testCompile "info.cukes:cucumber-java:1.1.2"
     testCompile "info.cukes:cucumber-junit:1.1.2"
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle
index 1c5f193..196fb3a 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle
@@ -23,5 +23,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..6a9ca40
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile "org.codehaus.groovy:groovy:2.3.10"
+    testCompile "junit:junit:4.12"
+}
+
+test {
+    testLogging {
+        quiet {
+            events "skipped", "failed"
+            minGranularity 2
+            maxGranularity -1
+            displayGranularity 3
+            exceptionFormat "full"
+            stackTraceFilters "truncate", "groovy"
+        }
+    }
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy
rename to subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle
new file mode 100644
index 0000000..983b04a
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle
@@ -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.
+ */
+
+apply plugin: "groovy"
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile "org.codehaus.groovy:groovy:2.3.10"
+    testCompile "junit:junit:4.12"
+}
+
+test {
+    testLogging {
+        quiet {
+            events "standardOut", "standardError"
+        }
+    }
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy
similarity index 100%
rename from subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy
rename to subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/build.gradle
deleted file mode 100644
index ba9f816..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-apply plugin: 'groovy'
-repositories { mavenCentral() }
-dependencies {
-    testCompile localGroovy()
-    testCompile 'junit:junit:4.11'
-}
-test {
-    testLogging {
-        showStandardStreams = true
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/src/test/groovy/org/gradle/SystemErrTest.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/src/test/groovy/org/gradle/SystemErrTest.groovy
deleted file mode 100644
index d48da1d..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemErr/src/test/groovy/org/gradle/SystemErrTest.groovy
+++ /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
-
-public class SystemErrTest {
-    @org.junit.Test
-    void test() {
-        System.err.println ("thread 0 err")
-        def thread1 = Thread.start {
-            System.err.println "thread 1 err"
-        }
-        def thread2 = Thread.start {
-            System.err.println "thread 2 err"
-        }
-        thread1.join()
-        thread2.join()
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/build.gradle
deleted file mode 100644
index ba9f816..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-apply plugin: 'groovy'
-repositories { mavenCentral() }
-dependencies {
-    testCompile localGroovy()
-    testCompile 'junit:junit:4.11'
-}
-test {
-    testLogging {
-        showStandardStreams = true
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/src/test/groovy/org/gradle/SystemOutTest.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/src/test/groovy/org/gradle/SystemOutTest.groovy
deleted file mode 100644
index 21a1f32..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHandleMultipleThreadsWritingToSystemOut/src/test/groovy/org/gradle/SystemOutTest.groovy
+++ /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
-
-public class SystemOutTest {
-    @org.junit.Test
-    void test() {
-        println ("thread 0 out")
-        def thread1 = Thread.start {
-            println "thread 1 out"
-        }
-        def thread2 = Thread.start {
-            println "thread 2 out"
-        }
-        thread1.join()
-        thread2.join()
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
index f2aaa6f..26a9eb6 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
@@ -17,5 +17,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle
index 822260c..8c0741b 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle
new file mode 100644
index 0000000..02731ea
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'junit:junit:4.12'
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java
new file mode 100644
index 0000000..9892c46
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java
@@ -0,0 +1,57 @@
+package org.gradle;
+
+import java.lang.reflect.Method;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+public class CustomRunner extends BlockJUnit4ClassRunner {
+    public static boolean isClassUnderTestLoaded;
+    private final Class<?> bootstrappedTestClass;
+
+    public CustomRunner(Class<?> clazz) throws Exception {
+        super(clazz);
+        bootstrappedTestClass = clazz;
+    }
+
+    @Override
+    protected Statement methodBlock(final FrameworkMethod method) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+
+                if(isClassUnderTestLoaded){
+                    throw new RuntimeException("Test Class should not be loaded");
+                }
+
+                final HelperTestRunner helperTestRunner = new HelperTestRunner(bootstrappedTestClass);
+                final Method bootstrappedMethod = bootstrappedTestClass.getMethod(method.getName());
+                final Statement statement = helperTestRunner.methodBlock(new FrameworkMethod(bootstrappedMethod));
+                statement.evaluate();
+            }
+        };
+    }
+
+    public class HelperTestRunner extends BlockJUnit4ClassRunner {
+        public HelperTestRunner(Class<?> testClass) throws InitializationError {
+            super(testClass);
+        }
+
+        @Override
+        protected Object createTest() throws Exception {
+            return super.createTest();
+        }
+
+        @Override
+        public Statement classBlock(RunNotifier notifier) {
+            return super.classBlock(notifier);
+        }
+
+        @Override
+        public Statement methodBlock(FrameworkMethod method) {
+            return super.methodBlock(method);
+        }
+    }
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java
new file mode 100644
index 0000000..b8f5dbf
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java
@@ -0,0 +1,20 @@
+package org.gradle;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+
+ at RunWith(CustomRunner.class)
+public class ExecutionOrderTest {
+
+    static{
+        CustomRunner.isClassUnderTestLoaded = true;
+    }
+
+	@Test
+	public void classUnderTestIsLoadedOnlyByRunner(){
+		// The CustomRunner class will fail this test if this class is initialized before its 
+		// run method is triggered. 
+	}
+	
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle
index 5bf20b1..02731ea 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle
@@ -4,5 +4,5 @@ repositories {
     mavenCentral()
 }
 dependencies {
-    compile 'junit:junit:4.11'
+    compile 'junit:junit:4.12'
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
index 9baa5a2..0c80015 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile 'junit:junit:4.11', 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1' }
+dependencies { testCompile 'junit:junit:4.12', 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1' }
 test {
     systemProperties.testSysProperty = 'value'
     systemProperties.projectDir = projectDir
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
index 51d8bd7..74f78c0 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
@@ -14,28 +14,10 @@ import java.util.regex.Pattern;
 import static org.junit.Assert.*;
 
 public class OkTest {
-    static {
-        System.out.println("class loaded");
-    }
-
-    public OkTest() {
-        System.out.println("test constructed");
-    }
-
-    @org.junit.BeforeClass public static void init() {
-        System.out.println("before class out");
-        System.err.println("before class err");
-    }
-
-    @org.junit.AfterClass public static void end() {
-        System.out.println("after class out");
-        System.err.println("after class err");
-    }
-
     @org.junit.Test
     public void ok() throws Exception {
         // check versions of dependencies
-        assertEquals("4.11", new org.junit.runner.JUnitCore().getVersion());
+        assertEquals("4.12", new org.junit.runner.JUnitCore().getVersion());
         assertTrue(org.apache.tools.ant.Main.getAntVersion().contains("1.6.1"));
 
         // check working dir
@@ -71,14 +53,6 @@ public class OkTest {
         assertEquals("Test worker", Thread.currentThread().getName());
         assertNull(System.getSecurityManager());
 
-        // check stdout and stderr and logging
-        System.out.println("This is test stdout");
-        System.out.println("non-asci char: ż");
-        System.out.print("no EOL");
-        System.out.println();
-        System.err.println("This is test stderr");
-        Logger.getLogger("test-logger").warning("this is a warning");
-
         final PrintStream out = System.out;
         // logging from a shutdown hook
         Runtime.getRuntime().addShutdownHook(new Thread() {
@@ -88,17 +62,6 @@ public class OkTest {
                 Logger.getLogger("test-logger").info("info from a shutdown hook.");
             }
         });
-
-        // logging from another thread
-        Thread thread = new Thread() {
-            @Override
-            public void run() {
-                System.out.println("stdout from another thread");
-                Logger.getLogger("test-logger").info("info from another thread.");
-            }
-        };
-        thread.start();
-        thread.join();
     }
 
     private List<URL> buildActualClassPath() {
@@ -117,7 +80,5 @@ public class OkTest {
 
     @org.junit.Test
     public void anotherOk() {
-        System.out.println("sys out from another test method");
-        System.err.println("sys err from another test method");
     }
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
index 36cbf50..52c9af4 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
@@ -1,22 +1,7 @@
 package org.gradle;
 
-import java.util.logging.Logger;
-
 public class OtherTest {
-    static {
-        System.out.println("other class loaded");
-    }
-
-    public OtherTest() {
-        System.out.println("other test constructed");
-    }
-
     @org.junit.Test
     public void ok() throws Exception {
-        // check stdout and stderr
-        System.out.println("This is other stdout");
-        System.err.println("This is other stderr");
-        // check logging
-        Logger.getLogger("test-logger").warning("this is another warning");
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
index 0fc8356..4a6b539 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
@@ -3,5 +3,5 @@ repositories {
     mavenCentral()
 }
 dependencies {
-    testCompile 'junit:junit:4.11'
+    testCompile 'junit:junit:4.12'
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
index 3019224..92753b3 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
@@ -12,4 +12,8 @@ public class Unloadable {
     @Test
     public void ok() {
     }
+
+    @Test
+    public void ok2() {
+    }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle
index ea091ee..e28a3f9 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile 'junit:junit:4.11'}
+dependencies { testCompile 'junit:junit:4.12'}
 test {
     include '**/ASuite.class'
     exclude '**/*Test.class'
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle
index fd11e8d..899a755 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile "junit:junit:4.11"
+    testCompile "junit:junit:4.12"
 }
 
 test {
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle
index d03384a..3c08b81 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'java'
 repositories { mavenCentral() }
-dependencies { testCompile 'junit:junit:4.11'}
+dependencies { testCompile 'junit:junit:4.12'}
 test {
     include '**/*Suite.class'
     exclude '**/*Test.class'
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle
new file mode 100644
index 0000000..2ea8b4c
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle
@@ -0,0 +1,10 @@
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.12'
+    testCompile 'net.java.dev.jna:jna-platform:4.1.0'
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java
new file mode 100644
index 0000000..70ed647
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java
@@ -0,0 +1,9 @@
+import org.junit.Test;
+import com.sun.jna.platform.win32.Shell32;
+
+public class OkTest {
+    @Test
+    public void ok() {
+        assert Shell32.INSTANCE != null;
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/shared/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/shared/build.gradle
deleted file mode 100644
index 79280e7..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-apply plugin: "groovy"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    compile "org.codehaus.groovy:groovy-all:2.0.5"
-    testCompile "junit:junit:4.11"
-}
-
-test {
-    testLogging {
-        quiet {
-            events "skipped", "failed"
-            minGranularity 2
-            maxGranularity -1
-            displayGranularity 3
-            exceptionFormat "full"
-            stackTraceFilters "truncate", "groovy"
-        }
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/standardOutputLogging/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/standardOutputLogging/build.gradle
deleted file mode 100644
index 68c2ba1..0000000
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/junit/JUnitLoggingIntegrationTest/standardOutputLogging/build.gradle
+++ /dev/null
@@ -1,34 +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.
- */
-
-apply plugin: "groovy"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    compile "org.codehaus.groovy:groovy-all:2.0.5"
-    testCompile "junit:junit:4.11"
-}
-
-test {
-    testLogging {
-        quiet {
-            events "standardOut", "standardError"
-        }
-    }
-}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..0e5cc3d
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: "java"
+
+repositories {
+    mavenCentral()
+}
+
+test {
+    useTestNG {
+        useDefaultListeners = true
+    }
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/src/test/java/org/gradle/failurepolicy/TestWithFailureInConfigMethod.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/src/test/java/org/gradle/failurepolicy/TestWithFailureInConfigMethod.java
new file mode 100644
index 0000000..7a78481
--- /dev/null
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGFailurePolicyIntegrationTest/shared/src/test/java/org/gradle/failurepolicy/TestWithFailureInConfigMethod.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.failurepolicy;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class TestWithFailureInConfigMethod {
+
+    @BeforeTest
+    public void fail() {
+        throw new RuntimeException("Failure in @BeforeTest");
+    }
+
+    @Test
+    public void someTest() {}
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
index ac7a3ad..90b26a6 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
@@ -1,7 +1,5 @@
 package org.gradle;
 
-import java.util.logging.Logger;
-
 import static org.testng.Assert.*;
 
 public class OkTest {
@@ -30,10 +28,5 @@ public class OkTest {
         // check other environmental stuff
         assertEquals("Test worker", Thread.currentThread().getName());
         assertNull(System.getSecurityManager());
-
-        // check logging
-        System.out.println("stdout");
-        System.err.println("stderr");
-        Logger.getLogger("test").warning("a warning");
     }
 }
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
index 0e7278c..af11fac 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
@@ -7,7 +7,7 @@ repositories {
 }
 
 dependencies {
-	compile "org.codehaus.groovy:groovy-all:2.0.5"
+	compile "org.codehaus.groovy:groovy:2.3.10"
     testCompile 'org.testng:testng:6.3.1'
 }
 
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
index 0e7278c..af11fac 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/testing/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
@@ -7,7 +7,7 @@ repositories {
 }
 
 dependencies {
-	compile "org.codehaus.groovy:groovy-all:2.0.5"
+	compile "org.codehaus.groovy:groovy:2.3.10"
     testCompile 'org.testng:testng:6.3.1'
 }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/distribution/plugins/DistributionPlugin.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/distribution/plugins/DistributionPlugin.groovy
index d8b504e..d287703 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/distribution/plugins/DistributionPlugin.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/distribution/plugins/DistributionPlugin.groovy
@@ -16,13 +16,13 @@
 
 package org.gradle.api.distribution.plugins
 
-import org.gradle.api.GradleException
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.Project
+import org.gradle.api.*
 import org.gradle.api.distribution.Distribution
 import org.gradle.api.distribution.internal.DefaultDistributionContainer
+import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact
 import org.gradle.api.internal.file.FileOperations
+import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
+import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.plugins.BasePlugin
 import org.gradle.api.tasks.Sync
 import org.gradle.api.tasks.bundling.AbstractArchiveTask
@@ -33,12 +33,17 @@ import org.gradle.internal.reflect.Instantiator
 import javax.inject.Inject
 
 /**
- * Adds the ability to create distributions of the project.
+ * <p>A {@link Plugin} to package project as a distribution.</p>
+ *
+ *
  */
 @Incubating
-class DistributionPlugin implements Plugin<Project> {
-
+class DistributionPlugin implements Plugin<ProjectInternal> {
+    /**
+     * Name of the main distribution
+     */
     private static final String MAIN_DISTRIBUTION_NAME = "main"
+
     private static final String DISTRIBUTION_GROUP = "distribution"
     private static final String TASK_DIST_ZIP_NAME = "distZip"
     private static final String TASK_DIST_TAR_NAME = "distTar"
@@ -48,46 +53,44 @@ class DistributionPlugin implements Plugin<Project> {
     private final FileOperations fileOperations
 
     @Inject
-    DistributionPlugin(Instantiator instantiator, FileOperations fileOperations) {
+    public DistributionPlugin(Instantiator instantiator, FileOperations fileOperations) {
+        this.instantiator = instantiator;
         this.fileOperations = fileOperations
-        this.instantiator = instantiator
     }
 
-    void apply(Project project) {
-        project.plugins.apply(BasePlugin)
+    public void apply(ProjectInternal project) {
+        project.pluginManager.apply(BasePlugin)
 
         def distributions = project.extensions.create("distributions", DefaultDistributionContainer, Distribution, instantiator, fileOperations)
-
         // TODO - refactor this action out so it can be unit tested
         distributions.all { dist ->
-            dist.baseName = dist.name == MAIN_DISTRIBUTION_NAME ? project.name : String.format("%s-%s", project.name, dist.name)
-            dist.contents.from("src/$dist.name/dist")
-
-            addZipTask(project, dist)
-            addTarTask(project, dist)
+            dist.conventionMapping.map("baseName", { dist.name == MAIN_DISTRIBUTION_NAME ? project.name : String.format("%s-%s", project.name, dist.name) })
+            dist.contents.from("src/${dist.name}/dist")
+            def zipTask = addZipTask(project, dist)
+            def tarTask = addTarTask(project, dist)
+            addAssembleTask(project, dist, zipTask, tarTask)
             addInstallTask(project, dist)
         }
-
         distributions.create(MAIN_DISTRIBUTION_NAME)
     }
 
-    void addZipTask(Project project, Distribution distribution) {
+    Task addZipTask(Project project, Distribution distribution) {
         def taskName = TASK_DIST_ZIP_NAME
-        if (MAIN_DISTRIBUTION_NAME != distribution.name) {
+        if (!MAIN_DISTRIBUTION_NAME.equals(distribution.name)) {
             taskName = distribution.name + "DistZip"
         }
         configureArchiveTask(project, taskName, distribution, Zip)
     }
 
-    void addTarTask(Project project, Distribution distribution) {
+    Task addTarTask(Project project, Distribution distribution) {
         def taskName = TASK_DIST_TAR_NAME
-        if (MAIN_DISTRIBUTION_NAME != distribution.name) {
+        if (!MAIN_DISTRIBUTION_NAME.equals(distribution.name)) {
             taskName = distribution.name + "DistTar"
         }
         configureArchiveTask(project, taskName, distribution, Tar)
     }
 
-    private <T extends AbstractArchiveTask> void configureArchiveTask(Project project, String taskName, Distribution distribution, Class<T> type) {
+    private <T extends AbstractArchiveTask> Task configureArchiveTask(Project project, String taskName, Distribution distribution, Class<T> type) {
         def archiveTask = project.tasks.create(taskName, type)
         archiveTask.description = "Bundles the project as a distribution."
         archiveTask.group = DISTRIBUTION_GROUP
@@ -101,27 +104,28 @@ class DistributionPlugin implements Plugin<Project> {
         archiveTask.into(baseDir) {
             with(distribution.contents)
         }
+        ArchivePublishArtifact archiveArtifact = new ArchivePublishArtifact(archiveTask);
+        project.getExtensions().getByType(DefaultArtifactPublicationSet.class).addCandidate(archiveArtifact);
+        archiveTask
     }
 
     private void addInstallTask(Project project, Distribution distribution) {
         def taskName = TASK_INSTALL_NAME
         if (MAIN_DISTRIBUTION_NAME != distribution.name) {
-            taskName = "install"+ distribution.name.capitalize() + "Dist"
+            taskName = "install" + distribution.name.capitalize() + "Dist"
         }
         def installTask = project.tasks.create(taskName, Sync)
-        installTask.description = "Installs the project as a JVM application along with libs and OS specific scripts."
+        installTask.description = "Installs the project as a distribution as-is."
         installTask.group = DISTRIBUTION_GROUP
         installTask.with distribution.contents
         installTask.into { project.file("${project.buildDir}/install/${distribution.baseName}") }
-        installTask.doFirst {
-            if (destinationDir.directory) {
-                if (!new File(destinationDir, 'lib').directory || !new File(destinationDir, 'bin').directory) {
-                    throw new GradleException("The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${distribution.name}'.\n" +
-                            "If you really want to install to this directory, delete it and run the install task again.\n" +
-                            "Alternatively, choose a different installation directory."
-                    )
-                }
-            }
-        }
+    }
+
+    private void addAssembleTask(Project project, Distribution distribution, Task... tasks) {
+        def taskName = "assemble" + distribution.name.capitalize() + "Dist"
+        Task assembleTask = project.getTasks().create(taskName);
+        assembleTask.setDescription("Assembles the " + distribution.name + " distributions");
+        assembleTask.setGroup(DISTRIBUTION_GROUP);
+        assembleTask.dependsOn(tasks);
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/AbstractLanguageSourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/AbstractLanguageSourceSet.java
new file mode 100644
index 0000000..2407e32
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/AbstractLanguageSourceSet.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.java;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+import org.gradle.language.base.internal.LanguageSourceSetInternal;
+
+public abstract class AbstractLanguageSourceSet extends AbstractBuildableModelElement implements LanguageSourceSetInternal {
+    private final String name;
+    private final String fullName;
+    private final String displayName;
+    private final SourceDirectorySet source;
+    private boolean generated;
+    private Task generatorTask;
+
+    public AbstractLanguageSourceSet(String name, String parentName, String typeName, SourceDirectorySet source) {
+        this.name = name;
+        this.fullName = parentName + StringUtils.capitalize(name);
+        this.displayName = String.format("%s '%s:%s'", typeName, parentName, name);
+        this.source = source;
+        super.builtBy(source.getBuildDependencies());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    @Override
+    public void builtBy(Object... tasks) {
+        generated = true;
+        super.builtBy(tasks);
+    }
+
+    public void generatedBy(Task generatorTask) {
+        this.generatorTask = generatorTask;
+    }
+
+    public Task getGeneratorTask() {
+        return generatorTask;
+    }
+
+    public boolean getMayHaveSources() {
+        // TODO:DAZ This doesn't take into account build dependencies of the SourceDirectorySet.
+        // Should just ditch SourceDirectorySet from here since it's not really a great model, and drags in too much baggage.
+        return generated || !source.isEmpty();
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    public void source(Action<? super SourceDirectorySet> config) {
+        config.execute(getSource());
+    }
+
+    public SourceDirectorySet getSource() {
+        return source;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJavaSourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJavaSourceSet.java
new file mode 100644
index 0000000..f2773cf
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJavaSourceSet.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.internal.java;
+
+import org.gradle.api.Task;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.tasks.TaskDependency;
+import org.gradle.jvm.Classpath;
+import org.gradle.language.java.JavaSourceSet;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DefaultJavaSourceSet extends AbstractLanguageSourceSet implements JavaSourceSet {
+    private final Classpath compileClasspath;
+
+    public DefaultJavaSourceSet(String name, String parent, SourceDirectorySet source, Classpath compileClasspath) {
+        super(name, parent, "Java source", source);
+        this.compileClasspath = compileClasspath;
+    }
+
+    public Classpath getCompileClasspath() {
+        return compileClasspath;
+    }
+
+    public TaskDependency getBuildDependencies() {
+        return new TaskDependency() {
+            public Set<? extends Task> getDependencies(Task task) {
+                Set<Task> dependencies = new HashSet<Task>();
+                dependencies.addAll(compileClasspath.getBuildDependencies().getDependencies(task));
+                dependencies.addAll(getSource().getBuildDependencies().getDependencies(task));
+                return dependencies;
+            }
+        };
+    }
+
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJvmResourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJvmResourceSet.java
new file mode 100644
index 0000000..bb2f1ba
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/java/DefaultJvmResourceSet.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.java;
+
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.language.jvm.JvmResourceSet;
+
+public class DefaultJvmResourceSet extends AbstractLanguageSourceSet implements JvmResourceSet {
+    public DefaultJvmResourceSet(String name, String parent, SourceDirectorySet source) {
+        super(name, parent, "JVM resources", source);
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingScheme.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingScheme.java
new file mode 100644
index 0000000..6718dd0
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingScheme.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.jvm;
+
+import org.gradle.api.Nullable;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.util.GUtil;
+
+import java.util.Collections;
+import java.util.List;
+
+class ClassDirectoryBinaryNamingScheme implements BinaryNamingScheme {
+    private final String baseName;
+    private final String collapsedName;
+
+    public ClassDirectoryBinaryNamingScheme(String baseName) {
+        this.baseName = baseName;
+        this.collapsedName = collapseMain(this.baseName);
+    }
+
+    private static String collapseMain(String name) {
+        return name.equals("main") ? "" : name;
+    }
+
+    public String getDescription() {
+        return String.format("classes '%s'", baseName);
+    }
+
+    public List<String> getVariantDimensions() {
+        return Collections.emptyList();
+    }
+
+    public String getBaseName() {
+        return baseName;
+    }
+
+    public String getLifecycleTaskName() {
+        return getTaskName(null, "classes");
+    }
+
+    public String getTaskName(@Nullable String verb) {
+        return getTaskName(verb, null);
+    }
+
+    public String getTaskName(@Nullable String verb, @Nullable String target) {
+        String name = baseName;
+        if (target != null) {
+            name = collapsedName;
+        }
+        return GUtil.toLowerCamelCase(String.format("%s %s %s", nullToEmpty(verb), name, nullToEmpty(target)));
+    }
+
+    private String nullToEmpty(String input) {
+        return input == null ? "" : input;
+    }
+
+    public String getOutputDirectoryBase() {
+        return baseName;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryRenderer.java
new file mode 100644
index 0000000..5e9322a
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryRenderer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.jvm;
+
+import org.gradle.jvm.internal.AbstractJvmBinaryRenderer;
+import org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder;
+import org.gradle.jvm.ClassDirectoryBinarySpec;
+
+public class ClassDirectoryBinaryRenderer extends AbstractJvmBinaryRenderer<ClassDirectoryBinarySpec> {
+    @Override
+    public Class<ClassDirectoryBinarySpec> getTargetType() {
+        return ClassDirectoryBinarySpec.class;
+    }
+
+    @Override
+    protected void renderOutputs(ClassDirectoryBinarySpec binary, TextReportBuilder builder) {
+        builder.item("classes dir", binary.getClassesDir());
+        builder.item("resources dir", binary.getResourcesDir());
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinarySpecInternal.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinarySpecInternal.java
new file mode 100644
index 0000000..5d98367
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinarySpecInternal.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.jvm;
+
+import org.gradle.jvm.ClassDirectoryBinarySpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.BinarySpecInternal;
+
+public interface ClassDirectoryBinarySpecInternal extends ClassDirectoryBinarySpec, BinarySpecInternal {
+    BinaryNamingScheme getNamingScheme();
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpec.java
new file mode 100644
index 0000000..01a5cc8
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpec.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.jvm;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.internal.AbstractBuildableModelElement;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.jvm.JvmBinaryTasks;
+import org.gradle.jvm.internal.DefaultJvmBinaryTasks;
+import org.gradle.jvm.platform.JavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.platform.base.BinaryTasksCollection;
+import org.gradle.platform.base.internal.*;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+import java.io.File;
+
+public class DefaultClassDirectoryBinarySpec extends AbstractBuildableModelElement implements ClassDirectoryBinarySpecInternal {
+    private final DefaultDomainObjectSet<LanguageSourceSet> sourceSets = new DefaultDomainObjectSet<LanguageSourceSet>(LanguageSourceSet.class);
+    private final BinaryNamingScheme namingScheme;
+    private final String name;
+    private final JavaToolChain toolChain;
+    private final JavaPlatform platform;
+    private final DefaultJvmBinaryTasks tasks;
+    private final ToolResolver toolResolver;
+    private File classesDir;
+    private File resourcesDir;
+    private boolean buildable = true;
+
+    public DefaultClassDirectoryBinarySpec(String name, JavaToolChain toolChain, JavaPlatform platform, Instantiator instantiator, ITaskFactory taskFactory, ToolResolver toolResolver) {
+        this.name = name;
+        this.toolChain = toolChain;
+        this.platform = platform;
+        this.namingScheme = new ClassDirectoryBinaryNamingScheme(removeClassesSuffix(name));
+        this.tasks = instantiator.newInstance(DefaultJvmBinaryTasks.class, new DefaultBinaryTasksCollection(this, taskFactory));
+        this.toolResolver = toolResolver;
+    }
+
+    private String removeClassesSuffix(String name) {
+        if (name.endsWith("Classes")) {
+            return name.substring(0, name.length() - 7);
+        }
+        return name;
+    }
+
+    public JvmBinaryTasks getTasks() {
+        return tasks;
+    }
+
+    @Override
+    public void tasks(Action<? super BinaryTasksCollection> action) {
+        action.execute(tasks);
+    }
+
+    public JavaToolChain getToolChain() {
+        return toolChain;
+    }
+
+    public JavaPlatform getTargetPlatform() {
+        return platform;
+    }
+
+    public void setTargetPlatform(JavaPlatform platform) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setToolChain(JavaToolChain toolChain) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ToolResolver getToolResolver() {
+        return toolResolver;
+    }
+
+    @Override
+    public void setToolResolver(ToolResolver toolResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean isBuildable() {
+        return getBuildAbility().isBuildable();
+    }
+
+    public void setBuildable(boolean buildable) {
+        this.buildable = buildable;
+    }
+
+    public boolean isLegacyBinary() {
+        return true;
+    }
+
+    public BinaryNamingScheme getNamingScheme() {
+        return namingScheme;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public File getClassesDir() {
+        return classesDir;
+    }
+
+    public void setClassesDir(File classesDir) {
+        this.classesDir = classesDir;
+    }
+
+    public File getResourcesDir() {
+        return resourcesDir;
+    }
+
+    public void setResourcesDir(File resourcesDir) {
+        this.resourcesDir = resourcesDir;
+    }
+
+    public FunctionalSourceSet getBinarySources() {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setBinarySources(FunctionalSourceSet sources) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void sources(Action<? super PolymorphicDomainObjectContainer<LanguageSourceSet>> action) {
+        throw new UnsupportedOperationException();
+    }
+
+    public DomainObjectSet<LanguageSourceSet> getSource() {
+        return sourceSets;
+    }
+
+    public void source(Object source) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getDisplayName() {
+        return namingScheme.getDescription();
+    }
+
+    public String toString() {
+        return getDisplayName();
+    }
+
+    @Override
+    public BinaryBuildAbility getBuildAbility() {
+        if (!buildable) {
+            return new FixedBuildAbility(false);
+        }
+        return new ToolSearchBuildAbility(toolResolver.checkToolAvailability(getTargetPlatform()));
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/CleanRule.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/CleanRule.java
deleted file mode 100644
index 4266b35..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/CleanRule.java
+++ /dev/null
@@ -1,56 +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.plugins;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Delete;
-import org.gradle.api.tasks.TaskContainer;
-
-public class CleanRule extends AbstractRule {
-
-    public static final String PREFIX = "clean";
-
-    private final TaskContainer tasks;
-
-    public CleanRule(TaskContainer tasks) {
-        this.tasks = tasks;
-    }
-
-    public String getDescription() {
-        return String.format("Pattern: %s<TaskName>: Cleans the output files of a task.", PREFIX);
-    }
-
-    public void apply(String taskName) {
-        if (!taskName.startsWith(PREFIX)) {
-            return;
-        }
-
-        String targetTaskName = taskName.substring(PREFIX.length());
-        if (Character.isLowerCase(targetTaskName.charAt(0))) {
-            return;
-        }
-
-        Task task = tasks.findByName(StringUtils.uncapitalize(targetTaskName));
-        if (task == null) {
-            return;
-        }
-
-        Delete clean = tasks.create(taskName, Delete.class);
-        clean.delete(task.getOutputs().getFiles());
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.java
deleted file mode 100644
index 231957a..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.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.plugins;
-
-/**
- * Copies resources from their source to their target directory, potentially processing them.
- * Makes sure no stale resources remain in the target directory.
- *
- * @deprecated use {@link org.gradle.language.jvm.tasks.ProcessResources} instead
- */
- at Deprecated
-public class ProcessResources extends org.gradle.language.jvm.tasks.ProcessResources {}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.groovy
deleted file mode 100644
index c92810c..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.groovy
+++ /dev/null
@@ -1,162 +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.plugins
-
-import groovy.text.SimpleTemplateEngine
-import org.gradle.util.TextUtil
-import org.gradle.util.AntUtil
-import org.apache.tools.ant.taskdefs.Chmod
-import org.gradle.util.GFileUtils
-
-class StartScriptGenerator {
-    /**
-     * The display name of the application
-     */
-    String applicationName
-
-    /**
-     * The environment variable to use to provide additional options to the JVM
-     */
-    String optsEnvironmentVar
-
-    /**
-     * The environment variable to use to control exit value (windows only)
-     */
-    String exitEnvironmentVar
-
-    String mainClassName
-
-    Iterable<String> defaultJvmOpts = []
-
-    /**
-     * The classpath, relative to the application home directory.
-     */
-    Iterable<String> classpath
-
-    /**
-     * The path of the script, relative to the application home directory.
-     */
-    String scriptRelPath
-
-    /**
-     * This system property to use to pass the script name to the application. May be null.
-     */
-    String appNameSystemProperty
-
-    private final engine = new SimpleTemplateEngine()
-
-    void generateUnixScript(File unixScript) {
-        String nativeOutput = generateUnixScriptContent()
-        writeToFile(nativeOutput, unixScript)
-        createExecutablePermission(unixScript)
-    }
-
-    String generateUnixScriptContent() {
-        def unixClassPath = classpath.collect { "\$APP_HOME/${it.replace('\\', '/')}" }.join(":")
-        def quotedDefaultJvmOpts = defaultJvmOpts.collect{
-            //quote ', ", \, $. Probably not perfect. TODO: identify non-working cases, fail-fast on them
-            it = it.replace('\\', '\\\\')
-            it = it.replace('"', '\\"')
-            it = it.replace(/'/, /'"'"'/)
-            it = it.replace(/`/, /'"`"'/)
-            it = it.replace('$', '\\$')
-            (/"${it}"/)
-        }
-        //put the whole arguments string in single quotes, unless defaultJvmOpts was empty,
-        // in which case we output "" to stay compatible with existing builds that scan the script for it
-        def defaultJvmOptsString = (quotedDefaultJvmOpts ? /'${quotedDefaultJvmOpts.join(' ')}'/ : '""')
-        def binding = [applicationName: applicationName,
-                optsEnvironmentVar: optsEnvironmentVar,
-                mainClassName: mainClassName,
-                defaultJvmOpts: defaultJvmOptsString,
-                appNameSystemProperty: appNameSystemProperty,
-                appHomeRelativePath: appHomeRelativePath,
-                classpath: unixClassPath]
-        return generateNativeOutput('unixStartScript.txt', binding, TextUtil.unixLineSeparator)
-    }
-
-    void generateWindowsScript(File windowsScript) {
-        String nativeOutput = generateWindowsScriptContent()
-        writeToFile(nativeOutput, windowsScript);
-    }
-
-    String generateWindowsScriptContent() {
-        def windowsClassPath = classpath.collect { "%APP_HOME%\\${it.replace('/', '\\')}" }.join(";")
-        def appHome = appHomeRelativePath.replace('/', '\\')
-        //argument quoting:
-        // - " must be encoded as \"
-        // - % must be encoded as %%
-        // - pathological case: \" must be encoded as \\\", but other than that, \ MUST NOT be quoted
-        // - other characters (including ') will not be quoted
-        // - use a state machine rather than regexps
-        def quotedDefaultJvmOpts = defaultJvmOpts.collect {
-            def wasOnBackslash = false
-            it = it.collect { ch ->
-                def repl = ch
-                if (ch == '%') {
-                    repl = '%%'
-                } else if (ch == '"') {
-                    repl = (wasOnBackslash ? '\\' : '') + '\\"'
-                }
-                wasOnBackslash = (ch == '\\')
-                repl
-            }
-            (/"${it.join()}"/)
-        }
-        def defaultJvmOptsString = quotedDefaultJvmOpts.join(' ')
-        def binding = [applicationName: applicationName,
-                optsEnvironmentVar: optsEnvironmentVar,
-                exitEnvironmentVar: exitEnvironmentVar,
-                mainClassName: mainClassName,
-                defaultJvmOpts: defaultJvmOptsString,
-                appNameSystemProperty: appNameSystemProperty,
-                appHomeRelativePath: appHome,
-                classpath: windowsClassPath]
-        return generateNativeOutput('windowsStartScript.txt', binding, TextUtil.windowsLineSeparator)
-
-    }
-
-    private void createExecutablePermission(File unixScriptFile) {
-        Chmod chmod = new Chmod()
-        chmod.file = unixScriptFile
-        chmod.perm = "ugo+rx"
-        chmod.project = AntUtil.createProject()
-        chmod.execute()
-    }
-
-    void writeToFile(String scriptContent, File scriptFile) {
-        GFileUtils.mkdirs(scriptFile.parentFile)
-        scriptFile.write(scriptContent)
-    }
-
-
-    private String generateNativeOutput(String templateName, Map binding, String lineSeparator) {
-        def stream = StartScriptGenerator.getResource(templateName)
-        def templateText = stream.text
-        def output = engine.createTemplate(templateText).make(binding)
-        def nativeOutput = TextUtil.convertLineSeparators(output as String, lineSeparator)
-        return nativeOutput;
-
-    }
-
-    private String getAppHomeRelativePath() {
-        def depth = scriptRelPath.count("/")
-        if (depth == 0) {
-            return ""
-        }
-        return (1..depth).collect {".."}.join("/")
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.java
new file mode 100644
index 0000000..bdfd311
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/StartScriptGenerator.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.plugins;
+
+import org.apache.tools.ant.taskdefs.Chmod;
+import org.gradle.api.Action;
+import org.gradle.internal.IoActions;
+import org.gradle.jvm.application.scripts.JavaAppStartScriptGenerationDetails;
+import org.gradle.jvm.application.scripts.ScriptGenerator;
+import org.gradle.util.AntUtil;
+import org.gradle.util.CollectionUtils;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.util.Collections;
+
+public class StartScriptGenerator {
+
+    private String applicationName;
+    private String optsEnvironmentVar;
+    private String exitEnvironmentVar;
+    private String mainClassName;
+    private Iterable<String> defaultJvmOpts = Collections.emptyList();
+    private Iterable<String> classpath;
+    private String scriptRelPath;
+    private String appNameSystemProperty;
+
+    private final ScriptGenerator unixStartScriptGenerator;
+    private final ScriptGenerator windowsStartScriptGenerator;
+    private final UnixFileOperation unixFileOperation;
+
+    public void setApplicationName(String applicationName) {
+        this.applicationName = applicationName;
+    }
+
+    public void setOptsEnvironmentVar(String optsEnvironmentVar) {
+        this.optsEnvironmentVar = optsEnvironmentVar;
+    }
+
+    public void setExitEnvironmentVar(String exitEnvironmentVar) {
+        this.exitEnvironmentVar = exitEnvironmentVar;
+    }
+
+    public void setMainClassName(String mainClassName) {
+        this.mainClassName = mainClassName;
+    }
+
+    public void setDefaultJvmOpts(Iterable<String> defaultJvmOpts) {
+        this.defaultJvmOpts = defaultJvmOpts;
+    }
+
+    public void setClasspath(Iterable<String> classpath) {
+        this.classpath = classpath;
+    }
+
+    public void setScriptRelPath(String scriptRelPath) {
+        this.scriptRelPath = scriptRelPath;
+    }
+
+    public void setAppNameSystemProperty(String appNameSystemProperty) {
+        this.appNameSystemProperty = appNameSystemProperty;
+    }
+
+    public StartScriptGenerator() {
+        this(new UnixStartScriptGenerator(), new WindowsStartScriptGenerator());
+    }
+
+    public StartScriptGenerator(ScriptGenerator unixStartScriptGenerator, ScriptGenerator windowsStartScriptGenerator) {
+        this(unixStartScriptGenerator, windowsStartScriptGenerator, new AntUnixFileOperation());
+    }
+
+    StartScriptGenerator(ScriptGenerator unixStartScriptGenerator, ScriptGenerator windowsStartScriptGenerator, UnixFileOperation unixFileOperation) {
+        this.unixStartScriptGenerator = unixStartScriptGenerator;
+        this.windowsStartScriptGenerator = windowsStartScriptGenerator;
+        this.unixFileOperation = unixFileOperation;
+    }
+
+    private JavaAppStartScriptGenerationDetails createStartScriptGenerationDetails() {
+        return new DefaultJavaAppStartScriptGenerationDetails(
+                applicationName,
+                optsEnvironmentVar,
+                exitEnvironmentVar,
+                mainClassName,
+                CollectionUtils.toStringList(defaultJvmOpts),
+                CollectionUtils.toStringList(classpath),
+                scriptRelPath,
+                appNameSystemProperty
+        );
+    }
+
+    public void generateUnixScript(final File unixScript) {
+        IoActions.writeTextFile(unixScript, new Generate(createStartScriptGenerationDetails(), unixStartScriptGenerator));
+        unixFileOperation.createExecutablePermission(unixScript);
+    }
+
+    public void generateWindowsScript(File windowsScript) {
+        IoActions.writeTextFile(windowsScript, new Generate(createStartScriptGenerationDetails(), windowsStartScriptGenerator));
+    }
+
+    static interface UnixFileOperation {
+        void createExecutablePermission(File file);
+    }
+
+    static class AntUnixFileOperation implements UnixFileOperation {
+        public void createExecutablePermission(File file) {
+            Chmod chmod = new Chmod();
+            chmod.setFile(file);
+            chmod.setPerm("ugo+rx");
+            chmod.setProject(AntUtil.createProject());
+            chmod.execute();
+        }
+    }
+
+    private class Generate implements Action<BufferedWriter> {
+        private final JavaAppStartScriptGenerationDetails startScriptGenerationDetails;
+        private final ScriptGenerator unixStartScriptGenerator;
+
+        public Generate(JavaAppStartScriptGenerationDetails startScriptGenerationDetails, ScriptGenerator unixStartScriptGenerator) {
+            this.startScriptGenerationDetails = startScriptGenerationDetails;
+            this.unixStartScriptGenerator = unixStartScriptGenerator;
+        }
+
+        @Override
+        public void execute(BufferedWriter writer) {
+            unixStartScriptGenerator.generateScript(startScriptGenerationDetails, writer);
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/UploadRule.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/UploadRule.java
index 242152a..39df5a2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/UploadRule.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/plugins/UploadRule.java
@@ -39,9 +39,11 @@ public class UploadRule extends AbstractRule {
     }
 
     public void apply(String taskName) {
-        for (Configuration configuration :  project.getConfigurations()) {
-            if (taskName.equals(configuration.getUploadTaskName())) {
-                createUploadTask(configuration.getUploadTaskName(), configuration, project);
+        if (taskName.startsWith(PREFIX)) {
+            for (Configuration configuration : project.getConfigurations()) {
+                if (taskName.equals(configuration.getUploadTaskName())) {
+                    createUploadTask(configuration.getUploadTaskName(), configuration, project);
+                }
             }
         }
     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/CompileServices.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/CompileServices.java
index 0cd3818..86e0100 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/CompileServices.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/CompileServices.java
@@ -16,22 +16,30 @@
 
 package org.gradle.api.internal.tasks;
 
-import org.gradle.StartParameter;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerClientsManager;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonStarter;
+import org.gradle.api.internal.jvm.ClassDirectoryBinaryRenderer;
 import org.gradle.api.internal.tasks.compile.daemon.InProcessCompilerDaemonFactory;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysisCache;
+import org.gradle.api.internal.tasks.compile.incremental.analyzer.DefaultClassAnalysisCache;
+import org.gradle.api.internal.tasks.compile.incremental.cache.DefaultGeneralCompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.cache.GeneralCompileCaches;
+import org.gradle.api.internal.tasks.compile.incremental.jar.DefaultJarSnapshotCache;
+import org.gradle.api.internal.tasks.compile.incremental.jar.JarSnapshotCache;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.cache.CacheRepository;
 import org.gradle.initialization.JdkToolsInitializer;
-import org.gradle.internal.Factory;
+import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.scopes.PluginServiceRegistry;
-import org.gradle.process.internal.WorkerProcessBuilder;
 
 public class CompileServices implements PluginServiceRegistry {
     public void registerGlobalServices(ServiceRegistration registration) {
+        registration.add(ClassDirectoryBinaryRenderer.class);
     }
 
     public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
         registration.addProvider(new BuildScopeCompileServices());
     }
 
@@ -44,12 +52,20 @@ public class CompileServices implements PluginServiceRegistry {
             initializer.initializeJdkTools();
         }
 
-        CompilerDaemonManager createCompilerDaemonManager(Factory<WorkerProcessBuilder> workerFactory, StartParameter startParameter) {
-            return new CompilerDaemonManager(new CompilerClientsManager(new CompilerDaemonStarter(workerFactory, startParameter)));
+        InProcessCompilerDaemonFactory createInProcessCompilerDaemonFactory(ClassLoaderFactory classLoaderFactory, Gradle gradle) {
+            return new InProcessCompilerDaemonFactory(classLoaderFactory, gradle.getGradleUserHomeDir());
+        }
+
+        GeneralCompileCaches createGeneralCompileCaches(ClassAnalysisCache classAnalysisCache, JarSnapshotCache jarSnapshotCache) {
+            return new DefaultGeneralCompileCaches(classAnalysisCache, jarSnapshotCache);
+        }
+
+        ClassAnalysisCache createClassAnalysisCache(CacheRepository cacheRepository) {
+            return new DefaultClassAnalysisCache(cacheRepository);
         }
 
-        InProcessCompilerDaemonFactory createInProcessCompilerDaemonFactory() {
-            return new InProcessCompilerDaemonFactory();
+        JarSnapshotCache createJarSnapshotCache(CacheRepository cacheRepository) {
+            return new DefaultJarSnapshotCache(cacheRepository);
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainer.java
index f120e1d..d3ccf0b 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainer.java
@@ -15,14 +15,12 @@
  */
 package org.gradle.api.internal.tasks;
 
-import groovy.lang.Closure;
 import org.gradle.api.Namer;
 import org.gradle.api.internal.AbstractNamedDomainObjectContainer;
-import org.gradle.internal.reflect.Instantiator;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.util.DeprecationLogger;
+import org.gradle.internal.reflect.Instantiator;
 
 public class DefaultSourceSetContainer extends AbstractNamedDomainObjectContainer<SourceSet> implements SourceSetContainer {
     private final FileResolver fileResolver;
@@ -30,7 +28,11 @@ public class DefaultSourceSetContainer extends AbstractNamedDomainObjectContaine
     private final Instantiator instantiator;
 
     public DefaultSourceSetContainer(FileResolver fileResolver, TaskResolver taskResolver, Instantiator classGenerator) {
-        super(SourceSet.class, classGenerator, new Namer<SourceSet>() { public String determineName(SourceSet ss) { return ss.getName(); }});
+        super(SourceSet.class, classGenerator, new Namer<SourceSet>() {
+            public String determineName(SourceSet ss) {
+                return ss.getName();
+            }
+        });
         this.fileResolver = fileResolver;
         this.taskResolver = taskResolver;
         this.instantiator = classGenerator;
@@ -43,15 +45,4 @@ public class DefaultSourceSetContainer extends AbstractNamedDomainObjectContaine
 
         return sourceSet;
     }
-
-    public SourceSet add(String name) {
-        DeprecationLogger.nagUserOfReplacedMethod("SourceSetContainer.add()", "create()");
-        return create(name);
-    }
-
-    public SourceSet add(String name, Closure closure) {
-        DeprecationLogger.nagUserOfReplacedMethod("SourceSetContainer.add()", "create()");
-        return create(name, closure);
-    }
-
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetOutput.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetOutput.java
index 300947b..405f0ea 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetOutput.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/DefaultSourceSetOutput.java
@@ -22,7 +22,6 @@ import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
 import org.gradle.api.internal.file.collections.FileCollectionResolveContext;
 import org.gradle.api.tasks.SourceSetOutput;
-import org.gradle.util.DeprecationLogger;
 
 import java.io.File;
 import java.util.HashMap;
@@ -95,13 +94,6 @@ public class DefaultSourceSetOutput extends CompositeFileCollection implements S
         this.dirs.from(dir);
         this.outputDirectories.from(dir);
 
-        Object buildBy = options.get("buildBy");
-        if (buildBy != null) {
-            DeprecationLogger.nagUserOfReplacedNamedParameter("buildBy:", "builtBy:");
-            this.builtBy(buildBy);
-            this.dirs.builtBy(buildBy);
-        }
-
         Object builtBy = options.get("builtBy");
         if (builtBy != null) {
             this.builtBy(builtBy);
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/SourceSetCompileClasspath.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/SourceSetCompileClasspath.java
index 6f4176d..f8931f1 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/SourceSetCompileClasspath.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/SourceSetCompileClasspath.java
@@ -17,7 +17,7 @@ package org.gradle.api.internal.tasks;
 
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.tasks.*;
-import org.gradle.language.jvm.Classpath;
+import org.gradle.jvm.Classpath;
 
 public class SourceSetCompileClasspath implements Classpath {
     private final SourceSet sourceSet;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy
deleted file mode 100644
index 562e407..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile
-
-import org.gradle.api.AntBuilder
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.internal.Factory
-import org.gradle.language.jvm.internal.StaleClassCleaner
-
-class AntDependsStaleClassCleaner extends StaleClassCleaner {
-    private final Factory<AntBuilder> antBuilderFactory
-    private final CompileOptions compileOptions
-
-    File dependencyCacheDir
-
-    AntDependsStaleClassCleaner(Factory<AntBuilder> antBuilderFactory, CompileOptions compileOptions) {
-        this.antBuilderFactory = antBuilderFactory;
-        this.compileOptions = compileOptions;
-    }
-
-    void execute() {
-        def dependArgs = [destDir: destinationDir]
-        def dependOptions = dependArgs + compileOptions.dependOptions.optionMap()
-        if (compileOptions.dependOptions.useCache) {
-            dependOptions['cache'] = dependencyCacheDir
-        }
-
-        def ant = antBuilderFactory.create()
-        ant.project.addTaskDefinition('gradleDepend', AntDepend)
-        ant.gradleDepend(dependOptions) {
-            source.addToAntBuilder(ant, 'src', FileCollection.AntType.MatchingTask)
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntGroovyCompiler.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntGroovyCompiler.groovy
deleted file mode 100644
index 93a34f1..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntGroovyCompiler.groovy
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.tasks.compile
-
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.ClassPathRegistry
-import org.gradle.api.internal.project.IsolatedAntBuilder
-import org.gradle.api.tasks.WorkResult
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.util.VersionNumber
-
-/**
- * Please note: includeAntRuntime=false is ignored if groovyc is used in non fork mode. In this case the runtime classpath is
- * added to the compile classpath.
- * See: http://jira.codehaus.org/browse/GROOVY-2717
- */
-class AntGroovyCompiler implements org.gradle.api.internal.tasks.compile.Compiler<GroovyJavaJointCompileSpec> {
-    private final IsolatedAntBuilder ant
-    private final ClassPathRegistry classPathRegistry
-
-    List nonGroovycJavacOptions = ['verbose', 'deprecation', 'includeJavaRuntime', 'includeAntRuntime', 'optimize', 'fork', 'failonerror', 'listfiles', 'nowarn', 'depend']
-
-    def AntGroovyCompiler(IsolatedAntBuilder ant, ClassPathRegistry classPathRegistry) {
-        this.ant = ant;
-        this.classPathRegistry = classPathRegistry;
-    }
-
-    WorkResult execute(GroovyJavaJointCompileSpec spec) {
-        int numFilesCompiled;
-
-        // Add in commons-cli, as the Groovy POM does not (for some versions of Groovy)
-        Collection antBuilderClasspath = (spec.groovyClasspath as List) + classPathRegistry.getClassPath("COMMONS_CLI").asFiles
-
-        def groovyVersion = sniffGroovyVersion(spec.groovyClasspath)
-        // in Groovy 1.7.11, 1.8.7, and beyond, the combination of includeAntRuntime=false and fork=false is no longer allowed by the
-        // groovyc Ant task and fails hard. That's why we have to enforce includeAntRuntime=true whenever fork=false in these versions,
-        // even though this breaks some stuff. For example, compiling a class that extends GroovyTestCase runs into a NoClassDefFoundError:
-        // org/junit/TestCase then.
-        def includeAntRuntime = groovyVersion == VersionNumber.parse("1.7.11") || groovyVersion >= VersionNumber.parse("1.8.7") ? !spec.groovyCompileOptions.fork : false
-
-        ant.withGroovy(antBuilderClasspath).execute {
-            taskdef(name: 'groovyc', classname: 'org.codehaus.groovy.ant.Groovyc')
-            def task = groovyc([includeAntRuntime: includeAntRuntime, destdir: spec.destinationDir, classpath: ((spec.classpath as List) + antBuilderClasspath).join(File.pathSeparator)]
-                    + spec.groovyCompileOptions.optionMap()) {
-                spec.source.addToAntBuilder(delegate, 'src', FileCollection.AntType.MatchingTask)
-                javac([source: spec.sourceCompatibility, target: spec.targetCompatibility] + filterNonGroovycOptions(spec.compileOptions)) {
-                    spec.compileOptions.compilerArgs.each {value ->
-                        compilerarg(value: value)
-                    }
-                }
-            }
-            numFilesCompiled = task.fileList.length
-        }
-
-        return { numFilesCompiled > 0 } as WorkResult
-    }
-
-    private Map filterNonGroovycOptions(CompileOptions options) {
-        // todo check if groupBy allows a more concise solution
-        Map result = [:]
-        options.optionMap().each {String key, Object value ->
-            if (!nonGroovycJavacOptions.contains(key)) {
-                result[key] = value
-            }
-        }
-        result
-    }
-
-    private VersionNumber sniffGroovyVersion(Iterable<File> classpath) {
-        def classLoader = new URLClassLoader(classpath*.toURI()*.toURL() as URL[], (ClassLoader) null)
-        try {
-            def clazz = classLoader.loadClass("groovy.lang.GroovySystem")
-            return VersionNumber.parse(clazz.getVersion())
-        } catch (ClassNotFoundException ignored) {
-            return VersionNumber.UNKNOWN
-        } catch (LinkageError ignored) {
-            return VersionNumber.UNKNOWN
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy
deleted file mode 100644
index 0b02045..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.tasks.compile
-
-import org.gradle.api.AntBuilder
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.WorkResult
-import org.gradle.internal.Factory
-import org.gradle.internal.jvm.Jvm
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-class AntJavaCompiler implements org.gradle.api.internal.tasks.compile.Compiler<JavaCompileSpec> {
-    private static final Logger LOGGER = LoggerFactory.getLogger(AntJavaCompiler)
-    private static final String CLASSPATH_ID = 'compile.classpath'
-
-    private final Factory<AntBuilder> antBuilderFactory
-
-    AntJavaCompiler(Factory<AntBuilder> antBuilderFactory) {
-        this.antBuilderFactory = antBuilderFactory
-    }
-
-    WorkResult execute(JavaCompileSpec spec) {
-        def ant = antBuilderFactory.create()
-
-        createAntClassPath(ant, spec.classpath)
-        Map otherArgs = [
-                includeAntRuntime: false,
-                destdir: spec.destinationDir,
-                classpathref: CLASSPATH_ID,
-                sourcepath: '',
-                target: spec.targetCompatibility,
-                source: spec.sourceCompatibility
-        ]
-        if (spec.compileOptions.fork && !spec.compileOptions.forkOptions.executable) {
-            spec.compileOptions.forkOptions.executable = Jvm.current().javacExecutable
-        }
-
-        Map options = otherArgs + spec.compileOptions.optionMap()
-        LOGGER.info("Compiling with Ant javac task.")
-        LOGGER.debug("Ant javac task options: {}", options)
-        def task = ant.javac(options) {
-            spec.source.addToAntBuilder(ant, 'src', FileCollection.AntType.MatchingTask)
-            spec.compileOptions.compilerArgs.each {value ->
-                compilerarg(value: value)
-            }
-        }
-
-        int numFilesCompiled = task.fileList.length;
-        return { numFilesCompiled > 0 } as WorkResult
-    }
-
-    private void createAntClassPath(AntBuilder ant, Iterable classpath) {
-        ant.path(id: CLASSPATH_ID) {
-            classpath.each {
-                LOGGER.debug("Add {} to Ant classpath!", it)
-                pathelement(location: it)
-            }
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
deleted file mode 100644
index 2ee42fd..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
+++ /dev/null
@@ -1,140 +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.tasks.compile;
-
-import com.google.common.collect.Iterables;
-import groovy.lang.GroovyClassLoader;
-import org.codehaus.groovy.control.CompilationUnit;
-import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.control.messages.SimpleMessage;
-import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
-import org.codehaus.groovy.tools.javac.JavaCompiler;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.internal.classloader.FilteringClassLoader;
-
-import java.io.File;
-import java.io.Serializable;
-import java.net.URLClassLoader;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ApiGroovyCompiler implements Compiler<GroovyJavaJointCompileSpec>, Serializable {
-    private final Compiler<JavaCompileSpec> javaCompiler;
-
-    public ApiGroovyCompiler(Compiler<JavaCompileSpec> javaCompiler) {
-        this.javaCompiler = javaCompiler;
-    }
-
-    public WorkResult execute(final GroovyJavaJointCompileSpec spec) {
-        CompilerConfiguration configuration = new CompilerConfiguration();
-        configuration.setVerbose(spec.getGroovyCompileOptions().isVerbose());
-        configuration.setSourceEncoding(spec.getGroovyCompileOptions().getEncoding());
-        configuration.setTargetBytecode(spec.getTargetCompatibility());
-        configuration.setTargetDirectory(spec.getDestinationDir());
-        canonicalizeValues(spec.getGroovyCompileOptions().getOptimizationOptions());
-        try {
-            configuration.setOptimizationOptions(spec.getGroovyCompileOptions().getOptimizationOptions());
-        } catch (NoSuchMethodError ignored) { /* method was only introduced in Groovy 1.8 */ }
-        Map<String, Object> jointCompilationOptions = new HashMap<String, Object>();
-        jointCompilationOptions.put("stubDir", spec.getGroovyCompileOptions().getStubDir());
-        jointCompilationOptions.put("keepStubs", spec.getGroovyCompileOptions().isKeepStubs());
-        configuration.setJointCompilationOptions(jointCompilationOptions);
-
-        // Necessary for Groovy compilation to pick up output of regular and joint Java compilation,
-        // and for joint Java compilation to pick up the output of regular Java compilation.
-        // Assumes that output of regular Java compilation (which is not under this task's control) also goes
-        // into spec.getDestinationDir(). We could configure this on source set level, but then spec.getDestinationDir()
-        // would end up on the compile class path of every compile task for that source set, which may not be desirable.
-        spec.setClasspath(Iterables.concat(spec.getClasspath(), Collections.singleton(spec.getDestinationDir())));
-
-        URLClassLoader classPathLoader = new GroovyCompileTransformingClassLoader(new DefaultClassPath(spec.getClasspath()));
-        GroovyClassLoader compileClasspathClassLoader = new GroovyClassLoader(classPathLoader, null);
-
-        FilteringClassLoader groovyCompilerClassLoader = new FilteringClassLoader(GroovyClassLoader.class.getClassLoader());
-        groovyCompilerClassLoader.allowPackage("org.codehaus.groovy");
-        groovyCompilerClassLoader.allowPackage("groovy");
-        // Disallow classes from Groovy Jar that reference external classes. Such classes must be loaded from astTransformClassLoader,
-        // or a NoClassDefFoundError will occur. Essentially this is drawing a line between the Groovy compiler and the Groovy
-        // library, albeit only for selected classes that run a high risk of being statically referenced from a transform.
-        groovyCompilerClassLoader.disallowClass("groovy.util.GroovyTestCase");
-        groovyCompilerClassLoader.disallowClass("groovy.servlet.GroovyServlet");
-
-        // AST transforms need their own class loader that shares compiler classes with the compiler itself
-        final GroovyClassLoader astTransformClassLoader = new GroovyClassLoader(groovyCompilerClassLoader, null);
-        // can't delegate to compileClasspathLoader because this would result in ASTTransformation interface
-        // (which is implemented by the transform class) being loaded by compileClasspathClassLoader (which is
-        // where the transform class is loaded from)
-        for (File file : spec.getClasspath()) {
-            astTransformClassLoader.addClasspath(file.getPath());
-        }
-
-        JavaAwareCompilationUnit unit = new JavaAwareCompilationUnit(configuration, compileClasspathClassLoader) {
-            @Override
-            public GroovyClassLoader getTransformLoader() {
-                return astTransformClassLoader;
-            }
-        };
-        unit.addSources(Iterables.toArray(spec.getSource(), File.class));
-        unit.setCompilerFactory(new org.codehaus.groovy.tools.javac.JavaCompilerFactory() {
-            public JavaCompiler createCompiler(final CompilerConfiguration config) {
-                return new JavaCompiler() {
-                    public void compile(List<String> files, CompilationUnit cu) {
-                        spec.setSource(spec.getSource().filter(new Spec<File>() {
-                            public boolean isSatisfiedBy(File file) {
-                                return file.getName().endsWith(".java");
-                            }
-                        }));
-                        spec.getCompileOptions().getCompilerArgs().add("-sourcepath");
-                        spec.getCompileOptions().getCompilerArgs().add(((File) config.getJointCompilationOptions().get("stubDir")).getAbsolutePath());
-                        try {
-                            javaCompiler.execute(spec);
-                        } catch (CompilationFailedException e) {
-                            cu.getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(), cu));
-                        }
-                    }
-                };
-            }
-        });
-
-        try {
-            unit.compile();
-        } catch (org.codehaus.groovy.control.CompilationFailedException e) {
-            System.err.println(e.getMessage());
-            throw new CompilationFailedException();
-        }
-
-        return new SimpleWorkResult(true);
-    }
-
-    // Make sure that map only contains Boolean.TRUE and Boolean.FALSE values and no other Boolean instances.
-    // This is necessary because:
-    // 1. serialization/deserialization of the compile spec doesn't preserve Boolean.TRUE/Boolean.FALSE but creates new instances
-    // 1. org.codehaus.groovy.classgen.asm.WriterController makes identity comparisons
-    private void canonicalizeValues(Map<String, Boolean> options) {
-        for (String key : options.keySet()) {
-            // unboxing and boxing does the trick
-            boolean value = options.get(key);
-            options.put(key, value);
-        }
-    }
-
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgCollector.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgCollector.java
deleted file mode 100644
index a630b63..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgCollector.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.tasks.compile;
-
-public interface ArgCollector {
-    
-    ArgCollector args(Object... args);
-
-    ArgCollector args(Iterable<?> args);
-
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgWriter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgWriter.java
deleted file mode 100755
index 6d901cf..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ArgWriter.java
+++ /dev/null
@@ -1,89 +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.tasks.compile;
-
-import org.gradle.api.Transformer;
-
-import java.io.PrintWriter;
-import java.util.regex.Pattern;
-
-public class ArgWriter implements ArgCollector {
-    private static final Pattern WHITESPACE = Pattern.compile("\\s");
-    private final PrintWriter writer;
-    private final boolean backslashEscape;
-
-    private ArgWriter(PrintWriter writer, boolean backslashEscape) {
-        this.writer = writer;
-        this.backslashEscape = backslashEscape;
-    }
-
-    public static ArgWriter unixStyle(PrintWriter writer) {
-        return new ArgWriter(writer, true);
-    }
-
-    public static Transformer<ArgWriter, PrintWriter> unixStyleFactory() {
-        return new Transformer<ArgWriter, PrintWriter>() {
-            public ArgWriter transform(PrintWriter original) {
-                return unixStyle(original);
-            }
-        };
-    }
-
-    public static ArgWriter windowsStyle(PrintWriter writer) {
-        return new ArgWriter(writer, false);
-    }
-
-    public static Transformer<ArgWriter, PrintWriter> windowsStyleFactory() {
-        return new Transformer<ArgWriter, PrintWriter>() {
-            public ArgWriter transform(PrintWriter original) {
-                return windowsStyle(original);
-            }
-        };
-    }
-
-    /**
-     * Writes a set of args on a single line, escaping and quoting as required.
-     */
-    public ArgWriter args(Object... args) {
-        for (int i = 0; i < args.length; i++) {
-            Object arg = args[i];
-            if (i > 0) {
-                writer.print(' ');
-            }
-            String str = arg.toString();
-            if (backslashEscape) {
-                str = str.replace("\\", "\\\\").replace("\"", "\\\"");
-            }
-            if (WHITESPACE.matcher(str).find()) {
-                writer.print('\"');
-                writer.print(str);
-                writer.print('\"');
-            } else {
-                writer.print(str);
-            }
-        }
-        writer.println();
-        return this;
-    }
-
-    public ArgCollector args(Iterable<?> args) {
-        for (Object arg : args) {
-            args(arg);
-        }
-        return this;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java
deleted file mode 100644
index dcc284e..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningGroovyCompiler.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile;
-
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner;
-import org.gradle.language.jvm.internal.StaleClassCleaner;
-
-public class CleaningGroovyCompiler extends CleaningJavaCompilerSupport<GroovyJavaJointCompileSpec> {
-    private final Compiler<GroovyJavaJointCompileSpec> compiler;
-    private final TaskOutputsInternal taskOutputs;
-
-    public CleaningGroovyCompiler(Compiler<GroovyJavaJointCompileSpec> compiler, TaskOutputsInternal taskOutputs) {
-        this.compiler = compiler;
-        this.taskOutputs = taskOutputs;
-    }
-
-    @Override
-    protected Compiler<GroovyJavaJointCompileSpec> getCompiler() {
-        return compiler;
-    }
-
-    @Override
-    protected StaleClassCleaner createCleaner(GroovyJavaJointCompileSpec spec) {
-        return new SimpleStaleClassCleaner(taskOutputs);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java
deleted file mode 100644
index 802cf7b..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompiler.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.internal.Factory;
-import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner;
-import org.gradle.language.jvm.internal.StaleClassCleaner;
-
-public class CleaningJavaCompiler extends CleaningJavaCompilerSupport<JavaCompileSpec> implements Compiler<JavaCompileSpec> {
-    private final Compiler<JavaCompileSpec> compiler;
-    private final Factory<AntBuilder> antBuilderFactory;
-    private final TaskOutputsInternal taskOutputs;
-
-    public CleaningJavaCompiler(Compiler<JavaCompileSpec> compiler, Factory<AntBuilder> antBuilderFactory,
-                                TaskOutputsInternal taskOutputs) {
-        this.compiler = compiler;
-        this.antBuilderFactory = antBuilderFactory;
-        this.taskOutputs = taskOutputs;
-    }
-
-    @Override
-    protected Compiler<JavaCompileSpec> getCompiler() {
-        return compiler;
-    }
-
-    protected StaleClassCleaner createCleaner(JavaCompileSpec spec) {
-        if (spec.getCompileOptions().isUseDepend()) {
-            AntDependsStaleClassCleaner cleaner = new AntDependsStaleClassCleaner(antBuilderFactory, spec.getCompileOptions());
-            cleaner.setDependencyCacheDir(spec.getDependencyCacheDir());
-            return cleaner;
-        } else {
-            return new SimpleStaleClassCleaner(taskOutputs);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java
deleted file mode 100644
index 042a374..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerSupport.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile;
-
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.language.jvm.internal.StaleClassCleaner;
-
-/**
- * Deletes stale classes before invoking the actual compiler
- */
-public abstract class CleaningJavaCompilerSupport<T extends JavaCompileSpec> implements Compiler<T> {
-    public WorkResult execute(T spec) {
-        StaleClassCleaner cleaner = createCleaner(spec);
-
-        cleaner.setDestinationDir(spec.getDestinationDir());
-        cleaner.setSource(spec.getSource());
-        cleaner.execute();
-
-        Compiler<? super T> compiler = getCompiler();
-        return compiler.execute(spec);
-    }
-
-    protected abstract Compiler<T> getCompiler();
-
-    protected abstract StaleClassCleaner createCleaner(T spec);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.java
deleted file mode 100644
index a974bbb..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompiler.java
+++ /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.tasks.compile;
-
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.tasks.WorkResult;
-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.File;
-import java.io.Serializable;
-
-/**
- * Executes the Java command line compiler specified in {@code JavaCompileSpec.forkOptions.getExecutable()}.
- */
-public class CommandLineJavaCompiler implements Compiler<JavaCompileSpec>, Serializable {
-    private static final Logger LOGGER = LoggerFactory.getLogger(CommandLineJavaCompiler.class);
-
-    private final CompileSpecToArguments<JavaCompileSpec> argumentsGenerator;
-    private final File workingDir;
-
-    public CommandLineJavaCompiler(TemporaryFileProvider tempFileProvider, File workingDir) {
-        argumentsGenerator = new CommandLineJavaCompilerArgumentsGenerator(tempFileProvider);
-        this.workingDir = workingDir;
-    }
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        String executable = spec.getCompileOptions().getForkOptions().getExecutable();
-        LOGGER.info("Compiling with Java command line compiler '{}'.", executable);
-
-        ExecHandle handle = createCompilerHandle(executable, spec);
-        executeCompiler(handle);
-
-        return new SimpleWorkResult(true);
-    }
-
-    private ExecHandle createCompilerHandle(String executable, JavaCompileSpec spec) {
-        ExecHandleBuilder builder = new ExecHandleBuilder();
-        builder.setWorkingDir(workingDir);
-        builder.setExecutable(executable);
-        argumentsGenerator.collectArguments(spec, new ExecSpecBackedArgCollector(builder));
-        builder.setIgnoreExitValue(true);
-        return builder.build();
-    }
-
-    private void executeCompiler(ExecHandle handle) {
-        handle.start();
-        ExecResult result = handle.waitForFinish();
-        if (result.getExitValue() != 0) {
-            throw new CompilationFailedException(result.getExitValue());
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.java
deleted file mode 100644
index 6b554fb..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGenerator.java
+++ /dev/null
@@ -1,81 +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.tasks.compile;
-
-import com.google.common.collect.Iterables;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-
-import java.io.*;
-import java.util.Collections;
-import java.util.List;
-
-public class CommandLineJavaCompilerArgumentsGenerator implements CompileSpecToArguments<JavaCompileSpec>, Serializable {
-    private final TemporaryFileProvider tempFileProvider;
-
-    public CommandLineJavaCompilerArgumentsGenerator(TemporaryFileProvider tempFileProvider) {
-        this.tempFileProvider = tempFileProvider;
-    }
-
-    public void collectArguments(JavaCompileSpec spec, ArgCollector collector) {
-        for (String arg : generate(spec)) {
-            collector.args(arg);
-        }
-    }
-
-    public Iterable<String> generate(JavaCompileSpec spec) {
-        List<String> launcherOptions = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeMainOptions(false).includeClasspath(false).build();
-        List<String> remainingArgs = new JavaCompilerArgumentsBuilder(spec).includeSourceFiles(true).build();
-        Iterable<String> allArgs = Iterables.concat(launcherOptions, remainingArgs);
-        if (exceedsWindowsCommandLineLengthLimit(allArgs)) {
-            return Iterables.concat(launcherOptions, shortenArgs(remainingArgs));
-        }
-        return allArgs;
-    }
-
-    private boolean exceedsWindowsCommandLineLengthLimit(Iterable<String> args) {
-        int length = 0;
-        for (String arg : args) {
-            length += arg.length() + 1;
-            // limit is 2047 on older Windows systems, and 8191 on newer ones
-            // http://support.microsoft.com/kb/830473
-            // let's play it safe, no need to optimize
-            if (length > 1500) { return true; }
-        }
-        return false;
-    }
-
-    private Iterable<String> shortenArgs(List<String> args) {
-        File file = tempFileProvider.createTemporaryFile("compile-args", null, "java-compiler");
-        // for command file format, see http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html#commandlineargfile
-        // use platform character and line encoding
-        try {
-            PrintWriter writer = new PrintWriter(new FileWriter(file));
-            try {
-                ArgWriter argWriter = ArgWriter.unixStyle(writer);
-                for (String arg : args) {
-                    argWriter.args(arg);
-                }
-            } finally {
-                writer.close();
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        return Collections.singleton("@" + file.getPath());
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpec.java
deleted file mode 100644
index ca2c159..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpec.java
+++ /dev/null
@@ -1,20 +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.tasks.compile;
-
-public interface CompileSpec {
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.java
deleted file mode 100644
index 1afd750..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/CompileSpecToArguments.java
+++ /dev/null
@@ -1,21 +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.tasks.compile;
-
-public interface CompileSpecToArguments<T> {
-    public void collectArguments(T spec, ArgCollector collector);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/Compiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/Compiler.java
deleted file mode 100644
index 2b9cb68..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/Compiler.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile;
-
-import org.gradle.api.tasks.WorkResult;
-
-public interface Compiler<T extends CompileSpec> {
-    WorkResult execute(T spec);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.java
deleted file mode 100644
index b0eefd8..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompileSpec.java
+++ /dev/null
@@ -1,51 +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.tasks.compile;
-
-import org.gradle.api.tasks.compile.CompileOptions;
-
-import java.io.File;
-
-public class DefaultJavaCompileSpec extends DefaultJvmLanguageCompileSpec implements JavaCompileSpec {
-    private String sourceCompatibility;
-    private File dependencyCacheDir;
-    private CompileOptions compileOptions;
-
-    public CompileOptions getCompileOptions() {
-        return compileOptions;
-    }
-
-    public void setCompileOptions(CompileOptions compileOptions) {
-        this.compileOptions = compileOptions;
-    }
-
-    public File getDependencyCacheDir() {
-        return dependencyCacheDir;
-    }
-
-    public void setDependencyCacheDir(File dependencyCacheDir) {
-        this.dependencyCacheDir = dependencyCacheDir;
-    }
-
-    public String getSourceCompatibility() {
-        return sourceCompatibility;
-    }
-
-    public void setSourceCompatibility(String sourceCompatibility) {
-        this.sourceCompatibility = sourceCompatibility;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java
deleted file mode 100644
index a4fb7f2..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactory.java
+++ /dev/null
@@ -1,108 +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.tasks.compile;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.api.internal.file.DefaultTemporaryFileProvider;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.compile.daemon.DaemonJavaCompiler;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.internal.Factory;
-
-import java.io.File;
-import java.io.Serializable;
-
-public class DefaultJavaCompilerFactory implements JavaCompilerFactory {
-    private final static Logger LOGGER = Logging.getLogger(DefaultJavaCompilerFactory.class);
-
-    private final ProjectInternal project;
-    private final Factory<AntBuilder> antBuilderFactory;
-    private final JavaCompilerFactory inProcessCompilerFactory;
-    private final CompilerDaemonManager compilerDaemonManager;
-    private boolean jointCompilation;
-
-    public DefaultJavaCompilerFactory(ProjectInternal project, Factory<AntBuilder> antBuilderFactory, JavaCompilerFactory inProcessCompilerFactory, CompilerDaemonManager compilerDaemonManager){
-        this.project = project;
-        this.antBuilderFactory = antBuilderFactory;
-        this.inProcessCompilerFactory = inProcessCompilerFactory;
-        this.compilerDaemonManager = compilerDaemonManager;
-    }
-
-    /**
-     * If true, the Java compiler to be created is used for joint compilation
-     * together with another language's compiler in the compiler daemon.
-     * In that case, the other language's normalizing and daemon compilers should be used.
-     */
-    public void setJointCompilation(boolean flag) {
-        jointCompilation = flag;
-    }
-
-    public Compiler<JavaCompileSpec> create(CompileOptions options) {
-        fallBackToAntIfNecessary(options);
-
-        if (options.isUseAnt()) {
-            return new AntJavaCompiler(antBuilderFactory);
-        }
-
-        Compiler<JavaCompileSpec> result = createTargetCompiler(options);
-        if (!jointCompilation) {
-            result = new NormalizingJavaCompiler(result);
-        }
-        return result;
-    }
-
-    private void fallBackToAntIfNecessary(CompileOptions options) {
-        if (options.isUseAnt()) { return; }
-
-        if (options.getCompiler() != null) {
-            LOGGER.warn("Falling back to Ant javac task ('CompileOptions.useAnt = true') because 'CompileOptions.compiler' is set.");
-            options.setUseAnt(true);
-        }
-    }
-
-    private Compiler<JavaCompileSpec> createTargetCompiler(CompileOptions options) {
-        if (options.isFork() && options.getForkOptions().getExecutable() != null) {
-            return new CommandLineJavaCompiler(createSerializableTempFileProvider(), project.getProjectDir());
-        }
-
-        Compiler<JavaCompileSpec> compiler = inProcessCompilerFactory.create(options);
-        if (options.isFork() && !jointCompilation) {
-            return new DaemonJavaCompiler(project, compiler, compilerDaemonManager);
-        }
-
-        return compiler;
-    }
-
-    private TemporaryFileProvider createSerializableTempFileProvider() {
-        return new DefaultTemporaryFileProvider(new FileFactory(project.getBuildDir()));
-    }
-
-    private static class FileFactory implements Factory<File>, Serializable {
-        private final File file;
-
-        private FileFactory(File file) {
-            this.file = file;
-        }
-
-        public File create() {
-            return file;
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java
deleted file mode 100644
index ee3d683..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DefaultJvmLanguageCompileSpec.java
+++ /dev/null
@@ -1,61 +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.tasks.compile;
-
-import org.gradle.api.file.FileCollection;
-
-import java.io.File;
-import java.io.Serializable;
-
-public class DefaultJvmLanguageCompileSpec implements JvmLanguageCompileSpec, Serializable {
-    private Iterable<File> classpath;
-    private File destinationDir;
-    private FileCollection source;
-    private String targetCompatibility;
-
-    public File getDestinationDir() {
-        return destinationDir;
-    }
-
-    public void setDestinationDir(File destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
-    public FileCollection getSource() {
-        return source;
-    }
-
-    public void setSource(FileCollection source) {
-        this.source = source;
-    }
-
-    public Iterable<File> getClasspath() {
-        return classpath;
-    }
-
-    public void setClasspath(Iterable<File> classpath) {
-        this.classpath = classpath;
-    }
-
-    public String getTargetCompatibility() {
-        return targetCompatibility;
-    }
-
-    public void setTargetCompatibility(String targetCompatibility) {
-        this.targetCompatibility = targetCompatibility;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingGroovyCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingGroovyCompiler.java
deleted file mode 100644
index d7c9874..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingGroovyCompiler.java
+++ /dev/null
@@ -1,31 +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.tasks.compile;
-
-import org.gradle.api.tasks.WorkResult;
-
-public class DelegatingGroovyCompiler implements Compiler<GroovyJavaJointCompileSpec> {
-    private final GroovyCompilerFactory compilerFactory;
-
-    public DelegatingGroovyCompiler(GroovyCompilerFactory compilerFactory) {
-        this.compilerFactory = compilerFactory;
-    }
-
-    public WorkResult execute(GroovyJavaJointCompileSpec spec) {
-        Compiler<GroovyJavaJointCompileSpec> delegate = compilerFactory.create(spec.getGroovyCompileOptions(), spec.getCompileOptions());
-        return delegate.execute(spec);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompiler.java
deleted file mode 100644
index b414d72..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompiler.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.tasks.compile;
-
-import org.gradle.api.tasks.WorkResult;
-
-public class DelegatingJavaCompiler implements Compiler<JavaCompileSpec> {
-    private final JavaCompilerFactory compilerFactory;
-    
-    public DelegatingJavaCompiler(JavaCompilerFactory compilerFactory) {
-        this.compilerFactory = compilerFactory;
-    }
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        Compiler<JavaCompileSpec> delegate = compilerFactory.create(spec.getCompileOptions());
-        return delegate.execute(spec);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.java
deleted file mode 100644
index 961f9ea..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/ExecSpecBackedArgCollector.java
+++ /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.api.internal.tasks.compile;
-
-import org.gradle.process.ExecSpec;
-
-public class ExecSpecBackedArgCollector implements ArgCollector {
-    private final ExecSpec action;
-
-    public ExecSpecBackedArgCollector(ExecSpec action) {
-        this.action = action;
-    }
-
-    public ArgCollector args(Object... args) {
-        action.args(args);
-        return this;
-    }
-
-    public ArgCollector args(Iterable<?> args) {
-        action.args(args);
-        return this;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java
deleted file mode 100644
index 8f9a13b..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoader.java
+++ /dev/null
@@ -1,124 +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.tasks.compile;
-
-import org.codehaus.groovy.transform.GroovyASTTransformationClass;
-import org.gradle.internal.classloader.TransformingClassLoader;
-import org.gradle.internal.classpath.ClassPath;
-import org.objectweb.asm.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Transforms @GroovyASTTransformationClass(classes = {classLiterals}) into @GroovyASTTransformationClass([classNames]),
- * to work around GROOVY-5416.
- */
-class GroovyCompileTransformingClassLoader extends TransformingClassLoader {
-    private static final String ANNOTATION_DESCRIPTOR = Type.getType(GroovyASTTransformationClass.class).getDescriptor();
-
-    public GroovyCompileTransformingClassLoader(ClassPath classpath) {
-        super(null, classpath);
-    }
-
-    @Override
-    protected byte[] transform(byte[] bytes) {
-        // First scan for annotation, and short circuit transformation if not present
-        ClassReader classReader = new ClassReader(bytes);
-
-        AnnotationDetector detector = new AnnotationDetector();
-        classReader.accept(detector, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
-        if (!detector.found) {
-            return bytes;
-        }
-
-        ClassWriter classWriter = new ClassWriter(0);
-        classReader.accept(new TransformingAdapter(classWriter), 0);
-        bytes = classWriter.toByteArray();
-        return bytes;
-    }
-
-    private static class AnnotationDetector extends ClassVisitor {
-        private boolean found;
-
-        private AnnotationDetector() {
-            super(Opcodes.ASM4);
-        }
-
-        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-            if (desc.equals(ANNOTATION_DESCRIPTOR)) {
-                found = true;
-            }
-            return null;
-        }
-    }
-
-    private static class TransformingAdapter extends ClassVisitor {
-        public TransformingAdapter(ClassWriter classWriter) {
-            super(Opcodes.ASM4, classWriter);
-        }
-
-        @Override
-        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-            if (desc.equals(ANNOTATION_DESCRIPTOR)) {
-                return new AnnotationTransformingVisitor(super.visitAnnotation(desc, visible));
-            }
-            return super.visitAnnotation(desc, visible);
-        }
-
-        private static class AnnotationTransformingVisitor extends AnnotationVisitor {
-            private final List<String> names = new ArrayList<String>();
-
-            public AnnotationTransformingVisitor(AnnotationVisitor annotationVisitor) {
-                super(Opcodes.ASM4, annotationVisitor);
-            }
-
-            public AnnotationVisitor visitArray(String name) {
-                if (name.equals("classes")) {
-                    return new AnnotationVisitor(Opcodes.ASM4){
-                        @Override
-                        public void visit(String name, Object value) {
-                            Type type = (Type) value;
-                            names.add(type.getClassName());
-                        }
-                    };
-                } else if (name.equals("value")) {
-                    return new AnnotationVisitor(Opcodes.ASM4) {
-                        @Override
-                        public void visit(String name, Object value) {
-                            String type = (String) value;
-                            names.add(type);
-                        }
-                    };
-                } else {
-                    return super.visitArray(name);
-                }
-            }
-
-            public void visitEnd() {
-                if (!names.isEmpty()) {
-                    AnnotationVisitor visitor = super.visitArray("value");
-                    for (String name : names) {
-                        visitor.visit(null, name);
-                    }
-                    visitor.visitEnd();
-                }
-                super.visitEnd();
-            }
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java
deleted file mode 100644
index b4d5e5d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/GroovyCompilerFactory.java
+++ /dev/null
@@ -1,82 +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.tasks.compile;
-
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.project.IsolatedAntBuilder;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.compile.daemon.DaemonGroovyCompiler;
-import org.gradle.api.internal.tasks.compile.daemon.InProcessCompilerDaemonFactory;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.api.tasks.compile.GroovyCompileOptions;
-import org.gradle.internal.Factory;
-import org.gradle.util.DeprecationLogger;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class GroovyCompilerFactory {
-    private static final Logger LOGGER = LoggerFactory.getLogger(GroovyCompilerFactory.class);
-    private final ProjectInternal project;
-    private final IsolatedAntBuilder antBuilder;
-    private final ClassPathRegistry classPathRegistry;
-    private final DefaultJavaCompilerFactory javaCompilerFactory;
-    private final CompilerDaemonManager compilerDaemonManager;
-    private final InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory;
-
-    public GroovyCompilerFactory(ProjectInternal project, IsolatedAntBuilder antBuilder, ClassPathRegistry classPathRegistry, DefaultJavaCompilerFactory javaCompilerFactory,
-                                 CompilerDaemonManager compilerDaemonManager, InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory) {
-        this.project = project;
-        this.antBuilder = antBuilder;
-        this.classPathRegistry = classPathRegistry;
-        this.javaCompilerFactory = javaCompilerFactory;
-        this.compilerDaemonManager = compilerDaemonManager;
-        this.inProcessCompilerDaemonFactory = inProcessCompilerDaemonFactory;
-    }
-
-    Compiler<GroovyJavaJointCompileSpec> create(final GroovyCompileOptions groovyOptions, final CompileOptions javaOptions) {
-        return DeprecationLogger.whileDisabled(new Factory<Compiler<GroovyJavaJointCompileSpec>>() {
-            public Compiler<GroovyJavaJointCompileSpec> create() {
-                // Some sanity checking of options
-                if (groovyOptions.isUseAnt() && !javaOptions.isUseAnt()) {
-                    LOGGER.warn("When groovyOptions.useAnt is enabled, options.useAnt must also be enabled. Ignoring options.useAnt = false.");
-                    javaOptions.setUseAnt(true);
-                } else if (!groovyOptions.isUseAnt() && javaOptions.isUseAnt()) {
-                    LOGGER.warn("When groovyOptions.useAnt is disabled, options.useAnt must also be disabled. Ignoring options.useAnt = true.");
-                    javaOptions.setUseAnt(false);
-                }
-
-                if (groovyOptions.isUseAnt()) {
-                    return new AntGroovyCompiler(antBuilder, classPathRegistry);
-                }
-
-                javaCompilerFactory.setJointCompilation(true);
-                Compiler<JavaCompileSpec> javaCompiler = javaCompilerFactory.create(javaOptions);
-                Compiler<GroovyJavaJointCompileSpec> groovyCompiler = new ApiGroovyCompiler(javaCompiler);
-                CompilerDaemonFactory daemonFactory;
-                if (groovyOptions.isFork()) {
-                    daemonFactory = compilerDaemonManager;
-                } else {
-                    daemonFactory = inProcessCompilerDaemonFactory;
-                }
-                groovyCompiler = new DaemonGroovyCompiler(project, groovyCompiler, daemonFactory);
-                return new NormalizingGroovyCompiler(groovyCompiler);
-            }
-        });
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactory.java
deleted file mode 100644
index b91b6df..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactory.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.tasks.compile;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.JavaVersion;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.internal.reflect.JavaReflectionUtil;
-
-public class InProcessJavaCompilerFactory implements JavaCompilerFactory {
-    private static final boolean SUN_COMPILER_AVAILABLE = JavaReflectionUtil.isClassAvailable("com.sun.tools.javac.Main");
-
-    public Compiler<JavaCompileSpec> create(CompileOptions options) {
-        if (JavaVersion.current().isJava6Compatible()) {
-            return createJdk6Compiler();
-        }
-        if (SUN_COMPILER_AVAILABLE) {
-            return new SunCompilerFactory().create();
-        }
-        throw new RuntimeException("Cannot find a Java compiler API. Please let us know which JDK/platform you are using. To work around this problem, try 'compileJava.options.useAnt=true'.");
-    }
-
-    private Compiler<JavaCompileSpec> createJdk6Compiler() {
-        try {
-            // excluded when Gradle is compiled against JDK5, hence we can't reference it statically
-            Class<?> clazz = getClass().getClassLoader().loadClass("org.gradle.api.internal.tasks.compile.jdk6.Jdk6JavaCompiler");
-            return (Compiler<JavaCompileSpec>) clazz.newInstance();
-        } catch (Exception e) {
-            throw new GradleException("Internal error: couldn't load or instantiate class Jdk6JavaCompiler", e);
-        }
-    }
-
-    // nested class to enforce lazy class loading
-    private static class SunCompilerFactory {
-        Compiler<JavaCompileSpec> create() {
-            return new SunJavaCompiler();
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java
deleted file mode 100644
index 9f30458..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompileSpec.java
+++ /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.api.internal.tasks.compile;
-
-import org.gradle.api.tasks.compile.CompileOptions;
-
-import java.io.File;
-
-public interface JavaCompileSpec extends JvmLanguageCompileSpec {
-    CompileOptions getCompileOptions();
-
-    File getDependencyCacheDir();
-
-    void setDependencyCacheDir(File dependencyCacheDir);
-
-    String getSourceCompatibility();
-
-    void setSourceCompatibility(String sourceCompatibility);
-
-    String getTargetCompatibility();
-
-    void setTargetCompatibility(String targetCompatibility);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java
deleted file mode 100644
index 0c4048a..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilder.java
+++ /dev/null
@@ -1,168 +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.tasks.compile;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.JavaVersion;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.api.tasks.compile.ForkOptions;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class JavaCompilerArgumentsBuilder {
-    private final JavaCompileSpec spec;
-
-    private boolean includeLauncherOptions;
-    private boolean includeMainOptions = true;
-    private boolean includeClasspath = true;
-    private boolean includeSourceFiles;
-
-    private List<String> args;
-
-    public JavaCompilerArgumentsBuilder(JavaCompileSpec spec) {
-        this.spec = spec;
-    }
-
-    public JavaCompilerArgumentsBuilder includeLauncherOptions(boolean flag) {
-        includeLauncherOptions = flag;
-        return this;
-    }
-
-    public JavaCompilerArgumentsBuilder includeMainOptions(boolean flag) {
-        includeMainOptions = flag;
-        return this;
-    }
-
-    public JavaCompilerArgumentsBuilder includeClasspath(boolean flag) {
-        includeClasspath = flag;
-        return this;
-    }
-
-    public JavaCompilerArgumentsBuilder includeSourceFiles(boolean flag) {
-        includeSourceFiles = flag;
-        return this;
-    }
-
-    public List<String> build() {
-        args = new ArrayList<String>();
-
-        addLauncherOptions();
-        addMainOptions();
-        addClasspath();
-        addSourceFiles();
-
-        return args;
-    }
-
-    private void addLauncherOptions() {
-        if (!includeLauncherOptions) { return; }
-
-        ForkOptions forkOptions = spec.getCompileOptions().getForkOptions();
-        if (forkOptions.getMemoryInitialSize() != null) {
-            args.add("-J-Xms" + forkOptions.getMemoryInitialSize().trim());
-        }
-        if (forkOptions.getMemoryMaximumSize() != null) {
-            args.add("-J-Xmx" + forkOptions.getMemoryMaximumSize().trim());
-        }
-        if (forkOptions.getJvmArgs() != null) {
-            args.addAll(forkOptions.getJvmArgs());
-        }
-    }
-
-    private void addMainOptions() {
-        if (!includeMainOptions) { return; }
-
-        String sourceCompatibility = spec.getSourceCompatibility();
-        if (sourceCompatibility != null && !JavaVersion.current().equals(JavaVersion.toVersion(sourceCompatibility))) {
-            args.add("-source");
-            args.add(sourceCompatibility);
-        }
-        String targetCompatibility = spec.getTargetCompatibility();
-        if (targetCompatibility != null && !JavaVersion.current().equals(JavaVersion.toVersion(targetCompatibility))) {
-            args.add("-target");
-            args.add(targetCompatibility);
-        }
-        File destinationDir = spec.getDestinationDir();
-        if (destinationDir != null) {
-            args.add("-d");
-            args.add(destinationDir.getPath());
-        }
-        CompileOptions compileOptions = spec.getCompileOptions();
-        if (compileOptions.isVerbose()) {
-            args.add("-verbose");
-        }
-        if (compileOptions.isDeprecation()) {
-            args.add("-deprecation");
-        }
-        if (!compileOptions.isWarnings()) {
-            args.add("-nowarn");
-        }
-        if (compileOptions.isDebug()) {
-            if (compileOptions.getDebugOptions().getDebugLevel() != null) {
-                args.add("-g:" + compileOptions.getDebugOptions().getDebugLevel().trim());
-            } else {
-                args.add("-g");
-            }
-        } else {
-            args.add("-g:none");
-        }
-        if (compileOptions.getEncoding() != null) {
-            args.add("-encoding");
-            args.add(compileOptions.getEncoding());
-        }
-        if (compileOptions.getBootClasspath() != null) {
-            args.add("-bootclasspath");
-            args.add(compileOptions.getBootClasspath());
-        }
-        if (compileOptions.getExtensionDirs() != null) {
-            args.add("-extdirs");
-            args.add(compileOptions.getExtensionDirs());
-        }
-        if (compileOptions.getCompilerArgs() != null) {
-            args.addAll(compileOptions.getCompilerArgs());
-        }
-    }
-
-    private void addClasspath() {
-        if (!includeClasspath) { return; }
-
-        Iterable<File> classpath = spec.getClasspath();
-        if (classpath != null && classpath.iterator().hasNext()) {
-            args.add("-classpath");
-            args.add(toFileCollection(classpath).getAsPath());
-        }
-    }
-
-    private void addSourceFiles() {
-        if (!includeSourceFiles) { return; }
-
-        for (File file : spec.getSource()) {
-            args.add(file.getPath());
-        }
-    }
-
-    private FileCollection toFileCollection(Iterable<File> classpath) {
-        if (classpath instanceof FileCollection) {
-            return (FileCollection) classpath;
-        }
-        return new SimpleFileCollection(Lists.newArrayList(classpath));
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.java
deleted file mode 100644
index 8cfcfe6..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerFactory.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.api.internal.tasks.compile;
-
-import org.gradle.api.tasks.compile.CompileOptions;
-
-/**
- * Creates Java compilers based on the provided compile options.
- */
-public interface JavaCompilerFactory {
-    Compiler<JavaCompileSpec> create(CompileOptions options);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.java
deleted file mode 100644
index c329b2c..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/JvmLanguageCompileSpec.java
+++ /dev/null
@@ -1,39 +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.tasks.compile;
-
-import org.gradle.api.file.FileCollection;
-
-import java.io.File;
-
-public interface JvmLanguageCompileSpec extends CompileSpec {
-    File getDestinationDir();
-
-    void setDestinationDir(File destinationDir);
-
-    FileCollection getSource();
-
-    void setSource(FileCollection source);
-
-    Iterable<File> getClasspath();
-
-    void setClasspath(Iterable<File> classpath);
-
-    String getTargetCompatibility();
-
-    void setTargetCompatibility(String version);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java
deleted file mode 100644
index d20b0b1..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NoOpStaleClassCleaner.java
+++ /dev/null
@@ -1,26 +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.tasks.compile;
-
-import org.gradle.language.jvm.internal.StaleClassCleaner;
-
-public class NoOpStaleClassCleaner extends StaleClassCleaner {
-    @Override
-    public void execute() {
-        // do nothing
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java
deleted file mode 100644
index ab744b1..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompiler.java
+++ /dev/null
@@ -1,109 +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.tasks.compile;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.util.CollectionUtils;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A Groovy {@link org.gradle.api.internal.tasks.compile.Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
- */
-public class NormalizingGroovyCompiler implements Compiler<GroovyJavaJointCompileSpec> {
-    private static final Logger LOGGER = Logging.getLogger(NormalizingGroovyCompiler.class);
-    private final Compiler<GroovyJavaJointCompileSpec> delegate;
-
-    public NormalizingGroovyCompiler(Compiler<GroovyJavaJointCompileSpec> delegate) {
-        this.delegate = delegate;
-    }
-
-    public WorkResult execute(GroovyJavaJointCompileSpec spec) {
-        resolveAndFilterSourceFiles(spec);
-        resolveClasspath(spec);
-        resolveNonStringsInCompilerArgs(spec);
-        logSourceFiles(spec);
-        logCompilerArguments(spec);
-        return delegateAndHandleErrors(spec);
-    }
-
-    private void resolveAndFilterSourceFiles(final GroovyJavaJointCompileSpec spec) {
-        FileCollection filtered = spec.getSource().filter(new Spec<File>() {
-            public boolean isSatisfiedBy(File element) {
-                for (String fileExtension : spec.getGroovyCompileOptions().getFileExtensions()) {
-                    if (element.getName().endsWith("." + fileExtension)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        });
-
-        spec.setSource(new SimpleFileCollection(filtered.getFiles()));
-    }
-
-    private void resolveClasspath(GroovyJavaJointCompileSpec spec) {
-        spec.setClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getClasspath())));
-        spec.setGroovyClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getGroovyClasspath())));
-    }
-
-    private void resolveNonStringsInCompilerArgs(GroovyJavaJointCompileSpec spec) {
-        // in particular, this is about GStrings
-        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
-    }
-
-    private void logSourceFiles(GroovyJavaJointCompileSpec spec) {
-        if (!spec.getGroovyCompileOptions().isListFiles()) { return; }
-
-        StringBuilder builder = new StringBuilder();
-        builder.append("Source files to be compiled:");
-        for (File file : spec.getSource()) {
-            builder.append('\n');
-            builder.append(file);
-        }
-
-        LOGGER.quiet(builder.toString());
-    }
-
-    private void logCompilerArguments(GroovyJavaJointCompileSpec spec) {
-        if (!LOGGER.isDebugEnabled()) { return; }
-
-        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
-        String joinedArgs = Joiner.on(' ').join(compilerArgs);
-        LOGGER.debug("Java compiler arguments: {}", joinedArgs);
-    }
-
-    private WorkResult delegateAndHandleErrors(GroovyJavaJointCompileSpec spec) {
-        try {
-            return delegate.execute(spec);
-        } catch (CompilationFailedException e) {
-            if (spec.getCompileOptions().isFailOnError()) {
-                throw e;
-            }
-            LOGGER.debug("Ignoring compilation failure.");
-            return new SimpleWorkResult(false);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java
deleted file mode 100644
index cfcc8f9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompiler.java
+++ /dev/null
@@ -1,105 +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.tasks.compile;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.specs.Spec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.util.CollectionUtils;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A Java {@link Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
- */
-public class NormalizingJavaCompiler implements Compiler<JavaCompileSpec> {
-    private static final Logger LOGGER = Logging.getLogger(NormalizingJavaCompiler.class);
-    private final Compiler<JavaCompileSpec> delegate;
-
-    public NormalizingJavaCompiler(Compiler<JavaCompileSpec> delegate) {
-        this.delegate = delegate;
-    }
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        resolveAndFilterSourceFiles(spec);
-        resolveClasspath(spec);
-        resolveNonStringsInCompilerArgs(spec);
-        logSourceFiles(spec);
-        logCompilerArguments(spec);
-        return delegateAndHandleErrors(spec);
-    }
-
-    private void resolveAndFilterSourceFiles(JavaCompileSpec spec) {
-        // this mimics the behavior of the Ant javac task (and therefore AntJavaCompiler),
-        // which silently excludes files not ending in .java
-        FileCollection javaOnly = spec.getSource().filter(new Spec<File>() {
-            public boolean isSatisfiedBy(File element) {
-                return element.getName().endsWith(".java");
-            }
-        });
-
-        spec.setSource(new SimpleFileCollection(javaOnly.getFiles()));
-    }
-
-    private void resolveClasspath(JavaCompileSpec spec) {
-        spec.setClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getClasspath())));
-    }
-
-    private void resolveNonStringsInCompilerArgs(JavaCompileSpec spec) {
-        // in particular, this is about GStrings
-        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
-    }
-
-    private void logSourceFiles(JavaCompileSpec spec) {
-        if (!spec.getCompileOptions().isListFiles()) { return; }
-
-        StringBuilder builder = new StringBuilder();
-        builder.append("Source files to be compiled:");
-        for (File file : spec.getSource()) {
-            builder.append('\n');
-            builder.append(file);
-        }
-
-        LOGGER.quiet(builder.toString());
-    }
-
-    private void logCompilerArguments(JavaCompileSpec spec) {
-        if (!LOGGER.isDebugEnabled()) { return; }
-
-        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
-        String joinedArgs = Joiner.on(' ').join(compilerArgs);
-        LOGGER.debug("Compiler arguments: {}", joinedArgs);
-    }
-
-    private WorkResult delegateAndHandleErrors(JavaCompileSpec spec) {
-        try {
-            return delegate.execute(spec);
-        } catch (CompilationFailedException e) {
-            if (spec.getCompileOptions().isFailOnError()) {
-                throw e;
-            }
-            LOGGER.debug("Ignoring compilation failure.");
-            return new SimpleWorkResult(false);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/SunJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/SunJavaCompiler.java
deleted file mode 100644
index 3c37109..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/SunJavaCompiler.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile;
-
-import com.sun.tools.javac.Main;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.tasks.WorkResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.Serializable;
-import java.util.List;
-
-public class SunJavaCompiler implements Compiler<JavaCompileSpec>, Serializable {
-    private static final Logger LOGGER = LoggerFactory.getLogger(SunJavaCompiler.class);
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        LOGGER.info("Compiling with Sun Java compiler API.");
-
-        String[] options = createCommandLineOptions(spec);
-        int exitCode = Main.compile(options);
-        if (exitCode != 0) {
-            throw new CompilationFailedException();
-        }
-
-        return new SimpleWorkResult(true);
-    }
-
-    private String[] createCommandLineOptions(JavaCompileSpec spec) {
-        List<String> options = new JavaCompilerArgumentsBuilder(spec).includeSourceFiles(true).build();
-        return options.toArray(new String[options.size()]);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java
deleted file mode 100644
index 8751d3f..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemon.java
+++ /dev/null
@@ -1,26 +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.tasks.compile.daemon;
-
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-import org.gradle.api.internal.tasks.compile.Compiler;
-
-/**
- * A service that executes compilers in a (potentially) long-lived process.
- */
-public interface CompilerDaemon {
-    <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.java
deleted file mode 100644
index 5e4f512..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonClient.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.api.internal.tasks.compile.daemon;
-
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.concurrent.Stoppable;
-import org.gradle.internal.UncheckedException;
-import org.gradle.process.internal.WorkerProcess;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.SynchronousQueue;
-
-class CompilerDaemonClient implements CompilerDaemon, CompilerDaemonClientProtocol, Stoppable {
-    private final DaemonForkOptions forkOptions;
-    private final WorkerProcess workerProcess;
-    private final CompilerDaemonServerProtocol server;
-    private final BlockingQueue<CompileResult> compileResults = new SynchronousQueue<CompileResult>();
-
-    public CompilerDaemonClient(DaemonForkOptions forkOptions, WorkerProcess workerProcess, CompilerDaemonServerProtocol server) {
-        this.forkOptions = forkOptions;
-        this.workerProcess = workerProcess;
-        this.server = server;
-    }
-
-    public <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec) {
-        // currently we just allow a single compilation thread at a time (per compiler daemon)
-        // one problem to solve when allowing multiple threads is how to deal with memory requirements specified by compile tasks
-        try {
-            server.execute(compiler, spec);
-            return compileResults.take();
-        } catch (InterruptedException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-
-    public boolean isCompatibleWith(DaemonForkOptions required) {
-        return forkOptions.isCompatibleWith(required);
-    }
-
-    public void stop() {
-        server.stop();
-        workerProcess.waitForStop();
-    }
-
-    public void executed(CompileResult result) {
-        try {
-            compileResults.put(result);
-        } catch (InterruptedException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java
deleted file mode 100644
index 80bb9e9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManager.java
+++ /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.tasks.compile.daemon;
-
-import net.jcip.annotations.ThreadSafe;
-import org.gradle.api.internal.tasks.compile.*;
-
-import java.io.File;
-
-/**
- * Controls the lifecycle of the compiler daemon and provides access to it.
- */
- at ThreadSafe
-public class CompilerDaemonManager implements CompilerDaemonFactory {
-
-    private CompilerClientsManager clientsManager;
-
-    public CompilerDaemonManager(CompilerClientsManager clientsManager) {
-        this.clientsManager = clientsManager;
-    }
-
-    public CompilerDaemon getDaemon(final File workingDir, final DaemonForkOptions forkOptions) {
-        return new CompilerDaemon() {
-            public <T extends CompileSpec> CompileResult execute(org.gradle.api.internal.tasks.compile.Compiler<T> compiler, T spec) {
-                CompilerDaemonClient client = clientsManager.reserveIdleClient(forkOptions);
-                if (client == null) {
-                    client = clientsManager.reserveNewClient(workingDir, forkOptions);
-                }
-                try {
-                    return client.execute(compiler, spec);
-                } finally {
-                    clientsManager.release(client);
-                }
-            }
-        };
-    }
-
-    public void stop() {
-        clientsManager.stop();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.java
deleted file mode 100644
index 4b4e19d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServer.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.tasks.compile.daemon;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.UncheckedException;
-import org.gradle.process.internal.WorkerProcessContext;
-
-import java.io.Serializable;
-import java.util.concurrent.CountDownLatch;
-
-
-public class CompilerDaemonServer implements Action<WorkerProcessContext>, CompilerDaemonServerProtocol, Serializable {
-    private static final Logger LOGGER = Logging.getLogger(CompilerDaemonServer.class);
-    
-    private volatile CompilerDaemonClientProtocol client;
-    private volatile CountDownLatch stop;
-    
-    public void execute(WorkerProcessContext context) {
-        stop = new CountDownLatch(1);
-        client = context.getServerConnection().addOutgoing(CompilerDaemonClientProtocol.class);
-        context.getServerConnection().addIncoming(CompilerDaemonServerProtocol.class, this);
-        context.getServerConnection().connect();
-        try {
-            stop.await();
-        } catch (InterruptedException e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-
-    }
-
-    public <T extends CompileSpec> void execute(Compiler<T> compiler, T spec) {
-        try {
-            LOGGER.info("Executing {} in compiler daemon.", compiler);
-            WorkResult result = compiler.execute(spec);
-            LOGGER.info("Successfully executed {} in compiler daemon.", compiler);
-            client.executed(new CompileResult(result.getDidWork(), null));
-        } catch (Throwable t) {
-            LOGGER.info("Exception executing {} in compiler daemon: {}.", compiler, t);
-            client.executed(new CompileResult(true, t));
-        }
-    }
-
-    public void stop() {
-        stop.countDown();
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.java
deleted file mode 100644
index 3e85df3..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonServerProtocol.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.api.internal.tasks.compile.daemon;
-
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.concurrent.Stoppable;
-
-/**
- * Server part of the compiler daemon protocol. Used to submit compilation jobs.
- */
-public interface CompilerDaemonServerProtocol extends Stoppable {
-    <T extends CompileSpec> void execute(Compiler<T> compiler, T spec);
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.java
deleted file mode 100644
index 5b14f7d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonStarter.java
+++ /dev/null
@@ -1,68 +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.tasks.compile.daemon;
-
-import org.gradle.StartParameter;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.internal.Factory;
-import org.gradle.internal.jvm.Jvm;
-import org.gradle.process.internal.JavaExecHandleBuilder;
-import org.gradle.process.internal.WorkerProcess;
-import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.util.Clock;
-
-import java.io.File;
-
-public class CompilerDaemonStarter {
-    private final static Logger LOG = Logging.getLogger(CompilerDaemonStarter.class);
-    private final Factory<WorkerProcessBuilder> workerFactory;
-    private final StartParameter startParameter;
-
-    public CompilerDaemonStarter(Factory<WorkerProcessBuilder> workerFactory, StartParameter startParameter) {
-        this.workerFactory = workerFactory;
-        this.startParameter = startParameter;
-    }
-
-    public CompilerDaemonClient startDaemon(File workingDir, DaemonForkOptions forkOptions) {
-        LOG.debug("Starting Gradle compiler daemon with fork options {}.", forkOptions);
-        Clock clock = new Clock();
-        WorkerProcessBuilder builder = workerFactory.create();
-        builder.setLogLevel(startParameter.getLogLevel()); // NOTE: might make sense to respect per-compile-task log level
-        builder.applicationClasspath(forkOptions.getClasspath());
-        builder.sharedPackages(forkOptions.getSharedPackages());
-        File toolsJar = Jvm.current().getToolsJar();
-        if (toolsJar != null) {
-            builder.getApplicationClasspath().add(toolsJar); // for SunJavaCompiler
-        }
-        JavaExecHandleBuilder javaCommand = builder.getJavaCommand();
-        javaCommand.setMinHeapSize(forkOptions.getMinHeapSize());
-        javaCommand.setMaxHeapSize(forkOptions.getMaxHeapSize());
-        javaCommand.setJvmArgs(forkOptions.getJvmArgs());
-        javaCommand.setWorkingDir(workingDir);
-        WorkerProcess process = builder.worker(new CompilerDaemonServer()).setBaseName("Gradle Compiler Daemon").build();
-        process.start();
-
-        CompilerDaemonServerProtocol server = process.getConnection().addOutgoing(CompilerDaemonServerProtocol.class);
-        CompilerDaemonClient client = new CompilerDaemonClient(forkOptions, process, server);
-        process.getConnection().addIncoming(CompilerDaemonClientProtocol.class, client);
-        process.getConnection().connect();
-
-        LOG.info("Started Gradle compiler daemon ({}) with fork options {}.", clock.getTime(), forkOptions);
-
-        return client;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.java
deleted file mode 100644
index 0a08231..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonGroovyCompiler.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.api.internal.tasks.compile.daemon;
-
-import com.google.common.collect.Iterables;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.GroovyJavaJointCompileSpec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.compile.ForkOptions;
-import org.gradle.api.tasks.compile.GroovyForkOptions;
-import org.gradle.internal.UncheckedException;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-public class DaemonGroovyCompiler implements Compiler<GroovyJavaJointCompileSpec> {
-    private final ProjectInternal project;
-    private final Compiler<GroovyJavaJointCompileSpec> delegate;
-    private final CompilerDaemonFactory daemonFactory;
-
-    public DaemonGroovyCompiler(ProjectInternal project, Compiler<GroovyJavaJointCompileSpec> delegate, CompilerDaemonFactory daemonFactory) {
-        this.project = project;
-        this.delegate = delegate;
-        this.daemonFactory = daemonFactory;
-    }
-
-    public WorkResult execute(GroovyJavaJointCompileSpec spec) {
-        DaemonForkOptions daemonForkOptions = createDaemonForkOptions(spec);
-        CompilerDaemon daemon = daemonFactory.getDaemon(project.getRootProject().getProjectDir(), daemonForkOptions);
-        CompileResult result = daemon.execute(delegate, spec);
-        if (result.isSuccess()) {
-            return result;
-        }
-        throw UncheckedException.throwAsUncheckedException(result.getException());
-    }
-
-    private DaemonForkOptions createDaemonForkOptions(GroovyJavaJointCompileSpec spec) {
-        return createJavaForkOptions(spec).mergeWith(createGroovyForkOptions(spec));
-    }
-    
-    private DaemonForkOptions createJavaForkOptions(GroovyJavaJointCompileSpec spec) {
-        ForkOptions options = spec.getCompileOptions().getForkOptions();
-        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(), options.getJvmArgs());
-    }
-
-    private DaemonForkOptions createGroovyForkOptions(GroovyJavaJointCompileSpec spec) {
-        GroovyForkOptions options = spec.getGroovyCompileOptions().getForkOptions();
-        // Ant is optional dependency of groovy(-all) module but mandatory dependency of Groovy compiler;
-        // that's why we add it here. The following assumes that any Groovy compiler version supported by Gradle
-        // is compatible with Gradle's current Ant version.
-        Collection<File> antFiles = project.getServices().get(ClassPathRegistry.class).getClassPath("ANT").getAsFiles();
-        Iterable<File> groovyFiles = Iterables.concat(spec.getGroovyClasspath(), antFiles);
-        List<String> groovyPackages = Arrays.asList("groovy", "org.codehaus.groovy", "groovyjarjarantlr", "groovyjarjarasm", "groovyjarjarcommonscli", "org.apache.tools.ant", "com.sun.tools.javac");
-        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(),
-                options.getJvmArgs(), groovyFiles, groovyPackages);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonJavaCompiler.java
deleted file mode 100644
index f2daf8f..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/DaemonJavaCompiler.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.api.internal.tasks.compile.daemon;
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.compile.ForkOptions;
-import org.gradle.internal.UncheckedException;
-
-import java.io.File;
-import java.util.Collections;
-
-public class DaemonJavaCompiler implements Compiler<JavaCompileSpec> {
-    private final ProjectInternal project;
-    private final Compiler<JavaCompileSpec> delegate;
-    private final CompilerDaemonManager compilerDaemonManager;
-
-    public DaemonJavaCompiler(ProjectInternal project, Compiler<JavaCompileSpec> delegate, CompilerDaemonManager compilerDaemonManager) {
-        this.project = project;
-        this.delegate = delegate;
-        this.compilerDaemonManager = compilerDaemonManager;
-    }
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        ForkOptions forkOptions = spec.getCompileOptions().getForkOptions();
-        DaemonForkOptions daemonForkOptions = new DaemonForkOptions(
-                forkOptions.getMemoryInitialSize(), forkOptions.getMemoryMaximumSize(), forkOptions.getJvmArgs(),
-                Collections.<File>emptyList(), Collections.singleton("com.sun.tools.javac"));
-        CompilerDaemon daemon = compilerDaemonManager.getDaemon(project.getRootProject().getProjectDir(), daemonForkOptions);
-        CompileResult result = daemon.execute(delegate, spec);
-        if (result.isSuccess()) {
-            return result;
-        }
-        throw UncheckedException.throwAsUncheckedException(result.getException());
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java
deleted file mode 100644
index 6cbd8e8..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/daemon/InProcessCompilerDaemonFactory.java
+++ /dev/null
@@ -1,79 +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.tasks.compile.daemon;
-
-import org.gradle.api.internal.tasks.compile.CompileSpec;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.internal.UncheckedException;
-import org.gradle.internal.classloader.*;
-import org.gradle.internal.classpath.DefaultClassPath;
-import org.gradle.internal.io.ClassLoaderObjectInputStream;
-import org.gradle.util.GUtil;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.Serializable;
-import java.util.concurrent.Callable;
-
-public class InProcessCompilerDaemonFactory implements CompilerDaemonFactory {
-    private static final InProcessCompilerDaemonFactory INSTANCE = new InProcessCompilerDaemonFactory();
-    private final ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
-
-    public static InProcessCompilerDaemonFactory getInstance() {
-        return INSTANCE;
-    }
-
-    public CompilerDaemon getDaemon(File workingDir, final DaemonForkOptions forkOptions) {
-        return new CompilerDaemon() {
-            public <T extends CompileSpec> CompileResult execute(Compiler<T> compiler, T spec) {
-                ClassLoader groovyClassLoader = classLoaderFactory.createIsolatedClassLoader(new DefaultClassPath(forkOptions.getClasspath()));
-                FilteringClassLoader filteredGroovy = classLoaderFactory.createFilteringClassLoader(groovyClassLoader);
-                for (String packageName : forkOptions.getSharedPackages()) {
-                    filteredGroovy.allowPackage(packageName);
-                }
-
-                ClassLoader workerClassLoader = new MutableURLClassLoader(filteredGroovy, ClasspathUtil.getClasspath(compiler.getClass().getClassLoader()));
-
-                try {
-                    byte[] serializedWorker = GUtil.serialize(new Worker<T>(compiler, spec));
-                    ClassLoaderObjectInputStream inputStream = new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedWorker), workerClassLoader);
-                    Callable<?> worker = (Callable<?>) inputStream.readObject();
-                    Object result = worker.call();
-                    byte[] serializedResult = GUtil.serialize(result);
-                    inputStream = new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedResult), getClass().getClassLoader());
-                    return (CompileResult) inputStream.readObject();
-                } catch (Exception e) {
-                    throw UncheckedException.throwAsUncheckedException(e);
-                }
-            }
-        };
-    }
-
-    private static class Worker<T extends CompileSpec> implements Callable<Object>, Serializable {
-        private final Compiler<T> compiler;
-        private final T spec;
-
-        private Worker(Compiler<T> compiler, T spec) {
-            this.compiler = compiler;
-            this.spec = spec;
-        }
-
-        public Object call() throws Exception {
-            return new CompileResult(compiler.execute(spec).getDidWork(), null);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfo.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfo.java
deleted file mode 100644
index 23019d9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfo.java
+++ /dev/null
@@ -1,41 +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.tasks.compile.incremental;
-
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.FileVisitor;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class AllFromJarRebuildInfo extends DefaultRebuildInfo {
-    AllFromJarRebuildInfo(JarArchive jarContents) {
-        super(false, classesInJar(jarContents.contents));
-    }
-
-    private static Set<String> classesInJar(FileTree jarContents) {
-        final Set<String> out = new HashSet<String>();
-        jarContents.visit(new FileVisitor() {
-            public void visitDir(FileVisitDetails dirDetails) {}
-            public void visitFile(FileVisitDetails fileDetails) {
-                out.add(fileDetails.getPath().replaceAll("/", ".").replaceAll("\\.class$", ""));
-            }
-        });
-        return out;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassDependents.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassDependents.java
deleted file mode 100644
index 6041b2d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassDependents.java
+++ /dev/null
@@ -1,44 +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.tasks.compile.incremental;
-
-import java.io.Serializable;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-public class ClassDependents implements Serializable {
-
-    private final Set<String> dependentClasses = new LinkedHashSet<String>();
-    private boolean dependentToAll;
-
-    public Set<String> getDependentClasses() {
-        return dependentClasses;
-    }
-
-    public boolean isDependentToAll() {
-        return dependentToAll;
-    }
-
-    public ClassDependents addClass(String className) {
-        dependentClasses.add(className);
-        return this;
-    }
-
-    public void setDependentToAll() {
-        dependentToAll = true;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProvider.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProvider.java
deleted file mode 100644
index 84aa40f..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProvider.java
+++ /dev/null
@@ -1,38 +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.tasks.compile.incremental;
-
-import java.io.File;
-
-import static org.gradle.util.GFileUtils.relativePath;
-
-public class ClassNameProvider {
-
-    private final File compiledClassesDir;
-
-    public ClassNameProvider(File compiledClassesDir) {
-        this.compiledClassesDir = compiledClassesDir;
-    }
-
-    public String provideName(File classFile) {
-        String path = relativePath(compiledClassesDir, classFile);
-        if (path.startsWith("/") || path.startsWith(".")) {
-            throw new IllegalArgumentException("Given input class file: '" + classFile + "' is not located inside of '" + compiledClassesDir + "'.");
-        }
-        return path.replaceAll("/", ".").replaceAll("\\.class", "");
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DefaultRebuildInfo.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DefaultRebuildInfo.java
deleted file mode 100644
index c3c1841..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DefaultRebuildInfo.java
+++ /dev/null
@@ -1,53 +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.tasks.compile.incremental;
-
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfo;
-import org.gradle.api.tasks.util.PatternSet;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-class DefaultRebuildInfo implements RebuildInfo {
-
-    static final RebuildInfo NOTHING_TO_REBUILD = new DefaultRebuildInfo(false, Collections.<String>emptyList());
-    static final RebuildInfo FULL_REBUILD = new DefaultRebuildInfo(true, Collections.<String>emptyList());
-
-    private boolean fullRebuildRequired;
-    protected final Collection<String> changedClassesInJar;
-
-    DefaultRebuildInfo(boolean fullRebuildRequired, Collection<String> changedClassesInJar) {
-        this.fullRebuildRequired = fullRebuildRequired;
-        this.changedClassesInJar = changedClassesInJar;
-    }
-
-    public boolean isFullRebuildRequired() {
-        return fullRebuildRequired;
-    }
-
-    //TODO SF tests
-    public void configureCompilation(PatternSet changedSourceOnly, SelectiveJavaCompiler compiler, ClassDependencyInfo dependencyInfo) {
-        for (String c : changedClassesInJar) {
-            Set<String> actualDependents = dependencyInfo.getActualDependents(c);
-            for (String d : actualDependents) {
-                compiler.addStaleClass(d);
-                changedSourceOnly.include(d.replaceAll("\\.", "/").concat(".java"));
-            }
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DummySerializer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DummySerializer.java
deleted file mode 100644
index 84b5e4e..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/DummySerializer.java
+++ /dev/null
@@ -1,51 +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.tasks.compile.incremental;
-
-import java.io.*;
-
-import static org.apache.commons.io.IOUtils.closeQuietly;
-
-public class DummySerializer {
-    public static void writeTargetTo(File outputFile, Object target) {
-        try {
-            FileOutputStream out = new FileOutputStream(outputFile);
-            ObjectOutputStream objectStr = new ObjectOutputStream(out);
-            objectStr.writeObject(target);
-            objectStr.flush();
-            objectStr.close();
-            out.close();
-        } catch (IOException e) {
-            throw new RuntimeException("Problems writing to the output file " + outputFile, e);
-        }
-    }
-
-    public static Object readFrom(File inputFile) {
-        FileInputStream in = null;
-        ObjectInputStream objectStr = null;
-        try {
-            in = new FileInputStream(inputFile);
-            objectStr = new ObjectInputStream(in);
-            return objectStr.readObject();
-        } catch (Exception e) {
-            throw new RuntimeException("Problems reading the class tree to the output file " + inputFile, e);
-        } finally {
-            closeQuietly(in);
-            closeQuietly(objectStr);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupport.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupport.java
deleted file mode 100644
index 52c90cf..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupport.java
+++ /dev/null
@@ -1,51 +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.tasks.compile.incremental;
-
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfo;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoExtractor;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoSerializer;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.util.Clock;
-
-public class IncrementalCompilationSupport {
-
-    private static final Logger LOG = Logging.getLogger(IncrementalCompilationSupport.class);
-    private JarSnapshotFeeder jarSnapshotFeeder;
-
-    public IncrementalCompilationSupport(JarSnapshotFeeder jarSnapshotFeeder) {
-        this.jarSnapshotFeeder = jarSnapshotFeeder;
-    }
-
-    public void compilationComplete(CompileOptions options,
-                                    ClassDependencyInfoExtractor extractor,
-                                    ClassDependencyInfoSerializer serializer,
-                                    Iterable<JarArchive> jarsOnClasspath) {
-        if (options.isIncremental()) {
-            Clock clock = new Clock();
-            ClassDependencyInfo info = extractor.extractInfo("");
-            serializer.writeInfo(info);
-            LOG.lifecycle("Performed class dependency analysis in {}, wrote results into {}", clock.getTime(), serializer);
-
-//            clock = new Clock();
-//            jarSnapshotFeeder.storeJarSnapshots(jarsOnClasspath);
-//            LOG.lifecycle("Wrote jar snapshots in {}.", clock.getTime());
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapper.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapper.java
deleted file mode 100644
index d60ed46..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapper.java
+++ /dev/null
@@ -1,60 +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.tasks.compile.incremental;
-
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-
-import static java.lang.String.format;
-
-public class InputOutputMapper {
-
-    private Iterable<File> sourceDirs;
-    private File compileDestination;
-
-    public InputOutputMapper(Iterable<File> sourceDirs, File compileDestination) {
-        this.sourceDirs = sourceDirs;
-        this.compileDestination = compileDestination;
-    }
-
-    public JavaSourceClass toJavaSourceClass(File javaSourceClass) {
-        for (File sourceDir : sourceDirs) {
-            if (javaSourceClass.getAbsolutePath().startsWith(sourceDir.getAbsolutePath())) { //perf tweak only
-                String relativePath = GFileUtils.relativePath(sourceDir, javaSourceClass);
-                if (!relativePath.startsWith("..")) {
-                    return new JavaSourceClass(relativePath, compileDestination);
-                }
-            }
-        }
-        throw new IllegalArgumentException(format("Unable to find source java class: '%s' because it does not belong to any of the source dirs: '%s'",
-                javaSourceClass, sourceDirs));
-
-    }
-
-    public JavaSourceClass toJavaSourceClass(String className) {
-        String relativePath = className.replaceAll("\\.", "/").concat(".java");
-        for (File sourceDir : sourceDirs) {
-            File sourceFile = new File(sourceDir, relativePath);
-            if (sourceFile.isFile()) {
-                return new JavaSourceClass(relativePath, compileDestination);
-            }
-        }
-        throw new IllegalArgumentException(format("Unable to find source java class for '%s'. The source file '%s' was not found in source dirs: '%s'",
-                className, relativePath, sourceDirs));
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarArchive.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarArchive.java
deleted file mode 100644
index d29b4c9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarArchive.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile.incremental;
-
-import org.gradle.api.file.FileTree;
-import org.gradle.api.tasks.util.PatternSet;
-
-import java.io.File;
-
-public class JarArchive {
-    final File file;
-    final FileTree contents;
-    public JarArchive(File jar, FileTree contents) {
-        this.file = jar;
-        this.contents = contents.matching(new PatternSet().include("**/*.class"));
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarChangeProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarChangeProcessor.java
deleted file mode 100644
index 28523c0..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarChangeProcessor.java
+++ /dev/null
@@ -1,56 +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.tasks.compile.incremental;
-
-import org.gradle.api.tasks.incremental.InputFileDetails;
-
-public class JarChangeProcessor {
-
-    private JarSnapshotFeeder jarSnapshotFeeder;
-
-    public JarChangeProcessor(JarSnapshotFeeder jarSnapshotFeeder) {
-        this.jarSnapshotFeeder = jarSnapshotFeeder;
-    }
-
-    //TODO SF coverage
-    public RebuildInfo processJarChange(InputFileDetails jarChangeDetails, JarArchive jarArchive) {
-        JarSnapshot existing = jarSnapshotFeeder.changedJar(jarChangeDetails.getFile());
-        if (jarChangeDetails.isAdded()) {
-            return DefaultRebuildInfo.NOTHING_TO_REBUILD;
-        }
-
-        if (jarChangeDetails.isRemoved()) {
-            if (existing != null) {
-                return new AllFromJarRebuildInfo(jarArchive);
-            } else {
-                return DefaultRebuildInfo.FULL_REBUILD;
-            }
-        }
-
-        if (jarChangeDetails.isModified()) {
-            if (existing != null) {
-                JarSnapshot newSnapshot = jarSnapshotFeeder.createSnapshot(jarArchive);
-                JarDelta jarDelta = existing.compareToSnapshot(newSnapshot);
-                return new SpecificClassesRebuildInfo(jarDelta);
-            } else {
-                return new AllFromJarRebuildInfo(jarArchive);
-            }
-        }
-
-        throw new IllegalArgumentException("Unknown input file details provided: " + jarChangeDetails);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarDelta.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarDelta.java
deleted file mode 100644
index c310a95..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarDelta.java
+++ /dev/null
@@ -1,23 +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.tasks.compile.incremental;
-
-import java.util.Collection;
-
-interface JarDelta {
-    Collection<String> getChangedClasses();
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshot.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshot.java
deleted file mode 100644
index a110c19..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshot.java
+++ /dev/null
@@ -1,43 +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.tasks.compile.incremental;
-
-import java.io.Serializable;
-import java.util.*;
-
-class JarSnapshot implements Serializable {
-
-    final Map<String, byte[]> classHashes;
-
-    JarSnapshot(Map<String, byte[]> classHashes) {
-        this.classHashes = classHashes;
-    }
-
-    JarDelta compareToSnapshot(JarSnapshot other) {
-        final List<String> changedClasses = new LinkedList<String>();
-        for (String thisCls : classHashes.keySet()) {
-            byte[] hash = other.classHashes.get(thisCls);
-            if (hash == null || !Arrays.equals(hash, classHashes.get(thisCls))) {
-                changedClasses.add(thisCls);
-            }
-        }
-        return new JarDelta() {
-            public Collection<String> getChangedClasses() {
-                return changedClasses;
-            }
-        };
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCache.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCache.java
deleted file mode 100644
index e8cffc5..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCache.java
+++ /dev/null
@@ -1,53 +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.tasks.compile.incremental;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-//TODO SF this obviously needs to be replaced with a proper cache
-public class JarSnapshotCache {
-
-    private File sharedJarSnapshotCache;
-    private Map<File, JarSnapshot> snapshots;
-
-    public JarSnapshotCache(File sharedJarSnapshotCache) {
-        this.sharedJarSnapshotCache = sharedJarSnapshotCache;
-    }
-
-    public JarSnapshot getSnapshot(File jar) {
-        init();
-        return snapshots.get(jar);
-    }
-
-    public void putSnapshots(Map<File, JarSnapshot> newSnapshots) {
-        init();
-        this.snapshots.putAll(newSnapshots);
-        DummySerializer.writeTargetTo(sharedJarSnapshotCache, this.snapshots);
-    }
-
-    private void init() {
-        if (snapshots == null) {
-            if (sharedJarSnapshotCache.isFile()) {
-                snapshots = (Map) DummySerializer.readFrom(sharedJarSnapshotCache);
-            } else {
-                snapshots = new HashMap<File, JarSnapshot>();
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeeder.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeeder.java
deleted file mode 100644
index 67fadc8..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeeder.java
+++ /dev/null
@@ -1,57 +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.tasks.compile.incremental;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class JarSnapshotFeeder {
-
-    private final JarSnapshotCache jarSnapshotCache;
-    private final Set<File> changedJars = new HashSet<File>();
-    private final JarSnapshotter jarSnapshotter;
-
-    public JarSnapshotFeeder(JarSnapshotCache jarSnapshotCache, JarSnapshotter jarSnapshotter) {
-        this.jarSnapshotCache = jarSnapshotCache;
-        this.jarSnapshotter = jarSnapshotter;
-    }
-
-    public JarSnapshot changedJar(File jarFile) {
-        JarSnapshot snapshot = jarSnapshotCache.getSnapshot(jarFile);
-        changedJars.add(jarFile);
-        return snapshot;
-    }
-
-    public void storeJarSnapshots(Iterable<JarArchive> jars) {
-        Map<File, JarSnapshot> newSnapshots = new HashMap<File, JarSnapshot>();
-        for (JarArchive jar : jars) {
-            if (!changedJars.contains(jar.file) && jarSnapshotCache.getSnapshot(jar.file) != null) {
-                //if jar was not changed and the the snapshot already exists, skip
-                continue;
-            }
-            newSnapshots.put(jar.file, jarSnapshotter.createSnapshot(jar.contents));
-        }
-        jarSnapshotCache.putSnapshots(newSnapshots);
-    }
-
-    public JarSnapshot createSnapshot(JarArchive jarArchive) {
-        return jarSnapshotter.createSnapshot(jarArchive.contents);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotter.java
deleted file mode 100644
index 5f94967..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotter.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile.incremental;
-
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.FileVisitDetails;
-import org.gradle.api.file.FileVisitor;
-import org.gradle.api.internal.hash.Hasher;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JarSnapshotter {
-
-    private final Hasher hasher;
-
-    public JarSnapshotter(Hasher hasher) {
-        this.hasher = hasher;
-    }
-
-    JarSnapshot createSnapshot(FileTree archivedClasses) {
-        final Map<String, byte[]> hashes = new HashMap<String, byte[]>();
-        archivedClasses.visit(new FileVisitor() {
-            public void visitDir(FileVisitDetails dirDetails) {
-            }
-
-            public void visitFile(FileVisitDetails fileDetails) {
-                hashes.put(fileDetails.getPath().replaceAll("/", ".").replaceAll("\\.class$", ""), hasher.hash(fileDetails.getFile()));
-            }
-        });
-        return new JarSnapshot(hashes);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClass.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClass.java
deleted file mode 100644
index 0339ff3..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClass.java
+++ /dev/null
@@ -1,41 +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.tasks.compile.incremental;
-
-import java.io.File;
-
-public class JavaSourceClass {
-    private final String relativePath;
-    private final File compileDestination;
-
-    public JavaSourceClass(String relativePath, File compileDestination) {
-        this.relativePath = relativePath;
-        this.compileDestination = compileDestination;
-    }
-
-    public String getRelativePath() {
-        return relativePath;
-    }
-
-    public String getClassName() {
-        return relativePath.replaceAll("/", ".").replaceAll("\\.java$", "");
-    }
-
-    public File getOutputFile() {
-        return new File(compileDestination, relativePath.replaceAll("\\.java$", ".class"));
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapper.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapper.java
deleted file mode 100644
index 20b694d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapper.java
+++ /dev/null
@@ -1,32 +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.tasks.compile.incremental;
-
-import java.io.File;
-
-public class OutputClassMapper {
-
-    private File destinationDir;
-
-    public OutputClassMapper(File destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
-    public File getOutputFile(String className) {
-        return new File(destinationDir, className.replaceAll("\\.", "/").concat(".class")); //TODO SF duplicated
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/RebuildInfo.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/RebuildInfo.java
deleted file mode 100644
index 72458be..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/RebuildInfo.java
+++ /dev/null
@@ -1,25 +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.tasks.compile.incremental;
-
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfo;
-import org.gradle.api.tasks.util.PatternSet;
-
-interface RebuildInfo {
-    boolean isFullRebuildRequired();
-    void configureCompilation(PatternSet changedSourceOnly, SelectiveJavaCompiler compiler, ClassDependencyInfo dependencyInfo);
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompilation.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompilation.java
deleted file mode 100644
index b2716e6..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveCompilation.java
+++ /dev/null
@@ -1,141 +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.tasks.compile.incremental;
-
-import org.gradle.api.Action;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.file.FileOperations;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.internal.hash.DefaultHasher;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfo;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoSerializer;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
-import org.gradle.api.tasks.incremental.InputFileDetails;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.Clock;
-
-import java.io.File;
-import java.util.Set;
-
-public class SelectiveCompilation {
-    private final FileCollection source;
-    private final FileCollection classpath;
-    private final JarSnapshotFeeder jarSnapshotFeeder;
-    private SelectiveJavaCompiler compiler;
-    private static final Logger LOG = Logging.getLogger(SelectiveCompilation.class);
-    private String fullRebuildNeeded;
-    private boolean compilationNeeded = true;
-    private final FileOperations operations;
-
-    public SelectiveCompilation(IncrementalTaskInputs inputs, FileTree source, FileCollection compileClasspath, final File compileDestination,
-                                final ClassDependencyInfoSerializer dependencyInfoSerializer, final JarSnapshotCache jarSnapshotCache, final SelectiveJavaCompiler compiler,
-                                Iterable<File> sourceDirs, final FileOperations operations) {
-        this.operations = operations;
-        this.jarSnapshotFeeder = new JarSnapshotFeeder(jarSnapshotCache, new JarSnapshotter(new DefaultHasher()));
-        this.compiler = compiler;
-
-        Clock clock = new Clock();
-        final InputOutputMapper mapper = new InputOutputMapper(sourceDirs, compileDestination);
-
-        //load dependency info
-        final ClassDependencyInfo dependencyInfo = dependencyInfoSerializer.readInfo();
-
-        //including only source java classes that were changed
-        final PatternSet changedSourceOnly = new PatternSet();
-        InputFileDetailsAction action = new InputFileDetailsAction(mapper, compiler, changedSourceOnly, dependencyInfo);
-        inputs.outOfDate(action);
-        inputs.removed(action);
-        if (fullRebuildNeeded != null) {
-            LOG.lifecycle("Stale classes detection completed in {}. Rebuild needed: {}.", clock.getTime(), fullRebuildNeeded);
-            this.classpath = compileClasspath;
-            this.source = source;
-            return;
-        }
-
-        compiler.deleteStaleClasses();
-        Set<File> filesToCompile = source.matching(changedSourceOnly).getFiles();
-        if (filesToCompile.isEmpty()) {
-            this.compilationNeeded = false;
-            this.classpath = compileClasspath;
-            this.source = source;
-        } else {
-            this.classpath = compileClasspath.plus(new SimpleFileCollection(compileDestination));
-            this.source = source.matching(changedSourceOnly);
-        }
-        LOG.lifecycle("Stale classes detection completed in {}. Compile include patterns: {}.", clock.getTime(), changedSourceOnly.getIncludes());
-    }
-
-    public FileCollection getSource() {
-        return source;
-    }
-
-    public FileCollection getClasspath() {
-        return classpath;
-    }
-
-    public boolean getCompilationNeeded() {
-        return compilationNeeded;
-    }
-
-    public boolean getFullRebuildRequired() {
-        return fullRebuildNeeded != null;
-    }
-
-    private class InputFileDetailsAction implements Action<InputFileDetails> {
-        private final InputOutputMapper mapper;
-        private final SelectiveJavaCompiler compiler;
-        private final PatternSet changedSourceOnly;
-        private final ClassDependencyInfo dependencyInfo;
-
-        public InputFileDetailsAction(InputOutputMapper mapper, SelectiveJavaCompiler compiler, PatternSet changedSourceOnly, ClassDependencyInfo dependencyInfo) {
-            this.mapper = mapper;
-            this.compiler = compiler;
-            this.changedSourceOnly = changedSourceOnly;
-            this.dependencyInfo = dependencyInfo;
-        }
-
-        public void execute(InputFileDetails inputFileDetails) {
-            if (fullRebuildNeeded != null) {
-                return;
-            }
-            File inputFile = inputFileDetails.getFile();
-            String name = inputFile.getName();
-            if (name.endsWith(".java")) {
-                JavaSourceClass source = mapper.toJavaSourceClass(inputFile);
-                compiler.addStaleClass(source);
-                changedSourceOnly.include(source.getRelativePath());
-                Set<String> actualDependents = dependencyInfo.getActualDependents(source.getClassName());
-                if (actualDependents == null) {
-                    fullRebuildNeeded = "change to " + source.getClassName() + " requires full rebuild";
-                    return;
-                }
-                for (String d : actualDependents) {
-                    JavaSourceClass dSource = mapper.toJavaSourceClass(d);
-                    compiler.addStaleClass(dSource);
-                    changedSourceOnly.include(dSource.getRelativePath());
-                }
-            }
-            if (name.endsWith(".jar")) {
-                fullRebuildNeeded = "change to " + inputFile + " requires full rebuild";
-                return;
-            }
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveJavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveJavaCompiler.java
deleted file mode 100644
index cdc9959..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SelectiveJavaCompiler.java
+++ /dev/null
@@ -1,66 +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.tasks.compile.incremental;
-
-import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.Clock;
-
-import java.io.File;
-import java.util.Set;
-
-public class SelectiveJavaCompiler implements Compiler<JavaCompileSpec> {
-    private Compiler<JavaCompileSpec> compiler;
-    private final FileTree destinationDir;
-    private final PatternSet staleClasses = new PatternSet();
-    private final static Logger LOG = Logging.getLogger(SelectiveJavaCompiler.class);
-
-    public SelectiveJavaCompiler(Compiler<JavaCompileSpec> compiler, FileTree destinationDir) {
-        this.compiler = compiler;
-        this.destinationDir = destinationDir;
-    }
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        return compiler.execute(spec);
-    }
-
-    void deleteStaleClasses() {
-        Clock clock = new Clock();
-        Set<File> files = destinationDir.matching(staleClasses).getFiles();
-        for (File file : files) {
-            file.delete();
-        }
-        LOG.lifecycle("Deleting {} stale classes took {}", files.size(), clock.getTime());
-    }
-
-    public void addStaleClass(JavaSourceClass source) {
-        addStaleClass(source.getClassName());
-    }
-
-    public void addStaleClass(String className) {
-        String path = className.replaceAll("\\.", "/");
-        String cls = path.concat(".class");
-        String inner = path.concat("$*.class");
-        staleClasses.include(cls);
-        staleClasses.include(inner);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SpecificClassesRebuildInfo.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SpecificClassesRebuildInfo.java
deleted file mode 100644
index dabc133..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/SpecificClassesRebuildInfo.java
+++ /dev/null
@@ -1,23 +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.tasks.compile.incremental;
-
-public class SpecificClassesRebuildInfo extends DefaultRebuildInfo {
-    public SpecificClassesRebuildInfo(JarDelta jarDelta) {
-        super(false, jarDelta.getChangedClasses());
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.java
deleted file mode 100644
index ac2fd2d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassAnalysis.java
+++ /dev/null
@@ -1,37 +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.tasks.compile.incremental.analyzer;
-
-import java.util.List;
-
-public class ClassAnalysis {
-    private final List<String> classDependencies;
-    private boolean dependentToAll;
-
-    public ClassAnalysis(List<String> classDependencies, boolean dependentToAll) {
-        this.classDependencies = classDependencies;
-        this.dependentToAll = dependentToAll;
-    }
-
-    public List<String> getClassDependencies() {
-        return classDependencies;
-    }
-
-    public boolean isDependentToAll() {
-        return dependentToAll;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java
deleted file mode 100644
index 44686cc..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzer.java
+++ /dev/null
@@ -1,74 +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.tasks.compile.incremental.analyzer;
-
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.Type;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.LinkedList;
-import java.util.List;
-
-public class ClassDependenciesAnalyzer {
-
-    public ClassAnalysis getClassAnalysis(String className, InputStream input) throws IOException {
-        ClassRelevancyFilter filter = new ClassRelevancyFilter(className);
-        ClassReader reader = new ClassReader(input);
-        ClassDependenciesVisitor visitor = new ClassDependenciesVisitor();
-        reader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
-
-        List<String> classDependencies = getClassDependencies(filter, reader);
-        return new ClassAnalysis(classDependencies, visitor.dependentToAll);
-    }
-
-    private List<String> getClassDependencies(ClassRelevancyFilter filter, ClassReader reader) {
-        List<String> out = new LinkedList<String>();
-        char[] charBuffer = new char[reader.getMaxStringLength()];
-        for (int i = 1; i < reader.getItemCount(); i++) {
-            int itemOffset = reader.getItem(i);
-            if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
-                // A CONSTANT_Class entry, read the class descriptor
-                String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
-                Type type = Type.getObjectType(classDescriptor);
-                while (type.getSort() == Type.ARRAY) {
-                    type = type.getElementType();
-                }
-                if (type.getSort() != Type.OBJECT) {
-                    // A primitive type
-                    continue;
-                }
-                String name = type.getClassName();
-                if (filter.isRelevant(name)) {
-                    out.add(name);
-                }
-            }
-        }
-        return out;
-    }
-
-    public ClassAnalysis getClassAnalysis(String className, File classFile) throws IOException {
-        FileInputStream input = new FileInputStream(classFile);
-        try {
-            return getClassAnalysis(className, input);
-        } finally {
-            input.close();
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.java
deleted file mode 100644
index 274c243..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesVisitor.java
+++ /dev/null
@@ -1,58 +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.tasks.compile.incremental.analyzer;
-
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
-
-public class ClassDependenciesVisitor extends ClassVisitor {
-
-    private final static int API = Opcodes.ASM4;
-    boolean dependentToAll;
-
-    public ClassDependenciesVisitor() {
-        super(API);
-    }
-
-    @Override
-    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
-        if (isAnnotationType(interfaces)) {
-            dependentToAll = true;
-        }
-    }
-
-    private boolean isAnnotationType(String[] interfaces) {
-        return interfaces.length == 1 && interfaces[0].equals("java/lang/annotation/Annotation");
-    }
-
-    @Override
-    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
-        if (isConstant(access) && !isPrivate(access)) {
-            dependentToAll = true; //non-private const
-        }
-        return null;
-    }
-
-    private static boolean isPrivate(int access) {
-        return (access & Opcodes.ACC_PRIVATE) != 0;
-    }
-
-    private static boolean isConstant(int access) {
-        return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.java
deleted file mode 100644
index e163c2b..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassRelevancyFilter.java
+++ /dev/null
@@ -1,30 +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.tasks.compile.incremental.analyzer;
-
-public class ClassRelevancyFilter {
-
-    private String excludedClassName;
-
-    public ClassRelevancyFilter(String excludedClassName) {
-        this.excludedClassName = excludedClassName;
-    }
-
-    boolean isRelevant(String className) {
-        return !className.startsWith("java.") && !excludedClassName.equals(className);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfo.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfo.java
deleted file mode 100644
index e5414a1..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfo.java
+++ /dev/null
@@ -1,59 +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.tasks.compile.incremental.graph;
-
-import org.gradle.api.internal.tasks.compile.incremental.ClassDependents;
-
-import java.io.Serializable;
-import java.util.*;
-
-public class ClassDependencyInfo implements Serializable {
-
-    private final Map<String, ClassDependents> dependents;
-
-    public ClassDependencyInfo(Map<String, ClassDependents> dependents) {
-        this.dependents = dependents;
-    }
-
-    public Set<String> getActualDependents(String className) {
-        Set<String> out = new HashSet<String>();
-        Set<String> visited = new HashSet<String>();
-        ClassDependents deps = dependents.get(className);
-        if (deps == null) {
-            return Collections.emptySet();
-        }
-        if (deps.isDependentToAll()) {
-            return null;
-        }
-        recurseDependents(visited, out, className, deps);
-        out.remove(className);
-        return out;
-    }
-
-    private void recurseDependents(Set<String> visited, Collection<String> accumulator, String className, ClassDependents incomingDependents) {
-        if (!visited.add(className) || incomingDependents == null) {
-            return;
-        }
-        for (String dependent : incomingDependents.getDependentClasses()) {
-            if (!dependent.contains("$") && !dependent.equals(className)) { //naive
-                accumulator.add(dependent);
-            }
-            ClassDependents currentDependents = this.dependents.get(dependent);
-            recurseDependents(visited, accumulator, dependent, currentDependents);
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractor.java
deleted file mode 100644
index dd2543a..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractor.java
+++ /dev/null
@@ -1,74 +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.tasks.compile.incremental.graph;
-
-import org.apache.commons.io.FileUtils;
-import org.gradle.api.internal.tasks.compile.incremental.ClassDependents;
-import org.gradle.api.internal.tasks.compile.incremental.ClassNameProvider;
-import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassAnalysis;
-import org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-public class ClassDependencyInfoExtractor {
-
-    private File classesDir;
-
-    public ClassDependencyInfoExtractor(File classesDir) {
-        this.classesDir = classesDir;
-    }
-
-    public ClassDependencyInfo extractInfo(String packagePrefix) {
-        Map<String, ClassDependents> dependents = new HashMap<String, ClassDependents>();
-        Iterator output = FileUtils.iterateFiles(classesDir, new String[]{"class"}, true);
-        ClassNameProvider nameProvider = new ClassNameProvider(classesDir);
-        while (output.hasNext()) {
-            File classFile = (File) output.next();
-            String className = nameProvider.provideName(classFile);
-            if (!className.startsWith(packagePrefix)) {
-                continue;
-            }
-            try {
-                ClassAnalysis analysis = new ClassDependenciesAnalyzer().getClassAnalysis(className, classFile);
-                for (String dependency : analysis.getClassDependencies()) {
-                    if (!dependency.equals(className) && dependency.startsWith(packagePrefix)) {
-                        getOrCreateDependentMapping(dependents, dependency).addClass(className);
-                    }
-                }
-                if (analysis.isDependentToAll()) {
-                    getOrCreateDependentMapping(dependents, className).setDependentToAll();
-                }
-            } catch (IOException e) {
-                throw new RuntimeException("Problems extracting class dependency from " + classFile, e);
-            }
-        }
-        return new ClassDependencyInfo(dependents);
-    }
-
-    private ClassDependents getOrCreateDependentMapping(Map<String, ClassDependents> dependents, String dependency) {
-        ClassDependents d = dependents.get(dependency);
-        if (d == null) {
-            d = new ClassDependents();
-            dependents.put(dependency, d);
-        }
-        return d;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializer.java
deleted file mode 100644
index cde3b1e..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializer.java
+++ /dev/null
@@ -1,47 +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.tasks.compile.incremental.graph;
-
-import org.gradle.api.internal.tasks.compile.incremental.DummySerializer;
-
-import java.io.File;
-
-public class ClassDependencyInfoSerializer {
-    private File storage;
-
-    public ClassDependencyInfoSerializer(File storage) {
-        this.storage = storage;
-    }
-
-    public void writeInfo(ClassDependencyInfo info) {
-        //TODO SF this needs to use our standard serialization machinery
-        DummySerializer.writeTargetTo(storage, info);
-    }
-
-    public ClassDependencyInfo readInfo() {
-        return (ClassDependencyInfo) DummySerializer.readFrom(storage);
-    }
-
-    public boolean isInfoAvailable() {
-        return storage.isFile();
-    }
-
-    @Override
-    public String toString() {
-        return storage.toString();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/jdk6/Jdk6JavaCompiler.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/jdk6/Jdk6JavaCompiler.java
deleted file mode 100644
index 5baf594..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/jdk6/Jdk6JavaCompiler.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile.jdk6;
-
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.CompilationFailedException;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
-import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.internal.jvm.Jvm;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.ToolProvider;
-import java.io.File;
-import java.io.Serializable;
-import java.nio.charset.Charset;
-import java.util.List;
-
-public class Jdk6JavaCompiler implements Compiler<JavaCompileSpec>, Serializable {
-    private static final Logger LOGGER = LoggerFactory.getLogger(Jdk6JavaCompiler.class);
-
-    public WorkResult execute(JavaCompileSpec spec) {
-        LOGGER.info("Compiling with JDK Java compiler API.");
-
-        JavaCompiler.CompilationTask task = createCompileTask(spec);
-        boolean success = task.call();
-        if (!success) {
-            throw new CompilationFailedException();
-        }
-
-        return new SimpleWorkResult(true);
-    }
-
-    private JavaCompiler.CompilationTask createCompileTask(JavaCompileSpec spec) {
-        List<String> options = new JavaCompilerArgumentsBuilder(spec).build();
-        JavaCompiler compiler = findCompiler();
-        if(compiler==null){
-            throw new RuntimeException("Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory.");
-        }
-        CompileOptions compileOptions = spec.getCompileOptions();
-        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, compileOptions.getEncoding() != null ? Charset.forName(compileOptions.getEncoding()) : null);
-        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(spec.getSource());
-        return compiler.getTask(null, null, null, options, null, compilationUnits);
-    }
-
-    private static JavaCompiler findCompiler() {
-        File realJavaHome = Jvm.current().getJavaHome();
-        File javaHomeFromToolProvidersPointOfView = new File(System.getProperty("java.home"));
-        if (realJavaHome.equals(javaHomeFromToolProvidersPointOfView)) {
-            return ToolProvider.getSystemJavaCompiler();
-        }
-
-        System.setProperty("java.home", realJavaHome.getAbsolutePath());
-        try {
-            return ToolProvider.getSystemJavaCompiler();
-        } finally {
-            System.setProperty("java.home", javaHomeFromToolProvidersPointOfView.getAbsolutePath());
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DecoratingTestDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DecoratingTestDescriptor.java
index e06bf88..e3f327f 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DecoratingTestDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DecoratingTestDescriptor.java
@@ -16,8 +16,6 @@
 
 package org.gradle.api.internal.tasks.testing;
 
-import org.gradle.api.tasks.testing.TestDescriptor;
-
 public class DecoratingTestDescriptor implements TestDescriptorInternal {
     private final TestDescriptorInternal descriptor;
     private final TestDescriptorInternal parent;
@@ -32,7 +30,7 @@ public class DecoratingTestDescriptor implements TestDescriptorInternal {
         return descriptor.toString();
     }
 
-    public TestDescriptor getParent() {
+    public TestDescriptorInternal getParent() {
         return parent;
     }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptor.java
index ee94597..5345278 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptor.java
@@ -28,6 +28,6 @@ public class DefaultTestClassDescriptor extends DefaultTestSuiteDescriptor {
 
     @Override
     public String toString() {
-        return String.format("test class %s", getClassName());
+        return String.format("Test class %s", getClassName());
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestDescriptor.java
index 327bb1e..f64d515 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestDescriptor.java
@@ -28,7 +28,7 @@ public class DefaultTestDescriptor extends AbstractTestDescriptor implements Ser
 
     @Override
     public String toString() {
-        return String.format("test %s(%s)", getName(), className);
+        return String.format("Test %s(%s)", getName(), className);
     }
 
     public boolean isComposite() {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestMethodDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestMethodDescriptor.java
index b7dd451..9efe519 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestMethodDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestMethodDescriptor.java
@@ -23,6 +23,6 @@ public class DefaultTestMethodDescriptor extends DefaultTestDescriptor {
 
     @Override
     public String toString() {
-        return String.format("test method %s(%s)", getName(), getClassName());
+        return String.format("Test method %s(%s)", getName(), getClassName());
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptor.java
index dcb4e42..18f55d3 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptor.java
@@ -25,7 +25,7 @@ public class DefaultTestSuiteDescriptor extends AbstractTestDescriptor implement
 
     @Override
     public String toString() {
-        return String.format("test '%s'", getName());
+        return String.format("Test suite '%s'", getName());
     }
 
     public boolean isComposite() {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java
index b80b085..6f7b410 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java
@@ -16,6 +16,8 @@
 
 package org.gradle.api.internal.tasks.testing;
 
+import org.gradle.api.internal.tasks.testing.junit.JULRedirector;
+import org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor;
 import org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor;
 import org.gradle.internal.TimeProvider;
 
@@ -34,7 +36,7 @@ public class SuiteTestClassProcessor implements TestClassProcessor {
 
     public void startProcessing(TestResultProcessor testResultProcessor) {
         try {
-            resultProcessor = new AttachParentTestResultProcessor(testResultProcessor);
+            resultProcessor = new AttachParentTestResultProcessor(new CaptureTestOutputTestResultProcessor(testResultProcessor, new JULRedirector()));
             resultProcessor.started(suiteDescriptor, new TestStartEvent(timeProvider.getCurrentTime()));
             processor.startProcessing(resultProcessor);
         } catch (Throwable t) {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassLoaderFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassLoaderFactory.java
new file mode 100644
index 0000000..1182be0
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassLoaderFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing;
+
+import org.gradle.api.internal.initialization.ClassLoaderIds;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.internal.Factory;
+import org.gradle.internal.classpath.DefaultClassPath;
+
+public class TestClassLoaderFactory implements Factory<ClassLoader> {
+    private final ClassLoaderCache classLoaderCache;
+    private final Test testTask;
+    private ClassLoader testClassLoader;
+
+    public TestClassLoaderFactory(ClassLoaderCache classLoaderCache, Test testTask) {
+        this.classLoaderCache = classLoaderCache;
+        this.testTask = testTask;
+    }
+
+    public ClassLoader create() {
+        if (testClassLoader == null) {
+            testClassLoader = classLoaderCache.get(ClassLoaderIds.testTaskClasspath(testTask.getPath()), new DefaultClassPath(testTask.getClasspath()), null, null);
+        }
+        return testClassLoader;
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestDescriptorInternal.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestDescriptorInternal.java
index fc13eab..42be070 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestDescriptorInternal.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestDescriptorInternal.java
@@ -16,8 +16,13 @@
 
 package org.gradle.api.internal.tasks.testing;
 
+import org.gradle.api.Nullable;
 import org.gradle.api.tasks.testing.TestDescriptor;
 
 public interface TestDescriptorInternal extends TestDescriptor {
+    @Nullable
+    @Override
+    TestDescriptorInternal getParent();
+
     Object getId();
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
index 5af69d8..f662608 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
@@ -72,6 +72,6 @@ public class DefaultTestExecuter implements TestExecuter {
         } else {
             detector = new DefaultTestClassScanner(testClassFiles, null, processor);
         }
-        new TestMainAction(detector, processor, testResultProcessor, new TrueTimeProvider()).run();
+        new TestMainAction(detector, processor, testResultProcessor, new TrueTimeProvider(), testTask.getPath(), String.format("Gradle Test Run %s", testTask.getPath())).run();
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/TestClassVisitor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/TestClassVisitor.java
index b8bcd5a..a71d506 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/TestClassVisitor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/TestClassVisitor.java
@@ -27,7 +27,7 @@ public abstract class TestClassVisitor extends ClassVisitor {
     protected final TestFrameworkDetector detector;
 
     protected TestClassVisitor(TestFrameworkDetector detector) {
-        super(Opcodes.ASM4);
+        super(Opcodes.ASM5);
         if (detector == null) {
             throw new IllegalArgumentException("detector == null!");
         }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitSpec.java
index 18ba671..d01806c 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitSpec.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitSpec.java
@@ -15,20 +15,17 @@
  */
 package org.gradle.api.internal.tasks.testing.junit;
 
-import org.gradle.api.tasks.testing.junit.JUnitOptions;
-
 import java.io.Serializable;
 import java.util.Set;
 
 public class JUnitSpec implements Serializable {
-
     private final Set<String> includeCategories;
     private final Set<String> excludeCategories;
     private final Set<String> includedTests;
 
-    public JUnitSpec(final JUnitOptions options, Set<String> includedTests) {
-        this.includeCategories = options.getIncludeCategories();
-        this.excludeCategories = options.getExcludeCategories();
+    public JUnitSpec(Set<String> includeCategories, Set<String> excludeCategories, Set<String> includedTests) {
+        this.includeCategories = includeCategories;
+        this.excludeCategories = excludeCategories;
         this.includedTests = includedTests;
     }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
index 155ce5b..0676458 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
@@ -55,7 +55,7 @@ public class JUnitTestClassExecuter {
     }
 
     private void runTestClass(String testClassName) throws ClassNotFoundException {
-        final Class<?> testClass = Class.forName(testClassName, true, applicationClassLoader);
+        final Class<?> testClass = Class.forName(testClassName, false, applicationClassLoader);
         Request request = Request.aClass(testClass);
         if (options.hasCategoryConfiguration()) {
             Transformer<Class<?>, String> transformer = new Transformer<Class<?>, String>() {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
index 537fbdf..2da25da 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
@@ -19,12 +19,9 @@ package org.gradle.api.internal.tasks.testing.junit;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor;
 import org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor;
 import org.gradle.internal.TimeProvider;
-import org.gradle.internal.TrueTimeProvider;
 import org.gradle.internal.id.IdGenerator;
-import org.gradle.logging.StandardOutputRedirector;
 import org.gradle.messaging.actor.Actor;
 import org.gradle.messaging.actor.ActorFactory;
 import org.slf4j.Logger;
@@ -34,24 +31,22 @@ public class JUnitTestClassProcessor implements TestClassProcessor {
     private static final Logger LOGGER = LoggerFactory.getLogger(JUnitTestClassProcessor.class);
     private final IdGenerator<?> idGenerator;
     private final ActorFactory actorFactory;
-    private final StandardOutputRedirector outputRedirector;
-    private final TimeProvider timeProvider = new TrueTimeProvider();
+    private final TimeProvider timeProvider;
     private final JUnitSpec spec;
     private JUnitTestClassExecuter executer;
     private Actor resultProcessorActor;
 
-    public JUnitTestClassProcessor(JUnitSpec spec, IdGenerator<?> idGenerator, ActorFactory actorFactory,
-                                   StandardOutputRedirector standardOutputRedirector) {
+    public JUnitTestClassProcessor(JUnitSpec spec, IdGenerator<?> idGenerator, ActorFactory actorFactory, TimeProvider timeProvider) {
         this.idGenerator = idGenerator;
         this.spec = spec;
         this.actorFactory = actorFactory;
-        this.outputRedirector = standardOutputRedirector;
+        this.timeProvider = timeProvider;
     }
 
     public void startProcessing(TestResultProcessor resultProcessor) {
         // Build a result processor chain
         ClassLoader applicationClassLoader = Thread.currentThread().getContextClassLoader();
-        TestResultProcessor resultProcessorChain = new AttachParentTestResultProcessor(new CaptureTestOutputTestResultProcessor(resultProcessor, outputRedirector));
+        TestResultProcessor resultProcessorChain = new AttachParentTestResultProcessor(resultProcessor);
         TestClassExecutionEventGenerator eventGenerator = new TestClassExecutionEventGenerator(resultProcessorChain, idGenerator, timeProvider);
 
         // Wrap the result processor chain up in a blocking actor, to make the whole thing thread-safe
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestEventAdapter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestEventAdapter.java
index 3855fa2..5ebb10b 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestEventAdapter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestEventAdapter.java
@@ -19,7 +19,6 @@ package org.gradle.api.internal.tasks.testing.junit;
 import org.gradle.api.internal.tasks.testing.*;
 import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.internal.TimeProvider;
-import org.gradle.internal.concurrent.ThreadSafe;
 import org.gradle.internal.id.IdGenerator;
 import org.junit.runner.Description;
 import org.junit.runner.notification.Failure;
@@ -42,7 +41,7 @@ public class JUnitTestEventAdapter extends RunListener {
 
     public JUnitTestEventAdapter(TestResultProcessor resultProcessor, TimeProvider timeProvider,
                                  IdGenerator<?> idGenerator) {
-        assert resultProcessor instanceof ThreadSafe;
+        assert resultProcessor instanceof org.gradle.internal.concurrent.ThreadSafe;
         this.resultProcessor = resultProcessor;
         this.timeProvider = timeProvider;
         this.idGenerator = idGenerator;
@@ -114,7 +113,7 @@ public class JUnitTestEventAdapter extends RunListener {
         synchronized (lock) {
             testInternal = executing.remove(description);
             if (testInternal == null && executing.size() == 1) {
-                // Assume that test has renamed itself
+                // Assume that test has renamed itself (this can actually happen)
                 testInternal = executing.values().iterator().next();
                 executing.clear();
             }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
index 30634b7..86d5d77 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
@@ -18,6 +18,8 @@ package org.gradle.api.internal.tasks.testing.junit;
 
 import org.gradle.api.Action;
 import org.gradle.api.GradleException;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
+import org.gradle.api.internal.tasks.testing.TestClassLoaderFactory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestFramework;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
@@ -25,32 +27,31 @@ import org.gradle.api.internal.tasks.testing.detection.ClassFileExtractionManage
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.api.tasks.testing.junit.JUnitOptions;
-import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.TimeProvider;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.process.internal.WorkerProcessBuilder;
 
 import java.io.Serializable;
-import java.net.URLClassLoader;
 
 public class JUnitTestFramework implements TestFramework {
     private JUnitOptions options;
-    private JUnitDetector detector;
-    private final Test testTask;
-    private DefaultTestFilter filter;
+    private final JUnitDetector detector;
+    private final DefaultTestFilter filter;
+    private final TestClassLoaderFactory classLoaderFactory;
 
-    public JUnitTestFramework(Test testTask, DefaultTestFilter filter) {
-        this.testTask = testTask;
+    public JUnitTestFramework(Test testTask, DefaultTestFilter filter, ClassLoaderCache classLoaderCache) {
         this.filter = filter;
         options = new JUnitOptions();
         detector = new JUnitDetector(new ClassFileExtractionManager(testTask.getTemporaryDirFactory()));
+        classLoaderFactory = new TestClassLoaderFactory(classLoaderCache, testTask);
     }
 
     public WorkerTestClassProcessorFactory getProcessorFactory() {
         verifyJUnitCategorySupport();
         verifyJUnitFilteringSupport();
-        return new TestClassProcessorFactoryImpl(new JUnitSpec(options, filter.getIncludePatterns()));
+        return new TestClassProcessorFactoryImpl(new JUnitSpec(options.getIncludeCategories(), options.getExcludeCategories(), filter.getIncludePatterns()));
     }
 
     private void verifyJUnitCategorySupport() {
@@ -78,8 +79,8 @@ public class JUnitTestFramework implements TestFramework {
         }
     }
 
-    private URLClassLoader getTestClassLoader() {
-        return new URLClassLoader(new DefaultClassPath(testTask.getClasspath()).getAsURLArray(), null);
+    private ClassLoader getTestClassLoader() {
+        return classLoaderFactory.create();
     }
 
     private void filteringNotSupported() {
@@ -116,7 +117,7 @@ public class JUnitTestFramework implements TestFramework {
         }
 
         public TestClassProcessor create(ServiceRegistry serviceRegistry) {
-            return new JUnitTestClassProcessor(spec, serviceRegistry.get(IdGenerator.class), serviceRegistry.get(ActorFactory.class), new JULRedirector());
+            return new JUnitTestClassProcessor(spec, serviceRegistry.get(IdGenerator.class), serviceRegistry.get(ActorFactory.class), serviceRegistry.get(TimeProvider.class));
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestMethodDetecter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestMethodDetecter.java
index 1b84358..8440154 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestMethodDetecter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestMethodDetecter.java
@@ -24,7 +24,7 @@ class JUnitTestMethodDetecter extends MethodVisitor {
     private final JUnitTestClassDetecter testClassDetecter;
 
     JUnitTestMethodDetecter(JUnitTestClassDetecter testClassDetecter) {
-        super(Opcodes.ASM4);
+        super(Opcodes.ASM5);
         this.testClassDetecter = testClassDetecter;
     }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java
index 209f2fe..dc17154 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/ClassPageRenderer.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.tasks.testing.junit.report;
 
 import org.gradle.internal.ErroringAction;
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 import org.gradle.api.internal.tasks.testing.junit.result.TestFailure;
 import org.gradle.api.internal.tasks.testing.junit.result.TestResultsProvider;
 import org.gradle.api.tasks.testing.TestOutputEvent;
@@ -29,10 +29,8 @@ import java.io.IOException;
 class ClassPageRenderer extends PageRenderer<ClassTestResults> {
     private final CodePanelRenderer codePanelRenderer = new CodePanelRenderer();
     private final TestResultsProvider resultsProvider;
-    private final long classId;
 
-    public ClassPageRenderer(long classId, TestResultsProvider provider) {
-        this.classId = classId;
+    public ClassPageRenderer(TestResultsProvider provider) {
         this.resultsProvider = provider;
     }
 
@@ -75,7 +73,7 @@ class ClassPageRenderer extends PageRenderer<ClassTestResults> {
             for (TestFailure failure : test.getFailures()) {
                 String message;
                 if (GUtil.isTrue(failure.getMessage()) && !failure.getStackTrace().contains(failure.getMessage())) {
-                    message = failure.getMessage() + SystemProperties.getLineSeparator() + SystemProperties.getLineSeparator() + failure.getStackTrace();
+                    message = failure.getMessage() + SystemProperties.getInstance().getLineSeparator() + SystemProperties.getInstance().getLineSeparator() + failure.getStackTrace();
                 } else {
                     message = failure.getStackTrace();
                 }
@@ -93,6 +91,7 @@ class ClassPageRenderer extends PageRenderer<ClassTestResults> {
                 renderTests(writer);
             }
         });
+        final long classId = getModel().getId();
         if (resultsProvider.hasOutput(classId, TestOutputEvent.Destination.StdOut)) {
             addTab("Standard output", new ErroringAction<SimpleHtmlWriter>() {
                 @Override
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java
index d928f08..d9d32f3 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReport.java
@@ -23,25 +23,20 @@ import org.gradle.api.internal.tasks.testing.junit.result.TestMethodResult;
 import org.gradle.api.internal.tasks.testing.junit.result.TestResultsProvider;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.reporting.HtmlReportBuilder;
 import org.gradle.reporting.HtmlReportRenderer;
+import org.gradle.reporting.ReportRenderer;
 import org.gradle.util.Clock;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 
 import static org.gradle.api.tasks.testing.TestResult.ResultType.SKIPPED;
 
 public class DefaultTestReport implements TestReporter {
-    private final HtmlReportRenderer htmlRenderer = new HtmlReportRenderer();
     private final static Logger LOG = Logging.getLogger(DefaultTestReport.class);
 
-    public DefaultTestReport() {
-        htmlRenderer.requireResource(getClass().getResource("/org/gradle/reporting/report.js"));
-        htmlRenderer.requireResource(getClass().getResource("/org/gradle/reporting/base-style.css"));
-        htmlRenderer.requireResource(getClass().getResource("/org/gradle/reporting/css3-pie-1.0beta3.htc"));
-        htmlRenderer.requireResource(getClass().getResource("style.css"));
-    }
-
     public void generateReport(TestResultsProvider resultsProvider, File reportDir) {
         LOG.info("Generating HTML test report...");
 
@@ -73,21 +68,26 @@ public class DefaultTestReport implements TestReporter {
         return model;
     }
 
-    private void generateFiles(AllTestResults model, TestResultsProvider resultsProvider, File reportDir) {
+    private void generateFiles(AllTestResults model, final TestResultsProvider resultsProvider, File reportDir) {
         try {
-            generatePage(model, new OverviewPageRenderer(), new File(reportDir, "index.html"));
-            for (PackageTestResults packageResults : model.getPackages()) {
-                generatePage(packageResults, new PackagePageRenderer(), new File(reportDir, packageResults.getBaseUrl()));
-                for (ClassTestResults classResults : packageResults.getClasses()) {
-                    generatePage(classResults, new ClassPageRenderer(classResults.getId(), resultsProvider), new File(reportDir, classResults.getBaseUrl()));
+            HtmlReportRenderer htmlRenderer = new HtmlReportRenderer();
+            htmlRenderer.render(model, new ReportRenderer<AllTestResults, HtmlReportBuilder>() {
+                @Override
+                public void render(AllTestResults model, HtmlReportBuilder output) throws IOException {
+                    PackagePageRenderer packagePageRenderer = new PackagePageRenderer();
+                    ClassPageRenderer classPageRenderer = new ClassPageRenderer(resultsProvider);
+
+                    output.renderHtmlPage("index.html", model, new OverviewPageRenderer());
+                    for (PackageTestResults packageResults : model.getPackages()) {
+                        output.renderHtmlPage(packageResults.getBaseUrl(), packageResults, packagePageRenderer);
+                        for (ClassTestResults classResults : packageResults.getClasses()) {
+                            output.renderHtmlPage(classResults.getBaseUrl(), classResults, classPageRenderer);
+                        }
+                    }
                 }
-            }
+            }, reportDir);
         } catch (Exception e) {
             throw new GradleException(String.format("Could not generate test report to '%s'.", reportDir), e);
         }
     }
-
-    private <T extends CompositeTestResults> void generatePage(T model, PageRenderer<T> renderer, File outputFile) throws Exception {
-        htmlRenderer.renderer(renderer).writeTo(model, outputFile);
-    }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java
index 36f58a7..43a5f70 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/OverviewPageRenderer.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.tasks.testing.junit.report;
 
 import org.gradle.internal.ErroringAction;
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 
 import java.io.IOException;
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java
index 4f63d3c..aa764c6 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PackagePageRenderer.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.tasks.testing.junit.report;
 
 import org.gradle.internal.ErroringAction;
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 
 import java.io.IOException;
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java
index c4b3d12..af27b46 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/report/PageRenderer.java
@@ -17,14 +17,17 @@ package org.gradle.api.internal.tasks.testing.junit.report;
 
 import org.gradle.api.Action;
 import org.gradle.internal.ErroringAction;
-import org.gradle.api.internal.html.SimpleHtmlWriter;
+import org.gradle.internal.html.SimpleHtmlWriter;
 import org.gradle.reporting.ReportRenderer;
 import org.gradle.reporting.TabbedPageRenderer;
 import org.gradle.reporting.TabsRenderer;
 
 import java.io.IOException;
+import java.net.URL;
 
 abstract class PageRenderer<T extends CompositeTestResults> extends TabbedPageRenderer<T> {
+    private static final URL STYLE_URL = PageRenderer.class.getResource("style.css");
+
     private T results;
     private final TabsRenderer<T> tabsRenderer = new TabsRenderer<T>();
 
@@ -36,6 +39,11 @@ abstract class PageRenderer<T extends CompositeTestResults> extends TabbedPageRe
 
     protected abstract void registerTabs();
 
+    @Override
+    protected URL getStyleUrl() {
+        return STYLE_URL;
+    }
+
     protected void addTab(String title, final Action<SimpleHtmlWriter> contentRenderer) {
         tabsRenderer.add(title, new ReportRenderer<T, SimpleHtmlWriter>() {
             @Override
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriter.java
index e46e215..dc7c195 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriter.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.tasks.testing.junit.result;
 
 import org.apache.tools.ant.util.DateUtils;
-import org.gradle.api.internal.xml.SimpleXmlWriter;
+import org.gradle.internal.xml.SimpleXmlWriter;
 import org.gradle.api.tasks.testing.TestOutputEvent;
 import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.internal.UncheckedException;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestOutputStore.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestOutputStore.java
index 1426ac4..bcadbe2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestOutputStore.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestOutputStore.java
@@ -23,8 +23,8 @@ import org.gradle.api.UncheckedIOException;
 import org.gradle.api.tasks.testing.TestOutputEvent;
 import org.gradle.internal.io.RandomAccessFileInputStream;
 import org.gradle.internal.UncheckedException;
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
 
 import java.io.*;
 import java.nio.charset.Charset;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestReportDataCollector.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestReportDataCollector.java
index 99365df..eb76bf6 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestReportDataCollector.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestReportDataCollector.java
@@ -114,6 +114,10 @@ public class TestReportDataCollector implements TestListener, TestOutputListener
         if (className == null) {
             //this means that we receive an output before even starting any class (or too late).
             //we don't have a place for such output in any of the reports so skipping.
+            //Unfortunately, this happens pretty often with current level of TestNG support
+            //because output events emitted by constructor, beforeTest, beforeClass
+            // are sent before test start event is started and there is no parent class event emitted by TestNG.
+            //In short, the TestNG support could be better. See also TestNGOutputEventsIntegrationTest
             return;
         }
         TestClassResult classResult = results.get(className);
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestResultSerializer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestResultSerializer.java
index 0e86775..555f4da 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestResultSerializer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/result/TestResultSerializer.java
@@ -20,11 +20,11 @@ import org.gradle.api.Action;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.internal.UncheckedException;
-import org.gradle.messaging.serialize.Decoder;
-import org.gradle.messaging.serialize.Encoder;
-import org.gradle.messaging.serialize.FlushableEncoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedDecoder;
-import org.gradle.messaging.serialize.kryo.KryoBackedEncoder;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+import org.gradle.internal.serialize.FlushableEncoder;
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder;
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
 
 import java.io.*;
 import java.util.Collection;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLogging.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLogging.java
index 4cfe4fc..2884ea4 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLogging.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLogging.java
@@ -119,7 +119,11 @@ public class DefaultTestLogging implements TestLogging {
     }
 
     public TestLogging setShowStandardStreams(boolean flag) {
-        events.addAll(EnumSet.of(TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR));
+        if (flag) {
+            events.addAll(EnumSet.of(TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR));
+        } else {
+            events.removeAll(EnumSet.of(TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR));
+        }
         return this;
     }
 
@@ -145,4 +149,4 @@ public class DefaultTestLogging implements TestLogging {
         }
         return result;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java
index 9ed8e27..8cc42cf 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java
@@ -16,21 +16,31 @@
 
 package org.gradle.api.internal.tasks.testing.processors;
 
-import org.gradle.api.internal.tasks.testing.*;
-import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
+import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
+import org.gradle.api.internal.tasks.testing.TestResultProcessor;
+import org.gradle.api.internal.tasks.testing.TestStartEvent;
 import org.gradle.api.tasks.testing.TestOutputEvent;
 import org.gradle.logging.StandardOutputRedirector;
 
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * A {@link org.gradle.api.internal.tasks.testing.TestResultProcessor} which redirect stdout and stderr during the
  * execution of a test suite.
  */
 public class CaptureTestOutputTestResultProcessor implements TestResultProcessor {
     private final TestResultProcessor processor;
-    private final StandardOutputRedirector outputRedirector;
-    private Object suiteId;
+    private final TestOutputRedirector outputRedirector;
+    private Object rootId;
+    private Map<Object, Object> parents = new ConcurrentHashMap<Object, Object>();
 
     public CaptureTestOutputTestResultProcessor(TestResultProcessor processor, StandardOutputRedirector outputRedirector) {
+        this(processor, new TestOutputRedirector(processor, outputRedirector));
+    }
+
+    CaptureTestOutputTestResultProcessor(TestResultProcessor processor, TestOutputRedirector outputRedirector) {
         this.processor = processor;
         this.outputRedirector = outputRedirector;
     }
@@ -38,38 +48,39 @@ public class CaptureTestOutputTestResultProcessor implements TestResultProcessor
     public void started(final TestDescriptorInternal test, TestStartEvent event) {
         processor.started(test, event);
 
-        //should redirect output for every particular test
-        redirectOutputFor(test.getId());
+        outputRedirector.setOutputOwner(test.getId());
 
-        //currently our test reports include std out/err per test class (aka suite) not per test method (aka test)
-        //for historical reasons. Therefore we only start/stop redirector per suite.
-        if (suiteId != null) {
-            return;
+        if (rootId == null) {
+            outputRedirector.startRedirecting();
+            rootId = test.getId();
+        } else {
+            Object parentId = event.getParentId();
+            if (parentId == null) {
+                //if we don't know the parent we will use the top suite
+                //this way we always have and id to attach logging events for
+                parentId = rootId;
+            }
+            parents.put(test.getId(), parentId);
         }
-        suiteId = test.getId();
-        outputRedirector.start();
     }
 
     public void completed(Object testId, TestCompleteEvent event) {
-        if (testId.equals(suiteId)) {
-            //when suite is completed we no longer redirect for this suite
+        if (testId.equals(rootId)) {
+            //when root suite is completed we stop redirecting
             try {
-                outputRedirector.stop();
+                outputRedirector.stopRedirecting();
             } finally {
-                suiteId = null;
+                rootId = null;
             }
         } else {
-            //when test is completed, should redirect output for the 'suite' to log things like @AfterSuite, etc.
-            redirectOutputFor(suiteId);
+            //when test is completed we should redirect output for the parent
+            //so that log events emitted during @AfterSuite, @AfterClass are processed
+            Object newOwner = parents.remove(testId);
+            outputRedirector.setOutputOwner(newOwner);
         }
         processor.completed(testId, event);
     }
 
-    private void redirectOutputFor(final Object testId) {
-        outputRedirector.redirectStandardOutputTo(new StdOutForwarder(testId));
-        outputRedirector.redirectStandardErrorTo(new StdErrForwarder(testId));
-    }
-
     public void output(Object testId, TestOutputEvent event) {
         processor.output(testId, event);
     }
@@ -77,29 +88,4 @@ public class CaptureTestOutputTestResultProcessor implements TestResultProcessor
     public void failure(Object testId, Throwable result) {
         processor.failure(testId, result);
     }
-
-
-    class StdOutForwarder implements StandardOutputListener {
-        private final Object testId;
-
-        public StdOutForwarder(Object testId) {
-            this.testId = testId;
-        }
-
-        public void onOutput(CharSequence output) {
-            processor.output(testId, new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, output.toString()));
-        }
-    }
-
-    class StdErrForwarder implements StandardOutputListener {
-        private final Object testId;
-
-        public StdErrForwarder(Object testId) {
-            this.testId = testId;
-        }
-
-        public void onOutput(CharSequence output) {
-            processor.output(testId, new DefaultTestOutputEvent(TestOutputEvent.Destination.StdErr, output.toString()));
-        }
-    }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainAction.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainAction.java
index e1356fe..a6b7d5f 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainAction.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainAction.java
@@ -18,24 +18,27 @@ package org.gradle.api.internal.tasks.testing.processors;
 
 import org.gradle.api.internal.tasks.testing.*;
 import org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor;
-import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.internal.TimeProvider;
 
 public class TestMainAction implements Runnable {
+    private final Runnable detector;
     private final TestClassProcessor processor;
     private final TestResultProcessor resultProcessor;
     private final TimeProvider timeProvider;
-    private final Runnable detector;
+    private final String rootTestSuiteId;
+    private final String displayName;
 
-    public TestMainAction(Runnable detector, TestClassProcessor processor, TestResultProcessor resultProcessor, TimeProvider timeProvider) {
+    public TestMainAction(Runnable detector, TestClassProcessor processor, TestResultProcessor resultProcessor, TimeProvider timeProvider, String rootTestSuiteId, String displayName) {
         this.detector = detector;
         this.processor = processor;
         this.resultProcessor = new AttachParentTestResultProcessor(resultProcessor);
         this.timeProvider = timeProvider;
+        this.rootTestSuiteId = rootTestSuiteId;
+        this.displayName = displayName;
     }
 
     public void run() {
-        RootTestSuiteDescriptor suite = new RootTestSuiteDescriptor();
+        RootTestSuiteDescriptor suite = new RootTestSuiteDescriptor(rootTestSuiteId, displayName);
         resultProcessor.started(suite, new TestStartEvent(timeProvider.getCurrentTime()));
         try {
             processor.startProcessing(resultProcessor);
@@ -50,13 +53,13 @@ public class TestMainAction implements Runnable {
     }
 
     private static class RootTestSuiteDescriptor extends DefaultTestSuiteDescriptor {
-        public RootTestSuiteDescriptor() {
-            super("root", "Test Run");
+        public RootTestSuiteDescriptor(Object id, String name) {
+            super(id, name);
         }
 
         @Override
         public String toString() {
-            return "tests";
+            return getName();
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirector.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirector.java
new file mode 100644
index 0000000..8609cda
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirector.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.processors;
+
+import org.gradle.api.internal.tasks.testing.DefaultTestOutputEvent;
+import org.gradle.api.internal.tasks.testing.TestResultProcessor;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.api.tasks.testing.TestOutputEvent;
+import org.gradle.logging.StandardOutputRedirector;
+
+class TestOutputRedirector {
+    private final StandardOutputRedirector redirector;
+    Forwarder outForwarder;
+    Forwarder errForwarder;
+
+    TestOutputRedirector(TestResultProcessor processor, StandardOutputRedirector redirector) {
+        this.redirector = redirector;
+        this.outForwarder = new Forwarder(processor, TestOutputEvent.Destination.StdOut);
+        this.errForwarder = new Forwarder(processor, TestOutputEvent.Destination.StdErr);
+    }
+
+    void startRedirecting() {
+        assert outForwarder.outputOwner != null;
+        assert errForwarder.outputOwner != null;
+
+        redirector.redirectStandardOutputTo(outForwarder);
+        redirector.redirectStandardErrorTo(errForwarder);
+        redirector.start();
+    }
+
+    void stopRedirecting() {
+        redirector.stop();
+    }
+
+    void setOutputOwner(Object testId) {
+        assert testId != null;
+        if (System.out != null) {
+            System.out.flush();
+        }
+        if (System.err != null) {
+            System.err.flush();
+        }
+        outForwarder.outputOwner = testId;
+        errForwarder.outputOwner = testId;
+    }
+
+    static class Forwarder implements StandardOutputListener {
+        final TestResultProcessor processor;
+        final TestOutputEvent.Destination dest;
+        Object outputOwner;
+
+        public Forwarder(TestResultProcessor processor, TestOutputEvent.Destination dest) {
+            this.processor = processor;
+            this.dest = dest;
+        }
+
+        public void onOutput(CharSequence output) {
+            if (outputOwner == null) {
+                throw new RuntimeException("Unable send output event from test executor. Please report this problem. Destination: " + dest + ", event: " + output.toString());
+            }
+            processor.output(outputOwner, new DefaultTestOutputEvent(dest, output.toString()));
+        }
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessor.java
index 60168cc..b71ac03 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessor.java
@@ -17,15 +17,19 @@
 package org.gradle.api.internal.tasks.testing.results;
 
 import org.gradle.api.internal.tasks.testing.*;
-import org.gradle.api.tasks.testing.TestDescriptor;
 import org.gradle.api.tasks.testing.TestOutputEvent;
 
 import java.util.HashMap;
 import java.util.Map;
 
-public abstract class StateTrackingTestResultProcessor implements TestResultProcessor {
+public class StateTrackingTestResultProcessor implements TestResultProcessor {
     private final Map<Object, TestState> executing = new HashMap<Object, TestState>();
-    private TestDescriptor currentParent;
+    private TestDescriptorInternal currentParent;
+    private final TestListenerInternal delegate;
+
+    public StateTrackingTestResultProcessor(TestListenerInternal delegate) {
+        this.delegate = delegate;
+    }
 
     public final void started(TestDescriptorInternal test, TestStartEvent event) {
         TestDescriptorInternal parent = null;
@@ -39,7 +43,7 @@ public abstract class StateTrackingTestResultProcessor implements TestResultProc
                     test, test.getId()));
         }
 
-        started(state);
+        delegate.started(state.test, event);
     }
 
     public final void completed(Object testId, TestCompleteEvent event) {
@@ -62,7 +66,7 @@ public abstract class StateTrackingTestResultProcessor implements TestResultProc
         currentParent = testState.test.getParent();
 
         testState.completed(event);
-        completed(testState);
+        delegate.completed(testState.test, new DefaultTestResult(testState), event);
     }
 
     public final void failure(Object testId, Throwable result) {
@@ -76,31 +80,20 @@ public abstract class StateTrackingTestResultProcessor implements TestResultProc
     }
 
     public final void output(Object testId, TestOutputEvent event) {
-        output(findDescriptor(testId), event);
+        delegate.output(findDescriptor(testId), event);
     }
 
-    private TestDescriptor findDescriptor(Object testId) {
+    private TestDescriptorInternal findDescriptor(Object testId) {
         TestState state = executing.get(testId);
         if (state != null) {
             return state.test;
         }
 
-        TestDescriptor d = currentParent;
-        if (d != null) {
-            return d;
+        if (currentParent != null) {
+            return currentParent;
         }
 
         //in theory this should not happen
         return new UnknownTestDescriptor();
     }
-
-    protected void output(TestDescriptor descriptor, TestOutputEvent event) {
-        // Don't care
-    }
-
-    protected void started(TestState state) {
-    }
-
-    protected void completed(TestState state) {
-    }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapter.java
index 1525082..ffd3139 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapter.java
@@ -16,10 +16,15 @@
 
 package org.gradle.api.internal.tasks.testing.results;
 
+import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
 import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
-import org.gradle.api.tasks.testing.*;
+import org.gradle.api.internal.tasks.testing.TestStartEvent;
+import org.gradle.api.tasks.testing.TestListener;
+import org.gradle.api.tasks.testing.TestOutputEvent;
+import org.gradle.api.tasks.testing.TestOutputListener;
+import org.gradle.api.tasks.testing.TestResult;
 
-public class TestListenerAdapter extends StateTrackingTestResultProcessor {
+public class TestListenerAdapter implements TestListenerInternal {
     private final TestListener testListener;
     private final TestOutputListener testOutputListener;
 
@@ -29,8 +34,7 @@ public class TestListenerAdapter extends StateTrackingTestResultProcessor {
     }
 
     @Override
-    protected void started(TestState state) {
-        TestDescriptorInternal test = state.test;
+    public void started(TestDescriptorInternal test, TestStartEvent startEvent) {
         if (test.isComposite()) {
             testListener.beforeSuite(test);
         } else {
@@ -39,9 +43,7 @@ public class TestListenerAdapter extends StateTrackingTestResultProcessor {
     }
 
     @Override
-    protected void completed(TestState state) {
-        TestResult result = new DefaultTestResult(state);
-        TestDescriptorInternal test = state.test;
+    public void completed(TestDescriptorInternal test, TestResult result, TestCompleteEvent completeEvent) {
         if (test.isComposite()) {
             testListener.afterSuite(test, result);
         } else {
@@ -50,7 +52,7 @@ public class TestListenerAdapter extends StateTrackingTestResultProcessor {
     }
 
     @Override
-    public void output(TestDescriptor test, TestOutputEvent event) {
+    public void output(TestDescriptorInternal test, TestOutputEvent event) {
         testOutputListener.onOutput(test, event);
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerInternal.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerInternal.java
new file mode 100644
index 0000000..ab336ad
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerInternal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.results;
+
+import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
+import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
+import org.gradle.api.internal.tasks.testing.TestStartEvent;
+import org.gradle.api.tasks.testing.TestOutputEvent;
+import org.gradle.api.tasks.testing.TestResult;
+
+public interface TestListenerInternal {
+    void started(TestDescriptorInternal testDescriptor, TestStartEvent startEvent);
+
+    void completed(TestDescriptorInternal testDescriptor, TestResult testResult, TestCompleteEvent completeEvent);
+
+    void output(TestDescriptorInternal testDescriptor, TestOutputEvent event);
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/UnknownTestDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/UnknownTestDescriptor.java
index 8a9fc48..12a031c 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/UnknownTestDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/UnknownTestDescriptor.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.tasks.testing.results;
 
 import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
-import org.gradle.api.tasks.testing.TestDescriptor;
 
 public class UnknownTestDescriptor implements TestDescriptorInternal {
 
@@ -36,7 +35,7 @@ public class UnknownTestDescriptor implements TestDescriptorInternal {
         return false;
     }
 
-    public TestDescriptor getParent() {
+    public TestDescriptorInternal getParent() {
         return null;
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactory.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactory.java
index a145baf..4a82f83 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactory.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactory.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.tasks.testing.testng;
 
 import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.gradle.internal.reflect.JavaMethod;
+import org.testng.ISuiteListener;
 import org.testng.ITestListener;
 
 import java.lang.reflect.InvocationHandler;
@@ -53,7 +54,7 @@ class TestNGListenerAdapterFactory {
     }
 
     private ITestListener createProxy(Class<?> configListenerClass, final ITestListener listener) {
-        return (ITestListener) Proxy.newProxyInstance(classLoader, new Class<?>[]{ITestListener.class, configListenerClass}, new InvocationHandler() {
+        return (ITestListener) Proxy.newProxyInstance(classLoader, new Class<?>[]{ITestListener.class, ISuiteListener.class, configListenerClass}, new InvocationHandler() {
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 Class<?> realReturnType = method.getReturnType();
                 Class<?> boxedReturnType = realReturnType;
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGSpec.java
index 1d4272f..86ab39b 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGSpec.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGSpec.java
@@ -38,6 +38,7 @@ public class TestNGSpec implements Serializable {
     private final Set<String> excludeGroups;
     private final Set<String> listeners;
     private final Set<String> includedTests;
+    private final String configFailurePolicy;
 
     public TestNGSpec(TestNGOptions options, DefaultTestFilter filter) {
         this.defaultSuiteName = options.getSuiteName();
@@ -52,6 +53,7 @@ public class TestNGSpec implements Serializable {
         this.excludeGroups = options.getExcludeGroups();
         this.listeners = options.getListeners();
         this.includedTests = filter.getIncludePatterns();
+        this.configFailurePolicy = options.getConfigFailurePolicy();
     }
 
     public Set<String> getListeners() {
@@ -101,4 +103,8 @@ public class TestNGSpec implements Serializable {
     public Set<String> getIncludedTests() {
         return includedTests;
     }
+
+    public String getConfigFailurePolicy() {
+        return configFailurePolicy;
+    }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
index f14ee1c..a0e5d70 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
@@ -20,12 +20,14 @@ import org.gradle.api.GradleException;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor;
 import org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher;
+import org.gradle.api.tasks.testing.testng.TestNGOptions;
+import org.gradle.internal.TimeProvider;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.reflect.JavaReflectionUtil;
 import org.gradle.internal.reflect.NoSuchMethodException;
-import org.gradle.logging.StandardOutputRedirector;
+import org.gradle.messaging.actor.Actor;
+import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.util.CollectionUtils;
 import org.gradle.util.GFileUtils;
 import org.testng.*;
@@ -42,28 +44,30 @@ public class TestNGTestClassProcessor implements TestClassProcessor {
     private final TestNGSpec options;
     private final List<File> suiteFiles;
     private final IdGenerator<?> idGenerator;
-    private final StandardOutputRedirector outputRedirector;
-    private TestNGTestResultProcessorAdapter testResultProcessor;
+    private final TimeProvider timeProvider;
+    private final ActorFactory actorFactory;
     private ClassLoader applicationClassLoader;
+    private Actor resultProcessorActor;
+    private TestResultProcessor resultProcessor;
 
-    public TestNGTestClassProcessor(File testReportDir, TestNGSpec options, List<File> suiteFiles, IdGenerator<?> idGenerator,
-                                    StandardOutputRedirector outputRedirector) {
+    public TestNGTestClassProcessor(File testReportDir, TestNGSpec options, List<File> suiteFiles, IdGenerator<?> idGenerator, TimeProvider timeProvider, ActorFactory actorFactory) {
         this.testReportDir = testReportDir;
         this.options = options;
         this.suiteFiles = suiteFiles;
         this.idGenerator = idGenerator;
-        this.outputRedirector = outputRedirector;
+        this.timeProvider = timeProvider;
+        this.actorFactory = actorFactory;
     }
 
     public void startProcessing(TestResultProcessor resultProcessor) {
-        TestResultProcessor resultProcessorChain = new CaptureTestOutputTestResultProcessor(resultProcessor, outputRedirector);
-
-        testResultProcessor = new TestNGTestResultProcessorAdapter(resultProcessorChain, idGenerator);
-
+        // Wrap the processor in an actor, to make it thread-safe
+        resultProcessorActor = actorFactory.createBlockingActor(resultProcessor);
+        this.resultProcessor = resultProcessorActor.getProxy(TestResultProcessor.class);
         applicationClassLoader = Thread.currentThread().getContextClassLoader();
     }
 
     public void processTestClass(TestClassRunInfo testClass) {
+        // TODO - do this inside some 'testng' suite, so that failures and logging are attached to 'testng' rather than some 'test worker'
         try {
             testClasses.add(applicationClassLoader.loadClass(testClass.getTestClassName()));
         } catch (Throwable e) {
@@ -72,12 +76,29 @@ public class TestNGTestClassProcessor implements TestClassProcessor {
     }
 
     public void stop() {
+        try {
+            runTests();
+        } finally {
+            resultProcessorActor.stop();
+        }
+    }
+
+    private void runTests() {
         TestNG testNg = new TestNG();
         testNg.setOutputDirectory(testReportDir.getAbsolutePath());
         testNg.setDefaultSuiteName(options.getDefaultSuiteName());
         testNg.setDefaultTestName(options.getDefaultTestName());
         testNg.setParallel(options.getParallel());
         testNg.setThreadCount(options.getThreadCount());
+        String configFailurePolicy = options.getConfigFailurePolicy();
+        try {
+            JavaReflectionUtil.method(TestNG.class, Object.class, "setConfigFailurePolicy", String.class).invoke(testNg, configFailurePolicy);
+        } catch (NoSuchMethodException e) {
+            if (!configFailurePolicy.equals(TestNGOptions.DEFAULT_CONFIG_FAILURE_POLICY)) {
+                // Should not reach this point as this is validated in the test framework implementation - just propagate the failure
+                throw e;
+            }
+        }
         try {
             JavaReflectionUtil.method(TestNG.class, Object.class, "setAnnotations").invoke(testNg, options.getAnnotations());
         } catch (NoSuchMethodException e) {
@@ -88,13 +109,12 @@ public class TestNGTestClassProcessor implements TestClassProcessor {
         }
 
         testNg.setUseDefaultListeners(options.getUseDefaultListeners());
-        testNg.addListener((Object) adaptListener(testResultProcessor));
-        if (!options.getIncludedTests().isEmpty()) {
-            testNg.addListener(new SelectedTestsFilter(options.getIncludedTests()));
-        }
         testNg.setVerbose(0);
         testNg.setGroups(CollectionUtils.join(",", options.getIncludeGroups()));
         testNg.setExcludedGroups(CollectionUtils.join(",", options.getExcludeGroups()));
+
+        //adding custom test listeners before Gradle's listeners.
+        //this way, custom listeners are more powerful and, for example, they can change test status.
         for (String listenerClass : options.getListeners()) {
             try {
                 testNg.addListener(applicationClassLoader.loadClass(listenerClass).newInstance());
@@ -103,12 +123,16 @@ public class TestNGTestClassProcessor implements TestClassProcessor {
             }
         }
 
+        if (!options.getIncludedTests().isEmpty()) {
+            testNg.addListener(new SelectedTestsFilter(options.getIncludedTests()));
+        }
+
         if (!suiteFiles.isEmpty()) {
             testNg.setTestSuites(GFileUtils.toPaths(suiteFiles));
         } else {
             testNg.setTestClasses(testClasses.toArray(new Class[testClasses.size()]));
         }
-
+        testNg.addListener((Object) adaptListener(new TestNGTestResultProcessorAdapter(resultProcessor, idGenerator, timeProvider)));
         testNg.run();
     }
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
index a438877..dcc10c2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
@@ -17,20 +17,25 @@
 package org.gradle.api.internal.tasks.testing.testng;
 
 import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.JavaVersion;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
 import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.tasks.testing.TestClassLoaderFactory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestFramework;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.detection.ClassFileExtractionManager;
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter;
-import org.gradle.api.internal.tasks.testing.junit.JULRedirector;
 import org.gradle.api.reporting.DirectoryReport;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.api.tasks.testing.testng.TestNGOptions;
+import org.gradle.internal.TimeProvider;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.process.internal.WorkerProcessBuilder;
 
 import java.io.File;
@@ -40,17 +45,19 @@ import java.util.concurrent.Callable;
 
 public class TestNGTestFramework implements TestFramework {
     private TestNGOptions options;
-    private TestNGDetector detector;
-    final Test testTask;
-    private DefaultTestFilter filter;
+    private final TestNGDetector detector;
+    private final Test testTask;
+    private final DefaultTestFilter filter;
+    private final TestClassLoaderFactory classLoaderFactory;
 
-    public TestNGTestFramework(Test testTask, DefaultTestFilter filter, Instantiator instantiator) {
+    public TestNGTestFramework(Test testTask, DefaultTestFilter filter, Instantiator instantiator, ClassLoaderCache classLoaderCache) {
         this.testTask = testTask;
         this.filter = filter;
         options = instantiator.newInstance(TestNGOptions.class, testTask.getProject().getProjectDir());
         options.setAnnotationsOnSourceCompatibility(JavaVersion.toVersion(testTask.getProject().property("sourceCompatibility")));
         conventionMapOutputDirectory(options, testTask.getReports().getHtml());
         detector = new TestNGDetector(new ClassFileExtractionManager(testTask.getTemporaryDirFactory()));
+        classLoaderFactory = new TestClassLoaderFactory(classLoaderCache, testTask);
     }
 
     private static void conventionMapOutputDirectory(TestNGOptions options, final DirectoryReport html) {
@@ -62,11 +69,25 @@ public class TestNGTestFramework implements TestFramework {
     }
 
     public WorkerTestClassProcessorFactory getProcessorFactory() {
+        verifyConfigFailurePolicy();
         options.setTestResources(testTask.getTestSrcDirs());
         List<File> suiteFiles = options.getSuites(testTask.getTemporaryDir());
         return new TestClassProcessorFactoryImpl(options.getOutputDirectory(), new TestNGSpec(options, filter), suiteFiles);
     }
 
+    private void verifyConfigFailurePolicy() {
+        if (!options.getConfigFailurePolicy().equals(TestNGOptions.DEFAULT_CONFIG_FAILURE_POLICY)) {
+            try {
+                Class<?> testNg = classLoaderFactory.create().loadClass("org.testng.TestNG");
+                testNg.getMethod("setConfigFailurePolicy", String.class);
+            } catch (ClassNotFoundException e) {
+                throw new GradleException("Could not load TestNG.", e);
+            } catch (NoSuchMethodException e) {
+                throw new InvalidUserDataException(String.format("The version of TestNG used does not support setting config failure policy to '%s'.", options.getConfigFailurePolicy()));
+            }
+        }
+    }
+
     public Action<WorkerProcessBuilder> getWorkerConfigurationAction() {
         return new Action<WorkerProcessBuilder>() {
             public void execute(WorkerProcessBuilder workerProcessBuilder) {
@@ -99,8 +120,7 @@ public class TestNGTestFramework implements TestFramework {
         }
 
         public TestClassProcessor create(ServiceRegistry serviceRegistry) {
-            return new TestNGTestClassProcessor(testReportDir, options, suiteFiles,
-                    serviceRegistry.get(IdGenerator.class), new JULRedirector());
+            return new TestNGTestClassProcessor(testReportDir, options, suiteFiles, serviceRegistry.get(IdGenerator.class), serviceRegistry.get(TimeProvider.class), serviceRegistry.get(ActorFactory.class));
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestMethodDetecter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestMethodDetecter.java
index e7ef57b..f9e70fe 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestMethodDetecter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestMethodDetecter.java
@@ -27,7 +27,7 @@ class TestNGTestMethodDetecter extends MethodVisitor {
     private final Set<String> testMethodAnnotations = new HashSet<String>();
 
     public TestNGTestMethodDetecter(TestNGTestClassDetecter testClassDetecter) {
-        super(Opcodes.ASM4);
+        super(Opcodes.ASM5);
         this.testClassDetecter = testClassDetecter;
         testMethodAnnotations.add("Lorg/testng/annotations/Test;");
         testMethodAnnotations.add("Lorg/testng/annotations/BeforeSuite;");
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
index 8d549a2..839a68a 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
@@ -16,51 +16,80 @@
 
 package org.gradle.api.internal.tasks.testing.testng;
 
-import com.google.common.collect.Maps;
 import org.gradle.api.internal.tasks.testing.*;
 import org.gradle.api.tasks.testing.TestResult;
+import org.gradle.internal.TimeProvider;
 import org.gradle.internal.id.IdGenerator;
-import org.testng.ITestContext;
-import org.testng.ITestListener;
-import org.testng.ITestNGMethod;
-import org.testng.ITestResult;
+import org.testng.*;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
-import java.util.concurrent.ConcurrentMap;
+import java.util.Set;
 
-public class TestNGTestResultProcessorAdapter implements ITestListener, TestNGConfigurationListener {
+public class TestNGTestResultProcessorAdapter implements ISuiteListener, ITestListener, TestNGConfigurationListener {
     private final TestResultProcessor resultProcessor;
     private final IdGenerator<?> idGenerator;
+    private final TimeProvider timeProvider;
     private final Object lock = new Object();
-    private Map<String, Object> suites = new HashMap<String, Object>();
-    private Map<ITestResult, Object> tests = new HashMap<ITestResult, Object>();
-    private Map<ITestNGMethod, Object> testMethodToSuiteMapping = new HashMap<ITestNGMethod, Object>();
-    private ConcurrentMap<ITestResult, Boolean> failedConfigurations = Maps.newConcurrentMap();
+    private final Map<ITestContext, Object> testId = new HashMap<ITestContext, Object>();
+    private final Map<ISuite, Object> suiteId = new HashMap<ISuite, Object>();
+    private final Map<ITestResult, Object> testMethodId = new HashMap<ITestResult, Object>();
+    private final Map<ITestNGMethod, Object> testMethodParentId = new HashMap<ITestNGMethod, Object>();
+    private final Set<ITestResult> failedConfigurations = new HashSet<ITestResult>();
 
-    public TestNGTestResultProcessorAdapter(TestResultProcessor resultProcessor, IdGenerator<?> idGenerator) {
+    public TestNGTestResultProcessorAdapter(TestResultProcessor resultProcessor, IdGenerator<?> idGenerator, TimeProvider timeProvider) {
         this.resultProcessor = resultProcessor;
         this.idGenerator = idGenerator;
+        this.timeProvider = timeProvider;
+    }
+
+    public void onStart(ISuite suite) {
+        TestDescriptorInternal testInternal;
+        synchronized (lock) {
+            if (suiteId.containsKey(suite)) {
+                // Can get duplicate start events
+                return;
+            }
+            testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), suite.getName());
+            suiteId.put(suite, testInternal.getId());
+        }
+        resultProcessor.started(testInternal, new TestStartEvent(timeProvider.getCurrentTime()));
+    }
+
+    public void onFinish(ISuite suite) {
+        Object id;
+        synchronized (lock) {
+            id = suiteId.remove(suite);
+            if (id == null) {
+                // Can get duplicate finish events
+                return;
+            }
+        }
+
+        resultProcessor.completed(id, new TestCompleteEvent(timeProvider.getCurrentTime()));
     }
 
     public void onStart(ITestContext iTestContext) {
         TestDescriptorInternal testInternal;
+        Object parentId;
         synchronized (lock) {
             testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), iTestContext.getName());
-            suites.put(testInternal.getName(), testInternal.getId());
+            parentId = suiteId.get(iTestContext.getSuite());
+            testId.put(iTestContext, testInternal.getId());
             for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
-                testMethodToSuiteMapping.put(method, testInternal.getId());
+                testMethodParentId.put(method, testInternal.getId());
             }
         }
-        resultProcessor.started(testInternal, new TestStartEvent(iTestContext.getStartDate().getTime()));
+        resultProcessor.started(testInternal, new TestStartEvent(iTestContext.getStartDate().getTime(), parentId));
     }
 
     public void onFinish(ITestContext iTestContext) {
         Object id;
         synchronized (lock) {
-            id = suites.remove(iTestContext.getName());
+            id = testId.remove(iTestContext);
             for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
-                testMethodToSuiteMapping.remove(method);
+                testMethodParentId.remove(method);
             }
         }
         resultProcessor.completed(id, new TestCompleteEvent(iTestContext.getEndDate().getTime()));
@@ -73,12 +102,12 @@ public class TestNGTestResultProcessorAdapter implements ITestListener, TestNGCo
             String name = calculateTestCaseName(iTestResult);
 
             testInternal = new DefaultTestMethodDescriptor(idGenerator.generateId(), iTestResult.getTestClass().getName(), name);
-            Object oldTestId = tests.put(iTestResult, testInternal.getId());
+            Object oldTestId = testMethodId.put(iTestResult, testInternal.getId());
             assert oldTestId == null : "Apparently some other test has started but it hasn't finished. "
                     + "Expect the resultProcessor to break. "
                     + "Don't expect to see this assertion stack trace due to the current architecture";
 
-            parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
+            parentId = testMethodParentId.get(iTestResult.getMethod());
             assert parentId != null;
         }
         resultProcessor.started(testInternal, new TestStartEvent(iTestResult.getStartMillis(), parentId));
@@ -142,11 +171,11 @@ public class TestNGTestResultProcessorAdapter implements ITestListener, TestNGCo
         Object testId;
         TestStartEvent startEvent = null;
         synchronized (lock) {
-            testId = tests.remove(iTestResult);
+            testId = testMethodId.remove(iTestResult);
             if (testId == null) {
                 // This can happen when a method fails which this method depends on 
                 testId = idGenerator.generateId();
-                Object parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
+                Object parentId = testMethodParentId.get(iTestResult.getMethod());
                 startEvent = new TestStartEvent(iTestResult.getStartMillis(), parentId);
             }
         }
@@ -167,9 +196,11 @@ public class TestNGTestResultProcessorAdapter implements ITestListener, TestNGCo
     }
 
     public void onConfigurationFailure(ITestResult testResult) {
-        if (failedConfigurations.put(testResult, true) != null) {
-            // workaround for bug in TestNG 6.2 (apparently fixed in some 6.3.x): listener is notified twice per event
-            return;
+        synchronized (lock) {
+            if (!failedConfigurations.add(testResult)) {
+                // workaround for bug in TestNG 6.2 (apparently fixed in some 6.3.x): listener is notified twice per event
+                return;
+            }
         }
         // Synthesise a test for the broken configuration method
         TestDescriptorInternal test = new DefaultTestMethodDescriptor(idGenerator.generateId(),
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
index 6fc5f78..662127e 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
@@ -20,9 +20,9 @@ import org.gradle.api.internal.tasks.testing.*;
 import org.gradle.api.tasks.testing.TestOutputEvent;
 import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.internal.id.CompositeIdGenerator;
+import org.gradle.internal.serialize.*;
 import org.gradle.messaging.remote.internal.Message;
-import org.gradle.messaging.serialize.*;
-import org.gradle.messaging.serialize.kryo.StatefulSerializer;
+import org.gradle.internal.serialize.kryo.StatefulSerializer;
 
 public class TestEventSerializer implements StatefulSerializer<Object[]> {
     private final Serializer<Object> paramSerializer;
@@ -67,25 +67,6 @@ public class TestEventSerializer implements StatefulSerializer<Object[]> {
         };
     }
 
-    private static class EnumSerializer<T extends Enum> implements Serializer<T> {
-        private final Class<T> type;
-
-        private EnumSerializer(Class<T> type) {
-            this.type = type;
-            if (type.getEnumConstants().length > Byte.MAX_VALUE) {
-                throw new IllegalArgumentException(String.format("Too many constants for enum %s", type.getName()));
-            }
-        }
-
-        public T read(Decoder decoder) throws Exception {
-            return type.getEnumConstants()[decoder.readByte()];
-        }
-
-        public void write(Encoder encoder, T value) throws Exception {
-            encoder.writeByte((byte) value.ordinal());
-        }
-    }
-
     private static class NullableSerializer<T> implements Serializer<T> {
         private final Serializer<T> serializer;
 
@@ -155,7 +136,7 @@ public class TestEventSerializer implements StatefulSerializer<Object[]> {
     }
 
     private static class TestCompleteEventSerializer implements Serializer<TestCompleteEvent> {
-        private final Serializer<TestResult.ResultType> typeSerializer = new NullableSerializer<TestResult.ResultType>(new EnumSerializer<TestResult.ResultType>(TestResult.ResultType.class));
+        private final Serializer<TestResult.ResultType> typeSerializer = new NullableSerializer<TestResult.ResultType>(new BaseSerializerFactory().getSerializerFor(TestResult.ResultType.class));
 
         public TestCompleteEvent read(Decoder decoder) throws Exception {
             long endTime = decoder.readLong();
@@ -170,7 +151,7 @@ public class TestEventSerializer implements StatefulSerializer<Object[]> {
     }
 
     private static class DefaultTestOutputEventSerializer implements Serializer<DefaultTestOutputEvent> {
-        private final Serializer<TestOutputEvent.Destination> destinationSerializer = new EnumSerializer<TestOutputEvent.Destination>(TestOutputEvent.Destination.class);
+        private final Serializer<TestOutputEvent.Destination> destinationSerializer = new BaseSerializerFactory().getSerializerFor(TestOutputEvent.Destination.class);
         
         public DefaultTestOutputEvent read(Decoder decoder) throws Exception {
             TestOutputEvent.Destination destination = destinationSerializer.read(decoder);
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestWorker.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestWorker.java
index c7b2e72..b420760 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestWorker.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/TestWorker.java
@@ -21,6 +21,7 @@ import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
+import org.gradle.internal.TimeProvider;
 import org.gradle.internal.TrueTimeProvider;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.DefaultExecutorFactory;
@@ -30,7 +31,7 @@ import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.id.LongIdGenerator;
 import org.gradle.internal.service.DefaultServiceRegistry;
 import org.gradle.internal.service.ServiceRegistry;
-import org.gradle.listener.ContextClassLoaderProxy;
+import org.gradle.messaging.dispatch.ContextClassLoaderProxy;
 import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.messaging.actor.internal.DefaultActorFactory;
 import org.gradle.messaging.remote.ObjectConnection;
@@ -82,7 +83,7 @@ public class TestWorker implements Action<WorkerProcessContext>, RemoteTestClass
         IdGenerator<Object> idGenerator = testServices.get(IdGenerator.class);
 
         targetProcessor = new WorkerTestClassProcessor(targetProcessor, idGenerator.generateId(),
-                workerProcessContext.getDisplayName(), new TrueTimeProvider());
+                workerProcessContext.getDisplayName(), testServices.get(TimeProvider.class));
         ContextClassLoaderProxy<TestClassProcessor> proxy = new ContextClassLoaderProxy<TestClassProcessor>(
                 TestClassProcessor.class, targetProcessor, workerProcessContext.getApplicationClassLoader());
         processor = proxy.getSource();
@@ -125,6 +126,10 @@ public class TestWorker implements Action<WorkerProcessContext>, RemoteTestClass
             this.workerProcessContext = workerProcessContext;
         }
 
+        protected TimeProvider createTimeProvider() {
+            return new TrueTimeProvider();
+        }
+
         protected IdGenerator<Object> createIdGenerator() {
             return new CompositeIdGenerator(workerProcessContext.getWorkerId(), new LongIdGenerator());
         }
@@ -133,8 +138,8 @@ public class TestWorker implements Action<WorkerProcessContext>, RemoteTestClass
             return new DefaultExecutorFactory();
         }
 
-        protected ActorFactory createActorFactory() {
-            return new DefaultActorFactory(get(ExecutorFactory.class));
+        protected ActorFactory createActorFactory(ExecutorFactory executorFactory) {
+            return new DefaultActorFactory(executorFactory);
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/WorkerTestClassProcessor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/WorkerTestClassProcessor.java
index 0392e33..7e2e70e 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/WorkerTestClassProcessor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/WorkerTestClassProcessor.java
@@ -33,7 +33,7 @@ public class WorkerTestClassProcessor extends SuiteTestClassProcessor {
 
         @Override
         public String toString() {
-            return String.format("process '%s'", getName());
+            return getName();
         }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java b/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java
deleted file mode 100644
index 09002fb..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.java.archives;
-
-import groovy.lang.Closure;
-
-import java.io.Writer;
-import java.util.Map;
-
-/**
- * Represents the manifest file of a JAR file.
- */
-public interface Manifest {
-    /**
-     * Returns the main attributes of the manifest.
-     */
-    Attributes getAttributes();
-
-    /**
-     * Returns the sections of the manifest (excluding the main section).
-     *
-     * @return A map with the sections, where the key represents the section name and value the section attributes.
-     */
-    Map<String, Attributes> getSections();
-
-    /**
-     * Adds content to the main attributes of the manifest.
-     *
-     * @param attributes The values to add to the main attributes. The values can be any object. For evaluating the value objects
-     * their {@link Object#toString()} method is used. This is done lazily either before writing or when {@link #getEffectiveManifest()}
-     * is called. 
-     *
-     * @return this
-     * @throws ManifestException If a key is invalid according to the manifest spec or if a key or value is null.
-     */
-    Manifest attributes(Map<String, ?> attributes) throws ManifestException;
-
-    /**
-     * Adds content to the given section of the manifest.
-     * 
-     * @param attributes The values to add to the section. The values can be any object. For evaluating the value objects
-     * their {@link Object#toString()} method is used. This is done lazily either before writing or when {@link #getEffectiveManifest()}
-     * is called.
-     * @param sectionName The name of the section
-     *
-     * @return this
-     * @throws ManifestException If a key is invalid according to the manifest spec or if a key or value is null.
-     */
-    Manifest attributes(Map<String, ?> attributes, String sectionName) throws ManifestException;
-
-    /**
-     * Returns a new manifest instance where all the attribute values are expanded (e.g. their toString method is called).
-     * The returned manifest also contains all the attributes of the to be merged manifests specified in {@link #from(Object...)}.
-     */
-    Manifest getEffectiveManifest();
-
-    /**
-     * Writes the manifest into a writer.
-     *
-     * @param writer The writer to write the manifest to
-     * @return this
-     */
-    Manifest writeTo(Writer writer);
-
-    /**
-     * Writes the manifest into a file. The path's are resolved as defined by {@link org.gradle.api.Project#files(Object...)}
-     *
-     * @param path The path of the file to write the manifest into.
-     * @return this
-     */
-    Manifest writeTo(Object path);
-
-    /**
-     * Specifies other manifests to be merged into this manifest. A merge path can either be another instance of
-     * {@link org.gradle.api.java.archives.Manifest} or a file path as interpreted by {@link org.gradle.api.Project#files(Object...)}.
-     *
-     * The merge is not happening instantaneously. It happens either before writing or when {@link #getEffectiveManifest()}
-     * is called.
-     *
-     * @param mergePath
-     * @return this
-     */
-    Manifest from(Object... mergePath);
-
-    /**
-     * Specifies other manifests to be merged into this manifest. A merge path is interpreted as described in
-     * {@link #from(Object...)}.
-     *
-     * The merge is not happening instantaneously. It happens either before writing or when {@link #getEffectiveManifest()}
-     * is called.
-     *
-     * The closure configures the underlying {@link org.gradle.api.java.archives.ManifestMergeSpec}.
-     *
-     * @param mergePath
-     * @param closure
-     * @return this
-     */
-    Manifest from(Object mergePath, Closure closure);
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java
deleted file mode 100644
index 8f04131..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.java.archives;
-
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-
-/**
- * Specifies how the entries of multiple manifests should be merged together.
- */
-public interface ManifestMergeSpec {
-    /**
-     * Adds a merge path to a manifest that should be merged into the base manifest. A merge path can be either another
-     * {@link org.gradle.api.java.archives.Manifest} or a path that is evaluated as per
-     * {@link org.gradle.api.Project#files(Object...)} . If multiple merge paths are specified, the manifest are merged
-     * in the order in which they are added.
-     * 
-     * @param mergePaths The paths of manifests to be merged
-     * @return this
-     */
-    ManifestMergeSpec from(Object... mergePaths);
-
-    /**
-     * Adds an action to be applied to each key-value tuple in a merge operation. If multiple merge paths are specified,
-     * the action is called for each key-value tuple of each merge operation. The given action is called with a
-     * {@link org.gradle.api.java.archives.ManifestMergeDetails} as its parameter. Actions are executed
-     * in the order added.
-     *
-     * @param mergeAction A merge action to be executed.
-     * @return this
-     */
-    ManifestMergeSpec eachEntry(Action<? super ManifestMergeDetails> mergeAction);
-
-    /**
-     * Adds an action to be applied to each key-value tuple in a merge operation. If multiple merge paths are specified,
-     * the action is called for each key-value tuple of each merge operation. The given closure is called with a
-     * {@link org.gradle.api.java.archives.ManifestMergeDetails} as its parameter. Actions are executed
-     * in the order added.
-     *
-     * @param mergeAction The action to execute.
-     * @return this
-     */
-    ManifestMergeSpec eachEntry(Closure mergeAction);
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultAttributes.java b/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultAttributes.java
deleted file mode 100644
index 3c9899f..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultAttributes.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.java.archives.internal;
-
-import org.gradle.api.java.archives.Attributes;
-import org.gradle.api.java.archives.ManifestException;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class DefaultAttributes implements Attributes {
-    protected Map<String, Object> attributes = new LinkedHashMap<String, Object>();
-
-    public int size() {
-        return attributes.size();
-    }
-
-    public boolean isEmpty() {
-        return attributes.isEmpty();
-    }
-
-    public boolean containsKey(Object key) {
-        return attributes.containsKey(key);
-    }
-
-    public boolean containsValue(Object value) {
-        return attributes.containsValue(value);
-    }
-
-    public Object get(Object key) {
-        return attributes.get(key);
-    }
-
-    public Object put(String key, Object value) {
-        if (key == null) {
-            throw new ManifestException("The key of a manifest attribute must not be null.");
-        }
-        if (value == null) {
-            throw new ManifestException("The value of a manifest attribute must not be null.");
-        }
-        try {
-            new java.util.jar.Attributes.Name(key);
-        } catch(IllegalArgumentException e) {
-            throw new ManifestException(String.format("The Key=%s violates the Manifest spec!", key));   
-        }
-        return attributes.put(key, value);
-    }
-
-    public Object remove(Object key) {
-        return attributes.remove(key);
-    }
-
-    public void putAll(Map<? extends String, ? extends Object> m) {
-        for (Entry<? extends String, ? extends Object> entry : m.entrySet()) {
-            put(entry.getKey(), entry.getValue());
-        }
-    }
-
-    public void clear() {
-        attributes.clear();
-    }
-
-    public Set<String> keySet() {
-        return attributes.keySet();
-    }
-
-    public Collection<Object> values() {
-        return attributes.values();
-    }
-
-    public Set<Entry<String, Object>> entrySet() {
-        return attributes.entrySet();
-    }
-
-    public boolean equals(Object o) {
-        return attributes.equals(o);
-    }
-
-    public int hashCode() {
-        return attributes.hashCode();
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java b/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java
deleted file mode 100644
index ad21bff..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java
+++ /dev/null
@@ -1,241 +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.java.archives.internal;
-
-import groovy.lang.Closure;
-import org.apache.tools.ant.taskdefs.Manifest;
-import org.apache.tools.ant.taskdefs.Manifest.Attribute;
-import org.apache.tools.ant.taskdefs.Manifest.Section;
-import org.apache.tools.ant.taskdefs.ManifestException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.internal.ErroringAction;
-import org.gradle.internal.IoActions;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.java.archives.Attributes;
-import org.gradle.api.java.archives.ManifestMergeSpec;
-import org.gradle.util.ConfigureUtil;
-
-import java.io.*;
-import java.util.*;
-
-public class DefaultManifest implements org.gradle.api.java.archives.Manifest {
-    private List<ManifestMergeSpec> manifestMergeSpecs = new ArrayList<ManifestMergeSpec>();
-
-    private DefaultAttributes attributes = new DefaultAttributes();
-
-    private Map<String, Attributes> sections = new LinkedHashMap<String, Attributes>();
-
-    private FileResolver fileResolver;
-
-    public DefaultManifest(FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-        init();
-    }
-
-    public DefaultManifest(Object manifestPath, FileResolver fileResolver) {
-        this.fileResolver = fileResolver;
-        read(manifestPath);
-    }
-
-    private void init() {
-        getAttributes().put("Manifest-Version", "1.0");
-    }
-
-    public DefaultManifest mainAttributes(Map<String, ?> attributes) {
-        return attributes(attributes);
-    }
-
-    public DefaultManifest attributes(Map<String, ?> attributes) {
-        getAttributes().putAll(attributes);
-        return this;
-    }
-
-    public DefaultManifest attributes(Map<String, ?> attributes, String sectionName) {
-        if (!sections.containsKey(sectionName)) {
-            sections.put(sectionName, new DefaultAttributes());
-        }
-        sections.get(sectionName).putAll(attributes);
-        return this;
-    }
-
-    public Attributes getAttributes() {
-        return attributes;
-    }
-
-    public Map<String, Attributes> getSections() {
-        return sections;
-    }
-
-    public DefaultManifest clear() {
-        attributes.clear();
-        sections.clear();
-        manifestMergeSpecs.clear();
-        init();
-        return this;
-    }
-
-    private Manifest generateAntManifest() {
-        Manifest antManifest = new Manifest();
-        addAttributesToAnt(antManifest);
-        addSectionAttributesToAnt(antManifest);
-        return antManifest;
-    }
-
-    private void addAttributesToAnt(Manifest antManifest) {
-        for (Map.Entry<String, Object> entry : attributes.entrySet()) {
-            try {
-                antManifest.addConfiguredAttribute(new Attribute(entry.getKey().toString(), entry.getValue().toString()));
-            } catch (ManifestException e) {
-                throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
-            }
-        }
-    }
-
-    private void addSectionAttributesToAnt(Manifest antManifest) {
-        for (Map.Entry<String, Attributes> entry : sections.entrySet()) {
-            Section section = new Section();
-            section.setName(entry.getKey());
-            try {
-                antManifest.addConfiguredSection(section);
-                for (Map.Entry<String, Object> attributeEntry : entry.getValue().entrySet()) {
-                    section.addConfiguredAttribute(new Attribute(attributeEntry.getKey().toString(), attributeEntry.getValue().toString()));
-                }
-            } catch (ManifestException e) {
-                throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
-            }
-        }
-    }
-
-    public DefaultManifest from(Object... mergePaths) {
-        from(mergePaths, null);
-        return this;
-    }
-
-    public DefaultManifest from(Object mergePaths, Closure closure) {
-        DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec();
-        mergeSpec.from(mergePaths);
-        manifestMergeSpecs.add(mergeSpec);
-        ConfigureUtil.configure(closure, mergeSpec);
-        return this;
-    }
-
-    public DefaultManifest getEffectiveManifest() {
-        return getEffectiveManifestInternal(this);
-    }
-
-    protected DefaultManifest getEffectiveManifestInternal(DefaultManifest baseManifest) {
-        DefaultManifest resultManifest = baseManifest;
-        for (ManifestMergeSpec manifestMergeSpec : manifestMergeSpecs) {
-            resultManifest = ((DefaultManifestMergeSpec) manifestMergeSpec).merge(resultManifest, fileResolver);
-        }
-        return resultManifest;
-    }
-
-    public DefaultManifest writeTo(Writer writer) {
-        PrintWriter printWriter = new PrintWriter(writer);
-        try {
-            getEffectiveManifest().generateAntManifest().write(printWriter);
-            printWriter.flush();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-        return this;
-    }
-
-    public org.gradle.api.java.archives.Manifest writeTo(Object path) {
-        IoActions.writeTextFile(fileResolver.resolve(path), new ErroringAction<Writer>() {
-            @Override
-            protected void doExecute(Writer writer) throws Exception {
-                writeTo(writer);
-            }
-        });
-        return this;
-    }
-
-    public List<ManifestMergeSpec> getMergeSpecs() {
-        return manifestMergeSpecs;
-    }
-
-    public boolean isEqualsTo(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || !(o instanceof DefaultManifest)) {
-            return false;
-        }
-
-        DefaultManifest effectiveThis = getEffectiveManifest();
-        DefaultManifest effectiveThat = ((DefaultManifest) o).getEffectiveManifest();
-
-        if (!effectiveThis.attributes.equals(effectiveThat.attributes)) {
-            return false;
-        }
-        if (!effectiveThis.sections.equals(effectiveThat.sections)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private void read(Object manifestPath) {
-        File manifestFile = fileResolver.resolve(manifestPath);
-        try {
-            FileReader reader = new FileReader(manifestFile);
-            Manifest antManifest;
-            try {
-                antManifest = new Manifest(reader);
-            } finally {
-                reader.close();
-            }
-            addAntManifestToAttributes(antManifest);
-            addAntManifestToSections(antManifest);
-        } catch (ManifestException e) {
-            throw new org.gradle.api.java.archives.ManifestException(e.getMessage(), e);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private void addAntManifestToAttributes(Manifest antManifest) {
-        Enumeration attributeKeys = antManifest.getMainSection().getAttributeKeys();
-        while (attributeKeys.hasMoreElements()) {
-            String key = (String) attributeKeys.nextElement();
-            String attributeKey = antManifest.getMainSection().getAttribute(key).getName();
-            attributes.put(attributeKey, antManifest.getMainSection().getAttributeValue(key));
-        }
-        attributes.put("Manifest-Version", antManifest.getManifestVersion());
-    }
-
-    private void addAntManifestToSections(Manifest antManifest) {
-        Enumeration sectionNames = antManifest.getSectionNames();
-        while (sectionNames.hasMoreElements()) {
-            String sectionName = (String) sectionNames.nextElement();
-            addAntManifestToSection(antManifest, sectionName);
-        }
-    }
-
-    private void addAntManifestToSection(Manifest antManifest, String sectionName) {
-        DefaultAttributes attributes = new DefaultAttributes();
-        sections.put(sectionName, attributes);
-        Enumeration attributeKeys = antManifest.getSection(sectionName).getAttributeKeys();
-        while (attributeKeys.hasMoreElements()) {
-            String key = (String) attributeKeys.nextElement();
-            String attributeKey = antManifest.getSection(sectionName).getAttribute(key).getName();
-            attributes.put(attributeKey, antManifest.getSection(sectionName).getAttributeValue(key));
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java b/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java
deleted file mode 100644
index 243581c..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifestMergeSpec.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.java.archives.internal;
-
-import com.google.common.collect.Sets;
-import groovy.lang.Closure;
-import org.gradle.api.Action;
-import org.gradle.api.internal.ClosureBackedAction;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.java.archives.Attributes;
-import org.gradle.api.java.archives.Manifest;
-import org.gradle.api.java.archives.ManifestMergeDetails;
-import org.gradle.api.java.archives.ManifestMergeSpec;
-import org.gradle.util.GUtil;
-import org.gradle.util.WrapUtil;
-
-import java.util.*;
-
-public class DefaultManifestMergeSpec implements ManifestMergeSpec {
-    List<Object> mergePaths = new ArrayList<Object>();
-    private final List<Action<? super ManifestMergeDetails>> actions = new ArrayList<Action<? super ManifestMergeDetails>>();
-
-    public ManifestMergeSpec from(Object... mergePaths) {
-        GUtil.flatten(mergePaths, this.mergePaths);
-        return this;
-    }
-
-    public ManifestMergeSpec eachEntry(Action<? super ManifestMergeDetails> mergeAction) {
-        actions.add(mergeAction);
-        return this;
-    }
-
-    public ManifestMergeSpec eachEntry(Closure mergeAction) {
-        return eachEntry(new ClosureBackedAction<ManifestMergeDetails>(mergeAction));
-    }
-
-    public DefaultManifest merge(Manifest baseManifest, FileResolver fileResolver) {
-        DefaultManifest mergedManifest = new DefaultManifest(fileResolver);
-        mergedManifest.getAttributes().putAll(baseManifest.getAttributes());
-        mergedManifest.getSections().putAll(baseManifest.getSections());
-        for (Object mergePath : mergePaths) {
-            DefaultManifest manifestToMerge = createManifest(mergePath, fileResolver);
-            mergedManifest = mergeManifest(mergedManifest, manifestToMerge, fileResolver);
-        }
-        return mergedManifest;
-    }
-
-    private DefaultManifest mergeManifest(DefaultManifest baseManifest, DefaultManifest toMergeManifest, FileResolver fileResolver) {
-        DefaultManifest mergedManifest = new DefaultManifest(fileResolver);
-        mergeSection(null, mergedManifest, baseManifest.getAttributes(), toMergeManifest.getAttributes());
-        Set<String> allSections = Sets.union(baseManifest.getSections().keySet(), toMergeManifest.getSections().keySet());
-        for (String section : allSections) {
-            mergeSection(section, mergedManifest,
-                    GUtil.elvis(baseManifest.getSections().get(section), new DefaultAttributes()),
-                    GUtil.elvis(toMergeManifest.getSections().get(section), new DefaultAttributes()));
-        }
-        return mergedManifest;
-    }
-
-    private void mergeSection(String section, DefaultManifest mergedManifest, Attributes baseAttributes, Attributes mergeAttributes) {
-        Map<String, Object> mergeOnlyAttributes = new LinkedHashMap<String, Object>(mergeAttributes);
-        Set<DefaultManifestMergeDetails> mergeDetailsSet = new LinkedHashSet<DefaultManifestMergeDetails>();
-
-        for (Map.Entry<String, Object> baseEntry : baseAttributes.entrySet()) {
-            Object mergeValue = mergeAttributes.get(baseEntry.getKey());
-            mergeDetailsSet.add(getMergeDetails(section, baseEntry.getKey(), baseEntry.getValue(), mergeValue));
-            mergeOnlyAttributes.remove(baseEntry.getKey());
-        }
-        for (Map.Entry<String, Object> mergeEntry : mergeOnlyAttributes.entrySet()) {
-            mergeDetailsSet.add(getMergeDetails(section, mergeEntry.getKey(), null, mergeEntry.getValue()));
-        }
-        
-        for (DefaultManifestMergeDetails mergeDetails : mergeDetailsSet) {
-            for (Action<? super ManifestMergeDetails> action : actions) {
-                action.execute(mergeDetails);
-            }
-            addMergeDetailToManifest(section, mergedManifest, mergeDetails);
-        }
-    }
-
-    private DefaultManifestMergeDetails getMergeDetails(String section, String key, Object baseValue, Object mergeValue) {
-        String value = null;
-        String baseValueString = baseValue != null ? baseValue.toString() : null;
-        String mergeValueString = mergeValue != null ? mergeValue.toString() : null;
-        value = mergeValueString == null ? baseValueString : mergeValueString; 
-        return new DefaultManifestMergeDetails(section, key, baseValueString, mergeValueString, value);
-    }
-
-    private void addMergeDetailToManifest(String section, DefaultManifest mergedManifest, DefaultManifestMergeDetails mergeDetails) {
-        if (!mergeDetails.isExcluded()) {
-            if (section == null) {
-                mergedManifest.attributes(WrapUtil.toMap(mergeDetails.getKey(), mergeDetails.getValue()));
-            } else {
-                mergedManifest.attributes(WrapUtil.toMap(mergeDetails.getKey(), mergeDetails.getValue()), section);
-            }
-        }
-    }
-
-    private DefaultManifest createManifest(Object mergePath, FileResolver fileResolver) {
-        if (mergePath instanceof DefaultManifest) {
-            return ((DefaultManifest) mergePath).getEffectiveManifest();
-        }
-        return new DefaultManifest(mergePath, fileResolver);
-    }
-
-    public List<Object> getMergePaths() {
-        return mergePaths;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy
index 4ab5f9f..b9e7f71 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/ApplicationPlugin.groovy
@@ -18,16 +18,18 @@ package org.gradle.api.plugins
 import org.gradle.api.GradleException
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.distribution.Distribution
+import org.gradle.api.distribution.plugins.DistributionPlugin
 import org.gradle.api.file.CopySpec
 import org.gradle.api.tasks.JavaExec
 import org.gradle.api.tasks.Sync
 import org.gradle.api.tasks.application.CreateStartScripts
-import org.gradle.api.tasks.bundling.AbstractArchiveTask
-import org.gradle.api.tasks.bundling.Tar
-import org.gradle.api.tasks.bundling.Zip
+import org.gradle.util.DeprecationLogger
 
 /**
  * <p>A {@link Plugin} which runs a project as a Java Application.</p>
+ *
  */
 class ApplicationPlugin implements Plugin<Project> {
     static final String APPLICATION_PLUGIN_NAME = "application"
@@ -44,17 +46,36 @@ class ApplicationPlugin implements Plugin<Project> {
 
     void apply(final Project project) {
         this.project = project
-        project.plugins.apply(JavaPlugin)
+        project.pluginManager.apply(JavaPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         addPluginConvention()
         addRunTask()
         addCreateScriptsTask()
 
-        configureDistSpec(pluginConvention.applicationDistribution)
+        def distribution = project.distributions[DistributionPlugin.MAIN_DISTRIBUTION_NAME]
+        distribution.conventionMapping.baseName = { pluginConvention.applicationName }
+        configureDistSpec(distribution.contents)
+        Task installAppTask = addInstallAppTask(distribution)
+        configureInstallTasks(installAppTask, project.tasks[DistributionPlugin.TASK_INSTALL_NAME])
+    }
 
-        addInstallTask()
-        addDistZipTask()
-        addDistTarTask()
+    void configureInstallTasks(Task... installTasks) {
+        installTasks.each { installTask ->
+            installTask.doFirst {
+                if (destinationDir.directory) {
+                    if (!new File(destinationDir, 'lib').directory || !new File(destinationDir, 'bin').directory) {
+                        throw new GradleException("The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${pluginConvention.applicationName}'.\n" +
+                                "If you really want to install to this directory, delete it and run the install task again.\n" +
+                                "Alternatively, choose a different installation directory."
+                        )
+                    }
+                }
+            }
+            installTask.doLast {
+                project.ant.chmod(file: "${destinationDir.absolutePath}/bin/${pluginConvention.applicationName}", perm: 'ugo+x')
+            }
+        }
     }
 
     private void addPluginConvention() {
@@ -83,44 +104,16 @@ class ApplicationPlugin implements Plugin<Project> {
         startScripts.conventionMapping.defaultJvmOpts = { pluginConvention.applicationDefaultJvmArgs }
     }
 
-    private void addInstallTask() {
+    private Task addInstallAppTask(Distribution distribution) {
         def installTask = project.tasks.create(TASK_INSTALL_NAME, Sync)
         installTask.description = "Installs the project as a JVM application along with libs and OS specific scripts."
         installTask.group = APPLICATION_GROUP
-        installTask.with pluginConvention.applicationDistribution
+        installTask.with distribution.contents
         installTask.into { project.file("${project.buildDir}/install/${pluginConvention.applicationName}") }
         installTask.doFirst {
-            if (destinationDir.directory) {
-                if (!new File(destinationDir, 'lib').directory || !new File(destinationDir, 'bin').directory) {
-                    throw new GradleException("The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${pluginConvention.applicationName}'.\n" +
-                            "If you really want to install to this directory, delete it and run the install task again.\n" +
-                            "Alternatively, choose a different installation directory."
-                    )
-                }
-            }
-        }
-        installTask.doLast {
-            project.ant.chmod(file: "${destinationDir.absolutePath}/bin/${pluginConvention.applicationName}", perm: 'ugo+x')
-        }
-    }
-
-    private void addDistZipTask() {
-        addArchiveTask(TASK_DIST_ZIP_NAME, Zip)
-    }
-
-	private void addDistTarTask() {
-        addArchiveTask(TASK_DIST_TAR_NAME, Tar)
-	}
-
-    private <T extends AbstractArchiveTask> void addArchiveTask(String name, Class<T> type) {
-        def archiveTask = project.tasks.create(name, type)
-        archiveTask.description = "Bundles the project as a JVM application with libs and OS specific scripts."
-        archiveTask.group = APPLICATION_GROUP
-        archiveTask.conventionMapping.baseName = { pluginConvention.applicationName }
-        def baseDir = { archiveTask.archiveName - ".${archiveTask.extension}" }
-        archiveTask.into(baseDir) {
-            with(pluginConvention.applicationDistribution)
+            DeprecationLogger.nagUserOfReplacedTask(ApplicationPlugin.TASK_INSTALL_NAME, DistributionPlugin.TASK_INSTALL_NAME);
         }
+        installTask
     }
 
     private CopySpec configureDistSpec(CopySpec distSpec) {
@@ -139,6 +132,7 @@ class ApplicationPlugin implements Plugin<Project> {
                 fileMode = 0755
             }
         }
+        distSpec.with(pluginConvention.applicationDistribution)
 
         distSpec
     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.java
index 6d492e5..7796fab 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.java
@@ -29,15 +29,18 @@ import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublication;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
 import org.gradle.api.internal.plugins.BuildConfigurationRule;
-import org.gradle.api.internal.plugins.CleanRule;
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
 import org.gradle.api.internal.plugins.UploadRule;
-import org.gradle.api.tasks.Delete;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.tasks.Upload;
 import org.gradle.api.tasks.bundling.AbstractArchiveTask;
-import org.gradle.api.tasks.bundling.Jar;
 import org.gradle.configuration.project.ProjectConfigurationActionContainer;
-import org.gradle.language.base.plugins.LanguageBasePlugin;
+import org.gradle.jvm.tasks.Jar;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.model.internal.core.ActionBackedModelAction;
+import org.gradle.model.internal.core.ModelActionRole;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor;
 
 import javax.inject.Inject;
 import java.io.File;
@@ -47,10 +50,11 @@ import java.util.concurrent.Callable;
  * <p>A  {@link org.gradle.api.Plugin}  which defines a basic project lifecycle and some common convention properties.</p>
  */
 public class BasePlugin implements Plugin<Project> {
-    public static final String CLEAN_TASK_NAME = "clean";
-    public static final String ASSEMBLE_TASK_NAME = "assemble";
+    public static final String CLEAN_TASK_NAME = LifecycleBasePlugin.CLEAN_TASK_NAME;
+    public static final String ASSEMBLE_TASK_NAME = LifecycleBasePlugin.ASSEMBLE_TASK_NAME;
+    public static final String BUILD_GROUP = LifecycleBasePlugin.BUILD_GROUP;
+
     public static final String UPLOAD_ARCHIVES_TASK_NAME = "uploadArchives";
-    public static final String BUILD_GROUP = LanguageBasePlugin.BUILD_GROUP;
     public static final String UPLOAD_GROUP = "upload";
 
     private final ProjectPublicationRegistry publicationRegistry;
@@ -63,6 +67,8 @@ public class BasePlugin implements Plugin<Project> {
     }
 
     public void apply(Project project) {
+        project.getPluginManager().apply(LifecycleBasePlugin.class);
+
         BasePluginConvention convention = new BasePluginConvention(project);
         project.getConvention().getPlugins().put("base", convention);
 
@@ -71,17 +77,7 @@ public class BasePlugin implements Plugin<Project> {
         configureUploadArchivesTask();
         configureArchiveDefaults(project, convention);
         configureConfigurations(project);
-
-        addClean(project);
-        addCleanRule(project);
-        addAssemble(project);
-    }
-
-    private void addAssemble(Project project) {
-        Task assembleTask = project.getTasks().create(ASSEMBLE_TASK_NAME);
-        assembleTask.setDescription("Assembles the outputs of this project.");
-        assembleTask.setGroup(BUILD_GROUP);
-        assembleTask.dependsOn(project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts().getBuildDependencies());
+        configureAssemble((ProjectInternal) project);
     }
 
     private void configureArchiveDefaults(final Project project, final BasePluginConvention pluginConvention) {
@@ -120,21 +116,6 @@ public class BasePlugin implements Plugin<Project> {
         });
     }
 
-    private void addClean(final Project project) {
-        Delete clean = project.getTasks().create(CLEAN_TASK_NAME, Delete.class);
-        clean.setDescription("Deletes the build directory.");
-        clean.setGroup(BUILD_GROUP);
-        clean.delete(new Callable<File>() {
-            public File call() throws Exception {
-                return project.getBuildDir();
-            }
-        });
-    }
-
-    private void addCleanRule(Project project) {
-        project.getTasks().addRule(new CleanRule(project.getTasks()));
-    }
-
     private void configureBuildConfigurationRule(Project project) {
         project.getTasks().addRule(new BuildConfigurationRule(project.getConfigurations(), project.getTasks()));
     }
@@ -147,10 +128,14 @@ public class BasePlugin implements Plugin<Project> {
         configurationActionContainer.add(new Action<Project>() {
             public void execute(Project project) {
                 Upload uploadArchives = project.getTasks().withType(Upload.class).findByName(UPLOAD_ARCHIVES_TASK_NAME);
-                if (uploadArchives == null) { return; }
+                if (uploadArchives == null) {
+                    return;
+                }
 
                 boolean hasIvyRepo = !uploadArchives.getRepositories().withType(IvyArtifactRepository.class).isEmpty();
-                if (!hasIvyRepo) { return; } // Maven repos are handled by MavenPlugin
+                if (!hasIvyRepo) {
+                    return;
+                } // Maven repos are handled by MavenPlugin
 
                 ConfigurationInternal configuration = (ConfigurationInternal) uploadArchives.getConfiguration();
                 ModuleInternal module = configuration.getModule();
@@ -185,4 +170,14 @@ public class BasePlugin implements Plugin<Project> {
             }
         });
     }
+
+    private void configureAssemble(final ProjectInternal project) {
+        // Note, this is implicitly retaining the project instance which is a problem for reuse
+        project.getModelRegistry().configure(ModelActionRole.Mutate, ActionBackedModelAction.of(ModelReference.of("tasks.assemble", Task.class), new SimpleModelRuleDescriptor("BasePlugin#configureAssemble"), new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                task.dependsOn(project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts().getBuildDependencies());
+            }
+        }));
+    }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
index 92cc706..3227df2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyBasePlugin.java
@@ -18,12 +18,10 @@ package org.gradle.api.plugins;
 
 import org.gradle.api.Action;
 import org.gradle.api.Plugin;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.Project;
 import org.gradle.api.file.FileTreeElement;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultGroovySourceSet;
 import org.gradle.api.internal.tasks.DefaultSourceSet;
 import org.gradle.api.reporting.ReportingExtension;
@@ -32,7 +30,6 @@ import org.gradle.api.tasks.GroovyRuntime;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.compile.GroovyCompile;
 import org.gradle.api.tasks.javadoc.Groovydoc;
-import org.gradle.util.DeprecationLogger;
 
 import javax.inject.Inject;
 import java.io.File;
@@ -42,22 +39,12 @@ import java.util.concurrent.Callable;
  * Extends {@link org.gradle.api.plugins.JavaBasePlugin} to provide support for compiling and documenting Groovy
  * source files.
  */
-public class GroovyBasePlugin implements Plugin<ProjectInternal> {
-    /**
-     * The name of the configuration holding the Groovy compiler and tools.
-     *
-     * @deprecated Typically, usages of {@code groovy} can simply be replaced with {@code compile}.
-     * In some cases, it may be necessary to additionally configure the {@code groovyClasspath} property
-     * of {@code GroovyCompile} and {@code Groovydoc} tasks.
-     */
-    @Deprecated
-    public static final String GROOVY_CONFIGURATION_NAME = "groovy";
-
+public class GroovyBasePlugin implements Plugin<Project> {
     public static final String GROOVY_RUNTIME_EXTENSION_NAME = "groovyRuntime";
 
     private final FileResolver fileResolver;
 
-    private ProjectInternal project;
+    private Project project;
     private GroovyRuntime groovyRuntime;
 
     @Inject
@@ -65,11 +52,11 @@ public class GroovyBasePlugin implements Plugin<ProjectInternal> {
         this.fileResolver = fileResolver;
     }
 
-    public void apply(ProjectInternal project) {
+    public void apply(Project project) {
         this.project = project;
-        JavaBasePlugin javaBasePlugin = project.getPlugins().apply(JavaBasePlugin.class);
+        project.getPluginManager().apply(JavaBasePlugin.class);
+        JavaBasePlugin javaBasePlugin = project.getPlugins().getPlugin(JavaBasePlugin.class);
 
-        configureConfigurations(project);
         configureGroovyRuntimeExtension();
         configureCompileDefaults();
         configureSourceSetDefaults(javaBasePlugin);
@@ -77,26 +64,10 @@ public class GroovyBasePlugin implements Plugin<ProjectInternal> {
         configureGroovydoc();
     }
 
-    private void configureConfigurations(ProjectInternal project) {
-        Configuration groovyConfiguration = project.getConfigurations().create(GROOVY_CONFIGURATION_NAME).setVisible(false).
-                setDescription("The Groovy libraries to be used for this Groovy project. (Deprecated)");
-        deprecateGroovyConfiguration(groovyConfiguration);
-    }
-
     private void configureGroovyRuntimeExtension() {
         groovyRuntime = project.getExtensions().create(GROOVY_RUNTIME_EXTENSION_NAME, GroovyRuntime.class, project);
     }
 
-    private void deprecateGroovyConfiguration(Configuration groovyConfiguration) {
-        groovyConfiguration.getDependencies().whenObjectAdded(new Action<Dependency>() {
-            public void execute(Dependency dependency) {
-                DeprecationLogger.nagUserOfDiscontinuedConfiguration(GROOVY_CONFIGURATION_NAME, "Typically, usages of 'groovy' "
-                        + "can simply be replaced with 'compile'. In some cases, it may be necessary to additionally configure "
-                        + "the 'groovyClasspath' property of GroovyCompile and Groovydoc tasks.");
-            }
-        });
-    }
-
     private void configureCompileDefaults() {
         project.getTasks().withType(GroovyCompile.class, new Action<GroovyCompile>() {
             public void execute(final GroovyCompile compile) {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
index 8e90970..2b82953 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
@@ -31,18 +31,11 @@ public class GroovyPlugin implements Plugin<Project> {
     public static final String GROOVYDOC_TASK_NAME = "groovydoc";
 
     public void apply(Project project) {
-        project.getPlugins().apply(GroovyBasePlugin.class);
-        project.getPlugins().apply(JavaPlugin.class);
-        configureConfigurations(project);
+        project.getPluginManager().apply(GroovyBasePlugin.class);
+        project.getPluginManager().apply(JavaPlugin.class);
         configureGroovydoc(project);
     }
 
-    private void configureConfigurations(Project project) {
-        project.getConfigurations().getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME).extendsFrom(
-                project.getConfigurations().getByName(GroovyBasePlugin.GROOVY_CONFIGURATION_NAME)
-        );
-    }
-
     private void configureGroovydoc(final Project project) {
         Groovydoc groovyDoc = project.getTasks().create(GROOVYDOC_TASK_NAME, Groovydoc.class);
         groovyDoc.setDescription("Generates Groovydoc API documentation for the main source code.");
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
index 4dc2d17..9a11387 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
@@ -30,16 +30,18 @@ import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.JavaCompile;
 import org.gradle.api.tasks.javadoc.Javadoc;
-import org.gradle.api.tasks.testing.*;
+import org.gradle.api.tasks.testing.Test;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.BinaryContainer;
+import org.gradle.jvm.ClassDirectoryBinarySpec;
+import org.gradle.jvm.Classpath;
 import org.gradle.language.base.FunctionalSourceSet;
 import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.language.java.internal.DefaultJavaSourceSet;
-import org.gradle.language.jvm.ClassDirectoryBinary;
-import org.gradle.language.jvm.Classpath;
-import org.gradle.language.jvm.ResourceSet;
-import org.gradle.language.jvm.internal.DefaultResourceSet;
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.api.internal.java.DefaultJavaSourceSet;
+import org.gradle.language.jvm.JvmResourceSet;
+import org.gradle.api.internal.java.DefaultJvmResourceSet;
+import org.gradle.platform.base.BinaryContainer;
 import org.gradle.util.WrapUtil;
 
 import javax.inject.Inject;
@@ -49,12 +51,13 @@ import java.util.concurrent.Callable;
 /**
  * <p>A {@link org.gradle.api.Plugin} which compiles and tests Java source, and assembles it into a JAR file.</p>
  */
-public class JavaBasePlugin implements Plugin<Project> {
-    public static final String CHECK_TASK_NAME = "check";
-    public static final String BUILD_TASK_NAME = "build";
+public class JavaBasePlugin implements Plugin<ProjectInternal> {
+    public static final String CHECK_TASK_NAME = LifecycleBasePlugin.CHECK_TASK_NAME;
+
+    public static final String VERIFICATION_GROUP = LifecycleBasePlugin.VERIFICATION_GROUP;
+    public static final String BUILD_TASK_NAME = LifecycleBasePlugin.BUILD_TASK_NAME;
     public static final String BUILD_DEPENDENTS_TASK_NAME = "buildDependents";
     public static final String BUILD_NEEDED_TASK_NAME = "buildNeeded";
-    public static final String VERIFICATION_GROUP = "verification";
     public static final String DOCUMENTATION_GROUP = "documentation";
 
     private final Instantiator instantiator;
@@ -64,12 +67,12 @@ public class JavaBasePlugin implements Plugin<Project> {
         this.instantiator = instantiator;
     }
 
-    public void apply(Project project) {
-        project.getPlugins().apply(BasePlugin.class);
-        project.getPlugins().apply(ReportingBasePlugin.class);
-        project.getPlugins().apply(JavaLanguagePlugin.class);
+    public void apply(ProjectInternal project) {
+        project.getPluginManager().apply(BasePlugin.class);
+        project.getPluginManager().apply(ReportingBasePlugin.class);
+        project.getPluginManager().apply(LegacyJavaComponentPlugin.class);
 
-        JavaPluginConvention javaConvention = new JavaPluginConvention((ProjectInternal) project, instantiator);
+        JavaPluginConvention javaConvention = new JavaPluginConvention(project, instantiator);
         project.getConvention().getPlugins().put("java", javaConvention);
 
         configureCompileDefaults(project, javaConvention);
@@ -77,8 +80,6 @@ public class JavaBasePlugin implements Plugin<Project> {
 
         configureJavaDoc(project, javaConvention);
         configureTest(project, javaConvention);
-        configureCheck(project);
-        configureBuild(project);
         configureBuildNeeded(project);
         configureBuildDependents(project);
     }
@@ -128,15 +129,15 @@ public class JavaBasePlugin implements Plugin<Project> {
                 sourceSet.getResources().srcDir(String.format("src/%s/resources", sourceSet.getName()));
                 sourceSet.compiledBy(sourceSet.getClassesTaskName());
 
-                FunctionalSourceSet functionalSourceSet = projectSourceSet.create(sourceSet.getName());
+                FunctionalSourceSet functionalSourceSet = instantiator.newInstance(DefaultFunctionalSourceSet.class, sourceSet.getName(), instantiator, projectSourceSet);
                 Classpath compileClasspath = new SourceSetCompileClasspath(sourceSet);
-                DefaultJavaSourceSet javaSourceSet = instantiator.newInstance(DefaultJavaSourceSet.class, "java", sourceSet.getJava(), compileClasspath, functionalSourceSet);
+                DefaultJavaSourceSet javaSourceSet = instantiator.newInstance(DefaultJavaSourceSet.class, "java", sourceSet.getName(), sourceSet.getJava(), compileClasspath);
                 functionalSourceSet.add(javaSourceSet);
-                ResourceSet resourceSet = instantiator.newInstance(DefaultResourceSet.class, "resources", sourceSet.getResources(), functionalSourceSet);
+                JvmResourceSet resourceSet = instantiator.newInstance(DefaultJvmResourceSet.class, "resources", sourceSet.getName(), sourceSet.getResources());
                 functionalSourceSet.add(resourceSet);
 
                 BinaryContainer binaryContainer = project.getExtensions().getByType(BinaryContainer.class);
-                ClassDirectoryBinary binary = binaryContainer.create(String.format("%sClasses", sourceSet.getName()), ClassDirectoryBinary.class);
+                ClassDirectoryBinarySpec binary = binaryContainer.create(String.format("%sClasses", sourceSet.getName()), ClassDirectoryBinarySpec.class);
                 ConventionMapping conventionMapping = new DslObject(binary).getConventionMapping();
                 conventionMapping.map("classesDir", new Callable<File>() {
                     public File call() throws Exception {
@@ -219,20 +220,6 @@ public class JavaBasePlugin implements Plugin<Project> {
         });
     }
 
-    private void configureCheck(final Project project) {
-        Task checkTask = project.getTasks().create(CHECK_TASK_NAME);
-        checkTask.setDescription("Runs all checks.");
-        checkTask.setGroup(VERIFICATION_GROUP);
-    }
-
-    private void configureBuild(Project project) {
-        DefaultTask buildTask = project.getTasks().create(BUILD_TASK_NAME, DefaultTask.class);
-        buildTask.setDescription("Assembles and tests this project.");
-        buildTask.setGroup(BasePlugin.BUILD_GROUP);
-        buildTask.dependsOn(BasePlugin.ASSEMBLE_TASK_NAME);
-        buildTask.dependsOn(CHECK_TASK_NAME);
-    }
-
     private void configureBuildNeeded(Project project) {
         DefaultTask buildTask = project.getTasks().create(BUILD_NEEDED_TASK_NAME, DefaultTask.class);
         buildTask.setDescription("Assembles and tests this project and all projects it depends on.");
@@ -268,7 +255,7 @@ public class JavaBasePlugin implements Plugin<Project> {
     private void overwriteDebugIfDebugPropertyIsSet(Test test) {
         String debugProp = getTaskPrefixedProperty(test, "debug");
         if (debugProp != null) {
-            test.doFirst(new Action<Task>() {
+            test.prependParallelSafeAction(new Action<Task>() {
                 public void execute(Task task) {
                     task.getLogger().info("Running tests for remote debugging.");
                 }
@@ -286,7 +273,7 @@ public class JavaBasePlugin implements Plugin<Project> {
             test.getInputs().source(test.getCandidateClassFiles());
             return;
         }
-        test.doFirst(new Action<Task>() {
+        test.prependParallelSafeAction(new Action<Task>() {
             public void execute(Task task) {
                 test.getLogger().info("Running single tests with pattern: {}", test.getIncludes());
             }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLanguagePlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLanguagePlugin.java
deleted file mode 100644
index 00d5da7..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLanguagePlugin.java
+++ /dev/null
@@ -1,113 +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.plugins;
-
-import org.gradle.api.*;
-import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.language.base.BinaryContainer;
-import org.gradle.language.base.internal.BinaryInternal;
-import org.gradle.language.base.internal.BinaryNamingScheme;
-import org.gradle.language.java.internal.DefaultJavaSourceSet;
-import org.gradle.language.java.JavaSourceSet;
-import org.gradle.api.tasks.compile.AbstractCompile;
-import org.gradle.api.tasks.compile.JavaCompile;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.language.base.FunctionalSourceSet;
-import org.gradle.language.base.ProjectSourceSet;
-import org.gradle.language.jvm.internal.DefaultClasspath;
-import org.gradle.language.base.internal.DefaultProjectSourceSet;
-import org.gradle.language.jvm.ClassDirectoryBinary;
-import org.gradle.language.jvm.plugins.JvmLanguagePlugin;
-
-import javax.inject.Inject;
-import java.util.concurrent.Callable;
-
-/**
- * Plugin for compiling Java code. Applies the {@link org.gradle.language.jvm.plugins.JvmLanguagePlugin}.
- * Adds a {@link JavaCompile} task for each {@link JavaSourceSet} added to a {@link org.gradle.language.jvm.ClassDirectoryBinary}.
- * Registers the {@link JavaSourceSet} element type for each {@link org.gradle.language.base.FunctionalSourceSet} added to {@link org.gradle.language.base.ProjectSourceSet}.
- */
- at Incubating
-public class JavaLanguagePlugin implements Plugin<Project> {
-    private final Instantiator instantiator;
-    private final FileResolver fileResolver;
-
-    @Inject
-    public JavaLanguagePlugin(Instantiator instantiator, FileResolver fileResolver) {
-        this.instantiator = instantiator;
-        this.fileResolver = fileResolver;
-    }
-
-    public void apply(final Project target) {
-        target.getPlugins().apply(JvmLanguagePlugin.class);
-
-        BinaryContainer jvmBinaryContainer = target.getExtensions().getByType(BinaryContainer.class);
-        jvmBinaryContainer.withType(ClassDirectoryBinary.class).all(new Action<ClassDirectoryBinary>() {
-            public void execute(final ClassDirectoryBinary binary) {
-                final BinaryNamingScheme namingScheme = ((BinaryInternal) binary).getNamingScheme();
-                binary.getSource().withType(JavaSourceSet.class).all(new Action<JavaSourceSet>() {
-                    public void execute(JavaSourceSet javaSourceSet) {
-                        // TODO: handle case where binary has multiple JavaSourceSet's
-                        JavaCompile compileTask = target.getTasks().create(namingScheme.getTaskName("compile", "java"), JavaCompile.class);
-                        configureCompileTask(compileTask, javaSourceSet, binary);
-                        binary.builtBy(compileTask);
-                    }
-                });
-            }
-        });
-
-        ProjectSourceSet projectSourceSet = target.getExtensions().getByType(DefaultProjectSourceSet.class);
-        projectSourceSet.all(new Action<FunctionalSourceSet>() {
-            public void execute(final FunctionalSourceSet functionalSourceSet) {
-                functionalSourceSet.registerFactory(JavaSourceSet.class, new NamedDomainObjectFactory<JavaSourceSet>() {
-                    public JavaSourceSet create(String name) {
-                        return instantiator.newInstance(DefaultJavaSourceSet.class, name,
-                                instantiator.newInstance(DefaultSourceDirectorySet.class, name, fileResolver),
-                                instantiator.newInstance(DefaultClasspath.class, fileResolver,
-                                        target.getTasks()), functionalSourceSet);
-                    }
-                });
-            }
-        });
-    }
-
-
-    /**
-     * Preconfigures the specified compile task based on the specified source set and class directory binary.
-     *
-     * @param compile the compile task to be preconfigured
-     * @param sourceSet the source set for the compile task
-     * @param binary the binary for the compile task
-     */
-    public void configureCompileTask(AbstractCompile compile, final JavaSourceSet sourceSet, final ClassDirectoryBinary binary) {
-        compile.setDescription(String.format("Compiles %s.", sourceSet));
-        compile.setSource(sourceSet.getSource());
-        compile.dependsOn(sourceSet);
-        ConventionMapping conventionMapping = compile.getConventionMapping();
-        conventionMapping.map("classpath", new Callable<Object>() {
-            public Object call() throws Exception {
-                return sourceSet.getCompileClasspath().getFiles();
-            }
-        });
-        conventionMapping.map("destinationDir", new Callable<Object>() {
-            public Object call() throws Exception {
-                return binary.getClassesDir();
-            }
-        });
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLibraryDistributionPlugin.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLibraryDistributionPlugin.groovy
index 9f20878..ab35784 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLibraryDistributionPlugin.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaLibraryDistributionPlugin.groovy
@@ -20,18 +20,19 @@ import org.gradle.api.Incubating
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.distribution.plugins.DistributionPlugin
+import org.gradle.api.internal.project.ProjectInternal
 
 /**
  * A {@link Plugin} which package a Java project as a distribution including the JAR and runtime dependencies.
  */
 @Incubating
-class JavaLibraryDistributionPlugin implements Plugin<Project> {
+class JavaLibraryDistributionPlugin implements Plugin<ProjectInternal> {
     private Project project
 
-    public void apply(Project project) {
+    public void apply(ProjectInternal project) {
         this.project = project
-        project.plugins.apply(JavaPlugin)
-        project.plugins.apply(DistributionPlugin)
+        project.pluginManager.apply(JavaPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         def contents = project.distributions[DistributionPlugin.MAIN_DISTRIBUTION_NAME].contents
         def jar = project.tasks[JavaPlugin.JAR_TASK_NAME]
         contents.with {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
index 8b009da..ca320f2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
@@ -25,9 +25,10 @@ import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
+import org.gradle.api.internal.component.BuildableJavaComponent;
+import org.gradle.api.internal.component.ComponentRegistry;
 import org.gradle.api.internal.java.JavaLibrary;
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
-import org.gradle.api.internal.plugins.EmbeddableJavaProject;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.bundling.Jar;
@@ -43,7 +44,7 @@ import java.util.concurrent.Callable;
 /**
  * <p>A {@link Plugin} which compiles and tests Java source, and assembles it into a JAR file.</p>
  */
-public class JavaPlugin implements Plugin<Project> {
+public class JavaPlugin implements Plugin<ProjectInternal> {
     public static final String PROCESS_RESOURCES_TASK_NAME = "processResources";
     public static final String CLASSES_TASK_NAME = "classes";
     public static final String COMPILE_JAVA_TASK_NAME = "compileJava";
@@ -59,11 +60,11 @@ public class JavaPlugin implements Plugin<Project> {
     public static final String TEST_RUNTIME_CONFIGURATION_NAME = "testRuntime";
     public static final String TEST_COMPILE_CONFIGURATION_NAME = "testCompile";
 
-    public void apply(Project project) {
-        project.getPlugins().apply(JavaBasePlugin.class);
+    public void apply(ProjectInternal project) {
+        project.getPluginManager().apply(JavaBasePlugin.class);
 
         JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class);
-        project.getConvention().getPlugins().put("embeddedJavaProject", new EmbeddableJavaProjectImpl(javaConvention));
+        project.getServices().get(ComponentRegistry.class).setMainComponent(new BuildableJavaComponentImpl(javaConvention));
 
         configureSourceSets(javaConvention);
         configureConfigurations(project);
@@ -98,15 +99,9 @@ public class JavaPlugin implements Plugin<Project> {
 
     private void configureArchivesAndComponent(final Project project, final JavaPluginConvention pluginConvention) {
         Jar jar = project.getTasks().create(JAR_TASK_NAME, Jar.class);
-        jar.getManifest().from(pluginConvention.getManifest());
         jar.setDescription("Assembles a jar archive containing the main classes.");
         jar.setGroup(BasePlugin.BUILD_GROUP);
         jar.from(pluginConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput());
-        jar.getMetaInf().from(new Callable() {
-            public Object call() throws Exception {
-                return pluginConvention.getMetaInf();
-            }
-        });
 
         ArchivePublishArtifact jarArtifact = new ArchivePublishArtifact(jar);
         Configuration runtimeConfiguration = project.getConfigurations().getByName(RUNTIME_CONFIGURATION_NAME);
@@ -181,10 +176,10 @@ public class JavaPlugin implements Plugin<Project> {
         task.dependsOn(configuration.getTaskDependencyFromProjectDependency(useDependedOn, otherProjectTaskName));
     }
 
-    private static class EmbeddableJavaProjectImpl implements EmbeddableJavaProject {
+    private static class BuildableJavaComponentImpl implements BuildableJavaComponent {
         private final JavaPluginConvention convention;
 
-        public EmbeddableJavaProjectImpl(JavaPluginConvention convention) {
+        public BuildableJavaComponentImpl(JavaPluginConvention convention) {
             this.convention = convention;
         }
 
@@ -202,5 +197,9 @@ public class JavaPlugin implements Plugin<Project> {
             FileCollection gradleApi = project.getConfigurations().detachedConfiguration(project.getDependencies().gradleApi(), project.getDependencies().localGroovy());
             return runtimeClasspath.minus(gradleApi);
         }
+
+        public Configuration getCompileDependencies() {
+            return convention.getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME);
+        }
     }
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
index fb8abb0..a89e915 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
@@ -48,7 +48,7 @@ class JavaPluginConvention {
     String testResultsDirName
 
     /**
-     * The name of the test reports directory. Can be a name or a path relative to the build dir.
+     * The name of the test reports directory. Can be a name or a path relative to {@link org.gradle.api.reporting.ReportingExtension#getBaseDir}.
      */
     String testReportDirName
 
@@ -60,18 +60,6 @@ class JavaPluginConvention {
     private JavaVersion srcCompat
     private JavaVersion targetCompat
 
-    /**
-     * Deprecated. Please use jar.metaInf instead. The property didn't add much value over the jar's setting
-     * and Gradle offers convenient ways of configuring all tasks of given type should someone needed.
-     * <p>
-     * The lines of metaInf file that will be configured by default to every jar task.
-     */
-    @Deprecated
-    List metaInf
-
-    @Deprecated
-    DefaultManifest manifest
-
     JavaPluginConvention(ProjectInternal project, Instantiator instantiator) {
         this.project = project
         sourceSets = instantiator.newInstance(DefaultSourceSetContainer.class, project.fileResolver, project.tasks, instantiator)
@@ -79,8 +67,6 @@ class JavaPluginConvention {
         docsDirName = 'docs'
         testResultsDirName = 'test-results'
         testReportDirName = 'tests'
-        manifest = manifest();
-        metaInf = []
     }
 
     /**
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/LegacyJavaComponentPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/LegacyJavaComponentPlugin.java
new file mode 100644
index 0000000..796d05d
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/LegacyJavaComponentPlugin.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.ConventionMapping;
+import org.gradle.api.internal.jvm.ClassDirectoryBinarySpecInternal;
+import org.gradle.api.internal.jvm.DefaultClassDirectoryBinarySpec;
+import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.jvm.ClassDirectoryBinarySpec;
+import org.gradle.jvm.platform.internal.DefaultJavaPlatform;
+import org.gradle.jvm.toolchain.JavaToolChain;
+import org.gradle.language.base.plugins.LanguageBasePlugin;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.language.java.JavaSourceSet;
+import org.gradle.language.jvm.JvmResourceSet;
+import org.gradle.language.jvm.tasks.ProcessResources;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.toolchain.ToolResolver;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.concurrent.Callable;
+
+/**
+ * Plugin for compiling Java code. Applies the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
+ *
+ * Base plugin for Java language support. Applies the {@link org.gradle.language.base.plugins.LanguageBasePlugin}.
+ * Registers the {@link org.gradle.jvm.ClassDirectoryBinarySpec} element type for the {@link org.gradle.platform.base.BinaryContainer}.
+ * Adds a lifecycle task named {@code classes} for each {@link org.gradle.jvm.ClassDirectoryBinarySpec}.
+ * Adds a {@link JavaCompile} task for each {@link JavaSourceSet} added to a {@link org.gradle.jvm.ClassDirectoryBinarySpec}.
+ */
+ at Incubating
+public class LegacyJavaComponentPlugin implements Plugin<Project> {
+
+    private final Instantiator instantiator;
+    private final JavaToolChain toolChain;
+    private final ITaskFactory taskFactory;
+    private final ToolResolver toolResolver;
+
+    @Inject
+    public LegacyJavaComponentPlugin(Instantiator instantiator, JavaToolChain toolChain, ITaskFactory taskFactory, ToolResolver toolResolver) {
+        this.instantiator = instantiator;
+        this.toolChain = toolChain;
+        this.taskFactory = taskFactory;
+        this.toolResolver = toolResolver;
+    }
+
+    public void apply(final Project target) {
+        target.getPluginManager().apply(LanguageBasePlugin.class);
+
+        BinaryContainer binaryContainer = target.getExtensions().getByType(BinaryContainer.class);
+        binaryContainer.registerFactory(ClassDirectoryBinarySpec.class, new NamedDomainObjectFactory<ClassDirectoryBinarySpec>() {
+            public ClassDirectoryBinarySpec create(String name) {
+                return instantiator.newInstance(DefaultClassDirectoryBinarySpec.class, name, toolChain, new DefaultJavaPlatform(JavaVersion.current()), instantiator, taskFactory, toolResolver);
+            }
+        });
+
+        binaryContainer.withType(ClassDirectoryBinarySpecInternal.class).all(new Action<ClassDirectoryBinarySpecInternal>() {
+            public void execute(ClassDirectoryBinarySpecInternal binary) {
+                createBinaryLifecycleTask(binary, target);
+                setClassesDirConvention(binary, target);
+                createProcessResourcesTaskForBinary(binary, target);
+                createCompileJavaTaskForBinary(binary, target);
+            }
+        });
+    }
+
+    private void setClassesDirConvention(ClassDirectoryBinarySpecInternal binary, final Project target) {
+        final BinaryNamingScheme namingScheme = binary.getNamingScheme();
+        ConventionMapping conventionMapping = new DslObject(binary).getConventionMapping();
+        conventionMapping.map("classesDir", new Callable<File>() {
+            public File call() throws Exception {
+                return new File(new File(target.getBuildDir(), "classes"), namingScheme.getOutputDirectoryBase());
+            }
+        });
+    }
+
+    private void createCompileJavaTaskForBinary(final ClassDirectoryBinarySpecInternal binary, final Project target) {
+        final BinaryNamingScheme namingScheme = binary.getNamingScheme();
+        binary.getSource().withType(JavaSourceSet.class).all(new Action<JavaSourceSet>() {
+            public void execute(JavaSourceSet javaSourceSet) {
+                JavaCompile compileTask = target.getTasks().create(namingScheme.getTaskName("compile", "java"), JavaCompile.class);
+                configureCompileTask(compileTask, javaSourceSet, binary);
+                binary.getTasks().add(compileTask);
+                binary.builtBy(compileTask);
+            }
+        });
+    }
+
+    private void createProcessResourcesTaskForBinary(final ClassDirectoryBinarySpecInternal binary, final Project target) {
+        final BinaryNamingScheme namingScheme = binary.getNamingScheme();
+        binary.getSource().withType(JvmResourceSet.class).all(new Action<JvmResourceSet>() {
+            public void execute(JvmResourceSet resourceSet) {
+                Copy resourcesTask = target.getTasks().create(namingScheme.getTaskName("process", "resources"), ProcessResources.class);
+                resourcesTask.setDescription(String.format("Processes %s.", resourceSet));
+                new DslObject(resourcesTask).getConventionMapping().map("destinationDir", new Callable<File>() {
+                    public File call() throws Exception {
+                        return binary.getResourcesDir();
+                    }
+                });
+                binary.getTasks().add(resourcesTask);
+                binary.builtBy(resourcesTask);
+                resourcesTask.from(resourceSet.getSource());
+            }
+        });
+    }
+
+    private void createBinaryLifecycleTask(ClassDirectoryBinarySpecInternal binary, Project target) {
+        Task binaryLifecycleTask = target.task(binary.getNamingScheme().getLifecycleTaskName());
+        binaryLifecycleTask.setGroup(LifecycleBasePlugin.BUILD_GROUP);
+        binaryLifecycleTask.setDescription(String.format("Assembles %s.", binary));
+        binary.setBuildTask(binaryLifecycleTask);
+    }
+
+
+    /**
+     * Preconfigures the specified compile task based on the specified source set and class directory binary.
+     *
+     * @param compile the compile task to be preconfigured
+     * @param sourceSet the source set for the compile task
+     * @param binary the binary for the compile task
+     */
+    public void configureCompileTask(AbstractCompile compile, final JavaSourceSet sourceSet, final ClassDirectoryBinarySpec binary) {
+        compile.setDescription(String.format("Compiles %s.", sourceSet));
+        compile.setSource(sourceSet.getSource());
+        compile.dependsOn(sourceSet);
+        ConventionMapping conventionMapping = compile.getConventionMapping();
+        conventionMapping.map("classpath", new Callable<Object>() {
+            public Object call() throws Exception {
+                return sourceSet.getCompileClasspath().getFiles();
+            }
+        });
+        conventionMapping.map("destinationDir", new Callable<Object>() {
+            public Object call() throws Exception {
+                return binary.getClassesDir();
+            }
+        });
+    }
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/WarPlugin.java b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/WarPlugin.java
index d14541a..d64c50a 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/WarPlugin.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/plugins/WarPlugin.java
@@ -42,7 +42,7 @@ public class WarPlugin implements Plugin<Project> {
     public static final String WEB_APP_GROUP = "web application";
 
     public void apply(final Project project) {
-        project.getPlugins().apply(JavaPlugin.class);
+        project.getPluginManager().apply(JavaPlugin.class);
         final WarPluginConvention pluginConvention = new WarPluginConvention(project);
         project.getConvention().getPlugins().put("war", pluginConvention);
 
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovyRuntime.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovyRuntime.java
index cfca7e1..dff5978 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovyRuntime.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/GroovyRuntime.java
@@ -17,12 +17,10 @@ package org.gradle.api.tasks;
 
 import com.google.common.collect.Lists;
 import org.gradle.api.*;
-import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection;
 import org.gradle.api.internal.plugins.GroovyJarFile;
-import org.gradle.api.plugins.GroovyBasePlugin;
 
 import java.io.File;
 import java.util.Collections;
@@ -31,7 +29,7 @@ import java.util.Set;
 
 /**
  * Provides information related to the Groovy runtime(s) used in a project. Added by the
- * {@link GroovyBasePlugin} as a project extension named {@code groovyRuntime}.
+ * {@link org.gradle.api.plugins.GroovyBasePlugin} as a project extension named {@code groovyRuntime}.
  *
  * <p>Example usage:
  *
@@ -71,11 +69,6 @@ public class GroovyRuntime {
      * @return a corresponding class path for executing Groovy tools such as the Groovy compiler and Groovydoc tool
      */
     public FileCollection inferGroovyClasspath(final Iterable<File> classpath) {
-        final Configuration groovyConfiguration = project.getConfigurations().getByName(GroovyBasePlugin.GROOVY_CONFIGURATION_NAME);
-        if (!groovyConfiguration.getDependencies().isEmpty()) {
-            return groovyConfiguration;
-        }
-
         // alternatively, we could return project.files(Runnable)
         // would differ in at least the following ways: 1. live 2. no autowiring
         return new LazilyInitializedFileCollection() {
@@ -99,7 +92,7 @@ public class GroovyRuntime {
                 // project.getDependencies().create(String) seems to be the only feasible way to create a Dependency with a classifier
                 dependencies.add(project.getDependencies().create(notation));
                 if (groovyJar.getVersion().getMajor() >= 2) {
-                    // add groovy-ant to bring in AntGroovyCompiler
+                    // add groovy-ant to bring in Groovydoc
                     dependencies.add(project.getDependencies().create(notation.replace(":groovy:", ":groovy-ant:")));
                 }
                 return project.getConfigurations().detachedConfiguration(dependencies.toArray(new Dependency[dependencies.size()]));
@@ -121,10 +114,14 @@ public class GroovyRuntime {
     }
 
     private GroovyJarFile findGroovyJarFile(Iterable<File> classpath) {
-        if (classpath == null) { return null; }
+        if (classpath == null) {
+            return null;
+        }
         for (File file : classpath) {
             GroovyJarFile groovyJar = GroovyJarFile.parse(file);
-            if (groovyJar != null) { return groovyJar; }
+            if (groovyJar != null) {
+                return groovyJar;
+            }
         }
         return null;
     }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
index 3e6287a..3309ba2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
@@ -179,7 +179,7 @@ public interface SourceSet {
     String getCompileTaskName(String language);
 
     /**
-     * Returns the name of the Jar task for this source set, if such a task exists.
+     * Returns the name of the Jar task for this source set.
      *
      * @return The task name. Never returns null.
      */
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetContainer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetContainer.java
index e77b0de..ca81bb1 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetContainer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetContainer.java
@@ -15,36 +15,11 @@
  */
 package org.gradle.api.tasks;
 
-import groovy.lang.Closure;
 import org.gradle.api.NamedDomainObjectContainer;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.NamedDomainObjectSet;
 
 /**
  * A {@code SourceSetContainer} manages a set of {@link SourceSet} objects.
  */
 public interface SourceSetContainer extends NamedDomainObjectContainer<SourceSet>, NamedDomainObjectSet<SourceSet> {
-    /**
-     * Adds a source set with the given name.
-     *
-     * @param name The name of the new source set.
-     * @return The newly added source set.
-     * @throws org.gradle.api.InvalidUserDataException when a source set with the given name already exists in this container.
-     * @deprecated use {@link #create(String)} instead
-     */
-    @Deprecated
-    SourceSet add(String name) throws InvalidUserDataException;
-
-    /**
-     * Adds a source set with the given name. The given configuration closure is executed against the source set
-     * before it is returned from this method.
-     *
-     * @param name The name of the new source set.
-     * @param configureClosure The closure to use to configure the source set.
-     * @return The newly added source set.
-     * @throws InvalidUserDataException when a source set with the given name already exists in this container.
-     * @deprecated use {@link #create(String, groovy.lang.Closure)} instead
-     */
-    @Deprecated
-    SourceSet add(String name, Closure configureClosure) throws InvalidUserDataException;
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetOutput.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetOutput.java
index 7b37684..b928ee2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetOutput.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/SourceSetOutput.java
@@ -136,7 +136,7 @@ public interface SourceSetOutput extends FileCollection {
     void dir(Object dir);
 
     /**
-     * Returns all dirs registered with with #dir method.
+     * Returns all dirs registered with #dir method.
      * Each file is resolved as {@link org.gradle.api.Project#file(Object)}
      * <p>
      * See example at {@link SourceSetOutput}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/Upload.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/Upload.java
index 2eeaf01..53daad6 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/Upload.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/Upload.java
@@ -46,12 +46,9 @@ public class Upload extends ConventionTask {
     private File descriptorDestination;
     private RepositoryHandler repositories;
 
-    private final ArtifactPublicationServices publicationServices;
-
     @Inject
-    public Upload(ArtifactPublicationServices publicationServices) {
-        this.publicationServices = publicationServices;
-        repositories = publicationServices.createRepositoryHandler();
+    protected ArtifactPublicationServices getPublicationServices() {
+        throw new UnsupportedOperationException();
     }
 
     @TaskAction
@@ -59,9 +56,9 @@ public class Upload extends ConventionTask {
         getLogger().info("Publishing configuration: " + configuration);
         ModuleInternal module = ((ConfigurationInternal) configuration).getModule();
 
-        ArtifactPublisher artifactPublisher = publicationServices.createArtifactPublisher();
+        ArtifactPublisher artifactPublisher = getPublicationServices().createArtifactPublisher();
         File descriptorDestination = isUploadDescriptor() ? getDescriptorDestination() : null;
-        List<PublicationAwareRepository> publishRepositories = collect(repositories, Transformers.cast(PublicationAwareRepository.class));
+        List<PublicationAwareRepository> publishRepositories = collect(getRepositories(), Transformers.cast(PublicationAwareRepository.class));
 
         try {
             artifactPublisher.publish(publishRepositories, module, configuration, descriptorDestination);
@@ -97,6 +94,9 @@ public class Upload extends ConventionTask {
      * Returns the repositories to upload to.
      */
     public RepositoryHandler getRepositories() {
+        if (repositories == null) {
+            repositories = getPublicationServices().createRepositoryHandler();
+        }
         return repositories;
     }
 
@@ -115,7 +115,7 @@ public class Upload extends ConventionTask {
      * Configures the set of repositories to upload to.
      */
     public RepositoryHandler repositories(Closure configureClosure) {
-        return ConfigureUtil.configure(configureClosure, repositories);
+        return ConfigureUtil.configure(configureClosure, getRepositories());
     }
 
     /**
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy
index b1acfaf..44903e2 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/application/CreateStartScripts.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 the original author or authors.
+ * Copyright 2015 the original author 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,99 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.gradle.api.tasks.application
-
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.internal.plugins.StartScriptGenerator
-import org.gradle.api.tasks.*
-import org.gradle.util.GUtil
-
-/**
- * <p>A {@link org.gradle.api.Task} for creating OS dependent start scripts.</p>
- */
-public class CreateStartScripts extends ConventionTask {
-
-    /**
-     * The directory to write the scripts into.
-     */
-    File outputDir
-
-    /**
-     * The application's main class.
-     */
-    @Input
-    String mainClassName
-
-    /**
-     * The application's default JVM options.
-     */
-    @Input
-    @Optional
-    Iterable<String> defaultJvmOpts = []
-
-    /**
-     * The application's name.
-     */
-    @Input
-    String applicationName
 
-    String optsEnvironmentVar
-
-    String exitEnvironmentVar
-
-    /**
-     * The class path for the application.
-     */
-    @InputFiles
-    FileCollection classpath
-
-    /**
-     * Returns the name of the application's OPTS environment variable.
-     */
-    @Input
-    String getOptsEnvironmentVar() {
-        if (optsEnvironmentVar) {
-            return optsEnvironmentVar
-        }
-        if (!getApplicationName()) {
-            return null
-        }
-        return "${GUtil.toConstant(getApplicationName())}_OPTS"
-    }
-
-    @Input
-    String getExitEnvironmentVar() {
-        if (exitEnvironmentVar) {
-            return exitEnvironmentVar
-        }
-        if (!getApplicationName()) {
-            return null
-        }
-        return "${GUtil.toConstant(getApplicationName())}_EXIT_CONSOLE"
-    }
-
-    @OutputFile
-    File getUnixScript() {
-        return new File(getOutputDir(), getApplicationName())
-    }
-
-    @OutputFile
-    File getWindowsScript() {
-        return new File(getOutputDir(), "${getApplicationName()}.bat")
-    }
+package org.gradle.api.tasks.application
 
-    @TaskAction
-    void generate() {
-        def generator = new StartScriptGenerator()
-        generator.applicationName = getApplicationName()
-        generator.mainClassName = getMainClassName()
-        generator.defaultJvmOpts = getDefaultJvmOpts()
-        generator.optsEnvironmentVar = getOptsEnvironmentVar()
-        generator.exitEnvironmentVar = getExitEnvironmentVar()
-        generator.classpath = getClasspath().collect { "lib/${it.name}" }
-        generator.scriptRelPath = "bin/${getUnixScript().name}"
-        generator.generateUnixScript(getUnixScript())
-        generator.generateWindowsScript(getWindowsScript())
-    }
+class CreateStartScripts extends org.gradle.jvm.application.tasks.CreateStartScripts {
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
index f64f02e..e49be1b 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 the original author or authors.
+ * 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.
@@ -16,94 +16,18 @@
 
 package org.gradle.api.tasks.bundling
 
-import org.gradle.api.file.CopySpec
-import org.gradle.api.file.FileCopyDetails
-import org.gradle.api.internal.file.FileResolver
-import org.gradle.api.internal.file.collections.FileTreeAdapter
-import org.gradle.api.internal.file.collections.MapFileTree
-import org.gradle.api.internal.file.copy.DefaultCopySpec
-import org.gradle.api.java.archives.Manifest
-import org.gradle.api.java.archives.internal.DefaultManifest
-import org.gradle.internal.nativeplatform.filesystem.Chmod
-import org.gradle.util.ConfigureUtil
+import org.gradle.api.tasks.ParallelizableTask
 
 /**
  * Assembles a JAR archive.
  */
-public class Jar extends Zip {
-    public static final String DEFAULT_EXTENSION = 'jar'
+ at ParallelizableTask
+public class Jar extends org.gradle.jvm.tasks.Jar {
 
-    private Manifest manifest
-    private final DefaultCopySpec metaInf
-
-    Jar() {
-        extension = DEFAULT_EXTENSION
-        manifest = new DefaultManifest(getServices().get(FileResolver))
-        // Add these as separate specs, so they are not affected by the changes to the main spec
-        metaInf = rootSpec.addFirst().into('META-INF')
-        metaInf.addChild().from {
-            MapFileTree manifestSource = new MapFileTree(temporaryDirFactory, services.get(Chmod))
-            manifestSource.add('MANIFEST.MF') {OutputStream outstr ->
-                Manifest manifest = getManifest() ?: new DefaultManifest(null)
-                manifest.writeTo(new OutputStreamWriter(outstr))
-            }
-            return new FileTreeAdapter(manifestSource)
-        }
-        mainSpec.eachFile { FileCopyDetails details ->
-            if (details.path.equalsIgnoreCase('META-INF/MANIFEST.MF')) {
-                details.exclude()
-            }
-        }
-    }
-
-    /**
-     * Returns the manifest for this JAR archive.
-     * @return The manifest
-     */
-    public Manifest getManifest() {
-        return manifest;
-    }
-
-    /**
-     * Sets the manifest for this JAR archive.
-     *
-     * @param manifest The manifest. May be null.
-     */
-    public void setManifest(Manifest manifest) {
-        this.manifest = manifest;
-    }
-
-    /**
-     * Configures the manifest for this JAR archive.
-     *
-     * <p>The given closure is executed to configure the manifest. The {@link org.gradle.api.java.archives.Manifest}
-     * is passed to the closure as its delegate.</p>
-     *
-     * @param configureClosure The closure.
-     * @return This.
-     */
-    public Jar manifest(Closure configureClosure) {
-        if (getManifest() == null) {
-            manifest = new DefaultManifest(project.fileResolver)
-        }
-        ConfigureUtil.configure(configureClosure, getManifest());
+    @Override
+    Jar manifest(Closure<?> configureClosure) {
+        super.manifest(configureClosure)
         return this;
     }
 
-    public CopySpec getMetaInf() {
-        return metaInf.addChild()
-    }
-
-    /**
-     * Adds content to this JAR archive's META-INF directory.
-     *
-     * <p>The given closure is executed to configure a {@code CopySpec}. The {@link CopySpec} is passed to the closure
-     * as its delegate.</p>
-     *
-     * @param configureClosure The closure.
-     * @return The created {@code CopySpec}
-     */
-    public CopySpec metaInf(Closure configureClosure) {
-        return ConfigureUtil.configure(configureClosure, getMetaInf())
-    }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractOptions.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractOptions.java
deleted file mode 100644
index 163e8c9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractOptions.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile;
-
-import com.google.common.collect.Maps;
-import org.gradle.api.Nullable;
-import org.gradle.internal.Factory;
-import org.gradle.internal.reflect.JavaReflectionUtil;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Map;
-
-/**
- * Base class for compilation-related options.
- */
-public abstract class AbstractOptions implements Serializable {
-    private static final long serialVersionUID = 0;
-
-    public void define(@Nullable Map<String, Object> args) {
-        if (args == null) { return; }
-        for (Map.Entry<String, Object> arg: args.entrySet()) {
-            JavaReflectionUtil.writeableProperty(getClass(), arg.getKey()).setValue(this, arg.getValue());
-        }
-    }
-
-    public Map<String, Object> optionMap() {
-        final Class<?> thisClass = getClass();
-        return DeprecationLogger.whileDisabled(new Factory<Map<String, Object>>() {
-            public Map<String, Object> create() {
-                Map<String, Object> map = Maps.newHashMap();
-                Class<?> currClass = thisClass;
-                if (currClass.getName().endsWith("_Decorated")) {
-                    currClass = currClass.getSuperclass();
-                }
-                while (currClass != AbstractOptions.class) {
-                    for (Field field : currClass.getDeclaredFields()) {
-                        if (isOptionField(field)) {
-                            addValueToMapIfNotNull(map, field);
-                        }
-                    }
-                    currClass = currClass.getSuperclass();
-                }
-                return map;
-            }
-        });
-    }
-
-    protected boolean excludeFromAntProperties(String fieldName) {
-        return false;
-    }
-
-    protected String getAntPropertyName(String fieldName) {
-        return fieldName;
-    }
-
-    protected Object getAntPropertyValue(String fieldName, Object value) {
-        return value;
-    }
-
-    private void addValueToMapIfNotNull(Map<String, Object> map, Field field) {
-        Object value = JavaReflectionUtil.readableProperty(getClass(), field.getName()).getValue(this);
-        if (value != null) {
-            map.put(getAntPropertyName(field.getName()), getAntPropertyValue(field.getName(), value));
-        }
-    }
-
-    private boolean isOptionField(Field field) {
-        return ((field.getModifiers() & Modifier.STATIC) == 0)
-                && (!field.getName().equals("metaClass"))
-                && (!excludeFromAntProperties(field.getName()));
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
deleted file mode 100644
index 7eedf8d..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.internal.file.FileOperations;
-import org.gradle.api.internal.hash.DefaultHasher;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.*;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.compile.incremental.*;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoExtractor;
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoSerializer;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.OutputDirectory;
-import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
-import org.gradle.internal.Factory;
-import org.gradle.util.Clock;
-import org.gradle.util.DeprecationLogger;
-import org.gradle.util.SingleMessageLogger;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.util.Collections.emptyList;
-
-/**
- * Compiles Java source files.
- *
- * @deprecated This class has been replaced by {@link JavaCompile}.
- */
- at Deprecated
-public class Compile extends AbstractCompile {
-
-    private static final Logger LOG = Logging.getLogger(Compile.class);
-
-    private Compiler<JavaCompileSpec> cleaningCompiler;
-    private File dependencyCacheDir;
-    private final CompileOptions compileOptions = new CompileOptions();
-    private final Compiler<JavaCompileSpec> javaCompiler;
-    private final IncrementalCompilationSupport incrementalCompilation;
-
-    public Compile() {
-        if (!(this instanceof JavaCompile)) {
-            DeprecationLogger.nagUserOfReplacedTaskType("Compile", "JavaCompile task type");
-        }
-        Factory<AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
-        JavaCompilerFactory inProcessCompilerFactory = new InProcessJavaCompilerFactory();
-        ProjectInternal projectInternal = (ProjectInternal) getProject();
-        CompilerDaemonManager compilerDaemonManager = getServices().get(CompilerDaemonManager.class);
-        JavaCompilerFactory defaultCompilerFactory = new DefaultJavaCompilerFactory(projectInternal, antBuilderFactory, inProcessCompilerFactory, compilerDaemonManager);
-        javaCompiler = new DelegatingJavaCompiler(defaultCompilerFactory);
-        cleaningCompiler = new CleaningJavaCompiler(javaCompiler, antBuilderFactory, getOutputs());
-        JarSnapshotFeeder jarSnapshotFeeder = new JarSnapshotFeeder(getJarSnapshotCache(), new JarSnapshotter(new DefaultHasher()));
-        incrementalCompilation = new IncrementalCompilationSupport(jarSnapshotFeeder);
-    }
-
-    private FileCollection compileClasspath; //TODO SF remove this hack
-
-    @TaskAction
-    protected void compile(IncrementalTaskInputs inputs) {
-        if (!maybeCompileIncrementally(inputs)) {
-            compile();
-        }
-        incrementalCompilation.compilationComplete(compileOptions,
-                new ClassDependencyInfoExtractor(getDestinationDir()),
-                getDependencyInfoSerializer(), Collections.<JarArchive>emptyList());
-    }
-
-    private ClassDependencyInfoSerializer getDependencyInfoSerializer() {
-        return new ClassDependencyInfoSerializer(new File(getProject().getBuildDir(), "class-info.bin"));
-    }
-
-    private boolean maybeCompileIncrementally(IncrementalTaskInputs inputs) {
-        if (!compileOptions.isIncremental()) {
-            return false;
-        }
-        //hack
-        List<File> sourceDirs = getSourceDirs();
-        if (sourceDirs.isEmpty()) {
-            LOG.lifecycle("{} cannot run incrementally because Gradle cannot infer the source directories.", getPath());
-            return false;
-        }
-        if (!inputs.isIncremental()) {
-            LOG.lifecycle("{} is not incremental (e.g. outputs have changed, no previous execution, etc). Using regular compile.", getPath());
-            return false;
-        }
-        ClassDependencyInfoSerializer dependencyInfoSerializer = getDependencyInfoSerializer();
-        if (!dependencyInfoSerializer.isInfoAvailable()) {
-            //TODO SF let's unit test a scenario when after regular compilation incremental compilation is scheduled
-            LOG.lifecycle("{} is not incremental because there is no class dependency data left from previous incremental build.", getPath());
-            return false;
-        }
-
-        SingleMessageLogger.incubatingFeatureUsed("Incremental java compilation");
-
-        SelectiveJavaCompiler compiler = new SelectiveJavaCompiler(javaCompiler, getProject().fileTree(getDestinationDir()));
-        SelectiveCompilation selectiveCompilation = new SelectiveCompilation(inputs, getSource(), getClasspath(), getDestinationDir(),
-                dependencyInfoSerializer, getJarSnapshotCache(), compiler, sourceDirs, (FileOperations) getProject());
-
-        if (!selectiveCompilation.getCompilationNeeded()) {
-            LOG.lifecycle("{} does not require recompilation. Skipping the compiler.", getPath());
-            return true;
-        }
-
-        Clock clock = new Clock();
-        performCompilation(selectiveCompilation.getSource(), selectiveCompilation.getClasspath(), selectiveCompilation.getFullRebuildRequired()? cleaningCompiler : compiler);
-        LOG.lifecycle("{} - incremental compilation took {}", getPath(), clock.getTime());
-
-        return true;
-    }
-
-    private List<File> getSourceDirs() {
-        List<File> sourceDirs = new LinkedList<File>();
-        for (Object s : source) {
-            if (s instanceof SourceDirectorySet) {
-                sourceDirs.addAll(((SourceDirectorySet) s).getSrcDirs());
-            } else {
-                return emptyList();
-            }
-        }
-        return sourceDirs;
-    }
-
-    protected void compile() {
-        FileTree source = getSource();
-        FileCollection classpath = getClasspath();
-
-        performCompilation(source, classpath, cleaningCompiler);
-    }
-
-    private void performCompilation(FileCollection source, FileCollection classpath, Compiler<JavaCompileSpec> compiler) {
-        DefaultJavaCompileSpec spec = new DefaultJavaCompileSpec();
-        spec.setSource(source);
-        spec.setDestinationDir(getDestinationDir());
-        spec.setClasspath(classpath);
-        spec.setDependencyCacheDir(getDependencyCacheDir());
-        spec.setSourceCompatibility(getSourceCompatibility());
-        spec.setTargetCompatibility(getTargetCompatibility());
-        spec.setCompileOptions(compileOptions);
-        WorkResult result = compiler.execute(spec);
-        setDidWork(result.getDidWork());
-        compileClasspath = classpath;
-    }
-
-    private JarSnapshotCache getJarSnapshotCache() {
-        //hack, needs fixing
-        return new JarSnapshotCache(new File(getProject().getRootProject().getProjectDir(), ".gradle/jar-snapshot-cache.bin"));
-    }
-
-    @OutputDirectory
-    public File getDependencyCacheDir() {
-        return dependencyCacheDir;
-    }
-
-    public void setDependencyCacheDir(File dependencyCacheDir) {
-        this.dependencyCacheDir = dependencyCacheDir;
-    }
-
-    /**
-     * Returns the compilation options.
-     *
-     * @return The compilation options.
-     */
-    @Nested
-    public CompileOptions getOptions() {
-        return compileOptions;
-    }
-
-    /**
-     * This method was never intended to be used by the users.
-     *
-     * @return the compiler
-     */
-    @Deprecated
-    public Compiler<JavaCompileSpec> getJavaCompiler() {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Compile.getJavaCompiler()");
-        return cleaningCompiler;
-    }
-
-    /**
-     * This method was never intended to be used by the users.
-     *
-     * @param javaCompiler to set
-     */
-    @Deprecated
-    public void setJavaCompiler(Compiler<JavaCompileSpec> javaCompiler) {
-        DeprecationLogger.nagUserOfDiscontinuedMethod("Compile.setJavaCompiler(Compiler<JavaCompileSpec>)");
-        this.cleaningCompiler = javaCompiler;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.java
deleted file mode 100644
index b5844d5..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/CompileOptions.java
+++ /dev/null
@@ -1,539 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import org.gradle.api.Incubating;
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.Optional;
-import org.gradle.util.DeprecationLogger;
-import org.gradle.util.SingleMessageLogger;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Main options for Java compilation.
- */
-public class CompileOptions extends AbstractOptions {
-    private static final long serialVersionUID = 0;
-
-    private static final ImmutableSet<String> EXCLUDE_FROM_ANT_PROPERTIES =
-            ImmutableSet.of("debugOptions", "forkOptions", "compilerArgs", "dependOptions", "useDepend", "useAnt", "incremental");
-
-    private boolean failOnError = true;
-
-    private boolean verbose;
-
-    private boolean listFiles;
-
-    private boolean deprecation;
-
-    private boolean warnings = true;
-
-    private String encoding;
-
-    private boolean optimize;
-
-    private boolean debug = true;
-
-    private DebugOptions debugOptions = new DebugOptions();
-
-    private boolean fork;
-
-    private ForkOptions forkOptions = new ForkOptions();
-
-    private boolean useDepend;
-
-    private DependOptions dependOptions = new DependOptions();
-
-    private String compiler;
-
-    private boolean includeJavaRuntime;
-
-    private String bootClasspath;
-
-    private String extensionDirs;
-
-    private List<String> compilerArgs = Lists.newArrayList();
-
-    private boolean useAnt;
-    private boolean incremental;
-
-    /**
-     * Tells whether to fail the build when compilation fails. Defaults to {@code true}.
-     */
-    @Input
-    public boolean isFailOnError() {
-        return failOnError;
-    }
-
-    /**
-     * Deprecated.
-     *
-     * @deprecated use {@link #isFailOnError()}
-     */
-    @Deprecated
-    public boolean getFailOnError() {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("CompileOptions.getFailOnError()", "CompileOptions.isFailOnError()");
-        return failOnError;
-    }
-
-    /**
-     * Sets whether to fail the build when compilation fails. Defaults to {@code true}.
-     */
-    public void setFailOnError(boolean failOnError) {
-        this.failOnError = failOnError;
-    }
-
-    /**
-     * Tells whether to produce verbose output. Defaults to {@code false}.
-     */
-    public boolean isVerbose() {
-        return verbose;
-    }
-
-    /**
-     * Sets whether to produce verbose output. Defaults to {@code false}.
-     */
-    public void setVerbose(boolean verbose) {
-        this.verbose = verbose;
-    }
-
-    /**
-     * Tells whether to log the files to be compiled. Defaults to {@code false}.
-     */
-    public boolean isListFiles() {
-        return listFiles;
-    }
-
-    /**
-     * Sets whether to log the files to be compiled. Defaults to {@code false}.
-     */
-    public void setListFiles(boolean listFiles) {
-        this.listFiles = listFiles;
-    }
-
-    /**
-     * Tells whether to log details of usage of deprecated members or classes. Defaults to {@code false}.
-     */
-    public boolean isDeprecation() {
-        return deprecation;
-    }
-
-    /**
-     * Sets whether to log details of usage of deprecated members or classes. Defaults to {@code false}.
-     */
-    public void setDeprecation(boolean deprecation) {
-        this.deprecation = deprecation;
-    }
-
-    /**
-     * Tells whether to log warning messages. The default is {@code true}.
-     */
-    public boolean isWarnings() {
-        return warnings;
-    }
-
-    /**
-     * Sets whether to log warning messages. The default is {@code true}.
-     */
-    public void setWarnings(boolean warnings) {
-        this.warnings = warnings;
-    }
-
-    /**
-     * Returns the character encoding to be used when reading source files. Defaults to {@code null}, in which
-     * case the platform default encoding will be used.
-     */
-    @Input
-    @Optional
-    public String getEncoding() {
-        return encoding;
-    }
-
-    /**
-     * Sets the character encoding to be used when reading source files. Defaults to {@code null}, in which
-     * case the platform default encoding will be used.
-     */
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-    }
-
-    /**
-     * Tells whether to produce optimized byte code. Only takes effect if {@code useAnt} is {@code true}.
-     * Note that this flag is ignored by Sun's javac starting with JDK 1.3.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public boolean isOptimize() {
-        return optimize;
-    }
-
-    /**
-     * Tells whether to produce optimized byte code. Only takes effect if {@code useAnt} is {@code true}.
-     * Note that this flag is ignored by Sun's javac starting with JDK 1.3.
-     *
-     * @deprecated No replacement
-     */
-    @Input
-    @Deprecated
-    public boolean getOptimize() {
-        return optimize;
-    }
-
-    /**
-     * Sets whether to produce optimized byte code. Only takes effect if {@code useAnt} is {@code true}.
-     * Note that this flag is ignored by Sun's javac starting with JDK 1.3.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setOptimize(boolean optimize) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("CompileOptions.optimize", "There is no replacement for this property.");
-        this.optimize = optimize;
-    }
-
-    /**
-     * Tells whether to include debugging information in the generated class files. Defaults
-     * to {@code true}. See {@link DebugOptions#getDebugLevel()} for which debugging information will be generated.
-     */
-    @Input
-    public boolean isDebug() {
-        return debug;
-    }
-
-    /**
-     * Deprecated.
-     *
-     * @deprecated use {@link #isDebug()}
-     */
-    @Deprecated
-    public boolean getDebug() {
-        DeprecationLogger.nagUserOfReplacedMethod("CompileOptions.getDebug()", "CompileOptions.isDebug()");
-        return debug;
-    }
-
-    /**
-     * Sets whether to include debugging information in the generated class files. Defaults
-     * to {@code true}. See {@link DebugOptions#getDebugLevel()} for which debugging information will be generated.
-     */
-    public void setDebug(boolean debug) {
-        this.debug = debug;
-    }
-
-    /**
-     * Returns options for generating debugging information.
-     */
-    @Nested
-    public DebugOptions getDebugOptions() {
-        return debugOptions;
-    }
-
-    /**
-     * Sets options for generating debugging information.
-     */
-    public void setDebugOptions(DebugOptions debugOptions) {
-        this.debugOptions = debugOptions;
-    }
-
-    /**
-     * Tells whether to run the compiler in its own process. Note that this does
-     * not necessarily mean that a new process will be created for each compile task.
-     * Defaults to {@code false}.
-     */
-    public boolean isFork() {
-        return fork;
-    }
-
-    /**
-     * Sets whether to run the compiler in its own process. Note that this does
-     * not necessarily mean that a new process will be created for each compile task.
-     * Defaults to {@code false}.
-     */
-    public void setFork(boolean fork) {
-        this.fork = fork;
-    }
-
-    /**
-     * Returns options for running the compiler in a child process.
-     */
-    @Nested
-    public ForkOptions getForkOptions() {
-        return forkOptions;
-    }
-
-    /**
-     * Sets options for running the compiler in a child process.
-     */
-    public void setForkOptions(ForkOptions forkOptions) {
-        this.forkOptions = forkOptions;
-    }
-
-    /**
-     * Tells whether to use the Ant {@code <depend>} task.
-     * Only takes effect if {@code useAnt} is {@code true}. Defaults to
-     * {@code false}.
-     */
-    public boolean isUseDepend() {
-        return useDepend;
-    }
-
-    /**
-     * Sets whether to use the Ant {@code <depend>} task.
-     * Only takes effect if {@code useAnt} is {@code true}. Defaults to
-     * {@code false}.
-     */
-    public void setUseDepend(boolean useDepend) {
-        this.useDepend = useDepend;
-    }
-
-    /**
-     * Returns options for using the Ant {@code <depend>} task.
-     */
-    public DependOptions getDependOptions() {
-        return dependOptions;
-    }
-
-    /**
-     * Sets options for using the Ant {@code <depend>} task.
-     */
-    public void setDependOptions(DependOptions dependOptions) {
-        this.dependOptions = dependOptions;
-    }
-
-    /**
-     * Returns the compiler to be used. Only takes effect if {@code useAnt} is {@code true}.
-     *
-     * @deprecated use {@code CompileOptions.forkOptions.executable} instead
-     */
-    @Deprecated
-    @Input @Optional
-    public String getCompiler() {
-        return compiler;
-    }
-
-    /**
-     * Sets the compiler to be used. Only takes effect if {@code useAnt} is {@code true}.
-     *
-     * @deprecated use {@code CompileOptions.forkOptions.executable instead}
-     */
-    @Deprecated
-    public void setCompiler(String compiler) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("CompileOptions.compiler", "To use an alternative compiler, "
-                + "set 'CompileOptions.fork' to 'true', and 'CompileOptions.forkOptions.executable' to the path of the compiler executable.");
-        this.compiler = compiler;
-    }
-
-    /**
-     * Tells whether the Java runtime should be put on the compile class path. Only takes effect if
-     * {@code useAnt} is {@code true}. Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public boolean isIncludeJavaRuntime() {
-        return includeJavaRuntime;
-    }
-
-    /**
-     * Tells whether the Java runtime should be put on the compile class path. Only takes effect if
-     * {@code useAnt} is {@code true}. Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Input
-    @Deprecated
-    public boolean getIncludeJavaRuntime() {
-        return includeJavaRuntime;
-    }
-
-    /**
-     * Sets whether the Java runtime should be put on the compile class path. Only takes effect if
-     * {@code useAnt} is {@code true}. Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setIncludeJavaRuntime(boolean includeJavaRuntime) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("CompileOptions.includeJavaRuntime", "There is no replacement for this property.");
-        this.includeJavaRuntime = includeJavaRuntime;
-    }
-
-    /**
-     * Returns the bootstrap classpath to be used for the compiler process.
-     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
-     */
-    @Input
-    @Optional
-    public String getBootClasspath() {
-        return bootClasspath;
-    }
-
-    /**
-     * Sets the bootstrap classpath to be used for the compiler process.
-     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
-     */
-    public void setBootClasspath(String bootClasspath) {
-        this.bootClasspath = bootClasspath;
-    }
-
-    /**
-     * Returns the extension dirs to be used for the compiler process.
-     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
-     */
-    @Input 
-    @Optional
-    public String getExtensionDirs() {
-        return extensionDirs;
-    }
-
-    /**
-     * Sets the extension dirs to be used for the compiler process.
-     * Only takes effect if {@code fork} is {@code true}. Defaults to {@code null}.
-     */
-    public void setExtensionDirs(String extensionDirs) {
-        this.extensionDirs = extensionDirs;
-    }
-
-    /**
-     * Returns any additional arguments to be passed to the compiler.
-     * Defaults to the empty list.
-     */
-    @Input
-    public List<String> getCompilerArgs() {
-        return compilerArgs;
-    }
-
-    /**
-     * Sets any additional arguments to be passed to the compiler.
-     * Defaults to the empty list.
-     */
-    public void setCompilerArgs(List<String> compilerArgs) {
-        this.compilerArgs = compilerArgs;
-    }
-
-    /**
-     * Tells whether to use the Ant javac task over Gradle's own Java compiler integration.
-     * Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public boolean isUseAnt() {
-        return useAnt;
-    }
-
-    /**
-     * Sets whether to use the Ant javac task over Gradle's own Java compiler integration.
-     * Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setUseAnt(boolean useAnt) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("CompileOptions.useAnt", "There is no replacement for this property.");
-        this.useAnt = useAnt;
-    }
-
-    /**
-     * Convenience method to set {@link ForkOptions} with named parameter syntax.
-     * Calling this method will set {@code fork} to {@code true}.
-     */
-    public CompileOptions fork(Map<String, Object> forkArgs) {
-        fork = true;
-        forkOptions.define(forkArgs);
-        return this;
-    }
-
-    /**
-     * Convenience method to set {@link DebugOptions} with named parameter syntax.
-     * Calling this method will set {@code debug} to {@code true}.
-     */
-    public CompileOptions debug(Map<String, Object> debugArgs) {
-        debug = true;
-        debugOptions.define(debugArgs);
-        return this;
-    }
-
-    /**
-     * Convenience method to set {@link DependOptions} with named parameter syntax.
-     * Calling this method will set {@code useDepend} to {@code true}.
-     */
-    public CompileOptions depend(Map<String, Object> dependArgs) {
-        useDepend = true;
-        dependOptions.define(dependArgs);
-        return this;
-    }
-
-    @Incubating
-    /**
-     * Configure the java compilation to be incremental (e.g. compiles only those java classes that were changed or that are dependencies to the changed classes).
-     * The feature is incubating and does not yet satisfies all compilation scenarios.
-     */
-    public CompileOptions setIncremental(boolean incremental) {
-        SingleMessageLogger.incubatingFeatureUsed("Incremental java compilation");
-        this.incremental = incremental;
-        return this;
-    }
-
-    /**
-     * Internal method.
-     */
-    public Map<String, Object> optionMap() {
-        Map<String, Object> map = super.optionMap();
-        map.putAll(debugOptions.optionMap());
-        map.putAll(forkOptions.optionMap());
-        return map;
-    }
-
-    @Override
-    protected boolean excludeFromAntProperties(String fieldName) {
-        return EXCLUDE_FROM_ANT_PROPERTIES.contains(fieldName);
-    }
-
-    @Override
-    protected String getAntPropertyName(String fieldName) {
-        if (fieldName.equals("warnings")) {
-            return "nowarn";
-        }
-        if (fieldName.equals("extensionDirs")) {
-            return "extdirs";
-        }
-        return fieldName;
-    }
-
-    @Override
-    protected Object getAntPropertyValue(String fieldName, Object value) {
-        if (fieldName.equals("warnings")) {
-            return !warnings;
-        }
-        return value;
-    }
-
-    /**
-     * informs whether to use experimental incremental compilation feature. See {@link #setIncremental(boolean)}
-     */
-    @Incubating
-    public boolean isIncremental() {
-        return incremental;
-    }
-}
-
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java
deleted file mode 100644
index 1bfffb8..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile;
-
-import org.gradle.api.AntBuilder;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.file.TemporaryFileProvider;
-import org.gradle.api.internal.project.IsolatedAntBuilder;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.*;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.compile.daemon.InProcessCompilerDaemonFactory;
-import org.gradle.api.tasks.InputFiles;
-import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.Factory;
-import org.gradle.util.GFileUtils;
-
-import java.io.File;
-
-/**
- * Compiles Groovy source files, and optionally, Java source files.
- */
-public class GroovyCompile extends AbstractCompile {
-    private Compiler<GroovyJavaJointCompileSpec> compiler;
-    private FileCollection groovyClasspath;
-    private final CompileOptions compileOptions = new CompileOptions();
-    private final GroovyCompileOptions groovyCompileOptions = new GroovyCompileOptions();
-    private final TemporaryFileProvider tempFileProvider;
-
-    public GroovyCompile() {
-        ProjectInternal projectInternal = (ProjectInternal) getProject();
-        IsolatedAntBuilder antBuilder = getServices().get(IsolatedAntBuilder.class);
-        ClassPathRegistry classPathRegistry = getServices().get(ClassPathRegistry.class);
-        Factory<AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
-        JavaCompilerFactory inProcessCompilerFactory = new InProcessJavaCompilerFactory();
-        tempFileProvider = projectInternal.getServices().get(TemporaryFileProvider.class);
-        CompilerDaemonManager compilerDaemonManager = getServices().get(CompilerDaemonManager.class);
-        InProcessCompilerDaemonFactory inProcessCompilerDaemonFactory = getServices().get(InProcessCompilerDaemonFactory.class);
-        DefaultJavaCompilerFactory javaCompilerFactory = new DefaultJavaCompilerFactory(projectInternal, antBuilderFactory, inProcessCompilerFactory, compilerDaemonManager);
-        GroovyCompilerFactory groovyCompilerFactory = new GroovyCompilerFactory(projectInternal, antBuilder, classPathRegistry, javaCompilerFactory, compilerDaemonManager, inProcessCompilerDaemonFactory);
-        Compiler<GroovyJavaJointCompileSpec> delegatingCompiler = new DelegatingGroovyCompiler(groovyCompilerFactory);
-        compiler = new CleaningGroovyCompiler(delegatingCompiler, getOutputs());
-    }
-
-    @TaskAction
-    protected void compile() {
-        checkGroovyClasspathIsNonEmpty();
-        DefaultGroovyJavaJointCompileSpec spec = new DefaultGroovyJavaJointCompileSpec();
-        spec.setSource(getSource());
-        spec.setDestinationDir(getDestinationDir());
-        spec.setClasspath(getClasspath());
-        spec.setSourceCompatibility(getSourceCompatibility());
-        spec.setTargetCompatibility(getTargetCompatibility());
-        spec.setGroovyClasspath(getGroovyClasspath());
-        spec.setCompileOptions(compileOptions);
-        spec.setGroovyCompileOptions(groovyCompileOptions);
-        if (spec.getGroovyCompileOptions().getStubDir() == null) {
-            File dir = tempFileProvider.newTemporaryFile("groovy-java-stubs");
-            GFileUtils.mkdirs(dir);
-            spec.getGroovyCompileOptions().setStubDir(dir);
-        }
-        WorkResult result = compiler.execute(spec);
-        setDidWork(result.getDidWork());
-    }
-
-    private void checkGroovyClasspathIsNonEmpty() {
-        if (getGroovyClasspath().isEmpty()) {
-            throw new InvalidUserDataException("'" + getName() + ".groovyClasspath' must not be empty. If a Groovy compile dependency is provided, "
-                    + "the 'groovy-base' plugin will attempt to configure 'groovyClasspath' automatically. Alternatively, you may configure 'groovyClasspath' explicitly.");
-        }
-    }
-
-    /**
-     * Gets the options for the Groovy compilation. To set specific options for the nested Java compilation, use {@link
-     * #getOptions()}.
-     *
-     * @return The Groovy compile options. Never returns null.
-     */
-    @Nested
-    public GroovyCompileOptions getGroovyOptions() {
-        return groovyCompileOptions;
-    }
-
-    /**
-     * Returns the options for Java compilation.
-     *
-     * @return The Java compile options. Never returns null.
-     */
-    @Nested
-    public CompileOptions getOptions() {
-        return compileOptions;
-    }
-
-    /**
-     * Returns the classpath containing the version of Groovy to use for compilation.
-     *
-     * @return The classpath.
-     */
-    @InputFiles
-    public FileCollection getGroovyClasspath() {
-        return groovyClasspath;
-    }
-
-    /**
-     * Sets the classpath containing the version of Groovy to use for compilation.
-     *
-     * @param groovyClasspath The classpath. Must not be null.
-     */
-    public void setGroovyClasspath(FileCollection groovyClasspath) {
-        this.groovyClasspath = groovyClasspath;
-    }
-
-    public Compiler<GroovyJavaJointCompileSpec> getCompiler() {
-        return compiler;
-    }
-
-    public void setCompiler(Compiler<GroovyJavaJointCompileSpec> compiler) {
-        this.compiler = compiler;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompileOptions.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompileOptions.java
deleted file mode 100644
index 4bb05ec..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompileOptions.java
+++ /dev/null
@@ -1,330 +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.tasks.compile;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import org.gradle.api.Incubating;
-import org.gradle.api.tasks.Input;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Compilation options to be passed to the Groovy compiler.
- */
-public class GroovyCompileOptions extends AbstractOptions {
-    private static final long serialVersionUID = 0;
-    private static final ImmutableSet<String> EXCLUDE_FROM_ANT_PROPERTIES =
-            ImmutableSet.of("forkOptions", "optimizationOptions", "useAnt", "stubDir", "keepStubs", "fileExtensions");
-
-    private boolean failOnError = true;
-
-    private boolean verbose;
-
-    private boolean listFiles;
-
-    private String encoding = "UTF-8";
-
-    private boolean fork = true;
-
-    private boolean keepStubs;
-
-    private List<String> fileExtensions = ImmutableList.of("java", "groovy");
-
-    private GroovyForkOptions forkOptions = new GroovyForkOptions();
-
-    private Map<String, Boolean> optimizationOptions = Maps.newHashMap();
-
-    private boolean stacktrace;
-
-    private boolean useAnt;
-
-    private boolean includeJavaRuntime;
-
-    private File stubDir;
-
-    /**
-     * Tells whether the compilation task should fail if compile errors occurred. Defaults to {@code true}.
-     */
-    public boolean isFailOnError() {
-        return failOnError;
-    }
-
-    /**
-     * Sets whether the compilation task should fail if compile errors occurred. Defaults to {@code true}.
-     */
-    public void setFailOnError(boolean failOnError) {
-        this.failOnError = failOnError;
-    }
-
-    /**
-     * Tells whether to turn on verbose output. Defaults to {@code false}.
-     */
-    public boolean isVerbose() {
-        return verbose;
-    }
-
-    /**
-     * Sets whether to turn on verbose output. Defaults to {@code false}.
-     */
-    public void setVerbose(boolean verbose) {
-        this.verbose = verbose;
-    }
-
-    /**
-     * Tells whether to print which source files are to be compiled. Defaults to {@code false}.
-     */
-    public boolean isListFiles() {
-        return listFiles;
-    }
-
-    /**
-     * Sets whether to print which source files are to be compiled. Defaults to {@code false}.
-     */
-    public void setListFiles(boolean listFiles) {
-        this.listFiles = listFiles;
-    }
-
-    /**
-     * Tells the source encoding. Defaults to {@code UTF-8}.
-     */
-    @Input
-    public String getEncoding() {
-        return encoding;
-    }
-
-    /**
-     * Sets the source encoding. Defaults to {@code UTF-8}.
-     */
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-    }
-
-    /**
-     * Tells whether to run the Groovy compiler in a separate process. Defaults to {@code true}.
-     */
-    public boolean isFork() {
-        return fork;
-    }
-
-    /**
-     * Sets whether to run the Groovy compiler in a separate process. Defaults to {@code true}.
-     */
-    public void setFork(boolean fork) {
-        this.fork = fork;
-    }
-
-    /**
-     * Returns options for running the Groovy compiler in a separate process. These options only take effect
-     * if {@code fork} is set to {@code true}.
-     */
-    public GroovyForkOptions getForkOptions() {
-        return forkOptions;
-    }
-
-    /**
-     * Sets options for running the Groovy compiler in a separate process. These options only take effect
-     * if {@code fork} is set to {@code true}.
-     */
-    public void setForkOptions(GroovyForkOptions forkOptions) {
-        this.forkOptions = forkOptions;
-    }
-
-    /**
-     * Returns optimization options for the Groovy compiler. Allowed values for an option are {@code true} and {@code false}.
-     * Only takes effect when compiling against Groovy 1.8 or higher.
-     *
-     * <p>Known options are:
-     *
-     * <dl>
-     *     <dt>indy
-     *     <dd>Use the invokedynamic bytecode instruction. Requires JDK7 or higher and Groovy 2.0 or higher. Disabled by default.
-     *     <dt>int
-     *     <dd>Optimize operations on primitive types (e.g. integers). Enabled by default.
-     *     <dt>all
-     *     <dd>Enable or disable all optimizations. Note that some optimizations might be mutually exclusive.
-     * </dl>
-     */
-    public Map<String, Boolean> getOptimizationOptions() {
-        return optimizationOptions;
-    }
-
-    /**
-     * Sets optimization options for the Groovy compiler. Allowed values for an option are {@code true} and {@code false}.
-     * Only takes effect when compiling against Groovy 1.8 or higher.
-     */
-    public void setOptimizationOptions(Map<String, Boolean> optimizationOptions) {
-        this.optimizationOptions = optimizationOptions;
-    }
-
-    /**
-     * Tells whether to print a stack trace when the compiler hits a problem (like a compile error).
-     * Defaults to {@code false}. Only used when {@link #isUseAnt()} is {@code true}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public boolean isStacktrace() {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.stacktrace", "There is no replacement for this property.");
-        return stacktrace;
-    }
-
-    /**
-     * Sets whether to print a stack trace when the compiler hits a problem (like a compile error).
-     * Defaults to {@code false}. Only used when {@link #isUseAnt()} is {@code true}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setStacktrace(boolean stacktrace) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.stacktrace", "This property has no replacement.");
-        this.stacktrace = stacktrace;
-    }
-
-    /**
-     * Tells whether the groovyc Ant task should be used over Gradle's own Groovy compiler integration.
-     * Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Input
-    @Deprecated
-    public boolean isUseAnt() {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.useAnt", "There is no replacement for this property.");
-        return useAnt;
-    }
-
-    /**
-     * Sets whether the groovyc Ant task should be used over Gradle's own Groovy compiler integration.
-     * Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setUseAnt(boolean useAnt) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.useAnt", "There is no replacement for this property.");
-        this.useAnt = useAnt;
-    }
-
-    /**
-     * Tells whether the Java runtime should be put on the compile class path. Only takes effect if
-     * {@code useAnt} is {@code true}. Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Input
-    @Deprecated
-    public boolean isIncludeJavaRuntime() {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.includeJavaRuntime", "There is no replacement for this property.");
-        return includeJavaRuntime;
-    }
-
-    /**
-     * Sets whether the Java runtime should be put on the compile class path. Only takes effect if
-     * {@code useAnt} is {@code true}. Defaults to {@code false}.
-     *
-     * @deprecated No replacement
-     */
-    @Deprecated
-    public void setIncludeJavaRuntime(boolean includeJavaRuntime) {
-        DeprecationLogger.nagUserOfDiscontinuedProperty("GroovyCompileOptions.includeJavaRuntime", "There is no replacement for this property.");
-        this.includeJavaRuntime = includeJavaRuntime;
-    }
-
-    /**
-     * Returns the directory where Java stubs for Groovy classes will be stored during Java/Groovy joint
-     * compilation. Defaults to {@code null}, in which case a temporary directory will be used.
-     */
-    public File getStubDir() {
-        return stubDir;
-    }
-
-    /**
-     * Sets the directory where Java stubs for Groovy classes will be stored during Java/Groovy joint
-     * compilation. Defaults to {@code null}, in which case a temporary directory will be used.
-     */
-    public void setStubDir(File stubDir) {
-        this.stubDir = stubDir;
-    }
-
-    /**
-     * Returns the list of acceptable source file extensions. Only takes effect when compiling against
-     * Groovy 1.7 or higher. Defaults to {@code ImmutableList.of("java", "groovy")}.
-     */
-    @Input
-    @Incubating
-    public List<String> getFileExtensions() {
-        return fileExtensions;
-    }
-
-    /**
-     * Sets the list of acceptable source file extensions. Only takes effect when compiling against
-     * Groovy 1.7 or higher. Defaults to {@code ImmutableList.of("java", "groovy")}.
-     */
-    @Incubating
-    public void setFileExtensions(List<String> fileExtensions) {
-        this.fileExtensions = fileExtensions;
-    }
-
-    /**
-     * Tells whether Java stubs for Groovy classes generated during Java/Groovy joint compilation
-     * should be kept after compilation has completed. Useful for joint compilation debugging purposes.
-     * Defaults to {@code false}.
-     */
-    public boolean isKeepStubs() {
-        return keepStubs;
-    }
-
-    /**
-     * Sets whether Java stubs for Groovy classes generated during Java/Groovy joint compilation
-     * should be kept after compilation has completed. Useful for joint compilation debugging purposes.
-     * Defaults to {@code false}.
-     */
-    public void setKeepStubs(boolean keepStubs) {
-        this.keepStubs = keepStubs;
-    }
-
-    /**
-     * Convenience method to set {@link GroovyForkOptions} with named parameter syntax.
-     * Calling this method will set {@code fork} to {@code true}.
-     */
-    public GroovyCompileOptions fork(Map forkArgs) {
-        fork = true;
-        forkOptions.define(forkArgs);
-        return this;
-    }
-
-    @Override
-    protected boolean excludeFromAntProperties(String fieldName) {
-        return EXCLUDE_FROM_ANT_PROPERTIES.contains(fieldName);
-    }
-
-    /**
-     * Internal method.
-     */
-    public Map<String, Object> optionMap() {
-        Map<String, Object> map = super.optionMap();
-        map.putAll(forkOptions.optionMap());
-        if (optimizationOptions.containsKey("indy")) {
-            map.put("indy", optimizationOptions.get("indy"));
-        }
-        return map;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/JavaCompile.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/JavaCompile.java
deleted file mode 100644
index e9884f9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/compile/JavaCompile.java
+++ /dev/null
@@ -1,31 +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.tasks.compile;
-
-/**
- * Compiles Java source files.
- *
- * <pre autoTested=''>
- *     apply plugin: 'java'
- *     compileJava {
- *         //enable compilation in a separate daemon process
- *         options.fork = true
- *     }
- * </pre>
- */
-public class JavaCompile extends Compile {
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy
deleted file mode 100644
index ccd8e45..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntGroovydoc.groovy
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.javadoc
-
-import org.gradle.api.Project
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.ClassPathRegistry
-import org.gradle.api.internal.project.IsolatedAntBuilder
-
-class AntGroovydoc {
-    private final IsolatedAntBuilder ant
-    private final ClassPathRegistry classPathRegistry
-
-    def AntGroovydoc(IsolatedAntBuilder ant, ClassPathRegistry classPathRegistry) {
-        this.ant = ant
-        this.classPathRegistry = classPathRegistry
-    }
-
-    void execute(FileCollection source, File destDir, boolean use, String windowTitle,
-                 String docTitle, String header, String footer, String overview, boolean includePrivate,
-                 Set links, Iterable groovyClasspath, Iterable classpath, Project project) {
-
-        def tmpDir = new File(project.buildDir, "tmp/groovydoc")
-        project.delete tmpDir
-        project.copy {
-            from source
-            into tmpDir
-        }
-
-        def args = [:]
-        args.sourcepath = tmpDir.toString()
-        args.destdir = destDir
-        args.use = use
-        args['private'] = includePrivate
-        putIfNotNull(args, 'windowtitle', windowTitle)
-        putIfNotNull(args, 'doctitle', docTitle)
-        putIfNotNull(args, 'header', header)
-        putIfNotNull(args, 'footer', footer)
-        putIfNotNull(args, 'overview', overview)
-
-        ant.withGroovy(groovyClasspath).withClasspath(classpath).execute {
-            taskdef(name: 'groovydoc', classname: 'org.codehaus.groovy.ant.Groovydoc')
-            groovydoc(args) {
-                links.each {gradleLink ->
-                    link(packages: gradleLink.packages.join(','), href: gradleLink.url)
-                }
-            }
-        }
-    }
-
-    void putIfNotNull(Map map, String key, Object value) {
-        if (value != null) {
-            map.put(key, value)
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntJavadoc.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntJavadoc.groovy
deleted file mode 100644
index 9446beb..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/AntJavadoc.groovy
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.javadoc
-
-/**
- * @deprecated No replacement
- */
- at Deprecated
-class AntJavadoc {
-    void execute(List<File> sourceDirs, File destDir, Set<File> classpathFiles, String windowTitle, String maxMemory,
-                 List<String> includes, List<String> excludes, boolean verbose, AntBuilder ant) {
-        Map otherArgs = [:]
-        if (maxMemory) {otherArgs.maxmemory = maxMemory}
-        if (windowTitle) {
-            otherArgs.windowtitle = windowTitle
-            otherArgs.doctitle = "<p>$windowTitle</p>"
-        }
-        ant.javadoc([destdir: destDir, failonerror: true, verbose: verbose] + otherArgs) {
-            sourceDirs.each {
-                fileset(dir: it) {
-                    includes.each {
-                        include(name: it)
-                    }
-                    excludes.each {
-                        exclude(name: it)
-                    }
-                    // This looks wrong. However, javadoc fails when package.html files are included explicitly. Javadoc
-                    // will include them in the documentation even if they are not included. So, exclude them.
-                    exclude(name: '**/package.html')
-                }
-            }
-            classpathFiles.each {
-                classpath(location: it)
-            }
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
deleted file mode 100644
index 19011e9..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
+++ /dev/null
@@ -1,355 +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.javadoc;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.project.IsolatedAntBuilder;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.tasks.*;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.*;
-
-/**
- * <p>Generates HTML API documentation for Groovy source, and optionally, Java source.
- *
- * <p>This task uses Groovy's Groovydoc tool to generate the API documentation. Please note that the Groovydoc tool has
- * some severe limitations at the moment (for example no doc for properties comments). The version of the Groovydoc that
- * is used, is the one from the Groovy defined in the build script. Please note also, that the Groovydoc tool prints to
- * System.out for many of its statements and does circumvents our logging currently.
- */
-public class Groovydoc extends SourceTask {
-    private FileCollection groovyClasspath;
-
-    private FileCollection classpath;
-
-    private File destinationDir;
-
-    private AntGroovydoc antGroovydoc;
-
-    private boolean use;
-
-    private String windowTitle;
-
-    private String docTitle;
-
-    private String header;
-
-    private String footer;
-
-    private String overview;
-
-    private Set<Link> links = new HashSet<Link>();
-
-    boolean includePrivate;
-
-    public Groovydoc() {
-        getLogging().captureStandardOutput(LogLevel.INFO);
-        IsolatedAntBuilder antBuilder = getServices().get(IsolatedAntBuilder.class);
-        ClassPathRegistry classPathRegistry = getServices().get(ClassPathRegistry.class);
-        antGroovydoc = new AntGroovydoc(antBuilder, classPathRegistry);
-    }
-
-    @TaskAction
-    protected void generate() {
-        checkGroovyClasspathNonEmpty(getGroovyClasspath().getFiles());
-        antGroovydoc.execute(getSource(), getDestinationDir(), isUse(), getWindowTitle(), getDocTitle(), getHeader(),
-                getFooter(), getOverview(), isIncludePrivate(), getLinks(), getGroovyClasspath(), getClasspath(), getProject());
-    }
-
-    private void checkGroovyClasspathNonEmpty(Collection<File> classpath) {
-        if (classpath.isEmpty()) {
-            throw new InvalidUserDataException("You must assign a Groovy library to the groovy configuration!");
-        }
-    }
-
-    /**
-     * Returns the directory to generate the documentation into.
-     *
-     * @return The directory to generate the documentation into
-     */
-    @OutputDirectory
-    public File getDestinationDir() {
-        return destinationDir;
-    }
-
-    /**
-     * Sets the directory to generate the documentation into.
-     */
-    public void setDestinationDir(File destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
-    /**
-     * Returns the classpath containing the Groovy library to be used.
-     *
-     * @return The classpath containing the Groovy library to be used
-     */   
-    @InputFiles
-    public FileCollection getGroovyClasspath() {
-        return groovyClasspath;
-    }
-
-    /**
-     * Sets the classpath containing the Groovy library to be used.
-     */
-    public void setGroovyClasspath(FileCollection groovyClasspath) {
-        this.groovyClasspath = groovyClasspath;
-    }
-
-    /**
-      * Returns the classpath used to locate classes referenced by the documented sources.
-      *
-      * @return The classpath used to locate classes referenced by the documented sources
-      */
-    @InputFiles
-    public FileCollection getClasspath() {
-        return classpath;
-    }
-
-    /**
-     * Sets the classpath used to locate classes referenced by the documented sources.
-     */
-    public void setClasspath(FileCollection classpath) {
-        this.classpath = classpath;
-    }
-
-    public AntGroovydoc getAntGroovydoc() {
-        return antGroovydoc;
-    }
-
-    public void setAntGroovydoc(AntGroovydoc antGroovydoc) {
-        this.antGroovydoc = antGroovydoc;
-    }
-
-    /**
-     * Returns whether to create class and package usage pages.
-     */
-    @Input
-    public boolean isUse() {
-        return use;
-    }
-
-    /**
-     * Sets whether to create class and package usage pages.
-     */
-    public void setUse(boolean use) {
-        this.use = use;
-    }
-
-    /**
-     * Returns the browser window title for the documentation. Set to {@code null} when there is no window title.
-     */
-    @Input @Optional
-    public String getWindowTitle() {
-        return windowTitle;
-    }
-
-    /**
-     * Sets the browser window title for the documentation.
-     *
-     * @param windowTitle A text for the windows title
-     */
-    public void setWindowTitle(String windowTitle) {
-        this.windowTitle = windowTitle;
-    }
-
-    /**
-     * Returns the title for the package index(first) page. Set to {@code null} when there is no document title.
-     */
-    @Input @Optional
-    public String getDocTitle() {
-        return docTitle;
-    }
-
-    /**
-     * Sets title for the package index(first) page (optional).
-     *
-     * @param docTitle the docTitle as HTML
-     */
-    public void setDocTitle(String docTitle) {
-        this.docTitle = docTitle;
-    }
-
-    /**
-     * Returns the HTML header for each page. Set to {@code null} when there is no header.
-     */
-    @Input @Optional
-    public String getHeader() {
-        return header;
-    }
-
-    /**
-     * Sets header text for each page (optional).
-     *
-     * @param header the header as HTML
-     */
-    public void setHeader(String header) {
-        this.header = header;
-    }
-
-    /**
-     * Returns the HTML footer for each page. Set to {@code null} when there is no footer.
-     */
-    @Input @Optional
-    public String getFooter() {
-        return footer;
-    }
-
-    /**
-     * Sets footer text for each page (optional).
-     *
-     * @param footer the footer as HTML
-     */
-    public void setFooter(String footer) {
-        this.footer = footer;
-    }
-
-    /**
-     * Returns a HTML file to be used for overview documentation. Set to {@code null} when there is no overview file.
-     */
-    public String getOverview() {
-        return overview;
-    }
-
-    /**
-     * Sets a HTML file to be used for overview documentation (optional).
-     */
-    public void setOverview(String overview) {
-        this.overview = overview;
-    }
-
-    /**
-     * Returns whether to include all classes and members (i.e. including private ones).
-     */
-    @Input
-    public boolean isIncludePrivate() {
-        return includePrivate;
-    }
-
-    /**
-     * Sets whether to include all classes and members (i.e. including private ones) if set to true.
-     */
-    public void setIncludePrivate(boolean includePrivate) {
-        this.includePrivate = includePrivate;
-    }
-
-    /**
-     * Returns the links to groovydoc/javadoc output at the given URL.
-     */
-    @Input
-    public Set<Link> getLinks() {
-        return Collections.unmodifiableSet(links);
-    }
-
-    /**
-     * Sets links to groovydoc/javadoc output at the given URL.
-     *
-     * @param links The links to set
-     * @see #link(String, String...)
-     */
-    public void setLinks(Set<Link> links) {
-        this.links = links;
-    }
-
-    /**
-     * Add links to groovydoc/javadoc output at the given URL.
-     *
-     * @param url Base URL of external site
-     * @param packages list of package prefixes
-     */
-    public void link(String url, String... packages) {
-        links.add(new Link(url, packages));
-    }
-
-    /**
-     * A Link class represent a link between groovydoc/javadoc output and url.
-     */
-    public static class Link implements Serializable {
-        private List<String> packages = new ArrayList<String>();
-        private String url;
-
-        /**
-         * Constructs a {@code Link}.
-         *
-         * @param url Base URL of external site
-         * @param packages list of package prefixes
-         */
-        public Link(String url, String... packages) {
-            throwExceptionIfNull(url, "Url must not be null");
-            if (packages.length == 0) {
-                throw new InvalidUserDataException("You must specify at least one package!");
-            }
-            for (String aPackage : packages) {
-                throwExceptionIfNull(aPackage, "A package must not be null");
-            }
-            this.packages = Arrays.asList(packages);
-            this.url = url;
-        }
-
-        private void throwExceptionIfNull(String value, String message) {
-            if (value == null) {
-                throw new InvalidUserDataException(message);
-            }
-        }
-
-        /**
-         * Returns a list of package prefixes to be linked with an external site.
-         */
-        public List<String> getPackages() {
-            return Collections.unmodifiableList(packages);
-        }
-
-        /**
-         * Returns the base url for the external site.
-         */
-        public String getUrl() {
-            return url;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Link link = (Link) o;
-
-            if (packages != null ? !packages.equals(link.packages) : link.packages != null) {
-                return false;
-            }
-            if (url != null ? !url.equals(link.url) : link.url != null) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = packages != null ? packages.hashCode() : 0;
-            result = 31 * result + (url != null ? url.hashCode() : 0);
-            return result;
-        }
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
deleted file mode 100644
index 3b04541..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.javadoc;
-
-import groovy.lang.Closure;
-import org.gradle.api.GradleException;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.tasks.*;
-import org.gradle.external.javadoc.MinimalJavadocOptions;
-import org.gradle.external.javadoc.StandardJavadocDocletOptions;
-import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecActionFactory;
-import org.gradle.process.internal.ExecException;
-import org.gradle.util.GUtil;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * <p>Generates HTML API documentation for Java classes.</p>
- * <p>
- * If you create your own Javadoc tasks remember to specify the 'source' property!
- * Without source the Javadoc task will not create any documentation. Example:
- * <pre autoTested=''>
- * apply plugin: 'java'
- *
- * task myJavadocs(type: Javadoc) {
- *   source = sourceSets.main.allJava
- * }
- * </pre>
- *
- * <p>
- * An example how to create a task that runs a custom doclet implementation:
- * <pre autoTested=''>
- * apply plugin: 'java'
- *
- * configurations {
- *   jaxDoclet
- * }
- *
- * dependencies {
- *   //jaxDoclet "some.interesting:Dependency:1.0"
- * }
- *
- * task generateRestApiDocs(type: Javadoc) {
- *   source = sourceSets.main.allJava
- *   destinationDir = reporting.file("rest-api-docs")
- *   options.docletpath = configurations.jaxDoclet.files.asType(List)
- *   options.doclet = "com.lunatech.doclets.jax.jaxrs.JAXRSDoclet"
- *   options.addStringOption("jaxrscontext", "http://localhost:8080/myapp")
- * }
- * </pre>
- */
-public class Javadoc extends SourceTask {
-    private JavadocExecHandleBuilder javadocExecHandleBuilder = new JavadocExecHandleBuilder(getServices().get(ExecActionFactory.class));
-
-    private File destinationDir;
-
-    private boolean failOnError = true;
-
-    private String title;
-
-    private String maxMemory;
-
-    private MinimalJavadocOptions options = new StandardJavadocDocletOptions();
-
-    private FileCollection classpath = getProject().files();
-
-    private String executable;
-
-    @TaskAction
-    protected void generate() {
-        final File destinationDir = getDestinationDir();
-
-        if (options.getDestinationDirectory() == null) {
-            options.destinationDirectory(destinationDir);
-        }
-
-        options.classpath(new ArrayList<File>(getClasspath().getFiles()));
-
-        if (!GUtil.isTrue(options.getWindowTitle()) && GUtil.isTrue(getTitle())) {
-            options.windowTitle(getTitle());
-        }
-        if (options instanceof StandardJavadocDocletOptions) {
-            StandardJavadocDocletOptions docletOptions = (StandardJavadocDocletOptions) options;
-            if (!GUtil.isTrue(docletOptions.getDocTitle()) && GUtil.isTrue(getTitle())) {
-                docletOptions.setDocTitle(getTitle());
-            }
-        }
-
-        if (maxMemory != null) {
-            final List<String> jFlags = options.getJFlags();
-            final Iterator<String> jFlagsIt = jFlags.iterator();
-            boolean containsXmx = false;
-            while (!containsXmx && jFlagsIt.hasNext()) {
-                final String jFlag = jFlagsIt.next();
-                if (jFlag.startsWith("-Xmx")) {
-                    containsXmx = true;
-                }
-            }
-            if (!containsXmx) {
-                options.jFlags("-Xmx" + maxMemory);
-            }
-        }
-
-        List<String> sourceNames = new ArrayList<String>();
-        for (File sourceFile : getSource()) {
-            sourceNames.add(sourceFile.getAbsolutePath());
-        }
-        options.setSourceNames(sourceNames);
-
-        executeExternalJavadoc();
-    }
-
-    private void executeExternalJavadoc() {
-        javadocExecHandleBuilder.setExecutable(executable);
-        javadocExecHandleBuilder.execDirectory(getProject().getRootDir()).options(options).optionsFile(
-                getOptionsFile());
-
-        ExecAction execAction = javadocExecHandleBuilder.getExecHandle();
-        if (!failOnError) {
-            execAction.setIgnoreExitValue(true);
-        }
-
-        try {
-            execAction.execute();
-        } catch (ExecException e) {
-            throw new GradleException("Javadoc generation failed.", e);
-        }
-    }
-
-    void setJavadocExecHandleBuilder(JavadocExecHandleBuilder javadocExecHandleBuilder) {
-        if (javadocExecHandleBuilder == null) {
-            throw new IllegalArgumentException("javadocExecHandleBuilder == null!");
-        }
-        this.javadocExecHandleBuilder = javadocExecHandleBuilder;
-    }
-
-    /**
-     * <p>Returns the directory to generate the documentation into.</p>
-     *
-     * @return The directory.
-     */
-    @OutputDirectory
-    public File getDestinationDir() {
-        return destinationDir;
-    }
-
-    /**
-     * <p>Sets the directory to generate the documentation into.</p>
-     */
-    public void setDestinationDir(File destinationDir) {
-        this.destinationDir = destinationDir;
-    }
-
-    /**
-     * Returns the amount of memory allocated to this task.
-     */
-    public String getMaxMemory() {
-        return maxMemory;
-    }
-
-    /**
-     * Sets the amount of memory allocated to this task.
-     *
-     * @param maxMemory The amount of memory
-     */
-    public void setMaxMemory(String maxMemory) {
-        this.maxMemory = maxMemory;
-    }
-
-    /**
-     * <p>Returns the title for the generated documentation.</p>
-     *
-     * @return The title, possibly null.
-     */
-    @Input
-    @Optional
-    public String getTitle() {
-        return title;
-    }
-
-    /**
-     * <p>Sets the title for the generated documentation.</p>
-     */
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    /**
-     * Returns whether Javadoc generation is accompanied by verbose output.
-     *
-     * @see #setVerbose(boolean)
-     */
-    public boolean isVerbose() {
-        return options.isVerbose();
-    }
-
-    /**
-     * Sets whether Javadoc generation is accompanied by verbose output or not. The verbose output is done via println
-     * (by the underlying Ant task). Thus it is not handled by our logging.
-     *
-     * @param verbose Whether the output should be verbose.
-     */
-    public void setVerbose(boolean verbose) {
-        if (verbose) {
-            options.verbose();
-        }
-    }
-
-    /**
-     * Returns the classpath to use to resolve type references in the source code.
-     *
-     * @return The classpath.
-     */
-    @InputFiles
-    public FileCollection getClasspath() {
-        return classpath;
-    }
-
-    /**
-     * Sets the classpath to use to resolve type references in this source code.
-     *
-     * @param classpath The classpath. Must not be null.
-     */
-    public void setClasspath(FileCollection classpath) {
-        this.classpath = classpath;
-    }
-
-    /**
-     * Returns the Javadoc generation options.
-     *
-     * @return The options. Never returns null.
-     */
-    @Nested
-    public MinimalJavadocOptions getOptions() {
-        return options;
-    }
-
-    /**
-     * Sets the Javadoc generation options.
-     *
-     * @param options The options. Must not be null.
-     */
-    public void setOptions(MinimalJavadocOptions options) {
-        this.options = options;
-    }
-
-    /**
-     * Convenience method for configuring Javadoc generation options.
-     *
-     * @param block The configuration block for Javadoc generation options.
-     */
-    public void options(Closure block) {
-        getProject().configure(getOptions(), block);
-    }
-
-    /**
-     * Specifies whether this task should fail when errors are encountered during Javadoc generation. When {@code true},
-     * this task will fail on Javadoc error. When {@code false}, this task will ignore Javadoc errors.
-     */
-    @Input
-    public boolean isFailOnError() {
-        return failOnError;
-    }
-
-    public void setFailOnError(boolean failOnError) {
-        this.failOnError = failOnError;
-    }
-
-    public File getOptionsFile() {
-        return new File(getTemporaryDir(), "javadoc.options");
-    }
-
-    /**
-     * Returns the Javadoc executable to use to generate the Javadoc. When {@code null}, the Javadoc executable for
-     * the current JVM is used.
-     *
-     * @return The executable. May be null.
-     */
-    @Input @Optional
-    public String getExecutable() {
-        return executable;
-    }
-
-    public void setExecutable(String executable) {
-        this.executable = executable;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
index be295e0..d9bb026 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
@@ -25,6 +25,7 @@ import org.gradle.api.file.FileTree;
 import org.gradle.api.file.FileTreeElement;
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache;
 import org.gradle.api.internal.tasks.options.Option;
 import org.gradle.api.internal.tasks.testing.DefaultTestTaskReports;
 import org.gradle.api.internal.tasks.testing.NoMatchingTestsReporter;
@@ -38,7 +39,9 @@ import org.gradle.api.internal.tasks.testing.junit.report.DefaultTestReport;
 import org.gradle.api.internal.tasks.testing.junit.report.TestReporter;
 import org.gradle.api.internal.tasks.testing.junit.result.*;
 import org.gradle.api.internal.tasks.testing.logging.*;
+import org.gradle.api.internal.tasks.testing.results.StateTrackingTestResultProcessor;
 import org.gradle.api.internal.tasks.testing.results.TestListenerAdapter;
+import org.gradle.api.internal.tasks.testing.results.TestListenerInternal;
 import org.gradle.api.internal.tasks.testing.testng.TestNGTestFramework;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.reporting.DirectoryReport;
@@ -51,10 +54,10 @@ import org.gradle.api.tasks.util.PatternFilterable;
 import org.gradle.api.tasks.util.PatternSet;
 import org.gradle.internal.Factory;
 import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.listener.ClosureBackedMethodInvocationDispatch;
-import org.gradle.listener.ListenerBroadcast;
-import org.gradle.listener.ListenerManager;
 import org.gradle.logging.ConsoleRenderer;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.logging.StyledTextOutputFactory;
@@ -64,7 +67,6 @@ import org.gradle.process.ProcessForkOptions;
 import org.gradle.process.internal.DefaultJavaForkOptions;
 import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.ConfigureUtil;
-import org.gradle.util.DeprecationLogger;
 
 import javax.inject.Inject;
 import java.io.File;
@@ -116,13 +118,12 @@ import java.util.*;
  * </pre>
 
  */
+ at ParallelizableTask
 public class Test extends ConventionTask implements JavaForkOptions, PatternFilterable, VerificationTask, Reporting<TestTaskReports> {
 
     private final ListenerBroadcast<TestListener> testListenerBroadcaster;
     private final ListenerBroadcast<TestOutputListener> testOutputListenerBroadcaster;
-    private final StyledTextOutputFactory textOutputFactory;
-    private final Instantiator instantiator;
-    private final ProgressLoggerFactory progressLoggerFactory;
+    private final ListenerBroadcast<TestListenerInternal> testListenerInternalBroadcaster;
     private final TestLoggingContainer testLogging;
     private final DefaultJavaForkOptions forkOptions;
     private final DefaultTestFilter filter;
@@ -143,20 +144,15 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     @Nested
     private final DefaultTestTaskReports reports;
 
-    @Inject
-    public Test(ListenerManager listenerManager, StyledTextOutputFactory textOutputFactory, FileResolver fileResolver,
-                Factory<WorkerProcessBuilder> processBuilderFactory, ActorFactory actorFactory, Instantiator instantiator,
-                ProgressLoggerFactory progressLoggerFactory) {
-        this.progressLoggerFactory = progressLoggerFactory;
-        this.instantiator = instantiator;
+    public Test() {
+        ListenerManager listenerManager = getListenerManager();
+        testListenerInternalBroadcaster = listenerManager.createAnonymousBroadcaster(TestListenerInternal.class);
         testListenerBroadcaster = listenerManager.createAnonymousBroadcaster(TestListener.class);
         testOutputListenerBroadcaster = listenerManager.createAnonymousBroadcaster(TestOutputListener.class);
-        this.textOutputFactory = textOutputFactory;
-        forkOptions = new DefaultJavaForkOptions(fileResolver);
+        forkOptions = new DefaultJavaForkOptions(getFileResolver());
         forkOptions.setEnableAssertions(true);
-        testExecuter = new DefaultTestExecuter(processBuilderFactory, actorFactory);
+        Instantiator instantiator = getInstantiator();
         testLogging = instantiator.newInstance(DefaultTestLoggingContainer.class, instantiator);
-        testReporter = new DefaultTestReport();
 
         reports = instantiator.newInstance(DefaultTestTaskReports.class, this);
         reports.getJunitXml().setEnabled(true);
@@ -165,6 +161,46 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
         filter = instantiator.newInstance(DefaultTestFilter.class);
     }
 
+    @Inject
+    protected ProgressLoggerFactory getProgressLoggerFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ActorFactory getActorFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ClassLoaderCache getClassLoaderCache() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected Factory<WorkerProcessBuilder> getProcessBuilderFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected StyledTextOutputFactory getTextOutputFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ListenerManager getListenerManager() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * ATM. for testing only
      */
@@ -176,6 +212,18 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
         this.testExecuter = testExecuter;
     }
 
+    ListenerBroadcast<TestListener> getTestListenerBroadcaster() {
+        return testListenerBroadcaster;
+    }
+
+    ListenerBroadcast<TestListenerInternal> getTestListenerInternalBroadcaster() {
+        return testListenerInternalBroadcaster;
+    }
+
+    ListenerBroadcast<TestOutputListener> getTestOutputListenerBroadcaster() {
+        return testOutputListenerBroadcaster;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -444,7 +492,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
         LogLevel currentLevel = getCurrentLogLevel();
         TestLogging levelLogging = testLogging.get(currentLevel);
         TestExceptionFormatter exceptionFormatter = getExceptionFormatter(levelLogging);
-        TestEventLogger eventLogger = new TestEventLogger(textOutputFactory, currentLevel, levelLogging, exceptionFormatter);
+        TestEventLogger eventLogger = new TestEventLogger(getTextOutputFactory(), currentLevel, levelLogging, exceptionFormatter);
         addTestListener(eventLogger);
         addTestOutputListener(eventLogger);
         if (!getFilter().getIncludePatterns().isEmpty()) {
@@ -464,17 +512,24 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
         addTestListener(testReportDataCollector);
         addTestOutputListener(testReportDataCollector);
 
-        TestCountLogger testCountLogger = new TestCountLogger(progressLoggerFactory);
+        TestCountLogger testCountLogger = new TestCountLogger(getProgressLoggerFactory());
         addTestListener(testCountLogger);
 
-        TestResultProcessor resultProcessor = new TestListenerAdapter(
-                getTestListenerBroadcaster().getSource(), testOutputListenerBroadcaster.getSource());
+        testListenerInternalBroadcaster.add(new TestListenerAdapter(testListenerBroadcaster.getSource(), testOutputListenerBroadcaster.getSource()));
+
+        TestResultProcessor resultProcessor = new StateTrackingTestResultProcessor(testListenerInternalBroadcaster.getSource());
+
+        if (testExecuter == null) {
+            testExecuter = new DefaultTestExecuter(getProcessBuilderFactory(), getActorFactory());
+        }
 
         try {
             testExecuter.execute(this, resultProcessor);
         } finally {
+            testExecuter = null;
             testListenerBroadcaster.removeAll();
             testOutputListenerBroadcaster.removeAll();
+            testListenerInternalBroadcaster.removeAll();
             outputWriter.close();
         }
 
@@ -483,6 +538,10 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
         TestResultsProvider testResultsProvider = new InMemoryTestResultsProvider(results.values(), testOutputStore.reader());
 
         try {
+            if (testReporter == null) {
+                testReporter = new DefaultTestReport();
+            }
+
             JUnitXmlReport junitXml = reports.getJunitXml();
             if (junitXml.isEnabled()) {
                 TestOutputAssociation outputAssociation = junitXml.isOutputPerTestCase()
@@ -500,23 +559,15 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
             }
         } finally {
             CompositeStoppable.stoppable(testResultsProvider).stop();
+            testReporter = null;
+            testFramework = null;
         }
 
-        testFramework = null;
-
         if (testCountLogger.hadFailures()) {
             handleTestFailures();
         }
     }
 
-
-    /**
-     * Returns the {@link org.gradle.api.tasks.testing.TestListener} broadcaster.  This broadcaster will send messages to all listeners that have been registered with the ListenerManager.
-     */
-    ListenerBroadcast<TestListener> getTestListenerBroadcaster() {
-        return testListenerBroadcaster;
-    }
-
     /**
      * Registers a test listener with this task. Consider also the following handy methods for quicker hooking into test execution: {@link #beforeTest(groovy.lang.Closure)}, {@link
      * #afterTest(groovy.lang.Closure)}, {@link #beforeSuite(groovy.lang.Closure)}, {@link #afterSuite(groovy.lang.Closure)} <p> This listener will NOT be notified of tests executed by other tasks. To
@@ -717,30 +768,6 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     }
 
     /**
-     * Returns the root folder for the test results in XML format.
-     *
-     * @return the test result directory, containing the test results in XML format.
-     * @deprecated Replaced by {@code getReports().getJunitXml().getDestination()}
-     */
-    @Deprecated
-    public File getTestResultsDir() {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testResultsDir", "Test.getReports().getJunitXml().getDestination()");
-        return reports.getJunitXml().getDestination();
-    }
-
-    /**
-     * Sets the root folder for the test results.
-     *
-     * @param testResultsDir The root folder
-     * @deprecated Replaced by {@code getReports().getJunitXml().setDestination()}
-     */
-    @Deprecated
-    public void setTestResultsDir(File testResultsDir) {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testResultsDir", "Test.getReports().getJunitXml().setDestination()");
-        reports.getJunitXml().setDestination(testResultsDir);
-    }
-
-    /**
      * Returns the root folder for the test results in internal binary format.
      *
      * @return the test result directory, containing the test results in binary format.
@@ -762,30 +789,6 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     }
 
     /**
-     * Returns the root folder for the test reports.
-     *
-     * @return the test report directory, containing the test report mostly in HTML form.
-     * @deprecated Replaced by {@code getReports().getHtml().getDestination()}
-     */
-    @Deprecated
-    public File getTestReportDir() {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReportDir", "Test.getReports().getHtml().getDestination()");
-        return reports.getHtml().getDestination();
-    }
-
-    /**
-     * Sets the root folder for the test reports.
-     *
-     * @param testReportDir The root folder
-     * @deprecated Replaced by {@code getReports().getHtml().setDestination()}
-     */
-    @Deprecated
-    public void setTestReportDir(File testReportDir) {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReportDir", "Test.getReports().getHtml().getDestination()");
-        reports.getHtml().setDestination(testReportDir);
-    }
-
-    /**
      * Returns the include patterns for test execution.
      *
      * @see #include(String...)
@@ -892,7 +895,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     }
 
     /**
-     * Specifies that JUnit should be used to execute the tests. <p> To configure TestNG specific options, see {@link #useJUnit(groovy.lang.Closure)}.
+     * Specifies that JUnit should be used to execute the tests. <p> To configure JUnit specific options, see {@link #useJUnit(groovy.lang.Closure)}.
      */
     public void useJUnit() {
         useJUnit(null);
@@ -905,7 +908,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
      * @param testFrameworkConfigure A closure used to configure the JUnit options.
      */
     public void useJUnit(Closure testFrameworkConfigure) {
-        useTestFramework(new JUnitTestFramework(this, filter), testFrameworkConfigure);
+        useTestFramework(new JUnitTestFramework(this, filter, getClassLoaderCache()), testFrameworkConfigure);
     }
 
     /**
@@ -922,7 +925,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
      * @param testFrameworkConfigure A closure used to configure the TestNG options.
      */
     public void useTestNG(Closure testFrameworkConfigure) {
-        useTestFramework(new TestNGTestFramework(this, this.filter, instantiator), testFrameworkConfigure);
+        useTestFramework(new TestNGTestFramework(this, this.filter, getInstantiator(), getClassLoaderCache()), testFrameworkConfigure);
     }
 
     /**
@@ -938,52 +941,6 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     }
 
     /**
-     * Specifies whether the test HTML report should be generated.
-     *
-     * @deprecated Replaced by {@code getReports().getHtml().isEnabled()}
-     */
-    @Deprecated
-    public boolean isTestReport() {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReport", "Test.getReports().getHtml().isEnabled()");
-        return reports.getHtml().isEnabled();
-    }
-
-    /**
-     * Sets whether the test HTML report should be generated.
-     *
-     * @deprecated Replaced by {@code getReports().getHtml().setEnabled()}
-     */
-    @Deprecated
-    public void setTestReport(boolean testReport) {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReport", "Test.getReports().getHtml().setEnabled()");
-        reports.getHtml().setEnabled(testReport);
-    }
-
-    /**
-     * Enables the HTML test report.
-     *
-     * @deprecated Replaced by {@code getReports().getHtml().setEnabled()}
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    @Deprecated
-    public void enableTestReport() {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReport", "Test.getReports().getHtml().setEnabled()");
-        reports.getHtml().setEnabled(true);
-    }
-
-    /**
-     * Disables the HTML test report.
-     *
-     * @deprecated Replaced by {@code getReports().getHtml().setEnabled()}
-     */
-    @SuppressWarnings("UnusedDeclaration")
-    @Deprecated
-    public void disableTestReport() {
-        DeprecationLogger.nagUserOfReplacedProperty("Test.testReport", "Test.getReports().getHtml().setEnabled()");
-        reports.getHtml().setEnabled(false);
-    }
-
-    /**
      * Returns the directories containing the test source.
      */
     @InputFiles
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
index ebdf2db..6396f29 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.tasks.testing;
 
+import org.gradle.api.Nullable;
 import org.gradle.internal.HasInternalProtocol;
 
 /**
@@ -36,6 +37,7 @@ public interface TestDescriptor {
      *
      * @return The class name. May return null.
      */
+    @Nullable
     String getClassName();
 
     /**
@@ -50,5 +52,6 @@ public interface TestDescriptor {
      *
      * @return The parent of this test. Null if this test has no parent.
      */
+    @Nullable
     TestDescriptor getParent();
 }
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestLogging.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestLogging.java
deleted file mode 100644
index 8751b17..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestLogging.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.tasks.testing;
-
-/**
- * Configures logging of the test execution, e.g. whether the std err / out should be eagerly shown.
- *
- * @deprecated use {@link org.gradle.api.tasks.testing.logging.TestLogging} instead
- */
- at Deprecated
-public interface TestLogging {
-    /**
-     * Whether to show eagerly the standard stream events. Standard output is printed at INFO level, standard error at ERROR level.
-     *
-     * @param standardStreams to configure
-     * @return this logging instance
-     */
-    TestLogging setShowStandardStreams(boolean standardStreams);
-
-    /**
-     * Whether to show eagerly the standard stream events. Standard output is printed at INFO level, standard error at ERROR level.
-     */
-    boolean getShowStandardStreams();
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestReport.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestReport.java
index eb4bb84..a829bd7 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestReport.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestReport.java
@@ -129,6 +129,7 @@ public class TestReport extends DefaultTask {
                 DefaultTestReport testReport = new DefaultTestReport();
                 testReport.generateReport(resultsProvider, getDestinationDir());
             } else {
+                getLogger().info("{} - no binary test results found in dirs: {}.", getPath(), getTestResultDirs().getFiles());
                 setDidWork(false);
             }
         } finally {
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
index 9db7c8d..3a897d2 100755
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
@@ -16,6 +16,8 @@
 
 package org.gradle.api.tasks.testing;
 
+import org.gradle.api.Nullable;
+
 import java.util.List;
 
 /**
@@ -42,11 +44,12 @@ public interface TestResult {
      *
      * @return The exception, if any, logged for this test.  If none, a null is returned.
      */
+    @Nullable
     Throwable getException();
 
     /**
      * If the test failed with any exceptions, this will contain the exceptions.  Some test frameworks do not fail
-     * without an exception (JUnit), so in those cases this method will never return null.
+     * without an exception (JUnit), so in those cases this method will never return an empty list.
      *
      * @return The exceptions, if any, logged for this test.  If none, an empty list is returned.
      */
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLogging.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLogging.java
index a3a82c9..26157dc 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLogging.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLogging.java
@@ -21,7 +21,7 @@ import java.util.Set;
 /**
  * Options that determine which test events get logged, and at which detail.
  */
-public interface TestLogging extends org.gradle.api.tasks.testing.TestLogging {
+public interface TestLogging {
     /**
      * Returns the events to be logged.
      *
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLoggingContainer.java b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLoggingContainer.java
index ca2fed2..74dc615 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLoggingContainer.java
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/logging/TestLoggingContainer.java
@@ -32,11 +32,16 @@ import org.gradle.api.logging.LogLevel;
  *         // set options for log level LIFECYCLE
  *         events "failed"
  *         exceptionFormat "short"
+ *
  *         // set options for log level DEBUG
  *         debug {
  *             events "started", "skipped", "failed"
  *             exceptionFormat "full"
  *         }
+ *
+ *         // remove standard output/error logging from --info builds
+ *         // by assigning only 'failed' and 'skipped' events
+ *         info.events = ["failed", "skipped"]
  *     }
  * }
  * </pre>
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/testng/TestNGOptions.groovy b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/testng/TestNGOptions.groovy
index 9a42a74..54b3d07 100644
--- a/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/testng/TestNGOptions.groovy
+++ b/subprojects/plugins/src/main/groovy/org/gradle/api/tasks/testing/testng/TestNGOptions.groovy
@@ -25,6 +25,7 @@ class TestNGOptions extends TestFrameworkOptions {
 
     static final String JDK_ANNOTATIONS = 'JDK'
     static final String JAVADOC_ANNOTATIONS = 'Javadoc'
+    public static final String DEFAULT_CONFIG_FAILURE_POLICY = "skip";
 
     /**
      * The location to write TestNG's output.
@@ -66,6 +67,12 @@ class TestNGOptions extends TestFrameworkOptions {
     Set<String> excludeGroups = new HashSet<String>()
 
     /**
+     * Option for what to do for other tests that use a configuration step when that step fails.
+     * Can be "skip" or "continue", defaults to "skip".
+     */
+    String configFailurePolicy = DEFAULT_CONFIG_FAILURE_POLICY
+
+    /**
      * Fully qualified classes that are TestNG listeners (instances of org.testng.ITestListener or
      * org.testng.IReporter). By default, the listeners set is empty.
      *
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOfflineLink.java b/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOfflineLink.java
deleted file mode 100755
index e51618f..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOfflineLink.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc;
-
-
-/**
- * This class is used to hold the information that can be provided to the javadoc executable via the -linkoffline
- * option.
- */
-public class JavadocOfflineLink {
-    private final String extDocUrl;
-    private final String packagelistLoc;
-
-    public JavadocOfflineLink(String extDocUrl, String packagelistLoc) {
-        this.extDocUrl = extDocUrl;
-        this.packagelistLoc = packagelistLoc;
-    }
-
-    public String getExtDocUrl() {
-        return extDocUrl;
-    }
-
-    public String getPackagelistLoc() {
-        return packagelistLoc;
-    }
-
-    public String toString() {
-        return extDocUrl + "' '" + packagelistLoc;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java b/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
deleted file mode 100644
index c56c88c..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.internal;
-
-import java.io.IOException;
-import java.util.List;
-
-/**
- * A base class for {@link org.gradle.external.javadoc.JavadocOptionFileOption} implementations whose value is a {@code List}.
- *
- * @param <T> The type which this option represents.
- */
-public abstract class AbstractListJavadocOptionFileOption<T extends List> extends AbstractJavadocOptionFileOption<T> {
-    protected String joinBy;
-
-    protected AbstractListJavadocOptionFileOption(String option, String joinBy) {
-        super(option);
-        this.joinBy = joinBy;
-    }
-
-    protected AbstractListJavadocOptionFileOption(String option, T value, String joinBy) {
-        super(option, value);
-        this.joinBy = joinBy;
-    }
-
-    public T getValue() {
-        return value;
-    }
-
-    public void setValue(T value) {
-        if (value == null) {
-            this.value.clear();
-        } else {
-            this.value = value;
-        }
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if (value != null && !value.isEmpty()) {
-            writeCollectionValue(writerContext);
-        }
-    }
-
-    protected abstract void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException;
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java b/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java
deleted file mode 100644
index 482f96c..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.internal;
-
-import org.gradle.external.javadoc.JavadocOptionFileOption;
-import org.gradle.external.javadoc.OptionLessJavadocOptionFileOption;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-
-public class JavadocOptionFile {
-    private final Map<String, JavadocOptionFileOption> options;
-
-    private final OptionLessJavadocOptionFileOption<List<String>> sourceNames;
-
-    public JavadocOptionFile() {
-        options = new HashMap<String, JavadocOptionFileOption>();
-        sourceNames = new OptionLessStringsJavadocOptionFileOption();
-    }
-
-    public OptionLessJavadocOptionFileOption<List<String>> getSourceNames() {
-        return sourceNames;
-    }
-
-    Map<String, JavadocOptionFileOption> getOptions() {
-        return Collections.unmodifiableMap(options);
-    }
-
-    public <T> JavadocOptionFileOption<T> addOption(JavadocOptionFileOption<T> option) {
-        if (option == null) {
-            throw new IllegalArgumentException("option == null!");
-        }
-
-        options.put(option.getOption(), option);
-
-        return option;
-    }
-
-    public JavadocOptionFileOption<String> addStringOption(String option) {
-        return addStringOption(option, null);
-    }
-
-    public JavadocOptionFileOption<String> addStringOption(String option, String value) {
-        return addOption(new StringJavadocOptionFileOption(option, value));
-    }
-
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option) {
-        return addEnumOption(option, null);
-    }
-
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
-        return addOption(new EnumJavadocOptionFileOption<T>(option, value));
-    }
-
-    public JavadocOptionFileOption<List<File>> addPathOption(String option) {
-        return addPathOption(option, System.getProperty("path.separator"));
-    }
-
-    public JavadocOptionFileOption<List<File>> addPathOption(String option, String joinBy) {
-        return addOption(new PathJavadocOptionFileOption(option, joinBy));
-    }
-
-    public JavadocOptionFileOption<List<String>> addStringsOption(String option) {
-        return addStringsOption(option, System.getProperty("path.separator"));
-    }
-
-    public JavadocOptionFileOption<List<String>> addStringsOption(String option, String joinBy) {
-        return addOption(new StringsJavadocOptionFileOption(option, new ArrayList<String>(), joinBy));
-    }
-
-    public JavadocOptionFileOption<List<String>> addMultilineStringsOption(String option) {
-        return addOption(new MultilineStringsJavadocOptionFileOption(option, new ArrayList<String>()));
-    }
-
-    public JavadocOptionFileOption<Boolean> addBooleanOption(String option) {
-        return addBooleanOption(option, false);
-    }
-
-    public JavadocOptionFileOption<Boolean> addBooleanOption(String option, boolean value) {
-        return addOption(new BooleanJavadocOptionFileOption(option, value));
-    }
-
-    public JavadocOptionFileOption<File> addFileOption(String option) {
-        return addFileOption(option, null);
-    }
-
-    public JavadocOptionFileOption<File> addFileOption(String option, File value) {
-        return addOption(new FileJavadocOptionFileOption(option, value));
-    }
-
-    public void write(File optionFile) throws IOException {
-        if (optionFile == null) {
-            throw new IllegalArgumentException("optionFile == null!");
-        }
-
-        final JavadocOptionFileWriter optionFileWriter = new JavadocOptionFileWriter(this);
-
-        optionFileWriter.write(optionFile);
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java b/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
deleted file mode 100644
index 40e5e67..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.internal;
-
-import org.gradle.internal.ErroringAction;
-import org.gradle.internal.IoActions;
-import org.gradle.external.javadoc.JavadocOptionFileOption;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class JavadocOptionFileWriter {
-    private final JavadocOptionFile optionFile;
-
-    public JavadocOptionFileWriter(JavadocOptionFile optionFile) {
-        if (optionFile == null) {
-            throw new IllegalArgumentException("optionFile == null!");
-        }
-        this.optionFile = optionFile;
-    }
-
-    void write(File outputFile) throws IOException {
-        IoActions.writeTextFile(outputFile, new ErroringAction<BufferedWriter>() {
-            @Override
-            protected void doExecute(BufferedWriter writer) throws Exception {
-                final Map<String, JavadocOptionFileOption> options = new TreeMap<String, JavadocOptionFileOption>(optionFile.getOptions());
-                JavadocOptionFileWriterContext writerContext = new JavadocOptionFileWriterContext(writer);
-
-                JavadocOptionFileOption localeOption = options.remove("locale");
-                if (localeOption != null) {
-                    localeOption.write(writerContext);
-                }
-
-                for (final String option : options.keySet()) {
-                    options.get(option).write(writerContext);
-                }
-
-                optionFile.getSourceNames().write(writerContext);
-            }
-        });
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java b/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
deleted file mode 100644
index 0888bf7..0000000
--- a/subprojects/plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.internal;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Iterator;
-
-public class JavadocOptionFileWriterContext {
-    private final BufferedWriter writer;
-
-    public JavadocOptionFileWriterContext(BufferedWriter writer) {
-        this.writer = writer;
-    }
-
-    public JavadocOptionFileWriterContext write(String string) throws IOException {
-        writer.write(string);
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext newLine() throws IOException {
-        writer.newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeOptionHeader(String option) throws IOException {
-        write("-");
-        write(option);
-        write(" ");
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeOption(String option) throws IOException {
-        writeOptionHeader(option);
-        newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValueOption(String option, String value) throws IOException {
-        writeOptionHeader(option);
-        writeValue(value);
-        newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValue(String value) throws IOException {
-        write("\'");
-        write(value.replaceAll("\\\\", "\\\\\\\\"));
-        write("\'");
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValueOption(String option, Collection<String> values) throws IOException {
-        for (final String value : values) {
-            writeValueOption(option, value);
-        }
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValuesOption(String option, Collection<String> values, String joinValuesBy) throws IOException {
-        StringBuilder builder = new StringBuilder();
-        Iterator<String> valuesIt = values.iterator();
-        while (valuesIt.hasNext()) {
-            builder.append(valuesIt.next());
-            if (valuesIt.hasNext()) {
-                builder.append(joinValuesBy);
-            }
-        }
-        writeValueOption(option, builder.toString());
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeMultilineValuesOption(String option, Collection<String> values) throws IOException {
-        for (String value : values) {
-            writeValueOption(option, value);
-        }
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writePathOption(String option, Collection<File> files, String joinValuesBy) throws IOException {
-        StringBuilder builder = new StringBuilder();
-        Iterator<File> filesIt = files.iterator();
-        while (filesIt.hasNext()) {
-            builder.append(filesIt.next().getAbsolutePath());
-            if (filesIt.hasNext()) {
-                builder.append(joinValuesBy);
-            }
-        }
-        writeValueOption(option, builder.toString());
-        return this;
-    }
-}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/jvm/ClassDirectoryBinarySpec.java b/subprojects/plugins/src/main/groovy/org/gradle/jvm/ClassDirectoryBinarySpec.java
new file mode 100644
index 0000000..6ab792b
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/jvm/ClassDirectoryBinarySpec.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.jvm;
+
+import org.gradle.api.Incubating;
+import org.gradle.internal.HasInternalProtocol;
+
+/**
+ * An exploded binary containing resources and compiled class files.
+ */
+// TODO: maybe we need to allow additional dirs like SourceSetOutput does
+// (esp. for backwards compatibility). Wonder if it's still necessary to distinguish
+// between classes and resources dirs, instead of just maintaining a collection of dirs.
+// As far as generated resources are concerned, it might be better to model
+// them as an additional (Buildable) ResourceSet.
+ at Incubating @HasInternalProtocol
+public interface ClassDirectoryBinarySpec extends JvmBinarySpec {
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/CreateStartScripts.java b/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/CreateStartScripts.java
new file mode 100644
index 0000000..5591bd1
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/CreateStartScripts.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.application.tasks;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.internal.plugins.StartScriptGenerator;
+import org.gradle.api.internal.plugins.UnixStartScriptGenerator;
+import org.gradle.api.internal.plugins.WindowsStartScriptGenerator;
+import org.gradle.api.tasks.*;
+import org.gradle.jvm.application.scripts.ScriptGenerator;
+import org.gradle.util.GUtil;
+
+import java.io.File;
+
+/**
+ * Creates start scripts for launching JVM applications.
+ * <p>
+ * Example:
+ * <pre autoTested=''>
+ * task createStartScripts(type: CreateStartScripts) {
+ *   outputDir = file('build/sample')
+ *   mainClassName = 'org.gradle.test.Main'
+ *   applicationName = 'myApp'
+ *   classpath = files('path/to/some.jar')
+ * }
+ * </pre>
+ * <p>
+ * Note: the Gradle {@code "application"} plugin adds a pre-configured task of this type named {@code "createStartScripts"}.
+ * <p>
+ * The task generates separate scripts targeted at Microsoft Windows environments and UNIX-like environments (e.g. Linux, Mac OS X).
+ * The actual generation is implemented by the {@link #getWindowsStartScriptGenerator()} and {@link #getUnixStartScriptGenerator()} properties, of type {@link ScriptGenerator}.
+ * <p>
+ * Example:
+ * <pre autoTested=''>
+ * task createStartScripts(type: CreateStartScripts) {
+ *   unixStartScriptGenerator = new CustomUnixStartScriptGenerator()
+ *   windowsStartScriptGenerator = new CustomWindowsStartScriptGenerator()
+ * }
+ *
+ * class CustomUnixStartScriptGenerator implements ScriptGenerator {
+ *   void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination) {
+ *     // implementation
+ *   }
+ * }
+ *
+ * class CustomWindowsStartScriptGenerator implements ScriptGenerator {
+ *   void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination) {
+ *     // implementation
+ *   }
+ * }
+ * </pre>
+ * <p>
+ * The default generators are of the type {@link org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator}, with default templates.
+ * This templates can be changed via the {@link org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator#setTemplate(org.gradle.api.resources.TextResource)} method.
+ * <p>
+ * The default implementations used by this task use <a href="http://docs.groovy-lang.org/latest/html/documentation/template-engines.html#_simpletemplateengine">Groovy's SimpleTemplateEngine</a>
+ * to parse the template, with the following variables available:
+ * <p>
+ * <ul>
+ * <li>{@code applicationName}</li>
+ * <li>{@code optsEnvironmentVar}</li>
+ * <li>{@code exitEnvironmentVar}</li>
+ * <li>{@code mainClassName}</li>
+ * <li>{@code defaultJvmOpts}</li>
+ * <li>{@code appNameSystemProperty}</li>
+ * <li>{@code appHomeRelativePath}</li>
+ * <li>{@code classpath}</li>
+ * </ul>
+ * <p>
+ * Example:
+ * <p>
+ * <pre>
+ * task createStartScripts(type: CreateStartScripts) {
+ *   unixStartScriptGenerator.template = resources.text.fromFile('customUnixStartScript.txt')
+ *   windowsStartScriptGenerator.template = resources.text.fromFile('customWindowsStartScript.txt')
+ * }
+ * </pre>
+ */
+public class CreateStartScripts extends ConventionTask {
+
+    private File outputDir;
+    private String mainClassName;
+    private Iterable<String> defaultJvmOpts = Lists.newLinkedList();
+    private String applicationName;
+    private String optsEnvironmentVar;
+    private String exitEnvironmentVar;
+    private FileCollection classpath;
+    private ScriptGenerator unixStartScriptGenerator = new UnixStartScriptGenerator();
+    private ScriptGenerator windowsStartScriptGenerator = new WindowsStartScriptGenerator();
+
+    /**
+     * The environment variable to use to provide additional options to the JVM.
+     */
+    @Input
+    @Optional
+    public String getOptsEnvironmentVar() {
+        if (GUtil.isTrue(optsEnvironmentVar)) {
+            return optsEnvironmentVar;
+        }
+
+        if (!GUtil.isTrue(getApplicationName())) {
+            return null;
+        }
+
+        return GUtil.toConstant(getApplicationName()) + "_OPTS";
+    }
+
+    /**
+     * The environment variable to use to control exit value (Windows only).
+     */
+    @Input
+    @Optional
+    public String getExitEnvironmentVar() {
+        if (GUtil.isTrue(exitEnvironmentVar)) {
+            return exitEnvironmentVar;
+        }
+
+        if (!GUtil.isTrue(getApplicationName())) {
+            return null;
+        }
+
+        return GUtil.toConstant(getApplicationName()) + "_EXIT_CONSOLE";
+    }
+
+    /**
+     * Returns the full path to the Unix script. The target directory is represented by the output directory, the file name is the application name without a file extension.
+     */
+    public File getUnixScript() {
+        return new File(getOutputDir(), getApplicationName());
+    }
+
+    /**
+     * Returns the full path to the Windows script. The target directory is represented by the output directory, the file name is the application name plus the file extension .bat.
+     */
+    public File getWindowsScript() {
+        return new File(getOutputDir(), getApplicationName() + ".bat");
+    }
+
+    /**
+     * The directory to write the scripts into.
+     */
+    @OutputDirectory
+    public File getOutputDir() {
+        return outputDir;
+    }
+
+    public void setOutputDir(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+    /**
+     * The main classname used to start the Java application.
+     */
+    @Input
+    public String getMainClassName() {
+        return mainClassName;
+    }
+
+    public void setMainClassName(String mainClassName) {
+        this.mainClassName = mainClassName;
+    }
+
+    /**
+     * The application's default JVM options. Defaults to an empty list.
+     */
+    @Input
+    @Optional
+    public Iterable<String> getDefaultJvmOpts() {
+        return defaultJvmOpts;
+    }
+
+    public void setDefaultJvmOpts(Iterable<String> defaultJvmOpts) {
+        this.defaultJvmOpts = defaultJvmOpts;
+    }
+
+    /**
+     * The application's name.
+     */
+    @Input
+    public String getApplicationName() {
+        return applicationName;
+    }
+
+    public void setApplicationName(String applicationName) {
+        this.applicationName = applicationName;
+    }
+
+    public void setOptsEnvironmentVar(String optsEnvironmentVar) {
+        this.optsEnvironmentVar = optsEnvironmentVar;
+    }
+
+    public void setExitEnvironmentVar(String exitEnvironmentVar) {
+        this.exitEnvironmentVar = exitEnvironmentVar;
+    }
+
+    /**
+     * The class path for the application.
+     */
+    @InputFiles
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * The UNIX-like start script generator.
+     * <p>
+     * Defaults to an implementation of {@link org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator}.
+     */
+    @Incubating
+    public ScriptGenerator getUnixStartScriptGenerator() {
+        return unixStartScriptGenerator;
+    }
+
+    public void setUnixStartScriptGenerator(ScriptGenerator unixStartScriptGenerator) {
+        this.unixStartScriptGenerator = unixStartScriptGenerator;
+    }
+
+    /**
+     * The Windows start script generator.
+     * <p>
+     * Defaults to an implementation of {@link org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator}.
+     */
+    @Incubating
+    public ScriptGenerator getWindowsStartScriptGenerator() {
+        return windowsStartScriptGenerator;
+    }
+
+    public void setWindowsStartScriptGenerator(ScriptGenerator windowsStartScriptGenerator) {
+        this.windowsStartScriptGenerator = windowsStartScriptGenerator;
+    }
+
+    @TaskAction
+    public void generate() {
+        StartScriptGenerator generator = new StartScriptGenerator(unixStartScriptGenerator, windowsStartScriptGenerator);
+        generator.setApplicationName(getApplicationName());
+        generator.setMainClassName(getMainClassName());
+        generator.setDefaultJvmOpts(getDefaultJvmOpts());
+        generator.setOptsEnvironmentVar(getOptsEnvironmentVar());
+        generator.setExitEnvironmentVar(getExitEnvironmentVar());
+        generator.setClasspath(getRelativeClasspath());
+        generator.setScriptRelPath("bin/" + getUnixScript().getName());
+        generator.generateUnixScript(getUnixScript());
+        generator.generateWindowsScript(getWindowsScript());
+    }
+
+    private Iterable<String> getRelativeClasspath() {
+        return Iterables.transform(getClasspath().getFiles(), new Function<File, String>() {
+            @Override
+            public String apply(File input) {
+                return "lib/" + input.getName();
+            }
+        });
+    }
+
+}
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/package-info.java b/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/package-info.java
new file mode 100644
index 0000000..9889d1e
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/jvm/application/tasks/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 the JVM application plugin.
+ */
+package org.gradle.jvm.application.tasks;
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/groovy/org/gradle/jvm/package-info.java b/subprojects/plugins/src/main/groovy/org/gradle/jvm/package-info.java
new file mode 100644
index 0000000..d2ccf28
--- /dev/null
+++ b/subprojects/plugins/src/main/groovy/org/gradle/jvm/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for adapting the legacy plugins to the new component model.
+ */
+package org.gradle.jvm;
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultJavaAppStartScriptGenerationDetails.java b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultJavaAppStartScriptGenerationDetails.java
new file mode 100644
index 0000000..9f6fc76
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultJavaAppStartScriptGenerationDetails.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.jvm.application.scripts.JavaAppStartScriptGenerationDetails;
+
+import java.util.List;
+
+public final class DefaultJavaAppStartScriptGenerationDetails implements JavaAppStartScriptGenerationDetails {
+
+    private final String applicationName;
+    private final String optsEnvironmentVar;
+    private final String exitEnvironmentVar;
+    private final String mainClassName;
+    private final List<String> defaultJvmOpts;
+    private final List<String> classpath;
+    private final String scriptRelPath;
+    private final String appNameSystemProperty;
+
+    public DefaultJavaAppStartScriptGenerationDetails(String applicationName, String optsEnvironmentVar, String exitEnvironmentVar, String mainClassName, List<String> defaultJvmOpts,
+                                                      List<String> classpath, String scriptRelPath, String appNameSystemProperty) {
+        this.applicationName = applicationName;
+        this.optsEnvironmentVar = optsEnvironmentVar;
+        this.exitEnvironmentVar = exitEnvironmentVar;
+        this.mainClassName = mainClassName;
+        this.defaultJvmOpts = defaultJvmOpts;
+        this.classpath = classpath;
+        this.scriptRelPath = scriptRelPath;
+        this.appNameSystemProperty = appNameSystemProperty;
+    }
+
+    public String getApplicationName() {
+        return applicationName;
+    }
+
+    public String getOptsEnvironmentVar() {
+        return optsEnvironmentVar;
+    }
+
+    public String getExitEnvironmentVar() {
+        return exitEnvironmentVar;
+    }
+
+    public String getMainClassName() {
+        return mainClassName;
+    }
+
+    public List<String> getDefaultJvmOpts() {
+        return defaultJvmOpts;
+    }
+
+    public List<String> getClasspath() {
+        return classpath;
+    }
+
+    public String getScriptRelPath() {
+        return scriptRelPath;
+    }
+
+    public String getAppNameSystemProperty() {
+        return appNameSystemProperty;
+    }
+
+    @SuppressWarnings("RedundantIfStatement")
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultJavaAppStartScriptGenerationDetails that = (DefaultJavaAppStartScriptGenerationDetails) o;
+
+        if (appNameSystemProperty != null ? !appNameSystemProperty.equals(that.appNameSystemProperty) : that.appNameSystemProperty != null) {
+            return false;
+        }
+        if (applicationName != null ? !applicationName.equals(that.applicationName) : that.applicationName != null) {
+            return false;
+        }
+        if (classpath != null ? !classpath.equals(that.classpath) : that.classpath != null) {
+            return false;
+        }
+        if (defaultJvmOpts != null ? !defaultJvmOpts.equals(that.defaultJvmOpts) : that.defaultJvmOpts != null) {
+            return false;
+        }
+        if (exitEnvironmentVar != null ? !exitEnvironmentVar.equals(that.exitEnvironmentVar) : that.exitEnvironmentVar != null) {
+            return false;
+        }
+        if (mainClassName != null ? !mainClassName.equals(that.mainClassName) : that.mainClassName != null) {
+            return false;
+        }
+        if (optsEnvironmentVar != null ? !optsEnvironmentVar.equals(that.optsEnvironmentVar) : that.optsEnvironmentVar != null) {
+            return false;
+        }
+        if (scriptRelPath != null ? !scriptRelPath.equals(that.scriptRelPath) : that.scriptRelPath != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = applicationName != null ? applicationName.hashCode() : 0;
+        result = 31 * result + (optsEnvironmentVar != null ? optsEnvironmentVar.hashCode() : 0);
+        result = 31 * result + (exitEnvironmentVar != null ? exitEnvironmentVar.hashCode() : 0);
+        result = 31 * result + (mainClassName != null ? mainClassName.hashCode() : 0);
+        result = 31 * result + (defaultJvmOpts != null ? defaultJvmOpts.hashCode() : 0);
+        result = 31 * result + (classpath != null ? classpath.hashCode() : 0);
+        result = 31 * result + (scriptRelPath != null ? scriptRelPath.hashCode() : 0);
+        result = 31 * result + (appNameSystemProperty != null ? appNameSystemProperty.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultTemplateBasedStartScriptGenerator.java b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultTemplateBasedStartScriptGenerator.java
new file mode 100644
index 0000000..852c2a5
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/DefaultTemplateBasedStartScriptGenerator.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.io.CharSource;
+import groovy.text.SimpleTemplateEngine;
+import groovy.text.Template;
+import org.gradle.api.Transformer;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.internal.resources.CharSourceBackedTextResource;
+import org.gradle.api.resources.TextResource;
+import org.gradle.internal.io.IoUtils;
+import org.gradle.internal.resource.CharsetUtil;
+import org.gradle.jvm.application.scripts.JavaAppStartScriptGenerationDetails;
+import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator;
+import org.gradle.util.TextUtil;
+
+import java.io.*;
+import java.util.Map;
+
+public class DefaultTemplateBasedStartScriptGenerator implements TemplateBasedScriptGenerator {
+
+    private final String lineSeparator;
+    private final Transformer<Map<String, String>, JavaAppStartScriptGenerationDetails> bindingFactory;
+
+    private TextResource template;
+
+    public DefaultTemplateBasedStartScriptGenerator(String lineSeparator, Transformer<Map<String, String>, JavaAppStartScriptGenerationDetails> bindingFactory, TextResource template) {
+        this.lineSeparator = lineSeparator;
+        this.bindingFactory = bindingFactory;
+        this.template = template;
+    }
+
+    public void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination) {
+        try {
+            Map<String, String> binding = bindingFactory.transform(details);
+            String scriptContent = generateStartScriptContentFromTemplate(binding);
+            destination.write(scriptContent);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    public void setTemplate(TextResource template) {
+        this.template = template;
+    }
+
+    public TextResource getTemplate() {
+        return template;
+    }
+
+    private String generateStartScriptContentFromTemplate(final Map<String, String> binding) {
+        return IoUtils.get(getTemplate().asReader(), new Transformer<String, Reader>() {
+            @Override
+            public String transform(Reader reader) {
+                try {
+                    SimpleTemplateEngine engine = new SimpleTemplateEngine();
+                    Template template = engine.createTemplate(reader);
+                    String output = template.make(binding).toString();
+                    return TextUtil.convertLineSeparators(output, lineSeparator);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        });
+    }
+
+    protected static TextResource utf8ClassPathResource(final Class<?> clazz, final String filename) {
+        return new CharSourceBackedTextResource(new CharSource() {
+            @Override
+            public Reader openStream() throws IOException {
+                InputStream stream = clazz.getResourceAsStream(filename);
+                if (stream == null) {
+                    throw new IllegalStateException("Could not find class path resource " + filename + " relative to " + clazz.getName());
+                }
+                return new BufferedReader(new InputStreamReader(stream, CharsetUtil.UTF_8));
+            }
+        });
+    }
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/StartScriptTemplateBindingFactory.java b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/StartScriptTemplateBindingFactory.java
new file mode 100644
index 0000000..4bd42e6
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/StartScriptTemplateBindingFactory.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Transformer;
+import org.gradle.jvm.application.scripts.JavaAppStartScriptGenerationDetails;
+import org.gradle.util.CollectionUtils;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class StartScriptTemplateBindingFactory implements Transformer<Map<String, String>, JavaAppStartScriptGenerationDetails> {
+
+    private final boolean windows;
+
+    private StartScriptTemplateBindingFactory(boolean windows) {
+        this.windows = windows;
+    }
+
+    public static StartScriptTemplateBindingFactory windows() {
+        return new StartScriptTemplateBindingFactory(true);
+    }
+
+    public static StartScriptTemplateBindingFactory unix() {
+        return new StartScriptTemplateBindingFactory(false);
+    }
+
+    @Override
+    public Map<String, String> transform(JavaAppStartScriptGenerationDetails details) {
+        Map<String, String> binding = new HashMap<String, String>();
+        binding.put(ScriptBindingParameter.APP_NAME.getKey(), details.getApplicationName());
+        binding.put(ScriptBindingParameter.OPTS_ENV_VAR.getKey(), details.getOptsEnvironmentVar());
+        binding.put(ScriptBindingParameter.EXIT_ENV_VAR.getKey(), details.getExitEnvironmentVar());
+        binding.put(ScriptBindingParameter.MAIN_CLASSNAME.getKey(), details.getMainClassName());
+        binding.put(ScriptBindingParameter.DEFAULT_JVM_OPTS.getKey(), createJoinedDefaultJvmOpts(details.getDefaultJvmOpts()));
+        binding.put(ScriptBindingParameter.APP_NAME_SYS_PROP.getKey(), details.getAppNameSystemProperty());
+        binding.put(ScriptBindingParameter.APP_HOME_REL_PATH.getKey(), createJoinedAppHomeRelativePath(details.getScriptRelPath()));
+        binding.put(ScriptBindingParameter.CLASSPATH.getKey(), createJoinedClasspath(details.getClasspath()));
+        return binding;
+
+    }
+
+    private String createJoinedClasspath(Iterable<String> classpath) {
+        if (windows) {
+            return Joiner.on(";").join(Iterables.transform(classpath, new Function<String, String>() {
+                public String apply(String input) {
+                    return "%APP_HOME%\\" + input.replace("/", "\\");
+                }
+            }));
+        } else {
+            return Joiner.on(":").join(Iterables.transform(classpath, new Function<String, String>() {
+                public String apply(String input) {
+                    return "$APP_HOME/" + input.replace("\\", "/");
+                }
+            }));
+        }
+    }
+
+    private String createJoinedDefaultJvmOpts(Iterable<String> defaultJvmOpts) {
+        if (windows) {
+            if (defaultJvmOpts == null) {
+                return "";
+            }
+
+            Iterable<String> quotedDefaultJvmOpts = Iterables.transform(CollectionUtils.toStringList(defaultJvmOpts), new Function<String, String>() {
+                public String apply(String jvmOpt) {
+                    return "\"" + escapeWindowsJvmOpt(jvmOpt) + "\"";
+                }
+            });
+
+            Joiner spaceJoiner = Joiner.on(" ");
+            return spaceJoiner.join(quotedDefaultJvmOpts);
+        } else {
+            if (defaultJvmOpts == null) {
+                return "";
+            }
+
+            Iterable<String> quotedDefaultJvmOpts = Iterables.transform(CollectionUtils.toStringList(defaultJvmOpts), new Function<String, String>() {
+                public String apply(String jvmOpt) {
+                    //quote ', ", \, $. Probably not perfect. TODO: identify non-working cases, fail-fast on them
+                    jvmOpt = jvmOpt.replace("\\", "\\\\");
+                    jvmOpt = jvmOpt.replace("\"", "\\\"");
+                    jvmOpt = jvmOpt.replace("'", "'\"'\"'");
+                    jvmOpt = jvmOpt.replace("`", "'\"`\"'");
+                    jvmOpt = jvmOpt.replace("$", "\\$");
+                    return "\"" + jvmOpt + "\"";
+                }
+            });
+
+            //put the whole arguments string in single quotes, unless defaultJvmOpts was empty,
+            // in which case we output "" to stay compatible with existing builds that scan the script for it
+            Joiner spaceJoiner = Joiner.on(" ");
+            if (Iterables.size(quotedDefaultJvmOpts) > 0) {
+                return "'" + spaceJoiner.join(quotedDefaultJvmOpts) + "'";
+            }
+
+            return "\"\"";
+        }
+    }
+
+    private String escapeWindowsJvmOpt(String jvmOpts) {
+        boolean wasOnBackslash = false;
+        StringBuilder escapedJvmOpt = new StringBuilder();
+        CharacterIterator it = new StringCharacterIterator(jvmOpts);
+
+        //argument quoting:
+        // - " must be encoded as \"
+        // - % must be encoded as %%
+        // - pathological case: \" must be encoded as \\\", but other than that, \ MUST NOT be quoted
+        // - other characters (including ') will not be quoted
+        // - use a state machine rather than regexps
+        for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
+            String repl = Character.toString(ch);
+
+            if (ch == '%') {
+                repl = "%%";
+            } else if (ch == '"') {
+                repl = (wasOnBackslash ? '\\' : "") + "\\\"";
+            }
+            wasOnBackslash = ch == '\\';
+            escapedJvmOpt.append(repl);
+        }
+
+        return escapedJvmOpt.toString();
+    }
+
+    private static enum ScriptBindingParameter {
+        APP_NAME("applicationName"),
+        OPTS_ENV_VAR("optsEnvironmentVar"),
+        EXIT_ENV_VAR("exitEnvironmentVar"),
+        MAIN_CLASSNAME("mainClassName"),
+        DEFAULT_JVM_OPTS("defaultJvmOpts"),
+        APP_NAME_SYS_PROP("appNameSystemProperty"),
+        APP_HOME_REL_PATH("appHomeRelativePath"),
+        CLASSPATH("classpath");
+
+        private final String key;
+
+        private ScriptBindingParameter(String key) {
+            this.key = key;
+        }
+
+        public String getKey() {
+            return key;
+        }
+    }
+
+    String createJoinedAppHomeRelativePath(String scriptRelPath) {
+        int depth = StringUtils.countMatches(scriptRelPath, "/");
+        if (depth == 0) {
+            return "";
+        }
+
+        List<String> appHomeRelativePath = new ArrayList<String>(depth);
+        for (int i = 0; i < depth; i++) {
+            appHomeRelativePath.add("..");
+        }
+
+        return Joiner.on(windows ? "\\" : "/").join(appHomeRelativePath);
+    }
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/UnixStartScriptGenerator.java b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/UnixStartScriptGenerator.java
new file mode 100644
index 0000000..5a6f010
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/UnixStartScriptGenerator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.TextUtil;
+
+public class UnixStartScriptGenerator extends DefaultTemplateBasedStartScriptGenerator {
+
+    public UnixStartScriptGenerator() {
+        super(
+                TextUtil.getUnixLineSeparator(),
+                StartScriptTemplateBindingFactory.unix(),
+                utf8ClassPathResource(UnixStartScriptGenerator.class, "unixStartScript.txt")
+        );
+    }
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/WindowsStartScriptGenerator.java b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/WindowsStartScriptGenerator.java
new file mode 100644
index 0000000..f98913b
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/internal/plugins/WindowsStartScriptGenerator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.TextUtil;
+
+public class WindowsStartScriptGenerator extends DefaultTemplateBasedStartScriptGenerator {
+    public WindowsStartScriptGenerator() {
+        super(
+                TextUtil.getWindowsLineSeparator(),
+                StartScriptTemplateBindingFactory.windows(),
+                utf8ClassPathResource(UnixStartScriptGenerator.class, "windowsStartScript.txt")
+        );
+    }
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/JavaAppStartScriptGenerationDetails.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/JavaAppStartScriptGenerationDetails.java
new file mode 100644
index 0000000..3ec220c
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/JavaAppStartScriptGenerationDetails.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.application.scripts;
+
+import org.gradle.api.Incubating;
+
+import java.util.List;
+
+/**
+ * Details for generating Java-based application start scripts.
+ */
+ at Incubating
+public interface JavaAppStartScriptGenerationDetails {
+
+    /**
+     * The display name of the application
+     */
+    String getApplicationName();
+
+    /**
+     * The environment variable to use to provide additional options to the JVM
+     */
+    String getOptsEnvironmentVar();
+
+    /**
+     * The environment variable to use to control exit value (windows only)
+     */
+    String getExitEnvironmentVar();
+
+    String getMainClassName();
+
+    List<String> getDefaultJvmOpts();
+
+    /**
+     * The classpath, relative to the application home directory.
+     */
+    List<String> getClasspath();
+
+    /**
+     * The path of the script, relative to the application home directory.
+     */
+    String getScriptRelPath();
+
+    /**
+     * This system property to use to pass the script name to the application. May be null.
+     */
+    String getAppNameSystemProperty();
+}
+
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/ScriptGenerator.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/ScriptGenerator.java
new file mode 100644
index 0000000..6be5740
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/ScriptGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.application.scripts;
+
+import org.gradle.api.Incubating;
+
+import java.io.Writer;
+
+/**
+ * Generates a script to start a JVM application.
+ *
+ * @see TemplateBasedScriptGenerator
+ */
+ at Incubating
+public interface ScriptGenerator {
+
+    /**
+     * Generate the script.
+     * <p>
+     * Implementations should not close the given writer.
+     * It is the responsibility of the caller to close the stream.
+     *
+     * @param details the application details
+     * @param destination the script destination
+     */
+    void generateScript(JavaAppStartScriptGenerationDetails details, Writer destination);
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/TemplateBasedScriptGenerator.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/TemplateBasedScriptGenerator.java
new file mode 100644
index 0000000..717bf80
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/TemplateBasedScriptGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.jvm.application.scripts;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.resources.TextResource;
+
+/**
+ * Interface for generating scripts with the provided details based on a provided template.
+ */
+ at Incubating
+public interface TemplateBasedScriptGenerator extends ScriptGenerator {
+
+    /**
+     * Sets the template text resource used for generating script.
+     *
+     * @param template Template text resource
+     */
+    void setTemplate(TextResource template);
+
+    /**
+     * Gets the template reader used for generating script.
+     *
+     * @return Template reader
+     */
+    TextResource getTemplate();
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/package-info.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/package-info.java
new file mode 100644
index 0000000..5111390
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/scripts/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 enable JVM application script generation.
+ */
+package org.gradle.jvm.application.scripts;
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/github-dependencies.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/github-dependencies.properties
deleted file mode 100644
index 6846864..0000000
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/github-dependencies.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.api.plugins.github.GitHubDependenciesPlugin
\ No newline at end of file
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-lang.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-lang.properties
deleted file mode 100644
index 6365cfa..0000000
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-lang.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.api.plugins.JavaLanguagePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/jvm-lang.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/jvm-lang.properties
deleted file mode 100644
index 6a30765..0000000
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/jvm-lang.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.language.jvm.plugins.JvmLanguagePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/lang-base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/lang-base.properties
deleted file mode 100644
index c8d282e..0000000
--- a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/lang-base.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.language.base.plugins.LanguageBasePlugin
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/application.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.application.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/application.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.application.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.base.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/base.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.base.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/distribution.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.distribution.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/distribution.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.distribution.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.groovy-base.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy-base.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.groovy-base.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.groovy.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/groovy.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.groovy.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java-base.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-base.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java-base.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-library-distribution.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java-library-distribution.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java-library-distribution.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java-library-distribution.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/java.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.java.properties
diff --git a/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties b/subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.war.properties
similarity index 100%
rename from subprojects/plugins/src/main/resources/META-INF/gradle-plugins/war.properties
rename to subprojects/plugins/src/main/resources/META-INF/gradle-plugins/org.gradle.war.properties
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css
index 346dff8..3dc4913 100644
--- a/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/internal/tasks/testing/junit/report/style.css
@@ -41,7 +41,6 @@
     border: solid 2px #d0d0d0;
     -moz-border-radius: 10px;
     border-radius: 10px;
-    behavior: url(htc/css3-pie-1.0beta3.htc);
 }
 
 #successRate {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/distribution/plugins/DistributionPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/distribution/plugins/DistributionPluginTest.groovy
index 33c2149..e9c75c7 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/distribution/plugins/DistributionPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/distribution/plugins/DistributionPluginTest.groovy
@@ -16,20 +16,24 @@
 
 package org.gradle.api.distribution.plugins
 
+import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.distribution.DistributionContainer
 import org.gradle.api.tasks.Sync
+import org.gradle.api.tasks.TaskDependencyMatchers
 import org.gradle.api.tasks.bundling.Zip
 import org.gradle.api.tasks.bundling.Tar
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
+import static org.gradle.util.Matchers.dependsOn
+
 class DistributionPluginTest extends Specification {
     private final Project project = TestUtil.builder().withName("test-project").build()
 
     def "adds convention object and a main distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         then:
         def distributions = project.extensions.getByType(DistributionContainer.class)
@@ -40,7 +44,7 @@ class DistributionPluginTest extends Specification {
 
     def "provides default values for additional distributions"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         then:
         def distributions = project.extensions.getByType(DistributionContainer.class)
@@ -51,7 +55,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds distZip task for main distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         then:
         def task = project.tasks.distZip
@@ -61,7 +65,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds distZip task for custom distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         project.distributions.create('custom')
 
         then:
@@ -72,7 +76,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds distTar task for main distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         then:
         def task = project.tasks.distTar
@@ -82,7 +86,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds distTar task for custom distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         project.distributions.create('custom')
 
         then:
@@ -91,9 +95,20 @@ class DistributionPluginTest extends Specification {
         task.archivePath == project.file("build/distributions/test-project-custom.tar")
     }
 
+    def "adds assembleDist task for custom distribution"() {
+        when:
+        project.pluginManager.apply(DistributionPlugin)
+        project.distributions.create('custom')
+
+        then:
+        def task = project.tasks.assembleCustomDist
+        task instanceof DefaultTask
+        task TaskDependencyMatchers.dependsOn ("customDistZip","customDistTar")
+    }
+
     def "distribution names include project version when specified"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         project.version = '1.2'
 
         then:
@@ -105,7 +120,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds installDist task for main distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
 
         then:
         def task = project.installDist
@@ -115,7 +130,7 @@ class DistributionPluginTest extends Specification {
 
     def "adds installDist task for custom distribution"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         project.distributions.create('custom')
 
         then:
@@ -126,7 +141,7 @@ class DistributionPluginTest extends Specification {
 
     public void "distribution name is configurable"() {
         when:
-        project.apply(plugin: DistributionPlugin)
+        project.pluginManager.apply(DistributionPlugin)
         project.distributions.main.baseName = "SuperApp";
 
         then:
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJavaSourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJavaSourceSetTest.groovy
new file mode 100644
index 0000000..64bacee
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJavaSourceSetTest.groovy
@@ -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.java
+import org.gradle.api.file.SourceDirectorySet
+import org.gradle.jvm.Classpath
+import spock.lang.Specification
+
+class DefaultJavaSourceSetTest extends Specification {
+    def "has useful String representation"() {
+        def resourceSet = new DefaultJavaSourceSet("javaX", "mainX", Stub(SourceDirectorySet), Stub(Classpath))
+
+        expect:
+        resourceSet.displayName == "Java source 'mainX:javaX'"
+        resourceSet.toString() == "Java source 'mainX:javaX'"
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJvmResourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJvmResourceSetTest.groovy
new file mode 100644
index 0000000..75df2aa
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/java/DefaultJvmResourceSetTest.groovy
@@ -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.java
+import org.gradle.api.file.SourceDirectorySet
+import spock.lang.Specification
+
+class DefaultJvmResourceSetTest extends Specification {
+    def "has useful String representation"() {
+        def resourceSet = new DefaultJvmResourceSet("resourcesX", "mainX", Stub(SourceDirectorySet))
+
+        expect:
+        resourceSet.displayName == "JVM resources 'mainX:resourcesX'"
+        resourceSet.toString() == "JVM resources 'mainX:resourcesX'"
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingSchemeTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingSchemeTest.groovy
new file mode 100644
index 0000000..452d20b
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/ClassDirectoryBinaryNamingSchemeTest.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.api.internal.jvm
+
+import spock.lang.Specification
+
+class ClassDirectoryBinaryNamingSchemeTest extends Specification {
+
+    def "generates task names for class directory binaries"() {
+        expect:
+        def namer = new ClassDirectoryBinaryNamingScheme(name)
+        namer.getTaskName(verb, target) == taskName
+
+        where:
+        name   | verb      | target      | taskName
+        "main" | null      | null        | "main"
+        "main" | "compile" | null        | "compileMain"
+        "main" | null      | "resources" | "resources"
+        "main" | "compile" | "java"      | "compileJava"
+
+        "test" | null      | null        | "test"
+        "test" | "compile" | null        | "compileTest"
+        "test" | null      | "resources" | "testResources"
+        "test" | "compile" | "java"      | "compileTestJava"
+    }
+
+    def "generates base name and output directory"() {
+        def namer = new ClassDirectoryBinaryNamingScheme(baseName)
+
+        expect:
+        namer.getLifecycleTaskName() == lifecycleName
+        namer.getOutputDirectoryBase() == outputDir
+
+        where:
+        baseName | lifecycleName | outputDir
+        "main"   | "classes"     | "main"
+        "test"   | "testClasses" | "test"
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpecTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpecTest.groovy
new file mode 100644
index 0000000..5c3493c
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/jvm/DefaultClassDirectoryBinarySpecTest.groovy
@@ -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.api.internal.jvm
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory
+import org.gradle.internal.reflect.DirectInstantiator
+import org.gradle.jvm.platform.JavaPlatform
+import org.gradle.jvm.toolchain.JavaToolChain
+import org.gradle.platform.base.internal.toolchain.ToolResolver
+import spock.lang.Specification
+
+public class DefaultClassDirectoryBinarySpecTest extends Specification {
+    def toolChain = Mock(JavaToolChain)
+    def platform = Mock(JavaPlatform)
+
+    def "uses short task names for binary with name 'mainClasses'"() {
+        when:
+        def binary = binary("mainClasses")
+
+        then:
+        binary.name == 'mainClasses'
+
+        and:
+        binary.namingScheme.lifecycleTaskName == 'classes'
+        binary.namingScheme.getTaskName(null, null) == 'main'
+        binary.namingScheme.getTaskName("compile", null) == 'compileMain'
+        binary.namingScheme.getTaskName(null, "groovy") == 'groovy'
+        binary.namingScheme.getTaskName("compile", "groovy") == 'compileGroovy'
+    }
+
+    def "uses medium task names for binary with name 'otherClasses'"() {
+        when:
+        def binary = binary("otherClasses")
+
+        then:
+        binary.name == 'otherClasses'
+
+        and:
+        binary.namingScheme.lifecycleTaskName == 'otherClasses'
+        binary.namingScheme.getTaskName(null, null) == 'other'
+        binary.namingScheme.getTaskName("compile", null) == 'compileOther'
+        binary.namingScheme.getTaskName(null, "groovy") == 'otherGroovy'
+        binary.namingScheme.getTaskName("compile", "groovy") == 'compileOtherGroovy'
+    }
+
+    def "uses long task names for binary with name 'otherBinary'"() {
+        when:
+        def binary = binary("otherBinary")
+
+        then:
+        binary.name == 'otherBinary'
+
+        and:
+        binary.namingScheme.lifecycleTaskName == 'otherBinaryClasses'
+        binary.namingScheme.getTaskName(null, null) == 'otherBinary'
+        binary.namingScheme.getTaskName("compile", null) == 'compileOtherBinary'
+        binary.namingScheme.getTaskName(null, "groovy") == 'otherBinaryGroovy'
+        binary.namingScheme.getTaskName("compile", "groovy") == 'compileOtherBinaryGroovy'
+    }
+
+    def "has a useful toString() representation"() {
+        expect:
+        def binary = binary(name)
+        binary.toString() == displayName
+        binary.displayName == displayName
+
+        where:
+        name           | displayName
+        'mainClasses'  | 'classes \'main\''
+        'otherClasses' | 'classes \'other\''
+        'otherBinary'  | 'classes \'otherBinary\''
+    }
+
+    private DefaultClassDirectoryBinarySpec binary(String name) {
+        new DefaultClassDirectoryBinarySpec(name, toolChain, platform, DirectInstantiator.INSTANCE, Mock(ITaskFactory), Mock(ToolResolver))
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/GroovyJarFileTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/GroovyJarFileTest.groovy
index 8ebe2c4..28252a5 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/GroovyJarFileTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/GroovyJarFileTest.groovy
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.plugins
 
+import org.gradle.util.VersionNumber
 import spock.lang.Specification
 
 class GroovyJarFileTest extends Specification {
@@ -31,7 +32,7 @@ class GroovyJarFileTest extends Specification {
         jar != null
         jar.file == new File("/lib/groovy-2.0.5.jar")
         jar.baseName == "groovy"
-        jar.version.toString() == "2.0.5"
+        jar.version == VersionNumber.parse("2.0.5")
         !jar.groovyAll
         !jar.indy
         jar.dependencyNotation == "org.codehaus.groovy:groovy:2.0.5"
@@ -44,7 +45,7 @@ class GroovyJarFileTest extends Specification {
         jar != null
         jar.file == new File("/lib/groovy-all-2.0.5.jar")
         jar.baseName == "groovy-all"
-        jar.version.toString() == "2.0.5"
+        jar.version == VersionNumber.parse("2.0.5")
         jar.groovyAll
         !jar.indy
         jar.dependencyNotation == "org.codehaus.groovy:groovy-all:2.0.5"
@@ -58,7 +59,7 @@ class GroovyJarFileTest extends Specification {
         jar != null
         jar.file == new File("/lib/groovy-2.0.5-indy.jar")
         jar.baseName == "groovy"
-        jar.version.toString() == "2.0.5"
+        jar.version == VersionNumber.parse("2.0.5")
         !jar.groovyAll
         jar.indy
         jar.dependencyNotation == "org.codehaus.groovy:groovy:2.0.5:indy"
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/StartScriptGeneratorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/StartScriptGeneratorTest.groovy
index e327936..26451f7 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/StartScriptGeneratorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/StartScriptGeneratorTest.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2015 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,159 +14,74 @@
  * limitations under the License.
  */
 
-
-
 package org.gradle.api.internal.plugins
 
+import org.gradle.jvm.application.scripts.JavaAppStartScriptGenerationDetails
+import org.gradle.jvm.application.scripts.ScriptGenerator
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
 import spock.lang.Specification
-import org.gradle.util.WrapUtil
-import org.gradle.util.TextUtil
 
 class StartScriptGeneratorTest extends Specification {
 
-    def generator = new StartScriptGenerator();
-
-    def "classpath for unix script uses slashes as path separator"() {
-        given:
-        generator.applicationName = "TestApp"
-        generator.setClasspath(WrapUtil.toList("path\\to\\Jar.jar"))
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.contains("CLASSPATH=\$APP_HOME/path/to/Jar.jar")
-    }
-
-
-    def "unix script uses unix line separator"() {
-        given:
-        generator.applicationName = "TestApp"
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.split(TextUtil.windowsLineSeparator).length == 1
-        unixScriptContent.split(TextUtil.unixLineSeparator).length == 164
-    }
-
-    def "classpath for windows script uses backslash as path separator and windows line separator"() {
-        given:
-        generator.applicationName = "TestApp"
-        generator.setClasspath(WrapUtil.toList("path/to/Jar.jar"))
-        generator.scriptRelPath = "bin"
-        when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.contains("set CLASSPATH=%APP_HOME%\\path\\to\\Jar.jar")
-        windowsScriptContent.split(TextUtil.windowsLineSeparator).length == 90
-    }
-
-    def "windows script uses windows line separator"() {
-        given:
-        generator.applicationName = "TestApp"
-        generator.scriptRelPath = "bin"
-        when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.split(TextUtil.windowsLineSeparator).length == 90
-    }
-
-    def "defaultJvmOpts is expanded properly in windows script"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=bar', '-Xint']
-        generator.scriptRelPath = "bin"
-        when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.contains('set DEFAULT_JVM_OPTS="-Dfoo=bar" "-Xint"')
-    }
-
-    def "defaultJvmOpts is expanded properly in windows script -- spaces"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=bar baz', '-Xint']
-        generator.scriptRelPath = "bin"
-        when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.contains(/set DEFAULT_JVM_OPTS="-Dfoo=bar baz" "-Xint"/)
+    private static final String APP_NAME = 'Gradle'
+    private static final String OPTS_ENV_VAR = 'GRADLE_OPTS'
+    private static final String EXIT_ENV_VAR = 'GRADLE_EXIT_CONSOLE'
+    private static final String MAIN_CLASSNAME = 'org.gradle.launcher.GradleMain'
+    private static final Iterable<String> DEFAULT_JVM_OPTS = ['-Xmx1024m']
+    private static final Iterable<String> CLASSPATH = ['libs/gradle.jar']
+    private static final String SCRIPT_REL_PATH = 'bin/gradle'
+    private static final String APP_NAME_SYS_PROP = 'org.gradle.appname'
+
+    ScriptGenerator unixStartScriptGenerator = Mock()
+    ScriptGenerator windowsStartScriptGenerator = Mock()
+    StartScriptGenerator.UnixFileOperation unixFileOperation = Mock()
+    StartScriptGenerator startScriptGenerator = new StartScriptGenerator(unixStartScriptGenerator, windowsStartScriptGenerator, unixFileOperation)
+    @Rule TestNameTestDirectoryProvider temporaryFolder
+
+    def setup() {
+        populateStartScriptGenerator()
     }
 
-    def "defaultJvmOpts is expanded properly in windows script -- double quotes"() {
+    def "can generate Unix script"() {
         given:
-        generator.defaultJvmOpts = ['-Dfoo=b"ar baz', '-Xi""nt', '-Xpatho\\"logical']
-        generator.scriptRelPath = "bin"
-        when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.contains(/set DEFAULT_JVM_OPTS="-Dfoo=b\"ar baz" "-Xi\"\"nt" "-Xpatho\\\"logical"/)
-    }
+        TestFile script = temporaryFolder.file('unix.sh')
 
-    def "defaultJvmOpts is expanded properly in windows script -- backslashes and shell metacharacters"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=b\\ar baz', '-Xint%PATH%']
-        generator.scriptRelPath = "bin"
         when:
-        String windowsScriptContent = generator.generateWindowsScriptContent()
-        then:
-        windowsScriptContent.contains(/set DEFAULT_JVM_OPTS="-Dfoo=b\ar baz" "-Xint%%PATH%%"/)
-    }
+        startScriptGenerator.generateUnixScript(script)
 
-    def "defaultJvmOpts is expanded properly in unix script"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=bar', '-Xint']
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
         then:
-        unixScriptContent.contains('DEFAULT_JVM_OPTS=\'"-Dfoo=bar" "-Xint"\'')
+        1 * unixStartScriptGenerator.generateScript(createJavaAppStartScriptGenerationDetails(), _ as Writer)
+        0 * windowsStartScriptGenerator.generateScript(_, _)
+        1 * unixFileOperation.createExecutablePermission(script)
     }
 
-    def "defaultJvmOpts is expanded properly in unix script -- spaces"() {
+    def "can generate Windows script"() {
         given:
-        generator.defaultJvmOpts = ['-Dfoo=bar baz', '-Xint']
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.contains(/DEFAULT_JVM_OPTS='"-Dfoo=bar baz" "-Xint"'/)
-    }
+        TestFile script = temporaryFolder.file('windows.bat')
 
-    def "defaultJvmOpts is expanded properly in unix script -- double quotes"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=b"ar baz', '-Xi""nt']
-        generator.scriptRelPath = "bin"
         when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.contains(/DEFAULT_JVM_OPTS='"-Dfoo=b\"ar baz" "-Xi\"\"nt"'/)
-    }
+        startScriptGenerator.generateWindowsScript(script)
 
-    def "defaultJvmOpts is expanded properly in unix script -- single quotes"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=b\'ar baz', '-Xi\'\'n`t']
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
         then:
-        unixScriptContent.contains(/DEFAULT_JVM_OPTS='"-Dfoo=b'"'"'ar baz" "-Xi'"'"''"'"'n'"`"'t"'/)
+        1 * windowsStartScriptGenerator.generateScript(createJavaAppStartScriptGenerationDetails(), _ as Writer)
+        0 * unixStartScriptGenerator.generateScript(_, _)
+        0 * unixFileOperation.createExecutablePermission(script)
     }
 
-    def "defaultJvmOpts is expanded properly in unix script -- backslashes and shell metacharacters"() {
-        given:
-        generator.defaultJvmOpts = ['-Dfoo=b\\ar baz', '-Xint$PATH']
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.contains(/DEFAULT_JVM_OPTS='"-Dfoo=b\\ar baz" "-Xint/ + '\\$PATH' + /"'/)
+    private void populateStartScriptGenerator() {
+        startScriptGenerator.applicationName = APP_NAME
+        startScriptGenerator.optsEnvironmentVar = OPTS_ENV_VAR
+        startScriptGenerator.exitEnvironmentVar = EXIT_ENV_VAR
+        startScriptGenerator.mainClassName = MAIN_CLASSNAME
+        startScriptGenerator.defaultJvmOpts = DEFAULT_JVM_OPTS
+        startScriptGenerator.classpath = CLASSPATH
+        startScriptGenerator.scriptRelPath = SCRIPT_REL_PATH
+        startScriptGenerator.appNameSystemProperty = APP_NAME_SYS_PROP
     }
 
-    def "defaultJvmOpts is expanded properly in unix script -- empty list"() {
-        given:
-        generator.scriptRelPath = "bin"
-        when:
-        String unixScriptContent = generator.generateUnixScriptContent()
-        then:
-        unixScriptContent.contains(/DEFAULT_JVM_OPTS=""/)
+    private JavaAppStartScriptGenerationDetails createJavaAppStartScriptGenerationDetails() {
+        return new DefaultJavaAppStartScriptGenerationDetails(APP_NAME, OPTS_ENV_VAR, EXIT_ENV_VAR, MAIN_CLASSNAME, DEFAULT_JVM_OPTS, CLASSPATH, SCRIPT_REL_PATH, APP_NAME_SYS_PROP)
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/UnixStartScriptGeneratorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/UnixStartScriptGeneratorTest.groovy
new file mode 100644
index 0000000..61a2310
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/UnixStartScriptGeneratorTest.groovy
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.jvm.application.scripts.JavaAppStartScriptGenerationDetails
+import org.gradle.util.TextUtil
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+
+class UnixStartScriptGeneratorTest extends Specification {
+
+    UnixStartScriptGenerator generator = new UnixStartScriptGenerator()
+
+    def "classpath for unix script uses slashes as path separator"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains("CLASSPATH=\$APP_HOME/path/to/Jar.jar")
+    }
+
+    def "unix script uses unix line separator"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().split(TextUtil.unixLineSeparator).length == 164
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=bar', '-Xint'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains('DEFAULT_JVM_OPTS=\'"-Dfoo=bar" "-Xint"\'')
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script -- spaces"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=bar baz', '-Xint'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/DEFAULT_JVM_OPTS='"-Dfoo=bar baz" "-Xint"'/)
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script -- double quotes"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=b"ar baz', '-Xi""nt'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/DEFAULT_JVM_OPTS='"-Dfoo=b\"ar baz" "-Xi\"\"nt"'/)
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script -- single quotes"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=b\'ar baz', '-Xi\'\'n`t'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/DEFAULT_JVM_OPTS='"-Dfoo=b'"'"'ar baz" "-Xi'"'"''"'"'n'"`"'t"'/)
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script -- backslashes and shell metacharacters"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=b\\ar baz', '-Xint$PATH'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/DEFAULT_JVM_OPTS='"-Dfoo=b\\ar baz" "-Xint/ + '\\$PATH' + /"'/)
+    }
+
+    def "defaultJvmOpts is expanded properly in unix script -- empty list"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails([], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/DEFAULT_JVM_OPTS=""/)
+    }
+
+    def "determines application-relative path"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin/sample/start')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains('cd "`dirname \\"$PRG\\"`/../.." >&-')
+    }
+
+    private JavaAppStartScriptGenerationDetails createScriptGenerationDetails(List<String> defaultJvmOpts, String scriptRelPath) {
+        final String applicationName = 'TestApp'
+        final List<String> classpath = WrapUtil.toList('path\\to\\Jar.jar')
+        return new DefaultJavaAppStartScriptGenerationDetails(applicationName, null, null, null, defaultJvmOpts, classpath, scriptRelPath, null)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/WindowsStartScriptGeneratorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/WindowsStartScriptGeneratorTest.groovy
new file mode 100644
index 0000000..c99f29e
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/plugins/WindowsStartScriptGeneratorTest.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.jvm.application.scripts.JavaAppStartScriptGenerationDetails
+import org.gradle.util.TextUtil
+import org.gradle.util.WrapUtil
+import spock.lang.Specification
+
+class WindowsStartScriptGeneratorTest extends Specification {
+
+    WindowsStartScriptGenerator generator = new WindowsStartScriptGenerator()
+
+    def "classpath for windows script uses backslash as path separator and windows line separator"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains("set CLASSPATH=%APP_HOME%\\path\\to\\Jar.jar")
+    }
+
+    def "windows script uses windows line separator"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().split(TextUtil.windowsLineSeparator).length == 90
+    }
+
+    def "defaultJvmOpts is expanded properly in windows script"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=bar', '-Xint'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains('set DEFAULT_JVM_OPTS="-Dfoo=bar" "-Xint"')
+    }
+
+    def "defaultJvmOpts is expanded properly in windows script -- spaces"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=bar baz', '-Xint'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/set DEFAULT_JVM_OPTS="-Dfoo=bar baz" "-Xint"/)
+    }
+
+    def "defaultJvmOpts is expanded properly in windows script -- double quotes"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=b"ar baz', '-Xi""nt', '-Xpatho\\"logical'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/set DEFAULT_JVM_OPTS="-Dfoo=b\"ar baz" "-Xi\"\"nt" "-Xpatho\\\"logical"/)
+    }
+
+    def "defaultJvmOpts is expanded properly in windows script -- backslashes and shell metacharacters"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(['-Dfoo=b\\ar baz', '-Xint%PATH%'], 'bin')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains(/set DEFAULT_JVM_OPTS="-Dfoo=b\ar baz" "-Xint%%PATH%%"/)
+    }
+
+    def "determines application-relative path"() {
+        given:
+        JavaAppStartScriptGenerationDetails details = createScriptGenerationDetails(null, 'bin/sample/start')
+        Writer destination = new StringWriter()
+
+        when:
+        generator.generateScript(details, destination)
+
+        then:
+        destination.toString().contains('set APP_HOME=%DIRNAME%..\\..')
+    }
+
+    private JavaAppStartScriptGenerationDetails createScriptGenerationDetails(List<String> defaultJvmOpts, String scriptRelPath) {
+        final String applicationName = 'TestApp'
+        final List<String> classpath = WrapUtil.toList('path/to/Jar.jar')
+        return new DefaultJavaAppStartScriptGenerationDetails(applicationName, null, null, null, defaultJvmOpts, classpath, scriptRelPath, null)
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
index 12db1af..0054954 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultGroovySourceSetTest.groovy
@@ -17,6 +17,7 @@ package org.gradle.api.internal.tasks
 
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.junit.Test
 import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
@@ -24,7 +25,11 @@ import static org.junit.Assert.assertThat
 
 class DefaultGroovySourceSetTest {
     private final DefaultGroovySourceSet sourceSet = new DefaultGroovySourceSet("<set-display-name>", [resolve: {it as File}] as FileResolver)
-    
+
+    static {
+        NativeServicesTestFixture.initialize()
+    }
+
     @Test
     public void defaultValues() {
         assertThat(sourceSet.groovy, instanceOf(DefaultSourceDirectorySet))
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainerTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainerTest.java
index d14c3a7..9e7084b 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainerTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetContainerTest.java
@@ -24,7 +24,7 @@ import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertThat;
 
 public class DefaultSourceSetContainerTest {
-    private final DefaultSourceSetContainer container = new DefaultSourceSetContainer(null, null, new DirectInstantiator());
+    private final DefaultSourceSetContainer container = new DefaultSourceSetContainer(null, null, DirectInstantiator.INSTANCE);
 
     @Test
     public void createsASourceSet() {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
index 72f69d2..6bc067c 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/DefaultSourceSetTest.groovy
@@ -19,6 +19,8 @@ import org.gradle.api.Task
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.tasks.SourceSet
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.junit.Before
 import org.junit.Test
 import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
@@ -34,6 +36,11 @@ class DefaultSourceSetTest {
         return s
     }
 
+    @Before
+    public void setup() {
+        NativeServicesTestFixture.initialize()
+    }
+
     @Test
     public void hasUsefulDisplayName() {
         SourceSet sourceSet = sourceSet('int-test')
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/ArgWriterTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/ArgWriterTest.groovy
deleted file mode 100755
index d7009bb..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/ArgWriterTest.groovy
+++ /dev/null
@@ -1,77 +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.tasks.compile
-
-import spock.lang.Specification
-
-import static org.gradle.util.TextUtil.toPlatformLineSeparators
-
-class ArgWriterTest extends Specification {
-    final StringWriter writer = new StringWriter()
-    final PrintWriter printWriter = new PrintWriter(writer, true)
-    final ArgWriter argWriter = ArgWriter.unixStyle(printWriter)
-
-    def "writes single argument to line"() {
-        when:
-        argWriter.args("-nologo")
-
-        then:
-        writer.toString() == toPlatformLineSeparators("-nologo\n")
-    }
-
-    def "writes multiple arguments to line"() {
-        when:
-        argWriter.args("-I", "some/dir")
-
-        then:
-        writer.toString() == toPlatformLineSeparators("-I some/dir\n")
-    }
-
-    def "quotes argument with whitespace"() {
-        when:
-        argWriter.args("ab c", "d e f")
-
-        then:
-        writer.toString() == toPlatformLineSeparators('"ab c" "d e f"\n')
-    }
-
-    def "escapes double quotes in argument"() {
-        when:
-        argWriter.args('"abc"', 'a" bc')
-
-        then:
-        writer.toString() == toPlatformLineSeparators('\\"abc\\" "a\\" bc"\n')
-    }
-
-    def "escapes backslash in argument"() {
-        when:
-        argWriter.args('a\\b', 'a \\ bc')
-
-        then:
-        writer.toString() == toPlatformLineSeparators('a\\\\b "a \\\\ bc"\n')
-    }
-
-    def "does not escape characters in windows style"() {
-        def argWriter = ArgWriter.windowsStyle(printWriter)
-
-        when:
-        argWriter.args('a\\b', 'a "\\" bc')
-
-        then:
-        writer.toString() == toPlatformLineSeparators('a\\b "a "\\" bc"\n')
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy
deleted file mode 100644
index 0bcd196..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CleaningJavaCompilerTest.groovy
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile
-
-import org.gradle.language.jvm.internal.StaleClassCleaner
-import spock.lang.Specification
-import org.gradle.api.tasks.WorkResult
-import org.gradle.api.file.FileCollection
-
-class CleaningJavaCompilerTest extends Specification {
-    private final Compiler<JavaCompileSpec> target = Mock()
-    private final JavaCompileSpec spec = Mock()
-    private final StaleClassCleaner cleaner = Mock()
-    private final CleaningJavaCompilerSupport<JavaCompileSpec> compiler = new CleaningJavaCompilerSupport<JavaCompileSpec>() {
-        @Override
-        protected Compiler<JavaCompileSpec> getCompiler() {
-            return target
-        }
-
-        protected StaleClassCleaner createCleaner(JavaCompileSpec spec) {
-            return cleaner
-        }
-    }
-    
-    def cleansStaleClassesAndThenInvokesCompiler() {
-        WorkResult result = Mock()
-        File destDir = new File('dest')
-        FileCollection source = Mock()
-        _ * spec.destinationDir >> destDir
-        _ * spec.source >> source
-
-        when:
-        def r = compiler.execute(spec)
-
-        then:
-        r == result
-
-        and:
-        1 * cleaner.setDestinationDir(destDir)
-        1 * cleaner.setSource(source)
-
-        and:
-        1 * cleaner.execute()
-
-        and:
-        1 * target.execute(spec) >> result
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.groovy
deleted file mode 100644
index e690afa..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/CommandLineJavaCompilerArgumentsGeneratorTest.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.tasks.compile
-
-import com.google.common.collect.Lists
-import org.gradle.api.internal.file.TemporaryFileProvider
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class CommandLineJavaCompilerArgumentsGeneratorTest extends Specification {
-    @Rule TestNameTestDirectoryProvider tempDir
-    TemporaryFileProvider tempFileProvider = Mock()
-    CommandLineJavaCompilerArgumentsGenerator argsGenerator = new CommandLineJavaCompilerArgumentsGenerator(tempFileProvider)
-
-    def "inlines arguments if they are short enough"() {
-        def spec = createCompileSpec(25)
-
-        when:
-        def args = argsGenerator.generate(spec)
-
-        then:
-        0 * tempFileProvider._
-        Lists.newArrayList(args) == ["-J-Xmx256m", "-g", "-classpath", spec.classpath.asPath, *spec.source*.path]
-    }
-
-    def "creates arguments file if arguments get too long"() {
-        def spec = createCompileSpec(100)
-        def argsFile = tempDir.createFile("compile-args")
-        tempFileProvider.createTemporaryFile(*_) >> argsFile
-
-        when:
-        def args = argsGenerator.generate(spec)
-
-        then: "argument list only contains launcher args and reference to args file"
-        Lists.newArrayList(args) == ["-J-Xmx256m", "@$argsFile"]
-
-        and: "args file contains remaining arguments (one per line, quoted)"
-        argsFile.readLines() == ["-g", "-classpath", quote("$spec.classpath.asPath"), *(spec.source*.path.collect { quote(it) })]
-    }
-
-    def createCompileSpec(numFiles) {
-        def sources = createFiles(numFiles)
-        def classpath = createFiles(numFiles)
-        def spec = new DefaultJavaCompileSpec()
-        spec.compileOptions = new CompileOptions()
-        spec.compileOptions.forkOptions.memoryMaximumSize = "256m"
-        spec.source = new SimpleFileCollection(sources)
-        spec.classpath = new SimpleFileCollection(classpath)
-        spec
-    }
-
-    def createFiles(numFiles) {
-        (1..numFiles).collect { new File("/foo bar/File$it") }
-    }
-
-    def quote(arg) {
-      "\"${arg.replace("\\", "\\\\")}\""
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy
deleted file mode 100644
index 51393bd..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DefaultJavaCompilerFactoryTest.groovy
+++ /dev/null
@@ -1,84 +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.tasks.compile
-
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager
-import org.gradle.api.internal.tasks.compile.daemon.DaemonJavaCompiler
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.internal.Factory
-
-import spock.lang.Specification
-
-class DefaultJavaCompilerFactoryTest extends Specification {
-    def inProcessCompiler = Mock(Compiler)
-    def inProcessCompilerFactory = Mock(JavaCompilerFactory)
-    def factory = new DefaultJavaCompilerFactory(Mock(ProjectInternal), Mock(Factory), inProcessCompilerFactory, Mock(CompilerDaemonManager))
-    def options = new CompileOptions()
-    
-    def setup() {
-        inProcessCompilerFactory.create(_) >> inProcessCompiler
-    }
-
-    def "creates Ant compiler when useAnt=true"() {
-        options.useAnt = true
-        options.fork = fork
-        
-        expect:
-        factory.create(options) instanceof AntJavaCompiler
-        
-        where: fork << [false, true]
-    }
-
-    def "falls back to Ant compiler when options.compiler is set"() {
-        options.useAnt = false
-        options.compiler = "jikes"
-
-        expect:
-        factory.create(options) instanceof AntJavaCompiler
-    }
-    
-    def "creates in-process compiler when fork=false"() {
-        options.useAnt = false
-        options.fork = false
-
-        expect:
-        def compiler = factory.create(options)
-        compiler instanceof NormalizingJavaCompiler
-        compiler.delegate.is(inProcessCompiler)
-    }
-
-    def "creates command line compiler when fork=true and forkOptions.executable is set"() {
-        options.useAnt = false
-        options.fork = true
-        options.forkOptions.executable = "/path/to/javac"
-
-        expect:
-        def compiler = factory.create(options)
-        compiler instanceof NormalizingJavaCompiler
-        compiler.delegate instanceof CommandLineJavaCompiler
-    }
-
-    def "creates daemon compiler when fork=true"() {
-        options.useAnt = false
-        options.fork = true
-
-        expect:
-        def compiler = factory.create(options)
-        compiler instanceof NormalizingJavaCompiler
-        compiler.delegate instanceof DaemonJavaCompiler
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompilerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompilerTest.groovy
deleted file mode 100644
index 307f4e4..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/DelegatingJavaCompilerTest.groovy
+++ /dev/null
@@ -1,33 +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.tasks.compile
-
-import spock.lang.Specification
-
-class DelegatingJavaCompilerTest extends Specification {
-    def "configures and executes the chosen compiler"() {
-        Compiler<JavaCompileSpec> chosen = Mock()
-        JavaCompileSpec spec = Mock()
-
-        def switchable = new DelegatingJavaCompiler({ chosen } as JavaCompilerFactory)
-        
-        when:
-        switchable.execute(spec)
-        
-        then:
-        1 * chosen.execute(spec)
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy
deleted file mode 100644
index d51aa95..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/GroovyCompileTransformingClassLoaderTest.groovy
+++ /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.api.internal.tasks.compile
-
-import spock.lang.Specification
-import org.codehaus.groovy.transform.GroovyASTTransformationClass
-import org.gradle.internal.classloader.ClasspathUtil
-import org.gradle.internal.classpath.DefaultClassPath
-
-class GroovyCompileTransformingClassLoaderTest extends Specification {
-    GroovyCompileTransformingClassLoader loader
-    Class<?> classAnnotation
-
-    def setup() {
-        def classPath = new DefaultClassPath(ClasspathUtil.getClasspathForClass(getClass()), ClasspathUtil.getClasspathForClass(GroovyASTTransformationClass))
-        loader = new GroovyCompileTransformingClassLoader(classPath)
-        classAnnotation = loader.loadClass(GroovyASTTransformationClass.name)
-    }
-
-    def "loads class annotated with transformer name"() {
-        expect:
-        def cl = loader.loadClass(WithNameSpecified.name)
-        def annotation = cl.getAnnotation(classAnnotation)
-        annotation.value() == ['some-type'] as String[]
-        annotation.classes() == [] as Class[]
-    }
-
-    def "loads class annotated with transformer names"() {
-        expect:
-        def cl = loader.loadClass(WithNamesSpecified.name)
-        def annotation = cl.getAnnotation(classAnnotation)
-        annotation.value() == ['some-type', 'some-other-type'] as String[]
-        annotation.classes() == [] as Class[]
-    }
-
-    def "loads class annotated with transformer class"() {
-        expect:
-        def cl = loader.loadClass(WithClassSpecified.name)
-        def annotation = cl.getAnnotation(classAnnotation)
-        annotation.value() == [Transformer.name] as String[]
-        annotation.classes() == [] as Class[]
-    }
-
-    def "loads class annotated with transformer classes"() {
-        expect:
-        def cl = loader.loadClass(WithClassesSpecified.name)
-        def annotation = cl.getAnnotation(classAnnotation)
-        annotation.value() == [Transformer.name, Runnable.name] as String[]
-        annotation.classes() == [] as Class[]
-    }
-
-    def "loads class annotated with transformer names and classes"() {
-        expect:
-        def cl = loader.loadClass(WithBothSpecified.name)
-        def annotation = cl.getAnnotation(classAnnotation)
-        annotation.value() as Set == ["some-type", Transformer.name, Runnable.name] as Set
-        annotation.classes() == [] as Class[]
-    }
-
-    static class Transformer {
-    }
-}
-
- at GroovyASTTransformationClass("some-type")
- at interface WithNameSpecified {
-}
-
- at GroovyASTTransformationClass(["some-type", "some-other-type"])
- at interface WithNamesSpecified {
-}
-
- at GroovyASTTransformationClass(classes = [GroovyCompileTransformingClassLoaderTest.Transformer])
- at interface WithClassSpecified {
-}
-
- at GroovyASTTransformationClass(classes = [GroovyCompileTransformingClassLoaderTest.Transformer, Runnable])
- at interface WithClassesSpecified {
-}
-
- at GroovyASTTransformationClass(value = "some-type", classes = [GroovyCompileTransformingClassLoaderTest.Transformer, Runnable])
- at interface WithBothSpecified {
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactoryTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactoryTest.groovy
deleted file mode 100644
index c164608..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/InProcessJavaCompilerFactoryTest.groovy
+++ /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.api.internal.tasks.compile
-
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.gradle.api.tasks.compile.CompileOptions
-
-import spock.lang.Specification
-
-class InProcessJavaCompilerFactoryTest extends Specification {
-    def factory = new InProcessJavaCompilerFactory()
-    def options = new CompileOptions()
-    
-    @Requires(TestPrecondition.JDK6_OR_LATER)
-    def "creates JDK 6 compiler on JDK 6"() {
-        expect:
-        factory.create(options).getClass().name == "org.gradle.api.internal.tasks.compile.jdk6.Jdk6JavaCompiler"
-    }
-
-    @Requires(TestPrecondition.JDK5)
-    def "creates Sun compiler on JDK 5"() {
-        expect:
-        factory.create(options) instanceof SunJavaCompiler
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy
deleted file mode 100644
index 24d2348..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/JavaCompilerArgumentsBuilderTest.groovy
+++ /dev/null
@@ -1,276 +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.tasks.compile
-
-import org.gradle.api.tasks.compile.CompileOptions
-import spock.lang.Specification
-import org.gradle.api.JavaVersion
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-
-class JavaCompilerArgumentsBuilderTest extends Specification {
-    def spec = new DefaultJavaCompileSpec()
-    def builder = new JavaCompilerArgumentsBuilder(spec)
-
-    def setup() {
-        spec.compileOptions = new CompileOptions()
-    }
-
-    def "generates options for an unconfigured spec"() {
-        expect:
-        builder.build() == ["-g"]
-    }
-
-    def "generates no -source option when current Jvm Version is used"() {
-        spec.sourceCompatibility = JavaVersion.current().toString();
-
-        expect:
-        builder.build() == ["-g"]
-    }
-
-    def "generates -source option when compatibility differs from current Jvm version"() {
-        spec.sourceCompatibility = "1.4"
-
-        expect:
-        builder.build() == ["-source", "1.4", "-g"]
-    }
-
-    def "generates no -target option when current Jvm Version is used"() {
-        spec.targetCompatibility = JavaVersion.current().toString();
-
-        expect:
-        builder.build() == ["-g"]
-    }
-
-    def "generates -target option when compatibility differs current Jvm version"() {
-        spec.targetCompatibility = "1.4"
-
-        expect:
-        builder.build() == ["-target", "1.4", "-g"]
-    }
-
-    def "generates -d option"() {
-        def file = new File("/project/build")
-        spec.destinationDir = file
-
-        expect:
-        builder.build() == ["-d", file.path, "-g"]
-    }
-
-    def "generates -verbose option"() {
-        when:
-        spec.compileOptions.verbose = true
-
-        then:
-        builder.build() == ["-verbose", "-g"]
-
-        when:
-        spec.compileOptions.verbose = false
-
-        then:
-        builder.build() == ["-g"]
-    }
-
-    def "generates -deprecation option"() {
-        when:
-        spec.compileOptions.deprecation = true
-
-        then:
-        builder.build() == ["-deprecation", "-g"]
-
-        when:
-        spec.compileOptions.deprecation = false
-
-        then:
-        builder.build() == ["-g"]
-    }
-
-    def "generates -nowarn option"() {
-        when:
-        spec.compileOptions.warnings = true
-
-        then:
-        builder.build() == ["-g"]
-
-        when:
-        spec.compileOptions.warnings = false
-
-        then:
-        builder.build() == ["-nowarn", "-g"]
-    }
-
-    def "generates -g option"() {
-        when:
-        spec.compileOptions.debug = true
-
-        then:
-        builder.build() == ["-g"]
-
-        when:
-        spec.compileOptions.debugOptions.debugLevel = "source,vars"
-
-        then:
-        builder.build() == ["-g:source,vars"]
-
-        when:
-        spec.compileOptions.debug = false
-
-        then:
-        builder.build() == ["-g:none"]
-    }
-
-    def "generates -encoding option"() {
-        spec.compileOptions.encoding = "some-encoding"
-
-        expect:
-        builder.build() == ["-g", "-encoding", "some-encoding"]
-    }
-
-    def "generates -bootclasspath option"() {
-        spec.compileOptions.bootClasspath = "/lib/lib1.jar:/lib/lib2.jar"
-
-        expect:
-        builder.build() == ["-g", "-bootclasspath", "/lib/lib1.jar:/lib/lib2.jar"]
-    }
-
-    def "generates -extdirs option"() {
-        spec.compileOptions.extensionDirs = "/dir1:/dir2"
-
-        expect:
-        builder.build() == ["-g", "-extdirs", "/dir1:/dir2"]
-    }
-
-    def "generates -classpath option"() {
-        def file1 = new File("/lib/lib1.jar")
-        def file2 = new File("/lib/lib2.jar")
-        spec.classpath = [file1, file2]
-
-        expect:
-        builder.build() == ["-g", "-classpath", "$file1$File.pathSeparator$file2"]
-    }
-
-    def "adds custom compiler args"() {
-        spec.compileOptions.compilerArgs = ["-a", "value-a", "-b", "value-b"]
-
-        expect:
-        builder.build() == ["-g", "-a", "value-a", "-b", "value-b"]
-    }
-
-    def "can include/exclude main options"() {
-        spec.sourceCompatibility = "1.4"
-
-        when:
-        builder.includeMainOptions(true)
-
-        then:
-        builder.build() == ["-source", "1.4", "-g"]
-
-        when:
-        builder.includeMainOptions(false)
-
-        then:
-        builder.build() == []
-    }
-
-    def "includes main options by default"() {
-        spec.sourceCompatibility = "1.4"
-
-        expect:
-        builder.build() == ["-source", "1.4", "-g"]
-    }
-
-    def "can include/exclude classpath"() {
-        def file1 = new File("/lib/lib1.jar")
-        def file2 = new File("/lib/lib2.jar")
-        spec.classpath = [file1, file2]
-
-        when:
-        builder.includeClasspath(true)
-
-        then:
-        builder.build() == ["-g", "-classpath", "$file1$File.pathSeparator$file2"]
-
-        when:
-        builder.includeClasspath(false)
-
-        then:
-        builder.build() == ["-g"]
-    }
-
-    def "includes classpath by default"() {
-        def file1 = new File("/lib/lib1.jar")
-        def file2 = new File("/lib/lib2.jar")
-        spec.classpath = [file1, file2]
-
-        expect:
-        builder.build() == ["-g", "-classpath", "$file1$File.pathSeparator$file2"]
-    }
-
-    def "can include/exclude launcher options"() {
-        spec.compileOptions.forkOptions.with {
-            memoryInitialSize = "64m"
-            memoryMaximumSize = "1g"
-        }
-
-        when:
-        builder.includeLauncherOptions(true)
-
-        then:
-        builder.build() == ["-J-Xms64m", "-J-Xmx1g", "-g"]
-
-        when:
-        builder.includeLauncherOptions(false)
-
-        then:
-        builder.build() == ["-g"]
-    }
-
-    def "does not include launcher options by default"() {
-        spec.compileOptions.forkOptions.with {
-            memoryInitialSize = "64m"
-            memoryMaximumSize = "1g"
-        }
-
-        expect:
-        builder.build() == ["-g"]
-    }
-
-    def "can include/exclude source files"() {
-        def file1 = new File("/src/Person.java")
-        def file2 = new File("Computer.java")
-        spec.source = new SimpleFileCollection(file1, file2)
-
-        when:
-        builder.includeSourceFiles(true)
-
-        then:
-        builder.build() == ["-g", file1.path, file2.path]
-
-        when:
-        builder.includeSourceFiles(false)
-
-        then:
-        builder.build() == ["-g"]
-    }
-
-    def "does not include source files by default"() {
-        def file1 = new File("/src/Person.java")
-        def file2 = new File("Computer.java")
-        spec.source = new SimpleFileCollection(file1, file2)
-
-        expect:
-        builder.build() == ["-g"]
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy
deleted file mode 100644
index 4c42148..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingGroovyCompilerTest.groovy
+++ /dev/null
@@ -1,67 +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.tasks.compile
-
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-
-import groovy.transform.InheritConstructors
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.api.tasks.compile.GroovyCompileOptions
-import spock.lang.Specification
-
-class NormalizingGroovyCompilerTest extends Specification { 
-    Compiler<GroovyJavaJointCompileSpec> target = Mock()
-    DefaultGroovyJavaJointCompileSpec spec = new DefaultGroovyJavaJointCompileSpec()
-    NormalizingGroovyCompiler compiler = new NormalizingGroovyCompiler(target)
-    
-    def setup() {
-        spec.classpath = files('Dep1.jar', 'Dep2.jar', 'Dep3.jar')
-        spec.groovyClasspath = spec.classpath
-        spec.source = files('House.scala', 'Person1.java', 'package.html', 'Person2.groovy')
-        spec.compileOptions = new CompileOptions()
-        spec.groovyCompileOptions = new GroovyCompileOptions()
-    }
-
-    def "silently excludes source files not ending in .java or .groovy by default"() {
-        when:
-        compiler.execute(spec)
-
-        then:
-        1 * target.execute(spec) >> {
-            assert spec.source.files == files('Person1.java', 'Person2.groovy').files
-        }
-    }
-
-    def "excludes source files that have extension different from specified by fileExtensions option"() {
-        spec.groovyCompileOptions.fileExtensions = ['html']
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        1 * target.execute(spec) >> {
-            assert spec.source.files == files('package.html').files
-        }
-    }
-
-    private files(String... paths) {
-        new TestFileCollection(paths.collect { new File(it) })
-    }
-
-    // file collection whose type is distinguishable from SimpleFileCollection
-    @InheritConstructors
-    static class TestFileCollection extends SimpleFileCollection {} 
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy
deleted file mode 100644
index 6a33dd6..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/NormalizingJavaCompilerTest.groovy
+++ /dev/null
@@ -1,125 +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.tasks.compile
-
-import org.gradle.api.tasks.WorkResult
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-
-import groovy.transform.InheritConstructors
-import org.gradle.api.tasks.compile.CompileOptions
-import spock.lang.Specification
-
-class NormalizingJavaCompilerTest extends Specification {
-    Compiler<JavaCompileSpec> target = Mock()
-    DefaultJavaCompileSpec spec = new DefaultJavaCompileSpec()
-    NormalizingJavaCompiler compiler = new NormalizingJavaCompiler(target)
-
-    def setup() {
-        spec.source = files("Source1.java", "Source2.java", "Source3.java")
-        spec.classpath = files("Dep1.jar", "Dep2.jar", "Dep3.jar")
-        spec.compileOptions = new CompileOptions()
-    }
-
-    def "delegates to target compiler after resolving source and classpath"() {
-        WorkResult workResult = Mock()
-
-        when:
-        def result = compiler.execute(spec)
-
-        then:
-        1 * target.execute(spec) >> {
-            assert spec.source.getClass() == SimpleFileCollection
-            assert spec.source.files == old(spec.source.files)
-            assert spec.classpath.getClass() == SimpleFileCollection
-            assert spec.classpath.files == old(spec.classpath.files)
-            workResult
-        }
-        result == workResult
-    }
-
-    def "silently excludes source files not ending in .java"() {
-        spec.source = files("House.scala", "Person1.java", "package.html", "Person2.java")
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        1 * target.execute(spec) >> {
-            assert spec.source.files == files("Person1.java", "Person2.java").files
-        }
-    }
-
-    def "propagates compile failure when failOnError is true"() {
-        def failure
-        target.execute(spec) >> { throw failure = new CompilationFailedException() }
-
-        spec.compileOptions.failOnError = true
-
-        when:
-        compiler.execute(spec)
-        
-        then:
-        CompilationFailedException e = thrown()
-        e == failure
-    }
-
-    def "ignores compile failure when failOnError is false"() {
-        target.execute(spec) >> { throw new CompilationFailedException() }
-
-        spec.compileOptions.failOnError = false
-
-        when:
-        def result = compiler.execute(spec)
-
-        then:
-        noExceptionThrown()
-        !result.didWork
-    }
-
-    def "propagates other failure"() {
-        def failure
-        target.execute(spec) >> { throw failure = new RuntimeException() }
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        RuntimeException e = thrown()
-        e == failure
-    }
-
-    def "resolves any non-strings that make it into custom compiler args"() {
-        spec.compileOptions.compilerArgs << "a dreaded ${"GString"}"
-        spec.compileOptions.compilerArgs << 42
-        assert !spec.compileOptions.compilerArgs.any { it instanceof String }
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        1 * target.execute(_) >> { JavaCompileSpec spec ->
-            assert spec.compileOptions.compilerArgs.every { it instanceof String }
-        }
-    }
-
-    private files(String... paths) {
-        new TestFileCollection(paths.collect { new File(it) })
-    }
-
-    // file collection whose type is distinguishable from SimpleFileCollection
-    @InheritConstructors
-    static class TestFileCollection extends SimpleFileCollection {}
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/SimpleStaleClassCleanerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/SimpleStaleClassCleanerTest.groovy
deleted file mode 100644
index 67edfff..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/SimpleStaleClassCleanerTest.groovy
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.compile
-
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.TaskOutputsInternal
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class SimpleStaleClassCleanerTest extends Specification {
-    @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    private final TaskOutputsInternal outputs = Mock()
-    private final SimpleStaleClassCleaner cleaner = new SimpleStaleClassCleaner(outputs)
-    
-    def deletesAllPreviousOutputFiles() {
-        def file1 = tmpDir.file('file1').createFile()
-        def file2 = tmpDir.file('file2').createFile()
-        cleaner.destinationDir = tmpDir.testDirectory
-
-        when:
-        cleaner.execute()
-
-        then:
-        !file1.exists()
-        !file2.exists()
-        1 * outputs.previousFiles >> { [iterator: { [file1, file2].iterator() }] as FileCollection }
-
-        and:
-        cleaner.didWork
-    }
-
-    def doesNotDeleteFilesWhichAreNotUnderTheDestinationDir() {
-        def destDir = tmpDir.file('dir')
-        def file1 = destDir.file('file1').createFile()
-        def file2 = tmpDir.file('file2').createFile()
-        cleaner.destinationDir = destDir
-
-        when:
-        cleaner.execute()
-
-        then:
-        !file1.exists()
-        file2.exists()
-        1 * outputs.previousFiles >> { [iterator: { [file1, file2].iterator() }] as FileCollection }
-
-        and:
-        cleaner.didWork
-    }
-
-    def reportsWhenNoWorkDone() {
-        cleaner.destinationDir = tmpDir.file('dir')
-
-        when:
-        cleaner.execute()
-
-        then:
-        1 * outputs.previousFiles >> { [iterator: { [].iterator() }] as FileCollection }
-
-        and:
-        !cleaner.didWork
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy
deleted file mode 100644
index 1f462bf..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/daemon/CompilerDaemonManagerTest.groovy
+++ /dev/null
@@ -1,100 +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.tasks.compile.daemon
-
-import org.gradle.api.internal.tasks.compile.CompileSpec
-import org.gradle.api.internal.tasks.compile.Compiler
-import spock.lang.Specification
-import spock.lang.Subject
-
-class CompilerDaemonManagerTest extends Specification {
-
-    def clientsManager = Mock(CompilerClientsManager)
-    def client = Mock(CompilerDaemonClient)
-
-    @Subject manager = new CompilerDaemonManager(clientsManager)
-
-    def workingDir = new File("some-dir")
-    def compiler = Stub(Compiler)
-    def options = Stub(DaemonForkOptions)
-    def compileSpec = Stub(CompileSpec)
-
-    def "getting a compiler daemon does not assume client use"() {
-        when:
-        manager.getDaemon(workingDir, options);
-
-        then:
-        0 * clientsManager._
-    }
-
-    def "new client is created when daemon is executed and no idle clients found"() {
-        when:
-        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
-
-        then:
-        1 * clientsManager.reserveIdleClient(options) >> null
-
-        then:
-        1 * clientsManager.reserveNewClient(workingDir, options) >> client
-
-        then:
-        1 * client.execute(compiler, compileSpec)
-
-        then:
-        1 * clientsManager.release(client)
-        0 * _._
-    }
-
-    def "idle client is reused when daemon is executed"() {
-        when:
-        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
-
-        then:
-        1 * clientsManager.reserveIdleClient(options) >> client
-
-        then:
-        1 * client.execute(compiler, compileSpec)
-
-        then:
-        1 * clientsManager.release(client)
-        0 * _._
-    }
-
-    def "client is released even if execution fails"() {
-        when:
-        manager.getDaemon(workingDir, options).execute(compiler, compileSpec)
-
-        then:
-        1 * clientsManager.reserveIdleClient(options) >> client
-
-        then:
-        1 * client.execute(compiler, compileSpec) >> { throw new RuntimeException("Boo!") }
-
-        then:
-        thrown(RuntimeException)
-        1 * clientsManager.release(client)
-        0 * _._
-    }
-
-    def "stops clients"() {
-        when:
-        manager.stop()
-
-        then:
-        clientsManager.stop()
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfoTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfoTest.groovy
deleted file mode 100644
index b7aa720..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/AllFromJarRebuildInfoTest.groovy
+++ /dev/null
@@ -1,44 +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.tasks.compile.incremental
-
-import org.gradle.api.internal.file.collections.DirectoryFileTree
-import org.gradle.api.internal.file.collections.FileTreeAdapter
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class AllFromJarRebuildInfoTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-
-    def "empty jar"() {
-        def classes = new FileTreeAdapter(new DirectoryFileTree(new File("missing")))
-
-        expect:
-        new AllFromJarRebuildInfo(new JarArchive(new File("j"), classes)).changedClassesInJar.isEmpty()
-    }
-
-    def "contains all classes"() {
-        temp.createFile("root/com/foo/Foo.class")
-        temp.createFile("root/com/Bar.class")
-        def classes = new FileTreeAdapter(new DirectoryFileTree(temp.file("root")))
-
-        expect:
-        new AllFromJarRebuildInfo(new JarArchive(new File("j"), classes)).changedClassesInJar == ["com.foo.Foo", "com.Bar"] as Set
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProviderTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProviderTest.groovy
deleted file mode 100644
index 1ad12ab..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/ClassNameProviderTest.groovy
+++ /dev/null
@@ -1,42 +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.tasks.compile.incremental
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Subject
-
-class ClassNameProviderTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-    @Subject provider = new ClassNameProvider(temp.createDir("root/dir"))
-
-    def "provides class name"() {
-        expect:
-        "foo.bar.Foo" == provider.provideName(temp.file("root/dir/foo/bar/Foo.class"))
-        "Foo" == provider.provideName(temp.file("root/dir/Foo.class"))
-        'Foo$Bar' == provider.provideName(temp.file('root/dir/Foo$Bar.class'))
-    }
-
-    def "fails when class is outside of root"() {
-        when:
-        provider.provideName(temp.file("foo/Foo.class"))
-        then:
-        thrown(IllegalArgumentException)
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupportTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupportTest.groovy
deleted file mode 100644
index f77f7ab..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/IncrementalCompilationSupportTest.groovy
+++ /dev/null
@@ -1,52 +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.tasks.compile.incremental
-
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfo
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoExtractor
-import org.gradle.api.internal.tasks.compile.incremental.graph.ClassDependencyInfoSerializer
-import org.gradle.api.tasks.compile.CompileOptions
-import spock.lang.Specification
-import spock.lang.Subject
-
-class IncrementalCompilationSupportTest extends Specification {
-
-    def options = Mock(CompileOptions)
-    def extractor = Mock(ClassDependencyInfoExtractor)
-    def serializer = Mock(ClassDependencyInfoSerializer)
-    def feeder = Mock(JarSnapshotFeeder)
-
-    @Subject support = new IncrementalCompilationSupport(feeder)
-
-    def "analyzes class dependencies when incremental"() {
-        options.incremental >> true
-        def jars = [Mock(JarArchive)]
-
-        when: support.compilationComplete(options, extractor, serializer, jars)
-        then:
-        1 * extractor.extractInfo("") >> Stub(ClassDependencyInfo)
-        1 * serializer.writeInfo(_ as ClassDependencyInfo)
-//        1 * feeder.storeJarSnapshots(jars)
-    }
-
-    def "does nothing when not incremental"() {
-        options.incremental >> false
-
-        when: support.compilationComplete(options, extractor, serializer, [])
-        then: 0 * extractor._
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapperTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapperTest.groovy
deleted file mode 100644
index c3d8354..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/InputOutputMapperTest.groovy
+++ /dev/null
@@ -1,53 +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.tasks.compile.incremental
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Subject
-
-class InputOutputMapperTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-    @Subject mapper = new InputOutputMapper([temp.file("src/main/java"), temp.file("src/main/java2")], temp.file("out"))
-
-    def "knows java source class relative path"() {
-        expect:
-        mapper.toJavaSourceClass(temp.file("src/main/java/Foo.java")).relativePath == "Foo.java"
-        mapper.toJavaSourceClass(temp.file("src/main/java/org/bar/Bar.java")).relativePath == "org/bar/Bar.java"
-        mapper.toJavaSourceClass(temp.file("src/main/java2/com/Com.java")).relativePath == "com/Com.java"
-
-        when: mapper.toJavaSourceClass(temp.file("src/main/unknown/Xxx.java"))
-        then: thrown(IllegalArgumentException)
-    }
-
-    def "infers java source class from name"() {
-        temp.createFile("src/main/java/Foo.java")
-        temp.createFile("src/main/java/org/bar/Bar.java")
-        temp.createFile("src/main/java2/com/Com.java")
-        temp.createFile("src/main/unknown/Xxx.java")
-
-        expect:
-        mapper.toJavaSourceClass("Foo").relativePath == "Foo.java"
-        mapper.toJavaSourceClass("org.bar.Bar").relativePath == "org/bar/Bar.java"
-        mapper.toJavaSourceClass("com.Com").relativePath == "com/Com.java"
-
-        when: mapper.toJavaSourceClass(temp.file("unknown.Xxx"))
-        then: thrown(IllegalArgumentException)
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCacheTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCacheTest.groovy
deleted file mode 100644
index 2811a5c..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotCacheTest.groovy
+++ /dev/null
@@ -1,50 +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.tasks.compile.incremental
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Subject
-
-class JarSnapshotCacheTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-    @Subject cache = new JarSnapshotCache(temp.file("cache.bin"))
-
-    def "empty cache"() {
-        expect:
-        !cache.getSnapshot(new File("foo.jar"))
-    }
-
-    def "caches snapshots"() {
-        when:
-        cache.putSnapshots([(new File("foo.jar")): new JarSnapshot(["Foo": "f".bytes])])
-
-        then:
-        cache.getSnapshot(new File("foo.jar")).classHashes == ["Foo": "f".bytes]
-    }
-
-    def "caches snapshots in file"() {
-        when:
-        cache.putSnapshots([(new File("foo.jar")): new JarSnapshot(["Foo": "f".bytes])])
-
-        then:
-        def cache2 = new JarSnapshotCache(temp.file("cache.bin"))
-        cache2.getSnapshot(new File("foo.jar")).classHashes == ["Foo": "f".bytes]
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeederTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeederTest.groovy
deleted file mode 100644
index 4e7d139..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotFeederTest.groovy
+++ /dev/null
@@ -1,72 +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.tasks.compile.incremental
-
-import org.gradle.api.file.FileTree
-import spock.lang.Specification
-import spock.lang.Subject
-
-class JarSnapshotFeederTest extends Specification {
-
-    def cache = Mock(JarSnapshotCache)
-    def snapshotter = Mock(JarSnapshotter)
-
-    @Subject feeder = new JarSnapshotFeeder(cache, snapshotter)
-
-    def "stores jar snapshot"() {
-        def jar1 = new JarArchive(new File("jar1.jar"), Mock(FileTree))
-        def snapshot = Mock(JarSnapshot)
-
-        when:
-        feeder.storeJarSnapshots([jar1])
-
-        then:
-        1 * cache.getSnapshot(jar1.file)
-        1 * snapshotter.createSnapshot(jar1.contents) >> snapshot
-        1 * cache.putSnapshots([(jar1.file): snapshot])
-        0 * _
-    }
-
-    def "stores multiple snapshots"() {
-        def jar1 = new JarArchive(new File("jar1.jar"), Mock(FileTree))
-        def jar2 = new JarArchive(new File("jar2.jar"), Mock(FileTree))
-
-        when:
-        feeder.storeJarSnapshots([jar1, jar2])
-
-        then:
-        1 * snapshotter.createSnapshot(jar1.contents) >> Mock(JarSnapshot)
-        1 * snapshotter.createSnapshot(jar2.contents) >> Mock(JarSnapshot)
-        1 * cache.putSnapshots({ it.size() == 2})
-    }
-
-    def "avoids storing unchanged jar snapshots"() {
-        def jar1 = new JarArchive(new File("jar1.jar"), Mock(FileTree))
-        def jar2 = new JarArchive(new File("jar2.jar"), Mock(FileTree))
-
-        when:
-        feeder.changedJar(jar2.file)
-        feeder.storeJarSnapshots([jar1, jar2])
-
-        then:
-        1 * cache.getSnapshot(jar1.file) >> Mock(JarSnapshot)
-        1 * cache.getSnapshot(jar2.file) >> Mock(JarSnapshot)
-        1 * snapshotter.createSnapshot(jar2.contents) >> Mock(JarSnapshot)
-        1 * cache.putSnapshots({ it[jar2.file] })
-        0 * _
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotTest.groovy
deleted file mode 100644
index 136cbc7..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotTest.groovy
+++ /dev/null
@@ -1,49 +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.tasks.compile.incremental
-
-import spock.lang.Specification
-
-class JarSnapshotTest extends Specification {
-
-    def "knows when snapshots are the same"() {
-        JarSnapshot s1 = new JarSnapshot(["com.Foo": "f".bytes, "Bar": "b".bytes])
-        JarSnapshot s2 = new JarSnapshot(["com.Foo": "f".bytes, "Bar": "b".bytes])
-
-        expect:
-        s1.compareToSnapshot(s2).changedClasses.isEmpty()
-        s2.compareToSnapshot(s1).changedClasses.isEmpty()
-    }
-
-    def "knows when other snapshots have extra/missing classes"() {
-        JarSnapshot s1 = new JarSnapshot(["com.Foo": "f".bytes, "Bar": "b".bytes, "Car": "c".bytes])
-        JarSnapshot s2 = new JarSnapshot(["com.Foo": "f".bytes])
-
-        expect:
-        s1.compareToSnapshot(s2).changedClasses == ["Bar", "Car"]
-        s2.compareToSnapshot(s1).changedClasses == [] //ignore class additions
-    }
-
-    def "knows when other snapshots have class with different hash"() {
-        JarSnapshot s1 = new JarSnapshot(["com.Foo": "f".bytes, "Bar": "b".bytes, "Car": "c".bytes])
-        JarSnapshot s2 = new JarSnapshot(["Car": "xxx".bytes, "com.Foo": "f".bytes])
-
-        expect:
-        s1.compareToSnapshot(s2).changedClasses == ["Bar", "Car"]
-        s2.compareToSnapshot(s1).changedClasses == ["Car"]
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotterTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotterTest.groovy
deleted file mode 100644
index b6fc15e..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JarSnapshotterTest.groovy
+++ /dev/null
@@ -1,52 +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.tasks.compile.incremental
-
-import org.gradle.api.internal.file.collections.DirectoryFileTree
-import org.gradle.api.internal.file.collections.FileTreeAdapter
-import org.gradle.api.internal.hash.Hasher
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-import spock.lang.Subject
-
-class JarSnapshotterTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-    def hasher = Mock(Hasher)
-    @Subject snapshotter = new JarSnapshotter(hasher);
-
-    def "creates snapshot of an empty jar"() {
-        expect:
-        def snapshot = snapshotter.createSnapshot(new FileTreeAdapter(new DirectoryFileTree(new File("missing"))))
-        snapshot.classHashes.isEmpty()
-    }
-
-    def "creates snapshot of a jar with classes"() {
-        def f1 = temp.createFile("foo/Foo.class")
-        def f2 = temp.createFile("foo/com/Foo2.class")
-
-        when:
-        def snapshot = snapshotter.createSnapshot(new FileTreeAdapter(new DirectoryFileTree(temp.file("foo"))))
-
-        then:
-        snapshot.classHashes.keySet() == ["Foo", "com.Foo2"] as Set
-        1 * hasher.hash(f1);
-        1 * hasher.hash(f2);
-        0 * _._
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClassTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClassTest.groovy
deleted file mode 100644
index 70e44d1..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/JavaSourceClassTest.groovy
+++ /dev/null
@@ -1,38 +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.tasks.compile.incremental
-
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class JavaSourceClassTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-
-    def "knows output file"() {
-        expect:
-        new JavaSourceClass("com/Foo.java", temp.file("dir")).outputFile == temp.file("dir/com/Foo.class")
-        new JavaSourceClass("Foo.java", temp.file("dir")).outputFile == temp.file("dir/Foo.class")
-    }
-
-    def "knows class name"() {
-        expect:
-        new JavaSourceClass("com/Foo.java", temp.file("dir")).className == "com.Foo"
-        new JavaSourceClass("Foo.java", temp.file("dir")).className == "Foo"
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapperTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapperTest.groovy
deleted file mode 100644
index fd6ef5b..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/OutputClassMapperTest.groovy
+++ /dev/null
@@ -1,28 +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.tasks.compile.incremental
-
-import spock.lang.Specification
-
-class OutputClassMapperTest extends Specification {
-
-    def "maps output classes"() {
-        expect:
-        new OutputClassMapper(new File("root")).getOutputFile("Foo") == new File("root/Foo.class")
-        new OutputClassMapper(new File("root")).getOutputFile("com.org.Bar") == new File("root/com/org/Bar.class")
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/AccessedFromPrivateClass.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/AccessedFromPrivateClass.java
deleted file mode 100644
index a4660dc..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/AccessedFromPrivateClass.java
+++ /dev/null
@@ -1,24 +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.tasks.compile.incremental.analyzer;
-
-public class AccessedFromPrivateClass {
-
-    public String toString() {
-        return "foo";
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzerTest.groovy
deleted file mode 100644
index 81abaaa..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/ClassDependenciesAnalyzerTest.groovy
+++ /dev/null
@@ -1,74 +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.tasks.compile.incremental.analyzer
-
-import org.gradle.api.internal.tasks.compile.incremental.analyzer.annotations.*
-import spock.lang.Specification
-import spock.lang.Subject
-
-class ClassDependenciesAnalyzerTest extends Specification {
-
-    @Subject analyzer = new ClassDependenciesAnalyzer()
-
-    private ClassAnalysis analyze(Class foo) {
-        analyzer.getClassAnalysis(foo.name, classStream(foo))
-    }
-
-    def "knows dependencies of a java class"() {
-        expect:
-        analyze(SomeOtherClass).classDependencies == [YetAnotherClass.name, SomeClass.name]
-    }
-
-    def "knows basic class dependencies of a groovy class"() {
-        def deps = analyze(ClassDependenciesAnalyzerTest).classDependencies
-
-        expect:
-        deps.contains(Specification.class.name)
-        //deps.contains(ClassDependenciesAnalyzer.class.name) // why this does not work (is it because of groovy)?
-    }
-
-    def "knows if a class have non-private constants"() {
-        expect:
-        analyze(HasNonPrivateConstants).classDependencies == [UsedByNonPrivateConstantsClass.name]
-        analyze(HasNonPrivateConstants).dependentToAll
-
-        analyze(HasPublicConstants).classDependencies == []
-        analyze(HasPublicConstants).dependentToAll
-
-        analyze(HasPrivateConstants).classDependencies == [HasNonPrivateConstants.name]
-        !analyze(HasPrivateConstants).dependentToAll
-    }
-
-    def "knows if a class uses annotations"() {
-        expect:
-        analyze(UsesRuntimeAnnotation).classDependencies == []
-        analyze(SomeRuntimeAnnotation).classDependencies == []
-        analyze(SomeRuntimeAnnotation).dependentToAll
-
-        analyze(UsesClassAnnotation).classDependencies == []
-        analyze(SomeClassAnnotation).classDependencies == []
-        analyze(SomeClassAnnotation).dependentToAll
-
-        analyze(UsesSourceAnnotation).classDependencies == [] //source annotations are wiped from the bytecode
-        analyze(SomeSourceAnnotation).classDependencies == []
-        analyze(SomeSourceAnnotation).dependentToAll
-    }
-
-    InputStream classStream(Class aClass) {
-        aClass.getResourceAsStream(aClass.getSimpleName() + ".class")
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasNonPrivateConstants.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasNonPrivateConstants.java
deleted file mode 100644
index 99a9ec5..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasNonPrivateConstants.java
+++ /dev/null
@@ -1,21 +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.tasks.compile.incremental.analyzer;
-
-public class HasNonPrivateConstants extends UsedByNonPrivateConstantsClass {
-    final static int X = 1;
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPrivateConstants.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPrivateConstants.java
deleted file mode 100644
index a666297..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPrivateConstants.java
+++ /dev/null
@@ -1,22 +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.tasks.compile.incremental.analyzer;
-
-public class HasPrivateConstants {
-    private final static int X = 1;
-    private final static HasNonPrivateConstants C = new HasNonPrivateConstants();
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPublicConstants.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPublicConstants.java
deleted file mode 100644
index a625094..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/HasPublicConstants.java
+++ /dev/null
@@ -1,21 +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.tasks.compile.incremental.analyzer;
-
-public class HasPublicConstants {
-    public final static int X = 1;
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeClass.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeClass.java
deleted file mode 100644
index aa446c8..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeClass.java
+++ /dev/null
@@ -1,35 +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.tasks.compile.incremental.analyzer;
-
-import java.util.*;
-
-public class SomeClass {
-
-    List<Integer> field = new LinkedList<Integer>();
-
-    private Set<String> stuff(HashMap map) {
-        System.out.println(new Foo());
-        return new HashSet<String>();
-    }
-
-    private class Foo {
-        public String toString() {
-            return "" + new AccessedFromPrivateClass();
-        }
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeOtherClass.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeOtherClass.java
deleted file mode 100644
index 0d71da3..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/SomeOtherClass.java
+++ /dev/null
@@ -1,24 +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.tasks.compile.incremental.analyzer;
-
-public class SomeOtherClass extends SomeClass {
-
-    void foo() {
-        System.out.println(new YetAnotherClass());
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/UsedByNonPrivateConstantsClass.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/UsedByNonPrivateConstantsClass.java
deleted file mode 100644
index 3a8711a..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/UsedByNonPrivateConstantsClass.java
+++ /dev/null
@@ -1,20 +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.tasks.compile.incremental.analyzer;
-
-public class UsedByNonPrivateConstantsClass {
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/YetAnotherClass.java b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/YetAnotherClass.java
deleted file mode 100644
index 0b9b1a6..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/analyzer/YetAnotherClass.java
+++ /dev/null
@@ -1,20 +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.tasks.compile.incremental.analyzer;
-
-public class YetAnotherClass {
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractorTest.groovy
deleted file mode 100644
index 089b151..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoExtractorTest.groovy
+++ /dev/null
@@ -1,37 +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.tasks.compile.incremental.graph
-
-import org.gradle.api.internal.tasks.compile.incremental.analyzer.*
-import spock.lang.Specification
-
-class ClassDependencyInfoExtractorTest extends Specification {
-
-    def "knows recursive dependency tree"() {
-        def info = new ClassDependencyInfoExtractor(new File(ClassDependencyInfoExtractorTest.classLoader.getResource("").toURI())).extractInfo("org.gradle.api.internal.tasks.compile.incremental")
-        expect:
-        info.getActualDependents(SomeClass.name) == [SomeOtherClass.name] as Set
-        info.getActualDependents(SomeOtherClass.name) == [] as Set
-        info.getActualDependents(YetAnotherClass.name) == [SomeOtherClass.name] as Set
-        info.getActualDependents(AccessedFromPrivateClass.name) == [SomeClass.name, SomeOtherClass.name] as Set
-        info.getActualDependents(HasPrivateConstants.name) == [] as Set
-        info.getActualDependents(HasNonPrivateConstants.name) == null
-        info.getActualDependents(UsedByNonPrivateConstantsClass.name) == [HasNonPrivateConstants.name, HasPrivateConstants.name] as Set
-    }
-
-    //TODO SF tighten and refactor the coverage
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializerTest.groovy
deleted file mode 100644
index d1a6e8a..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/graph/ClassDependencyInfoSerializerTest.groovy
+++ /dev/null
@@ -1,38 +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.tasks.compile.incremental.graph
-
-import org.gradle.api.internal.tasks.compile.incremental.ClassDependents
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-class ClassDependencyInfoSerializerTest extends Specification {
-
-    @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
-
-    def "stores dependency info"() {
-        def s = new ClassDependencyInfoSerializer(temp.file("foo.bin"))
-
-        when:
-        s.writeInfo(new ClassDependencyInfo(["foo.Foo": new ClassDependents().addClass("bar.Bar")]))
-        def info = s.readInfo()
-
-        then:
-        info.getActualDependents("foo.Foo") == ["bar.Bar"] as Set
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
index 8066fdd..102383a 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
@@ -24,6 +24,6 @@ class DefaultTestClassDescriptorTest extends Specification {
         DefaultTestClassDescriptor descriptor = new DefaultTestClassDescriptor('id', '<class-name>')
 
         expect:
-        descriptor.toString() == 'test class <class-name>'
+        descriptor.toString() == 'Test class <class-name>'
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptorTest.groovy
index e78f85d..2f6a7a2 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestSuiteDescriptorTest.groovy
@@ -25,6 +25,6 @@ class DefaultTestSuiteDescriptorTest extends Specification {
         DefaultTestSuiteDescriptor descriptor = new DefaultTestSuiteDescriptor('id', '<suite-name>')
 
         expect:
-        descriptor.toString() == 'test \'<suite-name>\''
+        descriptor.toString() == "Test suite '<suite-name>'"
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuterTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuterTest.groovy
index 51ce83a..476c509 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuterTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuterTest.groovy
@@ -43,6 +43,7 @@ class DefaultTestExecuterTest extends Specification {
     def setup() {
         _ * testTask.testFramework >> testFramework
         _ * testTask.getCandidateClassFiles() >> Mock(FileTree)
+        _ * testTask.getPath() >> ':'
         _ * actorFactory.createActor(_) >> resultProcessorActor
         _ * resultProcessorActor.getProxy(_) >> resultProcessor
         _ * testTask.isScanForTestClasses() >> true
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorData.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorData.groovy
index 99f6436..8ffa3af 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorData.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorData.groovy
@@ -276,6 +276,8 @@ public class ATestClassWhichCannotBeLoaded {
     static {
         throw new NoClassDefFoundError()
     }
+
+    @Test public void pass() {}
 }
 
 public class ATestClassWithSeveralMethods {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
index b5ee48e..bd44adf 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
@@ -18,9 +18,8 @@ package org.gradle.api.internal.tasks.testing.junit
 
 import org.gradle.api.internal.tasks.testing.DefaultTestClassRunInfo
 import org.gradle.api.internal.tasks.testing.TestResultProcessor
-import org.gradle.api.tasks.testing.junit.JUnitOptions
+import org.gradle.internal.TrueTimeProvider
 import org.gradle.internal.id.LongIdGenerator
-import org.gradle.logging.StandardOutputRedirector
 import org.gradle.messaging.actor.TestActorFactory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
@@ -35,12 +34,12 @@ class JUnitTestClassProcessorTest extends Specification {
     @Rule TestNameTestDirectoryProvider tmp = new TestNameTestDirectoryProvider()
 
     def processor = Mock(TestResultProcessor)
-    def spec = new JUnitSpec(new JUnitOptions(), [] as Set)
+    def spec = new JUnitSpec([] as Set, [] as Set, [] as Set)
     
     @Subject classProcessor = withSpec(spec)
 
     JUnitTestClassProcessor withSpec(spec) {
-        new JUnitTestClassProcessor(spec, new LongIdGenerator(), new TestActorFactory(), {} as StandardOutputRedirector)
+        new JUnitTestClassProcessor(spec, new LongIdGenerator(), new TestActorFactory(), new TrueTimeProvider())
     }
 
     void process(Class ... clazz) {
@@ -204,7 +203,7 @@ class JUnitTestClassProcessorTest extends Specification {
         when: process([testClassName])
 
         then: 1 * processor.started({ it.id == 1 }, { it.parentId == null })
-        then: 1 * processor.started({ it.id == 2 && it.name == 'initializationError' && it.className == testClassName }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'pass' && it.className == testClassName }, { it.parentId == 1 })
         then: 1 * processor.failure(2, _ as NoClassDefFoundError)
         then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
@@ -222,7 +221,7 @@ class JUnitTestClassProcessorTest extends Specification {
     }
 
     def "executes specific method"() {
-        classProcessor = withSpec(new JUnitSpec(new JUnitOptions(), [ATestClassWithSeveralMethods.name + ".pass"] as Set))
+        classProcessor = withSpec(new JUnitSpec([] as Set, [] as Set, [ATestClassWithSeveralMethods.name + ".pass"] as Set))
 
         when: process(ATestClassWithSeveralMethods)
 
@@ -234,7 +233,7 @@ class JUnitTestClassProcessorTest extends Specification {
     }
 
     def "executes multiple specific methods"() {
-        classProcessor = withSpec(new JUnitSpec(new JUnitOptions(), [ATestClassWithSeveralMethods.name + ".pass",
+        classProcessor = withSpec(new JUnitSpec([] as Set, [] as Set, [ATestClassWithSeveralMethods.name + ".pass",
                 ATestClassWithSeveralMethods.name + ".pass2"] as Set))
 
         when: process(ATestClassWithSeveralMethods)
@@ -247,7 +246,7 @@ class JUnitTestClassProcessorTest extends Specification {
     }
 
     def "executes methods from multiple classes by pattern"() {
-        classProcessor = withSpec(new JUnitSpec(new JUnitOptions(), ["*Methods.*Slowly*"] as Set))
+        classProcessor = withSpec(new JUnitSpec([] as Set, [] as Set, ["*Methods.*Slowly*"] as Set))
 
         when: process(ATestClassWithSeveralMethods, ATestClassWithSlowMethods, ATestClass)
 
@@ -262,7 +261,7 @@ class JUnitTestClassProcessorTest extends Specification {
     }
 
     def "executes no methods when method name does not match"() {
-        classProcessor = withSpec(new JUnitSpec(new JUnitOptions(), ["does not exist"] as Set))
+        classProcessor = withSpec(new JUnitSpec([] as Set, [] as Set, ["does not exist"] as Set))
 
         when: process(ATestClassWithSeveralMethods)
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy
index af8036c..a1fc20e 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/CompositeTestResultsTest.groovy
@@ -69,6 +69,26 @@ class CompositeTestResultsTest extends Specification {
         results.formattedSuccessRate == '50%'
     }
 
+    def formatsSuccessRateWhenAllTestsFail() {
+        def failed = results.addTest(test())
+        results.failed(failed)
+
+        expect:
+        results.successRate == 0
+        results.formattedSuccessRate == '0%'
+    }
+
+    def formatsSuccessRateWhenAllTestsAreIgnored() {
+        results.addTest(test())
+        results.addTest(test())
+        results.ignored(test());
+        results.ignored(test());
+
+        expect:
+        results.successRate == null
+        results.formattedSuccessRate == '-'
+    }
+
     def formatsDurationWhenNoTests() {
         expect:
         results.formattedDuration == '-'
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy
index 7d621fe..2ed2ea7 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/report/DefaultTestReportTest.groovy
@@ -49,67 +49,6 @@ class DefaultTestReportTest extends Specification {
         index.assertHasNoNavLinks()
     }
 
-    TestResultsProvider buildResults(Closure closure) {
-        ConfigureUtil.configure(closure, new BuildableTestResultsProvider())
-    }
-
-    TestResultsProvider passingBuildResults() {
-        buildResults {
-            testClassResult("org.gradle.passing.Passed") {
-                testcase("passed") {
-                    duration = 1000;
-                }
-            }
-            testClassResult("org.gradle.passing.subpackage.AlsoPassed") {
-                testcase("passedToo") {
-                    duration = 1000;
-                    stdout "this is\nstandard output"
-                    stderr "this is\nstandard error"
-                }
-            }
-        }
-    }
-
-    TestResultsProvider failingBuildResults() {
-        buildResults {
-            testClassResult("org.gradle.passing.Passed") {
-                testcase("passed") {
-                    duration = 1000;
-                }
-            }
-            testClassResult("org.gradle.passing.AlsoPassed") {
-                testcase("passedToo") {
-                    duration = 1000;
-                    stdout "this is\nstandard output"
-                    stderr "this is\nstandard error"
-                }
-            }
-            testClassResult("org.gradle.ignoring.SomeIgnoredSomePassed") {
-                testcase("passed") {
-                    duration = 1000;
-                }
-                testcase("ignored") {
-                    duration = 1000;
-                    ignore()
-                }
-            }
-            testClassResult("org.gradle.failing.SomeIgnoredSomePassedSomeFailed") {
-                testcase("passed") {
-                    duration = 1000;
-                }
-                testcase("ignored") {
-                    duration = 1000;
-                    ignore()
-                }
-                testcase("failed") {
-                    duration = 1000;
-                    failure("something failed", "this is the failure\nat someClass")
-                }
-            }
-        }
-    }
-
-
     def generatesReportWithAggregatedIndexPageForBuildWithNoFailures() {
         given:
         def testTestResults = passingBuildResults()
@@ -163,10 +102,8 @@ class DefaultTestReportTest extends Specification {
         alsoPassedClassDetails.assertSuccessRate("100%");
         alsoPassedClassDetails.assertPassed()
         alsoPassedClassDetails.assertLinksTo("classes/org.gradle.passing.subpackage.AlsoPassed.html");
-
     }
 
-
     def generatesReportWithAggregatedIndexPageForFailingBuild() {
         given:
         def testTestResults = failingBuildResults()
@@ -252,7 +189,6 @@ class DefaultTestReportTest extends Specification {
         someFailedClassDetails.assertLinksTo("classes/org.gradle.failing.SomeIgnoredSomePassedSomeFailed.html");
     }
 
-
     def generatesReportWithAggregatedPackagePages() {
         given:
         def testTestResults = failingBuildResults()
@@ -327,7 +263,6 @@ class DefaultTestReportTest extends Specification {
         someFailedClassDetails.assertLinksTo("classes/org.gradle.failing.SomeIgnoredSomePassedSomeFailed.html");
     }
 
-
     def generatesReportWithClassPages() {
         given:
         def testTestResults = failingBuildResults()
@@ -405,41 +340,6 @@ class DefaultTestReportTest extends Specification {
         failingClassFile.assertHasFailure('failed', 'something failed\n\nthis is the failure\nat someClass\n')
     }
 
-    TestResultsProvider aggregatedBuildResultsRun1() {
-        buildResults {
-            testClassResult("org.gradle.aggregation.FooTest") {
-                testcase("first") {
-                    duration = 1000;
-                }
-            }
-            testClassResult("org.gradle.aggregation.BarTest") {
-                testcase("second") {
-                    duration = 1000;
-                    stdout "this is\nstandard output"
-                    stderr "this is\nstandard error"
-                }
-            }
-        }
-    }
-
-    TestResultsProvider aggregatedBuildResultsRun2(methodNameSuffix = "") {
-        buildResults {
-            testClassResult("org.gradle.aggregation.FooTest") {
-                testcase("first" + methodNameSuffix) {
-                    duration = 1000;
-                }
-            }
-            testClassResult("org.gradle.aggregation.BarTest") {
-                testcase("second" + methodNameSuffix) {
-                    duration = 1100;
-                    stdout "failed on second run\nstandard output"
-                    stderr "failed on second run\nstandard error"
-                    failure("something failed", "this is the failure\nat someClass")
-                }
-            }
-        }
-    }
-
     def aggregateSameTestsRunWithDifferentResults() {
         given:
         def firstTestResults = aggregatedBuildResultsRun1()
@@ -577,6 +477,101 @@ class DefaultTestReportTest extends Specification {
         testClassFile.assertHasStandardError('err:\u0102')
     }
 
+    TestResultsProvider buildResults(Closure closure) {
+        ConfigureUtil.configure(closure, new BuildableTestResultsProvider())
+    }
+
+    TestResultsProvider passingBuildResults() {
+        buildResults {
+            testClassResult("org.gradle.passing.Passed") {
+                testcase("passed") {
+                    duration = 1000;
+                }
+            }
+            testClassResult("org.gradle.passing.subpackage.AlsoPassed") {
+                testcase("passedToo") {
+                    duration = 1000;
+                    stdout "this is\nstandard output"
+                    stderr "this is\nstandard error"
+                }
+            }
+        }
+    }
+
+    TestResultsProvider failingBuildResults() {
+        buildResults {
+            testClassResult("org.gradle.passing.Passed") {
+                testcase("passed") {
+                    duration = 1000;
+                }
+            }
+            testClassResult("org.gradle.passing.AlsoPassed") {
+                testcase("passedToo") {
+                    duration = 1000;
+                    stdout "this is\nstandard output"
+                    stderr "this is\nstandard error"
+                }
+            }
+            testClassResult("org.gradle.ignoring.SomeIgnoredSomePassed") {
+                testcase("passed") {
+                    duration = 1000;
+                }
+                testcase("ignored") {
+                    duration = 1000;
+                    ignore()
+                }
+            }
+            testClassResult("org.gradle.failing.SomeIgnoredSomePassedSomeFailed") {
+                testcase("passed") {
+                    duration = 1000;
+                }
+                testcase("ignored") {
+                    duration = 1000;
+                    ignore()
+                }
+                testcase("failed") {
+                    duration = 1000;
+                    failure("something failed", "this is the failure\nat someClass")
+                }
+            }
+        }
+    }
+
+    TestResultsProvider aggregatedBuildResultsRun1() {
+        buildResults {
+            testClassResult("org.gradle.aggregation.FooTest") {
+                testcase("first") {
+                    duration = 1000;
+                }
+            }
+            testClassResult("org.gradle.aggregation.BarTest") {
+                testcase("second") {
+                    duration = 1000;
+                    stdout "this is\nstandard output"
+                    stderr "this is\nstandard error"
+                }
+            }
+        }
+    }
+
+    TestResultsProvider aggregatedBuildResultsRun2(methodNameSuffix = "") {
+        buildResults {
+            testClassResult("org.gradle.aggregation.FooTest") {
+                testcase("first" + methodNameSuffix) {
+                    duration = 1000;
+                }
+            }
+            testClassResult("org.gradle.aggregation.BarTest") {
+                testcase("second" + methodNameSuffix) {
+                    duration = 1100;
+                    stdout "failed on second run\nstandard output"
+                    stderr "failed on second run\nstandard error"
+                    failure("something failed", "this is the failure\nat someClass")
+                }
+            }
+        }
+    }
+
     def results(TestFile file) {
         return new HtmlTestResultsFixture(file)
     }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/Binary2JUnitXmlReportGeneratorSpec.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/Binary2JUnitXmlReportGeneratorSpec.groovy
index 80369f5..342510e 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/Binary2JUnitXmlReportGeneratorSpec.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/Binary2JUnitXmlReportGeneratorSpec.groovy
@@ -18,7 +18,6 @@ package org.gradle.api.internal.tasks.testing.junit.result
 
 import org.gradle.api.Action
 import org.gradle.api.GradleException
-import org.gradle.api.tasks.testing.TestResult
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
 import spock.lang.Specification
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriterSpec.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriterSpec.groovy
index 31a2cfc..482db41 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriterSpec.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/result/JUnitXmlResultWriterSpec.groovy
@@ -183,6 +183,6 @@ class JUnitXmlResultWriterSpec extends Specification {
     def getXml(TestClassResult result) {
         def text = new ByteArrayOutputStream()
         generator.write(result, text)
-        return text.toString("UTF-8").replace(SystemProperties.lineSeparator, "\n")
+        return text.toString("UTF-8").replace(SystemProperties.instance.lineSeparator, "\n")
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingContainerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingContainerTest.groovy
index 43a0202..ec22cd7 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingContainerTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingContainerTest.groovy
@@ -28,7 +28,7 @@ import org.gradle.api.Action
 import org.gradle.api.tasks.testing.logging.TestLogging
 
 class DefaultTestLoggingContainerTest extends Specification {
-    DefaultTestLoggingContainer container = new DefaultTestLoggingContainer(new DirectInstantiator())
+    DefaultTestLoggingContainer container = new DefaultTestLoggingContainer(DirectInstantiator.INSTANCE)
 
     def "sets defaults for level ERROR"() {
         def logging = container.get(LogLevel.ERROR)
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingTest.groovy
index c696929..f902e48 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/DefaultTestLoggingTest.groovy
@@ -18,8 +18,9 @@ package org.gradle.api.internal.tasks.testing.logging
 
 import spock.lang.Specification
 
-import static org.gradle.api.tasks.testing.logging.TestStackTraceFilter.*
 import static org.gradle.api.tasks.testing.logging.TestLogEvent.*
+import static org.gradle.api.tasks.testing.logging.TestStackTraceFilter.GROOVY
+import static org.gradle.api.tasks.testing.logging.TestStackTraceFilter.TRUNCATE
 
 class DefaultTestLoggingTest extends Specification {
     private logging = new DefaultTestLogging()
@@ -63,4 +64,20 @@ class DefaultTestLoggingTest extends Specification {
         then:
         logging.stackTraceFilters == [TRUNCATE, GROOVY] as Set
     }
+
+    def "allows standardStreams to be turned on and off"() {
+        when:
+        logging.setShowStandardStreams(true)
+
+        then:
+        logging.showStandardStreams
+        logging.events == [STANDARD_OUT, STANDARD_ERROR] as Set
+
+        when:
+        logging.setShowStandardStreams(false)
+
+        then:
+        !logging.showStandardStreams
+        logging.events == [] as Set
+    }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/SimpleTestDescriptor.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/SimpleTestDescriptor.groovy
index 79e5a8c..bc24c61 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/SimpleTestDescriptor.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/logging/SimpleTestDescriptor.groovy
@@ -13,19 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
-
 package org.gradle.api.internal.tasks.testing.logging
 
-import org.gradle.api.tasks.testing.TestDescriptor
 import org.gradle.api.internal.tasks.testing.TestDescriptorInternal
 
 class SimpleTestDescriptor implements TestDescriptorInternal {
     String name = "testName"
     String className = "ClassName"
     boolean composite = false
-    TestDescriptor parent = null
+    TestDescriptorInternal parent = null
     Object getId() {
         "${parent?.id}$className$name" as String
     }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy
index d59e224..20f6f9c 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy
@@ -16,96 +16,89 @@
 
 package org.gradle.api.internal.tasks.testing.processors
 
-import org.gradle.api.tasks.testing.TestOutputEvent
-import org.gradle.logging.StandardOutputRedirector
-import spock.lang.Specification
 import org.gradle.api.internal.tasks.testing.*
+import spock.lang.Specification
+import spock.lang.Subject
 
 class CaptureTestOutputTestResultProcessorTest extends Specification {
-    def TestResultProcessor target = Mock()
-    def StandardOutputRedirector redirector = Mock()
-    def processor = new CaptureTestOutputTestResultProcessor(target, redirector)
-
-    def capturesStdOutputAndStdErrorWhileTestIsExecuting() {
-        TestDescriptorInternal test = Mock()
-        TestStartEvent startEvent = Mock()
-        TestCompleteEvent completeEvent = Mock()
-        String testId = 'id'
-        _ * test.getId() >> testId
-        def stdoutListener
-        def stderrListener
-
-        when:
-        processor.started(test, startEvent)
-
-        then:
-        1 * target.started(test, startEvent)
-        1 * redirector.redirectStandardOutputTo(!null) >> { args -> stdoutListener = args[0] }
-        1 * redirector.redirectStandardErrorTo(!null) >> { args -> stderrListener = args[0] }
-        1 * redirector.start()
 
-        when:
-        stdoutListener.onOutput('this is stdout')
-        stderrListener.onOutput('this is stderr')
+    TestResultProcessor target = Mock()
+    TestOutputRedirector redirector = Mock()
+    @Subject processor = new CaptureTestOutputTestResultProcessor(target, redirector)
 
-        then:
-        1 * target.output(testId, { TestOutputEvent event ->
-            event.destination == TestOutputEvent.Destination.StdOut && event.message == 'this is stdout'
-        })
-        1 * target.output(testId, { TestOutputEvent event ->
-            event.destination == TestOutputEvent.Destination.StdErr && event.message == 'this is stderr' })
+    def "starts capturing output"() {
+        def suite = new DefaultTestSuiteDescriptor("1", "Foo")
+        def event = new TestStartEvent(1)
 
         when:
-        processor.completed(testId, completeEvent)
+        processor.started(suite, event)
 
-        then:
-        1 * redirector.stop()
-        1 * target.completed(testId, completeEvent)
+        then: 1 * target.started(suite, event)
+        then: 1 * redirector.setOutputOwner("1")
+        then: 1 * redirector.startRedirecting()
+        0 * _
     }
 
-    def "configures redirector for suite and test"() {
-        //this test is not beautiful and it is also very strict.
-        //it's to avoid tricky bugs related to not having correct test ids passed onto the 'output' events
-        //it is covered in the integration tests but I decided to have hardcore mocking test for this algorithm as well :)
-        given:
-        def suite = new DefaultTestClassDescriptor("1", "DogTest");
-        def test = new DefaultTestClassDescriptor("1.1", "shouldBark");
+    def "starts capturing only on first test"() {
+        def test = new DefaultTestDescriptor("2", "Bar", "Baz")
+        def testEvent = new TestStartEvent(2, "1")
 
-        when:
-        processor.started(suite, Mock(TestStartEvent))
+        processor.started(new DefaultTestSuiteDescriptor("1", "Foo"), new TestStartEvent(1))
+
+        when: processor.started(test, testEvent)
 
         then:
-        1 * redirector.start()
+        1 * target.started(test, testEvent)
+        1 * redirector.setOutputOwner("2")
+        0 * _
+    }
 
-        1 * redirector.redirectStandardErrorTo({ it.testId == '1' })
-        1 * redirector.redirectStandardOutputTo({ it.testId == '1' })
+    def "when test completes its parent will be the owner of output"() {
+        def test = new DefaultTestDescriptor("2", "Bar", "Baz")
+        def testEvent = new TestStartEvent(2, "99")
+        def complete = new TestCompleteEvent(1)
 
-        0 * redirector._
+        processor.started(new DefaultTestSuiteDescriptor("1", "Foo"), new TestStartEvent(1))
+        processor.started(test, testEvent)
 
-        when:
-        processor.started(test, Mock(TestStartEvent))
+        when: processor.completed("2", complete)
 
         then:
-        1 * redirector.redirectStandardErrorTo({ it.testId == '1.1' })
-        1 * redirector.redirectStandardOutputTo({ it.testId == '1.1' })
+        1 * redirector.setOutputOwner("99")
+        1 * target.completed("2", complete)
+        0 * _
+    }
 
-        0 * redirector._
+    def "when test without parent completes the root suite be the owner of output"() {
+        def test = new DefaultTestDescriptor("2", "Bar", "Baz")
+        def testEvent = new TestStartEvent(2, null)
+        def complete = new TestCompleteEvent(1)
 
-        when:
-        processor.completed('1.1', Mock(TestCompleteEvent))
+        processor.started(new DefaultTestSuiteDescriptor("1", "Foo"), new TestStartEvent(1))
+        processor.started(test, testEvent)
+
+        when: processor.completed("2", complete)
 
         then:
-        1 * redirector.redirectStandardErrorTo({ it.testId == '1' })
-        1 * redirector.redirectStandardOutputTo({ it.testId == '1' })
+        1 * redirector.setOutputOwner("1")
+        1 * target.completed("2", complete)
+        0 * _
+    }
 
-        0 * redirector._
+    def "stops redirecting when suite is completed"() {
+        def test = new DefaultTestDescriptor("2", "Bar", "Baz")
+        def testEvent = new TestStartEvent(2, null)
+        def complete = new TestCompleteEvent(1)
 
-        when:
-        processor.completed('1', Mock(TestCompleteEvent))
+        processor.started(new DefaultTestSuiteDescriptor("1", "Foo"), new TestStartEvent(1))
+        processor.started(test, testEvent)
+        processor.completed("2", complete)
 
-        then:
+        when: processor.completed("1", complete)
 
-        1 * redirector.stop()
-        0 * redirector._
+        then:
+        1 * redirector.stopRedirecting()
+        1 * target.completed("1", complete)
+        0 * _
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainActionTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainActionTest.groovy
index c0cd2a1..35c69da 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainActionTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestMainActionTest.groovy
@@ -13,26 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
-
-
-
-
 package org.gradle.api.internal.tasks.testing.processors
 
-import org.gradle.api.internal.tasks.testing.TestClassProcessor
-import org.gradle.util.JUnit4GroovyMockery
+import org.gradle.api.internal.tasks.testing.*
 import org.gradle.internal.TimeProvider
+import org.gradle.util.JUnit4GroovyMockery
 import org.jmock.integration.junit4.JMock
 import org.junit.Test
 import org.junit.runner.RunWith
+
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import org.gradle.api.internal.tasks.testing.TestResultProcessor
-import org.gradle.api.internal.tasks.testing.TestDescriptorInternal
-import org.gradle.api.internal.tasks.testing.TestStartEvent
-import org.gradle.api.internal.tasks.testing.TestCompleteEvent
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.fail
 
 @RunWith(JMock.class)
 class TestMainActionTest {
@@ -41,7 +33,7 @@ class TestMainActionTest {
     private final TestResultProcessor resultProcessor = context.mock(TestResultProcessor.class)
     private final Runnable detector = context.mock(Runnable.class)
     private final TimeProvider timeProvider = context.mock(TimeProvider.class)
-    private final TestMainAction action = new TestMainAction(detector, processor, resultProcessor, timeProvider)
+    private final TestMainAction action = new TestMainAction(detector, processor, resultProcessor, timeProvider, "123", "Test Run")
 
     @Test
     public void firesStartAndEndEventsAroundDetectorExecution() {
@@ -50,7 +42,7 @@ class TestMainActionTest {
             will(returnValue(100L))
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
             will { TestDescriptorInternal suite, TestStartEvent event ->
-                assertThat(suite.id, equalTo('root'))
+                assertThat(suite.id as String, equalTo("123"))
                 assertThat(event.startTime, equalTo(100L))
             }
             one(processor).startProcessing(withParam(notNullValue()))
@@ -58,8 +50,9 @@ class TestMainActionTest {
             one(processor).stop()
             one(timeProvider).getCurrentTime()
             will(returnValue(200L))
-            one(resultProcessor).completed(withParam(equalTo('root')), withParam(notNullValue()))
+            one(resultProcessor).completed(withParam(equalTo("123")), withParam(notNullValue()))
             will { id, TestCompleteEvent event ->
+                assertThat(id as String, equalTo("123"))
                 assertThat(event.endTime, equalTo(200L))
                 assertThat(event.resultType, nullValue())
             }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirectorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirectorTest.groovy
new file mode 100644
index 0000000..47cbbad
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/TestOutputRedirectorTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.processors
+
+import org.gradle.api.internal.tasks.testing.DefaultTestOutputEvent
+import org.gradle.api.internal.tasks.testing.TestResultProcessor
+import org.gradle.api.tasks.testing.TestOutputEvent
+import org.gradle.logging.StandardOutputRedirector
+import spock.lang.Specification
+import spock.lang.Subject
+
+class TestOutputRedirectorTest extends Specification {
+
+    TestResultProcessor processor = Mock(TestResultProcessor)
+    StandardOutputRedirector redir = Mock(StandardOutputRedirector)
+
+    @Subject redirector = new TestOutputRedirector(processor, redir)
+
+    def "starts redirecting output and error"() {
+        when:
+        redirector.setOutputOwner("1")
+        redirector.startRedirecting()
+
+        then:
+        1 * redir.redirectStandardErrorTo({ it.dest == TestOutputEvent.Destination.StdErr })
+        1 * redir.redirectStandardOutputTo({ it.dest == TestOutputEvent.Destination.StdOut })
+
+        then:
+        1 * redir.start()
+        0 * _
+    }
+
+    def "disallows starting redirecting if test owner not provided"() {
+        when: redirector.startRedirecting()
+        then: thrown(AssertionError)
+    }
+
+    def "allows setting output owner"() {
+        when:
+        redirector.setOutputOwner("1")
+        redirector.startRedirecting()
+
+        then:
+        redirector.outForwarder.outputOwner == "1"
+        redirector.errForwarder.outputOwner == "1"
+
+        when:
+        redirector.setOutputOwner("2")
+
+        then:
+        redirector.outForwarder.outputOwner == "2"
+        redirector.errForwarder.outputOwner == "2"
+    }
+
+    def "passes output events"() {
+        def f = new TestOutputRedirector.Forwarder(processor, TestOutputEvent.Destination.StdErr)
+        f.outputOwner = "5"
+
+        when: f.onOutput("ala")
+
+        then:
+        1 * processor.output("5", { DefaultTestOutputEvent e ->
+            e.destination == TestOutputEvent.Destination.StdErr
+            e.message == "ala"
+        })
+        0 * _
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessorTest.groovy
new file mode 100644
index 0000000..593cbea
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessorTest.groovy
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.testing.results
+
+import org.gradle.api.internal.tasks.testing.*
+import org.gradle.api.tasks.testing.TestDescriptor
+import org.gradle.api.tasks.testing.TestOutputEvent
+import org.gradle.api.tasks.testing.TestResult
+import org.gradle.api.tasks.testing.TestResult.ResultType
+import org.junit.Test
+import spock.lang.Issue
+import spock.lang.Specification
+
+class StateTrackingTestResultProcessorTest extends Specification {
+
+    def listener = Mock(TestListenerInternal.class)
+    def adapter = new StateTrackingTestResultProcessor(listener)
+
+    public void notifiesBefore() {
+        given:
+        def test = new DefaultTestDescriptor("id", "Foo", "bar");
+        def startEvent = new TestStartEvent(100L)
+
+        when:
+        adapter.started(test, startEvent)
+
+        then:
+        1 * listener.started({ it instanceof DecoratingTestDescriptor && it.descriptor == test }, startEvent)
+        0 * _
+    }
+
+    public void notifiesAfter() {
+        given:
+        def test = new DefaultTestDescriptor("id", "Foo", "bar");
+        def startEvent = new TestStartEvent(100L)
+        def completeEvent = new TestCompleteEvent(200L)
+
+        when:
+        adapter.started(test, startEvent)
+        adapter.completed('id', completeEvent)
+
+        then:
+        1 * listener.started(_, _)
+        1 * listener.completed(
+                { it instanceof DecoratingTestDescriptor && it.descriptor == test },
+                { it.successfulTestCount == 1 && it.testCount == 1 && it.failedTestCount == 0 },
+                completeEvent
+        )
+        0 * _
+    }
+
+    public void createsAResultForATestWithFailure() {
+        given:
+        def failure = new RuntimeException()
+        def test = new DefaultTestDescriptor("15", "Foo", "bar");
+        def startEvent = new TestStartEvent(100L)
+        def completeEvent = new TestCompleteEvent(200L)
+
+        when:
+        adapter.started(test, startEvent)
+        adapter.failure('15', failure)
+        adapter.completed('15', completeEvent)
+
+        then:
+        1 * listener.started(_, _)
+        1 * listener.completed({ it.descriptor == test },
+                { it.successfulTestCount == 0 && it.testCount == 1 && it.failedTestCount == 1 && it.exception.is(failure) },
+                completeEvent
+        )
+        0 * _
+    }
+
+    public void createsAResultForATestWithMultipleFailures() {
+        given:
+        def failure1 = new RuntimeException()
+        def failure2 = new RuntimeException()
+        def test = new DefaultTestDescriptor("15", "Foo", "bar");
+        def startEvent = new TestStartEvent(100L)
+        def completeEvent = new TestCompleteEvent(200L)
+
+        when:
+        adapter.started(test, startEvent)
+        adapter.failure('15', failure1)
+        adapter.failure('15', failure2)
+        adapter.completed('15', completeEvent)
+
+        then:
+        1 * listener.completed(_,
+                { it.exception.is(failure1) && it.exceptions == [failure1, failure2] },
+                completeEvent
+        )
+    }
+
+    public void createsAnAggregateResultForEmptyTestSuite() {
+        given:
+        def suite = new DefaultTestSuiteDescriptor("15", "FastTests");
+        def startEvent = new TestStartEvent(100L)
+        def completeEvent = new TestCompleteEvent(200L)
+
+        when:
+        adapter.started(suite, startEvent)
+        adapter.completed('15', completeEvent)
+
+        then:
+        1 * listener.started({it.descriptor == suite}, startEvent)
+        1 * listener.completed({it.descriptor == suite}, {
+            it.testCount == 0 && it.resultType == ResultType.SUCCESS
+        },
+            completeEvent
+        )
+        0 * _
+    }
+
+    public void createsAnAggregateResultForTestSuiteWithPassedTest() {
+        given:
+        def suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
+        def test = new DefaultTestDescriptor("testId", "DogTest", "shouldBarkAtStrangers");
+        def startEvent = new TestStartEvent(100L)
+        def testStartEvent = new TestStartEvent(100L, "suiteId")
+        def testCompleteEvent = new TestCompleteEvent(200L)
+        def completeEvent = new TestCompleteEvent(300L)
+
+        when:
+        adapter.started(suite, startEvent)
+        adapter.started(test, testStartEvent)
+        adapter.completed('testId', testCompleteEvent)
+        adapter.completed('suiteId', completeEvent)
+
+        then:
+        1 * listener.started({it.descriptor == suite}, startEvent)
+        1 * listener.started({it.descriptor == test}, testStartEvent)
+        1 * listener.completed({it.descriptor == test}, _ as TestResult, testCompleteEvent)
+        1 * listener.completed({it.descriptor == suite}, { it.testCount == 1 }, completeEvent)
+        0 * _
+    }
+
+    public void createsAnAggregateResultForTestSuiteWithFailedTest() {
+        given:
+        def suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
+        def ok = new DefaultTestDescriptor("okId", "DogTest", "shouldBarkAtStrangers");
+        def broken = new DefaultTestDescriptor("brokenId", "DogTest", "shouldDrinkMilk");
+
+        when:
+        adapter.started(suite, new TestStartEvent(100L))
+        adapter.started(ok, new TestStartEvent(100L, 'suiteId'))
+        adapter.started(broken, new TestStartEvent(100L, 'suiteId'))
+        adapter.completed('okId', new TestCompleteEvent(200L))
+        adapter.failure('brokenId', new RuntimeException())
+        adapter.completed('brokenId', new TestCompleteEvent(200L))
+        adapter.completed('suiteId', new TestCompleteEvent(200L))
+
+        then:
+        1 * listener.started({it.descriptor == suite}, _)
+        1 * listener.started({it.descriptor == ok && it.parent.descriptor == suite}, _)
+        1 * listener.started({it.descriptor == broken && it.parent.descriptor == suite}, _)
+        1 * listener.completed({it.descriptor == ok}, _ as TestResult, _)
+        1 * listener.completed({it.descriptor == broken}, _ as TestResult, _)
+        1 * listener.completed({it.descriptor == suite}, { it.testCount == 2 && it.failedTestCount == 1 && it.successfulTestCount == 1 }, _)
+        0 * _
+    }
+
+    public void createsAnAggregateResultForTestSuiteWithSkippedTest() {
+        given:
+        def suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
+        def test = new DefaultTestDescriptor("testId", "DogTest", "shouldBarkAtStrangers");
+
+        when:
+        adapter.started(suite, new TestStartEvent(100L))
+        adapter.started(test, new TestStartEvent(100L, 'suiteId'))
+        adapter.completed('testId', new TestCompleteEvent(200L, ResultType.SKIPPED))
+        adapter.completed('suiteId', new TestCompleteEvent(200L))
+
+        then:
+        1 * listener.started({it.descriptor == suite}, _)
+        1 * listener.started({it.descriptor == test && it.parent.descriptor == suite}, _)
+        1 * listener.completed({it.descriptor == test}, _ as TestResult, _)
+        1 * listener.completed({it.descriptor == suite},
+                { it.resultType == ResultType.SUCCESS && it.testCount == 1 && it.failedTestCount == 0 && it.successfulTestCount == 0 },
+                _
+        )
+        0 * _
+    }
+
+    @Test
+    public void createsAnAggregateResultForTestSuiteWithNestedSuites() {
+        given:
+        def root = new DefaultTestSuiteDescriptor("root", "AllTests");
+        def suite1 = new DefaultTestSuiteDescriptor("suite1", "FastTests");
+        def suite2 = new DefaultTestSuiteDescriptor("suite2", "SlowTests");
+        def ok = new DefaultTestDescriptor("ok", "DogTest", "shouldBarkAtStrangers");
+        def broken = new DefaultTestDescriptor("broken", "DogTest", "shouldDrinkMilk");
+
+        when:
+        adapter.started(root, new TestStartEvent(100L))
+        adapter.started(suite1, new TestStartEvent(100L, 'root'))
+        adapter.started(ok, new TestStartEvent(100L, 'suite1'))
+        adapter.started(suite2, new TestStartEvent(100L, 'root'))
+        adapter.completed('ok', new TestCompleteEvent(200L))
+        adapter.completed('suite1', new TestCompleteEvent(200L))
+        adapter.started(broken, new TestStartEvent(100L, 'suite2'))
+        adapter.failure('broken', new RuntimeException())
+        adapter.completed('broken', new TestCompleteEvent(200L))
+        adapter.completed('suite2', new TestCompleteEvent(200L))
+        adapter.completed('root', new TestCompleteEvent(200L))
+
+        then:
+        1 * listener.started({it.descriptor == root}, _)
+        1 * listener.started({it.descriptor == suite1}, _)
+        1 * listener.started({it.descriptor == suite2}, _)
+
+
+        1 * listener.started({it.descriptor == ok && it.parent.descriptor == suite1}, _)
+        1 * listener.started({it.descriptor == broken && it.parent.descriptor == suite2}, _)
+
+        1 * listener.completed({it.descriptor == ok}, _ as TestResult, _)
+        1 * listener.completed({it.descriptor == broken}, _ as TestResult, _)
+
+        1 * listener.completed({it.descriptor == root}, { it.successfulTestCount == 1 && it.testCount == 2 && it.resultType == ResultType.FAILURE}, _)
+        1 * listener.completed({it.descriptor == suite1}, { it.successfulTestCount == 1 && it.testCount == 1 && it.resultType == ResultType.SUCCESS}, _)
+        1 * listener.completed({it.descriptor == suite2}, { it.successfulTestCount == 0 && it.testCount == 1 && it.resultType == ResultType.FAILURE}, _)
+
+        0 * _
+    }
+
+    public void createsAnAggregateResultForTestSuiteWithFailure() {
+        given:
+        def suite = new DefaultTestSuiteDescriptor("id", "FastTests");
+        def test = new DefaultTestDescriptor("testid", "DogTest", "shouldBarkAtStrangers");
+        def failure = new RuntimeException()
+
+        when:
+        adapter.started(suite, new TestStartEvent(100L))
+        adapter.started(test, new TestStartEvent(100L, 'id'))
+        adapter.completed('testid', new TestCompleteEvent(200L, ResultType.SKIPPED))
+        adapter.failure('id', failure)
+        adapter.completed('id', new TestCompleteEvent(200L))
+
+        then:
+        1 * listener.started({it.descriptor == suite}, _)
+        1 * listener.started({it.descriptor == test && it.parent.descriptor == suite}, _)
+        1 * listener.completed({it.descriptor == test}, _ as TestResult, _)
+        1 * listener.completed({it.descriptor == suite},
+                { it.resultType == ResultType.FAILURE && it.exception.is(failure) && it.exceptions == [failure] },
+                _
+        )
+        0 * _
+    }
+
+    def "notifies output listener"() {
+        given:
+        def event = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "hey!")
+        def test = new DefaultTestDescriptor("testid", "DogTest", "shouldBarkAtStrangers");
+
+        when:
+        adapter.started(test, new TestStartEvent(100L))
+        adapter.output("testid", event)
+
+        then:
+        1 * listener.output({it.descriptor == test}, event)
+    }
+
+    @Issue("GRADLE-2035")
+    def "behaves gracefully even if cannot match output to the test"() {
+        given:
+        def event = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "hey!")
+
+        when:
+        adapter.output("testid", event)
+
+        then:
+        1 * listener.output({it instanceof UnknownTestDescriptor}, event)
+    }
+
+    @Issue("GRADLE-2035")
+    def "output can be received after test completion"() {
+        given:
+        TestDescriptor suite = new DefaultTestSuiteDescriptor("1", "DogTest");
+        TestDescriptor test1 = new DefaultTestDescriptor("1.1", "DogTest", "shouldBarkAtStrangers");
+
+        def woof = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "woof woof!")
+
+        when:
+        adapter.started(suite, new TestStartEvent(100L))
+        adapter.started(test1, new TestStartEvent(100L, '1'))
+
+        adapter.completed('1.1', new TestCompleteEvent(200L))
+        adapter.output('1.1', woof)
+
+        adapter.completed('1', new TestCompleteEvent(200L))
+
+        then:
+        1 * listener.output({ it.id == '1' }, woof)
+    }
+
+    @Issue("GRADLE-2035")
+    def "outputs for completed tests use parent descriptors"() {
+        given:
+        TestDescriptor root = new DefaultTestSuiteDescriptor("1", "CanineSuite");
+        TestDescriptor suite = new DefaultTestSuiteDescriptor("1.1", "DogTest");
+        TestDescriptor test1 = new DefaultTestDescriptor("1.1.1", "DogTest", "shouldBarkAtStrangers");
+        TestDescriptor test2 = new DefaultTestDescriptor("1.1.2", "DogTest", "shouldLoiter");
+
+        def woof = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "woof woof!")
+        def grrr = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdErr, "grrr!")
+
+        when:
+        adapter.started(root, new TestStartEvent(1))
+        adapter.started(suite, new TestStartEvent(1, '1'))
+        adapter.started(test1, new TestStartEvent(1, '1.1'))
+        adapter.started(test2, new TestStartEvent(1, '1.1'))
+
+        adapter.completed('1.1.1', new TestCompleteEvent(1))
+
+        //test completed but we receive an output
+        adapter.output('1.1.1', woof)
+
+        adapter.completed('1.1.2', new TestCompleteEvent(1))
+        adapter.completed('1.1', new TestCompleteEvent(1))
+
+        //the suite is completed but for we receive an output
+        adapter.output('1.1.1', woof)
+
+        adapter.completed('1', new TestCompleteEvent(1))
+
+        //all tests are completed but we receive an output for one of the other tests
+        adapter.output('1.1.2', grrr)
+
+        then:
+        1 * listener.output({ it instanceof DecoratingTestDescriptor && it.id == '1.1' }, woof)
+        1 * listener.output({ it instanceof DecoratingTestDescriptor && it.id == '1' }, woof)
+        1 * listener.output({ it instanceof UnknownTestDescriptor }, grrr)
+        0 * listener.output(_, _)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapterTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapterTest.groovy
deleted file mode 100644
index e063e89..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestListenerAdapterTest.groovy
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.testing.results
-
-import org.gradle.api.tasks.testing.TestResult.ResultType
-import org.gradle.api.internal.tasks.testing.*
-import org.gradle.api.tasks.testing.*
-
-import org.junit.Test
-
-import spock.lang.Issue
-import spock.lang.Specification
-
-class TestListenerAdapterTest extends Specification {
-
-    def listener = Mock(TestListener.class)
-    def outputListener = Mock(TestOutputListener.class)
-    TestListenerAdapter adapter = new TestListenerAdapter(listener, outputListener)
-
-    public void notifiesBefore() {
-        given:
-        TestDescriptor test = new DefaultTestDescriptor("id", "Foo", "bar");
-
-        when:
-        adapter.started(test, new TestStartEvent(100L))
-
-        then:
-        1 * listener.beforeTest({ it instanceof DecoratingTestDescriptor && it.descriptor == test })
-        0 * _
-    }
-
-    public void notifiesAfter() {
-        given:
-        TestDescriptor test = new DefaultTestDescriptor("id", "Foo", "bar");
-
-        when:
-        adapter.started(test, new TestStartEvent(100L))
-        adapter.completed('id', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeTest(_)
-        1 * listener.afterTest(
-                { it instanceof DecoratingTestDescriptor && it.descriptor == test },
-                { it.successfulTestCount == 1 && it.testCount == 1 && it.failedTestCount == 0 }
-        )
-        0 * _
-    }
-
-    public void createsAResultForATestWithFailure() {
-        given:
-        RuntimeException failure = new RuntimeException()
-        TestDescriptor test = new DefaultTestDescriptor("15", "Foo", "bar");
-
-        when:
-        adapter.started(test, new TestStartEvent(100L))
-        adapter.failure('15', failure)
-        adapter.completed('15', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeTest(_)
-        1 * listener.afterTest({ it.descriptor == test },
-                { it.successfulTestCount == 0 && it.testCount == 1 && it.failedTestCount == 1 && it.exception.is(failure) })
-        0 * _
-    }
-
-    public void createsAResultForATestWithMultipleFailures() {
-        given:
-        RuntimeException failure1 = new RuntimeException()
-        RuntimeException failure2 = new RuntimeException()
-        TestDescriptor test = new DefaultTestDescriptor("15", "Foo", "bar");
-
-        when:
-        adapter.started(test, new TestStartEvent(100L))
-        adapter.failure('15', failure1)
-        adapter.failure('15', failure2)
-        adapter.completed('15', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.afterTest(_,
-                { it.exception.is(failure1) && it.exceptions == [failure1, failure2] })
-    }
-
-    public void createsAnAggregateResultForEmptyTestSuite() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("15", "FastTests");
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.completed('15', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == suite})
-        1 * listener.afterSuite({it.descriptor == suite}, {
-            it.testCount == 0 && it.resultType == ResultType.SUCCESS
-        })
-        0 * _
-    }
-
-    public void createsAnAggregateResultForTestSuiteWithPassedTest() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
-        TestDescriptor test = new DefaultTestDescriptor("testId", "DogTest", "shouldBarkAtStrangers");
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.started(test, new TestStartEvent(100L, 'suiteId'))
-        adapter.completed('testId', new TestCompleteEvent(200L))
-        adapter.completed('suiteId', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == suite})
-        1 * listener.beforeTest({it.descriptor == test})
-        1 * listener.afterTest({it.descriptor == test}, _ as TestResult)
-        1 * listener.afterSuite({it.descriptor == suite}, { it.testCount == 1 })
-        0 * _
-    }
-
-    public void createsAnAggregateResultForTestSuiteWithFailedTest() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
-        TestDescriptor ok = new DefaultTestDescriptor("okId", "DogTest", "shouldBarkAtStrangers");
-        TestDescriptor broken = new DefaultTestDescriptor("brokenId", "DogTest", "shouldDrinkMilk");
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.started(ok, new TestStartEvent(100L, 'suiteId'))
-        adapter.started(broken, new TestStartEvent(100L, 'suiteId'))
-        adapter.completed('okId', new TestCompleteEvent(200L))
-        adapter.failure('brokenId', new RuntimeException())
-        adapter.completed('brokenId', new TestCompleteEvent(200L))
-        adapter.completed('suiteId', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == suite})
-        1 * listener.beforeTest({it.descriptor == ok && it.parent.descriptor == suite})
-        1 * listener.beforeTest({it.descriptor == broken && it.parent.descriptor == suite})
-        1 * listener.afterTest({it.descriptor == ok}, _ as TestResult)
-        1 * listener.afterTest({it.descriptor == broken}, _ as TestResult)
-        1 * listener.afterSuite({it.descriptor == suite}, { it.testCount == 2 && it.failedTestCount == 1 && it.successfulTestCount == 1 })
-        0 * _
-    }
-
-    public void createsAnAggregateResultForTestSuiteWithSkippedTest() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("suiteId", "FastTests");
-        TestDescriptor test = new DefaultTestDescriptor("testId", "DogTest", "shouldBarkAtStrangers");
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.started(test, new TestStartEvent(100L, 'suiteId'))
-        adapter.completed('testId', new TestCompleteEvent(200L, ResultType.SKIPPED))
-        adapter.completed('suiteId', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == suite})
-        1 * listener.beforeTest({it.descriptor == test && it.parent.descriptor == suite})
-        1 * listener.afterTest({it.descriptor == test}, _ as TestResult)
-        1 * listener.afterSuite({it.descriptor == suite},
-                { it.resultType == ResultType.SUCCESS && it.testCount == 1 && it.failedTestCount == 0 && it.successfulTestCount == 0 })
-        0 * _
-    }
-
-    @Test
-    public void createsAnAggregateResultForTestSuiteWithNestedSuites() {
-        given:
-        TestDescriptor root = new DefaultTestSuiteDescriptor("root", "AllTests");
-        TestDescriptor suite1 = new DefaultTestSuiteDescriptor("suite1", "FastTests");
-        TestDescriptor suite2 = new DefaultTestSuiteDescriptor("suite2", "SlowTests");
-        TestDescriptor ok = new DefaultTestDescriptor("ok", "DogTest", "shouldBarkAtStrangers");
-        TestDescriptor broken = new DefaultTestDescriptor("broken", "DogTest", "shouldDrinkMilk");
-
-        when:
-        adapter.started(root, new TestStartEvent(100L))
-        adapter.started(suite1, new TestStartEvent(100L, 'root'))
-        adapter.started(ok, new TestStartEvent(100L, 'suite1'))
-        adapter.started(suite2, new TestStartEvent(100L, 'root'))
-        adapter.completed('ok', new TestCompleteEvent(200L))
-        adapter.completed('suite1', new TestCompleteEvent(200L))
-        adapter.started(broken, new TestStartEvent(100L, 'suite2'))
-        adapter.failure('broken', new RuntimeException())
-        adapter.completed('broken', new TestCompleteEvent(200L))
-        adapter.completed('suite2', new TestCompleteEvent(200L))
-        adapter.completed('root', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == root})
-        1 * listener.beforeSuite({it.descriptor == suite1})
-        1 * listener.beforeSuite({it.descriptor == suite2})
-
-
-        1 * listener.beforeTest({it.descriptor == ok && it.parent.descriptor == suite1})
-        1 * listener.beforeTest({it.descriptor == broken && it.parent.descriptor == suite2})
-
-        1 * listener.afterTest({it.descriptor == ok}, _ as TestResult)
-        1 * listener.afterTest({it.descriptor == broken}, _ as TestResult)
-
-        1 * listener.afterSuite({it.descriptor == root}, { it.successfulTestCount == 1 && it.testCount == 2 && it.resultType == ResultType.FAILURE})
-        1 * listener.afterSuite({it.descriptor == suite1}, { it.successfulTestCount == 1 && it.testCount == 1 && it.resultType == ResultType.SUCCESS})
-        1 * listener.afterSuite({it.descriptor == suite2}, { it.successfulTestCount == 0 && it.testCount == 1 && it.resultType == ResultType.FAILURE})
-
-        0 * _
-    }
-
-    public void createsAnAggregateResultForTestSuiteWithFailure() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("id", "FastTests");
-        TestDescriptor test = new DefaultTestDescriptor("testid", "DogTest", "shouldBarkAtStrangers");
-        RuntimeException failure = new RuntimeException()
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.started(test, new TestStartEvent(100L, 'id'))
-        adapter.completed('testid', new TestCompleteEvent(200L, ResultType.SKIPPED))
-        adapter.failure('id', failure)
-        adapter.completed('id', new TestCompleteEvent(200L))
-
-        then:
-        1 * listener.beforeSuite({it.descriptor == suite})
-        1 * listener.beforeTest({it.descriptor == test && it.parent.descriptor == suite})
-        1 * listener.afterTest({it.descriptor == test}, _ as TestResult)
-        1 * listener.afterSuite({it.descriptor == suite},
-                { it.resultType == ResultType.FAILURE && it.exception.is(failure) && it.exceptions == [failure] })
-        0 * _
-    }
-
-    def "notifies output listener"() {
-        given:
-        def event = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "hey!")
-        def test = new DefaultTestDescriptor("testid", "DogTest", "shouldBarkAtStrangers");
-
-        when:
-        adapter.started(test, new TestStartEvent(100L))
-        adapter.output("testid", event)
-
-        then:
-        1 * outputListener.onOutput({it.descriptor == test}, event)
-    }
-
-    @Issue("GRADLE-2035")
-    def "behaves gracefully even if cannot match output to the test"() {
-        given:
-        def event = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "hey!")
-
-        when:
-        adapter.output("testid", event)
-
-        then:
-        1 * outputListener.onOutput({it instanceof UnknownTestDescriptor}, event)
-    }
-
-    @Issue("GRADLE-2035")
-    def "output can be received after test completion"() {
-        given:
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("1", "DogTest");
-        TestDescriptor test1 = new DefaultTestDescriptor("1.1", "DogTest", "shouldBarkAtStrangers");
-
-        def woof = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "woof woof!")
-
-        when:
-        adapter.started(suite, new TestStartEvent(100L))
-        adapter.started(test1, new TestStartEvent(100L, '1'))
-
-        adapter.completed('1.1', new TestCompleteEvent(200L))
-        adapter.output('1.1', woof)
-
-        adapter.completed('1', new TestCompleteEvent(200L))
-
-        then:
-        1 * outputListener.onOutput({ it.id == '1' }, woof)
-    }
-
-    @Issue("GRADLE-2035")
-    def "outputs for completed tests use parent descriptors"() {
-        given:
-        TestDescriptor root = new DefaultTestSuiteDescriptor("1", "CanineSuite");
-        TestDescriptor suite = new DefaultTestSuiteDescriptor("1.1", "DogTest");
-        TestDescriptor test1 = new DefaultTestDescriptor("1.1.1", "DogTest", "shouldBarkAtStrangers");
-        TestDescriptor test2 = new DefaultTestDescriptor("1.1.2", "DogTest", "shouldLoiter");
-
-        def woof = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdOut, "woof woof!")
-        def grrr = new DefaultTestOutputEvent(TestOutputEvent.Destination.StdErr, "grrr!")
-
-        when:
-        adapter.started(root, new TestStartEvent(1))
-        adapter.started(suite, new TestStartEvent(1, '1'))
-        adapter.started(test1, new TestStartEvent(1, '1.1'))
-        adapter.started(test2, new TestStartEvent(1, '1.1'))
-
-        adapter.completed('1.1.1', new TestCompleteEvent(1))
-
-        //test completed but we receive an output
-        adapter.output('1.1.1', woof)
-
-        adapter.completed('1.1.2', new TestCompleteEvent(1))
-        adapter.completed('1.1', new TestCompleteEvent(1))
-
-        //the suite is completed but for we receive an output
-        adapter.output('1.1.1', woof)
-
-        adapter.completed('1', new TestCompleteEvent(1))
-
-        //all tests are completed but we receive an output for one of the other tests
-        adapter.output('1.1.2', grrr)
-
-        then:
-        1 * outputListener.onOutput({ it instanceof DecoratingTestDescriptor && it.id == '1.1' }, woof)
-        1 * outputListener.onOutput({ it instanceof DecoratingTestDescriptor && it.id == '1' }, woof)
-        1 * outputListener.onOutput({ it instanceof UnknownTestDescriptor }, grrr)
-        0 * outputListener._
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactorySpec.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactorySpec.groovy
index 14f5963..540c5db 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactorySpec.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactorySpec.groovy
@@ -15,10 +15,11 @@
  */
 package org.gradle.api.internal.tasks.testing.testng
 
-import spock.lang.Specification
 import org.testng.IConfigurationListener2
-import org.testng.ITestResult
+import org.testng.ITestContext
 import org.testng.ITestListener
+import org.testng.ITestResult
+import spock.lang.Specification
 
 class TestNGListenerAdapterFactorySpec extends Specification {
     TestNGListenerAdapterFactory factory = new TestNGListenerAdapterFactory(getClass().classLoader)
@@ -52,8 +53,9 @@ class TestNGListenerAdapterFactorySpec extends Specification {
     }
 
     def "adapter forwards all ITestListener calls"() {
-        ITestListener adapter = factory.createAdapter(listener)
-        ITestResult result = Mock()
+        def adapter = factory.createAdapter(listener)
+        def result = Mock(ITestResult)
+        def test = Mock(ITestContext)
 
         when:
         adapter.onTestStart(result)
@@ -61,8 +63,8 @@ class TestNGListenerAdapterFactorySpec extends Specification {
         adapter.onTestFailure(result)
         adapter.onTestSkipped(result)
         adapter.onTestFailedButWithinSuccessPercentage(result)
-        adapter.onStart(null) // we don't mock ITestContext as it would require us to put Guice on test class path
-        adapter.onFinish(null)
+        adapter.onStart(test)
+        adapter.onFinish(test)
 
         then:
         1 * listener.onTestStart(result)
@@ -70,8 +72,8 @@ class TestNGListenerAdapterFactorySpec extends Specification {
         1 * listener.onTestFailure(result)
         1 * listener.onTestSkipped(result)
         1 * listener.onTestFailedButWithinSuccessPercentage(result)
-        1 * listener.onStart(null)
-        1 * listener.onFinish(null)
+        1 * listener.onStart(test)
+        1 * listener.onFinish(test)
     }
 }
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
index 8a72df6..d22c1e2 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
@@ -19,14 +19,17 @@ package org.gradle.api.internal.tasks.testing.testng
 import org.gradle.api.GradleException
 import org.gradle.api.internal.tasks.testing.DefaultTestClassRunInfo
 import org.gradle.api.internal.tasks.testing.TestResultProcessor
-import org.gradle.api.internal.tasks.testing.TestStartEvent
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
 import org.gradle.api.tasks.testing.TestResult.ResultType
 import org.gradle.api.tasks.testing.testng.TestNGOptions
+import org.gradle.internal.TrueTimeProvider
 import org.gradle.internal.id.LongIdGenerator
-import org.gradle.logging.StandardOutputRedirector
+import org.gradle.messaging.actor.TestActorFactory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.junit.Rule
+import org.testng.ITestContext
+import org.testng.ITestListener
+import org.testng.ITestResult
 import org.testng.annotations.*
 import spock.lang.Ignore
 import spock.lang.Specification
@@ -34,13 +37,13 @@ import spock.lang.Subject
 
 class TestNGTestClassProcessorTest extends Specification {
 
-    @Rule TestNameTestDirectoryProvider reportDir = new TestNameTestDirectoryProvider()
+    @Rule TestNameTestDirectoryProvider dir = new TestNameTestDirectoryProvider()
 
     def processor = Mock(TestResultProcessor)
 
-    def options = Spy(TestNGSpec, constructorArgs:[new TestNGOptions(reportDir.testDirectory), new DefaultTestFilter()])
+    def options = Spy(TestNGSpec, constructorArgs:[new TestNGOptions(dir.testDirectory), new DefaultTestFilter()])
 
-    @Subject classProcessor = new TestNGTestClassProcessor(reportDir.testDirectory, options, [], new LongIdGenerator(), {} as StandardOutputRedirector)
+    @Subject classProcessor = new TestNGTestClassProcessor(dir.testDirectory, options, [], new LongIdGenerator(), new TrueTimeProvider(), new TestActorFactory())
 
     void process(Class ... clazz) {
         classProcessor.startProcessing(processor)
@@ -53,9 +56,11 @@ class TestNGTestClassProcessorTest extends Specification {
     void "executes the test class"() {
         when: process(ATestNGClass)
 
-        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle test' && it.className == null }, { it.parentId == null })
-        then: 1 * processor.started({ it.id == 2 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 1 })
-        then: 1 * processor.completed(2, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'Gradle test' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 3 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
         0 * processor._
     }
@@ -64,9 +69,11 @@ class TestNGTestClassProcessorTest extends Specification {
         when:
         process(ATestNGFactoryClass)
 
-        then: 1 * processor.started({ it.name == 'Gradle test' && it.className == null }, { it.parentId == null })
-        then: 1 * processor.started({ it.name == 'ok' && it.className == ATestNGClass.name }, _ as TestStartEvent)
-        then: 1 * processor.completed(2, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'Gradle test' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 3 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
 
         0 * processor._
@@ -77,9 +84,11 @@ class TestNGTestClassProcessorTest extends Specification {
 
         when: process(ATestNGClassWithManyMethods)
 
-        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle test' && it.className == null }, { it.parentId == null })
-        then: 1 * processor.started({ it.id == 2 && it.name == 'another' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 1 })
-        then: 1 * processor.completed(2, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'Gradle test' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 3 && it.name == 'another' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
 
         0 * processor._
@@ -91,9 +100,10 @@ class TestNGTestClassProcessorTest extends Specification {
         when: process(ATestNGClassWithManyMethods)
 
         then:
-        1 * processor.started({ it.id == 1 && it.name == 'Gradle test' && it.className == null }, { it.parentId == null })
-        1 * processor.started({ it.id == 2 && it.name == 'another' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 1 })
-        1 * processor.started({ it.id == 3 && it.name == 'yetAnother' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 1 })
+        1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' && it.className == null }, { it.parentId == null })
+        1 * processor.started({ it.id == 2 && it.name == 'Gradle test' && it.className == null }, { it.parentId == 1 })
+        1 * processor.started({ it.id == 3 && it.name == 'another' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 2 })
+        1 * processor.started({ it.id == 4 && it.name == 'yetAnother' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 2 })
         0 * processor.started(_, _)
     }
 
@@ -103,18 +113,21 @@ class TestNGTestClassProcessorTest extends Specification {
         when: process(ATestNGClassWithManyMethods)
 
         then:
-        1 * processor.started({ it.id == 1 && it.name == 'Gradle test' }, _)
-        1 * processor.started({ it.name == 'ok' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 1 })
-        1 * processor.started({ it.name == 'ok2' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 1 })
+        1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' }, _)
+        1 * processor.started({ it.id == 2 && it.name == 'Gradle test' }, _)
+        1 * processor.started({ it.name == 'ok' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 2 })
+        1 * processor.started({ it.name == 'ok2' && it.className == ATestNGClassWithManyMethods.name }, { it.parentId == 2 })
         0 * processor.started(_, _)
     }
 
-    void "executes not tests if none of the included test methods match"() {
+    void "executes no tests if none of the included test methods match"() {
         options.getIncludedTests() >> [ATestNGClassWithManyMethods.name + "does not exist"]
 
         when: process(ATestNGClassWithManyMethods)
 
-        then: 1 * processor.started({ it.id == 1 && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 1 && it.className == null  && it.name == 'Gradle suite' }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.className == null && it.name == 'Gradle test' }, { it.parentId == 1 })
+        then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
         0 * processor._
     }
@@ -122,9 +135,11 @@ class TestNGTestClassProcessorTest extends Specification {
     void "executes test with expected exception"() {
         when: process(ATestNGClassWithExpectedException)
 
-        then: 1 * processor.started({ it.id == 1} , _)
-        then: 1 * processor.started({ it.name == 'ok' && it.className == ATestNGClassWithExpectedException.name }, _)
-        then: 1 * processor.completed(2, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.started({ it.id == 1 } , _)
+        then: 1 * processor.started({ it.id == 2 } , _)
+        then: 1 * processor.started({ it.name == 'ok' && it.className == ATestNGClassWithExpectedException.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
         then: 1 * processor.completed(1, { it.resultType == null })
         0 * processor._
     }
@@ -132,31 +147,37 @@ class TestNGTestClassProcessorTest extends Specification {
     void "executes test with broken setup"() {
         when: process(ATestNGClassWithBrokenSetupMethod)
 
-        then: 1 * processor.started({ it.id == 1} , _)
+        then: 1 * processor.started({ it.id == 1 } , _)
+        then: 1 * processor.started({ it.id == 2 } , _)
         then: 1 * processor.started({ it.name == 'beforeMethod' && it.className == ATestNGClassWithBrokenSetupMethod.name }, _)
-        then: 1 * processor.failure(2, ATestNGClassWithBrokenSetupMethod.failure)
-        then: 1 * processor.completed(2, { it.resultType == ResultType.FAILURE })
+        then: 1 * processor.failure(3, ATestNGClassWithBrokenSetupMethod.failure)
+        then: 1 * processor.completed(3, { it.resultType == ResultType.FAILURE })
 
         then: 1 * processor.started({ it.name == 'test' && it.className == ATestNGClassWithBrokenSetupMethod.name }, _)
-        then: 1 * processor.completed(3, { it.resultType == ResultType.SKIPPED})
+        then: 1 * processor.completed(4, { it.resultType == ResultType.SKIPPED})
 
+        then: 1 * processor.completed(2, { it.resultType == null})
         then: 1 * processor.completed(1, { it.resultType == null})
+
         0 * processor._
     }
 
     void "executes test class with dependency method"() {
         when: process(ATestNGClassWithBrokenDependencyMethod)
 
-        then: 1 * processor.started({ it.id == 1} , _)
+        then: 1 * processor.started({ it.id == 1 } , _)
+        then: 1 * processor.started({ it.id == 2 } , _)
         then: 1 * processor.started({ it.name == 'beforeMethod' && it.className == ATestNGClassWithBrokenDependencyMethod.name }, _)
 
-        then: 1 * processor.failure(2, ATestNGClassWithBrokenDependencyMethod.failure)
-        then: 1 * processor.completed(2, { it.resultType == ResultType.FAILURE })
+        then: 1 * processor.failure(3, ATestNGClassWithBrokenDependencyMethod.failure)
+        then: 1 * processor.completed(3, { it.resultType == ResultType.FAILURE })
 
         then: 1 * processor.started({ it.name == 'test' && it.className == ATestNGClassWithBrokenDependencyMethod.name }, _)
-        then: 1 * processor.completed(3, { it.resultType == ResultType.SKIPPED})
+        then: 1 * processor.completed(4, { it.resultType == ResultType.SKIPPED})
 
+        then: 1 * processor.completed(2, { it.resultType == null})
         then: 1 * processor.completed(1, { it.resultType == null})
+
         0 * processor._
     }
 
@@ -168,10 +189,11 @@ class TestNGTestClassProcessorTest extends Specification {
         when: process(ATestNGClassWithGroups)
 
         then:
-        1 * processor.started({ it.id == 1} , _)
+        1 * processor.started({ it.id == 1 } , _)
+        1 * processor.started({ it.id == 2 } , _)
         1 * processor.started({ it.name == 'group1' && it.className == ATestNGClassWithGroups.name }, _)
         1 * processor.started({ it.name == 'group2' && it.className == ATestNGClassWithGroups.name }, _)
-        3 * processor.completed(_, _)
+        4 * processor.completed(_, _)
         0 * processor._
     }
 
@@ -207,12 +229,128 @@ class TestNGTestClassProcessorTest extends Specification {
         process(ATestNGClass, ATestNGClassWithBeforeAndAfter) //the latter is not matched
 
         then:
-        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle test' && it.className == null }, _)
-        then: 1 * processor.started({ it.id == 2 && it.name == 'ok' && it.className == ATestNGClass.name }, _)
+        then: 1 * processor.started({ it.id == 1 && it.name == 'Gradle suite' && it.className == null }, _)
+        then: 1 * processor.started({ it.id == 2 && it.name == 'Gradle test' && it.className == null }, _)
+        then: 1 * processor.started({ it.id == 3 && it.name == 'ok' && it.className == ATestNGClass.name }, _)
+        then: 1 * processor.completed(3, _)
         then: 1 * processor.completed(2, _)
         then: 1 * processor.completed(1, _)
         0 * processor._
     }
+
+    void "custom test listeners can change test status"() {
+        options.listeners << FailSkippedTestsListener.class.name
+
+        when: process(ATestNGClassWithSkippedTest)
+
+        then: 1 * processor.completed(_, { it.resultType == ResultType.FAILURE})
+    }
+
+    void "executes test from suite"() {
+        def suite = dir.file("suite.xml") << """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="AwesomeSuite">
+  <test name="AwesomeTest">
+    <classes>
+      <class name="${ATestNGClass.name}"/>
+    </classes>
+  </test>
+</suite>"""
+        classProcessor = new TestNGTestClassProcessor(dir.testDirectory, options, [suite], new LongIdGenerator(), new TrueTimeProvider(), new TestActorFactory())
+
+        when:
+        classProcessor.startProcessing(processor)
+        classProcessor.stop()
+
+        then: 1 * processor.started({ it.id == 1 && it.name == 'AwesomeSuite' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'AwesomeTest' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 3 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
+        then: 1 * processor.completed(1, { it.resultType == null })
+        0 * processor._
+    }
+
+    void "executes multiple suites and tests"() {
+        def suite1 = dir.file("suite1.xml") << """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="suite 1">
+  <test name="test 1">
+    <classes>
+      <class name="${ATestNGClass.name}"/>
+    </classes>
+  </test>
+  <test name="test 2">
+    <classes>
+      <class name="${ATestNGClass.name}"/>
+    </classes>
+  </test>
+</suite>"""
+        def suite2 = dir.file("suite2.xml") << """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="suite 2">
+  <test name="test 1">
+    <classes>
+      <class name="${ATestNGClass.name}"/>
+    </classes>
+  </test>
+  <test name="test 2">
+    <classes>
+      <class name="${ATestNGClass.name}"/>
+    </classes>
+  </test>
+</suite>"""
+        classProcessor = new TestNGTestClassProcessor(dir.testDirectory, options, [suite1, suite2], new LongIdGenerator(), new TrueTimeProvider(), new TestActorFactory())
+
+        when:
+        classProcessor.startProcessing(processor)
+        classProcessor.stop()
+
+        then: 1 * processor.started({ it.id == 1 && it.name == 'suite 1' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 2 && it.name == 'test 1' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 3 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 2 })
+        then: 1 * processor.completed(3, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(2, { it.resultType == null })
+
+        then: 1 * processor.started({ it.id == 4 && it.name == 'test 2' && it.className == null }, { it.parentId == 1 })
+        then: 1 * processor.started({ it.id == 5 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 4 })
+        then: 1 * processor.completed(5, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(4, { it.resultType == null })
+        then: 1 * processor.completed(1, { it.resultType == null })
+
+        then: 1 * processor.started({ it.id == 6 && it.name == 'suite 2' && it.className == null }, { it.parentId == null })
+        then: 1 * processor.started({ it.id == 7 && it.name == 'test 1' && it.className == null }, { it.parentId == 6 })
+        then: 1 * processor.started({ it.id == 8 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 7 })
+        then: 1 * processor.completed(8, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(7, { it.resultType == null })
+
+        then: 1 * processor.started({ it.id == 9 && it.name == 'test 2' && it.className == null }, { it.parentId == 6 })
+        then: 1 * processor.started({ it.id == 10 && it.name == 'ok' && it.className == ATestNGClass.name }, { it.parentId == 9 })
+        then: 1 * processor.completed(10, { it.resultType == ResultType.SUCCESS })
+        then: 1 * processor.completed(9, { it.resultType == null })
+        then: 1 * processor.completed(6, { it.resultType == null })
+
+        0 * processor._
+    }
+}
+
+public class FailSkippedTestsListener implements ITestListener {
+    void onTestStart(ITestResult result) {}
+    void onTestSuccess(ITestResult result) {
+        result.setStatus(ITestResult.FAILURE)
+    }
+    void onTestFailure(ITestResult result) {}
+    void onTestSkipped(ITestResult result) {
+
+    }
+    void onTestFailedButWithinSuccessPercentage(ITestResult result) {}
+    void onStart(ITestContext context) {}
+    void onFinish(ITestContext context) {}
+}
+
+public class ATestNGClassWithSkippedTest {
+    @org.testng.annotations.Test
+    public void skipMe() {}
 }
 
 public class ATestNGClass {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFrameworkTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFrameworkTest.groovy
index 60e2b81..0e116cd 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFrameworkTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestFrameworkTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.api.internal.tasks.testing.testng
 
 import org.gradle.api.internal.AsmBackedClassGenerator
 import org.gradle.api.internal.ClassGeneratorBackedInstantiator
+import org.gradle.api.internal.initialization.loadercache.ClassLoaderCache
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
 import org.gradle.api.tasks.testing.Test
 import org.gradle.api.tasks.testing.testng.TestNGOptions
@@ -31,7 +32,7 @@ import spock.lang.Specification
 
 public class TestNGTestFrameworkTest extends Specification {
 
-    @Shared Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    @Shared Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
 
     private project = new ProjectBuilder().build()
     Test testTask = TestUtil.createTask(Test, project)
@@ -69,6 +70,6 @@ public class TestNGTestFrameworkTest extends Specification {
     }
 
     TestNGTestFramework createFramework() {
-        new TestNGTestFramework(testTask, new DefaultTestFilter(), instantiator)
+        new TestNGTestFramework(testTask, new DefaultTestFilter(), instantiator, Stub(ClassLoaderCache))
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializerTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializerTest.groovy
index da9abea..344d282 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializerTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/TestEventSerializerTest.groovy
@@ -28,8 +28,8 @@ import org.gradle.api.internal.tasks.testing.TestStartEvent
 import org.gradle.api.tasks.testing.TestOutputEvent
 import org.gradle.api.tasks.testing.TestResult
 import org.gradle.internal.id.CompositeIdGenerator
-import org.gradle.messaging.serialize.InputStreamBackedDecoder
-import org.gradle.messaging.serialize.OutputStreamBackedEncoder
+import org.gradle.internal.serialize.InputStreamBackedDecoder
+import org.gradle.internal.serialize.OutputStreamBackedEncoder
 import spock.lang.Specification
 
 class TestEventSerializerTest extends Specification {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
index 1a3c62f..26df8c2 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/BasePluginTest.groovy
@@ -17,11 +17,11 @@
 package org.gradle.api.plugins
 
 import org.gradle.api.DefaultTask
-import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.PublishArtifact
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.tasks.Delete
 import org.gradle.api.tasks.Upload
 import org.gradle.api.tasks.bundling.Jar
@@ -34,11 +34,11 @@ import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
 import static org.hamcrest.Matchers.instanceOf
 
 class BasePluginTest extends Specification {
-    private final Project project = TestUtil.createRootProject()
+    private final DefaultProject project = TestUtil.createRootProject()
 
     public void addsConventionObjects() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         project.convention.plugins.base instanceof BasePluginConvention
@@ -47,7 +47,7 @@ class BasePluginTest extends Specification {
 
     public void createsTasksAndAppliesMappings() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         def clean = project.tasks[BasePlugin.CLEAN_TASK_NAME]
@@ -65,7 +65,7 @@ class BasePluginTest extends Specification {
         def someJar = project.tasks.create('someJar', Jar)
 
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
         project.artifacts.archives someJar
 
         then:
@@ -75,7 +75,7 @@ class BasePluginTest extends Specification {
 
     public void addsRulesWhenAConfigurationIsAdded() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         !project.tasks.rules.empty
@@ -86,7 +86,7 @@ class BasePluginTest extends Specification {
         def someJar = project.tasks.create('someJar', Jar)
 
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
         project.artifacts.archives someJar
 
         then:
@@ -121,7 +121,7 @@ class BasePluginTest extends Specification {
         test.outputs.files(project.buildDir)
 
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         Task cleanTest = project.tasks['cleanTest']
@@ -135,7 +135,7 @@ class BasePluginTest extends Specification {
         project.task('12')
 
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         project.tasks.findByName('cleantestTask') == null
@@ -146,7 +146,7 @@ class BasePluginTest extends Specification {
 
     public void appliesMappingsForArchiveTasks() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
         project.version = '1.0'
 
         then:
@@ -170,7 +170,7 @@ class BasePluginTest extends Specification {
 
     public void usesNullVersionWhenProjectVersionNotSpecified() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         def task = project.tasks.create('someJar', Jar)
@@ -185,7 +185,7 @@ class BasePluginTest extends Specification {
 
     public void addsConfigurationsToTheProject() {
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         then:
         def defaultConfig = project.configurations[Dependency.DEFAULT_CONFIGURATION]
@@ -204,7 +204,7 @@ class BasePluginTest extends Specification {
         PublishArtifact artifact = Mock()
 
         when:
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
         project.configurations.create("custom").artifacts.add(artifact)
 
         then:
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyBasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyBasePluginTest.groovy
index 8d39e4d..081bc10 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyBasePluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyBasePluginTest.groovy
@@ -16,8 +16,7 @@
  
 package org.gradle.api.plugins
 
-import org.gradle.api.Project
-import org.gradle.api.internal.artifacts.configurations.Configurations
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.api.tasks.compile.GroovyCompile
 import org.gradle.api.tasks.javadoc.Groovydoc
@@ -27,31 +26,22 @@ import org.junit.Test
 
 import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
 import static org.gradle.util.WrapUtil.toLinkedSet
-import static org.gradle.util.WrapUtil.toSet
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.assertTrue
 
 class GroovyBasePluginTest {
-    private final Project project = TestUtil.createRootProject()
+    private final DefaultProject project = TestUtil.createRootProject()
 
     @Before
     void before() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
     }
 
     @Test void appliesTheJavaBasePluginToTheProject() {
         assertTrue(project.getPlugins().hasPlugin(JavaBasePlugin));
     }
 
-    @Test void addsGroovyConfigurationToTheProject() {
-        def configuration = project.configurations.findByName('groovy')
-        assertNotNull(configuration)
-        assertThat(Configurations.getNames(configuration.extendsFrom, false), equalTo(toSet()))
-        assertFalse(configuration.visible)
-        assertTrue(configuration.transitive)
-    }
-
     @Test void appliesMappingsToNewSourceSet() {
         def sourceSet = project.sourceSets.create('custom')
         assertThat(sourceSet.groovy.displayName, equalTo("custom Groovy source"))
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyPluginTest.groovy
index 1e99344..6702f2a 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/GroovyPluginTest.groovy
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- 
+
 package org.gradle.api.plugins
 
 import org.gradle.api.Project
@@ -22,13 +22,13 @@ import org.gradle.api.reporting.ReportingExtension
 import org.gradle.api.tasks.compile.GroovyCompile
 import org.gradle.api.tasks.javadoc.Groovydoc
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Matchers
 import org.gradle.util.TestUtil
 import org.junit.Rule
 import org.junit.Test
 
 import static org.gradle.api.tasks.TaskDependencyMatchers.dependsOn
 import static org.gradle.util.WrapUtil.toLinkedSet
-import static org.gradle.util.WrapUtil.toSet
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
@@ -38,22 +38,25 @@ class GroovyPluginTest {
     private final Project project = TestUtil.createRootProject()
     private final GroovyPlugin groovyPlugin = new GroovyPlugin()
 
-    @Test public void appliesTheJavaPluginToTheProject() {
+    @Test
+    public void appliesTheJavaPluginToTheProject() {
         groovyPlugin.apply(project)
 
         assertTrue(project.getPlugins().hasPlugin(JavaPlugin));
     }
 
-    @Test public void addsGroovyConfigurationToTheProject() {
+    @Test
+    public void addsGroovyConfigurationToTheProject() {
         groovyPlugin.apply(project)
 
         def configuration = project.configurations.getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME)
-        assertThat(Configurations.getNames(configuration.extendsFrom, false), equalTo(toSet(GroovyBasePlugin.GROOVY_CONFIGURATION_NAME)))
+        assertThat(Configurations.getNames(configuration.extendsFrom), Matchers.isEmpty())
         assertFalse(configuration.visible)
         assertTrue(configuration.transitive)
     }
 
-    @Test public void addsGroovyConventionToEachSourceSet() {
+    @Test
+    public void addsGroovyConventionToEachSourceSet() {
         groovyPlugin.apply(project)
 
         def sourceSet = project.sourceSets.main
@@ -65,7 +68,8 @@ class GroovyPluginTest {
         assertThat(sourceSet.groovy.srcDirs, equalTo(toLinkedSet(project.file("src/test/groovy"))))
     }
 
-    @Test public void addsCompileTaskForEachSourceSet() {
+    @Test
+    public void addsCompileTaskForEachSourceSet() {
         groovyPlugin.apply(project)
 
         def task = project.tasks['compileGroovy']
@@ -79,7 +83,8 @@ class GroovyPluginTest {
         assertThat(task, dependsOn(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME, JavaPlugin.CLASSES_TASK_NAME))
     }
 
-    @Test public void dependenciesOfJavaPluginTasksIncludeGroovyCompileTasks() {
+    @Test
+    public void dependenciesOfJavaPluginTasksIncludeGroovyCompileTasks() {
         groovyPlugin.apply(project)
 
         def task = project.tasks[JavaPlugin.CLASSES_TASK_NAME]
@@ -88,8 +93,9 @@ class GroovyPluginTest {
         task = project.tasks[JavaPlugin.TEST_CLASSES_TASK_NAME]
         assertThat(task, dependsOn(hasItem('compileTestGroovy')))
     }
-    
-    @Test public void addsStandardTasksToTheProject() {
+
+    @Test
+    public void addsStandardTasksToTheProject() {
         groovyPlugin.apply(project)
 
         project.sourceSets.main.groovy.srcDirs(tmpDir.getTestDirectory())
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
index 07767f2..365f054 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 package org.gradle.api.plugins
+
 import org.gradle.api.DefaultTask
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.SourceSet
@@ -24,8 +25,9 @@ import org.gradle.api.tasks.bundling.Jar
 import org.gradle.api.tasks.compile.JavaCompile
 import org.gradle.api.tasks.javadoc.Javadoc
 import org.gradle.api.tasks.testing.Test
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.language.jvm.ClassDirectoryBinary
+import org.gradle.jvm.ClassDirectoryBinarySpec
+import org.gradle.language.java.JavaSourceSet
+import org.gradle.language.jvm.JvmResourceSet
 import org.gradle.util.SetSystemProperties
 import org.gradle.util.TestUtil
 import org.junit.Rule
@@ -37,23 +39,22 @@ import static org.gradle.util.WrapUtil.toLinkedSet
 class JavaBasePluginTest extends Specification {
     @Rule
     public SetSystemProperties sysProperties = new SetSystemProperties()
-    private final Project project = TestUtil.createRootProject()
-    private final JavaBasePlugin javaBasePlugin = new JavaBasePlugin(project.services.get(Instantiator))
+    private final DefaultProject project = TestUtil.createRootProject()
 
     void appliesBasePluginsAndAddsConventionObject() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
 
         then:
         project.plugins.hasPlugin(ReportingBasePlugin)
         project.plugins.hasPlugin(BasePlugin)
-        project.plugins.hasPlugin(JavaLanguagePlugin)
+        project.plugins.hasPlugin(LegacyJavaComponentPlugin)
         project.convention.plugins.java instanceof JavaPluginConvention
     }
 
     void createsTasksAndAppliesMappingsForNewSourceSet() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets.create('custom')
         
         then:
@@ -63,7 +64,7 @@ class JavaBasePluginTest extends Specification {
         set.output.classesDir == new File(project.buildDir, 'classes/custom')
 
         def processResources = project.tasks['processCustomResources']
-        processResources.description == "Processes resources 'custom:resources'."
+        processResources.description == "Processes JVM resources 'custom:resources'."
         processResources instanceof Copy
         TaskDependencyMatchers.dependsOn().matches(processResources)
         processResources.destinationDir == project.sourceSets.custom.output.resourcesDir
@@ -89,7 +90,7 @@ class JavaBasePluginTest extends Specification {
 
     void "wires generated resources task into classes task for sourceset"() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets.create('custom')
 
         and:
@@ -106,7 +107,7 @@ class JavaBasePluginTest extends Specification {
         def resourcesDir = project.file('target/resources')
 
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets.create('custom')
         project.sourceSets.custom.output.classesDir = classesDir
         project.sourceSets.custom.output.resourcesDir = resourcesDir
@@ -121,7 +122,7 @@ class JavaBasePluginTest extends Specification {
 
     void createsConfigurationsForNewSourceSet() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         def sourceSet = project.sourceSets.create('custom')
 
         then:
@@ -147,7 +148,7 @@ class JavaBasePluginTest extends Specification {
 
     void appliesMappingsToTasksDefinedByBuildScript() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
 
         then:
         def compile = project.task('customCompile', type: JavaCompile)
@@ -155,37 +156,19 @@ class JavaBasePluginTest extends Specification {
 
         def test = project.task('customTest', type: Test.class)
         test.workingDir == project.projectDir
-        test.testResultsDir == project.testResultsDir
-        test.testReportDir == project.testReportDir
-        test.testReport //by default (JUnit), the report is 'on'
+        test.reports.junitXml.destination == project.testResultsDir
+        test.reports.html.destination == project.testReportDir
+        test.reports.junitXml.enabled
+        test.reports.html.enabled
 
         def javadoc = project.task('customJavadoc', type: Javadoc)
         javadoc.destinationDir == project.file("$project.docsDir/javadoc")
         javadoc.title == project.extensions.getByType(ReportingExtension).apiDocTitle
     }
 
-    void "configures test task for testNG"() {
-        given:
-        javaBasePlugin.apply(project)
-        def test = project.task('customTest', type: Test.class)
-
-        when:
-        test.useTestNG()
-
-        then:
-        assert test.testReport
-
-        when:
-        test.testReport = false
-        test.useTestNG()
-
-        then:
-        assert !test.testReport
-    }
-
     void appliesMappingsToCustomJarTasks() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         def task = project.task('customJar', type: Jar)
 
         then:
@@ -195,7 +178,7 @@ class JavaBasePluginTest extends Specification {
 
     void createsLifecycleBuildTasks() {
         when:
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
 
         then:
         def build = project.tasks[JavaBasePlugin.BUILD_TASK_NAME]
@@ -209,7 +192,7 @@ class JavaBasePluginTest extends Specification {
     }
 
     def configuresTestTaskWhenDebugSystemPropertyIsSet() {
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         def task = project.tasks.create('test', Test.class)
 
         when:
@@ -221,7 +204,7 @@ class JavaBasePluginTest extends Specification {
     }
 
     def "configures test task when test.single is used"() {
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
         def task = project.tasks.create('test', Test.class)
         task.include 'ignoreme'
 
@@ -234,8 +217,8 @@ class JavaBasePluginTest extends Specification {
         task.inputs.getSourceFiles().empty
     }
 
-    def "adds functional and language source sets for each source set added to the 'sourceSets' container"() {
-        javaBasePlugin.apply(project)
+    def "adds language source sets for each source set added to the 'sourceSets' container"() {
+        project.pluginManager.apply(JavaBasePlugin)
 
         when:
         project.sourceSets {
@@ -251,23 +234,20 @@ class JavaBasePluginTest extends Specification {
         }
 
         then:
-        def functional = project.sources.findByName("custom")
-        functional != null
+        project.sources.size() == 2
 
         and:
-        def java = functional.findByName("java")
-        java != null
+        def java = project.sources.withType(JavaSourceSet).iterator().next()
         java.source.srcDirs as Set == [project.file("src1"), project.file("src2")] as Set
         java.compileClasspath.files as Set == project.files("jar1.jar", "jar2.jar") as Set
 
         and:
-        def resources = functional.findByName("resources")
-        resources != null
+        def resources = project.sources.withType(JvmResourceSet).iterator().next()
         resources.source.srcDirs as Set == [project.file("resrc1"), project.file("resrc2")] as Set
     }
 
     def "adds a class directory binary for each source set added to the 'sourceSets' container"() {
-        javaBasePlugin.apply(project)
+        project.pluginManager.apply(JavaBasePlugin)
 
         when:
         project.sourceSets {
@@ -279,9 +259,9 @@ class JavaBasePluginTest extends Specification {
 
         then:
         def binary = project.binaries.findByName("customClasses")
-        binary instanceof ClassDirectoryBinary
+        binary instanceof ClassDirectoryBinarySpec
         binary.classesDir == project.file("classes")
         binary.resourcesDir == project.file("resources")
-        binary.source as Set == [project.sources.custom.java, project.sources.custom.resources] as Set
+        binary.source as Set == project.sources as Set
     }
 }
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLanguagePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLanguagePluginTest.groovy
deleted file mode 100644
index 990bbec..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLanguagePluginTest.groovy
+++ /dev/null
@@ -1,50 +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.plugins
-
-import org.gradle.api.Project
-import org.gradle.language.java.JavaSourceSet
-import org.gradle.api.tasks.compile.JavaCompile
-import org.gradle.language.jvm.ClassDirectoryBinary
-import org.gradle.language.jvm.plugins.JvmLanguagePlugin
-import org.gradle.util.TestUtil
-
-import spock.lang.Specification
-
-class JavaLanguagePluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
-
-    def setup() {
-        project.plugins.apply(JavaLanguagePlugin)
-    }
-
-    def "applies jvm-lang plugin"() {
-        expect:
-        project.plugins.hasPlugin(JvmLanguagePlugin)
-    }
-
-    def "adds a JavaCompile task for every JavaSourceSet added to a ClassDirectoryBinary"() {
-        when:
-        project.sources.create("model").create("java", JavaSourceSet)
-        project.binaries.create("integTest", ClassDirectoryBinary)
-        project.binaries.integTest.source << project.sources.model.java
-
-        then:
-        def task = project.tasks.findByName("compileIntegTestJava")
-        task instanceof JavaCompile
-        task.description == "Compiles Java source 'model:java'."
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLibraryDistributionPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLibraryDistributionPluginTest.groovy
index e87eaa5..cc34fd2 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLibraryDistributionPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaLibraryDistributionPluginTest.groovy
@@ -27,7 +27,7 @@ class JavaLibraryDistributionPluginTest extends Specification {
 
     def "applies JavaPlugin and adds convention object with default values"() {
         when:
-        project.apply(plugin: JavaLibraryDistributionPlugin)
+        project.pluginManager.apply(JavaLibraryDistributionPlugin)
 
         then:
         project.plugins.hasPlugin(JavaPlugin.class)
@@ -38,7 +38,7 @@ class JavaLibraryDistributionPluginTest extends Specification {
 
     def "adds distZip task to project"() {
         when:
-        project.apply(plugin: JavaLibraryDistributionPlugin)
+        project.pluginManager.apply(JavaLibraryDistributionPlugin)
 
         then:
         def task = project.tasks.distZip
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginConventionTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginConventionTest.groovy
index cbd6ca1..de79d4f 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginConventionTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginConventionTest.groovy
@@ -44,13 +44,12 @@ class JavaPluginConventionTest {
     public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
     @Before public void setUp() {
-        project.plugins.apply(ReportingBasePlugin)
+        project.pluginManager.apply(ReportingBasePlugin)
         convention = new JavaPluginConvention(project, instantiator)
     }
 
     @Test public void defaultValues() {
         assertThat(convention.sourceSets, instanceOf(DefaultSourceSetContainer))
-        assertThat(convention.manifest, notNullValue())
         assertEquals('dependency-cache', convention.dependencyCacheDirName)
         assertEquals('docs', convention.docsDirName)
         assertEquals('test-results', convention.testResultsDirName)
@@ -92,10 +91,10 @@ class JavaPluginConventionTest {
     @Test public void testTestReportDirIsCalculatedRelativeToReportsDir() {
         assertEquals(new File(project.buildDir, 'reports/tests'), convention.testReportDir)
 
-        project.reportsDirName = 'other-reports-dir'
+        project.reporting.baseDir = 'other-reports-dir'
         convention.testReportDirName = 'other-test-dir'
 
-        assertEquals(new File(project.buildDir, 'other-reports-dir/other-test-dir'), convention.testReportDir)
+        assertEquals(new File(project.projectDir, 'other-reports-dir/other-test-dir'), convention.testReportDir)
     }
 
     @Test public void testTargetCompatibilityDefaultsToSourceCompatibilityWhenNotSet() {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
index 2a6f333..195d108 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.api.plugins
 
 import org.gradle.api.DefaultTask
-import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.file.FileCollectionMatchers
+import org.gradle.api.internal.component.BuildableJavaComponent
+import org.gradle.api.internal.component.ComponentRegistry
 import org.gradle.api.internal.java.JavaLibrary
-import org.gradle.api.internal.plugins.EmbeddableJavaProject
 import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.api.tasks.Copy
@@ -44,16 +44,18 @@ import static org.junit.Assert.*
 class JavaPluginTest {
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
-    private final Project project = TestUtil.createRootProject()
-    private final JavaPlugin javaPlugin = new JavaPlugin()
+    private final def project = TestUtil.createRootProject()
+    private final def javaPlugin = new JavaPlugin()
 
     @Test public void appliesBasePluginsAndAddsConventionObject() {
         javaPlugin.apply(project)
 
-        assertThat(project.convention.plugins.embeddedJavaProject, instanceOf(EmbeddableJavaProject))
-        assertThat(project.convention.plugins.embeddedJavaProject.rebuildTasks, equalTo([BasePlugin.CLEAN_TASK_NAME, JavaBasePlugin.BUILD_TASK_NAME]))
-        assertThat(project.convention.plugins.embeddedJavaProject.buildTasks, equalTo([JavaBasePlugin.BUILD_TASK_NAME]))
-        assertThat(project.convention.plugins.embeddedJavaProject.runtimeClasspath, notNullValue())
+        def component = project.services.get(ComponentRegistry).mainComponent
+        assertThat(component, instanceOf(BuildableJavaComponent))
+        assertThat(component.rebuildTasks, equalTo([BasePlugin.CLEAN_TASK_NAME, JavaBasePlugin.BUILD_TASK_NAME]))
+        assertThat(component.buildTasks, equalTo([JavaBasePlugin.BUILD_TASK_NAME]))
+        assertThat(component.runtimeClasspath, notNullValue())
+        assertThat(component.compileDependencies, equalTo(project.configurations.compile))
     }
 
     @Test public void addsConfigurationsToTheProject() {
@@ -184,9 +186,7 @@ class JavaPluginTest {
         assertThat(task.destinationDir, equalTo(project.libsDir))
         assertThat(task.mainSpec.sourcePaths, equalTo([project.sourceSets.main.output] as Set))
         assertThat(task.manifest, notNullValue())
-        assertThat(task.manifest, not(sameInstance(project.manifest)))
-        assertThat(task.manifest.mergeSpecs.size(), equalTo(1))
-        assertThat(task.manifest.mergeSpecs[0].mergePaths[0], sameInstance(project.manifest))
+        assertThat(task.manifest.mergeSpecs.size(), equalTo(0))
 
         task = project.tasks[BasePlugin.ASSEMBLE_TASK_NAME]
         assertThat(task, TaskDependencyMatchers.dependsOn(JavaPlugin.JAR_TASK_NAME))
@@ -243,8 +243,8 @@ class JavaPluginTest {
         assertThat(task.classpath, equalTo(project.sourceSets.test.runtimeClasspath))
         assertThat(task.testClassesDir, equalTo(project.sourceSets.test.output.classesDir))
         assertThat(task.workingDir, equalTo(project.projectDir))
-        assertThat(task.testResultsDir, equalTo(project.testResultsDir))
-        assertThat(task.testReportDir, equalTo(project.testReportDir))
+        assertThat(task.reports.junitXml.destination, equalTo(project.testResultsDir))
+        assertThat(task.reports.html.destination, equalTo(project.testReportDir))
     }
 
     @Test public void buildOtherProjects() {
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JvmLanguagePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JvmLanguagePluginTest.groovy
deleted file mode 100644
index f495ede..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JvmLanguagePluginTest.groovy
+++ /dev/null
@@ -1,89 +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.plugins
-import org.gradle.language.jvm.ClassDirectoryBinary
-import org.gradle.language.jvm.ResourceSet
-import org.gradle.language.jvm.internal.DefaultClassDirectoryBinary
-import org.gradle.language.jvm.plugins.JvmLanguagePlugin
-import org.gradle.language.jvm.tasks.ProcessResources
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class JvmLanguagePluginTest extends Specification {
-    def project = TestUtil.createRootProject()
-    def jvmLanguagePlugin = project.plugins.apply(JvmLanguagePlugin)
-
-    def "registers the 'ResourceSet' type for each functional source set added to the 'sources' container"() {
-        when:
-        project.sources.create("custom")
-        project.sources.custom.create("resources", ResourceSet)
-
-        then:
-        project.sources.custom.resources instanceof ResourceSet
-    }
-
-    def "registers the ClassDirectoryBinary type with the binaries container"() {
-        def binaries = project.extensions.findByName("binaries")
-        def binary = binaries.create("test", ClassDirectoryBinary)
-
-        expect:
-        binary != null
-        binary instanceof DefaultClassDirectoryBinary
-    }
-
-    def "adds a 'classes' task for every ClassDirectoryBinary added to the container"() {
-        when:
-        def binary = project.binaries.create("prod", ClassDirectoryBinary)
-
-        then:
-        binary.classesDir == new File("$project.buildDir/classes/prod")
-        def task = project.tasks.findByName("prodClasses")
-        task != null
-        task.description == "Assembles classes 'prod'."
-    }
-
-    def "adds a 'processResources' task for every ResourceSet added to a ClassDirectoryBinary"() {
-        ClassDirectoryBinary binary = project.binaries.create("prod", ClassDirectoryBinary)
-        ResourceSet resources = project.sources.create("main").create("resources", ResourceSet)
-
-        when:
-        binary.source.add(resources)
-
-        then:
-        project.tasks.size() == old(project.tasks.size()) + 1
-        def task = project.tasks.findByName("processProdResources")
-        task instanceof ProcessResources
-        task.description == "Processes resources 'main:resources'."
-    }
-
-    def "adds tasks based on short name when ClassDirectoryBinary has name ending in Classes"() {
-        when:
-        ClassDirectoryBinary binary = project.binaries.create("fooClasses", ClassDirectoryBinary)
-        ResourceSet resources = project.sources.create("main").create("resources", ResourceSet)
-        binary.source.add(resources)
-
-        then:
-        binary.classesDir == new File("$project.buildDir/classes/foo")
-        def task = project.tasks.findByName("fooClasses")
-        task != null
-        task.description == "Assembles classes 'foo'."
-
-        and:
-        def resourcesTask = project.tasks.findByName("processFooResources")
-        resourcesTask instanceof ProcessResources
-        resourcesTask.description == "Processes resources 'main:resources'."
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LanguageBasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LanguageBasePluginTest.groovy
deleted file mode 100644
index e227cf5..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LanguageBasePluginTest.groovy
+++ /dev/null
@@ -1,56 +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.plugins
-import org.gradle.api.Project
-import org.gradle.language.base.BinaryContainer
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.internal.BinaryInternal
-import org.gradle.language.base.internal.BinaryNamingScheme
-import org.gradle.language.base.plugins.LanguageBasePlugin
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-class LanguageBasePluginTest extends Specification {
-    Project project = TestUtil.createRootProject()
-
-    def setup() {
-        project.plugins.apply(LanguageBasePlugin)
-    }
-
-    def "adds a 'binaries' container to the project"() {
-        expect:
-        project.extensions.findByName("binaries") instanceof BinaryContainer
-    }
-
-    def "adds a 'sources' container to the project"() {
-        expect:
-        project.extensions.findByName("sources") instanceof ProjectSourceSet
-    }
-
-    def "creates a lifecycle task for each binary"() {
-        def binary = Mock(BinaryInternal)
-        def namingScheme = Mock(BinaryNamingScheme)
-
-        when:
-        project.extensions.findByType(BinaryContainer).add(binary)
-
-        then:
-        _ * binary.name >> "binaryName"
-        1 * binary.namingScheme >> namingScheme
-        1 * namingScheme.lifecycleTaskName >> "lifecycle"
-        1 * binary.setLifecycleTask({it == project.tasks.findByName("lifecycle")})
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LegacyJavaComponentPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LegacyJavaComponentPluginTest.groovy
new file mode 100644
index 0000000..6d9d97d
--- /dev/null
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/LegacyJavaComponentPluginTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins
+
+import org.gradle.api.internal.jvm.DefaultClassDirectoryBinarySpec
+import org.gradle.api.internal.project.DefaultProject
+import org.gradle.jvm.ClassDirectoryBinarySpec
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class LegacyJavaComponentPluginTest extends Specification {
+    DefaultProject project = TestUtil.createRootProject()
+
+    def setup() {
+        project.pluginManager.apply(LegacyJavaComponentPlugin)
+    }
+
+    def "applies jvm-lang plugin"() {
+        expect:
+        project.plugins.hasPlugin(LegacyJavaComponentPlugin)
+    }
+
+    def "registers the ClassDirectoryBinary type with the binaries container"() {
+        def binaries = project.extensions.findByName("binaries")
+        def binary = binaries.create("test", ClassDirectoryBinarySpec)
+
+        expect:
+        binary != null
+        binary instanceof DefaultClassDirectoryBinarySpec
+    }
+
+    def "adds a 'classes' task for every ClassDirectoryBinary added to the container"() {
+        when:
+        def binary = project.binaries.create("prod", ClassDirectoryBinarySpec)
+
+        then:
+        binary.classesDir == new File("$project.buildDir/classes/prod")
+        def task = project.tasks.findByName("prodClasses")
+        task != null
+        task.description == "Assembles classes 'prod'."
+    }
+}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/GroovyRuntimeTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/GroovyRuntimeTest.groovy
index 27cebdd..21450d0 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/GroovyRuntimeTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/GroovyRuntimeTest.groovy
@@ -26,7 +26,7 @@ class GroovyRuntimeTest extends Specification {
     def project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(GroovyBasePlugin)
+        project.pluginManager.apply(GroovyBasePlugin)
     }
 
     GroovyRuntime getGroovyRuntime() {
@@ -66,42 +66,6 @@ class GroovyRuntimeTest extends Specification {
         classifier << ["", "-indy"]
     }
 
-    def "inferred Groovy class path falls back to contents of 'groovy' configuration if the latter is explicitly configured"() {
-        project.dependencies {
-            groovy project.files("my-groovy.jar")
-        }
-
-        when:
-        def classpath = project.groovyRuntime.inferGroovyClasspath([project.file("other.jar"), project.file("groovy-all-2.1.2.jar")])
-
-        then:
-        classpath.singleFile.name == "my-groovy.jar"
-    }
-
-    def "inferred Groovy class path falls back to contents of 'groovy' configuration if no Groovy Jar found on class path"() {
-        project.dependencies {
-            groovy project.files("my-groovy.jar")
-        }
-
-        when:
-        def classpath = project.groovyRuntime.inferGroovyClasspath([project.file("other.jar"), project.file("other2.jar")])
-
-        then:
-        classpath.singleFile.name == "my-groovy.jar"
-    }
-
-    def "inferred Groovy class path falls back to contents of 'groovy' configuration if 'groovy' Jar found and no repository declared"() {
-        project.dependencies {
-            groovy project.files("my-groovy.jar")
-        }
-
-        when:
-        def classpath = project.groovyRuntime.inferGroovyClasspath([project.file("other.jar"), project.file("groovy-2.1.2.jar")])
-
-        then:
-        classpath.singleFile.name == "my-groovy.jar"
-    }
-
     def "useful error message is produced when no groovy runtime could be found on a classpath"() {
         given:
         def classpath = [project.file("other.jar")]
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy
index 9f95392..eac8e5a 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/application/CreateStartScriptsTest.groovy
@@ -15,12 +15,20 @@
  */
 package org.gradle.api.tasks.application
 
-import spock.lang.Specification
+import org.gradle.api.internal.plugins.UnixStartScriptGenerator
+import org.gradle.api.internal.plugins.WindowsStartScriptGenerator
 import org.gradle.util.TestUtil
+import spock.lang.Specification
 
 class CreateStartScriptsTest extends Specification {
     final CreateStartScripts task = TestUtil.createTask(CreateStartScripts.class)
 
+    def "uses default start script generators"() {
+        expect:
+        task.unixStartScriptGenerator instanceof UnixStartScriptGenerator
+        task.windowsStartScriptGenerator instanceof WindowsStartScriptGenerator
+    }
+
     def scriptNameDefaultsToApplicationName() {
         task.outputDir = new File('output')
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/bundling/JarTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/bundling/JarTest.groovy
deleted file mode 100644
index 1f9dd44..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/bundling/JarTest.groovy
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.bundling
-
-import org.gradle.api.java.archives.internal.DefaultManifest
-import org.junit.Before
-import org.junit.Test
-
-import static org.junit.Assert.assertEquals
-import static org.junit.Assert.assertNotNull
-
-class JarTest extends AbstractArchiveTaskTest {
-    Jar jar
-
-    @Before public void setUp()  {
-        jar = createTask(Jar)
-        configure(jar)
-    }
-
-    AbstractArchiveTask getArchiveTask() {
-        jar
-    }
-
-    @Test public void testJar() {
-        assertEquals(Jar.DEFAULT_EXTENSION, jar.extension)
-        assertNotNull(jar.manifest)
-        assertNotNull(jar.metaInf)
-    }
-
-    @Test public void testManifest() {
-        jar.manifest = new DefaultManifest(null);
-        jar.manifest {
-            attributes(key: 'value')
-        }
-        assertEquals(jar.manifest.attributes.key, 'value')
-    }
-
-    @Test public void testManifestWithNullManifest() {
-        jar.manifest = null
-        jar.manifest {
-            attributes(key: 'value')
-        }
-        assertEquals(jar.manifest.attributes.key, 'value')
-    }
-}
\ No newline at end of file
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy
deleted file mode 100644
index 8eee899..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/CompileOptionsTest.groovy
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile
-
-import org.junit.Before
-import org.junit.Test
-
-import static org.gradle.util.Matchers.isEmpty
-import static org.junit.Assert.*
-
-class CompileOptionsTest {
-    static final Map TEST_DEBUG_OPTION_MAP = [someDebugOption: 'someDebugOptionValue']
-    static final Map TEST_FORK_OPTION_MAP = [someForkOption: 'someForkOptionValue']
-    static final Map TEST_DEPEND_OPTION_MAP = [someDependOption: 'someDependOptionValue']
-
-    CompileOptions compileOptions
-
-    @Before public void setUp()  {
-        compileOptions = new CompileOptions()
-        compileOptions.debugOptions = [optionMap: {TEST_DEBUG_OPTION_MAP}] as DebugOptions
-        compileOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as ForkOptions
-    }
-
-    @Test public void testCompileOptions() {
-        assertTrue(compileOptions.debug)
-        assertTrue(compileOptions.failOnError)
-        assertTrue(compileOptions.warnings)
-
-        assertFalse(compileOptions.includeJavaRuntime)
-        assertFalse(compileOptions.deprecation)
-        assertFalse(compileOptions.listFiles)
-        assertFalse(compileOptions.verbose)
-        assertFalse(compileOptions.fork)
-        assertFalse(compileOptions.useAnt)
-
-        assertThat(compileOptions.compilerArgs, isEmpty())
-        assertNull(compileOptions.encoding)
-        assertNull(compileOptions.compiler)
-        assertNull(compileOptions.bootClasspath)
-        assertNull(compileOptions.extensionDirs)
-
-        assertNotNull(compileOptions.forkOptions)
-        assertNotNull(compileOptions.debugOptions)
-    }
-
-    @Test public void testOptionMapForDebugOptions() {
-        Map optionMap = compileOptions.optionMap()
-        assertEquals(optionMap.subMap(TEST_DEBUG_OPTION_MAP.keySet()), TEST_DEBUG_OPTION_MAP)
-        assertEquals(optionMap.subMap(TEST_FORK_OPTION_MAP.keySet()), TEST_FORK_OPTION_MAP)
-    }
-
-    @Test public void testOptionMapWithNullables() {
-        Map optionMap = compileOptions.optionMap()
-        Map nullables = [
-                encoding: 'encoding',
-                compiler: 'compiler',
-                bootClasspath: 'bootClasspath',
-                extensionDirs: 'extdirs'
-        ]
-        nullables.each {String field, String antProperty ->
-            assertFalse(optionMap.keySet().contains(antProperty))
-        }
-
-        nullables.keySet().each {compileOptions."$it" = "${it}Value"}
-        optionMap = compileOptions.optionMap()
-        nullables.each {String field, String antProperty ->
-            assertEquals("${field}Value" as String, optionMap[antProperty])
-        }
-    }
-
-    @Test public void testOptionMapWithTrueFalseValues() {
-        Map booleans = [
-                failOnError: 'failOnError',
-                verbose: 'verbose',
-                listFiles: 'listFiles',
-                deprecation: 'deprecation',
-                warnings: 'nowarn',
-                debug: 'debug',
-                includeJavaRuntime: 'includeJavaRuntime'
-        ]
-        booleans.keySet().each {compileOptions."$it" = true}
-        Map optionMap = compileOptions.optionMap()
-        booleans.values().each {
-            if (it.equals('nowarn')) {
-                assertEquals(false, optionMap[it])
-            } else {
-                assertEquals(true, optionMap[it])
-            }
-        }
-        booleans.keySet().each {compileOptions."$it" = false}
-        optionMap = compileOptions.optionMap()
-        booleans.values().each {
-            if (it.equals('nowarn')) {
-                assertEquals(true, optionMap[it])
-            } else {
-                assertEquals(false, optionMap[it])
-            }
-        }
-    }
-
-    @Test public void testWithExcludeFieldsFromOptionMap() {
-      compileOptions.compilerArgs = [[value: 'something']]
-        Map optionMap = compileOptions.optionMap()
-        ['debugOptions', 'forkOptions', 'compilerArgs'].each {
-            assertFalse(optionMap.containsKey(it))
-        }
-    }
-
-    @Test public void testFork() {
-        compileOptions.fork = false
-        boolean forkUseCalled = false
-        compileOptions.forkOptions = [define: {Map args ->
-            forkUseCalled = true
-            assertEquals(TEST_FORK_OPTION_MAP, args)
-        }] as ForkOptions
-        assert compileOptions.fork(TEST_FORK_OPTION_MAP).is(compileOptions)
-        assertTrue(compileOptions.fork)
-        assertTrue(forkUseCalled)
-    }
-
-    @Test public void testDebug() {
-        compileOptions.debug = false
-        boolean debugUseCalled = false
-        compileOptions.debugOptions = [define: {Map args ->
-            debugUseCalled = true
-            assertEquals(TEST_DEBUG_OPTION_MAP, args)
-        }] as DebugOptions
-        assert compileOptions.debug(TEST_DEBUG_OPTION_MAP).is(compileOptions)
-        assertTrue(compileOptions.debug)
-        assertTrue(debugUseCalled)
-    }
-
-    @Test public void testDepend() {
-        compileOptions.useDepend = false
-        boolean dependUseCalled = false
-        compileOptions.dependOptions = [define: {Map args ->
-            dependUseCalled = true
-            assertEquals(TEST_DEPEND_OPTION_MAP, args)
-        }] as DependOptions
-        assert compileOptions.depend(TEST_DEPEND_OPTION_MAP).is(compileOptions)
-        assertTrue(compileOptions.useDepend)
-        assertTrue(dependUseCalled)
-    }
-
-    @Test public void testDefine() {
-        compileOptions.debug = false
-        compileOptions.compiler = null
-        compileOptions.bootClasspath = 'xxxx'
-        compileOptions.fork = false
-        compileOptions.useDepend = false
-        compileOptions.define(debug: true, compiler: 'compiler', bootClasspath: null)
-        assertTrue(compileOptions.debug)
-        assertEquals('compiler', compileOptions.compiler)
-        assertNull(compileOptions.bootClasspath)
-        assertFalse(compileOptions.fork)
-        assertFalse(compileOptions.useDepend)
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy
deleted file mode 100644
index c64fb7e..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileOptionsTest.groovy
+++ /dev/null
@@ -1,101 +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.tasks.compile
-
-import org.junit.Before
-import org.junit.Test
-
-import static org.junit.Assert.*
-
-class GroovyCompileOptionsTest {
-    static final Map TEST_FORK_OPTION_MAP = [someForkOption: 'someForkOptionValue']
-
-    GroovyCompileOptions compileOptions
-
-    @Before public void setUp()  {
-        compileOptions = new GroovyCompileOptions()
-        compileOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as GroovyForkOptions
-    }
-
-    @Test public void testCompileOptions() {
-        assertTrue(compileOptions.failOnError)
-        assertFalse(compileOptions.includeJavaRuntime)
-        assertFalse(compileOptions.stacktrace)
-        assertFalse(compileOptions.listFiles)
-        assertFalse(compileOptions.verbose)
-        assertTrue(compileOptions.fork)
-        assertEquals(['java', 'groovy'], compileOptions.fileExtensions)
-        assertEquals('UTF-8', compileOptions.encoding)
-        assertNotNull(compileOptions.forkOptions)
-    }
-
-    @Test public void testOptionMapForForkOptions() {
-        Map optionMap = compileOptions.optionMap()
-        assertEquals(optionMap.subMap(TEST_FORK_OPTION_MAP.keySet()), TEST_FORK_OPTION_MAP)
-    }
-
-    @Test public void testOptionMapWithTrueFalseValues() {
-        Map booleans = [
-                failOnError: 'failOnError',
-                verbose: 'verbose',
-                listFiles: 'listFiles',
-                fork: 'fork',
-                includeJavaRuntime: 'includeJavaRuntime'
-        ]
-        booleans.keySet().each {compileOptions."$it" = true}
-        Map optionMap = compileOptions.optionMap()
-        booleans.values().each {
-            if (it.equals('nowarn')) {
-                assertEquals(false, optionMap[it])
-            } else {
-                assertEquals(true, optionMap[it])
-            }
-        }
-        booleans.keySet().each {compileOptions."$it" = false}
-        optionMap = compileOptions.optionMap()
-        booleans.values().each {
-            if (it.equals('nowarn')) {
-                assertEquals(true, optionMap[it])
-            } else {
-                assertEquals(false, optionMap[it])
-            }
-        }
-    }
-
-    @Test public void testFork() {
-        compileOptions.fork = false
-        boolean forkUseCalled = false
-        compileOptions.forkOptions = [define: {Map args ->
-            forkUseCalled = true
-            assertEquals(TEST_FORK_OPTION_MAP, args)
-        }] as GroovyForkOptions
-        assert compileOptions.fork(TEST_FORK_OPTION_MAP).is(compileOptions)
-        assertTrue(compileOptions.fork)
-        assertTrue(forkUseCalled)
-    }
-
-    @Test public void testDefine() {
-        compileOptions.stacktrace = false
-        compileOptions.verbose = false
-        compileOptions.encoding = 'xxxx'
-        compileOptions.fork = false
-        compileOptions.define(stacktrace: true, encoding: 'encoding')
-        assertTrue(compileOptions.stacktrace)
-        assertEquals('encoding', compileOptions.encoding)
-        assertFalse(compileOptions.verbose)
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileTest.java
index e4ed4f5..562b8a4 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/GroovyCompileTest.java
@@ -19,7 +19,7 @@ package org.gradle.api.tasks.compile;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.language.base.internal.compile.Compiler;
 import org.gradle.api.internal.tasks.compile.GroovyJavaJointCompileSpec;
 import org.gradle.api.tasks.WorkResult;
 import org.gradle.util.GFileUtils;
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.java
deleted file mode 100644
index 1be1aba..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/compile/JavaCompileTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.compile;
-
-import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.hamcrest.Matchers;
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class JavaCompileTest extends AbstractCompileTest {
-    private JavaCompile compile;
-
-    private Compiler<JavaCompileSpec> compilerMock;
-
-    private Mockery context = new JUnit4GroovyMockery();
-
-    @Before
-    public void setUp()  {
-        compile = createTask(JavaCompile.class);
-        compilerMock = context.mock(Compiler.class);
-        compile.setJavaCompiler(compilerMock);
-
-        GFileUtils.touch(new File(srcDir, "incl/file.java"));
-    }
-           
-    public ConventionTask getTask() {
-        return compile;
-    }
-
-    public void testExecute(final int numFilesCompiled) {
-        setUpMocksAndAttributes(compile);
-        context.checking(new Expectations() {{
-            WorkResult result = context.mock(WorkResult.class);
-
-            one(compilerMock).execute((JavaCompileSpec) with(Matchers.notNullValue()));
-            will(returnValue(result));
-            allowing(result).getDidWork();
-            will(returnValue(numFilesCompiled > 0));
-        }});
-        compile.compile();
-    }
-
-    @Test
-    public void testExecuteDoingWork() {
-        testExecute(7);
-        assertTrue(compile.getDidWork());
-    }
-
-    @Test
-    public void testExecuteNotDoingWork() {
-        testExecute(0);
-        assertFalse(compile.getDidWork());
-    }
-
-    public JavaCompile getCompile() {
-        return compile;
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
deleted file mode 100644
index b408421..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.javadoc;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.tasks.AbstractConventionTaskTest;
-import org.gradle.external.javadoc.StandardJavadocDocletOptions;
-import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.process.internal.ExecException;
-import org.gradle.test.fixtures.file.TestFile;
-import org.gradle.util.GFileUtils;
-import org.gradle.util.WrapUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class JavadocTest extends AbstractConventionTaskTest {
-    private final JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
-    private final TestFile testDir = tmpDir.getTestDirectory();
-    private final File destDir = new File(testDir, "dest");
-    private final File srcDir = new File(testDir, "srcdir");
-    private final Set<File> classpath = WrapUtil.toSet(new File("classpath"));
-    private JavadocExecHandleBuilder javadocExecHandleBuilderMock = context.mock(JavadocExecHandleBuilder.class);
-    private ExecAction execActionMock = context.mock(ExecAction.class);
-    private Javadoc task;
-    private FileCollection configurationMock = new SimpleFileCollection(classpath);
-    private String executable = "somepath";
-
-    @Before
-    public void setUp() {
-        task = createTask(Javadoc.class);
-        task.setClasspath(configurationMock);
-        task.setExecutable(executable);
-        task.setJavadocExecHandleBuilder(javadocExecHandleBuilderMock);
-        GFileUtils.touch(new File(srcDir, "file.java"));
-    }
-
-    public ConventionTask getTask() {
-        return task;
-    }
-
-    private void expectJavadocExecHandle() {
-        context.checking(new Expectations(){{
-            one(javadocExecHandleBuilderMock).execDirectory(getProject().getRootDir());
-            will(returnValue(javadocExecHandleBuilderMock));
-            one(javadocExecHandleBuilderMock).options(task.getOptions());
-            will(returnValue(javadocExecHandleBuilderMock));
-            one(javadocExecHandleBuilderMock).optionsFile(new File(getProject().getBuildDir(), "tmp/testTask/javadoc.options"));
-            will(returnValue(javadocExecHandleBuilderMock));
-            one(javadocExecHandleBuilderMock).getExecHandle();
-            will(returnValue(execActionMock));
-            one(javadocExecHandleBuilderMock).setExecutable(executable);
-        }});
-    }
-
-    @Test
-    public void defaultExecution() {
-        task.setDestinationDir(destDir);
-        task.source(srcDir);
-
-        expectJavadocExecHandle();
-        expectJavadocExec();
-
-        task.execute();
-    }
-
-    private void expectJavadocExec() {
-        context.checking(new Expectations(){{
-            one(execActionMock).execute();
-        }});
-    }
-
-    @Test
-    public void wrapsExecutionFailure() {
-        final ExecException failure = new ExecException(null);
-
-        task.setDestinationDir(destDir);
-        task.source(srcDir);
-
-        expectJavadocExecHandle();
-        context.checking(new Expectations(){{
-            one(execActionMock).execute();
-            will(throwException(failure));
-        }});
-
-        try {
-            task.generate();
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), endsWith("Javadoc generation failed."));
-            assertThat(e.getCause(), sameInstance((Throwable) failure));
-        }
-    }
-
-    @Test
-    public void executionWithOptionalAtributes() {
-        task.setDestinationDir(destDir);
-        task.source(srcDir);
-        task.setMaxMemory("max-memory");
-        task.setVerbose(true);
-
-        expectJavadocExecHandle();
-        expectJavadocExec();
-
-        task.execute();
-    }
-
-    @Test
-    public void setsTheWindowAndDocTitleIfNotSet() {
-        task.setDestinationDir(destDir);
-        task.source(srcDir);
-        task.setTitle("title");
-
-        expectJavadocExecHandle();
-        expectJavadocExec();
-
-        task.execute();
-        StandardJavadocDocletOptions options = (StandardJavadocDocletOptions) task.getOptions();
-        assertThat(options.getDocTitle(), equalTo("title"));
-        assertThat(options.getWindowTitle(), equalTo("title"));
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTaskSpec.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTaskSpec.groovy
index cdbe8ef..ce8227d 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTaskSpec.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTaskSpec.groovy
@@ -16,49 +16,219 @@
 
 package org.gradle.api.tasks.testing
 
-import org.gradle.api.internal.tasks.testing.TestFramework
-import org.gradle.api.internal.tasks.testing.TestResultProcessor
+import org.gradle.api.GradleException
+import org.gradle.api.internal.tasks.testing.*
 import org.gradle.api.internal.tasks.testing.detection.TestExecuter
 import org.gradle.api.internal.tasks.testing.junit.report.TestReporter
-import org.gradle.listener.ListenerBroadcast
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 class TestTaskSpec extends Specification {
+    def testExecuter = Mock(TestExecuter)
+    def testFramework = Mock(TestFramework)
+    def suiteDescriptor = Mock(TestDescriptorInternal)
+    def testDescriptor = Mock(TestDescriptorInternal)
 
-    private testExecuter = Mock(TestExecuter)
-    private testFramework = Mock(TestFramework)
-    private testListenerBroadcaster = Mock(ListenerBroadcast)
-    private testOutputListenerBroadcaster = Mock(ListenerBroadcast)
+    def task = TestUtil.createTask(Test, [testExecuter: testExecuter, testFramework: testFramework])
 
-    private task = TestUtil.createTask(Test, [testExecuter: testExecuter, testFramework: testFramework,
-            testListenerBroadcaster: testListenerBroadcaster, testOutputListenerBroadcaster: testOutputListenerBroadcaster])
+    def setup() {
+        task.testReporter = Mock(TestReporter)
+        task.binResultsDir = task.project.file('build/test-results')
+        task.reports.junitXml.destination = task.project.file('build/test-results')
+    }
+
+    def expectTestSuiteFails() {
+        def testId = "test"
+        suiteDescriptor.id >> testId
+        suiteDescriptor.parent >> null
+        suiteDescriptor.composite >> true
+        def startEvent = Stub(TestStartEvent) {
+            getParentId() >> null
+        }
+        def finishEvent = Stub(TestCompleteEvent) {
+            getResultType() >> TestResult.ResultType.FAILURE
+        }
 
-    public setup(){
-        task.setTestReporter(Mock(TestReporter))
-        task.setBinResultsDir(task.project.file('build/test-results'))
+        _ * testExecuter.execute(task, _) >> { Test task, TestResultProcessor processor ->
+            processor.started(suiteDescriptor, startEvent)
+            processor.completed(testId, finishEvent)
+        }
     }
 
-    def "adds listeners and removes after execution"() {
-        task.setTestNameIncludePattern("Foo")
+    def expectTestSuitePasses() {
+        def testId = "test"
+        suiteDescriptor.id >> testId
+        suiteDescriptor.parent >> null
+        suiteDescriptor.composite >> true
+        def startEvent = Stub(TestStartEvent) {
+            getParentId() >> null
+        }
+        def finishEvent = Stub(TestCompleteEvent) {
+            getResultType() >> TestResult.ResultType.SUCCESS
+        }
+
+        _ * testExecuter.execute(task, _) >> { Test task, TestResultProcessor processor ->
+            processor.started(suiteDescriptor, startEvent)
+            processor.completed(testId, finishEvent)
+        }
+    }
+
+    def expectTestPasses() {
+        suiteDescriptor.id >> "suite"
+        suiteDescriptor.parent >> null
+        suiteDescriptor.composite >> true
+
+        testDescriptor.id >> "test"
+        testDescriptor.parent >> suiteDescriptor
+        testDescriptor.composite >> false
+        testDescriptor.className >> "class"
+        testDescriptor.name >> "method"
+
+        def suiteStartEvent = Stub(TestStartEvent) {
+            getParentId() >> null
+        }
+        def testStartEvent = Stub(TestStartEvent) {
+            getParentId() >> "suite"
+        }
+        def finishEvent = Stub(TestCompleteEvent) {
+            getResultType() >> TestResult.ResultType.SUCCESS
+        }
+
+        _ * testExecuter.execute(task, _) >> { Test task, TestResultProcessor processor ->
+            processor.started(suiteDescriptor, suiteStartEvent)
+            processor.started(testDescriptor, testStartEvent)
+            processor.completed("test", finishEvent)
+            processor.completed("suite", finishEvent)
+        }
+    }
+
+    def "reports test failures"() {
+        given:
+        expectTestSuiteFails()
 
         when:
         task.executeTests()
 
         then:
-        4 * testListenerBroadcaster.add(_)
-        2 * testOutputListenerBroadcaster.add(_)
+        GradleException e = thrown()
+        e.message.startsWith("There were failing tests. See the report at")
+    }
+
+    def "notifies listener of test progress"() {
+        def listener = Mock(TestListener)
+
+        given:
+        expectTestPasses()
+
+        task.addTestListener(listener)
+
+        when:
+        task.executeTests()
 
         then:
-        1 * testExecuter.execute(task, _ as TestResultProcessor)
+        1 * listener.beforeSuite(_)
+        1 * listener.beforeTest(_)
+        1 * listener.afterTest(_, _)
+        1 * listener.afterSuite(_, _)
+        0 * listener._
+    }
+
+    def "notifies closure before suite"() {
+        def closure = Mock(Closure)
+
+        given:
+        expectTestSuitePasses()
+
+        task.beforeSuite(closure)
+
+        when:
+        task.executeTests()
 
         then:
-        1 * testListenerBroadcaster.removeAll()
-        1 * testOutputListenerBroadcaster.removeAll()
+        _ * closure.maximumNumberOfParameters >> 0
+        1 * closure.call()
+        0 * closure._
+    }
+
+    def "notifies closure after suite"() {
+        def closure = Mock(Closure)
+
+        given:
+        expectTestSuitePasses()
+
+        task.afterSuite(closure)
+
+        when:
+        task.executeTests()
+
+        then:
+        _ * closure.maximumNumberOfParameters >> 0
+        1 * closure.call()
+        0 * closure._
+    }
+
+    def "notifies closure before test"() {
+        def closure = Mock(Closure)
+
+        given:
+        expectTestPasses()
+
+        task.beforeTest(closure)
+
+        when:
+        task.executeTests()
+
+        then:
+        _ * closure.maximumNumberOfParameters >> 0
+        1 * closure.call()
+        0 * closure._
+    }
+
+    def "notifies closure after test"() {
+        def closure = Mock(Closure)
+
+        given:
+        expectTestPasses()
+
+        task.afterTest(closure)
+
+        when:
+        task.executeTests()
+
+        then:
+        _ * closure.maximumNumberOfParameters >> 0
+        1 * closure.call()
+        0 * closure._
+    }
+
+    def "adds listeners and removes after execution"() {
+        given:
+        expectTestSuitePasses()
+
+        when:
+        task.addTestListener(Stub(TestListener))
+        task.addTestOutputListener(Stub(TestOutputListener))
+
+        then:
+        !task.testListenerInternalBroadcaster.isEmpty()
+        !task.testOutputListenerBroadcaster.isEmpty()
+        !task.testListenerInternalBroadcaster.isEmpty()
+
+        when:
+        task.executeTests()
+
+        then:
+        task.testListenerInternalBroadcaster.isEmpty()
+        task.testOutputListenerBroadcaster.isEmpty()
+        task.testListenerInternalBroadcaster.isEmpty()
     }
 
     def "removes listeners even if execution fails"() {
-        testExecuter.execute(task, _ as TestResultProcessor) >> { throw new RuntimeException("Boo!")}
+        given:
+        testExecuter.execute(task, _ as TestResultProcessor) >> { throw new RuntimeException("Boo!") }
+
+        task.addTestListener(Stub(TestListener))
+        task.addTestOutputListener(Stub(TestOutputListener))
 
         when:
         task.executeTests()
@@ -68,7 +238,8 @@ class TestTaskSpec extends Specification {
         ex.message == "Boo!"
 
         and:
-        1 * testListenerBroadcaster.removeAll()
-        1 * testOutputListenerBroadcaster.removeAll()
+        task.testListenerInternalBroadcaster.isEmpty()
+        task.testOutputListenerBroadcaster.isEmpty()
+        task.testListenerInternalBroadcaster.isEmpty()
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
index 37dd054..3ba26a5 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.tasks.testing;
 
-import org.gradle.api.GradleException;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.ConventionTask;
@@ -27,18 +26,16 @@ import org.gradle.api.internal.file.collections.FileTreeAdapter;
 import org.gradle.api.internal.file.collections.SimpleFileCollection;
 import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
 import org.gradle.api.internal.tasks.testing.TestFramework;
+import org.gradle.api.internal.tasks.testing.TestResultProcessor;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.detection.TestExecuter;
 import org.gradle.api.internal.tasks.testing.detection.TestFrameworkDetector;
 import org.gradle.api.internal.tasks.testing.junit.JUnitTestFramework;
 import org.gradle.api.internal.tasks.testing.junit.report.TestReporter;
 import org.gradle.api.internal.tasks.testing.junit.result.TestResultsProvider;
-import org.gradle.api.internal.tasks.testing.results.TestListenerAdapter;
 import org.gradle.api.tasks.AbstractConventionTaskTest;
 import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.GFileUtils;
-import org.gradle.util.TestClosure;
-import org.gradle.util.TestUtil;
 import org.hamcrest.Description;
 import org.jmock.Expectations;
 import org.jmock.api.Action;
@@ -109,8 +106,8 @@ public class TestTest extends AbstractConventionTaskTest {
         assertThat(test.getTestFramework(), instanceOf(JUnitTestFramework.class));
         assertNull(test.getTestClassesDir());
         assertNull(test.getClasspath());
-        assertNull(test.getTestResultsDir());
-        assertNull(test.getTestReportDir());
+        assertNull(test.getReports().getJunitXml().getDestination());
+        assertNull(test.getReports().getHtml().getDestination());
         assertThat(test.getIncludes(), isEmpty());
         assertThat(test.getExcludes(), isEmpty());
         assertFalse(test.getIgnoreFailures());
@@ -139,18 +136,6 @@ public class TestTest extends AbstractConventionTaskTest {
     }
 
     @org.junit.Test
-    public void testExecuteWithTestFailuresAndStopAtFailures() {
-        configureTask();
-        expectTestsFail();
-        try {
-            test.executeTests();
-            fail();
-        } catch (GradleException e) {
-            assertThat(e.getMessage(), startsWith("There were failing tests. See the report at"));
-        }
-    }
-
-    @org.junit.Test
     public void testExecuteWithTestFailuresAndIgnoreFailures() {
         configureTask();
         test.setIgnoreFailures(true);
@@ -171,7 +156,7 @@ public class TestTest extends AbstractConventionTaskTest {
     @org.junit.Test
     public void testSetsTestFrameworkToNullAfterExecution() {
         configureTask();
-        // using a jmock generated mock for testFramework does not work here as it is referenced
+        // using a jmock generated taskFactory for testFramework does not work here as it is referenced
         // by jmock holds some references.
 
         test.useTestFramework(new TestFramework() {
@@ -193,7 +178,7 @@ public class TestTest extends AbstractConventionTaskTest {
             }
         });
         context.checking(new Expectations() {{
-            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestListenerAdapter.class)));
+            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestResultProcessor.class)));
         }});
 
         WeakReference<TestFramework> weakRef = new WeakReference<TestFramework>(test.getTestFramework());
@@ -229,78 +214,6 @@ public class TestTest extends AbstractConventionTaskTest {
     }
 
     @org.junit.Test
-    public void notifiesListenerOfEvents() {
-        final TestListener listener = context.mock(TestListener.class);
-        test.addTestListener(listener);
-
-        final TestDescriptor testDescriptor = context.mock(TestDescriptor.class);
-
-        context.checking(new Expectations() {{
-            one(listener).beforeSuite(testDescriptor);
-        }});
-
-        test.getTestListenerBroadcaster().getSource().beforeSuite(testDescriptor);
-    }
-
-    @org.junit.Test
-    public void notifiesListenerBeforeSuite() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        test.beforeSuite(TestUtil.toClosure(closure));
-
-        final TestDescriptor testDescriptor = context.mock(TestDescriptor.class);
-
-        context.checking(new Expectations() {{
-            one(closure).call(testDescriptor);
-        }});
-
-        test.getTestListenerBroadcaster().getSource().beforeSuite(testDescriptor);
-    }
-
-    @org.junit.Test
-    public void notifiesListenerAfterSuite() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        test.afterSuite(TestUtil.toClosure(closure));
-
-        final TestDescriptor testDescriptor = context.mock(TestDescriptor.class);
-        final TestResult result = context.mock(TestResult.class);
-
-        context.checking(new Expectations() {{
-            one(closure).call(testDescriptor);
-        }});
-
-        test.getTestListenerBroadcaster().getSource().afterSuite(testDescriptor, result);
-    }
-
-    @org.junit.Test
-    public void notifiesListenerBeforeTest() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        test.beforeTest(TestUtil.toClosure(closure));
-
-        final TestDescriptor testDescriptor = context.mock(TestDescriptor.class);
-
-        context.checking(new Expectations() {{
-            one(closure).call(testDescriptor);
-        }});
-
-        test.getTestListenerBroadcaster().getSource().beforeTest(testDescriptor);
-    }
-
-    @org.junit.Test
-    public void notifiesListenerAfterTest() {
-        final TestClosure closure = context.mock(TestClosure.class);
-        test.afterTest(TestUtil.toClosure(closure));
-
-        final TestDescriptor testDescriptor = context.mock(TestDescriptor.class);
-        final TestResult result = context.mock(TestResult.class);
-
-        context.checking(new Expectations() {{
-            one(closure).call(testDescriptor);
-        }});
-
-        test.getTestListenerBroadcaster().getSource().afterTest(testDescriptor, result);
-    }
-
-    @org.junit.Test
     public void testIncludes() {
         assertSame(test, test.include(TEST_PATTERN_1, TEST_PATTERN_2));
         assertEquals(toLinkedSet(TEST_PATTERN_1, TEST_PATTERN_2), test.getIncludes());
@@ -318,7 +231,7 @@ public class TestTest extends AbstractConventionTaskTest {
 
     private void expectTestsExecuted() {
         context.checking(new Expectations() {{
-            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestListenerAdapter.class)));
+            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestResultProcessor.class)));
         }});
     }
 
@@ -339,15 +252,15 @@ public class TestTest extends AbstractConventionTaskTest {
 
             ignoring(testDescriptor);
 
-            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestListenerAdapter.class)));
+            one(testExecuterMock).execute(with(sameInstance(test)), with(notNullValue(TestResultProcessor.class)));
             will(new Action() {
                 public void describeTo(Description description) {
                     description.appendText("fail tests");
                 }
 
                 public Object invoke(Invocation invocation) throws Throwable {
-                    TestTest.this.test.getTestListenerBroadcaster().getSource().beforeSuite(testDescriptor);
-                    TestTest.this.test.getTestListenerBroadcaster().getSource().afterSuite(testDescriptor, result);
+//                    test.getTestListenerBroadcaster().started(testDescriptor, null);
+//                    test.getTestListenerBroadcaster().completed(testDescriptor, result, null);
                     return null;
                 }
             });
@@ -359,9 +272,9 @@ public class TestTest extends AbstractConventionTaskTest {
         test.setTestExecuter(testExecuterMock);
 
         test.setTestClassesDir(classesDir);
-        test.setTestResultsDir(resultsDir);
+        test.getReports().getJunitXml().setDestination(resultsDir);
         test.setBinResultsDir(binResultsDir);
-        test.setTestReportDir(reportDir);
+        test.getReports().getHtml().setDestination(reportDir);
         test.setClasspath(classpathMock);
         test.setTestSrcDirs(Collections.<File>emptyList());
     }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/testng/TestNGOptionsTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/testng/TestNGOptionsTest.groovy
index 31b0993..a4b9049 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/testng/TestNGOptionsTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/testng/TestNGOptionsTest.groovy
@@ -60,6 +60,8 @@ public class TestNGOptionsTest extends AbstractTestFrameworkOptionsTest<TestNGTe
         assertEquals('Gradle suite', testngOptions.suiteName)
 
         assertEquals('Gradle test', testngOptions.testName)
+
+        assertEquals(TestNGOptions.DEFAULT_CONFIG_FAILURE_POLICY, testngOptions.configFailurePolicy)
     }
 
     @Test public void jdk14SourceCompatibilityAnnotationsDefaulting()
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java b/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
deleted file mode 100644
index 83e1342..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc;
-
-import org.gradle.external.javadoc.internal.GroupsJavadocOptionFileOption;
-import org.gradle.external.javadoc.internal.JavadocOptionFile;
-import org.gradle.external.javadoc.internal.LinksOfflineJavadocOptionFileOption;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.util.*;
-
-import static org.junit.Assert.*;
-
-public class StandardJavadocDocletOptionsTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private StandardJavadocDocletOptions options;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-
-        options = new StandardJavadocDocletOptions();
-    }
-
-    @Test
-    public void testDefaults() {
-        // core javadoc options
-        assertNull(options.getOverview());
-        assertNull(options.getMemberLevel());
-        assertNull(options.getDoclet());
-        assertEmpty(options.getDocletpath());
-        assertNull(options.getSource());
-        assertEmpty(options.getClasspath());
-        assertEmpty(options.getBootClasspath());
-        assertEmpty(options.getExtDirs());
-        assertEquals(options.getOutputLevel(), JavadocOutputLevel.QUIET);
-        assertFalse(options.isBreakIterator());
-        assertNull(options.getLocale());
-        assertNull(options.getEncoding());
-        assertEmpty(options.getJFlags());
-        assertEmpty(options.getSourceNames());
-        assertEmpty(options.getOptionFiles());
-        // standard doclet options
-        assertNull(options.getDestinationDirectory());
-        assertFalse(options.isUse());
-        assertFalse(options.isVersion());
-        assertFalse(options.isAuthor());
-        assertFalse(options.isSplitIndex());
-        assertNull(options.getWindowTitle());
-        assertNull(options.getDocTitle());
-        assertNull(options.getFooter());
-        assertNull(options.getBottom());
-        assertEmpty(options.getLinks());
-        assertEmpty(options.getLinksOffline());
-        assertFalse(options.isLinkSource());
-        assertEmpty(options.getGroups());
-        assertFalse(options.isNoDeprecated());
-        assertFalse(options.isNoDeprecatedList());
-        assertFalse(options.isNoSince());
-        assertFalse(options.isNoTree());
-        assertFalse(options.isNoIndex());
-        assertFalse(options.isNoHelp());
-        assertFalse(options.isNoNavBar());
-        assertNull(options.getHelpFile());
-        assertNull(options.getStylesheetFile());
-        assertFalse(options.isSerialWarn());
-        assertNull(options.getCharSet());
-        assertNull(options.getDocEncoding());
-        assertFalse(options.isKeyWords());
-        assertEmpty(options.getTags());
-        assertEmpty(options.getTaglets());
-        assertEmpty(options.getTagletPath());
-        assertFalse(options.isDocFilesSubDirs());
-        assertEmpty(options.getExcludeDocFilesSubDir());
-        assertEmpty(options.getNoQualifiers());
-        assertFalse(options.isNoTimestamp());
-        assertFalse(options.isNoComment());
-    }
-
-    @Test
-    public void testConstructor() {
-        final JavadocOptionFile optionFileMock = context.mock(JavadocOptionFile.class);
-
-        context.checking(new Expectations(){{
-            // core options
-            one(optionFileMock).addStringOption("overview");
-            one(optionFileMock).addEnumOption("memberLevel");
-            one(optionFileMock).addStringOption("doclet");
-            one(optionFileMock).addPathOption("docletclasspath");
-            one(optionFileMock).addStringOption("source");
-            one(optionFileMock).addPathOption("sourcepath");
-            one(optionFileMock).addPathOption("classpath");
-            one(optionFileMock).addStringsOption("subpackages", ";");
-            one(optionFileMock).addStringsOption("exclude", ":");
-            one(optionFileMock).addPathOption("bootclasspath");
-            one(optionFileMock).addPathOption("extdirs");
-            one(optionFileMock).addEnumOption("outputLevel", JavadocOutputLevel.QUIET);
-            one(optionFileMock).addBooleanOption("breakiterator");
-            one(optionFileMock).addStringOption("locale");
-            one(optionFileMock).addStringOption("encoding");
-            // standard doclet options
-            one(optionFileMock).addFileOption("d");
-            one(optionFileMock).addBooleanOption("use");
-            one(optionFileMock).addBooleanOption("version");
-            one(optionFileMock).addBooleanOption("author");
-            one(optionFileMock).addBooleanOption("splitindex");
-            one(optionFileMock).addStringOption("windowtitle");
-            one(optionFileMock).addStringOption("doctitle");
-            one(optionFileMock).addStringOption("footer");
-            one(optionFileMock).addStringOption("bottom");
-            one(optionFileMock).addStringOption("link");
-            allowing(optionFileMock).addOption(new LinksOfflineJavadocOptionFileOption("linkoffline"));
-            one(optionFileMock).addBooleanOption("linksource");
-            one(optionFileMock).addOption(new GroupsJavadocOptionFileOption("group"));
-            one(optionFileMock).addBooleanOption("nodeprecated");
-            one(optionFileMock).addBooleanOption("nodeprecatedlist");
-            one(optionFileMock).addBooleanOption("nosince");
-            one(optionFileMock).addBooleanOption("notree");
-            one(optionFileMock).addBooleanOption("noindex");
-            one(optionFileMock).addBooleanOption("nohelp");
-            one(optionFileMock).addBooleanOption("nonavbar");
-            one(optionFileMock).addFileOption("helpfile");
-            one(optionFileMock).addFileOption("stylesheetfile");
-            one(optionFileMock).addBooleanOption("serialwarn");
-            one(optionFileMock).addStringOption("charset");
-            one(optionFileMock).addStringOption("docencoding");
-            one(optionFileMock).addBooleanOption("keywords");
-            one(optionFileMock).addStringOption("tags");
-            one(optionFileMock).addStringOption("taglets");
-            one(optionFileMock).addPathOption("tagletpath");
-            one(optionFileMock).addBooleanOption("docfilessubdirs");
-            one(optionFileMock).addStringsOption("excludedocfilessubdir", ":");
-            one(optionFileMock).addStringsOption("noqualifier", ":");
-            one(optionFileMock).addBooleanOption("notimestamp");
-            one(optionFileMock).addBooleanOption("nocomment");
-        }});
-
-        options = new StandardJavadocDocletOptions();
-    }
-
-    @Test
-    public void testFluentOverview() {
-        final String overviewValue = "overview";
-        assertEquals(options, options.overview(overviewValue));
-        assertEquals(overviewValue, options.getOverview());
-    }
-
-    @Test
-    public void testShowAll() {
-        assertEquals(options, options.showAll());
-        assertEquals(JavadocMemberLevel.PRIVATE, options.getMemberLevel());
-    }
-
-    @Test
-    public void testShowFromPublic() {
-        assertEquals(options, options.showFromPublic());
-        assertEquals(JavadocMemberLevel.PUBLIC, options.getMemberLevel());
-    }
-
-    @Test
-    public void testShowFromPackage() {
-        assertEquals(options, options.showFromPackage());
-        assertEquals(JavadocMemberLevel.PACKAGE, options.getMemberLevel());
-    }
-
-    @Test
-    public void testShowFromProtected() {
-        assertEquals(options, options.showFromProtected());
-        assertEquals(JavadocMemberLevel.PROTECTED, options.getMemberLevel());
-    }
-
-    @Test
-    public void testShowFromPrivate() {
-        assertEquals(options, options.showFromPrivate());
-        assertEquals(JavadocMemberLevel.PRIVATE, options.getMemberLevel());
-    }
-
-    @Test
-    public void testFluentDocletClass() {
-        final String docletValue = "org.gradle.CustomDocletClass";
-        assertEquals(options, options.doclet(docletValue));
-        assertEquals(docletValue, options.getDoclet());
-    }
-
-    @Test
-    public void testFluentDocletClasspath() {
-        final File[] docletClasspathValue = new File[]{new File("doclet.jar"), new File("doclet-dep.jar")};
-        assertEquals(options, options.docletpath(docletClasspathValue));
-        assertArrayEquals(docletClasspathValue, options.getDocletpath().toArray());
-    }
-
-    @Test
-    public void testFluentSource() {
-        final String sourceValue = "1.5";
-        assertEquals(options, options.source(sourceValue));
-        assertEquals(sourceValue, options.getSource());
-    }
-
-    @Test
-    public void testFluentClasspath() {
-        final File[] classpathValue = new File[]{new File("classpath.jar"), new File("classpath-dir")};
-        assertEquals(options, options.classpath(classpathValue));
-        assertArrayEquals(classpathValue, options.getClasspath().toArray());
-    }
-
-    @Test
-    public void testFluentBootclasspath() {
-        final File[] bootClasspathValue = new File[]{new File("bootclasspath.jar"), new File("bootclasspath2.jar")};
-        assertEquals(options, options.bootClasspath(bootClasspathValue));
-        assertArrayEquals(bootClasspathValue, options.getBootClasspath().toArray());
-    }
-
-    @Test
-    public void testFluentExtDirs() {
-        final File[] extDirsValue = new File[]{new File("extDirOne"), new File("extDirTwo")};
-        assertEquals(options, options.extDirs(extDirsValue));
-        assertArrayEquals(extDirsValue, options.getExtDirs().toArray());
-    }
-
-    @Test
-    public void testQuietOutputLevel() {
-        assertEquals(options, options.quiet());
-        assertEquals(JavadocOutputLevel.QUIET, options.getOutputLevel());
-    }
-
-    @Test
-    public void testVerboseOutputLevel() {
-        assertEquals(options, options.verbose());
-        assertEquals(JavadocOutputLevel.VERBOSE, options.getOutputLevel());
-        assertTrue(options.isVerbose());
-    }
-
-    @Test
-    public void testFluentBreakIterator() {
-        assertEquals(options, options.breakIterator());
-        assertTrue(options.isBreakIterator());
-    }
-
-    @Test
-    public void testFluentLocale() {
-        final String localeValue = "nl";
-        assertEquals(options, options.locale(localeValue));
-        assertEquals(localeValue, options.getLocale());
-    }
-
-    @Test
-    public void testFluentEncoding() {
-        final String encodingValue = "UTF-8";
-        assertEquals(options, options.encoding(encodingValue));
-        assertEquals(encodingValue, options.getEncoding());
-    }
-
-    @Test
-    public void testFluentDirectory() {
-        final File directoryValue = new File("testOutput");
-        assertEquals(options, options.destinationDirectory(directoryValue));
-        assertEquals(directoryValue, options.getDestinationDirectory());
-    }
-
-    @Test
-    public void testFluentUse() {
-        assertEquals(options, options.use());
-        assertTrue(options.isUse());
-    }
-
-    @Test
-    public void testFluentVersion() {
-        assertEquals(options, options.version());
-        assertTrue(options.isVersion());
-    }
-
-    @Test
-    public void testFluentAuthor() {
-        assertEquals(options, options.author());
-        assertTrue(options.isAuthor());
-    }
-
-    @Test
-    public void testFluentSplitIndex() {
-        assertEquals(options, options.splitIndex());
-        assertTrue(options.isSplitIndex());
-    }
-
-    @Test
-    public void testFluentWindowTitle() {
-        final String windowTitleValue = "windowTitleValue";
-        assertEquals(options, options.windowTitle(windowTitleValue));
-        assertEquals(windowTitleValue, options.getWindowTitle());
-    }
-
-    @Test
-    public void testFluentDocTitle() {
-        final String docTitleValue = "docTitleValue";
-        assertEquals(options, options.docTitle(docTitleValue));
-        assertEquals(docTitleValue, options.getDocTitle());
-    }
-
-    @Test
-    public void testFluentFooter() {
-        final String footerValue = "footerValue";
-        assertEquals(options, options.footer(footerValue));
-        assertEquals(footerValue, options.getFooter());
-    }
-
-    @Test
-    public void testFluentBottom() {
-        final String bottomValue = "bottomValue";
-        assertEquals(options, options.bottom(bottomValue));
-        assertEquals(bottomValue, options.getBottom());
-    }
-
-    @Test
-    public void testFluentLink() {
-        final String[] linkValue = new String[]{"http://otherdomain.org/javadoc"};
-        assertEquals(options, options.links(linkValue));
-        assertArrayEquals(linkValue, options.getLinks().toArray());
-    }
-
-    @Test
-    public void testFluentLinkOffline() {
-        final String extDocUrl = "http://otherdomain.org/javadoc";
-        final String packageListLoc = "/home/someuser/used-lib-local-javadoc-list";
-        assertEquals(options, options.linksOffline(extDocUrl, packageListLoc));
-        assertEquals(extDocUrl, options.getLinksOffline().get(0).getExtDocUrl());
-        assertEquals(packageListLoc, options.getLinksOffline().get(0).getPackagelistLoc());
-    }
-
-    @Test
-    public void testFluentLinkSource() {
-        assertEquals(options, options.linkSource());
-        assertTrue(options.isLinkSource());
-    }
-
-    @Test
-    public void testFluentGroup() {
-        final String groupOneName = "groupOneName";
-        final String[] groupOnePackages = new String[]{"java.lang", "java.io"};
-
-        final String groupTwoName = "gradle";
-        final String[] groupTwoPackages = new String[]{"org.gradle"};
-
-        assertEquals(options, options.group(groupOneName, groupOnePackages));
-        assertEquals(options, options.group(groupTwoName, groupTwoPackages));
-        assertEquals(2, options.getGroups().size());
-        assertArrayEquals(groupOnePackages, options.getGroups().get(groupOneName).toArray());
-        assertArrayEquals(groupTwoPackages, options.getGroups().get(groupTwoName).toArray());
-    }
-
-    @Test
-    public void testFluentNoDeprecated() {
-        assertEquals(options, options.noDeprecated());
-        assertTrue(options.isNoDeprecated());
-    }
-
-    @Test
-    public void testFluentNoDeprecatedList() {
-        assertEquals(options, options.noDeprecatedList());
-        assertTrue(options.isNoDeprecatedList());
-    }
-
-    @Test
-    public void testFluentNoSince() {
-        assertEquals(options, options.noSince());
-        assertTrue(options.isNoSince());
-    }
-
-    @Test
-    public void testFluentNoTree() {
-        assertEquals(options, options.noTree());
-        assertTrue(options.isNoTree());
-    }
-
-    @Test
-    public void testFluentNoIndex() {
-        assertEquals(options, options.noIndex());
-        assertTrue(options.isNoIndex());
-    }
-
-    @Test
-    public void testFluentNoNavBar() {
-        assertEquals(options, options.noNavBar());
-        assertTrue(options.isNoNavBar());
-    }
-
-    @Test
-    public void testFluentHelpFile() {
-        final File helpFileValue = new File("help-file.txt");
-        assertEquals(options, options.helpFile(helpFileValue));
-        assertEquals(helpFileValue, options.getHelpFile());
-    }
-
-    @Test
-    public void testFluentStylesheetFile() {
-        final File stylesheetFileValue = new File("stylesheet.css");
-        assertEquals(options, options.stylesheetFile(stylesheetFileValue));
-        assertEquals(stylesheetFileValue, options.getStylesheetFile());
-    }
-
-    @Test
-    public void testFluentSerialWarn() {
-        assertEquals(options, options.serialWarn());
-        assertTrue(options.isSerialWarn());
-    }
-
-    @Test
-    public void testFluentCharset() {
-        final String charsetValue = "dummy-charset";
-        assertEquals(options, options.charSet(charsetValue));
-        assertEquals(charsetValue, options.getCharSet());
-    }
-
-    @Test
-    public void testFluentDocEncoding() {
-        final String docEncodingValue = "UTF-16";
-        assertEquals(options, options.docEncoding(docEncodingValue));
-        assertEquals(docEncodingValue, options.getDocEncoding());
-    }
-
-    @Test
-    public void testFluentKeywords() {
-        assertEquals(options, options.keyWords());
-        assertTrue(options.isKeyWords());
-    }
-
-    @Test
-    public void testFluentTags() {
-        final String[] tagsValue = new String[]{"param", "return", "todo:a:\"To Do:\""};
-
-        final List<String> tempList = new ArrayList<String>();
-        tempList.addAll(Arrays.asList(tagsValue));
-
-        final Object[] totalTagsValue = tempList.toArray();
-        assertEquals(options, options.tags(tagsValue));
-        assertArrayEquals(totalTagsValue, options.getTags().toArray());
-    }
-
-    @Test
-    public void testFluentTaglets() {
-        final String[] tagletsValue = new String[]{"com.sun.tools.doclets.ToDoTaglet"};
-
-        final List<String> tempList = new ArrayList<String>();
-        tempList.addAll(Arrays.asList(tagletsValue));
-
-        final Object[] totalTagletsValue = tempList.toArray();
-        assertEquals(options, options.taglets(tagletsValue));
-        assertArrayEquals(totalTagletsValue, options.getTaglets().toArray());
-    }
-
-    @Test
-    public void testFluentTagletPath() {
-        final File[] tagletPathValue = new File[]{new File("tagletOne.jar"), new File("tagletTwo.jar")};
-        assertEquals(options, options.tagletPath(tagletPathValue));
-        assertArrayEquals(tagletPathValue, options.getTagletPath().toArray());
-    }
-
-    @Test
-    public void testFluentDocFilesSubDirs() {
-        assertEquals(options, options.docFilesSubDirs());
-        assertTrue(options.isDocFilesSubDirs());
-    }
-
-    @Test
-    public void testFluentExcludeDocFilesSubDir() {
-        final String[] excludeDocFilesSubDirValue = new String[]{".hg", ".svn", ".bzr", ".git"};
-        assertEquals(options, options.excludeDocFilesSubDir(excludeDocFilesSubDirValue));
-        assertArrayEquals(excludeDocFilesSubDirValue, options.getExcludeDocFilesSubDir().toArray());
-    }
-
-    @Test
-    public void testFluentNoQualifier() {
-        String[] noQualifierValue = new String[]{"java.lang", "java.io"};
-        assertEquals(options, options.noQualifiers(noQualifierValue));
-        assertArrayEquals(noQualifierValue, options.getNoQualifiers().toArray());
-    }
-
-    @Test
-    public void testFluentNoTimestamp() {
-        assertEquals(options, options.noTimestamp());
-        assertTrue(options.isNoTimestamp());
-    }
-
-    @Test
-    public void testFluentNoComment() {
-        assertEquals(options, options.noComment());
-        assertTrue(options.isNoComment());
-    }
-
-    @After
-    public void tearDown() {
-        options = null;
-    }
-
-    public static void assertEmpty(Collection shouldBeEmptyCollection) {
-        assertNotNull(shouldBeEmptyCollection);
-        assertTrue(shouldBeEmptyCollection.isEmpty());
-    }
-
-    public static void assertEmpty(Map shouldBeEmptyMap) {
-        assertNotNull(shouldBeEmptyMap);
-        assertTrue(shouldBeEmptyMap.isEmpty());
-    }
-}
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java b/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java
deleted file mode 100644
index 65a5eee..0000000
--- a/subprojects/plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.internal;
-
-import org.gradle.util.WrapUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-
-public class JavadocOptionFileWriterContextTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private BufferedWriter bufferedWriterMock;
-
-    private JavadocOptionFileWriterContext writerContext;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        bufferedWriterMock = context.mock(BufferedWriter.class);
-
-        writerContext = new JavadocOptionFileWriterContext(bufferedWriterMock);
-    }
-
-    @Test
-    public void testWrite() throws IOException {
-        final String writeValue = "dummy";
-
-        context.checking(new Expectations() {{
-            one(bufferedWriterMock).write(writeValue);
-        }});
-
-        writerContext.write(writeValue);
-    }
-
-    @Test
-    public void testNewLine() throws IOException {
-        context.checking(new Expectations() {{
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.newLine();
-    }
-
-    @Test
-    public void quotesAndEscapesOptionValue() throws IOException {
-        context.checking(new Expectations(){{
-            one(bufferedWriterMock).write("-");
-            one(bufferedWriterMock).write("key");
-            one(bufferedWriterMock).write(" ");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).write("1\\\\2\\\\");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.writeValueOption("key", "1\\2\\");
-    }
-
-    @Test
-    public void quotesAndEscapesOptionValues() throws IOException {
-        context.checking(new Expectations(){{
-            one(bufferedWriterMock).write("-");
-            one(bufferedWriterMock).write("key");
-            one(bufferedWriterMock).write(" ");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).write("a\\\\b:c");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.writeValuesOption("key", WrapUtil.toList("a\\b", "c"), ":");
-    }
-}
diff --git a/subprojects/publish/publish.gradle b/subprojects/publish/publish.gradle
index a212189..e914e6c 100644
--- a/subprojects/publish/publish.gradle
+++ b/subprojects/publish/publish.gradle
@@ -16,7 +16,7 @@
 
 dependencies {
     compile project(':core')
-    compile project(':coreImpl')
+    compile project(':dependencyManagement')
 
     testCompile libraries.groovy
 
diff --git a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublicationFieldValidator.java b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublicationFieldValidator.java
index b80d949..7821981 100644
--- a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublicationFieldValidator.java
+++ b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublicationFieldValidator.java
@@ -51,6 +51,14 @@ public abstract class PublicationFieldValidator<T extends PublicationFieldValida
         if (value == null || value.length() == 0) {
             return type.cast(this);
         }
+        doesNotContainSpecialCharacters(false);
+        return type.cast(this);
+    }
+
+    public T doesNotContainSpecialCharacters(boolean allowSlash) {
+        if (value == null || value.length() == 0) {
+            return type.cast(this);
+        }
         // Iterate over unicode characters
         int offset = 0;
         while (offset < value.length()) {
@@ -58,7 +66,7 @@ public abstract class PublicationFieldValidator<T extends PublicationFieldValida
             if (Character.isISOControl(unicodeChar)) {
                 throw failure(String.format("%s cannot contain ISO control character '\\u%04x'.", name, unicodeChar));
             }
-            if ('\\' == unicodeChar || '/' == unicodeChar) {
+            if ('\\' == unicodeChar || ('/' == unicodeChar && !allowSlash)) {
                 throw failure(String.format("%s cannot contain '%c'.", name, (char) unicodeChar));
             }
             offset += Character.charCount(unicodeChar);
diff --git a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishOperation.java b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishOperation.java
index bd94b3d..0559700 100644
--- a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishOperation.java
+++ b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishOperation.java
@@ -17,15 +17,14 @@
 package org.gradle.api.publish.internal;
 
 import org.gradle.api.artifacts.PublishException;
-import org.gradle.api.artifacts.repositories.ArtifactRepository;
 import org.gradle.api.publish.Publication;
 
 public abstract class PublishOperation implements Runnable {
 
     private final Publication publication;
-    private final ArtifactRepository repository;
+    private final String repository;
 
-    protected PublishOperation(Publication publication, ArtifactRepository repository) {
+    protected PublishOperation(Publication publication, String repository) {
         this.publication = publication;
         this.repository = repository;
     }
@@ -36,7 +35,7 @@ public abstract class PublishOperation implements Runnable {
         try {
             publish();
         } catch (Exception e) {
-            throw new PublishException(String.format("Failed to publish publication '%s' to repository '%s'", publication.getName(), repository.getName()), e);
+            throw new PublishException(String.format("Failed to publish publication '%s' to repository '%s'", publication.getName(), repository), e);
         }
     }
 }
diff --git a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishServices.java b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishServices.java
index 615cfaf..6660c16 100644
--- a/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishServices.java
+++ b/subprojects/publish/src/main/groovy/org/gradle/api/publish/internal/PublishServices.java
@@ -27,6 +27,9 @@ public class PublishServices implements PluginServiceRegistry {
         registration.add(ProjectDependencyPublicationResolver.class);
     }
 
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
     public void registerProjectServices(ServiceRegistration registration) {
     }
 }
diff --git a/subprojects/publish/src/main/groovy/org/gradle/api/publish/plugins/PublishingPlugin.java b/subprojects/publish/src/main/groovy/org/gradle/api/publish/plugins/PublishingPlugin.java
index dd5c5da..5160f36 100644
--- a/subprojects/publish/src/main/groovy/org/gradle/api/publish/plugins/PublishingPlugin.java
+++ b/subprojects/publish/src/main/groovy/org/gradle/api/publish/plugins/PublishingPlugin.java
@@ -16,11 +16,16 @@
 
 package org.gradle.api.publish.plugins;
 
-import org.gradle.api.*;
+import org.gradle.api.Incubating;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.internal.artifacts.ArtifactPublicationServices;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublication;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.publish.Publication;
 import org.gradle.api.publish.PublicationContainer;
 import org.gradle.api.publish.PublishingExtension;
@@ -28,9 +33,11 @@ import org.gradle.api.publish.internal.DefaultPublicationContainer;
 import org.gradle.api.publish.internal.DefaultPublishingExtension;
 import org.gradle.api.publish.internal.PublicationInternal;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.model.ModelPath;
-import org.gradle.model.ModelRule;
-import org.gradle.model.ModelRules;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.model.collection.CollectionBuilder;
 
 import javax.inject.Inject;
 
@@ -46,16 +53,12 @@ public class PublishingPlugin implements Plugin<Project> {
     public static final String PUBLISH_LIFECYCLE_TASK_NAME = "publish";
 
     private final Instantiator instantiator;
-    private final ModelRules modelRules;
     private final ArtifactPublicationServices publicationServices;
-    private final ProjectPublicationRegistry publicationRegistry;
 
     @Inject
-    public PublishingPlugin(ArtifactPublicationServices publicationServices, Instantiator instantiator, ModelRules modelRules, ProjectPublicationRegistry publicationRegistry) {
+    public PublishingPlugin(ArtifactPublicationServices publicationServices, Instantiator instantiator) {
         this.publicationServices = publicationServices;
         this.instantiator = instantiator;
-        this.modelRules = modelRules;
-        this.publicationRegistry = publicationRegistry;
     }
 
     public void apply(final Project project) {
@@ -63,29 +66,36 @@ public class PublishingPlugin implements Plugin<Project> {
         PublicationContainer publications = instantiator.newInstance(DefaultPublicationContainer.class, instantiator);
 
         // TODO Registering an extension should register it with the model registry as well
-        final PublishingExtension extension = project.getExtensions().create(PublishingExtension.NAME, DefaultPublishingExtension.class, repositories, publications);
+        project.getExtensions().create(PublishingExtension.NAME, DefaultPublishingExtension.class, repositories, publications);
 
-        project.afterEvaluate(new Action<Project>() {
-            public void execute(Project project) {
-                for (Publication publication : extension.getPublications()) {
-                    PublicationInternal internalPublication = (PublicationInternal) publication;
-                    publicationRegistry.registerPublication(project.getPath(), new DefaultProjectPublication(internalPublication.getCoordinates()));
-                }
-            }
-        });
+        Task publishLifecycleTask = project.getTasks().create(PUBLISH_LIFECYCLE_TASK_NAME);
+        publishLifecycleTask.setDescription("Publishes all publications produced by this project.");
+        publishLifecycleTask.setGroup(PUBLISH_TASK_GROUP);
+    }
 
-        ModelPath extensionModelPath = ModelPath.path(PublishingExtension.NAME);
+    static class Rules extends RuleSource {
+        @Model
+        PublishingExtension publishing(ExtensionContainer extensions) {
+            return extensions.getByType(PublishingExtension.class);
+        }
 
-        modelRules.register(extensionModelPath.toString(), extension);
+        @Model
+        ProjectPublicationRegistry projectPublicationRegistry(ServiceRegistry serviceRegistry) {
+            return serviceRegistry.get(ProjectPublicationRegistry.class);
+        }
 
-        modelRules.rule(new ModelRule() {
-            public void triggerDeferredConfigurables(PublishingExtension publishingExtension) {
-                project.getExtensions().getByType(DefaultPublishingExtension.class);
+        @Mutate
+        void addConfiguredPublicationsToProjectPublicationRegistry(ProjectPublicationRegistry projectPublicationRegistry, PublishingExtension extension, ProjectIdentifier projectIdentifier) {
+            for (Publication publication : extension.getPublications()) {
+                PublicationInternal internalPublication = (PublicationInternal) publication;
+                projectPublicationRegistry.registerPublication(projectIdentifier.getPath(), new DefaultProjectPublication(internalPublication.getCoordinates()));
             }
-        });
+        }
 
-        Task publishLifecycleTask = project.getTasks().create(PUBLISH_LIFECYCLE_TASK_NAME);
-        publishLifecycleTask.setDescription("Publishes all publications produced by this project.");
-        publishLifecycleTask.setGroup(PUBLISH_TASK_GROUP);
+        @Mutate
+        void tasksDependOnProjectPublicationRegistry(CollectionBuilder<Task> tasks, ProjectPublicationRegistry publicationRegistry) {
+            //do nothing, the rule is here to introduce a dependency on ProjectPublicationRegistry to TaskContainer
+        }
     }
+
 }
diff --git a/subprojects/publish/src/main/resources/META-INF/gradle-plugins/publishing.properties b/subprojects/publish/src/main/resources/META-INF/gradle-plugins/org.gradle.publishing.properties
similarity index 100%
rename from subprojects/publish/src/main/resources/META-INF/gradle-plugins/publishing.properties
rename to subprojects/publish/src/main/resources/META-INF/gradle-plugins/org.gradle.publishing.properties
diff --git a/subprojects/publish/src/test/groovy/org/gradle/api/publish/internal/ProjectDependencyPublicationResolverTest.groovy b/subprojects/publish/src/test/groovy/org/gradle/api/publish/internal/ProjectDependencyPublicationResolverTest.groovy
index 2fa9981..18381ac 100644
--- a/subprojects/publish/src/test/groovy/org/gradle/api/publish/internal/ProjectDependencyPublicationResolverTest.groovy
+++ b/subprojects/publish/src/test/groovy/org/gradle/api/publish/internal/ProjectDependencyPublicationResolverTest.groovy
@@ -28,7 +28,7 @@ public class ProjectDependencyPublicationResolverTest extends Specification {
     def project = Mock(ProjectInternal)
     def extensions = Mock(ExtensionContainerInternal)
     def publishing = Mock(PublishingExtension)
-    def publications = new DefaultPublicationContainer(new DirectInstantiator())
+    def publications = new DefaultPublicationContainer(DirectInstantiator.INSTANCE)
     def publication = Mock(PublicationInternal)
 
     def "resolves project coordinates if project does not have publishing extension"() {
diff --git a/subprojects/publish/src/test/groovy/org/gradle/api/publish/plugins/PublishingPluginTest.groovy b/subprojects/publish/src/test/groovy/org/gradle/api/publish/plugins/PublishingPluginTest.groovy
index 4a0dbd2..864b53f 100644
--- a/subprojects/publish/src/test/groovy/org/gradle/api/publish/plugins/PublishingPluginTest.groovy
+++ b/subprojects/publish/src/test/groovy/org/gradle/api/publish/plugins/PublishingPluginTest.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.api.publish.plugins
 
-import org.gradle.api.Project
 import org.gradle.api.artifacts.dsl.RepositoryHandler
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.publish.PublicationContainer
 import org.gradle.api.publish.PublishingExtension
 import org.gradle.api.publish.internal.PublicationInternal
@@ -26,11 +26,11 @@ import spock.lang.Specification
 
 class PublishingPluginTest extends Specification {
 
-    Project project = TestUtil.createRootProject()
+    DefaultProject project = TestUtil.createRootProject()
     PublishingExtension extension
 
     def setup() {
-        project.plugins.apply(PublishingPlugin)
+        project.pluginManager.apply(PublishingPlugin)
         extension = project.extensions.getByType(PublishingExtension)
     }
 
diff --git a/subprojects/reporting/reporting.gradle b/subprojects/reporting/reporting.gradle
index 7b21d94..ad0c7c2 100644
--- a/subprojects/reporting/reporting.gradle
+++ b/subprojects/reporting/reporting.gradle
@@ -1,3 +1,13 @@
+apply plugin: 'javascript-base'
+
+configurations {
+    reports
+}
+
+repositories {
+    javaScript.googleApis()
+}
+
 dependencies {
     compile libraries.groovy
     compile project(':core')
@@ -6,6 +16,14 @@ dependencies {
     testCompile libraries.jsoup
     integTestRuntime project(':codeQuality')
     integTestRuntime project(':jacoco')
+
+    reports "jquery:jquery.min:1.11.0 at js"
+}
+
+task reportResources(type: Copy) {
+    from configurations.reports
+    into "$generatedResourcesDir/org/gradle/reporting"
 }
+sourceSets.main.output.dir generatedResourcesDir, builtBy: reportResources
 
-useTestFixtures()
\ No newline at end of file
+useTestFixtures()
diff --git a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/internal/TaskReportContainerIntegTest.groovy b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/internal/TaskReportContainerIntegTest.groovy
index a27053e..3fc8430 100644
--- a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/internal/TaskReportContainerIntegTest.groovy
+++ b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/internal/TaskReportContainerIntegTest.groovy
@@ -17,6 +17,8 @@
 package org.gradle.api.reporting.internal
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
 
 class TaskReportContainerIntegTest extends AbstractIntegrationSpec {
     
@@ -67,7 +69,8 @@ class TaskReportContainerIntegTest extends AbstractIntegrationSpec {
             }
         """ 
     }
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "task up to date when no reporting configuration change"() {
         expect:
         succeeds(task) && task in nonSkippedTasks
@@ -76,6 +79,7 @@ class TaskReportContainerIntegTest extends AbstractIntegrationSpec {
         succeeds(task) && task in skippedTasks
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "task not up to date when enabled set changes"() {
         expect:
         succeeds(task) && task in nonSkippedTasks
@@ -89,6 +93,7 @@ class TaskReportContainerIntegTest extends AbstractIntegrationSpec {
         succeeds(task) && task in nonSkippedTasks
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "task not up to date when enabled set changes but output files stays the same"() {
         given:
         buildFile << """
diff --git a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
index 6ce2f9d..8e6c793 100644
--- a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
+++ b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
@@ -17,9 +17,11 @@
 package org.gradle.api.reporting.plugins
 
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
+import spock.lang.IgnoreIf
 
 class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
 
@@ -44,7 +46,7 @@ class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
         buildFile << """
             allprojects {
                 dependencies{
-                    testCompile "junit:junit:4.11"
+                    testCompile "junit:junit:4.12"
                 }
             }
 """
@@ -104,10 +106,6 @@ class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
         file('settings.gradle') << "include 'subproject'"
     }
 
-    String getPluginId() {
-        'build-dashboard'
-    }
-
     String getMainTask() {
         'buildDashboard'
     }
@@ -242,6 +240,7 @@ class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
         !buildDashboardFile.exists()
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void 'buildDashboard is incremental'() {
         given:
         goodCode()
@@ -257,6 +256,7 @@ class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
         run('buildDashboard') && ':buildDashboard' in nonSkippedTasks
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void 'enabling an additional report renders buildDashboard out-of-date'() {
         given:
         goodCode()
@@ -287,6 +287,7 @@ class BuildDashboardPluginIntegrationTest extends WellBehavedPluginTest {
         hasReport(':codenarcMain', 'text')
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     void 'generating a report that was previously not available renders buildDashboard out-of-date'() {
         given:
         goodCode()
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePlugin.java b/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePlugin.java
index 1da1d3f..3e141d8 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePlugin.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePlugin.java
@@ -22,20 +22,16 @@ import org.gradle.api.reporting.ReportingExtension;
 /**
  * <p>A {@link Plugin} which provides the basic skeleton for reporting.</p>
  *
- * <p>This plugin adds the following convention objects to the project:</p>
+ * <p>This plugin adds the following extension objects to the project:</p>
  *
  * <ul>
  *
- * <li>{@link ReportingBasePluginConvention}</li>
+ * <li>{@link org.gradle.api.reporting.ReportingExtension}</li>
  *
  * </ul>
  */
 public class ReportingBasePlugin implements Plugin<ProjectInternal> {
     public void apply(ProjectInternal project) {
-        Convention convention = project.getConvention();
-        ReportingExtension extension = project.getExtensions().create(ReportingExtension.NAME, ReportingExtension.class, project);
-
-        // This convention is deprecated
-        convention.getPlugins().put("reportingBase", new ReportingBasePluginConvention(project, extension));
+        project.getExtensions().create(ReportingExtension.NAME, ReportingExtension.class, project);
     }
 }
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java b/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java
deleted file mode 100644
index bab1362..0000000
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins;
-
-import org.gradle.api.internal.file.FileLookup;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.reporting.ReportingExtension;
-import org.gradle.util.DeprecationLogger;
-
-import java.io.File;
-import java.util.concurrent.Callable;
-
-/**
- * <p>A {@code BasePluginConvention} defines the convention properties and methods used by the {@link
- * ReportingBasePlugin}.</p>
- * <p>
- * This convention has been deprecated. Use the reporting extension instead:
- * </p>
- * <pre>
- * reporting {
- *     baseDir "the-reports"
- * }
- * </pre>
- *
- * @deprecated This convention has been deprecated and replaced by {@link ReportingExtension}
- */
- at Deprecated
-public class ReportingBasePluginConvention {
-    private ReportingExtension extension;
-    private final ProjectInternal project;
-
-    public ReportingBasePluginConvention(ProjectInternal project, ReportingExtension extension) {
-        this.project = project;
-        this.extension = extension;
-    }
-
-    /**
-     * Returns the name of the reports directory, relative to the project's build directory.
-     *
-     * @deprecated use {@link org.gradle.api.reporting.ReportingExtension#getBaseDir()}
-     * @return The reports directory name. Never returns null.
-     */
-    @Deprecated
-    public String getReportsDirName() {
-        DeprecationLogger.nagUserOfReplacedProperty("reportsDirName", "reporting.baseDir");
-        return extension.getBaseDir().getName();
-    }
-
-    /**
-     * Sets the name of the reports directory, relative to the project's build directory.
-     *
-     * @deprecated use {@link ReportingExtension#setBaseDir(Object)}
-     * @param reportsDirName The reports directory name. Should not be null.
-     */
-    @Deprecated
-    public void setReportsDirName(final String reportsDirName) {
-        DeprecationLogger.nagUserOfReplacedProperty("reportsDirName", "reporting.baseDir");
-        extension.setBaseDir(new Callable<File>() {
-            public File call() throws Exception {
-                return project.getServices().get(FileLookup.class).getFileResolver(project.getBuildDir()).resolve(reportsDirName);
-            }
-        });
-    }
-
-    /**
-     * Returns the directory containing all reports for this project.
-     *
-     * @deprecated use {@link org.gradle.api.reporting.ReportingExtension#getBaseDir()}
-     * @return The reports directory. Never returns null.
-     */
-    @Deprecated
-    public File getReportsDir() {
-        DeprecationLogger.nagUserOfReplacedProperty("reportsDir", "reporting.baseDir");
-        return extension.getBaseDir();
-    }
-
-    /**
-     * Returns the title for API documentation for the project.
-     *
-     * @deprecated use {@link org.gradle.api.reporting.ReportingExtension#getApiDocTitle()}
-     * @return The title. Never returns null.
-     */
-    @Deprecated
-    public String getApiDocTitle() {
-        DeprecationLogger.nagUserOfReplacedProperty("apiDocTitle", "reporting.apiDocTitle");
-        return extension.getApiDocTitle();
-    }
-}
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/BuildDashboardReports.java b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/BuildDashboardReports.java
index aa3a57b..6464a90 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/BuildDashboardReports.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/BuildDashboardReports.java
@@ -19,7 +19,7 @@ package org.gradle.api.reporting;
 import org.gradle.api.Incubating;
 
 /**
- * The reporting configuration for the the {@link GenerateBuildDashboard} task.
+ * The reporting configuration for the {@link GenerateBuildDashboard} task.
  */
 @Incubating
 public interface BuildDashboardReports extends ReportContainer<Report> {
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/GenerateBuildDashboard.java b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/GenerateBuildDashboard.java
index 083e372..2a0aa13 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/GenerateBuildDashboard.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/GenerateBuildDashboard.java
@@ -47,12 +47,16 @@ public class GenerateBuildDashboard extends DefaultTask implements Reporting<Bui
     @Nested
     private final DefaultBuildDashboardReports reports;
 
-    @Inject
-    public GenerateBuildDashboard(Instantiator instantiator) {
-        reports = instantiator.newInstance(DefaultBuildDashboardReports.class, this);
+    public GenerateBuildDashboard() {
+        reports = getInstantiator().newInstance(DefaultBuildDashboardReports.class, this);
         reports.getHtml().setEnabled(true);
     }
 
+    @Inject
+    protected Instantiator getInstantiator() {
+        throw new UnsupportedOperationException();
+    }
+
     @Input
     public Set<ReportState> getInputReports() {
         Set<ReportState> inputs = new HashSet<ReportState>();
@@ -124,8 +128,8 @@ public class GenerateBuildDashboard extends DefaultTask implements Reporting<Bui
     @TaskAction
     void run() {
         if (getReports().getHtml().isEnabled()) {
-            BuildDashboardGenerator generator = new BuildDashboardGenerator(getEnabledInputReports(), reports.getHtml().getEntryPoint());
-            generator.generate();
+            BuildDashboardGenerator generator = new BuildDashboardGenerator();
+            generator.render(getEnabledInputReports(), reports.getHtml().getEntryPoint());
         } else {
             setDidWork(false);
         }
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/BuildDashboardGenerator.java b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/BuildDashboardGenerator.java
index b5d0034..983fd56 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/BuildDashboardGenerator.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/BuildDashboardGenerator.java
@@ -17,22 +17,25 @@
 package org.gradle.api.reporting.internal;
 
 import com.googlecode.jatl.Html;
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.reporting.DirectoryReport;
 import org.gradle.api.reporting.Report;
+import org.gradle.reporting.HtmlPageBuilder;
+import org.gradle.reporting.HtmlReportRenderer;
+import org.gradle.reporting.ReportRenderer;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.GradleVersion;
 
-import java.io.*;
-import java.util.Comparator;
-import java.util.Set;
-import java.util.TreeSet;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.*;
 
-public class BuildDashboardGenerator {
+public class BuildDashboardGenerator extends ReportRenderer<Collection<Report>, File> {
     private Set<Report> reports;
     private File outputFile;
 
-    public BuildDashboardGenerator(Set<Report> reports, File outputFile) {
+    @Override
+    public void render(Collection<Report> reports, final File outputFile) {
         this.reports = new TreeSet<Report>(new Comparator<Report>() {
             public int compare(Report o1, Report o2) {
                 return o1.getDisplayName().compareTo(o2.getDisplayName());
@@ -40,35 +43,26 @@ public class BuildDashboardGenerator {
         });
         this.reports.addAll(reports);
         this.outputFile = outputFile;
-    }
 
-    public void generate() {
-        try {
-            GFileUtils.parentMkdirs(outputFile);
-            Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8"));
-            try {
-                generate(writer);
-            } finally {
-                writer.close();
+        HtmlReportRenderer renderer = new HtmlReportRenderer();
+        renderer.renderRawSinglePage(reports, new ReportRenderer<Collection<Report>, HtmlPageBuilder<Writer>>() {
+            @Override
+            public void render(Collection<Report> model, HtmlPageBuilder<Writer> builder) throws IOException {
+                generate(builder);
             }
-            copyCss();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
+        }, outputFile);
     }
 
-    private void copyCss() {
-        GFileUtils.copyURLToFile(getClass().getResource("/org/gradle/reporting/base-style.css"), new File(outputFile.getParent(), "base-style.css"));
-        GFileUtils.copyURLToFile(getClass().getResource("style.css"), new File(outputFile.getParent(), "style.css"));
-    }
-
-    private void generate(Writer writer) {
-        new Html(writer) {{
+    private void generate(final HtmlPageBuilder<Writer> builder) {
+        final String baseCssLink = builder.requireResource(getClass().getResource("/org/gradle/reporting/base-style.css"));
+        final String cssLink = builder.requireResource(getClass().getResource("style.css"));
+        new Html(builder.getOutput()) {{
             html();
                 head();
                     meta().httpEquiv("Content-Type").content("text/html; charset=utf-8");
-                    link().rel("stylesheet").type("text/css").href("base-style.css").end();
-                    link().rel("stylesheet").type("text/css").href("style.css").end();
+                    meta().httpEquiv("x-ua-compatible").content("IE=edge");
+                    link().rel("stylesheet").type("text/css").href(baseCssLink).end();
+                    link().rel("stylesheet").type("text/css").href(cssLink).end();
                     title().text("Build dashboard").end();
                 end();
                 body();
@@ -89,16 +83,22 @@ public class BuildDashboardGenerator {
                     } else {
                         h1().text("There are no build reports available.").end();
                     }
+                    div().id("footer");
+                        p();
+                            text("Generated by ");
+                            a().href("http://www.gradle.org").text(GradleVersion.current().toString()).end();
+                            text(String.format(" at %s", builder.formatDate(new Date())));
+                        end();
+                    end();
                 end();
-                div().id("footer").text(String.format("Generated by %s", GradleVersion.current()));
             endAll();
         }};
     }
 
     private File getHtmlLinkedFileFromReport(Report report) {
-        if(report instanceof DirectoryReport){
+        if (report instanceof DirectoryReport) {
             return ((DirectoryReport) report).getEntryPoint();
-        } else{
+        } else {
             return report.getDestination();
         }
     }
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/DefaultReportContainer.java b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/DefaultReportContainer.java
index 44574d9..13e76c3 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/DefaultReportContainer.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/internal/DefaultReportContainer.java
@@ -74,6 +74,7 @@ public class DefaultReportContainer<T extends Report> extends DefaultNamedDomain
         }
 
         getStore().add(report);
+        index();
         return report;
     }
 }
diff --git a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/plugins/BuildDashboardPlugin.java b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/plugins/BuildDashboardPlugin.java
index cece2c6..621cba4 100644
--- a/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/plugins/BuildDashboardPlugin.java
+++ b/subprojects/reporting/src/main/groovy/org/gradle/api/reporting/plugins/BuildDashboardPlugin.java
@@ -19,7 +19,6 @@ package org.gradle.api.reporting.plugins;
 import org.gradle.api.*;
 import org.gradle.api.internal.ConventionMapping;
 import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.plugins.ReportingBasePlugin;
 import org.gradle.api.reporting.DirectoryReport;
 import org.gradle.api.reporting.GenerateBuildDashboard;
@@ -29,17 +28,19 @@ import org.gradle.api.reporting.ReportingExtension;
 import java.util.concurrent.Callable;
 
 /**
- * Adds a task, "buildDashboard", that aggregates the output of all tasks executed during the build that produce reports.
+ * Adds a task, "buildDashboard", that aggregates the output of all tasks that produce reports.
  */
 @Incubating
-public class BuildDashboardPlugin implements Plugin<ProjectInternal> {
+public class BuildDashboardPlugin implements Plugin<Project> {
 
     public static final String BUILD_DASHBOARD_TASK_NAME = "buildDashboard";
 
-    public void apply(final ProjectInternal project) {
-        project.getPlugins().apply(ReportingBasePlugin.class);
+    public void apply(final Project project) {
+        project.getPluginManager().apply(ReportingBasePlugin.class);
 
         final GenerateBuildDashboard buildDashboardTask = project.getTasks().create(BUILD_DASHBOARD_TASK_NAME, GenerateBuildDashboard.class);
+        buildDashboardTask.setDescription("Generates a dashboard of all the reports produced by this build.");
+        buildDashboardTask.setGroup("reporting");
 
         DirectoryReport htmlReport = buildDashboardTask.getReports().getHtml();
         ConventionMapping htmlReportConventionMapping = new DslObject(htmlReport).getConventionMapping();
diff --git a/subprojects/reporting/src/main/resources/META-INF/gradle-plugins/build-dashboard.properties b/subprojects/reporting/src/main/resources/META-INF/gradle-plugins/org.gradle.build-dashboard.properties
similarity index 100%
rename from subprojects/reporting/src/main/resources/META-INF/gradle-plugins/build-dashboard.properties
rename to subprojects/reporting/src/main/resources/META-INF/gradle-plugins/org.gradle.build-dashboard.properties
diff --git a/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginConventionTest.groovy b/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginConventionTest.groovy
deleted file mode 100644
index 431508c..0000000
--- a/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginConventionTest.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins
-
-import org.gradle.api.Project
-import org.gradle.api.reporting.ReportingExtension
-import org.gradle.testfixtures.ProjectBuilder
-import spock.lang.Specification
-
-// Note: ReportingBasePluginConvention has been deprecated
-public class ReportingBasePluginConventionTest extends Specification {
-
-    Project project = ProjectBuilder.builder().build()
-    ReportingExtension extension = new ReportingExtension(project)
-    ReportingBasePluginConvention convention = new ReportingBasePluginConvention(project, extension)
-
-    def "defaults to reports dir in build dir"() {
-        expect:
-        convention.reportsDirName == ReportingExtension.DEFAULT_REPORTS_DIR_NAME
-        convention.reportsDir == new File(project.buildDir, ReportingExtension.DEFAULT_REPORTS_DIR_NAME)
-    }
-
-    def "can set reports dir by name, relative to build dir"() {
-        when:
-        convention.reportsDirName = "something-else"
-
-        then:
-        convention.reportsDir == new File(project.buildDir, "something-else")
-
-        when:
-        project.buildDir = new File(project.buildDir, "new-build-dir")
-
-        then:
-        convention.reportsDir == new File(project.buildDir, "something-else")
-    }
-
-    def "calculates api doc title from project name and version"() {
-        expect:
-        project.version == Project.DEFAULT_VERSION
-
-        and:
-        convention.apiDocTitle == "$project.name API"
-
-        when:
-        project.version = "1.0"
-
-        then:
-        convention.apiDocTitle == "$project.name 1.0 API"
-    }
-
-}
diff --git a/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginTest.groovy b/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginTest.groovy
index 087aa01..8bce98c 100644
--- a/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginTest.groovy
+++ b/subprojects/reporting/src/test/groovy/org/gradle/api/plugins/ReportingBasePluginTest.groovy
@@ -15,22 +15,17 @@
  */
 package org.gradle.api.plugins
 
-import org.gradle.api.Project
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 public class ReportingBasePluginTest extends Specification {
 
-    Project project = TestUtil.createRootProject();
+    DefaultProject project = TestUtil.createRootProject();
 
     def setup() {
-        project.plugins.apply(ReportingBasePlugin)
-    }
-
-    def addsTasksAndConventionToProject() {
-        expect:
-        project.convention.plugins.get("reportingBase") instanceof ReportingBasePluginConvention
+        project.pluginManager.apply(ReportingBasePlugin)
     }
 
     def "adds reporting extension"() {
diff --git a/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/BuildDashboardGeneratorSpec.groovy b/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/BuildDashboardGeneratorSpec.groovy
index 2fc4dad..b71ff31 100644
--- a/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/BuildDashboardGeneratorSpec.groovy
+++ b/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/BuildDashboardGeneratorSpec.groovy
@@ -26,20 +26,17 @@ import spock.lang.Specification
 
 class BuildDashboardGeneratorSpec extends Specification {
 
-    @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
 
     File outputFile
-    BuildDashboardGenerator generator
+    BuildDashboardGenerator generator = new BuildDashboardGenerator()
 
     void setup() {
         outputFile = tmpDir.file('output.html')
     }
 
-    private void generatorFor(reports) {
-        generator = new BuildDashboardGenerator(reports as Set, outputFile)
-    }
-
-    private Document getOutputHtml() {
+    Document getOutputHtml() {
         Jsoup.parse(outputFile, null)
     }
 
@@ -51,20 +48,16 @@ class BuildDashboardGeneratorSpec extends Specification {
     }
 
     Report mockDirectoryReport(String name, File destinationDirectory) {
-        Stub(DirectoryReport){
+        Stub(DirectoryReport) {
             getDisplayName() >> name
             getDestination() >> destinationDirectory
             getEntryPoint() >> new File(destinationDirectory, "index.html")
         }
     }
 
-
     void 'appropriate message is displayed when there are no reports available'() {
-        given:
-        generatorFor([])
-
         when:
-        generator.generate()
+        generator.render([], outputFile)
 
         then:
         outputHtml.select('h1').text() == 'There are no build reports available.'
@@ -74,16 +67,15 @@ class BuildDashboardGeneratorSpec extends Specification {
         given:
         def htmlFolder = tmpDir.createDir('htmlContent');
         htmlFolder.createFile("index.html")
-        generatorFor([
-                mockReport('a', tmpDir.createFile('report.html')),
-                mockReport('b', tmpDir.createDir('inner').createFile('otherReport.html')),
-                mockReport('c', tmpDir.file('idonotexist.html')),
-                mockDirectoryReport('d', htmlFolder),
-                mockReport('e', tmpDir.createDir('simpleDirectory')),
-        ])
 
         when:
-        generator.generate()
+        generator.render([
+                        mockReport('a', tmpDir.createFile('report.html')),
+                        mockReport('b', tmpDir.createDir('inner').createFile('otherReport.html')),
+                        mockReport('c', tmpDir.file('idonotexist.html')),
+                        mockDirectoryReport('d', htmlFolder),
+                        mockReport('e', tmpDir.createDir('simpleDirectory')),
+                ], outputFile)
 
         then:
         outputHtml.select('h1').text() == 'Build reports'
@@ -97,15 +89,28 @@ class BuildDashboardGeneratorSpec extends Specification {
         }
     }
 
-    void 'report css is set up'() {
+    void 'encodes output using utf-8'() {
         given:
-        generatorFor([])
+        def htmlFolder = tmpDir.createDir('htmlContent');
+        htmlFolder.createFile("index.html")
 
         when:
-        generator.generate()
+        generator.render([mockReport('\u03b1\u03b2', tmpDir.createFile('report.html'))], outputFile)
+
+        then:
+        outputHtml.select('h1').text() == 'Build reports'
+        with outputHtml.select('ul li'), {
+            size() == 1
+            select('a[href=report.html]').text() == '\u03b1\u03b2'
+        }
+    }
+
+    void 'report css is set up'() {
+        when:
+        generator.render([], outputFile)
 
         then:
-        outputHtml.select('head link[type=text/css]').attr('href') == 'base-style.css'
-        tmpDir.file('base-style.css').text == getClass().getResource('/org/gradle/reporting/base-style.css').text
+        outputHtml.select('head link[type=text/css]').attr('href') == 'css/base-style.css'
+        tmpDir.file('css/base-style.css').text == getClass().getResource('/org/gradle/reporting/base-style.css').text
     }
 }
diff --git a/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/DefaultReportContainerTest.groovy b/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/DefaultReportContainerTest.groovy
index cca2d10..96803fa 100644
--- a/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/DefaultReportContainerTest.groovy
+++ b/subprojects/reporting/src/test/groovy/org/gradle/api/reporting/internal/DefaultReportContainerTest.groovy
@@ -31,7 +31,7 @@ import spock.lang.Specification
 
 class DefaultReportContainerTest extends Specification {
 
-    static Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), new DirectInstantiator())
+    static Instantiator instantiator = new ClassGeneratorBackedInstantiator(new AsmBackedClassGenerator(), DirectInstantiator.INSTANCE)
 
     static class TestReportContainer extends DefaultReportContainer {
         TestReportContainer(Closure c) {
diff --git a/subprojects/resources-http/resources-http.gradle b/subprojects/resources-http/resources-http.gradle
new file mode 100644
index 0000000..b34a596
--- /dev/null
+++ b/subprojects/resources-http/resources-http.gradle
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 project(':resources')
+    compile project(':baseServices')
+    compile project(':core') // TODO:DAZ Only required to bring in PluginServiceRegistry: should be in baseServices?
+    compile libraries.commons_httpclient
+    compile libraries.commons_lang
+    compile libraries.nekohtml
+
+    testCompile libraries.groovy
+}
+
+useTestFixtures()
+useClassycle()
+strictCompile()
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParser.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParser.java
new file mode 100644
index 0000000..2e1f13f
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParser.java
@@ -0,0 +1,145 @@
+/*
+ * 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.resource.transport.http;
+
+import org.cyberneko.html.parsers.SAXParser;
+import org.gradle.internal.resource.UriResource;
+import org.gradle.internal.resource.ResourceException;
+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.helpers.DefaultHandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ApacheDirectoryListingParser {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ApacheDirectoryListingParser.class);
+
+    public List<String> parse(URI baseURI, InputStream content, String contentType) throws Exception {
+        baseURI = addTrailingSlashes(baseURI);
+        if (contentType == null || !contentType.startsWith("text/html")) {
+            throw new ResourceException(baseURI, String.format("Unsupported ContentType %s for directory listing '%s'", contentType, baseURI));
+        }
+        String contentEncoding = UriResource.extractCharacterEncoding(contentType, "utf-8");
+        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);
+        htmlParser.parse(inputSource);
+
+        List<String> hrefs = anchorListerHandler.getHrefs();
+        List<URI> uris = resolveURIs(baseURI, hrefs);
+        return filterNonDirectChilds(baseURI, uris);
+    }
+
+    private URI addTrailingSlashes(URI uri) throws IOException, URISyntaxException {
+        if(uri.getPath() == null){
+            uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), "/", uri.getQuery(), uri.getFragment());
+        }else if (!uri.getPath().endsWith("/") && !uri.getPath().endsWith(".html")) {
+            uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath() + "/", uri.getQuery(), uri.getFragment());
+
+        }
+        return uri;
+    }
+
+    private List<String> filterNonDirectChilds(URI baseURI, List<URI> inputURIs) throws MalformedURLException {
+        final int baseURIPort = baseURI.getPort();
+        final String baseURIHost = baseURI.getHost();
+        final String baseURIScheme = baseURI.getScheme();
+
+        List<String> uris = new ArrayList<String>();
+        final String prefixPath = baseURI.getPath();
+        for (URI parsedURI : inputURIs) {
+            if (parsedURI.getHost() != null && !parsedURI.getHost().equals(baseURIHost)) {
+                continue;
+            }
+            if (parsedURI.getScheme() != null && !parsedURI.getScheme().equals(baseURIScheme)) {
+                continue;
+            }
+            if (parsedURI.getPort() != baseURIPort) {
+                continue;
+            }
+            if (parsedURI.getPath() != null && !parsedURI.getPath().startsWith(prefixPath)) {
+                continue;
+            }
+            String childPathPart = parsedURI.getPath().substring(prefixPath.length(), parsedURI.getPath().length());
+            if (childPathPart.startsWith("../")) {
+                continue;
+            }
+            if (childPathPart.equals("") || childPathPart.split("/").length > 1) {
+                continue;
+            }
+
+            String path = parsedURI.getPath();
+            int pos = path.lastIndexOf('/');
+            if (pos < 0) {
+                uris.add(path);
+            } else if (pos == path.length() - 1) {
+                int start = path.lastIndexOf('/', pos - 1);
+                if (start < 0) {
+                    uris.add(path.substring(0, pos));
+                } else {
+                    uris.add(path.substring(start + 1, pos));
+                }
+            } else {
+                uris.add(path.substring(pos + 1));
+            }
+        }
+        return uris;
+    }
+
+    private List<URI> resolveURIs(URI baseURI, List<String> hrefs) {
+        List<URI> uris = new ArrayList<URI>();
+        for (String href : hrefs) {
+            try {
+                uris.add(baseURI.resolve(href));
+            } catch (IllegalArgumentException ex) {
+                LOGGER.debug(String.format("Cannot resolve anchor: %s", href));
+            }
+        }
+        return uris;
+    }
+
+    private class AnchorListerHandler extends DefaultHandler {
+        List<String> hrefs = new ArrayList<String>();
+
+        public List<String> getHrefs() {
+            return hrefs;
+        }
+
+        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+            if (qName.equalsIgnoreCase("A")) {
+                final String href = atts.getValue("href");
+                if (href != null) {
+                    hrefs.add(href);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/DefaultHttpSettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/DefaultHttpSettings.java
new file mode 100644
index 0000000..1fd13a9
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/DefaultHttpSettings.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+
+import org.gradle.internal.resource.PasswordCredentials;
+
+public class DefaultHttpSettings implements HttpSettings {
+    private final PasswordCredentials passwordCredentials;
+    private final HttpProxySettings proxySettings = new JavaSystemPropertiesHttpProxySettings();
+
+    public DefaultHttpSettings(PasswordCredentials passwordCredentials) {
+        this.passwordCredentials = passwordCredentials;
+    }
+
+    public PasswordCredentials getCredentials() {
+        return passwordCredentials;
+    }
+
+    public HttpProxySettings getProxySettings() {
+        return proxySettings;
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientConfigurer.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientConfigurer.java
new file mode 100644
index 0000000..9fcd652
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientConfigurer.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.*;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.transport.http.ntlm.NTLMCredentials;
+import org.gradle.internal.resource.transport.http.ntlm.NTLMSchemeFactory;
+import org.gradle.internal.resource.UriResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class HttpClientConfigurer {
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfigurer.class);
+
+    private final HttpSettings httpSettings;
+
+    public HttpClientConfigurer(HttpSettings httpSettings) {
+        this.httpSettings = httpSettings;
+    }
+
+    public void configure(DefaultHttpClient httpClient) {
+        NTLMSchemeFactory.register(httpClient);
+        configureCredentials(httpClient, httpSettings.getCredentials());
+        configureProxyCredentials(httpClient, httpSettings.getProxySettings());
+        configureRetryHandler(httpClient);
+        configureUserAgent(httpClient);
+    }
+
+    private void configureCredentials(DefaultHttpClient httpClient, PasswordCredentials credentials) {
+        if(credentials != null) {
+            String username = credentials.getUsername();
+            if (username != null && username.length() > 0) {
+                useCredentials(httpClient, credentials, AuthScope.ANY_HOST, AuthScope.ANY_PORT);
+
+                // Use preemptive authorisation if no other authorisation has been established
+                httpClient.addRequestInterceptor(new PreemptiveAuth(new BasicScheme()), 0);
+            }
+        }
+    }
+
+    private void configureProxyCredentials(DefaultHttpClient httpClient, HttpProxySettings proxySettings) {
+        HttpProxySettings.HttpProxy proxy = proxySettings.getProxy();
+        if (proxy != null && proxy.credentials != null) {
+            useCredentials(httpClient, proxy.credentials, proxy.host, proxy.port);
+        }
+    }
+
+    private void useCredentials(DefaultHttpClient httpClient, PasswordCredentials credentials, String host, int port) {
+        Credentials basicCredentials = new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword());
+        httpClient.getCredentialsProvider().setCredentials(new AuthScope(host, port), basicCredentials);
+
+        NTLMCredentials ntlmCredentials = new NTLMCredentials(credentials);
+        Credentials ntCredentials = new NTCredentials(ntlmCredentials.getUsername(), ntlmCredentials.getPassword(), ntlmCredentials.getWorkstation(), ntlmCredentials.getDomain());
+        httpClient.getCredentialsProvider().setCredentials(new AuthScope(host, port, AuthScope.ANY_REALM, AuthPolicy.NTLM), ntCredentials);
+
+        LOGGER.debug("Using {} and {} for authenticating against '{}:{}'", new Object[]{credentials, ntlmCredentials, host, port});
+    }
+
+    private void configureRetryHandler(DefaultHttpClient httpClient) {
+        httpClient.setHttpRequestRetryHandler(new HttpRequestRetryHandler() {
+            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
+                return false;
+            }
+        });
+    }
+
+    public void configureUserAgent(DefaultHttpClient httpClient) {
+        HttpProtocolParams.setUserAgent(httpClient.getParams(), UriResource.getUserAgentString());
+    }
+
+    static class PreemptiveAuth implements HttpRequestInterceptor {
+        private final AuthScheme authScheme;
+
+        PreemptiveAuth(AuthScheme authScheme) {
+            this.authScheme = authScheme;
+        }
+
+        public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+
+            AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
+
+            if (authState.getAuthScheme() != null || authState.hasAuthOptions()) {
+                return;
+            }
+
+            // If no authState has been established and this is a PUT or POST request, add preemptive authorisation
+            String requestMethod = request.getRequestLine().getMethod();
+            if (requestMethod.equals(HttpPut.METHOD_NAME) || requestMethod.equals(HttpPost.METHOD_NAME)) {
+                CredentialsProvider credentialsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
+                HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+                Credentials credentials = credentialsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
+                if (credentials == null) {
+                    throw new HttpException("No credentials for preemptive authentication");
+                }
+                authState.update(authScheme, credentials);
+            }
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java
new file mode 100644
index 0000000..8412a76
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java
@@ -0,0 +1,129 @@
+/*
+ * 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.resource.transport.http;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.*;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.util.EntityUtils;
+import org.gradle.api.UncheckedIOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Provides some convenience and unified logging.
+ */
+public class HttpClientHelper {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientHelper.class);
+    private final HttpClient client;
+    private final BasicHttpContext httpContext = new BasicHttpContext();
+
+    public HttpClientHelper(HttpSettings settings) {
+        alwaysUseKeepAliveConnections();
+
+        DefaultHttpClient client = new SystemDefaultHttpClient();
+        new HttpClientConfigurer(settings).configure(client);
+        this.client = new DecompressingHttpClient(client);
+    }
+
+    private void alwaysUseKeepAliveConnections() {
+        // HttpClient 4.2.2 does not use the correct default value for "http.keepAlive" system property (default is "true").
+        // HttpClient NTLM authentication fails badly when this property value is true.
+        // So we force it to be true here: effectively, we're ignoring any user-supplied value for our HttpClient configuration.
+        System.setProperty("http.keepAlive", "true");
+    }
+
+    public HttpResponse performRawHead(String source) {
+        return performRequest(new HttpHead(source));        
+    }
+    
+    public HttpResponse performHead(String source) {
+        return processResponse(source, "HEAD", performRawHead(source));
+    }
+
+    public HttpResponse performRawGet(String source) {
+        return performRequest(new HttpGet(source));
+    }
+
+    public HttpResponse performGet(String source) {
+        return processResponse(source, "GET", performRawGet(source));
+    }
+
+    public HttpResponse performRequest(HttpRequestBase request) {
+        String method = request.getMethod();
+
+        HttpResponse response;
+        try {
+            response = executeGetOrHead(request);
+        } catch (IOException e) {
+            throw new HttpRequestException(String.format("Could not %s '%s'.", method, request.getURI()), e);
+        }
+
+        return response;
+    }
+
+    protected HttpResponse executeGetOrHead(HttpRequestBase method) throws IOException {
+        HttpResponse httpResponse = performHttpRequest(method);
+        // Consume content for non-successful, responses. This avoids the connection being left open.
+        if (!wasSuccessful(httpResponse)) {
+            EntityUtils.consume(httpResponse.getEntity());
+            return httpResponse;
+        }
+        return httpResponse;
+    }
+
+    public boolean wasMissing(HttpResponse response) {
+        int statusCode = response.getStatusLine().getStatusCode();
+        return statusCode == 404;
+    }
+
+    public boolean wasSuccessful(HttpResponse response) {
+        int statusCode = response.getStatusLine().getStatusCode();
+        return statusCode >= 200 && statusCode < 300;
+    }
+
+    public HttpResponse performHttpRequest(HttpRequestBase request) throws IOException {
+        // Without this, HTTP Client prohibits multiple redirects to the same location within the same context
+        httpContext.removeAttribute(DefaultRedirectStrategy.REDIRECT_LOCATIONS);
+
+        LOGGER.debug("Performing HTTP {}: {}", request.getMethod(), request.getURI());
+        return client.execute(request, httpContext);
+    }
+
+    private HttpResponse processResponse(String source, String method, HttpResponse response) {
+        if (wasMissing(response)) {
+            LOGGER.info("Resource missing. [HTTP {}: {}]", method, source);
+            return null;
+        }
+        if (!wasSuccessful(response)) {
+            LOGGER.info("Failed to get resource: {}. [HTTP {}: {}]", method, response.getStatusLine(), source);
+            throw new UncheckedIOException(String.format("Could not %s '%s'. Received status code %s from server: %s",
+                    method, source, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
+        }
+
+        return response;
+    }
+
+
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpConnectorFactory.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpConnectorFactory.java
new file mode 100644
index 0000000..88a714b
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpConnectorFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http;
+
+import com.google.common.collect.Sets;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.resource.connector.ResourceConnectorSpecification;
+import org.gradle.internal.resource.transfer.DefaultExternalResourceConnector;
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+
+import java.util.Set;
+
+public class HttpConnectorFactory implements ResourceConnectorFactory {
+    @Override
+    public Set<String> getSupportedProtocols() {
+        return Sets.newHashSet("http", "https");
+    }
+
+    @Override
+    public ExternalResourceConnector createResourceConnector(ResourceConnectorSpecification connectionDetails) {
+        HttpClientHelper http = new HttpClientHelper(new DefaultHttpSettings(connectionDetails.getCredentials(PasswordCredentials.class)));
+        HttpResourceAccessor accessor = new HttpResourceAccessor(http);
+        HttpResourceLister lister = new HttpResourceLister(accessor);
+        HttpResourceUploader uploader = new HttpResourceUploader(http);
+        return new DefaultExternalResourceConnector(accessor, lister, uploader);
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpProxySettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpProxySettings.java
new file mode 100644
index 0000000..fbb8b91
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpProxySettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+
+import org.gradle.internal.resource.PasswordCredentials;
+
+public interface HttpProxySettings {
+
+    HttpProxy getProxy();
+
+    HttpProxy getProxy(String host);
+
+    public class HttpProxy {
+        public final String host;
+        public final int port;
+        public final PasswordCredentials credentials;
+
+        public HttpProxy(String host, int port, String username, String password) {
+            this.host = host;
+            this.port = port;
+            if (username == null || username.length() == 0) {
+                credentials = null;
+            } else {
+                credentials = new PasswordCredentials(username, password);
+            }
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpRequestException.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpRequestException.java
new file mode 100644
index 0000000..971b8fb
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpRequestException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.resource.transport.http;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.internal.exceptions.Contextual;
+
+/**
+ * Signals that some error occurred when making an HTTP request.
+ * This is different from a HTTP request returning an HTTP error code.
+ */
+ at Contextual
+public class HttpRequestException extends UncheckedIOException {
+    public HttpRequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceAccessor.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceAccessor.java
new file mode 100644
index 0000000..119d207
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceAccessor.java
@@ -0,0 +1,116 @@
+/*
+ * 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.resource.transport.http;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceAccessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HttpResourceAccessor implements ExternalResourceAccessor {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpResourceAccessor.class);
+    private final HttpClientHelper http;
+
+    private final List<HttpResponseResource> openResources = new ArrayList<HttpResponseResource>();
+
+    public HttpResourceAccessor(HttpClientHelper http) {
+        this.http = http;
+    }
+
+    @Nullable
+    public HttpResponseResource openResource(final URI uri) {
+        abortOpenResources();
+        String location = uri.toString();
+        LOGGER.debug("Constructing external resource: {}", location);
+
+        HttpResponse response = http.performGet(location);
+        if (response != null) {
+            HttpResponseResource resource = wrapResponse(uri, response);
+            return recordOpenGetResource(resource);
+        }
+
+        return null;
+    }
+
+    /**
+     * Same as #getResource except that it always gives access to the response body,
+     * irrespective of the returned HTTP status code. Never returns {@code null}.
+     */
+    public HttpResponseResource getRawResource(final URI uri) {
+        abortOpenResources();
+        String location = uri.toString();
+        LOGGER.debug("Constructing external resource: {}", location);
+
+        HttpRequestBase request = new HttpGet(uri);
+        HttpResponse response;
+        try {
+            response = http.performHttpRequest(request);
+        } catch (IOException e) {
+            throw new HttpRequestException(String.format("Could not %s '%s'.", request.getMethod(), request.getURI()), e);
+        }
+
+        HttpResponseResource resource = wrapResponse(uri, response);
+        return recordOpenGetResource(resource);
+    }
+
+    public ExternalResourceMetaData getMetaData(URI uri) {
+        abortOpenResources();
+        String location = uri.toString();
+        LOGGER.debug("Constructing external resource metadata: {}", location);
+        HttpResponse response = http.performHead(location);
+        return response == null ? null : new HttpResponseResource("HEAD", uri, response).getMetaData();
+    }
+
+    private HttpResponseResource recordOpenGetResource(HttpResponseResource httpResource) {
+        openResources.add(httpResource);
+        return httpResource;
+    }
+
+    private void abortOpenResources() {
+        for (Closeable openResource : openResources) {
+            LOGGER.warn("Forcing close on abandoned resource: " + openResource);
+            try {
+                openResource.close();
+            } catch (IOException e) {
+                LOGGER.warn("Failed to close abandoned resource", e);
+            }
+        }
+        openResources.clear();
+    }
+
+    private HttpResponseResource wrapResponse(URI uri, HttpResponse response) {
+        return new HttpResponseResource("GET", uri, response) {
+            @Override
+            public void close() throws IOException {
+                super.close();
+                HttpResourceAccessor.this.openResources.remove(this);
+            }
+        };
+    }
+
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceLister.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceLister.java
new file mode 100644
index 0000000..3ff14f4
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceLister.java
@@ -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.internal.resource.transport.http;
+
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transfer.ExternalResourceLister;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+
+public class HttpResourceLister implements ExternalResourceLister {
+    private HttpResourceAccessor accessor;
+
+    public HttpResourceLister(HttpResourceAccessor accessor) {
+        this.accessor = accessor;
+    }
+
+    public List<String> list(final URI directory) {
+        final ExternalResourceReadResponse response = accessor.openResource(directory);
+        if (response == null) {
+            return null;
+        }
+        try {
+            try {
+                String contentType = response.getMetaData().getContentType();
+                ApacheDirectoryListingParser directoryListingParser = new ApacheDirectoryListingParser();
+                InputStream inputStream = response.openStream();
+                try {
+                    return directoryListingParser.parse(directory, inputStream, contentType);
+                } catch (Exception e) {
+                    throw new ResourceException(directory, String.format("Unable to parse HTTP directory listing for '%s'.", directory), e);
+                }
+            } finally {
+                response.close();
+            }
+        } catch (IOException e) {
+            throw ResourceException.getFailed(directory, e);
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceUploader.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceUploader.java
new file mode 100644
index 0000000..0b4c0a0
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourceUploader.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.internal.resource.transport.http;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.transfer.ExternalResourceUploader;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class HttpResourceUploader implements ExternalResourceUploader {
+
+    private final HttpClientHelper http;
+
+    public HttpResourceUploader(HttpClientHelper http) {
+        this.http = http;
+    }
+
+    public void upload(LocalResource resource, URI destination) throws IOException {
+        HttpPut method = new HttpPut(destination);
+        final RepeatableInputStreamEntity entity = new RepeatableInputStreamEntity(resource, ContentType.APPLICATION_OCTET_STREAM);
+        method.setEntity(entity);
+        HttpResponse response = http.performHttpRequest(method);
+        EntityUtils.consume(response.getEntity());
+        if (!http.wasSuccessful(response)) {
+            throw new IOException(String.format("Could not PUT '%s'. Received status code %s from server: %s",
+                    destination, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
+        }
+
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourcesPluginServiceRegistry.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourcesPluginServiceRegistry.java
new file mode 100644
index 0000000..41d9fd1
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResourcesPluginServiceRegistry.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http;
+
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class HttpResourcesPluginServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new GlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class GlobalScopeServices {
+        ResourceConnectorFactory createHttpConnectorFactory() {
+            return new HttpConnectorFactory();
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResponseResource.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResponseResource.java
new file mode 100644
index 0000000..80aaaf1
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpResponseResource.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.util.EntityUtils;
+import org.gradle.internal.hash.HashValue;
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class HttpResponseResource implements ExternalResourceReadResponse {
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpResponseResource.class);
+
+    private final String method;
+    private final URI source;
+    private final HttpResponse response;
+    private final ExternalResourceMetaData metaData;
+    private boolean wasOpened;
+
+    public HttpResponseResource(String method, URI source, HttpResponse response) {
+        this.method = method;
+        this.source = source;
+        this.response = response;
+
+        String etag = getEtag(response);
+        this.metaData = new DefaultExternalResourceMetaData(source, getLastModified(), getContentLength(), getContentType(), etag, getSha1(response, etag));
+    }
+
+    public URI getURI() {
+        return source;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Http %s Resource: %s", method, source);
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        return metaData;
+    }
+
+    public int getStatusCode() {
+        return response.getStatusLine().getStatusCode();
+    }
+
+    public long getLastModified() {
+        Header responseHeader = response.getFirstHeader("last-modified");
+        if (responseHeader == null) {
+            return 0;
+        }
+        try {
+            return DateUtils.parseDate(responseHeader.getValue()).getTime();
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    public long getContentLength() {
+        Header header = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
+        if (header == null) {
+            return -1;            
+        }
+
+        String value = header.getValue();
+        if (value == null) {
+            return -1;
+        }
+
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    public String getHeaderValue(String name) {
+        Header header = response.getFirstHeader(name);
+        return header != null ? header.getValue() : null;
+    }
+
+    public String getContentType() {
+        final Header header = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
+        return header == null ? null : header.getValue();
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+
+    public InputStream openStream() throws IOException {
+        if(wasOpened){
+            throw new IOException("Unable to open Stream as it was opened before.");
+        }
+        LOGGER.debug("Attempting to download resource {}.", source);
+        this.wasOpened = true;
+        return response.getEntity().getContent();
+    }
+
+    @Override
+    public void close() throws IOException {
+        EntityUtils.consume(response.getEntity());
+    }
+
+    private static String getEtag(HttpResponse response) {
+        Header etagHeader = response.getFirstHeader(HttpHeaders.ETAG);
+        return etagHeader == null ? null : etagHeader.getValue();
+    }
+    
+    private static HashValue getSha1(HttpResponse response, String etag) {
+        Header sha1Header = response.getFirstHeader("X-Checksum-Sha1");
+        if (sha1Header != null) {
+            return new HashValue(sha1Header.getValue());    
+        }
+
+        // Nexus uses sha1 etags, with a constant prefix
+        // e.g {SHA1{b8ad5573a5e9eba7d48ed77a48ad098e3ec2590b}}
+        if (etag != null && etag.startsWith("{SHA1{")) {
+            String hash = etag.substring(6, etag.length() - 2);
+            return new HashValue(hash);
+        }
+
+        return null;
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpSettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpSettings.java
new file mode 100644
index 0000000..e206386
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpSettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+
+import org.gradle.internal.resource.PasswordCredentials;
+
+public interface HttpSettings {
+    PasswordCredentials getCredentials();
+
+    HttpProxySettings getProxySettings();
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettings.java
new file mode 100644
index 0000000..d565df6
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+public class JavaSystemPropertiesHttpProxySettings extends JavaSystemPropertiesProxySettings {
+    private static final int DEFAULT_PROXY_PORT = 80;
+    private static final String PROPERTY_PREFIX = "http";
+
+    public JavaSystemPropertiesHttpProxySettings() {
+        super(PROPERTY_PREFIX, DEFAULT_PROXY_PORT);
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettings.java
new file mode 100644
index 0000000..82fb7b1
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettings.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public abstract class JavaSystemPropertiesProxySettings implements HttpProxySettings {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JavaSystemPropertiesProxySettings.class);
+
+    private final HttpProxy proxy;
+    private final List<Pattern> nonProxyHosts;
+    private final String propertyPrefix;
+    private final int defaultPort;
+
+    public JavaSystemPropertiesProxySettings(String propertyPrefix, int defaultPort) {
+        this(propertyPrefix, defaultPort,
+                System.getProperty(propertyPrefix + ".proxyHost"),
+                System.getProperty(propertyPrefix + ".proxyPort"),
+                System.getProperty(propertyPrefix + ".proxyUser"),
+                System.getProperty(propertyPrefix + ".proxyPassword"),
+                System.getProperty(propertyPrefix + ".nonProxyHosts"));
+    }
+
+    JavaSystemPropertiesProxySettings(String propertyPrefix, int defaultPort, String proxyHost, String proxyPortString, String proxyUser, String proxyPassword, String nonProxyHostsString) {
+        this.propertyPrefix = propertyPrefix;
+        this.defaultPort = defaultPort;
+        if (StringUtils.isBlank(proxyHost)) {
+            this.proxy = null;
+        } else {
+            this.proxy = new HttpProxy(proxyHost, initProxyPort(proxyPortString), proxyUser, proxyPassword);
+        }
+        this.nonProxyHosts = initNonProxyHosts(nonProxyHostsString);
+    }
+
+    private int initProxyPort(String proxyPortString) {
+        if (StringUtils.isBlank(proxyPortString)) {
+            return defaultPort;
+        }
+        try {
+            return Integer.parseInt(proxyPortString);
+        } catch (NumberFormatException e) {
+            String key = propertyPrefix + ".proxyPort";
+            LOGGER.warn("Invalid value for java system property '{}': {}. Default port '{}' will be used.",
+                    key, System.getProperty(key), defaultPort);
+            return defaultPort;
+        }
+    }
+
+    private List<Pattern> initNonProxyHosts(String nonProxyHostsString) {
+        if (StringUtils.isBlank(nonProxyHostsString)) {
+            return Collections.emptyList();
+        }
+
+        LOGGER.debug("Found java system property 'http.nonProxyHosts': {}. Will ignore proxy settings for these hosts.", nonProxyHostsString);
+        List<Pattern> patterns = new ArrayList<Pattern>();
+        for (String nonProxyHost : nonProxyHostsString.split("\\|")) {
+            patterns.add(createHostMatcher(nonProxyHost));
+        }
+        return patterns;
+    }
+
+    private Pattern createHostMatcher(String nonProxyHost) {
+        if (nonProxyHost.startsWith("*")) {
+            return Pattern.compile(".*" + Pattern.quote(nonProxyHost.substring(1)));
+        }
+        if (nonProxyHost.endsWith("*")) {
+            return Pattern.compile(Pattern.quote(nonProxyHost.substring(0, nonProxyHost.length() - 1)) + ".*");
+        }
+        return Pattern.compile(Pattern.quote(nonProxyHost));
+    }
+
+    public HttpProxySettings.HttpProxy getProxy() {
+        return proxy;
+    }
+
+    public HttpProxySettings.HttpProxy getProxy(String host) {
+        if (proxy == null || isNonProxyHost(host)) {
+            return null;
+        }
+        return proxy;
+    }
+
+    private boolean isNonProxyHost(String host) {
+        for (Pattern nonProxyHost : nonProxyHosts) {
+            if (nonProxyHost.matcher(host).matches()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public String getPropertyPrefix() {
+        return propertyPrefix;
+    }
+
+    public int getDefaultPort() {
+        return defaultPort;
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettings.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettings.java
new file mode 100644
index 0000000..9f8a391
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettings.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http;
+
+public class JavaSystemPropertiesSecureHttpProxySettings extends JavaSystemPropertiesProxySettings {
+    private static final int DEFAULT_PROXY_PORT = 443;
+    private static final String PROPERTY_PREFIX = "https";
+
+    public JavaSystemPropertiesSecureHttpProxySettings() {
+        super(PROPERTY_PREFIX, DEFAULT_PROXY_PORT);
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/RepeatableInputStreamEntity.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/RepeatableInputStreamEntity.java
new file mode 100644
index 0000000..8bce19f
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/RepeatableInputStreamEntity.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.internal.resource.transport.http;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.entity.ContentType;
+import org.gradle.internal.resource.local.LocalResource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class RepeatableInputStreamEntity extends AbstractHttpEntity {
+    private final LocalResource source;
+
+    public RepeatableInputStreamEntity(LocalResource source, ContentType contentType) {
+        super();
+        this.source = source;
+        if (contentType != null) {
+            setContentType(contentType.toString());
+        }
+    }
+
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    public long getContentLength() {
+        return source.getContentLength();
+    }
+
+    public InputStream getContent() throws IOException, IllegalStateException {
+        return source.open();
+    }
+
+    public void writeTo(OutputStream outstream) throws IOException {
+        IOUtils.copyLarge(getContent(), outstream);
+    }
+
+    public boolean isStreaming() {
+        return true;
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentials.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentials.java
new file mode 100644
index 0000000..ec41db6
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentials.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.resource.transport.http.ntlm;
+
+
+import org.gradle.internal.resource.PasswordCredentials;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class NTLMCredentials {
+    private static final String DEFAULT_DOMAIN = "";
+    private static final String DEFAULT_WORKSTATION = "";
+    private final String domain;
+    private final String username;
+    private final String password;
+    private final String workstation;
+
+    public NTLMCredentials(PasswordCredentials credentials) {
+        String domain;
+        String username = credentials.getUsername();
+        int slashPos = username.indexOf('\\');
+        slashPos = slashPos >= 0 ? slashPos : username.indexOf('/');
+        if (slashPos >= 0) {
+            domain = username.substring(0, slashPos);
+            username = username.substring(slashPos + 1);
+        } else {
+            domain = System.getProperty("http.auth.ntlm.domain", DEFAULT_DOMAIN);
+        }
+        this.domain = domain == null ? null : domain.toUpperCase();
+        this.username = username;
+        this.password = credentials.getPassword();
+        this.workstation = determineWorkstationName();
+    }
+
+    private String determineWorkstationName() {
+        // TODO:DAZ This is a temporary (hidden) property that may be useful to track down issues. Remove when NTLM Auth is solid.
+        String sysPropWorkstation = System.getProperty("http.auth.ntlm.workstation");
+        if (sysPropWorkstation != null) {
+            return sysPropWorkstation;
+        }
+
+        try {
+            return removeDotSuffix(getHostName()).toUpperCase();
+        } catch (UnknownHostException e) {
+            return DEFAULT_WORKSTATION;
+        }
+    }
+
+    protected String getHostName() throws UnknownHostException {
+        return InetAddress.getLocalHost().getHostName();
+    }
+
+    private String removeDotSuffix(String val) {
+        int dotPos = val.indexOf('.');
+        return dotPos == -1 ? val : val.substring(0, dotPos);
+    }
+
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public String getWorkstation() {
+        return workstation;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("NTLM Credentials [user: %s, domain: %s, workstation: %s]", username, domain, workstation);
+    }
+}
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMSchemeFactory.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMSchemeFactory.java
new file mode 100644
index 0000000..cfb3c2d
--- /dev/null
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/ntlm/NTLMSchemeFactory.java
@@ -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.internal.resource.transport.http.ntlm;
+
+import jcifs.ntlmssp.Type1Message;
+import jcifs.ntlmssp.Type2Message;
+import jcifs.ntlmssp.Type3Message;
+import jcifs.util.Base64;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.impl.auth.NTLMEngine;
+import org.apache.http.impl.auth.NTLMEngineException;
+import org.apache.http.impl.auth.NTLMScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.HttpParams;
+
+import java.io.IOException;
+
+// Copied from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
+public class NTLMSchemeFactory implements AuthSchemeFactory {
+
+    public static void register(DefaultHttpClient httpClient) {
+        httpClient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
+    }
+
+    public AuthScheme newInstance(HttpParams params) {
+        return new NTLMScheme(new JCIFSEngine());
+    }
+
+    private static class JCIFSEngine implements NTLMEngine {
+
+        public String generateType1Msg(String domain, String workstation) throws NTLMEngineException {
+            Type1Message type1Message = new Type1Message(Type1Message.getDefaultFlags(), domain, workstation);
+            return Base64.encode(type1Message.toByteArray());
+        }
+
+        public String generateType3Msg(String username, String password, String domain, String workstation, String challenge) throws NTLMEngineException {
+            Type2Message type2Message = decodeType2Message(challenge);
+            Type3Message type3Message = new Type3Message(type2Message, password, domain, username, workstation, Type3Message.getDefaultFlags());
+            return Base64.encode(type3Message.toByteArray());
+        }
+
+        private Type2Message decodeType2Message(String challenge) throws NTLMEngineException {
+            try {
+                return new Type2Message(Base64.decode(challenge));
+            } catch (final IOException exception) {
+                throw new NTLMEngineException("Invalid Type2 message", exception);
+            }
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/resources-http/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..968d54c
--- /dev/null
+++ b/subprojects/resources-http/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1,2 @@
+org.gradle.internal.resource.transport.http.HttpResourcesPluginServiceRegistry
+
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParserTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParserTest.groovy
new file mode 100644
index 0000000..b1ad660
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ApacheDirectoryListingParserTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * 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.resource.transport.http
+
+import org.gradle.internal.resource.ResourceException
+import org.gradle.util.Resources
+import org.junit.Rule
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static org.junit.Assert.assertNotNull
+
+class ApacheDirectoryListingParserTest extends Specification {
+    @Rule
+    public final Resources resources = new Resources();
+
+    private static final CONTENT_TYPE = "text/html;charset=utf-8";
+    private URI baseUrl = URI.create("http://testrepo/")
+    private ApacheDirectoryListingParser parser = new ApacheDirectoryListingParser();
+
+    def "parse returns empty List if no link can be found"() {
+        expect:
+        List urls = parser.parse(baseUrl, new ByteArrayInputStream("<html>no link here</html>".bytes), CONTENT_TYPE)
+        assertNotNull(urls)
+        urls.isEmpty()
+    }
+
+    def "addTrailingSlashes adds trailing slashes on relative URL if not exist"() {
+        expect:
+        new URI(resultingURI) == parser.addTrailingSlashes(new URI(inputURI))
+        where:
+        inputURI                     | resultingURI
+        "http://testrepo"            | "http://testrepo/"
+        "http://testrepo/"           | "http://testrepo/"
+        "http://testrepo/index.html" | "http://testrepo/index.html"
+    }
+
+    def "parse handles multiple listed links"() {
+        def html = """
+        <a href="directory1">directory1</a>
+        <a href="directory2">directory2</a>
+        <a href="directory3">directory3</a>
+        <a href="directory4"/>"""
+        expect:
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(html.bytes), CONTENT_TYPE)
+        assertNotNull(uris)
+        uris.collect { it.toString() } == ["directory1", "directory2", "directory3", "directory4"]
+    }
+
+    def "only text/html content type is supported"() {
+        def html = """
+        <a href="directory1">directory1</a>
+        <a href="directory2">directory2</a>"""
+        when:
+        parser.parse(baseUrl, new ByteArrayInputStream(html.bytes), contentType)
+        then:
+        thrown(ResourceException)
+        where:
+        contentType << ["text/plain", "application/octetstream"]
+    }
+
+    def "uses charset specified in content type"() {
+        def html = """
+        <a href="\u00c1\u00d2">directory1</a>
+        """
+        def encodedHtml = html.getBytes('ISO-8859-1')
+        assert !Arrays.equals(encodedHtml, html.getBytes("utf-8"))
+
+        expect:
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(encodedHtml), 'text/html;charset=ISO-8859-1')
+        uris.collect { it.toString() } == ["\u00c1\u00d2"]
+    }
+
+    def "defaults to utf-8 when no charset specified"() {
+        def html = """
+        <a href="\u0321\u0322">directory1</a>
+        """
+        def encodedHtml = html.getBytes('utf-8')
+
+        expect:
+        def uris = parser.parse(baseUrl, new ByteArrayInputStream(encodedHtml), 'text/html')
+        uris.collect { it.toString() } == ["\u0321\u0322"]
+    }
+
+    @Unroll
+    def "parse ignores #descr"() {
+        expect:
+        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"
+        "../"                                               | "links to parent URLs of base URL"
+        "http://[2h:23:3]"                                  | "invalid URLs"
+        "dir1/subdir1"                                      | "links to nested subdirectories"
+        "<![CDATA[<a href=\"directory2\">directory2</a>]]>" | "links in CDATA blocks"
+        "#achor"                                            | "anchor links"
+        "<a name=\"anchorname\">headline</a>"               | "anchor definitions"
+    }
+
+    @Unroll
+    def "parseLink handles #urlDescr"() {
+        def listingParser = new ApacheDirectoryListingParser()
+        expect:
+        def foundURIs = listingParser.parse(URI.create(baseUri), new ByteArrayInputStream("<a href=\"${href}\">link</a>".toString().bytes), CONTENT_TYPE)
+        !foundURIs.isEmpty()
+        foundURIs.collect { it.toString() } == ["directory1"]
+        where:
+        baseUri                | href                         | urlDescr
+        "http://testrepo"      | "directory1"                 | "relative URLS"
+        "http://testrepo"      | "/directory1"                | "absolute URLS"
+        "http://testrepo"      | "./directory1"               | "explicit relative URLS"
+        "http://testrepo"      | "directory1/"                | "trailing slash"
+        "http://testrepo"      | "./directory1/"              | "relative URL with trailing slash"
+        "http://testrepo"      | "http://testrepo/directory1" | "complete URLS"
+        "http://testrepo"      | "http://testrepo/directory1" | "hrefs with truncated text"
+        "http://testrepo"      | "http://testrepo/directory1" | "hrefs with truncated text"
+        "http://[2001:db8::7]" | "directory1"                 | "ipv6 host with relative URLS"
+        "http://[2001:db8::7]" | "./directory1"               | "ipv6 host with explicit relative URLS"
+        "http://192.0.0.10"    | "directory1"                 | "ipv4 host with relative URLS"
+        "http://192.0.0.10"    | "./directory1"               | "ipv4 host with relative URLS"
+    }
+
+    @Unroll
+    def "parse is compatible with #repoType"() {
+        setup:
+        def byte[] content = resources.getResource("${repoType}_dirlisting.html").bytes
+        expect:
+        List<String> urls = new ApacheDirectoryListingParser().parse(new URI(artifactRootURI), new ByteArrayInputStream(content), CONTENT_TYPE)
+        urls as Set == ["3.7",
+                        "3.8",
+                        "3.8.1",
+                        "3.8.2",
+                        "4.0",
+                        "4.1",
+                        "4.10",
+                        "4.2",
+                        "4.3",
+                        "4.3.1",
+                        "4.4",
+                        "4.5",
+                        "4.6",
+                        "4.7",
+                        "4.8",
+                        "4.8.1",
+                        "4.8.2",
+                        "4.9",
+                        "maven-metadata.xml",
+                        "maven-metadata.xml.md5",
+                        "maven-metadata.xml.sha1"] as Set
+        where:
+        artifactRootURI                                                         | repoType
+        "http://localhost:8081/artifactory/repo1/junit/junit/"                  | "artifactory"
+        "https://repo1.maven.org/maven2/junit/junit/"                           | "mavencentral"
+        "http://localhost:8081/nexus/content/repositories/central/junit/junit/" | "nexus"
+    }
+
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientConfigurerTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientConfigurerTest.groovy
new file mode 100644
index 0000000..346b392
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientConfigurerTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http
+
+import org.apache.http.auth.AuthScope
+import org.apache.http.impl.client.DefaultHttpClient
+import org.apache.http.params.HttpProtocolParams
+import org.gradle.internal.resource.PasswordCredentials
+import org.gradle.internal.resource.UriResource
+import spock.lang.Specification
+
+public class HttpClientConfigurerTest extends Specification {
+    DefaultHttpClient httpClient = new DefaultHttpClient()
+    PasswordCredentials credentials = Mock()
+    HttpSettings httpSettings = Mock()
+    HttpProxySettings proxySettings = Mock()
+    HttpClientConfigurer configurer = new HttpClientConfigurer(httpSettings)
+
+    def "configures http client with no credentials or proxy"() {
+        httpSettings.credentials >> credentials
+        httpSettings.proxySettings >> proxySettings
+
+        when:
+        configurer.configure(httpClient)
+
+        then:
+        !httpClient.getHttpRequestRetryHandler().retryRequest(new IOException(), 1, null)
+    }
+
+    def "configures http client with proxy credentials"() {
+        httpSettings.credentials >> credentials
+        httpSettings.proxySettings >> proxySettings
+        proxySettings.proxy >> new HttpProxySettings.HttpProxy("host", 1111, "domain/proxyUser", "proxyPass")
+
+        when:
+        configurer.configure(httpClient)
+
+        then:
+        def proxyCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope("host", 1111))
+        proxyCredentials.userPrincipal.name == "domain/proxyUser"
+        proxyCredentials.password == "proxyPass"
+
+        and:
+        def ntlmCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope("host", 1111, AuthScope.ANY_REALM, "ntlm"))
+        ntlmCredentials.userPrincipal.name == 'DOMAIN/proxyUser'
+        ntlmCredentials.domain == 'DOMAIN'
+        ntlmCredentials.userName == 'proxyUser'
+        ntlmCredentials.password == 'proxyPass'
+        ntlmCredentials.workstation != ''
+    }
+
+    def "configures http client with credentials"() {
+        httpSettings.credentials >> credentials
+        credentials.username >> "domain/user"
+        credentials.password >> "pass"
+        httpSettings.proxySettings >> proxySettings
+
+        when:
+        configurer.configure(httpClient)
+
+        then:
+        def basicCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT))
+        basicCredentials.userPrincipal.name == "domain/user"
+        basicCredentials.password == "pass"
+
+        and:
+        def ntlmCredentials = httpClient.getCredentialsProvider().getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "ntlm"))
+        ntlmCredentials.userPrincipal.name == 'DOMAIN/user'
+        ntlmCredentials.domain == 'DOMAIN'
+        ntlmCredentials.userName == 'user'
+        ntlmCredentials.password == 'pass'
+        ntlmCredentials.workstation != ''
+
+        and:
+        httpClient.getRequestInterceptor(0) instanceof HttpClientConfigurer.PreemptiveAuth
+    }
+
+    def "configures http client with user agent"() {
+        httpSettings.credentials >> credentials
+        httpSettings.proxySettings >> proxySettings
+
+        when:
+        configurer.configure(httpClient)
+
+        then:
+        HttpProtocolParams.getUserAgent(httpClient.params) == UriResource.userAgentString
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientHelperTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientHelperTest.groovy
new file mode 100644
index 0000000..3902dae
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpClientHelperTest.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.internal.resource.transport.http
+import org.apache.http.HttpResponse
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.client.methods.HttpRequestBase
+import org.gradle.internal.resource.PasswordCredentials
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+class HttpClientHelperTest extends Specification {
+    @Rule SetSystemProperties sysProp = new SetSystemProperties()
+
+    def "throws HttpRequestException if an IO error occurs during a request"() {
+        def client = new HttpClientHelper(httpSettings) {
+            @Override
+            protected HttpResponse executeGetOrHead(HttpRequestBase method) {
+                throw new IOException("ouch")
+            }
+        }
+
+        when:
+        client.performRequest(new HttpGet("http://gradle.org"))
+
+        then:
+        HttpRequestException e = thrown()
+        e.cause.message == "ouch"
+    }
+
+    def "always sets http.keepAlive system property to 'true'"() {
+        given:
+        System.setProperty("http.keepAlive", "false")
+
+        when:
+        new HttpClientHelper(httpSettings)
+
+        then:
+        System.getProperty("http.keepAlive", "true")
+    }
+
+    private HttpSettings getHttpSettings() {
+        return Stub(HttpSettings) {
+            getCredentials() >> Stub(PasswordCredentials)
+            getProxySettings() >> Stub(HttpProxySettings)
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResourceListerTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResourceListerTest.groovy
new file mode 100644
index 0000000..a510f99
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResourceListerTest.groovy
@@ -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.internal.resource.transport.http
+
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData
+import spock.lang.Specification
+
+class HttpResourceListerTest extends Specification {
+    HttpResourceAccessor accessorMock = Mock()
+    HttpResponseResource externalResource = Mock()
+    ExternalResourceMetaData metaData = Mock()
+    HttpResourceLister lister = new HttpResourceLister(accessorMock)
+
+    def "consumeExternalResource closes resource after reading into stream"() {
+        setup:
+        accessorMock.openResource(new URI("http://testrepo/")) >> externalResource;
+        when:
+        lister.list(new URI("http://testrepo/"))
+        then:
+        1 * externalResource.openStream() >> new ByteArrayInputStream("<a href='child'/>".bytes)
+        _ * externalResource.metaData >> metaData
+        1 * metaData.getContentType() >> "text/html"
+        1 * externalResource.close()
+    }
+
+    def "list returns null if HttpAccessor returns null"(){
+        setup:
+        accessorMock.openResource(new URI("http://testrepo/"))  >> null
+        expect:
+        null == lister.list(new URI("http://testrepo"))
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResponseResourceTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResponseResourceTest.groovy
new file mode 100644
index 0000000..eebafdd
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/HttpResponseResourceTest.groovy
@@ -0,0 +1,88 @@
+/*
+ * 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.resource.transport.http
+
+import org.apache.http.Header
+import org.apache.http.HttpHeaders
+import org.apache.http.HttpResponse
+import org.apache.http.message.BasicHeader
+import spock.lang.Specification
+import org.apache.http.HttpEntity
+
+class HttpResponseResourceTest extends Specification {
+
+    def sourceUrl = new URI("http://gradle.org")
+    def method = "GET"
+    def response = Mock(HttpResponse)
+
+    def "extracts etag"() {
+        given:
+        addHeader(HttpHeaders.ETAG, "abc")
+
+        expect:
+        resource().metaData.etag == "abc"
+    }
+
+    def "handles no etag"() {
+        expect:
+        resource().metaData.etag == null
+    }
+
+    def "is not openable more than once"() {
+        setup:
+        1 * response.entity >> Mock(HttpEntity)
+        when:
+        def resource = resource();
+        resource.openStream();
+        and:
+        resource.openStream()
+        then:
+        def ex = thrown(IOException);
+        ex.message == "Unable to open Stream as it was opened before."
+    }
+
+    def "provides access to arbitrary headers"() {
+        given:
+        addHeader(name, value)
+
+        expect:
+        resource().getHeaderValue(name) == value
+
+        where:
+        name = "X-Client-Deprecation-Message"
+        value = "Some message"
+    }
+
+    def "returns null when accessing value of a non existing header"() {
+        expect:
+        resource().getHeaderValue("X-No-Such-Header") == null
+    }
+
+    HttpResponseResource resource() {
+        new HttpResponseResource(method, sourceUrl, response)
+    }
+
+    void addHeader(String name, String value) {
+        interaction {
+            1 * response.getFirstHeader(name) >> header(name, value)
+        }
+    }
+
+    Header header(String name, String value) {
+        new BasicHeader(name, value)
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy
new file mode 100644
index 0000000..33304d3
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesHttpProxySettingsTest.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource.transport.http;
+
+
+import spock.lang.Specification
+
+class JavaSystemPropertiesHttpProxySettingsTest extends Specification {
+
+    def "should use default HTTP prefix and port"() {
+        JavaSystemPropertiesProxySettings settings = new JavaSystemPropertiesHttpProxySettings()
+        expect:
+        settings.propertyPrefix == 'http'
+        settings.defaultPort == 80
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettingsTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettingsTest.groovy
new file mode 100644
index 0000000..e091f24
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesProxySettingsTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http
+
+import spock.lang.Specification
+
+class JavaSystemPropertiesProxySettingsTest extends Specification {
+
+    def "proxy is not configured when proxyHost property not set"() {
+        expect:
+        def settings = settings(proxyHost, proxyPort, nonProxyHosts)
+        settings.getProxy(requestHost) == null
+
+        where:
+        proxyHost | proxyPort | nonProxyHosts | requestHost
+        null      | null      | null          | null
+        null      | null      | null          | "foo"
+        null      | "111"     | null          | "foo"
+        null      | null      | "foo|bar|baz" | "foo"
+        ""        | null      | null          | null
+        ""        | ""        | null          | null
+        null      | ""        | null          | null
+    }
+
+    private JavaSystemPropertiesProxySettings settings(host, proxyPort, nonProxyHosts) {
+        return new TestSystemProperties(host, proxyPort, null, null, nonProxyHosts)
+    }
+
+    def "proxy is not configured when host is in list of nonproxy hosts"() {
+        expect:
+        settings("proxyHost", "111", nonProxyHosts).getProxy(host)?.host == proxyHost
+
+        where:
+        nonProxyHosts | host      | proxyHost
+        null          | "foo"     | "proxyHost"
+        ""            | "foo"     | "proxyHost"
+        "bar"         | "foo"     | "proxyHost"
+        "foo"         | "foo"     | null
+        "fo"          | "foo"     | "proxyHost"
+        "foo|bar|baz" | "foo"     | null
+        "foo.*"       | "foo.bar" | null
+        "*.bar"       | "foo.bar" | null
+        "*.ba"        | "foo.bar" | "proxyHost"
+        "*"           | "foo"     | null
+        "*"           | "foo"     | null
+        "foo.*|baz"   | "foo.bar" | null
+    }
+
+    def "uses specified port property and default port when port property not set or invalid"() {
+        expect:
+        settings("proxyHost", prop, null).getProxy("host").port == value
+
+        where:
+        prop     | value
+        null     | 80
+        ""       | 80
+        "notInt" | 80
+        "0"      | 0
+        "111"    | 111
+    }
+
+    def "uses specified proxy user and password"() {
+        expect:
+        def proxy = new TestSystemProperties("proxyHost", null, user, password, null).getProxy("host")
+        proxy.credentials?.username == proxyUser
+        proxy.credentials?.password == proxyPassword
+
+        where:
+        user   | password   | proxyUser | proxyPassword
+        "user" | "password" | "user"    | "password"
+        "user" | ""         | "user"    | ""
+        ""     | "password" | null      | null
+        null   | "anything" | null      | null
+    }
+
+    class TestSystemProperties extends JavaSystemPropertiesProxySettings {
+        TestSystemProperties(String proxyHost, String proxyPortString, String proxyUser, String proxyPassword, String nonProxyHostsString) {
+            super('http', 80, proxyHost, proxyPortString, proxyUser, proxyPassword, nonProxyHostsString);
+        }
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettingsTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettingsTest.groovy
new file mode 100644
index 0000000..0fbd9b9
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/JavaSystemPropertiesSecureHttpProxySettingsTest.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.http
+
+import spock.lang.Specification
+
+class JavaSystemPropertiesSecureHttpProxySettingsTest extends Specification {
+
+    def "should use default HTTPS prefix and port"() {
+        JavaSystemPropertiesProxySettings settings = new JavaSystemPropertiesSecureHttpProxySettings()
+        expect:
+        settings.propertyPrefix == 'https'
+        settings.defaultPort == 443
+    }
+}
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentialsTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentialsTest.groovy
new file mode 100644
index 0000000..3dcc1d3
--- /dev/null
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/ntlm/NTLMCredentialsTest.groovy
@@ -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.internal.resource.transport.http.ntlm
+
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+public class NTLMCredentialsTest extends Specification {
+    PasswordCredentials credentials = Mock()
+
+    @Rule
+    public SetSystemProperties systemProperties = new SetSystemProperties()
+
+    def "uses domain when encoded in username"() {
+        credentials.username >> "domain\\username"
+        credentials.password >> "password"
+
+        when:
+        def ntlmCredentials = new NTLMCredentials(credentials)
+
+        then:
+        ntlmCredentials.domain == 'DOMAIN'
+        ntlmCredentials.username == 'username'
+        ntlmCredentials.password == 'password'
+    }
+
+    def "uses domain when encoded in username with forward slash"() {
+        credentials.username >> "domain/username"
+        credentials.password >> "password"
+
+        when:
+        def ntlmCredentials = new NTLMCredentials(credentials)
+
+        then:
+        ntlmCredentials.domain == 'DOMAIN'
+        ntlmCredentials.username == 'username'
+        ntlmCredentials.password == 'password'
+    }
+
+    def "uses default domain when not encoded in username"() {
+        credentials.username >> "username"
+        credentials.password >> "password"
+
+        when:
+        def ntlmCredentials = new NTLMCredentials(credentials)
+
+        then:
+        ntlmCredentials.domain == ''
+        ntlmCredentials.username == 'username'
+        ntlmCredentials.password == 'password'
+    }
+
+    def "uses system property for domain when not encoded in username"() {
+        System.setProperty("http.auth.ntlm.domain", "domain")
+        credentials.username >> "username"
+        credentials.password >> "password"
+
+        when:
+        def ntlmCredentials = new NTLMCredentials(credentials)
+
+        then:
+        ntlmCredentials.domain == 'DOMAIN'
+        ntlmCredentials.username == 'username'
+        ntlmCredentials.password == 'password'
+    }
+
+    def "uses truncated hostname for workstation"() {
+        credentials.username >> "username"
+        credentials.password >> "password"
+
+        when:
+        def ntlmCredentials = new NTLMCredentials(credentials) {
+            protected String getHostName() {
+                return "hostname.domain.org"
+            }
+        }
+
+        then:
+        ntlmCredentials.workstation == 'HOSTNAME'
+    }
+}
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/artifactory_dirlisting.html b/subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/artifactory_dirlisting.html
similarity index 100%
rename from subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/artifactory_dirlisting.html
rename to subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/artifactory_dirlisting.html
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/mavencentral_dirlisting.html b/subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/mavencentral_dirlisting.html
similarity index 100%
rename from subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/mavencentral_dirlisting.html
rename to subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/mavencentral_dirlisting.html
diff --git a/subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/nexus_dirlisting.html b/subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/nexus_dirlisting.html
similarity index 100%
rename from subprojects/core-impl/src/test/resources/org/gradle/api/internal/externalresource/transport/http/nexus_dirlisting.html
rename to subprojects/resources-http/src/test/resources/org/gradle/internal/resource/transport/http/nexus_dirlisting.html
diff --git a/subprojects/resources-s3/resources-s3.gradle b/subprojects/resources-s3/resources-s3.gradle
new file mode 100644
index 0000000..9bed6e1
--- /dev/null
+++ b/subprojects/resources-s3/resources-s3.gradle
@@ -0,0 +1,15 @@
+dependencies {
+    compile project(':resources')
+    compile project(':resourcesHttp')
+    compile project(':core')
+    compile libraries.guava
+    compile libraries.awsS3
+    compile libraries.commons_lang
+    testCompile libraries.groovy
+}
+
+useTestFixtures(project: ":dependencyManagement")
+useTestFixtures(project: ":ivy")
+useTestFixtures(project: ":maven")
+useClassycle()
+strictCompile()
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/AbstractS3DependencyResolutionTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/AbstractS3DependencyResolutionTest.groovy
new file mode 100644
index 0000000..c534588
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/AbstractS3DependencyResolutionTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.resource.s3.fixtures.IvyS3Repository
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Repository
+import org.gradle.integtests.resource.s3.fixtures.S3Resource
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.junit.Rule
+
+abstract class AbstractS3DependencyResolutionTest extends AbstractDependencyResolutionTest {
+
+    @Rule
+    public final S3Server server = new S3Server(temporaryFolder)
+
+    def setup() {
+        executer.withArgument('-i')
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.uri}")
+    }
+
+    String getBucket() {
+        return 'tests3bucket'
+    }
+
+    abstract String getRepositoryPath()
+
+
+    MavenS3Repository getMavenS3Repo() {
+        new MavenS3Repository(server, file(getTestDirectory()), getRepositoryPath(), getBucket())
+    }
+
+    IvyS3Repository getIvyS3Repo() {
+        new IvyS3Repository(server, file(getTestDirectory()), getRepositoryPath(), getBucket())
+    }
+
+    def assertLocallyAvailableLogged(S3Resource... resources) {
+        resources.each {
+            assert output.contains("Found locally available resource with matching checksum: [s3:/${it.relativeFilePath()}")
+        }
+    }
+
+    String mavenAwsRepoDsl() {
+        """
+        repositories {
+            maven {
+                url "${mavenS3Repo.uri}"
+                credentials(AwsCredentials) {
+                    accessKey "someKey"
+                    secretKey "someSecret"
+                }
+            }
+        }
+        """
+    }
+}
+
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy
new file mode 100644
index 0000000..6632f86
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3
+import com.amazonaws.services.s3.model.*
+import com.google.common.base.Optional
+import org.apache.commons.io.IOUtils
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.gradle.internal.credentials.DefaultAwsCredentials
+import org.gradle.internal.resource.transport.aws.s3.S3Client
+import org.gradle.internal.resource.transport.aws.s3.S3ConnectionProperties
+import org.gradle.internal.resource.transport.aws.s3.S3RegionalResource
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Ignore
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class S3ClientIntegrationTest extends Specification {
+
+    public static final String FILE_NAME = "mavenTest.txt"
+    final String accessKey = 'gradle-access-key'
+    final String secret = 'gradle-secret-key'
+    final String bucketName = 'org.gradle.artifacts'
+
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    @Shared
+    DefaultAwsCredentials awsCredentials = new DefaultAwsCredentials()
+
+    @Rule
+    public final S3Server server = new S3Server(temporaryFolder)
+
+    def setup() {
+        awsCredentials.setAccessKey(accessKey)
+        awsCredentials.setSecretKey(secret)
+    }
+
+    @Unroll
+    def "should perform #authenticationType put get and list on an S3 bucket"() {
+        setup:
+        def fileContents = 'This is only a test'
+        File file = temporaryFolder.createFile(FILE_NAME)
+        file << fileContents
+
+        server.stubPutFile(file, "/${bucketName}/maven/release/$FILE_NAME")
+
+        S3ConnectionProperties s3SystemProperties = Mock {
+            getEndpoint() >> Optional.of(server.uri)
+            getProxy() >> Optional.fromNullable(null)
+            getMaxErrorRetryCount() >> Optional.absent()
+        }
+
+        S3Client s3Client = new S3Client(authenticationImpl, s3SystemProperties)
+
+        when:
+        def stream = new FileInputStream(file)
+        def uri = new URI("s3://${bucketName}/maven/release/$FILE_NAME")
+
+        then:
+        s3Client.put(stream, file.length(), uri)
+
+        when:
+        server.stubMetaData(file, "/${bucketName}/maven/release/$FILE_NAME")
+        S3Object data = s3Client.getMetaData(uri)
+        def metadata = data.getObjectMetadata()
+
+        then:
+        metadata.getContentLength() == 0
+        metadata.getETag() ==~ /\w{32}/
+
+        when:
+        server.stubGetFile(file, "/${bucketName}/maven/release/$FILE_NAME")
+
+        then:
+        S3Object object = s3Client.getResource(uri)
+        object.metadata.getContentLength() == fileContents.length()
+        object.metadata.getETag() ==~ /\w{32}/
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream()
+        IOUtils.copyLarge(object.getObjectContent(), outStream);
+        outStream.toString() == fileContents
+
+        when:
+        server.stubListFile(temporaryFolder.testDirectory, bucketName)
+
+        then:
+        def files = s3Client.list(new URI("s3://${bucketName}/maven/release/"))
+        !files.isEmpty()
+        files.each {
+            assert it.contains(".")
+        }
+
+        where:
+        authenticationImpl | authenticationType
+        awsCredentials     | "authenticated"
+        null               | "anonymous"
+    }
+
+    /**
+     * Allows for quickly making real aws requests during development
+     */
+    @Ignore
+    def "should interact with real S3"() {
+        DefaultAwsCredentials credentials = new DefaultAwsCredentials()
+        String bucketName = System.getenv('G_S3_BUCKET')
+        credentials.setAccessKey(System.getenv('G_AWS_ACCESS_KEY_ID'))
+        credentials.setSecretKey(System.getenv('G_AWS_SECRET_ACCESS_KEY'))
+        S3Client s3Client = new S3Client(credentials, new S3ConnectionProperties())
+
+        def fileContents = 'This is only a test'
+        File file = temporaryFolder.createFile(FILE_NAME)
+        file << fileContents
+
+        expect:
+        def stream = new FileInputStream(file)
+        def uri = new URI("s3://${bucketName}/maven/release/${new Date().getTime()}-mavenTest.txt")
+        s3Client.put(stream, file.length(), uri)
+        s3Client.getResource(new URI("s3://${bucketName}/maven/release/idontExist.txt"))
+    }
+
+    @Ignore
+    def "should use region specific endpoints to interact with buckets in all regions"() {
+        setup:
+        String bucketPrefix = 'testv4signatures'
+        DefaultAwsCredentials credentials = new DefaultAwsCredentials()
+        credentials.setAccessKey(System.getenv('G_AWS_ACCESS_KEY_ID'))
+        credentials.setSecretKey(System.getenv('G_AWS_SECRET_ACCESS_KEY'))
+
+        S3Client s3Client = new S3Client(credentials, new S3ConnectionProperties())
+
+        def fileContents = 'This is only a test'
+        File file = temporaryFolder.createFile(FILE_NAME)
+        file << fileContents
+
+        expect:
+        (Region.values() - [Region.US_GovCloud, Region.CN_Beijing]).each { Region region ->
+            String bucketName = "${bucketPrefix}-${region ?: region.name}"
+
+            String key = "/maven/release/test.txt"
+            String regionForUrl = region == Region.US_Standard ? "s3.amazonaws.com" : "s3-${region.getFirstRegionId()}.amazonaws.com"
+            def uri = new URI("s3://${bucketName}.${regionForUrl}${key}")
+
+            S3RegionalResource s3RegionalResource = new S3RegionalResource(uri)
+            s3Client.amazonS3Client.setRegion(s3RegionalResource.region)
+
+
+            println "Regional uri: ${uri}"
+            println("Creating bucket: ${bucketName}")
+            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName, region)
+            s3Client.amazonS3Client.createBucket(createBucketRequest)
+
+            println "-- uploading"
+            s3Client.put(new FileInputStream(file), file.length(), uri)
+
+            println "------Getting object"
+            s3Client.getResource(uri)
+
+            ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
+                    .withBucketName(bucketName)
+
+            ObjectListing objects = s3Client.amazonS3Client.listObjects(listObjectsRequest)
+            objects.objectSummaries.each { S3ObjectSummary summary ->
+                println "-- Deleting object ${summary.getKey()}"
+                DeleteObjectRequest deleteObjectRequest = new DeleteObjectRequest(bucketName, summary.getKey())
+                s3Client.amazonS3Client.deleteObject(deleteObjectRequest)
+            }
+
+            println("Deleting bucket: ${bucketName}")
+            DeleteBucketRequest deleteBucketRequest = new DeleteBucketRequest(bucketName)
+            s3Client.amazonS3Client.deleteBucket(deleteBucketRequest)
+        }
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Module.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Module.groovy
new file mode 100644
index 0000000..a784269
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Module.groovy
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.ivy.IvyDescriptor
+import org.gradle.test.fixtures.ivy.IvyFileModule
+import org.gradle.test.fixtures.ivy.IvyModule
+import org.gradle.test.fixtures.ivy.RemoteIvyModule
+
+class IvyS3Module implements RemoteIvyModule {
+    IvyFileModule backingModule
+    String bucket
+    S3Server server
+    String repositoryPath
+
+    IvyS3Module(S3Server server, IvyFileModule backingModule, String repositoryPath, String bucket) {
+        this.bucket = bucket
+        this.server = server
+        this.backingModule = backingModule
+        this.repositoryPath = repositoryPath
+    }
+
+    @Override
+    void assertPublished() {
+        backingModule.assertPublished()
+    }
+
+    @Override
+    void assertArtifactsPublished(String... names) {
+        backingModule.assertArtifactsPublished(names)
+    }
+
+    @Override
+    void assertPublishedAsJavaModule() {
+        backingModule.assertPublishedAsJavaModule()
+    }
+
+    @Override
+    String getOrganisation() {
+        return backingModule.getOrganisation()
+    }
+
+    @Override
+    S3Artifact getIvy() {
+        return new S3Artifact(server, ivyFile, repositoryPath, bucket)
+    }
+
+    @Override
+    S3Artifact getJar() {
+        return new S3Artifact(server, jarFile, repositoryPath, bucket)
+    }
+
+    @Override
+    String getModule() {
+        return backingModule.module
+    }
+
+    @Override
+    String getRevision() {
+        return backingModule.revision
+    }
+
+    @Override
+    TestFile getIvyFile() {
+        return backingModule.ivyFile
+    }
+
+    @Override
+    TestFile getJarFile() {
+        return backingModule.jarFile
+    }
+
+    @Override
+    IvyModule withNoMetaData() {
+        return backingModule.withNoMetaData()
+    }
+
+    @Override
+    IvyModule withStatus(String status) {
+        return backingModule.withStatus(status)
+    }
+
+    @Override
+    IvyModule dependsOn(String organisation, String module, String revision) {
+        return backingModule.dependsOn(organisation, module, revision)
+    }
+
+    @Override
+    IvyModule extendsFrom(Map<String, ?> attributes) {
+        return backingModule.extendsFrom(attributes)
+    }
+
+    @Override
+    IvyModule dependsOn(Map<String, ?> attributes) {
+        return backingModule.dependsOn(attributes)
+    }
+
+    @Override
+    IvyModule artifact(Map<String, ?> options) {
+        return backingModule.artifact(options)
+    }
+
+    @Override
+    IvyModule undeclaredArtifact(Map<String, ?> options) {
+        return backingModule.undeclaredArtifact(options)
+    }
+
+    @Override
+    IvyModule withXml(Closure action) {
+        return backingModule.withXml(action)
+    }
+
+    @Override
+    IvyModule configuration(String name) {
+        return backingModule.configuration(name)
+    }
+
+    @Override
+    IvyModule configuration(Map<String, ?> options, String name) {
+        return backingModule.configuration(options, name)
+    }
+
+    @Override
+    IvyModule publishWithChangedContent() {
+        return backingModule.publishWithChangedContent()
+    }
+
+    @Override
+    IvyModule publish() {
+        return backingModule.publish()
+    }
+
+    @Override
+    IvyDescriptor getParsedIvy() {
+        return backingModule.getParsedIvy()
+    }
+
+    @Override
+    void assertIvyAndJarFilePublished() {
+        backingModule.assertIvyAndJarFilePublished()
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Repository.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Repository.groovy
new file mode 100644
index 0000000..ba03e42
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/IvyS3Repository.groovy
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.ivy.IvyFileRepository
+import org.gradle.test.fixtures.ivy.RemoteIvyRepository
+
+class IvyS3Repository implements RemoteIvyRepository {
+
+    S3Server server
+    String bucket
+    IvyFileRepository backingRepository
+    String repositoryPath
+
+    public IvyS3Repository(S3Server server, TestFile repoDir, String repositoryPath, String bucket, boolean m2Compatible = false, String dirPattern = null, String ivyFilePattern = null, String artifactFilePattern = null) {
+        assert !bucket.startsWith('/')
+        this.server = server
+        this.bucket = bucket
+        this.backingRepository = new IvyFileRepository(repoDir, m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern)
+        this.repositoryPath = repositoryPath
+    }
+
+    URI getUri() {
+        new URI("s3://${bucket}${repositoryPath}")
+    }
+
+    @Override
+    String getIvyPattern() {
+        return "$uri/${backingRepository.baseIvyPattern}"
+    }
+
+    @Override
+    IvyS3Module module(String organisation, String module) {
+        return new IvyS3Module(server, backingRepository.module(organisation, module), repositoryPath, bucket)
+    }
+
+    @Override
+    IvyS3Module module(String organisation, String module, Object revision) {
+        return new IvyS3Module(server, backingRepository.module(organisation, module, revision), repositoryPath, bucket)
+    }
+
+    String getArtifactPattern() {
+        return "$uri/${backingRepository.baseArtifactPattern}"
+    }
+
+    String getBaseIvyPattern() {
+        return backingRepository.baseIvyPattern
+    }
+
+    String getBaseArtifactPattern() {
+        return backingRepository.baseArtifactPattern
+    }
+
+    S3DirectoryResource directoryList(String organisation, String module) {
+        return new S3DirectoryResource(server, bucket, backingRepository.moduleDir(organisation, module))
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Module.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Module.groovy
new file mode 100644
index 0000000..1139960
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Module.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.maven.DelegatingMavenModule
+import org.gradle.test.fixtures.maven.MavenFileModule
+import org.gradle.test.fixtures.maven.MavenModule
+
+class MavenS3Module extends DelegatingMavenModule<MavenS3Module> implements MavenModule {
+    MavenFileModule backingModule
+    S3Server server
+    String bucket
+    String repositoryPath
+
+    MavenS3Module(S3Server server, MavenFileModule backingModule, String repositoryPath, String bucket) {
+        super(backingModule)
+        this.bucket = bucket
+        this.server = server
+        this.backingModule = backingModule
+        this.repositoryPath = repositoryPath
+    }
+
+    S3Artifact getPom() {
+        return new S3Artifact(server, pomFile, repositoryPath, bucket)
+    }
+
+    S3Artifact getArtifact() {
+        return new S3Artifact(server, artifactFile, repositoryPath, bucket)
+    }
+
+    S3Artifact getMetaData() {
+        new S3Artifact(server, backingModule.metaDataFile, repositoryPath, bucket)
+    }
+
+    S3Artifact getMavenRootMetaData() {
+        new S3Artifact(server, backingModule.rootMetaDataFile, repositoryPath, bucket)
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Repository.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Repository.groovy
new file mode 100644
index 0000000..ae7e200
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/MavenS3Repository.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import org.gradle.test.fixtures.maven.MavenRepository
+
+class MavenS3Repository implements MavenRepository {
+    private final S3Server server
+    private final MavenFileRepository backingRepository
+    private final String bucket
+    private final String repositoryPath
+
+    MavenS3Repository(S3Server server, File repoDir, String repositoryPath, String bucket) {
+        assert !bucket.startsWith('/')
+        this.server = server
+        this.bucket = bucket
+        this.backingRepository = new MavenFileRepository(repoDir.file(bucket + repositoryPath))
+        this.repositoryPath = repositoryPath
+    }
+
+    URI getUri() {
+        new URI("s3://${bucket}${repositoryPath}")
+    }
+
+    MavenS3Module module(String organisation, String module, Object revision = "1.0") {
+        new MavenS3Module(server, backingRepository.module(organisation, module, revision), repositoryPath, bucket)
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Artifact.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Artifact.groovy
new file mode 100644
index 0000000..8898458
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Artifact.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteArtifact
+
+public class S3Artifact extends S3Resource implements RemoteArtifact {
+    public S3Artifact(S3Server server, TestFile file, String repositoryPath, String bucket) {
+        super(server, file, repositoryPath, bucket)
+    }
+
+    @Override
+    public S3Resource getMd5() {
+        return new S3Resource(server, file.parentFile.file(file.name + ".md5"), repositoryPath, bucket)
+    }
+
+    @Override
+    public S3Resource getSha1() {
+        return new S3Resource(server, file.parentFile.file(file.name + ".sha1"), repositoryPath, bucket)
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3DirectoryResource.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3DirectoryResource.groovy
new file mode 100644
index 0000000..f4161a2
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3DirectoryResource.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteResource
+
+class S3DirectoryResource implements RemoteResource {
+
+    private final S3Server server
+    private final TestFile directory
+    private String bucket
+    private final String path
+
+    S3DirectoryResource(S3Server server, String bucket, TestFile directory) {
+        this.bucket = bucket
+        this.directory = directory
+        this.server = server
+        def directoryUri = directory.toURI().toString()
+        this.path = directoryUri.substring(directoryUri.indexOf(bucket) + bucket.length() + 1)
+    }
+
+    URI getUri() {
+        return new URI("s3", bucket, path, null, null)
+    }
+
+    @Override
+    void expectDownload() {
+        expectGet()
+    }
+
+    @Override
+    void expectDownloadBroken() {
+        expectGetBroken()
+    }
+
+    @Override
+    void expectDownloadMissing() {
+        expectGetMissing()
+    }
+
+    @Override
+    void expectMetadataRetrieve() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectMetadataRetrieveMissing() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectMetadataRetrieveBroken() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectParentMkdir() {
+        // Not required
+    }
+
+    @Override
+    void expectParentCheckdir() {
+        // Not required
+    }
+
+    @Override
+    void expectUpload() {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
+    void expectUploadBroken() {
+        throw new UnsupportedOperationException()
+    }
+
+    public void expectGet() {
+        server.stubListFile(directory, bucket, path)
+    }
+
+    public void expectGetMissing() {
+        server.expectGetMissing("/$bucket")
+    }
+
+    public void expectGetBroken() {
+        server.expectGetBroken("/$bucket")
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Resource.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Resource.groovy
new file mode 100644
index 0000000..aa9104d
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Resource.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.resource.RemoteResource
+
+class S3Resource implements RemoteResource {
+    S3Server server
+    TestFile file
+    String bucket
+    String repositoryPath
+
+    S3Resource(S3Server server, TestFile file, String repositoryPath, String bucket) {
+        this.repositoryPath = repositoryPath
+        this.bucket = bucket
+        this.server = server
+        this.file = file
+    }
+
+    @Override
+    URI getUri() {
+        return new URI("s3:/${relativeFilePath()}")
+    }
+
+    @Override
+    void expectDownload() {
+        server.stubGetFile(file, relativeFilePath())
+    }
+
+    @Override
+    void expectDownloadMissing() {
+        def path = relativeFilePath()
+        server.stubFileNotFound(path)
+    }
+
+    void expectDownloadAuthencicationError() {
+        server.stubGetFileAuthFailure(relativeFilePath())
+    }
+
+    @Override
+    void expectMetadataRetrieve() {
+        server.stubMetaData(file, relativeFilePath())
+    }
+
+    @Override
+    void expectMetadataRetrieveMissing() {
+        server.stubMetaDataMissing(relativeFilePath())
+    }
+
+    @Override
+    void expectDownloadBroken() {
+        server.stubGetFileBroken(relativeFilePath())
+    }
+
+    @Override
+    void expectParentMkdir() {
+        // Not required
+    }
+
+    @Override
+    void expectParentCheckdir() {
+        // Not required
+    }
+
+    void expectUpload() {
+        server.stubPutFile(file, relativeFilePath())
+    }
+
+    @Override
+    void expectUploadBroken() {
+        server.stubPutFileAuthFailure(relativeFilePath())
+    }
+
+    @Override
+    void expectMetadataRetrieveBroken() {
+        server.stubMetaDataBroken(relativeFilePath())
+    }
+
+    def relativeFilePath() {
+        String absolute = file.toURI()
+        String base = "/${bucket}$repositoryPath"
+        absolute.substring(absolute.indexOf(base), absolute.length())
+    }
+
+    def expectPutAuthencicationError() {
+        server.stubPutFileAuthFailure(relativeFilePath());
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy
new file mode 100644
index 0000000..3aa8566
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3Server.groovy
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures
+import groovy.xml.StreamingMarkupBuilder
+import org.gradle.integtests.resource.s3.fixtures.stub.HttpStub
+import org.gradle.integtests.resource.s3.fixtures.stub.StubRequest
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.gradle.test.fixtures.server.http.HttpServer
+import org.joda.time.DateTimeZone
+import org.joda.time.format.DateTimeFormat
+import org.joda.time.format.DateTimeFormatter
+import org.joda.time.tz.FixedDateTimeZone
+import org.mortbay.jetty.Request
+import org.mortbay.jetty.handler.AbstractHandler
+
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.security.MessageDigest
+
+class S3Server extends HttpServer implements RepositoryServer {
+
+    public static final String BUCKET_NAME = "tests3bucket"
+    private static final DateTimeZone GMT = new FixedDateTimeZone("GMT", "GMT", 0, 0)
+    protected static final DateTimeFormatter RCF_822_DATE_FORMAT = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss z")
+            .withLocale(Locale.US)
+            .withZone(GMT);
+
+    public static final String ETAG = 'd41d8cd98f00b204e9800998ecf8427e'
+    public static final String X_AMZ_REQUEST_ID = '0A398F9A1BAD4027'
+    public static final String X_AMZ_ID_2 = 'nwUZ/n/F2/ZFRTZhtzjYe7mcXkxCaRjfrJSWirV50lN7HuvhF60JpphwoiX/sMnh'
+    public static final String DATE_HEADER = 'Mon, 29 Sep 2014 11:04:27 GMT'
+    public static final String SERVER_AMAZON_S3 = 'AmazonS3'
+
+    TestDirectoryProvider testDirectoryProvider
+
+    S3Server(TestDirectoryProvider testDirectoryProvider) {
+        super()
+        this.testDirectoryProvider = testDirectoryProvider;
+    }
+
+    @Override
+    protected void before() {
+        start()
+    }
+
+    void assertRequest(HttpStub httpStub, HttpServletRequest request) {
+        StubRequest stubRequest = httpStub.request
+        String path = stubRequest.path
+        assert path.startsWith('/')
+        assert path == request.pathInfo
+        assert stubRequest.method == request.method
+        assert stubRequest.params.every {
+            request.getParameterMap()[it.key] == it.value
+        }
+    }
+
+    boolean requestMatches(HttpStub httpStub, HttpServletRequest request) {
+        StubRequest stubRequest = httpStub.request
+        String path = stubRequest.path
+        assert path.startsWith('/')
+        boolean result = path == request.pathInfo && stubRequest.method == request.method
+        result
+    }
+
+    @Override
+    IvyS3Repository getRemoteIvyRepo() {
+        new IvyS3Repository(this, testDirectoryProvider.testDirectory.file("$BUCKET_NAME/ivy"), "/ivy", BUCKET_NAME)
+    }
+
+    @Override
+    IvyS3Repository getRemoteIvyRepo(boolean m2Compatible, String dirPattern) {
+        new IvyS3Repository(this, testDirectoryProvider.testDirectory.file("$BUCKET_NAME/ivy"), "/ivy", BUCKET_NAME, m2Compatible, dirPattern)
+    }
+
+    @Override
+    IvyS3Repository getRemoteIvyRepo(boolean m2Compatible, String dirPattern, String ivyFilePattern, String artifactFilePattern) {
+        new IvyS3Repository(this, testDirectoryProvider.testDirectory.file("$BUCKET_NAME/ivy"), "/ivy", BUCKET_NAME, m2Compatible, dirPattern, ivyFilePattern, artifactFilePattern)
+    }
+
+    @Override
+    IvyS3Repository getRemoteIvyRepo(String contextPath) {
+        new IvyS3Repository(this, testDirectoryProvider.testDirectory.file("$BUCKET_NAME$contextPath"), "$contextPath", BUCKET_NAME)
+    }
+
+    @Override
+    String getValidCredentials() {
+        return """
+        credentials(AwsCredentials) {
+            accessKey "someKey"
+            secretKey "someSecret"
+        }"""
+    }
+
+    def stubPutFile(File file, String url) {
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'PUT'
+                path = url
+                headers = [
+                        'Content-Type': 'application/octet-stream',
+                        'Connection'  : 'Keep-Alive'
+                ]
+                body = { InputStream content ->
+                    file.parentFile.mkdirs()
+                    file.bytes = content.bytes
+                }
+            }
+            response {
+                status = 200
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        "ETag"            : { calculateEtag(file) },
+                        'Server'          : SERVER_AMAZON_S3
+                ]
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubMetaData(File file, String url) {
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': "application/x-www-form-urlencoded; charset=utf-8",
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 200
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'ETag'            : { calculateEtag(file) },
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Accept-Ranges'   : 'bytes',
+                        'Content-Type'    : 'application/octet-stream',
+                        'Content-Length'  : "0",
+                        'Last-Modified'   : RCF_822_DATE_FORMAT.print(new Date().getTime())
+                ]
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubMetaDataBroken(String url) {
+        stubMetaDataLightWeightGet(url, 500)
+    }
+
+    def stubMetaDataMissing(String url) {
+        stubFileNotFound(url)
+    }
+
+    private stubMetaDataLightWeightGet(String url, int statusCode) {
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': "application/x-www-form-urlencoded; charset=utf-8",
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = statusCode
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+            }
+        }
+       expect(httpStub)
+    }
+
+    def stubGetFile(File file, String url) {
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': "application/x-www-form-urlencoded; charset=utf-8",
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 200
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'ETag'            : { calculateEtag(file) },
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Accept-Ranges'   : 'bytes',
+                        'Content-Type'    : 'application/octet-stream',
+                        'Content-Length'  : { file.length()},
+                        'Last-Modified'   : RCF_822_DATE_FORMAT.print(new Date().getTime())
+                ]
+                body = { file.bytes }
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubListFile(File file, String bucketName, prefix = 'maven/release/', delimiter = '/') {
+        def xml = new StreamingMarkupBuilder().bind {
+            ListBucketResult(xmlns: "http://s3.amazonaws.com/doc/2006-03-01/") {
+                Name(bucketName)
+                Prefix(prefix)
+                Marker()
+                MaxKeys('1000')
+                Delimiter(delimiter)
+                IsTruncated('false')
+                Contents {
+                    Key(prefix)
+                    LastModified('2014-09-21T06:44:09.000Z')
+                    ETag(ETAG)
+                    Size('0')
+                    Owner {
+                        ID("${(1..57).collect { 'a' }.join()}")
+                        DisplayName('me')
+                    }
+                    StorageClass('STANDARD')
+                }
+                file.listFiles().each { currentFile ->
+                    Contents {
+                        Key(prefix + currentFile.name)
+                        LastModified('2014-10-01T13:03:29.000Z')
+                        ETag(ETAG)
+                        Size(currentFile.length())
+                        Owner {
+                            ID("${(1..57).collect { 'a' }.join()}")
+                            DisplayName('me')
+                        }
+                        StorageClass('STANDARD')
+                    }
+                    CommonPrefixes {
+                        Prefix("${prefix}com/")
+                    }
+                }
+                Contents {
+                    Key(prefix + file.name)
+                    LastModified('2014-10-01T13:03:29.000Z')
+                    ETag(ETAG)
+                    Size('19')
+                    Owner {
+                        ID("${(1..57).collect { 'a' }.join()}")
+                        DisplayName('me')
+                    }
+                    StorageClass('STANDARD')
+                }
+                CommonPrefixes {
+                    Prefix("${prefix}com/")
+                }
+            }
+        }
+
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = "/${bucketName}/"
+                headers = [
+                        'Content-Type': "application/x-www-form-urlencoded; charset=utf-8",
+                        'Connection'  : 'Keep-Alive'
+                ]
+                params = [
+                        'prefix'   : [prefix],
+                        'delimiter': [delimiter],
+                        'max-keys' : ["1000"]
+                ]
+            }
+            response {
+                status = 200
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+                body = { xml.toString() }
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubGetFileAuthFailure(String url) {
+        def xml = new StreamingMarkupBuilder().bind {
+            Error() {
+                Code("InvalidAccessKeyId")
+                Message("The AWS Access Key Id you provided does not exist in our records.")
+                AWSAccessKeyId("notRelevant")
+                RequestId("stubbedAuthFailureRequestId")
+                HostId("stubbedAuthFailureHostId")
+            }
+        }
+
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': 'application/octet-stream',
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 403
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+                body = { xml.toString() }
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubPutFileAuthFailure(String url) {
+        def xml = new StreamingMarkupBuilder().bind {
+            Error() {
+                Code("InvalidAccessKeyId")
+                Message("The AWS Access Key Id you provided does not exist in our records.")
+                AWSAccessKeyId("notRelevant")
+                RequestId("stubbedAuthFailureRequestId")
+                HostId("stubbedAuthFailureHostId")
+            }
+        }
+
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'PUT'
+                path = url
+                headers = [
+                        'Content-Type': 'application/octet-stream',
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 403
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+                body = { xml.toString() }
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubFileNotFound(String url) {
+        def xml = new StreamingMarkupBuilder().bind {
+            Error() {
+                Code("NoSuchKey")
+                Message("The specified key does not exist.")
+                Key(url)
+                RequestId("stubbedRequestId")
+                HostId("stubbedHostId")
+            }
+        }
+
+        HttpStub httpStub = HttpStub.stubInteraction {
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': 'application/octet-stream',
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 404
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+                body = { xml.toString() }
+            }
+        }
+        expect(httpStub)
+    }
+
+    def stubGetFileBroken(String url) {
+        HttpStub httpStub = HttpStub.stubInteraction {
+            def xml = new StreamingMarkupBuilder().bind {
+                Error() {
+                    Code("Internal Server Error")
+                    Message("Something went seriously wrong")
+                    Key(url)
+                    RequestId("stubbedRequestId")
+                    HostId("stubbedHostId")
+                }
+            }
+            request {
+                method = 'GET'
+                path = url
+                headers = [
+                        'Content-Type': 'application/octet-stream',
+                        'Connection'  : 'Keep-Alive'
+                ]
+            }
+            response {
+                status = 500
+                headers = [
+                        'x-amz-id-2'      : X_AMZ_ID_2,
+                        'x-amz-request-id': X_AMZ_REQUEST_ID,
+                        'Date'            : DATE_HEADER,
+                        'Server'          : SERVER_AMAZON_S3,
+                        'Content-Type'    : 'application/xml',
+                ]
+                body = { xml.toString() }
+            }
+
+        }
+        expect(httpStub)
+    }
+
+    private expect(HttpStub httpStub) {
+        add(httpStub, stubAction(httpStub))
+    }
+
+    private HttpServer.ActionSupport stubAction(HttpStub httpStub) {
+        new HttpServer.ActionSupport("Generic stub handler") {
+            void handle(HttpServletRequest request, HttpServletResponse response) {
+                if (httpStub.request.body) {
+                    httpStub.request.body.call(request.getInputStream())
+                }
+                httpStub.response?.headers?.each {
+                    response.addHeader(it.key, it.value instanceof Closure ? it.value.call().toString() : it.value.toString())
+                }
+                response.setStatus(httpStub.response.status)
+                if (httpStub.response?.body) {
+                    response.outputStream.bytes = httpStub.response.body.call()
+                }
+            }
+        }
+    }
+
+    private void add(HttpStub httpStub, HttpServer.ActionSupport action) {
+        HttpServer.HttpExpectOne expectation = new HttpServer.HttpExpectOne(action, [httpStub.request.method], httpStub.request.path)
+        expectations << expectation
+        addHandler(new AbstractHandler() {
+            void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) {
+                if (requestMatches(httpStub, request)) {
+                    assertRequest(httpStub, request)
+                    if (expectation.run) {
+                        println("This expectation for the request [${request.method} :${request.pathInfo}] was already handled - skipping")
+                        return
+                    }
+                    if (!((Request) request).isHandled()) {
+                        expectation.run = true
+                        action.handle(request, response)
+                        ((Request) request).setHandled(true)
+                    }
+                }
+            }
+        })
+    }
+
+
+    private calculateEtag(File file) {
+        MessageDigest digest = MessageDigest.getInstance("MD5")
+        digest.update(file.bytes);
+        new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpMessage.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpMessage.groovy
new file mode 100644
index 0000000..c01c594
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpMessage.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures.stub
+
+import groovy.transform.ToString
+
+ at ToString(includeNames = true)
+class HttpMessage {
+    Map<String, ?> headers = [:]
+    def Closure<?> body
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpStub.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpStub.groovy
new file mode 100644
index 0000000..3db2fde
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/HttpStub.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures.stub
+
+import groovy.transform.ToString
+
+ at ToString
+class HttpStub {
+    StubRequest request
+    StubResponse response
+
+    static stubInteraction(Closure<?> closure) {
+        closure.resolveStrategy = Closure.DELEGATE_FIRST
+        HttpStub stub = new HttpStub()
+        closure.delegate = stub
+        closure()
+        stub
+    }
+
+    def request(Closure<?> closure){
+        closure.resolveStrategy = Closure.DELEGATE_FIRST
+        StubRequest stubRequest = new StubRequest()
+        closure.delegate = stubRequest
+        closure()
+        request = stubRequest
+    }
+
+    def response(Closure<?> closure){
+        closure.resolveStrategy = Closure.DELEGATE_FIRST
+        StubResponse stubResponse= new StubResponse()
+        closure.delegate = stubResponse
+        closure()
+        response = stubResponse
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubRequest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubRequest.groovy
new file mode 100644
index 0000000..ad82e23
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubRequest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures.stub
+
+import groovy.transform.ToString
+
+ at ToString(includeNames = true, includeSuper = true)
+class StubRequest extends HttpMessage {
+    String method
+    String path
+    Map<String, List<String>> params = [:]
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubResponse.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubResponse.groovy
new file mode 100644
index 0000000..230c106
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/stub/StubResponse.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.fixtures.stub
+
+import groovy.transform.ToString
+
+ at ToString(includeNames = true, includeSuper = true)
+class StubResponse extends HttpMessage {
+    int status
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy
new file mode 100644
index 0000000..a87961f
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.ivy
+
+import org.gradle.api.publish.ivy.AbstractIvyPublishIntegTest
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.junit.Rule
+
+class IvyPublishS3IntegrationTest extends AbstractIvyPublishIntegTest {
+    @Rule
+    public S3Server server = new S3Server(temporaryFolder)
+
+    def setup() {
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.getUri()}")
+    }
+
+    def "can publish to an S3 Ivy repository"() {
+        given:
+        def ivyRepo = server.remoteIvyRepo
+
+        settingsFile << 'rootProject.name = "publishS3Test"'
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'ivy-publish'
+
+group = 'org.gradle.test'
+version = '1.0'
+
+publishing {
+    repositories {
+        ivy {
+            url "${ivyRepo.uri}"
+            credentials(AwsCredentials) {
+                accessKey "someKey"
+                secretKey "someSecret"
+            }
+        }
+    }
+    publications {
+        ivy(IvyPublication) {
+            from components.java
+        }
+    }
+}
+"""
+
+        when:
+        def module = ivyRepo.module('org.gradle.test', 'publishS3Test', '1.0')
+        module.jar.expectUpload()
+        module.jar.sha1.expectUpload()
+        module.ivy.expectUpload()
+        module.ivy.sha1.expectUpload()
+
+        succeeds 'publish'
+
+        then:
+        module.assertPublishedAsJavaModule()
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3RepoResolveIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3RepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..5b206a1
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3RepoResolveIntegrationTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.ivy
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.integtests.resolve.ivy.AbstractIvyRemoteRepoResolveIntegrationTest
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.junit.Rule
+
+class IvyS3RepoResolveIntegrationTest extends AbstractIvyRemoteRepoResolveIntegrationTest {
+
+    @Rule
+    final S3Server server = new S3Server(this)
+
+    @Override
+    RepositoryServer getServer() {
+        return server
+    }
+
+    protected ExecutionResult succeeds(String... tasks) {
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.uri}")
+        executer.withArgument("-Dorg.gradle.s3.maxErrorRetry=0")
+        result = executer.withTasks(*tasks).run()
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3UploadArchivesIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3UploadArchivesIntegrationTest.groovy
new file mode 100644
index 0000000..ad70187
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyS3UploadArchivesIntegrationTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.ivy
+
+import org.gradle.api.publish.ivy.AbstractIvyRemoteLegacyPublishIntegrationTest
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.junit.Rule
+
+class IvyS3UploadArchivesIntegrationTest extends AbstractIvyRemoteLegacyPublishIntegrationTest {
+    @Rule
+    public S3Server server = new S3Server(temporaryFolder)
+
+    @Override
+    RepositoryServer getServer() {
+        return server
+    }
+
+    def setup() {
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.uri}")
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3ErrorsIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3ErrorsIntegrationTest.groovy
new file mode 100644
index 0000000..e3090c7
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3ErrorsIntegrationTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Repository
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.junit.Rule
+
+class MavenPublishS3ErrorsIntegrationTest extends AbstractIntegrationSpec {
+
+    String mavenVersion = "1.45"
+    String projectName = "publishS3Test"
+    String bucket = 'tests3bucket'
+    String repositoryPath = '/maven/release/'
+
+    @Rule
+    public final S3Server server = new S3Server(temporaryFolder)
+
+    def setup() {
+        executer.withArgument('-i')
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.uri}")
+    }
+
+    def "should fail with an authentication error"() {
+        setup:
+        settingsFile << "rootProject.name = '${projectName}'"
+
+        buildFile << """
+    apply plugin: 'java'
+    apply plugin: 'maven-publish'
+
+    group = "org.gradle"
+    version = '${mavenVersion}'
+
+    publishing {
+        repositories {
+                maven {
+                   url "${mavenS3Repo.uri}"
+                    credentials(AwsCredentials) {
+                        accessKey "someKey"
+                        secretKey "someSecret"
+                    }
+                }
+            }
+        publications {
+            pub(MavenPublication) {
+                from components.java
+            }
+        }
+    }
+    """
+
+        when:
+        def module = mavenS3Repo.module("org.gradle", "publishS3Test", "1.45")
+        module.artifact.expectPutAuthencicationError()
+
+        then:
+        fails 'publish'
+
+        failure.assertHasDescription("Execution failed for task ':publishPubPublicationToMavenRepository'.")
+        failure.assertHasCause("Failed to publish publication 'pub' to repository 'maven'")
+        failure.assertHasCause("Could not write to resource '${module.artifact.uri}'.")
+        failure.assertHasCause("The AWS Access Key Id you provided does not exist in our records.")
+    }
+
+    MavenS3Repository getMavenS3Repo() {
+        new MavenS3Repository(server, file(getTestDirectory()), getRepositoryPath(), getBucket())
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy
new file mode 100644
index 0000000..af4ce48
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Repository
+import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.junit.Rule
+
+class MavenPublishS3IntegrationTest extends AbstractMavenPublishIntegTest {
+    @Rule
+    public S3Server server = new S3Server(temporaryFolder)
+
+    def setup() {
+        executer.withArgument("-Dorg.gradle.s3.endpoint=${server.getUri()}")
+    }
+
+    def "can publish to a S3 Maven repository"() {
+        given:
+        def mavenRepo = new MavenS3Repository(server, file("repo"), "/maven", "tests3Bucket")
+        settingsFile << 'rootProject.name = "publishS3Test"'
+        buildFile << """
+apply plugin: 'java'
+apply plugin: 'maven-publish'
+
+group = 'org.gradle.test'
+version = '1.0'
+
+publishing {
+    repositories {
+        maven {
+            url "${mavenRepo.uri}"
+            credentials(AwsCredentials) {
+                accessKey "someKey"
+                secretKey "someSecret"
+            }
+        }
+    }
+    publications {
+        maven(MavenPublication) {
+            from components.java
+        }
+    }
+}
+"""
+
+        when:
+        def module = mavenRepo.module('org.gradle.test', 'publishS3Test', '1.0')
+        module.artifact.expectUpload()
+        module.artifact.sha1.expectUpload()
+        module.artifact.md5.expectUpload()
+        module.pom.expectUpload()
+        module.pom.sha1.expectUpload()
+        module.pom.md5.expectUpload()
+        module.mavenRootMetaData.expectDownloadMissing()
+        module.mavenRootMetaData.expectUpload()
+        module.mavenRootMetaData.sha1.expectUpload()
+        module.mavenRootMetaData.md5.expectUpload()
+
+        succeeds 'publish'
+
+        then:
+        module.assertPublishedAsJavaModule()
+        module.parsedPom.scopes.isEmpty()
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3ProxiedRepoIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3ProxiedRepoIntegrationTest.groovy
new file mode 100644
index 0000000..5d61c06
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3ProxiedRepoIntegrationTest.groovy
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.integtests.resource.s3.AbstractS3DependencyResolutionTest
+import org.gradle.test.fixtures.server.http.TestProxyServer
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Module
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+
+class MavenS3ProxiedRepoIntegrationTest extends AbstractS3DependencyResolutionTest {
+
+    @Rule
+    TestProxyServer proxyServer = new TestProxyServer(server)
+    @Rule
+    SetSystemProperties systemProperties = new SetSystemProperties()
+
+    final String artifactVersion = "1.85"
+    MavenS3Module module
+
+    @Override
+    def setup() {
+        proxyServer.start()
+        module = getMavenS3Repo().module("org.gradle", "test", artifactVersion)
+    }
+
+    def "should proxy requests using HTTP system proxy settings"() {
+        setup:
+        module.publish()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+        and:
+        module.pom.expectDownload()
+        module.artifact.expectDownload()
+
+        when:
+        executer.withArguments(
+                "-Dorg.gradle.s3.endpoint=${server.uri}",
+                "-Dhttp.proxyHost=localhost",
+                "-Dhttp.proxyPort=${proxyServer.port}",
+                "-Dhttp.nonProxyHosts=foo",
+                "-Dhttp.proxyUser=proxyUser",
+                "-Dhttp.proxyPassword=proxyPassword"
+        )
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        proxyServer.port != server.port
+        proxyServer.requestCount == 2
+    }
+
+    def "should not proxy requests when HTTP system proxy settings has a nonProxyHost rule"() {
+        setup:
+        module.publish()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+        and:
+        module.pom.expectDownload()
+        module.artifact.expectDownload()
+
+        when:
+        executer.withArguments(
+                "-Dorg.gradle.s3.endpoint=${server.uri}",
+                "-Dhttp.proxyHost=localhost",
+                "-Dhttp.proxyPort=${proxyServer.port}",
+                "-Dhttp.nonProxyHosts=localhost",
+                "-Dhttp.proxyUser=proxyUser",
+                "-Dhttp.proxyPassword=proxyPassword"
+        )
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        proxyServer.port != server.port
+        proxyServer.requestCount == 0
+    }
+
+    @Override
+    String getRepositoryPath() {
+        return '/maven/release/'
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy
new file mode 100644
index 0000000..d804ff2
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.api.credentials.AwsCredentials
+import org.gradle.integtests.resource.s3.AbstractS3DependencyResolutionTest
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Module
+import org.gradle.util.TextUtil
+
+class MavenS3RepoErrorsIntegrationTest extends AbstractS3DependencyResolutionTest {
+    final String artifactVersion = "1.85"
+    MavenS3Module module;
+
+    @Override
+    String getRepositoryPath() {
+        return '/maven/release/'
+    }
+
+    def setup() {
+        module = mavenS3Repo.module("org.gradle", "test", artifactVersion)
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+    }
+
+    def "should fail with an AWS S3 authentication error"() {
+        setup:
+        buildFile << mavenAwsRepoDsl()
+        when:
+        module.pom.expectDownloadAuthencicationError()
+        then:
+        fails 'retrieve'
+        and:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Could not resolve org.gradle:test:1.85')
+                .assertHasCause("Could not get resource '${module.pom.uri}'.")
+                .assertHasCause("The AWS Access Key Id you provided does not exist in our records.")
+    }
+
+    def "fails when providing PasswordCredentials with decent error"() {
+        setup:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenS3Repo.uri}"
+        credentials {
+            username "someUserName"
+            password "someSecret"
+        }
+    }
+}
+"""
+
+        when:
+        fails 'retrieve'
+        then:
+        //TODO would be good to have a reference of the wrong configured repository in the error message
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause("Credentials must be an instance of '${AwsCredentials.class.getName()}'.")
+    }
+
+    def "fails when no credentials provided"() {
+        setup:
+        buildFile << """
+repositories {
+    maven {
+        url "${mavenS3Repo.uri}"
+    }
+}
+"""
+
+        when:
+        fails 'retrieve'
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause("AwsCredentials must be set for S3 backed repository.")
+
+    }
+
+    def "should include resource uri when file not found"() {
+        setup:
+        buildFile << mavenAwsRepoDsl()
+        when:
+        module.pom.expectDownloadMissing()
+        module.artifact.expectMetadataRetrieveMissing()
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+        errorOutput.contains(TextUtil.toPlatformLineSeparators(
+                """Could not find org.gradle:test:1.85.
+Searched in the following locations:
+    ${module.pom.uri}
+    ${module.artifact.uri}
+Required by:
+"""))
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoResolveIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..f6e2cf9
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoResolveIntegrationTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.integtests.resource.s3.AbstractS3DependencyResolutionTest
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Module
+
+class MavenS3RepoResolveIntegrationTest extends AbstractS3DependencyResolutionTest {
+
+    @Override
+    String getRepositoryPath() {
+        return '/maven/release/'
+    }
+
+    String artifactVersion = "1.85"
+    MavenS3Module module
+
+    def setup(){
+        module = getMavenS3Repo().module("org.gradle", "test", artifactVersion)
+    }
+
+    def "should not download artifacts when already present in maven home"() {
+        setup:
+        module.publish()
+
+        m2Installation.generateGlobalSettingsFile()
+        def localModule = m2Installation.mavenRepo().module("org.gradle", "test", artifactVersion).publish()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+        and:
+        module.pom.expectMetadataRetrieve()
+        module.pom.sha1.expectDownload()
+        module.artifact.expectMetadataRetrieve()
+        module.artifact.sha1.expectDownload()
+
+        when:
+        using m2Installation
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        localModule.artifactFile.assertIsCopyOf(module.artifactFile)
+        localModule.pomFile.assertIsCopyOf(module.pomFile)
+        file('libs/test-1.85.jar').assertIsCopyOf(module.artifactFile)
+
+        and:
+        assertLocallyAvailableLogged(module.pom, module.artifact)
+    }
+
+    def "should download artifacts when maven local artifacts are different to remote "() {
+        setup:
+        module.publish()
+        m2Installation.generateGlobalSettingsFile()
+        def localModule = m2Installation.mavenRepo().module("org.gradle", "test", artifactVersion).publishWithChangedContent()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+        and:
+        module.pom.expectMetadataRetrieve()
+        module.pom.sha1.expectDownload()
+        module.pom.expectDownload()
+        module.artifact.expectMetadataRetrieve()
+        module.artifact.sha1.expectDownload()
+        module.artifact.expectDownload()
+
+        when:
+        using m2Installation
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        localModule.artifactFile.assertIsDifferentFrom(module.artifactFile)
+        localModule.pomFile.assertIsDifferentFrom(module.pomFile)
+        file('libs/test-1.85.jar').assertIsCopyOf(module.artifactFile)
+    }
+}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3SnapshotRepoIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3SnapshotRepoIntegrationTest.groovy
new file mode 100644
index 0000000..b076942
--- /dev/null
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3SnapshotRepoIntegrationTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.s3.maven
+
+import org.gradle.integtests.resource.s3.AbstractS3DependencyResolutionTest
+import org.gradle.integtests.resource.s3.fixtures.MavenS3Module
+
+class MavenS3SnapshotRepoIntegrationTest extends AbstractS3DependencyResolutionTest {
+
+    String artifactVersion = "1.45-SNAPSHOT"
+    MavenS3Module module
+
+    def setup() {
+        module = getMavenS3Repo().module("org.gradle", "test", artifactVersion)
+    }
+
+    def "resolves a maven snapshot module stored in S3"() {
+        setup:
+        module.publish()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:1.45-SNAPSHOT'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        expect:
+        module.pom.expectDownload()
+        module.metaData.expectDownload()
+        module.artifact.expectDownload()
+
+        and:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('test-1.45-SNAPSHOT.jar')
+    }
+
+    def "resolves a dynamic maven snapshot module stored in S3"() {
+        setup:
+        module.publish()
+
+        and:
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+
+dependencies{
+    compile 'org.gradle:test:1.45-SNAPSHOT+'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+
+        expect:
+        module.mavenRootMetaData.expectDownload()
+        module.metaData.expectDownload()
+        module.pom.expectDownload()
+        module.artifact.expectDownload()
+
+        and:
+        succeeds 'retrieve'
+
+        and:
+        file('libs').assertHasDescendants('test-1.45-SNAPSHOT.jar')
+    }
+
+    def "should download snapshot artifacts when maven local artifacts are different to remote"() {
+        setup:
+        module.publishWithChangedContent()
+
+        m2Installation.generateGlobalSettingsFile()
+        def localModule = m2Installation.mavenRepo().module("org.gradle", "test", artifactVersion).publish()
+
+        buildFile << mavenAwsRepoDsl()
+        buildFile << """
+configurations { compile }
+
+dependencies{
+    compile 'org.gradle:test:$artifactVersion'
+}
+
+task retrieve(type: Sync) {
+    from configurations.compile
+    into 'libs'
+}
+"""
+        and:
+        module.metaData.expectDownload()
+        module.pom.expectDownload()
+        module.artifact.expectDownload()
+
+        when:
+        using m2Installation
+        run 'retrieve'
+
+        then:
+        succeeds 'retrieve'
+
+        and:
+        localModule.artifactFile.assertIsDifferentFrom(module.artifactFile)
+        localModule.pomFile.assertIsDifferentFrom(module.pomFile)
+        file("libs/test-${artifactVersion}.jar").assertIsCopyOf(module.artifactFile)
+    }
+
+    @Override
+    String getRepositoryPath() {
+        return '/maven/snapshot/'
+    }
+
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Client.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Client.java
new file mode 100644
index 0000000..0f41a4f
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Client.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.AmazonServiceException;
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.S3ClientOptions;
+import com.amazonaws.services.s3.model.*;
+import com.google.common.base.Optional;
+import org.gradle.api.credentials.AwsCredentials;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transport.http.HttpProxySettings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class S3Client {
+    private static final Logger LOGGER = LoggerFactory.getLogger(S3Client.class);
+    private static final Pattern FILENAME_PATTERN = Pattern.compile("[^\\/]+\\.*$");
+
+    private AmazonS3Client amazonS3Client;
+    private final S3ConnectionProperties s3ConnectionProperties;
+
+    public S3Client(AmazonS3Client amazonS3Client, S3ConnectionProperties s3ConnectionProperties) {
+        this.s3ConnectionProperties = s3ConnectionProperties;
+        this.amazonS3Client = amazonS3Client;
+    }
+
+    public S3Client(AwsCredentials awsCredentials, S3ConnectionProperties s3ConnectionProperties) {
+        this.s3ConnectionProperties = s3ConnectionProperties;
+        AWSCredentials credentials = awsCredentials == null ? null : new BasicAWSCredentials(awsCredentials.getAccessKey(), awsCredentials.getSecretKey());
+        amazonS3Client = createAmazonS3Client(credentials);
+    }
+
+    private AmazonS3Client createAmazonS3Client(AWSCredentials credentials) {
+        AmazonS3Client amazonS3Client = new AmazonS3Client(credentials, createConnectionProperties());
+        S3ClientOptions clientOptions = new S3ClientOptions();
+        Optional<URI> endpoint = s3ConnectionProperties.getEndpoint();
+        if (endpoint.isPresent()) {
+            amazonS3Client.setEndpoint(endpoint.get().toString());
+            clientOptions.withPathStyleAccess(true);
+        }
+        amazonS3Client.setS3ClientOptions(clientOptions);
+        return amazonS3Client;
+    }
+
+    private ClientConfiguration createConnectionProperties() {
+        ClientConfiguration clientConfiguration = new ClientConfiguration();
+        Optional<HttpProxySettings.HttpProxy> proxyOptional = s3ConnectionProperties.getProxy();
+        if (proxyOptional.isPresent()) {
+            HttpProxySettings.HttpProxy proxy = s3ConnectionProperties.getProxy().get();
+            clientConfiguration.setProxyHost(proxy.host);
+            clientConfiguration.setProxyPort(proxy.port);
+            PasswordCredentials credentials = proxy.credentials;
+            if (credentials != null) {
+                clientConfiguration.setProxyUsername(credentials.getUsername());
+                clientConfiguration.setProxyPassword(credentials.getPassword());
+            }
+        }
+        Optional<Integer> maxErrorRetryCount = s3ConnectionProperties.getMaxErrorRetryCount();
+        if (maxErrorRetryCount.isPresent()) {
+            clientConfiguration.setMaxErrorRetry(maxErrorRetryCount.get());
+        }
+        return clientConfiguration;
+    }
+
+    public void put(InputStream inputStream, Long contentLength, URI destination) {
+        try {
+            S3RegionalResource s3RegionalResource = new S3RegionalResource(destination);
+            String bucketName = s3RegionalResource.getBucketName();
+            String s3BucketKey = s3RegionalResource.getKey();
+            configureClient(s3RegionalResource);
+
+            ObjectMetadata objectMetadata = new ObjectMetadata();
+            objectMetadata.setContentLength(contentLength);
+
+            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, s3BucketKey, inputStream, objectMetadata);
+            LOGGER.debug("Attempting to put resource:[{}] into s3 bucket [{}]", s3BucketKey, bucketName);
+
+            amazonS3Client.putObject(putObjectRequest);
+        } catch (AmazonClientException e) {
+            throw ResourceException.putFailed(destination, e);
+        }
+    }
+
+    public S3Object getMetaData(URI uri) {
+        LOGGER.debug("Attempting to get s3 meta-data: [{}]", uri.toString());
+        //Would typically use GetObjectMetadataRequest but it does not work with v4 signatures
+        return doGetS3Object(uri, true);
+    }
+
+    public S3Object getResource(URI uri) {
+        LOGGER.debug("Attempting to get s3 resource: [{}]", uri.toString());
+        return doGetS3Object(uri, false);
+    }
+
+    public List<String> list(URI parent) {
+        List<String> results = new ArrayList<String>();
+
+        S3RegionalResource s3RegionalResource = new S3RegionalResource(parent);
+        String bucketName = s3RegionalResource.getBucketName();
+        String s3BucketKey = s3RegionalResource.getKey();
+        configureClient(s3RegionalResource);
+
+        ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
+                .withBucketName(bucketName)
+                .withPrefix(s3BucketKey)
+                .withMaxKeys(1000)
+                .withDelimiter("/");
+        ObjectListing objectListing = amazonS3Client.listObjects(listObjectsRequest);
+        results.addAll(resolveResourceNames(objectListing));
+
+        while (objectListing.isTruncated()) {
+            objectListing = amazonS3Client.listNextBatchOfObjects(objectListing);
+            results.addAll(resolveResourceNames(objectListing));
+        }
+        return results;
+    }
+
+    private List<String> resolveResourceNames(ObjectListing objectListing) {
+        List<String> results = new ArrayList<String>();
+        List<S3ObjectSummary> objectSummaries = objectListing.getObjectSummaries();
+        if (null != objectSummaries) {
+            for (S3ObjectSummary objectSummary : objectSummaries) {
+                String key = objectSummary.getKey();
+                String fileName = extractResourceName(key);
+                if (null != fileName) {
+                    results.add(fileName);
+                }
+            }
+        }
+        return results;
+    }
+
+    private String extractResourceName(String key) {
+        Matcher matcher = FILENAME_PATTERN.matcher(key);
+        if (matcher.find()) {
+            String group = matcher.group(0);
+            return group.contains(".") ? group : null;
+        }
+        return null;
+    }
+
+    private S3Object doGetS3Object(URI uri, boolean isLightWeight) {
+        S3RegionalResource s3RegionalResource = new S3RegionalResource(uri);
+        String bucketName = s3RegionalResource.getBucketName();
+        String s3BucketKey = s3RegionalResource.getKey();
+        configureClient(s3RegionalResource);
+
+        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, s3BucketKey);
+        if (isLightWeight) {
+            //Skip content download
+            getObjectRequest.setRange(0, 0);
+        }
+
+        try {
+            return amazonS3Client.getObject(getObjectRequest);
+        } catch (AmazonServiceException e) {
+            String errorCode = e.getErrorCode();
+            if (null != errorCode && errorCode.equalsIgnoreCase("NoSuchKey")) {
+                return null;
+            }
+            throw ResourceException.getFailed(uri, e);
+        }
+    }
+
+    private void configureClient(S3RegionalResource s3RegionalResource) {
+        if (s3ConnectionProperties.getEndpoint().isPresent()) {
+            amazonS3Client.setEndpoint(s3ConnectionProperties.getEndpoint().get().toString());
+        } else {
+            amazonS3Client.setRegion(s3RegionalResource.getRegion());
+        }
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectionProperties.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectionProperties.java
new file mode 100644
index 0000000..5884cee
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectionProperties.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.internal.resource.transport.http.HttpProxySettings;
+import org.gradle.internal.resource.transport.http.JavaSystemPropertiesHttpProxySettings;
+import org.gradle.internal.resource.transport.http.JavaSystemPropertiesSecureHttpProxySettings;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Set;
+
+import static com.amazonaws.services.s3.internal.Constants.S3_HOSTNAME;
+import static java.lang.System.getProperty;
+
+public class S3ConnectionProperties {
+    public static final String S3_ENDPOINT_PROPERTY = "org.gradle.s3.endpoint";
+    //The maximum number of times to retry a request when S3 responds with a http 5xx error
+    public static final String S3_MAX_ERROR_RETRY = "org.gradle.s3.maxErrorRetry";
+    private static final Set<String> SUPPORTED_SCHEMES = Sets.newHashSet("HTTP", "HTTPS");
+
+    private final Optional<URI> endpoint;
+    private final HttpProxySettings proxySettings;
+    private final HttpProxySettings secureProxySettings;
+    private final Optional<Integer> maxErrorRetryCount;
+
+    public S3ConnectionProperties() {
+        endpoint = configureEndpoint(getProperty(S3_ENDPOINT_PROPERTY));
+        proxySettings = new JavaSystemPropertiesHttpProxySettings();
+        secureProxySettings = new JavaSystemPropertiesSecureHttpProxySettings();
+        maxErrorRetryCount = configureErrorRetryCount(getProperty(S3_MAX_ERROR_RETRY));
+    }
+
+    public S3ConnectionProperties(HttpProxySettings proxySettings, HttpProxySettings secureProxySettings, URI endpoint, Integer maxErrorRetryCount) {
+        this.endpoint = Optional.fromNullable(endpoint);
+        this.proxySettings = proxySettings;
+        this.secureProxySettings = secureProxySettings;
+        this.maxErrorRetryCount = Optional.fromNullable(maxErrorRetryCount);
+    }
+
+    private Optional<URI> configureEndpoint(String property) {
+        URI uri = null;
+        if (StringUtils.isNotBlank(property)) {
+            try {
+                uri = new URI(property);
+                if (StringUtils.isBlank(uri.getScheme()) || !SUPPORTED_SCHEMES.contains(uri.getScheme().toUpperCase())) {
+                    throw new IllegalArgumentException("System property [" + S3_ENDPOINT_PROPERTY + "=" + property + "] must have a scheme of 'http' or 'https'");
+                }
+            } catch (URISyntaxException e) {
+                throw new IllegalArgumentException("System property [" + S3_ENDPOINT_PROPERTY + "=" + property + "]  must be a valid URI");
+            }
+        }
+        return Optional.fromNullable(uri);
+    }
+
+    public Optional<URI> getEndpoint() {
+        return endpoint;
+    }
+
+    public Optional<HttpProxySettings.HttpProxy> getProxy() {
+        if (endpoint.isPresent()) {
+            String host = endpoint.get().getHost();
+            if (endpoint.get().getScheme().toUpperCase().equals("HTTP")) {
+                return Optional.fromNullable(proxySettings.getProxy(host));
+            } else {
+                return Optional.fromNullable(secureProxySettings.getProxy(host));
+            }
+        }
+        return Optional.fromNullable(secureProxySettings.getProxy(S3_HOSTNAME));
+    }
+
+    private Optional<Integer> configureErrorRetryCount(String property) {
+        Integer count = null;
+        if (null != property) {
+            count = Ints.tryParse(property);
+            if (null == count || count < 0) {
+                throw new IllegalArgumentException("System property [" + S3_MAX_ERROR_RETRY + "=" + property + "]  must be a valid positive Integer");
+
+            }
+        }
+        return Optional.fromNullable(count);
+    }
+
+    public Optional<Integer> getMaxErrorRetryCount() {
+        return maxErrorRetryCount;
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactory.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactory.java
new file mode 100644
index 0000000..f065a55
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import org.gradle.api.credentials.AwsCredentials;
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.resource.connector.ResourceConnectorSpecification;
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class S3ConnectorFactory implements ResourceConnectorFactory {
+    @Override
+    public Set<String> getSupportedProtocols() {
+        return Collections.singleton("s3");
+    }
+
+    @Override
+    public ExternalResourceConnector createResourceConnector(ResourceConnectorSpecification connectionDetails) {
+        AwsCredentials credentials = connectionDetails.getCredentials(AwsCredentials.class);
+        if(credentials == null) {
+            throw new IllegalArgumentException("AwsCredentials must be set for S3 backed repository.");
+        }
+        return new S3ResourceConnector(new S3Client(credentials, new S3ConnectionProperties()));
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3RegionalResource.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3RegionalResource.java
new file mode 100644
index 0000000..2a491cc
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3RegionalResource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import com.amazonaws.regions.Region;
+import com.amazonaws.regions.RegionUtils;
+import com.amazonaws.regions.Regions;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class S3RegionalResource {
+    private static final Pattern REGIONAL_ENDPOINT_PATTERN = Pattern.compile("^s3:\\/\\/(.+)?\\.s3[.-]([a-z0-9-]+)\\.amazonaws\\.com(\\.[a-z]+)?\\/(.+)");
+    private static final Region DEFAULT_REGION = Region.getRegion(Regions.US_EAST_1);
+
+    private final URI uri;
+    private Region region;
+    private String bucketName;
+    private String key;
+
+    public S3RegionalResource(URI uri) {
+        this.uri = uri;
+        configure();
+    }
+
+    public Region getRegion() {
+        return region;
+    }
+
+    public String getBucketName() {
+        return bucketName;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+
+    private void configure() {
+        Matcher matcher = REGIONAL_ENDPOINT_PATTERN.matcher(uri.toString());
+        if (matcher.find()) {
+            String bucketName = matcher.group(1);
+            String region = matcher.group(2);
+            String key = matcher.group(4);
+            Region derivedRegion;
+            if (region.equals("external-1")) {
+                derivedRegion = Region.getRegion(Regions.US_EAST_1);
+            } else {
+                derivedRegion = RegionUtils.getRegion(region);
+            }
+
+            this.region = derivedRegion;
+            this.bucketName = bucketName;
+            this.key = key;
+        } else {
+            this.region = DEFAULT_REGION;
+            this.bucketName = getBucketName(uri.getHost());
+            this.key = getS3BucketKey(uri);
+        }
+    }
+
+    private String getS3BucketKey(URI destination) {
+        String path = destination.getPath();
+        return path.startsWith("/") ? path.substring(1) : path;
+    }
+
+    private String getBucketName(String bucket) {
+        return bucket.replaceAll("\\.s3\\.amazonaws\\.com", "").replaceAll("\\.s3-external-1\\.amazonaws\\.com", "");
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Resource.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Resource.java
new file mode 100644
index 0000000..965aeb4
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3Resource.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.S3Object;
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Date;
+
+public class S3Resource implements ExternalResourceReadResponse {
+
+    private final S3Object s3Object;
+    private final URI uri;
+
+    public S3Resource(S3Object s3Object, URI uri) {
+        this.s3Object = s3Object;
+        this.uri = uri;
+    }
+
+    public InputStream openStream() throws IOException {
+        return s3Object.getObjectContent();
+    }
+
+    public URI getURI() {
+        return uri;
+    }
+
+    public long getContentLength() {
+        return s3Object.getObjectMetadata().getContentLength();
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        ObjectMetadata objectMetadata = s3Object.getObjectMetadata();
+        Date lastModified = objectMetadata.getLastModified();
+        return new DefaultExternalResourceMetaData(uri,
+                lastModified.getTime(),
+                getContentLength(),
+                s3Object.getObjectMetadata().getContentType(),
+                s3Object.getObjectMetadata().getETag(),
+                null); // Passing null for sha1 - TODO - consider using the etag which is an MD5 hash of the file (when less than 5Gb)
+    }
+
+    @Override
+    public void close() throws IOException {
+        s3Object.close();
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnector.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnector.java
new file mode 100644
index 0000000..33b9d5d
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnector.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.S3Object;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+
+public class S3ResourceConnector implements ExternalResourceConnector {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(S3ResourceConnector.class);
+    private final S3Client s3Client;
+
+    public S3ResourceConnector(S3Client s3Client) {
+        this.s3Client = s3Client;
+    }
+
+    public List<String> list(URI parent) {
+        LOGGER.debug("Listing parent resources: {}", parent);
+        return s3Client.list(parent);
+    }
+
+    public ExternalResourceReadResponse openResource(URI location) {
+        LOGGER.debug("Attempting to get resource: {}", location);
+        S3Object s3Object = s3Client.getResource(location);
+        if (s3Object == null) {
+            return null;
+        }
+        return new S3Resource(s3Object, location);
+    }
+
+    public ExternalResourceMetaData getMetaData(URI location) {
+        LOGGER.debug("Attempting to get resource metadata: {}", location);
+        S3Object s3Object = s3Client.getMetaData(location);
+        if (s3Object == null) {
+            return null;
+        }
+        ObjectMetadata objectMetadata = s3Object.getObjectMetadata();
+        return new DefaultExternalResourceMetaData(location,
+                objectMetadata.getLastModified().getTime(),
+                objectMetadata.getContentLength(),
+                objectMetadata.getContentType(),
+                objectMetadata.getETag(),
+                null); // Passing null for sha1 - TODO - consider using the etag which is an MD5 hash of the file (when less than 5Gb)
+    }
+
+    @Override
+    public void upload(LocalResource resource, URI destination) throws IOException {
+        LOGGER.debug("Attempting to upload stream to : {}", destination);
+        InputStream inputStream = resource.open();
+        try {
+            s3Client.put(inputStream, resource.getContentLength(), destination);
+        } finally {
+            inputStream.close();
+        }
+    }
+}
diff --git a/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourcesPluginServiceRegistry.java b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourcesPluginServiceRegistry.java
new file mode 100644
index 0000000..7291107
--- /dev/null
+++ b/subprojects/resources-s3/src/main/java/org/gradle/internal/resource/transport/aws/s3/S3ResourcesPluginServiceRegistry.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3;
+
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class S3ResourcesPluginServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new GlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class GlobalScopeServices {
+        ResourceConnectorFactory createS3ConnectorFactory() {
+            return new S3ConnectorFactory();
+        }
+    }
+}
diff --git a/subprojects/resources-s3/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/resources-s3/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..4209ce1
--- /dev/null
+++ b/subprojects/resources-s3/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1,2 @@
+org.gradle.internal.resource.transport.aws.s3.S3ResourcesPluginServiceRegistry
+
diff --git a/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ClientTest.groovy b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ClientTest.groovy
new file mode 100644
index 0000000..1afa67e
--- /dev/null
+++ b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ClientTest.groovy
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3
+
+import com.amazonaws.services.s3.AmazonS3Client
+import com.amazonaws.services.s3.model.AmazonS3Exception
+import com.amazonaws.services.s3.model.ObjectListing
+import com.amazonaws.services.s3.model.PutObjectRequest
+import com.amazonaws.services.s3.model.S3ObjectSummary
+import com.google.common.base.Optional
+import org.gradle.internal.credentials.DefaultAwsCredentials
+import org.gradle.internal.resource.ResourceException
+import org.gradle.internal.resource.transport.http.HttpProxySettings
+import spock.lang.Ignore
+import spock.lang.Specification
+
+class S3ClientTest extends Specification {
+    final S3ConnectionProperties s3ConnectionProperties = Mock()
+
+
+    def setup(){
+        _ * s3ConnectionProperties.getEndpoint() >> Optional.absent()
+    }
+
+    def "Should upload to s3"() {
+        given:
+        AmazonS3Client amazonS3Client = Mock()
+        S3Client client = new S3Client(amazonS3Client, s3ConnectionProperties)
+        URI uri = new URI("s3://localhost/maven/snapshot/myFile.txt")
+
+        when:
+        client.put(Mock(InputStream), 12L, uri)
+        then:
+        1 * amazonS3Client.putObject(*_) >> { args ->
+            PutObjectRequest putObjectRequest = args[0]
+            assert putObjectRequest.bucketName == 'localhost'
+            assert putObjectRequest.key == 'maven/snapshot/myFile.txt'
+            assert putObjectRequest.metadata.contentLength == 12
+        }
+    }
+
+    def "should extract file name from s3 listing"() {
+        S3Client s3Client = new S3Client(Mock(AmazonS3Client), s3ConnectionProperties)
+
+        expect:
+        s3Client.extractResourceName(listing) == expected
+
+        where:
+        listing         | expected
+        '/a/b/file.pom' | 'file.pom'
+        '/file.pom'     | 'file.pom'
+        '/file.pom'     | 'file.pom'
+        '/SNAPSHOT/'    | null
+        '/SNAPSHOT/bin' | null
+        '/'             | null
+    }
+
+    def "should resolve resource names from an AWS objectlisting"() {
+        setup:
+        S3Client s3Client = new S3Client(Mock(AmazonS3Client), s3ConnectionProperties)
+        ObjectListing objectListing = Mock()
+        S3ObjectSummary objectSummary = Mock()
+        objectSummary.getKey() >> '/SNAPSHOT/some.jar'
+
+        S3ObjectSummary objectSummary2 = Mock()
+        objectSummary2.getKey() >> '/SNAPSHOT/someOther.jar'
+        objectListing.getObjectSummaries() >> [objectSummary, objectSummary2]
+
+        when:
+        def results = s3Client.resolveResourceNames(objectListing)
+
+        then:
+        results == ['some.jar', 'someOther.jar']
+    }
+
+    def "should make batch call when more than one object listing exists"() {
+        def amazonS3Client = Mock(AmazonS3Client)
+        S3Client s3Client = new S3Client(amazonS3Client, s3ConnectionProperties)
+        def uri = new URI("s3://mybucket.com.au/maven/release/")
+        ObjectListing firstListing = Mock()
+        firstListing.isTruncated() >> true
+
+        ObjectListing secondListing = Mock()
+        secondListing.isTruncated() >> false
+
+        when:
+        s3Client.list(uri)
+
+        then:
+        1 * amazonS3Client.listObjects(_) >> firstListing
+        1 * amazonS3Client.listNextBatchOfObjects(_) >> secondListing
+    }
+
+    def "should apply endpoint override with path style access"() {
+        setup:
+        Optional<URI> someEndpoint = Optional.of(new URI("http://someEndpoint"))
+        S3ConnectionProperties s3Properties = Stub()
+        s3Properties.getEndpoint() >> someEndpoint
+
+        when:
+        S3Client s3Client = new S3Client(credentials(), s3Properties)
+
+        then:
+        s3Client.amazonS3Client.clientOptions.pathStyleAccess == true
+        s3Client.amazonS3Client.endpoint == someEndpoint.get()
+    }
+
+    def "should configure HTTPS proxy"() {
+        setup:
+        S3ConnectionProperties s3Properties = Mock()
+        s3Properties.getProxy() >> Optional.of(new HttpProxySettings.HttpProxy("localhost", 8080, 'username', 'password'))
+        s3Properties.getEndpoint() >> Optional.absent()
+        s3Properties.getMaxErrorRetryCount() >> Optional.absent()
+        when:
+        S3Client s3Client = new S3Client(credentials(), s3Properties)
+
+        then:
+        s3Client.amazonS3Client.clientConfiguration.proxyHost == 'localhost'
+        s3Client.amazonS3Client.clientConfiguration.proxyPort == 8080
+        s3Client.amazonS3Client.clientConfiguration.proxyPassword == 'password'
+        s3Client.amazonS3Client.clientConfiguration.proxyUsername == 'username'
+    }
+
+    @Ignore
+    def "should not configure HTTPS proxy when non-proxied host"() {
+        setup:
+        HttpProxySettings proxySettings = Mock()
+        proxySettings.getProxy(nonProxied) >> null
+
+        S3ConnectionProperties s3Properties = Mock()
+        s3Properties.getProxy() >> Optional.absent()
+        s3Properties.getEndpoint() >> endpointOverride
+        when:
+
+        S3Client s3Client = new S3Client(credentials(), s3Properties)
+        then:
+        s3Client.amazonS3Client.clientConfiguration.proxyHost == null
+        s3Client.amazonS3Client.clientConfiguration.proxyPort == -1
+        s3Client.amazonS3Client.clientConfiguration.proxyPassword == null
+        s3Client.amazonS3Client.clientConfiguration.proxyUsername == null
+
+        where:
+        nonProxied                                               | endpointOverride
+        com.amazonaws.services.s3.internal.Constants.S3_HOSTNAME | Optional.absent()
+        "mydomain.com"                                           | Optional.absent()
+    }
+
+    def "should include uri when meta-data not found"() {
+        AmazonS3Client amazonS3Client = Mock()
+        URI uri = new URI("https://somehost/file.txt")
+        S3Client s3Client = new S3Client(amazonS3Client, s3ConnectionProperties)
+        AmazonS3Exception amazonS3Exception = new AmazonS3Exception("test exception")
+        amazonS3Client.getObject(_) >> { throw amazonS3Exception }
+
+        when:
+        s3Client.getMetaData(uri)
+        then:
+        def ex = thrown(ResourceException)
+        ex.message.startsWith("Could not get resource 'https://somehost/file.txt'")
+    }
+
+    def "should include uri when file not found"() {
+        AmazonS3Client amazonS3Client = Mock()
+        URI uri = new URI("https://somehost/file.txt")
+        S3Client s3Client = new S3Client(amazonS3Client, s3ConnectionProperties)
+        AmazonS3Exception amazonS3Exception = new AmazonS3Exception("test exception")
+        amazonS3Client.getObject(_) >> { throw amazonS3Exception }
+
+        when:
+        s3Client.getResource(uri)
+        then:
+        def ex = thrown(ResourceException)
+        ex.message.startsWith("Could not get resource 'https://somehost/file.txt'")
+    }
+
+    def "should include uri when upload fails"() {
+        AmazonS3Client amazonS3Client = Mock()
+        URI uri = new URI("https://somehost/file.txt")
+        S3Client s3Client = new S3Client(amazonS3Client, s3ConnectionProperties)
+        AmazonS3Exception amazonS3Exception = new AmazonS3Exception("test exception")
+        amazonS3Client.putObject(*_) >> { throw amazonS3Exception }
+
+        when:
+        s3Client.put(Mock(InputStream), 0, uri)
+        then:
+        def ex = thrown(ResourceException)
+        ex.message.startsWith("Could not write to resource 'https://somehost/file.txt'")
+    }
+
+    def credentials() {
+        def credentials = new DefaultAwsCredentials()
+        credentials.setAccessKey("AKey")
+        credentials.setSecretKey("ASecret")
+        credentials
+    }
+}
diff --git a/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectionPropertiesTest.groovy b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectionPropertiesTest.groovy
new file mode 100644
index 0000000..b9afedb
--- /dev/null
+++ b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectionPropertiesTest.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3
+
+import org.gradle.internal.resource.transport.http.HttpProxySettings
+import spock.lang.Specification
+
+import static com.amazonaws.services.s3.internal.Constants.S3_HOSTNAME
+
+class S3ConnectionPropertiesTest extends Specification {
+
+    final S3ConnectionProperties s3ConnectionProperties = new S3ConnectionProperties()
+
+    def "should report invalid scheme"() {
+        when:
+        s3ConnectionProperties.configureEndpoint(endpoint)
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "System property [org.gradle.s3.endpoint=$endpoint] must have a scheme of 'http' or 'https'"
+
+        where:
+        endpoint << ['httpd//somewhere', 'httpd://somewhere', 's3://somewhere']
+    }
+
+    def "should report invalid uri"() {
+        when:
+        s3ConnectionProperties.configureEndpoint('httpdasd%:/ads')
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "System property [org.gradle.s3.endpoint=httpdasd%:/ads]  must be a valid URI"
+    }
+
+    def "should allow case insensitive schemes"() {
+        expect:
+        endpoint == s3ConnectionProperties.configureEndpoint(endpoint).get().toString()
+        where:
+        endpoint << ['http://some', 'httP://some', 'httpS://some', 'HTTpS://some']
+    }
+
+    def "should get secure proxy for default s3 host when no endpoint override present"() {
+        HttpProxySettings.HttpProxy secureProxy = Mock()
+        HttpProxySettings secureHttpProxySettings = Mock()
+
+        1 * secureHttpProxySettings.getProxy(S3_HOSTNAME) >> secureProxy
+
+        when:
+        S3ConnectionProperties properties = new S3ConnectionProperties(Mock(HttpProxySettings), secureHttpProxySettings, null, null)
+
+        then:
+        properties.getProxy().get() == secureProxy
+    }
+
+    def "should get non-secure http proxy for override host"() {
+        String endpoint = "http://someproxy"
+        HttpProxySettings.HttpProxy proxy = Mock()
+        HttpProxySettings httpProxySettings = Mock()
+
+        1 * httpProxySettings.getProxy(_) >> proxy
+
+        when:
+        S3ConnectionProperties properties = new S3ConnectionProperties(httpProxySettings, Mock(HttpProxySettings), new URI(endpoint), null)
+
+        then:
+        properties.getProxy().get() == proxy
+    }
+
+    def "should get secure http proxy for override host"() {
+        String endpoint = "https://someproxy"
+        HttpProxySettings.HttpProxy secureProxy = Mock()
+        HttpProxySettings secureHttpProxySettings = Mock()
+
+        1 * secureHttpProxySettings.getProxy(_) >> secureProxy
+
+        when:
+        S3ConnectionProperties properties = new S3ConnectionProperties(Mock(HttpProxySettings), secureHttpProxySettings, new URI(endpoint), null)
+
+        then:
+        properties.getProxy().get() == secureProxy
+    }
+
+    def "should report invalid maxErrorRetryCount"() {
+        when:
+        s3ConnectionProperties.configureErrorRetryCount(value)
+        then:
+        def ex = thrown(IllegalArgumentException)
+        ex.message == "System property [org.gradle.s3.maxErrorRetry=$value]  must be a valid positive Integer"
+
+        where:
+        value << ['', 'w', '-1', "${Integer.MAX_VALUE + 1}"]
+    }
+}
diff --git a/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactoryTest.groovy b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactoryTest.groovy
new file mode 100644
index 0000000..8e9d5f8
--- /dev/null
+++ b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ConnectorFactoryTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3
+
+import org.gradle.api.credentials.AwsCredentials
+import org.gradle.internal.resource.connector.ResourceConnectorSpecification
+import spock.lang.Specification
+
+class S3ConnectorFactoryTest extends Specification {
+
+    S3ConnectorFactory factory = new S3ConnectorFactory()
+    def "fails when no aws credentials provided"() {
+        setup:
+        def resourceConnectorSpecification = Mock(ResourceConnectorSpecification)
+        1 * resourceConnectorSpecification.getCredentials(AwsCredentials) >> null
+
+        when:
+        factory.createResourceConnector(resourceConnectorSpecification)
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message ==  "AwsCredentials must be set for S3 backed repository."
+    }
+}
diff --git a/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3RegionalResourceTest.groovy b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3RegionalResourceTest.groovy
new file mode 100644
index 0000000..aa115be
--- /dev/null
+++ b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3RegionalResourceTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3
+
+import com.amazonaws.regions.RegionUtils
+import com.amazonaws.regions.Regions
+import com.amazonaws.services.s3.model.Region
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import static com.amazonaws.regions.Region.getRegion
+
+class S3RegionalResourceTest extends Specification {
+
+    @Unroll
+    def "should determine the aws region from virtual hosted urls"() {
+        expect:
+        S3RegionalResource regionalResource = new S3RegionalResource(uri)
+        regionalResource.region == expectedRegion
+        regionalResource.bucketName == epectedBucket
+        regionalResource.key == expectedKey
+
+
+        where:
+        uri                                                                        | expectedRegion                                         | epectedBucket   | expectedKey
+        new URI("s3://somebucket.au/a/b/file.txt")                                 | getRegion(Regions.US_EAST_1)                           | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3.amazonaws.com/a/b/file.txt")                | getRegion(Regions.US_EAST_1)                           | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3-external-1.amazonaws.com/a/b/file.txt")     | getRegion(Regions.US_EAST_1)                           | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3.eu-central-1.amazonaws.com/a/b/file.txt")   | getRegion(Regions.EU_CENTRAL_1)                        | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3-eu-central-1.amazonaws.com/a/b/file.txt")   | getRegion(Regions.EU_CENTRAL_1)                        | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3-ap-southeast-2.amazonaws.com/a/b/file.txt") | getRegion(Regions.AP_SOUTHEAST_2)                      | 'somebucket.au' | 'a/b/file.txt'
+        new URI("s3://somebucket.au.s3.cn-north-1.amazonaws.com.cn/a/b/file.txt")  | RegionUtils.getRegion(Region.CN_Beijing.firstRegionId) | 'somebucket.au' | 'a/b/file.txt'
+    }
+}
diff --git a/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnectorTest.groovy b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnectorTest.groovy
new file mode 100644
index 0000000..716c6f8
--- /dev/null
+++ b/subprojects/resources-s3/src/test/groovy/org/gradle/internal/resource/transport/aws/s3/S3ResourceConnectorTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.aws.s3
+
+import com.amazonaws.services.s3.model.ObjectMetadata
+import com.amazonaws.services.s3.model.S3Object
+import spock.lang.Specification
+
+class S3ResourceConnectorTest extends Specification {
+    URI uri = new URI("http://somewhere")
+
+    def "should list resources"() {
+        S3Client s3Client = Mock()
+        when:
+        new S3ResourceConnector(s3Client).list(uri)
+        then:
+        1 * s3Client.list(uri)
+    }
+
+    def "should get a resource"() {
+        ObjectMetadata objectMetadata = Mock()
+        S3Client s3Client = Mock {
+            1 * getResource(uri) >> Mock(S3Object) {
+                getObjectMetadata() >> objectMetadata
+            }
+        }
+
+        when:
+        def s3Resource = new S3ResourceConnector(s3Client).openResource(uri)
+
+        then:
+        s3Resource != null
+    }
+}
diff --git a/subprojects/resources-sftp/resources-sftp.gradle b/subprojects/resources-sftp/resources-sftp.gradle
new file mode 100644
index 0000000..d50fb8f
--- /dev/null
+++ b/subprojects/resources-sftp/resources-sftp.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 project(':resources')
+    compile project(':core')
+    compile libraries.jsch
+    testCompile libraries.groovy
+}
+
+useTestFixtures(project: ":dependencyManagement")
+useTestFixtures(project: ":ivy")
+useTestFixtures(project: ":maven")
+useTestFixtures()
+useClassycle()
+strictCompile()
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishSftpIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishSftpIntegrationTest.groovy
new file mode 100644
index 0000000..ca8e5b0
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishSftpIntegrationTest.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.api.publish.ivy
+
+import org.gradle.test.fixtures.server.sftp.IvySftpRepository
+import org.gradle.test.fixtures.server.sftp.SFTPServer
+import org.junit.Rule
+import spock.lang.Unroll
+
+ at Unroll
+class IvyPublishSftpIntegrationTest extends AbstractIvyPublishIntegTest {
+
+    @Rule
+    final SFTPServer server = new SFTPServer(this)
+
+    IvySftpRepository getIvySftpRepo(boolean m2Compatible = false, String dirPattern = null) {
+        new IvySftpRepository(server, '/repo', m2Compatible, dirPattern)
+    }
+
+    private void buildAndSettingsFilesForPublishing() {
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'ivy-publish'
+
+            version = '2'
+            group = 'org.group.name'
+
+            publishing {
+                repositories {
+                    ivy {
+                        url "${ivySftpRepo.uri}"
+                        credentials {
+                            username 'sftp'
+                            password 'sftp'
+                        }
+                    }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+    }
+
+    def "can publish to a SFTP repository with layout #layout"() {
+        given:
+        def ivySftpRepo = getIvySftpRepo(m2Compatible)
+        def module = ivySftpRepo.module("org.group.name", "publish", "2")
+
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'ivy-publish'
+
+            version = '2'
+            group = 'org.group.name'
+
+            publishing {
+                repositories {
+                    ivy {
+                        url "${ivySftpRepo.uri}"
+                        credentials {
+                            username 'sftp'
+                            password 'sftp'
+                        }
+                        layout "$layout"
+                    }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+
+        when:
+        module.jar.expectParentMkdir()
+        module.jar.expectFileUpload()
+        // TODO - should not check on each upload to a particular directory
+        module.jar.sha1.expectParentCheckdir()
+        module.jar.sha1.expectFileUpload()
+        module.ivy.expectParentCheckdir()
+        module.ivy.expectFileUpload()
+        module.ivy.sha1.expectParentCheckdir()
+        module.ivy.sha1.expectFileUpload()
+
+        then:
+        succeeds 'publish'
+
+        module.assertIvyAndJarFilePublished()
+        module.jarFile.assertIsCopyOf(file('build/libs/publish-2.jar'))
+
+        where:
+        layout   | m2Compatible
+        'gradle' | false
+        'maven'  | true
+    }
+
+    def "can publish to a SFTP repository with pattern layout and m2Compatible #m2Compatible"() {
+        given:
+        def ivySftpRepo = getIvySftpRepo(m2Compatible, "[module]/[organisation]/[revision]")
+        def module = ivySftpRepo.module("org.group.name", "publish", "2")
+
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'ivy-publish'
+
+            version = '2'
+            group = 'org.group.name'
+
+            publishing {
+                repositories {
+                    ivy {
+                        url "${ivySftpRepo.uri}"
+                        credentials {
+                            username 'sftp'
+                            password 'sftp'
+                        }
+                        layout "pattern", {
+                            artifact "${ivySftpRepo.baseArtifactPattern}"
+                            ivy "${ivySftpRepo.baseIvyPattern}"
+                            m2compatible = $m2Compatible
+                        }
+                    }
+                }
+                publications {
+                    ivy(IvyPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+
+        when:
+        module.jar.expectParentMkdir()
+        module.jar.expectFileUpload()
+        // TODO - should not check on each upload to a particular directory
+        module.jar.sha1.expectParentCheckdir()
+        module.jar.sha1.expectFileUpload()
+        module.ivy.expectParentCheckdir()
+        module.ivy.expectFileUpload()
+        module.ivy.sha1.expectParentCheckdir()
+        module.ivy.sha1.expectFileUpload()
+
+        then:
+        succeeds 'publish'
+
+        module.assertIvyAndJarFilePublished()
+        module.jarFile.assertIsCopyOf(file('build/libs/publish-2.jar'))
+
+        where:
+        m2Compatible << [true, false]
+    }
+
+    def "publishing to a SFTP repo when directory creation fails"() {
+        given:
+        buildAndSettingsFilesForPublishing()
+
+        when:
+        def directory = '/repo/org.group.name/publish/2'
+        directory.tokenize('/').findAll().inject('') { path, token ->
+            def currentPath = "$path/$token"
+            server.expectLstat(currentPath)
+            currentPath
+        }
+        server.expectMkdirBroken('/repo')
+
+        then:
+        fails 'publish'
+        failure.assertHasDescription("Execution failed for task ':publishIvyPublicationToIvyRepository'.")
+                .assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+                .assertHasCause("Could not create resource '${ivySftpRepo.uri}'.")
+    }
+
+    def "publishing to a SFTP repo when file uploading fails"() {
+        given:
+        buildAndSettingsFilesForPublishing()
+        def module = ivySftpRepo.module('org.group.name', 'publish', '2')
+
+        when:
+        module.jar.expectParentMkdir()
+        module.jar.expectUploadBroken()
+
+        then:
+        fails 'publish'
+        failure.assertHasDescription("Execution failed for task ':publishIvyPublicationToIvyRepository'.")
+                .assertHasCause("Failed to publish publication 'ivy' to repository 'ivy'")
+                .assertHasCause("Could not write to resource '${module.jar.uri}'.")
+    }
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvySftpLegacyPublishIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvySftpLegacyPublishIntegrationTest.groovy
new file mode 100644
index 0000000..6bc6611
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/ivy/IvySftpLegacyPublishIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.ivy
+
+import org.gradle.test.fixtures.server.sftp.SFTPServer
+import org.junit.Rule
+
+class IvySftpLegacyPublishIntegrationTest extends AbstractIvyRemoteLegacyPublishIntegrationTest {
+    @Rule
+    final SFTPServer server = new SFTPServer(this)
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishSftpIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishSftpIntegrationTest.groovy
new file mode 100644
index 0000000..99586ab
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishSftpIntegrationTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.publish.maven
+import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
+import org.gradle.test.fixtures.server.sftp.MavenSftpRepository
+import org.gradle.test.fixtures.server.sftp.SFTPServer
+import org.junit.Rule
+
+class MavenPublishSftpIntegrationTest extends AbstractMavenPublishIntegTest {
+    @Rule
+    final SFTPServer server = new SFTPServer(this)
+
+    MavenSftpRepository getMavenSftpRepo() {
+        new MavenSftpRepository(server, '/repo')
+    }
+
+    def "can publish to a SFTP repository"() {
+        given:
+        def mavenSftpRepo = getMavenSftpRepo()
+        def module = mavenSftpRepo.module('org.group.name', 'publish', '2')
+
+        settingsFile << 'rootProject.name = "publish"'
+        buildFile << """
+            apply plugin: 'java'
+            apply plugin: 'maven-publish'
+
+            version = '2'
+            group = 'org.group.name'
+
+            publishing {
+                repositories {
+                    maven {
+                        url "${mavenSftpRepo.uri}"
+                        credentials {
+                            username 'sftp'
+                            password 'sftp'
+                        }
+                    }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+
+        when:
+        module.artifact.expectParentMkdir()
+        module.artifact.expectFileUpload()
+        // TODO - should not check on each upload to a particular directory
+        module.artifact.sha1.expectParentCheckdir()
+        module.artifact.sha1.expectFileUpload()
+        module.artifact.md5.expectParentCheckdir()
+        module.artifact.md5.expectFileUpload()
+
+        module.rootMavenMetadata.expectLstatMissing()
+        module.rootMavenMetadata.expectParentCheckdir()
+        module.rootMavenMetadata.expectFileUpload()
+        module.rootMavenMetadata.sha1.expectParentCheckdir()
+        module.rootMavenMetadata.sha1.expectFileUpload()
+        module.rootMavenMetadata.md5.expectParentCheckdir()
+        module.rootMavenMetadata.md5.expectFileUpload()
+        module.pom.expectParentCheckdir()
+        module.pom.expectFileUpload()
+        module.pom.sha1.expectParentCheckdir()
+        module.pom.sha1.expectFileUpload()
+        module.pom.md5.expectParentCheckdir()
+        module.pom.md5.expectFileUpload()
+
+        and:
+        succeeds 'publish'
+
+        then:
+        module.backingModule.assertPublishedAsJavaModule()
+        module.parsedPom.scopes.isEmpty()
+    }
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/AbstractSftpDependencyResolutionTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/AbstractSftpDependencyResolutionTest.groovy
new file mode 100644
index 0000000..118c906
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/AbstractSftpDependencyResolutionTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.sftp
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.test.fixtures.server.sftp.IvySftpRepository
+import org.gradle.test.fixtures.server.sftp.MavenSftpRepository
+import org.gradle.test.fixtures.server.sftp.SFTPServer
+import org.junit.Rule
+
+class AbstractSftpDependencyResolutionTest extends AbstractDependencyResolutionTest {
+    @Rule final SFTPServer server = new SFTPServer(this)
+
+    MavenSftpRepository getMavenSftpRepo() {
+        new MavenSftpRepository(server, '/repo')
+    }
+
+    IvySftpRepository getIvySftpRepo(boolean m2Compatible, String dirPattern = null) {
+        new IvySftpRepository(server, '/repo', m2Compatible, dirPattern)
+    }
+
+    IvySftpRepository getIvySftpRepo(String contextPath) {
+        new IvySftpRepository(server, contextPath, false, null)
+    }
+
+    IvySftpRepository getIvySftpRepo() {
+        new IvySftpRepository(server, '/repo')
+    }
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoDynamicRevisionIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoDynamicRevisionIntegrationTest.groovy
new file mode 100644
index 0000000..e2a4262
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoDynamicRevisionIntegrationTest.groovy
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.sftp.ivy
+
+import org.gradle.integtests.resolve.resource.sftp.AbstractSftpDependencyResolutionTest
+
+class IvySftpRepoDynamicRevisionIntegrationTest extends AbstractSftpDependencyResolutionTest {
+    def "uses latest version from version range and latest status"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+
+            configurations { compile }
+
+            dependencies {
+                compile group: "group", name: "projectA", version: "1.+"
+                compile group: "group", name: "projectB", version: "latest.integration"
+            }
+
+            configurations.all {
+                resolutionStrategy.cacheDynamicVersionsFor 0, "seconds"
+            }
+
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when: "Version 1.1 is published"
+        def projectA1 = ivySftpRepo.module("group", "projectA", "1.1")
+        projectA1.publish()
+        ivySftpRepo.module("group", "projectA", "2.0").publish()
+        def projectB1 = ivySftpRepo.module("group", "projectB", "1.1")
+        projectB1.publish()
+
+        and:
+        server.expectStat('/repo/group/projectA/')
+        server.expectDirectoryList('/repo/group/projectA/')
+
+        projectA1.ivy.expectMetadataRetrieve()
+        projectA1.ivy.expectFileDownload()
+
+        server.expectStat('/repo/group/projectB/')
+        server.expectDirectoryList('/repo/group/projectB/')
+
+        projectB1.ivy.expectMetadataRetrieve()
+        projectB1.ivy.expectFileDownload()
+
+        projectA1.jar.expectMetadataRetrieve()
+        projectA1.jar.expectFileDownload()
+
+        projectB1.jar.expectMetadataRetrieve()
+        projectB1.jar.expectFileDownload()
+
+        and:
+        run 'retrieve'
+
+        then: "Version 1.1 is used"
+        file('libs').assertHasDescendants('projectA-1.1.jar', 'projectB-1.1.jar')
+        file('libs/projectA-1.1.jar').assertIsCopyOf(projectA1.jarFile)
+        file('libs/projectB-1.1.jar').assertIsCopyOf(projectB1.jarFile)
+
+        when: "New versions are published"
+        server.resetExpectations()
+        def projectA2 = ivySftpRepo.module("group", "projectA", "1.2")
+        projectA2.publish()
+        def projectB2 = ivySftpRepo.module("group", "projectB", "2.2")
+        projectB2.publish()
+
+        and:
+        server.expectStat('/repo/group/projectA/')
+        server.expectDirectoryList('/repo/group/projectA/')
+
+        projectA2.ivy.expectMetadataRetrieve()
+        projectA2.ivy.expectFileDownload()
+
+        server.expectStat('/repo/group/projectB/')
+        server.expectDirectoryList('/repo/group/projectB/')
+
+        projectB2.ivy.expectMetadataRetrieve()
+        projectB2.ivy.expectFileDownload()
+
+        projectA2.jar.expectMetadataRetrieve()
+        projectA2.jar.expectFileDownload()
+
+        projectB2.jar.expectMetadataRetrieve()
+        projectB2.jar.expectFileDownload()
+
+        and:
+        run 'retrieve'
+
+        then: "New versions are used"
+        file('libs').assertHasDescendants('projectA-1.2.jar', 'projectB-2.2.jar')
+        file('libs/projectA-1.2.jar').assertIsCopyOf(projectA2.jarFile)
+        file('libs/projectB-2.2.jar').assertIsCopyOf(projectB2.jarFile)
+    }
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy
new file mode 100644
index 0000000..096713b
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.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.integtests.resolve.resource.sftp.ivy
+
+import org.gradle.integtests.resolve.resource.sftp.AbstractSftpDependencyResolutionTest
+
+class IvySftpRepoErrorsIntegrationTest extends AbstractSftpDependencyResolutionTest {
+    void "resolve missing dependencies from a SFTP Ivy repository"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        def module = ivySftpRepo.module('org.group.name', 'projectA', '1.2')
+
+        when:
+        module.ivy.expectMetadataRetrieve()
+        module.jar.expectMetadataRetrieve()
+
+        then:
+        fails 'retrieve'
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause("""Could not find org.group.name:projectA:1.2.
+Searched in the following locations:
+    ${module.ivy.uri}
+    ${module.jar.uri}
+Required by:
+""")
+    }
+
+    void "resolve missing dynamic dependencies from a SFTP Ivy repository"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.+' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        server.expectStat('/repo/org.group.name/projectA/')
+
+        then:
+        fails 'retrieve'
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause("""Could not find any matches for org.group.name:projectA:1.+ as no versions of org.group.name:projectA are available.
+Searched in the following locations:
+    ${ivySftpRepo.uri}/org.group.name/projectA/
+Required by:
+""")
+    }
+
+    void "resolve dependencies from a SFTP Ivy repository with invalid credentials"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'bad'
+                        password 'credentials'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Could not resolve org.group.name:projectA:1.2')
+                .assertHasCause("Password authentication not supported or invalid credentials for SFTP server at ${ivySftpRepo.serverUri}")
+    }
+
+    void "resolve dependencies from a SFTP Ivy repository with unsupported password authentication"() {
+        given:
+        server.withPasswordAuthenticationDisabled()
+        and:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        fails 'retrieve'
+
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Could not resolve org.group.name:projectA:1.2')
+                .assertHasCause("Password authentication not supported or invalid credentials for SFTP server at ${ivySftpRepo.serverUri}")
+    }
+
+    void "resolve dependencies from an unreachable SFTP Ivy repository"() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        server.stop()
+
+        then:
+        fails 'retrieve'
+
+        and:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Could not resolve org.group.name:projectA:1.2')
+                .assertHasCause("Could not connect to SFTP server at ${ivySftpRepo.serverUri}")
+    }
+
+    void 'resolve dependencies from a SFTP Ivy that returns a failure'() {
+        given:
+        buildFile << """
+            repositories {
+                ivy {
+                    url "${ivySftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+
+        when:
+        def projectA = ivySftpRepo.module('org.group.name', 'projectA', '1.2')
+        projectA.ivy.expectMetadataRetrieveBroken()
+
+        and:
+        failure = executer.withStackTraceChecksDisabled().withTasks('retrieve').runWithFailure()
+
+        then:
+        failure.assertHasDescription("Could not resolve all dependencies for configuration ':compile'.")
+                .assertHasCause('Could not resolve org.group.name:projectA:1.2')
+                .assertHasCause("Could not get resource '${projectA.ivy.uri}'")
+    }
+}
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoResolveIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..0f9f910
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoResolveIntegrationTest.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.integtests.resolve.resource.sftp.ivy
+
+import org.gradle.integtests.resolve.ivy.AbstractIvyRemoteRepoResolveIntegrationTest
+import org.gradle.test.fixtures.server.RepositoryServer
+import org.gradle.test.fixtures.server.sftp.SFTPServer
+import org.junit.Rule
+
+class IvySftpRepoResolveIntegrationTest extends AbstractIvyRemoteRepoResolveIntegrationTest {
+
+    @Rule
+    final SFTPServer server = new SFTPServer(this)
+
+    @Override
+    RepositoryServer getServer() {
+        return server
+    }
+}
+
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/maven/MavenSftpRepoResolveIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/maven/MavenSftpRepoResolveIntegrationTest.groovy
new file mode 100644
index 0000000..43340a2
--- /dev/null
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/maven/MavenSftpRepoResolveIntegrationTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.sftp.maven
+
+import org.gradle.integtests.resolve.resource.sftp.AbstractSftpDependencyResolutionTest
+
+class MavenSftpRepoResolveIntegrationTest extends AbstractSftpDependencyResolutionTest {
+
+    void "can resolve dependencies from a SFTP Maven repository"() {
+        given:
+        def mavenSftpRepo = getMavenSftpRepo()
+        def module = mavenSftpRepo.module('org.group.name', 'projectA', '1.2')
+        module.publish()
+
+        and:
+        buildFile << """
+            repositories {
+                maven {
+                    url "${mavenSftpRepo.uri}"
+                    credentials {
+                        username 'sftp'
+                        password 'sftp'
+                    }
+                }
+            }
+            configurations { compile }
+            dependencies { compile 'org.group.name:projectA:1.2' }
+            task retrieve(type: Sync) {
+                from configurations.compile
+                into 'libs'
+            }
+        """
+
+        when:
+        module.pom.expectMetadataRetrieve()
+        module.pom.expectFileDownload()
+
+        module.artifact.expectMetadataRetrieve()
+        module.artifact.expectFileDownload()
+
+        then:
+        succeeds 'retrieve'
+        file('libs').assertHasDescendants 'projectA-1.2.jar'
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/LockableSftpClient.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/LockableSftpClient.java
new file mode 100644
index 0000000..f8a2fdb
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/LockableSftpClient.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import org.gradle.internal.concurrent.Stoppable;
+
+public interface LockableSftpClient extends Stoppable {
+    SftpHost getHost();
+    ChannelSftp getSftpClient();
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpClientFactory.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpClientFactory.java
new file mode 100644
index 0000000..2407ec7
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpClientFactory.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.jcraft.jsch.*;
+import net.jcip.annotations.ThreadSafe;
+import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.concurrent.Stoppable;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.List;
+
+ at ThreadSafe
+public class SftpClientFactory implements Stoppable {
+    private static final Logger LOGGER = LoggerFactory.getLogger(SftpClientFactory.class);
+
+    private SftpClientCreator sftpClientCreator = new SftpClientCreator();
+    private final Object lock = new Object();
+    private final ListMultimap<SftpHost, LockableSftpClient> clients = ArrayListMultimap.create();
+
+    public LockableSftpClient createSftpClient(URI uri, PasswordCredentials credentials) {
+        synchronized (lock) {
+            SftpHost sftpHost = new SftpHost(uri, credentials);
+            return acquireClient(sftpHost);
+        }
+    }
+
+    private LockableSftpClient acquireClient(SftpHost sftpHost) {
+        return clients.containsKey(sftpHost) ? reuseExistingOrCreateNewClient(sftpHost) : sftpClientCreator.createNewClient(sftpHost);
+    }
+
+    private LockableSftpClient reuseExistingOrCreateNewClient(SftpHost sftpHost) {
+        List<LockableSftpClient> clientsByHost = clients.get(sftpHost);
+        if (clientsByHost.isEmpty()) {
+            return sftpClientCreator.createNewClient(sftpHost);
+        }
+        return clientsByHost.remove(0);
+    }
+
+    private static class SftpClientCreator {
+        private JSch jsch;
+
+        public LockableSftpClient createNewClient(SftpHost sftpHost) {
+            try {
+                Session session = createJsch().getSession(sftpHost.getUsername(), sftpHost.getHostname(), sftpHost.getPort());
+                session.setPassword(sftpHost.getPassword());
+                session.connect();
+                Channel channel = session.openChannel("sftp");
+                channel.connect();
+                return new DefaultLockableSftpClient(sftpHost, (ChannelSftp) channel, session);
+            } catch (JSchException e) {
+                URI serverUri = URI.create(String.format("sftp://%s:%d", sftpHost.getHostname(), sftpHost.getPort()));
+                if (e.getMessage().equals("Auth fail")) {
+                    throw new ResourceException(serverUri, String.format("Password authentication not supported or invalid credentials for SFTP server at %s", serverUri), e);
+                }
+                throw new ResourceException(serverUri, String.format("Could not connect to SFTP server at %s", serverUri), e);
+            }
+        }
+
+        private JSch createJsch() {
+            if (jsch == null) {
+                JSch.setConfig("PreferredAuthentications", "password");
+                JSch.setConfig("MaxAuthTries", "1");
+                jsch = new JSch();
+                if(LOGGER.isDebugEnabled()) {
+                    JSch.setLogger(new com.jcraft.jsch.Logger() {
+                        public boolean isEnabled(int level) {
+                            return true;
+                        }
+                        public void log(int level, String message) {
+                            LOGGER.debug(message);
+                        }
+                    });
+                }
+                jsch.setHostKeyRepository(new HostKeyRepository() {
+                    public int check(String host, byte[] key) {
+                        return HostKeyRepository.OK;
+                    }
+
+                    public void add(HostKey hostkey, UserInfo ui) {
+                    }
+
+                    public void remove(String host, String type) {
+                    }
+
+                    public void remove(String host, String type, byte[] key) {
+                    }
+
+                    public String getKnownHostsRepositoryID() {
+                        return "allow-everything";
+                    }
+
+                    public HostKey[] getHostKey() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public HostKey[] getHostKey(String host, String type) {
+                        return new HostKey[0];
+                    }
+                });
+            }
+            return jsch;
+        }
+    }
+
+    public void releaseSftpClient(LockableSftpClient sftpClient) {
+        synchronized (lock) {
+            clients.put(sftpClient.getHost(), sftpClient);
+        }
+    }
+
+    public void stop() {
+        synchronized (lock) {
+            try {
+                CompositeStoppable stoppable = new CompositeStoppable();
+                for (LockableSftpClient client : clients.values()) {
+                    stoppable.add(client);
+                }
+                stoppable.stop();
+            } finally {
+                clients.clear();
+            }
+        }
+    }
+
+    private static class DefaultLockableSftpClient implements LockableSftpClient {
+        final SftpHost host;
+        final ChannelSftp channelSftp;
+        final Session session;
+
+        private DefaultLockableSftpClient(SftpHost host, ChannelSftp channelSftp, Session session) {
+            this.host = host;
+            this.channelSftp = channelSftp;
+            this.session = session;
+        }
+
+        public void stop() {
+            channelSftp.disconnect();
+            session.disconnect();
+        }
+
+        public SftpHost getHost() {
+            return host;
+        }
+
+        public ChannelSftp getSftpClient() {
+            return channelSftp;
+        }
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpConnectorFactory.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpConnectorFactory.java
new file mode 100644
index 0000000..a34acbe
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpConnectorFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp;
+
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.resource.connector.ResourceConnectorSpecification;
+import org.gradle.internal.resource.transfer.DefaultExternalResourceConnector;
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class SftpConnectorFactory implements ResourceConnectorFactory {
+    private final SftpClientFactory sftpClientFactory;
+
+    public SftpConnectorFactory(SftpClientFactory sftpClientFactory) {
+        this.sftpClientFactory = sftpClientFactory;
+    }
+
+    @Override
+    public Set<String> getSupportedProtocols() {
+        return Collections.singleton("sftp");
+    }
+
+    @Override
+    public ExternalResourceConnector createResourceConnector(ResourceConnectorSpecification connectionDetails) {
+        PasswordCredentials passwordCredentials = connectionDetails.getCredentials(PasswordCredentials.class);
+        SftpResourceAccessor accessor = new SftpResourceAccessor(sftpClientFactory, passwordCredentials);
+        SftpResourceLister lister = new SftpResourceLister(sftpClientFactory, passwordCredentials);
+        SftpResourceUploader uploader = new SftpResourceUploader(sftpClientFactory, passwordCredentials);
+        return new DefaultExternalResourceConnector(accessor, lister, uploader);
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpHost.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpHost.java
new file mode 100644
index 0000000..c9152b8
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpHost.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp;
+
+import org.gradle.internal.resource.PasswordCredentials;
+
+import java.net.URI;
+
+public class SftpHost {
+    private final String hostname;
+    private final int port;
+    private final String username;
+    private final String password;
+
+    public SftpHost(URI uri, PasswordCredentials credentials) {
+        hostname = uri.getHost();
+        port = uri.getPort();
+        username = credentials.getUsername();
+        password = credentials.getPassword();
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        SftpHost sftpHost = (SftpHost) o;
+
+        if (port != sftpHost.port) {
+            return false;
+        }
+        if (!hostname.equals(sftpHost.hostname)) {
+            return false;
+        }
+        if (password != null ? !password.equals(sftpHost.password) : sftpHost.password != null) {
+            return false;
+        }
+        if (username != null ? !username.equals(sftpHost.username) : sftpHost.username != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = hostname.hashCode();
+        result = 31 * result + port;
+        result = 31 * result + (username != null ? username.hashCode() : 0);
+        result = 31 * result + (password != null ? password.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s:%d (Username: %s)", hostname, port, username);
+    }
+}
\ No newline at end of file
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResource.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResource.java
new file mode 100644
index 0000000..5c6cd9e
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResource.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.internal.resource.transport.sftp;
+
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+
+import java.io.InputStream;
+import java.net.URI;
+
+public class SftpResource implements ExternalResourceReadResponse {
+
+    private final SftpClientFactory clientFactory;
+    private final ExternalResourceMetaData metaData;
+    private final URI uri;
+    private final PasswordCredentials credentials;
+
+    private LockableSftpClient client;
+
+    public SftpResource(SftpClientFactory clientFactory, ExternalResourceMetaData metaData, URI uri, PasswordCredentials credentials) {
+        this.clientFactory = clientFactory;
+        this.metaData = metaData;
+        this.uri = uri;
+        this.credentials = credentials;
+    }
+
+    @Override
+    public InputStream openStream() {
+        client = clientFactory.createSftpClient(uri, credentials);
+        try {
+            return client.getSftpClient().get(uri.getPath());
+        } catch (com.jcraft.jsch.SftpException e) {
+            throw ResourceException.getFailed(uri, e);
+        }
+    }
+
+    public URI getURI() {
+        return uri;
+    }
+
+    public boolean isLocal() {
+        return false;
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        return metaData;
+    }
+
+    public void close() {
+        clientFactory.releaseSftpClient(client);
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceAccessor.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceAccessor.java
new file mode 100644
index 0000000..826cfbc
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceAccessor.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.internal.resource.transport.sftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.SftpATTRS;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+import org.gradle.internal.resource.transfer.ExternalResourceAccessor;
+import org.gradle.internal.resource.transfer.ExternalResourceReadResponse;
+
+import java.net.URI;
+
+public class SftpResourceAccessor implements ExternalResourceAccessor {
+
+    private final SftpClientFactory sftpClientFactory;
+    private final PasswordCredentials credentials;
+
+    public SftpResourceAccessor(SftpClientFactory sftpClientFactory, PasswordCredentials credentials) {
+        this.sftpClientFactory = sftpClientFactory;
+        this.credentials = credentials;
+    }
+
+    public ExternalResourceMetaData getMetaData(URI uri) {
+        LockableSftpClient sftpClient = sftpClientFactory.createSftpClient(uri, credentials);
+        try {
+            SftpATTRS attributes = sftpClient.getSftpClient().lstat(uri.getPath());
+            return attributes != null ? toMetaData(uri, attributes) : null;
+        } catch (com.jcraft.jsch.SftpException e) {
+            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                return null;
+            }
+            throw ResourceException.getFailed(uri, e);
+        } finally {
+            sftpClientFactory.releaseSftpClient(sftpClient);
+        }
+    }
+
+    private ExternalResourceMetaData toMetaData(URI uri, SftpATTRS attributes) {
+        long lastModified = -1;
+        long contentLength = -1;
+
+        if ((attributes.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) != 0) {
+            lastModified = attributes.getMTime() * 1000;
+        }
+        if ((attributes.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) != 0) {
+            contentLength = attributes.getSize();
+        }
+
+        return new DefaultExternalResourceMetaData(uri, lastModified, contentLength);
+    }
+
+    public ExternalResourceReadResponse openResource(URI location) {
+        ExternalResourceMetaData metaData = getMetaData(location);
+        return metaData != null ? new SftpResource(sftpClientFactory, metaData, location, credentials) : null;
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceLister.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceLister.java
new file mode 100644
index 0000000..12be299
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceLister.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.internal.resource.transport.sftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transfer.ExternalResourceLister;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+public class SftpResourceLister implements ExternalResourceLister {
+    private final SftpClientFactory sftpClientFactory;
+    private final PasswordCredentials credentials;
+
+    public SftpResourceLister(SftpClientFactory sftpClientFactory, PasswordCredentials credentials) {
+        this.sftpClientFactory = sftpClientFactory;
+        this.credentials = credentials;
+    }
+
+    public List<String> list(URI directory) {
+        LockableSftpClient client = sftpClientFactory.createSftpClient(directory, credentials);
+
+        try {
+            @SuppressWarnings("unchecked")
+            Vector<ChannelSftp.LsEntry> entries = client.getSftpClient().ls(directory.getPath());
+            List<String> list = new ArrayList<String>();
+            for (ChannelSftp.LsEntry entry : entries) {
+                list.add(entry.getFilename());
+            }
+            return list;
+        } catch (com.jcraft.jsch.SftpException e) {
+            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                return null;
+            }
+            throw new ResourceException(directory, String.format("Could not list children for resource '%s'.", directory), e);
+        } finally {
+            sftpClientFactory.releaseSftpClient(client);
+        }
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceUploader.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceUploader.java
new file mode 100644
index 0000000..6bb84a4
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourceUploader.java
@@ -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.internal.resource.transport.sftp;
+
+import com.jcraft.jsch.ChannelSftp;
+import org.apache.commons.io.FilenameUtils;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.PasswordCredentials;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.transfer.ExternalResourceUploader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class SftpResourceUploader implements ExternalResourceUploader {
+
+    private final SftpClientFactory sftpClientFactory;
+    private final PasswordCredentials credentials;
+
+    public SftpResourceUploader(SftpClientFactory sftpClientFactory, PasswordCredentials credentials) {
+        this.sftpClientFactory = sftpClientFactory;
+        this.credentials = credentials;
+    }
+
+    @Override
+    public void upload(LocalResource resource, URI destination) throws IOException {
+        LockableSftpClient client = sftpClientFactory.createSftpClient(destination, credentials);
+
+        try {
+            ChannelSftp channel = client.getSftpClient();
+            ensureParentDirectoryExists(channel, destination);
+            InputStream sourceStream = resource.open();
+            try {
+                channel.put(sourceStream, destination.getPath());
+            } finally {
+                sourceStream.close();
+            }
+        } catch (com.jcraft.jsch.SftpException e) {
+            throw ResourceException.putFailed(destination, e);
+        } finally {
+            sftpClientFactory.releaseSftpClient(client);
+        }
+    }
+
+    private void ensureParentDirectoryExists(ChannelSftp channel, URI uri) {
+        String parentPath = FilenameUtils.getFullPathNoEndSeparator(uri.getPath());
+        if (parentPath.equals("")) {
+            return;
+        }
+        URI parent = uri.resolve(parentPath);
+
+        try {
+            channel.lstat(parentPath);
+            return;
+        } catch (com.jcraft.jsch.SftpException e) {
+            if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                throw new ResourceException(parent, String.format("Could not lstat resource '%s'.", parent), e);
+            }
+        }
+        ensureParentDirectoryExists(channel, parent);
+        try {
+            channel.mkdir(parentPath);
+        } catch (com.jcraft.jsch.SftpException e) {
+            throw new ResourceException(parent, String.format("Could not create resource '%s'.", parent), e);
+        }
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourcesPluginServiceRegistry.java b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourcesPluginServiceRegistry.java
new file mode 100644
index 0000000..4b10d95
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/java/org/gradle/internal/resource/transport/sftp/SftpResourcesPluginServiceRegistry.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp;
+
+
+import org.gradle.internal.resource.connector.ResourceConnectorFactory;
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class SftpResourcesPluginServiceRegistry implements PluginServiceRegistry {
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new GlobalScopeServices());
+    }
+
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+
+    private static class GlobalScopeServices {
+        SftpClientFactory createSftpClientFactory() {
+            return new SftpClientFactory();
+        }
+
+        ResourceConnectorFactory createSftpConnectorFactory(SftpClientFactory clientFactory) {
+            return new SftpConnectorFactory(clientFactory);
+        }
+    }
+}
diff --git a/subprojects/resources-sftp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/resources-sftp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..3cd5566
--- /dev/null
+++ b/subprojects/resources-sftp/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1,2 @@
+org.gradle.internal.resource.transport.sftp.SftpResourcesPluginServiceRegistry
+
diff --git a/subprojects/resources-sftp/src/test/groovy/org/gradle/internal/resource/transport/sftp/SftpClientFactoryTest.groovy b/subprojects/resources-sftp/src/test/groovy/org/gradle/internal/resource/transport/sftp/SftpClientFactoryTest.groovy
new file mode 100644
index 0000000..2d9fabc
--- /dev/null
+++ b/subprojects/resources-sftp/src/test/groovy/org/gradle/internal/resource/transport/sftp/SftpClientFactoryTest.groovy
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transport.sftp
+import org.gradle.internal.resource.PasswordCredentials
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
+
+import static org.gradle.internal.resource.transport.sftp.SftpClientFactory.SftpClientCreator
+
+class SftpClientFactoryTest extends ConcurrentSpec {
+    SftpClientFactory sftpClientFactory = new SftpClientFactory()
+    SftpClientCreator sftpClientCreator = Mock(SftpClientCreator)
+
+    def setup() {
+        sftpClientFactory.sftpClientCreator = sftpClientCreator
+    }
+
+    def "Can acquire and release single client"() {
+        def mockSftpClient = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+
+        when:
+        LockableSftpClient actualClient = sftpClientFactory.createSftpClient(uri, credentials)
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient
+        sftpClientFactory.clients.size() == 0
+
+        when:
+        sftpClientFactory.releaseSftpClient(actualClient)
+
+        then:
+        1 * mockSftpClient.host >> new SftpHost(uri, credentials)
+        sftpClientFactory.clients.size() == 1
+        List<SftpHost> clientsByHost = getClientsForSftpHost(uri, credentials)
+        clientsByHost.size() == 1
+        clientsByHost.get(0) == actualClient
+    }
+
+    def "Creates new client if existing client is in use"() {
+        def mockInitialSftpClient = Mock(LockableSftpClient)
+        def mockNewSftpClient = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+
+        when:
+        LockableSftpClient initialClient = sftpClientFactory.createSftpClient(uri, credentials)
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockInitialSftpClient
+        sftpClientFactory.clients.size() == 0
+
+        when:
+        LockableSftpClient newClient = sftpClientFactory.createSftpClient(uri, credentials)
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockNewSftpClient
+        sftpClientFactory.clients.size() == 0
+        initialClient != newClient
+    }
+
+    def "Can acquire, release and reuse single client"() {
+        def mockSftpClient = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+
+        when:
+        LockableSftpClient initialClient = sftpClientFactory.createSftpClient(uri, credentials)
+        sftpClientFactory.releaseSftpClient(initialClient)
+
+        then:
+        1 * mockSftpClient.host >> new SftpHost(uri, credentials)
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient
+        sftpClientFactory.clients.size() == 1
+        List<SftpHost> clientsByHost = getClientsForSftpHost(uri, credentials)
+        clientsByHost.size() == 1
+        clientsByHost.get(0) == initialClient
+
+        when:
+        LockableSftpClient reusedClient = sftpClientFactory.createSftpClient(uri, credentials)
+        sftpClientFactory.releaseSftpClient(reusedClient)
+
+        then:
+        1 * mockSftpClient.host >> new SftpHost(uri, credentials)
+        sftpClientFactory.clients.size() == 1
+        clientsByHost.size() == 1
+        clientsByHost.get(0) == reusedClient
+        initialClient == reusedClient
+    }
+
+    def "Can acquire and release multiple clients"() {
+        def mockSftpClient1 = Mock(LockableSftpClient)
+        def mockSftpClient2 = Mock(LockableSftpClient)
+
+        given:
+        URI uri1 = new URI('http://localhost:22/repo1')
+        URI uri2 = new URI('http://localhost:22/repo2')
+        PasswordCredentials credentials1 = new PasswordCredentials('sftp1', 'sftp1')
+        PasswordCredentials credentials2 = new PasswordCredentials('sftp2', 'sftp2')
+
+        when:
+        LockableSftpClient client1 = sftpClientFactory.createSftpClient(uri1, credentials1)
+        LockableSftpClient client2 = sftpClientFactory.createSftpClient(uri2, credentials2)
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri1, credentials1)) >> mockSftpClient1
+        sftpClientCreator.createNewClient(new SftpHost(uri2, credentials2)) >> mockSftpClient2
+        sftpClientFactory.clients.size() == 0
+
+        when:
+        sftpClientFactory.releaseSftpClient(client1)
+        sftpClientFactory.releaseSftpClient(client2)
+
+        then:
+        1 * mockSftpClient1.host >> new SftpHost(uri1, credentials1)
+        1 * mockSftpClient2.host >> new SftpHost(uri2, credentials2)
+        sftpClientFactory.clients.size() == 2
+        List<SftpHost> clientsByHost1 = getClientsForSftpHost(uri1, credentials1)
+        clientsByHost1.size() == 1
+        clientsByHost1.get(0) == client1
+        List<SftpHost> clientsByHost2 = getClientsForSftpHost(uri2, credentials2)
+        clientsByHost2.size() == 1
+        clientsByHost2.get(0) == client2
+    }
+
+    def "Can stop a single, released client"() {
+        def mockSftpClient = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+
+        when:
+        LockableSftpClient actualClient = sftpClientFactory.createSftpClient(uri, credentials)
+
+        and:
+        sftpClientFactory.releaseSftpClient(actualClient)
+        sftpClientFactory.stop()
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient
+        1 * mockSftpClient.host >> new SftpHost(uri, credentials)
+        sftpClientFactory.clients.size() == 0
+        1 * mockSftpClient.stop()
+    }
+
+    def "Can stop multiple, released clients"() {
+        def mockSftpClient1 = Mock(LockableSftpClient)
+        def mockSftpClient2 = Mock(LockableSftpClient)
+
+        given:
+        URI uri1 = new URI('http://localhost:22/repo1')
+        URI uri2 = new URI('http://localhost:22/repo2')
+        PasswordCredentials credentials1 = new PasswordCredentials('sftp1', 'sftp1')
+        PasswordCredentials credentials2 = new PasswordCredentials('sftp2', 'sftp2')
+
+        when:
+        LockableSftpClient client1 = sftpClientFactory.createSftpClient(uri1, credentials1)
+        LockableSftpClient client2 = sftpClientFactory.createSftpClient(uri2, credentials2)
+
+        and:
+        sftpClientFactory.releaseSftpClient(client1)
+        sftpClientFactory.releaseSftpClient(client2)
+        sftpClientFactory.stop()
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri1, credentials1)) >> mockSftpClient1
+        sftpClientCreator.createNewClient(new SftpHost(uri2, credentials2)) >> mockSftpClient2
+        1 * mockSftpClient1.host >> new SftpHost(uri1, credentials1)
+        1 * mockSftpClient2.host >> new SftpHost(uri2, credentials2)
+        sftpClientFactory.clients.size() == 0
+        1 * mockSftpClient1.stop()
+        1 * mockSftpClient2.stop()
+    }
+
+    def "Multiple threads can create and release a client concurrently"() {
+        def mockSftpClient = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+
+        when:
+        async {
+            10.times {
+                start {
+                    LockableSftpClient actualClient = sftpClientFactory.createSftpClient(uri, credentials)
+                    assert actualClient != null
+                    sftpClientFactory.releaseSftpClient(actualClient)
+                }
+            }
+        }
+
+        then:
+        sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient
+        mockSftpClient.host >> new SftpHost(uri, credentials)
+        sftpClientFactory.clients.size() > 0
+    }
+
+    def "Creates new client if currently in use by different thread"() {
+        def mockSftpClient1 = Mock(LockableSftpClient)
+        def mockSftpClient2 = Mock(LockableSftpClient)
+
+        given:
+        URI uri = new URI('http://localhost:22/repo')
+        PasswordCredentials credentials = new PasswordCredentials('sftp', 'sftp')
+        LockableSftpClient actualClient1
+        LockableSftpClient actualClient2
+
+        when:
+        async {
+            start {
+                actualClient1 = sftpClientFactory.createSftpClient(uri, credentials)
+                instant.action1
+                thread.blockUntil.action2
+                sftpClientFactory.releaseSftpClient(actualClient1)
+            }
+
+            start {
+                actualClient2 = sftpClientFactory.createSftpClient(uri, credentials)
+                instant.action2
+                thread.blockUntil.action1
+                sftpClientFactory.releaseSftpClient(actualClient2)
+            }
+        }
+
+        then:
+        1 * sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient1
+        1 * sftpClientCreator.createNewClient(new SftpHost(uri, credentials)) >> mockSftpClient2
+        1 * mockSftpClient1.host >> new SftpHost(uri, credentials)
+        1 * mockSftpClient2.host >> new SftpHost(uri, credentials)
+        sftpClientFactory.clients.size() == 2
+        actualClient1 != actualClient2
+    }
+
+    private List<SftpHost> getClientsForSftpHost(URI uri, PasswordCredentials credentials) {
+        sftpClientFactory.clients.get(new SftpHost(uri, credentials))
+    }
+}
diff --git a/subprojects/resources/resources.gradle b/subprojects/resources/resources.gradle
index 6474d53..b93971f 100644
--- a/subprojects/resources/resources.gradle
+++ b/subprojects/resources/resources.gradle
@@ -1,9 +1,13 @@
 /*
  * A set of general-purpose resource abstractions.
  */
+apply plugin: "groovy"
+
 dependencies {
     compile libraries.slf4j_api
     compile project(':baseServices')
+    compile project(':messaging')
+    compile libraries.commons_io
     testCompile libraries.groovy
 }
 
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStore.java b/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStore.java
deleted file mode 100644
index 7d12d92..0000000
--- a/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStore.java
+++ /dev/null
@@ -1,32 +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.internal.filestore;
-
-import org.gradle.api.Action;
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-
-import java.io.File;
-
-public interface FileStore<K> {
-
-    LocallyAvailableResource move(K key, File source);
-
-    LocallyAvailableResource copy(K key, File source);
-
-    void moveFilestore(File destination);
-
-    LocallyAvailableResource add(K key, Action<File> addAction);
-}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStoreSearcher.java b/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStoreSearcher.java
deleted file mode 100644
index 44bbfb9..0000000
--- a/subprojects/resources/src/main/java/org/gradle/internal/filestore/FileStoreSearcher.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.internal.filestore;
-
-import org.gradle.internal.resource.local.LocallyAvailableResource;
-
-import java.util.Set;
-
-public interface FileStoreSearcher<S> {
-
-    Set<? extends LocallyAvailableResource> search(S key);
-
-}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/AbstractExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/AbstractExternalResource.java
new file mode 100644
index 0000000..048df99
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/AbstractExternalResource.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.internal.resource;
+
+import org.apache.commons.io.IOUtils;
+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;
+
+    private InputStream openBuffered() {
+        try {
+            return new BufferedInputStream(openStream());
+        } catch (FileNotFoundException e) {
+            throw new ResourceNotFoundException(getURI(), String.format("Could not get resource '%s' as it does not exist.", getURI()), e);
+        } catch (IOException e) {
+            throw ResourceException.getFailed(getURI(), e);
+        }
+    }
+
+    private void close(InputStream input) {
+        try {
+            input.close();
+        } catch (IOException e) {
+            throw ResourceException.getFailed(getURI(), e);
+        }
+    }
+
+    public String getName() {
+        return getURI().toString();
+    }
+
+    public void writeTo(File destination) {
+        try {
+            FileOutputStream output = new FileOutputStream(destination);
+            try {
+                writeTo(output);
+            } finally {
+                output.close();
+            }
+        } catch (Exception e) {
+            throw ResourceException.getFailed(getURI(), e);
+        }
+    }
+
+    public void writeTo(OutputStream output) {
+        try {
+            InputStream input = openStream();
+            try {
+                IOUtils.copyLarge(input, output);
+            } finally {
+                input.close();
+            }
+        } catch (Exception e) {
+            throw ResourceException.getFailed(getURI(), e);
+        }
+    }
+
+    public void withContent(Action<? super InputStream> readAction) {
+        InputStream input = openBuffered();
+        try {
+            readAction.execute(input);
+        } finally {
+            close(input);
+        }
+    }
+
+    public <T> T withContent(Transformer<? extends T, ? super InputStream> readAction) {
+        InputStream input = openBuffered();
+        try {
+            return readAction.transform(input);
+        } finally {
+            close(input);
+        }
+    }
+
+    @Override
+    public <T> T withContent(ContentAction<? extends T> readAction) {
+        InputStream input = openBuffered();
+        try {
+            try {
+                return readAction.execute(input, getMetaData());
+            } catch (IOException e) {
+                throw ResourceException.getFailed(getURI(), e);
+            }
+        } finally {
+            close(input);
+        }
+    }
+
+    public void close() {
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/CachingResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/CachingResource.java
new file mode 100644
index 0000000..58a3ffe
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/CachingResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.resource;
+
+public class CachingResource extends DelegatingResource {
+    private String content;
+    private boolean fetched;
+
+    public CachingResource(Resource resource) {
+        super(resource);
+    }
+
+    @Override
+    public boolean getExists() {
+        maybeFetch();
+        return content != null;
+    }
+
+    @Override
+    public String getText() {
+        maybeFetch();
+        return content;
+    }
+
+    private void maybeFetch() {
+        if (!fetched) {
+            content = getResource().getText();
+            fetched = true;
+        }
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/CharsetUtil.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/CharsetUtil.java
new file mode 100644
index 0000000..bcef274
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/CharsetUtil.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource;
+
+import java.nio.charset.Charset;
+
+public abstract class CharsetUtil {
+
+    public static final Charset UTF_8 = Charset.forName("UTF8");
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/DelegatingResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/DelegatingResource.java
new file mode 100644
index 0000000..3ebbbbb
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/DelegatingResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource;
+
+import java.io.File;
+import java.net.URI;
+
+public class DelegatingResource implements Resource {
+    private final Resource resource;
+
+    public DelegatingResource(Resource resource) {
+        this.resource = resource;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    public String getDisplayName() {
+        return resource.getDisplayName();
+    }
+
+    public File getFile() {
+        return resource.getFile();
+    }
+
+    public URI getURI() {
+        return resource.getURI();
+    }
+
+    public boolean getExists() {
+        return resource.getExists();
+    }
+
+    public String getText() {
+        return resource.getText();
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResource.java
new file mode 100644
index 0000000..f0457e0
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResource.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.internal.resource;
+
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.*;
+import java.net.URI;
+
+/**
+ * This will be merged with {@link Resource}.
+ */
+public interface ExternalResource extends Closeable {
+    /**
+     * Get the URI of the resource.
+     */
+    public URI getURI();
+
+    /**
+     * Get the name of the resource. Use {@link #getURI()} instead.
+     */
+    public String getName();
+
+    /**
+     * 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.
+     *
+     * @throws ResourceException on failure to copy the content.
+     */
+    void writeTo(File destination) throws ResourceException;
+
+    /**
+     * Copies the binary contents of this resource to the given stream. Does not close the provided stream.
+     *
+     * @throws ResourceException on failure to copy the content.
+     */
+    void writeTo(OutputStream destination) throws ResourceException;
+
+    /**
+     * Executes the given action against the binary contents of this resource.
+     *
+     * @throws ResourceException on failure to read the content.
+     * @throws ResourceNotFoundException when the resource does not exist
+     */
+    void withContent(Action<? super InputStream> readAction) throws ResourceException;
+
+    /**
+     * Executes the given action against the binary contents of this resource.
+     *
+     * @throws ResourceException on failure to read the content.
+     * @throws ResourceNotFoundException when the resource does not exist
+     */
+    <T> T withContent(Transformer<? extends T, ? super InputStream> readAction) throws ResourceException;
+
+    /**
+     * Executes the given action against the binary contents and meta-data of this resource.
+     * Generally, this method will be less efficient than one of the other {@code withContent} methods that do
+     * not provide the meta-data, as additional requests may need to be made to obtain the meta-data.
+     *
+     * @throws ResourceException on failure to read the content.
+     * @throws ResourceNotFoundException when the resource does not exist
+     */
+    <T> T withContent(ContentAction<? extends T> readAction) throws ResourceException;
+
+    void close() throws ResourceException;
+
+    /**
+     * Returns the meta-data for this resource.
+     */
+    ExternalResourceMetaData getMetaData();
+
+    interface ContentAction<T> {
+        T execute(InputStream inputStream, ExternalResourceMetaData metaData) throws IOException;
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResourceName.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResourceName.java
new file mode 100644
index 0000000..12a4828
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResourceName.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource;
+
+import com.google.common.base.Objects;
+import org.gradle.internal.UncheckedException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * An immutable resource name. Resources are arranged in a hierarchy. Names may be relative, or absolute with some opaque root resource.
+ */
+public class ExternalResourceName {
+    private final String encodedRoot;
+    private final String path;
+
+    public ExternalResourceName(URI uri) {
+        if (uri.getPath() == null) {
+            throw new IllegalArgumentException(String.format("Cannot create resource name from non-hierarchical URI '%s'.", uri.toString()));
+        }
+        this.encodedRoot = encodeRoot(uri);
+        this.path = uri.getPath();
+    }
+
+    public ExternalResourceName(String path) {
+        encodedRoot = null;
+        this.path = path;
+    }
+
+    private ExternalResourceName(String encodedRoot, String path) {
+        this.encodedRoot = encodedRoot;
+        this.path = path;
+    }
+
+    public ExternalResourceName(URI parent, String path) {
+        if (parent.getPath() == null) {
+            throw new IllegalArgumentException(String.format("Cannot create resource name from non-hierarchical URI '%s'.", parent.toString()));
+        }
+        String newPath;
+        if (path.startsWith("/")) {
+            path = path.substring(1);
+        }
+        if (path.length() == 0) {
+            newPath = parent.getPath();
+        } else if (parent.getPath().endsWith("/")) {
+            newPath = parent.getPath() + path;
+        } else {
+            newPath = parent.getPath() + "/" + path;
+        }
+        this.encodedRoot = encodeRoot(parent);
+        this.path = newPath;
+    }
+
+    private String encodeRoot(URI uri) {
+        StringBuilder builder = new StringBuilder();
+        if (uri.getScheme() != null) {
+            builder.append(uri.getScheme());
+            builder.append(":");
+
+            if(uri.getScheme().equals("file")) {
+                if (uri.getPath().startsWith("//")) {
+                    builder.append("//");
+                }
+            }
+        }
+        if (uri.getHost() != null) {
+            builder.append("//");
+            builder.append(uri.getHost());
+        }
+        if (uri.getPort() > 0) {
+            builder.append(":");
+            builder.append(uri.getPort());
+        }
+        return builder.toString();
+    }
+
+    public String getDisplayName() {
+        return getDecoded();
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+    /**
+     * Returns a URI that represents this resource.
+     */
+    public URI getUri() {
+        try {
+            if (encodedRoot == null) {
+                return new URI(encode(path, false));
+            }
+            return new URI(encodedRoot + encode(path, true));
+        } catch (URISyntaxException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private String encode(String path, boolean isPathSeg) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < path.length(); i++) {
+            char ch = path.charAt(i);
+            if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9') {
+                builder.append(ch);
+            } else if (ch == '/' || ch == '@' || isPathSeg && ch == ':' || ch == '.' || ch == '-' || ch == '_' || ch == '~'
+                    || ch == '!' || ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '+'
+                    || ch == ',' || ch == ';' || ch == '=') {
+                builder.append(ch);
+            } else {
+                if (ch <= 0x7F) {
+                    escapeByte(ch, builder);
+                } else if (ch <= 0x7FF) {
+                    escapeByte(0xC0 | (ch >> 6) & 0x1F, builder);
+                    escapeByte(0x80 | ch & 0x3F, builder);
+                } else {
+                    escapeByte(0xE0 | (ch >> 12) & 0x1F, builder);
+                    escapeByte(0x80 | (ch >> 6) & 0x3F, builder);
+                    escapeByte(0x80 | ch & 0x3F, builder);
+                }
+            }
+        }
+        return builder.toString();
+    }
+
+    private void escapeByte(int ch, StringBuilder builder) {
+        builder.append('%');
+        builder.append(Character.toUpperCase(Character.forDigit(ch >> 4 & 0xFF, 16)));
+        builder.append(Character.toUpperCase(Character.forDigit(ch & 0xF, 16)));
+    }
+
+    /**
+     * Returns the 'decoded' name, which is the opaque root + the path of the name.
+     */
+    public String getDecoded() {
+        if (encodedRoot == null) {
+            return path;
+        }
+        return encodedRoot + path;
+    }
+
+    /**
+     * Returns the root name for this name.
+     */
+    public ExternalResourceName getRoot() {
+        return new ExternalResourceName(encodedRoot, path.startsWith("/") ? "/" : "");
+    }
+
+    /**
+     * Returns the path for this resource. The '/' character is used to separate the elements of the path.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Resolves the given path relative to this name. The path can be a relative path or an absolute path. The '/' character is used to separate the elements of the path.
+     */
+    public ExternalResourceName resolve(String path) {
+        String newPath;
+        if (path.startsWith("/")) {
+            newPath = path;
+        } else if (this.path.endsWith("/") || this.path.length() == 0) {
+            newPath = this.path + path;
+        } else {
+            newPath = this.path + "/" + path;
+        }
+        return new ExternalResourceName(encodedRoot, newPath);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || !obj.getClass().equals(getClass())) {
+            return false;
+        }
+        ExternalResourceName other = (ExternalResourceName) obj;
+        return Objects.equal(encodedRoot, other.encodedRoot) && path.equals(other.path);
+    }
+
+    @Override
+    public int hashCode() {
+        return (encodedRoot == null ? 0 : encodedRoot.hashCode()) ^ path.hashCode();
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/LocalFileStandInExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/LocalFileStandInExternalResource.java
new file mode 100644
index 0000000..d3cc586
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/LocalFileStandInExternalResource.java
@@ -0,0 +1,70 @@
+/*
+ * 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.resource;
+
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+/**
+ * Used when we find a file locally that matches the checksum of some external resource.
+ *
+ * It saves us downloading the file, but we don't get any metadata for it.
+ */
+public class LocalFileStandInExternalResource extends AbstractExternalResource {
+    private final File localFile;
+    private final URI source;
+    private ExternalResourceMetaData metaData;
+
+    public LocalFileStandInExternalResource(URI source, File localFile, ExternalResourceMetaData metaData) {
+        this.source = source;
+        this.localFile = localFile;
+        this.metaData = metaData;
+    }
+
+    public URI getURI() {
+        return source;
+    }
+
+    public long getLastModifiedTime() {
+        return localFile.lastModified();
+    }
+
+    public long getContentLength() {
+        return localFile.length();
+    }
+
+    public boolean isLocal() {
+        return true;
+    }
+
+    public InputStream openStream() throws IOException {
+        return new FileInputStream(localFile);
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        if (metaData == null) {
+            metaData = new DefaultExternalResourceMetaData(source, getLastModifiedTime(), getContentLength());
+        }
+        return metaData;
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/PasswordCredentials.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/PasswordCredentials.java
new file mode 100644
index 0000000..e36190a
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/PasswordCredentials.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource;
+
+public class PasswordCredentials {
+
+    private final String username;
+    private final String password;
+
+    public PasswordCredentials() {
+        this(null, null);
+    }
+
+    public PasswordCredentials(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/Resource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/Resource.java
new file mode 100644
index 0000000..4a4e298
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/Resource.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.resource;
+
+import org.gradle.api.Nullable;
+
+import java.io.File;
+import java.net.URI;
+
+/**
+ * A {@code Resource} represents some binary artifact.
+ *
+ * <p>Implementations are not required to be thread-safe.</p>
+ *
+ * <p>This type will be merged with {@link ExternalResource} and friends.</p>
+ */
+public interface Resource {
+    /**
+     * Returns a display name for this resource. This can be used in log and error messages.
+     *
+     * @return the display name
+     */
+    String getDisplayName();
+
+    /**
+     * Returns a file representing this resource. Not all resources are available as a file.
+     *
+     * @return A file representing this resource. Returns null if this resource is not available as a file.
+     */
+    @Nullable
+    File getFile();
+
+    /**
+     * Returns the URI for this resource. Not all resources have a URI.
+     *
+     * @return The URI for this resource. Returns null if this resource does not have a URI.
+     */
+    @Nullable
+    URI getURI();
+
+    /**
+     * Returns true if this resource exists, false if it does not exist. Note that this method may be expensive, depending on the implementation.
+     *
+     * @return true if this resource exists.
+     */
+    boolean getExists();
+
+    /**
+     * Returns the content of this resource, as a String. Note that this method may be expensive, depending on the implementation.
+     *
+     * @return the content. Never returns null.
+     * @throws ResourceNotFoundException When this resource does not exist.
+     */
+    String getText() throws ResourceNotFoundException;
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceException.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceException.java
new file mode 100644
index 0000000..2a92c03
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceException.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.internal.resource;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.Contextual;
+
+import java.net.URI;
+
+ at Contextual
+public class ResourceException extends GradleException {
+    private final URI location;
+
+    public ResourceException(URI location, String message) {
+        super(message);
+        this.location = location;
+    }
+
+    public ResourceException(URI location, String message, Throwable cause) {
+        super(message, cause);
+        this.location = location;
+    }
+
+    public ResourceException(String message, Throwable cause) {
+        super(message, cause);
+        this.location = null;
+    }
+
+    public static ResourceException getFailed(URI location, Throwable failure) {
+        return failure(location, String.format("Could not get resource '%s'.", location), failure);
+    }
+
+    public static ResourceException putFailed(URI location, Throwable failure) {
+        return failure(location, String.format("Could not write to resource '%s'.", location), failure);
+    }
+
+    /**
+     * Wraps the given failure, unless it is a ResourceException with the specified location.
+     */
+    public static ResourceException failure(URI location, String message, Throwable failure) {
+        if (failure instanceof ResourceException) {
+            ResourceException resourceException = (ResourceException) failure;
+            if (location.equals(resourceException.getLocation())) {
+                return resourceException;
+            }
+        }
+        return new ResourceException(location, message, failure);
+    }
+
+    public URI getLocation() {
+        return location;
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceNotFoundException.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceNotFoundException.java
new file mode 100644
index 0000000..4bab4d7
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/ResourceNotFoundException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.resource;
+
+import java.net.URI;
+
+/**
+ * An exception thrown when attempting to access the content of a {@link Resource} which does not exist.
+ */
+public class ResourceNotFoundException extends ResourceException {
+    public ResourceNotFoundException(URI location, String message) {
+        super(location, message);
+    }
+
+    public ResourceNotFoundException(URI location, String message, Throwable cause) {
+        super(location, message, cause);
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/StringResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/StringResource.java
new file mode 100644
index 0000000..921d66b
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/StringResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.resource;
+
+import java.io.File;
+import java.net.URI;
+
+public class StringResource implements Resource {
+    private final String displayName;
+    private final CharSequence contents;
+
+    public StringResource(String displayName, CharSequence contents) {
+        this.displayName = displayName;
+        this.contents = contents;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public String getText() {
+        return contents.toString();
+    }
+
+    public File getFile() {
+        return null;
+    }
+
+    public URI getURI() {
+        return null;
+    }
+
+    public boolean getExists() {
+        return true;
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/UriResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/UriResource.java
new file mode 100644
index 0000000..73770ac
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/UriResource.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.internal.resource;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.internal.SystemProperties;
+import org.gradle.util.GradleVersion;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URLConnection;
+
+/**
+ * A {@link Resource} implementation backed by a URI. Assumes content is encoded using UTF-8.
+ */
+public class UriResource implements Resource {
+    private final File sourceFile;
+    private final URI sourceUri;
+    private final String description;
+
+    public UriResource(String description, File sourceFile) {
+        this.description = description;
+        this.sourceFile = canonicalise(sourceFile);
+        this.sourceUri = sourceFile.toURI();
+    }
+
+    private File canonicalise(File file) {
+        try {
+            return file.getCanonicalFile();
+        } catch (IOException e) {
+            return file.getAbsoluteFile();
+        }
+    }
+
+    public UriResource(String description, URI sourceUri) {
+        this.description = description;
+        this.sourceFile = sourceUri.getScheme().equals("file") ? canonicalise(new File(sourceUri.getPath())) : null;
+        this.sourceUri = sourceUri;
+    }
+
+    public String getDisplayName() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(description);
+        builder.append(" '");
+        builder.append(sourceFile != null ? sourceFile.getAbsolutePath() : sourceUri);
+        builder.append("'");
+        return builder.toString();
+    }
+
+    public String getText() {
+        if (sourceFile != null && sourceFile.isDirectory()) {
+            throw new ResourceException(sourceUri, String.format("Could not read %s as it is a directory.", getDisplayName()));
+        }
+        try {
+            Reader reader = getInputStream(sourceUri);
+            try {
+                return IOUtils.toString(reader);
+            } finally {
+                reader.close();
+            }
+        } catch (FileNotFoundException e) {
+            throw new ResourceNotFoundException(sourceUri, String.format("Could not read %s as it does not exist.", getDisplayName()));
+        } catch (Exception e) {
+            throw ResourceException.failure(sourceUri, String.format("Could not read %s.", getDisplayName()), e);
+        }
+    }
+
+    public boolean getExists() {
+        try {
+            Reader reader = getInputStream(sourceUri);
+            try {
+                return true;
+            } finally {
+                reader.close();
+            }
+        } catch (FileNotFoundException e) {
+            return false;
+        } catch (Exception e) {
+            throw ResourceException.failure(sourceUri, String.format("Could not determine if %s exists.", getDisplayName()), e);
+        }
+    }
+
+    private Reader getInputStream(URI url) throws IOException {
+        final URLConnection urlConnection = url.toURL().openConnection();
+        urlConnection.setRequestProperty("User-Agent", getUserAgentString());
+        urlConnection.connect();
+        String charset = extractCharacterEncoding(urlConnection.getContentType(), "utf-8");
+        return new InputStreamReader(urlConnection.getInputStream(), charset);
+    }
+
+    public File getFile() {
+        return sourceFile;
+    }
+
+    public URI getURI() {
+        return sourceUri;
+    }
+
+    public static String extractCharacterEncoding(String contentType, String defaultEncoding) {
+        if (contentType == null) {
+            return defaultEncoding;
+        }
+        int pos = findFirstParameter(0, contentType);
+        if (pos == -1) {
+            return defaultEncoding;
+        }
+        StringBuilder paramName = new StringBuilder();
+        StringBuilder paramValue = new StringBuilder();
+        pos = findNextParameter(pos, contentType, paramName, paramValue);
+        while (pos != -1) {
+            if (paramName.toString().equals("charset") && paramValue.length() > 0) {
+                return paramValue.toString();
+            }
+            pos = findNextParameter(pos, contentType, paramName, paramValue);
+        }
+        return defaultEncoding;
+    }
+
+    private static int findFirstParameter(int pos, String contentType) {
+        int index = contentType.indexOf(';', pos);
+        if (index < 0) {
+            return -1;
+        }
+        return index + 1;
+    }
+
+    private static int findNextParameter(int pos, String contentType, StringBuilder paramName, StringBuilder paramValue) {
+        if (pos >= contentType.length()) {
+            return -1;
+        }
+        paramName.setLength(0);
+        paramValue.setLength(0);
+        int separator = contentType.indexOf("=", pos);
+        if (separator < 0) {
+            separator = contentType.length();
+        }
+        paramName.append(contentType.substring(pos, separator).trim());
+        if (separator >= contentType.length() - 1) {
+            return contentType.length();
+        }
+
+        int startValue = separator + 1;
+        int endValue;
+        if (contentType.charAt(startValue) == '"') {
+            startValue++;
+            int i = startValue;
+            while (i < contentType.length()) {
+                char ch = contentType.charAt(i);
+                if (ch == '\\' && i < contentType.length() - 1 && contentType.charAt(i + 1) == '"') {
+                    paramValue.append('"');
+                    i += 2;
+                } else if (ch == '"') {
+                    break;
+                } else {
+                    paramValue.append(ch);
+                    i++;
+                }
+            }
+            endValue = i + 1;
+        } else {
+            endValue = contentType.indexOf(';', startValue);
+            if (endValue < 0) {
+                endValue = contentType.length();
+            }
+            paramValue.append(contentType.substring(startValue, endValue));
+        }
+        if (endValue < contentType.length() && contentType.charAt(endValue) == ';') {
+            endValue++;
+        }
+        return endValue;
+    }
+
+    public static String getUserAgentString() {
+        String osName = System.getProperty("os.name");
+        String osVersion = System.getProperty("os.version");
+        String osArch = System.getProperty("os.arch");
+        String javaVendor = System.getProperty("java.vendor");
+        String javaVersion = SystemProperties.getInstance().getJavaVersion();
+        String javaVendorVersion = System.getProperty("java.vm.version");
+        return String.format("Gradle/%s (%s;%s;%s) (%s;%s;%s)",
+                GradleVersion.current().getVersion(),
+                osName,
+                osVersion,
+                osArch,
+                javaVendor,
+                javaVersion,
+                javaVendorVersion);
+    }
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorFactory.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorFactory.java
new file mode 100644
index 0000000..81a9aaf
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.connector;
+
+import org.gradle.internal.resource.transfer.ExternalResourceConnector;
+
+import java.util.Set;
+
+public interface ResourceConnectorFactory {
+    Set<String> getSupportedProtocols();
+
+    ExternalResourceConnector createResourceConnector(ResourceConnectorSpecification connectionDetails);
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorSpecification.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorSpecification.java
new file mode 100644
index 0000000..5852d3b
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/connector/ResourceConnectorSpecification.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.connector;
+
+public interface ResourceConnectorSpecification {
+    // TODO:DAZ <T extends Credentials>
+    <T> T getCredentials(Class<T> type);
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/AbstractLocallyAvailableResourceFinder.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/AbstractLocallyAvailableResourceFinder.java
new file mode 100644
index 0000000..8c01631
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/AbstractLocallyAvailableResourceFinder.java
@@ -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.internal.resource.local;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.Factory;
+
+import java.io.File;
+import java.util.List;
+
+public class AbstractLocallyAvailableResourceFinder<C> implements LocallyAvailableResourceFinder<C> {
+
+    private final Transformer<Factory<List<File>>, C> producer;
+
+    public AbstractLocallyAvailableResourceFinder(Transformer<Factory<List<File>>, C> producer) {
+        this.producer = producer;
+    }
+
+    public LocallyAvailableResourceCandidates findCandidates(C criterion) {
+        return new LazyLocallyAvailableResourceCandidates(producer.transform(criterion));
+    }
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/ByteArrayLocalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/ByteArrayLocalResource.java
new file mode 100644
index 0000000..7187b42
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/ByteArrayLocalResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.local;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+public class ByteArrayLocalResource implements LocalResource {
+    private final byte[] source;
+
+    public ByteArrayLocalResource(byte[] source) {
+        this.source = source;
+    }
+
+    @Override
+    public long getContentLength() {
+        return source.length;
+    }
+
+    @Override
+    public InputStream open() {
+        return new ByteArrayInputStream(source);
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinder.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinder.java
new file mode 100644
index 0000000..3377693
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/CompositeLocallyAvailableResourceFinder.java
@@ -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.internal.resource.local;
+
+import org.gradle.internal.hash.HashValue;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class CompositeLocallyAvailableResourceFinder<C> implements LocallyAvailableResourceFinder<C> {
+
+    private final List<LocallyAvailableResourceFinder<C>> composites;
+
+    public CompositeLocallyAvailableResourceFinder(List<LocallyAvailableResourceFinder<C>> composites) {
+        this.composites = composites;
+    }
+
+    public LocallyAvailableResourceCandidates findCandidates(C criterion) {
+        List<LocallyAvailableResourceCandidates> allCandidates = new LinkedList<LocallyAvailableResourceCandidates>();
+        for (LocallyAvailableResourceFinder<C> finder : composites) {
+            allCandidates.add(finder.findCandidates(criterion));
+        }
+
+        return new CompositeLocallyAvailableResourceCandidates(allCandidates);
+    }
+    
+    private static class CompositeLocallyAvailableResourceCandidates implements LocallyAvailableResourceCandidates {
+        private final List<LocallyAvailableResourceCandidates> allCandidates;
+
+        public CompositeLocallyAvailableResourceCandidates(List<LocallyAvailableResourceCandidates> allCandidates) {
+            this.allCandidates = allCandidates;
+        }
+
+        public boolean isNone() {
+            for (LocallyAvailableResourceCandidates candidates : allCandidates) {
+                if (!candidates.isNone()) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public LocallyAvailableResource findByHashValue(HashValue hashValue) {
+            for (LocallyAvailableResourceCandidates candidates : allCandidates) {
+                LocallyAvailableResource match = candidates.findByHashValue(hashValue);
+                if (match != null) {
+                    return match;
+                }
+            }
+
+            return null;
+        }
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/DefaultLocallyAvailableExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/DefaultLocallyAvailableExternalResource.java
new file mode 100644
index 0000000..63251d9
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/DefaultLocallyAvailableExternalResource.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.internal.resource.local;
+
+import org.gradle.internal.resource.LocalFileStandInExternalResource;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.net.URI;
+
+public class DefaultLocallyAvailableExternalResource extends LocalFileStandInExternalResource implements LocallyAvailableExternalResource {
+    private final LocallyAvailableResource locallyAvailableResource;
+
+    public DefaultLocallyAvailableExternalResource(URI source, LocallyAvailableResource locallyAvailableResource) {
+        this(source, locallyAvailableResource, null);
+    }
+
+    public DefaultLocallyAvailableExternalResource(URI source, LocallyAvailableResource locallyAvailableResource, ExternalResourceMetaData metaData) {
+        super(source, locallyAvailableResource.getFile(), metaData);
+        this.locallyAvailableResource = locallyAvailableResource;
+    }
+
+    @Override
+    public String toString() {
+        return locallyAvailableResource.toString();
+    }
+
+    public LocallyAvailableResource getLocalResource() {
+        return locallyAvailableResource;
+    }
+
+    @Override
+    public long getContentLength() {
+        return locallyAvailableResource.getContentLength();
+    }
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileLocalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileLocalResource.java
new file mode 100644
index 0000000..792b39d1
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileLocalResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.local;
+
+import org.gradle.internal.resource.ResourceNotFoundException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public class FileLocalResource implements LocalResource {
+    private final File file;
+
+    public FileLocalResource(File file) {
+        this.file = file;
+    }
+
+    @Override
+    public long getContentLength() {
+        return file.length();
+    }
+
+    public InputStream open() {
+        try {
+            return new FileInputStream(file);
+        } catch (FileNotFoundException e) {
+            throw new ResourceNotFoundException(file.toURI(), String.format("File '%s' does not exist.", file), e);
+        }
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStore.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStore.java
new file mode 100644
index 0000000..bcece54
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStore.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.internal.resource.local;
+
+import org.gradle.api.Action;
+
+import java.io.File;
+
+public interface FileStore<K> {
+
+    LocallyAvailableResource move(K key, File source);
+
+    LocallyAvailableResource copy(K key, File source);
+
+    void moveFilestore(File destination);
+
+    LocallyAvailableResource add(K key, Action<File> addAction);
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStoreSearcher.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStoreSearcher.java
new file mode 100644
index 0000000..7026eb6
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/FileStoreSearcher.java
@@ -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.internal.resource.local;
+
+import java.util.Set;
+
+public interface FileStoreSearcher<S> {
+
+    Set<? extends LocallyAvailableResource> search(S key);
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidates.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidates.java
new file mode 100644
index 0000000..b0e0984
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LazyLocallyAvailableResourceCandidates.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.internal.resource.local;
+
+import org.gradle.internal.Factory;
+import org.gradle.internal.hash.HashUtil;
+import org.gradle.internal.hash.HashValue;
+
+import java.io.File;
+import java.util.List;
+
+public class LazyLocallyAvailableResourceCandidates implements LocallyAvailableResourceCandidates {
+
+    private final Factory<List<File>> filesFactory;
+    private List<File> files;
+
+    public LazyLocallyAvailableResourceCandidates(Factory<List<File>> filesFactory) {
+        this.filesFactory = filesFactory;        
+    }
+
+    protected List<File> getFiles() {
+        if (files == null) {
+            files = filesFactory.create();
+        }
+        return files;
+    }
+    
+    public boolean isNone() {
+        return getFiles().isEmpty();
+    }
+
+    public LocallyAvailableResource findByHashValue(HashValue targetHash) {
+        HashValue thisHash;
+        for (File file : getFiles()) {
+            thisHash = HashUtil.sha1(file);
+            if (thisHash.equals(targetHash)) {
+                return new DefaultLocallyAvailableResource(file, thisHash);
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocalResource.java
new file mode 100644
index 0000000..cd868b7
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocalResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.local;
+
+import org.gradle.internal.resource.ResourceException;
+
+import java.io.InputStream;
+
+public interface LocalResource {
+    /**
+     * Unbuffered input stream to read contents of resource.
+     */
+    InputStream open() throws ResourceException;
+
+    long getContentLength();
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableExternalResource.java
new file mode 100644
index 0000000..6f19cfa
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableExternalResource.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.internal.resource.local;
+
+import org.gradle.internal.resource.ExternalResource;
+
+/**
+ * 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/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceCandidates.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceCandidates.java
new file mode 100644
index 0000000..d3b7c06
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceCandidates.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.internal.resource.local;
+
+import org.gradle.internal.hash.HashValue;
+
+/**
+ * A set of locally available resources that were “selected” through some means.
+ */
+public interface LocallyAvailableResourceCandidates {
+
+    boolean isNone();
+
+    LocallyAvailableResource findByHashValue(HashValue hashValue);
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinder.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinder.java
new file mode 100644
index 0000000..fc95133
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.resource.local;
+
+/**
+ * Can find a locally available candidates for an external resource, through some means.
+ *
+ * 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).
+ *
+ * @param <C> The type of the criterion object used to find candidates
+ */
+public interface LocallyAvailableResourceFinder<C> {
+
+    LocallyAvailableResourceCandidates findCandidates(C criterion);
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.java
new file mode 100644
index 0000000..708fa82
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/local/LocallyAvailableResourceFinderSearchableFileStoreAdapter.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.internal.resource.local;
+
+import org.gradle.api.Transformer;
+import org.gradle.internal.Factory;
+import org.gradle.util.CollectionUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Makes a LocallyAvailableResourceFinder out of a FileStoreSearcher.
+ * @param <C> The type of criterion the filestore can be searched for, and therefore locally available resources searched for.
+ */
+public class LocallyAvailableResourceFinderSearchableFileStoreAdapter<C> extends AbstractLocallyAvailableResourceFinder<C> {
+
+    public LocallyAvailableResourceFinderSearchableFileStoreAdapter(final FileStoreSearcher<C> fileStore) {
+        super(new Transformer<Factory<List<File>>, C>() {
+            public Factory<List<File>> transform(final C criterion) {
+                return new Factory<List<File>>() {
+                    public List<File> create() {
+                        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/resources/src/main/java/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaData.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaData.java
new file mode 100644
index 0000000..7865afd
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaData.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.internal.resource.metadata;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.hash.HashValue;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Date;
+
+public class DefaultExternalResourceMetaData implements ExternalResourceMetaData, Serializable {
+    private final URI location;
+    private final Date lastModified;
+    private final long contentLength;
+    private final String etag;
+    private final String sha1;
+    private final String contentType;
+
+    public DefaultExternalResourceMetaData(URI location, long lastModified, long contentLength) {
+        this(location, lastModified > 0 ? new Date(lastModified) : null, contentLength, null, null, null);
+    }
+
+    public DefaultExternalResourceMetaData(URI location, long lastModified, long contentLength, @Nullable String contentType, @Nullable String etag, @Nullable HashValue sha1) {
+        this(location, lastModified > 0 ? new Date(lastModified) : null, contentLength, contentType, etag, sha1);
+    }
+
+    public DefaultExternalResourceMetaData(URI location, @Nullable Date lastModified, long contentLength, @Nullable String contentType, @Nullable String etag, @Nullable HashValue sha1) {
+        this.location = location;
+        this.lastModified = lastModified;
+        this.contentLength = contentLength;
+        this.contentType = contentType;
+        this.etag = etag;
+        this.sha1 = sha1 == null ? null : sha1.asHexString();
+    }
+
+    public URI getLocation() {
+        return location;
+    }
+
+    @Nullable
+    public Date getLastModified() {
+        return lastModified;
+    }
+
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    @Nullable
+    @Override
+    public String getContentType() {
+        return contentType;
+    }
+
+    @Nullable
+    public String getEtag() {
+        return etag;
+    }
+
+    public HashValue getSha1() {
+        return sha1 == null ? null : HashValue.parse(sha1);
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaData.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaData.java
new file mode 100644
index 0000000..3240afc
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaData.java
@@ -0,0 +1,61 @@
+/*
+ * 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.resource.metadata;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.hash.HashValue;
+
+import java.net.URI;
+import java.util.Date;
+
+public interface ExternalResourceMetaData {
+
+    URI getLocation();
+
+    @Nullable
+    Date getLastModified();
+
+    @Nullable
+    String getContentType();
+
+    /**
+     * Returns -1 when the content length is unknown.
+     */
+    long getContentLength();
+
+    /**
+     * Some kind of opaque checksum that was advertised by the remote “server”.
+     * 
+     * For HTTP this is likely the value of the ETag header but it may be any kind of opaque checksum.
+     * 
+     * @return The entity tag, or null if there was no advertised or suitable etag.
+     */
+    @Nullable
+    String getEtag();
+
+    /**
+     * The advertised sha-1 of the external resource.
+     *
+     * This should only be collected if it is very cheap to do so. For example, some HTTP servers send an
+     * “X-Checksum-Sha1” that makes the sha1 available cheaply. In this case it makes sense to advertise this as metadata here.
+     *
+     * @return The sha1, or null if it's unknown.
+     */
+    @Nullable
+    HashValue getSha1();
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompare.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompare.java
new file mode 100644
index 0000000..64671ea
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompare.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.internal.resource.metadata;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.Factory;
+
+import java.util.Date;
+
+public abstract class ExternalResourceMetaDataCompare {
+    public static boolean isDefinitelyUnchanged(@Nullable ExternalResourceMetaData local, Factory<ExternalResourceMetaData> remoteFactory) {
+        if (local == null) {
+            return false;
+        }
+
+        String localEtag = local.getEtag();
+
+        Date localLastModified = local.getLastModified();
+        if (localEtag == null && localLastModified == null) {
+            return false;
+        }
+
+        long localContentLength = local.getContentLength();
+        if (localEtag == null && localContentLength < 1) {
+            return false;
+        }
+
+        // We have enough local data to make a comparison, get the remote metadata
+        ExternalResourceMetaData remote = remoteFactory.create();
+        if (remote == null) {
+            return false;
+        }
+
+        String remoteEtag = remote.getEtag();
+        if (localEtag != null && remoteEtag != null) {
+            return localEtag.equals(remoteEtag);
+        }
+
+        Date remoteLastModified = remote.getLastModified();
+        if (remoteLastModified == null) {
+            return false;
+        }
+
+        long remoteContentLength = remote.getContentLength();
+        //noinspection SimplifiableIfStatement
+        if (remoteContentLength < 1) {
+            return false;
+        }
+
+        return localContentLength == remoteContentLength && remoteLastModified.equals(localLastModified);
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResource.java
new file mode 100644
index 0000000..73197d1
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transfer;
+
+import org.gradle.internal.resource.AbstractExternalResource;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+public class DefaultExternalResource extends AbstractExternalResource {
+    private final URI uri;
+    private final ExternalResourceReadResponse response;
+
+    public DefaultExternalResource(URI uri, ExternalResourceReadResponse response) {
+        this.uri = uri;
+        this.response = response;
+    }
+
+    @Override
+    public URI getURI() {
+        return uri;
+    }
+
+    @Override
+    public ExternalResourceMetaData getMetaData() {
+        return response.getMetaData();
+    }
+
+    @Override
+    public boolean isLocal() {
+        return response.isLocal();
+    }
+
+    @Override
+    protected InputStream openStream() throws IOException {
+        return response.openStream();
+    }
+
+    @Override
+    public void close() {
+        try {
+            response.close();
+        } catch (IOException e) {
+            throw new ResourceException(uri, String.format("Could not close resource '%s'.", uri), e);
+        }
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResourceConnector.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResourceConnector.java
new file mode 100644
index 0000000..baa55da
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/DefaultExternalResourceConnector.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transfer;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.local.LocalResource;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+public class DefaultExternalResourceConnector implements ExternalResourceConnector {
+    private final ExternalResourceAccessor accessor;
+    private final ExternalResourceLister lister;
+    private final ExternalResourceUploader uploader;
+
+    public DefaultExternalResourceConnector(ExternalResourceAccessor accessor, ExternalResourceLister lister, ExternalResourceUploader uploader) {
+        this.accessor = accessor;
+        this.lister = lister;
+        this.uploader = uploader;
+    }
+
+    @Nullable
+    @Override
+    public ExternalResourceReadResponse openResource(URI location) {
+        return accessor.openResource(location);
+    }
+
+    @Nullable
+    @Override
+    public ExternalResourceMetaData getMetaData(URI location) {
+        return accessor.getMetaData(location);
+    }
+
+    @Nullable
+    @Override
+    public List<String> list(URI parent) {
+        return lister.list(parent);
+    }
+
+    @Override
+    public void upload(LocalResource resource, URI destination) throws IOException {
+        uploader.upload(resource, destination);
+    }
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceAccessor.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceAccessor.java
new file mode 100644
index 0000000..b739466
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceAccessor.java
@@ -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.internal.resource.transfer;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.ResourceException;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.net.URI;
+
+public interface ExternalResourceAccessor {
+
+    /**
+     * Read the resource at the given location.
+     *
+     * If the resource does not exist, this method should return null.
+     *
+     * If the resource may exist but can't be accessed due to some configuration issue, the implementation
+     * must throw an {@link ResourceException} to indicate a fatal condition.
+     *
+     * @param location The address of the resource to obtain
+     * @return The resource if it exists, otherwise null. Caller is responsible for closing the result.
+     * @throws ResourceException If the resource may exist, but not could be obtained for some reason.
+     */
+    @Nullable
+    ExternalResourceReadResponse openResource(URI location) throws ResourceException;
+
+    /**
+     * Obtains only the metadata about the resource.
+     *
+     * If it is determined that the resource does not exist, this method should return null.
+     *
+     * If the resource may exist but can't be accessed due to some configuration issue, the implementation
+     * must throw an {@link ResourceException} to indicate a fatal condition.
+     *
+     * @param location The location of the resource to obtain the metadata for
+     * @return The available metadata, null if the resource doesn't exist
+     * @throws ResourceException If the resource may exist, but not could be obtained for some reason
+     */
+    @Nullable
+    ExternalResourceMetaData getMetaData(URI location) throws ResourceException;
+    
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceConnector.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceConnector.java
new file mode 100644
index 0000000..73e171f
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceConnector.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transfer;
+
+public interface ExternalResourceConnector extends ExternalResourceAccessor, ExternalResourceLister, ExternalResourceUploader {
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceLister.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceLister.java
new file mode 100644
index 0000000..434a448
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceLister.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.internal.resource.transfer;
+
+import org.gradle.api.Nullable;
+import org.gradle.internal.resource.ResourceException;
+
+import java.net.URI;
+import java.util.List;
+
+public interface ExternalResourceLister {
+    @Nullable
+    public List<String> list(URI parent) throws ResourceException;
+
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceReadResponse.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceReadResponse.java
new file mode 100644
index 0000000..d70d9fb
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceReadResponse.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transfer;
+
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A single use read of some resource. Don't use this class directly - use the {@link org.gradle.internal.resource.ExternalResource} wrapper instead.
+ */
+public interface ExternalResourceReadResponse extends Closeable {
+    InputStream openStream() throws IOException;
+
+    ExternalResourceMetaData getMetaData();
+
+    boolean isLocal();
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceUploader.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceUploader.java
new file mode 100644
index 0000000..ed35b6e
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/ExternalResourceUploader.java
@@ -0,0 +1,26 @@
+/*
+ * 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.resource.transfer;
+
+import org.gradle.internal.resource.local.LocalResource;
+
+import java.io.IOException;
+import java.net.URI;
+
+public interface ExternalResourceUploader {
+    void upload(LocalResource resource, URI destination) throws IOException;
+}
diff --git a/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/UrlExternalResource.java b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/UrlExternalResource.java
new file mode 100644
index 0000000..6cbba36
--- /dev/null
+++ b/subprojects/resources/src/main/java/org/gradle/internal/resource/transfer/UrlExternalResource.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource.transfer;
+
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.resource.ExternalResource;
+import org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData;
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class UrlExternalResource implements ExternalResourceReadResponse {
+    private final URI uri;
+    private final URLConnection connection;
+    private final DefaultExternalResourceMetaData metaData;
+
+    public static ExternalResource open(URL url) throws IOException {
+        URI uri;
+        try {
+            uri = url.toURI();
+        } catch (URISyntaxException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        return new DefaultExternalResource(uri, new UrlExternalResource(uri, url));
+    }
+
+    private UrlExternalResource(URI uri, URL url) throws IOException {
+        connection = url.openConnection();
+        this.uri = uri;
+        metaData = new DefaultExternalResourceMetaData(uri, connection.getLastModified(), connection.getContentLength(), connection.getContentType(), null, null);
+    }
+
+    public URI getURI() {
+        return uri;
+    }
+
+    public ExternalResourceMetaData getMetaData() {
+        return metaData;
+    }
+
+    public boolean isLocal() {
+        return uri.getScheme().equalsIgnoreCase("file");
+    }
+
+    public long getContentLength() {
+        return connection.getContentLength();
+    }
+
+    public long getLastModified() {
+        return connection.getLastModified();
+    }
+
+    public InputStream openStream() throws IOException {
+        return connection.getInputStream();
+    }
+
+    @Override
+    public void close() {
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/AbstractExternalResourceTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/AbstractExternalResourceTest.groovy
new file mode 100644
index 0000000..1085682
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/AbstractExternalResourceTest.groovy
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource
+
+import org.gradle.api.Action
+import org.gradle.api.Transformer
+import org.gradle.internal.resource.metadata.ExternalResourceMetaData
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class AbstractExternalResourceTest extends Specification {
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
+
+    def "writes contents to file"() {
+        def resource = new TestResource("abc")
+        def file = tmpDir.file("out")
+
+        when:
+        resource.writeTo(file)
+
+        then:
+        file.text == "abc"
+    }
+
+    def "writes contents to output stream"() {
+        def resource = new TestResource("abc")
+        def outstr = new ByteArrayOutputStream()
+
+        when:
+        resource.writeTo(outstr)
+
+        then:
+        new String(outstr.toByteArray()) == "abc"
+    }
+
+    def "writes contents to output stream action"() {
+        def resource = new TestResource("abc")
+        def action = Mock(Action)
+
+        when:
+        resource.withContent(action)
+
+        then:
+        1 * action.execute(_) >> { InputStream instr ->
+            assert instr.text == "abc"
+        }
+    }
+
+    def "propagates stream action failure"() {
+        def resource = new TestResource("abc")
+        def action = Mock(Action)
+        def failure = new RuntimeException()
+
+        when:
+        resource.withContent(action)
+
+        then:
+        def e = thrown(RuntimeException)
+        e == failure
+        1 * action.execute(_) >> { throw failure }
+    }
+
+    def "writes contents to output stream transformer"() {
+        def resource = new TestResource("abc")
+        def action = Mock(Transformer)
+
+        when:
+        def result = resource.withContent(action)
+
+        then:
+        result == "result"
+        1 * action.transform(_) >> { InputStream instr ->
+            assert instr.text == "abc"
+            return "result"
+        }
+    }
+
+    def "propagates stream transformer failure"() {
+        def resource = new TestResource("abc")
+        def action = Mock(Transformer)
+        def failure = new RuntimeException()
+
+        when:
+        resource.withContent(action)
+
+        then:
+        def e = thrown(RuntimeException)
+        e == failure
+        1 * action.transform(_) >> { throw failure }
+    }
+
+    def "writes contents to content action"() {
+        def resource = new TestResource("abc")
+        def action = Mock(ExternalResource.ContentAction)
+
+        when:
+        def result = resource.withContent(action)
+
+        then:
+        result == "result"
+        1 * action.execute(_, _) >> { InputStream instr, ExternalResourceMetaData metaData ->
+            assert instr.text == "abc"
+            return "result"
+        }
+    }
+
+    def "propagates content action failure"() {
+        def resource = new TestResource("abc")
+        def action = Mock(ExternalResource.ContentAction)
+        def failure = new RuntimeException()
+
+        when:
+        resource.withContent(action)
+
+        then:
+        def e = thrown(RuntimeException)
+        e == failure
+        1 * action.execute(_, _) >> { throw failure }
+    }
+
+    class TestResource extends AbstractExternalResource {
+        final String content
+
+        TestResource(String content) {
+            this.content = content
+        }
+
+        @Override
+        protected InputStream openStream() throws IOException {
+            return new ByteArrayInputStream(content.getBytes())
+        }
+
+        @Override
+        URI getURI() {
+            return null
+        }
+
+        @Override
+        boolean isLocal() {
+            return false
+        }
+
+        @Override
+        ExternalResourceMetaData getMetaData() {
+            return null
+        }
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/CachingResourceTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/CachingResourceTest.groovy
new file mode 100644
index 0000000..e53af50
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/CachingResourceTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.internal.resource
+
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.integration.junit4.JMock
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.nullValue
+import static org.junit.Assert.*
+
+ at RunWith(JMock.class)
+class CachingResourceTest {
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    private final Resource target = context.mock(Resource.class)
+    private final CachingResource resource = new CachingResource(target)
+
+    @Test
+    public void fetchesAndCachesContentWhenExistenceIsChecked() {
+        context.checking {
+            one(target).getText()
+            will(returnValue('content'))
+        }
+
+        assertTrue(resource.exists)
+        assertThat(resource.text, equalTo('content'))
+    }
+
+    @Test
+    public void fetchesAndCachesContentWhenContentIsRead() {
+        context.checking {
+            one(target).getText()
+            will(returnValue('content'))
+        }
+
+        assertThat(resource.text, equalTo('content'))
+        assertTrue(resource.exists)
+    }
+    
+    @Test
+    public void fetchesAndCachesContentForResourceThatDoesNotExist() {
+        context.checking {
+            one(target).getText()
+            will(returnValue(null))
+        }
+
+        assertThat(resource.text, nullValue())
+        assertFalse(resource.exists)
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy
new file mode 100644
index 0000000..a54f594
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource
+import org.gradle.util.Matchers
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Shared
+import spock.lang.Specification
+
+class ExternalResourceNameTest extends Specification {
+    @Shared
+    def root = File.listRoots()[0]
+    @Shared
+    def base = new File(root, "base")
+
+    def "can construct a resource name from URI and path"() {
+        expect:
+        def base = URI.create(baseUri)
+        def name = new ExternalResourceName(base, path)
+        name.uri.toASCIIString() == new URI(base.scheme, null, base.host, base.port, expectedPath, null, null).toASCIIString()
+        name.path == expectedPath
+        name.uri.path == expectedPath
+        name.root.uri == URI.create(expectedRoot)
+        name.root == name.root.root
+
+        where:
+        baseUri                      | path                                               | expectedRoot        | expectedPath
+        "http://host/"               | "a/b/c.html"                                       | "http://host/"      | "/a/b/c.html"
+        "http://host/"               | "/a/b/c"                                           | "http://host/"      | "/a/b/c"
+        "http://host:8008"           | "/a/b/c"                                           | "http://host:8008/" | "/a/b/c"
+        "http://host/"               | "/"                                                | "http://host/"      | "/"
+        "http://host/a/b/c"          | ""                                                 | "http://host/"      | "/a/b/c"
+        "http://host/a/b/c"          | "[123]"                                            | "http://host/"      | "/a/b/c/[123]"
+        "http://host"                | "\u007b\u007f\u0080\u03b1\u07ff\u0800\u30b1\ufffe" | "http://host/"      | "/\u007b\u007f\u0080\u03b1\u07ff\u0800\u30b1\ufffe"
+        "http://host"                | ":?#-.~_@"                                         | "http://host/"      | "/:?#-.~_@"
+        this.base.toURI().toString() | "a/b/c"                                            | "file:/"            | this.base.toURI().path + "/a/b/c"
+    }
+
+    def "can construct a resource name from a path"() {
+        expect:
+        def name = new ExternalResourceName(path)
+        name.uri.toASCIIString() == URI.create(expectedUri).toASCIIString()
+        name.path == expectedPath
+        name.root.uri == URI.create(expectedRoot)
+        name.root == name.root.root
+
+        where:
+        path     | expectedUri | expectedRoot | expectedPath
+        "a/b/c"  | "a/b/c"     | ""           | "a/b/c"
+        "/a/b/c" | "/a/b/c"    | "/"          | "/a/b/c"
+        "/"      | "/"         | "/"          | "/"
+        "a:b"    | "a%3Ab"     | ""           | "a:b"
+        "a%:b"   | "a%25%3Ab"  | ""           | "a%:b"
+    }
+
+    def "can construct a resource name from a relative path"() {
+        expect:
+        def name = new ExternalResourceName(path)
+        name.uri == URI.create(expectedUri)
+        name.path == path
+        name.root.uri == new URI(null, null, expectedRoot, null)
+        name.root == name.root.root
+
+        where:
+        path     | expectedUri | expectedRoot
+        "a/b/c"  | "a/b/c"     | ""
+        "/a/b/c" | "/a/b/c"    | "/"
+        "/a/b/c" | "/a/b/c"    | "/"
+        "[123]"  | "%5B123%5D" | ""
+        "/"      | "/"         | "/"
+        ""       | ""          | ""
+    }
+
+    def "decoded name is base uri plus path"() {
+        expect:
+        def name = new ExternalResourceName(URI.create(uri))
+        name.decoded == expectedDecoded
+
+        where:
+        uri                          | expectedDecoded
+        "http://host:80/a/%5B123%5D" | "http://host:80/a/[123]"
+        "http://host/a/b"            | "http://host/a/b"
+        "http://host"                | "http://host"
+        "a/b/c"                      | "a/b/c"
+        "file:/a/b/c"                | "file:/a/b/c"
+    }
+
+    @Requires(TestPrecondition.WINDOWS)
+    def "can handle UNC paths"(){
+        expect:
+        def name = new ExternalResourceName(uri)
+        name.decoded == expectedDecoded
+
+        where:
+        uri                              | expectedDecoded
+        URI.create("file:////ms/dist")   | "file:////ms/dist"
+        new File('\\\\ms\\dist').toURI() | "file:////ms/dist"
+    }
+
+    def "has equals and hashcode"() {
+        def name = new ExternalResourceName(URI.create("http://host"), "a/b/c")
+        def same = new ExternalResourceName(URI.create("http://host"), "a/b/c")
+        def samePath = new ExternalResourceName(URI.create("http://host/a/b"), "c")
+        def differentRoot = new ExternalResourceName(URI.create("http://other"), "a/b/c")
+        def differentPath = new ExternalResourceName(URI.create("http://host"), "x/y/z")
+        def relative = new ExternalResourceName("a/b/c")
+        def sameRelative = new ExternalResourceName("a/b/c")
+
+        expect:
+        name Matchers.strictlyEqual(same)
+        name Matchers.strictlyEqual(samePath)
+        relative Matchers.strictlyEqual(sameRelative)
+        name != differentPath
+        name != differentRoot
+        name != relative
+    }
+
+    def "can resolve an absolute path"() {
+        expect:
+        def name = new ExternalResourceName(URI.create(uri)).resolve(path)
+        name.uri == URI.create(expectedUri)
+
+        where:
+        uri                      | path     | expectedUri
+        "http://host/a/b/c"      | "/z"     | "http://host/z"
+        "http://host:8080/a/b/c" | "/path"  | "http://host:8080/path"
+        base.toURI().toString()  | "/z"     | "file:/z"
+        "/a/b/c"                 | "/z"     | "/z"
+        "a/b/c"                  | "/z"     | "/z"
+        "a/b/c"                  | "/"      | "/"
+        "/"                      | "/a/b/c" | "/a/b/c"
+        ""                       | "/a/b/c" | "/a/b/c"
+    }
+
+    def "can resolve an relative path"() {
+        expect:
+        def name = new ExternalResourceName(URI.create(uri)).resolve(path)
+        name.uri == URI.create(expectedUri)
+
+        where:
+        uri                      | path    | expectedUri
+        "http://host/a/b/c"      | "d"     | "http://host/a/b/c/d"
+        "http://host:8080/a/b/c" | "d/e"   | "http://host:8080/a/b/c/d/e"
+        base.toURI().toString()  | "a/b/c" | new File(base, "a/b/c").toURI().toString()
+        "/a/b/c"                 | "z"     | "/a/b/c/z"
+        "a/b/c"                  | "z"     | "a/b/c/z"
+        "/"                      | "z"     | "/z"
+        ""                       | "z"     | "z"
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ResourceExceptionTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ResourceExceptionTest.groovy
new file mode 100644
index 0000000..48df703
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ResourceExceptionTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource
+
+import spock.lang.Specification
+
+class ResourceExceptionTest extends Specification {
+    def "wraps failure"() {
+        def location = URI.create("scheme:name")
+        def failure = new RuntimeException()
+
+        expect:
+        def e = ResourceException.failure(location, "broken", failure)
+        e instanceof ResourceException
+        e.location == location
+        e.message == "broken"
+        e.cause == failure
+    }
+
+    def "wraps failure with different location"() {
+        def location = URI.create("scheme:name")
+        def failure = new ResourceException(URI.create("scheme:other"), "cause")
+
+        expect:
+        def e = ResourceException.failure(location, "broken", failure)
+        e instanceof ResourceException
+        e.location == location
+        e.message == "broken"
+        e.cause == failure
+    }
+
+    def "does not wrap failure with same location"() {
+        def location = URI.create("scheme:name")
+        def failure = new ResourceException(location, "cause")
+
+        expect:
+        def e = ResourceException.failure(location, "broken", failure)
+        e.is(failure)
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/StringResourceTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/StringResourceTest.groovy
new file mode 100644
index 0000000..f6f3bc0
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/StringResourceTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.internal.resource
+
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.nullValue
+import static org.junit.Assert.assertThat
+import static org.junit.Assert.assertTrue
+
+class StringResourceTest {
+    private final StringResource resource = new StringResource('displayname', 'text')
+
+    @Test
+    public void hasTextContent() {
+         assertThat(resource.text, equalTo('text'))
+    }
+
+    @Test
+    public void exists() {
+         assertTrue(resource.exists)
+    }
+
+    @Test
+    public void hasNoFile() {
+         assertThat(resource.file, nullValue())
+    }
+
+    @Test
+    public void hasNoURI() {
+        assertThat(resource.URI, nullValue())
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/UriResourceTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/UriResourceTest.groovy
new file mode 100644
index 0000000..637d168
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/UriResourceTest.groovy
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.resource
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.TestPrecondition
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.nullValue
+import static org.junit.Assert.*
+
+class UriResourceTest {
+    private TestFile testDir;
+    private File file;
+    private URI fileUri;
+    @Rule
+    public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
+
+    @Before
+    public void setUp() throws URISyntaxException {
+        testDir = tmpDir.createDir('dir');
+        file = new File(testDir, 'build.script');
+        fileUri = file.toURI();
+    }
+
+    private URI createJar() throws URISyntaxException {
+        TestFile jarFile = tmpDir.testDirectory.file('test.jar');
+        testDir.file('ignoreme').write('content');
+        testDir.zipTo(jarFile);
+        return new URI("jar:${jarFile.toURI()}!/build.script")
+    }
+
+    @Test
+    public void canConstructResourceFromFile() {
+        UriResource resource = new UriResource('<display-name>', file);
+        assertThat(resource.file, equalTo(file));
+        assertThat(resource.URI, equalTo(fileUri));
+    }
+
+    @Test
+    public void canConstructResourceFromFileURI() {
+        UriResource resource = new UriResource('<display-name>', fileUri);
+        assertThat(resource.file, equalTo(file));
+        assertThat(resource.URI, equalTo(fileUri));
+    }
+
+    @Test
+    public void canConstructResourceFromJarURI() {
+        URI jarUri = createJar()
+        UriResource resource = new UriResource('<display-name>', jarUri);
+        assertThat(resource.file, nullValue());
+        assertThat(resource.URI, equalTo(jarUri));
+    }
+
+    @Test
+    public void readsFileContentWhenFileExists() throws IOException {
+        file.text = '<content>'
+
+        UriResource resource = new UriResource('<display-name>', file);
+        assertTrue(resource.exists)
+        assertThat(resource.text, equalTo('<content>'));
+    }
+
+    @Test
+    public void assumesFileIsEncodedUsingUtf8() throws IOException {
+        file.setText('\u03b1', 'utf-8')
+
+        UriResource resource = new UriResource('<display-name>', file);
+        assertTrue(resource.exists)
+        assertThat(resource.text, equalTo('\u03b1'));
+    }
+
+    @Test
+    public void hasNoContentWhenFileDoesNotExist() {
+        UriResource resource = new UriResource('<display-name>', file);
+        assertFalse(resource.exists)
+        try {
+            resource.text
+            fail()
+        } catch (ResourceNotFoundException e) {
+            assertThat(e.message, equalTo("Could not read <display-name> '$file' as it does not exist." as String))
+        }
+    }
+
+    @Test
+    public void hasNoContentWhenFileIsADirectory() {
+        TestFile dir = testDir.file('somedir').createDir()
+        UriResource resource = new UriResource('<display-name>', dir);
+        assertTrue(resource.exists)
+        try {
+            resource.text
+            fail()
+        } catch (ResourceException e) {
+            assertThat(e.message, equalTo("Could not read <display-name> '$dir' as it is a directory." as String))
+        }
+    }
+    
+    @Test
+    public void readsFileContentUsingFileUriWhenFileExists() {
+        file.text = '<content>'
+
+        UriResource resource = new UriResource('<display-name>', fileUri);
+        assertTrue(resource.exists)
+        assertThat(resource.text, equalTo('<content>'));
+    }
+
+    @Test
+    public void hasNoContentWhenUsingFileUriAndFileDoesNotExist() {
+        UriResource resource = new UriResource('<display-name>', fileUri);
+        assertFalse(resource.exists)
+        try {
+            resource.text
+            fail()
+        } catch (ResourceNotFoundException e) {
+            assertThat(e.message, equalTo("Could not read <display-name> '$file' as it does not exist." as String))
+        }
+    }
+
+    @Test
+    public void readsFileContentUsingJarUriWhenFileExists() {
+        file.text = '<content>'
+
+        UriResource resource = new UriResource('<display-name>', createJar());
+        assertTrue(resource.exists)
+        assertThat(resource.text, equalTo('<content>'));
+    }
+
+    @Test
+    public void hasNoContentWhenUsingJarUriAndFileDoesNotExistInJar() {
+        URI jarUri = createJar()
+        UriResource resource = new UriResource('<display-name>', jarUri);
+        assertFalse(resource.exists)
+        try {
+            resource.text
+            fail()
+        } catch (ResourceNotFoundException e) {
+            assertThat(e.message, equalTo("Could not read <display-name> '$jarUri' as it does not exist." as String))
+        }
+    }
+
+    @Test
+    public void hasNoContentWhenUsingHttpUriAndFileDoesNotExist() {
+        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)
+        try {
+            resource.text
+            fail()
+        } catch (ResourceNotFoundException e) {
+            assertThat(e.message, equalTo("Could not read <display-name> 'http://www.gradle.org/unknown.txt' as it does not exist." as String))
+        }
+    }
+
+    @Test
+    public void usesFilePathToBuildDisplayNameWhenUsingFile() {
+        UriResource resource = new UriResource("<file-type>", file);
+        assertThat(resource.displayName, equalTo(String.format("<file-type> '%s'", file.absolutePath)));
+    }
+
+    @Test
+    public void usesFilePathToBuildDisplayNameWhenUsingFileUri() {
+        UriResource resource = new UriResource("<file-type>", fileUri);
+        assertThat(resource.displayName, equalTo(String.format("<file-type> '%s'", file.absolutePath)));
+    }
+
+    @Test
+    public void usesUriToBuildDisplayNameWhenUsingHttpUri() {
+        UriResource resource = new UriResource("<file-type>", new URI("http://www.gradle.org/unknown.txt"));
+        assertThat(resource.displayName, equalTo('<file-type> \'http://www.gradle.org/unknown.txt\''))
+    }
+
+    @Test
+    public void extractsCharacterEncodingFromContentType() {
+        assertThat(UriResource.extractCharacterEncoding('content/unknown', null), nullValue())
+        assertThat(UriResource.extractCharacterEncoding('content/unknown', 'default'), equalTo('default'))
+        assertThat(UriResource.extractCharacterEncoding(null, 'default'), equalTo('default'))
+        assertThat(UriResource.extractCharacterEncoding('text/html', null), nullValue())
+        assertThat(UriResource.extractCharacterEncoding('text/html; charset=UTF-8', null), equalTo('UTF-8'))
+        assertThat(UriResource.extractCharacterEncoding('text/html; other=value; other="value"; charset=US-ASCII', null), equalTo('US-ASCII'))
+        assertThat(UriResource.extractCharacterEncoding('text/plain; other=value;', null), equalTo(null))
+        assertThat(UriResource.extractCharacterEncoding('text/plain; charset="charset"', null), equalTo('charset'))
+        assertThat(UriResource.extractCharacterEncoding('text/plain; charset="\\";\\="', null), equalTo('";\\='))
+        assertThat(UriResource.extractCharacterEncoding('text/plain; charset=', null), equalTo(null))
+        assertThat(UriResource.extractCharacterEncoding('text/plain; charset; charset=;charset="missing-quote', null), equalTo("missing-quote"))
+    }
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaDataTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaDataTest.groovy
new file mode 100644
index 0000000..f410955
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/DefaultExternalResourceMetaDataTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * 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.resource.metadata
+
+import spock.lang.Specification
+import org.gradle.internal.hash.HashValue
+
+class DefaultExternalResourceMetaDataTest extends Specification {
+
+    def "hash value is preserved"() {
+        given:
+        def sha1 = "abc"
+        def md = new DefaultExternalResourceMetaData(new URI("scheme:thing"), -1, -1, null, null, new HashValue(sha1))
+
+        expect:
+        md.sha1.equals(new HashValue(sha1))
+    }
+
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompareTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompareTest.groovy
new file mode 100644
index 0000000..17eaf5c
--- /dev/null
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/metadata/ExternalResourceMetaDataCompareTest.groovy
@@ -0,0 +1,172 @@
+/*
+ * 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.resource.metadata
+
+import org.gradle.internal.Factory
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class ExternalResourceMetaDataCompareTest extends Specification {
+
+    @Shared now = new Date()
+
+    def local = Mock(ExternalResourceMetaData)
+    def remote = Mock(ExternalResourceMetaData)
+    def factory = Mock(Factory)
+
+    def unchanged = false
+
+    def "always changed with no local metadata"() {
+        when:
+        compare(null, factory)
+
+        then:
+        !unchanged
+        0 * factory.create()
+    }
+
+    @Unroll
+    "always unchanged with incomplete local metadata"() {
+        given:
+        configureMetadata(local, etag, lastModified, contentLength)
+
+        when:
+        compare(local, factory)
+
+        then:
+        !unchanged
+        0 * factory.create()
+
+        where:
+        etag | lastModified | contentLength
+        null | null         | -1
+        null | now          | -1
+        null | null         | -1
+    }
+
+    @Unroll
+    "always unchanged with incomplete remote metadata"() {
+        given:
+        configureMetadata(local)
+        configureMetadata(remote, null, lastModified, contentLength)
+
+        when:
+        compare(local, factory)
+
+        then:
+        !unchanged
+        1 * factory.create() >> remote
+
+        where:
+        lastModified | contentLength
+        null         | -1
+        now          | -1
+        null         | -1
+    }
+
+    def "always changed with no remote metadata"() {
+        given:
+        configureMetadata(local)
+
+        when:
+        compare(local, factory)
+
+        then:
+        !unchanged
+        1 * factory.create() >> null
+    }
+
+    def "is unchanged if everything is equal"() {
+        given:
+        configureMetadata(local)
+        configureMetadataForEtagMatch(remote)
+
+        when:
+        compare(local, remote)
+
+        then:
+        unchanged
+    }
+
+    def "matching etags are enough to be considered equal"() {
+        given:
+        configureMetadata(local, "abc", null, -1)
+        configureMetadataForEtagMatch(remote, "abc")
+
+        when:
+        compare(local, remote)
+
+        then:
+        unchanged
+    }
+
+    def "non matching etags, no mod date, but matching content length does not match"() {
+        given:
+        configureMetadata(local, "abc", null, 10)
+        configureMetadataForEtagMatch(remote, "cde")
+
+        when:
+        compare(local, remote)
+
+        then:
+        !unchanged
+    }
+
+    def "non matching etags, matching mod date, but different content length does not match"() {
+        given:
+        configureMetadata(local, "abc", now, -1)
+        configureMetadataForEtagMatch(remote, "cde")
+
+        when:
+        compare(local, remote)
+
+        then:
+        !unchanged
+    }
+
+    def configureMetadata(ExternalResourceMetaData metaData, String etag = "abc", Date lastModified = now, long contentLength = 100) {
+        interaction {
+            1 * metaData.getEtag() >> etag
+
+            1 * metaData.getLastModified() >> lastModified
+            if (lastModified != null || etag != null) {
+                1 * metaData.getContentLength() >> contentLength
+            } else {
+                0 * metaData.getContentLength()
+            }
+        }
+    }
+
+    def configureMetadataForEtagMatch(ExternalResourceMetaData metaData, String etag = "abc") {
+        interaction {
+            1 * metaData.getEtag() >> etag
+            0 * metaData.getLastModified()
+            0 * metaData.getContentLength()
+        }
+    }
+    boolean compare(ExternalResourceMetaData local, ExternalResourceMetaData remote) {
+        compare(local, new Factory() {
+            def create() { remote }
+        })
+    }
+
+    boolean compare(ExternalResourceMetaData local, Factory<ExternalResourceMetaData> remoteFactory) {
+        unchanged = ExternalResourceMetaDataCompare.isDefinitelyUnchanged(local, remoteFactory)
+        unchanged
+    }
+}
diff --git a/subprojects/scala/scala.gradle b/subprojects/scala/scala.gradle
index e7e32f3..ac227b0 100644
--- a/subprojects/scala/scala.gradle
+++ b/subprojects/scala/scala.gradle
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-apply from: "$rootDir/gradle/providedConfiguration.gradle"
-
 dependencies {
     compile libraries.groovy
 
     compile project(":core")
     compile project(":languageJvm")
+    compile project(":languageScala")
     compile project(":plugins")
 
-    // keep in sync with ScalaBasePlugin code
-    provided("com.typesafe.zinc:zinc:0.3.0")
 
     testCompile libraries.slf4j_api
 
@@ -34,5 +31,5 @@ dependencies {
 useTestFixtures(project: ":plugins") // includes core test fixtures
 
 configure([integTest, daemonIntegTest]) {
-    jvmArgs "-XX:MaxPermSize=1g" // AntInProcessScalaCompilerIntegrationTest needs lots of permgen
+    jvmArgs "-XX:MaxPermSize=1500m" // AntInProcessScalaCompilerIntegrationTest needs lots of permgen
 }
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java
index 999ebaa..500c64c 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaProjectIntegrationTest.java
@@ -16,9 +16,14 @@
 package org.gradle.integtests;
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest;
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class ScalaProjectIntegrationTest extends AbstractIntegrationTest {
+    @Rule
+    public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, testDirectoryProvider);
+
     @Test
     public void handlesJavaSourceOnly() {
         testFile("src/main/java/somepackage/SomeClass.java").writelns("public class SomeClass { }");
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/fixtures/ScalaCoverage.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/fixtures/ScalaCoverage.groovy
new file mode 100644
index 0000000..f19a685
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/fixtures/ScalaCoverage.groovy
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.fixtures
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+
+class ScalaCoverage {
+
+    static final boolean USE_SINGLE_VERSION_ONLY = GradleContextualExecuter.isDaemon()
+
+    static final String NEWEST = "2.11.1"
+
+    static final String[] DEFAULT = USE_SINGLE_VERSION_ONLY ? [NEWEST] : ["2.10.4", NEWEST]
+
+    static final String[] OLDER = USE_SINGLE_VERSION_ONLY ? ["2.8.2"] : ["2.8.2", "2.9.2"]
+
+
+}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesMixedJavaAndScalaIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesMixedJavaAndScalaIntegrationTest.groovy
index 3f16fae..1e7f303 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesMixedJavaAndScalaIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesMixedJavaAndScalaIntegrationTest.groovy
@@ -18,7 +18,9 @@ package org.gradle.integtests.samples
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
 import org.junit.Test
@@ -28,6 +30,7 @@ import static org.hamcrest.Matchers.containsString
 class SamplesMixedJavaAndScalaIntegrationTest extends AbstractIntegrationTest {
 
     @Rule public final Sample sample = new Sample(testDirectoryProvider, 'scala/mixedJavaAndScala')
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, testDirectoryProvider)
 
     @Test
     public void canBuildJar() {
@@ -54,6 +57,11 @@ class SamplesMixedJavaAndScalaIntegrationTest extends AbstractIntegrationTest {
 
     @Test
     public void canBuildDocs() {
+        if (GradleContextualExecuter.isDaemon()) {
+            // don't load scala into the daemon as it exhausts permgen
+            return
+        }
+
         TestFile projectDir = sample.dir
         executer.inDirectory(projectDir).withTasks('clean', 'javadoc', 'scaladoc').run()
 
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaCustomizedLayoutIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaCustomizedLayoutIntegrationTest.groovy
index 0d9df1b..da03cd7 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaCustomizedLayoutIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaCustomizedLayoutIntegrationTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.integtests.samples
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
@@ -26,6 +27,7 @@ import org.junit.Test
 class SamplesScalaCustomizedLayoutIntegrationTest extends AbstractIntegrationTest {
 
     @Rule public final Sample sample = new Sample(testDirectoryProvider, 'scala/customizedLayout')
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, testDirectoryProvider)
 
     @Test
     public void canBuildJar() {
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaQuickstartIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaQuickstartIntegrationTest.groovy
index e2189f7..3b60672 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaQuickstartIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaQuickstartIntegrationTest.groovy
@@ -18,7 +18,9 @@ package org.gradle.integtests.samples
 
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Before
 import org.junit.Rule
@@ -29,6 +31,7 @@ import static org.hamcrest.Matchers.containsString
 class SamplesScalaQuickstartIntegrationTest extends AbstractIntegrationTest {
 
     @Rule public final Sample sample = new Sample(testDirectoryProvider, 'scala/quickstart')
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, testDirectoryProvider)
 
     private TestFile projectDir
 
@@ -58,6 +61,11 @@ class SamplesScalaQuickstartIntegrationTest extends AbstractIntegrationTest {
 
     @Test
     public void canBuildScalaDoc() {
+        if (GradleContextualExecuter.isDaemon()) {
+            // don't load scala into the daemon as it exhausts permgen
+            return
+        }
+
         executer.inDirectory(projectDir).withTasks('clean', 'scaladoc').run()
 
         projectDir.file('build/docs/scaladoc/index.html').assertExists()
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaZincIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaZincIntegrationTest.groovy
index 42bd699..2080ae8 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaZincIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/samples/SamplesScalaZincIntegrationTest.groovy
@@ -16,14 +16,11 @@
 
 package org.gradle.integtests.samples
 
-import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
-import spock.lang.IgnoreIf
 
- at IgnoreIf({!JavaVersion.current().java6Compatible})
 class SamplesScalaZincIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule Sample sample = new Sample(temporaryFolder, 'scala/zinc')
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaBasePluginIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaBasePluginIntegrationTest.groovy
index cfd9484..dbadfd0 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaBasePluginIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaBasePluginIntegrationTest.groovy
@@ -14,43 +14,19 @@
  * limitations under the License.
  */
 package org.gradle.scala
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.junit.Rule
 
 import static org.hamcrest.Matchers.startsWith
 
-class ScalaBasePluginIntegrationTest extends AbstractIntegrationSpec {
-    def "defaults scalaClasspath to 'scalaTools' configuration if the latter is non-empty"() {
-        executer.withDeprecationChecksDisabled()
-        file("build.gradle") << """
-apply plugin: "scala-base"
-
-sourceSets {
-    custom
-}
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    scalaTools "org.scala-lang:scala-compiler:2.10.1"
-}
-
-task scaladoc(type: ScalaDoc)
-
-task verify << {
-    assert compileCustomScala.scalaClasspath.is(configurations.scalaTools)
-    assert scalaCustomConsole.classpath.is(configurations.scalaTools)
-    assert scaladoc.scalaClasspath.is(configurations.scalaTools)
-}
-"""
-
-        expect:
-        succeeds("verify")
-    }
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+class ScalaBasePluginIntegrationTest extends MultiVersionIntegrationSpec {
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, temporaryFolder)
 
-    def "defaults scalaClasspath to inferred Scala compiler dependency if 'scalaTools' configuration is empty"() {
+    def "defaults scalaClasspath to inferred Scala compiler dependency"() {
         file("build.gradle") << """
 apply plugin: "scala-base"
 
@@ -63,7 +39,7 @@ repositories {
 }
 
 dependencies {
-    customCompile "org.scala-lang:scala-library:2.10.1"
+    customCompile "org.scala-lang:scala-library:$version"
 }
 
 task scaladoc(type: ScalaDoc) {
@@ -71,9 +47,9 @@ task scaladoc(type: ScalaDoc) {
 }
 
 task verify << {
-    assert compileCustomScala.scalaClasspath.files.any { it.name == "scala-compiler-2.10.1.jar" }
-    assert scalaCustomConsole.classpath.files.any { it.name == "scala-compiler-2.10.1.jar" }
-    assert scaladoc.scalaClasspath.files.any { it.name == "scala-compiler-2.10.1.jar" }
+    assert compileCustomScala.scalaClasspath.files.any { it.name == "scala-compiler-${version}.jar" }
+    assert scalaCustomConsole.classpath.files.any { it.name == "scala-compiler-${version}.jar" }
+    assert scaladoc.scalaClasspath.files.any { it.name == "scala-compiler-${version}.jar" }
 }
 """
 
@@ -94,7 +70,7 @@ repositories {
 }
 
 dependencies {
-    customCompile "org.scala-lang:scala-library:2.10.1"
+    customCompile "org.scala-lang:scala-library:$version"
 }
 
 task scaladoc(type: ScalaDoc) {
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginIntegrationTest.groovy
new file mode 100644
index 0000000..0d6b774
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginIntegrationTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.scala
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import spock.lang.Issue
+
+class ScalaPluginIntegrationTest extends AbstractIntegrationSpec {
+    @Requires(TestPrecondition.JDK8_OR_LATER)
+    @Issue("https://issues.gradle.org/browse/GRADLE-3094")
+    def "can apply scala plugin when running under java 8"() {
+        file("build.gradle") << """
+apply plugin: "scala"
+
+task someTask
+"""
+
+        expect:
+        succeeds("someTask")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntForkingScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntForkingScalaCompilerIntegrationTest.groovy
new file mode 100644
index 0000000..d37f5cd
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntForkingScalaCompilerIntegrationTest.groovy
@@ -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.scala.compile
+
+abstract class AbstractAntForkingScalaCompilerIntegrationTest extends BasicScalaCompilerIntegrationTest {
+    def setup() {
+        executer.requireIsolatedDaemons()
+    }
+
+    String compilerConfiguration() {
+        '''
+compileScala.scalaCompileOptions.with {
+    useAnt = true
+    fork = true
+    forkOptions.memoryMaximumSize = "512m"
+}
+'''
+    }
+
+    String logStatement() {
+        "Compiling with Ant scalac task"
+    }
+
+    String getErrorOutput() {
+        return result.output
+    }
+}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntInProcessScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntInProcessScalaCompilerIntegrationTest.groovy
new file mode 100644
index 0000000..f8a796d
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AbstractAntInProcessScalaCompilerIntegrationTest.groovy
@@ -0,0 +1,43 @@
+/*
+ * 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.scala.compile
+
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
+
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+abstract class AbstractAntInProcessScalaCompilerIntegrationTest extends BasicScalaCompilerIntegrationTest {
+    def setup() {
+        executer.requireIsolatedDaemons()
+    }
+
+    String compilerConfiguration() {
+        '''
+compileScala.scalaCompileOptions.with {
+    useAnt = true
+}
+'''
+    }
+
+    String logStatement() {
+        "Compiling with Ant scalac task"
+    }
+
+    String getErrorOutput() {
+        return result.output
+    }
+}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingOlderScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingOlderScalaCompilerIntegrationTest.groovy
new file mode 100644
index 0000000..fe9ce1d
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingOlderScalaCompilerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.scala.compile
+
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at TargetCoverage({ScalaCoverage.OLDER})
+ at Requires(TestPrecondition.JDK7_OR_EARLIER)
+class AntForkingOlderScalaCompilerIntegrationTest extends AbstractAntForkingScalaCompilerIntegrationTest {}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingScalaCompilerIntegrationTest.groovy
index 89356e9..559b6cc 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingScalaCompilerIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntForkingScalaCompilerIntegrationTest.groovy
@@ -15,29 +15,8 @@
  */
 
 package org.gradle.scala.compile
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
 
-import org.gradle.integtests.fixtures.TargetVersions
-
- at TargetVersions(["2.8.2", "2.9.2"])
-class AntForkingScalaCompilerIntegrationTest extends BasicScalaCompilerIntegrationTest {
-    def setup() {
-        executer.requireIsolatedDaemons()
-    }
-
-    String compilerConfiguration() {
-        '''
-compileScala.scalaCompileOptions.with {
-    useAnt = true
-    fork = true
-}
-'''
-    }
-
-    String logStatement() {
-        "Compiling with Ant scalac task"
-    }
-
-    String getErrorOutput() {
-        return result.output
-    }
-}
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+class AntForkingScalaCompilerIntegrationTest extends AbstractAntForkingScalaCompilerIntegrationTest {}
\ No newline at end of file
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessOlderScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessOlderScalaCompilerIntegrationTest.groovy
new file mode 100644
index 0000000..23daf81
--- /dev/null
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessOlderScalaCompilerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.scala.compile
+
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+ at Requires(TestPrecondition.JDK7_OR_EARLIER)
+class AntInProcessOlderScalaCompilerIntegrationTest extends AbstractAntInProcessScalaCompilerIntegrationTest {}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessScalaCompilerIntegrationTest.groovy
index 667be28..a1dee7f 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessScalaCompilerIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/AntInProcessScalaCompilerIntegrationTest.groovy
@@ -16,27 +16,8 @@
 
 package org.gradle.scala.compile
 
-import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
 
- at TargetVersions(["2.8.2", "2.9.2"])
-class AntInProcessScalaCompilerIntegrationTest extends BasicScalaCompilerIntegrationTest {
-    def setup() {
-        executer.requireIsolatedDaemons()
-    }
-
-    String compilerConfiguration() {
-        '''
-compileScala.scalaCompileOptions.with {
-    useAnt = true
-}
-'''
-    }
-
-    String logStatement() {
-        "Compiling with Ant scalac task"
-    }
-
-    String getErrorOutput() {
-        return result.output
-    }
-}
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+class AntInProcessScalaCompilerIntegrationTest extends AbstractAntInProcessScalaCompilerIntegrationTest {}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/BasicScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/BasicScalaCompilerIntegrationTest.groovy
index b3ec240..855067f 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/BasicScalaCompilerIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/BasicScalaCompilerIntegrationTest.groovy
@@ -16,8 +16,9 @@
 
 package org.gradle.scala.compile
 
-import org.gradle.integtests.fixtures.ClassFile
+
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.test.fixtures.file.ClassFile
 import org.gradle.util.VersionNumber
 
 abstract class BasicScalaCompilerIntegrationTest extends MultiVersionIntegrationSpec {
@@ -147,7 +148,6 @@ repositories {
 
 dependencies {
     compile "org.scala-lang:scala-library:$version"
-    compile localGroovy()
 }
 """
     }
@@ -162,12 +162,11 @@ dependencies {
 package compile.test
 
 import scala.collection.JavaConversions._
-import org.codehaus.groovy.runtime.DefaultGroovyMethods
 
 class Person(val name: String, val age: Int) {
     def hello() {
-        val x: java.util.Collection[Int] = List(3, 1, 2)
-        DefaultGroovyMethods.max(x)
+        val x: java.util.List[Int] = List(3, 1, 2)
+        java.util.Collections.reverse(x)
     }
 }
 """
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest.groovy
index faf68a2..c6d4f8c 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.scala.compile
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.TestResources
 import org.junit.Rule
 import spock.lang.Ignore
@@ -24,6 +25,7 @@ import spock.lang.Issue
 class IncrementalScalaCompileIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule TestResources resources = new TestResources(temporaryFolder)
+    @Rule public final ForkScalaCompileInDaemonModeFixture daemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, temporaryFolder)
 
     def recompilesSourceWhenPropertiesChange() {
         expect:
@@ -61,7 +63,7 @@ class IncrementalScalaCompileIntegrationTest extends AbstractIntegrationSpec {
             }
 
             dependencies {
-                compile 'org.scala-lang:scala-library:2.9.2'
+                compile 'org.scala-lang:scala-library:2.11.1'
             }
         """
 
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy
index 4124da4..1899eb2 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy
@@ -13,37 +13,86 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.gradle.scala.compile
 
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
 
- at Requires(TestPrecondition.JDK5)
-class ZincScalaCompilerIntegrationTest extends AbstractIntegrationSpec {
-    def "gives sensible error when run with Java 5"() {
-        buildFile <<
-"""
-apply plugin: "scala"
+ at TargetCoverage({ScalaCoverage.DEFAULT})
+class ZincScalaCompilerIntegrationTest extends BasicScalaCompilerIntegrationTest {
+    @Rule TestResources testResources = new TestResources(temporaryFolder)
 
-repositories {
-    mavenCentral()
+    String compilerConfiguration() {
+        """
+compileScala.scalaCompileOptions.with {
+    useAnt = false
 }
+        """
+    }
 
-dependencies {
-    compile "org.scala-lang:scala-library:2.9.2"
-}
+    String logStatement() {
+        "Compiling with Zinc Scala compiler"
+    }
 
-tasks.withType(ScalaCompile) {
-    scalaCompileOptions.useAnt = false
-}
-"""
+    def compilesScalaCodeIncrementally() {
+        setup:
+        def person = file("build/classes/main/Person.class")
+        def house = file("build/classes/main/House.class")
+        def other = file("build/classes/main/Other.class")
+        run("compileScala")
 
+        when:
+        file("src/main/scala/Person.scala").delete()
         file("src/main/scala/Person.scala") << "class Person"
+        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
+        run("compileScala")
+
+        then:
+        person.exists()
+        house.exists()
+        other.exists()
+        person.lastModified() != old(person.lastModified())
+        house.lastModified() != old(house.lastModified())
+        other.lastModified() == old(other.lastModified())
+    }
+
+    def compilesJavaCodeIncrementally() {
+        setup:
+        def person = file("build/classes/main/Person.class")
+        def house = file("build/classes/main/House.class")
+        def other = file("build/classes/main/Other.class")
+        run("compileScala")
+
+        when:
+        file("src/main/scala/Person.java").delete()
+        file("src/main/scala/Person.java") << "public class Person {}"
+        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
+        run("compileScala")
+
+        then:
+        person.lastModified() != old(person.lastModified())
+        house.lastModified() != old(house.lastModified())
+        other.lastModified() == old(other.lastModified())
+    }
+
+    def compilesIncrementallyAcrossProjectBoundaries() {
+        setup:
+        def person = file("prj1/build/classes/main/Person.class")
+        def house = file("prj2/build/classes/main/House.class")
+        def other = file("prj2/build/classes/main/Other.class")
+        run("compileScala")
+
+        when:
+        file("prj1/src/main/scala/Person.scala").delete()
+        file("prj1/src/main/scala/Person.scala") << "class Person"
+        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
+        run("compileScala")
 
-        expect:
-        fails("compileScala")
-        failure.assertHasCause("To use the Zinc Scala compiler, Java 6 or higher is required.")
+        then:
+        person.lastModified() != old(person.lastModified())
+        house.lastModified() != old(house.lastModified())
+        other.lastModified() == old(other.lastModified())
     }
 }
\ No newline at end of file
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntForkingScalaCompilerJdk6IntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntForkingScalaCompilerJdk6IntegrationTest.groovy
deleted file mode 100644
index 6bcbcb0..0000000
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntForkingScalaCompilerJdk6IntegrationTest.groovy
+++ /dev/null
@@ -1,45 +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.scala.compile.jdk6
-
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.scala.compile.BasicScalaCompilerIntegrationTest
-
- at TargetVersions(["2.10.0"])
-class AntForkingScalaCompilerJdk6IntegrationTest extends BasicScalaCompilerIntegrationTest {
-    def setup() {
-        executer.requireIsolatedDaemons()
-    }
-
-    String compilerConfiguration() {
-        '''
-compileScala.scalaCompileOptions.with {
-    useAnt = true
-    fork = true
-    forkOptions.memoryMaximumSize = "512m"
-}
-'''
-    }
-
-    String logStatement() {
-        "Compiling with Ant scalac task"
-    }
-
-    String getErrorOutput() {
-        return result.output
-    }
-}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntInProcessScalaCompilerJdk6IntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntInProcessScalaCompilerJdk6IntegrationTest.groovy
deleted file mode 100644
index 0158a8d..0000000
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/AntInProcessScalaCompilerJdk6IntegrationTest.groovy
+++ /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.scala.compile.jdk6
-
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.scala.compile.BasicScalaCompilerIntegrationTest
-
- at TargetVersions(["2.10.0"])
-class AntInProcessScalaCompilerJdk6IntegrationTest extends BasicScalaCompilerIntegrationTest {
-    def setup() {
-        executer.requireIsolatedDaemons()
-    }
-
-    String compilerConfiguration() {
-        '''
-compileScala.scalaCompileOptions.with {
-    useAnt = true
-}
-'''
-    }
-
-    String logStatement() {
-        "Compiling with Ant scalac task"
-    }
-
-    String getErrorOutput() {
-        return result.output
-    }
-}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest.groovy
deleted file mode 100644
index 42c481e..0000000
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest.groovy
+++ /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.scala.compile.jdk6
-
-import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.scala.compile.BasicScalaCompilerIntegrationTest
-import org.junit.Rule
-
- at TargetVersions(["2.8.2", "2.9.2", "2.10.0", "2.10.3"])
-class ZincScalaCompilerJdk6IntegrationTest extends BasicScalaCompilerIntegrationTest {
-    @Rule TestResources testResources = new TestResources(temporaryFolder)
-
-    String compilerConfiguration() {
-        """
-compileScala.scalaCompileOptions.with {
-    useAnt = false
-}
-        """
-    }
-
-    String logStatement() {
-        "Compiling with Zinc Scala compiler"
-    }
-
-    def compilesScalaCodeIncrementally() {
-        setup:
-        def person = file("build/classes/main/Person.class")
-        def house = file("build/classes/main/House.class")
-        def other = file("build/classes/main/Other.class")
-        run("compileScala")
-
-        when:
-        file("src/main/scala/Person.scala").delete()
-        file("src/main/scala/Person.scala") << "class Person"
-        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
-        run("compileScala")
-
-        then:
-        person.lastModified() != old(person.lastModified())
-        house.lastModified() != old(house.lastModified())
-        other.lastModified() == old(other.lastModified())
-    }
-
-    def compilesJavaCodeIncrementally() {
-        setup:
-        def person = file("build/classes/main/Person.class")
-        def house = file("build/classes/main/House.class")
-        def other = file("build/classes/main/Other.class")
-        run("compileScala")
-
-        when:
-        file("src/main/scala/Person.java").delete()
-        file("src/main/scala/Person.java") << "public class Person {}"
-        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
-        run("compileScala")
-
-        then:
-        person.lastModified() != old(person.lastModified())
-        house.lastModified() != old(house.lastModified())
-        other.lastModified() == old(other.lastModified())
-    }
-
-    def compilesIncrementallyAcrossProjectBoundaries() {
-        setup:
-        def person = file("prj1/build/classes/main/Person.class")
-        def house = file("prj2/build/classes/main/House.class")
-        def other = file("prj2/build/classes/main/Other.class")
-        run("compileScala")
-
-        when:
-        file("prj1/src/main/scala/Person.scala").delete()
-        file("prj1/src/main/scala/Person.scala") << "class Person"
-        args("-i", "-PscalaVersion=$version") // each run clears args (argh!)
-        run("compileScala")
-
-        then:
-        person.lastModified() != old(person.lastModified())
-        house.lastModified() != old(house.lastModified())
-        other.lastModified() == old(other.lastModified())
-    }
-}
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
index 3b9ea6d..a991e25 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
@@ -16,19 +16,27 @@
 
 package org.gradle.scala.environment
 
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.*
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.util.Requires
 import org.gradle.util.TestPrecondition
+import org.junit.Rule
 import spock.lang.IgnoreIf
 import spock.lang.Unroll
 
+ at TargetCoverage({ScalaCoverage.DEFAULT})
 class JreJavaHomeScalaIntegrationTest extends AbstractIntegrationSpec {
 
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, temporaryFolder)
+
     @IgnoreIf({ AvailableJavaHomes.bestJre == null})
-    @Requires(TestPrecondition.JDK6_OR_LATER)
     @Unroll
     def "scala java cross compilation works in forking mode = #forkMode when JAVA_HOME is set to JRE"() {
+        if (GradleContextualExecuter.daemon && !(forkMode && !useAnt)) {
+            // don't load up scala in process when testing with the daemon as it blows out permgen
+            return
+        }
+
         given:
         def jreJavaHome = AvailableJavaHomes.bestJre
         file("src/main/scala/org/test/JavaClazz.java") << """
@@ -49,7 +57,7 @@ class JreJavaHomeScalaIntegrationTest extends AbstractIntegrationSpec {
                     }
 
                     dependencies {
-                        compile 'org.scala-lang:scala-library:2.9.2'
+                        compile 'org.scala-lang:scala-library:2.11.1'
                     }
 
                     compileScala {
@@ -83,7 +91,7 @@ class JreJavaHomeScalaIntegrationTest extends AbstractIntegrationSpec {
                     }
 
                     dependencies {
-                        compile 'org.scala-lang:scala-library:2.9.2'
+                        compile 'org.scala-lang:scala-library:2.11.1'
                     }
                     """
         def envVars = System.getenv().findAll { !(it.key in ['GRADLE_OPTS', 'JAVA_HOME', 'Path']) }
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/test/ScalaTestIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/test/ScalaTestIntegrationTest.groovy
index 07b9f98..2889d64 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/test/ScalaTestIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/test/ScalaTestIntegrationTest.groovy
@@ -17,12 +17,14 @@ package org.gradle.scala.test
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.ForkScalaCompileInDaemonModeFixture
 import org.gradle.integtests.fixtures.TestResources
 import org.junit.Rule
 
 class ScalaTestIntegrationTest extends AbstractIntegrationSpec {
     @Rule TestResources resources = new TestResources(temporaryFolder)
-    
+    @Rule public final ForkScalaCompileInDaemonModeFixture forkScalaCompileInDaemonModeFixture = new ForkScalaCompileInDaemonModeFixture(executer, temporaryFolder)
+
     def executesTestsWithMultiLineDescriptions() {
         file("build.gradle") << """
 apply plugin: 'scala'
@@ -32,9 +34,9 @@ repositories {
 }
 
 dependencies {
-    compile "org.scala-lang:scala-library:2.9.2"
-    testCompile "org.scalatest:scalatest_2.9.2:1.8"
-    testCompile "junit:junit:4.11"
+    compile "org.scala-lang:scala-library:2.11.1"
+    testCompile "org.scalatest:scalatest_2.11:2.1.5"
+    testCompile "junit:junit:4.12"
 }
         """
 
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
index 3f70c75..545180c 100644
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesDependentClasses/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.2'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
\ No newline at end of file
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
index 4d9720c..557e99a 100644
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/build.gradle
@@ -5,5 +5,5 @@ repositories {
 }
 
 dependencies {
-    compile 'org.scala-lang:scala-library:2.9.2'
+    compile 'org.scala-lang:scala-library:2.11.1'
 }
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/build.gradle
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/build.gradle
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/build.gradle
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj1/src/main/scala/Person.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj1/src/main/scala/Person.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj1/src/main/scala/Person.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj1/src/main/scala/Person.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/House.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/House.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/House.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/House.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/Other.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/Other.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/Other.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/prj2/src/main/scala/Other.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/settings.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/settings.gradle
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesIncrementallyAcrossProjectBoundaries/settings.gradle
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesIncrementallyAcrossProjectBoundaries/settings.gradle
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/build.gradle
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/build.gradle
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/build.gradle
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java
new file mode 100644
index 0000000..034112b
--- /dev/null
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java
@@ -0,0 +1,11 @@
+public class House {
+    private final Person owner;
+
+    public House(Person owner) {
+        this.owner = owner;
+    }
+
+    public Person getOwner() {
+        return owner;
+    }
+}
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Other.java b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Other.java
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Other.java
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Other.java
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Person.java b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Person.java
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Person.java
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesJavaCodeIncrementally/src/main/scala/Person.java
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/build.gradle
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/build.gradle
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/build.gradle
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/House.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/House.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/House.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/House.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Other.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Other.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Other.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Other.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Person.scala b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Person.scala
similarity index 100%
rename from subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Person.scala
rename to subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest/compilesScalaCodeIncrementally/src/main/scala/Person.scala
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java
deleted file mode 100644
index 3f4cdf8..0000000
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/jdk6/ZincScalaCompilerJdk6IntegrationTest/compilesJavaCodeIncrementally/src/main/scala/House.java
+++ /dev/null
@@ -1,13 +0,0 @@
-import java.util.List;
-
-public class House {
-    private final Person owner;
-
-    public House(Person owner) {
-        this.owner = owner;
-    }
-
-    public Person getOwner() {
-        return owner;
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/AntScalaCompiler.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/AntScalaCompiler.groovy
index d624935..0f5a397 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/AntScalaCompiler.groovy
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/AntScalaCompiler.groovy
@@ -18,11 +18,12 @@ package org.gradle.api.internal.tasks.scala
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.project.IsolatedAntBuilder
 import org.gradle.api.tasks.WorkResult
+import org.gradle.api.tasks.scala.ScalaCompileOptions
+import org.gradle.language.base.internal.compile.Compiler
+import org.gradle.util.GUtil
+import org.gradle.util.VersionNumber
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
-import org.gradle.api.internal.tasks.compile.Compiler
-import org.gradle.util.VersionNumber
-import org.gradle.util.GUtil
 
 class AntScalaCompiler implements Compiler<ScalaCompileSpec> {
     private static final Logger LOGGER = LoggerFactory.getLogger(AntScalaCompiler)
@@ -30,27 +31,23 @@ class AntScalaCompiler implements Compiler<ScalaCompileSpec> {
     private final IsolatedAntBuilder antBuilder
     private final Iterable<File> bootclasspathFiles
     private final Iterable<File> extensionDirs
+    private Iterable<File> scalaClasspath
 
-    def AntScalaCompiler(IsolatedAntBuilder antBuilder) {
+    def AntScalaCompiler(IsolatedAntBuilder antBuilder, Iterable<File> scalaClasspath) {
+        this.scalaClasspath = scalaClasspath
         this.antBuilder = antBuilder
         this.bootclasspathFiles = []
         this.extensionDirs = []
     }
 
-    def AntScalaCompiler(IsolatedAntBuilder antBuilder, Iterable<File> bootclasspathFiles, Iterable<File> extensionDirs) {
-        this.antBuilder = antBuilder
-        this.bootclasspathFiles = bootclasspathFiles
-        this.extensionDirs = extensionDirs
-    }
-
     WorkResult execute(ScalaCompileSpec spec) {
         def destinationDir = spec.destinationDir
-        def scalaCompileOptions = spec.scalaCompileOptions
+        ScalaCompileOptions scalaCompileOptions = spec.scalaCompileOptions as ScalaCompileOptions
 
         def backend = chooseBackend(spec)
         def options = [destDir: destinationDir, target: backend] + scalaCompileOptions.optionMap()
         if (scalaCompileOptions.fork) {
-            options.compilerPath = GUtil.asPath(spec.scalaClasspath)
+            options.compilerPath = GUtil.asPath(scalaClasspath)
         }
         def taskName = scalaCompileOptions.useCompileDaemon ? 'fsc' : 'scalac'
         def compileClasspath = spec.classpath
@@ -58,7 +55,7 @@ class AntScalaCompiler implements Compiler<ScalaCompileSpec> {
         LOGGER.info("Compiling with Ant scalac task.")
         LOGGER.debug("Ant scalac task options: {}", options)
 
-        antBuilder.withClasspath(spec.scalaClasspath).execute { ant ->
+        antBuilder.withClasspath(scalaClasspath).execute { ant ->
             taskdef(resource: 'scala/tools/ant/antlib.xml')
 
             "${taskName}"(options) {
@@ -69,7 +66,6 @@ class AntScalaCompiler implements Compiler<ScalaCompileSpec> {
                 extensionDirs.each {dir ->
                     extdirs(location: dir)
                 }
-                classpath(location: destinationDir)
                 compileClasspath.each {file ->
                     classpath(location: file)
                 }
@@ -92,20 +88,20 @@ class AntScalaCompiler implements Compiler<ScalaCompileSpec> {
     }
 
     private String chooseBackend(ScalaCompileSpec spec) {
-        // deprecated, but must still be honored
-        if (spec.scalaCompileOptions.targetCompatibility) {
-            return VersionNumber.parse(spec.scalaCompileOptions.targetCompatibility)
+        def maxSupported
+        def scalaVersion = sniffScalaVersion(scalaClasspath)
+        if (scalaVersion >= VersionNumber.parse("2.10.0-M5")) {
+            maxSupported = VersionNumber.parse("1.7")
+        } else {
+            // prior to Scala 2.10.0-M5, scalac Ant task only supported "jvm-1.5" and "msil" backends
+            maxSupported = VersionNumber.parse("1.5")
         }
 
         def target = VersionNumber.parse(spec.targetCompatibility)
-        if (target <= VersionNumber.parse("1.5")) { return "jvm-${target.major}.${target.minor}" }
-
-        def scalaVersion = sniffScalaVersion(spec.scalaClasspath)
-        if (scalaVersion >= VersionNumber.parse("2.10.0-M5")) {
-            return "jvm-${target.major}.${target.minor}"
+        if (target > maxSupported) {
+            target = maxSupported
         }
 
-        // prior to Scala 2.10.0-M5, scalac Ant task only supported "jvm-1.5" and "msil" backends
-        return "jvm-1.5"
+        return "jvm-${target.major}.${target.minor}"
     }
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/CleaningScalaCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/CleaningScalaCompiler.java
index 5299a59..df1bbf1 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/CleaningScalaCompiler.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/CleaningScalaCompiler.java
@@ -16,13 +16,15 @@
 package org.gradle.api.internal.tasks.scala;
 
 import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.internal.tasks.compile.*;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.language.jvm.internal.SimpleStaleClassCleaner;
-import org.gradle.language.jvm.internal.StaleClassCleaner;
+import org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.tasks.SimpleStaleClassCleaner;
+import org.gradle.language.base.internal.tasks.StaleClassCleaner;
 
-public class CleaningScalaCompiler extends CleaningJavaCompilerSupport<ScalaJavaJointCompileSpec>
-        implements Compiler<ScalaJavaJointCompileSpec> {
+/**
+ * Cleaning compiler for scala. Not required for compiling with Zinc.
+ */
+public class CleaningScalaCompiler extends CleaningJavaCompilerSupport<ScalaJavaJointCompileSpec> {
     private final Compiler<ScalaJavaJointCompileSpec> compiler;
     private final TaskOutputsInternal taskOutputs;
 
@@ -38,9 +40,6 @@ public class CleaningScalaCompiler extends CleaningJavaCompilerSupport<ScalaJava
 
     @Override
     protected StaleClassCleaner createCleaner(ScalaJavaJointCompileSpec spec) {
-        if (spec.getScalaCompileOptions().isUseAnt()) {
-            return new SimpleStaleClassCleaner(taskOutputs);
-        }
-        return new NoOpStaleClassCleaner();
+        return new SimpleStaleClassCleaner(taskOutputs);
     }
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java
deleted file mode 100644
index ba97417..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DaemonScalaCompiler.java
+++ /dev/null
@@ -1,86 +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.tasks.scala;
-
-/*
- * 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.
- */
-
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.daemon.CompileResult;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemon;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
-import org.gradle.api.internal.tasks.compile.daemon.DaemonForkOptions;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.api.tasks.compile.ForkOptions;
-import org.gradle.api.tasks.scala.ScalaForkOptions;
-import org.gradle.internal.UncheckedException;
-
-import java.util.Arrays;
-import java.util.List;
-
-public class DaemonScalaCompiler implements org.gradle.api.internal.tasks.compile.Compiler<ScalaJavaJointCompileSpec> {
-    private final ProjectInternal project;
-    private final Compiler<ScalaJavaJointCompileSpec> delegate;
-    private final CompilerDaemonFactory daemonFactory;
-
-    public DaemonScalaCompiler(ProjectInternal project, Compiler<ScalaJavaJointCompileSpec> delegate, CompilerDaemonFactory daemonFactory) {
-        this.project = project;
-        this.delegate = delegate;
-        this.daemonFactory = daemonFactory;
-    }
-
-    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
-        DaemonForkOptions daemonForkOptions = createDaemonForkOptions(spec);
-        CompilerDaemon daemon = daemonFactory.getDaemon(project.getRootProject().getProjectDir(), daemonForkOptions);
-        CompileResult result = daemon.execute(delegate, spec);
-        if (result.isSuccess()) {
-            return result;
-        }
-        throw UncheckedException.throwAsUncheckedException(result.getException());
-    }
-
-    private DaemonForkOptions createDaemonForkOptions(ScalaJavaJointCompileSpec spec) {
-        return createJavaForkOptions(spec).mergeWith(createScalaForkOptions(spec));
-    }
-
-    private DaemonForkOptions createJavaForkOptions(ScalaJavaJointCompileSpec spec) {
-        ForkOptions options = spec.getCompileOptions().getForkOptions();
-        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(), options.getJvmArgs());
-    }
-
-    private DaemonForkOptions createScalaForkOptions(ScalaJavaJointCompileSpec spec) {
-        ScalaForkOptions options = spec.getScalaCompileOptions().getForkOptions();
-        List<String> sharedPackages = Arrays.asList("scala", "com.typesafe.zinc", "xsbti", "com.sun.tools.javac");
-        return new DaemonForkOptions(options.getMemoryInitialSize(), options.getMemoryMaximumSize(),
-                options.getJvmArgs(), spec.getZincClasspath(), sharedPackages);
-    }
-}
-
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaCompileSpec.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaCompileSpec.java
deleted file mode 100644
index 297cf32..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaCompileSpec.java
+++ /dev/null
@@ -1,58 +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.tasks.scala;
-
-import org.gradle.api.internal.tasks.compile.DefaultJvmLanguageCompileSpec;
-import org.gradle.api.tasks.scala.ScalaCompileOptions;
-
-import java.io.File;
-import java.util.Map;
-
-public class DefaultScalaCompileSpec extends DefaultJvmLanguageCompileSpec implements ScalaCompileSpec {
-    private final ScalaCompileOptions options = new ScalaCompileOptions();
-    private Iterable<File> scalaClasspath;
-    private Iterable<File> zincClasspath;
-    private Map<File, File> analysisMap;
-
-    public ScalaCompileOptions getScalaCompileOptions() {
-        return options;
-    }
-
-    public Iterable<File> getScalaClasspath() {
-        return scalaClasspath;
-    }
-
-    public void setScalaClasspath(Iterable<File> scalaClasspath) {
-        this.scalaClasspath = scalaClasspath;
-    }
-
-    public Iterable<File> getZincClasspath() {
-        return zincClasspath;
-    }
-
-    public void setZincClasspath(Iterable<File> zincClasspath) {
-        this.zincClasspath = zincClasspath;
-    }
-
-    public Map<File, File> getAnalysisMap() {
-        return analysisMap;
-    }
-
-    public void setAnalysisMap(Map<File, File> analysisMap) {
-        this.analysisMap = analysisMap;
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java
deleted file mode 100644
index b0b393e..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompileSpec.java
+++ /dev/null
@@ -1,62 +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.tasks.scala;
-
-import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec;
-import org.gradle.api.tasks.scala.ScalaCompileOptions;
-
-import java.io.File;
-import java.util.Map;
-
-public class DefaultScalaJavaJointCompileSpec extends DefaultJavaCompileSpec implements ScalaJavaJointCompileSpec {
-    private ScalaCompileOptions options;
-    private Iterable<File> scalaClasspath;
-    private Iterable<File> zincClasspath;
-    private Map<File, File> analysisMap;
-
-    public ScalaCompileOptions getScalaCompileOptions() {
-        return options;
-    }
-
-    public void setScalaCompileOptions(ScalaCompileOptions options) {
-        this.options = options;
-    }
-
-    public Iterable<File> getScalaClasspath() {
-        return scalaClasspath;
-    }
-
-    public void setScalaClasspath(Iterable<File> scalaClasspath) {
-        this.scalaClasspath = scalaClasspath;
-    }
-
-    public Iterable<File> getZincClasspath() {
-        return zincClasspath;
-    }
-
-    public void setZincClasspath(Iterable<File> zincClasspath) {
-        this.zincClasspath = zincClasspath;
-    }
-
-    public Map<File, File> getAnalysisMap() {
-        return analysisMap;
-    }
-
-    public void setAnalysisMap(Map<File, File> analysisMap) {
-        this.analysisMap = analysisMap;
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompiler.java
index 404ea5e..520070e 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompiler.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompiler.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.tasks.scala;
 
 import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.language.base.internal.compile.Compiler;
 import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
 import org.gradle.api.tasks.WorkResult;
 import org.gradle.api.tasks.util.PatternFilterable;
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DelegatingScalaCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DelegatingScalaCompiler.java
deleted file mode 100644
index 1e1c3f2..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/DelegatingScalaCompiler.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.api.internal.tasks.scala;
-
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.tasks.WorkResult;
-
-public class DelegatingScalaCompiler implements org.gradle.api.internal.tasks.compile.Compiler<ScalaJavaJointCompileSpec> {
-    private final ScalaCompilerFactory compilerFactory;
-
-    public DelegatingScalaCompiler(ScalaCompilerFactory compilerFactory) {
-        this.compilerFactory = compilerFactory;
-    }
-
-    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
-        Compiler<ScalaJavaJointCompileSpec> delegate = compilerFactory.create(spec.getScalaCompileOptions(), spec.getCompileOptions());
-        return delegate.execute(spec);
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java
deleted file mode 100644
index acd789b..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompiler.java
+++ /dev/null
@@ -1,107 +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.tasks.scala;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
-import org.gradle.api.internal.file.collections.SimpleFileCollection;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.CompilationFailedException;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.util.CollectionUtils;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * A Scala {@link org.gradle.api.internal.tasks.compile.Compiler} which does some normalization of the compile configuration and behaviour before delegating to some other compiler.
- */
-public class NormalizingScalaCompiler implements org.gradle.api.internal.tasks.compile.Compiler<ScalaJavaJointCompileSpec> {
-    private static final Logger LOGGER = Logging.getLogger(NormalizingScalaCompiler.class);
-    private final Compiler<ScalaJavaJointCompileSpec> delegate;
-
-    public NormalizingScalaCompiler(Compiler<ScalaJavaJointCompileSpec> delegate) {
-        this.delegate = delegate;
-    }
-
-    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
-        resolveAndFilterSourceFiles(spec);
-        resolveClasspath(spec);
-        resolveNonStringsInCompilerArgs(spec);
-        logSourceFiles(spec);
-        logCompilerArguments(spec);
-        return delegateAndHandleErrors(spec);
-    }
-
-    private void resolveAndFilterSourceFiles(final ScalaJavaJointCompileSpec spec) {
-        spec.setSource(new SimpleFileCollection(spec.getSource().getFiles()));
-    }
-
-    private void resolveClasspath(ScalaJavaJointCompileSpec spec) {
-        spec.setClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getClasspath())));
-        spec.setScalaClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getScalaClasspath())));
-        spec.setZincClasspath(new SimpleFileCollection(Lists.newArrayList(spec.getZincClasspath())));
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Class path: {}", spec.getClasspath());
-            LOGGER.debug("Scala class path: {}", spec.getScalaClasspath());
-            LOGGER.debug("Zinc class path: {}", spec.getZincClasspath());
-        }
-    }
-
-    private void resolveNonStringsInCompilerArgs(ScalaJavaJointCompileSpec spec) {
-        // in particular, this is about GStrings
-        spec.getCompileOptions().setCompilerArgs(CollectionUtils.toStringList(spec.getCompileOptions().getCompilerArgs()));
-    }
-
-    private void logSourceFiles(ScalaJavaJointCompileSpec spec) {
-        if (!spec.getScalaCompileOptions().isListFiles()) { return; }
-
-        StringBuilder builder = new StringBuilder();
-        builder.append("Source files to be compiled:");
-        for (File file : spec.getSource()) {
-            builder.append('\n');
-            builder.append(file);
-        }
-
-        LOGGER.quiet(builder.toString());
-    }
-
-    private void logCompilerArguments(ScalaJavaJointCompileSpec spec) {
-        if (!LOGGER.isDebugEnabled()) { return; }
-
-        List<String> compilerArgs = new JavaCompilerArgumentsBuilder(spec).includeLauncherOptions(true).includeSourceFiles(true).build();
-        String joinedArgs = Joiner.on(' ').join(compilerArgs);
-        LOGGER.debug("Java compiler arguments: {}", joinedArgs);
-    }
-
-    private WorkResult delegateAndHandleErrors(ScalaJavaJointCompileSpec spec) {
-        try {
-            return delegate.execute(spec);
-        } catch (CompilationFailedException e) {
-            if (spec.getScalaCompileOptions().isFailOnError()) {
-                throw e;
-            }
-            LOGGER.debug("Ignoring compilation failure.");
-            return new SimpleWorkResult(false);
-        }
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java
deleted file mode 100644
index 1d36e7f..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompileSpec.java
+++ /dev/null
@@ -1,39 +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.tasks.scala;
-
-import org.gradle.api.internal.tasks.compile.JvmLanguageCompileSpec;
-import org.gradle.api.tasks.scala.ScalaCompileOptions;
-
-import java.io.File;
-import java.util.Map;
-
-public interface ScalaCompileSpec extends JvmLanguageCompileSpec {
-    ScalaCompileOptions getScalaCompileOptions();
-
-    Iterable<File> getScalaClasspath();
-
-    void setScalaClasspath(Iterable<File> classpath);
-
-    Iterable<File> getZincClasspath();
-
-    void setZincClasspath(Iterable<File> classpath);
-
-    Map<File, File> getAnalysisMap();
-
-    void setAnalysisMap(Map<File, File> analysisMap);
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGenerator.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGenerator.java
deleted file mode 100644
index 818f8a4..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGenerator.java
+++ /dev/null
@@ -1,66 +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.tasks.scala;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.tasks.scala.ScalaCompileOptions;
-
-import java.util.List;
-
-public class ScalaCompilerArgumentsGenerator {
-    public List<String> generate(ScalaCompileSpec spec) {
-        List<String> result = Lists.newArrayList();
-
-        ScalaCompileOptions options = spec.getScalaCompileOptions();
-        addFlag("-deprecation", options.isDeprecation(), result);
-        addFlag("-unchecked", options.isUnchecked(), result);
-        addConcatenatedOption("-g:", options.getDebugLevel(), result);
-        addFlag("-optimise", options.isOptimize(), result);
-        addOption("-encoding", options.getEncoding(), result);
-        addFlag("-verbose", "verbose".equals(options.getDebugLevel()), result);
-        addFlag("-Ydebug", "debug".equals(options.getDebugLevel()), result);
-        if (options.getLoggingPhases() != null) {
-            for (String phase : options.getLoggingPhases()) {
-                addConcatenatedOption("-Ylog:", phase, result);
-            }
-        }
-        if (options.getAdditionalParameters() != null) {
-            result.addAll(options.getAdditionalParameters());
-        }
-
-        return result;
-    }
-
-    private void addFlag(String name, boolean value, List<String> result) {
-        if (value) {
-            result.add(name);
-        }
-    }
-
-    private void addOption(String name, Object value, List<String> result) {
-        if (value != null) {
-            result.add(name);
-            result.add(value.toString());
-        }
-    }
-
-    private void addConcatenatedOption(String name, Object value, List<String> result) {
-        if (value != null) {
-            result.add(name + value.toString());
-        }
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerFactory.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerFactory.java
index 6966569..5f0baa3 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerFactory.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerFactory.java
@@ -16,36 +16,44 @@
 
 package org.gradle.api.internal.tasks.scala;
 
-import org.gradle.api.AntBuilder;
 import org.gradle.api.GradleException;
+import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.project.IsolatedAntBuilder;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.AntJavaCompiler;
-import org.gradle.api.internal.tasks.compile.Compiler;
 import org.gradle.api.internal.tasks.compile.JavaCompileSpec;
-import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.tasks.compile.CompileOptions;
+import org.gradle.api.internal.tasks.compile.JavaCompilerFactory;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
 import org.gradle.api.tasks.scala.ScalaCompileOptions;
-import org.gradle.internal.Factory;
+import org.gradle.language.base.internal.compile.Compiler;
+import org.gradle.language.base.internal.compile.CompilerFactory;
 
-public class ScalaCompilerFactory {
-    private final ProjectInternal project;
+import java.io.File;
+import java.util.Set;
+
+public class ScalaCompilerFactory implements CompilerFactory<ScalaJavaJointCompileSpec> {
     private final IsolatedAntBuilder antBuilder;
-    private final Factory<AntBuilder> antBuilderFactory;
-    private final CompilerDaemonManager compilerDaemonManager;
+    private final JavaCompilerFactory javaCompilerFactory;
+    private final CompilerDaemonFactory compilerDaemonFactory;
+    private FileCollection scalaClasspath;
+    private FileCollection zincClasspath;
+    private final File rootProjectDirectory;
 
-    public ScalaCompilerFactory(ProjectInternal project, IsolatedAntBuilder antBuilder, Factory<AntBuilder> antBuilderFactory, CompilerDaemonManager compilerDaemonManager) {
-        this.project = project;
+    public ScalaCompilerFactory(File rootProjectDirectory, IsolatedAntBuilder antBuilder, JavaCompilerFactory javaCompilerFactory, CompilerDaemonFactory compilerDaemonFactory, FileCollection scalaClasspath, FileCollection zincClasspath) {
+        this.rootProjectDirectory = rootProjectDirectory;
         this.antBuilder = antBuilder;
-        this.antBuilderFactory = antBuilderFactory;
-        this.compilerDaemonManager = compilerDaemonManager;
+        this.javaCompilerFactory = javaCompilerFactory;
+        this.compilerDaemonFactory = compilerDaemonFactory;
+        this.scalaClasspath = scalaClasspath;
+        this.zincClasspath = zincClasspath;
     }
 
-    public org.gradle.api.internal.tasks.compile.Compiler<ScalaJavaJointCompileSpec> create(ScalaCompileOptions scalaOptions, CompileOptions javaOptions) {
+    @SuppressWarnings("unchecked")
+    public Compiler<ScalaJavaJointCompileSpec> newCompiler(ScalaJavaJointCompileSpec spec) {
+        ScalaCompileOptions scalaOptions = (ScalaCompileOptions) spec.getScalaCompileOptions();
+        Set<File> scalaClasspathFiles = scalaClasspath.getFiles();
         if (scalaOptions.isUseAnt()) {
-            Compiler<ScalaCompileSpec> scalaCompiler = new AntScalaCompiler(antBuilder);
-            Compiler<JavaCompileSpec> javaCompiler = new AntJavaCompiler(antBuilderFactory);
-            return new DefaultScalaJavaJointCompiler(scalaCompiler, javaCompiler);
+            Compiler<ScalaCompileSpec> scalaCompiler = new AntScalaCompiler(antBuilder, scalaClasspathFiles);
+            Compiler<JavaCompileSpec> javaCompiler = javaCompilerFactory.createForJointCompilation(spec.getClass());
+            return new NormalizingScalaCompiler(new DefaultScalaJavaJointCompiler(scalaCompiler, javaCompiler));
         }
 
         if (!scalaOptions.isFork()) {
@@ -53,16 +61,10 @@ public class ScalaCompilerFactory {
                     + "requires forking ('scalaCompileOptions.fork=true'), but the latter is set to 'false'.");
         }
 
-        // currently, we leave it to ZincScalaCompiler to also compile the Java code
-        Compiler<ScalaJavaJointCompileSpec> scalaCompiler;
-        try {
-            scalaCompiler = (Compiler<ScalaJavaJointCompileSpec>) getClass().getClassLoader()
-                    .loadClass("org.gradle.api.internal.tasks.scala.jdk6.ZincScalaCompiler").newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException("Internal error: Failed to load org.gradle.api.internal.tasks.scala.jdk6.ZincScalaCompiler", e);
-        }
+        Set<File> zincClasspathFiles = zincClasspath.getFiles();
 
-        scalaCompiler = new DaemonScalaCompiler(project, scalaCompiler, compilerDaemonManager);
+        // currently, we leave it to ZincScalaCompiler to also compile the Java code
+        Compiler<ScalaJavaJointCompileSpec> scalaCompiler = new DaemonScalaCompiler<ScalaJavaJointCompileSpec>(rootProjectDirectory, new ZincScalaCompiler(scalaClasspathFiles, zincClasspathFiles), compilerDaemonFactory, zincClasspathFiles);
         return new NormalizingScalaCompiler(scalaCompiler);
     }
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompiler.java
new file mode 100644
index 0000000..2c5f1b3
--- /dev/null
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/ScalaJavaJointCompiler.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.scala;
+
+/**
+ * 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.
+ */
+ at Deprecated
+public interface ScalaJavaJointCompiler {
+}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/jdk6/ZincScalaCompiler.java b/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/jdk6/ZincScalaCompiler.java
deleted file mode 100644
index b7fc85e..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/internal/tasks/scala/jdk6/ZincScalaCompiler.java
+++ /dev/null
@@ -1,124 +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.tasks.scala.jdk6;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.typesafe.zinc.*;
-import org.gradle.api.GradleException;
-import org.gradle.api.JavaVersion;
-import org.gradle.api.internal.tasks.SimpleWorkResult;
-import org.gradle.api.internal.tasks.compile.CompilationFailedException;
-import org.gradle.api.internal.tasks.compile.Compiler;
-import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
-import org.gradle.api.internal.tasks.scala.ScalaCompilerArgumentsGenerator;
-import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.WorkResult;
-import org.gradle.internal.jvm.Jvm;
-import scala.Option;
-import xsbti.F0;
-
-import java.io.File;
-import java.io.Serializable;
-import java.util.List;
-
-public class ZincScalaCompiler implements Compiler<ScalaJavaJointCompileSpec>, Serializable {
-    private static final Logger LOGGER = Logging.getLogger(ZincScalaCompiler.class);
-
-    public WorkResult execute(ScalaJavaJointCompileSpec spec) {
-        if (!JavaVersion.current().isJava6Compatible()) {
-            throw new GradleException("To use the Zinc Scala compiler, Java 6 or higher is required.");
-        }
-        return Compiler.execute(spec);
-    }
-
-    // need to defer loading of Zinc/sbt/Scala classes until we are
-    // running in the compiler daemon and have them on the class path
-    private static class Compiler {
-        static WorkResult execute(ScalaJavaJointCompileSpec spec) {
-            LOGGER.info("Compiling with Zinc Scala compiler.");
-
-            xsbti.Logger logger = new SbtLoggerAdapter();
-
-            com.typesafe.zinc.Compiler compiler = createCompiler(spec.getScalaClasspath(), spec.getZincClasspath(), logger);
-            List<String> scalacOptions = new ScalaCompilerArgumentsGenerator().generate(spec);
-            List<String> javacOptions = new JavaCompilerArgumentsBuilder(spec).includeClasspath(false).build();
-            Inputs inputs = Inputs.create(ImmutableList.copyOf(spec.getClasspath()), ImmutableList.copyOf(spec.getSource()), spec.getDestinationDir(),
-                    scalacOptions, javacOptions, spec.getScalaCompileOptions().getIncrementalOptions().getAnalysisFile(), spec.getAnalysisMap(), "mixed", getIncOptions(), true);
-            if (LOGGER.isDebugEnabled()) {
-                Inputs.debug(inputs, logger);
-            }
-
-            try {
-                compiler.compile(inputs, logger);
-            } catch (xsbti.CompileFailed e) {
-                throw new CompilationFailedException(e);
-            }
-
-            return new SimpleWorkResult(true);
-        }
-
-        private static IncOptions getIncOptions() {
-            //The values are based on what I have found in sbt-compiler-maven-plugin.googlecode.com and zinc documentation
-            //Hard to say what effect they have on the incremental build
-            int transitiveStep = 3;
-            double recompileAllFraction = 0.5d;
-            boolean relationsDebug = false;
-            boolean apiDebug = false;
-            int apiDiffContextSize = 5;
-            Option<File> apiDumpDirectory = Option.empty();
-            boolean transactional = false;
-            Option<File> backup = Option.empty();
-
-            return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, transactional, backup);
-        }
-
-        static com.typesafe.zinc.Compiler createCompiler(Iterable<File> scalaClasspath, Iterable<File> zincClasspath, xsbti.Logger logger) {
-            ScalaLocation scalaLocation = ScalaLocation.fromPath(Lists.newArrayList(scalaClasspath));
-            SbtJars sbtJars = SbtJars.fromPath(Lists.newArrayList(zincClasspath));
-            Setup setup = Setup.create(scalaLocation, sbtJars, Jvm.current().getJavaHome(), true);
-            if (LOGGER.isDebugEnabled()) {
-                Setup.debug(setup, logger);
-            }
-            return com.typesafe.zinc.Compiler.getOrCreate(setup, logger);
-        }
-    }
-
-    private static class SbtLoggerAdapter implements xsbti.Logger {
-        public void error(F0<String> msg) {
-            LOGGER.error(msg.apply());
-        }
-
-        public void warn(F0<String> msg) {
-            LOGGER.warn(msg.apply());
-        }
-
-        public void info(F0<String> msg) {
-            LOGGER.info(msg.apply());
-        }
-
-        public void debug(F0<String> msg) {
-            LOGGER.debug(msg.apply());
-        }
-
-        public void trace(F0<Throwable> exception) {
-            LOGGER.trace(exception.apply().toString());
-        }
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
index a69ed13..719810c 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaBasePlugin.groovy
@@ -14,43 +14,28 @@
  * limitations under the License.
  */
 package org.gradle.api.plugins.scala
-
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
 import org.gradle.api.file.FileTreeElement
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.tasks.DefaultScalaSourceSet
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.JavaPluginConvention
 import org.gradle.api.reporting.ReportingExtension
+import org.gradle.api.tasks.JavaExec
 import org.gradle.api.tasks.ScalaRuntime
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.scala.ScalaCompile
 import org.gradle.api.tasks.scala.ScalaDoc
-import org.gradle.api.tasks.JavaExec
-import org.gradle.util.DeprecationLogger
+import org.gradle.language.scala.internal.toolchain.DefaultScalaToolProvider
 
 import javax.inject.Inject
 
 class ScalaBasePlugin implements Plugin<Project> {
-    /**
-     * The name of the configuration holding the Scala compiler and tools.
-     *
-     * @deprecated Typically, usages of {@code scalaTools} can simply be removed,
-     * and the Scala tools libraries to be used will be inferred from the Scala
-     * library found on the regular (compile) class path. In some cases, it may
-     * be necessary to additionally configure the {@code scalaClasspath} property
-     * of {@code ScalaCompile} and {@code ScalaDoc} tasks.
-     */
-    static final String SCALA_TOOLS_CONFIGURATION_NAME = "scalaTools"
-
     static final String ZINC_CONFIGURATION_NAME = "zinc"
 
     static final String SCALA_RUNTIME_EXTENSION_NAME = "scalaRuntime"
 
-    private static final String DEFAULT_ZINC_VERSION = "0.3.0"
-
     private final FileResolver fileResolver
 
     private Project project
@@ -63,7 +48,8 @@ class ScalaBasePlugin implements Plugin<Project> {
 
     void apply(Project project) {
         this.project = project
-        def javaPlugin = project.plugins.apply(JavaBasePlugin.class)
+        project.pluginManager.apply(JavaBasePlugin)
+        def javaPlugin = project.plugins.getPlugin(JavaBasePlugin.class)
 
         configureConfigurations(project)
         configureScalaRuntimeExtension()
@@ -73,26 +59,11 @@ class ScalaBasePlugin implements Plugin<Project> {
     }
 
     private void configureConfigurations(Project project) {
-        def scalaToolsConfiguration = project.configurations.create(SCALA_TOOLS_CONFIGURATION_NAME)
-                .setVisible(false)
-                .setDescription("The Scala tools libraries to be used for this Scala project. (Deprecated)")
-        deprecateScalaToolsConfiguration(scalaToolsConfiguration)
-
         project.configurations.create(ZINC_CONFIGURATION_NAME)
                 .setVisible(false)
                 .setDescription("The Zinc incremental compiler to be used for this Scala project.")
     }
 
-    private void deprecateScalaToolsConfiguration(Configuration scalaConfiguration) {
-        scalaConfiguration.dependencies.whenObjectAdded {
-            DeprecationLogger.nagUserOfDiscontinuedConfiguration(SCALA_TOOLS_CONFIGURATION_NAME,
-                    "Typically, usages of 'scalaTools' can simply be removed, and the Scala tools libraries " +
-                    "to be used will be inferred from the Scala library found on the regular (compile) class path. " +
-                    "In some cases, it may be necessary to additionally configure the 'scalaClasspath' property of " +
-                    "ScalaCompile and ScalaDoc tasks.");
-        }
-    }
-
     private void configureScalaRuntimeExtension() {
         scalaRuntime = project.extensions.create(SCALA_RUNTIME_EXTENSION_NAME, ScalaRuntime, project)
     }
@@ -153,7 +124,7 @@ class ScalaBasePlugin implements Plugin<Project> {
                 def config = project.configurations[ZINC_CONFIGURATION_NAME]
                 if (!compile.scalaCompileOptions.useAnt && config.dependencies.empty) {
                     project.dependencies {
-                        zinc("com.typesafe.zinc:zinc:$DEFAULT_ZINC_VERSION")
+                        zinc("com.typesafe.zinc:zinc:$DefaultScalaToolProvider.DEFAULT_ZINC_VERSION")
                     }
                 }
                 config
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
index 56fda28..9d86d0d 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
@@ -26,8 +26,8 @@ public class ScalaPlugin implements Plugin<Project> {
     public static final String SCALA_DOC_TASK_NAME = "scaladoc";
 
     public void apply(Project project) {
-        project.plugins.apply(ScalaBasePlugin.class);
-        project.plugins.apply(JavaPlugin.class);
+        project.pluginManager.apply(ScalaBasePlugin);
+        project.pluginManager.apply(JavaPlugin);
 
         configureScaladoc(project);
     }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.groovy
deleted file mode 100644
index ad3b144..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.groovy
+++ /dev/null
@@ -1,159 +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.tasks
-
-import org.gradle.api.GradleException
-import org.gradle.api.Incubating
-import org.gradle.api.Nullable
-import org.gradle.api.Project
-import org.gradle.api.Buildable
-import org.gradle.api.Task
-import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection
-import org.gradle.api.plugins.scala.ScalaBasePlugin
-
-import java.util.regex.Pattern
-
-/**
- * Provides information related to the Scala runtime(s) used in a project. Added by the
- * {@link ScalaBasePlugin} as a project extension named {@code scalaRuntime}.
- *
- * <p>Example usage:
- *
- * <pre autoTested="">
- *     apply plugin: "scala"
- *
- *     repositories {
- *         mavenCentral()
- *     }
- *
- *     dependencies {
- *         compile "org.scala-lang:scala-library:2.10.1"
- *     }
- *
- *     def scalaClasspath = scalaRuntime.inferScalaClasspath(configurations.compile)
- *     // The returned class path can be used to configure the 'scalaClasspath' property of tasks
- *     // such as 'ScalaCompile' or 'ScalaDoc', or to execute these and other Scala tools directly.
- * </pre>
- */
- at Incubating
-class ScalaRuntime {
-    private static final Pattern SCALA_JAR_PATTERN = Pattern.compile("scala-(\\w.*?)-(\\d.*).jar")
-
-    // should be private but Groovy can't handle this
-    protected final Project project
-
-    ScalaRuntime(Project project) {
-        this.project = project
-    }
-
-    /**
-     * Searches the specified class path for a 'scala-library' Jar, and returns a class path
-     * containing a corresponding (same version) 'scala-compiler' Jar and its dependencies.
-     *
-     * <p>If the (deprecated) 'scalaTools' configuration is explicitly configured, no repository
-     * is declared for the project, no 'scala-library' Jar is found on the specified class path,
-     * or its version cannot be determined, a class path with the contents of the 'scalaTools'
-     * configuration is returned.
-     *
-     * <p>The returned class path may be empty, or may fail to resolve when asked for its contents.
-     *
-     * @param classpath a class path containing a 'scala-library' Jar
-     * @return a class path containing a corresponding 'scala-compiler' Jar and its dependencies
-     */
-    FileCollection inferScalaClasspath(Iterable<File> classpath) {
-        def scalaTools = project.configurations[ScalaBasePlugin.SCALA_TOOLS_CONFIGURATION_NAME]
-        if (!scalaTools.dependencies.empty) {
-            return scalaTools
-        }
-
-        // alternatively, we could return project.files(Runnable)
-        // would differ in the following ways: 1. live (not sure if we want live here) 2. no autowiring (probably want autowiring here)
-        return new LazilyInitializedFileCollection() {
-            @Override
-            FileCollection createDelegate() {
-                if (project.repositories.empty) {
-                    throw new GradleException("Cannot infer Scala class path because no repository is declared in $project")
-                }
-
-                def scalaLibraryJar = findScalaJar(classpath, "library")
-                if (scalaLibraryJar == null) {
-                    throw new GradleException("Cannot infer Scala class path because no Scala library Jar was found. "
-                            + "Does $project declare dependency to scala-library? Searched classpath: $classpath.")
-                }
-
-                def scalaVersion = getScalaVersion(scalaLibraryJar)
-                if (scalaVersion == null) {
-                    throw new AssertionError("Unexpectedly failed to parse version of Scala Jar file: $scalaLibraryJar in $project")
-                }
-
-                return project.configurations.detachedConfiguration(
-                        new DefaultExternalModuleDependency("org.scala-lang", "scala-compiler", scalaVersion))
-            }
-
-            // let's override this so that delegate isn't created at autowiring time (which would mean on every build)
-            @Override
-            TaskDependency getBuildDependencies() {
-                if (classpath instanceof Buildable) {
-                    return classpath.buildDependencies
-                }
-                return new TaskDependency() {
-                    Set<? extends Task> getDependencies(Task task) {
-                        Collections.emptySet()
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Searches the specified class path for a Scala Jar file (scala-compiler, scala-library,
-     * scala-jdbc, etc.) with the specified appendix (compiler, library, jdbc, etc.).
-     * If no such file is found, {@code null} is returned.
-     *
-     * @param classpath the class path to search
-     * @param appendix the appendix to search for
-     * @return a Scala Jar file with the specified appendix
-     */
-    @Nullable
-    File findScalaJar(Iterable<File> classpath, String appendix) {
-        for (file in classpath) {
-            def matcher = SCALA_JAR_PATTERN.matcher(file.name)
-            if (matcher.matches() && matcher.group(1) == appendix) {
-                return file
-            }
-        }
-        return null
-    }
-
-    /**
-     * Determines the version of a Scala Jar file (scala-compiler, scala-library,
-     * scala-jdbc, etc.). If the version cannot be determined, or the file is not a Scala
-     * Jar file, {@code null} is returned.
-     *
-     * <p>Implementation note: The version is determined by parsing the file name, which
-     * is expected to match the pattern 'scala-[component]-[version].jar'.
-     *
-     * @param scalaJar a Scala Jar file
-     * @return the version of the Scala Jar file
-     */
-    @Nullable
-    String getScalaVersion(File scalaJar) {
-        def matcher = SCALA_JAR_PATTERN.matcher(scalaJar.name)
-        matcher.matches() ? matcher.group(2) : null
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.java
new file mode 100644
index 0000000..218eaa9
--- /dev/null
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/ScalaRuntime.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.*;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
+import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Provides information related to the Scala runtime(s) used in a project. Added by the
+ * {@link org.gradle.api.plugins.scala.ScalaBasePlugin} as a project extension named {@code scalaRuntime}.
+ *
+ * <p>Example usage:
+ *
+ * <pre autoTested="">
+ *     apply plugin: "scala"
+ *
+ *     repositories {
+ *         mavenCentral()
+ *     }
+ *
+ *     dependencies {
+ *         compile "org.scala-lang:scala-library:2.10.1"
+ *     }
+ *
+ *     def scalaClasspath = scalaRuntime.inferScalaClasspath(configurations.compile)
+ *     // The returned class path can be used to configure the 'scalaClasspath' property of tasks
+ *     // such as 'ScalaCompile' or 'ScalaDoc', or to execute these and other Scala tools directly.
+ * </pre>
+ */
+ at Incubating
+public class ScalaRuntime {
+    private static final Pattern SCALA_JAR_PATTERN = Pattern.compile("scala-(\\w.*?)-(\\d.*).jar");
+
+    private final Project project;
+
+    public ScalaRuntime(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Searches the specified class path for a 'scala-library' Jar, and returns a class path
+     * containing a corresponding (same version) 'scala-compiler' Jar and its dependencies.
+     *
+     * <p>If the (deprecated) 'scalaTools' configuration is explicitly configured, no repository
+     * is declared for the project, no 'scala-library' Jar is found on the specified class path,
+     * or its version cannot be determined, a class path with the contents of the 'scalaTools'
+     * configuration is returned.
+     *
+     * <p>The returned class path may be empty, or may fail to resolve when asked for its contents.
+     *
+     * @param classpath a class path containing a 'scala-library' Jar
+     * @return a class path containing a corresponding 'scala-compiler' Jar and its dependencies
+     */
+    public FileCollection inferScalaClasspath(final Iterable<File> classpath) {
+        // alternatively, we could return project.files(Runnable)
+        // would differ in the following ways: 1. live (not sure if we want live here) 2. no autowiring (probably want autowiring here)
+        return new LazilyInitializedFileCollection() {
+            @Override
+            public FileCollection createDelegate() {
+                if (project.getRepositories().isEmpty()) {
+                    throw new GradleException(String.format("Cannot infer Scala class path because no repository is declared in %s", project));
+                }
+
+                File scalaLibraryJar = findScalaJar(classpath, "library");
+                if (scalaLibraryJar == null) {
+                    throw new GradleException(String.format("Cannot infer Scala class path because no Scala library Jar was found. "
+                            + "Does %s declare dependency to scala-library? Searched classpath: %s.", project, classpath));
+                }
+
+                String scalaVersion = getScalaVersion(scalaLibraryJar);
+                if (scalaVersion == null) {
+                    throw new AssertionError(String.format("Unexpectedly failed to parse version of Scala Jar file: %s in %s", scalaLibraryJar, project));
+                }
+
+                return project.getConfigurations().detachedConfiguration(
+                        new DefaultExternalModuleDependency("org.scala-lang", "scala-compiler", scalaVersion));
+            }
+
+            // let's override this so that delegate isn't created at autowiring time (which would mean on every build)
+            @Override
+            public TaskDependency getBuildDependencies() {
+                if (classpath instanceof Buildable) {
+                    return ((Buildable) classpath).getBuildDependencies();
+                }
+                return new TaskDependency() {
+                    public Set<? extends Task> getDependencies(Task task) {
+                        return Collections.emptySet();
+                    }
+                };
+            }
+        };
+    }
+
+    /**
+     * Searches the specified class path for a Scala Jar file (scala-compiler, scala-library,
+     * scala-jdbc, etc.) with the specified appendix (compiler, library, jdbc, etc.).
+     * If no such file is found, {@code null} is returned.
+     *
+     * @param classpath the class path to search
+     * @param appendix the appendix to search for
+     * @return a Scala Jar file with the specified appendix
+     */
+    @Nullable
+    public File findScalaJar(Iterable<File> classpath, String appendix) {
+        for (File file : classpath) {
+            Matcher matcher = SCALA_JAR_PATTERN.matcher(file.getName());
+            if (matcher.matches() && matcher.group(1).equals(appendix)) {
+                return file;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Determines the version of a Scala Jar file (scala-compiler, scala-library,
+     * scala-jdbc, etc.). If the version cannot be determined, or the file is not a Scala
+     * Jar file, {@code null} is returned.
+     *
+     * <p>Implementation note: The version is determined by parsing the file name, which
+     * is expected to match the pattern 'scala-[component]-[version].jar'.
+     *
+     * @param scalaJar a Scala Jar file
+     * @return the version of the Scala Jar file
+     */
+    @Nullable
+    public String getScalaVersion(File scalaJar) {
+        Matcher matcher = SCALA_JAR_PATTERN.matcher(scalaJar.getName());
+        return matcher.matches() ? matcher.group(2) : null;
+    }
+}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/IncrementalCompileOptions.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/IncrementalCompileOptions.java
deleted file mode 100644
index 2d2c285..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/IncrementalCompileOptions.java
+++ /dev/null
@@ -1,72 +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.tasks.scala;
-
-import org.gradle.api.Incubating;
-import org.gradle.api.tasks.Input;
-
-import java.io.File;
-import java.io.Serializable;
-
-/**
- * Options for incremental compilation of Scala code. Only used if
- * {@link org.gradle.api.tasks.scala.ScalaCompileOptions#isUseAnt()} is {@code false}.
- */
- at Incubating
-public class IncrementalCompileOptions implements Serializable {
-    private static final long serialVersionUID = 0;
-
-    private File analysisFile;
-    private File publishedCode;
-
-    /**
-     * Returns the file path where results of code analysis are to be stored.
-     *
-     * @return the file path where which results of code analysis are to be stored
-     */
-    @Input
-    public File getAnalysisFile() {
-        return analysisFile;
-    }
-
-    /**
-     * Sets the file path where results of code analysis are to be stored.
-     */
-    public void setAnalysisFile(File analysisFile) {
-        this.analysisFile = analysisFile;
-    }
-
-    /**
-     * Returns the directory or archive path by which the code produced by this task
-     * is published to other {@code ScalaCompile} tasks.
-     *
-     * @return the directory or archive path by which the code produced by this task
-     * is published to other {@code ScalaCompile} tasks
-     */
-    // only an input for other task instances
-    public File getPublishedCode() {
-        return publishedCode;
-    }
-
-    /**
-     * Sets the directory or archive path by which the code produced by this task
-     * is published to other {@code ScalaCompile} tasks.
-     */
-    public void setPublishedCode(File publishedCode) {
-        this.publishedCode = publishedCode;
-    }
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
index 9afceca..a0ddcd7 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
@@ -15,56 +15,42 @@
  */
 package org.gradle.api.tasks.scala;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.gradle.api.AntBuilder;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Project;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.project.IsolatedAntBuilder;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.compile.Compiler;
+import org.gradle.api.internal.tasks.compile.JavaCompilerFactory;
+import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonFactory;
 import org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonManager;
-import org.gradle.api.internal.tasks.scala.*;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.plugins.ExtraPropertiesExtension;
+import org.gradle.api.internal.tasks.scala.CleaningScalaCompiler;
+import org.gradle.api.internal.tasks.scala.ScalaCompileSpec;
+import org.gradle.api.internal.tasks.scala.ScalaCompilerFactory;
+import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
 import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.compile.AbstractCompile;
-import org.gradle.api.tasks.compile.CompileOptions;
-import org.gradle.internal.Factory;
+import org.gradle.language.scala.tasks.AbstractScalaCompile;
 
 import javax.inject.Inject;
-import java.io.File;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Compiles Scala source files, and optionally, Java source files.
  */
-public class ScalaCompile extends AbstractCompile {
-    private static final Logger LOGGER = Logging.getLogger(ScalaCompile.class);
+public class ScalaCompile extends AbstractScalaCompile {
 
     private FileCollection scalaClasspath;
     private FileCollection zincClasspath;
-    private Compiler<ScalaJavaJointCompileSpec> compiler;
-    private final CompileOptions compileOptions = new CompileOptions();
-    private final ScalaCompileOptions scalaCompileOptions = new ScalaCompileOptions();
+
+    private org.gradle.language.base.internal.compile.Compiler<ScalaJavaJointCompileSpec> compiler;
 
     @Inject
     public ScalaCompile() {
-        ProjectInternal projectInternal = (ProjectInternal) getProject();
-        IsolatedAntBuilder antBuilder = getServices().get(IsolatedAntBuilder.class);
-        Factory<AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
-        CompilerDaemonManager compilerDaemonManager = getServices().get(CompilerDaemonManager.class);
-        ScalaCompilerFactory scalaCompilerFactory = new ScalaCompilerFactory(projectInternal, antBuilder, antBuilderFactory, compilerDaemonManager);
-        Compiler<ScalaJavaJointCompileSpec> delegatingCompiler = new DelegatingScalaCompiler(scalaCompilerFactory);
-        compiler = new CleaningScalaCompiler(delegatingCompiler, getOutputs());
+        super(new ScalaCompileOptions());
+    }
+
+    @Nested
+    @Override
+    public ScalaCompileOptions getScalaCompileOptions() {
+        return (ScalaCompileOptions) super.getScalaCompileOptions();
     }
 
     /**
@@ -80,8 +66,7 @@ public class ScalaCompile extends AbstractCompile {
     }
 
     /**
-     * Returns the classpath to use to load the Zinc incremental compiler.
-     * This compiler in turn loads the Scala compiler.
+     * Returns the classpath to use to load the Zinc incremental compiler. This compiler in turn loads the Scala compiler.
      */
     @InputFiles
     public FileCollection getZincClasspath() {
@@ -93,95 +78,42 @@ public class ScalaCompile extends AbstractCompile {
     }
 
     /**
-     * Returns the Scala compilation options.
-     */
-    @Nested
-    public ScalaCompileOptions getScalaCompileOptions() {
-        return scalaCompileOptions;
-    }
-
-    /**
-     * Returns the Java compilation options.
-     */
-    @Nested
-    public CompileOptions getOptions() {
-        return compileOptions;
-    }
-
-    /**
      * For testing only.
      */
-    void setCompiler(Compiler<ScalaJavaJointCompileSpec> compiler) {
+    public void setCompiler(org.gradle.language.base.internal.compile.Compiler<ScalaJavaJointCompileSpec> compiler) {
         this.compiler = compiler;
     }
 
-    @TaskAction
-    protected void compile() {
-        checkScalaClasspathIsNonEmpty();
-        DefaultScalaJavaJointCompileSpec spec = new DefaultScalaJavaJointCompileSpec();
-        spec.setSource(getSource());
-        spec.setDestinationDir(getDestinationDir());
-        spec.setClasspath(getClasspath());
-        spec.setScalaClasspath(getScalaClasspath());
-        spec.setZincClasspath(getZincClasspath());
-        spec.setSourceCompatibility(getSourceCompatibility());
-        spec.setTargetCompatibility(getTargetCompatibility());
-        spec.setCompileOptions(compileOptions);
-        spec.setScalaCompileOptions(scalaCompileOptions);
-        if (!scalaCompileOptions.isUseAnt()) {
-            configureIncrementalCompilation(spec);
+    protected org.gradle.language.base.internal.compile.Compiler<ScalaJavaJointCompileSpec> getCompiler(ScalaJavaJointCompileSpec spec) {
+        assertScalaClasspathIsNonEmpty();
+        if (compiler == null) {
+            ProjectInternal projectInternal = (ProjectInternal) getProject();
+            IsolatedAntBuilder antBuilder = getServices().get(IsolatedAntBuilder.class);
+            CompilerDaemonFactory compilerDaemonFactory = getServices().get(CompilerDaemonManager.class);
+            JavaCompilerFactory javaCompilerFactory = getServices().get(JavaCompilerFactory.class);
+            ScalaCompilerFactory scalaCompilerFactory = new ScalaCompilerFactory(projectInternal.getRootProject().getProjectDir(), antBuilder, javaCompilerFactory, compilerDaemonFactory, getScalaClasspath(), getZincClasspath());
+            compiler = scalaCompilerFactory.newCompiler(spec);
+            if (getScalaCompileOptions().isUseAnt()) {
+                compiler = new CleaningScalaCompiler(compiler, getOutputs());
+            }
         }
-
-        compiler.execute(spec);
+        return compiler;
     }
 
-    private void checkScalaClasspathIsNonEmpty() {
-        if (getScalaClasspath().isEmpty()) {
-            throw new InvalidUserDataException("'" + getName() + ".scalaClasspath' must not be empty. If a Scala compile dependency is provided, "
-                    + "the 'scala-base' plugin will attempt to configure 'scalaClasspath' automatically. Alternatively, you may configure 'scalaClasspath' explicitly.");
+    @Override
+    protected void configureIncrementalCompilation(ScalaCompileSpec spec) {
+        if (getScalaCompileOptions().isUseAnt()) {
+            // Don't use incremental compilation with ant-backed compiler
+            return;
         }
+        super.configureIncrementalCompilation(spec);
     }
 
-    private void configureIncrementalCompilation(ScalaCompileSpec spec) {
-        Map<File, File> globalAnalysisMap = getOrCreateGlobalAnalysisMap();
-        HashMap<File, File> filteredMap = filterForClasspath(globalAnalysisMap, spec.getClasspath());
-        spec.setAnalysisMap(filteredMap);
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Analysis file: {}", scalaCompileOptions.getIncrementalOptions().getAnalysisFile());
-            LOGGER.debug("Published code: {}", scalaCompileOptions.getIncrementalOptions().getPublishedCode());
-            LOGGER.debug("Analysis map: {}", filteredMap);
-        }
-    }
 
-    @SuppressWarnings("unchecked")
-    private Map<File, File> getOrCreateGlobalAnalysisMap() {
-        ExtraPropertiesExtension extraProperties = getProject().getRootProject().getExtensions().getExtraProperties();
-        Map<File, File> analysisMap;
-
-        if (extraProperties.has("scalaCompileAnalysisMap")) {
-            analysisMap = (Map) extraProperties.get("scalaCompileAnalysisMap");
-        } else {
-            analysisMap = Maps.newHashMap();
-            for (Project project : getProject().getRootProject().getAllprojects()) {
-                for (ScalaCompile task : project.getTasks().withType(ScalaCompile.class)) {
-                    if (task.getScalaCompileOptions().isUseAnt()) { continue; }
-                    File publishedCode = task.getScalaCompileOptions().getIncrementalOptions().getPublishedCode();
-                    File analysisFile = task.getScalaCompileOptions().getIncrementalOptions().getAnalysisFile();
-                    analysisMap.put(publishedCode, analysisFile);
-                }
-            }
-            extraProperties.set("scalaCompileAnalysisMap", Collections.unmodifiableMap(analysisMap));
+    protected void assertScalaClasspathIsNonEmpty() {
+        if (getScalaClasspath().isEmpty()) {
+            throw new InvalidUserDataException("'" + getName() + ".scalaClasspath' must not be empty. If a Scala compile dependency is provided, "
+                    + "the 'scala-base' plugin will attempt to configure 'scalaClasspath' automatically. Alternatively, you may configure 'scalaClasspath' explicitly.");
         }
-        return analysisMap;
-    }
-
-    private HashMap<File, File> filterForClasspath(Map<File, File> analysisMap, Iterable<File> classpath) {
-        final Set<File> classpathLookup = Sets.newHashSet(classpath);
-        return Maps.newHashMap(Maps.filterEntries(analysisMap, new Predicate<Map.Entry<File, File>>() {
-            public boolean apply(Map.Entry<File, File> entry) {
-                return classpathLookup.contains(entry.getKey());
-            }
-        }));
     }
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.java
index 66b92fb..6f5e7f6 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.java
@@ -17,20 +17,12 @@ package org.gradle.api.tasks.scala;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
-
-import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.compile.AbstractOptions;
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.Optional;
-
-import java.util.List;
+import org.gradle.language.scala.tasks.BaseScalaCompileOptions;
 
 /**
- * Options for Scala compilation.
+ * Options for Scala compilation, including the use of the Ant-backed compiler.
  */
-public class ScalaCompileOptions extends AbstractOptions {
-    private static final long serialVersionUID = 0;
-
+public class ScalaCompileOptions extends BaseScalaCompileOptions {
     private static final ImmutableMap<String, String> FIELD_NAMES_TO_ANT_PROPERTIES = new ImmutableMap.Builder<String, String>()
             .put("loggingLevel", "logging")
             .put("loggingPhases", "logphase")
@@ -42,221 +34,29 @@ public class ScalaCompileOptions extends AbstractOptions {
             .put("additionalParameters", "addparams")
             .build();
 
-    private boolean useCompileDaemon;
-
-    private String daemonServer;
-
-    private boolean failOnError = true;
-
-    private boolean deprecation = true;
-
-    private boolean unchecked = true;
-
-    private String debugLevel;
-
-    private boolean optimize;
-
-    private String encoding;
-
-    private String force = "never";
-
-    private String targetCompatibility;
-
-    private List<String> additionalParameters;
-
-    private boolean listFiles;
-
-    private String loggingLevel;
-
-    private List<String> loggingPhases;
-
     private boolean fork;
 
-    private ScalaForkOptions forkOptions = new ScalaForkOptions();
-
     private boolean useAnt = true;
 
-    private IncrementalCompileOptions incrementalOptions = new IncrementalCompileOptions();
-
-    /**
-     * Whether to use the fsc compile daemon.
-     */
-    public boolean isUseCompileDaemon() {
-        return useCompileDaemon;
-    }
-
-    public void setUseCompileDaemon(boolean useCompileDaemon) {
-        this.useCompileDaemon = useCompileDaemon;
-    }
-
-    // NOTE: Does not work for scalac 2.7.1 due to a bug in the Ant task
-    /**
-     * Server (host:port) on which the compile daemon is running.
-     * The host must share disk access with the client process.
-     * If not specified, launches the daemon on the localhost.
-     * This parameter can only be specified if useCompileDaemon is true.
-     */
-    public String getDaemonServer() {
-        return daemonServer;
-    }
-
-    public void setDaemonServer(String daemonServer) {
-        this.daemonServer = daemonServer;
-    }
-
-    /**
-     * Fail the build on compilation errors.
-     */
-    public boolean isFailOnError() {
-        return failOnError;
-    }
-
-    public void setFailOnError(boolean failOnError) {
-        this.failOnError = failOnError;
-    }
-
-    /**
-     * Generate deprecation information.
-     */
-    public boolean isDeprecation() {
-        return deprecation;
-    }
-
-    public void setDeprecation(boolean deprecation) {
-        this.deprecation = deprecation;
-    }
-
-    /**
-     * Generate unchecked information.
-     */
-    public boolean isUnchecked() {
-        return unchecked;
-    }
-
-    public void setUnchecked(boolean unchecked) {
-        this.unchecked = unchecked;
-    }
-
-    /**
-     * Generate debugging information.
-     * Legal values: none, source, line, vars, notailcalls
-     */
-    @Input @Optional
-    public String getDebugLevel() {
-        return debugLevel;
-    }
-
-    public void setDebugLevel(String debugLevel) {
-        this.debugLevel = debugLevel;
-    }
-
-    /**
-     * Run optimizations.
-     */
-    @Input
-    public boolean isOptimize() {
-        return optimize;
-    }
-
-    public void setOptimize(boolean optimize) {
-        this.optimize = optimize;
-    }
-
-    /**
-     * Encoding of source files.
-     */
-    @Input @Optional
-    public String getEncoding() {
-        return encoding;
-    }
-
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-    }
-
-    /**
-     * Whether to force the compilation of all files.
-     * Legal values:
-     * - never (only compile modified files)
-     * - changed (compile all files when at least one file is modified)
-     * - always (always recompile all files)
-     */
-    public String getForce() {
-        return force;
-    }
-
-    public void setForce(String force) {
-        this.force = force;
-    }
-
-    /**
-     * Returns which backend is to be used.
-     *
-     * @deprecated use {@link ScalaCompile#getTargetCompatibility} instead
-     */
-    @Input
-    @Optional
-    @Deprecated
-    public String getTargetCompatibility() {
-        return targetCompatibility;
-    }
-
-    /**
-     * Sets which backend is to be used.
-     *
-     * @deprecated use {@link ScalaCompile#setTargetCompatibility} instead
-     */
-    @Deprecated
-    public void setTargetCompatibility(String targetCompatibility) {
-        this.targetCompatibility = targetCompatibility;
-    }
-
-    /**
-     * Additional parameters passed to the compiler.
-     * Each parameter must start with '-'.
-     */
-    public List<String> getAdditionalParameters() {
-        return additionalParameters;
-    }
-
-    public void setAdditionalParameters(List<String> additionalParameters) {
-        this.additionalParameters = additionalParameters;
-    }
-
-    /**
-     * List files to be compiled.
-     */
-    public boolean isListFiles() {
-        return listFiles;
-    }
-
-    public void setListFiles(boolean listFiles) {
-        this.listFiles = listFiles;
-    }
-
-    /**
-     * Specifies the amount of logging.
-     * Legal values:  none, verbose, debug
-     */
-    public String getLoggingLevel() {
-        return loggingLevel;
-    }
+    private boolean useCompileDaemon;
 
-    public void setLoggingLevel(String loggingLevel) {
-        this.loggingLevel = loggingLevel;
-    }
+    private String daemonServer;
 
     /**
-     * Phases of the compiler to log.
-     * Legal values: namer, typer, pickler, uncurry, tailcalls, transmatch, explicitouter, erasure,
-     *               lambdalift, flatten, constructors, mixin, icode, jvm, terminal.
+     * Tells whether to use Ant for compilation. If {@code true}, the standard Ant scalac (or fsc) task will be used for
+     * Scala and Java joint compilation. If {@code false}, the Zinc incremental compiler will be used
+     * instead. The latter can be significantly faster, especially if there are few source code changes
+     * between compiler runs. Defaults to {@code true}.
      */
-    public List<String> getLoggingPhases() {
-        return loggingPhases;
+    public boolean isUseAnt() {
+        return useAnt;
     }
 
-    public void setLoggingPhases(List<String> loggingPhases) {
-        this.loggingPhases = loggingPhases;
+    public void setUseAnt(boolean useAnt) {
+        this.useAnt = useAnt;
+        if (!useAnt) {
+            setFork(true);
+        }
     }
 
     /**
@@ -273,41 +73,29 @@ public class ScalaCompileOptions extends AbstractOptions {
     }
 
     /**
-     * Options for running the Scala compiler in a separate process. These options only take effect
-     * if {@code fork} is set to {@code true}.
+     * Whether to use the fsc compile daemon.
      */
-    public ScalaForkOptions getForkOptions() {
-        return forkOptions;
+    public boolean isUseCompileDaemon() {
+        return useCompileDaemon;
     }
 
-    public void setForkOptions(ScalaForkOptions forkOptions) {
-        this.forkOptions = forkOptions;
+    public void setUseCompileDaemon(boolean useCompileDaemon) {
+        this.useCompileDaemon = useCompileDaemon;
     }
 
+    // NOTE: Does not work for scalac 2.7.1 due to a bug in the Ant task
     /**
-     * Tells whether to use Ant for compilation. If {@code true}, the standard Ant scalac (or fsc) task will be used for
-     * Scala and Java joint compilation. If {@code false}, the Zinc incremental compiler will be used
-     * instead. The latter can be significantly faster, especially if there are few source code changes
-     * between compiler runs. Defaults to {@code true}.
+     * Server (host:port) on which the compile daemon is running.
+     * The host must share disk access with the client process.
+     * If not specified, launches the daemon on the localhost.
+     * This parameter can only be specified if useCompileDaemon is true.
      */
-    public boolean isUseAnt() {
-        return useAnt;
-    }
-
-    public void setUseAnt(boolean useAnt) {
-        this.useAnt = useAnt;
-        if (!useAnt) {
-            setFork(true);
-        }
-    }
-
-    @Nested
-    public IncrementalCompileOptions getIncrementalOptions() {
-        return incrementalOptions;
+    public String getDaemonServer() {
+        return daemonServer;
     }
 
-    public void setIncrementalOptions(IncrementalCompileOptions incrementalOptions) {
-        this.incrementalOptions = incrementalOptions;
+    public void setDaemonServer(String daemonServer) {
+        this.daemonServer = daemonServer;
     }
 
     protected boolean excludeFromAntProperties(String fieldName) {
@@ -316,7 +104,7 @@ public class ScalaCompileOptions extends AbstractOptions {
                 || fieldName.equals("useAnt")
                 || fieldName.equals("incrementalOptions")
                 || fieldName.equals("targetCompatibility") // handled directly by AntScalaCompiler
-                || fieldName.equals("optimize") && !optimize;
+                || fieldName.equals("optimize") && !isOptimize();
     }
 
     protected String getAntPropertyName(String fieldName) {
@@ -336,9 +124,6 @@ public class ScalaCompileOptions extends AbstractOptions {
         if (fieldName.equals("optimize")) {
             return toOnOffString(isOptimize());
         }
-        if (fieldName.equals("targetCompatibility")) {
-            return String.format("jvm-%s", getTargetCompatibility());
-        }
         if (fieldName.equals("loggingPhases")) {
             return getLoggingPhases().isEmpty() ? " " : Joiner.on(',').join(getLoggingPhases());
         }
@@ -351,4 +136,6 @@ public class ScalaCompileOptions extends AbstractOptions {
     private String toOnOffString(boolean flag) {
         return flag ? "on" : "off";
     }
+
+
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
index 8538684..7b5985e 100644
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
@@ -20,6 +20,7 @@ import org.gradle.api.internal.project.IsolatedAntBuilder;
 import org.gradle.api.tasks.*;
 import org.gradle.util.GUtil;
 
+import javax.inject.Inject;
 import java.io.File;
 
 /**
@@ -31,16 +32,12 @@ public class ScalaDoc extends SourceTask {
 
     private FileCollection classpath;
     private FileCollection scalaClasspath;
-    private AntScalaDoc antScalaDoc = new AntScalaDoc(getServices().get(IsolatedAntBuilder.class));
     private ScalaDocOptions scalaDocOptions = new ScalaDocOptions();
     private String title;
 
-    public AntScalaDoc getAntScalaDoc() {
-        return antScalaDoc;
-    }
-
-    public void setAntScalaDoc(AntScalaDoc antScalaDoc) {
-        this.antScalaDoc = antScalaDoc;
+    @Inject
+    protected IsolatedAntBuilder getAntBuilder() {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -61,7 +58,7 @@ public class ScalaDoc extends SourceTask {
      * @return The classpath.
      */
     @InputFiles
-    public Iterable<File> getClasspath() {
+    public FileCollection getClasspath() {
         return classpath;
     }
 
@@ -111,7 +108,8 @@ public class ScalaDoc extends SourceTask {
         if (!GUtil.isTrue(options.getDocTitle())) {
             options.setDocTitle(getTitle());
         }
-        getAntScalaDoc().execute(getSource(), getDestinationDir(), getClasspath(), getScalaClasspath(), options);
+        AntScalaDoc antScalaDoc = new AntScalaDoc(getAntBuilder());
+        antScalaDoc.execute(getSource(), getDestinationDir(), getClasspath(), getScalaClasspath(), options);
     }
 
 }
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.groovy b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.groovy
deleted file mode 100644
index da74b70..0000000
--- a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.groovy
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.scala
-
-import org.gradle.api.tasks.compile.AbstractOptions
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.InputFile
-
-public class ScalaDocOptions extends AbstractOptions {
-
-    /**
-     * Generate deprecation information.
-     */
-    @Input
-    boolean deprecation = true
-
-    /**
-     * Generate unchecked information.
-     */
-    @Input
-    boolean unchecked = true
-
-    /**
-     * Text to appear in the window title.
-     */
-    @Input @Optional
-    String windowTitle
-
-    /**
-     * Html text to appear in the main frame title.
-     */
-    @Input @Optional
-    String docTitle
-
-    /**
-     * Html text to appear in the header for each page.
-     */
-    @Input @Optional
-    String header
-
-    /**
-     * Html text to appear in the footer for each page.
-     */
-    @Input @Optional
-    String footer
-
-    /**
-     * Html text to appear in the top text for each page.
-     */
-    @Input @Optional
-    String top
-
-    /**
-     * Html text to appear in the bottom text for each page.
-     */
-    @Input @Optional
-    String bottom
-
-    /**
-     * Style sheet to override default style.
-     */
-    @InputFile @Optional
-    File styleSheet
-
-    /**
-     * Additional parameters passed to the compiler.
-     * Each parameter must start with '-'.
-     */
-    @Input @Optional
-    List<String> additionalParameters
-
-    @Override
-    protected String getAntPropertyName(String fieldName) {
-        if (fieldName == "additionalParameters") {
-            return "addParams"
-        }
-        return fieldName
-    }
-
-    @Override
-    protected Object getAntPropertyValue(String fieldName, Object value) {
-        if (fieldName == "deprecation") {
-            return toOnOffString(deprecation)
-        }
-        if (fieldName == "unchecked") {
-            return toOnOffString(unchecked)
-        }
-        if (fieldName == "additionalParameters") {
-            return additionalParameters.isEmpty() ? ' ' : additionalParameters.join(' ')
-        }
-        return value
-    }
-
-    private String toOnOffString(value) {
-        return value ? 'on' : 'off'
-    }
-
-}
diff --git a/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.java b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.java
new file mode 100644
index 0000000..d8cd53a
--- /dev/null
+++ b/subprojects/scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDocOptions.java
@@ -0,0 +1,220 @@
+/*
+ * 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.scala;
+
+import com.google.common.base.Joiner;
+import org.gradle.api.tasks.compile.AbstractOptions;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.InputFile;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Options for the ScalaDoc tool.
+ */
+public class ScalaDocOptions extends AbstractOptions {
+    private boolean deprecation = true;
+    private boolean unchecked = true;
+    private String windowTitle;
+    private String docTitle;
+    private String header;
+    private String footer;
+    private String top;
+    private String bottom;
+    private File styleSheet;
+    private List<String> additionalParameters;
+
+    /**
+     * Tells whether to generate deprecation information.
+     */
+    @Input
+    public boolean isDeprecation() {
+        return deprecation;
+    }
+
+    /**
+     * Sets whether to generate deprecation information.
+     */
+    public void setDeprecation(boolean deprecation) {
+        this.deprecation = deprecation;
+    }
+
+    /**
+     * Tells whether to generate unchecked information.
+     */
+    @Input
+    public boolean isUnchecked() {
+        return unchecked;
+    }
+
+    /**
+     * Sets whether to generate unchecked information.
+     */
+    public void setUnchecked(boolean unchecked) {
+        this.unchecked = unchecked;
+    }
+
+    /**
+     * Returns the text to appear in the window title.
+     */
+    @Input @Optional
+    public String getWindowTitle() {
+        return windowTitle;
+    }
+
+    /**
+     * Sets the text to appear in the window title.
+     */
+    public void setWindowTitle(String windowTitle) {
+        this.windowTitle = windowTitle;
+    }
+
+    /**
+     * Returns the HTML text to appear in the main frame title.
+     */
+    @Input @Optional
+    public String getDocTitle() {
+        return docTitle;
+    }
+
+    /**
+     * Sets the HTML text to appear in the main frame title.
+     */
+    public void setDocTitle(String docTitle) {
+        this.docTitle = docTitle;
+    }
+
+    /**
+     * Returns the HTML text to appear in the header for each page.
+     */
+    @Input @Optional
+    public String getHeader() {
+        return header;
+    }
+
+    /**
+     * Sets the HTML text to appear in the header for each page.
+     */
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    /**
+     * Returns the HTML text to appear in the footer for each page.
+     */
+    @Input @Optional
+    public String getFooter() {
+        return footer;
+    }
+
+    /**
+     * Sets the HTML text to appear in the footer for each page.
+     */
+    public void setFooter(String footer) {
+        this.footer = footer;
+    }
+
+    /**
+     * Returns the HTML text to appear in the top text for each page.
+     */
+    @Input @Optional
+    public String getTop() {
+        return top;
+    }
+
+    /**
+     * Sets the HTML text to appear in the top text for each page.
+     */
+    public void setTop(String top) {
+        this.top = top;
+    }
+
+    /**
+     * Returns the HTML text to appear in the bottom text for each page.
+     */
+    @Input @Optional
+    public String getBottom() {
+        return bottom;
+    }
+
+    /**
+     * Sets the HTML text to appear in the bottom text for each page.
+     */
+    public void setBottom(String bottom) {
+        this.bottom = bottom;
+    }
+
+    /**
+     * Returns the style sheet to override default style.
+     */
+    @InputFile @Optional
+    public File getStyleSheet() {
+        return styleSheet;
+    }
+
+    /**
+     * Sets the style sheet to override default style.
+     */
+    public void setStyleSheet(File styleSheet) {
+        this.styleSheet = styleSheet;
+    }
+
+    /**
+     * Returns the additional parameters passed to the compiler.
+     * Each parameter starts with '-'.
+     */
+    @Input @Optional
+    public List<String> getAdditionalParameters() {
+        return additionalParameters;
+    }
+
+    /**
+     * Sets the additional parameters passed to the compiler.
+     * Each parameter must start with '-'.
+     */
+    public void setAdditionalParameters(List<String> additionalParameters) {
+        this.additionalParameters = additionalParameters;
+    }
+
+    @Override
+    protected String getAntPropertyName(String fieldName) {
+        if (fieldName.equals("additionalParameters")) {
+            return "addParams";
+        }
+        return fieldName;
+    }
+
+    @Override
+    protected Object getAntPropertyValue(String fieldName, Object value) {
+        if (fieldName.equals("deprecation")) {
+            return toOnOffString(deprecation);
+        }
+        if (fieldName.equals("unchecked")) {
+            return toOnOffString(unchecked);
+        }
+        if (fieldName.equals("additionalParameters")) {
+            return additionalParameters.isEmpty() ? ' ' : Joiner.on(' ').join(additionalParameters);
+        }
+        return value;
+    }
+
+    private String toOnOffString(boolean value) {
+        return value ? "on" : "off";
+    }
+
+}
diff --git a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala-base.properties
similarity index 100%
rename from subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala-base.properties
rename to subprojects/scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala-base.properties
diff --git a/subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties b/subprojects/scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala.properties
similarity index 100%
rename from subprojects/scala/src/main/resources/META-INF/gradle-plugins/scala.properties
rename to subprojects/scala/src/main/resources/META-INF/gradle-plugins/org.gradle.scala.properties
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
index 669ca6a..ffd9b4a 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/DefaultScalaSourceSetTest.groovy
@@ -17,12 +17,17 @@ package org.gradle.api.internal.tasks
 
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.file.FileResolver
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.junit.Test
 import static org.gradle.util.Matchers.isEmpty
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.assertThat
 
 class DefaultScalaSourceSetTest {
+    static {
+        NativeServicesTestFixture.initialize()
+    }
+
     private final DefaultScalaSourceSet sourceSet = new DefaultScalaSourceSet("<set-display-name>", [resolve: {it as File}] as FileResolver)
 
     @Test
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompilerTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompilerTest.groovy
index e374c55..1074837 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompilerTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/DefaultScalaJavaJointCompilerTest.groovy
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 package org.gradle.api.internal.tasks.scala
-
-import spock.lang.Specification
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.FileTree
-import org.gradle.api.internal.tasks.compile.JvmLanguageCompileSpec
-import org.gradle.api.internal.tasks.compile.Compiler
 import org.gradle.api.internal.tasks.compile.JavaCompileSpec
+import org.gradle.language.base.internal.compile.Compiler
+import spock.lang.Specification
 
 class DefaultScalaJavaJointCompilerTest extends Specification {
     private final Compiler<ScalaCompileSpec> scalaCompiler = Mock()
-    private final JvmLanguageCompileSpec scalaSpec = Mock()
     private final Compiler<JavaCompileSpec> javaCompiler = Mock()
     private final FileCollection source = Mock()
     private final FileTree sourceTree = Mock()
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy
deleted file mode 100644
index 2a2b2c0..0000000
--- a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/NormalizingScalaCompilerTest.groovy
+++ /dev/null
@@ -1,127 +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.tasks.scala
-
-import org.gradle.api.tasks.WorkResult
-import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.api.tasks.scala.ScalaCompileOptions
-import org.gradle.api.internal.file.collections.SimpleFileCollection
-import org.gradle.api.internal.tasks.compile.CompilationFailedException
-import org.gradle.api.internal.tasks.compile.Compiler
-
-import groovy.transform.InheritConstructors
-
-import spock.lang.Specification
-
-class NormalizingScalaCompilerTest extends Specification {
-    Compiler<ScalaJavaJointCompileSpec> target = Mock()
-    DefaultScalaJavaJointCompileSpec spec = new DefaultScalaJavaJointCompileSpec()
-    NormalizingScalaCompiler compiler = new NormalizingScalaCompiler(target)
-
-    def setup() {
-        spec.source = files("Source1.java", "Source2.java", "Source3.java")
-        spec.classpath = files("Dep1.jar", "Dep2.jar", "Dep3.jar")
-        spec.scalaClasspath = files("scala-compiler.jar", "scala-library.jar")
-        spec.zincClasspath = files("zinc.jar", "zinc-dep.jar")
-        spec.compileOptions = new CompileOptions()
-        spec.scalaCompileOptions = new ScalaCompileOptions()
-    }
-
-    def "delegates to target compiler after resolving source and classpaths"() {
-        WorkResult workResult = Mock()
-
-        when:
-        def result = compiler.execute(spec)
-
-        then:
-        1 * target.execute(spec) >> {
-            assert spec.source.getClass() == SimpleFileCollection
-            assert spec.source as List == old(spec.source as List)
-
-            assert spec.classpath.getClass() == SimpleFileCollection
-            assert spec.classpath as List == old(spec.classpath as List)
-
-            assert spec.scalaClasspath.getClass() == SimpleFileCollection
-            assert spec.scalaClasspath as List == old(spec.scalaClasspath as List)
-
-            assert spec.zincClasspath.getClass() == SimpleFileCollection
-            assert spec.zincClasspath as List == old(spec.zincClasspath as List)
-
-            workResult
-        }
-        result == workResult
-    }
-
-    def "propagates compile failure"() {
-        def failure
-        target.execute(spec) >> { throw failure = new CompilationFailedException() }
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        CompilationFailedException e = thrown()
-        e == failure
-    }
-
-    def "ignores compile failure when failOnError is false"() {
-        target.execute(spec) >> { throw new CompilationFailedException() }
-
-        spec.scalaCompileOptions.failOnError = false
-
-        when:
-        def result = compiler.execute(spec)
-
-        then:
-        noExceptionThrown()
-        !result.didWork
-    }
-
-    def "propagates other failure"() {
-        def failure
-        target.execute(spec) >> { throw failure = new RuntimeException() }
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        RuntimeException e = thrown()
-        e == failure
-    }
-
-    def "resolves any non-strings that make it into custom compiler args"() {
-        spec.compileOptions.compilerArgs << "a dreaded ${"GString"}"
-        spec.compileOptions.compilerArgs << 42
-        assert !spec.compileOptions.compilerArgs.any { it instanceof String }
-
-        when:
-        compiler.execute(spec)
-
-        then:
-        1 * target.execute(_) >> { ScalaJavaJointCompileSpec spec ->
-            assert spec.compileOptions.compilerArgs.every { it instanceof String }
-        }
-    }
-
-    private files(String... paths) {
-        new TestFileCollection(paths.collect { new File(it) })
-    }
-
-    // file collection whose type is distinguishable from SimpleFileCollection
-    @InheritConstructors
-    static class TestFileCollection extends SimpleFileCollection {}
-}
-
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGeneratorTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGeneratorTest.groovy
deleted file mode 100644
index 6a75e6f..0000000
--- a/subprojects/scala/src/test/groovy/org/gradle/api/internal/tasks/scala/ScalaCompilerArgumentsGeneratorTest.groovy
+++ /dev/null
@@ -1,105 +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.tasks.scala
-
-import spock.lang.Specification
-
-class ScalaCompilerArgumentsGeneratorTest extends Specification {
-    def generator = new ScalaCompilerArgumentsGenerator()
-    def spec = new DefaultScalaCompileSpec()
-
-    def "default options"() {
-        expect:
-        generator.generate(spec) as Set == ["-deprecation", "-unchecked"] as Set
-    }
-
-    def "can suppress deprecation flag"() {
-        spec.scalaCompileOptions.deprecation = false
-
-        expect:
-        !generator.generate(spec).contains("-deprecation")
-    }
-
-    def "can suppress unchecked flag"() {
-        spec.scalaCompileOptions.unchecked = false
-
-        expect:
-        !generator.generate(spec).contains("-unchecked")
-    }
-
-    def "generates debug level option"() {
-        spec.scalaCompileOptions.debugLevel = "someLevel"
-
-        expect:
-        generator.generate(spec).contains("-g:someLevel")
-    }
-
-    def "generates optimize flag"() {
-        spec.scalaCompileOptions.optimize = true
-
-        expect:
-        generator.generate(spec).contains("-optimise")
-    }
-
-    def "generates encoding option"() {
-        spec.scalaCompileOptions.encoding = "some encoding"
-
-        when:
-        def args = generator.generate(spec)
-
-        then:
-        args.contains("-encoding")
-        args.contains("some encoding")
-    }
-
-    def "generates verbose flag"() {
-        spec.scalaCompileOptions.debugLevel = "verbose"
-
-        expect:
-        generator.generate(spec).contains("-verbose")
-    }
-
-    def "generates debug flag"() {
-        spec.scalaCompileOptions.debugLevel = "debug"
-
-        expect:
-        generator.generate(spec).contains("-Ydebug")
-    }
-
-    def "generates logging phases options"() {
-        spec.scalaCompileOptions.loggingPhases = ["foo", "bar", "baz"]
-
-        when:
-        def args = generator.generate(spec)
-
-        then:
-        args.contains("-Ylog:foo")
-        args.contains("-Ylog:bar")
-        args.contains("-Ylog:baz")
-    }
-
-    def "adds any additional parameters"() {
-        spec.scalaCompileOptions.additionalParameters = ["-other", "value"]
-
-        when:
-        def args = generator.generate(spec)
-
-        then:
-        args.contains("-other")
-        args.contains("value")
-    }
-}
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaBasePluginTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaBasePluginTest.groovy
index 36bc97c..e9a3a3d 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaBasePluginTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/plugins/scala/ScalaBasePluginTest.groovy
@@ -15,14 +15,14 @@
  */
 package org.gradle.api.plugins.scala
 
-import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
 import org.gradle.api.internal.artifacts.configurations.Configurations
+import org.gradle.api.internal.project.DefaultProject
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.reporting.ReportingExtension
 import org.gradle.api.tasks.bundling.Jar
 import org.gradle.api.tasks.scala.ScalaCompile
 import org.gradle.api.tasks.scala.ScalaDoc
-import org.gradle.api.artifacts.Configuration
 import org.gradle.util.TestUtil
 import org.junit.Before
 import org.junit.Test
@@ -35,32 +35,28 @@ import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
 public class ScalaBasePluginTest {
-    private final Project project = TestUtil.createRootProject()
+    private final DefaultProject project = TestUtil.createRootProject()
 
     @Before
     void before() {
-        project.plugins.apply(ScalaBasePlugin)
+        project.pluginManager.apply(ScalaBasePlugin)
     }
 
-    @Test void appliesTheJavaPluginToTheProject() {
+    @Test
+    void appliesTheJavaPluginToTheProject() {
         assertTrue(project.getPlugins().hasPlugin(JavaBasePlugin))
     }
 
-    @Test void addsScalaToolsConfigurationToTheProject() {
-        def configuration = project.configurations.getByName(ScalaBasePlugin.SCALA_TOOLS_CONFIGURATION_NAME)
-        assertThat(Configurations.getNames(configuration.extendsFrom, false), equalTo(toSet()))
-        assertFalse(configuration.visible)
-        assertTrue(configuration.transitive)
-    }
-
-    @Test void addsZincConfigurationToTheProject() {
+    @Test
+    void addsZincConfigurationToTheProject() {
         def configuration = project.configurations.getByName(ScalaBasePlugin.ZINC_CONFIGURATION_NAME)
-        assertThat(Configurations.getNames(configuration.extendsFrom, false), equalTo(toSet()))
+        assertThat(Configurations.getNames(configuration.extendsFrom), equalTo(toSet()))
         assertFalse(configuration.visible)
         assertTrue(configuration.transitive)
     }
 
-    @Test void preconfiguresZincClasspathForCompileTasksThatUseZinc() {
+    @Test
+    void preconfiguresZincClasspathForCompileTasksThatUseZinc() {
         project.sourceSets.create('custom')
         def task = project.tasks.compileCustomScala
         task.scalaCompileOptions.useAnt = false
@@ -68,7 +64,8 @@ public class ScalaBasePluginTest {
         assert task.zincClasspath.dependencies.find { it.name.contains('zinc') }
     }
 
-    @Test void doesNotPreconfigureZincClasspathForCompileTasksThatUseAnt() {
+    @Test
+    void doesNotPreconfigureZincClasspathForCompileTasksThatUseAnt() {
         project.sourceSets.create('custom')
         def task = project.tasks.compileCustomScala
         task.scalaCompileOptions.useAnt = true
@@ -76,13 +73,15 @@ public class ScalaBasePluginTest {
         assert task.zincClasspath.empty
     }
 
-    @Test void addsScalaConventionToNewSourceSet() {
+    @Test
+    void addsScalaConventionToNewSourceSet() {
         def sourceSet = project.sourceSets.create('custom')
         assertThat(sourceSet.scala.displayName, equalTo("custom Scala source"))
         assertThat(sourceSet.scala.srcDirs, equalTo(toLinkedSet(project.file("src/custom/scala"))))
     }
 
-    @Test void addsCompileTaskForNewSourceSet() {
+    @Test
+    void addsCompileTaskForNewSourceSet() {
         project.sourceSets.create('custom')
         def task = project.tasks['compileCustomScala']
         assertThat(task, instanceOf(ScalaCompile.class))
@@ -92,7 +91,8 @@ public class ScalaBasePluginTest {
         assertThat(task, dependsOn('compileCustomJava'))
     }
 
-    @Test void preconfiguresIncrementalCompileOptions() {
+    @Test
+    void preconfiguresIncrementalCompileOptions() {
         project.sourceSets.create('custom')
         project.tasks.create('customJar', Jar)
         ScalaCompile task = project.tasks['compileCustomScala']
@@ -102,7 +102,8 @@ public class ScalaBasePluginTest {
         assertThat(task.scalaCompileOptions.incrementalOptions.publishedCode, equalTo(project.tasks['customJar'].archivePath))
     }
 
-    @Test void incrementalCompileOptionsCanBeOverridden() {
+    @Test
+    void incrementalCompileOptionsCanBeOverridden() {
         project.sourceSets.create('custom')
         project.tasks.create('customJar', Jar)
         ScalaCompile task = project.tasks['compileCustomScala']
@@ -113,20 +114,23 @@ public class ScalaBasePluginTest {
         assertThat(task.scalaCompileOptions.incrementalOptions.analysisFile, equalTo(new File("/my/file")))
         assertThat(task.scalaCompileOptions.incrementalOptions.publishedCode, equalTo(new File("/my/published/code.jar")))
     }
-    
-    @Test void dependenciesOfJavaPluginTasksIncludeScalaCompileTasks() {
+
+    @Test
+    void dependenciesOfJavaPluginTasksIncludeScalaCompileTasks() {
         project.sourceSets.create('custom')
         def task = project.tasks['customClasses']
         assertThat(task, dependsOn(hasItem('compileCustomScala')))
     }
 
-    @Test void configuresCompileTasksDefinedByTheBuildScript() {
+    @Test
+    void configuresCompileTasksDefinedByTheBuildScript() {
         def task = project.task('otherCompile', type: ScalaCompile)
         assertThat(task.source, isEmpty())
         assertThat(task, dependsOn())
     }
 
-    @Test void configuresScalaDocTasksDefinedByTheBuildScript() {
+    @Test
+    void configuresScalaDocTasksDefinedByTheBuildScript() {
         def task = project.task('otherScaladoc', type: ScalaDoc)
         assertThat(task.destinationDir, equalTo(project.file("$project.docsDir/scaladoc")))
         assertThat(task.title, equalTo(project.extensions.getByType(ReportingExtension).apiDocTitle))
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/ScalaRuntimeTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/ScalaRuntimeTest.groovy
index 9620fb1..c629b72 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/ScalaRuntimeTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/ScalaRuntimeTest.groovy
@@ -20,14 +20,13 @@ import org.gradle.api.artifacts.Configuration
 import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection
 import org.gradle.api.plugins.scala.ScalaBasePlugin
 import org.gradle.util.TestUtil
-
 import spock.lang.Specification
 
 class ScalaRuntimeTest extends Specification {
     def project = TestUtil.createRootProject()
 
     def setup() {
-        project.plugins.apply(ScalaBasePlugin)
+        project.pluginManager.apply(ScalaBasePlugin)
     }
 
     def "inferred Scala class path contains 'scala-compiler' repository dependency matching 'scala-library' Jar found on class path"() {
@@ -52,18 +51,6 @@ class ScalaRuntimeTest extends Specification {
         }
     }
 
-    def "inferred Scala class path falls back to 'scalaTools' configuration if the latter is non-empty"() {
-        project.dependencies {
-            scalaTools "org.scala-lang:scala-compiler:2.10.1"
-        }
-
-        when:
-        def classpath = project.scalaRuntime.inferScalaClasspath([new File("other.jar"), new File("scala-library-2.10.1.jar")])
-
-        then:
-        classpath == project.configurations.scalaTools
-    }
-
     def "inference fails if 'scalaTools' configuration is empty and no repository declared"() {
         when:
         def scalaClasspath = project.scalaRuntime.inferScalaClasspath([new File("other.jar"), new File("scala-library-2.10.1.jar")])
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileTest.java b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileTest.java
index 71db9f8..4425e54 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileTest.java
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileTest.java
@@ -17,13 +17,17 @@ package org.gradle.api.tasks.scala;
 
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.ConventionTask;
-import org.gradle.api.internal.tasks.compile.Compiler;
 import org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec;
+import org.gradle.api.tasks.TaskExecutionException;
 import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.AbstractCompileTest;
+import org.gradle.language.base.internal.compile.Compiler;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.JUnit4GroovyMockery;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.hamcrest.core.IsNull;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -33,6 +37,7 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
 import java.io.File;
+import java.util.HashSet;
 
 public class ScalaCompileTest extends AbstractCompileTest {
     @Rule
@@ -72,7 +77,7 @@ public class ScalaCompileTest extends AbstractCompileTest {
             one(scalaCompiler).execute((ScalaJavaJointCompileSpec) with(IsNull.notNullValue()));
         }});
 
-        scalaCompile.compile();
+        scalaCompile.execute();
     }
 
     @Test
@@ -82,10 +87,10 @@ public class ScalaCompileTest extends AbstractCompileTest {
             allowing(scalaClasspath).isEmpty(); will(returnValue(true));
         }});
 
-        thrown.expect(InvalidUserDataException.class);
-        thrown.expectMessage("'testTask.scalaClasspath' must not be empty");
+        thrown.expect(TaskExecutionException.class);
+        thrown.expectCause(new CauseMatcher(InvalidUserDataException.class, "'testTask.scalaClasspath' must not be empty"));
 
-        scalaCompile.compile();
+        scalaCompile.execute();
     }
 
     protected void setUpMocksAndAttributes(final ScalaCompile compile) {
@@ -95,9 +100,38 @@ public class ScalaCompileTest extends AbstractCompileTest {
         compile.setSourceCompatibility("1.5");
         compile.setTargetCompatibility("1.5");
         compile.setDestinationDir(destDir);
-        scalaClasspath = context.mock(FileCollection.class);
+        scalaClasspath = context.mock(FileTree.class);
         compile.setScalaClasspath(scalaClasspath);
-        compile.setClasspath(context.mock(FileCollection.class));
+        final FileTree classpath = context.mock(FileTree.class);
+        final FileTree zincClasspath = context.mock(FileTree.class);
+
+        context.checking(new Expectations(){{
+            allowing(scalaClasspath).getFiles(); will(returnValue(new HashSet<File>()));
+            allowing(classpath).getFiles(); will(returnValue(new HashSet<File>()));
+            allowing(zincClasspath).getFiles(); will(returnValue(new HashSet<File>()));
+        }});
+        compile.setClasspath(classpath);
+        compile.setZincClasspath(zincClasspath);
+        compile.getScalaCompileOptions().getIncrementalOptions().setAnalysisFile(new File("analysisFile"));
     }
 
+
+    private class CauseMatcher<T extends Exception> extends BaseMatcher<T> {
+        private final Class<T> throwableClass;
+        private final String expectedMessage;
+
+        public CauseMatcher(Class<T> throwableClass, String expectedMessage) {
+            this.throwableClass = throwableClass;
+            this.expectedMessage = expectedMessage;
+        }
+
+        public boolean matches(Object item) {
+            return item.getClass().isAssignableFrom(throwableClass)
+                        && ((T)item).getMessage().contains(expectedMessage);
+        }
+
+        public void describeTo(Description description) {
+
+        }
+    }
 }
diff --git a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaDocTest.java b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaDocTest.java
index bfa6d9f..23e3d2c 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaDocTest.java
+++ b/subprojects/scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaDocTest.java
@@ -15,33 +15,21 @@
  */
 package org.gradle.api.tasks.scala;
 
-import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.AbstractTask;
 import org.gradle.api.tasks.AbstractTaskTest;
-import org.gradle.util.GFileUtils;
-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.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
-
 import static org.gradle.api.tasks.compile.AbstractCompileTest.*;
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 
 @RunWith(JMock.class)
 public class ScalaDocTest extends AbstractTaskTest {
     private ScalaDoc scalaDoc;
-    private AntScalaDoc antScalaDocMock;
-    private JUnit4Mockery context = new JUnit4GroovyMockery();
-    private File destDir;
-    private File srcDir;
 
     @Override
     public AbstractTask getTask() {
@@ -50,18 +38,7 @@ public class ScalaDocTest extends AbstractTaskTest {
 
     @Before
     public void setUp() {
-        destDir = getProject().file("destDir");
-        srcDir = getProject().file("src");
-        GFileUtils.touch(new File(srcDir, "file.scala"));
         scalaDoc = createTask(ScalaDoc.class);
-        antScalaDocMock = context.mock(AntScalaDoc.class);
-        scalaDoc.setAntScalaDoc(antScalaDocMock);
-    }
-
-    @Test
-    public void testExecutesAntScalaDoc() {
-        setUpMocksAndAttributes(scalaDoc);
-        scalaDoc.generate();
     }
 
     @Test
@@ -81,30 +58,4 @@ public class ScalaDocTest extends AbstractTaskTest {
         assertSame(scalaDoc.exclude(TEST_PATTERN_3), scalaDoc);
         assertEquals(scalaDoc.getExcludes(), WrapUtil.toLinkedSet(TEST_PATTERN_1, TEST_PATTERN_2, TEST_PATTERN_3));
     }
-
-    @Test
-    public void testSetsDocTitleIfNotSet() {
-        setUpMocksAndAttributes(scalaDoc);
-        scalaDoc.setTitle("title");
-
-        scalaDoc.generate();
-
-        assertThat(scalaDoc.getScalaDocOptions().getDocTitle(), equalTo("title"));
-    }
-
-    private void setUpMocksAndAttributes(final ScalaDoc docTask) {
-        docTask.source(srcDir);
-        docTask.setDestinationDir(destDir);
-        docTask.setScalaClasspath(context.mock(FileCollection.class));
-        docTask.setClasspath(context.mock(FileCollection.class));
-
-        context.checking(new Expectations() {{
-            one(antScalaDocMock).execute(
-                    with(hasSameItems(scalaDoc.getSource())),
-                    with(equalTo(scalaDoc.getDestinationDir())),
-                    with(equalTo(scalaDoc.getClasspath())),
-                    with(equalTo(scalaDoc.getScalaClasspath())),
-                    with(sameInstance(scalaDoc.getScalaDocOptions())));
-        }});
-    }
 }
diff --git a/subprojects/signing/signing.gradle b/subprojects/signing/signing.gradle
index 16e1455..f49af4a 100644
--- a/subprojects/signing/signing.gradle
+++ b/subprojects/signing/signing.gradle
@@ -21,9 +21,7 @@ dependencies {
     compile project(":plugins")
     compile project(":maven")
 
-    compile module("org.bouncycastle:bcpg-jdk15:1.46") {
-        dependency "org.bouncycastle:bcprov-jdk15:1.46 at jar"
-    }
+    compile libraries.bouncycastle_pgp
 }
 
-useTestFixtures()
\ No newline at end of file
+useTestFixtures()
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/NoSigningCredentialsIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/NoSigningCredentialsIntegrationSpec.groovy
index 80b8922..43adb1c 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/NoSigningCredentialsIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/NoSigningCredentialsIntegrationSpec.groovy
@@ -15,6 +15,9 @@
  */
 package org.gradle.plugins.signing
 
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
 class NoSigningCredentialsIntegrationSpec extends SigningIntegrationSpec {
 
     def setup() {
@@ -36,6 +39,7 @@ class NoSigningCredentialsIntegrationSpec extends SigningIntegrationSpec {
         failureHasCause "Cannot perform signing task ':signJar' because it has no configured signatory"
     }
 
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "trying to perform a signing operation without a signatory when not required does not error, and other artifacts still uploaded"() {
         when:
         buildFile << """
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
index 9995fe5..b500567 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningSamplesSpec.groovy
@@ -19,8 +19,10 @@ package org.gradle.plugins.signing
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UsesSample
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.junit.Rule
+import spock.lang.IgnoreIf
 
 class SigningSamplesSpec extends AbstractIntegrationSpec {
     @Rule public final Sample mavenSample = new Sample(temporaryFolder)
@@ -38,6 +40,7 @@ class SigningSamplesSpec extends AbstractIntegrationSpec {
     }
 
     @UsesSample('signing/conditional')
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "conditional signing"() {
         given:
         sample mavenSample
@@ -50,7 +53,7 @@ class SigningSamplesSpec extends AbstractIntegrationSpec {
 
         and:
         final module = repo.module('gradle', 'conditional', '1.0-SNAPSHOT')
-        module.assertArtifactsPublished("conditional-${module.publishArtifactVersion}.pom", "conditional-${module.publishArtifactVersion}.jar")
+        module.assertArtifactsPublished("maven-metadata.xml", "conditional-${module.publishArtifactVersion}.pom", "conditional-${module.publishArtifactVersion}.jar")
     }
 
     MavenFileRepository getRepo() {
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
index fbdab59..ebe0362 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningTasksIntegrationSpec.groovy
@@ -15,8 +15,12 @@
  */
 package org.gradle.plugins.signing
 
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
 class SigningTasksIntegrationSpec extends SigningIntegrationSpec {
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "sign jar with default signatory"() {
         given:
         buildFile << """
@@ -42,7 +46,8 @@ class SigningTasksIntegrationSpec extends SigningIntegrationSpec {
         then:
         ":signJar" in skippedTasks
     }
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "sign multiple jars with default signatory"() {
         given:
         buildFile << """
@@ -86,7 +91,8 @@ class SigningTasksIntegrationSpec extends SigningIntegrationSpec {
         then:
         failureHasCause "You cannot sign tasks that are not 'archive' tasks, such as 'jar', 'zip' etc. (you tried to sign task ':clean')"
     }
-    
+
+    @IgnoreIf({GradleContextualExecuter.parallel})
     def "changes to task information after signing block are respected"() {
         given:
         buildFile << """
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/Sign.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/Sign.groovy
index 5a8302c..d00087a 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/Sign.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/Sign.groovy
@@ -173,7 +173,7 @@ class Sign extends DefaultTask implements SignatureSpec {
     }
     
     /**
-     * Returns the the single signature generated by this task.
+     * Returns the single signature generated by this task.
      *
      * @return The signature.
      * @throws IllegalStateException if there is not exactly one signature.
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SignOperation.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SignOperation.groovy
index 2896b89..a9dd107 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SignOperation.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SignOperation.groovy
@@ -147,7 +147,7 @@ class SignOperation implements SignatureSpec {
     }
     
     /**
-     * Returns the the single registered signature.
+     * Returns the single registered signature.
      *
      * @return The signature.
      * @throws IllegalStateException if there is not exactly one registered signature.
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningExtension.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningExtension.groovy
index 43ec2a1..e76471e 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningExtension.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningExtension.groovy
@@ -362,7 +362,7 @@ class SigningExtension {
         }
 
         // Have to alter the "type" of the artifact to match what is published
-        // See http://issues.gradle.org/browse/GRADLE-1589
+        // See https://issues.gradle.org/browse/GRADLE-1589
         pomSignature.type = "pom." + pomSignature.signatureType.extension
 
         mavenDeployment.addArtifact(pomSignature)
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPlugin.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPlugin.groovy
index c6a2dc9..30536ff 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPlugin.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPlugin.groovy
@@ -26,21 +26,17 @@ import org.gradle.api.plugins.BasePlugin
 class SigningPlugin implements Plugin<Project> {
 
     /**
-     * <p>Adds the ability to digitially sign files and artifacts.</p>
+     * <p>Adds the ability to digitally sign files and artifacts.</p>
      * 
-     * <p>Adds the extensnion {@link org.gradle.plugins.signing.SigningExtension} with the name “signing”.
+     * <p>Adds the extension {@link org.gradle.plugins.signing.SigningExtension} with the name “signing”.
      * <p>Also adds conventions to all {@link org.gradle.plugins.signing.Sign sign tasks} to use the signing extension setting defaults.</p>
      * 
      * @see org.gradle.plugins.signing.SigningExtension
      */
     void apply(Project project) {
-        project.plugins.apply(BasePlugin)
+        project.pluginManager.apply(BasePlugin)
 
         project.extensions.create("signing", SigningExtension, project)
-        def extension = project.signing
-
-        def convention = new SigningPluginConvention(extension)
-        project.convention.plugins.signing = convention
     }
     
 }
\ No newline at end of file
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPluginConvention.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPluginConvention.groovy
deleted file mode 100644
index aa00154..0000000
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/SigningPluginConvention.groovy
+++ /dev/null
@@ -1,78 +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.signing
-
-import org.gradle.api.artifacts.PublishArtifact
-import org.gradle.api.artifacts.maven.MavenDeployment
-import org.gradle.util.DeprecationLogger
-
-/**
- * @deprecated Use {@link SigningExtension}
- */
- at Deprecated
-class SigningPluginConvention {
-    
-    private SigningExtension extension
-    
-    SigningPluginConvention(SigningExtension extension) {
-        this.extension = extension
-    }
-
-    /**
-     * @deprecated Use {@link SigningExtension#sign(PublishArtifact...) project.signing.sign(PublishArtifact...) }
-     */
-    @Deprecated
-    SignOperation sign(PublishArtifact... publishArtifacts) {
-        DeprecationLogger.nagUserOfReplacedMethod("sign()", "signing.sign()")
-        extension.sign(publishArtifacts)
-    }
-
-    /**
-     * @deprecated Use {@link SigningExtension#sign(File...) project.signing.sign(File...) }
-     */
-    @Deprecated
-    SignOperation sign(File... files) {
-        DeprecationLogger.nagUserOfReplacedMethod("sign()", "signing.sign()")
-        extension.sign(files)
-    }
-    
-    /**
-     * @deprecated Use {@link SigningExtension#sign(String, File...) project.signing.sign(String, File...)}
-     */
-    @Deprecated
-    SignOperation sign(String classifier, File... files) {
-        DeprecationLogger.nagUserOfReplacedMethod("sign()", "signing.sign()")
-        extension.sign(classifier, files)
-    }
-    
-    /**
-     * @deprecated Use {@link SigningExtension#sign(Closure) project.signing.sign \{ } }
-     */
-    @Deprecated
-    SignOperation sign(Closure closure) {
-        DeprecationLogger.nagUserOfReplacedMethod("sign()", "signing.sign()")
-        extension.sign(closure)
-    }
-    
-    /**
-     * @deprecated Use {@link SigningExtension#signPom() project.signing.signPom}
-     */
-    @Deprecated
-    Signature signPom(MavenDeployment mavenDeployment, Closure closure = null) {
-        DeprecationLogger.nagUserOfReplacedMethod("signPom()", "signing.signPom()")
-        extension.signPom(mavenDeployment, closure)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatory.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatory.groovy
index 6398bea..d3b1c17 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatory.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatory.groovy
@@ -15,6 +15,10 @@
  */
 package org.gradle.plugins.signing.signatory.pgp
 
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder
+import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder
+import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
 import org.bouncycastle.openpgp.PGPUtil
 import org.bouncycastle.openpgp.PGPSecretKey
 import org.bouncycastle.openpgp.PGPPrivateKey
@@ -42,7 +46,9 @@ class PgpSignatory extends SignatorySupport {
         this.name = name
         this.password = password
         this.secretKey = secretKey
-        this.privateKey = secretKey.extractPrivateKey(password.toCharArray(), BouncyCastleProvider.PROVIDER_NAME)
+
+        PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(password.toCharArray())
+        this.privateKey = secretKey.extractPrivateKey(decryptor)
     }
 
     PgpKeyId getKeyId() {
@@ -50,8 +56,8 @@ class PgpSignatory extends SignatorySupport {
     }
     
     PGPSignatureGenerator createSignatureGenerator() {
-        def generator = new PGPSignatureGenerator(secretKey.publicKey.algorithm, PGPUtil.SHA1, BouncyCastleProvider.PROVIDER_NAME)
-        generator.initSign(PGPSignature.BINARY_DOCUMENT, privateKey)
+        def generator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(secretKey.publicKey.algorithm, PGPUtil.SHA1))
+        generator.init(PGPSignature.BINARY_DOCUMENT, privateKey)
         generator
     }
     
@@ -77,4 +83,4 @@ class PgpSignatory extends SignatorySupport {
         bufferedOutput.flush()
     }
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatoryProvider.groovy b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatoryProvider.groovy
index ad6ff42..01a9c53 100644
--- a/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatoryProvider.groovy
+++ b/subprojects/signing/src/main/groovy/org/gradle/plugins/signing/signatory/pgp/PgpSignatoryProvider.groovy
@@ -23,7 +23,7 @@ import org.gradle.api.Project
 
 import org.gradle.plugins.signing.SigningExtension
 
-class PgpSignatoryProvider implements SignatoryProvider/*<PgpSignatory>*/ { // Groovy can't deal with this - 1.7.10
+class PgpSignatoryProvider implements SignatoryProvider<PgpSignatory> {
     
     private final factory = new PgpSignatoryFactory()
     private final Map<String, PgpSignatory> signatories = [:]
diff --git a/subprojects/signing/src/main/resources/META-INF/gradle-plugins/signing.properties b/subprojects/signing/src/main/resources/META-INF/gradle-plugins/org.gradle.signing.properties
similarity index 100%
rename from subprojects/signing/src/main/resources/META-INF/gradle-plugins/signing.properties
rename to subprojects/signing/src/main/resources/META-INF/gradle-plugins/org.gradle.signing.properties
diff --git a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
index e000eb4..72e2632 100644
--- a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
+++ b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
@@ -47,7 +47,7 @@ class SignOperationSpec extends SigningProjectSpec {
     
     def "sign single file with defaults"() {
         when:
-        def operation = sign(input1)
+        def operation = signing.sign(input1)
         
         then:
         output1.exists()
@@ -56,7 +56,7 @@ class SignOperationSpec extends SigningProjectSpec {
     
     def "sign single artifact with defaults"() {
         when:
-        def operation = sign(input1Artifact)
+        def operation = signing.sign(input1Artifact)
         
         then:
         output1.exists()
@@ -65,7 +65,7 @@ class SignOperationSpec extends SigningProjectSpec {
     
     def "sign multiple files with defaults"() {
         when:
-        def operation = sign(input1, input2)
+        def operation = signing.sign(input1, input2)
         
         then:
         output1.exists() && output2.exists()
@@ -76,7 +76,7 @@ class SignOperationSpec extends SigningProjectSpec {
 
     def "sign multiple artifacts with defaults"() {
         when:
-        def operation = sign(input1Artifact, input2Artifact)
+        def operation = signing.sign(input1Artifact, input2Artifact)
         
         then:
         output1.exists() && output2.exists()
diff --git a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignatoriesConfigurationSpec.groovy b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignatoriesConfigurationSpec.groovy
index 0bde32f..c46b5db 100644
--- a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignatoriesConfigurationSpec.groovy
+++ b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignatoriesConfigurationSpec.groovy
@@ -70,9 +70,9 @@ class SignatoriesConfigurationSpec extends SigningProjectSpec {
  
     def "trying to read non existent file produces reasonable error message"() {
         when:
-        setProperty("signing.keyId", "aaaaaaaa")
-        setProperty("signing.secretKeyRingFile", "i/dont/exist")
-        setProperty("signing.password", "anything")
+        project.ext["signing.keyId"] = "aaaaaaaa"
+        project.ext["signing.secretKeyRingFile"] = "i/dont/exist"
+        project.ext["signing.password"] = "anything"
         
         and:
         signing.signatory
diff --git a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningConfigurationsSpec.groovy b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningConfigurationsSpec.groovy
index e9a7396..03ceb4e 100644
--- a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningConfigurationsSpec.groovy
+++ b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningConfigurationsSpec.groovy
@@ -44,7 +44,7 @@ class SigningConfigurationsSpec extends SigningProjectSpec {
         // 
         // configurations.archives.buildArtifacts in signArchives.dependsOn
         // 
-        //        but we can't because of http://issues.gradle.org/browse/GRADLE-1608
+        //        but we can't because of https://issues.gradle.org/browse/GRADLE-1608
         
         and:
         configurations.signatures.artifacts.size() == 3
diff --git a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningProjectSpec.groovy b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningProjectSpec.groovy
index 1adabe3..7462c68 100644
--- a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningProjectSpec.groovy
+++ b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SigningProjectSpec.groovy
@@ -53,7 +53,7 @@ class SigningProjectSpec extends Specification {
     
     def addProperties(Map props) {
         props.each { k, v ->
-            project.setProperty(k, v)
+            project.ext.set(k, v)
         }
     }
     
diff --git a/subprojects/sonar/sonar.gradle b/subprojects/sonar/sonar.gradle
index 078aebe..4921849 100644
--- a/subprojects/sonar/sonar.gradle
+++ b/subprojects/sonar/sonar.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 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,14 +16,18 @@
 
 apply from: "$rootDir/gradle/providedConfiguration.gradle"
 
+repositories {
+    maven { url "http://repo.gradle.org/gradle/integ-tests" }
+}
+
 dependencies {
+    // TODO - Cleanup and remove old SonarPlugin
+
     compile project(":core")
     compile project(":plugins")
     compile project(":jacoco")
     compile libraries.groovy
 
-    // Sonar Runner plugin
-    compile "org.codehaus.sonar-plugins:sonar-runner:2.0"
     // version number is part of module name, to allow for multiple versions on same class path
     integTestRuntime "org.gradle.sonar:sonar-server-3.2:3.2 at war"
     integTestRuntime "org.gradle.sonar:sonar-test-server-home-dir-3.2:3.2 at zip"
@@ -40,6 +44,8 @@ dependencies {
     provided "org.codehaus.sonar:sonar-batch:2.9 at jar"
     provided "org.codehaus.sonar:sonar-plugin-api:2.9 at jar"
     provided "commons-configuration:commons-configuration:1.6 at jar"
+
+    integTestRuntime "org.sonarqube:sonarqube:4.3.2 at zip"
 }
 
 useTestFixtures()
diff --git a/subprojects/sonar/src/integTest/groovy/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest.groovy b/subprojects/sonar/src/integTest/groovy/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest.groovy
index c0341a5..1e7be56 100644
--- a/subprojects/sonar/src/integTest/groovy/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest.groovy
+++ b/subprojects/sonar/src/integTest/groovy/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest.groovy
@@ -18,15 +18,17 @@ package org.gradle.api.plugins.sonar
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
+import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.test.fixtures.server.http.ServletContainer
 import org.gradle.util.AvailablePortFinder
-import org.gradle.internal.classloader.ClasspathUtil
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
 import org.junit.Rule
-
 import spock.lang.AutoCleanup
 import spock.lang.Shared
 
+ at Requires(TestPrecondition.JDK7_OR_EARLIER)
 class SonarSmokeIntegrationTest extends AbstractIntegrationSpec {
     @Shared
     AvailablePortFinder portFinder = AvailablePortFinder.createPrivate()
@@ -68,7 +70,7 @@ sonar.embeddedDatabase.port=$databasePort
     def "can run Sonar analysis"() {
         executer.requireIsolatedDaemons()
         // Without forking, we run into problems with Sonar's BootStrapClassLoader, at least when running from IDEA.
-        // Problem is that BootStrapClassLoader, although generally isolated from its parent(s), always
+        // Problem is that BootStrapClassLoader, although generally isolated from its root(s), always
         // delegates to the system class loader. That class loader holds the test class path and therefore
         // also the Sonar dependencies with "provided" scope. Hence, the Sonar dependencies get loaded by
         // the wrong class loader.
diff --git a/subprojects/sonar/src/integTest/groovy/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest.groovy b/subprojects/sonar/src/integTest/groovy/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest.groovy
deleted file mode 100644
index be3f96b..0000000
--- a/subprojects/sonar/src/integTest/groovy/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest.groovy
+++ /dev/null
@@ -1,82 +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.sonar.runner
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.test.fixtures.server.http.ServletContainer
-import org.gradle.util.AvailablePortFinder
-import org.gradle.internal.classloader.ClasspathUtil
-import org.junit.Rule
-
-import spock.lang.AutoCleanup
-import spock.lang.Shared
-
-class SonarRunnerSmokeIntegrationTest extends AbstractIntegrationSpec {
-    @Shared
-    AvailablePortFinder portFinder = AvailablePortFinder.createPrivate()
-
-    @AutoCleanup("stop")
-    ServletContainer container
-
-    @Rule
-    TestNameTestDirectoryProvider tempDir = new TestNameTestDirectoryProvider()
-
-    @Rule
-    TestResources testResources = new TestResources(temporaryFolder)
-
-    int databasePort
-
-    def setup() {
-        def classpath = ClasspathUtil.getClasspath(getClass().classLoader).collect() { new File(it.toURI()) }
-        def warFile = classpath.find { it.name == "sonar-test-server-3.4.war" }
-        assert warFile
-        def zipFile = classpath.find { it.name == "sonar-test-server-home-dir-3.4.0.1.zip" }
-        assert zipFile
-
-        def sonarHome = tempDir.createDir("sonar-home")
-        System.setProperty("SONAR_HOME", sonarHome.path)
-        new AntBuilder().unzip(src: zipFile, dest: sonarHome, overwrite: true)
-
-        databasePort = portFinder.nextAvailable
-        sonarHome.file("conf/sonar.properties") << """
-sonar.jdbc.username=sonar
-sonar.jdbc.password=sonar
-sonar.jdbc.url=jdbc:h2:mem:sonartest
-sonar.embeddedDatabase.port=$databasePort
-        """.trim()
-
-        container = new ServletContainer(warFile)
-        container.start()
-    }
-
-    def "execute 'sonarRunner' task"() {
-        when:
-        executer.requireIsolatedDaemons()
-                .requireGradleHome()
-                .withArgument("-i")
-                .withArgument("-PserverUrl=foo") // dummy value for configuring sonarAnalyze task
-                .withArgument("-PdatabaseUrl=bar") // dummy value for configuring sonarAnalyze task
-                .withArgument("-Dsonar.host.url=http://localhost:${container.port}")
-                .withArgument("-Dsonar.jdbc.url=jdbc:h2:tcp://localhost:$databasePort/mem:sonartest")
-                .withTasks("sonarRunner").run()
-
-        then:
-        noExceptionThrown()
-    }
-}
diff --git a/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest.groovy b/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest.groovy
new file mode 100644
index 0000000..6d5f6b0
--- /dev/null
+++ b/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest.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.sonar.runner
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.TargetVersions
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+ at Requires(TestPrecondition.JDK7_OR_EARLIER)
+ at TargetVersions(['default', '2.4'])
+class SonarRunnerSmokeIntegrationTest extends MultiVersionIntegrationSpec {
+
+    @Rule
+    TestNameTestDirectoryProvider tempDir = new TestNameTestDirectoryProvider()
+
+    @Rule
+    TestResources testResources = new TestResources(temporaryFolder)
+
+    @Rule
+    SonarTestServer sonarServer = new SonarTestServer(tempDir, executer)
+
+    List<String> getWarningLogMessages() {
+        def warningLogMessages = output.readLines().findAll { it.contains("WARN") }
+        warningLogMessages.removeAll { it.contains("'sonar.dynamicAnalysis' is deprecated") }
+        warningLogMessages.removeAll { it.contains("H2 database should be used for evaluation purpose only") }
+        warningLogMessages
+    }
+
+    def "execute 'sonarRunner' task"() {
+        given:
+        executer.withDeprecationChecksDisabled() // sonar.dynamicAnalysis is deprecated since SonarQube 4.3
+
+        when:
+        buildFile << """
+           sonarRunner {
+              sonarProperties {
+                // Use a very long property to test limits
+                // https://issues.gradle.org/browse/GRADLE-3168
+                property "sonar.foo", ("a" * 200000)
+              }
+            }
+        """
+
+        if (getVersion() != "default") {
+            buildFile << """
+                sonarRunner {
+                  toolVersion = "${getVersion()}"
+                }
+            """
+        }
+
+        run "sonarRunner"
+
+        then:
+        sonarServer.assertProjectPresent('org.gradle.test.sonar:SonarTestBuild')
+
+        and: "no unexpected warnings are emitted"
+        !warningLogMessages
+
+        and: "no reports directory is created for projects with no production and no test sources"
+        !temporaryFolder.file("emptyJavaProject", "build", "test-results").exists()
+    }
+
+}
diff --git a/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarTestServer.groovy b/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarTestServer.groovy
new file mode 100644
index 0000000..8055ea0
--- /dev/null
+++ b/subprojects/sonar/src/integTest/groovy/org/gradle/sonar/runner/SonarTestServer.groovy
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner
+
+import groovy.json.JsonSlurper
+import org.apache.http.HttpResponse
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.DefaultHttpClient
+import org.gradle.integtests.fixtures.UrlValidator
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.internal.classloader.ClasspathUtil
+import org.gradle.internal.jvm.Jvm
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.AvailablePortFinder
+import org.junit.rules.ExternalResource
+
+class SonarTestServer extends ExternalResource {
+
+    private TestNameTestDirectoryProvider provider
+    private AvailablePortFinder portFinder
+
+    private int databasePort
+    private int httpPort
+
+    private Process sonarProcess
+
+    SonarTestServer(TestNameTestDirectoryProvider provider, GradleExecuter gradleExecuter) {
+        this.provider = provider
+        this.portFinder = AvailablePortFinder.createPrivate()
+
+        gradleExecuter.beforeExecute {
+            withArgument("-Dsonar.host.url=http://localhost:${httpPort}")
+            withArgument("-Dsonar.jdbc.url=jdbc:h2:tcp://localhost:${databasePort}/sonar")
+            withArgument("-Dsonar.jdbc.username=sonar")
+            withArgument("-Dsonar.jdbc.password=sonar")
+        }
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        startServer()
+        assertDbIsEmpty()
+    }
+
+    @Override
+    protected void after() {
+        stopServer()
+    }
+
+    void startServer() {
+        TestFile sonarHome = prepareSonarHomeFolder()
+        ProcessBuilder processBuilder = new ProcessBuilder()
+                .directory(sonarHome)
+                .redirectErrorStream(true)
+                .command(
+                Jvm.current().getJavaExecutable().absolutePath,
+                '-XX:MaxPermSize=160m', '-Xmx512m', '-Djava.awt.headless=true',
+                '-Dfile.encoding=UTF-8', '-Djruby.management.enabled=false',
+                '-cp', "lib/*${File.pathSeparator}conf", 'org.sonar.application.StartServer'
+        )
+
+        sonarProcess = processBuilder.start()
+
+        sonarProcess.consumeProcessOutput((Appendable) System.out, (Appendable) System.err)
+        UrlValidator.available(serverUrl, "sonar")
+        assert apiRequest('webservices/list').statusLine.statusCode < 400
+    }
+
+    private TestFile prepareSonarHomeFolder() {
+        databasePort = portFinder.nextAvailable
+        httpPort = portFinder.nextAvailable
+        def classpath = ClasspathUtil.getClasspath(getClass().classLoader).collect() {
+            new File(it.toURI())
+        }
+        def zipFile = classpath.find {
+            it.name ==~ "sonarqube.*\\.zip"
+        }
+        assert zipFile
+
+        new TestFile(zipFile).unzipTo(provider.testDirectory)
+        TestFile sonarHome = provider.testDirectory.file(zipFile.name - '.zip')
+
+        sonarHome.file("conf/sonar.properties") << """
+            sonar.web.port=$httpPort
+            sonar.jdbc.username=sonar
+            sonar.jdbc.password=sonar
+            sonar.jdbc.url=jdbc:h2:tcp://localhost:$databasePort/sonar
+            sonar.embeddedDatabase.port=$databasePort
+        """.stripIndent()
+
+        sonarHome
+    }
+
+    void stopServer() {
+        sonarProcess?.waitForOrKill(100)
+    }
+
+    HttpResponse apiRequest(String path) {
+        def httpClient = new DefaultHttpClient()
+        def request = new HttpGet(apiPath(path))
+        httpClient.execute(request)
+    }
+
+    private String apiPath(String path) {
+        "$serverUrl/api/$path"
+    }
+
+    void assertDbIsEmpty() {
+        assert getResources().empty
+    }
+
+    String getServerUrl() {
+        "http://localhost:$httpPort"
+    }
+
+    List<?> getResources() {
+        new JsonSlurper().parse(apiRequest('resources?format=json').entity.content)
+    }
+
+    void assertProjectPresent(String name) {
+        assert getResources()*.key.contains(name)
+    }
+}
\ No newline at end of file
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest/shared/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest/shared/build.gradle
index 8030a19..40a27af 100644
--- a/subprojects/sonar/src/integTest/resources/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest/shared/build.gradle
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/api/plugins/sonar/SonarSmokeIntegrationTest/shared/build.gradle
@@ -8,7 +8,7 @@ subprojects {
     }
 
     dependencies {
-        testCompile "junit:junit:4.11"
+        testCompile "junit:junit:4.12"
     }
 }
 
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle
deleted file mode 100644
index cdea0fc..0000000
--- a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,20 +0,0 @@
-apply plugin: "sonar-runner"
-
-description = "Sonar Test Build"
-
-allprojects {
-    version = "1.42"
-    group = "org.gradle.test.sonar"
-}
-
-subprojects {
-    apply plugin: "java"
-
-    repositories {
-        mavenCentral()
-    }
-
-    dependencies {
-        testCompile "junit:junit:4.11"
-    }
-}
\ No newline at end of file
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle
deleted file mode 100644
index 2f1848b..0000000
--- a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-apply plugin: 'groovy'
-
-description = "Sonar Test Groovy Project"
-
-dependencies {
-    compile 'org.codehaus.groovy:groovy-all:2.0.5'
-}
-
-sonarRunner {
-    sonarProperties {
-        // Sonar Groovy plugin can't currently be used because it fails as soon as sonar.project.key contains a colon
-        // property "sonar.language", "grvy"
-    }
-}
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle
deleted file mode 100644
index cb7798c..0000000
--- a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-include "javaProjectWithJacoco", "groovyProject", "skippedProject", "customizedProject", "nested:nested2:nestedProject"
-
-rootProject.name = "SonarTestBuild"
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..1ac0a8a
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/build.gradle
@@ -0,0 +1,20 @@
+apply plugin: "sonar-runner"
+
+description = "Sonar Test Build"
+
+allprojects {
+    version = "1.42"
+    group = "org.gradle.test.sonar"
+
+    repositories {
+        jcenter()
+    }
+}
+
+subprojects {
+    apply plugin: "java"
+
+    dependencies {
+        testCompile "junit:junit:4.12"
+    }
+}
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/build.gradle
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/build.gradle
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/build.gradle
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/src/org/gradle/test/customizedProject/Production1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/src/org/gradle/test/customizedProject/Production1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/src/org/gradle/test/customizedProject/Production1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/src/org/gradle/test/customizedProject/Production1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/test/org/gradle/test/customizedProject/Test1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/test/org/gradle/test/customizedProject/Test1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/test/org/gradle/test/customizedProject/Test1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/customizedProject/test/org/gradle/test/customizedProject/Test1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/emptyJavaProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/emptyJavaProject/build.gradle
new file mode 100644
index 0000000..c03bd1e
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/emptyJavaProject/build.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+description = "Sonar Test Java Project with no source and test source directories"
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/gradle.properties b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/gradle.properties
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/gradle.properties
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/gradle.properties
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle
new file mode 100644
index 0000000..2b982d9
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'groovy'
+
+description = "Sonar Test Groovy Project"
+
+dependencies {
+    compile 'org.codehaus.groovy:groovy:2.3.10'
+}
+
+sonarRunner {
+    sonarProperties {
+        // Sonar Groovy plugin can't currently be used because it fails as soon as sonar.project.key contains a colon
+        // property "sonar.language", "grvy"
+    }
+}
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy1.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy1.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy1.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy1.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy10.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy10.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy10.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy10.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy2.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy2.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy2.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy2.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy3.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy3.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy3.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy3.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy4.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy4.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy4.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy4.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy5.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy5.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy5.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy5.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy6.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy6.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy6.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy6.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy7.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy7.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy7.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy7.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy8.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy8.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy8.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy8.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy9.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy9.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy9.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/main/groovy/org/gradle/test/groovyProject/ProductionGroovy9.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy1.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy1.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy1.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy1.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy10.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy10.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy10.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy10.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy2.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy2.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy2.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy2.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy3.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy3.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy3.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy3.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy4.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy4.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy4.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy4.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy5.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy5.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy5.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy5.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy6.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy6.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy6.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy6.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy7.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy7.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy7.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy7.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy8.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy8.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy8.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy8.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy9.groovy b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy9.groovy
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy9.groovy
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/groovyProject/src/test/groovy/org/gradle/test/groovyProject/TestGroovy9.groovy
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/build.gradle
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/build.gradle
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/build.gradle
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production10.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production10.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production10.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production10.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production2.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production2.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production2.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production2.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production3.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production3.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production3.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production3.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production4.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production4.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production4.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production4.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production5.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production5.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production5.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production5.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production6.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production6.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production6.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production6.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production7.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production7.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production7.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production7.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production8.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production8.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production8.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production8.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production9.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production9.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production9.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/java/org/gradle/test/javaProject/Production9.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/resources/org/gradle/test/javaProject/productionResource.xml b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/resources/org/gradle/test/javaProject/productionResource.xml
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/resources/org/gradle/test/javaProject/productionResource.xml
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/main/resources/org/gradle/test/javaProject/productionResource.xml
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test10.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test10.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test10.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test10.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test2.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test2.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test2.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test2.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test3.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test3.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test3.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test3.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test4.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test4.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test4.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test4.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test5.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test5.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test5.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test5.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test6.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test6.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test6.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test6.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test7.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test7.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test7.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test7.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test8.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test8.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test8.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test8.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test9.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test9.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test9.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/java/org/gradle/test/javaProject/Test9.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/resources/org/gradle/test/javaProject/testResource.xml b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/resources/org/gradle/test/javaProject/testResource.xml
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/resources/org/gradle/test/javaProject/testResource.xml
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithJacoco/src/test/resources/org/gradle/test/javaProject/testResource.xml
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/build.gradle
new file mode 100644
index 0000000..40f957c
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/build.gradle
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+description = "Sonar Test Java Project with skipped test task"
+
+test {
+    onlyIf { false }
+}
\ No newline at end of file
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/src/test/java/Test1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/src/test/java/Test1.java
new file mode 100644
index 0000000..2039e87
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithSkippedTestTask/src/test/java/Test1.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static org.junit.Assert.*;
+
+public class Test1 {
+
+    @org.junit.Test
+    public void test() {
+        assertEquals("value", "value");
+    }
+}
\ No newline at end of file
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/build.gradle
new file mode 100644
index 0000000..c5af456
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/build.gradle
@@ -0,0 +1 @@
+description = "Sonar Test Java Project with no test classes"
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/src/main/java/Production1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/src/main/java/Production1.java
new file mode 100644
index 0000000..ba7e87e
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/javaProjectWithoutTestClasses/src/main/java/Production1.java
@@ -0,0 +1,12 @@
+public class Production1 {
+    private final String property;
+
+    public Production1(String param) {
+        this.property = param;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+}
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/build.gradle
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/build.gradle
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/build.gradle
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/main/java/org/gradle/test/nestedProject/Production1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/main/java/org/gradle/test/nestedProject/Production1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/main/java/org/gradle/test/nestedProject/Production1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/main/java/org/gradle/test/nestedProject/Production1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/test/java/org/gradle/test/nestedProject/Test1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/test/java/org/gradle/test/nestedProject/Test1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/test/java/org/gradle/test/nestedProject/Test1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/nested/nested2/nestedProject/src/test/java/org/gradle/test/nestedProject/Test1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle
new file mode 100644
index 0000000..00ed9fe
--- /dev/null
+++ b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/settings.gradle
@@ -0,0 +1,3 @@
+include "javaProjectWithJacoco", "emptyJavaProject", "groovyProject", "javaProjectWithoutTestClasses", "javaProjectWithSkippedTestTask", "skippedProject", "customizedProject", "nested:nested2:nestedProject"
+
+rootProject.name = "SonarTestBuild"
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/build.gradle b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/build.gradle
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/build.gradle
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/build.gradle
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/main/java/org/gradle/test/skippedProject/Production1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/main/java/org/gradle/test/skippedProject/Production1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/main/java/org/gradle/test/skippedProject/Production1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/main/java/org/gradle/test/skippedProject/Production1.java
diff --git a/subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/test/java/org/gradle/test/skippedProject/Test1.java b/subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/test/java/org/gradle/test/skippedProject/Test1.java
similarity index 100%
rename from subprojects/sonar/src/integTest/resources/org/gradle/api/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/test/java/org/gradle/test/skippedProject/Test1.java
rename to subprojects/sonar/src/integTest/resources/org/gradle/sonar/runner/SonarRunnerSmokeIntegrationTest/shared/skippedProject/src/test/java/org/gradle/test/skippedProject/Test1.java
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarAnalyze.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarAnalyze.groovy
index d5e8ce4..7e49540 100644
--- a/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarAnalyze.groovy
+++ b/subprojects/sonar/src/main/groovy/org/gradle/api/plugins/sonar/SonarAnalyze.groovy
@@ -15,13 +15,16 @@
  */
 package org.gradle.api.plugins.sonar
 
-import org.sonar.batch.bootstrapper.Bootstrapper
 import org.gradle.api.internal.ConventionTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.internal.classloader.ClasspathUtil
-
+import org.gradle.api.internal.classpath.ModuleRegistry
 import org.gradle.api.plugins.sonar.model.SonarRootModel
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.classloader.ClassLoaderFactory
+import org.gradle.internal.classloader.MutableURLClassLoader
 import org.gradle.util.GFileUtils
+import org.sonar.batch.bootstrapper.Bootstrapper
+
+import javax.inject.Inject
 
 /**
  * Analyzes a project hierarchy and writes the results to the
@@ -33,15 +36,32 @@ class SonarAnalyze extends ConventionTask {
      */
     SonarRootModel rootModel
 
+    @Inject
+    protected ModuleRegistry getModuleRegistry() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
+    @Inject
+    protected ClassLoaderFactory getClassLoaderFactory() {
+        // Decoration takes care of the implementation
+        throw new UnsupportedOperationException();
+    }
+
     @TaskAction
     void analyze() {
         GFileUtils.mkdirs(rootModel.bootstrapDir)
         def bootstrapper = new Bootstrapper("Gradle", rootModel.server.url, rootModel.bootstrapDir)
 
-        def classLoader = bootstrapper.createClassLoader(
-                [findGradleSonarJar()] as URL[], SonarAnalyze.classLoader,
-                        "groovy", "org.codehaus.groovy", "org.slf4j", "org.apache.log4j", "org.apache.commons.logging",
-                                "org.gradle.api.plugins.sonar.model", "ch.qos.logback")
+        def pluginClassLoaderAllowedPackages = ["groovy", "org.codehaus.groovy", "org.apache.log4j", "org.apache.commons.logging", "org.gradle.api.plugins.sonar.model"]
+
+        def filteringPluginClassLoader = classLoaderFactory.createFilteringClassLoader(SonarAnalyze.classLoader)
+        pluginClassLoaderAllowedPackages.each { filteringPluginClassLoader.allowPackage(it) }
+        filteringPluginClassLoader.allowResource("logback.xml")
+        def pluginAndLoggingClassLoader = new MutableURLClassLoader(filteringPluginClassLoader, getLogbackAndSlf4jUrls())
+
+        def bootstrapperParentClassLoaderAllowedPackages = pluginClassLoaderAllowedPackages + ["org.slf4j", "ch.qos.logback"]
+        def classLoader = bootstrapper.createClassLoader(getGradleSonarUrls() as URL[], pluginAndLoggingClassLoader, bootstrapperParentClassLoaderAllowedPackages as String[])
 
         def analyzerClass = classLoader.loadClass("org.gradle.api.plugins.sonar.internal.SonarCodeAnalyzer")
         def analyzer = analyzerClass.newInstance()
@@ -49,9 +69,12 @@ class SonarAnalyze extends ConventionTask {
         analyzer.execute()
     }
 
-    protected URL findGradleSonarJar() {
-        def url = ClasspathUtil.getClasspath(SonarAnalyze.classLoader).find { it.path.contains("gradle-sonar") }
-        assert url != null, "failed to detect file system location of gradle-sonar Jar"
-        url
+    protected List<URL> getGradleSonarUrls() {
+        moduleRegistry.getModule("gradle-sonar").implementationClasspath.asURLs
+    }
+
+    protected List<URL> getLogbackAndSlf4jUrls() {
+        def moduleNames = ["logback-classic", "logback-core", "slf4j-api"]
+        moduleNames.collectMany { moduleRegistry.getExternalModule(it).classpath.asURLs }
     }
 }
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarProperties.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarProperties.groovy
deleted file mode 100644
index 4d79c65..0000000
--- a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarProperties.groovy
+++ /dev/null
@@ -1,56 +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.sonar.runner
-
-import org.gradle.api.Incubating
-
-/**
- * The Sonar properties for the current Gradle project that are to be passed to the Sonar Runner.
- * The {@code properties} map is already populated with the defaults provided by Gradle, and can be
- * further manipulated as necessary. Before passing them on to the Sonar Runner, property values
- * are converted to Strings as follows:
- *
- * <ul>
- *     <li>{@Iterable}s are recursively converted and joined into a comma-separated String.</li>
- *     <li>All other values are converted to Strings by calling their {@code toString} method.</li>
- * </ul>
- */
- at Incubating
-class SonarProperties {
-    /**
-     * The Sonar properties for the current Gradle project that are to be passed to the Sonar runner.
-     */
-    Map<String, Object> properties = [:]
-
-    /**
-     * Convenience method for setting a single property.
-     *
-     * @param key the key of the property to be added
-     * @param value the value of the property to be added
-     */
-    void property(String key, Object value) {
-        properties[key] = value
-    }
-
-    /**
-     * Convenience method for setting multiple properties.
-     *
-     * @param properties the properties to be added
-     */
-    void properties(Map<String, ?> properties) {
-        this.properties.putAll(properties)
-    }
-}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunner.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunner.groovy
deleted file mode 100644
index bfaedb0..0000000
--- a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunner.groovy
+++ /dev/null
@@ -1,54 +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.sonar.runner
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.Incubating
-import org.gradle.api.logging.Logger
-import org.gradle.api.logging.Logging
-import org.gradle.api.tasks.TaskAction
-import org.sonar.runner.Runner
-
-/**
- * Analyses one or more projects with the <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">
- * Sonar Runner</a>. Can be used with or without the {@code sonar-runner} plugin. If used together with the plugin,
- * {@code sonarProperties} will be populated with defaults based on Gradle's object model and user-defined
- * values configured via {@link SonarRunnerExtension}. If used without the plugin, all properties have to be configured
- * manually.
- *
- * <p>For more information on how to configure the Sonar Runner, and on which properties are available, see the
- * <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">Sonar Runner documentation</a>.
- */
- at Incubating
-class SonarRunner extends DefaultTask {
-    private static final Logger LOGGER = Logging.getLogger(SonarRunner)
-
-    /**
-     * The String key/value pairs to be passed to the Sonar Runner. {@code null} values are not permitted.
-     */
-    Properties sonarProperties
-
-    @TaskAction
-    void run() {
-        def properties = getSonarProperties()
-        if (LOGGER.infoEnabled) {
-            LOGGER.info("Executing Sonar Runner with properties:\n{}",
-                    properties.sort().collect { key, value -> "$key: $value" }.join("\n"))
-        }
-        def runner = Runner.create(properties)
-        runner.execute()
-    }
-}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerExtension.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerExtension.groovy
deleted file mode 100644
index 875841f..0000000
--- a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerExtension.groovy
+++ /dev/null
@@ -1,78 +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.sonar.runner
-
-import groovy.transform.PackageScope
-
-import org.gradle.api.Action
-import org.gradle.api.Incubating
-import org.gradle.listener.ActionBroadcast
-
-/**
- * An extension for configuring the <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">
- * Sonar Runner</a>. The extension is added to all projects that have the {@code sonar-runner}
- * plugin applied, and all of their subprojects.
- *
- * <p>Example usage:
- *
- * <pre autoTested=''>
- * sonarRunner {
- *     skipProject = false // this is the default
- *
- *     sonarProperties {
- *         property "sonar.host.url", "http://my.sonar.server" // adding a single property
- *         properties mapOfProperties // adding multiple properties at once
- *         properties["sonar.sources"] += sourceSets.other.java.srcDirs // manipulating an existing property
- *     }
- * }
- * </pre>
- */
- at Incubating
-class SonarRunnerExtension {
-    /**
-     * Tells if the project will be excluded from analysis. Defaults to {@code false}.
-     */
-    boolean skipProject
-
-    /**
-     * Adds an action that configures Sonar properties for the associated Gradle project.
-     * <em>Global</em> Sonar properties (e.g. database connection settings) have to be set on the
-     * "root" project of the Sonar run. This is the project that has the {@code sonar-runner} plugin applied.
-     *
-     * <p>The action is passed an instance of {@code SonarProperties}.
-     * Evaluation of the action is deferred until {@code sonarRunner.sonarProperties} is requested.
-     * Hence it is safe to reference other Gradle model properties from inside the action.
-     *
-     * <p>Sonar properties can also be set via system properties (and therefore from the command line).
-     * This is mainly useful for global Sonar properties like database credentials.
-     * Every system property starting with {@code "sonar."} is automatically set on the "root" project of the
-     * Sonar run (i.e. the project that has the {@code sonar-runner} plugin applied). System properties take
-     * precedence over properties declared in build scripts.
-     *
-     * @param action an action that configures Sonar properties for the associated Gradle project
-     */
-    void sonarProperties(Action<? super SonarProperties> action) {
-        propertiesActions.add(action)
-    }
-
-    private final ActionBroadcast<SonarProperties> propertiesActions = new ActionBroadcast<SonarProperties>()
-
-    @PackageScope
-    void evaluateSonarPropertiesBlocks(Map<String, Object> properties) {
-        def sonarProperties = new SonarProperties(properties: properties)
-        propertiesActions.execute(sonarProperties)
-    }
-}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerPlugin.groovy b/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerPlugin.groovy
deleted file mode 100644
index 9817644..0000000
--- a/subprojects/sonar/src/main/groovy/org/gradle/api/sonar/runner/SonarRunnerPlugin.groovy
+++ /dev/null
@@ -1,243 +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.sonar.runner
-
-import org.gradle.api.Incubating
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.tasks.SourceSet
-import org.gradle.internal.jvm.Jvm
-import org.gradle.testing.jacoco.plugins.JacocoPlugin
-
-/**
- * A plugin for analyzing projects with the
- * <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">Sonar Runner</a>.
- * When applied to a project, both the project itself and its subprojects
- * will be analyzed (in a single run). Therefore, it's common to apply the
- * plugin only to the root project. To exclude selected subprojects from
- * being analyzed, set {@code sonarRunner.skipProject = true}.
- *
- * <p>The plugin is configured via {@link SonarRunnerExtension}. Here is a
- * small example:
- *
- * <pre autoTested=''>
- * sonarRunner {
- *     skipProject = false // this is the default
- *
- *     sonarProperties {
- *         property "sonar.host.url", "http://my.sonar.server" // adding a single property
- *         properties mapOfProperties // adding multiple properties at once
- *         properties["sonar.sources"] += sourceSets.other.java.srcDirs // manipulating an existing property
- *     }
- * }
- * </pre>
- *
- * The Sonar Runner already comes with defaults for some of the most important
- * Sonar properties (server URL, database settings, etc.). For details see
- * <a href="http://docs.codehaus.org/display/SONAR/Analysis+Parameters">Analysis Parameters</a>
- * in the Sonar documentation. The {@code sonar-runner} plugin provides the following additional
- * defaults:
- *
- * <dl>
- *     <dt>sonar.projectKey
- *     <dd>"$project.group:$project.name"
- *     <dt>sonar.projectName
- *     <dd>project.name
- *     <dt>sonar.projectDescription
- *     <dd>project.description
- *     <dt>sonar.projectVersion
- *     <dd>sonar.version
- *     <dt>sonar.projectBaseDir
- *     <dd>project.projectDir
- *     <dt>sonar.working.directory
- *     <dd>"$project.buildDir/sonar"
- *     <dt>sonar.dynamicAnalysis
- *     <dd>"reuseReports"
- * </dl>
- *
- * For project that have the {@code java-base} plugin applied, additionally the following defaults are provided:
- *
- * <dl>
- *     <dt>sonar.java.source
- *     <dd>project.sourceCompatibility
- *     <dt>sonar.java.target
- *     <dd>project.targetCompatibility
- * </dl>
- *
- * For project that have the {@code java} plugin applied, additionally the following defaults are provided:
- *
- * <dl>
- *     <dt>sonar.sources
- *     <dd>sourceSets.main.allSource.srcDirs (filtered to only include existing directories)
- *     <dt>sonar.tests
- *     <dd>sourceSets.test.allSource.srcDirs (filtered to only include existing directories)
- *     <dt>sonar.binaries
- *     <dd>sourceSets.main.runtimeClasspath (filtered to only include directories)
- *     <dt>sonar.libraries
- *     <dd>sourceSets.main.runtimeClasspath (filtering to only include files; {@code rt.jar} added if necessary)
- *     <dt>sonar.surefire.reportsPath
- *     <dd>test.testResultsDir (if the directory exists)
- *     <dt>sonar.junit.reportsPath
- *     <dd>test.testResultsDir (if the directory exists)
- * </dl>
- */
- at Incubating
-class SonarRunnerPlugin implements Plugin<Project> {
-    // the project to which the plugin was applied
-    Project targetProject
-
-    void apply(Project project) {
-        targetProject = project
-        def sonarRunnerTask = project.tasks.create("sonarRunner", SonarRunner)
-        sonarRunnerTask.with {
-            description = "Analyzes $project and its subprojects with Sonar Runner."
-        }
-        sonarRunnerTask.conventionMapping.with {
-            sonarProperties = {
-                def properties = new Properties()
-                computeSonarProperties(project, properties)
-                properties
-            }
-        }
-        project.allprojects {
-            extensions.create("sonarRunner", SonarRunnerExtension)
-        }
-        sonarRunnerTask.dependsOn {
-            project.allprojects.findAll { prj ->
-                prj.plugins.hasPlugin(JavaPlugin) && !prj.sonarRunner.skipProject
-            }.collect { it.tasks.test }
-        }
-    }
-
-    void computeSonarProperties(Project project, Properties properties) {
-        def extension = project.extensions.getByType(SonarRunnerExtension)
-        if (extension.skipProject) {
-            return
-        }
-
-        Map<String, Object> rawProperties = [:]
-        addGradleDefaults(project, rawProperties)
-        extension.evaluateSonarPropertiesBlocks(rawProperties)
-        if (project == targetProject) {
-            addSystemProperties(rawProperties)
-        }
-
-        def projectPrefix = project.path.substring(targetProject.path.size()).replace(":", ".")
-        if (projectPrefix.startsWith(".")) {
-            projectPrefix = projectPrefix.substring(1)
-        }
-        convertProperties(rawProperties, projectPrefix, properties)
-
-        def enabledChildProjects = project.childProjects.values().findAll { !it.sonarRunner.skipProject }.sort()
-        if (enabledChildProjects.empty) {
-            return
-        }
-
-        properties[convertKey("sonar.modules", projectPrefix)] = convertValue(enabledChildProjects.name)
-        for (childProject in enabledChildProjects) {
-            computeSonarProperties(childProject, properties)
-        }
-    }
-
-    private void addGradleDefaults(Project project, Map<String, Object> properties) {
-        properties["sonar.projectName"] = project.name
-        properties["sonar.projectDescription"] = project.description
-        properties["sonar.projectVersion"] = project.version
-        properties["sonar.projectBaseDir"] = project.projectDir
-        properties["sonar.dynamicAnalysis"] = "reuseReports"
-
-        if (project == targetProject) {
-            // We only set project key for root project because Sonar Runner 2.0 will automatically
-            // prefix subproject keys with parent key, even if subproject keys are set explicitly.
-            // Therefore it's better to rely on Sonar's defaults.
-            properties["sonar.projectKey"] = getProjectKey(project)
-            properties["sonar.environment.information.key"] = "Gradle"
-            properties["sonar.environment.information.version"] = project.gradle.gradleVersion
-            properties["sonar.working.directory"] = new File(project.buildDir, "sonar")
-        }
-
-        project.plugins.withType(JavaBasePlugin) {
-            properties["sonar.java.source"] = project.sourceCompatibility
-            properties["sonar.java.target"] = project.targetCompatibility
-        }
-
-        project.plugins.withType(JavaPlugin) {
-            SourceSet main = project.sourceSets.main
-            SourceSet test = project.sourceSets.test
-
-            properties["sonar.sources"] = main.allSource.srcDirs.findAll { it.exists() } ?: null
-            properties["sonar.tests"] = test.allSource.srcDirs.findAll { it.exists() } ?: null
-            properties["sonar.binaries"] = main.runtimeClasspath.findAll { it.directory } ?: null
-            properties["sonar.libraries"] = getLibraries(main)
-            File testResultsDir = project.test.reports.junitXml.destination
-            File testResultsValue = testResultsDir.exists() ? testResultsDir : null
-            properties["sonar.surefire.reportsPath"] = testResultsValue
-            // added due to http://issues.gradle.org/browse/GRADLE-3005
-            properties["sonar.junit.reportsPath"] = testResultsValue
-
-            project.plugins.withType(JacocoPlugin) {
-                properties["sonar.jacoco.reportPath"] = project.test.jacoco.destinationFile.exists() ? project.test.jacoco.destinationFile : null
-            }
-        }
-
-        if (properties["sonar.sources"] == null) {
-            // Should be able to remove this after upgrading to Sonar Runner 2.1 (issue is already marked as fixed),
-            // if we can live with the fact that leaf projects w/o source dirs will still cause a failure.
-            properties["sonar.sources"] = ""
-        }
-    }
-
-    private String getProjectKey(Project project) {
-        // Sonar uses project keys in URL parameters without internally URL-encoding them.
-        // According to my manual tests with sonar-runner plugin based on Sonar Runner 2.0 and Sonar 3.4.1,
-        // the current defaults will only cause a problem if project.group or project.name of
-        // the Gradle project to which the plugin is applied contains special characters.
-        // (':' works, ' ' doesn't.) In such a case, sonar.projectKey can be overridden manually.
-        project.group ? "$project.group:$project.name" : project.name
-    }
-
-    private void addSystemProperties(Map<String, Object> properties) {
-        properties.putAll(System.properties.findAll { key, value -> key.startsWith("sonar.") })
-    }
-
-    private Collection<File> getLibraries(SourceSet main) {
-        def libraries = main.runtimeClasspath.findAll { it.file }
-        def runtimeJar = Jvm.current().runtimeJar
-        if (runtimeJar != null) {
-            libraries << runtimeJar
-        }
-        libraries ?: null
-    }
-
-    private void convertProperties(Map<String, Object> rawProperties, String projectPrefix, Properties properties) {
-        rawProperties.each { key, value ->
-            if (value != null) {
-                properties[convertKey(key, projectPrefix)] = convertValue(value)
-            }
-        }
-    }
-
-    private String convertKey(String key, String projectPrefix) {
-        projectPrefix ? "${projectPrefix}.$key" : key
-    }
-
-    private String convertValue(Object value) {
-        value instanceof Iterable ? value.collect { convertValue(it) }.join(",") : value.toString()
-    }
-
-}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarProperties.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarProperties.java
new file mode 100644
index 0000000..003372a
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarProperties.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner;
+
+import org.gradle.api.Incubating;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * The Sonar properties for the current Gradle project that are to be passed to the Sonar Runner.
+ * <p>
+ * The {@code properties} map is already populated with the defaults provided by Gradle, and can be further manipulated as necessary.
+ * Before passing them on to the Sonar Runner, property values are converted to Strings as follows:
+ * <ul>
+ * <li>{@code Iterable}s are recursively converted and joined into a comma-separated String.</li>
+ * <li>All other values are converted to Strings by calling their {@code toString()} method.</li>
+ * </ul>
+ */
+ at Incubating
+public class SonarProperties {
+
+    public SonarProperties(Map<String, Object> properties) {
+        this.properties = properties;
+    }
+
+    private Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    /**
+     * Convenience method for setting a single property.
+     *
+     * @param key the key of the property to be added
+     * @param value the value of the property to be added
+     */
+    public void property(String key, Object value) {
+        properties.put(key, value);
+    }
+
+    /**
+     * Convenience method for setting multiple properties.
+     *
+     * @param properties the properties to be added
+     */
+    public void properties(Map<String, ?> properties) {
+        this.properties.putAll(properties);
+    }
+
+    /**
+     * The Sonar properties for the current Gradle project that are to be passed to the Sonar runner.
+     */
+    public Map<String, Object> getProperties() {
+        return properties;
+    }
+
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerExtension.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerExtension.java
new file mode 100644
index 0000000..32c2e70
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerExtension.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner;
+
+import org.gradle.api.Action;
+import org.gradle.api.Incubating;
+import org.gradle.listener.ActionBroadcast;
+
+/**
+ * An extension for configuring the <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+SonarQube+Runner">Sonar Runner</a> integration.
+ * <p>
+ * The extension is added to all projects that have the {@code "sonar-runner"} plugin applied, and all of their subprojects.
+ * The extension of the project that actually applies the {@code "sonar-runner"} plugin is a subclass of this type, {@link SonarRunnerRootExtension},
+ * which also allows configuration of the Sonar Runner process.
+ * <p>
+ * Example usage:
+ * <pre autoTested=''>
+ * sonarRunner {
+ *   skipProject = false // this is the default
+ *   sonarProperties {
+ *     property "sonar.host.url", "http://my.sonar.server" // adding a single property
+ *     properties mapOfProperties // adding multiple properties at once
+ *     properties["sonar.sources"] += sourceSets.other.java.srcDirs // manipulating an existing property }
+ *   }
+ * }
+ * </pre>
+ * <h3>Sonar Properties</h3>
+ * <p>
+ * The Sonar configuration is provided by using the {@link #sonarProperties(org.gradle.api.Action)} method and specifying properties.
+ * Certain properties are required, such as {@code "sonar.host.url"} which provides the address of the Sonar server.
+ * For details on what properties are available, see <a href="http://docs.codehaus.org/display/SONAR/Analysis+Parameters">Analysis Parameters</a> in the Sonar documentation.
+ * <p>
+ * The {@code "sonar-runner"} plugin adds default values for several plugins dependening on the nature of the project.
+ * Please see the “Sonar Runner Plugin” chapter of the Gradle User Guide for details on which properties are set and their values.
+ * <p>
+ * Please see the {@link SonarProperties} class for more information on the mechanics of setting Sonar properties, including laziness and property types.
+ */
+ at Incubating
+public class SonarRunnerExtension {
+
+    public static final String SONAR_RUNNER_CONFIGURATION_NAME = "sonarRunner";
+    public static final String SONAR_RUNNER_EXTENSION_NAME = "sonarRunner";
+    public static final String SONAR_RUNNER_TASK_NAME = "sonarRunner";
+
+    private boolean skipProject;
+    private final ActionBroadcast<SonarProperties> propertiesActions;
+
+    public SonarRunnerExtension(ActionBroadcast<SonarProperties> propertiesActions) {
+        this.propertiesActions = propertiesActions;
+    }
+
+    /**
+     * Adds an action that configures Sonar properties for the associated Gradle project.
+     * <p>
+     * <em>Global</em> Sonar properties (e.g. database connection settings) have to be set on the "root" project of the Sonar run.
+     * This is the project that has the {@code sonar-runner} plugin applied.
+     * <p>
+     * The action is passed an instance of {@code SonarProperties}.
+     * Evaluation of the action is deferred until {@code sonarRunner.sonarProperties} is requested.
+     * Hence it is safe to reference other Gradle model properties from inside the action.
+     * <p>
+     * Sonar properties can also be set via system properties (and therefore from the command line).
+     * This is mainly useful for global Sonar properties like database credentials.
+     * Every system property starting with {@code "sonar."} is automatically set on the "root" project of the Sonar run (i.e. the project that has the {@code sonar-runner} plugin applied).
+     * System properties take precedence over properties declared in build scripts.
+     *
+     * @param action an action that configures Sonar properties for the associated Gradle project
+     */
+    public void sonarProperties(Action<? super SonarProperties> action) {
+        propertiesActions.add(action);
+    }
+
+    /**
+     * If the project should be excluded from analysis.
+     * <p>
+     * Defaults to {@code false}.
+     */
+    public boolean isSkipProject() {
+        return skipProject;
+    }
+
+    public void setSkipProject(boolean skipProject) {
+        this.skipProject = skipProject;
+    }
+
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerRootExtension.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerRootExtension.java
new file mode 100644
index 0000000..1da68d4
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/SonarRunnerRootExtension.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner;
+
+import org.gradle.api.Action;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.process.JavaForkOptions;
+
+/**
+ * Specialization of {@link SonarRunnerExtension} that is used for the root of the project tree being analyzed.
+ * <p>
+ * This extension provides extra configuration options that are only applicable to the analysis as a whole,
+ * and therefore is used for the project (typically the root) that the plugin is applied too.
+ * <p>
+ * Example usage:
+ * <pre autoTested=''>
+ * sonarRunner {
+ *   toolVersion = '2.3' // default
+ *
+ *   forkOptions {
+ *     maxHeapSize = '1024m'
+ *     jvmArgs '-XX:MaxPermSize=128m'
+ *   }
+ * }
+ * </pre>
+ */
+public class SonarRunnerRootExtension extends SonarRunnerExtension {
+
+    /**
+     * The version of Sonar Runner used if another version was not specified with {@link #setToolVersion(String)}.
+     * <p>
+     * Value: {@value}
+     */
+    public static final String DEFAULT_SONAR_RUNNER_VERSION = "2.3"; // IMPORTANT: if updating this, update the DSL doc.
+
+    private String toolVersion = DEFAULT_SONAR_RUNNER_VERSION;
+    private JavaForkOptions forkOptions;
+
+    public SonarRunnerRootExtension(ActionBroadcast<SonarProperties> propertiesActions) {
+        super(propertiesActions);
+    }
+
+    /**
+     * Configure the {@link #forkOptions}.
+     *
+     * @param action the action to use to configure {@link #forkOptions}
+     */
+    public void forkOptions(Action<? super JavaForkOptions> action) {
+        action.execute(forkOptions);
+    }
+
+    /**
+     * Version of Sonar Runner JARs to use.
+     * <p>
+     * Defaults to {@code 2.3}.
+     */
+    // IMPORTANT: update above if DEFAULT_SONAR_RUNNER_VERSION changes
+    public String getToolVersion() {
+        return toolVersion;
+    }
+
+    public void setToolVersion(String toolVersion) {
+        this.toolVersion = toolVersion;
+    }
+
+    /**
+     * Options for the Sonar Runner process.
+     */
+    public JavaForkOptions getForkOptions() {
+        return forkOptions;
+    }
+
+    public void setForkOptions(JavaForkOptions forkOptions) {
+        this.forkOptions = forkOptions;
+    }
+
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/package-info.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/package-info.java
new file mode 100644
index 0000000..ed18213
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Sonar.
+ * <p>
+ * Please see <a href="http://gradle.org/current/docs/userguide/sonar_runner_plugin.html">Sonar Runner Plugin</a>
+ */
+package org.gradle.sonar.runner;
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPlugin.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPlugin.java
new file mode 100644
index 0000000..d795b01
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPlugin.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner.plugins;
+
+import com.beust.jcommander.internal.Maps;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.gradle.api.*;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.DependencySet;
+import org.gradle.api.artifacts.ResolvableDependencies;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.ConventionMapping;
+import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.internal.jvm.Jvm;
+import org.gradle.listener.ActionBroadcast;
+import org.gradle.sonar.runner.SonarProperties;
+import org.gradle.sonar.runner.SonarRunnerExtension;
+import org.gradle.sonar.runner.SonarRunnerRootExtension;
+import org.gradle.sonar.runner.tasks.SonarRunner;
+import org.gradle.testing.jacoco.plugins.JacocoPlugin;
+import org.gradle.testing.jacoco.plugins.JacocoTaskExtension;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import static org.gradle.util.CollectionUtils.nonEmptyOrNull;
+
+/**
+ * A plugin for analyzing projects with the <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+SonarQube+Runner">Sonar Runner</a>.
+ * <p>
+ * When applied to a project, both the project itself and its subprojects will be analyzed (in a single run).
+ * <p>
+ * Please see the “Sonar Runner Plugin” chapter of the Gradle User Guide for more information.
+ */
+ at Incubating
+public class SonarRunnerPlugin implements Plugin<Project> {
+
+    private static final Predicate<File> FILE_EXISTS = new Predicate<File>() {
+        public boolean apply(File input) {
+            return input.exists();
+        }
+    };
+    private static final Predicate<File> IS_DIRECTORY = new Predicate<File>() {
+        public boolean apply(File input) {
+            return input.isDirectory();
+        }
+    };
+    private static final Predicate<File> IS_FILE = new Predicate<File>() {
+        public boolean apply(File input) {
+            return input.isFile();
+        }
+    };
+    private static final Joiner COMMA_JOINER = Joiner.on(",");
+
+    private Project targetProject;
+
+    public void apply(Project project) {
+        targetProject = project;
+
+        final Map<Project, ActionBroadcast<SonarProperties>> actionBroadcastMap = Maps.newHashMap();
+        SonarRunner sonarRunnerTask = createTask(project, actionBroadcastMap);
+
+        ActionBroadcast<SonarProperties> actionBroadcast = addBroadcaster(actionBroadcastMap, project);
+        project.subprojects(new Action<Project>() {
+            public void execute(Project project) {
+                ActionBroadcast<SonarProperties> actionBroadcast = addBroadcaster(actionBroadcastMap, project);
+                project.getExtensions().create(SonarRunnerExtension.SONAR_RUNNER_EXTENSION_NAME, SonarRunnerExtension.class, actionBroadcast);
+            }
+        });
+        SonarRunnerRootExtension rootExtension = project.getExtensions().create(SonarRunnerExtension.SONAR_RUNNER_EXTENSION_NAME, SonarRunnerRootExtension.class, actionBroadcast);
+        addConfiguration(project, rootExtension);
+        rootExtension.setForkOptions(sonarRunnerTask.getForkOptions());
+    }
+
+    private ActionBroadcast<SonarProperties> addBroadcaster(Map<Project, ActionBroadcast<SonarProperties>> actionBroadcastMap, Project project) {
+        ActionBroadcast<SonarProperties> actionBroadcast = new ActionBroadcast<SonarProperties>();
+        actionBroadcastMap.put(project, actionBroadcast);
+        return actionBroadcast;
+    }
+
+    private SonarRunner createTask(final Project project, final Map<Project, ActionBroadcast<SonarProperties>> actionBroadcastMap) {
+        SonarRunner sonarRunnerTask = project.getTasks().create(SonarRunnerExtension.SONAR_RUNNER_TASK_NAME, SonarRunner.class);
+        sonarRunnerTask.setDescription("Analyzes " + project + " and its subprojects with Sonar Runner.");
+
+        ConventionMapping conventionMapping = new DslObject(sonarRunnerTask).getConventionMapping();
+        conventionMapping.map("sonarProperties", new Callable<Object>() {
+            public Object call() throws Exception {
+                Map<String, Object> properties = Maps.newLinkedHashMap();
+                computeSonarProperties(project, properties, actionBroadcastMap);
+                return properties;
+            }
+        });
+
+        sonarRunnerTask.dependsOn(new Callable<Iterable<? extends Task>>() {
+            public Iterable<? extends Task> call() throws Exception {
+                Iterable<Project> applicableProjects = Iterables.filter(project.getAllprojects(), new Predicate<Project>() {
+                    public boolean apply(Project input) {
+                        return input.getPlugins().hasPlugin(JavaPlugin.class)
+                                && !input.getExtensions().getByType(SonarRunnerExtension.class).isSkipProject();
+                    }
+                });
+
+                return Iterables.transform(applicableProjects, new Function<Project, Task>() {
+                    @Nullable
+                    public Task apply(Project input) {
+                        return input.getTasks().getByName(JavaPlugin.TEST_TASK_NAME);
+                    }
+                });
+            }
+        });
+
+        return sonarRunnerTask;
+    }
+
+    public void computeSonarProperties(Project project, Map<String, Object> properties, Map<Project, ActionBroadcast<SonarProperties>> sonarPropertiesActionBroadcastMap) {
+        SonarRunnerExtension extension = project.getExtensions().getByType(SonarRunnerExtension.class);
+        if (extension.isSkipProject()) {
+            return;
+        }
+
+        Map<String, Object> rawProperties = Maps.newLinkedHashMap();
+        addGradleDefaults(project, rawProperties);
+        evaluateSonarPropertiesBlocks(sonarPropertiesActionBroadcastMap.get(project), rawProperties);
+        if (project.equals(targetProject)) {
+            addSystemProperties(rawProperties);
+        }
+
+        String projectPrefix = project.getPath().substring(targetProject.getPath().length()).replace(":", ".");
+        if (projectPrefix.startsWith(".")) {
+            projectPrefix = projectPrefix.substring(1);
+        }
+
+        convertProperties(rawProperties, projectPrefix, properties);
+
+        List<Project> enabledChildProjects = Lists.newLinkedList(Iterables.filter(project.getChildProjects().values(), new Predicate<Project>() {
+            public boolean apply(Project input) {
+                return !input.getExtensions().getByType(SonarRunnerExtension.class).isSkipProject();
+            }
+        }));
+
+        if (enabledChildProjects.isEmpty()) {
+            return;
+        }
+
+        Collections.sort(enabledChildProjects);
+
+        String modules = COMMA_JOINER.join(Iterables.transform(enabledChildProjects, new Function<Project, String>() {
+            public String apply(Project input) {
+                return input.getName();
+            }
+        }));
+
+        properties.put(convertKey("sonar.modules", projectPrefix), modules);
+        for (Project childProject : enabledChildProjects) {
+            computeSonarProperties(childProject, properties, sonarPropertiesActionBroadcastMap);
+        }
+    }
+
+    private void addGradleDefaults(final Project project, final Map<String, Object> properties) {
+
+        // IMPORTANT: Whenever changing the properties/values here, ensure that the Gradle User Guide chapter on this is still in sync.
+
+        properties.put("sonar.projectName", project.getName());
+        properties.put("sonar.projectDescription", project.getDescription());
+        properties.put("sonar.projectVersion", project.getVersion());
+        properties.put("sonar.projectBaseDir", project.getProjectDir());
+        properties.put("sonar.dynamicAnalysis", "reuseReports");
+
+        if (project.equals(targetProject)) {
+            // We only set project key for root project because Sonar Runner 2.0 will automatically
+            // prefix subproject keys with parent key, even if subproject keys are set explicitly.
+            // Therefore it's better to rely on Sonar's defaults.
+            properties.put("sonar.projectKey", getProjectKey(project));
+            properties.put("sonar.environment.information.key", "Gradle");
+            properties.put("sonar.environment.information.version", project.getGradle().getGradleVersion());
+            properties.put("sonar.working.directory", new File(project.getBuildDir(), "sonar"));
+        }
+
+        project.getPlugins().withType(JavaBasePlugin.class, new Action<JavaBasePlugin>() {
+            public void execute(JavaBasePlugin javaBasePlugin) {
+                JavaPluginConvention javaPluginConvention = new DslObject(project).getConvention().getPlugin(JavaPluginConvention.class);
+                properties.put("sonar.java.source", javaPluginConvention.getSourceCompatibility());
+                properties.put("sonar.java.target", javaPluginConvention.getTargetCompatibility());
+            }
+        });
+
+        project.getPlugins().withType(JavaPlugin.class, new Action<JavaPlugin>() {
+            public void execute(JavaPlugin javaPlugin) {
+                JavaPluginConvention javaPluginConvention = new DslObject(project).getConvention().getPlugin(JavaPluginConvention.class);
+
+                SourceSet main = javaPluginConvention.getSourceSets().getAt("main");
+                List<File> sourceDirectories = nonEmptyOrNull(Iterables.filter(main.getAllSource().getSrcDirs(), FILE_EXISTS));
+                properties.put("sonar.sources" , sourceDirectories);
+                SourceSet test = javaPluginConvention.getSourceSets().getAt("test");
+                List<File> testDirectories = nonEmptyOrNull(Iterables.filter(test.getAllSource().getSrcDirs(), FILE_EXISTS));
+                properties.put("sonar.tests", testDirectories);
+
+                properties.put("sonar.binaries", nonEmptyOrNull(Iterables.filter(main.getRuntimeClasspath(), IS_DIRECTORY)));
+                properties.put("sonar.libraries", getLibraries(main));
+
+                final Test testTask = (Test) project.getTasks().getByName(JavaPlugin.TEST_TASK_NAME);
+
+                if (sourceDirectories != null || testDirectories != null) {
+                    File testResultsDir = testTask.getReports().getJunitXml().getDestination();
+                    // create the test results folder to prevent SonarQube from emitting
+                    // a warning if a project does not contain any tests
+                    testResultsDir.mkdirs();
+
+
+                    properties.put("sonar.surefire.reportsPath", testResultsDir);
+                    // added due to https://issues.gradle.org/browse/GRADLE-3005
+                    properties.put("sonar.junit.reportsPath", testResultsDir);
+                }
+
+                project.getPlugins().withType(JacocoPlugin.class, new Action<JacocoPlugin>() {
+                    public void execute(JacocoPlugin jacocoPlugin) {
+                        JacocoTaskExtension jacocoTaskExtension = testTask.getExtensions().getByType(JacocoTaskExtension.class);
+                        File destinationFile = jacocoTaskExtension.getDestinationFile();
+                        if (destinationFile.exists()) {
+                            properties.put("sonar.jacoco.reportPath", destinationFile);
+                        }
+                    }
+                });
+            }
+        });
+
+        if (properties.get("sonar.sources") == null) {
+            // Should be able to remove this after upgrading to Sonar Runner 2.1 (issue is already marked as fixed),
+            // if we can live with the fact that leaf projects w/o source dirs will still cause a failure.
+            properties.put("sonar.sources", "");
+        }
+    }
+
+    private String getProjectKey(Project project) {
+        // Sonar uses project keys in URL parameters without internally URL-encoding them.
+        // According to my manual tests with sonar-runner plugin based on Sonar Runner 2.0 and Sonar 3.4.1,
+        // the current defaults will only cause a problem if project.group or project.name of
+        // the Gradle project to which the plugin is applied contains special characters.
+        // (':' works, ' ' doesn't.) In such a case, sonar.projectKey can be overridden manually.
+        String name = project.getName();
+        String group = project.getGroup().toString();
+        return group.isEmpty() ? name : group + ":" + name;
+    }
+
+    private void addSystemProperties(Map<String, Object> properties) {
+        for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
+            String key = entry.getKey().toString();
+            if (key.startsWith("sonar.")) {
+                properties.put(key, entry.getValue());
+            }
+        }
+    }
+
+    private Collection<File> getLibraries(SourceSet main) {
+        List<File> libraries = Lists.newLinkedList(Iterables.filter(main.getRuntimeClasspath(), IS_FILE));
+        File runtimeJar = Jvm.current().getRuntimeJar();
+        if (runtimeJar != null) {
+            libraries.add(runtimeJar);
+        }
+
+        return libraries;
+    }
+
+    private void convertProperties(Map<String, Object> rawProperties, final String projectPrefix, final Map<String, Object> properties) {
+        for (Map.Entry<String, Object> entry : rawProperties.entrySet()) {
+            String value = convertValue(entry.getValue());
+            if (value != null) {
+                properties.put(convertKey(entry.getKey(), projectPrefix), value);
+            }
+        }
+    }
+
+    private String convertKey(String key, final String projectPrefix) {
+        return projectPrefix.isEmpty() ? key : projectPrefix + "." + key;
+    }
+
+    private String convertValue(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Iterable<?>) {
+            Iterable<String> flattened = Iterables.transform((Iterable<?>) value, new Function<Object, String>() {
+                public String apply(Object input) {
+                    return convertValue(input);
+                }
+            });
+
+            Iterable<String> filtered = Iterables.filter(flattened, Predicates.notNull());
+            String joined = COMMA_JOINER.join(filtered);
+            return joined.isEmpty() ? null : joined;
+        } else {
+            return value.toString();
+        }
+    }
+
+    private void addConfiguration(final Project project, final SonarRunnerRootExtension rootExtension) {
+        final Configuration configuration = project.getConfigurations().create(SonarRunnerExtension.SONAR_RUNNER_CONFIGURATION_NAME);
+        configuration
+                .setVisible(false)
+                .setTransitive(false)
+                .setDescription("The SonarRunner configuration to use to run analysis")
+                .getIncoming()
+                .beforeResolve(new Action<ResolvableDependencies>() {
+                    public void execute(ResolvableDependencies resolvableDependencies) {
+                        DependencySet dependencies = resolvableDependencies.getDependencies();
+                        if (dependencies.isEmpty()) {
+                            String toolVersion = rootExtension.getToolVersion();
+                            DependencyHandler dependencyHandler = project.getDependencies();
+                            Dependency dependency = dependencyHandler.create("org.codehaus.sonar.runner:sonar-runner-dist:" + toolVersion);
+                            configuration.getDependencies().add(dependency);
+                        }
+                    }
+                });
+    }
+
+    private static void evaluateSonarPropertiesBlocks(ActionBroadcast<? super SonarProperties> propertiesActions, Map<String, Object> properties) {
+        SonarProperties sonarProperties = new SonarProperties(properties);
+        propertiesActions.execute(sonarProperties);
+    }
+
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/package-info.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/package-info.java
new file mode 100644
index 0000000..6a545ac
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/plugins/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Sonar Runner integration.
+ *
+ * @see org.gradle.sonar.runner.plugins.SonarRunnerPlugin
+ */
+package org.gradle.sonar.runner.plugins;
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/SonarRunner.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/SonarRunner.java
new file mode 100644
index 0000000..521bc26
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/SonarRunner.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.sonar.runner.tasks;
+
+import com.beust.jcommander.internal.Maps;
+import com.google.common.base.Joiner;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Incubating;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.process.JavaForkOptions;
+import org.gradle.process.internal.DefaultJavaForkOptions;
+import org.gradle.process.internal.JavaExecHandleBuilder;
+import org.gradle.sonar.runner.SonarRunnerExtension;
+import org.gradle.util.GUtil;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Analyses one or more projects with the <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+Sonar+Runner">Sonar Runner</a>.
+ * <p>
+ * Can be used with or without the {@code "sonar-runner"} plugin.
+ * If used together with the plugin, {@code sonarProperties} will be populated with defaults based on Gradle's object model and user-defined
+ * values configured via {@link SonarRunnerExtension} and {@link org.gradle.sonar.runner.SonarRunnerRootExtension}.
+ * If used without the plugin, all properties have to be configured manually.
+ * <p>
+ * For more information on how to configure the Sonar Runner, and on which properties are available, see the
+ * <a href="http://docs.codehaus.org/display/SONAR/Analyzing+with+SonarQube+Runner">Sonar Runner documentation</a>.
+ */
+ at Incubating
+public class SonarRunner extends DefaultTask {
+
+    private static final Logger LOGGER = Logging.getLogger(SonarRunner.class);
+    private static final String MAIN_CLASS_NAME = "org.sonar.runner.Main";
+
+    private JavaForkOptions forkOptions;
+    private Map<String, Object> sonarProperties;
+
+    @TaskAction
+    public void run() {
+        prepareExec().build().start().waitForFinish().assertNormalExitValue();
+    }
+
+    JavaExecHandleBuilder prepareExec() {
+        Map<String, Object> properties = getSonarProperties();
+        if(getProject().file("sonar-project.properties").exists()){
+            LOGGER.warn("Found 'sonar-project.properties' in project directory: Sonar Runner may read this file to override the Gradle 'sonarRunner' configuration.");
+        }
+
+        if (LOGGER.isInfoEnabled()) {
+            LOGGER.info("Executing Sonar Runner with properties:\n[{}]", Joiner.on(", ").withKeyValueSeparator(": ").join(properties));
+        }
+
+        JavaExecHandleBuilder javaExec = new JavaExecHandleBuilder(getFileResolver());
+        getForkOptions().copyTo(javaExec);
+
+        FileCollection sonarRunnerConfiguration = getProject().getConfigurations().getAt(SonarRunnerExtension.SONAR_RUNNER_CONFIGURATION_NAME);
+
+        Properties propertiesObject = new Properties();
+        propertiesObject.putAll(properties);
+        File propertyFile = new File(getTemporaryDir(), "sonar-project.properties");
+        GUtil.saveProperties(propertiesObject, propertyFile);
+
+        return javaExec
+                .systemProperty("project.settings", propertyFile.getAbsolutePath())
+
+                // This value is set in the properties file, but Sonar Runner 2.4 requires it on the command line as well
+                // http://forums.gradle.org/gradle/topics/gradle-2-2-nightly-sonarrunner-task-fails-with-toolversion-2-4
+                .systemProperty("project.home", getProject().getProjectDir().getAbsolutePath())
+
+                .setClasspath(sonarRunnerConfiguration)
+                .setMain(MAIN_CLASS_NAME);
+    }
+
+    /**
+     * Options for the analysis process. Configured via {@link org.gradle.sonar.runner.SonarRunnerRootExtension#forkOptions}.
+     */
+    public JavaForkOptions getForkOptions() {
+        if (forkOptions == null) {
+            forkOptions = new DefaultJavaForkOptions(getFileResolver());
+        }
+
+        return forkOptions;
+    }
+
+    /**
+     * The String key/value pairs to be passed to the Sonar Runner.
+     *
+     * {@code null} values are not permitted.
+     */
+    @Input
+    public Map<String, Object> getSonarProperties() {
+        if (sonarProperties == null) {
+            sonarProperties = Maps.newLinkedHashMap();
+        }
+
+        return sonarProperties;
+    }
+
+    @Inject
+    protected FileResolver getFileResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/package-info.java b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/package-info.java
new file mode 100644
index 0000000..a17f9c4
--- /dev/null
+++ b/subprojects/sonar/src/main/groovy/org/gradle/sonar/runner/tasks/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 the Sonar Runner integration.
+ *
+ * @see org.gradle.sonar.runner.tasks.SonarRunner
+ */
+package org.gradle.sonar.runner.tasks;
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/org.gradle.sonar-runner.properties b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/org.gradle.sonar-runner.properties
new file mode 100644
index 0000000..1e2b066
--- /dev/null
+++ b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/org.gradle.sonar-runner.properties
@@ -0,0 +1 @@
+implementation-class=org.gradle.sonar.runner.plugins.SonarRunnerPlugin
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar.properties b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/org.gradle.sonar.properties
similarity index 100%
rename from subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar.properties
rename to subprojects/sonar/src/main/resources/META-INF/gradle-plugins/org.gradle.sonar.properties
diff --git a/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar-runner.properties b/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar-runner.properties
deleted file mode 100644
index f49b1b0..0000000
--- a/subprojects/sonar/src/main/resources/META-INF/gradle-plugins/sonar-runner.properties
+++ /dev/null
@@ -1 +0,0 @@
-implementation-class=org.gradle.api.sonar.runner.SonarRunnerPlugin
\ No newline at end of file
diff --git a/subprojects/sonar/src/main/resources/logback.xml b/subprojects/sonar/src/main/resources/logback.xml
new file mode 100644
index 0000000..b67a0a9
--- /dev/null
+++ b/subprojects/sonar/src/main/resources/logback.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright 2015 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<!-- This config file is here because the runner used by the old sonar plugin uses logback and is very noisy without it-->
+<configuration>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%xEx</pattern>
+        </encoder>
+    </appender>
+
+    <root level="warn">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>
\ No newline at end of file
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy
index 6e49766..8686e57 100644
--- a/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy
+++ b/subprojects/sonar/src/test/groovy/org/gradle/api/plugins/sonar/SonarPluginTest.groovy
@@ -32,7 +32,7 @@ class SonarPluginTest extends Specification {
         def project = TestUtil.createRootProject()
 
         when:
-        project.plugins.apply(SonarPlugin)
+        project.pluginManager.apply(SonarPlugin)
 
         then:
         project.sonar instanceof SonarRootModel
@@ -44,7 +44,7 @@ class SonarPluginTest extends Specification {
         def child = TestUtil.createChildProject(project, "child")
 
         when:
-        project.plugins.apply(SonarPlugin)
+        project.pluginManager.apply(SonarPlugin)
 
         then:
         child.sonar instanceof SonarProjectModel
@@ -55,7 +55,7 @@ class SonarPluginTest extends Specification {
         def project = TestUtil.createRootProject()
 
         when:
-        project.plugins.apply(SonarPlugin)
+        project.pluginManager.apply(SonarPlugin)
 
         then:
         SonarRootModel sonar = project.sonar
@@ -97,7 +97,7 @@ class SonarPluginTest extends Specification {
         sonarProject.java.targetCompatibility == project.targetCompatibility as String
 
         where:
-        project << createMultiProject { plugins.apply(JavaBasePlugin) }.allprojects
+        project << createMultiProject { pluginManager.apply(JavaBasePlugin) }.allprojects
     }
 
     def "provides additional defaults for project configuration if java plugin is present"(Project project) {
@@ -135,7 +135,7 @@ class SonarPluginTest extends Specification {
         ConfigureUtil.configure(commonConfig, child)
         child.group = "group"
 
-        root.plugins.apply(SonarPlugin)
+        root.pluginManager.apply(SonarPlugin)
 
         root
     }
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarPropertiesTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarPropertiesTest.groovy
deleted file mode 100644
index 2676f6f..0000000
--- a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarPropertiesTest.groovy
+++ /dev/null
@@ -1,48 +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.sonar.runner
-
-import spock.lang.Specification
-
-class SonarPropertiesTest extends Specification {
-    def properties = new SonarProperties()
-
-    def "set a single property"() {
-        when:
-        properties.property "foo", "one"
-
-        then:
-        properties.properties == [foo: "one"]
-    }
-
-    def "set multiple properties at once"() {
-        when:
-        properties.properties foo: "one", bar: "two"
-
-        then:
-        properties.properties == [foo:  "one", bar: "two"]
-    }
-
-    def "read and write the properties map directly"() {
-        when:
-        properties.properties = [foo: "one", bar: "two"]
-        properties.properties.bar *= 2
-        properties.properties.remove("foo")
-
-        then:
-        properties.properties == [bar: "twotwo"]
-    }
-}
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerExtensionTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerExtensionTest.groovy
deleted file mode 100644
index 12fdf43..0000000
--- a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerExtensionTest.groovy
+++ /dev/null
@@ -1,43 +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.sonar.runner
-
-import org.gradle.api.Action
-import spock.lang.Specification
-
-class SonarRunnerExtensionTest extends Specification {
-    def extension = new SonarRunnerExtension()
-
-    def "evaluate properties blocks"() {
-        def props = ["key.1": "value 1"]
-
-        when:
-        extension.sonarProperties({
-            it.property "key.2", ["value 2"]
-            it.properties(["key.3": "value 3", "key.4": "value 4"])
-        } as Action)
-        extension.sonarProperties({
-            it.property "key.5", "value 5"
-            it.properties["key.2"] << "value 6"
-            it.properties.remove("key.3")
-        } as Action)
-
-        extension.evaluateSonarPropertiesBlocks(props)
-
-        then:
-        props == ["key.1": "value 1", "key.2": ["value 2", "value 6"], "key.4": "value 4", "key.5": "value 5"]
-    }
-}
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerPluginTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerPluginTest.groovy
deleted file mode 100644
index 8f82274..0000000
--- a/subprojects/sonar/src/test/groovy/org/gradle/api/sonar/runner/SonarRunnerPluginTest.groovy
+++ /dev/null
@@ -1,377 +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.sonar.runner
-
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.tasks.TaskDependencyMatchers
-import org.gradle.internal.jvm.Jvm
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestUtil
-import org.junit.Rule
-
-import spock.lang.Specification
-
-import static spock.util.matcher.HamcrestSupport.*
-import static org.hamcrest.Matchers.*
-
-class SonarRunnerPluginTest extends Specification {
-    @Rule SetSystemProperties systemProperties
-
-    def rootProject = TestUtil.builder().withName("root").build()
-    def parentProject = TestUtil.builder().withName("parent").withParent(rootProject).build()
-    def childProject = TestUtil.builder().withName("child").withParent(parentProject).build()
-    def childProject2 = TestUtil.builder().withName("child2").withParent(parentProject).build()
-    def leafProject = TestUtil.builder().withName("leaf").withParent(childProject).build()
-
-    def setup() {
-        parentProject.plugins.apply(SonarRunnerPlugin)
-        rootProject.allprojects {
-            group = "group"
-            version = 1.3
-            description = "description"
-            buildDir = "buildDir"
-        }
-    }
-
-    def "adds a sonarRunner extension to the target project (i.e. the project to which the plugin is applied) and its subprojects"() {
-        expect:
-        rootProject.extensions.findByName("sonarRunner") == null
-        parentProject.extensions.findByName("sonarRunner") instanceof SonarRunnerExtension
-        childProject.extensions.findByName("sonarRunner") instanceof SonarRunnerExtension
-    }
-
-    def "adds a sonarRunner task to the target project"() {
-        expect:
-        parentProject.tasks.findByName("sonarRunner") instanceof SonarRunner
-        parentProject.tasks.sonarRunner.description == "Analyzes project ':parent' and its subprojects with Sonar Runner."
-
-        childProject.tasks.findByName("sonarRunner") == null
-    }
-
-    def "makes sonarRunner task depend on test tasks of the target project and its subprojects"() {
-        when:
-        rootProject.plugins.apply(JavaPlugin)
-        parentProject.plugins.apply(JavaPlugin)
-        childProject.plugins.apply(JavaPlugin)
-
-        then:
-        expect(parentProject.tasks.sonarRunner, TaskDependencyMatchers.dependsOnPaths(containsInAnyOrder(":parent:test", ":parent:child:test")))
-    }
-
-    def "doesn't make sonarRunner task depend on test task of skipped projects"() {
-        when:
-        rootProject.plugins.apply(JavaPlugin)
-        parentProject.plugins.apply(JavaPlugin)
-        childProject.plugins.apply(JavaPlugin)
-        childProject.sonarRunner.skipProject = true
-
-        then:
-        expect(parentProject.tasks.sonarRunner, TaskDependencyMatchers.dependsOnPaths(contains(":parent:test")))
-    }
-
-    def "adds default properties for target project and its subprojects"() {
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.sources"] == ""
-        properties["sonar.projectName"] == "parent"
-        properties["sonar.projectDescription"] == "description"
-        properties["sonar.projectVersion"] == "1.3"
-        properties["sonar.projectBaseDir"] == parentProject.projectDir as String
-        properties["sonar.working.directory"] == new File(parentProject.buildDir, "sonar") as String
-        properties["sonar.dynamicAnalysis"] == "reuseReports"
-
-        and:
-        properties["child.sonar.sources"] == ""
-        properties["child.sonar.projectName"] == "child"
-        properties["child.sonar.projectDescription"] == "description"
-        properties["child.sonar.projectVersion"] == "1.3"
-        properties["child.sonar.projectBaseDir"] == childProject.projectDir as String
-        properties["child.sonar.dynamicAnalysis"] == "reuseReports"
-    }
-
-    def "adds additional default properties for target project"() {
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.projectKey"] == "group:parent"
-        properties["sonar.environment.information.key"] == "Gradle"
-        properties["sonar.environment.information.version"] == parentProject.gradle.gradleVersion
-        properties["sonar.working.directory"] == new File(parentProject.buildDir, "sonar") as String
-
-        and:
-        !properties.containsKey("child.sonar.projectKey") // default left to Sonar
-        !properties.containsKey("child.sonar.environment.information.key")
-        !properties.containsKey("child.sonar.environment.information.version")
-        !properties.containsKey('child.sonar.working.directory')
-    }
-
-    def "defaults projectKey to project.name if project.group isn't set"() {
-        parentProject.group = "" // or null, but only rootProject.group can effectively be set to null
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.projectKey"] == "parent"
-    }
-
-    def "adds additional default properties for 'java-base' projects"() {
-        parentProject.plugins.apply(JavaBasePlugin)
-        childProject.plugins.apply(JavaBasePlugin)
-        parentProject.sourceCompatibility = 1.5
-        parentProject.targetCompatibility = 1.6
-        childProject.sourceCompatibility = 1.6
-        childProject.targetCompatibility = 1.7
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.java.source"] == "1.5"
-        properties["sonar.java.target"] == "1.6"
-        properties["child.sonar.java.source"] == "1.6"
-        properties["child.sonar.java.target"] == "1.7"
-    }
-
-    def "adds additional default properties for 'java' projects"() {
-        parentProject.plugins.apply(JavaPlugin)
-
-        parentProject.sourceSets.main.java.srcDirs = ["src"]
-        parentProject.sourceSets.test.java.srcDirs = ["test"]
-        parentProject.sourceSets.main.output.classesDir = "$parentProject.buildDir/out"
-        parentProject.sourceSets.main.output.resourcesDir = "$parentProject.buildDir/out"
-        parentProject.sourceSets.main.runtimeClasspath += parentProject.files("lib/SomeLib.jar")
-
-        new TestFile(parentProject.projectDir).create {
-            src {}
-            test {}
-            buildDir {
-                out {}
-                "test-results" {}
-            }
-            lib {
-                file("SomeLib.jar")
-            }
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.sources"] == new File(parentProject.projectDir, "src") as String
-        properties["sonar.tests"] == new File(parentProject.projectDir, "test") as String
-        properties["sonar.binaries"].contains(new File(parentProject.buildDir, "out") as String)
-        properties["sonar.libraries"].contains(new File(parentProject.projectDir, "lib/SomeLib.jar") as String)
-        properties["sonar.surefire.reportsPath"] == new File(parentProject.buildDir, "test-results") as String
-        properties["sonar.junit.reportsPath"] == new File(parentProject.buildDir, "test-results") as String
-    }
-
-    def "only adds existing directories"() {
-        parentProject.plugins.apply(JavaPlugin)
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        !properties.containsKey("sonar.tests")
-        !properties.containsKey("sonar.binaries")
-        properties.containsKey("sonar.libraries") == (Jvm.current().getRuntimeJar() != null)
-        !properties.containsKey("sonar.surefire.reportsPath")
-        !properties.containsKey("sonar.junit.reportsPath")
-    }
-
-    def "adds empty 'sonar.sources' property if no sources exist (because Sonar Runner 2.0 always expects this property to be set)"() {
-        childProject2.plugins.apply(JavaPlugin)
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.sources"] == ""
-        properties["child.sonar.sources"] == ""
-        properties["child2.sonar.sources"] == ""
-        properties["child.leaf.sonar.sources"] == ""
-    }
-
-    def "allows to configure Sonar properties via 'sonarRunner' extension"() {
-        parentProject.sonarRunner.sonarProperties {
-            property "sonar.some.key", "some value"
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.some.key"] == "some value"
-    }
-
-    def "prefixes property keys of subprojects"() {
-        childProject.sonarRunner.sonarProperties {
-            property "sonar.some.key", "other value"
-        }
-        leafProject.sonarRunner.sonarProperties {
-            property "sonar.some.key", "other value"
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["child.sonar.some.key"] == "other value"
-        properties["child.leaf.sonar.some.key"] == "other value"
-    }
-
-    def "adds 'modules' properties declaring (prefixes of) subprojects"() {
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.modules"] == "child,child2"
-        properties["child.sonar.modules"] == "leaf"
-        !properties.containsKey("child2.sonar.modules")
-        !properties.containsKey("child.leaf.sonar.modules")
-    }
-
-    def "handles 'modules' properties correctly if plugin is applied to root project"() {
-        def rootProject = TestUtil.builder().withName("root").build()
-        def project = TestUtil.builder().withName("parent").withParent(rootProject).build()
-        def project2 = TestUtil.builder().withName("parent2").withParent(rootProject).build()
-        def childProject = TestUtil.builder().withName("child").withParent(project).build()
-
-        rootProject.plugins.apply(SonarRunnerPlugin)
-
-        when:
-        def properties = rootProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.modules"] == "parent,parent2"
-        properties["parent.sonar.modules"] == "child"
-        !properties.containsKey("parent2.sonar.modules")
-        !properties.containsKey("parent.child.sonar.modules")
-
-    }
-
-    def "evaluates 'sonarRunner' block lazily"() {
-        parentProject.version = "1.0"
-        parentProject.sonarRunner.sonarProperties {
-            property "sonar.projectVersion", parentProject.version
-        }
-        parentProject.version = "1.2.3"
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.projectVersion"] == "1.2.3"
-    }
-
-    def "converts Sonar property values to strings"() {
-        def object = new Object() {
-            String toString() {
-                "object"
-            }
-        }
-
-        parentProject.sonarRunner.sonarProperties {
-            property "some.object", object
-            property "some.list", [1, object, 2]
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["some.object"] == "object"
-        properties["some.list"] == "1,object,2"
-    }
-
-    def "removes Sonar properties with null values"() {
-        parentProject.sonarRunner.sonarProperties {
-            property "some.key", null
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        !properties.containsKey("some.key")
-    }
-
-    def "allows to set Sonar properties for target project via 'sonar.xyz' system properties"() {
-        System.setProperty("sonar.some.key", "some value")
-        System.setProperty("sonar.projectVersion", "3.2")
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.some.key"] == "some value"
-        properties["sonar.projectVersion"] == "3.2"
-
-        and:
-        !properties.containsKey("child.sonar.some.key")
-        properties["child.sonar.projectVersion"] == "1.3"
-    }
-
-    def "handles system properties correctly if plugin is applied to root project"() {
-        def rootProject = TestUtil.builder().withName("root").build()
-        def project = TestUtil.builder().withName("parent").withParent(rootProject).build()
-
-        rootProject.allprojects { version = 1.3 }
-        rootProject.plugins.apply(SonarRunnerPlugin)
-        System.setProperty("sonar.some.key", "some value")
-        System.setProperty("sonar.projectVersion", "3.2")
-
-        when:
-        def properties = rootProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.some.key"] == "some value"
-        properties["sonar.projectVersion"] == "3.2"
-
-        and:
-        !properties.containsKey("parent.sonar.some.key")
-        properties["parent.sonar.projectVersion"] == "1.3"
-    }
-
-    def "system properties win over values set in build script"() {
-        System.setProperty("sonar.some.key", "win")
-        parentProject.sonarRunner.sonarProperties {
-            property "sonar.some.key", "lose"
-        }
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        properties["sonar.some.key"] == "win"
-    }
-
-    def "doesn't add Sonar properties for skipped projects"() {
-        childProject.sonarRunner.skipProject = true
-
-        when:
-        def properties = parentProject.tasks.sonarRunner.sonarProperties
-
-        then:
-        !properties.any { key, value -> key.startsWith("child.sonar.") }
-    }
-}
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarPropertiesTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarPropertiesTest.groovy
new file mode 100644
index 0000000..3f500b7
--- /dev/null
+++ b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarPropertiesTest.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.sonar.runner
+
+import spock.lang.Specification
+
+class SonarPropertiesTest extends Specification {
+    def properties = new SonarProperties([:])
+
+    def "set a single property"() {
+        when:
+        properties.property "foo", "one"
+
+        then:
+        properties.properties == [foo: "one"]
+    }
+
+    def "set multiple properties at once"() {
+        when:
+        properties.properties foo: "one", bar: "two"
+
+        then:
+        properties.properties == [foo: "one", bar: "two"]
+    }
+
+    def "read and write the properties map directly"() {
+        when:
+        properties.properties.putAll(foo: "one", bar: "two")
+        properties.properties.bar *= 2
+        properties.properties.remove("foo")
+
+        then:
+        properties.properties == [bar: "twotwo"]
+    }
+}
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarRunnerExtensionTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarRunnerExtensionTest.groovy
new file mode 100644
index 0000000..f89eb07
--- /dev/null
+++ b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/SonarRunnerExtensionTest.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.sonar.runner
+
+import org.gradle.listener.ActionBroadcast
+import spock.lang.Specification
+
+class SonarRunnerExtensionTest extends Specification {
+
+    def "evaluate properties blocks"() {
+        def actionBroadcast = new ActionBroadcast<SonarProperties>()
+        def extension = new SonarRunnerExtension(actionBroadcast)
+        def props = ["key.1": "value 1"]
+
+        when:
+        extension.sonarProperties {
+            it.property "key.2", ["value 2"]
+            it.properties(["key.3": "value 3", "key.4": "value 4"])
+        }
+
+        extension.sonarProperties {
+            it.property "key.5", "value 5"
+            it.properties["key.2"] << "value 6"
+            it.properties.remove("key.3")
+        }
+
+        def sonarProperties = new SonarProperties(props)
+        actionBroadcast.execute(sonarProperties)
+
+        then:
+        props == ["key.1": "value 1", "key.2": ["value 2", "value 6"], "key.4": "value 4", "key.5": "value 5"]
+    }
+}
diff --git a/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPluginTest.groovy b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPluginTest.groovy
new file mode 100644
index 0000000..ad4f0e5
--- /dev/null
+++ b/subprojects/sonar/src/test/groovy/org/gradle/sonar/runner/plugins/SonarRunnerPluginTest.groovy
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.sonar.runner.plugins
+
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.tasks.TaskDependencyMatchers
+import org.gradle.internal.jvm.Jvm
+import org.gradle.process.JavaForkOptions
+import org.gradle.process.internal.DefaultJavaForkOptions
+import org.gradle.process.internal.JavaExecHandleBuilder
+import org.gradle.sonar.runner.SonarRunnerExtension
+import org.gradle.sonar.runner.SonarRunnerRootExtension
+import org.gradle.sonar.runner.tasks.SonarRunner
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.SetSystemProperties
+import org.gradle.util.TestUtil
+import org.junit.Rule
+import spock.lang.Specification
+
+import static org.hamcrest.Matchers.contains
+import static org.hamcrest.Matchers.containsInAnyOrder
+import static spock.util.matcher.HamcrestSupport.expect
+
+class SonarRunnerPluginTest extends Specification {
+    @Rule
+    SetSystemProperties systemProperties
+
+    def rootProject = TestUtil.builder().withName("root").build()
+    def parentProject = TestUtil.builder().withName("parent").withParent(rootProject).build()
+    def childProject = TestUtil.builder().withName("child").withParent(parentProject).build()
+    def childProject2 = TestUtil.builder().withName("child2").withParent(parentProject).build()
+    def leafProject = TestUtil.builder().withName("leaf").withParent(childProject).build()
+
+    def setup() {
+        parentProject.pluginManager.apply(SonarRunnerPlugin)
+        parentProject.repositories {
+            mavenCentral()
+        }
+
+        rootProject.allprojects {
+            group = "group"
+            version = 1.3
+            description = "description"
+            buildDir = "buildDir"
+        }
+    }
+
+    def "adds a sonarRunner extension to the target project (i.e. the project to which the plugin is applied) and its subprojects"() {
+        expect:
+        rootProject.extensions.findByName("sonarRunner") == null
+        parentProject.extensions.findByName("sonarRunner") instanceof SonarRunnerRootExtension
+        childProject.extensions.findByName("sonarRunner") instanceof SonarRunnerExtension
+    }
+
+    def "adds a sonarRunner task to the target project"() {
+        expect:
+        parentProject.tasks.findByName("sonarRunner") instanceof SonarRunner
+        parentSonarRunnerTask().description == "Analyzes project ':parent' and its subprojects with Sonar Runner."
+
+        childProject.tasks.findByName("sonarRunner") == null
+    }
+
+    def "makes sonarRunner task depend on test tasks of the target project and its subprojects"() {
+        when:
+        rootProject.pluginManager.apply(JavaPlugin)
+        parentProject.pluginManager.apply(JavaPlugin)
+        childProject.pluginManager.apply(JavaPlugin)
+
+        then:
+        expect(parentSonarRunnerTask(), TaskDependencyMatchers.dependsOnPaths(containsInAnyOrder(":parent:test", ":parent:child:test")))
+    }
+
+    def "doesn't make sonarRunner task depend on test task of skipped projects"() {
+        when:
+        rootProject.pluginManager.apply(JavaPlugin)
+        parentProject.pluginManager.apply(JavaPlugin)
+        childProject.pluginManager.apply(JavaPlugin)
+        childProject.sonarRunner.skipProject = true
+
+        then:
+        expect(parentSonarRunnerTask(), TaskDependencyMatchers.dependsOnPaths(contains(":parent:test")))
+    }
+
+    def "adds default properties for target project and its subprojects"() {
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.sources"] == ""
+        properties["sonar.projectName"] == "parent"
+        properties["sonar.projectDescription"] == "description"
+        properties["sonar.projectVersion"] == "1.3"
+        properties["sonar.projectBaseDir"] == parentProject.projectDir as String
+        properties["sonar.working.directory"] == new File(parentProject.buildDir, "sonar") as String
+        properties["sonar.dynamicAnalysis"] == "reuseReports"
+
+        and:
+        properties["child.sonar.sources"] == ""
+        properties["child.sonar.projectName"] == "child"
+        properties["child.sonar.projectDescription"] == "description"
+        properties["child.sonar.projectVersion"] == "1.3"
+        properties["child.sonar.projectBaseDir"] == childProject.projectDir as String
+        properties["child.sonar.dynamicAnalysis"] == "reuseReports"
+    }
+
+    def "adds additional default properties for target project"() {
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.projectKey"] == "group:parent"
+        properties["sonar.environment.information.key"] == "Gradle"
+        properties["sonar.environment.information.version"] == parentProject.gradle.gradleVersion
+        properties["sonar.working.directory"] == new File(parentProject.buildDir, "sonar") as String
+
+        and:
+        !properties.containsKey("child.sonar.projectKey") // default left to Sonar
+        !properties.containsKey("child.sonar.environment.information.key")
+        !properties.containsKey("child.sonar.environment.information.version")
+        !properties.containsKey('child.sonar.working.directory')
+    }
+
+    def "defaults projectKey to project.name if project.group isn't set"() {
+        parentProject.group = "" // or null, but only rootProject.group can effectively be set to null
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.projectKey"] == "parent"
+    }
+
+    def "adds additional default properties for 'java-base' projects"() {
+        parentProject.pluginManager.apply(JavaBasePlugin)
+        childProject.pluginManager.apply(JavaBasePlugin)
+        parentProject.sourceCompatibility = 1.5
+        parentProject.targetCompatibility = 1.6
+        childProject.sourceCompatibility = 1.6
+        childProject.targetCompatibility = 1.7
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.java.source"] == "1.5"
+        properties["sonar.java.target"] == "1.6"
+        properties["child.sonar.java.source"] == "1.6"
+        properties["child.sonar.java.target"] == "1.7"
+    }
+
+    def "adds additional default properties for 'java' projects"() {
+        parentProject.pluginManager.apply(JavaPlugin)
+
+        parentProject.sourceSets.main.java.srcDirs = ["src"]
+        parentProject.sourceSets.test.java.srcDirs = ["test"]
+        parentProject.sourceSets.main.output.classesDir = "$parentProject.buildDir/out"
+        parentProject.sourceSets.main.output.resourcesDir = "$parentProject.buildDir/out"
+        parentProject.sourceSets.main.runtimeClasspath += parentProject.files("lib/SomeLib.jar")
+
+        new TestFile(parentProject.projectDir).create {
+            src {}
+            test {}
+            buildDir {
+                out {}
+                "test-results" {}
+            }
+            lib {
+                file("SomeLib.jar")
+            }
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.sources"] == new File(parentProject.projectDir, "src") as String
+        properties["sonar.tests"] == new File(parentProject.projectDir, "test") as String
+        properties["sonar.binaries"].contains(new File(parentProject.buildDir, "out") as String)
+        properties["sonar.libraries"].contains(new File(parentProject.projectDir, "lib/SomeLib.jar") as String)
+        properties["sonar.surefire.reportsPath"] == new File(parentProject.buildDir, "test-results") as String
+        properties["sonar.junit.reportsPath"] == new File(parentProject.buildDir, "test-results") as String
+    }
+
+    def "only adds existing directories"() {
+        parentProject.pluginManager.apply(JavaPlugin)
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        !properties.containsKey("sonar.tests")
+        !properties.containsKey("sonar.binaries")
+        properties.containsKey("sonar.libraries") == (Jvm.current().getRuntimeJar() != null)
+        !properties.containsKey("sonar.surefire.reportsPath")
+        !properties.containsKey("sonar.junit.reportsPath")
+    }
+
+    def "adds empty 'sonar.sources' property if no sources exist (because Sonar Runner 2.0 always expects this property to be set)"() {
+        childProject2.pluginManager.apply(JavaPlugin)
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.sources"] == ""
+        properties["child.sonar.sources"] == ""
+        properties["child2.sonar.sources"] == ""
+        properties["child.leaf.sonar.sources"] == ""
+    }
+
+    def "allows to configure Sonar properties via 'sonarRunner' extension"() {
+        parentProject.sonarRunner.sonarProperties {
+            property "sonar.some.key", "some value"
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.some.key"] == "some value"
+    }
+
+    def "prefixes property keys of subprojects"() {
+        childProject.sonarRunner.sonarProperties {
+            property "sonar.some.key", "other value"
+        }
+        leafProject.sonarRunner.sonarProperties {
+            property "sonar.some.key", "other value"
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["child.sonar.some.key"] == "other value"
+        properties["child.leaf.sonar.some.key"] == "other value"
+    }
+
+    def "adds 'modules' properties declaring (prefixes of) subprojects"() {
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.modules"] == "child,child2"
+        properties["child.sonar.modules"] == "leaf"
+        !properties.containsKey("child2.sonar.modules")
+        !properties.containsKey("child.leaf.sonar.modules")
+    }
+
+    def "handles 'modules' properties correctly if plugin is applied to root project"() {
+        def rootProject = TestUtil.builder().withName("root").build()
+        def project = TestUtil.builder().withName("parent").withParent(rootProject).build()
+        def project2 = TestUtil.builder().withName("parent2").withParent(rootProject).build()
+        def childProject = TestUtil.builder().withName("child").withParent(project).build()
+
+        rootProject.pluginManager.apply(SonarRunnerPlugin)
+
+        when:
+        def properties = rootProject.tasks.sonarRunner.sonarProperties
+
+        then:
+        properties["sonar.modules"] == "parent,parent2"
+        properties["parent.sonar.modules"] == "child"
+        !properties.containsKey("parent2.sonar.modules")
+        !properties.containsKey("parent.child.sonar.modules")
+
+    }
+
+    def "evaluates 'sonarRunner' block lazily"() {
+        parentProject.version = "1.0"
+        parentProject.sonarRunner.sonarProperties {
+            property "sonar.projectVersion", parentProject.version
+        }
+        parentProject.version = "1.2.3"
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.projectVersion"] == "1.2.3"
+    }
+
+    def "converts Sonar property values to strings"() {
+        def object = new Object() {
+            String toString() {
+                "object"
+            }
+        }
+
+        parentProject.sonarRunner.sonarProperties {
+            property "some.object", object
+            property "some.list", [1, object, 2]
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["some.object"] == "object"
+        properties["some.list"] == "1,object,2"
+    }
+
+    def "removes Sonar properties with null values"() {
+        parentProject.sonarRunner.sonarProperties {
+            property "some.key", null
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        !properties.containsKey("some.key")
+    }
+
+    def "allows to set Sonar properties for target project via 'sonar.xyz' system properties"() {
+        System.setProperty("sonar.some.key", "some value")
+        System.setProperty("sonar.projectVersion", "3.2")
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.some.key"] == "some value"
+        properties["sonar.projectVersion"] == "3.2"
+
+        and:
+        !properties.containsKey("child.sonar.some.key")
+        properties["child.sonar.projectVersion"] == "1.3"
+    }
+
+    def "handles system properties correctly if plugin is applied to root project"() {
+        def rootProject = TestUtil.builder().withName("root").build()
+        def project = TestUtil.builder().withName("parent").withParent(rootProject).build()
+
+        rootProject.allprojects { version = 1.3 }
+        rootProject.pluginManager.apply(SonarRunnerPlugin)
+        System.setProperty("sonar.some.key", "some value")
+        System.setProperty("sonar.projectVersion", "3.2")
+
+        when:
+        def properties = rootProject.tasks.sonarRunner.sonarProperties
+
+        then:
+        properties["sonar.some.key"] == "some value"
+        properties["sonar.projectVersion"] == "3.2"
+
+        and:
+        !properties.containsKey("parent.sonar.some.key")
+        properties["parent.sonar.projectVersion"] == "1.3"
+    }
+
+    def "system properties win over values set in build script"() {
+        System.setProperty("sonar.some.key", "win")
+        parentProject.sonarRunner.sonarProperties {
+            property "sonar.some.key", "lose"
+        }
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        properties["sonar.some.key"] == "win"
+    }
+
+    def "doesn't add Sonar properties for skipped projects"() {
+        childProject.sonarRunner.skipProject = true
+
+        when:
+        def properties = parentSonarRunnerTask().sonarProperties
+
+        then:
+        !properties.any { key, value -> key.startsWith("child.sonar.") }
+    }
+
+    def "sets default fork options correctly"() {
+
+        when:
+        JavaForkOptions forkOptions = parentSonarRunnerTask().forkOptions
+        SonarRunnerRootExtension rootExtension = parentProject.extensions.sonarRunner
+
+        then:
+        forkOptions instanceof DefaultJavaForkOptions
+        rootExtension.toolVersion == '2.3'
+    }
+
+    def "root extension can configure task fork options"() {
+
+        parentProject.sonarRunner {
+            forkOptions {
+                maxHeapSize = '1024m'
+                jvmArgs '-XX:MaxPermSize=128m'
+            }
+        }
+
+        when:
+        JavaForkOptions forkOptions = parentSonarRunnerTask().forkOptions
+
+        then:
+        forkOptions.allJvmArgs.contains('-Xmx1024m')
+        forkOptions.allJvmArgs.contains('-XX:MaxPermSize=128m')
+    }
+
+    def "sonar java process is configured properly"() {
+
+        when:
+        JavaExecHandleBuilder handleBuilder = parentSonarRunnerTask().prepareExec()
+
+        then:
+        handleBuilder.main == 'org.sonar.runner.Main'
+        handleBuilder.classpath.files*.name.contains("sonar-runner-dist-2.3.jar")
+    }
+
+    private SonarRunner parentSonarRunnerTask() {
+        parentProject.tasks.sonarRunner as SonarRunner
+    }
+
+    def "can choose sonar runner version"() {
+
+        parentProject.sonarRunner {
+            toolVersion = '2.4'
+        }
+
+        when:
+        JavaExecHandleBuilder handleBuilder = parentSonarRunnerTask().prepareExec()
+
+        then:
+        handleBuilder.classpath.files*.name.contains("sonar-runner-dist-2.4.jar")
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy
new file mode 100755
index 0000000..20e8a0e
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit
+
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.TextUtil
+
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class CUnitIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def prebuiltPath = TextUtil.normaliseFileSeparators(new IntegrationTestBuildContext().getSamplesDir().file("native-binaries/cunit/libs").path)
+    def app = new CHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: "cunit"
+
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            cunit {
+                headers.srcDir "${prebuiltPath}/cunit/2.1-2/include"
+                binaries.withType(StaticLibraryBinary) {
+                    staticLibraryFile = file("${prebuiltPath}/cunit/2.1-2/lib/${cunitPlatform}/${cunitLibName}")
+                }
+            }
+        }
+    }
+    platforms {
+        x86 {
+            architecture "x86"
+        }
+    }
+}
+"""
+        settingsFile << "rootProject.name = 'test'"
+    }
+
+    private void useStandardConfig() {
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+}
+binaries.withType(CUnitTestSuiteBinarySpec) {
+    lib library: "cunit", linkage: "static"
+}
+"""
+    }
+
+    private def getCunitPlatform() {
+        if (OperatingSystem.current().isMacOsX()) {
+            return "osx"
+        }
+        if (OperatingSystem.current().isLinux()) {
+            return "linux"
+        }
+        if (toolChain.displayName == "mingw") {
+            return "mingw"
+        }
+        if (toolChain.displayName == "gcc cygwin") {
+            return "cygwin"
+        }
+        if (toolChain.visualCpp) {
+            def vcVersion = (toolChain as AvailableToolChains.InstalledVisualCpp).version
+            switch (vcVersion.major) {
+                case "12":
+                    return "vs2013"
+                case "10":
+                    return "vs2010"
+            }
+        }
+        throw new IllegalStateException("No cunit binary available for ${toolChain.displayName}")
+    }
+
+    private def getCunitLibName() {
+        return OperatingSystem.current().getStaticLibraryName("cunit")
+    }
+
+    def "can build and run cunit test suite"() {
+        given:
+        useConventionalSourceLocations()
+        useStandardConfig()
+
+        when:
+        run "check"
+
+        then:
+        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestC",
+                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+
+        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
+        testResults.suiteNames == ['hello test']
+        testResults.suites['hello test'].passingTests == ['test_sum']
+        testResults.suites['hello test'].failingTests == []
+        testResults.checkTestCases(1, 1, 0)
+        testResults.checkAssertions(3, 3, 0)
+    }
+
+    def "can configure via testSuite component"() {
+        given:
+        useConventionalSourceLocations()
+
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+    testSuites {
+        helloTest {
+            binaries.all {
+                lib library: "cunit", linkage: "static"
+            }
+        }
+    }
+}
+"""
+
+        when:
+        run "check"
+
+        then:
+        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestC",
+                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+
+        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
+        testResults.suiteNames == ['hello test']
+        testResults.suites['hello test'].passingTests == ['test_sum']
+        testResults.suites['hello test'].failingTests == []
+        testResults.checkTestCases(1, 1, 0)
+        testResults.checkAssertions(3, 3, 0)
+    }
+
+    def "can supply cCompiler macro to cunit sources"() {
+        given:
+        useConventionalSourceLocations()
+        useStandardConfig()
+
+        when:
+        buildFile << """
+binaries.withType(CUnitTestSuiteBinarySpec) {
+    cCompiler.define "ONE_TEST"
+}
+"""
+        and:
+        run "runHelloTestCUnitExe"
+
+        then:
+        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
+        testResults.checkAssertions(1, 1, 0)
+    }
+
+    def "can configure location of cunit test sources"() {
+        given:
+        useStandardConfig()
+        app.library.writeSources(file("src/hello"))
+        app.cunitTests.writeSources(file("src/alternateHelloTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            sources {
+                // TODO:DAZ Should not need type here (source set should already be created)
+                c(CSourceSet) {
+                    source.srcDir "src/alternateHelloTest/c"
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "check"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "can configure location of cunit test sources before component is declared"() {
+        given:
+        app.library.writeSources(file("src/hello"))
+        app.cunitTests.writeSources(file("src/alternateHelloTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            sources {
+                // TODO:DAZ Should not need type here (source set should already be created)
+                c(CSourceSet) {
+                    source.srcDir "src/alternateHelloTest/c"
+                }
+            }
+        }
+    }
+}
+"""
+        useStandardConfig()
+
+        then:
+        succeeds "check"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "variant-dependent sources are included in test binary"() {
+        given:
+        app.library.headerFiles*.writeToDir(file("src/hello"))
+        app.cunitTests.writeSources(file("src/helloTest"))
+        app.library.sourceFiles*.writeToDir(file("src/variant"))
+
+        when:
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+            binaries.all {
+                sources {
+                    variant(CSourceSet) {
+                        source.srcDir "src/variant/c"
+                    }
+                }
+            }
+        }
+    }
+}
+binaries.withType(CUnitTestSuiteBinarySpec) {
+    lib library: "cunit", linkage: "static"
+}
+"""
+
+        then:
+        succeeds "check"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "can configure variant-dependent test sources"() {
+        given:
+        useStandardConfig()
+        app.library.writeSources(file("src/hello"))
+        app.cunitTests.writeSources(file("src/variantTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            binaries.all {
+                sources {
+                    variant(CSourceSet) {
+                        source.srcDir "src/variantTest/c"
+                        lib sources.c
+                        lib sources.cunitLauncher
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "check"
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "test suite skipped after successful run"() {
+        given:
+        useStandardConfig()
+        useConventionalSourceLocations()
+        run "runHelloTestCUnitExe"
+
+        when:
+        run "runHelloTestCUnitExe"
+
+        then:
+        skipped ":helloTestCUnitExe", ":runHelloTestCUnitExe"
+    }
+
+    def "can build and run cunit failing test suite"() {
+        when:
+        useStandardConfig()
+        useFailingTestSources()
+        fails "runHelloTestCUnitExe"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':runHelloTestCUnitExe'.")
+        failure.assertHasCause("There were failing tests. See the results at: ")
+
+        and:
+        executedAndNotSkipped ":compileHelloTestCUnitExeHelloC", ":compileHelloTestCUnitExeHelloTestC",
+                              ":linkHelloTestCUnitExe", ":helloTestCUnitExe", ":runHelloTestCUnitExe"
+        output.contains """
+There were test failures:
+"""
+        and:
+        def testResults = new CUnitTestResults(file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml"))
+        testResults.suiteNames == ['hello test']
+        testResults.suites['hello test'].passingTests == []
+        testResults.suites['hello test'].failingTests == ['test_sum']
+        testResults.checkTestCases(1, 0, 1)
+        testResults.checkAssertions(3, 1, 2)
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "build does not break for failing tests if ignoreFailures is true"() {
+        when:
+        useStandardConfig()
+        useFailingTestSources()
+        buildFile << """
+tasks.withType(RunTestExecutable) {
+    it.ignoreFailures = true
+}
+"""
+        succeeds "runHelloTestCUnitExe"
+
+        then:
+        output.contains """
+There were test failures:
+"""
+        output.contains "There were failing tests. See the results at: "
+
+        and:
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Results.xml").assertExists()
+        file("build/test-results/helloTestCUnitExe/CUnitAutomated-Listing.xml").assertExists()
+    }
+
+    def "test suite not skipped after failing run"() {
+        given:
+        useStandardConfig()
+        useFailingTestSources()
+        fails "runHelloTestCUnitExe"
+
+        when:
+        fails "runHelloTestCUnitExe"
+
+        then:
+        executedAndNotSkipped ":runHelloTestCUnitExe"
+    }
+
+    def "creates visual studio solution and project for cunit test suite"() {
+        given:
+        useStandardConfig()
+        useConventionalSourceLocations()
+        buildFile.text = "apply plugin: 'visual-studio'\n" + buildFile.text
+
+        when:
+        succeeds "helloTestVisualStudio"
+
+        then:
+        final mainSolution = new SolutionFile(file("helloTestExe.sln"))
+        mainSolution.assertHasProjects("helloTestExe")
+
+        and:
+        final projectFile = new ProjectFile(file("helloTestExe.vcxproj"))
+        projectFile.sourceFiles as Set == [
+                "build.gradle",
+                "build/src/helloTest/cunitLauncher/c/gradle_cunit_main.c",
+                "src/helloTest/c/test.c",
+                "src/hello/c/hello.c",
+                "src/hello/c/sum.c"
+        ] as Set
+        projectFile.headerFiles == [
+                "build/src/helloTest/cunitLauncher/headers/gradle_cunit_register.h",
+                "src/hello/headers/hello.h"
+        ]
+        projectFile.projectConfigurations.keySet() == ['debug'] as Set
+        with (projectFile.projectConfigurations['debug']) {
+            includePath == "src/helloTest/headers;build/src/helloTest/cunitLauncher/headers;src/hello/headers;${prebuiltPath}/cunit/2.1-2/include"
+        }
+    }
+
+    private useConventionalSourceLocations() {
+        app.library.writeSources(file("src/hello"))
+        app.cunitTests.writeSources(file("src/helloTest"))
+    }
+
+    private useFailingTestSources() {
+        useConventionalSourceLocations()
+        file("src/hello/c/sum.c").text = file("src/hello/c/sum.c").text.replace("return a + b;", "return 2;")
+    }
+
+    @Override
+    String getOutput() {
+        return normaliseLineSeparators(super.getOutput())
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..a1eb333
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativeplatform.test.cunit
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class CUnitSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule public final Sample cunit = sample(temporaryFolder, 'cunit')
+
+    private static Sample sample(TestDirectoryProvider testDirectoryProvider, String name) {
+        return new Sample(testDirectoryProvider, "native-binaries/${name}", name)
+    }
+
+    def "cunit components"() {
+        given:
+        sample cunit
+
+        when:
+        succeeds "components"
+
+        then:
+        output.contains "C unit exe 'operatorsTest:failing:cUnitExe'"
+        output.contains "C unit exe 'operatorsTest:passing:cUnitExe'"
+    }
+
+    def "cunit"() {
+        given:
+        // CUnit prebuilt library only works for VS2010 on windows
+        if (OperatingSystem.current().windows && !isVisualCpp2010()) {
+            return
+        }
+
+        when:
+        sample cunit
+        succeeds "runPassing"
+
+        then:
+        executedAndNotSkipped ":operatorsTestCUnitLauncher",
+                              ":compilePassingOperatorsTestCUnitExeOperatorsTestC", ":compilePassingOperatorsTestCUnitExeOperatorsTestCunitLauncher",
+                              ":linkPassingOperatorsTestCUnitExe", ":passingOperatorsTestCUnitExe",
+                              ":installPassingOperatorsTestCUnitExe", ":runPassingOperatorsTestCUnitExe"
+
+        and:
+        def passingResults = new CUnitTestResults(cunit.dir.file("build/test-results/operatorsTestCUnitExe/passing/CUnitAutomated-Results.xml"))
+        passingResults.suiteNames == ['operator tests']
+        passingResults.suites['operator tests'].passingTests == ['test_plus', 'test_minus']
+        passingResults.suites['operator tests'].failingTests == []
+        passingResults.checkTestCases(2, 2, 0)
+        passingResults.checkAssertions(6, 6, 0)
+
+        when:
+        sample cunit
+        fails "runFailing"
+
+        then:
+        skipped ":operatorsTestCUnitLauncher"
+        executedAndNotSkipped ":compileFailingOperatorsTestCUnitExeOperatorsTestC", ":compileFailingOperatorsTestCUnitExeOperatorsTestCunitLauncher",
+                              ":linkFailingOperatorsTestCUnitExe", ":failingOperatorsTestCUnitExe",
+                              ":installFailingOperatorsTestCUnitExe", ":runFailingOperatorsTestCUnitExe"
+
+        and:
+        def failingResults = new CUnitTestResults(cunit.dir.file("build/test-results/operatorsTestCUnitExe/failing/CUnitAutomated-Results.xml"))
+        failingResults.suiteNames == ['operator tests']
+        failingResults.suites['operator tests'].passingTests == ['test_minus']
+        failingResults.suites['operator tests'].failingTests == ['test_plus']
+        failingResults.checkTestCases(2, 1, 1)
+        failingResults.checkAssertions(6, 4, 2)
+    }
+
+    private static boolean isVisualCpp2010() {
+        return (AbstractInstalledToolChainIntegrationSpec.toolChain.visualCpp && (AbstractInstalledToolChainIntegrationSpec.toolChain as AvailableToolChains.InstalledVisualCpp).version.major == "10")
+    }
+}
\ No newline at end of file
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitTestResults.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitTestResults.groovy
new file mode 100644
index 0000000..cbe3b3b
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitTestResults.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit
+
+import org.gradle.test.fixtures.file.TestFile
+
+class CUnitTestResults {
+    TestFile testResultsFile
+    Node resultsNode
+    Map<String, Suite> suites = [:]
+    Map<String, SummaryRecord> summaryRecords = [:]
+
+    CUnitTestResults(TestFile testResultsFile) {
+        assert testResultsFile.exists()
+        this.testResultsFile = testResultsFile
+        final XmlParser parser = new XmlParser(false, false)
+        parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+        parser.setFeature("http://xml.org/sax/features/namespaces", false)
+        parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
+        this.resultsNode = parser.parse(testResultsFile)
+
+        resultsNode.CUNIT_RESULT_LISTING.CUNIT_RUN_SUITE.CUNIT_RUN_SUITE_SUCCESS.each { Node suiteNode ->
+            def suite = new Suite(suiteNode)
+            suites.put(suite.name, suite)
+        }
+
+        summaryRecords['Suites'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Suites'}) as Node)
+        summaryRecords['Test Cases'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Test Cases'}) as Node)
+        summaryRecords['Assertions'] = new SummaryRecord(resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text() == 'Assertions'}) as Node)
+    }
+
+    List<String> getSuiteNames() {
+        suites.keySet() as List
+    }
+
+    def checkTestCases(int total, int succeeded, int failed) {
+        checkSummaryRecord('Test Cases', total, succeeded, failed)
+    }
+
+    def checkAssertions(int total, int succeeded, int failed) {
+        checkSummaryRecord('Assertions', total, succeeded, failed)
+    }
+
+    private def checkSummaryRecord(String name, int total, int succeeded, int failed) {
+        def recordNode = resultsNode.CUNIT_RUN_SUMMARY.CUNIT_RUN_SUMMARY_RECORD.find({it.TYPE.text().trim() == name}) as Node
+        assert recordNode.RUN.text() as int == total
+        assert recordNode.SUCCEEDED.text() as int == succeeded
+        assert recordNode.FAILED.text() as int == failed
+        return true
+    }
+
+    class Suite {
+        final Node suiteNode
+
+        Suite(Node suiteNode) {
+            this.suiteNode = suiteNode
+        }
+
+        String getName() {
+            return suiteNode.SUITE_NAME.text().trim()
+        }
+
+        List<String> getPassingTests() {
+            suiteNode.CUNIT_RUN_TEST_RECORD.CUNIT_RUN_TEST_SUCCESS.TEST_NAME*.text()*.trim()
+        }
+
+        List<String> getFailingTests() {
+            suiteNode.CUNIT_RUN_TEST_RECORD.CUNIT_RUN_TEST_FAILURE.TEST_NAME*.text()*.trim().unique()
+        }
+    }
+
+    class SummaryRecord {
+        final Node recordNode
+
+        SummaryRecord(Node recordNode) {
+            this.recordNode = recordNode
+        }
+
+        int getRun() {
+            recordNode.RUN.text() as int
+        }
+
+        int getSucceeded() {
+            recordNode.SUCCEEDED.text() as int
+        }
+
+        int getFailed() {
+            recordNode.FAILED.text() as int
+        }
+
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/ComponentReportIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..e7988f6
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit
+
+import org.gradle.api.reporting.components.AbstractComponentReportIntegrationTest
+import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private String currentNative = NativePlatformsTestFixture.defaultPlatformName
+
+    def "shows details of native C executable with test suite"() {
+        given:
+        buildFile << """
+plugins {
+    id 'c'
+    id 'cunit'
+}
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        someExe(NativeExecutableSpec)
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+Native executable 'someExe'
+---------------------------
+
+Source sets
+    C source 'someExe:c'
+        src/someExe/c
+
+Binaries
+    Executable 'someExe:executable'
+        build using task: :someExeExecutable
+        install using task: :installSomeExeExecutable
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        executable file: build/binaries/someExeExecutable/someExe
+
+Cunit test suite 'someExeTest'
+------------------------------
+
+Source sets
+    C source 'someExeTest:c'
+        src/someExeTest/c
+    C source 'someExeTest:cunitLauncher'
+        build/src/someExeTest/cunitLauncher/c
+
+Binaries
+    C unit exe 'someExeTest:cUnitExe'
+        build using task: :someExeTestCUnitExe
+        run using task: :runSomeExeTestCUnitExe
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        executable file: build/binaries/someExeTestCUnitExe/someExeTest
+"""
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/plugins/CUnitPluginIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/plugins/CUnitPluginIntegrationTest.groovy
new file mode 100644
index 0000000..5b77f76
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/plugins/CUnitPluginIntegrationTest.groovy
@@ -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.nativeplatform.test.cunit.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class CUnitPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        return "cunit"
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/ComponentReportIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/ComponentReportIntegrationTest.groovy
new file mode 100644
index 0000000..9c40971
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/ComponentReportIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.googletest
+
+import org.gradle.api.reporting.components.AbstractComponentReportIntegrationTest
+import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
+
+class ComponentReportIntegrationTest extends AbstractComponentReportIntegrationTest {
+    private String currentNative = NativePlatformsTestFixture.defaultPlatformName
+
+    def "shows details of native C++ executable with test suite"() {
+        given:
+        buildFile << """
+plugins {
+    id 'cpp'
+    id 'google-test'
+}
+
+model {
+    toolChains {
+        ${toolChain.buildScriptConfig}
+    }
+    components {
+        someExe(NativeExecutableSpec)
+    }
+}
+"""
+        when:
+        succeeds "components"
+
+        then:
+        outputMatches output, """
+Native executable 'someExe'
+---------------------------
+
+Source sets
+    C++ source 'someExe:cpp'
+        src/someExe/cpp
+
+Binaries
+    Executable 'someExe:executable'
+        build using task: :someExeExecutable
+        install using task: :installSomeExeExecutable
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        executable file: build/binaries/someExeExecutable/someExe
+
+GoogleTest test suite 'someExeTest'
+-----------------------------------
+
+Source sets
+    C++ source 'someExeTest:cpp'
+        src/someExeTest/cpp
+
+Binaries
+    Google test exe 'someExeTest:googleTestExe'
+        build using task: :someExeTestGoogleTestExe
+        run using task: :runSomeExeTestGoogleTestExe
+        platform: $currentNative
+        build type: debug
+        flavor: default
+        tool chain: Tool chain 'clang' (Clang)
+        executable file: build/binaries/someExeTestGoogleTestExe/someExeTest
+"""
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy
new file mode 100755
index 0000000..0ef7952
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.googletest
+
+import org.gradle.ide.visualstudio.fixtures.ProjectFile
+import org.gradle.ide.visualstudio.fixtures.SolutionFile
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.gradle.util.TextUtil
+
+import static org.gradle.util.TextUtil.normaliseLineSeparators
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class GoogleTestIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+
+    def prebuiltPath = TextUtil.normaliseFileSeparators(new IntegrationTestBuildContext().getSamplesDir().file("native-binaries/google-test/libs").path)
+    def app = new CppHelloWorldApp()
+
+    def setup() {
+        buildFile << """
+apply plugin: "google-test"
+
+model {
+    repositories {
+        libs(PrebuiltLibraries) {
+            googleTest {
+                headers.srcDir "${prebuiltPath}/googleTest/1.7.0/include"
+                binaries.withType(StaticLibraryBinary) {
+                    staticLibraryFile = file("${prebuiltPath}/googleTest/1.7.0/lib/${googleTestPlatform}/${googleTestLib}")
+                }
+            }
+        }
+    }
+    platforms {
+        x86 {
+            architecture "x86"
+        }
+    }
+}
+"""
+        settingsFile << "rootProject.name = 'test'"
+    }
+
+    private void useStandardConfig() {
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+}
+binaries.withType(GoogleTestTestSuiteBinarySpec) {
+    lib library: "googleTest", linkage: "static"
+}
+
+tasks.withType(RunTestExecutable) {
+    args "--gtest_output=xml:test_detail.xml"
+}
+"""
+    }
+
+    private def getGoogleTestPlatform() {
+        if (OperatingSystem.current().isMacOsX()) {
+            return "osx"
+        }
+        if (OperatingSystem.current().isLinux()) {
+            return "linux"
+        }
+        if (toolChain.displayName == "mingw") {
+            return "mingw"
+        }
+        if (toolChain.displayName == "gcc cygwin") {
+            return "cygwin"
+        }
+        if (toolChain.visualCpp) {
+            def vcVersion = (toolChain as AvailableToolChains.InstalledVisualCpp).version
+            switch (vcVersion.major) {
+                case "12":
+                    return "vs2013"
+                case "10":
+                    return "vs2010"
+            }
+        }
+        throw new IllegalStateException("No googletest binary available for ${toolChain.displayName}")
+    }
+
+    private def getGoogleTestLib() {
+        return OperatingSystem.current().getStaticLibraryName("gtest")
+    }
+
+    def "can build and run googleTest test suite"() {
+        given:
+        useConventionalSourceLocations()
+        useStandardConfig()
+
+        when:
+        run "runHelloTestGoogleTestExe"
+
+        then:
+        executedAndNotSkipped ":compileHelloTestGoogleTestExeHelloCpp", ":compileHelloTestGoogleTestExeHelloTestCpp",
+                ":linkHelloTestGoogleTestExe", ":helloTestGoogleTestExe", ":runHelloTestGoogleTestExe"
+
+        def testResults = new GoogleTestTestResults(file("build/test-results/helloTestGoogleTestExe/test_detail.xml"))
+        testResults.suiteNames == ['HelloTest']
+        testResults.suites['HelloTest'].passingTests == ['test_sum']
+        testResults.suites['HelloTest'].failingTests == []
+        testResults.checkTestCases(1, 1, 0)
+    }
+
+    def "can configure via testSuite component"() {
+        given:
+        useConventionalSourceLocations()
+
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+        }
+    }
+    testSuites {
+        helloTest {
+            binaries.all {
+                lib library: "googleTest", linkage: "static"
+            }
+        }
+    }
+}
+
+tasks.withType(RunTestExecutable) {
+    args "--gtest_output=xml:test_detail.xml"
+}
+"""
+
+        when:
+        run "runHelloTestGoogleTestExe"
+
+        then:
+        executedAndNotSkipped ":compileHelloTestGoogleTestExeHelloCpp", ":compileHelloTestGoogleTestExeHelloTestCpp",
+                ":linkHelloTestGoogleTestExe", ":helloTestGoogleTestExe", ":runHelloTestGoogleTestExe"
+
+        def testResults = new GoogleTestTestResults(file("build/test-results/helloTestGoogleTestExe/test_detail.xml"))
+        testResults.suiteNames == ['HelloTest']
+        testResults.suites['HelloTest'].passingTests == ['test_sum']
+        testResults.suites['HelloTest'].failingTests == []
+        testResults.checkTestCases(1, 1, 0)
+    }
+
+    def "can supply cppCompiler macro to googleTest sources"() {
+        given:
+        useConventionalSourceLocations()
+        useStandardConfig()
+
+        when:
+        buildFile << """
+binaries.withType(GoogleTestTestSuiteBinarySpec) {
+    cppCompiler.define "ONE_TEST"
+}
+"""
+        and:
+        run "runHelloTestGoogleTestExe"
+
+        then:
+        def testResults = new GoogleTestTestResults(file("build/test-results/helloTestGoogleTestExe/test_detail.xml"))
+        testResults.checkTestCases(1, 1, 0)
+    }
+
+    def "can configure location of googleTest test sources"() {
+        given:
+        useStandardConfig()
+        app.library.writeSources(file("src/hello"))
+        app.googleTestTests.writeSources(file("src/alternateHelloTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            sources {
+                // TODO:DAZ Should not need type here (source set should already be created)
+                cpp(CppSourceSet) {
+                    source.srcDir "src/alternateHelloTest/cpp"
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "runHelloTestGoogleTestExe"
+    }
+
+    def "can configure location of googleTest test sources before component is declared"() {
+        given:
+        app.library.writeSources(file("src/hello"))
+        app.googleTestTests.writeSources(file("src/alternateHelloTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            sources {
+                // TODO:DAZ Should not need type here (source set should already be created)
+                cpp(CppSourceSet) {
+                    source.srcDir "src/alternateHelloTest/cpp"
+                }
+            }
+        }
+    }
+}
+"""
+        useStandardConfig()
+
+        then:
+        succeeds "runHelloTestGoogleTestExe"
+    }
+
+    def "variant-dependent sources are included in test binary"() {
+        given:
+        app.library.headerFiles*.writeToDir(file("src/hello"))
+        app.googleTestTests.writeSources(file("src/helloTest"))
+        app.library.sourceFiles*.writeToDir(file("src/variant"))
+
+        when:
+        buildFile << """
+model {
+    components {
+        hello(NativeLibrarySpec) {
+            targetPlatform "x86"
+            binaries.all {
+                sources {
+                    variant(CppSourceSet) {
+                        source.srcDir "src/variant/cpp"
+                    }
+                }
+            }
+        }
+    }
+}
+binaries.withType(GoogleTestTestSuiteBinarySpec) {
+    lib library: "googleTest", linkage: "static"
+}
+"""
+
+        then:
+        succeeds "runHelloTestGoogleTestExe"
+    }
+
+    def "can configure variant-dependent test sources"() {
+        given:
+        useStandardConfig()
+        app.library.writeSources(file("src/hello"))
+        app.googleTestTests.writeSources(file("src/variantTest"))
+
+        when:
+        buildFile << """
+model {
+    testSuites {
+        helloTest {
+            binaries.all {
+                sources {
+                    variant(CppSourceSet) {
+                        source.srcDir "src/variantTest/cpp"
+                        lib sources.cpp
+                    }
+                }
+            }
+        }
+    }
+}
+"""
+
+        then:
+        succeeds "runHelloTestGoogleTestExe"
+    }
+
+    def "test suite skipped after successful run"() {
+        given:
+        useStandardConfig()
+        useConventionalSourceLocations()
+        run "runHelloTestGoogleTestExe"
+
+        when:
+        run "runHelloTestGoogleTestExe"
+
+        then:
+        skipped ":helloTestGoogleTestExe", ":runHelloTestGoogleTestExe"
+    }
+
+    def "can build and run googleTest failing test suite"() {
+        when:
+        useStandardConfig()
+        useFailingTestSources()
+        fails "runHelloTestGoogleTestExe"
+
+        then:
+        failure.assertHasDescription("Execution failed for task ':runHelloTestGoogleTestExe'.")
+        failure.assertHasCause("There were failing tests. See the results at: ")
+
+        and:
+        executedAndNotSkipped ":compileHelloTestGoogleTestExeHelloCpp", ":compileHelloTestGoogleTestExeHelloTestCpp",
+                ":linkHelloTestGoogleTestExe", ":helloTestGoogleTestExe", ":runHelloTestGoogleTestExe"
+        output.contains "[  FAILED  ]"
+        and:
+        def testResults = new GoogleTestTestResults(file("build/test-results/helloTestGoogleTestExe/test_detail.xml"))
+        testResults.suiteNames == ['HelloTest']
+        testResults.suites['HelloTest'].passingTests == []
+        testResults.suites['HelloTest'].failingTests == ['test_sum']
+        testResults.checkTestCases(1, 0, 1)
+    }
+
+    def "build does not break for failing tests if ignoreFailures is true"() {
+        when:
+        useStandardConfig()
+        useFailingTestSources()
+        buildFile << """
+tasks.withType(RunTestExecutable) {
+    it.ignoreFailures = true
+}
+"""
+        succeeds "runHelloTestGoogleTestExe"
+
+        then:
+        output.contains "[  FAILED  ] "
+        output.contains "There were failing tests. See the results at: "
+
+        and:
+        file("build/test-results/helloTestGoogleTestExe/test_detail.xml").assertExists()
+    }
+
+    def "test suite not skipped after failing run"() {
+        given:
+        useStandardConfig()
+        useFailingTestSources()
+        fails "runHelloTestGoogleTestExe"
+
+        when:
+        fails "runHelloTestGoogleTestExe"
+
+        then:
+        executedAndNotSkipped ":runHelloTestGoogleTestExe"
+    }
+
+    def "creates visual studio solution and project for googleTest test suite"() {
+        given:
+        useStandardConfig()
+        useConventionalSourceLocations()
+        buildFile.text = "apply plugin: 'visual-studio'\n" + buildFile.text
+
+        when:
+        succeeds "helloTestVisualStudio"
+
+        then:
+        final mainSolution = new SolutionFile(file("helloTestExe.sln"))
+        mainSolution.assertHasProjects("helloTestExe")
+
+        and:
+        final projectFile = new ProjectFile(file("helloTestExe.vcxproj"))
+        projectFile.sourceFiles as Set == [
+                "build.gradle",
+                "src/helloTest/cpp/test.cpp",
+                "src/hello/cpp/hello.cpp",
+                "src/hello/cpp/sum.cpp"
+        ] as Set
+        projectFile.headerFiles == [
+                "src/hello/headers/hello.h"
+        ]
+        projectFile.projectConfigurations.keySet() == ['debug'] as Set
+        with (projectFile.projectConfigurations['debug']) {
+            includePath == "src/helloTest/headers;src/hello/headers;${prebuiltPath}/googleTest/1.7.0/include"
+        }
+    }
+
+    private useConventionalSourceLocations() {
+        app.library.writeSources(file("src/hello"))
+        app.googleTestTests.writeSources(file("src/helloTest"))
+    }
+
+    private useFailingTestSources() {
+        useConventionalSourceLocations()
+        file("src/hello/cpp/sum.cpp").text = file("src/hello/cpp/sum.cpp").text.replace("return a + b;", "return 2;")
+    }
+
+    @Override
+    String getOutput() {
+        return normaliseLineSeparators(super.getOutput())
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..09241fb
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.nativeplatform.test.googletest
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
+import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Rule
+
+ at Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+class GoogleTestSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
+    @Rule public final Sample googleTest = sample(temporaryFolder, 'google-test')
+
+    private static Sample sample(TestDirectoryProvider testDirectoryProvider, String name) {
+        return new Sample(testDirectoryProvider, "native-binaries/${name}", name)
+    }
+
+    def "googleTest"() {
+        given:
+        // GoogleTest sample only works out of the box with VS2013 on windows
+        if (OperatingSystem.current().windows && !isVisualCpp2013()) {
+            return
+        }
+        sample googleTest
+
+        when:
+        succeeds "runPassing"
+
+        then:
+        executedAndNotSkipped ":compilePassingOperatorsTestGoogleTestExeOperatorsTestCpp",
+                ":linkPassingOperatorsTestGoogleTestExe", ":passingOperatorsTestGoogleTestExe",
+                ":installPassingOperatorsTestGoogleTestExe", ":runPassingOperatorsTestGoogleTestExe"
+
+        and:
+        def passingResults = new GoogleTestTestResults(googleTest.dir.file("build/test-results/operatorsTestGoogleTestExe/passing/test_detail.xml"))
+        passingResults.suiteNames == ['OperatorTests']
+        passingResults.suites['OperatorTests'].passingTests == ['test_minus', 'test_plus']
+        passingResults.suites['OperatorTests'].failingTests == []
+        passingResults.checkTestCases(2, 2, 0)
+
+        when:
+        sample googleTest
+        fails "runFailing"
+
+        then:
+        executedAndNotSkipped ":compileFailingOperatorsTestGoogleTestExeOperatorsTestCpp",
+                ":linkFailingOperatorsTestGoogleTestExe", ":failingOperatorsTestGoogleTestExe",
+                ":installFailingOperatorsTestGoogleTestExe", ":runFailingOperatorsTestGoogleTestExe"
+
+        and:
+        def failingResults = new GoogleTestTestResults(googleTest.dir.file("build/test-results/operatorsTestGoogleTestExe/failing/test_detail.xml"))
+        failingResults.suiteNames == ['OperatorTests']
+        failingResults.suites['OperatorTests'].passingTests == ['test_minus']
+        failingResults.suites['OperatorTests'].failingTests == ['test_plus']
+        failingResults.checkTestCases(2, 1, 1)
+    }
+
+    private static boolean isVisualCpp2013() {
+        return (AbstractInstalledToolChainIntegrationSpec.toolChain.visualCpp && (AbstractInstalledToolChainIntegrationSpec.toolChain as AvailableToolChains.InstalledVisualCpp).version.major == "12")
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTestResults.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTestResults.groovy
new file mode 100644
index 0000000..aff724d
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTestResults.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.googletest
+
+import org.gradle.test.fixtures.file.TestFile
+
+class GoogleTestTestResults {
+    TestFile testResultsFile
+    Node resultsNode
+    Map<String, Suite> suites = [:]
+
+    GoogleTestTestResults(TestFile testResultsFile) {
+        assert testResultsFile.exists()
+        this.testResultsFile = testResultsFile
+        final XmlParser parser = new XmlParser(false, false)
+        parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+        parser.setFeature("http://xml.org/sax/features/namespaces", false)
+        parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
+        this.resultsNode = parser.parse(testResultsFile)
+
+        resultsNode.testsuite.each { Node suiteNode ->
+            def suite = new Suite(suiteNode)
+            suites.put(suite.name, suite)
+        }
+    }
+
+    List<String> getSuiteNames() {
+        suites.keySet() as List
+    }
+
+    def checkTestCases(int total, int succeeded, int failed) {
+        def tests = resultsNode. at tests as int
+        def disabled = resultsNode. at disabled as int
+        def testFailed = resultsNode. at failures as int
+        def run = tests - disabled
+        def testSucceeded = tests - failed
+        assert run == total
+        assert testSucceeded == succeeded
+        assert testFailed == failed
+        return true
+    }
+
+    class Suite {
+        final Node suiteNode
+
+        Suite(Node suiteNode) {
+            this.suiteNode = suiteNode
+        }
+
+        String getName() {
+            return suiteNode. at name
+        }
+
+        List<String> getPassingTests() {
+            suiteNode.testcase.findAll({ it.failure.size() == 0 })*. at name.sort()
+        }
+
+        List<String> getFailingTests() {
+            suiteNode.testcase.findAll({ it.failure.size() != 0 })*. at name.sort()
+        }
+    }
+}
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPluginIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPluginIntegrationTest.groovy
new file mode 100644
index 0000000..757d0bb
--- /dev/null
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPluginIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.googletest.plugins
+
+import org.gradle.integtests.fixtures.WellBehavedPluginTest
+
+class GoogleTestPluginIntegrationTest extends WellBehavedPluginTest {
+    @Override
+    String getPluginName() {
+        return "google-test"
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteBinarySpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteBinarySpec.java
new file mode 100644
index 0000000..d9c4242
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteBinarySpec.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.cunit;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+
+/**
+ * An executable which run a CUnit test suite.
+ */
+ at Incubating
+public interface CUnitTestSuiteBinarySpec extends NativeTestSuiteBinarySpec {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    CUnitTestSuiteSpec getTestSuite();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    CUnitTestSuiteSpec getComponent();
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteSpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteSpec.java
new file mode 100644
index 0000000..e342140
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/CUnitTestSuiteSpec.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.test.NativeTestSuiteSpec;
+
+/**
+ * Test suite of CUnit tests.
+ */
+ at Incubating
+public interface CUnitTestSuiteSpec extends NativeTestSuiteSpec {
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteBinary.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteBinary.java
new file mode 100644
index 0000000..383bdd6
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteBinary.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.cunit.internal;
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec;
+import org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec;
+import org.gradle.nativeplatform.test.internal.DefaultNativeTestSuiteBinarySpec;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+
+public class DefaultCUnitTestSuiteBinary extends DefaultNativeTestSuiteBinarySpec implements CUnitTestSuiteBinarySpec {
+
+    public static DefaultCUnitTestSuiteBinary create(NativeComponentSpec owner, NativeBinarySpecInternal testedBinary, BinaryNamingScheme namingScheme, NativeDependencyResolver resolver, Instantiator instantiator, ITaskFactory taskFactory) {
+        DefaultCUnitTestSuiteBinary spec = BaseBinarySpec.create(DefaultCUnitTestSuiteBinary.class, namingScheme.getLifecycleTaskName(), instantiator, taskFactory);
+        spec.setComponent(owner);
+        spec.setTestedBinary(testedBinary);
+        spec.setNamingScheme(namingScheme);
+        spec.setResolver(resolver);
+        return spec;
+    }
+
+    @Override
+    public CUnitTestSuiteSpec getTestSuite() {
+        return getComponent();
+    }
+
+    @Override
+    public CUnitTestSuiteSpec getComponent() {
+        return (CUnitTestSuiteSpec) super.getComponent();
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteSpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteSpec.java
new file mode 100644
index 0000000..52f3ba7
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/internal/DefaultCUnitTestSuiteSpec.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit.internal;
+
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.AbstractNativeComponentSpec;
+import org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+
+public class DefaultCUnitTestSuiteSpec extends AbstractNativeComponentSpec implements CUnitTestSuiteSpec, ComponentSpecInternal {
+    private NativeComponentSpec testedComponent;
+
+    public String getDisplayName() {
+        return String.format("cunit test suite '%s'", getName());
+    }
+
+    public NativeComponentSpec getTestedComponent() {
+        return testedComponent;
+    }
+
+    public void setTestedComponent(NativeComponentSpec testedComponent) {
+        this.testedComponent = testedComponent;
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/package-info.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/package-info.java
new file mode 100644
index 0000000..f6b219f
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/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 cunit integration.
+ */
+package org.gradle.nativeplatform.test.cunit;
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/plugins/CUnitPlugin.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/plugins/CUnitPlugin.java
new file mode 100644
index 0000000..1b850e7
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/plugins/CUnitPlugin.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.cunit.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.c.internal.DefaultCSourceSet;
+import org.gradle.language.c.plugins.CLangPlugin;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.model.*;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.SharedLibraryBinary;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.test.cunit.CUnitTestSuiteSpec;
+import org.gradle.nativeplatform.test.cunit.internal.DefaultCUnitTestSuiteBinary;
+import org.gradle.nativeplatform.test.cunit.internal.DefaultCUnitTestSuiteSpec;
+import org.gradle.nativeplatform.test.cunit.tasks.GenerateCUnitLauncher;
+import org.gradle.nativeplatform.test.plugins.NativeBinariesTestPlugin;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecIdentifier;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.platform.base.internal.DefaultBinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier;
+import org.gradle.platform.base.test.TestSuiteContainer;
+
+import java.io.File;
+
+/**
+ * A plugin that sets up the infrastructure for testing native binaries with CUnit.
+ */
+ at Incubating
+public class CUnitPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.getPluginManager().apply(NativeBinariesTestPlugin.class);
+        project.getPluginManager().apply(CLangPlugin.class);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+
+        private static final String CUNIT_LAUNCHER_SOURCE_SET = "cunitLauncher";
+
+        // TODO:DAZ Test suites should belong to ComponentSpecContainer, and we could rely on more conventions from the base plugins
+        @Defaults
+        public void createCUnitTestSuitePerComponent(TestSuiteContainer testSuites, NamedDomainObjectSet<NativeComponentSpec> components, ProjectSourceSet projectSourceSet, ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            for (NativeComponentSpec component : components) {
+                testSuites.add(createCUnitTestSuite(component, instantiator, projectSourceSet, fileResolver));
+            }
+        }
+
+        private CUnitTestSuiteSpec createCUnitTestSuite(final NativeComponentSpec testedComponent, Instantiator instantiator, ProjectSourceSet projectSourceSet, FileResolver fileResolver) {
+            String suiteName = String.format("%sTest", testedComponent.getName());
+            String path = testedComponent.getProjectPath();
+            ComponentSpecIdentifier id = new DefaultComponentSpecIdentifier(path, suiteName);
+            FunctionalSourceSet testSuiteSourceSet = createCUnitSources(instantiator, suiteName, projectSourceSet, fileResolver);
+            CUnitTestSuiteSpec testSuiteSpec = BaseComponentSpec.create(DefaultCUnitTestSuiteSpec.class, id, testSuiteSourceSet, instantiator);
+            testSuiteSpec.setTestedComponent(testedComponent);
+            return testSuiteSpec;
+        }
+
+        private FunctionalSourceSet createCUnitSources(final Instantiator instantiator, final String suiteName, ProjectSourceSet projectSourceSet, final FileResolver fileResolver) {
+            final FunctionalSourceSet functionalSourceSet = instantiator.newInstance(DefaultFunctionalSourceSet.class, suiteName, instantiator, projectSourceSet);
+            functionalSourceSet.registerFactory(CSourceSet.class, new NamedDomainObjectFactory<CSourceSet>() {
+                public CSourceSet create(String name) {
+                    return BaseLanguageSourceSet.create(DefaultCSourceSet.class, name, suiteName, fileResolver, instantiator);
+                }
+            });
+            return functionalSourceSet;
+        }
+
+        @Finalize
+        public void configureCUnitTestSuiteSources(TestSuiteContainer testSuites, @Path("buildDir") File buildDir) {
+
+            for (final CUnitTestSuiteSpec suite : testSuites.withType(CUnitTestSuiteSpec.class)) {
+                FunctionalSourceSet suiteSourceSet = ((ComponentSpecInternal) suite).getSources();
+                CSourceSet launcherSources = suiteSourceSet.maybeCreate(CUNIT_LAUNCHER_SOURCE_SET, CSourceSet.class);
+                File baseDir = new File(buildDir, String.format("src/%s/cunitLauncher", suite.getName()));
+                launcherSources.getSource().srcDir(new File(baseDir, "c"));
+                launcherSources.getExportedHeaders().srcDir(new File(baseDir, "headers"));
+
+                CSourceSet testSources = suiteSourceSet.maybeCreate("c", CSourceSet.class);
+                testSources.getSource().srcDir(String.format("src/%s/%s", suite.getName(), "c"));
+                testSources.getExportedHeaders().srcDir(String.format("src/%s/headers", suite.getName()));
+
+                testSources.lib(launcherSources);
+            }
+        }
+
+        @Mutate
+        public void createCUnitLauncherTasks(TaskContainer tasks, TestSuiteContainer testSuites) {
+            for (final CUnitTestSuiteSpec suite : testSuites.withType(CUnitTestSuiteSpec.class)) {
+
+                String taskName = suite.getName() + "CUnitLauncher";
+                GenerateCUnitLauncher skeletonTask = tasks.create(taskName, GenerateCUnitLauncher.class);
+
+                CSourceSet launcherSources = findLaucherSources(suite);
+                skeletonTask.setSourceDir(launcherSources.getSource().getSrcDirs().iterator().next());
+                skeletonTask.setHeaderDir(launcherSources.getExportedHeaders().getSrcDirs().iterator().next());
+                launcherSources.builtBy(skeletonTask);
+            }
+        }
+
+        private CSourceSet findLaucherSources(CUnitTestSuiteSpec suite) {
+            return suite.getSource().withType(CSourceSet.class).matching(new Spec<CSourceSet>() {
+                public boolean isSatisfiedBy(CSourceSet element) {
+                    return element.getName().equals(CUNIT_LAUNCHER_SOURCE_SET);
+                }
+            }).iterator().next();
+        }
+
+        @Mutate
+        public void createCUnitTestBinaries(final BinaryContainer binaries, TestSuiteContainer testSuites, @Path("buildDir") File buildDir, ServiceRegistry serviceRegistry, ITaskFactory taskFactory) {
+            for (final CUnitTestSuiteSpec cUnitTestSuite : testSuites.withType(CUnitTestSuiteSpec.class)) {
+                for (NativeBinarySpec testedBinary : cUnitTestSuite.getTestedComponent().getBinaries().withType(NativeBinarySpec.class)) {
+
+                    if (testedBinary instanceof SharedLibraryBinary) {
+                        // TODO:DAZ For now, we only create test suites for static library variants
+                        continue;
+                    }
+
+                    DefaultCUnitTestSuiteBinary testBinary = createTestBinary(serviceRegistry, cUnitTestSuite, testedBinary, taskFactory);
+
+                    configure(testBinary, buildDir);
+
+                    cUnitTestSuite.getBinaries().add(testBinary);
+                    binaries.add(testBinary);
+                }
+            }
+        }
+
+        private DefaultCUnitTestSuiteBinary createTestBinary(ServiceRegistry serviceRegistry, CUnitTestSuiteSpec cUnitTestSuite, NativeBinarySpec testedBinary, ITaskFactory taskFactory) {
+            BinaryNamingScheme namingScheme = new DefaultBinaryNamingSchemeBuilder(((NativeBinarySpecInternal) testedBinary).getNamingScheme())
+                    .withComponentName(cUnitTestSuite.getBaseName())
+                    .withTypeString("CUnitExe").build();
+
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            NativeDependencyResolver resolver = serviceRegistry.get(NativeDependencyResolver.class);
+            return DefaultCUnitTestSuiteBinary.create(cUnitTestSuite, (NativeBinarySpecInternal) testedBinary, namingScheme, resolver, instantiator, taskFactory);
+        }
+
+        private void configure(DefaultCUnitTestSuiteBinary testBinary, File buildDir) {
+            BinaryNamingScheme namingScheme = testBinary.getNamingScheme();
+            PlatformToolProvider toolProvider = testBinary.getPlatformToolProvider();
+            File binaryOutputDir = new File(new File(buildDir, "binaries"), namingScheme.getOutputDirectoryBase());
+            String baseName = testBinary.getComponent().getBaseName();
+
+            testBinary.setExecutableFile(new File(binaryOutputDir, toolProvider.getExecutableName(baseName)));
+
+            ((ExtensionAware) testBinary).getExtensions().create("cCompiler", DefaultPreprocessingTool.class);
+        }
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/plugins/package-info.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/plugins/package-info.java
new file mode 100644
index 0000000..d240a84
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/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 cunit testing.
+ */
+package org.gradle.nativeplatform.test.cunit.plugins;
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/tasks/GenerateCUnitLauncher.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/tasks/GenerateCUnitLauncher.java
new file mode 100644
index 0000000..563ef64
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/tasks/GenerateCUnitLauncher.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.cunit.tasks;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.util.GFileUtils;
+
+import java.io.File;
+
+/**
+ * Generated the Gradle CUnit launcher: main method and header.
+ */
+public class GenerateCUnitLauncher extends DefaultTask {
+    private File sourceDir;
+    private File headerDir;
+
+    @TaskAction
+    public void generate() {
+        writeToFile(sourceDir, "gradle_cunit_main.c");
+        writeToFile(headerDir, "gradle_cunit_register.h");
+    }
+
+    private void writeToFile(File directory, String fileName) {
+        final File file = new File(directory, fileName);
+        GFileUtils.copyURLToFile(getClass().getResource(fileName), file);
+    }
+
+    @OutputDirectory
+    public File getSourceDir() {
+        return sourceDir;
+    }
+
+    public void setSourceDir(File sourceDir) {
+        this.sourceDir = sourceDir;
+    }
+
+    @OutputDirectory
+    public File getHeaderDir() {
+        return headerDir;
+    }
+
+    public void setHeaderDir(File headerDir) {
+        this.headerDir = headerDir;
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/tasks/package-info.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/tasks/package-info.java
new file mode 100644
index 0000000..80fe556
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cunit/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 cunit integration.
+ */
+package org.gradle.nativeplatform.test.cunit.tasks;
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteBinarySpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteBinarySpec.java
new file mode 100644
index 0000000..09f3c21
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteBinarySpec.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.googletest;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec;
+
+/**
+ * An executable which run a Google Test test suite.
+ */
+ at Incubating
+public interface GoogleTestTestSuiteBinarySpec extends NativeTestSuiteBinarySpec {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    GoogleTestTestSuiteSpec getTestSuite();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    GoogleTestTestSuiteSpec getComponent();
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteSpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteSpec.java
new file mode 100644
index 0000000..11ecc88
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/GoogleTestTestSuiteSpec.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.googletest;
+
+import org.gradle.api.Incubating;
+import org.gradle.nativeplatform.test.NativeTestSuiteSpec;
+
+/**
+ * Test suite of Google Test tests.
+ */
+ at Incubating
+public interface GoogleTestTestSuiteSpec extends NativeTestSuiteSpec {
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteBinary.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteBinary.java
new file mode 100644
index 0000000..f32d955
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteBinary.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.googletest.internal;
+
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
+import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec;
+import org.gradle.nativeplatform.test.internal.DefaultNativeTestSuiteBinarySpec;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+
+public class DefaultGoogleTestTestSuiteBinary extends DefaultNativeTestSuiteBinarySpec implements GoogleTestTestSuiteBinarySpec {
+
+    public static DefaultGoogleTestTestSuiteBinary create(NativeComponentSpec owner, NativeBinarySpecInternal testedBinary, BinaryNamingScheme namingScheme, NativeDependencyResolver resolver, Instantiator instantiator, ITaskFactory taskFactory) {
+        DefaultGoogleTestTestSuiteBinary spec = BaseBinarySpec.create(DefaultGoogleTestTestSuiteBinary.class, namingScheme.getLifecycleTaskName(), instantiator, taskFactory);
+        spec.setComponent(owner);
+        spec.setTestedBinary(testedBinary);
+        spec.setNamingScheme(namingScheme);
+        spec.setResolver(resolver);
+        return spec;
+    }
+
+    @Override
+    public GoogleTestTestSuiteSpec getTestSuite() {
+        return getComponent();
+    }
+
+    @Override
+    public GoogleTestTestSuiteSpec getComponent() {
+        return (GoogleTestTestSuiteSpec) super.getComponent();
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteSpec.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteSpec.java
new file mode 100644
index 0000000..1d0cdd8
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/internal/DefaultGoogleTestTestSuiteSpec.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.googletest.internal;
+
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.internal.AbstractNativeComponentSpec;
+import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+
+public class DefaultGoogleTestTestSuiteSpec extends AbstractNativeComponentSpec implements GoogleTestTestSuiteSpec, ComponentSpecInternal {
+    private NativeComponentSpec testedComponent;
+
+    public String getDisplayName() {
+        return String.format("googleTest test suite '%s'", getName());
+    }
+
+    public NativeComponentSpec getTestedComponent() {
+        return testedComponent;
+    }
+
+    public void setTestedComponent(NativeComponentSpec testedComponent) {
+        this.testedComponent = testedComponent;
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/package-info.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/package-info.java
new file mode 100644
index 0000000..a2ee426
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Google Test integration.
+ */
+package org.gradle.nativeplatform.test.googletest;
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPlugin.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPlugin.java
new file mode 100644
index 0000000..7d881e5
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/GoogleTestPlugin.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.nativeplatform.test.googletest.plugins;
+
+import org.gradle.api.*;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.DefaultFunctionalSourceSet;
+import org.gradle.language.base.sources.BaseLanguageSourceSet;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.cpp.internal.DefaultCppSourceSet;
+import org.gradle.language.cpp.plugins.CppLangPlugin;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.model.*;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeComponentSpec;
+import org.gradle.nativeplatform.SharedLibraryBinary;
+import org.gradle.nativeplatform.internal.NativeBinarySpecInternal;
+import org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver;
+import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteSpec;
+import org.gradle.nativeplatform.test.googletest.internal.DefaultGoogleTestTestSuiteBinary;
+import org.gradle.nativeplatform.test.googletest.internal.DefaultGoogleTestTestSuiteSpec;
+import org.gradle.nativeplatform.test.plugins.NativeBinariesTestPlugin;
+import org.gradle.nativeplatform.toolchain.GccCompatibleToolChain;
+import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecIdentifier;
+import org.gradle.platform.base.component.BaseComponentSpec;
+import org.gradle.platform.base.internal.BinaryNamingScheme;
+import org.gradle.platform.base.internal.ComponentSpecInternal;
+import org.gradle.platform.base.internal.DefaultBinaryNamingSchemeBuilder;
+import org.gradle.platform.base.internal.DefaultComponentSpecIdentifier;
+import org.gradle.platform.base.test.TestSuiteContainer;
+
+import java.io.File;
+import java.util.Collections;
+
+/**
+ * A plugin that sets up the infrastructure for testing native binaries with GoogleTest.
+ */
+ at Incubating
+public class GoogleTestPlugin implements Plugin<Project> {
+
+    public void apply(final Project project) {
+        project.apply(Collections.singletonMap("plugin", NativeBinariesTestPlugin.class));
+        project.apply(Collections.singletonMap("plugin", CppLangPlugin.class));
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class Rules extends RuleSource {
+
+        // TODO:DAZ Test suites should belong to ComponentSpecContainer, and we could rely on more conventions from the base plugins
+        @Defaults
+        public void createGoogleTestTestSuitePerComponent(TestSuiteContainer testSuites, NamedDomainObjectSet<NativeComponentSpec> components, ProjectSourceSet projectSourceSet, ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            FileResolver fileResolver = serviceRegistry.get(FileResolver.class);
+            for (NativeComponentSpec component : components) {
+                testSuites.add(createGoogleTestTestSuite(component, instantiator, projectSourceSet, fileResolver));
+            }
+        }
+
+        private GoogleTestTestSuiteSpec createGoogleTestTestSuite(final NativeComponentSpec testedComponent, Instantiator instantiator, ProjectSourceSet projectSourceSet, FileResolver fileResolver) {
+            String suiteName = String.format("%sTest", testedComponent.getName());
+            String path = testedComponent.getProjectPath();
+            ComponentSpecIdentifier id = new DefaultComponentSpecIdentifier(path, suiteName);
+            FunctionalSourceSet testSuiteSourceSet = createGoogleTestSources(instantiator, suiteName, projectSourceSet, fileResolver);
+            GoogleTestTestSuiteSpec testSuiteSpec = BaseComponentSpec.create(DefaultGoogleTestTestSuiteSpec.class, id, testSuiteSourceSet, instantiator);
+            testSuiteSpec.setTestedComponent(testedComponent);
+            return testSuiteSpec;
+        }
+
+        private FunctionalSourceSet createGoogleTestSources(final Instantiator instantiator, final String suiteName, ProjectSourceSet projectSourceSet, final FileResolver fileResolver) {
+            final FunctionalSourceSet functionalSourceSet = instantiator.newInstance(DefaultFunctionalSourceSet.class, suiteName, instantiator, projectSourceSet);
+            functionalSourceSet.registerFactory(CppSourceSet.class, new NamedDomainObjectFactory<CppSourceSet>() {
+                public CppSourceSet create(String name) {
+                    return BaseLanguageSourceSet.create(DefaultCppSourceSet.class, name, suiteName, fileResolver, instantiator);
+                }
+            });
+            return functionalSourceSet;
+        }
+
+        @Finalize
+        public void configureGoogleTestTestSuiteSources(TestSuiteContainer testSuites, @Path("buildDir") File buildDir) {
+
+            for (final GoogleTestTestSuiteSpec suite : testSuites.withType(GoogleTestTestSuiteSpec.class)) {
+                FunctionalSourceSet suiteSourceSet = ((ComponentSpecInternal) suite).getSources();
+
+                CppSourceSet testSources = suiteSourceSet.maybeCreate("cpp", CppSourceSet.class);
+                testSources.getSource().srcDir(String.format("src/%s/%s", suite.getName(), "cpp"));
+                testSources.getExportedHeaders().srcDir(String.format("src/%s/headers", suite.getName()));
+            }
+        }
+
+        @Mutate
+        public void createGoogleTestTestBinaries(final BinaryContainer binaries, TestSuiteContainer testSuites, @Path("buildDir") File buildDir, ServiceRegistry serviceRegistry, ITaskFactory taskFactory) {
+            for (final GoogleTestTestSuiteSpec googleTestTestSuite : testSuites.withType(GoogleTestTestSuiteSpec.class)) {
+                for (NativeBinarySpec testedBinary : googleTestTestSuite.getTestedComponent().getBinaries().withType(NativeBinarySpec.class)) {
+                    if (testedBinary instanceof SharedLibraryBinary) {
+                        // TODO:DAZ For now, we only create test suites for static library variants
+                        continue;
+                    }
+                    DefaultGoogleTestTestSuiteBinary testBinary = createTestBinary(serviceRegistry, googleTestTestSuite, testedBinary, taskFactory);
+
+                    configure(testBinary, buildDir);
+
+                    googleTestTestSuite.getBinaries().add(testBinary);
+                    binaries.add(testBinary);
+                }
+            }
+        }
+
+        private DefaultGoogleTestTestSuiteBinary createTestBinary(ServiceRegistry serviceRegistry, GoogleTestTestSuiteSpec googleTestTestSuite, NativeBinarySpec testedBinary, ITaskFactory taskFactory) {
+            BinaryNamingScheme namingScheme = new DefaultBinaryNamingSchemeBuilder(((NativeBinarySpecInternal) testedBinary).getNamingScheme())
+                    .withComponentName(googleTestTestSuite.getBaseName())
+                    .withTypeString("GoogleTestExe").build();
+
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            NativeDependencyResolver resolver = serviceRegistry.get(NativeDependencyResolver.class);
+            return DefaultGoogleTestTestSuiteBinary.create(googleTestTestSuite, (NativeBinarySpecInternal) testedBinary, namingScheme, resolver, instantiator, taskFactory);
+        }
+
+        private void configure(DefaultGoogleTestTestSuiteBinary testBinary, File buildDir) {
+            BinaryNamingScheme namingScheme = testBinary.getNamingScheme();
+            PlatformToolProvider toolProvider = testBinary.getPlatformToolProvider();
+            File binaryOutputDir = new File(new File(buildDir, "binaries"), namingScheme.getOutputDirectoryBase());
+            String baseName = testBinary.getComponent().getBaseName();
+
+            testBinary.setExecutableFile(new File(binaryOutputDir, toolProvider.getExecutableName(baseName)));
+
+            ((ExtensionAware) testBinary).getExtensions().create("cppCompiler", DefaultPreprocessingTool.class);
+
+            // TODO:DAZ Not sure if this should be here...
+            // Need "-pthread" when linking on Linux
+            if (testBinary.getToolChain() instanceof GccCompatibleToolChain
+                    && testBinary.getTargetPlatform().getOperatingSystem().isLinux()) {
+                testBinary.getLinker().args("-pthread");
+            }
+        }
+    }
+}
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/package-info.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/package-info.java
new file mode 100644
index 0000000..578a9f4
--- /dev/null
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/googletest/plugins/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Google Test testing.
+ */
+package org.gradle.nativeplatform.test.googletest.plugins;
diff --git a/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cunit.properties b/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cunit.properties
new file mode 100644
index 0000000..13b6d59
--- /dev/null
+++ b/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.cunit.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.nativeplatform.test.cunit.plugins.CUnitPlugin
\ No newline at end of file
diff --git a/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.google-test.properties b/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.google-test.properties
new file mode 100644
index 0000000..48fbf5a
--- /dev/null
+++ b/subprojects/testing-native/src/main/resources/META-INF/gradle-plugins/org.gradle.google-test.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2014 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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.nativeplatform.test.googletest.plugins.GoogleTestPlugin
\ No newline at end of file
diff --git a/subprojects/cpp/src/main/resources/org/gradle/nativebinaries/test/cunit/tasks/gradle_cunit_main.c b/subprojects/testing-native/src/main/resources/org/gradle/nativeplatform/test/cunit/tasks/gradle_cunit_main.c
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/nativebinaries/test/cunit/tasks/gradle_cunit_main.c
rename to subprojects/testing-native/src/main/resources/org/gradle/nativeplatform/test/cunit/tasks/gradle_cunit_main.c
diff --git a/subprojects/cpp/src/main/resources/org/gradle/nativebinaries/test/cunit/tasks/gradle_cunit_register.h b/subprojects/testing-native/src/main/resources/org/gradle/nativeplatform/test/cunit/tasks/gradle_cunit_register.h
similarity index 100%
rename from subprojects/cpp/src/main/resources/org/gradle/nativebinaries/test/cunit/tasks/gradle_cunit_register.h
rename to subprojects/testing-native/src/main/resources/org/gradle/nativeplatform/test/cunit/tasks/gradle_cunit_register.h
diff --git a/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/cunit/CUnitTest.groovy b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/cunit/CUnitTest.groovy
new file mode 100644
index 0000000..0ea8fb4
--- /dev/null
+++ b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/cunit/CUnitTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.cunit
+import org.gradle.language.c.plugins.CPlugin
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.type.ModelType
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.platform.base.test.TestSuiteContainer
+import org.gradle.nativeplatform.test.cunit.plugins.CUnitPlugin
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class CUnitTest extends Specification {
+    final def project = TestUtil.createRootProject();
+
+    def "check the correct binary type are created for the test suite"() {
+        when:
+        project.pluginManager.apply(CPlugin)
+        project.pluginManager.apply(CUnitPlugin)
+        project.model {
+            components {
+                main(NativeLibrarySpec)
+            }
+        }
+        project.evaluate()
+
+        then:
+        def binaries = project.modelRegistry.realize(ModelPath.path("testSuites"), ModelType.of(TestSuiteContainer)).getByName("mainTest").binaries
+        binaries.collect({ it instanceof CUnitTestSuiteBinarySpec }) == [true] * binaries.size()
+    }
+}
diff --git a/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTest.groovy b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTest.groovy
new file mode 100644
index 0000000..e571fb4
--- /dev/null
+++ b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.nativeplatform.test.googletest
+import org.gradle.language.cpp.plugins.CppPlugin
+import org.gradle.model.internal.core.ModelPath
+import org.gradle.model.internal.type.ModelType
+import org.gradle.nativeplatform.NativeLibrarySpec
+import org.gradle.nativeplatform.test.googletest.plugins.GoogleTestPlugin
+import org.gradle.platform.base.test.TestSuiteContainer
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class GoogleTestTest extends Specification {
+    final def project = TestUtil.createRootProject();
+
+    def "check the correct binary type are created for the test suite"() {
+        when:
+        project.apply(plugin: CppPlugin)
+        project.apply(plugin: GoogleTestPlugin)
+        project.model {
+            components {
+                main(NativeLibrarySpec)
+            }
+        }
+        project.evaluate()
+
+        then:
+        def binaries = project.modelRegistry.realize(ModelPath.path("testSuites"), ModelType.of(TestSuiteContainer)).getByName("mainTest").binaries
+        binaries.collect({ it instanceof GoogleTestTestSuiteBinarySpec }) == [true] * binaries.size()
+    }
+}
diff --git a/subprojects/testing-native/testing-native.gradle b/subprojects/testing-native/testing-native.gradle
new file mode 100644
index 0000000..fba1196
--- /dev/null
+++ b/subprojects/testing-native/testing-native.gradle
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 project(':core')
+    compile project(':platformNative')
+    compile project(':languageNative')
+
+    testCompile libraries.groovy
+
+    integTestRuntime project(":ideNative")
+}
+
+useTestFixtures()
+useTestFixtures(project: ":platformNative")
+useTestFixtures(project: ':diagnostics')
+
+useClassycle()
+strictCompile()
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java
new file mode 100644
index 0000000..9b99f89
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/BuildModelActionRunner.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.runner;
+
+import org.gradle.api.Project;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.execution.ProjectConfigurer;
+import org.gradle.initialization.BuildEventConsumer;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.invocation.BuildActionRunner;
+import org.gradle.internal.invocation.BuildController;
+import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException;
+import org.gradle.tooling.internal.provider.BuildActionResult;
+import org.gradle.tooling.internal.provider.BuildModelAction;
+import org.gradle.tooling.internal.provider.PayloadSerializer;
+import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+import org.gradle.tooling.provider.model.UnknownModelException;
+
+public class BuildModelActionRunner implements BuildActionRunner {
+    @Override
+    public void run(BuildAction action, BuildController buildController) {
+        if (!(action instanceof BuildModelAction)) {
+            return;
+        }
+
+        BuildModelAction buildModelAction = (BuildModelAction) action;
+        GradleInternal gradle = buildController.getGradle();
+
+        // register a TestListener that dispatches all test progress via the registered BuildEventConsumer instance,
+        // this allows to send test progress events back to the DaemonClient (via short-cut)
+        if (buildModelAction.isSendTestProgressEvents()) {
+            BuildEventConsumer eventConsumer = gradle.getServices().get(BuildEventConsumer.class);
+            gradle.addListener(new ClientForwardingTestListener(eventConsumer));
+        }
+
+        if (buildModelAction.isRunTasks()) {
+            buildController.run();
+        } else {
+            buildController.configure();
+            // Currently need to force everything to be configured
+            gradle.getServices().get(ProjectConfigurer.class).configureHierarchy(gradle.getRootProject());
+            for (Project project : gradle.getRootProject().getAllprojects()) {
+                ProjectInternal projectInternal = (ProjectInternal) project;
+                projectInternal.getTasks().discoverTasks();
+                projectInternal.bindAllModelRules();
+            }
+        }
+
+        String modelName = buildModelAction.getModelName();
+        ToolingModelBuilderRegistry builderRegistry = getToolingModelBuilderRegistry(gradle);
+        ToolingModelBuilder builder;
+        try {
+            builder = builderRegistry.getBuilder(modelName);
+        } catch (UnknownModelException e) {
+            throw (InternalUnsupportedModelException) new InternalUnsupportedModelException().initCause(e);
+        }
+
+        Object result;
+        if (builder instanceof ProjectSensitiveToolingModelBuilder) {
+            result = ((ProjectSensitiveToolingModelBuilder) builder).buildAll(modelName, gradle.getDefaultProject(), true);
+        } else {
+            result = builder.buildAll(modelName, gradle.getDefaultProject());
+        }
+
+        PayloadSerializer payloadSerializer = gradle.getServices().get(PayloadSerializer.class);
+        BuildActionResult buildActionResult = new BuildActionResult(payloadSerializer.serialize(result), null);
+        buildController.setResult(buildActionResult);
+    }
+
+    private ToolingModelBuilderRegistry getToolingModelBuilderRegistry(GradleInternal gradle) {
+        return gradle.getDefaultProject().getServices().get(ToolingModelBuilderRegistry.class);
+    }
+}
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientForwardingTestListener.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientForwardingTestListener.java
new file mode 100644
index 0000000..b715fe7
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientForwardingTestListener.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.provider.runner;
+
+import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
+import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
+import org.gradle.api.internal.tasks.testing.TestStartEvent;
+import org.gradle.api.internal.tasks.testing.results.TestListenerInternal;
+import org.gradle.api.tasks.testing.TestOutputEvent;
+import org.gradle.api.tasks.testing.TestResult;
+import org.gradle.initialization.BuildEventConsumer;
+import org.gradle.tooling.internal.protocol.events.InternalJvmTestDescriptor;
+import org.gradle.tooling.internal.provider.events.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test listener that forwards all receiving events to the client via the provided {@code BuildEventConsumer} instance.
+ */
+class ClientForwardingTestListener implements TestListenerInternal {
+
+    private final BuildEventConsumer eventConsumer;
+
+    ClientForwardingTestListener(BuildEventConsumer eventConsumer) {
+        this.eventConsumer = eventConsumer;
+    }
+
+    @Override
+    public void started(TestDescriptorInternal testDescriptor, TestStartEvent startEvent) {
+        eventConsumer.dispatch(new DefaultTestStartedProgressEvent(startEvent.getStartTime(), adapt(testDescriptor)));
+    }
+
+    @Override
+    public void completed(TestDescriptorInternal testDescriptor, TestResult testResult, TestCompleteEvent completeEvent) {
+        eventConsumer.dispatch(new DefaultTestFinishedProgressEvent(completeEvent.getEndTime(), adapt(testDescriptor), adapt(testResult)));
+    }
+
+    @Override
+    public void output(TestDescriptorInternal testDescriptor, TestOutputEvent event) {
+        // Don't forward
+    }
+
+    private static DefaultTestDescriptor adapt(TestDescriptorInternal testDescriptor) {
+        return testDescriptor.isComposite() ? toTestDescriptorForSuite(testDescriptor) : toTestDescriptorForTest(testDescriptor);
+    }
+
+    private static DefaultTestDescriptor toTestDescriptorForSuite(TestDescriptorInternal suite) {
+        Object id = suite.getId();
+        String name = suite.getName();
+        String displayName = suite.toString();
+        String testKind = InternalJvmTestDescriptor.KIND_SUITE;
+        String suiteName = suite.getName();
+        String className = suite.getClassName();
+        String methodName = null;
+        Object parentId = suite.getParent() != null ? suite.getParent().getId() : null;
+        return new DefaultTestDescriptor(id, name, displayName, testKind, suiteName, className, methodName, parentId);
+    }
+
+    private static DefaultTestDescriptor toTestDescriptorForTest(TestDescriptorInternal test) {
+        Object id = test.getId();
+        String name = test.getName();
+        String displayName = test.toString();
+        String testKind = InternalJvmTestDescriptor.KIND_ATOMIC;
+        String suiteName = null;
+        String className = test.getClassName();
+        String methodName = test.getName();
+        Object parentId = test.getParent() != null ? test.getParent().getId() : null;
+        return new DefaultTestDescriptor(id, name, displayName, testKind, suiteName, className, methodName, parentId);
+    }
+
+    private static AbstractTestResult adapt(TestResult result) {
+        TestResult.ResultType resultType = result.getResultType();
+        switch (resultType) {
+            case SUCCESS:
+                return new DefaultTestSuccessResult(result.getStartTime(), result.getEndTime());
+            case SKIPPED:
+                return new DefaultTestSkippedResult(result.getStartTime(), result.getEndTime());
+            case FAILURE:
+                return new DefaultTestFailureResult(result.getStartTime(), result.getEndTime(), convertExceptions(result.getExceptions()));
+            default:
+                throw new IllegalStateException("Unknown test result type: " + resultType);
+        }
+    }
+
+    private static List<DefaultFailure> convertExceptions(List<Throwable> exceptions) {
+        List<DefaultFailure> failures = new ArrayList<DefaultFailure>(exceptions.size());
+        for (Throwable exception : exceptions) {
+            failures.add(DefaultFailure.fromThrowable(exception));
+        }
+        return failures;
+    }
+
+}
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java
new file mode 100644
index 0000000..572e0c2
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunner.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.runner;
+
+import org.gradle.api.BuildCancelledException;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.execution.ProjectConfigurer;
+import org.gradle.internal.invocation.BuildAction;
+import org.gradle.internal.invocation.BuildActionRunner;
+import org.gradle.internal.invocation.BuildController;
+import org.gradle.tooling.internal.protocol.InternalBuildAction;
+import org.gradle.tooling.internal.protocol.InternalBuildActionFailureException;
+import org.gradle.tooling.internal.protocol.InternalBuildCancelledException;
+import org.gradle.tooling.internal.protocol.InternalBuildController;
+import org.gradle.tooling.internal.provider.BuildActionResult;
+import org.gradle.tooling.internal.provider.ClientProvidedBuildAction;
+import org.gradle.tooling.internal.provider.PayloadSerializer;
+
+public class ClientProvidedBuildActionRunner implements BuildActionRunner {
+    @Override
+    public void run(BuildAction action, BuildController buildController) {
+        if (!(action instanceof ClientProvidedBuildAction)) {
+            return;
+        }
+
+        ClientProvidedBuildAction clientProvidedBuildAction = (ClientProvidedBuildAction) action;
+        GradleInternal gradle = buildController.getGradle();
+        PayloadSerializer payloadSerializer = gradle.getServices().get(PayloadSerializer.class);
+        InternalBuildAction<?> clientAction = (InternalBuildAction<?>) payloadSerializer.deserialize(clientProvidedBuildAction.getAction());
+
+        buildController.configure();
+        // Currently need to force everything to be configured
+        gradle.getServices().get(ProjectConfigurer.class).configureHierarchy(gradle.getRootProject());
+
+        InternalBuildController internalBuildController = new DefaultBuildController(gradle);
+        Object model = null;
+        Throwable failure = null;
+        try {
+            model = clientAction.execute(internalBuildController);
+        } catch (BuildCancelledException e) {
+            failure = new InternalBuildCancelledException(e);
+        } catch (RuntimeException e) {
+            failure = new InternalBuildActionFailureException(e);
+        }
+
+        BuildActionResult buildActionResult;
+        if (failure != null) {
+            buildActionResult = new BuildActionResult(null, payloadSerializer.serialize(failure));
+        } else {
+            buildActionResult = new BuildActionResult(payloadSerializer.serialize(model), null);
+        }
+        buildController.setResult(buildActionResult);
+    }
+}
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/DefaultBuildController.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/DefaultBuildController.java
new file mode 100644
index 0000000..a47f566
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/DefaultBuildController.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.tooling.internal.provider.runner;
+
+import org.gradle.api.BuildCancelledException;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.tooling.internal.gradle.GradleProjectIdentity;
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.tooling.internal.provider.connection.ProviderBuildResult;
+import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+import org.gradle.tooling.provider.model.UnknownModelException;
+
+class DefaultBuildController implements InternalBuildController {
+    private final GradleInternal gradle;
+
+    public DefaultBuildController(GradleInternal gradle) {
+        this.gradle = gradle;
+    }
+
+    public BuildResult<?> getBuildModel() throws BuildExceptionVersion1 {
+        return new ProviderBuildResult<Object>(gradle);
+    }
+
+    public BuildResult<?> getModel(Object target, ModelIdentifier modelIdentifier) throws BuildExceptionVersion1, InternalUnsupportedModelException {
+        BuildCancellationToken cancellationToken = gradle.getServices().get(BuildCancellationToken.class);
+        if (cancellationToken.isCancellationRequested()) {
+            throw new BuildCancelledException(String.format("Could not build '%s' model. Build cancelled.", modelIdentifier.getName()));
+        }
+        ToolingModelBuilderRegistry modelBuilderRegistry;
+        ProjectInternal project;
+        boolean isImplicitProject;
+        if (target == null) {
+            project = gradle.getDefaultProject();
+            isImplicitProject = true;
+        } else if (target instanceof GradleProjectIdentity) {
+            GradleProjectIdentity gradleProject = (GradleProjectIdentity) target;
+            project = gradle.getRootProject().project(gradleProject.getPath());
+            isImplicitProject = false;
+        } else {
+            throw new IllegalArgumentException("Don't know how to build models for " + target);
+        }
+        modelBuilderRegistry = project.getServices().get(ToolingModelBuilderRegistry.class);
+
+        ToolingModelBuilder builder;
+        try {
+            builder = modelBuilderRegistry.getBuilder(modelIdentifier.getName());
+        } catch (UnknownModelException e) {
+            throw (InternalUnsupportedModelException) (new InternalUnsupportedModelException()).initCause(e);
+        }
+        Object model;
+        if (builder instanceof ProjectSensitiveToolingModelBuilder) {
+            model = ((ProjectSensitiveToolingModelBuilder) builder).buildAll(modelIdentifier.getName(), project, isImplicitProject);
+        } else {
+            model = builder.buildAll(modelIdentifier.getName(), project);
+        }
+        return new ProviderBuildResult<Object>(model);
+    }
+}
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ToolingBuilderServices.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ToolingBuilderServices.java
new file mode 100644
index 0000000..6800aa8
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/ToolingBuilderServices.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.runner;
+
+import org.gradle.internal.service.ServiceRegistration;
+import org.gradle.internal.service.scopes.PluginServiceRegistry;
+
+public class ToolingBuilderServices implements PluginServiceRegistry {
+    @Override
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new Object() {
+            BuildModelActionRunner createBuildModelActionRunner() {
+                return new BuildModelActionRunner();
+            }
+
+            ClientProvidedBuildActionRunner createClientProvidedBuildActionRunner() {
+                return new ClientProvidedBuildActionRunner();
+            }
+        });
+    }
+
+    @Override
+    public void registerBuildServices(ServiceRegistration registration) {
+    }
+
+    public void registerGradleServices(ServiceRegistration registration) {
+    }
+
+    @Override
+    public void registerProjectServices(ServiceRegistration registration) {
+    }
+}
diff --git a/subprojects/tooling-api-builders/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry b/subprojects/tooling-api-builders/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
new file mode 100644
index 0000000..d5b37ac
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/main/resources/META-INF/services/org.gradle.internal.service.scopes.PluginServiceRegistry
@@ -0,0 +1 @@
+org.gradle.tooling.internal.provider.runner.ToolingBuilderServices
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy
new file mode 100644
index 0000000..8e755e8
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/ClientProvidedBuildActionRunnerTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.provider.runner
+
+import org.gradle.StartParameter
+import org.gradle.api.BuildCancelledException
+import org.gradle.api.internal.GradleInternal
+import org.gradle.execution.ProjectConfigurer
+import org.gradle.internal.invocation.BuildController
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.tooling.internal.protocol.InternalBuildAction
+import org.gradle.tooling.internal.protocol.InternalBuildActionFailureException
+import org.gradle.tooling.internal.protocol.InternalBuildCancelledException
+import org.gradle.tooling.internal.provider.BuildActionResult
+import org.gradle.tooling.internal.provider.ClientProvidedBuildAction
+import org.gradle.tooling.internal.provider.PayloadSerializer
+import org.gradle.tooling.internal.provider.SerializedPayload
+import spock.lang.Specification
+
+class ClientProvidedBuildActionRunnerTest extends Specification {
+    def action = Mock(SerializedPayload)
+    def startParameter = Mock(StartParameter)
+    def payloadSerializer = Mock(PayloadSerializer)
+    def projectConfigurer = Mock(ProjectConfigurer)
+    def buildController = Mock(BuildController) {
+        getGradle() >> Stub(GradleInternal) {
+            getServices() >> Stub(ServiceRegistry) {
+                get(PayloadSerializer) >> payloadSerializer
+                get(ProjectConfigurer) >> projectConfigurer
+            }
+        }
+    }
+    def clientProvidedBuildAction = new ClientProvidedBuildAction(startParameter, action)
+    def runner = new ClientProvidedBuildActionRunner()
+
+    def "can run action and returns result when completed"() {
+        given:
+        def model = new Object()
+        def output = Mock(SerializedPayload)
+        def internalAction = Mock(InternalBuildAction)
+
+        when:
+        runner.run(clientProvidedBuildAction, buildController)
+
+        then:
+        1 * internalAction.execute(_) >> model
+        1 * payloadSerializer.deserialize(action) >> internalAction
+        1 * payloadSerializer.serialize(model) >> output
+        1 * buildController.setResult(_) >> { BuildActionResult result ->
+            assert result.failure == null
+            assert result.result == output
+        }
+    }
+
+    def "can run action and reports failure"() {
+        given:
+        def failure = new RuntimeException()
+        def output = Mock(SerializedPayload)
+        def internalAction = Mock(InternalBuildAction)
+
+        when:
+        runner.run(clientProvidedBuildAction, buildController)
+
+        then:
+        1 * payloadSerializer.deserialize(action) >> internalAction
+        1 * internalAction.execute(_) >> { throw failure }
+        1 * payloadSerializer.serialize(_) >> { Throwable t ->
+            assert t instanceof InternalBuildActionFailureException
+            assert t.cause == failure
+            return output
+        }
+        1 * buildController.setResult(_) >> { BuildActionResult result ->
+            assert result.failure == output
+            assert result.result == null
+        }
+    }
+
+    def "can run action and propagate cancellation exception"() {
+        given:
+        def cancellation = new BuildCancelledException()
+        def output = Mock(SerializedPayload)
+        def internalAction = Mock(InternalBuildAction)
+
+        when:
+        runner.run(clientProvidedBuildAction, buildController)
+
+        then:
+        1 * payloadSerializer.deserialize(action) >> internalAction
+        1 * internalAction.execute(_) >> { throw cancellation }
+        1 * payloadSerializer.serialize(_) >> { Throwable t ->
+            assert t instanceof InternalBuildCancelledException
+            assert t.cause == cancellation
+            return output
+        }
+        1 * buildController.setResult(_) >> { BuildActionResult result ->
+            assert result.failure == output
+            assert result.result == null
+        }
+    }
+}
diff --git a/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/DefaultBuildControllerTest.groovy b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/DefaultBuildControllerTest.groovy
new file mode 100644
index 0000000..d090872
--- /dev/null
+++ b/subprojects/tooling-api-builders/src/test/groovy/org/gradle/tooling/internal/provider/runner/DefaultBuildControllerTest.groovy
@@ -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.tooling.internal.provider.runner
+
+import org.gradle.api.BuildCancelledException
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.initialization.BuildCancellationToken
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.tooling.internal.gradle.GradleProjectIdentity
+import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException
+import org.gradle.tooling.internal.protocol.ModelIdentifier
+import org.gradle.tooling.model.internal.ProjectSensitiveToolingModelBuilder
+import org.gradle.tooling.provider.model.ToolingModelBuilder
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
+import org.gradle.tooling.provider.model.UnknownModelException
+import spock.lang.Specification
+
+class DefaultBuildControllerTest extends Specification {
+    def cancellationToken = Stub(BuildCancellationToken)
+    def gradle = Stub(GradleInternal) {
+        getServices() >> Stub(ServiceRegistry) {
+            get(BuildCancellationToken) >> cancellationToken
+        }
+    }
+    def registry = Stub(ToolingModelBuilderRegistry)
+    def project = Stub(ProjectInternal) {
+        getServices() >> Stub(ServiceRegistry) {
+            get(ToolingModelBuilderRegistry) >> registry
+        }
+    }
+    def modelId = Stub(ModelIdentifier) {
+        getName() >> 'some.model'
+    }
+    def modelBuilder = Stub(ToolingModelBuilder)
+    def controller = new DefaultBuildController(gradle)
+
+    def "adapts model not found exception to protocol exception"() {
+        def failure = new UnknownModelException("not found")
+
+        given:
+        _ * gradle.defaultProject >> project
+        _ * registry.getBuilder('some.model') >> { throw failure }
+
+        when:
+        controller.getModel(null, modelId)
+
+        then:
+        InternalUnsupportedModelException e = thrown()
+        e.cause == failure
+    }
+
+    def "uses builder for specified project"() {
+        def target = Stub(GradleProjectIdentity)
+        def rootProject = Stub(ProjectInternal)
+        def model = new Object()
+
+        given:
+        _ * target.path >> ":some:path"
+        _ * gradle.rootProject >> rootProject
+        _ * rootProject.project(":some:path") >> project
+        _ * registry.getBuilder("some.model") >> modelBuilder
+        _ * modelBuilder.buildAll("some.model", project) >> model
+
+        when:
+        def result = controller.getModel(target, modelId)
+
+        then:
+        result.getModel() == model
+    }
+
+    def "uses builder for default project when none specified"() {
+        def model = new Object()
+
+        given:
+        _ * gradle.defaultProject >> project
+        _ * registry.getBuilder("some.model") >> modelBuilder
+        _ * modelBuilder.buildAll("some.model", project) >> model
+
+        when:
+        def result = controller.getModel(null, modelId)
+
+        then:
+        result.getModel() == model
+    }
+
+    def "passes information about default project when context sensitive builder is used"() {
+        def contextModelBuilder = Stub(ProjectSensitiveToolingModelBuilder)
+        def model = new Object()
+
+        given:
+        _ * gradle.defaultProject >> project
+        _ * registry.getBuilder("some.model") >> contextModelBuilder
+        _ * contextModelBuilder.buildAll("some.model", project, true) >> model
+
+        when:
+        def result = controller.getModel(null, modelId)
+
+        then:
+        result.getModel() == model
+    }
+
+    def "passes information about specified project when context sensitive builder is used"() {
+        def contextModelBuilder = Stub(ProjectSensitiveToolingModelBuilder)
+        def model = new Object()
+        def target = Stub(GradleProjectIdentity)
+        def rootProject = Stub(ProjectInternal)
+
+        given:
+        _ * target.path >> ":some:path"
+        _ * gradle.rootProject >> rootProject
+        _ * rootProject.project(":some:path") >> project
+        _ * registry.getBuilder("some.model") >> contextModelBuilder
+        _ * contextModelBuilder.buildAll("some.model", project, false) >> model
+
+        when:
+        def result = controller.getModel(target, modelId)
+
+        then:
+        result.getModel() == model
+    }
+
+    def "throws an exception when cancel was requested"() {
+        given:
+        _ * cancellationToken.cancellationRequested >> true
+        def target = Stub(GradleProjectIdentity)
+
+        when:
+        def result = controller.getModel(target, modelId)
+
+        then:
+        thrown(BuildCancelledException)
+    }
+}
diff --git a/subprojects/tooling-api-builders/tooling-api-builders.gradle b/subprojects/tooling-api-builders/tooling-api-builders.gradle
new file mode 100644
index 0000000..6e23fb9
--- /dev/null
+++ b/subprojects/tooling-api-builders/tooling-api-builders.gradle
@@ -0,0 +1,8 @@
+dependencies {
+    compile project(':plugins')
+    compile project(':launcher')
+    compile project(':toolingApi')
+}
+
+useClassycle()
+strictCompile()
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/AutoTestedSamplesToolingApiTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/AutoTestedSamplesToolingApiTest.groovy
index 13a4ea4..08ddd0f 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/AutoTestedSamplesToolingApiTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/AutoTestedSamplesToolingApiTest.groovy
@@ -16,16 +16,13 @@
 
 package org.gradle.integtests.tooling
 
-import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AutoTestedSamplesUtil
+import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.model.Element
-import org.gradle.internal.classloader.ClasspathUtil
 import org.junit.Rule
-import spock.lang.IgnoreIf
 import spock.lang.Specification
 
- at IgnoreIf({!JavaVersion.current().java6Compatible})
 public class AutoTestedSamplesToolingApiTest extends Specification {
 
     @Rule public final TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider()
@@ -60,7 +57,6 @@ public class Sample {
      * @param source
      */
     void tryCompile(String source) {
-        //TODO SF generalize and move the test out of integ tests, add unit tests
         source = normalize(source)
         def sourceFile = temp.testDirectory.file("Sample.java")
         sourceFile.text = source
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
index 3f76c39..f35792e 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.integtests.tooling
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.integtests.fixtures.executer.GradleDistribution
-import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
 import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
 import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
 import org.gradle.integtests.tooling.fixture.ConfigurableOperation
@@ -29,7 +29,6 @@ import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
-import org.gradle.tooling.internal.consumer.ConnectorServices
 import org.gradle.tooling.internal.consumer.Distribution
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.idea.IdeaProject
@@ -51,13 +50,8 @@ class ConcurrentToolingApiIntegrationSpec extends Specification {
 
     def setup() {
         //concurrent tooling api at the moment is only supported for forked mode
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
         concurrent.shortTimeout = 180000
-        new ConnectorServices().reset()
-    }
-
-    def cleanup() {
-        new ConnectorServices().reset()
     }
 
     def "handles the same target gradle version concurrently"() {
@@ -65,7 +59,7 @@ class ConcurrentToolingApiIntegrationSpec extends Specification {
 
         when:
         threads.times {
-            concurrent.start { useToolingApi(new UnderDevelopmentGradleDistribution()) }
+            concurrent.start { useToolingApi(toolingApi) }
         }
 
         then:
@@ -78,23 +72,23 @@ class ConcurrentToolingApiIntegrationSpec extends Specification {
 
     def "handles different target gradle versions concurrently"() {
         given:
-        def current = dist
-        def last = new ReleasedVersionDistributions().getMostRecentFinalRelease() 
-        assert current != last
-        println "Combination of versions used: current - $current, last - $last"
+        def last = new ReleasedVersionDistributions().getMostRecentFinalRelease()
+        assert dist != last
+        println "Combination of versions used: current - $dist, last - $last"
+        def oldDistApi = new ToolingApi(last, temporaryFolder)
 
         file('build.gradle')  << "apply plugin: 'java'"
 
         when:
-        concurrent.start { useToolingApi(current) }
-        concurrent.start { useToolingApi(last)}
+        concurrent.start { useToolingApi(toolingApi) }
+        concurrent.start { useToolingApi(oldDistApi)}
 
         then:
         concurrent.finished()
     }
 
-    def useToolingApi(GradleDistribution target) {
-        new ToolingApi(target, new IntegrationTestBuildContext().gradleUserHomeDir, temporaryFolder, false).withConnection { ProjectConnection connection ->
+    def useToolingApi(ToolingApi target) {
+        target.withConnection { ProjectConnection connection ->
             try {
                 def model = connection.getModel(IdeaProject)
                 assert model != null
@@ -279,12 +273,12 @@ project.description = text
             return 'mock'
         }
 
-        ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir) {
+        ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir, BuildCancellationToken cancellationToken) {
             def o = progressLoggerFactory.newOperation("mock")
             operation(o)
             o.started()
             o.completed()
-            return delegate.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir)
+            return delegate.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir, cancellationToken)
         }
     }
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
index 5e9b1f4..6252e09 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/SamplesToolingApiIntegrationTest.groovy
@@ -139,6 +139,7 @@ repositories {
     private ExecutionResult run(String task = 'run', File dir = sample.dir) {
         try {
             return new GradleContextualExecuter(distribution, temporaryFolder)
+                    .requireGradleHome()
                     .inDirectory(dir)
                     .withTasks(task)
                     .run()
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
index 1a8d90f..dfaef17 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
@@ -30,7 +30,7 @@ class ToolingApiClasspathIntegrationTest extends AbstractIntegrationSpec {
         then:
         resolve.classpath.size() == 2
         resolve.classpath.any {it.name ==~ /slf4j-api-.*\.jar/}
-        resolve.classpath.find {it.name ==~ /gradle-tooling-api.*\.jar/}.size() < 1.6 * 1024 * 1024
+        resolve.classpath.find { it.name ==~ /gradle-tooling-api.*\.jar/ }.size() < 1.8 * 1024 * 1024
 
         cleanup:
         resolver.stop()
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
index f934885..eda009a 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
@@ -26,8 +26,6 @@ import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.model.GradleProject
 import org.gradle.util.GradleVersion
-import org.gradle.util.SetSystemProperties
-import org.junit.Rule
 import spock.lang.Issue
 
 class ToolingApiIntegrationTest extends AbstractIntegrationSpec {
@@ -35,8 +33,6 @@ class ToolingApiIntegrationTest extends AbstractIntegrationSpec {
     final ToolingApi toolingApi = new ToolingApi(distribution, temporaryFolder)
     final GradleDistribution otherVersion = new ReleasedVersionDistributions().mostRecentFinalRelease
 
-    @Rule SetSystemProperties properties
-
     TestFile projectDir
 
     def setup() {
@@ -59,8 +55,6 @@ class ToolingApiIntegrationTest extends AbstractIntegrationSpec {
     }
 
     def "tooling api uses the wrapper properties to determine which version to use"() {
-        toolingApi.isEmbedded = false
-
         projectDir.file('build.gradle').text = """
 task wrapper(type: Wrapper) { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
 task check << { assert gradle.gradleVersion == '${otherVersion.version.version}' }
@@ -69,7 +63,7 @@ task check << { assert gradle.gradleVersion == '${otherVersion.version.version}'
 
         when:
         toolingApi.withConnector { connector ->
-            connector.useDefaultDistribution()
+            connector.useBuildDistribution()
         }
         toolingApi.withConnection { connection -> connection.newBuild().forTasks('check').run() }
 
@@ -78,8 +72,6 @@ task check << { assert gradle.gradleVersion == '${otherVersion.version.version}'
     }
 
     def "tooling api searches up from the project directory to find the wrapper properties"() {
-        toolingApi.isEmbedded = false
-
         projectDir.file('settings.gradle') << "include 'child'"
         projectDir.file('build.gradle') << """
 task wrapper(type: Wrapper) { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
@@ -92,7 +84,7 @@ allprojects {
 
         when:
         toolingApi.withConnector { connector ->
-            connector.useDefaultDistribution()
+            connector.useBuildDistribution()
             connector.searchUpwards(true)
             connector.forProjectDirectory(projectDir.file('child'))
         }
@@ -103,7 +95,6 @@ allprojects {
     }
 
     def "can specify a gradle installation to use"() {
-        toolingApi.isEmbedded = false
         projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
@@ -117,7 +108,6 @@ allprojects {
     }
 
     def "can specify a gradle distribution to use"() {
-        toolingApi.isEmbedded = false
         projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
@@ -131,8 +121,6 @@ allprojects {
     }
 
     def "can specify a gradle version to use"() {
-        System.setProperty("javax.net.debug", "ssl,handshake")
-        toolingApi.isEmbedded = false
         projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
@@ -163,12 +151,12 @@ allprojects {
 
             repositories {
                 maven { url "${new IntegrationTestBuildContext().libsRepo.toURI()}" }
-                maven { url "http://repo.gradle.org/gradle/repo" }
+                maven { url "https://repo.gradle.org/gradle/repo" }
             }
 
             dependencies {
                 compile "org.gradle:gradle-tooling-api:${distribution.version.version}"
-                runtime 'org.slf4j:slf4j-simple:1.7.2'
+                runtime 'org.slf4j:slf4j-simple:1.7.10'
             }
 
             mainClassName = 'Main'
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
index 57da953..080a120 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
@@ -19,23 +19,30 @@ package org.gradle.integtests.tooling
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.tooling.fixture.ToolingApi
 import org.gradle.test.fixtures.server.http.HttpServer
-import org.gradle.tooling.BuildLauncher
+import org.gradle.tooling.*
+import org.gradle.tooling.internal.consumer.DefaultCancellationTokenSource
 import org.gradle.util.GradleVersion
 import org.junit.Rule
+import org.mortbay.jetty.MimeTypes
+
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 
 import static org.gradle.test.matchers.UserAgentMatcher.matchesNameAndVersion
 
 class ToolingApiRemoteIntegrationTest extends AbstractIntegrationSpec {
     @Rule HttpServer server = new HttpServer()
-    final ToolingApi toolingApi = new ToolingApi(distribution, executer.gradleUserHomeDir, temporaryFolder, false)
+    final ToolingApi toolingApi = new ToolingApi(distribution, temporaryFolder)
 
     void setup() {
         server.start()
+        toolingApi.requireIsolatedUserHome()
     }
 
     def "downloads distribution with valid user-agent information"() {
         assert distribution.binDistribution.exists() : "bin distribution must exist to run this test, you need to run the :binZip task"
-        def userHomeDir = file("user-home-dir")
 
         given:
         settingsFile << "";
@@ -46,9 +53,8 @@ class ToolingApiRemoteIntegrationTest extends AbstractIntegrationSpec {
         server.expectUserAgent(matchesNameAndVersion("Gradle Tooling API", GradleVersion.current().getVersion()))
 
         and:
-        toolingApi.withConnector {
-            it.useDistribution(URI.create("http://localhost:${server.port}/custom-dist.zip"))
-            it.useGradleUserHomeDir(userHomeDir)
+        toolingApi.withConnector { GradleConnector connector ->
+            connector.useDistribution(URI.create("http://localhost:${server.port}/custom-dist.zip"))
         }
 
         when:
@@ -62,6 +68,88 @@ class ToolingApiRemoteIntegrationTest extends AbstractIntegrationSpec {
 
         then:
         buildOutput.toString().contains('hello')
-        userHomeDir.file("wrapper/dists/custom-dist").assertIsDir().listFiles().size() == 1
+        toolingApi.gradleUserHomeDir.file("wrapper/dists/custom-dist").assertIsDir().listFiles().size() == 1
+    }
+
+    def "can cancel distribution download"() {
+        assert distribution.binDistribution.exists() : "bin distribution must exist to run this test, you need to run the :binZip task"
+        def userHomeDir = file("user-home-dir")
+        server.server.setGracefulShutdown(2 * 1000)
+
+        given:
+        settingsFile << "";
+        buildFile << "task hello << { println hello }"
+        CancellationTokenSource tokenSource = new DefaultCancellationTokenSource()
+        CountDownLatch latch = new CountDownLatch(1)
+
+        server.expect("/cancelled-dist.zip", false, ['GET'], new SendDataAndCancelAction("/cancelled-dist.zip", distribution.binDistribution, tokenSource, latch))
+
+        and:
+        toolingApi.withConnector { GradleConnector connector ->
+            connector.useDistribution(URI.create("http://localhost:${server.port}/cancelled-dist.zip"))
+            connector.useGradleUserHomeDir(userHomeDir)
+        }
+
+        when:
+        toolingApi.withConnection { ProjectConnection connection ->
+            BuildLauncher launcher = connection.newBuild().forTasks("hello")
+                .withCancellationToken(tokenSource.token())
+            launcher.run()
+        }
+
+        then:
+        BuildCancelledException e = thrown()
+        e.message.contains('Distribution download cancelled.')
+
+        cleanup:
+        latch.countDown()
+    }
+
+    class SendDataAndCancelAction extends HttpServer.ActionSupport {
+        private final String path
+        private final File srcFile
+        private final CancellationTokenSource tokenSource
+        private final CountDownLatch latch
+
+        SendDataAndCancelAction(String path, File srcFile, CancellationTokenSource tokenSource, CountDownLatch latch) {
+            super("return contents of $srcFile.name")
+            this.srcFile = srcFile
+            this.path = path
+            this.tokenSource = tokenSource
+            this.latch = latch
+        }
+
+        void handle(HttpServletRequest request, HttpServletResponse response) {
+            def file
+            if (request.pathInfo == path) {
+                file = srcFile
+            } else {
+                def relativePath = request.pathInfo.substring(path.length() + 1)
+                file = new File(srcFile, relativePath)
+            }
+            if (file.isFile()) {
+                sendFile(response, file, interaction.contentType)
+            } else {
+                response.sendError(404, "'$request.pathInfo' does not exist")
+            }
+        }
+
+        private sendFile(HttpServletResponse response, File file, String contentType) {
+            response.setContentType(contentType ?: new MimeTypes().getMimeByExtension(file.name).toString())
+
+            def content = file.bytes
+            for (int i = 0; i < content.length; i++) {
+                response.outputStream.write(content[i])
+                if (i == 30000) { // more than one progress tick in output
+                    println('call cancel')
+                    tokenSource.cancel()
+                    println('cancel request processed')
+                    latch.await(10, TimeUnit.SECONDS)
+                    println('cancel request processed')
+                    break;
+                }
+            }
+            println('server handler done.')
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ExternalToolingApiDistribution.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ExternalToolingApiDistribution.groovy
index e8e8520..bac4e9d 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ExternalToolingApiDistribution.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ExternalToolingApiDistribution.groovy
@@ -37,8 +37,7 @@ class ExternalToolingApiDistribution implements ToolingApiDistribution {
     }
     
     ClassLoader getClassLoader() {
-        def classLoaderFactory = new DefaultClassLoaderFactory()
-        classLoaderFactory.createIsolatedClassLoader(classpath.collect { it.toURI() })
+        return new DefaultClassLoaderFactory().createIsolatedClassLoader(classpath*.toURI())
     }
     
     String toString() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TargetGradleVersion.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TargetGradleVersion.java
index 8e623f7..4bee0b6 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TargetGradleVersion.java
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TargetGradleVersion.java
@@ -25,7 +25,7 @@ import java.lang.annotation.*;
 @Inherited
 public @interface TargetGradleVersion {
     /**
-     * The requested target Gradle version. Can use '>=nnn', '<=nnn', '<nnn', '=nnn', 'current' or '!current' or space-separated list of patterns.
+     * The requested target Gradle version. Can use '>=nnn', '<=nnn', '<nnn', '>nnn', '=nnn', 'current' or '!current' or space-separated list of patterns.
      */
     String value();
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TextUtil.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TextUtil.java
index 54b9113..8ec51b7 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TextUtil.java
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/TextUtil.java
@@ -19,7 +19,7 @@ package org.gradle.integtests.tooling.fixture;
 public class TextUtil {
 
     /**
-     * TODO SF - temporary hack to avoid classloading issues. We should use org.gradle.util.TextUtil
+     * TODO - hack to avoid classloading issues. We should use org.gradle.util.TextUtil
      */
     public static String escapeString(Object obj) {
         return obj.toString().replaceAll("\\\\", "\\\\\\\\");
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
index 10367d4..145e02e 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
@@ -18,35 +18,83 @@ package org.gradle.integtests.tooling.fixture
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.launcher.daemon.testing.DaemonLogsAnalyzer
+import org.gradle.launcher.daemon.testing.DaemonsFixture
 import org.gradle.test.fixtures.file.TestDirectoryProvider
+import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.internal.consumer.DefaultGradleConnector
 import org.gradle.util.GradleVersion
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
 import java.util.concurrent.TimeUnit
 
-class ToolingApi {
+class ToolingApi implements TestRule {
     private static final Logger LOGGER = LoggerFactory.getLogger(ToolingApi)
 
     private GradleDistribution dist
     private TestDirectoryProvider testWorkDirProvider
-    private File userHomeDir
+    private TestFile gradleUserHomeDir
+    private TestFile daemonBaseDir
+    private boolean useSeparateDaemonBaseDir
+    private boolean inProcess;
+    private boolean requiresDaemon
+    private boolean requireIsolatedDaemons
 
     private final List<Closure> connectorConfigurers = []
-    boolean isEmbedded
     boolean verboseLogging = LOGGER.debugEnabled
+    List<DefaultGradleConnector> connectors = new ArrayList<DefaultGradleConnector>();
 
     ToolingApi(GradleDistribution dist, TestDirectoryProvider testWorkDirProvider) {
-        this(dist, new IntegrationTestBuildContext().gradleUserHomeDir, testWorkDirProvider, GradleContextualExecuter.embedded)
-    }
-
-    ToolingApi(GradleDistribution dist, File userHomeDir, TestDirectoryProvider testWorkDirProvider, boolean isEmbedded) {
         this.dist = dist
-        this.userHomeDir = userHomeDir
+        def context = new IntegrationTestBuildContext()
+        this.useSeparateDaemonBaseDir = dist.toolingApiDaemonBaseDirSupported && DefaultGradleConnector.metaClass.respondsTo(null, "daemonBaseDir")
+        this.gradleUserHomeDir = context.gradleUserHomeDir
+        this.daemonBaseDir = context.daemonBaseDir
+        this.requiresDaemon = !GradleContextualExecuter.embedded
+        this.inProcess = GradleContextualExecuter.embedded
         this.testWorkDirProvider = testWorkDirProvider
-        this.isEmbedded = isEmbedded
+    }
+
+    /**
+     * Specifies that the test use its own Gradle user home dir and daemon registry.
+     */
+    void requireIsolatedUserHome() {
+        gradleUserHomeDir = testWorkDirProvider.testDirectory.file("user-home-dir")
+        useSeparateDaemonBaseDir = false
+    }
+
+    TestFile getDaemonBaseDir() {
+        return useSeparateDaemonBaseDir ? daemonBaseDir : gradleUserHomeDir.file("daemon")
+    }
+
+    /**
+     * Specifies that the test use real daemon processes (not embedded) and a test-specific daemon registry. Uses a shared Gradle user home dir
+     */
+    void requireIsolatedDaemons() {
+        if (useSeparateDaemonBaseDir) {
+            daemonBaseDir = testWorkDirProvider.testDirectory.file("daemons")
+        } else {
+            gradleUserHomeDir = testWorkDirProvider.testDirectory.file("user-home-dir")
+        }
+        requireIsolatedDaemons = true
+        requiresDaemon = true
+    }
+
+    /**
+     * Specifies that the test use real daemon processes (not embedded).
+     */
+    void requireDaemons() {
+        requiresDaemon = true
+    }
+
+    DaemonsFixture getDaemons() {
+        return DaemonLogsAnalyzer.newAnalyzer(getDaemonBaseDir(), dist.version.version)
     }
 
     void withConnector(Closure cl) {
@@ -94,20 +142,23 @@ class ToolingApi {
     }
 
     GradleConnector connector() {
-        GradleConnector connector = GradleConnector.newConnector()
-        connector.useGradleUserHomeDir(userHomeDir)
+        DefaultGradleConnector connector = GradleConnector.newConnector()
+        connector.useGradleUserHomeDir(new File(gradleUserHomeDir.path))
+        if (useSeparateDaemonBaseDir) {
+            connector.daemonBaseDir(new File(daemonBaseDir.path))
+        }
         connector.forProjectDirectory(testWorkDirProvider.testDirectory)
         connector.searchUpwards(false)
         connector.daemonMaxIdleTime(120, TimeUnit.SECONDS)
         if (connector.metaClass.hasProperty(connector, 'verboseLogging')) {
             connector.verboseLogging = verboseLogging
         }
-        if (isEmbedded) {
-            LOGGER.info("Using embedded tooling API provider");
+        if (!requiresDaemon && GradleVersion.current() == dist.version) {
+            println("Using embedded tooling API provider from ${GradleVersion.current().version} to classpath (${dist.version.version})")
             connector.useClasspathDistribution()
             connector.embedded(true)
         } else {
-            LOGGER.info("Using daemon tooling API provider");
+            println("Using daemon tooling API provider from ${GradleVersion.current().version} to ${dist.version.version}")
             connector.useInstallation(dist.gradleHomeDir.absoluteFile)
             connector.embedded(false)
         }
@@ -116,4 +167,25 @@ class ToolingApi {
         }
         return connector
     }
+
+    @Override
+    Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    if (requireIsolatedDaemons) {
+                        try {
+                            getDaemons().killAll()
+                        } catch (RuntimeException ex) {
+                            //TODO once we figured out why pid from logfile can be null we should remove this again
+                            LOGGER.warn("Unable to kill daemon(s)", ex);
+                        }
+                    }
+                }
+            }
+        };
+    }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiCompatibilitySuiteRunner.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiCompatibilitySuiteRunner.groovy
index f5df166..b6723d4 100755
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiCompatibilitySuiteRunner.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiCompatibilitySuiteRunner.groovy
@@ -138,8 +138,10 @@ class ToolingApiCompatibilitySuiteRunner extends AbstractCompatibilityTestRunner
             sharedClassLoader.allowPackage('spock')
             sharedClassLoader.allowPackage('org.spockframework')
             sharedClassLoader.allowClass(SetSystemProperties)
+            sharedClassLoader.allowClass(RedirectStdOutAndErr)
             sharedClassLoader.allowPackage('org.gradle.integtests.fixtures')
             sharedClassLoader.allowPackage('org.gradle.test.fixtures')
+            sharedClassLoader.allowPackage('org.gradle.launcher.daemon.testing')
             sharedClassLoader.allowClass(OperatingSystem)
             sharedClassLoader.allowClass(Requires)
             sharedClassLoader.allowClass(TestPrecondition)
@@ -151,6 +153,7 @@ class ToolingApiCompatibilitySuiteRunner extends AbstractCompatibilityTestRunner
 
             def testClassPath = []
             testClassPath << ClasspathUtil.getClasspathForClass(target)
+            testClassPath << ClasspathUtil.getClasspathForClass(TestResultHandler)
 
             return new MutableURLClassLoader(parentClassLoader, testClassPath.collect { it.toURI().toURL() })
         }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiDistributionResolver.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiDistributionResolver.groovy
index 551ecb0..979fcf8 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiDistributionResolver.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiDistributionResolver.groovy
@@ -23,13 +23,13 @@ import org.gradle.api.internal.artifacts.DependencyResolutionServices
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
 import org.gradle.internal.concurrent.CompositeStoppable
-import org.gradle.internal.nativeplatform.services.NativeServices
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.internal.service.ServiceRegistryBuilder
 import org.gradle.internal.service.scopes.BuildScopeServices
 import org.gradle.internal.service.scopes.GlobalScopeServices
 import org.gradle.internal.service.scopes.ProjectScopeServices
 import org.gradle.logging.LoggingServiceRegistry
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.gradle.util.TestUtil
 
 class ToolingApiDistributionResolver {
@@ -50,7 +50,7 @@ class ToolingApiDistributionResolver {
     }
 
     ToolingApiDistributionResolver withDefaultRepository() {
-        withRepository("http://repo.gradle.org/gradle/repo")
+        withRepository("https://repo.gradle.org/gradle/repo")
     }
 
     ToolingApiDistribution resolve(String toolingApiVersion) {
@@ -75,7 +75,7 @@ class ToolingApiDistributionResolver {
     private DependencyResolutionServices createResolutionServices() {
         ServiceRegistry globalRegistry = ServiceRegistryBuilder.builder()
                 .parent(LoggingServiceRegistry.newEmbeddableLogging())
-                .parent(NativeServices.getInstance())
+                .parent(NativeServicesTestFixture.getInstance())
                 .provider(new GlobalScopeServices(false))
                 .build()
         StartParameter startParameter = new StartParameter()
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
index 35cb671..38cfca4 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
@@ -18,38 +18,47 @@ package org.gradle.integtests.tooling.fixture
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
 import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
+import org.gradle.test.fixtures.file.TestDistributionDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.internal.consumer.ConnectorServices
 import org.gradle.util.GradleVersion
 import org.gradle.util.SetSystemProperties
 import org.junit.Rule
+import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
 import spock.lang.Specification
 
 /**
- * A spec that executes tests against all compatible versions of tooling API consumer and provider, including the current Gradle version under test.
+ * A spec that executes tests against all compatible versions of tooling API consumer and testDirectoryProvider, including the current Gradle version under test.
  *
  * <p>A test class or test method can be annotated with the following annotations to specify which versions the test is compatible with:
  * </p>
  *
  * <ul>
  *     <li>{@link ToolingApiVersion} - specifies the tooling API consumer versions that the test is compatible with.
- *     <li>{@link TargetGradleVersion} - specifies the tooling API provider versions that the test is compatible with.
+ *     <li>{@link TargetGradleVersion} - specifies the tooling API testDirectoryProvider versions that the test is compatible with.
  * </ul>
  */
 @RunWith(ToolingApiCompatibilitySuiteRunner)
+ at ToolingApiVersion('>=1.2')
+ at TargetGradleVersion('>=1.0-milestone-8')
 abstract class ToolingApiSpecification extends Specification {
-    static final Logger LOGGER = LoggerFactory.getLogger(ToolingApiSpecification)
-    @Rule public final SetSystemProperties sysProperties = new SetSystemProperties()
-    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    @Rule
+    public final SetSystemProperties sysProperties = new SetSystemProperties()
+    public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
     final GradleDistribution dist = new UnderDevelopmentGradleDistribution()
     final IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext()
-    final ToolingApi toolingApi = new ToolingApi(dist, temporaryFolder)
     private static final ThreadLocal<GradleDistribution> VERSION = new ThreadLocal<GradleDistribution>()
 
+    TestDistributionDirectoryProvider temporaryDistributionFolder = new TestDistributionDirectoryProvider();
+    final ToolingApi toolingApi = new ToolingApi(targetDist, temporaryFolder)
+
+    @Rule
+    public RuleChain chain = RuleChain.outerRule(temporaryFolder).around(temporaryDistributionFolder).around(toolingApi);
+
     static void selectTargetDist(GradleDistribution version) {
         VERSION.set(version)
     }
@@ -58,28 +67,19 @@ abstract class ToolingApiSpecification extends Specification {
         VERSION.get()
     }
 
-    void setup() {
-        def consumerGradle = GradleVersion.current()
-        def target = GradleVersion.version(VERSION.get().version.version)
-        LOGGER.info(" Using Tooling API consumer ${consumerGradle}, provider ${target}")
-        this.toolingApi.withConnector {
-            if (consumerGradle.version != target.version) {
-                LOGGER.info("Overriding daemon tooling API provider to use installation: " + target);
-                it.useInstallation(new File(getTargetDist().gradleHomeDir.absolutePath))
-                it.embedded(false)
-            }
-        }
+    void reset() {
+        new ConnectorServices().reset()
     }
 
-    public void withConnector(Closure cl) {
+    public void withConnector(@DelegatesTo(GradleConnector) Closure cl) {
         toolingApi.withConnector(cl)
     }
 
-    public <T> T withConnection(Closure<T> cl) {
+    public <T> T withConnection(@DelegatesTo(ProjectConnection) Closure<T> cl) {
         toolingApi.withConnection(cl)
     }
 
-    public <T> T withConnection(GradleConnector connector, Closure<T> cl) {
+    public <T> T withConnection(GradleConnector connector, @DelegatesTo(ProjectConnection) Closure<T> cl) {
         toolingApi.withConnection(connector, cl)
     }
 
@@ -121,4 +121,77 @@ abstract class ToolingApiSpecification extends Specification {
         projectDir.file(path)
     }
 
+    /**
+     * Returns the set of implicit task names expected for a non-root project for the target Gradle version.
+     */
+    Set<String> getImplicitTasks() {
+        if (GradleVersion.version(targetDist.version.baseVersion.version) >= GradleVersion.version("2.4")) {
+            return ['components', 'dependencies', 'dependencyInsight', 'help', 'projects', 'properties', 'tasks', 'model']
+        } else if (GradleVersion.version(targetDist.version.baseVersion.version) >= GradleVersion.version("2.1")) {
+            return ['components', 'dependencies', 'dependencyInsight', 'help', 'projects', 'properties', 'tasks']
+        } else {
+            return ['dependencies', 'dependencyInsight', 'help', 'projects', 'properties', 'tasks']
+        }
+    }
+
+    /**
+     * Returns the set of implicit selector names expected for a non-root project for the target Gradle version.
+     *
+     * <p>Note that in some versions the handling of implicit selectors was broken, so this method may return a different value
+     * to {@link #getImplicitTasks()}.
+     */
+    Set<String> getImplicitSelectors() {
+        if (GradleVersion.version(targetDist.version.baseVersion.version) <= GradleVersion.version("2.0")) {
+            // Implicit tasks were ignored
+            return []
+        }
+        return getImplicitTasks()
+    }
+
+    /**
+     * Returns the set of implicit task names expected for a root project for the target Gradle version.
+     */
+    Set<String> getRootProjectImplicitTasks() {
+        def targetVersion = GradleVersion.version(targetDist.version.baseVersion.version)
+        if (targetVersion == GradleVersion.version("1.6")) {
+            return implicitTasks + ['setupBuild']
+        }
+        return implicitTasks + ['init', 'wrapper']
+    }
+
+    /**
+     * Returns the set of implicit selector names expected for a root project for the target Gradle version.
+     *
+     * <p>Note that in some versions the handling of implicit selectors was broken, so this method may return a different value
+     * to {@link #getRootProjectImplicitTasks()}.
+     */
+    Set<String> getRootProjectImplicitSelectors() {
+        def targetVersion = GradleVersion.version(targetDist.version.baseVersion.version)
+        if (targetVersion == GradleVersion.version("1.6")) {
+            // Implicit tasks were ignored, and setupBuild was added as a regular task
+            return ['setupBuild']
+        }
+        if (targetVersion <= GradleVersion.version("2.0")) {
+            // Implicit tasks were ignored
+            return []
+        }
+        return rootProjectImplicitTasks
+    }
+
+    /**
+     * Returns the set of implicit tasks returned by GradleProject.getTasks()
+     *
+     * <p>Note that in some versions the handling of implicit tasks was broken, so this method may return a different value
+     * to {@link #getRootProjectImplicitTasks()}.
+     */
+    Set<String> getRootProjectImplicitTasksForGradleProjectModel() {
+        def targetVersion = GradleVersion.version(targetDist.version.baseVersion.version)
+        if (targetVersion == GradleVersion.version("1.6")) {
+            // Implicit tasks were ignored, and setupBuild was added as a regular task
+            return ['setupBuild']
+        }
+
+        targetVersion < GradleVersion.version("2.3") ? [] : rootProjectImplicitTasks
+    }
+
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiVersion.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiVersion.java
index a05b648..b4cf0ef 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiVersion.java
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/fixture/ToolingApiVersion.java
@@ -25,7 +25,7 @@ import java.lang.annotation.*;
 @Inherited
 public @interface ToolingApiVersion {
     /**
-     * The requested tooling API version. Can use '>=nnn', '<=nnn', '<nnn', '=nnn', 'current' or '!current' or space-separated list of patterns.
+     * The requested tooling API version. Can use '>=nnn', '<=nnn', '<nnn', '>nnn', '=nnn', 'current' or '!current' or space-separated list of patterns.
      */
     String value();
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
index 050e653..4305d05 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -169,8 +169,6 @@ dependencies {
         eclipseProject.classpath.collect { it.javadoc?.name } as Set == [null, null] as Set
     }
 
-    //TODO SF: write a test that checks if minimal project has necessary project dependencies
-
     def "can build the minimal Eclipse model for a java project with the idea plugin applied"() {
         
         projectDir.file('build.gradle').text = '''
@@ -286,8 +284,9 @@ project(':c') {
         rootProject.parent == null
 
         rootProject.children.size() == 2
+        def children = rootProject.children.sort { it.name }
 
-        EclipseProject child1 = rootProject.children[0]
+        EclipseProject child1 = children[0]
         child1.name == 'child1'
         child1.parent == rootProject
         child1.children.size() == 1
@@ -297,7 +296,7 @@ project(':c') {
         child1Child1.parent == child1
         child1Child1.children.size() == 0
 
-        EclipseProject child2 = rootProject.children[1]
+        EclipseProject child2 = children[1]
         child2.name == 'child2'
         child2.parent == rootProject
         child2.children.size() == 0
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiLoggingCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiLoggingCrossVersionSpec.groovy
index 38ac731..1081ede 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiLoggingCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m3/ToolingApiLoggingCrossVersionSpec.groovy
@@ -16,31 +16,30 @@
 
 package org.gradle.integtests.tooling.m3
 
+import org.gradle.integtests.tooling.fixture.TestOutputStream
+import org.gradle.integtests.tooling.fixture.TestResultHandler
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.test.fixtures.ConcurrentTestUtil
-import org.gradle.tooling.GradleConnectionException
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
 import org.gradle.tooling.ProjectConnection
-import org.gradle.tooling.ResultHandler
-
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
+import org.junit.Rule
 
 class ToolingApiLoggingCrossVersionSpec extends ToolingApiSpecification {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
     def setup() {
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     def "logging is live"() {
-        def marker = file("marker.txt")
+        def waitingMessage = "logging task: connecting to ${server.uri}"
+        def finishedMessage = "logging task: finished"
 
         file("build.gradle") << """
 task log << {
-    println "waiting"
-    def marker = file('${marker.toURI()}')
-    long timeout = System.currentTimeMillis() + 10000
-    while (!marker.file && System.currentTimeMillis() < timeout) { Thread.sleep(200) }
-    if (!marker.file) { throw new RuntimeException("Timeout waiting for marker file") }
-    println "finished"
+    println "${waitingMessage}"
+    new URL("${server.uri}").text
+    println "${finishedMessage}"
 }
 """
 
@@ -51,53 +50,20 @@ task log << {
             def build = connection.newBuild()
             build.standardOutput = output
             build.forTasks("log")
+            build.withArguments("-d", "-s")
             build.run(resultHandler)
-            ConcurrentTestUtil.poll(10) { output.toString().contains("waiting") }
-            marker.text = 'go!'
+            server.waitFor()
+            ConcurrentTestUtil.poll {
+                // Need to poll, as logging output is delivered asynchronously to client
+                assert output.toString().contains(waitingMessage)
+            }
+            assert !output.toString().contains(finishedMessage)
+            server.release()
             resultHandler.finished()
         }
 
         then:
-        output.toString().contains("waiting")
-        output.toString().contains("finished")
-    }
-
-    class TestResultHandler implements ResultHandler<Object> {
-        final latch = new CountDownLatch(1)
-        def failure
-
-        void onComplete(Object result) {
-            latch.countDown()
-        }
-
-        void onFailure(GradleConnectionException failure) {
-            this.failure = failure
-            latch.countDown()
-        }
-
-        def finished() {
-            latch.await(10, TimeUnit.SECONDS)
-            if (failure != null) {
-                throw failure
-            }
-        }
-    }
-
-    class TestOutputStream extends OutputStream {
-        final buffer = new ByteArrayOutputStream()
-
-        @Override
-        void write(int b) throws IOException {
-            synchronized (buffer) {
-                buffer.write(b)
-            }
-        }
-
-        @Override
-        String toString() {
-            synchronized (buffer) {
-                return buffer.toString()
-            }
-        }
+        output.toString().contains(waitingMessage)
+        output.toString().contains(finishedMessage)
     }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseLinkedResourcesCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseLinkedResourcesCrossVersionSpec.groovy
index 2061b82..595de36 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseLinkedResourcesCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseLinkedResourcesCrossVersionSpec.groovy
@@ -16,13 +16,9 @@
 
 package org.gradle.integtests.tooling.m4
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
 
- at ToolingApiVersion('>=1.0-milestone-4')
- at TargetGradleVersion('>=1.0-milestone-4')
 class ToolingApiEclipseLinkedResourcesCrossVersionSpec extends ToolingApiSpecification {
     def "can build linked resources"() {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseMinimalModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseMinimalModelCrossVersionSpec.groovy
index 52a7e10..b938105 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseMinimalModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m4/ToolingApiEclipseMinimalModelCrossVersionSpec.groovy
@@ -15,11 +15,9 @@
  */
 package org.gradle.integtests.tooling.m4
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
 
- at TargetGradleVersion('>=1.0-milestone-4')
 class ToolingApiEclipseMinimalModelCrossVersionSpec extends ToolingApiSpecification {
     def "minimal Eclipse model does not attempt to resolve external dependencies"() {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/EclipseModelWithFlatRepoCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/EclipseModelWithFlatRepoCrossVersionSpec.groovy
index 57ad728..00acefe 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/EclipseModelWithFlatRepoCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/EclipseModelWithFlatRepoCrossVersionSpec.groovy
@@ -16,12 +16,10 @@
 
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.eclipse.EclipseProject
 import spock.lang.Issue
 
- at TargetGradleVersion('>=1.0-milestone-5')
 class EclipseModelWithFlatRepoCrossVersionSpec extends ToolingApiSpecification {
 
     @Issue("GRADLE-1621")
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
index dbe9248..dc12b73 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
@@ -15,16 +15,12 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.BuildException
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.Task
 import org.gradle.tooling.model.eclipse.EclipseProject
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiBuildExecutionCrossVersionSpec extends ToolingApiSpecification {
     def "can build the set of tasks for a project"() {
         file('build.gradle') << '''
@@ -39,13 +35,12 @@ task c
         GradleProject project = withConnection { connection -> connection.getModel(GradleProject.class) }
 
         then:
+        project.tasks*.name.toSet() == (["a", "b", "c"] + rootProjectImplicitTasksForGradleProjectModel).toSet()
         def taskA = project.tasks.find { it.name == 'a' }
         taskA != null
         taskA.path == ':a'
         taskA.description == 'this is task a'
         taskA.project == project
-        project.tasks.find { it.name == 'b' }
-        project.tasks.find { it.name == 'c' }
     }
 
     def "can execute a build for a project"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildableEclipseModelFixesCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildableEclipseModelFixesCrossVersionSpec.groovy
index d95aa5f..d8c46ae 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildableEclipseModelFixesCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildableEclipseModelFixesCrossVersionSpec.groovy
@@ -15,15 +15,11 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.eclipse.EclipseProject
 import spock.lang.Issue
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiBuildableEclipseModelFixesCrossVersionSpec extends ToolingApiSpecification {
     @Issue("GRADLE-1529")
     //this is just one of the ways of fixing the problem. See the issue for details
@@ -60,10 +56,10 @@ project(':api') {
         then:
         def rootTasks = eclipseProject.gradleProject.tasks.collect { it.name }
 
-        EclipseProject api = eclipseProject.children[1]
+        EclipseProject api = eclipseProject.children.find { it.name == "api" }
         def apiTasks = api.gradleProject.tasks.collect { it.name }
 
-        EclipseProject impl = eclipseProject.children[0]
+        EclipseProject impl = eclipseProject.children.find { it.name == "impl" }
         def implTasks = impl.gradleProject.tasks.collect { it.name }
 
         ['eclipse', 'cleanEclipse', 'eclipseProject', 'cleanEclipseProject'].each {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiEclipseModelCrossVersionSpec.groovy
index a511ac2..d3f03b8 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -15,11 +15,9 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.eclipse.EclipseProject
 
- at ToolingApiVersion('>=1.0-milestone-5')
 class ToolingApiEclipseModelCrossVersionSpec extends ToolingApiSpecification {
     def "eclipse project has access to gradle project and its tasks"() {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiGradleProjectCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiGradleProjectCrossVersionSpec.groovy
index 08cdd50..fda999f 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiGradleProjectCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiGradleProjectCrossVersionSpec.groovy
@@ -15,12 +15,10 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.GradleTask
 
- at ToolingApiVersion('>=1.0-milestone-5')
 class ToolingApiGradleProjectCrossVersionSpec extends ToolingApiSpecification {
 
     def "provides tasks of a project"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiHonorsProjectCustomizationsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiHonorsProjectCustomizationsCrossVersionSpec.groovy
index adbb978..f9cf43a 100755
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiHonorsProjectCustomizationsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiHonorsProjectCustomizationsCrossVersionSpec.groovy
@@ -15,14 +15,10 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.eclipse.EclipseProject
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiHonorsProjectCustomizationsCrossVersionSpec extends ToolingApiSpecification {
 
     def "should honour reconfigured project names"() {
@@ -47,9 +43,10 @@ project(':impl') {
         EclipseProject eclipseProject = withConnection { connection -> connection.getModel(EclipseProject.class) }
 
         then:
-        EclipseProject api = eclipseProject.children[1]
+        def children = eclipseProject.children.sort { it.name }
+        EclipseProject api = children[0]
         assert api.name == 'gradle-api'
-        EclipseProject impl = eclipseProject.children[0]
+        EclipseProject impl = children[1]
         assert impl.name == 'gradle-impl'
     }
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
index 9894ddd..680921d 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
@@ -15,14 +15,10 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.tooling.model.idea.*
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiIdeaModelCrossVersionSpec extends ToolingApiSpecification {
 
     def "builds the model even if idea plugin not applied"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
index 803a0f8..603aed6 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
@@ -15,14 +15,10 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.BuildException
 import org.gradle.tooling.model.GradleProject
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiModelCrossVersionSpec extends ToolingApiSpecification {
     def "receives progress while the model is building"() {
         file('build.gradle') << '''
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiReceivingStandardStreamsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiReceivingStandardStreamsCrossVersionSpec.groovy
index 3820282..5c5ba76 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiReceivingStandardStreamsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiReceivingStandardStreamsCrossVersionSpec.groovy
@@ -15,19 +15,15 @@
  */
 package org.gradle.integtests.tooling.m5
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.GradleProject
 
- at ToolingApiVersion('>=1.0-milestone-5')
- at TargetGradleVersion('>=1.0-milestone-5')
 class ToolingApiReceivingStandardStreamsCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //because embedded tooling api should not replace system out / err
         //we will run below tests only for forked mode
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     def "receives standard streams while the build is executing"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/UnsupportedModelFeedbackCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/UnsupportedModelFeedbackCrossVersionSpec.groovy
deleted file mode 100644
index 2572087..0000000
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m5/UnsupportedModelFeedbackCrossVersionSpec.groovy
+++ /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.integtests.tooling.m5
-
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.tooling.UnknownModelException
-import org.gradle.tooling.model.idea.BasicIdeaProject
-import org.gradle.tooling.model.idea.IdeaProject
-
- at TargetGradleVersion('<=1.0-milestone-4')
-class UnsupportedModelFeedbackCrossVersionSpec extends ToolingApiSpecification {
-    def "fails gracefully when unsupported model requested"() {
-        when:
-        withConnection { it.getModel(model) }
-
-        then:
-        UnknownModelException e = thrown()
-        e.message.contains(model.simpleName)
-
-        where:
-        model << [IdeaProject, BasicIdeaProject]
-    }
-}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/BuildEnvironmentModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/BuildEnvironmentModelCrossVersionSpec.groovy
index 635bc0b..474e2a1 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/BuildEnvironmentModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/BuildEnvironmentModelCrossVersionSpec.groovy
@@ -18,12 +18,9 @@ package org.gradle.integtests.tooling.m8
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
 
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('>=1.0-milestone-8')
 class BuildEnvironmentModelCrossVersionSpec extends ToolingApiSpecification {
 
     def "informs about build environment"() {
@@ -36,9 +33,19 @@ class BuildEnvironmentModelCrossVersionSpec extends ToolingApiSpecification {
         !model.java.jvmArguments.empty
     }
 
+    @TargetGradleVersion("<1.0-milestone-8")
+    def "partial BuildEnvironment model for pre 1.0m8 providers"() {
+        when:
+        BuildEnvironment buildEnv = withConnection { it.getModel(BuildEnvironment.class) }
+
+        then:
+        buildEnv != null
+        buildEnv.gradle.gradleVersion == targetDist.version.version
+    }
+
     def "informs about java args as in the build script"() {
         given:
-        toolingApi.isEmbedded = false //cannot be run in embedded mode
+        toolingApi.requireDaemons() //cannot be run in embedded mode
 
         file('build.gradle') <<
             "project.description = java.lang.management.ManagementFactory.runtimeMXBean.inputArguments.join('##')"
@@ -52,10 +59,26 @@ class BuildEnvironmentModelCrossVersionSpec extends ToolingApiSpecification {
         env.java.jvmArguments.each { inputArgsInBuild.contains(it) }
     }
 
+    @TargetGradleVersion(">=1.0-milestone-8 <=1.0-milestone-9")
+    def "informs about java home as in the build script for older versions"() {
+        given:
+        file('build.gradle') << """
+        description = org.gradle.util.Jvm.current().javaHome.toString()
+        """
+
+        when:
+        BuildEnvironment env = withConnection { it.getModel(BuildEnvironment.class) }
+        GradleProject project = withConnection { it.getModel(GradleProject.class) }
+
+        then:
+        env.java.javaHome.toString() == project.description
+    }
+
+    @TargetGradleVersion(">1.0-milestone-9")
     def "informs about java home as in the build script"() {
         given:
         file('build.gradle') << """
-        description = Jvm.current().javaHome.toString()
+        description = org.gradle.internal.jvm.Jvm.current().javaHome.toString()
         """
 
         when:
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ConsumingStandardInputCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ConsumingStandardInputCrossVersionSpec.groovy
index 3346da2..5e65daa 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ConsumingStandardInputCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ConsumingStandardInputCrossVersionSpec.groovy
@@ -16,20 +16,16 @@
 
 package org.gradle.integtests.tooling.m8
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.GradleProject
 import spock.lang.Timeout
 
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('>=1.0-milestone-8')
 class ConsumingStandardInputCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //since this test treats with standard input I will not run it for embedded daemon for safety.
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     @Timeout(90)
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/JavaConfigurabilityCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/JavaConfigurabilityCrossVersionSpec.groovy
index e2d4468..f8e98a6 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/JavaConfigurabilityCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/JavaConfigurabilityCrossVersionSpec.groovy
@@ -16,22 +16,18 @@
 
 package org.gradle.integtests.tooling.m8
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
 import spock.lang.Issue
 import spock.lang.Timeout
 
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('>=1.0-milestone-8')
 class JavaConfigurabilityCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //this test does not make any sense in embedded mode
         //as we don't own the process
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     def "configures the java settings"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/StrictLongRunningOperationCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/StrictLongRunningOperationCrossVersionSpec.groovy
deleted file mode 100644
index 65ec7a0..0000000
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/StrictLongRunningOperationCrossVersionSpec.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.integtests.tooling.m8
-
-import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
-import org.gradle.tooling.model.build.BuildEnvironment
-import spock.lang.IgnoreIf
-
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('<=1.0-milestone-7')
-class StrictLongRunningOperationCrossVersionSpec extends ToolingApiSpecification {
-
-    def setup() {
-        //this test does not make any sense in embedded mode
-        //as we don't own the process and we will attempt to configure system wide options.
-        toolingApi.isEmbedded = false
-    }
-
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
-    def "fails eagerly when java home unsupported for model"() {
-        def java = AvailableJavaHomes.bestAlternative
-        when:
-        withConnection {
-            def model = it.model(BuildEnvironment.class)
-            model.setJavaHome(java)
-            model.get()
-        }
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        assertExceptionInformative(e, "setJavaHome()")
-    }
-
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
-    def "fails eagerly when java home unsupported for build"() {
-        def java = AvailableJavaHomes.bestAlternative
-        when:
-        withConnection {
-            def build = it.newBuild()
-            build.setJavaHome(java)
-            build.forTasks('tasks').run()
-        }
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        assertExceptionInformative(e, "setJavaHome()")
-    }
-
-    def "fails eagerly when java args unsupported"() {
-        when:
-        withConnection {
-            def model = it.model(BuildEnvironment.class)
-            model.setJvmArguments("-Xmx512m")
-            model.get()
-        }
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        assertExceptionInformative(e, "setJvmArguments()")
-    }
-
-    def "fails eagerly when standard input unsupported"() {
-        when:
-        withConnection {
-            def model = it.model(BuildEnvironment.class)
-            model.setStandardInput(new ByteArrayInputStream('yo!'.bytes))
-            model.get()
-        }
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        assertExceptionInformative(e, "setStandardInput()")
-    }
-
-    void assertExceptionInformative(UnsupportedOperationConfigurationException actual, String expectedMessageSubstring) {
-        assert actual.message.contains(expectedMessageSubstring)
-    }
-}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiEclipseModelCrossVersionSpec.groovy
index df96e11..2421d92 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -15,13 +15,9 @@
  */
 package org.gradle.integtests.tooling.m8
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.eclipse.EclipseProject
 
- at ToolingApiVersion('>=1.0-milestone-3')
- at TargetGradleVersion('>=1.0-milestone-8')
 class ToolingApiEclipseModelCrossVersionSpec extends ToolingApiSpecification {
     def "can customize model late in the configuration phase"() {
         projectDir.file('build.gradle').text = """
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiLoggingCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiLoggingCrossVersionSpec.groovy
index f8c78ac..1b3d88a 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiLoggingCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/ToolingApiLoggingCrossVersionSpec.groovy
@@ -16,24 +16,21 @@
 
 package org.gradle.integtests.tooling.m8
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.tooling.internal.consumer.ConnectorServices
 import org.junit.Assume
 
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('>=1.0-milestone-8')
 class ToolingApiLoggingCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //for embedded tests we don't mess with global logging. Run with forks only.
-        toolingApi.isEmbedded = false
-        new ConnectorServices().reset()
+        toolingApi.requireDaemons()
+        reset()
     }
 
     def cleanup() {
-        new ConnectorServices().reset()
+        reset()
     }
 
     def "client receives same stdout and stderr when in verbose mode as if running from the command-line in debug mode"() {
@@ -91,7 +88,7 @@ project.logger.info ("info logging");
 project.logger.debug("debug logging");
 """
         when:
-        def commandLineResult = targetDist.executer(temporaryFolder).run();
+        def commandLineResult = runUsingCommandLine();
 
         and:
         def op = withBuild()
@@ -99,7 +96,7 @@ project.logger.debug("debug logging");
         then:
         def out = op.standardOutput
         def err = op.standardError
-        normaliseOutput(filterToolingApiSpecific(out)) == normaliseOutput(commandLineResult.output)
+        normaliseOutput(out) == normaliseOutput(commandLineResult.output)
         err == commandLineResult.error
 
         and:
@@ -114,16 +111,23 @@ project.logger.debug("debug logging");
         out.count("debug") == 0
     }
 
-    String normaliseOutput(String output) {
-        return output.replaceFirst("Total time: .+ secs", "Total time: 0 secs")
+    private ExecutionResult runUsingCommandLine() {
+        def executer = targetDist.executer(temporaryFolder)
+        if (!GradleContextualExecuter.longLivingProcess) {
+            //suppress daemon usage suggestions
+            executer.withArgument("--no-daemon")
+        }
+        executer.withGradleOpts("-Dorg.gradle.daemon.disable-starting-message=true")
+        executer.run()
     }
 
-    String filterToolingApiSpecific(String output) {
-        return output.replaceFirst("Connection from tooling API older than version 1.2 has been deprecated and is scheduled to be removed in Gradle 2.0" + System.getProperty("line.separator"), "")
+    String normaliseOutput(String output) {
+        return output.replaceFirst("Total time: .+ secs", "Total time: 0 secs")
     }
 
     void shouldNotContainProviderLogging(String output) {
         assert !output.contains("Provider implementation created.")
         assert !output.contains("Tooling API uses target gradle version:")
+        assert !output.contains("Tooling API is using target Gradle version:")
     }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/VersionOnlyBuildEnvironmentCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/VersionOnlyBuildEnvironmentCrossVersionSpec.groovy
deleted file mode 100644
index 18ff125..0000000
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m8/VersionOnlyBuildEnvironmentCrossVersionSpec.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.integtests.tooling.m8
-
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.tooling.model.UnsupportedMethodException
-import org.gradle.tooling.model.build.BuildEnvironment
-
- at ToolingApiVersion('>=1.0-milestone-8')
- at TargetGradleVersion('<=1.0-milestone-7')
-class VersionOnlyBuildEnvironmentCrossVersionSpec extends ToolingApiSpecification {
-
-    def "informs about version"() {
-        when:
-        BuildEnvironment model = withConnection { it.getModel(BuildEnvironment.class) }
-
-        then:
-        model.gradle.gradleVersion == targetDist.version.version
-    }
-
-    def "fails gracefully for other info"() {
-        given:
-        BuildEnvironment model = withConnection { it.getModel(BuildEnvironment.class) }
-
-        when:
-        model.java.javaHome
-        then:
-        def ex = thrown(UnsupportedMethodException)
-        ex instanceof UnsupportedOperationException //backwards compatibility
-
-        when:
-        model.java.jvmArguments
-        then:
-        def e = thrown(UnsupportedMethodException)
-        e instanceof UnsupportedOperationException //backwards compatibility
-    }
-}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/DaemonErrorFeedbackCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/DaemonErrorFeedbackCrossVersionSpec.groovy
index 151d34a..f579363 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/DaemonErrorFeedbackCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/DaemonErrorFeedbackCrossVersionSpec.groovy
@@ -17,13 +17,11 @@
 package org.gradle.integtests.tooling.m9
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.GradleConnectionException
 import spock.lang.Issue
 import spock.lang.Timeout
 
- at ToolingApiVersion('>=1.0-milestone-9')
 @TargetGradleVersion('>=1.0-milestone-9')
 class DaemonErrorFeedbackCrossVersionSpec extends ToolingApiSpecification {
 
@@ -32,7 +30,7 @@ class DaemonErrorFeedbackCrossVersionSpec extends ToolingApiSpecification {
     def "promptly discovers rubbish jvm arguments"() {
         //jvm arguments cannot be set for an existing process
         //so we must not run in embedded mode
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
 
         when:
         withConnection {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/GradlePropertiesToolingApiCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/GradlePropertiesToolingApiCrossVersionSpec.groovy
index fa5672f..d47d69c 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/GradlePropertiesToolingApiCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/GradlePropertiesToolingApiCrossVersionSpec.groovy
@@ -20,18 +20,16 @@ import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.TextUtil
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.build.BuildEnvironment
 import spock.lang.IgnoreIf
 
- at ToolingApiVersion('>=1.0-milestone-9')
 @TargetGradleVersion('>=1.0-milestone-9')
 class GradlePropertiesToolingApiCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //this test does not make any sense in embedded mode
         //as we don't own the process
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     def "tooling api honours jvm args specified in gradle.properties"() {
@@ -51,9 +49,9 @@ assert System.getProperty('some-prop') == 'some-value'
         env.java.jvmArguments.contains('-Xmx16m')
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null })
     def "tooling api honours java home specified in gradle.properties"() {
-        File javaHome = AvailableJavaHomes.bestAlternative
+        File javaHome = AvailableJavaHomes.differentJdk.javaHome
         String javaHomePath = TextUtil.escapeString(javaHome.canonicalPath)
 
         file('build.gradle') << "assert new File(System.getProperty('java.home')).canonicalPath.startsWith('$javaHomePath')"
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/M9JavaConfigurabilityCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/M9JavaConfigurabilityCrossVersionSpec.groovy
index 94b183f..933a769 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/M9JavaConfigurabilityCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/m9/M9JavaConfigurabilityCrossVersionSpec.groovy
@@ -20,23 +20,20 @@ import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.TextUtil
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
-import org.gradle.util.GradleVersion
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 import spock.lang.Timeout
 
- at ToolingApiVersion('>=1.0-milestone-9')
 @TargetGradleVersion('>=1.0-milestone-9')
 class M9JavaConfigurabilityCrossVersionSpec extends ToolingApiSpecification {
 
     def setup() {
         //this test does not make any sense in embedded mode
         //as we don't own the process
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
     }
 
     def "uses sensible java defaults if nulls configured"() {
@@ -87,13 +84,13 @@ class M9JavaConfigurabilityCrossVersionSpec extends ToolingApiSpecification {
         env.java.jvmArguments == env3.java.jvmArguments
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null })
     def "customized java home is reflected in the java.home and the build model"() {
         given:
         file('build.gradle') << "project.description = new File(System.getProperty('java.home')).canonicalPath"
 
         when:
-        File javaHome = AvailableJavaHomes.bestAlternative
+        File javaHome = AvailableJavaHomes.differentJdk.javaHome
         BuildEnvironment env
         GradleProject project
         withConnection {
@@ -105,11 +102,11 @@ class M9JavaConfigurabilityCrossVersionSpec extends ToolingApiSpecification {
         project.description.startsWith(env.java.javaHome.canonicalPath)
     }
 
-    @IgnoreIf({ AvailableJavaHomes.bestAlternative == null })
+    @IgnoreIf({ AvailableJavaHomes.differentJdk == null })
     def "tooling api provided java home takes precedence over gradle.properties"() {
-        File javaHome = AvailableJavaHomes.bestAlternative
+        File javaHome = AvailableJavaHomes.differentJdk.javaHome
         String javaHomePath = TextUtil.escapeString(javaHome.canonicalPath)
-        File otherJava = getOtherJava()
+        File otherJava = new File(System.getProperty("java.home"))
         String otherJavaPath = TextUtil.escapeString(otherJava.canonicalPath)
         file('build.gradle') << "assert new File(System.getProperty('java.home')).canonicalPath.startsWith('$javaHomePath')"
         file('gradle.properties') << "org.gradle.java.home=$otherJavaPath"
@@ -127,15 +124,4 @@ class M9JavaConfigurabilityCrossVersionSpec extends ToolingApiSpecification {
         env.java.javaHome == javaHome
         env.java.javaHome != otherJava
     }
-
-    // We use different ways of resolving JVM depending on the Gradle version
-    // this is necessary as we moved the Jvm class and dont ship the org.gradle.util.Jvm class with the
-    // toolingApi jar
-    File getOtherJava() {
-        if (GradleVersion.current().compareTo(GradleVersion.version("1.0-milestone-9")) > 0) {
-            return org.gradle.internal.jvm.Jvm.current().getJavaHome()
-        } else {
-            return org.gradle.util.Jvm.current().getJavaHome();
-        }
-    }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r10rc1/PassingCommandLineArgumentsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r10rc1/PassingCommandLineArgumentsCrossVersionSpec.groovy
index 5252f14..cf428e7 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r10rc1/PassingCommandLineArgumentsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r10rc1/PassingCommandLineArgumentsCrossVersionSpec.groovy
@@ -20,12 +20,10 @@ package org.gradle.integtests.tooling.r10rc1
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException
 import org.gradle.tooling.model.GradleProject
 
- at ToolingApiVersion(">=1.0")
 @TargetGradleVersion(">=1.0")
 class PassingCommandLineArgumentsCrossVersionSpec extends ToolingApiSpecification {
 
@@ -82,7 +80,7 @@ class PassingCommandLineArgumentsCrossVersionSpec extends ToolingApiSpecificatio
 
     def "can use custom log level"() {
         //logging infrastructure is not installed when running in-process to avoid issues
-        toolingApi.isEmbedded = false
+        toolingApi.requireDaemons()
 
         given:
         file("build.gradle") << """
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/BuildInvocationsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/BuildInvocationsCrossVersionSpec.groovy
index de6b15a..3e87cbd 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/BuildInvocationsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/BuildInvocationsCrossVersionSpec.groovy
@@ -20,14 +20,8 @@ import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.BuildLauncher
-import org.gradle.tooling.UnknownModelException
 import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException
-import org.gradle.tooling.model.GradleProject
-import org.gradle.tooling.model.GradleTask
-import org.gradle.tooling.model.Launchable
-import org.gradle.tooling.model.Task
-import org.gradle.tooling.model.TaskSelector
-import org.gradle.tooling.model.UnsupportedMethodException
+import org.gradle.tooling.model.*
 import org.gradle.tooling.model.gradle.BuildInvocations
 
 @ToolingApiVersion(">=1.12")
@@ -64,7 +58,7 @@ project(':b:c') {
     }
 
     @TargetGradleVersion(">=1.8 <=1.11")
-    def "no task selectors when running action in older container"() {
+    def "cannot fetch task selectors from action in older target version"() {
         when:
         withConnection { connection -> connection.action(new FetchAllTaskSelectorsBuildAction()).run() }
 
@@ -82,16 +76,14 @@ project(':b:c') {
         then:
         result != null
         result.keySet() == ['test', 'a', 'b', 'c'] as Set
-        result['test'] == ['t1', 't2', 't3'] as Set
-        result['b'] == ['t1', 't2', 't3'] as Set
-        result['c'] == ['t1', 't2'] as Set
-        result['a'].isEmpty()
+        result['test'] == rootProjectImplicitSelectors + ['t1', 't2', 't3'] as Set
+        result['b'] == implicitSelectors + ['t1', 't2', 't3'] as Set
+        result['c'] == implicitSelectors + ['t1', 't2'] as Set
+        result['a'] == implicitSelectors
     }
 
     @TargetGradleVersion(">=1.12")
-    def "build task selectors from action"() {
-        given:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
+    def "can run build using task selectors from action"() {
         when:
         BuildInvocations projectSelectors = withConnection { connection ->
             connection.action(new FetchTaskSelectorsBuildAction('b')).run() }
@@ -102,24 +94,10 @@ project(':b:c') {
 
         then:
         result.result.assertTasksExecuted(':b:c:t1')
-
-        when:
-        BuildInvocations rootProjectSelectors = withConnection { connection ->
-            connection.action(new FetchTaskSelectorsBuildAction('test')).run() }
-        TaskSelector rootSelector = rootProjectSelectors.taskSelectors.find { it -> it.name == 't1'}
-        withBuild { BuildLauncher it ->
-            it.forLaunchables(selector, rootSelector)
-        }
-
-        then:
-        UnsupportedBuildArgumentException e = thrown()
-        e.message.contains('Problem with provided launchable arguments')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
-    def "build task selectors from connection"() {
+    def "can run build using task selectors from connection"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         BuildInvocations model = withConnection { connection ->
             connection.getModel(BuildInvocations)
         }
@@ -134,10 +112,8 @@ project(':b:c') {
         result.result.assertTasksExecuted(':t1', ':b:c:t1')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
     def "build task selectors from connection in specified order"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         BuildInvocations model = withConnection { connection ->
             connection.getModel(BuildInvocations)
         }
@@ -157,55 +133,74 @@ project(':b:c') {
         result.result.assertTasksExecuted(':b:t2', ':b:c:t2', ':t1', ':b:c:t1')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
-    def "can request task selectors for project"() {
-        given:
+    def "can fetch task selectors for root project from connection"() {
+        when:
         BuildInvocations model = withConnection { connection ->
             connection.getModel(BuildInvocations)
         }
 
-        when:
-        def selectors = model.taskSelectors.findAll { TaskSelector it ->
-            !it.description.startsWith(':') && it.name != 'setupBuild' // synthetic task in 1.6
-        }
         then:
-        selectors*.name as Set == ['t1', 't2', 't3'] as Set
+        model.taskSelectors*.name as Set == rootProjectImplicitSelectors + ['t1', 't2', 't3'] as Set
     }
 
-    @TargetGradleVersion("<1.0-milestone-5")
-    def "cannot request BuildInvocations for old project"() {
+    @TargetGradleVersion("=1.12")
+    def "can fetch tasks for project using action"() {
         when:
-        withConnection { connection ->
-            connection.getModel(BuildInvocations)
+        List<Task> tasks = withConnection { connection ->
+            connection.action(new FetchTasksBuildAction(':b')).run()
         }
 
         then:
-        UnknownModelException e = thrown()
-        e.message.contains('does not support building a model of type \'' + BuildInvocations.simpleName + '\'')
+        tasks.size() == 2
+        tasks*.name as Set == ['t2', 't3'] as Set
+
+        when:
+        tasks[0].project
+        then:
+        UnsupportedMethodException e = thrown()
+        e != null
     }
 
-    @TargetGradleVersion(">=1.12")
-    def "get tasks for projects"() {
+    @TargetGradleVersion(">=2.0")
+    def "can fetch tasks including implicit for project using action"() {
+        def projectBExpectedTasks = (['t2', 't3'] + implicitTasks) as Set
+        def rootProjectExpectedTasks = (['t1'] + rootProjectImplicitTasks) as Set
+
         when:
         List<Task> tasks = withConnection { connection ->
             connection.action(new FetchTasksBuildAction(':b')).run()
         }
 
         then:
-        tasks.size() == 2
-        tasks*.name as Set == ['t2', 't3'] as Set
+        tasks.size() == projectBExpectedTasks.size()
+        tasks*.name as Set == projectBExpectedTasks
 
         when:
         tasks[0].project
+
         then:
         UnsupportedMethodException e = thrown()
         e != null
+
+        when:
+        tasks = withConnection { connection ->
+            connection.action(new FetchTasksBuildAction(':')).run()
+        }
+
+        then:
+        tasks.size() == rootProjectExpectedTasks.size()
+        tasks*.name as Set == rootProjectExpectedTasks
+
+        when:
+        tasks[0].project
+        then:
+        e = thrown()
+        e != null
     }
 
     @TargetGradleVersion(">=1.12")
     def "build tasks from BuildInvocations model as Launchable"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         List<Task> tasks = withConnection { connection ->
             connection.action(new FetchTasksBuildAction(':b')).run()
         }
@@ -219,10 +214,8 @@ project(':b:c') {
         result.result.assertTaskNotExecuted(':b:c:t2')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
     def "build task from connection as Launchable"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         BuildInvocations model = withConnection { connection ->
             connection.getModel(BuildInvocations)
         }
@@ -237,10 +230,8 @@ project(':b:c') {
         result.result.assertTasksExecuted(':t1')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
     def "build tasks Launchables in order"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         GradleProject model = withConnection { connection ->
             connection.getModel(GradleProject)
         }
@@ -250,6 +241,7 @@ project(':b:c') {
         def result = withBuild { BuildLauncher it ->
             it.forLaunchables(taskT1, taskBT2, taskBCT1)
         }
+
         then:
         result.result.assertTasksExecuted(':t1', ':b:t2', ':b:c:t1')
 
@@ -264,7 +256,6 @@ project(':b:c') {
     @TargetGradleVersion(">=1.12")
     def "build tasks and selectors in order"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         GradleProject model = withConnection { connection ->
             connection.getModel(GradleProject)
         }
@@ -281,10 +272,8 @@ project(':b:c') {
         result.result.assertTasksExecuted(':b:c:t1', ':b:t3', ':t1')
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
     def "build tasks and selectors in order cross version"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         GradleProject model = withConnection { connection ->
             connection.getModel(GradleProject)
         }
@@ -302,10 +291,9 @@ project(':b:c') {
         result.result.assertTasksExecuted(':t1', ':b:c:t1', ':b:t2', ':b:c:t2')
     }
 
-    @TargetGradleVersion(">=1.12")
+    @TargetGradleVersion("=1.12")
     def "build fails with selectors from different projects"() {
         when:
-        toolingApi.isEmbedded = false // to load launchables using correct classloader in integTest
         BuildInvocations rootSelectors = withConnection { connection ->
             connection.action(new FetchTaskSelectorsBuildAction('test')).run()
         }
@@ -323,7 +311,26 @@ project(':b:c') {
         e.message.contains 'Only selector from the same Gradle project can be built.'
     }
 
-    @TargetGradleVersion(">=1.0-milestone-5")
+    @TargetGradleVersion(">=2.0")
+    def "builds selectors from different projects"() {
+        when:
+        BuildInvocations rootSelectors = withConnection { connection ->
+            connection.action(new FetchTaskSelectorsBuildAction('test')).run()
+        }
+        BuildInvocations bSelectors = withConnection { connection ->
+            connection.action(new FetchTaskSelectorsBuildAction('b')).run()
+        }
+        TaskSelector selectorT1 = rootSelectors.taskSelectors.find { it.name == 't1' }
+        TaskSelector selectorBT1 = bSelectors.taskSelectors.find { it.name == 't1' }
+        TaskSelector selectorBT3 = bSelectors.taskSelectors.find { it.name == 't3' }
+        def result = withBuild { BuildLauncher it ->
+            it.forLaunchables(selectorBT1, selectorBT3, selectorT1)
+        }
+        then:
+        result.result.assertTasksExecuted(':b:c:t1', ':b:t3', ':t1')
+    }
+
+    @TargetGradleVersion(">=1.0-milestone-8")
     def "can request tasks for root project"() {
         // TODO make sure it is for root project if default project is different
 
@@ -332,13 +339,11 @@ project(':b:c') {
             connection.getModel(BuildInvocations)
         }
 
-        expect:
-        model.tasks.count { it.name != 'setupBuild' } == 1
-
         when:
-        def task = model.tasks.find { Task it -> it.name != 'setupBuild' }
+        def task = model.tasks.find { !rootProjectImplicitTasks.contains(it.name) }
 
         then:
+        task != null
         task.name == 't1'
         task.path == ':t1'
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/PublicationsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/PublicationsCrossVersionSpec.groovy
index 90fae77..c961707 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/PublicationsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/PublicationsCrossVersionSpec.groovy
@@ -25,6 +25,16 @@ import org.gradle.tooling.model.gradle.ProjectPublications
 @ToolingApiVersion('>=1.12')
 @TargetGradleVersion('>=1.12')
 class PublicationsCrossVersionSpec extends ToolingApiSpecification {
+    def "empty project"() {
+        when:
+        ProjectPublications publications = withConnection { connection ->
+            connection.getModel(ProjectPublications)
+        }
+
+        then:
+        publications.publications.empty
+    }
+
     def "project without any configured publications"() {
         buildFile << "apply plugin: 'java'"
 
@@ -48,7 +58,7 @@ group = "test.group"
 
 uploadArchives {
     repositories {
-        ivy { url uri("\$buildDir/ivy-repo") }
+        ivy { url uri("ivy-repo") }
     }
 }
 """
@@ -79,7 +89,7 @@ group = "test.group"
 uploadArchives {
     repositories {
         mavenDeployer {
-            repository(url: uri("\$buildDir/maven-repo"))
+            repository(url: uri("maven-repo"))
         }
     }
 }
@@ -111,7 +121,7 @@ group = "test.group"
 uploadArchives {
     repositories {
         mavenDeployer {
-            repository(url: uri("\$buildDir/maven-repo"))
+            repository(url: uri("maven-repo"))
             pom.groupId = "test.groupId"
             pom.artifactId = "test.artifactId"
             pom.version = "1.1"
@@ -147,8 +157,8 @@ group = "test.group"
 
 publishing {
     repositories {
-        ivy { url uri("\$buildDir/ivy-repo") }
-        maven { url uri("\$buildDir/maven-repo") }
+        ivy { url uri("ivy-repo") }
+        maven { url uri("maven-repo") }
     }
     publications {
         mainIvy(IvyPublication) {
@@ -188,7 +198,7 @@ publishing {
         pub2.id.version == "1.2"
     }
 
-    @TargetGradleVersion('<1.12')
+    @TargetGradleVersion('>=1.0-milestone-8 <1.12')
     def "decent error message for Gradle version that doesn't expose publications"() {
         when:
         ProjectPublications publications = withConnection { connection ->
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TaskDisplayNameCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TaskDisplayNameCrossVersionSpec.groovy
index bf9c62d..724a517 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TaskDisplayNameCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TaskDisplayNameCrossVersionSpec.groovy
@@ -15,14 +15,12 @@
  */
 package org.gradle.integtests.tooling.r112
 
-import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.GradleProject
 
 @ToolingApiVersion('>=1.12')
 class TaskDisplayNameCrossVersionSpec extends ToolingApiSpecification {
-    @TargetGradleVersion('>=1.0-milestone-5') // Task.path is broken on 1.0-m3
     def "can get task's display name"() {
         file('build.gradle') << '''
 task a
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TestFilteringCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TestFilteringCrossVersionSpec.groovy
index d74d829..8ea68b6 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TestFilteringCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/TestFilteringCrossVersionSpec.groovy
@@ -17,10 +17,8 @@ package org.gradle.integtests.tooling.r112
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import spock.lang.Issue
 
- at ToolingApiVersion(">=1.0")
 @TargetGradleVersion(">=1.10")
 class TestFilteringCrossVersionSpec extends ToolingApiSpecification {
     @Issue("GRADLE-2972")
@@ -28,7 +26,7 @@ class TestFilteringCrossVersionSpec extends ToolingApiSpecification {
         buildFile << """
             apply plugin: 'java'
             repositories { mavenCentral() }
-            dependencies { testCompile 'junit:junit:4.11' }
+            dependencies { testCompile 'junit:junit:4.12' }
             compileTestJava.options.fork = true
         """
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/ToolingApiDeprecationsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/ToolingApiDeprecationsCrossVersionSpec.groovy
index 9758f89..94dd8cf 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/ToolingApiDeprecationsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/ToolingApiDeprecationsCrossVersionSpec.groovy
@@ -19,8 +19,9 @@ package org.gradle.integtests.tooling.r112
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.ProjectConnection
-import org.gradle.tooling.model.GradleProject
+import org.gradle.tooling.UnsupportedVersionException
 import org.gradle.tooling.model.eclipse.EclipseProject
 
 class ToolingApiDeprecationsCrossVersionSpec extends ToolingApiSpecification {
@@ -34,7 +35,7 @@ task noop << {
 
     @ToolingApiVersion(">=1.12")
     @TargetGradleVersion("<1.0-milestone-8")
-    def "build shows deprecation warning for pre 1.0m8 providers"() {
+    def "build rejected for pre 1.0m8 providers"() {
         when:
         def output = new ByteArrayOutputStream()
         withConnection { ProjectConnection connection ->
@@ -45,12 +46,13 @@ task noop << {
         }
 
         then:
-        output.toString().contains(deprecationMessageProvider(targetDist.version.version))
+        UnsupportedVersionException e = thrown()
+        e.message == "Support for Gradle version ${targetDist.version.version} was removed in tooling API version 2.0. You should upgrade your Gradle build to use Gradle 1.0-milestone-8 or later."
     }
 
     @ToolingApiVersion(">=1.12")
     @TargetGradleVersion("<1.0-milestone-8")
-    def "model retrieving shows deprecation warning for pre 1.0m8 providers"() {
+    def "model retrieving fails for pre 1.0m8 providers"() {
         when:
         def output = new ByteArrayOutputStream()
         withConnection { ProjectConnection connection ->
@@ -60,63 +62,13 @@ task noop << {
         }
 
         then:
-        output.toString().contains(deprecationMessageProvider(targetDist.version.version))
-    }
-
-    @ToolingApiVersion(">=1.12")
-    @TargetGradleVersion(">=1.0-milestone-8")
-    def "build shows no deprecation warning for 1.0m8+ providers"() {
-        when:
-        def output = new ByteArrayOutputStream()
-        withConnection { ProjectConnection connection ->
-            def build = connection.newBuild()
-            build.standardOutput = output
-            build.forTasks("noop")
-            build.run()
-        }
-
-        then:
-        !output.toString().contains(deprecationMessageProvider(targetDist.version.version))
-    }
-
-    @ToolingApiVersion(">=1.12")
-    @TargetGradleVersion(">=1.0-milestone-8")
-    def "model retrieving shows no deprecation warning for 1.0m8+ providers"() {
-        when:
-        def output = new ByteArrayOutputStream()
-        withConnection { ProjectConnection connection ->
-            def modelBuilder = connection.model(GradleProject)
-            modelBuilder.standardOutput = output
-            modelBuilder.get()
-        }
-
-        then:
-        !output.toString().contains(deprecationMessageProvider(targetDist.version.version))
-    }
-
-    def deprecationMessageProvider(def version) {
-        "Connecting to Gradle version " + version + " from the Gradle tooling API has been deprecated and is scheduled to be removed in version 2.0 of the Gradle tooling API"
+        UnsupportedVersionException e = thrown()
+        e.message == "Support for Gradle version ${targetDist.version.version} was removed in tooling API version 2.0. You should upgrade your Gradle build to use Gradle 1.0-milestone-8 or later."
     }
 
     @ToolingApiVersion("<1.2")
     @TargetGradleVersion(">=1.12")
-    def "provider shows deprecation warning when build is requested by old toolingApi"() {
-        when:
-        def output = new ByteArrayOutputStream()
-        withConnection { ProjectConnection connection ->
-            def build = connection.newBuild()
-            build.standardOutput = output
-            build.forTasks("noop")
-            build.run()
-        }
-
-        then:
-        output.toString().contains(deprecationMessageApi(targetDist.version.version))
-    }
-
-    @ToolingApiVersion(">=1.2")
-    @TargetGradleVersion(">=1.12")
-    def "provider shows no deprecation warning when build is requested by supported toolingApi"() {
+    def "provider rejects build request from a tooling API older than 1.2"() {
         when:
         def output = new ByteArrayOutputStream()
         withConnection { ProjectConnection connection ->
@@ -127,12 +79,13 @@ task noop << {
         }
 
         then:
-        !output.toString().contains(deprecationMessageApi(targetDist.version.version))
+        GradleConnectionException e = thrown()
+        e.cause.message.contains('Support for clients using a tooling API version older than 1.2 was removed in Gradle 2.0. You should upgrade your tooling API client to version 1.2 or later.')
     }
 
     @ToolingApiVersion("<1.2")
     @TargetGradleVersion(">=1.12")
-    def "provider shows deprecation warning when model is requested by old toolingApi"() {
+    def "provider rejects model request from a tooling API older than 1.2"() {
         when:
         def output = new ByteArrayOutputStream()
         withConnection { ProjectConnection connection ->
@@ -142,26 +95,7 @@ task noop << {
         }
 
         then:
-        output.toString().contains(deprecationMessageApi(targetDist.version.version))
+        GradleConnectionException e = thrown()
+        e.cause.message.contains('Support for clients using a tooling API version older than 1.2 was removed in Gradle 2.0. You should upgrade your tooling API client to version 1.2 or later.')
     }
-
-    @ToolingApiVersion(">=1.2")
-    @TargetGradleVersion(">=1.12")
-    def "provider shows no deprecation warning when model is requested by supported toolingApi"() {
-        when:
-        def output = new ByteArrayOutputStream()
-        withConnection { ProjectConnection connection ->
-            def modelBuilder = connection.model(EclipseProject)
-            modelBuilder.standardOutput = output
-            modelBuilder.get()
-        }
-
-        then:
-        !output.toString().contains(deprecationMessageApi(targetDist.version.version))
-    }
-
-    def deprecationMessageApi(def version) {
-        "Connection from tooling API older than version 1.2 has been deprecated and is scheduled to be removed in Gradle 2.0"
-    }
-
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/UserHomeDirCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/UserHomeDirCrossVersionSpec.groovy
index 1237d37..32d9504 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/UserHomeDirCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r112/UserHomeDirCrossVersionSpec.groovy
@@ -32,7 +32,6 @@ class UserHomeDirCrossVersionSpec extends ToolingApiSpecification {
         toolingApi.withConnector { connector ->
             connector.useGradleUserHomeDir(userHomeDir)
         }
-        // TODO radim: consider using smaller heap and shorter timeout when applicable to all supported versions
         toolingApi.withConnection { connection ->
             BuildLauncher build = connection.newBuild();
             build.forTasks("gradleBuild");
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r11rc1/DependencyMetaDataCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r11rc1/DependencyMetaDataCrossVersionSpec.groovy
index 9997424..7a3cf73 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r11rc1/DependencyMetaDataCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r11rc1/DependencyMetaDataCrossVersionSpec.groovy
@@ -17,13 +17,11 @@ package org.gradle.integtests.tooling.r11rc1
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.tooling.model.ExternalDependency
 import org.gradle.tooling.model.eclipse.EclipseProject
 import org.gradle.tooling.model.idea.IdeaProject
 
- at ToolingApiVersion('>=1.1')
 @TargetGradleVersion('>=1.1')
 class DependencyMetaDataCrossVersionSpec extends ToolingApiSpecification {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
index eb314c2..d66647c 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
@@ -17,12 +17,10 @@
 package org.gradle.integtests.tooling.r12rc1
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
 
- at ToolingApiVersion(">=1.2")
 @TargetGradleVersion(">=1.2")
 class BuildModelCrossVersionSpec extends ToolingApiSpecification {
     def "can run tasks before building Eclipse model"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/ProjectOutcomesModuleCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/ProjectOutcomesModuleCrossVersionSpec.groovy
index 86f58a7..e954a60 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/ProjectOutcomesModuleCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/ProjectOutcomesModuleCrossVersionSpec.groovy
@@ -18,11 +18,9 @@ package org.gradle.integtests.tooling.r12rc1
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
 
- at ToolingApiVersion(">=1.2")
 @TargetGradleVersion(">=1.2")
 class ProjectOutcomesModuleCrossVersionSpec extends ToolingApiSpecification {
     def "modelContainsAllArchivesOnTheArchivesConfiguration"() {
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/UnsupportedOperationFeedbackCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/UnsupportedOperationFeedbackCrossVersionSpec.groovy
index 9c5ed54..b02cf8c 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/UnsupportedOperationFeedbackCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r12rc1/UnsupportedOperationFeedbackCrossVersionSpec.groovy
@@ -18,13 +18,11 @@ package org.gradle.integtests.tooling.r12rc1
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
 import org.gradle.tooling.model.eclipse.EclipseProject
 
- at ToolingApiVersion(">=1.2")
- at TargetGradleVersion("<=1.1")
+ at TargetGradleVersion(">=1.0-milestone-8 <=1.1")
 class UnsupportedOperationFeedbackCrossVersionSpec extends ToolingApiSpecification {
     def "fails when attempting to run tasks when building a model"() {
         when:
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r14/ToolingApiInitScriptCrossVersionIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r14/ToolingApiInitScriptCrossVersionIntegrationTest.groovy
index 3eb919a..1dcdbe4 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r14/ToolingApiInitScriptCrossVersionIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r14/ToolingApiInitScriptCrossVersionIntegrationTest.groovy
@@ -15,23 +15,22 @@
  */
 
 package org.gradle.integtests.tooling.r14
-
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.GradleConnector
 import spock.lang.Issue
-
 /**
  * Tests that init scripts are used from the _clients_ GRADLE_HOME, not the daemon server's.
  */
 @TargetGradleVersion('>=1.4')
- at Issue("http://issues.gradle.org/browse/GRADLE-2408")
+ at Issue("https://issues.gradle.org/browse/GRADLE-2408")
 class ToolingApiInitScriptCrossVersionIntegrationTest extends ToolingApiSpecification {
 
     TestFile createDistribution(int i) {
-        def distro = file("distro$i")
+        def distro = temporaryDistributionFolder.file("distro$i")
+
         distro.copyFrom(getTargetDist().getGradleHomeDir())
         distro.file("bin", OperatingSystem.current().getScriptName("gradle")).permissions = 'rwx------'
         distro.file("init.d/init.gradle") << """
@@ -43,10 +42,9 @@ class ToolingApiInitScriptCrossVersionIntegrationTest extends ToolingApiSpecific
     }
 
     String runWithInstallation(TestFile gradleHome) {
+        toolingApi.requireIsolatedDaemons()
         toolingApi.withConnector { GradleConnector it ->
             it.useInstallation(new File(gradleHome.absolutePath))
-            it.useGradleUserHomeDir(temporaryFolder.file("user home"))
-            it.embedded(false)
         }
         withBuild { it.forTasks("echo") }.standardOutput
     }
@@ -77,7 +75,5 @@ class ToolingApiInitScriptCrossVersionIntegrationTest extends ToolingApiSpecific
         distro2Output.contains "from distro 2"
         distro2Output.contains "runtime gradle home: ${distro1.absolutePath}"
     }
-
-
 }
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/CombiningCommandLineArgumentsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/CombiningCommandLineArgumentsCrossVersionSpec.groovy
index 9393c5b..8fe7bc4 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/CombiningCommandLineArgumentsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/CombiningCommandLineArgumentsCrossVersionSpec.groovy
@@ -21,11 +21,9 @@
 package org.gradle.integtests.tooling.r15
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import spock.lang.Issue
 
- at ToolingApiVersion(">=1.0")
 @TargetGradleVersion(">=1.5")
 class CombiningCommandLineArgumentsCrossVersionSpec extends ToolingApiSpecification {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/ToolingApiConfigurationOnDemandCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/ToolingApiConfigurationOnDemandCrossVersionSpec.groovy
index 661fe84..63ed4c8 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/ToolingApiConfigurationOnDemandCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r15/ToolingApiConfigurationOnDemandCrossVersionSpec.groovy
@@ -20,10 +20,8 @@ package org.gradle.integtests.tooling.r15
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.tooling.model.GradleProject
 
- at ToolingApiVersion(">=1.0-milestone-5") //because we acquire GradleProject model
 @TargetGradleVersion(">=1.5")
 class ToolingApiConfigurationOnDemandCrossVersionSpec extends ToolingApiSpecification {
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
index 69c4cc3..8dae590 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
@@ -35,7 +35,7 @@ class UnknownCustomModelFeedbackCrossVersionSpec extends ToolingApiSpecification
     }
 
     @ToolingApiVersion("current")
-    @TargetGradleVersion("<1.6")
+    @TargetGradleVersion(">=1.0-milestone-8 <1.6")
     def "fails gracefully when unknown model requested when custom models are not supported by the target version"() {
         when:
         withConnection { it.getModel(CustomModel.class) }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
index 7417a13..81771b2 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
@@ -52,6 +52,39 @@ class BuildActionCrossVersionSpec extends ToolingApiSpecification {
         nullModel == null
     }
 
+    @TargetGradleVersion(">=2.2")
+    def "action classes are reused"() {
+        toolingApi.requireIsolatedDaemons()
+
+        expect:
+        def result1 = withConnection { it.action(new CounterAction()).run() }
+        def result2 = withConnection { it.action(new CounterAction()).run() }
+        def result3 = withConnection { it.action(new CounterAction()).run() }
+        result1 == 1
+        result2 == 2
+        result3 == 3
+    }
+
+    @TargetGradleVersion(">=1.8 <=2.1")
+    def "action classes are reused when daemon is idle when operation starts"() {
+        toolingApi.requireIsolatedDaemons()
+
+        expect:
+        def result1 = withConnection { it.action(new CounterAction()).run() }
+
+        // Earlier versions return the build result before marking the daemon as idle. Wait for the daemon to be marked as idle
+        // before attempting the next operation otherwise the client will start a new daemon
+        toolingApi.daemons.daemon.becomesIdle()
+
+        def result2 = withConnection { it.action(new CounterAction()).run() }
+        toolingApi.daemons.daemon.becomesIdle()
+
+        def result3 = withConnection { it.action(new CounterAction()).run() }
+        result1 == 1
+        result2 == 2
+        result3 == 3
+    }
+
     def "client receives the exception thrown by the build action"() {
         when:
         withConnection { it.action(new BrokenAction()).run() }
@@ -67,6 +100,7 @@ class BuildActionCrossVersionSpec extends ToolingApiSpecification {
         withConnection { it.action(new FetchUnknownModel()).run() }
 
         then:
+        // Verification is in the action
         noExceptionThrown()
     }
 
@@ -92,7 +126,7 @@ class BuildActionCrossVersionSpec extends ToolingApiSpecification {
     }
 
     @ToolingApiVersion('current')
-    @TargetGradleVersion('<1.8')
+    @TargetGradleVersion('>=1.0-milestone-8 <1.8')
     def "gives reasonable error message when target Gradle version does not support build actions"() {
         when:
         withConnection { it.action(new FetchCustomModel()).run() }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildScriptModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildScriptModelCrossVersionSpec.groovy
index d9b4da8..2c47412 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildScriptModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/BuildScriptModelCrossVersionSpec.groovy
@@ -44,8 +44,7 @@ class BuildScriptModelCrossVersionSpec extends ToolingApiSpecification {
         project.buildScript.sourceFile == custom
     }
 
-    @ToolingApiVersion('current')
-    @TargetGradleVersion('<1.8 >=1.0-milestone-5')
+    @TargetGradleVersion('<1.8 >=1.0-milestone-8')
     def "gives reasonable error message when target Gradle version does not provide build script details"() {
         when:
         GradleProject project = withConnection { it.getModel(GradleProject.class) }
@@ -71,24 +70,4 @@ class BuildScriptModelCrossVersionSpec extends ToolingApiSpecification {
         e = thrown()
         e.message.startsWith('Unsupported method: GradleProject.getBuildScript().')
     }
-
-    @ToolingApiVersion('current')
-    @TargetGradleVersion('<1.0-milestone-5')
-    def "gives reasonable error message when build script details not available via Eclipse model"() {
-        when:
-        GradleProject project = withConnection { it.getModel(GradleProject.class) }
-        project.buildScript
-
-        then:
-        UnsupportedMethodException e = thrown()
-        e.message.startsWith('Unsupported method: GradleProject.getBuildScript().')
-
-        when:
-        EclipseProject eclipseProject = withConnection { it.getModel(EclipseProject.class) }
-        eclipseProject.gradleProject.buildScript
-
-        then:
-        e = thrown()
-        e.message.startsWith('Unsupported method: GradleProject.getBuildScript().')
-    }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/CounterAction.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/CounterAction.java
new file mode 100644
index 0000000..e0c3775
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/CounterAction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r18;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+
+public class CounterAction implements BuildAction<Integer> {
+    static int counter;
+
+    public Integer execute(BuildController controller) {
+        return ++counter;
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/GradleBuildModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/GradleBuildModelCrossVersionSpec.groovy
index bfbf92a..497bcf2 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/GradleBuildModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r18/GradleBuildModelCrossVersionSpec.groovy
@@ -39,7 +39,7 @@ allprojects {
 """
     }
 
-    @TargetGradleVersion("<1.8")
+    @TargetGradleVersion(">=1.0-milestone-8 <1.8")
     def "can request GradleBuild model"() {
         when:
         GradleBuild model = withConnection { connection -> connection.getModel(GradleBuild) }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/BrokenAction.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/BrokenAction.java
new file mode 100644
index 0000000..a66fffa
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/BrokenAction.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r20;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+
+public class BrokenAction implements BuildAction {
+    public Object execute(BuildController controller) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/GradleProjectModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/GradleProjectModelCrossVersionSpec.groovy
new file mode 100644
index 0000000..1909da0
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/GradleProjectModelCrossVersionSpec.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r20
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+
+class GradleProjectModelCrossVersionSpec extends ToolingApiSpecification {
+    @ToolingApiVersion("current")
+    @TargetGradleVersion("current")
+    def "can request the build directory"() {
+        given:
+        buildFile << """
+buildDir = 'custom/dir'
+"""
+
+        when:
+        def project = withConnection { ProjectConnection connection ->
+            connection.getModel(GradleProject)
+        }
+
+        then:
+        project.buildDirectory == file('custom/dir')
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy
new file mode 100644
index 0000000..0bbe20b
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r20
+
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+import spock.lang.IgnoreIf
+
+ at IgnoreIf({AvailableJavaHomes.java5 == null})
+ at TargetGradleVersion("current")
+class ToolingApiUnsupportedBuildJvmCrossVersionSpec extends ToolingApiSpecification {
+    def setup() {
+        projectDir.file("gradle.properties").writeProperties("org.gradle.java.home": AvailableJavaHomes.java5.javaHome.absolutePath)
+        toolingApi.requireDaemons()
+    }
+
+    def "cannot run a build when build is configured to use Java 5"() {
+        when:
+        toolingApi.withConnection { ProjectConnection connection ->
+            connection.newBuild().run()
+        }
+
+        then:
+        GradleConnectionException e = thrown()
+        e.message.startsWith("Could not execute build using Gradle installation")
+        e.cause.message == "Gradle ${targetDist.version.version} requires Java 6 or later to run. Your build is currently configured to use Java 5."
+    }
+
+    def "cannot fetch model when build is configured to use Java 5"() {
+        when:
+        toolingApi.withConnection { ProjectConnection connection ->
+            connection.model(GradleProject).get()
+        }
+
+        then:
+        GradleConnectionException e = thrown()
+        e.message.startsWith("Could not fetch model of type 'GradleProject' using Gradle installation")
+        e.cause.message == "Gradle ${targetDist.version.version} requires Java 6 or later to run. Your build is currently configured to use Java 5."
+    }
+
+    @ToolingApiVersion(">=1.8")
+    def "cannot run action when build is configured to use Java 5"() {
+        when:
+        toolingApi.withConnection { ProjectConnection connection ->
+            connection.action(new BrokenAction()).run()
+        }
+
+        then:
+        GradleConnectionException e = thrown()
+        e.message.startsWith("Could not run build action using Gradle installation")
+        e.cause.message == "Gradle ${targetDist.version.version} requires Java 6 or later to run. Your build is currently configured to use Java 5."
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedClientJvmCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedClientJvmCrossVersionSpec.groovy
new file mode 100644
index 0000000..a02114b
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r20/ToolingApiUnsupportedClientJvmCrossVersionSpec.groovy
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.tooling.r20
+
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.ScriptExecuter
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.util.GradleVersion
+import spock.lang.IgnoreIf
+
+ at IgnoreIf({ AvailableJavaHomes.java5 == null })
+class ToolingApiUnsupportedClientJvmCrossVersionSpec extends ToolingApiSpecification {
+    def setup() {
+        settingsFile << "rootProject.name = 'test'"
+
+        buildFile << """
+apply plugin: 'application'
+sourceCompatibility = 1.5
+targetCompatibility = 1.5
+repositories {
+    maven {
+        url 'https://repo.gradle.org/gradle/libs-releases-local'
+    }
+    maven {
+        url '${buildContext.libsRepo.toURI()}'
+    }
+    mavenCentral()
+}
+
+dependencies {
+    compile "org.gradle:gradle-tooling-api:${GradleVersion.current().version}"
+    runtime 'org.slf4j:slf4j-simple:1.7.10'
+}
+
+mainClassName = 'TestClient'
+"""
+        file('src/main/java/TestClient.java') << """
+import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.ProjectConnection;
+import java.io.File;
+import java.net.URI;
+
+public class TestClient {
+    public static void main(String[] args) {
+        try {
+            ProjectConnection connection = GradleConnector
+                .newConnector()
+                .forProjectDirectory(new File(new URI("${projectDir.toURI()}")))
+                .useInstallation(new File(new URI("${buildContext.gradleHomeDir.toURI()}")))
+                .connect();
+            connection.newBuild().run();
+        } catch(Throwable t) {
+            t.printStackTrace(System.out);
+        }
+        System.exit(0);
+    }
+}
+"""
+        targetDist.executer(temporaryFolder).inDirectory(projectDir).withTasks("installDist").requireGradleHome().run()
+    }
+
+    @TargetGradleVersion("current")
+    @ToolingApiVersion(">=1.2 <=1.12")
+    def "cannot use old tooling API client to run build using Java 5"() {
+        when:
+        def out = runScript()
+
+        then:
+        out.contains("Could not execute build using Gradle installation")
+        out.contains("Gradle ${targetDist.version.version} requires Java 6 or later to run. You are currently using Java 5.")
+    }
+
+    @TargetGradleVersion("current")
+    @ToolingApiVersion("current")
+    def "cannot use tooling API from Java 5"() {
+        when:
+        def out = runScript()
+
+        then:
+        out.contains("Gradle Tooling API ${targetDist.version.version} requires Java 6 or later to run. You are currently using Java 5.")
+    }
+
+    def runScript() {
+        def outStr = new ByteArrayOutputStream()
+        def executer = new ScriptExecuter()
+        executer.environment(JAVA_HOME: AvailableJavaHomes.java5.javaHome)
+        executer.workingDir(projectDir)
+        executer.standardOutput = outStr
+        executer.commandLine("build/install/test/bin/test")
+        executer.run()
+        println outStr
+        return outStr.toString()
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/CancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/CancellationCrossVersionSpec.groovy
new file mode 100644
index 0000000..ade3bd2
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/CancellationCrossVersionSpec.groovy
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package org.gradle.integtests.tooling.r21
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.TestResultHandler
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.BuildCancelledException
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.gradle.GradleBuild
+
+ at ToolingApiVersion(">=2.1")
+ at TargetGradleVersion(">=2.1")
+class CancellationCrossVersionSpec extends ToolingApiSpecification {
+    def setup() {
+        settingsFile << '''
+rootProject.name = 'cancelling'
+'''
+    }
+
+    def "early cancel stops the build before beginning"() {
+        buildFile << """
+throw new GradleException("should not run")
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+
+        when:
+        cancel.cancel()
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('hang')
+            build.withCancellationToken(cancel.token())
+            build.run(resultHandler)
+            resultHandler.finished()
+        }
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    def "can cancel build after completion"() {
+        buildFile << """
+task thing
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('thing')
+            build.withCancellationToken(cancel.token())
+            build.run(resultHandler)
+            resultHandler.finished()
+            cancel.cancel()
+        }
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "early cancel stops model retrieval before beginning"() {
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+
+        when:
+        cancel.cancel()
+        withConnection { ProjectConnection connection ->
+            def build = connection.model(GradleBuild)
+            build.withCancellationToken(cancel.token())
+            build.get(resultHandler)
+            resultHandler.finished()
+        }
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    def "can cancel build after model retrieval"() {
+        buildFile << """
+task thing
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.model(GradleBuild)
+            build.withCancellationToken(cancel.token())
+            build.get(resultHandler)
+            resultHandler.finished()
+            cancel.cancel()
+        }
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "early cancel stops the action before beginning"() {
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+
+        when:
+        cancel.cancel()
+        withConnection { ProjectConnection connection ->
+            def build = connection.action(new HangingBuildAction())
+            build.withCancellationToken(cancel.token())
+            build.run(resultHandler)
+            resultHandler.finished()
+        }
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/HangingBuildAction.java b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/HangingBuildAction.java
new file mode 100644
index 0000000..1fed771
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/HangingBuildAction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.tooling.r21;
+
+import org.gradle.api.GradleException;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+import org.gradle.tooling.model.GradleProject;
+
+public class HangingBuildAction implements BuildAction<Void> {
+    public Void execute(BuildController controller) {
+        controller.getModel(GradleProject.class);
+        throw new GradleException("Should be cancelled before the end of action.");
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/PreCancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/PreCancellationCrossVersionSpec.groovy
new file mode 100644
index 0000000..3a5d23f
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/PreCancellationCrossVersionSpec.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r21
+
+import org.gradle.integtests.tooling.fixture.*
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.junit.Rule
+
+ at ToolingApiVersion(">=2.1")
+ at TargetGradleVersion("<2.1 >=1.0-milestone-8")
+class PreCancellationCrossVersionSpec extends ToolingApiSpecification {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    def setup() {
+        settingsFile << '''
+rootProject.name = 'cancelling'
+'''
+    }
+
+    def "cancel with older provider issues warning only"() {
+        buildFile << """
+task t << {
+    new URL("${server.uri}").text
+    println "finished"
+}
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('t')
+                .withCancellationToken(cancel.token())
+                .setStandardOutput(output)
+            build.run(resultHandler)
+            server.waitFor()
+            cancel.cancel()
+            server.release()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure == null
+        output.toString().contains("finished")
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/R21CancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/R21CancellationCrossVersionSpec.groovy
new file mode 100644
index 0000000..eba0ac3
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/R21CancellationCrossVersionSpec.groovy
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r21
+
+import org.gradle.integtests.tooling.fixture.*
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.junit.Rule
+
+ at ToolingApiVersion("=2.1")
+ at TargetGradleVersion(">=2.1")
+class R21CancellationCrossVersionSpec extends ToolingApiSpecification {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    def setup() {
+        settingsFile << '''
+rootProject.name = 'cancelling'
+'''
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel build during configuration phase"() {
+        toolingApi.requireIsolatedDaemons()
+        def daemons = toolingApi.daemons
+
+        settingsFile << '''
+include 'sub'
+rootProject.name = 'cancelling'
+'''
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback{
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+        projectDir.file('sub/build.gradle') << """
+throw new RuntimeException("should not run")
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks(':first', ':sub:second')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            daemons.daemon.assertBusy()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof GradleConnectionException
+        resultHandler.failure.cause.message.contains('Build cancelled.')
+
+        and:
+        daemons.daemon.assertIdle()
+    }
+
+    def "can cancel build and skip some tasks"() {
+        toolingApi.requireIsolatedDaemons()
+        def daemons = toolingApi.daemons
+
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+task hang << {
+    def cancellationToken = services.get(BuildCancellationToken.class)
+    def latch = new CountDownLatch(1)
+
+    cancellationToken.addCallback {
+        latch.countDown()
+    }
+
+    new URL("${server.uri}").text
+    latch.await()
+}
+
+task notExecuted(dependsOn: hang) << {
+    throw new RuntimeException("should not run")
+}
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('notExecuted')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            daemons.daemon.assertBusy()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof GradleConnectionException
+        resultHandler.failure.cause.message.contains('Build cancelled.')
+
+        and:
+        daemons.daemon.assertIdle()
+    }
+
+    def "does not fail when build completes within the cancellation timeout"() {
+        toolingApi.requireIsolatedDaemons()
+        def daemons = toolingApi.daemons
+
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+task hang << {
+    def cancellationToken = services.get(BuildCancellationToken.class)
+    def latch = new CountDownLatch(1)
+
+    cancellationToken.addCallback {
+        latch.countDown()
+    }
+
+    new URL("${server.uri}").text
+    latch.await()
+}
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('hang')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            daemons.daemon.assertBusy()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        noExceptionThrown()
+
+        and:
+        daemons.daemon.assertIdle()
+    }
+
+    def "can cancel build through forced stop"() {
+        toolingApi.requireIsolatedDaemons()
+        def daemons = toolingApi.daemons
+
+        buildFile << """
+task hang << {
+    new URL("${server.uri}").text
+}
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('hang')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+            build.run(resultHandler)
+            server.waitFor()
+            daemons.daemon.assertBusy()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof GradleConnectionException
+        resultHandler.failure.cause.class.name == 'org.gradle.api.BuildCancelledException'
+        resultHandler.failure.cause.message == 'Build cancelled.'
+
+        and:
+        daemons.daemon.stops()
+    }
+
+    def "can cancel action"() {
+        toolingApi.requireIsolatedDaemons()
+        def daemons = toolingApi.daemons
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback {
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.action(new HangingBuildAction())
+            build.withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+            build.run(resultHandler)
+            server.sync()
+            daemons.daemon.assertBusy()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof GradleConnectionException
+
+        and:
+        daemons.daemon.assertIdle()
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/TaskVisibilityCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/TaskVisibilityCrossVersionSpec.groovy
new file mode 100644
index 0000000..4403ea1
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r21/TaskVisibilityCrossVersionSpec.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r21
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.model.Task
+import org.gradle.tooling.model.TaskSelector
+import org.gradle.tooling.model.UnsupportedMethodException
+import org.gradle.tooling.model.gradle.BuildInvocations
+
+ at ToolingApiVersion(">=2.1")
+class TaskVisibilityCrossVersionSpec extends ToolingApiSpecification {
+
+    def setup() {
+        settingsFile << '''
+include 'a'
+include 'b'
+include 'b:c'
+rootProject.name = 'test'
+'''
+        buildFile << '''
+task t1 {}
+task t2 {
+    group 'foo'
+}
+
+project(':b') {
+    task t3 {}
+    task t2 {
+        group 'build'
+    }
+}
+
+project(':b:c') {
+    task t1 {
+        group 'build'
+    }
+    task t2 {
+        group 'build'
+    }
+}'''
+    }
+
+    @TargetGradleVersion(">=2.1")
+    def "task visibility is correct"() {
+        def publicTasks = rootProjectImplicitTasks + ['t2']
+        def publicSelectors = rootProjectImplicitSelectors + ['t1', 't2']
+
+        when:
+        BuildInvocations model = withConnection { connection ->
+            connection.getModel(BuildInvocations)
+        }
+
+        then:
+        model.tasks.every { Task t -> publicTasks.contains(t.name) == t.public }
+        model.taskSelectors.every { TaskSelector ts -> publicSelectors.contains(ts.name) == ts.public }
+    }
+
+    @TargetGradleVersion(">=1.0-milestone-8 <2.1")
+    def "no visibility for older launchables"() {
+        when:
+        BuildInvocations model = withConnection { connection ->
+            connection.getModel(BuildInvocations)
+        }
+
+        then:
+        model.tasks.every { Task t ->
+            try {
+                t.public
+                false
+            } catch (UnsupportedMethodException e) {
+                true
+            }
+        }
+        model.taskSelectors.every { TaskSelector t ->
+            try {
+                t.public
+                false
+            } catch (UnsupportedMethodException e) {
+                true
+            }
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/BuildActionCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/BuildActionCrossVersionSpec.groovy
new file mode 100644
index 0000000..153af5d
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/BuildActionCrossVersionSpec.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r22
+
+import org.gradle.integtests.fixtures.executer.ForkingGradleExecuter
+import org.gradle.integtests.fixtures.executer.GradleBackedArtifactBuilder
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.BuildAction
+import org.gradle.tooling.BuildController
+import org.gradle.tooling.ProjectConnection
+
+ at ToolingApiVersion(">=1.8")
+class BuildActionCrossVersionSpec extends ToolingApiSpecification {
+    @TargetGradleVersion(">=2.2")
+    def "can change the implementation of an action"() {
+        // Make sure we reuse the same daemon
+        toolingApi.requireIsolatedDaemons()
+
+        def workDir = temporaryFolder.file("work")
+        def implJar = workDir.file("action-impl.jar")
+        def builder = new GradleBackedArtifactBuilder(new ForkingGradleExecuter(dist, temporaryFolder), workDir)
+
+        given:
+        builder.sourceFile('ActionImpl.java') << """
+public class ActionImpl implements ${BuildAction.name}<java.io.File> {
+    public java.io.File execute(${BuildController.name} controller) {
+        try {
+            return new java.io.File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
+        } catch (java.net.URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
+"""
+        // Discard the impl jar from the jvm's jar file cache and rebuild
+        forceJarClose(implJar)
+        builder.buildJar(implJar)
+        def cl1 = new URLClassLoader([implJar.toURI().toURL()] as URL[], getClass().classLoader)
+        def action1 = cl1.loadClass("ActionImpl").newInstance()
+
+        when:
+        File actualJar1 = withConnection { ProjectConnection connection ->
+            connection.action(action1).run()
+        }
+
+        then:
+        actualJar1 != implJar
+        actualJar1.name == implJar.name
+
+        when:
+        // Discard the impl jar from the jvm's jar file cache
+        forceJarClose(implJar)
+        workDir.deleteDir()
+        builder.sourceFile('ActionImpl.java') << """
+public class ActionImpl implements ${BuildAction.name}<String> {
+    public String execute(${BuildController.name} controller) {
+        return getClass().getProtectionDomain().getCodeSource().getLocation().toString();
+    }
+}
+"""
+        builder.buildJar(implJar)
+        def cl2 = new URLClassLoader([implJar.toURI().toURL()] as URL[], getClass().classLoader)
+        def action2 = cl2.loadClass("ActionImpl").newInstance()
+
+        String result2 = withConnection { ProjectConnection connection ->
+            connection.action(action2).run()
+        }
+        def actualJar2 = new File(new URI(result2))
+
+        then:
+        actualJar2 != implJar
+        actualJar2 != actualJar1
+        actualJar2.name == implJar.name
+    }
+
+    def forceJarClose(File jar) {
+        if (!jar.exists()) {
+            return
+        }
+        def factory = new sun.net.www.protocol.jar.JarFileFactory()
+        def file = factory.get(jar.toURI().toURL())
+        factory.close(file)
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy
new file mode 100644
index 0000000..1c04f8f
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r22
+
+import org.gradle.integtests.tooling.fixture.*
+import org.gradle.integtests.tooling.r20.BrokenAction
+import org.gradle.integtests.tooling.r21.HangingBuildAction
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
+import org.gradle.tooling.BuildCancelledException
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+import org.junit.Rule
+
+ at ToolingApiVersion(">=2.2")
+ at TargetGradleVersion(">=2.1")
+class CancellationCrossVersionSpec extends ToolingApiSpecification {
+    @Rule CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    def setup() {
+        settingsFile << '''
+rootProject.name = 'cancelling'
+'''
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel build during settings phase"() {
+        settingsFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = gradle.services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback {
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+        buildFile << """
+throw new RuntimeException("should not run")
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks(':sub:broken')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel build during configuration phase"() {
+        file("gradle.properties") << "org.gradle.configureondemand=${configureOnDemand}"
+        setupCancelInConfigurationBuild()
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks(':sub:broken')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+
+        where:
+        configureOnDemand << [true, false]
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel model creation during configuration phase"() {
+        file("gradle.properties") << "org.gradle.configureondemand=${configureOnDemand}"
+        setupCancelInConfigurationBuild()
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def model = connection.model(GradleProject)
+            model.withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            model.get(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+
+        where:
+        configureOnDemand << [true, false]
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel build action execution during configuration phase"() {
+        file("gradle.properties") << "org.gradle.configureondemand=${configureOnDemand}"
+        setupCancelInConfigurationBuild()
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def action = connection.action(new BrokenAction())
+            action.withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            action.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+
+        where:
+        configureOnDemand << [true, false]
+    }
+
+    def "can cancel build and skip some tasks"() {
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+task hang << {
+    def cancellationToken = services.get(BuildCancellationToken.class)
+    def latch = new CountDownLatch(1)
+
+    cancellationToken.addCallback {
+        latch.countDown()
+    }
+
+    new URL("${server.uri}").text
+    latch.await()
+}
+
+task notExecuted(dependsOn: hang) << {
+    throw new RuntimeException("should not run")
+}
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('notExecuted')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    def "does not fail when build completes within the cancellation timeout"() {
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+task hang << {
+    def cancellationToken = services.get(BuildCancellationToken.class)
+    def latch = new CountDownLatch(1)
+
+    cancellationToken.addCallback {
+        latch.countDown()
+    }
+
+    new URL("${server.uri}").text
+    latch.await()
+}
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('hang')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "can cancel build through forced stop"() {
+        // in-process call does not support forced stop
+        toolingApi.requireDaemons()
+        buildFile << """
+task hang << {
+    new URL("${server.uri}").text
+}
+"""
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks('hang')
+                    .withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+            build.run(resultHandler)
+            server.waitFor()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "can cancel model retrieval"() {
+        settingsFile << '''
+include 'sub'
+rootProject.name = 'cancelling'
+'''
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback {
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+        projectDir.file('sub/build.gradle') << """
+throw new RuntimeException("should not run")
+"""
+
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.model(GradleProject)
+            build.withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+                    .setStandardError(error)
+            build.get(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    def "can cancel action"() {
+        def cancel = GradleConnector.newCancellationTokenSource()
+        def resultHandler = new TestResultHandler()
+        def output = new TestOutputStream()
+
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback{
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.action(new HangingBuildAction())
+            build.withCancellationToken(cancel.token())
+                    .setStandardOutput(output)
+            build.run(resultHandler)
+            server.sync()
+            cancel.cancel()
+            resultHandler.finished()
+        }
+
+        then:
+        resultHandler.failure instanceof BuildCancelledException
+    }
+
+    def setupCancelInConfigurationBuild() {
+        settingsFile << '''
+include 'sub'
+rootProject.name = 'cancelling'
+'''
+        buildFile << """
+import org.gradle.initialization.BuildCancellationToken
+import java.util.concurrent.CountDownLatch
+
+def cancellationToken = services.get(BuildCancellationToken.class)
+def latch = new CountDownLatch(1)
+
+cancellationToken.addCallback {
+    latch.countDown()
+}
+
+new URL("${server.uri}").text
+latch.await()
+"""
+
+        projectDir.file('sub/build.gradle') << """
+throw new RuntimeException("should not run")
+"""
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/ClientShutdownCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/ClientShutdownCrossVersionSpec.groovy
new file mode 100644
index 0000000..0e293d7
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/ClientShutdownCrossVersionSpec.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r22
+
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.test.fixtures.server.http.CyclicBarrierHttpServer
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.internal.consumer.DefaultGradleConnector
+import org.gradle.tooling.model.gradle.GradleBuild
+import org.junit.Rule
+
+ at ToolingApiVersion(">=2.2")
+class ClientShutdownCrossVersionSpec extends ToolingApiSpecification {
+    @Rule
+    CyclicBarrierHttpServer server = new CyclicBarrierHttpServer()
+
+    def cleanup() {
+        reset()
+    }
+
+    def "can shutdown tooling API session when no operations have been executed"() {
+        given:
+        DefaultGradleConnector.close()
+
+        when:
+        GradleConnector.newConnector()
+
+        then:
+        IllegalStateException e = thrown()
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "cleans up idle daemons when tooling API session is shutdown"() {
+        given:
+        toolingApi.requireIsolatedDaemons()
+
+        withConnection { connection ->
+            connection.getModel(GradleBuild)
+        }
+        toolingApi.daemons.daemon.assertIdle()
+
+        when:
+        DefaultGradleConnector.close()
+
+        then:
+        toolingApi.daemons.daemon.stops()
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "cleans up busy daemons once they become idle when tooling API session is shutdown"() {
+        given:
+        buildFile << """
+task slow << { new URL("${server.uri}").text }
+"""
+
+        toolingApi.requireIsolatedDaemons()
+        withConnection { connection ->
+            connection.getModel(GradleBuild)
+        }
+        toolingApi.daemons.daemon.assertIdle()
+
+        def build = daemonExecutor().withTasks("slow").start()
+        server.waitFor()
+        toolingApi.daemons.daemon.assertBusy()
+
+        when:
+        DefaultGradleConnector.close()
+
+        then:
+        toolingApi.daemons.daemon.assertBusy()
+
+        when:
+        server.release()
+        build.waitForFinish()
+
+        then:
+        toolingApi.daemons.daemon.stops()
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "shutdown ignores daemons that are no longer running"() {
+        given:
+        toolingApi.requireIsolatedDaemons()
+
+        withConnection { connection ->
+            connection.getModel(GradleBuild)
+        }
+        toolingApi.daemons.daemon.assertIdle()
+        toolingApi.daemons.daemon.kill()
+
+        when:
+        DefaultGradleConnector.close()
+
+        then:
+        noExceptionThrown()
+    }
+
+    @TargetGradleVersion(">=2.2")
+    def "shutdown ignores daemons that were not started by client"() {
+        given:
+        toolingApi.requireIsolatedDaemons()
+        daemonExecutor().run()
+        toolingApi.daemons.daemon.assertIdle()
+
+        withConnection { connection ->
+            connection.getModel(GradleBuild)
+        }
+        toolingApi.daemons.daemon.assertIdle()
+
+        when:
+        DefaultGradleConnector.close()
+
+        then:
+        toolingApi.daemons.daemon.assertIdle()
+    }
+
+    private GradleExecuter daemonExecutor() {
+        targetDist.executer(temporaryFolder).withDaemonBaseDir(toolingApi.daemonBaseDir).withArguments("--daemon").requireGradleHome()
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/Idea13ModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/Idea13ModelCrossVersionSpec.groovy
new file mode 100644
index 0000000..11b849d
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r22/Idea13ModelCrossVersionSpec.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.tooling.r22
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.model.idea.*
+
+class Idea13ModelCrossVersionSpec extends ToolingApiSpecification {
+
+    @TargetGradleVersion(">=2.2 <2.3")
+    @ToolingApiVersion(">=2.2")
+    def "provides partial generated sources dir information"() {
+
+        file('build.gradle').text = """
+apply plugin: 'java'
+apply plugin: 'idea'
+
+idea {
+  module {
+    sourceDirs += file('foo')
+    testSourceDirs += file('foo2')
+    generatedSourceDirs += file('foo')
+    generatedSourceDirs += file('foo2')
+  }
+}
+"""
+
+        when:
+        IdeaProject project = withConnection { connection -> connection.getModel(IdeaProject.class) }
+        def contentRoot = project.children[0].contentRoots[0]
+
+        then:
+        contentRoot.sourceDirectories.findAll { it.generated }.collect { it.directory } == [file('foo')]
+        contentRoot.testDirectories.findAll { it.generated }.collect { it.directory } == [file('foo2')]
+        // 2.2 always returned empty `generatedSourceDirectories` and `generatedTestDirectories`
+    }
+
+    @TargetGradleVersion(">=2.3")
+    @ToolingApiVersion(">=2.2")
+    def "provides generated sources dir information"() {
+
+        file('build.gradle').text = """
+apply plugin: 'java'
+apply plugin: 'idea'
+
+idea {
+  module {
+    sourceDirs += file('foo')
+    testSourceDirs += file('foo2')
+    generatedSourceDirs += file('foo')
+    generatedSourceDirs += file('foo2')
+  }
+}
+"""
+
+        when:
+        IdeaProject project = withConnection { connection -> connection.getModel(IdeaProject.class) }
+        def contentRoot = project.children[0].contentRoots[0]
+
+        then:
+        contentRoot.sourceDirectories.findAll { it.generated }.collect { it.directory } == [file('foo')]
+        contentRoot.testDirectories.findAll { it.generated }.collect { it.directory } == [file('foo2')]
+        contentRoot.generatedSourceDirectories.size() == 1
+        contentRoot.generatedSourceDirectories.every { contentRoot.sourceDirectories.contains(it) }
+        contentRoot.generatedTestDirectories.size() == 1
+        contentRoot.getGeneratedTestDirectories().every { contentRoot.testDirectories.contains(it) }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/GradleProjectCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/GradleProjectCrossVersionSpec.groovy
new file mode 100644
index 0000000..e621c1a
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/GradleProjectCrossVersionSpec.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r23
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+
+class GradleProjectCrossVersionSpec extends ToolingApiSpecification {
+
+    @ToolingApiVersion(">=2.1")
+    @TargetGradleVersion(">=2.3")
+    def "populate isPublic field on tasks from GradleProject"() {
+        file("build.gradle") << """
+task publicTask {
+  group = 'myGroup'
+}
+task privateTask {
+}
+"""
+
+        when:
+        def gradleProject = withConnection { ProjectConnection connection ->
+            connection.getModel(GradleProject.class)
+        }
+
+        then:
+        gradleProject != null
+        gradleProject.tasks.find { it.name.equals('publicTask') }.isPublic()
+        !gradleProject.tasks.find { it.name.equals('privateTask') }.isPublic()
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ImplicitTasksToolingApiCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ImplicitTasksToolingApiCrossVersionSpec.groovy
new file mode 100644
index 0000000..78b504f
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ImplicitTasksToolingApiCrossVersionSpec.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r23
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.tooling.model.GradleProject
+
+ at TargetGradleVersion(">=2.3")
+class ImplicitTasksToolingApiCrossVersionSpec extends ToolingApiSpecification {
+
+    def "implicit/placeholder tasks are visible from tooling api"() {
+        file("settings.gradle") << """
+            include "subproject"
+        """
+
+        when:
+        GradleProject project = withConnection { it.getModel(GradleProject.class) }
+
+        then:
+        project.tasks*.name.containsAll rootProjectImplicitTasks
+        project.findByPath(":subproject").tasks*.name.containsAll implicitTasks
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy
new file mode 100644
index 0000000..9bc3b03
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r23
+
+import org.gradle.integtests.fixtures.executer.OutputScrapingExecutionResult
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ModelBuilder
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+import org.gradle.tooling.model.build.BuildEnvironment
+
+class ModelBuilderCrossVersionSpec extends ToolingApiSpecification {
+
+    @ToolingApiVersion(">=2.3")
+    def "empty list of tasks to execute when asking for BuildEnvironment is treated like null tasks and does not fail"() {
+        projectDir.file('build.gradle') << """
+            defaultTasks 'alpha'
+            task alpha() << { throw new RuntimeException() }
+        """
+
+        def outputStream = new ByteArrayOutputStream()
+        def errorStream = new ByteArrayOutputStream()
+
+        when:
+        BuildEnvironment model = toolingApi.withConnection { ProjectConnection connection ->
+            ModelBuilder<BuildEnvironment> modelBuilder = connection.model(BuildEnvironment.class)
+            modelBuilder.forTasks(new String[0])
+            modelBuilder.setStandardOutput(outputStream)
+            modelBuilder.setStandardError(errorStream)
+            modelBuilder.get()
+        }
+
+        then:
+        model != null
+        new OutputScrapingExecutionResult(outputStream.toString(), errorStream.toString()).assertTasksExecuted()
+    }
+
+    @ToolingApiVersion(">=2.3")
+    def "empty list of tasks to execute when asking for model from target Gradle is treated like null tasks and executes no tasks"() {
+        projectDir.file('build.gradle') << """
+            defaultTasks 'alpha'
+            task alpha() << { throw new RuntimeException() }
+        """
+
+        def outputStream = new ByteArrayOutputStream()
+        def errorStream = new ByteArrayOutputStream()
+
+        when:
+        GradleProject model = toolingApi.withConnection { ProjectConnection connection ->
+            ModelBuilder<GradleProject> modelBuilder = connection.model(GradleProject.class)
+            modelBuilder.forTasks(new String[0])
+            modelBuilder.setStandardOutput(outputStream)
+            modelBuilder.setStandardError(errorStream)
+            modelBuilder.get()
+        }
+
+        then:
+        model != null
+        new OutputScrapingExecutionResult(outputStream.toString(), errorStream.toString()).assertTasksExecuted()
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/StandardStreamsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/StandardStreamsCrossVersionSpec.groovy
new file mode 100644
index 0000000..9c8ee98
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r23/StandardStreamsCrossVersionSpec.groovy
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r23
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.TestOutputStream
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.util.RedirectStdOutAndErr
+import org.junit.Rule
+
+class StandardStreamsCrossVersionSpec extends ToolingApiSpecification {
+    @Rule RedirectStdOutAndErr stdOutAndErr = new RedirectStdOutAndErr()
+    def escapeHeader = "\u001b["
+
+    def setup() {
+        toolingApi.requireDaemons()
+    }
+
+    @TargetGradleVersion(">=2.3")
+    def "logging is not sent to System.out or System.err"() {
+        file("build.gradle") << """
+project.logger.error("error logging");
+project.logger.warn("warn logging");
+project.logger.lifecycle("lifecycle logging");
+project.logger.quiet("quiet logging");
+project.logger.info ("info logging");
+project.logger.debug("debug logging");
+
+task log << {
+    println "task logging"
+}
+"""
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.forTasks("log")
+            build.run()
+        }
+
+        then:
+        !stdOutAndErr.stdOut.contains("logging")
+        !stdOutAndErr.stdErr.contains("logging")
+    }
+
+    @TargetGradleVersion(">=2.3")
+    def "logging is not sent to System.out or System.err when using custom output streams"() {
+        file("build.gradle") << """
+project.logger.error("error logging");
+project.logger.warn("warn logging");
+project.logger.lifecycle("lifecycle logging");
+project.logger.quiet("quiet logging");
+project.logger.info ("info logging");
+project.logger.debug("debug logging");
+
+task log << {
+    println "task logging"
+}
+"""
+
+        when:
+        def output = new TestOutputStream()
+        def error = new TestOutputStream()
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.standardOutput = output
+            build.standardError = error
+            build.forTasks("log")
+            build.run()
+        }
+
+        then:
+        output.toString().contains("task logging")
+        output.toString().contains("warn logging")
+        output.toString().contains("lifecycle logging")
+        output.toString().contains("quiet logging")
+        error.toString().contains("error logging")
+
+        and:
+        !stdOutAndErr.stdOut.contains("logging")
+        !stdOutAndErr.stdErr.contains("logging")
+    }
+
+    @ToolingApiVersion(">=2.3")
+    @TargetGradleVersion(">=2.3")
+    def "can specify color output"() {
+        file("build.gradle") << """
+task log {
+    outputs.upToDateWhen { true }
+}
+"""
+
+        when:
+        def output = new TestOutputStream()
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.standardOutput = output
+            build.colorOutput = true
+            build.forTasks("log")
+            build.run()
+        }
+
+        then:
+        output.toString().contains("UP-TO-DATE" + escapeHeader)
+
+        and:
+        !stdOutAndErr.stdOut.contains(escapeHeader)
+        !stdOutAndErr.stdErr.contains(escapeHeader)
+    }
+
+    @ToolingApiVersion(">=2.3")
+    @TargetGradleVersion(">=1.0-milestone-8 <2.3")
+    def "can specify color output when target version does not support colored output"() {
+        file("build.gradle") << """
+task log {
+    outputs.upToDateWhen { true }
+}
+"""
+
+        when:
+        def output = new TestOutputStream()
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.standardOutput = output
+            build.colorOutput = true
+            build.forTasks("log")
+            build.run()
+        }
+
+        then:
+        !output.toString().contains(escapeHeader)
+
+        and:
+        !stdOutAndErr.stdOut.contains(escapeHeader)
+        !stdOutAndErr.stdErr.contains(escapeHeader)
+    }
+
+    @ToolingApiVersion(">=2.3")
+    def "can specify color output when output is being ignored"() {
+        file("build.gradle") << """
+task log {
+    outputs.upToDateWhen { true }
+}
+"""
+
+        when:
+        withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            build.colorOutput = true
+            build.forTasks("log")
+            build.run()
+        }
+
+        then:
+        !stdOutAndErr.stdOut.contains(escapeHeader)
+        !stdOutAndErr.stdErr.contains(escapeHeader)
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildEnvironmentCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildEnvironmentCrossVersionSpec.groovy
new file mode 100644
index 0000000..6b9f0dd
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildEnvironmentCrossVersionSpec.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+package org.gradle.integtests.tooling.r24
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.build.BuildEnvironment
+
+class BuildEnvironmentCrossVersionSpec extends ToolingApiSpecification {
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "provide Gradle user home information on BuildEnvironment"() {
+        file("build.gradle")
+
+        when:
+        def buildEnvironment = withConnection { ProjectConnection connection ->
+            connection.getModel(BuildEnvironment.class)
+        }
+
+        then:
+        buildEnvironment != null
+        buildEnvironment.gradle.gradleUserHome != null
+        buildEnvironment.gradle.gradleUserHome == toolingApi.gradleUserHomeDir
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildInvocationsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildInvocationsCrossVersionSpec.groovy
new file mode 100644
index 0000000..e8648e2
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/BuildInvocationsCrossVersionSpec.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r24
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.gradle.BuildInvocations
+
+class BuildInvocationsCrossVersionSpec extends ToolingApiSpecification {
+
+    @ToolingApiVersion(">=1.12")
+    @TargetGradleVersion(">=2.4")
+    def "set task selector description taken from task with path that has lowest ordering"() {
+        temporaryFolder.createFile('settings.gradle') << '''
+          rootProject.name = 'TestProject'
+          include 'sub'
+        '''
+
+        temporaryFolder.createFile('build.gradle') << '''
+          task alpha {
+            description = 'ALPHA from root project'
+          }
+          task beta {}
+        '''
+
+        temporaryFolder.createDir('sub')
+        temporaryFolder.createFile('sub', 'build.gradle') << '''
+          task alpha {
+            description = 'ALPHA from sub project'
+          }
+          task beta {
+            description = 'BETA from sub project'
+          }
+          task gamma {
+            description = 'GAMMA from sub'
+          }
+        '''
+
+        when:
+        def buildInvocations = withConnection { ProjectConnection connection ->
+            connection.getModel(BuildInvocations.class)
+        }
+
+        then:
+        buildInvocations != null
+        buildInvocations.taskSelectors.find { it.name == 'alpha' }.description == 'ALPHA from root project'
+        buildInvocations.taskSelectors.find { it.name == 'beta' }.description == null
+        buildInvocations.taskSelectors.find { it.name == 'gamma' }.description == 'GAMMA from sub'
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/DaemonUsageSuggestionCrossVersionTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/DaemonUsageSuggestionCrossVersionTest.groovy
new file mode 100644
index 0000000..72316ce
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/DaemonUsageSuggestionCrossVersionTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r24
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.tooling.fixture.ConfigurableOperation
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.launcher.exec.DaemonUsageSuggestingBuildActionExecuter
+import org.gradle.tooling.ProjectConnection
+import org.gradle.util.TestPrecondition
+import spock.lang.IgnoreIf
+
+ at IgnoreIf({ !GradleContextualExecuter.embedded || TestPrecondition.WINDOWS.fulfilled })
+ at ToolingApiVersion(">=2.4")
+ at TargetGradleVersion(">=2.4")
+class DaemonUsageSuggestionCrossVersionTest extends ToolingApiSpecification {
+
+    def "does not print suggestion to use the daemon when using tooling api in embedded mode"() {
+        when:
+        def operation = withConnection { ProjectConnection connection ->
+            def build = connection.newBuild()
+            def operation = new ConfigurableOperation(build)
+            build.forTasks("help").run()
+            operation
+        }
+
+        then:
+        !operation.standardOutput.contains(DaemonUsageSuggestingBuildActionExecuter.PLEASE_USE_DAEMON_MESSAGE_PREFIX)
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/GradleProjectCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/GradleProjectCrossVersionSpec.groovy
new file mode 100644
index 0000000..5a154a0
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/GradleProjectCrossVersionSpec.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r24
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.model.GradleProject
+
+class GradleProjectCrossVersionSpec extends ToolingApiSpecification {
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "provide getProjectDirectory on GradleProject"() {
+        file("build.gradle")
+
+        when:
+        def gradleProject = withConnection { ProjectConnection connection ->
+            connection.getModel(GradleProject.class)
+        }
+
+        then:
+        gradleProject != null
+        gradleProject.projectDirectory != null
+        gradleProject.projectDirectory.absolutePath == temporaryFolder.testDirectory.absolutePath
+    }
+
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/ModelTasksToolingApiCrossVersionTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/ModelTasksToolingApiCrossVersionTest.groovy
new file mode 100644
index 0000000..00e2187
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/ModelTasksToolingApiCrossVersionTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.r24
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.tooling.model.GradleProject
+
+ at TargetGradleVersion(">=2.4")
+class ModelTasksToolingApiCrossVersionTest extends ToolingApiSpecification {
+
+    def setup() {
+        file('build.gradle') << '''
+            model {
+                tasks {
+                    create("fromModel") {
+                        doLast {
+                            buildDir.mkdirs()
+                            new File(buildDir, "output.txt") << "from model rule defined task"
+                        }
+                    }
+                }
+            }
+        '''
+    }
+
+    def "tasks added using model rule are visible from tooling api"() {
+        when:
+        GradleProject project = withConnection { it.getModel(GradleProject.class) }
+
+        then:
+        project.tasks*.name.contains "fromModel"
+    }
+
+    def "tasks added using model rule can be executed"() {
+        when:
+        withConnection { connection ->
+            def build = connection.newBuild()
+            build.forTasks("fromModel")
+            build.run()
+        }
+
+        then:
+        file('build/output.txt').text == "from model rule defined task"
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressCrossVersionSpec.groovy
new file mode 100644
index 0000000..649ad45
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressCrossVersionSpec.groovy
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.tooling.r24
+
+import groovy.transform.NotYetImplemented
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.events.FinishEvent
+import org.gradle.tooling.events.StartEvent
+import org.gradle.tooling.events.test.JvmTestKind
+import org.gradle.tooling.events.test.TestFailureResult
+import org.gradle.tooling.events.test.TestProgressEvent
+import org.gradle.tooling.events.test.TestProgressListener
+import org.gradle.tooling.events.test.TestSkippedResult
+import org.gradle.tooling.events.test.TestSuccessResult
+import org.gradle.tooling.model.gradle.BuildInvocations
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+
+import java.util.concurrent.ConcurrentLinkedQueue
+
+class TestProgressCrossVersionSpec extends ToolingApiSpecification {
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=1.0-milestone-8 <2.4")
+    def "ignores listeners when Gradle version does not generate test events"() {
+        given:
+        goodCode()
+
+        when:
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener({ throw new RuntimeException() } as TestProgressListener).run()
+        }
+
+        then:
+        noExceptionThrown()
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive test progress events when requesting a model"() {
+        given:
+        goodCode()
+
+        when: "asking for a model and specifying some test task(s) to run first"
+        List<TestProgressEvent> result = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.model(BuildInvocations.class).forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        result.add(event)
+                    }
+                }).get()
+        }
+
+        then: "test progress events must be forwarded to the attached listeners"
+        result.size() > 0
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive test progress events when launching a build"() {
+        given:
+        goodCode()
+
+        when: "launching a build"
+        List<TestProgressEvent> result = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        result.add(event)
+                    }
+                }).run()
+        }
+
+        then: "test progress events must be forwarded to the attached listeners"
+        result.size() > 0
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "build aborts if a test listener throws an exception"() {
+        given:
+        goodCode()
+
+        when: "launching a build"
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        throw new IllegalStateException("Throwing an exception on purpose")
+                    }
+                }).run()
+        }
+
+        then: "build aborts if the test listener throws an exception"
+        thrown(GradleConnectionException)
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive current test progress event even if one of multiple test listeners throws an exception"() {
+        given:
+        goodCode()
+
+        when: "launching a build"
+        List<TestProgressEvent> resultsOfFirstListener = new ArrayList<TestProgressEvent>()
+        List<TestProgressEvent> resultsOfLastListener = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        resultsOfFirstListener.add(event)
+                    }
+                }).addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        throw new IllegalStateException("Throwing an exception on purpose")
+                    }
+                }).addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        resultsOfLastListener.add(event)
+                    }
+                }).run()
+        }
+
+        then: "current test progress event must still be forwarded to the attached listeners even if one of the listeners throws an exception"
+        thrown(GradleConnectionException)
+        resultsOfFirstListener.size() == 1
+        resultsOfLastListener.size() == 1
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive test progress events for successful test run"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+        """
+
+        file("src/test/java/example/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Test public void foo() throws Exception {
+                     Thread.sleep(100);  // sleep for a moment to ensure test duration is > 0 (due to limited clock resolution)
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+
+        when:
+        List<TestProgressEvent> result = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        assert event != null
+                        result.add(event)
+                    }
+                }).run()
+        }
+
+        then:
+        result.size() % 2 == 0          // same number of start events as finish events
+        result.size() == 8              // root suite, test process suite, test class suite, test method (each with a start and finish event)
+        result.each {
+            assert it.displayName == it.toString()
+            assert it.descriptor.displayName == it.descriptor.toString()
+        }
+
+        def rootStartedEvent = result[0]
+        rootStartedEvent instanceof StartEvent &&
+                rootStartedEvent.eventTime > 0 &&
+                rootStartedEvent.displayName == "Gradle Test Run :test started" &&
+                rootStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                rootStartedEvent.descriptor.name == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.displayName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.suiteName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.className == null &&
+                rootStartedEvent.descriptor.methodName == null &&
+                rootStartedEvent.descriptor.parent == null
+        def testProcessStartedEvent = result[1]
+        testProcessStartedEvent instanceof StartEvent &&
+                testProcessStartedEvent.eventTime > 0 &&
+                testProcessStartedEvent.displayName == "Gradle Test Executor 2 started" &&
+                testProcessStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testProcessStartedEvent.descriptor.name == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.displayName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.suiteName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.className == null &&
+                testProcessStartedEvent.descriptor.methodName == null &&
+                testProcessStartedEvent.descriptor.parent == rootStartedEvent.descriptor
+        def testClassStartedEvent = result[2]
+        testClassStartedEvent instanceof StartEvent &&
+                testClassStartedEvent.eventTime > 0 &&
+                testClassStartedEvent.displayName == "Test class example.MyTest started" &&
+                testClassStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testClassStartedEvent.descriptor.name == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.displayName == 'Test class example.MyTest' &&
+                testClassStartedEvent.descriptor.suiteName == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.className == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.methodName == null &&
+                testClassStartedEvent.descriptor.parent == testProcessStartedEvent.descriptor
+        def testStartedEvent = result[3]
+        testStartedEvent instanceof StartEvent &&
+                testStartedEvent.eventTime > 0 &&
+                testStartedEvent.displayName == "Test foo(example.MyTest) started" &&
+                testStartedEvent.descriptor.jvmTestKind == JvmTestKind.ATOMIC &&
+                testStartedEvent.descriptor.name == 'foo' &&
+                testStartedEvent.descriptor.displayName == 'Test foo(example.MyTest)' &&
+                testStartedEvent.descriptor.suiteName == null &&
+                testStartedEvent.descriptor.className == 'example.MyTest' &&
+                testStartedEvent.descriptor.methodName == 'foo' &&
+                testStartedEvent.descriptor.parent == testClassStartedEvent.descriptor
+        def testSucceededEvent = result[4]
+        testSucceededEvent instanceof FinishEvent &&
+                testSucceededEvent.eventTime >= testSucceededEvent.result.endTime &&
+                testSucceededEvent.displayName == "Test foo(example.MyTest) succeeded" &&
+                testSucceededEvent.descriptor == testStartedEvent.descriptor &&
+                testSucceededEvent.result instanceof TestSuccessResult &&
+                testSucceededEvent.result.startTime == testStartedEvent.eventTime &&
+                testSucceededEvent.result.endTime > testSucceededEvent.result.startTime &&
+                testSucceededEvent.result.endTime == testSucceededEvent.eventTime
+        def testClassSucceededEvent = result[5]
+        testClassSucceededEvent instanceof FinishEvent &&
+                testClassSucceededEvent.eventTime >= testClassSucceededEvent.result.endTime &&
+                testClassSucceededEvent.displayName == "Test class example.MyTest succeeded" &&
+                testClassSucceededEvent.descriptor == testClassStartedEvent.descriptor &&
+                testClassSucceededEvent.result instanceof TestSuccessResult &&
+                testClassSucceededEvent.result.startTime == testClassStartedEvent.eventTime &&
+                testClassSucceededEvent.result.endTime > testClassSucceededEvent.result.startTime &&
+                testClassSucceededEvent.result.endTime == testClassSucceededEvent.eventTime
+        def testProcessSucceededEvent = result[6]
+        testProcessSucceededEvent instanceof FinishEvent &&
+                testProcessSucceededEvent.eventTime >= testProcessSucceededEvent.result.endTime &&
+                testProcessSucceededEvent.displayName == "Gradle Test Executor 2 succeeded" &&
+                testProcessSucceededEvent.descriptor == testProcessStartedEvent.descriptor &&
+                testProcessSucceededEvent.result instanceof TestSuccessResult &&
+                testProcessSucceededEvent.result.startTime == testProcessStartedEvent.eventTime &&
+                testProcessSucceededEvent.result.endTime > testProcessSucceededEvent.result.startTime &&
+                testProcessSucceededEvent.result.endTime == testProcessSucceededEvent.eventTime
+        def rootSucceededEvent = result[7]
+        rootSucceededEvent instanceof FinishEvent &&
+                rootSucceededEvent.eventTime >= rootSucceededEvent.result.endTime &&
+                rootSucceededEvent.displayName == "Gradle Test Run :test succeeded" &&
+                rootSucceededEvent.descriptor == rootStartedEvent.descriptor &&
+                rootSucceededEvent.result instanceof TestSuccessResult &&
+                rootSucceededEvent.result.startTime == rootStartedEvent.eventTime &&
+                rootSucceededEvent.result.endTime > rootSucceededEvent.result.startTime &&
+                rootSucceededEvent.result.endTime == rootSucceededEvent.eventTime
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive test progress events for failed test run"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+            test.ignoreFailures = true
+        """
+
+        file("src/test/java/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Test public void foo() throws Exception {
+                     Thread.sleep(100);  // sleep for a moment to ensure test duration is > 0 (due to limited clock resolution)
+                     throw new RuntimeException("broken", new RuntimeException("nope"));
+                }
+            }
+        """
+
+        when:
+        List<TestProgressEvent> result = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        assert event != null
+                        result.add(event)
+                    }
+                }).run()
+        }
+
+        then:
+        result.size() % 2 == 0          // same number of start events as finish events
+        result.size() == 8              // root suite, test process suite, test class suite, test method (each with a start and finish event)
+        result.each {
+            assert it.displayName == it.toString()
+            assert it.descriptor.displayName == it.descriptor.toString()
+        }
+
+        def rootStartedEvent = result[0]
+        rootStartedEvent instanceof StartEvent &&
+                rootStartedEvent.eventTime > 0 &&
+                rootStartedEvent.displayName == "Gradle Test Run :test started" &&
+                rootStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                rootStartedEvent.descriptor.name == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.displayName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.suiteName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.className == null &&
+                rootStartedEvent.descriptor.methodName == null &&
+                rootStartedEvent.descriptor.parent == null
+        def testProcessStartedEvent = result[1]
+        testProcessStartedEvent instanceof StartEvent &&
+                testProcessStartedEvent.eventTime > 0 &&
+                testProcessStartedEvent.displayName == "Gradle Test Executor 2 started" &&
+                testProcessStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testProcessStartedEvent.descriptor.name == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.displayName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.suiteName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.className == null &&
+                testProcessStartedEvent.descriptor.methodName == null &&
+                testProcessStartedEvent.descriptor.parent == rootStartedEvent.descriptor
+        def testClassStartedEvent = result[2]
+        testClassStartedEvent instanceof StartEvent &&
+                testClassStartedEvent.eventTime > 0 &&
+                testClassStartedEvent.displayName == "Test class example.MyTest started" &&
+                testClassStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testClassStartedEvent.descriptor.name == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.displayName == 'Test class example.MyTest' &&
+                testClassStartedEvent.descriptor.suiteName == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.className == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.methodName == null &&
+                testClassStartedEvent.descriptor.parent == testProcessStartedEvent.descriptor
+        def testStartedEvent = result[3]
+        testStartedEvent instanceof StartEvent &&
+                testStartedEvent.eventTime > 0 &&
+                testStartedEvent.displayName == "Test foo(example.MyTest) started" &&
+                testStartedEvent.descriptor.jvmTestKind == JvmTestKind.ATOMIC &&
+                testStartedEvent.descriptor.name == 'foo' &&
+                testStartedEvent.descriptor.displayName == 'Test foo(example.MyTest)' &&
+                testStartedEvent.descriptor.suiteName == null &&
+                testStartedEvent.descriptor.className == 'example.MyTest' &&
+                testStartedEvent.descriptor.methodName == 'foo' &&
+                testStartedEvent.descriptor.parent == testClassStartedEvent.descriptor
+        def testFailedEvent = result[4]
+        testFailedEvent instanceof FinishEvent &&
+                testFailedEvent.eventTime >= testFailedEvent.result.endTime &&
+                testFailedEvent.displayName == "Test foo(example.MyTest) failed" &&
+                testFailedEvent.descriptor == testStartedEvent.descriptor &&
+                testFailedEvent.result instanceof TestFailureResult &&
+                testFailedEvent.result.startTime == testStartedEvent.eventTime &&
+                testFailedEvent.result.endTime == testFailedEvent.eventTime &&
+                testFailedEvent.result.failures.size() == 1 &&
+                testFailedEvent.result.failures[0].message == 'broken' &&
+                testFailedEvent.result.failures[0].description.startsWith("java.lang.RuntimeException: broken") &&
+                testFailedEvent.result.failures[0].description.contains("at example.MyTest.foo(MyTest.java:6)") &&
+                testFailedEvent.result.failures[0].causes.size() == 1 &&
+                testFailedEvent.result.failures[0].causes[0].message == 'nope' &&
+                testFailedEvent.result.failures[0].causes[0].causes.empty
+        def testClassFailedEvent = result[5]
+        testClassFailedEvent instanceof FinishEvent &&
+                testClassFailedEvent.eventTime >= testClassFailedEvent.result.endTime &&
+                testClassFailedEvent.displayName == "Test class example.MyTest failed" &&
+                testClassFailedEvent.descriptor == testClassStartedEvent.descriptor &&
+                testClassFailedEvent.result instanceof TestFailureResult &&
+                testClassFailedEvent.result.startTime == testClassStartedEvent.eventTime &&
+                testClassFailedEvent.result.endTime == testClassFailedEvent.eventTime &&
+                testClassFailedEvent.result.failures.size() == 0
+        def testProcessFailedEvent = result[6]
+        testProcessFailedEvent instanceof FinishEvent &&
+                testProcessFailedEvent.eventTime >= testProcessFailedEvent.result.endTime &&
+                testProcessFailedEvent.displayName == "Gradle Test Executor 2 failed" &&
+                testProcessFailedEvent.descriptor == testProcessStartedEvent.descriptor &&
+                testProcessFailedEvent.result instanceof TestFailureResult &&
+                testProcessFailedEvent.result.startTime == testProcessStartedEvent.eventTime &&
+                testProcessFailedEvent.result.endTime == testProcessFailedEvent.eventTime &&
+                testProcessFailedEvent.result.failures.size() == 0
+        def rootFailedEvent = result[7]
+        rootFailedEvent instanceof FinishEvent &&
+                rootFailedEvent.eventTime >= rootFailedEvent.result.endTime &&
+                rootFailedEvent.displayName == "Gradle Test Run :test failed" &&
+                rootFailedEvent.descriptor == rootStartedEvent.descriptor &&
+                rootFailedEvent.result instanceof TestFailureResult &&
+                rootFailedEvent.result.startTime == rootStartedEvent.eventTime &&
+                rootFailedEvent.result.endTime == rootFailedEvent.eventTime &&
+                rootFailedEvent.result.failures.size() == 0
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "receive test progress events for skipped test run"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+        """
+
+        file("src/test/java/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Ignore @org.junit.Test public void foo() throws Exception {
+                     Thread.sleep(100);  // sleep for a moment to ensure test duration is > 0 (due to limited clock resolution)
+                     org.junit.Assert.assertEquals(1, 2);
+                }
+            }
+        """
+
+        when:
+        List<TestProgressEvent> result = new ArrayList<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        assert event != null
+                        result.add(event)
+                    }
+                }).run()
+        }
+
+        then:
+        result.size() % 2 == 0          // same number of start events as finish events
+        result.size() == 8              // root suite, test process suite, test class suite, test method (each with a start and finish event)
+        result.each {
+            assert it.displayName == it.toString()
+            assert it.descriptor.displayName == it.descriptor.toString()
+        }
+
+        def rootStartedEvent = result[0]
+        rootStartedEvent instanceof StartEvent &&
+                rootStartedEvent.eventTime > 0 &&
+                rootStartedEvent.displayName == "Gradle Test Run :test started" &&
+                rootStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                rootStartedEvent.descriptor.name == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.displayName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.suiteName == 'Gradle Test Run :test' &&
+                rootStartedEvent.descriptor.className == null &&
+                rootStartedEvent.descriptor.methodName == null &&
+                rootStartedEvent.descriptor.parent == null
+        def testProcessStartedEvent = result[1]
+        testProcessStartedEvent instanceof StartEvent &&
+                testProcessStartedEvent.eventTime > 0 &&
+                testProcessStartedEvent.displayName == "Gradle Test Executor 2 started" &&
+                testProcessStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testProcessStartedEvent.descriptor.name == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.displayName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.suiteName == 'Gradle Test Executor 2' &&
+                testProcessStartedEvent.descriptor.className == null &&
+                testProcessStartedEvent.descriptor.methodName == null &&
+                testProcessStartedEvent.descriptor.parent == rootStartedEvent.descriptor
+        def testClassStartedEvent = result[2]
+        testClassStartedEvent instanceof StartEvent &&
+                testClassStartedEvent.eventTime > 0 &&
+                testClassStartedEvent.displayName == "Test class example.MyTest started" &&
+                testClassStartedEvent.descriptor.jvmTestKind == JvmTestKind.SUITE &&
+                testClassStartedEvent.descriptor.name == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.displayName == "Test class example.MyTest" &&
+                testClassStartedEvent.descriptor.suiteName == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.className == 'example.MyTest' &&
+                testClassStartedEvent.descriptor.methodName == null &&
+                testClassStartedEvent.descriptor.parent == testProcessStartedEvent.descriptor
+        def testStartedEvent = result[3]
+        testStartedEvent instanceof StartEvent &&
+                testStartedEvent.eventTime > 0 &&
+                testStartedEvent.displayName == "Test foo(example.MyTest) started" &&
+                testStartedEvent.descriptor.jvmTestKind == JvmTestKind.ATOMIC &&
+                testStartedEvent.descriptor.name == 'foo' &&
+                testStartedEvent.descriptor.displayName == 'Test foo(example.MyTest)' &&
+                testStartedEvent.descriptor.suiteName == null &&
+                testStartedEvent.descriptor.className == 'example.MyTest' &&
+                testStartedEvent.descriptor.methodName == 'foo' &&
+                testStartedEvent.descriptor.parent == testClassStartedEvent.descriptor
+        def testSkippedEvent = result[4]
+        testSkippedEvent instanceof FinishEvent &&
+                testSkippedEvent.eventTime > 0 &&
+                testSkippedEvent.displayName == "Test foo(example.MyTest) skipped" &&
+                testSkippedEvent.descriptor == testStartedEvent.descriptor &&
+                testSkippedEvent.result instanceof TestSkippedResult &&
+                testSkippedEvent.result.startTime == testStartedEvent.eventTime &&
+                testSkippedEvent.result.endTime == testSkippedEvent.eventTime
+        def testClassSucceededEvent = result[5]
+        testClassSucceededEvent instanceof FinishEvent &&
+                testClassSucceededEvent.eventTime >= testClassSucceededEvent.result.endTime &&
+                testClassSucceededEvent.displayName == "Test class example.MyTest succeeded" &&
+                testClassSucceededEvent.descriptor == testClassStartedEvent.descriptor &&
+                testClassSucceededEvent.result instanceof TestSuccessResult &&
+                testClassSucceededEvent.result.startTime == testClassStartedEvent.eventTime &&
+                testClassSucceededEvent.result.endTime == testClassSucceededEvent.eventTime
+        def testProcessSucceededEvent = result[6]
+        testProcessSucceededEvent instanceof FinishEvent &&
+                testProcessSucceededEvent.eventTime >= testProcessSucceededEvent.result.endTime &&
+                testProcessSucceededEvent.displayName == "Gradle Test Executor 2 succeeded" &&
+                testProcessSucceededEvent.descriptor == testProcessStartedEvent.descriptor &&
+                testProcessSucceededEvent.result instanceof TestSuccessResult &&
+                testProcessSucceededEvent.result.startTime == testProcessStartedEvent.eventTime &&
+                testProcessSucceededEvent.result.endTime == testProcessSucceededEvent.eventTime
+        def rootSucceededEvent = result[7]
+        rootSucceededEvent instanceof FinishEvent &&
+                rootSucceededEvent.eventTime >= rootSucceededEvent.result.endTime &&
+                rootSucceededEvent.displayName == "Gradle Test Run :test succeeded" &&
+                rootSucceededEvent.descriptor == rootStartedEvent.descriptor &&
+                rootSucceededEvent.result instanceof TestSuccessResult &&
+                rootSucceededEvent.result.startTime == rootStartedEvent.eventTime &&
+                rootSucceededEvent.result.endTime == rootSucceededEvent.eventTime
+    }
+
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "test progress event ids are unique across multiple test workers"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+            test.maxParallelForks = 2
+        """
+
+        file("src/test/java/example/MyTest1.java") << """
+            package example;
+            public class MyTest1 {
+                @org.junit.Test public void alpha() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void beta() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void gamma() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void delta() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+        file("src/test/java/example/MyTest2.java") << """
+            package example;
+            public class MyTest2 {
+                @org.junit.Test public void one() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void two() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void three() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void four() throws Exception {
+                     Thread.sleep(100);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+
+        when:
+        Queue<TestProgressEvent> result = new ConcurrentLinkedQueue<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        assert event != null
+                        result.add(event)
+                    }
+                }).run()
+        }
+
+        then: "start and end event is sent for each node in the test tree"
+        result.size() % 2 == 0                // same number of start events as finish events
+        result.size() == 2 * (1 + 2 + 2 + 8)  // 1 root suite, 2 test processes, 2 tests classes, 8 tests (each with a start and finish event)
+
+        then: "each node in the test tree has its own description"
+        result.collect { it.descriptor }.toSet().size() == 13
+
+        then: "number of nodes under the root suite is equal to the number of test worker processes"
+        result.findAll { it.descriptor.parent == null }.toSet().size() == 2  // 1 root suite with no further parent (start & finish events)
+    }
+
+    @Requires(TestPrecondition.NOT_WINDOWS)
+    @ToolingApiVersion(">=2.4")
+    @TargetGradleVersion(">=2.4")
+    def "test progress event ids are unique across multiple test tasks, even when run in parallel"() {
+        given:
+        projectDir.createFile('settings.gradle') << """
+            include ':sub1'
+            include ':sub2'
+        """
+        projectDir.createFile('build.gradle')
+
+        [projectDir.createDir('sub1'), projectDir.createDir('sub2')].eachWithIndex { TestFile it, def index ->
+            it.file('build.gradle') << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true
+            test.maxParallelForks = 2
+            test.ignoreFailures = true
+        """
+            it.file("src/test/java/sub/MyUnitTest1${index}.java") << """
+            package sub;
+            public class MyUnitTest1$index {
+                @org.junit.Test public void alpha() throws Exception {
+                     Thread.sleep(300);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void beta() throws Exception {
+                     Thread.sleep(1000);
+                     org.junit.Assert.assertEquals(2, 1);
+                }
+            }
+        """
+            it.file("src/test/java/sub/MyUnitTest2${index}.java") << """
+            package sub;
+            public class MyUnitTest2$index {
+                @org.junit.Test public void one() throws Exception {
+                     Thread.sleep(1000);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void two() throws Exception {
+                     Thread.sleep(300);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void three() throws Exception {
+                     Thread.sleep(300);
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+                @org.junit.Test public void four() throws Exception {
+                     Thread.sleep(300);
+                     org.junit.Assert.assertEquals(3, 1);
+                }
+            }
+        """
+        }
+
+        when:
+        Queue<TestProgressEvent> result = new ConcurrentLinkedQueue<TestProgressEvent>()
+        withConnection {
+            ProjectConnection connection ->
+                connection.newBuild().forTasks('test').addTestProgressListener(new TestProgressListener() {
+                    @Override
+                    void statusChanged(TestProgressEvent event) {
+                        assert event != null
+                        result.add(event)
+                    }
+                }).withArguments('--parallel').run()
+        }
+
+        then: "start and end event is sent for each node in the test tree"
+        result.size() % 2 == 0                    // same number of start events as finish events
+        result.size() == 2 * 2 * (1 + 2 + 2 + 6)  // two test tasks with each: 1 root suite, 2 test processes, 2 tests classes, 6 tests (each with a start and finish event)
+
+        then: "each node in the test tree has its own description"
+        result.collect { it.descriptor }.toSet().size() == 2 * 11
+
+        then: "number of nodes under the root suite is equal to the number of test worker processes"
+        result.findAll { it.descriptor.parent == null }.toSet().size() == 4  // 2 root suites with no further parent (start & finish events)
+
+        then: "names for root suites and worker suites are consistent"
+        result.findAll { it.descriptor.name =~ 'Gradle Test Run :sub[1|2]:test' }.toSet().size() == 4  // 2 root suites for 2 tasks (start & finish events)
+        result.findAll { it.descriptor.name =~ 'Gradle Test Executor \\d+' }.toSet().size() == 8       // 2 test processes for each task (start & finish events)
+    }
+
+    def goodCode() {
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+        """
+
+        file("src/test/java/example/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Test public void foo() throws Exception {
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+    }
+
+    @TargetGradleVersion('>=2.4')
+    @ToolingApiVersion('>=2.4')
+    @NotYetImplemented
+    def "should receive test events from buildSrc"() {
+        buildFile << """task dummy()"""
+        file("buildSrc/build.gradle") << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+        """
+        file("buildSrc/src/test/java/example/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Test public void foo() throws Exception {
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+
+        when:
+        def result = []
+        withConnection { ProjectConnection connection ->
+            connection.newBuild().forTasks('dummy').addTestProgressListener { TestProgressEvent event ->
+                assert event != null
+                result << event
+            }.run()
+        }
+
+        then:
+        !result.empty
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressDaemonErrorsCrossVersionSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressDaemonErrorsCrossVersionSpec.groovy
new file mode 100644
index 0000000..d840cf7
--- /dev/null
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/r24/TestProgressDaemonErrorsCrossVersionSpec.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.tooling.r24
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.events.test.TestProgressEvent
+
+class TestProgressDaemonErrorsCrossVersionSpec extends ToolingApiSpecification {
+
+    def goodCode() {
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies { testCompile 'junit:junit:4.12' }
+            compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
+        """
+
+        file("src/test/java/example/MyTest.java") << """
+            package example;
+            public class MyTest {
+                @org.junit.Test public void foo() throws Exception {
+                     org.junit.Assert.assertEquals(1, 1);
+                }
+            }
+        """
+    }
+
+    void setup() {
+        toolingApi.requireIsolatedDaemons()
+    }
+
+    @TargetGradleVersion('>=2.4')
+    @ToolingApiVersion('>=2.4')
+    def "should received failed event when daemon disappears unexpectedly"() {
+        given:
+        goodCode()
+
+        when:
+        def result = []
+        withConnection { ProjectConnection connection ->
+            connection.newBuild().forTasks('test').addTestProgressListener { TestProgressEvent event ->
+                result << event
+                toolingApi.daemons.daemon.kill()
+            }.run()
+        }
+
+        then: "build fails with a DaemonDisappearedException"
+        GradleConnectionException ex = thrown()
+        ex.cause.message.contains('Gradle build daemon disappeared unexpectedly')
+
+        and: "a single event was received"
+        result.size() == 1
+    }
+
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildActionExecuter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildActionExecuter.java
index dfd7d5c..d5046b2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildActionExecuter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildActionExecuter.java
@@ -17,8 +17,11 @@
 package org.gradle.tooling;
 
 import org.gradle.api.Incubating;
-import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
-import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException;
+import org.gradle.tooling.events.test.TestProgressListener;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 /**
  * Used to execute a {@link BuildAction} in the build process.
@@ -28,22 +31,87 @@ import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException;
  */
 @Incubating
 public interface BuildActionExecuter<T> extends LongRunningOperation {
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> withArguments(String... arguments);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> setStandardOutput(OutputStream outputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> setStandardError(OutputStream outputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    BuildActionExecuter<T> setColorOutput(boolean colorOutput);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> setStandardInput(InputStream inputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> setJavaHome(File javaHome);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> setJvmArguments(String... jvmArguments);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    BuildActionExecuter<T> addProgressListener(ProgressListener listener);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.4
+     */
+    @Incubating
+    BuildActionExecuter<T> addTestProgressListener(TestProgressListener listener);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    BuildActionExecuter<T> withCancellationToken(CancellationToken cancellationToken);
+
     /**
      * Runs the action, blocking until its result is available.
      *
      * @throws UnsupportedVersionException When the target Gradle version does not support build action execution.
-     * @throws UnsupportedOperationConfigurationException
+     * @throws org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
      *          When the target Gradle version does not support some requested configuration option such as
      *          {@link #setStandardInput(java.io.InputStream)}, {@link #setJavaHome(java.io.File)},
      *          {@link #setJvmArguments(String...)}.
-     * @throws UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
+     * @throws org.gradle.tooling.exceptions.UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
      * @throws BuildActionFailureException When the build action fails with an exception.
+     * @throws BuildCancelledException When the operation was cancelled before it completed successfully.
      * @throws BuildException On some failure executing the Gradle build.
      * @throws GradleConnectionException On some other failure using the connection.
      * @throws IllegalStateException When the connection has been closed or is closing.
      * @since 1.8
      */
-    T run() throws GradleConnectionException, IllegalStateException, UnsupportedOperationConfigurationException, UnsupportedVersionException, UnsupportedBuildArgumentException, BuildException, BuildActionFailureException;
+    T run() throws GradleConnectionException, IllegalStateException;
 
     /**
      * Starts executing the action, passing the result to the given handler when complete. This method returns immediately, and the result is later passed to the given handler's {@link
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildCancelledException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildCancelledException.java
new file mode 100644
index 0000000..40450b5
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildCancelledException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Thrown when a {@link org.gradle.tooling.LongRunningOperation} is cancelled before the operation completes.
+ *
+ * @since 2.1
+ */
+ at Incubating
+public class BuildCancelledException extends GradleConnectionException {
+    public BuildCancelledException(String message) {
+        super(message);
+    }
+
+    public BuildCancelledException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java
index 341cb33..bc21d78 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/BuildLauncher.java
@@ -16,8 +16,7 @@
 package org.gradle.tooling;
 
 import org.gradle.api.Incubating;
-import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
-import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException;
+import org.gradle.tooling.events.test.TestProgressListener;
 import org.gradle.tooling.model.Launchable;
 import org.gradle.tooling.model.Task;
 
@@ -76,6 +75,69 @@ import java.io.OutputStream;
  */
 public interface BuildLauncher extends LongRunningOperation {
     /**
+     * {@inheritDoc}
+     * @since 1.0
+     */
+    BuildLauncher withArguments(String ... arguments);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-3
+     */
+    BuildLauncher setStandardOutput(OutputStream outputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-3
+     */
+    BuildLauncher setStandardError(OutputStream outputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    BuildLauncher setColorOutput(boolean colorOutput);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-7
+     */
+    BuildLauncher setStandardInput(InputStream inputStream);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-8
+     */
+    BuildLauncher setJavaHome(File javaHome);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-9
+     */
+    BuildLauncher setJvmArguments(String... jvmArguments);
+
+    /**
+     * {@inheritDoc}
+     * @since 1.0-milestone-3
+     */
+    BuildLauncher addProgressListener(ProgressListener listener);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.4
+     */
+    @Incubating
+    BuildLauncher addTestProgressListener(TestProgressListener listener);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    BuildLauncher withCancellationToken(CancellationToken cancellationToken);
+
+    /**
      * Sets the tasks to be executed. If no tasks are specified, the project's default tasks are executed.
      *
      * @param tasks The paths of the tasks to be executed. Relative paths are evaluated relative to the project for which this launcher was created.
@@ -127,63 +189,21 @@ public interface BuildLauncher extends LongRunningOperation {
     BuildLauncher forLaunchables(Iterable<? extends Launchable> launchables);
 
     /**
-     * {@inheritDoc}
-     * @since 1.0
-     */
-    BuildLauncher withArguments(String ... arguments);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-3
-     */
-    BuildLauncher setStandardOutput(OutputStream outputStream);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-3
-     */
-    BuildLauncher setStandardError(OutputStream outputStream);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-7
-     */
-    BuildLauncher setStandardInput(InputStream inputStream);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-8
-     */
-    BuildLauncher setJavaHome(File javaHome);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-9
-     */
-    BuildLauncher setJvmArguments(String... jvmArguments);
-
-    /**
-     * {@inheritDoc}
-     * @since 1.0-milestone-3
-     */
-    BuildLauncher addProgressListener(ProgressListener listener);
-
-    /**
      * Executes the build, blocking until it is complete.
      *
      * @throws UnsupportedVersionException When the target Gradle version does not support build execution.
-     * @throws UnsupportedOperationConfigurationException
+     * @throws org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
      *          When the target Gradle version does not support some requested configuration option such as
      *          {@link #setStandardInput(java.io.InputStream)}, {@link #setJavaHome(java.io.File)},
      *          {@link #setJvmArguments(String...)}.
-     * @throws UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
+     * @throws org.gradle.tooling.exceptions.UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
      * @throws BuildException On some failure executing the Gradle build.
+     * @throws BuildCancelledException When the operation was cancelled before it completed successfully.
      * @throws GradleConnectionException On some other failure using the connection.
      * @throws IllegalStateException When the connection has been closed or is closing.
      * @since 1.0-milestone-3
      */
-    void run() throws GradleConnectionException, UnsupportedBuildArgumentException, IllegalStateException,
-            BuildException, UnsupportedVersionException, UnsupportedOperationConfigurationException;
+    void run() throws GradleConnectionException, IllegalStateException;
 
     /**
      * Launches the build. This method returns immediately, and the result is later passed to the given handler.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationToken.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationToken.java
new file mode 100644
index 0000000..3546893
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationToken.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Token that propagates notification that an operation should be cancelled. See {@link org.gradle.tooling.CancellationTokenSource} for details.
+ *
+ * <p>All implementations of this interface are required to be thread safe.</p>
+ *
+ * @since 2.1
+ */
+ at Incubating
+public interface CancellationToken {
+    /**
+     * Gets whether cancellation has been requested for this token.
+     * @return Cancellation status.
+     */
+    boolean isCancellationRequested();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationTokenSource.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationTokenSource.java
new file mode 100644
index 0000000..3e2117e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/CancellationTokenSource.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A {@code CancellationTokenSource} allows you to issue cancellation requests to one or more {@link org.gradle.tooling.LongRunningOperation}
+ * instances. To use a token source:
+ *
+ * <ul>
+ *     <li>Create a token source using {@link GradleConnector#newCancellationTokenSource()}.</li>
+ *     <li>Attach the token to one or more operations using {@link org.gradle.tooling.LongRunningOperation#withCancellationToken(CancellationToken)}.
+ *     You need to do this before you start the operation.
+ *     </li>
+ *     <li>Later, you can cancel the associated operations by calling {@link #cancel()} on this token source.</li>
+ * </ul>
+ *
+ * <p>All implementations of this interface are required to be thread safe.</p>
+ *
+ * @since 2.1
+ */
+ at Incubating
+public interface CancellationTokenSource {
+    /**
+     * Initiates cancel request. All operations that have been associated with this token will be cancelled.
+     *
+     * <p>It is assumed that the implementation will do 'best-effort' attempt to perform cancellation.
+     * This method returns immediately and if the cancellation is successful the cancelled operation
+     * will notify its {@link org.gradle.tooling.ResultHandler#onFailure(GradleConnectionException)}
+     * with a {@link BuildCancelledException} describing how it was cancelled.
+     * </p>
+     */
+    void cancel();
+
+    /**
+     * Returns a token associated with this {@code CancellationTokenSource}.
+     * Always returns the same instance.
+     *
+     * @return The cancellation token.
+     */
+    CancellationToken token();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/Failure.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/Failure.java
new file mode 100644
index 0000000..6cd8ce1
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/Failure.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+import java.util.List;
+
+/**
+ * Represents a failure. Failures are similar to exceptions but carry less information (only a message, a description and a cause) so
+ * they can be used in a wider scope than just the JVM where the exception failed.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface Failure {
+
+    /**
+     * Returns a short message (typically one line) for the failure.
+     *
+     * @return the failure message
+     */
+    @Nullable
+    String getMessage();
+
+    /**
+     * Returns a long description of the failure. For example, a stack trace.
+     *
+     * @return a long description of the failure
+     */
+    @Nullable
+    String getDescription();
+
+    /**
+     * Returns the underlying causes for this failure, if any.
+     *
+     * @return the causes for this failure. Returns an empty list if this failure has no causes.
+     */
+    List<? extends Failure> getCauses();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
index 75859f7..10bbb2e 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
@@ -61,12 +61,22 @@ public abstract class GradleConnector {
      * @since 1.0-milestone-3
      */
     public static GradleConnector newConnector() {
-        return new ConnectorServices().createConnector();
+        return ConnectorServices.createConnector();
     }
 
     /**
-     * Specifies which Gradle installation to use. This replaces any value specified using {@link #useDistribution(java.net.URI)} or {@link #useGradleVersion(String)}. Defaults to a project-specific
-     * Gradle version.
+     * Creates a new {@link CancellationTokenSource} that can be used to cancel one or more {@link org.gradle.tooling.LongRunningOperation} executions.
+     *
+     * @return The instance. Never returns {@code null}.
+     * @since 2.1
+     */
+    public static CancellationTokenSource newCancellationTokenSource() {
+        return ConnectorServices.createCancellationTokenSource();
+    }
+
+    /**
+     * Specifies which Gradle installation to use. This replaces any value specified using {@link #useDistribution(URI)}, {@link #useGradleVersion(String)}, or {@link #useBuildDistribution()}.
+     * Defaults to a project-specific Gradle version.
      *
      * @param gradleHome The Gradle installation directory.
      * @return this
@@ -76,7 +86,7 @@ public abstract class GradleConnector {
 
     /**
      * Specifies which Gradle version to use. The appropriate distribution is downloaded and installed into the user's Gradle home directory. This replaces any value specified using {@link
-     * #useInstallation(java.io.File)} or {@link #useDistribution(java.net.URI)}. Defaults to a project-specific Gradle version.
+     * #useInstallation(File)}, {@link #useDistribution(URI)}, or {@link #useBuildDistribution()}. Defaults to a project-specific Gradle version.
      *
      * @param gradleVersion The version to use.
      * @return this
@@ -86,7 +96,7 @@ public abstract class GradleConnector {
 
     /**
      * Specifies which Gradle distribution to use. The appropriate distribution is downloaded and installed into the user's Gradle home directory. This replaces any value specified using {@link
-     * #useInstallation(java.io.File)} or {@link #useGradleVersion(String)}. Defaults to a project-specific Gradle version.
+     * #useInstallation(File)}, {@link #useGradleVersion(String)}, or {@link #useBuildDistribution()}. Defaults to a project-specific Gradle version.
      *
      * @param gradleDistribution The distribution to use.
      * @return this
@@ -95,6 +105,16 @@ public abstract class GradleConnector {
     public abstract GradleConnector useDistribution(URI gradleDistribution);
 
     /**
+     * Specifies to use the Gradle distribution defined by the target Gradle build. The appropriate distribution defined by the target Gradle build is downloaded and installed into the user's
+     * Gradle home directory. If the target Gradle build does not define the distribution that it should be built with, the Gradle version of this connector is used. This replaces any value
+     * specified using {@link #useInstallation(File)}, {@link #useDistribution(URI)}, or {@link #useGradleVersion(String)}. Acts as the default behavior.
+     *
+     * @return this
+     * @since 2.3
+     */
+    public abstract GradleConnector useBuildDistribution();
+
+    /**
      * Specifies the working directory to use.
      *
      * @param projectDir The working directory.
@@ -120,6 +140,6 @@ public abstract class GradleConnector {
      * @throws GradleConnectionException On failure to establish a connection with the target Gradle version.
      * @since 1.0-milestone-3
      */
-    public abstract ProjectConnection connect() throws GradleConnectionException, UnsupportedVersionException;
+    public abstract ProjectConnection connect() throws GradleConnectionException;
 
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/LongRunningOperation.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/LongRunningOperation.java
index 7866b8a..4bfb312 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/LongRunningOperation.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/LongRunningOperation.java
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.gradle.tooling;
 
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.test.TestProgressListener;
+
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -58,6 +60,16 @@ public interface LongRunningOperation {
     LongRunningOperation setStandardError(OutputStream outputStream);
 
     /**
+     * Specifies whether to generate colored (ANSI encoded) output for logging. The default is to not generate color output.
+     *
+     * @param colorOutput {@code true} to request color output (using ANSI encoding).
+     * @return this
+     * @since 2.3
+     */
+    @Incubating
+    LongRunningOperation setColorOutput(boolean colorOutput);
+
+    /**
      * Sets the {@link java.io.InputStream} that will be used as standard input for this operation.
      * Defaults to an empty input stream.
      * <p>
@@ -79,7 +91,7 @@ public interface LongRunningOperation {
      * {@link org.gradle.tooling.model.build.BuildEnvironment} model contains information such as Java or Gradle environment.
      * If you want to get hold of this information you can ask tooling API to build this model.
      * <p>
-     * If not configured or null passed the sensible default will be used.
+     * If not configured or null is passed, then the sensible default will be used.
      *
      * @param javaHome to use for the Gradle process
      * @return this
@@ -97,7 +109,7 @@ public interface LongRunningOperation {
      * {@link org.gradle.tooling.model.build.BuildEnvironment} model contains information such as Java or Gradle environment.
      * If you want to get hold of this information you can ask tooling API to build this model.
      * <p>
-     * If not configured, null an empty array passed then the reasonable default will be used.
+     * If not configured, null, or an empty array is passed, then the reasonable default will be used.
      *
      * @param jvmArguments to use for the Gradle process
      * @return this
@@ -130,6 +142,8 @@ public interface LongRunningOperation {
      * <p>
      * See the example in the docs for {@link BuildLauncher}
      *
+     * If not configured, null, or an empty array is passed, then the reasonable default will be used.
+     *
      * @param arguments Gradle command line arguments
      * @return this
      * @since 1.0
@@ -145,4 +159,21 @@ public interface LongRunningOperation {
      */
     LongRunningOperation addProgressListener(ProgressListener listener);
 
+    /**
+     * Adds a test progress listener which will receive test progress events as the operation runs.
+     *
+     * @param listener The listener
+     * @return this
+     * @since 2.4
+     */
+    @Incubating
+    LongRunningOperation addTestProgressListener(TestProgressListener listener);
+
+    /**
+     * Sets the cancellation token to use to cancel the operation if required.
+     *
+     * @since 2.1
+     */
+    @Incubating
+    LongRunningOperation withCancellationToken(CancellationToken cancellationToken);
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java
index 645901b..cb2bd68 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ModelBuilder.java
@@ -16,8 +16,7 @@
 package org.gradle.tooling;
 
 import org.gradle.api.Incubating;
-import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
-import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException;
+import org.gradle.tooling.events.test.TestProgressListener;
 
 import java.io.File;
 import java.io.InputStream;
@@ -90,6 +89,13 @@ public interface ModelBuilder<T> extends LongRunningOperation {
 
     /**
      * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    ModelBuilder<T> setColorOutput(boolean colorOutput);
+
+    /**
+     * {@inheritDoc}
      * @since 1.0-milestone-7
      */
     ModelBuilder<T> setStandardInput(InputStream inputStream);
@@ -113,11 +119,26 @@ public interface ModelBuilder<T> extends LongRunningOperation {
     ModelBuilder<T> addProgressListener(ProgressListener listener);
 
     /**
-     * Specifies the tasks to execute before building the model. By default, no tasks are executed.
+     * {@inheritDoc}
+     * @since 2.4
+     */
+    @Incubating
+    ModelBuilder<T> addTestProgressListener(TestProgressListener listener);
+
+    /**
+     * {@inheritDoc}
+     * @since 2.3
+     */
+    @Incubating
+    ModelBuilder<T> withCancellationToken(CancellationToken cancellationToken);
+
+    /**
+     * Specifies the tasks to execute before building the model.
+     *
+     * If not configured, null, or an empty array is passed, then no tasks will be executed.
      *
      * @param tasks The paths of the tasks to be executed. Relative paths are evaluated relative to the project for which this launcher was created.
      * @return this
-     *
      * @since 1.2
      */
     @Incubating
@@ -129,17 +150,18 @@ public interface ModelBuilder<T> extends LongRunningOperation {
      * @return The model.
      * @throws UnsupportedVersionException When the target Gradle version does not support building models.
      * @throws UnknownModelException When the target Gradle version or build does not support the requested model.
-     * @throws UnsupportedOperationConfigurationException
+     * @throws org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
      *          When the target Gradle version does not support some requested configuration option such as
      *          {@link #setStandardInput(java.io.InputStream)}, {@link #setJavaHome(java.io.File)},
      *          {@link #setJvmArguments(String...)}.
-     * @throws UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
+     * @throws org.gradle.tooling.exceptions.UnsupportedBuildArgumentException When there is a problem with build arguments provided by {@link #withArguments(String...)}.
      * @throws BuildException On some failure executing the Gradle build.
+     * @throws BuildCancelledException When the operation was cancelled before it completed successfully.
      * @throws GradleConnectionException On some other failure using the connection.
      * @throws IllegalStateException When the connection has been closed or is closing.
      * @since 1.0-milestone-3
      */
-    T get() throws GradleConnectionException, UnsupportedVersionException, UnknownModelException, UnsupportedOperationConfigurationException, BuildException, IllegalStateException, UnsupportedBuildArgumentException;
+    T get() throws GradleConnectionException, IllegalStateException;
 
     /**
      * Starts fetching the model, passing the result to the given handler when complete. This method returns immediately, and the result is later passed to the given
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ResultHandler.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ResultHandler.java
index 5dec495..8dd4073 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ResultHandler.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ResultHandler.java
@@ -32,10 +32,8 @@ public interface ResultHandler<T> {
     void onComplete(T result);
 
     /**
-     * Handles failures. A failure happens when the target Gradle version does not support
-     * the features required to build this model. For example, when you have configured the long running operation with a settings
-     *  like: {@link LongRunningOperation#setStandardInput(java.io.InputStream)}, {@link LongRunningOperation#setJavaHome(java.io.File)}, {@link LongRunningOperation#setJvmArguments(String...)}
-     *  but those settings are not supported on the target Gradle.
+     * Handles a failed operation. This method is invoked once only for a given operation.
+     *
      * @param failure the failure
      * @since 1.0-milestone-3
      */
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FailureResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FailureResult.java
new file mode 100644
index 0000000..56cb970
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FailureResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.Failure;
+
+import java.util.List;
+
+/**
+ * Describes how an operation finished with failures.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface FailureResult extends OperationResult {
+
+    /**
+     * Returns the failures that occurred while running the operation, if any.
+     *
+     * @return the failures, empty if the operation failed without any specific failure information.
+     */
+    List<? extends Failure> getFailures();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FinishEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FinishEvent.java
new file mode 100644
index 0000000..7b3412f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/FinishEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * An event that informs about an operation having finished its execution.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface FinishEvent extends ProgressEvent {
+
+    /**
+     * Returns the result of the finished operation.
+     *
+     * @return the result of the finished operation
+     */
+    OperationResult getResult();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationDescriptor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationDescriptor.java
new file mode 100644
index 0000000..f940090
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationDescriptor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+/**
+ * Describes an operation for which an event has occurred.
+ *
+ * <p>You can use {@code equals()} to determine whether 2 different descriptors refer to the same operation.</p>
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface OperationDescriptor {
+
+    /**
+     * Returns the name of the operation. This name does not necessarily uniquely identify the operation.
+     *
+     * @return The name of the operation.
+     */
+    String getName();
+
+    /**
+     * Returns a human consumable display name for the operation.
+     *
+     * @return The display name of the operation.
+     */
+    String getDisplayName();
+
+    /**
+     * Returns the parent operation, if any.
+     *
+     * @return The parent operation.
+     */
+    @Nullable
+    OperationDescriptor getParent();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationResult.java
new file mode 100644
index 0000000..3bd9f02
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/OperationResult.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Describes the result of running an operation.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface OperationResult {
+
+    /**
+     * Returns the time when the operation started its execution.
+     *
+     * @return The start time, in milliseconds since the epoch.
+     */
+    long getStartTime();
+
+    /**
+     * Returns the time when the operation finished its execution.
+     *
+     * @return The end time, in milliseconds since the epoch.
+     */
+    long getEndTime();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/ProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/ProgressEvent.java
new file mode 100644
index 0000000..5427e84
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/ProgressEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Root interface for all events that signal progress while executing an operation. For example, an operation can be
+ * the execution of a build, of a task, of a test, etc. A progress event can, for example, signal that a test has started, a
+ * task has finished, etc.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface ProgressEvent {
+
+    /**
+     * Returns the time this event was triggered.
+     *
+     * @return The event time, in milliseconds since the epoch.
+     */
+    long getEventTime();
+
+    /**
+     * Returns a human consumable short description of the event.
+     *
+     * @return The short description of the event.
+     */
+    String getDisplayName();
+
+    /**
+     * Returns the description of the operation for which progress is reported.
+     *
+     * @return The description of the operation.
+     */
+    OperationDescriptor getDescriptor();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SkippedResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SkippedResult.java
new file mode 100644
index 0000000..573fa0c
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SkippedResult.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Describes how an operation was skipped.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface SkippedResult extends OperationResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/StartEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/StartEvent.java
new file mode 100644
index 0000000..f88991b
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/StartEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * An event that informs about an operation having started its execution.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface StartEvent extends ProgressEvent {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SuccessResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SuccessResult.java
new file mode 100644
index 0000000..43c38ba
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/SuccessResult.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Describes how an operation finished successfully.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface SuccessResult extends OperationResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseFinishEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseFinishEvent.java
new file mode 100644
index 0000000..e186e90
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseFinishEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.internal;
+
+import org.gradle.tooling.events.FinishEvent;
+import org.gradle.tooling.events.OperationDescriptor;
+import org.gradle.tooling.events.OperationResult;
+
+/**
+ * Base implementation of the {@code FinishEvent} interface.
+ */
+public abstract class BaseFinishEvent extends BaseProgressEvent implements FinishEvent {
+
+    private final OperationResult result;
+
+    public BaseFinishEvent(long eventTime, String displayName, OperationDescriptor descriptor, OperationResult result) {
+        super(eventTime, displayName, descriptor);
+        this.result = result;
+    }
+
+    @Override
+    public OperationResult getResult() {
+        return result;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseProgressEvent.java
new file mode 100644
index 0000000..b64bda6
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseProgressEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.internal;
+
+import org.gradle.tooling.events.OperationDescriptor;
+import org.gradle.tooling.events.ProgressEvent;
+
+/**
+ * Base class for {@code ProgressEvent} implementations.
+ */
+abstract class BaseProgressEvent implements ProgressEvent {
+
+    private final long eventTime;
+    private final String displayName;
+    private final OperationDescriptor descriptor;
+
+    BaseProgressEvent(long eventTime, String displayName, OperationDescriptor descriptor) {
+        this.eventTime = eventTime;
+        this.displayName = displayName;
+        this.descriptor = descriptor;
+    }
+
+    @Override
+    public long getEventTime() {
+        return eventTime;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public OperationDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    @Override
+    public String toString() {
+        return getDisplayName();
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseStartEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseStartEvent.java
new file mode 100644
index 0000000..2ea0837
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/internal/BaseStartEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.internal;
+
+import org.gradle.tooling.events.OperationDescriptor;
+import org.gradle.tooling.events.StartEvent;
+
+/**
+ * Base implementation of the {@code StartEvent} interface.
+ */
+public abstract class BaseStartEvent extends BaseProgressEvent implements StartEvent {
+
+    protected BaseStartEvent(long eventTime, String displayName, OperationDescriptor descriptor) {
+        super(eventTime, displayName, descriptor);
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/package-info.java
new file mode 100644
index 0000000..47834ec
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The interfaces and classes related to registering for event notifications and listening to dispatched events.
+ */
+package org.gradle.tooling.events;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestKind.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestKind.java
new file mode 100644
index 0000000..892a762
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestKind.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+
+/**
+ * Enumerates the different kinds of JVM tests. This allows to differentiate between test suites, atomic tests, etc.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public enum JvmTestKind {
+
+    SUITE("Test suite"),
+    ATOMIC("Atomic test"),
+    UNKNOWN("Unknown test kind");
+
+    private final String label;
+
+    JvmTestKind(String label) {
+        this.label = label;
+    }
+
+    /**
+     * Returns a label for the test kind. The label can be used to generate a prettified version of the test descriptor.
+     *
+     * @return a label corresponding to the test kind
+     */
+    public String getLabel() {
+        return label;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestOperationDescriptor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestOperationDescriptor.java
new file mode 100644
index 0000000..7ad37ae
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/JvmTestOperationDescriptor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.api.Nullable;
+
+/**
+ * Describes a test that runs on the JVM and for which an event has occurred. At least
+ * a suite name, class name, or method name is available for each JVM test.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface JvmTestOperationDescriptor extends TestOperationDescriptor {
+
+    /**
+     * Returns what kind of test this is.
+     *
+     * @return The test kind.
+     */
+    JvmTestKind getJvmTestKind();
+
+    /**
+     * Returns the name of the test suite, if any.
+     *
+     * @return The name of the test suite.
+     */
+    @Nullable
+    String getSuiteName();
+
+    /**
+     * Returns the name of the test class, if any.
+     *
+     * @return The name of the test class.
+     */
+    @Nullable
+    String getClassName();
+
+    /**
+     * Returns the name of the test method, if any.
+     *
+     * @return The name of the test method.
+     */
+    @Nullable
+    String getMethodName();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFailureResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFailureResult.java
new file mode 100644
index 0000000..ae60f30
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFailureResult.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.Failure;
+import org.gradle.tooling.events.FailureResult;
+
+import java.util.List;
+
+/**
+ * Describes how a test operation finished with failures.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestFailureResult extends TestOperationResult, FailureResult {
+
+    /**
+     * Returns the exceptions that occurred while running the test operation, if any.
+     *
+     * @return the exceptions, empty if the test operation failed without any exceptions
+     */
+    @Override
+    List<? extends Failure> getFailures();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFinishEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFinishEvent.java
new file mode 100644
index 0000000..1f48104
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestFinishEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.FinishEvent;
+
+/**
+ * An event that informs about a test having finished its execution. You can query the result of the
+ * test using {@link #getResult()}.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestFinishEvent extends TestProgressEvent, FinishEvent {
+
+    /**
+     * Returns the result of the finished test operation. Currently, the result will be one of the following
+     * sub-types:
+     *
+     * <ul>
+     *     <li>{@link TestSuccessResult}</li>
+     *     <li>{@link TestSkippedResult}</li>
+     *     <li>{@link TestFailureResult}</li>
+     * </ul>
+     *
+     * @return the result of the finished test operation
+     */
+    @Override
+    TestOperationResult getResult();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationDescriptor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationDescriptor.java
new file mode 100644
index 0000000..90a0751
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationDescriptor.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.OperationDescriptor;
+
+/**
+ * Describes a test operation for which an event has occurred.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestOperationDescriptor extends OperationDescriptor {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationResult.java
new file mode 100644
index 0000000..9121a94
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestOperationResult.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.OperationResult;
+
+/**
+ * Describes the result of running a test operation.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestOperationResult extends OperationResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressEvent.java
new file mode 100644
index 0000000..1518e17
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.ProgressEvent;
+
+/**
+ * Root interface for all events that signal progress while executing a test or test suite.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestProgressEvent extends ProgressEvent {
+    /**
+     * Returns the description of the test for which progress is reported. For JVM-based tests,
+     * the descriptor is of sub-type {@code JvmTestOperationDescriptor}.
+     *
+     * @return The description of the underlying test operation.
+     */
+    @Override
+    TestOperationDescriptor getDescriptor();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressListener.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressListener.java
new file mode 100644
index 0000000..9d4d97a
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestProgressListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+
+/**
+ * A listener which is notified when the tests that are executed as part of running a build make progress.
+ *
+ * @see org.gradle.tooling.LongRunningOperation#addTestProgressListener(TestProgressListener)
+ * @since 2.4
+ */
+ at Incubating
+public interface TestProgressListener {
+
+    /**
+     * Called when the test execution progresses.
+     *
+     * The following events are currently issued:
+     * <ul>
+     *    <li>{@link TestStartEvent}</li>
+     *    <li>{@link TestFinishEvent}</li>
+     * </ul>
+     *
+     * You can find out more about the test operation for which progress is reported
+     * by querying the test descriptor using {@link TestProgressEvent#getDescriptor()}.
+     *
+     * @param event An event describing the test operation progress.
+     * @see TestProgressEvent#getDescriptor()
+     */
+    void statusChanged(TestProgressEvent event);
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSkippedResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSkippedResult.java
new file mode 100644
index 0000000..730db37
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSkippedResult.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.SkippedResult;
+
+/**
+ * Describes how a test operation was skipped.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestSkippedResult extends TestOperationResult, SkippedResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestStartEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestStartEvent.java
new file mode 100644
index 0000000..787e394
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestStartEvent.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.StartEvent;
+
+/**
+ * An event that informs about a test having started its execution.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestStartEvent extends TestProgressEvent, StartEvent {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSuccessResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSuccessResult.java
new file mode 100644
index 0000000..c0f1ca7
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/TestSuccessResult.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test;
+
+import org.gradle.api.Incubating;
+import org.gradle.tooling.events.SuccessResult;
+
+/**
+ * Describes how a test operation finished successfully.
+ *
+ * @since 2.4
+ */
+ at Incubating
+public interface TestSuccessResult extends TestOperationResult, SuccessResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFailureResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFailureResult.java
new file mode 100644
index 0000000..6c8b5f6
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFailureResult.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test.internal;
+
+import org.gradle.tooling.Failure;
+import org.gradle.tooling.events.test.TestFailureResult;
+
+import java.util.List;
+
+/**
+ * Implementation of the {@code TestFailureResult} interface.
+ */
+public final class DefaultTestFailureResult implements TestFailureResult {
+
+    private final long startTime;
+    private final long endTime;
+    private final List<? extends Failure> failures;
+
+    public DefaultTestFailureResult(long startTime, long endTime, List<? extends Failure> failures) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.failures = failures;
+    }
+
+    @Override
+    public long getStartTime() {
+        return startTime;
+    }
+
+    @Override
+    public long getEndTime() {
+        return endTime;
+    }
+
+    @Override
+    public List<? extends Failure> getFailures() {
+        return failures;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFinishEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFinishEvent.java
new file mode 100644
index 0000000..c8a6e0a
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestFinishEvent.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test.internal;
+
+import org.gradle.tooling.events.internal.BaseFinishEvent;
+import org.gradle.tooling.events.test.TestFinishEvent;
+import org.gradle.tooling.events.test.TestOperationDescriptor;
+import org.gradle.tooling.events.test.TestOperationResult;
+
+/**
+ * Implementation of the {@code TestFinishEvent} interface.
+ */
+public final class DefaultTestFinishEvent extends BaseFinishEvent implements TestFinishEvent {
+
+    public DefaultTestFinishEvent(long eventTime, String displayName, TestOperationDescriptor descriptor, TestOperationResult result) {
+        super(eventTime, displayName, descriptor, result);
+    }
+
+    @Override
+    public TestOperationDescriptor getDescriptor() {
+        return (TestOperationDescriptor) super.getDescriptor();
+    }
+
+    @Override
+    public TestOperationResult getResult() {
+        return (TestOperationResult) super.getResult();
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSkippedResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSkippedResult.java
new file mode 100644
index 0000000..f971c05
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSkippedResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test.internal;
+
+import org.gradle.tooling.events.test.TestSkippedResult;
+
+/**
+ * Implementation of the {@code TestSkippedResult} interface.
+ */
+public final class DefaultTestSkippedResult implements TestSkippedResult {
+
+    private final long startTime;
+    private final long endTime;
+
+    public DefaultTestSkippedResult(long startTime, long endTime) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+    }
+
+    @Override
+    public long getStartTime() {
+        return startTime;
+    }
+
+    @Override
+    public long getEndTime() {
+        return endTime;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestStartEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestStartEvent.java
new file mode 100644
index 0000000..bfee1ad
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestStartEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test.internal;
+
+import org.gradle.tooling.events.internal.BaseStartEvent;
+import org.gradle.tooling.events.test.TestOperationDescriptor;
+import org.gradle.tooling.events.test.TestStartEvent;
+
+/**
+ * Implementation of the {@code TestStartEvent} interface.
+ */
+public final class DefaultTestStartEvent extends BaseStartEvent implements TestStartEvent {
+
+    public DefaultTestStartEvent(long eventTime, String displayName, TestOperationDescriptor descriptor) {
+        super(eventTime, displayName, descriptor);
+    }
+
+    @Override
+    public TestOperationDescriptor getDescriptor() {
+        return (TestOperationDescriptor) super.getDescriptor();
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSuccessResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSuccessResult.java
new file mode 100644
index 0000000..96ad6eb
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/internal/DefaultTestSuccessResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.events.test.internal;
+
+import org.gradle.tooling.events.test.TestSuccessResult;
+
+/**
+ * Implementation of the {@code TestSuccessResult} interface.
+ */
+public final class DefaultTestSuccessResult implements TestSuccessResult {
+
+    private final long startTime;
+    private final long endTime;
+
+    public DefaultTestSuccessResult(long startTime, long endTime) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+    }
+
+    @Override
+    public long getStartTime() {
+        return startTime;
+    }
+
+    @Override
+    public long getEndTime() {
+        return endTime;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/package-info.java
new file mode 100644
index 0000000..2bba8aa
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/events/test/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test execution specific interfaces and classes related to event notifications.
+ */
+package org.gradle.tooling.events.test;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/UnsupportedOperationConfigurationException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/UnsupportedOperationConfigurationException.java
index 1989538..a9f3c73 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/UnsupportedOperationConfigurationException.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/UnsupportedOperationConfigurationException.java
@@ -23,7 +23,7 @@ import org.gradle.tooling.UnsupportedVersionException;
  * with unsupported settings. For example {@link org.gradle.tooling.LongRunningOperation#setJavaHome(java.io.File)}
  * might not be supported by the target Gradle version.
  *
- * @since 1.0-rc-1
+ * @since 1.0
  */
 public class UnsupportedOperationConfigurationException extends UnsupportedVersionException {
     public UnsupportedOperationConfigurationException(String message) {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/adapter/ProtocolToModelAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/adapter/ProtocolToModelAdapter.java
index 986554c..fd18509 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/adapter/ProtocolToModelAdapter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/adapter/ProtocolToModelAdapter.java
@@ -412,12 +412,11 @@ public class ProtocolToModelAdapter implements Serializable {
 
         public void invoke(MethodInvocation invocation) throws Throwable {
             next.invoke(invocation);
-            if (invocation.found()) {
+            if (invocation.found() || invocation.getParameterTypes().length != 1) {
                 return;
             }
 
-            boolean getter = GETTER_METHOD.matcher(invocation.getName()).matches();
-            if (!getter || invocation.getParameterTypes().length != 1) {
+            if (!GETTER_METHOD.matcher(invocation.getName()).matches() && !IS_METHOD.matcher(invocation.getName()).matches()) {
                 return;
             }
 
@@ -471,7 +470,7 @@ public class ProtocolToModelAdapter implements Serializable {
             }
 
             if (instance == null) {
-                instance = new DirectInstantiator().newInstance(mixInClass, proxy);
+                instance = DirectInstantiator.INSTANCE.newInstance(mixInClass, proxy);
             }
             MethodInvocation beanInvocation = new MethodInvocation(invocation.getName(), invocation.getReturnType(), invocation.getGenericReturnType(), invocation.getParameterTypes(), instance, invocation.getParameters());
             current.set(beanInvocation);
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/DefaultBuildEnvironment.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/DefaultBuildEnvironment.java
index d723a3e..1a04a8a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/DefaultBuildEnvironment.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/DefaultBuildEnvironment.java
@@ -17,22 +17,40 @@
 package org.gradle.tooling.internal.build;
 
 import org.gradle.tooling.internal.protocol.InternalBuildEnvironment;
+import org.gradle.tooling.model.build.GradleEnvironment;
 import org.gradle.tooling.model.build.JavaEnvironment;
 
 import java.io.File;
 import java.io.Serializable;
 import java.util.List;
 
-public class DefaultBuildEnvironment extends VersionOnlyBuildEnvironment implements InternalBuildEnvironment, Serializable {
+public class DefaultBuildEnvironment implements InternalBuildEnvironment, Serializable {
+
+    private final File gradleUserHome;
+    private final String gradleVersion;
     private final File javaHome;
     private final List<String> jvmArguments;
 
-    public DefaultBuildEnvironment(String gradleVersion, File javaHome, List<String> jvmArguments) {
-        super(gradleVersion);
+    public DefaultBuildEnvironment(File gradleUserHome, String gradleVersion, File javaHome, List<String> jvmArguments) {
+        this.gradleUserHome = gradleUserHome;
+        this.gradleVersion = gradleVersion;
         this.javaHome = javaHome;
         this.jvmArguments = jvmArguments;
     }
 
+    public GradleEnvironment getGradle() {
+        return new GradleEnvironment() {
+            @Override
+            public File getGradleUserHome() {
+                return gradleUserHome;
+            }
+
+            public String getGradleVersion() {
+                return gradleVersion;
+            }
+        };
+    }
+
     public JavaEnvironment getJava() {
         return new JavaEnvironment() {
             public File getJavaHome() {
@@ -44,4 +62,5 @@ public class DefaultBuildEnvironment extends VersionOnlyBuildEnvironment impleme
             }
         };
     }
+
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/VersionOnlyBuildEnvironment.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/VersionOnlyBuildEnvironment.java
index f1c9488..902d4de 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/VersionOnlyBuildEnvironment.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/build/VersionOnlyBuildEnvironment.java
@@ -16,20 +16,22 @@
 
 package org.gradle.tooling.internal.build;
 
-import org.gradle.tooling.model.build.GradleEnvironment;
-
 public class VersionOnlyBuildEnvironment {
+
     private final String gradleVersion;
 
     public VersionOnlyBuildEnvironment(String gradleVersion) {
         this.gradleVersion = gradleVersion;
     }
 
-    public GradleEnvironment getGradle() {
-        return new GradleEnvironment() {
-            public String getGradleVersion() {
-                return gradleVersion;
-            }
-        };
+    public VersionOnlyGradleEnvironment getGradle() {
+        return new VersionOnlyGradleEnvironment();
     }
+
+    private class VersionOnlyGradleEnvironment {
+        public String getGradleVersion() {
+            return gradleVersion;
+        }
+    }
+
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java
index 58c1279..66cf4bb 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/AbstractLongRunningOperation.java
@@ -13,24 +13,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.gradle.tooling.internal.consumer;
 
+import com.google.common.base.Preconditions;
+import org.gradle.tooling.CancellationToken;
 import org.gradle.tooling.LongRunningOperation;
 import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.events.test.TestProgressListener;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-public abstract class AbstractLongRunningOperation<T extends LongRunningOperation> implements LongRunningOperation {
+public abstract class AbstractLongRunningOperation<T extends AbstractLongRunningOperation<T>> implements LongRunningOperation {
     protected final ConnectionParameters connectionParameters;
     protected final ConsumerOperationParameters.Builder operationParamsBuilder;
 
     protected AbstractLongRunningOperation(ConnectionParameters parameters) {
         connectionParameters = parameters;
         operationParamsBuilder = ConsumerOperationParameters.builder();
+        operationParamsBuilder.setCancellationToken(new DefaultCancellationTokenSource().token());
     }
 
     protected abstract T getThis();
@@ -60,6 +63,11 @@ public abstract class AbstractLongRunningOperation<T extends LongRunningOperatio
         return getThis();
     }
 
+    public T setColorOutput(boolean colorOutput) {
+        operationParamsBuilder.setColorOutput(colorOutput);
+        return getThis();
+    }
+
     public T setJavaHome(File javaHome) {
         operationParamsBuilder.setJavaHome(javaHome);
         return getThis();
@@ -74,4 +82,14 @@ public abstract class AbstractLongRunningOperation<T extends LongRunningOperatio
         operationParamsBuilder.addProgressListener(listener);
         return getThis();
     }
+
+    public T addTestProgressListener(TestProgressListener listener) {
+        operationParamsBuilder.addTestProgressListener(listener);
+        return getThis();
+    }
+
+    public T withCancellationToken(CancellationToken cancellationToken) {
+        operationParamsBuilder.setCancellationToken(Preconditions.checkNotNull(cancellationToken));
+        return getThis();
+    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CancellationTokenInternal.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CancellationTokenInternal.java
new file mode 100644
index 0000000..8cd61a3
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/CancellationTokenInternal.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.initialization.BuildCancellationToken;
+
+public interface CancellationTokenInternal {
+    BuildCancellationToken getToken();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
index 753e863..dfa7b55 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionFactory.java
@@ -15,30 +15,30 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.tooling.ProjectConnection;
 import org.gradle.tooling.internal.consumer.async.AsyncConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.connection.ConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor;
-import org.gradle.tooling.internal.consumer.connection.LoggingInitializerConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.loader.ToolingImplementationLoader;
 
 public class ConnectionFactory {
     private final ToolingImplementationLoader toolingImplementationLoader;
-    private final DefaultExecutorFactory executorFactory = new DefaultExecutorFactory();
+    private final ExecutorFactory executorFactory;
+    private final LoggingProvider loggingProvider;
 
-    public ConnectionFactory(ToolingImplementationLoader toolingImplementationLoader) {
+    public ConnectionFactory(ToolingImplementationLoader toolingImplementationLoader, ExecutorFactory executorFactory, LoggingProvider loggingProvider) {
         this.toolingImplementationLoader = toolingImplementationLoader;
+        this.executorFactory = executorFactory;
+        this.loggingProvider = loggingProvider;
     }
 
     public ProjectConnection create(Distribution distribution, ConnectionParameters parameters) {
-        SynchronizedLogging synchronizedLogging = new SynchronizedLogging();
-        ConsumerActionExecutor lazyConnection = new LazyConsumerActionExecutor(distribution, toolingImplementationLoader, synchronizedLogging, parameters);
-        ConsumerActionExecutor progressLoggingConnection = new ProgressLoggingConsumerActionExecutor(lazyConnection, synchronizedLogging);
-        ConsumerActionExecutor initializingConnection = new LoggingInitializerConsumerActionExecutor(progressLoggingConnection, synchronizedLogging);
-        AsyncConsumerActionExecutor asyncConnection = new DefaultAsyncConsumerActionExecutor(initializingConnection, executorFactory);
+        ConsumerActionExecutor lazyConnection = new LazyConsumerActionExecutor(distribution, toolingImplementationLoader, loggingProvider, parameters);
+        ConsumerActionExecutor progressLoggingConnection = new ProgressLoggingConsumerActionExecutor(lazyConnection, loggingProvider);
+        AsyncConsumerActionExecutor asyncConnection = new DefaultAsyncConsumerActionExecutor(progressLoggingConnection, executorFactory);
         return new DefaultProjectConnection(asyncConnection, parameters);
     }
 
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java
index 6dd4406..2509de1 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectionParameters.java
@@ -31,6 +31,11 @@ public interface ConnectionParameters extends org.gradle.tooling.internal.protoc
      */
     File getGradleUserHomeDir();
 
+    /**
+     * Returns the daemon base directory, or null to use default.
+     */
+    File getDaemonBaseDir();
+
     Boolean isEmbedded();
 
     Integer getDaemonMaxIdleTimeValue();
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServices.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServices.java
index dc97a6d..9da33b1 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServices.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ConnectorServices.java
@@ -16,32 +16,82 @@
 
 package org.gradle.tooling.internal.consumer;
 
+import org.gradle.api.JavaVersion;
+import org.gradle.internal.Factory;
+import org.gradle.internal.concurrent.DefaultExecutorFactory;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.jvm.UnsupportedJavaRuntimeException;
 import org.gradle.internal.service.DefaultServiceRegistry;
-import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.tooling.CancellationTokenSource;
 import org.gradle.tooling.internal.consumer.loader.CachingToolingImplementationLoader;
 import org.gradle.tooling.internal.consumer.loader.DefaultToolingImplementationLoader;
 import org.gradle.tooling.internal.consumer.loader.SynchronizedToolingImplementationLoader;
 import org.gradle.tooling.internal.consumer.loader.ToolingImplementationLoader;
 
 public class ConnectorServices {
+    private static DefaultServiceRegistry singletonRegistry = new ConnectorServiceRegistry();
 
-    private static ServiceRegistry singletonRegistry = new ConnectorServiceRegistry();
+    public static DefaultGradleConnector createConnector() {
+        assertJava6();
+        return singletonRegistry.getFactory(DefaultGradleConnector.class).create();
+    }
+
+    public static CancellationTokenSource createCancellationTokenSource() {
+        assertJava6();
+        return new DefaultCancellationTokenSource();
+    }
 
-    public DefaultGradleConnector createConnector() {
-        ConnectionFactory connectionFactory = new ConnectionFactory(singletonRegistry.get(ToolingImplementationLoader.class));
-        return new DefaultGradleConnector(connectionFactory, new DistributionFactory());
+    public static void close() {
+        assertJava6();
+        singletonRegistry.close();
     }
 
     /**
      * Resets the state of connector services. Meant to be used only for testing!
      */
-    public void reset() {
+    public static void reset() {
+        singletonRegistry.close();
         singletonRegistry = new ConnectorServiceRegistry();
     }
 
+    private static void assertJava6() {
+        JavaVersion javaVersion = JavaVersion.current();
+        if (!javaVersion.isJava6Compatible()) {
+            throw UnsupportedJavaRuntimeException.usingUnsupportedVersion("Gradle Tooling API", JavaVersion.VERSION_1_6);
+        }
+    }
+
     private static class ConnectorServiceRegistry extends DefaultServiceRegistry {
+        protected Factory<DefaultGradleConnector> createConnectorFactory(final ConnectionFactory connectionFactory, final DistributionFactory distributionFactory) {
+            return new Factory<DefaultGradleConnector>() {
+                public DefaultGradleConnector create() {
+                    return new DefaultGradleConnector(connectionFactory, distributionFactory);
+                }
+            };
+        }
+
+        protected ExecutorFactory createExecutorFactory() {
+            return new DefaultExecutorFactory();
+        }
+
+        protected ExecutorServiceFactory createExecutorServiceFactory() {
+            return new DefaultExecutorServiceFactory();
+        }
+
+        protected DistributionFactory createDistributionFactory(ExecutorServiceFactory executorFactory) {
+            return new DistributionFactory(executorFactory);
+        }
+
         protected ToolingImplementationLoader createToolingImplementationLoader() {
             return new SynchronizedToolingImplementationLoader(new CachingToolingImplementationLoader(new DefaultToolingImplementationLoader()));
         }
+
+        protected LoggingProvider createLoggingProvider() {
+            return new SynchronizedLogging();
+        }
+
+        protected ConnectionFactory createConnectionFactory(ToolingImplementationLoader toolingImplementationLoader, ExecutorFactory executorFactory, LoggingProvider loggingProvider) {
+            return new ConnectionFactory(toolingImplementationLoader, executorFactory, loggingProvider);
+        }
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuter.java
index 0cdea44..a63dcb6 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuter.java
@@ -16,7 +16,10 @@
 
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.tooling.*;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildActionExecuter;
+import org.gradle.tooling.GradleConnectionException;
+import org.gradle.tooling.ResultHandler;
 import org.gradle.tooling.internal.consumer.async.AsyncConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.connection.ConsumerAction;
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java
index aa365da..ec84d9e 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultBuildLauncher.java
@@ -15,28 +15,17 @@
  */
 package org.gradle.tooling.internal.consumer;
 
-import com.google.common.collect.Lists;
-import org.gradle.api.GradleException;
 import org.gradle.tooling.BuildLauncher;
 import org.gradle.tooling.ResultHandler;
-import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.consumer.async.AsyncConsumerActionExecutor;
 import org.gradle.tooling.internal.consumer.connection.ConsumerAction;
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
-import org.gradle.tooling.internal.gradle.TaskListingLaunchable;
-import org.gradle.tooling.internal.protocol.InternalLaunchable;
 import org.gradle.tooling.model.Launchable;
 import org.gradle.tooling.model.Task;
-import org.gradle.tooling.model.TaskSelector;
 
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
 
 class DefaultBuildLauncher extends AbstractLongRunningOperation<DefaultBuildLauncher> implements BuildLauncher {
     private final AsyncConsumerActionExecutor connection;
@@ -63,11 +52,7 @@ class DefaultBuildLauncher extends AbstractLongRunningOperation<DefaultBuildLaun
     }
 
     public BuildLauncher forTasks(Iterable<? extends Task> tasks) {
-        List<String> taskPaths = new ArrayList<String>();
-        for (Task task : tasks) {
-            taskPaths.add(task.getPath());
-        }
-        operationParamsBuilder.setTasks(taskPaths);
+        forLaunchables(tasks);
         return this;
     }
 
@@ -76,38 +61,10 @@ class DefaultBuildLauncher extends AbstractLongRunningOperation<DefaultBuildLaun
     }
 
     public BuildLauncher forLaunchables(Iterable<? extends Launchable> launchables) {
-        Set<String> taskPaths = new LinkedHashSet<String>();
-        List<InternalLaunchable> launchablesParams = Lists.newArrayList();
-        for (Launchable launchable : launchables) {
-            if (launchable instanceof Task) {
-                taskPaths.add(((Task) launchable).getPath());
-            } else if (launchable instanceof TaskListingLaunchable) {
-                taskPaths.addAll(((TaskListingLaunchable) launchable).getTaskNames());
-            } else if (!(launchable instanceof TaskSelector)) {
-                throw new GradleException("Only Task or TaskSelector instances are supported: "
-                        + (launchable != null ? launchable.getClass() : "null"));
-            }
-            maybeAddLaunchableParameter(launchablesParams, launchable);
-        }
-        operationParamsBuilder.setTasks(new ArrayList<String>(taskPaths));
-        operationParamsBuilder.setLaunchables(launchablesParams);
+        operationParamsBuilder.setLaunchables(launchables);
         return this;
     }
 
-    private void maybeAddLaunchableParameter(List<InternalLaunchable> launchablesParams, Launchable launchable) {
-        Object original = launchable;
-        try {
-            if (Proxy.isProxyClass(launchable.getClass())) {
-                original = new ProtocolToModelAdapter().unpack(launchable);
-            }
-        } catch (IllegalArgumentException iae) {
-            // ignore: launchable created on consumer side for older provider
-        }
-        if (original instanceof InternalLaunchable) {
-            launchablesParams.add((InternalLaunchable) original);
-        }
-    }
-
     public void run() {
         BlockingResultHandler<Void> handler = new BlockingResultHandler<Void>(Void.class);
         run(handler);
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSource.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSource.java
new file mode 100644
index 0000000..05b8f08
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSource.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.initialization.DefaultBuildCancellationToken;
+import org.gradle.tooling.CancellationToken;
+import org.gradle.tooling.CancellationTokenSource;
+
+public final class DefaultCancellationTokenSource implements CancellationTokenSource {
+    private final CancellationTokenImpl tokenImpl;
+
+    public DefaultCancellationTokenSource() {
+        tokenImpl = new CancellationTokenImpl(new DefaultBuildCancellationToken());
+    }
+
+    public void cancel() {
+        tokenImpl.token.doCancel();
+    }
+
+    public CancellationToken token() {
+        return tokenImpl;
+    }
+
+    private static class CancellationTokenImpl implements CancellationToken, CancellationTokenInternal {
+        private final DefaultBuildCancellationToken token;
+
+        private CancellationTokenImpl(DefaultBuildCancellationToken token) {
+            this.token = token;
+        }
+
+        public BuildCancellationToken getToken() {
+            return token;
+        }
+
+        public boolean isCancellationRequested() {
+            return token.isCancellationRequested();
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java
index 57984db..729dd33 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultConnectionParameters.java
@@ -27,6 +27,7 @@ public class DefaultConnectionParameters implements ConnectionParameters {
     private final Boolean embedded;
     private final Integer daemonMaxIdleTimeValue;
     private final TimeUnit daemonMaxIdleTimeUnits;
+    private final File daemonBaseDir;
     private final boolean verboseLogging;
 
     public static Builder builder() {
@@ -51,6 +52,7 @@ public class DefaultConnectionParameters implements ConnectionParameters {
         private Integer daemonMaxIdleTimeValue;
         private TimeUnit daemonMaxIdleTimeUnits;
         private boolean verboseLogging;
+        private File daemonBaseDir;
 
         private Builder() {
         }
@@ -92,21 +94,31 @@ public class DefaultConnectionParameters implements ConnectionParameters {
 
         public DefaultConnectionParameters build() {
             return new DefaultConnectionParameters(gradleUserHomeDir, projectDir, searchUpwards, embedded,
-                    daemonMaxIdleTimeValue, daemonMaxIdleTimeUnits, verboseLogging);
+                    daemonMaxIdleTimeValue, daemonMaxIdleTimeUnits, daemonBaseDir, verboseLogging);
+        }
+
+        public void setDaemonBaseDir(File daemonBaseDir) {
+            this.daemonBaseDir = daemonBaseDir;
         }
     }
 
     private DefaultConnectionParameters(File gradleUserHomeDir, File projectDir, Boolean searchUpwards, Boolean embedded,
-                                        Integer daemonMaxIdleTimeValue, TimeUnit daemonMaxIdleTimeUnits, boolean verboseLogging) {
+                                        Integer daemonMaxIdleTimeValue, TimeUnit daemonMaxIdleTimeUnits, File daemonBaseDir,
+                                        boolean verboseLogging) {
         this.gradleUserHomeDir = gradleUserHomeDir;
         this.projectDir = projectDir;
         this.searchUpwards = searchUpwards;
         this.embedded = embedded;
         this.daemonMaxIdleTimeValue = daemonMaxIdleTimeValue;
         this.daemonMaxIdleTimeUnits = daemonMaxIdleTimeUnits;
+        this.daemonBaseDir = daemonBaseDir;
         this.verboseLogging = verboseLogging;
     }
 
+    public File getDaemonBaseDir() {
+        return daemonBaseDir;
+    }
+
     public File getGradleUserHomeDir() {
         return gradleUserHomeDir;
     }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultExecutorServiceFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultExecutorServiceFactory.java
new file mode 100644
index 0000000..b7dcc72
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultExecutorServiceFactory.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class DefaultExecutorServiceFactory implements ExecutorServiceFactory {
+    public ExecutorService create() {
+        return Executors.newSingleThreadExecutor();
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultFailure.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultFailure.java
new file mode 100644
index 0000000..70dea0f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultFailure.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.tooling.Failure;
+
+import java.util.List;
+
+public final class DefaultFailure implements Failure {
+
+    private final String message;
+    private final String description;
+    private final List<? extends Failure> causes;
+
+    public DefaultFailure(String message, String description, List<? extends Failure> causes) {
+        this.message = message;
+        this.description = description;
+        this.causes = causes;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public List<? extends Failure> getCauses() {
+        return causes;
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
index 110bd16..71d7441 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
@@ -39,6 +39,24 @@ public class DefaultGradleConnector extends GradleConnector {
         this.distributionFactory = distributionFactory;
     }
 
+    /**
+     * Closes the tooling API, releasing all resources. Blocks until completed.
+     *
+     * <p>May attempt to expire some or all daemons started by this tooling API client. The exact behaviour here is implementation-specific and not guaranteed.
+     * The expiration is best effort only. This method may return before the daemons have stopped.</p>
+     *
+     * <p>Note: this is not yet part of the public tooling API yet.</p>
+     *
+     * TODO - need to model this as a long running operation, and allow stdout, stderr and progress listener to be supplied.
+     * TODO - need to define exceptions.
+     * TODO - no further operations are allowed after this has been called
+     * TODO - cancel current operations or block until complete
+     * TODO - introduce a 'tooling API client' interface and move this method there
+     */
+    public static void close() {
+        ConnectorServices.close();
+    }
+
     public GradleConnector useInstallation(File gradleHome) {
         distribution = distributionFactory.getDistribution(gradleHome);
         return this;
@@ -59,7 +77,7 @@ public class DefaultGradleConnector extends GradleConnector {
         return this;
     }
 
-    public GradleConnector useDefaultDistribution() {
+    public GradleConnector useBuildDistribution() {
         distribution = null;
         return this;
     }
@@ -90,17 +108,21 @@ public class DefaultGradleConnector extends GradleConnector {
         return this;
     }
 
+    public GradleConnector daemonBaseDir(File daemonBaseDir) {
+        connectionParamsBuilder.setDaemonBaseDir(daemonBaseDir);
+        return this;
+    }
+
     /**
      * If true then debug log statements will be shown
-     *
-     * @param verboseLogging
-     * @return
      */
     public DefaultGradleConnector setVerboseLogging(boolean verboseLogging) {
         connectionParamsBuilder.setVerboseLogging(verboseLogging);
         return this;
     }
 
+
+
     public ProjectConnection connect() throws GradleConnectionException {
         LOGGER.debug("Connecting from tooling API consumer version {}", GradleVersion.current().getVersion());
 
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java
index 1fb64b1..4283bf2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultModelBuilder.java
@@ -26,6 +26,7 @@ import org.gradle.tooling.model.UnsupportedMethodException;
 import org.gradle.tooling.model.internal.Exceptions;
 
 import java.util.Arrays;
+import java.util.List;
 
 public class DefaultModelBuilder<T> extends AbstractLongRunningOperation<DefaultModelBuilder<T>> implements ModelBuilder<T> {
     private final Class<T> modelType;
@@ -62,7 +63,11 @@ public class DefaultModelBuilder<T> extends AbstractLongRunningOperation<Default
     }
 
     public DefaultModelBuilder<T> forTasks(String... tasks) {
-        operationParamsBuilder.setTasks(Arrays.asList(tasks));
+        // only set a non-null task list on the operationParamsBuilder if at least one task has been given to this method,
+        // this is needed since any non-null list, even if empty, is treated as 'execute these tasks before building the model'
+        // this would cause an error when fetching the BuildEnvironment model
+        List<String> rationalizedTasks = tasks != null && tasks.length > 0 ? Arrays.asList(tasks) : null;
+        operationParamsBuilder.setTasks(rationalizedTasks);
         return this;
     }
 
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
index b4eb157..b134fea 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/Distribution.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.tooling.internal.consumer;
 
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.logging.ProgressLoggerFactory;
 
@@ -23,5 +24,6 @@ import java.io.File;
 public interface Distribution {
     String getDisplayName();
 
-    ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir);
+    ClassPath getToolingImplementationClasspath(
+            ProgressLoggerFactory progressLoggerFactory, File userHomeDir, BuildCancellationToken cancellationToken);
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
index cdce02b..8d118f1 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DistributionFactory.java
@@ -15,13 +15,18 @@
  */
 package org.gradle.tooling.internal.consumer;
 
+import com.google.common.base.Preconditions;
 import org.gradle.api.internal.classpath.EffectiveClassPath;
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.initialization.layout.BuildLayout;
 import org.gradle.initialization.layout.BuildLayoutFactory;
+import org.gradle.internal.Factory;
+import org.gradle.internal.UncheckedException;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.tooling.BuildCancelledException;
 import org.gradle.tooling.GradleConnectionException;
 import org.gradle.util.DistributionLocator;
 import org.gradle.util.GradleVersion;
@@ -31,9 +36,19 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.util.LinkedHashSet;
-import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 
 public class DistributionFactory {
+    private final Factory<? extends ExecutorService> executorFactory;
+
+    public DistributionFactory(Factory<? extends ExecutorService> executorFactory) {
+        this.executorFactory = Preconditions.checkNotNull(executorFactory);
+    }
+
     /**
      * Returns the default distribution to use for the specified project.
      */
@@ -41,7 +56,7 @@ public class DistributionFactory {
         BuildLayout layout = new BuildLayoutFactory().getLayoutFor(projectDir, searchUpwards);
         WrapperExecutor wrapper = WrapperExecutor.forProjectDirectory(layout.getRootDirectory(), System.out);
         if (wrapper.getDistribution() != null) {
-            return new ZippedDistribution(wrapper.getConfiguration());
+            return new ZippedDistribution(wrapper.getConfiguration(), executorFactory);
         }
         return getDownloadedDistribution(GradleVersion.current().getVersion());
     }
@@ -67,7 +82,7 @@ public class DistributionFactory {
     public Distribution getDistribution(URI gradleDistribution) {
         WrapperConfiguration configuration = new WrapperConfiguration();
         configuration.setDistribution(gradleDistribution);
-        return new ZippedDistribution(configuration);
+        return new ZippedDistribution(configuration, executorFactory);
     }
 
     /**
@@ -85,30 +100,65 @@ public class DistributionFactory {
     private static class ZippedDistribution implements Distribution {
         private InstalledDistribution installedDistribution;
         private final WrapperConfiguration wrapperConfiguration;
+        private final Factory<? extends ExecutorService> executorFactory;
 
-        private ZippedDistribution(WrapperConfiguration wrapperConfiguration) {
+        private ZippedDistribution(WrapperConfiguration wrapperConfiguration, Factory<? extends ExecutorService> executorFactory) {
             this.wrapperConfiguration = wrapperConfiguration;
+            this.executorFactory = executorFactory;
         }
 
         public String getDisplayName() {
             return String.format("Gradle distribution '%s'", wrapperConfiguration.getDistribution());
         }
 
-        public ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir) {
+        public ClassPath getToolingImplementationClasspath(final ProgressLoggerFactory progressLoggerFactory, final File userHomeDir, BuildCancellationToken cancellationToken) {
             if (installedDistribution == null) {
+                Callable<File> installDistroTask = new Callable<File>() {
+                    public File call() throws Exception {
+                        File installDir;
+                        try {
+                            File realUserHomeDir = userHomeDir != null ? userHomeDir : GradleUserHomeLookup.gradleUserHome();
+                            Install install = new Install(new Logger(false), new ProgressReportingDownload(progressLoggerFactory), new PathAssembler(realUserHomeDir));
+                            installDir = install.createDist(wrapperConfiguration);
+                        } catch (FileNotFoundException e) {
+                            throw new IllegalArgumentException(String.format("The specified %s does not exist.", getDisplayName()), e);
+                        } catch (CancellationException e) {
+                            throw new BuildCancelledException(String.format("Distribution download cancelled. Using distribution from '%s'.", wrapperConfiguration.getDistribution()), e);
+                        } catch (Exception e) {
+                            throw new GradleConnectionException(String.format("Could not install Gradle distribution from '%s'.", wrapperConfiguration.getDistribution()), e);
+                        }
+                        return installDir;
+                    }
+                };
                 File installDir;
+                ExecutorService executor = null;
                 try {
-                    File realUserHomeDir = userHomeDir != null ? userHomeDir : GradleUserHomeLookup.gradleUserHome();
-                    Install install = new Install(new ProgressReportingDownload(progressLoggerFactory), new PathAssembler(realUserHomeDir));
-                    installDir = install.createDist(wrapperConfiguration);
-                } catch (FileNotFoundException e) {
-                    throw new IllegalArgumentException(String.format("The specified %s does not exist.", getDisplayName()), e);
-                } catch (Exception e) {
+                    executor = executorFactory.create();
+                    final Future<File> installDirFuture = executor.submit(installDistroTask);
+                    cancellationToken.addCallback(new Runnable() {
+                        public void run() {
+                            // TODO(radim): better to close the connection too to allow quick finish of the task
+                            installDirFuture.cancel(true);
+                        }
+                    });
+                    installDir = installDirFuture.get();
+                } catch (CancellationException e) {
+                    throw new BuildCancelledException(String.format("Distribution download cancelled. Using distribution from '%s'.", wrapperConfiguration.getDistribution()), e);
+                } catch (InterruptedException e) {
+                    throw new GradleConnectionException(String.format("Could not install Gradle distribution from '%s'.", wrapperConfiguration.getDistribution()), e);
+                } catch (ExecutionException e) {
+                    if (e.getCause() != null) {
+                        UncheckedException.throwAsUncheckedException(e.getCause());
+                    }
                     throw new GradleConnectionException(String.format("Could not install Gradle distribution from '%s'.", wrapperConfiguration.getDistribution()), e);
+                } finally {
+                    if (executor != null) {
+                        executor.shutdown();
+                    }
                 }
                 installedDistribution = new InstalledDistribution(installDir, getDisplayName(), getDisplayName());
             }
-            return installedDistribution.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir);
+            return installedDistribution.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir, cancellationToken);
         }
     }
 
@@ -124,7 +174,7 @@ public class DistributionFactory {
             progressLogger.setDescription(String.format("Download %s", address));
             progressLogger.started();
             try {
-                new Download("Gradle Tooling API", GradleVersion.current().getVersion()).download(address, destination);
+                new Download(new Logger(false), "Gradle Tooling API", GradleVersion.current().getVersion()).download(address, destination);
             } finally {
                 progressLogger.completed();
             }
@@ -146,7 +196,7 @@ public class DistributionFactory {
             return displayName;
         }
 
-        public ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir) {
+        public ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir, BuildCancellationToken cancellationToken) {
             ProgressLogger progressLogger = progressLoggerFactory.newOperation(DistributionFactory.class);
             progressLogger.setDescription("Validate distribution");
             progressLogger.started();
@@ -168,7 +218,7 @@ public class DistributionFactory {
             if (!libDir.isDirectory()) {
                 throw new IllegalArgumentException(String.format("The specified %s does not appear to contain a Gradle distribution.", locationDisplayName));
             }
-            Set<File> files = new LinkedHashSet<File>();
+            LinkedHashSet<File> files = new LinkedHashSet<File>();
             for (File file : libDir.listFiles()) {
                 if (file.getName().endsWith(".jar")) {
                     files.add(file);
@@ -183,7 +233,7 @@ public class DistributionFactory {
             return "Gradle classpath distribution";
         }
 
-        public ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir) {
+        public ClassPath getToolingImplementationClasspath(ProgressLoggerFactory progressLoggerFactory, File userHomeDir, BuildCancellationToken cancellationToken) {
             return new EffectiveClassPath(getClass().getClassLoader());
         }
     }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ExecutorServiceFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ExecutorServiceFactory.java
new file mode 100644
index 0000000..997e3a2
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ExecutorServiceFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer;
+
+import org.gradle.internal.Factory;
+
+import java.util.concurrent.ExecutorService;
+
+public interface ExecutorServiceFactory extends Factory<ExecutorService> {
+    ExecutorService create();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LoggingProvider.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LoggingProvider.java
index 3e945f1..26bfe90 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LoggingProvider.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/LoggingProvider.java
@@ -16,7 +16,7 @@
 
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.logging.ProgressLoggerFactory;
 
 public interface LoggingProvider {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java
index 071df79..82beb53 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/ResultHandlerAdapter.java
@@ -15,12 +15,14 @@
  */
 package org.gradle.tooling.internal.consumer;
 
+import org.gradle.tooling.BuildCancelledException;
 import org.gradle.tooling.BuildException;
 import org.gradle.tooling.GradleConnectionException;
 import org.gradle.tooling.ResultHandler;
 import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
 import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException;
 import org.gradle.tooling.internal.protocol.BuildExceptionVersion1;
+import org.gradle.tooling.internal.protocol.InternalBuildCancelledException;
 import org.gradle.tooling.internal.protocol.ResultHandlerVersion1;
 import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException;
 
@@ -49,6 +51,8 @@ abstract class ResultHandlerAdapter<T> implements ResultHandlerVersion1<T> {
                     + "\n" + failure.getMessage(), failure.getCause()));
         } else if (failure instanceof GradleConnectionException) {
             handler.onFailure((GradleConnectionException) failure);
+        } else if (failure instanceof InternalBuildCancelledException) {
+            handler.onFailure(new BuildCancelledException(connectionFailureMessage(failure), failure.getCause()));
         } else if (failure instanceof BuildExceptionVersion1) {
             handler.onFailure(new BuildException(connectionFailureMessage(failure), failure.getCause()));
         } else {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/SynchronizedLogging.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/SynchronizedLogging.java
index 070ebfd..a9ea0b1 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/SynchronizedLogging.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/SynchronizedLogging.java
@@ -16,57 +16,45 @@
 
 package org.gradle.tooling.internal.consumer;
 
-import org.gradle.internal.Factory;
 import org.gradle.internal.TrueTimeProvider;
-import org.gradle.internal.concurrent.Synchronizer;
-import org.gradle.listener.DefaultListenerManager;
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.DefaultListenerManager;
+import org.gradle.internal.event.ListenerManager;
+import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.logging.internal.DefaultProgressLoggerFactory;
 import org.gradle.logging.internal.ProgressListener;
 
 /**
- * Thread safe logging provider that needs to be initialized before use.
+ * Provides logging services per thread.
  */
 public class SynchronizedLogging implements LoggingProvider {
-
-    private final ThreadLocal<ListenerManager> listenerManager = new ThreadLocal<ListenerManager>();
-    private final ThreadLocal<DefaultProgressLoggerFactory> progressLoggerFactory = new ThreadLocal<DefaultProgressLoggerFactory>();
-
-    //even though we use thread locals we need to synchronize a bit
-    //to avoid partial initialization / race conditions like listenerManager initialized but not yet progressLoggerFactory
-    private final Synchronizer synchronizer = new Synchronizer();
+    private final ThreadLocal<ThreadLoggingServices> services = new ThreadLocal<ThreadLoggingServices>();
 
     public ListenerManager getListenerManager() {
-        return synchronizer.synchronize(new Factory<ListenerManager>() {
-            public ListenerManager create() {
-                assertInitialized();
-                return listenerManager.get();
-            }
-        });
+        return services().listenerManager;
     }
 
-    public DefaultProgressLoggerFactory getProgressLoggerFactory() {
-        return synchronizer.synchronize(new Factory<DefaultProgressLoggerFactory>() {
-            public DefaultProgressLoggerFactory create() {
-                assertInitialized();
-                return progressLoggerFactory.get();
-            }
-        });
+    public ProgressLoggerFactory getProgressLoggerFactory() {
+        return services().progressLoggerFactory;
     }
 
-    public void init() {
-        synchronizer.synchronize(new Runnable() {
-            public void run() {
-                DefaultListenerManager manager = new DefaultListenerManager();
-                listenerManager.set(manager);
-                progressLoggerFactory.set(new DefaultProgressLoggerFactory(manager.getBroadcaster(ProgressListener.class), new TrueTimeProvider()));
-            }
-        });
+    private ThreadLoggingServices services() {
+        ThreadLoggingServices threadServices = services.get();
+        if (threadServices == null) {
+            DefaultListenerManager manager = new DefaultListenerManager();
+            DefaultProgressLoggerFactory progressLoggerFactory = new DefaultProgressLoggerFactory(manager.getBroadcaster(ProgressListener.class), new TrueTimeProvider());
+            threadServices = new ThreadLoggingServices(manager, progressLoggerFactory);
+            services.set(threadServices);
+        }
+        return threadServices;
     }
 
-    private void assertInitialized() {
-        if (listenerManager.get() == null) {
-            throw new IllegalStateException("Internal problem. Logging has not yet been initialized for this thread.");
+    private static class ThreadLoggingServices {
+        final ListenerManager listenerManager;
+        final ProgressLoggerFactory progressLoggerFactory;
+
+        private ThreadLoggingServices(ListenerManager listenerManager, ProgressLoggerFactory progressLoggerFactory) {
+            this.listenerManager = listenerManager;
+            this.progressLoggerFactory = progressLoggerFactory;
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractConsumerConnection.java
index 5bfbc57..5958f30 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractConsumerConnection.java
@@ -21,7 +21,6 @@ import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
 import org.gradle.tooling.internal.protocol.ConnectionVersion4;
-import org.gradle.tooling.model.internal.Exceptions;
 
 public abstract class AbstractConsumerConnection implements ConsumerConnection {
     private final ConnectionVersion4 delegate;
@@ -33,7 +32,6 @@ public abstract class AbstractConsumerConnection implements ConsumerConnection {
     }
 
     public void stop() {
-        delegate.stop();
     }
 
     public String getDisplayName() {
@@ -50,7 +48,15 @@ public abstract class AbstractConsumerConnection implements ConsumerConnection {
 
     public abstract void configure(ConnectionParameters connectionParameters);
 
-    public <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        throw Exceptions.unsupportedFeature("execution of build actions provided by the tooling API client", getVersionDetails().getVersion(), "1.8");
+    protected abstract ModelProducer getModelProducer();
+
+    protected abstract ActionRunner getActionRunner();
+
+    public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) {
+        return getModelProducer().produceModel(type, operationParameters);
+    }
+
+    public <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) {
+        return getActionRunner().run(action, operationParameters);
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractModelProducer.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractModelProducer.java
deleted file mode 100644
index 6b9785b..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractModelProducer.java
+++ /dev/null
@@ -1,33 +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.tooling.internal.consumer.connection;
-
-import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
-import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
-
-abstract class AbstractModelProducer implements ModelProducer{
-    protected final ProtocolToModelAdapter adapter;
-    protected final VersionDetails versionDetails;
-    protected final ModelMapping modelMapping;
-
-    public AbstractModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping) {
-        this.adapter = adapter;
-        this.versionDetails = versionDetails;
-        this.modelMapping = modelMapping;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractPre12ConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractPre12ConsumerConnection.java
deleted file mode 100644
index b8998e0..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/AbstractPre12ConsumerConnection.java
+++ /dev/null
@@ -1,57 +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.tooling.internal.consumer.connection;
-
-import org.gradle.tooling.internal.adapter.CompatibleIntrospector;
-import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
-import org.gradle.tooling.internal.consumer.ConnectionParameters;
-import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
-import org.gradle.tooling.internal.protocol.ConnectionVersion4;
-import org.gradle.tooling.model.internal.Exceptions;
-
-/**
- * An adapter to a pre 1.2 provider.
- */
-public abstract class AbstractPre12ConsumerConnection extends AbstractConsumerConnection {
-    public AbstractPre12ConsumerConnection(ConnectionVersion4 delegate, VersionDetails providerMetaData, ProtocolToModelAdapter adapter) {
-        super(delegate, providerMetaData);
-    }
-
-    @Override
-    public void configure(ConnectionParameters connectionParameters) {
-        new CompatibleIntrospector(getDelegate()).callSafely("configureLogging", connectionParameters.getVerboseLogging());
-    }
-
-    public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        if (type.equals(Void.class)) {
-            doRunBuild(operationParameters);
-            return null;
-        } else {
-            if (operationParameters.getTasks() != null) {
-                throw Exceptions.unsupportedOperationConfiguration("modelBuilder.forTasks()", getVersionDetails().getVersion());
-            }
-            return doGetModel(type, operationParameters);
-        }
-    }
-
-    protected abstract <T> T doGetModel(Class<T> modelType, ConsumerOperationParameters operationParameters);
-
-    protected void doRunBuild(ConsumerOperationParameters operationParameters) {
-        getDelegate().executeBuild(operationParameters, operationParameters);
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnection.java
index 1920bb5..969d8bc 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnection.java
@@ -21,41 +21,94 @@ import org.gradle.tooling.BuildActionFailureException;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
+import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
 import org.gradle.tooling.internal.protocol.*;
+import org.gradle.tooling.model.gradle.BuildInvocations;
+import org.gradle.util.GradleVersion;
 
-public class ActionAwareConsumerConnection extends ModelBuilderBackedConsumerConnection {
-    private final InternalBuildActionExecutor executor;
-    private final ProtocolToModelAdapter adapter;
+/**
+ * An adapter for {@link InternalBuildActionExecutor}.
+ *
+ * <p>Used for providers >= 1.8 and <= 2.0</p>
+ */
+public class ActionAwareConsumerConnection extends AbstractPost12ConsumerConnection {
+    private final ActionRunner actionRunner;
+    private final ModelProducer modelProducer;
 
     public ActionAwareConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
-        super(delegate, modelMapping, adapter);
-        this.adapter = adapter;
-        executor = (InternalBuildActionExecutor) delegate;
+        super(delegate, getVersionDetails(delegate.getMetaData().getVersion()));
+        ModelProducer modelProducer =  new ModelBuilderBackedModelProducer(adapter, getVersionDetails(), modelMapping, (ModelBuilder) delegate);
+        if (!getVersionDetails().maySupportModel(BuildInvocations.class)) {
+            modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelProducer);
+
+        }
+        this.modelProducer = modelProducer;
+        this.actionRunner = new InternalBuildActionExecutorBackedActionRunner((InternalBuildActionExecutor) delegate, adapter);
+    }
+
+    @Override
+    protected ModelProducer getModelProducer() {
+        return modelProducer;
     }
 
     @Override
-    public <T> T run(final BuildAction<T> action, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        BuildResult<T> result;
-        try {
-            result = executor.run(new BuildActionAdapter<T>(action, adapter), operationParameters);
-        } catch (InternalBuildActionFailureException e) {
-            throw new BuildActionFailureException("The supplied build action failed with an exception.", e.getCause());
-        }
-        return result.getModel();
+    protected ActionRunner getActionRunner() {
+        return actionRunner;
+    }
+
+    protected static VersionDetails getVersionDetails(String versionString) {
+        GradleVersion version = GradleVersion.version(versionString);
+        if (version.compareTo(GradleVersion.version("1.11")) > 0) {
+            return new R112VersionDetails(version.getVersion());
+        }
+        return new R18VersionDetails(version.getVersion());
     }
 
-    private static class BuildActionAdapter<T> implements InternalBuildAction<T> {
-        private final BuildAction<T> action;
+    static class R18VersionDetails extends VersionDetails {
+        private R18VersionDetails(String version) {
+            super(version);
+        }
+
+        @Override
+        public boolean maySupportModel(Class<?> modelType) {
+            return modelType != BuildInvocations.class;
+        }
+    }
+
+    static class R112VersionDetails extends VersionDetails {
+        private R112VersionDetails(String version) {
+            super(version);
+        }
+
+        @Override
+        public boolean maySupportModel(Class<?> modelType) {
+            return true;
+        }
+
+        @Override
+        public boolean supportsTaskDisplayName() {
+            return true;
+        }
+    }
+
+    private static class InternalBuildActionExecutorBackedActionRunner implements ActionRunner {
+        private final InternalBuildActionExecutor executor;
         private final ProtocolToModelAdapter adapter;
 
-        public BuildActionAdapter(BuildAction<T> action, ProtocolToModelAdapter adapter) {
-            this.action = action;
+        private InternalBuildActionExecutorBackedActionRunner(InternalBuildActionExecutor executor, ProtocolToModelAdapter adapter) {
+            this.executor = executor;
             this.adapter = adapter;
         }
 
-        public T execute(final InternalBuildController buildController) {
-            return action.execute(new BuildControllerAdapter(adapter, buildController, new ModelMapping()));
+        public <T> T run(final BuildAction<T> action, ConsumerOperationParameters operationParameters)
+                throws UnsupportedOperationException, IllegalStateException {
+            BuildResult<T> result;
+            try {
+                result = executor.run(new InternalBuildActionAdapter<T>(action, adapter), operationParameters);
+            } catch (InternalBuildActionFailureException e) {
+                throw new BuildActionFailureException("The supplied build action failed with an exception.", e.getCause());
+            }
+            return result.getModel();
         }
     }
-
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionRunner.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionRunner.java
new file mode 100644
index 0000000..94b618d
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ActionRunner.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
+
+public interface ActionRunner {
+    <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters);
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnection.java
index a114aa3..a35551f 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnection.java
@@ -36,19 +36,29 @@ import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes;
 
 /**
  * An adapter for a {@link BuildActionRunner} based provider.
+ *
+ * <p>Used for providers >= 1.2 and <= 1.6.</p>
  */
 public class BuildActionRunnerBackedConsumerConnection extends AbstractPost12ConsumerConnection {
     private final ModelProducer modelProducer;
+    private final UnsupportedActionRunner actionRunner;
 
     public BuildActionRunnerBackedConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
         super(delegate, new R12VersionDetails(delegate.getMetaData().getVersion()));
         ModelProducer consumerConnectionBackedModelProducer = new BuildActionRunnerBackedModelProducer(adapter, getVersionDetails(), modelMapping,  (BuildActionRunner) delegate);
-        ModelProducer producerWithGradleBuild = new GradleBuildAdapterProducer(adapter, getVersionDetails(), modelMapping, consumerConnectionBackedModelProducer);
-        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelMapping, producerWithGradleBuild);
+        ModelProducer producerWithGradleBuild = new GradleBuildAdapterProducer(adapter, consumerConnectionBackedModelProducer);
+        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), producerWithGradleBuild);
+        actionRunner = new UnsupportedActionRunner(getVersionDetails());
+    }
+
+    @Override
+    protected ActionRunner getActionRunner() {
+        return actionRunner;
     }
 
-    public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        return modelProducer.produceModel(type, operationParameters);
+    @Override
+    protected ModelProducer getModelProducer() {
+        return modelProducer;
     }
 
     private static class R12VersionDetails extends VersionDetails {
@@ -57,11 +67,6 @@ public class BuildActionRunnerBackedConsumerConnection extends AbstractPost12Con
         }
 
         @Override
-        public boolean supportsGradleProjectModel() {
-            return true;
-        }
-
-        @Override
         public boolean maySupportModel(Class<?> modelType) {
             return modelType.equals(ProjectOutcomes.class)
                     || modelType.equals(HierarchicalEclipseProject.class)
@@ -74,12 +79,17 @@ public class BuildActionRunnerBackedConsumerConnection extends AbstractPost12Con
         }
     }
 
-    private class BuildActionRunnerBackedModelProducer extends AbstractModelProducer {
+    private static class BuildActionRunnerBackedModelProducer implements ModelProducer {
+        private final ProtocolToModelAdapter adapter;
+        private final VersionDetails versionDetails;
+        private final ModelMapping modelMapping;
         private final BuildActionRunner buildActionRunner;
         private final Action<SourceObjectMapping> mapper;
 
         public BuildActionRunnerBackedModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, BuildActionRunner buildActionRunner) {
-            super(adapter, versionDetails, modelMapping);
+            this.adapter = adapter;
+            this.versionDetails = versionDetails;
+            this.modelMapping = modelMapping;
             this.buildActionRunner = buildActionRunner;
             mapper = new TaskPropertyHandlerFactory().forVersion(versionDetails);
         }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildInvocationsAdapterProducer.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildInvocationsAdapterProducer.java
index 39b5078..22cbcc2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildInvocationsAdapterProducer.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/BuildInvocationsAdapterProducer.java
@@ -19,30 +19,29 @@ package org.gradle.tooling.internal.consumer.connection;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.consumer.converters.BuildInvocationsConverter;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
-import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
 import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
-import org.gradle.tooling.internal.gradle.DefaultBuildInvocations;
-import org.gradle.tooling.internal.gradle.DefaultGradleTask;
 import org.gradle.tooling.model.GradleProject;
 import org.gradle.tooling.model.gradle.BuildInvocations;
 import org.gradle.tooling.model.internal.Exceptions;
 
-public class BuildInvocationsAdapterProducer extends AbstractModelProducer {
+public class BuildInvocationsAdapterProducer implements ModelProducer {
+    private final ProtocolToModelAdapter adapter;
+    private final VersionDetails versionDetails;
     private final ModelProducer delegate;
 
-    public BuildInvocationsAdapterProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, ModelProducer delegate) {
-        super(adapter, versionDetails, modelMapping);
+    public BuildInvocationsAdapterProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelProducer delegate) {
+        this.adapter = adapter;
+        this.versionDetails = versionDetails;
         this.delegate = delegate;
     }
 
     public <T> T produceModel(Class<T> type, ConsumerOperationParameters operationParameters) {
-        if (type.getName().equals(BuildInvocations.class.getName()) && !versionDetails.maySupportModel(type)) {
+        if (type.equals(BuildInvocations.class)) {
             if (!versionDetails.maySupportModel(GradleProject.class)) {
                 throw Exceptions.unsupportedModel(type, versionDetails.getVersion());
             }
             GradleProject gradleProject = delegate.produceModel(GradleProject.class, operationParameters);
-            DefaultBuildInvocations<DefaultGradleTask> convert = new BuildInvocationsConverter().convert(gradleProject);
-            return adapter.adapt(type, convert);
+            return adapter.adapt(type, new BuildInvocationsConverter().convert(gradleProject));
         }
         return delegate.produceModel(type, operationParameters);
     }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnection.java
new file mode 100644
index 0000000..90b969f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnection.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.api.Transformer;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildActionFailureException;
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
+import org.gradle.tooling.internal.consumer.parameters.BuildCancellationTokenAdapter;
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
+import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
+import org.gradle.tooling.internal.protocol.*;
+
+/**
+ * An adapter for {@link InternalCancellableConnection}.
+ *
+ * <p>Used for providers >= 2.1.</p>
+ */
+public class CancellableConsumerConnection extends AbstractPost12ConsumerConnection {
+    private final ActionRunner actionRunner;
+    private final ModelProducer modelProducer;
+
+    public CancellableConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
+        super(delegate, new R21VersionDetails(delegate.getMetaData().getVersion()));
+        Transformer<RuntimeException, RuntimeException> exceptionTransformer = new ExceptionTransformer();
+        InternalCancellableConnection connection = (InternalCancellableConnection) delegate;
+        modelProducer = new CancellableModelBuilderBackedModelProducer(adapter, getVersionDetails(), modelMapping, connection, exceptionTransformer);
+        actionRunner = new CancellableActionRunner(connection, adapter, exceptionTransformer);
+    }
+
+    @Override
+    protected ActionRunner getActionRunner() {
+        return actionRunner;
+    }
+
+    @Override
+    protected ModelProducer getModelProducer() {
+        return modelProducer;
+    }
+
+    private static class R21VersionDetails extends VersionDetails {
+        private R21VersionDetails(String version) {
+            super(version);
+        }
+
+        @Override
+        public boolean supportsTaskDisplayName() {
+            return true;
+        }
+
+        @Override
+        public boolean maySupportModel(Class<?> modelType) {
+            return true;
+        }
+
+        @Override
+        public boolean supportsCancellation() {
+            return true;
+        }
+    }
+
+    private static class ExceptionTransformer implements Transformer<RuntimeException, RuntimeException> {
+        public RuntimeException transform(RuntimeException e) {
+            for (Throwable t = e; t != null; t = t.getCause()) {
+                if ("org.gradle.api.BuildCancelledException".equals(t.getClass().getName())
+                        || "org.gradle.tooling.BuildCancelledException".equals(t.getClass().getName())) {
+                    return new InternalBuildCancelledException(e.getCause());
+                }
+            }
+            return e;
+        }
+    }
+
+    private static class CancellableActionRunner implements ActionRunner {
+        private final InternalCancellableConnection executor;
+        private final ProtocolToModelAdapter adapter;
+        private final Transformer<RuntimeException, RuntimeException> exceptionTransformer;
+
+        private CancellableActionRunner(InternalCancellableConnection executor, ProtocolToModelAdapter adapter, Transformer<RuntimeException, RuntimeException> exceptionTransformer) {
+            this.executor = executor;
+            this.adapter = adapter;
+            this.exceptionTransformer = exceptionTransformer;
+        }
+
+        public <T> T run(final BuildAction<T> action, ConsumerOperationParameters operationParameters)
+                throws UnsupportedOperationException, IllegalStateException {
+            BuildResult<T> result;
+            try {
+                try {
+                    result = executor.run(new InternalBuildActionAdapter<T>(action, adapter), new BuildCancellationTokenAdapter(operationParameters.getCancellationToken()), operationParameters);
+                } catch (RuntimeException e) {
+                    throw exceptionTransformer.transform(e);
+                }
+            } catch (InternalBuildActionFailureException e) {
+                throw new BuildActionFailureException("The supplied build action failed with an exception.", e.getCause());
+            }
+            return result.getModel();
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducer.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducer.java
new file mode 100644
index 0000000..3df1a4e
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducer.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
+import org.gradle.tooling.internal.adapter.SourceObjectMapping;
+import org.gradle.tooling.internal.consumer.converters.TaskPropertyHandlerFactory;
+import org.gradle.tooling.internal.consumer.parameters.BuildCancellationTokenAdapter;
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
+import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
+import org.gradle.tooling.internal.protocol.BuildResult;
+import org.gradle.tooling.internal.protocol.InternalCancellableConnection;
+import org.gradle.tooling.internal.protocol.InternalUnsupportedModelException;
+import org.gradle.tooling.internal.protocol.ModelIdentifier;
+import org.gradle.tooling.model.internal.Exceptions;
+
+public class CancellableModelBuilderBackedModelProducer implements ModelProducer {
+    private final ProtocolToModelAdapter adapter;
+    private final VersionDetails versionDetails;
+    private final ModelMapping modelMapping;
+    private final InternalCancellableConnection builder;
+    private final Transformer<RuntimeException, RuntimeException> exceptionTransformer;
+    private final Action<SourceObjectMapping> mapper;
+
+    public CancellableModelBuilderBackedModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, InternalCancellableConnection builder, Transformer<RuntimeException, RuntimeException> exceptionTransformer) {
+        this.adapter = adapter;
+        this.versionDetails = versionDetails;
+        this.modelMapping = modelMapping;
+        this.builder = builder;
+        this.exceptionTransformer = exceptionTransformer;
+        mapper = new TaskPropertyHandlerFactory().forVersion(versionDetails);
+    }
+
+    public <T> T produceModel(Class<T> type, ConsumerOperationParameters operationParameters) {
+        if (!versionDetails.maySupportModel(type)) {
+            throw Exceptions.unsupportedModel(type, versionDetails.getVersion());
+        }
+        final ModelIdentifier modelIdentifier = modelMapping.getModelIdentifierFromModelType(type);
+        BuildResult<?> result;
+        try {
+            result = builder.getModel(modelIdentifier, new BuildCancellationTokenAdapter(operationParameters.getCancellationToken()), operationParameters);
+        } catch (InternalUnsupportedModelException e) {
+            throw Exceptions.unknownModel(type, e);
+        } catch (RuntimeException e) {
+            throw exceptionTransformer.transform(e);
+        }
+        return adapter.adapt(type, result.getModel(), mapper);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnection.java
index c7549d6..c8f7366 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnection.java
@@ -16,160 +16,54 @@
 
 package org.gradle.tooling.internal.consumer.connection;
 
-import org.gradle.api.Action;
-import org.gradle.internal.Actions;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.UnsupportedVersionException;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
-import org.gradle.tooling.internal.adapter.SourceObjectMapping;
 import org.gradle.tooling.internal.build.VersionOnlyBuildEnvironment;
-import org.gradle.tooling.internal.consumer.converters.GradleProjectConverter;
-import org.gradle.tooling.internal.consumer.converters.PropertyHandlerFactory;
-import org.gradle.tooling.internal.consumer.converters.TaskPropertyHandlerFactory;
+import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
-import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
 import org.gradle.tooling.internal.protocol.ConnectionVersion4;
-import org.gradle.tooling.internal.protocol.ProjectVersion3;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
-import org.gradle.tooling.model.GradleProject;
 import org.gradle.tooling.model.build.BuildEnvironment;
-import org.gradle.tooling.model.eclipse.EclipseProject;
-import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject;
-import org.gradle.tooling.model.idea.BasicIdeaProject;
-import org.gradle.tooling.model.idea.IdeaProject;
-import org.gradle.tooling.model.internal.Exceptions;
-import org.gradle.util.GradleVersion;
-
-import java.io.IOException;
-import java.io.OutputStream;
 
 /**
- * An adapter that wraps a {@link ConnectionVersion4} based provider.
+ * An adapter for unsupported connection using a {@code ConnectionVersion4} based provider.
+ *
+ * <p>Used for providers >= 1.0-milestone-3 and <= 1.0-milestone-7.</p>
  */
-public class ConnectionVersion4BackedConsumerConnection extends AbstractPre12ConsumerConnection {
-    private final ModelProducer modelProducer;
+public class ConnectionVersion4BackedConsumerConnection implements ConsumerConnection {
+    private final Distribution distribution;
+    private final ProtocolToModelAdapter adapter;
+    private final String version;
+
+    public ConnectionVersion4BackedConsumerConnection(Distribution distribution, ConnectionVersion4 delegate, ProtocolToModelAdapter adapter) {
+        this.distribution = distribution;
+        this.adapter = adapter;
+        version = delegate.getMetaData().getVersion();
+    }
 
-    public ConnectionVersion4BackedConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
-        super(delegate, getMetaData(delegate), adapter);
-        ModelProducer consumerConnectionBackedModelProducer = new ConnectionVersion4BackedModelProducer(adapter, getVersionDetails(), modelMapping, delegate);
-        ModelProducer gradleProjectAdapterProducer = new GradleProjectAdapterProducer(adapter, getVersionDetails(), modelMapping, consumerConnectionBackedModelProducer);
-        ModelProducer producerWithGradleBuild = new GradleBuildAdapterProducer(adapter, getVersionDetails(), modelMapping, gradleProjectAdapterProducer);
-        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelMapping, producerWithGradleBuild);
+    public void stop() {
     }
 
-    private static VersionDetails getMetaData(final ConnectionVersion4 delegate) {
-        GradleVersion version = GradleVersion.version(delegate.getMetaData().getVersion());
-        if (version.compareTo(GradleVersion.version("1.0-milestone-5")) < 0) {
-            return new R10M3VersionDetails(delegate);
-        } else {
-            return new R10M5VersionDetails(delegate);
-        }
+    public String getDisplayName() {
+        return distribution.getDisplayName();
     }
 
-    @Override
     public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        VersionDetails versionDetails = getVersionDetails();
-        if (operationParameters.getJavaHome() != null) {
-            throw Exceptions.unsupportedOperationConfiguration("modelBuilder.setJavaHome() and buildLauncher.setJavaHome()", versionDetails.getVersion());
-        }
-        if (operationParameters.getJvmArguments() != null) {
-            throw Exceptions.unsupportedOperationConfiguration("modelBuilder.setJvmArguments() and buildLauncher.setJvmArguments()", versionDetails.getVersion());
-        }
-        if (operationParameters.getStandardInput() != null) {
-            throw Exceptions.unsupportedOperationConfiguration("modelBuilder.setStandardInput() and buildLauncher.setStandardInput()", versionDetails.getVersion());
-        }
-        OutputStream out = operationParameters.getStandardOutput();
-        if (out != null) {
-            try {
-                String deprecationMessage = String.format("Connecting to Gradle version %s from the Gradle tooling API has been deprecated and is scheduled to be removed in version 2.0 of the Gradle tooling API%n", versionDetails.getVersion());
-                out.write(deprecationMessage.getBytes());
-            } catch (IOException e) {
-                throw new RuntimeException("Cannot write to stream", e);
-            }
+        if (type.equals(BuildEnvironment.class)) {
+            return adapter.adapt(type, doGetBuildEnvironment());
         }
-        return super.run(type, operationParameters);
+        throw fail();
     }
 
-    @Override
-    protected <T> T doGetModel(Class<T> modelType, ConsumerOperationParameters operationParameters) {
-        return modelProducer.produceModel(modelType, operationParameters);
+    private Object doGetBuildEnvironment() {
+        return new VersionOnlyBuildEnvironment(version);
     }
 
-    private static class R10M3VersionDetails extends VersionDetails {
-        public R10M3VersionDetails(ConnectionVersion4 delegate) {
-            super(delegate.getMetaData().getVersion());
-        }
-
-        @Override
-        public boolean maySupportModel(Class<?> modelType) {
-            return modelType.equals(HierarchicalEclipseProject.class) || modelType.equals(EclipseProjectVersion3.class) || modelType.equals(EclipseProject.class) || modelType.equals(Void.class);
-        }
+    public <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
+        throw fail();
     }
 
-    private static class R10M5VersionDetails extends VersionDetails {
-        public R10M5VersionDetails(ConnectionVersion4 delegate) {
-            super(delegate.getMetaData().getVersion());
-        }
-
-        @Override
-        public boolean supportsGradleProjectModel() {
-            return true;
-        }
-
-        @Override
-        public boolean maySupportModel(Class<?> modelType) {
-            return modelType.equals(HierarchicalEclipseProject.class)
-                    || modelType.equals(EclipseProject.class)
-                    || modelType.equals(IdeaProject.class)
-                    || modelType.equals(BasicIdeaProject.class)
-                    || modelType.equals(GradleProject.class)
-                    || modelType.equals(Void.class);
-        }
-    }
-
-    private class ConnectionVersion4BackedModelProducer extends AbstractModelProducer {
-        private final ConnectionVersion4 delegate;
-        private final Action<SourceObjectMapping> mapper;
-
-        public ConnectionVersion4BackedModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, ConnectionVersion4 delegate) {
-            super(adapter, versionDetails, modelMapping);
-            this.delegate = delegate;
-            mapper = Actions.composite(
-                    new PropertyHandlerFactory().forVersion(versionDetails),
-                    new TaskPropertyHandlerFactory().forVersion(versionDetails));
-        }
-
-        public <T> T produceModel(Class<T> modelType, ConsumerOperationParameters operationParameters) {
-            if (modelType == BuildEnvironment.class && !versionDetails.maySupportModel(BuildEnvironment.class)) {
-                //early versions of provider do not support BuildEnvironment model
-                //since we know the gradle version at least we can give back some result
-                return adapter.adapt(modelType, new VersionOnlyBuildEnvironment(versionDetails.getVersion()), mapper);
-            }
-            if (!versionDetails.maySupportModel(modelType)) {
-                //don't bother asking the provider for this model
-                throw Exceptions.unsupportedModel(modelType, versionDetails.getVersion());
-            }
-            Class<? extends ProjectVersion3> protocolType = modelMapping.getProtocolType(modelType).asSubclass(ProjectVersion3.class);
-            final ProjectVersion3 model = delegate.getModel(protocolType, operationParameters);
-            return adapter.adapt(modelType, model, mapper);
-        }
-    }
-
-    private class GradleProjectAdapterProducer extends AbstractModelProducer {
-        private final ModelProducer delegate;
-
-        public GradleProjectAdapterProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, ModelProducer delegate) {
-            super(adapter, versionDetails, modelMapping);
-            this.delegate = delegate;
-        }
-
-        public <T> T produceModel(Class<T> modelType, ConsumerOperationParameters operationParameters) {
-            if (modelType == GradleProject.class && !versionDetails.maySupportModel(GradleProject.class)) {
-                //we broke compatibility around M9 wrt getting the tasks of a project (issue GRADLE-1875)
-                //this patch enables getting gradle tasks for target gradle version pre M5
-                EclipseProjectVersion3 project = delegate.produceModel(EclipseProjectVersion3.class, operationParameters);
-                return adapter.adapt(modelType, new GradleProjectConverter().convert(project));
-            }
-            return delegate.produceModel(modelType, operationParameters);
-        }
+    private UnsupportedVersionException fail() {
+        return new UnsupportedVersionException(String.format("Support for Gradle version %s was removed in tooling API version 2.0. You should upgrade your Gradle build to use Gradle 1.0-milestone-8 or later.", version));
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerActionExecutor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerActionExecutor.java
index ddc4f06..e3d5d8a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerActionExecutor.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerActionExecutor.java
@@ -16,11 +16,15 @@
 
 package org.gradle.tooling.internal.consumer.connection;
 
+import org.gradle.internal.concurrent.Stoppable;
+
 /**
  * Implementations must be thread-safe.
  */
-public interface ConsumerActionExecutor {
-
+public interface ConsumerActionExecutor extends Stoppable {
+    /**
+     * Blocks until all actions have completed.
+     */
     void stop();
     
     String getDisplayName();
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerConnection.java
index 49ab2b1..3f9d857 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ConsumerConnection.java
@@ -16,19 +16,24 @@
 
 package org.gradle.tooling.internal.consumer.connection;
 
+import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.tooling.BuildAction;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 
 /**
  * Implementations must be thread-safe.
  */
-public interface ConsumerConnection {
-
+public interface ConsumerConnection extends Stoppable {
+    /**
+     * Cleans up resources used by this connection. Blocks until complete.
+     */
     void stop();
     
     String getDisplayName();
 
-    <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException;
+    <T> T run(Class<T> type, ConsumerOperationParameters operationParameters)
+            throws UnsupportedOperationException, IllegalStateException;
 
-    <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException;
+    <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters)
+            throws UnsupportedOperationException, IllegalStateException;
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducer.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducer.java
index 1427507..bdad3cb 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducer.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducer.java
@@ -19,22 +19,21 @@ package org.gradle.tooling.internal.consumer.connection;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.consumer.converters.GradleBuildConverter;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
-import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
 import org.gradle.tooling.internal.gradle.DefaultGradleBuild;
 import org.gradle.tooling.model.GradleProject;
 import org.gradle.tooling.model.gradle.GradleBuild;
 
-public class GradleBuildAdapterProducer extends AbstractModelProducer {
+public class GradleBuildAdapterProducer implements ModelProducer {
+    private final ProtocolToModelAdapter adapter;
     private final ModelProducer delegate;
 
-    public GradleBuildAdapterProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, ModelProducer delegate) {
-        super(adapter, versionDetails, modelMapping);
+    public GradleBuildAdapterProducer(ProtocolToModelAdapter adapter, ModelProducer delegate) {
+        this.adapter = adapter;
         this.delegate = delegate;
     }
 
     public <T> T produceModel(Class<T> type, ConsumerOperationParameters operationParameters) {
-        if (type.getName().equals(GradleBuild.class.getName()) && !versionDetails.maySupportModel(type)) {
+        if (type.equals(GradleBuild.class)) {
             GradleProject gradleProject = delegate.produceModel(GradleProject.class, operationParameters);
             final DefaultGradleBuild convert = new GradleBuildConverter().convert(gradleProject);
             return adapter.adapt(type, convert);
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalBuildActionAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalBuildActionAdapter.java
new file mode 100644
index 0000000..b333a05
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalBuildActionAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
+import org.gradle.tooling.internal.protocol.InternalBuildAction;
+import org.gradle.tooling.internal.protocol.InternalBuildController;
+
+/**
+ * Adapter to create {@link org.gradle.tooling.internal.protocol.InternalBuildAction}
+ * from an instance of {@link org.gradle.tooling.BuildAction}.
+ * Used by consumer connections 1.8+.
+ */
+class InternalBuildActionAdapter<T> implements InternalBuildAction<T> {
+    private final BuildAction<T> action;
+    private final ProtocolToModelAdapter adapter;
+
+    public InternalBuildActionAdapter(BuildAction<T> action, ProtocolToModelAdapter adapter) {
+        this.action = action;
+        this.adapter = adapter;
+    }
+
+    public T execute(final InternalBuildController buildController) {
+        return action.execute(new BuildControllerAdapter(adapter, buildController, new ModelMapping()));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnection.java
index c4258c5..38b02b3 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnection.java
@@ -17,8 +17,10 @@
 package org.gradle.tooling.internal.consumer.connection;
 
 import org.gradle.api.Action;
+import org.gradle.tooling.internal.adapter.CompatibleIntrospector;
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
 import org.gradle.tooling.internal.adapter.SourceObjectMapping;
+import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.converters.TaskPropertyHandlerFactory;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
@@ -35,20 +37,35 @@ import org.gradle.tooling.model.internal.Exceptions;
 
 /**
  * An adapter for a {@link InternalConnection} based provider.
+ *
+ * <p>Used for providers >= 1.0-milestone-8 and <= 1.1.</p>
  */
-public class InternalConnectionBackedConsumerConnection extends AbstractPre12ConsumerConnection {
+public class InternalConnectionBackedConsumerConnection extends AbstractConsumerConnection {
     private final ModelProducer modelProducer;
+    private final UnsupportedActionRunner actionRunner;
 
     public InternalConnectionBackedConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
-        super(delegate, new R10M8VersionDetails(delegate.getMetaData().getVersion()), adapter);
-        ModelProducer consumerConnectionBackedModelProducer = new InternalConnectionBackedModelProducer(adapter, getVersionDetails(), modelMapping, (InternalConnection) delegate);
-        ModelProducer producerWithGradleBuild = new GradleBuildAdapterProducer(adapter, getVersionDetails(), modelMapping, consumerConnectionBackedModelProducer);
-        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelMapping, producerWithGradleBuild);
+        super(delegate, new R10M8VersionDetails(delegate.getMetaData().getVersion()));
+        ModelProducer modelProducer = new InternalConnectionBackedModelProducer(adapter, getVersionDetails(), modelMapping, (InternalConnection) delegate);
+        modelProducer = new GradleBuildAdapterProducer(adapter, modelProducer);
+        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelProducer);
+        this.modelProducer = new BuildExecutingModelProducer(modelProducer);
+        this.actionRunner = new UnsupportedActionRunner(getVersionDetails());
+    }
+
+    @Override
+    protected ActionRunner getActionRunner() {
+        return actionRunner;
+    }
+
+    @Override
+    protected ModelProducer getModelProducer() {
+        return modelProducer;
     }
 
     @Override
-    protected <T> T doGetModel(Class<T> modelType, ConsumerOperationParameters operationParameters) {
-        return modelProducer.produceModel(modelType, operationParameters);
+    public void configure(ConnectionParameters connectionParameters) {
+        new CompatibleIntrospector(getDelegate()).callSafely("configureLogging", connectionParameters.getVerboseLogging());
     }
 
     private static class R10M8VersionDetails extends VersionDetails {
@@ -57,11 +74,6 @@ public class InternalConnectionBackedConsumerConnection extends AbstractPre12Con
         }
 
         @Override
-        public boolean supportsGradleProjectModel() {
-            return true;
-        }
-
-        @Override
         public boolean maySupportModel(Class<?> modelType) {
             return modelType.equals(Void.class)
                     || modelType.equals(HierarchicalEclipseProject.class)
@@ -73,12 +85,37 @@ public class InternalConnectionBackedConsumerConnection extends AbstractPre12Con
         }
     }
 
-    private class InternalConnectionBackedModelProducer extends AbstractModelProducer {
+    private class BuildExecutingModelProducer implements ModelProducer {
+        private final ModelProducer delegate;
+
+        private BuildExecutingModelProducer(ModelProducer delegate) {
+            this.delegate = delegate;
+        }
+
+        public <T> T produceModel(Class<T> type, ConsumerOperationParameters operationParameters) {
+            if (type.equals(Void.class)) {
+                getDelegate().executeBuild(operationParameters, operationParameters);
+                return null;
+            } else {
+                if (operationParameters.getTasks() != null) {
+                    throw Exceptions.unsupportedOperationConfiguration("modelBuilder.forTasks()", getVersionDetails().getVersion());
+                }
+                return delegate.produceModel(type, operationParameters);
+            }
+        }
+    }
+
+    private static class InternalConnectionBackedModelProducer implements ModelProducer {
+        private final ProtocolToModelAdapter adapter;
+        private final VersionDetails versionDetails;
+        private final ModelMapping modelMapping;
         private final InternalConnection delegate;
         private final Action<SourceObjectMapping> mapper;
 
         public InternalConnectionBackedModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, InternalConnection delegate) {
-            super(adapter, versionDetails, modelMapping);
+            this.adapter = adapter;
+            this.versionDetails = versionDetails;
+            this.modelMapping = modelMapping;
             this.delegate = delegate;
             this.mapper = new TaskPropertyHandlerFactory().forVersion(versionDetails);
         }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutor.java
index ee6422e..a0f2f19 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutor.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutor.java
@@ -15,7 +15,9 @@
  */
 package org.gradle.tooling.internal.consumer.connection;
 
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.UncheckedException;
+import org.gradle.tooling.BuildCancelledException;
 import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.LoggingProvider;
@@ -51,7 +53,6 @@ public class LazyConsumerActionExecutor implements ConsumerActionExecutor {
     }
 
     public void stop() {
-        ConsumerConnection connection = null;
         lock.lock();
         try {
             stopped = true;
@@ -62,14 +63,10 @@ public class LazyConsumerActionExecutor implements ConsumerActionExecutor {
                     throw UncheckedException.throwAsUncheckedException(e);
                 }
             }
-            connection = this.connection;
             this.connection = null;
         } finally {
             lock.unlock();
         }
-        if (connection != null) {
-            connection.stop();
-        }
     }
 
     public String getDisplayName() {
@@ -78,14 +75,18 @@ public class LazyConsumerActionExecutor implements ConsumerActionExecutor {
 
     public <T> T run(ConsumerAction<T> action) throws UnsupportedOperationException, IllegalStateException {
         try {
-            ConsumerConnection connection = onStartAction();
+            BuildCancellationToken cancellationToken = action.getParameters().getCancellationToken();
+            if (cancellationToken.isCancellationRequested()) {
+                throw new BuildCancelledException("Build cancelled");
+            }
+            ConsumerConnection connection = onStartAction(cancellationToken);
             return action.run(connection);
         } finally {
             onEndAction();
         }
     }
 
-    private ConsumerConnection onStartAction() {
+    private ConsumerConnection onStartAction(BuildCancellationToken cancellationToken) {
         lock.lock();
         try {
             if (stopped) {
@@ -95,7 +96,7 @@ public class LazyConsumerActionExecutor implements ConsumerActionExecutor {
             if (connection == null) {
                 // Hold the lock while creating the connection. Not generally good form.
                 // In this instance, blocks other threads from creating the connection at the same time
-                connection = implementationLoader.create(distribution, loggingProvider.getProgressLoggerFactory(), connectionParameters);
+                connection = implementationLoader.create(distribution, loggingProvider.getProgressLoggerFactory(), connectionParameters, cancellationToken);
             }
             return connection;
         } finally {
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LoggingInitializerConsumerActionExecutor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LoggingInitializerConsumerActionExecutor.java
deleted file mode 100644
index 5483805..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/LoggingInitializerConsumerActionExecutor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.consumer.connection;
-
-import org.gradle.tooling.internal.consumer.SynchronizedLogging;
-
-/**
- * The idea is to initialize the logging infrastructure before we actually build the model or run a build.
- */
-public class LoggingInitializerConsumerActionExecutor implements ConsumerActionExecutor {
-
-    private final ConsumerActionExecutor actionExecutor;
-    private final SynchronizedLogging synchronizedLogging;
-
-    public LoggingInitializerConsumerActionExecutor(ConsumerActionExecutor actionExecutor, SynchronizedLogging synchronizedLogging) {
-        this.actionExecutor = actionExecutor;
-        this.synchronizedLogging = synchronizedLogging;
-    }
-
-    public void stop() {
-        actionExecutor.stop();
-    }
-
-    public String getDisplayName() {
-        return actionExecutor.getDisplayName();
-    }
-
-    public <T> T run(ConsumerAction<T> action) throws UnsupportedOperationException, IllegalStateException {
-        synchronizedLogging.init();
-        return actionExecutor.run(action);
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnection.java
index f83cabe..521485b 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnection.java
@@ -17,45 +17,43 @@
 package org.gradle.tooling.internal.consumer.connection;
 
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
-import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
 import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
 import org.gradle.tooling.internal.protocol.ConnectionVersion4;
 import org.gradle.tooling.internal.protocol.ModelBuilder;
 import org.gradle.tooling.model.gradle.BuildInvocations;
 import org.gradle.tooling.model.gradle.GradleBuild;
-import org.gradle.util.GradleVersion;
 
 /**
  * An adapter for a {@link ModelBuilder} based provider.
+ *
+ * <p>Used for providers >= 1.6 and <= 1.8</p>
  */
 public class ModelBuilderBackedConsumerConnection extends AbstractPost12ConsumerConnection {
     private final ModelProducer modelProducer;
+    private final ActionRunner actionRunner;
 
     public ModelBuilderBackedConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
-        super(delegate, getVersionDetails(delegate.getMetaData().getVersion()));
+        super(delegate, new R16VersionDetails(delegate.getMetaData().getVersion()));
         ModelBuilder builder = (ModelBuilder) delegate;
-        ModelProducer consumerConnectionBackedModelProducer = new ModelBuilderBackedModelProducer(adapter, getVersionDetails(), modelMapping, builder);
-        ModelProducer producerWithGradleBuild = new GradleBuildAdapterProducer(adapter, getVersionDetails(), modelMapping, consumerConnectionBackedModelProducer);
-        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelMapping, producerWithGradleBuild);
+        ModelProducer modelProducer =  new ModelBuilderBackedModelProducer(adapter, getVersionDetails(), modelMapping, builder);
+        modelProducer = new GradleBuildAdapterProducer(adapter, modelProducer);
+        modelProducer = new BuildInvocationsAdapterProducer(adapter, getVersionDetails(), modelProducer);
+        this.modelProducer = modelProducer;
+        this.actionRunner = new UnsupportedActionRunner(getVersionDetails());
     }
 
-    public static VersionDetails getVersionDetails(String versionString) {
-        GradleVersion version = GradleVersion.version(versionString);
-        if (version.compareTo(GradleVersion.version("1.11")) > 0) {
-            return new R112VersionDetails(version.getVersion());
-        }
-        if (version.compareTo(GradleVersion.version("1.8-rc-1")) >= 0) {
-            return new R18VersionDetails(version.getVersion());
-        }
-        return new R16VersionDetails(version.getVersion());
+    @Override
+    protected ActionRunner getActionRunner() {
+        return actionRunner;
     }
 
-    public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
-        return modelProducer.produceModel(type, operationParameters);
+    @Override
+    protected ModelProducer getModelProducer() {
+        return modelProducer;
     }
 
-    private static class R16VersionDetails extends VersionDetails {
+    static class R16VersionDetails extends VersionDetails {
         public R16VersionDetails(String version) {
             super(version);
         }
@@ -65,43 +63,5 @@ public class ModelBuilderBackedConsumerConnection extends AbstractPost12Consumer
             return modelType != BuildInvocations.class
                     && modelType != GradleBuild.class;
         }
-
-        @Override
-        public boolean supportsGradleProjectModel() {
-            return true;
-        }
-    }
-
-    private static class R18VersionDetails extends R16VersionDetails {
-        private R18VersionDetails(String version) {
-            super(version);
-        }
-
-        @Override
-        public boolean maySupportModel(Class<?> modelType) {
-            if (modelType == GradleBuild.class) {
-                return true;
-            }
-            return super.maySupportModel(modelType);
-        }
-    }
-
-    private static class R112VersionDetails extends R18VersionDetails {
-        private R112VersionDetails(String version) {
-            super(version);
-        }
-
-        @Override
-        public boolean maySupportModel(Class<?> modelType) {
-            if (modelType == BuildInvocations.class) {
-                return true;
-            }
-            return super.maySupportModel(modelType);
-        }
-
-        @Override
-        public boolean supportsTaskDisplayName() {
-            return true;
-        }
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedModelProducer.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedModelProducer.java
index 064dfe9..6d347f2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedModelProducer.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedModelProducer.java
@@ -29,12 +29,17 @@ import org.gradle.tooling.internal.protocol.ModelBuilder;
 import org.gradle.tooling.internal.protocol.ModelIdentifier;
 import org.gradle.tooling.model.internal.Exceptions;
 
-public class ModelBuilderBackedModelProducer extends AbstractModelProducer {
+public class ModelBuilderBackedModelProducer implements ModelProducer {
+    private final ProtocolToModelAdapter adapter;
+    private final VersionDetails versionDetails;
+    private final ModelMapping modelMapping;
     private final ModelBuilder builder;
     private final Action<SourceObjectMapping> mapper;
 
     public ModelBuilderBackedModelProducer(ProtocolToModelAdapter adapter, VersionDetails versionDetails, ModelMapping modelMapping, ModelBuilder builder) {
-        super(adapter, versionDetails, modelMapping);
+        this.adapter = adapter;
+        this.versionDetails = versionDetails;
+        this.modelMapping = modelMapping;
         this.builder = builder;
         mapper = new TaskPropertyHandlerFactory().forVersion(versionDetails);
     }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NoToolingApiConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NoToolingApiConnection.java
index aad5c14..2988d5a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NoToolingApiConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NoToolingApiConnection.java
@@ -21,6 +21,11 @@ import org.gradle.tooling.UnsupportedVersionException;
 import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
 
+/**
+ * A {@code ConsumerConnection} implementation for a Gradle version that does not support the tooling API.
+ *
+ * <p>Used for versions < 1.0-milestone-3.</p>
+ */
 public class NoToolingApiConnection implements ConsumerConnection {
     private final Distribution distribution;
 
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapter.java
new file mode 100644
index 0000000..c6bec6d
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NonCancellableConsumerConnectionAdapter implements ConsumerConnection {
+    private static final Logger LOGGER = LoggerFactory.getLogger(NonCancellableConsumerConnectionAdapter.class);
+
+    private final ConsumerConnection delegate;
+
+    public NonCancellableConsumerConnectionAdapter(ConsumerConnection delegate) {
+        this.delegate = delegate;
+    }
+
+    public void stop() {
+        delegate.stop();
+    }
+
+    public String getDisplayName() {
+        return delegate.getDisplayName();
+    }
+
+    public <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
+        Runnable callback = handleCancellationPreOperation(operationParameters.getCancellationToken());
+        try {
+            return delegate.run(action, operationParameters);
+        } finally {
+            handleCancellationPostOperation(operationParameters.getCancellationToken(), callback);
+        }
+    }
+
+    public <T> T run(Class<T> type, ConsumerOperationParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
+        Runnable callback = handleCancellationPreOperation(operationParameters.getCancellationToken());
+        try {
+            return delegate.run(type, operationParameters);
+        } finally {
+            handleCancellationPostOperation(operationParameters.getCancellationToken(), callback);
+        }
+    }
+
+    private Runnable handleCancellationPreOperation(BuildCancellationToken cancellationToken) {
+        Runnable callback = new Runnable() {
+            public void run() {
+                LOGGER.info("Note: Version of Gradle provider does not support cancellation. Upgrade your Gradle build.");
+            }
+        };
+        cancellationToken.addCallback(callback);
+        return callback;
+    }
+
+    private void handleCancellationPostOperation(BuildCancellationToken cancellationToken, Runnable callback) {
+        cancellationToken.removeCallback(callback);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutor.java
index 36e91aa..fb9bb25 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutor.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutor.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.tooling.internal.consumer.connection;
 
-import org.gradle.listener.ListenerManager;
+import org.gradle.internal.event.ListenerManager;
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.internal.ProgressCompleteEvent;
 import org.gradle.logging.internal.ProgressEvent;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ShutdownAwareConsumerConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ShutdownAwareConsumerConnection.java
new file mode 100644
index 0000000..2c653b3
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/ShutdownAwareConsumerConnection.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping;
+import org.gradle.tooling.internal.protocol.ConnectionVersion4;
+import org.gradle.tooling.internal.protocol.ShutdownParameters;
+import org.gradle.tooling.internal.protocol.StoppableConnection;
+
+/**
+ * <p>Used for providers >= 2.2.</p>
+ */
+public class ShutdownAwareConsumerConnection extends CancellableConsumerConnection {
+    public ShutdownAwareConsumerConnection(ConnectionVersion4 delegate, ModelMapping modelMapping, ProtocolToModelAdapter adapter) {
+        super(delegate, modelMapping, adapter);
+    }
+
+    @Override
+    public void stop() {
+        ((StoppableConnection) getDelegate()).shutdown(new ShutdownParameters() { });
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/UnsupportedActionRunner.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/UnsupportedActionRunner.java
new file mode 100644
index 0000000..8a5dff0
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/connection/UnsupportedActionRunner.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters;
+import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
+import org.gradle.tooling.model.internal.Exceptions;
+
+class UnsupportedActionRunner implements ActionRunner {
+    private final VersionDetails versionDetails;
+
+    UnsupportedActionRunner(VersionDetails versionDetails) {
+        this.versionDetails = versionDetails;
+    }
+
+    public <T> T run(BuildAction<T> action, ConsumerOperationParameters operationParameters) {
+        throw Exceptions.unsupportedFeature("execution of build actions provided by the tooling API client", versionDetails.getVersion(), "1.8");
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverter.java
index cfeacd9..c8abcf2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverter.java
@@ -16,50 +16,38 @@
 
 package org.gradle.tooling.internal.consumer.converters;
 
-import com.google.common.base.Function;
 import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
-import org.gradle.tooling.internal.gradle.BasicGradleTaskSelector;
-import org.gradle.tooling.internal.gradle.DefaultBuildInvocations;
-import org.gradle.tooling.internal.gradle.DefaultGradleTask;
+import org.gradle.tooling.internal.gradle.ConsumerProvidedTask;
+import org.gradle.tooling.internal.gradle.ConsumerProvidedTaskSelector;
 import org.gradle.tooling.model.GradleProject;
 import org.gradle.tooling.model.GradleTask;
 
-import java.util.Iterator;
 import java.util.List;
 import java.util.SortedSet;
 
 public class BuildInvocationsConverter {
-    public DefaultBuildInvocations<DefaultGradleTask> convert(GradleProject project) {
+    public ConsumerProvidedBuildInvocations convert(GradleProject project) {
         GradleProject rootProject = project;
         while (rootProject.getParent() != null) {
             rootProject = rootProject.getParent();
         }
-        List<BasicGradleTaskSelector> selectors = buildRecursively(rootProject);
-        return new DefaultBuildInvocations<DefaultGradleTask>()
-                .setSelectors(selectors)
-                .setTasks(convertTasks(rootProject.getTasks().iterator()));
+        List<ConsumerProvidedTaskSelector> selectors = buildRecursively(rootProject);
+        return new ConsumerProvidedBuildInvocations(selectors, convertTasks(rootProject.getTasks()));
     }
 
-    private List<BasicGradleTaskSelector> buildRecursively(GradleProject project) {
+    private List<ConsumerProvidedTaskSelector> buildRecursively(GradleProject project) {
         Multimap<String, String> aggregatedTasks = ArrayListMultimap.create();
-        for (GradleProject childProject : project.getChildren()) {
-            List<BasicGradleTaskSelector> childSelectors = buildRecursively(childProject);
-            for (BasicGradleTaskSelector childSelector : childSelectors) {
-                aggregatedTasks.putAll(childSelector.getName(), childSelector.getTaskNames());
-            }
-        }
-        for (GradleTask task : project.getTasks()) {
-            aggregatedTasks.put(task.getName(), task.getPath());
-        }
-        List<BasicGradleTaskSelector> selectors = Lists.newArrayList();
+
+        collectTasks(project, aggregatedTasks);
+
+        List<ConsumerProvidedTaskSelector> selectors = Lists.newArrayList();
         for (String selectorName : aggregatedTasks.keySet()) {
             SortedSet<String> selectorTasks = Sets.newTreeSet(new TaskNameComparator());
             selectorTasks.addAll(aggregatedTasks.get(selectorName));
-            selectors.add(new BasicGradleTaskSelector().
+            selectors.add(new ConsumerProvidedTaskSelector().
                     setName(selectorName).
                     setTaskNames(selectorTasks).
                     setDescription(project.getParent() != null
@@ -70,17 +58,23 @@ public class BuildInvocationsConverter {
         return selectors;
     }
 
-    private List<DefaultGradleTask> convertTasks(Iterator<? extends GradleTask> tasks) {
-        return Lists.newArrayList(Iterators.transform(
-                tasks,
-                new Function<GradleTask, DefaultGradleTask>() {
-                    public DefaultGradleTask apply(GradleTask task) {
-                        return new DefaultGradleTask()
-                                .setName(task.getName())
-                                .setPath(task.getPath())
-                                .setDescription(task.getDescription());
-                    }
-                }
-        ));
+    private void collectTasks(GradleProject project, Multimap<String, String> aggregatedTasks) {
+        for (GradleProject childProject : project.getChildren()) {
+            collectTasks(childProject, aggregatedTasks);
+        }
+        for (GradleTask task : project.getTasks()) {
+            aggregatedTasks.put(task.getName(), task.getPath());
+        }
+    }
+
+    private List<ConsumerProvidedTask> convertTasks(Iterable<? extends GradleTask> tasks) {
+        List<ConsumerProvidedTask> result = Lists.newArrayList();
+        for (GradleTask task : tasks) {
+            result.add(new ConsumerProvidedTask()
+                    .setName(task.getName())
+                    .setPath(task.getPath())
+                    .setDescription(task.getDescription()));
+        }
+        return result;
     }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/ConsumerProvidedBuildInvocations.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/ConsumerProvidedBuildInvocations.java
new file mode 100644
index 0000000..d283138
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/ConsumerProvidedBuildInvocations.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.converters;
+
+import org.gradle.tooling.internal.gradle.ConsumerProvidedTask;
+import org.gradle.tooling.internal.gradle.ConsumerProvidedTaskSelector;
+
+import java.util.List;
+
+/**
+ * A consumer-side implementation of {@link org.gradle.tooling.model.gradle.BuildInvocations}
+ */
+public class ConsumerProvidedBuildInvocations {
+    private final List<? extends ConsumerProvidedTaskSelector> selectors;
+    private final List<? extends ConsumerProvidedTask> tasks;
+
+    public ConsumerProvidedBuildInvocations(List<? extends ConsumerProvidedTaskSelector> selectors, List<? extends ConsumerProvidedTask> tasks) {
+        this.selectors = selectors;
+        this.tasks = tasks;
+    }
+
+    public List<? extends ConsumerProvidedTaskSelector> getTaskSelectors() {
+        return selectors;
+    }
+
+    public List<? extends ConsumerProvidedTask> getTasks() {
+        return tasks;
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectConverter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectConverter.java
deleted file mode 100644
index abe8400..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectConverter.java
+++ /dev/null
@@ -1,63 +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.tooling.internal.consumer.converters;
-
-import org.gradle.tooling.internal.gradle.DefaultConvertedGradleProject;
-import org.gradle.tooling.internal.gradle.DefaultGradleProjectTask;
-import org.gradle.tooling.internal.gradle.DefaultGradleTask;
-import org.gradle.tooling.internal.gradle.PartialGradleProject;
-import org.gradle.tooling.internal.protocol.TaskVersion1;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
-
-import java.util.LinkedList;
-import java.util.List;
-
-public class GradleProjectConverter {
-
-    public DefaultConvertedGradleProject convert(EclipseProjectVersion3 project) {
-        //build children recursively
-        List<DefaultConvertedGradleProject> children = new LinkedList<DefaultConvertedGradleProject>();
-        for (EclipseProjectVersion3 p : project.getChildren()) {
-            children.add(convert(p));
-        }
-        //build parent
-        DefaultConvertedGradleProject parent = new DefaultConvertedGradleProject()
-                .setPath(project.getPath())
-                .setName(project.getName())
-                .setChildren(children)
-                .setDescription(project.getDescription());
-
-        //build tasks
-        List<DefaultGradleTask> tasks = new LinkedList<DefaultGradleTask>();
-        for (TaskVersion1 t : project.getTasks()) {
-            tasks.add(new DefaultGradleProjectTask()
-                    .setProject(parent)
-                    .setName(t.getName())
-                    .setPath(t.getPath())
-                    .setDescription(t.getDescription())
-                    );
-        }
-        parent.setTasks(tasks);
-
-        //apply parent to each child
-        for (PartialGradleProject child : children) {
-            child.setParent(parent);
-        }
-
-        return parent;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectMixInHandler.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectMixInHandler.java
deleted file mode 100644
index 36b326a..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/GradleProjectMixInHandler.java
+++ /dev/null
@@ -1,30 +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.tooling.internal.consumer.converters;
-
-import org.gradle.tooling.internal.adapter.MethodInvocation;
-import org.gradle.tooling.internal.adapter.MethodInvoker;
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3;
-
-public class GradleProjectMixInHandler implements MethodInvoker {
-    public void invoke(MethodInvocation invocation) throws Throwable {
-        if (invocation.getName().equals("getGradleProject")
-                && invocation.getDelegate() instanceof EclipseProjectVersion3) {
-            invocation.setResult(new GradleProjectConverter().convert((EclipseProjectVersion3) invocation.getDelegate()));
-        }
-     }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/PropertyHandlerFactory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/PropertyHandlerFactory.java
deleted file mode 100644
index 8ed687b..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/converters/PropertyHandlerFactory.java
+++ /dev/null
@@ -1,44 +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.tooling.internal.consumer.converters;
-
-import org.gradle.api.Action;
-import org.gradle.tooling.internal.adapter.SourceObjectMapping;
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails;
-import org.gradle.tooling.model.eclipse.EclipseProject;
-
-import java.io.Serializable;
-
-public class PropertyHandlerFactory {
-    public Action<SourceObjectMapping> forVersion(VersionDetails versionDetails) {
-        return new ConsumerMapping(versionDetails);
-    }
-
-    private static class ConsumerMapping implements Action<SourceObjectMapping>, Serializable {
-        private final VersionDetails versionDetails;
-
-        public ConsumerMapping(VersionDetails versionDetails) {
-            this.versionDetails = versionDetails;
-        }
-
-        public void execute(SourceObjectMapping mapping) {
-            if (EclipseProject.class.isAssignableFrom(mapping.getTargetType()) && !versionDetails.supportsGradleProjectModel()) {
-                mapping.mixIn(new GradleProjectMixInHandler());
-            }
-        }
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoader.java
index 5da074c..a01ce3d 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoader.java
@@ -15,16 +15,19 @@
  */
 package org.gradle.tooling.internal.consumer.loader;
 
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection;
 
+import java.io.Closeable;
 import java.util.HashMap;
 import java.util.Map;
 
-public class CachingToolingImplementationLoader implements ToolingImplementationLoader {
+public class CachingToolingImplementationLoader implements ToolingImplementationLoader, Closeable {
     private final ToolingImplementationLoader loader;
     private final Map<ClassPath, ConsumerConnection> connections = new HashMap<ClassPath, ConsumerConnection>();
 
@@ -32,15 +35,23 @@ public class CachingToolingImplementationLoader implements ToolingImplementation
         this.loader = loader;
     }
 
-    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters) {
-        ClassPath classpath = distribution.getToolingImplementationClasspath(progressLoggerFactory, connectionParameters.getGradleUserHomeDir());
+    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters, BuildCancellationToken cancellationToken) {
+        ClassPath classpath = distribution.getToolingImplementationClasspath(progressLoggerFactory, connectionParameters.getGradleUserHomeDir(), cancellationToken);
 
         ConsumerConnection connection = connections.get(classpath);
         if (connection == null) {
-            connection = loader.create(distribution, progressLoggerFactory, connectionParameters);
+            connection = loader.create(distribution, progressLoggerFactory, connectionParameters, cancellationToken);
             connections.put(classpath, connection);
         }
 
         return connection;
     }
+
+    public void close() {
+        try {
+            CompositeStoppable.stoppable(connections.values()).stop();
+        } finally {
+            connections.clear();
+        }
+    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoader.java
index dab8617..b8fd527 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoader.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.tooling.internal.consumer.loader;
 
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.Factory;
 import org.gradle.internal.classloader.FilteringClassLoader;
 import org.gradle.internal.classloader.MultiParentClassLoader;
@@ -48,9 +49,9 @@ public class DefaultToolingImplementationLoader implements ToolingImplementation
         this.classLoader = classLoader;
     }
 
-    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters) {
+    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters, BuildCancellationToken cancellationToken) {
         LOGGER.debug("Using tooling provider from {}", distribution.getDisplayName());
-        ClassLoader classLoader = createImplementationClassLoader(distribution, progressLoggerFactory, connectionParameters.getGradleUserHomeDir());
+        ClassLoader classLoader = createImplementationClassLoader(distribution, progressLoggerFactory, connectionParameters.getGradleUserHomeDir(), cancellationToken);
         ServiceLocator serviceLocator = new ServiceLocator(classLoader);
         try {
             Factory<ConnectionVersion4> factory = serviceLocator.findFactory(ConnectionVersion4.class);
@@ -65,7 +66,11 @@ public class DefaultToolingImplementationLoader implements ToolingImplementation
 
             // Adopting the connection to a refactoring friendly type that the consumer owns
             AbstractConsumerConnection adaptedConnection;
-            if (connection instanceof ModelBuilder && connection instanceof InternalBuildActionExecutor) {
+            if (connection instanceof StoppableConnection) {
+                adaptedConnection = new ShutdownAwareConsumerConnection(connection, modelMapping, adapter);
+            } else if (connection instanceof InternalCancellableConnection) {
+                adaptedConnection = new CancellableConsumerConnection(connection, modelMapping, adapter);
+            } else if (connection instanceof ModelBuilder && connection instanceof InternalBuildActionExecutor) {
                 adaptedConnection = new ActionAwareConsumerConnection(connection, modelMapping, adapter);
             } else if (connection instanceof ModelBuilder) {
                 adaptedConnection = new ModelBuilderBackedConsumerConnection(connection, modelMapping, adapter);
@@ -74,9 +79,12 @@ public class DefaultToolingImplementationLoader implements ToolingImplementation
             } else if (connection instanceof InternalConnection) {
                 adaptedConnection = new InternalConnectionBackedConsumerConnection(connection, modelMapping, adapter);
             } else {
-                adaptedConnection = new ConnectionVersion4BackedConsumerConnection(connection, modelMapping, adapter);
+                return new ConnectionVersion4BackedConsumerConnection(distribution, connection, adapter);
             }
             adaptedConnection.configure(connectionParameters);
+            if (!adaptedConnection.getVersionDetails().supportsCancellation()) {
+                return new NonCancellableConsumerConnectionAdapter(adaptedConnection);
+            }
             return adaptedConnection;
         } catch (UnsupportedVersionException e) {
             throw e;
@@ -85,8 +93,8 @@ public class DefaultToolingImplementationLoader implements ToolingImplementation
         }
     }
 
-    private ClassLoader createImplementationClassLoader(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, File userHomeDir) {
-        ClassPath implementationClasspath = distribution.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir);
+    private ClassLoader createImplementationClassLoader(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, File userHomeDir, BuildCancellationToken cancellationToken) {
+        ClassPath implementationClasspath = distribution.getToolingImplementationClasspath(progressLoggerFactory, userHomeDir, cancellationToken);
         LOGGER.debug("Using tooling provider classpath: {}", implementationClasspath);
         // On IBM JVM 5, ClassLoader.getResources() uses a combination of findResources() and getParent() and traverses the hierarchy rather than just calling getResources()
         // Wrap our real classloader in one that hides the parent.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoader.java
index d16acf4..01fb3cd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoader.java
@@ -16,28 +16,30 @@
 
 package org.gradle.tooling.internal.consumer.loader;
 
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection;
 
+import java.io.Closeable;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-public class SynchronizedToolingImplementationLoader implements ToolingImplementationLoader {
-
-    Lock lock = new ReentrantLock();
+public class SynchronizedToolingImplementationLoader implements ToolingImplementationLoader, Closeable {
+    private final Lock lock = new ReentrantLock();
     private final ToolingImplementationLoader delegate;
 
     public SynchronizedToolingImplementationLoader(ToolingImplementationLoader delegate) {
         this.delegate = delegate;
     }
 
-    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters) {
+    public ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters, BuildCancellationToken cancellationToken) {
         if (lock.tryLock()) {
             try {
-                return delegate.create(distribution, progressLoggerFactory, connectionParameters);
+                return delegate.create(distribution, progressLoggerFactory, connectionParameters, cancellationToken);
             } finally {
                 lock.unlock();
             }
@@ -47,10 +49,19 @@ public class SynchronizedToolingImplementationLoader implements ToolingImplement
         logger.started();
         lock.lock();
         try {
-            return delegate.create(distribution, progressLoggerFactory, connectionParameters);
+            return delegate.create(distribution, progressLoggerFactory, connectionParameters, cancellationToken);
         } finally {
             lock.unlock();
             logger.completed();
         }
     }
+
+    public void close() {
+        lock.lock();
+        try {
+            CompositeStoppable.stoppable(delegate).stop();
+        } finally {
+            lock.unlock();
+        }
+    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/ToolingImplementationLoader.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/ToolingImplementationLoader.java
index dcde1ba..2cea344 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/ToolingImplementationLoader.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/loader/ToolingImplementationLoader.java
@@ -15,11 +15,13 @@
  */
 package org.gradle.tooling.internal.consumer.loader;
 
+import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.tooling.internal.consumer.ConnectionParameters;
 import org.gradle.tooling.internal.consumer.Distribution;
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection;
 
 public interface ToolingImplementationLoader {
-    ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory, ConnectionParameters connectionParameters);
+    ConsumerConnection create(Distribution distribution, ProgressLoggerFactory progressLoggerFactory,
+                              ConnectionParameters connectionParameters, BuildCancellationToken cancellationToken);
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildCancellationTokenAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildCancellationTokenAdapter.java
new file mode 100644
index 0000000..69d3380
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildCancellationTokenAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.parameters;
+
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.tooling.internal.protocol.InternalCancellationToken;
+
+public class BuildCancellationTokenAdapter implements InternalCancellationToken {
+    private final BuildCancellationToken cancellationToken;
+
+    public BuildCancellationTokenAdapter(BuildCancellationToken cancellationToken) {
+        this.cancellationToken = cancellationToken;
+    }
+
+    public boolean isCancellationRequested() {
+        return cancellationToken.isCancellationRequested();
+    }
+
+    public boolean addCallback(Runnable cancellationHandler) {
+        return cancellationToken.addCallback(cancellationHandler);
+    }
+
+    public void removeCallback(Runnable cancellationHandler) {
+        cancellationToken.removeCallback(cancellationHandler);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapter.java
new file mode 100644
index 0000000..94b67a0
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapter.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.consumer.parameters;
+
+import org.gradle.internal.event.ListenerBroadcast;
+import org.gradle.tooling.Failure;
+import org.gradle.tooling.events.test.*;
+import org.gradle.tooling.events.test.internal.*;
+import org.gradle.tooling.internal.consumer.DefaultFailure;
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.tooling.internal.protocol.events.*;
+
+import java.util.*;
+
+/**
+ * Converts progress events sent from the tooling provider to the tooling client to the corresponding event types available on the public Tooling API, and broadcasts the converted events to the
+ * matching progress listeners. This adapter handles all the different incoming progress event types (except the original logging-derived progress listener).
+ */
+class BuildProgressListenerAdapter implements InternalBuildProgressListener {
+
+    private final ListenerBroadcast<TestProgressListener> testProgressListeners = new ListenerBroadcast<TestProgressListener>(TestProgressListener.class);
+    private final Map<Object, TestOperationDescriptor> testDescriptorCache = new HashMap<Object, TestOperationDescriptor>();
+
+    BuildProgressListenerAdapter(List<TestProgressListener> testListeners) {
+        this.testProgressListeners.addAll(testListeners);
+    }
+
+    @Override
+    public List<String> getSubscribedOperations() {
+        return this.testProgressListeners.isEmpty() ? Collections.<String>emptyList() : Collections.singletonList(InternalBuildProgressListener.TEST_EXECUTION);
+    }
+
+    @Override
+    public void onEvent(final Object event) {
+        if (event instanceof InternalTestProgressEvent) {
+            broadcastTestProgressEvent((InternalTestProgressEvent) event);
+        }
+    }
+
+    private void broadcastTestProgressEvent(InternalTestProgressEvent event) {
+        TestProgressEvent testProgressEvent = toTestProgressEvent(event);
+        if (testProgressEvent != null) {
+            testProgressListeners.getSource().statusChanged(testProgressEvent);
+        }
+    }
+
+    private synchronized TestProgressEvent toTestProgressEvent(final InternalTestProgressEvent event) {
+        if (event instanceof InternalTestStartedProgressEvent) {
+            return testStartedEvent((InternalTestStartedProgressEvent) event);
+        } else if (event instanceof InternalTestFinishedProgressEvent) {
+            return testFinishedEvent((InternalTestFinishedProgressEvent) event);
+        } else {
+            return null;
+        }
+    }
+
+    private TestStartEvent testStartedEvent(InternalTestStartedProgressEvent event) {
+        long eventTime = event.getEventTime();
+        String displayName = event.getDisplayName();
+        TestOperationDescriptor testDescriptor = addTestDescriptor(event.getDescriptor());
+        return new DefaultTestStartEvent(eventTime, displayName, testDescriptor);
+    }
+
+    private TestFinishEvent testFinishedEvent(final InternalTestFinishedProgressEvent event) {
+        long eventTime = event.getEventTime();
+        String displayName = event.getDisplayName();
+        TestOperationDescriptor testDescriptor = removeTestDescriptor(event.getDescriptor());
+        TestOperationResult result = toTestResult(event.getResult());
+        return new DefaultTestFinishEvent(eventTime, displayName, testDescriptor, result);
+    }
+
+    private TestOperationDescriptor addTestDescriptor(InternalTestDescriptor testDescriptor) {
+        TestOperationDescriptor cachedTestDescriptor = this.testDescriptorCache.get(testDescriptor.getId());
+        if (cachedTestDescriptor != null) {
+            throw new IllegalStateException(String.format("Operation %s already available.", toString(testDescriptor)));
+        }
+        final TestOperationDescriptor parent = getParentTestDescriptor(testDescriptor);
+        TestOperationDescriptor newTestDescriptor = toTestDescriptor(testDescriptor, parent);
+        testDescriptorCache.put(testDescriptor.getId(), newTestDescriptor);
+        return newTestDescriptor;
+    }
+
+    private TestOperationDescriptor removeTestDescriptor(InternalTestDescriptor testDescriptor) {
+        TestOperationDescriptor cachedTestDescriptor = this.testDescriptorCache.remove(testDescriptor.getId());
+        if (cachedTestDescriptor == null) {
+            throw new IllegalStateException(String.format("Operation %s is not available.", toString(testDescriptor)));
+        }
+        return cachedTestDescriptor;
+    }
+
+    private static TestOperationDescriptor toTestDescriptor(final InternalTestDescriptor testDescriptor, final TestOperationDescriptor parent) {
+        if (testDescriptor instanceof InternalJvmTestDescriptor) {
+            final InternalJvmTestDescriptor jvmTestDescriptor = (InternalJvmTestDescriptor) testDescriptor;
+            return new JvmTestOperationDescriptor() {
+                @Override
+                public String getName() {
+                    return jvmTestDescriptor.getName();
+                }
+
+                @Override
+                public String getDisplayName() {
+                    return jvmTestDescriptor.getDisplayName();
+                }
+
+                @Override
+                public JvmTestKind getJvmTestKind() {
+                    return toJvmTestKind(jvmTestDescriptor);
+                }
+
+                @Override
+                public String getSuiteName() {
+                    return jvmTestDescriptor.getSuiteName();
+                }
+
+                @Override
+                public String getClassName() {
+                    return jvmTestDescriptor.getClassName();
+                }
+
+                @Override
+                public String getMethodName() {
+                    return jvmTestDescriptor.getMethodName();
+                }
+
+                @Override
+                public TestOperationDescriptor getParent() {
+                    return parent;
+                }
+
+                @Override
+                public String toString() {
+                    return getDisplayName();
+                }
+            };
+        } else {
+            return new TestOperationDescriptor() {
+                @Override
+                public String getName() {
+                    return testDescriptor.getName();
+                }
+
+                @Override
+                public String getDisplayName() {
+                    return testDescriptor.getDisplayName();
+                }
+
+                @Override
+                public TestOperationDescriptor getParent() {
+                    return parent;
+                }
+
+                @Override
+                public String toString() {
+                    return getDisplayName();
+                }
+            };
+        }
+    }
+
+    private static JvmTestKind toJvmTestKind(InternalJvmTestDescriptor jvmTestDescriptor) {
+        String jvmTestKind = jvmTestDescriptor.getTestKind();
+        if (InternalJvmTestDescriptor.KIND_SUITE.equals(jvmTestKind)) {
+            return JvmTestKind.SUITE;
+        } else if (InternalJvmTestDescriptor.KIND_ATOMIC.equals(jvmTestKind)) {
+            return JvmTestKind.ATOMIC;
+        } else {
+            return JvmTestKind.UNKNOWN;
+        }
+    }
+
+    private TestOperationResult toTestResult(final InternalTestResult result) {
+        if (result instanceof InternalTestSuccessResult) {
+            return new DefaultTestSuccessResult(result.getStartTime(), result.getEndTime());
+        } else if (result instanceof InternalTestSkippedResult) {
+            return new DefaultTestSkippedResult(result.getStartTime(), result.getEndTime());
+        } else if (result instanceof InternalTestFailureResult) {
+            return new DefaultTestFailureResult(result.getStartTime(), result.getEndTime(), toFailures(result));
+        } else {
+            return null;
+        }
+    }
+
+    private static List<Failure> toFailures(InternalTestResult testResult) {
+        List<Failure> failures = new ArrayList<Failure>();
+        for (InternalFailure origFailure : testResult.getFailures()) {
+            failures.add(toFailure(origFailure));
+        }
+        return failures;
+    }
+
+    private static Failure toFailure(InternalFailure origFailure) {
+        return origFailure == null ? null : new DefaultFailure(
+                origFailure.getMessage(),
+                origFailure.getDescription(),
+                toFailure(origFailure.getCauses()));
+    }
+
+    private static List<Failure> toFailure(List<? extends InternalFailure> causes) {
+        List<Failure> result = new ArrayList<Failure>();
+        for (InternalFailure cause : causes) {
+            result.add(toFailure(cause));
+        }
+        return result;
+    }
+
+    private TestOperationDescriptor getParentTestDescriptor(InternalTestDescriptor testDescriptor) {
+        Object parentId = testDescriptor.getParentId();
+        if (parentId == null) {
+            return null;
+        } else {
+            TestOperationDescriptor parentTestDescriptor = testDescriptorCache.get(parentId);
+            if (parentTestDescriptor == null) {
+                throw new IllegalStateException(String.format("Parent test descriptor with id %s not available for %s.", parentId, toString(testDescriptor)));
+            } else {
+                return parentTestDescriptor;
+            }
+        }
+    }
+
+    private static String toString(InternalTestDescriptor testDescriptor) {
+        if (testDescriptor instanceof InternalJvmTestDescriptor) {
+            return String.format("TestOperationDescriptor[id(%s), name(%s), className(%s), parent(%s)]",
+                    testDescriptor.getId(), testDescriptor.getName(), ((InternalJvmTestDescriptor) testDescriptor).getClassName(), testDescriptor.getParentId());
+        } else {
+            return String.format("TestOperationDescriptor[id(%s), name(%s), parent(%s)]", testDescriptor.getId(), testDescriptor.getName(), testDescriptor.getParentId());
+        }
+    }
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParameters.java
index 06bb2a8..c223b76 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParameters.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParameters.java
@@ -13,22 +13,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.gradle.tooling.internal.consumer.parameters;
 
+import com.google.common.collect.Lists;
+import org.gradle.api.GradleException;
+import org.gradle.initialization.BuildCancellationToken;
+import org.gradle.tooling.CancellationToken;
 import org.gradle.tooling.ProgressListener;
+import org.gradle.tooling.events.test.TestProgressListener;
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter;
+import org.gradle.tooling.internal.consumer.CancellationTokenInternal;
 import org.gradle.tooling.internal.consumer.ConnectionParameters;
-import org.gradle.tooling.internal.protocol.BuildOperationParametersVersion1;
-import org.gradle.tooling.internal.protocol.BuildParameters;
-import org.gradle.tooling.internal.protocol.BuildParametersVersion1;
-import org.gradle.tooling.internal.protocol.InternalLaunchable;
-import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
+import org.gradle.tooling.internal.gradle.TaskListingLaunchable;
+import org.gradle.tooling.internal.protocol.*;
+import org.gradle.tooling.model.Launchable;
+import org.gradle.tooling.model.Task;
 
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 public class ConsumerOperationParameters implements BuildOperationParametersVersion1, BuildParametersVersion1, BuildParameters {
@@ -38,10 +42,13 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
     }
 
     public static class Builder {
-        private final ProgressListenerAdapter progressListener = new ProgressListenerAdapter();
+        private final List<ProgressListener> progressListeners = new ArrayList<ProgressListener>();
+        private final List<TestProgressListener> testProgressListeners = new ArrayList<TestProgressListener>();
+        private CancellationToken cancellationToken;
         private ConnectionParameters parameters;
         private OutputStream stdout;
         private OutputStream stderr;
+        private Boolean colorOutput;
         private InputStream stdin;
         private File javaHome;
         private List<String> jvmArguments;
@@ -67,6 +74,11 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
             return this;
         }
 
+        public Builder setColorOutput(Boolean colorOutput) {
+            this.colorOutput = colorOutput;
+            return this;
+        }
+
         public Builder setStdin(InputStream stdin) {
             this.stdin = stdin;
             return this;
@@ -83,7 +95,7 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
             return this;
         }
 
-        public Builder setArguments(String [] arguments) {
+        public Builder setArguments(String[] arguments) {
             this.arguments = rationalizeInput(arguments);
             return this;
         }
@@ -93,27 +105,62 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
             return this;
         }
 
-        public Builder setLaunchables(List<InternalLaunchable> launchables) {
-            this.launchables = launchables;
+        public Builder setLaunchables(Iterable<? extends Launchable> launchables) {
+            Set<String> taskPaths = new LinkedHashSet<String>();
+            List<InternalLaunchable> launchablesParams = Lists.newArrayList();
+            for (Launchable launchable : launchables) {
+                Object original = new ProtocolToModelAdapter().unpack(launchable);
+                if (original instanceof InternalLaunchable) {
+                    // A launchable created by the provider - just hand it back
+                    launchablesParams.add((InternalLaunchable) original);
+                } else if (original instanceof TaskListingLaunchable) {
+                    // A launchable synthesized by the consumer - unpack it into a set of task names
+                    taskPaths.addAll(((TaskListingLaunchable) original).getTaskNames());
+                } else if (launchable instanceof Task) {
+                    // A task created by a provider that does not understand launchables
+                    taskPaths.add(((Task) launchable).getPath());
+                } else {
+                    throw new GradleException("Only Task or TaskSelector instances are supported: "
+                            + (launchable != null ? launchable.getClass() : "null"));
+                }
+            }
+            this.launchables = launchablesParams;
+            tasks = Lists.newArrayList(taskPaths);
             return this;
         }
 
         public void addProgressListener(ProgressListener listener) {
-            progressListener.add(listener);
+            progressListeners.add(listener);
+        }
+
+        public void addTestProgressListener(TestProgressListener listener) {
+            testProgressListeners.add(listener);
+        }
+
+        public void setCancellationToken(CancellationToken cancellationToken) {
+            this.cancellationToken = cancellationToken;
         }
 
         public ConsumerOperationParameters build() {
-            return new ConsumerOperationParameters(parameters, stdout, stderr, stdin,
-                    javaHome, jvmArguments, arguments, tasks, launchables, progressListener);
+            // create the listener adapters right when the ConsumerOperationParameters are instantiated but no earlier,
+            // this ensures that when multiple requests are issued that are built from the same builder, such requests do not share any state kept in the listener adapters
+            // e.g. if the listener adapters do per-request caching, such caching must not leak between different requests built from the same builder
+            ProgressListenerAdapter progressListenerAdapter = new ProgressListenerAdapter(this.progressListeners);
+            BuildProgressListenerAdapter testProgressListenerAdapter = new BuildProgressListenerAdapter(this.testProgressListeners);
+            return new ConsumerOperationParameters(parameters, stdout, stderr, colorOutput, stdin, javaHome, jvmArguments, arguments, tasks, launchables,
+                    progressListenerAdapter, testProgressListenerAdapter, cancellationToken);
         }
     }
 
     private final ProgressListenerAdapter progressListener;
+    private final BuildProgressListenerAdapter buildProgressListener;
+    private final CancellationToken cancellationToken;
     private final ConnectionParameters parameters;
     private final long startTime = System.currentTimeMillis();
 
     private final OutputStream stdout;
     private final OutputStream stderr;
+    private final Boolean colorOutput;
     private final InputStream stdin;
 
     private final File javaHome;
@@ -122,19 +169,22 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
     private final List<String> tasks;
     private final List<InternalLaunchable> launchables;
 
-    private ConsumerOperationParameters(ConnectionParameters parameters, OutputStream stdout, OutputStream stderr, InputStream stdin,
-                                        File javaHome, List<String> jvmArguments, List<String> arguments, List<String> tasks,
-                                        List<InternalLaunchable> launchables, ProgressListenerAdapter listener) {
+    private ConsumerOperationParameters(ConnectionParameters parameters, OutputStream stdout, OutputStream stderr, Boolean colorOutput, InputStream stdin,
+                                        File javaHome, List<String> jvmArguments, List<String> arguments, List<String> tasks, List<InternalLaunchable> launchables,
+                                        ProgressListenerAdapter progressListener, BuildProgressListenerAdapter buildProgressListener, CancellationToken cancellationToken) {
         this.parameters = parameters;
         this.stdout = stdout;
         this.stderr = stderr;
+        this.colorOutput = colorOutput;
         this.stdin = stdin;
         this.javaHome = javaHome;
         this.jvmArguments = jvmArguments;
         this.arguments = arguments;
         this.tasks = tasks;
         this.launchables = launchables;
-        this.progressListener = listener;
+        this.progressListener = progressListener;
+        this.buildProgressListener = buildProgressListener;
+        this.cancellationToken = cancellationToken;
     }
 
     private static List<String> rationalizeInput(String[] arguments) {
@@ -182,6 +232,10 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
         return parameters.getDaemonMaxIdleTimeValue();
     }
 
+    public File getDaemonBaseDir() {
+        return parameters.getDaemonBaseDir();
+    }
+
     public OutputStream getStandardOutput() {
         return stdout;
     }
@@ -190,8 +244,8 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
         return stderr;
     }
 
-    public ProgressListenerVersion1 getProgressListener() {
-        return progressListener;
+    public Boolean isColorOutput() {
+        return colorOutput;
     }
 
     public InputStream getStandardInput() {
@@ -217,4 +271,16 @@ public class ConsumerOperationParameters implements BuildOperationParametersVers
     public List<InternalLaunchable> getLaunchables() {
         return launchables;
     }
+
+    public ProgressListenerVersion1 getProgressListener() {
+        return progressListener;
+    }
+
+    public InternalBuildProgressListener getBuildProgressListener() {
+        return buildProgressListener;
+    }
+
+    public BuildCancellationToken getCancellationToken() {
+        return ((CancellationTokenInternal) cancellationToken).getToken();
+    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapter.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapter.java
index 900a4bd..e57a4a8 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapter.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapter.java
@@ -15,17 +15,22 @@
  */
 package org.gradle.tooling.internal.consumer.parameters;
 
-import org.gradle.listener.ListenerBroadcast;
+import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.tooling.ProgressEvent;
 import org.gradle.tooling.ProgressListener;
 import org.gradle.tooling.internal.protocol.ProgressListenerVersion1;
 
 import java.util.LinkedList;
+import java.util.List;
 
 class ProgressListenerAdapter implements ProgressListenerVersion1 {
     private final ListenerBroadcast<ProgressListener> listeners = new ListenerBroadcast<ProgressListener>(ProgressListener.class);
     private final LinkedList<String> stack = new LinkedList<String>();
 
+    ProgressListenerAdapter(List<ProgressListener> listeners) {
+        this.listeners.addAll(listeners);
+    }
+
     public void onOperationStart(final String description) {
         stack.addFirst(description == null ? "" : description);
         fireChangeEvent();
@@ -44,8 +49,4 @@ class ProgressListenerAdapter implements ProgressListenerVersion1 {
             }
         });
     }
-
-    public void add(ProgressListener listener) {
-        listeners.add(listener);
-    }
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/versioning/VersionDetails.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/versioning/VersionDetails.java
index 89fac8c..880ec28 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/versioning/VersionDetails.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/versioning/VersionDetails.java
@@ -35,11 +35,11 @@ public abstract class VersionDetails {
         return false;
     }
 
-    public boolean supportsGradleProjectModel() {
+    public boolean supportsTaskDisplayName() {
         return false;
     }
 
-    public boolean supportsTaskDisplayName() {
+    public boolean supportsCancellation() {
         return false;
     }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/BasicGradleTaskSelector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/BasicGradleTaskSelector.java
deleted file mode 100644
index 771232e..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/BasicGradleTaskSelector.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.gradle;
-
-import org.gradle.api.Nullable;
-import org.gradle.tooling.model.TaskSelector;
-
-import java.util.SortedSet;
-
-/**
- * Data used for {@link org.gradle.tooling.model.TaskSelector} when created in consumer.
- */
-public class BasicGradleTaskSelector implements TaskSelector, TaskListingLaunchable {
-    private String name;
-    private String displayName;
-    private String description;
-    private SortedSet<String> tasks;
-
-    public String getName() {
-        return name;
-    }
-
-    public BasicGradleTaskSelector setName(String name) {
-        this.name = name;
-        return this;
-    }
-
-    @Nullable
-    public String getDescription() {
-        return description;
-    }
-
-    public BasicGradleTaskSelector setDescription(String description) {
-        this.description = description;
-        return this;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public BasicGradleTaskSelector setDisplayName(String displayName) {
-        this.displayName = displayName;
-        return this;
-    }
-
-    public SortedSet<String> getTaskNames() {
-        return tasks;
-    }
-
-    public BasicGradleTaskSelector setTaskNames(SortedSet<String> tasks) {
-        this.tasks = tasks;
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return "BasicGradleTaskSelector{"
-                + "name='" + name + "' "
-                + "description='" + description + "'}";
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTask.java
new file mode 100644
index 0000000..adeb76f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTask.java
@@ -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.tooling.internal.gradle;
+
+import com.google.common.collect.Sets;
+import org.gradle.tooling.internal.consumer.converters.TaskNameComparator;
+
+import java.util.SortedSet;
+
+/**
+ * A consumer-side implementation of {@link org.gradle.tooling.model.Task}.
+ */
+public class ConsumerProvidedTask implements TaskListingLaunchable {
+
+    private String path;
+    private String name;
+    private String description;
+    private String displayName;
+
+    public String getPath() {
+        return path;
+    }
+
+    public ConsumerProvidedTask setPath(String path) {
+        this.path = path;
+        return this;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ConsumerProvidedTask setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public ConsumerProvidedTask setDisplayName(String displayName) {
+        this.displayName = displayName;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public ConsumerProvidedTask setDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public SortedSet<String> getTaskNames() {
+        // TODO use comparator
+        SortedSet result = Sets.newTreeSet(new TaskNameComparator());
+        result.add(getPath());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "GradleTask{"
+                + "name='" + name + '\''
+                + " path='" + path + '\''
+                + '}';
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTaskSelector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTaskSelector.java
new file mode 100644
index 0000000..54616da
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/ConsumerProvidedTaskSelector.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.gradle;
+
+import org.gradle.api.Nullable;
+
+import java.util.SortedSet;
+
+/**
+ * A consumer-side implementation of {@link org.gradle.tooling.model.TaskSelector}
+ */
+public class ConsumerProvidedTaskSelector implements TaskListingLaunchable {
+    private String name;
+    private String displayName;
+    private String description;
+    private SortedSet<String> tasks;
+
+    public String getName() {
+        return name;
+    }
+
+    public ConsumerProvidedTaskSelector setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    @Nullable
+    public String getDescription() {
+        return description;
+    }
+
+    public ConsumerProvidedTaskSelector setDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public ConsumerProvidedTaskSelector setDisplayName(String displayName) {
+        this.displayName = displayName;
+        return this;
+    }
+
+    public SortedSet<String> getTaskNames() {
+        return tasks;
+    }
+
+    public ConsumerProvidedTaskSelector setTaskNames(SortedSet<String> tasks) {
+        this.tasks = tasks;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "ConsumerProvidedTaskSelector{"
+                + "name='" + name + "' "
+                + "description='" + description + "'}";
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultBuildInvocations.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultBuildInvocations.java
deleted file mode 100644
index 0c84ba0..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultBuildInvocations.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.internal.gradle;
-
-
-import org.gradle.tooling.model.TaskSelector;
-
-import java.io.Serializable;
-import java.util.List;
-
-// used with LaunchingGradleTask from provider or with DefaultGradleTask from adapting consumer
-public class DefaultBuildInvocations<T> implements Serializable {
-    private List<? extends TaskSelector> selectors;
-    private List<T> tasks;
-
-    public DefaultBuildInvocations<T> setSelectors(List<? extends TaskSelector> selectors) {
-        this.selectors = selectors;
-        return this;
-    }
-
-    public List<? extends TaskSelector> getTaskSelectors() {
-        return selectors;
-    }
-
-    public DefaultBuildInvocations<T> setTasks(List<T> tasks) {
-        this.tasks = tasks;
-        return this;
-    }
-
-    public List<T> getTasks() {
-        return tasks;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultConvertedGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultConvertedGradleProject.java
deleted file mode 100644
index 9bf62e4..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultConvertedGradleProject.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.gradle;
-
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-public class DefaultConvertedGradleProject extends PartialGradleProject {
-    private List<DefaultGradleTask> tasks = new LinkedList<DefaultGradleTask>();
-
-    @Override
-    public DefaultConvertedGradleProject setName(String name) {
-        super.setName(name);
-        return this;
-    }
-
-    @Override
-    public DefaultConvertedGradleProject setPath(String path) {
-        super.setPath(path);
-        return this;
-    }
-
-    @Override
-    public DefaultConvertedGradleProject setDescription(String description) {
-        super.setDescription(description);
-        return this;
-    }
-
-    @Override
-    public DefaultConvertedGradleProject setChildren(List<? extends PartialGradleProject> children) {
-        super.setChildren(children);
-        return this;
-    }
-
-    public Collection<DefaultGradleTask> getTasks() {
-        return tasks;
-    }
-
-    public DefaultConvertedGradleProject setTasks(List<DefaultGradleTask> tasks) {
-        this.tasks = tasks;
-        return this;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProject.java
index d28f624..512b96b 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProject.java
@@ -16,16 +16,16 @@
 
 package org.gradle.tooling.internal.gradle;
 
-import org.gradle.tooling.internal.protocol.InternalGradleProject;
-
 import java.io.File;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 
-public class DefaultGradleProject<T> extends PartialGradleProject implements InternalGradleProject, Serializable, GradleProjectIdentity {
+public class DefaultGradleProject<T> extends PartialGradleProject implements Serializable, GradleProjectIdentity {
     private DefaultGradleScript buildScript = new DefaultGradleScript();
+    private File buildDirectory;
+    private File projectDirectory;
     private List<T> tasks = new LinkedList<T>();
 
     @Override
@@ -61,12 +61,26 @@ public class DefaultGradleProject<T> extends PartialGradleProject implements Int
         return this;
     }
 
-    public DefaultGradleScript getBuildScript() {
-        return buildScript;
+    public File getBuildDirectory() {
+        return buildDirectory;
+    }
+
+    public DefaultGradleProject<T> setBuildDirectory(File buildDirectory) {
+        this.buildDirectory = buildDirectory;
+        return this;
     }
 
     public File getProjectDirectory() {
-        throw new RuntimeException("ProjectVersion3 methods are deprecated.");
+        return projectDirectory;
+    }
+
+    public DefaultGradleProject<T> setProjectDirectory(File projectDirectory) {
+        this.projectDirectory = projectDirectory;
+        return this;
+    }
+
+    public DefaultGradleScript getBuildScript() {
+        return buildScript;
     }
 
     @Override
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProjectTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProjectTask.java
deleted file mode 100644
index b106d79..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleProjectTask.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2014 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.gradle;
-
-public class DefaultGradleProjectTask extends DefaultGradleTask {
-    private PartialGradleProject project;
-
-    public PartialGradleProject getProject() {
-        return project;
-    }
-
-    public DefaultGradleProjectTask setProject(PartialGradleProject project) {
-        this.project = project;
-        return this;
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleTask.java
deleted file mode 100644
index 3e89809..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/DefaultGradleTask.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.tooling.internal.gradle;
-
-import com.google.common.collect.Sets;
-import org.gradle.tooling.internal.consumer.converters.TaskNameComparator;
-
-import java.util.SortedSet;
-
-public class DefaultGradleTask implements TaskListingLaunchable {
-
-    private String path;
-    private String name;
-    private String description;
-    private String displayName;
-
-    public String getPath() {
-        return path;
-    }
-
-    public DefaultGradleTask setPath(String path) {
-        this.path = path;
-        return this;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public DefaultGradleTask setName(String name) {
-        this.name = name;
-        return this;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public DefaultGradleTask setDisplayName(String displayName) {
-        this.displayName = displayName;
-        return this;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public DefaultGradleTask setDescription(String description) {
-        this.description = description;
-        return this;
-    }
-
-    public SortedSet<String> getTaskNames() {
-        // TODO use comparator
-        SortedSet result = Sets.newTreeSet(new TaskNameComparator());
-        result.add(getPath());
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "GradleTask{"
-                + "name='" + name + '\''
-                + " path='" + path + '\''
-                + '}';
-    }
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/PartialGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/PartialGradleProject.java
index 29fe7f6..4a53fc8 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/PartialGradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/gradle/PartialGradleProject.java
@@ -21,6 +21,9 @@ import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 
+/**
+ * A partial implementation of {@link org.gradle.tooling.model.GradleProject}.
+ */
 public class PartialGradleProject implements Serializable {
     private String name;
     private String description;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildActionRunner.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildActionRunner.java
index f6bdcb6..c0af976 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildActionRunner.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildActionRunner.java
@@ -22,12 +22,12 @@ package org.gradle.tooling.internal.protocol;
  * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
  *
  * <p>Consumer compatibility: This interface is used by all consumer versions from 1.2-rc-1 to 1.5. It is also used by later consumers when the provider does not
- * implement {@link ModelBuilder}.
+ * implement newer interfaces.
  * </p>
  * <p>Provider compatibility: This interface is implemented by all provider versions from 1.2-rc-1.</p>
  *
  * @since 1.2-rc-1
- * @deprecated 1.6-rc-1. Use {@link ModelBuilder} instead.
+ * @deprecated 1.6-rc-1. Use {@link InternalCancellableConnection} instead.
  * @see ConnectionVersion4
  */
 @Deprecated
@@ -42,7 +42,7 @@ public interface BuildActionRunner extends InternalProtocolInterface {
      * @throws UnsupportedOperationException When the given model type is not supported.
      * @throws IllegalStateException When this connection has been stopped.
      * @since 1.2-rc-1
-     * @deprecated 1.6-rc-1. Use {@link ModelBuilder#getModel(ModelIdentifier, BuildParameters)} instead.
+     * @deprecated 1.6-rc-1. Use {@link InternalCancellableConnection#getModel(ModelIdentifier, InternalCancellationToken, BuildParameters)} instead.
      */
     @Deprecated
     <T> BuildResult<T> run(Class<T> type, BuildParameters operationParameters) throws UnsupportedOperationException, IllegalStateException;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java
index 5f44c14..fcaea93 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/BuildableProjectVersion1.java
@@ -18,6 +18,6 @@ package org.gradle.tooling.internal.protocol;
 /**
  * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
  */
-public interface BuildableProjectVersion1 extends ProjectVersion3 {
+public interface BuildableProjectVersion1 {
     Iterable<? extends TaskVersion1> getTasks();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java
index 6667a2d..8ea35ec 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ConnectionVersion4.java
@@ -21,10 +21,12 @@ package org.gradle.tooling.internal.protocol;
  * <p>The following constraints apply to implementations:
  * <ul>
  * <li>Implementations must be thread-safe.
- * <li>Implementations should implement {@link ModelBuilder}. This is used by all consumer versions from 1.6-rc-1.
+ * <li>Implementations should implement {@link InternalCancellableConnection}. This is used by all consumer versions from 2.1-rc-1.
  * <li>Implementations should implement {@link InternalBuildActionExecutor}. This is used by all consumer versions from 1.8-rc-1.
  * <li>Implementations should implement {@link ConfigurableConnection}. This is used by all consumer versions from 1.2-rc-1.
+ * <li>Implementations should implement {@link StoppableConnection}. This is used by all consumer versions from 2.2-rc-1.
  * <li>Implementations should provide a zero-args constructor. This is used by all consumer versions from 1.0-milestone-3.
+ * <li>For backwards compatibility, implementations should implement {@link ModelBuilder}. This is used by all consumer versions from 1.6-rc-1 to 2.0.
  * <li>For backwards compatibility, implementations should implement {@link BuildActionRunner}. This is used by consumer versions from 1.2-rc-1 to 1.5.
  * <li>For backwards compatibility, implementations should implement {@link InternalConnection}. This is used by consumer versions from 1.0-milestone-8 to 1.1.
  * <li>For backwards compatibility, implementations should provide a {@code void configureLogging(boolean verboseLogging)} method. This is used by consumer versions
@@ -46,7 +48,9 @@ public interface ConnectionVersion4 {
      * <p>Provider compatibility: This method is implemented by all provider versions from 1.0-milestone-3.</p>
      *
      * @since 1.0-milestone-3
+     * @deprecated 2.2-rc-1 Use {@link StoppableConnection} instead.
      */
+    @Deprecated
     void stop();
 
     /**
@@ -64,14 +68,14 @@ public interface ConnectionVersion4 {
      * <p>Fetches a snapshot of the model for the project.
      *
      * <p>Consumer compatibility: This method is used by all consumer versions from 1.0-milestone-3 to 1.0-milestone-7. It is also used by later consumers when the provider
-     * does not implement {@link InternalConnection} or {@link BuildActionRunner}
+     * does not implement newer interfaces.
      * </p>
-     * <p>Provider compatibility: This method is implemented by all provider versions from 1.0-milestone-3.</p>
+     * <p>Provider compatibility: This method is implemented by all provider versions from 1.0-milestone-3. Versions 2.0 and later fail with a 'no longer supported' exception.</p>
      *
      * @throws UnsupportedOperationException When the given model type is not supported.
      * @throws IllegalStateException When this connection has been stopped.
      * @since 1.0-milestone-3
-     * @deprecated 1.0-milestone-8. Use {@link ModelBuilder#getModel(ModelIdentifier, BuildParameters)} instead.
+     * @deprecated 1.0-milestone-8. Use {@link InternalCancellableConnection#getModel(ModelIdentifier, InternalCancellationToken, BuildParameters)} instead.
      */
     @Deprecated
     ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters) throws UnsupportedOperationException, IllegalStateException;
@@ -80,14 +84,14 @@ public interface ConnectionVersion4 {
      * <p>Executes a build.
      *
      * <p>Consumer compatibility: This method is used by all consumer versions from 1.0-milestone-3 to 1.1. It is also used by later consumers when the provider
-     * does not implement {@link BuildActionRunner}
+     * does not implement newer interfaces.
      * </p>
-     * <p>Provider compatibility: This method is implemented by all provider versions from 1.0-milestone-3.</p>
+     * <p>Provider compatibility: This method is implemented by all provider versions from 1.0-milestone-3. Versions 2.0 and later fail with a 'no longer supported' exception.</p>
      *
      * @param buildParameters The parameters for the build.
      * @throws IllegalStateException When this connection has been stopped.
      * @since 1.0-milestone-3
-     * @deprecated 1.2-rc-1. Use {@link ModelBuilder#getModel(ModelIdentifier, BuildParameters)} instead.
+     * @deprecated 1.2-rc-1. Use {@link InternalCancellableConnection#getModel(ModelIdentifier, InternalCancellationToken, BuildParameters)} instead.
      */
     @Deprecated
     void executeBuild(BuildParametersVersion1 buildParameters, BuildOperationParametersVersion1 operationParameters) throws IllegalStateException;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java
index 84fea62..afbecf5 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/HierarchicalProjectVersion1.java
@@ -18,7 +18,7 @@ package org.gradle.tooling.internal.protocol;
 /**
  * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
  */
-public interface HierarchicalProjectVersion1 extends ProjectVersion3 {
+public interface HierarchicalProjectVersion1 {
     HierarchicalProjectVersion1 getParent();
 
     Iterable<? extends HierarchicalProjectVersion1> getChildren();
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBasicIdeaProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBasicIdeaProject.java
index 3f39c9c..280adaf 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBasicIdeaProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBasicIdeaProject.java
@@ -21,4 +21,4 @@ package org.gradle.tooling.internal.protocol;
  *
  * @since 1.0-milestone-5
  */
-public interface InternalBasicIdeaProject extends ProjectVersion3, InternalProtocolInterface {}
+public interface InternalBasicIdeaProject extends InternalProtocolInterface {}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildActionExecutor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildActionExecutor.java
index 7064361..773f7e4 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildActionExecutor.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildActionExecutor.java
@@ -23,25 +23,29 @@ import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildA
  *
  * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
  *
- * <p>Consumer compatibility: This interface is used by all consumer versions from 1.8-rc-1.</p>
+ * <p>Consumer compatibility: This interface is used by all consumer versions from 1.8-rc-1 to 2.0.</p>
  * <p>Provider compatibility: This interface is implemented by all provider versions from 1.8-rc-1.</p>
  *
  * @since 1.8-rc-1
+ * @deprecated 2.1-rc-1 Use {@link InternalCancellableConnection} instead.
  * @see ConnectionVersion4
  */
+ at Deprecated
 public interface InternalBuildActionExecutor extends InternalProtocolInterface {
     /**
      * Performs some action against a build and returns the result.
      *
-     * <p>Consumer compatibility: This method is used by all consumer versions from 1.8-rc-1.</p>
+     * <p>Consumer compatibility: This method is used by all consumer versions from 1.8-rc-1 to 2.0.</p>
      * <p>Provider compatibility: This method is implemented by all provider versions from 1.8-rc-1.</p>
      *
      * @throws BuildExceptionVersion1 On build failure.
      * @throws InternalUnsupportedBuildArgumentException When the specified command-line options are not supported.
      * @throws InternalBuildActionFailureException When the action fails with an exception.
      * @throws IllegalStateException When this connection has been stopped.
+     * @deprecated 2.1-rc-1 Use {@link InternalCancellableConnection#run(InternalBuildAction, InternalCancellationToken, BuildParameters)} instead.
      * @since 1.8-rc-1
      */
+    @Deprecated
     <T> BuildResult<T> run(InternalBuildAction<T> action,
                            BuildParameters operationParameters) throws
             BuildExceptionVersion1,
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildCancelledException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildCancelledException.java
new file mode 100644
index 0000000..85bba39
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildCancelledException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * A wrapper thrown when a build action throws an exception to signal cancellation. The failure will be attached as the cause of this exception.
+ *
+ * DO NOT CHANGE THIS CLASS. It is part of the cross-version protocol.
+ *
+ * @since 2.2-rc-1
+ */
+public class InternalBuildCancelledException extends RuntimeException {
+    public InternalBuildCancelledException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildProgressListener.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildProgressListener.java
new file mode 100644
index 0000000..da37500
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalBuildProgressListener.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.util.List;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ */
+public interface InternalBuildProgressListener {
+
+    /**
+     * The constant for the test execution operations.
+     */
+    String TEST_EXECUTION = "TEST_EXECUTION";
+
+    /**
+     * Invoked when a progress event happens in the build being run, and one or more listeners for the given event type have been registered.
+     *
+     * The event types implemented in Gradle 2.4 are:
+     *
+     * <ul>
+     *     <li>{@link org.gradle.tooling.internal.protocol.events.InternalTestProgressEvent}</li>
+     * </ul>
+     *
+     * @param event The issued progress event
+     */
+    void onEvent(Object event);
+
+    /**
+     * Returns the type of operations that the listener wants to subscribe to.
+     *
+     * @return the type of operations to be notified about
+     */
+    List<String> getSubscribedOperations();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellableConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellableConnection.java
new file mode 100644
index 0000000..dc72ebe
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellableConnection.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol;
+
+import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException;
+
+/**
+ * Mixed into a provider connection, to allow tooling models to be requested by the client
+ * and to run client-provided actions (including builds) with cancellation support.
+ *
+ * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
+ *
+ * <p>Consumer compatibility: This interface is used by all consumer versions from 2.1-rc-1.</p>
+ * <p>Provider compatibility: This interface is implemented by all provider versions from 2.1-rc-1.</p>
+ *
+ * @since 2.1-rc-1
+ * @see org.gradle.tooling.internal.protocol.ConnectionVersion4
+ */
+public interface InternalCancellableConnection extends InternalProtocolInterface {
+    /**
+     * Performs some action against a build and returns the requested model.
+     *
+     * <p>Consumer compatibility: This method is used by all consumer versions from 2.1-rc-1.</p>
+     * <p>Provider compatibility: This method is implemented by all provider versions from 2.1-rc-1.</p>
+     *
+     * @param modelIdentifier The identifier of the model to build.
+     * @param cancellationToken The token to propagate cancellation.
+     * @throws BuildExceptionVersion1 On build failure.
+     * @throws InternalUnsupportedModelException When the requested model is not supported.
+     * @throws InternalUnsupportedBuildArgumentException When the specified command-line options are not supported.
+     * @throws InternalBuildCancelledException When the operation was cancelled before it could complete.
+     * @throws IllegalStateException When this connection has been stopped.
+     * @since 2.1-rc-1
+     */
+    BuildResult<?> getModel(ModelIdentifier modelIdentifier, InternalCancellationToken cancellationToken,
+                            BuildParameters operationParameters) throws
+            BuildExceptionVersion1,
+            InternalUnsupportedModelException,
+            InternalUnsupportedBuildArgumentException,
+            InternalBuildCancelledException,
+            IllegalStateException;
+
+    /**
+     * Performs some action against a build and returns the result.
+     *
+     * <p>Consumer compatibility: This method is used by all consumer versions from 2.1-rc-1.</p>
+     * <p>Provider compatibility: This method is implemented by all provider versions from 2.1-rc-1.</p>
+     *
+     * @throws BuildExceptionVersion1 On build failure.
+     * @throws InternalUnsupportedBuildArgumentException When the specified command-line options are not supported.
+     * @throws InternalBuildActionFailureException When the action fails with an exception.
+     * @throws InternalBuildCancelledException When the operation was cancelled before it could complete.
+     * @throws IllegalStateException When this connection has been stopped.
+     * @since 2.1-rc-1
+     */
+    <T> BuildResult<T> run(InternalBuildAction<T> action,
+                           InternalCancellationToken cancellationToken,
+                           BuildParameters operationParameters) throws
+            BuildExceptionVersion1,
+            InternalUnsupportedBuildArgumentException,
+            InternalBuildActionFailureException,
+            InternalBuildCancelledException,
+            IllegalStateException;
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellationToken.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellationToken.java
new file mode 100644
index 0000000..de05fd0
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalCancellationToken.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * Cancellation token passed from consumer to provider to propagate cancellation events.
+ * @since 2.1-rc-1
+ */
+public interface InternalCancellationToken {
+    boolean isCancellationRequested();
+
+    /**
+     * Adds a callback that will be executed when cancel event is triggered.
+     * It can be run synchronously if the token is already cancelled.
+     *
+     * @param cancellationHandler
+     * @return current state of cancellation request before callback was added.
+     */
+    boolean addCallback(Runnable cancellationHandler);
+
+    /**
+     * Removes a callback from a set of handlers called when cancel is requested.
+     *
+     * @param cancellationHandler removed callback.
+     */
+    void removeCallback(Runnable cancellationHandler);
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalConnection.java
index a538431..581c866 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalConnection.java
@@ -20,11 +20,11 @@ package org.gradle.tooling.internal.protocol;
  * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
  *
  * <p>Consumer compatibility: This interface is used by all consumer versions from 1.0-milestone-8 to 1.1. It is also used by later consumers when the
- * provider does not implement {@link BuildActionRunner} or {@link ModelBuilder}.</p>
+ * provider does not implement newer interfaces.</p>
  * <p>Provider compatibility: This interface is implemented by all provider versions from 1.0-milestone-8.</p>
  *
  * @since 1.0-milestone-8
- * @deprecated 1.2-rc-1. Use {@link BuildActionRunner} instead.
+ * @deprecated 1.2-rc-1. Use {@link InternalCancellableConnection} instead.
  * @see ConnectionVersion4
  */
 @Deprecated
@@ -36,13 +36,13 @@ public interface InternalConnection extends ConnectionVersion4, InternalProtocol
      * The other method on the interface, e.g. {@link #getModel(Class, BuildOperationParametersVersion1)} should be considered deprecated
      *
      * <p>Consumer compatibility: This method is used by all consumer versions from 1.0-milestone-8 to 1.1. It is also used by later consumers when the
-     * provider does not implement {@link BuildActionRunner} or {@link ModelBuilder}.</p>
-     * <p>Provider compatibility: This interface is implemented by all provider versions from 1.0-milestone-8.</p>
+     * provider does not implement newer interfaces.</p>
+     * <p>Provider compatibility: This interface is implemented by all provider versions from 1.0-milestone-8. Versions 2.0 and later fail with a 'no longer implemented' exception.</p>
      *
      * @throws UnsupportedOperationException When the given model type is not supported.
      * @throws IllegalStateException When this connection has been stopped.
      * @since 1.0-milestone-8
-     * @deprecated 1.2-rc-1 Use {@link ModelBuilder#getModel(ModelIdentifier, BuildParameters)} instead.
+     * @deprecated 1.2-rc-1 Use {@link InternalCancellableConnection#getModel(ModelIdentifier, InternalCancellationToken, BuildParameters)} instead.
      */
     @Deprecated
     <T> T getTheModel(Class<T> type, BuildOperationParametersVersion1 operationParameters) throws UnsupportedOperationException, IllegalStateException;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalFailure.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalFailure.java
new file mode 100644
index 0000000..3b6482c
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalFailure.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol;
+
+import java.util.List;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalFailure {
+
+    /**
+     * The message of the failure, if any.
+     *
+     * @return the failure message, can be null
+     */
+    String getMessage();
+
+    /**
+     * The description of the failure, if any.
+     *
+     * @return the failure description, can be null
+     */
+    String getDescription();
+
+    /**
+     * The cause of the failure, if any, which is again a failure.
+     *
+     * @return the cause of the failure, can be null
+     */
+    List<? extends InternalFailure> getCauses();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalGradleProject.java
index e903a8d..be88b76 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalGradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalGradleProject.java
@@ -21,4 +21,4 @@ package org.gradle.tooling.internal.protocol;
  *
  * @since 1.0-milestone-5
  */
-public interface InternalGradleProject extends ProjectVersion3, InternalProtocolInterface {}
+public interface InternalGradleProject extends InternalProtocolInterface {}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalIdeaProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalIdeaProject.java
index 6457f6c..1b357fd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalIdeaProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/InternalIdeaProject.java
@@ -21,5 +21,5 @@ package org.gradle.tooling.internal.protocol;
  *
  * @since 1.0-milestone-5
  */
-public interface InternalIdeaProject extends ProjectVersion3, InternalProtocolInterface {
+public interface InternalIdeaProject extends InternalProtocolInterface {
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ModelBuilder.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ModelBuilder.java
index b31ff5a..8c12357 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ModelBuilder.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ModelBuilder.java
@@ -23,17 +23,19 @@ import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildA
  *
  * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
  *
- * <p>Consumer compatibility: This interface is used by all consumer versions from 1.6-rc-1.</p>
+ * <p>Consumer compatibility: This interface is used by all consumer versions from 1.6-rc-1 to 2.0.</p>
  * <p>Provider compatibility: This interface is implemented by all provider versions from 1.6-rc-1.</p>
  *
  * @since 1.6-rc-1
+ * @deprecated 2.1-rc-1 Use {@link InternalCancellableConnection} instead.
  * @see ConnectionVersion4
  */
+ at Deprecated
 public interface ModelBuilder extends InternalProtocolInterface {
     /**
      * Performs some action against a build and returns the requested model.
      *
-     * <p>Consumer compatibility: This method is used by all consumer versions from 1.6-rc-1.</p>
+     * <p>Consumer compatibility: This method is used by all consumer versions from 1.6-rc-1 to 2.0.</p>
      * <p>Provider compatibility: This method is implemented by all provider versions from 1.6-rc-1.</p>
      *
      * @param modelIdentifier The identifier of the model to build.
@@ -42,7 +44,9 @@ public interface ModelBuilder extends InternalProtocolInterface {
      * @throws InternalUnsupportedBuildArgumentException When the specified command-line options are not supported.
      * @throws IllegalStateException When this connection has been stopped.
      * @since 1.6-rc-1
+     * @deprecated 2.1-rc-1 Use {@link InternalCancellableConnection#getModel(ModelIdentifier, InternalCancellationToken, BuildParameters)} instead.
      */
+    @Deprecated
     BuildResult<?> getModel(ModelIdentifier modelIdentifier, BuildParameters operationParameters) throws
             BuildExceptionVersion1,
             InternalUnsupportedModelException,
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ShutdownParameters.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ShutdownParameters.java
new file mode 100644
index 0000000..f0d0c21
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/ShutdownParameters.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * The parameters for shutting down a provider connection.
+ *
+ * <p>This is a marker interface. Instances are queried dynamically to see which parameters they support.
+ *
+ * @since 2.2-rc-1
+ */
+public interface ShutdownParameters extends InternalProtocolInterface {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/StoppableConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/StoppableConnection.java
new file mode 100644
index 0000000..5b37bd4
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/StoppableConnection.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol;
+
+/**
+ * Allows a connection to be shutdown.
+ *
+ * <p>DO NOT CHANGE THIS INTERFACE - it is part of the cross-version protocol.
+ *
+ * <p>Consumer compatibility: This interface is used by all consumer versions from 2.2-rc-1.</p>
+ * <p>Provider compatibility: This interface is implemented by all provider versions from 2.2-rc-1.</p>
+ *
+ * @since 2.2-rc-1
+ */
+public interface StoppableConnection extends InternalProtocolInterface {
+    /**
+     * Shuts down this connection. Blocks until complete. The connection should not be used after calling this method.
+     *
+     * <p>Consumer compatibility: This method is used by all consumer versions from 2.2-rc-1.</p>
+     * <p>Provider compatibility: This method is implemented by all provider versions from 2.2-rc-1.</p>
+     *
+     * @since 2.2-rc-1
+     */
+    void shutdown(ShutdownParameters parameters);
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java
index 96aad9a..5a6a78e 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/eclipse/EclipseProjectVersion3.java
@@ -17,13 +17,14 @@ package org.gradle.tooling.internal.protocol.eclipse;
 
 import org.gradle.tooling.internal.protocol.BuildableProjectVersion1;
 import org.gradle.tooling.internal.protocol.ExternalDependencyVersion1;
+import org.gradle.tooling.internal.protocol.ProjectVersion3;
 
 /**
  * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
  *
  * @since 1.0-milestone-3
  */
-public interface EclipseProjectVersion3 extends HierarchicalEclipseProjectVersion1, BuildableProjectVersion1 {
+public interface EclipseProjectVersion3 extends HierarchicalEclipseProjectVersion1, BuildableProjectVersion1, ProjectVersion3 {
     EclipseProjectVersion3 getParent();
 
     Iterable<? extends EclipseTaskVersion1> getTasks();
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalJvmTestDescriptor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalJvmTestDescriptor.java
new file mode 100644
index 0000000..54b4bb3
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalJvmTestDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalJvmTestDescriptor extends InternalTestDescriptor {
+
+    String KIND_SUITE = "SUITE";
+    String KIND_ATOMIC = "ATOMIC";
+
+    /**
+     * Returns the kind of test this is. See the constants on this interface for the supported kinds.
+     *
+     * @return The test kind (test suite, atomic test, etc.).
+     */
+    String getTestKind();
+
+    /**
+     * Returns the name of the test suite, if any.
+     *
+     * @return The name of the test suite, can be null.
+     */
+    String getSuiteName();
+
+    /**
+     * Returns the name of the test class, if any.
+     *
+     * @return The name of the test class, can be null.
+     */
+    String getClassName();
+
+    /**
+     * Returns the name of the test method, if any.
+     *
+     * @return The name of the test method, can be null.
+     */
+    String getMethodName();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestDescriptor.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestDescriptor.java
new file mode 100644
index 0000000..773ee4f
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestDescriptor.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+import org.gradle.tooling.internal.protocol.InternalProtocolInterface;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestDescriptor extends InternalProtocolInterface {
+
+    /**
+     * Returns the id that uniquely identifies the test.
+     *
+     * @return The unique id of the test, never null
+     */
+    Object getId();
+
+    /**
+     * Returns the name of the test.
+     *
+     * @return The name of the test, never null
+     */
+    String getName();
+
+    /**
+     * Returns a human consumable display name for the test.
+     */
+    String getDisplayName();
+
+    /**
+     * Returns the id of the parent of this test, if any.
+     *
+     * @return The id of the parent of this test, can be null
+     */
+    Object getParentId();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFailureResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFailureResult.java
new file mode 100644
index 0000000..30b4107
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFailureResult.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestFailureResult extends InternalTestResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFinishedProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFinishedProgressEvent.java
new file mode 100644
index 0000000..a0df00a
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestFinishedProgressEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestFinishedProgressEvent extends InternalTestProgressEvent {
+    /**
+     * Returns the result of running the test.
+     *
+     * @return The test result
+     */
+    InternalTestResult getResult();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestProgressEvent.java
new file mode 100644
index 0000000..4d3c0c4
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestProgressEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.tooling.internal.protocol.events;
+
+import org.gradle.tooling.internal.protocol.InternalProtocolInterface;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestProgressEvent extends InternalProtocolInterface {
+    /**
+     * Returns the time when the event happened.
+     *
+     * @return The event time
+     */
+    long getEventTime();
+
+    /**
+     * Returns a human consumable display name for this event.
+     */
+    String getDisplayName();
+
+    /**
+     * Returns the description of the test for which progress is reported.
+     *
+     * @return The test description
+     */
+    InternalTestDescriptor getDescriptor();
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestResult.java
new file mode 100644
index 0000000..b6d3ff8
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+import org.gradle.tooling.internal.protocol.InternalFailure;
+import org.gradle.tooling.internal.protocol.InternalProtocolInterface;
+
+import java.util.List;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestResult extends InternalProtocolInterface {
+    /**
+     * Returns the time the test execution started.
+     *
+     * @return The start time
+     */
+    long getStartTime();
+
+    /**
+     * Returns the time the test execution finished.
+     *
+     * @return The finish time
+     */
+    long getEndTime();
+
+    /**
+     * Returns the failures that occurred while running the test, if any.
+     *
+     * @return The failures that occurred
+     */
+    List<? extends InternalFailure> getFailures();
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSkippedResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSkippedResult.java
new file mode 100644
index 0000000..b65fadf
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSkippedResult.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * Represents a test that was not run.
+ *
+ * @since 2.4
+ */
+public interface InternalTestSkippedResult extends InternalTestResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestStartedProgressEvent.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestStartedProgressEvent.java
new file mode 100644
index 0000000..ac314e1
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestStartedProgressEvent.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * @since 2.4
+ */
+public interface InternalTestStartedProgressEvent extends InternalTestProgressEvent {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSuccessResult.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSuccessResult.java
new file mode 100644
index 0000000..ee6cfa9
--- /dev/null
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/protocol/events/InternalTestSuccessResult.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.protocol.events;
+
+/**
+ * DO NOT CHANGE THIS INTERFACE. It is part of the cross-version protocol.
+ *
+ * @since 2.4
+ */
+public interface InternalTestSuccessResult extends InternalTestResult {
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/GradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/GradleProject.java
index 01602ba..ab008c8 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/GradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/GradleProject.java
@@ -20,6 +20,8 @@ import org.gradle.api.Incubating;
 import org.gradle.api.Nullable;
 import org.gradle.tooling.model.gradle.GradleScript;
 
+import java.io.File;
+
 /**
  * Represents a Gradle project.
  *
@@ -65,4 +67,23 @@ public interface GradleProject extends HierarchicalElement, BuildableElement {
      */
     @Incubating
     GradleScript getBuildScript();
+
+    /**
+     * Returns the build directory for this project.
+     *
+     * @return The build directory.
+     * @since 2.0
+     */
+    @Incubating
+    File getBuildDirectory();
+
+    /**
+     * Returns the project directory for this project.
+     *
+     * @return The project directory.
+     * @since 2.4
+     */
+    @Incubating
+    File getProjectDirectory();
+
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Launchable.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Launchable.java
index 7e9a0d3..9f259b6 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Launchable.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Launchable.java
@@ -44,4 +44,13 @@ public interface Launchable {
      */
     @Nullable
     String getDescription();
+
+    /**
+     * Returns whether launchable is public or not. A public launchable is one that is considered a public 'entry point' to the build, that is interesting for
+     * an end user of the build to run.
+     *
+     * @return Public property.
+     * @since 2.1
+     */
+    boolean isPublic();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
index 9efac40..8e6cc37 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
@@ -54,9 +54,8 @@ public interface Task extends Launchable {
      * Returns the element which this task belongs to.
      *
      * @deprecated Do not use this method. It is assumed that the caller already has a reference to owning project.
-     * @return The element which this task belongs to. Since 1.12 some implementations can return {@code null}.
-     * @throws org.gradle.tooling.model.UnsupportedMethodException thrown from implementation like
-     * {@link org.gradle.tooling.model.gradle.BuildInvocations}.
+     * @return The element which this task belongs to.
+     * @throws org.gradle.tooling.model.UnsupportedMethodException From 1.12 for implementations that do not also implement {@link org.gradle.tooling.model.GradleTask}.
      * @since 1.0-milestone-3
      */
     @Deprecated
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/GradleEnvironment.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/GradleEnvironment.java
index e96d5ad..75e03c2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/GradleEnvironment.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/GradleEnvironment.java
@@ -16,6 +16,10 @@
 
 package org.gradle.tooling.model.build;
 
+import org.gradle.api.Incubating;
+
+import java.io.File;
+
 /**
  * Informs about the Gradle environment, for example the Gradle version.
  * <p>
@@ -26,9 +30,18 @@ package org.gradle.tooling.model.build;
 public interface GradleEnvironment {
 
     /**
+     * Informs about the Gradle user home.
+     *
+     * @since 2.4
+     */
+    @Incubating
+    File getGradleUserHome();
+
+    /**
      * Informs about the Gradle version.
      *
      * @since 1.0-milestone-8
      */
     String getGradleVersion();
-}
\ No newline at end of file
+
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java
deleted file mode 100644
index 4de0531..0000000
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/EclipseTask.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2011 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.tooling.model.eclipse;
-
-import org.gradle.tooling.model.Task;
-
-/**
- * Deprecated because Gradle tasks are not associated with Eclipse projects.
- *
- * @deprecated Use {@link EclipseProject#getGradleProject()} to determine the associated Gradle project for an Eclipse project,
- * then use {@link org.gradle.tooling.model.GradleProject#getTasks()} to determine the tasks for the Gradle project.
- */
- at Deprecated
-public interface EclipseTask extends Task {
-    /**
-     * {@inheritDoc}
-     */
-    EclipseProject getProject();
-}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java
index 3010fd0..4ec7653 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/HierarchicalEclipseProject.java
@@ -17,7 +17,6 @@ package org.gradle.tooling.model.eclipse;
 
 import org.gradle.tooling.model.DomainObjectSet;
 import org.gradle.tooling.model.HierarchicalElement;
-import org.gradle.tooling.model.UnsupportedMethodException;
 
 import java.io.File;
 
@@ -58,11 +57,8 @@ public interface HierarchicalEclipseProject extends HierarchicalElement {
      *
      * @return The linked resources.
      * @since 1.0-milestone-4
-     * @throws org.gradle.tooling.model.UnsupportedMethodException
-     *  When the target Gradle version does not support this information.
-     *  You can safely catch ignore this exception and query the model for other information.
      */
-    DomainObjectSet<? extends EclipseLinkedResource> getLinkedResources() throws UnsupportedMethodException;
+    DomainObjectSet<? extends EclipseLinkedResource> getLinkedResources();
 
     /**
      * Returns the project directory for this project.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
index 85a79cd..ed5f982 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
@@ -63,7 +63,6 @@ public interface BasicGradleProject extends Model {
      * Returns the child projects of this project, or the empty set if there are no child projects.
      *
      * @return The child projects of this project, or the empty set if there are no child projects.
-     * @since 1.0-milestone-5
      */
     DomainObjectSet<? extends BasicGradleProject> getChildren();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BuildInvocations.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BuildInvocations.java
index 8ab4e72..8cf34af 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BuildInvocations.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BuildInvocations.java
@@ -18,6 +18,7 @@ package org.gradle.tooling.model.gradle;
 
 import org.gradle.api.Incubating;
 import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.Model;
 import org.gradle.tooling.model.Task;
 import org.gradle.tooling.model.TaskSelector;
 
@@ -31,17 +32,18 @@ import org.gradle.tooling.model.TaskSelector;
  * @since 1.12
  */
 @Incubating
-public interface BuildInvocations {
-    /*
+public interface BuildInvocations extends Model {
+    /**
      * Returns tasks selectors that can be used to execute a build.
      *
+     * Selector is a {@link org.gradle.tooling.model.Launchable} that requests to build all tasks with a given name in context of some project and all its subprojects.
      * @return The task selectors.
      * @since 1.12
      */
     @Incubating
     DomainObjectSet<? extends TaskSelector> getTaskSelectors();
 
-    /*
+    /**
      * Returns the tasks that can be used to execute a build.
      *
      * @return The tasks.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/GradleBuild.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/GradleBuild.java
index 136fe8a..c34cc9c 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/GradleBuild.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/GradleBuild.java
@@ -18,6 +18,7 @@ package org.gradle.tooling.model.gradle;
 
 import org.gradle.api.Incubating;
 import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.Model;
 
 /**
  * Provides information about the structure of a Gradle build.
@@ -25,7 +26,7 @@ import org.gradle.tooling.model.DomainObjectSet;
  * @since 1.8
  */
 @Incubating
-public interface GradleBuild {
+public interface GradleBuild extends Model {
     /**
      * Returns the root project for this build.
      *
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/ProjectPublications.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/ProjectPublications.java
index 60c807e..45fc0f9 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/ProjectPublications.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/ProjectPublications.java
@@ -18,6 +18,7 @@ package org.gradle.tooling.model.gradle;
 
 import org.gradle.api.Incubating;
 import org.gradle.tooling.model.DomainObjectSet;
+import org.gradle.tooling.model.Model;
 
 /**
  * A model providing information about the publications of a Gradle project.
@@ -25,7 +26,7 @@ import org.gradle.tooling.model.DomainObjectSet;
  * @since 1.12
  */
 @Incubating
-public interface ProjectPublications {
+public interface ProjectPublications extends Model {
 
     /**
      * Returns the publications for this project.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaContentRoot.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaContentRoot.java
index 1f55559..f8c6acd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaContentRoot.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaContentRoot.java
@@ -16,6 +16,7 @@
 
 package org.gradle.tooling.model.idea;
 
+import org.gradle.api.Incubating;
 import org.gradle.tooling.model.DomainObjectSet;
 
 import java.io.File;
@@ -27,22 +28,38 @@ import java.util.Set;
 public interface IdeaContentRoot {
 
     /**
-     * root directory
+     * The content root directory.
      */
     File getRootDirectory();
 
     /**
-     * source dirs.
+     * The set of source directories.
      */
     DomainObjectSet<? extends IdeaSourceDirectory> getSourceDirectories();
 
     /**
-     * test dirs.
+     * The set of generated source directories. This is a subset of those directories returned by {@link #getSourceDirectories()}.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    DomainObjectSet<? extends IdeaSourceDirectory> getGeneratedSourceDirectories();
+
+    /**
+     * The set of test source directories.
      */
     DomainObjectSet<? extends IdeaSourceDirectory> getTestDirectories();
 
     /**
-     * exclude dirs
+     * The set of generated test directories. This is a subset of those directories returned by {@link #getTestDirectories()}.
+     *
+     * @since 2.2
+     */
+    @Incubating
+    DomainObjectSet<? extends IdeaSourceDirectory> getGeneratedTestDirectories();
+
+    /**
+     * The set of excluded directories.
      */
     Set<File> getExcludeDirectories();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaSourceDirectory.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaSourceDirectory.java
index ea862de..38cd2bb 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaSourceDirectory.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaSourceDirectory.java
@@ -16,6 +16,7 @@
 
 package org.gradle.tooling.model.idea;
 
+import org.gradle.api.Incubating;
 import org.gradle.tooling.model.SourceDirectory;
 
 /**
@@ -23,4 +24,13 @@ import org.gradle.tooling.model.SourceDirectory;
  *
  * @since 1.0-milestone-5
  */
-public interface IdeaSourceDirectory extends SourceDirectory {}
+public interface IdeaSourceDirectory extends SourceDirectory {
+    /**
+     * Return {@code true} if this source directory is generated.
+     *
+     * @return true if source directory is generated
+     * @since 2.2
+     */
+    @Incubating
+    boolean isGenerated();
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpecTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpecTest.groovy
index 9063ce9..7b1c6f1 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpecTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpecTest.groovy
@@ -37,6 +37,24 @@ class GradleVersionSpecTest extends Specification {
         !spec.isSatisfiedBy(GradleVersion.version("0.5"))
     }
 
+    def "greater-than version constraint matches all versions later than specified base version"() {
+        def spec = GradleVersionSpec.toSpec(">1.0")
+
+        expect:
+        !spec.isSatisfiedBy(GradleVersion.version("1.0"))
+        !spec.isSatisfiedBy(GradleVersion.version("1.0-rc-1"))
+        !spec.isSatisfiedBy(GradleVersion.version("1.0-12341010120000+1000"))
+
+        spec.isSatisfiedBy(GradleVersion.version("1.1"))
+        spec.isSatisfiedBy(GradleVersion.version("1.1-rc-7"))
+        spec.isSatisfiedBy(GradleVersion.version("1.1-12341010120000+1000"))
+        spec.isSatisfiedBy(GradleVersion.version("2.56"))
+
+        !spec.isSatisfiedBy(GradleVersion.version("1.0-milestone-9"))
+        !spec.isSatisfiedBy(GradleVersion.version("0.9.2"))
+        !spec.isSatisfiedBy(GradleVersion.version("0.5"))
+    }
+
     def "less-than-or-equal version constraint matches all versions with specified base version and earlier"() {
         def spec = GradleVersionSpec.toSpec("<=1.4")
 
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/adapter/ProtocolToModelAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/adapter/ProtocolToModelAdapterTest.groovy
index d2f2f81..04031aa 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/adapter/ProtocolToModelAdapterTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/adapter/ProtocolToModelAdapterTest.groovy
@@ -219,6 +219,16 @@ class ProtocolToModelAdapterTest extends Specification {
         model.getConfig("default") == "default"
     }
 
+    def "can use safe getter for boolean properties"() {
+        TestProtocolModel protocolModel = Mock()
+
+        when:
+        def model = adapter.adapt(TestModel.class, protocolModel)
+
+        then:
+        model.isThing(true)
+    }
+
     def "mapper can register method invoker to override getter method"() {
         MethodInvoker methodInvoker = Mock()
         Action mapper = Mock()
@@ -440,6 +450,8 @@ interface TestModel {
 
     String getConfig(String defaultValue)
 
+    Boolean isThing(Boolean defaultValue)
+
     DomainObjectSet<? extends TestProject> getChildren()
 
     List<TestProject> getChildList()
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy
deleted file mode 100644
index 11cc487..0000000
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectionFactoryTest.groovy
+++ /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.tooling.internal.consumer
-
-import org.gradle.listener.ListenerManager
-import org.gradle.logging.ProgressLoggerFactory
-import org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor
-import org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor
-import org.gradle.tooling.internal.consumer.connection.LoggingInitializerConsumerActionExecutor
-import org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor
-import org.gradle.tooling.internal.consumer.loader.ToolingImplementationLoader
-import spock.lang.Specification
-
-class ConnectionFactoryTest extends Specification {
-    final ToolingImplementationLoader implementationLoader = Mock()
-    final ListenerManager listenerManager = Mock()
-    final ProgressLoggerFactory progressLoggerFactory = Mock()
-    final Distribution distribution = Mock()
-    final ConnectionParameters parameters = DefaultConnectionParameters.builder().build()
-    final ConnectionFactory factory = new ConnectionFactory(implementationLoader)
-
-    def usesImplementationLoaderToLoadConnectionFactory() {
-        when:
-        def result = factory.create(distribution, parameters)
-
-        then:
-        result instanceof DefaultProjectConnection
-        result.connection instanceof DefaultAsyncConsumerActionExecutor
-        result.connection.actionExecutor instanceof LoggingInitializerConsumerActionExecutor
-        result.connection.actionExecutor.actionExecutor instanceof ProgressLoggingConsumerActionExecutor
-        result.connection.actionExecutor.actionExecutor.actionExecutor instanceof LazyConsumerActionExecutor
-        _ * distribution.displayName >> "[some distribution]"
-        0 * _._
-    }
-}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectorServicesTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectorServicesTest.groovy
index c5c581e..9088651 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectorServicesTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/ConnectorServicesTest.groovy
@@ -23,8 +23,8 @@ public class ConnectorServicesTest extends Specification {
 
     def "services sharing configuration"() {
         when:
-        def connectorOne = new ConnectorServices().createConnector()
-        def connectorTwo = new ConnectorServices().createConnector()
+        def connectorOne = ConnectorServices.createConnector()
+        def connectorTwo = ConnectorServices.createConnector()
 
         then:
         connectorOne != connectorTwo
@@ -35,4 +35,13 @@ public class ConnectorServicesTest extends Specification {
         //tooling impl loader must be shared across connectors, so that we have single DefaultConnection per distro/classpath
         connectorOne.connectionFactory.toolingImplementationLoader == connectorTwo.connectionFactory.toolingImplementationLoader
     }
+
+    def "can close services"() {
+        when:
+        ConnectorServices.close()
+        ConnectorServices.createConnector()
+
+        then:
+        IllegalStateException e = thrown()
+    }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuterTest.groovy
index 6384451..20de619 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuterTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildActionExecuterTest.groovy
@@ -47,7 +47,9 @@ class DefaultBuildActionExecuterTest extends ConcurrentSpec {
             action.run(connection)
             adaptedHandler = args[1]
         }
-        1 * connection.run(action, _) >> result
+        1 * connection.run(action, _) >> { args ->
+            return result
+        }
 
         when:
         adaptedHandler.onComplete(result)
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy
index 24a0366..dee2b71 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultBuildLauncherTest.groovy
@@ -20,11 +20,12 @@ import org.gradle.api.GradleException
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.ResultHandler
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
 import org.gradle.tooling.internal.consumer.async.AsyncConsumerActionExecutor
 import org.gradle.tooling.internal.consumer.connection.ConsumerAction
 import org.gradle.tooling.internal.consumer.connection.ConsumerConnection
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
-import org.gradle.tooling.internal.gradle.BasicGradleTaskSelector
+import org.gradle.tooling.internal.gradle.TaskListingLaunchable
 import org.gradle.tooling.internal.protocol.InternalLaunchable
 import org.gradle.tooling.internal.protocol.ResultHandlerVersion1
 import org.gradle.tooling.model.GradleProject
@@ -62,6 +63,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
             assert params.jvmArguments == null
             assert params.arguments == null
             assert params.progressListener != null
+            assert params.cancellationToken != null
             return null
         }
         1 * handler.onComplete(null)
@@ -103,7 +105,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
     }
 
     def "can configure task selector build operation for consumer generated selectors"() {
-        TaskSelector ts = Mock(BasicGradleTaskSelector)
+        def ts = Mock(TaskListingLaunchable)
         _ * ts.name >> 'myTask'
         _ * ts.taskNames >> Sets.newTreeSet([':a:myTask', ':b:myTask'])
         ResultHandlerVersion1<Void> adaptedHandler
@@ -114,7 +116,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
         when:
         launcher.standardOutput = stdout
         launcher.standardError = stderr
-        launcher.forLaunchables(ts)
+        launcher.forLaunchables(selector(ts))
         launcher.run(handler)
 
         then:
@@ -136,11 +138,8 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
         0 * handler._
     }
 
-    static interface InternalTaskSelectorImplementation extends TaskSelector, InternalLaunchable {
-    }
-
     def "can configure task selector build operation"() {
-        TaskSelector ts = Mock(InternalTaskSelectorImplementation)
+        def ts = Mock(InternalLaunchable)
         _ * ts.name >> 'myTask'
         ResultHandlerVersion1<Void> adaptedHandler
         ResultHandler<Void> handler = Mock()
@@ -150,7 +149,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
         when:
         launcher.standardOutput = stdout
         launcher.standardError = stderr
-        launcher.forLaunchables(ts)
+        launcher.forLaunchables(selector(ts))
         launcher.run(handler)
 
         then:
@@ -173,13 +172,13 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
     }
 
     def "preserves task selectors order in build operation"() {
-        TaskSelector ts1 = Mock(BasicGradleTaskSelector)
+        def ts1 = Mock(TaskListingLaunchable)
         _ * ts1.name >> 'firstTask'
         _ * ts1.taskNames >> Sets.newTreeSet([':firstTask'])
-        TaskSelector ts2 = Mock(BasicGradleTaskSelector)
+        def ts2 = Mock(TaskListingLaunchable)
         _ * ts2.name >> 'secondTask'
         _ * ts2.taskNames >> Sets.newTreeSet([':secondTask'])
-        TaskSelector ts3 = Mock(BasicGradleTaskSelector)
+        def ts3 = Mock(TaskListingLaunchable)
         _ * ts3.name >> 'thirdTask'
         _ * ts3.taskNames >> Sets.newTreeSet([':thirdTask'])
         ResultHandlerVersion1<Void> adaptedHandler
@@ -190,7 +189,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
         when:
         launcher.standardOutput = stdout
         launcher.standardError = stderr
-        launcher.forLaunchables(ts1, ts2, ts3)
+        launcher.forLaunchables(selector(ts1), selector(ts2), selector(ts3))
         launcher.run(handler)
 
         then:
@@ -315,7 +314,7 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
         Launchable task = Mock(Launchable)
 
         when:
-        launcher.forLaunchables(task)
+        launcher.forLaunchables(selector(task))
 
         then:
         def e = thrown(GradleException)
@@ -323,9 +322,14 @@ class DefaultBuildLauncherTest extends ConcurrentSpec {
     }
 
     def task(String path) {
-        Task task = Mock()
-        _ * task.path >> path
-        return task
+        def task = new Object() {
+            String getPath() { return path }
+        }
+        return new ProtocolToModelAdapter().adapt(Task, task)
+    }
+
+    def selector(def object) {
+        return new ProtocolToModelAdapter().adapt(TaskSelector, object)
     }
 }
 
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSourceTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSourceTest.groovy
new file mode 100644
index 0000000..db801ef
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultCancellationTokenSourceTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer
+
+import spock.lang.Specification
+
+class DefaultCancellationTokenSourceTest extends Specification {
+    def source = new DefaultCancellationTokenSource()
+
+    def 'token operation idempotent'() {
+        when:
+        def token1 = source.token()
+        def token2 = source.token()
+
+        then:
+        token1 == token2
+    }
+
+    def 'can unpack token'() {
+        expect:
+        source.token().token != null
+    }
+
+    def 'can cancel'() {
+        expect:
+        !source.token().cancellationRequested
+
+        when:
+        source.cancel()
+
+        then:
+        source.token().cancellationRequested
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy
index 5a18048..79424cc 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DefaultModelBuilderTest.groovy
@@ -56,6 +56,7 @@ class DefaultModelBuilderTest extends ConcurrentSpec {
             assert params.jvmArguments == null
             assert params.arguments == null
             assert params.progressListener != null
+            assert params.cancellationToken != null
             assert params.tasks == null
             return result
         }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy
index 4be0d44..c67dbb6 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/DistributionFactoryTest.groovy
@@ -15,22 +15,27 @@
  */
 package org.gradle.tooling.internal.consumer
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.logging.ProgressLogger
 import org.gradle.logging.ProgressLoggerFactory
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.util.DistributionLocator
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Specification
 
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
 class DistributionFactoryTest extends Specification {
     @Rule final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()
     final ProgressLoggerFactory progressLoggerFactory = Mock()
     final ProgressLogger progressLogger = Mock()
-    final DistributionFactory factory = new DistributionFactory()
+    final ExecutorServiceFactory executorFactory = Mock()
+    final BuildCancellationToken cancellationToken = Mock()
+    final ExecutorService executor = Executors.newSingleThreadExecutor()
+    final DistributionFactory factory = new DistributionFactory(executorFactory)
 
     def setup() {
         _ * progressLoggerFactory.newOperation(!null) >> progressLogger
@@ -71,7 +76,7 @@ class DistributionFactoryTest extends Specification {
 
         expect:
         def dist = factory.getDistribution(tmpDir.testDirectory)
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null).asFiles as Set == [libA, libB] as Set
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken).asFiles as Set == [libA, libB] as Set
     }
 
     def failsWhenInstallationDirectoryDoesNotExist() {
@@ -79,7 +84,7 @@ class DistributionFactoryTest extends Specification {
         def dist = factory.getDistribution(distDir)
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken)
 
         then:
         IllegalArgumentException e = thrown()
@@ -91,7 +96,7 @@ class DistributionFactoryTest extends Specification {
         def dist = factory.getDistribution(distDir)
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken)
 
         then:
         IllegalArgumentException e = thrown()
@@ -103,7 +108,7 @@ class DistributionFactoryTest extends Specification {
         def dist = factory.getDistribution(distDir)
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken)
 
         then:
         IllegalArgumentException e = thrown()
@@ -118,6 +123,7 @@ class DistributionFactoryTest extends Specification {
     }
 
     def usesContentsOfDistributionZipLibDirectoryAsImplementationClasspath() {
+        1 * executorFactory.create() >> executor
         def zipFile = createZip {
             lib {
                 file("a.jar")
@@ -127,10 +133,11 @@ class DistributionFactoryTest extends Specification {
         def dist = factory.getDistribution(zipFile.toURI())
 
         expect:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null).asFiles.name as Set == ['a.jar', 'b.jar'] as Set
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken).asFiles.name as Set == ['a.jar', 'b.jar'] as Set
     }
 
     def usesWrapperDistributionInstalledIntoSpecifiedUserHomeDirAsImplementationClasspath() {
+        1 * executorFactory.create() >> executor
         File customUserHome = tmpDir.file('customUserHome')
         def zipFile = createZip {
             lib {
@@ -140,7 +147,7 @@ class DistributionFactoryTest extends Specification {
         }
         tmpDir.file('gradle/wrapper/gradle-wrapper.properties') << "distributionUrl=${zipFile.toURI()}"
         def dist = factory.getDefaultDistribution(tmpDir.testDirectory, false)
-        def result = dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome)
+        def result = dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome, cancellationToken)
 
         expect:
         result.asFiles.name as Set == ['a.jar', 'b.jar'] as Set
@@ -148,6 +155,7 @@ class DistributionFactoryTest extends Specification {
     }
 
     def usesZipDistributionInstalledIntoSpecifiedUserHomeDirAsImplementationClasspath() {
+        1 * executorFactory.create() >> executor
         File customUserHome = tmpDir.file('customUserHome')
         def zipFile = createZip {
             lib {
@@ -156,7 +164,7 @@ class DistributionFactoryTest extends Specification {
             }
         }
         def dist = factory.getDistribution(zipFile.toURI())
-        def result = dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome)
+        def result = dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome, cancellationToken)
 
         expect:
         result.asFiles.name as Set == ['a.jar', 'b.jar'] as Set
@@ -175,7 +183,7 @@ class DistributionFactoryTest extends Specification {
         ProgressLogger loggerTwo = Mock()
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, customUserHome, cancellationToken)
 
         then:
         2 * progressLoggerFactory.newOperation(DistributionFactory.class) >>> [loggerOne, loggerTwo]
@@ -188,16 +196,19 @@ class DistributionFactoryTest extends Specification {
         1 * loggerTwo.started()
         1 * loggerTwo.completed()
 
+        1 * executorFactory.create() >> executor
+        1 * cancellationToken.addCallback(_)
+
         0 * _._
     }
 
-    @Requires(TestPrecondition.ONLINE)
     def failsWhenDistributionZipDoesNotExist() {
-        URI zipFile = new URI("http://google.com/does-not-exist/gradle-1.0.zip")
+        1 * executorFactory.create() >> executor
+        URI zipFile = tmpDir.file("no-exists.zip").toURI()
         def dist = factory.getDistribution(zipFile)
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken)
 
         then:
         IllegalArgumentException e = thrown()
@@ -209,9 +220,11 @@ class DistributionFactoryTest extends Specification {
         def dist = factory.getDistribution(zipFile.toURI())
 
         when:
-        dist.getToolingImplementationClasspath(progressLoggerFactory, null)
+        dist.getToolingImplementationClasspath(progressLoggerFactory, null, cancellationToken)
 
         then:
+        1 * executorFactory.create() >> executor
+        1 * cancellationToken.addCallback(_)
         IllegalArgumentException e = thrown()
         e.message == "The specified Gradle distribution '${zipFile.toURI()}' does not appear to contain a Gradle distribution."
     }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/SynchronizedLoggingTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/SynchronizedLoggingTest.groovy
index b21cf7c..ec45f28 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/SynchronizedLoggingTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/SynchronizedLoggingTest.groovy
@@ -14,35 +14,22 @@
  * limitations under the License.
  */
 
-package org.gradle.tooling.internal.consumer;
+package org.gradle.tooling.internal.consumer
 
-
-import org.gradle.test.fixtures.ConcurrentTestUtil
-import spock.lang.Specification
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 
 import java.util.concurrent.CopyOnWriteArraySet
 
-public class SynchronizedLoggingTest extends Specification {
-
+public class SynchronizedLoggingTest extends ConcurrentSpec {
     def logging = new SynchronizedLogging()
-    def concurrent = new ConcurrentTestUtil()
-
-    def "must be initialized"() {
-        when:
-        logging.listenerManager
-        then:
-        thrown(IllegalStateException)
 
-        when:
-        logging.progressLoggerFactory
-        then:
-        thrown(IllegalStateException)
+    def "initialises on first usage"() {
+        expect:
+        logging.listenerManager != null
+        logging.listenerManager == logging.listenerManager
 
-        when:
-        logging.init()
-        then:
-        logging.listenerManager
-        logging.progressLoggerFactory
+        logging.progressLoggerFactory != null
+        logging.progressLoggerFactory == logging.progressLoggerFactory
     }
 
     def "keeps state per thread"() {
@@ -51,16 +38,16 @@ public class SynchronizedLoggingTest extends Specification {
         Set loggingTools = new CopyOnWriteArraySet()
 
         when:
-        2.times {
-            concurrent.start {
-                logging.init()
-                loggingTools << logging.listenerManager
-                loggingTools << logging.progressLoggerFactory
+        async {
+            2.times {
+                start {
+                    loggingTools << logging.listenerManager
+                    loggingTools << logging.progressLoggerFactory
+                }
             }
         }
-        concurrent.finished()
 
-        then: "each thread has separate instances of logging tools"
+        then:
         loggingTools.size() == 4
     }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnectionTest.groovy
index 971b96f..c746f9a 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnectionTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ActionAwareConsumerConnectionTest.groovy
@@ -22,17 +22,76 @@ import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping
 import org.gradle.tooling.internal.protocol.*
+import org.gradle.tooling.model.GradleProject
+import org.gradle.tooling.model.build.BuildEnvironment
+import org.gradle.tooling.model.eclipse.EclipseProject
+import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
+import org.gradle.tooling.model.gradle.BuildInvocations
+import org.gradle.tooling.model.gradle.GradleBuild
+import org.gradle.tooling.model.idea.BasicIdeaProject
+import org.gradle.tooling.model.idea.IdeaProject
+import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
 import spock.lang.Specification
 
 class ActionAwareConsumerConnectionTest extends Specification {
+    final metaData = Stub(ConnectionMetaDataVersion1)
     final target = Mock(TestModelBuilder) {
-        getMetaData() >> Stub(ConnectionMetaDataVersion1) {
-            getVersion() >> "1.8"
-        }
+        getMetaData() >> metaData
     }
     final adapter = Mock(ProtocolToModelAdapter)
     final modelMapping = Stub(ModelMapping)
-    final connection = new ActionAwareConsumerConnection(target, modelMapping, adapter)
+
+    def "describes capabilities of 1.8 provider"() {
+        given:
+        metaData.version >> "1.8"
+        def connection = new ActionAwareConsumerConnection(target, modelMapping, adapter)
+        def details = connection.versionDetails
+
+        expect:
+        !details.supportsTaskDisplayName()
+        !details.supportsCancellation()
+
+        and:
+        details.maySupportModel(HierarchicalEclipseProject)
+        details.maySupportModel(EclipseProject)
+        details.maySupportModel(IdeaProject)
+        details.maySupportModel(BasicIdeaProject)
+        details.maySupportModel(GradleProject)
+        details.maySupportModel(BuildEnvironment)
+        details.maySupportModel(ProjectOutcomes)
+        details.maySupportModel(Void)
+        details.maySupportModel(CustomModel)
+        details.maySupportModel(GradleBuild)
+
+        and:
+        !details.maySupportModel(BuildInvocations)
+    }
+
+    def "describes capabilities of a post 1.12 provider"() {
+        given:
+        metaData.version >> "1.12"
+        def connection = new ActionAwareConsumerConnection(target, modelMapping, adapter)
+        def details = connection.versionDetails
+
+        expect:
+        details.supportsTaskDisplayName()
+
+        and:
+        !details.supportsCancellation()
+
+        and:
+        details.maySupportModel(HierarchicalEclipseProject)
+        details.maySupportModel(EclipseProject)
+        details.maySupportModel(IdeaProject)
+        details.maySupportModel(BasicIdeaProject)
+        details.maySupportModel(GradleProject)
+        details.maySupportModel(BuildEnvironment)
+        details.maySupportModel(ProjectOutcomes)
+        details.maySupportModel(Void)
+        details.maySupportModel(CustomModel)
+        details.maySupportModel(GradleBuild)
+        details.maySupportModel(BuildInvocations)
+    }
 
     def "delegates to connection to run build action"() {
         def action = Mock(BuildAction)
@@ -40,6 +99,8 @@ class ActionAwareConsumerConnectionTest extends Specification {
         def buildController = Mock(InternalBuildController)
 
         when:
+        metaData.version >> "1.8"
+        def connection = new ActionAwareConsumerConnection(target, modelMapping, adapter)
         def result = connection.run(action, parameters)
 
         then:
@@ -61,6 +122,8 @@ class ActionAwareConsumerConnectionTest extends Specification {
         def failure = new RuntimeException()
 
         when:
+        metaData.version >> "1.8"
+        def connection = new ActionAwareConsumerConnection(target, modelMapping, adapter)
         connection.run(action, parameters)
 
         then:
@@ -74,4 +137,7 @@ class ActionAwareConsumerConnectionTest extends Specification {
 
     interface TestModelBuilder extends ModelBuilder, ConnectionVersion4, ConfigurableConnection, InternalBuildActionExecutor {
     }
+
+    static class CustomModel {
+    }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnectionTest.groovy
index f52e3e7..6367575 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnectionTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/BuildActionRunnerBackedConsumerConnectionTest.groovy
@@ -29,6 +29,7 @@ import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
 import org.gradle.tooling.model.eclipse.EclipseProject
 import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
+import org.gradle.tooling.model.gradle.BuildInvocations
 import org.gradle.tooling.model.gradle.GradleBuild
 import org.gradle.tooling.model.idea.BasicIdeaProject
 import org.gradle.tooling.model.idea.IdeaProject
@@ -52,7 +53,8 @@ class BuildActionRunnerBackedConsumerConnectionTest extends Specification {
         def details = connection.versionDetails
 
         expect:
-        details.supportsGradleProjectModel()
+        !details.supportsTaskDisplayName()
+        !details.supportsCancellation()
 
         and:
         details.maySupportModel(HierarchicalEclipseProject)
@@ -65,6 +67,8 @@ class BuildActionRunnerBackedConsumerConnectionTest extends Specification {
         details.maySupportModel(Void)
 
         and:
+        !details.maySupportModel(GradleBuild)
+        !details.maySupportModel(BuildInvocations)
         !details.maySupportModel(CustomModel)
     }
 
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnectionTest.groovy
new file mode 100644
index 0000000..6817403
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableConsumerConnectionTest.groovy
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection
+
+import org.gradle.api.Action
+import org.gradle.api.BuildCancelledException
+import org.gradle.api.GradleException
+import org.gradle.tooling.BuildAction
+import org.gradle.tooling.BuildActionFailureException
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
+import org.gradle.tooling.internal.adapter.SourceObjectMapping
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping
+import org.gradle.tooling.internal.protocol.*
+import org.gradle.tooling.model.GradleProject
+import org.gradle.tooling.model.build.BuildEnvironment
+import org.gradle.tooling.model.eclipse.EclipseProject
+import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
+import org.gradle.tooling.model.gradle.BuildInvocations
+import org.gradle.tooling.model.gradle.GradleBuild
+import org.gradle.tooling.model.idea.BasicIdeaProject
+import org.gradle.tooling.model.idea.IdeaProject
+import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
+import spock.lang.Specification
+
+class CancellableConsumerConnectionTest extends Specification {
+    final target = Mock(TestModelBuilder) {
+        getMetaData() >> Stub(ConnectionMetaDataVersion1) {
+            getVersion() >> "2.1"
+        }
+    }
+    final adapter = Mock(ProtocolToModelAdapter)
+    final modelMapping = Mock(ModelMapping)
+    final connection = new CancellableConsumerConnection(target, modelMapping, adapter)
+
+    def "describes capabilities of provider"() {
+        given:
+        def details = connection.versionDetails
+
+        expect:
+        details.maySupportModel(HierarchicalEclipseProject)
+        details.maySupportModel(EclipseProject)
+        details.maySupportModel(IdeaProject)
+        details.maySupportModel(BasicIdeaProject)
+        details.maySupportModel(GradleProject)
+        details.maySupportModel(BuildEnvironment)
+        details.maySupportModel(ProjectOutcomes)
+        details.maySupportModel(Void)
+        details.maySupportModel(GradleBuild)
+        details.maySupportModel(BuildInvocations)
+        details.maySupportModel(CustomModel)
+
+        and:
+        details.supportsTaskDisplayName()
+        details.supportsCancellation()
+    }
+
+    def "delegates to connection to run build action"() {
+        def action = Mock(BuildAction)
+        def parameters = Stub(ConsumerOperationParameters)
+        def buildController = Mock(InternalBuildController)
+
+        when:
+        def result = connection.run(action, parameters)
+
+        then:
+        result == 'result'
+
+        and:
+        1 * target.run(_, _, parameters) >> { InternalBuildAction protocolAction, InternalCancellationToken cancel, def params ->
+            def actionResult = protocolAction.execute(buildController)
+            return Stub(BuildResult) {
+                getModel() >> actionResult
+            }
+        }
+        1 * action.execute({ it instanceof BuildControllerAdapter }) >> 'result'
+    }
+
+    def "adapts build action failure"() {
+        def action = Mock(BuildAction)
+        def parameters = Stub(ConsumerOperationParameters)
+        def failure = new RuntimeException()
+
+        when:
+        connection.run(action, parameters)
+
+        then:
+        BuildActionFailureException e = thrown()
+        e.message == /The supplied build action failed with an exception./
+        e.cause == failure
+
+        and:
+        1 * target.run(_, _, parameters) >> { throw new InternalBuildActionFailureException(failure) }
+    }
+
+    def "adapts implementation-specific cancellation failure when running build action"() {
+        def action = Mock(BuildAction)
+        def parameters = Stub(ConsumerOperationParameters)
+        def failure = new GradleException("broken", new BuildCancelledException("cancelled."))
+
+        when:
+        connection.run(action, parameters)
+
+        then:
+        InternalBuildCancelledException e = thrown()
+        e.cause == failure
+
+        and:
+        1 * target.run(_, _, parameters) >> { throw new InternalBuildActionFailureException(failure) }
+    }
+
+    def "runs build using connection's getModel() method"() {
+        def parameters = Stub(ConsumerOperationParameters)
+        def modelIdentifier = Stub(ModelIdentifier)
+
+        when:
+        def result = connection.run(Void.class, parameters)
+
+        then:
+        result == 'result'
+
+        and:
+        1 * modelMapping.getModelIdentifierFromModelType(Void) >> modelIdentifier
+        1 * target.getModel(modelIdentifier, _, parameters) >> { ModelIdentifier id, InternalCancellationToken cancel, def params ->
+            return Stub(BuildResult) {
+                getModel() >> 'result'
+            }
+        }
+        1 * adapter.adapt(Void, 'result', _) >> { Class type, Object source, Action<? super SourceObjectMapping> mapper ->
+            return source
+        }
+    }
+
+    def "adapts implementation-specific cancellation failure when fetching model"() {
+        def parameters = Stub(ConsumerOperationParameters)
+        def failure = new GradleException("broken", new BuildCancelledException("cancelled."))
+
+        when:
+        connection.run(Void.class, parameters)
+
+        then:
+        InternalBuildCancelledException e = thrown()
+        e.cause == failure
+
+        and:
+        1 * target.getModel(_, _, _) >> { throw new BuildExceptionVersion1(failure) }
+    }
+
+    interface TestModelBuilder extends ConnectionVersion4, ConfigurableConnection, InternalCancellableConnection {
+    }
+
+    interface CustomModel {
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducerTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducerTest.groovy
new file mode 100644
index 0000000..56fbf6b
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/CancellableModelBuilderBackedModelProducerTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection
+
+import org.gradle.internal.Transformers
+import org.gradle.tooling.UnknownModelException
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
+import org.gradle.tooling.internal.consumer.versioning.ModelMapping
+import org.gradle.tooling.internal.consumer.versioning.VersionDetails
+import org.gradle.tooling.internal.protocol.BuildResult
+import org.gradle.tooling.internal.protocol.InternalCancellableConnection
+import org.gradle.tooling.internal.protocol.ModelIdentifier
+import spock.lang.Specification
+
+class CancellableModelBuilderBackedModelProducerTest extends Specification {
+
+    def adapter = Mock(ProtocolToModelAdapter);
+    def versionDetails = Mock(VersionDetails);
+    def mapping = Mock(ModelMapping);
+    def transformer = Transformers.noOpTransformer()
+    def builder = Mock(InternalCancellableConnection)
+
+    def modelProducer = new CancellableModelBuilderBackedModelProducer(adapter, versionDetails, mapping, builder, transformer)
+
+    def setup() {
+        _ * versionDetails.getVersion() >> "X.Y"
+    }
+
+    def "builder not triggered for unsupported Models"() {
+        setup:
+        1 * versionDetails.maySupportModel(SomeModel.class) >> false
+        when:
+        modelProducer.produceModel(SomeModel.class, Mock(ConsumerOperationParameters))
+        then:
+        0 * builder.getModel(_, _, _)
+        def e = thrown(UnknownModelException)
+        e.message == "The version of Gradle you are using (X.Y) does not support building a model of type 'SomeModel'. Support for building custom tooling models was added in Gradle 1.6 and is available in all later versions."
+    }
+
+    def "builder triggered for supported Models"() {
+        setup:
+        SomeModel returnValue = new SomeModel()
+        1 * versionDetails.maySupportModel(SomeModel.class) >> true
+        ModelIdentifier someModelIdentifier = Mock(ModelIdentifier)
+        1 * mapping.getModelIdentifierFromModelType(SomeModel.class) >> someModelIdentifier
+        BuildResult buildResult = Mock(BuildResult)
+        ConsumerOperationParameters operationParameters = Mock(ConsumerOperationParameters)
+        when:
+        SomeModel model = modelProducer.produceModel(SomeModel.class, operationParameters)
+        then:
+        1 * builder.getModel(someModelIdentifier, {!null}, operationParameters) >> buildResult
+        1 * buildResult.model >> returnValue
+        1 * adapter.adapt(SomeModel.class, returnValue, _) >> returnValue
+        model != null
+    }
+
+    static class SomeModel {
+
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnectionTest.groovy
index 049d01a..f7c7e86 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnectionTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ConnectionVersion4BackedConsumerConnectionTest.groovy
@@ -15,274 +15,46 @@
  */
 package org.gradle.tooling.internal.consumer.connection
 
-import org.gradle.tooling.BuildAction
-import org.gradle.tooling.UnknownModelException
 import org.gradle.tooling.UnsupportedVersionException
-import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
-import org.gradle.tooling.internal.build.VersionOnlyBuildEnvironment
+import org.gradle.tooling.internal.consumer.Distribution
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
-import org.gradle.tooling.internal.consumer.versioning.CustomModel
-import org.gradle.tooling.internal.consumer.versioning.ModelMapping
-import org.gradle.tooling.internal.gradle.DefaultGradleBuild
-import org.gradle.tooling.internal.gradle.PartialGradleProject
 import org.gradle.tooling.internal.protocol.ConnectionMetaDataVersion1
 import org.gradle.tooling.internal.protocol.ConnectionVersion4
-import org.gradle.tooling.internal.protocol.ProjectVersion3
-import org.gradle.tooling.internal.protocol.eclipse.EclipseProjectVersion3
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
-import org.gradle.tooling.model.eclipse.EclipseProject
-import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
-import org.gradle.tooling.model.gradle.GradleBuild
-import org.gradle.tooling.model.idea.BasicIdeaProject
-import org.gradle.tooling.model.idea.IdeaProject
-import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
 import spock.lang.Specification
 
 class ConnectionVersion4BackedConsumerConnectionTest extends Specification {
-    final ConnectionMetaDataVersion1 metaData = Stub()
-    final ConnectionVersion4 target = Mock() {
-        getMetaData() >> metaData
-    }
+    final Distribution distribution = Mock(Distribution)
     final ConsumerOperationParameters parameters = Mock()
-    final ModelMapping modelMapping = Stub()
-    final ProtocolToModelAdapter adapter = Mock()
-
-    def "describes capabilities of a 1.0-m3 provider"() {
-        given:
-        metaData.version >> "1.0-milestone-3"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-        def details = connection.versionDetails
-
-        expect:
-        !details.supportsGradleProjectModel()
-
-        and:
-        details.maySupportModel(HierarchicalEclipseProject)
-        details.maySupportModel(EclipseProject)
-        details.maySupportModel(Void)
-
-        and:
-        !details.maySupportModel(IdeaProject)
-        !details.maySupportModel(BasicIdeaProject)
-        !details.maySupportModel(GradleProject)
-        !details.maySupportModel(BuildEnvironment)
-        !details.maySupportModel(ProjectOutcomes)
-        !details.maySupportModel(CustomModel)
-        !details.maySupportModel(GradleBuild)
-    }
-
-    def "describes capabilities of a 1.0-m5 provider"() {
-        given:
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-        def details = connection.versionDetails
-
-        expect:
-        details.supportsGradleProjectModel()
-
-        and:
-        details.maySupportModel(HierarchicalEclipseProject)
-        details.maySupportModel(EclipseProject)
-        details.maySupportModel(IdeaProject)
-        details.maySupportModel(BasicIdeaProject)
-        details.maySupportModel(GradleProject)
-        details.maySupportModel(Void)
-
-        and:
-        !details.maySupportModel(BuildEnvironment)
-        !details.maySupportModel(ProjectOutcomes)
-        !details.maySupportModel(CustomModel)
-        !details.maySupportModel(GradleBuild)
-    }
-
-    def "builds model using connection's getModel() method"() {
-        ProjectVersion3 protocolModel = Mock()
-        GradleProject model = Mock()
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        when:
-        def result = connection.run(GradleProject.class, parameters)
-
-        then:
-        result == model
-
-        and:
-        _ * modelMapping.getProtocolType(GradleProject.class) >> ProjectVersion3.class
-        1 * target.getModel(ProjectVersion3.class, parameters) >> protocolModel
-        1 * adapter.adapt(GradleProject.class, protocolModel, _) >> model
-        0 * target._
-    }
-
-    def "runs build using connection's executeBuild() method"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        when:
-        connection.run(Void.class, parameters)
-
-        then:
-        1 * target.executeBuild(parameters, parameters)
-        0 * target._
-    }
-
-    def "builds partial BuildEnvironment model locally"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-        BuildEnvironment model = Stub()
-
-        when:
-        def result = connection.run(BuildEnvironment.class, parameters)
-
-        then:
-        result == model
-
-        and:
-        1 * adapter.adapt(BuildEnvironment.class, {it instanceof VersionOnlyBuildEnvironment}, _) >> model
-        0 * target._
-    }
-
-    def "builds partial GradleProject model using the Eclipse model for a 1.0-m3 provider"() {
-        metaData.version >> "1.0-milestone-3"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-        EclipseProjectVersion3 protocolModel = Stub()
-        GradleProject model = Stub()
-
-        when:
-        def result = connection.run(GradleProject.class, parameters)
-
-        then:
-        result == model
-
-        and:
-        _ * modelMapping.getProtocolType(EclipseProjectVersion3) >> EclipseProjectVersion3.class
-        1 * target.getModel(EclipseProjectVersion3.class, parameters) >> protocolModel
-        1 * adapter.adapt(EclipseProjectVersion3.class, _, _) >> protocolModel
-        1 * adapter.adapt(GradleProject.class, _) >> model
-        0 * target._
-    }
-
-    def "builds partial GradleBuild model using the Eclipse model for a 1.0-m3 provider"() {
-        metaData.version >> "1.0-milestone-3"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-        EclipseProjectVersion3 protocolModel = Stub()
-        GradleProject gradleProject = Stub()
-        GradleBuild model = Stub()
+    final ConnectionVersion4 connection = Mock()
+    final ConnectionMetaDataVersion1 metaDataVersion1 = Mock()
+    final ProtocolToModelAdapter adapter = new ProtocolToModelAdapter()
 
-        when:
-        def result = connection.run(GradleBuild.class, parameters)
-
-        then:
-        result == model
-
-        and:
-        _ * modelMapping.getProtocolType(EclipseProjectVersion3) >> EclipseProjectVersion3.class
-        1 * target.getModel(EclipseProjectVersion3.class, parameters) >> protocolModel
-        1 * adapter.adapt(EclipseProjectVersion3.class, _, _) >> protocolModel
-        1 * adapter.adapt(GradleProject.class, { it instanceof PartialGradleProject }) >> gradleProject
-        1 * adapter.adapt(GradleBuild.class, { it instanceof DefaultGradleBuild }) >> model
-        0 * adapter._
-        0 * target._
-    }
-
-    def "fails when unknown model is requested"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        when:
-        connection.run(CustomModel.class, parameters)
-
-        then:
-        UnknownModelException e = thrown()
-        e.message == /The version of Gradle you are using (1.0-milestone-5) does not support building a model of type 'CustomModel'. Support for building custom tooling models was added in Gradle 1.6 and is available in all later versions./
+    def setup() {
+        _ * connection.metaData >> metaDataVersion1
+        _ * metaDataVersion1.version >> '1.0-milestoneX'
     }
 
-    def "fails when unsupported model is requested"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        when:
-        connection.run(ProjectOutcomes.class, parameters)
-
-        then:
-        UnknownModelException e = thrown()
-        e.message == /The version of Gradle you are using (1.0-milestone-5) does not support building a model of type 'ProjectOutcomes'. Support for building 'ProjectOutcomes' models was added in Gradle 1.2 and is available in all later versions./
-    }
-
-    def "fails when both tasks and model requested"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        given:
-        parameters.tasks >> ['a']
+    def "run fails"() {
+        def connection = new ConnectionVersion4BackedConsumerConnection(distribution, connection, adapter)
 
         when:
         connection.run(GradleProject.class, parameters)
 
         then:
-        UnsupportedOperationConfigurationException e = thrown()
-        e.message.startsWith("Unsupported configuration: modelBuilder.forTasks()")
-    }
-
-    def "fails when build action requested"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        given:
-        parameters.tasks >> ['a']
-
-        when:
-        connection.run(Stub(BuildAction), parameters)
-
-        then:
         UnsupportedVersionException e = thrown()
-        e.message == /The version of Gradle you are using (1.0-milestone-5) does not support execution of build actions provided by the tooling API client. Support for this was added in Gradle 1.8 and is available in all later versions./
+        e != null
     }
 
-    def "fails when stdin provided"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        given:
-        parameters.standardInput >> new ByteArrayInputStream("hi".bytes)
-
-        when:
-        connection.run(CustomModel.class, parameters)
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        e.message.startsWith("Unsupported configuration: modelBuilder.setStandardInput()")
-    }
-
-    def "fails when Java home specified"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        given:
-        parameters.javaHome >> new File("java-home")
-
-        when:
-        connection.run(CustomModel.class, parameters)
-
-        then:
-        UnsupportedOperationConfigurationException e = thrown()
-        e.message.startsWith("Unsupported configuration: modelBuilder.setJavaHome()")
-    }
-
-    def "fails when JVM args specified"() {
-        metaData.version >> "1.0-milestone-5"
-        def connection = new ConnectionVersion4BackedConsumerConnection(target, modelMapping, adapter)
-
-        given:
-        parameters.jvmArguments >> ['-Dsome.arg']
+    def "partial BuildEnvirnment"() {
+        def connection = new ConnectionVersion4BackedConsumerConnection(distribution, connection, adapter)
 
         when:
-        connection.run(CustomModel.class, parameters)
+        def buildEnv = connection.run(BuildEnvironment.class, parameters)
 
         then:
-        UnsupportedOperationConfigurationException e = thrown()
-        e.message.startsWith("Unsupported configuration: modelBuilder.setJvmArguments()")
+        buildEnv.gradle.gradleVersion == '1.0-milestoneX'
     }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducerTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducerTest.groovy
index 5755976..708f2a3 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducerTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/GradleBuildAdapterProducerTest.groovy
@@ -19,7 +19,6 @@ package org.gradle.tooling.internal.consumer.connection
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails
 import org.gradle.tooling.internal.protocol.ModelBuilder
 import org.gradle.tooling.model.DomainObjectSet
 import org.gradle.tooling.model.GradleProject
@@ -28,28 +27,14 @@ import spock.lang.Specification
 
 class GradleBuildAdapterProducerTest extends Specification {
     ProtocolToModelAdapter adapter = Mock(ProtocolToModelAdapter);
-    VersionDetails versionDetails = Mock(VersionDetails);
     ModelMapping mapping = Mock(ModelMapping);
     ModelBuilder builder = Mock(ModelBuilder);
     ModelProducer delegate = Mock(ModelProducer)
 
-    GradleBuildAdapterProducer modelProducer = new GradleBuildAdapterProducer(adapter, versionDetails, mapping, delegate);
-
-    def "passes request to delegate when supported GradleBuild is requested"() {
-        setup:
-        1 * versionDetails.maySupportModel(GradleBuild.class) >> true
-        GradleBuild gradleBuild = Mock(GradleBuild)
-        ConsumerOperationParameters operationParameters = Mock(ConsumerOperationParameters)
-        when:
-        def model = modelProducer.produceModel(GradleBuild.class, operationParameters)
-        then:
-        1 * delegate.produceModel(GradleBuild, operationParameters) >> gradleBuild
-        model == gradleBuild
-    }
+    GradleBuildAdapterProducer modelProducer = new GradleBuildAdapterProducer(adapter, delegate);
 
     def "requests GradleProject on delegate when unsupported GradleBuild requested"() {
         setup:
-        1 * versionDetails.maySupportModel(GradleBuild) >> false
         GradleProject gradleProject = gradleProject()
         ConsumerOperationParameters operationParameters = Mock(ConsumerOperationParameters)
         adapter.adapt(GradleProject, gradleProject) >> gradleProject
@@ -70,7 +55,6 @@ class GradleBuildAdapterProducerTest extends Specification {
         then:
         1 * delegate.produceModel(SomeModel, operationParameters) >> someModel
         returnValue == someModel
-        0 * versionDetails.maySupportModel(_)
         0 * adapter.adapt(_, _)
     }
 
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnectionTest.groovy
index 068aabf..dabdd0d 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnectionTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/InternalConnectionBackedConsumerConnectionTest.groovy
@@ -29,6 +29,7 @@ import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
 import org.gradle.tooling.model.eclipse.EclipseProject
 import org.gradle.tooling.model.eclipse.HierarchicalEclipseProject
+import org.gradle.tooling.model.gradle.BuildInvocations
 import org.gradle.tooling.model.gradle.GradleBuild
 import org.gradle.tooling.model.idea.BasicIdeaProject
 import org.gradle.tooling.model.idea.IdeaProject
@@ -52,7 +53,8 @@ class InternalConnectionBackedConsumerConnectionTest extends Specification {
         def details = connection.versionDetails
 
         expect:
-        details.supportsGradleProjectModel()
+        !details.supportsTaskDisplayName()
+        !details.supportsCancellation()
 
         and:
         details.maySupportModel(HierarchicalEclipseProject)
@@ -67,6 +69,7 @@ class InternalConnectionBackedConsumerConnectionTest extends Specification {
         !details.maySupportModel(ProjectOutcomes)
         !details.maySupportModel(CustomModel)
         !details.maySupportModel(GradleBuild)
+        !details.maySupportModel(BuildInvocations)
     }
 
     def "builds GradleBuild model by converting GradleProject"() {
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutorTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutorTest.groovy
index faf3ab5..5846ba6 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutorTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/LazyConsumerActionExecutorTest.groovy
@@ -15,7 +15,9 @@
  */
 package org.gradle.tooling.internal.consumer.connection
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.tooling.BuildCancelledException
 import org.gradle.tooling.internal.consumer.ConnectionParameters
 import org.gradle.tooling.internal.consumer.Distribution
 import org.gradle.tooling.internal.consumer.LoggingProvider
@@ -32,6 +34,7 @@ class LazyConsumerActionExecutorTest extends Specification {
     final ConsumerConnection consumerConnection = Mock()
     final LoggingProvider loggingProvider = Mock()
     final ProgressLoggerFactory progressLoggerFactory = Mock()
+    final BuildCancellationToken cancellationToken = Mock()
     final LazyConsumerActionExecutor connection = new LazyConsumerActionExecutor(distribution, implementationLoader, loggingProvider, connectionParams)
 
     def createsConnectionOnDemandToBuildModel() {
@@ -40,36 +43,49 @@ class LazyConsumerActionExecutorTest extends Specification {
 
         then:
         1 * loggingProvider.progressLoggerFactory >> progressLoggerFactory
-        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams) >> consumerConnection
+        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams, cancellationToken) >> consumerConnection
+        _ * cancellationToken.cancellationRequested >> false
+        _ * action.parameters >> params
+        _ * params.cancellationToken >> cancellationToken
         1 * action.run(consumerConnection)
         0 * _._
     }
 
-    def reusesConnection() {
-        def action2 = Mock(ConsumerAction)
-
+    def doesNotInvokeActionRunWhenCancellationRequested() {
         when:
         connection.run(action)
-        connection.run(action2)
 
         then:
-        1 * loggingProvider.getProgressLoggerFactory() >> progressLoggerFactory
-        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams) >> consumerConnection
-        1 * action.run(consumerConnection)
-        1 * action2.run(consumerConnection)
+        _ * cancellationToken.cancellationRequested >> true
+        _ * action.parameters >> params
+        _ * params.cancellationToken >> cancellationToken
         0 * _._
+
+        and:
+        BuildCancelledException e = thrown()
     }
 
-    def stopsConnectionOnStop() {
+    def reusesConnection() {
+        def action2 = Mock(ConsumerAction)
+
         when:
         connection.run(action)
-        connection.stop()
+        connection.run(action2)
 
         then:
         1 * loggingProvider.getProgressLoggerFactory() >> progressLoggerFactory
-        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams) >> consumerConnection
+        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams, cancellationToken) >> consumerConnection
+        1 * cancellationToken.cancellationRequested >> false
+        _ * action.parameters >> params
+        1 * params.cancellationToken >> cancellationToken
         1 * action.run(consumerConnection)
-        1 * consumerConnection.stop()
+        0 * _._
+
+        then:
+        _ * cancellationToken.cancellationRequested >> false
+        _ * action2.parameters >> params
+        _ * params.cancellationToken >> cancellationToken
+        1 * action2.run(consumerConnection)
         0 * _._
     }
 
@@ -91,7 +107,10 @@ class LazyConsumerActionExecutorTest extends Specification {
         RuntimeException e = thrown()
         e == failure
         1 * loggingProvider.getProgressLoggerFactory() >> progressLoggerFactory
-        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams) >> { throw failure }
+        1 * implementationLoader.create(distribution, progressLoggerFactory, connectionParams, cancellationToken) >> { throw failure }
+        _ * cancellationToken.cancellationRequested >> false
+        _ * action.parameters >> params
+        _ * params.cancellationToken >> cancellationToken
 
         when:
         connection.stop()
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnectionTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnectionTest.groovy
index e1719ca..184afa3 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnectionTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ModelBuilderBackedConsumerConnectionTest.groovy
@@ -22,7 +22,6 @@ import org.gradle.tooling.UnsupportedVersionException
 import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
 import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
 import org.gradle.tooling.internal.consumer.versioning.ModelMapping
-import org.gradle.tooling.internal.consumer.versioning.VersionDetails
 import org.gradle.tooling.internal.protocol.*
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.build.BuildEnvironment
@@ -33,27 +32,27 @@ import org.gradle.tooling.model.gradle.GradleBuild
 import org.gradle.tooling.model.idea.BasicIdeaProject
 import org.gradle.tooling.model.idea.IdeaProject
 import org.gradle.tooling.model.internal.outcomes.ProjectOutcomes
-import org.gradle.util.GradleVersion
 import spock.lang.Specification
-import spock.lang.Unroll
 
 class ModelBuilderBackedConsumerConnectionTest extends Specification {
-    final metaData = Stub(ConnectionMetaDataVersion1)
+    final metaData = Stub(ConnectionMetaDataVersion1) {
+        getVersion() >> "1.6"
+    }
     final parameters = Stub(ConsumerOperationParameters)
     final target = Mock(TestModelBuilder) {
         getMetaData() >> metaData
     }
     final adapter = Mock(ProtocolToModelAdapter)
     final modelMapping = Mock(ModelMapping)
+    def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
 
-    def "describes capabilities of a pre 1.8-rc-1 provider"() {
+    def "describes capabilities of the provider"() {
         given:
-        metaData.version >> "1.2"
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
         def details = connection.versionDetails
 
         expect:
-        details.supportsGradleProjectModel()
+        !details.supportsTaskDisplayName()
+        !details.supportsCancellation()
 
         and:
         details.maySupportModel(HierarchicalEclipseProject)
@@ -68,37 +67,12 @@ class ModelBuilderBackedConsumerConnectionTest extends Specification {
 
         and:
         !details.maySupportModel(GradleBuild)
-    }
-
-    def "describes capabilities of a post 1.8-rc-1 provider"() {
-        given:
-        metaData.version >> "1.8-rc-1"
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
-        def details = connection.versionDetails
-
-        expect:
-        details.supportsGradleProjectModel()
-
-        and:
-        details.maySupportModel(HierarchicalEclipseProject)
-        details.maySupportModel(EclipseProject)
-        details.maySupportModel(IdeaProject)
-        details.maySupportModel(BasicIdeaProject)
-        details.maySupportModel(GradleProject)
-        details.maySupportModel(BuildEnvironment)
-        details.maySupportModel(ProjectOutcomes)
-        details.maySupportModel(Void)
-        details.maySupportModel(CustomModel)
-        details.maySupportModel(GradleBuild)
+        !details.maySupportModel(BuildInvocations)
     }
 
     def "maps model type to model identifier"() {
         def modelIdentifier = Stub(ModelIdentifier)
 
-        given:
-        metaData.version >> "1.2"
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
-
         when:
         connection.run(GradleProject, parameters)
 
@@ -113,8 +87,6 @@ class ModelBuilderBackedConsumerConnectionTest extends Specification {
         given:
         _ * modelMapping.getModelIdentifierFromModelType(GradleProject) >> modelIdentifier
         _ * target.getModel(modelIdentifier, parameters) >> { throw new InternalUnsupportedModelException() }
-        _ * metaData.version >> "1.2"
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
 
         when:
         connection.run(GradleProject, parameters)
@@ -130,9 +102,7 @@ class ModelBuilderBackedConsumerConnectionTest extends Specification {
         def gradleProject = Stub(GradleProject.class)
 
         given:
-        _ * metaData.version >> "1.2"
         _ * modelMapping.getModelIdentifierFromModelType(GradleProject) >> modelIdentifier
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
 
         when:
         def result = connection.run(GradleBuild.class, parameters)
@@ -150,36 +120,18 @@ class ModelBuilderBackedConsumerConnectionTest extends Specification {
     def "fails when build action requested"() {
         given:
         parameters.tasks >> ['a']
-        metaData.version >> "1.2"
-        def connection = new ModelBuilderBackedConsumerConnection(target, modelMapping, adapter)
 
         when:
         connection.run(Stub(BuildAction), parameters)
 
         then:
         UnsupportedVersionException e = thrown()
-        e.message == /The version of Gradle you are using (1.2) does not support execution of build actions provided by the tooling API client. Support for this was added in Gradle 1.8 and is available in all later versions./
+        e.message == /The version of Gradle you are using (1.6) does not support execution of build actions provided by the tooling API client. Support for this was added in Gradle 1.8 and is available in all later versions./
     }
 
     interface TestModelBuilder extends ModelBuilder, ConnectionVersion4, ConfigurableConnection {
     }
 
-    @Unroll('VersionDetails for #versionString support expected models')
-    def "VersionDetails supports expected models"() {
-        when:
-        VersionDetails version = ModelBuilderBackedConsumerConnection.getVersionDetails(versionString)
-        def gradleVersion = GradleVersion.version(versionString)
-
-        then:
-        version.maySupportModel(GradleBuild) == gradleVersion.compareTo(GradleVersion.version("1.8")) >= 0
-        version.maySupportModel(BuildInvocations) == gradleVersion.compareTo(GradleVersion.version("1.11")) > 0
-        version.supportsGradleProjectModel() == gradleVersion.compareTo(GradleVersion.version("1.6")) >= 0
-        version.maySupportModel(ModelBuilderBackedConsumerConnectionTest.CustomModel)
-
-        where:
-        versionString << ['1.6', '1.7', '1.8', '1.9', '1.10', '1.11', '1.12']
-    }
-
     static class CustomModel {
 
     }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapterTest.groovy
new file mode 100644
index 0000000..37ede69
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/NonCancellableConsumerConnectionAdapterTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.tooling.internal.consumer.connection
+
+import org.gradle.initialization.DefaultBuildCancellationToken
+import org.gradle.logging.ConfigureLogging
+import org.gradle.logging.TestOutputEventListener
+import org.gradle.tooling.BuildAction
+import org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters
+import org.junit.Rule
+import spock.lang.Specification
+
+class NonCancellableConsumerConnectionAdapterTest extends Specification {
+    final target = Mock(ConsumerConnection)
+    final connection = new NonCancellableConsumerConnectionAdapter(target)
+    final outputEventListener = new TestOutputEventListener()
+    @Rule ConfigureLogging logging = new ConfigureLogging(outputEventListener)
+
+    def "delegates to connection to run build action"() {
+        def action = Mock(BuildAction)
+        def parameters = Stub(ConsumerOperationParameters)
+
+        when:
+        def result = connection.run(action, parameters)
+
+        then:
+        result == 'result'
+
+        and:
+        1 * target.run(action, parameters) >> 'result'
+    }
+
+    def "logs when cancelled"() {
+        def action = Mock(BuildAction)
+        def cancellation = new DefaultBuildCancellationToken()
+        def parameters = Stub(ConsumerOperationParameters) {
+            getCancellationToken() >> cancellation
+        }
+
+        given:
+        _ * target.run(action, parameters) >> {
+            cancellation.doCancel()
+            'result'
+        }
+
+        when:
+        def result = connection.run(action, parameters)
+
+        then:
+        result == 'result'
+
+        and:
+        cancellation.cancellationRequested
+        outputEventListener.toString().contains('Note: Version of Gradle provider does not support cancellation.')
+    }
+
+    def "no logging when cancelled after action"() {
+        def action = Mock(BuildAction)
+        def cancellation = new DefaultBuildCancellationToken()
+        def parameters = Stub(ConsumerOperationParameters) {
+            getCancellationToken() >> cancellation
+        }
+
+        when:
+        def result = connection.run(action, parameters)
+        cancellation.doCancel()
+
+        then:
+        result == 'result'
+
+        and:
+        1 * target.run(action, parameters) >> 'result'
+        cancellation.cancellationRequested
+        outputEventListener.toString().empty
+    }
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutorTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutorTest.groovy
index e54567e..3ddc28a 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutorTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/connection/ProgressLoggingConsumerActionExecutorTest.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.tooling.internal.consumer.connection
 
-import org.gradle.listener.ListenerManager
+import org.gradle.internal.event.ListenerManager
 import org.gradle.logging.ProgressLogger
 import org.gradle.logging.ProgressLoggerFactory
 import org.gradle.tooling.internal.consumer.LoggingProvider
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverterTest.groovy
index 42c4a1e..1a9d11e 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverterTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/converters/BuildInvocationsConverterTest.groovy
@@ -16,8 +16,7 @@
 
 package org.gradle.tooling.internal.consumer.converters
 
-import org.gradle.tooling.internal.gradle.BasicGradleTaskSelector
-import org.gradle.tooling.internal.gradle.DefaultBuildInvocations
+import org.gradle.tooling.internal.gradle.ConsumerProvidedTaskSelector
 import org.gradle.tooling.model.DomainObjectSet
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.GradleTask
@@ -30,9 +29,9 @@ class BuildInvocationsConverterTest extends Specification {
         _ * project.children >> ([] as DomainObjectSet)
         _ * project.tasks >> ([] as DomainObjectSet)
         when:
-        DefaultBuildInvocations builds = new BuildInvocationsConverter().convert(project)
+        def builds = new BuildInvocationsConverter().convert(project)
         then:
-        builds.selectors.isEmpty()
+        builds.taskSelectors.isEmpty()
     }
 
     def "converts child projects"() {
@@ -45,7 +44,7 @@ class BuildInvocationsConverterTest extends Specification {
         _ * rootProject.children >> ([sub1] as DomainObjectSet)
         _ * rootProject.tasks >> ([] as DomainObjectSet)
         when:
-        DefaultBuildInvocations builds = new BuildInvocationsConverter().convert(rootProject)
+        def builds = new BuildInvocationsConverter().convert(rootProject)
         then:
         builds.taskSelectors.size() == 1
         builds.taskSelectors*.name as Set == ['t1'] as Set
@@ -70,14 +69,14 @@ class BuildInvocationsConverterTest extends Specification {
         _ * project.children >> ([child1] as DomainObjectSet)
 
         when:
-        DefaultBuildInvocations builds = new BuildInvocationsConverter().convert(project)
+        def builds = new BuildInvocationsConverter().convert(project)
 
         then:
         builds.taskSelectors.size() == 2
-        builds.taskSelectors.find { BasicGradleTaskSelector it ->
+        builds.taskSelectors.find { ConsumerProvidedTaskSelector it ->
             it.name == 't1'
         }?.taskNames == [':child1:child1a:t1', ':child1:child1b:t1'] as Set
-        builds.taskSelectors.find { BasicGradleTaskSelector it ->
+        builds.taskSelectors.find { ConsumerProvidedTaskSelector it ->
             it.name == 't2'
         }?.taskNames == [':child1:child1b:t2'] as Set
         builds.taskSelectors*.name.each { it != null }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoaderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoaderTest.groovy
index 8db5b02..48f9558 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoaderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/CachingToolingImplementationLoaderTest.groovy
@@ -15,6 +15,7 @@
  */
 package org.gradle.tooling.internal.consumer.loader
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.internal.classpath.DefaultClassPath
 import org.gradle.logging.ProgressLoggerFactory
 import org.gradle.tooling.internal.consumer.ConnectionParameters
@@ -26,60 +27,87 @@ class CachingToolingImplementationLoaderTest extends Specification {
     final ToolingImplementationLoader target = Mock()
     final ProgressLoggerFactory loggerFactory = Mock()
     final ConnectionParameters params = Mock()
+    final BuildCancellationToken cancellationToken = Mock()
     final CachingToolingImplementationLoader loader = new CachingToolingImplementationLoader(target)
 
     def delegatesToTargetLoaderToCreateImplementation() {
-        final Distribution distribution = Mock()
-        final ConsumerConnection connection = Mock()
-        final File userHomeDir = Mock()
+        def distribution = Mock(Distribution)
+        def connection = Mock(ConsumerConnection)
+        def userHomeDir = new File("user-home")
 
         when:
-        def impl = loader.create(distribution, loggerFactory, params)
+        def impl = loader.create(distribution, loggerFactory, params, cancellationToken)
 
         then:
         impl == connection
-        1 * target.create(distribution, loggerFactory, params) >> connection
+        1 * target.create(distribution, loggerFactory, params, cancellationToken) >> connection
         1 * params.getGradleUserHomeDir() >> userHomeDir
-        _ * distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir) >> new DefaultClassPath(new File('a.jar'))
+        _ * distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir, cancellationToken) >> new DefaultClassPath(new File('a.jar'))
         0 * _._
     }
 
     def reusesImplementationWithSameClasspath() {
-        final Distribution distribution = Mock()
-        final ConsumerConnection connection = Mock()
-        final File userHomeDir = Mock()
+        def distribution = Mock(Distribution)
+        def connection = Mock(ConsumerConnection)
+        def userHomeDir = new File("user-home")
 
         when:
-        def impl = loader.create(distribution, loggerFactory, params)
-        def impl2 = loader.create(distribution, loggerFactory, params)
+        def impl = loader.create(distribution, loggerFactory, params, cancellationToken)
+        def impl2 = loader.create(distribution, loggerFactory, params, cancellationToken)
 
         then:
         impl == connection
         impl2 == connection
-        1 * target.create(distribution, loggerFactory, params) >> connection
+        1 * target.create(distribution, loggerFactory, params, cancellationToken) >> connection
         2 * params.getGradleUserHomeDir() >> userHomeDir
-        _ * distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir) >> { new DefaultClassPath(new File('a.jar')) }
+        _ * distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir, cancellationToken) >> { new DefaultClassPath(new File('a.jar')) }
         0 * _._
     }
 
     def createsNewImplementationWhenClasspathNotSeenBefore() {
-        ConsumerConnection connection1 = Mock()
-        ConsumerConnection connection2 = Mock()
-        Distribution distribution1 = Mock()
-        Distribution distribution2 = Mock()
+        def connection1 = Mock(ConsumerConnection)
+        def connection2 = Mock(ConsumerConnection)
+        def distribution1 = Mock(Distribution)
+        def distribution2 = Mock(Distribution)
 
         when:
-        def impl = loader.create(distribution1, loggerFactory, params)
-        def impl2 = loader.create(distribution2, loggerFactory, params)
+        def impl = loader.create(distribution1, loggerFactory, params, cancellationToken)
+        def impl2 = loader.create(distribution2, loggerFactory, params, cancellationToken)
 
         then:
         impl == connection1
         impl2 == connection2
-        1 * target.create(distribution1, loggerFactory, params) >> connection1
-        1 * target.create(distribution2, loggerFactory, params) >> connection2
+        1 * target.create(distribution1, loggerFactory, params, cancellationToken) >> connection1
+        1 * target.create(distribution2, loggerFactory, params, cancellationToken) >> connection2
         2 * params.getGradleUserHomeDir() >> null
-        _ * distribution1.getToolingImplementationClasspath(loggerFactory, null) >> new DefaultClassPath(new File('a.jar'))
-        _ * distribution2.getToolingImplementationClasspath(loggerFactory, null) >> new DefaultClassPath(new File('b.jar'))
+        _ * distribution1.getToolingImplementationClasspath(loggerFactory, null, cancellationToken) >> new DefaultClassPath(new File('a.jar'))
+        _ * distribution2.getToolingImplementationClasspath(loggerFactory, null, cancellationToken) >> new DefaultClassPath(new File('b.jar'))
         0 * _._
     }
+
+    def closesConnectionsWhenClosed() {
+        def connection1 = Mock(ConsumerConnection)
+        def connection2 = Mock(ConsumerConnection)
+        def distribution1 = Mock(Distribution)
+        def distribution2 = Mock(Distribution)
+
+        given:
+        loader.create(distribution1, loggerFactory, params, cancellationToken)
+        loader.create(distribution2, loggerFactory, params, cancellationToken)
+        loader.create(distribution1, loggerFactory, params, cancellationToken)
+
+        _ * target.create(distribution1, loggerFactory, params, cancellationToken) >> connection1
+        _ * target.create(distribution2, loggerFactory, params, cancellationToken) >> connection2
+        _ * params.getGradleUserHomeDir() >> null
+        _ * distribution1.getToolingImplementationClasspath(loggerFactory, null, cancellationToken) >> new DefaultClassPath(new File('a.jar'))
+        _ * distribution2.getToolingImplementationClasspath(loggerFactory, null, cancellationToken) >> new DefaultClassPath(new File('b.jar'))
+
+        when:
+        loader.close()
+
+        then:
+        connection1.stop()
+        connection2.stop()
+        0 * _
+    }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoaderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoaderTest.groovy
index 1e42e04..eb29b22 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoaderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/DefaultToolingImplementationLoaderTest.groovy
@@ -15,14 +15,15 @@
  */
 package org.gradle.tooling.internal.consumer.loader
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.internal.classloader.ClasspathUtil
 import org.gradle.internal.classpath.DefaultClassPath
 import org.gradle.logging.ProgressLoggerFactory
 import org.gradle.messaging.actor.ActorFactory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.tooling.internal.consumer.ConnectionParameters
 import org.gradle.tooling.internal.consumer.Distribution
 import org.gradle.tooling.internal.consumer.connection.*
-import org.gradle.tooling.internal.consumer.ConnectionParameters
 import org.gradle.tooling.internal.protocol.*
 import org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException
 import org.gradle.util.GradleVersion
@@ -39,37 +40,58 @@ class DefaultToolingImplementationLoaderTest extends Specification {
         getVerboseLogging() >> true
     }
     File userHomeDir = Mock()
+    final BuildCancellationToken cancellationToken = Mock()
     final loader = new DefaultToolingImplementationLoader()
 
     def "locates connection implementation using meta-inf service then instantiates and configures the connection"() {
         given:
-        distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir) >> new DefaultClassPath(
+        distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir, cancellationToken) >> new DefaultClassPath(
                 getToolingApiResourcesDir(connectionImplementation),
                 ClasspathUtil.getClasspathForClass(TestConnection.class),
                 ClasspathUtil.getClasspathForClass(ActorFactory.class),
                 ClasspathUtil.getClasspathForClass(Logger.class),
                 ClasspathUtil.getClasspathForClass(GroovyObject.class),
-                getVersionResourcesDir(),
                 ClasspathUtil.getClasspathForClass(GradleVersion.class))
 
         when:
-        def adaptedConnection = loader.create(distribution, loggerFactory, connectionParameters)
+        def adaptedConnection = loader.create(distribution, loggerFactory, connectionParameters, cancellationToken)
 
         then:
-        adaptedConnection.delegate.class != connectionImplementation //different classloaders
-        adaptedConnection.delegate.class.name == connectionImplementation.name
-        adaptedConnection.delegate.configured
+        def consumerConnection = wrappedToNonCancellableAdapter ? adaptedConnection.delegate : adaptedConnection
+        consumerConnection.delegate.class != connectionImplementation //different classloaders
+        consumerConnection.delegate.class.name == connectionImplementation.name
+        consumerConnection.delegate.configured
 
         and:
-        adaptedConnection.class == adapter
+        wrappedToNonCancellableAdapter  || adaptedConnection.class == adapter
+        !wrappedToNonCancellableAdapter || adaptedConnection.class == NonCancellableConsumerConnectionAdapter
+        !wrappedToNonCancellableAdapter || adaptedConnection.delegate.class == adapter
 
         where:
-        connectionImplementation  | adapter
-        TestConnection.class      | ActionAwareConsumerConnection.class
-        TestR16Connection.class   | ModelBuilderBackedConsumerConnection.class
-        TestR12Connection.class   | BuildActionRunnerBackedConsumerConnection.class
-        TestR10M8Connection.class | InternalConnectionBackedConsumerConnection.class
-        TestR10M3Connection.class | ConnectionVersion4BackedConsumerConnection.class
+        connectionImplementation  | adapter                                          | wrappedToNonCancellableAdapter
+        TestConnection.class      | ShutdownAwareConsumerConnection.class            | false
+        TestR21Connection.class   | CancellableConsumerConnection.class              | false
+        TestR18Connection.class   | ActionAwareConsumerConnection.class              | true
+        TestR16Connection.class   | ModelBuilderBackedConsumerConnection.class       | true
+        TestR12Connection.class   | BuildActionRunnerBackedConsumerConnection.class  | true
+        TestR10M8Connection.class | InternalConnectionBackedConsumerConnection.class | true
+    }
+
+    def "locates connection implementation using meta-inf service for deprecated connection"() {
+        given:
+        distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir, cancellationToken) >> new DefaultClassPath(
+                getToolingApiResourcesDir(TestR10M3Connection.class),
+                ClasspathUtil.getClasspathForClass(TestConnection.class),
+                ClasspathUtil.getClasspathForClass(ActorFactory.class),
+                ClasspathUtil.getClasspathForClass(Logger.class),
+                ClasspathUtil.getClasspathForClass(GroovyObject.class),
+                ClasspathUtil.getClasspathForClass(GradleVersion.class))
+
+        when:
+        def adaptedConnection = loader.create(distribution, loggerFactory, connectionParameters, cancellationToken)
+
+        then:
+        adaptedConnection.class == ConnectionVersion4BackedConsumerConnection.class
     }
 
     private getToolingApiResourcesDir(Class implementation) {
@@ -77,24 +99,26 @@ class DefaultToolingImplementationLoaderTest extends Specification {
         return tmpDir.testDirectory;
     }
 
-    private getVersionResourcesDir() {
-        return ClasspathUtil.getClasspathForResource(getClass().classLoader, "org/gradle/build-receipt.properties")
-    }
-
     def "creates broken connection when resource not found"() {
         def loader = new DefaultToolingImplementationLoader()
 
         given:
-        distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir) >> new DefaultClassPath()
+        distribution.getToolingImplementationClasspath(loggerFactory, userHomeDir, cancellationToken) >> new DefaultClassPath()
 
         expect:
-        loader.create(distribution, loggerFactory, connectionParameters) instanceof NoToolingApiConnection
+        loader.create(distribution, loggerFactory, connectionParameters, cancellationToken) instanceof NoToolingApiConnection
     }
 }
 
 class TestMetaData implements ConnectionMetaDataVersion1 {
+    private final String version;
+
+    TestMetaData(String version) {
+        this.version = version
+    }
+
     String getVersion() {
-        return "1.1"
+        return version
     }
 
     String getDisplayName() {
@@ -102,16 +126,53 @@ class TestMetaData implements ConnectionMetaDataVersion1 {
     }
 }
 
-class TestConnection extends TestR16Connection implements InternalBuildActionExecutor {
+class TestConnection extends TestR21Connection implements StoppableConnection {
+    @Override
+    void shutdown(ShutdownParameters parameters) {
+        throw new UnsupportedOperationException()
+    }
+
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('2.2')
+    }
+}
+
+class TestR21Connection extends TestR18Connection implements InternalCancellableConnection {
+    @Override
+    BuildResult<?> getModel(ModelIdentifier modelIdentifier, InternalCancellationToken cancellationToken, BuildParameters operationParameters)
+            throws BuildExceptionVersion1, InternalUnsupportedModelException, InternalUnsupportedBuildArgumentException, IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    def <T> BuildResult<T> run(InternalBuildAction<T> action, InternalCancellationToken cancellationToken, BuildParameters operationParameters)
+            throws BuildExceptionVersion1, InternalUnsupportedBuildArgumentException, InternalBuildActionFailureException, IllegalStateException {
+        throw new UnsupportedOperationException();
+    }
+
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('2.1')
+    }
+}
+
+class TestR18Connection extends TestR16Connection implements InternalBuildActionExecutor {
     def <T> BuildResult<T> run(InternalBuildAction<T> action, BuildParameters operationParameters) throws BuildExceptionVersion1, InternalUnsupportedBuildArgumentException, IllegalStateException {
         throw new UnsupportedOperationException()
     }
+
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('1.8')
+    }
 }
 
 class TestR16Connection extends TestR12Connection implements ModelBuilder {
     BuildResult<Object> getModel(ModelIdentifier modelIdentifier, BuildParameters operationParameters) throws UnsupportedOperationException, IllegalStateException {
         throw new UnsupportedOperationException()
     }
+
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('1.6')
+    }
 }
 
 class TestR12Connection extends TestR10M8Connection implements BuildActionRunner, ConfigurableConnection {
@@ -124,6 +185,10 @@ class TestR12Connection extends TestR10M8Connection implements BuildActionRunner
         throw new UnsupportedOperationException()
     }
 
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('1.2')
+    }
+
     def <T> BuildResult<T> run(Class<T> type, BuildParameters parameters) {
         throw new UnsupportedOperationException()
     }
@@ -137,6 +202,10 @@ class TestR10M8Connection extends TestR10M3Connection implements InternalConnect
     void configureLogging(boolean verboseLogging) {
         configured = verboseLogging
     }
+
+    ConnectionMetaDataVersion1 getMetaData() {
+        return new TestMetaData('1.0-milestone-8')
+    }
 }
 
 class TestR10M3Connection implements ConnectionVersion4 {
@@ -151,7 +220,7 @@ class TestR10M3Connection implements ConnectionVersion4 {
     }
 
     ConnectionMetaDataVersion1 getMetaData() {
-        return new TestMetaData()
+        return new TestMetaData('1.0-milestone-3')
     }
 
     ProjectVersion3 getModel(Class<? extends ProjectVersion3> type, BuildOperationParametersVersion1 operationParameters) {
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoaderTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoaderTest.groovy
index 49ee0de..7bfc248 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoaderTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/loader/SynchronizedToolingImplementationLoaderTest.groovy
@@ -16,77 +16,61 @@
 
 package org.gradle.tooling.internal.consumer.loader
 
+import org.gradle.initialization.BuildCancellationToken
 import org.gradle.logging.ProgressLogger
 import org.gradle.logging.ProgressLoggerFactory
-import org.gradle.test.fixtures.ConcurrentTestUtil
+import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.gradle.tooling.internal.consumer.ConnectionParameters
 import org.gradle.tooling.internal.consumer.Distribution
-import spock.lang.Specification
+import org.gradle.tooling.internal.consumer.connection.ConsumerConnection
 
-import java.util.concurrent.locks.Lock
-import java.util.concurrent.locks.ReentrantLock
-
-public class SynchronizedToolingImplementationLoaderTest extends Specification {
+public class SynchronizedToolingImplementationLoaderTest extends ConcurrentSpec {
 
     def factory = Mock(ProgressLoggerFactory)
     def distro = Mock(Distribution)
     def logger = Mock(ProgressLogger)
     def params = Mock(ConnectionParameters)
+    def cancellationToken = Mock(BuildCancellationToken)
+    def target = Mock(ToolingImplementationLoader)
+    def loader = new SynchronizedToolingImplementationLoader(target)
 
-    def loader = new SynchronizedToolingImplementationLoader(Mock(ToolingImplementationLoader))
-
-    def setup() {
-        loader.lock = Mock(Lock)
-    }
-
-    def "reports progress when busy"() {
+    def "blocks and reports progress when busy"() {
         when:
-        loader.create(distro, factory, params)
-
-        then: "stubs"
-        1 * loader.lock.tryLock() >> false
-        1 * factory.newOperation(_ as Class) >> logger
+        start {
+            loader.create(distro, factory, params, cancellationToken)
+        }
+        async {
+            thread.blockUntil.busy
+            loader.create(distro, factory, params, cancellationToken)
+        }
 
         then:
+        instant.idle < instant.created
+        1 * target.create(distro, factory, params, cancellationToken) >> {
+            instant.busy
+            thread.block()
+            instant.idle
+            Stub(ConsumerConnection)
+        }
+        1 * target.create(distro, factory, params, cancellationToken) >> {
+            instant.created
+            Stub(ConsumerConnection)
+        }
+
+        and:
+        1 * factory.newOperation(_ as Class) >> logger
         1 * logger.setDescription(_ as String)
-        then:
         1 * logger.started()
-        then:
-        1 * loader.lock.lock()
-        then:
-        1 * loader.delegate.create(distro, factory, params)
-        then:
         1 * logger.completed()
-        1 * loader.lock.unlock()
         0 * _
     }
 
     def "does not report progress when appropriate"() {
         when:
-        loader.create(distro, factory, params)
+        loader.create(distro, factory, params, cancellationToken)
 
         then:
-        1 * loader.lock.tryLock() >> true
-        then:
-        1 * loader.delegate.create(distro, factory, params)
-        then:
-        1 * loader.lock.unlock()
+        1 * target.create(distro, factory, params, cancellationToken)
         0 * _
     }
-
-    def concurrent = new ConcurrentTestUtil()
-
-    def "is thread safe"() {
-        given:
-        loader.lock = new ReentrantLock()
-        factory.newOperation(_ as Class) >> logger
-
-        when:
-        5.times {
-            concurrent.start { loader.create(distro, factory, params) }
-        }
-
-        then:
-        concurrent.finished()
-    }
 }
\ No newline at end of file
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapterTest.groovy
new file mode 100644
index 0000000..8bab478
--- /dev/null
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/BuildProgressListenerAdapterTest.groovy
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.tooling.internal.consumer.parameters
+
+import org.gradle.tooling.events.FinishEvent
+import org.gradle.tooling.events.StartEvent
+import org.gradle.tooling.events.test.*
+import org.gradle.tooling.internal.protocol.*
+import org.gradle.tooling.internal.protocol.events.InternalJvmTestDescriptor
+import org.gradle.tooling.internal.protocol.events.InternalTestDescriptor
+import org.gradle.tooling.internal.protocol.events.InternalTestFailureResult
+import org.gradle.tooling.internal.protocol.events.InternalTestFinishedProgressEvent
+import org.gradle.tooling.internal.protocol.events.InternalTestProgressEvent
+import org.gradle.tooling.internal.protocol.events.InternalTestSkippedResult
+import org.gradle.tooling.internal.protocol.events.InternalTestStartedProgressEvent
+import org.gradle.tooling.internal.protocol.events.InternalTestSuccessResult
+import spock.lang.Specification
+
+class BuildProgressListenerAdapterTest extends Specification {
+
+    def "adapter is only subscribing to test progress events if at least one test progress listener is attached"() {
+        when:
+        def adapter = new BuildProgressListenerAdapter([])
+
+        then:
+        adapter.getSubscribedOperations() == []
+
+        when:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        adapter = new BuildProgressListenerAdapter([listener])
+
+        then:
+        adapter.getSubscribedOperations() == [InternalBuildProgressListener.TEST_EXECUTION]
+    }
+
+    def "only TestProgressEventVersionX instances are processed"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        adapter.onEvent(new Object())
+
+        then:
+        0 * listener.statusChanged(_)
+    }
+
+    def "only TestProgressEventVersionX instances of known type are processed"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def unknownEvent = Mock(InternalTestProgressEvent)
+        adapter.onEvent(unknownEvent)
+
+        then:
+        0 * listener.statusChanged(_)
+    }
+
+    def "conversion of start events throws exception if previous start event with same test descriptor exists"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        adapter.onEvent(startEvent)
+        adapter.onEvent(startEvent)
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message.contains('already available')
+    }
+
+    def "conversion of non-start events throws exception if no previous start event with same test descriptor exists"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getParentId() >> null
+
+        def skippedEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * skippedEvent.getEventTime() >> 999
+        _ * skippedEvent.getDescriptor() >> testDescriptor
+
+        adapter.onEvent(skippedEvent)
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message.contains('not available')
+    }
+
+    def "conversion of child events throws exception if no previous parent event exists"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def childTestDescriptor = Mock(InternalTestDescriptor)
+        _ * childTestDescriptor.getId() >> 2
+        _ * childTestDescriptor.getName() >> 'some child'
+        _ * childTestDescriptor.getParentId() >> 1
+
+        def childEvent = Mock(InternalTestStartedProgressEvent)
+        _ * childEvent.getEventTime() >> 999
+        _ * childEvent.getDescriptor() >> childTestDescriptor
+
+        adapter.onEvent(childEvent)
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message.contains('not available')
+    }
+
+    def "conversion of child events expects parent event exists"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def parentTestDescriptor = Mock(InternalTestDescriptor)
+        _ * parentTestDescriptor.getId() >> 1
+        _ * parentTestDescriptor.getName() >> 'some parent'
+        _ * parentTestDescriptor.getParentId() >> null
+
+        def parentEvent = Mock(InternalTestStartedProgressEvent)
+        _ * parentEvent.getEventTime() >> 999
+        _ * parentEvent.getDescriptor() >> parentTestDescriptor
+
+        def childTestDescriptor = Mock(InternalTestDescriptor)
+        _ * childTestDescriptor.getId() >> 2
+        _ * childTestDescriptor.getName() >> 'some child'
+        _ * childTestDescriptor.getParentId() >> parentTestDescriptor.getId()
+
+        def childEvent = Mock(InternalTestStartedProgressEvent)
+        _ * childEvent.getEventTime() >> 999
+        _ * childEvent.getDescriptor() >> childTestDescriptor
+
+        adapter.onEvent(parentEvent)
+        adapter.onEvent(childEvent)
+
+        then:
+        notThrown(IllegalStateException)
+    }
+
+    def "convert all JvmTestDescriptorVersion1 attributes"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getDisplayName() >> 'some test suite in human readable form'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_SUITE
+        _ * testDescriptor.getSuiteName() >> 'some suite'
+        _ * testDescriptor.getClassName() >> 'some class'
+        _ * testDescriptor.getMethodName() >> 'some method'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDisplayName() >> 'test suite started'
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        adapter.onEvent(startEvent)
+
+        then:
+        1 * listener.statusChanged(_ as StartEvent) >> { StartEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test suite started"
+            assert event.descriptor.name == 'some test suite'
+            assert event.descriptor.displayName == 'some test suite in human readable form'
+            assert event.descriptor.jvmTestKind == JvmTestKind.SUITE
+            assert event.descriptor.suiteName == 'some suite'
+            assert event.descriptor.className == 'some class'
+            assert event.descriptor.methodName == 'some method'
+            assert event.descriptor.parent == null
+        }
+    }
+
+    def "convert to TestSuiteStartedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_SUITE
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDisplayName() >> 'test suite started'
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        adapter.onEvent(startEvent)
+
+        then:
+        1 * listener.statusChanged(_ as StartEvent) >> { StartEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test suite started"
+            assert event.descriptor.name == 'some test suite'
+            assert event.descriptor.jvmTestKind == JvmTestKind.SUITE
+            assert event.descriptor.parent == null
+        }
+    }
+
+    def "convert to TestSuiteSkippedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_SUITE
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestSkippedResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+
+        def skippedEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * skippedEvent.getEventTime() >> 999
+        _ * skippedEvent.getDisplayName() >> 'test suite skipped'
+        _ * skippedEvent.getDescriptor() >> testDescriptor
+        _ * skippedEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // skippedEvent always assumes a previous startEvent
+        adapter.onEvent(skippedEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test suite skipped"
+            assert event.descriptor.name == 'some test suite'
+            assert event.descriptor.jvmTestKind == JvmTestKind.SUITE
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestSkippedResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+        }
+    }
+
+    def "convert to TestSuiteSucceededEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_SUITE
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestSuccessResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+
+        def succeededEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * succeededEvent.getEventTime() >> 999
+        _ * succeededEvent.getDisplayName() >> 'test suite succeeded'
+        _ * succeededEvent.getDescriptor() >> testDescriptor
+        _ * succeededEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // succeededEvent always assumes a previous startEvent
+        adapter.onEvent(succeededEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test suite succeeded"
+            assert event.descriptor.name == 'some test suite'
+            assert event.descriptor.jvmTestKind == JvmTestKind.SUITE
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestSuccessResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+        }
+    }
+
+    def "convert to TestSuiteFailedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test suite'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_SUITE
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestFailureResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+        _ * testResult.getFailures() >> [Stub(InternalFailure)]
+
+        def failedEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * failedEvent.getEventTime() >> 999
+        _ * failedEvent.getDisplayName() >> 'test suite failed'
+        _ * failedEvent.getDescriptor() >> testDescriptor
+        _ * failedEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // failedEvent always assumes a previous startEvent
+        adapter.onEvent(failedEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test suite failed"
+            assert event.descriptor.name == 'some test suite'
+            assert event.descriptor.jvmTestKind == JvmTestKind.SUITE
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestFailureResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+            assert event.result.failures.size() == 1
+        }
+    }
+
+    def "convert to TestStartedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_ATOMIC
+        _ * testDescriptor.getClassName() >> 'Foo'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDisplayName() >> 'test started'
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        adapter.onEvent(startEvent)
+
+        then:
+        1 * listener.statusChanged(_ as StartEvent) >> { StartEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test started"
+            assert event.descriptor.name == 'some test'
+            assert event.descriptor.jvmTestKind == JvmTestKind.ATOMIC
+            assert event.descriptor.className == 'Foo'
+            assert event.descriptor.parent == null
+        }
+    }
+
+    def "convert to TestSkippedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_ATOMIC
+        _ * testDescriptor.getClassName() >> 'Foo'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestSkippedResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+
+        def skippedEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * skippedEvent.getEventTime() >> 999
+        _ * skippedEvent.getDisplayName() >> 'test skipped'
+        _ * skippedEvent.getDescriptor() >> testDescriptor
+        _ * skippedEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // skippedEvent always assumes a previous startEvent
+        adapter.onEvent(skippedEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test skipped"
+            assert event.descriptor.name == 'some test'
+            assert event.descriptor.jvmTestKind == JvmTestKind.ATOMIC
+            assert event.descriptor.className == 'Foo'
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestSkippedResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+        }
+    }
+
+    def "convert to TestSucceededEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_ATOMIC
+        _ * testDescriptor.getClassName() >> 'Foo'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestSuccessResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+
+        def succeededEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * succeededEvent.getEventTime() >> 999
+        _ * succeededEvent.getDisplayName() >> 'test succeeded'
+        _ * succeededEvent.getDescriptor() >> testDescriptor
+        _ * succeededEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // succeededEvent always assumes a previous startEvent
+        adapter.onEvent(succeededEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test succeeded"
+            assert event.descriptor.name == 'some test'
+            assert event.descriptor.jvmTestKind == JvmTestKind.ATOMIC
+            assert event.descriptor.className == 'Foo'
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestSuccessResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+        }
+    }
+
+    def "convert to TestFailedEvent"() {
+        given:
+        final TestProgressListener listener = Mock(TestProgressListener)
+        def adapter = new BuildProgressListenerAdapter([listener])
+
+        when:
+        def testDescriptor = Mock(InternalJvmTestDescriptor)
+        _ * testDescriptor.getId() >> 1
+        _ * testDescriptor.getName() >> 'some test'
+        _ * testDescriptor.getTestKind() >> InternalJvmTestDescriptor.KIND_ATOMIC
+        _ * testDescriptor.getClassName() >> 'Foo'
+        _ * testDescriptor.getParentId() >> null
+
+        def startEvent = Mock(InternalTestStartedProgressEvent)
+        _ * startEvent.getEventTime() >> 999
+        _ * startEvent.getDescriptor() >> testDescriptor
+
+        def testResult = Mock(InternalTestFailureResult)
+        _ * testResult.getStartTime() >> 1
+        _ * testResult.getEndTime() >> 2
+        _ * testResult.getFailures() >> [Stub(InternalFailure)]
+
+        def failedEvent = Mock(InternalTestFinishedProgressEvent)
+        _ * failedEvent.getEventTime() >> 999
+        _ * failedEvent.getDisplayName() >> 'test failed'
+        _ * failedEvent.getDescriptor() >> testDescriptor
+        _ * failedEvent.getResult() >> testResult
+
+        adapter.onEvent(startEvent) // failedEvent always assumes a previous startEvent
+        adapter.onEvent(failedEvent)
+
+        then:
+        1 * listener.statusChanged(_ as FinishEvent) >> { FinishEvent event ->
+            assert event.eventTime == 999
+            assert event.displayName == "test failed"
+            assert event.descriptor.name == 'some test'
+            assert event.descriptor.jvmTestKind == JvmTestKind.ATOMIC
+            assert event.descriptor.className == 'Foo'
+            assert event.descriptor.parent == null
+            assert event.result instanceof TestFailureResult
+            assert event.result.startTime == 1
+            assert event.result.endTime == 2
+            assert event.result.failures.size() == 1
+        }
+    }
+
+}
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParametersTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParametersTest.groovy
index 0725563..dd52ae2 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParametersTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ConsumerOperationParametersTest.groovy
@@ -16,13 +16,17 @@
 
 package org.gradle.tooling.internal.consumer.parameters
 
+import com.google.common.collect.Sets
+import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
+import org.gradle.tooling.internal.gradle.TaskListingLaunchable
+import org.gradle.tooling.internal.protocol.InternalLaunchable
+import org.gradle.tooling.model.TaskSelector
 import spock.lang.Specification
 
 class ConsumerOperationParametersTest extends Specification {
-    
-    def params = ConsumerOperationParameters.builder()
-    
+
     def "null or empty arguments have the same meaning"() {
+        def params = ConsumerOperationParameters.builder()
         when:
         params.arguments = null
 
@@ -43,6 +47,7 @@ class ConsumerOperationParametersTest extends Specification {
     }
 
     def "null or empty jvm arguments have the same meaning"() {
+        def params = ConsumerOperationParameters.builder()
         when:
         params.jvmArguments = null
 
@@ -61,4 +66,52 @@ class ConsumerOperationParametersTest extends Specification {
         then:
         params.build().jvmArguments == ['-Xmx']
     }
+
+    def "task names and empty launchables"() {
+        def builder = ConsumerOperationParameters.builder()
+        when:
+        builder.tasks = ['a', 'b']
+        def params = builder.build()
+
+        then:
+        params.tasks == ['a', 'b']
+        params.launchables == null
+    }
+
+    def "launchables from provider"() {
+        def builder = ConsumerOperationParameters.builder()
+        when:
+        def launchable1 = Mock(InternalLaunchable)
+        def launchable2 = Mock(InternalLaunchable)
+        builder.launchables = [adapt(launchable1), adapt(launchable2)]
+        def params = builder.build()
+
+        then:
+        params.tasks == []
+        params.launchables == [launchable1, launchable2]
+    }
+
+    def "launchables from adapters"() {
+        def builder = ConsumerOperationParameters.builder()
+        when:
+        def launchable1 = Mock(TaskListingLaunchable)
+        def paths1 = Sets.newTreeSet()
+        paths1.add(':a')
+        _ * launchable1.taskNames >> paths1
+        def launchable2 = Mock(TaskListingLaunchable)
+        def paths2 = Sets.newTreeSet()
+        paths2.add(':b')
+        paths2.add(':lib:b')
+        _ * launchable2.taskNames >> paths2
+        builder.launchables = [adapt(launchable1), adapt(launchable2)]
+        def params = builder.build()
+
+        then:
+        params.tasks == [':a', ':b', ':lib:b']
+        params.launchables == []
+    }
+
+    def adapt(def object) {
+        return new ProtocolToModelAdapter().adapt(TaskSelector, object)
+    }
 }
diff --git a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapterTest.groovy b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapterTest.groovy
index c5e57ab..1db0e99 100644
--- a/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapterTest.groovy
+++ b/subprojects/tooling-api/src/test/groovy/org/gradle/tooling/internal/consumer/parameters/ProgressListenerAdapterTest.groovy
@@ -20,11 +20,7 @@ import spock.lang.Specification
 
 class ProgressListenerAdapterTest extends Specification {
     final ProgressListener listener = Mock()
-    final ProgressListenerAdapter adapter = new ProgressListenerAdapter()
-
-    def setup() {
-        adapter.add(listener)
-    }
+    ProgressListenerAdapter adapter = new ProgressListenerAdapter(Collections.singletonList(listener))
 
     def notifiesListenerOnOperationStartAndEnd() {
         when:
@@ -58,6 +54,5 @@ class ProgressListenerAdapterTest extends Specification {
         1 * listener.statusChanged({it.description == 'main'})
         1 * listener.statusChanged({it.description == ''})
         0 * _._
-
     }
 }
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpec.java b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpec.java
index d915a14..e64e4ba 100644
--- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpec.java
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/GradleVersionSpec.java
@@ -59,6 +59,13 @@ public class GradleVersionSpec {
                         return element.getBaseVersion().compareTo(minVersion) >= 0;
                     }
                 });
+            } else if (value.startsWith(">")) {
+                final GradleVersion minVersion = GradleVersion.version(value.substring(1));
+                specs.add(new Spec<GradleVersion>() {
+                    public boolean isSatisfiedBy(GradleVersion element) {
+                        return element.getBaseVersion().compareTo(minVersion) > 0;
+                    }
+                });
             } else if (value.startsWith("<=")) {
                 final GradleVersion maxVersion = GradleVersion.version(value.substring(2));
                 specs.add(new Spec<GradleVersion>() {
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestOutputStream.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestOutputStream.groovy
new file mode 100644
index 0000000..9273b8e
--- /dev/null
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestOutputStream.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.fixture
+
+class TestOutputStream extends OutputStream {
+    private final buffer = new ByteArrayOutputStream()
+
+    @Override
+    void write(int b) throws IOException {
+        synchronized (buffer) {
+            buffer.write(b)
+        }
+    }
+
+    @Override
+    String toString() {
+        synchronized (buffer) {
+            return buffer.toString()
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestResultHandler.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestResultHandler.groovy
new file mode 100644
index 0000000..c29689d
--- /dev/null
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/TestResultHandler.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests.tooling.fixture
+
+import org.gradle.tooling.GradleConnectionException
+import org.gradle.tooling.ResultHandler
+
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class TestResultHandler implements ResultHandler<Object> {
+    final latch = new CountDownLatch(1)
+    def failure
+
+    void onComplete(Object result) {
+        latch.countDown()
+    }
+
+    void onFailure(GradleConnectionException failure) {
+        this.failure = failure
+        latch.countDown()
+    }
+
+    def finished() {
+        finished(20)
+    }
+
+    def finished(int seconds) {
+        if (!latch.await(seconds, TimeUnit.SECONDS)) {
+            throw new AssertionError("Timeout waiting for operation to complete.")
+        }
+    }
+}
diff --git a/subprojects/tooling-api/tooling-api.gradle b/subprojects/tooling-api/tooling-api.gradle
index 9e0b22d..0103fb9 100644
--- a/subprojects/tooling-api/tooling-api.gradle
+++ b/subprojects/tooling-api/tooling-api.gradle
@@ -17,12 +17,18 @@ dependencies {
     integTestRuntime project(':buildComparison')
     integTestRuntime project(":ivy")
     integTestRuntime project(":maven")
+    integTestRuntime project(":toolingApiBuilders")
 }
 
 useTestFixtures()
+useTestFixtures(project: ':launcher', sourceSet: "integTest")
 
 integTestTasks.all {
     dependsOn({ rootProject.getTasksByName('publishLocalArchives', true) }, ':distributions:binZip')
+
+    if (isCiServer) {
+        maxParallelForks = Math.min(2, rootProject.maxParallelForks)
+    }
 }
 
 daemonIntegTest {
@@ -51,7 +57,15 @@ task jarjarJar(type: JarJarJar) {
     keep('org.gradle.tooling.**')
 }
 
-sourceJar{
+task publishJar(type: Zip) {
+    appendix = "bundle"
+    destinationDir = jar.destinationDir
+    extension = 'jar'
+    dependsOn jar, jarjarJar
+    from zipTree(jarjarJar.getArchivePath())
+}
+
+sourceJar {
     configurations.compile.allDependencies.withType(ProjectDependency).each {
         from it.dependencyProject.sourceSets.main.groovy.srcDirs
         from it.dependencyProject.sourceSets.main.java.srcDirs
@@ -59,11 +73,11 @@ sourceJar{
 }
 
 artifacts {
-    publishCompileWithProjectJar jarjarJar
-    publishRuntime file: jarjarJar.getArchivePath(), name: archivesBaseName, type: 'jar', builtBy: jarjarJar
+    publishCompileWithProjectJar publishJar
+    publishRuntime file: publishJar.getArchivePath(), name: archivesBaseName, type: 'jar', builtBy: publishJar
 }
 
-configurations.publishRuntime { artifacts.removeAll { it instanceof ArchivePublishArtifact && it.archiveTask == jar } }
+configurations.publishRuntime { artifacts.removeAll { it instanceof ArchivePublishArtifact && it.archiveTask in [jar, jarjarJar] } }
 
 eclipse {
     classpath {
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/BlockingRequestObserver.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/BlockingRequestObserver.java
new file mode 100644
index 0000000..5bede9e
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/BlockingRequestObserver.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests;
+
+import org.gradle.internal.UncheckedException;
+import org.gradle.openapi.external.foundation.RequestObserverVersion1;
+import org.gradle.openapi.external.foundation.RequestVersion1;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BlockingRequestObserver implements RequestObserverVersion1 {
+    private final String typeOfInterest;  //either RequestVersion1.EXECUTION_TYPE or RequestVersion1.REFRESH_TYPE
+    private RequestVersion1 request;
+    private Integer result;
+    private String output;
+    private final Lock lock = new ReentrantLock();
+    private final Condition condition = lock.newCondition();
+    private Throwable failure;
+
+    public BlockingRequestObserver() {
+        this(RequestVersion1.EXECUTION_TYPE);
+    }
+
+    public BlockingRequestObserver(String typeOfInterest) {
+        this.typeOfInterest = typeOfInterest;
+    }
+
+    public RequestVersion1 getRequest() {
+        lock.lock();
+        try {
+            assertComplete();
+            return request;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public int getResult() {
+        lock.lock();
+        try {
+            assertComplete();
+            return result;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public String getOutput() {
+        lock.lock();
+        try {
+            assertComplete();
+            return output;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void assertComplete() {
+        if (request == null) {
+            throw new AssertionError("Request has not completed.");
+        }
+    }
+
+    void reset() {
+        lock.lock();
+        try {
+            request = null;
+            result = null;
+            output = null;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void executionRequestAdded(RequestVersion1 request) {
+    }
+
+    public void refreshRequestAdded(RequestVersion1 request) {
+    }
+
+    public void aboutToExecuteRequest(RequestVersion1 request) {
+    }
+
+    public void requestExecutionComplete(RequestVersion1 request, int result, String output) {
+        if (this.typeOfInterest.equals(request.getType())) {
+            lock.lock();
+            try {
+                if (this.request != null) {
+                    failure = new AssertionError("Multiple results for request.");
+                }
+                this.request = request;
+                this.result = result;
+                this.output = output;
+                condition.signalAll();
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    void waitForRequestExecutionComplete(int timeOutValue, TimeUnit timeOutUnits) {
+        lock.lock();
+        try {
+            Date expiry = new Date(System.currentTimeMillis() + timeOutUnits.toMillis(timeOutValue));
+            while (failure == null && request == null) {
+                if (!condition.awaitUntil(expiry)) {
+                    throw new AssertionError("Timeout waiting for request to complete.");
+                }
+            }
+            if (failure != null) {
+                throw failure;
+            }
+        } catch(Throwable t) {
+            throw UncheckedException.throwAsUncheckedException(t);
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ExtraTestCommandLineOptionsListenerWrapper.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ExtraTestCommandLineOptionsListenerWrapper.java
new file mode 100644
index 0000000..1c8609d
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ExtraTestCommandLineOptionsListenerWrapper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests;
+
+import org.gradle.openapi.external.ui.CommandLineArgumentAlteringListenerVersion1;
+
+import java.io.File;
+
+public class ExtraTestCommandLineOptionsListenerWrapper implements CommandLineArgumentAlteringListenerVersion1 {
+    private final File gradleUserHomeDir;
+
+    public ExtraTestCommandLineOptionsListenerWrapper(File gradleUserHomeDir) {
+        this.gradleUserHomeDir = gradleUserHomeDir;
+    }
+
+    public String getAdditionalCommandLineArguments(String commandLineArguments) {
+        return String.format("--no-search-upward --gradle-user-home \'%s\'", gradleUserHomeDir);
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
index 1e99fb3..4a07a74 100644
--- a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
@@ -69,20 +69,19 @@ public class FavoritesIntegrationTest {
         Task subsubCompileTask = TestUtility.createTask(context, "compile", "compile description");
         Task subsubLibTask = TestUtility.createTask(context, "lib", "lib description");
         Task subsubDocTask = TestUtility.createTask(context, "doc", "doc description");
-        Project subsubProject = TestUtility.createMockProject(context, "mysubsubproject", "filepath3", 2, null, new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null, (Project[]) null);
+        Project subsubProject = TestUtility.createMockProject(context, "mysubsubproject", "filepath3", 2, null, new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null);
 
         Task subCompileTask1 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask1 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask1 = TestUtility.createTask(context, "doc", "doc description");
-        Project subProject1 = TestUtility.createMockProject(context, "mysubproject1", "filepath2a", 1, new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null,
-                (Project[]) null);
+        Project subProject1 = TestUtility.createMockProject(context, "mysubproject1", "filepath2a", 1, new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null);
 
         Task subCompileTask2 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask2 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask2 = TestUtility.createTask(context, "doc", "doc description");
-        Project subProject2 = TestUtility.createMockProject(context, "mysubproject2", "filepath2b", 1, null, new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null, (Project[]) null);
+        Project subProject2 = TestUtility.createMockProject(context, "mysubproject2", "filepath2b", 1, null, new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null);
 
-        Project rootProject = TestUtility.createMockProject(context, "myrootproject", "filepath1", 0, new Project[]{subProject1, subProject2}, null, null, (Project[]) null);
+        Project rootProject = TestUtility.createMockProject(context, "myrootproject", "filepath1", 0, new Project[]{subProject1, subProject2}, null, null);
 
         buildInformation = new BuildInformation(rootProject);
 
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
index 59a9cc8..b023169 100644
--- a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
@@ -16,25 +16,18 @@
 package org.gradle.integtests
 
 import org.gradle.foundation.TestUtility
-import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol
 import org.gradle.foundation.output.FileLink
 import org.gradle.foundation.output.FileLinkDefinitionLord
 import org.gradle.foundation.output.LiveOutputParser
 import org.gradle.gradleplugin.foundation.GradlePluginLord
-import org.gradle.gradleplugin.foundation.runner.GradleRunner
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.Sample
-import org.gradle.logging.ShowStacktrace
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.locks.Condition
-import java.util.concurrent.locks.Lock
-import java.util.concurrent.locks.ReentrantLock
-
 /**
 This tests the that live output is gathered while executing a task.
 */
@@ -46,6 +39,7 @@ class LiveOutputIntegrationTest extends AbstractIntegrationTest {
 
     @Before
     void setUp() {
+        NativeServicesTestFixture.initialize(executer.gradleUserHomeDir)
         javaprojectDir = sample.dir
     }
 
@@ -73,33 +67,6 @@ that's likely to change over time. This version executes the command via GradleP
         verifyLiveOutputObtained( executionInteraction );
     }
 
-    /**
-This executes 'build' on the java multiproject sample. We want to make sure that
-we do get live output from gradle. We're not concerned with what it is, because
-that's likely to change over time. This version executes the command via GradleRunner.
-*/
-    @Test
-    public void liveOutputObtainedViaGradleRunner() {
-        File multiProjectDirectory = sample.getDir();
-        Assert.assertTrue(multiProjectDirectory.exists()); //make sure things are setup the way we expect
-
-        GradleRunner gradleRunner = new GradleRunner( multiProjectDirectory, distribution.gradleHomeDir, null );
-
-        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
-
-        //execute a command. We don't really care what the command is, just something that generates output
-        def cl = new ExtraTestCommandLineOptionsListener(executer.gradleUserHomeDir).getAdditionalCommandLineArguments('') + ' tasks'
-        gradleRunner.executeCommand(cl, org.gradle.api.logging.LogLevel.LIFECYCLE,
-                                            ShowStacktrace.INTERNAL_EXCEPTIONS,
-                                            executionInteraction);
-
-        executionInteraction.waitForCompletion(100, TimeUnit.SECONDS)
-
-        verifyLiveOutputObtained( executionInteraction );
-    }
-
-
-
    /**
   This verifies that it has live output. It also checks that we received some final output as well
   as that the execution was successful
@@ -218,71 +185,3 @@ that's likely to change over time. This version executes the command via GradleR
      }
 
 }
-
-//this class just holds onto our liveOutput and also tracks whether or not we've finished.
-public class TestExecutionInteraction implements ExecuteGradleCommandServerProtocol.ExecutionInteraction {
-    private StringBuilder liveOutput = new StringBuilder();
-    public boolean executionFinishedReported = false;
-    public boolean wasSuccessful = false;
-    public String finalMessage;
-    private Throwable failure
-    private final Lock lock = new ReentrantLock()
-    private final Condition condition = lock.newCondition()
-
-    public void reportLiveOutput(String message) {
-        liveOutput.append(message);
-    }
-
-    //when we finish executing, we'll make sure we got some type of live output from gradle.
-
-    public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-        lock.lock()
-        try {
-            executionFinishedReported = true
-            this.wasSuccessful = wasSuccessful
-            this.finalMessage = message
-            failure = throwable
-            condition.signalAll()
-        } finally {
-            lock.unlock()
-        }
-    }
-
-    def assertCompleted() {
-        lock.lock()
-        try {
-            if (!executionFinishedReported) {
-                throw new AssertionError("Request has not completed.")
-            }
-        } finally {
-            lock.unlock()
-        }
-    }
-
-    public waitForCompletion(int maxWaitValue, TimeUnit maxWaitUnits) {
-        Date expiry = new Date(System.currentTimeMillis() + maxWaitUnits.toMillis(maxWaitValue))
-        lock.lock()
-        try {
-            while (!executionFinishedReported) {
-                if (!condition.awaitUntil(expiry)) {
-                    throw new AssertionError("Timeout waiting for execution to complete.")
-                }
-            }
-            if (failure != null) {
-                throw failure
-            }
-        } finally {
-            lock.unlock()
-        }
-    }
-
-    public void reportExecutionStarted() { }
-
-    public void reportNumberOfTasksToExecute(int size) { }
-
-    public void reportTaskStarted(String message, float percentComplete) { }
-
-    public void reportTaskComplete(String message, float percentComplete) { }
-
-
-}
\ No newline at end of file
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ModelTasksGradleUIIntegrationTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ModelTasksGradleUIIntegrationTest.groovy
new file mode 100644
index 0000000..848e456
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/ModelTasksGradleUIIntegrationTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests
+
+import org.gradle.foundation.ProjectView
+import org.gradle.foundation.TestUtility
+import org.gradle.gradleplugin.foundation.GradlePluginLord
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+
+import java.util.concurrent.TimeUnit
+
+class ModelTasksGradleUIIntegrationTest extends AbstractIntegrationSpec {
+
+    GradlePluginLord gradlePluginLord = new GradlePluginLord()
+
+    def setup() {
+        NativeServicesTestFixture.initialize(executer.gradleUserHomeDir)
+        gradlePluginLord.setCurrentDirectory(temporaryFolder.testDirectory);
+        gradlePluginLord.setGradleHomeDirectory(distribution.gradleHomeDir);
+        gradlePluginLord.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(executer.gradleUserHomeDir))
+
+        file('build.gradle') << '''
+            model {
+                tasks {
+                    create("fromModel") {
+                        doLast {
+                            buildDir.mkdirs()
+                            new File(buildDir, "output.txt") << "from model rule defined task"
+                        }
+                    }
+                }
+            }
+        '''
+        TestUtility.refreshProjectsBlocking(gradlePluginLord, 40, TimeUnit.SECONDS);
+
+    }
+
+    def "tasks added using model rule are visible"() {
+        when:
+        List<ProjectView> projects = gradlePluginLord.getProjects();
+
+        then:
+        projects.first().getTask("fromModel")
+    }
+
+    def "tasks added using model rule can be executed"() {
+        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
+
+        when:
+        TestUtility.executeBlocking(gradlePluginLord, "fromModel", "Test Execution", executionInteraction, 40)
+
+        then:
+        file('build/output.txt').text == "from model rule defined task"
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
index e77daf7..bf2ec71 100644
--- a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
@@ -23,6 +23,7 @@ import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.openapi.external.foundation.ProjectVersion1
 import org.gradle.openapi.wrappers.foundation.GradleInterfaceWrapperVersion1
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
@@ -34,19 +35,12 @@ import java.util.concurrent.TimeUnit
  This tests the multiproject sample with the GradleView mechanism.
  */
 class MultiprojectProjectAndTaskListIntegrationTest extends AbstractIntegrationTest {
-
-    static final String JAVA_PROJECT_NAME = 'javaproject'
-    static final String SHARED_NAME = 'shared'
-    static final String API_NAME = 'api'
-    static final String WEBAPP_NAME = 'webservice'
-    static final String SERVICES_NAME = 'services'
-    static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
     @Rule public final Sample sample = new Sample(testDirectoryProvider, 'java/multiproject')
     GradlePluginLord gradlePluginLord = new GradlePluginLord()
 
     @Before
     void setUp() {
+        NativeServicesTestFixture.initialize(executer.gradleUserHomeDir)
         gradlePluginLord.setCurrentDirectory(sample.dir);
         gradlePluginLord.setGradleHomeDirectory(distribution.gradleHomeDir);
         gradlePluginLord.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(executer.gradleUserHomeDir))
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiFixture.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiFixture.java
new file mode 100644
index 0000000..ab9d528
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiFixture.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests;
+
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext;
+import org.gradle.internal.UncheckedException;
+import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
+import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
+import org.gradle.openapi.wrappers.ui.SinglePaneUIWrapper;
+import org.gradle.test.fixtures.file.TestDirectoryProvider;
+import org.junit.Assert;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class OpenApiFixture implements MethodRule {
+    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext();
+    private final TestDirectoryProvider testDirectoryProvider;
+    private final List<JFrame> frames = new ArrayList<JFrame>();
+
+    public OpenApiFixture(TestDirectoryProvider testDirectoryProvider) {
+        this.testDirectoryProvider = testDirectoryProvider;
+    }
+
+    public Statement apply(final Statement base, FrameworkMethod method, final Object target) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    SwingUtilities.invokeAndWait(new Runnable() {
+                        public void run() {
+                            for (JFrame frame : frames) {
+                                frame.dispose();
+                            }
+                        }
+                    });
+                }
+            }
+        };
+    }
+
+    public SinglePaneUIVersion1 createSinglePaneUI() {
+        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
+        return createSinglePaneUI(testSingleDualPaneUIInteractionVersion1);
+    }
+
+    public SinglePaneUIVersion1 createSinglePaneUI(SinglePaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1) {
+        System.setProperty("gradle.home", buildContext.getGradleHomeDir().getAbsolutePath());
+        SinglePaneUIVersion1 singlePane;
+        try {
+            singlePane = new SinglePaneUIWrapper(testSingleDualPaneUIInteractionVersion1, false);
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+
+        //make sure we got something
+        Assert.assertNotNull(singlePane);
+
+        singlePane.setCurrentDirectory(testDirectoryProvider.getTestDirectory());
+        singlePane.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListenerWrapper(buildContext.getGradleUserHomeDir()));
+
+        return singlePane;
+    }
+
+    public JFrame open(SinglePaneUIVersion1 ui) {
+        return createTestFrame(ui.getComponent(), null);
+    }
+
+    /*
+    * This shows the specified frame for a moment so the event queue can be emptied. This
+    * ensures Swing events a processed
+    * Don't place anything between the following three lines (especially something that might
+    * throw an exception). This shows and hides the UI, giving it time to actually show itself
+    * and empty the event dispatch queue.
+    */
+    public void flushEventQueue(final JFrame frame) {
+        try {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    frame.setVisible(true);
+                }
+            });
+            Thread.sleep(500);
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    frame.setVisible(false);
+                }
+            });
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    private JFrame createTestFrame(Component mainComponent, Component outputComponent) {
+        JFrame frame = new JFrame();
+        frames.add(frame);
+        frame.setSize(650, 500);
+        JPanel panel = new JPanel(new BorderLayout());
+        frame.getContentPane().add(panel);
+
+        //add a large red label explaining this
+        JLabel label2 = new JLabel("Performing Open API Integration Test!");
+        label2.setForeground(Color.red);
+        label2.setFont(label2.getFont().deriveFont(26f));
+        panel.add(label2, BorderLayout.NORTH);
+
+        //add the main UI to the frame
+        panel.add(mainComponent, BorderLayout.CENTER);
+
+        if (outputComponent != null) {
+            panel.add(outputComponent, BorderLayout.SOUTH);
+        }
+
+        return frame;
+    }
+
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy
new file mode 100644
index 0000000..e194fc9
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy
@@ -0,0 +1,933 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests
+
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.openapi.external.foundation.GradleInterfaceVersion2
+import org.gradle.openapi.external.foundation.ProjectVersion1
+import org.gradle.openapi.external.foundation.RequestVersion1
+import org.gradle.openapi.external.foundation.TaskVersion1
+import org.gradle.openapi.external.foundation.favorites.FavoriteTaskVersion1
+import org.gradle.openapi.external.foundation.favorites.FavoritesEditorVersion1
+import org.gradle.openapi.external.ui.*
+import org.gradle.openapi.wrappers.ui.SinglePaneUIWrapper
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+import org.gradle.util.PreconditionVerifier
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.*
+
+import javax.swing.*
+import java.awt.*
+import java.awt.event.HierarchyEvent
+import java.awt.event.HierarchyListener
+import java.util.concurrent.TimeUnit
+
+import static org.hamcrest.Matchers.*
+
+/**
+ * This tests numerous aspects of the Open API UI. This is how the Idea plugin extracts the UI from
+ * Gradle.
+ *
+ * Note: even though the open API has been removed, this test covers aspects of the GUI that are not covered elsewhere. This test will
+ * gradually be replaced by other GUI tests.
+ */
+ at Requires(TestPrecondition.SWING)
+class OpenApiUiTest {
+
+    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+    private IntegrationTestBuildContext buildContext = new IntegrationTestBuildContext();
+    @Rule public OpenApiFixture openApi = new OpenApiFixture(temporaryFolder)
+    @ClassRule public static PreconditionVerifier verifier = new PreconditionVerifier()
+
+    @Before
+    void setup() {
+        NativeServicesTestFixture.initialize(buildContext.gradleUserHomeDir)
+        temporaryFolder.file("settings.gradle") << "include 'services', 'services:webservice', 'api'"
+        temporaryFolder.file("build.gradle") << "allprojects { apply plugin: 'java' }"
+    }
+
+    /**
+     This tests to see if we can call the UIFactory to create a single pane UI.
+     This is only testing that extracting the UI returns something without giving
+     errors and that it has a component. This is just a good general-case test
+     to make sure the basics are working.
+     */
+    @Test
+    void testSinglePaneBasic() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+        //make sure we got something
+        Assert.assertNotNull(singlePane)
+
+        //tell it we're about to show it, so it'll create a component
+        singlePane.aboutToShow();
+
+        //make sure we now have that component
+        Assert.assertNotNull(singlePane.getComponent())
+    }
+
+    /**
+     * This verifies that favorites are working for some basics. We're going to test this with both
+     * the single pane UIs (they actually use the same editor then for other tests we'll
+     * assume they're same).
+     */
+    @Test
+    void testFavoritesBasic() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        checkFavoritesBasic(singlePane)
+    }
+
+    /**
+     * This verifies that we favorites are basically working based on the given UI. We're going to add one, then
+     * do some 'gets' to find the just-added favorite.
+     */
+    private void checkFavoritesBasic(BasicGradleUIVersion1 basicGradleUI) {
+        FavoritesEditorVersion1 editor = basicGradleUI.getFavoritesEditor()
+
+        //there should be no favorites as of yet
+        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
+
+        //add one (doesn't really matter what it is)
+        def fullCommandLine = "-t -S"
+        def displayName = "Task List With Stack trace"
+        FavoriteTaskVersion1 favorite = editor.addFavorite(fullCommandLine, displayName, true)
+
+        //make sure something was added
+        Assert.assertEquals(1, editor.getFavoriteTasks().size())
+
+        //get the newly-added favorite by command line.
+        FavoriteTaskVersion1 matchingFavorite1 = editor.getFavorite(fullCommandLine)
+        Assert.assertEquals(favorite, matchingFavorite1)
+
+        //get the newly-added favorite by displayName.
+        FavoriteTaskVersion1 matchingFavorite2 = editor.getFavoriteByDisplayName(displayName)
+        Assert.assertEquals(favorite, matchingFavorite2)
+
+        Assert.assertTrue(matchingFavorite2.alwaysShowOutput())
+    }
+
+    /**
+     * This verifies that we can edit favorites. We're going to add a favorite then edit its
+     * command line, display name, and 'show output' setting.
+     */
+    @Test
+    void testEditingFavorites() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+        def originalFullCommandLine = "-t -S"
+        def originalDisplayName = "Task List With Stack trace"
+        FavoriteTaskVersion1 addedFavorite = editor.addFavorite(originalFullCommandLine, originalDisplayName, true)
+
+        //make sure we can find the just-added favorite
+        Assert.assertNotNull(editor.getFavorite(originalFullCommandLine))
+        Assert.assertNotNull(editor.getFavoriteByDisplayName(originalDisplayName))
+
+        String newFullCommandLine = "-t -S -d"
+        String newDisplayName = "new task list"
+        String error = editor.editFavorite(addedFavorite, newFullCommandLine, newDisplayName, false)
+        Assert.assertNull(error)  //we should get no error
+
+        //now we shouldn't be able to find the favorite using the original values. This is part of verifying the values were in fact changed.
+        Assert.assertNull(editor.getFavorite(originalFullCommandLine))
+        Assert.assertNull(editor.getFavoriteByDisplayName(originalDisplayName))
+
+        //make sure we can find it using the new values. This is part of verifying the values were in fact changed.
+        Assert.assertNotNull(editor.getFavorite(newFullCommandLine))
+        Assert.assertNotNull(editor.getFavoriteByDisplayName(newDisplayName))
+
+        //there should just be 1 favorite
+        Assert.assertEquals(1, editor.getFavoriteTasks().size())
+    }
+
+    /**
+     * This verifies that we can remove favorites. We're going to add some favorites then remove them
+     * verifying that they've gone.
+     */
+    @Test
+    void testRemovingFavorites() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+        //there should be no favorites as of yet
+        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
+
+        //add one (doesn't really matter what it is)
+        String command1 = "build"
+        FavoriteTaskVersion1 favorite1 = editor.addFavorite(command1, command1, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command1))
+        Assert.assertEquals(1, editor.getFavoriteTasks().size())
+
+        //add another one
+        String command2 = "build -xtest"
+        FavoriteTaskVersion1 favorite2 = editor.addFavorite(command2, command2, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command2))
+        Assert.assertEquals(2, editor.getFavoriteTasks().size())
+
+        String command3 = "clean"
+        FavoriteTaskVersion1 favorite3 = editor.addFavorite(command3, command3, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command3))
+        Assert.assertEquals(3, editor.getFavoriteTasks().size())
+
+        String command4 = "docs"
+        FavoriteTaskVersion1 favorite4 = editor.addFavorite(command4, command4, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command4))
+        Assert.assertEquals(4, editor.getFavoriteTasks().size())
+
+        //now remove one of them
+        java.util.List removed1 = [favorite2]
+        editor.removeFavorites(removed1)
+
+        //make sure it was removed
+        Assert.assertNull(editor.getFavorite(command2))
+        Assert.assertEquals(3, editor.getFavoriteTasks().size())
+
+        //now remove multiples
+        java.util.List removed2 = [favorite1, favorite4]
+        editor.removeFavorites(removed2)
+
+        //make sure they were both removed
+        Assert.assertNull(editor.getFavorite(command1))
+        Assert.assertNull(editor.getFavorite(command4))
+        Assert.assertEquals(1, editor.getFavoriteTasks().size())
+    }
+
+    /**
+     * This tests executing multiple favorites at once. We'll add two favorites, then execute both of them
+     * via GradleInterfaceVersion1.executeFavorites(). This should execute both as a single command
+     * concatenating them together.
+     */
+    @Test
+    void testExecutingFavorites() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+        //this starts the execution queue
+        singlePane.aboutToShow()
+
+        //there should be no favorites as of yet
+        Assert.assertTrue(editor.getFavoriteTasks().isEmpty())
+
+        //add one (doesn't really matter what it is)
+        String command1 = "build"
+        FavoriteTaskVersion1 favorite1 = editor.addFavorite(command1, command1, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command1))
+        Assert.assertEquals(1, editor.getFavoriteTasks().size())
+
+        //add another one
+        String command2 = "clean"
+        FavoriteTaskVersion1 favorite2 = editor.addFavorite(command2, command2, true)
+
+        //make sure it was added
+        Assert.assertNotNull(editor.getFavorite(command2))
+        Assert.assertEquals(2, editor.getFavoriteTasks().size())
+
+        //add a request observer so we can observe when the command is finished. This allows us to
+        //see what was actually executed.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
+        ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).addRequestObserver(testRequestObserver)
+
+        //now execute both favorites
+        java.util.List<FavoriteTaskVersion1> favorites = [favorite1, favorite2]
+        RequestVersion1 request = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).executeFavorites(favorites)
+
+        Assert.assertNotNull(request)
+
+        //verify that the actual command that was executed is a concatenation of both favorites
+        Assert.assertThat(request.getFullCommandLine(), startsWith("build clean"))
+    }
+
+    /**
+     * This tests getting projects and tasks from gradle. It then goes through a series of tests
+     * related to projects and tasks (such as making sure their description is returned, their
+     * root is returned, etc).
+     */
+    @Test
+    void testProjectsAndTasks() {
+        SinglePaneUIVersion1 panel = openApi.createSinglePaneUI()
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) panel.getGradleInterfaceVersion1()
+
+        //make sure our samples directory exists
+        Assert.assertTrue(gradleInterface.getCurrentDirectory().isDirectory())
+
+        //add a request observer so we can observe when the command is finished. This allows us to
+        //see what was actually executed.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
+        gradleInterface.addRequestObserver(testRequestObserver)
+
+        //this starts the execution queue
+        panel.aboutToShow()
+
+        gradleInterface.refreshTaskTree()
+
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+        Assert.assertEquals("Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
+
+        java.util.List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
+        Assert.assertFalse(rootProjects.isEmpty());   //do we have any root projects?
+
+        ProjectVersion1 rootProject = rootProjects.get(0);
+        Assert.assertNotNull(rootProject);
+        Assert.assertThat(rootProject.getSubProjects().size(), equalTo(2))
+
+        //Quick check to make sure there are tasks on each of the sub projects.
+        //The exact task names will change over time, so I don't want to try
+        //to test for those. I'll just make sure there are several.
+        Iterator<ProjectVersion1> iterator = rootProjects.get(0).getSubProjects().iterator();
+        while (iterator.hasNext()) {
+            ProjectVersion1 projectVersion1 = iterator.next();
+            Assert.assertTrue(projectVersion1.getTasks().size() > 4);
+        }
+
+        //there should be a 'services' project
+        ProjectVersion1 servicesProject = rootProjects.get(0).getSubProject("services");
+        Assert.assertNotNull(servicesProject);
+
+        //and it contains a 'webservice' sub project
+        ProjectVersion1 webserviceProject = servicesProject.getSubProject("webservice");
+        Assert.assertNotNull(webserviceProject);
+
+        ProjectVersion1 apiProject = rootProjects.get(0).getSubProject("api");
+        Assert.assertNotNull(apiProject);
+
+        //verify the root project is set correctly
+        Assert.assertEquals(servicesProject, webserviceProject.getParentProject())
+
+        //verify its full name is correct (this might should be prefixed with a colon)
+        Assert.assertEquals("services:webservice", webserviceProject.getFullProjectName())
+
+        //verify getSubProjectFromFullPath works
+        ProjectVersion1 foundProject = rootProject.getSubProjectFromFullPath("services:webservice")
+        Assert.assertNotNull("Failed to find services:webservice", foundProject)
+        Assert.assertEquals(webserviceProject, foundProject)
+
+        //verify that are multiple tasks here (we know their should be)
+        Assert.assertTrue(webserviceProject.getTasks().size() > 4);
+
+        //verify getTaskFromFullPath works
+        TaskVersion1 apiBuildTask = rootProject.getTaskFromFullPath(":api:build")
+        Assert.assertNotNull("Failed to find :api:build", apiBuildTask)
+
+        Assert.assertEquals(apiProject, apiBuildTask.getProject())
+
+        //then make sure it has a description
+        Assert.assertNotNull(apiBuildTask.getDescription())
+
+        //and that its not marked as the default (we need a task to be the default so we can verify it returns true)
+        Assert.assertFalse(apiBuildTask.isDefault())
+
+        //there are no default tasks here
+        Assert.assertTrue(apiProject.getDefaultTasks().isEmpty())
+
+        //this build task is a child of the api project. Should be the same task we got earlier
+        TaskVersion1 buildTask = apiProject.getTask("build")
+        Assert.assertNotNull("Failed to find build task", buildTask)
+        Assert.assertEquals(apiBuildTask, buildTask)
+    }
+
+    /**
+     * This verifies that the GradleInterfaceVersion1.refreshTaskTree that takes
+     * additional arguments works. We're not really interested in what those additional
+     * arguments are, just that it passes them along.
+     */
+    @Test
+    void testRefreshWithArguments() {
+        SinglePaneUIVersion1 panel = openApi.createSinglePaneUI()
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) panel.getGradleInterfaceVersion1()
+
+        //make sure our samples directory exists
+        if (!gradleInterface.getCurrentDirectory().exists()) {
+            throw new AssertionError('sample project missing. Expected it at: ' + gradleInterface.getCurrentDirectory())
+        }
+
+        BlockingRequestObserver setupListener = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
+        gradleInterface.addRequestObserver(setupListener)
+
+        //this starts the execution queue
+        panel.aboutToShow()
+
+        // wait for the implicit refresh to complete
+        setupListener.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+        gradleInterface.removeRequestObserver(setupListener)
+
+        //add a request observer so we can observe when the command is finished. This allows us to
+        //see what was actually executed.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
+        gradleInterface.addRequestObserver(testRequestObserver)
+
+        RequestVersion1 request = gradleInterface.refreshTaskTree2("-xtest")
+
+        //make sure that the actual request is the normal refresh request with our
+        //(this line is really what we're trying to test)
+        Assert.assertThat(request.getFullCommandLine(), startsWith("tasks -xtest"))
+
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+        Assert.assertEquals("Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
+
+        Assert.assertEquals("Not our request", request, testRequestObserver.request);
+    }
+
+    /**
+     * This verifies that you can add custom stuff to the setup tab. This is a UI test and is kinda tricky. We're going
+     * to use a HierarchyListener to see if our component is made visible. This will confirm if it was added or not
+     * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
+     * really doing here, is adding a 'custom' component to the UI, then adding the UI to a frame, then showing the frame,
+     * so we can verify that our component was shown.
+     */
+    @Test
+    void testAddingComponentToSetupTab() {
+        if (java.awt.GraphicsEnvironment.isHeadless()) {
+            return;  // Can't run this test in headless mode!
+        }
+
+        JLabel label = new JLabel("Testing Testing 123")
+        TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
+        label.addHierarchyListener(hierarchyAdapter)
+
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+        //make sure we haven't been told the component was shown or hidden yet
+        Assert.assertFalse(hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
+
+        singlePane.aboutToShow();
+
+        singlePane.setCustomPanelToSetupTab(label)
+
+        //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
+        Assert.assertFalse(hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
+
+        //now create a frame, place the UI in it, then show it briefly
+        JFrame frame = openApi.open(singlePane)
+
+        //set the Setup tab as the current tab. This is required to actually show the component.
+        int setupTabIndex = singlePane.getGradleTabIndex("Setup");
+        Assert.assertTrue("Failed to get index of setup tab", setupTabIndex != -1)
+        singlePane.setCurrentGradleTab(setupTabIndex);
+
+        //still should not show the component (its not yet visible, but is about to be)
+        Assert.assertFalse(hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(hierarchyAdapter.componentWasHidden)
+
+        //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
+        //queue. This is required for the setup tab to become current as well as show the custom component we added.
+        openApi.flushEventQueue(frame)
+
+        Assert.assertEquals("The setup tab was not selected", setupTabIndex, singlePane.getCurrentGradleTab())
+
+        //now the label should have been made visible then invisible
+        Assert.assertTrue(hierarchyAdapter.componentWasShown)
+        Assert.assertTrue(hierarchyAdapter.componentWasHidden)
+    }
+
+    /**
+     * This verifies that you can add a custom tab to the UI. This is a UI test and is kinda tricky. We're going
+     * to use a HierarchyListener to see if our tab component is made visible. This will confirm if it was added or not
+     * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
+     * really doing here, is adding a tab to the UI, then adding the UI to a frame, then showing the frame so we can
+     * then verify that our tab was shown (actually using our tab's component).
+     */
+    @Test
+    void testAddingCustomTab() {
+        if (java.awt.GraphicsEnvironment.isHeadless()) {
+            return;  // Can't run this test in headless mode!
+        }
+
+        TestTab testTab = new TestTab()
+
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+        //make sure we haven't been told the component was shown or hidden yet
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
+
+        //make sure things are initialized properly. These should all be false
+        Assert.assertFalse(testTab.nameRetrieved);
+        Assert.assertFalse(testTab.informedAboutToShow);
+        Assert.assertFalse(testTab.componentCreated);
+
+        int originalCount = singlePane.getGradleTabCount();
+
+        singlePane.addTab(99, testTab) //I don't really care about the index. It should accept a number that is too large and handle it appropriately.
+
+        singlePane.aboutToShow()
+
+        //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
+
+        //now create a frame, place the UI in it, then show it briefly
+        JFrame frame = openApi.open(singlePane)
+
+        String testTabName = "Test Tab"
+
+        //set the test tab as the current tab. This is required to actually show the component.
+        int testTabIndex = singlePane.getGradleTabIndex(testTabName)
+        Assert.assertTrue("Failed to get index of test tab", testTabIndex != -1)
+        singlePane.setCurrentGradleTab(testTabIndex)
+
+        //just to test getGradleTabName, make sure it returns our tab name
+        Assert.assertEquals(testTabName, singlePane.getGradleTabName(testTabIndex))
+
+        //to test getGradleTabCount, make sure the tab count went up by 1
+        Assert.assertEquals(originalCount + 1, singlePane.getGradleTabCount())
+
+        //still should not show the component (its not yet visible, but is about to be)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
+
+        //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
+        //queue. This is required for the test tab to become current.
+        openApi.flushEventQueue(frame)
+
+        Assert.assertEquals("The test tab was not selected", testTabIndex, singlePane.getCurrentGradleTab())
+
+        //now the label should have been made visible then invisible
+        Assert.assertTrue(testTab.hierarchyAdapter.componentWasShown)
+        Assert.assertTrue(testTab.hierarchyAdapter.componentWasHidden)
+
+        //at the end, the name should have been queried, we should have been told we were about to shown, and the component should be created
+        Assert.assertTrue(testTab.nameRetrieved);
+        Assert.assertTrue(testTab.informedAboutToShow);
+        Assert.assertTrue(testTab.componentCreated);
+
+        //reset the test tab (resets the listener so we can remove the tab and verify that it no longer shows up, as well as some of our test variables)
+        testTab.reset()
+        singlePane.removeTab(testTab)
+
+        //I'm going to set the current tab, but this shouldn't do anything because the tab was removed
+        singlePane.setCurrentGradleTab(testTabIndex);
+
+        //part of showing the UI is telling it its about to be shown. In this case, nothing should happen
+        //related to the test tab. It has been removed
+        singlePane.aboutToShow()
+
+        //This shows and hides the UI, giving it time to actually show itself and empty the event
+        //dispatch queue. This is required for the test tab to become current (were it still present).
+        openApi.flushEventQueue(frame)
+
+        //try to get the test tab
+        testTabIndex = singlePane.getGradleTabIndex("Test Tab");
+        Assert.assertTrue("Erroneously got index of test tab. It was removed", testTabIndex == -1)
+
+        //we've removed it, so it shouldn't have been polled about or informed of anything
+        Assert.assertFalse(testTab.nameRetrieved);
+        Assert.assertFalse(testTab.informedAboutToShow);
+        Assert.assertFalse(testTab.componentCreated);
+
+        //It was not shown after the reset, these should both be false
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasShown)
+        Assert.assertFalse(testTab.hierarchyAdapter.componentWasHidden)
+    }
+
+    /**
+     * We want to make sure the settings are working correctly here. This is the mechanism that
+     * handles saving/restoring the values within the UI and can be stored in different ways
+     * depending on how the UI integrated with its root (its up to whoever implements
+     * SettingsNodeVersion1). Here, to spot check that the basics are working, we'll create a
+     * UI, set a value, close it, then recreate it using the same settings object. The values
+     * should be saved upon close and then restored.
+     */
+    @Test
+    void testSettings() {
+        TestSettingsNodeVersion1 settingsNode = new TestSettingsNodeVersion1();
+
+        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), settingsNode);
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI(testSingleDualPaneUIInteractionVersion1);
+
+        File illegalDirectory = temporaryFolder.testDirectory.file("non-existant").createDir();
+        if (illegalDirectory.equals(singlePane.getCurrentDirectory())) {
+            throw new AssertionError("Directory already set to 'test' directory. The test is not setup correctly.");
+        }
+
+        //this is required to get the ball rolling
+        singlePane.aboutToShow();
+
+        //set the current directory after calling aboutToShow (otherwise, it'll stomp over us when it restores its default settings)
+        singlePane.setCurrentDirectory(illegalDirectory);
+
+        //close the UI. This saves the current settings.
+        singlePane.close();
+
+        //now instantiate it again
+        testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), settingsNode);
+        try {
+            singlePane = new SinglePaneUIWrapper(testSingleDualPaneUIInteractionVersion1, false);
+        } catch (Exception e) {
+            throw new AssertionError("Failed to extract single pane (second time): Caused by " + e.getMessage())
+        }
+
+        //this should restore the previous settings
+        singlePane.aboutToShow();
+
+        Assert.assertEquals(illegalDirectory, singlePane.getCurrentDirectory());
+    }
+
+    /**
+     * This tests that the command line altering mechanism works. This adds additional
+     * things to the command line being executed. This is used by gradle build systems
+     * that need custom, system-specific arguments passed to it that aren't known by gradle
+     * proper. For example: you're working with a large project made of MANY subprojects, a
+     * custom IDE plugin can track which projects you're focusing on and pass that information
+     * to the build system via this mechanism, so it only builds appropriate projects.
+     * This is awkward to test because its real use requires a customized build system,
+     * so I'm going to pass an argument that is illegal by itself -- meaning no tasks
+     * are specified. Then I'll alter the command line by adding an actual task. Then
+     * wait for it to complete and verify what was executed
+     */
+    @Test
+    void testCommandLineAlteringListener() {
+        SinglePaneUIVersion1 panel = openApi.createSinglePaneUI()
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) panel.getGradleInterfaceVersion1()
+
+        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+        panel.aboutToShow()
+
+        //add a request observer so we can observe when the command is finished. This allows us to
+        //see what was actually executed.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
+        gradleInterface.addRequestObserver(testRequestObserver)
+
+        //now that we know that command is illegal by itself, try it again but the listener will append 'build'
+        //to the command line which makes it legal (again, we don't really care what we execute.
+        TestCommandLineArgumentAlteringListenerVersion1 commandLineArgumentAlteringListener = new TestCommandLineArgumentAlteringListenerVersion1("classes")
+        gradleInterface.addCommandLineArgumentAlteringListener(commandLineArgumentAlteringListener)
+
+        //execute this before we do our test. This is not legal by itself. It should fail. That means our
+        //test is setup correctly. For example: if someone adds a default task to this project, this will
+        //generate NO error and thus, our test will prove nothing. If you get a test failure here, you
+        //can try changing the command line to something that's illegal by itself (we don't care what).
+        RequestVersion1 request = gradleInterface.executeCommand2("-s", "test command")
+
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith("-s "))
+        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), endsWith(" classes"))
+
+        //make sure it completed execution correctly
+        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
+
+        //the request that was executed should be equal to our original command with our 'altered' command added to it
+        Assert.assertNotNull("Missing 'execution completed' request", testRequestObserver.request)
+
+        //just to be paranoid, let's make sure it was actually our request. If this fails, it probably represents something
+        //fundamentally flawed with the request or request wrapper mechanism.
+        Assert.assertEquals(request, testRequestObserver.request)
+
+        gradleInterface.removeRequestObserver(testRequestObserver)
+        gradleInterface.removeCommandLineArgumentAlteringListener(commandLineArgumentAlteringListener)
+    }
+
+    /**
+     * This tests that getVersion returns the same thing as the jar's suffix.
+     * We'll get the gradle jar, then strip off its extension and verify that
+     * the jar's name ends with the version number.
+     */
+    @Test
+    void testVersion() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        String version = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion()
+
+        Assert.assertNotNull("null version number", version)
+
+        Assert.assertFalse("Empty version number", version.trim().equals(""))       //shouldn't be empty
+    }
+
+    /**
+     * This is just a spot check that getGradleHomeDirectory works. Its based off
+     * of the gradle you're running.
+     */
+    @Test
+    void testGradleHomeDirectory() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+        Assert.assertEquals(buildContext.gradleHomeDir, singlePane.getGradleHomeDirectory())
+    }
+
+    /**
+     * This is just a spot check that we can get an instance of the OutputUILord.
+     * Other tests cover its functionality more thoroughly. This is just to make sure
+     * its working when accessed via the Open API.
+     */
+    @Test
+    void testOutputUILord() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+        Assert.assertNotNull(outputUILord)
+    }
+
+    /**
+     * This tests that you can correctly obtain the number of output tabs from the ui.
+     */
+    @Test
+    void testOutputPaneNumber() {
+        if (java.awt.GraphicsEnvironment.isHeadless()) {
+            return;  // Can't run this test in headless mode!
+        }
+
+        SinglePaneUIWrapper panel = openApi.createSinglePaneUI()
+
+        //now create a frame, place the UI in it, then show it briefly
+        JFrame frame = openApi.open(panel)
+
+        //make sure we got something
+        Assert.assertNotNull(panel)
+
+        //tell it we're about to show it, so it'll create a component
+        panel.aboutToShow()
+
+        panel.refreshTaskTree()
+
+        openApi.flushEventQueue(frame)
+
+        //there should be one opened output tab for the refresh
+        Assert.assertEquals(1, panel.getNumberOfOpenedOutputTabs())
+
+        panel.executeCommand("build", "build")
+
+        openApi.flushEventQueue(frame)
+
+        //there should be 2 opened output tabs. One for refresh, one for build
+        Assert.assertEquals(2, panel.getNumberOfOpenedOutputTabs())
+    }
+
+    /**
+     * This tests whether or not a the UI is considered busy. Its busy if its
+     * executing a command. To test this, we'll execute a command and verify
+     * we're busy. When it finishes, we'll verify we're not longer busy.
+     * We'll also check that canClose works properly. If we're busy, calling
+     * canClose should prompt the user to confirm closing and return their
+     * answer. If we're not busy, it should not prompt the user and return
+     * true.
+     */
+    @Test
+    void testBusy() {
+        SinglePaneUIVersion1 panel = openApi.createSinglePaneUI()
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) panel.getGradleInterfaceVersion1()
+
+        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+        panel.aboutToShow()
+
+        //add a request observer so we can observe when the command is finished.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.EXECUTION_TYPE)
+        gradleInterface.addRequestObserver(testRequestObserver)
+
+        gradleInterface.executeCommand("build", "test command")
+
+        //now that there's a real command in the queue, we should be considered busy
+        Assert.assertTrue(panel.isBusy())
+        Assert.assertTrue(gradleInterface.isBusy())
+
+        //we're busy, we shouldn't be able to close
+        TestCloseInteraction testCloseInteraction = new TestCloseInteraction(false)
+        Assert.assertFalse(panel.canClose(testCloseInteraction))
+
+        //since we just asked to close and we're busy, make sure we prompted the user
+        Assert.assertTrue(testCloseInteraction.wasPromptedToConfirmClose)
+
+        testRequestObserver.waitForRequestExecutionComplete(120, TimeUnit.SECONDS)
+
+        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith("build"))
+
+        //make sure it completed execution correctly
+        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
+
+        //make sure we're not longer considered busy
+        Assert.assertFalse(panel.isBusy())
+        Assert.assertFalse(gradleInterface.isBusy())
+
+        //make sure we can close now
+        testCloseInteraction = new TestCloseInteraction(false)
+        Assert.assertTrue(panel.canClose(testCloseInteraction))
+
+        //since we just asked to close and we're NOT busy, make sure we did NOT prompt the user
+        Assert.assertFalse(testCloseInteraction.wasPromptedToConfirmClose)
+
+        gradleInterface.removeRequestObserver(testRequestObserver)
+    }
+
+    /**
+     * This tests that we can set a custom gradle executor.
+     */
+    @Test
+    void testSettingCustomGradleExecutor() {
+        SinglePaneUIVersion1 panel = openApi.createSinglePaneUI()
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) panel.getGradleInterfaceVersion1()
+
+        //it should be null by default
+        Assert.assertNull(gradleInterface.getCustomGradleExecutable())
+
+        //now let's set it to a custom gradle executable. Actually, we're not going to really get
+        //a custom one; we'll use the normal one. Why? Because a real custom one would probably
+        //become a pain for maintaining this test. Here, we're interested that the basics are working
+        //from an open-api standpoint.
+        File gradleExecutor = getCustomGradleExecutable()
+
+        gradleInterface.setCustomGradleExecutable(gradleExecutor)
+
+        //make sure it was set
+        Assert.assertEquals(gradleExecutor, gradleInterface.getCustomGradleExecutable())
+        Assert.assertEquals(gradleExecutor, panel.getCustomGradleExecutable()) //just another way to get it
+
+        //add a request observer so we can observe when the command is finished.
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE)
+        gradleInterface.addRequestObserver(testRequestObserver)
+
+        //this starts the execution queue
+        panel.aboutToShow()
+
+        panel.refreshTaskTree()
+
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+        //make sure it completed execution correctly
+        Assert.assertEquals("Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output, 0, testRequestObserver.result)
+
+        gradleInterface.removeRequestObserver(testRequestObserver)
+    }
+
+    /**
+     * This gets a gradle executable. That is, a way to launch gradle (shell script or batch file).
+     */
+    private File getCustomGradleExecutable() {
+        //now let's set it to a custom gradle executable. We'll just point it to the regular
+        //gradle file (but it'll be the custom one.
+        String name = OperatingSystem.current().getScriptName("bin/gradle");
+
+        File gradleExecutor = new File(buildContext.getGradleHomeDir(), name)
+
+        //make sure the executable exists
+        Assert.assertTrue("Missing gradle executable at: " + gradleExecutor, gradleExecutor.exists())
+
+        return gradleExecutor
+    }
+}
+
+/**
+ * Inner class for tracking a component's visiblity has changed.
+ * A HierarchyListener is how Swing notifies you that a component's visibility has changed.
+ * We'll use it to track if the component was shown and then hidden.
+ */
+class TestVisibilityHierarchyListener implements HierarchyListener {
+    private boolean componentWasShown = false;
+    private boolean componentWasHidden = false;
+
+    void hierarchyChanged(HierarchyEvent e) {
+        if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
+            if (e.getComponent().isShowing()) {
+                componentWasShown = true;
+            }
+            else {
+                componentWasHidden = true;
+            }
+        }
+    }
+}
+
+/**
+ * A class that manages a dummy gradle tab. It just consists of a label,
+ *  but tracks that certain fields were called.
+ */
+class TestTab implements GradleTabVersion1 {
+    private JLabel label = new JLabel("Testing Testing 123")
+    private TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
+    private boolean nameRetrieved
+    private boolean informedAboutToShow
+    private boolean componentCreated
+
+    def TestTab() {
+        label.addHierarchyListener(hierarchyAdapter)
+    }
+
+    private void reset() {
+        label.removeHierarchyListener(hierarchyAdapter)         //remove the existing listener
+        hierarchyAdapter = new TestVisibilityHierarchyListener()  //create a new one
+        label.addHierarchyListener(hierarchyAdapter)            //add it
+
+        nameRetrieved = false
+        informedAboutToShow = false
+        componentCreated = false
+    }
+
+    String getName() {
+        nameRetrieved = true;
+        return "Test Tab";
+    }
+
+    Component createComponent() {
+        componentCreated = true;
+        return label;
+    }
+
+    void aboutToShow() {
+        informedAboutToShow = true;
+    }
+}
+
+/**
+ * Class that tracks whether we were prompted to confirm close. It also returns a specific
+ * value to that prompt.
+ */
+class TestCloseInteraction implements BasicGradleUIVersion1.CloseInteraction {
+    boolean wasPromptedToConfirmClose
+    boolean promptResult
+
+
+
+    def TestCloseInteraction(promptResult) {
+        this.promptResult = promptResult;
+    }
+
+    boolean promptUserToConfirmClosingWhileBusy() {
+        wasPromptedToConfirmClose = true
+        return promptResult
+    }
+}
+
+/**
+ * This appends a specified string to the command line when executing a command.
+ */
+class TestCommandLineArgumentAlteringListenerVersion1 implements CommandLineArgumentAlteringListenerVersion1 {
+    private final String additionalArguments;
+
+    def TestCommandLineArgumentAlteringListenerVersion1(additionalArguments) {
+        this.additionalArguments = additionalArguments;
+    }
+
+    String getAdditionalCommandLineArguments(String commandLineArguments) {
+        if (commandLineArguments.startsWith("-s ")) {  //we're only interested in altering this one command
+            return additionalArguments;
+        }
+
+        return null;
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy
new file mode 100644
index 0000000..bc7d6f1
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests
+
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.openapi.external.ui.OutputUILordVersion1
+import org.gradle.openapi.external.ui.SinglePaneUIVersion1
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.PreconditionVerifier
+import org.gradle.util.Requires
+import org.gradle.util.TestPrecondition
+import org.junit.Assert
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+
+import javax.swing.*
+import java.awt.*
+import java.util.concurrent.TimeUnit
+
+import static org.hamcrest.Matchers.startsWith
+
+/**
+ * Tests aspects of the OutputUILord in OpenAPI
+ */
+ at Requires(TestPrecondition.SWING)
+class OutputUILordTest {
+
+    @Rule public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider()
+
+    @Rule public OpenApiFixture openApi = new OpenApiFixture(temporaryFolder)
+    @Rule public TestResources resources = new TestResources(temporaryFolder, 'testProject')
+    @ClassRule public static PreconditionVerifier verifier = new PreconditionVerifier()
+
+    /**
+     * This verifies that you can add file extension to the output lord. This is for
+     * highlighting file links in the output. Here, we're just interested in whether
+     * or not the functions work via/exists in the Open API. The actual functionality
+     * is tested elsewhere.
+     */
+    @Test
+    void testAddingFileExtension() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+        outputUILord.addFileExtension('.txt', ':')
+        java.util.List extensions = outputUILord.getFileExtensions()
+        Assert.assertTrue(extensions.contains('.txt'))
+    }
+
+    /**
+     * This verifies that you can add prefixed file extensions to the output lord. This
+     * is for highlighting file links in the output. Here, we're just interested in whether
+     * or not the functions work via/exists in the Open API. The actual functionality is tested elsewhere.
+     */
+    @Test
+    void testAddingPrefixedFileLink() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+        outputUILord.addPrefixedFileLink("Error Text", "The error is:", ".txt", ":")
+    }
+
+    /**
+     * This tests setting the font. There's not much here to do other than set it and then
+     * get it, making sure its the same. This isn't worried so much about the font itself as
+     * much as the open API doesn't have a problem with setting the font.
+     */
+    @Test
+    void testFont() {
+        if (java.awt.GraphicsEnvironment.isHeadless()) {
+            return;  // Can't run this test in headless mode!
+        }
+
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+        Font font = UIManager.getFont("Button.font")  //this specific font is not important
+
+        //make sure that the above font doesn't happen to be the default font for the output lord. If it
+        //is, this test will silently succeed even if it should fail.
+        Assert.assertNotSame("Fonts are the same. This test is not setup correctly.", font, outputUILord.getOutputTextFont())
+
+        //now set the new font and then make sure it worked
+        outputUILord.setOutputTextFont(font)
+    }
+
+    @Test
+    void testReExecute() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+        OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+        //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+        singlePane.aboutToShow()
+
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver()
+        singlePane.getGradleInterfaceVersion1().addRequestObserver(testRequestObserver)
+
+        //now execute a command
+        singlePane.executeCommand("build", "test build")
+
+        //wait for it to complete
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+        testRequestObserver.reset()
+
+        //now the single command we're trying to test
+        outputUILord.reExecuteLastCommand();
+
+        //wait again for it exit
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+        //make sure it executed the correct request
+        Assert.assertThat(testRequestObserver.request.getFullCommandLine(), startsWith('build'))
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java
new file mode 100644
index 0000000..79d2843
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests;
+
+import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
+
+import java.io.File;
+
+/**
+ * Implementation of AlternateUIInteractionVersion1 for testing purposes.
+ * This would lend itself well for mocking. 
+  */
+public class TestAlternateUIInteractionVersion1 implements AlternateUIInteractionVersion1 {
+
+    private boolean supportsEditingOpeningFiles;
+
+    public TestAlternateUIInteractionVersion1() {
+    }
+
+    public TestAlternateUIInteractionVersion1(boolean supportsEditingOpeningFiles) {
+        this.supportsEditingOpeningFiles = supportsEditingOpeningFiles;
+    }
+
+    public void openFile(File file, int line) {
+
+    }
+
+    public void editFile(File file, int line) {
+
+    }
+
+    public boolean doesSupportEditingOpeningFiles() {
+        return supportsEditingOpeningFiles;
+    }
+
+    public void aboutToExecuteCommand(String fullCommandLine) {
+
+    }
+}
\ No newline at end of file
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestExecutionInteraction.groovy b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestExecutionInteraction.groovy
new file mode 100644
index 0000000..492d28d
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestExecutionInteraction.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests
+
+import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol
+
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.locks.Condition
+import java.util.concurrent.locks.Lock
+import java.util.concurrent.locks.ReentrantLock
+
+//this class just holds onto our liveOutput and also tracks whether or not we've finished.
+public class TestExecutionInteraction implements ExecuteGradleCommandServerProtocol.ExecutionInteraction {
+    private StringBuilder liveOutput = new StringBuilder();
+    public boolean executionFinishedReported = false;
+    public boolean wasSuccessful = false;
+    public String finalMessage;
+    private Throwable failure
+    private final Lock lock = new ReentrantLock()
+    private final Condition condition = lock.newCondition()
+
+    public void reportLiveOutput(String message) {
+        liveOutput.append(message);
+    }
+
+    //when we finish executing, we'll make sure we got some type of live output from gradle.
+
+    public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
+        lock.lock()
+        try {
+            executionFinishedReported = true
+            this.wasSuccessful = wasSuccessful
+            this.finalMessage = message
+            failure = throwable
+            condition.signalAll()
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    def assertCompleted() {
+        lock.lock()
+        try {
+            if (!executionFinishedReported) {
+                throw new AssertionError("Request has not completed.")
+            }
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    public waitForCompletion(int maxWaitValue, TimeUnit maxWaitUnits) {
+        Date expiry = new Date(System.currentTimeMillis() + maxWaitUnits.toMillis(maxWaitValue))
+        lock.lock()
+        try {
+            while (!executionFinishedReported) {
+                if (!condition.awaitUntil(expiry)) {
+                    throw new AssertionError("Timeout waiting for execution to complete.")
+                }
+            }
+            if (failure != null) {
+                throw failure
+            }
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    public void reportExecutionStarted() {}
+
+    public void reportNumberOfTasksToExecute(int size) {}
+
+    public void reportTaskStarted(String message, float percentComplete) {}
+
+    public void reportTaskComplete(String message, float percentComplete) {}
+
+
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java
new file mode 100644
index 0000000..b870883
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests;
+
+import org.gradle.openapi.external.ui.SettingsNodeVersion1;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Implementation of settings node. It basically mirrors a DOM.
+ */
+public class TestSettingsNodeVersion1 implements SettingsNodeVersion1 {
+
+    private String name;
+    private String value;
+    private HashMap<String, String> attributes = new HashMap<String, String>();
+    private SettingsNodeVersion1 parent;
+    private List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
+
+    public TestSettingsNodeVersion1() {
+        //this creates a root settings node.
+    }
+
+    public TestSettingsNodeVersion1(SettingsNodeVersion1 parent) {
+        this.parent = parent;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValueOfChild(String name, String value) {
+        SettingsNodeVersion1 settingsNode = addChildIfNotPresent(name);
+        settingsNode.setValue(value);
+    }
+
+    public String getValueOfChild(String name, String defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+            if (value != null) {
+                return value;
+            }
+        }
+        return defaultValue;
+    }
+
+    public SettingsNodeVersion1 getChildNode(String name) {
+        Iterator<SettingsNodeVersion1> iterator = children.iterator();
+        while (iterator.hasNext()) {
+            SettingsNodeVersion1 childNode = iterator.next();
+            if (name.equals(childNode.getName())) {
+                return childNode;
+            }
+        }
+        return null;
+    }
+
+    public List<SettingsNodeVersion1> getChildNodes() {
+        return children;
+    }
+
+    public List<SettingsNodeVersion1> getChildNodes(String name) {
+        List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
+
+        Iterator<SettingsNodeVersion1> iterator = children.iterator();
+        while (iterator.hasNext()) {
+            SettingsNodeVersion1 childNode = iterator.next();
+            if (name.equals(childNode.getName())) {
+                children.add(childNode);
+            }
+        }
+
+        return children;
+    }
+
+    public int getValueOfChildAsInt(String name, int defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            try {
+                if (value != null) {
+                    return Integer.parseInt(value);
+                }
+            } catch (NumberFormatException e) {
+                //we couldn't parse it. Just return the default.
+            }
+        }
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsInt(String name, int value) {
+        setValueOfChild(name, Integer.toString(value));
+    }
+
+    public long getValueOfChildAsLong(String name, long defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            try {
+                if (value != null) {
+                    return Long.parseLong(value);
+                }
+            } catch (NumberFormatException e) {
+                //we couldn't parse it. Just return the default.
+            }
+        }
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsLong(String name, long value) {
+        setValueOfChild(name, Long.toString(value));
+    }
+
+    public boolean getValueOfChildAsBoolean(String name, boolean defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            //I'm not calling 'Boolean.parseBoolean( value )' because it will return false if the value isn't true/false
+            //and we want it to return whatever the default is if its not a boolean.
+            if (value != null) {
+                if ("true".equalsIgnoreCase(value)) {
+                    return true;
+                }
+
+                if ("false".equalsIgnoreCase(value)) {
+                    return false;
+                }
+            }
+        }
+
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsBoolean(String name, boolean value) {
+        setValueOfChild(name, Boolean.toString(value));
+    }
+
+    public SettingsNodeVersion1 addChild(String name) {
+        SettingsNodeVersion1 childNode = new TestSettingsNodeVersion1(this);
+        childNode.setName(name);
+
+        children.add(childNode);
+        return childNode;
+    }
+
+    public SettingsNodeVersion1 addChildIfNotPresent(String name) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode == null) {
+            settingsNode = addChild(name);
+        }
+
+        return settingsNode;
+    }
+
+    public SettingsNodeVersion1 getNodeAtPath(String... pathPortions) {
+        if (pathPortions == null || pathPortions.length == 0) {
+            return null;
+        }
+
+        String firstPathPortion = pathPortions[0];
+
+        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
+
+        int index = 1; //Skip the first one. we've already used that one.
+        while (index < pathPortions.length && currentNode != null) {
+            String pathPortion = pathPortions[index];
+            currentNode = currentNode.getChildNode(pathPortion);
+            index++;
+        }
+
+        return currentNode;
+    }
+
+    private SettingsNodeVersion1 getNodeAtPathCreateIfNotFound(String... pathPortions) {
+        if (pathPortions == null || pathPortions.length == 0) {
+            return null;
+        }
+
+        String firstPathPortion = pathPortions[0];
+
+        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
+        if (currentNode == null) {
+            currentNode = addChild(firstPathPortion);
+        }
+
+        int index = 1;
+        while (index < pathPortions.length) {
+            String pathPortion = pathPortions[index];
+            currentNode = currentNode.getChildNode(pathPortion);
+            if (currentNode == null) {
+                currentNode = addChild(firstPathPortion);
+            }
+
+            index++;
+        }
+
+        return currentNode;
+    }
+
+    public void removeFromParent() {
+        ((TestSettingsNodeVersion1) this.parent).children.remove(this);
+        this.parent = null;
+    }
+
+    public void removeAllChildren() {
+        children.clear();
+    }
+
+    @Override
+    public String toString() {
+        return getName() + "='" + getValue() + "' " + children.size() + " children";
+    }
+}
diff --git a/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.java b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.java
new file mode 100644
index 0000000..98ef7c5
--- /dev/null
+++ b/subprojects/ui/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.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.integtests;
+
+import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
+import org.gradle.openapi.external.ui.SettingsNodeVersion1;
+import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
+
+/**
+ * This is a test implementation of both SinglePaneUIInteractionVersion1 and DualPaneUIInteractionVersion1.
+ * This is really just a container to hand off more complex interactions to the UI when asked for.
+ */
+public class TestSingleDualPaneUIInteractionVersion1 implements SinglePaneUIInteractionVersion1 {
+
+    private AlternateUIInteractionVersion1 alternateUIInteractionVersion1;
+    private SettingsNodeVersion1 settingsNodeVersion1;
+
+    public TestSingleDualPaneUIInteractionVersion1(AlternateUIInteractionVersion1 alternateUIInteractionVersion1, SettingsNodeVersion1 settingsNodeVersion1) {
+        this.alternateUIInteractionVersion1 = alternateUIInteractionVersion1;
+        this.settingsNodeVersion1 = settingsNodeVersion1;
+    }
+
+    public AlternateUIInteractionVersion1 instantiateAlternateUIInteraction() {
+        return alternateUIInteractionVersion1;
+    }
+
+    public SettingsNodeVersion1 instantiateSettings() {
+        return settingsNodeVersion1;
+    }
+}
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ProjectConverter.java b/subprojects/ui/src/main/java/org/gradle/foundation/ProjectConverter.java
index 92d96c0..1264cfc 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ProjectConverter.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ProjectConverter.java
@@ -17,20 +17,21 @@ package org.gradle.foundation;
 
 import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
+import org.gradle.api.internal.project.ProjectTaskLister;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 /**
  * This converts Gradle's projects into ProjectView objects. These can be safely reused unlike Gradle's projects.
  */
 public class ProjectConverter {
     private List<ProjectView> rootLevelResultingProjects = new ArrayList<ProjectView>();
-    private HashMap<Project, ProjectView> projectMap = new HashMap<Project, ProjectView>();
-    private final Logger logger = Logging.getLogger(ProjectConverter.class);
+    private final ProjectTaskLister taskLister;
 
-    public ProjectConverter() {
+    public ProjectConverter(ProjectTaskLister taskLister) {
+        this.taskLister = taskLister;
     }
 
     /**
@@ -40,23 +41,19 @@ public class ProjectConverter {
      */
     public List<ProjectView> convertProjects(Project rootProject) {
         rootLevelResultingProjects.clear();
-        projectMap.clear();
 
         addRootLevelProject(rootProject);
 
-        buildDependencies();
-
         return rootLevelResultingProjects;
     }
 
     /**
-     * This adds the specified poject as a root level projects. It then adds all tasks and recursively adds all sub projects.
+     * This adds the specified project as a root level projects. It then adds all tasks and recursively adds all sub projects.
      *
      * @param rootLevelProject a root level project.
      */
     public void addRootLevelProject(Project rootLevelProject) {
         ProjectView rootLevelProjectView = new ProjectView(null, rootLevelProject.getName(), rootLevelProject.getBuildFile(), rootLevelProject.getDescription());
-        projectMap.put(rootLevelProject, rootLevelProjectView);
 
         rootLevelResultingProjects.add(rootLevelProjectView);
 
@@ -77,7 +74,6 @@ public class ProjectConverter {
         Collection<Project> subProjects = parentProject.getChildProjects().values();
         for (Project subProject : subProjects) {
             ProjectView projectView = new ProjectView(parentProjectView, subProject.getName(), subProject.getBuildFile(), subProject.getDescription());
-            projectMap.put(subProject, projectView);
 
             addTasks(subProject, projectView);
 
@@ -95,7 +91,7 @@ public class ProjectConverter {
      */
     private void addTasks(Project project, ProjectView projectView) {
         List<String> defaultTasks = project.getDefaultTasks();
-        for (Task task : project.getTasks()) {
+        for (Task task : taskLister.listProjectTasks(project)) {
             String taskName = task.getName();
 
             boolean isDefault = defaultTasks.contains(taskName);
@@ -103,38 +99,4 @@ public class ProjectConverter {
             projectView.createTask(taskName, task.getDescription(), isDefault);
         }
     }
-
-    /**
-     * This sets the dependencies on the ProjectViews. We ask the gradle projects for the dependencies and then convert them to ProjectViews. Obviously, this must be done after converting all Projects
-     * to ProjectViews.
-     */
-    private void buildDependencies() {
-        for (Project project : projectMap.keySet()) {
-            ProjectView projectView = projectMap.get(project);
-
-            List<ProjectView> projectViewList = getProjectViews(project.getDependsOnProjects());
-
-            projectView.setDependsOnProjects(projectViewList);
-        }
-    }
-
-    /**
-     * Converts a set of projects to the existing project views. This does not actually instantiate new ProjectView objects.
-     */
-    private List<ProjectView> getProjectViews(Set<Project> projects) {
-        List<ProjectView> views = new ArrayList<ProjectView>();
-
-        Iterator<Project> projectIterator = projects.iterator();
-        while (projectIterator.hasNext()) {
-            Project project = projectIterator.next();
-            ProjectView projectView = projectMap.get(project);
-            if (projectView == null) {
-                logger.error("Missing project: " + project.getName());
-            } else {
-                views.add(projectView);
-            }
-        }
-
-        return views;
-    }
 }
\ No newline at end of file
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ProjectView.java b/subprojects/ui/src/main/java/org/gradle/foundation/ProjectView.java
index de3fe02..9a7d079 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ProjectView.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ProjectView.java
@@ -34,7 +34,6 @@ public class ProjectView implements Comparable<ProjectView>, Serializable {
     // It is null for the root project.
     private final List<ProjectView> subProjects = new ArrayList<ProjectView>();
     private final List<TaskView> tasks = new ArrayList<TaskView>();
-    private final List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();
 
     private final File buildFile;
     private final String description;
@@ -91,22 +90,6 @@ public class ProjectView implements Comparable<ProjectView>, Serializable {
         subProjects.add(subProject);
     }
 
-    /**
-     * Sets the project that this project depends on. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
-     */
-    /*package*/ void setDependsOnProjects(List<ProjectView> newDependsOnProjects) {
-        if (newDependsOnProjects == null) {
-            return;
-        }
-
-        this.dependsOnProjects.clear();
-        this.dependsOnProjects.addAll(newDependsOnProjects);
-    }
-
-    public List<ProjectView> getDependsOnProjects() {
-        return dependsOnProjects;
-    }
-
     public List<TaskView> getTasks() {
         return Collections.unmodifiableList(tasks);
     }
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/basic/ClientProcess.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/basic/ClientProcess.java
index 5cc8a62..b01dac4 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/basic/ClientProcess.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/basic/ClientProcess.java
@@ -15,12 +15,12 @@
  */
 package org.gradle.foundation.ipc.basic;
 
+import org.gradle.api.UncheckedIOException;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 
 import java.io.IOException;
 import java.io.Serializable;
-import java.net.ConnectException;
 import java.net.Socket;
 
 /**
@@ -64,33 +64,26 @@ public class ClientProcess {
      * Call this to attempt to connect to the server.
      *
      * @param port where the server is listening. Since it launched this client, it should have either been passed to it on the command line or via a system property (-D).
-     * @return true if we connected to the server, false if not.
      */
-    public boolean start(int port) {
-        Socket clientSocket = null;
+    public void start(int port) {
+        Socket clientSocket;
         try {
             clientSocket = new Socket((String) null, port);
             socketWrapper = new ObjectSocketWrapper(clientSocket);
             if (protocol.serverConnected(clientSocket)) {
-                return true;
+                return;
             }
-
-            logger.error("Failed to connect to server (might not have returned correct connection string): " + port);
-        } catch (ConnectException e) {
-            logger.error("Failed to connect to server: " + port);
-        } catch (Exception e) {
-            logger.error("Failed to connect to server: " + port, e);
+        } catch (IOException e) {
+            throw new UncheckedIOException(String.format("Could not connect to GUI server at port %s.", port), e);
         }
 
         try {
-            if (clientSocket != null) {
-                clientSocket.close();
-            }
+            clientSocket.close();
             socketWrapper = null;
-        } catch (IOException e1) {
-            logger.error("Failed to close socket", e1);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
         }
-        return false;
+        throw new UncheckedIOException("GUI protocol failed to connect.");
     }
 
     /**
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
index 92c044e..311e64b 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
@@ -117,7 +117,7 @@ public abstract class AbstractGradleServerProtocol implements ProcessLauncherSer
     }
 
     /**
-     * Notification that a message has been received. If we just connected, we'll do a quick handshake to verify the client, then we just pass the rest on our our output panel.
+     * Notification that a message has been received. If we just connected, we'll do a quick handshake to verify the client, then we just pass the rest on our output panel.
      *
      * @param message the message that was received.
      */
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandServerProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandServerProtocol.java
index 8af79a8..b7af1bc 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandServerProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/ExecuteGradleCommandServerProtocol.java
@@ -109,7 +109,7 @@ public class ExecuteGradleCommandServerProtocol extends AbstractGradleServerProt
     }
 
     /**
-     * This is called when when the client exits. This does not mean it succeeded. This is probably the only way you'll get ALL of the client's output as it continues to output things like error
+     * This is called when the client exits. This does not mean it succeeded. This is probably the only way you'll get ALL of the client's output as it continues to output things like error
      * messages after it sends us an executionFinished message.
      *
      * @param returnCode the return code of the application
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/GradleClient.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/GradleClient.java
index 55825fc..3986b5f 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/GradleClient.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/GradleClient.java
@@ -15,9 +15,8 @@
  */
 package org.gradle.foundation.ipc.gradle;
 
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
 import org.gradle.foundation.ipc.basic.ClientProcess;
+import org.gradle.internal.UncheckedException;
 
 import java.io.Serializable;
 
@@ -28,31 +27,24 @@ import java.io.Serializable;
  */
 public class GradleClient {
     private ClientProcess clientProcess;
-    private final Logger logger = Logging.getLogger(GradleClient.class);
-
-    public GradleClient() {
-    }
 
     /**
      * Call this to start the client. This version gets the port number as a system property. It does nothing if this property isn't defined.
      *
      * @param protocol the protocol to use to communicate with the server.
-     * @return true if successful, false if not.
      */
-    public boolean start(ClientProcess.Protocol protocol) {
+    public void start(ClientProcess.Protocol protocol) {
         //make sure we've been given the port number to use
         String portText = System.getProperty(ProtocolConstants.PORT_NUMBER_SYSTEM_PROPERTY);
         if (portText == null) {
-            logger.error("No port number specified. Cannot run client");
-            return false;
+            throw new RuntimeException("No port number specified. Cannot run client");
         }
 
         try {
             int port = Integer.parseInt(portText);
-            return start(protocol, port);
+            start(protocol, port);
         } catch (NumberFormatException e) {
-            logger.error("Parsing port '" + portText + "'", e);
-            return false;
+            throw UncheckedException.throwAsUncheckedException(e);
         }
     }
 
@@ -61,16 +53,10 @@ public class GradleClient {
      *
      * @param protocol the protocol to use to communicate with the server.
      * @param port the port the server is listening on
-     * @return true if successful, false if not.
      */
-    public boolean start(ClientProcess.Protocol protocol, int port) {
+    public void start(ClientProcess.Protocol protocol, int port) {
         clientProcess = new ClientProcess(protocol);
-
-        if (!clientProcess.start(port)) {
-            return false;
-        }
-
-        return true;
+        clientProcess.start(port);
     }
 
     /**
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/KillGradleServerProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/KillGradleServerProtocol.java
index 13323c0..29bf2e4 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/KillGradleServerProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/KillGradleServerProtocol.java
@@ -22,14 +22,10 @@ import org.gradle.foundation.ipc.basic.Server;
  * This protocol is used by a client that launches its own server. See KillGradleClientProtocol.
  */
 public class KillGradleServerProtocol implements Server.Protocol<Server> {
-    private Server server;
-
     public void initialize(Server server) {
-        this.server = server;
     }
 
     public void connectionAccepted() {
-
     }
 
     public boolean continueConnection() {
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
index cff7b00..10a639a 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
@@ -17,6 +17,8 @@ package org.gradle.foundation.ipc.gradle;
 
 import org.gradle.BuildAdapter;
 import org.gradle.BuildResult;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectTaskLister;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -91,7 +93,7 @@ public class TaskListClientProtocol implements ClientProcess.Protocol {
 
                 client.sendMessage(ProtocolConstants.TASK_LIST_COMPLETED_WITH_ERRORS_TYPE, output, wasSuccessful);
             } else {
-                ProjectConverter buildExecuter = new ProjectConverter();
+                ProjectConverter buildExecuter = new ProjectConverter(((GradleInternal) buildResult.getGradle()).getServices().get(ProjectTaskLister.class));
                 List<ProjectView> projects = new ArrayList<ProjectView>();
                 projects.addAll(buildExecuter.convertProjects(buildResult.getGradle().getRootProject()));
 
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
index 531afa9..4ff6059 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
@@ -92,7 +92,7 @@ public class TaskListServerProtocol extends AbstractGradleServerProtocol {
     }
 
     /**
-     * This is called when when the client exits. This does not mean it succeeded. This is probably the only way you'll get ALL of the client's output as it continues to output things like error
+     * This is called when the client exits. This does not mean it succeeded. This is probably the only way you'll get ALL of the client's output as it continues to output things like error
      * messages after it sends us an executionFinished message.
      *
      * @param returnCode the return code of the application
diff --git a/subprojects/ui/src/main/java/org/gradle/foundation/queue/ExecutionQueue.java b/subprojects/ui/src/main/java/org/gradle/foundation/queue/ExecutionQueue.java
index d23d21f..886dc2a 100644
--- a/subprojects/ui/src/main/java/org/gradle/foundation/queue/ExecutionQueue.java
+++ b/subprojects/ui/src/main/java/org/gradle/foundation/queue/ExecutionQueue.java
@@ -112,9 +112,10 @@ public class ExecutionQueue<R extends ExecutionQueue.Request> {
         public void run() {
             while (true) {
                 R request = getNextAvailableRequest();
-                if (request != null) {
-                    executeInteraction.execute(request);
+                if (request == null) {
+                    return;
                 }
+                executeInteraction.execute(request);
             }
         }
     }
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
index bd6290b..653ca33 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
@@ -17,10 +17,10 @@ package org.gradle.gradleplugin.foundation;
 
 import org.codehaus.groovy.runtime.StackTraceUtils;
 import org.gradle.api.internal.classpath.DefaultModuleRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.foundation.CommandLineAssistant;
 import org.gradle.foundation.ProjectView;
 import org.gradle.foundation.TaskView;
@@ -126,8 +126,8 @@ public class GradlePluginLord {
     public interface SettingsObserver {
 
         /**
-         * Notification that some settings have changed for the plugin. Settings such as current directory, gradle home directory, etc. This is useful for UIs that need to update their UIs when this is
-         * changed by other means.
+         * Notification that some settings have changed for the plugin. Settings such as current directory, gradle home directory, etc. This is useful for UIs that need to update their UIs when this
+         * is changed by other means.
          */
         public void settingsChanged();
     }
@@ -137,7 +137,7 @@ public class GradlePluginLord {
 
         //create the queue that executes the commands. The contents of this interaction are where we actually launch gradle.
 
-        currentDirectory = SystemProperties.getCurrentDir();
+        currentDirectory = SystemProperties.getInstance().getCurrentDir();
 
         String gradleHomeProperty = System.getProperty("gradle.home");
         if (gradleHomeProperty != null) {
@@ -148,6 +148,9 @@ public class GradlePluginLord {
     }
 
     public File getGradleHomeDirectory() {
+        if (gradleHomeDirectory == null || !gradleHomeDirectory.isDirectory()) {
+            throw new IllegalArgumentException("Could not locate Gradle home directory.");
+        }
         return gradleHomeDirectory;
     }
 
@@ -459,7 +462,7 @@ public class GradlePluginLord {
         //we'll request a task list since there is no way to do a no op. We're not really interested
         //in what's being executed, just the ability to get the task list (which must be populated as
         //part of executing anything).
-        String fullCommandLine = ImplicitTasksConfigurer.TASKS_TASK;
+        String fullCommandLine = ProjectInternal.TASKS_TASK;
 
         if (additionalCommandLineArguments != null) {
             fullCommandLine += ' ' + additionalCommandLineArguments;
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/request/ExecutionRequest.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/request/ExecutionRequest.java
index d5571e7..d3c18a7 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/request/ExecutionRequest.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/request/ExecutionRequest.java
@@ -25,7 +25,7 @@ import org.gradle.logging.ShowStacktrace;
 import java.io.File;
 
 /**
- * This represents a reques to gradle that is executed in a separate process using the ProcessLauncherServer. This version is for directly executing commands in gradle (the most common type of
+ * This represents a request to gradle that is executed in a separate process using the ProcessLauncherServer. This version is for directly executing commands in gradle (the most common type of
  * request).
  */
 public class ExecutionRequest extends AbstractRequest {
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/runner/GradleRunner.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/runner/GradleRunner.java
deleted file mode 100644
index 87cb237..0000000
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/foundation/runner/GradleRunner.java
+++ /dev/null
@@ -1,73 +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.gradleplugin.foundation.runner;
-
-import org.gradle.api.logging.LogLevel;
-import org.gradle.foundation.ipc.basic.ProcessLauncherServer;
-import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol;
-import org.gradle.logging.ShowStacktrace;
-
-import java.io.File;
-
-/**
- * This executes a command line in an external process.
- */
-public class GradleRunner {
-    private File currentDirectory;
-    private File gradleHomeDirectory;
-    private File customGradleExecutor;
-    private ProcessLauncherServer server;
-
-    public GradleRunner(File currentDirectory, File gradleHomeDirectory, File customGradleExecutor) {
-        this.currentDirectory = currentDirectory;
-        this.gradleHomeDirectory = gradleHomeDirectory;
-        this.customGradleExecutor = customGradleExecutor;
-    }
-
-    public synchronized void executeCommand(String commandLine, LogLevel logLevel, ShowStacktrace stackTraceLevel,
-                                            ExecuteGradleCommandServerProtocol.ExecutionInteraction executionInteraction) {
-        //the protocol manages the command line and messaging observers
-        ExecuteGradleCommandServerProtocol serverProtocol = new ExecuteGradleCommandServerProtocol(currentDirectory, gradleHomeDirectory, customGradleExecutor, commandLine, logLevel, stackTraceLevel,
-                executionInteraction);
-
-        //the server kicks off gradle as an external process and manages the communication with said process
-        server = new ProcessLauncherServer(serverProtocol);
-        server.addServerObserver(new ProcessLauncherServer.ServerObserver() {
-            public void clientExited(int result, String output) {
-            }
-
-            public void serverExited() {
-                clearServer();
-            }
-        }, false);
-
-        executionInteraction.reportExecutionStarted();  //go ahead and fire off that the execution has started. Normally, this is done by the request, but we don't have a request in this case.
-        server.start();
-    }
-
-    /**
-     * Call this to stop the gradle process.
-     */
-    public synchronized void killProcess() {
-        if (server != null) {
-            server.killProcess();
-        }
-    }
-
-    private synchronized void clearServer() {
-        server = null;
-    }
-}
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
index 0d6348d..a7d33c0 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
@@ -127,7 +127,7 @@ public class PreferencesAssistant {
     /**
      * Saves the settings of the file chooser; and by settings I mean the 'last visited directory'.
      *
-     * @param saveCurrentDirectoryVsSelectedFilesParent this should be true true if you're selecting only directories, false if you're selecting only files. I don't know what if you allow both.
+     * @param saveCurrentDirectoryVsSelectedFilesParent this should be true if you're selecting only directories, false if you're selecting only files. I don't know what if you allow both.
      */
     public static void saveSettings(SettingsNode settingsNode, JFileChooser fileChooser, String id, Class fileChooserClass, boolean saveCurrentDirectoryVsSelectedFilesParent) {
         SettingsNode childNode = settingsNode.addChildIfNotPresent(getPrefix(fileChooserClass, id));
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/DualPaneUIInstance.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/DualPaneUIInstance.java
deleted file mode 100644
index a1e9e8f..0000000
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/DualPaneUIInstance.java
+++ /dev/null
@@ -1,57 +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.gradleplugin.userinterface.swing.generic;
-
-import org.gradle.gradleplugin.foundation.settings.SettingsNode;
-import org.gradle.gradleplugin.userinterface.AlternateUIInteraction;
-
-import javax.swing.*;
-import java.awt.*;
-
-/**
- * A simple UI for gradle. This has two panels that can be inserted into a stand-alone application or an IDE. This is meant to hide most of the complexities of gradle. The two panes are a tabbed pane
- * for executing tasks and an output pane.
- */
-public class DualPaneUIInstance extends AbstractGradleUIInstance {
-    private OutputPanelLord outputPanelLord;
-
-    public DualPaneUIInstance() {
-    }
-
-    public void initialize(SettingsNode settings, AlternateUIInteraction alternateUIInteraction) {
-
-        outputPanelLord = new OutputPanelLord(gradlePluginLord, alternateUIInteraction);
-
-        super.initialize(settings, alternateUIInteraction);
-    }
-
-    /**
-     * We've overridden this to setup our splitter and our output window.
-     */
-    @Override
-    protected void setupUI() {
-        mainPanel = new JPanel(new BorderLayout());
-        mainPanel.add(createMainGradlePanel(), BorderLayout.CENTER);
-    }
-
-    public OutputUILord getOutputUILord() {
-        return outputPanelLord;
-    }
-
-    public Component getOutputPanel() {
-        return outputPanelLord.getMainPanel();
-    }
-}
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/OutputPanel.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/OutputPanel.java
index 9a52d45..c4c8a01 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/OutputPanel.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/OutputPanel.java
@@ -461,7 +461,7 @@ public class OutputPanel extends JPanel implements ExecuteGradleCommandServerPro
      *
      * @param size the total number of tasks.
      */
-    public void reportNumberOfTasksToExecute(final int size) {  //if we only have a single task, then the intire process will be indeterminately long (it'll just from 0 to 100)
+    public void reportNumberOfTasksToExecute(final int size) {  //if we only have a single task, then the entire process will be indeterminately long (it'll just from 0 to 100)
         SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 boolean isIndeterminate = size == 1;
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/SwingEditFavoriteInteraction.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/SwingEditFavoriteInteraction.java
index f73dbc5..ceee18d 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/SwingEditFavoriteInteraction.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/SwingEditFavoriteInteraction.java
@@ -30,7 +30,7 @@ import java.awt.event.*;
 public class SwingEditFavoriteInteraction implements FavoritesEditor.EditFavoriteInteraction {
 
     public enum SynchronizeType {
-        OnlyIfAlreadySynchronized,   //the the display name in synch with the command only if they are already synchronized (and it can be overridden by the user if they change the display name manually)
+        OnlyIfAlreadySynchronized,   //the display name in synch with the command only if they are already synchronized (and it can be overridden by the user if they change the display name manually)
         Never                        //Do not attempt to keep them in synch
     };
 
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
index 80d079c..d3922af 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
@@ -191,7 +191,7 @@ public class SetupTab implements GradleTab, GradlePluginLord.SettingsObserver {
     private File browseForDirectory(File initialFile) {
 
         if (initialFile == null) {
-            initialFile = SystemProperties.getCurrentDir();
+            initialFile = SystemProperties.getInstance().getCurrentDir();
         }
 
         JFileChooser chooser = new JFileChooser(initialFile);
@@ -490,7 +490,7 @@ public class SetupTab implements GradleTab, GradlePluginLord.SettingsObserver {
      * Call this to browse for a custom gradle executor.
      */
     private void browseForCustomGradleExecutor() {
-        File startingDirectory = new File(SystemProperties.getUserHome());
+        File startingDirectory = new File(SystemProperties.getInstance().getUserHome());
         File currentFile = gradlePluginLord.getCustomGradleExecutor();
         if (currentFile != null) {
             startingDirectory = currentFile.getAbsoluteFile();
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/Application.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/Application.java
index 751e819..883b09d 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/Application.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/Application.java
@@ -57,28 +57,11 @@ public class Application implements AlternateUIInteraction {
      */
     public interface LifecycleListener {
         /**
-         * Notification that the application has started successfully. This is fired within the same thread that instantiates us.
-         */
-        public void hasStarted();
-
-        /**
          * Notification that the application has shut down. This is fired from the Event Dispatch Thread.
          */
         public void hasShutDown();
     }
 
-    public static void main(String[] args) {
-        new Application(new LifecycleListener() {
-            public void hasStarted() {
-                //we don't care
-            }
-
-            public void hasShutDown() {
-                System.exit(0);
-            }
-        });
-    }
-
     public Application(LifecycleListener lifecycleListener) {
         this.lifecycleListener = lifecycleListener;
 
@@ -104,8 +87,6 @@ public class Application implements AlternateUIInteraction {
         restoreSettings();
 
         frame.setVisible(true);
-
-        lifecycleListener.hasStarted();  //notify listeners that we have successfully started
     }
 
     private void setupUI() {
@@ -306,7 +287,7 @@ public class Application implements AlternateUIInteraction {
      * @return the file that we save our settings to.
      */
     private File getSettingsFile() {
-        return new File(SystemProperties.getCurrentDir(), "gradle-app" + SETTINGS_EXTENSION);
+        return new File(SystemProperties.getInstance().getCurrentDir(), "gradle-app" + SETTINGS_EXTENSION);
     }
 
     private class SettingsImportInteraction implements DOM4JSerializer.ImportInteraction {
diff --git a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java
index 1cbd2c5..6cd80fa 100644
--- a/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java
+++ b/subprojects/ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/standalone/BlockingApplication.java
@@ -19,6 +19,7 @@ import org.gradle.internal.UncheckedException;
 
 import javax.swing.*;
 import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * This is the same as Application, but this version blocks the calling thread until the Application shuts down.
@@ -34,70 +35,25 @@ public class BlockingApplication {
         }
 
         //create a lock to wait on
-        final WaitingLock waitingLock = new WaitingLock();
+        final CountDownLatch completed = new CountDownLatch(1);
 
         //instantiate the app in the Event Dispatch Thread
         try {
             SwingUtilities.invokeAndWait(new Runnable() {
                 public void run() {
                     new Application(new Application.LifecycleListener() {
-                        /**
-                         Notification that the application has started successfully. This is
-                         fired within the same thread that instantiates us.
-                         */
-                        public void hasStarted() {  //only lock if we start
-                            waitingLock.lock();
-                        }
-
-                        /**
-                         Notification that the application has shut down. This is fired from the
-                         Event Dispatch Thread.
-                         */
-                        public void hasShutDown() {  //when we shutdown we'll unlock
-                            waitingLock.unlock();
+                        public void hasShutDown() {
+                            completed.countDown();
                         }
                     });
                 }
             });
+
+            completed.await();
         } catch (InterruptedException e) {
             throw UncheckedException.throwAsUncheckedException(e);
         } catch (InvocationTargetException e) {
             throw UncheckedException.unwrapAndRethrow(e);
         }
-
-        //the calling thread will now block until the caller is complete.
-        waitingLock.waitOnLock();
-    }
-
-    /**
-     * Lock so the calling thread can wait on the Application to exit.
-     */
-    private static class WaitingLock {
-        private boolean isLocked;
-
-        public synchronized void lock() {
-            isLocked = true;
-
-            //Notify status has changed.
-            notifyAll();
-        }
-
-        public synchronized void unlock() {
-            isLocked = false;
-
-            //Notify status has changed.
-            notifyAll();
-        }
-
-        public synchronized void waitOnLock() {
-            //Wait only if we're locked
-            while (isLocked) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    throw new UncheckedException(e);
-                }
-            }
-        }
     }
 }
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/NoLongerSupportedException.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/NoLongerSupportedException.java
new file mode 100644
index 0000000..40983f5
--- /dev/null
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/NoLongerSupportedException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.openapi.wrappers;
+
+public class NoLongerSupportedException extends UnsupportedOperationException {
+    public NoLongerSupportedException() {
+        super("Support for the Gradle Open API was removed in the Gradle 2.0 release. You should use the Gradle tooling API instead.");
+    }
+}
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/RunnerWrapperFactory.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/RunnerWrapperFactory.java
deleted file mode 100644
index 8ec718a..0000000
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/RunnerWrapperFactory.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.openapi.wrappers;
-
-import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1;
-import org.gradle.openapi.external.runner.GradleRunnerVersion1;
-import org.gradle.openapi.wrappers.runner.GradleRunnerWrapper;
-
-import java.io.File;
-
-/**
- * This factory instantiates GradleRunnerWrappers by an external process. It is meant to be called via the Open API GradleRunnerFactory class using reflection. This is because it is called
- * dynamically. It is also meant to help shield a Gradle user from changes to different versions of the GradleRunner. It does so by using wrappers that can dynamically choose what/how to implement.
- * The wrappers usually use the latest, however, some of the functionality requires a matching Open API jar (which will be included with the external process trying to use this). If the matching
- * functionality is not found (a NoClassDefFoundError is thrown), it will fall back to earlier versions.
- *
- * This class should not be moved or renamed, nor should its functions be renamed or have arguments added to/removed from them. This is to ensure forward/backward compatibility with multiple versions
- * of IDE plugins. Instead, consider changing the interaction that is passed to the functions as a means of having the caller provide different functionality.
- */
-public class RunnerWrapperFactory {
-
-    /*
-      Call this to instantiate an object that you can use to execute gradle
-      commands directly.
-
-      Note: this function is meant to be backward and forward compatible. So
-      this signature should not change at all, however, it may take and return
-      objects that implement ADDITIONAL interfaces. That is, it will always
-      return a GradleRunnerVersion1, but it may also be an object that implements
-      GradleRunnerVersion2 (notice the 2). The caller will need to dynamically
-      determine that. The GradleRunnerInteractionVersion1 may take an object
-      that also implements GradleRunnerInteractionVersion2. If so, we'll
-      dynamically determine that and handle it. Of course, this all depends on
-      what happens in the future.
-
-      @param  gradleHomeDirectory  the root directory of a gradle installation
-      @param  interaction          this is how we interact with the caller.
-      @param  showDebugInfo        true to show some additional information that
-                                   may be helpful diagnosing problems is this
-                                   fails
-      @return a gradle runner
-   */
-    public static GradleRunnerVersion1 createGradleRunner(File gradleHomeDirectory, GradleRunnerInteractionVersion1 interaction, boolean showDebugInfo) throws Exception {
-        return new GradleRunnerWrapper(gradleHomeDirectory, interaction);
-    }
-}
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/UIWrapperFactory.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/UIWrapperFactory.java
deleted file mode 100644
index 138f497..0000000
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/UIWrapperFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.openapi.wrappers;
-
-import org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1;
-import org.gradle.openapi.external.ui.DualPaneUIVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
-import org.gradle.openapi.wrappers.ui.DualPaneUIWrapper;
-import org.gradle.openapi.wrappers.ui.SinglePaneUIWrapper;
-
-/**
- * This factory instantiates Gradle UIs used in IDE plugins. It is meant to be called via the Open API UIFactory class using reflection. This is because it is called dynamically. It is also meant to
- * help shield a Gradle user from changes to different versions of UI. It does so by using wrappers that can dynamically choose what/how to implement. The wrappers usually use the latest, however,
- * some of the functionality requires a matching Open API jar (which will be included with the plugin trying to use this). If the matching functionality is not found (a NoClassDefFoundError is
- * thrown), it will fall back to earlier versions.
- *
- * This class should not be moved or renamed, nor should its functions be renamed or have arguments added to/removed from them. This is to ensure forward/backward compatibility with multiple versions
- * of IDE plugins. Instead, consider changing the interaction that is passed to the functions as a means of having the caller provide different functionality.
- */
-public class UIWrapperFactory {
-
-    /**
-     * Creates a single-pane Gradle UI. The main UI and output panes are self-contained.
-     *
-     * @param interaction this is how we interact with the caller.
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return a single pane UI.
-     */
-    public static SinglePaneUIVersion1 createSinglePaneUI(final SinglePaneUIInteractionVersion1 interaction, boolean showDebugInfo) throws Exception {
-        return new SinglePaneUIWrapper(interaction);
-    }
-
-    /**
-     * Creates a dual-pane Gradle UI, consisting of a main panel (containing task tree, favorites, etc) and a separate panel containing the output.
-     *
-     * @param interaction this is how we interact with the caller.
-     * @param showDebugInfo true to show some additional information that may be helpful diagnosing problems is this fails
-     * @return a dual pane UI.
-     */
-    public static DualPaneUIVersion1 createDualPaneUI(final DualPaneUIInteractionVersion1 interaction, boolean showDebugInfo) throws Exception {
-        return new DualPaneUIWrapper(interaction);
-    }
-}
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/ProjectWrapper.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/ProjectWrapper.java
index 1168f8d..cf18449 100644
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/ProjectWrapper.java
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/foundation/ProjectWrapper.java
@@ -22,6 +22,7 @@ import org.gradle.openapi.external.foundation.TaskVersion1;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -57,7 +58,7 @@ public class ProjectWrapper implements ProjectVersion1 {
     }
 
     public List<ProjectVersion1> getDependantProjects() {
-        return convertProjects(projectView.getDependsOnProjects());
+        return Collections.emptyList();
     }
 
     public ProjectVersion1 getSubProject(String name) {
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerInteractionWrapper.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerInteractionWrapper.java
deleted file mode 100644
index 11df951..0000000
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerInteractionWrapper.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.openapi.wrappers.runner;
-
-import org.gradle.api.logging.LogLevel;
-import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol;
-import org.gradle.logging.ShowStacktrace;
-import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1;
-
-import java.io.File;
-
-/**
- * Wrapper to shield version changes in GradleRunnerInteractionVersion1 from an external user of gradle open API.
- */
-public class GradleRunnerInteractionWrapper implements ExecuteGradleCommandServerProtocol.ExecutionInteraction {
-    private GradleRunnerInteractionVersion1 interactionVersion1;
-
-    public GradleRunnerInteractionWrapper(GradleRunnerInteractionVersion1 interactionVersion1) {
-        this.interactionVersion1 = interactionVersion1;
-    }
-
-    /**
-     * @return the log level. This determines the detail level of information reported via reportLiveOutput and reportExecutionFinished.
-     */
-    public LogLevel getLogLevel() {
-        GradleRunnerInteractionVersion1.LogLevel logLevel = interactionVersion1.getLogLevel();
-        switch (logLevel) {
-            case Quiet:
-                return LogLevel.QUIET;
-            case Lifecycle:
-                return LogLevel.LIFECYCLE;
-            case Debug:
-                return LogLevel.DEBUG;
-        }
-
-        return LogLevel.LIFECYCLE;
-    }
-
-    /**
-     * @return the stack trace level. This determines the detail level of any stack traces should an exception occur.
-     */
-    public ShowStacktrace getStackTraceLevel() {
-        GradleRunnerInteractionVersion1.StackTraceLevel stackTraceLevel = interactionVersion1.getStackTraceLevel();
-        switch (stackTraceLevel) {
-            case InternalExceptions:
-                return ShowStacktrace.INTERNAL_EXCEPTIONS;
-            case Always:
-                return ShowStacktrace.ALWAYS;
-            case AlwaysFull:
-                return ShowStacktrace.ALWAYS_FULL;
-        }
-
-        return ShowStacktrace.INTERNAL_EXCEPTIONS;
-    }
-
-    /**
-     * Notification that overall execution has been started. This is only called once at the end.
-     */
-    public void reportExecutionStarted() {
-        this.interactionVersion1.reportExecutionStarted();
-    }
-
-    /**
-     * Notification of the total number of tasks that will be executed. This is called after reportExecutionStarted and before any tasks are executed.
-     *
-     * @param size the total number of tasks.
-     */
-    public void reportNumberOfTasksToExecute(int size) {
-        this.interactionVersion1.reportNumberOfTasksToExecute(size);
-    }
-
-    /**
-     * Notification that a single task has completed. Note: the task you kicked off probably executes other tasks and this notifies you of those tasks and provides completion progress.
-     *
-     * @param currentTaskName the task being executed
-     * @param percentComplete the percent complete of all the tasks that make up the task you requested.
-     */
-    public void reportTaskStarted(String currentTaskName, float percentComplete) {
-        this.interactionVersion1.reportTaskStarted(currentTaskName, percentComplete);
-    }
-
-    public void reportTaskComplete(String currentTaskName, float percentComplete) {
-        this.interactionVersion1.reportTaskComplete(currentTaskName, percentComplete);
-    }
-
-    /**
-     * Report real-time output from gradle and its subsystems (such as ant).
-     *
-     * @param output a single line of text to show.
-     */
-    public void reportLiveOutput(String output) {
-        this.interactionVersion1.reportLiveOutput(output);
-    }
-
-    public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-        this.interactionVersion1.reportExecutionFinished(wasSuccessful, message, throwable);
-    }
-
-    /*
-    * This is called to get a custom gradle executable file. If you don't run
-    * gradle.bat or gradle shell script to run gradle, use this to specify
-    * what you do run. Note: we're going to pass it the arguments that we would
-    * pass to gradle so if you don't like that, see alterCommandLineArguments.
-    * Normaly, this should return null.
-    * @return the Executable to run gradle command or null to use the default
-    */
-    public File getCustomGradleExecutable() {
-        return interactionVersion1.getCustomGradleExecutable();
-    }
-}
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerWrapper.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerWrapper.java
index f3eb7bd..9b93689 100644
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerWrapper.java
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/runner/GradleRunnerWrapper.java
@@ -15,40 +15,16 @@
  */
 package org.gradle.openapi.wrappers.runner;
 
-import org.gradle.gradleplugin.foundation.runner.GradleRunner;
 import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1;
-import org.gradle.openapi.external.runner.GradleRunnerVersion1;
+import org.gradle.openapi.wrappers.NoLongerSupportedException;
 
 import java.io.File;
 
 /**
- * Wrapper to shield version changes in GradleRunner from an external user of gradle open API.
+ * Entry point for the open API. No longer usable but still implemented so that old clients receive a decent error message.
  */
-public class GradleRunnerWrapper implements GradleRunnerVersion1 {
-    private GradleRunner gradleRunner;
-    private File gradleHomeDirectory;
-    private File workingDirectory;
-    private GradleRunnerInteractionWrapper interactionWrapper;
-
+public class GradleRunnerWrapper {
     public GradleRunnerWrapper(File gradleHomeDirectory, GradleRunnerInteractionVersion1 interactionVersion1) {
-        this.gradleHomeDirectory = gradleHomeDirectory;
-        this.workingDirectory = interactionVersion1.getWorkingDirectory();
-        interactionWrapper = new GradleRunnerInteractionWrapper(interactionVersion1);
-        File customGradleExecutable = interactionVersion1.getCustomGradleExecutable();
-
-        gradleRunner = new GradleRunner(workingDirectory, gradleHomeDirectory, customGradleExecutable);
-    }
-
-    public void executeCommand(String commandLine) {
-        gradleRunner.executeCommand(commandLine, interactionWrapper.getLogLevel(), interactionWrapper.getStackTraceLevel(), interactionWrapper);
-    }
-
-    /*
-       Call this to stop the gradle command. This is killing the process, not
-       gracefully exiting.
-    */
-
-    public void killProcess() {
-        gradleRunner.killProcess();
+        throw new NoLongerSupportedException();
     }
 }
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/DualPaneUIWrapper.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/DualPaneUIWrapper.java
index 7005120..ec1de42 100644
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/DualPaneUIWrapper.java
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/DualPaneUIWrapper.java
@@ -15,47 +15,14 @@
  */
 package org.gradle.openapi.wrappers.ui;
 
-import org.gradle.gradleplugin.userinterface.swing.generic.DualPaneUIInstance;
 import org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1;
-import org.gradle.openapi.external.ui.DualPaneUIVersion1;
-
-import javax.swing.*;
-import java.awt.*;
+import org.gradle.openapi.wrappers.NoLongerSupportedException;
 
 /**
- * This wraps a DualPaneUIVersion1 for the purpose of being instantiated for an external tool such an IDE plugin. It wraps several interfaces and uses delegation in an effort to make this backward and
- * forward compatible. Most of the work is done in AbstractOpenAPIUIWrapper
+ * Entry point for the open API. No longer usable but still implemented so that old clients receive a decent error message.
  */
-public class DualPaneUIWrapper extends AbstractOpenAPIUIWrapper<DualPaneUIInstance> implements DualPaneUIVersion1 {
+public class DualPaneUIWrapper {
     public DualPaneUIWrapper(DualPaneUIInteractionVersion1 dualPaneUIArguments) {
-
-        super(dualPaneUIArguments.instantiateSettings(), dualPaneUIArguments.instantiateAlternateUIInteraction());
-
-        //the main thing this does in instantiate the DualPaneUIInstance.
-        DualPaneUIInstance uiInstance = new DualPaneUIInstance();
-        uiInstance.initialize(settingsVersionWrapper, alternateUIInteractionVersionWrapper);
-        initialize(uiInstance);
-    }
-
-    /**
-     * Returns a component that shows the task tree tab, favorites tab, etc. suitable for inserting in your UI.
-     *
-     * @return the main component
-     */
-    public JComponent getMainComponent() {
-        return getGradleUI().getComponent();
-    }
-
-    /**
-     * Returns a component that shows the output of the tasks being executed.
-     *
-     * @return the output component
-     */
-    public Component getOutputPanel() {
-        return getGradleUI().getOutputPanel();
-    }
-
-    public int getNumberOfOpenedOutputTabs() {
-        return getGradleUI().getOutputUILord().getTabCount();
+        throw new NoLongerSupportedException();
     }
 }
diff --git a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/SinglePaneUIWrapper.java b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/SinglePaneUIWrapper.java
index 442f96a..82c3a71 100644
--- a/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/SinglePaneUIWrapper.java
+++ b/subprojects/ui/src/main/java/org/gradle/openapi/wrappers/ui/SinglePaneUIWrapper.java
@@ -16,8 +16,10 @@
 package org.gradle.openapi.wrappers.ui;
 
 import org.gradle.gradleplugin.userinterface.swing.generic.SinglePaneUIInstance;
+import org.gradle.openapi.external.ui.SettingsNodeVersion1;
 import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
 import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
+import org.gradle.openapi.wrappers.NoLongerSupportedException;
 
 import javax.swing.*;
 
@@ -27,6 +29,17 @@ import javax.swing.*;
  */
 public class SinglePaneUIWrapper extends AbstractOpenAPIUIWrapper<SinglePaneUIInstance> implements SinglePaneUIVersion1 {
     public SinglePaneUIWrapper(SinglePaneUIInteractionVersion1 singlePaneUIArguments) {
+        super(notSupported(), null);
+    }
+
+    private static SettingsNodeVersion1 notSupported() {
+        throw new NoLongerSupportedException();
+    }
+
+    /**
+     * The open API uses the other constructor.
+     */
+    public SinglePaneUIWrapper(SinglePaneUIInteractionVersion1 singlePaneUIArguments, boolean dummy) {
 
         super(singlePaneUIArguments.instantiateSettings(), singlePaneUIArguments.instantiateAlternateUIInteraction());
 
@@ -36,6 +49,10 @@ public class SinglePaneUIWrapper extends AbstractOpenAPIUIWrapper<SinglePaneUIIn
         initialize(singlePaneUIInstance);
     }
 
+    public int getNumberOfOpenedOutputTabs() {
+        return getGradleUI().getOutputUILord().getTabCount();
+    }
+
     /**
      * Returns this panel as a Swing object suitable for inserting in your UI.
      *
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/BuildInformation.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/BuildInformation.java
index dee43fc..8de669c 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/BuildInformation.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/BuildInformation.java
@@ -16,6 +16,8 @@
 package org.gradle.foundation;
 
 import org.gradle.api.Project;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectTaskLister;
 
 import java.util.Collections;
 import java.util.Iterator;
@@ -28,7 +30,8 @@ public class BuildInformation {
     private List<ProjectView> projects;
 
     public BuildInformation(Project rootProject) {
-        ProjectConverter buildExecuter = new ProjectConverter();
+        ProjectTaskLister taskLister = ((ProjectInternal) rootProject).getServices().get(ProjectTaskLister.class);
+        ProjectConverter buildExecuter = new ProjectConverter(taskLister);
         projects = buildExecuter.convertProjects(rootProject);
     }
 
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/FavoritesTest.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/FavoritesTest.java
index 993438d..4b0e104 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/FavoritesTest.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/FavoritesTest.java
@@ -61,23 +61,22 @@ public class FavoritesTest extends TestCase {
         Task subsubLibTask = TestUtility.createTask(context, "lib", "lib description");
         Task subsubDocTask = TestUtility.createTask(context, "doc", "doc description");
         Project subsubProject = TestUtility.createMockProject(context, "mysubsubproject", "filepath3", 2, null,
-                new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null, (Project[]) null);
+                new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null);
 
         Task subCompileTask1 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask1 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask1 = TestUtility.createTask(context, "doc", "doc description");
         Project subProject1 = TestUtility.createMockProject(context, "mysubproject1", "filepath2a", 1,
-                new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null,
-                (Project[]) null);
+                new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null);
 
         Task subCompileTask2 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask2 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask2 = TestUtility.createTask(context, "doc", "doc description");
         Project subProject2 = TestUtility.createMockProject(context, "mysubproject2", "filepath2b", 1, null,
-                new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null, (Project[]) null);
+                new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null);
 
         Project rootProject = TestUtility.createMockProject(context, "myrootproject", "filepath1", 0,
-                new Project[]{subProject1, subProject2}, null, null, (Project[]) null);
+                new Project[]{subProject1, subProject2}, null, null);
 
         buildInformation = new BuildInformation(rootProject);
 
@@ -471,7 +470,7 @@ public class FavoritesTest extends TestCase {
 
 
     /**
-     * This edits the favorite and expects an error. It makes sure the error was recieved and that the original task was
+     * This edits the favorite and expects an error. It makes sure the error was received and that the original task was
      * not altered.
      */
     private void editExpectingError(FavoritesEditor editor, FavoriteTask favoriteTaskToEdit, String newName,
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/FileLinkTests.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/FileLinkTests.java
index a5de7b4..9371366 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/FileLinkTests.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/FileLinkTests.java
@@ -155,7 +155,7 @@ public class FileLinkTests extends TestCase {
 
    /**
       This test that test reports file is found. This is a special case since we're not given
-      the actual file path and instead given only its parent path. The code assumes the report file
+      the actual file path and instead given only its root path. The code assumes the report file
       is present.
       */
    public void testFailedTestsReportFile() {
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/FilterTest.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/FilterTest.java
index 2c924eb..f5678c9 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/FilterTest.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/FilterTest.java
@@ -58,23 +58,22 @@ public class FilterTest extends TestCase {
         Task subsubLibTask = TestUtility.createTask(context, "lib", "lib description");
         Task subsubDocTask = TestUtility.createTask(context, "doc", "doc description");
         Project subsubProject = TestUtility.createMockProject(context, "mysubsubproject", "filepath3", 2, null,
-                new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null, (Project[]) null);
+                new Task[]{subsubCompileTask, subsubLibTask, subsubDocTask}, null);
 
         Task subCompileTask1 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask1 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask1 = TestUtility.createTask(context, "doc", "doc description");
         Project subProject1 = TestUtility.createMockProject(context, "mysubproject1", "filepath2a", 1,
-                new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null,
-                (Project[]) null);
+                new Project[]{subsubProject}, new Task[]{subCompileTask1, subLibTask1, subDocTask1}, null);
 
         Task subCompileTask2 = TestUtility.createTask(context, "compile", "compile description");
         Task subLibTask2 = TestUtility.createTask(context, "lib", "lib description");
         Task subDocTask2 = TestUtility.createTask(context, "doc", "doc description");
         Project subProject2 = TestUtility.createMockProject(context, "mysubproject2", "filepath2b", 1, null,
-                new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null, (Project[]) null);
+                new Task[]{subCompileTask2, subLibTask2, subDocTask2}, null);
 
         Project rootProject = TestUtility.createMockProject(context, "myrootproject", "filepath1", 0,
-                new Project[]{subProject1, subProject2}, null, null, (Project[]) null);
+                new Project[]{subProject1, subProject2}, null, null);
 
         buildInformation = new BuildInformation(rootProject);
 
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy b/subprojects/ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy
index d534bc5..f7d2c40 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy
@@ -15,13 +15,17 @@
  */
 package org.gradle.foundation
 
+import org.gradle.api.internal.project.ProjectTaskLister
 import spock.lang.Specification
 import org.gradle.api.Project
 import org.gradle.api.tasks.TaskContainer
 import org.gradle.api.Task
 
 class ProjectConverterTest extends Specification {
-    private final ProjectConverter converter = new ProjectConverter()
+    def lister = Stub(ProjectTaskLister) {
+        listProjectTasks(_) >> { Project p -> p.tasks as List }
+    }
+    def converter = new ProjectConverter(lister)
 
     def convertsProjectHierarchy() {
         Project child1 = project('child1')
@@ -99,7 +103,6 @@ class ProjectConverterTest extends Specification {
         _ * project.tasks >> taskContainer
         _ * project.defaultTasks >> []
         _ * taskContainer.iterator() >> tasks.iterator()
-        _ * project.dependsOnProjects >> []
         _ * project.childProjects >> subprojects.inject([:]) { v, p -> v[p.name] = p; v }
         return project
     }
diff --git a/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java b/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
index 4ff7bd5..7ca385a 100644
--- a/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
+++ b/subprojects/ui/src/test/groovy/org/gradle/foundation/TestUtility.java
@@ -17,7 +17,10 @@ package org.gradle.foundation;
 
 import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.internal.project.DefaultProjectTaskLister;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectTaskLister;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol;
 import org.gradle.gradleplugin.foundation.DOM4JSerializer;
 import org.gradle.gradleplugin.foundation.GradlePluginLord;
@@ -25,6 +28,8 @@ import org.gradle.gradleplugin.foundation.request.ExecutionRequest;
 import org.gradle.gradleplugin.foundation.request.RefreshTaskListRequest;
 import org.gradle.gradleplugin.foundation.request.Request;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.internal.service.ServiceRegistryBuilder;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Assert;
@@ -47,9 +52,13 @@ public class TestUtility {
      *
      * Note: depth is 0 for a root project. 1 for a root project's subproject, etc.
      */
-    public static Project createMockProject(JUnit4Mockery context, final String name, final String buildFilePath, final int depth, Project[] subProjectArray, Task[] tasks, String[] defaultTasks,
-                                            Project... dependsOnProjects) {
-        final Project project = context.mock(Project.class, "[project]_" + name + '_' + uniqueNameCounter++);
+    public static Project createMockProject(JUnit4Mockery context, final String name, final String buildFilePath, final int depth, Project[] subProjectArray, Task[] tasks, String[] defaultTasks) {
+        final ProjectInternal project = context.mock(ProjectInternal.class, "[project]_" + name + '_' + uniqueNameCounter++);
+        final ServiceRegistry services = ServiceRegistryBuilder.builder().provider(new Object() {
+            ProjectTaskLister createTaskLister() {
+                return new DefaultProjectTaskLister();
+            }
+        }).build();
 
         context.checking(new Expectations() {{
             allowing(project).getName();
@@ -60,22 +69,23 @@ public class TestUtility {
             will(returnValue(new File(buildFilePath)));
             allowing(project).getDepth();
             will(returnValue(depth));
+            allowing(project).getServices();
+            will(returnValue(services));
         }});
 
         attachSubProjects(context, project, subProjectArray);
         attachTasks(context, project, tasks);
         assignDefaultTasks(context, project, defaultTasks);
-        assignDependsOnProjects(context, project, dependsOnProjects);
 
         return project;
     }
 
     /**
-     * This makes the sub projects children of the parent project. If you call this repeatedly on the same parentProject, any previous sub projects will be replaced with the new ones.
+     * This makes the sub projects children of the root project. If you call this repeatedly on the same parentProject, any previous sub projects will be replaced with the new ones.
      *
      * @param context the mock context
      * @param parentProject where to attach the sub projects. This must be a mock object.
-     * @param subProjectArray the sub projects to attach to the parent. These must be mock objects. Pass in null or an empty array to set no sub projects.
+     * @param subProjectArray the sub projects to attach to the root. These must be mock objects. Pass in null or an empty array to set no sub projects.
      */
     public static void attachSubProjects(JUnit4Mockery context, final Project parentProject, Project... subProjectArray) {
         final Map<String, Project> childProjects = new LinkedHashMap<String, Project>();
@@ -113,19 +123,20 @@ public class TestUtility {
     }
 
     /**
-     * This makes the tasks children of the parent project. If you call this repeatedly on the same parentProject, any previous tasks will be replaced with the new ones.
+     * This makes the tasks children of the root project. If you call this repeatedly on the same parentProject, any previous tasks will be replaced with the new ones.
      *
      * @param context the mock context
      * @param parentProject where to attach the sub projects. This must be a mock object.
-     * @param taskArray the tasks to attach to the parent. these must be mock objects. Pass in null or an empty array to set no tasks.
+     * @param taskArray the tasks to attach to the root. these must be mock objects. Pass in null or an empty array to set no tasks.
      */
     public static void attachTasks(JUnit4Mockery context, final Project parentProject, Task... taskArray) {
         //first, make our project return our task container
-        final TaskContainer taskContainer = context.mock(TaskContainer.class, "[taskcontainer]_" + parentProject.getName() + '_' + uniqueNameCounter++);
+        final TaskContainerInternal taskContainer = context.mock(TaskContainerInternal.class, "[taskcontainer]_" + parentProject.getName() + '_' + uniqueNameCounter++);
 
         context.checking(new Expectations() {{
             allowing(parentProject).getTasks();
             will(returnValue(taskContainer));
+            allowing(taskContainer).realize();
         }});
 
         final Set<Task> set
@@ -134,7 +145,7 @@ public class TestUtility {
         if (taskArray != null && taskArray.length != 0) {
             set.addAll(Arrays.asList(taskArray));
 
-            //set the parent project of the tasks
+            //set the root project of the tasks
             for (int index = 0; index < taskArray.length; index++) {
                 final Task task = taskArray[index];
 
@@ -165,21 +176,6 @@ public class TestUtility {
         }});
     }
 
-    private static void assignDependsOnProjects(JUnit4Mockery context, final Project project, final Project... dependsOnProjects) {
-        final Set<Project> set
-                = new LinkedHashSet<Project>();   //using a LinkedHashSet rather than TreeSet (which is what gradle uses) so I don't have to deal with compareTo() being called on mock objects.
-
-        if (dependsOnProjects != null && dependsOnProjects.length != 0) {
-            set.addAll(Arrays.asList(dependsOnProjects));
-        }
-
-        //populate the subprojects (this may be an empty set)
-        context.checking(new Expectations() {{
-            allowing(project).getDependsOnProjects();
-            will(returnValue(set));
-        }});
-    }
-
     public static <T> void assertListContents(List<T> actualObjects, T... expectedObjectsArray) {
         assertListContents(actualObjects, Arrays.asList(expectedObjectsArray));
     }
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
index 2da94dd..7de3b06 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
@@ -37,7 +37,7 @@ class WrapperConcurrentDownloadTest extends AbstractIntegrationSpec {
         executer.beforeExecute(new WrapperSetup())
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-2699")
+    @Issue("https://issues.gradle.org/browse/GRADLE-2699")
     def "concurrent downloads do not stomp over each other"() {
         given:
         buildFile << """
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperCrossVersionIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperCrossVersionIntegrationTest.groovy
index 126fff8..ee3323d 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperCrossVersionIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperCrossVersionIntegrationTest.groovy
@@ -17,8 +17,15 @@ package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleDistribution
+import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.internal.SystemProperties
 
 class WrapperCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
+    def setup() {
+        requireOwnGradleUserHomeDir()
+    }
+
     public void canUseWrapperFromPreviousVersionToRunCurrentVersion() {
         expect:
         checkWrapperWorksWith(previous, current)
@@ -43,8 +50,6 @@ task wrapper(type: Wrapper) {
     gradleVersion = '$executionVersion.version.version'
 }
 
-//(SF) not sure if we want to keep coverage for old 'urlRoot' that was already removed
-//I'm keeping it so that old versions are tested via the urlRoot.
 if (wrapper.hasProperty('urlRoot')) {
     println "configuring the wrapper using the old way: 'urlRoot'..."
     wrapper.urlRoot = '${executionVersion.binDistribution.parentFile.toURI()}'
@@ -60,8 +65,37 @@ task hello {
 }
 """
         version(wrapperGenVersion).withTasks('wrapper').run()
-        def result = version(wrapperGenVersion).usingExecutable('gradlew').withDeprecationChecksDisabled().withTasks('hello').run()
+        def result = version(executionVersion, wrapperGenVersion).usingExecutable('gradlew').withTasks('hello').run()
         assert result.output.contains("hello from $executionVersion.version.version")
     }
+
+    GradleExecuter version(GradleDistribution runtime, GradleDistribution wrapper) {
+        def executer = super.version(runtime)
+        /**
+         * We additionally pass the gradle user home as a system property.
+         * Early gradle wrapper (< 1.7 don't honor --gradle-user-home command line option correctly
+         * and leaking gradle dist under test into ~/.gradle/wrapper.
+         */
+        if (!wrapper.wrapperSupportsGradleUserHomeCommandLineOption) {
+            if (!wrapper.supportsSpacesInGradleAndJavaOpts) {
+                // Don't use the test-specific location as this contains spaces
+                executer.withGradleUserHomeDir(new IntegrationTestBuildContext().gradleUserHomeDir)
+
+                def buildDirTmp = file("build/tmp")
+                if (buildDirTmp.absolutePath.contains(" ")) {
+                    def jvmTmp = SystemProperties.instance.javaIoTmpDir
+                    if (jvmTmp.contains(" ")) {
+                        throw new IllegalStateException("Cannot run test as there is no tmp dir location available that does not contain a space in the path")
+                    } else {
+                        executer.withTmpDir(jvmTmp)
+                    }
+                } else {
+                    executer.withTmpDir(buildDirTmp.absolutePath)
+                }
+            }
+            executer.withGradleOpts("-Dgradle.user.home=${executer.gradleUserHomeDir}")
+        }
+        return executer
+    }
 }
 
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
index 91d6f3d..6f81ed2 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
@@ -20,15 +20,13 @@ import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.util.TextUtil
 
 class WrapperGenerationIntegrationTest extends AbstractIntegrationSpec {
-    def setup() {
+    def "generated wrapper scripts use correct line separators"() {
         buildFile << """
             wrapper {
                 distributionUrl = 'http://localhost:8080/gradlew/dist'
             }
         """
-    }
 
-    def "generated wrapper scripts use correct line separators"() {
         when:
         run "wrapper"
 
@@ -39,11 +37,33 @@ class WrapperGenerationIntegrationTest extends AbstractIntegrationSpec {
     }
 
     def "wrapper jar is small"() {
+        buildFile << """
+            wrapper {
+                distributionUrl = 'http://localhost:8080/gradlew/dist'
+            }
+        """
+
         when:
         run "wrapper"
 
         then:
         // wrapper needs to be small. Let's check it's smaller than some arbitrary 'small' limit
-        file("gradle/wrapper/gradle-wrapper.jar").length() < 51 * 1024
+        file("gradle/wrapper/gradle-wrapper.jar").length() < 52 * 1024
+    }
+
+    def "generated wrapper scripts for given version from command-line"() {
+        when:
+        run "wrapper", "--gradle-version", "2.2.1"
+
+        then:
+        file("gradle/wrapper/gradle-wrapper.properties").text.contains("distributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-bin.zip")
+    }
+
+    def "generated wrapper scripts for given distribution URL from command-line"() {
+        when:
+        run "wrapper", "--gradle-distribution-url", "http://localhost:8080/gradlew/dist"
+
+        then:
+        file("gradle/wrapper/gradle-wrapper.properties").text.contains("distributionUrl=http\\://localhost\\:8080/gradlew/dist")
     }
 }
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..76fa0ef
--- /dev/null
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.integtests
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class WrapperLoggingIntegrationTest extends AbstractIntegrationSpec {
+
+    void setup() {
+        assert distribution.binDistribution.exists(): "bin distribution must exist to run this test, you need to run the :distributions:binZip task"
+        executer.beforeExecute(new WrapperSetup())
+    }
+
+    private prepareWrapper() {
+        file("build.gradle") << """
+            wrapper {
+                distributionUrl = '${distribution.binDistribution.toURI()}'
+            }
+
+            task emptyTask
+        """
+        executer.withTasks('wrapper').run()
+        executer.usingExecutable('gradlew').inDirectory(testDirectory)
+    }
+
+    def "wrapper does not output anything when executed in quiet mode"() {
+        given:
+        prepareWrapper()
+
+        when:
+        args '-q'
+        succeeds("emptyTask")
+
+        then:
+        output.empty
+    }
+}
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
index 0f4dd1a..b755964 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperProjectIntegrationTest.groovy
@@ -63,7 +63,7 @@ class WrapperProjectIntegrationTest extends AbstractIntegrationSpec {
         failure.assertThatDescription(Matchers.startsWith("Task 'unknown' not found in root project"))
     }
 
-    @Issue("http://issues.gradle.org/browse/GRADLE-1871")
+    @Issue("https://issues.gradle.org/browse/GRADLE-1871")
     public void "can specify project properties containing D"() {
         given:
         prepareWrapper()
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperUserHomeIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperUserHomeIntegrationTest.groovy
index aac1714..59d0e9e 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperUserHomeIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperUserHomeIntegrationTest.groovy
@@ -56,7 +56,7 @@ class WrapperUserHomeIntegrationTest extends AbstractIntegrationSpec {
         installationIn gradleUserHome exists()
     }
 
-    @Issue('http://issues.gradle.org/browse/GRADLE-2802')
+    @Issue('https://issues.gradle.org/browse/GRADLE-2802')
     void 'uses gradle user home set by -g'() {
         given:
         prepareWrapper()
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Download.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Download.java
index 39e6b4c..733a326 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Download.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Download.java
@@ -22,10 +22,12 @@ import java.net.*;
 public class Download implements IDownload {
     private static final int PROGRESS_CHUNK = 20000;
     private static final int BUFFER_SIZE = 10000;
+    private final Logger logger;
     private final String applicationName;
     private final String applicationVersion;
 
-    public Download(String applicationName, String applicationVersion) {
+    public Download(Logger logger, String applicationName, String applicationVersion) {
+        this.logger = logger;
         this.applicationName = applicationName;
         this.applicationVersion = applicationVersion;
         configureProxyAuthentication();
@@ -58,15 +60,19 @@ public class Download implements IDownload {
             int numRead;
             long progressCounter = 0;
             while ((numRead = in.read(buffer)) != -1) {
+                if (Thread.currentThread().isInterrupted()) {
+                    System.out.print("interrupted");
+                    throw new IOException("Download was interrupted.");
+                }
                 progressCounter += numRead;
                 if (progressCounter / PROGRESS_CHUNK > 0) {
-                    System.out.print(".");
+                    logger.append(".");
                     progressCounter = progressCounter - PROGRESS_CHUNK;
                 }
                 out.write(buffer, 0, numRead);
             }
         } finally {
-            System.out.println("");
+            logger.log("");
             if (in != null) {
                 in.close();
             }
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/ExclusiveFileAccessManager.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/ExclusiveFileAccessManager.java
index f63ef49..880bcdc 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/ExclusiveFileAccessManager.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/ExclusiveFileAccessManager.java
@@ -35,7 +35,7 @@ public class ExclusiveFileAccessManager {
         this.pollIntervalMs = pollIntervalMs;
     }
 
-    public <T> T access(File exclusiveFile, Callable<T> task) {
+    public <T> T access(File exclusiveFile, Callable<T> task) throws Exception {
         final File lockFile = new File(exclusiveFile.getParentFile(), exclusiveFile.getName() + LOCK_FILE_SUFFIX);
         lockFile.getParentFile().mkdirs();
         RandomAccessFile randomAccessFile = null;
@@ -71,12 +71,6 @@ public class ExclusiveFileAccessManager {
                 maybeCloseQuietly(randomAccessFile);
                 randomAccessFile = null;
             }
-        } catch (Exception e) {
-            if (e instanceof RuntimeException) {
-                throw (RuntimeException) e;
-            } else {
-                throw new RuntimeException(e);
-            }
         } finally {
             maybeCloseQuietly(channel);
             maybeCloseQuietly(randomAccessFile);
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/GradleWrapperMain.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/GradleWrapperMain.java
index d2cb34b..c8e7110 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/GradleWrapperMain.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/GradleWrapperMain.java
@@ -24,11 +24,14 @@ import java.io.File;
 import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.HashMap;
 import java.util.Properties;
 
 public class GradleWrapperMain {
     public static final String GRADLE_USER_HOME_OPTION = "g";
     public static final String GRADLE_USER_HOME_DETAILED_OPTION = "gradle-user-home";
+    public static final String GRADLE_QUIET_OPTION = "q";
+    public static final String GRADLE_QUIET_DETAILED_OPTION = "quiet";
 
     public static void main(String[] args) throws Exception {
         File wrapperJar = wrapperJar();
@@ -38,6 +41,7 @@ public class GradleWrapperMain {
         CommandLineParser parser = new CommandLineParser();
         parser.allowUnknownOptions();
         parser.option(GRADLE_USER_HOME_OPTION, GRADLE_USER_HOME_DETAILED_OPTION).hasArgument();
+        parser.option(GRADLE_QUIET_OPTION, GRADLE_QUIET_DETAILED_OPTION);
 
         SystemPropertiesCommandLineConverter converter = new SystemPropertiesCommandLineConverter();
         converter.configure(parser);
@@ -45,16 +49,18 @@ public class GradleWrapperMain {
         ParsedCommandLine options = parser.parse(args);
 
         Properties systemProperties = System.getProperties();
-        systemProperties.putAll(converter.convert(options));
+        systemProperties.putAll(converter.convert(options, new HashMap<String, String>()));
 
         File gradleUserHome = gradleUserHome(options);
-
+        
         addSystemProperties(gradleUserHome, rootDir);
+        
+        Logger logger = logger(options);
 
-        WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile, System.out);
+        WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile, logger);
         wrapperExecutor.execute(
                 args,
-                new Install(new Download("gradlew", wrapperVersion()), new PathAssembler(gradleUserHome)),
+                new Install(logger, new Download(logger, "gradlew", wrapperVersion()), new PathAssembler(gradleUserHome)),
                 new BootstrapMainStarter());
     }
 
@@ -112,4 +118,8 @@ public class GradleWrapperMain {
         }
         return GradleUserHomeLookup.gradleUserHome();
     }
+    
+    private static Logger logger(ParsedCommandLine options) {
+        return new Logger(options.hasOption(GRADLE_QUIET_OPTION));
+    }
 }
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
index 144fcb0..f1ce549 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Install.java
@@ -25,11 +25,13 @@ import java.util.zip.ZipFile;
 
 public class Install {
     public static final String DEFAULT_DISTRIBUTION_PATH = "wrapper/dists";
+    private final Logger logger;
     private final IDownload download;
     private final PathAssembler pathAssembler;
     private final ExclusiveFileAccessManager exclusiveFileAccessManager = new ExclusiveFileAccessManager(120000, 200);
 
-    public Install(IDownload download, PathAssembler pathAssembler) {
+    public Install(Logger logger, IDownload download, PathAssembler pathAssembler) {
+        this.logger = logger;
         this.download = download;
         this.pathAssembler = pathAssembler;
     }
@@ -53,17 +55,17 @@ public class Install {
                 if (needsDownload) {
                     File tmpZipFile = new File(localZipFile.getParentFile(), localZipFile.getName() + ".part");
                     tmpZipFile.delete();
-                    System.out.println("Downloading " + distributionUrl);
+                    logger.log("Downloading " + distributionUrl);
                     download.download(distributionUrl, tmpZipFile);
                     tmpZipFile.renameTo(localZipFile);
                 }
 
                 List<File> topLevelDirs = listDirs(distDir);
                 for (File dir : topLevelDirs) {
-                    System.out.println("Deleting directory " + dir.getAbsolutePath());
+                    logger.log("Deleting directory " + dir.getAbsolutePath());
                     deleteDir(dir);
                 }
-                System.out.println("Unzipping " + localZipFile.getAbsolutePath() + " to " + distDir.getAbsolutePath());
+                logger.log("Unzipping " + localZipFile.getAbsolutePath() + " to " + distDir.getAbsolutePath());
                 unzip(localZipFile, distDir);
 
                 File root = getDistributionRoot(distDir, distributionUrl.toString());
@@ -108,7 +110,7 @@ public class Install {
             ProcessBuilder pb = new ProcessBuilder("chmod", "755", gradleCommand.getCanonicalPath());
             Process p = pb.start();
             if (p.waitFor() == 0) {
-                System.out.println("Set executable permissions for: " + gradleCommand.getAbsolutePath());
+                logger.log("Set executable permissions for: " + gradleCommand.getAbsolutePath());
             } else {
                 BufferedReader is = new BufferedReader(new InputStreamReader(p.getInputStream()));
                 Formatter stdout = new Formatter();
@@ -124,8 +126,8 @@ public class Install {
             errorMessage = e.getMessage();
         }
         if (errorMessage != null) {
-            System.out.println("Could not set executable permissions for: " + gradleCommand.getAbsolutePath());
-            System.out.println("Please do this manually if you want to use the Gradle UI.");
+            logger.log("Could not set executable permissions for: " + gradleCommand.getAbsolutePath());
+            logger.log("Please do this manually if you want to use the Gradle UI.");
         }
     }
 
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/Logger.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Logger.java
new file mode 100644
index 0000000..82bbe90
--- /dev/null
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/Logger.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.wrapper;
+
+public class Logger implements Appendable {
+
+    private final boolean quiet;
+
+    public Logger(boolean quiet) {
+        this.quiet = quiet;
+    }
+    
+    public void log(String message) {
+        if (!quiet) {
+            System.out.println(message);
+        }
+    }
+
+    public Appendable append(CharSequence csq) {
+        if (!quiet) {
+            System.out.append(csq);
+        }
+        return this;
+    }
+
+    public Appendable append(CharSequence csq, int start, int end) {
+        if (!quiet) {
+            System.out.append(csq, start, end);
+        }
+        return this;
+    }
+
+    public Appendable append(char c) {
+        if(!quiet) {
+            System.out.append(c);
+        }
+        return this;
+    }
+}
diff --git a/subprojects/wrapper/src/main/java/org/gradle/wrapper/PathAssembler.java b/subprojects/wrapper/src/main/java/org/gradle/wrapper/PathAssembler.java
index d3c51bd..8c2e3bc 100644
--- a/subprojects/wrapper/src/main/java/org/gradle/wrapper/PathAssembler.java
+++ b/subprojects/wrapper/src/main/java/org/gradle/wrapper/PathAssembler.java
@@ -46,16 +46,26 @@ public class PathAssembler {
     }
 
     private String rootDirName(String distName, WrapperConfiguration configuration) {
-        String urlHash = getMd5Hash(configuration.getDistribution().toString());
+        String urlHash = getHash(configuration.getDistribution().toString());
         return String.format("%s/%s", distName, urlHash);
     }
 
-    private String getMd5Hash(String string) {
+    /**
+     * This method computes a hash of the provided {@code string}.
+     * <p>
+     * The algorithm in use by this method is as follows:
+     * <ol>
+     *    <li>Compute the MD5 value of {@code string}.</li>
+     *    <li>Truncate leading zeros (i.e., treat the MD5 value as a number).</li>
+     *    <li>Convert to base 36 (the characters {@code 0-9a-z}).</li>
+     * </ol>
+     */
+    private String getHash(String string) {
         try {
             MessageDigest messageDigest = MessageDigest.getInstance("MD5");
             byte[] bytes = string.getBytes();
             messageDigest.update(bytes);
-            return new BigInteger(1, messageDigest.digest()).toString(32);
+            return new BigInteger(1, messageDigest.digest()).toString(36);
         } catch (Exception e) {
             throw new RuntimeException("Could not hash input string.", e);
         }
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/DownloadTest.groovy b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/DownloadTest.groovy
index 2c178b0..417d03c 100644
--- a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/DownloadTest.groovy
+++ b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/DownloadTest.groovy
@@ -34,7 +34,7 @@ class DownloadTest {
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider();
 
     @Before public void setUp() {
-        download = new Download("gradlew", "aVersion")
+        download = new Download(new Logger(true), "gradlew", "aVersion")
         testDir = tmpDir.testDirectory
         rootDir = new File(testDir, 'root')
         downloadFile = new File(rootDir, 'file')
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/InstallTest.groovy b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/InstallTest.groovy
index f1748c8..8fd1cab 100644
--- a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/InstallTest.groovy
+++ b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/InstallTest.groovy
@@ -50,7 +50,7 @@ class InstallTest extends Specification {
         gradleHomeDir = new TestFile(distributionDir, 'gradle-0.9')
         zipStore = new File(testDir, 'zips');
         zipDestination = new TestFile(zipStore, 'gradle-0.9.zip')
-        install = new Install(download, pathAssembler)
+        install = new Install(new Logger(true), download, pathAssembler)
     }
 
     void createTestZip(File zipDestination) {
diff --git a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/PathAssemblerTest.java b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/PathAssemblerTest.java
index e7fab02..74ce594 100644
--- a/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/PathAssemblerTest.java
+++ b/subprojects/wrapper/src/test/groovy/org/gradle/wrapper/PathAssemblerTest.java
@@ -21,7 +21,6 @@ import org.junit.Test;
 import java.io.File;
 import java.net.URI;
 
-import static org.gradle.util.Matchers.matchesRegexp;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.*;
 
@@ -43,7 +42,7 @@ public class PathAssemblerTest {
         configuration.setDistribution(new URI("http://server/dist/gradle-0.9-bin.zip"));
         
         File distributionDir = pathAssembler.getDistribution(configuration).getDistributionDir();
-        assertThat(distributionDir.getName(), matchesRegexp("[a-z0-9]+"));
+        assertThat(distributionDir.getName(), equalTo("emn8ua2x0re2y4jlewhnxhasz"));
         assertThat(distributionDir.getParentFile(), equalTo(file(TEST_GRADLE_USER_HOME + "/somePath/gradle-0.9-bin")));
     }
 
@@ -53,7 +52,7 @@ public class PathAssemblerTest {
         configuration.setDistribution(new URI("http://server/dist/gradle-0.9-bin.zip"));
 
         File distributionDir = pathAssembler.getDistribution(configuration).getDistributionDir();
-        assertThat(distributionDir.getName(), matchesRegexp("[a-z0-9]+"));
+        assertThat(distributionDir.getName(), equalTo("emn8ua2x0re2y4jlewhnxhasz"));
         assertThat(distributionDir.getParentFile(), equalTo(file(currentDirPath() + "/somePath/gradle-0.9-bin")));
     }
 
@@ -76,7 +75,7 @@ public class PathAssemblerTest {
 
         File dist = pathAssembler.getDistribution(configuration).getZipFile();
         assertThat(dist.getName(), equalTo("gradle-1.0.zip"));
-        assertThat(dist.getParentFile().getName(), matchesRegexp("[a-z0-9]+"));
+        assertThat(dist.getParentFile().getName(), equalTo("98xa9n94mamfu7vl4mzwomw11"));
         assertThat(dist.getParentFile().getParentFile(), equalTo(file(TEST_GRADLE_USER_HOME + "/somePath/gradle-1.0")));
     }
 
@@ -87,7 +86,7 @@ public class PathAssemblerTest {
 
         File dist = pathAssembler.getDistribution(configuration).getZipFile();
         assertThat(dist.getName(), equalTo("gradle-1.0.zip"));
-        assertThat(dist.getParentFile().getName(), matchesRegexp("[a-z0-9]+"));
+        assertThat(dist.getParentFile().getName(), equalTo("98xa9n94mamfu7vl4mzwomw11"));
         assertThat(dist.getParentFile().getParentFile(), equalTo(file(currentDirPath() + "/somePath/gradle-1.0")));
     }
 
diff --git a/version.txt b/version.txt
index 809bdcb..6b4950e 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.12
+2.4

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



More information about the pkg-java-commits mailing list